├── .gitignore ├── .gitmodules ├── LICENSE ├── README.md ├── premake5.lua └── source ├── Imports.h ├── Main.cpp ├── RenderWindow.cpp ├── RenderWindow.h ├── V6-VF5FS.cpp ├── VersionInfo.lua ├── Y6-VF5FS.h ├── Y6 ├── ImportSymbols.cpp ├── ImportSymbols.h ├── Patch.cpp ├── Patch.h ├── async_request.cpp ├── async_request.h ├── cs_game.cpp ├── cs_game.h ├── file_access.cpp ├── file_access.h ├── gs.cpp ├── gs.h ├── sl.cpp ├── sl.h ├── sys_util.cpp └── sys_util.h ├── YAMPGeneral.cpp ├── YAMPGeneral.h ├── YAMPSettings.cpp ├── YAMPSettings.h ├── YAMPUserInterface.cpp ├── YAMPUserInterface.h ├── blitShader.hlsl ├── criware ├── CriStub.cpp ├── CriStub.h └── icri.h ├── imgui ├── imconfig.h ├── imgui.cpp ├── imgui.h ├── imgui_demo.cpp ├── imgui_draw.cpp ├── imgui_impl_dx11.cpp ├── imgui_impl_dx11.h ├── imgui_impl_win32.cpp ├── imgui_impl_win32.h ├── imgui_internal.h ├── imgui_tables.cpp ├── imgui_widgets.cpp ├── imstb_rectpack.h ├── imstb_textedit.h └── imstb_truetype.h ├── pxd_shader.h ├── pxd_types.h ├── resources └── VersionInfo.rc ├── sl_internal.h └── wil ├── com.h ├── common.h ├── cppwinrt.h ├── cppwinrt_wrl.h ├── filesystem.h ├── nt_result_macros.h ├── registry.h ├── resource.h ├── result.h ├── result_macros.h ├── result_originate.h ├── rpc_helpers.h ├── safecast.h ├── stl.h ├── token_helpers.h ├── win32_helpers.h ├── winrt.h ├── wistd_config.h ├── wistd_functional.h ├── wistd_memory.h ├── wistd_type_traits.h └── wrl.h /.gitignore: -------------------------------------------------------------------------------- 1 | /build/ 2 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "source/Utils"] 2 | path = source/Utils 3 | url = https://github.com/CookiePLMonster/ModUtils.git 4 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2021 Adrian Zdanowicz (Silent) 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Yakuza Arcade Machines Player 2 | 3 | Yakuza Arcade Machines Player is a launcher that allows you to run Virtua Fighter 5: Final Showdown, 4 | standalone and native, on PC, provided you own a Steam copy of Yakuza 6. 5 | 6 | ## Known issues and shortcomings 7 | 8 | * Audio is not implemented. 9 | * No online features are implemented and are unlikely to be implemented for a long time. 10 | * Only Yakuza 6 is supported so far, Yakuza: Like a Dragon is planned to be added later. 11 | * Just like when playing through in-game arcades, the game renders at fixed 720p and stretches to fullscreen. Proper high resolution rendering support may be added later. 12 | * Offline Versus cannot be played with a keyboard and a gamepad. For now, two gamepads are required. 13 | * Keyboard bindings are hardcoded for now. Please refer to `F1` -> `Controls` for a list of controls. 14 | 15 | # Disclaimer 16 | 17 | **Yakuza Arcade Machines Player does not redistribute ANY copyrighted files.** 18 | **You must own an original Steam copy of Yakuza 6: The Song of Life to play games via YAMP.** 19 | **Pirated game copies WILL NOT receive any support.** 20 | 21 | All rights to Virtua Fighter 5: Final Showdown belong to SEGA. 22 | 23 | [![Preview](https://i.imgur.com/wN49APOl.jpg)](https://i.imgur.com/wN49APO.jpg "Preview") 24 | [![Preview](https://i.imgur.com/gim5Q58l.jpg)](https://i.imgur.com/gim5Q58.jpg "Preview") 25 | [![Preview](https://i.imgur.com/aJH3SALl.jpg)](https://i.imgur.com/aJH3SAL.jpg "Preview") 26 | [![Preview](https://i.imgur.com/hqdC9ikl.jpg)](https://i.imgur.com/hqdC9ik.jpg "Preview") 27 | -------------------------------------------------------------------------------- /premake5.lua: -------------------------------------------------------------------------------- 1 | workspace "YAMP" 2 | platforms { "Win64" } 3 | 4 | project "YAMP" 5 | kind "WindowedApp" 6 | language "C++" 7 | 8 | include "source/VersionInfo.lua" 9 | files { "**/MemoryMgr.h", "**/Trampoline.h", "**/Patterns.*" } 10 | 11 | 12 | workspace "*" 13 | configurations { "Debug", "Release", "Master" } 14 | location "build" 15 | 16 | vpaths { ["Headers/*"] = "source/**.h", 17 | ["Sources/*"] = { "source/**.c", "source/**.cpp" }, 18 | ["Resources"] = "source/**.rc" 19 | } 20 | 21 | files { "source/*.h", "source/*.cpp", "source/resources/*.rc", "source/criware/*", "source/wil/*", "source/Y6/*", 22 | "source/imgui/*" } 23 | 24 | cppdialect "C++17" 25 | staticruntime "on" 26 | buildoptions { "/sdl" } 27 | warnings "Extra" 28 | 29 | -- Automated defines for resources 30 | defines { "rsc_Extension=\"%{prj.targetextension}\"", 31 | "rsc_Name=\"%{prj.name}\"" } 32 | 33 | filter "configurations:Debug" 34 | defines { "DEBUG" } 35 | runtime "Debug" 36 | 37 | filter "configurations:Master" 38 | defines { "NDEBUG", "RESULT_DIAGNOSTICS_LEVEL=0", "RESULT_INCLUDE_CALLER_RETURNADDRESS=0" } 39 | symbols "Off" 40 | 41 | filter "configurations:not Debug" 42 | optimize "Speed" 43 | functionlevellinking "on" 44 | flags { "LinkTimeOptimization" } 45 | 46 | filter { "platforms:Win32" } 47 | system "Windows" 48 | architecture "x86" 49 | 50 | filter { "platforms:Win64" } 51 | system "Windows" 52 | architecture "x86_64" 53 | 54 | filter { "toolset:*_xp"} 55 | defines { "WINVER=0x0501", "_WIN32_WINNT=0x0501" } -- Target WinXP 56 | buildoptions { "/Zc:threadSafeInit-" } 57 | 58 | filter { "toolset:not *_xp"} 59 | defines { "WINVER=0x0601", "_WIN32_WINNT=0x0601" } -- Target Win7 60 | buildoptions { "/permissive-" } -------------------------------------------------------------------------------- /source/Imports.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | #include "wil/common.h" 7 | 8 | enum class ImportSymbol; // Symbols are per-DLL 9 | 10 | class Imports 11 | { 12 | public: 13 | Imports(std::initializer_list> symbols) 14 | : m_symbolMap(std::move(symbols)) 15 | { 16 | } 17 | 18 | Imports(const Imports&) = delete; 19 | Imports(Imports&&) = default; 20 | 21 | void Add(ImportSymbol key, void* value) 22 | { 23 | m_symbolMap.emplace(key, value); 24 | } 25 | 26 | auto GetSymbol(ImportSymbol symbol) const 27 | { 28 | assert(m_symbolMap.count(symbol) == 1); 29 | return m_symbolMap.find(symbol)->second; 30 | } 31 | 32 | auto GetSymbolRange(ImportSymbol symbol) const 33 | { 34 | assert(m_symbolMap.count(symbol) != 0); 35 | auto range = m_symbolMap.equal_range(symbol); 36 | return wil::make_range(range.first, range.second); 37 | } 38 | 39 | private: 40 | std::multimap m_symbolMap; 41 | }; -------------------------------------------------------------------------------- /source/Main.cpp: -------------------------------------------------------------------------------- 1 | #define WIN32_LEAN_AND_MEAN 2 | #include 3 | 4 | #include "RenderWindow.h" 5 | #include "Y6-VF5FS.h" 6 | #include "imgui/imgui.h" 7 | 8 | #include "wil/com.h" 9 | 10 | int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nShowCmd) 11 | { 12 | // TODO: This is a hack, currently shutdown crashes because of mismatched allocators 13 | // Once this is handled, remove this 14 | auto shutdownHack = wil::scope_exit([] { TerminateProcess(GetCurrentProcess(), 0); }); 15 | auto coinit = wil::CoInitializeEx(COINIT_MULTITHREADED); 16 | 17 | IMGUI_CHECKVERSION(); 18 | ImGui::CreateContext(); 19 | 20 | ImGuiIO& io = ImGui::GetIO(); 21 | io.IniFilename = nullptr; 22 | io.ConfigFlags |= ImGuiConfigFlags_NoMouseCursorChange; 23 | 24 | HMODULE dll = Y6::VF5FS::LoadDLL(); 25 | if (dll == nullptr) 26 | { 27 | // TODO: Show a native error message about game DLL not found 28 | return -1; 29 | } 30 | 31 | Y6::VF5FS::PreInitialize(); 32 | 33 | RenderWindow window(hInstance, dll, nShowCmd); 34 | 35 | Y6::VF5FS::Run(window); 36 | 37 | return 0; 38 | } -------------------------------------------------------------------------------- /source/RenderWindow.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "YAMPUserInterface.h" 4 | 5 | #define WIN32_LEAN_AND_MEAN 6 | #include 7 | #include 8 | 9 | #include "wil/com.h" 10 | #include "wil/resource.h" 11 | 12 | #include 13 | #include 14 | #include 15 | 16 | // A simple manager for the window handle and the D3D11 device 17 | class RenderWindow 18 | { 19 | public: 20 | RenderWindow(HINSTANCE instance, HINSTANCE dllInstance, int cmdShow); 21 | ~RenderWindow(); 22 | 23 | ID3D11Device* GetD3D11Device() const { return m_device.get(); } 24 | ID3D11DeviceContext* GetD3D11DeviceContext() const { return m_deviceContext.get(); } 25 | IDXGISwapChain* GetSwapChain() const { return m_swapChain.get(); } 26 | 27 | void BlitGameFrame(ID3D11ShaderResourceView* src); 28 | 29 | void NewImGuiFrame(); 30 | void RenderImGui(); 31 | 32 | uint32_t GetWidth() const { return m_width; } 33 | uint32_t GetHeight() const { return m_height; } 34 | 35 | bool IsShuttingDown() const { return m_shuttingDownWindow.load(std::memory_order_relaxed); } 36 | 37 | private: 38 | wil::com_ptr CreateSwapChainForWindow(ID3D11Device* device, HWND window); 39 | void CreateRenderResources(); 40 | void EnumerateDisplayModes(); 41 | void CalculateViewport(); 42 | 43 | std::atomic_bool m_shuttingDownWindow { false }; 44 | std::thread m_windowThread; 45 | wil::unique_hwnd m_window; 46 | 47 | wil::com_ptr m_device; 48 | wil::com_ptr m_deviceContext; 49 | wil::com_ptr m_swapChain; 50 | 51 | // Render resources required to render to backbuffer 52 | wil::com_ptr m_backBufferRTV; 53 | wil::com_ptr m_vs; 54 | wil::com_ptr m_ps; 55 | wil::com_ptr m_inputLayout; 56 | wil::com_ptr m_vb; 57 | UINT m_vbStride; 58 | D3D11_VIEWPORT m_viewport; 59 | bool m_requiresClear = false; 60 | 61 | uint32_t m_width, m_height; 62 | 63 | YAMPUserInterface m_ui; 64 | }; -------------------------------------------------------------------------------- /source/V6-VF5FS.cpp: -------------------------------------------------------------------------------- 1 | #include "Y6-VF5FS.h" 2 | 3 | #define WIN32_LEAN_AND_MEAN 4 | #include 5 | #include "wil/resource.h" 6 | 7 | #include "criware/CriStub.h" 8 | 9 | #include "Y6/sl.h" 10 | #include "Y6/gs.h" 11 | #include "Imports.h" 12 | #include "Y6/Patch.h" 13 | #include "Y6/sys_util.h" 14 | #include "Y6/cs_game.h" 15 | 16 | #include "Y6/ImportSymbols.h" 17 | #include "Imports.h" 18 | #include "YAMPGeneral.h" 19 | 20 | #include "Utils/MemoryMgr.h" 21 | #include "Utils/ScopedUnprotect.hpp" 22 | 23 | static const wchar_t* DLL_NAME = L"vf5fs-pxd-w64-Retail Steam.dll"; 24 | 25 | // Contexts 26 | // TODO: Move elsewhere, as they will get very, very long 27 | // But not in V6-VF5FS.h as they are private! 28 | 29 | // Seems unused, so don't bother getting it right 30 | struct ct_context_t 31 | { 32 | uint32_t tag_id; // Unknown 33 | uint32_t version; // Unknown 34 | uint32_t size_of_struct = sizeof(decltype(*this)); // Should be 48 when complete 35 | }; 36 | 37 | // TODO: Later these can be put by value, for now put them on the heap to make full use of page heap 38 | // and catch any out-of-bounds access 39 | ct_context_t* g_ct_context = new ct_context_t; 40 | 41 | // Configs 42 | struct vf5fs_game_config_t 43 | { 44 | // TODO: Fill with defaults 45 | uint16_t energy; 46 | int8_t round; 47 | int8_t time; 48 | int8_t diff; 49 | int8_t game_mode; 50 | int8_t lang; 51 | bool is_triangle_start : 4; 52 | bool is_dural_unlocked : 4; 53 | }; 54 | static_assert(sizeof(vf5fs_game_config_t) == 8); 55 | 56 | struct alignas(16) vf5fs_execute_info_t 57 | { 58 | enum assign_t 59 | { 60 | assign_invalid = 0x0, 61 | assign_none = 0x1, 62 | assign_p = 0x2, 63 | assign_k = 0x3, 64 | assign_g = 0x4, 65 | assign_pg = 0x5, 66 | assign_pkg = 0x6, 67 | assign_pk = 0x7, 68 | assign_kg = 0x8, 69 | }; 70 | 71 | 72 | size_t size_of_struct; 73 | cgs_device_context* p_device_context; 74 | int status; 75 | int result; 76 | unsigned int output_texid; 77 | std::byte gap[4]; 78 | uint8_t assign[2][8]; 79 | csl_pad pad[2]; 80 | std::byte gap2[16]; 81 | }; 82 | static_assert(sizeof(vf5fs_execute_info_t) == 800); 83 | static_assert(offsetof(vf5fs_execute_info_t, assign) == 0x20); 84 | static_assert(offsetof(vf5fs_execute_info_t, pad) == 0x30); 85 | 86 | static void ImportFunctions(const Imports& symbols) 87 | { 88 | auto Import = [&symbols](auto& var, auto symbol) 89 | { 90 | var = static_cast>(symbols.GetSymbol(symbol)); 91 | }; 92 | 93 | Import(sl::sm_context, ImportSymbol::SL_CONTEXT_INSTANCE); 94 | Import(gs::sm_context, ImportSymbol::GS_CONTEXT_INSTANCE); 95 | Import(sl::file_create_internal, ImportSymbol::SL_FILE_CREATE); 96 | Import(sl::file_open_internal, ImportSymbol::SL_FILE_OPEN); 97 | Import(sl::file_read, ImportSymbol::SL_FILE_READ); 98 | Import(sl::file_close, ImportSymbol::SL_FILE_CLOSE); 99 | Import(sl::handle_create_internal, ImportSymbol::SL_HANDLE_CREATE); 100 | Import(sl::file_handle_destroy, ImportSymbol::SL_FILE_HANDLE_DESTROY); 101 | Import(sl::archive_lock_wlock, ImportSymbol::ARCHIVE_LOCK_WLOCK); 102 | Import(sl::archive_lock_wunlock, ImportSymbol::ARCHIVE_LOCK_WUNLOCK); 103 | Import(cgs_device_context::reset_state_all_internal, ImportSymbol::DEVICE_CONTEXT_RESET_STATE_ALL); 104 | Import(gs::vb_create, ImportSymbol::VB_CREATE); 105 | Import(gs::ib_create, ImportSymbol::IB_CREATE); 106 | Import(shift_next_mode, ImportSymbol::SHIFT_NEXT_MODE); 107 | Import(shift_next_mode_sub, ImportSymbol::SHIFT_NEXT_MODE_SUB); 108 | } 109 | 110 | static void PrefillVariables(const Imports& symbols, const RenderWindow& window) 111 | { 112 | auto Import = [&symbols](auto& var, auto symbol) 113 | { 114 | var = static_cast>(symbols.GetSymbol(symbol)); 115 | }; 116 | 117 | gs::context_t** ppContext; 118 | Import(ppContext, ImportSymbol::GS_CONTEXT_PTR); 119 | *ppContext = gs::sm_context; 120 | 121 | ID3D11Device** ppDevice; 122 | Import(ppDevice, ImportSymbol::D3DDEVICE); 123 | *ppDevice = window.GetD3D11Device(); 124 | } 125 | 126 | static bool ResolveSymbolsAndInstallPatches(void* dll, const RenderWindow& window) try 127 | { 128 | const Imports symbolMap = BuildSymbolMap(dll); 129 | 130 | const ScopedUnprotect::Section text(static_cast(dll), ".text"); 131 | const ScopedUnprotect::Section rdata(static_cast(dll), ".rdata"); 132 | 133 | // Patch up structures and do post-DllMain work here 134 | // Saves having to reimplement all the complex constructors and data types 135 | ImportFunctions(symbolMap); 136 | PrefillVariables(symbolMap, window); // Pre-fill those variables gs/sl initialization relies on 137 | 138 | if (!gGeneral.GetSettings()->m_dontApplyPatches) 139 | { 140 | // Install hooks re-adding logging 141 | ReinstateLogging(dll, symbolMap); 142 | // Install additional "assertions" 143 | InjectTraps(symbolMap); 144 | 145 | // Restore saving 146 | Patch_SysUtil(dll, symbolMap); 147 | 148 | // Misc patches - bugfixes, un-folding no-ops etc 149 | Patch_Misc(dll, symbolMap); 150 | Patch_CsGame(dll, symbolMap); 151 | } 152 | 153 | PatchSl(sl::sm_context); 154 | PatchGs(gs::sm_context, window); 155 | 156 | return true; 157 | } 158 | catch (...) 159 | { 160 | // TODO: Show this in native UI 161 | const std::wstring str(L"Failed to resolve imports and/or patch " + std::wstring(DLL_NAME) + L".dll!\n\nIt's either not a valid Virtua Fighter 5: Final Showdown DLL file from Yakuza 6, " 162 | "or the game has been updated and YAMP is not forward compatible with that new version."); 163 | MessageBoxW(nullptr, str.c_str(), L"Yakuza Arcade Machines Player", MB_ICONERROR | MB_OK); 164 | 165 | return false; 166 | } 167 | 168 | // TODO: Move elsewhere 169 | static std::filesystem::path gamePath; 170 | 171 | static wil::unique_hmodule gameDll; 172 | HMODULE Y6::VF5FS::LoadDLL() 173 | { 174 | // TODO: Clean up 175 | { 176 | DWORD dwSize = GetCurrentDirectoryW(0, nullptr); 177 | auto buf = std::make_unique(dwSize); 178 | GetCurrentDirectoryW(dwSize, buf.get()); 179 | gamePath.assign(buf.get()); 180 | } 181 | 182 | gameDll.reset(LoadLibraryW((gamePath / DLL_NAME).c_str())); 183 | if (gameDll == nullptr) 184 | { 185 | // Try loading from a subdirectory 186 | gamePath.append(L"vf5fs"); 187 | gameDll.reset(LoadLibraryW((gamePath / DLL_NAME).c_str())); 188 | } 189 | 190 | if (!gameDll) 191 | { 192 | const std::wstring str(L"Could not load " + std::wstring(DLL_NAME) + L"!\n\nMake sure that YAMP.exe is located in your Yakuza 6: The Song of Life directory or its \"vf5fs\" subdirectory, next to the DLL file."); 193 | MessageBoxW(nullptr, str.c_str(), L"Yakuza Arcade Machines Player", MB_ICONERROR | MB_OK); 194 | } 195 | else 196 | { 197 | gGeneral.SetDLLName(WcharToUTF8(DLL_NAME)); 198 | 199 | // Get the checksum 200 | PIMAGE_DOS_HEADER dosHeader = reinterpret_cast(gameDll.get()); 201 | PIMAGE_NT_HEADERS ntHeader = reinterpret_cast(reinterpret_cast(dosHeader) + dosHeader->e_lfanew); 202 | const DWORD timeStamp = ntHeader->FileHeader.TimeDateStamp; 203 | gGeneral.SetDLLTimestamp(timeStamp); 204 | 205 | // Reject known old DLLs 206 | if (timeStamp == 0x603E22E3 || timeStamp == 0x606D6969 || timeStamp == 0x6075A65A) 207 | { 208 | const std::wstring str(std::wstring(DLL_NAME) + L" is of an unsupported version!\n\nPlease update your Yakuza 6: The Song of Life to the latest version."); 209 | MessageBoxW(nullptr, str.c_str(), L"Yakuza Arcade Machines Player", MB_ICONERROR | MB_OK); 210 | gameDll.reset(); 211 | } 212 | } 213 | 214 | return gameDll.get(); 215 | } 216 | 217 | void Y6::VF5FS::PreInitialize() 218 | { 219 | gGeneral.SetDataPath(u8"Sega", u8"Virtua Fighter 5 Final Showdown"); 220 | gGeneral.LoadSettings(); 221 | } 222 | 223 | static void CheckForExecutable() 224 | { 225 | // TODO: Make more graceful instead of killing the app 226 | const std::wstring executablePath = (gamePath.parent_path() / L"Yakuza6.exe").native(); 227 | 228 | // We'll consider the file valid if it has MZ and PE magic values 229 | wil::unique_hfile file(CreateFileW(executablePath.c_str(), GENERIC_READ, FILE_SHARE_READ, nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr)); 230 | FAIL_FAST_IMMEDIATE_IF(!file); 231 | 232 | wil::unique_handle fileMapping(CreateFileMapping(file.get(), nullptr, PAGE_READONLY, 0, 0, nullptr)); 233 | FAIL_FAST_IMMEDIATE_IF_NULL(fileMapping); 234 | 235 | wil::unique_mapview_ptr dosHeaderView(static_cast(MapViewOfFile(fileMapping.get(), FILE_MAP_READ, 0, 0, 0))); 236 | FAIL_FAST_IMMEDIATE_IF_NULL(dosHeaderView); 237 | 238 | FAIL_FAST_IMMEDIATE_IF(dosHeaderView->e_magic != 'ZM'); 239 | 240 | PIMAGE_NT_HEADERS64 peHeaderView = reinterpret_cast(reinterpret_cast(dosHeaderView.get()) + dosHeaderView->e_lfanew); 241 | 242 | FAIL_FAST_IMMEDIATE_IF(peHeaderView->Signature != 'EP'); 243 | } 244 | 245 | void Y6::VF5FS::Run(RenderWindow& window) 246 | { 247 | const auto module_start = reinterpret_cast(GetProcAddress(gameDll.get(), "module_start")); 248 | THROW_LAST_ERROR_IF_NULL(module_start); 249 | const auto module_stop = reinterpret_cast(GetProcAddress(gameDll.get(), "module_stop")); 250 | THROW_LAST_ERROR_IF_NULL(module_stop); 251 | module_func_t module_main; 252 | 253 | CheckForExecutable(); 254 | 255 | if (!ResolveSymbolsAndInstallPatches(gameDll.get(), window)) 256 | { 257 | // Init failed 258 | return; 259 | } 260 | 261 | // Initialize Criware stub and module stubs 262 | CriStub criware_stub; 263 | 264 | struct sl_module_t 265 | { 266 | size_t size = sizeof(decltype(*this)); 267 | sl::context_t* context; 268 | } sl_module; 269 | sl_module.context = sl::sm_context; 270 | 271 | struct gs_module_t 272 | { 273 | size_t size = sizeof(decltype(*this)); // Should be 80 when complete, but other fields are unknown so far 274 | gs::context_t* context; 275 | uint8_t pad[64]; 276 | } gs_module; 277 | gs_module.context = gs::sm_context; 278 | 279 | const struct ct_module_t 280 | { 281 | size_t size = sizeof(decltype(*this)); 282 | ct_context_t* context = g_ct_context; 283 | } ct_module; 284 | 285 | struct module_params_t 286 | { 287 | size_t size = sizeof(decltype(*this)); 288 | const sl_module_t* sl_module; 289 | const gs_module_t* gs_module; 290 | const ct_module_t* ct_module; 291 | const icri* cri_ptr; 292 | const char* root_path; 293 | module_func_t* module_main; 294 | vf5fs_game_config_t config{}; 295 | } params; 296 | static_assert(sizeof(params) == 64); 297 | 298 | const std::string utf8Path = gamePath.u8string(); 299 | 300 | params.sl_module = &sl_module; 301 | params.gs_module = &gs_module; 302 | params.ct_module = &ct_module; 303 | params.cri_ptr = &criware_stub; 304 | params.module_main = &module_main; 305 | params.root_path = utf8Path.c_str(); 306 | 307 | const auto* settings = gGeneral.GetSettings(); 308 | 309 | params.config.is_dural_unlocked = false; 310 | params.config.is_triangle_start = false; 311 | params.config.game_mode = settings->m_arcadeMode ? 1 : 0; 312 | params.config.lang = settings->m_language; 313 | params.config.diff = 1; 314 | params.config.energy = 200; 315 | params.config.round = 2; 316 | params.config.time = 60; 317 | 318 | // Set up a FPS limiter 319 | // TODO: Do more gracefully 320 | int64_t frameTimeTicks; 321 | { 322 | // We want to enforce 60 FPS, unless the cap is disabled in Debug 323 | if (!settings->m_enableFpsCap) 324 | { 325 | frameTimeTicks = 0; 326 | } 327 | else 328 | { 329 | LARGE_INTEGER frequency; 330 | QueryPerformanceFrequency(&frequency); 331 | frameTimeTicks = (frequency.QuadPart * 50) / 3; 332 | } 333 | } 334 | 335 | // Kick off the game 336 | if (module_start(sizeof(params), ¶ms) == 0) 337 | { 338 | LARGE_INTEGER lastTime; 339 | QueryPerformanceCounter(&lastTime); 340 | while (!window.IsShuttingDown()) 341 | { 342 | if (!GameLoop(module_main, window)) break; 343 | 344 | // TODO: Waitable timer 345 | LARGE_INTEGER currentTime; 346 | do 347 | { 348 | QueryPerformanceCounter(¤tTime); 349 | } 350 | while (((currentTime.QuadPart - lastTime.QuadPart) * 1000) < frameTimeTicks); 351 | lastTime = currentTime; 352 | } 353 | 354 | // TODO: module_stop 355 | } 356 | } 357 | 358 | bool Y6::VF5FS::GameLoop(module_func_t func, RenderWindow& window) 359 | { 360 | vf5fs_execute_info_t execute_info{}; 361 | execute_info.size_of_struct = sizeof(execute_info); 362 | execute_info.p_device_context = gs::sm_context->p_device_context; 363 | 364 | // TODO: Set up mappings better 365 | // They're ignored now, in-game mappings are used instead 366 | /*{ 367 | size_t i = 0; 368 | auto& mappings1P = execute_info.assign[0]; 369 | auto& mappings2P = execute_info.assign[1]; 370 | mappings1P[i] = mappings2P[i] = vf5fs_execute_info_t::assign_p; i++; 371 | mappings1P[i] = mappings2P[i] = vf5fs_execute_info_t::assign_k; i++; 372 | mappings1P[i] = mappings2P[i] = vf5fs_execute_info_t::assign_g; i++; 373 | mappings1P[i] = mappings2P[i] = vf5fs_execute_info_t::assign_p; i++; 374 | mappings1P[i] = mappings2P[i] = vf5fs_execute_info_t::assign_pg; i++; 375 | mappings1P[i] = mappings2P[i] = vf5fs_execute_info_t::assign_pkg; i++; 376 | mappings1P[i] = mappings2P[i] = vf5fs_execute_info_t::assign_pk; i++; 377 | mappings1P[i] = mappings2P[i] = vf5fs_execute_info_t::assign_kg; i++; 378 | }*/ 379 | 380 | // TODO: Beautify 381 | execute_info.pad[0].set_state(0); 382 | execute_info.pad[1].set_state(1); 383 | execute_info.status |= 2; 384 | 385 | window.NewImGuiFrame(); 386 | 387 | if (func(sizeof(execute_info), &execute_info) != 0) return false; 388 | 389 | cgs_tex* display_tex = gs::sm_context->handle_tex.get(execute_info.output_texid); 390 | if (display_tex == nullptr) return false; 391 | if (display_tex->m_type != 2) return false; 392 | 393 | // TODO: Beautify this 394 | auto& swapChain = gs::sm_context->sbgl_device.m_swap_chain; 395 | window.BlitGameFrame(display_tex->mp_sbgl_resource->m_pD3DShaderResourceView); 396 | 397 | window.RenderImGui(); 398 | 399 | HRESULT hr = swapChain.m_pDXGISwapChain->Present(1, 0); 400 | if (FAILED(hr)) return false; 401 | 402 | gs::sm_context->frame_counter++; 403 | return true; 404 | } 405 | -------------------------------------------------------------------------------- /source/VersionInfo.lua: -------------------------------------------------------------------------------- 1 | defines { 2 | "rsc_FullName=\"Yakuza Arcade Machines Player\"", 3 | "rsc_MinorVersion=0", 4 | "rsc_RevisionID=1", 5 | "rsc_BuildID=1", 6 | "rsc_Copyright=\"2021\"", 7 | "rsc_UpdateURL=\"https://github.com/CookiePLMonster/YAMP/releases\"" 8 | } -------------------------------------------------------------------------------- /source/Y6-VF5FS.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "RenderWindow.h" 4 | 5 | using module_func_t = int(*)(size_t args, const void* argp); 6 | 7 | namespace Y6 8 | { 9 | namespace VF5FS 10 | { 11 | HMODULE LoadDLL(); 12 | void PreInitialize(); 13 | void Run(RenderWindow& window); 14 | bool GameLoop(module_func_t func, RenderWindow& window); 15 | } 16 | } -------------------------------------------------------------------------------- /source/Y6/ImportSymbols.cpp: -------------------------------------------------------------------------------- 1 | #include "ImportSymbols.h" 2 | #include "../Imports.h" 3 | 4 | #include "../Utils/Patterns.h" 5 | #include "../Utils/MemoryMgr.h" 6 | 7 | using namespace hook::txn; 8 | 9 | template 10 | static auto get_module_pattern(void* module, std::string_view pattern_string, ptrdiff_t offset = 0) 11 | { 12 | return pattern(module, std::move(pattern_string)).get_first(offset); 13 | } 14 | 15 | static void* immediate(void* addr) 16 | { 17 | void* val; 18 | Memory::ReadOffsetValue(addr, val); 19 | return val; 20 | } 21 | 22 | static void* immediate8(void* addr) 23 | { 24 | intptr_t srcAddr = (intptr_t)addr; 25 | intptr_t dstAddr = srcAddr + 1 + *(int8_t*)srcAddr; 26 | return reinterpret_cast(dstAddr); 27 | } 28 | 29 | Imports BuildSymbolMap(void* dll) 30 | { 31 | using S = ImportSymbol; 32 | 33 | Imports symbols{ 34 | // Functions/globals 35 | { S::SL_CONTEXT_INSTANCE, immediate(get_module_pattern(dll, "48 89 5C 24 ? 48 8D 3D", 5 + 3)) }, 36 | { S::GS_CONTEXT_INSTANCE, immediate(get_module_pattern(dll, "48 8D 2D ? ? ? ? 48 89 68 08", 3)) }, 37 | { S::GS_CONTEXT_PTR, immediate(get_module_pattern(dll, "48 8B 05 ? ? ? ? 8B F1 BA", 3)) }, 38 | { S::D3DDEVICE, immediate(get_module_pattern(dll, "48 89 05 ? ? ? ? 48 8B 41 28", 3)) }, 39 | { S::SL_FILE_CREATE, get_module_pattern(dll, "48 8B 05 ? ? ? ? 48 8B F9", -0x13) }, 40 | { S::SL_FILE_OPEN, get_module_pattern(dll, "48 8B 05 ? ? ? ? 48 8B D9 45 33 F6", -0x12) }, 41 | { S::SL_FILE_READ, get_module_pattern(dll, "4C 8B 0D ? ? ? ? 8B C1", -0x6) }, 42 | { S::SL_FILE_CLOSE, immediate(get_module_pattern(dll, "E8 ? ? ? ? 48 C7 44 3B", 1)) }, 43 | { S::SL_HANDLE_CREATE, get_module_pattern(dll, "48 8B 3D ? ? ? ? 48 8B F1 45 33 FF", -0x18) }, 44 | { S::SL_FILE_HANDLE_DESTROY, get_module_pattern(dll, "48 89 5C 24 ? 48 81 C1", -0x11) }, 45 | { S::PRJ_TRAP, immediate(get_module_pattern(dll, "E8 ? ? ? ? 8B 43 0C 4C 3B E0", 1)) }, 46 | { S::ARCHIVE_LOCK_WLOCK, immediate(get_module_pattern(dll, "E8 ? ? ? ? 8B 7B 18", 1)) }, 47 | { S::ARCHIVE_LOCK_WUNLOCK, immediate(get_module_pattern(dll, "E8 ? ? ? ? 90 83 7B 24 00", 1)) }, 48 | { S::DEVICE_CONTEXT_RESET_STATE_ALL, get_module_pattern(dll, "48 83 EC 30 48 83 09 01", -0x6) }, 49 | { S::VB_CREATE, get_module_pattern(dll, "48 83 EC 40 48 8B 05 ? ? ? ? 48 8B F1", -0x14) }, 50 | { S::IB_CREATE, get_module_pattern(dll, "48 8B 05 ? ? ? ? 44 8B F1", -0x18) }, 51 | { S::SHIFT_NEXT_MODE, immediate(get_module_pattern(dll, "E8 ? ? ? ? 33 C9 83 7B 58 01", 1)) }, 52 | { S::SHIFT_NEXT_MODE_SUB, immediate(get_module_pattern(dll, "E8 ? ? ? ? 41 B7 01", 1)) }, 53 | 54 | // Places to patch 55 | { S::TRAP_ALLOC_INSTANCE_TBL, immediate8(get_module_pattern(dll, "73 ? 4C 8B 41 08", 1)) }, 56 | { S::SYS_UTIL_CHECK_ENABLE_STORAGE_PATCH, get_module_pattern(dll, "E8 ? ? ? ? 84 C0 74 0D 33 D2") }, 57 | { S::SYS_UTIL_CHECK_ENABLE_STORAGE_PATCH, get_module_pattern(dll, "8B C8 E8 ? ? ? ? 84 C0 0F 85 ? ? ? ? 48 8D 8B", 2) }, 58 | { S::SYS_UTIL_CHECK_ENABLE_STORAGE_PATCH, get_module_pattern(dll, "E8 ? ? ? ? 48 8B CB 84 C0 75 26") }, 59 | { S::SYS_UTIL_CHECK_ENABLE_STORAGE_PATCH, get_module_pattern(dll, "E8 ? ? ? ? 84 C0 74 09 40 84 ED") }, 60 | { S::SYS_UTIL_CHECK_ENABLE_STORAGE_PATCH, get_module_pattern(dll, "8B CB E8 ? ? ? ? 84 C0 74 6C", 2) }, 61 | { S::SYS_UTIL_CHECK_ENABLE_STORAGE_PATCH, get_module_pattern(dll, "8B CB E8 ? ? ? ? 84 C0 74 56", 2) }, 62 | { S::SYS_UTIL_CHECK_ENABLE_STORAGE_PATCH, get_module_pattern(dll, "B9 FF FF FF FF E8 ? ? ? ? 84 C0 75 0D", 5) }, 63 | { S::SYS_UTIL_CHECK_ENABLE_STORAGE_PATCH, get_module_pattern(dll, "4C 89 7C 24 ? E8 ? ? ? ? 84 C0", 5) }, 64 | { S::SYS_UTIL_START_LOAD_SYSTEMDATA_TASK_PATCH, get_module_pattern(dll, "40 88 7C 24 ? E8 ? ? ? ? 48 8B 5C 24", 5) }, 65 | { S::SYS_UTIL_START_SAVE_SYSTEMDATA_TASK_PATCH, get_module_pattern(dll, "41 B8 FC 4F 00 00 8B CB E8", 6 + 2) }, 66 | { S::SYS_UTIL_IS_ENTER_CIRCLE_PATCH, get_module_pattern(dll, "E8 ? ? ? ? 84 C0 4C 8D 15") }, 67 | { S::SYS_UTIL_IS_ENTER_CIRCLE_PATCH, get_module_pattern(dll, "E8 ? ? ? ? 84 C0 75 2A 83 FB 30") }, 68 | { S::ASSIGN_HELPER_ENABLE_SHARED_FROM_THIS_PATCH, get_module_pattern(dll, "48 8B D6 48 8D 4C 24 ? E8 ? ? ? ? 90", 3 + 5) }, 69 | { S::VF5_APP_CTOR_PATCH, get_module_pattern(dll, "E8 ? ? ? ? 48 8D 05 ? ? ? ? 48 89 03 EB 03") }, 70 | { S::PRESS_START_POS_Y_PATCH, immediate(get_module_pattern(dll, "75 3F C5 FA 10 0D", 2 + 4)) }, 71 | }; 72 | 73 | // Other special cases 74 | auto cs_switch_mapping_override = make_module_pattern(dll, "66 42 89 84 33").count(8); 75 | cs_switch_mapping_override.for_each_result([&symbols](hook::pattern_match match) { 76 | symbols.Add(S::CS_SWITCH_MAPPING_OVERRIDE_PATCH, match.get()); 77 | }); 78 | 79 | auto sys_util_is_enter_circle = make_module_pattern(dll, "57 48 83 EC 20 E8 ? ? ? ? 84 C0").count(3); // + 2 from a different pattern 80 | sys_util_is_enter_circle.for_each_result([&symbols](hook::pattern_match match) { 81 | symbols.Add(S::SYS_UTIL_IS_ENTER_CIRCLE_PATCH, match.get(1 + 4)); 82 | }); 83 | 84 | auto task_pause_ctrl_countdown = make_module_pattern(dll, "C5 FA 10 71 ? E8 ? ? ? ? C5 CA 5C D0").count(2); 85 | task_pause_ctrl_countdown.for_each_result([&symbols](hook::pattern_match match) { 86 | symbols.Add(S::TASK_PAUSE_CTRL_COUNTDOWN_PATCH, match.get(5)); 87 | }); 88 | 89 | auto press_start_pos_x_ptr = make_module_pattern(dll, "C5 FA 10 05 ? ? ? ? 41 B8 09 00 00 00 E8 ? ? ? ? B9 19 00 00 00").count(3); 90 | press_start_pos_x_ptr.for_each_result([&symbols](hook::pattern_match match) { 91 | symbols.Add(S::PRESS_START_POS_X_PTR_PATCH, match.get(4)); 92 | }); 93 | 94 | struct ModeSubTable 95 | { 96 | uint32_t name; 97 | bool pause_enable; 98 | bool (__fastcall *func[3])(); 99 | }; 100 | auto mode_sub_table = static_cast(immediate(get_module_pattern(dll, "4C 8D 05 ? ? ? ? 49 8B C0 40 32 FF", 3))); 101 | symbols.Add(S::DEST_CS_AUTOLOAD_PATCH, &mode_sub_table[5].func[2]); 102 | 103 | return symbols; 104 | } 105 | -------------------------------------------------------------------------------- /source/Y6/ImportSymbols.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | enum class ImportSymbol 4 | { 5 | SL_CONTEXT_INSTANCE, 6 | GS_CONTEXT_INSTANCE, 7 | GS_CONTEXT_PTR, 8 | D3DDEVICE, 9 | 10 | SL_FILE_CREATE, 11 | SL_FILE_OPEN, 12 | SL_FILE_READ, 13 | // file_write is implemented by us, this is not a mistake 14 | SL_FILE_CLOSE, 15 | 16 | SL_HANDLE_CREATE, 17 | SL_FILE_HANDLE_DESTROY, 18 | PRJ_TRAP, 19 | 20 | ARCHIVE_LOCK_WLOCK, 21 | ARCHIVE_LOCK_WUNLOCK, 22 | 23 | DEVICE_CONTEXT_RESET_STATE_ALL, 24 | VB_CREATE, 25 | IB_CREATE, 26 | 27 | TRAP_ALLOC_INSTANCE_TBL, 28 | 29 | SHIFT_NEXT_MODE, 30 | SHIFT_NEXT_MODE_SUB, 31 | 32 | // Patches 33 | SYS_UTIL_START_LOAD_SYSTEMDATA_TASK_PATCH, 34 | SYS_UTIL_START_SAVE_SYSTEMDATA_TASK_PATCH, 35 | SYS_UTIL_CHECK_ENABLE_STORAGE_PATCH, 36 | SYS_UTIL_IS_ENTER_CIRCLE_PATCH, 37 | ASSIGN_HELPER_ENABLE_SHARED_FROM_THIS_PATCH, 38 | TASK_PAUSE_CTRL_COUNTDOWN_PATCH, 39 | VF5_APP_CTOR_PATCH, 40 | PRESS_START_POS_Y_PATCH, 41 | PRESS_START_POS_X_PTR_PATCH, 42 | CS_SWITCH_MAPPING_OVERRIDE_PATCH, 43 | DEST_CS_AUTOLOAD_PATCH, 44 | }; 45 | 46 | class Imports BuildSymbolMap(void* dll); -------------------------------------------------------------------------------- /source/Y6/Patch.cpp: -------------------------------------------------------------------------------- 1 | #include "Patch.h" 2 | 3 | #include "file_access.h" 4 | #include "../pxd_types.h" 5 | #include "async_request.h" 6 | 7 | #include "../wil/common.h" 8 | #include "../Utils/MemoryMgr.h" 9 | #include "../Utils/Trampoline.h" 10 | 11 | #include "sys_util.h" 12 | #include "cs_game.h" 13 | 14 | #include "ImportSymbols.h" 15 | #include "../Imports.h" 16 | 17 | void PatchSl(sl::context_t* context) 18 | { 19 | // Populate handle_free_queue 20 | static constexpr size_t NUM_HANDLES = 1000; 21 | 22 | context->handle_max = NUM_HANDLES; 23 | context->handles.p_handle_buffer = new sl::handle_internal_buffer_t[NUM_HANDLES]; 24 | 25 | for (auto& handle : wil::make_range(context->handles.p_handle_buffer, NUM_HANDLES)) 26 | { 27 | context->handle_free_queue.enqueue(&handle); 28 | } 29 | 30 | // Create sync_archive_condvar 31 | 32 | sl::archive_lock* lock = new sl::archive_lock; 33 | lock->_afterConstruct(); 34 | 35 | context->sync_archive_condvar = sl::handle_create(lock, 4); 36 | 37 | // Set up file access 38 | context->p_file_access = new csl_file_access; 39 | context->p_archive_access = new csl_file_access_archive; 40 | 41 | // Set up file_handle_pool 42 | static constexpr uint32_t NUM_FILE_HANDLES = 250; 43 | context->file_handle_pool.reserve(NUM_FILE_HANDLES); 44 | 45 | // TODO: Validate this 46 | sl::file_handle_internal_t* handles = new sl::file_handle_internal_t[NUM_FILE_HANDLES]{}; 47 | 48 | for (auto& handle : wil::make_range(handles, NUM_FILE_HANDLES)) 49 | { 50 | handle._afterConstruct(); 51 | // TODO: Especially this 52 | const auto handlePtr = &handle; 53 | context->file_handle_pool.push_back(&handlePtr); 54 | } 55 | 56 | // Set up async file requests 57 | static constexpr uint32_t NUM_REQUESTS = NUM_FILE_HANDLES + 64; 58 | 59 | context->p_file_async_request = new csl_file_async_request(&context->p_file_access, NUM_REQUESTS); 60 | context->p_archive_async_request = new csl_file_async_request(reinterpret_cast(&context->p_archive_access), NUM_REQUESTS); 61 | } 62 | 63 | void PatchGs(gs::context_t* context, const RenderWindow& window) 64 | { 65 | // Initialize cgs_device_context 66 | cgs_device_context* device_context = new cgs_device_context{}; 67 | 68 | context->sbgl_device.initialize(window); 69 | 70 | static constexpr size_t NUM_CONTEXTES = 16; // TODO: Uneducated guess, real value comes from the init struct 71 | for (size_t i = 0; i < NUM_CONTEXTES; i++) 72 | { 73 | cgs_cb_pool* cbPool = new cgs_cb_pool; 74 | context->stack_cb_pool.push(cbPool); 75 | 76 | // TODO: Figure out proper sizes ASAP, now hardcoded for both which is terrible and probably wrong 77 | constexpr unsigned int UP_VB_SIZE = 4096, UP_IB_SIZE = 4096; 78 | cgs_up_pool* upPool = new cgs_up_pool; 79 | upPool->initialize(UP_VB_SIZE, UP_IB_SIZE, true); 80 | context->stack_up_pool.push(upPool); 81 | 82 | cgs_shader_uniform* uniform = new cgs_shader_uniform; 83 | uniform->initialize(); 84 | 85 | context->stack_shader_uniform.push(uniform); 86 | } 87 | 88 | device_context->initialize(reinterpret_cast(context->sbgl_device.m_pD3DDeviceContext)); 89 | context->p_device_context = device_context; 90 | 91 | constexpr unsigned int FX_MAX = 256; 92 | constexpr unsigned int VS_MAX = 512; 93 | constexpr unsigned int PS_MAX = 512; 94 | constexpr unsigned int GS_MAX = 256; 95 | constexpr unsigned int DS_MAX = 256; 96 | constexpr unsigned int HS_MAX = 256; 97 | constexpr unsigned int GTS_MAX = 256; 98 | constexpr unsigned int TEX_MAX = 1024; 99 | context->handle_tex.initialize(nullptr, TEX_MAX); 100 | context->handle_vs.initialize(nullptr, VS_MAX); 101 | context->handle_ps.initialize(nullptr, PS_MAX); 102 | context->handle_gs.initialize(nullptr, GS_MAX); 103 | context->handle_ds.initialize(nullptr, DS_MAX); 104 | context->handle_hs.initialize(nullptr, HS_MAX); 105 | context->handle_gts.initialize(nullptr, GTS_MAX); 106 | context->handle_fx.initialize(nullptr, FX_MAX); 107 | 108 | // Fill the export context 109 | auto& export_context = context->export_context; 110 | export_context.size_of_struct = sizeof(export_context); 111 | export_context.sbgl_context.p_value[0] = window.GetD3D11Device(); 112 | export_context.sbgl_context.p_value[1] = static_cast(&context->sbgl_device); 113 | export_context.sbgl_context.p_value[2] = &context->sbgl_device.m_swap_chain; 114 | 115 | gs::primitive_initialize(); 116 | } 117 | 118 | static void prj_trap(const char* format, ...) 119 | { 120 | char buf[512]; 121 | va_list vl; 122 | va_start(vl, format); 123 | vsprintf_s(buf, format, vl); 124 | va_end(vl); 125 | 126 | OutputDebugStringA(buf); 127 | #ifdef _DEBUG 128 | __debugbreak(); 129 | #endif 130 | } 131 | 132 | void ReinstateLogging(void* dll, const Imports& symbols) 133 | { 134 | Trampoline* t = Trampoline::MakeTrampoline(dll); 135 | for (const auto& [key, func] : symbols.GetSymbolRange(ImportSymbol::PRJ_TRAP)) 136 | { 137 | Memory::InjectHook(func, t->Jump(&prj_trap), Memory::HookType::Jump); 138 | } 139 | } 140 | 141 | void InjectTraps(const Imports& symbols) 142 | { 143 | #ifdef _DEBUG 144 | for (const auto& [key, ptr] : symbols.GetSymbolRange(ImportSymbol::TRAP_ALLOC_INSTANCE_TBL)) 145 | { 146 | Memory::Patch(ptr, 0xCC); 147 | } 148 | #endif 149 | } 150 | 151 | void Patch_SysUtil(void* dll, const Imports& symbols) 152 | { 153 | Trampoline* hop = Trampoline::MakeTrampoline(dll); 154 | { 155 | void* sys_util_enable_storage = hop->Jump(&sys_util_check_enable_storage); 156 | for (const auto& [key, addr] : symbols.GetSymbolRange(ImportSymbol::SYS_UTIL_CHECK_ENABLE_STORAGE_PATCH)) 157 | { 158 | Memory::InjectHook(addr, sys_util_enable_storage); 159 | } 160 | } 161 | 162 | { 163 | void* sys_util_load_systemdata_task = hop->Jump(&sys_util_start_load_systemdata_task); 164 | for (const auto& [key, addr] : symbols.GetSymbolRange(ImportSymbol::SYS_UTIL_START_LOAD_SYSTEMDATA_TASK_PATCH)) 165 | { 166 | Memory::InjectHook(addr, sys_util_load_systemdata_task); 167 | } 168 | } 169 | 170 | { 171 | void* sys_util_save_systemdata_task = hop->Jump(&sys_util_start_save_systemdata_task); 172 | for (const auto& [key, addr] : symbols.GetSymbolRange(ImportSymbol::SYS_UTIL_START_SAVE_SYSTEMDATA_TASK_PATCH)) 173 | { 174 | Memory::InjectHook(addr, sys_util_save_systemdata_task); 175 | } 176 | } 177 | 178 | { 179 | void* sys_util_circle_enter = hop->Jump(&sys_util_is_enter_circle); 180 | for (const auto& [key, addr] : symbols.GetSymbolRange(ImportSymbol::SYS_UTIL_IS_ENTER_CIRCLE_PATCH)) 181 | { 182 | Memory::InjectHook(addr, sys_util_circle_enter); 183 | } 184 | } 185 | } 186 | 187 | void Patch_CsGame(void* dll, const Imports& symbols) 188 | { 189 | Trampoline* hop = Trampoline::MakeTrampoline(dll); 190 | { 191 | void* dest_autoload = hop->Jump(&dest_cs_autoload); 192 | for (const auto& [key, addr] : symbols.GetSymbolRange(ImportSymbol::DEST_CS_AUTOLOAD_PATCH)) 193 | { 194 | Memory::Patch(addr, dest_autoload); 195 | } 196 | } 197 | } 198 | 199 | static void assign_helper_enable_shared_from_this(...) 200 | { 201 | } 202 | 203 | static float get_frame_speed_pause_stub() 204 | { 205 | return 1.0f; 206 | } 207 | 208 | static void* (*orgVF5AppCtor)(void* obj, int argc, char** argv); 209 | static void* VF5AppCtor_arguments(void* obj, int /*argc*/, char** /*argv*/) 210 | { 211 | return orgVF5AppCtor(obj, __argc, __argv); 212 | } 213 | 214 | void Patch_Misc(void* dll, const Imports& symbols) 215 | { 216 | Trampoline* hop = Trampoline::MakeTrampoline(dll); 217 | // prj::shared_ptr_internal::assign_helper_enable_shared_from_this folds with prj_trap, causing a false-positive log and eventually crashing 218 | { 219 | void* assign_helper = hop->Jump(&assign_helper_enable_shared_from_this); 220 | for (const auto& [key, addr] : symbols.GetSymbolRange(ImportSymbol::ASSIGN_HELPER_ENABLE_SHARED_FROM_THIS_PATCH)) 221 | { 222 | Memory::InjectHook(addr, assign_helper); 223 | } 224 | } 225 | 226 | // Fix pause countdown not counting down 227 | // In Y6 this code uses frame time, in YLAD it just uses count-- - a possible failed attempt at making the code support high framerates? 228 | { 229 | void* get_frame_speed_stub = hop->Jump(&get_frame_speed_pause_stub); 230 | for (const auto& [key, addr] : symbols.GetSymbolRange(ImportSymbol::TASK_PAUSE_CTRL_COUNTDOWN_PATCH)) 231 | { 232 | Memory::InjectHook(addr, get_frame_speed_stub); 233 | } 234 | } 235 | 236 | // Pass commandline arguments from the launcher 237 | { 238 | for (const auto& [key, addr] : symbols.GetSymbolRange(ImportSymbol::VF5_APP_CTOR_PATCH)) 239 | { 240 | Memory::ReadCall(addr, orgVF5AppCtor); 241 | Memory::InjectHook(addr, hop->Jump(VF5AppCtor_arguments));; 242 | } 243 | } 244 | 245 | // Reinstate "Press START button" 246 | { 247 | for (const auto& [key, addr] : symbols.GetSymbolRange(ImportSymbol::PRESS_START_POS_Y_PATCH)) 248 | { 249 | Memory::Patch(addr, 320.0f); 250 | } 251 | 252 | float& posX = hop->Reference(); 253 | posX = 250.0f; 254 | 255 | for (const auto& [key, addr] : symbols.GetSymbolRange(ImportSymbol::PRESS_START_POS_X_PTR_PATCH)) 256 | { 257 | Memory::WriteOffsetValue(addr, &posX); 258 | } 259 | } 260 | 261 | 262 | // Reinstate button mappings 263 | { 264 | for (const auto& [key, addr] : symbols.GetSymbolRange(ImportSymbol::CS_SWITCH_MAPPING_OVERRIDE_PATCH)) 265 | { 266 | Memory::Nop(addr, 9); 267 | } 268 | } 269 | } -------------------------------------------------------------------------------- /source/Y6/Patch.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "../RenderWindow.h" 4 | #include "sl.h" 5 | #include "gs.h" 6 | 7 | #include "../Imports.h" 8 | 9 | void PatchSl(sl::context_t* context); 10 | void PatchGs(gs::context_t* context, const RenderWindow& window); 11 | void ReinstateLogging(void* dll, const Imports& symbols); 12 | void InjectTraps(const Imports& symbols); 13 | 14 | void Patch_SysUtil(void* dll, const Imports& symbols); 15 | void Patch_CsGame(void* dll, const Imports& symbols); 16 | void Patch_Misc(void* dll, const Imports& symbols); -------------------------------------------------------------------------------- /source/Y6/async_request.cpp: -------------------------------------------------------------------------------- 1 | #include "async_request.h" 2 | 3 | #include 4 | #include 5 | 6 | uint32_t csl_file_async_request::sm_serial = 0; 7 | 8 | csl_file_async_request::csl_file_async_request(isl_file_access** pp_interface, uint32_t max_req_item) 9 | : mpp_file_access(pp_interface) 10 | { 11 | char threadName[128]; 12 | sprintf_s(threadName, "file_async(%d)", sm_serial++); 13 | m_request = sl::semaphore_create(0); 14 | mp_req_item_buf = new req_item_t[max_req_item]; 15 | for (uint32_t i = 0; i < max_req_item; i++) 16 | { 17 | m_free_queue.enqueue(&mp_req_item_buf[max_req_item - i - 1]); 18 | } 19 | 20 | m_h_thread = sl::thread_create(stub_thread_routine, reinterpret_cast(this), threadName); 21 | } 22 | 23 | uint32_t csl_file_async_request::thread_routine() 24 | { 25 | // TODO: The game uses sl::pcounter_count and sl::pcounter_milli_sec for these but for now storing frequency locally is easier 26 | int64_t ticksPerSec; 27 | { 28 | LARGE_INTEGER li; 29 | QueryPerformanceFrequency(&li); 30 | ticksPerSec = li.QuadPart; 31 | } 32 | int64_t lastTick = 0; 33 | 34 | // Unknown meaning 35 | bool skipSemaWait = false; 36 | bool lastSkipSemaWait = false; 37 | 38 | bool loadingWaitStarted = false; 39 | while (true) 40 | { 41 | if (skipSemaWait) 42 | { 43 | lastSkipSemaWait = false; 44 | } 45 | else 46 | { 47 | sl::semaphore_internal_t* sema = sl::semaphore_handle_instance(m_request); 48 | WaitForSingleObject(reinterpret_cast(sema->h_semaphore), INFINITE); 49 | } 50 | 51 | // TODO: This is probably supposed to be scoped 52 | m_mutex_request.lock(); 53 | 54 | // Try to get the request item 55 | req_item_t* item = nullptr; 56 | for (size_t i = 0; i < 6; i++) 57 | { 58 | item = m_busy_queue[i].dequeue(); 59 | if (item != nullptr) break; 60 | 61 | item = m_request_queue[i].dequeue(); 62 | if (item != nullptr) break; 63 | } 64 | 65 | if (m_is_finish_req != 0 && item == nullptr) break; // Terminate thread 66 | 67 | assert(item != nullptr); 68 | 69 | // TODO: Scoped? 70 | sl::spinlock_lock(item->m_locked); 71 | // TODO: Beautify, it's probably a "processed" flag 72 | const unsigned int prevStatus = std::exchange(item->m_status, (item->m_status & 0xFFFF0000) | 0x300); 73 | sl::spinlock_unlock(item->m_locked); 74 | 75 | m_mutex_request.unlock(); 76 | 77 | if ((prevStatus & 0xFF00) == 0x400) // Request done? 78 | { 79 | m_free_queue.enqueue(item); 80 | skipSemaWait = lastSkipSemaWait; 81 | continue; 82 | } 83 | 84 | sl::file_handle_internal_t* file = sl::file_handle_instance(item->m_h_file); 85 | if (item->m_abort) 86 | { 87 | m_h_busy_file = {}; 88 | if (file != nullptr) 89 | { 90 | sl::rwspinlock_wlock(file->m_locked); 91 | file->m_last_async_status = (prevStatus & 0xFF0000) | 0x189; // TODO: Verify correctness 92 | file->end_async_request(); 93 | sl::rwspinlock_wunlock(file->m_locked); 94 | } 95 | m_free_queue.enqueue(item); 96 | skipSemaWait = lastSkipSemaWait; 97 | continue; 98 | } 99 | 100 | // Process the file 101 | m_h_busy_file = item->m_h_file; 102 | if (file != nullptr) 103 | { 104 | isl_file_access* file_access = *mpp_file_access; 105 | file->m_error_code = 0; 106 | 107 | // TODO: Did this code look like ths originally? It probably has plenty of duplications 108 | if (file->m_flags & 0x100) 109 | { 110 | // TODO: Use sl pcounters 111 | LARGE_INTEGER time; 112 | QueryPerformanceCounter(&time); 113 | if (loadingWaitStarted) 114 | { 115 | if ((((time.QuadPart - lastTick) * 1000) / ticksPerSec) < 1000 && !m_is_finish_req) 116 | { 117 | SleepEx(0, TRUE); 118 | m_busy_queue->enqueue(item); 119 | skipSemaWait = lastSkipSemaWait; 120 | 121 | sl::semaphore_internal_t* sema = sl::semaphore_handle_instance(m_request); 122 | ReleaseSemaphore(reinterpret_cast(sema->h_semaphore), 1, nullptr); 123 | continue; 124 | } 125 | } 126 | else 127 | { 128 | loadingWaitStarted = true; 129 | lastTick = time.QuadPart; 130 | sl::semaphore_internal_t* sema = sl::semaphore_handle_instance(m_request); 131 | ReleaseSemaphore(reinterpret_cast(sema->h_semaphore), 1, nullptr); 132 | continue; 133 | } 134 | } 135 | else 136 | { 137 | loadingWaitStarted = false; 138 | } 139 | 140 | const uint32_t asyncMethod = (prevStatus & 0xFF0000) >> 16; 141 | switch (asyncMethod) // TODO: Verify correctness 142 | { 143 | case 1: 144 | { 145 | // TODO: Do 146 | assert(!"Unimplemented"); 147 | break; 148 | } 149 | case 2: 150 | { 151 | // TODO: Do 152 | assert(!"Unimplemented"); 153 | break; 154 | } 155 | case 3: 156 | { 157 | // Command: Close 158 | if ((file->m_flags & 0xC0) != 0) 159 | { 160 | file_access->close(item->m_h_file); 161 | if ((file->m_flags & 1) == 0) 162 | { 163 | // TODO: sl::filecache_close 164 | } 165 | } 166 | // TODO: This will be duplicated, figure out how it may have looked originally 167 | // pls no gotos 168 | m_h_busy_file = {}; 169 | 170 | uint32_t error = 0; 171 | const uint32_t newStatus = error | (asyncMethod << 16) | 0x100; 172 | sl::spinlock_lock(item->m_locked); 173 | if (item->m_abort) 174 | { 175 | sl::rwspinlock_wlock(file->m_locked); 176 | file->mp_callback_func = nullptr; 177 | sl::rwspinlock_wunlock(file->m_locked); 178 | } 179 | item->m_status = newStatus; 180 | sl::spinlock_unlock(item->m_locked); 181 | 182 | file->callback(static_cast(asyncMethod - 1), newStatus); 183 | if (asyncMethod == 3) 184 | { 185 | sl::file_handle_destroy(file); 186 | } 187 | m_free_queue.enqueue(item); 188 | skipSemaWait = lastSkipSemaWait; 189 | continue; 190 | } 191 | case 4: 192 | { 193 | // Command: Read 194 | int64_t bytesRead = 0; 195 | const uint64_t offset = file->m_read_offset; 196 | // TODO: mp_cache 197 | assert(file->mp_cache == nullptr); 198 | if (file->mp_cache != nullptr) 199 | { 200 | 201 | } 202 | else 203 | { 204 | if (file->m_flags & 1) // Loose file? Probably 205 | { 206 | bytesRead = file_access->read_offset(item->m_h_file, file->mp_buffer, file->m_buffer_size, file->m_file_pointer); 207 | if (bytesRead == -2) // Can this even happen? 208 | { 209 | sl::rwspinlock_wlock(file->m_locked); 210 | file->m_last_async_status = (prevStatus & 0xFF0000) | 0x189; // TODO: Verify correctness 211 | file->end_async_request(); 212 | sl::rwspinlock_wunlock(file->m_locked); 213 | 214 | m_free_queue.enqueue(item); 215 | skipSemaWait = lastSkipSemaWait; 216 | continue; 217 | } 218 | } 219 | else 220 | { 221 | // TODO: Tidy up 222 | uint64_t offsetToRead = 0x20000; // WARNING: This is different in Y:LAD! 223 | uint64_t fileSpaceLeft = file->m_real_file_size - offset; 224 | /*if ((file->m_flags >> 9) & 1) // Y:LAD only 225 | offsetToRead = 0xFFFFFFFF;*/ 226 | if (file->m_buffer_size - offset < offsetToRead) 227 | offsetToRead = file->m_buffer_size - offset; 228 | 229 | if (offsetToRead < fileSpaceLeft) 230 | fileSpaceLeft = offsetToRead; 231 | 232 | bytesRead = file_access->read_offset(item->m_h_file, reinterpret_cast(file->mp_buffer) + file->m_read_offset, fileSpaceLeft, file->m_file_pointer); 233 | } 234 | 235 | if (bytesRead == -1) 236 | { 237 | // TODO: This will be duplicated, figure out how it may have looked originally 238 | // pls no gotos 239 | m_h_busy_file = {}; 240 | 241 | uint32_t error = file->m_error_code != 0 ? file->m_error_code : 135; 242 | const uint32_t newStatus = error | (asyncMethod << 16) | 0x100; 243 | sl::spinlock_lock(item->m_locked); 244 | if (item->m_abort) 245 | { 246 | sl::rwspinlock_wlock(file->m_locked); 247 | file->mp_callback_func = nullptr; 248 | sl::rwspinlock_wunlock(file->m_locked); 249 | } 250 | item->m_status = newStatus; 251 | sl::spinlock_unlock(item->m_locked); 252 | 253 | file->callback(static_cast(asyncMethod - 1), newStatus); 254 | if (asyncMethod == 3) 255 | { 256 | sl::file_handle_destroy(file); 257 | } 258 | m_free_queue.enqueue(item); 259 | skipSemaWait = lastSkipSemaWait; 260 | continue; 261 | } 262 | } 263 | file->m_read_offset += bytesRead; 264 | if (file->m_read_offset >= file->m_buffer_size || file->m_file_pointer >= file->m_real_file_size) 265 | { 266 | // TODO: This will be duplicated, figure out how it may have looked originally 267 | // pls no gotos 268 | m_h_busy_file = {}; 269 | 270 | uint32_t error = 0; 271 | const uint32_t newStatus = error | (asyncMethod << 16) | 0x100; 272 | sl::spinlock_lock(item->m_locked); 273 | if (item->m_abort) 274 | { 275 | sl::rwspinlock_wlock(file->m_locked); 276 | file->mp_callback_func = nullptr; 277 | sl::rwspinlock_wunlock(file->m_locked); 278 | } 279 | item->m_status = newStatus; 280 | sl::spinlock_unlock(item->m_locked); 281 | 282 | file->callback(static_cast(asyncMethod - 1), newStatus); 283 | if (asyncMethod == 3) 284 | { 285 | sl::file_handle_destroy(file); 286 | } 287 | m_free_queue.enqueue(item); 288 | skipSemaWait = lastSkipSemaWait; 289 | continue; 290 | } 291 | 292 | sl::spinlock_lock(item->m_locked); 293 | if (!item->m_abort) 294 | { 295 | item->m_status = (item->m_status & 0xFFF0000) | 0x200; 296 | sl::spinlock_unlock(item->m_locked); 297 | m_busy_queue->enqueue(item); 298 | lastSkipSemaWait = true; 299 | } 300 | else 301 | { 302 | sl::spinlock_unlock(item->m_locked); 303 | 304 | sl::rwspinlock_wlock(file->m_locked); 305 | file->m_last_async_status = (prevStatus & 0xFF0000) | 0x189; // TODO: Verify correctness 306 | file->end_async_request(); 307 | sl::rwspinlock_wunlock(file->m_locked); 308 | 309 | m_free_queue.enqueue(item); 310 | } 311 | skipSemaWait = lastSkipSemaWait; 312 | continue; 313 | 314 | break; 315 | } 316 | case 5: 317 | assert(!"Unimplemented"); 318 | break; 319 | case 6: 320 | assert(!"Unimplemented"); 321 | break; 322 | default: 323 | assert(!"Unimplemented"); 324 | break; 325 | } 326 | } 327 | 328 | } 329 | return 0; 330 | } 331 | 332 | uint32_t csl_file_async_request::stub_thread_routine(uint64_t arg) 333 | { 334 | csl_file_async_request* obj = reinterpret_cast(arg); 335 | return obj->thread_routine(); 336 | } 337 | -------------------------------------------------------------------------------- /source/Y6/async_request.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "sl.h" 4 | #include "file_access.h" 5 | 6 | class csl_file_async_request 7 | { 8 | public: 9 | csl_file_async_request(isl_file_access** pp_interface, uint32_t max_req_item); 10 | 11 | public: // TODO: Make private once I figure out how to deal with offsetof below 12 | static constexpr size_t NUM_REQUEST_QUEUES = 6; 13 | 14 | struct req_item_t 15 | { 16 | t_locked_queue_node m_node; 17 | sl::handle_t m_h_file; 18 | spinlock_t m_locked; 19 | volatile unsigned int m_status = 0; 20 | volatile unsigned int m_abort = 0; 21 | }; 22 | 23 | sl::handle_t m_request; 24 | sl::handle_t m_h_thread; 25 | isl_file_access **mpp_file_access = nullptr; 26 | csl_file_async_request::req_item_t *mp_req_item_buf = nullptr; 27 | volatile int m_is_finish_req = 0; 28 | sl::handle_t m_h_busy_file; 29 | sl::mutex_t m_mutex_request; 30 | t_locked_queue m_free_queue; 31 | t_locked_queue m_busy_queue[NUM_REQUEST_QUEUES]; 32 | t_locked_queue m_request_queue[NUM_REQUEST_QUEUES]; 33 | 34 | private: 35 | uint32_t thread_routine(); 36 | 37 | static uint32_t sm_serial; 38 | 39 | static uint32_t stub_thread_routine(uint64_t arg); 40 | }; 41 | static_assert(sizeof(csl_file_async_request) == 496); 42 | static_assert(offsetof(csl_file_async_request, m_free_queue) == 0x50); -------------------------------------------------------------------------------- /source/Y6/cs_game.cpp: -------------------------------------------------------------------------------- 1 | #include "cs_game.h" 2 | 3 | bool dest_cs_autoload() 4 | { 5 | shift_next_mode(4); 6 | shift_next_mode_sub(48); // MODE_SUB_MAX 7 | return true; 8 | } -------------------------------------------------------------------------------- /source/Y6/cs_game.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | inline void (*shift_next_mode)(int mode); 4 | inline void (*shift_next_mode_sub)(int modeSub); 5 | 6 | bool dest_cs_autoload(); -------------------------------------------------------------------------------- /source/Y6/file_access.cpp: -------------------------------------------------------------------------------- 1 | #include "file_access.h" 2 | 3 | #include 4 | #include "../YAMPGeneral.h" 5 | 6 | bool csl_file_access::open(const char* path, sl::handle_t handle) 7 | { 8 | HANDLE file = CreateFileW(UTF8ToWchar(path).c_str(), GENERIC_READ, FILE_SHARE_READ, nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr); 9 | if (file == INVALID_HANDLE_VALUE) 10 | { 11 | return false; 12 | } 13 | 14 | sl::file_handle_internal_t* internalHandle = sl::file_handle_instance(handle); 15 | internalHandle->m_h_native = file; 16 | 17 | return true; 18 | } 19 | 20 | bool csl_file_access::create(const char* path, sl::handle_t handle) 21 | { 22 | HANDLE file = CreateFileW(UTF8ToWchar(path).c_str(), GENERIC_WRITE, FILE_SHARE_READ, nullptr, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, nullptr); 23 | if (file == INVALID_HANDLE_VALUE) 24 | { 25 | return false; 26 | } 27 | 28 | sl::file_handle_internal_t* internalHandle = sl::file_handle_instance(handle); 29 | internalHandle->m_h_native = file; 30 | 31 | return true; 32 | } 33 | 34 | bool csl_file_access::remove(const char*) 35 | { 36 | // Deliberately unimplemented for now 37 | assert(!"csl_file_access::remove unimplemented!"); 38 | return false; 39 | } 40 | 41 | bool csl_file_access::close(sl::handle_t handle) 42 | { 43 | sl::file_handle_internal_t* internalHandle = sl::file_handle_instance(handle); 44 | if (internalHandle != nullptr) 45 | { 46 | HANDLE& nativeHandle = reinterpret_cast(internalHandle->m_h_native); 47 | CloseHandle(nativeHandle); 48 | nativeHandle = INVALID_HANDLE_VALUE; 49 | return true; 50 | } 51 | return false; 52 | } 53 | 54 | bool csl_file_access::is_exist(const char* path) 55 | { 56 | return GetFileAttributesW(UTF8ToWchar(path).c_str()) != INVALID_FILE_ATTRIBUTES; 57 | } 58 | 59 | int64_t csl_file_access::read(sl::handle_t handle, void* buffer, unsigned int size) 60 | { 61 | int64_t result = -1; 62 | sl::file_handle_internal_t* internalHandle = sl::file_handle_instance(handle); 63 | if (internalHandle != nullptr) 64 | { 65 | HANDLE nativeHandle = reinterpret_cast(internalHandle->m_h_native); 66 | if (nativeHandle != INVALID_HANDLE_VALUE) 67 | { 68 | DWORD numBytesRead; 69 | if (ReadFile(nativeHandle, buffer, size, &numBytesRead, nullptr) != FALSE) 70 | { 71 | internalHandle->m_file_pointer += numBytesRead; 72 | result = numBytesRead; 73 | } 74 | } 75 | } 76 | return result; 77 | } 78 | 79 | int64_t csl_file_access::write(sl::handle_t handle, const void* buffer, unsigned int size) 80 | { 81 | int64_t result = -1; 82 | sl::file_handle_internal_t* internalHandle = sl::file_handle_instance(handle); 83 | if (internalHandle != nullptr) 84 | { 85 | HANDLE nativeHandle = reinterpret_cast(internalHandle->m_h_native); 86 | if (nativeHandle != INVALID_HANDLE_VALUE) 87 | { 88 | DWORD numBytesWritten; 89 | if (WriteFile(nativeHandle, buffer, size, &numBytesWritten, nullptr) != FALSE) 90 | { 91 | internalHandle->m_file_pointer += numBytesWritten; 92 | result = numBytesWritten; 93 | } 94 | } 95 | } 96 | return result; 97 | } 98 | 99 | int64_t csl_file_access::get_size(sl::handle_t handle) 100 | { 101 | int64_t size = -1; 102 | const sl::file_handle_internal_t* internalHandle = sl::file_handle_instance(handle); 103 | if (internalHandle != nullptr) 104 | { 105 | HANDLE nativeHandle = reinterpret_cast(internalHandle->m_h_native); 106 | if (nativeHandle != INVALID_HANDLE_VALUE) 107 | { 108 | LARGE_INTEGER s; 109 | if (GetFileSizeEx(nativeHandle, &s) != FALSE) 110 | { 111 | size = s.QuadPart; 112 | } 113 | } 114 | } 115 | return size; 116 | } 117 | 118 | int64_t csl_file_access::seek(sl::handle_t handle, int64_t offset, sl::FILE_SEEK seekMode) 119 | { 120 | int64_t newPointer = -1; 121 | sl::file_handle_internal_t* internalHandle = sl::file_handle_instance(handle); 122 | if (internalHandle != nullptr) 123 | { 124 | HANDLE nativeHandle = reinterpret_cast(internalHandle->m_h_native); 125 | if (nativeHandle != INVALID_HANDLE_VALUE) 126 | { 127 | LARGE_INTEGER newOffset; 128 | LARGE_INTEGER off; 129 | off.QuadPart = offset; 130 | if (SetFilePointerEx(nativeHandle, off, &newOffset, seekMode != sl::FILE_SEEK_SET ? FILE_CURRENT : FILE_BEGIN) != FALSE) 131 | { 132 | newPointer = newOffset.QuadPart; 133 | internalHandle->m_file_pointer = newOffset.QuadPart; 134 | } 135 | } 136 | } 137 | return newPointer; 138 | } 139 | 140 | int64_t csl_file_access::read_offset(sl::handle_t handle, void* buffer, unsigned int size, uint64_t offset) 141 | { 142 | seek(handle, offset, sl::FILE_SEEK_SET); 143 | return read(handle, buffer, size); 144 | } 145 | 146 | uint64_t csl_file_access::burst_read(sl::handle_t, void*, unsigned int, unsigned __int64) 147 | { 148 | return 0; 149 | } 150 | 151 | uint64_t csl_file_access::burst_read_flush(sl::handle_t) 152 | { 153 | return 0; 154 | } 155 | 156 | uint64_t csl_file_access::burst_read_wait(sl::handle_t) 157 | { 158 | return 0; 159 | } 160 | 161 | void csl_file_access::burst_status_write(unsigned int) 162 | { 163 | } 164 | 165 | bool csl_file_access::burst_status_is_complete(unsigned int) 166 | { 167 | return false; 168 | } 169 | 170 | void csl_file_access::burst_status_wait(unsigned int) 171 | { 172 | } 173 | 174 | // =================================================================== 175 | 176 | csl_file_access_archive::csl_file_access_archive() 177 | { 178 | // TODO: Allocator, if needed 179 | void* buf = ::operator new(0x88000); 180 | mp_sector_cache_buffer = buf; 181 | char* decode = static_cast(buf) + 0x8000; 182 | mp_decode_buffer[0] = decode; 183 | mp_decode_buffer[1] = decode + 0x40000; 184 | for (auto& table : m_sector_cache_tbl) 185 | { 186 | m_sector_cache_list.push_back(&table); 187 | } 188 | } 189 | 190 | bool csl_file_access_archive::open(const char* path, sl::handle_t handle) 191 | { 192 | return false; 193 | } 194 | 195 | bool csl_file_access_archive::create(const char* /*path*/, sl::handle_t /*handle*/) 196 | { 197 | // Deliberately unimplemented 198 | return false; 199 | } 200 | 201 | bool csl_file_access_archive::remove(const char*) 202 | { 203 | // Deliberately unimplemented 204 | return false; 205 | } 206 | 207 | bool csl_file_access_archive::close(sl::handle_t /*handle*/) 208 | { 209 | return true; 210 | } 211 | 212 | bool csl_file_access_archive::is_exist(const char* path) 213 | { 214 | return false; 215 | } 216 | 217 | int64_t csl_file_access_archive::read(sl::handle_t handle, void* /*buffer*/, unsigned int size) 218 | { 219 | sl::file_handle_internal_t* internalHandle = sl::file_handle_instance(handle); 220 | if (internalHandle != nullptr) 221 | { 222 | sl::handle_t nativeHandle; 223 | nativeHandle.h.m_handle = reinterpret_cast(internalHandle->m_h_native); 224 | csl_archive* archive = csl_archive::create_instance(nativeHandle); 225 | if (archive != nullptr) 226 | { 227 | const int64_t offset = archive->read_file(handle, size); 228 | archive->release(); 229 | if (offset != -1) 230 | { 231 | internalHandle->m_file_pointer += offset; 232 | } 233 | return offset; 234 | } 235 | } 236 | return -1; 237 | } 238 | 239 | int64_t csl_file_access_archive::write(sl::handle_t, const void*, unsigned int) 240 | { 241 | return -1; 242 | } 243 | 244 | int64_t csl_file_access_archive::get_size(sl::handle_t handle) 245 | { 246 | return int64_t(); 247 | } 248 | 249 | int64_t csl_file_access_archive::seek(sl::handle_t handle, int64_t offset, sl::FILE_SEEK seekMode) 250 | { 251 | sl::file_handle_internal_t* internalHandle = sl::file_handle_instance(handle); 252 | if (internalHandle != nullptr) 253 | { 254 | if ((internalHandle->m_flags & 0x100000) == 0 && (internalHandle->m_flags & 0x10000) != 0) 255 | { 256 | sl::rwspinlock_wlock(internalHandle->m_locked); 257 | 258 | if (seekMode != sl::FILE_SEEK_SET) 259 | { 260 | offset += internalHandle->m_file_pointer; 261 | } 262 | if (offset >= 0) 263 | { 264 | offset = std::min(offset, internalHandle->m_real_file_size); 265 | } 266 | else 267 | { 268 | offset = 0; 269 | } 270 | internalHandle->m_file_pointer = offset; 271 | 272 | sl::rwspinlock_wunlock(internalHandle->m_locked); 273 | } 274 | } 275 | return -1; // Returns -1 even on success - original bug? 276 | } 277 | 278 | int64_t csl_file_access_archive::read_offset(sl::handle_t handle, void* buffer, unsigned int size, uint64_t offset) 279 | { 280 | seek(handle, offset, sl::FILE_SEEK_SET); 281 | return read(handle, buffer, size); 282 | } 283 | 284 | uint64_t csl_file_access_archive::burst_read(sl::handle_t, void*, unsigned int, unsigned __int64) 285 | { 286 | return uint64_t(); 287 | } 288 | 289 | uint64_t csl_file_access_archive::burst_read_flush(sl::handle_t) 290 | { 291 | return uint64_t(); 292 | } 293 | 294 | uint64_t csl_file_access_archive::burst_read_wait(sl::handle_t) 295 | { 296 | return uint64_t(); 297 | } 298 | 299 | void csl_file_access_archive::burst_status_write(unsigned int) 300 | { 301 | } 302 | 303 | bool csl_file_access_archive::burst_status_is_complete(unsigned int) 304 | { 305 | return false; 306 | } 307 | 308 | void csl_file_access_archive::burst_status_wait(unsigned int) 309 | { 310 | } 311 | 312 | // =================================================================== 313 | 314 | csl_archive* csl_archive::create_instance(sl::handle_t handle) 315 | { 316 | sl::archive_lock_wlock(sl::sm_context->sync_archive_condvar); 317 | csl_archive* archive = sl::handle_instance(handle, 6); 318 | if (archive != nullptr) 319 | { 320 | archive->add_ref(); 321 | } 322 | sl::archive_lock_wunlock(sl::sm_context->sync_archive_condvar); 323 | return archive; 324 | } 325 | -------------------------------------------------------------------------------- /source/Y6/file_access.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "sl.h" 4 | 5 | class __declspec(novtable) isl_file_access 6 | { 7 | public: 8 | virtual ~isl_file_access() {}; 9 | virtual bool open(const char* path, sl::handle_t handle) = 0; 10 | virtual bool create(const char* path, sl::handle_t handle) = 0; 11 | virtual bool remove(const char*) = 0; 12 | virtual bool close(sl::handle_t handle) = 0; 13 | virtual bool is_exist(const char *) = 0; 14 | virtual int64_t read(sl::handle_t handle, void* buffer, unsigned int size) = 0; 15 | virtual int64_t write(sl::handle_t handle, const void* buffer, unsigned int size) = 0; 16 | virtual int64_t get_size(sl::handle_t handle) = 0; 17 | virtual int64_t seek(sl::handle_t handle, int64_t offset, sl::FILE_SEEK seekMode) = 0; 18 | virtual int64_t read_offset(sl::handle_t handle, void* buffer, unsigned int size, uint64_t offset) = 0; 19 | 20 | // These don't seem to exist in Yakuza 6, but keep them for now 21 | virtual uint64_t burst_read(sl::handle_t, void *, unsigned int, unsigned __int64) = 0; 22 | virtual uint64_t burst_read_flush(sl::handle_t) = 0; 23 | virtual uint64_t burst_read_wait(sl::handle_t) = 0; 24 | virtual void burst_status_write(unsigned int) = 0; 25 | virtual bool burst_status_is_complete(unsigned int) = 0; 26 | virtual void burst_status_wait(unsigned int) = 0; 27 | }; 28 | 29 | class csl_file_access final : public isl_file_access 30 | { 31 | public: 32 | virtual bool open(const char* path, sl::handle_t handle) override; 33 | virtual bool create(const char* path, sl::handle_t handle) override; 34 | virtual bool remove(const char*) override; 35 | virtual bool close(sl::handle_t handle) override; 36 | virtual bool is_exist(const char* path) override; 37 | virtual int64_t read(sl::handle_t handle, void* buffer, unsigned int size) override; 38 | virtual int64_t write(sl::handle_t handle, const void* buffer, unsigned int size) override; 39 | virtual int64_t get_size(sl::handle_t handle) override; 40 | virtual int64_t seek(sl::handle_t handle, int64_t offset, sl::FILE_SEEK seekMode) override; 41 | virtual int64_t read_offset(sl::handle_t handle, void* buffer, unsigned int size, uint64_t offset) override; 42 | virtual uint64_t burst_read(sl::handle_t, void*, unsigned int, unsigned __int64) override; 43 | virtual uint64_t burst_read_flush(sl::handle_t) override; 44 | virtual uint64_t burst_read_wait(sl::handle_t) override; 45 | virtual void burst_status_write(unsigned int) override; 46 | virtual bool burst_status_is_complete(unsigned int) override; 47 | virtual void burst_status_wait(unsigned int) override; 48 | }; 49 | 50 | // TODO: Alignment seems wrong 51 | class alignas(32) csl_file_access_archive final : public isl_file_access 52 | { 53 | public: 54 | csl_file_access_archive(); 55 | 56 | virtual bool open(const char* path, sl::handle_t handle) override; 57 | virtual bool create(const char* path, sl::handle_t handle) override; 58 | virtual bool remove(const char*) override; 59 | virtual bool close(sl::handle_t handle) override; 60 | virtual bool is_exist(const char* path) override; 61 | virtual int64_t read(sl::handle_t handle, void* buffer, unsigned int size) override; 62 | virtual int64_t write(sl::handle_t, const void*, unsigned int) override; 63 | virtual int64_t get_size(sl::handle_t handle) override; 64 | virtual int64_t seek(sl::handle_t handle, int64_t offset, sl::FILE_SEEK seekMode) override; 65 | virtual int64_t read_offset(sl::handle_t handle, void* buffer, unsigned int size, uint64_t offset) override; 66 | virtual uint64_t burst_read(sl::handle_t, void*, unsigned int, unsigned __int64) override; 67 | virtual uint64_t burst_read_flush(sl::handle_t) override; 68 | virtual uint64_t burst_read_wait(sl::handle_t) override; 69 | virtual void burst_status_write(unsigned int) override; 70 | virtual bool burst_status_is_complete(unsigned int) override; 71 | virtual void burst_status_wait(unsigned int) override; 72 | 73 | struct sector_cache_t 74 | { 75 | short m_prev = 0; 76 | short m_next = 0; 77 | sl::handle_t m_h_file; 78 | uint64_t m_offset = 0; 79 | }; 80 | 81 | private: 82 | void* mp_sector_cache_buffer = nullptr; 83 | sl::mutex_t m_mutex_sector_cache; 84 | sl::mutex_t m_mutex_decode_buffer; 85 | sector_cache_t m_sector_cache_tbl[16]; 86 | t_pointer_list m_sector_cache_list; 87 | void *mp_decode_buffer[2] = {}; 88 | }; 89 | static_assert(sizeof(csl_file_access_archive) == 416); 90 | 91 | // This class is implemented in the DLL, we only need the interface (for now) 92 | class __declspec(novtable) isl_archive 93 | { 94 | public: 95 | virtual ~isl_archive() {}; 96 | virtual unsigned int release() = 0; 97 | virtual unsigned int get_subdirectory_num(unsigned int parent_directory) = 0; 98 | virtual unsigned int get_directory_id(unsigned int parent_directory, unsigned int entry_no) = 0; 99 | virtual unsigned int get_directory_name(unsigned int parent_directory, unsigned int entry_no, char* p_name, unsigned int max_name_length) = 0; 100 | virtual unsigned int get_file_num(unsigned int parent_directory) = 0; 101 | virtual unsigned int get_file_id(unsigned int parent_directory, unsigned int entry_no) = 0; 102 | virtual unsigned int get_file_name(unsigned int parent_directory, unsigned int entry_no, char* p_name, unsigned int max_name_length) = 0; 103 | virtual int find_file(const char* psz_file_name, unsigned int* p_file_index) = 0; 104 | virtual unsigned int extract_file(unsigned int file_id, void* p_buffer, unsigned int buffer_size) = 0; 105 | virtual unsigned int get_file_size(unsigned int file_index) = 0; 106 | }; 107 | 108 | // We leave it as abstract as there's no need to instantiate this class (so far) 109 | class csl_archive : public isl_archive 110 | { 111 | public: 112 | enum ASYNC_LOAD_STEP 113 | { 114 | ASYNC_LOAD_STEP_READY = 0x0, 115 | ASYNC_LOAD_STEP_OPEN = 0x1, 116 | ASYNC_LOAD_STEP_READ_HEADER = 0x2, 117 | ASYNC_LOAD_STEP_READ = 0x3, 118 | }; 119 | 120 | virtual int initialize(void* p_image, sl::handle_t h_file) = 0; 121 | virtual uint64_t read_file(sl::handle_t h_file, unsigned int read_bytes) = 0; 122 | virtual void async_load_process_func(ASYNC_LOAD_STEP load_step) = 0; 123 | 124 | static csl_archive* create_instance(sl::handle_t handle); 125 | 126 | uint32_t add_ref() { return m_ref_count++; } 127 | 128 | public: 129 | std::byte gap[16]; 130 | uint32_t m_ref_count; 131 | }; 132 | static_assert(offsetof(csl_archive, m_ref_count) == 24); -------------------------------------------------------------------------------- /source/Y6/gs.cpp: -------------------------------------------------------------------------------- 1 | #include "gs.h" 2 | 3 | #include "../RenderWindow.h" 4 | 5 | namespace gs { 6 | 7 | cgs_vb* (*vb_create)(uint64_t fvf, unsigned int vertices, vb_usage_t usage, unsigned int flags, const void* p_initial_data, const char* sz_name); 8 | cgs_ib* (*ib_create)(unsigned int indices, ib_usage_t usage, unsigned int flags, const void* p_initial_data, const char* sz_name); 9 | context_t* sm_context; 10 | 11 | void primitive_initialize() 12 | { 13 | context_t* context = sm_context; 14 | 15 | for (size_t i = 0; i < 3; i++) 16 | { 17 | context->p_vb_sphere[i] = nullptr; 18 | context->p_vb_capsule[i] = nullptr; 19 | } 20 | 21 | { 22 | constexpr size_t NUM_PRIMITIVES = 192; 23 | uint16_t verts[NUM_PRIMITIVES][6]; 24 | uint16_t primNum = 0; 25 | for (auto& vert : verts) 26 | { 27 | const uint16_t startVertNum = 4 * primNum++; 28 | size_t idx = 0; 29 | vert[idx++] = startVertNum; 30 | vert[idx++] = startVertNum + 1; 31 | vert[idx++] = startVertNum + 2; 32 | vert[idx++] = startVertNum; 33 | vert[idx++] = startVertNum + 2; 34 | vert[idx++] = startVertNum + 3; 35 | } 36 | context->p_ib_quad = ib_create(NUM_PRIMITIVES * 6, IB_USAGE_IMMUTABLE, 0, verts, nullptr); 37 | } 38 | { 39 | constexpr size_t NUM_PRIMITIVES = 256; 40 | uint16_t verts[NUM_PRIMITIVES][3]; 41 | uint16_t primNum = 0; 42 | for (auto& vert : verts) 43 | { 44 | const uint16_t startVertexNum = primNum++; 45 | size_t idx = 0; 46 | vert[idx++] = startVertexNum + 2; 47 | vert[idx++] = 0; 48 | vert[idx++] = startVertexNum + 1; 49 | } 50 | // BUG: Original code creates a IB 2x bigger, but this seems like a bug 51 | context->p_ib_fan = ib_create(NUM_PRIMITIVES * 3, IB_USAGE_IMMUTABLE, 0, verts, nullptr); 52 | } 53 | } 54 | 55 | } 56 | 57 | void cgs_device_context::initialize(sbgl::ccontext* p_context) 58 | { 59 | if (p_context != nullptr) 60 | { 61 | mp_sbgl_context = p_context; 62 | 63 | mp_cb_pool = gs::sm_context->stack_cb_pool.pop(); 64 | mp_up_pool = gs::sm_context->stack_up_pool.pop(); 65 | mp_shader_uniform = gs::sm_context->stack_shader_uniform.pop(); 66 | reset_state_all(); 67 | 68 | // TODO: Yakuza 6 returns an error code here?? 69 | } 70 | } 71 | 72 | namespace sbgl { 73 | 74 | void cdevice::initialize(const RenderWindow& renderWindow) 75 | { 76 | m_swap_chain.initialize(renderWindow); 77 | m_pD3DDeviceContext = renderWindow.GetD3D11DeviceContext(); 78 | 79 | std::fill(std::begin(m_p_border_color), std::end(m_p_border_color), _mm_set1_ps(std::numeric_limits::quiet_NaN())); 80 | m_p_border_color[0] = _mm_setzero_ps(); 81 | m_p_border_color[1] = _mm_set_ps(0.0f, 0.0f, 0.0f, 1.0f); 82 | m_p_border_color[2] = _mm_set1_ps(1.0f); 83 | 84 | // TODO: YLAD presents an empty frame here... Yakuza 6 doesn't seem to 85 | // The game sets private data, then renders a frame, then resets private data 86 | // For now, just do it directly 87 | m_context_desc.reset(nullptr, 0.0f, 0, 0); 88 | void* dataPtr = &m_context_desc; 89 | m_pD3DDeviceContext->SetPrivateData(ccontext_native::GUID_ContextPrivateData, sizeof(dataPtr), &dataPtr); 90 | } 91 | 92 | HRESULT cswap_chain_common::initialize(const RenderWindow& window) 93 | { 94 | // TODO: Validate 95 | constexpr sbgl_format_t depth_format = SBGL_FORMAT_D32_F_S8X24_U; 96 | 97 | ID3D11Device* device = window.GetD3D11Device(); 98 | 99 | m_pDXGISwapChain = window.GetSwapChain(); 100 | 101 | wil::com_ptr swapChainBuffer; 102 | HRESULT hr = m_pDXGISwapChain->GetBuffer(0, IID_PPV_ARGS(swapChainBuffer.addressof())); 103 | if (SUCCEEDED(hr)) 104 | { 105 | hr = device->CreateRenderTargetView(swapChainBuffer.get(), nullptr, &m_pD3DRenderTargetView); 106 | if (SUCCEEDED(hr)) 107 | { 108 | D3D11_TEXTURE2D_DESC dsDesc; 109 | dsDesc.Width = window.GetWidth(); 110 | dsDesc.Height = window.GetHeight(); 111 | dsDesc.MipLevels = 1; 112 | dsDesc.ArraySize = 1; 113 | dsDesc.Format = static_cast((depth_format >> 7) & 0x7F); 114 | dsDesc.SampleDesc.Count = 1; 115 | dsDesc.SampleDesc.Quality = 0; 116 | dsDesc.Usage = D3D11_USAGE_DEFAULT; 117 | dsDesc.BindFlags = D3D11_BIND_DEPTH_STENCIL; 118 | dsDesc.CPUAccessFlags = 0; 119 | dsDesc.MiscFlags = 0; 120 | hr = device->CreateTexture2D(&dsDesc, nullptr, &m_pD3DDepthStencilBuffer); 121 | if (SUCCEEDED(hr)) 122 | { 123 | D3D11_DEPTH_STENCIL_VIEW_DESC dsvDesc; 124 | dsvDesc.Format = static_cast(depth_format & 0x7F); 125 | dsvDesc.ViewDimension = D3D11_DSV_DIMENSION_TEXTURE2D; 126 | dsvDesc.Flags = 0; 127 | dsvDesc.Texture2D.MipSlice = 0; 128 | hr = device->CreateDepthStencilView(m_pD3DDepthStencilBuffer, &dsvDesc, &m_pD3DDepthStencilView); 129 | } 130 | } 131 | } 132 | return hr; 133 | } 134 | 135 | void ccontext_native::desc_st::reset(ID3D11RenderTargetView* pDepthStencilView, float depth_clear_value, unsigned int width, unsigned int height) 136 | { 137 | for (size_t i = 0; i < 8; i++) 138 | { 139 | m_ppRenderTargetView[i] = nullptr; 140 | //m_p_fast_clear_color[i] = {}; // YLAD only 141 | } 142 | m_num_slots = 0; 143 | m_width = width; 144 | m_height = 0; 145 | m_depth_clear_value = depth_clear_value; 146 | } 147 | 148 | } 149 | 150 | void cgs_up_pool::initialize(unsigned int vb_size, unsigned int ib_size, bool is_push_support) 151 | { 152 | m_vb_size = (vb_size + 63) & ~63; 153 | m_ib_size = (ib_size + 63) & ~63; 154 | 155 | if (m_vb_size != 0) 156 | { 157 | mp_vb = gs::vb_create(0xE00003u, m_vb_size / 16, gs::VB_USAGE_DYNAMIC, 0, nullptr, nullptr); 158 | } 159 | if (m_ib_size != 0) 160 | { 161 | mp_ib = gs::ib_create(m_ib_size / 2, gs::IB_USAGE_DYNAMIC, 0, nullptr, nullptr); 162 | } 163 | if (is_push_support) 164 | { 165 | char* buf = static_cast(::operator new(2 * m_vb_size, std::align_val_t(16))); 166 | mp_push_polygon = buf; 167 | mp_push_line = buf+m_vb_size; 168 | } 169 | } 170 | 171 | void cgs_shader_uniform::initialize() 172 | { 173 | m_clip_far = 0.0f; 174 | m_clip_far = 1.0f; 175 | m_data.m_data_material_modify.saturation = 1.0f; 176 | m_data.m_data_material_modify.palette0 = -1; 177 | m_data.m_data_material_modify.palette1 = -1; 178 | m_data.m_data_material_modify._reserve0 = 1.0f; 179 | } 180 | -------------------------------------------------------------------------------- /source/Y6/sl.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | #define WIN32_LEAN_AND_MEAN 7 | #define NOMINMAX 8 | #include 9 | 10 | #include "../pxd_types.h" 11 | #include "../sl_internal.h" 12 | 13 | class isl_file_access; 14 | class csl_file_access_archive; 15 | 16 | // sl definitions for Yakuza 6 17 | class csl_file_async_request; 18 | 19 | struct csl_pad 20 | { 21 | public: 22 | void set_state(unsigned int index); 23 | 24 | unsigned int m_now; 25 | unsigned int m_push; 26 | unsigned int m_pull; 27 | unsigned int m_prev; 28 | float m_x1; 29 | float m_y1; 30 | float m_x2; 31 | float m_y2; 32 | int m_button_frame[32]; 33 | uint8_t m_buttons[32]; 34 | uint8_t m_prev_buttons[32]; 35 | unsigned int m_port; 36 | int m_user_id; 37 | bool m_is_connected; 38 | bool m_is_remote; 39 | std::byte gap[132]; 40 | }; 41 | static_assert(sizeof(csl_pad) == 0x170); 42 | 43 | namespace sl { 44 | 45 | enum BUTTON 46 | { 47 | BUTTON_A = 0x0, 48 | BUTTON_B = 0x1, 49 | BUTTON_X = 0x2, 50 | BUTTON_Y = 0x3, 51 | BUTTON_LB = 0x4, 52 | BUTTON_RB = 0x5, 53 | BUTTON_LT = 0x6, 54 | BUTTON_RT = 0x7, 55 | BUTTON_START = 0x8, 56 | BUTTON_BACK = 0x9, 57 | BUTTON_L_THUMB = 0xA, 58 | BUTTON_R_THUMB = 0xB, 59 | BUTTON_UP = 0xC, 60 | BUTTON_DOWN = 0xD, 61 | BUTTON_LEFT = 0xE, 62 | BUTTON_RIGHT = 0xF, 63 | BUTTON_L_UP = 0x10, 64 | BUTTON_L_DOWN = 0x11, 65 | BUTTON_L_LEFT = 0x12, 66 | BUTTON_L_RIGHT = 0x13, 67 | BUTTON_R_UP = 0x14, 68 | BUTTON_R_DOWN = 0x15, 69 | BUTTON_R_LEFT = 0x16, 70 | BUTTON_R_RIGHT = 0x17, 71 | BUTTON_ALL_UP = 0x18, 72 | BUTTON_ALL_DOWN = 0x19, 73 | BUTTON_ALL_LEFT = 0x1A, 74 | BUTTON_ALL_RIGHT = 0x1B, 75 | BUTTON_DA_UP = 0x1C, 76 | BUTTON_DA_DOWN = 0x1D, 77 | BUTTON_DA_LEFT = 0x1E, 78 | BUTTON_DA_RIGHT = 0x1F, 79 | BUTTON_MAX = 0x20, 80 | BUTTON_UNKNOWN = 0xFF, 81 | BUTTON_FORCE_32BIT = 0x7FFFFFFF, 82 | }; 83 | 84 | // Imported function 85 | extern handle_t* (*handle_create_internal)(handle_t* obj, void* ptr, uint32_t type); 86 | handle_t handle_create(void* ptr, uint32_t type); 87 | 88 | handle_t semaphore_create(uint32_t initialCount); 89 | handle_t thread_create(uint32_t (*p_routine)(uint64_t), uint64_t arg, const char* name); 90 | 91 | inline handle_t* (*file_open_internal)(handle_t* obj, const char* in_sz_file_path); 92 | inline handle_t* (*file_create_internal)(handle_t* obj, const char* in_sz_file_path); 93 | handle_t file_open(const char* in_sz_file_path); 94 | handle_t file_create(const char* in_sz_file_path); 95 | 96 | inline int64_t (*file_read)(handle_t h_file, void* p_buffer, unsigned int read_size); 97 | inline int (*file_close)(handle_t h_file); 98 | int64_t file_write(handle_t h_file, const void* p_buffer, unsigned int write_size); 99 | 100 | struct alignas(16) file_handle_internal_t : public file_handle_t 101 | { 102 | volatile unsigned int m_flags; 103 | unsigned int m_buffer_size; 104 | unsigned int m_archive_file_no; 105 | unsigned int m_error_code; 106 | handle_t m_async_event; 107 | handle_t m_h_basefile; 108 | unsigned __int64 m_basefile_offset; 109 | void *mp_buffer; 110 | void *mp_cache; // TODO: csl_filecache_name 111 | uint64_t m_read_offset; 112 | uint64_t m_real_file_size; 113 | void (*mp_callback_func)(handle_t, unsigned int, void *); 114 | void *mp_callback_param; 115 | volatile uint64_t m_callback_execute_thread; 116 | FILE_ASYNC_METHOD m_callback_method; 117 | unsigned int m_req_item_index; 118 | unsigned int m_last_async_status; 119 | rwspinlock_t m_locked; 120 | file_handle_internal_t *mp_link; 121 | 122 | void begin_async_request(); 123 | void end_async_request(); 124 | void callback(FILE_ASYNC_METHOD type, uint32_t status); 125 | 126 | public: 127 | void _afterConstruct(); 128 | }; 129 | static_assert(sizeof(file_handle_internal_t) == 0x4D0); 130 | static_assert(offsetof(file_handle_internal_t, m_buffer_size) == 1116); 131 | 132 | inline void (*file_handle_destroy)(sl::file_handle_internal_t* p_handle); 133 | 134 | struct export_context_t 135 | { 136 | size_t size_of_struct; 137 | void *p_context; 138 | }; 139 | 140 | struct alignas(16) semaphore_internal_t 141 | { 142 | uint32_t tag_id = 0x4D455348; 143 | void* h_semaphore; 144 | }; 145 | 146 | struct alignas(16) thread_internal_t 147 | { 148 | uint32_t tag_id = 0x44525448; 149 | char sz_name[28]; 150 | void *h_thread; 151 | uint64_t thread_id; 152 | uint64_t arg; 153 | uint32_t (*p_routine)(uint64_t arg); 154 | }; 155 | static_assert(sizeof(thread_internal_t) == 0x40); 156 | 157 | struct context_t 158 | { 159 | uint32_t tag_id; 160 | uint32_t version; 161 | uint32_t size_of_struct; 162 | export_context_t export_context; 163 | uint32_t processor_num; 164 | uint64_t main_thread_id; 165 | uint64_t processor_affinity_mask; 166 | void *p_temp_work; 167 | size_t temp_work_size; 168 | uint64_t count_frequency; 169 | double count_per_milli_second; 170 | double count_per_micro_second; 171 | double count_per_nano_second; 172 | double count_per_tick; 173 | union 174 | { 175 | handle_internal_buffer_t *p_handle_buffer; 176 | handle_internal_t *p_handle_tbl; 177 | } handles; 178 | uint32_t handle_max; 179 | uint32_t file_handle_max; 180 | uint32_t file_callback_thread_stack_size; 181 | std::byte gap3[12]; 182 | isl_file_access* p_file_access; 183 | csl_file_async_request* p_file_async_request; 184 | std::byte gap6[120]; 185 | csl_file_access_archive* p_archive_access; 186 | csl_file_async_request* p_archive_async_request; 187 | std::byte gap[1240]; 188 | t_locked_queue handle_free_queue; 189 | std::byte gap2[5408]; 190 | spinlock_t sync_file_handle_pool; 191 | std::byte gap5[4]; 192 | t_fixed_deque file_handle_pool; 193 | std::byte gap4[32]; 194 | handle_t sync_archive_condvar; 195 | }; 196 | // Validate important offsets 197 | static_assert(offsetof(context_t, handles) == 0x70); 198 | static_assert(offsetof(context_t, p_file_access) == 0x90); 199 | static_assert(offsetof(context_t, p_file_async_request) == 0x98); 200 | static_assert(offsetof(context_t, p_archive_access) == 0x118); 201 | static_assert(offsetof(context_t, p_archive_async_request) == 0x120); 202 | static_assert(offsetof(context_t, handle_free_queue) == 0x600); 203 | static_assert(offsetof(context_t, sync_file_handle_pool) == 0x1B40); 204 | static_assert(offsetof(context_t, file_handle_pool) == 0x1B48); 205 | static_assert(offsetof(context_t, sync_archive_condvar) == 0x1B80); 206 | 207 | // TODO: Consider changing this to a pointer to real sm_context 208 | extern context_t* sm_context; 209 | 210 | // Custom types 211 | struct archive_lock 212 | { 213 | uint32_t tag_id = 0x4C575248; 214 | handle_t eventHandle1; 215 | handle_t eventHandle2; 216 | uint32_t unk1 = 0; 217 | uint32_t unk2 = 0; 218 | uint32_t unk3 = 0; 219 | uint32_t unk4 = 0; 220 | sl::mutex_t critSec1; 221 | sl::mutex_t critSec2; 222 | 223 | public: 224 | void _afterConstruct(); 225 | }; 226 | static_assert(sizeof(archive_lock) == 0x80); 227 | static_assert(offsetof(archive_lock, critSec1) == 32); 228 | static_assert(offsetof(archive_lock, critSec2) == 80); 229 | 230 | struct file_handle_event 231 | { 232 | void* gap; 233 | HANDLE eventHandle; 234 | 235 | public: 236 | void _afterConstruct(); 237 | }; 238 | static_assert(sizeof(file_handle_event) == 16); 239 | 240 | // Y:LAD changed this to a recursive_rwspinlock 241 | extern void (*archive_lock_wlock)(handle_t handle); 242 | extern void (*archive_lock_wunlock)(handle_t handle); 243 | 244 | 245 | template 246 | inline T* handle_instance(handle_t handle, uint32_t type) 247 | { 248 | T* result = nullptr; 249 | if (handle.h.data.m_bank < sm_context->handle_max) 250 | { 251 | const handle_internal_t& internalHandle = sm_context->handles.p_handle_tbl[handle.h.data.m_bank]; 252 | if (internalHandle.intern.data.m_serial == handle.h.data.m_serial && internalHandle.intern.data.m_type == type) 253 | { 254 | result = reinterpret_cast(internalHandle.intern.data.m_ptr << 4); 255 | } 256 | } 257 | 258 | return result; 259 | } 260 | 261 | inline file_handle_internal_t* file_handle_instance(handle_t handle) 262 | { 263 | return handle_instance(handle, 5); 264 | } 265 | 266 | inline semaphore_internal_t* semaphore_handle_instance(handle_t handle) 267 | { 268 | return handle_instance(handle, 2); 269 | } 270 | 271 | }; -------------------------------------------------------------------------------- /source/Y6/sys_util.cpp: -------------------------------------------------------------------------------- 1 | #include "sys_util.h" 2 | 3 | #include "sl.h" 4 | #include "../YAMPGeneral.h" 5 | 6 | #include "../wil/resource.h" 7 | 8 | namespace fs = std::filesystem; 9 | 10 | static const char* SAVE_FILE_NAME = u8"system.dat"; 11 | 12 | struct sys_util_save_header 13 | { 14 | uint32_t version; // 100 15 | std::byte unk[8]; 16 | uint32_t size; 17 | }; 18 | static_assert(sizeof(sys_util_save_header) == 16); 19 | 20 | bool sys_util_check_enable_storage(int port) 21 | { 22 | // TODO: Implement properly 23 | return true; 24 | } 25 | 26 | void sys_util_start_load_systemdata_task(int port, void* buf, unsigned int buf_size, bool autoload, bool create, void(*cbfunc)(bool success, bool creator)) 27 | { 28 | bool cbSuccess = false; 29 | auto cbOnExit = wil::scope_exit([&] { 30 | if (cbfunc != nullptr) 31 | cbfunc(cbSuccess, create); 32 | }); 33 | 34 | // TODO: Do we need to use the port parameter? 35 | // TODO: Should we call the callback if 'create' is set to off and the file doesn't exist? 36 | const auto utf8PathToFile = (gGeneral.GetDataPath() / SAVE_FILE_NAME).u8string(); 37 | sl::handle_t file = create ? sl::file_create(utf8PathToFile.c_str()) : sl::file_open(utf8PathToFile.c_str()); 38 | if (file == sl::INVALID_HANDLE) return; 39 | 40 | sys_util_save_header header; 41 | if (sl::file_read(file, &header, sizeof(header)) == sizeof(header)) 42 | { 43 | // TODO: What about the unknown fields? Should the size check be == and not <=? 44 | if (header.version == 100 && header.size <= buf_size) 45 | { 46 | cbSuccess = sl::file_read(file, buf, header.size) == header.size; 47 | } 48 | } 49 | sl::file_close(file); 50 | } 51 | 52 | void sys_util_start_save_systemdata_task(int port, const void* buf, unsigned int buf_size, bool autosave, void(*cbfunc)(bool success, bool unknown)) 53 | { 54 | bool cbSuccess = false; 55 | auto cbOnExit = wil::scope_exit([&] { 56 | if (cbfunc != nullptr) 57 | cbfunc(cbSuccess, false); 58 | }); 59 | 60 | // Create the entire directory structure if needed 61 | std::error_code ec; 62 | if (!fs::create_directories(gGeneral.GetDataPath(), ec)) return; 63 | 64 | const auto utf8PathToFile = (gGeneral.GetDataPath() / SAVE_FILE_NAME).u8string(); 65 | sl::handle_t file = sl::file_create(utf8PathToFile.c_str()); 66 | if (file == sl::INVALID_HANDLE) return; 67 | 68 | sys_util_save_header header {}; 69 | header.version = 100; 70 | header.size = buf_size; 71 | if (sl::file_write(file, &header, sizeof(header)) == sizeof(header)) 72 | { 73 | cbSuccess = sl::file_write(file, buf, buf_size) == buf_size; 74 | } 75 | sl::file_close(file); 76 | } 77 | 78 | bool sys_util_is_enter_circle() 79 | { 80 | return gGeneral.GetSettings()->m_circleConfirm; 81 | } 82 | -------------------------------------------------------------------------------- /source/Y6/sys_util.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | bool sys_util_check_enable_storage(int port); 4 | void sys_util_start_load_systemdata_task(int port, void* buf, unsigned int buf_size, bool autoload, bool create, void (*cbfunc)(bool success, bool creator)); 5 | void sys_util_start_save_systemdata_task(int port, const void* buf, unsigned int buf_size, bool autosave, void (*cbfunc)(bool success, bool unknown)); 6 | bool sys_util_is_enter_circle(); 7 | -------------------------------------------------------------------------------- /source/YAMPGeneral.cpp: -------------------------------------------------------------------------------- 1 | #include "YAMPGeneral.h" 2 | 3 | #include 4 | #include 5 | #include "wil/resource.h" 6 | 7 | YAMPGeneral gGeneral; 8 | 9 | void YAMPGeneral::LoadSettings() 10 | { 11 | // TODO: Make per-game settings somehow 12 | m_settings = std::make_unique(); 13 | m_settings->LoadSettings(GetDataPath()); 14 | } 15 | 16 | std::filesystem::path YAMPGeneral::GetLocalAppDataPath() const 17 | { 18 | std::filesystem::path result; 19 | 20 | wil::unique_cotaskmem_string str; 21 | if (SUCCEEDED(SHGetKnownFolderPath(FOLDERID_RoamingAppData, KF_FLAG_DEFAULT, nullptr, str.addressof()))) 22 | { 23 | result.assign(str.get()); 24 | } 25 | return result; 26 | } 27 | -------------------------------------------------------------------------------- /source/YAMPGeneral.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | #include 8 | 9 | #include "wil/resource.h" 10 | 11 | #include "YAMPSettings.h" 12 | 13 | // TODO: Move 14 | static std::wstring UTF8ToWchar(std::string_view text) 15 | { 16 | std::wstring result; 17 | 18 | const int count = MultiByteToWideChar(CP_UTF8, 0, text.data(), static_cast(text.size()), nullptr, 0); 19 | if ( count != 0 ) 20 | { 21 | result.resize(count); 22 | MultiByteToWideChar(CP_UTF8, 0, text.data(), static_cast(text.size()), result.data(), count); 23 | } 24 | 25 | return result; 26 | } 27 | 28 | static std::string WcharToUTF8(std::wstring_view text) 29 | { 30 | std::string result; 31 | 32 | const int count = WideCharToMultiByte(CP_UTF8, 0, text.data(), static_cast(text.size()), nullptr, 0, nullptr, nullptr); 33 | if ( count != 0 ) 34 | { 35 | result.resize(count); 36 | WideCharToMultiByte(CP_UTF8, 0, text.data(), static_cast(text.size()), result.data(), count, nullptr, nullptr); 37 | } 38 | 39 | return result; 40 | } 41 | 42 | // TODO: Move to YAMP namespace maybe? 43 | class YAMPGeneral 44 | { 45 | public: 46 | const auto& GetDataPath() const { return m_userDataPath; } 47 | const auto* GetSettings() const { return m_settings.get(); } 48 | const auto& GetPressedKeys() const { return m_pressedKeyboardKeys; } 49 | const auto& GetDLLName() const { return m_dllName; } 50 | const auto GetDLLTimestamp() const { return m_dllTimestamp; } 51 | 52 | template 53 | void SetDataPath(Args&&... args) 54 | { 55 | // TODO: Allow for portable mode 56 | m_userDataPath = GetLocalAppDataPath(); 57 | (m_userDataPath.append(args), ...); 58 | } 59 | 60 | void SetDLLName(std::string name) { m_dllName = std::move(name); } 61 | void SetDLLTimestamp(uint32_t timestamp) { m_dllTimestamp = timestamp; } 62 | 63 | void SetKeyPressed(uint32_t key, bool pressed) 64 | { 65 | assert(key < m_pressedKeyboardKeys.size()); 66 | m_pressedKeyboardKeys[key] = pressed; 67 | } 68 | 69 | void LoadSettings(); 70 | 71 | // For use in the UI 72 | auto GetSettingsUpdateToken() 73 | { 74 | // (settings_ptr, apply_token) pair 75 | // Calls SaveSettings when it goes out of scope 76 | return std::make_pair(m_settings.get(), wil::scope_exit([this] { 77 | m_settings->SaveSettings(GetDataPath()); 78 | })); 79 | } 80 | 81 | private: 82 | std::filesystem::path GetLocalAppDataPath() const; 83 | 84 | std::string m_dllName; 85 | uint32_t m_dllTimestamp = 0; 86 | std::filesystem::path m_userDataPath; 87 | std::unique_ptr m_settings; 88 | 89 | std::array m_pressedKeyboardKeys; 90 | }; 91 | 92 | extern YAMPGeneral gGeneral; -------------------------------------------------------------------------------- /source/YAMPSettings.cpp: -------------------------------------------------------------------------------- 1 | #include "YAMPSettings.h" 2 | 3 | #define WIN32_LEAN_AND_MEAN 4 | #include 5 | 6 | #include 7 | 8 | static constexpr std::string_view INI_FILE_NAME = "settings.ini"; 9 | static constexpr uint32_t SETTINGS_VERSION = 1; 10 | 11 | static float GetPrivateProfileFloatW(LPCWSTR lpAppName, LPCWSTR lpKeyName, float fDefault, LPCWSTR lpFileName) 12 | { 13 | wchar_t buf[32]; 14 | GetPrivateProfileStringW(lpAppName, lpKeyName, nullptr, buf, std::size(buf), lpFileName); 15 | float val = _wtof(buf); 16 | if (val != 0.0f) 17 | { 18 | return val; 19 | } 20 | return fDefault; 21 | } 22 | 23 | static void WritePrivateProfileIntW(LPCWSTR lpAppName, LPCWSTR lpKeyName, int nValue, LPCWSTR lpFileName) 24 | { 25 | wchar_t buf[32]; 26 | swprintf_s(buf, L"%d", nValue); 27 | WritePrivateProfileStringW(lpAppName, lpKeyName, buf, lpFileName); 28 | } 29 | 30 | static void WritePrivateProfileFloatW(LPCWSTR lpAppName, LPCWSTR lpKeyName, float fValue, LPCWSTR lpFileName) 31 | { 32 | wchar_t buf[32]; 33 | swprintf_s(buf, L"%g", fValue); 34 | WritePrivateProfileStringW(lpAppName, lpKeyName, buf, lpFileName); 35 | } 36 | 37 | void YAMPSettings::LoadSettings(const std::filesystem::path& dirPath) 38 | { 39 | const std::filesystem::path iniPath = dirPath / std::filesystem::u8path(INI_FILE_NAME); 40 | 41 | { 42 | const wchar_t* SECTION_NAME = L"General"; 43 | if (int version = GetPrivateProfileIntW(SECTION_NAME, L"Version", 0, iniPath.c_str()); version != SETTINGS_VERSION) 44 | { 45 | return; 46 | } 47 | 48 | m_buildLastShowedDisclaimer = GetPrivateProfileIntW(SECTION_NAME, L"Disclaimer", 0, iniPath.c_str()); 49 | } 50 | 51 | { 52 | const wchar_t* SECTION_NAME = L"Graphics"; 53 | int resX = GetPrivateProfileIntW(SECTION_NAME, L"ResolutionX", 0, iniPath.c_str()); 54 | int resY = GetPrivateProfileIntW(SECTION_NAME, L"ResolutionY", 0, iniPath.c_str()); 55 | if (resX != 0 && resY != 0) 56 | { 57 | m_resX = resX; 58 | m_resY = resY; 59 | } 60 | 61 | float refRate = GetPrivateProfileFloatW(SECTION_NAME, L"RefreshRate", 0.0f, iniPath.c_str()); 62 | if (refRate != 0.0f) 63 | { 64 | m_refreshRate = refRate; 65 | } 66 | 67 | m_fullscreen = GetPrivateProfileIntW(SECTION_NAME, L"Fullscreen", 0, iniPath.c_str()) != 0; 68 | m_enableFpsCap = GetPrivateProfileIntW(SECTION_NAME, L"FPSCap", 1, iniPath.c_str()) != 0; 69 | } 70 | 71 | { 72 | const wchar_t* SECTION_NAME = L"Debug"; 73 | m_dontApplyPatches = GetPrivateProfileIntW(SECTION_NAME, L"DoNotApplyPatches", 0, iniPath.c_str()) != 0; 74 | m_useD3DDebugLayer = GetPrivateProfileIntW(SECTION_NAME, L"UseDebugD3D", 0, iniPath.c_str()) != 0; 75 | } 76 | 77 | { 78 | const wchar_t* SECTION_NAME = L"VF5FS"; 79 | m_arcadeMode = GetPrivateProfileIntW(SECTION_NAME, L"ArcadeMode", 0, iniPath.c_str()) != 0; 80 | m_circleConfirm = GetPrivateProfileIntW(SECTION_NAME, L"CircleConfirm", 0, iniPath.c_str()) != 0; 81 | m_language = GetPrivateProfileIntW(SECTION_NAME, L"Language", 1, iniPath.c_str()); 82 | } 83 | } 84 | 85 | void YAMPSettings::SaveSettings(const std::filesystem::path& dirPath) 86 | { 87 | const std::filesystem::path iniPath = dirPath / std::filesystem::u8path(INI_FILE_NAME); 88 | 89 | { 90 | const wchar_t* SECTION_NAME = L"General"; 91 | WritePrivateProfileIntW(SECTION_NAME, L"Version", SETTINGS_VERSION, iniPath.c_str()); 92 | WritePrivateProfileIntW(SECTION_NAME, L"Disclaimer", m_buildLastShowedDisclaimer, iniPath.c_str()); 93 | } 94 | 95 | { 96 | const wchar_t* SECTION_NAME = L"Graphics"; 97 | WritePrivateProfileIntW(SECTION_NAME, L"ResolutionX", m_resX, iniPath.c_str()); 98 | WritePrivateProfileIntW(SECTION_NAME, L"ResolutionY", m_resY, iniPath.c_str()); 99 | 100 | WritePrivateProfileFloatW(SECTION_NAME, L"RefreshRate", m_refreshRate, iniPath.c_str()); 101 | 102 | WritePrivateProfileIntW(SECTION_NAME, L"Fullscreen", m_fullscreen, iniPath.c_str()); 103 | WritePrivateProfileIntW(SECTION_NAME, L"FPSCap", m_enableFpsCap, iniPath.c_str()); 104 | } 105 | 106 | { 107 | const wchar_t* SECTION_NAME = L"Debug"; 108 | WritePrivateProfileIntW(SECTION_NAME, L"DoNotApplyPatches", m_dontApplyPatches, iniPath.c_str()); 109 | WritePrivateProfileIntW(SECTION_NAME, L"UseDebugD3D", m_useD3DDebugLayer, iniPath.c_str()); 110 | } 111 | 112 | { 113 | const wchar_t* SECTION_NAME = L"VF5FS"; 114 | WritePrivateProfileIntW(SECTION_NAME, L"ArcadeMode", m_arcadeMode, iniPath.c_str()); 115 | WritePrivateProfileIntW(SECTION_NAME, L"CircleConfirm", m_circleConfirm, iniPath.c_str()); 116 | WritePrivateProfileIntW(SECTION_NAME, L"Language", m_language, iniPath.c_str()); 117 | } 118 | } 119 | -------------------------------------------------------------------------------- /source/YAMPSettings.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | class YAMPSettings 6 | { 7 | public: 8 | void LoadSettings(const std::filesystem::path& dirPath); 9 | void SaveSettings(const std::filesystem::path& dirPath); 10 | 11 | public: 12 | // Graphics 13 | uint32_t m_resX = 1280; 14 | uint32_t m_resY = 720; 15 | float m_refreshRate = 60.0f; 16 | bool m_fullscreen = false; 17 | bool m_enableFpsCap = true; 18 | 19 | // Game 20 | // TODO: Subclass once more games are added 21 | bool m_arcadeMode = false; 22 | bool m_circleConfirm = false; 23 | uint32_t m_language = 1; // English 24 | 25 | // Debug settings 26 | bool m_dontApplyPatches = false; 27 | bool m_useD3DDebugLayer = false; 28 | 29 | // Misc 30 | uint32_t m_buildLastShowedDisclaimer = 0; 31 | }; -------------------------------------------------------------------------------- /source/YAMPUserInterface.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | class YAMPUserInterface 9 | { 10 | public: 11 | YAMPUserInterface() = default; 12 | 13 | void Draw(); 14 | void AddResolution(uint32_t width, uint32_t height, float refreshRate); 15 | void GetDefaultsFromSettings(); 16 | 17 | private: 18 | void DrawGraphics(); 19 | void DrawGame(); 20 | void DrawControls(); 21 | void DrawDebug(); 22 | void DrawAbout(); 23 | bool DrawSettingsConfirmation(); 24 | 25 | void DrawDisclaimer(); 26 | 27 | bool ProcessF1Key(); 28 | 29 | void ApplySettings(); 30 | void DiscardSettings(); 31 | 32 | private: 33 | struct Resolution 34 | { 35 | struct RefreshRate 36 | { 37 | float refreshRate; 38 | std::string displayString; 39 | }; 40 | 41 | uint32_t width, height; 42 | std::string displayString; 43 | std::vector refreshRates; 44 | }; 45 | 46 | std::vector m_resolutions; 47 | 48 | // Current settings 49 | // Graphics 50 | size_t m_currentResolutionIndex = 0; 51 | size_t m_currentRefRateIndex = 0; 52 | bool m_currentFullscreen = false; 53 | bool m_enableFpsCap = true; 54 | 55 | // Game settings 56 | // TODO: Subclass once more games are added 57 | bool m_arcadeMode = false; 58 | bool m_circleConfirm = false; 59 | uint32_t m_language = 1; 60 | 61 | // Debug settings 62 | bool m_dontApplyPatches = false; 63 | bool m_useD3DDebugLayer = false; 64 | 65 | // Volatile state 66 | bool m_settingsOpen = false, m_pageModified = false, m_showRestartWarning = false; 67 | std::optional m_debugInfoAccepted { false }; 68 | }; -------------------------------------------------------------------------------- /source/blitShader.hlsl: -------------------------------------------------------------------------------- 1 | struct vs_in { 2 | float2 position_local : POSITION; 3 | float2 uv : TEXCOORD0; 4 | }; 5 | 6 | struct vs_out { 7 | float4 position_clip : SV_POSITION; 8 | float2 uv : TEXCOORD0; 9 | }; 10 | 11 | Texture2D backBufferTexture : register(t0); 12 | SamplerState backBufferSampler : register(s0); 13 | 14 | vs_out vs_main(vs_in input) { 15 | vs_out output; 16 | output.position_clip = float4(input.position_local, 1.0, 1.0); 17 | output.uv = input.uv; 18 | return output; 19 | } 20 | 21 | float4 ps_main(vs_out input) : SV_TARGET { 22 | return backBufferTexture.Sample(backBufferSampler, input.uv); 23 | } -------------------------------------------------------------------------------- /source/criware/CriStub.cpp: -------------------------------------------------------------------------------- 1 | #include "CriStub.h" 2 | 3 | // Stub implementation for icri.h interfaces 4 | CriAtomExPlayerTag* CriStub::criAtomExPlayer_Create(CriAtomExPlayerConfigTag*, void*, int) 5 | { 6 | return nullptr; 7 | } 8 | 9 | void CriStub::gap1() 10 | { 11 | } 12 | 13 | void CriStub::gap2() 14 | { 15 | } 16 | 17 | void CriStub::gap3() 18 | { 19 | } 20 | 21 | void CriStub::gap4() 22 | { 23 | } 24 | 25 | void CriStub::gap5() 26 | { 27 | } 28 | 29 | void CriStub::gap6() 30 | { 31 | } 32 | 33 | void CriStub::gap7() 34 | { 35 | } 36 | 37 | void CriStub::gap8() 38 | { 39 | } 40 | 41 | void CriStub::gap9() 42 | { 43 | } 44 | 45 | int CriStub::criAtomExPlayer_CalculateWorkSize(CriAtomExPlayerConfigTag*) 46 | { 47 | return 0; 48 | } 49 | 50 | unsigned int CriStub::criAtomExPlayer_Start(CriAtomExPlayerTag*) 51 | { 52 | return 0; 53 | } 54 | 55 | void CriStub::criAtomExPlayer_Stop(CriAtomExPlayerTag*) 56 | { 57 | } 58 | 59 | void CriStub::criAtomExPlayer_StopWithoutReleaseTime(CriAtomExPlayerTag*) 60 | { 61 | } 62 | 63 | void CriStub::criAtomExPlayer_ResetParameters(CriAtomExPlayerTag*) 64 | { 65 | } 66 | 67 | void CriStub::criAtomExPlayer_UpdateAll(CriAtomExPlayerTag*) 68 | { 69 | } 70 | 71 | void CriStub::criAtomExPlayer_SetPitch(CriAtomExPlayerTag*, float) 72 | { 73 | } 74 | 75 | void CriStub::criAtomExPlayer_SetPan3dAngle(CriAtomExPlayerTag*, float) 76 | { 77 | } 78 | 79 | void CriStub::criAtomExPlayer_Pause(CriAtomExPlayerTag*, int) 80 | { 81 | } 82 | 83 | void CriStub::criAtomExPlayer_SetAisacControlByName(CriAtomExPlayerTag*, const char*, float) 84 | { 85 | } 86 | 87 | int CriStub::criAtomExPlayer_IsPaused(CriAtomExPlayerTag*) 88 | { 89 | return 0; 90 | } 91 | 92 | void CriStub::criAtom_ExecuteMain() 93 | { 94 | } 95 | 96 | void CriStub::criAtomExPlayer_SetData(CriAtomExPlayerTag*, void*, int) 97 | { 98 | } 99 | 100 | void CriStub::criAtomExPlayer_SetFormat(CriAtomExPlayerTag*, unsigned int) 101 | { 102 | } 103 | 104 | void CriStub::criAtomExPlayer_SetNumChannels(CriAtomExPlayerTag*, int) 105 | { 106 | } 107 | 108 | void CriStub::criAtomExPlayer_SetSamplingRate(CriAtomExPlayerTag*, int) 109 | { 110 | } 111 | 112 | void CriStub::criAtomExPlayer_SetFile(CriAtomExPlayerTag*, CriFsBinderHnObjTag*, const char*) 113 | { 114 | } 115 | 116 | void CriStub::criAtomExPlayer_LimitLoopCount(CriAtomExPlayerTag*, int) 117 | { 118 | } 119 | 120 | void CriStub::criAtomExPlayer_SetVoicePriority(CriAtomExPlayerTag*, int) 121 | { 122 | } 123 | 124 | int CriStub::criManaPlayer_CalculateHandleWorkSize() 125 | { 126 | return 0; 127 | } 128 | 129 | CriManaPlayerTag* CriStub::criManaPlayer_Create(void*, int) 130 | { 131 | return nullptr; 132 | } 133 | 134 | void CriStub::criManaPlayer_Destroy(CriManaPlayerTag*) 135 | { 136 | } 137 | 138 | void CriStub::criManaPlayer_SetFile(CriManaPlayerTag*, CriFsBinderHnObjTag*, const char*) 139 | { 140 | } 141 | 142 | void CriStub::criManaPlayer_Start(CriManaPlayerTag*) 143 | { 144 | } 145 | 146 | void CriStub::criManaPlayer_Stop(CriManaPlayerTag*) 147 | { 148 | } 149 | 150 | void CriStub::criManaPlayer_StopAndWaitCompletion(CriManaPlayerTag*) 151 | { 152 | } 153 | 154 | void CriStub::criManaPlayer_Pause(CriManaPlayerTag*, int) 155 | { 156 | } 157 | 158 | int CriStub::criManaPlayer_IsPaused(CriManaPlayerTag*) 159 | { 160 | return 0; 161 | } 162 | 163 | void CriStub::criManaPlayer_GetTime(CriManaPlayerTag*, unsigned __int64*, unsigned __int64*) 164 | { 165 | } 166 | 167 | CriManaPlayerStatus CriStub::criManaPlayer_GetStatus(CriManaPlayerTag*) 168 | { 169 | return CriManaPlayerStatus(); 170 | } 171 | 172 | int CriStub::criManaPlayer_ReferFrame(CriManaPlayerTag*, CriManaFrameInfo*) 173 | { 174 | return 0; 175 | } 176 | 177 | int CriStub::criManaPlayer_IsFrameOnTime(CriManaPlayerTag*, CriManaFrameInfo*) 178 | { 179 | return 0; 180 | } 181 | 182 | void CriStub::criManaPlayer_CopyFrameToBuffersYUV(CriManaPlayerTag*, CriManaFrameInfo*, CriManaTextureBuffersYUV*) 183 | { 184 | } 185 | 186 | void CriStub::criManaPlayer_DiscardFrame(CriManaPlayerTag*, CriManaFrameInfo*) 187 | { 188 | } 189 | 190 | float CriStub::criManaPlayer_GetVolume(CriManaPlayerTag*) 191 | { 192 | return 0.0f; 193 | } 194 | 195 | void CriStub::criManaPlayer_SetVolume(CriManaPlayerTag*, float) 196 | { 197 | } 198 | 199 | int CriStub::criManaPlayer_GetPlaybackWorkParam(CriManaPlayerTag*, CriManaPlaybackBasicWorkConfig*, CriManaPlaybackExWorkConfig*) 200 | { 201 | return 0; 202 | } 203 | 204 | int CriStub::criManaPlayer_CalculatePlaybackWorkSize(CriManaPlayerTag*, CriManaPlaybackBasicWorkConfig*, CriManaPlaybackExWorkConfig*) 205 | { 206 | return 0; 207 | } 208 | 209 | void CriStub::criManaPlayer_SetPlaybackWork(CriManaPlayerTag*, CriManaPlaybackBasicWorkConfig*, CriManaPlaybackExWorkConfig*, void*, int) 210 | { 211 | } 212 | 213 | void CriStub::criManaPlayer_FreePlaybackWork(CriManaPlayerTag*) 214 | { 215 | } 216 | 217 | void CriStub::criManaPlayer_Prepare(CriManaPlayerTag*) 218 | { 219 | } 220 | 221 | void CriStub::criManaPlayer_DecodeHeader(CriManaPlayerTag*) 222 | { 223 | } 224 | 225 | void CriStub::criMana_SyncMasterTimer() 226 | { 227 | } 228 | 229 | void CriStub::criMana_ExecuteMain() 230 | { 231 | } 232 | 233 | void* CriStub::alloc(size_t size, size_t align) 234 | { 235 | return nullptr; 236 | } 237 | 238 | void CriStub::free(void*) 239 | { 240 | } 241 | 242 | int CriStub::criAtomEx_CalculateWorkSizeForRegisterAcfData(void*, int) 243 | { 244 | return 0; 245 | } 246 | 247 | void CriStub::criAtomEx_RegisterAcfData(void*, int, void*, int) 248 | { 249 | } 250 | 251 | void CriStub::criAtomEx_UnregisterAcf() 252 | { 253 | } 254 | 255 | int CriStub::criAtomEx_CalculateWorkSizeForDspBusSetting(const char*) 256 | { 257 | return 0; 258 | } 259 | 260 | int CriStub::criAtomEx_CalculateWorkSizeForDspBusSettingFromAcfData(void*, int, const char*) 261 | { 262 | return 0; 263 | } 264 | 265 | void CriStub::criAtomEx_AttachDspBusSetting(const char*, void*, int) 266 | { 267 | } 268 | 269 | CriAtomExVoicePoolTag* CriStub::criAtomExVoicePool_AllocateStandardVoicePool(CriAtomExStandardVoicePoolConfigTag*, void*, int) 270 | { 271 | return nullptr; 272 | } 273 | 274 | void CriStub::criAtomExVoicePool_Free(CriAtomExVoicePoolTag*) 275 | { 276 | } 277 | 278 | void CriStub::criAtomDbas_Destroy(int) 279 | { 280 | } 281 | 282 | void CriStub::criAtomEx_DetachDspBusSetting() 283 | { 284 | } 285 | 286 | void CriStub::criAtomEx_ExecuteMain() 287 | { 288 | } 289 | 290 | void CriStub::criAtomExPlayer_SetBusSendLevelByName(CriAtomExPlayerTag*, const char*, float) 291 | { 292 | } 293 | 294 | void CriStub::criAtomExPlayer_SetBusSendLevelOffsetByName(CriAtomExPlayerTag*, const char*, float) 295 | { 296 | } 297 | 298 | unsigned int CriStub::criAtomExPlayer_Prepare(CriAtomExPlayerTag*) 299 | { 300 | return 0; 301 | } 302 | 303 | void CriStub::criAtomExPlayer_SetAisacControlById(CriAtomExPlayerTag*, unsigned int, float) 304 | { 305 | } 306 | 307 | void CriStub::criAtomExPlayer_Resume(CriAtomExPlayerTag*, CriAtomExResumeModeTag) 308 | { 309 | } 310 | 311 | void CriStub::criAtomExPlayer_Update(CriAtomExPlayerTag*, unsigned int) 312 | { 313 | } 314 | 315 | void CriStub::unmount(unsigned int) 316 | { 317 | } 318 | 319 | void CriStub::remount(unsigned int) 320 | { 321 | } 322 | 323 | void CriStub::criAtomExPlayer_SetVoicePoolIdentifier(CriAtomExPlayerTag*, unsigned int) 324 | { 325 | } 326 | 327 | void CriStub::criAtomExPlayer_SetDspParameter(CriAtomExPlayerTag*, int, float) 328 | { 329 | } 330 | 331 | int CriStub::criAtomExAcb_GetWaveformInfoByName(CriAtomExAcbTag*, const char*, CriAtomExWaveformInfoTag*) 332 | { 333 | return 0; 334 | } 335 | -------------------------------------------------------------------------------- /source/criware/CriStub.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "icri.h" 4 | 5 | class CriStub final : public icri 6 | { 7 | public: 8 | virtual CriAtomExPlayerTag* criAtomExPlayer_Create(CriAtomExPlayerConfigTag*, void*, int) override; 9 | virtual void gap1() override; 10 | virtual void gap2() override; 11 | virtual void gap3() override; 12 | virtual void gap4() override; 13 | virtual void gap5() override; 14 | virtual void gap6() override; 15 | virtual void gap7() override; 16 | virtual void gap8() override; 17 | virtual void gap9() override; 18 | virtual int criAtomExPlayer_CalculateWorkSize(CriAtomExPlayerConfigTag*) override; 19 | virtual unsigned int criAtomExPlayer_Start(CriAtomExPlayerTag*) override; 20 | virtual void criAtomExPlayer_Stop(CriAtomExPlayerTag*) override; 21 | virtual void criAtomExPlayer_StopWithoutReleaseTime(CriAtomExPlayerTag*) override; 22 | virtual void criAtomExPlayer_ResetParameters(CriAtomExPlayerTag*) override; 23 | virtual void criAtomExPlayer_UpdateAll(CriAtomExPlayerTag*) override; 24 | virtual void criAtomExPlayer_SetPitch(CriAtomExPlayerTag*, float) override; 25 | virtual void criAtomExPlayer_SetPan3dAngle(CriAtomExPlayerTag*, float) override; 26 | virtual void criAtomExPlayer_Pause(CriAtomExPlayerTag*, int) override; 27 | virtual void criAtomExPlayer_SetAisacControlByName(CriAtomExPlayerTag*, const char*, float) override; 28 | virtual int criAtomExPlayer_IsPaused(CriAtomExPlayerTag*) override; 29 | virtual void criAtom_ExecuteMain() override; 30 | virtual void criAtomExPlayer_SetData(CriAtomExPlayerTag*, void*, int) override; 31 | virtual void criAtomExPlayer_SetFormat(CriAtomExPlayerTag*, unsigned int) override; 32 | virtual void criAtomExPlayer_SetNumChannels(CriAtomExPlayerTag*, int) override; 33 | virtual void criAtomExPlayer_SetSamplingRate(CriAtomExPlayerTag*, int) override; 34 | virtual void criAtomExPlayer_SetFile(CriAtomExPlayerTag*, CriFsBinderHnObjTag*, const char*) override; 35 | virtual void criAtomExPlayer_LimitLoopCount(CriAtomExPlayerTag*, int) override; 36 | virtual void criAtomExPlayer_SetVoicePriority(CriAtomExPlayerTag*, int) override; 37 | virtual int criManaPlayer_CalculateHandleWorkSize() override; 38 | virtual CriManaPlayerTag* criManaPlayer_Create(void*, int) override; 39 | virtual void criManaPlayer_Destroy(CriManaPlayerTag*) override; 40 | virtual void criManaPlayer_SetFile(CriManaPlayerTag*, CriFsBinderHnObjTag*, const char*) override; 41 | virtual void criManaPlayer_Start(CriManaPlayerTag*) override; 42 | virtual void criManaPlayer_Stop(CriManaPlayerTag*) override; 43 | virtual void criManaPlayer_StopAndWaitCompletion(CriManaPlayerTag*) override; 44 | virtual void criManaPlayer_Pause(CriManaPlayerTag*, int) override; 45 | virtual int criManaPlayer_IsPaused(CriManaPlayerTag*) override; 46 | virtual void criManaPlayer_GetTime(CriManaPlayerTag*, unsigned __int64*, unsigned __int64*) override; 47 | virtual CriManaPlayerStatus criManaPlayer_GetStatus(CriManaPlayerTag*) override; 48 | virtual int criManaPlayer_ReferFrame(CriManaPlayerTag*, CriManaFrameInfo*) override; 49 | virtual int criManaPlayer_IsFrameOnTime(CriManaPlayerTag*, CriManaFrameInfo*) override; 50 | virtual void criManaPlayer_CopyFrameToBuffersYUV(CriManaPlayerTag*, CriManaFrameInfo*, CriManaTextureBuffersYUV*) override; 51 | virtual void criManaPlayer_DiscardFrame(CriManaPlayerTag*, CriManaFrameInfo*) override; 52 | virtual float criManaPlayer_GetVolume(CriManaPlayerTag*) override; 53 | virtual void criManaPlayer_SetVolume(CriManaPlayerTag*, float) override; 54 | virtual int criManaPlayer_GetPlaybackWorkParam(CriManaPlayerTag*, CriManaPlaybackBasicWorkConfig*, CriManaPlaybackExWorkConfig*) override; 55 | virtual int criManaPlayer_CalculatePlaybackWorkSize(CriManaPlayerTag*, CriManaPlaybackBasicWorkConfig*, CriManaPlaybackExWorkConfig*) override; 56 | virtual void criManaPlayer_SetPlaybackWork(CriManaPlayerTag*, CriManaPlaybackBasicWorkConfig*, CriManaPlaybackExWorkConfig*, void*, int) override; 57 | virtual void criManaPlayer_FreePlaybackWork(CriManaPlayerTag*) override; 58 | virtual void criManaPlayer_Prepare(CriManaPlayerTag*) override; 59 | virtual void criManaPlayer_DecodeHeader(CriManaPlayerTag*) override; 60 | virtual void criMana_SyncMasterTimer() override; 61 | virtual void criMana_ExecuteMain() override; 62 | virtual void* alloc(size_t size, size_t align) override; 63 | virtual void free(void*) override; 64 | virtual int criAtomEx_CalculateWorkSizeForRegisterAcfData(void*, int) override; 65 | virtual void criAtomEx_RegisterAcfData(void*, int, void*, int) override; 66 | virtual void criAtomEx_UnregisterAcf() override; 67 | virtual int criAtomEx_CalculateWorkSizeForDspBusSetting(const char*) override; 68 | virtual int criAtomEx_CalculateWorkSizeForDspBusSettingFromAcfData(void*, int, const char*) override; 69 | virtual void criAtomEx_AttachDspBusSetting(const char*, void*, int) override; 70 | virtual CriAtomExVoicePoolTag* criAtomExVoicePool_AllocateStandardVoicePool(CriAtomExStandardVoicePoolConfigTag*, void*, int) override; 71 | virtual void criAtomExVoicePool_Free(CriAtomExVoicePoolTag*) override; 72 | virtual void criAtomDbas_Destroy(int) override; 73 | virtual void criAtomEx_DetachDspBusSetting() override; 74 | virtual void criAtomEx_ExecuteMain() override; 75 | virtual void criAtomExPlayer_SetBusSendLevelByName(CriAtomExPlayerTag*, const char*, float) override; 76 | virtual void criAtomExPlayer_SetBusSendLevelOffsetByName(CriAtomExPlayerTag*, const char*, float) override; 77 | virtual unsigned int criAtomExPlayer_Prepare(CriAtomExPlayerTag*) override; 78 | virtual void criAtomExPlayer_SetAisacControlById(CriAtomExPlayerTag*, unsigned int, float) override; 79 | virtual void criAtomExPlayer_Resume(CriAtomExPlayerTag*, CriAtomExResumeModeTag) override; 80 | virtual void criAtomExPlayer_Update(CriAtomExPlayerTag*, unsigned int) override; 81 | virtual void unmount(unsigned int) override; 82 | virtual void remount(unsigned int) override; 83 | virtual void criAtomExPlayer_SetVoicePoolIdentifier(CriAtomExPlayerTag*, unsigned int) override; 84 | virtual void criAtomExPlayer_SetDspParameter(CriAtomExPlayerTag*, int, float) override; 85 | virtual int criAtomExAcb_GetWaveformInfoByName(CriAtomExAcbTag*, const char*, CriAtomExWaveformInfoTag*) override; 86 | }; -------------------------------------------------------------------------------- /source/criware/icri.h: -------------------------------------------------------------------------------- 1 | /* 2 | This file has been generated by IDA. 3 | It contains local type definitions from 4 | the type library 'vf5fs-pxd-w64-retail' 5 | */ 6 | 7 | struct CriAtomExPlayerConfigTag; 8 | struct CriManaFrameInfo; 9 | struct CriManaTextureBuffersYUV; 10 | struct CriManaPlaybackBasicWorkConfig; 11 | struct CriManaPlaybackExWorkConfig; 12 | struct CriAtomExStandardVoicePoolConfigTag; 13 | struct CriAtomExWaveformInfoTag; 14 | 15 | /* 720 */ 16 | enum CriManaPlayerStatus 17 | { 18 | CRIMANAPLAYER_STATUS_STOP = 0x0, 19 | CRIMANAPLAYER_STATUS_DECHDR = 0x1, 20 | CRIMANAPLAYER_STATUS_WAIT_PREP = 0x2, 21 | CRIMANAPLAYER_STATUS_PREP = 0x3, 22 | CRIMANAPLAYER_STATUS_READY = 0x4, 23 | CRIMANAPLAYER_STATUS_PLAYING = 0x5, 24 | CRIMANAPLAYER_STATUS_PLAYEND = 0x6, 25 | CRIMANAPLAYER_STATUS_ERROR = 0x7, 26 | CRIMANAPLAYER_STATUS_ENUM_SIZE_IS_4BYTES = 0x7FFFFFFF, 27 | }; 28 | 29 | /* 723 */ 30 | enum CriAtomExResumeModeTag 31 | { 32 | CRIATOMEX_RESUME_ALL_PLAYBACK = 0x0, 33 | CRIATOMEX_RESUME_PAUSED_PLAYBACK = 0x1, 34 | CRIATOMEX_RESUME_PREPARED_PLAYBACK = 0x2, 35 | CRIATOMEX_RESUME_MODE_RESERVED = 0x3, 36 | CRIATOMEX_RESUME_MODE_ENUM_SIZE_IS_4BYTES = 0x7FFFFFFF, 37 | }; 38 | 39 | /* 724 */ 40 | enum CriAtomExVoiceAllocationMethodTag 41 | { 42 | CRIATOMEX_ALLOCATE_VOICE_ONCE = 0x0, 43 | CRIATOMEX_RETRY_VOICE_ALLOCATION = 0x1, 44 | CRIATOMEX_VOICE_ALLOCATION_METHOD_IS_4BYTE = 0x7FFFFFFF, 45 | }; 46 | 47 | /* 5574 */ 48 | const struct CriAtomExPlayerConfigTag 49 | { 50 | CriAtomExVoiceAllocationMethodTag voice_allocation_method; 51 | int max_path_strings; 52 | int max_path; 53 | char max_aisacs; 54 | int updates_time; 55 | int enable_audio_synced_timer; 56 | }; 57 | 58 | /* 5576 */ 59 | struct CriManaImageBufferInfo 60 | { 61 | char *imageptr; 62 | unsigned int bufsize; 63 | unsigned int line_pitch; 64 | unsigned int line_size; 65 | unsigned int num_lines; 66 | }; 67 | 68 | /* 728 */ 69 | enum CriManaAlphaType 70 | { 71 | CRIMANA_COMPO_OPAQ = 0x0, 72 | CRIMANA_COMPO_ALPHFULL = 0x1, 73 | CRIMANA_COMPO_ALPH3STEP = 0x2, 74 | CRIMANA_COMPO_ALPH32BIT = 0x3, 75 | CRIMANA_COMPO_ALPH1BIT = 0x4, 76 | CRIMANA_COMPO_ALPH2BIT = 0x5, 77 | CRIMANA_COMPO_ALPH3BIT = 0x6, 78 | CRIMANA_COMPO_ALPH4BIT = 0x7, 79 | CRIMANA_COMPO_ENUM_SIZE_IS_4BYTES = 0x7FFFFFFF, 80 | }; 81 | 82 | /* 729 */ 83 | enum CriManaReferFrameResult 84 | { 85 | CRIMANA_REFER_RESULT_OK = 0x0, 86 | CRIMANA_REFER_RESULT_SHORT_INPUT = 0x1, 87 | CRIMANA_REFER_RESULT_SHORT_CPUTIME = 0x2, 88 | CRIMANA_REFER_RESULT_NO_MORE_KEEP = 0x3, 89 | CRIMANA_REFER_RESULT_ENUM_SIZE_IS_4BYTES = 0x7FFFFFFF, 90 | }; 91 | 92 | /* 730 */ 93 | enum CriManaColorSpaceConversionType 94 | { 95 | CRIMANA_COLORSPACE_CONVERSION_TYPE_ITU_R_BT601_LIMITED = 0x0, 96 | CRIMANA_COLORSPACE_CONVERSION_TYPE_ITU_R_BT601_FULLRANGE = 0x1, 97 | CRIMANA_COLORSPACE_CONVERSION_TYPE_ENUM_SIZE_IS_4BYTES = 0x7FFFFFFF, 98 | }; 99 | 100 | /* 5577 */ 101 | struct __declspec(align(8)) CriManaFrameInfo 102 | { 103 | int frame_no; 104 | int frame_no_per_file; 105 | unsigned int width; 106 | unsigned int height; 107 | unsigned int disp_width; 108 | unsigned int disp_height; 109 | unsigned int framerate; 110 | unsigned int framerate_n; 111 | unsigned int framerate_d; 112 | unsigned int total_frames_per_file; 113 | unsigned __int64 time; 114 | unsigned __int64 time_per_file; 115 | unsigned __int64 tunit; 116 | unsigned int cnt_concatenated_movie; 117 | int num_images; 118 | CriManaImageBufferInfo image_info[4]; 119 | int csc_flag; 120 | CriManaAlphaType alpha_type; 121 | CriManaReferFrameResult ref_result; 122 | void *details_ptr[2]; 123 | CriManaColorSpaceConversionType color_conv; 124 | unsigned int cnt_skipped_frames; 125 | unsigned int reserved[1]; 126 | }; 127 | 128 | /* 5578 */ 129 | struct CriManaTextureBuffer 130 | { 131 | char *imagebuf; 132 | unsigned int bufsize; 133 | unsigned int pitch; 134 | }; 135 | 136 | /* 5579 */ 137 | struct CriManaTextureBuffersYUV 138 | { 139 | CriManaTextureBuffer y_plane; 140 | CriManaTextureBuffer u_plane; 141 | CriManaTextureBuffer v_plane; 142 | CriManaTextureBuffer a_plane; 143 | }; 144 | 145 | /* 5580 */ 146 | struct CriManaPlaybackCommonParams 147 | { 148 | int readbuf_size_byte; 149 | unsigned int max_audio_tracks; 150 | }; 151 | 152 | /* 726 */ 153 | enum CriManaVideoCodecType 154 | { 155 | CRIMANA_VIDEO_CODEC_UNKNOWN = 0x0, 156 | CRIMANA_VIDEO_CODEC_SOFDEC_PRIME = 0x1, 157 | CRIMANA_VIDEO_CODEC_H264 = 0x5, 158 | CRIMANA_VIDEO_CODEC_VP9 = 0x9, 159 | CRIMANA_VIDEO_CODEC_ENUM_SIZE_IS_4BYTES = 0x7FFFFFFF, 160 | }; 161 | 162 | /* 5581 */ 163 | struct CriManaPlaybackVideoParams 164 | { 165 | int video_flag; 166 | int max_width; 167 | int max_height; 168 | int num_frame_pools; 169 | CriManaVideoCodecType codec_type; 170 | int capacity_of_picsize; 171 | int framerate_n; 172 | int framerate_d; 173 | }; 174 | 175 | /* 727 */ 176 | enum CriManaAudioCodecType 177 | { 178 | CRIMANA_AUDIO_CODEC_UNKNOWN = 0x0, 179 | CRIMANA_AUDIO_CODEC_ADX = 0x2, 180 | CRIMANA_AUDIO_CODEC_HCA = 0x4, 181 | CRIMANA_AUDIO_CODEC_ENUM_SIZE_IS_4BYTES = 0x7FFFFFFF, 182 | }; 183 | 184 | /* 5582 */ 185 | struct CriManaPlaybackAudioParams 186 | { 187 | int audio_flag; 188 | unsigned int sampling_rate; 189 | unsigned int num_channels; 190 | int output_buffer_samples; 191 | CriManaAudioCodecType codec_type; 192 | }; 193 | 194 | /* 5583 */ 195 | struct CriManaPlaybackSubtitleParams 196 | { 197 | int subtitle_flag; 198 | int max_datasize; 199 | }; 200 | 201 | /* 5584 */ 202 | struct CriManaPlaybackBasicWorkConfig 203 | { 204 | CriManaPlaybackCommonParams common_params; 205 | CriManaPlaybackVideoParams video_params; 206 | CriManaPlaybackAudioParams main_audio_params; 207 | CriManaPlaybackSubtitleParams subtitle_params; 208 | }; 209 | 210 | /* 5585 */ 211 | struct CriManaPlaybackAlphaParams 212 | { 213 | int alpha_flag; 214 | CriManaAlphaType alpha_type; 215 | int max_width; 216 | int max_height; 217 | CriManaVideoCodecType codec_type; 218 | int capacity_of_picsize; 219 | }; 220 | 221 | /* 5586 */ 222 | struct CriManaPlaybackExWorkConfig 223 | { 224 | CriManaPlaybackAudioParams sub_audio_params; 225 | CriManaPlaybackAudioParams extra_audio_params; 226 | CriManaPlaybackAlphaParams alpha_params; 227 | }; 228 | 229 | /* 718 */ 230 | enum CriAtomSoundRendererTypeTag 231 | { 232 | CRIATOM_SOUND_RENDERER_NATIVE = 0x1, 233 | CRIATOM_SOUND_RENDERER_ASR = 0x2, 234 | CRIATOM_SOUND_RENDERER_HAPTIC = 0x3, 235 | CRIATOM_SOUND_RENDERER_HW1 = 0x1, 236 | CRIATOM_SOUND_RENDERER_HW2 = 0x5, 237 | CRIATOM_SOUND_RENDERER_HW3 = 0x9, 238 | CRIATOM_SOUND_RENDERER_HW4 = 0xD, 239 | CRIATOM_SOUND_RENDERER_ANY = 0x0, 240 | CRIATOM_SOUND_RENDERER_ENUM_SIZE_IS_4BYTES = 0x7FFFFFFF, 241 | }; 242 | 243 | /* 5587 */ 244 | struct CriAtomStandardPlayerConfigTag 245 | { 246 | int max_channels; 247 | int max_sampling_rate; 248 | int streaming_flag; 249 | CriAtomSoundRendererTypeTag sound_renderer_type; 250 | int decode_latency; 251 | }; 252 | 253 | /* 5588 */ 254 | const struct CriAtomExStandardVoicePoolConfigTag 255 | { 256 | unsigned int identifier; 257 | int num_voices; 258 | CriAtomStandardPlayerConfigTag player_config; 259 | }; 260 | 261 | /* 5589 */ 262 | struct CriAtomExWaveformInfoTag 263 | { 264 | int wave_id; 265 | unsigned int format; 266 | int sampling_rate; 267 | int num_channels; 268 | __int64 num_samples; 269 | int streaming_flag; 270 | unsigned int reserved[1]; 271 | }; 272 | 273 | class __declspec(novtable) icri 274 | { 275 | public: 276 | virtual struct CriAtomExPlayerTag *criAtomExPlayer_Create(CriAtomExPlayerConfigTag *, void *, int) = 0; 277 | // These are private or never used, as IDA defined them as a "gap" 278 | virtual void gap1() = 0; 279 | virtual void gap2() = 0; 280 | virtual void gap3() = 0; 281 | virtual void gap4() = 0; 282 | virtual void gap5() = 0; 283 | virtual void gap6() = 0; 284 | virtual void gap7() = 0; 285 | virtual void gap8() = 0; 286 | virtual void gap9() = 0; 287 | virtual int criAtomExPlayer_CalculateWorkSize(CriAtomExPlayerConfigTag *) = 0; 288 | virtual unsigned int criAtomExPlayer_Start(struct CriAtomExPlayerTag *) = 0; 289 | virtual void criAtomExPlayer_Stop(struct CriAtomExPlayerTag *) = 0; 290 | virtual void criAtomExPlayer_StopWithoutReleaseTime(struct CriAtomExPlayerTag *) = 0; 291 | virtual void criAtomExPlayer_ResetParameters(struct CriAtomExPlayerTag *) = 0; 292 | virtual void criAtomExPlayer_UpdateAll(struct CriAtomExPlayerTag *) = 0; 293 | virtual void criAtomExPlayer_SetPitch(struct CriAtomExPlayerTag *, float) = 0; 294 | virtual void criAtomExPlayer_SetPan3dAngle(struct CriAtomExPlayerTag *, float) = 0; 295 | virtual void criAtomExPlayer_Pause(struct CriAtomExPlayerTag *, int) = 0; 296 | virtual void criAtomExPlayer_SetAisacControlByName(struct CriAtomExPlayerTag *, const char *, float) = 0; 297 | virtual int criAtomExPlayer_IsPaused(struct CriAtomExPlayerTag *) = 0; 298 | virtual void criAtom_ExecuteMain() = 0; 299 | virtual void criAtomExPlayer_SetData(struct CriAtomExPlayerTag *, void *, int) = 0; 300 | virtual void criAtomExPlayer_SetFormat(struct CriAtomExPlayerTag *, unsigned int) = 0; 301 | virtual void criAtomExPlayer_SetNumChannels(struct CriAtomExPlayerTag *, int) = 0; 302 | virtual void criAtomExPlayer_SetSamplingRate(struct CriAtomExPlayerTag *, int) = 0; 303 | virtual void criAtomExPlayer_SetFile(struct CriAtomExPlayerTag *, struct CriFsBinderHnObjTag *, const char *) = 0; 304 | virtual void criAtomExPlayer_LimitLoopCount(struct CriAtomExPlayerTag *, int) = 0; 305 | virtual void criAtomExPlayer_SetVoicePriority(struct CriAtomExPlayerTag *, int) = 0; 306 | virtual int criManaPlayer_CalculateHandleWorkSize() = 0; 307 | virtual struct CriManaPlayerTag* criManaPlayer_Create(void *, int) = 0; 308 | virtual void criManaPlayer_Destroy(struct CriManaPlayerTag *) = 0; 309 | virtual void criManaPlayer_SetFile(struct CriManaPlayerTag *, struct CriFsBinderHnObjTag *, const char *) = 0; 310 | virtual void criManaPlayer_Start(struct CriManaPlayerTag *) = 0; 311 | virtual void criManaPlayer_Stop(struct CriManaPlayerTag *) = 0; 312 | virtual void criManaPlayer_StopAndWaitCompletion(struct CriManaPlayerTag *) = 0; 313 | virtual void criManaPlayer_Pause(struct CriManaPlayerTag *, int) = 0; 314 | virtual int criManaPlayer_IsPaused(struct CriManaPlayerTag *) = 0; 315 | virtual void criManaPlayer_GetTime(struct CriManaPlayerTag *, unsigned __int64 *, unsigned __int64 *) = 0; 316 | virtual CriManaPlayerStatus criManaPlayer_GetStatus(struct CriManaPlayerTag *) = 0; 317 | virtual int criManaPlayer_ReferFrame(struct CriManaPlayerTag *, CriManaFrameInfo *) = 0; 318 | virtual int criManaPlayer_IsFrameOnTime(struct CriManaPlayerTag *, CriManaFrameInfo *) = 0; 319 | virtual void criManaPlayer_CopyFrameToBuffersYUV(struct CriManaPlayerTag *, CriManaFrameInfo *, CriManaTextureBuffersYUV *) = 0; 320 | virtual void criManaPlayer_DiscardFrame(struct CriManaPlayerTag *, CriManaFrameInfo *) = 0; 321 | virtual float criManaPlayer_GetVolume(struct CriManaPlayerTag *) = 0; 322 | virtual void criManaPlayer_SetVolume(struct CriManaPlayerTag *, float) = 0; 323 | virtual int criManaPlayer_GetPlaybackWorkParam(struct CriManaPlayerTag *, CriManaPlaybackBasicWorkConfig *, CriManaPlaybackExWorkConfig *) = 0; 324 | virtual int criManaPlayer_CalculatePlaybackWorkSize(struct CriManaPlayerTag *, CriManaPlaybackBasicWorkConfig *, CriManaPlaybackExWorkConfig *) = 0; 325 | virtual void criManaPlayer_SetPlaybackWork(struct CriManaPlayerTag *, CriManaPlaybackBasicWorkConfig *, CriManaPlaybackExWorkConfig *, void *, int) = 0; 326 | virtual void criManaPlayer_FreePlaybackWork(struct CriManaPlayerTag *) = 0; 327 | virtual void criManaPlayer_Prepare(struct CriManaPlayerTag *) = 0; 328 | virtual void criManaPlayer_DecodeHeader(struct CriManaPlayerTag *) = 0; 329 | virtual void criMana_SyncMasterTimer() = 0; 330 | virtual void criMana_ExecuteMain() = 0; 331 | virtual ~icri() { }; 332 | virtual void *alloc(size_t size, size_t align) = 0; 333 | virtual void free(void *) = 0; 334 | virtual int criAtomEx_CalculateWorkSizeForRegisterAcfData(void *, int) = 0; 335 | virtual void criAtomEx_RegisterAcfData(void *, int, void *, int) = 0; 336 | virtual void criAtomEx_UnregisterAcf() = 0; 337 | virtual int criAtomEx_CalculateWorkSizeForDspBusSetting(const char *) = 0; 338 | virtual int criAtomEx_CalculateWorkSizeForDspBusSettingFromAcfData(void *, int, const char *) = 0; 339 | virtual void criAtomEx_AttachDspBusSetting(const char *, void *, int) = 0; 340 | virtual struct CriAtomExVoicePoolTag *criAtomExVoicePool_AllocateStandardVoicePool(CriAtomExStandardVoicePoolConfigTag *, void *, int) = 0; 341 | virtual void criAtomExVoicePool_Free(struct CriAtomExVoicePoolTag *) = 0; 342 | virtual void criAtomDbas_Destroy(int) = 0; 343 | virtual void criAtomEx_DetachDspBusSetting() = 0; 344 | virtual void criAtomEx_ExecuteMain() = 0; 345 | virtual void criAtomExPlayer_SetBusSendLevelByName(struct CriAtomExPlayerTag *, const char *, float) = 0; 346 | virtual void criAtomExPlayer_SetBusSendLevelOffsetByName(struct CriAtomExPlayerTag *, const char *, float) = 0; 347 | virtual unsigned int criAtomExPlayer_Prepare(struct CriAtomExPlayerTag *) = 0; 348 | virtual void criAtomExPlayer_SetAisacControlById(struct CriAtomExPlayerTag *, unsigned int, float) = 0; 349 | virtual void criAtomExPlayer_Resume(struct CriAtomExPlayerTag *, CriAtomExResumeModeTag) = 0; 350 | virtual void criAtomExPlayer_Update(struct CriAtomExPlayerTag *, unsigned int) = 0; 351 | virtual void unmount(unsigned int) = 0; 352 | virtual void remount(unsigned int) = 0; 353 | virtual void criAtomExPlayer_SetVoicePoolIdentifier(struct CriAtomExPlayerTag *, unsigned int) = 0; 354 | virtual void criAtomExPlayer_SetDspParameter(struct CriAtomExPlayerTag *, int, float) = 0; 355 | virtual int criAtomExAcb_GetWaveformInfoByName(struct CriAtomExAcbTag *, const char *, CriAtomExWaveformInfoTag *) = 0; 356 | }; -------------------------------------------------------------------------------- /source/imgui/imconfig.h: -------------------------------------------------------------------------------- 1 | //----------------------------------------------------------------------------- 2 | // COMPILE-TIME OPTIONS FOR DEAR IMGUI 3 | // Runtime options (clipboard callbacks, enabling various features, etc.) can generally be set via the ImGuiIO structure. 4 | // You can use ImGui::SetAllocatorFunctions() before calling ImGui::CreateContext() to rewire memory allocation functions. 5 | //----------------------------------------------------------------------------- 6 | // A) You may edit imconfig.h (and not overwrite it when updating Dear ImGui, or maintain a patch/rebased branch with your modifications to it) 7 | // B) or '#define IMGUI_USER_CONFIG "my_imgui_config.h"' in your project and then add directives in your own file without touching this template. 8 | //----------------------------------------------------------------------------- 9 | // You need to make sure that configuration settings are defined consistently _everywhere_ Dear ImGui is used, which include the imgui*.cpp 10 | // files but also _any_ of your code that uses Dear ImGui. This is because some compile-time options have an affect on data structures. 11 | // Defining those options in imconfig.h will ensure every compilation unit gets to see the same data structure layouts. 12 | // Call IMGUI_CHECKVERSION() from your .cpp files to verify that the data structures your files are using are matching the ones imgui.cpp is using. 13 | //----------------------------------------------------------------------------- 14 | 15 | #pragma once 16 | 17 | //---- Define assertion handler. Defaults to calling assert(). 18 | // If your macro uses multiple statements, make sure is enclosed in a 'do { .. } while (0)' block so it can be used as a single statement. 19 | //#define IM_ASSERT(_EXPR) MyAssert(_EXPR) 20 | //#define IM_ASSERT(_EXPR) ((void)(_EXPR)) // Disable asserts 21 | 22 | //---- Define attributes of all API symbols declarations, e.g. for DLL under Windows 23 | // Using Dear ImGui via a shared library is not recommended, because of function call overhead and because we don't guarantee backward nor forward ABI compatibility. 24 | // DLL users: heaps and globals are not shared across DLL boundaries! You will need to call SetCurrentContext() + SetAllocatorFunctions() 25 | // for each static/DLL boundary you are calling from. Read "Context and Memory Allocators" section of imgui.cpp for more details. 26 | //#define IMGUI_API __declspec( dllexport ) 27 | //#define IMGUI_API __declspec( dllimport ) 28 | 29 | //---- Don't define obsolete functions/enums/behaviors. Consider enabling from time to time after updating to avoid using soon-to-be obsolete function/names. 30 | //#define IMGUI_DISABLE_OBSOLETE_FUNCTIONS 31 | 32 | //---- Disable all of Dear ImGui or don't implement standard windows. 33 | // It is very strongly recommended to NOT disable the demo windows during development. Please read comments in imgui_demo.cpp. 34 | //#define IMGUI_DISABLE // Disable everything: all headers and source files will be empty. 35 | //#define IMGUI_DISABLE_DEMO_WINDOWS // Disable demo windows: ShowDemoWindow()/ShowStyleEditor() will be empty. Not recommended. 36 | //#define IMGUI_DISABLE_METRICS_WINDOW // Disable metrics/debugger window: ShowMetricsWindow() will be empty. 37 | 38 | //---- Don't implement some functions to reduce linkage requirements. 39 | #define IMGUI_DISABLE_WIN32_DEFAULT_CLIPBOARD_FUNCTIONS // [Win32] Don't implement default clipboard handler. Won't use and link with OpenClipboard/GetClipboardData/CloseClipboard etc. (user32.lib/.a, kernel32.lib/.a) 40 | #define IMGUI_DISABLE_WIN32_DEFAULT_IME_FUNCTIONS // [Win32] Don't implement default IME handler. Won't use and link with ImmGetContext/ImmSetCompositionWindow. (imm32.lib/.a) 41 | //#define IMGUI_DISABLE_WIN32_FUNCTIONS // [Win32] Won't use and link with any Win32 function (clipboard, ime). 42 | //#define IMGUI_ENABLE_OSX_DEFAULT_CLIPBOARD_FUNCTIONS // [OSX] Implement default OSX clipboard handler (need to link with '-framework ApplicationServices', this is why this is not the default). 43 | //#define IMGUI_DISABLE_DEFAULT_FORMAT_FUNCTIONS // Don't implement ImFormatString/ImFormatStringV so you can implement them yourself (e.g. if you don't want to link with vsnprintf) 44 | //#define IMGUI_DISABLE_DEFAULT_MATH_FUNCTIONS // Don't implement ImFabs/ImSqrt/ImPow/ImFmod/ImCos/ImSin/ImAcos/ImAtan2 so you can implement them yourself. 45 | //#define IMGUI_DISABLE_FILE_FUNCTIONS // Don't implement ImFileOpen/ImFileClose/ImFileRead/ImFileWrite and ImFileHandle at all (replace them with dummies) 46 | //#define IMGUI_DISABLE_DEFAULT_FILE_FUNCTIONS // Don't implement ImFileOpen/ImFileClose/ImFileRead/ImFileWrite and ImFileHandle so you can implement them yourself if you don't want to link with fopen/fclose/fread/fwrite. This will also disable the LogToTTY() function. 47 | //#define IMGUI_DISABLE_DEFAULT_ALLOCATORS // Don't implement default allocators calling malloc()/free() to avoid linking with them. You will need to call ImGui::SetAllocatorFunctions(). 48 | 49 | //---- Include imgui_user.h at the end of imgui.h as a convenience 50 | //#define IMGUI_INCLUDE_IMGUI_USER_H 51 | 52 | //---- Pack colors to BGRA8 instead of RGBA8 (to avoid converting from one to another) 53 | //#define IMGUI_USE_BGRA_PACKED_COLOR 54 | 55 | //---- Use 32-bit for ImWchar (default is 16-bit) to support unicode planes 1-16. (e.g. point beyond 0xFFFF like emoticons, dingbats, symbols, shapes, ancient languages, etc...) 56 | //#define IMGUI_USE_WCHAR32 57 | 58 | //---- Avoid multiple STB libraries implementations, or redefine path/filenames to prioritize another version 59 | // By default the embedded implementations are declared static and not available outside of Dear ImGui sources files. 60 | //#define IMGUI_STB_TRUETYPE_FILENAME "my_folder/stb_truetype.h" 61 | //#define IMGUI_STB_RECT_PACK_FILENAME "my_folder/stb_rect_pack.h" 62 | //#define IMGUI_DISABLE_STB_TRUETYPE_IMPLEMENTATION 63 | //#define IMGUI_DISABLE_STB_RECT_PACK_IMPLEMENTATION 64 | 65 | //---- Use stb_printf's faster implementation of vsnprintf instead of the one from libc (unless IMGUI_DISABLE_DEFAULT_FORMAT_FUNCTIONS is defined) 66 | // Requires 'stb_sprintf.h' to be available in the include path. Compatibility checks of arguments and formats done by clang and GCC will be disabled in order to support the extra formats provided by STB sprintf. 67 | // #define IMGUI_USE_STB_SPRINTF 68 | 69 | //---- Use FreeType to build and rasterize the font atlas (instead of stb_truetype which is embedded by default in Dear ImGui) 70 | // Requires FreeType headers to be available in the include path. Requires program to be compiled with 'misc/freetype/imgui_freetype.cpp' (in this repository) + the FreeType library (not provided). 71 | // On Windows you may use vcpkg with 'vcpkg install freetype' + 'vcpkg integrate install'. 72 | //#define IMGUI_ENABLE_FREETYPE 73 | 74 | //---- Use stb_truetype to build and rasterize the font atlas (default) 75 | // The only purpose of this define is if you want force compilation of the stb_truetype backend ALONG with the FreeType backend. 76 | //#define IMGUI_ENABLE_STB_TRUETYPE 77 | 78 | //---- Define constructor and implicit cast operators to convert back<>forth between your math types and ImVec2/ImVec4. 79 | // This will be inlined as part of ImVec2 and ImVec4 class declarations. 80 | /* 81 | #define IM_VEC2_CLASS_EXTRA \ 82 | ImVec2(const MyVec2& f) { x = f.x; y = f.y; } \ 83 | operator MyVec2() const { return MyVec2(x,y); } 84 | 85 | #define IM_VEC4_CLASS_EXTRA \ 86 | ImVec4(const MyVec4& f) { x = f.x; y = f.y; z = f.z; w = f.w; } \ 87 | operator MyVec4() const { return MyVec4(x,y,z,w); } 88 | */ 89 | 90 | //---- Use 32-bit vertex indices (default is 16-bit) is one way to allow large meshes with more than 64K vertices. 91 | // Your renderer backend will need to support it (most example renderer backends support both 16/32-bit indices). 92 | // Another way to allow large meshes while keeping 16-bit indices is to handle ImDrawCmd::VtxOffset in your renderer. 93 | // Read about ImGuiBackendFlags_RendererHasVtxOffset for details. 94 | //#define ImDrawIdx unsigned int 95 | 96 | //---- Override ImDrawCallback signature (will need to modify renderer backends accordingly) 97 | //struct ImDrawList; 98 | //struct ImDrawCmd; 99 | //typedef void (*MyImDrawCallback)(const ImDrawList* draw_list, const ImDrawCmd* cmd, void* my_renderer_user_data); 100 | //#define ImDrawCallback MyImDrawCallback 101 | 102 | //---- Debug Tools: Macro to break in Debugger 103 | // (use 'Metrics->Tools->Item Picker' to pick widgets with the mouse and break into them for easy debugging.) 104 | //#define IM_DEBUG_BREAK IM_ASSERT(0) 105 | //#define IM_DEBUG_BREAK __debugbreak() 106 | 107 | //---- Debug Tools: Have the Item Picker break in the ItemAdd() function instead of ItemHoverable(), 108 | // (which comes earlier in the code, will catch a few extra items, allow picking items other than Hovered one.) 109 | // This adds a small runtime cost which is why it is not enabled by default. 110 | //#define IMGUI_DEBUG_TOOL_ITEM_PICKER_EX 111 | 112 | //---- Debug Tools: Enable slower asserts 113 | //#define IMGUI_DEBUG_PARANOID 114 | 115 | //---- Tip: You can add extra functions within the ImGui:: namespace, here or in your own headers files. 116 | /* 117 | namespace ImGui 118 | { 119 | void MyFunction(const char* name, const MyMatrix44& v); 120 | } 121 | */ 122 | -------------------------------------------------------------------------------- /source/imgui/imgui_impl_dx11.h: -------------------------------------------------------------------------------- 1 | // dear imgui: Renderer Backend for DirectX11 2 | // This needs to be used along with a Platform Backend (e.g. Win32) 3 | 4 | // Implemented features: 5 | // [X] Renderer: User texture binding. Use 'ID3D11ShaderResourceView*' as ImTextureID. Read the FAQ about ImTextureID! 6 | // [X] Renderer: Support for large meshes (64k+ vertices) with 16-bit indices. 7 | 8 | // You can copy and use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this. 9 | // If you are new to Dear ImGui, read documentation from the docs/ folder + read the top of imgui.cpp. 10 | // Read online: https://github.com/ocornut/imgui/tree/master/docs 11 | 12 | #pragma once 13 | #include "imgui.h" // IMGUI_IMPL_API 14 | 15 | struct ID3D11Device; 16 | struct ID3D11DeviceContext; 17 | 18 | IMGUI_IMPL_API bool ImGui_ImplDX11_Init(ID3D11Device* device, ID3D11DeviceContext* device_context); 19 | IMGUI_IMPL_API void ImGui_ImplDX11_Shutdown(); 20 | IMGUI_IMPL_API void ImGui_ImplDX11_NewFrame(); 21 | IMGUI_IMPL_API void ImGui_ImplDX11_RenderDrawData(ImDrawData* draw_data); 22 | 23 | // Use if you want to reset your rendering device without losing Dear ImGui state. 24 | IMGUI_IMPL_API void ImGui_ImplDX11_InvalidateDeviceObjects(); 25 | IMGUI_IMPL_API bool ImGui_ImplDX11_CreateDeviceObjects(); 26 | -------------------------------------------------------------------------------- /source/imgui/imgui_impl_win32.h: -------------------------------------------------------------------------------- 1 | // dear imgui: Platform Backend for Windows (standard windows API for 32 and 64 bits applications) 2 | // This needs to be used along with a Renderer (e.g. DirectX11, OpenGL3, Vulkan..) 3 | 4 | // Implemented features: 5 | // [X] Platform: Clipboard support (for Win32 this is actually part of core dear imgui) 6 | // [X] Platform: Mouse cursor shape and visibility. Disable with 'io.ConfigFlags |= ImGuiConfigFlags_NoMouseCursorChange'. 7 | // [X] Platform: Keyboard arrays indexed using VK_* Virtual Key Codes, e.g. ImGui::IsKeyPressed(VK_SPACE). 8 | // [X] Platform: Gamepad support. Enabled with 'io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad'. 9 | 10 | // You can copy and use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this. 11 | // If you are new to Dear ImGui, read documentation from the docs/ folder + read the top of imgui.cpp. 12 | // Read online: https://github.com/ocornut/imgui/tree/master/docs 13 | 14 | #pragma once 15 | #include "imgui.h" // IMGUI_IMPL_API 16 | 17 | IMGUI_IMPL_API bool ImGui_ImplWin32_Init(void* hwnd); 18 | IMGUI_IMPL_API void ImGui_ImplWin32_Shutdown(); 19 | IMGUI_IMPL_API void ImGui_ImplWin32_NewFrame(); 20 | 21 | // Win32 message handler your application need to call. 22 | // - Intentionally commented out in a '#if 0' block to avoid dragging dependencies on from this helper. 23 | // - You should COPY the line below into your .cpp code to forward declare the function and then you can call it. 24 | #if 0 25 | extern IMGUI_IMPL_API LRESULT ImGui_ImplWin32_WndProcHandler(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam); 26 | #endif 27 | 28 | // DPI-related helpers (optional) 29 | // - Use to enable DPI awareness without having to create an application manifest. 30 | // - Your own app may already do this via a manifest or explicit calls. This is mostly useful for our examples/ apps. 31 | // - In theory we could call simple functions from Windows SDK such as SetProcessDPIAware(), SetProcessDpiAwareness(), etc. 32 | // but most of the functions provided by Microsoft require Windows 8.1/10+ SDK at compile time and Windows 8/10+ at runtime, 33 | // neither we want to require the user to have. So we dynamically select and load those functions to avoid dependencies. 34 | IMGUI_IMPL_API void ImGui_ImplWin32_EnableDpiAwareness(); 35 | IMGUI_IMPL_API float ImGui_ImplWin32_GetDpiScaleForHwnd(void* hwnd); // HWND hwnd 36 | IMGUI_IMPL_API float ImGui_ImplWin32_GetDpiScaleForMonitor(void* monitor); // HMONITOR monitor 37 | 38 | // Transparency related helpers (optional) [experimental] 39 | // - Use to enable alpha compositing transparency with the desktop. 40 | // - Use together with e.g. clearing your framebuffer with zero-alpha. 41 | IMGUI_IMPL_API void ImGui_ImplWin32_EnableAlphaCompositing(void* hwnd); // HWND hwnd 42 | -------------------------------------------------------------------------------- /source/pxd_shader.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | struct matrix 7 | { 8 | __m128 m_vm0; 9 | __m128 m_vm1; 10 | __m128 m_vm2; 11 | __m128 m_vm3; 12 | }; 13 | 14 | struct alignas(16) pxd_shader_bool_t 15 | { 16 | unsigned int c_bool; 17 | }; 18 | 19 | struct pxd_shader_cb12_t 20 | { 21 | matrix c_matP; 22 | matrix c_matV; 23 | matrix c_matVP; 24 | matrix c_matT[4]; 25 | __m128 c_rvtx_param; 26 | __m128 c_rt_size; 27 | __m128 c_location; 28 | }; 29 | 30 | struct pxd_shader_cb10b_t 31 | { 32 | __m128 c_matW[3]; 33 | }; 34 | 35 | struct pxd_shader_cb10_t 36 | { 37 | __m128 c_matTBL[256][3]; 38 | }; 39 | 40 | struct pxd_shader_sys_material_t 41 | { 42 | union 43 | { 44 | __m128 diffuse; 45 | union 46 | { 47 | float diffuse_r; 48 | float diffuse_g; 49 | float diffuse_b; 50 | float diffuse_a; 51 | } comps; 52 | } diffuse; 53 | union 54 | { 55 | __m128 specular; 56 | union 57 | { 58 | float specular_r; 59 | float specular_g; 60 | float specular_b; 61 | float specular_power; 62 | } comps; 63 | } specular; 64 | union 65 | { 66 | __m128 emissive_ambient; 67 | union 68 | { 69 | float emissive_r; 70 | float emissive_g; 71 | float emissive_b; 72 | float ambient; 73 | } comps; 74 | } emissive_ambient; 75 | __m128 scene_ambient; 76 | __m128 scene_light_vector[2]; 77 | __m128 scene_light_color[2]; 78 | }; 79 | 80 | struct pxd_shader_material_anim_t 81 | { 82 | union 83 | { 84 | union 85 | { 86 | float uv_offset[3][2]; 87 | float blend_scale; 88 | float opacity_scale; 89 | } comps; 90 | __m128 v[2]; 91 | uint64_t u[4]; 92 | } anim; 93 | }; 94 | 95 | struct pxd_shader_material_modify_base_t 96 | { 97 | union 98 | { 99 | union 100 | { 101 | float diffuse_scale[3]; 102 | float global_opacity; 103 | float specular_scale[3]; 104 | float emissive_scale; 105 | } comps; 106 | __m128 v[2]; 107 | uint64_t u[4]; 108 | } modify; 109 | }; 110 | 111 | struct pxd_shader_material_modify_t : public pxd_shader_material_modify_base_t 112 | { 113 | float saturation; 114 | unsigned int palette0; 115 | unsigned int palette1; 116 | float _reserve0; 117 | }; 118 | 119 | struct pxd_shader_user_t 120 | { 121 | __m128 c_user_param[32]; 122 | }; 123 | 124 | struct uniform_struct_base_t 125 | { 126 | pxd_shader_bool_t m_data_bool_vs; 127 | pxd_shader_bool_t m_data_bool_ps; 128 | pxd_shader_bool_t m_data_bool_gs; 129 | pxd_shader_bool_t m_data_bool_hs; 130 | pxd_shader_bool_t m_data_bool_ds; 131 | pxd_shader_bool_t m_data_bool_cs; 132 | pxd_shader_cb12_t m_data_cb12; 133 | union 134 | { 135 | pxd_shader_cb10b_t m_data_cb10b; 136 | pxd_shader_cb10_t m_data_cb10; 137 | } cb10; 138 | pxd_shader_sys_material_t m_data_sys_material; 139 | pxd_shader_material_anim_t m_data_material_anim; 140 | pxd_shader_material_modify_t m_data_material_modify; 141 | pxd_shader_user_t m_data_user_vs; 142 | pxd_shader_user_t m_data_user_ps; 143 | pxd_shader_user_t m_data_user_gs; 144 | pxd_shader_user_t m_data_user_hs; 145 | pxd_shader_user_t m_data_user_ds; 146 | pxd_shader_user_t m_data_user_cs; 147 | }; -------------------------------------------------------------------------------- /source/pxd_types.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | // Types that are (hopefully) identical across all games 7 | 8 | struct spinlock_t 9 | { 10 | volatile unsigned int m_lock_status = 0; 11 | }; 12 | 13 | struct rwspinlock_t 14 | { 15 | volatile unsigned int m_lock_status = 0; 16 | }; 17 | 18 | struct recursive_rwspinlock_t 19 | { 20 | union lock_status_t 21 | { 22 | volatile unsigned int status; 23 | struct 24 | { 25 | uint32_t ref_count : 16; 26 | uint32_t thread_sid : 16; 27 | } s; 28 | }; 29 | 30 | volatile lock_status_t m_lock_status {}; 31 | }; 32 | 33 | namespace sl { 34 | 35 | struct alignas(16) mutex_t 36 | { 37 | CRITICAL_SECTION m_cs; 38 | 39 | mutex_t(); 40 | ~mutex_t(); 41 | 42 | void lock(); 43 | void unlock(); 44 | }; 45 | 46 | void mutex_construct(mutex_t& mutex); 47 | void mutex_destruct(mutex_t& mutex); 48 | 49 | void spinlock_lock(spinlock_t& spinlock); 50 | void spinlock_unlock(spinlock_t& spinlock); 51 | 52 | void rwspinlock_wlock(rwspinlock_t& spinlock); 53 | void rwspinlock_wunlock(rwspinlock_t& spinlock); 54 | void rwspinlock_rlock(rwspinlock_t& spinlock); 55 | void rwspinlock_runlock(rwspinlock_t& spinlock); 56 | 57 | enum FILE_SEEK 58 | { 59 | FILE_SEEK_SET = 0x0, 60 | FILE_SEEK_CURRENT = 0x1, 61 | FILE_SEEK_FORCE_U32 = 0xFFFFFFFF, 62 | }; 63 | 64 | enum FILE_ASYNC_METHOD 65 | { 66 | FILE_ASYNC_METHOD_OPEN = 0x0, 67 | FILE_ASYNC_METHOD_CREATE = 0x1, 68 | FILE_ASYNC_METHOD_CLOSE = 0x2, 69 | FILE_ASYNC_METHOD_READ = 0x3, 70 | FILE_ASYNC_METHOD_WRITE = 0x4, 71 | FILE_ASYNC_METHOD_PRELOAD = 0x5, 72 | FILE_ASYNC_METHOD_NUM = 0x6, 73 | FILE_ASYNC_METHOD_INVALID = 0xFFFFFFFF, 74 | }; 75 | 76 | enum ARCHIVE_FIND_RESULT 77 | { 78 | ARCHIVE_FIND_FILE_FOUND = 0x0, 79 | ARCHIVE_FIND_DIRECTORY_FOUND = 0x1, 80 | ARCHIVE_FIND_NOT_FOUND = 0xFFFFFFFF, 81 | }; 82 | 83 | 84 | struct handle_internal_t 85 | { 86 | union 87 | { 88 | uint64_t m_qhandle; 89 | struct 90 | { 91 | uint64_t m_serial : 12; 92 | uint64_t m_ptr : 44; 93 | uint64_t m_type : 8; 94 | } data; 95 | } intern; 96 | }; 97 | 98 | struct handle_t 99 | { 100 | union 101 | { 102 | struct 103 | { 104 | uint32_t m_bank : 20; 105 | uint32_t m_serial : 12; 106 | } data; 107 | uint32_t m_handle = 0; 108 | } h; 109 | 110 | bool operator==(const handle_t& handle) const { return h.m_handle == handle.h.m_handle; } 111 | }; 112 | inline constexpr handle_t INVALID_HANDLE {}; 113 | 114 | struct alignas(8) file_handle_t 115 | { 116 | char m_user_work[48]; 117 | void *m_h_native; 118 | volatile uint64_t m_file_pointer; 119 | handle_t m_handle; 120 | char m_file_path[1040]; 121 | }; 122 | static_assert(sizeof(file_handle_t) == 1112); 123 | 124 | } 125 | 126 | template 127 | struct t_locked_queue_node 128 | { 129 | t_locked_queue_node *volatile mp_next = nullptr; 130 | }; 131 | 132 | template 133 | class alignas(16) t_locked_queue 134 | { 135 | public: 136 | void enqueue(T* p) 137 | { 138 | sl::spinlock_lock(m_sync); 139 | 140 | m_size++; 141 | p->m_node.mp_next = nullptr; 142 | if (this->mp_head != nullptr) 143 | mp_tail->mp_next = &p->m_node; 144 | else 145 | this->mp_head = &p->m_node; 146 | this->mp_tail = &p->m_node; 147 | 148 | sl::spinlock_unlock(m_sync); 149 | } 150 | 151 | T* dequeue() 152 | { 153 | T* item = nullptr; 154 | sl::spinlock_lock(m_sync); 155 | t_locked_queue_node* head = this->mp_head; 156 | if (head != nullptr) 157 | { 158 | m_size--; 159 | this->mp_head = head->mp_next; 160 | if (this->mp_tail == head) 161 | this->mp_tail = nullptr; 162 | head->mp_next = nullptr; 163 | item = reinterpret_cast(reinterpret_cast(head) - offsetof(T, m_node)); 164 | } 165 | sl::spinlock_unlock(m_sync); 166 | return item; 167 | } 168 | 169 | private: 170 | t_locked_queue_node *mp_head = nullptr; 171 | t_locked_queue_node *mp_tail = nullptr; 172 | spinlock_t m_sync; 173 | uint32_t m_size = 0; 174 | }; 175 | 176 | template 177 | class t_fixed_deque 178 | { 179 | public: 180 | void push_back(const T* p) 181 | { 182 | assert(m_element_size < m_deque_size); 183 | mp_element[m_index_end] = *p; 184 | m_element_size++; 185 | m_index_end = m_index_end != m_deque_size ? m_index_end + 1 : 0; 186 | } 187 | 188 | void reserve(unsigned int size) 189 | { 190 | assert(mp_element == nullptr); 191 | mp_element = new T[size + 1]; 192 | m_deque_size = size; 193 | } 194 | 195 | private: 196 | T *mp_element = nullptr; 197 | unsigned int m_deque_size = 0; 198 | unsigned int m_element_size = 0; 199 | unsigned int m_index_begin = 0; 200 | unsigned int m_index_end = 0; 201 | }; 202 | 203 | template 204 | class t_pointer_list 205 | { 206 | public: 207 | void push_front(T* p_data) 208 | { 209 | T* top = mp_top; 210 | if (top != nullptr) 211 | { 212 | // TODO: Cases like this might be why a "linker" class is needed? 213 | // Right now, the sector cache needs to be public for this to work 214 | top->m_prev = static_castm_prev)>(p_data - top); 215 | } 216 | else 217 | { 218 | mp_bottom = p_data; 219 | } 220 | p_data->m_next = top != nullptr ? static_castm_next)>(top - p_data) : 0; 221 | p_data->m_prev = 0; 222 | mp_top = p_data; 223 | } 224 | 225 | void push_back(T* p_data) 226 | { 227 | if (mp_top == nullptr) 228 | { 229 | push_front(p_data); 230 | return; 231 | } 232 | mp_bottom->m_next = static_castm_next)>(p_data - mp_bottom); 233 | p_data->m_prev = static_castm_prev)>(mp_bottom - p_data); 234 | p_data->m_next = 0; 235 | mp_bottom = p_data; 236 | } 237 | 238 | private: 239 | T* mp_top = nullptr; 240 | T* mp_bottom = nullptr; 241 | }; 242 | 243 | template 244 | struct t_status_ptr 245 | { 246 | union 247 | { 248 | struct 249 | { 250 | int64_t m_ptr : 48; 251 | uint64_t m_status : 16; 252 | } p; 253 | uint64_t m_status_ptr; 254 | } v; 255 | }; 256 | 257 | template 258 | struct t_lockfree_ptr 259 | { 260 | struct counter_ptr_t 261 | { 262 | union 263 | { 264 | struct 265 | { 266 | int64_t m_ptr : 48; 267 | int64_t m_counter : 16; 268 | } p; 269 | uint64_t m_quad_ptr; 270 | } val; 271 | }; 272 | 273 | counter_ptr_t m_counter_ptr; 274 | }; 275 | 276 | template 277 | class t_lockfree_stack 278 | { 279 | public: 280 | void push(T* p) 281 | { 282 | auto desired = m_top.m_counter_ptr.val; 283 | decltype(desired) exchanged; 284 | p->mp_link = reinterpret_cast(desired.p.m_ptr); 285 | exchanged.p.m_ptr = reinterpret_cast(p); 286 | exchanged.p.m_counter = desired.p.m_counter + 1; 287 | while (desired.m_quad_ptr != InterlockedCompareExchange(&m_top.m_counter_ptr.val.m_quad_ptr, exchanged.m_quad_ptr, 288 | desired.m_quad_ptr)) 289 | { 290 | _mm_pause(); 291 | desired = m_top.m_counter_ptr.val; 292 | p->mp_link = reinterpret_cast(desired.p.m_ptr); 293 | exchanged.p.m_ptr = reinterpret_cast(p); 294 | exchanged.p.m_counter = desired.p.m_counter + 1; 295 | } 296 | } 297 | 298 | T* pop() 299 | { 300 | auto desired = m_top.m_counter_ptr.val; 301 | T* result = reinterpret_cast(desired.p.m_ptr); 302 | if (result == nullptr) return nullptr; 303 | 304 | decltype(desired) exchanged; 305 | exchanged.p.m_ptr = reinterpret_cast(result->mp_link); 306 | exchanged.p.m_counter = desired.p.m_counter + 1; 307 | do 308 | { 309 | if (desired.m_quad_ptr == InterlockedCompareExchange(&m_top.m_counter_ptr.val.m_quad_ptr, exchanged.m_quad_ptr, 310 | desired.m_quad_ptr)) 311 | { 312 | break; 313 | } 314 | 315 | desired = m_top.m_counter_ptr.val; 316 | result = reinterpret_cast(desired.p.m_ptr); 317 | exchanged.p.m_ptr = reinterpret_cast(result->mp_link); 318 | exchanged.p.m_counter = desired.p.m_counter + 1; 319 | } 320 | while (result != nullptr); 321 | return result; 322 | } 323 | 324 | private: 325 | t_lockfree_ptr m_top; 326 | }; 327 | 328 | template 329 | class t_instance_tbl 330 | { 331 | public: 332 | void initialize(char* space, unsigned int size) 333 | { 334 | m_instance_max = size; 335 | m_free_top = 0; 336 | m_free_tbl_size = (size + 63) / 64; 337 | if (space != nullptr) 338 | { 339 | m_status |= 1; 340 | mpp_instance_tbl = reinterpret_cast(space); 341 | mp_free_tbl = reinterpret_cast(space + sizeof(mpp_instance_tbl[0]) * size); 342 | } 343 | else 344 | { 345 | mpp_instance_tbl = new T*[size]; 346 | mp_free_tbl = new uint64_t[size]; 347 | } 348 | std::fill_n(mp_free_tbl, m_free_tbl_size, uint64_t(-1)); 349 | std::fill_n(mpp_instance_tbl, m_instance_max, nullptr); 350 | } 351 | 352 | T* get(unsigned int id) const 353 | { 354 | assert(id >= 1); 355 | const unsigned int index = id - 1; 356 | return index < m_instance_max ? mpp_instance_tbl[index] : nullptr; 357 | } 358 | 359 | private: 360 | T** mpp_instance_tbl; 361 | uint64_t* mp_free_tbl; 362 | unsigned int m_status; 363 | unsigned int m_instance_max; 364 | unsigned int m_free_top; 365 | unsigned int m_free_tbl_size; 366 | }; 367 | 368 | template 369 | class t_avl_tree_node 370 | { 371 | 372 | private: 373 | t_status_ptr > mp_left; 374 | t_status_ptr > mp_right; 375 | }; 376 | 377 | struct csl_hash 378 | { 379 | union 380 | { 381 | struct 382 | { 383 | uint16_t m_hash_id; 384 | char m_sz_hash_name[30]; 385 | } h; 386 | uint64_t m_hash[4]; 387 | } hash {}; 388 | }; -------------------------------------------------------------------------------- /source/resources/VersionInfo.rc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CookiePLMonster/YAMP/45c0d9ae273096679833d540a3714399c8a1d6e0/source/resources/VersionInfo.rc -------------------------------------------------------------------------------- /source/sl_internal.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | namespace sl { 4 | 5 | struct handle_internal_buffer_t 6 | { 7 | t_locked_queue_node m_node; 8 | }; 9 | 10 | } -------------------------------------------------------------------------------- /source/wil/cppwinrt.h: -------------------------------------------------------------------------------- 1 | //********************************************************* 2 | // 3 | // Copyright (c) Microsoft. All rights reserved. 4 | // This code is licensed under the MIT License. 5 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF 6 | // ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED 7 | // TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 8 | // PARTICULAR PURPOSE AND NONINFRINGEMENT. 9 | // 10 | //********************************************************* 11 | #ifndef __WIL_CPPWINRT_INCLUDED 12 | #define __WIL_CPPWINRT_INCLUDED 13 | 14 | #include "common.h" 15 | #include 16 | #include 17 | #include 18 | #include 19 | 20 | // WIL and C++/WinRT use two different exception types for communicating HRESULT failures. Thus, both libraries need to 21 | // understand how to translate these exception types into the correct HRESULT values at the ABI boundary. Prior to 22 | // C++/WinRT "2.0" this was accomplished by injecting the WINRT_EXTERNAL_CATCH_CLAUSE macro - that WIL defines below - 23 | // into its exception handler (winrt::to_hresult). Starting with C++/WinRT "2.0" this mechanism has shifted to a global 24 | // function pointer - winrt_to_hresult_handler - that WIL sets automatically when this header is included and 25 | // 'CPPWINRT_SUPPRESS_STATIC_INITIALIZERS' is not defined. 26 | 27 | /// @cond 28 | namespace wil::details 29 | { 30 | // Since the C++/WinRT version macro is a string... 31 | // For example: "2.0.210122.3" 32 | inline constexpr int version_from_string(const char* versionString) 33 | { 34 | int result = 0; 35 | auto str = versionString; 36 | while ((*str >= '0') && (*str <= '9')) 37 | { 38 | result = result * 10 + (*str - '0'); 39 | ++str; 40 | } 41 | 42 | return result; 43 | } 44 | 45 | inline constexpr int major_version_from_string(const char* versionString) 46 | { 47 | return version_from_string(versionString); 48 | } 49 | 50 | inline constexpr int minor_version_from_string(const char* versionString) 51 | { 52 | auto str = versionString; 53 | int dotCount = 0; 54 | while ((*str != '\0')) 55 | { 56 | if (*str == '.') 57 | { 58 | ++dotCount; 59 | } 60 | 61 | ++str; 62 | if (dotCount == 2) 63 | { 64 | break; 65 | } 66 | } 67 | 68 | if (*str == '\0') 69 | { 70 | return 0; 71 | } 72 | 73 | return version_from_string(str); 74 | } 75 | } 76 | /// @endcond 77 | 78 | #ifdef CPPWINRT_VERSION 79 | // Prior to C++/WinRT "2.0" this header needed to be included before 'winrt/base.h' so that our definition of 80 | // 'WINRT_EXTERNAL_CATCH_CLAUSE' would get picked up in the implementation of 'winrt::to_hresult'. This is no longer 81 | // problematic, so only emit an error when using a version of C++/WinRT prior to 2.0 82 | static_assert(::wil::details::major_version_from_string(CPPWINRT_VERSION) >= 2, 83 | "Please include wil/cppwinrt.h before including any C++/WinRT headers"); 84 | #endif 85 | 86 | // NOTE: Will eventually be removed once C++/WinRT 2.0 use can be assumed 87 | #ifdef WINRT_EXTERNAL_CATCH_CLAUSE 88 | #define __WI_CONFLICTING_WINRT_EXTERNAL_CATCH_CLAUSE 1 89 | #else 90 | #define WINRT_EXTERNAL_CATCH_CLAUSE \ 91 | catch (const wil::ResultException& e) \ 92 | { \ 93 | return winrt::hresult_error(e.GetErrorCode(), winrt::to_hstring(e.what())).to_abi(); \ 94 | } 95 | #endif 96 | 97 | #include "result_macros.h" 98 | #include 99 | 100 | #if __WI_CONFLICTING_WINRT_EXTERNAL_CATCH_CLAUSE 101 | static_assert(::wil::details::major_version_from_string(CPPWINRT_VERSION) >= 2, 102 | "C++/WinRT external catch clause already defined outside of WIL"); 103 | #endif 104 | 105 | // In C++/WinRT 2.0 and beyond, this function pointer exists. In earlier versions it does not. It's much easier to avoid 106 | // linker errors than it is to SFINAE on variable existence, so we declare the variable here, but are careful not to 107 | // use it unless the version of C++/WinRT is high enough 108 | extern std::int32_t(__stdcall* winrt_to_hresult_handler)(void*) noexcept; 109 | 110 | // The same is true with this function pointer as well, except that the version must be 2.X or higher. 111 | extern void(__stdcall* winrt_throw_hresult_handler)(uint32_t, char const*, char const*, void*, winrt::hresult const) noexcept; 112 | 113 | /// @cond 114 | namespace wil::details 115 | { 116 | inline void MaybeGetExceptionString( 117 | const winrt::hresult_error& exception, 118 | _Out_writes_opt_(debugStringChars) PWSTR debugString, 119 | _When_(debugString != nullptr, _Pre_satisfies_(debugStringChars > 0)) size_t debugStringChars) 120 | { 121 | if (debugString) 122 | { 123 | StringCchPrintfW(debugString, debugStringChars, L"winrt::hresult_error: %ls", exception.message().c_str()); 124 | } 125 | } 126 | 127 | inline HRESULT __stdcall ResultFromCaughtException_CppWinRt( 128 | _Inout_updates_opt_(debugStringChars) PWSTR debugString, 129 | _When_(debugString != nullptr, _Pre_satisfies_(debugStringChars > 0)) size_t debugStringChars, 130 | _Inout_ bool* isNormalized) noexcept 131 | { 132 | if (g_pfnResultFromCaughtException) 133 | { 134 | try 135 | { 136 | throw; 137 | } 138 | catch (const ResultException& exception) 139 | { 140 | *isNormalized = true; 141 | MaybeGetExceptionString(exception, debugString, debugStringChars); 142 | return exception.GetErrorCode(); 143 | } 144 | catch (const winrt::hresult_error& exception) 145 | { 146 | MaybeGetExceptionString(exception, debugString, debugStringChars); 147 | return exception.to_abi(); 148 | } 149 | catch (const std::bad_alloc& exception) 150 | { 151 | MaybeGetExceptionString(exception, debugString, debugStringChars); 152 | return E_OUTOFMEMORY; 153 | } 154 | catch (const std::out_of_range& exception) 155 | { 156 | MaybeGetExceptionString(exception, debugString, debugStringChars); 157 | return E_BOUNDS; 158 | } 159 | catch (const std::invalid_argument& exception) 160 | { 161 | MaybeGetExceptionString(exception, debugString, debugStringChars); 162 | return E_INVALIDARG; 163 | } 164 | catch (...) 165 | { 166 | auto hr = RecognizeCaughtExceptionFromCallback(debugString, debugStringChars); 167 | if (FAILED(hr)) 168 | { 169 | return hr; 170 | } 171 | } 172 | } 173 | else 174 | { 175 | try 176 | { 177 | throw; 178 | } 179 | catch (const ResultException& exception) 180 | { 181 | *isNormalized = true; 182 | MaybeGetExceptionString(exception, debugString, debugStringChars); 183 | return exception.GetErrorCode(); 184 | } 185 | catch (const winrt::hresult_error& exception) 186 | { 187 | MaybeGetExceptionString(exception, debugString, debugStringChars); 188 | return exception.to_abi(); 189 | } 190 | catch (const std::bad_alloc& exception) 191 | { 192 | MaybeGetExceptionString(exception, debugString, debugStringChars); 193 | return E_OUTOFMEMORY; 194 | } 195 | catch (const std::out_of_range& exception) 196 | { 197 | MaybeGetExceptionString(exception, debugString, debugStringChars); 198 | return E_BOUNDS; 199 | } 200 | catch (const std::invalid_argument& exception) 201 | { 202 | MaybeGetExceptionString(exception, debugString, debugStringChars); 203 | return E_INVALIDARG; 204 | } 205 | catch (const std::exception& exception) 206 | { 207 | MaybeGetExceptionString(exception, debugString, debugStringChars); 208 | return HRESULT_FROM_WIN32(ERROR_UNHANDLED_EXCEPTION); 209 | } 210 | catch (...) 211 | { 212 | // Fall through to returning 'S_OK' below 213 | } 214 | } 215 | 216 | // Tell the caller that we were unable to map the exception by succeeding... 217 | return S_OK; 218 | } 219 | } 220 | /// @endcond 221 | 222 | namespace wil 223 | { 224 | inline std::int32_t __stdcall winrt_to_hresult(void* returnAddress) noexcept 225 | { 226 | // C++/WinRT only gives us the return address (caller), so pass along an empty 'DiagnosticsInfo' since we don't 227 | // have accurate file/line/etc. information 228 | return static_cast(details::ReportFailure_CaughtException(__R_DIAGNOSTICS_RA(DiagnosticsInfo{}, returnAddress))); 229 | } 230 | 231 | inline void __stdcall winrt_throw_hresult(uint32_t lineNumber, char const* fileName, char const* functionName, void* returnAddress, winrt::hresult const result) noexcept 232 | { 233 | void* callerReturnAddress{nullptr}; PCSTR code{nullptr}; 234 | wil::details::ReportFailure_Hr(__R_FN_CALL_FULL __R_COMMA result); 235 | } 236 | 237 | inline void WilInitialize_CppWinRT() 238 | { 239 | details::g_pfnResultFromCaughtException_CppWinRt = details::ResultFromCaughtException_CppWinRt; 240 | if constexpr (details::major_version_from_string(CPPWINRT_VERSION) >= 2) 241 | { 242 | WI_ASSERT(winrt_to_hresult_handler == nullptr); 243 | winrt_to_hresult_handler = winrt_to_hresult; 244 | } 245 | 246 | if constexpr ((details::major_version_from_string(CPPWINRT_VERSION) >= 2) && (details::minor_version_from_string(CPPWINRT_VERSION) >= 210122)) 247 | { 248 | WI_ASSERT(winrt_throw_hresult_handler == nullptr); 249 | winrt_throw_hresult_handler = winrt_throw_hresult; 250 | } 251 | } 252 | 253 | /// @cond 254 | namespace details 255 | { 256 | #ifndef CPPWINRT_SUPPRESS_STATIC_INITIALIZERS 257 | WI_ODR_PRAGMA("CPPWINRT_SUPPRESS_STATIC_INITIALIZERS", "0") 258 | WI_HEADER_INITITALIZATION_FUNCTION(WilInitialize_CppWinRT, [] 259 | { 260 | ::wil::WilInitialize_CppWinRT(); 261 | return 1; 262 | }); 263 | #else 264 | WI_ODR_PRAGMA("CPPWINRT_SUPPRESS_STATIC_INITIALIZERS", "1") 265 | #endif 266 | } 267 | /// @endcond 268 | 269 | // Provides an overload of verify_hresult so that the WIL macros can recognize winrt::hresult as a valid "hresult" type. 270 | inline long verify_hresult(winrt::hresult hr) noexcept 271 | { 272 | return hr; 273 | } 274 | 275 | // Provides versions of get_abi and put_abi for genericity that directly use HSTRING for convenience. 276 | template 277 | auto get_abi(T const& object) noexcept 278 | { 279 | return winrt::get_abi(object); 280 | } 281 | 282 | inline auto get_abi(winrt::hstring const& object) noexcept 283 | { 284 | return static_cast(winrt::get_abi(object)); 285 | } 286 | 287 | template 288 | auto put_abi(T& object) noexcept 289 | { 290 | return winrt::put_abi(object); 291 | } 292 | 293 | inline auto put_abi(winrt::hstring& object) noexcept 294 | { 295 | return reinterpret_cast(winrt::put_abi(object)); 296 | } 297 | 298 | inline ::IUnknown* com_raw_ptr(const winrt::Windows::Foundation::IUnknown& ptr) noexcept 299 | { 300 | return static_cast<::IUnknown*>(winrt::get_abi(ptr)); 301 | } 302 | 303 | // Needed to power wil::cx_object_from_abi that requires IInspectable 304 | inline ::IInspectable* com_raw_ptr(const winrt::Windows::Foundation::IInspectable& ptr) noexcept 305 | { 306 | return static_cast<::IInspectable*>(winrt::get_abi(ptr)); 307 | } 308 | 309 | // Taken from the docs.microsoft.com article 310 | template 311 | T convert_from_abi(::IUnknown* from) 312 | { 313 | T to{ nullptr }; // `T` is a projected type. 314 | winrt::check_hresult(from->QueryInterface(winrt::guid_of(), winrt::put_abi(to))); 315 | return to; 316 | } 317 | } 318 | 319 | #endif // __WIL_CPPWINRT_INCLUDED 320 | -------------------------------------------------------------------------------- /source/wil/cppwinrt_wrl.h: -------------------------------------------------------------------------------- 1 | //********************************************************* 2 | // 3 | // Copyright (c) Microsoft. All rights reserved. 4 | // This code is licensed under the MIT License. 5 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF 6 | // ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED 7 | // TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 8 | // PARTICULAR PURPOSE AND NONINFRINGEMENT. 9 | // 10 | //********************************************************* 11 | #ifndef __WIL_CPPWINRT_WRL_INCLUDED 12 | #define __WIL_CPPWINRT_WRL_INCLUDED 13 | 14 | #include "cppwinrt.h" 15 | #include 16 | 17 | #include "result_macros.h" 18 | #include 19 | 20 | // wil::wrl_factory_for_winrt_com_class provides interopability between a 21 | // C++/WinRT class and the WRL Module system, allowing the winrt class to be 22 | // CoCreatable. 23 | // 24 | // Usage: 25 | // - In your cpp, add: 26 | // CoCreatableCppWinRtClass(className) 27 | // 28 | // - In the dll.cpp (or equivalent) for the module containing your class, add: 29 | // CoCreatableClassWrlCreatorMapInclude(className) 30 | // 31 | namespace wil 32 | { 33 | namespace details 34 | { 35 | template 36 | class module_count_wrapper : public TCppWinRTClass 37 | { 38 | public: 39 | module_count_wrapper() 40 | { 41 | if (auto modulePtr = ::Microsoft::WRL::GetModuleBase()) 42 | { 43 | modulePtr->IncrementObjectCount(); 44 | } 45 | } 46 | 47 | virtual ~module_count_wrapper() 48 | { 49 | if (auto modulePtr = ::Microsoft::WRL::GetModuleBase()) 50 | { 51 | modulePtr->DecrementObjectCount(); 52 | } 53 | } 54 | }; 55 | } 56 | 57 | template 58 | class wrl_factory_for_winrt_com_class : public ::Microsoft::WRL::ClassFactory<> 59 | { 60 | public: 61 | IFACEMETHODIMP CreateInstance(_In_opt_ ::IUnknown* unknownOuter, REFIID riid, _COM_Outptr_ void **object) noexcept try 62 | { 63 | *object = nullptr; 64 | RETURN_HR_IF(CLASS_E_NOAGGREGATION, unknownOuter != nullptr); 65 | 66 | return winrt::make>().as(riid, object); 67 | } 68 | CATCH_RETURN() 69 | }; 70 | } 71 | 72 | #define CoCreatableCppWinRtClass(className) CoCreatableClassWithFactory(className, ::wil::wrl_factory_for_winrt_com_class) 73 | 74 | #endif // __WIL_CPPWINRT_WRL_INCLUDED 75 | -------------------------------------------------------------------------------- /source/wil/nt_result_macros.h: -------------------------------------------------------------------------------- 1 | //********************************************************* 2 | // 3 | // Copyright (c) Microsoft. All rights reserved. 4 | // This code is licensed under the MIT License. 5 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF 6 | // ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED 7 | // TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 8 | // PARTICULAR PURPOSE AND NONINFRINGEMENT. 9 | // 10 | //********************************************************* 11 | #ifndef __WIL_NT_RESULTMACROS_INCLUDED 12 | #define __WIL_NT_RESULTMACROS_INCLUDED 13 | 14 | #include "result_macros.h" 15 | 16 | // Helpers for return macros 17 | #define __NT_RETURN_NTSTATUS(status, str) __WI_SUPPRESS_4127_S do { NTSTATUS __status = (status); if (FAILED_NTSTATUS(__status)) { __R_FN(Return_NtStatus)(__R_INFO(str) __status); } return __status; } while ((void)0, 0) 18 | #define __NT_RETURN_NTSTATUS_MSG(status, str, fmt, ...) __WI_SUPPRESS_4127_S do { NTSTATUS __status = (status); if (FAILED_NTSTATUS(__status)) { __R_FN(Return_NtStatusMsg)(__R_INFO(str) __status, fmt, ##__VA_ARGS__); } return __status; } while ((void)0, 0) 19 | 20 | //***************************************************************************** 21 | // Macros for returning failures as NTSTATUS 22 | //***************************************************************************** 23 | 24 | // Always returns a known result (NTSTATUS) - always logs failures 25 | #define NT_RETURN_NTSTATUS(status) __NT_RETURN_NTSTATUS(wil::verify_ntstatus(status), #status) 26 | 27 | // Always returns a known failure (NTSTATUS) - always logs a var-arg message on failure 28 | #define NT_RETURN_NTSTATUS_MSG(status, fmt, ...) __NT_RETURN_NTSTATUS_MSG(wil::verify_ntstatus(status), #status, fmt, ##__VA_ARGS__) 29 | 30 | // Conditionally returns failures (NTSTATUS) - always logs failures 31 | #define NT_RETURN_IF_NTSTATUS_FAILED(status) __WI_SUPPRESS_4127_S do { const auto __statusRet = wil::verify_ntstatus(status); if (FAILED_NTSTATUS(__statusRet)) { __NT_RETURN_NTSTATUS(__statusRet, #status); }} __WI_SUPPRESS_4127_E while ((void)0, 0) 32 | 33 | // Conditionally returns failures (NTSTATUS) - always logs a var-arg message on failure 34 | #define NT_RETURN_IF_NTSTATUS_FAILED_MSG(status, fmt, ...) __WI_SUPPRESS_4127_S do { const auto __statusRet = wil::verify_ntstatus(status); if (FAILED_NTSTATUS(__statusRet)) { __NT_RETURN_NTSTATUS_MSG(__statusRet, #status, fmt, ##__VA_ARGS__); }} __WI_SUPPRESS_4127_E while((void)0, 0) 35 | 36 | //***************************************************************************** 37 | // Macros to catch and convert exceptions on failure 38 | //***************************************************************************** 39 | 40 | // Use these macros *within* a catch (...) block to handle exceptions 41 | #define NT_RETURN_CAUGHT_EXCEPTION() return __R_FN(Nt_Return_CaughtException)(__R_INFO_ONLY(nullptr)) 42 | #define NT_RETURN_CAUGHT_EXCEPTION_MSG(fmt, ...) return __R_FN(Nt_Return_CaughtExceptionMsg)(__R_INFO(nullptr) fmt, ##__VA_ARGS__) 43 | 44 | // Use these macros in place of a catch block to handle exceptions 45 | #define NT_CATCH_RETURN() catch (...) { NT_RETURN_CAUGHT_EXCEPTION(); } 46 | #define NT_CATCH_RETURN_MSG(fmt, ...) catch (...) { NT_RETURN_CAUGHT_EXCEPTION_MSG(fmt, ##__VA_ARGS__); } 47 | 48 | 49 | namespace wil 50 | { 51 | //***************************************************************************** 52 | // Public Helpers that catch -- mostly only enabled when exceptions are enabled 53 | //***************************************************************************** 54 | 55 | // StatusFromCaughtException is a function that is meant to be called from within a catch(...) block. Internally 56 | // it re-throws and catches the exception to convert it to an NTSTATUS. If an exception is of an unrecognized type 57 | // the function will fail fast. 58 | // 59 | // try 60 | // { 61 | // // Code 62 | // } 63 | // catch (...) 64 | // { 65 | // status = wil::StatusFromCaughtException(); 66 | // } 67 | _Always_(_Post_satisfies_(return < 0)) 68 | __declspec(noinline) inline NTSTATUS StatusFromCaughtException() WI_NOEXCEPT 69 | { 70 | bool isNormalized = false; 71 | NTSTATUS status = STATUS_SUCCESS; 72 | if (details::g_pfnResultFromCaughtExceptionInternal) 73 | { 74 | status = details::g_pfnResultFromCaughtExceptionInternal(nullptr, 0, &isNormalized).status; 75 | } 76 | if (FAILED_NTSTATUS(status)) 77 | { 78 | return status; 79 | } 80 | 81 | // Caller bug: an unknown exception was thrown 82 | __WIL_PRIVATE_FAIL_FAST_HR_IF(__HRESULT_FROM_WIN32(ERROR_UNHANDLED_EXCEPTION), g_fResultFailFastUnknownExceptions); 83 | return wil::details::HrToNtStatus(__HRESULT_FROM_WIN32(ERROR_UNHANDLED_EXCEPTION)); 84 | } 85 | 86 | namespace details 87 | { 88 | template 89 | __declspec(noinline) inline NTSTATUS ReportStatus_CaughtException(__R_FN_PARAMS_FULL, SupportedExceptions supported = SupportedExceptions::Default); 90 | template 91 | __declspec(noinline) inline NTSTATUS ReportStatus_CaughtExceptionMsg(__R_FN_PARAMS_FULL, _Printf_format_string_ PCSTR formatString, va_list argList); 92 | 93 | namespace __R_NS_NAME 94 | { 95 | #ifdef WIL_ENABLE_EXCEPTIONS 96 | __R_DIRECT_METHOD(NTSTATUS, Nt_Return_CaughtException)(__R_DIRECT_FN_PARAMS_ONLY) WI_NOEXCEPT 97 | { 98 | __R_FN_LOCALS; 99 | return wil::details::ReportStatus_CaughtException(__R_DIRECT_FN_CALL_ONLY); 100 | } 101 | 102 | __R_DIRECT_METHOD(NTSTATUS, Nt_Return_CaughtExceptionMsg)(__R_DIRECT_FN_PARAMS _Printf_format_string_ PCSTR formatString, ...) WI_NOEXCEPT 103 | { 104 | va_list argList; 105 | va_start(argList, formatString); 106 | __R_FN_LOCALS; 107 | return wil::details::ReportStatus_CaughtExceptionMsg(__R_DIRECT_FN_CALL formatString, argList); 108 | } 109 | #endif 110 | } 111 | 112 | template 113 | __declspec(noinline) inline NTSTATUS ReportStatus_CaughtException(__R_FN_PARAMS_FULL, SupportedExceptions supported) 114 | { 115 | wchar_t message[2048]; 116 | message[0] = L'\0'; 117 | return ReportFailure_CaughtExceptionCommon(__R_FN_CALL_FULL, message, ARRAYSIZE(message), supported).status; 118 | } 119 | 120 | template<> 121 | __declspec(noinline) inline NTSTATUS ReportStatus_CaughtException(__R_FN_PARAMS_FULL, SupportedExceptions supported) 122 | { 123 | wchar_t message[2048]; 124 | message[0] = L'\0'; 125 | RESULT_NORETURN_RESULT(ReportFailure_CaughtExceptionCommon(__R_FN_CALL_FULL, message, ARRAYSIZE(message), supported).status); 126 | } 127 | 128 | template<> 129 | __declspec(noinline) inline NTSTATUS ReportStatus_CaughtException(__R_FN_PARAMS_FULL, SupportedExceptions supported) 130 | { 131 | wchar_t message[2048]; 132 | message[0] = L'\0'; 133 | RESULT_NORETURN_RESULT(ReportFailure_CaughtExceptionCommon(__R_FN_CALL_FULL, message, ARRAYSIZE(message), supported).status); 134 | } 135 | 136 | template 137 | __declspec(noinline) inline NTSTATUS ReportStatus_CaughtExceptionMsg(__R_FN_PARAMS_FULL, _Printf_format_string_ PCSTR formatString, va_list argList) 138 | { 139 | // Pre-populate the buffer with our message, the exception message will be added to it... 140 | wchar_t message[2048]; 141 | PrintLoggingMessage(message, ARRAYSIZE(message), formatString, argList); 142 | StringCchCatW(message, ARRAYSIZE(message), L" -- "); 143 | return ReportFailure_CaughtExceptionCommon(__R_FN_CALL_FULL, message, ARRAYSIZE(message), SupportedExceptions::Default).status; 144 | } 145 | 146 | template<> 147 | __declspec(noinline) inline NTSTATUS ReportStatus_CaughtExceptionMsg(__R_FN_PARAMS_FULL, _Printf_format_string_ PCSTR formatString, va_list argList) 148 | { 149 | // Pre-populate the buffer with our message, the exception message will be added to it... 150 | wchar_t message[2048]; 151 | PrintLoggingMessage(message, ARRAYSIZE(message), formatString, argList); 152 | StringCchCatW(message, ARRAYSIZE(message), L" -- "); 153 | RESULT_NORETURN_RESULT(ReportFailure_CaughtExceptionCommon(__R_FN_CALL_FULL, message, ARRAYSIZE(message), SupportedExceptions::Default).status); 154 | } 155 | 156 | template<> 157 | __declspec(noinline) inline NTSTATUS ReportStatus_CaughtExceptionMsg(__R_FN_PARAMS_FULL, _Printf_format_string_ PCSTR formatString, va_list argList) 158 | { 159 | // Pre-populate the buffer with our message, the exception message will be added to it... 160 | wchar_t message[2048]; 161 | PrintLoggingMessage(message, ARRAYSIZE(message), formatString, argList); 162 | StringCchCatW(message, ARRAYSIZE(message), L" -- "); 163 | RESULT_NORETURN_RESULT(ReportFailure_CaughtExceptionCommon(__R_FN_CALL_FULL, message, ARRAYSIZE(message), SupportedExceptions::Default).status); 164 | } 165 | } 166 | } 167 | 168 | #endif // __WIL_NT_RESULTMACROS_INCLUDED 169 | -------------------------------------------------------------------------------- /source/wil/registry.h: -------------------------------------------------------------------------------- 1 | //********************************************************* 2 | // 3 | // Copyright (c) Microsoft. All rights reserved. 4 | // This code is licensed under the MIT License. 5 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF 6 | // ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED 7 | // TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 8 | // PARTICULAR PURPOSE AND NONINFRINGEMENT. 9 | // 10 | //********************************************************* 11 | #ifndef __WIL_REGISTRY_INCLUDED 12 | #define __WIL_REGISTRY_INCLUDED 13 | 14 | #ifdef _KERNEL_MODE 15 | #error This header is not supported in kernel-mode. 16 | #endif 17 | 18 | #include 19 | #include // new(std::nothrow) 20 | #include "resource.h" // unique_hkey 21 | 22 | namespace wil 23 | { 24 | //! The key name includes the absolute path of the key in the registry, always starting at a 25 | //! base key, for example, HKEY_LOCAL_MACHINE. 26 | size_t const max_registry_key_name_length = 255; 27 | 28 | //! The maximum number of characters allowed in a registry value's name. 29 | size_t const max_registry_value_name_length = 16383; 30 | 31 | // unique_registry_watcher/unique_registry_watcher_nothrow/unique_registry_watcher_failfast 32 | // These classes make it easy to execute a provided function when a 33 | // registry key changes (optionally recursively). Specify the key 34 | // either as a root key + path, or an open registry handle as wil::unique_hkey 35 | // or a raw HKEY value (that will be duplicated). 36 | // 37 | // Example use with exceptions base error handling: 38 | // auto watcher = wil::make_registry_watcher(HKEY_CURRENT_USER, L"Software\\MyApp", true, wil::RegistryChangeKind changeKind[] 39 | // { 40 | // if (changeKind == RegistryChangeKind::Delete) 41 | // { 42 | // watcher.reset(); 43 | // } 44 | // // invalidate cached registry data here 45 | // }); 46 | // 47 | // Example use with error code base error handling: 48 | // auto watcher = wil::make_registry_watcher_nothrow(HKEY_CURRENT_USER, L"Software\\MyApp", true, wil::RegistryChangeKind[] 49 | // { 50 | // // invalidate cached registry data here 51 | // }); 52 | // RETURN_IF_NULL_ALLOC(watcher); 53 | 54 | enum class RegistryChangeKind 55 | { 56 | Modify = 0, 57 | Delete = 1, 58 | }; 59 | 60 | /// @cond 61 | namespace details 62 | { 63 | struct registry_watcher_state 64 | { 65 | registry_watcher_state(unique_hkey &&keyToWatch, bool isRecursive, wistd::function &&callback) 66 | : m_callback(wistd::move(callback)), m_keyToWatch(wistd::move(keyToWatch)), m_isRecursive(isRecursive) 67 | { 68 | } 69 | wistd::function m_callback; 70 | unique_hkey m_keyToWatch; 71 | unique_event_nothrow m_eventHandle; 72 | 73 | // While not strictly needed since this is ref counted the thread pool wait 74 | // should be last to ensure that the other members are valid 75 | // when it is destructed as it will reference them. 76 | unique_threadpool_wait m_threadPoolWait; 77 | bool m_isRecursive; 78 | 79 | volatile long m_refCount = 1; 80 | srwlock m_lock; 81 | 82 | // Returns true if the refcount can be increased from a non zero value, 83 | // false it was zero impling that the object is in or on the way to the destructor. 84 | // In this case ReleaseFromCallback() should not be called. 85 | bool TryAddRef() 86 | { 87 | return ::InterlockedIncrement(&m_refCount) > 1; 88 | } 89 | 90 | void Release() 91 | { 92 | auto lock = m_lock.lock_exclusive(); 93 | if (0 == ::InterlockedDecrement(&m_refCount)) 94 | { 95 | lock.reset(); // leave the lock before deleting it. 96 | delete this; 97 | } 98 | } 99 | 100 | void ReleaseFromCallback(bool rearm) 101 | { 102 | auto lock = m_lock.lock_exclusive(); 103 | if (0 == ::InterlockedDecrement(&m_refCount)) 104 | { 105 | // Destroy the thread pool wait now to avoid the wait that would occur in the 106 | // destructor. That wait would cause a deadlock since we are doing this from the callback. 107 | ::CloseThreadpoolWait(m_threadPoolWait.release()); 108 | lock.reset(); // leave the lock before deleting it. 109 | delete this; 110 | // Sleep(1); // Enable for testing to find use after free bugs. 111 | } 112 | else if (rearm) 113 | { 114 | ::SetThreadpoolWait(m_threadPoolWait.get(), m_eventHandle.get(), nullptr); 115 | } 116 | } 117 | }; 118 | 119 | inline void delete_registry_watcher_state(_In_opt_ registry_watcher_state *watcherStorage) { watcherStorage->Release(); } 120 | 121 | typedef resource_policy registry_watcher_state_resource_policy; 123 | } 124 | /// @endcond 125 | 126 | template 127 | class registry_watcher_t : public storage_t 128 | { 129 | public: 130 | // forward all base class constructors... 131 | template 132 | explicit registry_watcher_t(args_t&&... args) WI_NOEXCEPT : storage_t(wistd::forward(args)...) {} 133 | 134 | // HRESULT or void error handling... 135 | typedef typename err_policy::result result; 136 | 137 | // Exception-based constructors 138 | registry_watcher_t(HKEY rootKey, _In_ PCWSTR subKey, bool isRecursive, wistd::function &&callback) 139 | { 140 | static_assert(wistd::is_same::value, "this constructor requires exceptions; use the create method"); 141 | create(rootKey, subKey, isRecursive, wistd::move(callback)); 142 | } 143 | 144 | registry_watcher_t(unique_hkey &&keyToWatch, bool isRecursive, wistd::function &&callback) 145 | { 146 | static_assert(wistd::is_same::value, "this constructor requires exceptions; use the create method"); 147 | create(wistd::move(keyToWatch), isRecursive, wistd::move(callback)); 148 | } 149 | 150 | // Pass a root key, sub key pair or use an empty string to use rootKey as the key to watch. 151 | result create(HKEY rootKey, _In_ PCWSTR subKey, bool isRecursive, wistd::function &&callback) 152 | { 153 | // Most use will want to create the key, consider adding an option for open as a future design change. 154 | unique_hkey keyToWatch; 155 | HRESULT hr = HRESULT_FROM_WIN32(RegCreateKeyExW(rootKey, subKey, 0, nullptr, 0, KEY_NOTIFY, nullptr, &keyToWatch, nullptr)); 156 | if (FAILED(hr)) 157 | { 158 | return err_policy::HResult(hr); 159 | } 160 | return err_policy::HResult(create_common(wistd::move(keyToWatch), isRecursive, wistd::move(callback))); 161 | } 162 | 163 | result create(unique_hkey &&keyToWatch, bool isRecursive, wistd::function &&callback) 164 | { 165 | return err_policy::HResult(create_common(wistd::move(keyToWatch), isRecursive, wistd::move(callback))); 166 | } 167 | 168 | private: 169 | // Factored into a standalone function to support Clang which does not support conversion of stateless lambdas 170 | // to __stdcall 171 | static void __stdcall callback(PTP_CALLBACK_INSTANCE, void *context, TP_WAIT *, TP_WAIT_RESULT) 172 | { 173 | #ifndef __WIL_REGISTRY_CHANGE_CALLBACK_TEST 174 | #define __WIL_REGISTRY_CHANGE_CALLBACK_TEST 175 | #endif 176 | __WIL_REGISTRY_CHANGE_CALLBACK_TEST 177 | auto watcherState = static_cast(context); 178 | if (watcherState->TryAddRef()) 179 | { 180 | // using auto reset event so don't need to manually reset. 181 | 182 | // failure here is a programming error. 183 | const LSTATUS error = RegNotifyChangeKeyValue(watcherState->m_keyToWatch.get(), watcherState->m_isRecursive, 184 | REG_NOTIFY_CHANGE_LAST_SET | REG_NOTIFY_CHANGE_NAME | REG_NOTIFY_THREAD_AGNOSTIC, 185 | watcherState->m_eventHandle.get(), TRUE); 186 | 187 | // Call the client before re-arming to ensure that multiple callbacks don't 188 | // run concurrently. 189 | switch (error) 190 | { 191 | case ERROR_SUCCESS: 192 | case ERROR_ACCESS_DENIED: 193 | // Normal modification: send RegistryChangeKind::Modify and re-arm. 194 | watcherState->m_callback(RegistryChangeKind::Modify); 195 | watcherState->ReleaseFromCallback(true); 196 | break; 197 | 198 | case ERROR_KEY_DELETED: 199 | // Key deleted, send RegistryChangeKind::Delete, do not re-arm. 200 | watcherState->m_callback(RegistryChangeKind::Delete); 201 | watcherState->ReleaseFromCallback(false); 202 | break; 203 | 204 | case ERROR_HANDLE_REVOKED: 205 | // Handle revoked. This can occur if the user session ends before 206 | // the watcher shuts-down. Disarm silently since there is generally no way to respond. 207 | watcherState->ReleaseFromCallback(false); 208 | break; 209 | 210 | default: 211 | FAIL_FAST_HR(HRESULT_FROM_WIN32(error)); 212 | } 213 | } 214 | } 215 | 216 | // This function exists to avoid template expansion of this code based on err_policy. 217 | HRESULT create_common(unique_hkey &&keyToWatch, bool isRecursive, wistd::function &&callback) 218 | { 219 | wistd::unique_ptr watcherState(new(std::nothrow) details::registry_watcher_state( 220 | wistd::move(keyToWatch), isRecursive, wistd::move(callback))); 221 | RETURN_IF_NULL_ALLOC(watcherState); 222 | RETURN_IF_FAILED(watcherState->m_eventHandle.create()); 223 | RETURN_IF_WIN32_ERROR(RegNotifyChangeKeyValue(watcherState->m_keyToWatch.get(), 224 | watcherState->m_isRecursive, REG_NOTIFY_CHANGE_LAST_SET | REG_NOTIFY_CHANGE_NAME | REG_NOTIFY_THREAD_AGNOSTIC, 225 | watcherState->m_eventHandle.get(), TRUE)); 226 | 227 | watcherState->m_threadPoolWait.reset(CreateThreadpoolWait(®istry_watcher_t::callback, watcherState.get(), nullptr)); 228 | RETURN_LAST_ERROR_IF(!watcherState->m_threadPoolWait); 229 | storage_t::reset(watcherState.release()); // no more failures after this, pass ownership 230 | SetThreadpoolWait(storage_t::get()->m_threadPoolWait.get(), storage_t::get()->m_eventHandle.get(), nullptr); 231 | return S_OK; 232 | } 233 | }; 234 | 235 | typedef unique_any_t, err_returncode_policy>> unique_registry_watcher_nothrow; 236 | typedef unique_any_t, err_failfast_policy>> unique_registry_watcher_failfast; 237 | 238 | inline unique_registry_watcher_nothrow make_registry_watcher_nothrow(HKEY rootKey, _In_ PCWSTR subKey, bool isRecursive, wistd::function &&callback) WI_NOEXCEPT 239 | { 240 | unique_registry_watcher_nothrow watcher; 241 | watcher.create(rootKey, subKey, isRecursive, wistd::move(callback)); 242 | return watcher; // caller must test for success using if (watcher) 243 | } 244 | 245 | inline unique_registry_watcher_nothrow make_registry_watcher_nothrow(unique_hkey &&keyToWatch, bool isRecursive, wistd::function &&callback) WI_NOEXCEPT 246 | { 247 | unique_registry_watcher_nothrow watcher; 248 | watcher.create(wistd::move(keyToWatch), isRecursive, wistd::move(callback)); 249 | return watcher; // caller must test for success using if (watcher) 250 | } 251 | 252 | inline unique_registry_watcher_failfast make_registry_watcher_failfast(HKEY rootKey, _In_ PCWSTR subKey, bool isRecursive, wistd::function &&callback) 253 | { 254 | return unique_registry_watcher_failfast(rootKey, subKey, isRecursive, wistd::move(callback)); 255 | } 256 | 257 | inline unique_registry_watcher_failfast make_registry_watcher_failfast(unique_hkey &&keyToWatch, bool isRecursive, wistd::function &&callback) 258 | { 259 | return unique_registry_watcher_failfast(wistd::move(keyToWatch), isRecursive, wistd::move(callback)); 260 | } 261 | 262 | #ifdef WIL_ENABLE_EXCEPTIONS 263 | typedef unique_any_t, err_exception_policy >> unique_registry_watcher; 264 | 265 | inline unique_registry_watcher make_registry_watcher(HKEY rootKey, _In_ PCWSTR subKey, bool isRecursive, wistd::function &&callback) 266 | { 267 | return unique_registry_watcher(rootKey, subKey, isRecursive, wistd::move(callback)); 268 | } 269 | 270 | inline unique_registry_watcher make_registry_watcher(unique_hkey &&keyToWatch, bool isRecursive, wistd::function &&callback) 271 | { 272 | return unique_registry_watcher(wistd::move(keyToWatch), isRecursive, wistd::move(callback)); 273 | } 274 | #endif // WIL_ENABLE_EXCEPTIONS 275 | } // namespace wil 276 | 277 | #endif 278 | -------------------------------------------------------------------------------- /source/wil/result_originate.h: -------------------------------------------------------------------------------- 1 | //********************************************************* 2 | // 3 | // Copyright (c) Microsoft. All rights reserved. 4 | // This code is licensed under the MIT License. 5 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF 6 | // ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED 7 | // TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 8 | // PARTICULAR PURPOSE AND NONINFRINGEMENT. 9 | // 10 | //********************************************************* 11 | 12 | // Note: When origination is enabled by including this file, origination is done as part of the RETURN_* and THROW_* macros. Before originating 13 | // a new error we will observe whether there is already an error payload associated with the current thread. If there is, and the HRESULTs match, 14 | // then a new error will not be originated. Otherwise we will overwrite it with a new origination. The ABI boundary for WinRT APIs will check the 15 | // per-thread error information. The act of checking the error clears it, so there should be minimal risk of failing to originate distinct errors 16 | // simply because the HRESULTs match. 17 | // 18 | // For THROW_ macros we will examine the thread-local error storage once per throw. So typically once, with additional calls if the exception is 19 | // caught and re-thrown. 20 | // 21 | // For RETURN_ macros we will have to examine the thread-local error storage once per frame as the call stack unwinds. Because error conditions 22 | // -should- be uncommon the performance impact of checking TLS should be minimal. The more expensive part is originating the error because it must 23 | // capture the entire stack and some additional data. 24 | 25 | #ifndef __WIL_RESULT_ORIGINATE_INCLUDED 26 | #define __WIL_RESULT_ORIGINATE_INCLUDED 27 | 28 | #include "result.h" 29 | #include // RestrictedErrorInfo uses BSTRs :( 30 | #include "resource.h" 31 | #include "com.h" 32 | #include 33 | 34 | namespace wil 35 | { 36 | namespace details 37 | { 38 | // Note: The name must begin with "Raise" so that the !analyze auto-bucketing will ignore this stack frame. Otherwise this line of code gets all the blame. 39 | inline void __stdcall RaiseRoOriginateOnWilExceptions(wil::FailureInfo const& failure) WI_NOEXCEPT 40 | { 41 | if ((failure.type == FailureType::Return) || (failure.type == FailureType::Exception)) 42 | { 43 | bool shouldOriginate = true; 44 | 45 | wil::com_ptr_nothrow restrictedErrorInformation; 46 | if (GetRestrictedErrorInfo(&restrictedErrorInformation) == S_OK) 47 | { 48 | // This thread already has an error origination payload. Don't originate again if it has the same HRESULT that we are 49 | // observing right now. 50 | wil::unique_bstr descriptionUnused; 51 | HRESULT existingHr = failure.hr; 52 | wil::unique_bstr restrictedDescriptionUnused; 53 | wil::unique_bstr capabilitySidUnused; 54 | if (SUCCEEDED(restrictedErrorInformation->GetErrorDetails(&descriptionUnused, &existingHr, &restrictedDescriptionUnused, &capabilitySidUnused))) 55 | { 56 | shouldOriginate = (failure.hr != existingHr); 57 | } 58 | } 59 | 60 | if (shouldOriginate) 61 | { 62 | #if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP | WINAPI_PARTITION_SYSTEM) 63 | wil::unique_hmodule errorModule; 64 | if (GetModuleHandleExW(0, L"api-ms-win-core-winrt-error-l1-1-1.dll", &errorModule)) 65 | { 66 | auto pfn = reinterpret_cast(GetProcAddress(errorModule.get(), "RoOriginateError")); 67 | if (pfn != nullptr) 68 | { 69 | pfn(failure.hr, nullptr); 70 | } 71 | } 72 | #else // DESKTOP | SYSTEM 73 | ::RoOriginateError(failure.hr, nullptr); 74 | #endif // DESKTOP | SYSTEM 75 | } 76 | else if (restrictedErrorInformation) 77 | { 78 | // GetRestrictedErrorInfo returns ownership of the error information. If we aren't originating, and an error was already present, 79 | // then we need to restore the error information for later observation. 80 | SetRestrictedErrorInfo(restrictedErrorInformation.get()); 81 | } 82 | } 83 | } 84 | 85 | // This method will check for the presence of stowed exception data on the current thread. If such data exists, and the HRESULT 86 | // matches the current failure, then we will call RoFailFastWithErrorContext. RoFailFastWithErrorContext in this situation will 87 | // result in -VASTLY- improved crash bucketing. It is hard to express just how much better. In other cases we just return and 88 | // the calling method fails fast the same way it always has. 89 | inline void __stdcall FailfastWithContextCallback(wil::FailureInfo const& failure) WI_NOEXCEPT 90 | { 91 | wil::com_ptr_nothrow restrictedErrorInformation; 92 | if (GetRestrictedErrorInfo(&restrictedErrorInformation) == S_OK) 93 | { 94 | wil::unique_bstr descriptionUnused; 95 | HRESULT existingHr = failure.hr; 96 | wil::unique_bstr restrictedDescriptionUnused; 97 | wil::unique_bstr capabilitySidUnused; 98 | if (SUCCEEDED(restrictedErrorInformation->GetErrorDetails(&descriptionUnused, &existingHr, &restrictedDescriptionUnused, &capabilitySidUnused)) && 99 | (existingHr == failure.hr)) 100 | { 101 | // GetRestrictedErrorInfo returns ownership of the error information. We want it to be available for RoFailFastWithErrorContext 102 | // so we must restore it via SetRestrictedErrorInfo first. 103 | SetRestrictedErrorInfo(restrictedErrorInformation.get()); 104 | RoFailFastWithErrorContext(existingHr); 105 | } 106 | else 107 | { 108 | // The error didn't match the current failure. Put it back in thread-local storage even though we aren't failing fast 109 | // in this method, so it is available in the debugger just-in-case. 110 | SetRestrictedErrorInfo(restrictedErrorInformation.get()); 111 | } 112 | } 113 | } 114 | } // namespace details 115 | } // namespace wil 116 | 117 | // Automatically call RoOriginateError upon error origination by including this file 118 | WI_HEADER_INITITALIZATION_FUNCTION(ResultStowedExceptionInitialize, [] 119 | { 120 | ::wil::SetOriginateErrorCallback(::wil::details::RaiseRoOriginateOnWilExceptions); 121 | ::wil::SetFailfastWithContextCallback(::wil::details::FailfastWithContextCallback); 122 | return 1; 123 | }); 124 | 125 | #endif // __WIL_RESULT_ORIGINATE_INCLUDED 126 | -------------------------------------------------------------------------------- /source/wil/rpc_helpers.h: -------------------------------------------------------------------------------- 1 | //********************************************************* 2 | // 3 | // Copyright (c) Microsoft. All rights reserved. 4 | // This code is licensed under the MIT License. 5 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF 6 | // ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED 7 | // TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 8 | // PARTICULAR PURPOSE AND NONINFRINGEMENT. 9 | // 10 | //********************************************************* 11 | #ifndef __WIL_RPC_HELPERS_INCLUDED 12 | #define __WIL_RPC_HELPERS_INCLUDED 13 | 14 | #include "result.h" 15 | #include "resource.h" 16 | #include "wistd_functional.h" 17 | #include "wistd_type_traits.h" 18 | 19 | namespace wil 20 | { 21 | 22 | /// @cond 23 | namespace details 24 | { 25 | // This call-adapter template converts a void-returning 'wistd::invoke' into 26 | // an HRESULT-returning 'wistd::invoke' that emits S_OK. It can be eliminated 27 | // with 'if constexpr' when C++17 is in wide use. 28 | template struct call_adapter 29 | { 30 | template static HRESULT call(TArgs&& ... args) 31 | { 32 | return wistd::invoke(wistd::forward(args)...); 33 | } 34 | }; 35 | 36 | template<> struct call_adapter 37 | { 38 | template static HRESULT call(TArgs&& ... args) 39 | { 40 | wistd::invoke(wistd::forward(args)...); 41 | return S_OK; 42 | } 43 | }; 44 | 45 | // Some RPC exceptions are already HRESULTs. Others are in the regular Win32 46 | // error space. If the incoming exception code isn't an HRESULT, wrap it. 47 | constexpr HRESULT map_rpc_exception(DWORD code) 48 | { 49 | return IS_ERROR(code) ? code : __HRESULT_FROM_WIN32(code); 50 | } 51 | } 52 | /// @endcond 53 | 54 | /** Invokes an RPC method, mapping structured exceptions to HRESULTs 55 | Failures encountered by the RPC infrastructure (such as server crashes, authentication 56 | errors, client parameter issues, etc.) are emitted by raising a structured exception from 57 | within the RPC machinery. This method wraps the requested call in the usual RpcTryExcept, 58 | RpcTryCatch, and RpcEndExcept sequence then maps the exceptions to HRESULTs for the usual 59 | flow control machinery to use. 60 | 61 | Many RPC methods are defined as returning HRESULT themselves, where the HRESULT indicates 62 | the result of the _work_. HRESULTs returned by a successful completion of the _call_ are 63 | returned as-is. 64 | 65 | RPC methods that have a return type of 'void' are mapped to returning S_OK when the _call_ 66 | completes successfully. 67 | 68 | For example, consider an RPC interface method defined in idl as: 69 | ~~~ 70 | HRESULT GetKittenState([in, ref, string] const wchar_t* name, [out, retval] KittenState** state); 71 | ~~~ 72 | To call this method, use: 73 | ~~~ 74 | wil::unique_rpc_binding binding = // typically gotten elsewhere; 75 | wil::unique_midl_ptr state; 76 | HRESULT hr = wil::invoke_rpc_nothrow(GetKittenState, binding.get(), L"fluffy", state.put()); 77 | RETURN_IF_FAILED(hr); 78 | ~~~ 79 | */ 80 | template HRESULT invoke_rpc_nothrow(TCall&&... args) WI_NOEXCEPT 81 | { 82 | RpcTryExcept 83 | { 84 | // Note: this helper type can be removed with C++17 enabled via 85 | // 'if constexpr(wistd::is_same_v)' 86 | using result_t = typename wistd::__invoke_of::type; 87 | RETURN_IF_FAILED(details::call_adapter::call(wistd::forward(args)...)); 88 | return S_OK; 89 | } 90 | RpcExcept(RpcExceptionFilter(RpcExceptionCode())) 91 | { 92 | RETURN_HR(details::map_rpc_exception(RpcExceptionCode())); 93 | } 94 | RpcEndExcept 95 | } 96 | 97 | /** Invokes an RPC method, mapping structured exceptions to HRESULTs 98 | Failures encountered by the RPC infrastructure (such as server crashes, authentication 99 | errors, client parameter issues, etc.) are emitted by raising a structured exception from 100 | within the RPC machinery. This method wraps the requested call in the usual RpcTryExcept, 101 | RpcTryCatch, and RpcEndExcept sequence then maps the exceptions to HRESULTs for the usual 102 | flow control machinery to use. 103 | 104 | Some RPC methods return results (such as a state enumeration or other value) directly in 105 | their signature. This adapter writes that result into a caller-provided object then 106 | returns S_OK. 107 | 108 | For example, consider an RPC interface method defined in idl as: 109 | ~~~ 110 | GUID GetKittenId([in, ref, string] const wchar_t* name); 111 | ~~~ 112 | To call this method, use: 113 | ~~~ 114 | wil::unique_rpc_binding binding = // typically gotten elsewhere; 115 | GUID id; 116 | HRESULT hr = wil::invoke_rpc_result_nothrow(id, GetKittenId, binding.get(), L"fluffy"); 117 | RETURN_IF_FAILED(hr); 118 | ~~~ 119 | */ 120 | template HRESULT invoke_rpc_result_nothrow(TResult& result, TCall&&... args) WI_NOEXCEPT 121 | { 122 | RpcTryExcept 123 | { 124 | result = wistd::invoke(wistd::forward(args)...); 125 | return S_OK; 126 | } 127 | RpcExcept(RpcExceptionFilter(RpcExceptionCode())) 128 | { 129 | RETURN_HR(details::map_rpc_exception(RpcExceptionCode())); 130 | } 131 | RpcEndExcept 132 | } 133 | 134 | namespace details 135 | { 136 | // Provides an adapter around calling the context-handle-close method on an 137 | // RPC interface, which itself is an RPC call. 138 | template 139 | struct rpc_closer_t 140 | { 141 | static void Close(TStorage arg) WI_NOEXCEPT 142 | { 143 | LOG_IF_FAILED(invoke_rpc_nothrow(close_fn, &arg)); 144 | } 145 | }; 146 | } 147 | 148 | /** Manages explicit RPC context handles 149 | Explicit RPC context handles are used in many RPC interfaces. Most interfaces with 150 | context handles have an explicit `FooClose([in, out] CONTEXT*)` method that lets 151 | the server close out the context handle. As the close method itself is an RPC call, 152 | it can fail and raise a structured exception. 153 | 154 | This type routes the context-handle-specific `Close` call through the `invoke_rpc_nothrow` 155 | helper, ensuring correct cleanup and lifecycle management. 156 | ~~~ 157 | // Assume the interface has two methods: 158 | // HRESULT OpenFoo([in] handle_t binding, [out] FOO_CONTEXT*); 159 | // HRESULT UseFoo([in] FOO_CONTEXT context; 160 | // void CloseFoo([in, out] PFOO_CONTEXT); 161 | using unique_foo_context = wil::unique_rpc_context_handle; 162 | unique_foo_context context; 163 | RETURN_IF_FAILED(wil::invoke_rpc_nothrow(OpenFoo, m_binding.get(), context.put())); 164 | RETURN_IF_FAILED(wil::invoke_rpc_nothrow(UseFoo, context.get())); 165 | context.reset(); 166 | ~~~ 167 | */ 168 | template 169 | using unique_rpc_context_handle = unique_any::Close), details::rpc_closer_t::Close>; 170 | 171 | #ifdef WIL_ENABLE_EXCEPTIONS 172 | /** Invokes an RPC method, mapping structured exceptions to C++ exceptions 173 | See `wil::invoke_rpc_nothrow` for additional information. Failures during the _call_ 174 | and those returned by the _method_ are mapped to HRESULTs and thrown inside a 175 | wil::ResultException. Using the example RPC method provided above: 176 | ~~~ 177 | wil::unique_midl_ptr state; 178 | wil::invoke_rpc(GetKittenState, binding.get(), L"fluffy", state.put()); 179 | // use 'state' 180 | ~~~ 181 | */ 182 | template void invoke_rpc(TCall&& ... args) 183 | { 184 | THROW_IF_FAILED(invoke_rpc_nothrow(wistd::forward(args)...)); 185 | } 186 | 187 | /** Invokes an RPC method, mapping structured exceptions to C++ exceptions 188 | See `wil::invoke_rpc_result_nothrow` for additional information. Failures during the 189 | _call_ are mapped to HRESULTs and thrown inside a `wil::ResultException`. Using the 190 | example RPC method provided above: 191 | ~~~ 192 | GUID id = wil::invoke_rpc_result(GetKittenId, binding.get()); 193 | // use 'id' 194 | ~~~ 195 | */ 196 | template auto invoke_rpc_result(TCall&& ... args) 197 | { 198 | using result_t = typename wistd::__invoke_of::type; 199 | result_t result{}; 200 | THROW_IF_FAILED(invoke_rpc_result_nothrow(result, wistd::forward(args)...)); 201 | return result; 202 | } 203 | #endif 204 | } 205 | 206 | #endif 207 | -------------------------------------------------------------------------------- /source/wil/stl.h: -------------------------------------------------------------------------------- 1 | //********************************************************* 2 | // 3 | // Copyright (c) Microsoft. All rights reserved. 4 | // This code is licensed under the MIT License. 5 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF 6 | // ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED 7 | // TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 8 | // PARTICULAR PURPOSE AND NONINFRINGEMENT. 9 | // 10 | //********************************************************* 11 | #ifndef __WIL_STL_INCLUDED 12 | #define __WIL_STL_INCLUDED 13 | 14 | #include "common.h" 15 | #include "resource.h" 16 | #include 17 | #include 18 | #include 19 | 20 | #if defined(WIL_ENABLE_EXCEPTIONS) 21 | 22 | namespace wil 23 | { 24 | /** Secure allocator for STL containers. 25 | The `wil::secure_allocator` allocator calls `SecureZeroMemory` before deallocating 26 | memory. This provides a mechanism for secure STL containers such as `wil::secure_vector`, 27 | `wil::secure_string`, and `wil::secure_wstring`. */ 28 | template 29 | struct secure_allocator 30 | : public std::allocator 31 | { 32 | template 33 | struct rebind 34 | { 35 | typedef secure_allocator other; 36 | }; 37 | 38 | secure_allocator() 39 | : std::allocator() 40 | { 41 | } 42 | 43 | ~secure_allocator() = default; 44 | 45 | secure_allocator(const secure_allocator& a) 46 | : std::allocator(a) 47 | { 48 | } 49 | 50 | template 51 | secure_allocator(const secure_allocator& a) 52 | : std::allocator(a) 53 | { 54 | } 55 | 56 | T* allocate(size_t n) 57 | { 58 | return std::allocator::allocate(n); 59 | } 60 | 61 | void deallocate(T* p, size_t n) 62 | { 63 | SecureZeroMemory(p, sizeof(T) * n); 64 | std::allocator::deallocate(p, n); 65 | } 66 | }; 67 | 68 | //! `wil::secure_vector` will be securely zeroed before deallocation. 69 | template 70 | using secure_vector = std::vector>; 71 | //! `wil::secure_wstring` will be securely zeroed before deallocation. 72 | using secure_wstring = std::basic_string, wil::secure_allocator>; 73 | //! `wil::secure_string` will be securely zeroed before deallocation. 74 | using secure_string = std::basic_string, wil::secure_allocator>; 75 | 76 | /// @cond 77 | namespace details 78 | { 79 | template<> struct string_maker 80 | { 81 | HRESULT make(_In_reads_opt_(length) PCWSTR source, size_t length) WI_NOEXCEPT try 82 | { 83 | m_value = source ? std::wstring(source, length) : std::wstring(length, L'\0'); 84 | return S_OK; 85 | } 86 | catch (...) 87 | { 88 | return E_OUTOFMEMORY; 89 | } 90 | 91 | wchar_t* buffer() { return &m_value[0]; } 92 | 93 | HRESULT trim_at_existing_null(size_t length) { m_value.erase(length); return S_OK; } 94 | 95 | std::wstring release() { return std::wstring(std::move(m_value)); } 96 | 97 | static PCWSTR get(const std::wstring& value) { return value.c_str(); } 98 | 99 | private: 100 | std::wstring m_value; 101 | }; 102 | } 103 | /// @endcond 104 | 105 | // str_raw_ptr is an overloaded function that retrieves a const pointer to the first character in a string's buffer. 106 | // This is the overload for std::wstring. Other overloads available in resource.h. 107 | inline PCWSTR str_raw_ptr(const std::wstring& str) 108 | { 109 | return str.c_str(); 110 | } 111 | 112 | } // namespace wil 113 | 114 | #endif // WIL_ENABLE_EXCEPTIONS 115 | 116 | #endif // __WIL_STL_INCLUDED 117 | -------------------------------------------------------------------------------- /source/wil/wrl.h: -------------------------------------------------------------------------------- 1 | //********************************************************* 2 | // 3 | // Copyright (c) Microsoft. All rights reserved. 4 | // This code is licensed under the MIT License. 5 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF 6 | // ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED 7 | // TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 8 | // PARTICULAR PURPOSE AND NONINFRINGEMENT. 9 | // 10 | //********************************************************* 11 | #ifndef __WIL_WRL_INCLUDED 12 | #define __WIL_WRL_INCLUDED 13 | 14 | #include 15 | #include "result.h" 16 | #include "common.h" // wistd type_traits helpers 17 | 18 | namespace wil 19 | { 20 | 21 | #ifdef WIL_ENABLE_EXCEPTIONS 22 | #pragma region Object construction helpers that throw exceptions 23 | 24 | /** Used to construct a RuntimeClass based object that uses 2 phase construction. 25 | Construct a RuntimeClass based object that uses 2 phase construction (by implementing 26 | RuntimeClassInitialize() and returning error codes for failures. 27 | ~~~~ 28 | // SomeClass uses 2 phase initialization by implementing RuntimeClassInitialize() 29 | auto someClass = MakeAndInitializeOrThrow(L"input", true); 30 | ~~~~ */ 31 | 32 | template 33 | Microsoft::WRL::ComPtr MakeAndInitializeOrThrow(TArgs&&... args) 34 | { 35 | Microsoft::WRL::ComPtr obj; 36 | THROW_IF_FAILED(Microsoft::WRL::MakeAndInitialize(&obj, Microsoft::WRL::Details::Forward(args)...)); 37 | return obj; 38 | } 39 | 40 | /** Used to construct an RuntimeClass based object that uses exceptions in its constructor (and does 41 | not require 2 phase construction). 42 | ~~~~ 43 | // SomeClass uses exceptions for error handling in its constructor. 44 | auto someClass = MakeOrThrow(L"input", true); 45 | ~~~~ */ 46 | 47 | template 48 | Microsoft::WRL::ComPtr MakeOrThrow(TArgs&&... args) 49 | { 50 | // This is how you can detect the presence of RuntimeClassInitialize() and find dangerous use. 51 | // Unfortunately this produces false positives as all RuntimeClass derived classes have 52 | // a RuntimeClassInitialize() method from their base class. 53 | // static_assert(!std::is_member_function_pointer::value, 54 | // "class has a RuntimeClassInitialize member, use MakeAndInitializeOrThrow instead"); 55 | auto obj = Microsoft::WRL::Make(Microsoft::WRL::Details::Forward(args)...); 56 | THROW_IF_NULL_ALLOC(obj.Get()); 57 | return obj; 58 | } 59 | #pragma endregion 60 | 61 | #endif // WIL_ENABLE_EXCEPTIONS 62 | 63 | /** By default WRL Callback objects are not agile, use this to make an agile one. Replace use of Callback<> with MakeAgileCallback<>. 64 | Will return null on failure, translate that into E_OUTOFMEMORY using XXX_IF_NULL_ALLOC() 65 | from wil\result.h to test the result. */ 66 | template 67 | ::Microsoft::WRL::ComPtr MakeAgileCallbackNoThrow(Args&&... args) WI_NOEXCEPT 68 | { 69 | using namespace Microsoft::WRL; 70 | return Callback, TDelegateInterface, FtmBase>>(wistd::forward(args)...); 71 | } 72 | 73 | #ifdef WIL_ENABLE_EXCEPTIONS 74 | template 75 | ::Microsoft::WRL::ComPtr MakeAgileCallback(Args&&... args) 76 | { 77 | auto result = MakeAgileCallbackNoThrow(wistd::forward(args)...); 78 | THROW_IF_NULL_ALLOC(result); 79 | return result; 80 | } 81 | #endif // WIL_ENABLE_EXCEPTIONS 82 | } // namespace wil 83 | 84 | #endif // __WIL_WRL_INCLUDED 85 | --------------------------------------------------------------------------------