mirror of
https://github.com/Xeeynamo/sotn-decomp.git
synced 2026-11-07 09:16:07 -10:00
Add playable Maria on PSX (#2638)
 The mod replaces Richter with Maria as a playable character. I created the folder `mods/`, where I put `maria-psx` and planning to move the debug module in as well. I am not sure if the path is what we can all agree on, but let's decide before merging this in. I re-compiled the PSP code by rebasing the address to `0x8013C000` with the simple `player.ld` in the repo root. I plan on migrating `RIC.BIN` to use it too and allow relocating the overlay. `stage.ld` and `servant.ld` will follow. This follows the `weapon0.ld` pattern found in the repo root. I am happy to move those new linker scripts anywhere you suggest me to. PSP Maria loads most of the graphics on the same Video RAM address that is used on PS1 by the second screen buffer. In `ovl_load.c` you will find some ifdef to keep the PSP match while relocating the Maria graphics to the Video RAM section that were used for Richter exclusive content (e.g. Kid Maria and the four beasts during the prologue). The graphics need to be strategically located to minimize the code changes on entities that use UV coordinates. The only exception so far is the `u -= 0x80;` you will find on the Dragon Crash. The palette also had to be relocated from `0x114` (PSP Maria) to `0x120` (PSX RIC). The reason is the 0x114 palette row is replaced at runtime by the NO1 and RNO1 overlay, resulting on a Maria all black and all her sub-weapons with the incorrect colours. Luckily the `enum MarPalette` made it very easy to re-locate everything. The HUD was the main source of issue, as it is found in DRA and not in the player overlay. I created a `PatchHudSubweaponUV` to patch DRA on the fly. Alucard and Richter have separate UV coordinates to show their respective subweapon graphics in the HUD. Since the mod replaces Richter, I am just patching the Richter UVs with Maria. Another issue is where the sub-weapon IDs need to be re-mapped. Maria uses the subweapon DOLL when subweapon == 0. As PS1 DRA will not show any icon when the subweapon is zero, I am pulling and pushing the correct ID right before and right after the player loop. I needed to do the same task for the other subweapons to reflect the PSP behaviour, where the Doll replaced Bible, Dragon replaces Vithubi and so on. It is all documented in the respective functions. Like the PSP, picking up a subweapon that does not exist for maria (e.g. Cross), will not dismiss the current subweapon and instead it will increase the heart count by 1. Maria on PSP allows to hold two sub-weapons. The active subweapon can be swapped with Triangle. While the swapping logic is present in the Maria overlay, the HUD cannot be patched. There is a way to make the HUD working nicely, but we need to agree to what extend this mod can go. All Maria grunts are absent: we need to find a way to convert PSP sound effects to PS1, with the appropriate VH/VB container. But I think it is out of scope for this PR. The graphics of the subweapon drops from candles will incorrectly show the Richter subweapons. I can think of two options to fix this: 1. Replace the UV in DRA. For example `D_800D1A28` contains the UVs of the Axe subweapon. If they get replaced by the correspondent Maria subweapon, the correct graphics will always be displayed. The downside is soft-resetting / gameover to play with Alucard will result in the wrong graphics. 2. We could patch the graphics file by blitting the Maria subweapons over the Richter ones. This has the advantage of being more self-contained. But the Bird icon cannot replace the Knife as they're of different size. EDIT: the palette will be incorrect, so this is not possible.
This commit is contained in:
1 parent
6c17c285c6
commit
52962323a7
23 files changed
+596
-93
No files matched your search
@@ -11,7 +11,7 @@ on:
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
format:
|
||||
push-docker-image:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Clone main repository
|
||||
|
||||
@@ -0,0 +1,74 @@
|
||||
name: Build Maria (PSP) for PSX
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
paths:
|
||||
- 'include/**'
|
||||
- 'src/maria/**'
|
||||
- 'mods/mariapsp-forpsx/**'
|
||||
pull_request:
|
||||
paths:
|
||||
- 'include/**'
|
||||
- 'src/maria/**'
|
||||
- 'mods/mariapsp-forpsx/**'
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
build:
|
||||
if: github.repository == 'Xeeynamo/sotn-decomp'
|
||||
strategy:
|
||||
matrix:
|
||||
version: ["us"]
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
VERSION: ${{ matrix.version }}
|
||||
steps:
|
||||
- name: Install requirements
|
||||
run: sudo apt-get install gcc-mipsel-linux-gnu
|
||||
- name: Clone repo
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
ref: ${{ github.ref }}
|
||||
submodules: false
|
||||
- uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: '>=1.24.4'
|
||||
- name: Get dependencies
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: 'disks/dependencies'
|
||||
key: sotn-pspeu-deps
|
||||
- name: Setting up dependencies
|
||||
working-directory: disks
|
||||
run: cat dependencies/* | tar -zxf -
|
||||
- name: Extract dependencies
|
||||
run: |
|
||||
mkdir -p ~/go/bin
|
||||
ln -s $(which go) ~/go/bin/
|
||||
VERSION=pspeu make extract_disk
|
||||
- name: build
|
||||
working-directory: mods/mariapsp-forpsx
|
||||
run: VERSION=${{ matrix.version }} make
|
||||
push-docker-image:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Clone main repository
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: false
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v3
|
||||
- name: Log in to Docker Hub
|
||||
uses: docker/login-action@v2
|
||||
with:
|
||||
username: ${{ vars.DOCKERHUB_USERNAME }}
|
||||
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||
- name: Build and push Docker image
|
||||
uses: docker/build-push-action@v6
|
||||
with:
|
||||
context: ./mods/mariapsp-forpsx
|
||||
file: ./mods/mariapsp-forpsx/Dockerfile
|
||||
push: true
|
||||
tags: ${{ vars.DOCKERHUB_USERNAME }}/sotn-mariapsp-forpsx:latest
|
||||
@@ -7,6 +7,7 @@ __pycache__
|
||||
.venv/
|
||||
asm/
|
||||
build/
|
||||
release/
|
||||
expected/
|
||||
disks/
|
||||
nonmatchings/
|
||||
|
||||
@@ -86,8 +86,7 @@ MWCCGAP_APP := $(TOOLS_DIR)/mwccgap/mwccgap.py
|
||||
DOSEMU_APP := $(or $(shell which dosemu),/usr/bin/dosemu)
|
||||
SATURN_SPLITTER_DIR := $(TOOLS_DIR)/saturn-splitter
|
||||
SATURN_SPLITTER_APP := $(SATURN_SPLITTER_DIR)/rust-dis/target/release/rust-dis
|
||||
SOTNDISK_DIR := $(TOOLS_DIR)/sotn-disk/
|
||||
SOTNDISK := $(GOPATH)/bin/sotn-disk
|
||||
SOTNDISK := bin/sotn-disk
|
||||
SOTNASSETS := bin/sotn-assets
|
||||
|
||||
# Directories
|
||||
@@ -209,7 +208,7 @@ format-src: bin/clang-format format-sotn-lint
|
||||
@# -print0 : print only the matching entries, delimit by NULL to
|
||||
@# ensure files with characters xargs uses as delimiters are
|
||||
@# properly handled
|
||||
find $(SRC_DIR) $(INCLUDE_DIR) \
|
||||
find $(SRC_DIR) $(INCLUDE_DIR) mods \
|
||||
-type d \( -name 3rd -o -name CMakeFiles -o -name gen \) -prune \
|
||||
-o \( -type f \( -name '*.c' -o -name '*.h' \) \) \
|
||||
-print0 \
|
||||
@@ -242,7 +241,7 @@ format-symbols-pspeu: format-symbols-pspeu-sort
|
||||
format-symbols: format-symbols-us format-symbols-hd format-symbols-pspeu
|
||||
|
||||
format-license:
|
||||
find src/ | grep -E '\.c$$|\.h$$' | grep -vE 'PsyCross|mednafen|psxsdk|3rd|saturn/lib' | $(PYTHON) ./tools/lint-license.py - AGPL-3.0-or-later
|
||||
find src/ mods/ | grep -E '\.c$$|\.h$$' | grep -vE 'PsyCross|mednafen|psxsdk|3rd|saturn/lib' | $(PYTHON) ./tools/lint-license.py - AGPL-3.0-or-later
|
||||
$(PYTHON) ./tools/lint-license.py include/game.h AGPL-3.0-or-later
|
||||
$(PYTHON) ./tools/lint-license.py include/entity.h AGPL-3.0-or-later
|
||||
$(PYTHON) ./tools/lint-license.py include/items.h AGPL-3.0-or-later
|
||||
@@ -459,7 +458,7 @@ $(GO):
|
||||
tar -C $(HOME) -xzf go1.22.4.linux-amd64.tar.gz
|
||||
rm go1.22.4.linux-amd64.tar.gz
|
||||
$(SOTNDISK): $(GO) $(SOTNDISK_SOURCES)
|
||||
cd tools/sotn-disk; $(GO) install
|
||||
$(GO) build -C tools/sotn-disk -o ../../$@ .
|
||||
$(SOTNASSETS): $(GO) $(SOTNASSETS_SOURCES)
|
||||
$(GO) build -C tools/sotn-assets -o ../../$@ .
|
||||
|
||||
|
||||
@@ -72,6 +72,7 @@ CompMatrix = 0x8927678;
|
||||
DpqColor = 0x0892788C;
|
||||
NormalColorDpq = 0x089278B0;
|
||||
NormalClip = 0x089278D4;
|
||||
RotTrans = 0x892796C;
|
||||
RotTransPers = 0x89279C4;
|
||||
RotTransPers4 = 0x8927AF8;
|
||||
RotAverage3 = 0x8927B88;
|
||||
|
||||
@@ -2108,7 +2108,6 @@ u8* GetLangAt(s32 idx, u8* en, u8* fr, u8* sp, u8* ge, u8* it);
|
||||
|
||||
// PSP exclusive functions in PS.ELF
|
||||
void func_892667C(s32 paletteID, u16* palette);
|
||||
void func_892796C(SVECTOR* in, VECTOR* out, s32* arg3);
|
||||
s16 func_90E0E30(PrimitiveType, s32);
|
||||
void func_91040A0(u_long** gfxBank);
|
||||
|
||||
|
||||
@@ -27,6 +27,7 @@ typedef struct {
|
||||
MATRIX* CompMatrix(MATRIX* m0, MATRIX* m1, MATRIX* m2);
|
||||
MATRIX* RotMatrix(SVECTOR* r, MATRIX* m);
|
||||
void SetGeomOffset(long ofx, long ofy);
|
||||
void RotTrans(SVECTOR* v0, VECTOR* v1, long* flag);
|
||||
long RotTransPers(SVECTOR*, long*, long*, long*);
|
||||
void SetGeomScreen(long h);
|
||||
void SetRotMatrix(MATRIX* m);
|
||||
|
||||
+1
-2
@@ -656,7 +656,7 @@ enum Sfx {
|
||||
SFX_UNK_7BE = 0x7BE,
|
||||
SFX_UNK_761 = 0x761,
|
||||
|
||||
#if defined(VERSION_PSP) || defined(VERSION_PC)
|
||||
// Exclusive to Maria PSP
|
||||
/* 0x8E6 */ SFX_VO_MAR_8E6 = 0x8E6,
|
||||
/* 0x8E7 */ SFX_VO_MAR_ATTACK_C,
|
||||
/* 0x8E8 */ SFX_VO_MAR_8E8,
|
||||
@@ -673,7 +673,6 @@ enum Sfx {
|
||||
/* 0x8F3 */ SFX_VO_MAR_PAIN_C,
|
||||
/* 0x8F4 */ SFX_VO_MAR_PAIN_D,
|
||||
/* 0x8F5 */ SFX_VO_MAR_DEATH,
|
||||
#endif
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
# Minimal image to create a PSX disk image with Maria replacing Richter.
|
||||
#
|
||||
# 1. build image
|
||||
# docker build --tag sotn-mariapsp-forpsx:latest .
|
||||
#
|
||||
# 2. create the directory disks/ and place the following two files:
|
||||
# disks/sotn.us.bin
|
||||
# disks/sotn.pspeu.iso
|
||||
#
|
||||
# 3. generate disks/sotn_mariapsp.us.bin
|
||||
# docker run --rm -v ./disks:/disks sotn-mariapsp-forpsx:latest
|
||||
|
||||
FROM xeeynamo/sotn-build:latest
|
||||
|
||||
USER ubuntu
|
||||
WORKDIR /disks
|
||||
RUN git clone https://github.com/xeeynamo/sotn-decomp.git --branch master --depth 1 /sotn/sotn-decomp && \
|
||||
cp -r /sotn/.venv /sotn/sotn-decomp/ && \
|
||||
rm -rf /sotn/sotn-decomp/disks && \
|
||||
ln -s /disks /sotn/sotn-decomp/
|
||||
WORKDIR /sotn/sotn-decomp/mods/mariapsp-forpsx
|
||||
ENTRYPOINT ["make", "disk"]
|
||||
@@ -0,0 +1,68 @@
|
||||
ROOT_DIR = ../..
|
||||
SRC_DIR = $(ROOT_DIR)/src/maria
|
||||
VERSION ?= us
|
||||
BUILD_DIR = build/$(VERSION)
|
||||
TARGET = $(BUILD_DIR)/maria.elf
|
||||
MAP = $(BUILD_DIR)/maria.map
|
||||
OVERLAY = $(BUILD_DIR)/MARIA.BIN
|
||||
|
||||
CC = mipsel-linux-gnu-gcc
|
||||
CFLAGS = -mno-abicalls -G0 -fno-pic -g -O2 -I$(ROOT_DIR)/include -I$(ROOT_DIR)/src/maria -msoft-float -march=3000
|
||||
CFLAGS += -funsigned-char -fpeephole -ffunction-cse -fpcc-struct-return -fcommon -fverbose-asm -fno-stack-protector
|
||||
LDFLAGS = -nostdlib -Wl,-Map=$(MAP) -T symexport.txt -T $(ROOT_DIR)/player.ld -T $(ROOT_DIR)/config/undefined_syms.$(VERSION).txt -T $(BUILD_DIR)/symbols.txt
|
||||
|
||||
HEADER_SRC = maria.c
|
||||
SRCS = $(wildcard $(SRC_DIR)/*.c)
|
||||
OBJS = $(BUILD_DIR)/maria.o $(patsubst $(SRC_DIR)/%.c, $(BUILD_DIR)/%.o, $(SRCS))
|
||||
ASSETS = $(BUILD_DIR)/.assets_done
|
||||
|
||||
|
||||
.PHONY: all build clean release disk
|
||||
|
||||
all: build
|
||||
|
||||
build: $(OVERLAY)
|
||||
|
||||
$(BUILD_DIR):
|
||||
mkdir -p $@
|
||||
$(ASSETS): $(BUILD_DIR)
|
||||
cd $(ROOT_DIR) && make bin/sotn-assets
|
||||
cd $(ROOT_DIR) && bin/sotn-assets extract config/assets.pspeu.yaml
|
||||
cd $(ROOT_DIR) && bin/sotn-assets build config/assets.pspeu.yaml
|
||||
touch $@
|
||||
|
||||
$(BUILD_DIR)/%.o: $(SRC_DIR)/%.c | $(BUILD_DIR) $(ASSETS)
|
||||
$(CC) $(CFLAGS) -c $< -o $@
|
||||
$(BUILD_DIR)/maria.o: maria.c | $(BUILD_DIR) $(ASSETS)
|
||||
$(CC) $(CFLAGS) -c $< -o $@
|
||||
|
||||
$(TARGET): $(OBJS) | $(BUILD_DIR) $(ASSETS)
|
||||
sed 's;//.*;;' $(ROOT_DIR)/config/symbols.$(VERSION).txt > $(BUILD_DIR)/symbols.txt
|
||||
$(CC) $(LDFLAGS) -o $@ $^
|
||||
$(OVERLAY): $(TARGET)
|
||||
mipsel-linux-gnu-objcopy -O binary $< $@
|
||||
|
||||
clean:
|
||||
rm $(OVERLAY) $(MAP) $(BUILD_DIR)/symbols.txt $(ASSETS) $(TARGET) $(OBJS)
|
||||
|
||||
# prepare release by complying with the AGPL-3.0-or-later license
|
||||
RELEASE_DIR = release
|
||||
release:
|
||||
cd $(ROOT_DIR) && VERSION=pspeu make extract_disk
|
||||
make $(OVERLAY)
|
||||
mkdir -p $(RELEASE_DIR)/src
|
||||
cp $(OVERLAY) $(RELEASE_DIR)/
|
||||
cp $(MAP) $(RELEASE_DIR)/
|
||||
cp $(ROOT_DIR)/LICENSE $(RELEASE_DIR)/
|
||||
cp $$(find . -maxdepth 1 -type f) release/src/
|
||||
cp $$(find $(ROOT_DIR)/src/maria -maxdepth 1 -type f) release/src/
|
||||
cp $$(find $(ROOT_DIR)/include -maxdepth 1 -type f) release/src/
|
||||
cd release && sha256sum MARIA.BIN > MARIA.BIN.sha256
|
||||
|
||||
DISK_DIR = $(ROOT_DIR)/disks/mariapsp-forpsx
|
||||
disk: release
|
||||
rm -rf $(DISK_DIR) || true
|
||||
cp -r $(RELEASE_DIR) $(DISK_DIR)
|
||||
cp $(ROOT_DIR)/disks/sotn.$(VERSION).bin $(DISK_DIR)/sotn-patched.$(VERSION).bin
|
||||
cd $(ROOT_DIR) && make bin/sotn-disk
|
||||
$(ROOT_DIR)/bin/sotn-disk inject $(DISK_DIR)/sotn-patched.$(VERSION).bin BIN/RIC.BIN $(OVERLAY)
|
||||
@@ -0,0 +1,213 @@
|
||||
// SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
#include <maria.h>
|
||||
|
||||
// copied from src/dra/5F60C.c
|
||||
typedef struct {
|
||||
s32 x;
|
||||
s32 y;
|
||||
s32 w;
|
||||
s32 h;
|
||||
s32 u;
|
||||
s32 v;
|
||||
s32 tpage;
|
||||
s32 clut;
|
||||
} SubwpnIconParams;
|
||||
static SubwpnIconParams D_psp_09147418[] = {
|
||||
{0x05, 0x09, 0x1F, 0x17, 0xE0, 0xB0, 0x01E, 0x17F}, // CAT
|
||||
{0x0C, 0x07, 0x0F, 0x17, 0xD0, 0xC0, 0x01E, 0x17F}, // CARDINAL
|
||||
{0x05, 0x09, 0x1F, 0x17, 0xE0, 0xC8, 0x01E, 0x17F}, // TURTLE
|
||||
{0x0C, 0x09, 0x10, 0x17, 0x98, 0xD8, 0x01E, 0x17F}, // DOLL (dummy)
|
||||
{0x0C, 0x09, 0x10, 0x17, 0x98, 0xD8, 0x01E, 0x17F}, // DOLL
|
||||
{0x0C, 0x09, 0x10, 0x17, 0x98, 0xD8, 0x01E, 0x17F}, // DOLL (dummy)
|
||||
{0x0C, 0x09, 0x10, 0x17, 0x98, 0xD8, 0x01E, 0x17F}, // DOLL (dummy)
|
||||
{0x04, 0x00, 0x1F, 0x1F, 0xE0, 0xE0, 0x01E, 0x17F}, // DRAGON
|
||||
{0x0C, 0x09, 0x10, 0x17, 0x98, 0xD8, 0x01E, 0x17F}, // DOLL (dummy)
|
||||
};
|
||||
static s32 _hud_search_pattern[] = {
|
||||
0x009, 0x00F, 0x018, 0x010, 0x0A8, 0x0C0, 0x01E, 0x17F};
|
||||
static bool DoesRichterHudMatch(s32* ptr) {
|
||||
return ptr[0] == _hud_search_pattern[0] &&
|
||||
ptr[1] == _hud_search_pattern[1] &&
|
||||
ptr[2] == _hud_search_pattern[2] &&
|
||||
ptr[3] == _hud_search_pattern[3] &&
|
||||
ptr[4] == _hud_search_pattern[4] &&
|
||||
ptr[5] == _hud_search_pattern[5] &&
|
||||
ptr[6] == _hud_search_pattern[6] && ptr[7] == _hud_search_pattern[7];
|
||||
}
|
||||
static SubwpnIconParams* SearchRichterSubwpnIconsDef(s32* ptr) {
|
||||
if (DoesRichterHudMatch(ptr)) {
|
||||
return (SubwpnIconParams*)ptr;
|
||||
}
|
||||
for (ptr = (s32*)0x800A0000; (u32)ptr < 0x8013C000U; ptr++) {
|
||||
if (DoesRichterHudMatch(ptr)) {
|
||||
return (SubwpnIconParams*)ptr;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
static void PatchHudSubweaponUV() {
|
||||
// offset of g_ricSubwpnIcons, from build/us/dra.map
|
||||
s32* DefaultOffset = (s32*)0x800A3014;
|
||||
SubwpnIconParams* params = SearchRichterSubwpnIconsDef(DefaultOffset);
|
||||
if (!params) {
|
||||
// failed to patch, silently fail and continue instead of crashing
|
||||
return;
|
||||
}
|
||||
// Patch Richter subweapon UVs and replace them with those from Maria
|
||||
memcpy(params, D_psp_09147418, sizeof(D_psp_09147418));
|
||||
}
|
||||
|
||||
// On MARIA overlay, PL_W_NONE is used for the Doll Crash. We need to convert it
|
||||
// back and forth to make it work in the overlay and in the HUD managed by DRA.
|
||||
// * Bible -> Doll
|
||||
// * Knife -> Cat
|
||||
// * Vithubi -> Dragon
|
||||
// * Axe -> Bird
|
||||
// * Water -> Turtle
|
||||
static int equipped_subweapon;
|
||||
static inline void ConvertSubweaponRicToMaria() {
|
||||
switch (g_Status.subWeapon) {
|
||||
case SUBWPN_NONE:
|
||||
g_Status.subWeapon = PL_W_NONE;
|
||||
break;
|
||||
case SUBWPN_DAGGER:
|
||||
g_Status.subWeapon = PL_W_CAT;
|
||||
break;
|
||||
case SUBWPN_AXE:
|
||||
g_Status.subWeapon = PL_W_CARDINAL;
|
||||
break;
|
||||
case SUBWPN_HOLYWATER:
|
||||
g_Status.subWeapon = PL_W_TURTLE;
|
||||
break;
|
||||
case SUBWPN_BIBLE:
|
||||
g_Status.subWeapon = PL_W_NONE;
|
||||
break;
|
||||
case SUBWPN_VIBHUTI:
|
||||
g_Status.subWeapon = PL_W_DRAGON;
|
||||
break;
|
||||
default:
|
||||
// Re-use previous subweapon, and increase heart count by 1
|
||||
g_Status.subWeapon = equipped_subweapon;
|
||||
if (g_Status.hearts < g_Status.heartsMax) {
|
||||
g_Status.hearts++;
|
||||
}
|
||||
break;
|
||||
}
|
||||
equipped_subweapon = g_Status.subWeapon;
|
||||
}
|
||||
static inline void ConvertSubweaponMariaToRic() {
|
||||
switch (g_Status.subWeapon) {
|
||||
case PL_W_NONE:
|
||||
g_Status.subWeapon = SUBWPN_BIBLE;
|
||||
break;
|
||||
case PL_W_CAT:
|
||||
g_Status.subWeapon = SUBWPN_DAGGER;
|
||||
break;
|
||||
case PL_W_CARDINAL:
|
||||
g_Status.subWeapon = SUBWPN_AXE;
|
||||
break;
|
||||
case PL_W_TURTLE:
|
||||
g_Status.subWeapon = SUBWPN_HOLYWATER;
|
||||
break;
|
||||
case PL_W_DRAGON:
|
||||
g_Status.subWeapon = SUBWPN_VIBHUTI;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
int func_8919BA8() { return 0; }
|
||||
int func_90E4C58() { return 0; }
|
||||
void func_90E4C18() {}
|
||||
void func_90E4C68() {}
|
||||
void func_9101FC8() {}
|
||||
|
||||
void func_91040A0(u_long** _gfxBank) {
|
||||
GfxBank* gfxBank;
|
||||
GfxLoad* gfxLoad;
|
||||
s32 i;
|
||||
|
||||
gfxBank = (GfxBank*)_gfxBank; // avoid problems with the game.h signature
|
||||
if (gfxBank->kind == GFX_BANK_NONE) {
|
||||
return -1;
|
||||
}
|
||||
if (gfxBank->kind == -1) {
|
||||
return -1;
|
||||
}
|
||||
for (i = 0; i < LEN(g_GfxLoad); i++) {
|
||||
gfxLoad = &g_GfxLoad[i];
|
||||
if (gfxLoad->kind == GFX_BANK_NONE) {
|
||||
gfxLoad->kind = gfxBank->kind;
|
||||
gfxLoad->unk6 = 0;
|
||||
gfxLoad->unk8 = 0;
|
||||
gfxLoad->next = gfxBank->entries;
|
||||
return i;
|
||||
}
|
||||
gfxLoad++;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
void func_892667C(s32 paletteID, u16* paletteData) {
|
||||
int x = (paletteID & 15) * 16;
|
||||
int y = (paletteID >> 4) & 15;
|
||||
RECT rect = {x, 240 + y, 16, 1};
|
||||
memcpy(
|
||||
&g_Clut[(paletteID & 0x300) >> 8][(paletteID & 0xFF) * COLORS_PER_PAL],
|
||||
paletteData, PALETTE_LEN);
|
||||
LoadImage(&rect, (u_long*)paletteData);
|
||||
}
|
||||
|
||||
void func_9142FC8(int sfx) {}
|
||||
|
||||
void func_89285A0(s32 angle, MATRIX* out) {
|
||||
short c = (short)rcos(angle);
|
||||
short s = (short)rsin(angle);
|
||||
out->m[0][0] = c;
|
||||
out->m[0][1] = (s16)-s;
|
||||
out->m[0][2] = 0;
|
||||
out->m[1][0] = s;
|
||||
out->m[1][1] = c;
|
||||
out->m[1][2] = 0;
|
||||
out->m[2][0] = 0;
|
||||
out->m[2][1] = 0;
|
||||
out->m[2][2] = 4096;
|
||||
out->t[2] = 0;
|
||||
out->t[1] = 0;
|
||||
out->t[0] = 0;
|
||||
}
|
||||
|
||||
s16 func_90E0E30(PrimitiveType kind, s32 count) {
|
||||
return g_api.AllocPrimitives(kind, count);
|
||||
}
|
||||
|
||||
void MARIA_Load();
|
||||
void MarInit(s16 initParam);
|
||||
static void MarPsxInit(u16 initParam) {
|
||||
static int isInitialized = 0;
|
||||
if (!isInitialized) {
|
||||
MARIA_Load();
|
||||
PatchHudSubweaponUV();
|
||||
g_api.CalcDealDamageMaria = g_api.func_800FD664;
|
||||
g_api.CalcPlayerDamageMaria = g_api.CalcPlayerDamage;
|
||||
isInitialized = true;
|
||||
}
|
||||
|
||||
// Call the actual init function
|
||||
MarInit(initParam);
|
||||
}
|
||||
|
||||
void MarMain(void);
|
||||
static void MarPsxMain() {
|
||||
ConvertSubweaponRicToMaria();
|
||||
MarMain();
|
||||
ConvertSubweaponMariaToRic();
|
||||
}
|
||||
|
||||
#include "gen/maria.h"
|
||||
void MarUpdatePlayerEntities(void);
|
||||
void MarGetPlayerSensor(Collider* col);
|
||||
// needs to stay at the bottom for the struct to fall to 0x8013C000
|
||||
PlayerOvl MARIA_override = {
|
||||
MarPsxMain, MarPsxInit, MarUpdatePlayerEntities, MarGetPlayerSensor,
|
||||
maria_sprites, maria_sprites, maria_sprites, maria_sprites,
|
||||
};
|
||||
@@ -0,0 +1 @@
|
||||
EXTERN(MARIA_override);
|
||||
@@ -0,0 +1,13 @@
|
||||
SECTIONS {
|
||||
. = 0x8013C000;
|
||||
.data : {
|
||||
*(.data*)
|
||||
}
|
||||
.rodata : {
|
||||
*(.rodata*)
|
||||
}
|
||||
.text : {
|
||||
*(.text*)
|
||||
}
|
||||
end = .;
|
||||
}
|
||||
+1
-1
@@ -723,7 +723,7 @@ void DrawRichterHud(void) {
|
||||
}
|
||||
|
||||
RicSubwpnIconParams g_ricSubwpnIcons[] = {
|
||||
{0x9, 0x00F, 0x018, 0x010, 0x0A8, 0x0C0, 0x01E, 0x17F},
|
||||
{0x009, 0x00F, 0x018, 0x010, 0x0A8, 0x0C0, 0x01E, 0x17F},
|
||||
{0x009, 0x007, 0x018, 0x018, 0x080, 0x0C0, 0x01E, 0x17F},
|
||||
{0x00C, 0x00D, 0x010, 0x010, 0x028, 0x070, 0x01B, 0x102},
|
||||
{0x008, 0x00C, 0x018, 0x018, 0x038, 0x068, 0x01B, 0x102},
|
||||
|
||||
@@ -62,7 +62,9 @@ static u8 wpn_dragon_gfx[] = {
|
||||
#include "gen/wpn_dragon_gfx.h"
|
||||
};
|
||||
|
||||
#if defined(VERSION_PSP) || defined(VERSION_PC)
|
||||
#include "gen/maria.h"
|
||||
#endif
|
||||
|
||||
static u8 items_gfx[] = {
|
||||
#include "gen/items_gfx.h"
|
||||
@@ -101,37 +103,65 @@ static u16 crash_cardinal_pal[] = {
|
||||
// clang-format
|
||||
static u_long* wpn_owl_img[] = {
|
||||
(u_long*)GFX_BANK_COMPRESSED,
|
||||
#ifdef VERSION_PSP
|
||||
GFX_ENTRY(0x000, 0x180, 128, 128, wpn_owl_gfx),
|
||||
#else
|
||||
GFX_ENTRY(0x180, 0x240, 128, 128, wpn_owl_gfx),
|
||||
#endif
|
||||
GFX_TERMINATE(),
|
||||
};
|
||||
static u_long* wpn_turtle_img[] = {
|
||||
(u_long*)GFX_BANK_COMPRESSED,
|
||||
#ifdef VERSION_PSP
|
||||
GFX_ENTRY(0x000, 0x100, 128, 128, wpn_turtle_gfx),
|
||||
#else
|
||||
GFX_ENTRY(0x100, 0x2E0, 128, 128, wpn_turtle_gfx),
|
||||
#endif
|
||||
GFX_TERMINATE(),
|
||||
};
|
||||
static u_long* wpn_cat_img[] = {
|
||||
(u_long*)GFX_BANK_COMPRESSED,
|
||||
#ifdef VERSION_PSP
|
||||
GFX_ENTRY(0x000, 0x140, 128, 128, wpn_cat_gfx),
|
||||
#else
|
||||
GFX_ENTRY(0x180, 0x2C0, 128, 128, wpn_cat_gfx),
|
||||
#endif
|
||||
GFX_TERMINATE(),
|
||||
};
|
||||
static u_long* wpn_cardinal_img[] = {
|
||||
(u_long*)GFX_BANK_COMPRESSED,
|
||||
#ifdef VERSION_PSP
|
||||
GFX_ENTRY(0x000, 0x120, 128, 128, wpn_cardinal_gfx),
|
||||
#else
|
||||
GFX_ENTRY(0x100, 0x260, 128, 128, wpn_cardinal_gfx),
|
||||
#endif
|
||||
GFX_TERMINATE(),
|
||||
};
|
||||
static u_long* wpn_dragon_img[] = {
|
||||
(u_long*)GFX_BANK_COMPRESSED,
|
||||
#ifdef VERSION_PSP
|
||||
GFX_ENTRY(0x000, 0x160, 128, 128, wpn_dragon_gfx),
|
||||
#else
|
||||
GFX_ENTRY(0x100, 0x240, 128, 128, wpn_dragon_gfx),
|
||||
#endif
|
||||
GFX_TERMINATE(),
|
||||
};
|
||||
static u_long* wpn_doll_img[] = {
|
||||
(u_long*)GFX_BANK_COMPRESSED,
|
||||
#ifdef VERSION_PSP
|
||||
GFX_ENTRY(0x000, 0x1A0, 128, 128, wpn_doll_gfx),
|
||||
#else
|
||||
GFX_ENTRY(0x180, 0x260, 128, 128, wpn_doll_gfx),
|
||||
#endif
|
||||
GFX_TERMINATE(),
|
||||
};
|
||||
static u_long* crash_img[] = {
|
||||
(u_long*)GFX_BANK_COMPRESSED,
|
||||
#ifdef VERSION_PSP
|
||||
GFX_ENTRY(0x000, 0x1C0, 128, 128, crash_turtle_gfx),
|
||||
#else
|
||||
GFX_ENTRY(0x100, 0x300, 128, 128, crash_turtle_gfx),
|
||||
#endif
|
||||
GFX_TERMINATE(),
|
||||
};
|
||||
static u_long* hud_img[] = {
|
||||
@@ -199,8 +229,10 @@ void MarLoadGraphics(void) {
|
||||
|
||||
extern PlayerOvl MARIA_player;
|
||||
void MARIA_Load(void) {
|
||||
#if defined(VERSION_PSP) || defined(VERSION_PC)
|
||||
memcpy(&g_PlOvl, &MARIA_player, sizeof(PlayerOvl));
|
||||
memcpy(&g_PlOvlSpritesheet, maria_sprites, sizeof(maria_sprites));
|
||||
#endif
|
||||
func_91040A0(wpn_owl_img);
|
||||
func_91040A0(wpn_turtle_img);
|
||||
func_91040A0(wpn_cat_img);
|
||||
|
||||
+16
-34
@@ -44,11 +44,6 @@ static Entity* MarGetFreeEntityReverse(s16 start, s16 end) {
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
#if defined(VERSION_PSP)
|
||||
extern s32 D_80174F80[11];
|
||||
#else
|
||||
static s32 D_80174F80[11];
|
||||
#endif
|
||||
void func_maria_8015F9F0(Entity* entity) {
|
||||
s32 i;
|
||||
s32 enemyId;
|
||||
@@ -69,21 +64,6 @@ void func_maria_8015F9F0(Entity* entity) {
|
||||
}
|
||||
}
|
||||
|
||||
#if defined(VERSION_PSP)
|
||||
extern u8 D_80174FAC;
|
||||
extern u8 D_80174FB0;
|
||||
extern u8 D_80174FB4;
|
||||
extern u8 D_80174FB8;
|
||||
#else
|
||||
static u8 D_80174FAC;
|
||||
STATIC_PAD_BSS(3);
|
||||
static u8 D_80174FB0;
|
||||
STATIC_PAD_BSS(3);
|
||||
static u8 D_80174FB4;
|
||||
STATIC_PAD_BSS(3);
|
||||
static u8 D_80174FB8;
|
||||
STATIC_PAD_BSS(3);
|
||||
#endif
|
||||
void func_maria_8015FA5C(s32 arg0) {
|
||||
D_80174FAC = D_80154674[arg0][0];
|
||||
D_80174FB0 = D_80154674[arg0][1];
|
||||
@@ -184,8 +164,7 @@ static s32 func_8015FDB0(Primitive* prim, s16 posX, s16 posY) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
void MarEntityDummy(Entity*) {}
|
||||
|
||||
void MarEntityDummy(Entity* self) {}
|
||||
void MarEntityFactory(Entity* self);
|
||||
void MarEntitySmokePuff(Entity* self);
|
||||
void EntityMariaDragonAttack(Entity* self);
|
||||
@@ -1328,7 +1307,11 @@ void EntityMariaOwl(Entity* self) {
|
||||
case 0:
|
||||
self->flags = FLAG_UNK_100000 | FLAG_KEEP_ALIVE_OFFCAMERA |
|
||||
FLAG_POS_CAMERA_LOCKED;
|
||||
#ifdef VERSION_PSP
|
||||
self->unk5A = 0x18;
|
||||
#else
|
||||
self->unk5A = 0x66;
|
||||
#endif
|
||||
self->zPriority = PLAYER.zPriority - 8;
|
||||
self->palette = PAL_WPN_OWL;
|
||||
self->animSet = ANIMSET_WPN_OWL;
|
||||
@@ -1391,7 +1374,7 @@ void EntityMariaOwl(Entity* self) {
|
||||
VECTOR sp5C = {0, 0, 0};
|
||||
VECTOR sp4C;
|
||||
SVECTOR sp44;
|
||||
s32 sp40;
|
||||
long sp40;
|
||||
s32 x;
|
||||
s32 y;
|
||||
s32 var_s0;
|
||||
@@ -1431,7 +1414,7 @@ void EntityMariaOwl(Entity* self) {
|
||||
sp44.vx = self->ext.mariaOwl.velocityX;
|
||||
sp44.vy = 0;
|
||||
sp44.vz = 0;
|
||||
func_892796C(&sp44, &sp4C, &sp40);
|
||||
RotTrans(&sp44, &sp4C, &sp40);
|
||||
self->velocityX = sp4C.vx << 8;
|
||||
self->velocityY = sp4C.vy << 8;
|
||||
self->posX.val += self->velocityX;
|
||||
@@ -1467,11 +1450,6 @@ bool func_maria_80162E9C(Entity* entity) {
|
||||
|
||||
// EntityPlayerBlinkWhite (Richter's version).
|
||||
// Same general logic flow as in DRA but lots of missing cases.
|
||||
#if defined(VERSION_PSP)
|
||||
extern s32 D_80154F7C[];
|
||||
#else
|
||||
static s32 D_80154F7C[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
|
||||
#endif
|
||||
static s16 D_80154FBC[][10] = {
|
||||
{0, 0, 0, 0x15F, 0x0100, 0x0100, 0x0100, 0x0060, 0x31, 0x200},
|
||||
{0, 2, 6, 0x15F, 0x0100, 0x0100, 0x0100, 0x0040, 0x31, 0x200},
|
||||
@@ -2449,11 +2427,6 @@ static void func_80166044() {
|
||||
// Entity ID 66. Made by blueprint 77 (the very last one).
|
||||
// Created in 3 spots in 2 functions (total of 6 calls).
|
||||
// DRA version is very similar.
|
||||
#if defined(VERSION_PSP)
|
||||
extern Point16 D_80175000[32];
|
||||
#else
|
||||
static Point16 D_80175000[32];
|
||||
#endif
|
||||
void MarEntityTeleport(Entity* self) {
|
||||
Primitive* prim;
|
||||
s32 w;
|
||||
@@ -2999,7 +2972,12 @@ void EntityMariaCrashSummon(Entity* self) {
|
||||
return;
|
||||
}
|
||||
self->flags = FLAG_HAS_PRIMS | FLAG_KEEP_ALIVE_OFFCAMERA;
|
||||
#ifdef VERSION_PSP
|
||||
self->unk5A = 0x1C;
|
||||
#else
|
||||
self->unk5A = 0x70;
|
||||
#endif
|
||||
|
||||
self->zPriority = 0x1C0;
|
||||
LoadCrashSummonResouces(self->ext.mariaCrashSummon.crashId);
|
||||
MarSetInvincibilityFrames(0, 999);
|
||||
@@ -3010,7 +2988,11 @@ void EntityMariaCrashSummon(Entity* self) {
|
||||
self->ext.mariaCrashSummon.timer -= 0x10;
|
||||
if (self->ext.mariaCrashSummon.timer > 0) {
|
||||
prim = &g_PrimBuf[self->primIndex];
|
||||
#ifdef VERSION_PSP
|
||||
prim->tpage = 7;
|
||||
#else
|
||||
prim->tpage = 0x1C;
|
||||
#endif
|
||||
prim->clut = 0x11E;
|
||||
prim->priority = 0x1C0;
|
||||
x = (self->ext.mariaCrashSummon.timer * 128) / 256;
|
||||
|
||||
@@ -679,10 +679,7 @@ void MarMain(void) {
|
||||
break;
|
||||
}
|
||||
g_Player.unk08 = g_Player.status;
|
||||
#if defined(VERSION_PC) || defined(VERSION_PSP)
|
||||
// uninitialized on PSX, it was a coincidence it worked
|
||||
newStatus = 0;
|
||||
#endif
|
||||
switch (PLAYER.step) {
|
||||
case PL_S_STAND:
|
||||
newStatus = NO_AFTERIMAGE;
|
||||
|
||||
@@ -392,11 +392,9 @@ static void func_80159C04(void) {
|
||||
s16 var_s1;
|
||||
|
||||
entity = PLAYER.unkB8;
|
||||
#if defined(VERSION_PSP)
|
||||
if (!entity) {
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
if (entity->facingLeft) {
|
||||
var_s3 = -entity->hitboxOffX;
|
||||
} else {
|
||||
|
||||
+98
-13
@@ -1,6 +1,13 @@
|
||||
// SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
#include "maria.h"
|
||||
|
||||
// ** How to calculate self->unk5A
|
||||
// GFX_ENTRY(Y, X, ...
|
||||
// where Y and X are variables for the python script below
|
||||
// def calc_tpage(y, x):
|
||||
// return (y // 0x100) * 0x40 + ((y & 0x80) // 0x40) + \
|
||||
// ((x & 0x20) // 0x20) + ((x // 0x20) * 2)
|
||||
|
||||
static s32 D_pspeu_092E5F18;
|
||||
static s32 D_pspeu_092E5D18[0x80];
|
||||
static Point16 D_pspeu_092E5B18[0x80];
|
||||
@@ -20,7 +27,11 @@ void EntityMariaDollAttack(Entity* self) {
|
||||
case 0:
|
||||
self->flags = FLAG_POS_CAMERA_LOCKED | FLAG_KEEP_ALIVE_OFFCAMERA |
|
||||
FLAG_UNK_100000;
|
||||
#ifdef VERSION_PSP
|
||||
self->unk5A = 0x19;
|
||||
#else
|
||||
self->unk5A = 0x67;
|
||||
#endif
|
||||
self->zPriority = PLAYER.zPriority - 8;
|
||||
self->palette = PAL_WPN_DOLL;
|
||||
self->animSet = ANIMSET_WPN_DOLL;
|
||||
@@ -110,7 +121,11 @@ void EntityMariaDollActivate(Entity* self) {
|
||||
case 0:
|
||||
self->flags = FLAG_POS_CAMERA_LOCKED | FLAG_KEEP_ALIVE_OFFCAMERA |
|
||||
FLAG_UNK_100000;
|
||||
#ifdef VERSION_PSP
|
||||
self->unk5A = 0x19;
|
||||
#else
|
||||
self->unk5A = 0x67;
|
||||
#endif
|
||||
self->zPriority = PLAYER.zPriority - 8;
|
||||
self->palette = PAL_WPN_DOLL;
|
||||
self->animSet = ANIMSET_WPN_DOLL;
|
||||
@@ -263,7 +278,11 @@ void EntityMariaCatAttack(Entity* self) {
|
||||
case 0:
|
||||
self->flags = FLAG_POS_CAMERA_LOCKED | FLAG_KEEP_ALIVE_OFFCAMERA |
|
||||
FLAG_UNK_100000;
|
||||
#ifdef VERSION_PSP
|
||||
self->unk5A = 0x14;
|
||||
#else
|
||||
self->unk5A = 0x6E;
|
||||
#endif
|
||||
self->zPriority = PLAYER.zPriority - 8;
|
||||
self->palette = PAL_WPN_CAT;
|
||||
self->animSet = ANIMSET_WPN_CAT;
|
||||
@@ -396,7 +415,11 @@ void EntityMariaCatCrash(Entity* self) {
|
||||
case 0:
|
||||
self->flags = FLAG_POS_CAMERA_LOCKED | FLAG_KEEP_ALIVE_OFFCAMERA |
|
||||
FLAG_UNK_100000;
|
||||
#ifdef VERSION_PSP
|
||||
self->unk5A = 0x14;
|
||||
#else
|
||||
self->unk5A = 0x6E;
|
||||
#endif
|
||||
self->zPriority = PLAYER.zPriority - 8;
|
||||
self->palette = PAL_WPN_CAT_CRASH;
|
||||
self->animSet = ANIMSET_WPN_CAT;
|
||||
@@ -475,7 +498,11 @@ void EntityMariaCatCrashAttack(Entity* self) {
|
||||
case 0:
|
||||
self->flags = FLAG_UNK_100000 | FLAG_KEEP_ALIVE_OFFCAMERA |
|
||||
FLAG_POS_CAMERA_LOCKED;
|
||||
#ifdef VERSION_PSP
|
||||
self->unk5A = 0x14;
|
||||
#else
|
||||
self->unk5A = 0x6E;
|
||||
#endif
|
||||
self->zPriority = PLAYER.zPriority - 8;
|
||||
self->palette = PAL_WPN_CAT;
|
||||
self->animSet = ANIMSET_WPN_CAT;
|
||||
@@ -518,7 +545,11 @@ void EntityMariaDragonAttack(Entity* self) {
|
||||
switch (self->step) {
|
||||
case 0:
|
||||
self->flags = FLAG_UNK_100000 | FLAG_KEEP_ALIVE_OFFCAMERA;
|
||||
#ifdef VERSION_PSP
|
||||
self->unk5A = 0x15;
|
||||
#else
|
||||
self->unk5A = 0x64;
|
||||
#endif
|
||||
self->zPriority = PLAYER.zPriority - 8;
|
||||
self->palette = PAL_WPN_DRAGON;
|
||||
self->animSet = ANIMSET_WPN_DRAGON;
|
||||
@@ -672,7 +703,11 @@ void EntityMariaDragonCrash(Entity* self) {
|
||||
case 0:
|
||||
self->flags = FLAG_POS_CAMERA_LOCKED | FLAG_KEEP_ALIVE_OFFCAMERA |
|
||||
FLAG_UNK_100000;
|
||||
#ifdef VERSION_PSP
|
||||
self->unk5A = 0x15;
|
||||
#else
|
||||
self->unk5A = 0x64;
|
||||
#endif
|
||||
self->zPriority = 0x1C0;
|
||||
self->palette = PAL_WPN_DRAGON_CRASH;
|
||||
self->animSet = ANIMSET_WPN_DRAGON;
|
||||
@@ -718,7 +753,7 @@ void EntityMariaDragonCrash(Entity* self) {
|
||||
VECTOR vTranslate = {0, 0, 0};
|
||||
VECTOR vVelocity;
|
||||
SVECTOR svVelocity;
|
||||
s32 out;
|
||||
long out;
|
||||
if (!IsTargetValid(self->ext.mariaDragon.target)) {
|
||||
self->ext.mariaDragon.target = FindTarget(self);
|
||||
}
|
||||
@@ -751,7 +786,7 @@ void EntityMariaDragonCrash(Entity* self) {
|
||||
svVelocity.vx = self->ext.mariaDragon.velocity;
|
||||
svVelocity.vy = 0;
|
||||
svVelocity.vz = 0;
|
||||
func_892796C(&svVelocity, &vVelocity, &out);
|
||||
RotTrans(&svVelocity, &vVelocity, &out);
|
||||
self->velocityX = vVelocity.vx << 8;
|
||||
self->velocityY = vVelocity.vy << 8;
|
||||
FntPrint("%08x : %08x\n", self->velocityX, self->velocityY);
|
||||
@@ -822,7 +857,11 @@ void EntityMariaDragonCrashBodyPart(Entity* self) {
|
||||
return;
|
||||
}
|
||||
self->flags = FLAG_KEEP_ALIVE_OFFCAMERA | FLAG_HAS_PRIMS;
|
||||
#ifdef VERSION_PSP
|
||||
self->unk5A = 0x15;
|
||||
#else
|
||||
self->unk5A = 0x64;
|
||||
#endif
|
||||
self->zPriority = 0x1C0;
|
||||
self->palette = PAL_WPN_DRAGON_CRASH;
|
||||
self->animSet = ANIMSET_WPN_DRAGON;
|
||||
@@ -851,7 +890,11 @@ void EntityMariaDragonCrashBodyPart(Entity* self) {
|
||||
prim = &g_PrimBuf[self->primIndex];
|
||||
var_s2 = D_pspeu_092E5900 - 1;
|
||||
for (i = 0; i < LEN(D_pspeu_092E5B08); i++) {
|
||||
#ifdef VERSION_PSP
|
||||
prim->tpage = 5;
|
||||
#else
|
||||
prim->tpage = 0x19;
|
||||
#endif
|
||||
prim->clut = self->palette & 0x3FF;
|
||||
prim->priority = self->zPriority;
|
||||
if (var_s2 >= 7) {
|
||||
@@ -876,12 +919,18 @@ void EntityMariaDragonCrashBodyPart(Entity* self) {
|
||||
SVECTOR sp50;
|
||||
SVECTOR svPosStart;
|
||||
SVECTOR sp40;
|
||||
s32 out;
|
||||
long out;
|
||||
s32 angle;
|
||||
angle =
|
||||
NormalizeAngle(ratan2(spD0 - var_s5, spD4 - var_s6));
|
||||
func_89285A0(angle, &m);
|
||||
SetGeomOffset(var_s6, var_s5);
|
||||
#ifndef VERSION_PSP
|
||||
// I do not know why this works. The stack might be messed
|
||||
// up and function just so happen to match PSP decomp.
|
||||
spA0.vx = var_s6;
|
||||
spA0.vy = var_s5;
|
||||
#endif
|
||||
TransMatrix(&m, &spA0);
|
||||
SetRotMatrix(&m);
|
||||
SetTransMatrix(&m);
|
||||
@@ -898,10 +947,10 @@ void EntityMariaDragonCrashBodyPart(Entity* self) {
|
||||
svPosEnd.vx = 16;
|
||||
svPosEnd.vy = 16;
|
||||
svPosEnd.vz = 0;
|
||||
func_892796C(&sp40, &sp60, &out);
|
||||
func_892796C(&svPosStart, &vPosStart, &out);
|
||||
func_892796C(&sp50, &sp80, &out);
|
||||
func_892796C(&svPosEnd, &vPosEnd, &out);
|
||||
RotTrans(&sp40, &sp60, &out);
|
||||
RotTrans(&svPosStart, &vPosStart, &out);
|
||||
RotTrans(&sp50, &sp80, &out);
|
||||
RotTrans(&svPosEnd, &vPosEnd, &out);
|
||||
prim->x0 = sp60.vx;
|
||||
prim->y0 = sp60.vy;
|
||||
prim->x1 = vPosStart.vx;
|
||||
@@ -917,8 +966,8 @@ void EntityMariaDragonCrashBodyPart(Entity* self) {
|
||||
svPosEnd.vx = 16;
|
||||
svPosEnd.vy = 16;
|
||||
svPosEnd.vz = 0;
|
||||
func_892796C(&svPosStart, &vPosStart, &out);
|
||||
func_892796C(&svPosEnd, &vPosEnd, &out);
|
||||
RotTrans(&svPosStart, &vPosStart, &out);
|
||||
RotTrans(&svPosEnd, &vPosEnd, &out);
|
||||
prim->x0 = xStart;
|
||||
prim->y0 = yStart;
|
||||
prim->x1 = vPosStart.vx;
|
||||
@@ -944,6 +993,10 @@ void EntityMariaDragonCrashBodyPart(Entity* self) {
|
||||
u = 0x80;
|
||||
v = 0;
|
||||
}
|
||||
#ifndef VERSION_PSP
|
||||
// Dragon GFXs are re-located in the VRAM on other platforms
|
||||
u -= 0x80;
|
||||
#endif
|
||||
prim->u1 = u;
|
||||
prim->v1 = v + 0x1F;
|
||||
prim->u0 = u + 0x1F;
|
||||
@@ -997,7 +1050,11 @@ void EntityMariaCardinalAttack(Entity* self) {
|
||||
case 0:
|
||||
self->flags = FLAG_UNK_100000 | FLAG_KEEP_ALIVE_OFFCAMERA |
|
||||
FLAG_POS_CAMERA_LOCKED;
|
||||
#ifdef VERSION_PSP
|
||||
self->unk5A = 0x11;
|
||||
#else
|
||||
self->unk5A = 0x65;
|
||||
#endif
|
||||
self->drawMode = DRAW_TPAGE | DRAW_TPAGE2;
|
||||
self->zPriority = PLAYER.zPriority - 8;
|
||||
self->palette = PAL_WPN_CARDINAL;
|
||||
@@ -1062,7 +1119,11 @@ void EntityMariaCardinalCrash(Entity* self) {
|
||||
case 0:
|
||||
self->flags = FLAG_POS_CAMERA_LOCKED | FLAG_KEEP_ALIVE_OFFCAMERA |
|
||||
FLAG_UNK_100000;
|
||||
#ifdef VERSION_PSP
|
||||
self->unk5A = 0x11;
|
||||
#else
|
||||
self->unk5A = 0x65;
|
||||
#endif
|
||||
self->zPriority = 0x1C0;
|
||||
self->palette = PAL_WPN_CARDINAL;
|
||||
self->animSet = ANIMSET_WPN_CARDINAL;
|
||||
@@ -1292,7 +1353,11 @@ void EntityMariaCardinalCrashFireball(Entity* self) {
|
||||
}
|
||||
self->flags =
|
||||
FLAG_HAS_PRIMS | FLAG_KEEP_ALIVE_OFFCAMERA | FLAG_POS_CAMERA_LOCKED;
|
||||
#ifdef VERSION_PSP
|
||||
self->unk5A = 0x11;
|
||||
#else
|
||||
self->unk5A = 0x65;
|
||||
#endif
|
||||
self->zPriority = 0x1C0;
|
||||
self->palette = PAL_WPN_CARDINAL;
|
||||
self->animSet = ANIMSET_WPN_CARDINAL;
|
||||
@@ -1334,7 +1399,7 @@ void EntityMariaCardinalCrashFireball(Entity* self) {
|
||||
VECTOR vTransform = {0, 0, 0};
|
||||
VECTOR vVelocity;
|
||||
SVECTOR svVelocity;
|
||||
s32 sp48;
|
||||
long sp48;
|
||||
if (self->ext.mariaCardinalCrash.timer < 35) {
|
||||
if (IsTargetValid2(self->ext.mariaCardinalCrash.target) == 0) {
|
||||
self->ext.mariaCardinalCrash.target = FindTarget2(self);
|
||||
@@ -1382,7 +1447,7 @@ void EntityMariaCardinalCrashFireball(Entity* self) {
|
||||
svVelocity.vx = self->ext.mariaCardinalCrash.velocityX;
|
||||
svVelocity.vy = 0;
|
||||
svVelocity.vz = 0;
|
||||
func_892796C(&svVelocity, &vVelocity, &sp48);
|
||||
RotTrans(&svVelocity, &vVelocity, &sp48);
|
||||
self->velocityX = vVelocity.vx << 8;
|
||||
self->velocityY = vVelocity.vy << 8;
|
||||
self->posX.val += self->velocityX;
|
||||
@@ -1396,7 +1461,7 @@ void EntityMariaCardinalCrashFireball(Entity* self) {
|
||||
prim = &g_PrimBuf[self->primIndex];
|
||||
for (i = 0; i < 4; i++) {
|
||||
color = -0x80;
|
||||
prim->clut = 0x11A;
|
||||
prim->clut = PAL_WPN_CARDINAL & 0x3FF;
|
||||
prim->priority = self->zPriority;
|
||||
prim->drawMode = DRAW_DEFAULT;
|
||||
switch (i) {
|
||||
@@ -1434,7 +1499,11 @@ void EntityMariaCardinalCrashFireball(Entity* self) {
|
||||
prim->v2 = 0x7F;
|
||||
prim->u3 = 0xCF;
|
||||
prim->v3 = 0x7F;
|
||||
prim->tpage = 4;
|
||||
#ifdef VERSION_PSP
|
||||
prim->tpage = 0x4;
|
||||
#else
|
||||
prim->tpage = 0x19;
|
||||
#endif
|
||||
} else {
|
||||
prim->u0 = 0x98;
|
||||
prim->v0 = 0x38;
|
||||
@@ -1444,7 +1513,11 @@ void EntityMariaCardinalCrashFireball(Entity* self) {
|
||||
prim->v2 = 0x47;
|
||||
prim->u3 = 0xA7;
|
||||
prim->v3 = 0x47;
|
||||
#ifdef VERSION_PSP
|
||||
prim->tpage = 0x24;
|
||||
#else
|
||||
prim->tpage = 0x39;
|
||||
#endif
|
||||
prim->drawMode |= FLAG_DRAW_SCALEX | FLAG_DRAW_ROTATE;
|
||||
if (i == 1) {
|
||||
color = -0x80;
|
||||
@@ -1477,7 +1550,11 @@ void EntityMariaTurtleAttack(Entity* self) {
|
||||
case 0:
|
||||
self->flags = FLAG_POS_CAMERA_LOCKED | FLAG_KEEP_ALIVE_OFFCAMERA |
|
||||
FLAG_UNK_100000;
|
||||
#ifdef VERSION_PSP
|
||||
self->unk5A = 0x10;
|
||||
#else
|
||||
self->unk5A = 0x6D;
|
||||
#endif
|
||||
self->zPriority = PLAYER.zPriority - 8;
|
||||
self->palette = PAL_WPN_TURTLE;
|
||||
self->animSet = ANIMSET_WPN_TURTLE;
|
||||
@@ -1529,7 +1606,11 @@ void EntityMariaTurtleCrash(Entity* self) {
|
||||
case 0:
|
||||
self->flags = FLAG_UNK_100000 | FLAG_KEEP_ALIVE_OFFCAMERA |
|
||||
FLAG_POS_CAMERA_LOCKED;
|
||||
#ifdef VERSION_PSP
|
||||
self->unk5A = 0x10;
|
||||
#else
|
||||
self->unk5A = 0x6D;
|
||||
#endif
|
||||
self->zPriority = PLAYER.zPriority - 6;
|
||||
self->palette = PAL_WPN_TURTLE;
|
||||
self->animSet = ANIMSET_WPN_TURTLE;
|
||||
@@ -1654,7 +1735,11 @@ void EntityMariaTurtleCrashVortex(Entity* self) {
|
||||
case 0:
|
||||
self->flags = FLAG_UNK_100000 | FLAG_KEEP_ALIVE_OFFCAMERA |
|
||||
FLAG_POS_CAMERA_LOCKED;
|
||||
#ifdef VERSION_PSP
|
||||
self->unk5A = 0x10;
|
||||
#else
|
||||
self->unk5A = 0x6D;
|
||||
#endif
|
||||
self->zPriority = PLAYER.zPriority - 7;
|
||||
self->palette = PAL_WPN_TURTLE;
|
||||
self->animSet = ANIMSET_WPN_TURTLE;
|
||||
|
||||
@@ -36,9 +36,6 @@ void func_maria_8015C4AC(void) {
|
||||
g_Entities[1].pose--;
|
||||
return;
|
||||
}
|
||||
#if !defined(VERSION_PSP)
|
||||
prim = &g_PrimBuf[g_Entities[1].primIndex];
|
||||
#endif
|
||||
for (prim = &g_PrimBuf[g_Entities[1].primIndex], i = 0; prim != NULL; i++,
|
||||
prim = prim->next) {
|
||||
if (i == g_Entities[1].entityId) {
|
||||
|
||||
+2
-9
@@ -4,10 +4,10 @@
|
||||
|
||||
// To use Maria as a playable character, add -DUSE_PLAYER_MARIA=1 in cmake
|
||||
|
||||
void MAR_Load();
|
||||
void MARIA_Load();
|
||||
void InitPlayerMaria(void) {
|
||||
g_CurrentEntity = &PLAYER;
|
||||
MAR_Load();
|
||||
MARIA_Load();
|
||||
}
|
||||
|
||||
int func_8919BA8() { return 0; }
|
||||
@@ -60,13 +60,6 @@ void func_89285A0(s32 angle, MATRIX* out) {
|
||||
out->t[0] = 0;
|
||||
}
|
||||
|
||||
void func_892796C(SVECTOR* in, VECTOR* out, s32* unk) {
|
||||
out->vx = in->vx;
|
||||
out->vy = in->vy;
|
||||
out->vz = in->vz;
|
||||
*unk = 0;
|
||||
}
|
||||
|
||||
s16 func_90E0E30(PrimitiveType kind, s32 count) {
|
||||
return g_api.AllocPrimitives(kind, count);
|
||||
}
|
||||
+30
-10
@@ -3,7 +3,6 @@ package main
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path"
|
||||
"strings"
|
||||
@@ -18,13 +17,13 @@ type cueTrack struct {
|
||||
mode string
|
||||
}
|
||||
|
||||
func performCueAction(cuePath string, action imageAction) error {
|
||||
func openImageFromCueFile(cuePath string) (*iso9660.Image, error) {
|
||||
tracks, err := parseTracks(cuePath)
|
||||
if err != nil {
|
||||
return err
|
||||
return nil, err
|
||||
}
|
||||
if len(tracks) == 0 {
|
||||
return errors.New("CUE do not provide any track")
|
||||
return nil, errors.New("CUE do not provide any track")
|
||||
}
|
||||
|
||||
mainTrack := getFirstTrack(tracks)
|
||||
@@ -35,7 +34,7 @@ func performCueAction(cuePath string, action imageAction) error {
|
||||
case "MODE2/2352":
|
||||
mode = iso9660.TrackMode2_2352
|
||||
default:
|
||||
return fmt.Errorf("unrecognized TRACK mode '%s'", mainTrack.mode)
|
||||
return nil, fmt.Errorf("unrecognized TRACK mode '%s'", mainTrack.mode)
|
||||
}
|
||||
|
||||
baseDir, _ := path.Split(cuePath)
|
||||
@@ -43,15 +42,36 @@ func performCueAction(cuePath string, action imageAction) error {
|
||||
|
||||
f, err := os.OpenFile(binPath, os.O_RDWR, 0)
|
||||
if err != nil {
|
||||
return err
|
||||
return nil, err
|
||||
}
|
||||
defer f.Close()
|
||||
return iso9660.OpenImage(f, mode)
|
||||
}
|
||||
|
||||
image, err := iso9660.OpenImage(f, mode)
|
||||
func performCueAction(path string, action imageAction) error {
|
||||
stat, err := os.Stat(path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var image *iso9660.Image
|
||||
if stat.Size() < 65536 {
|
||||
img, err := openImageFromCueFile(path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
image = img
|
||||
} else {
|
||||
// most likely a bin file
|
||||
f, err := os.OpenFile(path, os.O_RDWR, 0)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
img, err := iso9660.OpenImage(f, iso9660.TrackMode2_2352)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
image = img
|
||||
}
|
||||
defer image.Close()
|
||||
return action(image.RootDir())
|
||||
}
|
||||
|
||||
@@ -68,7 +88,7 @@ func getFirstTrack(tracks []cueTrack) cueTrack {
|
||||
}
|
||||
|
||||
func parseTracks(cuePath string) ([]cueTrack, error) {
|
||||
content, err := ioutil.ReadFile(cuePath)
|
||||
content, err := os.ReadFile(cuePath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -13,6 +13,7 @@ type TrackMode int
|
||||
type ReaderWriterAt interface {
|
||||
io.ReaderAt
|
||||
io.WriterAt
|
||||
io.Closer
|
||||
}
|
||||
|
||||
const (
|
||||
@@ -25,9 +26,9 @@ var (
|
||||
)
|
||||
|
||||
type Image struct {
|
||||
reader ReaderWriterAt
|
||||
mode TrackMode
|
||||
Pvd PrimaryVolumeDescriptor
|
||||
file ReaderWriterAt
|
||||
mode TrackMode
|
||||
Pvd PrimaryVolumeDescriptor
|
||||
}
|
||||
|
||||
type File struct {
|
||||
@@ -36,23 +37,27 @@ type File struct {
|
||||
mode TrackMode
|
||||
}
|
||||
|
||||
func OpenImage(rw ReaderWriterAt, mode TrackMode) (*Image, error) {
|
||||
pvdSec, err := readSector(rw, 16, mode, false)
|
||||
func OpenImage(file ReaderWriterAt, mode TrackMode) (*Image, error) {
|
||||
pvdSec, err := readSector(file, 16, mode, false)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &Image{
|
||||
reader: rw,
|
||||
mode: mode,
|
||||
Pvd: parsePVD(pvdSec), // TODO assume first volume is always primary
|
||||
file: file,
|
||||
mode: mode,
|
||||
Pvd: parsePVD(pvdSec), // TODO assume first volume is always primary
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (img *Image) Close() error {
|
||||
return img.file.Close()
|
||||
}
|
||||
|
||||
func (img *Image) RootDir() File {
|
||||
return File{
|
||||
DirectoryEntry: img.Pvd.DirectoryRecord,
|
||||
handler: img.reader,
|
||||
handler: img.file,
|
||||
mode: img.mode,
|
||||
}
|
||||
}
|
||||
@@ -128,6 +133,9 @@ func (file File) WriteFromData(b []byte) error {
|
||||
if uint32(len(b)) > file.DataLength.LSB {
|
||||
return fmt.Errorf("data too long (%d > %d)", len(b), file.DataLength.LSB)
|
||||
}
|
||||
newB := make([]byte, file.DataLength.LSB)
|
||||
copy(newB, b)
|
||||
b = newB
|
||||
for loc := location(file.ExtentLocation.LSB); len(b) > 0; loc++ {
|
||||
chunk := b
|
||||
if len(b) > sectorSize {
|
||||
|
||||
Reference in new issue
Block a user