mirror of
https://github.com/SourMesen/Mesen.git
synced 2025-04-02 10:52:48 -04:00
Saved config, save state slots, MRU list
This commit is contained in:
parent
c40e207301
commit
b80b0a0d02
15 changed files with 5266 additions and 36 deletions
|
@ -140,9 +140,16 @@ void Console::SaveState()
|
|||
_saveStateFilename.clear();
|
||||
}
|
||||
|
||||
void Console::LoadState(wstring filename)
|
||||
bool Console::LoadState(wstring filename)
|
||||
{
|
||||
_loadStateFilename = filename;
|
||||
ifstream file(filename, ios::out | ios::binary);
|
||||
|
||||
if(file) {
|
||||
file.close();
|
||||
_loadStateFilename = filename;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void Console::LoadState()
|
||||
|
|
|
@ -50,7 +50,7 @@ class Console
|
|||
void SaveTestResult();
|
||||
|
||||
void SaveState(wstring filename);
|
||||
void LoadState(wstring filename);
|
||||
bool LoadState(wstring filename);
|
||||
|
||||
static bool CheckFlag(int flag);
|
||||
static void SetFlags(int flags);
|
||||
|
|
BIN
GUI/GUI.rc
BIN
GUI/GUI.rc
Binary file not shown.
|
@ -147,6 +147,9 @@
|
|||
<ProjectReference Include="..\Core\Core.vcxproj">
|
||||
<Project>{78fef1a1-6df1-4cbb-a373-ae6fa7ce5ce0}</Project>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\Utilities\Utilities.vcxproj">
|
||||
<Project>{b5330148-e8c7-46ba-b54e-69be59ea337d}</Project>
|
||||
</ProjectReference>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="Calibri.30.spritefont">
|
||||
|
@ -157,7 +160,7 @@
|
|||
<ItemGroup>
|
||||
<Content Include="Calibri.30.spritefont">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
</Content>
|
||||
</Content>
|
||||
</ItemGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||
<ImportGroup Label="ExtensionTargets">
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
#include "Resource.h"
|
||||
#include "MainWindow.h"
|
||||
#include "../Core/Console.h"
|
||||
#include "../Utilities/Timer.h"
|
||||
#include "../Utilities/ConfigManager.h"
|
||||
#include "InputManager.h"
|
||||
|
||||
using namespace DirectX;
|
||||
|
@ -144,35 +144,201 @@ namespace NES {
|
|||
return (INT_PTR)FALSE;
|
||||
}
|
||||
|
||||
void MainWindow::InitializeOptions()
|
||||
INT_PTR CALLBACK MainWindow::ControllerSetup(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
|
||||
{
|
||||
Console::SetFlags(EmulationFlags::LimitFPS);
|
||||
UNREFERENCED_PARAMETER(lParam);
|
||||
static HWND _focusedControl;
|
||||
switch(message) {
|
||||
case WM_INITDIALOG:
|
||||
HWND cboPort1;
|
||||
cboPort1 = GetDlgItem(hDlg, IDC_CBOPORT1);
|
||||
SendMessage(cboPort1, CB_ADDSTRING, 0, (LPARAM)_T("<none>"));
|
||||
SendMessage(cboPort1, CB_ADDSTRING, 0, (LPARAM)_T("Gamepad"));
|
||||
SendMessage(cboPort1, CB_SETCURSEL, 0, 0);
|
||||
return (INT_PTR)TRUE;
|
||||
|
||||
case WM_SETFOCUS:
|
||||
case WM_SETCURSOR:
|
||||
_focusedControl = GetFocus();
|
||||
break;
|
||||
|
||||
case WM_KEYDOWN:
|
||||
break;
|
||||
|
||||
case WM_COMMAND:
|
||||
if(LOWORD(wParam) == IDOK || LOWORD(wParam) == IDCANCEL) {
|
||||
if(LOWORD(wParam) == IDOK) {
|
||||
//Save settings
|
||||
ConfigManager::SetValue(Config::Player1_ButtonA, "V");
|
||||
}
|
||||
|
||||
EndDialog(hDlg, LOWORD(wParam));
|
||||
return (INT_PTR)TRUE;
|
||||
}
|
||||
break;
|
||||
}
|
||||
return (INT_PTR)FALSE;
|
||||
}
|
||||
|
||||
wstring MainWindow::SelectROM()
|
||||
INT_PTR CALLBACK MainWindow::InputConfig(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
|
||||
{
|
||||
wchar_t buffer[2000];
|
||||
UNREFERENCED_PARAMETER(lParam);
|
||||
switch(message) {
|
||||
case WM_INITDIALOG:
|
||||
HWND cboPort1;
|
||||
cboPort1 = GetDlgItem(hDlg, IDC_CBOPORT1);
|
||||
SendMessage(cboPort1, CB_ADDSTRING, 0, (LPARAM)_T("<none>"));
|
||||
SendMessage(cboPort1, CB_ADDSTRING, 0, (LPARAM)_T("Gamepad"));
|
||||
SendMessage(cboPort1, CB_SETCURSEL, 0, 0);
|
||||
return (INT_PTR)TRUE;
|
||||
|
||||
OPENFILENAME ofn;
|
||||
ZeroMemory(&ofn, sizeof(ofn));
|
||||
ofn.lStructSize = sizeof(ofn);
|
||||
ofn.hwndOwner = nullptr;
|
||||
ofn.lpstrFile = buffer;
|
||||
ofn.lpstrFile[0] = '\0';
|
||||
ofn.nMaxFile = sizeof(buffer);
|
||||
ofn.lpstrFilter = L"NES Roms\0*.NES\0All\0*.*";
|
||||
ofn.nFilterIndex = 1;
|
||||
ofn.lpstrFileTitle = nullptr;
|
||||
ofn.nMaxFileTitle = 0;
|
||||
ofn.lpstrInitialDir = nullptr;
|
||||
ofn.Flags = OFN_PATHMUSTEXIST | OFN_FILEMUSTEXIST;
|
||||
case WM_COMMAND:
|
||||
if(LOWORD(wParam) == IDC_BTNPORT1KEYS) {
|
||||
DialogBox(nullptr, MAKEINTRESOURCE(IDD_CONTROLLERSETUP), hDlg, ControllerSetup);
|
||||
} else if(LOWORD(wParam) == IDOK || LOWORD(wParam) == IDCANCEL) {
|
||||
if(LOWORD(wParam) == IDOK) {
|
||||
//Save settings
|
||||
ConfigManager::SetValue(Config::Player1_ButtonA, "V");
|
||||
}
|
||||
|
||||
GetOpenFileName(&ofn);
|
||||
EndDialog(hDlg, LOWORD(wParam));
|
||||
return (INT_PTR)TRUE;
|
||||
}
|
||||
break;
|
||||
}
|
||||
return (INT_PTR)FALSE;
|
||||
}
|
||||
|
||||
wstring filepath(buffer);
|
||||
void MainWindow::InitializeOptions()
|
||||
{
|
||||
if(ConfigManager::GetValue<bool>(Config::LimitFPS)) {
|
||||
Console::SetFlags(EmulationFlags::LimitFPS);
|
||||
SetMenuCheck(ID_OPTIONS_LIMITFPS, true);
|
||||
}
|
||||
|
||||
if(ConfigManager::GetValue<bool>(Config::ShowFPS)) {
|
||||
_renderer->SetFlags(UIFlags::ShowFPS);
|
||||
SetMenuCheck(ID_OPTIONS_SHOWFPS, true);
|
||||
}
|
||||
|
||||
wstring filename = filepath.substr(filepath.find_last_of(L"/\\") + 1);
|
||||
SetWindowText(_hWnd, (wstring(_windowName) + L": " + filename).c_str());
|
||||
UpdateMRUMenu();
|
||||
}
|
||||
|
||||
void MainWindow::AddToMRU(wstring romFilename)
|
||||
{
|
||||
wstring MRU0 = ConfigManager::GetValue<wstring>(Config::MRU0);
|
||||
wstring MRU1 = ConfigManager::GetValue<wstring>(Config::MRU1);
|
||||
wstring MRU2 = ConfigManager::GetValue<wstring>(Config::MRU2);
|
||||
wstring MRU3 = ConfigManager::GetValue<wstring>(Config::MRU3);
|
||||
wstring MRU4 = ConfigManager::GetValue<wstring>(Config::MRU4);
|
||||
|
||||
if(MRU0.compare(romFilename) == 0) {
|
||||
return;
|
||||
} else if(MRU1.compare(romFilename) == 0) {
|
||||
MRU1 = MRU0;
|
||||
MRU0 = romFilename;
|
||||
} else if(MRU2.compare(romFilename) == 0) {
|
||||
MRU2 = MRU1;
|
||||
MRU1 = MRU0;
|
||||
MRU0 = romFilename;
|
||||
} else if(MRU3.compare(romFilename) == 0) {
|
||||
MRU3 = MRU2;
|
||||
MRU2 = MRU1;
|
||||
MRU1 = MRU0;
|
||||
MRU0 = romFilename;
|
||||
} else {
|
||||
MRU4 = MRU3;
|
||||
MRU3 = MRU2;
|
||||
MRU2 = MRU1;
|
||||
MRU1 = MRU0;
|
||||
MRU0 = romFilename;
|
||||
}
|
||||
|
||||
ConfigManager::SetValue(Config::MRU0, MRU0);
|
||||
ConfigManager::SetValue(Config::MRU1, MRU1);
|
||||
ConfigManager::SetValue(Config::MRU2, MRU2);
|
||||
ConfigManager::SetValue(Config::MRU3, MRU3);
|
||||
ConfigManager::SetValue(Config::MRU4, MRU4);
|
||||
|
||||
UpdateMRUMenu();
|
||||
}
|
||||
|
||||
void MainWindow::UpdateMRUMenu()
|
||||
{
|
||||
wstring MRU0 = ConfigManager::GetValue<wstring>(Config::MRU0);
|
||||
wstring MRU1 = ConfigManager::GetValue<wstring>(Config::MRU1);
|
||||
wstring MRU2 = ConfigManager::GetValue<wstring>(Config::MRU2);
|
||||
wstring MRU3 = ConfigManager::GetValue<wstring>(Config::MRU3);
|
||||
wstring MRU4 = ConfigManager::GetValue<wstring>(Config::MRU4);
|
||||
|
||||
HMENU hMenu = GetMenu(_hWnd);
|
||||
|
||||
MENUITEMINFOW info;
|
||||
//Initialize MENUITEMINFO structure:
|
||||
memset(&info, 0, sizeof(info));
|
||||
info.cbSize = sizeof(info);
|
||||
info.fMask = MIIM_TYPE;
|
||||
info.fType = MFT_STRING;
|
||||
info.cch = 256;
|
||||
|
||||
if(!MRU0.empty()) {
|
||||
info.dwTypeData = (LPWSTR)MRU0.c_str();
|
||||
SetMenuItemInfo(hMenu, ID_RECENTFILES_MRU1, false, &info);
|
||||
}
|
||||
|
||||
if(!MRU1.empty()) {
|
||||
info.dwTypeData = (LPWSTR)MRU1.c_str();
|
||||
SetMenuItemInfo(hMenu, ID_RECENTFILES_MRU2, false, &info);
|
||||
}
|
||||
|
||||
if(!MRU2.empty()) {
|
||||
info.dwTypeData = (LPWSTR)MRU2.c_str();
|
||||
SetMenuItemInfo(hMenu, ID_RECENTFILES_MRU3, false, &info);
|
||||
}
|
||||
|
||||
if(!MRU3.empty()) {
|
||||
info.dwTypeData = (LPWSTR)MRU3.c_str();
|
||||
SetMenuItemInfo(hMenu, ID_RECENTFILES_MRU4, false, &info);
|
||||
}
|
||||
|
||||
if(!MRU4.empty()) {
|
||||
info.dwTypeData = (LPWSTR)MRU4.c_str();
|
||||
SetMenuItemInfo(hMenu, ID_RECENTFILES_MRU5, false, &info);
|
||||
}
|
||||
}
|
||||
|
||||
wstring MainWindow::SelectROM(wstring filepath)
|
||||
{
|
||||
if(filepath.empty()) {
|
||||
wchar_t buffer[2000];
|
||||
|
||||
OPENFILENAME ofn;
|
||||
ZeroMemory(&ofn, sizeof(ofn));
|
||||
ofn.lStructSize = sizeof(ofn);
|
||||
ofn.hwndOwner = nullptr;
|
||||
ofn.lpstrFile = buffer;
|
||||
ofn.lpstrFile[0] = '\0';
|
||||
ofn.nMaxFile = sizeof(buffer);
|
||||
ofn.lpstrFilter = L"NES Roms\0*.NES\0All\0*.*";
|
||||
ofn.nFilterIndex = 1;
|
||||
ofn.lpstrFileTitle = nullptr;
|
||||
ofn.nMaxFileTitle = 0;
|
||||
ofn.lpstrInitialDir = nullptr;
|
||||
ofn.Flags = OFN_PATHMUSTEXIST | OFN_FILEMUSTEXIST;
|
||||
|
||||
GetOpenFileName(&ofn);
|
||||
|
||||
filepath = wstring(buffer);
|
||||
}
|
||||
|
||||
if(!filepath.empty()) {
|
||||
wstring filename = filepath.substr(filepath.find_last_of(L"/\\") + 1);
|
||||
SetWindowText(_hWnd, (wstring(_windowName) + L": " + filename).c_str());
|
||||
|
||||
AddToMRU(filepath);
|
||||
|
||||
Start(filename);
|
||||
}
|
||||
|
||||
return filepath;
|
||||
}
|
||||
|
@ -200,8 +366,7 @@ namespace NES {
|
|||
SetMenuEnabled(ID_NES_STOP, true);
|
||||
SetMenuEnabled(ID_NES_RESUME, false);
|
||||
|
||||
SetMenuEnabled(ID_FILE_QUICKLOAD, true);
|
||||
|
||||
SetMenuEnabled(ID_FILE_QUICKLOAD, true);
|
||||
SetMenuEnabled(ID_FILE_QUICKSAVE, true);
|
||||
|
||||
_renderer->ClearFlags(UIFlags::ShowPauseScreen);
|
||||
|
@ -275,8 +440,10 @@ namespace NES {
|
|||
{
|
||||
if(ToggleMenuCheck(ID_OPTIONS_LIMITFPS)) {
|
||||
Console::SetFlags(EmulationFlags::LimitFPS);
|
||||
ConfigManager::SetValue(Config::LimitFPS, true);
|
||||
} else {
|
||||
Console::ClearFlags(EmulationFlags::LimitFPS);
|
||||
ConfigManager::SetValue(Config::LimitFPS, false);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -284,8 +451,10 @@ namespace NES {
|
|||
{
|
||||
if(ToggleMenuCheck(ID_OPTIONS_SHOWFPS)) {
|
||||
_renderer->SetFlags(UIFlags::ShowFPS);
|
||||
ConfigManager::SetValue(Config::ShowFPS, true);
|
||||
} else {
|
||||
_renderer->ClearFlags(UIFlags::ShowFPS);
|
||||
ConfigManager::SetValue(Config::ShowFPS, false);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -388,6 +557,18 @@ namespace NES {
|
|||
std::cout << "------------------------" << std::endl;
|
||||
}
|
||||
|
||||
void MainWindow::SelectSaveSlot(int slot)
|
||||
{
|
||||
_currentSaveSlot = slot % 5;
|
||||
_renderer->DisplayMessage(L"Savestate slot: " + std::to_wstring(_currentSaveSlot + 1), 3000);
|
||||
|
||||
SetMenuCheck(ID_SAVESTATESLOT_1, _currentSaveSlot == 0);
|
||||
SetMenuCheck(ID_SAVESTATESLOT_2, _currentSaveSlot == 1);
|
||||
SetMenuCheck(ID_SAVESTATESLOT_3, _currentSaveSlot == 2);
|
||||
SetMenuCheck(ID_SAVESTATESLOT_4, _currentSaveSlot == 3);
|
||||
SetMenuCheck(ID_SAVESTATESLOT_5, _currentSaveSlot == 4);
|
||||
}
|
||||
|
||||
LRESULT CALLBACK MainWindow::WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
|
||||
{
|
||||
static MainWindow *mainWindow = MainWindow::GetInstance();
|
||||
|
@ -404,18 +585,54 @@ namespace NES {
|
|||
switch (wmId) {
|
||||
case ID_FILE_OPEN:
|
||||
filename = mainWindow->SelectROM();
|
||||
if(filename.length() > 0) {
|
||||
mainWindow->Start(filename);
|
||||
break;
|
||||
|
||||
case ID_SAVESTATESLOT_1:
|
||||
mainWindow->SelectSaveSlot(0);
|
||||
break;
|
||||
case ID_SAVESTATESLOT_2:
|
||||
mainWindow->SelectSaveSlot(1);
|
||||
break;
|
||||
case ID_SAVESTATESLOT_3:
|
||||
mainWindow->SelectSaveSlot(2);
|
||||
break;
|
||||
case ID_SAVESTATESLOT_4:
|
||||
mainWindow->SelectSaveSlot(3);
|
||||
break;
|
||||
case ID_SAVESTATESLOT_5:
|
||||
mainWindow->SelectSaveSlot(4);
|
||||
break;
|
||||
|
||||
case ID_RECENTFILES_MRU1:
|
||||
mainWindow->SelectROM(ConfigManager::GetValue<wstring>(Config::MRU0));
|
||||
break;
|
||||
case ID_RECENTFILES_MRU2:
|
||||
mainWindow->SelectROM(ConfigManager::GetValue<wstring>(Config::MRU1));
|
||||
break;
|
||||
case ID_RECENTFILES_MRU3:
|
||||
mainWindow->SelectROM(ConfigManager::GetValue<wstring>(Config::MRU2));
|
||||
break;
|
||||
case ID_RECENTFILES_MRU4:
|
||||
mainWindow->SelectROM(ConfigManager::GetValue<wstring>(Config::MRU3));
|
||||
break;
|
||||
case ID_RECENTFILES_MRU5:
|
||||
mainWindow->SelectROM(ConfigManager::GetValue<wstring>(Config::MRU4));
|
||||
break;
|
||||
|
||||
case ID_FILE_QUICKLOAD:
|
||||
if(mainWindow->_console->LoadState(mainWindow->_currentROM + L".ss" + std::to_wstring(mainWindow->_currentSaveSlot + 1))) {
|
||||
mainWindow->_renderer->DisplayMessage(L"State loaded.", 3000);
|
||||
} else {
|
||||
mainWindow->_renderer->DisplayMessage(L"Slot is empty.", 3000);
|
||||
}
|
||||
break;
|
||||
case ID_FILE_QUICKLOAD:
|
||||
mainWindow->_console->LoadState(mainWindow->_currentROM + L".svs");
|
||||
mainWindow->_renderer->DisplayMessage(L"State loaded.", 3000);
|
||||
break;
|
||||
case ID_FILE_QUICKSAVE:
|
||||
mainWindow->_console->SaveState(mainWindow->_currentROM + L".svs");
|
||||
mainWindow->_console->SaveState(mainWindow->_currentROM + L".ss" + std::to_wstring(mainWindow->_currentSaveSlot + 1));
|
||||
mainWindow->_renderer->DisplayMessage(L"State saved.", 3000);
|
||||
break;
|
||||
case ID_CHANGESLOT:
|
||||
mainWindow->SelectSaveSlot(mainWindow->_currentSaveSlot + 1);
|
||||
break;
|
||||
case ID_FILE_EXIT:
|
||||
DestroyWindow(hWnd);
|
||||
break;
|
||||
|
@ -439,6 +656,9 @@ namespace NES {
|
|||
case ID_OPTIONS_SHOWFPS:
|
||||
mainWindow->ShowFPS_Click();
|
||||
break;
|
||||
case ID_OPTIONS_INPUT:
|
||||
DialogBox(nullptr, MAKEINTRESOURCE(IDD_INPUTCONFIG), hWnd, InputConfig);
|
||||
break;
|
||||
|
||||
case ID_TESTS_RUNTESTS:
|
||||
mainWindow->RunTests();
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
#include "Renderer.h"
|
||||
#include "SoundManager.h"
|
||||
#include "../Core/Console.h"
|
||||
#include "../Utilities/ConfigManager.h"
|
||||
|
||||
namespace NES {
|
||||
class MainWindow
|
||||
|
@ -20,11 +21,17 @@ namespace NES {
|
|||
unique_ptr<thread> _emuThread;
|
||||
wstring _currentROM;
|
||||
|
||||
int _currentSaveSlot = 0;
|
||||
|
||||
private:
|
||||
bool Initialize();
|
||||
HRESULT InitWindow();
|
||||
static LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam);
|
||||
static INT_PTR CALLBACK About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);
|
||||
static INT_PTR CALLBACK InputConfig(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);
|
||||
static INT_PTR CALLBACK ControllerSetup(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);
|
||||
|
||||
static wstring MainWindow::VKToString(int vk);
|
||||
|
||||
static MainWindow* GetInstance() { return MainWindow::Instance; }
|
||||
|
||||
|
@ -43,13 +50,17 @@ namespace NES {
|
|||
bool SetMenuCheck(int resourceID, bool checked);
|
||||
bool ToggleMenuCheck(int resourceID);
|
||||
|
||||
wstring SelectROM();
|
||||
wstring SelectROM(wstring filepath = L"");
|
||||
void Start(wstring romFilename);
|
||||
void Reset();
|
||||
void Stop(bool powerOff);
|
||||
|
||||
void InitializeOptions();
|
||||
|
||||
void SelectSaveSlot(int slot);
|
||||
void AddToMRU(wstring romFilename);
|
||||
void UpdateMRUMenu();
|
||||
|
||||
public:
|
||||
MainWindow(HINSTANCE hInstance, int nCmdShow) : _hInstance(hInstance), _nCmdShow(nCmdShow)
|
||||
{
|
||||
|
|
BIN
GUI/resource.h
BIN
GUI/resource.h
Binary file not shown.
1
NES.sln
1
NES.sln
|
@ -7,6 +7,7 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Core", "Core\Core.vcxproj",
|
|||
EndProject
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "GUI", "GUI\GUI.vcxproj", "{6675D943-322A-4045-B16C-4756FD32CAF2}"
|
||||
ProjectSection(ProjectDependencies) = postProject
|
||||
{B5330148-E8C7-46BA-B54E-69BE59EA337D} = {B5330148-E8C7-46BA-B54E-69BE59EA337D}
|
||||
{78FEF1A1-6DF1-4CBB-A373-AE6FA7CE5CE0} = {78FEF1A1-6DF1-4CBB-A373-AE6FA7CE5CE0}
|
||||
EndProjectSection
|
||||
EndProject
|
||||
|
|
78
Utilities/ConfigManager.cpp
Normal file
78
Utilities/ConfigManager.cpp
Normal file
|
@ -0,0 +1,78 @@
|
|||
#include "stdafx.h"
|
||||
#include "ConfigManager.h"
|
||||
#include <Shlobj.h>
|
||||
|
||||
string ConfigManager::ConfigTagNames[] = {
|
||||
"ShowFPS",
|
||||
"LimitFPS",
|
||||
"MRU0",
|
||||
"MRU1",
|
||||
"MRU2",
|
||||
"MRU3",
|
||||
"MRU4",
|
||||
"Player1_ButtonA",
|
||||
"Player1_ButtonB",
|
||||
"Player1_Select",
|
||||
"Player1_Start",
|
||||
"Player1_Up",
|
||||
"Player1_Down",
|
||||
"Player1_Left",
|
||||
"Player1_Right",
|
||||
};
|
||||
|
||||
shared_ptr<ConfigManager> ConfigManager::Instance = nullptr;
|
||||
|
||||
void ConfigManager::LoadConfigFile()
|
||||
{
|
||||
PWSTR pathName;
|
||||
SHGetKnownFolderPath(FOLDERID_LocalAppData, 0, nullptr, &pathName);
|
||||
|
||||
_configFilePath = wstring((wchar_t*)pathName) + L"\\NesEMU\\Settings.xml";
|
||||
CreateDirectory((wstring((wchar_t*)pathName) + L"\\NesEMU").c_str(), nullptr);
|
||||
|
||||
CoTaskMemFree(pathName);
|
||||
|
||||
ifstream configFile(_configFilePath, ifstream::in);
|
||||
|
||||
if(configFile) {
|
||||
configFile.close();
|
||||
_xmlDocument.LoadFile(utf8util::UTF8FromUTF16(_configFilePath).c_str());
|
||||
} else {
|
||||
_xmlDocument.InsertFirstChild(_xmlDocument.NewDeclaration());
|
||||
|
||||
tinyxml2::XMLElement* mainNode = _xmlDocument.NewElement("Config");
|
||||
_xmlDocument.InsertEndChild(mainNode);
|
||||
}
|
||||
}
|
||||
|
||||
tinyxml2::XMLElement* ConfigManager::GetConfigNode()
|
||||
{
|
||||
return _xmlDocument.LastChildElement();
|
||||
}
|
||||
|
||||
tinyxml2::XMLElement* ConfigManager::GetNode(Config config)
|
||||
{
|
||||
if(!_loaded) {
|
||||
LoadConfigFile();
|
||||
_loaded = true;
|
||||
}
|
||||
|
||||
tinyxml2::XMLElement* currentNode = GetConfigNode()->FirstChildElement();
|
||||
tinyxml2::XMLElement* configNode = nullptr;
|
||||
if(currentNode) {
|
||||
do {
|
||||
if(ConfigTagNames[(int)config].compare(currentNode->Name()) == 0) {
|
||||
configNode = currentNode;
|
||||
break;
|
||||
}
|
||||
}
|
||||
while(currentNode = currentNode->NextSiblingElement());
|
||||
}
|
||||
|
||||
if(!configNode) {
|
||||
configNode = _xmlDocument.NewElement(ConfigTagNames[(int)config].c_str());
|
||||
GetConfigNode()->InsertEndChild(configNode);
|
||||
}
|
||||
|
||||
return configNode;
|
||||
}
|
106
Utilities/ConfigManager.h
Normal file
106
Utilities/ConfigManager.h
Normal file
|
@ -0,0 +1,106 @@
|
|||
#pragma once
|
||||
|
||||
#include "stdafx.h"
|
||||
#include "../Utilities/tinyxml2.h"
|
||||
#include "utf8conv.h"
|
||||
|
||||
enum class Config
|
||||
{
|
||||
ShowFPS = 0,
|
||||
LimitFPS,
|
||||
MRU0,
|
||||
MRU1,
|
||||
MRU2,
|
||||
MRU3,
|
||||
MRU4,
|
||||
Player1_ButtonA,
|
||||
Player1_ButtonB,
|
||||
Player1_Select,
|
||||
Player1_Start,
|
||||
Player1_Up,
|
||||
Player1_Down,
|
||||
Player1_Left,
|
||||
Player1_Right,
|
||||
};
|
||||
|
||||
class ConfigManager
|
||||
{
|
||||
private:
|
||||
static shared_ptr<ConfigManager> Instance;
|
||||
static string ConfigTagNames[256];
|
||||
|
||||
bool _loaded;
|
||||
wstring _configFilePath;
|
||||
tinyxml2::XMLDocument _xmlDocument;
|
||||
ConfigManager() { }
|
||||
|
||||
void LoadConfigFile();
|
||||
|
||||
tinyxml2::XMLElement* GetNode(Config config);
|
||||
tinyxml2::XMLElement* GetConfigNode();
|
||||
|
||||
template<typename T>
|
||||
T GetValueAttribute(tinyxml2::XMLElement* element);
|
||||
|
||||
template<>
|
||||
int GetValueAttribute<int>(tinyxml2::XMLElement* element)
|
||||
{
|
||||
return element->IntAttribute("value");
|
||||
}
|
||||
|
||||
template<>
|
||||
bool GetValueAttribute<bool>(tinyxml2::XMLElement* element)
|
||||
{
|
||||
return element->BoolAttribute("value");
|
||||
}
|
||||
|
||||
template<>
|
||||
double GetValueAttribute<double>(tinyxml2::XMLElement* element)
|
||||
{
|
||||
return element->DoubleAttribute("value");
|
||||
}
|
||||
|
||||
template<>
|
||||
wstring GetValueAttribute<wstring>(tinyxml2::XMLElement* element)
|
||||
{
|
||||
if(element->Attribute("value")) {
|
||||
return utf8util::UTF16FromUTF8(string(element->Attribute("value")));
|
||||
} else {
|
||||
return L"";
|
||||
}
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void SetValueAttribute(tinyxml2::XMLElement* element, T value)
|
||||
{
|
||||
element->SetAttribute("value", value);
|
||||
}
|
||||
|
||||
template<>
|
||||
void SetValueAttribute<std::wstring>(tinyxml2::XMLElement* element, std::wstring value)
|
||||
{
|
||||
element->SetAttribute("value", utf8util::UTF8FromUTF16(value).c_str());
|
||||
}
|
||||
|
||||
public:
|
||||
static shared_ptr<ConfigManager> GetInstance()
|
||||
{
|
||||
if(ConfigManager::Instance == nullptr) {
|
||||
ConfigManager::Instance.reset(new ConfigManager());
|
||||
}
|
||||
return ConfigManager::Instance;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
static T GetValue(Config config)
|
||||
{
|
||||
return GetInstance()->GetValueAttribute<T>(GetInstance()->GetNode(config));
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
static void SetValue(Config config, T value)
|
||||
{
|
||||
GetInstance()->SetValueAttribute(GetInstance()->GetNode(config), value);
|
||||
GetInstance()->_xmlDocument.SaveFile(utf8util::UTF8FromUTF16(GetInstance()->_configFilePath).c_str(), false);
|
||||
}
|
||||
};
|
|
@ -5,3 +5,13 @@
|
|||
#define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers
|
||||
|
||||
#include <windows.h>
|
||||
|
||||
#include <string>
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
#include <memory>
|
||||
|
||||
using std::shared_ptr;
|
||||
using std::ifstream;
|
||||
using std::string;
|
||||
using std::wstring;
|
||||
|
|
2188
Utilities/tinyxml2.cpp
Normal file
2188
Utilities/tinyxml2.cpp
Normal file
File diff suppressed because it is too large
Load diff
2076
Utilities/tinyxml2.h
Normal file
2076
Utilities/tinyxml2.h
Normal file
File diff suppressed because it is too large
Load diff
162
Utilities/utf8conv.h
Normal file
162
Utilities/utf8conv.h
Normal file
|
@ -0,0 +1,162 @@
|
|||
//////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// FILE: utf8conv.h
|
||||
//
|
||||
// Header file defining prototypes of helper functions for converting
|
||||
// strings between Unicode UTF-8 and UTF-16.
|
||||
// (The implementation file is "utf8conv_inl.h").
|
||||
//
|
||||
// UTF-8 text is stored in std::string;
|
||||
// UTF-16 text is stored in std::wstring.
|
||||
//
|
||||
// This code just uses Win32 Platform SDK and C++ standard library;
|
||||
// so it can be used also with the Express editions of Visual Studio.
|
||||
//
|
||||
//
|
||||
// Original code: February 4th, 2011
|
||||
// Last update: October 15th, 2011
|
||||
//
|
||||
// - Added more information to the utf8_conversion_error class
|
||||
// (like the return code of ::GetLastError());
|
||||
// moreover, the class now derives from std::runtime_error.
|
||||
//
|
||||
// - Added conversion function overloads taking raw C strings as input.
|
||||
// (This is more efficient when there are raw C strings already
|
||||
// available, because it avoids the creation of temporary
|
||||
// new std::[w]string's.)
|
||||
//
|
||||
// - UTF-8 conversion functions now detect invalid UTF-8 sequences
|
||||
// thanks to MB_ERR_INVALID_CHARS flag, and throw an exception
|
||||
// in this case.
|
||||
//
|
||||
//
|
||||
// by Giovanni Dicanio <gdicanio@mvps.org>
|
||||
//
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
#pragma once
|
||||
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
// INCLUDES
|
||||
//------------------------------------------------------------------------
|
||||
|
||||
#include <stdexcept> // std::runtime_error
|
||||
#include <string> // STL string classes
|
||||
|
||||
|
||||
|
||||
namespace utf8util {
|
||||
|
||||
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
// Exception class representing an error occurred during UTF-8 conversion.
|
||||
//------------------------------------------------------------------------
|
||||
class utf8_conversion_error
|
||||
: public std::runtime_error
|
||||
{
|
||||
public:
|
||||
|
||||
//
|
||||
// Naming convention note:
|
||||
// -----------------------
|
||||
//
|
||||
// This exception class is derived from std::runtime_error class,
|
||||
// so I chose to use the same naming convention of STL classes
|
||||
// (e.g. do_something_intersting() instead of DoSomethingInteresting()).
|
||||
//
|
||||
|
||||
|
||||
// Error code type
|
||||
// (a DWORD, as the return value type from ::GetLastError())
|
||||
typedef unsigned long error_code_type;
|
||||
|
||||
// Type of conversion
|
||||
enum conversion_type
|
||||
{
|
||||
conversion_utf8_from_utf16, // UTF-16 ---> UTF-8
|
||||
conversion_utf16_from_utf8 // UTF-8 ---> UTF-16
|
||||
};
|
||||
|
||||
|
||||
// Constructs an UTF-8 conversion error exception
|
||||
// with a raw C string message, conversion type and error code.
|
||||
utf8_conversion_error(
|
||||
const char * message,
|
||||
conversion_type conversion,
|
||||
error_code_type error_code
|
||||
);
|
||||
|
||||
|
||||
// Constructs an UTF-8 conversion error exception
|
||||
// with a std::string message, conversion type and error code.
|
||||
utf8_conversion_error(
|
||||
const std::string & message,
|
||||
conversion_type conversion,
|
||||
error_code_type error_code
|
||||
);
|
||||
|
||||
|
||||
// Returns the type of conversion (UTF-8 from UTF-16, or vice versa)
|
||||
conversion_type conversion() const;
|
||||
|
||||
|
||||
// Returns the error code occurred during the conversion
|
||||
// (which is typically the return value of ::GetLastError()).
|
||||
error_code_type error_code() const;
|
||||
|
||||
|
||||
|
||||
//
|
||||
// IMPLEMENTATION
|
||||
//
|
||||
private:
|
||||
conversion_type m_conversion; // kind of conversion
|
||||
error_code_type m_error_code; // error code
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
|
||||
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
// Converts a string from UTF-8 to UTF-16.
|
||||
// On error, can throw an utf8_conversion_error exception.
|
||||
//------------------------------------------------------------------------
|
||||
std::wstring UTF16FromUTF8(const std::string & utf8);
|
||||
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
// Converts a raw C string from UTF-8 to UTF-16.
|
||||
// On error, can throw an utf8_conversion_error exception.
|
||||
// If the input pointer is NULL, an empty string is returned.
|
||||
//------------------------------------------------------------------------
|
||||
std::wstring UTF16FromUTF8(const char * utf8);
|
||||
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
// Converts a string from UTF-16 to UTF-8.
|
||||
// On error, can throw an utf8_conversion_error exception.
|
||||
//------------------------------------------------------------------------
|
||||
std::string UTF8FromUTF16(const std::wstring & utf16);
|
||||
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
// Converts a raw C string from UTF-16 to UTF-8.
|
||||
// On error, can throw an utf8_conversion_error exception.
|
||||
// If the input pointer is NULL, an empty string is returned.
|
||||
//------------------------------------------------------------------------
|
||||
std::string UTF8FromUTF16(const wchar_t * utf16);
|
||||
|
||||
|
||||
} // namespace utf8util
|
||||
|
||||
|
||||
|
||||
#include "utf8conv_inl.h" // inline implementations
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
368
Utilities/utf8conv_inl.h
Normal file
368
Utilities/utf8conv_inl.h
Normal file
|
@ -0,0 +1,368 @@
|
|||
//////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// FILE: utf8conv_inl.h
|
||||
//
|
||||
// by Giovanni Dicanio <gdicanio@mvps.org>
|
||||
//
|
||||
// Private header file containing implementations of inline functions.
|
||||
// The public header file for this module is "utf8conv.h";
|
||||
// users should *not* #include this private header file directly.
|
||||
//
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#pragma once
|
||||
|
||||
|
||||
#include <string.h> // strlen()
|
||||
|
||||
#include <Windows.h> // Win32 Platform SDK main header
|
||||
|
||||
|
||||
|
||||
namespace utf8util {
|
||||
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
// Implementation of utf8_conversion_error class methods
|
||||
//------------------------------------------------------------------------
|
||||
|
||||
inline utf8_conversion_error::utf8_conversion_error(
|
||||
const char * message,
|
||||
conversion_type conversion,
|
||||
error_code_type error_code
|
||||
) :
|
||||
std::runtime_error(message),
|
||||
m_conversion(conversion),
|
||||
m_error_code(error_code)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
inline utf8_conversion_error::utf8_conversion_error(
|
||||
const std::string & message,
|
||||
conversion_type conversion,
|
||||
error_code_type error_code
|
||||
) :
|
||||
std::runtime_error(message),
|
||||
m_conversion(conversion),
|
||||
m_error_code(error_code)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
inline utf8_conversion_error::conversion_type utf8_conversion_error::conversion() const
|
||||
{
|
||||
return m_conversion;
|
||||
}
|
||||
|
||||
|
||||
inline utf8_conversion_error::error_code_type utf8_conversion_error::error_code() const
|
||||
{
|
||||
return m_error_code;
|
||||
}
|
||||
|
||||
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
// Implementation of module functions
|
||||
//------------------------------------------------------------------------
|
||||
|
||||
|
||||
inline std::wstring UTF16FromUTF8(const std::string & utf8)
|
||||
{
|
||||
//
|
||||
// Special case of empty input string
|
||||
//
|
||||
if (utf8.empty())
|
||||
return std::wstring();
|
||||
|
||||
|
||||
// Fail if an invalid input character is encountered
|
||||
const DWORD conversionFlags = MB_ERR_INVALID_CHARS;
|
||||
|
||||
|
||||
//
|
||||
// Get length (in wchar_t's) of resulting UTF-16 string
|
||||
//
|
||||
const int utf16Length = ::MultiByteToWideChar(
|
||||
CP_UTF8, // convert from UTF-8
|
||||
conversionFlags, // flags
|
||||
utf8.data(), // source UTF-8 string
|
||||
utf8.length(), // length (in chars) of source UTF-8 string
|
||||
NULL, // unused - no conversion done in this step
|
||||
0 // request size of destination buffer, in wchar_t's
|
||||
);
|
||||
if (utf16Length == 0)
|
||||
{
|
||||
// Error
|
||||
DWORD error = ::GetLastError();
|
||||
|
||||
throw utf8_conversion_error(
|
||||
(error == ERROR_NO_UNICODE_TRANSLATION) ?
|
||||
"Invalid UTF-8 sequence found in input string." :
|
||||
"Can't get length of UTF-16 string (MultiByteToWideChar failed).",
|
||||
utf8_conversion_error::conversion_utf16_from_utf8,
|
||||
error);
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// Allocate destination buffer for UTF-16 string
|
||||
//
|
||||
std::wstring utf16;
|
||||
utf16.resize(utf16Length);
|
||||
|
||||
|
||||
//
|
||||
// Do the conversion from UTF-8 to UTF-16
|
||||
//
|
||||
if ( ! ::MultiByteToWideChar(
|
||||
CP_UTF8, // convert from UTF-8
|
||||
0, // validation was done in previous call,
|
||||
// so speed up things with default flags
|
||||
utf8.data(), // source UTF-8 string
|
||||
utf8.length(), // length (in chars) of source UTF-8 string
|
||||
&utf16[0], // destination buffer
|
||||
utf16.length() // size of destination buffer, in wchar_t's
|
||||
) )
|
||||
{
|
||||
// Error
|
||||
DWORD error = ::GetLastError();
|
||||
throw utf8_conversion_error(
|
||||
"Can't convert string from UTF-8 to UTF-16 (MultiByteToWideChar failed).",
|
||||
utf8_conversion_error::conversion_utf16_from_utf8,
|
||||
error);
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// Return resulting UTF-16 string
|
||||
//
|
||||
return utf16;
|
||||
}
|
||||
|
||||
|
||||
|
||||
inline std::wstring UTF16FromUTF8(const char * utf8)
|
||||
{
|
||||
//
|
||||
// Special case of empty input string
|
||||
//
|
||||
if (utf8 == NULL || *utf8 == '\0')
|
||||
return std::wstring();
|
||||
|
||||
|
||||
// Prefetch the length of the input UTF-8 string
|
||||
const int utf8Length = static_cast<int>(strlen(utf8));
|
||||
|
||||
// Fail if an invalid input character is encountered
|
||||
const DWORD conversionFlags = MB_ERR_INVALID_CHARS;
|
||||
|
||||
//
|
||||
// Get length (in wchar_t's) of resulting UTF-16 string
|
||||
//
|
||||
const int utf16Length = ::MultiByteToWideChar(
|
||||
CP_UTF8, // convert from UTF-8
|
||||
conversionFlags, // flags
|
||||
utf8, // source UTF-8 string
|
||||
utf8Length, // length (in chars) of source UTF-8 string
|
||||
NULL, // unused - no conversion done in this step
|
||||
0 // request size of destination buffer, in wchar_t's
|
||||
);
|
||||
if (utf16Length == 0)
|
||||
{
|
||||
// Error
|
||||
DWORD error = ::GetLastError();
|
||||
throw utf8_conversion_error(
|
||||
(error == ERROR_NO_UNICODE_TRANSLATION) ?
|
||||
"Invalid UTF-8 sequence found in input string." :
|
||||
"Can't get length of UTF-16 string (MultiByteToWideChar failed).",
|
||||
utf8_conversion_error::conversion_utf16_from_utf8,
|
||||
error);
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// Allocate destination buffer for UTF-16 string
|
||||
//
|
||||
std::wstring utf16;
|
||||
utf16.resize(utf16Length);
|
||||
|
||||
|
||||
//
|
||||
// Do the conversion from UTF-8 to UTF-16
|
||||
//
|
||||
if ( ! ::MultiByteToWideChar(
|
||||
CP_UTF8, // convert from UTF-8
|
||||
0, // validation was done in previous call,
|
||||
// so speed up things with default flags
|
||||
utf8, // source UTF-8 string
|
||||
utf8Length, // length (in chars) of source UTF-8 string
|
||||
&utf16[0], // destination buffer
|
||||
utf16.length() // size of destination buffer, in wchar_t's
|
||||
) )
|
||||
{
|
||||
// Error
|
||||
DWORD error = ::GetLastError();
|
||||
throw utf8_conversion_error(
|
||||
"Can't convert string from UTF-8 to UTF-16 (MultiByteToWideChar failed).",
|
||||
utf8_conversion_error::conversion_utf16_from_utf8,
|
||||
error);
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// Return resulting UTF-16 string
|
||||
//
|
||||
return utf16;
|
||||
}
|
||||
|
||||
|
||||
|
||||
inline std::string UTF8FromUTF16(const std::wstring & utf16)
|
||||
{
|
||||
//
|
||||
// Special case of empty input string
|
||||
//
|
||||
if (utf16.empty())
|
||||
return std::string();
|
||||
|
||||
|
||||
//
|
||||
// Get length (in chars) of resulting UTF-8 string
|
||||
//
|
||||
const int utf8Length = ::WideCharToMultiByte(
|
||||
CP_UTF8, // convert to UTF-8
|
||||
0, // default flags
|
||||
utf16.data(), // source UTF-16 string
|
||||
utf16.length(), // source string length, in wchar_t's,
|
||||
NULL, // unused - no conversion required in this step
|
||||
0, // request buffer size
|
||||
NULL, NULL // unused
|
||||
);
|
||||
if (utf8Length == 0)
|
||||
{
|
||||
// Error
|
||||
DWORD error = ::GetLastError();
|
||||
throw utf8_conversion_error(
|
||||
"Can't get length of UTF-8 string (WideCharToMultiByte failed).",
|
||||
utf8_conversion_error::conversion_utf8_from_utf16,
|
||||
error);
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// Allocate destination buffer for UTF-8 string
|
||||
//
|
||||
std::string utf8;
|
||||
utf8.resize(utf8Length);
|
||||
|
||||
|
||||
//
|
||||
// Do the conversion from UTF-16 to UTF-8
|
||||
//
|
||||
if ( ! ::WideCharToMultiByte(
|
||||
CP_UTF8, // convert to UTF-8
|
||||
0, // default flags
|
||||
utf16.data(), // source UTF-16 string
|
||||
utf16.length(), // source string length, in wchar_t's,
|
||||
&utf8[0], // destination buffer
|
||||
utf8.length(), // destination buffer size, in chars
|
||||
NULL, NULL // unused
|
||||
) )
|
||||
{
|
||||
// Error
|
||||
DWORD error = ::GetLastError();
|
||||
throw utf8_conversion_error(
|
||||
"Can't convert string from UTF-16 to UTF-8 (WideCharToMultiByte failed).",
|
||||
utf8_conversion_error::conversion_utf8_from_utf16,
|
||||
error);
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// Return resulting UTF-8 string
|
||||
//
|
||||
return utf8;
|
||||
}
|
||||
|
||||
|
||||
|
||||
inline std::string UTF8FromUTF16(const wchar_t * utf16)
|
||||
{
|
||||
//
|
||||
// Special case of empty input string
|
||||
//
|
||||
if (utf16 == NULL || *utf16 == L'\0')
|
||||
return std::string();
|
||||
|
||||
|
||||
// Prefetch the length of the input UTF-16 string
|
||||
const int utf16Length = static_cast<int>(wcslen(utf16));
|
||||
|
||||
|
||||
//
|
||||
// Get length (in chars) of resulting UTF-8 string
|
||||
//
|
||||
const int utf8Length = ::WideCharToMultiByte(
|
||||
CP_UTF8, // convert to UTF-8
|
||||
0, // default flags
|
||||
utf16, // source UTF-16 string
|
||||
utf16Length, // source string length, in wchar_t's,
|
||||
NULL, // unused - no conversion required in this step
|
||||
0, // request buffer size
|
||||
NULL, NULL // unused
|
||||
);
|
||||
if (utf8Length == 0)
|
||||
{
|
||||
// Error
|
||||
DWORD error = ::GetLastError();
|
||||
throw utf8_conversion_error(
|
||||
"Can't get length of UTF-8 string (WideCharToMultiByte failed).",
|
||||
utf8_conversion_error::conversion_utf8_from_utf16,
|
||||
error);
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// Allocate destination buffer for UTF-8 string
|
||||
//
|
||||
std::string utf8;
|
||||
utf8.resize(utf8Length);
|
||||
|
||||
|
||||
//
|
||||
// Do the conversion from UTF-16 to UTF-8
|
||||
//
|
||||
if ( ! ::WideCharToMultiByte(
|
||||
CP_UTF8, // convert to UTF-8
|
||||
0, // default flags
|
||||
utf16, // source UTF-16 string
|
||||
utf16Length, // source string length, in wchar_t's,
|
||||
&utf8[0], // destination buffer
|
||||
utf8.length(), // destination buffer size, in chars
|
||||
NULL, NULL // unused
|
||||
) )
|
||||
{
|
||||
// Error
|
||||
DWORD error = ::GetLastError();
|
||||
throw utf8_conversion_error(
|
||||
"Can't convert string from UTF-16 to UTF-8 (WideCharToMultiByte failed).",
|
||||
utf8_conversion_error::conversion_utf8_from_utf16,
|
||||
error);
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// Return resulting UTF-8 string
|
||||
//
|
||||
return utf8;
|
||||
}
|
||||
|
||||
|
||||
|
||||
} // namespace utf8util
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
Loading…
Add table
Reference in a new issue