#ifndef PKMNLIB_IPKMNBINARYSTREAM_HPP #define PKMNLIB_IPKMNBINARYSTREAM_HPP #include #include #include #include class IPkmnBinaryStream : public asIBinaryStream { protected: size_t _angelScriptBound; IPkmnBinaryStream(size_t angelScriptBound) : _angelScriptBound(angelScriptBound) {} public: virtual void WriteTypes( const ArbUt::Dictionary>& types, const ArbUt::Dictionary& itemUseTypes, const ArbUt::Dictionary& evolutionTypes) { // We serialize our types in the format // "[category(byte)][name(str)]\2[decl(str)]\2[name(str)]\2[decl(str)]\1[category(byte)]...." i16 categoryArr[1]; for (const auto& dic : types) { // Write the category categoryArr[0] = (i16)dic.first; Write(categoryArr, sizeof(i16)); for (const auto& inner : dic.second) { // Write the script name Write(inner.first.c_str(), sizeof(char) * inner.first.Length()); // Write the divider Write("\2", sizeof(char)); // Write the declaration of the script auto decl = inner.second->GetDecl(); Write(decl, sizeof(char) * strlen(decl)); // Write another divider. Write("\2", sizeof(char)); } // Write the divider between categories. Write("\1", sizeof(char)); } categoryArr[0] = (i16)-1; Write(categoryArr, sizeof(i16)); for (const auto& inner : itemUseTypes) { // Write the script name Write(inner.first.c_str(), sizeof(char) * inner.first.Length()); // Write the divider Write("\2", sizeof(char)); // Write the declaration of the script auto decl = inner.second->GetName(); Write(decl, sizeof(char) * strlen(decl)); // Write another divider. Write("\2", sizeof(char)); } // Write the divider between categories. Write("\1", sizeof(char)); categoryArr[0] = (i16)-2; Write(categoryArr, sizeof(i16)); for (const auto& inner : evolutionTypes) { // Write the script name Write(inner.first.c_str(), sizeof(char) * inner.first.Length()); // Write the divider Write("\2", sizeof(char)); // Write the declaration of the script auto decl = inner.second->GetName(); Write(decl, sizeof(char) * strlen(decl)); // Write another divider. Write("\2", sizeof(char)); } // Write the divider between categories. Write("\1", sizeof(char)); } virtual ArbUt::Dictionary> ReadTypes() { _angelScriptBound = SIZE_MAX; ArbUt::Dictionary> types; i16 categoryArr[1]; while (true) { // Every inner database starts with the category, of known size. Read that. auto read = Read(categoryArr, sizeof(i16)); // If we haven't read anything, we are finished. if (read == 0) { break; } ArbUt::Dictionary innerDb; // We don't know the sizes of the name and decl. Allocate 128 characters for them, as that should be enough. char name[128]; char decl[128]; size_t pos = 0; bool isDecl = false; while (true) { // Keep reading characters char cArr[1]; Read(cArr, sizeof(char)); auto c = cArr[0]; // If we find a '\1' separator if (c == '\1') { // and if we were reading the decl if (isDecl) { // Insert the name and decl into the dictionary. Close off the decl with eof as well. decl[pos] = '\0'; innerDb.Set(ArbUt::StringView(name), ArbUt::StringView::CalculateHash(decl)); } // If we have found \1, we are done with the current category, so break. break; } // If we find a '\2' separator, we need to toggle between writing to name and writing to decl. If we // were writing a decl, also insert the name and decl into the dictionary. if (c == '\2') { if (isDecl) { // Insert the name and decl into the dictionary. Close off the decl with eof as well. decl[pos] = '\0'; innerDb.Set(ArbUt::StringView(name), ArbUt::StringView::CalculateHash(decl)); // Reset our position and toggle back to name. pos = 0; isDecl = false; continue; } else { // Close of the name with eof, reset position and toggle to decl. name[pos] = '\0'; pos = 0; isDecl = true; continue; } } else { // If we haven't found any control character, just add the character to the thing we are writing to, // and increment the position. if (isDecl) { decl[pos++] = c; } else { name[pos++] = c; } } } types.Set(categoryArr[0], innerDb); } return types; } }; #endif // PKMNLIB_IPKMNBINARYSTREAM_HPP