Skip to content

Commit

Permalink
Merge branch 'main' into fix/mad-cleanup-handles
Browse files Browse the repository at this point in the history
  • Loading branch information
Alystrasz committed Sep 7, 2024
2 parents 6d5e9eb + 8824340 commit 5f0be57
Show file tree
Hide file tree
Showing 4 changed files with 109 additions and 22 deletions.
40 changes: 40 additions & 0 deletions primedev/client/audio.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -509,13 +509,53 @@ static void __fastcall h_MilesLog(int level, const char* string)
spdlog::info("[MSS] {} - {}", level, string);
}

static void(__fastcall* o_pSub_18003EBD0)(DWORD dwThreadID, const char* threadName) = nullptr;
static void __fastcall h_Sub_18003EBD0(DWORD dwThreadID, const char* threadName)
{
HANDLE hThread = OpenThread(THREAD_SET_LIMITED_INFORMATION, FALSE, dwThreadID);

if (hThread != NULL)
{
// TODO: This "method" of "charset conversion" from string to wstring is abhorrent. Change it to a proper one
// as soon as Northstar has some helper function to do proper charset conversions.
auto tmp = std::string(threadName);
HRESULT WINAPI _SetThreadDescription(HANDLE hThread, PCWSTR lpThreadDescription);
_SetThreadDescription(hThread, std::wstring(tmp.begin(), tmp.end()).c_str());

CloseHandle(hThread);
}

o_pSub_18003EBD0(dwThreadID, threadName);
}

static char*(__fastcall* o_pSub_18003BC10)(void* a1, void* a2, void* a3, void* a4, void* a5, int a6) = nullptr;
static char* __fastcall h_Sub_18003BC10(void* a1, void* a2, void* a3, void* a4, void* a5, int a6)
{
HANDLE hThread;
char* ret = o_pSub_18003BC10(a1, a2, a3, a4, a5, a6);

if (ret != NULL && (hThread = reinterpret_cast<HANDLE>(*((uint64_t*)ret + 55))) != NULL)
{
HRESULT WINAPI _SetThreadDescription(HANDLE hThread, PCWSTR lpThreadDescription);
_SetThreadDescription(hThread, L"[Miles] WASAPI Service Thread");
}

return ret;
}

ON_DLL_LOAD("mileswin64.dll", MilesWin64_Audio, (CModule module))
{
o_pLoadSampleMetadata = module.Offset(0xF110).RCast<decltype(o_pLoadSampleMetadata)>();
HookAttach(&(PVOID&)o_pLoadSampleMetadata, (PVOID)h_LoadSampleMetadata);

o_pSub_1800294C0 = module.Offset(0x294C0).RCast<decltype(o_pSub_1800294C0)>();
HookAttach(&(PVOID&)o_pSub_1800294C0, (PVOID)h_Sub_1800294C0);

o_pSub_18003EBD0 = module.Offset(0x3EBD0).RCast<decltype(o_pSub_18003EBD0)>();
HookAttach(&(PVOID&)o_pSub_18003EBD0, (PVOID)h_Sub_18003EBD0);

o_pSub_18003BC10 = module.Offset(0x3BC10).RCast<decltype(o_pSub_18003BC10)>();
HookAttach(&(PVOID&)o_pSub_18003BC10, (PVOID)h_Sub_18003BC10);
}

ON_DLL_LOAD_RELIESON("engine.dll", MilesLogFuncHooks, ConVar, (CModule module))
Expand Down
29 changes: 29 additions & 0 deletions primedev/core/tier0.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,40 @@ void TryCreateGlobalMemAlloc()
g_pMemAllocSingleton = CreateGlobalMemAlloc(); // if it already exists, this returns the preexisting IMemAlloc instance
}

HRESULT WINAPI _SetThreadDescription(HANDLE hThread, PCWSTR lpThreadDescription)
{
// need to grab it dynamically as this function was only introduced at some point in Windows 10
static decltype(&SetThreadDescription) _SetThreadDescription =
CModule("KernelBase.dll").GetExportedFunction("SetThreadDescription").RCast<decltype(&SetThreadDescription)>();

if (_SetThreadDescription)
return _SetThreadDescription(hThread, lpThreadDescription);

return ERROR_OLD_WIN_VERSION;
}

static void(__fastcall* o_pThreadSetDebugName)(HANDLE threadHandle, const char* name) = nullptr;
static void __fastcall h_ThreadSetDebugName(HANDLE threadHandle, const char* name)
{
if (threadHandle == 0)
threadHandle = GetCurrentThread();

// TODO: This "method" of "charset conversion" from string to wstring is abhorrent. Change it to a proper one
// as soon as Northstar has some helper function to do proper charset conversions.
auto tmp = std::string(name);
_SetThreadDescription(threadHandle, std::wstring(tmp.begin(), tmp.end()).c_str());

o_pThreadSetDebugName(threadHandle, name);
}

ON_DLL_LOAD("tier0.dll", Tier0GameFuncs, (CModule module))
{
// shouldn't be necessary, but do this just in case
TryCreateGlobalMemAlloc();

o_pThreadSetDebugName = module.GetExportedFunction("ThreadSetDebugName").RCast<decltype(o_pThreadSetDebugName)>();
HookAttach(&(PVOID&)o_pThreadSetDebugName, (PVOID)h_ThreadSetDebugName);

// setup tier0 funcs
CommandLine = module.GetExportedFunction("CommandLine").RCast<CommandLineType>();
Plat_FloatTime = module.GetExportedFunction("Plat_FloatTime").RCast<Plat_FloatTimeType>();
Expand Down
42 changes: 26 additions & 16 deletions primedev/mods/autodownload/moddownloader.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -103,34 +103,38 @@ void ModDownloader::FetchModsListFromAPI()
for (auto i = verifiedModsJson.MemberBegin(); i != verifiedModsJson.MemberEnd(); ++i)
{
// Format testing
if (!i->value.HasMember("DependencyPrefix") || !i->value.HasMember("Versions"))
if (!i->value.HasMember("Repository") || !i->value.HasMember("Versions"))
{
spdlog::warn("Verified mods manifesto format is unrecognized, skipping loading.");
return;
}

std::string name = i->name.GetString();
std::string dependency = i->value["DependencyPrefix"].GetString();

std::unordered_map<std::string, VerifiedModVersion> modVersions;

rapidjson::Value& versions = i->value["Versions"];
assert(versions.IsArray());
for (auto& attribute : versions.GetArray())
{
assert(attribute.IsObject());
// Format testing
if (!attribute.HasMember("Version") || !attribute.HasMember("Checksum"))
if (!attribute.HasMember("Version") || !attribute.HasMember("Checksum") || !attribute.HasMember("DownloadLink") ||
!attribute.HasMember("Platform"))
{
spdlog::warn("Verified mods manifesto format is unrecognized, skipping loading.");
return;
}

std::string version = attribute["Version"].GetString();
std::string checksum = attribute["Checksum"].GetString();
modVersions.insert({version, {.checksum = checksum}});
std::string downloadLink = attribute["DownloadLink"].GetString();
std::string platformValue = attribute["Platform"].GetString();
VerifiedModPlatform platform =
platformValue.compare("thunderstore") == 0 ? VerifiedModPlatform::Thunderstore : VerifiedModPlatform::Unknown;
modVersions.insert({version, {.checksum = checksum, .downloadLink = downloadLink, .platform = platform}});
}

VerifiedModDetails modConfig = {.dependencyPrefix = dependency, .versions = modVersions};
VerifiedModDetails modConfig = {.versions = modVersions};
verifiedMods.insert({name, modConfig});
spdlog::info("==> Loaded configuration for mod \"" + name + "\"");
}
Expand Down Expand Up @@ -164,13 +168,10 @@ int ModDownloader::ModFetchingProgressCallback(
return 0;
}

std::optional<fs::path> ModDownloader::FetchModFromDistantStore(std::string_view modName, std::string_view modVersion)
std::optional<fs::path> ModDownloader::FetchModFromDistantStore(std::string_view modName, VerifiedModVersion version)
{
// Retrieve mod prefix from local mods list, or use mod name as mod prefix if bypass flag is set
std::string modPrefix = strstr(GetCommandLineA(), VERIFICATION_FLAG) ? modName.data() : verifiedMods[modName.data()].dependencyPrefix;
// Build archive distant URI
std::string archiveName = std::format("{}-{}.zip", modPrefix, modVersion.data());
std::string url = STORE_URL + archiveName;
std::string url = version.downloadLink;
std::string archiveName = fs::path(url).filename().generic_string();
spdlog::info(std::format("Fetching mod archive from {}", url));

// Download destination
Expand Down Expand Up @@ -390,7 +391,7 @@ int GetModArchiveSize(unzFile file, unz_global_info64 info)
return totalSize;
}

void ModDownloader::ExtractMod(fs::path modPath, fs::path destinationPath)
void ModDownloader::ExtractMod(fs::path modPath, fs::path destinationPath, VerifiedModPlatform platform)
{
unzFile file;

Expand Down Expand Up @@ -426,6 +427,14 @@ void ModDownloader::ExtractMod(fs::path modPath, fs::path destinationPath)
modState.total = GetModArchiveSize(file, gi);
modState.progress = 0;

// Right now, we only know how to extract Thunderstore mods
if (platform != VerifiedModPlatform::Thunderstore)
{
spdlog::error("Failed extracting mod from unknown platform (value: {}).", platform);
modState.state = UNKNOWN_PLATFORM;
return;
}

for (int i = 0; i < gi.number_entry; i++)
{
char zipFilename[256];
Expand Down Expand Up @@ -609,8 +618,9 @@ void ModDownloader::DownloadMod(std::string modName, std::string modVersion)
});

// Download mod archive
std::string expectedHash = verifiedMods[modName].versions[modVersion].checksum;
std::optional<fs::path> fetchingResult = FetchModFromDistantStore(std::string_view(modName), std::string_view(modVersion));
VerifiedModVersion fullVersion = verifiedMods[modName].versions[modVersion];
std::string expectedHash = fullVersion.checksum;
std::optional<fs::path> fetchingResult = FetchModFromDistantStore(std::string_view(modName), fullVersion);
if (!fetchingResult.has_value())
{
spdlog::error("Something went wrong while fetching archive, aborting.");
Expand All @@ -631,7 +641,7 @@ void ModDownloader::DownloadMod(std::string modName, std::string modVersion)
modDirectory = GetRemoteModFolderPath() / name;

// Extract downloaded mod archive
ExtractMod(archiveLocation, modDirectory);
ExtractMod(archiveLocation, modDirectory, fullVersion.platform);
});

requestThread.detach();
Expand Down
20 changes: 14 additions & 6 deletions primedev/mods/autodownload/moddownloader.h
Original file line number Diff line number Diff line change
Expand Up @@ -5,17 +5,22 @@ class ModDownloader
private:
const char* VERIFICATION_FLAG = "-disablemodverification";
const char* CUSTOM_MODS_URL_FLAG = "-customverifiedurl=";
const char* STORE_URL = "https://gcdn.thunderstore.io/live/repository/packages/";
const char* DEFAULT_MODS_LIST_URL = "https://raw.githubusercontent.com/R2Northstar/VerifiedMods/main/verified-mods.json";
char* modsListUrl;

enum class VerifiedModPlatform
{
Unknown,
Thunderstore
};
struct VerifiedModVersion
{
std::string checksum;
std::string downloadLink;
VerifiedModPlatform platform;
};
struct VerifiedModDetails
{
std::string dependencyPrefix;
std::unordered_map<std::string, VerifiedModVersion> versions = {};
};
std::unordered_map<std::string, VerifiedModDetails> verifiedMods = {};
Expand Down Expand Up @@ -45,7 +50,7 @@ class ModDownloader
* @param modVersion version of the mod to be downloaded
* @returns location of the downloaded archive
*/
std::optional<fs::path> FetchModFromDistantStore(std::string_view modName, std::string_view modVersion);
std::optional<fs::path> FetchModFromDistantStore(std::string_view modName, VerifiedModVersion modVersion);

/**
* Tells if a mod archive has not been corrupted.
Expand All @@ -65,13 +70,15 @@ class ModDownloader
* Extracts a mod archive to the game folder.
*
* This extracts a downloaded mod archive from its original location, `modPath`,
* to the specified `destinationPath`.
* to the specified `destinationPath`; the way to decompress the archive is
* defined by the `platform` parameter.
*
* @param modPath location of the downloaded archive
* @param destinationPath destination of the extraction
* @param platform origin of the downloaded archive
* @returns nothing
*/
void ExtractMod(fs::path modPath, fs::path destinationPath);
void ExtractMod(fs::path modPath, fs::path destinationPath, VerifiedModPlatform platform);

public:
ModDownloader();
Expand Down Expand Up @@ -132,7 +139,8 @@ class ModDownloader
MOD_FETCHING_FAILED,
MOD_CORRUPTED, // Downloaded archive checksum does not match verified hash
NO_DISK_SPACE_AVAILABLE,
NOT_FOUND // Mod is not currently being auto-downloaded
NOT_FOUND, // Mod is not currently being auto-downloaded
UNKNOWN_PLATFORM
};

struct MOD_STATE
Expand Down

0 comments on commit 5f0be57

Please sign in to comment.