Merge branch 'main' of github.com:HarbourMasters/Starship

This commit is contained in:
SonicDcer
2025-05-25 16:47:24 -03:00
20 changed files with 306 additions and 66 deletions
+5 -7
View File
@@ -1,7 +1,7 @@
cmake_minimum_required(VERSION 3.16.0 FATAL_ERROR)
# Set the project version and language
project(Starship VERSION 0.1.0 LANGUAGES C CXX ASM)
project(Starship VERSION 2.0.1 LANGUAGES C CXX ASM)
include(FetchContent)
set(NATO_PHONETIC_ALPHABET
@@ -17,8 +17,8 @@ math(EXPR PATCH_INDEX "${PROJECT_VERSION_PATCH}")
# Use the patch number to select the correct word
list(GET NATO_PHONETIC_ALPHABET ${PATCH_INDEX} PROJECT_PATCH_WORD)
set(PROJECT_BUILD_NAME "Centauri ${PROJECT_PATCH_WORD}" CACHE STRING "" FORCE)
set(PROJECT_TEAM "github.com/harbourmasters" CACHE STRING "" FORCE)
set(PROJECT_BUILD_NAME "Barnard ${PROJECT_PATCH_WORD}" CACHE STRING "" FORCE)
set(PROJECT_TEAM "SonicDcer & Lywx" CACHE STRING "" FORCE)
if(APPLE)
enable_language(OBJCXX)
@@ -30,10 +30,6 @@ set(CMAKE_OSX_DEPLOYMENT_TARGET "10.15" CACHE STRING "Minimum OS X deployment ve
# Set the C++ standard and enable the MSVC parallel build option
set(CMAKE_CXX_STANDARD 20 CACHE STRING "The C++ standard to use")
set(CMAKE_C_STANDARD 11 CACHE STRING "The C standard to use")
set(PROJECT_TEAM "Lywx & YoshiCrystal")
set(PROJECT_VERSION_MAJOR 2)
set(PROJECT_VERSION_MINOR 0)
set(PROJECT_VERSION_PATCH 0)
#add_compile_options(-fsanitize=address)
#add_link_options(-fsanitize=address)
@@ -574,6 +570,8 @@ if (CMAKE_CXX_COMPILER_ID MATCHES "GNU|Clang|AppleClang")
else()
if(CMAKE_SYSTEM_PROCESSOR MATCHES "x86_64")
set(CPU_OPTION -msse2 -mfpmath=sse)
elseif(CMAKE_SYSTEM_PROCESSOR MATCHES "aarch64")
set(CPU_OPTION -mfpu=neon)
endif()
target_compile_options(${PROJECT_NAME} PRIVATE
+16 -10
View File
@@ -18,27 +18,33 @@ If you're having any trouble after reading through this `README`, feel free ask
Starship does not include any copyrighted assets. You are required to provide a supported copy of the game.
### 1. Verify your ROM dump
The supported ROM is the USA 1.1 Rev A version. You can verify you have dumped a supported copy of the game by using the SHA-1 File Checksum Online at https://www.romhacking.net/hash/. The hash for a US 1.1 ROM is SHA-1: 09F0D105F476B00EFA5303A3EBC42E60A7753B7A.
The supported ROMs are US 1.0 and US 1.1 Rev A versions. You can verify you have dumped a supported copy of the game by using the SHA-1 File Checksum Online at https://www.romhacking.net/hash/.
* The SHA-1 hash for a US 1.0 ROM is D8B1088520F7C5F81433292A9258C1184AFA1457.
* The SHA-1 hash for a US 1.1 ROM is 09F0D105F476B00EFA5303A3EBC42E60A7753B7A.
Starship also supports voice language replacement use from both EU (Lylat) and JP (Japanese) when used in conjunction with an US ROM.
Note: JP and EU versions of the game are not supported for the base asset O2R creation, a US ROM must be used for it.
### 2. Verify your ROM is in .z64 format
Your ROM needs to be in .z64 format. If it's in .n64 format, use the following to convert it to a .z64: https://hack64.net/tools/swapper.php
### 2. Download Starship from [Releases](https://github.com/HarbourMasters/Starship/releases)
### 3. Generating the O2R from the ROM
### 3. Generating the OTR from the ROM and Play!
#### Windows
* Extract every file from the zip into a folder of your choosing.
* Copy your ROM to the root of the folder you extracted the zip to.
* Run "generate_o2r.bat"
* Run starship.exe and select your US 1.0 or US 1.1 ROM.
#### Linux
* Extract every file from the zip into a folder of your choosing.
* Execute starship.appimage. You may have to chmod +x the appimage via terminal.
* Select your US 1.0 or US 1.1 ROM.
#### MacOS
* Extract every file from the zip into a folder of your choosing.
* Copy your ROM to the root of the folder you extracted the zip to.
* Run "generate_o2r.sh"
### 4. Play!
* Launch `Starship.exe`
Congratulations, you are now sailing with Starship! Have fun!
* Run starship and select your US 1.0 or US 1.1 ROM.
# Configuration
+9 -11
View File
@@ -144,17 +144,13 @@ void Audio_InitNoteSub(Note* note, NoteAttributes* noteAttr) {
panVolumeCenter = 1.0f;
}
else if (stereo.s.is_sfx) { // SFX
float pan_angle = (float)(pan + 64) / 128 * 2 * M_PI;
float pan_angle = ((float) pan) / 128 * 2 * M_PI;
// Speaker angles in radians
const float front_left = -0.5236;
const float front_right = 0.5236;
const float rear_left = -1.92;
const float rear_right = 1.92;
// Normalize pan_angle to [0, 2π]
pan_angle = fmodf(pan_angle, 2 * M_PI);
if (pan_angle < 0) pan_angle += 2 * M_PI;
const float front_left = (CVarGetInteger("gPositionFrontLeft", 240) - 90) * (M_PI / 180.0f);
const float front_right = (CVarGetInteger("gPositionFrontRight", 300) - 90) * (M_PI / 180.0f);
const float rear_left = (CVarGetInteger("gPositionRearLeft", 160) - 90) * (M_PI / 180.0f);
const float rear_right = (CVarGetInteger("gPositionRearRight", 20) - 90) * (M_PI / 180.0f);
// Calculate volumes using cosine panning law
panVolumeLeft = fmaxf(0, cosf(pan_angle - front_left)); // Front Left
@@ -164,8 +160,10 @@ void Audio_InitNoteSub(Note* note, NoteAttributes* noteAttr) {
} else { // MUSIC
panVolumeLeft = gStereoPanVolume[pan];
panVolumeRight = gStereoPanVolume[ARRAY_COUNT(gStereoPanVolume) - 1 - pan];
panVolumeRearLeft = gStereoPanVolume[pan];
panVolumeRearRight = gStereoPanVolume[ARRAY_COUNT(gStereoPanVolume) - 1 - pan];
f32 rearMusicVolume = CVarGetFloat("gVolumeRearMusic", 1.0f);
panVolumeRearLeft = gStereoPanVolume[pan] * rearMusicVolume;
panVolumeRearRight = gStereoPanVolume[ARRAY_COUNT(gStereoPanVolume) - 1 - pan] * rearMusicVolume;
}
}
+5 -4
View File
@@ -1368,6 +1368,7 @@ Acmd* AudioSynth_ProcessEnvelope(Acmd* aList, NoteSubEu* noteSub, NoteSynthesisS
synthState->curVolLfe = curVolLfe + (rampLfe * aiBufLenSmall);
synthState->curVolRLeft = curVolRLeft + (rampRLeft * aiBufLenSmall);
synthState->curVolRRight = curVolRRight + (rampRRight * aiBufLenSmall);
uint32_t cutoffFreqLfe = CVarGetInteger("gSubwooferThreshold", 80);
if (noteSub->bitField0.usesHeadsetPanEffects) {
int32_t num_audio_channels = 2;
@@ -1378,24 +1379,24 @@ Acmd* AudioSynth_ProcessEnvelope(Acmd* aList, NoteSubEu* noteSub, NoteSynthesisS
switch (delaySide) {
case HAAS_EFFECT_DELAY_LEFT:
aEnvMixer(aList++, dmemSrc, aiBufLen, ((sourceReverbVol & 0x80) >> 7),
noteSub->bitField0.stereoStrongRight, noteSub->bitField0.stereoStrongLeft, (DMEM_WET_LEFT_CH << 16) | DMEM_LEFT_CH, DMEM_HAAS_TEMP << 16, num_audio_channels);
noteSub->bitField0.stereoStrongRight, noteSub->bitField0.stereoStrongLeft, (DMEM_WET_LEFT_CH << 16) | DMEM_LEFT_CH, DMEM_HAAS_TEMP << 16, num_audio_channels, cutoffFreqLfe);
break;
case HAAS_EFFECT_DELAY_RIGHT:
aEnvMixer(aList++, dmemSrc, aiBufLen, ((sourceReverbVol & 0x80) >> 7),
noteSub->bitField0.stereoStrongRight, noteSub->bitField0.stereoStrongLeft, (DMEM_WET_LEFT_CH << 16) | DMEM_LEFT_CH, DMEM_HAAS_TEMP, num_audio_channels);
noteSub->bitField0.stereoStrongRight, noteSub->bitField0.stereoStrongLeft, (DMEM_WET_LEFT_CH << 16) | DMEM_LEFT_CH, DMEM_HAAS_TEMP, num_audio_channels, cutoffFreqLfe);
break;
default: // HAAS_EFFECT_DELAY_NONE
aEnvMixer(aList++, dmemSrc, aiBufLen, ((sourceReverbVol & 0x80) >> 7),
noteSub->bitField0.stereoStrongRight, noteSub->bitField0.stereoStrongLeft, (DMEM_WET_LEFT_CH << 16) | DMEM_LEFT_CH, 0, num_audio_channels);
noteSub->bitField0.stereoStrongRight, noteSub->bitField0.stereoStrongLeft, (DMEM_WET_LEFT_CH << 16) | DMEM_LEFT_CH, 0, num_audio_channels, cutoffFreqLfe);
break;
}
} else {
aEnvSetup1(aList++, (sourceReverbVol & 0x7F), rampReverb, rampLeft, rampRight, rampCenter, rampLfe, rampRLeft, rampRRight);
aEnvSetup2(aList++, curVolLeft, curVolRight, curVolCenter, curVolLfe, curVolRLeft, curVolRRight);
aEnvMixer(aList++, dmemSrc, aiBufLen, ((sourceReverbVol & 0x80) >> 7),
noteSub->bitField0.stereoStrongRight, noteSub->bitField0.stereoStrongLeft, (DMEM_WET_LEFT_CH << 16) | DMEM_LEFT_CH, 0, GetNumAudioChannels());
noteSub->bitField0.stereoStrongRight, noteSub->bitField0.stereoStrongLeft, (DMEM_WET_LEFT_CH << 16) | DMEM_LEFT_CH, 0, GetNumAudioChannels(), cutoffFreqLfe);
}
return aList;
+3 -3
View File
@@ -77,7 +77,6 @@ static __m128i m256i_clamp_to_m128i(m256i a) {
#define BUF_S16(a) (int16_t*) BUF_U8(a)
#define SAMPLE_RATE 32000 // Adjusted to match the actual sample rate of 32 kHz
#define CUTOFF_FREQ_LFE 80 // Cutoff frequency of 80 Hz
static struct {
uint16_t in;
@@ -645,7 +644,8 @@ void aEnvSetup2Impl(uint16_t initial_vol_left, uint16_t initial_vol_right, int16
void aEnvMixerImpl(uint16_t in_addr, uint16_t n_samples, bool swap_reverb,
bool neg_left, bool neg_right,
uint32_t wet_dry_addr, uint32_t haas_temp_addr, uint32_t num_channels)
uint32_t wet_dry_addr, uint32_t haas_temp_addr, uint32_t num_channels,
uint32_t cutoff_freq_lfe)
{
// Note: max number of samples is 192 (192 * 2 = 384 bytes = 0x180)
int max_num_samples = 192;
@@ -675,7 +675,7 @@ void aEnvMixerImpl(uint16_t in_addr, uint16_t n_samples, bool swap_reverb,
if (num_channels == 6) {
// Calculate the filter coefficient
float RC = 1.f / (2 * M_PI * CUTOFF_FREQ_LFE);
float RC = 1.f / (2 * M_PI * cutoff_freq_lfe);
float dt = 1.f / SAMPLE_RATE;
float alpha = dt / (RC + dt);
+4 -3
View File
@@ -47,7 +47,8 @@ void aEnvSetup1Impl(uint8_t initial_vol_wet, uint16_t rate_wet, uint16_t rate_le
void aEnvSetup2Impl(uint16_t initial_vol_left, uint16_t initial_vol_right, int16_t initial_vol_center,
int16_t initial_vol_lfe, int16_t initial_vol_rear_left, int16_t initial_vol_rear_right);
void aEnvMixerImpl(uint16_t in_addr, uint16_t n_samples, bool swap_reverb, bool neg_left,
bool neg_right, uint32_t wet_dry_addr, uint32_t haas_temp_addr, uint32_t num_channels);
bool neg_right, uint32_t wet_dry_addr, uint32_t haas_temp_addr, uint32_t num_channels,
uint32_t cutoff_freq_lfe);
void aMixImpl(uint16_t count, int16_t gain, uint16_t in_addr, uint16_t out_addr);
void aS8DecImpl(uint8_t flags, ADPCM_STATE state);
void aAddMixerImpl(uint16_t count, uint16_t in_addr, uint16_t out_addr);
@@ -76,8 +77,8 @@ void aUnkCmd19Impl(uint8_t f, uint16_t count, uint16_t out_addr, uint16_t in_add
aEnvSetup1Impl(initialVolReverb, rampReverb, rampLeft, rampRight, rampCenter, rampLfe, rampRLeft, rampRRight)
#define aEnvSetup2(pkt, initialVolLeft, initialVolRight, initialVolCenter, initialVolLfe, initialVolRLeft, initialVolRRight) \
aEnvSetup2Impl(initialVolLeft, initialVolRight, initialVolCenter, initialVolLfe, initialVolRLeft, initialVolRRight)
#define aEnvMixer(pkt, inAddr, nSamples, swapReverb, negLeft, negRight, wetDryAddr, haasTempAddr, numChannels) \
aEnvMixerImpl(inAddr, nSamples, swapReverb, negLeft, negRight, wetDryAddr, haasTempAddr, numChannels)
#define aEnvMixer(pkt, inAddr, nSamples, swapReverb, negLeft, negRight, wetDryAddr, haasTempAddr, numChannels, cutoffFreqLfe) \
aEnvMixerImpl(inAddr, nSamples, swapReverb, negLeft, negRight, wetDryAddr, haasTempAddr, numChannels, cutoffFreqLfe)
#define aMix(pkt, c, g, i, o) aMixImpl(c, g, i, o)
#define aS8Dec(pkt, f, s) aS8DecImpl(f, s)
#define aAddMixer(pkt, s, d, c) aAddMixerImpl(s, d, c)
+17
View File
@@ -432,6 +432,10 @@ void Display_LandmasterThrusters(Player* player) {
}
Matrix_Push(&gGfxMatrix);
// @port: Tag the transform.
FrameInterpolation_RecordOpenChild("Display_LandmasterThrusters_1", player->num);
Matrix_Translate(gGfxMatrix, 20.0f, 30.0f, -10.0f, MTXF_APPLY);
if (!gVersusMode) {
@@ -447,6 +451,10 @@ void Display_LandmasterThrusters(Player* player) {
} else {
gSPDisplayList(gMasterDisp++, D_versus_301B6E0);
}
// @port Pop the transform id.
FrameInterpolation_RecordCloseChild();
Matrix_Pop(&gGfxMatrix);
}
@@ -461,6 +469,10 @@ void Display_LandmasterThrusters(Player* player) {
}
Matrix_Push(&gGfxMatrix);
// @port: Tag the transform.
FrameInterpolation_RecordOpenChild("Display_LandmasterThrusters_2", player->num);
Matrix_Translate(gGfxMatrix, -20.0f, 30.0f, -10.0f, MTXF_APPLY);
if (!gVersusMode) {
@@ -476,8 +488,13 @@ void Display_LandmasterThrusters(Player* player) {
} else {
gSPDisplayList(gMasterDisp++, D_versus_301B6E0);
}
// @port Pop the transform id.
FrameInterpolation_RecordCloseChild();
Matrix_Pop(&gGfxMatrix);
}
Matrix_Pop(&gGfxMatrix);
}
+20 -1
View File
@@ -218,6 +218,9 @@ void Effect_Effect372_Draw(Effect372* this) {
}
void Effect_Effect382_Draw(Effect382* this) {
// @port Skip interpolation
FrameInterpolation_ShouldInterpolateFrame(false);
RCP_SetupDL_49();
gDPSetPrimColor(gMasterDisp++, 0, 0, 255, 255, 255, this->unk_44);
gDPSetEnvColor(gMasterDisp++, 255, 255, 255, this->unk_44);
@@ -225,6 +228,10 @@ void Effect_Effect382_Draw(Effect382* this) {
Matrix_Translate(gGfxMatrix, 0.0f, 20.0f, 0.0f, MTXF_APPLY);
Matrix_SetGfxMtx(&gMasterDisp);
gSPDisplayList(gMasterDisp++, D_ZO_6024220);
// @port renable interpolation
FrameInterpolation_ShouldInterpolateFrame(true);
RCP_SetupDL(&gMasterDisp, SETUPDL_64);
}
@@ -746,6 +753,9 @@ void Effect_Effect357_Draw(Effect357* this) {
gSPFogPosition(gMasterDisp++, gFogNear, 1005);
}
// @port: Tag the transform.
FrameInterpolation_RecordOpenChild("Effect357", this->unk_4C | (this->index << 16) & 0x00FF);
Graphics_SetScaleMtx(this->scale2);
switch (gCurrentLevel) {
@@ -887,6 +897,8 @@ void Effect_Effect357_Draw(Effect357* this) {
}
break;
}
// @port Pop the transform id.
FrameInterpolation_RecordCloseChild();
RCP_SetupDL(&gMasterDisp, SETUPDL_64);
@@ -2276,11 +2288,18 @@ void Effect_Effect374_Draw(Effect374* this) {
break;
case 1:
// @port Skip interpolation
FrameInterpolation_ShouldInterpolateFrame(false);
Matrix_Scale(gGfxMatrix, this->scale1, this->scale2, 2.5f, MTXF_APPLY);
Matrix_SetGfxMtx(&gMasterDisp);
RCP_SetupDL_40();
gSPClearGeometryMode(gMasterDisp++, G_CULL_BACK);
gSPDisplayList(gMasterDisp++, D_ENMY_PLANET_4008F70);
// @port renable interpolation
FrameInterpolation_ShouldInterpolateFrame(true);
RCP_SetupDL(&gMasterDisp, SETUPDL_64);
break;
}
@@ -3795,7 +3814,7 @@ void Effect_Effect395_Update(Effect395* this) {
D_ctx_801779A8[0] = 50.0f;
if (this->unk_46 == 10) {
gFillScreenRed = gFillScreenGreen = gFillScreenBlue = 255;
if (CVarGetInteger("gDisableGorgonFlash", 0) == 0){
if (CVarGetInteger("gDisableGorgonFlash", 0) == 0) {
gFillScreenAlpha = gFillScreenAlphaTarget = 255;
}
gFillScreenAlphaTarget = 0;
+1
View File
@@ -34,6 +34,7 @@ void Load_RomFile(void* vRomAddress, void* dest, ptrdiff_t size) {
u8 Load_SceneFiles(Scene* scene) {
#if 1
// TODO: BUG! sCurrentScene and scene never change so this will never be true.
bool hasSceneChanged = memcmp(&sCurrentScene, scene, sizeof(Scene)) != 0;
sCurrentScene = *scene;
return hasSceneChanged;
+2
View File
@@ -42,6 +42,8 @@ void Lib_Texture_Scroll(u16* texture, s32 width, s32 height, u8 mode) {
width = newWidth;
height = newHeight;
scale = 1; // TODO: a higher scale causes performance issues for large textures ?
for(s32 i = 0; i < (s32) scale; i++){
switch (mode) {
case 0:
+6 -4
View File
@@ -97,7 +97,7 @@ void func_versus_800BC9DC(f32 xPos, f32 yPos, f32 scale, s32 yScale) {
s32 D_800D4AB0[] = { 40, 64, 64 };
Lib_TextureRect_CI8(&gMasterDisp, D_800D4ABC[yScale], D_800D4AA4[yScale], D_800D4AB0[yScale], 40, xPos, yPos, scale,
scale);
scale);
}
void func_versus_800BCB44(f32 xPos, f32 yPos, f32 scale) {
@@ -110,7 +110,7 @@ void func_versus_800BCC48(f32 xPos, f32 yPos, f32 xScale, f32 yScale, s32 arg4)
s32 D_800D4AE8[] = { 104, 152, 168, 152 };
Lib_TextureRect_CI8(&gMasterDisp, D_800D4AD8[arg4], D_800D4AC8[arg4], D_800D4AE8[arg4], 25, xPos, yPos, xScale,
yScale);
yScale);
}
void func_versus_800BCE24(f32 xPos, f32 yPos, f32 xScale, f32 yScale) {
@@ -168,8 +168,7 @@ void func_versus_800BD350(f32 xPos, f32 yPos) {
}
void func_versus_800BD3A8(f32 xPos, f32 yPos) {
Lib_TextureRect_CI4(&gMasterDisp, aVsHandicapFrameTex, aVsHandicapFrameTLUT, 80, 71, xPos, yPos, 1.0f,
1.0f);
Lib_TextureRect_CI4(&gMasterDisp, aVsHandicapFrameTex, aVsHandicapFrameTLUT, 80, 71, xPos, yPos, 1.0f, 1.0f);
}
void func_versus_800BD4D4(f32 xPos, f32 yPos, s32 arg2) {
@@ -1262,6 +1261,9 @@ s32 func_versus_800C1138(s32 max, s32 arg1) {
void Versus_InitMatch(void) {
s32 i;
// Until Load_SceneFiles gets fixed, this fixes most of the audio issues in versus
AUDIO_SET_SPEC_ALT(SFXCHAN_3, AUDIOSPEC_16);
for (i = 0, sVsPlayerCount = 0; i < 4; i++) {
if (!gPlayerInactive[i]) {
sVsPlayerCount++;
+37 -1
View File
@@ -48,6 +48,25 @@ bool D_ending_80198584;
s32 D_ending_80198588;
s32 D_ending_8019858C;
void Ending_Port_InitOverlay(void) {
D_ending_80192E70 = 0; // ending sequence frame counter
D_ending_80196D04 = 0; // ending text frame counter
memset(D_ending_80196D08, 0, sizeof(D_ending_80196D08));
D_ending_80196F88 = 0;
D_ending_80196F8C = 0;
D_ending_80196F90 = 0;
D_ending_80196F94 = 0;
D_ending_80196F98 = 0;
D_ending_80196F9C = 0.0f;
memset(D_ending_80196FA0, 0, sizeof(D_ending_80196FA0));
memset(D_ending_80197900, 0, sizeof(D_ending_80197900));
memset(D_ending_80198260, 0, sizeof(D_ending_80198260));
D_ending_80198580 = 0.0f;
D_ending_80198584 = false;
D_ending_80198588 = 0;
D_ending_8019858C = 0;
}
const char str1[] = "fogR= %d, fogG= %d, fogB= %d\n";
const char str2[] = "ligR= %d, ligG= %d, ligB= %d\n";
const char str3[] = "kanR= %d, kanG= %d, kanB= %d\n";
@@ -1028,8 +1047,19 @@ void Ending_Main(void) {
gCsFrameCount++;
gGameFrameCount++;
if (gSaveFile.save.data.padEE[0] == 1) {
gControllerLock = 0;
if (gControllerPress[0].button & START_BUTTON) {
D_ending_80196D00 = 7;
D_ending_80196D04 = 7200;
D_ending_80192E70 = 7200;
}
} else {
gControllerLock = 10000;
}
switch (D_ending_80196D00) {
case 0:
case 0: // Ending Init
gRadioState = 0;
gGameFrameCount = 0;
gSceneSetup = 0;
@@ -1037,6 +1067,12 @@ void Ending_Main(void) {
gCsCamAtX = gCsCamAtY = 0.0f;
gCsCamAtZ = -100.0f;
D_ending_80196D00 = 1;
// @port Bugfix:
// In the original game, these variables are set to zero when the overlay is reloaded.
// Since we don't use overlays, the absence of this initializer causes the ending not to play
// as it should after a the first playthrough.
Ending_Port_InitOverlay();
break;
case 1:
+27 -3
View File
@@ -458,8 +458,24 @@ void Ending_8018EDB8(u32 arg0, AssetInfo* asset) {
gDPLoadTextureBlock(gMasterDisp++, D_END_700EA38, G_IM_FMT_RGBA, G_IM_SIZ_16b, 32, 32, 0, G_TX_WRAP | G_TX_NOMIRROR,
G_TX_WRAP | G_TX_NOMIRROR, 5, 5, G_TX_NOLOD, G_TX_NOLOD);
gDPSetupTile(gMasterDisp++, G_IM_FMT_RGBA, G_IM_SIZ_16b, 32, 32, arg0 * 14, 0, G_TX_NOMIRROR | G_TX_WRAP,
G_TX_NOMIRROR | G_TX_WRAP, 5, 5, G_TX_NOLOD, G_TX_NOLOD);
int interpolatedFrames = GameEngine_GetInterpolationFrameCount();
float scrollArg = arg0 * 14;
float inc = 14 / (float) interpolatedFrames;
for (int i = 0; i < interpolatedFrames; i++) {
gDPSetInterpolation(gMasterDisp++, i);
gDPSetupTile2(gMasterDisp++, G_IM_FMT_RGBA, G_IM_SIZ_16b, 32, 32, scrollArg, 0, G_TX_NOMIRROR | G_TX_WRAP,
G_TX_NOMIRROR | G_TX_WRAP, 5, 5, G_TX_NOLOD, G_TX_NOLOD);
gDPSetTileSizeInterp(gMasterDisp, G_TX_RENDERTILE, scrollArg, 0, 32 << 2, 0);
gMasterDisp += 3;
scrollArg += inc;
}
gSPDisplayList(gMasterDisp++, D_END_700E9E0);
}
@@ -820,6 +836,10 @@ void Ending_80191234(u32 arg0, AssetInfo* asset) {
gBgColor = 0;
gStarCount = 0;
gControllerLock = 10;
// @port: Ending seen at least once.
gSaveFile.save.data.padEE[0] = 1;
Save_Write();
}
void Ending_80191294(u32 arg0, AssetInfo* asset) {
@@ -1083,7 +1103,11 @@ void Ending_801924EC(u32 arg0) {
}
void Ending_801926D4(void) {
gControllerLock = 10000;
if (gSaveFile.save.data.padEE[0] == 1) {
gControllerLock = 0;
} else {
gControllerLock = 10000;
}
Matrix_Push(&gGfxMatrix);
+6 -6
View File
@@ -5925,11 +5925,17 @@ void Macbeth_MaBombDrop_Draw(MaBombDrop* this) {
break;
case 1:
// @port Skip interpolation
FrameInterpolation_ShouldInterpolateFrame(false);
Matrix_Scale(gGfxMatrix, this->fwork[0], this->scale, 2.5f, MTXF_APPLY);
Matrix_SetGfxMtx(&gMasterDisp);
RCP_SetupDL_40();
gSPClearGeometryMode(gMasterDisp++, G_CULL_BACK);
gSPDisplayList(gMasterDisp++, D_ENMY_PLANET_4008F70);
// @port renable interpolation
FrameInterpolation_ShouldInterpolateFrame(true);
RCP_SetupDL(&gMasterDisp, SETUPDL_64);
break;
}
@@ -6496,12 +6502,6 @@ f32 D_i5_801BA854[8] = { 1.5f, -1.0f, 0.7f, 0.0f, 0.9f, 0.7f, -1.0f, 1.5f };
f32 D_i5_801BA874[8] = { 200.0f, 300.0f, 400.0f, 0.0f, 500.0f, 100.0f, 120.0f, 100.0f };
f32 D_i5_801BA894[8] = { 200.0f, 250.0f, 220.0f, 0.0f, 200.0f, 230.0f, 220.0f, 350.0f };
void Macbeth_LevelComplete2(Player* player) {
s32 i;
s32 j;
+9 -4
View File
@@ -1959,7 +1959,7 @@ void Option_Data_Select(void) {
}
void Option_Data_Draw(void) {
s32 i;
s32 i = 0;
s32 sp7C[2];
s32 mask[2];
static f32 D_menu_801AF084[2] = { 172.0f, 76.0f };
@@ -1974,8 +1974,7 @@ void Option_Data_Draw(void) {
gDPSetPrimColor(gMasterDisp++, 0, 0, 255, 255, 255, 255);
Lib_TextureRect_IA8(&gMasterDisp, D_OPT_80084B0, 176, 13, D_menu_801AF094[0], D_menu_801AF0AC[0] + (4.0f * i), 1.0f,
1.0f);
Lib_TextureRect_IA8(&gMasterDisp, D_OPT_80084B0, 176, 13, D_menu_801AF094[0], D_menu_801AF0AC[0], 1.0f, 1.0f);
if (D_menu_801B91CC < 2) {
Lib_TextureRect_IA8_MirX(&gMasterDisp, aArrowTex, 8, 8, D_menu_801AF084[D_menu_801B91C0], 140.0f, 1.0f, 1.0f);
@@ -2250,7 +2249,7 @@ void Option_80197914(void) {
// @port: Tag the transform.
FrameInterpolation_RecordOpenChild("RANKING_BORDERS", i);
Matrix_Translate(gGfxMatrix, vec1->x, vec1->y, -500.0f, MTXF_APPLY);
Matrix_Translate(gGfxMatrix, vec1->x, vec1->y, -500.0f, MTXF_APPLY);
// @port: Increase the scale by 2.5f to compensate for missing borders
Matrix_Scale(gGfxMatrix, vec2->x * 4, vec2->y + 2.5f, 1.0f, MTXF_APPLY);
@@ -3492,6 +3491,9 @@ void Option_DrawMenuCard(OptionCardFrame arg0) {
Matrix_Push(&gGfxMatrix);
// @port: Tag the transform.
FrameInterpolation_RecordOpenChild("MenuCard", (u32) & arg0);
Matrix_Translate(gGfxMatrix, arg0.x, arg0.y, arg0.z, MTXF_APPLY);
Matrix_Scale(gGfxMatrix, arg0.xScale, arg0.yScale, 1.0f, MTXF_APPLY);
Matrix_RotateX(gGfxMatrix, M_DTOR * 90.0f, MTXF_APPLY);
@@ -3502,6 +3504,9 @@ void Option_DrawMenuCard(OptionCardFrame arg0) {
Matrix_Pop(&gGfxMatrix);
// @port Pop the transform id.
FrameInterpolation_RecordCloseChild();
Lib_InitPerspective(&gMasterDisp);
}
+2
View File
@@ -146,6 +146,8 @@ f32 D_menu_801B9078;
f32 D_menu_801B907C;
f32 D_menu_801B9080;
f32 D_menu_801B9084;
// @port: Timer for drawing the mirrored Great Fox Deck.
s32 segataSanshiroTimer = 0;
TitleAnimation sTeamAnim[4] = {
+4 -4
View File
@@ -88,7 +88,7 @@ GameEngine::GameEngine() {
if (std::filesystem::exists(main_path)) {
archiveFiles.push_back(main_path);
} else {
if (ShowYesNoBox("No O2R Files", "No O2R files found. Generate one now?") == IDYES) {
if (ShowYesNoBox("Starship - Asset Extraction", "Please provide a Starfox 64 ROM.\n\nSupported Versions:\nUS 1.0\nUS 1.1\n\nAssets will be extracted into an O2R file.") == IDYES) {
if(!GenAssetFile()){
ShowMessage("Error", "An error occured, no O2R file was generated.\n\nExiting...");
exit(1);
@@ -96,7 +96,7 @@ GameEngine::GameEngine() {
archiveFiles.push_back(main_path);
}
if (ShowYesNoBox("Extraction Complete", "ROM Extracted. Extract another?") == IDYES) {
if (ShowYesNoBox("Extraction Complete", "ROM Extracted. Extract another?\n\n Starship supports JP and EU ROMs for voice replacement.\n Voice replacement ROM assets can also be installed in:\n Settings->Language->Install JP/EU Audio") == IDYES) {
if(!GenAssetFile()){
ShowMessage("Error", "An error occured, no O2R file was generated.");
}
@@ -291,7 +291,7 @@ bool GameEngine::GenAssetFile(bool exitOnFail) {
}
}
ShowMessage(("Found " + game.value()).c_str(), "The extraction process will now begin.\n\nThis may take a few minutes.", SDL_MESSAGEBOX_INFORMATION);
ShowMessage(("Starship - Extraction - Found " + game.value()).c_str(), "The extraction process will now begin.\n\nThis may take a few minutes.", SDL_MESSAGEBOX_INFORMATION);
return extractor->GenerateOTR();
}
@@ -522,7 +522,7 @@ void GameEngine::ProcessGfxCommands(Gfx* commands) {
if (wnd != nullptr) {
wnd->SetTargetFps(fps);
wnd->SetMaximumFrameLatency(CVarGetInteger("gRenderParallelization", 0) ? 2 : 1);
wnd->SetMaximumFrameLatency(CVarGetInteger("gRenderParallelization", 1) ? 2 : 1);
}
// When the gfx debugger is active, only run with the final mtx
+131 -3
View File
@@ -116,6 +116,117 @@ static const char* voiceLangs[] = {
"Original", /*"Japanese",*/ "Lylat"
};
void DrawSpeakerPositionEditor() {
static ImVec2 lastCanvasPos;
ImGui::Text("Speaker Position Editor");
ImVec2 canvasSize = ImVec2(200, 200); // Static canvas size
ImVec2 canvasPos = ImGui::GetCursorScreenPos();
ImVec2 center = ImVec2(canvasPos.x + canvasSize.x / 2, canvasPos.y + canvasSize.y / 2);
// Speaker positions
static ImVec2 speakerPositions[4];
static bool initialized = false;
static float radius = 80.0f;
// Reset positions if canvas position changed (window resized/moved)
if (!initialized || (lastCanvasPos.x != canvasPos.x || lastCanvasPos.y != canvasPos.y)) {
const char* cvarNames[4] = { "gPositionFrontLeft", "gPositionFrontRight", "gPositionRearLeft", "gPositionRearRight" };
float angles[4] = { 240.f, 300.f, 160.f, 20.f }; // Default angles
for (int i = 0; i < 4; i++) {
int savedAngle = CVarGetInteger(cvarNames[i], -1);
if (savedAngle != -1) {
angles[i] = static_cast<float>(savedAngle);
}
float rad = angles[i] * (M_PI / 180.0f);
speakerPositions[i] = ImVec2(center.x + radius * cosf(rad), center.y + radius * sinf(rad));
}
initialized = true;
lastCanvasPos = canvasPos;
}
// Draw canvas
ImDrawList* drawList = ImGui::GetWindowDrawList();
drawList->AddRectFilled(canvasPos, ImVec2(canvasPos.x + canvasSize.x, canvasPos.y + canvasSize.y), IM_COL32(26, 26, 26, 255));
drawList->AddCircleFilled(center, 5.0f, IM_COL32(255, 255, 255, 255)); // Central person
// Draw circle line for speaker positions
drawList->AddCircle(center, radius, IM_COL32(163, 163, 163, 255), 100);
// Add markers at 0, 22.5, 45, etc.
for (float angle = 0; angle < 360; angle += 22.5f) {
float rad = angle * (M_PI / 180.0f);
ImVec2 markerStart = ImVec2(center.x + (radius - 5) * cosf(rad), center.y + (radius - 5) * sinf(rad));
ImVec2 markerEnd = ImVec2(center.x + radius * cosf(rad), center.y + radius * sinf(rad));
drawList->AddLine(markerStart, markerEnd, IM_COL32(163, 163, 163, 255));
}
const char* speakerLabels[4] = { "L", "R", "RL", "RR" };
const char* cvarNames[4] = { "gPositionFrontLeft", "gPositionFrontRight", "gPositionRearLeft", "gPositionRearRight" };
const float snapThreshold = 2.5f; // Degrees within which snapping occurs
for (int i = 0; i < 4; i++) {
// Draw speaker as a darker blue circle
drawList->AddCircleFilled(speakerPositions[i], 10.0f, IM_COL32(34, 52, 78, 255)); // Dark blue color
drawList->AddText(ImVec2(speakerPositions[i].x - 6, speakerPositions[i].y - 6), IM_COL32(255, 255, 255, 255), speakerLabels[i]);
// Handle dragging
ImGui::SetCursorScreenPos(ImVec2(speakerPositions[i].x - 10, speakerPositions[i].y - 10));
ImGui::InvisibleButton(speakerLabels[i], ImVec2(20, 20));
if (ImGui::IsItemActive() && ImGui::IsMouseDragging(ImGuiMouseButton_Left)) {
ImVec2 mouseDelta = ImGui::GetMouseDragDelta(ImGuiMouseButton_Left);
ImVec2 newPos = ImVec2(speakerPositions[i].x + mouseDelta.x, speakerPositions[i].y + mouseDelta.y);
// Constrain position to the circle
ImVec2 direction = ImVec2(newPos.x - center.x, newPos.y - center.y);
float length = sqrtf(direction.x * direction.x + direction.y * direction.y);
ImVec2 constrainedPos = ImVec2(center.x + (direction.x / length) * radius, center.y + (direction.y / length) * radius);
// Calculate angle of the constrained position
float angle = atan2f(constrainedPos.y - center.y, constrainedPos.x - center.x) * (180.0f / M_PI);
if (angle < 0) angle += 360.0f;
// Snap to the nearest 22.5-degree marker if within the snap threshold
float snappedAngle = roundf(angle / 22.5f) * 22.5f;
if (fabsf(snappedAngle - angle) <= snapThreshold) {
float rad = snappedAngle * (M_PI / 180.0f);
constrainedPos = ImVec2(center.x + radius * cosf(rad), center.y + radius * sinf(rad));
}
speakerPositions[i] = constrainedPos;
ImGui::ResetMouseDragDelta();
// Save the updated angle to CVar after dragging
float updatedAngle = atan2f(speakerPositions[i].y - center.y, speakerPositions[i].x - center.x) * (180.0f / M_PI);
if (updatedAngle < 0) updatedAngle += 360.0f;
CVarSetInteger(cvarNames[i], static_cast<int>(updatedAngle));
Ship::Context::GetInstance()->GetWindow()->GetGui()->SaveConsoleVariablesNextFrame(); // Mark for saving
}
// Calculate angle and save to CVar
float angle = atan2f(speakerPositions[i].y - center.y, speakerPositions[i].x - center.x) * (180.0f / M_PI);
if (angle < 0) angle += 360.0f;
CVarSetInteger(cvarNames[i], static_cast<int>(angle));
}
// Reset cursor position for button placement
ImGui::SetCursorScreenPos(ImVec2(canvasPos.x, canvasPos.y + canvasSize.y + 10));
if (ImGui::Button("Reset Positions")) {
float defaultAngles[4] = { 240.f, 300.f, 160.f, 20.f };
for (int i = 0; i < 4; i++) {
float rad = defaultAngles[i] * (M_PI / 180.0f);
speakerPositions[i] = ImVec2(center.x + radius * cosf(rad), center.y + radius * sinf(rad));
CVarSetInteger(cvarNames[i], static_cast<int>(defaultAngles[i]));
}
Ship::Context::GetInstance()->GetWindow()->GetGui()->SaveConsoleVariablesNextFrame();
}
// Reset cursor position to ensure canvas size remains static
ImGui::SetCursorScreenPos(ImVec2(canvasPos.x, canvasPos.y + canvasSize.y + 10));
}
void DrawSettingsMenu(){
if(UIWidgets::BeginMenu("Settings")){
if (UIWidgets::BeginMenu("Audio")) {
@@ -173,6 +284,23 @@ void DrawSettingsMenu(){
}
UIWidgets::PaddedEnhancementCheckbox("Surround 5.1 (Needs reload)", "gAudioChannelsSetting", 1, 0);
if (CVarGetInteger("gAudioChannelsSetting", 0) == 1) {
// Subwoofer threshold
UIWidgets::CVarSliderInt("Subwoofer threshold (Hz)", "gSubwooferThreshold", 10u, 1000u, 80u, {
.tooltip = "The threshold for the subwoofer to be activated. Any sound under this frequency will be played on the subwoofer.",
.format = "%d",
});
// Rear music volume slider
UIWidgets::CVarSliderFloat("Rear music volume", "gVolumeRearMusic", 0.0f, 1.0f, 1.0f, {
.format = "%.0f%%",
.isPercentage = true,
});
// Configurable positioning of speakers
DrawSpeakerPositionEditor();
}
ImGui::EndMenu();
}
@@ -192,10 +320,10 @@ void DrawSettingsMenu(){
};
} else {
if (UIWidgets::Button("Install JP/EU Audio")) {
if (GameEngine::GenAssetFile()){
if (GameEngine::GenAssetFile(false)){
GameEngine::ShowMessage("Success", "Audio assets installed. Changes will be applied on the next startup.", SDL_MESSAGEBOX_INFORMATION);
Ship::Context::GetInstance()->GetWindow()->Close();
}
Ship::Context::GetInstance()->GetWindow()->Close();
}
}
ImGui::EndMenu();
@@ -321,7 +449,7 @@ void DrawSettingsMenu(){
UIWidgets::Tooltip("Matches interpolation value to the refresh rate of your display.");
if (Ship::Context::GetInstance()->GetWindow()->GetWindowBackend() == Ship::WindowBackend::FAST3D_DXGI_DX11) {
UIWidgets::PaddedEnhancementCheckbox("Render parallelization","gRenderParallelization", true, false);
UIWidgets::PaddedEnhancementCheckbox("Render parallelization","gRenderParallelization", true, false, {}, {}, {}, true);
UIWidgets::Tooltip(
"This setting allows the CPU to work on one frame while GPU works on the previous frame.\n"
"Recommended if you can't reach the FPS you set, despite it being set below your refresh rate "