From f704bbbd58705483c1c09555e5b1d1fae3fed6de Mon Sep 17 00:00:00 2001 From: hieuxyz00 Date: Thu, 30 Jan 2025 17:14:15 +0700 Subject: [PATCH] w --- CasioEmuMsvc/CasioEmuMsvc.vcxproj | 2 +- CasioEmuMsvc/CasioEmuMsvc.vcxproj.filters | 2 +- CasioEmuMsvc/Ext/SysDialog.cpp | 459 +++- CasioEmuMsvc/Ext/SysDialog.h | 17 +- CasioEmuMsvc/Gui/AddressWindow.cpp | 4 +- CasioEmuMsvc/Gui/CallAnalysis.cpp | 2 +- CasioEmuMsvc/Gui/CodeViewer.cpp | 4 +- CasioEmuMsvc/Gui/Injector.cpp | 474 ++-- CasioEmuMsvc/Gui/Injector.hpp | 45 +- CasioEmuMsvc/Gui/LabelViewer.cpp | 7 +- CasioEmuMsvc/Gui/Localization.cpp | 2 +- CasioEmuMsvc/Gui/Localization.h | 470 ++-- CasioEmuMsvc/Gui/MemBreakPoint.cpp | 5 +- CasioEmuMsvc/Gui/Theme.cpp | 106 +- CasioEmuMsvc/Gui/Theme.h | 5 +- CasioEmuMsvc/Gui/UIScaling.cpp | 9 +- CasioEmuMsvc/Gui/UIScaling.h | 135 +- CasioEmuMsvc/Gui/Ui.cpp | 285 +-- CasioEmuMsvc/Gui/Ui.hpp | 60 + CasioEmuMsvc/Gui/VariableWindow.cpp | 6 +- CasioEmuMsvc/Gui/WatchWindow.cpp | 6 +- CasioEmuMsvc/Gui/hex.hpp | 3 +- CasioEmuMsvc/Gui/ui.hpp | 38 - CasioEmuMsvc/Peripheral/Screen.cpp | 2388 +++++++++++---------- CasioEmuMsvc/StartupUi/StartupUi.cpp | 673 ++++-- CasioEmuMsvc/casioemu.cpp | 2 +- CasioEmuMsvc/locales/en_US.lc | 30 +- CasioEmuMsvc/locales/{vn.lc => vi_VN.lc} | 94 +- CasioEmuMsvc/locales/zh_CN.lc | 23 +- 29 files changed, 3164 insertions(+), 2192 deletions(-) create mode 100644 CasioEmuMsvc/Gui/Ui.hpp delete mode 100644 CasioEmuMsvc/Gui/ui.hpp rename CasioEmuMsvc/locales/{vn.lc => vi_VN.lc} (62%) diff --git a/CasioEmuMsvc/CasioEmuMsvc.vcxproj b/CasioEmuMsvc/CasioEmuMsvc.vcxproj index 7c82203..56181a8 100644 --- a/CasioEmuMsvc/CasioEmuMsvc.vcxproj +++ b/CasioEmuMsvc/CasioEmuMsvc.vcxproj @@ -462,7 +462,7 @@ PreserveNewest - + PreserveNewest diff --git a/CasioEmuMsvc/CasioEmuMsvc.vcxproj.filters b/CasioEmuMsvc/CasioEmuMsvc.vcxproj.filters index ce9706c..4c8c67b 100644 --- a/CasioEmuMsvc/CasioEmuMsvc.vcxproj.filters +++ b/CasioEmuMsvc/CasioEmuMsvc.vcxproj.filters @@ -465,7 +465,7 @@ - + diff --git a/CasioEmuMsvc/Ext/SysDialog.cpp b/CasioEmuMsvc/Ext/SysDialog.cpp index 3dab294..ce199d3 100644 --- a/CasioEmuMsvc/Ext/SysDialog.cpp +++ b/CasioEmuMsvc/Ext/SysDialog.cpp @@ -7,126 +7,361 @@ #include void SystemDialogs::OpenFileDialog(std::function callback) { - IFileDialog* pfd; - HRESULT hr = CoCreateInstance(CLSID_FileOpenDialog, NULL, CLSCTX_INPROC_SERVER, - IID_PPV_ARGS(&pfd)); - if (SUCCEEDED(hr)) { - // 设置选项 - DWORD dwOptions; - if (SUCCEEDED(pfd->GetOptions(&dwOptions))) { - pfd->SetOptions(dwOptions | FOS_FORCEFILESYSTEM); - } - - // 设置文件类型过滤器 - COMDLG_FILTERSPEC rgSpec[] = { - {L"All Files", L"*.*"}}; - pfd->SetFileTypes(1, rgSpec); - - // 显示对话框 - if (SUCCEEDED(pfd->Show(NULL))) { - IShellItem* psi; - if (SUCCEEDED(pfd->GetResult(&psi))) { - PWSTR path; - if (SUCCEEDED(psi->GetDisplayName(SIGDN_FILESYSPATH, &path))) { - callback(std::filesystem::path(path)); - CoTaskMemFree(path); - } - psi->Release(); - } - } - pfd->Release(); - } + IFileDialog* pfd; + HRESULT hr = CoCreateInstance(CLSID_FileOpenDialog, NULL, CLSCTX_INPROC_SERVER, + IID_PPV_ARGS(&pfd)); + if (SUCCEEDED(hr)) { + DWORD dwOptions; + if (SUCCEEDED(pfd->GetOptions(&dwOptions))) { + pfd->SetOptions(dwOptions | FOS_FORCEFILESYSTEM); + } + + COMDLG_FILTERSPEC rgSpec[] = { + {L"All Files", L"*.*"}}; + pfd->SetFileTypes(1, rgSpec); + + if (SUCCEEDED(pfd->Show(NULL))) { + IShellItem* psi; + if (SUCCEEDED(pfd->GetResult(&psi))) { + PWSTR path; + if (SUCCEEDED(psi->GetDisplayName(SIGDN_FILESYSPATH, &path))) { + callback(std::filesystem::path(path)); + CoTaskMemFree(path); + } + psi->Release(); + } + } + pfd->Release(); + } } void SystemDialogs::SaveFileDialog(std::string preferred_name, std::function callback) { - IFileSaveDialog* pfd; - HRESULT hr = CoCreateInstance(CLSID_FileSaveDialog, NULL, CLSCTX_INPROC_SERVER, - IID_PPV_ARGS(&pfd)); - if (SUCCEEDED(hr)) { - // 设置选项 - DWORD dwOptions; - if (SUCCEEDED(pfd->GetOptions(&dwOptions))) { - pfd->SetOptions(dwOptions | FOS_FORCEFILESYSTEM); - } - - // 设置默认文件名 - if (!preferred_name.empty()) { - int needed = MultiByteToWideChar(CP_UTF8, 0, preferred_name.c_str(), -1, NULL, 0); - if (needed > 0) { - std::wstring wname(needed - 1, 0); - MultiByteToWideChar(CP_UTF8, 0, preferred_name.c_str(), -1, wname.data(), needed); - pfd->SetFileName(wname.c_str()); - } - } - - // 设置文件类型过滤器 - COMDLG_FILTERSPEC rgSpec[] = { - {L"All Files", L"*.*"}}; - pfd->SetFileTypes(1, rgSpec); - - // 显示对话框 - if (SUCCEEDED(pfd->Show(NULL))) { - IShellItem* psi; - if (SUCCEEDED(pfd->GetResult(&psi))) { - PWSTR path; - if (SUCCEEDED(psi->GetDisplayName(SIGDN_FILESYSPATH, &path))) { - callback(std::filesystem::path(path)); - CoTaskMemFree(path); - } - psi->Release(); - } - } - pfd->Release(); - } + IFileSaveDialog* pfd; + HRESULT hr = CoCreateInstance(CLSID_FileSaveDialog, NULL, CLSCTX_INPROC_SERVER, + IID_PPV_ARGS(&pfd)); + if (SUCCEEDED(hr)) { + DWORD dwOptions; + if (SUCCEEDED(pfd->GetOptions(&dwOptions))) { + pfd->SetOptions(dwOptions | FOS_FORCEFILESYSTEM); + } + + if (!preferred_name.empty()) { + int needed = MultiByteToWideChar(CP_UTF8, 0, preferred_name.c_str(), -1, NULL, 0); + if (needed > 0) { + std::wstring wname(needed - 1, 0); + MultiByteToWideChar(CP_UTF8, 0, preferred_name.c_str(), -1, wname.data(), needed); + pfd->SetFileName(wname.c_str()); + } + } + + COMDLG_FILTERSPEC rgSpec[] = { + {L"All Files", L"*.*"}}; + pfd->SetFileTypes(1, rgSpec); + + if (SUCCEEDED(pfd->Show(NULL))) { + IShellItem* psi; + if (SUCCEEDED(pfd->GetResult(&psi))) { + PWSTR path; + if (SUCCEEDED(psi->GetDisplayName(SIGDN_FILESYSPATH, &path))) { + callback(std::filesystem::path(path)); + CoTaskMemFree(path); + } + psi->Release(); + } + } + pfd->Release(); + } } void SystemDialogs::OpenFolderDialog(std::function callback) { - IFileDialog* pfd; - HRESULT hr = CoCreateInstance(CLSID_FileOpenDialog, NULL, CLSCTX_INPROC_SERVER, - IID_PPV_ARGS(&pfd)); - if (SUCCEEDED(hr)) { - DWORD dwOptions; - if (SUCCEEDED(pfd->GetOptions(&dwOptions))) { - pfd->SetOptions(dwOptions | FOS_PICKFOLDERS | FOS_FORCEFILESYSTEM); - } - - if (SUCCEEDED(pfd->Show(NULL))) { - IShellItem* psi; - if (SUCCEEDED(pfd->GetResult(&psi))) { - PWSTR path; - if (SUCCEEDED(psi->GetDisplayName(SIGDN_FILESYSPATH, &path))) { - callback(std::filesystem::path(path)); - CoTaskMemFree(path); - } - psi->Release(); - } - } - pfd->Release(); - } + IFileDialog* pfd; + HRESULT hr = CoCreateInstance(CLSID_FileOpenDialog, NULL, CLSCTX_INPROC_SERVER, + IID_PPV_ARGS(&pfd)); + if (SUCCEEDED(hr)) { + DWORD dwOptions; + if (SUCCEEDED(pfd->GetOptions(&dwOptions))) { + pfd->SetOptions(dwOptions | FOS_PICKFOLDERS | FOS_FORCEFILESYSTEM); + } + + if (SUCCEEDED(pfd->Show(NULL))) { + IShellItem* psi; + if (SUCCEEDED(pfd->GetResult(&psi))) { + PWSTR path; + if (SUCCEEDED(psi->GetDisplayName(SIGDN_FILESYSPATH, &path))) { + callback(std::filesystem::path(path)); + CoTaskMemFree(path); + } + psi->Release(); + } + } + pfd->Release(); + } } void SystemDialogs::SaveFolderDialog(std::function callback) { - IFileDialog* pfd; - HRESULT hr = CoCreateInstance(CLSID_FileSaveDialog, NULL, CLSCTX_INPROC_SERVER, - IID_PPV_ARGS(&pfd)); - if (SUCCEEDED(hr)) { - DWORD dwOptions; - if (SUCCEEDED(pfd->GetOptions(&dwOptions))) { - pfd->SetOptions(dwOptions | FOS_PICKFOLDERS | FOS_FORCEFILESYSTEM); - } - - if (SUCCEEDED(pfd->Show(NULL))) { - IShellItem* psi; - if (SUCCEEDED(pfd->GetResult(&psi))) { - PWSTR path; - if (SUCCEEDED(psi->GetDisplayName(SIGDN_FILESYSPATH, &path))) { - callback(std::filesystem::path(path)); - CoTaskMemFree(path); - } - psi->Release(); - } - } - pfd->Release(); - } + IFileDialog* pfd; + HRESULT hr = CoCreateInstance(CLSID_FileSaveDialog, NULL, CLSCTX_INPROC_SERVER, + IID_PPV_ARGS(&pfd)); + if (SUCCEEDED(hr)) { + DWORD dwOptions; + if (SUCCEEDED(pfd->GetOptions(&dwOptions))) { + pfd->SetOptions(dwOptions | FOS_PICKFOLDERS | FOS_FORCEFILESYSTEM); + } + + if (SUCCEEDED(pfd->Show(NULL))) { + IShellItem* psi; + if (SUCCEEDED(pfd->GetResult(&psi))) { + PWSTR path; + if (SUCCEEDED(psi->GetDisplayName(SIGDN_FILESYSPATH, &path))) { + callback(std::filesystem::path(path)); + CoTaskMemFree(path); + } + psi->Release(); + } + } + pfd->Release(); + } } #endif + +#ifdef __ANDROID__ +#include +#include +#include +#include +#include + +// Initialize static members +std::function SystemDialogs::fileOpenCallback; +std::function SystemDialogs::fileSaveCallback; +std::function SystemDialogs::folderOpenCallback; +std::function SystemDialogs::folderSaveCallback; + +void WriteFile(const std::filesystem::path& path, const std::vector& data) { + std::ofstream file(path, std::ios::binary); + if (!file) { + throw std::runtime_error("Cannot open file for writing"); + } + file.write(reinterpret_cast(data.data()), data.size()); +} + +static bool GetJNIEnv(JNIEnv **env) { + *env = (JNIEnv*)SDL_AndroidGetJNIEnv(); + return (*env != NULL); +} + +void SystemDialogs::OpenFileDialog(std::function callback) { + fileOpenCallback = callback; + + JNIEnv *env; + if (!GetJNIEnv(&env)) { + return; + } + + jobject activity = (jobject)SDL_AndroidGetActivity(); + if (!activity) { + return; + } + + jclass systemDialogsClass = env->FindClass("com/tele/u8emulator/SystemDialogs"); + if (!systemDialogsClass) { + env->DeleteLocalRef(activity); + return; + } + + jmethodID openFileMethod = env->GetStaticMethodID(systemDialogsClass, "openFileDialog", + "(Landroid/app/Activity;)V"); + if (!openFileMethod) { + env->DeleteLocalRef(systemDialogsClass); + env->DeleteLocalRef(activity); + return; + } + + env->CallStaticVoidMethod(systemDialogsClass, openFileMethod, activity); + + env->DeleteLocalRef(systemDialogsClass); + env->DeleteLocalRef(activity); +} + +void SystemDialogs::SaveFileDialog(std::string preferred_name, std::function callback) { + fileSaveCallback = callback; + + JNIEnv *env; + if (!GetJNIEnv(&env)) { + return; + } + + jobject activity = (jobject)SDL_AndroidGetActivity(); + if (!activity) { + return; + } + + jclass systemDialogsClass = env->FindClass("com/tele/u8emulator/SystemDialogs"); + if (!systemDialogsClass) { + env->DeleteLocalRef(activity); + return; + } + + jmethodID saveFileMethod = env->GetStaticMethodID(systemDialogsClass, "saveFileDialog", + "(Landroid/app/Activity;Ljava/lang/String;)V"); + if (!saveFileMethod) { + env->DeleteLocalRef(systemDialogsClass); + env->DeleteLocalRef(activity); + return; + } + + jstring jPreferredName = env->NewStringUTF(preferred_name.c_str()); + env->CallStaticVoidMethod(systemDialogsClass, saveFileMethod, activity, jPreferredName); + + env->DeleteLocalRef(jPreferredName); + env->DeleteLocalRef(systemDialogsClass); + env->DeleteLocalRef(activity); +} + +void SystemDialogs::OpenFolderDialog(std::function callback) { + folderOpenCallback = callback; + + JNIEnv *env; + if (!GetJNIEnv(&env)) { + return; + } + + jobject activity = (jobject)SDL_AndroidGetActivity(); + if (!activity) { + return; + } + + jclass systemDialogsClass = env->FindClass("com/tele/u8emulator/SystemDialogs"); + if (!systemDialogsClass) { + env->DeleteLocalRef(activity); + return; + } + + jmethodID openFolderMethod = env->GetStaticMethodID(systemDialogsClass, "openFolderDialog", + "(Landroid/app/Activity;)V"); + if (!openFolderMethod) { + env->DeleteLocalRef(systemDialogsClass); + env->DeleteLocalRef(activity); + return; + } + + env->CallStaticVoidMethod(systemDialogsClass, openFolderMethod, activity); + + env->DeleteLocalRef(systemDialogsClass); + env->DeleteLocalRef(activity); +} + +void SystemDialogs::SaveFolderDialog(std::function callback) { + folderSaveCallback = callback; + + JNIEnv *env; + if (!GetJNIEnv(&env)) { + return; + } + + jobject activity = (jobject)SDL_AndroidGetActivity(); + if (!activity) { + return; + } + + jclass systemDialogsClass = env->FindClass("com/tele/u8emulator/SystemDialogs"); + if (!systemDialogsClass) { + env->DeleteLocalRef(activity); + return; + } + + jmethodID saveFolderMethod = env->GetStaticMethodID(systemDialogsClass, "saveFolderDialog", + "(Landroid/app/Activity;)V"); + if (!saveFolderMethod) { + env->DeleteLocalRef(systemDialogsClass); + env->DeleteLocalRef(activity); + return; + } + + env->CallStaticVoidMethod(systemDialogsClass, saveFolderMethod, activity); + + env->DeleteLocalRef(systemDialogsClass); + env->DeleteLocalRef(activity); +} + +// JNI callbacks +extern "C" { + JNIEXPORT void JNICALL Java_com_tele_u8emulator_Game_onFileSelected(JNIEnv* env, jclass clazz, jstring path, jbyteArray data) { + if (SystemDialogs::fileOpenCallback) { + const char* cPath = env->GetStringUTFChars(path, nullptr); + jbyte* bytes = env->GetByteArrayElements(data, nullptr); + jsize length = env->GetArrayLength(data); + + if (bytes == nullptr || length == 0) { + SDL_Log("Error: Received empty or null data"); + if (bytes) env->ReleaseByteArrayElements(data, bytes, JNI_ABORT); + if (cPath) env->ReleaseStringUTFChars(path, cPath); + return; + } + + std::vector fileData(bytes, bytes + length); + std::filesystem::path tempDir = "./tmp"; + std::filesystem::create_directories(tempDir); + std::filesystem::path fileName = std::filesystem::path(cPath).filename(); + std::filesystem::path tempPath = tempDir / fileName; + + try { + std::ofstream test(tempPath, std::ios::binary); + if (!test) { + throw std::runtime_error("Cannot create temp file for writing"); + } + test.close(); + + WriteFile(tempPath, fileData); + SDL_Log("Successfully wrote temp file: %s", tempPath.string().c_str()); + SDL_Log("File size: %zu bytes", fileData.size()); + + SystemDialogs::fileOpenCallback(tempPath); + std::error_code ec; + std::filesystem::remove(tempPath, ec); + if(ec) { + SDL_Log("Failed to remove temp file: %s", ec.message().c_str()); + } + std::filesystem::remove(tempDir, ec); + if(ec) { + SDL_Log("Failed to remove temp directory: %s", ec.message().c_str()); + } + } + catch (const std::exception& e) { + SDL_Log("Failed to write temp file: %s", e.what()); + } + + env->ReleaseByteArrayElements(data, bytes, JNI_ABORT); + env->ReleaseStringUTFChars(path, cPath); + } + } + + JNIEXPORT void JNICALL Java_com_tele_u8emulator_Game_onFolderSelected(JNIEnv* env, jclass clazz, jstring path) { + if (SystemDialogs::folderOpenCallback) { + const char* cPath = env->GetStringUTFChars(path, nullptr); + SystemDialogs::folderOpenCallback(std::filesystem::path(cPath)); + env->ReleaseStringUTFChars(path, cPath); + } + } + + JNIEXPORT void JNICALL Java_com_tele_u8emulator_Game_onFolderSaved(JNIEnv* env, jclass clazz, jstring path) { + if (SystemDialogs::folderSaveCallback) { + const char* cPath = env->GetStringUTFChars(path, nullptr); + SystemDialogs::folderSaveCallback(std::filesystem::path(cPath)); + env->ReleaseStringUTFChars(path, cPath); + } + } + + JNIEXPORT void JNICALL Java_com_tele_u8emulator_Game_onExportFailed(JNIEnv* env, jclass clazz) { + SDL_Log("Export failed"); + } + + JNIEXPORT void JNICALL Java_com_tele_u8emulator_Game_onFileSaved(JNIEnv* env, jclass clazz, jstring uri) { + if (SystemDialogs::fileSaveCallback) { + const char* cUri = env->GetStringUTFChars(uri, nullptr); + SystemDialogs::fileSaveCallback(std::filesystem::path(cUri)); + env->ReleaseStringUTFChars(uri, cUri); + } + } +} +#endif \ No newline at end of file diff --git a/CasioEmuMsvc/Ext/SysDialog.h b/CasioEmuMsvc/Ext/SysDialog.h index acb1da8..7a2bb43 100644 --- a/CasioEmuMsvc/Ext/SysDialog.h +++ b/CasioEmuMsvc/Ext/SysDialog.h @@ -1,10 +1,17 @@ #pragma once #include #include +#include + class SystemDialogs { public: - static void OpenFileDialog(std::function); - static void SaveFileDialog(std::string prefered_name, std::function); - static void OpenFolderDialog(std::function); - static void SaveFolderDialog(std::function); -}; + static void OpenFileDialog(std::function callback); + static void SaveFileDialog(std::string preferred_name, std::function callback); + static void OpenFolderDialog(std::function callback); + static void SaveFolderDialog(std::function callback); + + static std::function fileOpenCallback; + static std::function fileSaveCallback; + static std::function folderOpenCallback; + static std::function folderSaveCallback; +}; \ No newline at end of file diff --git a/CasioEmuMsvc/Gui/AddressWindow.cpp b/CasioEmuMsvc/Gui/AddressWindow.cpp index de67dba..7116caf 100644 --- a/CasioEmuMsvc/Gui/AddressWindow.cpp +++ b/CasioEmuMsvc/Gui/AddressWindow.cpp @@ -15,7 +15,7 @@ class AddressWindow : public UIWindow { } void RenderCore() override { - ImGui::Text("AddressWindow.Header"_lc); + ImGui::Text("%s", "AddressWindow.Header"_lc); ImGui::Separator(); RenderAddressTable(); @@ -65,7 +65,7 @@ class AddressWindow : public UIWindow { } void RenderAddAddressControls() { - ImGui::Text("AddressWindow.Add"_lc); + ImGui::Text("%s", "AddressWindow.Add"_lc); ImGui::InputScalar("Address", ImGuiDataType_U32, &newAddress, 0, 0, "%x"); // ImGui::InputScalar("Value", ImGuiDataType_U8, &newValue); diff --git a/CasioEmuMsvc/Gui/CallAnalysis.cpp b/CasioEmuMsvc/Gui/CallAnalysis.cpp index 7a52729..078c590 100644 --- a/CasioEmuMsvc/Gui/CallAnalysis.cpp +++ b/CasioEmuMsvc/Gui/CallAnalysis.cpp @@ -155,7 +155,7 @@ struct CallAnalysis : public UIWindow { funcs.clear(); } ImGui::Separator(); - ImGui::Text("CallAnalysis.Filters"_lc); + ImGui::Text("%s", "CallAnalysis.Filters"_lc); ImGui::Checkbox("CallAnalysis.CalleeFilter"_lc, &check_callee); ImGui::SameLine(); diff --git a/CasioEmuMsvc/Gui/CodeViewer.cpp b/CasioEmuMsvc/Gui/CodeViewer.cpp index 8085323..b6214e9 100644 --- a/CasioEmuMsvc/Gui/CodeViewer.cpp +++ b/CasioEmuMsvc/Gui/CodeViewer.cpp @@ -305,7 +305,7 @@ void CodeViewer::RenderCore() { int w = ImGui::CalcTextSize("F").x; if (!is_loaded) { ImGui::SetCursorPos(ImVec2(w * 2, h * 5)); - ImGui::Text("CodeViewer.Loading"_lc); + ImGui::Text("%s", "CodeViewer.Loading"_lc); return; } ImVec2 sz; @@ -338,7 +338,7 @@ void CodeViewer::RenderCore() { ImGui::EndChild(); ImGui::SameLine(); ImGui::Separator(); - ImGui::Text("CodeViewer.Goto"_lc); + ImGui::Text("%s", "CodeViewer.Goto"_lc); ImGui::SameLine(); ImGui::SetNextItemWidth(ImGui::CalcTextSize("000000").x); ImGui::InputText("##input", adrbuf, 8); diff --git a/CasioEmuMsvc/Gui/Injector.cpp b/CasioEmuMsvc/Gui/Injector.cpp index 55a41ed..03ed6b9 100644 --- a/CasioEmuMsvc/Gui/Injector.cpp +++ b/CasioEmuMsvc/Gui/Injector.cpp @@ -3,13 +3,10 @@ #include "Peripheral/BatteryBackedRAM.hpp" #include "hex.hpp" #include "imgui/imgui.h" - #include "Models.h" - #include #include #include - #include "Config.hpp" #include "Ui.hpp" #include @@ -17,148 +14,333 @@ #include #include #include +#include +#include + +void Injector::TrimString(std::string& str) { + str.erase(0, str.find_first_not_of(" \t\n\r")); + if (!str.empty()) { + str.erase(str.find_last_not_of(" \t\n\r") + 1); + } +} + +bool Injector::IsHexString(const std::string& str) { + return str.find_first_not_of("0123456789abcdefABCDEFxX") == std::string::npos; +} + +void Injector::InitCustomInjectionsFile() { + std::filesystem::path filepath = "./hc-inj.txt"; + if (!std::filesystem::exists(filepath)) { + std::ofstream file(filepath); + if (file.is_open()) { + file << "# Function added by hieuxyz\n\n"; + file << "ex1 = {\n"; + file << " 0xd180 = \"30303030\",\n"; + file << " 0xe9e0 = \"1234567890\"\n"; + file << "}\n\n"; + file << "ex2 = {\n"; + file << " 0xd830 = \"11111111\",\n"; + file << " 0xe9f0 = \"88888888\"\n"; + file << "}\n"; + file.close(); + } + } +} + +bool Injector::ParseCustomInjections(const std::string& content) { + customInjections.clear(); + + std::istringstream stream(content); + std::string line; + CustomInjection currentInj; + bool inInjection = false; + + while (std::getline(stream, line)) { + TrimString(line); + + if (line.empty() || line[0] == '#') { + continue; + } + + if (line.find('=') != std::string::npos && line.find('{') != std::string::npos) { + if (inInjection) continue; + + inInjection = true; + currentInj = CustomInjection(); + currentInj.name = line.substr(0, line.find('=')); + TrimString(currentInj.name); + continue; + } + + if (line.find('}') != std::string::npos) { + if (inInjection) { + inInjection = false; + if (!currentInj.pairs.empty()) { + customInjections.push_back(currentInj); + } + } + continue; + } + + if (inInjection && line.find('=') != std::string::npos) { + size_t equalPos = line.find('='); + std::string addr = line.substr(0, equalPos); + std::string data = line.substr(equalPos + 1); + + TrimString(addr); + TrimString(data); + + // Remove quotes and comma + data.erase(std::remove(data.begin(), data.end(), '"'), data.end()); + data.erase(std::remove(data.begin(), data.end(), ','), data.end()); + TrimString(data); + + if (IsHexString(addr) && !data.empty()) { + InjectionPair pair; + pair.address = addr; + pair.data = data; + currentInj.pairs.push_back(pair); + } + } + } + + return !customInjections.empty(); +} + +void Injector::LoadCustomInjections() { + std::ifstream file("./hc-inj.txt"); + if (file.is_open()) { + std::string content((std::istreambuf_iterator(file)), + std::istreambuf_iterator()); + file.close(); + ParseCustomInjections(content); + } +} + +void Injector::ApplyInjection(const CustomInjection& inj, bool& show_info, std::string& info_msg) { + bool success = true; + + for (const auto& pair : inj.pairs) { + try { + auto plc = strtol(pair.address.c_str(), nullptr, 16); + const auto& data = pair.data; + + for (size_t i = 0; i < data.length(); i += 2) { + if (i + 1 >= data.length()) break; + + char hex[3] = {data[i], data[i + 1], '\0'}; + uint8_t byte = strtoul(hex, nullptr, 16); + me_mmu->WriteData(plc + (i/2), byte); + } + } catch (...) { + success = false; + break; + } + } + + char buf[256]; + if (success) { + snprintf(buf, sizeof(buf), "Rop.CustomInjectApplied"_lc, inj.name.c_str()); + } else { + snprintf(buf, sizeof(buf), "Rop.CustomInjectError"_lc, inj.name.c_str()); + } + info_msg = buf; + show_info = true; +} + +void Injector::RenderCustomInjectTab(bool& show_info, std::string& info_msg) { + if (ImGui::Button("Rop.ReloadCustomInjects"_lc)) { + LoadCustomInjections(); + info_msg = "Rop.CustomInjectReloaded"_l; + show_info = true; + } + + ImGui::Separator(); + + for (const auto& inj : customInjections) { + if (ImGui::CollapsingHeader(inj.name.c_str())) { + for (const auto& pair : inj.pairs) { + ImGui::Text("%s: %s", "Rop.Address"_lc, pair.address.c_str()); + ImGui::SameLine(); + ImGui::Text("%s: %s", "Rop.Data"_lc, pair.data.c_str()); + } + + if (ImGui::Button((inj.name + "##inject").c_str())) { + ApplyInjection(inj, show_info, info_msg); + } + + ImGui::Separator(); + } + } +} + +void Injector::RenderInjectorTab(InjectorData& inj, int index, bool& show_info, std::string& info_msg) { + auto valid_hex = [](char c) { + if (c >= '0' && c <= '9') return true; + if ((c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F')) return true; + return false; + }; + + ImGui::Text("%s", "Rop.InjectAddr"_lc); + ImGui::SameLine(); + ImGui::SetNextItemWidth(80); + ImGui::InputText(("##addr" + std::to_string(index)).c_str(), inj.addr, 10); + + if (ImGui::Button(("Rop.Paste"_l + "##" + std::to_string(index)).c_str())) { + if (ImGui::GetClipboardText() != nullptr) { + strncpy(inj.data, ImGui::GetClipboardText(), sizeof(inj.data) - 1); + inj.data[sizeof(inj.data) - 1] = '\0'; + } + } + ImGui::SameLine(); + if (ImGui::Button(("Rop.Clear"_l + "##" + std::to_string(index)).c_str())) { + memset(inj.data, 0, sizeof(inj.data)); + } + + ImGui::SetNextItemWidth(ImGui::GetWindowWidth() * 1.4f); + ImGui::InputTextMultiline( + ("##hex" + std::to_string(index)).c_str(), + inj.data, + IM_ARRAYSIZE(inj.data) - 1, + ImVec2(-1, ImGui::GetTextLineHeight() * 8) + ); + + if (ImGui::Button(("Rop.Inject"_l + "##" + std::to_string(index)).c_str())) { + auto plc = strtol(inj.addr, 0, 16); + size_t i = 0, j = 0; + char hex_buf[3]; + + while (inj.data[i] != '\0' && inj.data[i + 1] != '\0') { + if (inj.data[i] == ';' || inj.data[i + 1] == ';') { + for (;; ++i) { + if (inj.data[i] == '\0') + goto exit; + if (inj.data[i] == '\n') { + ++i; + break; + } + } + } + else { + if (!(valid_hex(inj.data[i]) && valid_hex(inj.data[i + 1]))) { + ++i; + continue; + } + hex_buf[0] = inj.data[i]; + hex_buf[1] = inj.data[i + 1]; + hex_buf[2] = '\0'; + uint8_t byte = strtoul(hex_buf, nullptr, 16); + me_mmu->WriteData(plc + j, byte); + i += 2; + ++j; + } + } + exit: + info_msg = "Rop.Injected"_l; + show_info = true; + } +} + void Injector::RenderCore() { - static int range = 64; - static char strbuf[65536] = {0}; - static char buf[10] = {0}; - static char buf2[10] = {0}; - static MemoryEditor editor; - static const char* info_msg; - auto inputbase = m_emu->hardware_id == casioemu::HardwareId::HW_CLASSWIZ_II ? 0x9268 : 0xD180; - char* base_addr = n_ram_buffer - casioemu::GetRamBaseAddr(m_emu->hardware_id); - ImGui::BeginChild("Rop.Input"_lc, - ImVec2(0, ImGui::GetWindowHeight() * 0.4)); - editor.DrawContents(data_buf, range); - ImGui::EndChild(); - ImGui::SliderInt("Rop.InputSize"_lc, - &range, 64, 1024); - // ImGuiIO& io = ImGui::GetIO(); - // ImGui::SliderFloat("缩放", &io.FontGlobalScale, 0.5, 2); - // if (ImGui::Button( - // #if LANGUAGE == 2 - // "计算模式数学输入" - // #else - // "Math IO with Calc Mode" - // #endif - // )) { - // *(base_addr + 0x91A1) = 0xc1; - // *(base_addr + 0x91AE) = 0x01; - // #if LANGUAGE == 2 - // info_msg = "模式已修改"; - // #else - // info_msg = "Mode changed"; - // #endif - // ImGui::OpenPopup("info"); - // } - if (ImGui::Button("Rop.LoadToInputArea"_lc)) { - memcpy(base_addr + inputbase, data_buf, range); - info_msg = "Rop.LoadedTip"_lc; - ImGui::OpenPopup("info"); - } - ImGui::Separator(); - ImGui::Text("Rop.XAnMode"_lc); - ImGui::SameLine(); - ImGui::InputText( - "##off", - buf, 9); - ImGui::SameLine(); - if (ImGui::Button("Rop.InputAn"_lc)) { - int off = atoi(buf); - if (off > 100) { - memset(base_addr + inputbase, 0x31, 100); - memset(base_addr + inputbase + 100, 0xa6, 1); - memset(base_addr + inputbase + 101, 0x31, off - 100); - } - else { - memset(base_addr + inputbase, 0x31, off); - } - *(base_addr + inputbase + off) = 0xfd; - *(base_addr + inputbase + off + 1) = 0x20; - info_msg = "Rop.AnInputed"_lc; - ImGui::OpenPopup("info"); - } - ImGui::Separator(); - // if (ImGui::Button("加载 Rop 二进制文件")) { - // auto f = OpenFile(); - // std::ifstream ifs2(f, std::ios::in | std::ios::binary); - // if (!ifs2) { - // info_msg = "文件打开失败"; - // ImGui::OpenPopup("info"); - // } - // else { - // char load[0x1000]{}; - // ifs2.get(load, 0x1000); - // ifs2.seekg(0, std::ios::end); - // auto sz = (size_t)ifs2.tellg(); - // if (sz < 0xF7) { - // info_msg = "Rop 二进制文件不正确"; - // ImGui::OpenPopup("info"); - // } - // else { - // memcpy(base_addr + 0x9268, load + 0x8, 200); - // memcpy(base_addr + 0x965E, load + 0x5E, 0xF7 - 0x5E); - // if (sz > 0xF8) { - // std::cout << "Loaded to stack:" << sz - 0xF8 << "\n"; - // memcpy(base_addr + 0xE300, load + 0xF8, sz - 0xF8); - // } - // info_msg = "Rop 已加载,按 Exe 执行"; - // ImGui::OpenPopup("info"); - // } - // } - // } - ImGui::SetNextItemWidth(60); - ImGui::InputText("Rop.InjectAddr"_lc, - buf2, 10); - // ImGui::SameLine(); - ImGui::InputTextMultiline( - "## hex", - strbuf, IM_ARRAYSIZE(strbuf) - 1); - if (ImGui::Button("Rop.InjectHex"_lc)) { - info_msg = "Rop.LoadedTip"_lc; - auto plc = strtol(buf2, 0, 16); - auto valid_hex = [](char c) { - if (c >= '0' && c <= '9') - return true; - if ((c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F')) - return true; - return false; - }; - size_t i = 0, j = 0; - char hex_buf[3]; - while (strbuf[i] != '\0' && strbuf[i + 1] != '\0') { - if (strbuf[i] == ';' || strbuf[i + 1] == ';') { - // begin comment; skip the rest of the line - for (;; ++i) { - if (strbuf[i] == '\0') - goto exit; - if (strbuf[i] == '\n') { - ++i; - break; - } - } - } - else { - if (!(valid_hex(strbuf[i]) && valid_hex(strbuf[i + 1]))) { - ++i; - continue; - } - hex_buf[0] = strbuf[i], hex_buf[1] = strbuf[i + 1], hex_buf[2] = '\0'; - uint8_t byte = strtoul(hex_buf, nullptr, 16); - me_mmu->WriteData(plc + j, byte); - //*(data_buf + j) = (char)byte; - i += 2; - ++j; - } - } - exit: - ImGui::OpenPopup("info"); - } - - if (ImGui::BeginPopupModal("info", 0, ImGuiWindowFlags_AlwaysAutoResize)) { - ImGui::TextUnformatted(info_msg); - if (ImGui::Button("Button.Positive"_lc)) { - ImGui::CloseCurrentPopup(); - } - ImGui::EndPopup(); - } + static int range = 64; + static char buf[10] = {0}; + static MemoryEditor editor; + static bool show_info = false; + static std::string info_msg; + auto inputbase = m_emu->hardware_id == casioemu::HardwareId::HW_CLASSWIZ_II ? 0x9268 : 0xD180; + char* base_addr = n_ram_buffer - casioemu::GetRamBaseAddr(m_emu->hardware_id); + + if (ImGui::BeginTabBar("Rop.TabBar"_lc)) { + if (ImGui::BeginTabItem("Rop.XAnMode"_lc)) { + ImGui::Text("%s", "Rop.InputSize"_lc); + ImGui::SameLine(); + ImGui::SetNextItemWidth(80); + ImGui::InputText("##off", buf, 9); + ImGui::SameLine(); + + if (ImGui::Button("Rop.InputAn"_lc)) { + int off = atoi(buf); + if (off > 100) { + memset(base_addr + inputbase, 0x31, 100); + memset(base_addr + inputbase + 100, 0xa6, 1); + memset(base_addr + inputbase + 101, 0x31, off - 100); + } + else { + memset(base_addr + inputbase, 0x31, off); + } + *(base_addr + inputbase + off) = 0xfd; + *(base_addr + inputbase + off + 1) = 0x20; + info_msg = "Rop.AnInputed"_l; + show_info = true; + } + + ImGui::EndTabItem(); + } + + if (ImGui::BeginTabItem("Rop.InjectHex"_lc)) { + if (ImGui::Button("Rop.AddInjector"_lc)) { + injectors.push_back(InjectorData()); + } + + ImGui::SetNextItemWidth(ImGui::GetWindowWidth() * 1.4f); + for (size_t i = 0; i < injectors.size(); i++) { + ImGui::PushID(static_cast(i)); + + std::string header = "Rop.InjectorNum"_l + " " + std::to_string(i + 1); + + if (injectors.size() > 1) { + if (ImGui::Button(("Rop.RemoveInjector"_l + "##" + std::to_string(i)).c_str())) { + injectors.erase(injectors.begin() + i); + ImGui::PopID(); + break; + } + } + + if (ImGui::CollapsingHeader(header.c_str())) { + RenderInjectorTab(injectors[i], i, show_info, info_msg); + } + + ImGui::PopID(); + } + + ImGui::EndTabItem(); + } + + if (ImGui::BeginTabItem("Rop.Input"_lc)) { + ImGui::BeginChild("RopInput"); + editor.DrawContents(data_buf, range); + ImGui::EndChild(); + + ImGui::SetNextItemWidth(ImGui::GetWindowWidth() * 1.4f); + ImGui::SliderInt("Rop.InputSize"_lc, &range, 64, 1024); + + if (ImGui::Button("Rop.LoadToInputArea"_lc)) { + memcpy(base_addr + inputbase, data_buf, range); + info_msg = "Rop.LoadedTip"_l; + show_info = true; + } + + ImGui::EndTabItem(); + } + + if (ImGui::BeginTabItem("Rop.CustomInject"_lc)) { + RenderCustomInjectTab(show_info, info_msg); + ImGui::EndTabItem(); + } + + ImGui::EndTabBar(); + } + + if (show_info) { + ImGui::OpenPopup("Rop.InfoPopup"_lc); + } + + if (ImGui::BeginPopupModal("Rop.InfoPopup"_lc, nullptr, ImGuiWindowFlags_AlwaysAutoResize)) { + ImGui::Text("%s", info_msg.c_str()); + if (ImGui::Button("Button.Positive"_lc)) { + show_info = false; + ImGui::CloseCurrentPopup(); + } + ImGui::EndPopup(); + } } \ No newline at end of file diff --git a/CasioEmuMsvc/Gui/Injector.hpp b/CasioEmuMsvc/Gui/Injector.hpp index d673fb9..0ccd455 100644 --- a/CasioEmuMsvc/Gui/Injector.hpp +++ b/CasioEmuMsvc/Gui/Injector.hpp @@ -1,11 +1,48 @@ #pragma once #include "Emulator.hpp" #include "Ui.hpp" +#include +#include +#include +#include + +struct InjectorData { + char addr[10]; + char data[65536]; +}; + +struct InjectionPair { + std::string address; + std::string data; +}; + +struct CustomInjection { + std::string name; + std::vector pairs; +}; + class Injector : public UIWindow { private: - char data_buf[1024]; + char data_buf[1024]; + std::vector injectors; + std::vector customInjections; + int current_tab = 0; + char strbuf[65536]; + + void LoadCustomInjections(); + void InitCustomInjectionsFile(); + bool ParseCustomInjections(const std::string& content); + void TrimString(std::string& str); + bool IsHexString(const std::string& str); + void ApplyInjection(const CustomInjection& inj, bool& show_info, std::string& info_msg); public: - Injector() : UIWindow("Rop") {} - void RenderCore() override; -}; + Injector() : UIWindow("Rop") { + injectors.push_back(InjectorData()); + InitCustomInjectionsFile(); + LoadCustomInjections(); + } + void RenderCore() override; + void RenderInjectorTab(InjectorData& inj, int index, bool& show_info, std::string& info_msg); + void RenderCustomInjectTab(bool& show_info, std::string& info_msg); +}; \ No newline at end of file diff --git a/CasioEmuMsvc/Gui/LabelViewer.cpp b/CasioEmuMsvc/Gui/LabelViewer.cpp index 18d3613..1154be1 100644 --- a/CasioEmuMsvc/Gui/LabelViewer.cpp +++ b/CasioEmuMsvc/Gui/LabelViewer.cpp @@ -1,5 +1,4 @@ - -#include +#include #include "LabelViewer.h" #include "Models.h" #include "Ui.hpp" @@ -7,7 +6,7 @@ #include "stringhelper.h" void LabelViewer::RenderCore() { - ImGui::Text("Label.GeneralHeader"_lc); + ImGui::Text("%s", "Label.GeneralHeader"_lc); ImGui::Separator(); auto labels = casioemu::GetCommonMemLabels(m_emu->hardware_id); std::sort(labels.begin(), labels.end()); @@ -28,7 +27,7 @@ void LabelViewer::RenderCore() { ImGui::TextUnformatted(desc.c_str()); ImGui::Separator(); } - ImGui::Text("Label.SfrsHeader"_lc); + ImGui::Text("%s", "Label.SfrsHeader"_lc); ImGui::Separator(); auto regs = me_mmu->GetRegions(); std::sort(regs.begin(), regs.end(), [](casioemu::MMURegion* a, casioemu::MMURegion* b) { return a->base < b->base; }); diff --git a/CasioEmuMsvc/Gui/Localization.cpp b/CasioEmuMsvc/Gui/Localization.cpp index 5213c64..060773e 100644 --- a/CasioEmuMsvc/Gui/Localization.cpp +++ b/CasioEmuMsvc/Gui/Localization.cpp @@ -1,2 +1,2 @@ -#include "localization.h" +#include "Localization.h" Localization g_local; \ No newline at end of file diff --git a/CasioEmuMsvc/Gui/Localization.h b/CasioEmuMsvc/Gui/Localization.h index 1e497de..49a069a 100644 --- a/CasioEmuMsvc/Gui/Localization.h +++ b/CasioEmuMsvc/Gui/Localization.h @@ -1,7 +1,6 @@ #pragma once #include #include -#include #include #include #include @@ -13,254 +12,261 @@ #include class LocalizationException : public std::runtime_error { - using std::runtime_error::runtime_error; + using std::runtime_error::runtime_error; }; class Localization { public: - void Load() { - std::fstream fs("locale.txt", std::ios::in); - std::string locale; - if (fs >> locale) { - ChangeLanguage(locale); - } - else { - ChangeLanguage("en_US"); - } - } - // ԣǷɹ - bool ChangeLanguage(const std::string& localeName) { - try { - m_translations.clear(); - m_pluralRules.clear(); - m_currentLocale = localeName; - - LoadTranslations(localeName); - std::fstream fs("locale.txt", std::ios::out); - fs << localeName; - return true; - } - catch (const std::exception& e) { - // ʧʱз - throw LocalizationException( - std::format("Failed to load language {}: {}", localeName, e.what())); - } - } - - // ȡǰ - std::string GetCurrentLanguage() const { - return m_currentLocale; - } - - // - std::string Get(std::string_view key) const { - auto iter = m_translations.find(std::string(key)); - if (iter == m_translations.end()) - return std::string(key); - return iter->second; - } - const char* GetCStr(const char* key) const { - auto iter = m_translations.find(key); - if (iter == m_translations.end()) - return key; - return iter->second.c_str(); - } - - // ָ֧ʽķ - template - std::string Format(std::string_view key, Args&&... args) const { - std::string text = Get(key); - try { - return std::vformat(text, std::make_format_args(std::forward(args)...)); - } - catch (const std::exception& e) { - throw LocalizationException( - std::format("Format error for key '{}': {}", key, e.what())); - } - } - - // ָ֧ʽ - std::string GetPlural(std::string_view key, int count) const { - std::string baseKey(key); - auto pluralRule = m_pluralRules.find(baseKey); - - if (pluralRule != m_pluralRules.end()) { - const auto& rules = pluralRule->second; - for (const auto& [condition, form] : rules) { - if (EvaluatePluralCondition(condition, count)) { - return Format(form, count); - } - } - } - - return Format(Get(key), count); - } - - // - std::string operator[](std::string_view key) const { - return Get(key); - } + void Load() { + std::fstream fs("locale.txt", std::ios::in); + std::string locale; + if (fs >> locale) { + ChangeLanguage(locale); + } + else { + ChangeLanguage("en_US"); + } + } + + bool ChangeLanguage(const std::string& localeName) { + try { + m_translations.clear(); + m_pluralRules.clear(); + m_currentLocale = localeName; + + LoadTranslations(localeName); + std::fstream fs("locale.txt", std::ios::out); + fs << localeName; + return true; + } + catch (const std::exception& e) { + char buffer[512]; + snprintf(buffer, sizeof(buffer), "Failed to load language %s: %s", + localeName.c_str(), e.what()); + throw LocalizationException(buffer); + } + } + + std::string GetCurrentLanguage() const { + return m_currentLocale; + } + + std::string Get(std::string_view key) const { + auto iter = m_translations.find(std::string(key)); + if (iter == m_translations.end()) + return std::string(key); + return iter->second; + } + + const char* GetCStr(const char* key) const { + auto iter = m_translations.find(key); + if (iter == m_translations.end()) + return key; + return iter->second.c_str(); + } + + template + std::string Format(std::string_view key, const Args&... args) const { + std::string text = Get(key); + char buffer[1024]; + try { + snprintf(buffer, sizeof(buffer), text.c_str(), + ToString(args)...); + return std::string(buffer); + } + catch (const std::exception& e) { + char errBuffer[512]; + snprintf(errBuffer, sizeof(errBuffer), + "Format error for key '%s': %s", + std::string(key).c_str(), e.what()); + throw LocalizationException(errBuffer); + } + } + + std::string GetPlural(std::string_view key, int count) const { + std::string baseKey(key); + auto pluralRule = m_pluralRules.find(baseKey); + + if (pluralRule != m_pluralRules.end()) { + const auto& rules = pluralRule->second; + for (const auto& [condition, form] : rules) { + if (EvaluatePluralCondition(condition, count)) { + return Format(form, count); + } + } + } + + return Format(Get(key), count); + } + + std::string operator[](std::string_view key) const { + return Get(key); + } private: - struct PluralRule { - std::string condition; - std::string form; - }; - - std::string m_basePath = "./locales/"; - std::string m_currentLocale = "en_US"; - std::unordered_map m_translations; - std::unordered_map> m_pluralRules; - - // طļ - void LoadTranslations(const std::string& localeName) { - std::filesystem::path filePath = - std::filesystem::path(m_basePath) / (localeName + ".lc"); - - std::ifstream file(filePath); - if (!file.is_open()) { - throw LocalizationException( - std::format("Cannot open locale file: {}", filePath.string())); - } - - std::string line; - int lineNumber = 0; - while (std::getline(file, line)) { - lineNumber++; - if (line.empty() || line[0] == '#') - continue; - - try { - ProcessLine(line); - } - catch (const std::exception& e) { - throw LocalizationException( - std::format("Error at line {}: {}", lineNumber, e.what())); - } - } - } - - // з - void ProcessLine(const std::string& line) { - std::istringstream lineStream(line); - std::string key, value; - - if (!std::getline(lineStream, key, '=')) { - throw LocalizationException("Invalid format"); - } - std::getline(lineStream, value); - - key = Trim(key); - value = Trim(value); - - if (key.empty()) { - throw LocalizationException("Empty key"); - } - - // ʽ - if (key.ends_with("|plural")) { - ProcessPluralForm(key.substr(0, key.length() - 7), value); - } - else { - m_translations[key] = DecodeEscapes(value); - } - } - - // ʽ - void ProcessPluralForm(const std::string& key, const std::string& value) { - std::istringstream ss(value); - std::string rule; - - while (std::getline(ss, rule, ';')) { - size_t pos = rule.find(':'); - if (pos == std::string::npos) { - throw LocalizationException("Invalid plural rule format"); - } - - std::string condition = Trim(rule.substr(0, pos)); - std::string form = Trim(rule.substr(pos + 1)); - - m_pluralRules[key].push_back({condition, DecodeEscapes(form)}); - } - } - - // 㸴 - bool EvaluatePluralCondition(const std::string& condition, int count) const { - if (condition == "one") - return count == 1; - if (condition == "zero") - return count == 0; - if (condition == "many") - return count >= 5; - if (condition == "few") - return count >= 2 && count <= 4; - return condition == "other"; - } - - // תַ - static std::string DecodeEscapes(const std::string& input) { - std::string result; - result.reserve(input.length()); - - for (size_t i = 0; i < input.length(); ++i) { - if (input[i] == '\\' && i + 1 < input.length()) { - switch (input[++i]) { - case 'n': - result += '\n'; - break; - case 't': - result += '\t'; - break; - case 'r': - result += '\r'; - break; - case '\\': - result += '\\'; - break; - case '=': - result += '='; - break; - default: - result += input[i]; - break; - } - } - else { - result += input[i]; - } - } - return result; - } - - // ȥַ˵Ŀհַ - static std::string Trim(std::string_view str) { - const auto start = str.find_first_not_of(" \t\r\n"); - if (start == std::string_view::npos) - return std::string(); - - const auto end = str.find_last_not_of(" \t\r\n"); - return std::string(str.substr(start, end - start + 1)); - } + struct PluralRule { + std::string condition; + std::string form; + }; + + std::string m_basePath = "./locales/"; + std::string m_currentLocale = "en_US"; + std::unordered_map m_translations; + std::unordered_map> m_pluralRules; + + // Helper method to convert arguments to const char* + template + static auto ToString(const T& value) -> decltype(value) { + return value; + } + + static const char* ToString(const std::string& str) { + return str.c_str(); + } + + void LoadTranslations(const std::string& localeName) { + std::filesystem::path filePath = + std::filesystem::path(m_basePath) / (localeName + ".lc"); + + std::ifstream file(filePath); + if (!file.is_open()) { + char buffer[512]; + snprintf(buffer, sizeof(buffer), "Cannot open locale file: %s", + filePath.string().c_str()); + throw LocalizationException(buffer); + } + + std::string line; + int lineNumber = 0; + while (std::getline(file, line)) { + lineNumber++; + if (line.empty() || line[0] == '#') + continue; + + try { + ProcessLine(line); + } + catch (const std::exception& e) { + char buffer[512]; + snprintf(buffer, sizeof(buffer), "Error at line %d: %s", + lineNumber, e.what()); + throw LocalizationException(buffer); + } + } + } + + void ProcessLine(const std::string& line) { + std::istringstream lineStream(line); + std::string key, value; + + if (!std::getline(lineStream, key, '=')) { + throw LocalizationException("Invalid format"); + } + std::getline(lineStream, value); + + key = Trim(key); + value = Trim(value); + + if (key.empty()) { + throw LocalizationException("Empty key"); + } + + if (key.ends_with("|plural")) { + ProcessPluralForm(key.substr(0, key.length() - 7), value); + } + else { + m_translations[key] = DecodeEscapes(value); + } + } + + void ProcessPluralForm(const std::string& key, const std::string& value) { + std::istringstream ss(value); + std::string rule; + + while (std::getline(ss, rule, ';')) { + size_t pos = rule.find(':'); + if (pos == std::string::npos) { + throw LocalizationException("Invalid plural rule format"); + } + + std::string condition = Trim(rule.substr(0, pos)); + std::string form = Trim(rule.substr(pos + 1)); + + m_pluralRules[key].push_back({condition, DecodeEscapes(form)}); + } + } + + bool EvaluatePluralCondition(const std::string& condition, int count) const { + if (condition == "one") + return count == 1; + if (condition == "zero") + return count == 0; + if (condition == "many") + return count >= 5; + if (condition == "few") + return count >= 2 && count <= 4; + return condition == "other"; + } + + static std::string DecodeEscapes(const std::string& input) { + std::string result; + result.reserve(input.length()); + + for (size_t i = 0; i < input.length(); ++i) { + if (input[i] == '\\' && i + 1 < input.length()) { + switch (input[++i]) { + case 'n': + result += '\n'; + break; + case 't': + result += '\t'; + break; + case 'r': + result += '\r'; + break; + case '\\': + result += '\\'; + break; + case '=': + result += '='; + break; + default: + result += input[i]; + break; + } + } + else { + result += input[i]; + } + } + return result; + } + + static std::string Trim(std::string_view str) { + const auto start = str.find_first_not_of(" \t\r\n"); + if (start == std::string_view::npos) + return std::string(); + + const auto end = str.find_last_not_of(" \t\r\n"); + return std::string(str.substr(start, end - start + 1)); + } }; -// ȫʵ extern Localization g_local; -// ַ inline std::string operator""_l(const char* str, size_t) { - return g_local[str]; + return g_local[str]; } + inline const char* operator""_lc(const char* str, size_t) { - return g_local.GetCStr(str); + return g_local.GetCStr(str); } -// ʽַ template -inline std::string localstr(std::string_view key, Args&&... args) { - return g_local.Format(key, std::forward(args)...); +inline std::string localstr(std::string_view key, const Args&... args) { + return g_local.Format(key, args...); } -// ʽ inline std::string plural(std::string_view key, int count) { - return g_local.GetPlural(key, count); + return g_local.GetPlural(key, count); } \ No newline at end of file diff --git a/CasioEmuMsvc/Gui/MemBreakPoint.cpp b/CasioEmuMsvc/Gui/MemBreakPoint.cpp index 77ce418..4b7ff8a 100644 --- a/CasioEmuMsvc/Gui/MemBreakPoint.cpp +++ b/CasioEmuMsvc/Gui/MemBreakPoint.cpp @@ -29,7 +29,8 @@ void MemBreakPoint::DrawContent() { ImGui::PopID(); if (ImGui::BeginPopupContextItem()) { selected = i; - ImGui::Text("MemBP.BPType"_lc); + + ImGui::Text("%s", "MemBP.BPType"_lc); if (ImGui::Button("HexEditors.ContextMenu.MonitorRead"_lc)) { target_addr = i; data.enableWrite = 0; @@ -59,7 +60,7 @@ void MemBreakPoint::DrawContent() { void MemBreakPoint::DrawFindContent() { if (target_addr == -1) { - ImGui::TextColored(ImVec4(255, 255, 0, 255), "MemBP.NoBPHint"_lc); + ImGui::TextColored(ImVec4(255, 255, 0, 255), "%s", "MemBP.NoBPHint"_lc); return; } int write = break_point_hash[target_addr].enableWrite; diff --git a/CasioEmuMsvc/Gui/Theme.cpp b/CasioEmuMsvc/Gui/Theme.cpp index 6cb3c60..c2a4e6f 100644 --- a/CasioEmuMsvc/Gui/Theme.cpp +++ b/CasioEmuMsvc/Gui/Theme.cpp @@ -2,34 +2,88 @@ #include #include #include "Theme.h" +#include +#include + +struct ThemeSettings { + bool isDarkMode = true; + char language[30] = ""; + float scale = 1.0f; +}; + +static ThemeSettings g_settings; + +void SaveThemeSettings() { + std::ofstream file("./theme.txt"); + if (file.is_open()) { + file << g_settings.isDarkMode << std::endl; + file << g_settings.language << std::endl; + file << g_settings.scale << std::endl; + file.close(); + } +} + +void LoadThemeSettings() { + std::ifstream file("./theme.txt"); + if (file.is_open()) { + file >> g_settings.isDarkMode; + file >> g_settings.language; + file >> g_settings.scale; + file.close(); + + if (g_settings.isDarkMode) { + ImGui::StyleColorsDark(); + } else { + ImGui::StyleColorsLight(); + } + if (strlen(g_settings.language) > 0) { + g_local.ChangeLanguage(g_settings.language); + } + RebuildFont_Scale = g_settings.scale; + RebuildFont_Requested = true; + } +} + class ThemeWindow : public UIWindow { public: - ThemeWindow() : UIWindow("Theme") { - } - void RenderCore() override { - if (ImGui::Button("Ui.DarkMode"_lc)) { - ImGui::StyleColorsDark(); - } - ImGui::SameLine(); - if (ImGui::Button("Ui.LightMode"_lc)) { - ImGui::StyleColorsLight(); - } - static char lang_input[30]{}; - ImGui::InputText("##language_input", lang_input, 30); - if (ImGui::Button("Ui.ChangeLang"_lc)) { - g_local.ChangeLanguage(lang_input); - RebuildFont_Requested = true; - } - ImGui::TextUnformatted("Ui.CurrentLang"_lc); - ImGui::SameLine(); - ImGui::TextUnformatted("Localization.LanguageName"_lc); - ImGui::SliderFloat("Ui.Scale"_lc, &RebuildFont_Scale, 0, 5, "%.2f"); - if (ImGui::Button("Ui.ApplyScale"_lc)) { - RebuildFont_Requested = true; - } - } + ThemeWindow() : UIWindow("Theme") { + LoadThemeSettings(); + } + + void RenderCore() override { + if (ImGui::Button("Ui.DarkMode"_lc)) { + ImGui::StyleColorsDark(); + g_settings.isDarkMode = true; + SaveThemeSettings(); + } + ImGui::SameLine(); + if (ImGui::Button("Ui.LightMode"_lc)) { + ImGui::StyleColorsLight(); + g_settings.isDarkMode = false; + SaveThemeSettings(); + } + + ImGui::InputText("##language_input", g_settings.language, 30); + if (ImGui::Button("Ui.ChangeLang"_lc)) { + g_local.ChangeLanguage(g_settings.language); + RebuildFont_Requested = true; + SaveThemeSettings(); + } + + ImGui::TextUnformatted("Ui.CurrentLang"_lc); + ImGui::SameLine(); + ImGui::TextUnformatted("Localization.LanguageName"_lc); + + if (ImGui::SliderFloat("Ui.Scale"_lc, &RebuildFont_Scale, 0, 5, "%.2f")) { + g_settings.scale = RebuildFont_Scale; + } + if (ImGui::Button("Ui.ApplyScale"_lc)) { + RebuildFont_Requested = true; + SaveThemeSettings(); + } + } }; UIWindow* MakeThemeWindow() { - return new ThemeWindow(); -} + return new ThemeWindow(); +} \ No newline at end of file diff --git a/CasioEmuMsvc/Gui/Theme.h b/CasioEmuMsvc/Gui/Theme.h index 21a542b..f8f6293 100644 --- a/CasioEmuMsvc/Gui/Theme.h +++ b/CasioEmuMsvc/Gui/Theme.h @@ -1,3 +1,6 @@ #pragma once #include -UIWindow* MakeThemeWindow(); \ No newline at end of file +UIWindow* MakeThemeWindow(); + +void SaveThemeSettings(); +void LoadThemeSettings(); diff --git a/CasioEmuMsvc/Gui/UIScaling.cpp b/CasioEmuMsvc/Gui/UIScaling.cpp index 0237e65..38def54 100644 --- a/CasioEmuMsvc/Gui/UIScaling.cpp +++ b/CasioEmuMsvc/Gui/UIScaling.cpp @@ -1,13 +1,12 @@ -// UIScaling.cpp #include "UIScaling.h" namespace UI { float Scaling::fontScale = 1.0f; float Scaling::padding = 8.0f; - float Scaling::buttonHeight = 40.0f; - float Scaling::minColumnWidth = 60.0f; - float Scaling::labelWidth = 100.0f; + float Scaling::buttonHeight = 32.0f; + float Scaling::minColumnWidth = 50.0f; + float Scaling::labelWidth = 80.0f; float Scaling::windowWidth = 1920.0f; float Scaling::windowHeight = 1080.0f; float Scaling::aspectRatio = 16.0f/9.0f; -} +} \ No newline at end of file diff --git a/CasioEmuMsvc/Gui/UIScaling.h b/CasioEmuMsvc/Gui/UIScaling.h index d1e1203..3435abf 100644 --- a/CasioEmuMsvc/Gui/UIScaling.h +++ b/CasioEmuMsvc/Gui/UIScaling.h @@ -1,4 +1,3 @@ -// UIScaling.h #pragma once #include #include @@ -9,79 +8,115 @@ namespace UI { struct Scaling { static float fontScale; static float padding; - static float buttonHeight; + static float buttonHeight; static float minColumnWidth; static float labelWidth; static float windowWidth; static float windowHeight; static float aspectRatio; + static float GetDensityDpi() { + SDL_DisplayMode displayMode; + float densityDpi = 160.0f; // Default fallback + + if (SDL_GetCurrentDisplayMode(0, &displayMode) == 0) { + float diagonalPixels = sqrt(pow(displayMode.w, 2) + pow(displayMode.h, 2)); + float physicalWidth, physicalHeight; + if (SDL_GetDisplayDPI(0, &densityDpi, &physicalWidth, &physicalHeight) != 0) { + // If SDL_GetDisplayDPI fails, estimate based on resolution + if (displayMode.h <= 480) densityDpi = 120.0f; // ldpi + else if (displayMode.h <= 800) densityDpi = 160.0f; // mdpi + else if (displayMode.h <= 1280) densityDpi = 240.0f; // hdpi + else if (displayMode.h <= 1920) densityDpi = 320.0f; // xhdpi + else if (displayMode.h <= 2560) densityDpi = 480.0f; // xxhdpi + else densityDpi = 640.0f; // xxxhdpi + } + } + return densityDpi; + } + static void UpdateUIScale() { ImGuiIO& io = ImGui::GetIO(); windowWidth = io.DisplaySize.x; windowHeight = io.DisplaySize.y; aspectRatio = windowWidth / windowHeight; - // Tính density scale cho Android - int displayDensity = 1; - SDL_DisplayMode displayMode; - if (SDL_GetCurrentDisplayMode(0, &displayMode) == 0) { - float diagonalPixels = sqrt(pow(displayMode.w, 2) + pow(displayMode.h, 2)); - // Điều chỉnh density dựa trên kích thước màn hình - if (diagonalPixels > 2500) { - displayDensity = 4; // Ultra HD - } else if (diagonalPixels > 2000) { - displayDensity = 3; // xxxhdpi - } else if (diagonalPixels > 1500) { - displayDensity = 2; // xxhdpi - } else { - displayDensity = 1; // xhdpi và thấp hơn - } + // Calculate base scale considering both resolution and density + float densityDpi = GetDensityDpi(); + float densityScale = densityDpi / 160.0f; // Using mdpi as baseline + + // Calculate base scale based on screen resolution + float baseScale = std::min(windowWidth / 1920.0f, windowHeight / 1080.0f); + + // Adjust scale based on screen size category + float screenSizeAdjustment = 1.0f; + float diagonalPixels = sqrt(pow(windowWidth, 2) + pow(windowHeight, 2)); + float diagonalInches = diagonalPixels / densityDpi; + + if (diagonalInches <= 4.0f) { + screenSizeAdjustment = 0.85f; // Very small phones + } else if (diagonalInches <= 5.0f) { + screenSizeAdjustment = 0.9f; // Small phones + } else if (diagonalInches <= 6.0f) { + screenSizeAdjustment = 1.0f; // Standard phones + } else if (diagonalInches <= 7.0f) { + screenSizeAdjustment = 1.1f; // Large phones + } else if (diagonalInches <= 10.0f) { + screenSizeAdjustment = 1.2f; // Tablets + } else { + screenSizeAdjustment = 1.3f; // Large tablets } - // Tính toán scale riêng cho chiều rộng và chiều cao - float widthScale = windowWidth / 1920.0f; - float heightScale = windowHeight / 1080.0f; + // Calculate final font scale + fontScale = baseScale * screenSizeAdjustment * sqrt(densityScale); - // Sử dụng scale nhỏ hơn để đảm bảo UI không bị quá to - fontScale = std::min(widthScale, heightScale) * displayDensity; - - // Giới hạn scale - fontScale = std::clamp(fontScale, 0.8f, 2.5f); - - // Điều chỉnh global font scale - io.FontGlobalScale = fontScale; + // More aggressive clamping for Android + fontScale = std::clamp(fontScale, 0.7f, 1.6f); + + // Update global font scale with minimum readable size + io.FontGlobalScale = std::max(fontScale, 0.85f); - // Tính các thông số UI với tỉ lệ màn hình - padding = std::max(8.0f * fontScale, 6.0f); - buttonHeight = std::max(40.0f * fontScale, 35.0f); - minColumnWidth = std::max(60.0f * fontScale * aspectRatio, 50.0f); - labelWidth = std::max(100.0f * fontScale * aspectRatio, 80.0f); + // Adjust touch-friendly sizes + float touchScale = std::max(fontScale, 1.0f); // Ensure minimum touch target size + padding = 10.0f * touchScale; + buttonHeight = 36.0f * touchScale; // Increased for better touch targets + minColumnWidth = 60.0f * fontScale; + labelWidth = 90.0f * fontScale; - // Cập nhật style ImGui + // Update ImGui style ImGuiStyle& style = ImGui::GetStyle(); + + // Increase padding for touch interfaces style.WindowPadding = ImVec2(padding, padding); - style.FramePadding = ImVec2(padding * 0.8f, padding * 0.8f); - style.ItemSpacing = ImVec2(padding * aspectRatio, padding); - style.ItemInnerSpacing = ImVec2(padding * aspectRatio, padding); - style.TouchExtraPadding = ImVec2(padding * 0.4f, padding * 0.4f); + style.FramePadding = ImVec2(padding * 0.7f, padding * 0.7f); + style.ItemSpacing = ImVec2(padding * 0.8f, padding * 0.8f); + style.ItemInnerSpacing = ImVec2(padding * 0.5f, padding * 0.5f); + style.TouchExtraPadding = ImVec2(padding * 0.6f, padding * 0.6f); - // Điều chỉnh các thành phần UI - style.WindowRounding = 4.0f * fontScale; - style.ScrollbarSize = std::max(20.0f * fontScale, 15.0f); - style.GrabMinSize = std::max(30.0f * fontScale, 25.0f); + // Adjust sizes for touch interaction + style.ScrollbarSize = 18.0f * touchScale; + style.GrabMinSize = 24.0f * touchScale; style.WindowTitleAlign = ImVec2(0.5f, 0.5f); - style.MouseCursorScale = 1.5f * fontScale; - style.TabRounding = 4.0f * fontScale; - style.FrameRounding = 4.0f * fontScale; - style.ScrollbarRounding = 4.0f * fontScale; - style.GrabRounding = 4.0f * fontScale; + style.MouseCursorScale = 1.0f * touchScale; - // Điều chỉnh khoảng cách cho màn hình rộng - if (aspectRatio > 1.5f) { + // Rounded corners for modern Android look + float rounding = 6.0f * std::min(touchScale, 1.2f); + style.WindowRounding = rounding; + style.ChildRounding = rounding; + style.FrameRounding = rounding; + style.ScrollbarRounding = rounding; + style.GrabRounding = rounding; + style.TabRounding = rounding; + style.PopupRounding = rounding; + + // Adjust spacing for wide screens + if (aspectRatio > 1.8f) { // More aggressive for ultra-wide style.ItemSpacing.x *= 1.2f; style.WindowPadding.x *= 1.2f; } + + // Ensure minimum touch target size + style.FramePadding.y = std::max(buttonHeight * 0.15f, 8.0f); } }; -} +} \ No newline at end of file diff --git a/CasioEmuMsvc/Gui/Ui.cpp b/CasioEmuMsvc/Gui/Ui.cpp index 5cdc65d..455db3c 100644 --- a/CasioEmuMsvc/Gui/Ui.cpp +++ b/CasioEmuMsvc/Gui/Ui.cpp @@ -25,7 +25,6 @@ char* n_ram_buffer = 0; casioemu::MMU* me_mmu = 0; - SDL_Window* window = 0; SDL_Renderer* renderer = 0; @@ -40,132 +39,170 @@ std::vector windows{}; static ImVec4 clear_color = ImVec4(0.45f, 0.55f, 0.60f, 1.00f); void gui_loop() { - if (!m_emu->Running()) - return; - - ImGuiIO& io = ImGui::GetIO(); - - ImGui_ImplSDLRenderer2_NewFrame(); - ImGui_ImplSDL2_NewFrame(); - ImGui::NewFrame(); - for (auto win : windows) { - win->Render(); - } -#ifndef __ANDROID__ - ImGui::Begin("Debug"); - if (ImGui::Button("Crash")) { - throw 0; - } - ImGui::End(); -#endif - -#ifdef __ANDROID__ - ImGui::SetNextWindowBgAlpha(0.0f); // 设置透明背景 - ImGui::Begin("Overlay", nullptr, ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_NoDocking | ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoBackground | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoMove); - - ImGui::SetWindowPos(ImVec2(0, 0)); // 设置窗口位置,这决定了按钮的位置 - static UIWindow* current_filter = 0; - if (ImGui::BeginCombo("##cb", current_filter ? current_filter->name : 0)) { - for (int n = 0; n < windows.size(); n++) { - bool is_selected = (current_filter == - windows[n]); // You can store your selection however you want, outside or inside your objects - if (ImGui::Selectable(windows[n]->name, is_selected)) - current_filter = windows[n]; - if (is_selected) - ImGui::SetItemDefaultFocus(); // You may set the initial focus when opening the combo (scrolling + for keyboard navigation support) - } - ImGui::EndCombo(); - } - ImGui::SameLine(); - if (ImGui::Button("Open")) { - if (current_filter != 0) - current_filter->open = true; - } - ImGui::SameLine(); - if (ImGui::Button("Close all")) { - for (auto& win : windows) { - win->open = false; - } - } - ImGui::End(); -#endif - ImGui::Render(); - // SDL_RenderSetScale(renderer, io.DisplayFramebufferScale.x, io.DisplayFramebufferScale.y); -#ifdef SINGLE_WINDOW - ImGui_ImplSDLRenderer2_RenderDrawData(ImGui::GetDrawData()); -#else - ImGui_ImplSDLRenderer2_RenderDrawData(ImGui::GetDrawData()); - SDL_RenderPresent(renderer); -#endif + if (!m_emu->Running()) + return; + + ImGuiIO& io = ImGui::GetIO(); + + #ifdef __ANDROID__ + UI::Scaling::UpdateUIScale(); + #endif + + ImGui_ImplSDLRenderer2_NewFrame(); + ImGui_ImplSDL2_NewFrame(); + ImGui::NewFrame(); + + for (auto win : windows) { + win->Render(); + } + + #ifndef __ANDROID__ + ImGui::Begin("Debug"); + if (ImGui::Button("Crash")) { + throw 0; + } + ImGui::End(); + #endif + + #ifdef __ANDROID__ + ImGui::SetNextWindowBgAlpha(0.0f); + ImGui::Begin("Overlay", nullptr, + ImGuiWindowFlags_NoDecoration | + ImGuiWindowFlags_NoDocking | + ImGuiWindowFlags_AlwaysAutoResize | + ImGuiWindowFlags_NoBackground | + ImGuiWindowFlags_NoTitleBar | + ImGuiWindowFlags_NoMove); + + ImGui::SetWindowPos(ImVec2(UI::Scaling::padding, UI::Scaling::padding)); + + static UIWindow* current_filter = 0; + ImGui::SetNextItemWidth(UI::Scaling::labelWidth * 2); + if (ImGui::BeginCombo("##cb", current_filter ? current_filter->name : 0)) { + for (int n = 0; n < windows.size(); n++) { + bool is_selected = (current_filter == windows[n]); + if (ImGui::Selectable(windows[n]->name, is_selected)) + current_filter = windows[n]; + if (is_selected) + ImGui::SetItemDefaultFocus(); + } + ImGui::EndCombo(); + } + + ImGui::SameLine(0, UI::Scaling::padding); + ImVec2 buttonSize(UI::Scaling::buttonHeight * 2, UI::Scaling::buttonHeight); + if (ImGui::Button("Open", buttonSize)) { + if (current_filter != 0) + current_filter->open = true; + } + + ImGui::SameLine(0, UI::Scaling::padding); + if (ImGui::Button("Close all", buttonSize)) { + for (auto& win : windows) { + win->open = false; + } + } + ImGui::End(); + #endif + + ImGui::Render(); + #ifdef SINGLE_WINDOW + ImGui_ImplSDLRenderer2_RenderDrawData(ImGui::GetDrawData()); + #else + ImGui_ImplSDLRenderer2_RenderDrawData(ImGui::GetDrawData()); + SDL_RenderPresent(renderer); + #endif } int test_gui(bool* guiCreated, SDL_Window* wnd, SDL_Renderer* rnd) { - SDL_SetHint(SDL_HINT_IME_SHOW_UI, "1"); -#ifdef SINGLE_WINDOW - window = wnd; - renderer = rnd; -#else - window = SDL_CreateWindow("CasioEmuMsvc Debugger", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, 1280, 720, SDL_WINDOW_RESIZABLE); - renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_PRESENTVSYNC | SDL_RENDERER_ACCELERATED); -#endif // __ANDROID - if (renderer == nullptr) { - SDL_Log("Error creating SDL_Renderer!"); - return 0; - } - IMGUI_CHECKVERSION(); - ImGui::CreateContext(); - ImGuiIO& io = ImGui::GetIO(); - RebuildFont(); - SetupDefaultTheme(); - io.WantCaptureKeyboard = true; - io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; // Enable Keyboard Controls - io.ConfigFlags |= ImGuiConfigFlags_DockingEnable; - - ImGui_ImplSDL2_InitForSDLRenderer(window, renderer); - ImGui_ImplSDLRenderer2_Init(renderer); - if (guiCreated) - *guiCreated = true; - while (!me_mmu) - std::this_thread::sleep_for(std::chrono::microseconds(1)); - - g_labels = parseFile(m_emu->GetModelFilePath("labels")); - - if (m_emu->hardware_id == casioemu::HW_FX_5800P) { - windows.push_back(CreateFx5800FileSystem()); - } - - for (auto item : std::initializer_list{ - new VariableWindow(), - new HwController(), - new LabelViewer(), - new WatchWindow(), - CreateCallAnalysisWindow(), - code_viewer = new CodeViewer(), - injector = new Injector(), - membp = new MemBreakPoint(), - CreateAddressWindow(), - MakeAssemblerUI(), - MakeThemeWindow()}) - windows.push_back(item); - for (auto item : GetEditors()) - windows.push_back(item); - -#ifdef __ANDROID__ - for (auto item : windows) { - item->open = false; - } -#endif - - return 0; + SDL_SetHint(SDL_HINT_IME_SHOW_UI, "1"); + + #ifdef SINGLE_WINDOW + window = wnd; + renderer = rnd; + #else + #ifdef __ANDROID__ + window = SDL_CreateWindow("CasioEmuMsvc Debugger", + SDL_WINDOWPOS_CENTERED, + SDL_WINDOWPOS_CENTERED, + (int)UI::Scaling::windowWidth, + (int)UI::Scaling::windowHeight, + SDL_WINDOW_RESIZABLE); + #else + window = SDL_CreateWindow("CasioEmuMsvc Debugger", + SDL_WINDOWPOS_CENTERED, + SDL_WINDOWPOS_CENTERED, + 1280, 720, + SDL_WINDOW_RESIZABLE); + #endif + renderer = SDL_CreateRenderer(window, -1, + SDL_RENDERER_PRESENTVSYNC | SDL_RENDERER_ACCELERATED); + #endif + + if (renderer == nullptr) { + SDL_Log("Error creating SDL_Renderer!"); + return 0; + } + + IMGUI_CHECKVERSION(); + ImGui::CreateContext(); + ImGuiIO& io = ImGui::GetIO(); + + #ifdef __ANDROID__ + UI::Scaling::UpdateUIScale(); + #endif + + RebuildFont(); + SetupDefaultTheme(); + + io.WantCaptureKeyboard = true; + io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; + io.ConfigFlags |= ImGuiConfigFlags_DockingEnable; + + ImGui_ImplSDL2_InitForSDLRenderer(window, renderer); + ImGui_ImplSDLRenderer2_Init(renderer); + if (guiCreated) + *guiCreated = true; + while (!me_mmu) + std::this_thread::sleep_for(std::chrono::microseconds(1)); + + g_labels = parseFile(m_emu->GetModelFilePath("labels")); + + if (m_emu->hardware_id == casioemu::HW_FX_5800P) { + windows.push_back(CreateFx5800FileSystem()); + } + + for (auto item : std::initializer_list{ + new VariableWindow(), + new HwController(), + new LabelViewer(), + new WatchWindow(), + CreateCallAnalysisWindow(), + code_viewer = new CodeViewer(), + injector = new Injector(), + membp = new MemBreakPoint(), + CreateAddressWindow(), + MakeAssemblerUI(), + MakeThemeWindow()}) + windows.push_back(item); + for (auto item : GetEditors()) + windows.push_back(item); + + #ifdef __ANDROID__ + for (auto item : windows) { + item->open = false; + } + #endif + + return 0; } void gui_cleanup() { - // Cleanup - ImGui_ImplSDLRenderer2_Shutdown(); - ImGui_ImplSDL2_Shutdown(); - ImGui::DestroyContext(); - - SDL_DestroyRenderer(renderer); - SDL_DestroyWindow(window); - SDL_Quit(); -} + ImGui_ImplSDLRenderer2_Shutdown(); + ImGui_ImplSDL2_Shutdown(); + ImGui::DestroyContext(); + + SDL_DestroyRenderer(renderer); + SDL_DestroyWindow(window); + SDL_Quit(); +} \ No newline at end of file diff --git a/CasioEmuMsvc/Gui/Ui.hpp b/CasioEmuMsvc/Gui/Ui.hpp new file mode 100644 index 0000000..c451a3d --- /dev/null +++ b/CasioEmuMsvc/Gui/Ui.hpp @@ -0,0 +1,60 @@ +#pragma once +#include "Chipset/MMU.hpp" +#include "Emulator.hpp" +#include "LabelFile.h" +#include "imgui/imgui.h" +#ifdef __ANDROID__ +#include "UIScaling.h" +#endif + +int test_gui(bool* guiCreated,SDL_Window*,SDL_Renderer*); +void gui_cleanup(); +void gui_loop(); +extern char* n_ram_buffer; +extern casioemu::MMU* me_mmu; +extern casioemu::Emulator* m_emu; +extern SDL_Window* window; +extern SDL_Renderer* renderer; +extern std::vector