mirror of
https://github.com/snesrev/zelda3.git
synced 2025-12-19 18:05:55 -05:00
Migrate to C99 instead of C
Co-authored-by: Rémy F <yne@users.noreply.github.com>
This commit is contained in:
9
.gitignore
vendored
9
.gitignore
vendored
@@ -1,5 +1,6 @@
|
||||
/.vs/
|
||||
/packages/
|
||||
/.vscode/
|
||||
.DS_Store
|
||||
*.dSYM
|
||||
/Debug
|
||||
@@ -12,7 +13,11 @@
|
||||
/tables/zelda3.sfc
|
||||
/tables/zelda3.smc
|
||||
/saves/*.sav
|
||||
/saves/sram.dat
|
||||
/saves/sram.bak
|
||||
/zelda3
|
||||
/.vs/
|
||||
__pycache__
|
||||
*.o
|
||||
/*.o
|
||||
/*.exe
|
||||
/*.out
|
||||
/snes/*.o
|
||||
|
||||
20
Makefile
20
Makefile
@@ -1,22 +1,22 @@
|
||||
# adapted from original README.md:
|
||||
# `clang++ -I/usr/include/SDL2 -lSDL2 -O2 -ozelda3 *.cpp snes/*.cpp`
|
||||
# `clang -I/usr/include/SDL2 -lSDL2 -O2 -ozelda3 *.c snes/*.c`
|
||||
|
||||
ifneq (,$(shell command -v clang++))
|
||||
CXX = clang++
|
||||
else ifneq (,$(shell command -v g++))
|
||||
CXX = g++
|
||||
ifneq (,$(shell which clang))
|
||||
CC = clang
|
||||
else ifneq (,$(shell which gcc))
|
||||
CC = gcc
|
||||
endif
|
||||
|
||||
ifneq (,$(findstring clang,$(CXX)))
|
||||
ifneq (,$(findstring clang,$(CC)))
|
||||
LTO = -flto=thin
|
||||
else ifneq (,$(findstring g++,$(CXX)))
|
||||
else ifneq (,$(findstring gcc,$(CC)))
|
||||
LTO = -flto=auto
|
||||
endif
|
||||
|
||||
override CXXFLAGS := -O2 -I/usr/include/SDL2 $(LTO) $(CXXFLAGS)
|
||||
override CFLAGS := -O2 -I/usr/include/SDL2 $(LTO) $(CXXFLAGS)
|
||||
override LDFLAGS := -lSDL2 $(LDFLAGS)
|
||||
|
||||
override OBJS = $(patsubst %.cpp,%.o,$(wildcard *.cpp snes/*.cpp))
|
||||
override OBJS = $(patsubst %.c,%.o,$(wildcard *.c snes/*.c))
|
||||
override BIN = zelda3
|
||||
|
||||
.PHONY: all clean
|
||||
@@ -27,4 +27,4 @@ clean:
|
||||
$(RM) $(BIN) $(OBJS)
|
||||
|
||||
$(BIN): $(OBJS)
|
||||
$(CXX) $(CXXFLAGS) -o $(BIN) $(OBJS) $(LDFLAGS)
|
||||
$(CC) $(CFLAGS) -o $(BIN) $(OBJS) $(LDFLAGS)
|
||||
|
||||
27
README.md
27
README.md
@@ -5,28 +5,32 @@ A reimplementation of Zelda 3.
|
||||
|
||||
This is a reverse engineered clone of Zelda 3 - A Link to the Past.
|
||||
|
||||
It's around 70-80kLOC of C/C++ code, and reimplements all parts of the original game. The game is playable from start to end.
|
||||
It's around 70-80kLOC of C code, and reimplements all parts of the original game. The game is playable from start to end.
|
||||
|
||||
You need a copy of the ROM to extract game resources (levels, images). Then once that's done, the ROM is no longer needed.
|
||||
|
||||
It uses the PPU and DSP implementation from LakeSnes. Additionally, it can be configured to also run the original machine code side by side. Then the RAM state is compared after each frame, to verify that the C++ implementation is correct.
|
||||
It uses the PPU and DSP implementation from [LakeSnes](https://github.com/elzo-d/LakeSnes).
|
||||
Additionally, it can be configured to also run the original machine code side by side. Then the RAM state is compared after each frame, to verify that the C implementation is correct.
|
||||
|
||||
I got much assistance from spannierism's Zelda 3 JP disassembly and the other ones that documented loads of function names and variables.
|
||||
|
||||
## Compiling
|
||||
## Dependencies
|
||||
|
||||
Put the ROM in tables/zelda3.sfc. The ROM needs to be the US ROM with SHA256 hash `66871d66be19ad2c34c927d6b14cd8eb6fc3181965b6e517cb361f7316009cfb`.
|
||||
- the `libsdl2-dev` library (ubuntu: `apt install libsdl2-dev`, macOS: `brew install sdl2`). On Windows, it's installed automatically with NuGet.
|
||||
- a `tables/zelda3.sfc` US ROM file (for asset extraction step only) with SHA256 hash `66871d66be19ad2c34c927d6b14cd8eb6fc3181965b6e517cb361f7316009cfb`.
|
||||
- The `pillow` and `pyyaml` python dependencies used by the assets extractor. `pip install pillow pyyaml`
|
||||
|
||||
## Compiling
|
||||
|
||||
`cd tables`
|
||||
|
||||
Install python dependencies: `pip install pillow` and `pip install pyyaml`
|
||||
Run `python3 extract_resources.py` to extract resources from the ROM into a more human readable format.
|
||||
|
||||
Run `python extract_resources.py` to extract resources from the ROM into a more human readable format.
|
||||
Run `python3 compile_resources.py` to produce .h files that get included by the C code.
|
||||
|
||||
Run `python compile_resources.py` to produce .h files that gets included by the C++ code.
|
||||
|
||||
### Windows
|
||||
Build the .sln file with Visual Studio
|
||||
First extract and compile resources, see above. Then build the .sln file with Visual Studio.
|
||||
|
||||
### Linux/macOS
|
||||
#### Dependencies
|
||||
@@ -35,20 +39,19 @@ Linux: `apt install libsdl2-dev`
|
||||
macOS: `brew install sdl2`
|
||||
|
||||
#### Building
|
||||
Make sure you are in the root directory.
|
||||
First extract and compile resources, see above. Then make sure you are in the root directory.
|
||||
|
||||
```
|
||||
clang++ `sdl2-config --cflags` -O2 -ozelda3 *.cpp snes/*.cpp `sdl2-config --libs`
|
||||
clang++ `sdl2-config --cflags` -O2 -ozelda3 *.c snes/*.c `sdl2-config --libs`
|
||||
```
|
||||
or
|
||||
`make -j$(nproc)`
|
||||
|
||||
|
||||
## Usage and controls
|
||||
|
||||
The game supports snapshots. The joypad input history is also saved in the snapshot. It's thus possible to replay a playthrough in turbo mode to verify that the game behaves correctly.
|
||||
|
||||
The game is run with `./zelda3` and takes an optional path to the ROM-file, which will verify for each frame that the C++ code matches the original behavior.
|
||||
The game is run with `./zelda3` and takes an optional path to the ROM-file, which will verify for each frame that the C code matches the original behavior.
|
||||
|
||||
| Button | Key |
|
||||
| ------ | ----------- |
|
||||
|
||||
@@ -102,10 +102,10 @@ static const uint8 kBombosBlasts_Tab[72] = {
|
||||
};
|
||||
static const uint8 kQuake_Tab1[5] = {0x17, 0x16, 0x17, 0x16, 0x10};
|
||||
static const uint8 kQuakeDrawGroundBolts_Char[15] = { 0x40, 0x42, 0x44, 0x46, 0x48, 0x4a, 0x4c, 0x4e, 0x60, 0x62, 0x64, 0x66, 0x68, 0x6a, 0x63 };
|
||||
struct QuakeItem {
|
||||
typedef struct QuakeItem {
|
||||
int8 x, y;
|
||||
uint8 f;
|
||||
};
|
||||
} QuakeItem;
|
||||
static const QuakeItem kQuakeItems[] = {
|
||||
{0, -16, 0x00},
|
||||
{0, -16, 0x01},
|
||||
@@ -3273,13 +3273,13 @@ endif_7:
|
||||
hookshot_effect_index = k;
|
||||
}
|
||||
|
||||
do_draw:
|
||||
static const int8 kHookShot_Move_X[4] = {0, 0, 8, -8};
|
||||
static const int8 kHookShot_Move_Y[4] = {8, -9, 0, 0};
|
||||
static const uint8 kHookShot_Draw_Flags[12] = {0, 0, 0xff, 0x80, 0x80, 0xff, 0x40, 0xff, 0x40, 0, 0xff, 0};
|
||||
static const uint8 kHookShot_Draw_Char[12] = {9, 0xa, 0xff, 9, 0xa, 0xff, 9, 0xff, 0xa, 9, 0xff, 0xa};
|
||||
|
||||
Point16U info;
|
||||
do_draw:
|
||||
Ancilla_PrepOamCoord(k, &info);
|
||||
if (ancilla_L[k])
|
||||
oam_priority_value = 0x3000;
|
||||
@@ -3640,8 +3640,8 @@ skipit:;
|
||||
ancilla_arr3[k] = kReceiveItem_Tab4[a];
|
||||
WriteTo4BPPBuffer_at_7F4000(kReceiveItem_Tab5[a]);
|
||||
}
|
||||
endif_12:
|
||||
Point16U pt;
|
||||
endif_12:
|
||||
Ancilla_PrepAdjustedOamCoord(k, &pt);
|
||||
Ancilla_ReceiveItem_Draw(k, pt.x, pt.y);
|
||||
}
|
||||
@@ -4032,9 +4032,9 @@ lbl_else:
|
||||
return;
|
||||
}
|
||||
}
|
||||
uint8 astep;
|
||||
label_b:
|
||||
|
||||
uint8 astep;
|
||||
|
||||
astep = ancilla_step[k];
|
||||
if (astep != 0)
|
||||
@@ -4056,8 +4056,8 @@ label_b:
|
||||
AncillaDraw_GTCutsceneCrystal(oam, x, y);
|
||||
oam++;
|
||||
}
|
||||
label_a:
|
||||
Point16U info;
|
||||
label_a:
|
||||
Ancilla_PrepAdjustedOamCoord(k, &info);
|
||||
|
||||
breaktowerseal_base_sparkle_x_lo[7] = info.x;
|
||||
13
ancilla.h
13
ancilla.h
@@ -1,21 +1,22 @@
|
||||
#pragma once
|
||||
#include "types.h"
|
||||
#include "zelda_rtl.h"
|
||||
#include "sprite.h"
|
||||
|
||||
struct CheckPlayerCollOut {
|
||||
typedef struct CheckPlayerCollOut {
|
||||
uint16 r4, r6;
|
||||
uint16 r8, r10;
|
||||
};
|
||||
} CheckPlayerCollOut;
|
||||
|
||||
struct AncillaOamInfo {
|
||||
typedef struct AncillaOamInfo {
|
||||
uint8 x;
|
||||
uint8 y;
|
||||
uint8 flags;
|
||||
};
|
||||
} AncillaOamInfo;
|
||||
|
||||
struct AncillaRadialProjection {
|
||||
typedef struct AncillaRadialProjection {
|
||||
uint8 r0, r2, r4, r6;
|
||||
};
|
||||
} AncillaRadialProjection;
|
||||
|
||||
uint16 Ancilla_GetX(int k);
|
||||
uint16 Ancilla_GetY(int k);
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
#pragma once
|
||||
struct AttractOamInfo {
|
||||
typedef struct AttractOamInfo {
|
||||
int8 x, y;
|
||||
uint8 c, f, e;
|
||||
};
|
||||
} AttractOamInfo;
|
||||
|
||||
|
||||
extern const uint16 kMapMode_Zooms1[224];
|
||||
|
||||
@@ -2331,7 +2331,7 @@ void Object_Draw_DoorRight_3x4(uint16 src, int door) {
|
||||
|
||||
void Dungeon_OpeningLockedDoor_Combined(bool skip_anim) {
|
||||
uint8 ctr = 2;
|
||||
int k, dma_ptr;
|
||||
int m, k, dma_ptr;
|
||||
|
||||
if (skip_anim) {
|
||||
door_animation_step_indicator = 16;
|
||||
@@ -2343,7 +2343,7 @@ void Dungeon_OpeningLockedDoor_Combined(bool skip_anim) {
|
||||
if (door_animation_step_indicator != 12)
|
||||
goto middle;
|
||||
step12:
|
||||
int m = kUpperBitmasks[dung_bg2_attr_table[dung_cur_door_pos] & 7];
|
||||
m = kUpperBitmasks[dung_bg2_attr_table[dung_cur_door_pos] & 7];
|
||||
dung_door_opened_incl_adjacent |= m;
|
||||
dung_door_opened |= m;
|
||||
ctr = 4;
|
||||
@@ -2631,7 +2631,7 @@ void Dungeon_LoadRoom() { // 81873a
|
||||
RoomDraw_DrawAllObjects(cur_p0); // Draw Layer 3 objects to BG2
|
||||
|
||||
for (dung_load_ptr_offs = 0; dung_load_ptr_offs != 0x18C; dung_load_ptr_offs += 4) {
|
||||
MovableBlockData &m = movable_block_datas[dung_load_ptr_offs >> 2];
|
||||
MovableBlockData m = movable_block_datas[dung_load_ptr_offs >> 2];
|
||||
if (m.room == dungeon_room_index)
|
||||
DrawObjects_PushableBlock(m.tilemap, dung_load_ptr_offs);
|
||||
}
|
||||
@@ -5174,8 +5174,9 @@ handle_62:
|
||||
return;
|
||||
}
|
||||
if ((a & 0xf0) == 0xf0) {
|
||||
int j;
|
||||
handle_f0:
|
||||
int j = a & 0xf;
|
||||
j = a & 0xf;
|
||||
a = door_type_and_slot[j] & 0xfe;
|
||||
if (a != kDoorType_BreakableWall && a != 0x2A && a != 0x2E)
|
||||
return;
|
||||
@@ -5739,8 +5740,9 @@ uint8 OpenChestForItem(uint8 tile, int *chest_position) { // 81eb66
|
||||
overworld_tileattr[pos + 1] = ptr[2];
|
||||
overworld_tileattr[pos + 65] = ptr[3];
|
||||
|
||||
uint8 attr;
|
||||
afterStoreCrap:
|
||||
uint8 attr = (loc < 0x8000) ? 0x27 : 0x00;
|
||||
attr = (loc < 0x8000) ? 0x27 : 0x00;
|
||||
|
||||
dung_bg2_attr_table[pos + 0] = attr;
|
||||
dung_bg2_attr_table[pos + 64] = attr;
|
||||
@@ -6131,7 +6133,7 @@ void ClearAndStripeExplodingWall(uint16 dsto) { // 81f811
|
||||
if (!(dung_door_direction[dung_cur_door_idx >> 1] & 2))
|
||||
r6++;
|
||||
|
||||
uint16 *uvdata = &uvram.t3.data[0];
|
||||
uint16 *uvdata = &uvram.data[0];
|
||||
for (;;) {
|
||||
const uint16 *bg2 = &dung_bg2[dsto];
|
||||
do {
|
||||
@@ -8532,7 +8534,7 @@ void PushBlock_HandleCollision(uint8 i, uint16 x, uint16 y) { // 87efb9
|
||||
bitmask_of_dragstate |= index_of_changable_dungeon_objs[i] ? 4 : 1;
|
||||
if (dir & 1 ? (r8 >= r10 && (uint16)(r8 - r10) < 8) : (uint16)(r8 - r10) >= 0xfff8) {
|
||||
*coord_p -= r8 - r10;
|
||||
(dir & 2 ? link_x_vel : link_y_vel) -= r8 - r10;
|
||||
*(dir & 2 ? &link_x_vel : &link_y_vel) -= r8 - r10;
|
||||
}
|
||||
}
|
||||
HandleIndoorCameraAndDoors();
|
||||
@@ -34,21 +34,21 @@ enum {
|
||||
kDoorType_ShutterTrapDL = 74,
|
||||
};
|
||||
|
||||
struct DungPalInfo {
|
||||
typedef struct DungPalInfo {
|
||||
uint8 pal0;
|
||||
uint8 pal1;
|
||||
uint8 pal2;
|
||||
uint8 pal3;
|
||||
};
|
||||
} DungPalInfo;
|
||||
|
||||
struct RoomBounds {
|
||||
typedef struct RoomBounds {
|
||||
union {
|
||||
struct {
|
||||
uint16 a0, b0, a1, b1;
|
||||
};
|
||||
uint16 v[4];
|
||||
};
|
||||
};
|
||||
} RoomBounds;
|
||||
|
||||
#define room_bounds_y (*(RoomBounds*)(g_ram+0x600))
|
||||
#define room_bounds_x (*(RoomBounds*)(g_ram+0x608))
|
||||
|
||||
@@ -2289,7 +2289,7 @@ void Credits_HandleCameraScrollControl() { // 8eaea6
|
||||
*which -= 0x10;
|
||||
overworld_screen_trans_dir_bits2 |= sign8(yvel) ? 8 : 4;
|
||||
}
|
||||
(sign8(yvel) ? overworld_unk1_neg : overworld_unk1) = -*which;
|
||||
*(sign8(yvel) ? &overworld_unk1_neg : &overworld_unk1) = -*which;
|
||||
uint16 r4 = (int8)yvel, subp;
|
||||
WORD(byte_7E069E[0]) = r4;
|
||||
uint8 oi = BYTE(overlay_index);
|
||||
@@ -2321,7 +2321,7 @@ void Credits_HandleCameraScrollControl() { // 8eaea6
|
||||
*which -= 0x10;
|
||||
overworld_screen_trans_dir_bits2 |= sign8(xvel) ? 2 : 1;
|
||||
}
|
||||
(sign8(xvel) ? overworld_unk3_neg : overworld_unk3) = -*which;
|
||||
*(sign8(xvel) ? &overworld_unk3_neg : &overworld_unk3) = -*which;
|
||||
|
||||
uint16 r4 = (int8)xvel, subp;
|
||||
WORD(byte_7E069E[1]) = r4;
|
||||
4
ending.h
4
ending.h
@@ -1,10 +1,10 @@
|
||||
#pragma once
|
||||
|
||||
struct IntroSpriteEnt {
|
||||
typedef struct IntroSpriteEnt {
|
||||
int8 x, y;
|
||||
uint8 charnum, flags;
|
||||
uint8 ext;
|
||||
};
|
||||
} IntroSpriteEnt;
|
||||
|
||||
void Intro_SetupScreen();
|
||||
void Intro_LoadTextPointersAndPalettes();
|
||||
|
||||
248
hud.cpp → hud.c
248
hud.cpp → hud.c
@@ -8,6 +8,13 @@
|
||||
|
||||
#include "variables.h"
|
||||
#include "hud.h"
|
||||
|
||||
typedef struct ItemBoxGfx {
|
||||
uint16 v[4];
|
||||
} ItemBoxGfx;
|
||||
|
||||
static void Hud_DrawItem(uint16 a, const ItemBoxGfx *src);
|
||||
|
||||
const uint8 kMaxBombsForLevel[] = { 10, 15, 20, 25, 30, 35, 40, 50 };
|
||||
const uint8 kMaxArrowsForLevel[] = { 30, 35, 40, 45, 50, 55, 60, 70 };
|
||||
static const uint8 kMaxHealthForLevel[] = { 9, 9, 9, 9, 9, 9, 9, 9, 17, 17, 17, 17, 17, 17, 17, 25, 25, 25, 25, 25, 25 };
|
||||
@@ -28,7 +35,8 @@ static const uint16 kHudBottlesGfx[128] = {
|
||||
0x24f5, 0x24f5, 0x24f5, 0x24f5, 0x24f5, 0x24f5, 0x24f5, 0x24f5, 0x2551, 0x2554, 0x2554, 0x24f5, 0x24f5, 0x24f5, 0x24f5, 0x24f5,
|
||||
0x24f5, 0x24f5, 0x24f5, 0x24f5, 0x24f5, 0x24f5, 0x24f5, 0x24f5, 0x2556, 0x255e, 0x255e, 0x2553, 0x24f5, 0x2551, 0x2554, 0x2554,
|
||||
};
|
||||
static const uint16 kHudItemBottles[9][4] = {
|
||||
|
||||
static const ItemBoxGfx kHudItemBottles[9] = {
|
||||
{0x20f5, 0x20f5, 0x20f5, 0x20f5},
|
||||
{0x2044, 0x2045, 0x2046, 0x2047},
|
||||
{0x2837, 0x2838, 0x2cc3, 0x2cd3},
|
||||
@@ -39,155 +47,155 @@ static const uint16 kHudItemBottles[9][4] = {
|
||||
{0x2837, 0x2838, 0x2839, 0x283a},
|
||||
{0x2837, 0x2838, 0x2839, 0x283a},
|
||||
};
|
||||
static const uint16 kHudItemBow[5][4] = {
|
||||
static const ItemBoxGfx kHudItemBow[5] = {
|
||||
{0x20f5, 0x20f5, 0x20f5, 0x20f5},
|
||||
{0x28ba, 0x28e9, 0x28e8, 0x28cb},
|
||||
{0x28ba, 0x284a, 0x2849, 0x28cb},
|
||||
{0x28ba, 0x28e9, 0x28e8, 0x28cb},
|
||||
{0x28ba, 0x28bb, 0x24ca, 0x28cb},
|
||||
};
|
||||
static const uint16 kHudItemBoomerang[3][4] = {
|
||||
static const ItemBoxGfx kHudItemBoomerang[3] = {
|
||||
{0x20f5, 0x20f5, 0x20f5, 0x20f5},
|
||||
{0x2cb8, 0x2cb9, 0x2cf5, 0x2cc9},
|
||||
{0x24b8, 0x24b9, 0x24f5, 0x24c9},
|
||||
};
|
||||
static const uint16 kHudItemHookshot[2][4] = {
|
||||
static const ItemBoxGfx kHudItemHookshot[2] = {
|
||||
{0x20f5, 0x20f5, 0x20f5, 0x20f5},
|
||||
{0x24f5, 0x24f6, 0x24c0, 0x24f5},
|
||||
};
|
||||
static const uint16 kHudItemBombs[2][4] = {
|
||||
static const ItemBoxGfx kHudItemBombs[2] = {
|
||||
{0x20f5, 0x20f5, 0x20f5, 0x20f5},
|
||||
{0x2cb2, 0x2cb3, 0x2cc2, 0x6cc2},
|
||||
};
|
||||
static const uint16 kHudItemMushroom[3][4] = {
|
||||
static const ItemBoxGfx kHudItemMushroom[3] = {
|
||||
{0x20f5, 0x20f5, 0x20f5, 0x20f5},
|
||||
{0x2444, 0x2445, 0x2446, 0x2447},
|
||||
{0x203b, 0x203c, 0x203d, 0x203e},
|
||||
};
|
||||
static const uint16 kHudItemFireRod[2][4] = {
|
||||
static const ItemBoxGfx kHudItemFireRod[2] = {
|
||||
{0x20f5, 0x20f5, 0x20f5, 0x20f5},
|
||||
{0x24b0, 0x24b1, 0x24c0, 0x24c1},
|
||||
};
|
||||
static const uint16 kHudItemIceRod[2][4] = {
|
||||
static const ItemBoxGfx kHudItemIceRod[2] = {
|
||||
{0x20f5, 0x20f5, 0x20f5, 0x20f5},
|
||||
{0x2cb0, 0x2cbe, 0x2cc0, 0x2cc1},
|
||||
};
|
||||
static const uint16 kHudItemBombos[2][4] = {
|
||||
static const ItemBoxGfx kHudItemBombos[2] = {
|
||||
{0x20f5, 0x20f5, 0x20f5, 0x20f5},
|
||||
{0x287d, 0x287e, 0xe87e, 0xe87d},
|
||||
};
|
||||
static const uint16 kHudItemEther[2][4] = {
|
||||
static const ItemBoxGfx kHudItemEther[2] = {
|
||||
{0x20f5, 0x20f5, 0x20f5, 0x20f5},
|
||||
{0x2876, 0x2877, 0xE877, 0xE876},
|
||||
};
|
||||
static const uint16 kHudItemQuake[2][4] = {
|
||||
static const ItemBoxGfx kHudItemQuake[2] = {
|
||||
{0x20f5, 0x20f5, 0x20f5, 0x20f5},
|
||||
{0x2866, 0x2867, 0xE867, 0xE866},
|
||||
};
|
||||
static const uint16 kHudItemTorch[2][4] = {
|
||||
static const ItemBoxGfx kHudItemTorch[2] = {
|
||||
{0x20f5, 0x20f5, 0x20f5, 0x20f5},
|
||||
{0x24bc, 0x24bd, 0x24cc, 0x24cd},
|
||||
};
|
||||
static const uint16 kHudItemHammer[2][4] = {
|
||||
static const ItemBoxGfx kHudItemHammer[2] = {
|
||||
{0x20f5, 0x20f5, 0x20f5, 0x20f5},
|
||||
{0x20b6, 0x20b7, 0x20c6, 0x20c7},
|
||||
};
|
||||
static const uint16 kHudItemFlute[4][4] = {
|
||||
static const ItemBoxGfx kHudItemFlute[4] = {
|
||||
{0x20f5, 0x20f5, 0x20f5, 0x20f5},
|
||||
{0x20d0, 0x20d1, 0x20e0, 0x20e1},
|
||||
{0x2cd4, 0x2cd5, 0x2ce4, 0x2ce5},
|
||||
{0x2cd4, 0x2cd5, 0x2ce4, 0x2ce5},
|
||||
};
|
||||
static const uint16 kHudItemBugNet[2][4] = {
|
||||
static const ItemBoxGfx kHudItemBugNet[2] = {
|
||||
{0x20f5, 0x20f5, 0x20f5, 0x20f5},
|
||||
{0x3c40, 0x3c41, 0x2842, 0x3c43},
|
||||
};
|
||||
static const uint16 kHudItemBookMudora[2][4] = {
|
||||
static const ItemBoxGfx kHudItemBookMudora[2] = {
|
||||
{0x20f5, 0x20f5, 0x20f5, 0x20f5},
|
||||
{0x3ca5, 0x3ca6, 0x3cd8, 0x3cd9},
|
||||
};
|
||||
static const uint16 kHudItemCaneSomaria[2][4] = {
|
||||
static const ItemBoxGfx kHudItemCaneSomaria[2] = {
|
||||
{0x20f5, 0x20f5, 0x20f5, 0x20f5},
|
||||
{0x24dc, 0x24dd, 0x24ec, 0x24ed},
|
||||
};
|
||||
static const uint16 kHudItemCaneByrna[2][4] = {
|
||||
static const ItemBoxGfx kHudItemCaneByrna[2] = {
|
||||
{0x20f5, 0x20f5, 0x20f5, 0x20f5},
|
||||
{0x2cdc, 0x2cdd, 0x2cec, 0x2ced},
|
||||
};
|
||||
static const uint16 kHudItemCape[2][4] = {
|
||||
static const ItemBoxGfx kHudItemCape[2] = {
|
||||
{0x20f5, 0x20f5, 0x20f5, 0x20f5},
|
||||
{0x24b4, 0x24b5, 0x24c4, 0x24c5},
|
||||
};
|
||||
static const uint16 kHudItemMirror[4][4] = {
|
||||
static const ItemBoxGfx kHudItemMirror[4] = {
|
||||
{0x20f5, 0x20f5, 0x20f5, 0x20f5},
|
||||
{0x28de, 0x28df, 0x28ee, 0x28ef},
|
||||
{0x2c62, 0x2c63, 0x2c72, 0x2c73},
|
||||
{0x2886, 0x2887, 0x2888, 0x2889},
|
||||
};
|
||||
static const uint16 kHudItemGloves[3][4] = {
|
||||
static const ItemBoxGfx kHudItemGloves[3] = {
|
||||
{0x20f5, 0x20f5, 0x20f5, 0x20f5},
|
||||
{0x2130, 0x2131, 0x2140, 0x2141},
|
||||
{0x28da, 0x28db, 0x28ea, 0x28eb},
|
||||
};
|
||||
static const uint16 kHudItemBoots[2][4] = {
|
||||
static const ItemBoxGfx kHudItemBoots[2] = {
|
||||
{0x20f5, 0x20f5, 0x20f5, 0x20f5},
|
||||
{0x3429, 0x342a, 0x342b, 0x342c},
|
||||
};
|
||||
static const uint16 kHudItemFlippers[2][4] = {
|
||||
static const ItemBoxGfx kHudItemFlippers[2] = {
|
||||
{0x20f5, 0x20f5, 0x20f5, 0x20f5},
|
||||
{0x2c9a, 0x2c9b, 0x2c9d, 0x2c9e},
|
||||
};
|
||||
static const uint16 kHudItemMoonPearl[2][4] = {
|
||||
static const ItemBoxGfx kHudItemMoonPearl[2] = {
|
||||
{0x20f5, 0x20f5, 0x20f5, 0x20f5},
|
||||
{0x2433, 0x2434, 0x2435, 0x2436},
|
||||
};
|
||||
static const uint16 kHudItemEmpty[1][4] = {
|
||||
static const ItemBoxGfx kHudItemEmpty[1] = {
|
||||
{0x20f5, 0x20f5, 0x20f5, 0x20f5},
|
||||
};
|
||||
static const uint16 kHudItemSword[5][4] = {
|
||||
static const ItemBoxGfx kHudItemSword[5] = {
|
||||
{0x20f5, 0x20f5, 0x20f5, 0x20f5},
|
||||
{0x2c64, 0x2cce, 0x2c75, 0x3d25},
|
||||
{0x2c8a, 0x2c65, 0x2474, 0x3d26},
|
||||
{0x248a, 0x2465, 0x3c74, 0x2d48},
|
||||
{0x288a, 0x2865, 0x2c74, 0x2d39},
|
||||
};
|
||||
static const uint16 kHudItemShield[4][4] = {
|
||||
static const ItemBoxGfx kHudItemShield[4] = {
|
||||
{0x24f5, 0x24f5, 0x24f5, 0x24f5},
|
||||
{0x2cfd, 0x6cfd, 0x2cfe, 0x6cfe},
|
||||
{0x34ff, 0x74ff, 0x349f, 0x749f},
|
||||
{0x2880, 0x2881, 0x288d, 0x288e},
|
||||
};
|
||||
static const uint16 kHudItemArmor[5][4] = {
|
||||
static const ItemBoxGfx kHudItemArmor[5] = {
|
||||
{0x3c68, 0x7c68, 0x3c78, 0x7c78},
|
||||
{0x2c68, 0x6c68, 0x2c78, 0x6c78},
|
||||
{0x2468, 0x6468, 0x2478, 0x6478},
|
||||
};
|
||||
static const uint16 kHudItemDungeonCompass[2][4] = {
|
||||
static const ItemBoxGfx kHudItemDungeonCompass[2] = {
|
||||
{0x20f5, 0x20f5, 0x20f5, 0x20f5},
|
||||
{0x24bf, 0x64bf, 0x2ccf, 0x6ccf},
|
||||
};
|
||||
static const uint16 kHudItemPalaceItem[3][4] = {
|
||||
static const ItemBoxGfx kHudItemPalaceItem[3] = {
|
||||
{0x20f5, 0x20f5, 0x20f5, 0x20f5},
|
||||
{0x28d6, 0x68d6, 0x28e6, 0x28e7},
|
||||
{0x354b, 0x354c, 0x354d, 0x354e},
|
||||
};
|
||||
static const uint16 kHudItemDungeonMap[2][4] = {
|
||||
static const ItemBoxGfx kHudItemDungeonMap[2] = {
|
||||
{0x20f5, 0x20f5, 0x20f5, 0x20f5},
|
||||
{0x28de, 0x28df, 0x28ee, 0x28ef},
|
||||
};
|
||||
static const uint16 kHudPendants0[2][4] = {
|
||||
static const ItemBoxGfx kHudPendants0[2] = {
|
||||
{0x313b, 0x313c, 0x313d, 0x313e},
|
||||
{0x252b, 0x252c, 0x252d, 0x252e}
|
||||
};
|
||||
static const uint16 kHudPendants1[2][4] = {
|
||||
static const ItemBoxGfx kHudPendants1[2] = {
|
||||
{0x313b, 0x313c, 0x313d, 0x313e},
|
||||
{0x2d2b, 0x2d2c, 0x2d2d, 0x2d2e}
|
||||
};
|
||||
static const uint16 kHudPendants2[2][4] = {
|
||||
static const ItemBoxGfx kHudPendants2[2] = {
|
||||
{0x313b, 0x313c, 0x313d, 0x313e},
|
||||
{0x3d2b, 0x3d2c, 0x3d2d, 0x3d2e}
|
||||
};
|
||||
static const uint16 kHudItemHeartPieces[4][4] = {
|
||||
static const ItemBoxGfx kHudItemHeartPieces[4] = {
|
||||
{0x2484, 0x6484, 0x2485, 0x6485},
|
||||
{0x24ad, 0x6484, 0x2485, 0x6485},
|
||||
{0x24ad, 0x6484, 0x24ae, 0x6485},
|
||||
@@ -285,39 +293,40 @@ static const uint16 kHudTilemap[165] = {
|
||||
0x207f, 0x207f, 0x2854, 0x305e, 0x6854, 0x207f, 0x207f, 0x207f, 0x207f, 0x207f, 0x207f, 0x207f, 0x207f, 0x207f, 0x207f, 0x207f, 0x207f, 0x207f, 0x207f, 0x207f, 0x207f, 0x207f, 0x207f, 0x207f, 0x207f, 0x207f, 0x207f, 0x207f, 0x207f, 0x207f, 0x207f, 0x207f,
|
||||
0x207f, 0x207f, 0xa850, 0x2856, 0xe850,
|
||||
};
|
||||
const uint16 *const kHudItemBoxGfxPtrs[] = {
|
||||
kHudItemBow[0],
|
||||
kHudItemBoomerang[0],
|
||||
kHudItemHookshot[0],
|
||||
kHudItemBombs[0],
|
||||
kHudItemMushroom[0],
|
||||
kHudItemFireRod[0],
|
||||
kHudItemIceRod[0],
|
||||
kHudItemBombos[0],
|
||||
kHudItemEther[0],
|
||||
kHudItemQuake[0],
|
||||
kHudItemTorch[0],
|
||||
kHudItemHammer[0],
|
||||
kHudItemFlute[0],
|
||||
kHudItemBugNet[0],
|
||||
kHudItemBookMudora[0],
|
||||
kHudItemBottles[0],
|
||||
kHudItemCaneSomaria[0],
|
||||
kHudItemCaneByrna[0],
|
||||
kHudItemCape[0],
|
||||
kHudItemMirror[0],
|
||||
kHudItemGloves[0],
|
||||
kHudItemBoots[0],
|
||||
kHudItemFlippers[0],
|
||||
kHudItemMoonPearl[0],
|
||||
kHudItemEmpty[0],
|
||||
kHudItemSword[0],
|
||||
kHudItemShield[0],
|
||||
kHudItemArmor[0],
|
||||
kHudItemBottles[0],
|
||||
kHudItemBottles[0],
|
||||
kHudItemBottles[0],
|
||||
kHudItemBottles[0],
|
||||
|
||||
static const ItemBoxGfx * const kHudItemBoxGfxPtrs[] = {
|
||||
kHudItemBow,
|
||||
kHudItemBoomerang,
|
||||
kHudItemHookshot,
|
||||
kHudItemBombs,
|
||||
kHudItemMushroom,
|
||||
kHudItemFireRod,
|
||||
kHudItemIceRod,
|
||||
kHudItemBombos,
|
||||
kHudItemEther,
|
||||
kHudItemQuake,
|
||||
kHudItemTorch,
|
||||
kHudItemHammer,
|
||||
kHudItemFlute,
|
||||
kHudItemBugNet,
|
||||
kHudItemBookMudora,
|
||||
kHudItemBottles,
|
||||
kHudItemCaneSomaria,
|
||||
kHudItemCaneByrna,
|
||||
kHudItemCape,
|
||||
kHudItemMirror,
|
||||
kHudItemGloves,
|
||||
kHudItemBoots,
|
||||
kHudItemFlippers,
|
||||
kHudItemMoonPearl,
|
||||
kHudItemEmpty,
|
||||
kHudItemSword,
|
||||
kHudItemShield,
|
||||
kHudItemArmor,
|
||||
kHudItemBottles,
|
||||
kHudItemBottles,
|
||||
kHudItemBottles,
|
||||
kHudItemBottles,
|
||||
};
|
||||
static const uint16 kUpdateMagicPowerTilemap[17][4] = {
|
||||
{0x3cf5, 0x3cf5, 0x3cf5, 0x3cf5},
|
||||
@@ -832,11 +841,11 @@ void Hud_UpdateBottleMenu() { // 8de17f
|
||||
for (int x = 0; x < 8; x++)
|
||||
uvram_screen.row[y].col[22 + x] = 0x24f5;
|
||||
|
||||
Hud_DrawItem(0x1372, kHudItemBottles[link_bottle_info[0]]);
|
||||
Hud_DrawItem(0x1472, kHudItemBottles[link_bottle_info[1]]);
|
||||
Hud_DrawItem(0x1572, kHudItemBottles[link_bottle_info[2]]);
|
||||
Hud_DrawItem(0x1672, kHudItemBottles[link_bottle_info[3]]);
|
||||
Hud_DrawItem(0x1408, kHudItemBottles[link_item_bottles ? link_bottle_info[link_item_bottles - 1] : 0]);
|
||||
Hud_DrawItem(0x1372, &kHudItemBottles[link_bottle_info[0]]);
|
||||
Hud_DrawItem(0x1472, &kHudItemBottles[link_bottle_info[1]]);
|
||||
Hud_DrawItem(0x1572, &kHudItemBottles[link_bottle_info[2]]);
|
||||
Hud_DrawItem(0x1672, &kHudItemBottles[link_bottle_info[3]]);
|
||||
Hud_DrawItem(0x1408, &kHudItemBottles[link_item_bottles ? link_bottle_info[link_item_bottles - 1] : 0]);
|
||||
|
||||
uint16 *p = (uint16 *)&g_ram[kHudItemInVramPtr[hud_cur_item - 1]];
|
||||
uvram_screen.row[6].col[25] = p[0];
|
||||
@@ -902,12 +911,12 @@ void Hud_RestoreNormalMenu() { // 8de346
|
||||
BYTE(nmi_load_target_addr) = 0x22;
|
||||
}
|
||||
|
||||
void Hud_DrawItem(uint16 a, const uint16 *src) { // 8de372
|
||||
static void Hud_DrawItem(uint16 a, const ItemBoxGfx *src) { // 8de372
|
||||
uint16 *dst = (uint16 *)&g_ram[a];
|
||||
dst[0] = src[0];
|
||||
dst[1] = src[1];
|
||||
dst[32] = src[2];
|
||||
dst[33] = src[3];
|
||||
dst[0] = src->v[0];
|
||||
dst[1] = src->v[1];
|
||||
dst[32] = src->v[2];
|
||||
dst[33] = src->v[3];
|
||||
}
|
||||
|
||||
void Hud_SearchForEquippedItem() { // 8de399
|
||||
@@ -959,26 +968,26 @@ void Hud_DrawYButtonItems(uint16 mask) { // 8de3d9
|
||||
uvram_screen.row[5].col[3] = 0x246E;
|
||||
uvram_screen.row[5].col[4] = 0x246F;
|
||||
|
||||
Hud_DrawItem(0x11c8, kHudItemBow[link_item_bow]);
|
||||
Hud_DrawItem(0x11ce, kHudItemBoomerang[link_item_boomerang]);
|
||||
Hud_DrawItem(0x11d4, kHudItemHookshot[link_item_hookshot]);
|
||||
Hud_DrawItem(0x11da, kHudItemBombs[link_item_bombs ? 1 : 0]);
|
||||
Hud_DrawItem(0x11e0, kHudItemMushroom[link_item_mushroom]);
|
||||
Hud_DrawItem(0x1288, kHudItemFireRod[link_item_fire_rod]);
|
||||
Hud_DrawItem(0x128e, kHudItemIceRod[link_item_ice_rod]);
|
||||
Hud_DrawItem(0x1294, kHudItemBombos[link_item_bombos_medallion]);
|
||||
Hud_DrawItem(0x129a, kHudItemEther[link_item_ether_medallion]);
|
||||
Hud_DrawItem(0x12a0, kHudItemQuake[link_item_quake_medallion]);
|
||||
Hud_DrawItem(0x1348, kHudItemTorch[link_item_torch]);
|
||||
Hud_DrawItem(0x134e, kHudItemHammer[link_item_hammer]);
|
||||
Hud_DrawItem(0x1354, kHudItemFlute[link_item_flute]);
|
||||
Hud_DrawItem(0x135a, kHudItemBugNet[link_item_bug_net]);
|
||||
Hud_DrawItem(0x1360, kHudItemBookMudora[link_item_book_of_mudora]);
|
||||
Hud_DrawItem(0x1408, kHudItemBottles[link_item_bottles ? link_bottle_info[link_item_bottles - 1] : 0]);
|
||||
Hud_DrawItem(0x140e, kHudItemCaneSomaria[link_item_cane_somaria]);
|
||||
Hud_DrawItem(0x1414, kHudItemCaneByrna[link_item_cane_byrna]);
|
||||
Hud_DrawItem(0x141a, kHudItemCape[link_item_cape]);
|
||||
Hud_DrawItem(0x1420, kHudItemMirror[link_item_mirror]);
|
||||
Hud_DrawItem(0x11c8, &kHudItemBow[link_item_bow]);
|
||||
Hud_DrawItem(0x11ce, &kHudItemBoomerang[link_item_boomerang]);
|
||||
Hud_DrawItem(0x11d4, &kHudItemHookshot[link_item_hookshot]);
|
||||
Hud_DrawItem(0x11da, &kHudItemBombs[link_item_bombs ? 1 : 0]);
|
||||
Hud_DrawItem(0x11e0, &kHudItemMushroom[link_item_mushroom]);
|
||||
Hud_DrawItem(0x1288, &kHudItemFireRod[link_item_fire_rod]);
|
||||
Hud_DrawItem(0x128e, &kHudItemIceRod[link_item_ice_rod]);
|
||||
Hud_DrawItem(0x1294, &kHudItemBombos[link_item_bombos_medallion]);
|
||||
Hud_DrawItem(0x129a, &kHudItemEther[link_item_ether_medallion]);
|
||||
Hud_DrawItem(0x12a0, &kHudItemQuake[link_item_quake_medallion]);
|
||||
Hud_DrawItem(0x1348, &kHudItemTorch[link_item_torch]);
|
||||
Hud_DrawItem(0x134e, &kHudItemHammer[link_item_hammer]);
|
||||
Hud_DrawItem(0x1354, &kHudItemFlute[link_item_flute]);
|
||||
Hud_DrawItem(0x135a, &kHudItemBugNet[link_item_bug_net]);
|
||||
Hud_DrawItem(0x1360, &kHudItemBookMudora[link_item_book_of_mudora]);
|
||||
Hud_DrawItem(0x1408, &kHudItemBottles[link_item_bottles ? link_bottle_info[link_item_bottles - 1] : 0]);
|
||||
Hud_DrawItem(0x140e, &kHudItemCaneSomaria[link_item_cane_somaria]);
|
||||
Hud_DrawItem(0x1414, &kHudItemCaneByrna[link_item_cane_byrna]);
|
||||
Hud_DrawItem(0x141a, &kHudItemCape[link_item_cape]);
|
||||
Hud_DrawItem(0x1420, &kHudItemMirror[link_item_mirror]);
|
||||
}
|
||||
|
||||
void Hud_DrawUnknownBox(uint16 palmask) { // 8de647
|
||||
@@ -1072,9 +1081,9 @@ void Hud_DrawAbilityText(uint16 palmask) { // 8de6b6
|
||||
}
|
||||
|
||||
void Hud_DrawAbilityIcons() { // 8de7b7
|
||||
Hud_DrawItem(0x16D0, kHudItemGloves[link_item_gloves]);
|
||||
Hud_DrawItem(0x16C8, kHudItemBoots[link_item_boots]);
|
||||
Hud_DrawItem(0x16D8, kHudItemFlippers[link_item_flippers]);
|
||||
Hud_DrawItem(0x16D0, &kHudItemGloves[link_item_gloves]);
|
||||
Hud_DrawItem(0x16C8, &kHudItemBoots[link_item_boots]);
|
||||
Hud_DrawItem(0x16D8, &kHudItemFlippers[link_item_flippers]);
|
||||
if (link_item_gloves)
|
||||
Hud_DrawGlovesText(link_item_gloves != 1);
|
||||
}
|
||||
@@ -1100,9 +1109,9 @@ void Hud_DrawProgressIcons_Pendants() { // 8de9d3
|
||||
src += 10;
|
||||
}
|
||||
|
||||
Hud_DrawItem(0x13B2, kHudPendants0[(link_which_pendants >> 0) & 1]);
|
||||
Hud_DrawItem(0x146E, kHudPendants1[(link_which_pendants >> 1) & 1]);
|
||||
Hud_DrawItem(0x1476, kHudPendants2[(link_which_pendants >> 2) & 1]);
|
||||
Hud_DrawItem(0x13B2, &kHudPendants0[(link_which_pendants >> 0) & 1]);
|
||||
Hud_DrawItem(0x146E, &kHudPendants1[(link_which_pendants >> 1) & 1]);
|
||||
Hud_DrawItem(0x1476, &kHudPendants2[(link_which_pendants >> 2) & 1]);
|
||||
}
|
||||
|
||||
void Hud_DrawProgressIcons_Crystals() { // 8dea62
|
||||
@@ -1188,7 +1197,7 @@ void Hud_DrawSelectedYButtonItem() { // 8deb3a
|
||||
}
|
||||
|
||||
void Hud_DrawMoonPearl() { // 8dece9
|
||||
Hud_DrawItem(0x16e0, kHudItemMoonPearl[link_item_moon_pearl]);
|
||||
Hud_DrawItem(0x16e0, &kHudItemMoonPearl[link_item_moon_pearl]);
|
||||
}
|
||||
|
||||
void Hud_DrawEquipment(uint16 palmask) { // 8ded29
|
||||
@@ -1232,34 +1241,34 @@ void Hud_DrawEquipment(uint16 palmask) { // 8ded29
|
||||
if (cur_palace_index_x2 == 0xff) {
|
||||
for (int i = 0; i < 8; i++)
|
||||
uvram_screen.row[26].col[22 + i] = 0x24F5;
|
||||
Hud_DrawItem(0x16f2, kHudItemHeartPieces[link_heart_pieces]);
|
||||
Hud_DrawItem(0x16f2, &kHudItemHeartPieces[link_heart_pieces]);
|
||||
}
|
||||
Hud_DrawItem(0x15ec, kHudItemSword[link_sword_type == 0xff ? 0 : link_sword_type]);
|
||||
Hud_DrawItem(0x15ec, &kHudItemSword[link_sword_type == 0xff ? 0 : link_sword_type]);
|
||||
}
|
||||
|
||||
void Hud_DrawShield() { // 8dee21
|
||||
Hud_DrawItem(0x15f2, kHudItemShield[link_shield_type]);
|
||||
Hud_DrawItem(0x15f2, &kHudItemShield[link_shield_type]);
|
||||
}
|
||||
|
||||
void Hud_DrawArmor() { // 8dee3c
|
||||
Hud_DrawItem(0x15f8, kHudItemArmor[link_armor]);
|
||||
Hud_DrawItem(0x15f8, &kHudItemArmor[link_armor]);
|
||||
}
|
||||
|
||||
void Hud_DrawMapAndBigKey() { // 8dee57
|
||||
if (cur_palace_index_x2 != 0xff &&
|
||||
(link_bigkey << (cur_palace_index_x2 >> 1)) & 0x8000) {
|
||||
Hud_DrawItem(0x16F8, kHudItemPalaceItem[CheckPalaceItemPosession() + 1]);
|
||||
Hud_DrawItem(0x16F8, &kHudItemPalaceItem[CheckPalaceItemPosession() + 1]);
|
||||
}
|
||||
if (cur_palace_index_x2 != 0xff &&
|
||||
(link_dungeon_map << (cur_palace_index_x2 >> 1)) & 0x8000) {
|
||||
Hud_DrawItem(0x16EC, kHudItemDungeonMap[1]);
|
||||
Hud_DrawItem(0x16EC, &kHudItemDungeonMap[1]);
|
||||
}
|
||||
}
|
||||
|
||||
void Hud_DrawCompass() { // 8def39
|
||||
if (cur_palace_index_x2 != 0xff &&
|
||||
(link_compass << (cur_palace_index_x2 >> 1)) & 0x8000) {
|
||||
Hud_DrawItem(0x16F2, kHudItemDungeonCompass[1]);
|
||||
Hud_DrawItem(0x16F2, &kHudItemDungeonCompass[1]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1284,11 +1293,11 @@ void Hud_DrawBottleMenu(uint16 palmask) { // 8def67
|
||||
for (int x = 0; x < 8; x++)
|
||||
uvram_screen.row[y].col[22 + x] = 0x24f5;
|
||||
|
||||
Hud_DrawItem(0x1372, kHudItemBottles[link_bottle_info[0]]);
|
||||
Hud_DrawItem(0x1472, kHudItemBottles[link_bottle_info[1]]);
|
||||
Hud_DrawItem(0x1572, kHudItemBottles[link_bottle_info[2]]);
|
||||
Hud_DrawItem(0x1672, kHudItemBottles[link_bottle_info[3]]);
|
||||
Hud_DrawItem(0x1408, kHudItemBottles[link_bottle_info[link_item_bottles - 1]]);
|
||||
Hud_DrawItem(0x1372, &kHudItemBottles[link_bottle_info[0]]);
|
||||
Hud_DrawItem(0x1472, &kHudItemBottles[link_bottle_info[1]]);
|
||||
Hud_DrawItem(0x1572, &kHudItemBottles[link_bottle_info[2]]);
|
||||
Hud_DrawItem(0x1672, &kHudItemBottles[link_bottle_info[3]]);
|
||||
Hud_DrawItem(0x1408, &kHudItemBottles[link_bottle_info[link_item_bottles - 1]]);
|
||||
|
||||
uint16 *p = (uint16 *)&g_ram[kHudItemInVramPtr[hud_cur_item - 1]];
|
||||
|
||||
@@ -1411,7 +1420,7 @@ void Hud_UpdateItemBox() { // 8dfafd
|
||||
else if (hud_cur_item == 16)
|
||||
item_val = link_bottle_info[item_val - 1];
|
||||
|
||||
const uint16 *p = kHudItemBoxGfxPtrs[hud_cur_item - 1] + item_val * 4;
|
||||
const uint16 *p = kHudItemBoxGfxPtrs[hud_cur_item - 1][item_val].v;
|
||||
|
||||
hud_tile_indices_buffer[37] = p[0];
|
||||
hud_tile_indices_buffer[38] = p[1];
|
||||
@@ -1484,3 +1493,6 @@ void Hud_UpdateHearts(uint16 *dst, const uint16 *src, int n) { // 8dfdab
|
||||
}
|
||||
}
|
||||
|
||||
const uint16 *Hud_GetItemBoxPtr(int item) {
|
||||
return kHudItemBoxGfxPtrs[item]->v;
|
||||
}
|
||||
10
hud.h
10
hud.h
@@ -1,14 +1,6 @@
|
||||
#pragma once
|
||||
#include "types.h"
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
extern const uint16 *const kHudItemBoxGfxPtrs[];
|
||||
void Hud_RefreshIcon();
|
||||
uint8 CheckPalaceItemPosession();
|
||||
void Hud_GotoPrevItem();
|
||||
@@ -38,7 +30,6 @@ void Hud_BottleMenu();
|
||||
void Hud_UpdateBottleMenu();
|
||||
void Hud_EraseBottleMenu();
|
||||
void Hud_RestoreNormalMenu();
|
||||
void Hud_DrawItem(uint16 a, const uint16 *src);
|
||||
void Hud_SearchForEquippedItem();
|
||||
uint16 Hud_GetPaletteMask(uint8 what);
|
||||
void Hud_DrawYButtonItems(uint16 mask);
|
||||
@@ -70,3 +61,4 @@ void Hud_UpdateInternal();
|
||||
void Hud_Update_IgnoreItemBox();
|
||||
void Hud_Update_IgnoreHealth();
|
||||
void Hud_UpdateHearts(uint16 *dst, const uint16 *src, int n);
|
||||
const uint16 *Hud_GetItemBoxPtr(int item);
|
||||
@@ -1,4 +1,3 @@
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
@@ -20,6 +19,9 @@
|
||||
|
||||
#include "zelda_rtl.h"
|
||||
|
||||
extern Ppu *GetPpuForRendering();
|
||||
extern Dsp *GetDspForRendering();
|
||||
|
||||
extern uint8 g_emulated_ram[0x20000];
|
||||
bool g_run_without_emu = false;
|
||||
|
||||
@@ -33,13 +35,17 @@ bool RunOneFrame(Snes *snes, int input_state, bool turbo);
|
||||
|
||||
static uint8_t* readFile(char* name, size_t* length);
|
||||
static bool loadRom(char* name, Snes* snes);
|
||||
static bool checkExtention(const char* name, bool forZip);
|
||||
static void playAudio(Snes *snes, SDL_AudioDeviceID device, int16_t* audioBuffer);
|
||||
static void renderScreen(SDL_Renderer* renderer, SDL_Texture* texture);
|
||||
static void handleInput(int keyCode, int modCode, bool pressed);
|
||||
|
||||
int input1_current_state;
|
||||
|
||||
void NORETURN Die(const char *error) {
|
||||
fprintf(stderr, "Error: %s\n", error);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
void setButtonState(int button, bool pressed) {
|
||||
// set key in constroller
|
||||
if (pressed) {
|
||||
@@ -49,27 +55,6 @@ void setButtonState(int button, bool pressed) {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void ZeldaReadSram(Snes *snes) {
|
||||
FILE *f = fopen("saves/sram.dat", "rb");
|
||||
if (f) {
|
||||
fread(g_zenv.sram, 1, 8192, f);
|
||||
memcpy(snes->cart->ram, g_zenv.sram, 8192);
|
||||
fclose(f);
|
||||
}
|
||||
}
|
||||
|
||||
void ZeldaWriteSram() {
|
||||
rename("saves/sram.dat", "saves/sram.bak");
|
||||
FILE *f = fopen("saves/sram.dat", "wb");
|
||||
if (f) {
|
||||
fwrite(g_zenv.sram, 1, 8192, f);
|
||||
fclose(f);
|
||||
} else {
|
||||
fprintf(stderr, "Unable to write saves/sram.dat\n");
|
||||
}
|
||||
}
|
||||
|
||||
#undef main
|
||||
int main(int argc, char** argv) {
|
||||
// set up SDL
|
||||
@@ -243,9 +228,6 @@ int main(int argc, char** argv) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
extern struct Ppu *GetPpuForRendering();
|
||||
extern struct Dsp *GetDspForRendering();
|
||||
|
||||
static void playAudio(Snes *snes, SDL_AudioDeviceID device, int16_t* audioBuffer) {
|
||||
// generate enough samples
|
||||
if (!kIsOrigEmu && snes) {
|
||||
@@ -335,31 +317,11 @@ static void handleInput(int keyCode, int keyMod, bool pressed) {
|
||||
}
|
||||
}
|
||||
|
||||
static bool checkExtention(const char* name, bool forZip) {
|
||||
if(name == NULL) return false;
|
||||
int length = strlen(name);
|
||||
if(length < 4) return false;
|
||||
if(forZip) {
|
||||
if(strcmp(name + length - 4, ".zip") == 0) return true;
|
||||
if(strcmp(name + length - 4, ".ZIP") == 0) return true;
|
||||
} else {
|
||||
if(strcmp(name + length - 4, ".smc") == 0) return true;
|
||||
if(strcmp(name + length - 4, ".SMC") == 0) return true;
|
||||
if(strcmp(name + length - 4, ".sfc") == 0) return true;
|
||||
if(strcmp(name + length - 4, ".SFC") == 0) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool loadRom(char* name, Snes* snes) {
|
||||
// zip library from https://github.com/kuba--/zip
|
||||
size_t length = 0;
|
||||
uint8_t* file = NULL;
|
||||
file = readFile(name, &length);
|
||||
if(file == NULL) {
|
||||
puts("Failed to read file");
|
||||
return false;
|
||||
}
|
||||
if(!file) Die("Failed to read file");
|
||||
|
||||
PatchRom(file);
|
||||
|
||||
@@ -368,6 +330,7 @@ static bool loadRom(char* name, Snes* snes) {
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
static uint8_t* readFile(char* name, size_t* length) {
|
||||
FILE* f = fopen(name, "rb");
|
||||
if(f == NULL) {
|
||||
@@ -377,6 +340,7 @@ static uint8_t* readFile(char* name, size_t* length) {
|
||||
int size = ftell(f);
|
||||
rewind(f);
|
||||
uint8_t* buffer = (uint8_t *)malloc(size);
|
||||
if (!buffer) Die("malloc failed");
|
||||
fread(buffer, size, 1, f);
|
||||
fclose(f);
|
||||
*length = size;
|
||||
@@ -2746,7 +2746,7 @@ void RenderText_FindYItem_Next() { // 8ecded
|
||||
|
||||
void RenderText_DrawSelectedYItem() { // 8ece14
|
||||
int item = choice_in_multiselect_box;
|
||||
const uint16 *p = kHudItemBoxGfxPtrs[item];
|
||||
const uint16 *p = Hud_GetItemBoxPtr(item);
|
||||
p += ((item == 3 || item == 32) ? 1 : (&link_item_bow)[item]) * 4;
|
||||
uint8 *vwf300 = &g_ram[0x1300];
|
||||
memcpy(vwf300 + 0xc2, p, 4);
|
||||
@@ -276,7 +276,7 @@ void NMI_DoUpdates() { // 8089e0
|
||||
}
|
||||
|
||||
if (nmi_copy_packets_flag) {
|
||||
uint8 *p = (uint8 *)uvram.t3.data;
|
||||
uint8 *p = (uint8 *)uvram.data;
|
||||
do {
|
||||
int dst = WORD(p[0]);
|
||||
int vmain = p[2];
|
||||
@@ -321,7 +321,7 @@ void NMI_UploadBG3Text() { // 808ce4
|
||||
}
|
||||
|
||||
void NMI_UpdateOWScroll() { // 808d13
|
||||
uint8 *src = (uint8 *)uvram.t3.data, *src_org = src;
|
||||
uint8 *src = (uint8 *)uvram.data, *src_org = src;
|
||||
int f = WORD(src[0]);
|
||||
int step = (f & 0x8000) ? 32 : 1;
|
||||
int len = f & 0x3fff;
|
||||
@@ -21,7 +21,7 @@ def replace_in_file(fname):
|
||||
|
||||
|
||||
|
||||
for filename in glob.glob('*.cpp') + glob.glob('*.h'):
|
||||
for filename in glob.glob('*.c') + glob.glob('*.h'):
|
||||
replace_in_file(filename)
|
||||
|
||||
|
||||
|
||||
@@ -225,7 +225,7 @@ static PlayerHandlerFunc *const kOverworldSubmodules[48] = {
|
||||
&Overworld_Func1E,
|
||||
&Overworld_Func1F,
|
||||
&Overworld_LoadOverlays2,
|
||||
&Overworld_LoadAmbientOverlay,
|
||||
&Overworld_LoadAmbientOverlayFalse,
|
||||
&Overworld_Func22,
|
||||
&Module09_MirrorWarp,
|
||||
&Overworld_StartMosaicTransition,
|
||||
@@ -2054,7 +2054,7 @@ void Overworld_LoadAmbientOverlay(bool load_map_data) { // 82ed25
|
||||
INIDISP_copy = 0;
|
||||
}
|
||||
|
||||
void Overworld_LoadAmbientOverlay() { // 82ed24
|
||||
void Overworld_LoadAmbientOverlayFalse() { // 82ed24
|
||||
Overworld_LoadAmbientOverlay(false);
|
||||
}
|
||||
|
||||
@@ -2109,7 +2109,7 @@ void Overworld_HandleOverlaysAndBombDoors() { // 82ef29
|
||||
void TriggerAndFinishMapLoadStripe_Y(int n) { // 82ef7a
|
||||
BYTE(overworld_screen_trans_dir_bits2) = 8;
|
||||
nmi_subroutine_index = 3;
|
||||
uint16 *dst = uvram.t3.data;
|
||||
uint16 *dst = uvram.data;
|
||||
*dst++ = 0x80;
|
||||
do {
|
||||
dst = BufferAndBuildMap16Stripes_Y(dst);
|
||||
@@ -2122,7 +2122,7 @@ void TriggerAndFinishMapLoadStripe_Y(int n) { // 82ef7a
|
||||
void TriggerAndFinishMapLoadStripe_X(int n) { // 82efb3
|
||||
BYTE(overworld_screen_trans_dir_bits2) = 2;
|
||||
nmi_subroutine_index = 3;
|
||||
uint16 *dst = uvram.t3.data;
|
||||
uint16 *dst = uvram.data;
|
||||
*dst++ = 0x8040;
|
||||
do {
|
||||
dst = BufferAndBuildMap16Stripes_X(dst);
|
||||
@@ -2240,7 +2240,7 @@ void CreateInitialOWScreenView_Small_East() { // 82f1b7
|
||||
}
|
||||
|
||||
void OverworldTransitionScrollAndLoadMap() { // 82f20e
|
||||
uint16 *dst = uvram.t3.data;
|
||||
uint16 *dst = uvram.data;
|
||||
switch (BYTE(overworld_screen_trans_dir_bits2)) {
|
||||
case 1: dst = BuildFullStripeDuringTransition_East(dst); break;
|
||||
case 2: dst = BuildFullStripeDuringTransition_West(dst); break;
|
||||
@@ -2251,7 +2251,7 @@ void OverworldTransitionScrollAndLoadMap() { // 82f20e
|
||||
submodule_index = 0;
|
||||
}
|
||||
dst[0] = dst[1] = 0xffff;
|
||||
if (dst != uvram.t3.data)
|
||||
if (dst != uvram.data)
|
||||
nmi_subroutine_index = 3;
|
||||
}
|
||||
|
||||
@@ -2288,7 +2288,7 @@ uint16 *BuildFullStripeDuringTransition_East(uint16 *dst) { // 82f24a
|
||||
}
|
||||
|
||||
void OverworldHandleMapScroll() { // 82f273
|
||||
uint16 *dst = uvram.t3.data;
|
||||
uint16 *dst = uvram.data;
|
||||
switch (BYTE(overworld_screen_trans_dir_bits2)) {
|
||||
case 1:
|
||||
dst = CheckForNewlyLoadedMapAreas_East(dst);
|
||||
@@ -2321,7 +2321,7 @@ void OverworldHandleMapScroll() { // 82f273
|
||||
submodule_index = 0;
|
||||
}
|
||||
dst[0] = dst[1] = 0xffff;
|
||||
if (dst != uvram.t3.data)
|
||||
if (dst != uvram.data)
|
||||
nmi_subroutine_index = 3;
|
||||
overworld_screen_transition = overworld_screen_trans_dir_bits2;
|
||||
}
|
||||
@@ -3313,8 +3313,8 @@ uint16 Overworld_ToolAndTileInteraction(uint16 x, uint16 y) { // 9bbd82
|
||||
scratch_1 = y & ~0xf;
|
||||
index_of_interacting_tile = yv;
|
||||
yv = (attr == 0x72a) ? 0xdc8 : 0xdc7;
|
||||
check_secret:
|
||||
uint32 result;
|
||||
check_secret:
|
||||
result = Overworld_RevealSecret(pos);
|
||||
if (result != 0)
|
||||
yv = result;
|
||||
@@ -96,7 +96,7 @@ void Overworld_LoadBirdTravelPos(int k);
|
||||
void FluteMenu_LoadSelectedScreenPalettes();
|
||||
void FindPartnerWhirlpoolExit();
|
||||
void Overworld_LoadAmbientOverlay(bool load_map_data);
|
||||
void Overworld_LoadAmbientOverlay();
|
||||
void Overworld_LoadAmbientOverlayFalse();
|
||||
void Overworld_LoadAndBuildScreen();
|
||||
void Module08_02_LoadAndAdvance();
|
||||
void Overworld_DrawQuadrantsAndOverlays();
|
||||
|
||||
@@ -2006,8 +2006,8 @@ void Link_HandleAPress() { // 879baa
|
||||
}
|
||||
action = 1;
|
||||
}
|
||||
attempt_action:
|
||||
static const uint8 kAbilityBitmasks[] = { 0xE0, 0x40, 4, 0xE0, 0xE0, 0xE0, 0xE0, 0xE0 };
|
||||
attempt_action:
|
||||
if (!(kAbilityBitmasks[action] & link_ability_flags)) {
|
||||
bitfield_for_a_button = 0;
|
||||
return;
|
||||
@@ -396,10 +396,10 @@ static const uint16 kLinkDmaGraphicsIndices[511] = {
|
||||
203, 72, 113, 99, 26, 295, 102, 30, 105, 203, 107, 10, 10, 109, 109, 13, 13, 112, 114, 110, 203, 297, 299, 300, 301, 302, 63, 16, 16, 79, 79, 294,
|
||||
80, 294, 19, 19, 20, 20, 21, 21, 81, 82, 81, 23, 23, 24, 24, 25, 130, 131, 132, 133, 134, 134, 28, 28, 29, 121, 122, 123, 124, 129, 129,
|
||||
};
|
||||
struct LinkSpriteBody {
|
||||
typedef struct LinkSpriteBody {
|
||||
int8 y, x;
|
||||
uint8 tile;
|
||||
};
|
||||
} LinkSpriteBody;
|
||||
static const LinkSpriteBody kLinkSpriteBodys[303] = {
|
||||
{ 0, 0, 0x00},
|
||||
{ 1, 0, 0x00},
|
||||
@@ -1,10 +1,10 @@
|
||||
#pragma once
|
||||
#include "types.h"
|
||||
|
||||
struct SwordResult {
|
||||
typedef struct SwordResult {
|
||||
uint8 r6;
|
||||
uint8 r12;
|
||||
};
|
||||
} SwordResult;
|
||||
|
||||
bool PlayerOam_WantInvokeSword();
|
||||
void CalculateSwordHitBox();
|
||||
|
||||
@@ -24,9 +24,9 @@ static const int8 kPolySinCos[320] = {
|
||||
45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 56, 57, 58, 59,
|
||||
59, 60, 60, 61, 61, 62, 62, 62, 63, 63, 63, 64, 64, 64, 64, 64,
|
||||
};
|
||||
struct Vertex3 {
|
||||
typedef struct Vertex3 {
|
||||
int8 x, y, z;
|
||||
};
|
||||
} Vertex3;
|
||||
static const Vertex3 kPoly0_Vtx[6] = {
|
||||
{ 0, 65, 0},
|
||||
{ 0, -65, 0},
|
||||
@@ -60,12 +60,12 @@ static const uint8 kPoly1_Polys[28] = {
|
||||
4, 1, 5, 4, 2, 4,
|
||||
4, 3, 0, 2, 4, 3,
|
||||
};
|
||||
struct PolyConfig {
|
||||
typedef struct PolyConfig {
|
||||
uint8 num_vtx, num_poly;
|
||||
uint16 vtx_val, polys_val;
|
||||
const Vertex3 *vertex;
|
||||
const uint8 *poly;
|
||||
};
|
||||
} PolyConfig;
|
||||
static const PolyConfig kPolyConfigs[2] = {
|
||||
{6, 8, 0xff98, 0xffaa, kPoly0_Vtx, kPoly0_Polys},
|
||||
{6, 5, 0xffd2, 0xffe4, kPoly1_Vtx, kPoly1_Polys},
|
||||
@@ -31,8 +31,6 @@ void apu_free(Apu* apu) {
|
||||
}
|
||||
|
||||
void apu_saveload(Apu *apu, SaveLoadFunc *func, void *ctx) {
|
||||
size_t size = sizeof(struct Apu2);
|
||||
size_t size2 = offsetof(Apu, hist);
|
||||
func(ctx, apu->ram, offsetof(Apu, hist) - offsetof(Apu, ram));
|
||||
dsp_saveload(apu->dsp, func, ctx);
|
||||
spc_saveload(apu->spc, func, ctx);
|
||||
@@ -157,7 +155,7 @@ void apu_cpuWrite(Apu* apu, uint16_t adr, uint8_t val) {
|
||||
int i = apu->hist.count;
|
||||
if (i != 256) {
|
||||
apu->hist.count = i + 1;
|
||||
apu->hist.addr[i] = (DspReg)apu->dspAdr;
|
||||
apu->hist.addr[i] = apu->dspAdr;
|
||||
apu->hist.val[i] = val;
|
||||
}
|
||||
if(apu->dspAdr < 0x80) dsp_write(apu->dsp, apu->dspAdr, val);
|
||||
@@ -39,7 +39,7 @@ struct Apu {
|
||||
};
|
||||
};
|
||||
|
||||
struct Apu2 {
|
||||
typedef struct Apu2 {
|
||||
// Snes* snes;
|
||||
Spc* spc;
|
||||
Dsp* dsp;
|
||||
@@ -51,7 +51,7 @@ struct Apu2 {
|
||||
uint8_t outPorts[4];
|
||||
Timer timer[3];
|
||||
uint8_t cpuCyclesLeft;
|
||||
};
|
||||
} Apu2;
|
||||
|
||||
Apu* apu_init();
|
||||
void apu_free(Apu* apu);
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#include "dsp_regs.h"
|
||||
typedef struct Dsp Dsp;
|
||||
|
||||
#include "saveload.h"
|
||||
@@ -81,11 +82,9 @@ struct Dsp {
|
||||
};
|
||||
|
||||
|
||||
#include "../dsp_regs.h"
|
||||
|
||||
typedef struct DspRegWriteHistory {
|
||||
uint32_t count;
|
||||
enum DspReg addr[256];
|
||||
uint8_t addr[256];
|
||||
uint8_t val[256];
|
||||
} DspRegWriteHistory;
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#pragma once
|
||||
#ifndef DSP_REGS_H
|
||||
#define DSP_REGS_H
|
||||
|
||||
enum DspReg {
|
||||
V0VOLL = 0,
|
||||
@@ -106,3 +107,4 @@ enum DspReg {
|
||||
EDL = 0x7D,
|
||||
FIR7 = 0x7F,
|
||||
};
|
||||
#endif // DSP_REGS_H
|
||||
@@ -3,21 +3,157 @@
|
||||
#include <stdlib.h>
|
||||
#include <SDL.h>
|
||||
#include <assert.h>
|
||||
#include <algorithm>
|
||||
#include "types.h"
|
||||
|
||||
#include "snes/spc.h"
|
||||
#include "snes/dsp_regs.h"
|
||||
#include "tracing.h"
|
||||
|
||||
#include "spc_player.h"
|
||||
#include "dsp_regs.h"
|
||||
|
||||
typedef struct MemMap {
|
||||
uint16 off, org_off;
|
||||
} MemMap;
|
||||
|
||||
typedef struct MemMapSized {
|
||||
uint16 off, org_off, size;
|
||||
} MemMapSized;
|
||||
|
||||
static const MemMap kChannel_Maps[] = {
|
||||
{offsetof(Channel, pattern_order_ptr_for_chan), 0x8030},
|
||||
{offsetof(Channel, note_ticks_left), 0x70},
|
||||
{offsetof(Channel, note_keyoff_ticks_left), 0x71},
|
||||
{offsetof(Channel, subroutine_num_loops), 0x80},
|
||||
{offsetof(Channel, volume_fade_ticks), 0x90},
|
||||
{offsetof(Channel, pan_num_ticks), 0x91},
|
||||
{offsetof(Channel, pitch_slide_length), 0xa0},
|
||||
{offsetof(Channel, pitch_slide_delay_left), 0xa1},
|
||||
{offsetof(Channel, vibrato_hold_count), 0xb0},
|
||||
{offsetof(Channel, vib_depth), 0xb1},
|
||||
{offsetof(Channel, tremolo_hold_count), 0xc0},
|
||||
{offsetof(Channel, tremolo_depth), 0xc1},
|
||||
{offsetof(Channel, vibrato_change_count), 0x100},
|
||||
{offsetof(Channel, note_length), 0x200},
|
||||
{offsetof(Channel, note_gate_off_fixedpt), 0x201},
|
||||
{offsetof(Channel, channel_volume_master), 0x210},
|
||||
{offsetof(Channel, instrument_id), 0x211},
|
||||
{offsetof(Channel, instrument_pitch_base), 0x8220},
|
||||
{offsetof(Channel, saved_pattern_ptr), 0x8230},
|
||||
{offsetof(Channel, pattern_start_ptr), 0x8240},
|
||||
{offsetof(Channel, pitch_envelope_num_ticks), 0x280},
|
||||
{offsetof(Channel, pitch_envelope_delay), 0x281},
|
||||
{offsetof(Channel, pitch_envelope_direction), 0x290},
|
||||
{offsetof(Channel, pitch_envelope_slide_value), 0x291},
|
||||
{offsetof(Channel, vibrato_count), 0x2a0},
|
||||
{offsetof(Channel, vibrato_rate), 0x2a1},
|
||||
{offsetof(Channel, vibrato_delay_ticks), 0x2b0},
|
||||
{offsetof(Channel, vibrato_fade_num_ticks), 0x2b1},
|
||||
{offsetof(Channel, vibrato_fade_add_per_tick), 0x2c0},
|
||||
{offsetof(Channel, vibrato_depth_target), 0x2c1},
|
||||
{offsetof(Channel, tremolo_count), 0x2d0},
|
||||
{offsetof(Channel, tremolo_rate), 0x2d1},
|
||||
{offsetof(Channel, tremolo_delay_ticks), 0x2e0},
|
||||
{offsetof(Channel, channel_transposition), 0x2f0},
|
||||
{offsetof(Channel, channel_volume), 0x8300},
|
||||
{offsetof(Channel, volume_fade_addpertick), 0x8310},
|
||||
{offsetof(Channel, volume_fade_target), 0x320},
|
||||
{offsetof(Channel, final_volume), 0x321},
|
||||
{offsetof(Channel, pan_value), 0x8330},
|
||||
{offsetof(Channel, pan_add_per_tick), 0x8340},
|
||||
{offsetof(Channel, pan_target_value), 0x350},
|
||||
{offsetof(Channel, pan_flag_with_phase_invert), 0x351},
|
||||
{offsetof(Channel, pitch), 0x8360},
|
||||
{offsetof(Channel, pitch_add_per_tick), 0x8370},
|
||||
{offsetof(Channel, pitch_target), 0x380},
|
||||
{offsetof(Channel, fine_tune), 0x381},
|
||||
{offsetof(Channel, sfx_sound_ptr), 0x8390},
|
||||
{offsetof(Channel, sfx_which_sound), 0x3a0},
|
||||
{offsetof(Channel, sfx_arr_countdown), 0x3a1},
|
||||
{offsetof(Channel, sfx_note_length_left), 0x3b0},
|
||||
{offsetof(Channel, sfx_note_length), 0x3b1},
|
||||
{offsetof(Channel, sfx_pan), 0x3d0},
|
||||
};
|
||||
|
||||
static const MemMapSized kSpcPlayer_Maps[] = {
|
||||
{offsetof(SpcPlayer, new_value_from_snes), 0x0, 4},
|
||||
{offsetof(SpcPlayer, port_to_snes), 0x4, 4},
|
||||
{offsetof(SpcPlayer, last_value_from_snes), 0x8, 4},
|
||||
{offsetof(SpcPlayer, counter_sf0c), 0xc, 1},
|
||||
{offsetof(SpcPlayer, _always_zero), 0xe, 2},
|
||||
{offsetof(SpcPlayer, temp_accum), 0x10, 2},
|
||||
{offsetof(SpcPlayer, ttt), 0x12, 1},
|
||||
{offsetof(SpcPlayer, did_affect_volumepitch_flag), 0x13, 1},
|
||||
{offsetof(SpcPlayer, addr0), 0x14, 2},
|
||||
{offsetof(SpcPlayer, addr1), 0x16, 2},
|
||||
{offsetof(SpcPlayer, lfsr_value), 0x18, 2},
|
||||
{offsetof(SpcPlayer, is_chan_on), 0x1a, 1},
|
||||
{offsetof(SpcPlayer, fast_forward), 0x1b, 1},
|
||||
{offsetof(SpcPlayer, sfx_start_arg_pan), 0x20, 1},
|
||||
{offsetof(SpcPlayer, sfx_sound_ptr_cur), 0x2c, 2},
|
||||
{offsetof(SpcPlayer, music_ptr_toplevel), 0x40, 2},
|
||||
{offsetof(SpcPlayer, block_count), 0x42, 1},
|
||||
{offsetof(SpcPlayer, sfx_timer_accum), 0x43, 1},
|
||||
{offsetof(SpcPlayer, chn), 0x44, 1},
|
||||
{offsetof(SpcPlayer, key_ON), 0x45, 1},
|
||||
{offsetof(SpcPlayer, key_OFF), 0x46, 1},
|
||||
{offsetof(SpcPlayer, cur_chan_bit), 0x47, 1},
|
||||
{offsetof(SpcPlayer, reg_FLG), 0x48, 1},
|
||||
{offsetof(SpcPlayer, reg_NON), 0x49, 1},
|
||||
{offsetof(SpcPlayer, reg_EON), 0x4a, 1},
|
||||
{offsetof(SpcPlayer, reg_PMON), 0x4b, 1},
|
||||
{offsetof(SpcPlayer, echo_stored_time), 0x4c, 1},
|
||||
{offsetof(SpcPlayer, echo_parameter_EDL), 0x4d, 1},
|
||||
{offsetof(SpcPlayer, reg_EFB), 0x4e, 1},
|
||||
{offsetof(SpcPlayer, global_transposition), 0x50, 1},
|
||||
{offsetof(SpcPlayer, main_tempo_accum), 0x51, 1},
|
||||
{offsetof(SpcPlayer, tempo), 0x52, 2},
|
||||
{offsetof(SpcPlayer, tempo_fade_num_ticks), 0x54, 1},
|
||||
{offsetof(SpcPlayer, tempo_fade_final), 0x55, 1},
|
||||
{offsetof(SpcPlayer, tempo_fade_add), 0x56, 2},
|
||||
{offsetof(SpcPlayer, master_volume), 0x58, 2},
|
||||
{offsetof(SpcPlayer, master_volume_fade_ticks), 0x5a, 1},
|
||||
{offsetof(SpcPlayer, master_volume_fade_target), 0x5b, 1},
|
||||
{offsetof(SpcPlayer, master_volume_fade_add_per_tick), 0x5c, 2},
|
||||
{offsetof(SpcPlayer, vol_dirty), 0x5e, 1},
|
||||
{offsetof(SpcPlayer, percussion_base_id), 0x5f, 1},
|
||||
{offsetof(SpcPlayer, echo_volume_left), 0x60, 2},
|
||||
{offsetof(SpcPlayer, echo_volume_right), 0x62, 2},
|
||||
{offsetof(SpcPlayer, echo_volume_fade_add_left), 0x64, 2},
|
||||
{offsetof(SpcPlayer, echo_volume_fade_add_right), 0x66, 2},
|
||||
{offsetof(SpcPlayer, echo_volume_fade_ticks), 0x68, 1},
|
||||
{offsetof(SpcPlayer, echo_volume_fade_target_left), 0x69, 1},
|
||||
{offsetof(SpcPlayer, echo_volume_fade_target_right), 0x6a, 1},
|
||||
{offsetof(SpcPlayer, sfx_channel_index), 0x3c0, 1},
|
||||
{offsetof(SpcPlayer, current_bit), 0x3c1, 1},
|
||||
{offsetof(SpcPlayer, dsp_register_index), 0x3c2, 1},
|
||||
{offsetof(SpcPlayer, echo_channels), 0x3c3, 1},
|
||||
{offsetof(SpcPlayer, byte_3C4), 0x3c4, 1},
|
||||
{offsetof(SpcPlayer, byte_3C5), 0x3c5, 1},
|
||||
{offsetof(SpcPlayer, echo_fract_incr), 0x3c7, 1},
|
||||
{offsetof(SpcPlayer, sfx_channel_index2), 0x3c8, 1},
|
||||
{offsetof(SpcPlayer, sfx_channel_bit), 0x3c9, 1},
|
||||
{offsetof(SpcPlayer, pause_music_ctr), 0x3ca, 1},
|
||||
{offsetof(SpcPlayer, port2_active), 0x3cb, 1},
|
||||
{offsetof(SpcPlayer, port2_current_bit), 0x3cc, 1},
|
||||
{offsetof(SpcPlayer, port3_active), 0x3cd, 1},
|
||||
{offsetof(SpcPlayer, port3_current_bit), 0x3ce, 1},
|
||||
{offsetof(SpcPlayer, port1_active), 0x3cf, 1},
|
||||
{offsetof(SpcPlayer, port1_current_bit), 0x3e0, 1},
|
||||
{offsetof(SpcPlayer, byte_3E1), 0x3e1, 1},
|
||||
{offsetof(SpcPlayer, sfx_play_echo_flag), 0x3e2, 1},
|
||||
{offsetof(SpcPlayer, sfx_channels_echo_mask2), 0x3e3, 1},
|
||||
{offsetof(SpcPlayer, port1_counter), 0x3e4, 1},
|
||||
{offsetof(SpcPlayer, channel_67_volume), 0x3e5, 1},
|
||||
{offsetof(SpcPlayer, cutk_always_zero), 0x3ff, 1},
|
||||
};
|
||||
|
||||
static void PlayNote(SpcPlayer *p, Channel *c, uint8 note);
|
||||
|
||||
static void Dsp_Write(SpcPlayer *p, uint8_t reg, uint8 value) {
|
||||
if (DspRegWriteHistory *hist = p->reg_write_history) {
|
||||
DspRegWriteHistory *hist = p->reg_write_history;
|
||||
if (hist) {
|
||||
if (hist->count < 256) {
|
||||
hist->addr[hist->count] = (DspReg)reg;
|
||||
hist->addr[hist->count] = reg;
|
||||
hist->val[hist->count] = value;
|
||||
hist->count++;
|
||||
}
|
||||
@@ -85,7 +221,7 @@ static void WriteVolumeToDsp(SpcPlayer *p, Channel *c, uint16 volume) {
|
||||
t = t * c->final_volume >> 8;
|
||||
if ((c->pan_flag_with_phase_invert << i) & 0x80)
|
||||
t = -t;
|
||||
Dsp_Write(p, (DspReg)(V0VOLL + i + c->index * 16), t);
|
||||
Dsp_Write(p, V0VOLL + i + c->index * 16, t);
|
||||
volume = 0x1400 - volume;
|
||||
}
|
||||
}
|
||||
@@ -108,8 +244,8 @@ static void WritePitch(SpcPlayer *p, Channel *c, uint16 pitch) {
|
||||
t = c->instrument_pitch_base * t >> 8;
|
||||
if (!(p->cur_chan_bit & p->is_chan_on)) {
|
||||
uint8 reg = c->index * 16;
|
||||
Dsp_Write(p, (DspReg)(reg + V0PITCHL), t & 0xff);
|
||||
Dsp_Write(p, (DspReg)(reg + V0PITCHH), t >> 8);
|
||||
Dsp_Write(p, reg + V0PITCHL, t & 0xff);
|
||||
Dsp_Write(p, reg + V0PITCHH, t >> 8);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -149,7 +285,7 @@ static void Channel_SetInstrument(SpcPlayer *p, Channel *c, uint8 instrument) {
|
||||
// noise
|
||||
p->reg_FLG = (p->reg_FLG & 0x20) | ip[0] & 0x1f;
|
||||
p->reg_NON |= p->cur_chan_bit;
|
||||
Dsp_Write(p, (DspReg)(reg + V0SRCN), 0);
|
||||
Dsp_Write(p, reg + V0SRCN, 0);
|
||||
} else {
|
||||
Dsp_Write(p, reg + V0SRCN, ip[0]);
|
||||
}
|
||||
@@ -648,8 +784,8 @@ static void PlayNote(SpcPlayer *p, Channel *c, uint8 note) {
|
||||
return;
|
||||
|
||||
static const char *const kNoteNames[] = { "C-", "C#", "D-", "D#", "E-", "F-", "F#", "G-", "G#", "A-", "A#", "B-" };
|
||||
if (c->index==0)
|
||||
printf("%s%d\n", kNoteNames[(note & 0x7f) % 12], (note & 0x7f) / 12 + 1);
|
||||
//if (c->index==0)
|
||||
// printf("%s%d\n", kNoteNames[(note & 0x7f) % 12], (note & 0x7f) / 12 + 1);
|
||||
|
||||
c->pitch = ((note & 0x7f) + p->global_transposition + c->channel_transposition) << 8 | c->fine_tune;
|
||||
c->vibrato_count = c->vibrato_fade_num_ticks << 7;
|
||||
@@ -1072,7 +1208,7 @@ void SpcPlayer_CopyVariablesToRam(SpcPlayer *p) {
|
||||
for (const MemMap *m = &kChannel_Maps[0]; m != &kChannel_Maps[countof(kChannel_Maps)]; m++)
|
||||
memcpy(&p->ram[(m->org_off & 0x7fff) + i * 2], (uint8 *)c + m->off, m->org_off & 0x8000 ? 2 : 1);
|
||||
}
|
||||
for (const MemMap2 *m = &kSpcPlayer_Maps[0]; m != &kSpcPlayer_Maps[countof(kSpcPlayer_Maps)]; m++)
|
||||
for (const MemMapSized *m = &kSpcPlayer_Maps[0]; m != &kSpcPlayer_Maps[countof(kSpcPlayer_Maps)]; m++)
|
||||
memcpy(&p->ram[m->org_off], (uint8 *)p + m->off, m->size);
|
||||
}
|
||||
|
||||
@@ -1082,7 +1218,7 @@ void SpcPlayer_CopyVariablesFromRam(SpcPlayer *p) {
|
||||
for (const MemMap *m = &kChannel_Maps[0]; m != &kChannel_Maps[countof(kChannel_Maps)]; m++)
|
||||
memcpy((uint8 *)c + m->off, &p->ram[(m->org_off & 0x7fff) + i * 2], m->org_off & 0x8000 ? 2 : 1);
|
||||
}
|
||||
for (const MemMap2 *m = &kSpcPlayer_Maps[0]; m != &kSpcPlayer_Maps[countof(kSpcPlayer_Maps)]; m++)
|
||||
for (const MemMapSized *m = &kSpcPlayer_Maps[0]; m != &kSpcPlayer_Maps[countof(kSpcPlayer_Maps)]; m++)
|
||||
memcpy((uint8 *)p + m->off, &p->ram[m->org_off], m->size);
|
||||
}
|
||||
|
||||
@@ -1140,8 +1276,8 @@ void SpcPlayer_Upload(SpcPlayer *p, const uint8_t *data) {
|
||||
|
||||
// =======================================
|
||||
|
||||
DspRegWriteHistory my_write_hist;
|
||||
SpcPlayer my_spc, my_spc_snapshot;
|
||||
static DspRegWriteHistory my_write_hist;
|
||||
static SpcPlayer my_spc, my_spc_snapshot;
|
||||
static int loop_ctr;
|
||||
|
||||
bool CompareSpcImpls(SpcPlayer *p, SpcPlayer *p_org, Apu *apu) {
|
||||
144
spc_player.h
144
spc_player.h
@@ -1,8 +1,7 @@
|
||||
#include <stddef.h>
|
||||
struct DspRegWriteHistory;
|
||||
struct Dsp;
|
||||
#include "snes/dsp.h"
|
||||
|
||||
struct Channel {
|
||||
typedef struct Channel {
|
||||
uint16 pattern_order_ptr_for_chan;
|
||||
uint8 note_ticks_left;
|
||||
uint8 note_keyoff_ticks_left;
|
||||
@@ -56,8 +55,9 @@ struct Channel {
|
||||
uint8 sfx_note_length;
|
||||
uint8 sfx_pan;
|
||||
uint8 index;
|
||||
};
|
||||
struct SpcPlayer {
|
||||
} Channel;
|
||||
|
||||
typedef struct SpcPlayer {
|
||||
DspRegWriteHistory *reg_write_history;
|
||||
uint8 timer_cycles;
|
||||
Dsp *dsp;
|
||||
@@ -135,139 +135,7 @@ struct SpcPlayer {
|
||||
uint8 input_ports[4];
|
||||
Channel channel[8];
|
||||
uint8 ram[65536]; // rest of ram
|
||||
};
|
||||
struct MemMap {
|
||||
uint16 off, org_off;
|
||||
};
|
||||
struct MemMap2 {
|
||||
uint16 off, org_off, size;
|
||||
};
|
||||
const MemMap kChannel_Maps[] = {
|
||||
{offsetof(Channel, pattern_order_ptr_for_chan), 0x8030},
|
||||
{offsetof(Channel, note_ticks_left), 0x70},
|
||||
{offsetof(Channel, note_keyoff_ticks_left), 0x71},
|
||||
{offsetof(Channel, subroutine_num_loops), 0x80},
|
||||
{offsetof(Channel, volume_fade_ticks), 0x90},
|
||||
{offsetof(Channel, pan_num_ticks), 0x91},
|
||||
{offsetof(Channel, pitch_slide_length), 0xa0},
|
||||
{offsetof(Channel, pitch_slide_delay_left), 0xa1},
|
||||
{offsetof(Channel, vibrato_hold_count), 0xb0},
|
||||
{offsetof(Channel, vib_depth), 0xb1},
|
||||
{offsetof(Channel, tremolo_hold_count), 0xc0},
|
||||
{offsetof(Channel, tremolo_depth), 0xc1},
|
||||
{offsetof(Channel, vibrato_change_count), 0x100},
|
||||
{offsetof(Channel, note_length), 0x200},
|
||||
{offsetof(Channel, note_gate_off_fixedpt), 0x201},
|
||||
{offsetof(Channel, channel_volume_master), 0x210},
|
||||
{offsetof(Channel, instrument_id), 0x211},
|
||||
{offsetof(Channel, instrument_pitch_base), 0x8220},
|
||||
{offsetof(Channel, saved_pattern_ptr), 0x8230},
|
||||
{offsetof(Channel, pattern_start_ptr), 0x8240},
|
||||
{offsetof(Channel, pitch_envelope_num_ticks), 0x280},
|
||||
{offsetof(Channel, pitch_envelope_delay), 0x281},
|
||||
{offsetof(Channel, pitch_envelope_direction), 0x290},
|
||||
{offsetof(Channel, pitch_envelope_slide_value), 0x291},
|
||||
{offsetof(Channel, vibrato_count), 0x2a0},
|
||||
{offsetof(Channel, vibrato_rate), 0x2a1},
|
||||
{offsetof(Channel, vibrato_delay_ticks), 0x2b0},
|
||||
{offsetof(Channel, vibrato_fade_num_ticks), 0x2b1},
|
||||
{offsetof(Channel, vibrato_fade_add_per_tick), 0x2c0},
|
||||
{offsetof(Channel, vibrato_depth_target), 0x2c1},
|
||||
{offsetof(Channel, tremolo_count), 0x2d0},
|
||||
{offsetof(Channel, tremolo_rate), 0x2d1},
|
||||
{offsetof(Channel, tremolo_delay_ticks), 0x2e0},
|
||||
{offsetof(Channel, channel_transposition), 0x2f0},
|
||||
{offsetof(Channel, channel_volume), 0x8300},
|
||||
{offsetof(Channel, volume_fade_addpertick), 0x8310},
|
||||
{offsetof(Channel, volume_fade_target), 0x320},
|
||||
{offsetof(Channel, final_volume), 0x321},
|
||||
{offsetof(Channel, pan_value), 0x8330},
|
||||
{offsetof(Channel, pan_add_per_tick), 0x8340},
|
||||
{offsetof(Channel, pan_target_value), 0x350},
|
||||
{offsetof(Channel, pan_flag_with_phase_invert), 0x351},
|
||||
{offsetof(Channel, pitch), 0x8360},
|
||||
{offsetof(Channel, pitch_add_per_tick), 0x8370},
|
||||
{offsetof(Channel, pitch_target), 0x380},
|
||||
{offsetof(Channel, fine_tune), 0x381},
|
||||
{offsetof(Channel, sfx_sound_ptr), 0x8390},
|
||||
{offsetof(Channel, sfx_which_sound), 0x3a0},
|
||||
{offsetof(Channel, sfx_arr_countdown), 0x3a1},
|
||||
{offsetof(Channel, sfx_note_length_left), 0x3b0},
|
||||
{offsetof(Channel, sfx_note_length), 0x3b1},
|
||||
{offsetof(Channel, sfx_pan), 0x3d0},
|
||||
};
|
||||
const MemMap2 kSpcPlayer_Maps[] = {
|
||||
{offsetof(SpcPlayer, new_value_from_snes), 0x0, 4},
|
||||
{offsetof(SpcPlayer, port_to_snes), 0x4, 4},
|
||||
{offsetof(SpcPlayer, last_value_from_snes), 0x8, 4},
|
||||
{offsetof(SpcPlayer, counter_sf0c), 0xc, 1},
|
||||
{offsetof(SpcPlayer, _always_zero), 0xe, 2},
|
||||
{offsetof(SpcPlayer, temp_accum), 0x10, 2},
|
||||
{offsetof(SpcPlayer, ttt), 0x12, 1},
|
||||
{offsetof(SpcPlayer, did_affect_volumepitch_flag), 0x13, 1},
|
||||
{offsetof(SpcPlayer, addr0), 0x14, 2},
|
||||
{offsetof(SpcPlayer, addr1), 0x16, 2},
|
||||
{offsetof(SpcPlayer, lfsr_value), 0x18, 2},
|
||||
{offsetof(SpcPlayer, is_chan_on), 0x1a, 1},
|
||||
{offsetof(SpcPlayer, fast_forward), 0x1b, 1},
|
||||
{offsetof(SpcPlayer, sfx_start_arg_pan), 0x20, 1},
|
||||
{offsetof(SpcPlayer, sfx_sound_ptr_cur), 0x2c, 2},
|
||||
{offsetof(SpcPlayer, music_ptr_toplevel), 0x40, 2},
|
||||
{offsetof(SpcPlayer, block_count), 0x42, 1},
|
||||
{offsetof(SpcPlayer, sfx_timer_accum), 0x43, 1},
|
||||
{offsetof(SpcPlayer, chn), 0x44, 1},
|
||||
{offsetof(SpcPlayer, key_ON), 0x45, 1},
|
||||
{offsetof(SpcPlayer, key_OFF), 0x46, 1},
|
||||
{offsetof(SpcPlayer, cur_chan_bit), 0x47, 1},
|
||||
{offsetof(SpcPlayer, reg_FLG), 0x48, 1},
|
||||
{offsetof(SpcPlayer, reg_NON), 0x49, 1},
|
||||
{offsetof(SpcPlayer, reg_EON), 0x4a, 1},
|
||||
{offsetof(SpcPlayer, reg_PMON), 0x4b, 1},
|
||||
{offsetof(SpcPlayer, echo_stored_time), 0x4c, 1},
|
||||
{offsetof(SpcPlayer, echo_parameter_EDL), 0x4d, 1},
|
||||
{offsetof(SpcPlayer, reg_EFB), 0x4e, 1},
|
||||
{offsetof(SpcPlayer, global_transposition), 0x50, 1},
|
||||
{offsetof(SpcPlayer, main_tempo_accum), 0x51, 1},
|
||||
{offsetof(SpcPlayer, tempo), 0x52, 2},
|
||||
{offsetof(SpcPlayer, tempo_fade_num_ticks), 0x54, 1},
|
||||
{offsetof(SpcPlayer, tempo_fade_final), 0x55, 1},
|
||||
{offsetof(SpcPlayer, tempo_fade_add), 0x56, 2},
|
||||
{offsetof(SpcPlayer, master_volume), 0x58, 2},
|
||||
{offsetof(SpcPlayer, master_volume_fade_ticks), 0x5a, 1},
|
||||
{offsetof(SpcPlayer, master_volume_fade_target), 0x5b, 1},
|
||||
{offsetof(SpcPlayer, master_volume_fade_add_per_tick), 0x5c, 2},
|
||||
{offsetof(SpcPlayer, vol_dirty), 0x5e, 1},
|
||||
{offsetof(SpcPlayer, percussion_base_id), 0x5f, 1},
|
||||
{offsetof(SpcPlayer, echo_volume_left), 0x60, 2},
|
||||
{offsetof(SpcPlayer, echo_volume_right), 0x62, 2},
|
||||
{offsetof(SpcPlayer, echo_volume_fade_add_left), 0x64, 2},
|
||||
{offsetof(SpcPlayer, echo_volume_fade_add_right), 0x66, 2},
|
||||
{offsetof(SpcPlayer, echo_volume_fade_ticks), 0x68, 1},
|
||||
{offsetof(SpcPlayer, echo_volume_fade_target_left), 0x69, 1},
|
||||
{offsetof(SpcPlayer, echo_volume_fade_target_right), 0x6a, 1},
|
||||
{offsetof(SpcPlayer, sfx_channel_index), 0x3c0, 1},
|
||||
{offsetof(SpcPlayer, current_bit), 0x3c1, 1},
|
||||
{offsetof(SpcPlayer, dsp_register_index), 0x3c2, 1},
|
||||
{offsetof(SpcPlayer, echo_channels), 0x3c3, 1},
|
||||
{offsetof(SpcPlayer, byte_3C4), 0x3c4, 1},
|
||||
{offsetof(SpcPlayer, byte_3C5), 0x3c5, 1},
|
||||
{offsetof(SpcPlayer, echo_fract_incr), 0x3c7, 1},
|
||||
{offsetof(SpcPlayer, sfx_channel_index2), 0x3c8, 1},
|
||||
{offsetof(SpcPlayer, sfx_channel_bit), 0x3c9, 1},
|
||||
{offsetof(SpcPlayer, pause_music_ctr), 0x3ca, 1},
|
||||
{offsetof(SpcPlayer, port2_active), 0x3cb, 1},
|
||||
{offsetof(SpcPlayer, port2_current_bit), 0x3cc, 1},
|
||||
{offsetof(SpcPlayer, port3_active), 0x3cd, 1},
|
||||
{offsetof(SpcPlayer, port3_current_bit), 0x3ce, 1},
|
||||
{offsetof(SpcPlayer, port1_active), 0x3cf, 1},
|
||||
{offsetof(SpcPlayer, port1_current_bit), 0x3e0, 1},
|
||||
{offsetof(SpcPlayer, byte_3E1), 0x3e1, 1},
|
||||
{offsetof(SpcPlayer, sfx_play_echo_flag), 0x3e2, 1},
|
||||
{offsetof(SpcPlayer, sfx_channels_echo_mask2), 0x3e3, 1},
|
||||
{offsetof(SpcPlayer, port1_counter), 0x3e4, 1},
|
||||
{offsetof(SpcPlayer, channel_67_volume), 0x3e5, 1},
|
||||
{offsetof(SpcPlayer, cutk_always_zero), 0x3ff, 1},
|
||||
};
|
||||
} SpcPlayer;
|
||||
|
||||
SpcPlayer *SpcPlayer_Create();
|
||||
void SpcPlayer_GenerateSamples(SpcPlayer *p);
|
||||
|
||||
@@ -2366,8 +2366,9 @@ void AgahnimBalls_DamageAgahnim(int k, uint8 dmg, uint8 r0_hit_timer) { // 86ed
|
||||
sprite_flags5[k] & 0x10 ? 0x1c : 8;
|
||||
sound_effect_2 = sfx | Sprite_CalculateSfxPan(k);
|
||||
}
|
||||
uint8 type;
|
||||
flag4:
|
||||
uint8 type = sprite_type[k];
|
||||
type = sprite_type[k];
|
||||
sprite_F[k] = (damage_type_determiner >= 13) ? 0 :
|
||||
(type == 9) ? 20 :
|
||||
(type == 0x53 || type == 0x18) ? 11 : 15;
|
||||
18
sprite.h
18
sprite.h
@@ -3,13 +3,13 @@
|
||||
#include "variables.h"
|
||||
|
||||
|
||||
struct PrepOamCoordsRet {
|
||||
typedef struct PrepOamCoordsRet {
|
||||
uint16 x, y;
|
||||
uint8 r4;
|
||||
uint8 flags;
|
||||
};
|
||||
} PrepOamCoordsRet;
|
||||
|
||||
struct SpriteHitBox {
|
||||
typedef struct SpriteHitBox {
|
||||
uint8 r0_xlo;
|
||||
uint8 r8_xhi;
|
||||
uint8 r1_ylo;
|
||||
@@ -23,25 +23,23 @@ struct SpriteHitBox {
|
||||
uint8 r11_spr_yhi;
|
||||
uint8 r6_spr_xsize;
|
||||
uint8 r7_spr_ysize;
|
||||
};
|
||||
} SpriteHitBox;
|
||||
|
||||
struct SpriteSpawnInfo {
|
||||
typedef struct SpriteSpawnInfo {
|
||||
uint16 r0_x;
|
||||
uint16 r2_y;
|
||||
uint8 r4_z;
|
||||
uint16 r5_overlord_x;
|
||||
uint16 r7_overlord_y;
|
||||
};
|
||||
} SpriteSpawnInfo;
|
||||
|
||||
extern const uint8 kAbsorbBigKey[2];
|
||||
|
||||
|
||||
|
||||
struct DrawMultipleData {
|
||||
typedef struct DrawMultipleData {
|
||||
int8 x, y;
|
||||
uint16 char_flags;
|
||||
uint8 ext;
|
||||
};
|
||||
} DrawMultipleData;
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -13108,9 +13108,9 @@ void FortuneTeller_PerformPseudoScience(int k) { // 8dc849
|
||||
if (link_sword_type < 4) ADD_MSG(13);
|
||||
ADD_MSG(14);
|
||||
ADD_MSG(15);
|
||||
int j;
|
||||
done:
|
||||
|
||||
int j = ((sram_progress_flags ^= 0x40) & 0x40) != 0;
|
||||
j = ((sram_progress_flags ^= 0x40) & 0x40) != 0;
|
||||
Sprite_ShowMessageUnconditional(kFortuneTeller_Readings[slots[j]]);
|
||||
}
|
||||
|
||||
@@ -21258,8 +21258,8 @@ void Sprite_9A_Kyameron(int k) { // 9e9e7b
|
||||
sprite_ai_state[k] = 4;
|
||||
sprite_delay_main[k] = 15;
|
||||
SpriteSfx_QueueSfx2WithPan(k, 0x28);
|
||||
skip_sound:
|
||||
static const uint8 kKyameron_Moving_Gfx[4] = {3, 2, 1, 0};
|
||||
skip_sound:
|
||||
sprite_graphics[k] = kKyameron_Moving_Gfx[++sprite_subtype2[k] >> 3 & 3];
|
||||
if (!((k ^ frame_counter) & 7)) {
|
||||
uint16 x = (GetRandomNumber() & 0xf) - 4;
|
||||
@@ -24935,6 +24935,7 @@ void OldMan_EnableCutscene() { // 9ee989
|
||||
}
|
||||
|
||||
void Sprite_AD_OldMan(int k) { // 9ee992
|
||||
static const uint16 kOldMountainManMsgs[3] = {0x9e, 0x9f, 0xa0};
|
||||
int j;
|
||||
OldMountainMan_Draw(k);
|
||||
if (Sprite_ReturnIfInactive(k))
|
||||
@@ -25006,7 +25007,6 @@ void Sprite_AD_OldMan(int k) { // 9ee992
|
||||
}
|
||||
break;
|
||||
case 2: // sitting at home
|
||||
static const uint16 kOldMountainManMsgs[3] = {0x9e, 0x9f, 0xa0};
|
||||
Sprite_BehaveAsBarrier(k);
|
||||
if (sprite_ai_state[k]) {
|
||||
link_hearts_filler = 160;
|
||||
@@ -61,7 +61,7 @@ def print_dialogue():
|
||||
print_int_array('kDialogueText', new_r, 'uint8', True, 16, file = f)
|
||||
f.close()
|
||||
|
||||
ROM = util.LoadedRom()
|
||||
ROM = util.LoadedRom(sys.argv[1])
|
||||
|
||||
|
||||
kCompSpritePtrs = [
|
||||
|
||||
@@ -463,8 +463,8 @@ def extract_sound_data(rom):
|
||||
print_song('ending', open('sound_ending.txt', 'w'))
|
||||
|
||||
if __name__ == "__main__":
|
||||
ROM = util.LoadedRom()
|
||||
song = sys.argv[1]
|
||||
ROM = util.LoadedRom(sys.argv[1])
|
||||
song = sys.argv[2]
|
||||
|
||||
load_song(ROM, song)
|
||||
print_song(song, sys.stdout)
|
||||
|
||||
@@ -459,11 +459,11 @@ def extract_sound_data(rom):
|
||||
print_all_sfx(open('sfx.txt', 'w'))
|
||||
|
||||
if __name__ == "__main__":
|
||||
if len(sys.argv) < 2:
|
||||
print('extract_music.py [intro|lightworld|indoor|ending]')
|
||||
if len(sys.argv) < 3:
|
||||
print('extract_music.py [rom-filename] [intro|lightworld|indoor|ending]')
|
||||
sys.exit(0)
|
||||
ROM = util.LoadedRom()
|
||||
song = sys.argv[1]
|
||||
ROM = util.LoadedRom(None if sys.argv[1] == '' else sys.argv[1])
|
||||
song = sys.argv[2]
|
||||
|
||||
load_song(ROM, song)
|
||||
print_song(song, sys.stdout)
|
||||
|
||||
@@ -7,22 +7,10 @@ import tables
|
||||
import yaml
|
||||
import extract_music
|
||||
import os
|
||||
import getopt
|
||||
|
||||
PATH=''
|
||||
|
||||
option_list, args = getopt.getopt(sys.argv[1:], 'r:', ['rom_path='])
|
||||
rom_path = None
|
||||
for opt, arg in option_list:
|
||||
if opt in ('-r', '--rom_path'):
|
||||
rom_path = arg
|
||||
|
||||
try:
|
||||
ROM = util.LoadedRom(rom_path)
|
||||
except Exception as error:
|
||||
print(f"Failed to load ROM data for extraction. Error: {error}")
|
||||
sys.exit(1)
|
||||
|
||||
ROM = util.LoadedRom(sys.argv[1] if len(sys.argv) >= 2 else None)
|
||||
|
||||
get_byte = ROM.get_byte
|
||||
get_word = ROM.get_word
|
||||
|
||||
@@ -41,13 +41,13 @@ static const uint8 kTagalong_IndoorOffsets[8] = {0, 3, 6, 7, 9, 10, 11, 12};
|
||||
static const uint8 kTagalong_OutdoorOffsets[4] = {0, 1, 4, 5};
|
||||
static const uint16 kTagalong_IndoorRooms[7] = {0xf1, 0x61, 0x51, 2, 0xdb, 0xab, 0x22};
|
||||
static const uint16 kTagalong_OutdoorRooms[3] = {3, 0x5e, 0};
|
||||
struct TagalongSprXY {
|
||||
typedef struct TagalongSprXY {
|
||||
int8 y1, x1, y2, x2;
|
||||
};
|
||||
struct TagalongDmaFlags {
|
||||
} TagalongSprXY;
|
||||
typedef struct TagalongDmaFlags {
|
||||
uint8 dma6, dma7;
|
||||
uint8 flags;
|
||||
};
|
||||
} TagalongDmaFlags;
|
||||
static const TagalongSprXY kTagalongDraw_SprXY[56] = {
|
||||
{-2, 0, 0, 0},
|
||||
{-2, 0, 0, 0},
|
||||
@@ -393,8 +393,8 @@ void Follower_BasicMover() { // 89a197
|
||||
}
|
||||
|
||||
if (link_x_vel | link_y_vel) {
|
||||
label_e:
|
||||
uint8 t;
|
||||
label_e:
|
||||
t = tagalong_var1 - 15;
|
||||
if (sign8(t))
|
||||
t += 20;
|
||||
@@ -643,9 +643,9 @@ incr:
|
||||
bytewise_extended_oam[oam - oam_buf] = ext;
|
||||
oam++;
|
||||
}
|
||||
uint8 pal;
|
||||
skip_first_sprites:
|
||||
|
||||
uint8 pal = kTagalongDraw_Pals[savegame_tagalong];
|
||||
pal = kTagalongDraw_Pals[savegame_tagalong];
|
||||
if (pal == 7 && overworld_palette_swap_flag)
|
||||
pal = 0;
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
#pragma once
|
||||
#include "types.h"
|
||||
|
||||
struct TagalongMessageInfo {
|
||||
typedef struct TagalongMessageInfo {
|
||||
uint16 y, x, bit, msg, tagalong;
|
||||
};
|
||||
} TagalongMessageInfo;
|
||||
|
||||
bool Tagalong_IsFollowing();
|
||||
bool Follower_ValidateMessageFreedom();
|
||||
|
||||
47
types.h
47
types.h
@@ -1,5 +1,6 @@
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdbool.h>
|
||||
#pragma once
|
||||
|
||||
typedef uint8_t uint8;
|
||||
@@ -16,9 +17,12 @@ typedef int32_t int32;
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#define countof _countof
|
||||
#define NORETURN __declspec(noreturn)
|
||||
#else
|
||||
#define countof(a) (sizeof(a)/sizeof(*(a)))
|
||||
#define NORETURN
|
||||
#endif
|
||||
|
||||
static inline uint16 abs16(uint16 t) { return sign16(t) ? -t : t; }
|
||||
static inline uint8 abs8(uint8 t) { return sign8(t) ? -t : t; }
|
||||
|
||||
@@ -29,48 +33,43 @@ static inline uint8 abs8(uint8 t) { return sign8(t) ? -t : t; }
|
||||
|
||||
static inline uint16 swap16(uint16 v) { return (v << 8) | (v >> 8); }
|
||||
|
||||
struct Point16U {
|
||||
typedef struct Point16U {
|
||||
uint16 x, y;
|
||||
};
|
||||
struct PointU8 {
|
||||
} Point16U;
|
||||
typedef struct PointU8 {
|
||||
uint8 x, y;
|
||||
};
|
||||
|
||||
struct Pair16U {
|
||||
} PointU8;
|
||||
typedef struct Pair16U {
|
||||
uint16 a, b;
|
||||
};
|
||||
} Pair16U;
|
||||
|
||||
struct PairU8 {
|
||||
typedef struct PairU8 {
|
||||
uint8 a, b;
|
||||
};
|
||||
} PairU8;
|
||||
|
||||
struct ProjectSpeedRet {
|
||||
typedef struct ProjectSpeedRet {
|
||||
uint8 x, y;
|
||||
uint8 xdiff, ydiff;
|
||||
};
|
||||
} ProjectSpeedRet;
|
||||
|
||||
struct OamEnt {
|
||||
typedef struct OamEnt {
|
||||
uint8 x, y, charnum, flags;
|
||||
};
|
||||
} OamEnt;
|
||||
|
||||
struct UploadVram_Row {
|
||||
typedef struct UploadVram_Row {
|
||||
uint16 col[32];
|
||||
};
|
||||
} UploadVram_Row;
|
||||
|
||||
struct UploadVram_32x32 {
|
||||
typedef struct UploadVram_32x32 {
|
||||
UploadVram_Row row[32];
|
||||
};
|
||||
} UploadVram_32x32;
|
||||
|
||||
struct UploadVram_3 {
|
||||
typedef struct UploadVram_3 {
|
||||
uint8 pad[256];
|
||||
uint16 data[4];
|
||||
};
|
||||
} UploadVram_3;
|
||||
|
||||
union UploadVram {
|
||||
UploadVram_3 t3;
|
||||
};
|
||||
|
||||
#define uvram (*(UploadVram*)(&g_ram[0x1000]))
|
||||
#define uvram (*(UploadVram_3*)(&g_ram[0x1000]))
|
||||
|
||||
typedef void PlayerHandlerFunc();
|
||||
typedef void HandlerFuncK(int k);
|
||||
@@ -123,77 +123,77 @@
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="ancilla.cpp" />
|
||||
<ClCompile Include="attract.cpp" />
|
||||
<ClCompile Include="dungeon.cpp" />
|
||||
<ClCompile Include="ending.cpp" />
|
||||
<ClCompile Include="hud.cpp" />
|
||||
<ClCompile Include="load_gfx.cpp" />
|
||||
<ClCompile Include="main.cpp">
|
||||
<ClCompile Include="ancilla.c" />
|
||||
<ClCompile Include="attract.c" />
|
||||
<ClCompile Include="dungeon.c" />
|
||||
<ClCompile Include="ending.c" />
|
||||
<ClCompile Include="hud.c" />
|
||||
<ClCompile Include="load_gfx.c" />
|
||||
<ClCompile Include="main.c">
|
||||
<Optimization Condition="'$(Configuration)|$(Platform)'=='Release|x64'">Disabled</Optimization>
|
||||
<InlineFunctionExpansion Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">Disabled</InlineFunctionExpansion>
|
||||
</ClCompile>
|
||||
<ClCompile Include="messaging.cpp" />
|
||||
<ClCompile Include="misc.cpp" />
|
||||
<ClCompile Include="nmi.cpp" />
|
||||
<ClCompile Include="other_modules.cpp" />
|
||||
<ClCompile Include="overlord.cpp" />
|
||||
<ClCompile Include="overworld.cpp" />
|
||||
<ClCompile Include="player.cpp" />
|
||||
<ClCompile Include="player_oam.cpp" />
|
||||
<ClCompile Include="poly.cpp" />
|
||||
<ClCompile Include="select_file.cpp" />
|
||||
<ClCompile Include="snes\apu.cpp">
|
||||
<ClCompile Include="messaging.c" />
|
||||
<ClCompile Include="misc.c" />
|
||||
<ClCompile Include="nmi.c" />
|
||||
<ClCompile Include="other_modules.c" />
|
||||
<ClCompile Include="overlord.c" />
|
||||
<ClCompile Include="overworld.c" />
|
||||
<ClCompile Include="player.c" />
|
||||
<ClCompile Include="player_oam.c" />
|
||||
<ClCompile Include="poly.c" />
|
||||
<ClCompile Include="select_file.c" />
|
||||
<ClCompile Include="snes\apu.c">
|
||||
<Optimization Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">Disabled</Optimization>
|
||||
<InlineFunctionExpansion Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">Disabled</InlineFunctionExpansion>
|
||||
</ClCompile>
|
||||
<ClCompile Include="snes\cart.cpp">
|
||||
<ClCompile Include="snes\cart.c">
|
||||
<Optimization Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">Disabled</Optimization>
|
||||
<InlineFunctionExpansion Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">Disabled</InlineFunctionExpansion>
|
||||
</ClCompile>
|
||||
<ClCompile Include="snes\cpu.cpp">
|
||||
<ClCompile Include="snes\cpu.c">
|
||||
<Optimization Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">Disabled</Optimization>
|
||||
<InlineFunctionExpansion Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">Disabled</InlineFunctionExpansion>
|
||||
</ClCompile>
|
||||
<ClCompile Include="snes\dma.cpp">
|
||||
<ClCompile Include="snes\dma.c">
|
||||
<Optimization Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">Disabled</Optimization>
|
||||
<InlineFunctionExpansion Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">Disabled</InlineFunctionExpansion>
|
||||
</ClCompile>
|
||||
<ClCompile Include="snes\dsp.cpp">
|
||||
<ClCompile Include="snes\dsp.c">
|
||||
<Optimization Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">Disabled</Optimization>
|
||||
<InlineFunctionExpansion Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">Disabled</InlineFunctionExpansion>
|
||||
</ClCompile>
|
||||
<ClCompile Include="snes\input.cpp">
|
||||
<ClCompile Include="snes\input.c">
|
||||
<InlineFunctionExpansion Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">Disabled</InlineFunctionExpansion>
|
||||
</ClCompile>
|
||||
<ClCompile Include="snes\ppu.cpp">
|
||||
<ClCompile Include="snes\ppu.c">
|
||||
<Optimization Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">MaxSpeed</Optimization>
|
||||
<InlineFunctionExpansion Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">AnySuitable</InlineFunctionExpansion>
|
||||
</ClCompile>
|
||||
<ClCompile Include="snes\snes.cpp">
|
||||
<ClCompile Include="snes\snes.c">
|
||||
<Optimization Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">Disabled</Optimization>
|
||||
<InlineFunctionExpansion Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">Disabled</InlineFunctionExpansion>
|
||||
</ClCompile>
|
||||
<ClCompile Include="snes\snes_other.cpp">
|
||||
<ClCompile Include="snes\snes_other.c">
|
||||
<InlineFunctionExpansion Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">Disabled</InlineFunctionExpansion>
|
||||
</ClCompile>
|
||||
<ClCompile Include="snes\spc.cpp">
|
||||
<ClCompile Include="snes\spc.c">
|
||||
<Optimization Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">Disabled</Optimization>
|
||||
<InlineFunctionExpansion Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">Disabled</InlineFunctionExpansion>
|
||||
</ClCompile>
|
||||
<ClCompile Include="spc_player.cpp" />
|
||||
<ClCompile Include="sprite.cpp" />
|
||||
<ClCompile Include="sprite_main.cpp">
|
||||
<ClCompile Include="spc_player.c" />
|
||||
<ClCompile Include="sprite.c" />
|
||||
<ClCompile Include="sprite_main.c">
|
||||
<Optimization Condition="'$(Configuration)|$(Platform)'=='Release|x64'">MaxSpeed</Optimization>
|
||||
</ClCompile>
|
||||
<ClCompile Include="tagalong.cpp" />
|
||||
<ClCompile Include="tile_detect.cpp" />
|
||||
<ClCompile Include="tracing.cpp">
|
||||
<ClCompile Include="tagalong.c" />
|
||||
<ClCompile Include="tile_detect.c" />
|
||||
<ClCompile Include="tracing.c">
|
||||
<Optimization Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">Disabled</Optimization>
|
||||
<InlineFunctionExpansion Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">Disabled</InlineFunctionExpansion>
|
||||
</ClCompile>
|
||||
<ClCompile Include="zelda_cpu_infra.cpp" />
|
||||
<ClCompile Include="zelda_rtl.cpp" />
|
||||
<ClCompile Include="zelda_cpu_infra.c" />
|
||||
<ClCompile Include="zelda_rtl.c" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="ancilla.h" />
|
||||
@@ -217,6 +217,7 @@
|
||||
<ClInclude Include="snes\cpu.h" />
|
||||
<ClInclude Include="snes\dma.h" />
|
||||
<ClInclude Include="snes\dsp.h" />
|
||||
<ClInclude Include="snes\dsp_regs.h" />
|
||||
<ClInclude Include="snes\input.h" />
|
||||
<ClInclude Include="snes\ppu.h" />
|
||||
<ClInclude Include="snes\saveload.h" />
|
||||
|
||||
@@ -10,109 +10,109 @@
|
||||
</Filter>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="snes\apu.cpp">
|
||||
<ClCompile Include="snes\apu.c">
|
||||
<Filter>Snes</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="snes\cart.cpp">
|
||||
<ClCompile Include="snes\cart.c">
|
||||
<Filter>Snes</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="snes\cpu.cpp">
|
||||
<ClCompile Include="snes\cpu.c">
|
||||
<Filter>Snes</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="snes\dma.cpp">
|
||||
<ClCompile Include="snes\dma.c">
|
||||
<Filter>Snes</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="snes\dsp.cpp">
|
||||
<ClCompile Include="snes\dsp.c">
|
||||
<Filter>Snes</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="snes\input.cpp">
|
||||
<ClCompile Include="snes\input.c">
|
||||
<Filter>Snes</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="main.cpp">
|
||||
<ClCompile Include="main.c">
|
||||
<Filter>Snes</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="snes\ppu.cpp">
|
||||
<ClCompile Include="snes\ppu.c">
|
||||
<Filter>Snes</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="snes\snes.cpp">
|
||||
<ClCompile Include="snes\snes.c">
|
||||
<Filter>Snes</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="snes\snes_other.cpp">
|
||||
<ClCompile Include="snes\snes_other.c">
|
||||
<Filter>Snes</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="snes\spc.cpp">
|
||||
<ClCompile Include="snes\spc.c">
|
||||
<Filter>Snes</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="tracing.cpp">
|
||||
<ClCompile Include="tracing.c">
|
||||
<Filter>Snes</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="zelda_rtl.cpp">
|
||||
<ClCompile Include="zelda_rtl.c">
|
||||
<Filter>Zelda</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="hud.cpp">
|
||||
<ClCompile Include="hud.c">
|
||||
<Filter>Zelda</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="dungeon.cpp">
|
||||
<ClCompile Include="dungeon.c">
|
||||
<Filter>Zelda</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="nmi.cpp">
|
||||
<ClCompile Include="nmi.c">
|
||||
<Filter>Zelda</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="player_oam.cpp">
|
||||
<ClCompile Include="player_oam.c">
|
||||
<Filter>Zelda</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="load_gfx.cpp">
|
||||
<ClCompile Include="load_gfx.c">
|
||||
<Filter>Zelda</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="messaging.cpp">
|
||||
<ClCompile Include="messaging.c">
|
||||
<Filter>Zelda</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="overworld.cpp">
|
||||
<ClCompile Include="overworld.c">
|
||||
<Filter>Zelda</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="sprite.cpp">
|
||||
<ClCompile Include="sprite.c">
|
||||
<Filter>Zelda</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="sprite_main.cpp">
|
||||
<ClCompile Include="sprite_main.c">
|
||||
<Filter>Zelda</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="tagalong.cpp">
|
||||
<ClCompile Include="tagalong.c">
|
||||
<Filter>Zelda</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="overlord.cpp">
|
||||
<ClCompile Include="overlord.c">
|
||||
<Filter>Zelda</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="poly.cpp">
|
||||
<ClCompile Include="poly.c">
|
||||
<Filter>Zelda</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="ancilla.cpp">
|
||||
<ClCompile Include="ancilla.c">
|
||||
<Filter>Zelda</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="ending.cpp">
|
||||
<ClCompile Include="ending.c">
|
||||
<Filter>Zelda</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="attract.cpp">
|
||||
<ClCompile Include="attract.c">
|
||||
<Filter>Zelda</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="select_file.cpp">
|
||||
<ClCompile Include="select_file.c">
|
||||
<Filter>Zelda</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="spc_player.cpp">
|
||||
<ClCompile Include="spc_player.c">
|
||||
<Filter>Zelda</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="player.cpp">
|
||||
<ClCompile Include="player.c">
|
||||
<Filter>Zelda</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="tile_detect.cpp">
|
||||
<ClCompile Include="tile_detect.c">
|
||||
<Filter>Zelda</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="other_modules.cpp">
|
||||
<ClCompile Include="other_modules.c">
|
||||
<Filter>Zelda</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="misc.cpp">
|
||||
<ClCompile Include="misc.c">
|
||||
<Filter>Zelda</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="zelda_cpu_infra.cpp">
|
||||
<ClCompile Include="zelda_cpu_infra.c">
|
||||
<Filter>Zelda</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
@@ -228,6 +228,9 @@
|
||||
<ClInclude Include="sprite_main.h">
|
||||
<Filter>Zelda</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="snes\dsp_regs.h">
|
||||
<Filter>Snes</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="packages.config" />
|
||||
|
||||
@@ -7,39 +7,35 @@
|
||||
#include "nmi.h"
|
||||
#include "poly.h"
|
||||
#include "attract.h"
|
||||
#include "spc_player.h"
|
||||
|
||||
#include "snes/snes.h"
|
||||
#include "snes/cpu.h"
|
||||
#include "snes/cart.h"
|
||||
#include "tracing.h"
|
||||
|
||||
#include <vector>
|
||||
|
||||
Snes *g_snes;
|
||||
Cpu *g_cpu;
|
||||
uint8 g_emulated_ram[0x20000];
|
||||
|
||||
|
||||
void SaveLoadSlot(int cmd, int which);
|
||||
|
||||
//uint8 *GetPtr(uint32 addr) {
|
||||
// Cart *cart = g_snes->cart;
|
||||
// return &cart->rom[(((addr >> 16) << 15) | (addr & 0x7fff)) & (cart->romSize - 1)];
|
||||
//}
|
||||
uint8 *GetPtr(uint32 addr) {
|
||||
Cart *cart = g_snes->cart;
|
||||
return &cart->rom[(((addr >> 16) << 15) | (addr & 0x7fff)) & (cart->romSize - 1)];
|
||||
}
|
||||
|
||||
//extern "C" uint8 *GetCartRamPtr(uint32 addr) {
|
||||
// Cart *cart = g_snes->cart;
|
||||
// return &cart->ram[addr];
|
||||
//}
|
||||
uint8 *GetCartRamPtr(uint32 addr) {
|
||||
Cart *cart = g_snes->cart;
|
||||
return &cart->ram[addr];
|
||||
}
|
||||
|
||||
struct Snapshot {
|
||||
typedef struct Snapshot {
|
||||
uint16 a, x, y, sp, dp, pc;
|
||||
uint8 k, db, flags;
|
||||
uint8 ram[0x20000];
|
||||
uint16 vram[0x8000];
|
||||
uint16 sram[0x2000];
|
||||
};
|
||||
} Snapshot;
|
||||
|
||||
static Snapshot g_snapshot_mine, g_snapshot_theirs, g_snapshot_before;
|
||||
|
||||
@@ -322,16 +318,36 @@ Dsp *GetDspForRendering() {
|
||||
return g_zenv.player->dsp;
|
||||
}
|
||||
|
||||
typedef struct ByteArray {
|
||||
uint8 *data;
|
||||
size_t size, capacity;
|
||||
} ByteArray;
|
||||
|
||||
void saveFunc(void *ctx, void *data, size_t data_size) {
|
||||
std::vector<uint8> *vec = (std::vector<uint8> *)ctx;
|
||||
vec->resize(vec->size() + data_size);
|
||||
memcpy(vec->data() + vec->size() - data_size, data, data_size);
|
||||
void ByteArray_Resize(ByteArray *arr, size_t new_size) {
|
||||
arr->size = new_size;
|
||||
if (new_size > arr->capacity) {
|
||||
size_t minsize = arr->capacity + (arr->capacity >> 1) + 8;
|
||||
arr->capacity = new_size < minsize ? minsize : new_size;
|
||||
void *data = realloc(arr->data, arr->capacity);
|
||||
if (!data) Die("memory allocation failed");
|
||||
arr->data = data;
|
||||
}
|
||||
}
|
||||
|
||||
struct LoadFuncState {
|
||||
void ByteArray_Destroy(ByteArray *arr) {
|
||||
free(arr->data);
|
||||
arr->data = NULL;
|
||||
}
|
||||
|
||||
void saveFunc(void *ctx_in, void *data, size_t data_size) {
|
||||
ByteArray *arr = (ByteArray *)ctx_in;
|
||||
ByteArray_Resize(arr, arr->size + data_size);
|
||||
memcpy(arr->data + arr->size - data_size, data, data_size);
|
||||
}
|
||||
|
||||
typedef struct LoadFuncState {
|
||||
uint8 *p, *pend;
|
||||
};
|
||||
} LoadFuncState;
|
||||
|
||||
void loadFunc(void *ctx, void *data, size_t data_size) {
|
||||
LoadFuncState *st = (LoadFuncState *)ctx;
|
||||
@@ -340,7 +356,6 @@ void loadFunc(void *ctx, void *data, size_t data_size) {
|
||||
st->p += data_size;
|
||||
}
|
||||
|
||||
|
||||
void CopyStateAfterSnapshotRestore(bool is_reset) {
|
||||
memcpy(g_zenv.ram, g_snes->ram, 0x20000);
|
||||
memcpy(g_zenv.sram, g_snes->cart->ram, g_snes->cart->ramSize);
|
||||
@@ -357,9 +372,7 @@ void CopyStateAfterSnapshotRestore(bool is_reset) {
|
||||
g_zenv.player->timer_cycles = 0;
|
||||
}
|
||||
|
||||
std::vector<uint8> SaveSnesState() {
|
||||
std::vector<uint8> data;
|
||||
|
||||
void SaveSnesState(ByteArray *ctx) {
|
||||
MakeSnapshot(&g_snapshot_before);
|
||||
|
||||
// Copy from my state into the emulator
|
||||
@@ -371,138 +384,115 @@ std::vector<uint8> SaveSnesState() {
|
||||
memcpy(g_snes->apu->dsp->ram, g_zenv.player->dsp->ram, sizeof(Dsp) - offsetof(Dsp, ram));
|
||||
memcpy(g_snes->dma->channel, g_zenv.dma->channel, sizeof(Dma) - offsetof(Dma, channel));
|
||||
|
||||
snes_saveload(g_snes, &saveFunc, &data);
|
||||
|
||||
snes_saveload(g_snes, &saveFunc, ctx);
|
||||
|
||||
RestoreSnapshot(&g_snapshot_before);
|
||||
return data;
|
||||
}
|
||||
|
||||
|
||||
class StateRecorder {
|
||||
public:
|
||||
StateRecorder() : last_inputs_(0), frames_since_last_(0), total_frames_(0), replay_mode_(false) {}
|
||||
void Record(uint16 inputs);
|
||||
void RecordPatchByte(uint32 addr, const uint8 *value, int num);
|
||||
|
||||
void Load(FILE *f, bool replay_mode);
|
||||
void Save(FILE *f);
|
||||
|
||||
uint16 ReadNextReplayState();
|
||||
bool is_replay_mode() { return replay_mode_; }
|
||||
|
||||
void MigrateToBaseSnapshot();
|
||||
private:
|
||||
|
||||
void RecordJoypadBit(int command);
|
||||
void AppendByte(uint8 v);
|
||||
void AppendVl(uint32 v);
|
||||
|
||||
uint16 last_inputs_;
|
||||
uint32 frames_since_last_;
|
||||
uint32 total_frames_;
|
||||
typedef struct StateRecorder {
|
||||
uint16 last_inputs;
|
||||
uint32 frames_since_last;
|
||||
uint32 total_frames;
|
||||
|
||||
// For replay
|
||||
uint32 replay_pos_, replay_frame_counter_, replay_next_cmd_at_;
|
||||
uint8 replay_cmd_;
|
||||
bool replay_mode_;
|
||||
uint32 replay_pos;
|
||||
uint32 replay_frame_counter;
|
||||
uint32 replay_next_cmd_at;
|
||||
uint8 replay_cmd;
|
||||
bool replay_mode;
|
||||
|
||||
std::vector<uint8> log_;
|
||||
std::vector<uint8> base_snapshot_;
|
||||
};
|
||||
ByteArray log;
|
||||
ByteArray base_snapshot;
|
||||
} StateRecorder;
|
||||
|
||||
uint32 RamChecksum() {
|
||||
uint64_t cksum = 0, cksum2 = 0;
|
||||
for (int i = 0; i < 0x20000; i += 4) {
|
||||
cksum += *(uint32 *)&g_ram[i];
|
||||
cksum2 += cksum;
|
||||
}
|
||||
return cksum ^ (cksum >> 32) ^ cksum2 ^ (cksum2 >> 32);
|
||||
static StateRecorder state_recorder;
|
||||
|
||||
void StateRecorder_Init(StateRecorder *sr) {
|
||||
memset(sr, 0, sizeof(*sr));
|
||||
}
|
||||
|
||||
void StateRecorder::AppendByte(uint8 v) {
|
||||
log_.push_back(v);
|
||||
void StateRecorder_AppendByte(StateRecorder *sr, uint8 v) {
|
||||
ByteArray_Resize(&sr->log, sr->log.size + 1);
|
||||
sr->log.data[sr->log.size - 1] = v;
|
||||
printf("%.2x ", v);
|
||||
}
|
||||
|
||||
void StateRecorder::AppendVl(uint32 v) {
|
||||
void StateRecorder_AppendVl(StateRecorder *sr, uint32 v) {
|
||||
for (; v >= 255; v -= 255)
|
||||
AppendByte(255);
|
||||
AppendByte(v);
|
||||
StateRecorder_AppendByte(sr, 255);
|
||||
StateRecorder_AppendByte(sr, v);
|
||||
}
|
||||
|
||||
void StateRecorder::RecordJoypadBit(int command) {
|
||||
int frames = frames_since_last_;
|
||||
AppendByte(command << 4 | (frames < 15 ? frames : 15));
|
||||
void StateRecorder_RecordJoypadBit(StateRecorder *sr, int command) {
|
||||
int frames = sr->frames_since_last;
|
||||
StateRecorder_AppendByte(sr, command << 4 | (frames < 15 ? frames : 15));
|
||||
if (frames >= 15)
|
||||
AppendVl(frames - 15);
|
||||
frames_since_last_ = 0;
|
||||
StateRecorder_AppendVl(sr, frames - 15);
|
||||
sr->frames_since_last = 0;
|
||||
}
|
||||
|
||||
void StateRecorder::Record(uint16 inputs) {
|
||||
uint16 diff = inputs ^ last_inputs_;
|
||||
void StateRecorder_Record(StateRecorder *sr, uint16 inputs) {
|
||||
uint16 diff = inputs ^ sr->last_inputs;
|
||||
if (diff != 0) {
|
||||
last_inputs_ = inputs;
|
||||
printf("0x%.4x %d: ", diff, frames_since_last_);
|
||||
sr->last_inputs = inputs;
|
||||
printf("0x%.4x %d: ", diff, sr->frames_since_last);
|
||||
for (int i = 0; i < 12; i++) {
|
||||
if ((diff >> i) & 1)
|
||||
RecordJoypadBit(i);
|
||||
StateRecorder_RecordJoypadBit(sr, i);
|
||||
}
|
||||
printf("\n");
|
||||
}
|
||||
frames_since_last_++;
|
||||
total_frames_++;
|
||||
sr->frames_since_last++;
|
||||
sr->total_frames++;
|
||||
}
|
||||
|
||||
void StateRecorder::RecordPatchByte(uint32 addr, const uint8 *value, int num) {
|
||||
void StateRecorder_RecordPatchByte(StateRecorder *sr, uint32 addr, const uint8 *value, int num) {
|
||||
assert(addr < 0x20000);
|
||||
printf("%d: PatchByte(0x%x, 0x%x. %d): ", frames_since_last_, addr, *value, num);
|
||||
printf("%d: PatchByte(0x%x, 0x%x. %d): ", sr->frames_since_last, addr, *value, num);
|
||||
|
||||
int frames = frames_since_last_;
|
||||
int frames = sr->frames_since_last;
|
||||
sr->frames_since_last = 0;
|
||||
|
||||
int lq = (num - 1) <= 3 ? (num - 1) : 3;
|
||||
|
||||
AppendByte(0xc0 | (frames != 0 ? 1 : 0) | (addr & 0x10000 ? 2 : 0) | lq << 2);
|
||||
StateRecorder_AppendByte(sr, 0xc0 | (frames != 0 ? 1 : 0) | (addr & 0x10000 ? 2 : 0) | lq << 2);
|
||||
if (frames != 0)
|
||||
AppendVl(frames - 1);
|
||||
StateRecorder_AppendVl(sr, frames - 1);
|
||||
if (lq == 3)
|
||||
AppendVl(num - 1 - 3);
|
||||
|
||||
frames_since_last_ = 0;
|
||||
AppendByte(addr >> 8);
|
||||
AppendByte(addr);
|
||||
StateRecorder_AppendVl(sr, num - 1 - 3);
|
||||
StateRecorder_AppendByte(sr, addr >> 8);
|
||||
StateRecorder_AppendByte(sr, addr);
|
||||
for(int i = 0; i < num; i++)
|
||||
AppendByte(value[i]);
|
||||
StateRecorder_AppendByte(sr, value[i]);
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
void StateRecorder::Load(FILE *f, bool replay_mode) {
|
||||
void StateRecorder_Load(StateRecorder *sr, FILE *f, bool replay_mode) {
|
||||
uint32 hdr[8] = { 0 };
|
||||
fread(hdr, 8, 4, f);
|
||||
|
||||
assert(hdr[0] == 1);
|
||||
|
||||
total_frames_ = hdr[1];
|
||||
log_.resize(hdr[2]);
|
||||
fread(log_.data(), 1, hdr[2], f);
|
||||
last_inputs_ = hdr[3];
|
||||
frames_since_last_ = hdr[4];
|
||||
sr->total_frames = hdr[1];
|
||||
ByteArray_Resize(&sr->log, hdr[2]);
|
||||
fread(sr->log.data, 1, sr->log.size, f);
|
||||
sr->last_inputs = hdr[3];
|
||||
sr->frames_since_last = hdr[4];
|
||||
|
||||
base_snapshot_.resize((hdr[5] & 1) ? hdr[6] : 0);
|
||||
fread(base_snapshot_.data(), 1, base_snapshot_.size(), f);
|
||||
ByteArray_Resize(&sr->base_snapshot, (hdr[5] & 1) ? hdr[6] : 0);
|
||||
fread(sr->base_snapshot.data, 1, sr->base_snapshot.size, f);
|
||||
|
||||
bool is_reset = false;
|
||||
replay_mode_ = replay_mode;
|
||||
sr->replay_mode = replay_mode;
|
||||
if (replay_mode) {
|
||||
replay_next_cmd_at_ = frames_since_last_ = 0;
|
||||
last_inputs_ = 0;
|
||||
replay_pos_ = 0;
|
||||
replay_frame_counter_ = 0;
|
||||
replay_cmd_ = 0xff;
|
||||
sr->replay_next_cmd_at = sr->frames_since_last = 0;
|
||||
sr->last_inputs = 0;
|
||||
sr->replay_pos = 0;
|
||||
sr->replay_frame_counter = 0;
|
||||
sr->replay_cmd = 0xff;
|
||||
// Load snapshot from |base_snapshot_|, or reset if empty.
|
||||
|
||||
if (base_snapshot_.size()) {
|
||||
LoadFuncState state = { base_snapshot_.data(), base_snapshot_.data() + base_snapshot_.size() };
|
||||
if (sr->base_snapshot.size) {
|
||||
LoadFuncState state = { sr->base_snapshot.data, sr->base_snapshot.data + sr->base_snapshot.size };
|
||||
snes_saveload(g_snes, &loadFunc, &state);
|
||||
assert(state.p == state.pend);
|
||||
} else {
|
||||
@@ -511,97 +501,97 @@ void StateRecorder::Load(FILE *f, bool replay_mode) {
|
||||
is_reset = true;
|
||||
}
|
||||
} else {
|
||||
std::vector<uint8> data;
|
||||
data.resize(hdr[6]);
|
||||
fread(data.data(), 1, data.size(), f);
|
||||
|
||||
LoadFuncState state = { data.data(), data.data() + data.size() };
|
||||
ByteArray arr = { 0 };
|
||||
ByteArray_Resize(&arr, hdr[6]);
|
||||
fread(arr.data, 1, arr.size, f);
|
||||
LoadFuncState state = { arr.data, arr.data + arr.size };
|
||||
snes_saveload(g_snes, &loadFunc, &state);
|
||||
ByteArray_Destroy(&arr);
|
||||
assert(state.p == state.pend);
|
||||
}
|
||||
CopyStateAfterSnapshotRestore(is_reset);
|
||||
}
|
||||
|
||||
void StateRecorder::Save(FILE *f) {
|
||||
void StateRecorder_Save(StateRecorder *sr, FILE *f) {
|
||||
uint32 hdr[8] = { 0 };
|
||||
|
||||
std::vector<uint8> data = SaveSnesState();
|
||||
assert(base_snapshot_.size() == 0 || base_snapshot_.size() == data.size());
|
||||
ByteArray arr = {0};
|
||||
SaveSnesState(&arr);
|
||||
assert(sr->base_snapshot.size == 0 || sr->base_snapshot.size == arr.size);
|
||||
|
||||
hdr[0] = 1;
|
||||
hdr[1] = total_frames_;
|
||||
hdr[2] = log_.size();
|
||||
hdr[3] = last_inputs_;
|
||||
hdr[4] = frames_since_last_;
|
||||
hdr[5] = (base_snapshot_.size() ? 1 : 0);
|
||||
hdr[6] = data.size();
|
||||
hdr[1] = sr->total_frames;
|
||||
hdr[2] = sr->log.size;
|
||||
hdr[3] = sr->last_inputs;
|
||||
hdr[4] = sr->frames_since_last;
|
||||
hdr[5] = sr->base_snapshot.size ? 1 : 0;
|
||||
hdr[6] = arr.size;
|
||||
|
||||
fwrite(hdr, 8, 4, f);
|
||||
fwrite(log_.data(), 1, log_.size(), f);
|
||||
fwrite(base_snapshot_.data(), 1, base_snapshot_.size(), f);
|
||||
fwrite(data.data(), 1, data.size(), f);
|
||||
fwrite(sr->log.data, 1, sr->log.size, f);
|
||||
fwrite(sr->base_snapshot.data, 1, sr->base_snapshot.size, f);
|
||||
fwrite(arr.data, 1, arr.size, f);
|
||||
|
||||
ByteArray_Destroy(&arr);
|
||||
}
|
||||
|
||||
void StateRecorder::MigrateToBaseSnapshot() {
|
||||
void StateRecorder_MigrateToBaseSnapshot(StateRecorder *sr) {
|
||||
printf("Migrating to base snapshot!\n");
|
||||
std::vector<uint8> data = SaveSnesState();
|
||||
base_snapshot_ = std::move(data);
|
||||
|
||||
replay_mode_ = false;
|
||||
frames_since_last_ = 0;
|
||||
last_inputs_ = 0;
|
||||
total_frames_ = 0;
|
||||
log_.clear();
|
||||
sr->base_snapshot.size = 0;
|
||||
SaveSnesState(&sr->base_snapshot);
|
||||
sr->replay_mode = false;
|
||||
sr->frames_since_last = 0;
|
||||
sr->last_inputs = 0;
|
||||
sr->total_frames = 0;
|
||||
sr->log.size = 0;
|
||||
}
|
||||
|
||||
uint16 StateRecorder::ReadNextReplayState() {
|
||||
assert(replay_mode_);
|
||||
while (frames_since_last_ >= replay_next_cmd_at_) {
|
||||
frames_since_last_ = 0;
|
||||
uint16 StateRecorder_ReadNextReplayState(StateRecorder *sr) {
|
||||
assert(sr->replay_mode);
|
||||
while (sr->frames_since_last >= sr->replay_next_cmd_at) {
|
||||
sr->frames_since_last = 0;
|
||||
// Apply next command
|
||||
if (replay_cmd_ != 0xff) {
|
||||
if (replay_cmd_ < 0xc0) {
|
||||
last_inputs_ ^= 1 << (replay_cmd_ >> 4);
|
||||
} else if (replay_cmd_ < 0xd0) {
|
||||
int nb = 1 + ((replay_cmd_ >> 2) & 3);
|
||||
if (sr->replay_cmd != 0xff) {
|
||||
if (sr->replay_cmd < 0xc0) {
|
||||
sr->last_inputs ^= 1 << (sr->replay_cmd >> 4);
|
||||
} else if (sr->replay_cmd < 0xd0) {
|
||||
int nb = 1 + ((sr->replay_cmd >> 2) & 3);
|
||||
uint8 t;
|
||||
if (nb == 4) do {
|
||||
nb += t = log_[replay_pos_++];
|
||||
nb += t = sr->log.data[sr->replay_pos++];
|
||||
} while (t == 255);
|
||||
uint32 addr = ((replay_cmd_ >> 1) & 1) << 16;
|
||||
addr |= log_[replay_pos_++] << 8;
|
||||
addr |= log_[replay_pos_++];
|
||||
uint32 addr = ((sr->replay_cmd >> 1) & 1) << 16;
|
||||
addr |= sr->log.data[sr->replay_pos++] << 8;
|
||||
addr |= sr->log.data[sr->replay_pos++];
|
||||
do {
|
||||
g_emulated_ram[addr & 0x1ffff] = g_ram[addr & 0x1ffff] = log_[replay_pos_++];
|
||||
g_emulated_ram[addr & 0x1ffff] = g_ram[addr & 0x1ffff] = sr->log.data[sr->replay_pos++];
|
||||
} while (addr++, --nb);
|
||||
} else {
|
||||
assert(0);
|
||||
}
|
||||
}
|
||||
if (replay_pos_ >= log_.size()) {
|
||||
replay_cmd_ = 0xff;
|
||||
replay_next_cmd_at_ = 0xffffffff;
|
||||
if (sr->replay_pos >= sr->log.size) {
|
||||
sr->replay_cmd = 0xff;
|
||||
sr->replay_next_cmd_at = 0xffffffff;
|
||||
break;
|
||||
}
|
||||
// Read the next one
|
||||
uint8 cmd = log_[replay_pos_++], t;
|
||||
uint8 cmd = sr->log.data[sr->replay_pos++], t;
|
||||
int mask = (cmd < 0xc0) ? 0xf : 0x1;
|
||||
int frames = cmd & mask;
|
||||
if (frames == mask) do {
|
||||
frames += t = log_[replay_pos_++];
|
||||
frames += t = sr->log.data[sr->replay_pos++];
|
||||
} while (t == 255);
|
||||
replay_next_cmd_at_ = frames;
|
||||
replay_cmd_ = cmd;
|
||||
sr->replay_next_cmd_at = frames;
|
||||
sr->replay_cmd = cmd;
|
||||
}
|
||||
frames_since_last_++;
|
||||
sr->frames_since_last++;
|
||||
// Turn off replay mode after we reached the final frame position
|
||||
if (++replay_frame_counter_ >= total_frames_) {
|
||||
replay_mode_ = false;
|
||||
if (++sr->replay_frame_counter >= sr->total_frames) {
|
||||
sr->replay_mode = false;
|
||||
}
|
||||
return last_inputs_;
|
||||
return sr->last_inputs;
|
||||
}
|
||||
|
||||
StateRecorder input_recorder;
|
||||
static int frame_ctr;
|
||||
|
||||
int IncrementCrystalCountdown(uint8 *a, int v) {
|
||||
@@ -619,24 +609,24 @@ bool RunOneFrame(Snes *snes, int input_state, bool turbo) {
|
||||
}
|
||||
|
||||
// Either copy state or apply state
|
||||
if (input_recorder.is_replay_mode()) {
|
||||
input_state = input_recorder.ReadNextReplayState();
|
||||
if (state_recorder.replay_mode) {
|
||||
input_state = StateRecorder_ReadNextReplayState(&state_recorder);
|
||||
} else {
|
||||
input_recorder.Record(input_state);
|
||||
StateRecorder_Record(&state_recorder, input_state);
|
||||
turbo = false;
|
||||
|
||||
// This is whether APUI00 is true or false, this is used by the ancilla code.
|
||||
uint8 apui00 = g_zenv.player->port_to_snes[0] != 0;
|
||||
if (apui00 != g_ram[kRam_APUI00]) {
|
||||
g_emulated_ram[kRam_APUI00] = g_ram[kRam_APUI00] = apui00;
|
||||
input_recorder.RecordPatchByte(kRam_APUI00, &apui00, 1);
|
||||
if (apui00 != g_ram[0x648]) {
|
||||
g_emulated_ram[0x648] = g_ram[0x648] = apui00;
|
||||
StateRecorder_RecordPatchByte(&state_recorder, 0x648, &apui00, 1);
|
||||
}
|
||||
|
||||
// Whenever we're no longer replaying, we'll remember what bugs were fixed,
|
||||
// but only if game is initialized.
|
||||
if (g_ram[kRam_BugsFixed] < kBugFix_Latest && animated_tile_data_src != 0) {
|
||||
g_emulated_ram[kRam_BugsFixed] = g_ram[kRam_BugsFixed] = kBugFix_Latest;
|
||||
input_recorder.RecordPatchByte(kRam_BugsFixed, &g_ram[kRam_BugsFixed], 1);
|
||||
StateRecorder_RecordPatchByte(&state_recorder, kRam_BugsFixed, &g_ram[kRam_BugsFixed], 1);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -761,7 +751,6 @@ void PatchRom(uint8_t *rom) {
|
||||
.9B:C049 85 00 mov.w R0, A
|
||||
.9B:C04B 68 pop A
|
||||
.9B:C04C 85 02 mov.w R2, A
|
||||
|
||||
*/
|
||||
uint8_t *tp = rom + 0x6ffd8;
|
||||
*tp++ = 0xa5; *tp++ = 0x00; *tp++ = 0x48;
|
||||
@@ -869,51 +858,72 @@ void SaveLoadSlot(int cmd, int which) {
|
||||
cmd==kSaveLoad_Save ? "Saving" : cmd==kSaveLoad_Load ? "Loading" : "Replaying", which);
|
||||
|
||||
if (cmd != kSaveLoad_Save)
|
||||
input_recorder.Load(f, cmd == kSaveLoad_Replay);
|
||||
StateRecorder_Load(&state_recorder, f, cmd == kSaveLoad_Replay);
|
||||
else
|
||||
input_recorder.Save(f);
|
||||
StateRecorder_Save(&state_recorder, f);
|
||||
|
||||
fclose(f);
|
||||
}
|
||||
}
|
||||
|
||||
class PatchRamByteBatch {
|
||||
public:
|
||||
PatchRamByteBatch() : count_(0), addr_(0) {}
|
||||
~PatchRamByteBatch();
|
||||
void Patch(uint32 addr, uint8 value);
|
||||
|
||||
private:
|
||||
uint32 count_, addr_;
|
||||
uint8 vals_[256];
|
||||
};
|
||||
|
||||
PatchRamByteBatch::~PatchRamByteBatch() {
|
||||
if (count_)
|
||||
input_recorder.RecordPatchByte(addr_, vals_, count_);
|
||||
void ZeldaReadSram(Snes *snes) {
|
||||
FILE *f = fopen("saves/sram.dat", "rb");
|
||||
if (f) {
|
||||
fread(g_zenv.sram, 1, 8192, f);
|
||||
memcpy(snes->cart->ram, g_zenv.sram, 8192);
|
||||
fclose(f);
|
||||
}
|
||||
}
|
||||
|
||||
void PatchRamByteBatch::Patch(uint32 addr, uint8 value) {
|
||||
if (count_ >= 256 || addr != addr_ + count_) {
|
||||
if (count_)
|
||||
input_recorder.RecordPatchByte(addr_, vals_, count_);
|
||||
addr_ = addr;
|
||||
count_ = 0;
|
||||
void ZeldaWriteSram() {
|
||||
rename("saves/sram.dat", "saves/sram.bak");
|
||||
FILE *f = fopen("saves/sram.dat", "wb");
|
||||
if (f) {
|
||||
fwrite(g_zenv.sram, 1, 8192, f);
|
||||
fclose(f);
|
||||
} else {
|
||||
fprintf(stderr, "Unable to write saves/sram.dat\n");
|
||||
}
|
||||
vals_[count_++] = value;
|
||||
}
|
||||
|
||||
typedef struct StateRecoderMultiPatch {
|
||||
uint32 count;
|
||||
uint32 addr;
|
||||
uint8 vals[256];
|
||||
} StateRecoderMultiPatch;
|
||||
|
||||
|
||||
void StateRecoderMultiPatch_Init(StateRecoderMultiPatch *mp) {
|
||||
mp->count = mp->addr = 0;
|
||||
}
|
||||
|
||||
void StateRecoderMultiPatch_Commit(StateRecoderMultiPatch *mp) {
|
||||
if (mp->count)
|
||||
StateRecorder_RecordPatchByte(&state_recorder, mp->addr, mp->vals, mp->count);
|
||||
}
|
||||
|
||||
void StateRecoderMultiPatch_Patch(StateRecoderMultiPatch *mp, uint32 addr, uint8 value) {
|
||||
if (mp->count >= 256 || addr != mp->addr + mp->count) {
|
||||
StateRecoderMultiPatch_Commit(mp);
|
||||
mp->addr = addr;
|
||||
mp->count = 0;
|
||||
}
|
||||
mp->vals[mp->count++] = value;
|
||||
g_emulated_ram[addr] = g_ram[addr] = value;
|
||||
}
|
||||
|
||||
void PatchCommand(char c) {
|
||||
PatchRamByteBatch b;
|
||||
StateRecoderMultiPatch mp;
|
||||
|
||||
StateRecoderMultiPatch_Init(&mp);
|
||||
if (c == 'w') {
|
||||
b.Patch(0xf372, 80); // health filler
|
||||
b.Patch(0xf373, 80); // magic filler
|
||||
// b.Patch(0x1FE01, 25);
|
||||
StateRecoderMultiPatch_Patch(&mp, 0xf372, 80); // health filler
|
||||
StateRecoderMultiPatch_Patch(&mp, 0xf373, 80); // magic filler
|
||||
// b.Patch(0x1FE01, 25);
|
||||
} else if (c == 'k') {
|
||||
input_recorder.MigrateToBaseSnapshot();
|
||||
StateRecorder_MigrateToBaseSnapshot(&state_recorder);
|
||||
} else if (c == 'o') {
|
||||
b.Patch(0xf36f, 1);
|
||||
StateRecoderMultiPatch_Patch(&mp, 0xf36f, 1);
|
||||
}
|
||||
StateRecoderMultiPatch_Commit(&mp);
|
||||
}
|
||||
@@ -3,5 +3,6 @@
|
||||
|
||||
uint8 *GetPtr(uint32 addr);
|
||||
|
||||
void RunEmulatedFuncSilent(uint32 pc, uint16 a, uint16 x, uint16 y, bool mf, bool xf, int b = -1, int whatflags = 0);
|
||||
void RunEmulatedFunc(uint32 pc, uint16 a, uint16 x, uint16 y, bool mf, bool xf, int b = -1, int whatflags = 0);
|
||||
void RunEmulatedFuncSilent(uint32 pc, uint16 a, uint16 x, uint16 y, bool mf, bool xf, int b, int whatflags);
|
||||
void RunEmulatedFunc(uint32 pc, uint16 a, uint16 x, uint16 y, bool mf, bool xf, int b, int whatflags);
|
||||
|
||||
|
||||
@@ -4,20 +4,19 @@
|
||||
#include "nmi.h"
|
||||
#include "poly.h"
|
||||
#include "attract.h"
|
||||
#include "spc_player.h"
|
||||
#include "snes/ppu.h"
|
||||
|
||||
ZeldaEnv g_zenv;
|
||||
// These point to the rewritten instance of the emu.
|
||||
uint8 g_ram[131072];
|
||||
struct SimpleHdma {
|
||||
typedef struct SimpleHdma {
|
||||
const uint8 *table;
|
||||
const uint8 *indir_ptr;
|
||||
uint8 rep_count;
|
||||
uint8 mode;
|
||||
uint8 ppu_addr;
|
||||
uint8 indir_bank;
|
||||
};
|
||||
} SimpleHdma;
|
||||
void SimpleHdma_Init(SimpleHdma *c, DmaChannel *dc);
|
||||
void SimpleHdma_DoLine(SimpleHdma *c);
|
||||
|
||||
@@ -34,6 +33,11 @@ static const uint8 bAdrOffsets[8][4] = {
|
||||
static const uint8 transferLength[8] = {
|
||||
1, 2, 2, 4, 4, 4, 2, 4
|
||||
};
|
||||
const uint16 kUpperBitmasks[] = { 0x8000, 0x4000, 0x2000, 0x1000, 0x800, 0x400, 0x200, 0x100, 0x80, 0x40, 0x20, 0x10, 8, 4, 2, 1 };
|
||||
const uint8 kLitTorchesColorPlus[] = {31, 8, 4, 0};
|
||||
const uint8 kDungeonCrystalPendantBit[13] = {0, 0, 4, 2, 0, 16, 2, 1, 64, 4, 1, 32, 8};
|
||||
const int8 kGetBestActionToPerformOnTile_x[4] = { 7, 7, -3, 16 };
|
||||
const int8 kGetBestActionToPerformOnTile_y[4] = { 6, 24, 12, 12 };
|
||||
#define AT_WORD(x) (uint8)(x), (x)>>8
|
||||
// direct
|
||||
static const uint8 kAttractDmaTable0[13] = {0x20, AT_WORD(0x00ff), 0x50, AT_WORD(0xe018), 0x50, AT_WORD(0xe018), 1, AT_WORD(0x00ff), 0};
|
||||
@@ -181,7 +185,7 @@ void ZeldaDrawPpuFrame() {
|
||||
}
|
||||
|
||||
void HdmaSetup(uint32 addr6, uint32 addr7, uint8 transfer_unit, uint8 reg6, uint8 reg7, uint8 indirect_bank) {
|
||||
struct Dma *dma = g_zenv.dma;
|
||||
Dma *dma = g_zenv.dma;
|
||||
if (addr6) {
|
||||
dma_write(dma, DMAP6, transfer_unit);
|
||||
dma_write(dma, BBAD6, reg6);
|
||||
58
zelda_rtl.h
58
zelda_rtl.h
@@ -1,3 +1,5 @@
|
||||
#ifndef ZELDA_RTL_H
|
||||
#define ZELDA_RTL_H
|
||||
#pragma once
|
||||
|
||||
#include <stdio.h>
|
||||
@@ -8,60 +10,54 @@
|
||||
|
||||
#include "types.h"
|
||||
#include "snes_regs.h"
|
||||
#include "snes/dma.h"
|
||||
#include "snes/ppu.h"
|
||||
#include "spc_player.h"
|
||||
|
||||
|
||||
struct ZeldaEnv {
|
||||
typedef struct ZeldaEnv {
|
||||
uint8 *ram;
|
||||
uint8 *sram;
|
||||
uint16 *vram;
|
||||
struct Ppu *ppu;
|
||||
struct SpcPlayer *player;
|
||||
struct Dma *dma;
|
||||
};
|
||||
Ppu *ppu;
|
||||
SpcPlayer *player;
|
||||
Dma *dma;
|
||||
} ZeldaEnv;
|
||||
extern ZeldaEnv g_zenv;
|
||||
|
||||
// it's here so that the variables.h can access it
|
||||
extern uint8 g_ram[131072];
|
||||
extern const uint16 kUpperBitmasks[];
|
||||
extern const uint8 kLitTorchesColorPlus[];
|
||||
extern const uint8 kDungeonCrystalPendantBit[];
|
||||
extern const int8 kGetBestActionToPerformOnTile_x[];
|
||||
extern const int8 kGetBestActionToPerformOnTile_y[];
|
||||
|
||||
static inline void zelda_snes_dummy_write(uint32 adr, uint8 val) {}
|
||||
|
||||
|
||||
static inline void zelda_snes_dummy_write(uint32_t adr, uint8_t val) {}
|
||||
|
||||
const uint16 kUpperBitmasks[] = { 0x8000, 0x4000, 0x2000, 0x1000, 0x800, 0x400, 0x200, 0x100, 0x80, 0x40, 0x20, 0x10, 8, 4, 2, 1 };
|
||||
const uint8 kLitTorchesColorPlus[] = {31, 8, 4, 0};
|
||||
const uint8 kDungeonCrystalPendantBit[13] = {0, 0, 4, 2, 0, 16, 2, 1, 64, 4, 1, 32, 8};
|
||||
const int8 kGetBestActionToPerformOnTile_x[4] = { 7, 7, -3, 16 };
|
||||
const int8 kGetBestActionToPerformOnTile_y[4] = { 6, 24, 12, 12 };
|
||||
|
||||
struct MovableBlockData {
|
||||
typedef struct MovableBlockData {
|
||||
uint16 room;
|
||||
uint16 tilemap;
|
||||
};
|
||||
} MovableBlockData;
|
||||
|
||||
struct OamEntSigned {
|
||||
typedef struct OamEntSigned {
|
||||
int8 x, y;
|
||||
uint8 charnum, flags;
|
||||
};
|
||||
} OamEntSigned;
|
||||
|
||||
|
||||
|
||||
#define movable_block_datas ((MovableBlockData*)(g_ram+0xf940))
|
||||
#define oam_buf ((OamEnt*)(g_ram+0x800))
|
||||
|
||||
|
||||
|
||||
|
||||
struct OwScrollVars {
|
||||
typedef struct OwScrollVars {
|
||||
uint16 ystart, yend, xstart, xend;
|
||||
};
|
||||
} OwScrollVars;
|
||||
|
||||
#define ow_scroll_vars0 (*(OwScrollVars*)(g_ram+0x600))
|
||||
#define ow_scroll_vars1 (*(OwScrollVars*)(g_ram+0x608))
|
||||
|
||||
#define ow_scroll_vars0_exit (*(OwScrollVars*)(g_ram+0xC154))
|
||||
|
||||
|
||||
|
||||
|
||||
extern const uint8 kLayoutQuadrantFlags[];
|
||||
extern const uint8 kVariousPacks[16];
|
||||
extern const uint8 kMaxBombsForLevel[];
|
||||
@@ -75,7 +71,7 @@ extern const uint16 kOverworld_OffsetBaseX[64];
|
||||
// forwards
|
||||
|
||||
|
||||
struct MirrorHdmaVars {
|
||||
typedef struct MirrorHdmaVars {
|
||||
uint16 var0;
|
||||
uint16 var1[2];
|
||||
uint16 var3[2];
|
||||
@@ -88,7 +84,7 @@ struct MirrorHdmaVars {
|
||||
uint16 var11;
|
||||
uint16 pad;
|
||||
uint8 ctr2, ctr;
|
||||
};
|
||||
} MirrorHdmaVars;
|
||||
|
||||
|
||||
// Special RAM locations that are unused but I use for compat things.
|
||||
@@ -199,4 +195,8 @@ void ZeldaRunFrame(uint16 input, int run_what);
|
||||
void ClearOamBuffer();
|
||||
void Startup_InitializeMemory();
|
||||
void LoadSongBank(const uint8 *p);
|
||||
void NORETURN Die(const char *error);
|
||||
void ZeldaWriteSram();
|
||||
void ZeldaReadSram(Snes *snes);
|
||||
|
||||
#endif // ZELDA_RTL_H
|
||||
|
||||
Reference in New Issue
Block a user