From e92685c56420abb88028ff0b639a070555d160a8 Mon Sep 17 00:00:00 2001 From: Sour Date: Thu, 2 Mar 2023 00:00:55 -0500 Subject: [PATCH] NES: HD packs load optimizations --- Core/NES/HdPacks/HdData.h | 16 ++ Core/NES/HdPacks/HdPackConditions.h | 11 + Core/NES/HdPacks/HdPackLoader.cpp | 315 ++++++++++++++++------------ Utilities/FastString.h | 12 ++ Utilities/UTF8Util.cpp | 16 ++ 5 files changed, 238 insertions(+), 132 deletions(-) diff --git a/Core/NES/HdPacks/HdData.h b/Core/NES/HdPacks/HdData.h index f501b47f..03aa9acc 100644 --- a/Core/NES/HdPacks/HdData.h +++ b/Core/NES/HdPacks/HdData.h @@ -136,10 +136,26 @@ struct HdScreenInfo } }; +enum class HdPackConditionType +{ + HMirror, + VMirror, + BgPriority, + FrameRange, + MemoryCheck, + MemoryCheckConstant, + TileNearby, + TileAtPos, + SpriteAtPos, + SpriteNearby, + SpritePalette, +}; + struct HdPackCondition { string Name; + virtual HdPackConditionType GetConditionType() = 0; virtual string GetConditionName() = 0; virtual bool IsExcludedFromFile() { return Name.size() > 0 && Name[0] == '!'; } virtual string ToString() = 0; diff --git a/Core/NES/HdPacks/HdPackConditions.h b/Core/NES/HdPacks/HdPackConditions.h index b45be079..09c2aeec 100644 --- a/Core/NES/HdPacks/HdPackConditions.h +++ b/Core/NES/HdPacks/HdPackConditions.h @@ -102,6 +102,7 @@ struct HdPackBaseMemoryCondition : public HdPackCondition struct HdPackHorizontalMirroringCondition : public HdPackCondition { + HdPackConditionType GetConditionType() override { return HdPackConditionType::HMirror; } string GetConditionName() override { return "hmirror"; } string ToString() override { return ""; } bool IsExcludedFromFile() override { return true; } @@ -114,6 +115,7 @@ struct HdPackHorizontalMirroringCondition : public HdPackCondition struct HdPackVerticalMirroringCondition : public HdPackCondition { + HdPackConditionType GetConditionType() override { return HdPackConditionType::VMirror; } string GetConditionName() override { return "vmirror"; } string ToString() override { return ""; } bool IsExcludedFromFile() override { return true; } @@ -126,6 +128,7 @@ struct HdPackVerticalMirroringCondition : public HdPackCondition struct HdPackBgPriorityCondition : public HdPackCondition { + HdPackConditionType GetConditionType() override { return HdPackConditionType::BgPriority; } string GetConditionName() override { return "bgpriority"; } string ToString() override { return ""; } bool IsExcludedFromFile() override { return true; } @@ -138,6 +141,7 @@ struct HdPackBgPriorityCondition : public HdPackCondition struct HdPackMemoryCheckCondition : public HdPackBaseMemoryCondition { + HdPackConditionType GetConditionType() override { return HdPackConditionType::MemoryCheck; } HdPackMemoryCheckCondition() { _useCache = true; } string GetConditionName() override { return IsPpuCondition() ? "ppuMemoryCheck" : "memoryCheck"; } @@ -160,6 +164,7 @@ struct HdPackMemoryCheckCondition : public HdPackBaseMemoryCondition struct HdPackMemoryCheckConstantCondition : public HdPackBaseMemoryCondition { + HdPackConditionType GetConditionType() override { return HdPackConditionType::MemoryCheckConstant; } HdPackMemoryCheckConstantCondition() { _useCache = true; } string GetConditionName() override { return IsPpuCondition() ? "ppuMemoryCheckConstant" : "memoryCheckConstant"; } @@ -187,6 +192,7 @@ struct HdPackFrameRangeCondition : public HdPackCondition HdPackFrameRangeCondition() { _useCache = true; } + HdPackConditionType GetConditionType() override { return HdPackConditionType::FrameRange; } string GetConditionName() override { return "frameRange"; } void Initialize(uint32_t operandA, uint32_t operandB) @@ -214,6 +220,7 @@ struct HdPackFrameRangeCondition : public HdPackCondition struct HdPackTileAtPositionCondition : public HdPackBaseTileCondition { HdPackTileAtPositionCondition() { _useCache = true; } + HdPackConditionType GetConditionType() override { return HdPackConditionType::TileAtPos; } string GetConditionName() override { return "tileAtPosition"; } bool InternalCheckCondition(HdScreenInfo *screenInfo, int x, int y, HdPpuTileInfo* tile) override @@ -230,6 +237,7 @@ struct HdPackTileAtPositionCondition : public HdPackBaseTileCondition struct HdPackSpriteAtPositionCondition : public HdPackBaseTileCondition { HdPackSpriteAtPositionCondition() { _useCache = true; } + HdPackConditionType GetConditionType() override { return HdPackConditionType::SpriteAtPos; } string GetConditionName() override { return "spriteAtPosition"; } bool InternalCheckCondition(HdScreenInfo *screenInfo, int x, int y, HdPpuTileInfo* tile) override @@ -252,6 +260,7 @@ struct HdPackSpriteAtPositionCondition : public HdPackBaseTileCondition struct HdPackTileNearbyCondition : public HdPackBaseTileCondition { + HdPackConditionType GetConditionType() override { return HdPackConditionType::TileNearby; } string GetConditionName() override { return "tileNearby"; } bool InternalCheckCondition(HdScreenInfo *screenInfo, int x, int y, HdPpuTileInfo* tile) override @@ -272,6 +281,7 @@ struct HdPackTileNearbyCondition : public HdPackBaseTileCondition struct HdPackSpriteNearbyCondition : public HdPackBaseTileCondition { + HdPackConditionType GetConditionType() override { return HdPackConditionType::SpriteNearby; } string GetConditionName() override { return "spriteNearby"; } bool InternalCheckCondition(HdScreenInfo *screenInfo, int x, int y, HdPpuTileInfo* tile) override @@ -304,6 +314,7 @@ struct HdPackSpriteNearbyCondition : public HdPackBaseTileCondition template struct HdPackSpritePaletteCondition : public HdPackCondition { + HdPackConditionType GetConditionType() override { return HdPackConditionType::SpritePalette; } string GetConditionName() override { return "sppalette"; } string ToString() override { return ""; } bool IsExcludedFromFile() override { return true; } diff --git a/Core/NES/HdPacks/HdPackLoader.cpp b/Core/NES/HdPacks/HdPackLoader.cpp index 9aa1929a..9007cb07 100644 --- a/Core/NES/HdPacks/HdPackLoader.cpp +++ b/Core/NES/HdPacks/HdPackLoader.cpp @@ -11,6 +11,7 @@ #include "Utilities/StringUtilities.h" #include "Utilities/HexUtilities.h" #include "Utilities/PNGHelper.h" +#include "Utilities/FastString.h" #define checkConstraint(x, y) if(!(x)) { MessageManager::Log(y); return; } @@ -118,7 +119,7 @@ bool HdPackLoader::LoadFile(string filename, vector &fileData) uint32_t fileSize = (uint32_t)file.tellg(); file.seekg(0, ios::beg); - fileData = vector(fileSize, 0); + fileData.resize(fileSize); file.read((char*)fileData.data(), fileSize); return true; @@ -130,7 +131,7 @@ bool HdPackLoader::LoadFile(string filename, vector &fileData) bool HdPackLoader::LoadPack() { - string currentLine; + string lineContent; try { vector hdDefinition; if(!LoadFile("hires.txt", hdDefinition)) { @@ -139,7 +140,21 @@ bool HdPackLoader::LoadPack() InitializeGlobalConditions(); - for(string lineContent : StringUtilities::Split(string(hdDefinition.data(), hdDefinition.data() + hdDefinition.size()), '\n')) { + size_t len = hdDefinition.size(); + size_t pos = 0; + while(pos < len) { + lineContent.clear(); + + size_t start = pos; + for(; pos < len; pos++) { + if(hdDefinition[pos] == '\n') { + pos++; + break; + } + } + + lineContent.insert(0, (char*)hdDefinition.data() + start, pos < len ? (pos - start - 1) : (len - start)); + if(lineContent.empty()) { continue; } @@ -147,17 +162,41 @@ bool HdPackLoader::LoadPack() if(lineContent[lineContent.size() - 1] == '\r') { lineContent = lineContent.substr(0, lineContent.size() - 1); } - currentLine = lineContent; vector conditions; if(lineContent.substr(0, 1) == "[") { size_t endOfCondition = lineContent.find_first_of(']', 1); + if(endOfCondition == string::npos) { + MessageManager::Log("[HDPack] Invalid condition tag: " + lineContent); + continue; + } conditions = ParseConditionString(lineContent.substr(1, endOfCondition - 1)); lineContent = lineContent.substr(endOfCondition + 1); } vector tokens; - if(lineContent.substr(0, 5) == "") { + if(lineContent.substr(0, 6) == "") { + tokens = StringUtilities::Split(lineContent.substr(6), ','); + ProcessTileTag(tokens, conditions); + } else if(lineContent.substr(0, 12) == "") { + tokens = StringUtilities::Split(lineContent.substr(12), ','); + ProcessBackgroundTag(tokens, conditions); + } else if(lineContent.substr(0, 11) == "") { + tokens = StringUtilities::Split(lineContent.substr(11), ','); + ProcessConditionTag(tokens, false); + ProcessConditionTag(tokens, true); + } else if(lineContent.substr(0, 5) == "") { + lineContent = lineContent.substr(5); + if(!ProcessImgTag(lineContent)) { + return false; + } + } else if(lineContent.substr(0, 5) == "") { + tokens = StringUtilities::Split(lineContent.substr(5), ','); + ProcessBgmTag(tokens); + } else if(lineContent.substr(0, 5) == "") { + tokens = StringUtilities::Split(lineContent.substr(5), ','); + ProcessSfxTag(tokens); + } else if(lineContent.substr(0, 5) == "") { _data->Version = stoi(lineContent.substr(5)); if(_data->Version > HdNesPack::CurrentVersion) { MessageManager::Log("[HDPack] This HD Pack was built with a more recent version of Mesen - update Mesen to the latest version and try again."); @@ -169,33 +208,12 @@ bool HdPackLoader::LoadPack() } else if(lineContent.substr(0, 10) == "") { tokens = StringUtilities::Split(lineContent.substr(10), ','); ProcessOverscanTag(tokens); - } else if(lineContent.substr(0, 5) == "") { - lineContent = lineContent.substr(5); - if(!ProcessImgTag(lineContent)) { - return false; - } } else if(lineContent.substr(0, 7) == "") { tokens = StringUtilities::Split(lineContent.substr(7), ','); ProcessPatchTag(tokens); - } else if(lineContent.substr(0, 12) == "") { - tokens = StringUtilities::Split(lineContent.substr(12), ','); - ProcessBackgroundTag(tokens, conditions); - } else if(lineContent.substr(0, 11) == "") { - tokens = StringUtilities::Split(lineContent.substr(11), ','); - ProcessConditionTag(tokens, false); - ProcessConditionTag(tokens, true); - } else if(lineContent.substr(0, 6) == "") { - tokens = StringUtilities::Split(lineContent.substr(6), ','); - ProcessTileTag(tokens, conditions); } else if(lineContent.substr(0, 9) == "") { tokens = StringUtilities::Split(lineContent.substr(9), ','); ProcessOptionTag(tokens); - } else if(lineContent.substr(0, 5) == "") { - tokens = StringUtilities::Split(lineContent.substr(5), ','); - ProcessBgmTag(tokens); - } else if(lineContent.substr(0, 5) == "") { - tokens = StringUtilities::Split(lineContent.substr(5), ','); - ProcessSfxTag(tokens); } } @@ -204,7 +222,7 @@ bool HdPackLoader::LoadPack() return true; } catch(std::exception &ex) { - MessageManager::Log(string("[HDPack] Error loading HDPack: ") + ex.what() + " on line: " + currentLine); + MessageManager::Log(string("[HDPack] Error loading HDPack: ") + ex.what() + " on line: " + lineContent); return false; } } @@ -311,15 +329,15 @@ void HdPackLoader::ProcessTileTag(vector &tokens, vectorConditions = conditions; tileInfo->ForceDisableCache = false; for(HdPackCondition* condition : conditions) { - if(dynamic_cast(condition)) { - tileInfo->ForceDisableCache = true; - break; - } else if(dynamic_cast(condition)) { - HdPackTileNearbyCondition* tileNearby = dynamic_cast(condition); - if(tileNearby->TileX % 8 > 0 || tileNearby->TileY % 8 > 0) { - tileInfo->ForceDisableCache = true; + HdPackConditionType type = condition->GetConditionType(); + switch(type){ + case HdPackConditionType::SpriteNearby: tileInfo->ForceDisableCache = true; break; + case HdPackConditionType::TileNearby: + HdPackTileNearbyCondition* tileNearby = (HdPackTileNearbyCondition*)condition; + if(tileNearby->TileX % 8 > 0 || tileNearby->TileY % 8 > 0) { + tileInfo->ForceDisableCache = true; + } break; - } } } @@ -415,98 +433,116 @@ void HdPackLoader::ProcessConditionTag(vector &tokens, bool createInvert } int index = 2; - if(dynamic_cast(condition.get())) { - checkConstraint(tokens.size() >= 6, "[HDPack] Condition tag should contain at least 6 parameters"); + switch(condition->GetConditionType()) { + case HdPackConditionType::TileNearby: + case HdPackConditionType::TileAtPos: + case HdPackConditionType::SpriteNearby: + case HdPackConditionType::SpriteAtPos: { + checkConstraint(tokens.size() >= 6, "[HDPack] Condition tag should contain at least 6 parameters"); - int x = std::stoi(tokens[index++]); - int y = std::stoi(tokens[index++]); - string token = tokens[index++]; - int32_t tileIndex = -1; - string tileData; - if(token.size() == 32) { - tileData = token; - } else { - if(_data->Version < 104) { - tileIndex = std::stoi(token); + int x = std::stoi(tokens[index++]); + int y = std::stoi(tokens[index++]); + string token = tokens[index++]; + int32_t tileIndex = -1; + string tileData; + if(token.size() == 32) { + tileData = token; } else { - //Tile indexes are stored as hex starting from version 104+ - tileIndex = HexUtilities::FromHex(token); + if(_data->Version < 104) { + tileIndex = std::stoi(token); + } else { + //Tile indexes are stored as hex starting from version 104+ + tileIndex = HexUtilities::FromHex(token); + } } - } - uint32_t palette = HexUtilities::FromHex(tokens[index++]); + uint32_t palette = HexUtilities::FromHex(tokens[index++]); - ((HdPackBaseTileCondition*)condition.get())->Initialize(x, y, palette, tileIndex, tileData); - } else if(dynamic_cast(condition.get())) { - checkConstraint(_data->Version >= 101, "[HDPack] This feature requires version 101+ of HD Packs"); - checkConstraint(tokens.size() >= 5, "[HDPack] Condition tag should contain at least 5 parameters"); + ((HdPackBaseTileCondition*)condition.get())->Initialize(x, y, palette, tileIndex, tileData); + break; + } + + case HdPackConditionType::MemoryCheck: + case HdPackConditionType::MemoryCheckConstant: { + checkConstraint(_data->Version >= 101, "[HDPack] This feature requires version 101+ of HD Packs"); + checkConstraint(tokens.size() >= 5, "[HDPack] Condition tag should contain at least 5 parameters"); - bool usePpuMemory = tokens[1].substr(0, 3) == "ppu"; - uint32_t operandA = HexUtilities::FromHex(tokens[index++]); + bool usePpuMemory = tokens[1].substr(0, 3) == "ppu"; + uint32_t operandA = HexUtilities::FromHex(tokens[index++]); - if(usePpuMemory) { - checkConstraint(operandA <= 0x3FFF, "[HDPack] Out of range memoryCheck operand"); - operandA |= HdPackBaseMemoryCondition::PpuMemoryMarker; - } else { - checkConstraint(operandA <= 0xFFFF, "[HDPack] Out of range memoryCheck operand"); - } - - HdPackConditionOperator op; - string opString = tokens[index++]; - if(opString == "==") { - op = HdPackConditionOperator::Equal; - } else if(opString == "!=") { - op = HdPackConditionOperator::NotEqual; - } else if(opString == ">") { - op = HdPackConditionOperator::GreaterThan; - } else if(opString == "<") { - op = HdPackConditionOperator::LowerThan; - } else if(opString == "<=") { - op = HdPackConditionOperator::LowerThanOrEqual; - } else if(opString == ">=") { - op = HdPackConditionOperator::GreaterThanOrEqual; - } else { - checkConstraint(false, "[HDPack] Invalid operator."); - } - - uint32_t operandB = HexUtilities::FromHex(tokens[index++]); - uint32_t mask = 0xFF; - if(tokens.size() > 5 && _data->Version >= 103) { - checkConstraint(operandB <= 0xFF, "[HDPack] Out of range memoryCheck mask"); - mask = HexUtilities::FromHex(tokens[index++]); - } - - if(dynamic_cast(condition.get())) { if(usePpuMemory) { - checkConstraint(operandB <= 0x3FFF, "[HDPack] Out of range memoryCheck operand"); - operandB |= HdPackBaseMemoryCondition::PpuMemoryMarker; + checkConstraint(operandA <= 0x3FFF, "[HDPack] Out of range memoryCheck operand"); + operandA |= HdPackBaseMemoryCondition::PpuMemoryMarker; } else { - checkConstraint(operandB <= 0xFFFF, "[HDPack] Out of range memoryCheck operand"); + checkConstraint(operandA <= 0xFFFF, "[HDPack] Out of range memoryCheck operand"); } - _data->WatchedMemoryAddresses.emplace(operandB); - } else if(dynamic_cast(condition.get())) { - checkConstraint(operandB <= 0xFF, "[HDPack] Out of range memoryCheckConstant operand"); - } - _data->WatchedMemoryAddresses.emplace(operandA); - ((HdPackBaseMemoryCondition*)condition.get())->Initialize(operandA, op, operandB, (uint8_t)mask); - } else if(dynamic_cast(condition.get())) { - checkConstraint(_data->Version >= 101, "[HDPack] This feature requires version 101+ of HD Packs"); - checkConstraint(tokens.size() >= 4, "[HDPack] Condition tag should contain at least 4 parameters"); - int32_t operandA; - int32_t operandB; - if(_data->Version == 101) { - operandA = HexUtilities::FromHex(tokens[index++]); - operandB = HexUtilities::FromHex(tokens[index++]); - } else { - //Version 102+ - operandA = std::stoi(tokens[index++]); - operandB = std::stoi(tokens[index++]); + HdPackConditionOperator op; + string opString = tokens[index++]; + if(opString == "==") { + op = HdPackConditionOperator::Equal; + } else if(opString == "!=") { + op = HdPackConditionOperator::NotEqual; + } else if(opString == ">") { + op = HdPackConditionOperator::GreaterThan; + } else if(opString == "<") { + op = HdPackConditionOperator::LowerThan; + } else if(opString == "<=") { + op = HdPackConditionOperator::LowerThanOrEqual; + } else if(opString == ">=") { + op = HdPackConditionOperator::GreaterThanOrEqual; + } else { + checkConstraint(false, "[HDPack] Invalid operator."); + } + + uint32_t operandB = HexUtilities::FromHex(tokens[index++]); + uint32_t mask = 0xFF; + if(tokens.size() > 5 && _data->Version >= 103) { + checkConstraint(operandB <= 0xFF, "[HDPack] Out of range memoryCheck mask"); + mask = HexUtilities::FromHex(tokens[index++]); + } + + switch(condition->GetConditionType()) { + case HdPackConditionType::MemoryCheck: + if(usePpuMemory) { + checkConstraint(operandB <= 0x3FFF, "[HDPack] Out of range memoryCheck operand"); + operandB |= HdPackBaseMemoryCondition::PpuMemoryMarker; + } else { + checkConstraint(operandB <= 0xFFFF, "[HDPack] Out of range memoryCheck operand"); + } + _data->WatchedMemoryAddresses.emplace(operandB); + break; + + case HdPackConditionType::MemoryCheckConstant: + checkConstraint(operandB <= 0xFF, "[HDPack] Out of range memoryCheckConstant operand"); + break; + } + + _data->WatchedMemoryAddresses.emplace(operandA); + ((HdPackBaseMemoryCondition*)condition.get())->Initialize(operandA, op, operandB, (uint8_t)mask); + break; } - checkConstraint(operandA >= 0 && operandA <= 0xFFFF, "[HDPack] Out of range frameRange operand"); - checkConstraint(operandB >= 0 && operandB <= 0xFFFF, "[HDPack] Out of range frameRange operand"); + case HdPackConditionType::FrameRange: { + checkConstraint(_data->Version >= 101, "[HDPack] This feature requires version 101+ of HD Packs"); + checkConstraint(tokens.size() >= 4, "[HDPack] Condition tag should contain at least 4 parameters"); - ((HdPackFrameRangeCondition*)condition.get())->Initialize(operandA, operandB); + int32_t operandA; + int32_t operandB; + if(_data->Version == 101) { + operandA = HexUtilities::FromHex(tokens[index++]); + operandB = HexUtilities::FromHex(tokens[index++]); + } else { + //Version 102+ + operandA = std::stoi(tokens[index++]); + operandB = std::stoi(tokens[index++]); + } + + checkConstraint(operandA >= 0 && operandA <= 0xFFFF, "[HDPack] Out of range frameRange operand"); + checkConstraint(operandB >= 0 && operandB <= 0xFFFF, "[HDPack] Out of range frameRange operand"); + + ((HdPackFrameRangeCondition*)condition.get())->Initialize(operandA, operandB); + break; + } } HdPackCondition *cond = condition.get(); @@ -549,19 +585,21 @@ void HdPackLoader::ProcessBackgroundTag(vector &tokens, vector(condition) && - !dynamic_cast(condition) && - !dynamic_cast(condition) && - !dynamic_cast(condition) && - !dynamic_cast(condition) - ) { - MessageManager::Log("[HDPack] Invalid condition type for background: " + tokens[0]); - return; - } else { - backgroundInfo.Conditions.push_back(condition); + switch(condition->GetConditionType()) { + case HdPackConditionType::TileAtPos: + case HdPackConditionType::SpriteAtPos: + case HdPackConditionType::MemoryCheck: + case HdPackConditionType::MemoryCheckConstant: + case HdPackConditionType::FrameRange: + backgroundInfo.Conditions.push_back(condition); + break; + + default: + MessageManager::Log("[HDPack] Invalid condition type for background: " + tokens[0]); + return; } } @@ -644,20 +682,33 @@ void HdPackLoader::ProcessSfxTag(vector &tokens) vector HdPackLoader::ParseConditionString(string conditionString) { - vector conditionNames = StringUtilities::Split(conditionString, '&'); - + FastString conditionName; vector conditions; - for(string conditionName : conditionNames) { - conditionName.erase(conditionName.find_last_not_of(" \n\r\t") + 1); + conditions.reserve(3); - auto result = _conditionsByName.find(conditionName); + auto processCondition = [&] { + auto result = _conditionsByName.find(conditionName.ToString()); if(result != _conditionsByName.end()) { conditions.push_back(result->second); - } else { - MessageManager::Log("[HDPack] Condition not found: " + conditionName); + } else { + MessageManager::Log("[HDPack] Condition not found: " + string(conditionName.ToString())); + } + conditionName.Reset(); + }; + + for(size_t i = 0, len = conditionString.size(); i < len; i++) { + char c = conditionString[i]; + if(c == ' ' || c == '\n' || c == '\r' || c == '\t') { + continue; + } else if(c == '&') { + processCondition(); + } else { + conditionName.WriteSafe(c); } } + processCondition(); + return conditions; } diff --git a/Utilities/FastString.h b/Utilities/FastString.h index 883e1adc..96184ee8 100644 --- a/Utilities/FastString.h +++ b/Utilities/FastString.h @@ -15,6 +15,13 @@ public: FastString(const char* str, int size) { Write(str, size); } FastString(string &str) { Write(str); } + void WriteSafe(char c) + { + if(_pos < 999) { + _buffer[_pos++] = c; + } + } + void Write(char c) { if(_lowerCase) { @@ -77,6 +84,11 @@ public: return _pos; } + void Reset() + { + _pos = 0; + } + template void WriteAll(T first, Args... args) { diff --git a/Utilities/UTF8Util.cpp b/Utilities/UTF8Util.cpp index 3f66916d..07bc6b26 100644 --- a/Utilities/UTF8Util.cpp +++ b/Utilities/UTF8Util.cpp @@ -3,12 +3,28 @@ #include #include +#ifdef _MSC_VER +#define WIN32_LEAN_AND_MEAN +#include +#undef WIN32_LEAN_AND_MEAN +#endif + namespace utf8 { std::wstring utf8::decode(const std::string &str) { +#ifdef _MSC_VER + std::wstring ret; + int len = MultiByteToWideChar(CP_UTF8, 0, str.c_str(), (int)str.length(), NULL, 0); + if(len > 0) { + ret.resize(len); + MultiByteToWideChar(CP_UTF8, 0, str.c_str(), (int)str.length(), &ret[0], len); + } + return ret; +#else std::wstring_convert> conv; return conv.from_bytes(str); +#endif } std::string utf8::encode(const std::wstring &wstr)