mirror of
https://github.com/hedge-dev/UnleashedRecomp
synced 2026-06-08 12:28:15 -04:00
Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 11863beb46 |
@@ -14,33 +14,30 @@ jobs:
|
||||
runs-on: ubuntu-24.04
|
||||
|
||||
steps:
|
||||
- name: Checkout Repository
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: recursive
|
||||
token: ${{ secrets.ORG_TOKEN }}
|
||||
|
||||
- name: Checkout Private Repository
|
||||
- name: Checkout private repository
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
repository: ${{ secrets.ASSET_REPO }}
|
||||
token: ${{ secrets.ASSET_REPO_TOKEN }}
|
||||
path: ./private
|
||||
path: flatpak/private
|
||||
|
||||
- name: Install Dependencies
|
||||
- name: Install dependencies
|
||||
run: |-
|
||||
sudo apt update
|
||||
sudo apt install -y flatpak-builder ccache
|
||||
|
||||
- name: Setup ccache
|
||||
- name: Cache ccache directory
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: /tmp/ccache
|
||||
key: ccache-${{ runner.os }}
|
||||
|
||||
- name: Prepare Project
|
||||
run: cp ./private/* ./UnleashedRecompLib/private
|
||||
|
||||
- name: Prepare Flatpak
|
||||
run: |
|
||||
flatpak --user remote-add --if-not-exists flathub https://flathub.org/repo/flathub.flatpakrepo
|
||||
|
||||
@@ -553,7 +553,6 @@ BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/images/ga
|
||||
BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/images/game_icon_night.bmp" DEST_FILE "${RESOURCES_OUTPUT_PATH}/images/game_icon_night.bmp" ARRAY_NAME "g_game_icon_night")
|
||||
|
||||
## Audio ##
|
||||
BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/music/installer.ogg" DEST_FILE "${RESOURCES_OUTPUT_PATH}/music/installer.ogg" ARRAY_NAME "g_installer_music")
|
||||
BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/sounds/sys_worldmap_cursor.ogg" DEST_FILE "${RESOURCES_OUTPUT_PATH}/sounds/sys_worldmap_cursor.ogg" ARRAY_NAME "g_sys_worldmap_cursor")
|
||||
BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/sounds/sys_worldmap_finaldecide.ogg" DEST_FILE "${RESOURCES_OUTPUT_PATH}/sounds/sys_worldmap_finaldecide.ogg" ARRAY_NAME "g_sys_worldmap_finaldecide")
|
||||
BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/sounds/sys_actstg_pausecansel.ogg" DEST_FILE "${RESOURCES_OUTPUT_PATH}/sounds/sys_actstg_pausecansel.ogg" ARRAY_NAME "g_sys_actstg_pausecansel")
|
||||
@@ -561,6 +560,3 @@ BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/sounds/sy
|
||||
BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/sounds/sys_actstg_pausedecide.ogg" DEST_FILE "${RESOURCES_OUTPUT_PATH}/sounds/sys_actstg_pausedecide.ogg" ARRAY_NAME "g_sys_actstg_pausedecide")
|
||||
BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/sounds/sys_actstg_pausewinclose.ogg" DEST_FILE "${RESOURCES_OUTPUT_PATH}/sounds/sys_actstg_pausewinclose.ogg" ARRAY_NAME "g_sys_actstg_pausewinclose")
|
||||
BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/sounds/sys_actstg_pausewinopen.ogg" DEST_FILE "${RESOURCES_OUTPUT_PATH}/sounds/sys_actstg_pausewinopen.ogg" ARRAY_NAME "g_sys_actstg_pausewinopen")
|
||||
|
||||
## Keyboard QTE ##
|
||||
BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/images/common/raw/kb_key_0.png" DEST_FILE "${RESOURCES_OUTPUT_PATH}/images/common/raw/kb_key_0.png" ARRAY_NAME "g_kb_key_0" COMPRESSION_TYPE "zstd")
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
#pragma once
|
||||
|
||||
#include <SWA.inl>
|
||||
|
||||
namespace Hedgehog::Mirage
|
||||
{
|
||||
class CMatrixNodeListener : public Base::CObject
|
||||
{
|
||||
public:
|
||||
SWA_INSERT_PADDING(0x04);
|
||||
};
|
||||
}
|
||||
@@ -31,6 +31,7 @@
|
||||
#include "Hedgehog/Math/Quaternion.h"
|
||||
#include "Hedgehog/Math/Vector.h"
|
||||
#include "Hedgehog/MirageCore/MatrixNode/hhMatrixNode.h"
|
||||
#include "Hedgehog/MirageCore/MatrixNode/hhMatrixNodeListener.h"
|
||||
#include "Hedgehog/MirageCore/Misc/hhTransform.h"
|
||||
#include "Hedgehog/MirageCore/Misc/hhVertexDeclarationPtr.h"
|
||||
#include "Hedgehog/MirageCore/RenderData/hhMaterialData.h"
|
||||
@@ -86,11 +87,12 @@
|
||||
#include "SWA/Inspire/InspireTextureAnimationInfo.h"
|
||||
#include "SWA/Inspire/InspireTextureOverlay.h"
|
||||
#include "SWA/Inspire/InspireTextureOverlayInfo.h"
|
||||
#include "SWA/Menu/MenuWindowBase.h"
|
||||
#include "SWA/Message/MsgRequestHelp.h"
|
||||
#include "SWA/Menu/MenuWindowBase.h"
|
||||
#include "SWA/Movie/MovieDisplayer.h"
|
||||
#include "SWA/Movie/MovieManager.h"
|
||||
#include "SWA/Object/Common/DashPanel/ObjDashPanel.h"
|
||||
#include "SWA/Object/Event/Hint/ObjHintRing.h"
|
||||
#include "SWA/Object/SonicStage/EU/RollingBarrel/ObjRollingBarrel.h"
|
||||
#include "SWA/Player/Character/EvilSonic/EvilSonic.h"
|
||||
#include "SWA/Player/Character/EvilSonic/EvilSonicContext.h"
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
#pragma once
|
||||
|
||||
#include <SWA.inl>
|
||||
|
||||
namespace SWA
|
||||
{
|
||||
class CObjHintRing
|
||||
{
|
||||
public:
|
||||
SWA_INSERT_PADDING(0x160);
|
||||
be<uint32_t> m_AnimationID;
|
||||
bool m_Field164;
|
||||
};
|
||||
}
|
||||
@@ -2,7 +2,6 @@
|
||||
#include <apu/embedded_player.h>
|
||||
#include <user/config.h>
|
||||
|
||||
#include <res/music/installer.ogg.h>
|
||||
#include <res/sounds/sys_worldmap_cursor.ogg.h>
|
||||
#include <res/sounds/sys_worldmap_finaldecide.ogg.h>
|
||||
#include <res/sounds/sys_actstg_pausecansel.ogg.h>
|
||||
@@ -88,17 +87,13 @@ static void PlayEmbeddedSound(EmbeddedSound s)
|
||||
data.chunk = Mix_LoadWAV_RW(SDL_RWFromConstMem(soundData, soundDataSize), 1);
|
||||
}
|
||||
|
||||
Mix_VolumeChunk(data.chunk, Config::MasterVolume * Config::EffectsVolume * MIX_MAX_VOLUME);
|
||||
Mix_PlayChannel(g_channelIndex % MIX_CHANNELS, data.chunk, 0);
|
||||
++g_channelIndex;
|
||||
}
|
||||
|
||||
static Mix_Music* g_installerMusic;
|
||||
|
||||
void EmbeddedPlayer::Init()
|
||||
{
|
||||
Mix_OpenAudio(XAUDIO_SAMPLES_HZ, AUDIO_F32SYS, 2, 2048);
|
||||
g_installerMusic = Mix_LoadMUS_RW(SDL_RWFromConstMem(g_installer_music, sizeof(g_installer_music)), 1);
|
||||
Mix_OpenAudio(XAUDIO_SAMPLES_HZ, AUDIO_F32SYS, 2, 256);
|
||||
|
||||
s_isActive = true;
|
||||
}
|
||||
@@ -116,21 +111,6 @@ void EmbeddedPlayer::Play(const char *name)
|
||||
PlayEmbeddedSound(it->second);
|
||||
}
|
||||
|
||||
void EmbeddedPlayer::PlayMusic()
|
||||
{
|
||||
if (!Mix_PlayingMusic())
|
||||
{
|
||||
Mix_PlayMusic(g_installerMusic, INT_MAX);
|
||||
Mix_VolumeMusic(Config::MasterVolume * Config::MusicVolume * MUSIC_VOLUME * MIX_MAX_VOLUME);
|
||||
}
|
||||
}
|
||||
|
||||
void EmbeddedPlayer::FadeOutMusic()
|
||||
{
|
||||
if (Mix_PlayingMusic())
|
||||
Mix_FadeOutMusic(1000);
|
||||
}
|
||||
|
||||
void EmbeddedPlayer::Shutdown()
|
||||
{
|
||||
for (EmbeddedSoundData &data : g_embeddedSoundData)
|
||||
@@ -139,9 +119,6 @@ void EmbeddedPlayer::Shutdown()
|
||||
Mix_FreeChunk(data.chunk);
|
||||
}
|
||||
|
||||
Mix_HaltMusic();
|
||||
Mix_FreeMusic(g_installerMusic);
|
||||
|
||||
Mix_CloseAudio();
|
||||
Mix_Quit();
|
||||
|
||||
|
||||
@@ -2,14 +2,9 @@
|
||||
|
||||
struct EmbeddedPlayer
|
||||
{
|
||||
// Arbitrarily picked volume to match the mixing in the original game.
|
||||
static constexpr float MUSIC_VOLUME = 0.25f;
|
||||
|
||||
static inline bool s_isActive = false;
|
||||
inline static bool s_isActive = false;
|
||||
|
||||
static void Init();
|
||||
static void Play(const char *name);
|
||||
static void PlayMusic();
|
||||
static void FadeOutMusic();
|
||||
static void Shutdown();
|
||||
};
|
||||
|
||||
@@ -1350,9 +1350,6 @@ struct ImGuiPushConstants
|
||||
|
||||
extern ImFontBuilderIO g_fontBuilderIO;
|
||||
|
||||
extern void InitKeyboardQTE();
|
||||
extern void DrawKeyboardQTE();
|
||||
|
||||
static void CreateImGuiBackend()
|
||||
{
|
||||
ImGuiIO& io = ImGui::GetIO();
|
||||
@@ -1375,7 +1372,6 @@ static void CreateImGuiBackend()
|
||||
MessageWindow::Init();
|
||||
OptionsMenu::Init();
|
||||
InstallerWizard::Init();
|
||||
InitKeyboardQTE();
|
||||
|
||||
ImGui_ImplSDL2_InitForOther(GameWindow::s_pWindow);
|
||||
|
||||
@@ -2468,7 +2464,6 @@ static void DrawImGui()
|
||||
InstallerWizard::Draw();
|
||||
MessageWindow::Draw();
|
||||
ButtonGuide::Draw();
|
||||
DrawKeyboardQTE();
|
||||
Fader::Draw();
|
||||
BlackBar::Draw();
|
||||
|
||||
|
||||
@@ -13,6 +13,38 @@ bool DisableHintsMidAsmHook()
|
||||
return !Config::Hints;
|
||||
}
|
||||
|
||||
// Disable hint ring visuals.
|
||||
PPC_FUNC_IMPL(__imp__sub_82738088);
|
||||
PPC_FUNC(sub_82738088)
|
||||
{
|
||||
auto pObjHintRing = (SWA::CObjHintRing*)(base + ctx.r3.u32);
|
||||
auto pDeltaTime = (be<float>*)(base + ctx.r4.u32);
|
||||
|
||||
if (!Config::Hints)
|
||||
{
|
||||
auto pAnimationControl = PPC_LOAD_U32(ctx.r3.u32 + 0xF0);
|
||||
|
||||
// how does this even work
|
||||
guest_stack_var<be<float>> time = 0.0f;
|
||||
GuestToHostFunction<int>(sub_82BB4A40, pAnimationControl, time.get());
|
||||
GuestToHostFunction<int>(sub_82BBC050, pAnimationControl, *pDeltaTime);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
__imp__sub_82738088(ctx, base);
|
||||
}
|
||||
|
||||
// Disable hint ring hit event.
|
||||
PPC_FUNC_IMPL(__imp__sub_827391E0);
|
||||
PPC_FUNC(sub_827391E0)
|
||||
{
|
||||
if (!Config::Hints)
|
||||
return;
|
||||
|
||||
__imp__sub_827391E0(ctx, base);
|
||||
}
|
||||
|
||||
// Disable Perfect Dark Gaia hints.
|
||||
PPC_FUNC_IMPL(__imp__sub_82AC36E0);
|
||||
PPC_FUNC(sub_82AC36E0)
|
||||
@@ -24,9 +56,14 @@ PPC_FUNC(sub_82AC36E0)
|
||||
__imp__sub_82AC36E0(ctx, base);
|
||||
}
|
||||
|
||||
bool DisableControlTutorialMidAsmHook()
|
||||
// Disable navigation volumes.
|
||||
PPC_FUNC_IMPL(__imp__sub_8273C4C8);
|
||||
PPC_FUNC(sub_8273C4C8)
|
||||
{
|
||||
return !Config::ControlTutorial;
|
||||
if (!Config::ControlTutorial)
|
||||
return;
|
||||
|
||||
__imp__sub_8273C4C8(ctx, base);
|
||||
}
|
||||
|
||||
bool DisableEvilControlTutorialMidAsmHook(PPCRegister& r4, PPCRegister& r5)
|
||||
@@ -120,9 +157,9 @@ PPC_FUNC(sub_82736E80)
|
||||
|
||||
if (!Config::Hints)
|
||||
{
|
||||
// WhiteIsland_ACT1_001: "Your friend went off that way, Sonic. Quick, let's go after him!"
|
||||
// s20n_mykETF_c_navi_2: "Huh? Weird! We can't get through here anymore. We were able to earlier!"
|
||||
if (strcmp(groupId, "WhiteIsland_ACT1_001") != 0 && strcmp(groupId, "s20n_mykETF_c_navi_2") != 0)
|
||||
// WhiteIsland_ACT1_001 (Windmill Isle Act 1 Night, Start)
|
||||
// Your friend went off that way, Sonic. Quick, let's go after him!
|
||||
if (strcmp(groupId, "WhiteIsland_ACT1_001") != 0)
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -139,200 +176,11 @@ PPC_FUNC(sub_824C1E60)
|
||||
|
||||
if (!Config::Hints)
|
||||
{
|
||||
// s10d_mykETF_c_navi: "Looks like we can get to a bunch of places in the village from here!"
|
||||
// s10d_mykETF_c_navi (Town Mykonos Entrance, First Entry)
|
||||
// Looks like we can get to a bunch of places in the village from here!
|
||||
if (strcmp(pMsgRequestHelp->m_Name.c_str(), "s10d_mykETF_c_navi") == 0)
|
||||
return;
|
||||
}
|
||||
|
||||
__imp__sub_824C1E60(ctx, base);
|
||||
}
|
||||
|
||||
#include <gpu/video.h>
|
||||
#include <gpu/imgui/imgui_snapshot.h>
|
||||
#include <res/images/common/raw/kb_key_0.png.h>
|
||||
#include <decompressor.h>
|
||||
#include <ui/imgui_utils.h>
|
||||
#include <exports.h>
|
||||
#include <words.h>
|
||||
#include <random>
|
||||
|
||||
static std::string g_qteText;
|
||||
static std::vector<double> g_qteTimes;
|
||||
static bool g_shouldDrawKeyboardQTE;
|
||||
static std::unique_ptr<GuestTexture> g_keyboardTexture;
|
||||
static uint8_t g_prevScanCodes[SDL_NUM_SCANCODES];
|
||||
static std::default_random_engine g_engine{ std::random_device {}() };
|
||||
static std::uniform_int_distribution g_intDistribution;
|
||||
static std::uniform_real_distribution g_floatDistribution(0.0, 1.0);
|
||||
|
||||
void InitKeyboardQTE()
|
||||
{
|
||||
g_keyboardTexture = LOAD_ZSTD_TEXTURE(g_kb_key_0);
|
||||
}
|
||||
|
||||
void DrawKeyboardQTE()
|
||||
{
|
||||
memcpy(g_prevScanCodes, SDL_GetKeyboardState(nullptr), sizeof(g_prevScanCodes));
|
||||
|
||||
if (!g_shouldDrawKeyboardQTE)
|
||||
return;
|
||||
|
||||
g_shouldDrawKeyboardQTE = false;
|
||||
|
||||
auto drawList = ImGui::GetBackgroundDrawList();
|
||||
ImFont* font = ImFontAtlasSnapshot::GetFont("FOT-NewRodinPro-DB.otf");
|
||||
|
||||
// y: 307
|
||||
// h: 110
|
||||
|
||||
// font: x 36 y 30 30pt
|
||||
|
||||
auto& res = ImGui::GetIO().DisplaySize;
|
||||
|
||||
constexpr float padding = -40.0f;
|
||||
float width = Scale(g_qteText.size() * 110 + (g_qteText.size() - 1) * padding);
|
||||
for (size_t i = 0; i < g_qteText.size(); i++)
|
||||
{
|
||||
float x = res.x / 2.0f - width / 2.0f + Scale((110 + padding)) * i;
|
||||
double motion = ComputeLinearMotion(g_qteTimes[i], 0, 5);
|
||||
|
||||
if (g_qteText[i] != ' ')
|
||||
{
|
||||
std::stringstream text;
|
||||
text << std::toupper(g_qteText[i], std::locale::classic());
|
||||
|
||||
float x = res.x / 2.0f - width / 2.0f + Scale((110 + padding)) * i;
|
||||
drawList->AddImage(g_keyboardTexture.get(), { x, Scale(307) }, { x + Scale(110), Scale(307 + 110) }, {0.0f, 0.0f}, {1.0f, 1.0f}, IM_COL32(255, 255, 255, 255 * motion));
|
||||
drawList->AddText(font, Scale(24.0f), { x + Scale(36), Scale(307 + 28) }, IM_COL32(0, 0, 0, 255 * motion), text.str().c_str());
|
||||
}
|
||||
else
|
||||
{
|
||||
drawList->AddImage(g_keyboardTexture.get(), { x, Scale(307) }, { x + Scale(110), Scale(307 + 110) }, { 0.0f, 0.0f }, { 1.0f, 1.0f }, IM_COL32(255, 255, 255, 255 * (1.0 - motion)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// + 100 is success bool
|
||||
// return true to indicate either succeeded or failed
|
||||
PPC_FUNC_IMPL(__imp__sub_823329F8);
|
||||
PPC_FUNC(sub_823329F8)
|
||||
{
|
||||
struct DelayCall
|
||||
{
|
||||
PPCRegister r3;
|
||||
uint8_t* base;
|
||||
PPCRegister time;
|
||||
|
||||
DelayCall(PPCRegister& r3, uint8_t* base) : r3(r3), base(base)
|
||||
{
|
||||
time.u32 = PPC_LOAD_U32(r3.u32 + 104);
|
||||
|
||||
PPCRegister newTime = time;
|
||||
newTime.f32 *= 5.0f;
|
||||
PPC_STORE_U32(r3.u32 + 104, newTime.u32);
|
||||
}
|
||||
|
||||
~DelayCall()
|
||||
{
|
||||
PPC_STORE_U32(r3.u32 + 104, time.u32);
|
||||
}
|
||||
} delayCall(ctx.r3, base);
|
||||
|
||||
g_shouldDrawKeyboardQTE = true;
|
||||
|
||||
bool foundAny = false;
|
||||
|
||||
for (size_t i = 0; i < g_qteText.size(); i++)
|
||||
{
|
||||
if (g_qteText[i] != ' ')
|
||||
{
|
||||
int lower = std::tolower(g_qteText[i], std::locale::classic());
|
||||
SDL_Scancode scancode = SDL_GetScancodeFromKey(lower);
|
||||
|
||||
for (size_t j = SDL_SCANCODE_A; j <= SDL_SCANCODE_Z; j++)
|
||||
{
|
||||
if (g_prevScanCodes[j] == 0 && SDL_GetKeyboardState(nullptr)[j] != 0)
|
||||
{
|
||||
if (j == scancode) // pressed the right one
|
||||
{
|
||||
g_qteText[i] = ' ';
|
||||
g_qteTimes[i] = ImGui::GetTime();
|
||||
|
||||
ctx.r3.u32 = 0;
|
||||
Game_PlaySound("objsn_trickjump_button");
|
||||
|
||||
return;
|
||||
}
|
||||
else // wrong one!
|
||||
{
|
||||
PPC_STORE_U8(ctx.r3.u32 + 100, 0);
|
||||
ctx.r3.u32 = 1;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
foundAny = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// ran out of time
|
||||
if (PPC_LOAD_U32(ctx.r3.u32 + 132) >= PPC_LOAD_U32(ctx.r3.u32 + 104))
|
||||
{
|
||||
PPC_STORE_U8(ctx.r3.u32 + 100, 0);
|
||||
ctx.r3.u32 = 1;
|
||||
return;
|
||||
}
|
||||
|
||||
if (!foundAny)
|
||||
{
|
||||
// pressed all of them correctly
|
||||
PPC_STORE_U8(ctx.r3.u32 + 100, 1);
|
||||
ctx.r3.u32 = 1;
|
||||
return;
|
||||
}
|
||||
|
||||
__imp__sub_823329F8(ctx, base);
|
||||
ctx.r3.u32 = 0;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
PPC_FUNC_IMPL(__imp__sub_826117E0);
|
||||
PPC_FUNC(sub_826117E0)
|
||||
{
|
||||
auto counts = reinterpret_cast<uint32_t*>(base + PPC_LOAD_U32(ctx.r3.u32 + 252));
|
||||
auto times = reinterpret_cast<uint32_t*>(base + PPC_LOAD_U32(ctx.r3.u32 + 236));
|
||||
|
||||
for (size_t i = 1; i < 3; i++)
|
||||
{
|
||||
if (counts[i] == 0)
|
||||
{
|
||||
counts[i] = counts[0];
|
||||
times[i] = times[0];
|
||||
}
|
||||
}
|
||||
|
||||
__imp__sub_826117E0(ctx, base);
|
||||
}
|
||||
|
||||
void QteButtonPromptInitMidAsmHook()
|
||||
{
|
||||
g_qteText = g_words[g_intDistribution(g_engine) % std::size(g_words)];
|
||||
g_qteTimes.clear();
|
||||
g_qteTimes.resize(g_qteText.size(), ImGui::GetTime());
|
||||
}
|
||||
|
||||
void QteButtonPromptUpdateMidAsmHook()
|
||||
{
|
||||
}
|
||||
|
||||
void TrickJumperCompareMidAsmHook(PPCRegister& r28)
|
||||
{
|
||||
if (r28.u32 == 12)
|
||||
{
|
||||
if (g_floatDistribution(g_engine) < 0.3)
|
||||
r28.u32 = 0;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
VERSION_MILESTONE="Release Candidate 3"
|
||||
VERSION_MILESTONE="Release Candidate 2"
|
||||
VERSION_MAJOR=1
|
||||
VERSION_MINOR=0
|
||||
VERSION_REVISION=0
|
||||
|
||||
@@ -1721,24 +1721,6 @@ static void PickerCheckResults()
|
||||
g_currentPickerVisible = false;
|
||||
}
|
||||
|
||||
static bool g_fadingOutMusic;
|
||||
|
||||
static void ProcessMusic()
|
||||
{
|
||||
if (g_isDisappearing)
|
||||
{
|
||||
if (!g_fadingOutMusic)
|
||||
{
|
||||
EmbeddedPlayer::FadeOutMusic();
|
||||
g_fadingOutMusic = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
EmbeddedPlayer::PlayMusic();
|
||||
}
|
||||
}
|
||||
|
||||
void InstallerWizard::Init()
|
||||
{
|
||||
auto &io = ImGui::GetIO();
|
||||
@@ -1868,7 +1850,6 @@ bool InstallerWizard::Run(std::filesystem::path installPath, bool skipGame)
|
||||
while (s_isVisible)
|
||||
{
|
||||
Video::WaitOnSwapChain();
|
||||
ProcessMusic();
|
||||
SDL_PumpEvents();
|
||||
SDL_FlushEvents(SDL_FIRSTEVENT, SDL_LASTEVENT);
|
||||
GameWindow::Update();
|
||||
|
||||
@@ -1206,7 +1206,6 @@ static void DrawConfigOptions()
|
||||
|
||||
int32_t rowCount = 0;
|
||||
|
||||
bool isStage = OptionsMenu::s_pauseMenuType == SWA::eMenuType_Stage || OptionsMenu::s_pauseMenuType == SWA::eMenuType_Hub;
|
||||
auto cmnReason = &Localise("Options_Desc_NotAvailable");
|
||||
|
||||
// TODO: Don't use raw numbers here!
|
||||
@@ -1216,8 +1215,8 @@ static void DrawConfigOptions()
|
||||
DrawConfigOption(rowCount++, yOffset, &Config::Language, !OptionsMenu::s_isPause, cmnReason);
|
||||
DrawConfigOption(rowCount++, yOffset, &Config::VoiceLanguage, OptionsMenu::s_pauseMenuType == SWA::eMenuType_WorldMap, cmnReason);
|
||||
DrawConfigOption(rowCount++, yOffset, &Config::Subtitles, true);
|
||||
DrawConfigOption(rowCount++, yOffset, &Config::Hints, !isStage, cmnReason);
|
||||
DrawConfigOption(rowCount++, yOffset, &Config::ControlTutorial, !isStage, cmnReason);
|
||||
DrawConfigOption(rowCount++, yOffset, &Config::Hints, true);
|
||||
DrawConfigOption(rowCount++, yOffset, &Config::ControlTutorial, true);
|
||||
DrawConfigOption(rowCount++, yOffset, &Config::AchievementNotifications, true);
|
||||
DrawConfigOption(rowCount++, yOffset, &Config::TimeOfDayTransition, !Config::UseArrowsForTimeOfDayTransition);
|
||||
break;
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -37,15 +37,6 @@ add_custom_command(
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/config/SWA.toml"
|
||||
)
|
||||
|
||||
add_custom_command(
|
||||
OUTPUT
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/private/shader_decompressed.ar"
|
||||
COMMAND
|
||||
$<TARGET_FILE:x_decompress> "${CMAKE_CURRENT_SOURCE_DIR}/private/shader.ar" "${CMAKE_CURRENT_SOURCE_DIR}/private/shader_decompressed.ar"
|
||||
DEPENDS
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/private/shader.ar"
|
||||
)
|
||||
|
||||
set(XENOS_RECOMP_ROOT "${UNLEASHED_RECOMP_TOOLS_ROOT}/XenosRecomp/XenosRecomp")
|
||||
set(XENOS_RECOMP_INCLUDE "${XENOS_RECOMP_ROOT}/shader_common.h")
|
||||
|
||||
@@ -67,7 +58,7 @@ add_custom_command(
|
||||
$<TARGET_FILE:XenosRecomp>
|
||||
DEPENDS
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/private/default_patched.xex"
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/private/shader_decompressed.ar"
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/private/shader.ar"
|
||||
${XENOS_RECOMP_SOURCES}
|
||||
${XENOS_RECOMP_INCLUDE}
|
||||
)
|
||||
|
||||
@@ -106,12 +106,6 @@ jump_address = 0x82468EE0
|
||||
name = "ResetScoreOnRestartMidAsmHook"
|
||||
address = 0x82304374
|
||||
|
||||
# Disable hint rings
|
||||
[[midasm_hook]]
|
||||
name = "DisableHintsMidAsmHook"
|
||||
address = 0x827A2E34
|
||||
jump_address_on_true = 0x827A2E4C
|
||||
|
||||
# Disable Tornado Defense hints
|
||||
[[midasm_hook]]
|
||||
name = "DisableHintsMidAsmHook"
|
||||
@@ -184,12 +178,6 @@ name = "DisableHintsMidAsmHook"
|
||||
address = 0x82691CB0
|
||||
jump_address_on_true = 0x82691E24
|
||||
|
||||
# Disable navigation volumes
|
||||
[[midasm_hook]]
|
||||
name = "DisableControlTutorialMidAsmHook"
|
||||
address = 0x827AA5EC
|
||||
jump_address_on_true = 0x827AA604
|
||||
|
||||
# Disable Werehog button prompts
|
||||
[[midasm_hook]]
|
||||
name = "DisableEvilControlTutorialMidAsmHook"
|
||||
@@ -1093,18 +1081,3 @@ registers = ["r31", "r29", "r28"]
|
||||
name = "ObjGrindDashPanelAllocMidAsmHook"
|
||||
address = 0x82614948
|
||||
registers = ["r3"]
|
||||
|
||||
[[midasm_hook]]
|
||||
name = "QteButtonPromptInitMidAsmHook"
|
||||
address = 0x823339D8
|
||||
jump_address = 0x82333D14
|
||||
|
||||
[[midasm_hook]]
|
||||
name = "QteButtonPromptUpdateMidAsmHook"
|
||||
address = 0x82332A90
|
||||
jump_address = 0x82332BC4
|
||||
|
||||
[[midasm_hook]]
|
||||
name = "TrickJumperCompareMidAsmHook"
|
||||
address = 0x82611910
|
||||
registers = ["r28"]
|
||||
|
||||
+1
-1
Submodule UnleashedRecompResources updated: e5a4adccb3...1375ed7184
@@ -32,6 +32,26 @@
|
||||
{
|
||||
"type": "dir",
|
||||
"path": "../"
|
||||
},
|
||||
{
|
||||
"type": "file",
|
||||
"path": "private/default.xex",
|
||||
"dest": "UnleashedRecompLib/private"
|
||||
},
|
||||
{
|
||||
"type": "file",
|
||||
"path": "private/default.xexp",
|
||||
"dest": "UnleashedRecompLib/private"
|
||||
},
|
||||
{
|
||||
"type": "file",
|
||||
"path": "private/default_patched.xex",
|
||||
"dest": "UnleashedRecompLib/private"
|
||||
},
|
||||
{
|
||||
"type": "file",
|
||||
"path": "private/shader.ar",
|
||||
"dest": "UnleashedRecompLib/private"
|
||||
}
|
||||
],
|
||||
"build-options": {
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
add_subdirectory(${UNLEASHED_RECOMP_TOOLS_ROOT}/bc_diff)
|
||||
add_subdirectory(${UNLEASHED_RECOMP_TOOLS_ROOT}/file_to_c)
|
||||
add_subdirectory(${UNLEASHED_RECOMP_TOOLS_ROOT}/fshasher)
|
||||
add_subdirectory(${UNLEASHED_RECOMP_TOOLS_ROOT}/x_decompress)
|
||||
add_subdirectory(${UNLEASHED_RECOMP_TOOLS_ROOT}/XenonRecomp)
|
||||
add_subdirectory(${UNLEASHED_RECOMP_TOOLS_ROOT}/XenosRecomp)
|
||||
|
||||
@@ -1,11 +0,0 @@
|
||||
project("x_decompress")
|
||||
set(CMAKE_CXX_STANDARD 17)
|
||||
|
||||
add_executable(x_decompress
|
||||
"x_decompress.cpp"
|
||||
"${UNLEASHED_RECOMP_TOOLS_ROOT}/XenonRecomp/thirdparty/libmspack/libmspack/mspack/lzxd.c"
|
||||
)
|
||||
|
||||
target_include_directories(x_decompress
|
||||
PRIVATE "${UNLEASHED_RECOMP_TOOLS_ROOT}/XenonRecomp/thirdparty/libmspack/libmspack/mspack"
|
||||
)
|
||||
@@ -1,247 +0,0 @@
|
||||
#include <algorithm>
|
||||
#include <cstdint>
|
||||
#include <cstddef>
|
||||
#include <cstring>
|
||||
#include <vector>
|
||||
#include <fstream>
|
||||
#include <cassert>
|
||||
#include <mspack.h>
|
||||
#include <lzx.h>
|
||||
|
||||
static std::vector<uint8_t> readAllBytes(const char* path)
|
||||
{
|
||||
std::ifstream file{ path, std::ios::binary };
|
||||
std::vector<uint8_t> result{};
|
||||
|
||||
if (!file.good()) {
|
||||
return result;
|
||||
}
|
||||
|
||||
file.seekg(0, std::ios::end);
|
||||
result.resize(file.tellg());
|
||||
|
||||
file.seekg(0, std::ios::beg);
|
||||
file.read(reinterpret_cast<char*>(result.data()), result.size());
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
static T byteSwap(T value)
|
||||
{
|
||||
if constexpr (sizeof(T) == 1)
|
||||
return value;
|
||||
else if constexpr (sizeof(T) == 2)
|
||||
return static_cast<T>(__builtin_bswap16(static_cast<uint16_t>(value)));
|
||||
else if constexpr (sizeof(T) == 4)
|
||||
return static_cast<T>(__builtin_bswap32(static_cast<uint32_t>(value)));
|
||||
else if constexpr (sizeof(T) == 8)
|
||||
return static_cast<T>(__builtin_bswap64(static_cast<uint64_t>(value)));
|
||||
|
||||
assert(false && "Unexpected byte size.");
|
||||
return value;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
static void byteSwapInplace(T& value)
|
||||
{
|
||||
value = byteSwap(value);
|
||||
}
|
||||
|
||||
struct ReadStream
|
||||
{
|
||||
const uint8_t* data = nullptr;
|
||||
int size = 0; // Size from every compressed block.
|
||||
};
|
||||
|
||||
static int mspackRead(mspack_file* file, void* buffer, int bytes)
|
||||
{
|
||||
ReadStream* stream = reinterpret_cast<ReadStream*>(file);
|
||||
|
||||
if (stream->size == 0)
|
||||
{
|
||||
uint16_t size = byteSwap(*reinterpret_cast<const uint16_t*>(stream->data));
|
||||
stream->data += sizeof(uint16_t);
|
||||
|
||||
// This indicates there is an uncompressed block size available. We don't need it so we skip it.
|
||||
if ((size & 0xFF00) == 0xFF00)
|
||||
{
|
||||
stream->data += 1;
|
||||
size = byteSwap(*reinterpret_cast<const uint16_t*>(stream->data));
|
||||
stream->data += sizeof(uint16_t);
|
||||
}
|
||||
|
||||
stream->size = size;
|
||||
}
|
||||
|
||||
int sizeToRead = std::min(stream->size, bytes);
|
||||
|
||||
memcpy(buffer, stream->data, sizeToRead);
|
||||
stream->data += sizeToRead;
|
||||
stream->size -= sizeToRead;
|
||||
|
||||
return sizeToRead;
|
||||
}
|
||||
|
||||
struct WriteStream
|
||||
{
|
||||
uint8_t* data = nullptr;
|
||||
std::size_t size = 0; // Remaining available space in the stream.
|
||||
};
|
||||
|
||||
static int mspackWrite(mspack_file* file, void* buffer, int bytes)
|
||||
{
|
||||
WriteStream* stream = reinterpret_cast<WriteStream*>(file);
|
||||
|
||||
std::size_t sizeToWrite = std::min(stream->size, static_cast<std::size_t>(bytes));
|
||||
|
||||
memcpy(stream->data, buffer, sizeToWrite);
|
||||
stream->data += sizeToWrite;
|
||||
stream->size -= sizeToWrite;
|
||||
|
||||
return static_cast<int>(sizeToWrite);
|
||||
}
|
||||
|
||||
static void* mspackAlloc(mspack_system* self, size_t bytes)
|
||||
{
|
||||
return operator new(bytes);
|
||||
}
|
||||
|
||||
static void mspackFree(void* ptr)
|
||||
{
|
||||
operator delete(ptr);
|
||||
}
|
||||
|
||||
static void mspackCopy(void* src, void* dst, size_t bytes)
|
||||
{
|
||||
memcpy(dst, src, bytes);
|
||||
}
|
||||
|
||||
static mspack_system g_lzxSystem =
|
||||
{
|
||||
nullptr,
|
||||
nullptr,
|
||||
mspackRead,
|
||||
mspackWrite,
|
||||
nullptr,
|
||||
nullptr,
|
||||
nullptr,
|
||||
mspackAlloc,
|
||||
mspackFree,
|
||||
mspackCopy
|
||||
};
|
||||
|
||||
// Xbox Compression header definitions.
|
||||
static constexpr uint32_t XCompressSignature = 0xFF512EE;
|
||||
|
||||
struct XCompressHeader
|
||||
{
|
||||
uint32_t signature;
|
||||
uint32_t field04;
|
||||
uint32_t field08;
|
||||
uint32_t field0C;
|
||||
uint32_t windowSize;
|
||||
uint32_t compressedBlockSize;
|
||||
uint64_t uncompressedSize;
|
||||
uint64_t compressedSize;
|
||||
uint32_t uncompressedBlockSize;
|
||||
uint32_t field2C;
|
||||
|
||||
void byteSwap()
|
||||
{
|
||||
byteSwapInplace(signature);
|
||||
byteSwapInplace(field04);
|
||||
byteSwapInplace(field08);
|
||||
byteSwapInplace(field0C);
|
||||
byteSwapInplace(windowSize);
|
||||
byteSwapInplace(compressedBlockSize);
|
||||
byteSwapInplace(uncompressedSize);
|
||||
byteSwapInplace(compressedSize);
|
||||
byteSwapInplace(uncompressedBlockSize);
|
||||
byteSwapInplace(field2C);
|
||||
}
|
||||
};
|
||||
|
||||
int main(int argc, char** argv)
|
||||
{
|
||||
if (argc < 3)
|
||||
{
|
||||
printf("Usage: x_decompress [input file path] [output file path]");
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
std::vector<uint8_t> file = readAllBytes(argv[1]);
|
||||
if (file.empty())
|
||||
{
|
||||
fprintf(stderr, "Input file \"%s\" not found or empty", argv[1]);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
std::vector<uint8_t> decompressedFile;
|
||||
|
||||
if (file.size() >= sizeof(XCompressHeader) && byteSwap(*reinterpret_cast<uint32_t*>(file.data())) == XCompressSignature)
|
||||
{
|
||||
XCompressHeader* header = reinterpret_cast<XCompressHeader*>(file.data());
|
||||
header->byteSwap();
|
||||
|
||||
decompressedFile.resize(header->uncompressedSize);
|
||||
|
||||
const uint8_t* srcBytes = file.data() + sizeof(XCompressHeader);
|
||||
|
||||
WriteStream dstStream;
|
||||
dstStream.data = decompressedFile.data();
|
||||
dstStream.size = decompressedFile.size();
|
||||
|
||||
// libmspack wants the bit index. This value is always guaranteed to be a power of two,
|
||||
// so we can extract the bit index by counting the amount of leading zeroes.
|
||||
int windowBits = 0;
|
||||
uint32_t windowSize = header->windowSize;
|
||||
while ((windowSize & 0x1) == 0)
|
||||
{
|
||||
++windowBits;
|
||||
windowSize >>= 1;
|
||||
}
|
||||
|
||||
// Loop over compressed blocks.
|
||||
while (srcBytes < (file.data() + file.size()) && dstStream.data < (decompressedFile.data() + decompressedFile.size()))
|
||||
{
|
||||
uint32_t compressedSize = byteSwap(*reinterpret_cast<const uint32_t*>(srcBytes));
|
||||
srcBytes += sizeof(uint32_t);
|
||||
|
||||
ReadStream srcStream;
|
||||
srcStream.data = srcBytes;
|
||||
|
||||
std::size_t uncompressedBlockSize = std::min(static_cast<std::size_t>(header->uncompressedBlockSize), dstStream.size);
|
||||
|
||||
lzxd_stream* lzx = lzxd_init(
|
||||
&g_lzxSystem,
|
||||
reinterpret_cast<mspack_file*>(&srcStream),
|
||||
reinterpret_cast<mspack_file*>(&dstStream),
|
||||
windowBits,
|
||||
0,
|
||||
static_cast<int>(header->compressedBlockSize),
|
||||
static_cast<off_t>(uncompressedBlockSize),
|
||||
0);
|
||||
|
||||
lzxd_decompress(lzx, uncompressedBlockSize);
|
||||
lzxd_free(lzx);
|
||||
|
||||
srcBytes += compressedSize;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
decompressedFile = std::move(file);
|
||||
}
|
||||
|
||||
std::ofstream outputFile(argv[2], std::ios::binary);
|
||||
if (!outputFile.good())
|
||||
{
|
||||
fprintf(stderr, "Cannot open output file \"%s\" for writing", argv[2]);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
outputFile.write(reinterpret_cast<const char*>(decompressedFile.data()), decompressedFile.size());
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
Reference in New Issue
Block a user