@@ -17,6 +17,107 @@ std::string escENC(std::string str) {
1717 return matjson::Value (str).dump ().c_str ();
1818}
1919
20+ namespace FileCache {
21+ struct FileResult {
22+ std::string content;
23+ std::string error;
24+ std::filesystem::file_time_type time;
25+ bool ok = false ;
26+ };
27+
28+ struct IniResult {
29+ CSimpleIni ini;
30+ SI_Error err = SI_OK;
31+ std::string errorStr;
32+ std::filesystem::file_time_type time;
33+ bool ok = false ;
34+ };
35+
36+ inline std::unordered_map<std::string, FileResult> fileCache;
37+ inline std::unordered_map<std::string, IniResult> iniCache;
38+ inline std::recursive_mutex mtx;
39+
40+ inline std::filesystem::file_time_type getWriteTime (const std::string& path) {
41+ std::error_code ec;
42+ return std::filesystem::last_write_time (path, ec);
43+ }
44+
45+ inline FileResult& getText (const std::string& path) {
46+ std::lock_guard lock (mtx);
47+
48+ auto now = getWriteTime (path);
49+ auto it = fileCache.find (path);
50+
51+ if (it != fileCache.end () && it->second .time == now) {
52+ return it->second ;
53+ }
54+
55+ auto & res = fileCache[path];
56+
57+ res.time = now;
58+ res.ok = false ;
59+ res.error .clear ();
60+ res.content .clear ();
61+
62+ auto read = file::readString (path.c_str ());
63+ if (read.err ()) {
64+ res.error = read.err ().value ();
65+ }
66+ else {
67+ res.ok = true ;
68+ res.content = read.unwrap ();
69+ }
70+
71+ return res;
72+ }
73+
74+ inline IniResult& getIni (const std::string& path) {
75+ std::lock_guard lock (mtx);
76+
77+ auto now = getWriteTime (path);
78+ auto it = iniCache.find (path);
79+
80+ if (it != iniCache.end () && it->second .time == now) {
81+ return it->second ;
82+ }
83+
84+ auto & res = iniCache[path];
85+
86+ res.time = now;
87+ res.ok = false ;
88+ res.errorStr .clear ();
89+ res.ini .Reset ();
90+
91+ auto & file = getText (path);
92+
93+ if (!file.ok ) {
94+ res.ok = false ;
95+ res.errorStr = file.error ;
96+ }
97+ else {
98+ res.err = res.ini .LoadData (file.content );
99+
100+ if (res.err < 0 ) {
101+ if (res.err == SI_FAIL) res.errorStr = " Generic failure" ;
102+ else if (res.err == SI_NOMEM) res.errorStr = " Out of memory" ;
103+ else if (res.err == SI_FILE) res.errorStr = std::string (" File error\n " ) + strerror (errno);
104+ else res.errorStr = " Unknown error" ;
105+ }
106+ else {
107+ res.ok = true ;
108+ }
109+ }
110+
111+ return res;
112+ }
113+
114+ inline void invalidate (const std::string& path) {
115+ std::lock_guard lock (mtx);
116+ fileCache.erase (path);
117+ iniCache.erase (path);
118+ }
119+ }
120+
20121$execute{
21122 for (auto path : {
22123 string::pathToString (Mod::get ()->getConfigDir ()),
@@ -43,23 +144,22 @@ class $modify(MLE_LocalLevelManager, LocalLevelManager) {
43144 }
44145
45146 auto path = CCFileUtils::get ()->fullPathForFilename (filename.c_str (), !" why" );
46- auto read = file::readString (path.c_str ());
47- if (auto err = read.err ()) {
48- // yea
49- log::error (" {}.readString: {}" , __FUNCTION__, err);
147+ auto & res = FileCache::getText (path);
148+ if (!res.ok ) {
149+ log::error (" {}.readString: {}" , __FUNCTION__, res.error );
50150 // whyyy the fuuuuck i done that
51151 std::string errlevel = R"( kS38,1_110_2_110_3_112_6_1000_7_1_15_0_18_0_8_1|1_0_2_0_3_0_6_1001_7_1_15_0_18_0_8_1|1_0_2_102_3_255_11_255_12_255_13_255_4_-1_6_1009_7_1_15_1_18_0_8_1|1_255_2_255_3_255_6_1002_5_1_7_1_15_0_18_0_8_1|1_40_2_125_3_255_11_255_12_255_13_255_4_-1_6_1013_7_1_15_1_18_0_8_1|1_40_2_125_3_255_11_255_12_255_13_255_4_-1_6_1014_7_1_15_1_18_0_8_1|1_255_2_255_3_255_6_1004_7_1_15_0_18_0_8_1|1_255_2_255_3_255_6_1003_7_1_15_0_18_0_8_1|1_125_2_255_3_0_11_255_12_255_13_255_4_-1_6_1005_5_1_7_1_15_1_18_0_8_1|1_0_2_255_3_255_11_255_12_255_13_255_4_-1_6_1006_5_1_7_1_15_1_18_0_8_1|,kA13,0,kA15,0,kA16,0,kA14,,kA6,0,kA7,0,kA25,0,kA17,0,kA18,0,kS39,0,kA2,1,kA3,0,kA8,0,kA4,0,kA9,0,kA10,0,kA22,1,kA23,0,kA24,0,kA27,0,kA40,0,kA48,0,kA41,0,kA42,0,kA28,0,kA29,0,kA31,0,kA32,0,kA36,0,kA43,0,kA44,0,kA45,0,kA46,0,kA47,0,kA33,0,kA34,0,kA35,0,kA37,0,kA38,0,kA39,0,kA19,0,kA26,0,kA20,0,kA21,0,kA11,0;
521521,2925,2,-45,3,135,155,1,36,1,111,1,112,1,113,20,114,1;
531531,1934,2,-45,3,105,155,1,13,1,36,1,406,1,421,1,422,0.5,10,0.5;
541541,914,2,495,3,135,155,2,128,0.5,129,0.5,31,YXNk;
551551,2899,2,-45,3,165,155,1,36,1,532,1;)" ;
56156 errlevel = string::replace (errlevel, " YXNk" , ZipUtils::base64URLEncode (
57- " Failed to load " + filename + " !\n " + err. value_or ( " unk err " )
157+ " Failed to load " + filename + " !\n " + res. error
58158 ).c_str ());
59159 return errlevel;
60160 }
61161
62- return read. unwrapOr ( LocalLevelManager::getMainLevelString (id)) ;
162+ return res. content ;
63163 };
64164};
65165
@@ -93,8 +193,8 @@ class $modify(MLE_LevelTools, LevelTools) {
93193
94194 auto path = CCFileUtils::get ()->fullPathForFilename (filename.c_str (), !" why" );
95195
96- CSimpleIni Ini ;
97- auto sierr = Ini. LoadFile (path. c_str ()) ;
196+ auto & iniRes = FileCache::getIni (path) ;
197+ auto & Ini = iniRes. ini ;
98198
99199 // m_levelID
100200 if (!(Ini.KeyExists (" GJGameLevel" , " m_levelID" ))) Ini.SetLongValue (
@@ -165,16 +265,13 @@ class $modify(MLE_LevelTools, LevelTools) {
165265 );
166266 else level->m_capacityString = escDEC (Ini.GetValue (" GJGameLevel" , " m_capacityString" )).c_str ();
167267
168- if (sierr < 0 ) {
169- auto err = std::string ();
170- if (sierr == SI_FAIL) err = " Generic failure" ;
171- if (sierr == SI_NOMEM) err = " Out of memory" ;
172- if (sierr == SI_FILE) err = " File error\n " + std::string (strerror (errno));
173- level->m_levelName = " [INI ERROR]: " + err;
174- level->m_difficulty = GJDifficulty::Harder;
268+ if (!iniRes.ok ) {
269+ level->m_levelName = " [INI ERROR]: " + iniRes.errorStr ;
270+ level->m_difficulty = GJDifficulty::Harder;
175271 }
176272
177273 Ini.SaveFile (path.c_str ());
274+ FileCache::invalidate (path);
178275
179276 return level;
180277 };
@@ -280,14 +377,14 @@ class $modify(BoomScrollLayerLevelSelectExt, BoomScrollLayer) {
280377 ).err ()) log::error (" {}.writeString: {}" , __FUNCTION__, err);
281378 }
282379 auto path = CCFileUtils::get ()->fullPathForFilename (file, !" why" );
283- auto read = file::readString (path. c_str () );
380+ auto read = FileCache::getText (path);
284381
285- if (read.err () ) {
286- log::error (" {}.readString: {}" , __FUNCTION__, read.err (). value () );
382+ if (! read.ok ) {
383+ log::error (" {}.readString: {}" , __FUNCTION__, read.error );
287384 if (layer) queueInMainThread ( // hi Node IDs
288385 [file, read, layer = Ref (layer)] {
289386 auto label = CCLabelBMFont::create (
290- fmt::format (" Error reading {}: \n {}" , file, read.err (). value () ).c_str (),
387+ fmt::format (" Error reading {}! \n {}" , file, read.error ).c_str (),
291388 " bigFont.fnt"
292389 );
293390 label->setID (" err-label" _spr);
@@ -302,7 +399,7 @@ class $modify(BoomScrollLayerLevelSelectExt, BoomScrollLayer) {
302399
303400 unk3->removeAllObjects ();
304401
305- for (auto id : parseListingIDs (read.unwrapOr ( " " ) )) unk3->addObject (
402+ for (auto id : parseListingIDs (read.content )) unk3->addObject (
306403 GameLevelManager::get ()->getMainLevel (id, 0 )
307404 );
308405
0 commit comments