@@ -73,6 +73,11 @@ bool ActivityMan::ForceAbortSave() {
7373 return SaveCurrentGame (" AbortSave" );
7474}
7575
76+ // Not sure why this isn't in the minizip header, but we save some of the files without compression
77+ // (index, because it's so small, and pngs, because they're already compressed)
78+ #define MZ_COMPRESS_METHOD_STORE 0
79+ #define MZ_COMPRESS_LEVEL_FAST 2
80+
7681bool ActivityMan::SaveCurrentGame (const std::string& fileName) {
7782 m_SaveGameTask.wait ();
7883 m_SaveGameTask = BS ::multi_future<void >();
@@ -131,30 +136,45 @@ bool ActivityMan::SaveCurrentGame(const std::string& fileName) {
131136 writer->NewPropertyWithValue (" PlaceUnitsIfSceneIsRestarted" , g_SceneMan.GetPlaceUnitsOnLoad ());
132137 writer->NewPropertyWithValue (" Scene" , modifiableScene.get ());
133138
139+ // Save a small little file with index info (activity and original scene name) so we can display info in the samegame menu without needing to decompress and read through the entire zip
140+ std::unique_ptr<std::stringstream> indexStream = std::make_unique<std::stringstream>();
141+ Writer* indexWriter = new Writer (std::move (indexStream));
142+ indexWriter->NewPropertyWithValue (" ActivityName" , activity->GetPresetName ());
143+ indexWriter->NewPropertyWithValue (" OriginalScenePresetName" , scene->GetPresetName ());
144+
134145 // Get BITMAPS so save into our zip
135146 // I tried std::moving this into the function directly but threadpool really doesn't like that
136147 std::vector<SceneLayerInfo>* sceneLayerInfos = new std::vector<SceneLayerInfo>();
137148 *sceneLayerInfos = std::move (scene->GetCopiedSceneLayerBitmaps ());
138149
139- auto saveWriterData = [fileName, sceneLayerInfos](Writer* writerToSave) {
140- std::stringstream* stream = static_cast <std::stringstream*>(writerToSave->GetStream ());
141- stream->flush ();
142-
143- // Ugly copies, but eh. todo - use a string stream that just gives us a raw buffer to grab at
144- std::string streamAsString = stream->str ();
145-
146- zip_fileinfo zfi = {0 };
147-
150+ auto saveWriterData = [fileName, sceneLayerInfos, indexWriter](Writer* mainWriter) {
148151 // Create zip sav file
149152 zipFile zippedSaveFile = zipOpen ((g_PresetMan.GetFullModulePath (c_UserScriptedSavesModuleName) + " /" + fileName + " .ccsave" ).c_str (), APPEND_STATUS_CREATE );
150153 if (!zippedSaveFile) {
151154 g_ConsoleMan.PrintString (" ERROR: Couldn't create zip save file!" );
155+ delete mainWriter;
156+ delete indexWriter;
157+ delete sceneLayerInfos;
152158 return ;
153159 }
160+
161+ std::stringstream* mainStream = static_cast <std::stringstream*>(mainWriter->GetStream ());
162+ std::stringstream* indexStream = static_cast <std::stringstream*>(indexWriter->GetStream ());
163+ mainStream->flush ();
164+ indexStream->flush ();
165+
166+ // Ugly copies, but eh. todo - use a string stream that just gives us a raw buffer to grab at
167+ std::string mainStreamAsString = mainStream->str ();
168+ std::string indexStreamAsString = indexStream->str ();
169+
170+ zip_fileinfo zfi = {0 };
171+
172+ zipOpenNewFileInZip (zippedSaveFile, " Index.ini" , &zfi, nullptr , 0 , nullptr , 0 , nullptr , MZ_COMPRESS_METHOD_STORE , MZ_COMPRESS_LEVEL_FAST );
173+ zipWriteInFileInZip (zippedSaveFile, indexStreamAsString.data (), indexStreamAsString.size ());
174+ zipCloseFileInZip (zippedSaveFile);
154175
155- const int defaultCompression = 6 ;
156- zipOpenNewFileInZip (zippedSaveFile, " Save.ini" , &zfi, nullptr , 0 , nullptr , 0 , nullptr , Z_DEFLATED , defaultCompression);
157- zipWriteInFileInZip (zippedSaveFile, streamAsString.data (), streamAsString.size ());
176+ zipOpenNewFileInZip (zippedSaveFile, " Save.ini" , &zfi, nullptr , 0 , nullptr , 0 , nullptr , Z_DEFLATED , MZ_COMPRESS_LEVEL_FAST );
177+ zipWriteInFileInZip (zippedSaveFile, mainStreamAsString.data (), mainStreamAsString.size ());
158178 zipCloseFileInZip (zippedSaveFile);
159179
160180 PALETTE palette;
@@ -188,7 +208,7 @@ bool ActivityMan::SaveCurrentGame(const std::string& fileName) {
188208 continue ;
189209 }
190210
191- zipOpenNewFileInZip (zippedSaveFile, (" Save " + layerInfo.name + " .png" ).c_str (), &zfi, nullptr , 0 , nullptr , 0 , nullptr , Z_DEFLATED , defaultCompression );
211+ zipOpenNewFileInZip (zippedSaveFile, (" Save " + layerInfo.name + " .png" ).c_str (), &zfi, nullptr , 0 , nullptr , 0 , nullptr , MZ_COMPRESS_METHOD_STORE , MZ_COMPRESS_LEVEL_FAST );
192212 zipWriteInFileInZip (zippedSaveFile, static_cast <const char *>(buffer), size);
193213 zipCloseFileInZip (zippedSaveFile);
194214
@@ -197,7 +217,8 @@ bool ActivityMan::SaveCurrentGame(const std::string& fileName) {
197217
198218 zipClose (zippedSaveFile, fileName.c_str ());
199219
200- delete writerToSave;
220+ delete mainWriter;
221+ delete indexWriter;
201222 delete sceneLayerInfos;
202223 };
203224
@@ -235,7 +256,7 @@ bool ActivityMan::LoadAndLaunchGame(const std::string& fileName) {
235256 unzOpenCurrentFile (zippedSaveFile);
236257 unzGetCurrentFileInfo (zippedSaveFile, &info, nullptr , 0 , nullptr , 0 , nullptr , 0 );
237258
238- buffer = (char *)malloc (info.uncompressed_size );
259+ buffer = (char *)malloc (info.uncompressed_size + 1 ); // add one so we can add a pretend null terminator on the end
239260 if (!buffer) {
240261 // If this ever hits I've lost all faith in modern OSes, but alas when one is writing C, one must dance along
241262 RTEError::ShowMessageBox (" Catastrophic failure! Failed to allocate memory for savegame" );
@@ -282,7 +303,13 @@ bool ActivityMan::LoadAndLaunchGame(const std::string& fileName) {
282303 }
283304 }
284305
285- unzipFileIntoBuffer (" Save.ini" );
306+ if (!unzipFileIntoBuffer (" Save.ini" ))
307+ {
308+ RTEError::ShowMessageBox (" Game loading failed! This save looks invalid or corrupted." );
309+ return false ;
310+ }
311+
312+ buffer[info.uncompressed_size ] = 0 ; // null terminate
286313
287314 Reader reader (std::make_unique<std::istringstream>(buffer), filePath + " /Save.ini" , true , nullptr , false );
288315
0 commit comments