mirror of
https://github.com/izzy2lost/Shipwright-Android.git
synced 2026-07-05 15:19:26 -07:00
8.0.2 Update and Controller Reconnect Fix
This commit is contained in:
+2
-2
@@ -16,8 +16,8 @@ android {
|
||||
}
|
||||
minSdkVersion 18
|
||||
targetSdkVersion 31
|
||||
versionCode 2
|
||||
versionName "1.1.0"
|
||||
versionCode 3
|
||||
versionName "1.1.1"
|
||||
externalNativeBuild {
|
||||
//ndkBuild {
|
||||
// arguments "APP_PLATFORM=android-23"
|
||||
|
||||
@@ -5,8 +5,8 @@ set(CMAKE_CXX_STANDARD 20 CACHE STRING "The C++ standard to use")
|
||||
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++20 ")
|
||||
|
||||
project(Game VERSION 8.0.0 LANGUAGES C CXX)
|
||||
set(PROJECT_BUILD_NAME "MacReady Alfa" CACHE STRING "")
|
||||
project(Game VERSION 8.0.2 LANGUAGES C CXX)
|
||||
set(PROJECT_BUILD_NAME "MacReady Charlie" CACHE STRING "")
|
||||
set(PROJECT_TEAM "github.com/harbourmasters" CACHE STRING "")
|
||||
|
||||
add_subdirectory(src)
|
||||
|
||||
@@ -5,8 +5,8 @@ set(CMAKE_CXX_STANDARD 20 CACHE STRING "The C++ standard to use")
|
||||
|
||||
set(CMAKE_OSX_DEPLOYMENT_TARGET "10.15" CACHE STRING "Minimum OS X deployment version")
|
||||
|
||||
project(Ship VERSION 8.0.0 LANGUAGES C CXX)
|
||||
set(PROJECT_BUILD_NAME "MacReady Alfa" CACHE STRING "")
|
||||
project(Ship VERSION 8.0.2 LANGUAGES C CXX)
|
||||
set(PROJECT_BUILD_NAME "MacReady Charlie" CACHE STRING "")
|
||||
set(PROJECT_TEAM "github.com/harbourmasters" CACHE STRING "")
|
||||
|
||||
set_property(DIRECTORY ${CMAKE_SOURCE_DIR} PROPERTY VS_STARTUP_PROJECT soh)
|
||||
|
||||
@@ -333,10 +333,6 @@ if (CMAKE_SYSTEM_NAME STREQUAL "Darwin")
|
||||
set(CMAKE_FIND_FRAMEWORK LAST)
|
||||
endif()
|
||||
|
||||
if (CMAKE_SYSTEM_NAME STREQUAL "Darwin")
|
||||
set(CMAKE_FIND_FRAMEWORK LAST)
|
||||
endif()
|
||||
|
||||
if (NOT CMAKE_SYSTEM_NAME STREQUAL "Android")
|
||||
find_package(PNG REQUIRED)
|
||||
endif()
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#include "CrashHandler.h"
|
||||
#include "Utils/StringHelper.h"
|
||||
|
||||
#if __has_include(<unistd.h>)
|
||||
#if __has_include(<unistd.h>) && !defined(__ANDROID__)
|
||||
#define HAS_POSIX 1
|
||||
#else
|
||||
#define HAS_POSIX 0
|
||||
@@ -25,6 +25,8 @@
|
||||
#include <inttypes.h>
|
||||
|
||||
#pragma comment(lib, "Dbghelp.lib")
|
||||
#else
|
||||
#include "WarningHandler.h"
|
||||
#endif
|
||||
|
||||
// Feel free to add more crash messages.
|
||||
@@ -50,7 +52,7 @@ static std::array<const char* const, 14> crashEasterEgg = {
|
||||
"\tOh! MY GOD!!",
|
||||
};
|
||||
|
||||
#if HAS_POSIX == 1 && !defined(__ANDROID__)
|
||||
#if HAS_POSIX == 1
|
||||
void ErrorHandler(int sig)
|
||||
{
|
||||
std::array<void*, 4096> arr;
|
||||
@@ -193,12 +195,12 @@ LONG seh_filter(_EXCEPTION_POINTERS* ex)
|
||||
|
||||
void CrashHandler_Init()
|
||||
{
|
||||
#if HAS_POSIX == 1 && !defined(__ANDROID__)
|
||||
#if HAS_POSIX == 1
|
||||
signal(SIGSEGV, ErrorHandler);
|
||||
signal(SIGABRT, ErrorHandler);
|
||||
#elif defined(_MSC_VER)
|
||||
SetUnhandledExceptionFilter(seh_filter);
|
||||
#elif !defined(__ANDROID__)
|
||||
#else
|
||||
HANDLE_WARNING(WarningType::Always,
|
||||
"tried to set error handler, but this ZAPD build lacks support for one", "");
|
||||
#endif
|
||||
|
||||
+1
-1
Submodule app/jni/src/libultraship updated: eb2c774355...8a9c48fa66
@@ -1,9 +1,9 @@
|
||||
|
||||
#define VER_FILEVERSION 8, 0, 0, 0
|
||||
#define VER_FILEVERSION_STR "8.0.0\0"
|
||||
#define VER_FILEVERSION 8, 0, 2, 0
|
||||
#define VER_FILEVERSION_STR "8.0.2\0"
|
||||
|
||||
#define VER_PRODUCTVERSION 8, 0, 0, 0
|
||||
#define VER_PRODUCTVERSION_str "8.0.0\0"
|
||||
#define VER_PRODUCTVERSION 8, 0, 2, 0
|
||||
#define VER_PRODUCTVERSION_str "8.0.2\0"
|
||||
|
||||
#define VER_COMPANYNAME_STR "github.com/harbourmasters\0"
|
||||
#define VER_PRODUCTNAME_STR "Ship of Harkinian\0"
|
||||
@@ -11,4 +11,4 @@
|
||||
#define VER_INTERNALNAME_STR "soh\0"
|
||||
#define VER_ORIGINALFILENAME_STR "soh.exe\0"
|
||||
|
||||
#define VER_FILEDESCRIPTION_STR "Ship of Harkinian - MacReady Alfa\0"
|
||||
#define VER_FILEDESCRIPTION_STR "Ship of Harkinian - MacReady Charlie\0"
|
||||
|
||||
@@ -439,7 +439,7 @@ static std::unordered_map<u16, const char*> actorDescriptions = {
|
||||
{ ACTOR_EN_DAIKU_KAKARIKO, "Carpenters (Kakariko)" },
|
||||
{ ACTOR_BG_BOWL_WALL, "Bombchu Bowling Alley Wall" },
|
||||
{ ACTOR_EN_WALL_TUBO, "Bombchu Bowling Alley Bullseyes" },
|
||||
{ ACTOR_EN_PO_DESERT, "Poe Guide (Desert Wasteland)" },
|
||||
{ ACTOR_EN_PO_DESERT, "Poe Guide (Haunted Wasteland)" },
|
||||
{ ACTOR_EN_CROW, "Guay" },
|
||||
{ ACTOR_DOOR_KILLER, "Fake Door" },
|
||||
{ ACTOR_BG_SPOT11_OASIS, "Oasis (Desert Colossus)" },
|
||||
|
||||
@@ -400,8 +400,9 @@ void AudioCollection::InitializeShufflePool() {
|
||||
if (shufflePoolInitialized) return;
|
||||
|
||||
for (auto& [seqId, seqInfo] : sequenceMap) {
|
||||
if (!seqInfo.canBeUsedAsReplacement) continue;
|
||||
const std::string cvarKey = "gAudioEditor.Excluded." + seqInfo.sfxKey;
|
||||
if (CVarGetInteger(cvarKey.c_str(), 0) && !seqInfo.canBeUsedAsReplacement) {
|
||||
if (CVarGetInteger(cvarKey.c_str(), 0)) {
|
||||
excludedSequences.insert(&seqInfo);
|
||||
} else {
|
||||
includedSequences.insert(&seqInfo);
|
||||
|
||||
@@ -1767,6 +1767,10 @@ void CosmeticsEditorWindow::DrawElement() {
|
||||
ImGui::SameLine();
|
||||
UIWidgets::EnhancementCombobox("gCosmetics.DefaultColorScheme", colorSchemes, COLORSCHEME_N64);
|
||||
UIWidgets::EnhancementCheckbox("Advanced Mode", "gCosmetics.AdvancedMode");
|
||||
UIWidgets::InsertHelpHoverText(
|
||||
"Some cosmetic options may not apply if you have any mods that provide custom models for the cosmetic option.\n\n"
|
||||
"For example, if you have custom Link model, then the Link's Hair color option will most likely not apply."
|
||||
);
|
||||
if (CVarGetInteger("gCosmetics.AdvancedMode", 0)) {
|
||||
if (ImGui::Button("Lock All Advanced", ImVec2(ImGui::GetContentRegionAvail().x / 2, 30.0f))) {
|
||||
for (auto& [id, cosmeticOption] : cosmeticOptions) {
|
||||
|
||||
@@ -380,13 +380,13 @@ CrowdControl::Effect* CrowdControl::ParseMessage(char payload[512]) {
|
||||
effect->category = kEffectCatBoots;
|
||||
effect->timeRemaining = 30000;
|
||||
effect->giEffect = new GameInteractionEffect::ForceEquipBoots();
|
||||
effect->giEffect->parameters[0] = PLAYER_BOOTS_IRON;
|
||||
effect->giEffect->parameters[0] = EQUIP_VALUE_BOOTS_IRON;
|
||||
break;
|
||||
case kEffectForceHoverBoots:
|
||||
effect->category = kEffectCatBoots;
|
||||
effect->timeRemaining = 30000;
|
||||
effect->giEffect = new GameInteractionEffect::ForceEquipBoots();
|
||||
effect->giEffect->parameters[0] = PLAYER_BOOTS_HOVER;
|
||||
effect->giEffect->parameters[0] = EQUIP_VALUE_BOOTS_HOVER;
|
||||
break;
|
||||
case kEffectSlipperyFloor:
|
||||
effect->category = kEffectCatSlipperyFloor;
|
||||
|
||||
@@ -207,6 +207,7 @@ static bool ResetHandler(std::shared_ptr<LUS::Console> Console, std::vector<std:
|
||||
return 1;
|
||||
}
|
||||
|
||||
gPlayState->gameplayFrames = 0;
|
||||
SET_NEXT_GAMESTATE(&gPlayState->state, TitleSetup_Init, GameState);
|
||||
gPlayState->state.running = false;
|
||||
GameInteractor::Instance->ExecuteHooks<GameInteractor::OnExitGame>(gSaveContext.fileNum);
|
||||
|
||||
@@ -35,8 +35,6 @@ extern PlayState* gPlayState;
|
||||
extern void Overlay_DisplayText(float duration, const char* text);
|
||||
uint32_t ResourceMgr_IsSceneMasterQuest(s16 sceneNum);
|
||||
}
|
||||
bool performDelayedSave = false;
|
||||
bool performSave = false;
|
||||
|
||||
// TODO: When there's more uses of something like this, create a new GI::RawAction?
|
||||
void ReloadSceneTogglingLinkAge() {
|
||||
@@ -258,14 +256,12 @@ void RegisterOcarinaTimeTravel() {
|
||||
|
||||
void AutoSave(GetItemEntry itemEntry) {
|
||||
u8 item = itemEntry.itemId;
|
||||
bool performSave = false;
|
||||
// Don't autosave immediately after buying items from shops to prevent getting them for free!
|
||||
// Don't autosave in the Chamber of Sages since resuming from that map breaks the game
|
||||
// Don't autosave during the Ganon fight when picking up the Master Sword
|
||||
// Don't autosave in the fishing pond to prevent getting rod on B outside of the pond
|
||||
// Don't autosave in the bombchu bowling alley to prevent having chus on B outside of the minigame
|
||||
// Don't autosave in grottos since resuming from grottos breaks the game.
|
||||
if ((CVarGetInteger("gAutosave", AUTOSAVE_OFF) != AUTOSAVE_OFF) && (gPlayState != NULL) && (gSaveContext.pendingSale == ITEM_NONE) &&
|
||||
(gPlayState->gameplayFrames > 60 && gSaveContext.cutsceneIndex < 0xFFF0) && (gPlayState->sceneNum != SCENE_GANON_BOSS)) {
|
||||
(gPlayState->gameplayFrames > 60 && gSaveContext.cutsceneIndex < 0xFFF0) && (gPlayState->sceneNum != SCENE_GANON_BOSS) && (gPlayState->sceneNum != SCENE_CHAMBER_OF_THE_SAGES)) {
|
||||
if (((CVarGetInteger("gAutosave", AUTOSAVE_OFF) == AUTOSAVE_LOCATION_AND_ALL_ITEMS) || (CVarGetInteger("gAutosave", AUTOSAVE_OFF) == AUTOSAVE_ALL_ITEMS)) && (item != ITEM_NONE)) {
|
||||
// Autosave for all items
|
||||
performSave = true;
|
||||
@@ -326,25 +322,9 @@ void AutoSave(GetItemEntry itemEntry) {
|
||||
CVarGetInteger("gAutosave", AUTOSAVE_OFF) == AUTOSAVE_LOCATION) {
|
||||
performSave = true;
|
||||
}
|
||||
if (gPlayState->sceneNum == SCENE_FAIRYS_FOUNTAIN || gPlayState->sceneNum == SCENE_GROTTOS ||
|
||||
gPlayState->sceneNum == SCENE_CHAMBER_OF_THE_SAGES || gPlayState->sceneNum == SCENE_FISHING_POND ||
|
||||
gPlayState->sceneNum == SCENE_BOMBCHU_BOWLING_ALLEY) {
|
||||
if (CVarGetInteger("gAutosave", AUTOSAVE_OFF) == AUTOSAVE_LOCATION_AND_MAJOR_ITEMS ||
|
||||
CVarGetInteger("gAutosave", AUTOSAVE_OFF) == AUTOSAVE_LOCATION_AND_ALL_ITEMS ||
|
||||
CVarGetInteger("gAutosave", AUTOSAVE_OFF) == AUTOSAVE_LOCATION) {
|
||||
performSave = false;
|
||||
return;
|
||||
}
|
||||
if (performSave) {
|
||||
performSave = false;
|
||||
performDelayedSave = true;
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (performSave || performDelayedSave) {
|
||||
if (performSave) {
|
||||
Play_PerformSave(gPlayState);
|
||||
performSave = false;
|
||||
performDelayedSave = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -783,7 +783,7 @@ std::map<RandomizerCheckArea, std::string> rcAreaNames = {
|
||||
{ RCAREA_LAKE_HYLIA, "Lake Hylia"},
|
||||
{ RCAREA_GERUDO_VALLEY, "Gerudo Valley"},
|
||||
{ RCAREA_GERUDO_FORTRESS, "Gerudo Fortress"},
|
||||
{ RCAREA_WASTELAND, "Desert Wasteland"},
|
||||
{ RCAREA_WASTELAND, "Haunted Wasteland"},
|
||||
{ RCAREA_DESERT_COLOSSUS, "Desert Colossus"},
|
||||
{ RCAREA_MARKET, "Hyrule Market"},
|
||||
{ RCAREA_HYRULE_CASTLE, "Hyrule Castle"},
|
||||
|
||||
@@ -452,6 +452,63 @@ bool HasItemBeenCollected(RandomizerCheck rc) {
|
||||
return false;
|
||||
}
|
||||
|
||||
void CheckTrackerLoadGame(int32_t fileNum) {
|
||||
LoadSettings();
|
||||
TrySetAreas();
|
||||
for (auto [rc, rcObj] : RandomizerCheckObjects::GetAllRCObjects()) {
|
||||
RandomizerCheckTrackerData rcTrackerData = gSaveContext.checkTrackerData[rc];
|
||||
if (rc == RC_UNKNOWN_CHECK || rc == RC_MAX || rc == RC_LINKS_POCKET ||
|
||||
!RandomizerCheckObjects::GetAllRCObjects().contains(rc))
|
||||
continue;
|
||||
|
||||
RandomizerCheckObject realRcObj;
|
||||
if (rc == RC_GIFT_FROM_SAGES && !IS_RANDO) {
|
||||
realRcObj = RCO_RAORU;
|
||||
} else {
|
||||
realRcObj = rcObj;
|
||||
}
|
||||
if (!IsVisibleInCheckTracker(realRcObj)) continue;
|
||||
|
||||
checksByArea.find(realRcObj.rcArea)->second.push_back(realRcObj);
|
||||
if (rcTrackerData.status == RCSHOW_SAVED || rcTrackerData.skipped) {
|
||||
areaChecksGotten[realRcObj.rcArea]++;
|
||||
}
|
||||
|
||||
if (areaChecksGotten[realRcObj.rcArea] != 0 || RandomizerCheckObjects::AreaIsOverworld(realRcObj.rcArea)) {
|
||||
areasSpoiled |= (1 << realRcObj.rcArea);
|
||||
}
|
||||
}
|
||||
if (OTRGlobals::Instance->gRandomizer->GetRandoSettingValue(RSK_LINKS_POCKET) != RO_LINKS_POCKET_NOTHING && IS_RANDO) {
|
||||
s8 startingAge = OTRGlobals::Instance->gRandomizer->GetRandoSettingValue(RSK_STARTING_AGE);
|
||||
RandomizerCheckArea startingArea;
|
||||
switch (startingAge) {
|
||||
case RO_AGE_CHILD:
|
||||
startingArea = RCAREA_KOKIRI_FOREST;
|
||||
break;
|
||||
case RO_AGE_ADULT:
|
||||
startingArea = RCAREA_MARKET;
|
||||
break;
|
||||
default:
|
||||
startingArea = RCAREA_KOKIRI_FOREST;
|
||||
break;
|
||||
}
|
||||
RandomizerCheckObject linksPocket = { RC_LINKS_POCKET, RCVORMQ_BOTH, RCTYPE_LINKS_POCKET, startingArea, ACTOR_ID_MAX, SCENE_ID_MAX, 0x00, GI_NONE, false, "Link's Pocket", "Link's Pocket" };
|
||||
|
||||
checksByArea.find(startingArea)->second.push_back(linksPocket);
|
||||
areaChecksGotten[startingArea]++;
|
||||
}
|
||||
|
||||
showVOrMQ = (OTRGlobals::Instance->gRandomizer->GetRandoSettingValue(RSK_RANDOM_MQ_DUNGEONS) == RO_MQ_DUNGEONS_RANDOM_NUMBER ||
|
||||
(OTRGlobals::Instance->gRandomizer->GetRandoSettingValue(RSK_RANDOM_MQ_DUNGEONS) == RO_MQ_DUNGEONS_SET_NUMBER &&
|
||||
OTRGlobals::Instance->gRandomizer->GetRandoSettingValue(RSK_MQ_DUNGEON_COUNT) < 12));
|
||||
LinksPocket();
|
||||
SongFromImpa();
|
||||
GiftFromSages();
|
||||
initialized = true;
|
||||
UpdateAllOrdering();
|
||||
UpdateInventoryChecks();
|
||||
}
|
||||
|
||||
void CheckTrackerDialogClosed() {
|
||||
if (messageCloseCheck) {
|
||||
messageCloseCheck = false;
|
||||
@@ -679,9 +736,6 @@ void SaveFile(SaveContext* saveContext, int sectionID, bool fullSave) {
|
||||
}
|
||||
|
||||
void LoadFile() {
|
||||
Teardown();
|
||||
LoadSettings();
|
||||
TrySetAreas();
|
||||
SaveManager::Instance->LoadArray("checks", RC_MAX, [](size_t i) {
|
||||
SaveManager::Instance->LoadStruct("", [&]() {
|
||||
SaveManager::Instance->LoadData("status", gSaveContext.checkTrackerData[i].status);
|
||||
@@ -689,58 +743,7 @@ void LoadFile() {
|
||||
SaveManager::Instance->LoadData("price", gSaveContext.checkTrackerData[i].price);
|
||||
SaveManager::Instance->LoadData("hintItem", gSaveContext.checkTrackerData[i].hintItem);
|
||||
});
|
||||
RandomizerCheckTrackerData entry = gSaveContext.checkTrackerData[i];
|
||||
RandomizerCheck rc = static_cast<RandomizerCheck>(i);
|
||||
if (rc == RC_UNKNOWN_CHECK || rc == RC_MAX ||
|
||||
!RandomizerCheckObjects::GetAllRCObjects().contains(rc))
|
||||
return;
|
||||
|
||||
RandomizerCheckObject entry2;
|
||||
if (rc == RC_GIFT_FROM_SAGES && !IS_RANDO) {
|
||||
entry2 = RCO_RAORU;
|
||||
} else {
|
||||
entry2 = RandomizerCheckObjects::GetAllRCObjects().find(rc)->second;
|
||||
}
|
||||
if (!IsVisibleInCheckTracker(entry2)) return;
|
||||
|
||||
checksByArea.find(entry2.rcArea)->second.push_back(entry2);
|
||||
if (entry.status == RCSHOW_SAVED || entry.skipped) {
|
||||
areaChecksGotten[entry2.rcArea]++;
|
||||
}
|
||||
|
||||
if (areaChecksGotten[entry2.rcArea] != 0 || RandomizerCheckObjects::AreaIsOverworld(entry2.rcArea)) {
|
||||
areasSpoiled |= (1 << entry2.rcArea);
|
||||
}
|
||||
});
|
||||
if (OTRGlobals::Instance->gRandomizer->GetRandoSettingValue(RSK_LINKS_POCKET) != RO_LINKS_POCKET_NOTHING && IS_RANDO) {
|
||||
s8 startingAge = OTRGlobals::Instance->gRandomizer->GetRandoSettingValue(RSK_STARTING_AGE);
|
||||
RandomizerCheckArea startingArea;
|
||||
switch (startingAge) {
|
||||
case RO_AGE_CHILD:
|
||||
startingArea = RCAREA_KOKIRI_FOREST;
|
||||
break;
|
||||
case RO_AGE_ADULT:
|
||||
startingArea = RCAREA_MARKET;
|
||||
break;
|
||||
default:
|
||||
startingArea = RCAREA_KOKIRI_FOREST;
|
||||
break;
|
||||
}
|
||||
RandomizerCheckObject linksPocket = { RC_LINKS_POCKET, RCVORMQ_BOTH, RCTYPE_LINKS_POCKET, startingArea, ACTOR_ID_MAX, SCENE_ID_MAX, 0x00, GI_NONE, false, "Link's Pocket", "Link's Pocket" };
|
||||
|
||||
checksByArea.find(startingArea)->second.push_back(linksPocket);
|
||||
areaChecksGotten[startingArea]++;
|
||||
}
|
||||
|
||||
showVOrMQ = (OTRGlobals::Instance->gRandomizer->GetRandoSettingValue(RSK_RANDOM_MQ_DUNGEONS) == RO_MQ_DUNGEONS_RANDOM_NUMBER ||
|
||||
(OTRGlobals::Instance->gRandomizer->GetRandoSettingValue(RSK_RANDOM_MQ_DUNGEONS) == RO_MQ_DUNGEONS_SET_NUMBER &&
|
||||
OTRGlobals::Instance->gRandomizer->GetRandoSettingValue(RSK_MQ_DUNGEON_COUNT) < 12));
|
||||
LinksPocket();
|
||||
SongFromImpa();
|
||||
GiftFromSages();
|
||||
initialized = true;
|
||||
UpdateAllOrdering();
|
||||
UpdateInventoryChecks();
|
||||
}
|
||||
|
||||
void Teardown() {
|
||||
@@ -1533,6 +1536,7 @@ void CheckTrackerWindow::InitElement() {
|
||||
SaveManager::Instance->AddInitFunction(InitTrackerData);
|
||||
sectionId = SaveManager::Instance->AddSaveFunction("trackerData", 1, SaveFile, true, -1);
|
||||
SaveManager::Instance->AddLoadFunction("trackerData", 1, LoadFile);
|
||||
GameInteractor::Instance->RegisterGameHook<GameInteractor::OnLoadGame>(CheckTrackerLoadGame);
|
||||
GameInteractor::Instance->RegisterGameHook<GameInteractor::OnExitGame>([](uint32_t fileNum) {
|
||||
Teardown();
|
||||
});
|
||||
|
||||
@@ -219,7 +219,7 @@ std::unordered_map<RandomizerTrickArea, std::string> rtAreaNames = {
|
||||
{ RTAREA_LAKE_HYLIA, "Lake Hylia"},
|
||||
{ RTAREA_GERUDO_VALLEY, "Gerudo Valley"},
|
||||
{ RTAREA_GERUDO_FORTRESS, "Gerudo Fortress"},
|
||||
{ RTAREA_WASTELAND, "Desert Wasteland"},
|
||||
{ RTAREA_WASTELAND, "Haunted Wasteland"},
|
||||
{ RTAREA_DESERT_COLOSSUS, "Desert Colossus"},
|
||||
{ RTAREA_MARKET, "Hyrule Market"},
|
||||
{ RTAREA_HYRULE_CASTLE, "Hyrule Castle"},
|
||||
|
||||
@@ -1569,6 +1569,11 @@ extern "C" Gfx* ResourceMgr_LoadGfxByName(const char* path)
|
||||
return (Gfx*)&res->Instructions[0];
|
||||
}
|
||||
|
||||
extern "C" uint8_t ResourceMgr_FileIsCustomByName(const char* path) {
|
||||
auto res = std::static_pointer_cast<LUS::DisplayList>(GetResourceByNameHandlingMQ(path));
|
||||
return res->GetInitData()->IsCustom;
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
int index;
|
||||
Gfx instruction;
|
||||
@@ -1600,6 +1605,11 @@ extern "C" void ResourceMgr_PatchGfxByName(const char* path, const char* patchNa
|
||||
// index /= 2;
|
||||
// }
|
||||
|
||||
// Do not patch custom assets as they most likely do not have the same instructions as authentic assets
|
||||
if (res->GetInitData()->IsCustom) {
|
||||
return;
|
||||
}
|
||||
|
||||
Gfx* gfx = (Gfx*)&res->Instructions[index];
|
||||
|
||||
if (!originalGfx.contains(path) || !originalGfx[path].contains(patchName)) {
|
||||
@@ -1616,6 +1626,11 @@ extern "C" void ResourceMgr_PatchGfxCopyCommandByName(const char* path, const ch
|
||||
auto res = std::static_pointer_cast<LUS::DisplayList>(
|
||||
LUS::Context::GetInstance()->GetResourceManager()->LoadResource(path));
|
||||
|
||||
// Do not patch custom assets as they most likely do not have the same instructions as authentic assets
|
||||
if (res->GetInitData()->IsCustom) {
|
||||
return;
|
||||
}
|
||||
|
||||
Gfx* destinationGfx = (Gfx*)&res->Instructions[destinationIndex];
|
||||
Gfx sourceGfx = res->Instructions[sourceIndex];
|
||||
|
||||
|
||||
@@ -103,6 +103,7 @@ AnimationHeaderCommon* ResourceMgr_LoadAnimByName(const char* path);
|
||||
char* ResourceMgr_GetNameByCRC(uint64_t crc, char* alloc);
|
||||
Gfx* ResourceMgr_LoadGfxByCRC(uint64_t crc);
|
||||
Gfx* ResourceMgr_LoadGfxByName(const char* path);
|
||||
uint8_t ResourceMgr_FileIsCustomByName(const char* path);
|
||||
void ResourceMgr_PatchGfxByName(const char* path, const char* patchName, int index, Gfx instruction);
|
||||
void ResourceMgr_UnpatchGfxByName(const char* path, const char* patchName);
|
||||
char* ResourceMgr_LoadArrayByNameAsVec3s(const char* path);
|
||||
|
||||
@@ -47,6 +47,11 @@ std::filesystem::path SaveManager::GetFileName(int fileNum) {
|
||||
return sSavePath / ("file" + std::to_string(fileNum + 1) + ".sav");
|
||||
}
|
||||
|
||||
std::filesystem::path SaveManager::GetFileTempName(int fileNum) {
|
||||
const std::filesystem::path sSavePath(LUS::Context::GetPathRelativeToAppDirectory("Save"));
|
||||
return sSavePath / ("file" + std::to_string(fileNum + 1) + ".temp");
|
||||
}
|
||||
|
||||
SaveManager::SaveManager() {
|
||||
coreSectionIDsByName["base"] = SECTION_ID_BASE;
|
||||
coreSectionIDsByName["randomizer"] = SECTION_ID_RANDOMIZER;
|
||||
@@ -65,6 +70,10 @@ SaveManager::SaveManager() {
|
||||
|
||||
AddInitFunction(InitFileImpl);
|
||||
|
||||
GameInteractor::Instance->RegisterGameHook<GameInteractor::OnExitGame>([this](uint32_t fileNum) { ThreadPoolWait(); });
|
||||
|
||||
smThreadPool = std::make_shared<BS::thread_pool>(1);
|
||||
|
||||
for (SaveFileMetaInfo& info : fileMetaInfo) {
|
||||
info.valid = false;
|
||||
info.deaths = 0;
|
||||
@@ -357,12 +366,14 @@ void SaveManager::SaveRandomizer(SaveContext* saveContext, int sectionID, bool f
|
||||
});
|
||||
}
|
||||
|
||||
// Init() here is an extension of InitSram, and thus not truly an initializer for SaveManager itself. don't put any class initialization stuff here
|
||||
void SaveManager::Init() {
|
||||
// Wait on saves that snuck through the Wait in OnExitGame
|
||||
ThreadPoolWait();
|
||||
const std::filesystem::path sSavePath(LUS::Context::GetPathRelativeToAppDirectory("Save"));
|
||||
const std::filesystem::path sGlobalPath = sSavePath / std::string("global.sav");
|
||||
auto sOldSavePath = LUS::Context::GetPathRelativeToAppDirectory("oot_save.sav");
|
||||
auto sOldBackupSavePath = LUS::Context::GetPathRelativeToAppDirectory("oot_save.bak");
|
||||
GameInteractor::Instance->RegisterGameHook<GameInteractor::OnExitGame>([this](uint32_t fileNum) { ThreadPoolWait(); });
|
||||
|
||||
// If the save directory does not exist, create it
|
||||
if (!std::filesystem::exists(sSavePath)) {
|
||||
@@ -403,7 +414,6 @@ void SaveManager::Init() {
|
||||
} else {
|
||||
CreateDefaultGlobal();
|
||||
}
|
||||
smThreadPool = std::make_shared<BS::thread_pool>(1);
|
||||
|
||||
// Load files to initialize metadata
|
||||
for (int fileNum = 0; fileNum < MaxFiles; fileNum++) {
|
||||
@@ -869,6 +879,32 @@ void SaveManager::InitFileMaxed() {
|
||||
gSaveContext.sceneFlags[5].swch = 0x40000000;
|
||||
}
|
||||
|
||||
#if defined(__WIIU__) || defined(__SWITCH__)
|
||||
// std::filesystem::copy_file doesn't work properly with the Wii U's toolchain atm
|
||||
int copy_file(const char* src, const char* dst) {
|
||||
alignas(0x40) uint8_t buf[4096];
|
||||
FILE* r = fopen(src, "r");
|
||||
if (!r) {
|
||||
return -1;
|
||||
}
|
||||
FILE* w = fopen(dst, "w");
|
||||
if (!w) {
|
||||
return -2;
|
||||
}
|
||||
|
||||
size_t res;
|
||||
while ((res = fread(buf, 1, sizeof(buf), r)) > 0) {
|
||||
if (fwrite(buf, 1, res, w) != res) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
fclose(r);
|
||||
fclose(w);
|
||||
return res >= 0 ? 0 : res;
|
||||
}
|
||||
#endif
|
||||
|
||||
// Threaded SaveFile takes copy of gSaveContext for local unmodified storage
|
||||
|
||||
void SaveManager::SaveFileThreaded(int fileNum, SaveContext* saveContext, int sectionID) {
|
||||
@@ -910,19 +946,42 @@ void SaveManager::SaveFileThreaded(int fileNum, SaveContext* saveContext, int se
|
||||
svi.func(saveContext, sectionID, false);
|
||||
}
|
||||
|
||||
std::filesystem::path fileName = GetFileName(fileNum);
|
||||
std::filesystem::path tempFile = GetFileTempName(fileNum);
|
||||
|
||||
if (std::filesystem::exists(tempFile)) {
|
||||
std::filesystem::remove(tempFile);
|
||||
}
|
||||
|
||||
#if defined(__SWITCH__) || defined(__WIIU__)
|
||||
FILE* w = fopen(GetFileName(fileNum).c_str(), "w");
|
||||
FILE* w = fopen(tempFile.c_str(), "w");
|
||||
std::string json_string = saveBlock.dump(4);
|
||||
fwrite(json_string.c_str(), sizeof(char), json_string.length(), w);
|
||||
fclose(w);
|
||||
#else
|
||||
std::ofstream output(GetFileName(fileNum));
|
||||
std::ofstream output(tempFile);
|
||||
output << std::setw(4) << saveBlock << std::endl;
|
||||
output.close();
|
||||
#endif
|
||||
|
||||
if (std::filesystem::exists(fileName)) {
|
||||
std::filesystem::remove(fileName);
|
||||
}
|
||||
|
||||
#if defined(__SWITCH__) || defined(__WIIU__)
|
||||
copy_file(tempFile.c_str(), fileName.c_str());
|
||||
#else
|
||||
std::filesystem::copy_file(tempFile, fileName);
|
||||
#endif
|
||||
|
||||
if (std::filesystem::exists(tempFile)) {
|
||||
std::filesystem::remove(tempFile);
|
||||
}
|
||||
|
||||
delete saveContext;
|
||||
InitMeta(fileNum);
|
||||
GameInteractor::Instance->ExecuteHooks<GameInteractor::OnSaveFile>(fileNum);
|
||||
SPDLOG_INFO("Save File Finish - fileNum: {}", fileNum);
|
||||
}
|
||||
|
||||
// SaveSection creates a copy of gSaveContext to prevent mid-save data modification, and passes its reference to SaveFileThreaded
|
||||
@@ -2105,32 +2164,6 @@ void SaveManager::LoadStruct(const std::string& name, LoadStructFunc func) {
|
||||
}
|
||||
}
|
||||
|
||||
#if defined(__WIIU__) || defined(__SWITCH__)
|
||||
// std::filesystem::copy_file doesn't work properly with the Wii U's toolchain atm
|
||||
int copy_file(const char* src, const char* dst) {
|
||||
alignas(0x40) uint8_t buf[4096];
|
||||
FILE* r = fopen(src, "r");
|
||||
if (!r) {
|
||||
return -1;
|
||||
}
|
||||
FILE* w = fopen(dst, "w");
|
||||
if (!w) {
|
||||
return -2;
|
||||
}
|
||||
|
||||
size_t res;
|
||||
while ((res = fread(buf, 1, sizeof(buf), r)) > 0) {
|
||||
if (fwrite(buf, 1, res, w) != res) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
fclose(r);
|
||||
fclose(w);
|
||||
return res >= 0 ? 0 : res;
|
||||
}
|
||||
#endif
|
||||
|
||||
void SaveManager::CopyZeldaFile(int from, int to) {
|
||||
assert(std::filesystem::exists(GetFileName(from)));
|
||||
DeleteZeldaFile(to);
|
||||
|
||||
@@ -142,6 +142,7 @@ class SaveManager {
|
||||
|
||||
private:
|
||||
std::filesystem::path GetFileName(int fileNum);
|
||||
std::filesystem::path GetFileTempName(int fileNum);
|
||||
nlohmann::json saveBlock;
|
||||
|
||||
void ConvertFromUnversioned();
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user