From 60d1ce3be9e5d81d593fb597979f0ca0911fde05 Mon Sep 17 00:00:00 2001 From: Celtic Minstrel Date: Sat, 19 Apr 2014 22:37:40 -0400 Subject: [PATCH] Initial setup for saving game states (code may or may not compile at this point) --- osx/BoE.xcodeproj/project.pbxproj | 12 +- osx/boe.fileio.cpp | 40 +- osx/boe.fileio.h | 1 - osx/tools/fileio.cpp | 589 ++++++------------------------ osx/tools/fileio.h | 2 + osx/tools/tarball.cpp | 98 +++++ osx/tools/tarball.hpp | 51 +++ 7 files changed, 268 insertions(+), 525 deletions(-) create mode 100644 osx/tools/tarball.cpp create mode 100644 osx/tools/tarball.hpp diff --git a/osx/BoE.xcodeproj/project.pbxproj b/osx/BoE.xcodeproj/project.pbxproj index 4bb2427b..c5a664af 100644 --- a/osx/BoE.xcodeproj/project.pbxproj +++ b/osx/BoE.xcodeproj/project.pbxproj @@ -501,6 +501,8 @@ 91B3EF5B0F969F3000BF5B67 /* scen.dlgutil.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 91B3EEF60F969BA700BF5B67 /* scen.dlgutil.cpp */; }; 91B3F1850F97894A00BF5B67 /* scen.graphics.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 91B3EEF30F969BA700BF5B67 /* scen.graphics.cpp */; }; 91BFA3D71901B18F001686E4 /* mask.vert in Copy Shaders */ = {isa = PBXBuildFile; fileRef = 91BFA3D61901B024001686E4 /* mask.vert */; }; + 91BFA3DA1902B13D001686E4 /* tarball.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 91BFA3D81902AD78001686E4 /* tarball.cpp */; }; + 91BFA3DB1902B13F001686E4 /* tarball.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 91BFA3D81902AD78001686E4 /* tarball.cpp */; }; 91BFA3E919033E01001686E4 /* gzstream.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 91BFA3DE19033E01001686E4 /* gzstream.cpp */; }; 91BFA3EA19033E01001686E4 /* gzstream.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 91BFA3DE19033E01001686E4 /* gzstream.cpp */; }; 91BFA3EB19033E01001686E4 /* gzstream.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 91BFA3DE19033E01001686E4 /* gzstream.cpp */; }; @@ -1607,6 +1609,8 @@ 91B3F11D0F97801F00BF5B67 /* mathutil.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = mathutil.h; sourceTree = ""; }; 91B3F11E0F97801F00BF5B67 /* mathutil.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = mathutil.cpp; sourceTree = ""; }; 91BFA3D61901B024001686E4 /* mask.vert */ = {isa = PBXFileReference; explicitFileType = sourcecode.glsl; fileEncoding = 4; path = mask.vert; sourceTree = ""; }; + 91BFA3D81902AD78001686E4 /* tarball.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = tarball.cpp; sourceTree = ""; }; + 91BFA3D91902ADD5001686E4 /* tarball.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = tarball.hpp; sourceTree = ""; }; 91BFA3DE19033E01001686E4 /* gzstream.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = gzstream.cpp; sourceTree = ""; }; 91BFA3DF19033E01001686E4 /* gzstream.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = gzstream.h; sourceTree = ""; }; 91C688E60FD702B9000F6D01 /* cursors.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = cursors.h; sourceTree = ""; }; @@ -2097,11 +2101,12 @@ 91B3F10A0F9779C300BF5B67 /* graphtool.cpp */, 91B3F11E0F97801F00BF5B67 /* mathutil.cpp */, 913D005A0F9FEEC200184C18 /* porting.cpp */, + 91EC481018FBABB100BB1E86 /* prefs.mac.mm */, 91F6F8F518F8DE6300E3EA15 /* qdpict.cpp */, 91B3F10F0F9779D000BF5B67 /* soundtool.cpp */, + 91BFA3D81902AD78001686E4 /* tarball.cpp */, 912283C80FD0E16C00B21642 /* undo.cpp */, 919145FF18E63B70005CF3A4 /* winutil.mac.mm */, - 91EC481018FBABB100BB1E86 /* prefs.mac.mm */, 91A0B15A1900F73E00EF438F /* mask.frag */, 91BFA3D61901B024001686E4 /* mask.vert */, ); @@ -2116,10 +2121,11 @@ 91B3F1090F9779C300BF5B67 /* graphtool.h */, 91B3F11D0F97801F00BF5B67 /* mathutil.h */, 913D00590F9FEEC200184C18 /* porting.h */, + 91EC480E18FBAA8700BB1E86 /* prefs.hpp */, 91B3F10E0F9779D000BF5B67 /* soundtool.h */, + 91BFA3D91902ADD5001686E4 /* tarball.hpp */, 917B573F100B956C0096C978 /* undo.h */, 919145FE18E63B41005CF3A4 /* winutil.h */, - 91EC480E18FBAA8700BB1E86 /* prefs.hpp */, ); name = headers; sourceTree = ""; @@ -2820,6 +2826,7 @@ 91F6F8F618F8DE6300E3EA15 /* qdpict.cpp in Sources */, 91EC483B18FBAD8000BB1E86 /* prefs.mac.mm in Sources */, 91FCC8F218FEEFE0007026CE /* pc.editors.cpp in Sources */, + 91BFA3DA1902B13D001686E4 /* tarball.cpp in Sources */, 91BFA3E919033E01001686E4 /* gzstream.cpp in Sources */, ); runOnlyForDeploymentPostprocessing = 0; @@ -2872,6 +2879,7 @@ 91FCC8DC18FE2CE8007026CE /* pc.menus.mac.mm in Sources */, 91FCC8F118FEEDC6007026CE /* winutil.mac.mm in Sources */, 91FCC8F418FF0866007026CE /* pc.appleevents.mm in Sources */, + 91BFA3DB1902B13F001686E4 /* tarball.cpp in Sources */, 91BFA3EA19033E01001686E4 /* gzstream.cpp in Sources */, ); runOnlyForDeploymentPostprocessing = 0; diff --git a/osx/boe.fileio.cpp b/osx/boe.fileio.cpp index d1a3c51e..1b68959a 100644 --- a/osx/boe.fileio.cpp +++ b/osx/boe.fileio.cpp @@ -86,7 +86,7 @@ void add_outdoor_maps(); short specials_res_id,data_dump_file_id; char start_name[256]; short start_volume,data_volume; -fs::path progDir; +extern fs::path progDir; //outdoor_record_type dummy_out;//// //town_record_type dummy_town; @@ -99,44 +99,6 @@ fs::path progDir; cCustomGraphics spec_scen_g; std::ofstream flog("bladeslog.txt"); -// TODO: Move this to the common fileio.cpp -void init_directories() -{ - char cPath[768]; - CFBundleRef mainBundle=CFBundleGetMainBundle(); - - CFStringRef progURL = CFURLCopyFileSystemPath(CFBundleCopyBundleURL(mainBundle), kCFURLPOSIXPathStyle); - const char* tmp = CFStringGetCStringPtr(progURL, kCFStringEncodingASCII);//kCFStringEncodingUTF8); - if(tmp == NULL){ - bool success = CFStringGetCString(progURL, cPath, sizeof(cPath), kCFStringEncodingUTF8); - if(success) { - progDir = cPath; - std::cout << cPath << "\n\n" << progDir << "\n\n"; - } else { - std::cout << "Couldn't retrieve application path.\n"; - exit(1); - } - }else progDir = tmp; - progDir = progDir.parent_path(); - std::cout<(progDir/"Scenario Editor"/"graphics.exd"/"mac"); - ResMgr::pushPath(progDir/"Scenario Editor"/"graphics.exd"/"mac"/"cursors"); - ResMgr::pushPath(progDir/"data"/"fonts"); - ResMgr::pushPath(progDir/"data"/"strings"); - ResMgr::pushPath(progDir/"Scenario Editor"/"sounds.exa"); - - // now I generate the directory ID of the folder which contains the scenarios - // code copied from Mac Prog FAQ book -// myCPB.dirInfo.ioNamePtr = folder_name; -// myCPB.dirInfo.ioVRefNum = start_volume; -// myCPB.dirInfo.ioFDirIndex = 0; -// myCPB.dirInfo.ioDrDirID = start_dir; -// error = PBGetCatalogInfoSync(&myCPB); // false means not async -// -// scen_dir = myCPB.dirInfo.ioDrDirID; -} - void finish_load_party(){ bool town_restore = (univ.town.num < 200) ? true : false; diff --git a/osx/boe.fileio.h b/osx/boe.fileio.h index 351c3a52..dd3d272a 100644 --- a/osx/boe.fileio.h +++ b/osx/boe.fileio.h @@ -3,7 +3,6 @@ namespace fs = boost::filesystem; // TODO: Centralize this alias! -void init_directories(); //void load_file(); //void save_file(short mode); void change_rect_terrain(RECT r,ter_num_t terrain_type,short probability,bool hollow); diff --git a/osx/tools/fileio.cpp b/osx/tools/fileio.cpp index ad2475e0..326e3197 100644 --- a/osx/tools/fileio.cpp +++ b/osx/tools/fileio.cpp @@ -11,7 +11,6 @@ #include #include -#include //#include "scen.global.h" #include #include "classes.h" @@ -25,6 +24,8 @@ #include "porting.h" #include "dlogutil.h" #include "restypes.hpp" +#include "tarball.hpp" +#include "gzstream.h" extern bool cur_scen_is_mac, mac_is_intel; extern cScenario scenario; @@ -36,66 +37,53 @@ extern cUniverse univ; //extern unsigned char borders[4][50]; //extern cOutdoors current_terrain; //extern cTown* town; -extern fs::path progDir; +fs::path progDir, tempDir; //extern short overall_mode; -struct header_posix_ustar { - char name[100]; - char mode[8]; - char uid[8]; - char gid[8]; - char size[12]; - char mtime[12]; - char checksum[8]; - char typeflag[1]; - char linkname[100]; - char magic[6]; - char version[2]; - char uname[32]; - char gname[32]; - char devmajor[8]; - char devminor[8]; - char prefix[155]; - char pad[12]; -}; - #include +// TODO: Try to find a way to get our path without using CoreFoundation, and also replace cout with printf +#include -static header_posix_ustar generateTarHeader(const std::string& fileName, unsigned long long fileSize,bool directory=false){ - if(fileSize>077777777777LL) - throw std::length_error("Specified file size >= 8 GB"); - if(fileName.length()>=100) - throw std::length_error("Specified file name longer than 99 characters."); - header_posix_ustar header; - char* init = (char*) &header; - for(unsigned int i = 0; i < sizeof(header); i++) init[i] = 0; +void init_directories() { + char cPath[768]; + CFBundleRef mainBundle = CFBundleGetMainBundle(); - sprintf(header.name,"%s",fileName.c_str()); - sprintf(header.mode,"%07o",0600); - //leave uid filled with NULs - //leave gid filled with NULs - sprintf(header.size,"%011llo",fileSize); - sprintf(header.mtime,"%011lo",time(NULL)); - memset(header.checksum,' ',8); - header.typeflag[0]=directory?'5':'0'; - //leave linkname filled with NULs - sprintf(header.magic,"ustar "); //overwrites header with " \0" - //leave uname filled with NULs - //leave gname filled with NULs - //leave devmajor filled with NULs - //leave devminor filled with NULs - //leave prefix filled with NULs - //leave pad filled with NULs + CFStringRef progURL = CFURLCopyFileSystemPath(CFBundleCopyBundleURL(mainBundle), kCFURLPOSIXPathStyle); + const char* tmp = CFStringGetCStringPtr(progURL, kCFStringEncodingASCII); + if(tmp == NULL){ + bool success = CFStringGetCString(progURL, cPath, sizeof(cPath), kCFStringEncodingUTF8); + if(success) { + progDir = cPath; + std::cout << cPath << "\n\n" << progDir << "\n\n"; + } else { + std::cout << "Couldn't retrieve application path.\n"; + exit(1); + } + } else progDir = tmp; + progDir = progDir.parent_path(); + // TODO: If this places us in the "Scenario Editor" folder, back out one more directory + std::cout << progDir << '\n'; + // Initialize the resource manager paths + ResMgr::pushPath(progDir/"Scenario Editor"/"graphics.exd"/"mac"); + ResMgr::pushPath(progDir/"Scenario Editor"/"graphics.exd"/"mac"/"cursors"); + ResMgr::pushPath(progDir/"data"/"fonts"); + ResMgr::pushPath(progDir/"data"/"strings"); + ResMgr::pushPath(progDir/"Scenario Editor"/"sounds.exa"); - unsigned int sum=0; - unsigned char* ptr=reinterpret_cast(&header); - for(unsigned int i=0; i0777777) - throw std::runtime_error("Checksum overflow"); - sprintf(header.checksum,"%o",sum); - return(header); + // We need a location for temporary files, primarily for loading and saving operations + // The scenario editor may also use this location as "scratch space" +#if defined(_WIN32) || defined(_WIN64) + tempDir = getenv("APPDATA"); + tempDir /= "Blades of Exile"; +#else + tempDir = getenv("HOME"); +#ifdef __APPLE__ + tempDir /= "Library/Application Support/Blades of Exile"; +#else + tempDir /= ".oboe/blades"; +#endif // __APPLE__ +#endif // _Win32||_Win64 + tempDir /= "Temporary Files"; } bool load_scenario(fs::path file_to_load, bool skip_strings){ @@ -1264,450 +1252,85 @@ bool load_party_v2(fs::path file_to_load, bool town_restore, bool in_scen, bool bool save_party(fs::path dest_file) //mode; // 0 - normal 1 - save as { - //printf("Saving is currently disabled.\n"); + if(!fs::exists(tempDir)) fs::create_directories(tempDir); + bool in_scen = univ.party.scen_name != ""; bool in_town = univ.town.num < 200; bool save_maps = !univ.party.stuff_done[306][0]; - // TODO: Strip off the header and save the remainder to a temporary file, then use gzip on that; this should fix the issue of the header being interpreted as compressed. - /* Open the GZip file and write the (uncompressed) header */ - std::ofstream fout(dest_file.c_str(), std::ios_base::binary); + tarball partyOut; + + // First, write the main party data + univ.party.writeTo(partyOut.newFile("save/party.txt")); + { + std::ostream& fout = partyOut.newFile("save/setup.dat"); + static uint16_t magic = 0x0B0E; + fout.write((char*)&magic, 2); + fout.write((char*)&univ.party.setup, sizeof(cParty::setup)); + } + + // Then write the data for each of the party members + for(int i = 0; i < 6; i++) { + static char fname[] = "save/pc1.txt"; + fname[7] = i + '1'; + univ.party[i].writeTo(partyOut.newFile(fname)); + } + + if(in_scen) { + if(in_town) { + // Write the current town data + univ.town.writeTo(partyOut.newFile("save/town.txt")); + + if(save_maps) { + // Write the town map data + std::ostream& fout = partyOut.newFile("save/townmaps.dat"); + for(int i = 0; i < 200; i++) + for(int j = 0; j < 8; j++) + for(int k = 0; k < 64; k++) + fout.put(univ.town_maps[i][j][k]); + } + } + + // Write the current outdoors data + univ.out.writeTo(partyOut.newFile("save/out.txt")); + + if(save_maps) { + // Write the outdoors map data + std::ostream& fout = partyOut.newFile("save/outmaps.dat"); + for(int i = 0; i < 100; i++) + for(int j = 0; j < 6; j++) + for(int k = 0; k < 48; k++) + fout.put(univ.out_maps[i][j][k]); + } + } + + // TODO: Add the party graphics sheet + + // Write out the compressed data + fs::path tempPath = tempDir/"savetemp.exg"; + ogzstream zout(tempPath.c_str()); + partyOut.writeTo(zout); + zout.close(); + + // Now add the header. We use the temporary file because we want the header to be uncompressed. short flags[] = { 0x0B0E, // to indicate new format static_cast(in_town ? 1342 : 5790), // is the party in town? static_cast(in_scen ? 100 : 200), // is the party in a scenario? static_cast(save_maps ? 5567 : 3422), // is the save maps feature enabled? 0x0100, // current version number, major and minor revisions only - // Version 1 indicates a beta format that won't be supported in the final release + // Version 1 indicates a beta format that may not be supported in the final release }; if(!mac_is_intel) // must flip all the flags to little-endian for(int i = 0; i < 5; i++) flip_short(&flags[i]); - //fwrite(flags, sizeof(short), 5, f); - //gzFile party_file = gzdopen(fileno(f),"wb0"); // 0 indicates no compression; this is temporary - //gzwrite(party_file, flags, sizeof(short)*5); - //int q = gzflush(party_file, Z_FULL_FLUSH); // required in order to avoid the header getting compressed - //gzsetparams(party_file, Z_DEFAULT_COMPRESSION, Z_DEFAULT_STRATEGY); + + std::ifstream fin(tempPath.c_str(), std::ios_base::binary); + std::ofstream fout(dest_file.c_str(), std::ios_base::binary); fout.write((char*) flags, 5*sizeof(short)); - - /* Initialize buffer and other variables */ - header_posix_ustar header; - static char footer[2*sizeof(header_posix_ustar)] = {0}; - std::ostringstream sout(""); - std::ostringstream bout("",std::ios_base::binary); - std::string tar_entry; - size_t tar_size, y; - int x; - sout << x; - /* Write main party data to a buffer, and then to the file */ - univ.party.writeTo(sout); - //write the footer to end the file -// for(unsigned int j=0; j<2*sizeof(header_posix_ustar); j++) -// sout.put('\0'); - tar_entry = sout.str(); - y = tar_size = tar_entry.length(); - while(y % 512){ - y++; - tar_entry += '\0'; - } - header = generateTarHeader("save/party.txt",tar_size); - bout.write((char*) &header, sizeof(header)); - bout.write(tar_entry.c_str(), y); -// x = gzwrite(party_file, (char*) &header, sizeof(header)); -// x = gzwrite(party_file, tar_entry.c_str(), y); -// //x = gzwrite(party_file, footer, 2*sizeof(header_posix_ustar)); -// if(x == 0) { // error -// gzerror(party_file, &x); -// oops_error((x == Z_ERRNO) ? Z_ERRNO + 1000 : 1, (x == Z_ERRNO) ? errno : x, 1); -// gzclose(party_file); -// return false; -// } - sout.str(""); // clear the stream (I think) - - /* Write player character data to a buffer, and then to the file */ - for(int i = 0; i < 6; i++){ - char f_name[20]; - univ.party[i].writeTo(sout); - //write the footer to end the file -// for(unsigned int j=0; j<2*sizeof(header_posix_ustar); j++) -// sout.put('\0'); - tar_entry = sout.str(); - y = tar_size = tar_entry.length(); - while(y % 512){ - y++; - tar_entry += '\0'; - } - sprintf(f_name,"save/pc%i.txt",i+1); - header = generateTarHeader(f_name,tar_size); - bout.write((char*) &header, sizeof(header)); - bout.write(tar_entry.c_str(), y); -// x = gzwrite(party_file, (char*) &header, sizeof(header)); -// x = gzwrite(party_file, tar_entry.c_str(), y); -// //x = gzwrite(party_file, footer, 2*sizeof(header_posix_ustar)); -// if(x == 0) { // error -// gzerror(party_file, &x); -// oops_error((x == Z_ERRNO) ? Z_ERRNO + 1000 : 1, (x == Z_ERRNO) ? errno : x, 1); -// gzclose(party_file); -// return false; -// } - sout.str(""); // clear the stream (I think) - } - - if(in_scen){ - if(in_town){ - /* Write current town data to a buffer, and then to the file */ - univ.town.writeTo(sout); - //write the footer to end the file -// for(unsigned int j=0; j<2*sizeof(header_posix_ustar); j++) -// sout.put('\0'); - tar_entry = sout.str(); - y = tar_size = tar_entry.length(); - while(y % 512){ - y++; - tar_entry += '\0'; - } - header = generateTarHeader("save/town.txt",tar_size); - bout.write((char*) &header, sizeof(header)); - bout.write(tar_entry.c_str(), y); -// x = gzwrite(party_file, (char*) &header, sizeof(header)); -// x = gzwrite(party_file, tar_entry.c_str(), y); -// //x = gzwrite(party_file, footer, 2*sizeof(header_posix_ustar)); -// if(x == 0) { // error -// gzerror(party_file, &x); -// oops_error((x == Z_ERRNO) ? Z_ERRNO + 1000 : 1, (x == Z_ERRNO) ? errno : x, 1); -// gzclose(party_file); -// return false; -// } - sout.str(""); // clear the stream (I think) - - if(save_maps){ - /* Write town maps data to a buffer, and then to the file */ - for(int i = 0; i < 200; i++) - for(int j = 0; j < 8; j++) - for(int k = 0; k < 64; k++) - sout.put(univ.town_maps[i][j][k]); - //write the footer to end the file -// for(unsigned int j=0; j<2*sizeof(header_posix_ustar); j++) -// sout.put('\0'); - tar_entry = sout.str(); - y = tar_size = tar_entry.length(); - while(y % 512){ - y++; - tar_entry += '\0'; - } - header = generateTarHeader("save/townmaps.dat",tar_size); - bout.write((char*) &header, sizeof(header)); - bout.write(tar_entry.c_str(), y); -// x = gzwrite(party_file, (char*) &header, sizeof(header)); -// x = gzwrite(party_file, tar_entry.c_str(), y); -// //x = gzwrite(party_file, footer, 2*sizeof(header_posix_ustar)); -// if(x == 0) { // error -// gzerror(party_file, &x); -// oops_error((x == Z_ERRNO) ? Z_ERRNO + 1000 : 1, (x == Z_ERRNO) ? errno : x, 1); -// gzclose(party_file); -// return false; -// } - sout.str(""); // clear the stream (I think) - } - } - - /* Write current outdoors data to a buffer, and then to the file */ - univ.out.writeTo(sout); - //write the footer to end the file -// for(unsigned int j=0; j<2*sizeof(header_posix_ustar); j++) -// sout.put('\0'); - tar_entry = sout.str(); - y = tar_size = tar_entry.length(); - while(y % 512){ - y++; - tar_entry += '\0'; - } - header = generateTarHeader("save/out.txt",tar_size); - bout.write((char*) &header, sizeof(header)); - bout.write(tar_entry.c_str(), y); -// x = gzwrite(party_file, (char*) &header, sizeof(header)); -// x = gzwrite(party_file, tar_entry.c_str(), y); -// //x = gzwrite(party_file, footer, 2*sizeof(header_posix_ustar)); -// if(x == 0) { // error -// gzerror(party_file, &x); -// oops_error((x == Z_ERRNO) ? Z_ERRNO + 1000 : 1, (x == Z_ERRNO) ? errno : x, 1); -// gzclose(party_file); -// return false; -// } - sout.str(""); // clear the stream (I think) - - if(save_maps){ - /* Write outdoor maps data to a buffer, and then to the file */ - for(int i = 0; i < 100; i++) - for(int j = 0; j < 6; j++) - for(int k = 0; k < 48; k++) - sout.put(univ.out_maps[i][j][k]); - //write the footer to end the file -// for(unsigned int j=0; j<2*sizeof(header_posix_ustar); j++) -// sout.put('\0'); - tar_entry = sout.str(); - y = tar_size = tar_entry.length(); - while(y % 512){ - y++; - tar_entry += '\0'; - } - header = generateTarHeader("save/outmaps.dat",tar_size); - bout.write((char*) &header, sizeof(header)); - bout.write(tar_entry.c_str(), y); -// x = gzwrite(party_file, (char*) &header, sizeof(header)); -// x = gzwrite(party_file, tar_entry.c_str(), y); -// //x = gzwrite(party_file, footer, 2*sizeof(header_posix_ustar)); -// if(x == 0) { // error -// gzerror(party_file, &x); -// oops_error((x == Z_ERRNO) ? Z_ERRNO + 1000 : 1, (x == Z_ERRNO) ? errno : x, 1); -// gzclose(party_file); -// return false; -// } -// sout.str(""); // clear the stream (I think) - } - } - //x = gzwrite(party_file, footer, 2*sizeof(header_posix_ustar)); - //gzclose(party_file); - bout.write(footer, 2*sizeof(header_posix_ustar)); - tar_entry = bout.str(); - tar_size = tar_entry.length(); - unsigned char* buf = new unsigned char[tar_size]; - compress(buf, &tar_size, (unsigned char*) tar_entry.data(), tar_size); - fout.write((char*)buf, tar_size); - delete buf; + fout << fin.rdbuf(); + fin.close(); fout.close(); -// NavReplyRecord reply; -// OSErr error; -// short file_id; -// bool town_save = false; -// Str63 store_name; -// FSSpec to_load; -// -// short i; -// -// long len,store_len,count; -// flag_type flag; -// flag_type *store; -// party_record_type *party_ptr; -// setup_save_type *setup_ptr; -// pc_record_type *pc_ptr; -// // out_info_type store_explored; -// current_town_type *town_ptr; -// big_tr_type *town_data_ptr; -// town_item_list *item_ptr; -// stored_items_list_type *items_ptr; -// stored_town_maps_type *maps_ptr; -// stored_outdoor_maps_type *o_maps_ptr; -// -// char *party_encryptor; -// -// if ((in_startup_mode == false) && (is_town())) -// town_save = true; -// -// strcpy ((char *) store_name, (char *) last_load_file); -// -// -// if ((mode == 1) || (loaded_yet == false)) { -// NavPutFile(NULL,&reply,NULL,NULL,'beSV','blx!',NULL); -// if (reply.validRecord == false) -// return; -// -// AEKeyword keyword; -// DescType descType; -// Size actualSize; -// -// AEGetNthPtr(&reply.selection,1,typeFSS,&keyword,&descType,&to_load,sizeof(FSSpec),&actualSize); -// loaded_yet = true; -// } -// else to_load = store_file_reply; -// -// error = FSpCreate(&to_load,'blx!','beSV',reply.keyScript); -// if ((error != 0) && (error != dupFNErr)) { -// add_string_to_buf("Save: Couldn't create file. "); -// SysBeep(2); -// return; -// } -// if ((error = FSpOpenDF(&to_load,3,&file_id)) != 0) { -// add_string_to_buf("Save: Couldn't open file. "); -// SysBeep(2); -// return; -// } -// strcpy ((char *) last_load_file, (char *) to_load.name); -// store_file_reply = to_load; -// -// -// store = &flag; -// -// len = sizeof(flag_type); -// -// flag.i = (town_save == true) ? 1342 : 5790; -// if ((error = FSWrite(file_id, &len, (char *) store)) != 0){ -// add_string_to_buf("Save: Couldn't write to file. "); -// FSClose(file_id); -// SysBeep(2); -// } -// flag.i = (in_startup_mode == false) ? 100 : 200; -// if ((error = FSWrite(file_id, &len, (char *) store)) != 0) { -// add_string_to_buf("Save: Couldn't write to file. "); -// FSClose(file_id); -// SysBeep(2); -// return; -// } -// flag.i = (save_maps == true) ? 5567 : 3422; -// if ((error = FSWrite(file_id, &len, (char *) store)) != 0){ -// add_string_to_buf("Save: Couldn't write to file. "); -// FSClose(file_id); -// SysBeep(2); -// return; -// } -// -// // SAVE PARTY -// party_ptr = &party; -// len = sizeof(party_record_type); -// -// store_len = len; -// party_encryptor = (char *) party_ptr; -// for (count = 0; count < store_len; count++) -// party_encryptor[count] ^= 0x5C; -// if ((error = FSWrite(file_id, &len, (char *) party_ptr)) != 0) { -// add_string_to_buf("Save: Couldn't write to file. "); -// FSClose(file_id); -// for (count = 0; count < store_len; count++) -// party_encryptor[count] ^= 0x5C; -// SysBeep(2); -// return; -// } -// for (count = 0; count < store_len; count++) -// party_encryptor[count] ^= 0x5C; -// -// // SAVE SETUP -// setup_ptr = &setup_save; -// len = sizeof(setup_save_type); -// if ((error = FSWrite(file_id, &len, (char *) setup_ptr)) != 0){ -// add_string_to_buf("Save: Couldn't write to file. "); -// FSClose(file_id); -// SysBeep(2); -// return; -// } -// -// // SAVE PCS -// store_len = sizeof(pc_record_type); -// for (i = 0; i < 6; i++) { -// pc_ptr = &adven[i]; -// -// len = store_len; -// party_encryptor = (char *) pc_ptr; -// for (count = 0; count < store_len; count++) -// party_encryptor[count] ^= 0x6B; -// if ((error = FSWrite(file_id, &len, (char *) pc_ptr)) != 0){ -// add_string_to_buf("Save: Couldn't write to file. "); -// FSClose(file_id); -// for (count = 0; count < store_len; count++) -// party_encryptor[count] ^= 0x6B; -// SysBeep(2); -// return; -// } -// for (count = 0; count < store_len; count++) -// party_encryptor[count] ^= 0x6B; -// } -// -// if (in_startup_mode == false) { -// -// // SAVE OUT DATA -// len = sizeof(out_info_type); -// if ((error = FSWrite(file_id, &len, (char *) out_e)) != 0) { -// add_string_to_buf("Save: Couldn't write to file. "); -// FSClose(file_id); -// SysBeep(2); -// return; -// } -// -// if (town_save == true) { -// town_ptr = &c_town; -// len = sizeof(current_town_type); -// if ((error = FSWrite(file_id, &len, (char *) town_ptr)) != 0) { -// add_string_to_buf("Save: Couldn't write to file. "); -// FSClose(file_id); -// SysBeep(2); -// return; -// } -// town_data_ptr = &t_d; -// len = sizeof(big_tr_type); -// if ((error = FSWrite(file_id, &len, (char *) town_data_ptr)) != 0) { -// add_string_to_buf("Save: Couldn't write to file. "); -// FSClose(file_id); -// SysBeep(2); -// return; -// } -// item_ptr = &t_i; -// len = sizeof(town_item_list); -// if ((error = FSWrite(file_id, &len, (char *) item_ptr)) != 0) { -// add_string_to_buf("Save: Couldn't write to file. "); -// FSClose(file_id); -// SysBeep(2); -// return; -// } -// } -// -// // Save stored items -// for (i = 0; i < 3; i++) { -// items_ptr = &stored_items[i]; -// len = (long) sizeof(stored_items_list_type); -// if ((error = FSWrite(file_id, &len, (char *) items_ptr)) != 0){ -// add_string_to_buf("Save: Couldn't write to file. "); -// FSClose(file_id); -// SysBeep(2); -// return; -// } -// } -// -// // If saving maps, save maps -// if (save_maps == true) { -// maps_ptr = &(town_maps); -// len = (long) sizeof(stored_town_maps_type); -// if ((error = FSWrite(file_id, &len, (char *) maps_ptr)) != 0){ -// add_string_to_buf("Save: Couldn't write to file. "); -// FSClose(file_id); -// SysBeep(2); -// return; -// } -// -// o_maps_ptr = &o_maps; -// len = (long) sizeof(stored_outdoor_maps_type); -// if ((error = FSWrite(file_id, &len, (char *) o_maps_ptr)) != 0) { -// add_string_to_buf("Save: Couldn't write to file. "); -// FSClose(file_id); -// SysBeep(2); -// return; -// } -// } -// -// // SAVE SFX and MISC_I -// len = (long) (64 * 64); -// if ((error = FSWrite(file_id, &len, (char *) sfx)) != 0){ -// add_string_to_buf("Save: Couldn't write to file. "); -// FSClose(file_id); -// SysBeep(2); -// return; -// } -// if ((error = FSWrite(file_id, &len, (char *) misc_i)) != 0){ -// add_string_to_buf("Save: Couldn't write to file. "); -// FSClose(file_id); -// SysBeep(2); -// return; -// } -// -// -// -// } -// -// if ((error = FSClose(file_id)) != 0) { -// add_string_to_buf("Save: Couldn't close file. "); -// SysBeep(2); -// return; -// } -// if (in_startup_mode == false) -// add_string_to_buf("Save: Game saved. "); return true; } diff --git a/osx/tools/fileio.h b/osx/tools/fileio.h index 85e090d7..ff9a65b2 100644 --- a/osx/tools/fileio.h +++ b/osx/tools/fileio.h @@ -27,3 +27,5 @@ std::vector load_strings(std::string which); bool load_party(fs::path file_to_load); bool save_party(fs::path dest_file); + +void init_directories(); diff --git a/osx/tools/tarball.cpp b/osx/tools/tarball.cpp new file mode 100644 index 00000000..deb3cfbf --- /dev/null +++ b/osx/tools/tarball.cpp @@ -0,0 +1,98 @@ +// +// tarball.cpp +// BoE +// +// Created by Celtic Minstrel on 14-04-19. +// +// + +#include "tarball.hpp" +#include + +tarball::header_posix_ustar tarball::generateTarHeader(const std::string& fileName, unsigned long long fileSize, bool directory){ + static_assert(sizeof(header_posix_ustar) == 512, "Uh-oh! Padding in the tarball header!"); + if(fileSize > 077777777777LL) + throw std::length_error("Specified file size >= 8 GB"); + if(fileName.length() >= 100) + throw std::length_error("Specified file name longer than 99 characters."); + header_posix_ustar header; + char* init = (char*) &header; + for(unsigned int i = 0; i < sizeof(header); i++) init[i] = 0; + + snprintf(header.name,100,"%s",fileName.c_str()); + snprintf(header.mode,8,"%07o",0600); + // leave uid filled with NULs + // leave gid filled with NULs + snprintf(header.size,12,"%011llo",fileSize); + snprintf(header.mtime,12,"%011lo",time(NULL)); + memset(header.checksum,' ',8); + header.typeflag[0] = directory ? '5' : '0'; + // leave linkname filled with NULs + snprintf(header.magic,6,"ustar "); + snprintf(header.version,2," "); + // leave uname filled with NULs + // leave gname filled with NULs + // leave devmajor filled with NULs + // leave devminor filled with NULs + // leave prefix filled with NULs + // leave pad filled with NULs + + unsigned int sum = 0; + unsigned char* ptr = reinterpret_cast(&header); + for(unsigned int i = 0; i < sizeof(header); i++){ + sum += ptr[i]; + } + if(sum > 0777777) + throw std::runtime_error("Checksum overflow"); + snprintf(header.checksum,8,"%o",sum); + return header; +} + +void tarball::writeTo(std::ostream& out) { + static const char padding[512] = {0}; + for(tarfile& entry : files) { + entry.contents.seekg(0, std::ios_base::end); + unsigned long long size = entry.contents.tellg(); + unsigned long long padLength = 512 - size % 512; + entry.contents.seekg(0); + entry.header = generateTarHeader(entry.filename, size); + out.write((char*)&entry.header, sizeof(header_posix_ustar)); + out << entry.contents.rdbuf(); + out.write(padding, padLength); + } +} + +void tarball::readFrom(std::istream& in) { + static char buf[513]; + while(!in.eof()) { + files.push_back(tarfile()); + header_posix_ustar& header = files.back().header; + in.read((char*)&header, sizeof(header_posix_ustar)); + files.back().filename = header.name; + unsigned long long size = boost::lexical_cast(header.size); + unsigned long long padLength = 512 - size % 512; + in.read(buf, size); + files.back().contents.write(buf, size); + in.seekg(padLength, std::ios_base::cur); + } +} + +std::ostream& tarball::newFile(std::string fname) { + files.push_back(tarfile()); + files.back().filename = fname; + return files.back().contents; +} + +std::istream& tarball::getFile(std::string fname) { + for(tarfile& entry : files) { + if(entry.filename == fname) { + entry.contents.seekg(0); + return entry.contents; + } + } + // If the file doesn't exist, return an empty stream + static std::istringstream empty; + empty.clear(); + empty.seekg(0); + return empty; +} diff --git a/osx/tools/tarball.hpp b/osx/tools/tarball.hpp new file mode 100644 index 00000000..5590c558 --- /dev/null +++ b/osx/tools/tarball.hpp @@ -0,0 +1,51 @@ +// +// tarball.hpp +// BoE +// +// Created by Celtic Minstrel on 14-04-19. +// +// + +#ifndef BoE_tarball_hpp +#define BoE_tarball_hpp + +#include +#include + +class tarball { + struct header_posix_ustar { + char name[100]; + char mode[8]; + char uid[8]; + char gid[8]; + char size[12]; + char mtime[12]; + char checksum[8]; + char typeflag[1]; + char linkname[100]; + char magic[6]; + char version[2]; + char uname[32]; + char gname[32]; + char devmajor[8]; + char devminor[8]; + char prefix[155]; + char pad[12]; + }; + struct tarfile { + header_posix_ustar header; + std::string filename; + std::stringstream contents; + }; + std::deque files; + static header_posix_ustar generateTarHeader(const std::string& fileName, unsigned long long fileSize, bool directory=false); +public: + void writeTo(std::ostream& out); + void readFrom(std::istream& in); + std::ostream& newFile(std::string fname); + void newDirectory(std::string dname); + std::istream& getFile(std::string fname); +}; + + +#endif