RigsofRods
Soft-body Physics Simulation
CacheSystem.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-2023 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 
25 
26 #include "CacheSystem.h"
27 
28 #include "Actor.h"
29 #include "AddonPartFileFormat.h"
30 #include "Application.h"
31 #include "SimData.h"
32 #include "ContentManager.h"
33 #include "ErrorUtils.h"
34 #include "GUI_LoadingWindow.h"
35 #include "GUI_GameMainMenu.h"
36 #include "GUIManager.h"
37 #include "GenericFileFormat.h"
38 #include "GfxActor.h"
39 #include "GfxScene.h"
40 #include "Language.h"
41 #include "PlatformUtils.h"
42 #include "RigDef_Parser.h"
43 #include "ScriptEngine.h"
44 #include "SkinFileFormat.h"
45 #include "Terrain.h"
46 #include "Terrn2FileFormat.h"
47 #include "TuneupFileFormat.h"
48 #include "Utils.h"
49 
50 #include <OgreException.h>
51 #include <OgreFileSystem.h>
52 #include <OgreFileSystemLayer.h>
53 #include <rapidjson/document.h>
54 #include <rapidjson/istreamwrapper.h>
55 #include <rapidjson/ostreamwrapper.h>
56 #include <rapidjson/writer.h>
57 #include <fstream>
58 
59 using namespace Ogre;
60 using namespace RoR;
61 
62 CacheEntry::CacheEntry() :
63  addtimestamp(0),
64  beamcount(0),
65  categoryid(CID_None),
66  commandscount(0),
67  custom_particles(false),
68  customtach(false),
69  deleted(false),
70  driveable(NOT_DRIVEABLE),
71  enginetype('t'), // enginetype = t = truck is default
72  exhaustscount(0),
73  fileformatversion(0),
74  filetime(0),
75  fixescount(0),
76  flarescount(0),
77  flexbodiescount(0),
78  forwardcommands(false),
79  hasSubmeshs(false),
80  hydroscount(0),
81  importcommands(false),
82  loadmass(0),
83  maxrpm(0),
84  minrpm(0),
85  nodecount(0),
86  number(0),
87  numgears(0),
88  propscount(0),
89  propwheelcount(0),
90  rescuer(false),
91  rotatorscount(0),
92  shockcount(0),
93  soundsourcescount(0),
94  torque(0),
95  truckmass(0),
96  turbojetcount(0),
97  turbopropscount(0),
98  usagecounter(0),
99  version(0),
100  wheelcount(0),
101  wingscount(0)
102 {
103 }
104 
106 {
107  // Destructs `TuneupDefPtr` which is a `RefCountingObjectPtr<>` so it doesn't compile without `#include "TuneupFileFormat.h"` and thus should not be in header.
108 }
109 
111 {
112  // Constructs `ActorPtr` - doesn't compile without `#include Actor.h` - not pretty if in header (even if auto-generated by C++).
113 }
114 
116 {
117  // Destructs `ActorPtr` - doesn't compile without `#include Actor.h` - not pretty if in header (even if auto-generated by C++).
118 }
119 
121  cqr_entry(entry),
122  cqr_score(score)
123 {}
124 
126 {
127  // register the extensions
128  m_known_extensions.push_back("machine");
129  m_known_extensions.push_back("fixed");
130  m_known_extensions.push_back("terrn2");
131  m_known_extensions.push_back("truck");
132  m_known_extensions.push_back("car");
133  m_known_extensions.push_back("boat");
134  m_known_extensions.push_back("airplane");
135  m_known_extensions.push_back("trailer");
136  m_known_extensions.push_back("load");
137  m_known_extensions.push_back("train");
138  m_known_extensions.push_back("skin");
139  m_known_extensions.push_back("addonpart");
140  m_known_extensions.push_back("tuneup");
141  m_known_extensions.push_back("assetpack");
142  m_known_extensions.push_back("dashboard");
143  m_known_extensions.push_back("gadget");
144 
145  // register the dirs
146  m_content_dirs.push_back("mods");
147  m_content_dirs.push_back("packs");
148  m_content_dirs.push_back("terrains");
149  m_content_dirs.push_back("vehicles");
150  m_content_dirs.push_back("projects");
151 }
152 
154 {
155  m_resource_paths.clear();
157 
158  if (validity != CacheValidity::VALID)
159  {
160  if (validity == CacheValidity::NEEDS_REBUILD)
161  {
162  RoR::Log("[RoR|ModCache] Performing rebuild ...");
163  this->ClearCache();
164  }
165  else
166  {
167  RoR::Log("[RoR|ModCache] Performing update ...");
168  this->ClearResourceGroups();
169  this->PruneCache();
170  }
171  const bool orig_echo = App::diag_log_console_echo->getBool();
176  this->DetectDuplicates();
177  this->WriteCacheFileJson();
178 
179  this->LoadCacheFileJson();
180  }
181 
182  RoR::Log("[RoR|ModCache] Cache loaded");
183  m_loaded = true;
184 }
185 
186 CacheEntryPtr CacheSystem::FindEntryByFilename(LoaderType type, bool partial, const std::string& _filename_maybe_bundlequalified)
187 {
188  // "Bundle-qualified" format also specifies the ZIP/directory in modcache, i.e. "mybundle.zip:myactor.truck"
189  // Like the filename, the bundle name lookup is case-insensitive.
190  // -------------------------------------------------------------------------------------------------
191 
192  std::string filename;
193  std::string bundlename;
194  SplitBundleQualifiedFilename(_filename_maybe_bundlequalified, bundlename, filename);
195  StringUtil::toLowerCase(filename);
196  StringUtil::toLowerCase(bundlename);
197  size_t partial_match_length = std::numeric_limits<size_t>::max();
198  CacheEntryPtr partial_match = nullptr;
199  std::vector<CacheEntryPtr> log_candidates;
200  for (CacheEntryPtr& entry : m_entries)
201  {
202  if ((type == LT_Terrain) != (entry->fext == "terrn2") ||
203  (type == LT_DashBoard) != (entry->fext == "dashboard") ||
204  (type == LT_AllBeam && entry->fext == "skin"))
205  continue;
206 
207  String fname = entry->fname;
208  String fname_without_uid = entry->fname_without_uid;
209  String bname;
210  String _path_placeholder;
211  StringUtil::splitFilename(entry->resource_bundle_path, bname, _path_placeholder);
212  StringUtil::toLowerCase(fname);
213  StringUtil::toLowerCase(fname_without_uid);
214  StringUtil::toLowerCase(bname);
215  if (fname == filename || fname_without_uid == filename)
216  {
217  if (bundlename == "" || bname == bundlename)
218  {
219  return entry;
220  }
221  else
222  {
223  log_candidates.push_back(entry);
224  }
225  }
226  else if (partial &&
227  fname.length() < partial_match_length &&
228  fname.find(filename) != std::string::npos)
229  {
230  if (bundlename == "" || bname == bundlename)
231  {
232  partial_match = entry;
233  partial_match_length = fname.length();
234  }
235  else
236  {
237  log_candidates.push_back(entry);
238  }
239  }
240  }
241 
242  if (log_candidates.size() > 0)
243  {
245  fmt::format(_LC("CacheSystem", "Mod '{}' was not found in cache; candidates ({}) are:"), _filename_maybe_bundlequalified, log_candidates.size()));
246  for (CacheEntryPtr& entry: log_candidates)
247  {
248  std::string bundle_name, bundle_path;
249  StringUtil::toLowerCase(bundle_name);
250  Ogre::StringUtil::splitFilename(entry->resource_bundle_path, bundle_name, bundle_path);
252  fmt::format(_LC("CacheSystem", "* {}:{}"), bundle_name, entry->fname));
253  }
254  }
255 
256  return (partial) ? partial_match : nullptr;
257 }
258 
260 {
262 
263  // Load cache file
264  CacheValidity validity = this->LoadCacheFileJson();
265 
266  if (validity != CacheValidity::VALID)
267  {
268  RoR::Log("[RoR|ModCache] Cannot load cache file: wrong version, corrupted or missing.");
269  return validity;
270  }
271 
272  // Compare stored hash with generated hash
274  {
275  RoR::Log("[RoR|ModCache] Cache file out of date");
277  }
278 
279  for (auto& entry : m_entries)
280  {
281  std::string fn = entry->resource_bundle_path;
282  if (entry->resource_bundle_type == "FileSystem")
283  {
284  fn = PathCombine(fn, entry->fname);
285  }
286 
287  if ((entry->filetime != RoR::GetFileLastModifiedTime(fn)))
288  {
290  }
291  }
292 
293  RoR::Log("[RoR|ModCache] Cache valid");
294  return CacheValidity::VALID;
295 }
296 
297 void CacheSystem::ImportEntryFromJson(rapidjson::Value& j_entry, CacheEntryPtr & out_entry)
298 {
299  // Common details
300  out_entry->usagecounter = j_entry["usagecounter"].GetInt();
301  out_entry->addtimestamp = j_entry["addtimestamp"].GetInt();
302  out_entry->resource_bundle_type = j_entry["resource_bundle_type"].GetString();
303  out_entry->resource_bundle_path = j_entry["resource_bundle_path"].GetString();
304  out_entry->fpath = j_entry["fpath"].GetString();
305  out_entry->fname = j_entry["fname"].GetString();
306  out_entry->fname_without_uid = j_entry["fname_without_uid"].GetString();
307  out_entry->fext = j_entry["fext"].GetString();
308  out_entry->filetime = j_entry["filetime"].GetInt();
309  out_entry->dname = j_entry["dname"].GetString();
310  out_entry->uniqueid = j_entry["uniqueid"].GetString();
311  out_entry->version = j_entry["version"].GetInt();
312  out_entry->filecachename = j_entry["filecachename"].GetString();
313 
314  out_entry->guid = j_entry["guid"].GetString();
315  Ogre::StringUtil::trim(out_entry->guid);
316 
317  // Category
318  int category_id = j_entry["categoryid"].GetInt();
319  auto category_itor = m_categories.find(category_id);
320  if (category_itor == m_categories.end() || category_id >= CID_Max)
321  {
322  category_itor = m_categories.find(CID_Unsorted);
323  }
324  out_entry->categoryname = category_itor->second;
325  out_entry->categoryid = category_itor->first;
326 
327  // Common - Authors
328  for (rapidjson::Value& j_author: j_entry["authors"].GetArray())
329  {
330  AuthorInfo author;
331 
332  author.type = j_author["type"].GetString();
333  author.name = j_author["name"].GetString();
334  author.email = j_author["email"].GetString();
335  author.id = j_author["id"].GetInt();
336 
337  out_entry->authors.push_back(author);
338  }
339 
340  // Vehicle details
341  out_entry->description = j_entry["description"].GetString();
342  out_entry->tags = j_entry["tags"].GetString();
343  out_entry->default_skin = j_entry["default_skin"].GetString();
344  out_entry->fileformatversion = j_entry["fileformatversion"].GetInt();
345  out_entry->hasSubmeshs = j_entry["hasSubmeshs"].GetBool();
346  out_entry->nodecount = j_entry["nodecount"].GetInt();
347  out_entry->beamcount = j_entry["beamcount"].GetInt();
348  out_entry->shockcount = j_entry["shockcount"].GetInt();
349  out_entry->fixescount = j_entry["fixescount"].GetInt();
350  out_entry->hydroscount = j_entry["hydroscount"].GetInt();
351  out_entry->wheelcount = j_entry["wheelcount"].GetInt();
352  out_entry->propwheelcount = j_entry["propwheelcount"].GetInt();
353  out_entry->commandscount = j_entry["commandscount"].GetInt();
354  out_entry->flarescount = j_entry["flarescount"].GetInt();
355  out_entry->propscount = j_entry["propscount"].GetInt();
356  out_entry->wingscount = j_entry["wingscount"].GetInt();
357  out_entry->turbopropscount = j_entry["turbopropscount"].GetInt();
358  out_entry->turbojetcount = j_entry["turbojetcount"].GetInt();
359  out_entry->rotatorscount = j_entry["rotatorscount"].GetInt();
360  out_entry->exhaustscount = j_entry["exhaustscount"].GetInt();
361  out_entry->flexbodiescount = j_entry["flexbodiescount"].GetInt();
362  out_entry->soundsourcescount = j_entry["soundsourcescount"].GetInt();
363  out_entry->truckmass = j_entry["truckmass"].GetFloat();
364  out_entry->loadmass = j_entry["loadmass"].GetFloat();
365  out_entry->minrpm = j_entry["minrpm"].GetFloat();
366  out_entry->maxrpm = j_entry["maxrpm"].GetFloat();
367  out_entry->torque = j_entry["torque"].GetFloat();
368  out_entry->customtach = j_entry["customtach"].GetBool();
369  out_entry->custom_particles = j_entry["custom_particles"].GetBool();
370  out_entry->forwardcommands = j_entry["forwardcommands"].GetBool();
371  out_entry->importcommands = j_entry["importcommands"].GetBool();
372  out_entry->rescuer = j_entry["rescuer"].GetBool();
373  out_entry->driveable = ActorType(j_entry["driveable"].GetInt());
374  out_entry->numgears = j_entry["numgears"].GetInt();
375  out_entry->enginetype = static_cast<char>(j_entry["enginetype"].GetInt());
376 
377  // Vehicle 'section-configs' (aka Modules in RigDef namespace)
378  for (rapidjson::Value& j_module_name: j_entry["sectionconfigs"].GetArray())
379  {
380  out_entry->sectionconfigs.push_back(j_module_name.GetString());
381  }
382 
383  // Addon part suggested mod guids
384  for (rapidjson::Value& j_addonguid: j_entry["addonpart_guids"].GetArray())
385  {
386  out_entry->addonpart_guids.insert(j_addonguid.GetString());
387  }
388 
389  // Addon part suggested mod filenames
390  for (rapidjson::Value& j_addonfname: j_entry["addonpart_filenames"].GetArray())
391  {
392  out_entry->addonpart_filenames.insert(j_addonfname.GetString());
393  }
394 
395  // Tuneup details
396  out_entry->tuneup_associated_filename = j_entry["tuneup_associated_filename"].GetString();
397 }
398 
400 {
401  // Clear existing entries
402  m_entries.clear();
403 
404  rapidjson::Document j_doc;
405  if (!App::GetContentManager()->LoadAndParseJson(CACHE_FILE, RGN_CACHE, j_doc) ||
406  !j_doc.IsObject() || !j_doc.HasMember("entries") || !j_doc["entries"].IsArray())
407  {
408  RoR::Log("[RoR|ModCache] Error, cache file still invalid after check/update, content selector will be empty.");
410  }
411 
412  if (j_doc["format_version"].GetInt() != CACHE_FILE_FORMAT)
413  {
414  RoR::Log("[RoR|ModCache] Invalid cache file format");
416  }
417 
418  for (rapidjson::Value& j_entry: j_doc["entries"].GetArray())
419  {
420  CacheEntryPtr entry = new CacheEntry();
421  this->ImportEntryFromJson(j_entry, entry);
422  entry->number = static_cast<int>(m_entries.size() + 1); // Let's number mods from 1
423  m_entries.push_back(entry);
424  }
425 
426  m_filenames_hash_loaded = j_doc["global_hash"].GetString();
427 
428  return CacheValidity::VALID;
429 }
430 
432 {
433  this->LoadCacheFileJson();
434 
435  std::vector<String> paths;
436  for (auto& entry : m_entries)
437  {
438  std::string fn = entry->resource_bundle_path;
439  if (entry->resource_bundle_type == "FileSystem")
440  {
441  fn = PathCombine(fn, entry->fname);
442  }
443 
444  if (!RoR::FileExists(fn.c_str()) || (entry->filetime != RoR::GetFileLastModifiedTime(fn)))
445  {
446  if (!entry->deleted)
447  {
448  if (std::find(paths.begin(), paths.end(), fn) == paths.end())
449  {
450  RoR::LogFormat("[RoR|ModCache] Removing '%s'", fn.c_str());
451  paths.push_back(fn);
452  }
453  this->RemoveFileCache(entry);
454  }
455  entry->deleted = true;
456  }
457  else
458  {
459  m_resource_paths.insert(fn);
460  }
461  }
462 }
463 
465 {
466  for (auto& entry : m_entries)
467  {
468  String group = entry->resource_group;
469  if (!group.empty())
470  {
471  if (ResourceGroupManager::getSingleton().resourceGroupExists(group))
472  ResourceGroupManager::getSingleton().destroyResourceGroup(group);
473  }
474  }
475 }
476 
478 {
479  RoR::Log("[RoR|ModCache] Searching for duplicates ...");
480  std::map<String, String> possible_duplicates;
481  for (int i=0; i<m_entries.size(); i++)
482  {
483  CacheEntryPtr entryA = m_entries[i];
484 
485  if (entryA->deleted)
486  continue;
487 
488  String dnameA = entryA->dname;
489  StringUtil::toLowerCase(dnameA);
490  StringUtil::trim(dnameA);
491  String dirA = entryA->resource_bundle_path;
492  StringUtil::toLowerCase(dirA);
493  String basenameA, basepathA;
494  StringUtil::splitFilename(dirA, basenameA, basepathA);
495  String filenameWUIDA = entryA->fname_without_uid;
496  StringUtil::toLowerCase(filenameWUIDA);
497 
498  for (int j=i+1; j<m_entries.size(); j++)
499  {
500  CacheEntryPtr entryB = m_entries[j];
501 
502  if (entryB->deleted)
503  continue;
504 
505  String filenameWUIDB = entryB->fname_without_uid;
506  StringUtil::toLowerCase(filenameWUIDB);
507  if (filenameWUIDA != filenameWUIDB)
508  continue;
509 
510  String dnameB = entryB->dname;
511  StringUtil::toLowerCase(dnameB);
512  StringUtil::trim(dnameB);
513  if (dnameA != dnameB)
514  continue;
515 
516  String dirB = entryB->resource_bundle_path;
517  StringUtil::toLowerCase(dirB);
518  String basenameB, basepathB;
519  StringUtil::splitFilename(dirB, basenameB, basepathB);
520  basenameA = Ogre::StringUtil::replaceAll(basenameA, " ", "_");
521  basenameA = Ogre::StringUtil::replaceAll(basenameA, "-", "_");
522  basenameB = Ogre::StringUtil::replaceAll(basenameB, " ", "_");
523  basenameB = Ogre::StringUtil::replaceAll(basenameB, "-", "_");
524  if (StripSHA1fromString(basenameA) != StripSHA1fromString(basenameB))
525  continue;
526 
527  if (entryA->resource_bundle_path == entryB->resource_bundle_path)
528  {
529  LOG("- duplicate: " + entryA->fpath + entryA->fname
530  + " <--> " + entryB->fpath + entryB->fname);
531  LOG(" - " + entryB->resource_bundle_path);
532  int idx = entryA->fpath.size() < entryB->fpath.size() ? i : j;
533  m_entries[idx]->deleted = true;
534  }
535  else
536  {
537  possible_duplicates[entryA->resource_bundle_path] = entryB->resource_bundle_path;
538  }
539  }
540  }
541  for (auto duplicate : possible_duplicates)
542  {
543  LOG("- possible duplicate: ");
544  LOG(" - " + duplicate.first);
545  LOG(" - " + duplicate.second);
546  }
547 }
548 
550 {
551  for (CacheEntryPtr& entry: m_entries)
552  {
553  if (modid == entry->number)
554  return entry;
555  }
556  return 0;
557 }
558 
559 String CacheSystem::GetPrettyName(String fname)
560 {
561  for (CacheEntryPtr& entry: m_entries)
562  {
563  if (fname == entry->fname)
564  return entry->dname;
565  }
566  return "";
567 }
568 
570 {
571  switch (driveable)
572  {
573  case ActorType::NOT_DRIVEABLE: return _LC("MainSelector", "Non-Driveable");
574  case ActorType::TRUCK: return _LC("MainSelector", "Truck");
575  case ActorType::AIRPLANE: return _LC("MainSelector", "Airplane");
576  case ActorType::BOAT: return _LC("MainSelector", "Boat");
577  case ActorType::MACHINE: return _LC("MainSelector", "Machine");
578  case ActorType::AI: return _LC("MainSelector", "A.I.");
579  default: return "";
580  };
581 }
582 
583 void CacheSystem::ExportEntryToJson(rapidjson::Value& j_entries, rapidjson::Document& j_doc, CacheEntryPtr const & entry)
584 {
585  rapidjson::Value j_entry(rapidjson::kObjectType);
586 
587  // Common details
588  j_entry.AddMember("usagecounter", entry->usagecounter, j_doc.GetAllocator());
589  j_entry.AddMember("addtimestamp", static_cast<int64_t>(entry->addtimestamp), j_doc.GetAllocator());
590  j_entry.AddMember("resource_bundle_type", rapidjson::StringRef(entry->resource_bundle_type.c_str()), j_doc.GetAllocator());
591  j_entry.AddMember("resource_bundle_path", rapidjson::StringRef(entry->resource_bundle_path.c_str()), j_doc.GetAllocator());
592  j_entry.AddMember("fpath", rapidjson::StringRef(entry->fpath.c_str()), j_doc.GetAllocator());
593  j_entry.AddMember("fname", rapidjson::StringRef(entry->fname.c_str()), j_doc.GetAllocator());
594  j_entry.AddMember("fname_without_uid", rapidjson::StringRef(entry->fname_without_uid.c_str()), j_doc.GetAllocator());
595  j_entry.AddMember("fext", rapidjson::StringRef(entry->fext.c_str()), j_doc.GetAllocator());
596  j_entry.AddMember("filetime", static_cast<int64_t>(entry->filetime), j_doc.GetAllocator());
597  j_entry.AddMember("dname", rapidjson::StringRef(entry->dname.c_str()), j_doc.GetAllocator());
598  j_entry.AddMember("categoryid", entry->categoryid, j_doc.GetAllocator());
599  j_entry.AddMember("uniqueid", rapidjson::StringRef(entry->uniqueid.c_str()), j_doc.GetAllocator());
600  j_entry.AddMember("guid", rapidjson::StringRef(entry->guid.c_str()), j_doc.GetAllocator());
601  j_entry.AddMember("version", entry->version, j_doc.GetAllocator());
602  j_entry.AddMember("filecachename", rapidjson::StringRef(entry->filecachename.c_str()), j_doc.GetAllocator());
603 
604  // Common - Authors
605  rapidjson::Value j_authors(rapidjson::kArrayType);
606  for (AuthorInfo const& author: entry->authors)
607  {
608  rapidjson::Value j_author(rapidjson::kObjectType);
609 
610  j_author.AddMember("type", rapidjson::StringRef(author.type.c_str()), j_doc.GetAllocator());
611  j_author.AddMember("name", rapidjson::StringRef(author.name.c_str()), j_doc.GetAllocator());
612  j_author.AddMember("email", rapidjson::StringRef(author.email.c_str()), j_doc.GetAllocator());
613  j_author.AddMember("id", author.id, j_doc.GetAllocator());
614 
615  j_authors.PushBack(j_author, j_doc.GetAllocator());
616  }
617  j_entry.AddMember("authors", j_authors, j_doc.GetAllocator());
618 
619  // Vehicle details
620  j_entry.AddMember("description", rapidjson::StringRef(entry->description.c_str()), j_doc.GetAllocator());
621  j_entry.AddMember("tags", rapidjson::StringRef(entry->tags.c_str()), j_doc.GetAllocator());
622  j_entry.AddMember("default_skin", rapidjson::StringRef(entry->default_skin.c_str()), j_doc.GetAllocator());
623  j_entry.AddMember("fileformatversion", entry->fileformatversion, j_doc.GetAllocator());
624  j_entry.AddMember("hasSubmeshs", entry->hasSubmeshs, j_doc.GetAllocator());
625  j_entry.AddMember("nodecount", entry->nodecount, j_doc.GetAllocator());
626  j_entry.AddMember("beamcount", entry->beamcount, j_doc.GetAllocator());
627  j_entry.AddMember("shockcount", entry->shockcount, j_doc.GetAllocator());
628  j_entry.AddMember("fixescount", entry->fixescount, j_doc.GetAllocator());
629  j_entry.AddMember("hydroscount", entry->hydroscount, j_doc.GetAllocator());
630  j_entry.AddMember("wheelcount", entry->wheelcount, j_doc.GetAllocator());
631  j_entry.AddMember("propwheelcount", entry->propwheelcount, j_doc.GetAllocator());
632  j_entry.AddMember("commandscount", entry->commandscount, j_doc.GetAllocator());
633  j_entry.AddMember("flarescount", entry->flarescount, j_doc.GetAllocator());
634  j_entry.AddMember("propscount", entry->propscount, j_doc.GetAllocator());
635  j_entry.AddMember("wingscount", entry->wingscount, j_doc.GetAllocator());
636  j_entry.AddMember("turbopropscount", entry->turbopropscount, j_doc.GetAllocator());
637  j_entry.AddMember("turbojetcount", entry->turbojetcount, j_doc.GetAllocator());
638  j_entry.AddMember("rotatorscount", entry->rotatorscount, j_doc.GetAllocator());
639  j_entry.AddMember("exhaustscount", entry->exhaustscount, j_doc.GetAllocator());
640  j_entry.AddMember("flexbodiescount", entry->flexbodiescount, j_doc.GetAllocator());
641  j_entry.AddMember("soundsourcescount", entry->soundsourcescount, j_doc.GetAllocator());
642  j_entry.AddMember("truckmass", entry->truckmass, j_doc.GetAllocator());
643  j_entry.AddMember("loadmass", entry->loadmass, j_doc.GetAllocator());
644  j_entry.AddMember("minrpm", entry->minrpm, j_doc.GetAllocator());
645  j_entry.AddMember("maxrpm", entry->maxrpm, j_doc.GetAllocator());
646  j_entry.AddMember("torque", entry->torque, j_doc.GetAllocator());
647  j_entry.AddMember("customtach", entry->customtach, j_doc.GetAllocator());
648  j_entry.AddMember("custom_particles", entry->custom_particles, j_doc.GetAllocator());
649  j_entry.AddMember("forwardcommands", entry->forwardcommands, j_doc.GetAllocator());
650  j_entry.AddMember("importcommands", entry->importcommands, j_doc.GetAllocator());
651  j_entry.AddMember("rescuer", entry->rescuer, j_doc.GetAllocator());
652  j_entry.AddMember("driveable", entry->driveable, j_doc.GetAllocator());
653  j_entry.AddMember("numgears", entry->numgears, j_doc.GetAllocator());
654  j_entry.AddMember("enginetype", entry->enginetype, j_doc.GetAllocator());
655 
656  // Vehicle 'section-configs' (aka Modules in RigDef namespace)
657  rapidjson::Value j_sectionconfigs(rapidjson::kArrayType);
658  for (std::string const & module_name: entry->sectionconfigs)
659  {
660  j_sectionconfigs.PushBack(rapidjson::StringRef(module_name.c_str()), j_doc.GetAllocator());
661  }
662  j_entry.AddMember("sectionconfigs", j_sectionconfigs, j_doc.GetAllocator());
663 
664  // Addon part details
665  rapidjson::Value j_addonguids(rapidjson::kArrayType);
666  for (std::string const & ag: entry->addonpart_guids)
667  {
668  j_addonguids.PushBack(rapidjson::StringRef(ag.c_str()), j_doc.GetAllocator());
669  }
670  j_entry.AddMember("addonpart_guids", j_addonguids, j_doc.GetAllocator());
671 
672  rapidjson::Value j_addonfnames(rapidjson::kArrayType);
673  for (std::string const & ag: entry->addonpart_filenames)
674  {
675  j_addonfnames.PushBack(rapidjson::StringRef(ag.c_str()), j_doc.GetAllocator());
676  }
677  j_entry.AddMember("addonpart_filenames", j_addonfnames, j_doc.GetAllocator());
678 
679  // Tuneup details
680  j_entry.AddMember("tuneup_associated_filename", rapidjson::StringRef(entry->tuneup_associated_filename.c_str()), j_doc.GetAllocator());
681 
682  // Add entry to list
683  j_entries.PushBack(j_entry, j_doc.GetAllocator());
684 }
685 
687 {
688  // Basic file structure
689  rapidjson::Document j_doc;
690  j_doc.SetObject();
691  j_doc.AddMember("format_version", CACHE_FILE_FORMAT, j_doc.GetAllocator());
692  j_doc.AddMember("global_hash", rapidjson::StringRef(m_filenames_hash_generated.c_str()), j_doc.GetAllocator());
693 
694  // Entries
695  rapidjson::Value j_entries(rapidjson::kArrayType);
696  for (CacheEntryPtr const& entry : m_entries)
697  {
698  if (!entry->deleted)
699  {
700  this->ExportEntryToJson(j_entries, j_doc, entry);
701  }
702  }
703  j_doc.AddMember("entries", j_entries, j_doc.GetAllocator());
704 
705  // Write to file
706  if (App::GetContentManager()->SerializeAndWriteJson(CACHE_FILE, RGN_CACHE, j_doc)) // Logs errors
707  {
708  RoR::LogFormat("[RoR|ModCache] File '%s' written OK", CACHE_FILE);
709  }
710 }
711 
713 {
715  for (auto& entry : m_entries)
716  {
717  String group = entry->resource_group;
718  if (!group.empty())
719  {
720  if (ResourceGroupManager::getSingleton().resourceGroupExists(group))
721  ResourceGroupManager::getSingleton().destroyResourceGroup(group);
722  }
723  this->RemoveFileCache(entry);
724  }
725  m_entries.clear();
726 }
727 
728 Ogre::String CacheSystem::StripUIDfromString(Ogre::String uidstr)
729 {
730  size_t pos = uidstr.find("-");
731  if (pos != String::npos && pos >= 3 && uidstr.substr(pos - 3, 3) == "UID")
732  return uidstr.substr(pos + 1, uidstr.length() - pos);
733  return uidstr;
734 }
735 
736 Ogre::String CacheSystem::StripSHA1fromString(Ogre::String sha1str)
737 {
738  size_t pos = sha1str.find_first_of("-_");
739  if (pos != String::npos && pos >= 20)
740  return sha1str.substr(pos + 1, sha1str.length() - pos);
741  return sha1str;
742 }
743 
744 void CacheSystem::AddFile(String group, Ogre::FileInfo f, String ext)
745 {
746  String type = f.archive ? f.archive->getType() : "FileSystem";
747  String path = f.archive ? f.archive->getName() : "";
748 
749  if (std::find_if(m_entries.begin(), m_entries.end(), [&](CacheEntryPtr& entry)
750  { return !entry->deleted && entry->fname == f.filename && entry->resource_bundle_path == path; }) != m_entries.end())
751  return;
752 
753  RoR::LogFormat("[RoR|CacheSystem] Preparing to add file '%f'", f.filename.c_str());
754 
755  try
756  {
757  DataStreamPtr ds = ResourceGroupManager::getSingleton().openResource(f.filename, group);
758  // ds closes automatically, so do _not_ close it explicitly below
759 
760  std::vector<CacheEntryPtr> new_entries;
761  if (ext == "terrn2")
762  {
763  CacheEntryPtr entry = new CacheEntry();
764  FillTerrainDetailInfo(entry, ds, f.filename);
765  new_entries.push_back(entry);
766  }
767  else if (ext == "skin")
768  {
769  auto new_skins = RoR::SkinParser::ParseSkins(ds);
770  for (auto skin_def: new_skins)
771  {
772  CacheEntryPtr entry = new CacheEntry();
773  FillSkinDetailInfo(entry, skin_def);
774  new_entries.push_back(entry);
775  }
776  }
777  else if (ext == "addonpart")
778  {
779  CacheEntryPtr entry = new CacheEntry();
780  FillAddonPartDetailInfo(entry, ds);
781  new_entries.push_back(entry);
782  }
783  else if (ext == "tuneup")
784  {
785  auto new_tuneups = RoR::TuneupUtil::ParseTuneups(ds);
786  for (auto tuneup_def: new_tuneups)
787  {
788  CacheEntryPtr entry = new CacheEntry();
789  FillTuneupDetailInfo(entry, tuneup_def);
790  new_entries.push_back(entry);
791  }
792  }
793  else if (ext == "assetpack")
794  {
795  CacheEntryPtr entry = new CacheEntry();
796  FillAssetPackDetailInfo(entry, ds);
797  new_entries.push_back(entry);
798  }
799  else if (ext == "dashboard")
800  {
801  CacheEntryPtr entry = new CacheEntry();
802  FillDashboardDetailInfo(entry, ds);
803  new_entries.push_back(entry);
804  }
805  else if (ext == "gadget")
806  {
807  CacheEntryPtr entry = new CacheEntry();
808  FillGadgetDetailInfo(entry, ds);
809  new_entries.push_back(entry);
810  }
811  else
812  {
813  CacheEntryPtr entry = new CacheEntry();
814  FillTruckDetailInfo(entry, ds, f.filename, group);
815  new_entries.push_back(entry);
816  }
817 
818  for (auto& entry: new_entries)
819  {
820  Ogre::StringUtil::toLowerCase(entry->guid); // Important for comparsion
821  entry->fpath = f.path;
822  entry->fname = f.filename;
823  entry->fname_without_uid = StripUIDfromString(f.filename);
824  entry->fext = ext;
825  if (type == "Zip")
826  {
827  entry->filetime = RoR::GetFileLastModifiedTime(path);
828  }
829  else
830  {
831  entry->filetime = RoR::GetFileLastModifiedTime(PathCombine(path, f.filename));
832  }
833  entry->resource_bundle_type = type;
834  entry->resource_bundle_path = path;
835  entry->number = static_cast<int>(m_entries.size() + 1); // Let's number mods from 1
836  entry->addtimestamp = m_update_time;
837  this->GenerateFileCache(entry, group);
838  m_entries.push_back(entry);
839  }
840  }
841  catch (Ogre::Exception& e)
842  {
843  RoR::LogFormat("[RoR|CacheSystem] Error processing file '%s', message :%s",
844  f.filename.c_str(), e.getFullDescription().c_str());
845  }
846 }
847 
848 void CacheSystem::FillTruckDetailInfo(CacheEntryPtr& entry, Ogre::DataStreamPtr stream, String file_name, String group)
849 {
850  /* LOAD AND PARSE THE VEHICLE */
851  RigDef::Parser parser;
852  parser.Prepare();
853  parser.ProcessOgreStream(stream.getPointer(), group);
854  parser.GetSequentialImporter()->Disable();
855  parser.Finalize();
856 
857  /* RETRIEVE DATA */
858 
859  RigDef::DocumentPtr def = parser.GetFile();
860 
861  /* Name */
862  if (!def->name.empty())
863  {
864  entry->dname = def->name; // Use retrieved name
865  }
866  else
867  {
868  entry->dname = "@" + file_name; // Fallback
869  }
870 
871  /* Description */
872  std::vector<Ogre::String>::iterator desc_itor = def->root_module->description.begin();
873  for (; desc_itor != def->root_module->description.end(); desc_itor++)
874  {
875  entry->description += *desc_itor + "\n";
876  }
877 
878  /* Authors */
879  std::vector<RigDef::Author>::iterator author_itor = def->root_module->author.begin();
880  for (; author_itor != def->root_module->author.end(); author_itor++)
881  {
882  AuthorInfo author;
883  author.email = author_itor->email;
884  author.id = (author_itor->_has_forum_account) ? static_cast<int>(author_itor->forum_account_id) : -1;
885  author.name = author_itor->name;
886  author.type = author_itor->type;
887 
888  entry->authors.push_back(author);
889  }
890 
891  /* Default skin */
892  if (def->root_module->default_skin.size() > 0)
893  {
894  entry->default_skin = def->root_module->default_skin.back().skin_name;
895  }
896 
897  /* Modules (previously called "sections") */
898  std::map<Ogre::String, std::shared_ptr<RigDef::Document::Module>>::iterator module_itor = def->user_modules.begin();
899  for (; module_itor != def->user_modules.end(); module_itor++)
900  {
901  entry->sectionconfigs.push_back(module_itor->second->name);
902  }
903 
904  /* Engine */
905  /* TODO: Handle engines in modules */
906  if (def->root_module->engine.size() > 0)
907  {
908  RigDef::Engine& engine = def->root_module->engine[def->root_module->engine.size() - 1];
909  entry->numgears = static_cast<int>(engine.gear_ratios.size());
910  entry->minrpm = engine.shift_down_rpm;
911  entry->maxrpm = engine.shift_up_rpm;
912  entry->torque = engine.torque;
913  entry->enginetype = 't'; /* Truck (default) */
914  if (def->root_module->engoption.size() > 0)
915  {
916  entry->enginetype = (char)def->root_module->engoption[def->root_module->engoption.size() - 1].type;
917  }
918  }
919 
920  /* File info */
921  if (def->root_module->fileinfo.size() > 0)
922  {
923  RigDef::Fileinfo& data = def->root_module->fileinfo[def->root_module->fileinfo.size() - 1];
924 
925  entry->uniqueid = data.unique_id;
926  entry->categoryid = static_cast<int>(data.category_id);
927  entry->version = static_cast<int>(data.file_version);
928  }
929  else
930  {
931  entry->uniqueid = "-1";
932  entry->categoryid = -1;
933  entry->version = -1;
934  }
935 
936  /* Vehicle type */
937  /* NOTE: RigDef::Document allows modularization of vehicle type. Cache only supports single type.
938  This is a temporary solution which has undefined results for mixed-type vehicles.
939  */
940  ActorType vehicle_type = NOT_DRIVEABLE;
941  module_itor = def->user_modules.begin();
942  for (; module_itor != def->user_modules.end(); module_itor++)
943  {
944  if (module_itor->second->engine.size() > 0)
945  {
946  vehicle_type = TRUCK;
947  }
948  else if (module_itor->second->screwprops.size() > 0)
949  {
950  vehicle_type = BOAT;
951  }
952  /* Note: Sections 'turboprops' and 'turboprops2' are unified in TruckParser2013 */
953  else if (module_itor->second->turbojets.size() > 0 || module_itor->second->pistonprops.size() > 0 || module_itor->second->turboprops2.size() > 0)
954  {
955  vehicle_type = AIRPLANE;
956  }
957  }
958  /* Root module */
959  if (def->root_module->engine.size() > 0)
960  {
961  vehicle_type = TRUCK;
962  }
963  else if (def->root_module->screwprops.size() > 0)
964  {
965  vehicle_type = BOAT;
966  }
967  /* Note: Sections 'turboprops' and 'turboprops2' are unified in TruckParser2013 */
968  else if (def->root_module->turbojets.size() > 0 || def->root_module->pistonprops.size() > 0 || def->root_module->turboprops2.size() > 0)
969  {
970  vehicle_type = AIRPLANE;
971  }
972 
973  if (def->root_module->globals.size() > 0)
974  {
975  entry->truckmass = def->root_module->globals[def->root_module->globals.size() - 1].dry_mass;
976  entry->loadmass = def->root_module->globals[def->root_module->globals.size() - 1].cargo_mass;
977  }
978 
979  entry->forwardcommands = def->forward_commands;
980  entry->importcommands = def->import_commands;
981  entry->rescuer = def->rescuer;
982  if (def->root_module->guid.size() > 0)
983  {
984  entry->guid = def->root_module->guid[def->root_module->guid.size() - 1].guid;
985  Ogre::StringUtil::toLowerCase(entry->guid);
986  }
987  entry->fileformatversion = 0;
988  if (def->root_module->fileformatversion.size() > 0)
989  {
990  entry->fileformatversion = def->root_module->fileformatversion[def->root_module->fileformatversion.size() - 1].version;
991  }
992  entry->hasSubmeshs = static_cast<int>(def->root_module->submeshes.size() > 0);
993  entry->nodecount = static_cast<int>(def->root_module->nodes.size());
994  entry->beamcount = static_cast<int>(def->root_module->beams.size());
995  entry->shockcount = static_cast<int>(def->root_module->shocks.size() + def->root_module->shocks2.size());
996  entry->fixescount = static_cast<int>(def->root_module->fixes.size());
997  entry->hydroscount = static_cast<int>(def->root_module->hydros.size());
998  entry->driveable = vehicle_type;
999  entry->commandscount = static_cast<int>(def->root_module->commands2.size());
1000  entry->flarescount = static_cast<int>(def->root_module->flares2.size());
1001  entry->propscount = static_cast<int>(def->root_module->props.size());
1002  entry->wingscount = static_cast<int>(def->root_module->wings.size());
1003  entry->turbopropscount = static_cast<int>(def->root_module->turboprops2.size());
1004  entry->rotatorscount = static_cast<int>(def->root_module->rotators.size() + def->root_module->rotators2.size());
1005  entry->exhaustscount = static_cast<int>(def->root_module->exhausts.size());
1006  entry->custom_particles = def->root_module->particles.size() > 0;
1007  entry->turbojetcount = static_cast<int>(def->root_module->turbojets.size());
1008  entry->flexbodiescount = static_cast<int>(def->root_module->flexbodies.size());
1009  entry->soundsourcescount = static_cast<int>(def->root_module->soundsources.size() + def->root_module->soundsources.size());
1010 
1011  entry->wheelcount = 0;
1012  entry->propwheelcount = 0;
1013  for (const auto& w : def->root_module->wheels)
1014  {
1015  entry->wheelcount++;
1016  if (w.propulsion != RigDef::WheelPropulsion::NONE)
1017  entry->propwheelcount++;
1018  }
1019  for (const auto& w : def->root_module->wheels2)
1020  {
1021  entry->wheelcount++;
1022  if (w.propulsion != RigDef::WheelPropulsion::NONE)
1023  entry->propwheelcount++;
1024  }
1025  for (const auto& w : def->root_module->meshwheels)
1026  {
1027  entry->wheelcount++;
1028  if (w.propulsion != RigDef::WheelPropulsion::NONE)
1029  entry->propwheelcount++;
1030  }
1031  for (const auto& w : def->root_module->meshwheels2)
1032  {
1033  entry->wheelcount++;
1034  if (w.propulsion != RigDef::WheelPropulsion::NONE)
1035  entry->propwheelcount++;
1036  }
1037  for (const auto& w : def->root_module->flexbodywheels)
1038  {
1039  entry->wheelcount++;
1040  if (w.propulsion != RigDef::WheelPropulsion::NONE)
1041  entry->propwheelcount++;
1042  }
1043 
1044  if (!def->root_module->axles.empty())
1045  {
1046  entry->propwheelcount = static_cast<int>(def->root_module->axles.size() * 2);
1047  }
1048 
1049  /* NOTE: std::shared_ptr cleans everything up. */
1050 }
1051 
1052 Ogre::String detectMiniType(String filename, String group)
1053 {
1054  if (ResourceGroupManager::getSingleton().resourceExists(group, filename + "dds"))
1055  return "dds";
1056 
1057  if (ResourceGroupManager::getSingleton().resourceExists(group, filename + "png"))
1058  return "png";
1059 
1060  if (ResourceGroupManager::getSingleton().resourceExists(group, filename + "jpg"))
1061  return "jpg";
1062 
1063  return "";
1064 }
1065 
1067 {
1068  if (!entry->filecachename.empty())
1069  {
1071  }
1072 }
1073 
1075 {
1076  if (entry->fname.empty())
1077  return;
1078 
1079  String bundle_basename, bundle_path;
1080  StringUtil::splitFilename(entry->resource_bundle_path, bundle_basename, bundle_path);
1081 
1082  String src_path;
1083  String dst_path;
1084  if (entry->fext == "skin")
1085  {
1086  if (entry->skin_def->thumbnail.empty())
1087  return;
1088  src_path = entry->skin_def->thumbnail;
1089  String mini_fbase, minitype;
1090  StringUtil::splitBaseFilename(entry->skin_def->thumbnail, mini_fbase, minitype);
1091  dst_path = bundle_basename + "_" + mini_fbase + ".mini." + minitype;
1092  }
1093  else
1094  {
1095  String fbase, fext;
1096  StringUtil::splitBaseFilename(entry->fname, fbase, fext);
1097  String minifn = fbase + "-mini.";
1098  String minitype = detectMiniType(minifn, group);
1099  if (minitype.empty())
1100  return;
1101  src_path = minifn + minitype;
1102  dst_path = bundle_basename + "_" + entry->fname + ".mini." + minitype;
1103  }
1104 
1105  try
1106  {
1107  DataStreamPtr src_ds = ResourceGroupManager::getSingleton().openResource(src_path, group);
1108  DataStreamPtr dst_ds = ResourceGroupManager::getSingleton().createResource(dst_path, RGN_CACHE, true);
1109  std::vector<char> buf(src_ds->size());
1110  size_t read = src_ds->read(buf.data(), src_ds->size());
1111  if (read > 0)
1112  {
1113  dst_ds->write(buf.data(), read);
1114  entry->filecachename = dst_path;
1115  }
1116  }
1117  catch (Ogre::Exception& e)
1118  {
1119  LOG("error while generating file cache: " + e.getFullDescription());
1120  }
1121 
1122  LOG("done generating file cache!");
1123 }
1124 
1126 {
1127  auto files = ResourceGroupManager::getSingleton().findResourceFileInfo(group, "*.zip");
1128  auto skinzips = ResourceGroupManager::getSingleton().findResourceFileInfo(group, "*.skinzip");
1129  for (const auto& skinzip : *skinzips)
1130  files->push_back(skinzip);
1131 
1132  int i = 0, count = static_cast<int>(files->size());
1133  for (const auto& file : *files)
1134  {
1135  int progress = ((float)i++ / (float)count) * 100;
1136  std::string text = fmt::format("{}{}\n{}\n{}/{}",
1137  _L("Loading zips in group "), group, file.filename, i, count);
1139 
1140  String path = PathCombine(file.archive->getName(), file.filename);
1141  this->ParseSingleZip(path);
1142  }
1143 
1146 }
1147 
1149 {
1150  if (std::find(m_resource_paths.begin(), m_resource_paths.end(), path) == m_resource_paths.end())
1151  {
1152  RoR::LogFormat("[RoR|ModCache] Adding archive '%s'", path.c_str());
1153  ResourceGroupManager::getSingleton().createResourceGroup(RGN_TEMP, false);
1154  try
1155  {
1156  ResourceGroupManager::getSingleton().addResourceLocation(path, "Zip", RGN_TEMP);
1158  {
1159  LOG("No usable content in: '" + path + "'");
1160  }
1161  }
1162  catch (Ogre::Exception& e)
1163  {
1164  LOG("Error while opening archive: '" + path + "': " + e.getFullDescription());
1165  }
1166  ResourceGroupManager::getSingleton().destroyResourceGroup(RGN_TEMP);
1167  m_resource_paths.insert(path);
1168  }
1169 }
1170 
1171 bool CacheSystem::ParseKnownFiles(Ogre::String group)
1172 {
1173  bool empty = true;
1174  for (auto ext : m_known_extensions)
1175  {
1176  auto files = ResourceGroupManager::getSingleton().findResourceFileInfo(group, "*." + ext);
1177  for (const auto& file : *files)
1178  {
1179  this->AddFile(group, file, ext);
1180  empty = false;
1181  }
1182  }
1183  return empty;
1184 }
1185 
1187 {
1188  std::string filenames = App::GetContentManager()->ListAllUserContent();
1189  m_filenames_hash_generated = HashData(filenames.c_str(), static_cast<int>(filenames.size()));
1190 }
1191 
1192 void CacheSystem::FillTerrainDetailInfo(CacheEntryPtr& entry, Ogre::DataStreamPtr ds, Ogre::String fname)
1193 {
1194  Terrn2Parser parser;
1195  Terrn2DocumentPtr def = parser.LoadTerrn2(ds);
1196  if (!def)
1197  {
1199  fmt::format("Mod cache entry not populated - could not load terrain {}", ds->getName()));
1200  return;
1201  }
1202 
1203  for (Terrn2Author& author : def->authors)
1204  {
1205  AuthorInfo a;
1206  a.id = -1;
1207  a.name = author.name;
1208  a.type = author.type;
1209  entry->authors.push_back(a);
1210  }
1211 
1212  entry->dname = def->name;
1213  entry->categoryid = def->category_id;
1214  entry->uniqueid = def->guid;
1215  entry->version = def->version;
1216 }
1217 
1218 void CacheSystem::FillSkinDetailInfo(CacheEntryPtr &entry, std::shared_ptr<SkinDocument>& skin_def)
1219 {
1220  if (!skin_def->author_name.empty())
1221  {
1222  AuthorInfo a;
1223  a.id = skin_def->author_id;
1224  a.name = skin_def->author_name;
1225  entry->authors.push_back(a);
1226  }
1227 
1228  entry->dname = skin_def->name;
1229  entry->guid = skin_def->guid;
1230  entry->description = skin_def->description;
1231  entry->categoryid = -1;
1232  entry->skin_def = skin_def; // Needed to generate preview image
1233 
1234  Ogre::StringUtil::toLowerCase(entry->guid);
1235 }
1236 
1237 void CacheSystem::FillAddonPartDetailInfo(CacheEntryPtr &entry, Ogre::DataStreamPtr ds)
1238 {
1239  GenericDocumentPtr doc = new GenericDocument();
1241  doc->loadFromDataStream(ds, options);
1242 
1243  GenericDocContextPtr ctx = new GenericDocContext(doc);
1244  while (!ctx->endOfFile())
1245  {
1246  if (ctx->isTokKeyword() && ctx->getTokKeyword() == "addonpart_name")
1247  {
1248  entry->dname = ctx->getTokString(1);
1249  }
1250  else if (ctx->isTokKeyword() && ctx->getTokKeyword() == "addonpart_description")
1251  {
1252  entry->description = ctx->getTokString(1);
1253  }
1254  else if (ctx->isTokKeyword() && ctx->getTokKeyword() == "addonpart_guid")
1255  {
1256  std::string guid = ctx->getTokString(1);
1257  Ogre::StringUtil::toLowerCase(guid);
1258  entry->addonpart_guids.insert(guid);
1259  }
1260  else if (ctx->isTokKeyword() && ctx->getTokKeyword() == "addonpart_filename")
1261  {
1262  std::string fname = ctx->getTokString(1);
1263  Ogre::StringUtil::toLowerCase(fname);
1264  entry->addonpart_filenames.insert(fname);
1265  }
1266 
1267  ctx->seekNextLine();
1268  }
1269 }
1270 
1271 void CacheSystem::FillAssetPackDetailInfo(CacheEntryPtr &entry, Ogre::DataStreamPtr ds)
1272 {
1273  GenericDocumentPtr doc = new GenericDocument();
1275  doc->loadFromDataStream(ds, options);
1276 
1277  GenericDocContextPtr ctx = new GenericDocContext(doc);
1278  while (!ctx->endOfFile())
1279  {
1280  if (ctx->isTokKeyword() && ctx->getTokKeyword() == "assetpack_name")
1281  {
1282  entry->dname = ctx->getTokString(1);
1283  }
1284  else if (ctx->isTokKeyword() && ctx->getTokKeyword() == "assetpack_description")
1285  {
1286  entry->description = ctx->getTokString(1);
1287  }
1288  else if (ctx->isTokKeyword() && ctx->getTokKeyword() == "assetpack_author")
1289  {
1290  int n = ctx->countLineArgs();
1291  AuthorInfo author;
1292  if (n > 1) { author.type = ctx->getTokString(1); }
1293  if (n > 2) { author.id = (int)ctx->getTokFloat(2); }
1294  if (n > 3) { author.name = ctx->getTokString(3); }
1295  if (n > 4) { author.email = ctx->getTokString(4); }
1296  entry->authors.push_back(author);
1297  }
1298 
1299  ctx->seekNextLine();
1300  }
1301 }
1302 
1303 void CacheSystem::FillDashboardDetailInfo(CacheEntryPtr& entry, Ogre::DataStreamPtr ds)
1304 {
1305  GenericDocumentPtr doc = new GenericDocument();
1307  doc->loadFromDataStream(ds, options);
1308 
1309  GenericDocContextPtr ctx = new GenericDocContext(doc);
1310  while (!ctx->endOfFile())
1311  {
1312  if (ctx->isTokKeyword() && ctx->getTokKeyword() == "dashboard_name" && ctx->isTokString(1))
1313  {
1314  entry->dname = ctx->getTokString(1);
1315  }
1316  else if (ctx->isTokKeyword() && ctx->getTokKeyword() == "dashboard_description" && ctx->isTokString(1))
1317  {
1318  entry->description = ctx->getTokString(1);
1319  }
1320  else if (ctx->isTokKeyword() && ctx->getTokKeyword() == "dashboard_category" && ctx->isTokInt(1))
1321  {
1322  entry->categoryid = ctx->getTokInt(1);
1323  }
1324  else if (ctx->isTokKeyword() && ctx->getTokKeyword() == "dashboard_author")
1325  {
1326  int n = ctx->countLineArgs();
1327  AuthorInfo author;
1328  if (n > 1) { author.type = ctx->getTokString(1); }
1329  if (n > 2) { author.id = ctx->getTokInt(2); }
1330  if (n > 3) { author.name = ctx->getTokString(3); }
1331  if (n > 4) { author.email = ctx->getTokString(4); }
1332  entry->authors.push_back(author);
1333  }
1334 
1335  ctx->seekNextLine();
1336  }
1337 
1338 }
1339 
1340 void CacheSystem::FillGadgetDetailInfo(CacheEntryPtr& entry, Ogre::DataStreamPtr ds)
1341 {
1342  GenericDocumentPtr doc = new GenericDocument();
1343  BitMask_t options
1347  doc->loadFromDataStream(ds, options);
1348 
1349  GenericDocContextPtr ctx = new GenericDocContext(doc);
1350  while (!ctx->endOfFile())
1351  {
1352  if (ctx->isTokKeyword() && ctx->getTokKeyword() == "gadget_name" && ctx->isTokString(1))
1353  {
1354  entry->dname = ctx->getTokString(1);
1355  }
1356  else if (ctx->isTokKeyword() && ctx->getTokKeyword() == "gadget_description" && ctx->isTokString(1))
1357  {
1358  if (entry->description == "")
1359  {
1360  entry->description = ctx->getTokString(1);
1361  }
1362  else
1363  {
1364  entry->description += "\n" + ctx->getTokString(1);
1365  }
1366  }
1367  else if (ctx->isTokKeyword() && ctx->getTokKeyword() == "gadget_category" && ctx->isTokInt(1))
1368  {
1369  entry->categoryid = ctx->getTokInt(1);
1370  }
1371  else if (ctx->isTokKeyword() && ctx->getTokKeyword() == "gadget_author")
1372  {
1373  int n = ctx->countLineArgs();
1374  AuthorInfo author;
1375  if (n > 1) { author.type = ctx->getTokString(1); }
1376  if (n > 2) { author.id = ctx->getTokInt(2); }
1377  if (n > 3) { author.name = ctx->getTokString(3); }
1378  if (n > 4) { author.email = ctx->getTokString(4); }
1379  entry->authors.push_back(author);
1380  }
1381 
1382  ctx->seekNextLine();
1383  }
1384 
1385 }
1386 
1388 {
1389  if (!tuneup_def->author_name.empty())
1390  {
1391  AuthorInfo a;
1392  a.id = tuneup_def->author_id;
1393  a.name = tuneup_def->author_name;
1394  entry->authors.push_back(a);
1395  }
1396 
1397  entry->dname = tuneup_def->name;
1398  entry->guid = tuneup_def->guid;
1399  entry->description = tuneup_def->description;
1400  entry->categoryid = tuneup_def->category_id;
1401  entry->tuneup_def = tuneup_def; // Needed to generate preview image
1402  entry->tuneup_associated_filename = tuneup_def->filename;
1403 
1404  Ogre::StringUtil::toLowerCase(entry->guid);
1405  Ogre::StringUtil::toLowerCase(entry->tuneup_associated_filename);
1406 }
1407 
1408 void CacheSystem::LoadAssetPack(CacheEntryPtr& target_entry, Ogre::String const & assetpack_filename)
1409 {
1410  // Load asset packs into the mod-bundle's resource group (quick & dirty approach).
1411  // See also `ContentManager::resourceCollision()` - we always keep the original file and dump the colliding one.
1412  // --------------------------------------------------------------------------------------------------------------
1413 
1414  ROR_ASSERT(!target_entry->deleted);
1415  ROR_ASSERT(target_entry->resource_group != "");
1416  ROR_ASSERT(assetpack_filename != "");
1417 
1418  CacheEntryPtr assetpack_entry = App::GetCacheSystem()->FindEntryByFilename(LT_AssetPack, /*partial=*/false, assetpack_filename);
1419  if (assetpack_entry)
1420  {
1421  try
1422  {
1423  Ogre::ResourceGroupManager::getSingleton().addResourceLocation(
1424  assetpack_entry->resource_bundle_path, // name (source)
1425  assetpack_entry->resource_bundle_type, // type (source)
1426  target_entry->resource_group, // resGroup (target)
1427  false, // recursive
1428  assetpack_entry->resource_bundle_type != "FileSystem"); // readOnly
1429 
1430  // This is messy but there's no other way - OGRE resource groups cannot update incrementally.
1431  Ogre::ResourceGroupManager::getSingleton().clearResourceGroup(target_entry->resource_group);
1432  Ogre::ResourceGroupManager::getSingleton().initialiseResourceGroup(target_entry->resource_group);
1433  }
1434  catch (std::exception const& e)
1435  {
1437  fmt::format(_L("Failed to load asset pack '{}' (requested by '{}'): {}"), assetpack_entry->fname, target_entry->fname, e.what()));
1438  }
1439  }
1440  else
1441  {
1443  fmt::format(_L("Asset pack '{}' (requested by '{}') not found"), assetpack_filename, target_entry->fname));
1444  }
1445 }
1446 
1447 static bool CheckAndReplacePathIgnoreCase(const CacheEntryPtr& entry, CVar* dir, const std::string& dir_label, std::string& out_rgname)
1448 {
1449  // Helper for `ComposeResourceGroupName()`
1450  // ---------------------------------------
1451 
1452  // Sanity check - assert on Debug, minimize damage on Release
1453  ROR_ASSERT(entry->resource_bundle_path != "");
1454  if (entry->resource_bundle_path == "")
1455  {
1456  LOG(fmt::format("[RoR|CacheSystem] CheckAndReplacePathIgnoreCase(): INTERNAL ERROR - entry '{}' has no bundle path!", entry->fname));
1457  return false;
1458  }
1459 
1460  // Lowercase everything
1461  std::string lower_bundlepath = entry->resource_bundle_path;
1462  Ogre::StringUtil::toLowerCase(lower_bundlepath);
1463 
1464  std::string lower_dir = dir->getStr();
1465  Ogre::StringUtil::toLowerCase(lower_dir);
1466 
1467  // Look for match and replace
1468  if (Ogre::StringUtil::startsWith(lower_bundlepath, lower_dir, /*lowercase:*/true))
1469  {
1470  // Sanity check; Should be guaranteed by the `startsWith()` check, but just to be sure...
1471  ROR_ASSERT(lower_bundlepath.size() > lower_dir.size());
1472  if (lower_bundlepath.size() > lower_dir.size())
1473  {
1474  std::string localpath = entry->resource_bundle_path.substr(lower_dir.length());
1475  out_rgname = fmt::format("{{bundle {}:{}}}", dir_label, localpath);
1476  return true;
1477  }
1478  }
1479  return false;
1480 }
1481 
1483 {
1484  // Compose group name as "{bundle <local path>}", where 'local path' means either:
1485  // - under `sys_user_dir` (by default 'Documenst\My Games\Rigs of Rods')
1486  // - under `app_extra_mod_path` (empty by default)
1487  // - under 'sys_process_dir' (autodetected)
1488  // -------------------------------------------------------------------------------
1489 
1490  std::string rg_name;
1491  if (CheckAndReplacePathIgnoreCase(entry, App::sys_user_dir, "USER", rg_name) ||
1492  CheckAndReplacePathIgnoreCase(entry, App::sys_process_dir, "BIN", rg_name) ||
1493  CheckAndReplacePathIgnoreCase(entry, App::app_extra_mod_path, "EXTRA", rg_name))
1494  {
1495  return rg_name;
1496  }
1497  else
1498  {
1499  return fmt::format("{{bundle FULL:{}}}", entry->resource_bundle_path);
1500  }
1501 }
1502 
1504 {
1505  // Because we use one resource group per bundle and multiple entries can share the same bundle,
1506  // we need to load the supplementary documents even if the bundle is already loaded.
1507  // -------------------------------------------------------------------------------------------
1508 
1509  if (!entry)
1510  return;
1511 
1512  ROR_ASSERT(entry->resource_group != "");
1513 
1514  if (entry->fext == "skin")
1515  {
1516  this->LoadAssociatedSkinDef(entry);
1517  }
1518  else if (entry->fext == "tuneup")
1519  {
1520  this->LoadAssociatedTuneupDef(entry);
1521  }
1522 }
1523 
1524 bool CacheSystem::IsPathContentDirRoot(const std::string& path) const
1525 {
1526  // Helper for `LoadResource()` because OGRE's 'readOnly' flag, see explanation in `ContentManager::InitModCache()`
1527  // --------------------------------------------------------------------------------------------------------------
1528 
1529  for (const std::string& cdir: m_content_dirs)
1530  {
1531  if (path == PathCombine(App::sys_user_dir->getStr(), cdir))
1532  {
1533  return true;
1534  }
1535  }
1536  return false;
1537 }
1538 
1540 {
1541  if (!entry)
1542  return;
1543 
1544  // Check if already loaded for this entry->
1545  if (entry->resource_group != "")
1546  {
1547  this->LoadSupplementaryDocuments(entry);
1548  return;
1549  }
1550 
1551  Ogre::String group = CacheSystem::ComposeResourceGroupName(entry);
1552 
1553  // Make "FileSystem" (directory) bundles writable (Default is read-only), except if it's a root directory.
1554  // See explanation of `readOnly` OGRE flag in `ContentManager::InitModCache()`.
1555  bool readonly = entry->resource_bundle_type == "Zip" || this->IsPathContentDirRoot(entry->resource_bundle_path);
1556  bool recursive = false;
1557 
1558  // Load now.
1559  try
1560  {
1561  if (entry->fext == "terrn2")
1562  {
1563  // PagedGeometry is hardcoded to use `Ogre::ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME`
1564  ResourceGroupManager::getSingleton().createResourceGroup(group, /*inGlobalPool=*/true);
1565  ResourceGroupManager::getSingleton().addResourceLocation(
1566  entry->resource_bundle_path, entry->resource_bundle_type, group, recursive, readonly);
1567  }
1568  else if (entry->fext == "skin")
1569  {
1570  // This is a SkinZip bundle - use `inGlobalPool=false` to prevent resource name conflicts.
1571  // Note: this code won't execute for .skin files in vehicle-bundles because in such case the bundle is already loaded by the vehicle's Cacheentry->
1572  ResourceGroupManager::getSingleton().createResourceGroup(group, /*inGlobalPool=*/false);
1573  ResourceGroupManager::getSingleton().addResourceLocation(
1574  entry->resource_bundle_path, entry->resource_bundle_type, group, recursive, readonly);
1576  }
1577  else if (entry->fext == "tuneup")
1578  {
1579  // This is a .tuneup bundle - use `inGlobalPool=false` to prevent resource name conflicts.
1580  ResourceGroupManager::getSingleton().createResourceGroup(group, /*inGlobalPool=*/false);
1581  ResourceGroupManager::getSingleton().addResourceLocation(
1582  entry->resource_bundle_path, entry->resource_bundle_type, group, recursive, readonly);
1584  }
1585  else if (entry->fext == "gadget")
1586  {
1587  // This is a .gadget bundle - use `inGlobalPool=false` to prevent resource name conflicts.
1588  ResourceGroupManager::getSingleton().createResourceGroup(group, /*inGlobalPool=*/false);
1589  ResourceGroupManager::getSingleton().addResourceLocation(
1590  entry->resource_bundle_path, entry->resource_bundle_type, group, recursive, readonly);
1592  // Allow using builtin include scripts
1594  }
1595  else
1596  {
1597  // A vehicle bundle - use `inGlobalPool=false` to prevent resource name conflicts.
1598  // See bottom 'note' at https://ogrecave.github.io/ogre/api/latest/_resource-_management.html#Resource-Groups
1599  ResourceGroupManager::getSingleton().createResourceGroup(group, /*inGlobalPool=*/false);
1600  ResourceGroupManager::getSingleton().addResourceLocation(
1601  entry->resource_bundle_path, entry->resource_bundle_type, group, recursive, readonly);
1602 
1607  }
1608 
1609  // Initialize resource group
1610  ResourceGroupManager::getSingleton().initialiseResourceGroup(group);
1611  entry->resource_group = group;
1612 
1613  this->LoadSupplementaryDocuments(entry);
1614 
1615  // Inform other entries sharing this bundle (i.e. '.skin' entries in vehicle bundles)
1616  for (CacheEntryPtr& i_entry: m_entries)
1617  {
1618  if (i_entry->resource_bundle_path == entry->resource_bundle_path)
1619  {
1620  i_entry->resource_group = group; // Mark as loaded
1621  }
1622  }
1623  }
1624  catch (Ogre::Exception& e)
1625  {
1626  RoR::LogFormat("[RoR] Error while loading '%s', message: %s",
1627  entry->resource_bundle_path.c_str(), e.getFullDescription().c_str());
1628  if (ResourceGroupManager::getSingleton().resourceGroupExists(group))
1629  {
1630  ResourceGroupManager::getSingleton().destroyResourceGroup(group);
1631  }
1632  }
1633 }
1634 
1636 {
1637  if (entry->resource_group == "")
1638  {
1639  return; // Not loaded - nothing to do
1640  }
1641 
1642  // IMPORTANT! No actors must use the bundle while reloading, use RoR::MsgType::MSG_EDI_RELOAD_BUNDLE_REQUESTED
1643 
1644  this->UnLoadResource(entry);
1645  this->LoadResource(entry); // Will create the same resource group again
1646 }
1647 
1649 {
1650  if (entry->resource_group == "")
1651  {
1652  return; // Not loaded - nothing to do
1653  }
1654 
1655  // IMPORTANT! No actors must use the bundle after reloading, use RoR::MsgType::MSG_EDI_RELOAD_BUNDLE_REQUESTED
1656 
1657  std::string resource_group = entry->resource_group; // Keep local copy, the CacheEntry will be blanked!
1658  for (CacheEntryPtr& i_entry: m_entries)
1659  {
1660  if (i_entry->resource_group == resource_group)
1661  {
1662  // Delete cached documents - force reload from disk
1663  i_entry->actor_def = nullptr;
1664  i_entry->tuneup_def = nullptr;
1665  i_entry->skin_def = nullptr;
1666  // Mark as unloaded
1667  i_entry->resource_group = "";
1668  }
1669  }
1670 
1671  Ogre::ResourceGroupManager::getSingleton().destroyResourceGroup(resource_group);
1672 }
1673 
1674 CacheEntryPtr CacheSystem::FetchSkinByName(std::string const & skin_name)
1675 {
1676  for (CacheEntryPtr & entry: m_entries)
1677  {
1678  if (entry->dname == skin_name && entry->fext == "skin")
1679  {
1680  return entry;
1681  }
1682  }
1683  return nullptr;
1684 }
1685 
1687 {
1688  // A .skin file defines multiple skins, so we need to locate and update all associated cache entries.
1689  // --------------------------------------------------------------------------------------------------
1690 
1691  if (!cache_entry)
1692  return;
1693 
1694  ROR_ASSERT(cache_entry->resource_group != ""); // Must be already loaded
1695 
1696  if (cache_entry->skin_def != nullptr) // If already parsed, re-use
1697  {
1698  return;
1699  }
1700 
1701  try
1702  {
1703  Ogre::DataStreamPtr ds = Ogre::ResourceGroupManager::getSingleton()
1704  .openResource(cache_entry->fname, cache_entry->resource_group);
1705 
1706  auto new_skins = RoR::SkinParser::ParseSkins(ds); // Load the '.skin' file
1707  for (auto def: new_skins)
1708  {
1709  for (CacheEntryPtr& entry: m_entries)
1710  {
1711  if (entry->resource_bundle_path == cache_entry->resource_bundle_path
1712  && entry->resource_bundle_type == cache_entry->resource_bundle_type
1713  && entry->fname == cache_entry->fname
1714  && entry->dname == def->name)
1715  {
1716  entry->skin_def = def;
1717  entry->resource_group = cache_entry->resource_group;
1718  }
1719  }
1720  }
1721 
1722  if (cache_entry->skin_def == nullptr)
1723  {
1724  RoR::LogFormat("Definition of skin '%s' was not found in file '%s'",
1725  cache_entry->dname.c_str(), cache_entry->fname.c_str());
1726  }
1727  }
1728  catch (Ogre::Exception& oex)
1729  {
1730  RoR::LogFormat("[RoR] Error loading skin file '%s', message: %s",
1731  cache_entry->fname.c_str(), oex.getFullDescription().c_str());
1732  }
1733 }
1734 
1736 {
1737  // A .tuneup file defines multiple tuneups, so we need to locate and update all associated cache entries.
1738  // --------------------------------------------------------------------------------------------------
1739 
1740  if (!cache_entry)
1741  return;
1742 
1743  ROR_ASSERT(cache_entry->resource_group != ""); // Must be already loaded
1744 
1745  if (cache_entry->tuneup_def != nullptr) // If already parsed, re-use
1746  {
1747  return;
1748  }
1749 
1750  try
1751  {
1752  Ogre::DataStreamPtr ds = Ogre::ResourceGroupManager::getSingleton()
1753  .openResource(cache_entry->fname, cache_entry->resource_group);
1754 
1755  auto new_tuneups = RoR::TuneupUtil::ParseTuneups(ds); // Load the '.tuneup' file
1756  for (auto def: new_tuneups)
1757  {
1758  for (CacheEntryPtr& entry: m_entries)
1759  {
1760  if (entry->resource_bundle_path == cache_entry->resource_bundle_path
1761  && entry->resource_bundle_type == cache_entry->resource_bundle_type
1762  && entry->fname == cache_entry->fname
1763  && entry->dname == def->name)
1764  {
1765  entry->tuneup_def = def;
1766  entry->resource_group = cache_entry->resource_group;
1767  }
1768  }
1769  }
1770 
1771  if (cache_entry->tuneup_def == nullptr)
1772  {
1773  RoR::LogFormat("Definition of tuneup '%s' was not found in file '%s'",
1774  cache_entry->dname.c_str(), cache_entry->fname.c_str());
1775  }
1776  }
1777  catch (Ogre::Exception& oex)
1778  {
1779  RoR::LogFormat("[RoR] Error loading tuneup file '%s', message: %s",
1780  cache_entry->fname.c_str(), oex.getFullDescription().c_str());
1781  }
1782 }
1783 
1785 {
1786 
1787  // Validate the request
1788  if (!request->cpr_source_entry)
1789  {
1791  fmt::format(_LC("CacheSystem", "Cannot create project '{}' - no source mod specified!"), request->cpr_name));
1792  return nullptr;
1793  }
1794 
1795  // Make sure projects folder exists
1797 
1798  // Create subfolder
1799  std::string project_path = PathCombine(App::sys_projects_dir->getStr(), request->cpr_name);
1800  if (FolderExists(project_path) && !request->cpr_overwrite)
1801  {
1803  fmt::format(_LC("CacheSystem", "Project directory '{}' already exists!"), request->cpr_name));
1804  return nullptr;
1805  }
1806  CreateFolder(project_path);
1807  if (!FolderExists(project_path))
1808  {
1810  fmt::format(_LC("CacheSystem", "Project directory '{}' could not be created!"), request->cpr_name));
1811  return nullptr;
1812  }
1813 
1814  // Check if a project with the same name already exists
1815  CacheEntryPtr project_entry;
1816  bool project_entry_created = false;
1817  if (request->cpr_overwrite)
1818  {
1819  project_entry = this->FindEntryByFilename(LT_Tuneup, /*partial:*/false, fmt::format("{}.tuneup", request->cpr_name));
1820  this->LoadResource(project_entry); // This fills `entry.resource_group`
1821  }
1822 
1823  if (!project_entry)
1824  {
1825  // Create preliminary cache entry
1826  project_entry = new CacheEntry();
1827  project_entry_created = true;
1828 
1830  {
1831  project_entry->fext = "tuneup"; // Tell modcache what it is.
1832  project_entry->categoryid = CID_Tuneups; // For display in modcache
1833  project_entry->guid = request->cpr_source_entry->guid; // For lookup of tuneups by vehicle GUID.
1834  Ogre::StringUtil::toLowerCase(project_entry->guid);
1835  project_entry->tuneup_associated_filename = request->cpr_source_entry->fname; // For additional filtering of results (GUID marks a family, not individual mod).
1836  Ogre::StringUtil::toLowerCase(project_entry->tuneup_associated_filename);
1837  }
1838  else
1839  {
1840  project_entry->fext = request->cpr_source_entry->fext; // Tell modcache what it is.
1841  project_entry->categoryid = CID_Projects; // To list projects easily from cache
1842  }
1843  project_entry->categoryname = m_categories[project_entry->categoryid];
1844  project_entry->resource_bundle_type = "FileSystem"; // Tell modcache how to load it.
1845  project_entry->resource_bundle_path = project_path; // Tell modcache where to load it from.
1846  project_entry->fname = fmt::format("{}.{}", request->cpr_name, project_entry->fext); // Compose target mod filename
1847  project_entry->dname = request->cpr_name;
1848  project_entry->description = request->cpr_description;
1849  project_entry->number = static_cast<int>(m_entries.size() + 1); // Let's number mods from 1
1850  this->LoadResource(project_entry); // This fills `entry.resource_group`
1851  }
1852 
1854  {
1855  // Tuneup projects don't contain any media, just the .tuneup file which lists addonparts to use.
1856 
1857  // Prepare the .tuneup document
1858  ROR_ASSERT(request->cpr_source_actor);
1860 
1861  TuneupDefPtr tuneup = request->cpr_source_actor->getWorkingTuneupDef()->clone();
1862  tuneup->guid = request->cpr_source_entry->guid; // For lookup of tuneups by vehicle GUID.
1863  tuneup->filename = request->cpr_source_entry->fname; // For additional filtering of results (GUID marks a family, not individual mod).
1864  tuneup->name = request->cpr_name;
1865  tuneup->description = request->cpr_description;
1866  tuneup->thumbnail = request->cpr_source_entry->filecachename;
1867  tuneup->category_id = (CacheCategoryId)project_entry->categoryid;
1868 
1869  // Write out the .tuneup file.
1870  Ogre::DataStreamPtr datastream = Ogre::ResourceGroupManager::getSingleton().createResource(
1871  project_entry->fname, project_entry->resource_group, request->cpr_overwrite);
1872  TuneupUtil::ExportTuneup(datastream, tuneup);
1873 
1874  // Attach the document to the entry in memory
1875  project_entry->tuneup_def = tuneup;
1876 
1877  // In the likely case this was invoked from TopMenubarUI, update it.
1878  if (App::GetGuiManager()->TopMenubar.tuning_savebox_visible)
1879  {
1881  App::GetGuiManager()->TopMenubar.tuning_actor = nullptr; // Force refresh
1882  }
1883  }
1884  else
1885  {
1886 
1887  // Create temporary resource group with only the data we want.
1888  std::string temp_rg = "TempProjectSourceRG";
1889  // Apart from `Resources` and resource groups, OGRE also keeps `Archives` in `ArchiveManager`
1890  // These aren't unloaded on destroying resource groups, and keep a 'readOnly' flag (defaults to true).
1891  // Upon loading/creating new resource groups, OGRE complains if the submitted flag doesn't match.
1892  // Since we want to make subdirs (with upacked mods) writable, we must purge subdir-archives now.
1893  bool readonly = request->cpr_source_entry->resource_bundle_type == "Zip";
1894  bool recursive = false;
1895  Ogre::ResourceGroupManager::getSingleton().addResourceLocation(
1897  request->cpr_source_entry->resource_bundle_type, temp_rg, recursive, readonly);
1898  Ogre::ResourceGroupManager::getSingleton().initialiseResourceGroup(temp_rg);
1899 
1900  // Copy the files, one by one
1901  Ogre::FileInfoListPtr filelist = Ogre::ResourceGroupManager::getSingleton().findResourceFileInfo(temp_rg, "*.*");
1902  for (size_t i = 0; i < filelist->size(); i++)
1903  {
1904  Ogre::FileInfo fileinfo = filelist->at(i);
1905 
1906  // Render a frame with a progress window on it.
1908  (i+1)/filelist->size(),
1909  fmt::format("Creating project from existing mod...\nCopying file {}/{} '{}'", i, filelist->size(), fileinfo.filename),
1910  /*render_frame:*/true);
1911 
1912  // Copy one file
1913  try
1914  {
1915  DataStreamPtr src_ds = ResourceGroupManager::getSingleton().openResource(fileinfo.filename, temp_rg);
1916  DataStreamPtr dst_ds = ResourceGroupManager::getSingleton().createResource(fileinfo.filename, project_entry->resource_group);
1917  std::vector<char> buf(src_ds->size());
1918  size_t read = src_ds->read(buf.data(), src_ds->size());
1919  if (read > 0)
1920  {
1921  dst_ds->write(buf.data(), read);
1922  }
1923  }
1924  catch (Ogre::Exception& oex)
1925  {
1927  fmt::format(_LC("CacheSystem", "Could not copy file '{}' to project '{}', message: {}."),
1928  fileinfo.filename, request->cpr_name, oex.getDescription()));
1929  }
1930  }
1931 
1933  Ogre::ResourceGroupManager::getSingleton().destroyResourceGroup(temp_rg);
1934 
1935  // Finally rename the mod file
1936  Ogre::FileSystemLayer::renameFile(
1937  /*oldPath:*/ PathCombine(project_path, request->cpr_source_entry->fname),
1938  /*newPath:*/ PathCombine(project_path, project_entry->fname));
1939  }
1940 
1941 
1942  if (project_entry_created)
1943  {
1944  // Add the new entry to database
1945  m_entries.push_back(project_entry);
1946  }
1947 
1948  // notify script
1949  modCacheActivityType activity_type = (project_entry_created) ? MODCACHEACTIVITY_ENTRY_ADDED : MODCACHEACTIVITY_ENTRY_MODIFIED;
1951  /*ints*/ activity_type, project_entry->number, 0, 0,
1952  /*strings*/ project_entry->fname, project_entry->fext);
1953 
1954  return project_entry;
1955 }
1956 
1958 {
1959  ROR_ASSERT(request->mpr_target_actor);
1961 
1962 
1963  switch (request->mpr_type)
1964  {
1966  {
1968  if (request->mpr_target_actor->getWorkingTuneupDef()->use_addonparts.count(request->mpr_subject) != 0)
1969  {
1971  fmt::format(_LC("Tuning", "Addon part '{}' is already equipped."), request->mpr_subject));
1972  return; // Nothing to do!
1973  }
1974 
1975  CacheEntryPtr subject_entry = this->FindEntryByFilename(LT_AddonPart, /*partial=*/false, request->mpr_subject);
1976  if (!subject_entry)
1977  {
1979  fmt::format(_LC("Tuning", "Addon part '{}' was not found in mod cache (probably not installed)."), request->mpr_subject));
1980  return; // Nothing to do!
1981  }
1982 
1984  {
1985  return; // Error message box already shown
1986  }
1987  else
1988  {
1989  request->mpr_target_actor->getWorkingTuneupDef()->use_addonparts.insert(request->mpr_subject);
1990  }
1991 
1992  break;
1993  }
1994 
1997  request->mpr_target_actor->getWorkingTuneupDef()->use_addonparts.erase(request->mpr_subject);
1998  break;
1999 
2003  break;
2004 
2008  break;
2009 
2013  break;
2014 
2018  break;
2019 
2023  break;
2024 
2028  break;
2029 
2033  break;
2034 
2038  break;
2039 
2043  break;
2044 
2048  break;
2049 
2053  break;
2054 
2058  break;
2059 
2063  break;
2064 
2068  break;
2069 
2073  break;
2074 
2078  break;
2079 
2083  break;
2084 
2088  break;
2089 
2093  break;
2094 
2098  break;
2099 
2103  break;
2104 
2108  break;
2109 
2113  break;
2114 
2118  break;
2119 
2121  {
2122  // Instead of loading with the saved tuneup directly, keep the autogenerated and sync it with the save.
2123  // That way, subsequent editing doesn't modify the save until user saves again.
2124  CacheEntryPtr save_entry = App::GetCacheSystem()->FindEntryByFilename(LT_Tuneup, /*partial:*/false, request->mpr_subject);
2125  if (!save_entry)
2126  {
2128  fmt::format(_LC("CacheSystem", "Error loading tuneup: file '{}', not found in mod cache"), request->mpr_subject));
2129  return;
2130  }
2131  this->LoadResource(save_entry);
2132  ROR_ASSERT(save_entry->tuneup_def);
2133  request->mpr_target_actor->getWorkingTuneupDef() = save_entry->tuneup_def->clone();
2135  break;
2136  }
2137 
2141  break;
2142 
2143  default:
2144  break;
2145  }
2146 
2147  // Create spawn request while actor still exists
2148  // Note we don't use `ActorModifyRequest::Type::RELOAD` because we don't need the bundle reloaded.
2150  srq->asr_position = Ogre::Vector3(request->mpr_target_actor->getPosition().x, request->mpr_target_actor->getMinHeight(), request->mpr_target_actor->getPosition().z);
2151  srq->asr_rotation = Ogre::Quaternion(Ogre::Degree(270) - Ogre::Radian(request->mpr_target_actor->getRotation()), Ogre::Vector3::UNIT_Y);
2152  srq->asr_config = request->mpr_target_actor->getSectionConfig();
2156  srq->asr_debugview = (int)request->mpr_target_actor->GetGfxActor()->GetDebugView();
2158 
2159  // Remove the actor
2161 
2162  // Load our actor again, but only after it was deleted.
2164 }
2165 
2167 {
2168 
2169  this->UnLoadResource(entry);
2170 
2171  // Delete the files, one by one
2172  const std::string DELETEPROJ_TEMP_RG = "DeleteProjectTempRG";
2173  Ogre::ResourceGroupManager::getSingleton().createResourceGroup(DELETEPROJ_TEMP_RG, /*inGlobalPool=*/false);
2174  Ogre::ResourceGroupManager::getSingleton().addResourceLocation(
2175  entry->resource_bundle_path, entry->resource_bundle_type, DELETEPROJ_TEMP_RG, /*recursive=*/false, /*readOnly=*/false);
2176  Ogre::FileInfoListPtr filelist = Ogre::ResourceGroupManager::getSingleton().findResourceFileInfo(DELETEPROJ_TEMP_RG, "*.*");
2177  LOG(fmt::format("[RoR|ModCache] Deleting project '{}' (resource group '{}'), found {} files to erase.", entry->fname, entry->resource_group, filelist->size()));
2178  for (size_t i = 0; i < filelist->size(); i++)
2179  {
2180  Ogre::FileInfo fileinfo = filelist->at(i);
2181  if (!Ogre::FileSystemLayer::removeFile(PathCombine(entry->resource_bundle_path, fileinfo.filename)))
2182  {
2184  fmt::format(_LC("CacheSystem", "Problem deleting project '{}' - could not delete file '{}'"), entry->fname, fileinfo.filename));
2185  }
2186  }
2187 
2188  Ogre::ResourceGroupManager::getSingleton().destroyResourceGroup(DELETEPROJ_TEMP_RG);
2189 
2190  // Delete the directory itself
2191  if (!Ogre::FileSystemLayer::removeDirectory(entry->resource_bundle_path))
2192  {
2194  fmt::format(_LC("CacheSystem", "Problem deleting project '{}' - could not delete directory '{}'"), entry->fname, entry->resource_bundle_path));
2195  }
2196 
2197  // Remove the entry
2198  RoR::EraseIf(m_entries, [entry](CacheEntryPtr& e) { return e == entry; });
2199 
2200  // Force update of Tuning menu in TopMenubarUI.
2202 }
2203 
2205 {
2206  Ogre::StringUtil::toLowerCase(query.cqy_search_string);
2207  Ogre::StringUtil::toLowerCase(query.cqy_filter_guid);
2208  Ogre::StringUtil::toLowerCase(query.cqy_filter_target_filename);
2209  std::time_t cur_time = std::time(nullptr);
2210  for (CacheEntryPtr& entry: m_entries)
2211  {
2212  // Filter by GUID
2213  if (query.cqy_filter_guid != "")
2214  {
2215  // Addon parts have `guid` empty
2216  if ((entry->fext == "addonpart" && entry->addonpart_guids.count(query.cqy_filter_guid) == 0) ||
2217  (entry->fext != "addonpart" && entry->guid != query.cqy_filter_guid))
2218  {
2219  continue;
2220  }
2221  }
2222 
2223  // Filter by target filename; pass items which have no target filenames listed.
2224  if (query.cqy_filter_target_filename != "")
2225  {
2226  if (entry->fext == "addonpart"
2227  && entry->addonpart_filenames.size() > 0
2228  && entry->addonpart_filenames.count(query.cqy_filter_target_filename) == 0)
2229  {
2230  continue;
2231  }
2232  else if (entry->fext == "tuneup"
2233  && entry->tuneup_associated_filename != ""
2234  && entry->tuneup_associated_filename != query.cqy_filter_target_filename)
2235  {
2236  continue;
2237  }
2238  }
2239 
2240  // Filter by entry type
2241  bool add = false;
2242  if (entry->fext == "terrn2")
2243  add = (query.cqy_filter_type == LT_Terrain);
2244  if (entry->fext == "skin")
2245  add = (query.cqy_filter_type == LT_Skin);
2246  else if (entry->fext == "addonpart")
2247  add = (query.cqy_filter_type == LT_AddonPart);
2248  else if (entry->fext == "tuneup")
2249  add = (query.cqy_filter_type == LT_Tuneup);
2250  else if (entry->fext == "assetpack")
2251  add = (query.cqy_filter_type == LT_AssetPack);
2252  else if (entry->fext == "dashboard")
2253  add = (query.cqy_filter_type == LT_DashBoard);
2254  else if (entry->fext == "gadget")
2255  add = (query.cqy_filter_type == LT_Gadget);
2256  else if (entry->fext == "truck")
2257  add = (query.cqy_filter_type == LT_AllBeam || query.cqy_filter_type == LT_Vehicle || query.cqy_filter_type == LT_Truck);
2258  else if (entry->fext == "car")
2259  add = (query.cqy_filter_type == LT_AllBeam || query.cqy_filter_type == LT_Vehicle || query.cqy_filter_type == LT_Truck || query.cqy_filter_type == LT_Car);
2260  else if (entry->fext == "boat")
2261  add = (query.cqy_filter_type == LT_AllBeam || query.cqy_filter_type == LT_Boat);
2262  else if (entry->fext == "airplane")
2263  add = (query.cqy_filter_type == LT_AllBeam || query.cqy_filter_type == LT_Airplane);
2264  else if (entry->fext == "trailer")
2265  add = (query.cqy_filter_type == LT_AllBeam || query.cqy_filter_type == LT_Trailer || query.cqy_filter_type == LT_Extension);
2266  else if (entry->fext == "train")
2267  add = (query.cqy_filter_type == LT_AllBeam || query.cqy_filter_type == LT_Train);
2268  else if (entry->fext == "load")
2269  add = (query.cqy_filter_type == LT_AllBeam || query.cqy_filter_type == LT_Load || query.cqy_filter_type == LT_Extension);
2270 
2271  if (!add)
2272  {
2273  continue;
2274  }
2275 
2276  // Category usage stats
2277  query.cqy_res_category_usage[entry->categoryid]++;
2278 
2280 
2281  const bool is_fresh = (cur_time - entry->addtimestamp) < CACHE_FILE_FRESHNESS;
2282  if (is_fresh)
2284 
2285  // Filter by category
2286  if ((query.cqy_filter_category_id <= CacheCategoryId::CID_Max && query.cqy_filter_category_id != entry->categoryid) ||
2287  (query.cqy_filter_category_id == CID_Fresh && !is_fresh))
2288  {
2289  continue;
2290  }
2291 
2292  // Search
2293  size_t score = 0;
2294  bool match = false;
2295  Str<100> wheels_str;
2296  switch (query.cqy_search_method)
2297  {
2299  if (match = this->Match(score, entry->dname, query.cqy_search_string, 0)) { break; }
2300  if (match = this->Match(score, entry->fname, query.cqy_search_string, 100)) { break; }
2301  if (match = this->Match(score, entry->description, query.cqy_search_string, 200)) { break; }
2302  for (AuthorInfo const& author: entry->authors)
2303  {
2304  if (match = this->Match(score, author.name, query.cqy_search_string, 300)) { break; }
2305  if (match = this->Match(score, author.email, query.cqy_search_string, 400)) { break; }
2306  }
2307  break;
2308 
2310  match = this->Match(score, entry->guid, query.cqy_search_string, 0);
2311  break;
2312 
2314  for (AuthorInfo const& author: entry->authors)
2315  {
2316  if (match = this->Match(score, author.name, query.cqy_search_string, 0)) { break; }
2317  if (match = this->Match(score, author.email, query.cqy_search_string, 0)) { break; }
2318  }
2319  break;
2320 
2322  wheels_str << entry->wheelcount << "x" << entry->propwheelcount;
2323  match = this->Match(score, wheels_str.ToCStr(), query.cqy_search_string, 0);
2324  break;
2325 
2327  match = this->Match(score, entry->fname, query.cqy_search_string, 100);
2328  break;
2329 
2330  default: // CacheSearchMethod::
2331  match = true;
2332  break;
2333  };
2334 
2335  if (match)
2336  {
2337  query.cqy_results.emplace_back(entry, score);
2338  query.cqy_res_last_update = std::max(query.cqy_res_last_update, entry->addtimestamp);
2339  }
2340  }
2341 
2342  std::sort(query.cqy_results.begin(), query.cqy_results.end());
2343  return query.cqy_results.size();
2344 }
2345 
2346 bool CacheSystem::Match(size_t& out_score, std::string data, std::string const& query, size_t score)
2347 {
2348  Ogre::StringUtil::toLowerCase(data);
2349  size_t pos = data.find(query);
2350  if (pos != std::string::npos)
2351  {
2352  out_score = score + pos;
2353  return true;
2354  }
2355  else
2356  {
2357  return false;
2358  }
2359 }
2360 
2362 {
2363  if (cqr_score == other.cqr_score)
2364  {
2365  Ogre::String first = this->cqr_entry->dname;
2366  Ogre::String second = other.cqr_entry->dname;
2367  Ogre::StringUtil::toLowerCase(first);
2368  Ogre::StringUtil::toLowerCase(second);
2369  return first < second;
2370  }
2371 
2372  return cqr_score < other.cqr_score;
2373 }
2374 
RoR::App::sys_user_dir
CVar * sys_user_dir
Definition: Application.cpp:163
ROR_ASSERT
#define ROR_ASSERT(_EXPR)
Definition: Application.h:40
RoR::CacheEntry::resource_bundle_type
std::string resource_bundle_type
Archive type recognized by OGRE resource system: 'FileSystem' or 'Zip'.
Definition: CacheSystem.h:80
RoR::CacheEntry::addonpart_guids
std::set< std::string > addonpart_guids
GUIDs of all vehicles this addonpart is used with.
Definition: CacheSystem.h:98
RigDef::Engine::shift_up_rpm
float shift_up_rpm
Definition: RigDef_File.h:809
RoR::CacheQueryResult::cqr_score
size_t cqr_score
Definition: CacheSystem.h:154
RoR::CacheSystem::m_known_extensions
std::vector< Ogre::String > m_known_extensions
the extensions we track in the cache system
Definition: CacheSystem.h:376
RoR::CacheSystem::LoadAssetPack
void LoadAssetPack(CacheEntryPtr &t_dest, Ogre::String const &assetpack_filename)
Adds asset pack to the requesting cache entry's resource group.
Definition: CacheSystem.cpp:1408
RoR::App::GetContentManager
ContentManager * GetContentManager()
Definition: Application.cpp:271
RoR::CacheSystem::UnLoadResource
void UnLoadResource(CacheEntryPtr &t)
Unloads associated bundle, destroying all spawned actors.
Definition: CacheSystem.cpp:1648
RoR::CacheSystem::FillTerrainDetailInfo
void FillTerrainDetailInfo(CacheEntryPtr &entry, Ogre::DataStreamPtr ds, Ogre::String fname)
Definition: CacheSystem.cpp:1192
RoR::ModifyProjectRequest::mpr_subject
std::string mpr_subject
Definition: CacheSystem.h:256
RoR::TuneupDef::clone
TuneupDefPtr clone()
Definition: TuneupFileFormat.cpp:38
RoR::TuneupDef::force_remove_flexbodies
std::set< FlexbodyID_t > force_remove_flexbodies
UI overrides.
Definition: TuneupFileFormat.h:126
RoR::CID_Unsorted
@ CID_Unsorted
Definition: Application.h:344
RoR::CacheEntry::categoryid
int categoryid
category id
Definition: CacheSystem.h:72
RoR::CacheEntry::dname
Ogre::String dname
name parsed from the file
Definition: CacheSystem.h:70
RoR::CacheSearchMethod::GUID
@ GUID
Partial match in: guid.
RoR::TuneupDef::protected_flares
std::set< FlareID_t > protected_flares
Flares which cannot be altered via 'addonpart_unwanted_flare' directive.
Definition: TuneupFileFormat.h:139
RGN_CACHE
#define RGN_CACHE
Definition: Application.h:46
RoR::TuneupUtil::ParseTuneups
static std::vector< TuneupDefPtr > ParseTuneups(Ogre::DataStreamPtr &stream)
Definition: TuneupFileFormat.cpp:546
RoR::ModifyProjectRequestType::TUNEUP_FORCEREMOVE_PROP_SET
@ TUNEUP_FORCEREMOVE_PROP_SET
'subject_id' is prop ID.
RoR::ModifyProjectRequest::mpr_subject_id
int mpr_subject_id
Definition: CacheSystem.h:257
RoR::CacheEntry::tags
Ogre::String tags
Definition: CacheSystem.h:106
RoR::CreateProjectRequest::cpr_description
std::string cpr_description
Optional, implemented for tuneups.
Definition: CacheSystem.h:210
RoR::GenericDocContext::isTokInt
bool isTokInt(int offset=0) const
Definition: GenericFileFormat.h:124
RoR::MACHINE
@ MACHINE
its a machine
Definition: SimData.h:96
RoR::Actor::ensureWorkingTuneupDef
void ensureWorkingTuneupDef()
Creates a working tuneup def if it doesn't exist yet.
Definition: Actor.cpp:4679
RoR::CacheEntry::deleted
bool deleted
is this mod deleted?
Definition: CacheSystem.h:84
RoR::TRUCK
@ TRUCK
its a truck (or other land vehicle)
Definition: SimData.h:93
RoR::CacheSystem::DeleteProject
void DeleteProject(CacheEntryPtr &entry)
Definition: CacheSystem.cpp:2166
RoR::Console::CONSOLE_MSGTYPE_TERRN
@ CONSOLE_MSGTYPE_TERRN
Parsing/spawn/simulation messages for terrain.
Definition: Console.h:64
RoR::TuneupDef::force_remove_props
std::set< PropID_t > force_remove_props
UI overrides.
Definition: TuneupFileFormat.h:125
RoR::CacheSystem::FillDashboardDetailInfo
void FillDashboardDetailInfo(CacheEntryPtr &entry, Ogre::DataStreamPtr ds)
Definition: CacheSystem.cpp:1303
RoR::CacheQuery::cqy_filter_category_id
int cqy_filter_category_id
Definition: CacheSystem.h:170
RoR::CacheEntry::tuneup_def
TuneupDefPtr tuneup_def
Cached tuning info, added on first use or during cache rebuild.
Definition: CacheSystem.h:93
RoR::LT_AssetPack
@ LT_AssetPack
Definition: Application.h:323
RoR::CacheSystem::FillGadgetDetailInfo
void FillGadgetDetailInfo(CacheEntryPtr &entry, Ogre::DataStreamPtr ds)
Definition: CacheSystem.cpp:1340
RoR::LT_AddonPart
@ LT_AddonPart
Definition: Application.h:321
RoR::Terrn2Parser
Definition: Terrn2FileFormat.h:80
RoR::GenericDocContext::isTokKeyword
bool isTokKeyword(int offset=0) const
Definition: GenericFileFormat.h:126
RoR::ModifyProjectRequestType::TUNEUP_PROTECTED_FLEXBODY_SET
@ TUNEUP_PROTECTED_FLEXBODY_SET
'subject_id' is flexbody ID.
RoR::ActorSpawnRequest::asr_origin
Origin asr_origin
Definition: SimData.h:839
RoR::App::GetGuiManager
GUIManager * GetGuiManager()
Definition: Application.cpp:273
GUI_GameMainMenu.h
RoR::ContentManager::DeleteDiskFile
bool DeleteDiskFile(std::string const &filename, std::string const &rg_name)
Definition: ContentManager.cpp:522
RoR::ModifyProjectRequestType::TUNEUP_FORCEREMOVE_PROP_RESET
@ TUNEUP_FORCEREMOVE_PROP_RESET
'subject_id' is prop ID.
RoR::AuthorInfo::type
Ogre::String type
Definition: CacheSystem.h:50
RigDef::Engine::gear_ratios
std::vector< float > gear_ratios
Definition: RigDef_File.h:814
RoR::CacheEntry::version
int version
file's version
Definition: CacheSystem.h:78
file
This is a raw Ogre binding for Imgui No project cmake file
Definition: README-OgreImGui.txt:3
RoR::CacheSystem::ClearResourceGroups
void ClearResourceGroups()
Definition: CacheSystem.cpp:464
RoR::ModifyProjectRequestType::TUNEUP_FORCEREMOVE_FLEXBODY_SET
@ TUNEUP_FORCEREMOVE_FLEXBODY_SET
'subject_id' is flexbody ID.
RoR::CacheSystem::RemoveFileCache
void RemoveFileCache(CacheEntryPtr &entry)
Definition: CacheSystem.cpp:1066
RigDef::Parser::GetFile
RigDef::DocumentPtr GetFile()
Definition: RigDef_Parser.h:77
RoR::CacheQuery::cqy_search_method
CacheSearchMethod cqy_search_method
Definition: CacheSystem.h:173
RigDef::Parser::GetSequentialImporter
SequentialImporter * GetSequentialImporter()
Definition: RigDef_Parser.h:82
RoR::LT_Skin
@ LT_Skin
Definition: Application.h:319
RigDef::Parser::Finalize
void Finalize()
Definition: RigDef_Parser.cpp:2850
ContentManager.h
RoR::CreateProjectRequestType::SAVE_TUNEUP
@ SAVE_TUNEUP
Dumps .tuneup file with CID_Tuneup from source actor, will not overwrite existing unless explicitly i...
RoR::CacheSystem::m_update_time
std::time_t m_update_time
Ensures that all inserted files share the same timestamp.
Definition: CacheSystem.h:372
RoR::CreateProjectRequest::~CreateProjectRequest
~CreateProjectRequest()
Definition: CacheSystem.cpp:115
format
Truck file format(technical spec)
RoR::ContentManager::InitManagedMaterials
void InitManagedMaterials(std::string const &rg_name)
Definition: ContentManager.cpp:379
RoR::CacheSystem::LoadModCache
void LoadModCache(CacheValidity validity)
Definition: CacheSystem.cpp:153
RoR::CacheSystem::AddFile
void AddFile(Ogre::String group, Ogre::FileInfo f, Ogre::String ext)
Definition: CacheSystem.cpp:744
RoR::CacheEntry::driveable
ActorType driveable
Definition: CacheSystem.h:139
RoR::ActorState::DISPOSED
@ DISPOSED
removed from simulation, still in memory to satisfy pointers.
RoR::CacheEntry::number
CacheEntryID_t number
Sequential number, assigned internally, used by Selector-GUI.
Definition: CacheSystem.h:64
RoR::CacheEntry::importcommands
bool importcommands
Definition: CacheSystem.h:136
Terrn2FileFormat.h
RoR::LT_Tuneup
@ LT_Tuneup
Definition: Application.h:322
RoR::CreateProjectRequest::cpr_type
CreateProjectRequestType cpr_type
Definition: CacheSystem.h:213
RoR::CacheSystem::m_entries
std::vector< CacheEntryPtr > m_entries
Definition: CacheSystem.h:375
RoR::CacheSearchMethod::FILENAME
@ FILENAME
Partial match in file name.
RoR::CVar::getBool
bool getBool() const
Definition: CVar.h:98
RoR::TRIGGER_EVENT_ASYNC
void TRIGGER_EVENT_ASYNC(scriptEvents type, int arg1, int arg2ex=0, int arg3ex=0, int arg4ex=0, std::string arg5ex="", std::string arg6ex="", std::string arg7ex="", std::string arg8ex="")
Asynchronously (via MSG_SIM_SCRIPT_EVENT_TRIGGERED) invoke script function eventCallbackEx(),...
Definition: ScriptEngine.h:51
RoR::modCacheActivityType
modCacheActivityType
Argument #2 of script event RoR::SE_GENERIC_MODCACHE_ACTIVITY
Definition: ScriptEvents.h:88
RoR::GenericDocument::OPTION_ALLOW_SLASH_COMMENTS
static const BitMask_t OPTION_ALLOW_SLASH_COMMENTS
Allow comments starting with //.
Definition: GenericFileFormat.h:69
RoR::ModifyProjectRequestType::TUNEUP_USE_ADDONPART_RESET
@ TUNEUP_USE_ADDONPART_RESET
'subject' is addonpart filename.
RigDef::Parser
Checks the rig-def file syntax and pulls data to File object.
Definition: RigDef_Parser.h:56
RoR::CacheQuery
Definition: CacheSystem.h:167
RoR::LogFormat
void LogFormat(const char *format,...)
Improved logging utility. Uses fixed 2Kb buffer.
Definition: Application.cpp:428
RoR::ModifyProjectRequestType::TUNEUP_PROTECTED_WHEEL_RESET
@ TUNEUP_PROTECTED_WHEEL_RESET
'subject_id' is wheel ID.
RoR::SplitBundleQualifiedFilename
void SplitBundleQualifiedFilename(const std::string &bundleQualifiedFilename, std::string &out_bundleName, std::string &out_filename)
Definition: Utils.cpp:228
RoR::CacheSystem::GetEntryByNumber
CacheEntryPtr GetEntryByNumber(int modid)
Definition: CacheSystem.cpp:549
RoR::HashData
Ogre::String HashData(const char *key, int len)
Definition: Utils.cpp:51
RoR::TuneupUtil::ExportTuneup
static void ExportTuneup(Ogre::DataStreamPtr &stream, TuneupDefPtr &tuneup)
Definition: TuneupFileFormat.cpp:638
RoR::ModifyProjectRequestType::TUNEUP_PROTECTED_FLEXBODY_RESET
@ TUNEUP_PROTECTED_FLEXBODY_RESET
'subject_id' is flexbody ID.
RoR::CID_Projects
@ CID_Projects
For truck files under 'projects/' directory, to allow listing from editors.
Definition: Application.h:340
RoR::GenericDocContext::getTokString
std::string getTokString(int offset=0) const
Definition: GenericFileFormat.h:114
RoR::TuneupDef::name
std::string name
Definition: TuneupFileFormat.h:97
RoR::CacheEntry::propwheelcount
int propwheelcount
Definition: CacheSystem.h:116
RoR::Console::putMessage
void putMessage(MessageArea area, MessageType type, std::string const &msg, std::string icon="")
Definition: Console.cpp:103
RoR::SE_GENERIC_MODCACHE_ACTIVITY
@ SE_GENERIC_MODCACHE_ACTIVITY
Triggered when status of modcache changes, args: #1 type, #2 entry number, for other args see RoR::mo...
Definition: ScriptEvents.h:62
RoR::CacheEntry::tuneup_associated_filename
std::string tuneup_associated_filename
Value of 'filename' field in the tuneup file; always lowercase.
Definition: CacheSystem.h:102
RoR::CacheSystem::FillAddonPartDetailInfo
void FillAddonPartDetailInfo(CacheEntryPtr &entry, Ogre::DataStreamPtr ds)
Definition: CacheSystem.cpp:1237
RoR::CacheEntry::hasSubmeshs
bool hasSubmeshs
Definition: CacheSystem.h:109
RoR::LT_Load
@ LT_Load
Definition: Application.h:317
RoR::CacheEntry::resource_group
Ogre::String resource_group
Resource group of the loaded bundle. Empty if not loaded yet.
Definition: CacheSystem.h:89
RoR::LT_Airplane
@ LT_Airplane
Definition: Application.h:314
RoR::CacheSystem::ModifyProject
void ModifyProject(ModifyProjectRequest *request)
Definition: CacheSystem.cpp:1957
RoR::GenericDocContext::seekNextLine
bool seekNextLine()
Definition: GenericFileFormat.cpp:1140
RoR::ActorSpawnRequest::asr_working_tuneup
TuneupDefPtr asr_working_tuneup
Only filled when editing tuneup via Tuning menu.
Definition: SimData.h:838
TuneupFileFormat.h
The vehicle tuning system; applies addonparts and user overrides to vehicles.
detectMiniType
Ogre::String detectMiniType(String filename, String group)
Definition: CacheSystem.cpp:1052
RoR::Actor::GetGfxActor
GfxActor * GetGfxActor()
Definition: Actor.h:275
RoR::FolderExists
bool FolderExists(const char *path)
Path must be UTF-8 encoded.
Definition: PlatformUtils.cpp:169
RoR::TuneupDef::use_addonparts
std::set< std::string > use_addonparts
Addonpart filenames.
Definition: TuneupFileFormat.h:109
RigDef::Engine::shift_down_rpm
float shift_down_rpm
Definition: RigDef_File.h:808
RoR::ModifyProjectRequestType::TUNEUP_USE_ADDONPART_SET
@ TUNEUP_USE_ADDONPART_SET
'subject' is addonpart filename.
RoR::ModifyProjectRequestType::TUNEUP_PROTECTED_EXHAUST_RESET
@ TUNEUP_PROTECTED_EXHAUST_RESET
'subject_id' is exhaust ID.
RoR::GenericDocContext::getTokFloat
float getTokFloat(int offset=0) const
Definition: GenericFileFormat.h:115
RoR::CID_None
@ CID_None
Definition: Application.h:330
RoR::LT_Car
@ LT_Car
Definition: Application.h:312
RoR::ModifyProjectRequestType::TUNEUP_PROTECTED_MANAGEDMAT_SET
@ TUNEUP_PROTECTED_MANAGEDMAT_SET
'subject' is managed material name.
RoR::CreateProjectRequest::CreateProjectRequest
CreateProjectRequest()
Definition: CacheSystem.cpp:110
RoR::CacheEntry::customtach
bool customtach
Definition: CacheSystem.h:133
RoR::CacheEntry::sectionconfigs
std::vector< Ogre::String > sectionconfigs
Definition: CacheSystem.h:142
RoR::CacheSystem::ExportEntryToJson
void ExportEntryToJson(rapidjson::Value &j_entries, rapidjson::Document &j_doc, CacheEntryPtr const &entry)
Definition: CacheSystem.cpp:583
Utils.h
RoR::NOT_DRIVEABLE
@ NOT_DRIVEABLE
not drivable at all
Definition: SimData.h:92
RoR::CacheSystem::StripSHA1fromString
static Ogre::String StripSHA1fromString(Ogre::String sha1str)
Definition: CacheSystem.cpp:736
RoR::CacheSystem::ImportEntryFromJson
void ImportEntryFromJson(rapidjson::Value &j_entry, CacheEntryPtr &out_entry)
Definition: CacheSystem.cpp:297
RoR::CacheEntry::enginetype
char enginetype
Definition: CacheSystem.h:141
Language.h
RoR::CacheEntry::minrpm
float minrpm
Definition: CacheSystem.h:130
RoR::Terrn2DocumentPtr
std::shared_ptr< Terrn2Document > Terrn2DocumentPtr
Definition: ForwardDeclarations.h:217
RoR::Terrn2Author
Definition: Terrn2FileFormat.h:36
RoR::CacheEntry::description
Ogre::String description
Definition: CacheSystem.h:105
RoR::LT_Extension
@ LT_Extension
Definition: Application.h:318
RoR::CacheQueryResult::cqr_entry
CacheEntryPtr cqr_entry
Definition: CacheSystem.h:153
RoR::App::app_extra_mod_path
CVar * app_extra_mod_path
Definition: Application.cpp:87
RefCountingObjectPtr< CacheEntry >
RoR::Console::CONSOLE_SYSTEM_ERROR
@ CONSOLE_SYSTEM_ERROR
Definition: Console.h:52
RoR::CacheSystem::PruneCache
void PruneCache()
Definition: CacheSystem.cpp:431
RigDef::Parser::ProcessOgreStream
void ProcessOgreStream(Ogre::DataStream *stream, Ogre::String resource_group)
Definition: RigDef_Parser.cpp:3405
GUIManager.h
RoR::CreateProjectRequest::cpr_source_entry
CacheEntryPtr cpr_source_entry
The original mod to copy files from.
Definition: CacheSystem.h:211
Actor.h
RoR::CreateProjectRequest::cpr_name
std::string cpr_name
Directory and also the mod file (without extension).
Definition: CacheSystem.h:209
RoR::Terrn2Author::name
std::string name
Definition: Terrn2FileFormat.h:39
RoR::ModifyProjectRequestType::TUNEUP_FORCEREMOVE_MANAGEDMAT_SET
@ TUNEUP_FORCEREMOVE_MANAGEDMAT_SET
'subject' is managed material name.
RoR::CacheSystem::IsPathContentDirRoot
bool IsPathContentDirRoot(const std::string &path) const
Definition: CacheSystem.cpp:1524
RoR::GUIManager::LoadingWindow
GUI::LoadingWindow LoadingWindow
Definition: GUIManager.h:130
RoR::TuneupDef::protected_props
std::set< PropID_t > protected_props
Props which cannot be altered via 'addonpart_tweak_prop' or 'addonpart_unwanted_prop' directive.
Definition: TuneupFileFormat.h:137
w
float w
Definition: (ValueTypes) quaternion.h:4
RoR::CacheEntry::forwardcommands
bool forwardcommands
Definition: CacheSystem.h:135
RoR::Console::CONSOLE_SYSTEM_NOTICE
@ CONSOLE_SYSTEM_NOTICE
Definition: Console.h:51
RoR::ActorSpawnRequest
Definition: SimData.h:813
RoR::CacheEntry::beamcount
int beamcount
Definition: CacheSystem.h:111
RoR::getTimeStamp
std::time_t getTimeStamp()
Definition: Utils.cpp:74
RoR::GUI::TopMenubar::tuning_savebox_visible
bool tuning_savebox_visible
User pressed 'save active' to open savebox.
Definition: GUI_TopMenubar.h:117
RoR::CacheEntry::addonpart_filenames
std::set< std::string > addonpart_filenames
File names of all vehicles this addonpart is used with. If empty, any filename goes.
Definition: CacheSystem.h:99
RoR::CacheQueryResult::operator<
bool operator<(CacheQueryResult const &other) const
Definition: CacheSystem.cpp:2361
RoR::CacheSystem::LoadCacheFileJson
CacheValidity LoadCacheFileJson()
Definition: CacheSystem.cpp:399
RoR::GenericDocContext::isTokString
bool isTokString(int offset=0) const
Definition: GenericFileFormat.h:122
RoR::LT_Boat
@ LT_Boat
Definition: Application.h:313
RoR::CacheEntry::shockcount
int shockcount
Definition: CacheSystem.h:112
RigDef::Fileinfo
Definition: RigDef_File.h:870
RoR::ModifyProjectRequestType::TUNEUP_FORCED_WHEEL_SIDE_SET
@ TUNEUP_FORCED_WHEEL_SIDE_SET
'subject_id' is wheel ID, 'value_int' is RoR::WheelSide
RoR::ModifyProjectRequestType::TUNEUP_PROTECTED_FLARE_RESET
@ TUNEUP_PROTECTED_FLARE_RESET
'subject_id' is flare ID.
RigDef::Fileinfo::unique_id
Ogre::String unique_id
Definition: RigDef_File.h:872
RoR::LT_Truck
@ LT_Truck
Definition: Application.h:311
RoR::CacheQuery::cqy_res_category_usage
std::map< int, size_t > cqy_res_category_usage
Total usage (ignores search params + category filter)
Definition: CacheSystem.h:177
RoR::CacheSystem::LoadAssociatedSkinDef
void LoadAssociatedSkinDef(CacheEntryPtr &cache_entry)
Loads+parses the .skin file and updates all related CacheEntries.
Definition: CacheSystem.cpp:1686
RoR::ActorSpawnRequest::asr_config
Ogre::String asr_config
Definition: SimData.h:832
RoR::GenericDocContext::countLineArgs
int countLineArgs()
Definition: GenericFileFormat.cpp:1158
RoR::CacheSearchMethod::AUTHORS
@ AUTHORS
Partial match in: author name/email.
RoR::CacheEntry::rotatorscount
int rotatorscount
Definition: CacheSystem.h:123
RoR::ModifyProjectRequestType::TUNEUP_FORCEREMOVE_FLARE_RESET
@ TUNEUP_FORCEREMOVE_FLARE_RESET
'subject_id' is flare ID.
RoR::CacheEntry::soundsourcescount
int soundsourcescount
Definition: CacheSystem.h:126
RoR::CacheSystem::FillTruckDetailInfo
void FillTruckDetailInfo(CacheEntryPtr &entry, Ogre::DataStreamPtr ds, Ogre::String fname, Ogre::String group)
Definition: CacheSystem.cpp:848
RoR::ModifyProjectRequestType::TUNEUP_PROTECTED_FLARE_SET
@ TUNEUP_PROTECTED_FLARE_SET
'subject_id' is flare ID.
RoR::GameContext::ChainMessage
void ChainMessage(Message m)
Add to last pushed message's chain.
Definition: GameContext.cpp:73
RoR::Actor::getMinHeight
float getMinHeight(bool skip_virtual_nodes=true)
Definition: Actor.cpp:1479
RoR::CVar::getStr
std::string const & getStr() const
Definition: CVar.h:95
RoR::Actor::removeWorkingTuneupDef
void removeWorkingTuneupDef()
Deletes the working tuneup def object if it exists.
Definition: Actor.cpp:4688
RoR::CacheEntry::torque
float torque
Definition: CacheSystem.h:132
RoR::Str
Wrapper for classic c-string (local buffer) Refresher: strlen() excludes '\0' terminator; strncat() A...
Definition: Str.h:35
RoR::CacheSystem::FindEntryByFilename
CacheEntryPtr FindEntryByFilename(RoR::LoaderType type, bool partial, const std::string &_filename_maybe_bundlequalified)
Returns NULL if none found; "Bundle-qualified" format also specifies the ZIP/directory in modcache,...
Definition: CacheSystem.cpp:186
RoR::CacheSystem::ClearCache
void ClearCache()
Definition: CacheSystem.cpp:712
SimData.h
Core data structures for simulation; Everything affected by by either physics, network or user intera...
RoR::CacheSystem::Match
bool Match(size_t &out_score, std::string data, std::string const &query, size_t)
Definition: CacheSystem.cpp:2346
RoR::CacheEntry::authors
std::vector< AuthorInfo > authors
authors
Definition: CacheSystem.h:86
RoR::PathCombine
std::string PathCombine(std::string a, std::string b)
Definition: PlatformUtils.h:48
GUI_LoadingWindow.h
RoR::CacheEntry::filecachename
Ogre::String filecachename
preview image filename
Definition: CacheSystem.h:87
CacheSystem.h
A database of user-installed content alias 'mods' (vehicles, terrains...)
RoR::CacheSystem::ParseZipArchives
void ParseZipArchives(Ogre::String group)
Definition: CacheSystem.cpp:1125
RoR::CacheEntry::propscount
int propscount
Definition: CacheSystem.h:119
RoR::CacheSystem::DetectDuplicates
void DetectDuplicates()
Definition: CacheSystem.cpp:477
RoR::TuneupDef::protected_wheels
std::set< WheelID_t > protected_wheels
Wheels that cannot be altered via 'addonpart_tweak_wheel'.
Definition: TuneupFileFormat.h:136
RoR::SkinParser::ParseSkins
static std::vector< SkinDocumentPtr > ParseSkins(Ogre::DataStreamPtr &stream)
Definition: SkinFileFormat.cpp:34
RoR::CacheValidity
CacheValidity
Definition: CacheSystem.h:188
ErrorUtils.h
RGN_TEMP
#define RGN_TEMP
Definition: Application.h:45
RoR::ModifyProjectRequestType::TUNEUP_PROTECTED_EXHAUST_SET
@ TUNEUP_PROTECTED_EXHAUST_SET
'subject_id' is exhaust ID.
CheckAndReplacePathIgnoreCase
static bool CheckAndReplacePathIgnoreCase(const CacheEntryPtr &entry, CVar *dir, const std::string &dir_label, std::string &out_rgname)
Definition: CacheSystem.cpp:1447
RoR::ActorSpawnRequest::asr_cache_entry
CacheEntryPtr asr_cache_entry
Optional, overrides 'asr_filename' and 'asr_cache_entry_num'.
Definition: SimData.h:830
RoR::ContentManager::ResourcePack::SCRIPTS
static const ResourcePack SCRIPTS
Definition: ContentManager.h:68
RoR::CacheQueryResult
Definition: CacheSystem.h:147
ScriptEngine.h
RigDef::Engine::torque
float torque
Definition: RigDef_File.h:810
RoR::CacheEntry::addtimestamp
std::time_t addtimestamp
timestamp when this file was added to the cache
Definition: CacheSystem.h:75
RoR::AuthorInfo::id
int id
Definition: CacheSystem.h:49
RoR::GameContext::PushMessage
void PushMessage(Message m)
Doesn't guarantee order! Use ChainMessage() if order matters.
Definition: GameContext.cpp:66
CACHE_FILE_FRESHNESS
#define CACHE_FILE_FRESHNESS
Definition: CacheSystem.h:43
RoR::ContentManager::ResourcePack::TEXTURES
static const ResourcePack TEXTURES
Definition: ContentManager.h:70
RoR::Actor::getSectionConfig
Ogre::String getSectionConfig()
Definition: Actor.h:236
RoR::CacheEntry::~CacheEntry
~CacheEntry()
Definition: CacheSystem.cpp:105
RoR::AuthorInfo::email
Ogre::String email
Definition: CacheSystem.h:52
RoR::TuneupDef::thumbnail
std::string thumbnail
Definition: TuneupFileFormat.h:100
RoR::Str::ToCStr
const char * ToCStr() const
Definition: Str.h:46
RigDef::Fileinfo::file_version
int file_version
Definition: RigDef_File.h:874
RoR::CacheSystem::ParseSingleZip
void ParseSingleZip(Ogre::String path)
Definition: CacheSystem.cpp:1148
RoR::CacheSystem::FillTuneupDetailInfo
void FillTuneupDetailInfo(CacheEntryPtr &entry, TuneupDefPtr &tuneup_def)
Definition: CacheSystem.cpp:1387
RoR::CacheSystem::LoadAssociatedTuneupDef
void LoadAssociatedTuneupDef(CacheEntryPtr &cache_entry)
Loads+parses the .tuneup file and updates all related CacheEntries.
Definition: CacheSystem.cpp:1735
RoR::MODCACHEACTIVITY_ENTRY_ADDED
@ MODCACHEACTIVITY_ENTRY_ADDED
Args: #1 type, #2 entry number, –, –, #5 fname, #6 fext.
Definition: ScriptEvents.h:92
RoR::CacheSystem::GetPrettyName
Ogre::String GetPrettyName(Ogre::String fname)
Definition: CacheSystem.cpp:559
RoR::GUI::LoadingWindow::SetVisible
void SetVisible(bool v)
Definition: GUI_LoadingWindow.h:41
RoR::CacheSystem::ParseKnownFiles
bool ParseKnownFiles(Ogre::String group)
Definition: CacheSystem.cpp:1171
RoR::CacheEntry::skin_def
SkinDocumentPtr skin_def
Cached skin info, added on first use or during cache rebuild.
Definition: CacheSystem.h:92
RoR::ModifyProjectRequestType::TUNEUP_FORCEREMOVE_MANAGEDMAT_RESET
@ TUNEUP_FORCEREMOVE_MANAGEDMAT_RESET
'subject' is managed material name.
RoR::GUI::LoadingWindow::SetProgress
void SetProgress(int _percent, const std::string &_text="", bool render_frame=true)
Definition: GUI_LoadingWindow.cpp:33
RoR::Actor::getPosition
Ogre::Vector3 getPosition()
Definition: Actor.cpp:371
RoR::TuneupDef::protected_managedmats
std::set< std::string > protected_managedmats
Managed materials which cannot be altered via 'addonpart_tweak_managedmaterial' directive.
Definition: TuneupFileFormat.h:141
RoR::CreateFolder
void CreateFolder(const char *path)
Path must be UTF-8 encoded.
Definition: PlatformUtils.cpp:175
GfxScene.h
PlatformUtils.h
Platform-specific utilities. We use narrow UTF-8 encoded strings as paths. Inspired by http://utf8eve...
RoR::CacheEntry::fpath
Ogre::String fpath
filepath relative to the .zip file
Definition: CacheSystem.h:66
RoR::CacheSearchMethod::FULLTEXT
@ FULLTEXT
Partial match in: name, filename, description, author name/mail.
RoR::LT_Gadget
@ LT_Gadget
Definition: Application.h:325
RoR::CacheSearchMethod::WHEELS
@ WHEELS
Wheel configuration, i.e. 4x4.
RoR::LT_Vehicle
@ LT_Vehicle
Definition: Application.h:310
RoR::LT_Trailer
@ LT_Trailer
Definition: Application.h:315
RoR::CacheCategoryId
CacheCategoryId
Definition: Application.h:328
RoR::LoaderType
LoaderType
< Search mode for ModCache::Query() & Operation mode for GUI::MainSelector
Definition: Application.h:306
RoR::ModifyProjectRequestType::TUNEUP_FORCEREMOVE_EXHAUST_RESET
@ TUNEUP_FORCEREMOVE_EXHAUST_RESET
'subject_id' is exhaust ID.
RoR::CacheEntry::rescuer
bool rescuer
Definition: CacheSystem.h:137
RoR::ModifyProjectRequest::mpr_target_actor
ActorPtr mpr_target_actor
Definition: CacheSystem.h:252
Application.h
Central state/object manager and communications hub.
RoR::App::GetConsole
Console * GetConsole()
Definition: Application.cpp:274
RoR::CacheSystem::m_filenames_hash_loaded
std::string m_filenames_hash_loaded
hash from cachefile, for quick update detection
Definition: CacheSystem.h:373
RoR::LT_DashBoard
@ LT_DashBoard
Definition: Application.h:324
RoR::CacheQueryResult::CacheQueryResult
CacheQueryResult(CacheEntryPtr entry, size_t score)
Definition: CacheSystem.cpp:120
RoR::TuneupDef::description
std::string description
Definition: TuneupFileFormat.h:101
RigDef::Parser::Prepare
void Prepare()
Definition: RigDef_Parser.cpp:2733
RoR::CacheQuery::cqy_search_string
std::string cqy_search_string
Definition: CacheSystem.h:174
RoR::AddonPartUtility::DoubleCheckForAddonpartConflict
static bool DoubleCheckForAddonpartConflict(ActorPtr target_actor, CacheEntryPtr addonpart_entry)
Definition: AddonPartFileFormat.cpp:911
RoR::CacheSystem::m_content_dirs
std::vector< std::string > m_content_dirs
the various mod directories we track in the cache system
Definition: CacheSystem.h:377
RoR::App::GetGameContext
GameContext * GetGameContext()
Definition: Application.cpp:284
RoR::CacheSystem::m_categories
std::map< int, Ogre::String > m_categories
Definition: CacheSystem.h:379
RoR::CacheEntry
Definition: CacheSystem.h:55
files
This is a raw Ogre binding for Imgui No project cmake no just four source files
Definition: README-OgreImGui.txt:3
RGN_CONTENT
#define RGN_CONTENT
Definition: Application.h:49
RoR::ContentManager::ResourcePack::MATERIALS
static const ResourcePack MATERIALS
Definition: ContentManager.h:59
RoR::CacheSystem::m_filenames_hash_generated
std::string m_filenames_hash_generated
stores hash over the content, for quick update detection
Definition: CacheSystem.h:374
RoR::AIRPLANE
@ AIRPLANE
its an airplane
Definition: SimData.h:94
RoR::CacheQuery::cqy_res_last_update
std::time_t cqy_res_last_update
Definition: CacheSystem.h:178
RoR::CacheSystem::GenerateFileCache
void GenerateFileCache(CacheEntryPtr &entry, Ogre::String group)
Definition: CacheSystem.cpp:1074
CACHE_FILE_FORMAT
#define CACHE_FILE_FORMAT
Definition: CacheSystem.h:42
RoR::CacheEntry::uniqueid
Ogre::String uniqueid
file's unique id
Definition: CacheSystem.h:76
RoR::CacheSystem::EvaluateCacheValidity
CacheValidity EvaluateCacheValidity()
Definition: CacheSystem.cpp:259
RoR::ActorSpawnRequest::asr_skin_entry
CacheEntryPtr asr_skin_entry
Definition: SimData.h:836
RoR::AuthorInfo
Definition: CacheSystem.h:47
RoR::ModifyProjectRequest::mpr_value_int
int mpr_value_int
Definition: CacheSystem.h:258
RoR::CacheEntry::loadmass
float loadmass
Definition: CacheSystem.h:129
RoR::ModifyProjectRequestType::TUNEUP_FORCEREMOVE_EXHAUST_SET
@ TUNEUP_FORCEREMOVE_EXHAUST_SET
'subject_id' is exhaust ID.
RoR::CacheEntry::fixescount
int fixescount
Definition: CacheSystem.h:113
RoR::Actor::getUsedActorEntry
CacheEntryPtr & getUsedActorEntry()
The actor entry itself.
Definition: Actor.cpp:4664
_LC
#define _LC(ctx, str)
Definition: Language.h:42
RoR::GUIManager::TopMenubar
GUI::TopMenubar TopMenubar
Definition: GUIManager.h:131
RoR::CacheSystem::CreateProject
CacheEntryPtr CreateProject(CreateProjectRequest *request)
Creates subdirectory in 'My Games\Rigs of Rods\projects', pre-populates it with files and adds modcac...
Definition: CacheSystem.cpp:1784
RoR::CacheSystem::LoadSupplementaryDocuments
void LoadSupplementaryDocuments(CacheEntryPtr &t)
Loads the associated .truck*, .skin and .tuneup files.
Definition: CacheSystem.cpp:1503
RoR::CacheSystem::FillSkinDetailInfo
void FillSkinDetailInfo(CacheEntryPtr &entry, std::shared_ptr< SkinDocument > &skin_def)
Definition: CacheSystem.cpp:1218
RoR::CVar
Quake-style console variable, defined in RoR.cfg or crated via Console UI and scripts.
Definition: CVar.h:52
RoR::ModifyProjectRequestType::TUNEUP_FORCEREMOVE_FLARE_SET
@ TUNEUP_FORCEREMOVE_FLARE_SET
'subject_id' is flare ID.
RoR::App::diag_log_console_echo
CVar * diag_log_console_echo
Definition: Application.cpp:146
RoR::ModifyProjectRequest
Definition: CacheSystem.h:250
RoR::CID_Fresh
@ CID_Fresh
Definition: Application.h:346
RoR::GenericDocContext::endOfFile
bool endOfFile(int offset=0) const
Definition: GenericFileFormat.h:111
RoR::CreateProjectRequest
Creates subdirectory in 'My Games\Rigs of Rods\projects', pre-populates it with files and adds modcac...
Definition: CacheSystem.h:204
RoR::LT_Terrain
@ LT_Terrain
Definition: Application.h:309
RoR::ModifyProjectRequestType::PROJECT_LOAD_TUNEUP
@ PROJECT_LOAD_TUNEUP
'subject' is tuneup filename. This overwrites the auto-generated tuneup with the save.
RoR::TuneupDef::force_remove_managedmats
std::set< std::string > force_remove_managedmats
User unticked an UI checkbox in Tuning menu, section Managed Materials.
Definition: TuneupFileFormat.h:130
RoR::CacheSystem::ActorTypeToName
std::string ActorTypeToName(ActorType driveable)
Definition: CacheSystem.cpp:569
RoR::CacheEntry::fileformatversion
int fileformatversion
Definition: CacheSystem.h:108
RoR::CacheEntry::numgears
int numgears
Definition: CacheSystem.h:140
RoR::CacheSystem::ComposeResourceGroupName
static std::string ComposeResourceGroupName(const CacheEntryPtr &entry)
Definition: CacheSystem.cpp:1482
RoR::ModifyProjectRequestType::TUNEUP_PROTECTED_PROP_SET
@ TUNEUP_PROTECTED_PROP_SET
'subject_id' is prop ID.
RoR::CreateProjectRequest::cpr_source_actor
ActorPtr cpr_source_actor
Only for type SAVE_TUNEUP
Definition: CacheSystem.h:212
RoR::TuneupDef::author_id
int author_id
Definition: TuneupFileFormat.h:103
RoR::App::GetCacheSystem
CacheSystem * GetCacheSystem()
Definition: Application.cpp:276
RoR::CacheValidity::VALID
@ VALID
RoR::ContentManager::AddResourcePack
void AddResourcePack(ResourcePack const &resource_pack, std::string const &override_rgn="")
Loads resources if not already loaded.
Definition: ContentManager.cpp:99
RoR::CacheSystem::FetchSkinByName
CacheEntryPtr FetchSkinByName(std::string const &skin_name)
Definition: CacheSystem.cpp:1674
RoR::CacheValidity::NEEDS_REBUILD
@ NEEDS_REBUILD
RoR::GenericDocument::OPTION_NAKEDSTR_USCORES_TO_SPACES
static const BitMask_t OPTION_NAKEDSTR_USCORES_TO_SPACES
Only for OPTION_ALLOW_NAKED_STRINGS: Replace underscores with spaces in naked strings (classic behavi...
Definition: GenericFileFormat.h:76
RoR::CID_Tuneups
@ CID_Tuneups
For unsorted tuneup files.
Definition: Application.h:341
RoR::CacheEntry::usagecounter
int usagecounter
how much it was used already
Definition: CacheSystem.h:85
RoR::CacheValidity::NEEDS_UPDATE
@ NEEDS_UPDATE
RoR::CacheEntry::fext
Ogre::String fext
file's extension
Definition: CacheSystem.h:69
RoR::TuneupDef::author_name
std::string author_name
Definition: TuneupFileFormat.h:102
RoR::Actor::getRotation
float getRotation()
Definition: Actor.cpp:356
RoR::TuneupDef::protected_exhausts
std::set< ExhaustID_t > protected_exhausts
Exhausts which cannot be altered via 'addonpart_unwanted_exhaust' directive.
Definition: TuneupFileFormat.h:140
RoR::TuneupDef::protected_flexbodies
std::set< FlexbodyID_t > protected_flexbodies
Flexbodies which cannot be altered via 'addonpart_tweak_flexbody' or 'addonpart_unwanted_flexbody' di...
Definition: TuneupFileFormat.h:138
RoR::CacheQuery::cqy_filter_type
RoR::LoaderType cqy_filter_type
Definition: CacheSystem.h:169
SkinFileFormat.h
RoR::GetFileLastModifiedTime
std::time_t GetFileLastModifiedTime(std::string const &path)
Definition: PlatformUtils.cpp:238
RoR::CacheSystem::LoadResource
void LoadResource(CacheEntryPtr &t)
Loads the associated resource bundle if not already done.
Definition: CacheSystem.cpp:1539
RoR::GenericDocument
Definition: GenericFileFormat.h:66
RoR::Message
Unified game event system - all requests and state changes are reported using a message.
Definition: GameContext.h:51
RoR::CacheEntry::wheelcount
int wheelcount
Definition: CacheSystem.h:115
RoR::CacheSystem::WriteCacheFileJson
void WriteCacheFileJson()
Definition: CacheSystem.cpp:686
RoR::CVar::setVal
void setVal(T val)
Definition: CVar.h:72
RoR::CID_Max
@ CID_Max
SPECIAL VALUE - Maximum allowed to be present in any mod files.
Definition: Application.h:343
RoR::TuneupDef::filename
std::string filename
target vehicle filename
Definition: TuneupFileFormat.h:99
_L
#define _L
Definition: ErrorUtils.cpp:34
RoR::Actor::getUsedSkinEntry
CacheEntryPtr & getUsedSkinEntry()
Definition: Actor.cpp:4669
RoR::ModifyProjectRequestType::TUNEUP_FORCEREMOVE_FLEXBODY_RESET
@ TUNEUP_FORCEREMOVE_FLEXBODY_RESET
'subject_id' is flexbody ID.
RoR::CacheEntry::nodecount
int nodecount
Definition: CacheSystem.h:110
RigDef::Engine
Definition: RigDef_File.h:806
RoR::CreateProjectRequest::cpr_overwrite
bool cpr_overwrite
Definition: CacheSystem.h:214
RoR::CacheEntry::resource_bundle_path
std::string resource_bundle_path
Path of ZIP or directory which contains the media. Shared between CacheEntries, loaded only once.
Definition: CacheSystem.h:81
RoR::ActorSpawnRequest::asr_debugview
int asr_debugview
Definition: SimData.h:840
RoR::CacheEntry::flexbodiescount
int flexbodiescount
Definition: CacheSystem.h:125
RigDef_Parser.h
Checks the rig-def file syntax and loads data to memory.
RoR::ModifyProjectRequestType::TUNEUP_PROTECTED_PROP_RESET
@ TUNEUP_PROTECTED_PROP_RESET
'subject_id' is prop ID.
RoR::CacheEntry::wingscount
int wingscount
Definition: CacheSystem.h:120
RoR::App::sys_projects_dir
CVar * sys_projects_dir
Definition: Application.cpp:173
RoR::MSG_SIM_SPAWN_ACTOR_REQUESTED
@ MSG_SIM_SPAWN_ACTOR_REQUESTED
Payload = RoR::ActorSpawnRequest* (owner)
Definition: Application.h:121
RoR::ModifyProjectRequestType::TUNEUP_PROTECTED_MANAGEDMAT_RESET
@ TUNEUP_PROTECTED_MANAGEDMAT_RESET
'subject' is managed material name.
RoR::CacheSystem::ReLoadResource
void ReLoadResource(CacheEntryPtr &t)
Forces reloading the associated bundle.
Definition: CacheSystem.cpp:1635
RoR::CacheEntry::categoryname
Ogre::String categoryname
category name
Definition: CacheSystem.h:73
RoR::EraseIf
void EraseIf(std::vector< T, A > &c, Predicate pred)
Definition: Utils.h:74
RoR::CacheEntry::exhaustscount
int exhaustscount
Definition: CacheSystem.h:124
RoR::ModifyProjectRequestType::TUNEUP_PROTECTED_WHEEL_SET
@ TUNEUP_PROTECTED_WHEEL_SET
'subject_id' is wheel ID.
RoR::ActorPtr
RefCountingObjectPtr< Actor > ActorPtr
Definition: ForwardDeclarations.h:219
RoR::MSG_SIM_DELETE_ACTOR_REQUESTED
@ MSG_SIM_DELETE_ACTOR_REQUESTED
Payload = RoR::ActorPtr* (owner)
Definition: Application.h:123
RoR::LT_AllBeam
@ LT_AllBeam
Definition: Application.h:320
RoR::CacheEntry::truckmass
float truckmass
Definition: CacheSystem.h:128
RoR::TuneupDef::category_id
CacheCategoryId category_id
Definition: TuneupFileFormat.h:104
RoR::GenericDocContext::getTokKeyword
std::string getTokKeyword(int offset=0) const
Definition: GenericFileFormat.h:119
Terrain.h
RoR::CacheEntry::maxrpm
float maxrpm
Definition: CacheSystem.h:131
RoR::ModifyProjectRequestType::PROJECT_RESET_TUNEUP
@ PROJECT_RESET_TUNEUP
'subject' is empty. This resets the auto-generated tuneup to orig. values.
RoR::CacheSystem::CacheSystem
CacheSystem()
Definition: CacheSystem.cpp:125
RoR::CacheEntry::turbopropscount
int turbopropscount
Definition: CacheSystem.h:121
BitMask_t
uint32_t BitMask_t
Definition: BitFlags.h:7
RigDef::SequentialImporter::Disable
void Disable()
Definition: RigDef_SequentialImporter.h:120
RoR::GenericDocContext
Definition: GenericFileFormat.h:90
RoR::CacheQuery::cqy_filter_guid
std::string cqy_filter_guid
Exact match (case-insensitive); leave empty to disable.
Definition: CacheSystem.h:171
RoR::TuneupDef::force_remove_flares
std::set< FlareID_t > force_remove_flares
User unticked an UI checkbox in Tuning menu, section Flares.
Definition: TuneupFileFormat.h:128
Ogre
Definition: ExtinguishableFireAffector.cpp:35
GfxActor.h
Manager for all visuals belonging to a single actor.
RoR::CacheEntry::turbojetcount
int turbojetcount
Definition: CacheSystem.h:122
RoR::ActorSpawnRequest::Origin::USER
@ USER
Direct selection by user via GUI.
RoR::Console::CONSOLE_MSGTYPE_ACTOR
@ CONSOLE_MSGTYPE_ACTOR
Parsing/spawn/simulation messages for actors.
Definition: Console.h:63
RoR::App::sys_process_dir
CVar * sys_process_dir
Definition: Application.cpp:162
AddonPartFileFormat.h
RoR::GenericDocument::loadFromDataStream
virtual void loadFromDataStream(Ogre::DataStreamPtr datastream, BitMask_t options=0)
Definition: GenericFileFormat.cpp:1018
RoR::GfxActor::GetDebugView
DebugViewType GetDebugView() const
Definition: GfxActor.h:144
RigDef::DocumentPtr
std::shared_ptr< Document > DocumentPtr
Definition: ForwardDeclarations.h:272
RoR::Console::CONSOLE_SYSTEM_WARNING
@ CONSOLE_SYSTEM_WARNING
Definition: Console.h:53
RoR::TuneupDef::force_wheel_sides
std::map< WheelID_t, WheelSide > force_wheel_sides
UI overrides.
Definition: TuneupFileFormat.h:127
RoR::Terrn2Author::type
std::string type
Definition: Terrn2FileFormat.h:38
RoR::CacheEntry::hydroscount
int hydroscount
Definition: CacheSystem.h:114
RoR::Console::CONSOLE_MSGTYPE_INFO
@ CONSOLE_MSGTYPE_INFO
Generic message.
Definition: Console.h:60
RoR::WheelSide
WheelSide
Used by rig-def/addonpart/tuneup formats to specify wheel rim mesh orientation.
Definition: GfxData.h:115
RoR::ActorSpawnRequest::asr_position
Ogre::Vector3 asr_position
Definition: SimData.h:833
RoR::ModifyProjectRequest::mpr_type
ModifyProjectRequestType mpr_type
Definition: CacheSystem.h:253
RoR::TuneupDef::guid
std::string guid
target vehicle GUID
Definition: TuneupFileFormat.h:98
RoR::ActorType
ActorType
< Aka 'Driveable'
Definition: SimData.h:88
RoR::ModifyProjectRequestType::TUNEUP_FORCED_WHEEL_SIDE_RESET
@ TUNEUP_FORCED_WHEEL_SIDE_RESET
'subject_id' is wheel ID.
RoR::CacheEntry::filetime
std::time_t filetime
filetime
Definition: CacheSystem.h:83
RoR::BOAT
@ BOAT
its a boat
Definition: SimData.h:95
RoR::CacheSystem::FillAssetPackDetailInfo
void FillAssetPackDetailInfo(CacheEntryPtr &entry, Ogre::DataStreamPtr ds)
Definition: CacheSystem.cpp:1271
RoR::GUI::GameMainMenu::CacheUpdatedNotice
void CacheUpdatedNotice()
Definition: GUI_GameMainMenu.cpp:56
RoR::Actor::ar_state
ActorState ar_state
Definition: Actor.h:453
RoR::CacheEntry::custom_particles
bool custom_particles
Definition: CacheSystem.h:134
RoR::ContentManager::ListAllUserContent
std::string ListAllUserContent()
Used by ModCache for quick detection of added/removed content.
Definition: ContentManager.cpp:434
GenericFileFormat.h
Generic text file parser.
RoR::AI
@ AI
machine controlled by an Artificial Intelligence
Definition: SimData.h:97
RoR::GenericDocument::OPTION_ALLOW_NAKED_STRINGS
static const BitMask_t OPTION_ALLOW_NAKED_STRINGS
Allow strings without quotes, for backwards compatibility.
Definition: GenericFileFormat.h:68
RoR::CID_All
@ CID_All
Definition: Application.h:345
RoR::AuthorInfo::name
Ogre::String name
Definition: CacheSystem.h:51
RoR::CacheQuery::cqy_filter_target_filename
std::string cqy_filter_target_filename
Exact match (case-insensitive); leave empty to disable (currently only used with addonparts)
Definition: CacheSystem.h:172
RoR::CacheEntry::commandscount
int commandscount
Definition: CacheSystem.h:117
RoR::ActorSpawnRequest::asr_rotation
Ogre::Quaternion asr_rotation
Definition: SimData.h:834
RoR::Actor::getWorkingTuneupDef
TuneupDefPtr & getWorkingTuneupDef()
Definition: Actor.cpp:4674
RoR
Definition: AppContext.h:36
RoR::MODCACHEACTIVITY_ENTRY_MODIFIED
@ MODCACHEACTIVITY_ENTRY_MODIFIED
Args: #1 type, #2 entry number, –, –, #5 fname, #6 fext.
Definition: ScriptEvents.h:93
RoR::CacheSystem::Query
size_t Query(CacheQuery &query)
Definition: CacheSystem.cpp:2204
RoR::ContentManager::ResourcePack::MESHES
static const ResourcePack MESHES
Definition: ContentManager.h:60
RoR::TuneupDef::force_remove_exhausts
std::set< ExhaustID_t > force_remove_exhausts
User unticked an UI checkbox in Tuning menu, section Exhausts.
Definition: TuneupFileFormat.h:129
RoR::Log
void Log(const char *msg)
The ultimate, application-wide logging function. Adds a line (any length) in 'RoR....
Definition: Application.cpp:423
RoR::GUIManager::GameMainMenu
GUI::GameMainMenu GameMainMenu
Definition: GUIManager.h:115
RoR::CacheQuery::cqy_results
std::vector< CacheQueryResult > cqy_results
Definition: CacheSystem.h:176
RoR::LT_Train
@ LT_Train
Definition: Application.h:316
RoR::CacheEntry::default_skin
std::string default_skin
Definition: CacheSystem.h:107
RoR::CacheSystem::GenerateHashFromFilenames
void GenerateHashFromFilenames()
For quick detection of added/removed content.
Definition: CacheSystem.cpp:1186
RoR::FileExists
bool FileExists(const char *path)
Path must be UTF-8 encoded.
Definition: PlatformUtils.cpp:163
RoR::CacheEntry::guid
Ogre::String guid
global unique id; Type "addonpart" leaves this empty and uses addonpart_guids; Always lowercase.
Definition: CacheSystem.h:77
RoR::Terrn2Parser::LoadTerrn2
Terrn2DocumentPtr LoadTerrn2(Ogre::DataStreamPtr &ds)
Definition: Terrn2FileFormat.cpp:38
CACHE_FILE
#define CACHE_FILE
Definition: CacheSystem.h:41
RoR::GUI::TopMenubar::tuning_actor
ActorPtr tuning_actor
Detecting actor change to update cached values.
Definition: GUI_TopMenubar.h:111
RoR::CacheSystem::m_resource_paths
std::set< Ogre::String > m_resource_paths
A temporary list of existing resource paths.
Definition: CacheSystem.h:378
RoR::CacheEntry::flarescount
int flarescount
Definition: CacheSystem.h:118
RigDef::WheelPropulsion::NONE
@ NONE
RoR::CacheSystem::m_loaded
bool m_loaded
Definition: CacheSystem.h:371
RoR::CacheSystem::StripUIDfromString
static Ogre::String StripUIDfromString(Ogre::String uidstr)
Definition: CacheSystem.cpp:728
RigDef::Fileinfo::category_id
int category_id
Definition: RigDef_File.h:873
RoR::CacheEntry::fname
Ogre::String fname
filename
Definition: CacheSystem.h:67
RoR::GenericDocContext::getTokInt
int getTokInt(int offset=0) const
Definition: GenericFileFormat.h:116
RoR::CacheEntry::fname_without_uid
Ogre::String fname_without_uid
filename
Definition: CacheSystem.h:68