~ubuntu-branches/ubuntu/natty/spring/natty

« back to all changes in this revision

Viewing changes to rts/System/LoadSave/LuaLoadSaveHandler.cpp

  • Committer: Bazaar Package Importer
  • Author(s): Scott Ritchie
  • Date: 2010-09-23 18:56:03 UTC
  • mfrom: (3.1.9 experimental)
  • Revision ID: james.westby@ubuntu.com-20100923185603-st97s5chplo42y7w
Tags: 0.82.5.1+dfsg1-1ubuntu1
* Latest upstream version for online play
* debian/control: Replace (rather than conflict) spring-engine
  - spring-engine will be a dummy package (LP: #612905)
  - also set maintainer to MOTU

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/* This file is part of the Spring engine (GPL v2 or later), see LICENSE.html */
 
2
 
 
3
#include <string>
 
4
#include <sstream>
 
5
 
 
6
#include "StdAfx.h"
 
7
#include "LuaLoadSaveHandler.h"
 
8
 
 
9
#include "lib/minizip/zip.h"
 
10
 
 
11
#include "ExternalAI/EngineOutHandler.h"
 
12
#include "Game/GameSetup.h"
 
13
#include "Lua/LuaZip.h"
 
14
#include "Map/MapDamage.h"
 
15
#include "Map/ReadMap.h"
 
16
#include "System/FileSystem/FileSystem.h"
 
17
#include "System/FileSystem/ArchiveZip.h"
 
18
#include "System/Platform/byteorder.h"
 
19
#include "System/EventHandler.h"
 
20
#include "System/Exceptions.h"
 
21
#include "System/LogOutput.h"
 
22
 
 
23
#include "mmgr.h"
 
24
 
 
25
 
 
26
// Prefix for all files in the save file.
 
27
// May be used to prevent clashes with other code. (AI, Lua)
 
28
#define PREFIX "Spring/"
 
29
 
 
30
// Names of files in save file for various components of engine.
 
31
// They all have a version number as suffix. When breaking compatibility
 
32
// in the respective file format, please increment its version number.
 
33
static const char* FILE_STARTSCRIPT  = PREFIX"startscript.0";
 
34
static const char* FILE_AIDATA       = PREFIX"aidata.0";
 
35
static const char* FILE_HEIGHTMAP    = PREFIX"heightmap.0";
 
36
 
 
37
#undef PREFIX
 
38
 
 
39
 
 
40
CLuaLoadSaveHandler::CLuaLoadSaveHandler()
 
41
: savefile(NULL), loadfile(NULL)
 
42
{
 
43
}
 
44
 
 
45
 
 
46
CLuaLoadSaveHandler::~CLuaLoadSaveHandler()
 
47
{
 
48
        delete loadfile;
 
49
}
 
50
 
 
51
 
 
52
void CLuaLoadSaveHandler::SaveGame(const std::string& file)
 
53
{
 
54
        const std::string realname = filesystem.LocateFile(file, FileSystem::WRITE).c_str();
 
55
 
 
56
        filename = file;
 
57
        savefile = NULL;
 
58
 
 
59
        try {
 
60
                // Remove any existing file
 
61
                filesystem.Remove(realname);
 
62
 
 
63
                // Open the zip
 
64
                if (realname.empty() ||
 
65
                                (savefile = zipOpen(realname.c_str(), APPEND_STATUS_CREATE)) == NULL) {
 
66
                        throw content_error("Unable to open save file \"" + filename + "\"");
 
67
                }
 
68
 
 
69
                SaveEventClients();
 
70
                SaveGameStartInfo();
 
71
                SaveAIData();
 
72
                SaveHeightmap();
 
73
 
 
74
                // Close zip file.
 
75
                if (Z_OK != zipClose(savefile, "Spring save file, visit http://springrts.com/ for details.")) {
 
76
                        logOutput.Print("Unable to close save file \"" + filename + "\"");
 
77
                }
 
78
                return; // Success
 
79
        }
 
80
        catch (content_error &e) {
 
81
                logOutput.Print("Save failed(content error): %s", e.what());
 
82
        }
 
83
        catch (std::exception &e) {
 
84
                logOutput.Print("Save failed: %s", e.what());
 
85
        }
 
86
        catch (char* &e) {
 
87
                logOutput.Print("Save failed: %s", e);
 
88
        }
 
89
        catch (...) {
 
90
                logOutput.Print("Save failed(unknown error)");
 
91
        }
 
92
 
 
93
        // Failure => cleanup
 
94
        if (savefile != NULL) {
 
95
                zipClose(savefile, NULL);
 
96
                savefile = NULL;
 
97
                filesystem.Remove(realname);
 
98
        }
 
99
}
 
100
 
 
101
 
 
102
void CLuaLoadSaveHandler::SaveEventClients()
 
103
{
 
104
        // FIXME: need some way to 'chroot' them into a single directory?
 
105
        //        (maybe abstract zipFile void* after all...)
 
106
        eventHandler.Save(savefile);
 
107
}
 
108
 
 
109
 
 
110
void CLuaLoadSaveHandler::SaveGameStartInfo()
 
111
{
 
112
        const std::string scriptText = gameSetup->gameSetupText;
 
113
        SaveEntireFile(FILE_STARTSCRIPT, "game setup", scriptText.data(), scriptText.size());
 
114
}
 
115
 
 
116
 
 
117
void CLuaLoadSaveHandler::SaveAIData()
 
118
{
 
119
        // Save to a stringstream first, to be able to use current interface.
 
120
        // FIXME: maybe expose richer stream to AI interface?
 
121
        //        (e.g. one file in the zip per AI?)
 
122
        std::stringstream aidata;
 
123
        eoh->Save(&aidata);
 
124
        SaveEntireFile(FILE_AIDATA, "AI data", aidata.str().data(), aidata.tellp());
 
125
}
 
126
 
 
127
 
 
128
void CLuaLoadSaveHandler::SaveHeightmap()
 
129
{
 
130
        // This implements a trivial compression algorithm (relying on zip):
 
131
        // For every heightmap pixel the bits are XOR'ed with the orig bits,
 
132
        // so that unmodified terrain comes out as 0.
 
133
        // Big chunks of 0s are then very well compressed by zip.
 
134
        const int* currHeightmap = (const int*) (const char*) readmap->GetHeightmap();
 
135
        const int* origHeightmap = (const int*) (const char*) readmap->orgheightmap;
 
136
        const int size = (gs->mapx + 1) * (gs->mapy + 1);
 
137
        int* temp = new int[size];
 
138
        for (int i = 0; i < size; ++i) {
 
139
                temp[i] = swabdword(currHeightmap[i] ^ origHeightmap[i]);
 
140
        }
 
141
        SaveEntireFile(FILE_HEIGHTMAP, "heightmap", temp, size * sizeof(int));
 
142
        delete[] temp;
 
143
}
 
144
 
 
145
 
 
146
void CLuaLoadSaveHandler::SaveEntireFile(const char* file, const char* what, const void* data, int size, bool throwOnError)
 
147
{
 
148
        std::string err;
 
149
 
 
150
        if (Z_OK != zipOpenNewFileInZip(savefile, file, NULL, NULL, 0, NULL, 0, NULL, Z_DEFLATED, Z_BEST_COMPRESSION)) {
 
151
                err = "open";
 
152
        }
 
153
        else if (Z_OK != zipWriteInFileInZip(savefile, data, size)) {
 
154
                err = "write";
 
155
        }
 
156
        else if (Z_OK != zipCloseFileInZip(savefile)) {
 
157
                err = "close";
 
158
        }
 
159
 
 
160
        if (!err.empty()) {
 
161
                err = "Unable to " + err + " " + what + " file in save file \"" + filename + "\"";
 
162
                if (throwOnError) {
 
163
                        throw content_error(err);
 
164
                }
 
165
                else {
 
166
                        logOutput.Print(err);
 
167
                }
 
168
        }
 
169
}
 
170
 
 
171
 
 
172
void CLuaLoadSaveHandler::LoadGameStartInfo(const std::string& file)
 
173
{
 
174
        const std::string realfile = filesystem.LocateFile(FindSaveFile(file)).c_str();
 
175
 
 
176
        filename = file;
 
177
        loadfile = new CArchiveZip(realfile);
 
178
 
 
179
        if (!loadfile->IsOpen()) {
 
180
                logOutput.Print("Unable to open save file \"" + filename + "\"");
 
181
                return;
 
182
        }
 
183
 
 
184
        scriptText = LoadEntireFile(FILE_STARTSCRIPT);
 
185
}
 
186
 
 
187
 
 
188
void CLuaLoadSaveHandler::LoadGame()
 
189
{
 
190
        LoadEventClients();
 
191
        LoadAIData();
 
192
        LoadHeightmap();
 
193
}
 
194
 
 
195
 
 
196
void CLuaLoadSaveHandler::LoadEventClients()
 
197
{
 
198
        // FIXME: need some way to 'chroot' them into a single directory?
 
199
        eventHandler.Load(loadfile);
 
200
}
 
201
 
 
202
 
 
203
void CLuaLoadSaveHandler::LoadAIData()
 
204
{
 
205
        std::stringstream aidata(LoadEntireFile(FILE_AIDATA));
 
206
        eoh->Load(&aidata);
 
207
}
 
208
 
 
209
 
 
210
void CLuaLoadSaveHandler::LoadHeightmap()
 
211
{
 
212
        std::vector<boost::uint8_t> buf;
 
213
 
 
214
        if (loadfile->GetFile(FILE_HEIGHTMAP, buf)) {
 
215
                const int size = (gs->mapx + 1) * (gs->mapy + 1);
 
216
                const int* temp = (const int*) (const char*) &*buf.begin();
 
217
                const int* origHeightmap = (const int*) (const char*) readmap->orgheightmap;
 
218
 
 
219
                for (int i = 0; i < size; ++i) {
 
220
                        const int newHeightBits = swabdword(temp[i]) ^ origHeightmap[i];
 
221
                        const float newHeight = *(const float*) (const char*) &newHeightBits;
 
222
                        readmap->SetHeight(i, newHeight);
 
223
                }
 
224
                mapDamage->RecalcArea(0, gs->mapx, 0, gs->mapy);
 
225
        }
 
226
        else {
 
227
                logOutput.Print("Unable to load heightmap from save file \"" + filename + "\"");
 
228
        }
 
229
}
 
230
 
 
231
 
 
232
std::string CLuaLoadSaveHandler::LoadEntireFile(const std::string& file)
 
233
{
 
234
        std::vector<boost::uint8_t> buf;
 
235
        if (loadfile->GetFile(file, buf)) {
 
236
                return std::string((char*) &*buf.begin(), buf.size());
 
237
        }
 
238
        return "";
 
239
}