mirror of
https://github.com/Dingo-Shenanigans/ReSkate.git
synced 2026-10-17 17:27:19 +02:00
Mod archives (Engine/Vfs/mod_merge*) - A free archive index no longer has to sit at or below the highest index the layout already declares. The engine keeps archives in a table keyed by layer, install chunk and a 16-bit index, with no range to stay inside; what it cannot survive is a reference to an archive the layout never declared. Indices are taken lowest first, per package directory. - So a mod that collides takes an index of its own and its file is hard linked rather than concatenated, and no two mods share one archive's 4 GB of addressable bytes. Sharing is left for a mod added while the game runs, which has no declared index to take, and for a directory with no index left. - A placement addresses its payload with a 32-bit offset, so both append paths now refuse to cross 4 GB instead of wrapping round and pointing the game at some other payload: CasStore::write fails with the archive that is full, and the live-apply path checks archive 1's size plus the mod's before appending and asks for a restart, which gives the mod an archive of its own anyway. Injection through a hooked LoadLibraryW (Engine/Core/Platform) - A player could not launch at all: "Child LoadLibraryW entry bytes match neither Windows' kernel32 file nor this process". Anti-virus and overlays hook the entry with a jump to a per-process trampoline, so the bytes differ in the launcher and in the child and neither comparison could match. The guard already allowed the same hook when both processes carried it identically, so it refused on an accident of how the hook was written rather than on intent. - inline_jump_length recognises the forms a hooking engine writes (rel32 jmp or call, push/ret, jmp [rip], mov rax/jmp rax, short jmp). When one is present and every byte after it still matches the clean kernel32 file, the launch goes ahead: the body is Windows' own, and the provider image already matched on timestamp, checksum and size. A wholesale replacement is still refused, and now says so. - The failure names the modules loaded into Skate that came from neither Windows nor the game folder, instead of guessing at "security software". An accepted hook is logged as a warning with the same list. Launching (Launcher/launch.cpp) - Exporting loose scripts is a convenience, and a folder that would not take it was failing the whole launch: fs::create_directories threw and nothing caught it. The export is skipped, RESKATE_LOOSE_FILES goes to 0 so the runtime does not look for files that are not there, and Skate starts. Someone whose install is under Program Files can play; only editing loose scripts is lost. Failures worth reading (Launcher/problem.h) - A hooked LoadLibraryW, a refused write and ERROR_ELEVATION_REQUIRED each explain themselves now. Windows reports the last two in the user's own language -- "Zugriff verweigert", "La operación solicitada requiere elevación" -- so the rules key on the operation names and the error number, which are never translated. Saved skaters (Extension/Customization, Extension/Profile) - Outfits stopped saving and every slot showed the standard skater. The load hook marks a preset blocked on entry and clears it only on a full success, so a load refused for a catalog that was not ready yet left the preset blocked for the session and every later save was refused as previous_load_rejected. The game asks once per level load and does not ask again, so one lost race cost a skater until a restart. Logs show the catalog arriving three seconds after the refused load, at startup and again on a map switch 17 minutes later. - The record is remembered in CosmeticRuntime::pending_loads and applied by retry_pending_cosmetic_loads from the customization pump, which already waits for refresh_cosmetic_catalog. The preset stays blocked until a load really succeeds, so nothing is written over the outfit on disk. - Those records belong to the loadout manager's array, which can be replaced in between, so each pointer is checked against the live array exactly as a save checks one -- manager current, begin/end sane, 0x48 stride, at most 10 records, in range and aligned. A record that no longer belongs is dropped as retry_record_moved rather than written through.
115 lines
7.0 KiB
C++
115 lines
7.0 KiB
C++
#pragma once
|
|
|
|
#include <string>
|
|
#include <string_view>
|
|
|
|
// Failures are logged in the words that help whoever reads ReSkate.log, which
|
|
// are rarely the words that help whoever is trying to skate. This turns one
|
|
// into the other: what went wrong, and what to do about it. The raw message
|
|
// stays in the log, and the STATUS tile can still copy it.
|
|
namespace dingosdk::launcher_problem {
|
|
|
|
struct Problem {
|
|
std::string headline; // what went wrong, in a line
|
|
std::string advice; // the next thing to try
|
|
};
|
|
|
|
inline bool mentions(std::string_view raw, std::string_view needle) {
|
|
return raw.find(needle) != std::string_view::npos;
|
|
}
|
|
|
|
// Anti-virus and overlays are behind most injection failures, so they share
|
|
// one piece of advice.
|
|
inline constexpr std::string_view attach_advice =
|
|
"Anti-virus software usually blocks this. Add your skate. folder as an exclusion, close overlays "
|
|
"(Discord, MSI Afterburner, RTSS), then press RETRY.";
|
|
|
|
inline Problem explain(std::string_view raw) {
|
|
// Most specific first: the later rules match whole families of failures.
|
|
if (mentions(raw, "must be beside ReSkateLauncher.exe"))
|
|
return {"ReSkate is not in your skate. folder",
|
|
"Put ReSkateLauncher.exe and ReSkate.dll beside Skate.exe, in the folder Steam opens with "
|
|
"skate. > Manage > Browse local files."};
|
|
if (mentions(raw, "ReSkate.dll is missing"))
|
|
return {"ReSkate.dll is missing",
|
|
"Extract ReSkateLauncher.exe and ReSkate.dll from the same release zip into this folder, "
|
|
"then press RETRY."};
|
|
if (mentions(raw, "ReSkate.dll is not an x64") || mentions(raw, "DingoSDKDebugInitialize is outside"))
|
|
return {"ReSkate.dll is damaged",
|
|
"Download the release zip again and extract both files over this folder. Your anti-virus may "
|
|
"also have quarantined part of it."};
|
|
if (mentions(raw, "Skate.exe is missing"))
|
|
return {"Skate.exe is not in this folder",
|
|
"Run the launcher from your skate. folder, or press INSTALL to download the game here."};
|
|
if (mentions(raw, "steam_api64.dll is missing") || mentions(raw, "steam_api64"))
|
|
return {"The original steam_api64.dll is missing",
|
|
"Verify the game files in Steam, or press DOWNLOAD to fetch the supported build again."};
|
|
if (mentions(raw, "This launcher is out of date"))
|
|
return {"This launcher is out of date",
|
|
"Download the newest ReSkate release from GitHub and extract it over this folder."};
|
|
if (mentions(raw, "update server could not be reached"))
|
|
return {"The ReSkate update server could not be reached",
|
|
"Check your internet connection and press RETRY. If the game files are already correct you can "
|
|
"turn on Offline mode in Settings and play without the check."};
|
|
if (mentions(raw, "not supported"))
|
|
return {"This copy of skate. is not the supported build",
|
|
"Press DOWNLOAD to get the supported build with your Steam account. Only the files that differ "
|
|
"are downloaded."};
|
|
if (mentions(raw, "Steam download did not finish"))
|
|
return {"The Steam download did not finish",
|
|
"Check that the account you signed in with owns skate. and that you have about 14 GB free, "
|
|
"then press DOWNLOAD again. Signing in again often clears it."};
|
|
if (mentions(raw, "installing the game content cache"))
|
|
return {"Another ReSkate launcher is busy",
|
|
"It is installing the game data this build needs. Wait for it to finish, then press RETRY."};
|
|
if (mentions(raw, "content cache"))
|
|
return {"The one-time game data download failed",
|
|
"ReSkate downloads the game's catalogues once per build. Check your internet connection and "
|
|
"press RETRY."};
|
|
if (mentions(raw, "need different keys"))
|
|
return {"The menu and console share a key",
|
|
"Open Settings > KEYS and pick a different key for one of them."};
|
|
// ERROR_ELEVATION_REQUIRED. Windows localises the text, so match the
|
|
// number win32_failure puts in front of it.
|
|
if (mentions(raw, "failed (740)"))
|
|
return {"Skate.exe is set to always run as administrator",
|
|
"ReSkate has to start Skate itself, and Windows does not let it start a program marked to "
|
|
"need administrator. Right-click Skate.exe, pick Properties, then Compatibility, and untick "
|
|
"\"Run this program as an administrator\". Starting ReSkateLauncher.exe as "
|
|
"administrator works too."};
|
|
if (mentions(raw, "LoadLibraryW is hooked")) {
|
|
std::string advice = "Security software or an overlay is modifying Skate while it starts, and ReSkate will "
|
|
"not load itself through that. Allow ReSkateLauncher.exe and Skate.exe in it, or close "
|
|
"it, then press RETRY.";
|
|
// The launcher named what it found in Skate; that is the useful half.
|
|
if (const auto found = raw.find("Loaded into Skate"); found != std::string_view::npos)
|
|
advice += " " + std::string(raw.substr(found));
|
|
return {"Something is hooking Skate as it starts", std::move(advice)};
|
|
}
|
|
if (mentions(raw, "Skate closed while it was starting"))
|
|
return {"Skate closed while it was starting", std::string(attach_advice)};
|
|
if (mentions(raw, "timed out after"))
|
|
return {"Skate took too long to start",
|
|
"A slow disk or an anti-virus scan usually causes this. Press RETRY; if it keeps happening, add "
|
|
"your skate. folder as an exclusion."};
|
|
if (mentions(raw, "validated Skate.exe") || mentions(raw, "loaded-image") || mentions(raw, "DOS header") ||
|
|
mentions(raw, "module list") || mentions(raw, "VirtualAllocEx") || mentions(raw, "NtQueryInformationProcess") ||
|
|
mentions(raw, "DingoSDKDebugInitialize") || mentions(raw, "Remote "))
|
|
return {"ReSkate could not attach to Skate", std::string(attach_advice)};
|
|
// Windows reports a refused write in the user's own language, so these
|
|
// key on the operation names and the path, which are never translated.
|
|
const bool write_failure = mentions(raw, "create_directories") || mentions(raw, "create_directory") ||
|
|
mentions(raw, "Cannot write") || mentions(raw, "Cannot create") ||
|
|
mentions(raw, "Cannot publish") || mentions(raw, "Cannot append") ||
|
|
mentions(raw, "Cannot replace");
|
|
if (write_failure)
|
|
return {"ReSkate cannot write to its own folder",
|
|
"Move the whole ReSkate folder somewhere else, such as C:\\Games\\ReSkate, and start it from "
|
|
"there. Program Files and OneDrive do not let it write."};
|
|
return {std::string(raw),
|
|
"Press RETRY. If it keeps happening, copy the details below and share them with logs\\ReSkate.log "
|
|
"on the ReSkate Discord."};
|
|
}
|
|
|
|
} // namespace dingosdk::launcher_problem
|