mirror of
				https://github.com/TES3MP/openmw-tes3mp.git
				synced 2025-10-31 20:26:48 +00:00 
			
		
		
		
	Can now save strings with proper encoding, byte-perfect clones up until land records
This commit is contained in:
		
							parent
							
								
									f16a9ce5ed
								
							
						
					
					
						commit
						a74aeace73
					
				
					 35 changed files with 570 additions and 134 deletions
				
			
		|  | @ -282,7 +282,9 @@ int load(Arguments& info) | |||
|         while(esm.hasMoreRecs()) | ||||
|         { | ||||
|             NAME n = esm.getRecName(); | ||||
|             esm.getRecHeader(); | ||||
|             uint32_t flags; | ||||
|             esm.getRecHeader(flags); | ||||
| 
 | ||||
|             string id = esm.getHNOString("NAME"); | ||||
|             if(!quiet) | ||||
|                 cout << "\nRecord: " << n.toString() | ||||
|  | @ -726,6 +728,8 @@ int load(Arguments& info) | |||
|             { | ||||
|                 rec->setId(id); | ||||
| 
 | ||||
|                 rec->setFlags((int)flags); | ||||
| 
 | ||||
|                 if (save) | ||||
|                     info.data.records.push_back(rec); | ||||
|                 else | ||||
|  | @ -803,6 +807,7 @@ int clone(Arguments& info) | |||
|     cout << endl << "Saving records to: " << info.outname << "..." << endl; | ||||
| 
 | ||||
|     ESMWriter esm; | ||||
|     esm.setEncoding(info.encoding); | ||||
|     esm.setAuthor(info.data.author); | ||||
|     esm.setDescription(info.data.description); | ||||
|     esm.setVersion(info.data.version); | ||||
|  | @ -822,13 +827,22 @@ int clone(Arguments& info) | |||
|         NAME n; | ||||
|         n.val = rec->getName(); | ||||
| 
 | ||||
|         esm.startRecord(n.toString(), 0); | ||||
|         esm.startRecord(n.toString(), rec->getFlags()); | ||||
|         string id = rec->getId(); | ||||
| 
 | ||||
|         if (n.val == REC_GLOB || n.val == REC_CLAS || n.val == REC_FACT || n.val == REC_RACE) | ||||
|         if (n.val == REC_GLOB || n.val == REC_CLAS || n.val == REC_FACT || n.val == REC_RACE || n.val == REC_SOUN  | ||||
|             || n.val == REC_REGN || n.val == REC_BSGN || n.val == REC_LTEX || n.val == REC_STAT || n.val == REC_DOOR | ||||
|             || n.val == REC_MISC || n.val == REC_WEAP || n.val == REC_CONT || n.val == REC_SPEL || n.val == REC_CREA | ||||
|             || n.val == REC_BODY || n.val == REC_LIGH || n.val == REC_ENCH || n.val == REC_NPC_ || n.val == REC_ARMO | ||||
|             || n.val == REC_CLOT || n.val == REC_REPA || n.val == REC_ACTI || n.val == REC_APPA || n.val == REC_LOCK | ||||
|             || n.val == REC_PROB || n.val == REC_INGR || n.val == REC_BOOK || n.val == REC_ALCH || n.val == REC_LEVI | ||||
|             || n.val == REC_LEVC) | ||||
|             esm.writeHNCString("NAME", id); | ||||
|         else if (n.val == REC_CELL) | ||||
|             esm.writeHNString("NAME", id); | ||||
|         else | ||||
|             esm.writeHNOString("NAME", id); | ||||
| 
 | ||||
|         rec->save(esm); | ||||
| 
 | ||||
|         if (n.val == REC_CELL && !info.data.cellRefs[rec].empty()) | ||||
|  |  | |||
|  | @ -51,7 +51,7 @@ struct SpellList | |||
|     void save(ESMWriter &esm) | ||||
|     { | ||||
|         for (std::vector<std::string>::iterator it = list.begin(); it != list.end(); ++it) | ||||
|             esm.writeHNString("NPCS", *it); | ||||
|             esm.writeHNString("NPCS", *it, 32); | ||||
|     } | ||||
| }; | ||||
| 
 | ||||
|  | @ -83,6 +83,139 @@ struct ENAMstruct | |||
| 
 | ||||
|     // Struct size should be 24 bytes
 | ||||
| }; | ||||
| 
 | ||||
| struct AIDTstruct | ||||
| { | ||||
|     // These are probabilities
 | ||||
|     char hello, u1, fight, flee, alarm, u2, u3, u4; | ||||
|     // The last u's might be the skills that this NPC can train you
 | ||||
|     // in?
 | ||||
|     int services; // See the Services enum
 | ||||
| };  // 12 bytes
 | ||||
| 
 | ||||
| struct DODTstruct | ||||
| { | ||||
|     float pos[3]; | ||||
|     float rot[3]; | ||||
| }; | ||||
| 
 | ||||
| struct AI_Package | ||||
| { | ||||
|     void load(ESMReader& esm) | ||||
|     { | ||||
|         getData(esm); | ||||
|         cndt = esm.getHNOString("CNDT"); | ||||
|     } | ||||
| 
 | ||||
|     void save(ESMWriter& esm) | ||||
|     { | ||||
|         esm.startSubRecord(getName()); | ||||
|         saveData(esm); | ||||
|         esm.endRecord(getName()); | ||||
| 
 | ||||
|         esm.writeHNOCString("CNDT", cndt); | ||||
|     } | ||||
| 
 | ||||
|     std::string cndt; | ||||
| 
 | ||||
|     virtual void getData(ESMReader&) = 0; | ||||
|     virtual void saveData(ESMWriter&) = 0; | ||||
| 
 | ||||
|     virtual std::string getName() const = 0; | ||||
|     virtual int size() const = 0; | ||||
| }; | ||||
| 
 | ||||
| struct AI_Wstruct : AI_Package | ||||
| { | ||||
|     struct Data | ||||
|     { | ||||
|         short distance, duration; | ||||
|         char timeofday; | ||||
|         char idle[8]; | ||||
|         char unknown; | ||||
|     }; | ||||
| 
 | ||||
|     Data data; | ||||
| 
 | ||||
|     void getData(ESMReader& esm) { esm.getHExact(&data, sizeof(data)); } | ||||
|     void saveData(ESMWriter& esm) { esm.writeT(data); } | ||||
| 
 | ||||
|     std::string getName() const { return "AI_W"; } | ||||
|     int size() const { return sizeof(AI_Wstruct); } | ||||
| }; | ||||
| 
 | ||||
| struct AI_Tstruct : AI_Package | ||||
| { | ||||
|     struct Data | ||||
|     { | ||||
|         float pos[3]; | ||||
|         int unknown; | ||||
|     }; | ||||
| 
 | ||||
|     Data data; | ||||
| 
 | ||||
|     void getData(ESMReader& esm) { esm.getHExact(&data, sizeof(data)); } | ||||
|     void saveData(ESMWriter& esm) { esm.writeT(data); } | ||||
| 
 | ||||
|     std::string getName() const { return "AI_T"; } | ||||
|     int size() const { return sizeof(AI_Tstruct); } | ||||
| }; | ||||
| 
 | ||||
| struct AI_Fstruct : AI_Package | ||||
| { | ||||
|     struct Data | ||||
|     { | ||||
|         float pos[3]; | ||||
|         short duration; | ||||
|         NAME32 id; | ||||
|         short unknown; | ||||
|     }; | ||||
| 
 | ||||
|     Data data; | ||||
|      | ||||
|     void getData(ESMReader& esm) { esm.getHExact(&data, sizeof(data)); } | ||||
|     void saveData(ESMWriter& esm) { esm.writeT(data); } | ||||
| 
 | ||||
|     std::string getName() const { return "AI_F"; } | ||||
|     int size() const { return sizeof(AI_Fstruct); } | ||||
| }; | ||||
| 
 | ||||
| struct AI_Estruct : AI_Package | ||||
| { | ||||
|     struct Data | ||||
|     { | ||||
|         float pos[3]; | ||||
|         short duration; | ||||
|         NAME32 id; | ||||
|         short unknown; | ||||
|     }; | ||||
| 
 | ||||
|     Data data; | ||||
| 
 | ||||
|     void getData(ESMReader& esm) { esm.getHExact(&data, sizeof(data)); } | ||||
|     void saveData(ESMWriter& esm) { esm.writeT(data); } | ||||
| 
 | ||||
|     std::string getName() const { return "AI_E"; } | ||||
|     int size() const { return sizeof(AI_Estruct); } | ||||
| }; | ||||
| 
 | ||||
| struct AI_Astruct : AI_Package | ||||
| { | ||||
|     struct Data | ||||
|     { | ||||
|         NAME32 name; | ||||
|         char unknown; | ||||
|     }; | ||||
| 
 | ||||
|     Data data; | ||||
| 
 | ||||
|     void getData(ESMReader& esm) { esm.getHExact(&data, sizeof(data)); } | ||||
|     void saveData(ESMWriter& esm) { esm.writeT(data); } | ||||
| 
 | ||||
|     std::string getName() const { return "AI_A"; } | ||||
|     int size() const { return sizeof(AI_Astruct); } | ||||
| }; | ||||
| 
 | ||||
| #pragma pack(pop) | ||||
| 
 | ||||
| struct EffectList | ||||
|  | @ -107,5 +240,86 @@ struct EffectList | |||
|     } | ||||
| }; | ||||
| 
 | ||||
| struct AIData | ||||
| { | ||||
|     struct Travelstruct | ||||
|     { | ||||
|         DODTstruct dodt; | ||||
|         std::string dnam; | ||||
|     }; | ||||
| 
 | ||||
|     AIDTstruct aidt; | ||||
|     bool hasAI; | ||||
|     std::vector<AI_Package*> packages; | ||||
|     std::vector<Travelstruct> travel; | ||||
|      | ||||
|     void load(ESMReader &esm) | ||||
|     { | ||||
|         if (esm.isNextSub("AIDT")) | ||||
|         { | ||||
|             esm.getHExact(&aidt, sizeof(aidt)); | ||||
|             hasAI = true; | ||||
|         } | ||||
|         else | ||||
|             hasAI = false; | ||||
| 
 | ||||
| #define LOAD_IF_FOUND(x)                        \ | ||||
|         if (esm.isNextSub(#x))                  \ | ||||
|         {                                       \ | ||||
|             found = true;                       \ | ||||
|             x##struct *t = new x##struct();     \ | ||||
|             t->load(esm);                       \ | ||||
|             packages.push_back(t);              \ | ||||
|         } | ||||
|          | ||||
|         bool found = true; | ||||
|         while (esm.hasMoreSubs() && found) | ||||
|         { | ||||
|             found = false; | ||||
| 
 | ||||
|             if (esm.isNextSub("DODT")) | ||||
|             { | ||||
|                 found = true; | ||||
|                 Travelstruct t; | ||||
|                 esm.getHExact(&t.dodt, sizeof(t.dodt)); | ||||
|                 t.dnam = esm.getHNOString("DNAM"); | ||||
|                 travel.push_back(t); | ||||
|             } | ||||
| 
 | ||||
|             LOAD_IF_FOUND(AI_W); | ||||
|             LOAD_IF_FOUND(AI_T); | ||||
|             LOAD_IF_FOUND(AI_F); | ||||
|             LOAD_IF_FOUND(AI_E); | ||||
|             LOAD_IF_FOUND(AI_A); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     void save(ESMWriter &esm) | ||||
|     { | ||||
|         if (hasAI) | ||||
|             esm.writeHNT("AIDT", aidt); | ||||
| 
 | ||||
|         for (std::vector<Travelstruct>::iterator it = travel.begin(); it != travel.end(); ++it)    | ||||
|         { | ||||
|             esm.writeHNT("DODT", it->dodt); | ||||
|             esm.writeHNOCString("DNAM", it->dnam); | ||||
|         } | ||||
| 
 | ||||
|         for (std::vector<AI_Package*>::iterator it = packages.begin(); it != packages.end(); ++it) | ||||
|         { | ||||
|             (*it)->save(esm); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     ~AIData() | ||||
|     { | ||||
|         for (std::vector<AI_Package*>::iterator it = packages.begin(); it != packages.end();) | ||||
|         { | ||||
|             delete *it; | ||||
|             packages.erase(it++); | ||||
|         } | ||||
|     } | ||||
| }; | ||||
| 
 | ||||
| } | ||||
| #endif | ||||
|  |  | |||
|  | @ -136,7 +136,7 @@ void ESMWriter::writeHNString(const std::string& name, const std::string& data) | |||
|     endRecord(name); | ||||
| } | ||||
| 
 | ||||
|     void ESMWriter::writeHNString(const std::string& name, const std::string& data, int size) | ||||
| void ESMWriter::writeHNString(const std::string& name, const std::string& data, int size) | ||||
| { | ||||
|     assert(data.size() <= size); | ||||
|     startSubRecord(name); | ||||
|  | @ -156,7 +156,23 @@ void ESMWriter::writeHString(const std::string& data) | |||
|     if (data.size() == 0) | ||||
|         write("\0", 1); | ||||
|     else | ||||
|         write(data.c_str(), data.size()); | ||||
|     { | ||||
|         char *ptr = ToUTF8::getBuffer(data.size()+1); | ||||
|         strncpy(ptr, &data[0], data.size()); | ||||
|         ptr[data.size()] = '\0'; | ||||
| 
 | ||||
|         // Convert to UTF8 and return
 | ||||
|         std::string ascii = ToUTF8::getASCII(m_encoding); | ||||
| 
 | ||||
|         write(ascii.c_str(), ascii.size()); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| void ESMWriter::writeHCString(const std::string& data) | ||||
| { | ||||
|     writeHString(data); | ||||
|     if (data.size() > 0 && data[data.size()-1] != '\0') | ||||
|         write("\0", 1); | ||||
| } | ||||
| 
 | ||||
| void ESMWriter::writeName(const std::string& name) | ||||
|  | @ -176,4 +192,21 @@ void ESMWriter::write(const char* data, int size) | |||
|     m_stream->write(data, size); | ||||
| } | ||||
| 
 | ||||
| void ESMWriter::setEncoding(const std::string& encoding) | ||||
| { | ||||
|     if (encoding == "win1250") | ||||
|     { | ||||
|         m_encoding = ToUTF8::WINDOWS_1250; | ||||
|     } | ||||
|     else if (encoding == "win1251") | ||||
|     { | ||||
|         m_encoding = ToUTF8::WINDOWS_1251; | ||||
|     } | ||||
|     else | ||||
|     { | ||||
|         // Default Latin encoding
 | ||||
|         m_encoding = ToUTF8::WINDOWS_1252; | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| } | ||||
|  |  | |||
|  | @ -6,6 +6,7 @@ | |||
| #include <assert.h> | ||||
| 
 | ||||
| #include "esm_common.hpp" | ||||
| #include "../to_utf8/to_utf8.hpp" | ||||
| 
 | ||||
| namespace ESM { | ||||
| 
 | ||||
|  | @ -23,7 +24,7 @@ public: | |||
|     void setVersion(int ver); | ||||
|     int getType(); | ||||
|     void setType(int type); | ||||
| //    void setEncoding(const std::string& encoding); // Write strings as UTF-8?
 | ||||
|     void setEncoding(const std::string& encoding); // Write strings as UTF-8?
 | ||||
|     void setAuthor(const std::string& author); | ||||
|     void setDescription(const std::string& desc); | ||||
| 
 | ||||
|  | @ -38,9 +39,7 @@ public: | |||
|     void writeHNCString(const std::string& name, const std::string& data) | ||||
|     { | ||||
|         startSubRecord(name); | ||||
|         writeHString(data); | ||||
|         if (data.size() > 0 && data[data.size()-1] != '\0') | ||||
|             write("\0", 1); | ||||
|         writeHCString(data); | ||||
|         endRecord(name); | ||||
|     } | ||||
|     void writeHNOString(const std::string& name, const std::string& data) | ||||
|  | @ -48,6 +47,11 @@ public: | |||
|         if (!data.empty()) | ||||
|             writeHNString(name, data); | ||||
|     } | ||||
|     void writeHNOCString(const std::string& name, const std::string& data) | ||||
|     { | ||||
|         if (!data.empty()) | ||||
|             writeHNCString(name, data); | ||||
|     } | ||||
| 
 | ||||
|     template<typename T> | ||||
|     void writeHNT(const std::string& name, const T& data) | ||||
|  | @ -81,6 +85,7 @@ public: | |||
|     void startSubRecord(const std::string& name); | ||||
|     void endRecord(const std::string& name); | ||||
|     void writeHString(const std::string& data); | ||||
|     void writeHCString(const std::string& data); | ||||
|     void writeName(const std::string& data); | ||||
|     void write(const char* data, int size); | ||||
| 
 | ||||
|  | @ -89,6 +94,7 @@ private: | |||
|     std::list<RecordData> m_records; | ||||
|     std::ostream* m_stream; | ||||
|     std::streampos m_headerPos; | ||||
|     ToUTF8::FromType m_encoding; | ||||
|     int m_recordCount; | ||||
| 
 | ||||
|     HEDRstruct m_header; | ||||
|  |  | |||
|  | @ -10,8 +10,8 @@ void Activator::load(ESMReader &esm) | |||
| } | ||||
| void Activator::save(ESMWriter &esm) | ||||
| { | ||||
|     esm.writeHNString("MODL", model); | ||||
|     esm.writeHNString("FNAM", name); | ||||
|     esm.writeHNOString("SCRI", script); | ||||
|     esm.writeHNCString("MODL", model); | ||||
|     esm.writeHNCString("FNAM", name); | ||||
|     esm.writeHNOCString("SCRI", script); | ||||
| } | ||||
| } | ||||
|  |  | |||
|  | @ -13,10 +13,10 @@ void Potion::load(ESMReader &esm) | |||
| } | ||||
| void Potion::save(ESMWriter &esm) | ||||
| { | ||||
|     esm.writeHNString("MODL", model); | ||||
|     esm.writeHNOString("TEXT", icon); | ||||
|     esm.writeHNOString("SCRI", script); | ||||
|     esm.writeHNOString("FNAM", name); | ||||
|     esm.writeHNCString("MODL", model); | ||||
|     esm.writeHNOCString("TEXT", icon); | ||||
|     esm.writeHNOCString("SCRI", script); | ||||
|     esm.writeHNOCString("FNAM", name); | ||||
|     esm.writeHNT("ALDT", data, 12); | ||||
|     effects.save(esm); | ||||
| } | ||||
|  |  | |||
|  | @ -12,10 +12,10 @@ void Apparatus::load(ESMReader &esm) | |||
| } | ||||
| void Apparatus::save(ESMWriter &esm) | ||||
| { | ||||
|     esm.writeHNString("MODL", model); | ||||
|     esm.writeHNString("FNAM", name); | ||||
|     esm.writeHNCString("MODL", model); | ||||
|     esm.writeHNCString("FNAM", name); | ||||
|     esm.writeHNT("AADT", data, 16); | ||||
|     esm.writeHNOString("SCRI", script); | ||||
|     esm.writeHNString("ITEX", icon); | ||||
|     esm.writeHNOCString("SCRI", script); | ||||
|     esm.writeHNCString("ITEX", icon); | ||||
| } | ||||
| } | ||||
|  |  | |||
|  | @ -38,13 +38,13 @@ void Armor::load(ESMReader &esm) | |||
| 
 | ||||
| void Armor::save(ESMWriter &esm) | ||||
| { | ||||
|     esm.writeHNString("MODL", model); | ||||
|     esm.writeHNString("FNAM", name); | ||||
|     esm.writeHNOString("SCRI", script); | ||||
|     esm.writeHNCString("MODL", model); | ||||
|     esm.writeHNCString("FNAM", name); | ||||
|     esm.writeHNOCString("SCRI", script); | ||||
|     esm.writeHNT("AODT", data, 24); | ||||
|     esm.writeHNOString("ITEX", icon); | ||||
|     esm.writeHNOCString("ITEX", icon); | ||||
|     parts.save(esm); | ||||
|     esm.writeHNOString("ENAM", enchant); | ||||
|     esm.writeHNOCString("ENAM", enchant); | ||||
| } | ||||
| 
 | ||||
| } | ||||
|  |  | |||
|  | @ -11,8 +11,8 @@ void BodyPart::load(ESMReader &esm) | |||
| } | ||||
| void BodyPart::save(ESMWriter &esm) | ||||
| { | ||||
|     esm.writeHNString("MODL", model); | ||||
|     esm.writeHNString("FNAM", name); | ||||
|     esm.writeHNCString("MODL", model); | ||||
|     esm.writeHNCString("FNAM", name); | ||||
|     esm.writeHNT("BYDT", data, 4); | ||||
| } | ||||
| 
 | ||||
|  |  | |||
|  | @ -15,13 +15,13 @@ void Book::load(ESMReader &esm) | |||
| } | ||||
| void Book::save(ESMWriter &esm) | ||||
| { | ||||
|     esm.writeHNString("MODL", model); | ||||
|     esm.writeHNOString("FNAM", name); | ||||
|     esm.writeHNCString("MODL", model); | ||||
|     esm.writeHNOCString("FNAM", name); | ||||
|     esm.writeHNT("BKDT", data, 20); | ||||
|     esm.writeHNOString("SCRI", script); | ||||
|     esm.writeHNOString("ITEX", icon); | ||||
|     esm.writeHNOCString("SCRI", script); | ||||
|     esm.writeHNOCString("ITEX", icon); | ||||
|     esm.writeHNOString("TEXT", text); | ||||
|     esm.writeHNOString("ENAM", enchant); | ||||
|     esm.writeHNOCString("ENAM", enchant); | ||||
| } | ||||
| 
 | ||||
| } | ||||
|  |  | |||
|  | @ -13,9 +13,9 @@ void BirthSign::load(ESMReader &esm) | |||
| } | ||||
| void BirthSign::save(ESMWriter &esm) | ||||
| { | ||||
|     esm.writeHNString("FNAM", name); | ||||
|     esm.writeHNOString("TNAM", texture); | ||||
|     esm.writeHNOString("DESC", description); | ||||
|     esm.writeHNCString("FNAM", name); | ||||
|     esm.writeHNOCString("TNAM", texture); | ||||
|     esm.writeHNOCString("DESC", description); | ||||
| 
 | ||||
|     powers.save(esm); | ||||
| } | ||||
|  |  | |||
|  | @ -108,7 +108,7 @@ void Cell::save(ESMWriter &esm) | |||
|     } | ||||
|     else | ||||
|     { | ||||
|         esm.writeHNOString("RGNN", region); | ||||
|         esm.writeHNOCString("RGNN", region); | ||||
|         if (mapColor != 0) | ||||
|             esm.writeHNT("NAM5", mapColor); | ||||
|     } | ||||
|  |  | |||
|  | @ -18,16 +18,16 @@ void Clothing::load(ESMReader &esm) | |||
| } | ||||
| void Clothing::save(ESMWriter &esm) | ||||
| { | ||||
|     esm.writeHNString("MODL", model); | ||||
|     esm.writeHNOString("FNAM", name); | ||||
|     esm.writeHNCString("MODL", model); | ||||
|     esm.writeHNOCString("FNAM", name); | ||||
|     esm.writeHNT("CTDT", data, 12); | ||||
| 
 | ||||
|     esm.writeHNOString("SCRI", script); | ||||
|     esm.writeHNOString("ITEX", icon); | ||||
|     esm.writeHNOCString("SCRI", script); | ||||
|     esm.writeHNOCString("ITEX", icon); | ||||
|      | ||||
|     parts.save(esm); | ||||
|      | ||||
|     esm.writeHNOString("ENAM", enchant); | ||||
|     esm.writeHNOCString("ENAM", enchant); | ||||
| } | ||||
| 
 | ||||
| } | ||||
|  |  | |||
|  | @ -40,12 +40,12 @@ void Container::load(ESMReader &esm) | |||
| 
 | ||||
| void Container::save(ESMWriter &esm) | ||||
| { | ||||
|     esm.writeHNString("MODL", model); | ||||
|     esm.writeHNOString("FNAM", name); | ||||
|     esm.writeHNCString("MODL", model); | ||||
|     esm.writeHNOCString("FNAM", name); | ||||
|     esm.writeHNT("CNDT", weight, 4); | ||||
|     esm.writeHNT("FLAG", flags, 4); | ||||
| 
 | ||||
|     esm.writeHNOString("SCRI", script); | ||||
|     esm.writeHNOCString("SCRI", script); | ||||
| 
 | ||||
|     inventory.save(esm); | ||||
| } | ||||
|  |  | |||
|  | @ -16,6 +16,8 @@ void Creature::load(ESMReader &esm) | |||
|     esm.getHNOT(scale, "XSCL"); | ||||
| 
 | ||||
|     inventory.load(esm); | ||||
|     spells.load(esm); | ||||
|     aiData.load(esm); | ||||
| 
 | ||||
|     // More subrecords:
 | ||||
| 
 | ||||
|  | @ -37,15 +39,18 @@ void Creature::load(ESMReader &esm) | |||
| 
 | ||||
| void Creature::save(ESMWriter &esm) | ||||
| { | ||||
|     esm.writeHNString("MODL", model); | ||||
|     esm.writeHNOString("CNAM", original); | ||||
|     esm.writeHNOString("FNAM", name); | ||||
|     esm.writeHNOString("SCRI", script); | ||||
|     esm.writeHNCString("MODL", model); | ||||
|     esm.writeHNOCString("CNAM", original); | ||||
|     esm.writeHNOCString("FNAM", name); | ||||
|     esm.writeHNOCString("SCRI", script); | ||||
|     esm.writeHNT("NPDT", data, 96); | ||||
|     esm.writeHNT("FLAG", flags); | ||||
|     if (scale != 1.0) | ||||
|         esm.writeHNT("XSCL", scale); | ||||
| 
 | ||||
|     inventory.save(esm); | ||||
|     spells.save(esm); | ||||
|     aiData.save(esm); | ||||
| } | ||||
| 
 | ||||
| } | ||||
|  |  | |||
|  | @ -5,6 +5,7 @@ | |||
| #include "esm_reader.hpp" | ||||
| #include "esm_writer.hpp" | ||||
| #include "loadcont.hpp" | ||||
| #include "defs.hpp" | ||||
| 
 | ||||
| namespace ESM | ||||
| { | ||||
|  | @ -54,6 +55,7 @@ struct Creature : public Record | |||
|     }; // 96 bytes
 | ||||
| 
 | ||||
|     NPDTstruct data; | ||||
|     AIData aiData; | ||||
| 
 | ||||
|     int flags; | ||||
|     float scale; | ||||
|  | @ -62,6 +64,7 @@ struct Creature : public Record | |||
| 
 | ||||
|     // Defined in loadcont.hpp
 | ||||
|     InventoryList inventory; | ||||
|     SpellList spells; | ||||
| 
 | ||||
|     void load(ESMReader &esm); | ||||
|     void save(ESMWriter &esm); | ||||
|  |  | |||
|  | @ -13,11 +13,11 @@ void Door::load(ESMReader &esm) | |||
| } | ||||
| void Door::save(ESMWriter &esm) | ||||
| { | ||||
|     esm.writeHNString("MODL", model); | ||||
|     esm.writeHNOString("FNAM", name); | ||||
|     esm.writeHNOString("SCRI", script); | ||||
|     esm.writeHNOString("SNAM", openSound); | ||||
|     esm.writeHNOString("ANAM", closeSound); | ||||
|     esm.writeHNCString("MODL", model); | ||||
|     esm.writeHNOCString("FNAM", name); | ||||
|     esm.writeHNOCString("SCRI", script); | ||||
|     esm.writeHNOCString("SNAM", openSound); | ||||
|     esm.writeHNOCString("ANAM", closeSound); | ||||
| } | ||||
| 
 | ||||
| } | ||||
|  |  | |||
|  | @ -13,11 +13,11 @@ void Ingredient::load(ESMReader &esm) | |||
| } | ||||
| void Ingredient::save(ESMWriter &esm) | ||||
| { | ||||
|     esm.writeHNString("MODL", model); | ||||
|     esm.writeHNString("FNAM", name); | ||||
|     esm.writeHNCString("MODL", model); | ||||
|     esm.writeHNCString("FNAM", name); | ||||
|     esm.writeHNT("IRDT", data, 56); | ||||
|     esm.writeHNOString("SCRI", script); | ||||
|     esm.writeHNOString("ITEX", script); | ||||
|     esm.writeHNOCString("SCRI", script); | ||||
|     esm.writeHNOCString("ITEX", icon); | ||||
| } | ||||
| 
 | ||||
| } | ||||
|  |  | |||
|  | @ -38,7 +38,7 @@ void LeveledListBase::save(ESMWriter &esm) | |||
|      | ||||
|     for (std::vector<LevelItem>::iterator it = list.begin(); it != list.end(); ++it) | ||||
|     { | ||||
|         esm.writeHNString(recName, it->id); | ||||
|         esm.writeHNCString(recName, it->id); | ||||
|         esm.writeHNT("INTV", it->level); | ||||
|     } | ||||
| } | ||||
|  |  | |||
|  | @ -15,12 +15,12 @@ void Light::load(ESMReader &esm) | |||
| } | ||||
| void Light::save(ESMWriter &esm) | ||||
| { | ||||
|     esm.writeHNString("MODL", model); | ||||
|     esm.writeHNOString("FNAM", name); | ||||
|     esm.writeHNOString("ITEX", icon); | ||||
|     esm.writeHNCString("MODL", model); | ||||
|     esm.writeHNOCString("FNAM", name); | ||||
|     esm.writeHNOCString("ITEX", icon); | ||||
|     esm.writeHNT("LHDT", data, 24); | ||||
|     esm.writeHNOString("SCRI", script); | ||||
|     esm.writeHNOString("SNAM", sound); | ||||
|     esm.writeHNOCString("SCRI", script); | ||||
|     esm.writeHNOCString("SNAM", sound); | ||||
| } | ||||
| 
 | ||||
| } | ||||
|  |  | |||
|  | @ -33,8 +33,8 @@ void Tool::load(ESMReader &esm) | |||
| } | ||||
| void Tool::save(ESMWriter &esm) | ||||
| { | ||||
|     esm.writeHNString("MODL", model); | ||||
|     esm.writeHNString("FNAM", name); | ||||
|     esm.writeHNCString("MODL", model); | ||||
|     esm.writeHNCString("FNAM", name); | ||||
|      | ||||
|     std::string typeName; | ||||
|     switch(type) | ||||
|  | @ -44,9 +44,17 @@ void Tool::save(ESMWriter &esm) | |||
|     case Type_Probe: typeName = "PBDT"; break; | ||||
|     } | ||||
|      | ||||
|     esm.writeHNT(typeName, data, 16); | ||||
|     Data write = data; | ||||
|     if (type == Type_Repair) | ||||
|     { | ||||
|         float tmp = *((float*) &write.uses); | ||||
|         write.uses = *((int*) &write.quality); | ||||
|         write.quality = tmp; | ||||
|     } | ||||
| 
 | ||||
|     esm.writeHNT(typeName, write, 16); | ||||
|     esm.writeHNOString("SCRI", script); | ||||
|     esm.writeHNOString("ITEX", icon); | ||||
|     esm.writeHNOCString("ITEX", icon); | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
|  |  | |||
|  | @ -11,7 +11,7 @@ void LandTexture::load(ESMReader &esm) | |||
| void LandTexture::save(ESMWriter &esm) | ||||
| { | ||||
|     esm.writeHNT("INTV", index); | ||||
|     esm.writeHNString("DATA", texture); | ||||
|     esm.writeHNCString("DATA", texture); | ||||
| } | ||||
| 
 | ||||
| } | ||||
|  |  | |||
|  | @ -28,17 +28,17 @@ void MagicEffect::save(ESMWriter &esm) | |||
|     esm.writeHNT("INDX", index); | ||||
|     esm.writeHNT("MEDT", data, 36); | ||||
| 
 | ||||
|     esm.writeHNOString("ITEX", icon); | ||||
|     esm.writeHNOString("PTEX", particle); | ||||
|     esm.writeHNOString("BSND", boltSound); | ||||
|     esm.writeHNOString("CSND", castSound); | ||||
|     esm.writeHNOString("HSND", hitSound); | ||||
|     esm.writeHNOString("ASND", areaSound); | ||||
|     esm.writeHNOCString("ITEX", icon); | ||||
|     esm.writeHNOCString("PTEX", particle); | ||||
|     esm.writeHNOCString("BSND", boltSound); | ||||
|     esm.writeHNOCString("CSND", castSound); | ||||
|     esm.writeHNOCString("HSND", hitSound); | ||||
|     esm.writeHNOCString("ASND", areaSound); | ||||
|      | ||||
|     esm.writeHNOString("CVFX", casting); | ||||
|     esm.writeHNOString("BVFX", bolt); | ||||
|     esm.writeHNOString("HVFX", hit); | ||||
|     esm.writeHNOString("AVFX", area); | ||||
|     esm.writeHNOCString("CVFX", casting); | ||||
|     esm.writeHNOCString("BVFX", bolt); | ||||
|     esm.writeHNOCString("HVFX", hit); | ||||
|     esm.writeHNOCString("AVFX", area); | ||||
|      | ||||
|     esm.writeHNOString("DESC", description); | ||||
| } | ||||
|  |  | |||
|  | @ -13,11 +13,11 @@ void Miscellaneous::load(ESMReader &esm) | |||
| } | ||||
| void Miscellaneous::save(ESMWriter &esm) | ||||
| { | ||||
|     esm.writeHNString("MODL", model); | ||||
|     esm.writeHNOString("FNAM", name); | ||||
|     esm.writeHNCString("MODL", model); | ||||
|     esm.writeHNOCString("FNAM", name); | ||||
|     esm.writeHNT("MCDT", data, 12); | ||||
|     esm.writeHNOString("SCRI", script); | ||||
|     esm.writeHNOString("ITEX", icon); | ||||
|     esm.writeHNOCString("SCRI", script); | ||||
|     esm.writeHNOCString("ITEX", icon); | ||||
| } | ||||
| 
 | ||||
| } | ||||
|  |  | |||
|  | @ -37,27 +37,20 @@ void NPC::load(ESMReader &esm) | |||
| 
 | ||||
|     inventory.load(esm); | ||||
|     spells.load(esm); | ||||
| 
 | ||||
|     if (esm.isNextSub("AIDT")) | ||||
|     { | ||||
|         esm.getHExact(&AI, sizeof(AI)); | ||||
|         hasAI = true; | ||||
|     } | ||||
|     else | ||||
|         hasAI = false; | ||||
|     aiData.load(esm); | ||||
| 
 | ||||
|     esm.skipRecord(); | ||||
| } | ||||
| void NPC::save(ESMWriter &esm) | ||||
| { | ||||
|     esm.writeHNOString("MODL", model); | ||||
|     esm.writeHNOString("FNAM", name); | ||||
|     esm.writeHNString("RNAM", race); | ||||
|     esm.writeHNString("CNAM", cls); | ||||
|     esm.writeHNString("ANAM", faction); | ||||
|     esm.writeHNString("BNAM", head); | ||||
|     esm.writeHNString("KNAM", hair); | ||||
|     esm.writeHNOString("SCRI", script); | ||||
|     esm.writeHNOCString("MODL", model); | ||||
|     esm.writeHNOCString("FNAM", name); | ||||
|     esm.writeHNCString("RNAM", race); | ||||
|     esm.writeHNCString("CNAM", cls); | ||||
|     esm.writeHNCString("ANAM", faction); | ||||
|     esm.writeHNCString("BNAM", head); | ||||
|     esm.writeHNCString("KNAM", hair); | ||||
|     esm.writeHNOCString("SCRI", script); | ||||
|      | ||||
|     if (npdtType == 52) | ||||
|         esm.writeHNT("NPDT", npdt52, 52); | ||||
|  | @ -68,9 +61,7 @@ void NPC::save(ESMWriter &esm) | |||
|      | ||||
|     inventory.save(esm); | ||||
|     spells.save(esm); | ||||
|      | ||||
|     if (hasAI) | ||||
|         esm.writeHNT("AIDT", AI); | ||||
|     aiData.save(esm); | ||||
| } | ||||
| 
 | ||||
| } | ||||
|  |  | |||
|  | @ -74,15 +74,6 @@ struct NPC : public Record | |||
|     int gold; // ?? not certain
 | ||||
|   }; // 12 bytes
 | ||||
| 
 | ||||
|   struct AIDTstruct | ||||
|   { | ||||
|     // These are probabilities
 | ||||
|     char hello, u1, fight, flee, alarm, u2, u3, u4; | ||||
|     // The last u's might be the skills that this NPC can train you
 | ||||
|     // in?
 | ||||
|     int services; // See the Services enum
 | ||||
|   }; // 12 bytes
 | ||||
| 
 | ||||
| #pragma pack(pop) | ||||
| 
 | ||||
|   char npdtType; | ||||
|  | @ -93,12 +84,10 @@ struct NPC : public Record | |||
| 
 | ||||
|   InventoryList inventory; | ||||
|   SpellList spells; | ||||
| 
 | ||||
|   AIDTstruct AI; | ||||
|   bool hasAI; | ||||
|     AIData aiData; | ||||
| 
 | ||||
|   std::string name, model, race, cls, faction, script, | ||||
|     hair, head; // body parts
 | ||||
|       hair, head; // body parts
 | ||||
| 
 | ||||
|     //std::string mId;
 | ||||
| 
 | ||||
|  |  | |||
|  | @ -27,14 +27,14 @@ void Region::load(ESMReader &esm) | |||
| } | ||||
| void Region::save(ESMWriter &esm) | ||||
| { | ||||
|     esm.writeHNString("FNAM", name); | ||||
|     esm.writeHNCString("FNAM", name); | ||||
|      | ||||
|     if (esm.getVersion() == VER_12) | ||||
|         esm.writeHNT("WEAT", data, sizeof(data) - 2); | ||||
|     else | ||||
|         esm.writeHNT("WEAT", data); | ||||
|      | ||||
|     esm.writeHNOString("BNAM", sleepList); | ||||
|     esm.writeHNOCString("BNAM", sleepList); | ||||
|      | ||||
|     esm.writeHNT("CNAM", mapColor); | ||||
|     for (std::vector<SoundRef>::iterator it = soundList.begin(); it != soundList.end(); ++it) | ||||
|  |  | |||
|  | @ -42,21 +42,25 @@ void Script::save(ESMWriter &esm) | |||
| { | ||||
|     std::string varNameString; | ||||
|     if (!varNames.empty()) | ||||
|     { | ||||
|         for (std::vector<std::string>::iterator it = varNames.begin(); it != varNames.end(); ++it) | ||||
|         { | ||||
|             varNameString.append(*it); | ||||
|             //varNameString.append("\0");
 | ||||
|         } | ||||
| 
 | ||||
|         data.stringTableSize = varNameString.size(); | ||||
|     } | ||||
| 
 | ||||
|     esm.writeHNT("SCHD", data, 52); | ||||
|      | ||||
|     esm.writeHNOString("SCVR", varNameString); | ||||
|     if (!varNames.empty()) | ||||
|     { | ||||
|         esm.startSubRecord("SCVR"); | ||||
|         for (std::vector<std::string>::iterator it = varNames.begin(); it != varNames.end(); ++it) | ||||
|         { | ||||
|             esm.writeHCString(*it); | ||||
|         } | ||||
|         esm.endRecord("SCVR"); | ||||
|     } | ||||
| 
 | ||||
|     esm.startSubRecord("SCDT"); | ||||
|     esm.write(&scriptData[0], data.scriptDataSize); | ||||
|     esm.endRecord("SCDT"); | ||||
| 
 | ||||
|     esm.writeHNString("SCDT", std::string(&scriptData[0], scriptData.size())); | ||||
|     esm.writeHNOString("SCTX", scriptText); | ||||
| } | ||||
| 
 | ||||
|  |  | |||
|  | @ -16,7 +16,7 @@ void Sound::load(ESMReader &esm) | |||
| } | ||||
| void Sound::save(ESMWriter &esm) | ||||
| { | ||||
|     esm.writeHNString("FNAM", sound); | ||||
|     esm.writeHNCString("FNAM", sound); | ||||
|     esm.writeHNT("DATA", data, 3); | ||||
| } | ||||
| 
 | ||||
|  |  | |||
|  | @ -11,7 +11,7 @@ void Spell::load(ESMReader &esm) | |||
| } | ||||
| void Spell::save(ESMWriter &esm) | ||||
| { | ||||
|     esm.writeHNOString("FNAM", name); | ||||
|     esm.writeHNOCString("FNAM", name); | ||||
|     esm.writeHNT("SPDT", data, 12); | ||||
|     effects.save(esm); | ||||
| } | ||||
|  |  | |||
|  | @ -9,7 +9,7 @@ void Static::load(ESMReader &esm) | |||
| } | ||||
| void Static::save(ESMWriter &esm) | ||||
| { | ||||
|     esm.writeHNString("MODL", model); | ||||
|     esm.writeHNCString("MODL", model); | ||||
| } | ||||
| 
 | ||||
| } | ||||
|  |  | |||
|  | @ -14,12 +14,12 @@ void Weapon::load(ESMReader &esm) | |||
| } | ||||
| void Weapon::save(ESMWriter &esm) | ||||
| { | ||||
|     esm.writeHNString("MODL", model); | ||||
|     esm.writeHNOString("FNAM", name); | ||||
|     esm.writeHNCString("MODL", model); | ||||
|     esm.writeHNOCString("FNAM", name); | ||||
|     esm.writeHNT("WPDT", data, 32); | ||||
|     esm.writeHNOString("SCRI", script); | ||||
|     esm.writeHNOString("ITEX", icon); | ||||
|     esm.writeHNOString("ENAM", enchant); | ||||
|     esm.writeHNOCString("SCRI", script); | ||||
|     esm.writeHNOCString("ITEX", icon); | ||||
|     esm.writeHNOCString("ENAM", enchant); | ||||
| } | ||||
| 
 | ||||
| } | ||||
|  |  | |||
|  | @ -67,10 +67,14 @@ public: | |||
|     std::string getId() const { return m_id; } | ||||
|     void setId(const std::string& in) { m_id = in; }  | ||||
|      | ||||
|     int getFlags() const { return (m_flags & 0x1 ? 0x00002000 : 0) | (m_flags & 0x2 ? 0x00000400 : 0); } | ||||
|     void setFlags(int in) { m_flags = (in & 0x00002000 ? 0x1 : 0) | (in & 0x00000400 ? 0x2 : 0); } | ||||
| 
 | ||||
|     virtual int getName() = 0; | ||||
| 
 | ||||
| protected: | ||||
|     std::string m_id; | ||||
|     char m_flags; | ||||
| }; | ||||
| 
 | ||||
| } | ||||
|  |  | |||
|  | @ -193,3 +193,167 @@ std::string ToUTF8::getUtf8(ToUTF8::FromType from) | |||
|   return std::string(&output[0], outlen); | ||||
| } | ||||
| 
 | ||||
| static size_t getLength2(const char *arr, const char* input, bool &ascii) | ||||
| { | ||||
|   ascii = true; | ||||
|   size_t len = 0; | ||||
|   const char* ptr = input; | ||||
|   unsigned char inp = *ptr; | ||||
| 
 | ||||
|   // Do away with the ascii part of the string first (this is almost
 | ||||
|   // always the entire string.)
 | ||||
|   while(inp && inp < 128) | ||||
|     inp = *(++ptr); | ||||
|   len += (ptr-input); | ||||
| 
 | ||||
|   // If we're not at the null terminator at this point, then there
 | ||||
|   // were some non-ascii characters to deal with. Go to slow-mode for
 | ||||
|   // the rest of the string.
 | ||||
|   if(inp) | ||||
|     { | ||||
|       ascii = false; | ||||
|       while(inp) | ||||
|         { | ||||
|             len += 1; | ||||
|           // Find the translated length of this character in the
 | ||||
|           // lookup table.
 | ||||
|             switch(inp) | ||||
|             { | ||||
|             case 0xe2: len -= 2; break; | ||||
|             case 0xc2: | ||||
|             case 0xcb: | ||||
|             case 0xc4: | ||||
|             case 0xc6: | ||||
|             case 0xc3: | ||||
|             case 0xd0: | ||||
|             case 0xd1: | ||||
|             case 0xd2: | ||||
|             case 0xc5: len -= 1; break; | ||||
|             } | ||||
| 
 | ||||
|           inp = *(++ptr); | ||||
|         } | ||||
|     } | ||||
|   return len; | ||||
| } | ||||
| 
 | ||||
| #include <iostream> | ||||
| #include <iomanip> | ||||
| 
 | ||||
| static void copyFromArray2(const char *arr, char*& chp, char* &out) | ||||
| { | ||||
|     unsigned char ch = *(chp++); | ||||
|   // Optimize for ASCII values
 | ||||
|   if(ch < 128) | ||||
|     { | ||||
|       *(out++) = ch; | ||||
|       return; | ||||
|     } | ||||
| 
 | ||||
|   int len = 1; | ||||
|   switch (ch) | ||||
|   { | ||||
|   case 0xe2: len = 3; break; | ||||
|   case 0xc2: | ||||
|   case 0xcb: | ||||
|   case 0xc4: | ||||
|   case 0xc6: | ||||
|   case 0xc3: | ||||
|   case 0xd0: | ||||
|   case 0xd1: | ||||
|   case 0xd2: | ||||
|   case 0xc5: len = 2; break; | ||||
|   } | ||||
| 
 | ||||
|   if (len == 1) // There is no 1 length utf-8 glyph that is not 0x20 (empty space)
 | ||||
|   { | ||||
|       *(out++) = ch; | ||||
|       return; | ||||
|   } | ||||
| 
 | ||||
|   unsigned char ch2 = *(chp++); | ||||
|   unsigned char ch3 = '\0'; | ||||
|   if (len == 3) | ||||
|       ch3 = *(chp++); | ||||
| 
 | ||||
|   for (int i = 128; i < 256; i++) | ||||
|   { | ||||
|       unsigned char b1 = arr[i*6 + 1], b2 = arr[i*6 + 2], b3 = arr[i*6 + 3]; | ||||
|       if (b1 == ch && b2 == ch2 && (len != 3 || b3 == ch3)) | ||||
|       { | ||||
|           *(out++) = (char)i; | ||||
|           return; | ||||
|       } | ||||
|   } | ||||
| 
 | ||||
|   std::cout << "Could not find glyph " << std::hex << (int)ch << " " << (int)ch2 << " " << (int)ch3 << std::endl; | ||||
| 
 | ||||
|   *(out++) = ch; // Could not find glyph, just put whatever
 | ||||
| } | ||||
| 
 | ||||
| std::string ToUTF8::getASCII(ToUTF8::FromType to) | ||||
| { | ||||
|   // Pick translation array
 | ||||
|   const char *arr; | ||||
|   switch (to) | ||||
|   { | ||||
|     case ToUTF8::WINDOWS_1252: | ||||
|     { | ||||
|       arr = ToUTF8::windows_1252; | ||||
|       break; | ||||
|     } | ||||
|     case ToUTF8::WINDOWS_1250: | ||||
|     { | ||||
|       arr = ToUTF8::windows_1250; | ||||
|       break; | ||||
|     } | ||||
|     case ToUTF8::WINDOWS_1251: | ||||
|     { | ||||
|       arr = ToUTF8::windows_1251; | ||||
|       break; | ||||
|     } | ||||
|     default: | ||||
|     { | ||||
|       assert(0); | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|   // Double check that the input string stops at some point (it might
 | ||||
|   // contain zero terminators before this, inside its own data, which
 | ||||
|   // is also ok.)
 | ||||
|   char* input = &buf[0]; | ||||
|   assert(input[size] == 0); | ||||
| 
 | ||||
|   // TODO: The rest of this function is designed for single-character
 | ||||
|   // input encodings only. It also assumes that the input the input
 | ||||
|   // encoding shares its first 128 values (0-127) with ASCII. These
 | ||||
|   // conditions must be checked again if you add more input encodings
 | ||||
|   // later.
 | ||||
| 
 | ||||
|   // Compute output length, and check for pure ascii input at the same
 | ||||
|   // time.
 | ||||
|   bool ascii; | ||||
|   size_t outlen = getLength2(arr, input, ascii); | ||||
| 
 | ||||
|   // If we're pure ascii, then don't bother converting anything.
 | ||||
|   if(ascii) | ||||
|       return std::string(input, outlen); | ||||
| 
 | ||||
|   // Make sure the output is large enough
 | ||||
|   resize(output, outlen); | ||||
|   char *out = &output[0]; | ||||
| 
 | ||||
|   // Translate
 | ||||
|   while(*input) | ||||
|     copyFromArray2(arr, input, out); | ||||
| 
 | ||||
|   // Make sure that we wrote the correct number of bytes
 | ||||
|   assert((out-&output[0]) == (int)outlen); | ||||
| 
 | ||||
|   // And make extra sure the output is null terminated
 | ||||
|   assert(output.size() > outlen); | ||||
|   assert(output[outlen] == 0); | ||||
| 
 | ||||
|   // Return a string
 | ||||
|   return std::string(&output[0], outlen); | ||||
| } | ||||
|  |  | |||
|  | @ -21,6 +21,7 @@ namespace ToUTF8 | |||
|   // Convert the previously written buffer to UTF8 from the given code
 | ||||
|   // page.
 | ||||
|   std::string getUtf8(FromType from); | ||||
|   std::string getASCII(FromType to); | ||||
| } | ||||
| 
 | ||||
| #endif | ||||
|  |  | |||
		Loading…
	
		Reference in a new issue