Skip to content

Commit 177bd6b

Browse files
committed
* Fixed showinjg scene/activity name in menu
* Use faster compression level. Real issue is PNG compression taking a little while- maybe can be threaded? * Fixed readers not being null-terminated, sometimes reading garbage
1 parent 6c9354e commit 177bd6b

File tree

3 files changed

+84
-33
lines changed

3 files changed

+84
-33
lines changed

Source/Managers/ActivityMan.cpp

Lines changed: 43 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -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+
7681
bool 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

Source/Managers/ActivityMan.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,9 @@ namespace RTE {
129129
/// @param fileName Path to the file.
130130
/// @return Whether or not the saved game was successfully loaded.
131131
bool LoadAndLaunchGame(const std::string& fileName);
132+
133+
/// Waits for the task that saves the game to complete.
134+
void WaitForSaveGameTask() const { m_SaveGameTask.wait(); }
132135
#pragma endregion
133136

134137
#pragma region Activity Start Handling

Source/Menus/SaveLoadMenuGUI.cpp

Lines changed: 38 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,9 @@
2121

2222
#include <execution>
2323

24+
#include "zip.h"
25+
#include "unzip.h"
26+
2427
using namespace RTE;
2528

2629
SaveLoadMenuGUI::SaveLoadMenuGUI(AllegroScreen* guiScreen, GUIInputWrapper* guiInput, bool createForPauseMenu) {
@@ -75,14 +78,16 @@ SaveLoadMenuGUI::SaveLoadMenuGUI(AllegroScreen* guiScreen, GUIInputWrapper* guiI
7578
}
7679

7780
void SaveLoadMenuGUI::PopulateSaveGamesList() {
81+
g_ActivityMan.WaitForSaveGameTask();
82+
7883
m_SaveGames.clear();
7984
m_SaveGameName->SetText("");
8085

8186
m_GUIControlManager->GetManager()->SetFocus(nullptr);
8287

8388
std::string saveFilePath = g_PresetMan.GetFullModulePath(c_UserScriptedSavesModuleName) + "/";
8489
for (const auto& entry: std::filesystem::directory_iterator(saveFilePath)) {
85-
if (entry.path().extension() == ".ccsave" && entry.path().filename() != "Index.ini") {
90+
if (entry.path().extension() == ".ccsave") {
8691
SaveRecord record;
8792
record.SavePath = entry.path();
8893
record.SaveDate = entry.last_write_time();
@@ -93,31 +98,47 @@ void SaveLoadMenuGUI::PopulateSaveGamesList() {
9398
std::for_each(std::execution::par_unseq,
9499
m_SaveGames.begin(), m_SaveGames.end(),
95100
[](SaveRecord& record) {
96-
Reader reader(record.SavePath.string(), true, nullptr, true);
101+
// load zip sav file
102+
std::string filePath = record.SavePath.string();
103+
unzFile zippedSaveFile = unzOpen(filePath.c_str());
104+
if (!zippedSaveFile) {
105+
return;
106+
}
107+
108+
if (unzLocateFile(zippedSaveFile, "Index.ini", nullptr) == UNZ_END_OF_LIST_OF_FILE) {
109+
unzClose(zippedSaveFile);
110+
return;
111+
}
97112

98-
bool readActivity = false;
99-
bool readSceneName = false;
113+
unz_file_info info;
114+
unzOpenCurrentFile(zippedSaveFile);
115+
unzGetCurrentFileInfo(zippedSaveFile, &info, nullptr, 0, nullptr, 0, nullptr, 0);
100116

101-
GAScripted activity;
117+
char* buffer = (char*)malloc(info.uncompressed_size + 1);
118+
if (!buffer) {
119+
// If this ever hits I've lost all faith in modern OSes, but alas when one is writing C, one must dance along
120+
RTEError::ShowMessageBox("Catastrophic failure! Failed to allocate memory for savegame");
121+
unzClose(zippedSaveFile);
122+
return;
123+
}
124+
125+
unzReadCurrentFile(zippedSaveFile, buffer, info.uncompressed_size);
126+
unzCloseCurrentFile(zippedSaveFile);
127+
128+
buffer[info.uncompressed_size] = 0; // need to null-terminate manually
102129

103-
std::string originalScenePresetName;
130+
Reader reader(std::make_unique<std::istringstream>(buffer), record.SavePath.string(), true, nullptr, false);
104131
while (reader.NextProperty()) {
105132
std::string propName = reader.ReadPropName();
106-
if (propName == "Activity") {
107-
reader >> activity;
108-
readActivity = true;
133+
if (propName == "ActivityName") {
134+
reader >> record.Activity;
109135
} else if (propName == "OriginalScenePresetName") {
110-
reader >> originalScenePresetName;
111-
readSceneName = true;
112-
}
113-
114-
if (readActivity && readSceneName) {
115-
break;
136+
reader >> record.Scene;
116137
}
117138
}
118139

119-
record.Activity = activity.GetPresetName();
120-
record.Scene = originalScenePresetName;
140+
unzClose(zippedSaveFile);
141+
free(buffer);
121142
});
122143

123144
UpdateSaveGamesGUIList();

0 commit comments

Comments
 (0)