Skip to content

Commit c524733

Browse files
Merge branch 'main' into crash-check
2 parents 7726afe + acf260a commit c524733

19 files changed

+524
-92
lines changed

UnleashedRecomp/CMakeLists.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -183,6 +183,8 @@ set(UNLEASHED_RECOMP_USER_CXX_SOURCES
183183
"user/config.cpp"
184184
"user/registry.cpp"
185185
"user/paths.cpp"
186+
"user/persistent_data.cpp"
187+
"user/persistent_storage_manager.cpp"
186188
)
187189

188190
set(UNLEASHED_RECOMP_MOD_CXX_SOURCES

UnleashedRecomp/api/SWA/System/ApplicationDocument.h

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -80,9 +80,13 @@ namespace SWA
8080
boost::shared_ptr<Hedgehog::Mirage::CRenderScene> m_spRenderScene;
8181
SWA_INSERT_PADDING(0x04);
8282
boost::shared_ptr<CGameParameter> m_spGameParameter;
83-
SWA_INSERT_PADDING(0x78);
83+
SWA_INSERT_PADDING(0x0C);
84+
boost::anonymous_shared_ptr m_spItemParamManager;
85+
SWA_INSERT_PADDING(0x64);
8486
boost::shared_ptr<Hedgehog::Base::CCriticalSection> m_spCriticalSection;
85-
SWA_INSERT_PADDING(0x20);
87+
SWA_INSERT_PADDING(0x14);
88+
bool m_ShowDLCInfo;
89+
SWA_INSERT_PADDING(0x08);
8690
};
8791

8892
// TODO: Hedgehog::Base::TSynchronizedPtr<CApplicationDocument>
@@ -111,7 +115,9 @@ namespace SWA
111115
SWA_ASSERT_OFFSETOF(CApplicationDocument::CMember, m_Field10C, 0x10C);
112116
SWA_ASSERT_OFFSETOF(CApplicationDocument::CMember, m_spRenderScene, 0x12C);
113117
SWA_ASSERT_OFFSETOF(CApplicationDocument::CMember, m_spGameParameter, 0x138);
118+
SWA_ASSERT_OFFSETOF(CApplicationDocument::CMember, m_spItemParamManager, 0x14C);
114119
SWA_ASSERT_OFFSETOF(CApplicationDocument::CMember, m_spCriticalSection, 0x1B8);
120+
SWA_ASSERT_OFFSETOF(CApplicationDocument::CMember, m_ShowDLCInfo, 0x1D4);
115121
SWA_ASSERT_SIZEOF(CApplicationDocument::CMember, 0x1E0);
116122

117123
SWA_ASSERT_OFFSETOF(CApplicationDocument, m_pMember, 0x04);

UnleashedRecomp/api/SWA/System/GameParameter.h

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,19 @@ namespace SWA
77
class CGameParameter // : public Hedgehog::Universe::CMessageActor
88
{
99
public:
10-
struct SSaveData;
10+
struct SSaveData
11+
{
12+
SWA_INSERT_PADDING(0x8600);
13+
be<uint32_t> DLCFlags[8];
14+
SWA_INSERT_PADDING(0x15C);
15+
};
16+
1117
struct SStageParameter;
1218

1319
SWA_INSERT_PADDING(0x94);
1420
xpointer<SSaveData> m_pSaveData;
1521
xpointer<SStageParameter> m_pStageParameter;
1622
};
23+
24+
SWA_ASSERT_OFFSETOF(CGameParameter::SSaveData, DLCFlags, 0x8600);
1725
}

UnleashedRecomp/hid/driver/sdl_hid.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -310,6 +310,8 @@ void hid::Init()
310310
SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_PS5_RUMBLE, "1");
311311
SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_WII, "1");
312312
SDL_SetHint(SDL_HINT_XINPUT_ENABLED, "1");
313+
314+
SDL_SetHint(SDL_HINT_GAMECONTROLLER_USE_BUTTON_LABELS, "0"); // Uses Button Labels. This hint is disabled for Nintendo Controllers.
313315

314316
SDL_InitSubSystem(SDL_INIT_EVENTS);
315317
SDL_AddEventWatch(HID_OnSDLEvent, nullptr);

UnleashedRecomp/install/installer.cpp

Lines changed: 162 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,73 @@ static std::unique_ptr<VirtualFileSystem> createFileSystemFromPath(const std::fi
6767
}
6868
}
6969

70+
static bool checkFile(const FilePair &pair, const uint64_t *fileHashes, const std::filesystem::path &targetDirectory, std::vector<uint8_t> &fileData, Journal &journal, const std::function<bool()> &progressCallback, bool checkSizeOnly) {
71+
const std::string fileName(pair.first);
72+
const uint32_t hashCount = pair.second;
73+
const std::filesystem::path filePath = targetDirectory / fileName;
74+
if (!std::filesystem::exists(filePath))
75+
{
76+
journal.lastResult = Journal::Result::FileMissing;
77+
journal.lastErrorMessage = fmt::format("File {} does not exist.", fileName);
78+
return false;
79+
}
80+
81+
std::error_code ec;
82+
size_t fileSize = std::filesystem::file_size(filePath, ec);
83+
if (ec)
84+
{
85+
journal.lastResult = Journal::Result::FileReadFailed;
86+
journal.lastErrorMessage = fmt::format("Failed to read file size for {}.", fileName);
87+
return false;
88+
}
89+
90+
if (checkSizeOnly)
91+
{
92+
journal.progressTotal += fileSize;
93+
}
94+
else
95+
{
96+
std::ifstream fileStream(filePath, std::ios::binary);
97+
if (fileStream.is_open())
98+
{
99+
fileData.resize(fileSize);
100+
fileStream.read((char *)(fileData.data()), fileSize);
101+
}
102+
103+
if (!fileStream.is_open() || fileStream.bad())
104+
{
105+
journal.lastResult = Journal::Result::FileReadFailed;
106+
journal.lastErrorMessage = fmt::format("Failed to read file {}.", fileName);
107+
return false;
108+
}
109+
110+
uint64_t fileHash = XXH3_64bits(fileData.data(), fileSize);
111+
bool fileHashFound = false;
112+
for (uint32_t i = 0; i < hashCount && !fileHashFound; i++)
113+
{
114+
fileHashFound = fileHash == fileHashes[i];
115+
}
116+
117+
if (!fileHashFound)
118+
{
119+
journal.lastResult = Journal::Result::FileHashFailed;
120+
journal.lastErrorMessage = fmt::format("File {} did not match any of the known hashes.", fileName);
121+
return false;
122+
}
123+
124+
journal.progressCounter += fileSize;
125+
}
126+
127+
if (!progressCallback())
128+
{
129+
journal.lastResult = Journal::Result::Cancelled;
130+
journal.lastErrorMessage = "Check was cancelled.";
131+
return false;
132+
}
133+
134+
return true;
135+
}
136+
70137
static bool copyFile(const FilePair &pair, const uint64_t *fileHashes, VirtualFileSystem &sourceVfs, const std::filesystem::path &targetDirectory, bool skipHashChecks, std::vector<uint8_t> &fileData, Journal &journal, const std::function<bool()> &progressCallback) {
71138
const std::string filename(pair.first);
72139
const uint32_t hashCount = pair.second;
@@ -204,6 +271,45 @@ static DLC detectDLC(const std::filesystem::path &sourcePath, VirtualFileSystem
204271
}
205272
}
206273

274+
static bool fillDLCSource(DLC dlc, Installer::DLCSource &dlcSource)
275+
{
276+
switch (dlc)
277+
{
278+
case DLC::Spagonia:
279+
dlcSource.filePairs = { SpagoniaFiles, SpagoniaFilesSize };
280+
dlcSource.fileHashes = SpagoniaHashes;
281+
dlcSource.targetSubDirectory = SpagoniaDirectory;
282+
return true;
283+
case DLC::Chunnan:
284+
dlcSource.filePairs = { ChunnanFiles, ChunnanFilesSize };
285+
dlcSource.fileHashes = ChunnanHashes;
286+
dlcSource.targetSubDirectory = ChunnanDirectory;
287+
return true;
288+
case DLC::Mazuri:
289+
dlcSource.filePairs = { MazuriFiles, MazuriFilesSize };
290+
dlcSource.fileHashes = MazuriHashes;
291+
dlcSource.targetSubDirectory = MazuriDirectory;
292+
return true;
293+
case DLC::Holoska:
294+
dlcSource.filePairs = { HoloskaFiles, HoloskaFilesSize };
295+
dlcSource.fileHashes = HoloskaHashes;
296+
dlcSource.targetSubDirectory = HoloskaDirectory;
297+
return true;
298+
case DLC::ApotosShamar:
299+
dlcSource.filePairs = { ApotosShamarFiles, ApotosShamarFilesSize };
300+
dlcSource.fileHashes = ApotosShamarHashes;
301+
dlcSource.targetSubDirectory = ApotosShamarDirectory;
302+
return true;
303+
case DLC::EmpireCityAdabat:
304+
dlcSource.filePairs = { EmpireCityAdabatFiles, EmpireCityAdabatFilesSize };
305+
dlcSource.fileHashes = EmpireCityAdabatHashes;
306+
dlcSource.targetSubDirectory = EmpireCityAdabatDirectory;
307+
return true;
308+
default:
309+
return false;
310+
}
311+
}
312+
207313
bool Installer::checkGameInstall(const std::filesystem::path &baseDirectory, std::filesystem::path &modulePath)
208314
{
209315
modulePath = baseDirectory / PatchedDirectory / GameExecutableFile;
@@ -254,6 +360,40 @@ bool Installer::checkAllDLC(const std::filesystem::path& baseDirectory)
254360
return result;
255361
}
256362

363+
bool Installer::checkInstallIntegrity(const std::filesystem::path &baseDirectory, Journal &journal, const std::function<bool()> &progressCallback)
364+
{
365+
// Run the file checks twice: once to fill out the progress counter and the file sizes, and another pass to do the hash integrity checks.
366+
for (uint32_t checkPass = 0; checkPass < 2; checkPass++)
367+
{
368+
bool checkSizeOnly = (checkPass == 0);
369+
if (!checkFiles({ GameFiles, GameFilesSize }, GameHashes, baseDirectory / GameDirectory, journal, progressCallback, checkSizeOnly))
370+
{
371+
return false;
372+
}
373+
374+
if (!checkFiles({ UpdateFiles, UpdateFilesSize }, UpdateHashes, baseDirectory / UpdateDirectory, journal, progressCallback, checkSizeOnly))
375+
{
376+
return false;
377+
}
378+
379+
for (int i = 1; i < (int)DLC::Count; i++)
380+
{
381+
if (checkDLCInstall(baseDirectory, (DLC)i))
382+
{
383+
Installer::DLCSource dlcSource;
384+
fillDLCSource((DLC)i, dlcSource);
385+
386+
if (!checkFiles(dlcSource.filePairs, dlcSource.fileHashes, baseDirectory / dlcSource.targetSubDirectory, journal, progressCallback, checkSizeOnly))
387+
{
388+
return false;
389+
}
390+
}
391+
}
392+
}
393+
394+
return true;
395+
}
396+
257397
bool Installer::computeTotalSize(std::span<const FilePair> filePairs, const uint64_t *fileHashes, VirtualFileSystem &sourceVfs, Journal &journal, uint64_t &totalSize)
258398
{
259399
for (FilePair pair : filePairs)
@@ -272,6 +412,27 @@ bool Installer::computeTotalSize(std::span<const FilePair> filePairs, const uint
272412
return true;
273413
}
274414

415+
bool Installer::checkFiles(std::span<const FilePair> filePairs, const uint64_t *fileHashes, const std::filesystem::path &targetDirectory, Journal &journal, const std::function<bool()> &progressCallback, bool checkSizeOnly)
416+
{
417+
FilePair validationPair = {};
418+
uint32_t validationHashIndex = 0;
419+
uint32_t hashIndex = 0;
420+
uint32_t hashCount = 0;
421+
std::vector<uint8_t> fileData;
422+
for (FilePair pair : filePairs)
423+
{
424+
hashIndex = hashCount;
425+
hashCount += pair.second;
426+
427+
if (!checkFile(pair, &fileHashes[hashIndex], targetDirectory, fileData, journal, progressCallback, checkSizeOnly))
428+
{
429+
return false;
430+
}
431+
}
432+
433+
return true;
434+
}
435+
275436
bool Installer::copyFiles(std::span<const FilePair> filePairs, const uint64_t *fileHashes, VirtualFileSystem &sourceVfs, const std::filesystem::path &targetDirectory, const std::string &validationFile, bool skipHashChecks, Journal &journal, const std::function<bool()> &progressCallback)
276437
{
277438
std::error_code ec;
@@ -387,39 +548,8 @@ bool Installer::parseSources(const Input &input, Journal &journal, Sources &sour
387548
}
388549

389550
DLC dlc = detectDLC(path, *dlcSource.sourceVfs, journal);
390-
switch (dlc)
551+
if (!fillDLCSource(dlc, dlcSource))
391552
{
392-
case DLC::Spagonia:
393-
dlcSource.filePairs = { SpagoniaFiles, SpagoniaFilesSize };
394-
dlcSource.fileHashes = SpagoniaHashes;
395-
dlcSource.targetSubDirectory = SpagoniaDirectory;
396-
break;
397-
case DLC::Chunnan:
398-
dlcSource.filePairs = { ChunnanFiles, ChunnanFilesSize };
399-
dlcSource.fileHashes = ChunnanHashes;
400-
dlcSource.targetSubDirectory = ChunnanDirectory;
401-
break;
402-
case DLC::Mazuri:
403-
dlcSource.filePairs = { MazuriFiles, MazuriFilesSize };
404-
dlcSource.fileHashes = MazuriHashes;
405-
dlcSource.targetSubDirectory = MazuriDirectory;
406-
break;
407-
case DLC::Holoska:
408-
dlcSource.filePairs = { HoloskaFiles, HoloskaFilesSize };
409-
dlcSource.fileHashes = HoloskaHashes;
410-
dlcSource.targetSubDirectory = HoloskaDirectory;
411-
break;
412-
case DLC::ApotosShamar:
413-
dlcSource.filePairs = { ApotosShamarFiles, ApotosShamarFilesSize };
414-
dlcSource.fileHashes = ApotosShamarHashes;
415-
dlcSource.targetSubDirectory = ApotosShamarDirectory;
416-
break;
417-
case DLC::EmpireCityAdabat:
418-
dlcSource.filePairs = { EmpireCityAdabatFiles, EmpireCityAdabatFilesSize };
419-
dlcSource.fileHashes = EmpireCityAdabatHashes;
420-
dlcSource.targetSubDirectory = EmpireCityAdabatDirectory;
421-
break;
422-
default:
423553
return false;
424554
}
425555

UnleashedRecomp/install/installer.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,9 @@ struct Installer
7575
static bool checkGameInstall(const std::filesystem::path &baseDirectory, std::filesystem::path &modulePath);
7676
static bool checkDLCInstall(const std::filesystem::path &baseDirectory, DLC dlc);
7777
static bool checkAllDLC(const std::filesystem::path &baseDirectory);
78+
static bool checkInstallIntegrity(const std::filesystem::path &baseDirectory, Journal &journal, const std::function<bool()> &progressCallback);
7879
static bool computeTotalSize(std::span<const FilePair> filePairs, const uint64_t *fileHashes, VirtualFileSystem &sourceVfs, Journal &journal, uint64_t &totalSize);
80+
static bool checkFiles(std::span<const FilePair> filePairs, const uint64_t *fileHashes, const std::filesystem::path &targetDirectory, Journal &journal, const std::function<bool()> &progressCallback, bool checkSizeOnly);
7981
static bool copyFiles(std::span<const FilePair> filePairs, const uint64_t *fileHashes, VirtualFileSystem &sourceVfs, const std::filesystem::path &targetDirectory, const std::string &validationFile, bool skipHashChecks, Journal &journal, const std::function<bool()> &progressCallback);
8082
static bool parseContent(const std::filesystem::path &sourcePath, std::unique_ptr<VirtualFileSystem> &targetVfs, Journal &journal);
8183
static bool parseSources(const Input &input, Journal &journal, Sources &sources);

UnleashedRecomp/locale/locale.cpp

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -703,6 +703,28 @@ std::unordered_map<std::string_view, std::unordered_map<ELanguage, std::string>>
703703
{ ELanguage::Italian, "Impossibile trovare il modulo \"%s\".\n\nAssicurati che:\n\n- Hai estratto questa copia di Unleashed Recompiled correttamente e non solo il file *.exe.\n- Non stai eseguendo Unleashed Recompiled da un file *.zip." }
704704
}
705705
},
706+
{
707+
"IntegrityCheck_Success",
708+
{
709+
{ ELanguage::English, "Installation check has finished.\n\nAll files seem to be correct.\n\nThe game will now close. Remove the launch argument to play the game." },
710+
{ ELanguage::Japanese, "インストールチェックが終了しました\n\nすべてのファイルは正しいようです\n\nゲームは終了します、ゲームをプレイするには起動引数を削除してください" },
711+
{ ELanguage::German, "Die Installation wurde überprüft.\n\nAlle Dateien scheinen korrekt zu sein.\n\nDas Spiel wird nun geschlossen. Entferne die Startoption, um das Spiel zu spielen." },
712+
{ ELanguage::French, "La vérification de l'installation est terminée.\n\nTous les fichiers semblent corrects.\n\nL'application va maintenant se fermer. Retirez l'argument de lancement pour pouvoir lancer le jeu." },
713+
{ ELanguage::Spanish, "La verificación de la instalación ha terminado.\n\nTodos los archivos parecen correctos.\n\nEl juego se cerrará ahora. Elimina el argumento de lanzamiento para jugar al juego." },
714+
{ ELanguage::Italian, "La verifica dei file d'installazione è terminata.\n\nTutti i file sembrano corretti.\n\nIl gioco si chiuderà. Rimuovi l'argomento di avvio per poter giocare." }
715+
}
716+
},
717+
{
718+
"IntegrityCheck_Failed",
719+
{
720+
{ ELanguage::English, "Installation check has failed.\n\nError: %s\n\nThe game will now close. Try reinstalling the game by using the --install launch argument." },
721+
{ ELanguage::Japanese, "インストールチェックに失敗しました\n\nエラー:%s\n\nゲームは終了します、--install 起動引数を使用してゲームを再インストールしてください" },
722+
{ ELanguage::German, "Die Installationsprüfung ist fehlgeschlagen.\n\nFehler: %s\n\nDas Spiel wird nun geschlossen. Versuche das Spiel durch Verwendung der Startoption --install neu zu installieren." },
723+
{ ELanguage::French, "La vérification de l'installation a échoué.\n\nErreur : %s\n\nL'application va maintenant se fermer. Essayez de réinstaller le jeu en utilisant l'argument de lancement --install." },
724+
{ ELanguage::Spanish, "La verificación de la instalación ha fallado.\n\nError: %s\n\nEl juego se cerrará ahora. Intenta reinstalar el juego utilizando el argumento de lanzamiento --install." },
725+
{ ELanguage::Italian, "La verifica dei file d'installazione non è andata a buon fine.\n\nErrore: %s\n\nIl gioco si chiuderà. Prova a reinstallare il gioco utilizzando l'argomento di avvio --install." }
726+
}
727+
},
706728
{
707729
"Common_On",
708730
{

0 commit comments

Comments
 (0)