Skip to content

Instantly share code, notes, and snippets.

@ArtemGr
Last active August 29, 2015 14:01
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save ArtemGr/a15e11332dc6e05ce31d to your computer and use it in GitHub Desktop.
Save ArtemGr/a15e11332dc6e05ce31d to your computer and use it in GitHub Desktop.
Eador: Masters of the Broken World - savegame unpacker
// Cygwin: cd /tmp; g++ -O3 -fno-inline-functions -Wall -std=c++11 eador-unpack.cc -o eador-unpack -lz
// MinGW: g++ -static -O3 -fno-inline-functions -Wall -std=c++11 eador-unpack.cc -o eador-unpack -lz && upx eador-unpack.exe
// "They are just using ionic zip to compress part of the contents"
// http://www.cheathappens.com/show_board2.asp?headID=117333&titleID=19617&onPage=11
#include <stdlib.h>
#include <stdio.h>
#include <sys/stat.h>
#include <unistd.h>
#include <ctype.h>
#include <errno.h>
#include <string.h>
#ifdef __MINGW32__
# include "windows.h"
#else
# include <sys/mman.h>
#endif
#include <stdexcept>
using std::runtime_error;
#include <fstream>
#include <iostream>
using std::cout; using std::cerr; using std::endl;
#include <string>
using std::string;
#include <sstream>
#include <memory>
#include <zlib.h>
inline string operator"" _s (const char* str, size_t size) {return string (str, size);}
/** Read file into string. */
inline string slurp (const string& path) {
std::ostringstream buf; std::ifstream input (path.c_str(), std::ios::binary | std::ios::in); buf << input.rdbuf();
if (!input.good()) throw runtime_error ("Error reading from " + path + ": " + strerror (errno));
return buf.str();
}
int main (int argc, char** argv) try {
if (argc < 2) {cout << "Usage: eador-unpack $pathToSaveGame" << endl; return 0;}
const char *pathToSaveGame = argv[1];
string save (slurp (pathToSaveGame));
cout << "Got " << save.size() << " bytes from '" << pathToSaveGame << "'. Looking for the compressed piece..." << endl;
const char* endMagic = "section.end";
string::size_type endPos = save.find (endMagic); if (endPos == string::npos) throw runtime_error ("'"_s + endMagic + "' not found.");
string::size_type pos = endPos + strlen (endMagic);
for (string::size_type end = save.size(); pos < end; ++pos) {
char ch = save[pos]; if (ch != '\n' && ch != '\r') break;
}
if (save.size() - pos < 1024) throw runtime_error ("Compressed piece is too small...");
cout << "Found compressed piece of size " << (save.size() - pos) << ". Unpacking..." << endl;
#ifdef __MINGW32__
uLongf bufSize = 300 * 1024 * 1024; // 300 mebibyte.
tryAgain:
// http://msdn.microsoft.com/en-us/library/aa366887%28VS.85%29.aspx
char* buf = (char*) VirtualAlloc (nullptr, bufSize, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
if (buf == nullptr && bufSize > 50 * 1024 * 1024) {bufSize -= 10 * 1024 * 1024; goto tryAgain;}
if (buf == nullptr) throw runtime_error ("!CreateFileMapping");
std::shared_ptr<char> unmap (buf, [bufSize](char* buf) {VirtualFree (buf, bufSize, MEM_RELEASE);});
#else
uLongf bufSize = 1024 * 1024 * 1024; // 1 gibibyte.
char* buf = (char*) mmap (nullptr, bufSize, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
if (!buf) throw runtime_error ("!mmap");
std::shared_ptr<char> unmap (buf, [bufSize](char* buf) {munmap (buf, bufSize);});
#endif
cout << "Memory allocated (" << (bufSize / 1024 / 1024) << " MiB)." << endl;
if (uncompress ((unsigned char*) buf, &bufSize, (unsigned char*) save.data() + pos, save.size() - pos) != Z_OK) throw runtime_error ("!uncompress");
cout << "Unpacked " << bufSize << " bytes." << endl;
std::ofstream of (pathToSaveGame, std::ios::binary); of.write (buf, bufSize);
if (!of.good()) throw runtime_error ("Error writing to '"_s + pathToSaveGame + ": " + strerror (errno));
cout << "Saved to '" << pathToSaveGame << "' NP." << endl;
return 0;
} catch (const std::exception& ex) {cerr << ex.what() << endl; return 1;}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment