@@ -73,6 +73,11 @@ bool ActivityMan::ForceAbortSave() {
73
73
return SaveCurrentGame (" AbortSave" );
74
74
}
75
75
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
+
76
81
bool ActivityMan::SaveCurrentGame (const std::string& fileName) {
77
82
m_SaveGameTask.wait ();
78
83
m_SaveGameTask = BS::multi_future<void >();
@@ -131,30 +136,45 @@ bool ActivityMan::SaveCurrentGame(const std::string& fileName) {
131
136
writer->NewPropertyWithValue (" PlaceUnitsIfSceneIsRestarted" , g_SceneMan.GetPlaceUnitsOnLoad ());
132
137
writer->NewPropertyWithValue (" Scene" , modifiableScene.get ());
133
138
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
+
134
145
// Get BITMAPS so save into our zip
135
146
// I tried std::moving this into the function directly but threadpool really doesn't like that
136
147
std::vector<SceneLayerInfo>* sceneLayerInfos = new std::vector<SceneLayerInfo>();
137
148
*sceneLayerInfos = std::move (scene->GetCopiedSceneLayerBitmaps ());
138
149
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) {
148
151
// Create zip sav file
149
152
zipFile zippedSaveFile = zipOpen ((g_PresetMan.GetFullModulePath (c_UserScriptedSavesModuleName) + " /" + fileName + " .ccsave" ).c_str (), APPEND_STATUS_CREATE);
150
153
if (!zippedSaveFile) {
151
154
g_ConsoleMan.PrintString (" ERROR: Couldn't create zip save file!" );
155
+ delete mainWriter;
156
+ delete indexWriter;
157
+ delete sceneLayerInfos;
152
158
return ;
153
159
}
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);
154
175
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 ());
158
178
zipCloseFileInZip (zippedSaveFile);
159
179
160
180
PALETTE palette;
@@ -188,7 +208,7 @@ bool ActivityMan::SaveCurrentGame(const std::string& fileName) {
188
208
continue ;
189
209
}
190
210
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 );
192
212
zipWriteInFileInZip (zippedSaveFile, static_cast <const char *>(buffer), size);
193
213
zipCloseFileInZip (zippedSaveFile);
194
214
@@ -197,7 +217,8 @@ bool ActivityMan::SaveCurrentGame(const std::string& fileName) {
197
217
198
218
zipClose (zippedSaveFile, fileName.c_str ());
199
219
200
- delete writerToSave;
220
+ delete mainWriter;
221
+ delete indexWriter;
201
222
delete sceneLayerInfos;
202
223
};
203
224
@@ -235,7 +256,7 @@ bool ActivityMan::LoadAndLaunchGame(const std::string& fileName) {
235
256
unzOpenCurrentFile (zippedSaveFile);
236
257
unzGetCurrentFileInfo (zippedSaveFile, &info, nullptr , 0 , nullptr , 0 , nullptr , 0 );
237
258
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
239
260
if (!buffer) {
240
261
// If this ever hits I've lost all faith in modern OSes, but alas when one is writing C, one must dance along
241
262
RTEError::ShowMessageBox (" Catastrophic failure! Failed to allocate memory for savegame" );
@@ -282,7 +303,13 @@ bool ActivityMan::LoadAndLaunchGame(const std::string& fileName) {
282
303
}
283
304
}
284
305
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
286
313
287
314
Reader reader (std::make_unique<std::istringstream>(buffer), filePath + " /Save.ini" , true , nullptr , false );
288
315
0 commit comments