parent
4a77a4fc06
commit
b38fabd65b
|
@ -1240,6 +1240,27 @@ namespace GakumasLocal::HookMain {
|
|||
return ret;
|
||||
}
|
||||
|
||||
DEFINE_HOOK(void*, WindowHandle_SetWindowLong, (int32_t nIndex, intptr_t dwNewLong, void* mtd)) {
|
||||
if (nIndex == GWL_STYLE) {
|
||||
// printf("GWL_STYLE\n");
|
||||
|
||||
HWND hwnd = FindWindowW(L"UnityWndClass", L"gakumas");
|
||||
LONG_PTR style = GetWindowLongPtr(hwnd, GWL_STYLE);
|
||||
style |= WS_OVERLAPPEDWINDOW;
|
||||
SetWindowLongPtr(hwnd, GWL_STYLE, style);
|
||||
dwNewLong = style;
|
||||
}
|
||||
return WindowHandle_SetWindowLong_Orig(nIndex, dwNewLong, mtd);
|
||||
}
|
||||
|
||||
DEFINE_HOOK(void*, AspectRatioHandler_WindowProc, (intptr_t hWnd,
|
||||
uint32_t msg,
|
||||
intptr_t wParam,
|
||||
intptr_t lParam, void* mtd)) {
|
||||
|
||||
return AspectRatioHandler_WindowProc_Orig(hWnd, msg, wParam, lParam, mtd);
|
||||
}
|
||||
|
||||
|
||||
void UpdateSwingBreastBonesData(void* initializeData) {
|
||||
if (!Config::enableBreastParam) return;
|
||||
|
@ -1634,6 +1655,11 @@ namespace GakumasLocal::HookMain {
|
|||
"RenderPipeline", "EndCameraRendering"));
|
||||
|
||||
#ifdef GKMS_WINDOWS
|
||||
ADD_HOOK(WindowHandle_SetWindowLong, Il2cppUtils::GetMethodPointer("Assembly-CSharp.dll", "Campus.Common.StandAloneWindow",
|
||||
"WindowHandle", "SetWindowLong"));
|
||||
ADD_HOOK(AspectRatioHandler_WindowProc, Il2cppUtils::GetMethodPointer("Assembly-CSharp.dll", "Campus.Common.StandAloneWindow",
|
||||
"AspectRatioHandler", "WindowProc"));
|
||||
|
||||
g_extra_assetbundle_paths.push_back((gakumasLocalPath / "local-files/gakumasassets").string());
|
||||
LoadExtraAssetBundle();
|
||||
GkmsResourceUpdate::CheckUpdateFromAPI(false);
|
||||
|
|
|
@ -56,7 +56,7 @@ namespace I18nData {
|
|||
{ "axisx_y", "axisX.y" },
|
||||
{ "axisy_y", "axisY.y" },
|
||||
{ "axisz_y", "axisZ.y" },
|
||||
{ "basic_settings", "Basic Ssettings" },
|
||||
{ "basic_settings", "Basic Settings" },
|
||||
{ "graphic_settings", "Graphic Settings" },
|
||||
{ "camera_settings", "Camera Settings" },
|
||||
{ "test_mode_live", "Test Mode - LIVE" },
|
||||
|
|
|
@ -130,13 +130,16 @@ namespace GkmsResourceUpdate {
|
|||
return content;
|
||||
}
|
||||
|
||||
bool unzipFileFromURL(std::string downloadUrl, const std::string& unzipPath) {
|
||||
bool unzipFileFromURL(std::string downloadUrl, const std::string& unzipPath, const std::string& targetDir = "") {
|
||||
std::string tempZipFile = (gakumasLocalPath / "temp_download.zip").string();
|
||||
if (std::filesystem::exists(tempZipFile)) {
|
||||
std::filesystem::remove(tempZipFile);
|
||||
}
|
||||
if (!DownloadFile(downloadUrl, tempZipFile)) {
|
||||
GakumasLocal::Log::Error("Download zip file failed.");
|
||||
return false;
|
||||
}
|
||||
if (!UnzipFile(tempZipFile, unzipPath)) {
|
||||
if (!UnzipFile(tempZipFile, unzipPath, targetDir)) {
|
||||
GakumasLocal::Log::Error("Unzip file failed.");
|
||||
return false;
|
||||
}
|
||||
|
@ -214,9 +217,9 @@ namespace GkmsResourceUpdate {
|
|||
}).detach();
|
||||
}
|
||||
|
||||
void checkUpdateFromURL(std::string downloadUrl) {
|
||||
std::thread([&downloadUrl]() {
|
||||
if (unzipFileFromURL(downloadUrl, gakumasLocalPath.string())) {
|
||||
void checkUpdateFromURL(const std::string& downloadUrl) {
|
||||
std::thread([downloadUrl]() {
|
||||
if (unzipFileFromURL(downloadUrl, gakumasLocalPath.string(), "local-files")) {
|
||||
g_reload_all_data();
|
||||
GakumasLocal::Log::Info("Update completed.");
|
||||
}
|
||||
|
|
|
@ -6,5 +6,5 @@ namespace GkmsResourceUpdate {
|
|||
void saveProgramConfig();
|
||||
std::string GetCurrentResourceVersion(bool useCache);
|
||||
void CheckUpdateFromAPI(bool isManual);
|
||||
void checkUpdateFromURL(std::string downloadUrl);
|
||||
void checkUpdateFromURL(const std::string& downloadUrl);
|
||||
}
|
|
@ -147,3 +147,181 @@ static bool UnzipFile(const std::string& zipPath, const std::string& destination
|
|||
unzClose(zipfile);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
static bool UnzipFile(const std::string& zipPath, const std::string& destinationFolder, std::string_view targetDir) {
|
||||
if (targetDir.empty()) {
|
||||
return UnzipFile(zipPath, destinationFolder);
|
||||
}
|
||||
|
||||
// 打开 zip 文件
|
||||
unzFile zipfile = unzOpen(zipPath.c_str());
|
||||
if (zipfile == nullptr) {
|
||||
GakumasLocal::Log::ErrorFmt("Can't open zip file: %s", zipPath.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
// 用于标识:ZIP 根目录是否直接包含 targetDir 文件夹
|
||||
bool targetAtRoot = false;
|
||||
// 如果在根目录下没找到,再尝试记录哪个根目录下包含 targetDir(只检查一层)
|
||||
std::string candidateRoot;
|
||||
|
||||
// 第一遍扫描:遍历所有条目,判断目标文件夹所在位置
|
||||
int ret = unzGoToFirstFile(zipfile);
|
||||
if (ret != UNZ_OK) {
|
||||
GakumasLocal::Log::ErrorFmt("Can't read first file: %s", zipPath.c_str());
|
||||
unzClose(zipfile);
|
||||
return false;
|
||||
}
|
||||
do {
|
||||
char filename[512] = { 0 };
|
||||
unz_file_info fileInfo{};
|
||||
ret = unzGetCurrentFileInfo(zipfile, &fileInfo,
|
||||
filename, sizeof(filename),
|
||||
nullptr, 0, nullptr, 0);
|
||||
if (ret != UNZ_OK) {
|
||||
GakumasLocal::Log::ErrorFmt("Read ZIP File Info Error");
|
||||
unzClose(zipfile);
|
||||
return false;
|
||||
}
|
||||
std::string entryName = filename;
|
||||
// 将 '\' 统一替换为 '/'
|
||||
std::replace(entryName.begin(), entryName.end(), '\\', '/');
|
||||
|
||||
// 若条目全名等于 targetDir(可以带或不带结尾 '/'),且没有前缀,则认为在根目录
|
||||
if ((entryName == targetDir || entryName == std::string(targetDir) + "/") &&
|
||||
entryName.find('/') == entryName.size() - 1) {
|
||||
targetAtRoot = true;
|
||||
}
|
||||
else {
|
||||
// 对于含有路径分隔符的条目,提取第一级目录
|
||||
if (auto pos = entryName.find('/'); pos != std::string::npos) {
|
||||
std::string root = entryName.substr(0, pos);
|
||||
// remainder 为 root 后面的路径
|
||||
std::string remainder = entryName.substr(pos + 1);
|
||||
// 仅检查第一层目录
|
||||
auto pos2 = remainder.find('/');
|
||||
std::string firstSubDir = (pos2 != std::string::npos) ? remainder.substr(0, pos2) : remainder;
|
||||
if (firstSubDir == targetDir) {
|
||||
candidateRoot = root;
|
||||
}
|
||||
}
|
||||
}
|
||||
ret = unzGoToNextFile(zipfile);
|
||||
} while (ret == UNZ_OK);
|
||||
|
||||
// 根据扫描结果确定 extractionPrefix
|
||||
// 若目标文件夹在根目录,则仅解压出 targetDir 内的条目(写入时去掉 targetDir 前缀)
|
||||
// 否则,若 candidateRoot 不为空,则解压 candidateRoot 内的所有条目(写入时去除 candidateRoot 前缀)
|
||||
std::string extractionPrefix;
|
||||
if (targetAtRoot) {
|
||||
extractionPrefix = std::string(targetDir) + "/";
|
||||
}
|
||||
else if (!candidateRoot.empty()) {
|
||||
extractionPrefix = candidateRoot + "/";
|
||||
}
|
||||
else {
|
||||
GakumasLocal::Log::ErrorFmt("Target directory '%.*s' not found in zip", (int)targetDir.size(), targetDir.data());
|
||||
unzClose(zipfile);
|
||||
return false;
|
||||
}
|
||||
|
||||
// 重新打开 zip 文件进行解压(或重置扫描位置)
|
||||
unzClose(zipfile);
|
||||
zipfile = unzOpen(zipPath.c_str());
|
||||
if (zipfile == nullptr) {
|
||||
GakumasLocal::Log::ErrorFmt("Can't reopen zip file: %s", zipPath.c_str());
|
||||
return false;
|
||||
}
|
||||
ret = unzGoToFirstFile(zipfile);
|
||||
if (ret != UNZ_OK) {
|
||||
GakumasLocal::Log::ErrorFmt("Can't read first file: %s", zipPath.c_str());
|
||||
unzClose(zipfile);
|
||||
return false;
|
||||
}
|
||||
|
||||
// 第二遍扫描:仅解压属于指定根目录内的条目
|
||||
do {
|
||||
char filename[512] = { 0 };
|
||||
unz_file_info fileInfo{};
|
||||
ret = unzGetCurrentFileInfo(zipfile, &fileInfo,
|
||||
filename, sizeof(filename),
|
||||
nullptr, 0, nullptr, 0);
|
||||
if (ret != UNZ_OK) {
|
||||
GakumasLocal::Log::ErrorFmt("Read ZIP File Info Error");
|
||||
unzClose(zipfile);
|
||||
return false;
|
||||
}
|
||||
std::string entryName = filename;
|
||||
std::replace(entryName.begin(), entryName.end(), '\\', '/');
|
||||
|
||||
// 仅处理以 extractionPrefix 开头的条目
|
||||
if (entryName.rfind(extractionPrefix, 0) == 0) {
|
||||
// 去除 extractionPrefix 得到相对路径
|
||||
std::string relativePath = entryName.substr(extractionPrefix.size());
|
||||
|
||||
// 构造输出全路径
|
||||
std::string fullPath = destinationFolder;
|
||||
if (!fullPath.empty() && fullPath.back() != '/' && fullPath.back() != '\\')
|
||||
fullPath += "/";
|
||||
fullPath += relativePath;
|
||||
|
||||
// 如果 relativePath 为空或以 '/' 结尾,认为是目录
|
||||
if (relativePath.empty() || relativePath.back() == '/') {
|
||||
if (!CreateDirectoryRecursively(fullPath)) {
|
||||
GakumasLocal::Log::ErrorFmt("Create Dir Failed: %s", fullPath.c_str());
|
||||
unzClose(zipfile);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else {
|
||||
// 确保上级目录存在
|
||||
if (auto pos = fullPath.find_last_of("/\\"); pos != std::string::npos) {
|
||||
std::string directory = fullPath.substr(0, pos);
|
||||
if (!CreateDirectoryRecursively(directory)) {
|
||||
GakumasLocal::Log::ErrorFmt("Create Dir Failed: %s", directory.c_str());
|
||||
unzClose(zipfile);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
ret = unzOpenCurrentFile(zipfile);
|
||||
if (ret != UNZ_OK) {
|
||||
GakumasLocal::Log::ErrorFmt("Open file in zip failed: %s", entryName.c_str());
|
||||
unzClose(zipfile);
|
||||
return false;
|
||||
}
|
||||
FILE* outFile = fopen(fullPath.c_str(), "wb");
|
||||
if (outFile == nullptr) {
|
||||
GakumasLocal::Log::ErrorFmt("Can't create output file: %s", fullPath.c_str());
|
||||
unzCloseCurrentFile(zipfile);
|
||||
unzClose(zipfile);
|
||||
return false;
|
||||
}
|
||||
constexpr int bufferSize = 8192;
|
||||
std::vector<char> buffer(bufferSize);
|
||||
int bytesRead = 0;
|
||||
while ((bytesRead = unzReadCurrentFile(zipfile, buffer.data(), bufferSize)) > 0) {
|
||||
if (fwrite(buffer.data(), 1, bytesRead, outFile) != static_cast<size_t>(bytesRead)) {
|
||||
GakumasLocal::Log::ErrorFmt("Write File Error: %s", fullPath.c_str());
|
||||
fclose(outFile);
|
||||
unzCloseCurrentFile(zipfile);
|
||||
unzClose(zipfile);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (bytesRead < 0) {
|
||||
GakumasLocal::Log::ErrorFmt("Read File Error: %s", entryName.c_str());
|
||||
fclose(outFile);
|
||||
unzCloseCurrentFile(zipfile);
|
||||
unzClose(zipfile);
|
||||
return false;
|
||||
}
|
||||
fclose(outFile);
|
||||
unzCloseCurrentFile(zipfile);
|
||||
}
|
||||
}
|
||||
ret = unzGoToNextFile(zipfile);
|
||||
} while (ret == UNZ_OK);
|
||||
unzClose(zipfile);
|
||||
return true;
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue