Rigs of Rods 2026.01
Soft-body Physics Simulation
Loading...
Searching...
No Matches
ContentManager.cpp
Go to the documentation of this file.
1/*
2 This source file is part of Rigs of Rods
3 Copyright 2005-2012 Pierre-Michel Ricordel
4 Copyright 2007-2012 Thomas Fischer
5 Copyright 2013-2018 Petr Ohlidal
6
7 For more information, see http://www.rigsofrods.org/
8
9 Rigs of Rods is free software: you can redistribute it and/or modify
10 it under the terms of the GNU General Public License version 3, as
11 published by the Free Software Foundation.
12
13 Rigs of Rods is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
17
18 You should have received a copy of the GNU General Public License
19 along with Rigs of Rods. If not, see <http://www.gnu.org/licenses/>.
20*/
21
22#include "ContentManager.h"
23
24
25#include <Overlay/OgreOverlayManager.h>
26#include <Overlay/OgreOverlay.h>
27#include <Plugins/ParticleFX/OgreBoxEmitterFactory.h>
28
29
30#include "Application.h"
32#include "ErrorUtils.h"
33#include "SoundScriptManager.h"
34#include "SkinFileFormat.h"
35#include "Language.h"
36#include "PlatformUtils.h"
37
38#include "CacheSystem.h"
39
41
42// Removed by Skybon as part of OGRE 1.9 port
43// Disabling temporarily for 1.8.1 as well. ~ only_a_ptr, 2015-11
44// TODO: Study the system, then re-enable or remove entirely.
45//#include "OgreBoxEmitterFactory.h"
46
47#ifdef USE_ANGELSCRIPT
50#endif // USE_ANGELSCRIPT
51
52#include "Utils.h"
53
54#include <OgreFileSystem.h>
55#include <regex>
56#include <rapidjson/stringbuffer.h>
57#include <rapidjson/writer.h>
58#include <sstream>
59#include <OgreMeshLodGenerator.h>
60
61using namespace Ogre;
62using namespace RoR;
63
64// ================================================================================
65// Static variables
66// ================================================================================
67
68#define DECLARE_RESOURCE_PACK(_FIELD_, _NAME_, _RESOURCE_GROUP_) \
69 const ContentManager::ResourcePack ContentManager::ResourcePack::_FIELD_(_NAME_, _RESOURCE_GROUP_);
70
71DECLARE_RESOURCE_PACK( OGRE_CORE, "OgreCore", "OgreCoreRG");
72DECLARE_RESOURCE_PACK( WALLPAPERS, "wallpapers", "Wallpapers");
73DECLARE_RESOURCE_PACK( AIRFOILS, "airfoils", "AirfoilsRG");
74DECLARE_RESOURCE_PACK( CAELUM, "caelum", "CaelumRG");
75DECLARE_RESOURCE_PACK( CUBEMAPS, "cubemaps", "CubemapsRG");
76DECLARE_RESOURCE_PACK( DASHBOARDS, "dashboards", "DashboardsRG");
77DECLARE_RESOURCE_PACK( FAMICONS, "famicons", "FamiconsRG");
78DECLARE_RESOURCE_PACK( FLAGS, "flags", "FlagsRG");
79DECLARE_RESOURCE_PACK( FONTS, "fonts", "FontsRG");
80DECLARE_RESOURCE_PACK( HYDRAX, "hydrax", "HydraxRG");
81DECLARE_RESOURCE_PACK( ICONS, "icons", "IconsRG");
82DECLARE_RESOURCE_PACK( MATERIALS, "materials", "MaterialsRG");
83DECLARE_RESOURCE_PACK( MESHES, "meshes", "MeshesRG");
84DECLARE_RESOURCE_PACK( MYGUI, "mygui", "MyGuiRG");
85DECLARE_RESOURCE_PACK( OVERLAYS, "overlays", "OverlaysRG");
86DECLARE_RESOURCE_PACK( PAGED, "paged", "PagedRG");
87DECLARE_RESOURCE_PACK( PARTICLES, "particles", "ParticlesRG");
88DECLARE_RESOURCE_PACK( PSSM, "pssm", "PssmRG");
89DECLARE_RESOURCE_PACK( RTSHADER, "rtshader", "RtShaderRG");
90DECLARE_RESOURCE_PACK( SCRIPTS, "scripts", "ScriptsRG");
91DECLARE_RESOURCE_PACK( SOUNDS, "sounds", "SoundsRG");
92DECLARE_RESOURCE_PACK( TEXTURES, "textures", "TexturesRG");
93DECLARE_RESOURCE_PACK( SKYX, "SkyX", "SkyXRG");
94
95// ================================================================================
96// Functions
97// ================================================================================
98
99void ContentManager::AddResourcePack(ResourcePack const& resource_pack, std::string const& override_rgn)
100{
101 Ogre::ResourceGroupManager& rgm = Ogre::ResourceGroupManager::getSingleton();
102
103 Ogre::String rg_name;
104 if (!override_rgn.empty()) // Custom RG defined?
105 {
106 rg_name = override_rgn;
107 }
108 else // Use default RG
109 {
110 if (rgm.resourceGroupExists(resource_pack.resource_group_name)) // Already loaded?
111 {
112 return; // Nothing to do, nothing to report
113 }
114 rg_name = resource_pack.resource_group_name;
115 }
116
117 std::stringstream log_msg;
118 log_msg << "[RoR|ContentManager] Loading resource pack \"" << resource_pack.name << "\" to group \"" << rg_name << "\"";
119 std::string dir_path = PathCombine(App::sys_resources_dir->getStr(), resource_pack.name);
120 std::string zip_path = dir_path + ".zip";
121 if (FileExists(zip_path))
122 {
123 log_msg << " (ZIP archive)";
124 LOG(log_msg.str());
125 rgm.addResourceLocation(zip_path, "Zip", rg_name);
126 }
127 else
128 {
129 if (FolderExists(dir_path))
130 {
131 log_msg << " (directory)";
132 LOG(log_msg.str());
133 rgm.addResourceLocation(dir_path, "FileSystem", rg_name);
134 }
135 else
136 {
137 log_msg << " failed, data not found.";
138 throw std::runtime_error(log_msg.str());
139 }
140 }
141
142 if (override_rgn.empty()) // Only init the default RG
143 {
144 rgm.initialiseResourceGroup(rg_name);
145 }
146}
147
149{
150 ResourceGroupManager::getSingleton().addResourceLocation(
151 App::sys_config_dir->getStr(), "FileSystem", RGN_CONFIG, /*recursive=*/false, /*readOnly=*/false);
152 ResourceGroupManager::getSingleton().addResourceLocation(
153 App::sys_savegames_dir->getStr(), "FileSystem", RGN_SAVEGAMES, /*recursive=*/false, /*readOnly=*/false);
154 ResourceGroupManager::getSingleton().addResourceLocation(
155 App::sys_scripts_dir->getStr(), "FileSystem", RGN_SCRIPTS, /*recursive:*/false, /*readonly:*/false);
156 ResourceGroupManager::getSingleton().addResourceLocation(
157 App::sys_logs_dir->getStr(), "FileSystem", RGN_LOGS, /*recursive:*/false, /*readonly:*/false);
158
159 // Server scripts must be isolated from any others.
160 ResourceGroupManager::getSingleton().createResourceGroup(RGN_SERVER_SCRIPTS, /* inGlobalPool: */false);
161 ResourceGroupManager::getSingleton().addResourceLocation(
162 App::sys_server_scripts_dir->getStr(), "FileSystem", RGN_SERVER_SCRIPTS, /*recursive:*/false, /*readonly:*/false);
163 // Recurse one level down, that's all the https://github.com/RigsOfRods/RoRServerScripts package needs.
164 // NOTE: We can't use OGRE's 'recursive' mode because it prepends subdir names to the resource name, and we need bare filenames.
165 FileInfoListPtr dirs = ResourceGroupManager::getSingleton().findResourceFileInfo(RGN_SERVER_SCRIPTS, "*", /*dirs:*/true);
166 for (const auto& dir_fileinfo : *dirs)
167 {
168 if (!dir_fileinfo.archive)
169 continue;
170 String fullpath = PathCombine(dir_fileinfo.archive->getName(), dir_fileinfo.filename);
171 ResourceGroupManager::getSingleton().addResourceLocation(fullpath, "FileSystem", RGN_SERVER_SCRIPTS, /*recursive:*/false, /*readonly:*/false);
172 }
173
174 Ogre::ScriptCompilerManager::getSingleton().setListener(this);
175
176 // Initialize "managed materials" first
177 // These are base materials referenced by user content
178 // They must be initialized before any content is loaded,
179 // otherwise material links are unresolved and loading ends with an exception
181
182 // set listener if none has already been set
183 if (!Ogre::ResourceGroupManager::getSingleton().getLoadingListener())
184 Ogre::ResourceGroupManager::getSingleton().setLoadingListener(this);
185
186 // by default, display everything in the depth map
187 Ogre::MovableObject::setDefaultVisibilityFlags(DEPTHMAP_ENABLED);
188
189
192
193
194#ifdef _WIN32
195 // TODO: FIX UNDER LINUX!
196 // register particle classes
197 LOG("RoR|ContentManager: Registering Particle Box Emitter");
198 ParticleSystemRendererFactory* mParticleSystemRendererFact = OGRE_NEW ShaderParticleRendererFactory();
199 ParticleSystemManager::getSingleton().addRendererFactory(mParticleSystemRendererFact);
200
201 // Removed by Skybon as part of OGRE 1.9 port
202 // Disabling temporarily for 1.8.1 as well. ~ only_a_ptr, 2015-11
203 //ParticleEmitterFactory *mParticleEmitterFact = OGRE_NEW BoxEmitterFactory();
204 //ParticleSystemManager::getSingleton().addEmitterFactory(mParticleEmitterFact);
205
206#endif // _WIN32
207
208#ifdef USE_ANGELSCRIPT
209 // FireExtinguisherAffector
210 ParticleAffectorFactory* pAffFact = OGRE_NEW FireExtinguisherAffectorFactory();
211 ParticleSystemManager::getSingleton().addAffectorFactory(pAffFact);
212
213 // ExtinguishableFireAffector
214 pAffFact = OGRE_NEW ExtinguishableFireAffectorFactory();
215 ParticleSystemManager::getSingleton().addAffectorFactory(pAffFact);
216#endif // USE_ANGELSCRIPT
217
218 // sound is a bit special as we mark the base sounds so we don't clear them accidentally later on
219#ifdef USE_OPENAL
220 LOG("RoR|ContentManager: Creating Sound Manager");
223#endif // USE_OPENAL
224
226
227 // streams path, to be processed later by the cache system
228 LOG("RoR|ContentManager: Loading filesystems");
229
230 LOG("RoR|ContentManager: Registering colored text overlay factory");
232 OverlayManager::getSingleton().addOverlayElementFactory(pCT);
233
234 // set default mipmap level (NB some APIs ignore this)
235 if (TextureManager::getSingletonPtr())
236 TextureManager::getSingleton().setDefaultNumMipmaps(5);
237
238 TextureFilterOptions tfo = TFO_NONE;
239 switch (App::gfx_texture_filter->getEnum<GfxTexFilter>())
240 {
241 case GfxTexFilter::ANISOTROPIC: tfo = TFO_ANISOTROPIC; break;
242 case GfxTexFilter::TRILINEAR: tfo = TFO_TRILINEAR; break;
243 case GfxTexFilter::BILINEAR: tfo = TFO_BILINEAR; break;
244 case GfxTexFilter::NONE: tfo = TFO_NONE; break;
245 }
246 MaterialManager::getSingleton().setDefaultAnisotropy(Math::Clamp(App::gfx_anisotropy->getInt(), 1, 16));
247 MaterialManager::getSingleton().setDefaultTextureFiltering(tfo);
248
249 // load all resources now, so the zip files are also initiated
250 LOG("RoR|ContentManager: Calling initialiseAllResourceGroups()");
251 try
252 {
253 ResourceGroupManager::getSingleton().initialiseAllResourceGroups();
254 }
255 catch (Ogre::Exception& e)
256 {
257 LOG("RoR|ContentManager: catched error while initializing Resource groups: " + e.getFullDescription());
258 }
259#ifdef USE_OPENAL
261#endif // USE_OPENAL
262
263 new Ogre::MeshLodGenerator();
264}
265
267{
268 // Sets up RGN_CONTENT which encompasses all mods, scans it for changes and deletes it again.
269 // IMPORTANT NOTE ON 'readOnly' FLAG:
270 // We need mods in subdirs to be writable for the Tuning menu to work.
271 // Apart from `Resources` and resource groups, OGRE also keeps `Archives` in `ArchiveManager`
272 // These aren't unloaded on destroying resource groups, and keep a 'readOnly' flag (defaults to true).
273 // Upon loading/creating new resource groups, OGRE complains (=assert on Debug, exception on Release) if the submitted flag doesn't match.
274 // It's possible to manually unload archives to reset the flag, but for simplicity we just always load subdirs as 'writable', even during modcache update.
275 // ------------------------------------------------------------------------------------------
276
277 ResourceGroupManager::getSingleton().addResourceLocation(
278 App::sys_cache_dir->getStr(), "FileSystem", RGN_CACHE, /*recursive=*/false, /*readOnly=*/false);
279 ResourceGroupManager::getSingleton().addResourceLocation(
280 App::sys_thumbnails_dir->getStr(), "FileSystem", RGN_THUMBNAILS, /*recursive=*/false, /*readOnly=*/false);
281 ResourceGroupManager::getSingleton().addResourceLocation(
282 App::sys_repo_attachments_dir->getStr(), "FileSystem", RGN_REPO_ATTACHMENTS, /*recursive=*/false, /*readOnly=*/false);
283
284 // Add top-level ZIPs/directories to RGN_CONTENT (non-recursive)
285
286 if (!App::app_extra_mod_path->getStr().empty())
287 {
288 std::string extra_mod_path = App::app_extra_mod_path->getStr();
289 ResourceGroupManager::getSingleton().addResourceLocation(extra_mod_path , "FileSystem", RGN_CONTENT);
290 }
291 for (const std::string& dirname : App::GetCacheSystem()->GetContentDirs())
292 {
293 ResourceGroupManager::getSingleton().addResourceLocation(PathCombine(App::sys_user_dir->getStr(), dirname), "FileSystem", RGN_CONTENT);
294 }
295 ResourceGroupManager::getSingleton().addResourceLocation(PathCombine(App::sys_process_dir->getStr(), "content") , "FileSystem", RGN_CONTENT);
296 std::string objects = PathCombine("resources", "beamobjects.zip");
297 ResourceGroupManager::getSingleton().addResourceLocation(PathCombine(App::sys_process_dir->getStr(), objects) , "Zip" , RGN_CONTENT);
298 std::string dashboards = PathCombine("resources", "dashboards.zip");
299 ResourceGroupManager::getSingleton().addResourceLocation(PathCombine(App::sys_process_dir->getStr(), dashboards), "Zip", RGN_CONTENT);
300 std::string gadgets = PathCombine("resources", "gadgets.zip");
301 ResourceGroupManager::getSingleton().addResourceLocation(PathCombine(App::sys_process_dir->getStr(), gadgets), "Zip", RGN_CONTENT);
302
303 // Create RGN_TEMP in recursive mode to find all subdirectories.
304
305 ResourceGroupManager::getSingleton().createResourceGroup(RGN_TEMP, false);
306 if (!App::app_extra_mod_path->getStr().empty())
307 {
308 std::string extra_mod_path = App::app_extra_mod_path->getStr();
309 ResourceGroupManager::getSingleton().addResourceLocation(extra_mod_path , "FileSystem", RGN_TEMP, true);
310 }
311 for (const std::string& dirname : App::GetCacheSystem()->GetContentDirs())
312 {
313 ResourceGroupManager::getSingleton().addResourceLocation(PathCombine(App::sys_user_dir->getStr(), dirname), "FileSystem", RGN_TEMP, true);
314 }
315 ResourceGroupManager::getSingleton().addResourceLocation(PathCombine(App::sys_process_dir->getStr(), "content") , "FileSystem", RGN_TEMP, true);
316
317 // Traverse RGN_TEMP and add all subdirectories to RGN_CONTENT.
318 // NOTE we can't use OGRE's 'recursive' mode because it prepends subdir names to the resource name, and we need bare filenames.
319
320 FileInfoListPtr dirs = ResourceGroupManager::getSingleton().findResourceFileInfo(RGN_TEMP, "*", /*dirs:*/true);
321 for (const auto& dir_fileinfo : *dirs)
322 {
323 if (!dir_fileinfo.archive)
324 continue;
325 String fullpath = PathCombine(dir_fileinfo.archive->getName(), dir_fileinfo.filename);
326 ResourceGroupManager::getSingleton().addResourceLocation(fullpath, "FileSystem", RGN_CONTENT, /*recursive:*/false, /*readonly:*/false);
327 }
328 ResourceGroupManager::getSingleton().destroyResourceGroup(RGN_TEMP);
329
330 // Traverse RGN_CONTENT and detect updates
331
332 if (validity == CacheValidity::UNKNOWN)
333 {
334 validity = App::GetCacheSystem()->EvaluateCacheValidity(); // Must be called while RGN_CONTENT is alive.
335 }
337
338 ResourceGroupManager::getSingleton().destroyResourceGroup(RGN_CONTENT);
339
340}
341
342Ogre::DataStreamPtr ContentManager::resourceLoading(const Ogre::String& name, const Ogre::String& group, Ogre::Resource* resource)
343{
344 return Ogre::DataStreamPtr();
345}
346
347void ContentManager::resourceStreamOpened(const Ogre::String& name, const Ogre::String& group, Ogre::Resource* resource, Ogre::DataStreamPtr& dataStream)
348{
349}
350
351bool ContentManager::resourceCollision(Ogre::Resource* resource, Ogre::ResourceManager* resourceManager)
352{
353 // RoR loads each resource bundle (see CacheSystem.h for info)
354 // into dedicated resource group outside the global pool [see CacheSystem::LoadResource()]
355 // This means resource collision is pretty much content creator's fault, with 2 exceptions:
356 // * asset packs (introduced 2024) are mixed into the requesting mod's resource group.
357 // * bundled resources (e.g. beamobjects.zip) are also mixed into the mod's resource group.
358 RoR::LogFormat("[RoR|ContentManager] Skipping resource with duplicate name: '%s' (origin: '%s')",
359 resource->getName().c_str(), resource->getOrigin().c_str());
360 return false; // Instruct OGRE to drop the new resource and keep the original.
361}
362
363bool ContentManager::handleEvent(ScriptCompiler *compiler, ScriptCompilerEvent *evt, void *retval)
364{
365 if (evt->mType == CreateMaterialScriptCompilerEvent::eventType)
366 {
367 // Workaround for OGRE script compiler not properly checking that material name is not empty.
368 // See https://github.com/RigsOfRods/rigs-of-rods/issues/2349
369 auto* matEvent = static_cast<CreateMaterialScriptCompilerEvent*>(evt);
370 if (matEvent->mName.empty())
371 {
372 RoR::LogFormat("[RoR] Got malformed material (empty name) from file: '%s' - forcing OGRE to fail loading.",
373 matEvent->mFile.c_str());
374 // Report "handled" but create nothing -> OGRE will interrupt the loading
375 // with message "failed to find or create material" [in MaterialTranslator::translate()]
376 return true;
377 }
378 }
379 else if (evt->mType == CreateParticleSystemScriptCompilerEvent::eventType)
380 {
381 // Workaround for OGRE ignoring resource groups when registering particle templates
382 // See https://github.com/RigsOfRods/rigs-of-rods/pull/2398
383 auto* particleEvent = static_cast<CreateParticleSystemScriptCompilerEvent*>(evt);
384 if (Ogre::ParticleSystemManager::getSingleton().getTemplate(particleEvent->mName) != nullptr)
385 {
386 // Duplicate name -> OGRE would throw exception and fail initializing whole resource group
387 RoR::LogFormat("[RoR] Duplicate particle system name '%s' in file: '%s' - forcing OGRE to fail loading.",
388 particleEvent->mName.c_str(), particleEvent->mFile.c_str());
389 return true; // Instruct OGRE to skip the particle system
390 }
391 }
392#if OGRE_VERSION < 0x010C08 // OGRE 1.12.8+ does this itself, see `D3D9HLSLProgram::getTarget()`
393 else if (evt->mType == CreateHighLevelGpuProgramScriptCompilerEvent::eventType)
394 {
395 // Backport of the fallback HLSL target, so scripts written for newer OGRE may omit `target`.
396 // OGRE 1.11 leaves it empty, which fails `isSupported()` already when the material is compiled.
397 // Unlike OGRE, prefer shader model 3: 'OgreUnifiedShader.h' fragment shaders declare a `VPOS` input
398 // and commonly `FOG`, neither of which ps_2_0 accepts. D3D11 maps these to its own profiles.
399 auto* progEvent = static_cast<CreateHighLevelGpuProgramScriptCompilerEvent*>(evt);
400 if (progEvent->mLanguage != "hlsl")
401 return false;
402
403 HighLevelGpuProgramPtr prog = HighLevelGpuProgramManager::getSingleton().createProgram(
404 progEvent->mName, progEvent->mResourceGroup, progEvent->mLanguage, progEvent->mProgramType);
405 if (prog) // Null if duplicate, see `resourceCollision()`
406 {
407 prog->setSourceFile(progEvent->mSource); // Normally done by OGRE, but not when we handle the event.
408 const String prefix = (progEvent->mProgramType == GPT_VERTEX_PROGRAM) ? "vs_" : "ps_";
409 const bool sm3 = GpuProgramManager::getSingleton().isSyntaxSupported(prefix + "3_0");
410 prog->setParameter("target", prefix + (sm3 ? "3_0" : "2_0"));
411 }
412 *static_cast<HighLevelGpuProgram**>(retval) = prog.get(); // Script properties, i.e. an explicit `target`, are applied afterwards.
413 return true;
414 }
415#endif
416
417 return false; // Report "not handled"
418}
419
420void ContentManager::InitManagedMaterials(std::string const & rg_name)
421{
422 Ogre::String managed_materials_dir = PathCombine(App::sys_resources_dir->getStr(), "managed_materials");
423
424 //Dirty, needs to be improved
425 if (App::gfx_shadow_type->getEnum<GfxShadowType>() == GfxShadowType::PSSM)
426 {
427 if (rg_name == RGN_MANAGED_MATS) // Only load shared resources on startup
428 {
429 ResourceGroupManager::getSingleton().addResourceLocation(PathCombine(managed_materials_dir, "shadows/pssm/on/shared"), "FileSystem", rg_name);
430 }
431 ResourceGroupManager::getSingleton().addResourceLocation(PathCombine(managed_materials_dir, "shadows/pssm/on"), "FileSystem", rg_name);
432 }
433 else
434 {
435 ResourceGroupManager::getSingleton().addResourceLocation(PathCombine(managed_materials_dir,"shadows/pssm/off"), "FileSystem", rg_name);
436 }
437
438 ResourceGroupManager::getSingleton().addResourceLocation(PathCombine(managed_materials_dir, "texture"), "FileSystem", rg_name);
439
440 // Last
441 ResourceGroupManager::getSingleton().addResourceLocation(managed_materials_dir, "FileSystem", rg_name);
442
443 if (rg_name == RGN_MANAGED_MATS) // Only initialize the global resource group
444 ResourceGroupManager::getSingleton().initialiseResourceGroup(rg_name);
445}
446
474
476{
477 std::stringstream buf;
478
479 auto dir_list = Ogre::ResourceGroupManager::getSingleton().listResourceFileInfo(RGN_CONTENT, true);
480 for (auto dir: *dir_list)
481 {
482 buf << dir.filename << std::endl;
483 }
484
485 // Any filename + listed extensions, ignore case
486 std::regex file_whitelist("^.\\.(airplane|boat|car|fixed|load|machine|skin|terrn2|train|truck)$", std::regex::icase);
487
488 auto file_list = Ogre::ResourceGroupManager::getSingleton().listResourceFileInfo(RGN_CONTENT, false);
489 for (auto file: *file_list)
490 {
491 if ((file.archive != nullptr) || std::regex_match(file.filename, file_whitelist))
492 {
493 buf << file.filename << std::endl;
494 }
495 }
496
497 return buf.str();
498}
499
500bool ContentManager::LoadAndParseJson(std::string const& filename, std::string const& rg_name, rapidjson::Document& j_doc)
501{
502 try
503 {
504 Ogre::DataStreamPtr stream = Ogre::ResourceGroupManager::getSingleton().openResource(filename, rg_name);
505 Ogre::String json_str = stream->getAsString();
506 rapidjson::MemoryStream j_stream(json_str.data(), json_str.length());
507 j_doc.ParseStream<rapidjson::kParseNanAndInfFlag>(j_stream);
508 }
509 catch (Ogre::FileNotFoundException)
510 {
511 return false; // Error already logged by OGRE
512 }
513 catch (std::exception& e)
514 {
515 RoR::LogFormat("[RoR] Failed to open or read json file '%s' (resource group '%s'), message: '%s'",
516 filename.c_str(), rg_name.c_str(), e.what());
517 return false;
518 }
519
520 if (j_doc.HasParseError())
521 {
522 RoR::LogFormat("[RoR] Error parsing JSON file '%s' (resource group '%s')",
523 filename.c_str(), rg_name.c_str());
524 return false;
525 }
526
527 return true;
528}
529
530bool ContentManager::SerializeAndWriteJson(std::string const& filename, std::string const& rg_name, rapidjson::Document& j_doc)
531{
532 // Serialize JSON to string
533 rapidjson::StringBuffer buffer;
534 rapidjson::Writer<rapidjson::StringBuffer, rapidjson::UTF8<>, rapidjson::UTF8<>,
535 rapidjson::CrtAllocator, rapidjson::kWriteNanAndInfFlag>
536 writer(buffer);
537 j_doc.Accept(writer);
538
539 // Write JSON to file
540 try
541 {
542 Ogre::DataStreamPtr stream
543 = Ogre::ResourceGroupManager::getSingleton().createResource(
544 filename, rg_name, /*overwrite=*/true);
545 size_t written = stream->write(buffer.GetString(), buffer.GetSize());
546 if (written < buffer.GetSize())
547 {
548 RoR::LogFormat("[RoR] Error writing JSON file '%s' (resource group '%s'), ",
549 "only written %u out of %u bytes!",
550 filename.c_str(), rg_name.c_str(), written, buffer.GetSize());
551 return false;
552 }
553 return true;
554 }
555 catch (std::exception& e)
556 {
557 RoR::LogFormat("[RoR] Error writing JSON file '%s' (resource group '%s'), message: '%s'",
558 filename.c_str(), rg_name.c_str(), e.what());
559 return false;
560 }
561}
562
563bool ContentManager::DeleteDiskFile(std::string const& filename, std::string const& rg_name)
564{
565 try
566 {
567 Ogre::ResourceGroupManager::getSingleton().deleteResource(filename, rg_name);
568 return true;
569 }
570 catch (std::exception& e)
571 {
572 RoR::LogFormat("[RoR|ModCache] Error deleting file '%s' (resource group '%s'), message: '%s'",
573 filename.c_str(), rg_name.c_str(), e.what());
574 return false;
575 }
576}
577
Central state/object manager and communications hub.
#define RGN_TEMP
Definition Application.h:45
#define RGN_CONTENT
Definition Application.h:50
#define RGN_REPO_ATTACHMENTS
Definition Application.h:48
#define RGN_CONFIG
Definition Application.h:49
#define RGN_SERVER_SCRIPTS
Definition Application.h:54
#define RGN_MANAGED_MATS
Definition Application.h:52
#define RGN_THUMBNAILS
Definition Application.h:47
void LOG(const char *msg)
Legacy alias - formerly a macro.
#define RGN_CACHE
Definition Application.h:46
#define RGN_SCRIPTS
Definition Application.h:53
#define RGN_LOGS
Definition Application.h:55
#define RGN_SAVEGAMES
Definition Application.h:51
A database of user-installed content alias 'mods' (vehicles, terrains...)
#define DECLARE_RESOURCE_PACK(_FIELD_, _NAME_, _RESOURCE_GROUP_)
Platform-specific utilities. We use narrow UTF-8 encoded strings as paths. Inspired by http://utf8eve...
Factory for creating TextAreaOverlayElement instances.
Factory class for DeflectorPlaneAffector.
Factory class for DeflectorPlaneAffector.
Factory class for ShaderParticleRenderer.
std::string const & getStr() const
Definition CVar.h:95
CacheValidity EvaluateCacheValidity()
const std::vector< std::string > & GetContentDirs() const
void LoadModCache(CacheValidity validity)
bool resourceCollision(Ogre::Resource *resource, Ogre::ResourceManager *resourceManager) override
Ogre::DataStreamPtr resourceLoading(const Ogre::String &name, const Ogre::String &group, Ogre::Resource *resource) override
void AddResourcePack(ResourcePack const &resource_pack, std::string const &override_rgn="")
Loads resources if not already loaded.
bool handleEvent(Ogre::ScriptCompiler *compiler, Ogre::ScriptCompilerEvent *evt, void *retval) override
bool DeleteDiskFile(std::string const &filename, std::string const &rg_name)
bool LoadAndParseJson(std::string const &filename, std::string const &rg_name, rapidjson::Document &j_doc)
bool SerializeAndWriteJson(std::string const &filename, std::string const &rg_name, rapidjson::Document &j_doc)
void resourceStreamOpened(const Ogre::String &name, const Ogre::String &group, Ogre::Resource *resource, Ogre::DataStreamPtr &dataStream) override
void InitModCache(CacheValidity validity)
std::string ListAllUserContent()
Used by ModCache for quick detection of added/removed content.
void LoadGameplayResources()
Checks GVar settings and loads required resources.
void InitManagedMaterials(std::string const &rg_name)
void setLoadingBaseSounds(bool value)
bool FolderExists(const char *path)
Path must be UTF-8 encoded.
std::string PathCombine(std::string a, std::string b)
bool FileExists(const char *path)
Path must be UTF-8 encoded.
CVar * gfx_sky_mode
CVar * sys_repo_attachments_dir
CVar * gfx_water_mode
CVar * sys_logs_dir
CVar * gfx_anisotropy
SoundScriptManager * GetSoundScriptManager()
CVar * sys_config_dir
CVar * sys_savegames_dir
CVar * sys_server_scripts_dir
CVar * sys_user_dir
CVar * sys_cache_dir
CVar * gfx_vegetation_mode
CacheSystem * GetCacheSystem()
CVar * gfx_texture_filter
CVar * sys_resources_dir
void CreateSoundScriptManager()
CVar * sys_scripts_dir
CVar * sys_thumbnails_dir
CVar * app_extra_mod_path
CVar * gfx_shadow_type
CVar * sys_process_dir
CacheValidity
void LogFormat(const char *format,...)
Improved logging utility. Uses fixed 2Kb buffer.
@ CAELUM
Caelum (best looking, slower)
@ SKYX
SkyX (best looking, slower)
@ DEPTHMAP_ENABLED
static const ResourcePack HYDRAX
static const ResourcePack DASHBOARDS
static const ResourcePack OVERLAYS
static const ResourcePack PARTICLES
static const ResourcePack TEXTURES
static const ResourcePack MESHES
static const ResourcePack SOUNDS
static const ResourcePack MYGUI
static const ResourcePack AIRFOILS
static const ResourcePack SKYX
static const ResourcePack CAELUM
static const ResourcePack FAMICONS
static const ResourcePack MATERIALS
static const ResourcePack PAGED