/* by korenkonder GitHub/GitLab: korenkonder */ #include "x_save.hpp" #include "f2/header.hpp" #include "io/file_stream.hpp" #include "aes.hpp" #include "deflate.hpp" static const uint8_t savedata_key[] = { 0xA4, 0xB7, 0x31, 0xD0, 0x33, 0xFB, 0x4A, 0x63, 0x9D, 0xD2, 0x46, 0xA5, 0x05, 0xCD, 0x4B, 0xE5, 0xF2, 0x10, 0xEE, 0x05, 0xC3, 0x56, 0x45, 0x3A, 0xAF, 0x22, 0x5C, 0x88, 0xA0, 0x9F, 0xB6, 0x8A, }; static const uint32_t savedata_version = 0xE589B8; static bool x_save_decode(std::vector& data, void*& dec, size_t& dec_len); static bool x_save_encode(std::vector& data, void*& enc, x_save_encode_flags flags); bool x_save_decode(const char* in_path, const char* out_path) { std::vector data; { file_stream ifs; ifs.open(in_path, "rb"); if (ifs.check_null() || ifs.get_length() <= F2_HEADER_DEFAULT_LENGTH) return false; data.resize(ifs.get_length()); ifs.read(data.data(), data.size()); ifs.close(); } void* dec = 0; size_t dec_len = 0; if (!x_save_decode(data, dec, dec_len)) return false; if (dec && dec_len) { file_stream ofs; ofs.open(out_path, "wb"); ofs.write(dec, dec_len); ofs.close(); } if (dec) free(dec); return true; } bool x_save_decode(const wchar_t* in_path, const wchar_t* out_path) { std::vector data; { file_stream ifs; ifs.open(in_path, L"rb"); if (ifs.check_null() || ifs.get_length() <= F2_HEADER_DEFAULT_LENGTH) return false; data.resize(ifs.get_length()); ifs.read(data.data(), data.size()); ifs.close(); } void* dec = 0; size_t dec_len = 0; if (!x_save_decode(data, dec, dec_len)) return false; if (dec && dec_len) { file_stream ofs; ofs.open(out_path, L"wb"); ofs.write(dec, dec_len); ofs.close(); } if (dec) free(dec); return true; } bool x_save_encode(const char* in_path, const char* out_path, x_save_encode_flags flags) { std::vector data; { file_stream ifs; ifs.open(in_path, "rb"); if (ifs.check_null()) return false; data.resize(ifs.get_length()); ifs.read(data.data(), data.size()); ifs.close(); } void* enc; if (!x_save_encode(data, enc, flags)) return false; if (enc) { f2_header* head = (f2_header*)enc; file_stream ofs; ofs.open(out_path, "wb"); ofs.write(enc, (size_t)head->get_length() + head->get_data_size()); ofs.close(); } if (enc) free(enc); return true; } bool x_save_encode(const wchar_t* in_path, const wchar_t* out_path, x_save_encode_flags flags) { std::vector data; { file_stream ifs; ifs.open(in_path, L"rb"); if (ifs.check_null()) return false; data.resize(ifs.get_length()); ifs.read(data.data(), data.size()); ifs.close(); } void* enc; if (!x_save_encode(data, enc, flags)) return false; if (enc) { f2_header* head = (f2_header*)enc; file_stream ofs; ofs.open(out_path, L"wb"); ofs.write(enc, (size_t)head->get_length() + head->get_data_size()); ofs.close(); } if (enc) free(enc); return true; } static bool x_save_decode(std::vector& data, void*& dec, size_t& dec_len) { f2_header* head = (f2_header*)data.data(); bool reset_flags = head->attrib.get_gzip() || head->attrib.get_xor_data(); uint32_t version = 0; if (head) version = head->version; if (version != (savedata_version & 0x7FFFFFF)) return false; if (head->attrib.get_crc()) { if (!head->validate_crc()) return false; if (reset_flags) head->reset_crc(); } if (head->attrib.get_xor_data()) head->remove_xor(); if (head->attrib.get_aes()) { uint8_t* section_data = head->get_section_data(); uint32_t section_size = head->get_section_size(); if (section_data && section_size == align_val(section_size, 0x20)) { aes256_ctx aes; aes256_init_ctx(&aes, savedata_key); aes256_ecb_decrypt_buffer(&aes, section_data, section_size); head->attrib.set_aes(false); } } if (head->attrib.get_gzip()) { uint8_t* section_data = head->get_section_data(); uint32_t section_size = head->get_section_size(); if (!head->custom || !section_data || !section_size) return false; dec = 0; dec_len = head->custom; if (deflate::decompress(section_data, section_size, dec, dec_len, deflate::MODE_GZIP) < 0 || !dec_len || dec_len < head->custom) { if (dec) free(dec); return false; } if (reset_flags) head->attrib.set_gzip(false); } else { dec = malloc(head->custom); if (dec) { dec_len = head->custom; memset(dec, 0, head->custom); memcpy(dec, head->get_section_data(), min_def(head->custom, head->get_section_size())); } } return true; } static bool x_save_encode(std::vector& data, void*& enc, x_save_encode_flags flags) { size_t max_enc_len = align_val(data.size(), 0x20) + F2_HEADER_EXTENDED_LENGTH; enc = malloc(max_enc_len); if (!enc || max_enc_len <= F2_HEADER_EXTENDED_LENGTH) { if (enc) free(enc); return false; } memset(enc, 0, max_enc_len); f2_header* head = new (enc) f2_header('DATA', (uint32_t)(max_enc_len - F2_HEADER_EXTENDED_LENGTH), 0, true); uint32_t size = (uint32_t)data.size(); if (head) head->custom = size; if (head) head->version = savedata_version & 0x7FFFFFF; if (flags & X_SAVE_ENCODE_GZIP) { void* comp = 0; size_t comp_len = 0; if (deflate::compress(data.data(), data.size(), comp, comp_len, deflate::MODE_GZIP) < 0 || !comp_len || head->get_section_size() < comp_len) { if (comp) free(comp); if (size > head->get_section_size()) { if (enc) free(enc); return false; } memcpy(head->get_section_data(), data.data(), size); } else { memcpy(head->get_section_data(), comp, comp_len); if (comp) free(comp); uint32_t comp_size = align_val((uint32_t)comp_len, 0x20); head->set_section_size(comp_size); head->set_data_size(comp_size); head->attrib.set_gzip(true); } } else { if (size > head->get_section_size()) { if (enc) free(enc); return false; } memcpy(head->get_section_data(), data.data(), size); } if (flags & X_SAVE_ENCODE_AES) { uint8_t* section_data = head->get_section_data(); uint32_t section_size = head->get_section_size(); if (section_data && section_size == align_val(section_size, 0x20)) { aes256_ctx aes; aes256_init_ctx(&aes, savedata_key); aes256_ecb_encrypt_buffer(&aes, section_data, section_size); head->attrib.set_aes(true); } } if (flags & X_SAVE_ENCODE_XOR) head->apply_xor(); if (flags & X_SAVE_ENCODE_CRC) head->calc_crc(); return true; }