├── README.md ├── base64.cpp ├── base64.h ├── deflate.cpp ├── deflate.h ├── entries.txt ├── lexer.cpp ├── lexer.h ├── main.cpp ├── save.cpp ├── save.h ├── table.cpp └── table.h /README.md: -------------------------------------------------------------------------------- 1 | # Dont Starve Save API 2 | Don't Starve save editor for deserialize/serialize a Don't Starve save file 3 | 4 | Allow you to access of all variables of the game including: 5 | - player's health, sanity, hunger, inventory, etc 6 | - game entities and objects 7 | - world tiles 8 | 9 | So you can add or remove monsters/objects/other entities, edit the world structure, etc... 10 | 11 | Example of what you can do if you draw tiles/entities loaded by the save loader: 12 | ![alt text](http://i.imgur.com/0dhl5Qa.jpg) 13 | ![alt text](http://i.imgur.com/3xyYVgc.jpg) 14 | 15 | Credits: 16 | * LuaTable parser/writer by Marmontel Boris 17 | * Base64 decoder/encoder by René Nyffenegger 18 | * Deflate string compress/decompress by Timo Bingmann 19 | -------------------------------------------------------------------------------- /base64.cpp: -------------------------------------------------------------------------------- 1 | #include "base64.h" 2 | 3 | /* 4 | base64.cpp and base64.h 5 | 6 | Copyright (C) 2004-2008 René Nyffenegger 7 | 8 | This source code is provided 'as-is', without any express or implied 9 | warranty. In no event will the author be held liable for any damages 10 | arising from the use of this software. 11 | 12 | Permission is granted to anyone to use this software for any purpose, 13 | including commercial applications, and to alter it and redistribute it 14 | freely, subject to the following restrictions: 15 | 16 | 1. The origin of this source code must not be misrepresented; you must not 17 | claim that you wrote the original source code. If you use this source code 18 | in a product, an acknowledgment in the product documentation would be 19 | appreciated but is not required. 20 | 21 | 2. Altered source versions must be plainly marked as such, and must not be 22 | misrepresented as being the original source code. 23 | 24 | 3. This notice may not be removed or altered from any source distribution. 25 | 26 | René Nyffenegger rene.nyffenegger@adp-gmbh.ch 27 | 28 | */ 29 | 30 | static const std::string base64_chars = 31 | "ABCDEFGHIJKLMNOPQRSTUVWXYZ" 32 | "abcdefghijklmnopqrstuvwxyz" 33 | "0123456789+/"; 34 | 35 | 36 | static inline bool is_base64(unsigned char c) { 37 | return (isalnum(c) || (c == '+') || (c == '/')); 38 | } 39 | 40 | std::string base64_encode(unsigned char const* bytes_to_encode, unsigned int in_len) { 41 | std::string ret; 42 | int i = 0; 43 | int j = 0; 44 | unsigned char char_array_3[3]; 45 | unsigned char char_array_4[4]; 46 | 47 | while (in_len--) { 48 | char_array_3[i++] = *(bytes_to_encode++); 49 | if (i == 3) { 50 | char_array_4[0] = (char_array_3[0] & 0xfc) >> 2; 51 | char_array_4[1] = ((char_array_3[0] & 0x03) << 4) + ((char_array_3[1] & 0xf0) >> 4); 52 | char_array_4[2] = ((char_array_3[1] & 0x0f) << 2) + ((char_array_3[2] & 0xc0) >> 6); 53 | char_array_4[3] = char_array_3[2] & 0x3f; 54 | 55 | for(i = 0; (i <4) ; i++) 56 | ret += base64_chars[char_array_4[i]]; 57 | i = 0; 58 | } 59 | } 60 | 61 | if (i) 62 | { 63 | for(j = i; j < 3; j++) 64 | char_array_3[j] = '\0'; 65 | 66 | char_array_4[0] = (char_array_3[0] & 0xfc) >> 2; 67 | char_array_4[1] = ((char_array_3[0] & 0x03) << 4) + ((char_array_3[1] & 0xf0) >> 4); 68 | char_array_4[2] = ((char_array_3[1] & 0x0f) << 2) + ((char_array_3[2] & 0xc0) >> 6); 69 | char_array_4[3] = char_array_3[2] & 0x3f; 70 | 71 | for (j = 0; (j < i + 1); j++) 72 | ret += base64_chars[char_array_4[j]]; 73 | 74 | while((i++ < 3)) 75 | ret += '='; 76 | 77 | } 78 | 79 | return ret; 80 | 81 | } 82 | std::string base64_decode(std::string const& encoded_string) { 83 | int in_len = encoded_string.size(); 84 | int i = 0; 85 | int j = 0; 86 | int in_ = 0; 87 | unsigned char char_array_4[4], char_array_3[3]; 88 | std::string ret; 89 | 90 | while (in_len-- && ( encoded_string[in_] != '=') && is_base64(encoded_string[in_])) { 91 | char_array_4[i++] = encoded_string[in_]; in_++; 92 | if (i ==4) { 93 | for (i = 0; i <4; i++) 94 | char_array_4[i] = base64_chars.find(char_array_4[i]); 95 | 96 | char_array_3[0] = (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4); 97 | char_array_3[1] = ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2); 98 | char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3]; 99 | 100 | for (i = 0; (i < 3); i++) 101 | ret += char_array_3[i]; 102 | i = 0; 103 | } 104 | } 105 | 106 | if (i) { 107 | for (j = i; j <4; j++) 108 | char_array_4[j] = 0; 109 | 110 | for (j = 0; j <4; j++) 111 | char_array_4[j] = base64_chars.find(char_array_4[j]); 112 | 113 | char_array_3[0] = (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4); 114 | char_array_3[1] = ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2); 115 | char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3]; 116 | 117 | for (j = 0; (j < i - 1); j++) ret += char_array_3[j]; 118 | } 119 | 120 | return ret; 121 | } 122 | -------------------------------------------------------------------------------- /base64.h: -------------------------------------------------------------------------------- 1 | #ifndef BASE64_H_INCLUDED 2 | #define BASE64_H_INCLUDED 3 | 4 | #include 5 | 6 | std::string base64_encode(unsigned char const* bytes_to_encode, unsigned int in_len); 7 | std::string base64_decode(std::string const& encoded_string); 8 | 9 | #endif // BASE64_H_INCLUDED 10 | -------------------------------------------------------------------------------- /deflate.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2007 Timo Bingmann 2 | // Distributed under the Boost Software License, Version 1.0. 3 | // (See http://www.boost.org/LICENSE_1_0.txt) 4 | 5 | #include "deflate.h" 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | std::string compress_string(const std::string& str) 14 | { 15 | z_stream zs; // z_stream is zlib's control structure 16 | memset(&zs, 0, sizeof(zs)); 17 | 18 | if (deflateInit(&zs, Z_BEST_COMPRESSION) != Z_OK) 19 | throw(std::runtime_error("deflateInit failed while compressing.")); 20 | 21 | zs.next_in = (Bytef*)str.data(); 22 | zs.avail_in = str.size(); // set the z_stream's input 23 | 24 | int ret; 25 | char outbuffer[32768]; 26 | std::string outstring; 27 | 28 | // retrieve the compressed bytes blockwise 29 | do { 30 | zs.next_out = reinterpret_cast(outbuffer); 31 | zs.avail_out = sizeof(outbuffer); 32 | 33 | ret = deflate(&zs, Z_FINISH); 34 | 35 | if (outstring.size() < zs.total_out) { 36 | // append the block to the output string 37 | outstring.append(outbuffer, 38 | zs.total_out - outstring.size()); 39 | } 40 | } while (ret == Z_OK); 41 | 42 | deflateEnd(&zs); 43 | 44 | if (ret != Z_STREAM_END) { // an error occurred that was not EOF 45 | std::ostringstream oss; 46 | oss << "Exception during zlib compression: (" << ret << ") " << zs.msg; 47 | throw(std::runtime_error(oss.str())); 48 | } 49 | 50 | return outstring; 51 | } 52 | 53 | std::string decompress_string(const std::string& str) 54 | { 55 | z_stream zs; // z_stream is zlib's control structure 56 | memset(&zs, 0, sizeof(zs)); 57 | 58 | if (inflateInit(&zs) != Z_OK) 59 | throw(std::runtime_error("inflateInit failed while decompressing.")); 60 | 61 | zs.next_in = (Bytef*)str.data(); 62 | zs.avail_in = str.size(); 63 | 64 | int ret; 65 | char outbuffer[32768]; 66 | std::string outstring; 67 | 68 | // get the decompressed bytes blockwise using repeated calls to inflate 69 | do { 70 | zs.next_out = reinterpret_cast(outbuffer); 71 | zs.avail_out = sizeof(outbuffer); 72 | 73 | ret = inflate(&zs, 0); 74 | 75 | if (outstring.size() < zs.total_out) { 76 | outstring.append(outbuffer, 77 | zs.total_out - outstring.size()); 78 | } 79 | 80 | } while (ret == Z_OK); 81 | 82 | inflateEnd(&zs); 83 | 84 | if (ret != Z_STREAM_END) { // an error occurred that was not EOF 85 | std::ostringstream oss; 86 | oss << "Exception during zlib decompression: (" << ret << ") " 87 | << zs.msg; 88 | throw(std::runtime_error(oss.str())); 89 | } 90 | 91 | return outstring; 92 | } 93 | -------------------------------------------------------------------------------- /deflate.h: -------------------------------------------------------------------------------- 1 | #ifndef DEFLATE_H_INCLUDED 2 | #define DEFLATE_H_INCLUDED 3 | 4 | #include 5 | 6 | std::string compress_string(const std::string& str); 7 | std::string decompress_string(const std::string& str); 8 | 9 | #endif // DEFLATE_H_INCLUDED 10 | -------------------------------------------------------------------------------- /entries.txt: -------------------------------------------------------------------------------- 1 | armouredboat 2 2 | bambootree 5 3 | beebox 8 4 | beehive 9 5 | telebase 11 6 | berrybush 12 7 | berrybush2 13 8 | birdtrap 15 9 | bush_vine 17 10 | cactus 19 11 | cargoboat 20 12 | catcoonden 21 13 | cave_entrance 24 14 | cave_exit 25 15 | chester_eyebone 28 16 | cookpot 34 17 | coral_brain_rock 35 18 | coralreef 36 19 | evergreen 40 20 | evergreen_sparse 41 21 | slow_farmplot 45 22 | fast_farmplot 46 23 | firepit 47 24 | flamegeyser 51 25 | grass 53 26 | grass_water 54 27 | gravestone 55 28 | houndmound 57 29 | rock_ice 58 30 | icebox 59 31 | walrus_camp 62 32 | jungletree 63 33 | lightning_rod 67 34 | livingjungletree 69 35 | livinglog 70 36 | rowboat 72 37 | marbletree 77 38 | marsh_tree 78 39 | meatrack 80 40 | mermhouse 81 41 | messagebottle 83 42 | mussel_farm 90 43 | insanityrock 93 44 | octopusking 94 45 | palmtree 98 46 | pighouse 105 47 | pigking 106 48 | pond 110 49 | pond_mos 111 50 | rabbithouse 114 51 | trap 115 52 | raft 116 53 | rainometer 117 54 | reeds 118 55 | researchlab1 120 56 | researchlab2 121 57 | rock1 126 58 | rock2 126 59 | rock_flintless 127 60 | rock_flintless_low 127 61 | rock_flintless_med 127 62 | magmarock 128 63 | magmarock_gold 128 64 | sapling 131 65 | seaweed_planted 133 66 | spiderden 138 67 | stalagmite_low 139 68 | stalagmite_tall_low 140 69 | stalagmite_tall 140 70 | statueglommer 142 71 | teleportato_base 149 72 | teleportato_sw_base 150 73 | tent 151 74 | tentacle_garden 152 75 | treasurechest 157 76 | deciduoustree 158 77 | wormhole 165 78 | -------------------------------------------------------------------------------- /lexer.cpp: -------------------------------------------------------------------------------- 1 | #include "lexer.h" 2 | #include 3 | #include 4 | 5 | Lexer::Lexer(const std::string & str): 6 | _str(str), 7 | 8 | _last_char(' '), 9 | _last_token(), 10 | _cursor(0), 11 | 12 | _identifier(), 13 | _value(0.f), 14 | 15 | _currentLine(0), 16 | _linePos(0) 17 | { 18 | } 19 | 20 | char Lexer::getchar() 21 | { 22 | if(_cursor < _str.length()) 23 | return _str[_cursor++]; 24 | return '\0'; 25 | } 26 | 27 | Token Lexer::get() 28 | { 29 | return _last_token = gettok(); 30 | } 31 | 32 | 33 | Token Lexer::gettok() 34 | { 35 | auto& last_char = _last_char; 36 | 37 | // Skip spaces 38 | while( isspace(last_char) || last_char == '\n' || last_char == '\t' ) 39 | { 40 | last_char = getchar(); 41 | } 42 | 43 | // Skip comments 44 | if(last_char == '/') 45 | { 46 | last_char = getchar(); 47 | // One line comment 48 | if(last_char == '/') 49 | { 50 | while( last_char != '\n' && last_char != '\0' ) 51 | last_char = getchar(); 52 | 53 | if(last_char != '\0') 54 | return gettok(); 55 | else 56 | return Token::Eof; 57 | } 58 | else 59 | // Multi lines comment 60 | if(last_char == '*') 61 | { 62 | do { 63 | while( (last_char = getchar()) != '*' ) 64 | { 65 | if(last_char == '\0') 66 | return Token::Eof; 67 | } 68 | } while( (last_char = getchar()) != '/' ); 69 | last_char = getchar(); 70 | return gettok(); 71 | } 72 | else 73 | _cursor--; 74 | } 75 | 76 | if(last_char == '{') 77 | { 78 | last_char = getchar(); 79 | return Token::LeftBrace; 80 | } 81 | if(last_char == '}') 82 | { 83 | last_char = getchar(); 84 | return Token::RightBrace; 85 | } 86 | if(last_char == '[') 87 | { 88 | last_char = getchar(); 89 | return Token::LeftBracket; 90 | } 91 | if(last_char == ']') 92 | { 93 | last_char = getchar(); 94 | return Token::RightBracket; 95 | } 96 | if(last_char == '=') 97 | { 98 | last_char = getchar(); 99 | return Token::Equal; 100 | } 101 | if(last_char == ',') 102 | { 103 | last_char = getchar(); 104 | return Token::Comma; 105 | } 106 | 107 | if(last_char == '"') 108 | { 109 | _identifier.clear(); 110 | 111 | while((last_char = getchar()) != '"') 112 | { 113 | if(last_char == '\\') 114 | { 115 | last_char = getchar(); 116 | if(last_char == '"') 117 | _identifier += '"'; 118 | else if(last_char == 'n') 119 | _identifier += '\n'; 120 | else 121 | { 122 | _identifier += '\\'; 123 | _identifier += last_char; 124 | } 125 | } 126 | _identifier += last_char; 127 | } 128 | last_char = getchar(); 129 | return Token::String; 130 | } 131 | 132 | // parse identifier 133 | // @todo ajouter boolean pour savoir s'il s'agit d'une fonction/expression lua... 134 | if(isalpha(last_char)) 135 | { 136 | _identifier = last_char; 137 | while(isalnum(last_char = getchar()) || last_char == '_') 138 | { 139 | _identifier += last_char; 140 | } 141 | 142 | if(last_char == '(') 143 | { 144 | _identifier += last_char; 145 | while( (last_char = getchar()) != ')') 146 | { 147 | if(last_char == '"') { 148 | _identifier += last_char; 149 | while( (last_char = getchar()) != '"') { 150 | _identifier += last_char; 151 | } 152 | } 153 | _identifier += last_char; 154 | } 155 | _identifier += last_char; 156 | last_char = getchar(); 157 | 158 | return Token::Expression; 159 | } 160 | 161 | if(_identifier == "true") 162 | { 163 | _value_int = 1; 164 | return Token::Boolean; 165 | } 166 | if(_identifier == "false") 167 | { 168 | _value_int = 0; 169 | return Token::Boolean; 170 | } 171 | return Token::Identifier; 172 | } 173 | 174 | if(isdigit(last_char) || last_char == '.' || last_char == '-') // + ? 175 | { 176 | bool is_float = last_char == '.'; 177 | 178 | std::string n; 179 | n += last_char; 180 | while(isdigit(last_char = getchar()) || last_char == '.' || last_char == 'e' || last_char == '-') // mouai, ameliorer pour le e-007 181 | { 182 | n += last_char; 183 | if(last_char == '.' || last_char == 'e') 184 | is_float = true; 185 | } 186 | 187 | if(!is_float) 188 | { 189 | //printf("%s %d\n", n.c_str(), atoi(n.c_str())); 190 | _value_int = atoi(n.c_str()); 191 | return Token::Integer; 192 | } 193 | 194 | _identifier = n; 195 | return Token::Expression; 196 | } 197 | return Token::Eof; 198 | } 199 | -------------------------------------------------------------------------------- /lexer.h: -------------------------------------------------------------------------------- 1 | #ifndef LEXER_H_INCLUDED 2 | #define LEXER_H_INCLUDED 3 | 4 | #include 5 | 6 | struct Token 7 | { 8 | enum Kind 9 | { 10 | Identifier, // str 11 | String, // "str" 12 | Float, // 123 | 12.3 13 | Integer, 14 | Boolean, // true | false 15 | Expression, 16 | LeftBrace, // { 17 | RightBrace, // } 18 | LeftBracket, // [ 19 | RightBracket,// ] 20 | Equal, // = 21 | Comma, // , 22 | Undefined, 23 | Eof 24 | }; 25 | Kind value; 26 | size_t line; 27 | size_t pos; 28 | 29 | Token():value(Undefined) {} 30 | Token(Kind value):value(value) {} 31 | 32 | operator Kind() const { return value; } 33 | }; 34 | 35 | class Lexer 36 | { 37 | const std::string & _str; 38 | 39 | char _last_char; 40 | Token _last_token; 41 | size_t _cursor; 42 | 43 | std::string _identifier; 44 | double _value; 45 | int _value_int; 46 | 47 | size_t _currentLine; 48 | size_t _linePos; 49 | 50 | char getchar(); 51 | Token gettok(); 52 | 53 | public: 54 | Lexer(const std::string & str); 55 | 56 | Token get(); 57 | 58 | const Token last() const { return _last_token; } 59 | const std::string identifier() const { return _identifier; } 60 | double value() const { return _value; } 61 | int valueInt() const { return _value_int; } 62 | 63 | size_t cursor() const { return _cursor; } 64 | }; 65 | 66 | 67 | #endif // LEXER_H_INCLUDED 68 | -------------------------------------------------------------------------------- /main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "save.h" 4 | 5 | int main(int argc, char** argv) 6 | { 7 | if(argc < 2) 8 | { 9 | printf("Usage:\nmapviewer savename\n"); 10 | return 1; 11 | } 12 | 13 | std::string path(argv[0]); 14 | int p1 = path.find_last_of('\\'); 15 | if(p1 == -1) p1 = path.find_last_of('/'); 16 | if(p1 != -1) 17 | path = path.substr(0, p1 + 1); 18 | 19 | std::string file; 20 | for(int i=1; i > entries; 26 | // read entries 27 | { 28 | std::ifstream f(path + "entries.txt"); 29 | 30 | if(f.is_open()) 31 | { 32 | std::string line; 33 | while(std::getline(f, line)) 34 | { 35 | std::istringstream iss(line); 36 | std::string str; 37 | int icon; 38 | iss >> str >> icon; 39 | entries.push_back( std::make_pair(str, icon) ); 40 | } 41 | } 42 | } 43 | 44 | SaveEditor save; 45 | save.deserialize(file); 46 | 47 | Table * tiles_table = save.table().find("map.tiles"); 48 | std::string tiles = base64_decode(tiles_table->value_string); 49 | std::cout << tiles.size() << '\n' << save.width() << 'x' << save.height() << '\n'; 50 | std::cout << 2 * save.width() * save.height() << '\n'; 51 | 52 | using namespace od; 53 | Window win(320, 240, "OniDev app"); 54 | win.setSynchronization(true); 55 | 56 | TexturePacker packer(path + "sprites", true); 57 | metatextureAdd("icons", new MetaTexture(packer)); 58 | metatextureFind("icons").texture().setFiltering(Texture::Linear, Texture::Linear); 59 | int spr_icons = spriteIndex("icons"); 60 | 61 | int size = 4; 62 | Surface surf(save.width() * size, save.height() * size); 63 | surf.setTarget(); 64 | glBegin(GL_QUADS); 65 | for(int i=0; i 3 | 4 | bool SaveEditor::deserialize(const std::string& fname, bool skip_data) 5 | { 6 | std::string encoded; 7 | { 8 | FILE * f = fopen(fname.c_str(), "rb"); 9 | if(!f) 10 | { 11 | //throw(std::runtime_error("Unable to open the file " + fname)); 12 | return false; 13 | } 14 | 15 | fseek(f, 0, SEEK_SET); 16 | int c; 17 | 18 | while( (c = fgetc(f)) != EOF ) 19 | { 20 | encoded += static_cast(c); 21 | } 22 | fclose(f); 23 | } 24 | 25 | encoded = encoded.substr(11); 26 | encoded = base64_decode(encoded); 27 | encoded = encoded.substr(16); 28 | encoded = decompress_string(encoded); 29 | encoded = encoded.substr(7); // "return " 30 | 31 | // { 32 | // FILE* f = fopen("out_raw", "wb"); 33 | // if(!f) 34 | // return false; 35 | // 36 | // for(size_t i=0; i= encoded.size()) 55 | // return 1; 56 | // } 57 | // converted += "\"\""; 58 | // continue; 59 | // } 60 | // 61 | // converted += c; 62 | // } 63 | // encoded.swap(converted); 64 | // } 65 | 66 | _table = Table::parseString(encoded); 67 | 68 | Table* t = _table.find("map.width"); 69 | if(t) _world_wid = t->value_int; 70 | 71 | t = _table.find("map.height"); 72 | if(t) _world_hei = t->value_int; 73 | 74 | return true; 75 | } 76 | 77 | bool SaveEditor::serialize(const std::string& fname) const 78 | { 79 | static const unsigned char head[] = { 80 | 1, 0, 0, 0, 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 81 | }; 82 | 83 | std::string encoded = "return " + _table.asString(); 84 | //printf("decompressed size %d\n", encoded.size()); 85 | int decoded_size = encoded.size(); 86 | encoded = std::string(head, head + 16) + compress_string(encoded); 87 | //printf("compressed size %d\n", encoded.size() - 16); 88 | int encoded_size = encoded.size() - 16; 89 | 90 | encoded[8 ] = (decoded_size >> 0 )&0xFF; 91 | encoded[9 ] = (decoded_size >> 8 )&0xFF; 92 | encoded[10] = (decoded_size >> 16)&0xFF; 93 | encoded[11] = (decoded_size >> 24)&0xFF; 94 | 95 | encoded[12] = (encoded_size >> 0 )&0xFF; 96 | encoded[13] = (encoded_size >> 8 )&0xFF; 97 | encoded[14] = (encoded_size >> 16)&0xFF; 98 | encoded[15] = (encoded_size >> 24)&0xFF; 99 | 100 | encoded = "KLEI 1D" + base64_encode(reinterpret_cast(encoded.data()), encoded.length()); 101 | 102 | { 103 | FILE* f = fopen(fname.c_str(), "wb"); 104 | if(!f) 105 | return false; 106 | 107 | for(size_t i=0; i(c); 130 | } 131 | fclose(f); 132 | } 133 | 134 | _table = Table::parseString(encoded); 135 | 136 | // Table* hunger = _table.find("playerinfo.data.hunger.hunger"); 137 | // 138 | // if(hunger) 139 | // { 140 | // printf("%s\n", hunger->value_string.c_str()); 141 | // *hunger = Table(150.f); 142 | // } 143 | 144 | 145 | 146 | return true; 147 | } 148 | 149 | bool SaveEditor::saveAsString(const std::string& fname, bool formatted) const 150 | { 151 | std::string encoded = formatted? _table.asStringFormatted(0) : _table.asString(); 152 | 153 | { 154 | FILE* f = fopen(fname.c_str(), "wb"); 155 | if(!f) 156 | return false; 157 | 158 | for(size_t i=0; i SaveEditor::getEntities(const std::string& name) 167 | { 168 | std::vector ret; 169 | 170 | Table* entities = _table.find("ents." + name); 171 | 172 | if(entities) 173 | { 174 | for(auto& e: entities->elements) 175 | { 176 | Table* x = e->value.find("x"); 177 | Table* z = e->value.find("z"); 178 | 179 | if(x && z) 180 | { 181 | float pos_x, pos_y; 182 | if(x->type == Table::Expression) 183 | pos_x = atof( x->value_string.c_str() ); 184 | else 185 | if(x->type == Table::Int) 186 | pos_x = x->value_int; 187 | else 188 | pos_x = x->value_float; 189 | 190 | if(z->type == Table::Expression) 191 | pos_y = atof( z->value_string.c_str() ); 192 | else 193 | if(z->type == Table::Int) 194 | pos_y = z->value_int; 195 | else 196 | pos_y = z->value_float; 197 | 198 | //printf("%f %f\n", pos_x, pos_y); 199 | 200 | ret.push_back( Entity{pos_x, pos_y, &e->value} ); 201 | } 202 | } 203 | } 204 | 205 | return ret; 206 | } 207 | 208 | -------------------------------------------------------------------------------- /save.h: -------------------------------------------------------------------------------- 1 | #ifndef SAVE_H_INCLUDED 2 | #define SAVE_H_INCLUDED 3 | 4 | #include 5 | #include 6 | #include "base64.h" 7 | #include "deflate.h" 8 | #include "table.h" 9 | 10 | struct Entity 11 | { 12 | float x; 13 | float y; 14 | 15 | Table* table; 16 | }; 17 | 18 | class SaveEditor 19 | { 20 | Table _table; 21 | 22 | float _world_wid; 23 | float _world_hei; 24 | 25 | public: 26 | bool deserialize(const std::string& fname, bool skip_data = false); 27 | bool serialize(const std::string& fname) const; 28 | 29 | bool loadFromString(const std::string& fname); 30 | bool saveAsString(const std::string& fname, bool formatted = false) const; 31 | 32 | float width () const { return _world_wid; } 33 | float height() const { return _world_hei; } 34 | 35 | const Table& table() const { return _table; } 36 | Table& table() { return _table; } 37 | 38 | std::vector getEntities(const std::string& name); 39 | }; 40 | 41 | #endif // SAVE_H_INCLUDED 42 | -------------------------------------------------------------------------------- /table.cpp: -------------------------------------------------------------------------------- 1 | #include "table.h" 2 | #include // a virer a terme 3 | #include // a virer a terme 4 | 5 | static std::string to_string(int x) 6 | { 7 | char buff[30]; 8 | sprintf(buff, "%d", x); 9 | return buff; 10 | } 11 | 12 | Table::Table(Table&& table): 13 | type(table.type), 14 | value_float(table.value_float) 15 | { 16 | std::swap(value_string, table.value_string); 17 | std::swap(elements, table.elements); 18 | } 19 | 20 | Table& Table::operator=(Table&& table) 21 | { 22 | type = table.type; 23 | value_float = table.value_float; 24 | std::swap(value_string, table.value_string); 25 | std::swap(elements, table.elements); 26 | return *this; 27 | } 28 | 29 | void Table::parse(Table& root, Lexer& lex) 30 | { 31 | if(lex.last() == Token::LeftBrace) 32 | { 33 | Table new_root; 34 | 35 | while(lex.get() != Token::RightBrace) 36 | { 37 | parse(new_root, lex); 38 | if(lex.last() != Token::Comma) { 39 | if(lex.last() == Token::RightBrace) 40 | break; 41 | else 42 | { 43 | printf("error (%d) %d\n", (int)lex.last(), lex.cursor()); /// throw 44 | exit(1); 45 | } 46 | } 47 | } 48 | 49 | if(root.type == Table::List) 50 | { 51 | root.elements.emplace_back( new TableElement{0, "", "", std::move(new_root)} ); 52 | } 53 | else if(root.type == Table::Empty) 54 | { 55 | root = std::move(new_root); 56 | root.type = Table::List; 57 | } 58 | } 59 | else if(lex.last() == Token::Float) 60 | { 61 | if(root.type == Table::List) 62 | { 63 | root.elements.emplace_back( new TableElement{0, "", "", Table(static_cast(lex.value()))} ); 64 | } 65 | else if(root.type == Table::Empty) 66 | { 67 | root.value_float = lex.value(); 68 | root.type = Table::Float; 69 | } 70 | } 71 | else if(lex.last() == Token::Integer) 72 | { 73 | if(root.type == Table::List) 74 | { 75 | root.elements.emplace_back( new TableElement{0, "", "", Table(static_cast(lex.valueInt()))} ); 76 | } 77 | else if(root.type == Table::Empty) 78 | { 79 | root.value_int = lex.valueInt(); 80 | root.type = Table::Int; 81 | } 82 | } 83 | else if(lex.last() == Token::Boolean) 84 | { 85 | if(root.type == Table::List) 86 | { 87 | root.elements.emplace_back( new TableElement{0, "", "", Table(static_cast(lex.valueInt()))} ); 88 | } 89 | else if(root.type == Table::Empty) 90 | { 91 | root.value_int = lex.valueInt(); 92 | root.type = Table::Boolean; 93 | } 94 | } 95 | else if(lex.last() == Token::Expression) 96 | { 97 | if(root.type == Table::List) 98 | { 99 | Table table(lex.identifier()); 100 | table.type = Table::Expression; 101 | root.elements.emplace_back( new TableElement{0, "", "", std::move(table)} ); 102 | } 103 | else if(root.type == Table::Empty) 104 | { 105 | root.value_string = lex.identifier(); 106 | root.type = Table::Expression; 107 | } 108 | } 109 | else if(lex.last() == Token::String) 110 | { 111 | if(root.type == Table::List) 112 | { 113 | root.elements.emplace_back( new TableElement{0, "", "", Table(lex.identifier())} ); 114 | } 115 | else if(root.type == Table::Empty) 116 | { 117 | root.value_string = lex.identifier(); 118 | root.type = Table::String; 119 | } 120 | } 121 | else if(lex.last() == Token::Identifier) 122 | { 123 | std::string key = lex.identifier(); 124 | if(lex.get() != Token::Equal) 125 | { 126 | throw(std::runtime_error("at position " + to_string(lex.cursor()) + " '=' expected (2)")); 127 | } 128 | lex.get(); 129 | 130 | if(lex.last() == Token::Float) 131 | { 132 | root.elements.emplace_back( new TableElement{0, "", key, Table(static_cast(lex.value()))} ); 133 | } 134 | else if(lex.last() == Token::Integer) 135 | { 136 | root.elements.emplace_back( new TableElement{0, "", key, Table(static_cast(lex.valueInt()))} ); 137 | } 138 | else if(lex.last() == Token::Boolean) 139 | { 140 | root.elements.emplace_back( new TableElement{0, "", key, Table(static_cast(lex.valueInt()))} ); 141 | } 142 | else if(lex.last() == Token::Expression) 143 | { 144 | Table table(lex.identifier()); 145 | table.type = Table::Expression; 146 | root.elements.emplace_back( new TableElement{0, "", key, std::move(table)} ); 147 | } 148 | else 149 | { 150 | Table table(Empty); 151 | Table::parse(table, lex); 152 | root.elements.emplace_back( new TableElement{0, "", key, std::move(table)} ); 153 | return; 154 | } 155 | } 156 | else if(lex.last() == Token::LeftBracket) 157 | { 158 | lex.get(); 159 | 160 | int index = 0; 161 | std::string index_str; 162 | 163 | if(lex.last() == Token::Float) 164 | index = lex.value(); 165 | else if(lex.last() == Token::Integer || lex.last() == Token::Boolean) 166 | index = lex.valueInt(); 167 | else if(lex.last() == Token::String) 168 | index_str = lex.identifier(); 169 | else 170 | { 171 | printf("(%d) ", (int)lex.last()); 172 | throw(std::runtime_error("at position " + to_string(lex.cursor()) + " [index] expect a value")); 173 | } 174 | 175 | if(lex.get() != Token::RightBracket) 176 | { 177 | throw(std::runtime_error("at position " + to_string(lex.cursor()) + " ']' expected")); 178 | } 179 | if(lex.get() != Token::Equal) 180 | { 181 | throw(std::runtime_error("at position " + to_string(lex.cursor()) + " '=' expected")); 182 | } 183 | 184 | lex.get(); 185 | 186 | if(lex.last() == Token::Float) 187 | { 188 | root.elements.emplace_back( new TableElement{index, index_str, "", Table(static_cast(lex.value()))} ); 189 | } 190 | else if(lex.last() == Token::Integer) 191 | { 192 | root.elements.emplace_back( new TableElement{index, index_str, "", Table(static_cast(lex.valueInt()))} ); 193 | } 194 | else if(lex.last() == Token::Boolean) 195 | { 196 | root.elements.emplace_back( new TableElement{index, index_str, "", Table(static_cast(lex.valueInt()))} ); 197 | } 198 | else if(lex.last() == Token::Expression) 199 | { 200 | Table table(lex.identifier()); 201 | table.type = Table::Expression; 202 | root.elements.emplace_back( new TableElement{index, index_str, "", std::move(table)} ); 203 | } 204 | else 205 | { 206 | Table table(Empty); 207 | Table::parse(table, lex); 208 | root.elements.emplace_back( new TableElement{index, index_str, "", std::move(table)} ); 209 | return; 210 | } 211 | } 212 | 213 | lex.get(); 214 | } 215 | 216 | Table Table::parseString(const std::string& str) 217 | { 218 | Table root(Table::Empty); 219 | Lexer lex(str); 220 | lex.get(); 221 | parse(root, lex); 222 | return root; 223 | } 224 | 225 | std::string Table::asString() const 226 | { 227 | if(type == Float) 228 | { 229 | char buff[30]; 230 | 231 | if(static_cast(value_float) == value_float) 232 | { 233 | sprintf(buff, "%d", static_cast(value_float)); 234 | return buff; 235 | } 236 | else 237 | if(value_float < 0.1 && value_float > -0.1) 238 | { 239 | sprintf(buff, "%.13e", value_float); 240 | return buff; 241 | } 242 | else 243 | { 244 | if(value_float < 1.0) 245 | { 246 | sprintf(buff, "%.15f", value_float); 247 | std::string ret(buff); 248 | if(ret.size() > 16) 249 | ret.resize(16); 250 | while(ret.back() == '0') 251 | ret.pop_back(); 252 | return ret; 253 | } 254 | else 255 | { 256 | sprintf(buff, "%.14f", value_float); 257 | std::string ret(buff); 258 | if(ret.size() > 15) 259 | ret.resize(15); 260 | while(ret.back() == '0') 261 | ret.pop_back(); 262 | return ret; 263 | } 264 | } 265 | } 266 | else if(type == Int) 267 | { 268 | char buff[30]; 269 | sprintf(buff, "%d", value_int); 270 | return buff; 271 | } 272 | else if(type == Boolean) 273 | { 274 | if(value_int == 0) 275 | return "false"; 276 | return "true"; 277 | } 278 | 279 | if(type == Expression) { 280 | return value_string; 281 | } 282 | if(type == String) { 283 | return "\"" + value_string + "\""; 284 | } 285 | 286 | if(type == List) { 287 | std::string ret; 288 | ret += '{'; 289 | 290 | for(auto it=elements.begin(); it!=elements.end(); ++it) 291 | { 292 | auto& e = **it; 293 | if(!e.key.empty()) 294 | { 295 | ret += e.key + '=' + e.value.asString(); 296 | } 297 | else if(!e.index_str.empty()) 298 | { 299 | ret += "[\"" + e.index_str + "\"]=" + e.value.asString(); 300 | } 301 | else if(e.index != 0) 302 | { 303 | char buff[30]; 304 | sprintf(buff, "[%d]=", e.index); 305 | ret += std::string(buff) + e.value.asString(); 306 | } 307 | else 308 | { 309 | ret += e.value.asString(); 310 | } 311 | 312 | auto it2 = it; 313 | if(++it2 != elements.end()) 314 | ret += ','; 315 | } 316 | ret += '}'; 317 | return ret; 318 | } 319 | 320 | return std::string(); 321 | } 322 | 323 | std::string Table::asStringFormatted(int indent) const 324 | { 325 | if(type == Float) { 326 | char buff[30]; 327 | 328 | if(static_cast(value_float) == value_float) 329 | { 330 | sprintf(buff, "%d", static_cast(value_float)); 331 | return buff; 332 | } 333 | else 334 | if(value_float < 0.1 && value_float > -0.1) 335 | { 336 | sprintf(buff, "%.13e", value_float); 337 | return buff; 338 | } 339 | else 340 | { 341 | if(value_float < 1.0) 342 | { 343 | sprintf(buff, "%.15f", value_float); 344 | std::string ret(buff); 345 | if(ret.size() > 16) 346 | ret.resize(16); 347 | while(ret.back() == '0') 348 | ret.pop_back(); 349 | return ret; 350 | } 351 | else 352 | { 353 | sprintf(buff, "%.14f", value_float); 354 | std::string ret(buff); 355 | if(ret.size() > 15) 356 | ret.resize(15); 357 | while(ret.back() == '0') 358 | ret.pop_back(); 359 | return ret; 360 | } 361 | } 362 | } 363 | else if(type == Int) 364 | { 365 | char buff[30]; 366 | sprintf(buff, "%d", value_int); 367 | return buff; 368 | } 369 | else if(type == Boolean) 370 | { 371 | if(value_int == 0) 372 | return "false"; 373 | return "true"; 374 | } 375 | 376 | if(type == Expression) { 377 | return value_string; 378 | } 379 | if(type == String) { 380 | return "\"" + value_string + "\""; 381 | } 382 | 383 | if(type == List) { 384 | std::string ret("\n"); 385 | 386 | for(int i=0; ielements) 446 | { 447 | if(e->key == key) 448 | { 449 | //printf("%s.", key.c_str()); 450 | find = true; 451 | ret = &e->value; 452 | break; 453 | } 454 | //else printf("%s\n", e->key.c_str()); 455 | } 456 | if(!find) 457 | return nullptr; 458 | } 459 | 460 | return ret; 461 | } 462 | 463 | std::vector split(const std::string &s, char delim) 464 | { 465 | std::stringstream ss(s); 466 | std::string item; 467 | std::vector elems; 468 | while (std::getline(ss, item, delim)) { 469 | elems.push_back(item); 470 | } 471 | return elems; 472 | } 473 | 474 | 475 | -------------------------------------------------------------------------------- /table.h: -------------------------------------------------------------------------------- 1 | #ifndef TABLE_H_INCLUDED 2 | #define TABLE_H_INCLUDED 3 | 4 | #include "lexer.h" 5 | 6 | #include 7 | #include 8 | #include 9 | 10 | struct TableElement; 11 | 12 | struct Table 13 | { 14 | enum Type { 15 | Empty, 16 | List, 17 | Float, 18 | Int, 19 | Boolean, 20 | Expression, 21 | String 22 | }; 23 | 24 | // Indique le type de la table 25 | Type type; 26 | 27 | // Si juste valeur 28 | double value_float = 0.f; 29 | int value_int = 0; 30 | std::string value_string; 31 | 32 | std::vector< std::unique_ptr > elements; 33 | 34 | Table():type(List){} 35 | Table(Type type):type(type){} 36 | Table(double x):type(Float),value_float(x){} 37 | Table(int x):type(Int),value_int(x){} 38 | Table(bool x):type(Boolean),value_int(x){} 39 | Table(const std::string& str):type(String),value_string(str){} 40 | 41 | Table(Table&& table); 42 | Table& operator=(Table&& table); 43 | 44 | static void parse(Table& root, Lexer& lex); 45 | static Table parseString(const std::string& str); 46 | std::string asString() const; 47 | std::string asStringFormatted(int indent) const; 48 | 49 | Table* find(const std::string& keys); //a.b.c 50 | }; 51 | 52 | struct TableElement 53 | { 54 | int index; 55 | std::string index_str; 56 | std::string key; 57 | Table value; 58 | }; 59 | 60 | std::vector split(const std::string &s, char delim); 61 | 62 | #endif // TABLE_H_INCLUDED 63 | --------------------------------------------------------------------------------