#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef _WIN32 #include #endif const size_t XMAIOBegin = 0x7FEA0000; const size_t XMAIOEnd = XMAIOBegin + 0x0000FFFF; Memory g_memory; Heap g_userHeap; XDBFWrapper g_xdbfWrapper; std::unordered_map g_xdbfTextureCache; void HostStartup() { #ifdef _WIN32 CoInitializeEx(nullptr, COINIT_MULTITHREADED); #endif g_userHeap.Init(); hid::Init(); } // Name inspired from nt's entry point void KiSystemStartup() { const auto gameContent = XamMakeContent(XCONTENTTYPE_RESERVED, "Game"); const auto updateContent = XamMakeContent(XCONTENTTYPE_RESERVED, "Update"); XamRegisterContent(gameContent, GAME_INSTALL_DIRECTORY "/game"); XamRegisterContent(updateContent, GAME_INSTALL_DIRECTORY "/update"); const auto saveFilePath = GetSaveFilePath(true); bool saveFileExists = std::filesystem::exists(saveFilePath); if (!saveFileExists) { // Copy base save data to modded save as fallback. std::error_code ec; std::filesystem::create_directories(saveFilePath.parent_path(), ec); if (!ec) { std::filesystem::copy_file(GetSaveFilePath(false), saveFilePath, ec); saveFileExists = !ec; } } if (saveFileExists) { std::u8string savePathU8 = saveFilePath.parent_path().u8string(); XamRegisterContent(XamMakeContent(XCONTENTTYPE_SAVEDATA, "SYS-DATA"), (const char*)(savePathU8.c_str())); } // Mount game XamContentCreateEx(0, "game", &gameContent, OPEN_EXISTING, nullptr, nullptr, 0, 0, nullptr); XamContentCreateEx(0, "update", &updateContent, OPEN_EXISTING, nullptr, nullptr, 0, 0, nullptr); // OS mounts game data to D: XamContentCreateEx(0, "D", &gameContent, OPEN_EXISTING, nullptr, nullptr, 0, 0, nullptr); std::error_code ec; for (auto& file : std::filesystem::directory_iterator(GAME_INSTALL_DIRECTORY "/dlc", ec)) { if (file.is_directory()) { std::u8string fileNameU8 = file.path().filename().u8string(); std::u8string filePathU8 = file.path().u8string(); XamRegisterContent(XamMakeContent(XCONTENTTYPE_DLC, (const char*)(fileNameU8.c_str())), (const char*)(filePathU8.c_str())); } } XAudioInitializeSystem(); } uint32_t LdrLoadModule(const std::filesystem::path &path) { auto loadResult = LoadFile(path); if (loadResult.empty()) { assert("Failed to load module" && false); return 0; } auto* header = reinterpret_cast(loadResult.data()); auto* security = reinterpret_cast(loadResult.data() + header->securityOffset); const auto* fileFormatInfo = reinterpret_cast(getOptHeaderPtr(loadResult.data(), XEX_HEADER_FILE_FORMAT_INFO)); auto entry = *reinterpret_cast(getOptHeaderPtr(loadResult.data(), XEX_HEADER_ENTRY_POINT)); ByteSwapInplace(entry); auto srcData = loadResult.data() + header->headerSize; auto destData = reinterpret_cast(g_memory.Translate(security->loadAddress)); if (fileFormatInfo->compressionType == XEX_COMPRESSION_NONE) { memcpy(destData, srcData, security->imageSize); } else if (fileFormatInfo->compressionType == XEX_COMPRESSION_BASIC) { auto* blocks = reinterpret_cast(fileFormatInfo + 1); const size_t numBlocks = (fileFormatInfo->infoSize / sizeof(Xex2FileBasicCompressionInfo)) - 1; for (size_t i = 0; i < numBlocks; i++) { memcpy(destData, srcData, blocks[i].dataSize); srcData += blocks[i].dataSize; destData += blocks[i].dataSize; memset(destData, 0, blocks[i].zeroSize); destData += blocks[i].zeroSize; } } else { assert(false && "Unknown compression type."); } auto res = reinterpret_cast(getOptHeaderPtr(loadResult.data(), XEX_HEADER_RESOURCE_INFO)); g_xdbfWrapper = XDBFWrapper((uint8_t*)g_memory.Translate(res->offset.get()), res->sizeOfData); return entry; } int main(int argc, char *argv[]) { #ifdef _WIN32 timeBeginPeriod(1); #endif if (!os::registry::Init()) LOGN_WARNING("OS doesn't support registry"); os::logger::Init(); bool forceInstaller = false; bool forceDLCInstaller = false; const char *sdlVideoDriver = nullptr; for (uint32_t i = 1; i < argc; i++) { forceInstaller = forceInstaller || (strcmp(argv[i], "--install") == 0); forceDLCInstaller = forceDLCInstaller || (strcmp(argv[i], "--install-dlc") == 0); if (strcmp(argv[i], "--sdl-video-driver") == 0) { if ((i + 1) < argc) sdlVideoDriver = argv[++i]; else LOGN_WARNING("No argument was specified for --sdl-video-driver. Option will be ignored."); } } Config::Load(); // Check the time since the last time an update was checked. Store the new time if the difference is more than six hours. constexpr double TimeBetweenUpdateChecksInSeconds = 6 * 60 * 60; time_t timeNow = std::time(nullptr); double timeDifferenceSeconds = difftime(timeNow, Config::LastChecked); if (timeDifferenceSeconds > TimeBetweenUpdateChecksInSeconds) { UpdateChecker::initialize(); UpdateChecker::start(); Config::LastChecked = timeNow; Config::Save(); } if (Config::ShowConsole) os::process::ShowConsole(); HostStartup(); std::filesystem::path modulePath; bool isGameInstalled = Installer::checkGameInstall(GAME_INSTALL_DIRECTORY, modulePath); bool runInstallerWizard = forceInstaller || forceDLCInstaller || !isGameInstalled; if (runInstallerWizard) { if (!Video::CreateHostDevice(sdlVideoDriver)) { SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, GameWindow::GetTitle(), Localise("Video_BackendError").c_str(), GameWindow::s_pWindow); std::_Exit(1); } if (!InstallerWizard::Run(GAME_INSTALL_DIRECTORY, isGameInstalled && forceDLCInstaller)) { std::_Exit(0); } } ModLoader::Init(); KiSystemStartup(); uint32_t entry = LdrLoadModule(modulePath); if (!runInstallerWizard) { if (!Video::CreateHostDevice(sdlVideoDriver)) { SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, GameWindow::GetTitle(), Localise("Video_BackendError").c_str(), GameWindow::s_pWindow); std::_Exit(1); } } Video::StartPipelinePrecompilation(); GuestThread::Start({ entry, 0, 0 }); return 0; } GUEST_FUNCTION_STUB(__imp__vsprintf); GUEST_FUNCTION_STUB(__imp___vsnprintf); GUEST_FUNCTION_STUB(__imp__sprintf); GUEST_FUNCTION_STUB(__imp___snprintf); GUEST_FUNCTION_STUB(__imp___snwprintf); GUEST_FUNCTION_STUB(__imp__vswprintf); GUEST_FUNCTION_STUB(__imp___vscwprintf); GUEST_FUNCTION_STUB(__imp__swprintf);