Compare commits
4 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
24f3845fac | ||
|
|
3d5a36d68e | ||
|
|
30b044de2e | ||
|
|
07e25636ca |
1
.github/ISSUE_TEMPLATE/bugs.yml
vendored
1
.github/ISSUE_TEMPLATE/bugs.yml
vendored
@@ -18,6 +18,5 @@ body:
|
||||
- "Windows"
|
||||
- "Linux"
|
||||
- "Mac"
|
||||
- "Nintendo Switch"
|
||||
validations:
|
||||
required: true
|
||||
|
||||
27
.gitignore
vendored
27
.gitignore
vendored
@@ -7,27 +7,24 @@
|
||||
*.aps
|
||||
/obj/
|
||||
/bin/
|
||||
/assets/overworld/*.yaml
|
||||
/assets/dungeon/*.yaml
|
||||
/assets/img/
|
||||
/assets/old/
|
||||
/zelda3.sfc
|
||||
/zelda3.smc
|
||||
/tables/overworld/*.yaml
|
||||
/tables/dungeon/*.yaml
|
||||
/tables/img/
|
||||
/tables/old/
|
||||
/tables/zelda3.sfc
|
||||
/tables/zelda3.smc
|
||||
/saves/*.sav
|
||||
/saves/sram.dat
|
||||
/saves/sram.bak
|
||||
/zelda3
|
||||
__pycache__
|
||||
/src/*.o
|
||||
/*.o
|
||||
/*.exe
|
||||
/*.out
|
||||
/snes/*.o
|
||||
/msu/
|
||||
/msu/alttp_msu-*.pcm
|
||||
/tmp/
|
||||
/zelda3_assets.dat
|
||||
/SDL2.dll
|
||||
/zelda3.*.ini
|
||||
/zelda3.wiki
|
||||
/sprites-gfx
|
||||
/glsl-shaders
|
||||
/token.txt
|
||||
/tables/zelda3_assets.dat
|
||||
/third_party/tcc/
|
||||
/third_party/SDL2-2.24.0/
|
||||
/SDL2.dll
|
||||
32
LICENSE.txt
32
LICENSE.txt
@@ -20,35 +20,3 @@ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
|
||||
|
||||
This is the license for Opus:
|
||||
|
||||
/* Copyright (c) 2007-2008 CSIRO
|
||||
Copyright (c) 2007-2009 Xiph.Org Foundation
|
||||
Copyright (c) 2008-2009 Gregory Maxwell
|
||||
Written by Jean-Marc Valin and Gregory Maxwell */
|
||||
/*
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions
|
||||
are met:
|
||||
|
||||
- Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
|
||||
- Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
|
||||
OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
36
Makefile
36
Makefile
@@ -1,38 +1,28 @@
|
||||
TARGET_EXEC:=zelda3
|
||||
ROM:=tables/zelda3.sfc
|
||||
SRCS:=$(wildcard src/*.c snes/*.c) third_party/gl_core/gl_core_3_1.c third_party/opus-1.3.1-stripped/opus_decoder_amalgam.c
|
||||
SRCS:=$(wildcard *.c snes/*.c)
|
||||
OBJS:=$(SRCS:%.c=%.o)
|
||||
PYTHON:=/usr/bin/env python3
|
||||
CFLAGS:=$(if $(CFLAGS),$(CFLAGS),-O2 -Werror) -I .
|
||||
CFLAGS:=${CFLAGS} $(shell sdl2-config --cflags) -DSYSTEM_VOLUME_MIXER_AVAILABLE=0
|
||||
CFLAGS:=$(if $(CFLAGS),$(CFLAGS),-O2)
|
||||
|
||||
ifeq (${OS},Windows_NT)
|
||||
WINDRES:=windres
|
||||
RES:=zelda3.res
|
||||
SDLFLAGS:=-Wl,-Bstatic $(shell sdl2-config --static-libs)
|
||||
else
|
||||
SDLFLAGS:=$(shell sdl2-config --libs) -lm
|
||||
endif
|
||||
CFLAGS:=${CFLAGS} $(shell sdl2-config --cflags) -DSYSTEM_VOLUME_MIXER_AVAILABLE=0
|
||||
LDFLAGS:=${LDFLAGS} $(shell sdl2-config --libs)
|
||||
|
||||
.PHONY: all clean clean_obj clean_gen
|
||||
|
||||
all: $(TARGET_EXEC) zelda3_assets.dat
|
||||
$(TARGET_EXEC): $(OBJS) $(RES)
|
||||
$(CC) $^ -o $@ $(LDFLAGS) $(SDLFLAGS)
|
||||
all: $(TARGET_EXEC) tables/zelda3_assets.dat
|
||||
$(TARGET_EXEC): $(OBJS)
|
||||
$(CC) $(OBJS) -o $@ $(LDFLAGS)
|
||||
%.o : %.c
|
||||
$(CC) -c $(CFLAGS) $< -o $@
|
||||
|
||||
$(RES): src/platform/win32/zelda3.rc
|
||||
@echo "Generating Windows resources"
|
||||
@$(WINDRES) $< -O coff -o $@
|
||||
|
||||
zelda3_assets.dat:
|
||||
@echo "Extracting game resources"
|
||||
$(PYTHON) assets/restool.py --extract-from-rom
|
||||
tables/zelda3_assets.dat: tables/dialogue.txt
|
||||
cd tables; $(PYTHON) compile_resources.py ../$(ROM)
|
||||
tables/dialogue.txt:
|
||||
cd tables; $(PYTHON) extract_resources.py ../$(ROM)
|
||||
|
||||
clean: clean_obj clean_gen
|
||||
clean_obj:
|
||||
@$(RM) $(OBJS) $(TARGET_EXEC)
|
||||
$(RM) $(OBJS) $(TARGET_EXEC)
|
||||
clean_gen:
|
||||
@$(RM) $(RES) zelda3_assets.dat tables/zelda3_assets.dat tables/*.txt tables/*.png tables/sprites/*.png tables/*.yaml
|
||||
@rm -rf tables/__pycache__ tables/dungeon tables/img tables/overworld tables/sound
|
||||
$(RM) tables/zelda3_assets.dat
|
||||
|
||||
135
README.md
135
README.md
@@ -14,80 +14,82 @@ You need a copy of the ROM to extract game resources (levels, images). Then once
|
||||
It uses the PPU and DSP implementation from [LakeSnes](https://github.com/elzo-d/LakeSnes), but with lots of speed optimizations.
|
||||
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 spannerism's Zelda 3 JP disassembly and the other ones that documented loads of function names and variables.
|
||||
I got much assistance from spannierism's Zelda 3 JP disassembly and the other ones that documented loads of function names and variables.
|
||||
|
||||
## Additional features
|
||||
|
||||
A bunch of features have been added that are not supported by the original game. Some of them are:
|
||||
|
||||
Support for pixel shaders.
|
||||
|
||||
Support for enhanced aspect ratios of 16:9 or 16:10.
|
||||
|
||||
Higher quality world map.
|
||||
|
||||
Support for MSU audio tracks.
|
||||
Some features have been added that are not supported by the original game.
|
||||
|
||||
Secondary item slot on button X (Hold X in inventory to select).
|
||||
|
||||
Displays max rupees, bombs and arrows with yellow or orange color.
|
||||
|
||||
Extends throwing bombs to four instead of two.
|
||||
|
||||
Support for MSU audio tracks.
|
||||
|
||||
Support for enhanced aspect ratios of 16:9 or 16:10.
|
||||
|
||||
Switching current item with L/R keys.
|
||||
|
||||
## How to Play:
|
||||
Reordering of inventory by pressing Y+Arrows.
|
||||
|
||||
Option 1: Launcher by RadzPrower (windows only) https://github.com/ajohns6/Zelda-3-Launcher
|
||||
Higher quality map screen.
|
||||
|
||||
Option 2: Building it yourself
|
||||
Disable low health beep.
|
||||
|
||||
Visit Wiki for more info on building the project: https://github.com/snesrev/zelda3/wiki
|
||||
Pick up items and destroy pots with Sword.
|
||||
|
||||
## Installing Python & libraries on Windows (required for asset extraction steps)
|
||||
1. Download [Python](https://www.python.org/ftp/python/3.11.1/python-3.11.1-amd64.exe) installer and install with "Add to PATH" checkbox checked
|
||||
2. Open the command prompt
|
||||
3. Type `python -m pip install --upgrade pip pillow pyyaml` and hit enter
|
||||
4. Close the command prompt
|
||||
## Dependencies
|
||||
|
||||
## Compiling on Windows with TCC (1mb Tiny C Compiler)
|
||||
1. Download the project by clicking "Code > Download ZIP" on the github page
|
||||
2. Extract the ZIP to your hard drive
|
||||
3. Place the USA rom named `zelda3.sfc` in the root directory.
|
||||
4. Double-click `extract_assets.bat` in the main dir to create `zelda3_assets.dat` in that same dir
|
||||
5. Download [TCC](https://github.com/FitzRoyX/tinycc/releases/download/tcc_20221020/tcc_20221020.zip) and extract to the "\third_party" subfolder
|
||||
6. Download [SDL2](https://github.com/libsdl-org/SDL/releases/download/release-2.26.3/SDL2-devel-2.26.3-VC.zip) and extract to the "\third_party" subfolder
|
||||
7. Double-click `run_with_tcc.bat` in the main dir to create `zelda3.exe` in that same dir
|
||||
8. Configure with `zelda3.ini` in the main dir
|
||||
- the `libsdl2-dev` library
|
||||
- Windows: automatically installed with NuGet
|
||||
- Ubuntu: `apt install libsdl2-dev`
|
||||
- macOS: `brew install sdl2`
|
||||
- a `tables/zelda3.sfc` US ROM file (for asset extraction step only)
|
||||
- SHA256 : `66871d66be19ad2c34c927d6b14cd8eb6fc3181965b6e517cb361f7316009cfb`.
|
||||
- The `pillow` and `pyyaml` python dependencies used by the assets extractor.
|
||||
- `python -m pip install -r requirements.txt`
|
||||
|
||||
## Compiling on Windows with Visual Studio (4.5gb IDE and compiler)
|
||||
Same Steps 1-4 above<br/>
|
||||
8. Double-click `Zelda3.sln`<br/>
|
||||
9. Install the **Desktop development with C++** workload with the VS Installer if you don't have it already (it should prompt you to do this).<br/>
|
||||
10. Change "debug" to "release" in the top dropdown<br/>
|
||||
12. Choose "build > build Zelda3" in the menu to create `zelda3.exe` in the "/bin/release" subfolder<br/>
|
||||
13. Configure with `zelda3.ini` in the main dir<br/>
|
||||
## Compiling
|
||||
|
||||
## Installing libraries on Linux/MacOS
|
||||
1. Open a terminal
|
||||
2. Install pip if not already installed
|
||||
```sh
|
||||
python3 -m ensurepip
|
||||
```
|
||||
3. Clone the repo and `cd` into it
|
||||
```sh
|
||||
git clone https://github.com/snesrev/zelda3
|
||||
cd zelda3
|
||||
```
|
||||
4. Install requirements using pip
|
||||
```sh
|
||||
python3 -m pip install -r requirements.txt
|
||||
```
|
||||
5. Install SDL2
|
||||
* Ubuntu/Debian `sudo apt install libsdl2-dev`
|
||||
* Fedora Linux `sudo dnf install SDL2-devel`
|
||||
* Arch Linux `sudo pacman -S sdl2`
|
||||
* macOS: `brew install sdl2` (you can get homebrew [here](https://brew.sh/))
|
||||
Look at the wiki at https://github.com/snesrev/zelda3/wiki for more help.
|
||||
|
||||
### Windows
|
||||
First extract and compile resources.
|
||||
|
||||
`cd tables`
|
||||
|
||||
Run `python3 extract_resources.py` to extract resources from the ROM into a more human readable format.
|
||||
|
||||
Run `python3 compile_resources.py` to create a file called `zelda3_assets.dat` that gets loaded by the executable.
|
||||
|
||||
In case you're planning to move the .exe to a different folder, please include `zelda3_assets.dat`.
|
||||
|
||||
Then build the .sln file with Visual Studio.
|
||||
|
||||
### Building with TCC instead of Visual Studio on Windows
|
||||
|
||||
You can also build and run it through TCC in case you don't have Visual Studio. Note that TCC builds without optimizations so the game will run considerably slower.
|
||||
|
||||
|
||||
<details>
|
||||
<summary>
|
||||
Read about how to use TCC here...
|
||||
</summary>
|
||||
Extract the assets as detailed above.
|
||||
|
||||
Unzip [TCC for win64](http://download.savannah.gnu.org/releases/tinycc/tcc-0.9.27-win64-bin.zip) into `third_party/tcc`.
|
||||
|
||||
Unzip [SDL2](https://github.com/libsdl-org/SDL/releases/download/release-2.24.0/SDL2-devel-2.24.0-VC.zip) into `third_party/SDL2-2.24.0`.
|
||||
|
||||
Start `run_with_tcc.bat`. It will perform some basic error checks and then start the game.
|
||||
|
||||
</details>
|
||||
|
||||
|
||||
### Linux/macOS
|
||||
|
||||
## Compiling on Linux/MacOS
|
||||
1. Place your US ROM file named `zelda3.sfc` in `zelda3`
|
||||
2. Compile
|
||||
```sh
|
||||
make
|
||||
```
|
||||
@@ -103,7 +105,7 @@ CC=clang make # specify compiler
|
||||
```
|
||||
</details>
|
||||
|
||||
## Nintendo Switch
|
||||
### Nintendo switch
|
||||
|
||||
You need [DevKitPro](https://devkitpro.org/wiki/Getting_Started) and [Atmosphere](https://github.com/Atmosphere-NX/Atmosphere) installed.
|
||||
|
||||
@@ -115,14 +117,7 @@ make # Add -j$(nproc) to build using all cores ( Optional )
|
||||
nxlink -s zelda3.nro
|
||||
```
|
||||
|
||||
## More Compilation Help
|
||||
|
||||
Look at the wiki at https://github.com/snesrev/zelda3/wiki for more help.
|
||||
|
||||
The ROM needs to be named `zelda3.sfc` and has to be from the US region with this exact SHA256 hash
|
||||
`66871d66be19ad2c34c927d6b14cd8eb6fc3181965b6e517cb361f7316009cfb`
|
||||
|
||||
In case you're planning to move the executable to a different location, please include the file `zelda3_assets.dat`.
|
||||
In case you're planning to move the executable to a different location, please include the file `tables/zelda3_assets.dat`.
|
||||
|
||||
## Usage and controls
|
||||
|
||||
@@ -169,10 +164,10 @@ Additionally, the following commands are available:
|
||||
| Alt+Enter | Toggle Fullscreen |
|
||||
| Shift+F1-F10 | Save snapshot |
|
||||
| Ctrl+F1-F10 | Replay the snapshot |
|
||||
| 1-9 | Load a dungeons playthrough snapshot |
|
||||
| Ctrl+1-9 | Run a dungeons playthrough in turbo mode |
|
||||
| 1-9 | run a dungeons playthrough snapshots |
|
||||
| Ctrl+1-9 | run a dungeons playthrough in turbo mode |
|
||||
|
||||
|
||||
## License
|
||||
|
||||
This project is licensed under the MIT license. See 'LICENSE.txt' for details.
|
||||
This project is licensed under the MIT license. See 'LICENSE.txt' for details.
|
||||
@@ -346,7 +346,7 @@ int Ancilla_AllocHigh() {
|
||||
|
||||
static void Ancilla_SetOam(OamEnt *oam, uint16 x, uint16 y, uint8 charnum, uint8 flags, uint8 big) {
|
||||
uint8 yval = 0xf0;
|
||||
int xt = enhanced_features0 & kFeatures0_ExtendScreen64 ? 0x40 : 0;
|
||||
int xt = enhanced_features0 ? 0x40 : 0;
|
||||
if ((uint16)(x + xt) < 256 + xt * 2 && y < 256) {
|
||||
big |= (x >> 8) & 1;
|
||||
oam->x = x;
|
||||
@@ -362,8 +362,7 @@ static void Ancilla_SetOam(OamEnt *oam, uint16 x, uint16 y, uint8 charnum, uint8
|
||||
static void Ancilla_SetOam_Safe(OamEnt *oam, uint16 x, uint16 y, uint8 charnum, uint8 flags, uint8 big) {
|
||||
uint8 yval = 0xf0;
|
||||
oam->x = x;
|
||||
int xt = enhanced_features0 & kFeatures0_ExtendScreen64 ? 0x48 : 0;
|
||||
if ((uint16)(x + 0x80) < (0x180 + xt)) {
|
||||
if ((uint16)(x + 0x80) < 0x180) {
|
||||
big |= (x >> 8) & 1;
|
||||
if ((uint16)(y + 0x10) < 0x100)
|
||||
yval = y;
|
||||
@@ -3743,7 +3742,7 @@ void Ancilla29_MilestoneItemReceipt(int k) { // 88ca8c
|
||||
}
|
||||
if (!ancilla_arr3[k] && ancilla_item_to_link[k] == 0x20) {
|
||||
ancilla_arr3[k] = 1;
|
||||
palette_sp6r_indoors = 4;
|
||||
palette_sp6 = 4;
|
||||
overworld_palette_aux_or_main = 0x200;
|
||||
Palette_Load_SpriteEnvironment_Dungeon();
|
||||
flag_update_cgram_in_nmi++;
|
||||
@@ -3903,7 +3902,7 @@ lbl_else:
|
||||
ancilla_x_lo[k] = bak0;
|
||||
ancilla_x_hi[k] = bak1;
|
||||
if (breaktowerseal_var4 >= 240) {
|
||||
palette_sp6r_indoors = 0;
|
||||
palette_sp6 = 0;
|
||||
overworld_palette_aux_or_main = 0x200;
|
||||
Palette_Load_SpriteEnvironment_Dungeon();
|
||||
flag_update_cgram_in_nmi++;
|
||||
@@ -4754,10 +4753,12 @@ endif_5:
|
||||
}
|
||||
}
|
||||
|
||||
int AncillaAdd_SomariaBlock(uint8 type, uint8 y) { // 88e078
|
||||
void AncillaAdd_SomariaBlock(uint8 type, uint8 y) { // 88e078
|
||||
int k = AncillaAdd_AddAncilla_Bank08(type, y);
|
||||
if (k < 0)
|
||||
return k;
|
||||
if (k < 0) {
|
||||
Refund_Magic(4);
|
||||
return;
|
||||
}
|
||||
for (int j = 4; j >= 0; j--) {
|
||||
if (j == k || ancilla_type[j] != 0x2c)
|
||||
continue;
|
||||
@@ -4770,7 +4771,7 @@ int AncillaAdd_SomariaBlock(uint8 type, uint8 y) { // 88e078
|
||||
bitmask_of_dragstate = 0;
|
||||
link_speed_setting = 0;
|
||||
}
|
||||
return k;
|
||||
return;
|
||||
}
|
||||
|
||||
Ancilla_Sfx3_Near(0x2a);
|
||||
@@ -4803,7 +4804,6 @@ int AncillaAdd_SomariaBlock(uint8 type, uint8 y) { // 88e078
|
||||
Ancilla_SetY(k, link_y_coord + kCaneOfSomaria_Y[j]);
|
||||
SomariaBlock_CheckForTransitTile(k);
|
||||
}
|
||||
return k;
|
||||
}
|
||||
|
||||
void SomariaBlock_CheckForTransitTile(int k) { // 88e191
|
||||
@@ -5165,7 +5165,7 @@ void Ancilla2E_SomariaBlockFission(int k) { // 88eb3e
|
||||
}
|
||||
Point16U pt;
|
||||
Ancilla_PrepAdjustedOamCoord(k, &pt);
|
||||
OamEnt *oam = GetOamCurPtr();
|
||||
OamEnt *oam = GetOamCurPtr(), *oam_org = oam;
|
||||
|
||||
int8 z = ancilla_z[k] + (ancilla_K[k] == 3 && BYTE(link_z_coord) != 0xff ? BYTE(link_z_coord) : 0);
|
||||
int j = ancilla_item_to_link[k] * 8;
|
||||
@@ -5377,11 +5377,8 @@ void Ancilla3A_BigBombExplosion(int k) { // 88f18d
|
||||
}
|
||||
}
|
||||
if (ancilla_item_to_link[k] == 3 && ancilla_arr3[k] == 1) {
|
||||
// Changed so this is reset elsewhere. Some code depends on the value 13.
|
||||
uint8 old = (enhanced_features0 & kFeatures0_MiscBugFixes) ? follower_indicator : 0;
|
||||
follower_indicator = 13;
|
||||
Bomb_CheckForDestructibles(Ancilla_GetX(k), Ancilla_GetY(k), 0); // r14?
|
||||
follower_indicator = old;
|
||||
follower_indicator = 0;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5487,9 +5484,6 @@ void RevivalFairy_MonitorHP() { // 88f430
|
||||
} else if (link_is_bunny) {
|
||||
link_player_handler_state = kPlayerState_PermaBunny;
|
||||
link_is_bunny_mirror = 1;
|
||||
//bugfix: dying as permabunny doesn't restore link palette during death animation
|
||||
if (enhanced_features0 & kFeatures0_MiscBugFixes)
|
||||
LoadGearPalettes_bunny();
|
||||
} else {
|
||||
link_player_handler_state = kPlayerState_Ground;
|
||||
}
|
||||
@@ -5926,6 +5920,7 @@ void AncillaAdd_TossedPondItem(uint8 a, uint8 xin, uint8 yin) { // 898a32
|
||||
ancilla_z[k] = 0;
|
||||
ancilla_timer[k] = 16;
|
||||
ancilla_item_to_link[k] = link_receiveitem_index;
|
||||
int j = link_receiveitem_index;
|
||||
Ancilla_SetXY(k,
|
||||
link_x_coord + kWishPondItem_X[link_receiveitem_index],
|
||||
link_y_coord + kWishPondItem_Y[link_receiveitem_index]);
|
||||
@@ -6092,7 +6087,7 @@ void AncillaAdd_SomariaPlatformPoof(int k) { // 898dd2
|
||||
Player_TileDetectNearby();
|
||||
}
|
||||
|
||||
int AncillaAdd_SuperBombExplosion(uint8 a, uint8 y) { // 898df9
|
||||
void AncillaAdd_SuperBombExplosion(uint8 a, uint8 y) { // 898df9
|
||||
int k = Ancilla_AddAncilla(a, y);
|
||||
if (k >= 0) {
|
||||
ancilla_R[k] = 0;
|
||||
@@ -6106,7 +6101,6 @@ int AncillaAdd_SuperBombExplosion(uint8 a, uint8 y) { // 898df9
|
||||
int x = tagalong_x_lo[j] | tagalong_x_hi[j] << 8;
|
||||
Ancilla_SetXY(k, x + 8, y + 16);
|
||||
}
|
||||
return k;
|
||||
}
|
||||
|
||||
void ConfigureRevivalAncillae() { // 898e4e
|
||||
@@ -6370,6 +6364,8 @@ void AncillaAdd_SwordSwingSparkle(uint8 a, uint8 y) { // 8993c2
|
||||
}
|
||||
|
||||
void AncillaAdd_DashTremor(uint8 a, uint8 y) { // 8993f3
|
||||
static const uint8 kAddDashingDust_X[4] = {4, 4, 6, 0};
|
||||
static const uint8 kAddDashingDust_Y[4] = {20, 4, 16, 16};
|
||||
static const uint8 kAddDashTremor_Dir[4] = {2, 2, 0, 0};
|
||||
static const uint8 kAddDashTremor_Tab[2] = {0x80, 0x78};
|
||||
if (AncillaAdd_CheckForPresence(a))
|
||||
@@ -6717,7 +6713,7 @@ void AncillaAdd_GTCutscene() { // 899b83
|
||||
for (int i = 0x17; i >= 0; i--)
|
||||
breaktowerseal_sparkle_var1[i] = 0xff;
|
||||
DecodeAnimatedSpriteTile_variable(0x28);
|
||||
palette_sp6r_indoors = 4;
|
||||
palette_sp6 = 4;
|
||||
overworld_palette_aux_or_main = 0x200;
|
||||
Palette_Load_SpriteEnvironment_Dungeon();
|
||||
flag_update_cgram_in_nmi++;
|
||||
@@ -173,7 +173,7 @@ void Ancilla31_ByrnaSpark(int k);
|
||||
void Ancilla_SwordBeam(int k);
|
||||
void Ancilla0D_SpinAttackFullChargeSpark(int k);
|
||||
void Ancilla27_Duck(int k);
|
||||
int AncillaAdd_SomariaBlock(uint8 type, uint8 y);
|
||||
void AncillaAdd_SomariaBlock(uint8 type, uint8 y);
|
||||
void SomariaBlock_CheckForTransitTile(int k);
|
||||
int Ancilla_CheckBasicSpriteCollision(int k);
|
||||
bool Ancilla_CheckBasicSpriteCollision_Single(int k, int j);
|
||||
@@ -219,7 +219,7 @@ void AncillaAdd_ChargedSpinAttackSparkle();
|
||||
void AncillaAdd_ExplodingWeatherVane(uint8 a, uint8 y);
|
||||
void AncillaAdd_CutsceneDuck(uint8 a, uint8 y);
|
||||
void AncillaAdd_SomariaPlatformPoof(int k);
|
||||
int AncillaAdd_SuperBombExplosion(uint8 a, uint8 y);
|
||||
void AncillaAdd_SuperBombExplosion(uint8 a, uint8 y);
|
||||
void ConfigureRevivalAncillae();
|
||||
void AncillaAdd_LampFlame(uint8 a, uint8 y);
|
||||
void AncillaAdd_MSCutscene(uint8 a, uint8 y);
|
||||
@@ -1,332 +1,339 @@
|
||||
#pragma once
|
||||
#include "types.h"
|
||||
|
||||
enum {
|
||||
kNumberOfAssets = 165
|
||||
};
|
||||
extern const uint8 *g_asset_ptrs[kNumberOfAssets];
|
||||
extern uint32 g_asset_sizes[kNumberOfAssets];
|
||||
extern MemBlk FindInAssetArray(int asset, int idx);
|
||||
|
||||
#define kSoundBank_intro ((uint8*)g_asset_ptrs[0])
|
||||
#define kSoundBank_intro_SIZE (g_asset_sizes[0])
|
||||
#define kSoundBank_indoor ((uint8*)g_asset_ptrs[1])
|
||||
#define kSoundBank_indoor_SIZE (g_asset_sizes[1])
|
||||
#define kSoundBank_ending ((uint8*)g_asset_ptrs[2])
|
||||
#define kSoundBank_ending_SIZE (g_asset_sizes[2])
|
||||
#define kDungeonRoom ((uint8*)g_asset_ptrs[3])
|
||||
#define kDungeonRoom_SIZE (g_asset_sizes[3])
|
||||
#define kDungeonRoomOffs ((uint16*)g_asset_ptrs[4])
|
||||
#define kDungeonRoomOffs_SIZE (g_asset_sizes[4])
|
||||
#define kDungeonRoomDoorOffs ((uint16*)g_asset_ptrs[5])
|
||||
#define kDungeonRoomDoorOffs_SIZE (g_asset_sizes[5])
|
||||
#define kDungeonRoomHeaders ((uint8*)g_asset_ptrs[6])
|
||||
#define kDungeonRoomHeaders_SIZE (g_asset_sizes[6])
|
||||
#define kDungeonRoomHeadersOffs ((uint16*)g_asset_ptrs[7])
|
||||
#define kDungeonRoomHeadersOffs_SIZE (g_asset_sizes[7])
|
||||
#define kDungeonRoomChests ((uint8*)g_asset_ptrs[8])
|
||||
#define kDungeonRoomChests_SIZE (g_asset_sizes[8])
|
||||
#define kDungeonRoomTeleMsg ((uint16*)g_asset_ptrs[9])
|
||||
#define kDungeonRoomTeleMsg_SIZE (g_asset_sizes[9])
|
||||
#define kDungeonPitsHurtPlayer ((uint16*)g_asset_ptrs[10])
|
||||
#define kDungeonPitsHurtPlayer_SIZE (g_asset_sizes[10])
|
||||
#define kEntranceData_rooms ((uint16*)g_asset_ptrs[11])
|
||||
#define kEntranceData_rooms_SIZE (g_asset_sizes[11])
|
||||
#define kEntranceData_relativeCoords ((uint8*)g_asset_ptrs[12])
|
||||
#define kEntranceData_relativeCoords_SIZE (g_asset_sizes[12])
|
||||
#define kEntranceData_scrollX ((uint16*)g_asset_ptrs[13])
|
||||
#define kEntranceData_scrollX_SIZE (g_asset_sizes[13])
|
||||
#define kEntranceData_scrollY ((uint16*)g_asset_ptrs[14])
|
||||
#define kEntranceData_scrollY_SIZE (g_asset_sizes[14])
|
||||
#define kEntranceData_playerX ((uint16*)g_asset_ptrs[15])
|
||||
#define kEntranceData_playerX_SIZE (g_asset_sizes[15])
|
||||
#define kEntranceData_playerY ((uint16*)g_asset_ptrs[16])
|
||||
#define kEntranceData_playerY_SIZE (g_asset_sizes[16])
|
||||
#define kEntranceData_cameraX ((uint16*)g_asset_ptrs[17])
|
||||
#define kEntranceData_cameraX_SIZE (g_asset_sizes[17])
|
||||
#define kEntranceData_cameraY ((uint16*)g_asset_ptrs[18])
|
||||
#define kEntranceData_cameraY_SIZE (g_asset_sizes[18])
|
||||
#define kEntranceData_blockset ((uint8*)g_asset_ptrs[19])
|
||||
#define kEntranceData_blockset_SIZE (g_asset_sizes[19])
|
||||
#define kEntranceData_floor ((int8*)g_asset_ptrs[20])
|
||||
#define kEntranceData_floor_SIZE (g_asset_sizes[20])
|
||||
#define kEntranceData_palace ((int8*)g_asset_ptrs[21])
|
||||
#define kEntranceData_palace_SIZE (g_asset_sizes[21])
|
||||
#define kEntranceData_doorwayOrientation ((uint8*)g_asset_ptrs[22])
|
||||
#define kEntranceData_doorwayOrientation_SIZE (g_asset_sizes[22])
|
||||
#define kEntranceData_startingBg ((uint8*)g_asset_ptrs[23])
|
||||
#define kEntranceData_startingBg_SIZE (g_asset_sizes[23])
|
||||
#define kEntranceData_quadrant1 ((uint8*)g_asset_ptrs[24])
|
||||
#define kEntranceData_quadrant1_SIZE (g_asset_sizes[24])
|
||||
#define kEntranceData_quadrant2 ((uint8*)g_asset_ptrs[25])
|
||||
#define kEntranceData_quadrant2_SIZE (g_asset_sizes[25])
|
||||
#define kEntranceData_doorSettings ((uint16*)g_asset_ptrs[26])
|
||||
#define kEntranceData_doorSettings_SIZE (g_asset_sizes[26])
|
||||
#define kEntranceData_musicTrack ((uint8*)g_asset_ptrs[27])
|
||||
#define kEntranceData_musicTrack_SIZE (g_asset_sizes[27])
|
||||
#define kStartingPoint_rooms ((uint16*)g_asset_ptrs[28])
|
||||
#define kStartingPoint_rooms_SIZE (g_asset_sizes[28])
|
||||
#define kStartingPoint_relativeCoords ((uint8*)g_asset_ptrs[29])
|
||||
#define kStartingPoint_relativeCoords_SIZE (g_asset_sizes[29])
|
||||
#define kStartingPoint_scrollX ((uint16*)g_asset_ptrs[30])
|
||||
#define kStartingPoint_scrollX_SIZE (g_asset_sizes[30])
|
||||
#define kStartingPoint_scrollY ((uint16*)g_asset_ptrs[31])
|
||||
#define kStartingPoint_scrollY_SIZE (g_asset_sizes[31])
|
||||
#define kStartingPoint_playerX ((uint16*)g_asset_ptrs[32])
|
||||
#define kStartingPoint_playerX_SIZE (g_asset_sizes[32])
|
||||
#define kStartingPoint_playerY ((uint16*)g_asset_ptrs[33])
|
||||
#define kStartingPoint_playerY_SIZE (g_asset_sizes[33])
|
||||
#define kStartingPoint_cameraX ((uint16*)g_asset_ptrs[34])
|
||||
#define kStartingPoint_cameraX_SIZE (g_asset_sizes[34])
|
||||
#define kStartingPoint_cameraY ((uint16*)g_asset_ptrs[35])
|
||||
#define kStartingPoint_cameraY_SIZE (g_asset_sizes[35])
|
||||
#define kStartingPoint_blockset ((uint8*)g_asset_ptrs[36])
|
||||
#define kStartingPoint_blockset_SIZE (g_asset_sizes[36])
|
||||
#define kStartingPoint_floor ((int8*)g_asset_ptrs[37])
|
||||
#define kStartingPoint_floor_SIZE (g_asset_sizes[37])
|
||||
#define kStartingPoint_palace ((int8*)g_asset_ptrs[38])
|
||||
#define kStartingPoint_palace_SIZE (g_asset_sizes[38])
|
||||
#define kStartingPoint_doorwayOrientation ((uint8*)g_asset_ptrs[39])
|
||||
#define kStartingPoint_doorwayOrientation_SIZE (g_asset_sizes[39])
|
||||
#define kStartingPoint_startingBg ((uint8*)g_asset_ptrs[40])
|
||||
#define kStartingPoint_startingBg_SIZE (g_asset_sizes[40])
|
||||
#define kStartingPoint_quadrant1 ((uint8*)g_asset_ptrs[41])
|
||||
#define kStartingPoint_quadrant1_SIZE (g_asset_sizes[41])
|
||||
#define kStartingPoint_quadrant2 ((uint8*)g_asset_ptrs[42])
|
||||
#define kStartingPoint_quadrant2_SIZE (g_asset_sizes[42])
|
||||
#define kStartingPoint_doorSettings ((uint16*)g_asset_ptrs[43])
|
||||
#define kStartingPoint_doorSettings_SIZE (g_asset_sizes[43])
|
||||
#define kStartingPoint_entrance ((uint8*)g_asset_ptrs[44])
|
||||
#define kStartingPoint_entrance_SIZE (g_asset_sizes[44])
|
||||
#define kStartingPoint_musicTrack ((uint8*)g_asset_ptrs[45])
|
||||
#define kStartingPoint_musicTrack_SIZE (g_asset_sizes[45])
|
||||
#define kDungeonRoomDefault ((uint8*)g_asset_ptrs[46])
|
||||
#define kDungeonRoomDefault_SIZE (g_asset_sizes[46])
|
||||
#define kDungeonRoomDefaultOffs ((uint16*)g_asset_ptrs[47])
|
||||
#define kDungeonRoomDefaultOffs_SIZE (g_asset_sizes[47])
|
||||
#define kDungeonRoomOverlay ((uint8*)g_asset_ptrs[48])
|
||||
#define kDungeonRoomOverlay_SIZE (g_asset_sizes[48])
|
||||
#define kDungeonRoomOverlayOffs ((uint16*)g_asset_ptrs[49])
|
||||
#define kDungeonRoomOverlayOffs_SIZE (g_asset_sizes[49])
|
||||
#define kDungeonSecrets ((uint8*)g_asset_ptrs[50])
|
||||
#define kDungeonSecrets_SIZE (g_asset_sizes[50])
|
||||
#define kDungAttrsForTile_Offs ((uint16*)g_asset_ptrs[51])
|
||||
#define kDungAttrsForTile_Offs_SIZE (g_asset_sizes[51])
|
||||
#define kDungAttrsForTile ((uint8*)g_asset_ptrs[52])
|
||||
#define kDungAttrsForTile_SIZE (g_asset_sizes[52])
|
||||
#define kMovableBlockDataInit ((uint16*)g_asset_ptrs[53])
|
||||
#define kMovableBlockDataInit_SIZE (g_asset_sizes[53])
|
||||
#define kTorchDataInit ((uint16*)g_asset_ptrs[54])
|
||||
#define kTorchDataInit_SIZE (g_asset_sizes[54])
|
||||
#define kTorchDataJunk ((uint16*)g_asset_ptrs[55])
|
||||
#define kTorchDataJunk_SIZE (g_asset_sizes[55])
|
||||
#define kEnemyDamageData ((uint8*)g_asset_ptrs[56])
|
||||
#define kEnemyDamageData_SIZE (g_asset_sizes[56])
|
||||
#define kLinkGraphics ((uint8*)g_asset_ptrs[57])
|
||||
#define kLinkGraphics_SIZE (g_asset_sizes[57])
|
||||
#define kDungeonSprites ((uint8*)g_asset_ptrs[58])
|
||||
#define kDungeonSprites_SIZE (g_asset_sizes[58])
|
||||
#define kDungeonSpriteOffs ((uint16*)g_asset_ptrs[59])
|
||||
#define kDungeonSpriteOffs_SIZE (g_asset_sizes[59])
|
||||
#define kMap32ToMap16_0 ((uint8*)g_asset_ptrs[60])
|
||||
#define kMap32ToMap16_0_SIZE (g_asset_sizes[60])
|
||||
#define kMap32ToMap16_1 ((uint8*)g_asset_ptrs[61])
|
||||
#define kMap32ToMap16_1_SIZE (g_asset_sizes[61])
|
||||
#define kMap32ToMap16_2 ((uint8*)g_asset_ptrs[62])
|
||||
#define kMap32ToMap16_2_SIZE (g_asset_sizes[62])
|
||||
#define kMap32ToMap16_3 ((uint8*)g_asset_ptrs[63])
|
||||
#define kMap32ToMap16_3_SIZE (g_asset_sizes[63])
|
||||
#define kSprGfx(idx) FindInAssetArray(64, idx)
|
||||
#define kBgGfx(idx) FindInAssetArray(65, idx)
|
||||
#define kOverworldMapGfx ((uint8*)g_asset_ptrs[66])
|
||||
#define kOverworldMapGfx_SIZE (g_asset_sizes[66])
|
||||
#define kLightOverworldTilemap ((uint8*)g_asset_ptrs[67])
|
||||
#define kLightOverworldTilemap_SIZE (g_asset_sizes[67])
|
||||
#define kDarkOverworldTilemap ((uint8*)g_asset_ptrs[68])
|
||||
#define kDarkOverworldTilemap_SIZE (g_asset_sizes[68])
|
||||
#define kPredefinedTileData ((uint16*)g_asset_ptrs[69])
|
||||
#define kPredefinedTileData_SIZE (g_asset_sizes[69])
|
||||
#define kMap16ToMap8 ((uint16*)g_asset_ptrs[70])
|
||||
#define kMap16ToMap8_SIZE (g_asset_sizes[70])
|
||||
#define kGeneratedWishPondItem ((uint8*)g_asset_ptrs[71])
|
||||
#define kGeneratedWishPondItem_SIZE (g_asset_sizes[71])
|
||||
#define kGeneratedBombosArr ((uint8*)g_asset_ptrs[72])
|
||||
#define kGeneratedBombosArr_SIZE (g_asset_sizes[72])
|
||||
#define kGeneratedEndSequence15 ((uint8*)g_asset_ptrs[73])
|
||||
#define kGeneratedEndSequence15_SIZE (g_asset_sizes[73])
|
||||
#define kEnding_Credits_Text ((uint8*)g_asset_ptrs[74])
|
||||
#define kEnding_Credits_Text_SIZE (g_asset_sizes[74])
|
||||
#define kEnding_Credits_Offs ((uint16*)g_asset_ptrs[75])
|
||||
#define kEnding_Credits_Offs_SIZE (g_asset_sizes[75])
|
||||
#define kEnding_MapData ((uint16*)g_asset_ptrs[76])
|
||||
#define kEnding_MapData_SIZE (g_asset_sizes[76])
|
||||
#define kEnding0_Offs ((uint16*)g_asset_ptrs[77])
|
||||
#define kEnding0_Offs_SIZE (g_asset_sizes[77])
|
||||
#define kEnding0_Data ((uint8*)g_asset_ptrs[78])
|
||||
#define kEnding0_Data_SIZE (g_asset_sizes[78])
|
||||
#define kPalette_DungBgMain ((uint16*)g_asset_ptrs[79])
|
||||
#define kPalette_DungBgMain_SIZE (g_asset_sizes[79])
|
||||
#define kPalette_MainSpr ((uint16*)g_asset_ptrs[80])
|
||||
#define kPalette_MainSpr_SIZE (g_asset_sizes[80])
|
||||
#define kPalette_ArmorAndGloves ((uint16*)g_asset_ptrs[81])
|
||||
#define kPalette_ArmorAndGloves_SIZE (g_asset_sizes[81])
|
||||
#define kPalette_Sword ((uint16*)g_asset_ptrs[82])
|
||||
#define kPalette_Sword_SIZE (g_asset_sizes[82])
|
||||
#define kPalette_Shield ((uint16*)g_asset_ptrs[83])
|
||||
#define kPalette_Shield_SIZE (g_asset_sizes[83])
|
||||
#define kPalette_SpriteAux3 ((uint16*)g_asset_ptrs[84])
|
||||
#define kPalette_SpriteAux3_SIZE (g_asset_sizes[84])
|
||||
#define kPalette_MiscSprite_Indoors ((uint16*)g_asset_ptrs[85])
|
||||
#define kPalette_MiscSprite_Indoors_SIZE (g_asset_sizes[85])
|
||||
#define kPalette_SpriteAux1 ((uint16*)g_asset_ptrs[86])
|
||||
#define kPalette_SpriteAux1_SIZE (g_asset_sizes[86])
|
||||
#define kPalette_OverworldBgMain ((uint16*)g_asset_ptrs[87])
|
||||
#define kPalette_OverworldBgMain_SIZE (g_asset_sizes[87])
|
||||
#define kPalette_OverworldBgAux12 ((uint16*)g_asset_ptrs[88])
|
||||
#define kPalette_OverworldBgAux12_SIZE (g_asset_sizes[88])
|
||||
#define kPalette_OverworldBgAux3 ((uint16*)g_asset_ptrs[89])
|
||||
#define kPalette_OverworldBgAux3_SIZE (g_asset_sizes[89])
|
||||
#define kPalette_PalaceMapBg ((uint16*)g_asset_ptrs[90])
|
||||
#define kPalette_PalaceMapBg_SIZE (g_asset_sizes[90])
|
||||
#define kPalette_PalaceMapSpr ((uint16*)g_asset_ptrs[91])
|
||||
#define kPalette_PalaceMapSpr_SIZE (g_asset_sizes[91])
|
||||
#define kHudPalData ((uint16*)g_asset_ptrs[92])
|
||||
#define kHudPalData_SIZE (g_asset_sizes[92])
|
||||
#define kOverworldMapPaletteData ((uint16*)g_asset_ptrs[93])
|
||||
#define kOverworldMapPaletteData_SIZE (g_asset_sizes[93])
|
||||
#define kDialogue(idx) FindInAssetArray(94, idx)
|
||||
#define kDialogueFont(idx) FindInAssetArray(95, idx)
|
||||
#define kDialogueMap(idx) FindInAssetArray(96, idx)
|
||||
#define kDungMap_FloorLayout(idx) FindInAssetArray(97, idx)
|
||||
#define kDungMap_Tiles(idx) FindInAssetArray(98, idx)
|
||||
#define kBgTilemap_0 ((uint8*)g_asset_ptrs[99])
|
||||
#define kBgTilemap_0_SIZE (g_asset_sizes[99])
|
||||
#define kBgTilemap_1 ((uint8*)g_asset_ptrs[100])
|
||||
#define kBgTilemap_1_SIZE (g_asset_sizes[100])
|
||||
#define kBgTilemap_2 ((uint8*)g_asset_ptrs[101])
|
||||
#define kBgTilemap_2_SIZE (g_asset_sizes[101])
|
||||
#define kBgTilemap_3 ((uint8*)g_asset_ptrs[102])
|
||||
#define kBgTilemap_3_SIZE (g_asset_sizes[102])
|
||||
#define kBgTilemap_4 ((uint8*)g_asset_ptrs[103])
|
||||
#define kBgTilemap_4_SIZE (g_asset_sizes[103])
|
||||
#define kBgTilemap_5 ((uint8*)g_asset_ptrs[104])
|
||||
#define kBgTilemap_5_SIZE (g_asset_sizes[104])
|
||||
#define kOverworld_Hibytes_Comp(idx) FindInAssetArray(105, idx)
|
||||
#define kOverworld_Lobytes_Comp(idx) FindInAssetArray(106, idx)
|
||||
#define kOverworldMapIsSmall ((uint8*)g_asset_ptrs[107])
|
||||
#define kOverworldMapIsSmall_SIZE (g_asset_sizes[107])
|
||||
#define kOverworldAuxTileThemeIndexes ((uint8*)g_asset_ptrs[108])
|
||||
#define kOverworldAuxTileThemeIndexes_SIZE (g_asset_sizes[108])
|
||||
#define kOverworldBgPalettes ((uint8*)g_asset_ptrs[109])
|
||||
#define kOverworldBgPalettes_SIZE (g_asset_sizes[109])
|
||||
#define kOverworld_SignText ((uint16*)g_asset_ptrs[110])
|
||||
#define kOverworld_SignText_SIZE (g_asset_sizes[110])
|
||||
#define kOwMusicSets ((uint8*)g_asset_ptrs[111])
|
||||
#define kOwMusicSets_SIZE (g_asset_sizes[111])
|
||||
#define kOwMusicSets2 ((uint8*)g_asset_ptrs[112])
|
||||
#define kOwMusicSets2_SIZE (g_asset_sizes[112])
|
||||
#define kBirdTravel_ScreenIndex ((uint16*)g_asset_ptrs[113])
|
||||
#define kBirdTravel_ScreenIndex_SIZE (g_asset_sizes[113])
|
||||
#define kBirdTravel_Map16LoadSrcOff ((uint16*)g_asset_ptrs[114])
|
||||
#define kBirdTravel_Map16LoadSrcOff_SIZE (g_asset_sizes[114])
|
||||
#define kBirdTravel_ScrollX ((uint16*)g_asset_ptrs[115])
|
||||
#define kBirdTravel_ScrollX_SIZE (g_asset_sizes[115])
|
||||
#define kBirdTravel_ScrollY ((uint16*)g_asset_ptrs[116])
|
||||
#define kBirdTravel_ScrollY_SIZE (g_asset_sizes[116])
|
||||
#define kBirdTravel_LinkXCoord ((uint16*)g_asset_ptrs[117])
|
||||
#define kBirdTravel_LinkXCoord_SIZE (g_asset_sizes[117])
|
||||
#define kBirdTravel_LinkYCoord ((uint16*)g_asset_ptrs[118])
|
||||
#define kBirdTravel_LinkYCoord_SIZE (g_asset_sizes[118])
|
||||
#define kBirdTravel_CameraXScroll ((uint16*)g_asset_ptrs[119])
|
||||
#define kBirdTravel_CameraXScroll_SIZE (g_asset_sizes[119])
|
||||
#define kBirdTravel_CameraYScroll ((uint16*)g_asset_ptrs[120])
|
||||
#define kBirdTravel_CameraYScroll_SIZE (g_asset_sizes[120])
|
||||
#define kBirdTravel_Unk1 ((int8*)g_asset_ptrs[121])
|
||||
#define kBirdTravel_Unk1_SIZE (g_asset_sizes[121])
|
||||
#define kBirdTravel_Unk3 ((int8*)g_asset_ptrs[122])
|
||||
#define kBirdTravel_Unk3_SIZE (g_asset_sizes[122])
|
||||
#define kWhirlpoolAreas ((uint16*)g_asset_ptrs[123])
|
||||
#define kWhirlpoolAreas_SIZE (g_asset_sizes[123])
|
||||
#define kOverworld_Entrance_Area ((uint16*)g_asset_ptrs[124])
|
||||
#define kOverworld_Entrance_Area_SIZE (g_asset_sizes[124])
|
||||
#define kOverworld_Entrance_Pos ((uint16*)g_asset_ptrs[125])
|
||||
#define kOverworld_Entrance_Pos_SIZE (g_asset_sizes[125])
|
||||
#define kOverworld_Entrance_Id ((uint8*)g_asset_ptrs[126])
|
||||
#define kOverworld_Entrance_Id_SIZE (g_asset_sizes[126])
|
||||
#define kFallHole_Area ((uint16*)g_asset_ptrs[127])
|
||||
#define kFallHole_Area_SIZE (g_asset_sizes[127])
|
||||
#define kFallHole_Pos ((uint16*)g_asset_ptrs[128])
|
||||
#define kFallHole_Pos_SIZE (g_asset_sizes[128])
|
||||
#define kFallHole_Entrances ((uint8*)g_asset_ptrs[129])
|
||||
#define kFallHole_Entrances_SIZE (g_asset_sizes[129])
|
||||
#define kExitData_ScreenIndex ((uint8*)g_asset_ptrs[130])
|
||||
#define kExitData_ScreenIndex_SIZE (g_asset_sizes[130])
|
||||
#define kExitDataRooms ((uint16*)g_asset_ptrs[131])
|
||||
#define kExitDataRooms_SIZE (g_asset_sizes[131])
|
||||
#define kExitData_Map16LoadSrcOff ((uint16*)g_asset_ptrs[132])
|
||||
#define kExitData_Map16LoadSrcOff_SIZE (g_asset_sizes[132])
|
||||
#define kExitData_ScrollX ((uint16*)g_asset_ptrs[133])
|
||||
#define kExitData_ScrollX_SIZE (g_asset_sizes[133])
|
||||
#define kExitData_ScrollY ((uint16*)g_asset_ptrs[134])
|
||||
#define kExitData_ScrollY_SIZE (g_asset_sizes[134])
|
||||
#define kExitData_XCoord ((uint16*)g_asset_ptrs[135])
|
||||
#define kExitData_XCoord_SIZE (g_asset_sizes[135])
|
||||
#define kExitData_YCoord ((uint16*)g_asset_ptrs[136])
|
||||
#define kExitData_YCoord_SIZE (g_asset_sizes[136])
|
||||
#define kExitData_CameraXScroll ((uint16*)g_asset_ptrs[137])
|
||||
#define kExitData_CameraXScroll_SIZE (g_asset_sizes[137])
|
||||
#define kExitData_CameraYScroll ((uint16*)g_asset_ptrs[138])
|
||||
#define kExitData_CameraYScroll_SIZE (g_asset_sizes[138])
|
||||
#define kExitData_NormalDoor ((uint16*)g_asset_ptrs[139])
|
||||
#define kExitData_NormalDoor_SIZE (g_asset_sizes[139])
|
||||
#define kExitData_FancyDoor ((uint16*)g_asset_ptrs[140])
|
||||
#define kExitData_FancyDoor_SIZE (g_asset_sizes[140])
|
||||
#define kExitData_Unk1 ((int8*)g_asset_ptrs[141])
|
||||
#define kExitData_Unk1_SIZE (g_asset_sizes[141])
|
||||
#define kExitData_Unk3 ((int8*)g_asset_ptrs[142])
|
||||
#define kExitData_Unk3_SIZE (g_asset_sizes[142])
|
||||
#define kSpExit_Top ((uint16*)g_asset_ptrs[143])
|
||||
#define kSpExit_Top_SIZE (g_asset_sizes[143])
|
||||
#define kSpExit_Bottom ((uint16*)g_asset_ptrs[144])
|
||||
#define kSpExit_Bottom_SIZE (g_asset_sizes[144])
|
||||
#define kSpExit_Left ((uint16*)g_asset_ptrs[145])
|
||||
#define kSpExit_Left_SIZE (g_asset_sizes[145])
|
||||
#define kSpExit_Right ((uint16*)g_asset_ptrs[146])
|
||||
#define kSpExit_Right_SIZE (g_asset_sizes[146])
|
||||
#define kSpExit_Tab4 ((int16*)g_asset_ptrs[147])
|
||||
#define kSpExit_Tab4_SIZE (g_asset_sizes[147])
|
||||
#define kSpExit_Tab5 ((int16*)g_asset_ptrs[148])
|
||||
#define kSpExit_Tab5_SIZE (g_asset_sizes[148])
|
||||
#define kSpExit_Tab6 ((int16*)g_asset_ptrs[149])
|
||||
#define kSpExit_Tab6_SIZE (g_asset_sizes[149])
|
||||
#define kSpExit_Tab7 ((int16*)g_asset_ptrs[150])
|
||||
#define kSpExit_Tab7_SIZE (g_asset_sizes[150])
|
||||
#define kSpExit_LeftEdgeOfMap ((uint16*)g_asset_ptrs[151])
|
||||
#define kSpExit_LeftEdgeOfMap_SIZE (g_asset_sizes[151])
|
||||
#define kSpExit_Dir ((uint8*)g_asset_ptrs[152])
|
||||
#define kSpExit_Dir_SIZE (g_asset_sizes[152])
|
||||
#define kSpExit_SprGfx ((uint8*)g_asset_ptrs[153])
|
||||
#define kSpExit_SprGfx_SIZE (g_asset_sizes[153])
|
||||
#define kSpExit_AuxGfx ((uint8*)g_asset_ptrs[154])
|
||||
#define kSpExit_AuxGfx_SIZE (g_asset_sizes[154])
|
||||
#define kSpExit_PalBg ((uint8*)g_asset_ptrs[155])
|
||||
#define kSpExit_PalBg_SIZE (g_asset_sizes[155])
|
||||
#define kSpExit_PalSpr ((uint8*)g_asset_ptrs[156])
|
||||
#define kSpExit_PalSpr_SIZE (g_asset_sizes[156])
|
||||
#define kOverworldSecrets_Offs ((uint16*)g_asset_ptrs[157])
|
||||
#define kOverworldSecrets_Offs_SIZE (g_asset_sizes[157])
|
||||
#define kOverworldSecrets ((uint8*)g_asset_ptrs[158])
|
||||
#define kOverworldSecrets_SIZE (g_asset_sizes[158])
|
||||
#define kOverworldSpriteOffs ((uint16*)g_asset_ptrs[159])
|
||||
#define kOverworldSpriteOffs_SIZE (g_asset_sizes[159])
|
||||
#define kOverworldSprites ((uint8*)g_asset_ptrs[160])
|
||||
#define kOverworldSprites_SIZE (g_asset_sizes[160])
|
||||
#define kOverworldSpriteGfx ((uint8*)g_asset_ptrs[161])
|
||||
#define kOverworldSpriteGfx_SIZE (g_asset_sizes[161])
|
||||
#define kOverworldSpritePalettes ((uint8*)g_asset_ptrs[162])
|
||||
#define kOverworldSpritePalettes_SIZE (g_asset_sizes[162])
|
||||
#define kMap8DataToTileAttr ((uint8*)g_asset_ptrs[163])
|
||||
#define kMap8DataToTileAttr_SIZE (g_asset_sizes[163])
|
||||
#define kSomeTileAttr ((uint8*)g_asset_ptrs[164])
|
||||
#define kSomeTileAttr_SIZE (g_asset_sizes[164])
|
||||
#define kAssets_Sig 90, 101, 108, 100, 97, 51, 95, 118, 48, 32, 32, 32, 32, 32, 10, 0, 27, 174, 233, 45, 74, 174, 252, 50, 49, 27, 153, 197, 27, 43, 216, 197, 132, 101, 173, 169, 36, 108, 15, 155, 176, 169, 57, 131, 174, 101, 51, 207
|
||||
#pragma once
|
||||
#include "types.h"
|
||||
|
||||
enum {
|
||||
kNumberOfAssets = 165
|
||||
};
|
||||
extern const uint8 *g_asset_ptrs[kNumberOfAssets];
|
||||
extern uint32 g_asset_sizes[kNumberOfAssets];
|
||||
#define kSoundBank_intro ((uint8*)g_asset_ptrs[0])
|
||||
#define kSoundBank_intro_SIZE (g_asset_sizes[0])
|
||||
#define kSoundBank_indoor ((uint8*)g_asset_ptrs[1])
|
||||
#define kSoundBank_indoor_SIZE (g_asset_sizes[1])
|
||||
#define kSoundBank_ending ((uint8*)g_asset_ptrs[2])
|
||||
#define kSoundBank_ending_SIZE (g_asset_sizes[2])
|
||||
#define kDungeonRoom ((uint8*)g_asset_ptrs[3])
|
||||
#define kDungeonRoom_SIZE (g_asset_sizes[3])
|
||||
#define kDungeonRoomOffs ((uint16*)g_asset_ptrs[4])
|
||||
#define kDungeonRoomOffs_SIZE (g_asset_sizes[4])
|
||||
#define kDungeonRoomDoorOffs ((uint16*)g_asset_ptrs[5])
|
||||
#define kDungeonRoomDoorOffs_SIZE (g_asset_sizes[5])
|
||||
#define kDungeonRoomHeaders ((uint8*)g_asset_ptrs[6])
|
||||
#define kDungeonRoomHeaders_SIZE (g_asset_sizes[6])
|
||||
#define kDungeonRoomHeadersOffs ((uint16*)g_asset_ptrs[7])
|
||||
#define kDungeonRoomHeadersOffs_SIZE (g_asset_sizes[7])
|
||||
#define kDungeonRoomChests ((uint8*)g_asset_ptrs[8])
|
||||
#define kDungeonRoomChests_SIZE (g_asset_sizes[8])
|
||||
#define kDungeonRoomTeleMsg ((uint16*)g_asset_ptrs[9])
|
||||
#define kDungeonRoomTeleMsg_SIZE (g_asset_sizes[9])
|
||||
#define kDungeonPitsHurtPlayer ((uint16*)g_asset_ptrs[10])
|
||||
#define kDungeonPitsHurtPlayer_SIZE (g_asset_sizes[10])
|
||||
#define kEntranceData_rooms ((uint16*)g_asset_ptrs[11])
|
||||
#define kEntranceData_rooms_SIZE (g_asset_sizes[11])
|
||||
#define kEntranceData_relativeCoords ((uint8*)g_asset_ptrs[12])
|
||||
#define kEntranceData_relativeCoords_SIZE (g_asset_sizes[12])
|
||||
#define kEntranceData_scrollX ((uint16*)g_asset_ptrs[13])
|
||||
#define kEntranceData_scrollX_SIZE (g_asset_sizes[13])
|
||||
#define kEntranceData_scrollY ((uint16*)g_asset_ptrs[14])
|
||||
#define kEntranceData_scrollY_SIZE (g_asset_sizes[14])
|
||||
#define kEntranceData_playerX ((uint16*)g_asset_ptrs[15])
|
||||
#define kEntranceData_playerX_SIZE (g_asset_sizes[15])
|
||||
#define kEntranceData_playerY ((uint16*)g_asset_ptrs[16])
|
||||
#define kEntranceData_playerY_SIZE (g_asset_sizes[16])
|
||||
#define kEntranceData_cameraX ((uint16*)g_asset_ptrs[17])
|
||||
#define kEntranceData_cameraX_SIZE (g_asset_sizes[17])
|
||||
#define kEntranceData_cameraY ((uint16*)g_asset_ptrs[18])
|
||||
#define kEntranceData_cameraY_SIZE (g_asset_sizes[18])
|
||||
#define kEntranceData_blockset ((uint8*)g_asset_ptrs[19])
|
||||
#define kEntranceData_blockset_SIZE (g_asset_sizes[19])
|
||||
#define kEntranceData_floor ((int8*)g_asset_ptrs[20])
|
||||
#define kEntranceData_floor_SIZE (g_asset_sizes[20])
|
||||
#define kEntranceData_palace ((int8*)g_asset_ptrs[21])
|
||||
#define kEntranceData_palace_SIZE (g_asset_sizes[21])
|
||||
#define kEntranceData_doorwayOrientation ((uint8*)g_asset_ptrs[22])
|
||||
#define kEntranceData_doorwayOrientation_SIZE (g_asset_sizes[22])
|
||||
#define kEntranceData_startingBg ((uint8*)g_asset_ptrs[23])
|
||||
#define kEntranceData_startingBg_SIZE (g_asset_sizes[23])
|
||||
#define kEntranceData_quadrant1 ((uint8*)g_asset_ptrs[24])
|
||||
#define kEntranceData_quadrant1_SIZE (g_asset_sizes[24])
|
||||
#define kEntranceData_quadrant2 ((uint8*)g_asset_ptrs[25])
|
||||
#define kEntranceData_quadrant2_SIZE (g_asset_sizes[25])
|
||||
#define kEntranceData_doorSettings ((uint16*)g_asset_ptrs[26])
|
||||
#define kEntranceData_doorSettings_SIZE (g_asset_sizes[26])
|
||||
#define kEntranceData_musicTrack ((uint8*)g_asset_ptrs[27])
|
||||
#define kEntranceData_musicTrack_SIZE (g_asset_sizes[27])
|
||||
#define kStartingPoint_rooms ((uint16*)g_asset_ptrs[28])
|
||||
#define kStartingPoint_rooms_SIZE (g_asset_sizes[28])
|
||||
#define kStartingPoint_relativeCoords ((uint8*)g_asset_ptrs[29])
|
||||
#define kStartingPoint_relativeCoords_SIZE (g_asset_sizes[29])
|
||||
#define kStartingPoint_scrollX ((uint16*)g_asset_ptrs[30])
|
||||
#define kStartingPoint_scrollX_SIZE (g_asset_sizes[30])
|
||||
#define kStartingPoint_scrollY ((uint16*)g_asset_ptrs[31])
|
||||
#define kStartingPoint_scrollY_SIZE (g_asset_sizes[31])
|
||||
#define kStartingPoint_playerX ((uint16*)g_asset_ptrs[32])
|
||||
#define kStartingPoint_playerX_SIZE (g_asset_sizes[32])
|
||||
#define kStartingPoint_playerY ((uint16*)g_asset_ptrs[33])
|
||||
#define kStartingPoint_playerY_SIZE (g_asset_sizes[33])
|
||||
#define kStartingPoint_cameraX ((uint16*)g_asset_ptrs[34])
|
||||
#define kStartingPoint_cameraX_SIZE (g_asset_sizes[34])
|
||||
#define kStartingPoint_cameraY ((uint16*)g_asset_ptrs[35])
|
||||
#define kStartingPoint_cameraY_SIZE (g_asset_sizes[35])
|
||||
#define kStartingPoint_blockset ((uint8*)g_asset_ptrs[36])
|
||||
#define kStartingPoint_blockset_SIZE (g_asset_sizes[36])
|
||||
#define kStartingPoint_floor ((int8*)g_asset_ptrs[37])
|
||||
#define kStartingPoint_floor_SIZE (g_asset_sizes[37])
|
||||
#define kStartingPoint_palace ((int8*)g_asset_ptrs[38])
|
||||
#define kStartingPoint_palace_SIZE (g_asset_sizes[38])
|
||||
#define kStartingPoint_doorwayOrientation ((uint8*)g_asset_ptrs[39])
|
||||
#define kStartingPoint_doorwayOrientation_SIZE (g_asset_sizes[39])
|
||||
#define kStartingPoint_startingBg ((uint8*)g_asset_ptrs[40])
|
||||
#define kStartingPoint_startingBg_SIZE (g_asset_sizes[40])
|
||||
#define kStartingPoint_quadrant1 ((uint8*)g_asset_ptrs[41])
|
||||
#define kStartingPoint_quadrant1_SIZE (g_asset_sizes[41])
|
||||
#define kStartingPoint_quadrant2 ((uint8*)g_asset_ptrs[42])
|
||||
#define kStartingPoint_quadrant2_SIZE (g_asset_sizes[42])
|
||||
#define kStartingPoint_doorSettings ((uint16*)g_asset_ptrs[43])
|
||||
#define kStartingPoint_doorSettings_SIZE (g_asset_sizes[43])
|
||||
#define kStartingPoint_entrance ((uint8*)g_asset_ptrs[44])
|
||||
#define kStartingPoint_entrance_SIZE (g_asset_sizes[44])
|
||||
#define kStartingPoint_musicTrack ((uint8*)g_asset_ptrs[45])
|
||||
#define kStartingPoint_musicTrack_SIZE (g_asset_sizes[45])
|
||||
#define kDungeonRoomDefault ((uint8*)g_asset_ptrs[46])
|
||||
#define kDungeonRoomDefault_SIZE (g_asset_sizes[46])
|
||||
#define kDungeonRoomDefaultOffs ((uint16*)g_asset_ptrs[47])
|
||||
#define kDungeonRoomDefaultOffs_SIZE (g_asset_sizes[47])
|
||||
#define kDungeonRoomOverlay ((uint8*)g_asset_ptrs[48])
|
||||
#define kDungeonRoomOverlay_SIZE (g_asset_sizes[48])
|
||||
#define kDungeonRoomOverlayOffs ((uint16*)g_asset_ptrs[49])
|
||||
#define kDungeonRoomOverlayOffs_SIZE (g_asset_sizes[49])
|
||||
#define kDungeonSecrets ((uint8*)g_asset_ptrs[50])
|
||||
#define kDungeonSecrets_SIZE (g_asset_sizes[50])
|
||||
#define kDungAttrsForTile_Offs ((uint16*)g_asset_ptrs[51])
|
||||
#define kDungAttrsForTile_Offs_SIZE (g_asset_sizes[51])
|
||||
#define kDungAttrsForTile ((uint8*)g_asset_ptrs[52])
|
||||
#define kDungAttrsForTile_SIZE (g_asset_sizes[52])
|
||||
#define kMovableBlockDataInit ((uint16*)g_asset_ptrs[53])
|
||||
#define kMovableBlockDataInit_SIZE (g_asset_sizes[53])
|
||||
#define kTorchDataInit ((uint16*)g_asset_ptrs[54])
|
||||
#define kTorchDataInit_SIZE (g_asset_sizes[54])
|
||||
#define kTorchDataJunk ((uint16*)g_asset_ptrs[55])
|
||||
#define kTorchDataJunk_SIZE (g_asset_sizes[55])
|
||||
#define kEnemyDamageData ((uint8*)g_asset_ptrs[56])
|
||||
#define kEnemyDamageData_SIZE (g_asset_sizes[56])
|
||||
#define kLinkGraphics ((uint8*)g_asset_ptrs[57])
|
||||
#define kLinkGraphics_SIZE (g_asset_sizes[57])
|
||||
#define kDungeonSprites ((uint8*)g_asset_ptrs[58])
|
||||
#define kDungeonSprites_SIZE (g_asset_sizes[58])
|
||||
#define kDungeonSpriteOffs ((uint16*)g_asset_ptrs[59])
|
||||
#define kDungeonSpriteOffs_SIZE (g_asset_sizes[59])
|
||||
#define kMap32ToMap16_0 ((uint8*)g_asset_ptrs[60])
|
||||
#define kMap32ToMap16_0_SIZE (g_asset_sizes[60])
|
||||
#define kMap32ToMap16_1 ((uint8*)g_asset_ptrs[61])
|
||||
#define kMap32ToMap16_1_SIZE (g_asset_sizes[61])
|
||||
#define kMap32ToMap16_2 ((uint8*)g_asset_ptrs[62])
|
||||
#define kMap32ToMap16_2_SIZE (g_asset_sizes[62])
|
||||
#define kMap32ToMap16_3 ((uint8*)g_asset_ptrs[63])
|
||||
#define kMap32ToMap16_3_SIZE (g_asset_sizes[63])
|
||||
#define kDialogueOffs ((uint16*)g_asset_ptrs[64])
|
||||
#define kDialogueOffs_SIZE (g_asset_sizes[64])
|
||||
#define kDialogueText ((uint8*)g_asset_ptrs[65])
|
||||
#define kDialogueText_SIZE (g_asset_sizes[65])
|
||||
#define kSprGfx ((uint8*)g_asset_ptrs[66])
|
||||
#define kSprGfx_SIZE (g_asset_sizes[66])
|
||||
#define kBgGfx ((uint8*)g_asset_ptrs[67])
|
||||
#define kBgGfx_SIZE (g_asset_sizes[67])
|
||||
#define kOverworldMapGfx ((uint8*)g_asset_ptrs[68])
|
||||
#define kOverworldMapGfx_SIZE (g_asset_sizes[68])
|
||||
#define kLightOverworldTilemap ((uint8*)g_asset_ptrs[69])
|
||||
#define kLightOverworldTilemap_SIZE (g_asset_sizes[69])
|
||||
#define kDarkOverworldTilemap ((uint8*)g_asset_ptrs[70])
|
||||
#define kDarkOverworldTilemap_SIZE (g_asset_sizes[70])
|
||||
#define kPredefinedTileData ((uint16*)g_asset_ptrs[71])
|
||||
#define kPredefinedTileData_SIZE (g_asset_sizes[71])
|
||||
#define kFontData ((uint16*)g_asset_ptrs[72])
|
||||
#define kFontData_SIZE (g_asset_sizes[72])
|
||||
#define kMap16ToMap8 ((uint16*)g_asset_ptrs[73])
|
||||
#define kMap16ToMap8_SIZE (g_asset_sizes[73])
|
||||
#define kGeneratedWishPondItem ((uint8*)g_asset_ptrs[74])
|
||||
#define kGeneratedWishPondItem_SIZE (g_asset_sizes[74])
|
||||
#define kGeneratedBombosArr ((uint8*)g_asset_ptrs[75])
|
||||
#define kGeneratedBombosArr_SIZE (g_asset_sizes[75])
|
||||
#define kGeneratedEndSequence15 ((uint8*)g_asset_ptrs[76])
|
||||
#define kGeneratedEndSequence15_SIZE (g_asset_sizes[76])
|
||||
#define kEnding_Credits_Text ((uint8*)g_asset_ptrs[77])
|
||||
#define kEnding_Credits_Text_SIZE (g_asset_sizes[77])
|
||||
#define kEnding_Credits_Offs ((uint16*)g_asset_ptrs[78])
|
||||
#define kEnding_Credits_Offs_SIZE (g_asset_sizes[78])
|
||||
#define kEnding_MapData ((uint16*)g_asset_ptrs[79])
|
||||
#define kEnding_MapData_SIZE (g_asset_sizes[79])
|
||||
#define kEnding0_Offs ((uint16*)g_asset_ptrs[80])
|
||||
#define kEnding0_Offs_SIZE (g_asset_sizes[80])
|
||||
#define kEnding0_Data ((uint8*)g_asset_ptrs[81])
|
||||
#define kEnding0_Data_SIZE (g_asset_sizes[81])
|
||||
#define kPalette_DungBgMain ((uint16*)g_asset_ptrs[82])
|
||||
#define kPalette_DungBgMain_SIZE (g_asset_sizes[82])
|
||||
#define kPalette_MainSpr ((uint16*)g_asset_ptrs[83])
|
||||
#define kPalette_MainSpr_SIZE (g_asset_sizes[83])
|
||||
#define kPalette_ArmorAndGloves ((uint16*)g_asset_ptrs[84])
|
||||
#define kPalette_ArmorAndGloves_SIZE (g_asset_sizes[84])
|
||||
#define kPalette_Sword ((uint16*)g_asset_ptrs[85])
|
||||
#define kPalette_Sword_SIZE (g_asset_sizes[85])
|
||||
#define kPalette_Shield ((uint16*)g_asset_ptrs[86])
|
||||
#define kPalette_Shield_SIZE (g_asset_sizes[86])
|
||||
#define kPalette_SpriteAux3 ((uint16*)g_asset_ptrs[87])
|
||||
#define kPalette_SpriteAux3_SIZE (g_asset_sizes[87])
|
||||
#define kPalette_MiscSprite_Indoors ((uint16*)g_asset_ptrs[88])
|
||||
#define kPalette_MiscSprite_Indoors_SIZE (g_asset_sizes[88])
|
||||
#define kPalette_SpriteAux1 ((uint16*)g_asset_ptrs[89])
|
||||
#define kPalette_SpriteAux1_SIZE (g_asset_sizes[89])
|
||||
#define kPalette_OverworldBgMain ((uint16*)g_asset_ptrs[90])
|
||||
#define kPalette_OverworldBgMain_SIZE (g_asset_sizes[90])
|
||||
#define kPalette_OverworldBgAux12 ((uint16*)g_asset_ptrs[91])
|
||||
#define kPalette_OverworldBgAux12_SIZE (g_asset_sizes[91])
|
||||
#define kPalette_OverworldBgAux3 ((uint16*)g_asset_ptrs[92])
|
||||
#define kPalette_OverworldBgAux3_SIZE (g_asset_sizes[92])
|
||||
#define kPalette_PalaceMapBg ((uint16*)g_asset_ptrs[93])
|
||||
#define kPalette_PalaceMapBg_SIZE (g_asset_sizes[93])
|
||||
#define kPalette_PalaceMapSpr ((uint16*)g_asset_ptrs[94])
|
||||
#define kPalette_PalaceMapSpr_SIZE (g_asset_sizes[94])
|
||||
#define kHudPalData ((uint16*)g_asset_ptrs[95])
|
||||
#define kHudPalData_SIZE (g_asset_sizes[95])
|
||||
#define kOverworldMapPaletteData ((uint16*)g_asset_ptrs[96])
|
||||
#define kOverworldMapPaletteData_SIZE (g_asset_sizes[96])
|
||||
#define kDungMap_FloorLayout ((uint8*)g_asset_ptrs[97])
|
||||
#define kDungMap_FloorLayout_SIZE (g_asset_sizes[97])
|
||||
#define kDungMap_Tiles ((uint8*)g_asset_ptrs[98])
|
||||
#define kDungMap_Tiles_SIZE (g_asset_sizes[98])
|
||||
#define kBgTilemap_0 ((uint8*)g_asset_ptrs[99])
|
||||
#define kBgTilemap_0_SIZE (g_asset_sizes[99])
|
||||
#define kBgTilemap_1 ((uint8*)g_asset_ptrs[100])
|
||||
#define kBgTilemap_1_SIZE (g_asset_sizes[100])
|
||||
#define kBgTilemap_2 ((uint8*)g_asset_ptrs[101])
|
||||
#define kBgTilemap_2_SIZE (g_asset_sizes[101])
|
||||
#define kBgTilemap_3 ((uint8*)g_asset_ptrs[102])
|
||||
#define kBgTilemap_3_SIZE (g_asset_sizes[102])
|
||||
#define kBgTilemap_4 ((uint8*)g_asset_ptrs[103])
|
||||
#define kBgTilemap_4_SIZE (g_asset_sizes[103])
|
||||
#define kBgTilemap_5 ((uint8*)g_asset_ptrs[104])
|
||||
#define kBgTilemap_5_SIZE (g_asset_sizes[104])
|
||||
#define kOverworld_Hibytes_Comp ((uint8*)g_asset_ptrs[105])
|
||||
#define kOverworld_Hibytes_Comp_SIZE (g_asset_sizes[105])
|
||||
#define kOverworld_Lobytes_Comp ((uint8*)g_asset_ptrs[106])
|
||||
#define kOverworld_Lobytes_Comp_SIZE (g_asset_sizes[106])
|
||||
#define kOverworldMapIsSmall ((uint8*)g_asset_ptrs[107])
|
||||
#define kOverworldMapIsSmall_SIZE (g_asset_sizes[107])
|
||||
#define kOverworldAuxTileThemeIndexes ((uint8*)g_asset_ptrs[108])
|
||||
#define kOverworldAuxTileThemeIndexes_SIZE (g_asset_sizes[108])
|
||||
#define kOverworldBgPalettes ((uint8*)g_asset_ptrs[109])
|
||||
#define kOverworldBgPalettes_SIZE (g_asset_sizes[109])
|
||||
#define kOverworld_SignText ((uint16*)g_asset_ptrs[110])
|
||||
#define kOverworld_SignText_SIZE (g_asset_sizes[110])
|
||||
#define kOwMusicSets ((uint8*)g_asset_ptrs[111])
|
||||
#define kOwMusicSets_SIZE (g_asset_sizes[111])
|
||||
#define kOwMusicSets2 ((uint8*)g_asset_ptrs[112])
|
||||
#define kOwMusicSets2_SIZE (g_asset_sizes[112])
|
||||
#define kBirdTravel_ScreenIndex ((uint16*)g_asset_ptrs[113])
|
||||
#define kBirdTravel_ScreenIndex_SIZE (g_asset_sizes[113])
|
||||
#define kBirdTravel_Map16LoadSrcOff ((uint16*)g_asset_ptrs[114])
|
||||
#define kBirdTravel_Map16LoadSrcOff_SIZE (g_asset_sizes[114])
|
||||
#define kBirdTravel_ScrollX ((uint16*)g_asset_ptrs[115])
|
||||
#define kBirdTravel_ScrollX_SIZE (g_asset_sizes[115])
|
||||
#define kBirdTravel_ScrollY ((uint16*)g_asset_ptrs[116])
|
||||
#define kBirdTravel_ScrollY_SIZE (g_asset_sizes[116])
|
||||
#define kBirdTravel_LinkXCoord ((uint16*)g_asset_ptrs[117])
|
||||
#define kBirdTravel_LinkXCoord_SIZE (g_asset_sizes[117])
|
||||
#define kBirdTravel_LinkYCoord ((uint16*)g_asset_ptrs[118])
|
||||
#define kBirdTravel_LinkYCoord_SIZE (g_asset_sizes[118])
|
||||
#define kBirdTravel_CameraXScroll ((uint16*)g_asset_ptrs[119])
|
||||
#define kBirdTravel_CameraXScroll_SIZE (g_asset_sizes[119])
|
||||
#define kBirdTravel_CameraYScroll ((uint16*)g_asset_ptrs[120])
|
||||
#define kBirdTravel_CameraYScroll_SIZE (g_asset_sizes[120])
|
||||
#define kBirdTravel_Unk1 ((int8*)g_asset_ptrs[121])
|
||||
#define kBirdTravel_Unk1_SIZE (g_asset_sizes[121])
|
||||
#define kBirdTravel_Unk3 ((int8*)g_asset_ptrs[122])
|
||||
#define kBirdTravel_Unk3_SIZE (g_asset_sizes[122])
|
||||
#define kWhirlpoolAreas ((uint16*)g_asset_ptrs[123])
|
||||
#define kWhirlpoolAreas_SIZE (g_asset_sizes[123])
|
||||
#define kOverworld_Entrance_Area ((uint16*)g_asset_ptrs[124])
|
||||
#define kOverworld_Entrance_Area_SIZE (g_asset_sizes[124])
|
||||
#define kOverworld_Entrance_Pos ((uint16*)g_asset_ptrs[125])
|
||||
#define kOverworld_Entrance_Pos_SIZE (g_asset_sizes[125])
|
||||
#define kOverworld_Entrance_Id ((uint8*)g_asset_ptrs[126])
|
||||
#define kOverworld_Entrance_Id_SIZE (g_asset_sizes[126])
|
||||
#define kFallHole_Area ((uint16*)g_asset_ptrs[127])
|
||||
#define kFallHole_Area_SIZE (g_asset_sizes[127])
|
||||
#define kFallHole_Pos ((uint16*)g_asset_ptrs[128])
|
||||
#define kFallHole_Pos_SIZE (g_asset_sizes[128])
|
||||
#define kFallHole_Entrances ((uint8*)g_asset_ptrs[129])
|
||||
#define kFallHole_Entrances_SIZE (g_asset_sizes[129])
|
||||
#define kExitData_ScreenIndex ((uint8*)g_asset_ptrs[130])
|
||||
#define kExitData_ScreenIndex_SIZE (g_asset_sizes[130])
|
||||
#define kExitDataRooms ((uint16*)g_asset_ptrs[131])
|
||||
#define kExitDataRooms_SIZE (g_asset_sizes[131])
|
||||
#define kExitData_Map16LoadSrcOff ((uint16*)g_asset_ptrs[132])
|
||||
#define kExitData_Map16LoadSrcOff_SIZE (g_asset_sizes[132])
|
||||
#define kExitData_ScrollX ((uint16*)g_asset_ptrs[133])
|
||||
#define kExitData_ScrollX_SIZE (g_asset_sizes[133])
|
||||
#define kExitData_ScrollY ((uint16*)g_asset_ptrs[134])
|
||||
#define kExitData_ScrollY_SIZE (g_asset_sizes[134])
|
||||
#define kExitData_XCoord ((uint16*)g_asset_ptrs[135])
|
||||
#define kExitData_XCoord_SIZE (g_asset_sizes[135])
|
||||
#define kExitData_YCoord ((uint16*)g_asset_ptrs[136])
|
||||
#define kExitData_YCoord_SIZE (g_asset_sizes[136])
|
||||
#define kExitData_CameraXScroll ((uint16*)g_asset_ptrs[137])
|
||||
#define kExitData_CameraXScroll_SIZE (g_asset_sizes[137])
|
||||
#define kExitData_CameraYScroll ((uint16*)g_asset_ptrs[138])
|
||||
#define kExitData_CameraYScroll_SIZE (g_asset_sizes[138])
|
||||
#define kExitData_NormalDoor ((uint16*)g_asset_ptrs[139])
|
||||
#define kExitData_NormalDoor_SIZE (g_asset_sizes[139])
|
||||
#define kExitData_FancyDoor ((uint16*)g_asset_ptrs[140])
|
||||
#define kExitData_FancyDoor_SIZE (g_asset_sizes[140])
|
||||
#define kExitData_Unk1 ((int8*)g_asset_ptrs[141])
|
||||
#define kExitData_Unk1_SIZE (g_asset_sizes[141])
|
||||
#define kExitData_Unk3 ((int8*)g_asset_ptrs[142])
|
||||
#define kExitData_Unk3_SIZE (g_asset_sizes[142])
|
||||
#define kSpExit_Top ((uint16*)g_asset_ptrs[143])
|
||||
#define kSpExit_Top_SIZE (g_asset_sizes[143])
|
||||
#define kSpExit_Bottom ((uint16*)g_asset_ptrs[144])
|
||||
#define kSpExit_Bottom_SIZE (g_asset_sizes[144])
|
||||
#define kSpExit_Left ((uint16*)g_asset_ptrs[145])
|
||||
#define kSpExit_Left_SIZE (g_asset_sizes[145])
|
||||
#define kSpExit_Right ((uint16*)g_asset_ptrs[146])
|
||||
#define kSpExit_Right_SIZE (g_asset_sizes[146])
|
||||
#define kSpExit_Tab4 ((int16*)g_asset_ptrs[147])
|
||||
#define kSpExit_Tab4_SIZE (g_asset_sizes[147])
|
||||
#define kSpExit_Tab5 ((int16*)g_asset_ptrs[148])
|
||||
#define kSpExit_Tab5_SIZE (g_asset_sizes[148])
|
||||
#define kSpExit_Tab6 ((int16*)g_asset_ptrs[149])
|
||||
#define kSpExit_Tab6_SIZE (g_asset_sizes[149])
|
||||
#define kSpExit_Tab7 ((int16*)g_asset_ptrs[150])
|
||||
#define kSpExit_Tab7_SIZE (g_asset_sizes[150])
|
||||
#define kSpExit_LeftEdgeOfMap ((uint16*)g_asset_ptrs[151])
|
||||
#define kSpExit_LeftEdgeOfMap_SIZE (g_asset_sizes[151])
|
||||
#define kSpExit_Dir ((uint8*)g_asset_ptrs[152])
|
||||
#define kSpExit_Dir_SIZE (g_asset_sizes[152])
|
||||
#define kSpExit_SprGfx ((uint8*)g_asset_ptrs[153])
|
||||
#define kSpExit_SprGfx_SIZE (g_asset_sizes[153])
|
||||
#define kSpExit_AuxGfx ((uint8*)g_asset_ptrs[154])
|
||||
#define kSpExit_AuxGfx_SIZE (g_asset_sizes[154])
|
||||
#define kSpExit_PalBg ((uint8*)g_asset_ptrs[155])
|
||||
#define kSpExit_PalBg_SIZE (g_asset_sizes[155])
|
||||
#define kSpExit_PalSpr ((uint8*)g_asset_ptrs[156])
|
||||
#define kSpExit_PalSpr_SIZE (g_asset_sizes[156])
|
||||
#define kOverworldSecrets_Offs ((uint16*)g_asset_ptrs[157])
|
||||
#define kOverworldSecrets_Offs_SIZE (g_asset_sizes[157])
|
||||
#define kOverworldSecrets ((uint8*)g_asset_ptrs[158])
|
||||
#define kOverworldSecrets_SIZE (g_asset_sizes[158])
|
||||
#define kOverworldSpriteOffs ((uint16*)g_asset_ptrs[159])
|
||||
#define kOverworldSpriteOffs_SIZE (g_asset_sizes[159])
|
||||
#define kOverworldSprites ((uint8*)g_asset_ptrs[160])
|
||||
#define kOverworldSprites_SIZE (g_asset_sizes[160])
|
||||
#define kOverworldSpriteGfx ((uint8*)g_asset_ptrs[161])
|
||||
#define kOverworldSpriteGfx_SIZE (g_asset_sizes[161])
|
||||
#define kOverworldSpritePalettes ((uint8*)g_asset_ptrs[162])
|
||||
#define kOverworldSpritePalettes_SIZE (g_asset_sizes[162])
|
||||
#define kMap8DataToTileAttr ((uint8*)g_asset_ptrs[163])
|
||||
#define kMap8DataToTileAttr_SIZE (g_asset_sizes[163])
|
||||
#define kSomeTileAttr ((uint8*)g_asset_ptrs[164])
|
||||
#define kSomeTileAttr_SIZE (g_asset_sizes[164])
|
||||
#define kAssets_Sig 90, 101, 108, 100, 97, 51, 95, 118, 48, 32, 32, 32, 32, 32, 10, 0, 140, 60, 238, 107, 183, 109, 113, 156, 115, 98, 147, 236, 79, 160, 95, 71, 26, 185, 82, 158, 43, 82, 22, 60, 62, 92, 250, 152, 23, 230, 97, 235
|
||||
Binary file not shown.
@@ -1,52 +0,0 @@
|
||||
import argparse
|
||||
import util
|
||||
import sys
|
||||
import os
|
||||
|
||||
os.chdir(os.path.dirname(__file__))
|
||||
|
||||
|
||||
parser = argparse.ArgumentParser(description='Resource tool used to build zelda3_assets.dat', allow_abbrev=False)
|
||||
parser.add_argument('-r', '--rom', nargs='?', metavar='ROM')
|
||||
parser.add_argument('--extract-from-rom', '-e', action='store_true', help='Extract/overwrite things from the ROM')
|
||||
|
||||
optional = parser.add_argument_group('Language settings')
|
||||
optional.add_argument('--extract-dialogue', action='store_true', help = 'Extract dialogue from a translated ROM')
|
||||
optional.add_argument('--languages', action='store', metavar='L1,L2', help = 'Comma separated list of additional languages to build (de,fr,fr-c,en,es,pl,pt,redux,nl,sv).')
|
||||
|
||||
optional = parser.add_argument_group('Debug things')
|
||||
optional.add_argument('--no-build', action='store_true', help="Don't actually build zelda3_assets.dat")
|
||||
optional.add_argument('--print-strings', action='store_true', help="Print all dialogue strings")
|
||||
optional.add_argument('--print-assets-header', action='store_true')
|
||||
|
||||
optional = parser.add_argument_group('Image handling')
|
||||
optional.add_argument('--sprites-from-png', action='store_true', help="When compiling, load sprites from png instead of from ROM")
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
if args.extract_dialogue:
|
||||
ROM = util.load_rom(args.rom, True)
|
||||
import extract_resources, sprite_sheets
|
||||
sprite_sheets.decode_font()
|
||||
extract_resources.print_dialogue()
|
||||
sys.exit(0)
|
||||
|
||||
ROM = util.load_rom(args.rom)
|
||||
|
||||
want_compile = True
|
||||
|
||||
if args.extract_from_rom:
|
||||
import extract_resources
|
||||
extract_resources.main()
|
||||
|
||||
if args.print_strings:
|
||||
import text_compression
|
||||
text_compression.print_strings(ROM)
|
||||
want_compile = False
|
||||
|
||||
if want_compile and not args.no_build:
|
||||
import compile_resources
|
||||
compile_resources.main(args)
|
||||
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,640 +0,0 @@
|
||||
from PIL import Image
|
||||
import sprite_sheet_info
|
||||
import util
|
||||
from util import get_bytes, get_words, get_byte, cache
|
||||
import array
|
||||
import tables
|
||||
import sys
|
||||
|
||||
override_armor_palette = None
|
||||
#override_armor_palette = [0x7fff, 0x237e, 0x11b7, 0x369e, 0x14a5, 0x1ff, 0x1078, 0x599d, 0x3647, 0x3b68, 0xa4a, 0x12ef, 0x2a5c, 0x1571, 0x7a18,
|
||||
# 0x7fff, 0x237e, 0x11b7, 0x369e, 0x14a5, 0x1ff, 0x1078, 0x599d, 0x6980, 0x7691, 0x26b8, 0x437f, 0x2a5c, 0x1199, 0x7a18,
|
||||
# 0x7fff, 0x237e, 0x11b7, 0x369e, 0x14a5, 0x1ff, 0x1078, 0x599d, 0x1057, 0x457e, 0x6df3, 0xfeb9, 0x2a5c, 0x2227, 0x7a18,
|
||||
# 0x7fff, 0x237e, 0x11b7, 0x369e, 0x14a5, 0x1ff, 0x1078, 0x3d97, 0x3647, 0x3b68, 0xa4a, 0x12ef, 0x567e, 0x1571, 0x7a18,
|
||||
# 0, 0x0EFA, 0x7DD1, 0, 0x7F1A, 0x7F1A,0, 0x716E, 0x7DD1, 0x40A7, 0x7DD1, 0x40A7, 0x48E9, 0x50CF, 0x7FFF]
|
||||
|
||||
|
||||
def save_as_png(dimensions, data, fname, palette = None):
|
||||
img = Image.new('L' if palette == None else 'P', dimensions)
|
||||
img.putdata(data)
|
||||
if palette != None:
|
||||
img.putpalette(palette)
|
||||
img.save(fname)
|
||||
|
||||
def save_as_24bpp_png(dimensions, data, fname):
|
||||
img = Image.new("RGB", dimensions)
|
||||
img.putdata(data)
|
||||
img.save(fname)
|
||||
|
||||
@cache
|
||||
def decode_2bit_tileset(tileset, height = 32, base = 0):
|
||||
data = util.decomp(tables.kCompSpritePtrs[tileset], get_byte, False)
|
||||
assert len(data) == 0x400 * height // 32
|
||||
dst = bytearray(128*height)
|
||||
def decode_2bit(offs, toffs, base):
|
||||
for y in range(8):
|
||||
d0, d1 = data[offs + y * 2], data[offs + y * 2 + 1]
|
||||
for x in range(8):
|
||||
t = ((d0 >> x) & 1) * 1 + ((d1 >> x) & 1) * 2
|
||||
dst[toffs + y * 128 + (7 - x)] = t + base
|
||||
for i in range(16*height//8):
|
||||
x = i % 16
|
||||
y = i // 16
|
||||
decode_2bit(i * 16, x * 8 + y * 8 * 128, base[i] if isinstance(base, tuple) else base)
|
||||
return dst
|
||||
|
||||
def is_high_3bit_tileset(tileset):
|
||||
# is 0x5c, 0x5e, 0x5f included or not?
|
||||
return tileset in (0x52, 0x53, 0x5a, 0x5b, 0x5c, 0x5e, 0x5f)
|
||||
|
||||
def get_unpacked_snes_tileset(tileset):
|
||||
if tileset < 12:
|
||||
return get_bytes(tables.kCompSpritePtrs[tileset], 0x600)
|
||||
else:
|
||||
return util.decomp(tables.kCompSpritePtrs[tileset], get_byte, False)
|
||||
|
||||
@cache
|
||||
def decode_3bit_tileset(tileset):
|
||||
data = get_unpacked_snes_tileset(tileset)
|
||||
assert len(data) == 0x600
|
||||
base = 8 if is_high_3bit_tileset(tileset) else 0
|
||||
height = 32
|
||||
dst = bytearray(128*height)
|
||||
def decode_3bit(offs, toffs):
|
||||
for y in range(8):
|
||||
d0, d1, d2 = data[offs + y * 2], data[offs + y * 2 + 1], data[offs + y + 16]
|
||||
for x in range(8):
|
||||
t = ((d0 >> x) & 1) * 1 + ((d1 >> x) & 1) * 2 + ((d2 >> x) & 1) * 4
|
||||
dst[toffs + y * 128 + (7 - x)] = base + t
|
||||
for i in range(16*height//8):
|
||||
x, y = i % 16, i // 16
|
||||
decode_3bit(i * 24, x * 8 + y * 8 * 128)
|
||||
return dst
|
||||
|
||||
def decode_4bit_tileset_link():
|
||||
height = 448
|
||||
data = get_bytes(0x108000, 0x800 * height // 32) # only link sprites for now
|
||||
dst = bytearray(128*height)
|
||||
def decode_4bit(offs, toffs):
|
||||
for y in range(8):
|
||||
d0, d1, d2, d3 = data[offs + y * 2 + 0], data[offs + y * 2 + 1], data[offs + y * 2 + 16], data[offs + y * 2 + 17]
|
||||
for x in range(8):
|
||||
t = ((d0 >> x) & 1) * 1 + ((d1 >> x) & 1) * 2 + ((d2 >> x) & 1) * 4 + ((d3 >> x) & 1) * 8
|
||||
dst[toffs + y * 128 + (7 - x)] = t
|
||||
for i in range(16*height//8):
|
||||
x, y = i % 16, i // 16
|
||||
decode_4bit(i * 32, x * 8 + y * 8 * 128)
|
||||
return dst
|
||||
|
||||
def convert_snes_palette(v):
|
||||
rr=bytearray()
|
||||
for x in v:
|
||||
r, g, b = x & 0x1f, x >> 5 & 0x1f, x >> 10 & 0x1f
|
||||
rr.extend((r << 3 | r >> 2, g << 3 | g >> 2, b << 3 | b >> 2))
|
||||
return rr
|
||||
|
||||
def convert_int24_palette_to_bytes(v):
|
||||
rr=bytearray()
|
||||
for x in v:
|
||||
rr.extend((x & 0xff, x >> 8 & 0xff, x >> 16 & 0xff))
|
||||
return rr
|
||||
|
||||
def convert_snes_palette_to_int(v):
|
||||
rr=[]
|
||||
for x in v:
|
||||
r, g, b = x & 0x1f, x >> 5 & 0x1f, x >> 10 & 0x1f
|
||||
rr.append((r << 3 | r >> 2) | (g << 3 | g >> 2) << 8 | (b << 3 | b >> 2) << 16)
|
||||
return rr
|
||||
|
||||
def decode_link_sprites():
|
||||
kLinkPalette = [0, 0x7fff, 0x237e, 0x11b7, 0x369e, 0x14a5, 0x1ff, 0x1078, 0x599d, 0x3647, 0x3b68, 0xa4a, 0x12ef, 0x2a5c, 0x1571, 0x7a18]
|
||||
save_as_png((128, 448), decode_4bit_tileset_link(), 'linksprite.png', convert_snes_palette(kLinkPalette))
|
||||
|
||||
def get_hud_snes_palette():
|
||||
hud_palette = get_words(0x9BD660, 64)
|
||||
palette = [(31 << 10 | 31) for i in range(256)]
|
||||
for i in range(16):
|
||||
for j in range(1, 4):
|
||||
palette[i * 16 + j] = hud_palette[i * 4 + j]
|
||||
return palette
|
||||
|
||||
def decode_hud_icons():
|
||||
class PaletteUsage:
|
||||
def __init__(self):
|
||||
self.data = open('palette_usage.bin', 'rb').read()
|
||||
def get(self, icon):
|
||||
usage = self.data[icon]
|
||||
for j in range(8):
|
||||
if usage & (1 << j):
|
||||
return j
|
||||
return 0
|
||||
pu = PaletteUsage()
|
||||
dst = bytearray()
|
||||
for slot, image_set in enumerate([106, 107, 105]):
|
||||
tbase = tuple([pu.get(slot * 128 + i) * 16 for i in range(128)])
|
||||
dst += decode_2bit_tileset(image_set, height = 64, base = tbase)
|
||||
|
||||
save_as_png((128, 64 * 3), dst, 'hud_icons.png', convert_snes_palette(get_hud_snes_palette()[:128]))
|
||||
|
||||
def get_pt_remapper():
|
||||
b = util.ROM.get_bytes(0x8EFC09, 121 * 3)
|
||||
d = {}
|
||||
for i in range(121):
|
||||
ch = (i & 0xf) | (i << 1) & 0xe0
|
||||
d[ch] = b[i*3+0]
|
||||
d[ch|0x10] = b[i*3+1]
|
||||
return d
|
||||
|
||||
kFontTypes = {
|
||||
'us' : (0x8e8000, 256, 'font.png', (0x8ECADF, 99)),
|
||||
'de' : (0xCC6E8, 256, 'font_de.png', (0x8CDECF, 112)),
|
||||
'fr' : (0xCC6E8, 256, 'font_fr.png', (0x8CDEAF, 112)),
|
||||
'fr-c' : (0xCD078, 256, 'font_fr_c.png', (0x8CE83F, 112)),
|
||||
'en' : (0x8E8000, 256, 'font_en.png', (0x8ECAFF, 102)),
|
||||
'es' : (0x8e8000, 256, 'font_es.png', (0x8ECADF, 99)),
|
||||
'pl' : (0x8e8000, 256, 'font_pl.png', (0x8ECADF, 99)),
|
||||
'pt' : (0x8e8000, 256, 'font_pt.png', (0x8ECADF, 121)),
|
||||
'redux': (0x8e8000, 256, 'font_redux.png', (0x8ECADF, 99)),
|
||||
'nl': (0x8e8000, 256, 'font_nl.png', (0x8ECADF, 99)),
|
||||
'sv': (0x8e8000, 256, 'font_sv.png', (0x8ECADF, 99)),
|
||||
}
|
||||
|
||||
def decode_font():
|
||||
lang = util.ROM.language
|
||||
def decomp_one_spr_2bit(data, offs, target, toffs, pitch, palette_base):
|
||||
for y in range(8):
|
||||
d0, d1 = data[offs + y * 2], data[offs + y * 2 + 1]
|
||||
for x in range(8):
|
||||
t = ((d0 >> x) & 1) * 1 + ((d1 >> x) & 1) * 2
|
||||
target[toffs + y * pitch + (7 - x)] = t + palette_base
|
||||
ft = kFontTypes[lang]
|
||||
if lang == 'pt':
|
||||
W = util.ROM.get_bytes(0x8EFC09, 121 * 3)
|
||||
W = [W[i*3+2] for i in range(121)]
|
||||
remapper = get_pt_remapper()
|
||||
else:
|
||||
W = get_bytes(*ft[3])
|
||||
remapper = {}
|
||||
w = 128 + 15
|
||||
hi = ft[1] // 32
|
||||
h = hi * 17
|
||||
data = get_bytes(ft[0], ft[1] * 16)
|
||||
dst = bytearray(w * h)
|
||||
|
||||
for i in range(ft[1]):
|
||||
x, y = i % 16, i // 16
|
||||
pal_base = 6 * 16
|
||||
base_offs = x * 9 + (y * 8 + (y >> 1)) * w
|
||||
decomp_one_spr_2bit(data, remapper.get(i, i) * 16, dst, base_offs + w, w, pal_base)
|
||||
if (y & 1) == 0:
|
||||
j = (y >> 1) * 16 + x
|
||||
if j < len(W):
|
||||
dst[base_offs + W[j] - 1] = 255
|
||||
pal = convert_snes_palette(get_hud_snes_palette()[:128])
|
||||
pal.extend([0] * 384)
|
||||
pal[0], pal[1], pal[2] = 192, 192, 192
|
||||
pal[255*3+0], pal[255*3+1], pal[255*3+2] = 128, 128, 128
|
||||
save_as_png((w, h), dst, ft[2], pal)
|
||||
if lang != 'pt':
|
||||
assert (data, W) == encode_font_from_png(lang)
|
||||
|
||||
def encode_font_from_png(lang):
|
||||
font_data = Image.open(kFontTypes[lang][2]).tobytes()
|
||||
def encode_one_spr_2bit(data, offs, target, toffs, pitch):
|
||||
for y in range(8):
|
||||
d0, d1 = 0, 0
|
||||
for x in range(8):
|
||||
pixel = data[offs + y * pitch + 7 - x]
|
||||
d0 |= (pixel & 1) << x
|
||||
d1 |= ((pixel >> 1) & 1) << x
|
||||
target[toffs + y * 2 + 0], target[toffs + y * 2 + 1] = d0, d1
|
||||
w = 128 + 15
|
||||
dst = bytearray(256 * 16)
|
||||
def get_width(base_offs):
|
||||
for i in range(8):
|
||||
if font_data[base_offs + i] == 255:
|
||||
break
|
||||
return i + 1
|
||||
W = bytearray()
|
||||
for i in range(256):
|
||||
x, y = i % 16, i // 16
|
||||
base_offs = x * 9 + (y * 8 + (y >> 1)) * w
|
||||
if (y & 1) == 0:
|
||||
W.append(get_width(base_offs))
|
||||
encode_one_spr_2bit(font_data, base_offs + w, dst, i * 16, w)
|
||||
chars_per_lang = kFontTypes[lang][3][1]
|
||||
return dst, W[:chars_per_lang]
|
||||
|
||||
# Returns the dungeon palette for the specified palette index
|
||||
# 0 = lightworld, 1 = darkworld, 2 = dungeon
|
||||
@cache
|
||||
def get_palette_subidx(palset_idx, dungeon_or_ow, which_palette):
|
||||
spmain = 1 if (dungeon_or_ow == 1) else 0
|
||||
if dungeon_or_ow == 2:
|
||||
main, sp0l, sp5l, sp6l = tables.kDungPalinfos[palset_idx]
|
||||
sp0r = (main // 2) + 11
|
||||
sp6r = 10
|
||||
else:
|
||||
sp5l, sp6l = tables.kOwSprPalInfo[palset_idx * 2 : palset_idx * 2 + 2]
|
||||
sp0l = 3 if dungeon_or_ow == 1 else 1
|
||||
sp0r = 9 if dungeon_or_ow == 1 else 7
|
||||
sp6r = 8 if dungeon_or_ow == 1 else 6
|
||||
pal_defs = (sp0l, sp0r,
|
||||
spmain, None, spmain, None, spmain, None, None, None,
|
||||
sp5l, None,
|
||||
sp6l, sp6r,
|
||||
None, None)
|
||||
return pal_defs[which_palette]
|
||||
|
||||
@cache
|
||||
def get_palette_subset(pal_idx, j):
|
||||
if j == None: j = 0
|
||||
pal = [0] * 7
|
||||
if pal_idx == 0: # 0
|
||||
kPalette_SpriteAux3 = get_words(0x9BD39E, 84)
|
||||
return kPalette_SpriteAux3[j * 7 : j * 7 + 7]
|
||||
if pal_idx == 1: # 0R
|
||||
if j < 11:
|
||||
kPalette_MiscSprite = get_words(0x9BD446, 77)
|
||||
return kPalette_MiscSprite[j * 7 : j * 7 + 7]
|
||||
else:
|
||||
kPalette_DungBgMain = get_words(0x9BD734, 1800)
|
||||
return kPalette_DungBgMain[(j - 11) * 90 : (j - 11) * 90 + 7]
|
||||
if pal_idx in (2, 3, 4, 5, 6, 7, 8, 9):
|
||||
o = j * 60 + ((pal_idx - 2) >> 1) * 15 + (pal_idx & 1) * 8
|
||||
kPalette_MainSpr = get_words(0x9BD218, 120)
|
||||
return kPalette_MainSpr[o : o + 7]
|
||||
if pal_idx in (10, 12):
|
||||
kPalette_SpriteAux1 = get_words(0x9BD4E0, 168)
|
||||
return kPalette_SpriteAux1[j * 7 : j * 7 + 7]
|
||||
assert pal_idx != 11
|
||||
if pal_idx == 13:
|
||||
kPalette_MiscSprite = get_words(0x9BD446, 77)
|
||||
return kPalette_MiscSprite[j * 7 : j * 7 + 7]
|
||||
elif pal_idx == 14:
|
||||
kPalette_ArmorAndGloves = get_words(0x9BD308, 75)
|
||||
return kPalette_ArmorAndGloves[1:8]
|
||||
elif pal_idx == 15:
|
||||
kPalette_ArmorAndGloves = get_words(0x9BD308, 75)
|
||||
return kPalette_ArmorAndGloves[9:16]
|
||||
|
||||
@cache
|
||||
def get_full_palette(pal_idx, pal_subidx):
|
||||
rv = []
|
||||
if pal_idx & 1:
|
||||
rv.extend([0x00fe00] * 9)
|
||||
else:
|
||||
rv.extend([0x00fe00] * 1)
|
||||
rv.extend(convert_snes_palette_to_int(get_palette_subset(pal_idx, pal_subidx)))
|
||||
rv += [0xe000e0] * (256 - len(rv))
|
||||
# change the transparent to teal #008080
|
||||
for i in range(0, 128, 8):
|
||||
rv[i] = 0x808000
|
||||
rv[251] = 0xe0c0c0 # blueish text
|
||||
rv[252] = 0xc0c0c0 # palette text
|
||||
rv[253] = 0xf0f0f0 # unallocated
|
||||
rv[254] = 0x404040 # lines
|
||||
rv[255] = 0xe0e0e0 # bg
|
||||
return rv
|
||||
|
||||
@cache
|
||||
def get_font_3x5():
|
||||
return Image.open('../other/3x5_font.png').tobytes()
|
||||
|
||||
def draw_letter3x5(dst, dst_pitch, dx, dy, ch, color):
|
||||
font = get_font_3x5()
|
||||
dst_offs = dy * dst_pitch + dx
|
||||
src_offs = (ord(ch) & 31) * 4 + (ord(ch) // 32) * 6 * 128
|
||||
for y in range(6):
|
||||
for x in range(3):
|
||||
if font[src_offs + y * 128 + x] == 0:
|
||||
dst[dst_offs + y * dst_pitch + x] = color
|
||||
|
||||
def draw_string3x5(dst, dst_pitch, dx, dy, s, color):
|
||||
for ch in s:
|
||||
draw_letter3x5(dst, dst_pitch, dx, dy, ch, color)
|
||||
dx += 4
|
||||
|
||||
def convert_to_24bpp(data, palette):
|
||||
out = [0] * len(data)
|
||||
for i in range(len(data)):
|
||||
out[i] = palette[data[i]]
|
||||
return out
|
||||
|
||||
def concat_byte_arrays(ar):
|
||||
r = bytearray()
|
||||
for a in ar:
|
||||
r += a
|
||||
return r
|
||||
|
||||
def fixup_sprite_set_entry(e, e_prev):
|
||||
sprite_index = int(e.name[:2], 16) if e.name[0] != 'X' else 10000
|
||||
|
||||
if e.dungeon_or_ow != 3:
|
||||
e.tileset = tables.kSpriteTilesets[e.tileset + (64 if e.dungeon_or_ow == 2 else 0)][e.ss_idx]
|
||||
e.high_palette = is_high_3bit_tileset(e.tileset)
|
||||
e.skip_header = False
|
||||
if e.pal_base == None:
|
||||
if sprite_index < len(tables.kSpriteInit_Flags3):
|
||||
e.pal_base = (tables.kSpriteInit_Flags3[sprite_index] >> 1) & 7
|
||||
else:
|
||||
e.pal_base = 4 # just default to some palette
|
||||
|
||||
e.pal_idx = e.pal_base * 2 + e.high_palette
|
||||
e.pal_subidx = get_palette_subidx(e.palset_idx, e.dungeon_or_ow, e.pal_idx)
|
||||
|
||||
if e_prev != None and e_prev.name == e.name and e_prev.pal_idx == e.pal_idx and e_prev.pal_subidx == e.pal_subidx:
|
||||
e.skip_header = True
|
||||
|
||||
# the (pal_idx, pal_subidx) tuple identifies the palette
|
||||
e.encoded_id = e.pal_base | e.tileset << 3 | e.skip_header << 10 | (e.pal_subidx or 0) << 11
|
||||
e.encoded_id = e.encoded_id << 8 | ((e.encoded_id + 41) % 255)
|
||||
|
||||
|
||||
BIGW = 148
|
||||
def save_sprite_set_entry(finaldst, e, master_tilesheets = None):
|
||||
empty_253 = [253] * 8
|
||||
def fill_with_empty(dst, dst_offs):
|
||||
for y in range(8):
|
||||
o = dst_offs + y * BIGW
|
||||
dst[o : o + 8] = empty_253
|
||||
|
||||
def copy_8x8(dst, dst_offs, src, src_offs, base):
|
||||
for y in range(8):
|
||||
for x in range(8):
|
||||
dst[dst_offs + y * BIGW + x] = src[src_offs + y * 128 + x] + base
|
||||
|
||||
def hline(dst, x1, x2, y, color):
|
||||
for x in range(x1, x2 + 1):
|
||||
dst[y * BIGW + x] = color
|
||||
|
||||
def vline(dst, x, y1, y2, color):
|
||||
for y in range(y1, y2 + 1):
|
||||
dst[y * BIGW + x] = color
|
||||
|
||||
def fillrect(dst, x1, x2, y1, y2, color):
|
||||
for y in range(y1, y2 + 1):
|
||||
hline(dst, x1, x2, y, color)
|
||||
|
||||
def encode_id(dst, x, y, v):
|
||||
while v:
|
||||
if v & 1:
|
||||
dst[y * BIGW + x] = 253
|
||||
x -= 1
|
||||
v >>= 1
|
||||
|
||||
palette = get_full_palette(e.pal_idx, e.pal_subidx)
|
||||
|
||||
if not e.skip_header:
|
||||
pal_subidx = e.pal_subidx
|
||||
if e.high_palette:
|
||||
pal_name = '%sR' % e.pal_base
|
||||
else:
|
||||
pal_name = '%s' % e.pal_base
|
||||
if e.pal_base in (1, 2, 3):
|
||||
assert e.pal_subidx in (0, 1)
|
||||
pal_subidx = 'LW' if pal_subidx == 0 else 'DW'
|
||||
if pal_subidx != None:
|
||||
pal_name = '%s-%s' % (pal_name, pal_subidx)
|
||||
|
||||
header = bytearray([255] * BIGW * 9)
|
||||
draw_string3x5(header, BIGW, 1, 3, e.name[:22], 254)
|
||||
|
||||
for i in range(7):
|
||||
xx = BIGW - 37 + i * 5 - 9
|
||||
fillrect(header, xx, xx + 4, 3, 7, i + (9 if e.high_palette else 1))
|
||||
|
||||
draw_string3x5(header, BIGW, BIGW - 9, 3, '%2s' % e.tileset, 252)
|
||||
draw_string3x5(header, BIGW, BIGW - 45 - 1 - len(pal_name) * 4, 3, pal_name, 252)
|
||||
finaldst.extend(convert_to_24bpp(header, palette))
|
||||
|
||||
bigdst = bytearray([255] * BIGW * 36)
|
||||
hline(bigdst, 1, 137, 0, 254)
|
||||
hline(bigdst, 1, 137, 34, 254)
|
||||
vline(bigdst, 1, 0, 34, 254)
|
||||
vline(bigdst, 137, 0, 34, 254)
|
||||
|
||||
encode_id(bigdst, 137, 35, e.encoded_id << 9 | 0x55)
|
||||
|
||||
src = decode_3bit_tileset(e.tileset) # returns 128x64
|
||||
|
||||
for i in range(16*4):
|
||||
x, y = i % 16, i // 16
|
||||
dst_offs = (y * 8 + 1 + (y >> 1)) * BIGW + x * 8 + 2 + (x >> 1)
|
||||
if x & 8: dst_offs
|
||||
m = e.matrix[y][x]
|
||||
if m == '.':
|
||||
fill_with_empty(bigdst, dst_offs)
|
||||
else:
|
||||
copy_8x8(bigdst, dst_offs, src, x * 8 + y * 8 * 128, 0)
|
||||
if master_tilesheets:
|
||||
master_tilesheets.insert(e.tileset, src, x, y, palette, 0)
|
||||
|
||||
# display vram addresses
|
||||
draw_string3x5(bigdst, BIGW, BIGW - 9, 4, '%Xx' % (e.ss_idx * 0x4), 251)
|
||||
draw_string3x5(bigdst, BIGW, BIGW - 9, 4 + 17, '%Xx' % (e.ss_idx * 0x4 + 2), 251)
|
||||
|
||||
# collapse unused matrix sections
|
||||
if all(m=='.' for m in e.matrix[0]) and all(m=='.' for m in e.matrix[1]):
|
||||
del bigdst[1*BIGW:17*BIGW]
|
||||
elif all(m=='.' for m in e.matrix[2]) and all(m=='.' for m in e.matrix[3]):
|
||||
del bigdst[18*BIGW:34*BIGW]
|
||||
|
||||
if e.skip_header:
|
||||
draw_string3x5(bigdst, BIGW, BIGW - 9, len(bigdst)//BIGW - 6, '%.2d' % e.tileset, 252)
|
||||
|
||||
finaldst.extend(convert_to_24bpp(bigdst, palette))
|
||||
|
||||
def fixup_entries():
|
||||
e_prev = None
|
||||
for e in sprite_sheet_info.entries:
|
||||
fixup_sprite_set_entry(e, e_prev)
|
||||
e_prev = e
|
||||
|
||||
class MasterTilesheets:
|
||||
def __init__(self):
|
||||
self.sheets = {}
|
||||
|
||||
def insert(self, tileset, src, xp, yp, palette, pal_base):
|
||||
sheet = self.sheets.get(tileset)
|
||||
if sheet == None:
|
||||
sheet = (array.array('I', [0x00f000] * 128 * 32), None)
|
||||
self.sheets[tileset] = sheet
|
||||
sheet24 = sheet[0]
|
||||
for y in range(yp * 8, yp * 8 + 8):
|
||||
for x in range(xp * 8, xp * 8 + 8):
|
||||
o = y * 128 + x
|
||||
sheet24[o] = palette[pal_base + src[o]]
|
||||
|
||||
def add_verify_8x8(self, tileset, pal_lut, img_data, pitch, dst_pos, src_pos):
|
||||
# add to the master sheet
|
||||
sheet = self.sheets.get(tileset)
|
||||
if sheet == None:
|
||||
sheet = (array.array('I', [0x00f000] * 128 * 32), bytearray([248] * 128 * 32))
|
||||
self.sheets[tileset] = sheet
|
||||
sheet24, sheet8 = sheet[0], sheet[1]
|
||||
for y in range(8):
|
||||
for x in range(8):
|
||||
pixel = img_data[src_pos + y * pitch + x]
|
||||
o = dst_pos + y * 128 + x
|
||||
sheet24[o] = pixel
|
||||
pixel8 = pal_lut.get(pixel)
|
||||
if pixel8 == None:
|
||||
raise Exception('Pixel #%.6x not found' % pixel)
|
||||
if sheet8[o] != 248 and pixel8 != sheet8[o]:
|
||||
raise Exception('Pixel has more than one value')
|
||||
sheet8[o] = pixel8
|
||||
|
||||
def save_to_all_sheets(self, save_also_8bit = False):
|
||||
rv = array.array('I')
|
||||
if save_also_8bit:
|
||||
rv8 = bytearray()
|
||||
|
||||
def extend_with_string_line(text):
|
||||
header = array.array('I', [0xf0f0f0] * 128 * 8)
|
||||
draw_string3x5(header, 128, 1, 2, text, 0x404040)
|
||||
rv.extend(header)
|
||||
if save_also_8bit:
|
||||
header8 = bytearray([255] * 128 * 8)
|
||||
draw_string3x5(header8, 128, 1, 2, text, 254)
|
||||
rv8.extend(header8)
|
||||
extend_with_string_line('AUTO GENERATED DO NOT EDIT')
|
||||
kk = 0
|
||||
for k in sorted(self.sheets.keys()):
|
||||
extend_with_string_line('Sheet %d' % k)
|
||||
rv.extend(self.sheets[k][0])
|
||||
if save_also_8bit:
|
||||
rv8.extend(self.sheets[k][1])
|
||||
if k != kk:
|
||||
print('Missing %d' % kk)
|
||||
kk = k + 1
|
||||
if save_also_8bit:
|
||||
palette8 = get_full_palette(4, 0)
|
||||
for i, v in enumerate(convert_snes_palette_to_int(get_palette_subset(5, 0))):
|
||||
palette8[i + 9] = v
|
||||
palette8[248] = 0x00f000
|
||||
save_as_png((128, len(rv)//128), rv8, 'sprites/all_sheets_8bit.png', palette = convert_int24_palette_to_bytes(palette8))
|
||||
self.verify_identical_to_snes()
|
||||
save_as_24bpp_png((128, len(rv)//128), rv, 'sprites/all_sheets.png')
|
||||
|
||||
def verify_identical_to_snes(self):
|
||||
for tileset in range(103):
|
||||
a = self.encode_sheet_in_snes_format(tileset)
|
||||
b = get_unpacked_snes_tileset(tileset)
|
||||
if a != b:
|
||||
print('Sheet %d mismatch' % tileset)
|
||||
break
|
||||
|
||||
# convert a tileset to the 8bit snes format, 24 bytes per tile
|
||||
def encode_sheet_in_snes_format(self, tileset):
|
||||
sheet8 = self.sheets[tileset][1]
|
||||
result = bytearray(24 * 16 * 4)
|
||||
def encode_into(dst_pos, src_pos):
|
||||
for y in range(8):
|
||||
for x in range(8):
|
||||
b = sheet8[src_pos + 128 * y + 7 - x]
|
||||
result[dst_pos+2*y] |= (b & 1) << x
|
||||
result[dst_pos+2*y+1] |= ((b & 2) >> 1) << x
|
||||
result[dst_pos+y+16] |= ((b & 4) >> 2) << x
|
||||
for y in range(4):
|
||||
for x in range(16):
|
||||
encode_into((y * 16 + x) * 24, y * 8 * 128 + x * 8)
|
||||
return result
|
||||
|
||||
def decode_sprite_sheets():
|
||||
master_tilesheets = MasterTilesheets()
|
||||
|
||||
fixup_entries()
|
||||
for char in "0123456789ABCDEFX":
|
||||
dst = []
|
||||
for e in sprite_sheet_info.entries:
|
||||
if e.name[0].upper() == char:
|
||||
save_sprite_set_entry(dst, e, master_tilesheets)
|
||||
if len(dst):
|
||||
save_as_24bpp_png((BIGW, len(dst) // BIGW), dst, 'sprites/sprites_%s.png' % char)
|
||||
master_tilesheets.save_to_all_sheets()
|
||||
|
||||
def load_sprite_sheets():
|
||||
master_tilesheets = MasterTilesheets()
|
||||
for char in "0123456789ABCDEFX":
|
||||
img = Image.open('sprites/sprites_%s.png' % char)
|
||||
img_size = img.size
|
||||
img_data = array.array('I', (a[0] | a[1] << 8 | a[2] << 16 for a in img.getdata()))
|
||||
pitch = img.size[0]
|
||||
|
||||
def find_next_tag(start_pos):
|
||||
for i in range(start_pos, len(img_data) - pitch * 2, pitch if start_pos != 0 else 1):
|
||||
if img_data[i+0] == 0x404040 and img_data[i+pitch] == 0x404040 and \
|
||||
img_data[i+pitch * 2 + 0] == 0xf0f0f0 and img_data[i+pitch * 2 - 1] == 0xe0e0e0 and \
|
||||
img_data[i+pitch * 2 - 2] == 0xf0f0f0 and img_data[i+pitch * 2 - 3] == 0xe0e0e0 and \
|
||||
img_data[i+pitch * 2 - 4] == 0xf0f0f0 and img_data[i+pitch * 2 - 5] == 0xe0e0e0 and \
|
||||
img_data[i+pitch * 2 - 6] == 0xf0f0f0 and img_data[i+pitch * 2 - 7] == 0xe0e0e0:
|
||||
return i + pitch * 2
|
||||
else:
|
||||
return None
|
||||
|
||||
def decode_tag(pos):
|
||||
r = 0
|
||||
for i in range(64):
|
||||
v = img_data[pos - 63 + i]
|
||||
assert v in (0xe0e0e0, 0xf0f0f0)
|
||||
r = r * 2 + (v == 0xf0f0f0)
|
||||
if (r & 0x1ff) != 0x55:
|
||||
raise Exception('Invalid tag')
|
||||
r >>= 9
|
||||
if (((r >> 8) + 41) % 255) != (r & 0xff):
|
||||
raise Exception('Invalid checksum')
|
||||
r >>= 8
|
||||
return r & 7, (r >> 3) & 127, (r >> 10) & 1, (r >> 11) & 31
|
||||
|
||||
def determine_image_rects(tag_pos):
|
||||
h = 1
|
||||
while (img_data[tag_pos - pitch * (h + 1)] == 0x404040):
|
||||
h += 1
|
||||
if h == 19:
|
||||
if img_data[tag_pos - pitch * 2 - 1] == 0xe0e0e0:
|
||||
image_rects = ((0, tag_pos - 135 - pitch * 18), )
|
||||
elif img_data[tag_pos - pitch * 18 - 1] == 0xe0e0e0:
|
||||
image_rects = ((1, tag_pos - 135 - pitch * 17), )
|
||||
else:
|
||||
raise Exception('cant uncompact')
|
||||
elif h == 35:
|
||||
image_rects = ((0, tag_pos - 135 - pitch * 34), (1, tag_pos - 135 - pitch * 17))
|
||||
else:
|
||||
raise Exception('height not found')
|
||||
return image_rects, h
|
||||
|
||||
def get_pal_lut(tag_pos, is_high):
|
||||
# read palette colors from the image pixels
|
||||
pal_lut = {img_data[tag_pos + 5 * i] : i + (9 if is_high else 1) for i in range(7)}
|
||||
pal_lut[0x808000] = 0 # transparent color
|
||||
return pal_lut
|
||||
|
||||
def is_empty(pos):
|
||||
return all(img_data[pos+y*pitch+x] == 0xf0f0f0 for x in range(8) for y in range(8))
|
||||
|
||||
pal_lut = None
|
||||
tag_pos = 0
|
||||
while True:
|
||||
tag_pos = find_next_tag(tag_pos)
|
||||
if tag_pos == None:
|
||||
break
|
||||
pal_base, tileset, headerless, pal_subidx = decode_tag(tag_pos)
|
||||
image_rects, box_height = determine_image_rects(tag_pos)
|
||||
if not headerless:
|
||||
pal_lut = get_pal_lut(tag_pos - pitch * (box_height + 3) - 34, is_high_3bit_tileset(tileset))
|
||||
for idx, sheet_pos in image_rects:
|
||||
for y in range(2):
|
||||
for x in range(16):
|
||||
src_pos = sheet_pos + (y * 8 + (y >> 1)) * pitch + (x * 8 + (x >> 1))
|
||||
dst_pos = (y + idx * 2) * 8 * 128 + x * 8
|
||||
if not is_empty(src_pos):
|
||||
master_tilesheets.add_verify_8x8(tileset, pal_lut, img_data, pitch, dst_pos, src_pos)
|
||||
return master_tilesheets
|
||||
|
||||
#if __name__ == "__main__":
|
||||
# ROM = util.load_rom(sys.argv[1] if len(sys.argv) >= 2 else None, True)
|
||||
# decode_font()
|
||||
|
||||
|
||||
|
||||
1
assets/sprites/.gitignore
vendored
1
assets/sprites/.gitignore
vendored
@@ -1 +0,0 @@
|
||||
/*.png
|
||||
@@ -1,551 +0,0 @@
|
||||
import util, sys
|
||||
|
||||
kTextAlphabet_US = [
|
||||
"A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", # 0 - 15
|
||||
"Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z", "a", "b", "c", "d", "e", "f", # 16 - 31
|
||||
"g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", # 32 - 47
|
||||
"w", "x", "y", "z", "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "!", "?", # 48 - 63
|
||||
"-", ".", ",",
|
||||
# 64 - 79
|
||||
"[...]", ">", "(", ")",
|
||||
"[Ankh]", "[Waves]", "[Snake]", "[LinkL]", "[LinkR]",
|
||||
"\"", "[Up]", "[Down]", "[Left]",
|
||||
# 80 - 95
|
||||
"[Right]", "'", "[1HeartL]", "[1HeartR]", "[2HeartL]", "[3HeartL]", "[3HeartR]",
|
||||
"[4HeartL]", "[4HeartR]", " ", "<", "[A]", "[B]", "[X]", "[Y]",
|
||||
]
|
||||
|
||||
kTextAlphabet_DE = [
|
||||
"A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", # 0 - 15
|
||||
"Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z", "a", "b", "c", "d", "e", "f", # 16 - 31
|
||||
"g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", # 32 - 47
|
||||
"w", "x", "y", "z", "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "!", "?", # 48 - 63
|
||||
# 64 - 79
|
||||
"-", ".", ",", "[...]", ">", "(", ")",
|
||||
"[Ankh]", "[Waves]", "[Snake]", "[LinkL]", "[LinkR]",
|
||||
"\"", "[UpL]", "[UpR]", "[LeftL]",
|
||||
# 80 - 95
|
||||
"[LeftR]", "'", "[1HeartL]", "[1HeartR]", "[2HeartL]", "[3HeartL]", "[3HeartR]",
|
||||
"[4HeartL]", "[4HeartR]", " ", "ö", "[A]", "[B]", "[X]", "[Y]", "ü",
|
||||
# 96-111
|
||||
"ß", ":", "[DownL]", "[DownR]", "[RightL]", "[RightR]",
|
||||
"è", "é", "ê", "à", "ù", "ç", "Ä", "Ö", "Ü", "ä"
|
||||
# 112-
|
||||
]
|
||||
|
||||
kTextAlphabet_FR = [
|
||||
"A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", # 0 - 15
|
||||
"Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z", "a", "b", "c", "d", "e", "f", # 16 - 31
|
||||
"g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", # 32 - 47
|
||||
"w", "x", "y", "z", "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "!", "?", # 48 - 63
|
||||
# 64 - 79
|
||||
"-", ".", ",", "[...]", ">", "(", ")",
|
||||
"[Ankh]", "[Waves]", "[Snake]", "[LinkL]", "[LinkR]",
|
||||
"\"", "[UpL]", "[UpR]", "[LeftL]",
|
||||
# 80 - 95
|
||||
"[LeftR]", "'", "[1HeartL]", "[1HeartR]", "[2HeartL]", "[3HeartL]", "[3HeartR]",
|
||||
"[4HeartL]", "[4HeartR]", " ", "ö", "[A]", "[B]", "[X]", "[Y]", "ü",
|
||||
# 96-111
|
||||
"ô", ":", "[DownL]", "[DownR]", "[RightL]", "[RightR]",
|
||||
"è", "é", "ê", "à", "ù", "ç", "â", "û", "î", "ä"
|
||||
# 112-
|
||||
]
|
||||
|
||||
kText_CommandLengths_US = [1, 1, 1, 1, 2, 2, 2, 2, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 1, 1, 1, 1, 1, ]
|
||||
kText_CommandNames_US = [
|
||||
"NextPic", "Choose", "Item", "Name", "Window", "Number",
|
||||
"Position","ScrollSpd", "Selchg", "Unused_Crash", "Choose3",
|
||||
"Choose2", "Scroll", "1", "2", "3", "Color",
|
||||
"Wait", "Sound", "Speed", "Unused_Mark", "Unused_Mark2", "Unused_Clear",
|
||||
"Waitkey"
|
||||
]
|
||||
|
||||
kTextDictionary_US = [
|
||||
' ', ' ', ' ', "'s ", 'and ',
|
||||
'are ', 'all ', 'ain', 'and', 'at ',
|
||||
'ast', 'an', 'at', 'ble', 'ba',
|
||||
'be', 'bo', 'can ', 'che', 'com',
|
||||
'ck', 'des', 'di', 'do', 'en ',
|
||||
'er ', 'ear', 'ent', 'ed ', 'en',
|
||||
'er', 'ev', 'for', 'fro', 'give ',
|
||||
'get', 'go', 'have', 'has', 'her',
|
||||
'hi', 'ha', 'ight ', 'ing ', 'in',
|
||||
'is', 'it', 'just', 'know', 'ly ',
|
||||
'la', 'lo', 'man', 'ma', 'me',
|
||||
'mu', "n't ", 'non', 'not', 'open',
|
||||
'ound', 'out ', 'of', 'on', 'or',
|
||||
'per', 'ple', 'pow', 'pro', 're ',
|
||||
're', 'some', 'se', 'sh', 'so',
|
||||
'st', 'ter ', 'thin', 'ter', 'tha',
|
||||
'the', 'thi', 'to', 'tr', 'up',
|
||||
'ver', 'with', 'wa', 'we', 'wh',
|
||||
'wi', 'you', 'Her', 'Tha', 'The',
|
||||
'Thi', 'You',
|
||||
]
|
||||
|
||||
|
||||
kText_CommandLengths_EU = [1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2]
|
||||
kText_CommandNames_EU = [
|
||||
"Selchg", "Choose3", "Choose2", "Scroll", "1", "2", "3",
|
||||
"Color", "Wait", "Sound", "Speed", "Mark", "Mark2",
|
||||
"Clear", "Waitkey", "EndMessage", "NextPic", "Choose",
|
||||
"Item", "Name", "Window", "Number", "Position", "ScrollSpd",
|
||||
]
|
||||
|
||||
kTextDictionary_DE = [
|
||||
' ', ' ', ' ', '-Knopf', ' ich ',
|
||||
' Sch', ' Ver', ' zu ', ' es ', 'aber',
|
||||
'alle', 'auch', 'ang', 'aus', 'auf',
|
||||
'an', 'bist', 'bin', 'bei', 'der ',
|
||||
'die ', 'das ', 'den ', 'dem ', 'daß',
|
||||
'der', 'die', 'das', 'den', 'da',
|
||||
'etwas', 'ein ', 'ein', 'en ', 'er ',
|
||||
'es ', 'en', 'er', 'es', 'ei',
|
||||
'für', 'fe', 'habe', 'hier', 'hast',
|
||||
'her', 'ich ', 'icht', 'ich', 'ist',
|
||||
'ie ', 'im', 'ie', 'kannst ', 'kannst',
|
||||
'kommen', 'kann ', 'll', 'mich', 'mein',
|
||||
'mit', 'mal', 'mir', 'nicht ', 'nicht',
|
||||
'nen', 'nn', 'och ', 'och', 'or',
|
||||
'schon', 'sich', 'sein', 'sch', 'sie',
|
||||
'st', 'tte', 'te ', 'te', 'und ',
|
||||
'und', 'ung', 'um', 'von', 'ver',
|
||||
'vor', 'wird', 'zu ', 'Amulett', 'Aber',
|
||||
'Deine', 'Dich ', 'Dir ', 'Dir', 'Der',
|
||||
'Die', 'Das', 'Du ', 'Du', 'Da',
|
||||
'Ein', 'Hyrule', 'Hier', 'Ich ', 'Master-Schwert',
|
||||
'Mach', 'Rubine', 'Sch', 'Sie', 'Ver',
|
||||
'Weisen', 'Zelda',
|
||||
]
|
||||
|
||||
kTextDictionary_FR = [
|
||||
' ', ' de ', ' la ', ' le ', ' ! ',
|
||||
' d', ' p', ' t', ' !', ", c'est moi, Sahasrahla",
|
||||
', ', 'ais ', 'as ', 'an', 'ai',
|
||||
'a ', 'che', 'ce', 'ch', 'dans ',
|
||||
'des ', 'de ', 'de', 'est ', 'ent',
|
||||
'en ', 'er ', 'es ', 'en', 'es',
|
||||
'et', 'eu', 'e,', 'e ', 'ique',
|
||||
'ien', 'is ', 'ie', 'in', 'ir',
|
||||
'is', 'i ', 'les ', 'la ', 'le ',
|
||||
'le', 'll', 'maintenant', 'magique', 'ment',
|
||||
'mon', 'mai', 'me', 'ne ', 'onne',
|
||||
'oir', 'our', 'ouv', 'oi', 'on',
|
||||
'ou', 'or', 'pouvoir', 'pour', 'peux',
|
||||
'pas', 'que ', 'qu', 'rubis', 're ',
|
||||
'ra', 're', 'r ', 'sorcier', 's l',
|
||||
's d', 'se', 'so', 's ', 'tro',
|
||||
'te ', 'tu ', 'te', 't ', 'un',
|
||||
'ur', 'u ', 'ver', 'Ah ! Ah ! Ah !', "C'est",
|
||||
'Ganon', 'Maintenant', 'Merci', 'Monde', 'Perle de Lune',
|
||||
'Tu as trouvé ', 'Ténèbres', 'Tu peux', 'Tu ',
|
||||
]
|
||||
|
||||
|
||||
# Uses the original format
|
||||
def org_encoder(cmd, param):
|
||||
if cmd not in kText_CommandNames_US:
|
||||
raise Exception(f'Invalid cmd {cmd}')
|
||||
cmd_index = kText_CommandNames_US.index(cmd)
|
||||
if kText_CommandLengths_US[cmd_index] != (1 if param == None else 2):
|
||||
raise Exception(f'Invalid cmd params {cmd} {param}')
|
||||
if param == None:
|
||||
return [cmd_index + 0x67]
|
||||
return [cmd_index + 0x67, int(param)]
|
||||
|
||||
kCmdInfo = {
|
||||
"Scroll" : (0x80, ),
|
||||
"Waitkey" : (0x81, ),
|
||||
"1" : (0x82, ),
|
||||
"2" : (0x83, ),
|
||||
"3" : (0x84, ),
|
||||
"Name" : (0x85, ),
|
||||
"Wait" : (0x87, {i:i+0x00 for i in range(16)}),
|
||||
"Color" : (0x87, {i:i+0x10 for i in range(16)}),
|
||||
"Number" : (0x87, {i:i+0x20 for i in range(16)}),
|
||||
"Speed" : (0x87, {i:i+0x30 for i in range(16)}),
|
||||
"Sound" : (0x87, {45 : 0x40, 64 : None}), # pt uses 64 for some reason
|
||||
"Choose" : (0x87, 0x80),
|
||||
"Choose2" : (0x87, 0x81),
|
||||
"Choose3" : (0x87, 0x82),
|
||||
"Selchg" : (0x87, 0x83),
|
||||
"Item" : (0x87, 0x84),
|
||||
"NextPic" : (0x87, 0x85),
|
||||
"Window" : (0x87, {0 : None, 2 : 0x86}),
|
||||
"Position" : (0x87, {0: 0x87, 1: 0x88}),
|
||||
"ScrollSpd" : (0, {0 : None}),
|
||||
}
|
||||
|
||||
# Uses another format where there's more bytes left for characters
|
||||
def new_encoder(cmd, param):
|
||||
if cmd not in kCmdInfo:
|
||||
raise Exception(f'Invalid cmd {cmd}')
|
||||
info = kCmdInfo[cmd]
|
||||
if len(info) <= 1 or isinstance(info[1], int):
|
||||
if param != None:
|
||||
raise Exception(f'Invalid cmd params {cmd} {param}')
|
||||
return info
|
||||
else:
|
||||
if param == None or param not in info[1]:
|
||||
raise Exception(f'Invalid cmd params {cmd} {param}')
|
||||
r = info[1][param]
|
||||
return (info[0], r) if r != None else ()
|
||||
|
||||
kEncoders = {'org' : org_encoder, 'new' : new_encoder}
|
||||
|
||||
|
||||
class LangUS:
|
||||
alphabet = kTextAlphabet_US
|
||||
dictionary = kTextDictionary_US
|
||||
command_lengths = kText_CommandLengths_US
|
||||
command_names = kText_CommandNames_US
|
||||
rom_addrs = [0x9c8000, 0x8edf40]
|
||||
COMMAND_START = 0x67
|
||||
SWITCH_BANK = 0x80
|
||||
FINISH = 0xff
|
||||
DICT_BASE_ENC, DICT_BASE_DEC = 0x88, 0x88
|
||||
ESCAPE_CHARACTER = None
|
||||
encoder = 'org'
|
||||
|
||||
class LangEN(LangUS):
|
||||
alphabet = kTextAlphabet_DE
|
||||
dictionary = kTextDictionary_US
|
||||
rom_addrs = [0x9c8000, 0x8edf60]
|
||||
|
||||
class LangES(LangUS):
|
||||
alphabet = [
|
||||
"A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", # 0 - 15
|
||||
"Q", "R", "S", "T", "U", "V", "W", "é", "Y", "Z", "a", "b", "c", "d", "e", "f", # 16 - 31
|
||||
"g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", # 32 - 47
|
||||
"ó", "x", "y", "z", "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "!", "?", # 48 - 63
|
||||
"[Waves]", ".", ",",
|
||||
# 64 - 79
|
||||
"[...]", ">", "(", ")",
|
||||
"ñ", "ú", "á", "[LinkL]", "[LinkR]", "\"", "[Up]", "[Down]", "[Left]",
|
||||
# 80 - 95
|
||||
"[Right]", "í", "[1HeartL]", "[1HeartR]", "[2HeartL]", "[3HeartL]", "[3HeartR]",
|
||||
"[Ankh]", "[4HeartR]", " ", "[Snake]", "[A]", "[B]", "[X]", "[Y]", "[I]",
|
||||
"¡", "¿", "Ñ"
|
||||
]
|
||||
# "[Ankh]", "[Waves]", "[Snake]"
|
||||
|
||||
dictionary = [
|
||||
' ', ' ', ' ', ' en', ' la ', ' el ', ' de ', 'ien', 'tra', ' de',
|
||||
'te ', 'ar', 'a ', 'ada', 'es', 'as', 'o ', ' con', 'ero', 'ado',
|
||||
'e ', 'que', 'en', 'al', 'os ', 'ora', 'nte', ' al', 'lo ', 'or',
|
||||
'os', 'er', 'aci', 'res', ' que ', ' es', 'el', 'los ', 'tar', ' se',
|
||||
', ', 'ro', ' de l', ' est', 're', 'on', 'an', 'pued', ' del', 'ás ',
|
||||
'la', 'ti', 'la ', 'Es', 'to', 'ta', 'para', 'uer', 'ier', ' un ',
|
||||
' por', 'oder', 'da', 'in', 'cu', ' ha', 'per', 'ano', ' ve', 'cer',
|
||||
'lo', ' no ', 'ic', 'ra', 'ab', 'ir', ' una', 'undo', 'es ', 'as ',
|
||||
'con', 'a, ', 'te', ' m', 'gu', ' tu', 'ando', ' p', 'de', 'le',
|
||||
'ol', 'o, ', 'ten', 'lle', ' a ', 'aba', 'com',
|
||||
]
|
||||
rom_addrs = [0x9c8000, 0x8edf40]
|
||||
|
||||
|
||||
class LangNL(LangUS):
|
||||
alphabet = [
|
||||
"A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", # 0 - 15
|
||||
"Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z", "a", "b", "c", "d", "e", "f", # 16 - 31
|
||||
"g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", # 32 - 47
|
||||
"w", "x", "y", "z", "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "!", "?", # 48 - 63
|
||||
"-", ".", ",", "[...]", ">", "(", ")", "[Ankh]",
|
||||
"[Waves]", "[Snake]", "[LinkL]", "[LinkR]", "\"", "[Up]", "[Down]", "[Left]",
|
||||
# 80 - 95
|
||||
"[Right]", "'", "[1HeartL]", "[1HeartR]", "[2HeartL]", "[3HeartL]", "[3HeartR]",
|
||||
"[4HeartL]", "[4HeartR]", " ", "<", "[A]", "[B]", "[X]", "[Y]",
|
||||
]
|
||||
|
||||
dictionary = [
|
||||
' ', ' ', ' ', "'s ", 'and ', 'are ', 'all ', 'ain', 'and', 'at ',
|
||||
'ast', 'an', 'at', 'ble', 'ba', 'be', 'bo', 'can ', 'che', 'com',
|
||||
'ck', 'des', 'di', 'do', 'en ', 'er ', 'ear', 'ent', 'ed ', 'en',
|
||||
'er', 'ev', 'for', 'fro', 'give ', 'get', 'go', 'have', 'has', 'her',
|
||||
'hi', 'ha', 'ight ', 'ing ', 'in', 'is', 'it', 'just', 'know', 'ly ',
|
||||
'la', 'lo', 'man', 'ma', 'me', 'mu', "n't ", 'non', 'not', 'open',
|
||||
'ound', 'out ', 'of', 'on', 'or', 'per', 'ple', 'pow', 'pro', 're ',
|
||||
're', 'some', 'se', 'sh', 'so', 'st', 'ter ', 'thin', 'ter', 'tha',
|
||||
'the', 'thi', 'to', 'tr', 'up', 'ver', 'with', 'wa', 'we', 'wh',
|
||||
'wi', 'you', 'Her', 'Tha', 'The', 'Thi', 'You',
|
||||
]
|
||||
rom_addrs = [0x9c8000, 0x8edf40]
|
||||
|
||||
|
||||
|
||||
class LangSV(LangUS):
|
||||
alphabet = [
|
||||
"A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "Ö", "P", # 0 - 15
|
||||
"Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z", "a", "b", "c", "d", "e", "f", # 16 - 31
|
||||
"g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", # 32 - 47
|
||||
"w", "x", "y", "z", "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "!", "?", # 48 - 63
|
||||
"å", ".", ",", "ä", ">", "(", ")","ö",
|
||||
"Å", "Ä", "[LinkL]", "[LinkR]", "\"", "[Up]", "[Down]", "[Left]",
|
||||
# 80 - 95
|
||||
"[Right]", "'", "[1HeartL]", "[1HeartR]", "[2HeartL]", "[3HeartL]", "[3HeartR]",
|
||||
"[4HeartL]", "[4HeartR]", " ", "<", "[Ankh]", "[Waves]", "[Snake]", "-", "[I]",
|
||||
"[i]", "…", " ",
|
||||
]
|
||||
|
||||
dictionary = [
|
||||
' ', ' ', ' ', 'Du ', 'till', 'vill', 'bara', 'det', 'den', 'och',
|
||||
'en ', 'r ', 'n ', 'ett', 'en', ' d', 'a ', 'Hjäl', 'har', 'ter',
|
||||
't ', 'var', ' s', 'de', 'kan', 'med', 'som', 'för', 'att', 'ar',
|
||||
' h', 'er', 'jag', 'dig', 'öppna', 'mig', 'är', 'inte', 'hit', 'på ',
|
||||
'an', 'e ', 'rupie', '0kej', ' m', 'et', ', ', 'gång', 'måst', 'ten',
|
||||
' f', 'u ', 'men', 'te', 'tt', 'ka', 'vara', 'ken', '0m ', 'från',
|
||||
'myck', 'någo', 'in', ' k', ' i', 'vil', 'bar', 'ond', 'För', 'Jag',
|
||||
'ra', 'tack', 'll', 'g ', 'ta', 'om', 'anna', 'alla', 'en,', 'ber',
|
||||
'hem', 'han', 'st', 'ig', ' t', 'tro', 'kraf', 'ör', ' v', 'ag',
|
||||
'… ', 'får', 'sin', 'mme', 'mma', 'en ', 'tat',
|
||||
]
|
||||
rom_addrs = [0x9c8000, 0x8edf40]
|
||||
|
||||
|
||||
class LangPL(LangUS):
|
||||
alphabet = [
|
||||
"A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", # 0 - 15
|
||||
"Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z", "a", "b", "c", "d", "e", "f", # 16 - 31
|
||||
"g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", # 32 - 47
|
||||
"w", "x", "y", "z", "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "!", "?", # 48 - 63
|
||||
"-", ".", ",", "ć", "[Right]", "(", ")", "[Ankh]",
|
||||
"[Waves]", "[Snake]", "[LinkL]", "[LinkR]","\"", "[Up]", "[Down]", "ę",
|
||||
|
||||
"ł", "ń", "[1HeartL]", "[1HeartR]", "[2HeartL]", "[3HeartL]", "[3HeartR]",
|
||||
"ą", "[4HeartR]", " ", "[Left]", "ó", "ś", "ż", "ź", "Ł",
|
||||
"Ś", "Ż", "Ź",
|
||||
] #
|
||||
dictionary = ['Trój', '...', 'ść', 'Nie', ' nie', ' się', 'może', ' że', 'and', 'at ',
|
||||
' ty', 'an', 'at', 'kus', 'ba', 'be', 'bo', 'chce', 'che', 'ki ',
|
||||
'za', 'des', 'di', 'do', 'en ', 'er ', 'sz ', 'ent', 'ed ', 'en',
|
||||
'er', ' w', 'moc', 'zię', 'przez', 'ale', 'go', 'dzie', 'has', 'rze',
|
||||
'hi', 'ha', 'który', 'aby ', 'in', 'is', 'it', 'twoj', 'Może', 'łeś',
|
||||
'la', 'lo', 'czn', 'ma', 'me', 'mu', 'szcz', 'ska', 'śli', 'przy',
|
||||
'znaj', 'iecz', 'of', 'on', 'or', ' ', 'ple', 'pow', 'pro', 're ',
|
||||
're', 'mnie', 'se', ' z', 'so', 'st', 'któr', ' jak', 'ksz', 'sze',
|
||||
'coś', ' je', 'to', 'tr', 'up', 'kie', 'praw', 'wa', 'we', 'mi',
|
||||
'wi', 'szy', 'chc', 'pra', 'cie', ' i ', 'esz',
|
||||
]
|
||||
|
||||
rom_addrs = [0x9c8000, 0x8edf40]
|
||||
|
||||
class LangPT(LangUS):
|
||||
alphabet = [
|
||||
"A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", # 0 - 15
|
||||
"Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z", "a", "b", "c", "d", "e", "f", # 16 - 31
|
||||
"g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", # 32 - 47
|
||||
"w", "x", "y", "z", "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "!", "?", # 48 - 63
|
||||
"-", ".", ",", "[...]", ">", "(", ")", "[Ankh]",
|
||||
"[Waves]", "[Snake]", "[LinkL]", "[LinkR]", "\"", "[Up]", "[Down]", "[Left]",
|
||||
# 80 - 95
|
||||
"[Right]", "'", "[1HeartL]", "[1HeartR]", "[2HeartL]", "[3HeartL]", "[3HeartR]",
|
||||
"[4HeartL]", "[4HeartR]", " ", "<", "[A]", "[B]", "[X]", "[Y]", "[I]",
|
||||
|
||||
"¡", "[!]", "Á", "À", "Â", "Ã", "É", "Ê", "Í", "Ó", "Ô", "Õ", "Ú", "á", "à", "â",
|
||||
"ã", "é", "ê", "í", "ó", "ô", "õ", "ú", "ç",
|
||||
]
|
||||
dictionary = [
|
||||
' ', ' ', ' ', ' ', 'o ', 'a ', 'e ', '..', 'de', 'ar',
|
||||
's ', 'ra', ' d', 'es', 'ocê ', 'do', ' a', ' p', 'er', ' e',
|
||||
'que', 'r ', 'os', 'te', ', ', 'as', 'or', 'm ', 'en', ' o',
|
||||
'nt', 're', ' s', 'co', 'da', 'se', 'st', ' c', ' m', 'em',
|
||||
'ma', 'ta', ' n', 'ad', 'on', 'al', 'ro', 'an', 'u ', 'nd',
|
||||
' um', 'pa', 'ca', 'el', ' f', 'to', 'in', ' t', 'ou', 'ei',
|
||||
'ss', 'ir', 'no', 'ri', 'tr', 'me', 'la', 'ia', 'le', 've',
|
||||
'is', 'sa', 'eu', 'pe', 'a.', 'na', 'so', 'mo', 'ga', 'o.',
|
||||
'á ', 'lo', 'ha', 'pr', 'ua', ' l', '! ', 'ui', 'am', 'ti',
|
||||
'io', 'gu', 'i ', 'di', 'nh', ' i', 'id',
|
||||
]
|
||||
ESCAPE_CHARACTER = 0x62
|
||||
rom_addrs = [0x9c8000, 0x8edf40]
|
||||
encoder = 'new'
|
||||
|
||||
def __init__(self):
|
||||
assert len(self.alphabet) == 121
|
||||
|
||||
class LangEU:
|
||||
command_lengths = kText_CommandLengths_EU
|
||||
command_names = kText_CommandNames_EU
|
||||
COMMAND_START = 0x70
|
||||
SWITCH_BANK = 0x88
|
||||
FINISH = 0x8f
|
||||
DICT_BASE_ENC, DICT_BASE_DEC = 0x88, 0x90
|
||||
ESCAPE_CHARACTER = None
|
||||
encoder = 'new'
|
||||
|
||||
class LangDE(LangEU):
|
||||
alphabet = kTextAlphabet_DE
|
||||
dictionary = kTextDictionary_DE
|
||||
rom_addrs = [0x9c8000, 0x8CEB00]
|
||||
|
||||
class LangFR(LangEU):
|
||||
alphabet = kTextAlphabet_FR
|
||||
dictionary = kTextDictionary_FR
|
||||
rom_addrs = [0x9c8000, 0x8CE800]
|
||||
|
||||
class LangFR_C(LangEU):
|
||||
alphabet = kTextAlphabet_FR
|
||||
dictionary = kTextDictionary_FR
|
||||
rom_addrs = [0x9c8000, 0x8CF150]
|
||||
|
||||
kLanguages = {
|
||||
'us' : LangUS(),
|
||||
'de' : LangDE(),
|
||||
'fr' : LangFR(),
|
||||
'fr-c' : LangFR_C(),
|
||||
'en' : LangEN(),
|
||||
'es' : LangES(),
|
||||
'pl' : LangPL(),
|
||||
'pt' : LangPT(),
|
||||
'redux' : LangUS(),
|
||||
'nl' : LangNL(),
|
||||
'sv' : LangSV(),
|
||||
}
|
||||
|
||||
def dialogue_filename(s):
|
||||
if s == 'us': return 'dialogue.txt'
|
||||
return f"dialogue_{s.replace('-', '_')}.txt"
|
||||
|
||||
def uses_new_format(lang):
|
||||
return kLanguages[lang].encoder == 'new'
|
||||
|
||||
dict_expansion = []
|
||||
|
||||
def decode_strings_generic(get_byte, lang):
|
||||
info = kLanguages[lang]
|
||||
p, rom_idx = info.rom_addrs[0], 1
|
||||
result = []
|
||||
while True:
|
||||
s, srcdata = '', []
|
||||
while True:
|
||||
c = get_byte(p)
|
||||
srcdata.append(c)
|
||||
l = info.command_lengths[c - info.COMMAND_START] if c >= info.COMMAND_START and c < info.SWITCH_BANK else 1
|
||||
|
||||
p += l
|
||||
if c == 0x7f: # EndMessage
|
||||
break
|
||||
if c < info.COMMAND_START:
|
||||
if c == info.ESCAPE_CHARACTER:
|
||||
c = get_byte(p); p += 1
|
||||
srcdata.append(c)
|
||||
s += info.alphabet[c]
|
||||
elif c < info.SWITCH_BANK:
|
||||
if l == 2:
|
||||
srcdata.append(get_byte(p - 1))
|
||||
s += '[%s %.2d]' % (info.command_names[c - info.COMMAND_START], get_byte(p - 1))
|
||||
else:
|
||||
s += '[%s]' % info.command_names[c - info.COMMAND_START]
|
||||
elif c == info.FINISH:
|
||||
return result # done
|
||||
elif c == info.SWITCH_BANK:
|
||||
p = info.rom_addrs[rom_idx]; rom_idx += 1
|
||||
s, srcdata = '', []
|
||||
elif c < info.SWITCH_BANK + 8:
|
||||
if lang != 'pt':
|
||||
assert 0
|
||||
else:
|
||||
s += info.dictionary[c - info.DICT_BASE_DEC]
|
||||
dict_expansion.append(len(info.dictionary[c - info.DICT_BASE_DEC]))
|
||||
result.append((s, srcdata))
|
||||
if len(result) >= 397 and lang == 'pt':
|
||||
return result
|
||||
# print(len(result), s)
|
||||
# if len(result) == 89:
|
||||
# print(s)
|
||||
# sys.exit(0)
|
||||
|
||||
|
||||
|
||||
def print_strings(rom, file = None):
|
||||
texts = decode_strings_generic(rom.get_byte, rom.language)
|
||||
if len(texts) == 396:
|
||||
extra_str = "[Speed 00]0- [Number 00]. 1- [Number 01][2]2- [Number 02]. 3- [Number 03]"
|
||||
texts = texts[:4] + [(extra_str, None)] + texts[4:]
|
||||
|
||||
for i, s in enumerate(texts):
|
||||
print('%s: %s' % (i + 1, s[0]), file = file)
|
||||
|
||||
|
||||
def encode_greedy_from_dict(s, i, rev, a2i, info):
|
||||
a = s[i:]
|
||||
if r := rev.get(a[0]):
|
||||
for k, v in r.items():
|
||||
if a.startswith(k):
|
||||
return [v + info.DICT_BASE_ENC], len(k)
|
||||
|
||||
if a[0] == '[':
|
||||
cmd, param = a[1:a.index(']')], None
|
||||
cmdlen = len(cmd)
|
||||
if r := a2i.get(a[:cmdlen+2]):
|
||||
return [r], cmdlen+2
|
||||
if ' ' in cmd:
|
||||
cmd, param = cmd.split(' ', 1)
|
||||
param = int(param)
|
||||
return kEncoders[info.encoder](cmd, param), cmdlen + 2
|
||||
else:
|
||||
return [a2i[a[0]]], 1
|
||||
|
||||
print('substr %s not found' % a)
|
||||
assert 0
|
||||
|
||||
def compress_strings(xs, lang = 'us'):
|
||||
info = kLanguages[lang]
|
||||
rev = {}
|
||||
for a,b in enumerate(info.dictionary):
|
||||
rev.setdefault(b[0], {})[b] = a
|
||||
#rev = {b:a for a,b in enumerate(info.dictionary)}
|
||||
a2i = {e:i for i,e in enumerate(info.alphabet)}
|
||||
def compress_string(s):
|
||||
i = 0
|
||||
r = bytearray()
|
||||
while i < len(s):
|
||||
what, num = encode_greedy_from_dict(s, i, rev, a2i, info)
|
||||
r.extend(what)
|
||||
i += num
|
||||
return r
|
||||
return [compress_string(x) for x in xs]
|
||||
|
||||
def verify(get_byte):
|
||||
for i, (decoded, original) in enumerate(decode_strings_generic(get_byte, 'us')):
|
||||
c = compress_strings([decoded])[0]
|
||||
if c != original:
|
||||
print('String %s not match: %s, %s' % (decoded, c, original))
|
||||
break
|
||||
else:
|
||||
pass
|
||||
|
||||
def encode_dictionary(lang = 'us'):
|
||||
info = kLanguages[lang]
|
||||
rev = {b:a for a,b in enumerate(info.alphabet)}
|
||||
return [bytearray(rev[c] for c in line) for line in info.dictionary]
|
||||
|
||||
if __name__ == "__main__":
|
||||
ROM = util.load_rom(sys.argv[1] if len(sys.argv) >= 2 else None, True)
|
||||
|
||||
decoded = decode_strings_generic(ROM.get_byte, 'de')
|
||||
print('Total bytes: %d' % sum(len(a[1]) for a in decoded))
|
||||
|
||||
print('Dict tokens: %d' % len(dict_expansion))
|
||||
print('Dict save: %d' % (sum(dict_expansion) - len(dict_expansion)))
|
||||
|
||||
print('US size ', len(kTextDictionary_US))
|
||||
print('DE size ', len(kTextDictionary_DE))
|
||||
|
||||
texts = [a[0] for a in decoded]
|
||||
|
||||
|
||||
# Pal seems to have one string too little
|
||||
if len(texts) == 396:
|
||||
extra_str = "[Speed 00]0- [Number 00]. 1- [Number 01][2]2- [Number 02]. 3- [Number 03]"
|
||||
texts = texts[:4] + [extra_str] + texts[4:]
|
||||
|
||||
#for i, s in enumerate(texts):
|
||||
# print('%s: %s' % (i + 1, s), file = None)
|
||||
|
||||
|
||||
#encode_dictionary()
|
||||
compr = compress_strings(texts, 'de')
|
||||
print(f'Compressed size (excl eof): {sum(len(a) for a in compr)}')
|
||||
|
||||
|
||||
@@ -360,10 +360,10 @@ void Dungeon_SaveAndLoadLoadAllPalettes(uint8 a, uint8 k) { // 82c546
|
||||
overworld_palette_aux_or_main = 0x200;
|
||||
flag_update_cgram_in_nmi++;
|
||||
Palette_BgAndFixedColor_Black();
|
||||
Palette_Load_Sp0L();
|
||||
Palette_Load_SpritePal0Left();
|
||||
Palette_Load_SpriteMain();
|
||||
Palette_Load_Sp5L();
|
||||
Palette_Load_Sp6L();
|
||||
Palette_Load_SpriteAux1();
|
||||
Palette_Load_SpriteAux2();
|
||||
Palette_Load_SpriteEnvironment_Dungeon();
|
||||
Palette_Load_HUD();
|
||||
Palette_Load_DungeonSet();
|
||||
@@ -416,7 +416,11 @@ void Attract_InitGraphics() { // 8cee0c
|
||||
Palette_Load_OWBGMain();
|
||||
Palette_Load_HUD();
|
||||
Palette_Load_LinkArmorAndGloves();
|
||||
// Set text color blue
|
||||
main_palette_buffer[0x1d] = 0x3800;
|
||||
if (kPpuUpsample2x2)
|
||||
g_zenv.cgram[0x171] = 0x3800;
|
||||
|
||||
flag_update_cgram_in_nmi++;
|
||||
BYTE(BG3VOFS_copy2) = 20;
|
||||
Attract_BuildBackgrounds();
|
||||
@@ -524,10 +528,10 @@ void AttractScene_ThroneRoom() { // 8cef4e
|
||||
Dungeon_LoadAndDrawEntranceRoom(0x74);
|
||||
WORD(attract_state) = bak1;
|
||||
attract_var12 = bak0;
|
||||
palette_main_indoors = 0;
|
||||
palette_sp0l = 0;
|
||||
palette_sp5l = 14;
|
||||
palette_sp6l = 3;
|
||||
dung_hdr_palette_1 = 0;
|
||||
overworld_palette_sp0 = 0;
|
||||
sprite_aux1_palette = 14;
|
||||
sprite_aux2_palette = 3;
|
||||
Dungeon_SaveAndLoadLoadAllPalettes(0, 0x7e);
|
||||
|
||||
main_palette_buffer[0x1d] = 0x3800;
|
||||
@@ -560,10 +564,10 @@ void Attract_PrepZeldaPrison() { // 8cefe3
|
||||
WORD(attract_state) = bak1;
|
||||
attract_var12 = bak0;
|
||||
|
||||
palette_main_indoors = 2;
|
||||
palette_sp0l = 0;
|
||||
palette_sp5l = 14;
|
||||
palette_sp6l = 3;
|
||||
dung_hdr_palette_1 = 2;
|
||||
overworld_palette_sp0 = 0;
|
||||
sprite_aux1_palette = 14;
|
||||
sprite_aux2_palette = 3;
|
||||
Dungeon_SaveAndLoadLoadAllPalettes(1, 0x7f);
|
||||
main_palette_buffer[0x1d] = 0x3800;
|
||||
|
||||
@@ -589,16 +593,16 @@ void Attract_PrepMaidenWarp() { // 8cf058
|
||||
WORD(attract_state) = bak1;
|
||||
attract_var12 = bak0;
|
||||
|
||||
palette_main_indoors = 0;
|
||||
palette_sp0l = 0;
|
||||
palette_sp5l = 14;
|
||||
palette_sp6l = 3;
|
||||
dung_hdr_palette_1 = 0;
|
||||
overworld_palette_sp0 = 0;
|
||||
sprite_aux1_palette = 14;
|
||||
sprite_aux2_palette = 3;
|
||||
|
||||
overworld_palette_aux_or_main = 0;
|
||||
Palette_Load_Sp0L();
|
||||
Palette_Load_SpritePal0Left();
|
||||
Palette_Load_SpriteMain();
|
||||
Palette_Load_Sp5L();
|
||||
Palette_Load_Sp6L();
|
||||
Palette_Load_SpriteAux1();
|
||||
Palette_Load_SpriteAux2();
|
||||
Palette_Load_SpriteEnvironment_Dungeon();
|
||||
Palette_Load_HUD();
|
||||
Palette_Load_DungeonSet();
|
||||
@@ -4,7 +4,7 @@
|
||||
#include <string.h>
|
||||
#include <SDL.h>
|
||||
#include "features.h"
|
||||
#include "util.h"
|
||||
|
||||
|
||||
enum {
|
||||
kKeyMod_ScanCode = 0x200,
|
||||
@@ -22,7 +22,6 @@ Config g_config;
|
||||
#define C(x) REMAP_SDL_KEYCODE(x) | kKeyMod_Ctrl
|
||||
#define N 0
|
||||
static const uint16 kDefaultKbdControls[kKeys_Total] = {
|
||||
0,
|
||||
// Controls
|
||||
_(SDLK_UP), _(SDLK_DOWN), _(SDLK_LEFT), _(SDLK_RIGHT), _(SDLK_RSHIFT), _(SDLK_RETURN), _(SDLK_x), _(SDLK_z), _(SDLK_s), _(SDLK_a), _(SDLK_c), _(SDLK_v),
|
||||
// LoadState
|
||||
@@ -32,9 +31,9 @@ static const uint16 kDefaultKbdControls[kKeys_Total] = {
|
||||
// Replay State
|
||||
C(SDLK_F1), C(SDLK_F2), C(SDLK_F3), C(SDLK_F4), C(SDLK_F5), C(SDLK_F6), C(SDLK_F7), C(SDLK_F8), C(SDLK_F9), C(SDLK_F10), N, N, N, N, N, N, N, N, N, N,
|
||||
// Load Ref State
|
||||
N, N, N, N, N, N, N, N, N, N, N, N, N, N, N, N, N, N, N, N,
|
||||
_(SDLK_1), _(SDLK_2), _(SDLK_3), _(SDLK_4), _(SDLK_5), _(SDLK_6), _(SDLK_7), _(SDLK_8), _(SDLK_9), _(SDLK_0), _(SDLK_MINUS), _(SDLK_EQUALS), _(SDLK_BACKSPACE), N, N, N, N, N, N, N,
|
||||
// Replay Ref State
|
||||
N, N, N, N, N, N, N, N, N, N, N, N, N, N, N, N, N, N, N, N,
|
||||
C(SDLK_1), C(SDLK_2), C(SDLK_3), C(SDLK_4), C(SDLK_5), C(SDLK_6), C(SDLK_7), C(SDLK_8), C(SDLK_9), C(SDLK_0), C(SDLK_MINUS), C(SDLK_EQUALS), C(SDLK_BACKSPACE), N, N, N, N, N, N, N,
|
||||
// CheatLife, CheatKeys, CheatEquipment, CheatWalkThroughWalls
|
||||
_(SDLK_w), _(SDLK_o), S(SDLK_w), C(SDLK_e),
|
||||
// ClearKeyLog, StopReplay, Fullscreen, Reset, Pause, PauseDimmed, Turbo, ReplayTurbo, WindowBigger, WindowSmaller, DisplayPerf, ToggleRenderer
|
||||
@@ -54,7 +53,6 @@ typedef struct KeyNameId {
|
||||
#define M(n) {#n, kKeys_##n, kKeys_##n##_Last - kKeys_##n + 1}
|
||||
#define S(n) {#n, kKeys_##n, 1}
|
||||
static const KeyNameId kKeyNameId[] = {
|
||||
{"Null", kKeys_Null, 65535},
|
||||
M(Controls), M(Load), M(Save), M(Replay), M(LoadRef), M(ReplayRef),
|
||||
S(CheatLife), S(CheatKeys), S(CheatEquipment), S(CheatWalkThroughWalls),
|
||||
S(ClearKeyLog), S(StopReplay), S(Fullscreen), S(Reset),
|
||||
@@ -71,7 +69,7 @@ static KeyMapHashEnt *keymap_hash;
|
||||
static int keymap_hash_size;
|
||||
static bool has_keynameid[countof(kKeyNameId)];
|
||||
|
||||
static bool KeyMapHash_Add(uint16 key, uint16 cmd) {
|
||||
bool KeyMapHash_Add(uint16 key, uint16 cmd) {
|
||||
if ((keymap_hash_size & 0xff) == 0) {
|
||||
if (keymap_hash_size > 10000)
|
||||
Die("Too many keys");
|
||||
@@ -103,27 +101,58 @@ static int KeyMapHash_Find(uint16 key) {
|
||||
return ent->cmd;
|
||||
i = ent->next;
|
||||
}
|
||||
return 0;
|
||||
return -1;
|
||||
}
|
||||
|
||||
int FindCmdForSdlKey(SDL_Keycode code, SDL_Keymod mod) {
|
||||
if (code & ~(SDLK_SCANCODE_MASK | 0x1ff))
|
||||
return 0;
|
||||
int key = 0;
|
||||
if (code != SDLK_LALT && code != SDLK_RALT)
|
||||
key |= mod & KMOD_ALT ? kKeyMod_Alt : 0;
|
||||
if (code != SDLK_LCTRL && code != SDLK_RCTRL)
|
||||
key |= mod & KMOD_CTRL ? kKeyMod_Ctrl : 0;
|
||||
if (code != SDLK_LSHIFT && code != SDLK_RSHIFT)
|
||||
key |= mod & KMOD_SHIFT ? kKeyMod_Shift : 0;
|
||||
return -1;
|
||||
int key = mod & KMOD_ALT ? kKeyMod_Alt : 0;
|
||||
key |= mod & KMOD_CTRL ? kKeyMod_Ctrl : 0;
|
||||
key |= mod & KMOD_SHIFT ? kKeyMod_Shift : 0;
|
||||
key |= REMAP_SDL_KEYCODE(code);
|
||||
return KeyMapHash_Find(key);
|
||||
}
|
||||
|
||||
static char *NextDelim(char **s, int sep) {
|
||||
char *r = *s;
|
||||
if (r) {
|
||||
while (r[0] == ' ' || r[0] == '\t')
|
||||
r++;
|
||||
char *t = strchr(r, sep);
|
||||
*s = t ? (*t++ = 0, t) : NULL;
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
static inline int ToLower(int a) {
|
||||
return a + (a >= 'A' && a <= 'Z') * 32;
|
||||
}
|
||||
|
||||
static bool StringEqualsNoCase(const char *a, const char *b) {
|
||||
for (;;) {
|
||||
int aa = ToLower(*a++), bb = ToLower(*b++);
|
||||
if (aa != bb)
|
||||
return false;
|
||||
if (aa == 0)
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
static bool StringStartsWithNoCase(const char *a, const char *b) {
|
||||
for (;;) {
|
||||
int aa = ToLower(*a++), bb = ToLower(*b++);
|
||||
if (bb == 0)
|
||||
return true;
|
||||
if (aa != bb)
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
static void ParseKeyArray(char *value, int cmd, int size) {
|
||||
char *s;
|
||||
int i = 0;
|
||||
for (; i < size && (s = NextDelim(&value, ',')) != NULL; i++, cmd += (cmd != 0)) {
|
||||
for (; i < size && (s = NextDelim(&value, ',')) != NULL; i++, cmd++) {
|
||||
if (*s == 0)
|
||||
continue;
|
||||
int key_with_mod = 0;
|
||||
@@ -148,122 +177,18 @@ static void ParseKeyArray(char *value, int cmd, int size) {
|
||||
}
|
||||
}
|
||||
|
||||
typedef struct GamepadMapEnt {
|
||||
uint32 modifiers;
|
||||
uint16 cmd, next;
|
||||
} GamepadMapEnt;
|
||||
|
||||
static uint16 joymap_first[kGamepadBtn_Count];
|
||||
static GamepadMapEnt *joymap_ents;
|
||||
static int joymap_size;
|
||||
static bool has_joypad_controls;
|
||||
|
||||
static int CountBits32(uint32 n) {
|
||||
int count = 0;
|
||||
for (; n != 0; count++)
|
||||
n &= (n - 1);
|
||||
return count;
|
||||
}
|
||||
|
||||
static void GamepadMap_Add(int button, uint32 modifiers, uint16 cmd) {
|
||||
if ((joymap_size & 0xff) == 0) {
|
||||
if (joymap_size > 1000)
|
||||
Die("Too many joypad keys");
|
||||
joymap_ents = realloc(joymap_ents, sizeof(GamepadMapEnt) * (joymap_size + 64));
|
||||
if (!joymap_ents) Die("realloc failure");
|
||||
}
|
||||
uint16 *p = &joymap_first[button];
|
||||
// Insert it as early as possible but before after any entry with more modifiers.
|
||||
int cb = CountBits32(modifiers);
|
||||
while (*p && cb < CountBits32(joymap_ents[*p - 1].modifiers))
|
||||
p = &joymap_ents[*p - 1].next;
|
||||
int i = joymap_size++;
|
||||
GamepadMapEnt *ent = &joymap_ents[i];
|
||||
ent->modifiers = modifiers;
|
||||
ent->cmd = cmd;
|
||||
ent->next = *p;
|
||||
*p = i + 1;
|
||||
}
|
||||
|
||||
int FindCmdForGamepadButton(int button, uint32 modifiers) {
|
||||
GamepadMapEnt *ent;
|
||||
for(int e = joymap_first[button]; e != 0; e = ent->next) {
|
||||
ent = &joymap_ents[e - 1];
|
||||
if ((modifiers & ent->modifiers) == ent->modifiers)
|
||||
return ent->cmd;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ParseGamepadButtonName(const char **value) {
|
||||
const char *s = *value;
|
||||
// Longest substring first
|
||||
static const char *const kGamepadKeyNames[] = {
|
||||
"Back", "Guide", "Start", "L3", "R3",
|
||||
"L1", "R1", "DpadUp", "DpadDown", "DpadLeft", "DpadRight", "L2", "R2",
|
||||
"Lb", "Rb", "A", "B", "X", "Y"
|
||||
};
|
||||
static const uint8 kGamepadKeyIds[] = {
|
||||
kGamepadBtn_Back, kGamepadBtn_Guide, kGamepadBtn_Start, kGamepadBtn_L3, kGamepadBtn_R3,
|
||||
kGamepadBtn_L1, kGamepadBtn_R1, kGamepadBtn_DpadUp, kGamepadBtn_DpadDown, kGamepadBtn_DpadLeft, kGamepadBtn_DpadRight, kGamepadBtn_L2, kGamepadBtn_R2,
|
||||
kGamepadBtn_L1, kGamepadBtn_R1, kGamepadBtn_A, kGamepadBtn_B, kGamepadBtn_X, kGamepadBtn_Y,
|
||||
};
|
||||
for (size_t i = 0; i != countof(kGamepadKeyNames); i++) {
|
||||
const char *r = StringStartsWithNoCase(s, kGamepadKeyNames[i]);
|
||||
if (r) {
|
||||
*value = r;
|
||||
return kGamepadKeyIds[i];
|
||||
}
|
||||
}
|
||||
return kGamepadBtn_Invalid;
|
||||
}
|
||||
|
||||
static const uint8 kDefaultGamepadCmds[] = {
|
||||
kGamepadBtn_DpadUp, kGamepadBtn_DpadDown, kGamepadBtn_DpadLeft, kGamepadBtn_DpadRight, kGamepadBtn_Back, kGamepadBtn_Start,
|
||||
kGamepadBtn_B, kGamepadBtn_A, kGamepadBtn_Y, kGamepadBtn_X, kGamepadBtn_L1, kGamepadBtn_R1,
|
||||
};
|
||||
|
||||
static void ParseGamepadArray(char *value, int cmd, int size) {
|
||||
char *s;
|
||||
int i = 0;
|
||||
for (; i < size && (s = NextDelim(&value, ',')) != NULL; i++, cmd += (cmd != 0)) {
|
||||
if (*s == 0)
|
||||
continue;
|
||||
uint32 modifiers = 0;
|
||||
const char *ss = s;
|
||||
for (;;) {
|
||||
int button = ParseGamepadButtonName(&ss);
|
||||
if (button == kGamepadBtn_Invalid) BAD: {
|
||||
fprintf(stderr, "Unknown gamepad button: '%s'\n", s);
|
||||
break;
|
||||
}
|
||||
while (*ss == ' ' || *ss == '\t') ss++;
|
||||
if (*ss == '+') {
|
||||
ss++;
|
||||
modifiers |= 1 << button;
|
||||
} else if (*ss == 0) {
|
||||
GamepadMap_Add(button, modifiers, cmd);
|
||||
break;
|
||||
} else
|
||||
goto BAD;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void RegisterDefaultKeys() {
|
||||
for (int i = 1; i < countof(kKeyNameId); i++) {
|
||||
for (int i = 0; i < countof(kKeyNameId); i++) {
|
||||
if (!has_keynameid[i]) {
|
||||
int size = kKeyNameId[i].size, k = kKeyNameId[i].id;
|
||||
for (int j = 0; j < size; j++, k++)
|
||||
KeyMapHash_Add(kDefaultKbdControls[k], k);
|
||||
}
|
||||
}
|
||||
if (!has_joypad_controls) {
|
||||
for (int i = 0; i < countof(kDefaultGamepadCmds); i++)
|
||||
GamepadMap_Add(kDefaultGamepadCmds[i], 0, kKeys_Controls + i);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static int GetIniSection(const char *s) {
|
||||
if (StringEqualsNoCase(s, "[KeyMap]"))
|
||||
return 0;
|
||||
@@ -275,31 +200,18 @@ static int GetIniSection(const char *s) {
|
||||
return 3;
|
||||
if (StringEqualsNoCase(s, "[Features]"))
|
||||
return 4;
|
||||
if (StringEqualsNoCase(s, "[GamepadMap]"))
|
||||
return 5;
|
||||
return -1;
|
||||
}
|
||||
|
||||
bool ParseBool(const char *value, bool *result) {
|
||||
bool rv = false;
|
||||
switch (*value++ | 32) {
|
||||
case '0': if (*value == 0) break; return false;
|
||||
case 'f': if (StringEqualsNoCase(value, "alse")) break; return false;
|
||||
case 'n': if (StringEqualsNoCase(value, "o")) break; return false;
|
||||
case 'o':
|
||||
rv = (*value | 32) == 'n';
|
||||
if (StringEqualsNoCase(value, rv ? "n" : "ff")) break;
|
||||
return false;
|
||||
case '1': rv = true; if (*value == 0) break; return false;
|
||||
case 'y': rv = true; if (StringEqualsNoCase(value, "es")) break; return false;
|
||||
case 't': rv = true; if (StringEqualsNoCase(value, "rue")) break; return false;
|
||||
default: return false;
|
||||
}
|
||||
if (result) {
|
||||
*result = rv;
|
||||
static bool ParseBool(const char *value, bool *result) {
|
||||
if (StringEqualsNoCase(value, "0") || StringEqualsNoCase(value, "false")) {
|
||||
*result = false;
|
||||
return true;
|
||||
} else if (StringEqualsNoCase(value, "1") || StringEqualsNoCase(value, "true")) {
|
||||
*result = true;
|
||||
return true;
|
||||
}
|
||||
return rv;
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool ParseBoolBit(const char *value, uint32 *data, uint32 mask) {
|
||||
@@ -319,15 +231,6 @@ static bool HandleIniConfig(int section, const char *key, char *value) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
} else if (section == 5) {
|
||||
for (int i = 0; i < countof(kKeyNameId); i++) {
|
||||
if (StringEqualsNoCase(key, kKeyNameId[i].name)) {
|
||||
if (i == 1)
|
||||
has_joypad_controls = true;
|
||||
ParseGamepadArray(value, kKeyNameId[i].id, kKeyNameId[i].size);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
} else if (section == 1) {
|
||||
if (StringEqualsNoCase(key, "WindowSize")) {
|
||||
char *s;
|
||||
@@ -356,24 +259,11 @@ static bool HandleIniConfig(int section, const char *key, char *value) {
|
||||
} else if (StringEqualsNoCase(key, "WindowScale")) {
|
||||
g_config.window_scale = (uint8)strtol(value, (char**)NULL, 10);
|
||||
return true;
|
||||
} else if (StringEqualsNoCase(key, "OutputMethod")) {
|
||||
g_config.output_method = StringEqualsNoCase(value, "SDL-Software") ? kOutputMethod_SDLSoftware :
|
||||
StringEqualsNoCase(value, "OpenGL") ? kOutputMethod_OpenGL :
|
||||
StringEqualsNoCase(value, "OpenGL ES") ? kOutputMethod_OpenGL_ES :
|
||||
kOutputMethod_SDL;
|
||||
return true;
|
||||
} else if (StringEqualsNoCase(key, "LinearFiltering")) {
|
||||
return ParseBool(value, &g_config.linear_filtering);
|
||||
} else if (StringEqualsNoCase(key, "NoSpriteLimits")) {
|
||||
return ParseBool(value, &g_config.no_sprite_limits);
|
||||
} else if (StringEqualsNoCase(key, "LinkGraphics")) {
|
||||
g_config.link_graphics = value;
|
||||
return true;
|
||||
} else if (StringEqualsNoCase(key, "Shader")) {
|
||||
g_config.shader = *value ? value : NULL;
|
||||
return true;
|
||||
} else if (StringEqualsNoCase(key, "DimFlashes")) {
|
||||
return ParseBoolBit(value, &g_config.features0, kFeatures0_DimFlashes);
|
||||
}
|
||||
} else if (section == 2) {
|
||||
if (StringEqualsNoCase(key, "EnableAudio")) {
|
||||
@@ -388,23 +278,7 @@ static bool HandleIniConfig(int section, const char *key, char *value) {
|
||||
g_config.audio_samples = (uint16)strtol(value, (char**)NULL, 10);
|
||||
return true;
|
||||
} else if (StringEqualsNoCase(key, "EnableMSU")) {
|
||||
if (StringEqualsNoCase(value, "opuz"))
|
||||
g_config.enable_msu = kMsuEnabled_Opuz;
|
||||
else if (StringEqualsNoCase(value, "deluxe"))
|
||||
g_config.enable_msu = kMsuEnabled_MsuDeluxe;
|
||||
else if (StringEqualsNoCase(value, "deluxe-opuz"))
|
||||
g_config.enable_msu = kMsuEnabled_MsuDeluxe | kMsuEnabled_Opuz;
|
||||
else
|
||||
return ParseBool(value, (bool*)&g_config.enable_msu);
|
||||
return true;
|
||||
} else if (StringEqualsNoCase(key, "MSUPath")) {
|
||||
g_config.msu_path = value;
|
||||
return true;
|
||||
} else if (StringEqualsNoCase(key, "MSUVolume")) {
|
||||
g_config.msuvolume = atoi(value);
|
||||
return true;
|
||||
} else if (StringEqualsNoCase(key, "ResumeMSU")) {
|
||||
return ParseBool(value, &g_config.resume_msu);
|
||||
return ParseBool(value, &g_config.enable_msu);
|
||||
}
|
||||
} else if (section == 3) {
|
||||
if (StringEqualsNoCase(key, "Autosave")) {
|
||||
@@ -442,15 +316,10 @@ static bool HandleIniConfig(int section, const char *key, char *value) {
|
||||
return ParseBool(value, &g_config.display_perf_title);
|
||||
} else if (StringEqualsNoCase(key, "DisableFrameDelay")) {
|
||||
return ParseBool(value, &g_config.disable_frame_delay);
|
||||
} else if (StringEqualsNoCase(key, "Language")) {
|
||||
g_config.language = value;
|
||||
return true;
|
||||
}
|
||||
} else if (section == 4) {
|
||||
if (StringEqualsNoCase(key, "ItemSwitchLR")) {
|
||||
return ParseBoolBit(value, &g_config.features0, kFeatures0_SwitchLR);
|
||||
} else if (StringEqualsNoCase(key, "ItemSwitchLRLimit")) {
|
||||
return ParseBoolBit(value, &g_config.features0, kFeatures0_SwitchLRLimit);
|
||||
} else if (StringEqualsNoCase(key, "TurnWhileDashing")) {
|
||||
return ParseBoolBit(value, &g_config.features0, kFeatures0_TurnWhileDashing);
|
||||
} else if (StringEqualsNoCase(key, "MirrorToDarkworld")) {
|
||||
@@ -480,50 +349,79 @@ static bool HandleIniConfig(int section, const char *key, char *value) {
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool ParseOneConfigFile(const char *filename, int depth) {
|
||||
char *filedata = (char*)ReadWholeFile(filename, NULL), *p;
|
||||
if (!filedata)
|
||||
return false;
|
||||
|
||||
int section = -2;
|
||||
g_config.memory_buffer = filedata;
|
||||
|
||||
for (int lineno = 1; (p = NextLineStripComments(&filedata)) != NULL; lineno++) {
|
||||
if (*p == 0)
|
||||
uint8 *ReadFile(const char *name, size_t *length) {
|
||||
FILE *f = fopen(name, "rb");
|
||||
if (f == NULL)
|
||||
return NULL;
|
||||
fseek(f, 0, SEEK_END);
|
||||
size_t size = ftell(f);
|
||||
rewind(f);
|
||||
uint8 *buffer = (uint8 *)malloc(size + 1);
|
||||
if (!buffer) Die("malloc failed");
|
||||
// Always zero terminate so this function can be used also for strings.
|
||||
buffer[size] = 0;
|
||||
if (fread(buffer, 1, size, f) != size)
|
||||
Die("fread failed");
|
||||
fclose(f);
|
||||
if (length) *length = size;
|
||||
return buffer;
|
||||
}
|
||||
|
||||
void ParseConfigFile() {
|
||||
uint8 *file = ReadFile("zelda3.user.ini", NULL);
|
||||
if (!file) {
|
||||
file = ReadFile("zelda3.ini", NULL);
|
||||
if (!file)
|
||||
return;
|
||||
}
|
||||
fprintf(stderr, "Loading zelda3.ini\n");
|
||||
int section = -2;
|
||||
int lineno = 1;
|
||||
char *p, *next_p = (char*)file;
|
||||
for (; (p = next_p) != NULL; lineno++) {
|
||||
// find end of line
|
||||
char *eol = strchr(p, '\n');
|
||||
next_p = eol ? eol + 1 : NULL;
|
||||
eol = eol ? eol : p + strlen(p);
|
||||
// strip comments
|
||||
char *comment = memchr(p, '#', eol - p);
|
||||
eol = (comment != 0) ? comment : eol;
|
||||
// strip trailing whitespace
|
||||
while (eol > p && (eol[-1] == '\r' || eol[-1] == ' ' || eol[-1] == '\t'))
|
||||
eol--;
|
||||
*eol = 0;
|
||||
if (p == eol)
|
||||
continue; // empty line
|
||||
// strip leading whitespace
|
||||
while (p[0] == ' ' || p[0] == '\t')
|
||||
p++;
|
||||
if (*p == '[') {
|
||||
section = GetIniSection(p);
|
||||
if (section < 0)
|
||||
fprintf(stderr, "%s:%d: Invalid .ini section %s\n", filename, lineno, p);
|
||||
} else if (*p == '!' && SkipPrefix(p + 1, "include ")) {
|
||||
char *tt = p + 8;
|
||||
char *new_filename = ReplaceFilenameWithNewPath(filename, NextPossiblyQuotedString(&tt));
|
||||
if (depth > 10 || !ParseOneConfigFile(new_filename, depth + 1))
|
||||
fprintf(stderr, "Warning: Unable to read %s\n", new_filename);
|
||||
free(new_filename);
|
||||
fprintf(stderr, "zelda3.ini:%d: Invalid .ini section %s\n", lineno, p);
|
||||
} else if (section == -2) {
|
||||
fprintf(stderr, "%s:%d: Expecting [section]\n", filename, lineno);
|
||||
fprintf(stderr, "zelda3.ini:%d: Expecting [section]\n", lineno);
|
||||
} else {
|
||||
char *v = SplitKeyValue(p);
|
||||
if (v == NULL) {
|
||||
fprintf(stderr, "%s:%d: Expecting 'key=value'\n", filename, lineno);
|
||||
continue;
|
||||
char *equals = memchr(p, '=', eol - p);
|
||||
if (equals == NULL) {
|
||||
fprintf(stderr, "zelda3.ini:%d: Expecting 'key=value'\n", lineno);
|
||||
} else {
|
||||
char *kr = equals;
|
||||
while (kr > p && (kr[-1] == ' ' || kr[-1] == '\t'))
|
||||
kr--;
|
||||
*kr = 0;
|
||||
char *v = equals + 1;
|
||||
while (v[0] == ' ' || v[0] == '\t')
|
||||
v++;
|
||||
if (section >= 0 && !HandleIniConfig(section, p, v))
|
||||
fprintf(stderr, "zelda3.ini:%d: Can't parse '%s'\n", lineno, p);
|
||||
}
|
||||
if (section >= 0 && !HandleIniConfig(section, p, v))
|
||||
fprintf(stderr, "%s:%d: Can't parse '%s'\n", filename, lineno, p);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
g_config.memory_buffer = file;
|
||||
}
|
||||
|
||||
void ParseConfigFile(const char *filename) {
|
||||
g_config.msuvolume = 100; // default msu volume, 100%
|
||||
|
||||
if (filename != NULL || !ParseOneConfigFile("zelda3.user.ini", 0)) {
|
||||
if (filename == NULL)
|
||||
filename = "zelda3.ini";
|
||||
if (!ParseOneConfigFile(filename, 0))
|
||||
fprintf(stderr, "Warning: Unable to read config file %s\n", filename);
|
||||
}
|
||||
void AfterConfigParse() {
|
||||
RegisterDefaultKeys();
|
||||
}
|
||||
@@ -3,8 +3,7 @@
|
||||
#include <SDL_keycode.h>
|
||||
|
||||
enum {
|
||||
kKeys_Null,
|
||||
kKeys_Controls,
|
||||
kKeys_Controls = 0,
|
||||
kKeys_Controls_Last = kKeys_Controls + 11,
|
||||
kKeys_Load,
|
||||
kKeys_Load_Last = kKeys_Load + 19,
|
||||
@@ -37,13 +36,6 @@ enum {
|
||||
kKeys_Total,
|
||||
};
|
||||
|
||||
enum {
|
||||
kOutputMethod_SDL,
|
||||
kOutputMethod_SDLSoftware,
|
||||
kOutputMethod_OpenGL,
|
||||
kOutputMethod_OpenGL_ES,
|
||||
};
|
||||
|
||||
typedef struct Config {
|
||||
int window_width;
|
||||
int window_height;
|
||||
@@ -53,8 +45,6 @@ typedef struct Config {
|
||||
uint8 fullscreen;
|
||||
uint8 window_scale;
|
||||
bool enable_audio;
|
||||
bool linear_filtering;
|
||||
uint8 output_method;
|
||||
uint16 audio_freq;
|
||||
uint8 audio_channels;
|
||||
uint16 audio_samples;
|
||||
@@ -63,48 +53,18 @@ typedef struct Config {
|
||||
bool extend_y;
|
||||
bool no_sprite_limits;
|
||||
bool display_perf_title;
|
||||
uint8 enable_msu;
|
||||
bool resume_msu;
|
||||
bool enable_msu;
|
||||
bool disable_frame_delay;
|
||||
uint8 msuvolume;
|
||||
uint32 features0;
|
||||
|
||||
const char *link_graphics;
|
||||
char *memory_buffer;
|
||||
const char *shader;
|
||||
const char *msu_path;
|
||||
const char *language;
|
||||
uint8 *memory_buffer;
|
||||
} Config;
|
||||
|
||||
enum {
|
||||
kMsuEnabled_Msu = 1,
|
||||
kMsuEnabled_MsuDeluxe = 2,
|
||||
kMsuEnabled_Opuz = 4,
|
||||
};
|
||||
enum {
|
||||
kGamepadBtn_Invalid = -1,
|
||||
kGamepadBtn_A,
|
||||
kGamepadBtn_B,
|
||||
kGamepadBtn_X,
|
||||
kGamepadBtn_Y,
|
||||
kGamepadBtn_Back,
|
||||
kGamepadBtn_Guide,
|
||||
kGamepadBtn_Start,
|
||||
kGamepadBtn_L3,
|
||||
kGamepadBtn_R3,
|
||||
kGamepadBtn_L1,
|
||||
kGamepadBtn_R1,
|
||||
kGamepadBtn_DpadUp,
|
||||
kGamepadBtn_DpadDown,
|
||||
kGamepadBtn_DpadLeft,
|
||||
kGamepadBtn_DpadRight,
|
||||
kGamepadBtn_L2,
|
||||
kGamepadBtn_R2,
|
||||
kGamepadBtn_Count,
|
||||
};
|
||||
|
||||
extern Config g_config;
|
||||
|
||||
void ParseConfigFile(const char *filename);
|
||||
int FindCmdForSdlKey(SDL_Keycode code, SDL_Keymod mod);
|
||||
int FindCmdForGamepadButton(int button, uint32 modifiers);
|
||||
uint8 *ReadFile(const char *name, size_t *length);
|
||||
void ParseConfigFile();
|
||||
void AfterConfigParse();
|
||||
|
||||
int FindCmdForSdlKey(SDL_Keycode code, SDL_Keymod mod);
|
||||
@@ -2052,14 +2052,13 @@ void RoomBounds_SubA(RoomBounds *r) {
|
||||
}
|
||||
|
||||
void Dungeon_StartInterRoomTrans_Left() {
|
||||
assert(submodule_index == 0);
|
||||
link_quadrant_x ^= 1;
|
||||
Dungeon_AdjustQuadrant();
|
||||
RoomBounds_SubA(&room_bounds_x);
|
||||
Dung_SaveDataForCurrentRoom();
|
||||
DungeonTransition_AdjustCamera_X(link_quadrant_x ^ 1);
|
||||
HandleEdgeTransition_AdjustCameraBoundaries(3);
|
||||
submodule_index = 1;
|
||||
submodule_index++;
|
||||
if (link_quadrant_x) {
|
||||
RoomBounds_SubB(&room_bounds_x);
|
||||
BYTE(dungeon_room_index_prev) = dungeon_room_index;
|
||||
@@ -2073,7 +2072,7 @@ void Dungeon_StartInterRoomTrans_Left() {
|
||||
}
|
||||
dungeon_room_index--;
|
||||
}
|
||||
submodule_index = 2;
|
||||
submodule_index += 1;
|
||||
if (room_transitioning_flags & 1) {
|
||||
link_is_on_lower_level ^= 1;
|
||||
link_is_on_lower_level_mirror = link_is_on_lower_level;
|
||||
@@ -2092,14 +2091,13 @@ void Dung_StartInterRoomTrans_Left_Plus() {
|
||||
}
|
||||
|
||||
void Dungeon_StartInterRoomTrans_Up() {
|
||||
assert(submodule_index == 0);
|
||||
link_quadrant_y ^= 2;
|
||||
Dungeon_AdjustQuadrant();
|
||||
RoomBounds_SubA(&room_bounds_y);
|
||||
Dung_SaveDataForCurrentRoom();
|
||||
DungeonTransition_AdjustCamera_Y(link_quadrant_y ^ 2);
|
||||
HandleEdgeTransition_AdjustCameraBoundaries(1);
|
||||
submodule_index = 1;
|
||||
submodule_index++;
|
||||
if (link_quadrant_y) {
|
||||
RoomBounds_SubB(&room_bounds_y);
|
||||
BYTE(dungeon_room_index_prev) = dungeon_room_index;
|
||||
@@ -2121,7 +2119,7 @@ void Dungeon_StartInterRoomTrans_Up() {
|
||||
Dungeon_AdjustAfterSpiralStairs();
|
||||
}
|
||||
BYTE(dungeon_room_index) -= 0x10;
|
||||
submodule_index = 2;
|
||||
submodule_index += 1;
|
||||
if (room_transitioning_flags & 1) {
|
||||
link_is_on_lower_level ^= 1;
|
||||
link_is_on_lower_level_mirror = link_is_on_lower_level;
|
||||
@@ -2135,14 +2133,13 @@ void Dungeon_StartInterRoomTrans_Up() {
|
||||
}
|
||||
|
||||
void Dungeon_StartInterRoomTrans_Down() {
|
||||
assert(submodule_index == 0);
|
||||
link_quadrant_y ^= 2;
|
||||
Dungeon_AdjustQuadrant();
|
||||
RoomBounds_AddA(&room_bounds_y);
|
||||
Dung_SaveDataForCurrentRoom();
|
||||
DungeonTransition_AdjustCamera_Y(link_quadrant_y);
|
||||
HandleEdgeTransition_AdjustCameraBoundaries(0);
|
||||
submodule_index = 1;
|
||||
submodule_index++;
|
||||
if (!link_quadrant_y) {
|
||||
RoomBounds_AddB(&room_bounds_y);
|
||||
BYTE(dungeon_room_index_prev) = dungeon_room_index;
|
||||
@@ -2156,7 +2153,7 @@ void Dungeon_StartInterRoomTrans_Down() {
|
||||
Dungeon_AdjustAfterSpiralStairs();
|
||||
}
|
||||
BYTE(dungeon_room_index) += 16;
|
||||
submodule_index = 2;
|
||||
submodule_index += 1;
|
||||
if (room_transitioning_flags & 1) {
|
||||
link_is_on_lower_level ^= 1;
|
||||
link_is_on_lower_level_mirror = link_is_on_lower_level;
|
||||
@@ -2381,6 +2378,10 @@ uint16 Dungeon_GetTeleMsg(int room) {
|
||||
return kDungeonRoomTeleMsg[room];
|
||||
}
|
||||
|
||||
uint8 GetEntranceMusicTrack(int entrance) {
|
||||
return kEntranceData_musicTrack[entrance];
|
||||
}
|
||||
|
||||
bool Dungeon_IsPitThatHurtsPlayer() {
|
||||
for (int i = kDungeonPitsHurtPlayer_SIZE / 2 - 1; i >= 0; i--) {
|
||||
if (kDungeonPitsHurtPlayer[i] == dungeon_room_index)
|
||||
@@ -3698,10 +3699,10 @@ void Dungeon_LoadHeader() { // 81b564
|
||||
dung_want_lights_out_copy = dung_want_lights_out;
|
||||
dung_want_lights_out = hdr_ptr[0] & 1;
|
||||
const DungPalInfo *dpi = &kDungPalinfos[hdr_ptr[1]];
|
||||
palette_main_indoors = dpi->pal0;
|
||||
palette_sp0l = dpi->pal1;
|
||||
palette_sp5l = dpi->pal2;
|
||||
palette_sp6l = dpi->pal3;
|
||||
dung_hdr_palette_1 = dpi->pal0;
|
||||
overworld_palette_sp0 = dpi->pal1;
|
||||
sprite_aux1_palette = dpi->pal2;
|
||||
sprite_aux2_palette = dpi->pal3;
|
||||
aux_tile_theme_index = hdr_ptr[2];
|
||||
sprite_graphics_index = hdr_ptr[3] + 0x40;
|
||||
dung_hdr_collision_2 = hdr_ptr[4];
|
||||
@@ -4280,14 +4281,6 @@ void Dungeon_FlipCrystalPegAttribute() { // 81c22a
|
||||
void Dungeon_HandleRoomTags() { // 81c2fd
|
||||
if (!flag_skip_call_tag_routines) {
|
||||
Dungeon_DetectStaircase();
|
||||
|
||||
// Dungeon_DetectStaircase might change the submodule, so avoid
|
||||
// calling the tag routines cause they could also change the submodule,
|
||||
// causing items to spawn in incorrect locations cause link_x/y_coord gets
|
||||
// out of sync if you enter a staircase exactly when a room tag triggers.
|
||||
if (enhanced_features0 & kFeatures0_MiscBugFixes && submodule_index != 0)
|
||||
return;
|
||||
|
||||
g_ram[14] = 0;
|
||||
kDungTagroutines[dung_hdr_tag[0]](0);
|
||||
g_ram[14] = 1;
|
||||
@@ -6428,7 +6421,7 @@ void Module_PreDungeon() { // 82821e
|
||||
Dungeon_LoadAttributeTable();
|
||||
misc_sprites_graphics_index = 10;
|
||||
InitializeTilesets();
|
||||
palette_sp6r_indoors = 10;
|
||||
palette_sp6 = 10;
|
||||
Dungeon_LoadPalettes();
|
||||
if (link_is_bunny_mirror | link_is_bunny)
|
||||
LoadGearPalettes_bunny();
|
||||
@@ -6913,7 +6906,7 @@ table:
|
||||
}
|
||||
|
||||
void Module07_0E_01_HandleMusicAndResetProps() { // 828c78
|
||||
if ((dungeon_room_index == 7 || dungeon_room_index == 23 && !ZeldaIsPlayingMusicTrack(17)) && !(link_which_pendants & 1))
|
||||
if ((dungeon_room_index == 7 || dungeon_room_index == 23 && music_unk1 != 17) && !(link_which_pendants & 1))
|
||||
music_control = 0xf1;
|
||||
staircase_var1 = (which_staircase_index & 4) ? 106 : 88;
|
||||
overworld_map_state = 0;
|
||||
@@ -7347,7 +7340,7 @@ void Dungeon_SetBossMusicUnorthodox() { // 829165
|
||||
x = 0x15;
|
||||
if (dungeon_room_index != 7) {
|
||||
x = 0x11;
|
||||
if (dungeon_room_index != 23 || ZeldaIsPlayingMusicTrack(17))
|
||||
if (dungeon_room_index != 23 || music_unk1 == 17)
|
||||
return;
|
||||
}
|
||||
if (music_unk1 != 0xf1 && (link_which_pendants & 1))
|
||||
@@ -7463,8 +7456,8 @@ void Module07_0F_01_OperateSpotlight() { // 829334
|
||||
TMW_copy = 0;
|
||||
TSW_copy = 0;
|
||||
subsubmodule_index = 0;
|
||||
if (queued_music_control != 0xff)
|
||||
music_control = queued_music_control;
|
||||
if (buffer_for_playing_songs != 0xff)
|
||||
music_control = buffer_for_playing_songs;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7739,7 +7732,7 @@ void Module07_19_MirrorFade() { // 8298f7
|
||||
submodule_index = 0;
|
||||
nmi_load_bg_from_vram = 0;
|
||||
last_music_control = music_unk1;
|
||||
if (palette_swap_flag)
|
||||
if (overworld_palette_swap_flag)
|
||||
Palette_RevertTranslucencySwap();
|
||||
}
|
||||
}
|
||||
@@ -7820,7 +7813,7 @@ void Module11_DungeonFallingEntrance() { // 829af9
|
||||
flag_skip_call_tag_routines++;
|
||||
Dungeon_PlayBlipAndCacheQuadrantVisits();
|
||||
ResetThenCacheRoomEntryProperties();
|
||||
music_control = queued_music_control;
|
||||
music_control = buffer_for_playing_songs;
|
||||
last_music_control = music_unk1;
|
||||
break;
|
||||
}
|
||||
@@ -7857,7 +7850,7 @@ void Module11_02_LoadEntrance() { // 829b1c
|
||||
subsubmodule_index = bak + 1;
|
||||
misc_sprites_graphics_index = 10;
|
||||
InitializeTilesets();
|
||||
palette_sp6r_indoors = 10;
|
||||
palette_sp6 = 10;
|
||||
Dungeon_LoadPalettes();
|
||||
Hud_RestoreTorchBackground();
|
||||
button_mask_b_y = 0;
|
||||
@@ -7873,10 +7866,10 @@ void Module11_02_LoadEntrance() { // 829b1c
|
||||
}
|
||||
|
||||
void Dungeon_LoadSongBankIfNeeded() { // 829bd7
|
||||
if (queued_music_control == 0xff || queued_music_control == 0xf2)
|
||||
if (buffer_for_playing_songs == 0xff || buffer_for_playing_songs == 0xf2)
|
||||
return;
|
||||
|
||||
if (queued_music_control == 3 || queued_music_control == 7 || queued_music_control == 14) {
|
||||
if (buffer_for_playing_songs == 3 || buffer_for_playing_songs == 7 || buffer_for_playing_songs == 14) {
|
||||
LoadOWMusicIfNeeded();
|
||||
} else {
|
||||
if (flag_which_music_type)
|
||||
@@ -7965,14 +7958,13 @@ void HandleEdgeTransitionMovementEast_RightBy8() { // 82b62e
|
||||
}
|
||||
|
||||
void Dungeon_StartInterRoomTrans_Right() { // 82b63a
|
||||
assert(submodule_index == 0);
|
||||
link_quadrant_x ^= 1;
|
||||
Dungeon_AdjustQuadrant();
|
||||
RoomBounds_AddA(&room_bounds_x);
|
||||
Dung_SaveDataForCurrentRoom();
|
||||
DungeonTransition_AdjustCamera_X(link_quadrant_x);
|
||||
HandleEdgeTransition_AdjustCameraBoundaries(2);
|
||||
submodule_index = 1;
|
||||
submodule_index++;
|
||||
if (!link_quadrant_x) {
|
||||
RoomBounds_AddB(&room_bounds_x);
|
||||
BYTE(dungeon_room_index_prev) = dungeon_room_index;
|
||||
@@ -7986,7 +7978,7 @@ void Dungeon_StartInterRoomTrans_Right() { // 82b63a
|
||||
}
|
||||
dungeon_room_index += 1;
|
||||
}
|
||||
submodule_index = 2;
|
||||
submodule_index += 1;
|
||||
if (room_transitioning_flags & 1) {
|
||||
link_is_on_lower_level ^= 1;
|
||||
link_is_on_lower_level_mirror = link_is_on_lower_level;
|
||||
@@ -8364,9 +8356,9 @@ void Dungeon_LoadEntrance() { // 82d8b3
|
||||
link_quadrant_x = kStartingPoint_quadrant2[i] >> 4;
|
||||
link_quadrant_y = kStartingPoint_quadrant2[i] & 0xf;
|
||||
|
||||
queued_music_control = kStartingPoint_musicTrack[i];
|
||||
buffer_for_playing_songs = kStartingPoint_musicTrack[i];
|
||||
if (i == 0 && sram_progress_indicator == 0)
|
||||
queued_music_control = 0xff;
|
||||
buffer_for_playing_songs = 0xff;
|
||||
death_var4 = 0;
|
||||
} else {
|
||||
int i = which_entrance;
|
||||
@@ -8401,9 +8393,9 @@ void Dungeon_LoadEntrance() { // 82d8b3
|
||||
|
||||
link_direction_facing = (i == 0 || i == 0x43) ? 2 : 0;
|
||||
main_tile_theme_index = kEntranceData_blockset[i];
|
||||
queued_music_control = ZeldaGetEntranceMusicTrack(i);
|
||||
if (queued_music_control == 3 && sram_progress_indicator >= 2)
|
||||
queued_music_control = 18;
|
||||
buffer_for_playing_songs = kEntranceData_musicTrack[i];
|
||||
if (buffer_for_playing_songs == 3 && sram_progress_indicator >= 2)
|
||||
buffer_for_playing_songs = 18;
|
||||
|
||||
dung_cur_floor = kEntranceData_floor[i];
|
||||
BYTE(cur_palace_index_x2) = kEntranceData_palace[i];
|
||||
@@ -8599,7 +8591,7 @@ void HandleLinkOnSpiralStairs() { // 87f2c1
|
||||
link_actual_vel_x = 2;
|
||||
}
|
||||
}
|
||||
Link_MovePosition();
|
||||
LinkHop_FindArbitraryLandingSpot();
|
||||
Link_HandleMovingAnimation_StartWithDash();
|
||||
if (!link_timer_push_get_tired && sign8(--countdown_timer_for_staircases)) {
|
||||
countdown_timer_for_staircases = 0;
|
||||
@@ -8638,7 +8630,7 @@ void SpiralStairs_FindLandingSpot() { // 87f391
|
||||
link_actual_vel_x = -4, link_actual_vel_y = 2;
|
||||
if (some_animation_timer_steps == 2)
|
||||
link_actual_vel_x = 0, link_actual_vel_y = 16;
|
||||
Link_MovePosition();
|
||||
LinkHop_FindArbitraryLandingSpot();
|
||||
Link_HandleMovingAnimation_StartWithDash();
|
||||
if ((uint8)link_x_coord == (uint8)tiledetect_which_y_pos[1])
|
||||
some_animation_timer_steps = 2;
|
||||
@@ -82,6 +82,7 @@ void Object_Draw_DoorRight_3x4(uint16 src, int door);
|
||||
void Dungeon_OpeningLockedDoor_Combined(bool skip_anim);
|
||||
const DungPalInfo *GetDungPalInfo(int idx);
|
||||
uint16 Dungeon_GetTeleMsg(int room);
|
||||
uint8 GetEntranceMusicTrack(int entrance);
|
||||
bool Dungeon_IsPitThatHurtsPlayer();
|
||||
void Dungeon_PrepareNextRoomQuadrantUpload();
|
||||
void WaterFlood_BuildOneQuadrantForVRAM();
|
||||
@@ -246,11 +246,11 @@ void Credits_LoadScene_Dungeon() { // 8286fd
|
||||
int i = submodule_index >> 1;
|
||||
sprite_graphics_index = kEnding_SpritePack[i];
|
||||
const DungPalInfo *dpi = GetDungPalInfo(kEnding_SpritePal[i] & 0x3f);
|
||||
palette_sp5l = dpi->pal2;
|
||||
palette_sp6l = dpi->pal3;
|
||||
sprite_aux1_palette = dpi->pal2;
|
||||
sprite_aux2_palette = dpi->pal3;
|
||||
misc_sprites_graphics_index = 10;
|
||||
InitializeTilesets();
|
||||
palette_sp6r_indoors = 10;
|
||||
palette_sp6 = 10;
|
||||
Dungeon_LoadPalettes();
|
||||
BGMODE_copy = 9;
|
||||
R16 = 0;
|
||||
@@ -578,7 +578,7 @@ void Intro_InitializeMemory_darken() { // 8cc1f5
|
||||
DecompressAnimatedDungeonTiles(0x5d);
|
||||
bg_tile_animation_countdown = 2;
|
||||
BYTE(overworld_screen_index) = 0;
|
||||
palette_main_indoors = 0;
|
||||
dung_hdr_palette_1 = 0;
|
||||
overworld_palette_aux3_bp7_lo = 0;
|
||||
R16 = 0;
|
||||
R18 = 0;
|
||||
@@ -1211,7 +1211,7 @@ void Intro_PeriodicSwordAndIntroFlash() { // 8cfe56
|
||||
SetBackdropcolorBlack();
|
||||
if (intro_times_pal_flash) {
|
||||
if ((intro_times_pal_flash & 3) != 0) {
|
||||
(&COLDATA_copy0)[intro_sword_24] |= (enhanced_features0 & kFeatures0_DimFlashes) ? 0x05 : 0x1f;
|
||||
(&COLDATA_copy0)[intro_sword_24] |= 0x1f;
|
||||
intro_sword_24 = (intro_sword_24 == 2) ? 0 : intro_sword_24 + 1;
|
||||
}
|
||||
intro_times_pal_flash--;
|
||||
@@ -1,25 +0,0 @@
|
||||
@echo off
|
||||
|
||||
2>nul (del zelda3_assets.dat)
|
||||
python assets/restool.py --extract-from-rom
|
||||
IF NOT ERRORLEVEL 0 goto ERROR
|
||||
|
||||
IF NOT EXIST "zelda3_assets.dat" (
|
||||
ECHO ERROR: The python program didn't generate zelda3_assets.dat successfully.
|
||||
goto ERROR
|
||||
) ELSE (
|
||||
REM
|
||||
)
|
||||
|
||||
goto DONE
|
||||
|
||||
|
||||
:ERROR
|
||||
ECHO:
|
||||
ECHO ERROR: Asset extraction failed!
|
||||
pause
|
||||
EXIT /B 1
|
||||
|
||||
:DONE
|
||||
echo Complete!
|
||||
pause
|
||||
@@ -42,24 +42,17 @@ enum {
|
||||
kFeatures0_CancelBirdTravel = 8192,
|
||||
|
||||
kFeatures0_GameChangingBugFixes = 16384,
|
||||
|
||||
kFeatures0_SwitchLRLimit = 32768,
|
||||
|
||||
kFeatures0_DimFlashes = 65536,
|
||||
};
|
||||
|
||||
#define enhanced_features0 (*(uint32*)(g_ram+0x64c))
|
||||
#define msu_curr_sample (*(uint32*)(g_ram+0x650))
|
||||
#define msu_volume (*(uint8*)(g_ram+0x654))
|
||||
#define msu_track (*(uint8*)(g_ram+0x655))
|
||||
#define hud_inventory_order ((uint8*)(g_ram + 0x225)) // 4x6 bytes
|
||||
#define hud_cur_item_x (*(uint8*)(g_ram+0x656))
|
||||
#define hud_cur_item_l (*(uint8*)(g_ram+0x657))
|
||||
#define hud_cur_item_r (*(uint8*)(g_ram+0x658))
|
||||
|
||||
|
||||
#define hud_inventory_order ((uint8*)(g_ram + 0x225)) // 4x6 bytes
|
||||
|
||||
extern uint32 g_wanted_zelda_features;
|
||||
extern bool msu_enabled;
|
||||
|
||||
|
||||
#endif // ZELDA3_FEATURES_H_
|
||||
@@ -46,139 +46,150 @@ static const uint16 kHudItemInVramPtr_Old[20] = {
|
||||
|
||||
#define kHudItemInVramPtr (kNewStyleInventory ? kHudItemInVramPtr_New : kHudItemInVramPtr_Old)
|
||||
|
||||
static const uint16 kHudBottlesGfx[128] = {
|
||||
0x24f5, 0x24f5, 0x24f5, 0x24f5, 0x24f5, 0x24f5, 0x24f5, 0x24f5, 0x255c, 0x2564, 0x2562, 0x2557, 0x2561, 0x255e, 0x255e, 0x255c,
|
||||
0x24f5, 0x24f5, 0x24f5, 0x24f5, 0x24f5, 0x24f5, 0x24f5, 0x24f5, 0x2551, 0x255e, 0x2563, 0x2563, 0x255b, 0x2554, 0x24f5, 0x24f5,
|
||||
0x255b, 0x2558, 0x2555, 0x2554, 0x24f5, 0x24f5, 0x24f5, 0x24f5, 0x255c, 0x2554, 0x2553, 0x2558, 0x2552, 0x2558, 0x255d, 0x2554,
|
||||
0x255c, 0x2550, 0x2556, 0x2558, 0x2552, 0x24f5, 0x24f5, 0x24f5, 0x255c, 0x2554, 0x2553, 0x2558, 0x2552, 0x2558, 0x255d, 0x2554,
|
||||
0x2552, 0x2564, 0x2561, 0x2554, 0x256a, 0x2550, 0x255b, 0x255b, 0x255c, 0x2554, 0x2553, 0x2558, 0x2552, 0x2558, 0x255d, 0x2554,
|
||||
0x24f5, 0x24f5, 0x24f5, 0x24f5, 0x24f5, 0x24f5, 0x24f5, 0x24f5, 0x2555, 0x2550, 0x2554, 0x2561, 0x2558, 0x2554, 0x24f5, 0x24f5,
|
||||
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 ItemBoxGfx kHudItemBottles[9] = {
|
||||
{{0x20f5, 0x20f5, 0x20f5, 0x20f5}},
|
||||
{{0x2044, 0x2045, 0x2046, 0x2047}},
|
||||
{{0x2837, 0x2838, 0x2cc3, 0x2cd3}},
|
||||
{{0x24d2, 0x64d2, 0x24e2, 0x24e3}},
|
||||
{{0x3cd2, 0x7cd2, 0x3ce2, 0x3ce3}},
|
||||
{{0x2cd2, 0x6cd2, 0x2ce2, 0x2ce3}},
|
||||
{{0x2855, 0x6855, 0x2c57, 0x2c5a}},
|
||||
{{0x2837, 0x2838, 0x2839, 0x283a}},
|
||||
{{0x2837, 0x2838, 0x2839, 0x283a}},
|
||||
{0x20f5, 0x20f5, 0x20f5, 0x20f5},
|
||||
{0x2044, 0x2045, 0x2046, 0x2047},
|
||||
{0x2837, 0x2838, 0x2cc3, 0x2cd3},
|
||||
{0x24d2, 0x64d2, 0x24e2, 0x24e3},
|
||||
{0x3cd2, 0x7cd2, 0x3ce2, 0x3ce3},
|
||||
{0x2cd2, 0x6cd2, 0x2ce2, 0x2ce3},
|
||||
{0x2855, 0x6855, 0x2c57, 0x2c5a},
|
||||
{0x2837, 0x2838, 0x2839, 0x283a},
|
||||
{0x2837, 0x2838, 0x2839, 0x283a},
|
||||
};
|
||||
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}},
|
||||
{0x20f5, 0x20f5, 0x20f5, 0x20f5},
|
||||
{0x28ba, 0x28e9, 0x28e8, 0x28cb},
|
||||
{0x28ba, 0x284a, 0x2849, 0x28cb},
|
||||
{0x28ba, 0x28e9, 0x28e8, 0x28cb},
|
||||
{0x28ba, 0x28bb, 0x24ca, 0x28cb},
|
||||
};
|
||||
static const ItemBoxGfx kHudItemBoomerang[3] = {
|
||||
{{0x20f5, 0x20f5, 0x20f5, 0x20f5}},
|
||||
{{0x2cb8, 0x2cb9, 0x2cf5, 0x2cc9}},
|
||||
{{0x24b8, 0x24b9, 0x24f5, 0x24c9}},
|
||||
{0x20f5, 0x20f5, 0x20f5, 0x20f5},
|
||||
{0x2cb8, 0x2cb9, 0x2cf5, 0x2cc9},
|
||||
{0x24b8, 0x24b9, 0x24f5, 0x24c9},
|
||||
};
|
||||
static const ItemBoxGfx kHudItemHookshot[2] = {
|
||||
{{0x20f5, 0x20f5, 0x20f5, 0x20f5}},
|
||||
{{0x24f5, 0x24f6, 0x24c0, 0x24f5}},
|
||||
{0x20f5, 0x20f5, 0x20f5, 0x20f5},
|
||||
{0x24f5, 0x24f6, 0x24c0, 0x24f5},
|
||||
};
|
||||
static const ItemBoxGfx kHudItemBombs[2] = {
|
||||
{{0x20f5, 0x20f5, 0x20f5, 0x20f5}},
|
||||
{{0x2cb2, 0x2cb3, 0x2cc2, 0x6cc2}},
|
||||
{0x20f5, 0x20f5, 0x20f5, 0x20f5},
|
||||
{0x2cb2, 0x2cb3, 0x2cc2, 0x6cc2},
|
||||
};
|
||||
static const ItemBoxGfx kHudItemMushroom[3] = {
|
||||
{{0x20f5, 0x20f5, 0x20f5, 0x20f5}},
|
||||
{{0x2444, 0x2445, 0x2446, 0x2447}},
|
||||
{{0x203b, 0x203c, 0x203d, 0x203e}},
|
||||
{0x20f5, 0x20f5, 0x20f5, 0x20f5},
|
||||
{0x2444, 0x2445, 0x2446, 0x2447},
|
||||
{0x203b, 0x203c, 0x203d, 0x203e},
|
||||
};
|
||||
static const ItemBoxGfx kHudItemFireRod[2] = {
|
||||
{{0x20f5, 0x20f5, 0x20f5, 0x20f5}},
|
||||
{{0x24b0, 0x24b1, 0x24c0, 0x24c1}},
|
||||
{0x20f5, 0x20f5, 0x20f5, 0x20f5},
|
||||
{0x24b0, 0x24b1, 0x24c0, 0x24c1},
|
||||
};
|
||||
static const ItemBoxGfx kHudItemIceRod[2] = {
|
||||
{{0x20f5, 0x20f5, 0x20f5, 0x20f5}},
|
||||
{{0x2cb0, 0x2cbe, 0x2cc0, 0x2cc1}},
|
||||
{0x20f5, 0x20f5, 0x20f5, 0x20f5},
|
||||
{0x2cb0, 0x2cbe, 0x2cc0, 0x2cc1},
|
||||
};
|
||||
static const ItemBoxGfx kHudItemBombos[2] = {
|
||||
{{0x20f5, 0x20f5, 0x20f5, 0x20f5}},
|
||||
{{0x287d, 0x287e, 0xe87e, 0xe87d}},
|
||||
{0x20f5, 0x20f5, 0x20f5, 0x20f5},
|
||||
{0x287d, 0x287e, 0xe87e, 0xe87d},
|
||||
};
|
||||
static const ItemBoxGfx kHudItemEther[2] = {
|
||||
{{0x20f5, 0x20f5, 0x20f5, 0x20f5}},
|
||||
{{0x2876, 0x2877, 0xE877, 0xE876}},
|
||||
{0x20f5, 0x20f5, 0x20f5, 0x20f5},
|
||||
{0x2876, 0x2877, 0xE877, 0xE876},
|
||||
};
|
||||
static const ItemBoxGfx kHudItemQuake[2] = {
|
||||
{{0x20f5, 0x20f5, 0x20f5, 0x20f5}},
|
||||
{{0x2866, 0x2867, 0xE867, 0xE866}},
|
||||
{0x20f5, 0x20f5, 0x20f5, 0x20f5},
|
||||
{0x2866, 0x2867, 0xE867, 0xE866},
|
||||
};
|
||||
static const ItemBoxGfx kHudItemTorch[2] = {
|
||||
{{0x20f5, 0x20f5, 0x20f5, 0x20f5}},
|
||||
{{0x24bc, 0x24bd, 0x24cc, 0x24cd}},
|
||||
{0x20f5, 0x20f5, 0x20f5, 0x20f5},
|
||||
{0x24bc, 0x24bd, 0x24cc, 0x24cd},
|
||||
};
|
||||
static const ItemBoxGfx kHudItemHammer[2] = {
|
||||
{{0x20f5, 0x20f5, 0x20f5, 0x20f5}},
|
||||
{{0x20b6, 0x20b7, 0x20c6, 0x20c7}},
|
||||
{0x20f5, 0x20f5, 0x20f5, 0x20f5},
|
||||
{0x20b6, 0x20b7, 0x20c6, 0x20c7},
|
||||
};
|
||||
static const ItemBoxGfx kHudItemFlute[4] = {
|
||||
{{0x20f5, 0x20f5, 0x20f5, 0x20f5}},
|
||||
{{0x20d0, 0x20d1, 0x20e0, 0x20e1}},
|
||||
{{0x2cd4, 0x2cd5, 0x2ce4, 0x2ce5}},
|
||||
{{0x2cd4, 0x2cd5, 0x2ce4, 0x2ce5}},
|
||||
{0x20f5, 0x20f5, 0x20f5, 0x20f5},
|
||||
{0x20d0, 0x20d1, 0x20e0, 0x20e1},
|
||||
{0x2cd4, 0x2cd5, 0x2ce4, 0x2ce5},
|
||||
{0x2cd4, 0x2cd5, 0x2ce4, 0x2ce5},
|
||||
};
|
||||
static const ItemBoxGfx kHudItemBugNet[2] = {
|
||||
{{0x20f5, 0x20f5, 0x20f5, 0x20f5}},
|
||||
{{0x3c40, 0x3c41, 0x2842, 0x3c43}},
|
||||
{0x20f5, 0x20f5, 0x20f5, 0x20f5},
|
||||
{0x3c40, 0x3c41, 0x2842, 0x3c43},
|
||||
};
|
||||
static const ItemBoxGfx kHudItemBookMudora[2] = {
|
||||
{{0x20f5, 0x20f5, 0x20f5, 0x20f5}},
|
||||
{{0x3ca5, 0x3ca6, 0x3cd8, 0x3cd9}},
|
||||
{0x20f5, 0x20f5, 0x20f5, 0x20f5},
|
||||
{0x3ca5, 0x3ca6, 0x3cd8, 0x3cd9},
|
||||
};
|
||||
static const ItemBoxGfx kHudItemCaneSomaria[2] = {
|
||||
{{0x20f5, 0x20f5, 0x20f5, 0x20f5}},
|
||||
{{0x24dc, 0x24dd, 0x24ec, 0x24ed}},
|
||||
{0x20f5, 0x20f5, 0x20f5, 0x20f5},
|
||||
{0x24dc, 0x24dd, 0x24ec, 0x24ed},
|
||||
};
|
||||
static const ItemBoxGfx kHudItemCaneByrna[2] = {
|
||||
{{0x20f5, 0x20f5, 0x20f5, 0x20f5}},
|
||||
{{0x2cdc, 0x2cdd, 0x2cec, 0x2ced}},
|
||||
{0x20f5, 0x20f5, 0x20f5, 0x20f5},
|
||||
{0x2cdc, 0x2cdd, 0x2cec, 0x2ced},
|
||||
};
|
||||
static const ItemBoxGfx kHudItemCape[2] = {
|
||||
{{0x20f5, 0x20f5, 0x20f5, 0x20f5}},
|
||||
{{0x24b4, 0x24b5, 0x24c4, 0x24c5}},
|
||||
{0x20f5, 0x20f5, 0x20f5, 0x20f5},
|
||||
{0x24b4, 0x24b5, 0x24c4, 0x24c5},
|
||||
};
|
||||
static const ItemBoxGfx kHudItemMirror[4] = {
|
||||
{{0x20f5, 0x20f5, 0x20f5, 0x20f5}},
|
||||
{{0x28de, 0x28df, 0x28ee, 0x28ef}},
|
||||
{{0x2c62, 0x2c63, 0x2c72, 0x2c73}},
|
||||
{{0x2886, 0x2887, 0x2888, 0x2889}},
|
||||
{0x20f5, 0x20f5, 0x20f5, 0x20f5},
|
||||
{0x28de, 0x28df, 0x28ee, 0x28ef},
|
||||
{0x2c62, 0x2c63, 0x2c72, 0x2c73},
|
||||
{0x2886, 0x2887, 0x2888, 0x2889},
|
||||
};
|
||||
static const ItemBoxGfx kHudItemGloves[3] = {
|
||||
{{0x20f5, 0x20f5, 0x20f5, 0x20f5}},
|
||||
{{0x2130, 0x2131, 0x2140, 0x2141}},
|
||||
{{0x28da, 0x28db, 0x28ea, 0x28eb}},
|
||||
{0x20f5, 0x20f5, 0x20f5, 0x20f5},
|
||||
{0x2130, 0x2131, 0x2140, 0x2141},
|
||||
{0x28da, 0x28db, 0x28ea, 0x28eb},
|
||||
};
|
||||
static const ItemBoxGfx kHudItemBoots[2] = {
|
||||
{{0x20f5, 0x20f5, 0x20f5, 0x20f5}},
|
||||
{{0x3429, 0x342a, 0x342b, 0x342c}},
|
||||
{0x20f5, 0x20f5, 0x20f5, 0x20f5},
|
||||
{0x3429, 0x342a, 0x342b, 0x342c},
|
||||
};
|
||||
static const ItemBoxGfx kHudItemFlippers[2] = {
|
||||
{{0x20f5, 0x20f5, 0x20f5, 0x20f5}},
|
||||
{{0x2c9a, 0x2c9b, 0x2c9d, 0x2c9e}},
|
||||
{0x20f5, 0x20f5, 0x20f5, 0x20f5},
|
||||
{0x2c9a, 0x2c9b, 0x2c9d, 0x2c9e},
|
||||
};
|
||||
static const ItemBoxGfx kHudItemMoonPearl[2] = {
|
||||
{{0x20f5, 0x20f5, 0x20f5, 0x20f5}},
|
||||
{{0x2433, 0x2434, 0x2435, 0x2436}},
|
||||
{0x20f5, 0x20f5, 0x20f5, 0x20f5},
|
||||
{0x2433, 0x2434, 0x2435, 0x2436},
|
||||
};
|
||||
static const ItemBoxGfx kHudItemEmpty[1] = {
|
||||
{{0x20f5, 0x20f5, 0x20f5, 0x20f5}},
|
||||
{0x20f5, 0x20f5, 0x20f5, 0x20f5},
|
||||
};
|
||||
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}},
|
||||
{0x20f5, 0x20f5, 0x20f5, 0x20f5},
|
||||
{0x2c64, 0x2cce, 0x2c75, 0x3d25},
|
||||
{0x2c8a, 0x2c65, 0x2474, 0x3d26},
|
||||
{0x248a, 0x2465, 0x3c74, 0x2d48},
|
||||
{0x288a, 0x2865, 0x2c74, 0x2d39},
|
||||
};
|
||||
static const ItemBoxGfx kHudItemShield[4] = {
|
||||
{{0x24f5, 0x24f5, 0x24f5, 0x24f5}},
|
||||
{{0x2cfd, 0x6cfd, 0x2cfe, 0x6cfe}},
|
||||
{{0x34ff, 0x74ff, 0x349f, 0x749f}},
|
||||
{{0x2880, 0x2881, 0x288d, 0x288e}},
|
||||
{0x24f5, 0x24f5, 0x24f5, 0x24f5},
|
||||
{0x2cfd, 0x6cfd, 0x2cfe, 0x6cfe},
|
||||
{0x34ff, 0x74ff, 0x349f, 0x749f},
|
||||
{0x2880, 0x2881, 0x288d, 0x288e},
|
||||
};
|
||||
static const ItemBoxGfx kHudItemArmor[5] = {
|
||||
{{0x3c68, 0x7c68, 0x3c78, 0x7c78}},
|
||||
{{0x2c68, 0x6c68, 0x2c78, 0x6c78}},
|
||||
{{0x2468, 0x6468, 0x2478, 0x6478}},
|
||||
{0x3c68, 0x7c68, 0x3c78, 0x7c78},
|
||||
{0x2c68, 0x6c68, 0x2c78, 0x6c78},
|
||||
{0x2468, 0x6468, 0x2478, 0x6478},
|
||||
};
|
||||
|
||||
|
||||
@@ -594,62 +605,46 @@ static void Hud_EquipItemBelow(uint8 *item) { // 8ddf00
|
||||
} while (!Hud_DoWeHaveThisItem(*item));
|
||||
}
|
||||
|
||||
int GetCurrentItemButtonIndex() {
|
||||
if (enhanced_features0 & kFeatures0_SwitchLR) {
|
||||
return (joypad1L_last & kJoypadL_X) ? 1 :
|
||||
(joypad1L_last & kJoypadL_L) ? 2 :
|
||||
(joypad1L_last & kJoypadL_R) ? 3 : 0;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint8 *GetCurrentItemButtonPtr(int i) {
|
||||
return (i == 0) ? &hud_cur_item :
|
||||
(i == 1) ? &hud_cur_item_x :
|
||||
(i == 2) ? &hud_cur_item_l : &hud_cur_item_r;
|
||||
}
|
||||
|
||||
void Hud_NormalMenu() { // 8ddf15
|
||||
timer_for_flashing_circle++;
|
||||
if (!BYTE(joypad1H_last))
|
||||
BYTE(hud_tmp1) = 0;
|
||||
|
||||
if (filtered_joypad_H & kJoypadH_Start) {
|
||||
if (filtered_joypad_H & 0x10) { // start
|
||||
overworld_map_state = 5;
|
||||
sound_effect_2 = 18;
|
||||
return;
|
||||
}
|
||||
|
||||
// Allow select to open the save/exit thing
|
||||
if (joypad1H_last & kJoypadH_Select && sram_progress_indicator) {
|
||||
if (joypad1H_last & 0x20 && sram_progress_indicator) { // select
|
||||
BG3VOFS_copy2 = -8;
|
||||
Hud_CloseMenu();
|
||||
DisplaySelectMenu();
|
||||
return;
|
||||
}
|
||||
|
||||
if (joypad1H_last & kJoypadH_Y && !(joypad1L_last & kJoypadL_X) && (enhanced_features0 & kFeatures0_SwitchLR)) {
|
||||
if (filtered_joypad_H & kJoypadH_Up) {
|
||||
if (joypad1H_last & 0x40 && !(joypad1L_last & 0x40) && (enhanced_features0 & kFeatures0_SwitchLR)) {
|
||||
if (filtered_joypad_H & 8) {
|
||||
Hud_ReorderItem(kNewStyleInventory ? -6 : -5);
|
||||
} else if (filtered_joypad_H & kJoypadH_Down) {
|
||||
} else if (filtered_joypad_H & 4) {
|
||||
Hud_ReorderItem(kNewStyleInventory ? 6 : 5);
|
||||
} else if (filtered_joypad_H & kJoypadH_Left) {
|
||||
} else if (filtered_joypad_H & 2) {
|
||||
Hud_ReorderItem(-1);
|
||||
} else if (filtered_joypad_H & kJoypadH_Right) {
|
||||
} else if (filtered_joypad_H & 1) {
|
||||
Hud_ReorderItem(1);
|
||||
}
|
||||
} else if (!BYTE(hud_tmp1)) {
|
||||
// If Special Key button is down, then move their circle
|
||||
int btn_index = GetCurrentItemButtonIndex();
|
||||
uint8 *item_p = GetCurrentItemButtonPtr(btn_index);
|
||||
// If the x button is down, then move the blue circle
|
||||
uint8 *item_p = (joypad1L_last & 0x40 && (enhanced_features0 & kFeatures0_SwitchLR)) ? &hud_cur_item_x : &hud_cur_item;
|
||||
uint16 old_item = *item_p;
|
||||
if (filtered_joypad_H & kJoypadH_Up) {
|
||||
if (filtered_joypad_H & 8) {
|
||||
Hud_EquipItemAbove(item_p);
|
||||
} else if (filtered_joypad_H & kJoypadH_Down) {
|
||||
} else if (filtered_joypad_H & 4) {
|
||||
Hud_EquipItemBelow(item_p);
|
||||
} else if (filtered_joypad_H & kJoypadH_Left) {
|
||||
} else if (filtered_joypad_H & 2) {
|
||||
Hud_EquipPrevItem(item_p);
|
||||
} else if (filtered_joypad_H & kJoypadH_Right) {
|
||||
} else if (filtered_joypad_H & 1) {
|
||||
Hud_EquipNextItem(item_p);
|
||||
}
|
||||
BYTE(hud_tmp1) = filtered_joypad_H;
|
||||
@@ -758,11 +753,11 @@ void Hud_ExpandBottleMenu() { // 8de08c
|
||||
|
||||
void Hud_BottleMenu() { // 8de0df
|
||||
timer_for_flashing_circle++;
|
||||
if (filtered_joypad_H & kJoypadH_Start) {
|
||||
if (filtered_joypad_H & 0x10) {
|
||||
sound_effect_2 = 18;
|
||||
overworld_map_state = 5;
|
||||
} else if (filtered_joypad_H & (kJoypadH_Left | kJoypadH_Right)) {
|
||||
if (filtered_joypad_H & kJoypadH_Left) {
|
||||
} else if (filtered_joypad_H & 3) {
|
||||
if (filtered_joypad_H & 2) {
|
||||
Hud_EquipPrevItem(&hud_cur_item);
|
||||
} else {
|
||||
Hud_EquipNextItem(&hud_cur_item);
|
||||
@@ -776,10 +771,10 @@ void Hud_BottleMenu() { // 8de0df
|
||||
return;
|
||||
}
|
||||
Hud_DrawBottleMenu_Update();
|
||||
if (filtered_joypad_H & (kJoypadH_Down | kJoypadH_Up)) {
|
||||
if (filtered_joypad_H & 12) {
|
||||
uint8 old_val = link_item_bottle_index - 1, val = old_val;
|
||||
|
||||
if (filtered_joypad_H & kJoypadH_Up) {
|
||||
if (filtered_joypad_H & 8) {
|
||||
do {
|
||||
val = (val - 1) & 3;
|
||||
} while (!link_bottle_info[val]);
|
||||
@@ -928,80 +923,22 @@ static const ItemBoxGfx *Hud_GetIconForItem(int i) {
|
||||
return &kHudItemBoxGfxPtrs[i - 1][item_val];
|
||||
}
|
||||
|
||||
static void CopyTilesForSwitchLR(int switch_lr) {
|
||||
#define PV(a0,a1,a2,a3,a4,a5,a6,a7) ((a0 & 1) << 7 | (a0 >> 1 & 1) << 15 | (a1 & 1) << 6 | (a1 >> 1 & 1) << 14 | (a2 & 1) <<5 | (a2 >> 1&1) <<13 | (a3 & 1) << 4 | (a3>> 1 & 1) << 12 | (a4 & 1) << 3 | (a4 >> 1 & 1) << 11 | (a5 & 1) << 2 | (a5 >> 1 & 1) << 10 | (a6 & 1) << 1 | (a6 >> 1 & 1) << 9 | (a7 & 1) << 0 | (a7 >> 1 & 1) << 8)
|
||||
|
||||
if (switch_lr == 3) {
|
||||
static const uint16 kBytesForNewTile0xC_TopOfR[8] = {
|
||||
PV(1,1,1,1,1,1,3,3),
|
||||
PV(1,1,1,1,1,1,1,3),
|
||||
PV(1,1,1,1,1,1,1,1),
|
||||
PV(1,1,1,3,3,1,1,1),
|
||||
PV(1,1,1,3,3,1,1,1),
|
||||
PV(1,1,1,3,3,1,1,1),
|
||||
PV(1,1,1,3,3,1,1,1),
|
||||
PV(1,1,1,1,1,1,1,3)
|
||||
};
|
||||
memcpy(&g_zenv.vram[0x7000 + 0xc * 8], kBytesForNewTile0xC_TopOfR, sizeof(kBytesForNewTile0xC_TopOfR));
|
||||
|
||||
static const uint16 kBytesForNewTile0xD_BottomofR[8] = {
|
||||
PV(1,1,1,1,1,1,3,3),
|
||||
PV(1,1,1,3,1,1,1,3),
|
||||
PV(1,1,1,3,3,1,1,1),
|
||||
PV(1,1,1,3,3,1,1,1),
|
||||
PV(1,1,1,3,3,1,1,1),
|
||||
PV(1,1,1,3,3,1,1,1),
|
||||
PV(1,1,1,3,3,1,1,1),
|
||||
PV(1,1,1,3,3,1,1,1)
|
||||
};
|
||||
memcpy(&g_zenv.vram[0x7000 + 0xd * 8], kBytesForNewTile0xD_BottomofR, sizeof(kBytesForNewTile0xD_BottomofR));
|
||||
} else if (switch_lr == 2) {
|
||||
static const uint16 kBytesForNewTile0xE_TopOfL[8] = {
|
||||
PV(1,1,1,3,3,3,3,3),
|
||||
PV(1,1,1,3,3,3,3,3),
|
||||
PV(1,1,1,3,3,3,3,3),
|
||||
PV(1,1,1,3,3,3,3,3),
|
||||
PV(1,1,1,3,3,3,3,3),
|
||||
PV(1,1,1,3,3,3,3,3),
|
||||
PV(1,1,1,3,3,3,3,3),
|
||||
PV(1,1,1,3,3,3,3,3)
|
||||
};
|
||||
memcpy(&g_zenv.vram[0x7000 + 0xe * 8], kBytesForNewTile0xE_TopOfL, sizeof(kBytesForNewTile0xE_TopOfL));
|
||||
|
||||
static const uint16 kBytesForNewTile0xF_BottomofL[8] = {
|
||||
PV(1,1,1,3,3,3,3,3),
|
||||
PV(1,1,1,3,3,3,3,3),
|
||||
PV(1,1,1,3,3,3,3,3),
|
||||
PV(1,1,1,3,3,3,3,3),
|
||||
PV(1,1,1,3,3,3,3,3),
|
||||
PV(1,1,1,1,1,1,1,1),
|
||||
PV(1,1,1,1,1,1,1,1),
|
||||
PV(1,1,1,1,1,1,1,1)
|
||||
};
|
||||
memcpy(&g_zenv.vram[0x7000 + 0xf * 8], kBytesForNewTile0xF_BottomofL, sizeof(kBytesForNewTile0xF_BottomofL));
|
||||
}
|
||||
#undef PV
|
||||
}
|
||||
|
||||
static const uint8 kSwitchLR_palettes[] = { 7, 3, 4, 4 };
|
||||
|
||||
void Hud_DrawYButtonItems() { // 8de3d9
|
||||
uint16 *dst = uvram_screen.row[0].col;
|
||||
int x = kNewStyleInventory ? 0 : 1;
|
||||
|
||||
int btn_index = GetCurrentItemButtonIndex();
|
||||
CopyTilesForSwitchLR(btn_index);
|
||||
Hud_DrawBox(dst, x, 5, 20 - x, 19, kSwitchLR_palettes[btn_index]);
|
||||
static const uint16 kEquipmentLetterTiles[4][2] = {
|
||||
{0x3CF0, 0x3CF1}, // Y
|
||||
{0x2CF0, 0x2CF0 | 0x8000}, // X
|
||||
{0x200E | 4 << 10, 0x200F | 4 << 10}, // L
|
||||
{0x200C | 4 << 10, 0x200D | 4 << 10} // R
|
||||
};
|
||||
bool is_x = (joypad1L_last & 0x40 && (enhanced_features0 & kFeatures0_SwitchLR));
|
||||
uint8 palette = is_x ? 3 : 7;
|
||||
Hud_DrawBox(dst, x, 5, 20 - x, 19, palette);
|
||||
|
||||
if (!kNewStyleInventory) {
|
||||
dst[HUDXY(2, 6)] = kEquipmentLetterTiles[btn_index][0];
|
||||
dst[HUDXY(2, 7)] = kEquipmentLetterTiles[btn_index][1];
|
||||
if (palette == 3) {
|
||||
dst[HUDXY(2, 6)] = 0x2CF0;
|
||||
dst[HUDXY(2, 7)] = 0x2CF0 | 0x8000;
|
||||
} else {
|
||||
dst[HUDXY(2, 6)] = 0x3CF0;
|
||||
dst[HUDXY(2, 7)] = 0x3CF1;
|
||||
}
|
||||
}
|
||||
dst[HUDXY(x + 2, 5)] = 0x246E;
|
||||
dst[HUDXY(x + 3, 5)] = 0x246F;
|
||||
@@ -1076,16 +1013,16 @@ void Hud_DrawProgressIcons_Pendants() { // 8de9d3
|
||||
0xa8fb, 0xa8f9, 0xa8f9, 0xa8f9, 0xa8f9, 0xa8f9, 0xa8f9, 0xa8f9, 0xa8f9, 0xe8fb,
|
||||
};
|
||||
static const ItemBoxGfx kHudPendants0[2] = {
|
||||
{{0x313b, 0x313c, 0x313d, 0x313e}},
|
||||
{{0x252b, 0x252c, 0x252d, 0x252e}},
|
||||
{0x313b, 0x313c, 0x313d, 0x313e},
|
||||
{0x252b, 0x252c, 0x252d, 0x252e}
|
||||
};
|
||||
static const ItemBoxGfx kHudPendants1[2] = {
|
||||
{{0x313b, 0x313c, 0x313d, 0x313e}},
|
||||
{{0x2d2b, 0x2d2c, 0x2d2d, 0x2d2e}},
|
||||
{0x313b, 0x313c, 0x313d, 0x313e},
|
||||
{0x2d2b, 0x2d2c, 0x2d2d, 0x2d2e}
|
||||
};
|
||||
static const ItemBoxGfx kHudPendants2[2] = {
|
||||
{{0x313b, 0x313c, 0x313d, 0x313e}},
|
||||
{{0x3d2b, 0x3d2c, 0x3d2d, 0x3d2e}},
|
||||
{0x313b, 0x313c, 0x313d, 0x313e},
|
||||
{0x3d2b, 0x3d2c, 0x3d2d, 0x3d2e}
|
||||
};
|
||||
uint16 *dst = uvram_screen.row[0].col + (kNewStyleInventory ? HUDXY(22, 11) : HUDXY(21, 11));
|
||||
Hud_DrawNxN(dst, kProgressIconPendantsBg, 10, 9);
|
||||
@@ -1202,18 +1139,20 @@ void Hud_DrawSelectedYButtonItem() { // 8deb3a
|
||||
|
||||
uint16 *dst_org = uvram_screen.row[0].col;
|
||||
uint16 *dst_box = dst_org + (kNewStyleInventory ? 1 : 0);
|
||||
|
||||
int btn_index = GetCurrentItemButtonIndex();
|
||||
int item = *GetCurrentItemButtonPtr(btn_index);
|
||||
Hud_DrawBox(dst_box, 21, 5, 21 + 9, 10, kSwitchLR_palettes[btn_index]);
|
||||
|
||||
bool is_x = (joypad1L_last & 0x40 && (enhanced_features0 & kFeatures0_SwitchLR));
|
||||
uint8 palette = is_x ? 3 : 7;
|
||||
Hud_DrawBox(dst_box, 21, 5, 21 + 9, 10, palette);
|
||||
|
||||
// Display either the current item or the item assigned
|
||||
// to the x, l, or r key.
|
||||
// to the x key.
|
||||
int item = is_x ? hud_cur_item_x : hud_cur_item;
|
||||
|
||||
if (item != 0) {
|
||||
uint16 *p = dst_org + kHudItemInVramPtr[Hud_GetItemPosition(item)];
|
||||
Hud_Copy2x2(dst_box + HUDXY(25, 6), p);
|
||||
if (timer_for_flashing_circle & 0x10)
|
||||
Hud_DrawFlashingCircle(p, kSwitchLR_palettes[btn_index]);
|
||||
Hud_DrawFlashingCircle(p, palette);
|
||||
}
|
||||
|
||||
const uint16 *src_p;
|
||||
@@ -1232,7 +1171,7 @@ void Hud_DrawSelectedYButtonItem() { // 8deb3a
|
||||
} else if (item == kHudItem_Shovel) {
|
||||
src_p = &kHudItemText[(13 - 1) * 16];
|
||||
} else if (item == 0) {
|
||||
src_p = btn_index ? kNotAssignedItemText : &kHudItemText[(20 - 1) * 16];
|
||||
src_p = is_x ? kNotAssignedItemText : &kHudItemText[(20 - 1) * 16];
|
||||
} else {
|
||||
src_p = &kHudItemText[(item - 1) * 16];
|
||||
}
|
||||
@@ -1256,10 +1195,10 @@ void Hud_DrawEquipmentBox() { // 8ded29
|
||||
memcpy(dst + HUDXY(22, 26), &kHudEquipmentDungeonItemText[8], 8 * sizeof(uint16));
|
||||
|
||||
static const ItemBoxGfx kHudItemHeartPieces[4] = {
|
||||
{{0x2484, 0x6484, 0x2485, 0x6485}},
|
||||
{{0x24ad, 0x6484, 0x2485, 0x6485}},
|
||||
{{0x24ad, 0x6484, 0x24ae, 0x6485}},
|
||||
{{0x24ad, 0x64ad, 0x24ae, 0x6485}},
|
||||
{0x2484, 0x6484, 0x2485, 0x6485},
|
||||
{0x24ad, 0x6484, 0x2485, 0x6485},
|
||||
{0x24ad, 0x6484, 0x24ae, 0x6485},
|
||||
{0x24ad, 0x64ad, 0x24ae, 0x6485},
|
||||
};
|
||||
if (cur_palace_index_x2 == 0xff) {
|
||||
for (int i = 0; i < 8; i++)
|
||||
@@ -1271,14 +1210,14 @@ void Hud_DrawEquipmentBox() { // 8ded29
|
||||
Hud_DrawItem(dst + HUDXY(28, 23), &kHudItemArmor[link_armor]);
|
||||
|
||||
static const ItemBoxGfx kHudItemPalaceItem[2] = {
|
||||
{{0x28d6, 0x68d6, 0x28e6, 0x28e7}},
|
||||
{{0x354b, 0x354c, 0x354d, 0x354e}},
|
||||
{0x28d6, 0x68d6, 0x28e6, 0x28e7},
|
||||
{0x354b, 0x354c, 0x354d, 0x354e},
|
||||
};
|
||||
static const ItemBoxGfx kHudItemDungeonMap[1] = {
|
||||
{{0x28de, 0x28df, 0x28ee, 0x28ef}},
|
||||
{0x28de, 0x28df, 0x28ee, 0x28ef},
|
||||
};
|
||||
static const ItemBoxGfx kHudItemDungeonCompass[1] = {
|
||||
{{0x24bf, 0x64bf, 0x2ccf, 0x6ccf}},
|
||||
{0x24bf, 0x64bf, 0x2ccf, 0x6ccf},
|
||||
};
|
||||
if (cur_palace_index_x2 != 0xff &&
|
||||
(link_bigkey << (cur_palace_index_x2 >> 1)) & 0x8000) {
|
||||
@@ -1506,31 +1445,25 @@ const uint16 *Hud_GetItemBoxPtr(int item) {
|
||||
void Hud_HandleItemSwitchInputs() {
|
||||
if (!(enhanced_features0 & kFeatures0_SwitchLR))
|
||||
return;
|
||||
|
||||
bool direction;
|
||||
|
||||
if (filtered_joypad_L & kJoypadL_L && (hud_cur_item_l == 0))
|
||||
direction = (hud_cur_item_r != 0);
|
||||
else if (filtered_joypad_L & kJoypadL_R && (hud_cur_item_r == 0))
|
||||
direction = true;
|
||||
else
|
||||
return;
|
||||
|
||||
uint8 item = hud_cur_item;
|
||||
for (int i = 0; i < kHudItemCount; i++) {
|
||||
if (!direction)
|
||||
Hud_GotoPrevItem(&item, 1);
|
||||
else
|
||||
Hud_GotoNextItem(&item, 1);
|
||||
if (Hud_DoWeHaveThisItem(item) && (!(enhanced_features0 & kFeatures0_SwitchLRLimit) || Hud_GetItemPosition(item) <= 3)) {
|
||||
if (item != hud_cur_item) {
|
||||
hud_cur_item = item;
|
||||
sound_effect_2 = 32;
|
||||
Hud_UpdateEquippedItem();
|
||||
Hud_UpdateItemBox();
|
||||
flag_update_hud_in_nmi++;
|
||||
if (filtered_joypad_L & (0x20 | 0x10)) { // left/right shoulder
|
||||
int old_item = hud_cur_item;
|
||||
for (int i = 0; ; i++) {
|
||||
if (i >= kHudItemCount) {
|
||||
hud_cur_item = 0;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
if (filtered_joypad_L & 0x20)
|
||||
Hud_GotoPrevItem(&hud_cur_item, 1);
|
||||
else
|
||||
Hud_GotoNextItem(&hud_cur_item, 1);
|
||||
if (Hud_DoWeHaveThisItem(hud_cur_item))
|
||||
break;
|
||||
}
|
||||
if (hud_cur_item != old_item) {
|
||||
sound_effect_2 = 32;
|
||||
Hud_UpdateEquippedItem();
|
||||
Hud_UpdateItemBox();
|
||||
flag_update_hud_in_nmi++;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -56,7 +56,5 @@ void Hud_RestoreTorchBackground();
|
||||
void Hud_RebuildIndoor();
|
||||
void Hud_Rebuild();
|
||||
const uint16 *Hud_GetItemBoxPtr(int item);
|
||||
int GetCurrentItemButtonIndex();
|
||||
uint8 *GetCurrentItemButtonPtr(int i);
|
||||
|
||||
void Hud_HandleItemSwitchInputs();
|
||||
@@ -6,6 +6,12 @@
|
||||
#include "player.h"
|
||||
#include "sprite.h"
|
||||
#include "assets.h"
|
||||
#include "snes/ppu.h"
|
||||
|
||||
|
||||
struct ImageDataX2 g_image_data_x2;
|
||||
uint16 g_cgram_data_x2[256];
|
||||
|
||||
|
||||
// Allow this to be overwritten
|
||||
uint16 kGlovesColor[2] = {0x52f6, 0x376};
|
||||
@@ -325,7 +331,7 @@ static const int8 kGraphicsLoadSp6[20] = {
|
||||
static const uint8 kMirrorWarp_LoadNext_NmiLoad[15] = {0, 14, 15, 16, 17, 0, 0, 0, 0, 0, 0, 18, 19, 20, 0};
|
||||
|
||||
static const uint8 *GetCompSpritePtr(int i) {
|
||||
return kSprGfx(i).ptr;
|
||||
return kSprGfx + *(uint32 *)(kSprGfx + i * 4);
|
||||
}
|
||||
|
||||
void ApplyPaletteFilter_bounce() {
|
||||
@@ -420,7 +426,7 @@ void RecoverPegGFXFromMapping() {
|
||||
Dungeon_UpdatePegGFXBuffer(0x180, 0x0);
|
||||
else
|
||||
Dungeon_UpdatePegGFXBuffer(0x0, 0x180);
|
||||
}
|
||||
}
|
||||
|
||||
void LoadOverworldMapPalette() {
|
||||
memcpy(main_palette_buffer, &kOverworldMapPaletteData[overworld_screen_index & 0x40 ? 0x80 : 0], 256);
|
||||
@@ -824,6 +830,7 @@ void InitializeTilesets() { // 80e19b
|
||||
LoadSpriteGraphics(&g_zenv.vram[0x5400], sprite_gfx_subset_1, &g_ram[0x7e00]);
|
||||
LoadSpriteGraphics(&g_zenv.vram[0x5800], sprite_gfx_subset_2, &g_ram[0x8400]);
|
||||
LoadSpriteGraphics(&g_zenv.vram[0x5c00], sprite_gfx_subset_3, &g_ram[0x8a00]);
|
||||
CONVERT_SPRITE_TO_X2(0x5000, 256);
|
||||
|
||||
const uint8 *mt = kMainTilesets[main_tile_theme_index];
|
||||
const uint8 *at = kAuxTilesets[aux_tile_theme_index];
|
||||
@@ -841,6 +848,7 @@ void InitializeTilesets() { // 80e19b
|
||||
LoadBackgroundGraphics(&g_zenv.vram[0x3400], aux_bg_subset_2, 2, &g_ram[0x6c00]);
|
||||
LoadBackgroundGraphics(&g_zenv.vram[0x3800], aux_bg_subset_3, 1, &g_ram[0x7200]);
|
||||
LoadBackgroundGraphics(&g_zenv.vram[0x3c00], mt[7], 0, &g_ram[0x14000]);
|
||||
CONVERT_BG_TO_X2(0x2000, 512);
|
||||
}
|
||||
|
||||
void LoadDefaultGraphics() { // 80e2d0
|
||||
@@ -858,16 +866,22 @@ void LoadDefaultGraphics() { // 80e2d0
|
||||
*vram_ptr++ = src[0] | (src[0] | tmp[i]) << 8;
|
||||
}
|
||||
} while (--num);
|
||||
CONVERT_SPRITE_TO_X2(0x4000, 64);
|
||||
|
||||
// Load 2bpp graphics used for hud
|
||||
DecompAndUpload2bpp(&g_zenv.vram[0x7000], 0x6a);
|
||||
DecompAndUpload2bpp(&g_zenv.vram[0x7400], 0x6b);
|
||||
DecompAndUpload2bpp(&g_zenv.vram[0x7800], 0x69);
|
||||
|
||||
// Load the 2X hud icons
|
||||
if (kPpuXPixels)
|
||||
memcpy(g_zenv.ext_vram, g_image_data_x2.icons, sizeof(g_image_data_x2.icons));
|
||||
}
|
||||
|
||||
void Attract_LoadBG3GFX() { // 80e36d
|
||||
// load 2bpp gfx for attract images
|
||||
DecompAndUpload2bpp(&g_zenv.vram[0x7800], 0x67);
|
||||
CONVERT_HUD_TO_X2(0x7800, 8 * 16);
|
||||
}
|
||||
|
||||
void Graphics_LoadChrHalfSlot() { // 80e3fa
|
||||
@@ -877,9 +891,9 @@ void Graphics_LoadChrHalfSlot() { // 80e3fa
|
||||
|
||||
int8 sp6 = kGraphicsLoadSp6[k - 1];
|
||||
if (sp6 >= 0) {
|
||||
palette_sp6r_indoors = sp6;
|
||||
palette_sp6 = sp6;
|
||||
if (k == 1) {
|
||||
palette_sp6r_indoors = 10;
|
||||
palette_sp6 = 10;
|
||||
overworld_palette_aux_or_main = 0x200;
|
||||
Palette_Load_SpriteEnvironment();
|
||||
flag_update_cgram_in_nmi++;
|
||||
@@ -932,7 +946,9 @@ void Graphics_LoadChrHalfSlot() { // 80e3fa
|
||||
}
|
||||
|
||||
void TransferFontToVRAM() { // 80e556
|
||||
memcpy(&g_zenv.vram[0x7000], FindIndexInMemblk(kDialogueFont(0), 0).ptr, 0x800 * sizeof(uint16));
|
||||
memcpy(&g_zenv.vram[0x7000], kFontData, 0x800 * sizeof(uint16));
|
||||
if (kPpuUpsample2x2)
|
||||
memcpy(g_zenv.ext_vram, g_image_data_x2.font, sizeof(g_image_data_x2.font));
|
||||
}
|
||||
|
||||
void Do3To4High(uint16 *vram_ptr, const uint8 *decomp_addr) { // 80e5af
|
||||
@@ -986,22 +1002,16 @@ void LoadCommonSprites() { // 80e6b7
|
||||
LoadSpriteGraphics(&g_zenv.vram[0x4800], 94, &g_ram[0x14000]);
|
||||
LoadSpriteGraphics(&g_zenv.vram[0x4c00], 95, &g_ram[0x14000]);
|
||||
}
|
||||
CONVERT_SPRITE_TO_X2(0x4400, 64 * 3);
|
||||
}
|
||||
|
||||
int Decomp_spr(uint8 *dst, int gfx) { // 80e772
|
||||
if (gfx < 12)
|
||||
gfx = 12; // ensure it wont decode bad sheets.
|
||||
MemBlk blk = kSprGfx(gfx);
|
||||
const uint8 *sprite_data = GetCompSpritePtr(gfx);
|
||||
// If the size is not 0x600 then it's compressed
|
||||
if (gfx >= 103 || blk.size != 0x600)
|
||||
return Decompress(dst, blk.ptr);
|
||||
memcpy(dst, blk.ptr, 0x600);
|
||||
return 0x600;
|
||||
return Decompress(dst, GetCompSpritePtr(gfx));
|
||||
}
|
||||
|
||||
int Decomp_bg(uint8 *dst, int gfx) { // 80e78f
|
||||
return Decompress(dst, kBgGfx(gfx).ptr);
|
||||
const uint8 *p = kBgGfx + *(uint32 *)(kBgGfx + gfx * 4);
|
||||
return Decompress(dst, p);
|
||||
}
|
||||
|
||||
int Decompress(uint8 *dst, const uint8 *src) { // 80e79e
|
||||
@@ -1494,8 +1504,8 @@ void IrisSpotlight_ConfigureTable() { // 80f312
|
||||
if (main_module_index == 7 || main_module_index == 16) {
|
||||
if (!player_is_indoors)
|
||||
sound_effect_ambient = overworld_music[BYTE(overworld_screen_index)] >> 4;
|
||||
if (queued_music_control != 0xff)
|
||||
music_control = queued_music_control;
|
||||
if (buffer_for_playing_songs != 0xff)
|
||||
music_control = buffer_for_playing_songs;
|
||||
}
|
||||
main_module_index = saved_module_for_menu;
|
||||
if (main_module_index == 6)
|
||||
@@ -1656,7 +1666,7 @@ void Dungeon_UpdatePegGFXBuffer(int x, int y) { // 829773
|
||||
}
|
||||
|
||||
void Dungeon_HandleTranslucencyAndPalette() { // 82a1e9
|
||||
if (palette_swap_flag)
|
||||
if (overworld_palette_swap_flag)
|
||||
Palette_RevertTranslucencySwap();
|
||||
|
||||
CGWSEL_copy = 2;
|
||||
@@ -1690,9 +1700,9 @@ void Dungeon_HandleTranslucencyAndPalette() { // 82a1e9
|
||||
darkening_or_lightening_screen = 2;
|
||||
overworld_palette_aux_or_main = 0;
|
||||
Palette_Load_DungeonSet();
|
||||
Palette_Load_Sp0L();
|
||||
Palette_Load_Sp5L();
|
||||
Palette_Load_Sp6L();
|
||||
Palette_Load_SpritePal0Left();
|
||||
Palette_Load_SpriteAux1();
|
||||
Palette_Load_SpriteAux2();
|
||||
subsubmodule_index += 1;
|
||||
}
|
||||
|
||||
@@ -1704,12 +1714,12 @@ void Overworld_LoadAllPalettes() { // 82c5b2
|
||||
overworld_palette_aux1_bp2to4_hi = 3;
|
||||
overworld_palette_aux2_bp5to7_hi = 3;
|
||||
overworld_palette_aux3_bp7_lo = 0;
|
||||
palette_sp6r_indoors = 5;
|
||||
palette_sp0l = 11;
|
||||
palette_swap_flag = 0;
|
||||
palette_sp6 = 5;
|
||||
overworld_palette_sp0 = 11;
|
||||
overworld_palette_swap_flag = 0;
|
||||
overworld_palette_aux_or_main = 0;
|
||||
Palette_BgAndFixedColor_Black();
|
||||
Palette_Load_Sp0L();
|
||||
Palette_Load_SpritePal0Left();
|
||||
Palette_Load_SpriteMain();
|
||||
Palette_Load_OWBGMain();
|
||||
Palette_Load_OWBG1();
|
||||
@@ -1725,10 +1735,10 @@ void Overworld_LoadAllPalettes() { // 82c5b2
|
||||
void Dungeon_LoadPalettes() { // 82c630
|
||||
overworld_palette_aux_or_main = 0;
|
||||
Palette_BgAndFixedColor_Black();
|
||||
Palette_Load_Sp0L();
|
||||
Palette_Load_SpritePal0Left();
|
||||
Palette_Load_SpriteMain();
|
||||
Palette_Load_Sp5L();
|
||||
Palette_Load_Sp6L();
|
||||
Palette_Load_SpriteAux1();
|
||||
Palette_Load_SpriteAux2();
|
||||
Palette_Load_Sword();
|
||||
Palette_Load_Shield();
|
||||
Palette_Load_SpriteEnvironment();
|
||||
@@ -1739,7 +1749,7 @@ void Dungeon_LoadPalettes() { // 82c630
|
||||
}
|
||||
|
||||
void Overworld_LoadPalettesInner() { // 82c65f
|
||||
overworld_pal_unk1 = palette_main_indoors;
|
||||
overworld_pal_unk1 = dung_hdr_palette_1;
|
||||
overworld_pal_unk2 = overworld_palette_aux3_bp7_lo;
|
||||
overworld_pal_unk3 = byte_7E0AB7;
|
||||
darkening_or_lightening_screen = 2;
|
||||
@@ -1760,13 +1770,13 @@ void Overworld_LoadAreaPalettesEx(uint8 x) { // 82c6ad
|
||||
overworld_palette_aux_or_main &= 0xff;
|
||||
Palette_Load_SpriteMain();
|
||||
Palette_Load_SpriteEnvironment();
|
||||
Palette_Load_Sp5L();
|
||||
Palette_Load_Sp6L();
|
||||
Palette_Load_SpriteAux1();
|
||||
Palette_Load_SpriteAux2();
|
||||
Palette_Load_Sword();
|
||||
Palette_Load_Shield();
|
||||
Palette_Load_LinkArmorAndGloves();
|
||||
palette_sp0l = (savegame_is_darkworld & 0x40) ? 3 : 1;
|
||||
Palette_Load_Sp0L();
|
||||
overworld_palette_sp0 = (savegame_is_darkworld & 0x40) ? 3 : 1;
|
||||
Palette_Load_SpritePal0Left();
|
||||
Palette_Load_HUD();
|
||||
Palette_Load_OWBGMain();
|
||||
}
|
||||
@@ -1807,14 +1817,14 @@ void Overworld_LoadPalettes(uint8 bg, uint8 spr) { // 8ed5a8
|
||||
|
||||
d = kOwSprPalInfo + spr * 2;
|
||||
if (d[0] >= 0)
|
||||
palette_sp5l = d[0];
|
||||
sprite_aux1_palette = d[0];
|
||||
if (d[1] >= 0)
|
||||
palette_sp6l = d[1];
|
||||
sprite_aux2_palette = d[1];
|
||||
Palette_Load_OWBG1();
|
||||
Palette_Load_OWBG2();
|
||||
Palette_Load_OWBG3();
|
||||
Palette_Load_Sp5L();
|
||||
Palette_Load_Sp6L();
|
||||
Palette_Load_SpriteAux1();
|
||||
Palette_Load_SpriteAux2();
|
||||
}
|
||||
|
||||
void Palette_BgAndFixedColor_Black() { // 8ed5f4
|
||||
@@ -1859,7 +1869,7 @@ void Palette_AssertTranslucencySwap() { // 8ed657
|
||||
}
|
||||
|
||||
void Palette_SetTranslucencySwap(bool v) { // 8ed65c
|
||||
palette_swap_flag = v;
|
||||
overworld_palette_swap_flag = v;
|
||||
uint16 a, b;
|
||||
for (int i = 0; i < 8; i++) {
|
||||
a = aux_palette_buffer[i + 0x80];
|
||||
@@ -1910,11 +1920,6 @@ void LoadGearPalettes(uint8 sword, uint8 shield, uint8 armor) { // 8ed6e8
|
||||
flag_update_cgram_in_nmi++;
|
||||
}
|
||||
|
||||
void LoadGearPalette(int dst, const uint16 *src, int n) { // 8ed741
|
||||
memcpy(&aux_palette_buffer[dst >> 1], src, sizeof(uint16) * n);
|
||||
memcpy(&main_palette_buffer[dst >> 1], src, sizeof(uint16) * n);
|
||||
}
|
||||
|
||||
void Filter_Majorly_Whiten_Bg() { // 8ed757
|
||||
for (int i = 32; i < 128; i++)
|
||||
main_palette_buffer[i] = Filter_Majorly_Whiten_Color(aux_palette_buffer[i]);
|
||||
@@ -1922,12 +1927,11 @@ void Filter_Majorly_Whiten_Bg() { // 8ed757
|
||||
}
|
||||
|
||||
uint16 Filter_Majorly_Whiten_Color(uint16 c) { // 8ed7fe
|
||||
int amt = (enhanced_features0 & kFeatures0_DimFlashes) ? 3 : 14;
|
||||
int r = (c & 0x1f) + amt;
|
||||
int g = (c & 0x3e0) + (amt << 5);
|
||||
int b = (c & 0x7c00) + (amt << 10);
|
||||
int r = (c & 0x1f) + 14;
|
||||
if (r > 0x1f) r = 0x1f;
|
||||
int g = (c & 0x3e0) + 0x1c0;
|
||||
if (g > 0x3e0) g = 0x3e0;
|
||||
int b = (c & 0x7c00) + 0x3800;
|
||||
if (b > 0x7c00) b = 0x7c00;
|
||||
return r | g | b;
|
||||
}
|
||||
@@ -1967,64 +1971,35 @@ void Palette_Restore_BG_And_HUD() { // 8ed8fb
|
||||
Palette_Restore_Coldata();
|
||||
}
|
||||
|
||||
/* Summary of sprite palette usage
|
||||
0l: kPalette_SpriteAux3[palette_sp0l]
|
||||
0r: kPalette_MiscSprite[7 / 9] or kPalette_DungBgMain[(palette_main_indoors >> 1) * 90]
|
||||
1 : common sprites
|
||||
2 : -"-
|
||||
3 : -"-
|
||||
4 : -"-
|
||||
5l: palette_sp5l
|
||||
5r: link sword/shield
|
||||
6l: palette_sp6l
|
||||
6r: kPalette_MiscSprite[6 / 8] or kPalette_MiscSprite[palette_sp6r_indoors]
|
||||
7 : link armor and gloves
|
||||
*/
|
||||
|
||||
enum {
|
||||
kPal_sp0l = 0x102,
|
||||
kPal_sp0r = 0x112,
|
||||
kPal_sp1to4 = 0x122, // This is used for 64 colors, colors switched if in darkworld mode
|
||||
kPal_sp5l = 0x1a2,
|
||||
kPal_Sword = 0x1b2,
|
||||
kPal_Shield = 0x1b8,
|
||||
kPal_sp6l = 0x1c2,
|
||||
kPal_sp6r = 0x1d2,
|
||||
kPal_sp7l = 0x1e2,
|
||||
kPal_sp7r = 0x1f2,
|
||||
kPal_ArmorGloves = 0x1e2,
|
||||
kPal_PalaceMap = 0x182,
|
||||
};
|
||||
|
||||
void Palette_Load_Sp0L() { // 9bec77
|
||||
const uint16 *src = kPalette_SpriteAux3 + palette_sp0l * 7;
|
||||
Palette_LoadSingle(src, palette_swap_flag ? kPal_sp7l : kPal_sp0l, 6);
|
||||
void Palette_Load_SpritePal0Left() { // 9bec77
|
||||
const uint16 *src = kPalette_SpriteAux3 + overworld_palette_sp0 * 7;
|
||||
Palette_LoadSingle(src, overworld_palette_swap_flag ? 0x1e2 : 0x102, 6);
|
||||
}
|
||||
|
||||
void Palette_Load_SpriteMain() { // 9bec9e
|
||||
const uint16 *src = kPalette_MainSpr + (overworld_screen_index & 0x40 ? 60 : 0);
|
||||
Palette_LoadMultiple(src, kPal_sp1to4, 14, 3);
|
||||
Palette_LoadMultiple(src, 0x122, 14, 3);
|
||||
}
|
||||
|
||||
void Palette_Load_Sp5L() { // 9becc5
|
||||
const uint16 *src = kPalette_SpriteAux1 + (palette_sp5l) * 7;
|
||||
Palette_LoadSingle(src, kPal_sp5l, 6);
|
||||
void Palette_Load_SpriteAux1() { // 9becc5
|
||||
const uint16 *src = kPalette_SpriteAux1 + (sprite_aux1_palette) * 7;
|
||||
Palette_LoadSingle(src, 0x1A2, 6);
|
||||
}
|
||||
|
||||
void Palette_Load_Sp6L() { // 9bece4
|
||||
const uint16 *src = kPalette_SpriteAux1 + (palette_sp6l) * 7;
|
||||
Palette_LoadSingle(src, kPal_sp6l, 6);
|
||||
void Palette_Load_SpriteAux2() { // 9bece4
|
||||
const uint16 *src = kPalette_SpriteAux1 + (sprite_aux2_palette) * 7;
|
||||
Palette_LoadSingle(src, 0x1C2, 6);
|
||||
}
|
||||
|
||||
void Palette_Load_Sword() { // 9bed03
|
||||
const uint16 *src = kPalette_Sword + ((int8)link_sword_type > 0 ? link_sword_type - 1 : 0) * 3; // wtf: zelda reads offset 0xff
|
||||
Palette_LoadMultiple_Arbitrary(src, kPal_Sword, 2);
|
||||
Palette_LoadMultiple_Arbitrary(src, 0x1b2, 2);
|
||||
flag_update_cgram_in_nmi += 1;
|
||||
}
|
||||
|
||||
void Palette_Load_Shield() { // 9bed29
|
||||
const uint16 *src = kPalette_Shield + (link_shield_type ? link_shield_type - 1 : 0) * 4;
|
||||
Palette_LoadMultiple_Arbitrary(src, kPal_Shield, 3);
|
||||
Palette_LoadMultiple_Arbitrary(src, 0x1b8, 3);
|
||||
flag_update_cgram_in_nmi += 1;
|
||||
}
|
||||
|
||||
@@ -2035,28 +2010,26 @@ void Palette_Load_SpriteEnvironment() { // 9bed6e
|
||||
Palette_MiscSprite_Outdoors();
|
||||
}
|
||||
|
||||
// avoid renaming in assets.dat
|
||||
#define kPalette_MiscSprite kPalette_MiscSprite_Indoors
|
||||
|
||||
void Palette_Load_SpriteEnvironment_Dungeon() { // 9bed72
|
||||
const uint16 *src = kPalette_MiscSprite + palette_sp6r_indoors * 7;
|
||||
Palette_LoadSingle(src, kPal_sp6r, 6);
|
||||
const uint16 *src = kPalette_MiscSprite_Indoors + palette_sp6 * 7;
|
||||
Palette_LoadSingle(src, 0x1d2, 6);
|
||||
}
|
||||
|
||||
void Palette_MiscSprite_Outdoors() { // 9bed91
|
||||
int t = (overworld_screen_index & 0x40) ? 9 : 7;
|
||||
const uint16 *src = kPalette_MiscSprite + t * 7;
|
||||
Palette_LoadSingle(src, palette_swap_flag ? kPal_sp7r : kPal_sp0r, 6);
|
||||
Palette_LoadSingle(src - 7, kPal_sp6r, 6);
|
||||
const uint16 *src = kPalette_MiscSprite_Indoors + t * 7;
|
||||
Palette_LoadSingle(src, overworld_palette_swap_flag ? 0x1f2 : 0x112, 6);
|
||||
src = kPalette_MiscSprite_Indoors + (t - 1) * 7;
|
||||
Palette_LoadSingle(src, 0x1d2, 6);
|
||||
}
|
||||
|
||||
void Palette_Load_DungeonMapSprite() { // 9beddd
|
||||
Palette_LoadMultiple(kPalette_PalaceMapSpr, kPal_PalaceMap, 6, 2);
|
||||
Palette_LoadMultiple(kPalette_PalaceMapSpr, 0x182, 6, 2);
|
||||
}
|
||||
|
||||
void Palette_Load_LinkArmorAndGloves() { // 9bedf9
|
||||
const uint16 *src = kPalette_ArmorAndGloves + link_armor * 15;
|
||||
Palette_LoadMultiple_Arbitrary(src, kPal_ArmorGloves, 14);
|
||||
Palette_LoadMultiple_Arbitrary(src, 0x1e2, 14);
|
||||
Palette_UpdateGlovesColor();
|
||||
}
|
||||
|
||||
@@ -2073,12 +2046,13 @@ void Palette_Load_DungeonMapBG() { // 9bee3a
|
||||
void Palette_Load_HUD() { // 9bee52
|
||||
const uint16 *src = kHudPalData + hud_palette * 32;
|
||||
Palette_LoadMultiple(src, 0x0, 15, 1);
|
||||
LoadHudPaletteX2();
|
||||
}
|
||||
|
||||
void Palette_Load_DungeonSet() { // 9bee74
|
||||
const uint16 *src = kPalette_DungBgMain + (palette_main_indoors >> 1) * 90;
|
||||
const uint16 *src = kPalette_DungBgMain + (dung_hdr_palette_1 >> 1) * 90;
|
||||
Palette_LoadMultiple(src, 0x42, 14, 5);
|
||||
Palette_LoadSingle(src, palette_swap_flag ? kPal_sp7r : kPal_sp0r, 6);
|
||||
Palette_LoadSingle(src, overworld_palette_swap_flag ? 0x1f2 : 0x112, 6);
|
||||
}
|
||||
|
||||
void Palette_Load_OWBG3() { // 9beea8
|
||||
@@ -2180,3 +2154,70 @@ void HandleScreenFlash() { // 9de9b6
|
||||
flag_update_cgram_in_nmi++;
|
||||
}
|
||||
|
||||
|
||||
void LoadImageFilesX2() {
|
||||
FILE *f = fopen("tables/x2_icons.bin", "rb");
|
||||
if (f) {
|
||||
if (fread(&g_image_data_x2, 1, sizeof(g_image_data_x2), f) != sizeof(g_image_data_x2))
|
||||
fprintf(stderr, "Unable to read all x2 icons\n");
|
||||
fclose(f);
|
||||
} else {
|
||||
printf("Error reading bitmap!\n");
|
||||
}
|
||||
f = fopen("tables/x2_icons.pal", "rb");
|
||||
if (f) {
|
||||
fread(g_cgram_data_x2, 1, sizeof(g_cgram_data_x2), f);
|
||||
fclose(f);
|
||||
} else {
|
||||
printf("Error reading palette!\n");
|
||||
}
|
||||
LoadHudPaletteX2();
|
||||
LoadGraphicsExtended();
|
||||
}
|
||||
|
||||
void LoadHudPaletteX2() {
|
||||
struct Ppu *ppu = g_zenv.ppu;
|
||||
ppu->cgramDirty = true;
|
||||
memcpy(ppu->cgram + 256, g_cgram_data_x2 + hud_palette * 128, 128 * 2);
|
||||
}
|
||||
|
||||
void LoadGraphicsExtended() {
|
||||
// todo: fix so snapshot restore works on other places than in-game
|
||||
struct Ppu *ppu = g_zenv.ppu;
|
||||
memcpy(ppu->extendedVram, g_image_data_x2.icons, sizeof(g_image_data_x2.icons));
|
||||
}
|
||||
|
||||
|
||||
void Convert2bppToX2(const uint16 *src, uint32 dst_addr, size_t count) {
|
||||
#define DO_PIXEL(i) r += (uint64)((bits >> (7 - i)) & 1 | (bits >> (14 - i)) & 2) << (i * 8)
|
||||
uint16 *dst = g_zenv.ext_vram + dst_addr;
|
||||
do {
|
||||
for (size_t j = 0; j < 8; j++) {
|
||||
uint32 bits = *src++;
|
||||
uint64 r = 0;
|
||||
DO_PIXEL(0); DO_PIXEL(1); DO_PIXEL(2); DO_PIXEL(3);
|
||||
DO_PIXEL(4); DO_PIXEL(5); DO_PIXEL(6); DO_PIXEL(7);
|
||||
*(uint64 *)&dst[4] = *(uint64 *)&dst[0] = r + (r << 4);
|
||||
dst += 8;
|
||||
}
|
||||
} while (--count);
|
||||
#undef DO_PIXEL
|
||||
}
|
||||
|
||||
void Convert4bppToX2(const uint16 *src, uint32 dst_addr, size_t count) {
|
||||
#define DO_PIXEL(i) r += (uint64)((bits >> (7 - i)) & 1 | (bits >> (14 - i)) & 2 | (bits >> (21 - i)) & 4 | (bits >> (28 - i)) & 8) << (i * 8)
|
||||
uint16 *dst = g_zenv.ext_vram + dst_addr;
|
||||
do {
|
||||
for (size_t j = 0; j < 8; j++) {
|
||||
uint32 bits = src[0] | src[8] << 16;
|
||||
uint64 r = 0;
|
||||
DO_PIXEL(0); DO_PIXEL(1); DO_PIXEL(2); DO_PIXEL(3);
|
||||
DO_PIXEL(4); DO_PIXEL(5); DO_PIXEL(6); DO_PIXEL(7);
|
||||
// r = j&1 ? 0x23456789abcdef1 : 0x123456789abcdef;
|
||||
*(uint64 *)&dst[4] = *(uint64 *)&dst[0] = r + (r << 4);
|
||||
dst += 8, src += 1;
|
||||
}
|
||||
} while (src += 8, --count);
|
||||
#undef DO_PIXEL
|
||||
}
|
||||
|
||||
@@ -126,16 +126,15 @@ void LoadActualGearPalettes();
|
||||
void Palette_ElectroThemedGear();
|
||||
void LoadGearPalettes_bunny();
|
||||
void LoadGearPalettes(uint8 sword, uint8 shield, uint8 armor);
|
||||
void LoadGearPalette(int dst, const uint16 *src, int n);
|
||||
void Filter_Majorly_Whiten_Bg();
|
||||
uint16 Filter_Majorly_Whiten_Color(uint16 c);
|
||||
void Palette_Restore_BG_From_Flash();
|
||||
void Palette_Restore_Coldata();
|
||||
void Palette_Restore_BG_And_HUD();
|
||||
void Palette_Load_Sp0L();
|
||||
void Palette_Load_SpritePal0Left();
|
||||
void Palette_Load_SpriteMain();
|
||||
void Palette_Load_Sp5L();
|
||||
void Palette_Load_Sp6L();
|
||||
void Palette_Load_SpriteAux1();
|
||||
void Palette_Load_SpriteAux2();
|
||||
void Palette_Load_Sword();
|
||||
void Palette_Load_Shield();
|
||||
void Palette_Load_SpriteEnvironment();
|
||||
@@ -24,27 +24,24 @@
|
||||
#include "config.h"
|
||||
#include "assets.h"
|
||||
#include "load_gfx.h"
|
||||
#include "util.h"
|
||||
#include "audio.h"
|
||||
|
||||
static bool g_run_without_emu = 0;
|
||||
|
||||
// Forwards
|
||||
static bool LoadRom(const char *filename);
|
||||
static void LoadLinkGraphics();
|
||||
static void RenderNumber(uint8 *dst, size_t pitch, int n, bool big);
|
||||
static void PlayAudio(SDL_AudioDeviceID device, int channels, int16 *audioBuffer);
|
||||
static void RenderScreen(SDL_Window *window, SDL_Renderer *renderer, SDL_Texture *texture, bool fullscreen);
|
||||
static void HandleInput(int keyCode, int modCode, bool pressed);
|
||||
static void HandleCommand(uint32 j, bool pressed);
|
||||
static int RemapSdlButton(int button);
|
||||
static void HandleGamepadInput(int button, bool pressed);
|
||||
static void HandleGamepadAxisInput(int gamepad_id, int axis, int value);
|
||||
static void OpenOneGamepad(int i);
|
||||
static void HandleVolumeAdjustment(int volume_adjustment);
|
||||
static void LoadAssets();
|
||||
static void SwitchDirectory();
|
||||
|
||||
enum {
|
||||
kDefaultFullscreen = 0,
|
||||
kDefaultWindowScale = 2,
|
||||
kMaxWindowScale = 10,
|
||||
kDefaultFreq = 44100,
|
||||
kDefaultChannels = 2,
|
||||
@@ -52,9 +49,10 @@ enum {
|
||||
};
|
||||
|
||||
static const char kWindowTitle[] = "The Legend of Zelda: A Link to the Past";
|
||||
|
||||
static uint32 g_win_flags = SDL_WINDOW_RESIZABLE;
|
||||
static SDL_Window *g_window;
|
||||
|
||||
static SDL_Renderer *g_renderer;
|
||||
static uint8 g_paused, g_turbo, g_replay_turbo = true, g_cursor = true;
|
||||
static uint8 g_current_window_scale;
|
||||
static uint8 g_gamepad_buttons;
|
||||
@@ -62,23 +60,26 @@ static int g_input1_state;
|
||||
static bool g_display_perf;
|
||||
static int g_curr_fps;
|
||||
static int g_ppu_render_flags = 0;
|
||||
static bool g_run_without_emu = 0;
|
||||
static int g_snes_width, g_snes_height;
|
||||
static int g_sdl_audio_mixer_volume = SDL_MIX_MAXVOLUME;
|
||||
static struct RendererFuncs g_renderer_funcs;
|
||||
static uint32 g_gamepad_modifiers;
|
||||
static uint16 g_gamepad_last_cmd[kGamepadBtn_Count];
|
||||
|
||||
void NORETURN Die(const char *error) {
|
||||
#if defined(NDEBUG) && defined(_WIN32)
|
||||
SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, kWindowTitle, error, NULL);
|
||||
#endif
|
||||
fprintf(stderr, "Error: %s\n", error);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
void SetButtonState(int button, bool pressed) {
|
||||
// set key in constroller
|
||||
if (pressed) {
|
||||
g_input1_state |= 1 << button;
|
||||
} else {
|
||||
g_input1_state &= ~(1 << button);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void ChangeWindowScale(int scale_step) {
|
||||
if ((SDL_GetWindowFlags(g_window) & (SDL_WINDOW_FULLSCREEN_DESKTOP | SDL_WINDOW_FULLSCREEN | SDL_WINDOW_MINIMIZED | SDL_WINDOW_MAXIMIZED)) != 0)
|
||||
return;
|
||||
int screen = SDL_GetWindowDisplayIndex(g_window);
|
||||
if (screen < 0) screen = 0;
|
||||
int max_scale = kMaxWindowScale;
|
||||
@@ -93,14 +94,14 @@ void ChangeWindowScale(int scale_step) {
|
||||
bt = 31;
|
||||
}
|
||||
// Allow a scale level slightly above the max that fits on screen
|
||||
int mw = (bounds.w - bl - br + g_snes_width / 4) / g_snes_width;
|
||||
int mh = (bounds.h - bt - bb + g_snes_height / 4) / g_snes_height;
|
||||
int mw = (bounds.w - bl - br + (g_snes_width / kDefaultWindowScale) / 4) / (g_snes_width / kDefaultWindowScale);
|
||||
int mh = (bounds.h - bt - bb + (g_snes_height / kDefaultWindowScale) / 4) / (g_snes_height / kDefaultWindowScale);
|
||||
max_scale = IntMin(mw, mh);
|
||||
}
|
||||
int new_scale = IntMax(IntMin(g_current_window_scale + scale_step, max_scale), 1);
|
||||
g_current_window_scale = new_scale;
|
||||
int w = new_scale * g_snes_width;
|
||||
int h = new_scale * g_snes_height;
|
||||
int w = new_scale * (g_snes_width / kDefaultWindowScale);
|
||||
int h = new_scale * (g_snes_height / kDefaultWindowScale);
|
||||
|
||||
//SDL_RenderSetLogicalSize(g_renderer, w, h);
|
||||
SDL_SetWindowSize(g_window, w, h);
|
||||
@@ -116,47 +117,19 @@ void ChangeWindowScale(int scale_step) {
|
||||
}
|
||||
}
|
||||
|
||||
#define RESIZE_BORDER 20
|
||||
static SDL_HitTestResult HitTestCallback(SDL_Window *win, const SDL_Point *pt, void *data) {
|
||||
static SDL_HitTestResult HitTestCallback(SDL_Window *win, const SDL_Point *area, void *data) {
|
||||
uint32 flags = SDL_GetWindowFlags(win);
|
||||
if ((flags & SDL_WINDOW_FULLSCREEN_DESKTOP) != 0 || (flags & SDL_WINDOW_FULLSCREEN) != 0)
|
||||
return SDL_HITTEST_NORMAL;
|
||||
|
||||
if ((SDL_GetModState() & KMOD_CTRL) != 0)
|
||||
return SDL_HITTEST_DRAGGABLE;
|
||||
|
||||
int w, h;
|
||||
SDL_GetWindowSize(win, &w, &h);
|
||||
|
||||
if (pt->y < RESIZE_BORDER) {
|
||||
return (pt->x < RESIZE_BORDER) ? SDL_HITTEST_RESIZE_TOPLEFT :
|
||||
(pt->x >= w - RESIZE_BORDER) ? SDL_HITTEST_RESIZE_TOPRIGHT : SDL_HITTEST_RESIZE_TOP;
|
||||
} else if (pt->y >= h - RESIZE_BORDER) {
|
||||
return (pt->x < RESIZE_BORDER) ? SDL_HITTEST_RESIZE_BOTTOMLEFT :
|
||||
(pt->x >= w - RESIZE_BORDER) ? SDL_HITTEST_RESIZE_BOTTOMRIGHT : SDL_HITTEST_RESIZE_BOTTOM;
|
||||
} else {
|
||||
if (pt->x < RESIZE_BORDER) {
|
||||
return SDL_HITTEST_RESIZE_LEFT;
|
||||
} else if (pt->x >= w - RESIZE_BORDER) {
|
||||
return SDL_HITTEST_RESIZE_RIGHT;
|
||||
}
|
||||
}
|
||||
return SDL_HITTEST_NORMAL;
|
||||
return ((flags & SDL_WINDOW_FULLSCREEN_DESKTOP) == 0 || (flags & SDL_WINDOW_FULLSCREEN) == 0) &&
|
||||
(SDL_GetModState() & KMOD_CTRL) != 0 ? SDL_HITTEST_DRAGGABLE : SDL_HITTEST_NORMAL;
|
||||
}
|
||||
|
||||
static void DrawPpuFrameWithPerf() {
|
||||
int render_scale = PpuGetCurrentRenderScale(g_zenv.ppu, g_ppu_render_flags);
|
||||
uint8 *pixel_buffer = 0;
|
||||
int pitch = 0;
|
||||
|
||||
g_renderer_funcs.BeginDraw(g_snes_width * render_scale,
|
||||
g_snes_height * render_scale,
|
||||
&pixel_buffer, &pitch);
|
||||
static bool RenderScreenWithPerf(uint8 *pixel_buffer, size_t pitch, uint32 render_flags) {
|
||||
bool rv;
|
||||
if (g_display_perf || g_config.display_perf_title) {
|
||||
static float history[64], average;
|
||||
static int history_pos;
|
||||
uint64 before = SDL_GetPerformanceCounter();
|
||||
ZeldaDrawPpuFrame(pixel_buffer, pitch, g_ppu_render_flags);
|
||||
rv = ZeldaDrawPpuFrame(pixel_buffer, pitch, render_flags);
|
||||
uint64 after = SDL_GetPerformanceCounter();
|
||||
float v = (double)SDL_GetPerformanceFrequency() / (after - before);
|
||||
average += v - history[history_pos];
|
||||
@@ -164,11 +137,35 @@ static void DrawPpuFrameWithPerf() {
|
||||
history_pos = (history_pos + 1) & 63;
|
||||
g_curr_fps = average * (1.0f / 64);
|
||||
} else {
|
||||
ZeldaDrawPpuFrame(pixel_buffer, pitch, g_ppu_render_flags);
|
||||
rv = ZeldaDrawPpuFrame(pixel_buffer, pitch, render_flags);
|
||||
}
|
||||
return rv;
|
||||
}
|
||||
|
||||
// Go some steps up and find zelda3.ini
|
||||
static void SwitchDirectory() {
|
||||
char buf[4096];
|
||||
if (!getcwd(buf, sizeof(buf) - 32))
|
||||
return;
|
||||
size_t pos = strlen(buf);
|
||||
|
||||
for (int step = 0; pos != 0 && step < 3; step++) {
|
||||
memcpy(buf + pos, "/zelda3.ini", 12);
|
||||
FILE *f = fopen(buf, "rb");
|
||||
if (f) {
|
||||
fclose(f);
|
||||
buf[pos] = 0;
|
||||
if (step != 0) {
|
||||
printf("Found zelda3.ini in %s\n", buf);
|
||||
int err = chdir(buf);
|
||||
(void)err;
|
||||
}
|
||||
return;
|
||||
}
|
||||
pos--;
|
||||
while (pos != 0 && buf[pos] != '/' && buf[pos] != '\\')
|
||||
pos--;
|
||||
}
|
||||
if (g_display_perf)
|
||||
RenderNumber(pixel_buffer + pitch * render_scale, pitch, g_curr_fps, render_scale == 4);
|
||||
g_renderer_funcs.EndDraw();
|
||||
}
|
||||
|
||||
static SDL_mutex *g_audio_mutex;
|
||||
@@ -200,100 +197,22 @@ static void SDLCALL AudioCallback(void *userdata, Uint8 *stream, int len) {
|
||||
SDL_UnlockMutex(g_audio_mutex);
|
||||
}
|
||||
|
||||
// State for sdl renderer
|
||||
static SDL_Renderer *g_renderer;
|
||||
static SDL_Texture *g_texture;
|
||||
static SDL_Rect g_sdl_renderer_rect;
|
||||
|
||||
static bool SdlRenderer_Init(SDL_Window *window) {
|
||||
|
||||
if (g_config.shader)
|
||||
fprintf(stderr, "Warning: Shaders are supported only with the OpenGL backend\n");
|
||||
|
||||
SDL_Renderer *renderer = SDL_CreateRenderer(g_window, -1,
|
||||
g_config.output_method == kOutputMethod_SDLSoftware ? SDL_RENDERER_SOFTWARE :
|
||||
SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC);
|
||||
if (renderer == NULL) {
|
||||
printf("Failed to create renderer: %s\n", SDL_GetError());
|
||||
return false;
|
||||
}
|
||||
SDL_RendererInfo renderer_info;
|
||||
SDL_GetRendererInfo(renderer, &renderer_info);
|
||||
if (kDebugFlag) {
|
||||
printf("Supported texture formats:");
|
||||
for (int i = 0; i < renderer_info.num_texture_formats; i++)
|
||||
printf(" %s", SDL_GetPixelFormatName(renderer_info.texture_formats[i]));
|
||||
printf("\n");
|
||||
}
|
||||
g_renderer = renderer;
|
||||
if (!g_config.ignore_aspect_ratio)
|
||||
SDL_RenderSetLogicalSize(renderer, g_snes_width, g_snes_height);
|
||||
if (g_config.linear_filtering)
|
||||
SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "best");
|
||||
|
||||
int tex_mult = (g_ppu_render_flags & kPpuRenderFlags_4x4Mode7) ? 4 : 1;
|
||||
g_texture = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_ARGB8888, SDL_TEXTUREACCESS_STREAMING,
|
||||
g_snes_width * tex_mult, g_snes_height * tex_mult);
|
||||
if (g_texture == NULL) {
|
||||
printf("Failed to create texture: %s\n", SDL_GetError());
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static void SdlRenderer_Destroy() {
|
||||
SDL_DestroyTexture(g_texture);
|
||||
SDL_DestroyRenderer(g_renderer);
|
||||
}
|
||||
|
||||
static void SdlRenderer_BeginDraw(int width, int height, uint8 **pixels, int *pitch) {
|
||||
g_sdl_renderer_rect.w = width;
|
||||
g_sdl_renderer_rect.h = height;
|
||||
if (SDL_LockTexture(g_texture, &g_sdl_renderer_rect, (void **)pixels, pitch) != 0) {
|
||||
printf("Failed to lock texture: %s\n", SDL_GetError());
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
static void SdlRenderer_EndDraw() {
|
||||
|
||||
// uint64 before = SDL_GetPerformanceCounter();
|
||||
SDL_UnlockTexture(g_texture);
|
||||
// uint64 after = SDL_GetPerformanceCounter();
|
||||
// float v = (double)(after - before) / SDL_GetPerformanceFrequency();
|
||||
// printf("%f ms\n", v * 1000);
|
||||
SDL_RenderClear(g_renderer);
|
||||
SDL_RenderCopy(g_renderer, g_texture, &g_sdl_renderer_rect, NULL);
|
||||
SDL_RenderPresent(g_renderer); // vsyncs to 60 FPS?
|
||||
}
|
||||
|
||||
static const struct RendererFuncs kSdlRendererFuncs = {
|
||||
&SdlRenderer_Init,
|
||||
&SdlRenderer_Destroy,
|
||||
&SdlRenderer_BeginDraw,
|
||||
&SdlRenderer_EndDraw,
|
||||
};
|
||||
|
||||
void OpenGLRenderer_Create(struct RendererFuncs *funcs, bool use_opengl_es);
|
||||
|
||||
#undef main
|
||||
int main(int argc, char** argv) {
|
||||
argc--, argv++;
|
||||
const char *config_file = NULL;
|
||||
if (argc >= 2 && strcmp(argv[0], "--config") == 0) {
|
||||
config_file = argv[1];
|
||||
argc -= 2, argv += 2;
|
||||
} else {
|
||||
SwitchDirectory();
|
||||
}
|
||||
ParseConfigFile(config_file);
|
||||
SwitchDirectory();
|
||||
ParseConfigFile();
|
||||
AfterConfigParse();
|
||||
LoadAssets();
|
||||
LoadLinkGraphics();
|
||||
|
||||
ZeldaInitialize();
|
||||
LoadImageFilesX2();
|
||||
|
||||
g_zenv.ppu->extraLeftRight = UintMin(g_config.extended_aspect_ratio, kPpuExtraLeftRight);
|
||||
g_snes_width = (g_config.extended_aspect_ratio * 2 + 256);
|
||||
g_snes_height = (g_config.extend_y ? 240 : 224);
|
||||
g_snes_width = 2 * (g_config.extended_aspect_ratio * 2 + 256);
|
||||
g_snes_height = (g_config.extend_y ? 240 : 224) * 2;
|
||||
|
||||
|
||||
// Delay actually setting those features in ram until any snapshots finish playing.
|
||||
@@ -303,8 +222,7 @@ int main(int argc, char** argv) {
|
||||
g_config.enhanced_mode7 * kPpuRenderFlags_4x4Mode7 |
|
||||
g_config.extend_y * kPpuRenderFlags_Height240 |
|
||||
g_config.no_sprite_limits * kPpuRenderFlags_NoSpriteLimits;
|
||||
ZeldaEnableMsu(g_config.enable_msu);
|
||||
ZeldaSetLanguage(g_config.language);
|
||||
msu_enabled = g_config.enable_msu;
|
||||
|
||||
if (g_config.fullscreen == 1)
|
||||
g_win_flags ^= SDL_WINDOW_FULLSCREEN_DESKTOP;
|
||||
@@ -333,16 +251,8 @@ int main(int argc, char** argv) {
|
||||
}
|
||||
|
||||
bool custom_size = g_config.window_width != 0 && g_config.window_height != 0;
|
||||
int window_width = custom_size ? g_config.window_width : g_current_window_scale * g_snes_width;
|
||||
int window_height = custom_size ? g_config.window_height : g_current_window_scale * g_snes_height;
|
||||
|
||||
if (g_config.output_method == kOutputMethod_OpenGL ||
|
||||
g_config.output_method == kOutputMethod_OpenGL_ES) {
|
||||
g_win_flags |= SDL_WINDOW_OPENGL;
|
||||
OpenGLRenderer_Create(&g_renderer_funcs, (g_config.output_method == kOutputMethod_OpenGL_ES));
|
||||
} else {
|
||||
g_renderer_funcs = kSdlRendererFuncs;
|
||||
}
|
||||
int window_width = custom_size ? g_config.window_width : g_current_window_scale * (g_snes_width / kDefaultWindowScale);
|
||||
int window_height = custom_size ? g_config.window_height : g_current_window_scale * (g_snes_height / kDefaultWindowScale);
|
||||
|
||||
SDL_Window* window = SDL_CreateWindow(kWindowTitle, SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, window_width, window_height, g_win_flags);
|
||||
if(window == NULL) {
|
||||
@@ -351,11 +261,30 @@ int main(int argc, char** argv) {
|
||||
}
|
||||
g_window = window;
|
||||
SDL_SetWindowHitTest(window, HitTestCallback, NULL);
|
||||
|
||||
if (!g_renderer_funcs.Initialize(window))
|
||||
SDL_Renderer* renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC);
|
||||
if(renderer == NULL) {
|
||||
printf("Failed to create renderer: %s\n", SDL_GetError());
|
||||
return 1;
|
||||
}
|
||||
|
||||
SDL_AudioDeviceID device = 0;
|
||||
SDL_RendererInfo renderer_info;
|
||||
SDL_GetRendererInfo(renderer, &renderer_info);
|
||||
printf("Supported texture formats:");
|
||||
for (int i = 0; i < renderer_info.num_texture_formats; i++)
|
||||
printf(" %s", SDL_GetPixelFormatName(renderer_info.texture_formats[i]));
|
||||
printf("\n");
|
||||
|
||||
g_renderer = renderer;
|
||||
if (!g_config.ignore_aspect_ratio)
|
||||
SDL_RenderSetLogicalSize(renderer, g_snes_width, g_snes_height);
|
||||
SDL_Texture* texture = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_ARGB8888, SDL_TEXTUREACCESS_STREAMING, g_snes_width * 2, g_snes_height * 2);
|
||||
if(texture == NULL) {
|
||||
printf("Failed to create texture: %s\n", SDL_GetError());
|
||||
return 1;
|
||||
}
|
||||
SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "best");
|
||||
|
||||
SDL_AudioDeviceID device;
|
||||
SDL_AudioSpec want = { 0 }, have;
|
||||
g_audio_mutex = SDL_CreateMutex();
|
||||
if (!g_audio_mutex) Die("No mutex");
|
||||
@@ -376,8 +305,8 @@ int main(int argc, char** argv) {
|
||||
g_audiobuffer = malloc(g_frames_per_block * have.channels * sizeof(int16));
|
||||
}
|
||||
|
||||
if (argc >= 1 && !g_run_without_emu)
|
||||
LoadRom(argv[0]);
|
||||
if (argc >= 2 && !g_run_without_emu)
|
||||
LoadRom(argv[1]);
|
||||
|
||||
#if defined(_WIN32)
|
||||
_mkdir("saves");
|
||||
@@ -410,19 +339,18 @@ int main(int argc, char** argv) {
|
||||
HandleGamepadAxisInput(event.caxis.which, event.caxis.axis, event.caxis.value);
|
||||
break;
|
||||
case SDL_CONTROLLERBUTTONDOWN:
|
||||
case SDL_CONTROLLERBUTTONUP: {
|
||||
int b = RemapSdlButton(event.cbutton.button);
|
||||
if (b >= 0)
|
||||
HandleGamepadInput(b, event.type == SDL_CONTROLLERBUTTONDOWN);
|
||||
HandleGamepadInput(event.cbutton.button, event.cbutton.state == SDL_PRESSED);
|
||||
break;
|
||||
case SDL_CONTROLLERBUTTONUP:
|
||||
HandleGamepadInput(event.cbutton.button, event.cbutton.state == SDL_PRESSED);
|
||||
break;
|
||||
}
|
||||
case SDL_MOUSEWHEEL:
|
||||
if (SDL_GetModState() & KMOD_CTRL && event.wheel.y != 0)
|
||||
if (((g_win_flags & SDL_WINDOW_FULLSCREEN_DESKTOP) == 0 || (g_win_flags & SDL_WINDOW_FULLSCREEN) == 0) && event.wheel.y != 0 && SDL_GetModState() & KMOD_CTRL)
|
||||
ChangeWindowScale(event.wheel.y > 0 ? 1 : -1);
|
||||
break;
|
||||
case SDL_MOUSEBUTTONDOWN:
|
||||
if (event.button.button == SDL_BUTTON_LEFT && event.button.state == SDL_PRESSED && event.button.clicks == 2) {
|
||||
if ((g_win_flags & SDL_WINDOW_FULLSCREEN_DESKTOP) == 0 && (g_win_flags & SDL_WINDOW_FULLSCREEN) == 0 && SDL_GetModState() & KMOD_SHIFT) {
|
||||
if (((g_win_flags & SDL_WINDOW_FULLSCREEN_DESKTOP) == 0 || (g_win_flags & SDL_WINDOW_FULLSCREEN) == 0) && SDL_GetModState() & KMOD_SHIFT) {
|
||||
g_win_flags ^= SDL_WINDOW_BORDERLESS;
|
||||
SDL_SetWindowBordered(g_window, (g_win_flags & SDL_WINDOW_BORDERLESS) == 0);
|
||||
}
|
||||
@@ -442,8 +370,7 @@ int main(int argc, char** argv) {
|
||||
|
||||
if (g_paused != audiopaused) {
|
||||
audiopaused = g_paused;
|
||||
if (device)
|
||||
SDL_PauseAudioDevice(device, audiopaused);
|
||||
SDL_PauseAudioDevice(device, audiopaused);
|
||||
}
|
||||
|
||||
if (g_paused) {
|
||||
@@ -467,17 +394,13 @@ int main(int argc, char** argv) {
|
||||
continue;
|
||||
}
|
||||
|
||||
DrawPpuFrameWithPerf();
|
||||
|
||||
if (g_config.display_perf_title) {
|
||||
char title[60];
|
||||
snprintf(title, sizeof(title), "%s | FPS: %d", kWindowTitle, g_curr_fps);
|
||||
SDL_SetWindowTitle(g_window, title);
|
||||
}
|
||||
RenderScreen(window, renderer, texture, (g_win_flags &SDL_WINDOW_FULLSCREEN_DESKTOP) != 0);
|
||||
SDL_RenderPresent(renderer); // vsyncs to 60 FPS?
|
||||
|
||||
// if vsync isn't working, delay manually
|
||||
curTick = SDL_GetTicks();
|
||||
|
||||
|
||||
if (!g_config.disable_frame_delay) {
|
||||
static const uint8 delays[3] = { 17, 17, 16 }; // 60 fps
|
||||
lastTick += delays[frameCtr % 3];
|
||||
@@ -488,7 +411,6 @@ int main(int argc, char** argv) {
|
||||
lastTick = curTick - 500;
|
||||
delta = 500;
|
||||
}
|
||||
// printf("Sleeping %d\n", delta);
|
||||
SDL_Delay(delta);
|
||||
} else if (curTick - lastTick > 500) {
|
||||
lastTick = curTick;
|
||||
@@ -504,11 +426,11 @@ int main(int argc, char** argv) {
|
||||
SDL_CloseAudioDevice(device);
|
||||
}
|
||||
|
||||
|
||||
SDL_DestroyMutex(g_audio_mutex);
|
||||
free(g_audiobuffer);
|
||||
|
||||
g_renderer_funcs.Destroy();
|
||||
|
||||
SDL_DestroyTexture(texture);
|
||||
SDL_DestroyRenderer(renderer);
|
||||
SDL_DestroyWindow(window);
|
||||
SDL_Quit();
|
||||
//SaveConfigFile();
|
||||
@@ -560,15 +482,51 @@ static void RenderNumber(uint8 *dst, size_t pitch, int n, bool big) {
|
||||
RenderDigit(dst + (i << big), pitch, *s - '0', 0xffffff, big);
|
||||
}
|
||||
|
||||
static void RenderScreen(SDL_Window *window, SDL_Renderer *renderer, SDL_Texture *texture, bool fullscreen) {
|
||||
uint8* pixels = NULL;
|
||||
int pitch = 0;
|
||||
uint64 t0 = SDL_GetPerformanceCounter();
|
||||
if(SDL_LockTexture(texture, NULL, (void**)&pixels, &pitch) != 0) {
|
||||
printf("Failed to lock texture: %s\n", SDL_GetError());
|
||||
return;
|
||||
}
|
||||
uint64 t1 = SDL_GetPerformanceCounter();
|
||||
bool hq = RenderScreenWithPerf(pixels, pitch, g_ppu_render_flags);
|
||||
if (g_display_perf) {
|
||||
RenderNumber(pixels + (pitch * 2 << hq), pitch, g_curr_fps, hq);
|
||||
}
|
||||
if (g_config.display_perf_title) {
|
||||
char title[60];
|
||||
snprintf(title, sizeof(title), "%s | FPS: %d", kWindowTitle, g_curr_fps);
|
||||
SDL_SetWindowTitle(window, title);
|
||||
}
|
||||
uint64 t2 = SDL_GetPerformanceCounter();
|
||||
SDL_UnlockTexture(texture);
|
||||
uint64 t3 = SDL_GetPerformanceCounter();
|
||||
SDL_RenderClear(renderer);
|
||||
uint64 t4 = SDL_GetPerformanceCounter();
|
||||
SDL_Rect src_rect = { 0, 0, g_snes_width, g_snes_height };
|
||||
SDL_RenderCopy(renderer, texture, hq ? NULL : &src_rect, NULL);
|
||||
uint64 t5 = SDL_GetPerformanceCounter();
|
||||
|
||||
double f = 1e3 / (double)SDL_GetPerformanceFrequency();
|
||||
if (0) printf("RenderPerf %6.2f %6.2f %6.2f %6.2f %6.2f\n",
|
||||
(t1 - t0) * f,
|
||||
(t2 - t1) * f,
|
||||
(t3 - t2) * f,
|
||||
(t4 - t3) * f,
|
||||
(t5 - t4) * f
|
||||
);
|
||||
|
||||
|
||||
}
|
||||
|
||||
static void HandleCommand_Locked(uint32 j, bool pressed);
|
||||
|
||||
static void HandleCommand(uint32 j, bool pressed) {
|
||||
if (j <= kKeys_Controls_Last) {
|
||||
static const uint8 kKbdRemap[] = { 0, 4, 5, 6, 7, 2, 3, 8, 0, 9, 1, 10, 11 };
|
||||
if (pressed)
|
||||
g_input1_state |= 1 << kKbdRemap[j];
|
||||
else
|
||||
g_input1_state &= ~(1 << kKbdRemap[j]);
|
||||
static const uint8 kKbdRemap[12] = { 4, 5, 6, 7, 2, 3, 8, 0, 9, 1, 10, 11 };
|
||||
SetButtonState(kKbdRemap[j], pressed);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -584,15 +542,6 @@ static void HandleCommand(uint32 j, bool pressed) {
|
||||
SDL_UnlockMutex(g_audio_mutex);
|
||||
}
|
||||
|
||||
void ZeldaApuLock() {
|
||||
SDL_LockMutex(g_audio_mutex);
|
||||
}
|
||||
|
||||
void ZeldaApuUnlock() {
|
||||
SDL_UnlockMutex(g_audio_mutex);
|
||||
}
|
||||
|
||||
|
||||
static void HandleCommand_Locked(uint32 j, bool pressed) {
|
||||
if (!pressed)
|
||||
return;
|
||||
@@ -650,8 +599,11 @@ static void HandleCommand_Locked(uint32 j, bool pressed) {
|
||||
}
|
||||
|
||||
static void HandleInput(int keyCode, int keyMod, bool pressed) {
|
||||
if (keyCode == SDLK_SPACE)
|
||||
LoadImageFilesX2(g_zenv.ppu);
|
||||
|
||||
int j = FindCmdForSdlKey(keyCode, keyMod);
|
||||
if (j != 0)
|
||||
if (j >= 0)
|
||||
HandleCommand(j, pressed);
|
||||
}
|
||||
|
||||
@@ -663,35 +615,21 @@ static void OpenOneGamepad(int i) {
|
||||
}
|
||||
}
|
||||
|
||||
static int RemapSdlButton(int button) {
|
||||
switch (button) {
|
||||
case SDL_CONTROLLER_BUTTON_A: return kGamepadBtn_A;
|
||||
case SDL_CONTROLLER_BUTTON_B: return kGamepadBtn_B;
|
||||
case SDL_CONTROLLER_BUTTON_X: return kGamepadBtn_X;
|
||||
case SDL_CONTROLLER_BUTTON_Y: return kGamepadBtn_Y;
|
||||
case SDL_CONTROLLER_BUTTON_BACK: return kGamepadBtn_Back;
|
||||
case SDL_CONTROLLER_BUTTON_GUIDE: return kGamepadBtn_Guide;
|
||||
case SDL_CONTROLLER_BUTTON_START: return kGamepadBtn_Start;
|
||||
case SDL_CONTROLLER_BUTTON_LEFTSTICK: return kGamepadBtn_L3;
|
||||
case SDL_CONTROLLER_BUTTON_RIGHTSTICK: return kGamepadBtn_R3;
|
||||
case SDL_CONTROLLER_BUTTON_LEFTSHOULDER: return kGamepadBtn_L1;
|
||||
case SDL_CONTROLLER_BUTTON_RIGHTSHOULDER: return kGamepadBtn_R1;
|
||||
case SDL_CONTROLLER_BUTTON_DPAD_UP: return kGamepadBtn_DpadUp;
|
||||
case SDL_CONTROLLER_BUTTON_DPAD_DOWN: return kGamepadBtn_DpadDown;
|
||||
case SDL_CONTROLLER_BUTTON_DPAD_LEFT: return kGamepadBtn_DpadLeft;
|
||||
case SDL_CONTROLLER_BUTTON_DPAD_RIGHT: return kGamepadBtn_DpadRight;
|
||||
default: return -1;
|
||||
}
|
||||
}
|
||||
|
||||
static void HandleGamepadInput(int button, bool pressed) {
|
||||
if (!!(g_gamepad_modifiers & (1 << button)) == pressed)
|
||||
return;
|
||||
g_gamepad_modifiers ^= 1 << button;
|
||||
if (pressed)
|
||||
g_gamepad_last_cmd[button] = FindCmdForGamepadButton(button, g_gamepad_modifiers);
|
||||
if (g_gamepad_last_cmd[button] != 0)
|
||||
HandleCommand(g_gamepad_last_cmd[button], pressed);
|
||||
switch (button) {
|
||||
case SDL_CONTROLLER_BUTTON_A: SetButtonState(0, pressed); break;
|
||||
case SDL_CONTROLLER_BUTTON_X: SetButtonState(1, pressed); break;
|
||||
case SDL_CONTROLLER_BUTTON_BACK: SetButtonState(2, pressed); break;
|
||||
case SDL_CONTROLLER_BUTTON_START: SetButtonState(3, pressed); break;
|
||||
case SDL_CONTROLLER_BUTTON_DPAD_UP: SetButtonState(4, pressed); break;
|
||||
case SDL_CONTROLLER_BUTTON_DPAD_DOWN: SetButtonState(5, pressed); break;
|
||||
case SDL_CONTROLLER_BUTTON_DPAD_LEFT: SetButtonState(6, pressed); break;
|
||||
case SDL_CONTROLLER_BUTTON_DPAD_RIGHT: SetButtonState(7, pressed); break;
|
||||
case SDL_CONTROLLER_BUTTON_B: SetButtonState(8, pressed); break;
|
||||
case SDL_CONTROLLER_BUTTON_Y: SetButtonState(9, pressed); break;
|
||||
case SDL_CONTROLLER_BUTTON_LEFTSHOULDER: SetButtonState(10, pressed); break;
|
||||
case SDL_CONTROLLER_BUTTON_RIGHTSHOULDER: SetButtonState(11, pressed); break;
|
||||
}
|
||||
}
|
||||
|
||||
static void HandleVolumeAdjustment(int volume_adjustment) {
|
||||
@@ -757,15 +695,12 @@ static void HandleGamepadAxisInput(int gamepad_id, int axis, int value) {
|
||||
buttons = kSegmentToButtons[(uint8)(angle + 16 + 64) >> 5];
|
||||
}
|
||||
g_gamepad_buttons = buttons;
|
||||
} else if ((axis == SDL_CONTROLLER_AXIS_TRIGGERLEFT || axis == SDL_CONTROLLER_AXIS_TRIGGERRIGHT)) {
|
||||
if (value < 12000 || value >= 16000) // hysteresis
|
||||
HandleGamepadInput(axis == SDL_CONTROLLER_AXIS_TRIGGERLEFT ? kGamepadBtn_L2 : kGamepadBtn_R2, value >= 12000);
|
||||
}
|
||||
}
|
||||
|
||||
static bool LoadRom(const char *filename) {
|
||||
size_t length = 0;
|
||||
uint8 *file = ReadWholeFile(filename, &length);
|
||||
uint8 *file = ReadFile(filename, &length);
|
||||
if(!file) Die("Failed to read file");
|
||||
bool result = EmuInitialize(file, length);
|
||||
free(file);
|
||||
@@ -797,7 +732,7 @@ static void LoadLinkGraphics() {
|
||||
if (g_config.link_graphics) {
|
||||
fprintf(stderr, "Loading Link Graphics: %s\n", g_config.link_graphics);
|
||||
size_t length = 0;
|
||||
uint8 *file = ReadWholeFile(g_config.link_graphics, &length);
|
||||
uint8 *file = ReadFile(g_config.link_graphics, &length);
|
||||
if (file == NULL || !ParseLinkGraphics(file, length))
|
||||
Die("Unable to load file");
|
||||
free(file);
|
||||
@@ -810,10 +745,10 @@ uint32 g_asset_sizes[kNumberOfAssets];
|
||||
|
||||
static void LoadAssets() {
|
||||
size_t length = 0;
|
||||
uint8 *data = ReadWholeFile("zelda3_assets.dat", &length);
|
||||
uint8 *data = ReadFile("tables/zelda3_assets.dat", &length);
|
||||
if (!data)
|
||||
data = ReadWholeFile("tables/zelda3_assets.dat", &length);
|
||||
if (!data) Die("Failed to read zelda3_assets.dat. Please see the README for information about how you get this file.");
|
||||
data = ReadFile("zelda3_assets.dat", &length);
|
||||
if (!data) Die("Failed to read zelda3_assets.dat");
|
||||
|
||||
static const char kAssetsSig[] = { kAssets_Sig };
|
||||
|
||||
@@ -833,40 +768,4 @@ static void LoadAssets() {
|
||||
g_asset_ptrs[i] = data + offset;
|
||||
offset += size;
|
||||
}
|
||||
|
||||
if (g_config.features0 & kFeatures0_DimFlashes) { // patch dungeon floor palettes
|
||||
kPalette_DungBgMain[0x484] = 0x70;
|
||||
kPalette_DungBgMain[0x485] = 0x95;
|
||||
kPalette_DungBgMain[0x486] = 0x57;
|
||||
}
|
||||
}
|
||||
|
||||
// Go some steps up and find zelda3.ini
|
||||
static void SwitchDirectory() {
|
||||
char buf[4096];
|
||||
if (!getcwd(buf, sizeof(buf) - 32))
|
||||
return;
|
||||
size_t pos = strlen(buf);
|
||||
|
||||
for (int step = 0; pos != 0 && step < 3; step++) {
|
||||
memcpy(buf + pos, "/zelda3.ini", 12);
|
||||
FILE *f = fopen(buf, "rb");
|
||||
if (f) {
|
||||
fclose(f);
|
||||
buf[pos] = 0;
|
||||
if (step != 0) {
|
||||
printf("Found zelda3.ini in %s\n", buf);
|
||||
int err = chdir(buf);
|
||||
(void)err;
|
||||
}
|
||||
return;
|
||||
}
|
||||
pos--;
|
||||
while (pos != 0 && buf[pos] != '/' && buf[pos] != '\\')
|
||||
pos--;
|
||||
}
|
||||
}
|
||||
|
||||
MemBlk FindInAssetArray(int asset, int idx) {
|
||||
return FindIndexInMemblk((MemBlk) { g_asset_ptrs[asset], g_asset_sizes[asset] }, idx);
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -4,6 +4,7 @@
|
||||
const uint8 *GetDungmapFloorLayout();
|
||||
uint8 GetOtherDungmapInfo(int count);
|
||||
void DungMap_4();
|
||||
const uint8 *GetCurrentTextPtr();
|
||||
void Module_Messaging_6();
|
||||
void OverworldMap_SetupHdma();
|
||||
const uint8 *GetLightOverworldTilemap();
|
||||
@@ -62,6 +63,8 @@ void WorldMap_ExitMap();
|
||||
void WorldMap_SetUpHDMA();
|
||||
void WorldMap_FillTilemapWithEF();
|
||||
void WorldMap_HandleSprites();
|
||||
bool WorldMap_CalculateOamCoordinates(PointU8 *pt);
|
||||
void WorldMap_HandleSpriteBlink(int spr, uint8 r11_ext, uint8 r12_flags, uint8 r13_char, uint8 r14_x, uint8 r15_y);
|
||||
bool OverworldMap_CheckForPendant(int k);
|
||||
bool OverworldMap_CheckForCrystal(int k);
|
||||
void Module0E_03_DungeonMap();
|
||||
@@ -107,7 +110,9 @@ void RenderText_Draw_BorderIncremental();
|
||||
void RenderText_Draw_CharacterTilemap();
|
||||
void RenderText_Draw_MessageCharacters();
|
||||
void RenderText_Draw_Finish();
|
||||
void RenderText_Draw_RenderCharacter_All();
|
||||
void VWF_RenderSingle();
|
||||
void VWF_RenderCharacter();
|
||||
void RenderText_Draw_Choose2LowOr3();
|
||||
void RenderText_Draw_ChooseItem();
|
||||
void RenderText_FindYItem_Previous();
|
||||
@@ -116,10 +121,12 @@ void RenderText_DrawSelectedYItem();
|
||||
void RenderText_Draw_Choose2HiOr3();
|
||||
void RenderText_Draw_Choose3();
|
||||
void RenderText_Draw_Choose1Or2();
|
||||
bool RenderText_Draw_Scroll();
|
||||
void RenderText_Draw_Scroll();
|
||||
void RenderText_Draw_Command7B();
|
||||
void RenderText_Draw_ABunchOfSpaces();
|
||||
void RenderText_Draw_EmptyBuffer();
|
||||
void RenderText_SetDefaultWindowPosition();
|
||||
void RenderText_DrawBorderInitialize();
|
||||
uint16 *RenderText_DrawBorderRow(uint16 *d, int y);
|
||||
void Text_BuildCharacterTilemap();
|
||||
void RenderText_Refresh();
|
||||
void Text_GenerateMessagePointers();
|
||||
@@ -5,13 +5,7 @@
|
||||
#include "snes/snes_regs.h"
|
||||
#include "snes/ppu.h"
|
||||
#include "assets.h"
|
||||
#include "audio.h"
|
||||
|
||||
static const uint8 kNmiVramAddrs[] = {
|
||||
0, 0, 4, 8, 12, 8, 12, 0, 4, 0, 8, 4, 12, 4, 12, 0,
|
||||
8, 16, 20, 24, 28, 24, 28, 16, 20, 16, 24, 20, 28, 20, 28, 16,
|
||||
24, 96, 104,
|
||||
};
|
||||
static PlayerHandlerFunc *const kNmiSubroutines[25] = {
|
||||
&NMI_UploadTilemap_doNothing,
|
||||
&NMI_UploadTilemap,
|
||||
@@ -47,20 +41,14 @@ void NMI_UploadSubscreenOverlayLatter() {
|
||||
NMI_HandleArbitraryTileMap(&g_ram[0x13000], 0x40, 0x80);
|
||||
}
|
||||
|
||||
static void CopyToVram(uint32 dstv, const uint8 *src, int len) {
|
||||
memcpy(&g_zenv.vram[dstv], src, len);
|
||||
}
|
||||
|
||||
static void CopyToVramVertical(uint32 dstv, const uint8 *src, int len) {
|
||||
static void CopyToVramVertical(uint16 *dst, const uint8 *src, int len) {
|
||||
assert(!(len & 1));
|
||||
uint16 *dst = &g_zenv.vram[dstv];
|
||||
for (int i = 0, i_end = len >> 1; i < i_end; i++, dst += 32, src += 2)
|
||||
*dst = WORD(*src);
|
||||
}
|
||||
|
||||
static void CopyToVramLow(const uint8 *src, uint32 addr, int num) {
|
||||
uint16 *dst = &g_zenv.vram[addr];
|
||||
for (int i = 0; i < num; i++)
|
||||
static void CopyToVramLow(uint16 *dst, const uint8 *src, int num) {
|
||||
for (int i = 0; i < num; i++)
|
||||
dst[i] = (dst[i] & ~0xff) | src[i];
|
||||
}
|
||||
|
||||
@@ -106,15 +94,15 @@ void WritePpuRegisters() {
|
||||
zelda_ppu_write(BG34NBA, 7);
|
||||
}
|
||||
|
||||
static void Interrupt_NMI_AudioParts_Locked() {
|
||||
void Interrupt_NMI(uint16 joypad_input) { // 8080c9
|
||||
if (music_control == 0) {
|
||||
// if (zelda_apu_read(APUI00) == last_music_control)
|
||||
// zelda_apu_write(APUI00, 0);
|
||||
// Zelda causes unwanted music change when going in a portal. last_music_control doesn't hold the
|
||||
// song but the last applied effect
|
||||
} else if (!ZeldaIsPlayingMusicTrackWithBug(music_control)) {
|
||||
if (zelda_apu_read(APUI00) == last_music_control)
|
||||
zelda_apu_write(APUI00, 0);
|
||||
// Zelda causes unwanted music change when going in a portal. last_music_control doesn't hold the
|
||||
// song but the last applied effect
|
||||
} else if (music_control != (enhanced_features0 & kFeatures0_MiscBugFixes ? music_unk1 : last_music_control)) {
|
||||
last_music_control = music_control;
|
||||
ZeldaPlayMsuAudioTrack(music_control);
|
||||
ZeldaPlayMsuAudioTrack();
|
||||
if (music_control < 0xf2)
|
||||
music_unk1 = music_control;
|
||||
music_control = 0;
|
||||
@@ -133,12 +121,6 @@ static void Interrupt_NMI_AudioParts_Locked() {
|
||||
sound_effect_1 = 0;
|
||||
sound_effect_2 = 0;
|
||||
|
||||
}
|
||||
|
||||
void Interrupt_NMI(uint16 joypad_input) { // 8080c9
|
||||
|
||||
Interrupt_NMI_AudioParts_Locked();
|
||||
|
||||
if (!nmi_boolean) {
|
||||
nmi_boolean = true;
|
||||
NMI_DoUpdates();
|
||||
@@ -171,19 +153,32 @@ void NMI_ReadJoypads(uint16 joypad_input) { // 8083d1
|
||||
|
||||
void NMI_DoUpdates() { // 8089e0
|
||||
if (!nmi_disable_core_updates) {
|
||||
memcpy(&g_zenv.vram[0x4100], &kLinkGraphics[dma_source_addr_0 - 0x8000], 0x40);
|
||||
memcpy(&g_zenv.vram[0x4120], &kLinkGraphics[dma_source_addr_1 - 0x8000], 0x40);
|
||||
memcpy(&g_zenv.vram[0x4140], &kLinkGraphics[dma_source_addr_2 - 0x8000], 0x20);
|
||||
|
||||
memcpy(&g_zenv.vram[0x4000], &kLinkGraphics[dma_source_addr_3 - 0x8000], 0x40);
|
||||
memcpy(&g_zenv.vram[0x4020], &kLinkGraphics[dma_source_addr_4 - 0x8000], 0x40);
|
||||
memcpy(&g_zenv.vram[0x4040], &kLinkGraphics[dma_source_addr_5 - 0x8000], 0x20);
|
||||
memcpy(&g_zenv.vram[0x4100], &kLinkGraphics[dma_source_addr_0 - 0x8000], 0x40);
|
||||
memcpy(&g_zenv.vram[0x4120], &kLinkGraphics[dma_source_addr_1 - 0x8000], 0x40);
|
||||
memcpy(&g_zenv.vram[0x4140], &kLinkGraphics[dma_source_addr_2 - 0x8000], 0x20);
|
||||
if (0) {
|
||||
CONVERT_SPRITE_TO_X2(0x4000, (0x4050 - 0x4000) / 16);
|
||||
CONVERT_SPRITE_TO_X2(0x4100, (0x4150 - 0x4100) / 16);
|
||||
} else {
|
||||
#define GET_LINK_SPRITE(x) g_image_data_x2.link_sprite[(x - 0x8000) >> 5]
|
||||
memcpy(&g_zenv.ext_vram[GET_SPRITE_ADDR_X2(0x4000)], GET_LINK_SPRITE(dma_source_addr_3), 2 * 128);
|
||||
memcpy(&g_zenv.ext_vram[GET_SPRITE_ADDR_X2(0x4020)], GET_LINK_SPRITE(dma_source_addr_4), 2 * 128);
|
||||
memcpy(&g_zenv.ext_vram[GET_SPRITE_ADDR_X2(0x4040)], GET_LINK_SPRITE(dma_source_addr_5), 128);
|
||||
memcpy(&g_zenv.ext_vram[GET_SPRITE_ADDR_X2(0x4100)], GET_LINK_SPRITE(dma_source_addr_0), 2 * 128);
|
||||
memcpy(&g_zenv.ext_vram[GET_SPRITE_ADDR_X2(0x4120)], GET_LINK_SPRITE(dma_source_addr_1), 2 * 128);
|
||||
memcpy(&g_zenv.ext_vram[GET_SPRITE_ADDR_X2(0x4140)], GET_LINK_SPRITE(dma_source_addr_2), 128);
|
||||
#undef GET_LINK_SPRITE
|
||||
}
|
||||
|
||||
memcpy(&g_zenv.vram[0x4050], &g_ram[dma_source_addr_6], 0x40);
|
||||
memcpy(&g_zenv.vram[0x4070], &g_ram[dma_source_addr_7], 0x40);
|
||||
memcpy(&g_zenv.vram[0x4090], &g_ram[dma_source_addr_8], 0x40);
|
||||
memcpy(&g_zenv.vram[0x40b0], &g_ram[dma_source_addr_9], 0x20);
|
||||
memcpy(&g_zenv.vram[0x40c0], &g_ram[dma_source_addr_10], 0x40);
|
||||
|
||||
memcpy(&g_zenv.vram[0x4150], &g_ram[dma_source_addr_11], 0x40);
|
||||
memcpy(&g_zenv.vram[0x4170], &g_ram[dma_source_addr_12], 0x40);
|
||||
memcpy(&g_zenv.vram[0x4190], &g_ram[dma_source_addr_13], 0x40);
|
||||
@@ -200,8 +195,14 @@ void NMI_DoUpdates() { // 8089e0
|
||||
memcpy(&g_zenv.vram[0x40e0], &g_ram[dma_source_addr_20], 0x40);
|
||||
memcpy(&g_zenv.vram[0x41e0], &g_ram[dma_source_addr_21], 0x40);
|
||||
}
|
||||
CONVERT_SPRITE_TO_X2(0x4050, (0x4100 - 0x4050) / 16);
|
||||
CONVERT_SPRITE_TO_X2(0x4150, (0x4360 - 0x4150) / 16);
|
||||
|
||||
memcpy(&g_zenv.vram[animated_tile_vram_addr], &g_ram[animated_tile_data_src], 0x400);
|
||||
if (animated_tile_vram_addr >= 0x4000 && animated_tile_vram_addr < 0x6000)
|
||||
CONVERT_SPRITE_TO_X2(animated_tile_vram_addr, 32);
|
||||
else if (animated_tile_vram_addr >= 0x2000 && animated_tile_vram_addr < 0x4000)
|
||||
CONVERT_BG_TO_X2(animated_tile_vram_addr, 32);
|
||||
}
|
||||
|
||||
if (flag_update_hud_in_nmi) {
|
||||
@@ -209,6 +210,7 @@ void NMI_DoUpdates() { // 8089e0
|
||||
}
|
||||
|
||||
if (flag_update_cgram_in_nmi) {
|
||||
g_zenv.ppu->cgramDirty = true;
|
||||
memcpy(g_zenv.ppu->cgram, main_palette_buffer, 0x200);
|
||||
}
|
||||
|
||||
@@ -238,7 +240,11 @@ void NMI_DoUpdates() { // 8089e0
|
||||
}
|
||||
|
||||
if (nmi_update_tilemap_dst) {
|
||||
// This is about sprites
|
||||
memcpy(&g_zenv.vram[nmi_update_tilemap_dst * 256], &g_ram[0x10000 + nmi_update_tilemap_src], 0x200);
|
||||
if (nmi_update_tilemap_dst * 256 >= 0x4000 && nmi_update_tilemap_dst * 256 < 0x6000)
|
||||
CONVERT_SPRITE_TO_X2(nmi_update_tilemap_dst * 256, 16);
|
||||
|
||||
nmi_update_tilemap_dst = 0;
|
||||
}
|
||||
|
||||
@@ -268,13 +274,20 @@ void NMI_DoUpdates() { // 8089e0
|
||||
}
|
||||
|
||||
int idx = nmi_subroutine_index;
|
||||
nmi_subroutine_index = 0;
|
||||
kNmiSubroutines[idx]();
|
||||
if (idx) {
|
||||
nmi_subroutine_index = 0;
|
||||
kNmiSubroutines[idx]();
|
||||
}
|
||||
}
|
||||
|
||||
void NMI_UploadTilemap() { // 808cb0
|
||||
memcpy(&g_zenv.vram[kNmiVramAddrs[BYTE(nmi_load_target_addr)] << 8], &g_ram[0x1000], 0x800);
|
||||
|
||||
static const uint8 kNmiVramAddrs[] = {
|
||||
0, 0, 4, 8, 12, 8, 12, 0, 4, 0, 8, 4, 12, 4, 12, 0,
|
||||
8, 16, 20, 24, 28, 24, 28, 16, 20, 16, 24, 20, 28, 20, 28, 16,
|
||||
24, 96, 104,
|
||||
};
|
||||
int load_addr = kNmiVramAddrs[BYTE(nmi_load_target_addr)] << 8;
|
||||
memcpy(&g_zenv.vram[load_addr], &g_ram[0x1000], 0x800);
|
||||
*(uint16 *)&g_ram[0x1000] = 0;
|
||||
nmi_disable_core_updates = 0;
|
||||
}
|
||||
@@ -283,12 +296,12 @@ void NMI_UploadTilemap_doNothing() { // 808ce3
|
||||
}
|
||||
|
||||
void NMI_UploadBG3Text() { // 808ce4
|
||||
memcpy(&g_zenv.vram[0x7c00], &g_ram[0x10000], 0x7e0);
|
||||
memcpy(&g_zenv.vram[0x7c00], messaging_buf, 0x7e0);
|
||||
nmi_disable_core_updates = 0;
|
||||
}
|
||||
|
||||
void NMI_UpdateOWScroll() { // 808d13
|
||||
uint8 *src = (uint8 *)uvram.data;
|
||||
uint8 *src = (uint8 *)uvram.data, *src_org = src;
|
||||
int f = WORD(src[0]);
|
||||
int step = (f & 0x8000) ? 32 : 1;
|
||||
int len = f & 0x3fff;
|
||||
@@ -317,8 +330,8 @@ void NMI_HandleArbitraryTileMap(const uint8 *src, int i, int i_end) { // 808dae
|
||||
|
||||
void NMI_UpdateBG1Wall() { // 808e09
|
||||
// Secret Wall Right
|
||||
CopyToVramVertical(nmi_load_target_addr, &g_ram[0xc880], 0x40);
|
||||
CopyToVramVertical(nmi_load_target_addr + 0x800, &g_ram[0xc8c0], 0x40);
|
||||
CopyToVramVertical(&g_zenv.vram[nmi_load_target_addr], &g_ram[0xc880], 0x40);
|
||||
CopyToVramVertical(&g_zenv.vram[nmi_load_target_addr + 0x800], &g_ram[0xc8c0], 0x40);
|
||||
}
|
||||
|
||||
void NMI_TileMapNothing() { // 808e4b
|
||||
@@ -330,7 +343,7 @@ void NMI_UpdateLoadLightWorldMap() { // 808e54
|
||||
for (int j = 0; j != 4; j++) {
|
||||
int t = kLightWorldTileMapDsts[j];
|
||||
for (int i = 0x20; i; i--) {
|
||||
CopyToVramLow(src, t, 0x20);
|
||||
CopyToVramLow(&g_zenv.vram[t], src, 0x20);
|
||||
src += 32;
|
||||
t += 0x80;
|
||||
}
|
||||
@@ -338,79 +351,94 @@ void NMI_UpdateLoadLightWorldMap() { // 808e54
|
||||
}
|
||||
|
||||
void NMI_UpdateBG2Left() { // 808ea9
|
||||
CopyToVram(0, &g_ram[0x10000], 0x800);
|
||||
CopyToVram(0x800, &g_ram[0x10800], 0x800);
|
||||
memcpy(&g_zenv.vram[0], &g_ram[0x10000], 0x800);
|
||||
memcpy(&g_zenv.vram[0x800], &g_ram[0x10800], 0x800);
|
||||
}
|
||||
|
||||
static void NMI_RunTileMapUpdateDMA(uint16 *dst) { // 808fc9
|
||||
memcpy(dst, &g_ram[0x10000], 0x1000);
|
||||
nmi_disable_core_updates = 0;
|
||||
}
|
||||
|
||||
void NMI_UpdateBGChar3and4() { // 808ee7
|
||||
memcpy(&g_zenv.vram[0x2c00], &g_ram[0x10000], 0x1000);
|
||||
nmi_disable_core_updates = 0;
|
||||
NMI_RunTileMapUpdateDMA(&g_zenv.vram[0x2c00]);
|
||||
CONVERT_BG_TO_X2(0x2c00, 128);
|
||||
}
|
||||
|
||||
void NMI_UpdateBGChar5and6() { // 808f16
|
||||
memcpy(&g_zenv.vram[0x3400], &g_ram[0x11000], 0x1000);
|
||||
CONVERT_BG_TO_X2(0x3400, 128);
|
||||
nmi_disable_core_updates = 0;
|
||||
}
|
||||
|
||||
void NMI_UpdateBGCharHalf() { // 808f45
|
||||
memcpy(&g_zenv.vram[BYTE(nmi_load_target_addr) * 256], &g_ram[0x11000], 0x400);
|
||||
int dst_addr = BYTE(nmi_load_target_addr) * 256;
|
||||
memcpy(&g_zenv.vram[dst_addr], &g_ram[0x11000], 0x400);
|
||||
if (dst_addr >= 0x4000 && dst_addr < 0x6000)
|
||||
CONVERT_SPRITE_TO_X2(dst_addr, 32);
|
||||
}
|
||||
|
||||
void NMI_UpdateBGChar0() { // 808f72
|
||||
NMI_RunTileMapUpdateDMA(0x2000);
|
||||
NMI_RunTileMapUpdateDMA(&g_zenv.vram[0x2000]);
|
||||
CONVERT_BG_TO_X2(0x2000, 128);
|
||||
}
|
||||
|
||||
void NMI_UpdateBGChar1() { // 808f79
|
||||
NMI_RunTileMapUpdateDMA(0x2800);
|
||||
NMI_RunTileMapUpdateDMA(&g_zenv.vram[0x2800]);
|
||||
CONVERT_BG_TO_X2(0x2800, 128);
|
||||
}
|
||||
|
||||
void NMI_UpdateBGChar2() { // 808f80
|
||||
NMI_RunTileMapUpdateDMA(0x3000);
|
||||
NMI_RunTileMapUpdateDMA(&g_zenv.vram[0x3000]);
|
||||
CONVERT_BG_TO_X2(0x3000, 128);
|
||||
}
|
||||
|
||||
void NMI_UpdateBGChar3() { // 808f87
|
||||
NMI_RunTileMapUpdateDMA(0x3800);
|
||||
NMI_RunTileMapUpdateDMA(&g_zenv.vram[0x3800]);
|
||||
CONVERT_BG_TO_X2(0x3800, 128);
|
||||
}
|
||||
|
||||
void NMI_UpdateObjChar0() { // 808f8e
|
||||
CopyToVram(0x4400, &g_ram[0x10000], 0x800);
|
||||
memcpy(&g_zenv.vram[0x4400], &g_ram[0x10000], 0x800);
|
||||
CONVERT_SPRITE_TO_X2(0x4400, 64);
|
||||
nmi_disable_core_updates = 0;
|
||||
}
|
||||
|
||||
void NMI_UpdateObjChar2() { // 808fbd
|
||||
NMI_RunTileMapUpdateDMA(0x5000);
|
||||
NMI_RunTileMapUpdateDMA(&g_zenv.vram[0x5000]);
|
||||
CONVERT_SPRITE_TO_X2(0x5000, 64);
|
||||
}
|
||||
|
||||
void NMI_UpdateObjChar3() { // 808fc4
|
||||
NMI_RunTileMapUpdateDMA(0x5800);
|
||||
}
|
||||
|
||||
void NMI_RunTileMapUpdateDMA(int dst) { // 808fc9
|
||||
CopyToVram(dst, &g_ram[0x10000], 0x1000);
|
||||
nmi_disable_core_updates = 0;
|
||||
NMI_RunTileMapUpdateDMA(&g_zenv.vram[0x5800]);
|
||||
CONVERT_SPRITE_TO_X2(0x5800, 64);
|
||||
}
|
||||
|
||||
void NMI_UploadDarkWorldMap() { // 808ff3
|
||||
const uint8 *src = g_ram + 0x1000;
|
||||
int t = 0x810;
|
||||
uint16 *dst = &g_zenv.vram[0x810];
|
||||
for (int i = 0x20; i; i--) {
|
||||
CopyToVramLow(src, t, 0x20);
|
||||
CopyToVramLow(dst, src, 0x20);
|
||||
src += 32;
|
||||
t += 0x80;
|
||||
dst += 0x80;
|
||||
}
|
||||
}
|
||||
|
||||
void NMI_UploadGameOverText() { // 809038
|
||||
CopyToVram(0x7800, &g_ram[0x2000], 0x800);
|
||||
CopyToVram(0x7d00, &g_ram[0x3400], 0x600);
|
||||
memcpy(&g_zenv.vram[0x7800], &g_ram[0x2000], 0x800);
|
||||
memcpy(&g_zenv.vram[0x7d00], &g_ram[0x3400], 0x600);
|
||||
CONVERT_HUD_TO_X2(0x7800, 128);
|
||||
CONVERT_HUD_TO_X2(0x7d00, 96);
|
||||
}
|
||||
|
||||
void NMI_UpdatePegTiles() { // 80908b
|
||||
CopyToVram(0x3d00, &g_ram[0x10000], 0x100);
|
||||
memcpy(&g_zenv.vram[0x3d00], &g_ram[0x10000], 0x100);
|
||||
CONVERT_BG_TO_X2(0x3d00, 8);
|
||||
}
|
||||
|
||||
void NMI_UpdateStarTiles() { // 8090b7
|
||||
CopyToVram(0x3ed0, &g_ram[0x10000], 0x40);
|
||||
memcpy(&g_zenv.vram[0x3ed0], &g_ram[0x10000], 0x40);
|
||||
CONVERT_BG_TO_X2(0x3ed0, 2);
|
||||
}
|
||||
|
||||
void HandleStripes14(const uint8 *p) { // 8092a1
|
||||
@@ -455,6 +483,7 @@ void HandleStripes14(const uint8 *p) { // 8092a1
|
||||
void NMI_UpdateIRQGFX() { // 809347
|
||||
if (nmi_flag_update_polyhedral) {
|
||||
memcpy(&g_zenv.vram[0x5800], &g_ram[0xe800], 0x800);
|
||||
// CONVERT_SPRITE_TO_X2(0x5800, 64);
|
||||
nmi_flag_update_polyhedral = 0;
|
||||
}
|
||||
}
|
||||
@@ -26,7 +26,6 @@ void NMI_UpdateBGChar3();
|
||||
void NMI_UpdateObjChar0();
|
||||
void NMI_UpdateObjChar2();
|
||||
void NMI_UpdateObjChar3();
|
||||
void NMI_RunTileMapUpdateDMA(int dst);
|
||||
void NMI_UploadDarkWorldMap();
|
||||
void NMI_UploadGameOverText();
|
||||
void NMI_UpdatePegTiles();
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 590 B |
@@ -1,117 +0,0 @@
|
||||
import array
|
||||
|
||||
memos = {}
|
||||
memoslist = []
|
||||
def memo(s):
|
||||
m = memos.get(s)
|
||||
if m == None:
|
||||
m = len(memoslist)
|
||||
memos[s] = m
|
||||
memoslist.append(s)
|
||||
return m
|
||||
|
||||
def tos(s): return "".join(memoslist[c] for c in s)
|
||||
|
||||
lines = []
|
||||
for line in open('dialogue.txt', 'r').read().splitlines():
|
||||
line = line.split(': ')[1]
|
||||
|
||||
r = array.array('H')
|
||||
|
||||
i = 0
|
||||
while i < len(line):
|
||||
if line[i] == '[':
|
||||
j = line.index(']', i + 1)
|
||||
r.append(memo(line[i:j+1]))
|
||||
i = j + 1
|
||||
else:
|
||||
r.append(memo(line[i]))
|
||||
i += 1
|
||||
|
||||
#print(repr(line))
|
||||
#print(r)
|
||||
lines.append(list(r))
|
||||
import collections
|
||||
|
||||
|
||||
def find_all_ngrams(lines, N, cost):
|
||||
ctr = collections.Counter()
|
||||
for line in lines:
|
||||
for i in range(len(line) - N + 1):
|
||||
if line[i] != line[i+1]:
|
||||
ctr[tuple(line[i:i+N])] += 1
|
||||
r = list((b, a) for a, b in ctr.items() if b >= 2)
|
||||
if len(r) == 0:
|
||||
return None, 0
|
||||
b, a = max(r)
|
||||
return a, (N - cost) * b - N - 2 # 2 is the overhead of the dict
|
||||
|
||||
def find_best_ngram(cost):
|
||||
best_score=0
|
||||
|
||||
for i in range(2, 32):
|
||||
text, score = find_all_ngrams(lines, i, cost)
|
||||
if score > best_score:
|
||||
best_score = score
|
||||
best_text = text
|
||||
return best_score, best_text
|
||||
|
||||
def update_ngrams(lines, replace_from, replace_to):
|
||||
for line in lines:
|
||||
for i in range(len(line) - len(replace_from) + 1):
|
||||
if tuple(line[i:i+len(replace_from)]) == replace_from:
|
||||
line[i:i+len(replace_from)] = replace_to
|
||||
|
||||
total_gain = 0
|
||||
|
||||
original_tokens = sum(len(line) for line in lines)
|
||||
|
||||
|
||||
kTextDictionary_US = [
|
||||
' ', ' ', ' ', "'s ", 'and ',
|
||||
'are ', 'all ', 'ain', 'and', 'at ',
|
||||
'ast', 'an', 'at', 'ble', 'ba',
|
||||
'be', 'bo', 'can ', 'che', 'com',
|
||||
'ck', 'des', 'di', 'do', 'en ',
|
||||
'er ', 'ear', 'ent', 'ed ', 'en',
|
||||
'er', 'ev', 'for', 'fro', 'give ',
|
||||
'get', 'go', 'have', 'has', 'her',
|
||||
'hi', 'ha', 'ight ', 'ing ', 'in',
|
||||
'is', 'it', 'just', 'know', 'ly ',
|
||||
'la', 'lo', 'man', 'ma', 'me',
|
||||
'mu', "n't ", 'non', 'not', 'open',
|
||||
'ound', 'out ', 'of', 'on', 'or',
|
||||
'per', 'ple', 'pow', 'pro', 're ',
|
||||
're', 'some', 'se', 'sh', 'so',
|
||||
'st', 'ter ', 'thin', 'ter', 'tha',
|
||||
'the', 'thi', 'to', 'tr', 'up',
|
||||
'ver', 'with', 'wa', 'we', 'wh',
|
||||
'wi', 'you', 'Her', 'Tha', 'The',
|
||||
'Thi', 'You',
|
||||
]
|
||||
|
||||
|
||||
dictionary = []
|
||||
|
||||
for i in range(111+256):
|
||||
best_score, best_text = find_best_ngram(1 if i < 111 else 2)
|
||||
if best_score == 0:
|
||||
break
|
||||
|
||||
total_gain += best_score
|
||||
|
||||
print(f'Removed best bigram "{tos(best_text)}" with gain {best_score}, total gain {total_gain} / {original_tokens}')
|
||||
|
||||
dictionary.append(best_text)
|
||||
|
||||
update_ngrams(lines, best_text, [memo('{%s}' % tos(best_text))])
|
||||
|
||||
#print('kTextDictionary_NEW = [')
|
||||
#for i, d in enumerate(dictionary):
|
||||
# repl = tos(d).replace('{', '').replace('}', '')
|
||||
# print(f'{repr(repl)},')
|
||||
#print(']')
|
||||
|
||||
|
||||
for i, a in enumerate(lines):
|
||||
print(i, tos(a))
|
||||
@@ -1,128 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
import concurrent.futures
|
||||
import opuslib
|
||||
import struct
|
||||
import samplerate # pip install samplerate
|
||||
import numpy as np
|
||||
|
||||
# File format
|
||||
# 4 byte: OPUZ
|
||||
# 4 byte: VERSION = 0
|
||||
# Foreach Repeat Entry {
|
||||
# 4 byte: seek to file offset
|
||||
# 4 byte: num pcm samples to play from here
|
||||
# 2 byte: # samples to skip + flags
|
||||
# }
|
||||
|
||||
def encode_to_msu_opus(msu_infile, bitrate = 128000):
|
||||
raw_msu1 = bytearray(open(msu_infile, 'rb').read())
|
||||
|
||||
if raw_msu1[:4] != b'MSU1':
|
||||
raise Exception('Expecting MSU1 file')
|
||||
msu_repeat_pos, = struct.unpack_from('<I', raw_msu1, 4)
|
||||
|
||||
np_audio = np.frombuffer(raw_msu1[8:], dtype=np.int16)
|
||||
np_audio = np.reshape(np_audio, (-1, 2)).astype(np.float32) * (1.0/32768.0)
|
||||
assert np_audio.shape[1] == 2
|
||||
|
||||
#print('Song has %d samples in 44.1khz: %s' % (np_audio.shape[0], np_audio.dtype))
|
||||
np_audio = samplerate.resample(np_audio, 48000 / 44100, 'sinc_best')
|
||||
source_samples = np_audio.shape[0]
|
||||
msu_repeat_pos = msu_repeat_pos * 48000 // 44100
|
||||
#print('Song has %d samples in 48khz: %s' % (np_audio.shape[0], np_audio.dtype))
|
||||
|
||||
audio = bytearray(np_audio.tobytes())
|
||||
|
||||
FRAME_SIZE = 960
|
||||
BYTES_PER_SAMPLE = 8
|
||||
BYTES_PER_FRAME = FRAME_SIZE * BYTES_PER_SAMPLE
|
||||
|
||||
def create_encoder(bitrate):
|
||||
enc = opuslib.Encoder(48000, 2, 'audio')
|
||||
enc.vbr = True
|
||||
enc.complexity = 10
|
||||
enc.bitrate = bitrate
|
||||
opuslib.api.encoder.encoder_ctl(enc.encoder_state, opuslib.api.ctl.ctl_set(11002), 1002) # force celt encoding
|
||||
return enc
|
||||
|
||||
def pad_data_to_frame_size(audio, lookahead):
|
||||
audio.extend(bytearray(lookahead * BYTES_PER_SAMPLE))
|
||||
rest = len(audio) % BYTES_PER_FRAME
|
||||
if rest:
|
||||
audio.extend(bytearray(BYTES_PER_FRAME - rest))
|
||||
|
||||
enc = create_encoder(bitrate)
|
||||
pad_data_to_frame_size(audio, enc.lookahead)
|
||||
|
||||
assert len(audio) % BYTES_PER_FRAME == 0
|
||||
|
||||
result = bytearray()
|
||||
framelist = []
|
||||
for i in range(len(audio)//BYTES_PER_FRAME):
|
||||
l = enc.encode_float(bytes(audio[i*BYTES_PER_FRAME:i*BYTES_PER_FRAME+BYTES_PER_FRAME]), frame_size = FRAME_SIZE)
|
||||
code = len(l)
|
||||
if l[0] == 0xfc:
|
||||
l = l[1:]
|
||||
code = (code - 1) | 0x8000
|
||||
framelist.append((i*FRAME_SIZE, len(result)))
|
||||
result.extend(struct.pack('<H', code))
|
||||
result.extend(l)
|
||||
# print(len(l), '%.2x'%l[0])
|
||||
return result, framelist, enc.lookahead, msu_repeat_pos, source_samples
|
||||
|
||||
def convert_to_opuz(filename, repeat):
|
||||
print('Converting %s' % filename)
|
||||
result, framelist, preskip, msu_repeat_pos, songlength = encode_to_msu_opus(filename, 128000)
|
||||
|
||||
# lookup what sample to seek to based on a sample nr
|
||||
def lookup_sample(framelist, sample_nr):
|
||||
last = None
|
||||
for sample, fileoffs in framelist:
|
||||
if sample_nr < sample:
|
||||
break
|
||||
last = sample, fileoffs
|
||||
return last
|
||||
|
||||
def calc_play_ranges(xs):
|
||||
r = []
|
||||
for i, (start, end, repeat_from_here) in enumerate(xs):
|
||||
frame = lookup_sample(framelist, start)
|
||||
r.append((frame[1], end - start, preskip + start - frame[0], start, repeat_from_here))
|
||||
return r
|
||||
|
||||
def serialize_header(xs):
|
||||
r = b'OPUZ' + struct.pack('<I', 0)
|
||||
offs = len(r) + len(xs) * 10
|
||||
for i, (a, b, c, d, e) in enumerate(xs):
|
||||
f = c | (0x4000 if e else 0x0) | (0x8000 if i == len(xs) - 1 else 0)
|
||||
r += struct.pack('<IIH', a + offs, b, f)
|
||||
return r
|
||||
|
||||
# lookup the frame that contains msu_repeat_pos
|
||||
if repeat:
|
||||
play_ranges = [(0, songlength, False), (msu_repeat_pos, songlength, True)]
|
||||
else:
|
||||
play_ranges = [(0, songlength, False)]
|
||||
|
||||
play_ranges = calc_play_ranges(play_ranges)
|
||||
|
||||
header = serialize_header(play_ranges)
|
||||
if filename.endswith('.pcm'):
|
||||
filename = filename[:-4]
|
||||
open(filename + '.opuz', 'wb').write(header + result)
|
||||
|
||||
kMsuTracksWithRepeat = [
|
||||
1,0,1,1,1,1,1,1,0,1,0,1,1,1,1,0,
|
||||
1,1,1,0,1,1,1,1,1,1,1,1,1,0,1,1,
|
||||
1,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,
|
||||
]
|
||||
|
||||
def task(i):
|
||||
convert_to_opuz(r"../../msu/ALttP-msu-Deluxe-%d.pcm" % i, i >= 48 or kMsuTracksWithRepeat[i])
|
||||
|
||||
with concurrent.futures.ThreadPoolExecutor() as executor:
|
||||
results = [executor.submit(task, i) for i in range(1, 115)]
|
||||
|
||||
for future in concurrent.futures.as_completed(results):
|
||||
result = future.result()
|
||||
|
||||
@@ -445,7 +445,7 @@ void PreOverworld_LoadProperties() { // 8283c7
|
||||
if (dr != 0 && dr != 0xe1) {
|
||||
dark:
|
||||
xt = 0xf3;
|
||||
if (queued_music_control == 0xf2)
|
||||
if (buffer_for_playing_songs == 0xf2)
|
||||
goto setsong;
|
||||
xt = sram_progress_indicator < 2 ? 3 : 2;
|
||||
}
|
||||
@@ -456,7 +456,7 @@ dark:
|
||||
xt = 4;
|
||||
}
|
||||
setsong:
|
||||
queued_music_control = xt;
|
||||
buffer_for_playing_songs = xt;
|
||||
DecompressAnimatedOverworldTiles(ow_anim_tiles);
|
||||
InitializeTilesets();
|
||||
OverworldLoadScreensPaletteSet();
|
||||
@@ -605,11 +605,11 @@ void Dungeon_PrepExitWithSpotlight() { // 8299ca
|
||||
Ancilla_TerminateWaterfallSplashes();
|
||||
link_y_coord_exit = link_y_coord;
|
||||
}
|
||||
uint8 m = ZeldaGetEntranceMusicTrack(which_entrance);
|
||||
uint8 m = GetEntranceMusicTrack(which_entrance);
|
||||
if (m != 3 || (m = sram_progress_indicator) >= 2) {
|
||||
if (m != 0xf2) // fade to 0x40
|
||||
m = 0xf1; // fade to zero
|
||||
else if (music_unk1 == 12)
|
||||
if (m != 0xf2)
|
||||
m = 0xf1;
|
||||
else if (music_unk1 == 0xc)
|
||||
m = 7;
|
||||
music_control = m;
|
||||
}
|
||||
@@ -813,21 +813,21 @@ after:
|
||||
y >>= 1;
|
||||
Dungeon_ResetTorchBackgroundAndPlayerInner();
|
||||
map16_load_src_off &= kSwitchAreaTab0[y];
|
||||
int pushed = ((current_area_of_player + kSwitchAreaTab3[y]) >> 1) & 0x3f;
|
||||
map16_load_src_off += kSwitchAreaTab1[y * 64 + pushed];
|
||||
uint16 pushed = current_area_of_player + kSwitchAreaTab3[y];
|
||||
map16_load_src_off += kSwitchAreaTab1[(y * 128 | pushed) >> 1];
|
||||
|
||||
uint8 old_screen = overworld_screen_index;
|
||||
if (old_screen == 0x2a)
|
||||
sound_effect_ambient = 0x80;
|
||||
|
||||
uint8 new_area = kOverworldAreaHeads[pushed] | savegame_is_darkworld;
|
||||
uint8 new_area = kOverworldAreaHeads[pushed >> 1] | savegame_is_darkworld;
|
||||
BYTE(overworld_screen_index) = new_area;
|
||||
BYTE(overworld_area_index) = new_area;
|
||||
if (!savegame_is_darkworld || link_item_moon_pearl) {
|
||||
uint8 music = overworld_music[new_area];
|
||||
if ((music & 0xf0) == 0)
|
||||
sound_effect_ambient = 5;
|
||||
if (!ZeldaIsPlayingMusicTrack(music & 0xf))
|
||||
if ((music & 0xf) != music_unk1)
|
||||
music_control = 0xf1;
|
||||
}
|
||||
Overworld_LoadGFXAndScreenSize();
|
||||
@@ -1055,7 +1055,7 @@ void Overworld_StartMosaicTransition() { // 82ae5e
|
||||
switch (subsubmodule_index) {
|
||||
case 0:
|
||||
if (BYTE(overworld_screen_index) != 0x80) {
|
||||
if (!ZeldaIsPlayingMusicTrack(overworld_music[BYTE(overworld_screen_index)] & 0xf))
|
||||
if ((overworld_music[BYTE(overworld_screen_index)] & 0xf) != music_unk1)
|
||||
music_control = 0xf1;
|
||||
}
|
||||
ResetTransitionPropsAndAdvance_ResetInterface();
|
||||
@@ -1202,7 +1202,7 @@ void Module09_FadeBackInFromMosaic() { // 82b0d2
|
||||
if (BYTE(overworld_screen_index) != 0x80 && BYTE(overworld_screen_index) != 0x2a) {
|
||||
uint8 m = overworld_music[BYTE(overworld_screen_index)];
|
||||
sound_effect_ambient = (m >> 4) ? (m >> 4) : 5;
|
||||
if (!ZeldaIsPlayingMusicTrack(m & 0xf))
|
||||
if ((m & 0xf) != music_unk1)
|
||||
music_control = (m & 0xf);
|
||||
}
|
||||
submodule_index = 8;
|
||||
@@ -1449,7 +1449,7 @@ void Module09_2E_Whirlpool() { // 82b40f
|
||||
overworld_palette_aux_or_main = 0;
|
||||
Palette_Load_SpriteMain();
|
||||
Palette_Load_SpriteEnvironment();
|
||||
Palette_Load_Sp0L();
|
||||
Palette_Load_SpritePal0Left();
|
||||
Palette_Load_HUD();
|
||||
Palette_Load_OWBGMain();
|
||||
uint8 sc = overworld_screen_index;
|
||||
@@ -1506,16 +1506,14 @@ void Module09_2A_00_ScrollToLand() { // 82b532
|
||||
uint16 x = link_x_coord, xd = 0;
|
||||
if (x != link_x_coord_cached) {
|
||||
int16 d = (x > link_x_coord_cached) ? -1 : 1;
|
||||
if ((x += d) != link_x_coord_cached)
|
||||
x += d;
|
||||
((x += d) != link_x_coord_cached) && (x += d);
|
||||
xd = x - link_x_coord;
|
||||
link_x_coord = x;
|
||||
}
|
||||
uint16 y = link_y_coord, yd = 0;
|
||||
if (y != link_y_coord_cached) {
|
||||
int16 d = (y > link_y_coord_cached) ? -1 : 1;
|
||||
if ((y += d) != link_y_coord_cached)
|
||||
y += d;
|
||||
((y += d) != link_y_coord_cached) && (y += d);
|
||||
yd = y - link_y_coord;
|
||||
link_y_coord = y;
|
||||
}
|
||||
@@ -2449,20 +2447,23 @@ void Overworld_DecompressAndDrawAllQuadrants() { // 82f54a
|
||||
}
|
||||
|
||||
static const uint8 *GetOverworldHibytes(int i) {
|
||||
return kOverworld_Hibytes_Comp(i).ptr;
|
||||
return kOverworld_Hibytes_Comp + *(uint32 *)(kOverworld_Hibytes_Comp + i * 4);
|
||||
}
|
||||
|
||||
static const uint8 *GetOverworldLobytes(int i) {
|
||||
return kOverworld_Lobytes_Comp(i).ptr;
|
||||
return kOverworld_Lobytes_Comp + *(uint32 *)(kOverworld_Lobytes_Comp + i * 4);
|
||||
}
|
||||
|
||||
|
||||
void Overworld_DecompressAndDrawOneQuadrant(uint16 *dst, int screen) { // 82f595
|
||||
Decompress_bank02(&g_ram[0x14400], GetOverworldHibytes(screen));
|
||||
int rv;
|
||||
|
||||
|
||||
rv = Decompress_bank02(&g_ram[0x14400], GetOverworldHibytes(screen));
|
||||
for (int i = 0; i < 256; i++)
|
||||
g_ram[0x14001 + i * 2] = g_ram[0x14400 + i];
|
||||
|
||||
Decompress_bank02(&g_ram[0x14400], GetOverworldLobytes(screen));
|
||||
rv = Decompress_bank02(&g_ram[0x14400], GetOverworldLobytes(screen));
|
||||
for (int i = 0; i < 256; i++)
|
||||
g_ram[0x14000 + i * 2] = g_ram[0x14400 + i];
|
||||
|
||||
@@ -40,10 +40,7 @@ include $(DEVKITPRO)/libnx/switch_rules
|
||||
SRC_DIR := ../..
|
||||
TARGET := zelda3
|
||||
BUILD := bin
|
||||
SOURCES := $(SRC_DIR) $(SRC_DIR)/snes $(SRC_DIR)/third_party/gl_core $(SRC_DIR)/third_party/opus-1.3.1-stripped
|
||||
|
||||
CFILES := $(wildcard $(SRC_DIR)/*.c $(SRC_DIR)/snes/*.c) $(SRC_DIR)/third_party/gl_core/gl_core_3_1.c $(SRC_DIR)/third_party/opus-1.3.1-stripped/opus_decoder_amalgam.c
|
||||
|
||||
SOURCES := $(SRC_DIR) $(SRC_DIR)/snes
|
||||
INCLUDES := include
|
||||
APP_TITLE := The Legend of Zelda: A Link to the Past
|
||||
APP_AUTHOR := snesrev & Lywx
|
||||
@@ -54,7 +51,7 @@ APP_VERSION := $(shell git rev-parse --short HEAD) $(shell git rev-parse --abbr
|
||||
#---------------------------------------------------------------------------------
|
||||
ARCH := -march=armv8-a+crc+crypto -mtune=cortex-a57 -mtp=soft -fPIE
|
||||
|
||||
CFLAGS := -g -Wall -O2 -ffunction-sections -Wno-parentheses \
|
||||
CFLAGS := -g -Wall -O2 -ffunction-sections \
|
||||
$(ARCH) $(DEFINES)
|
||||
|
||||
CFLAGS += -D__SWITCH__ $(INCLUDE) -DSTBI_NO_THREAD_LOCALS `sdl2-config --cflags`
|
||||
@@ -84,15 +81,13 @@ export OUTPUT := $(CURDIR)/$(TARGET)
|
||||
export TOPDIR := $(CURDIR)
|
||||
|
||||
export VPATH := $(foreach dir,$(SOURCES),$(CURDIR)/$(dir)) \
|
||||
$(foreach dir,$(DATA),$(CURDIR)/$(dir)) \
|
||||
|
||||
$(foreach dir,$(DATA),$(CURDIR)/$(dir))
|
||||
|
||||
export DEPSDIR := $(CURDIR)/$(BUILD)
|
||||
|
||||
#CFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.c)))
|
||||
#CFILES := $(wildcard ../../*.c ../../snes/*.c) ../../third_party/gl_core/gl_core_3_1.c ../../third_party/opus-1.3.1-stripped/opus_decoder_amalgam.c
|
||||
#CPPFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.cpp)))
|
||||
#SFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.s)))
|
||||
CFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.c)))
|
||||
CPPFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.cpp)))
|
||||
SFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.s)))
|
||||
BINFILES := $(foreach dir,$(DATA),$(notdir $(wildcard $(dir)/*.*)))
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
@@ -102,7 +97,7 @@ export LD := $(CXX)
|
||||
#---------------------------------------------------------------------------------
|
||||
|
||||
export OFILES_BIN := $(addsuffix .o,$(BINFILES))
|
||||
export OFILES_SRC := $(CPPFILES:.cpp=.o) $(notdir $(CFILES:.c=.o)) $(SFILES:.s=.o)
|
||||
export OFILES_SRC := $(CPPFILES:.cpp=.o) $(CFILES:.c=.o) $(SFILES:.s=.o)
|
||||
export OFILES := $(OFILES_BIN) $(OFILES_SRC)
|
||||
export HFILES_BIN := $(addsuffix .h,$(subst .,_,$(BINFILES)))
|
||||
|
||||
@@ -162,7 +157,6 @@ endif
|
||||
all: $(BUILD)
|
||||
|
||||
$(BUILD):
|
||||
@echo $(CFILES) ...
|
||||
@[ -d $@ ] || mkdir -p $@
|
||||
@$(MAKE) --no-print-directory -C $(BUILD) -f $(CURDIR)/Makefile
|
||||
|
||||
@@ -221,4 +215,4 @@ $(OFILES_SRC) : $(HFILES_BIN)
|
||||
|
||||
#---------------------------------------------------------------------------------------
|
||||
endif
|
||||
#---------------------------------------------------------------------------------------
|
||||
#---------------------------------------------------------------------------------------
|
||||
|
Before Width: | Height: | Size: 33 KiB After Width: | Height: | Size: 33 KiB |
|
Before Width: | Height: | Size: 228 KiB After Width: | Height: | Size: 228 KiB |
@@ -27,7 +27,7 @@ LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
|
||||
|
||||
1 TEXTINCLUDE
|
||||
BEGIN
|
||||
"src\\platform\\win32\\resource.h\0"
|
||||
"platform\\win32\\resource.h\0"
|
||||
END
|
||||
|
||||
2 TEXTINCLUDE
|
||||
@@ -52,7 +52,7 @@ END
|
||||
|
||||
// Icon with lowest ID value placed first to ensure application icon
|
||||
// remains consistent on all systems.
|
||||
IDI_ICON1 ICON "src\\platform\\win32\\triforce.ico"
|
||||
IDI_ICON1 ICON "platform\\win32\\triforce.ico"
|
||||
|
||||
#endif // English (United States) resources
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
@@ -13,8 +13,6 @@
|
||||
#include "player_oam.h"
|
||||
#include "sprite_main.h"
|
||||
|
||||
static bool g_ApplyLinksMovementToCamera_called;
|
||||
|
||||
static const uint8 kSpinAttackDelays[] = { 1, 0, 0, 0, 0, 3, 0, 0, 1, 0, 3, 3, 3, 3, 4, 4, 1, 5 };
|
||||
static const uint8 kFireBeamSounds[] = { 1, 2, 3, 4, 0, 9, 18, 27 };
|
||||
static const int8 kTagalongArr1[] = { -1, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
|
||||
@@ -77,7 +75,6 @@ static const uint8 kLinkItem_MagicCosts[] = { 16, 8, 4, 32, 16, 8, 8, 4, 2, 8, 4
|
||||
static const uint8 kBombosAnimDelays[] = { 5, 5, 5, 5, 5, 5, 5, 5, 3, 3, 3, 3, 3, 7, 1, 1, 1, 1, 1, 13 };
|
||||
static const uint8 kBombosAnimStates[] = { 0, 1, 2, 3, 0, 1, 2, 3, 8, 9, 10, 11, 12, 10, 8, 13, 14, 15, 16, 17 };
|
||||
static const uint8 kEtherAnimDelays[] = { 5, 5, 5, 5, 5, 5, 5, 5, 7, 7, 3, 3 };
|
||||
static const uint8 kEtherAnimDelaysNoFlash[] = { 5, 5, 5, 5, 5, 5, 5, 5, 7, 7, 24, 24 };
|
||||
static const uint8 kEtherAnimStates[] = { 0, 1, 2, 3, 0, 1, 2, 3, 4, 5, 6, 7 };
|
||||
static const uint8 kQuakeAnimDelays[] = { 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 19 };
|
||||
static const uint8 kQuakeAnimStates[] = { 0, 1, 2, 3, 0, 1, 2, 3, 18, 19, 20, 22 };
|
||||
@@ -180,6 +177,7 @@ void Link_ControlHandler() { // 87807f
|
||||
}
|
||||
if (link_player_handler_state)
|
||||
Player_CheckHandleCapeStuff();
|
||||
|
||||
kPlayerHandlers[link_player_handler_state]();
|
||||
}
|
||||
|
||||
@@ -234,29 +232,20 @@ void HandleLink_From1D() { // 878130
|
||||
}
|
||||
|
||||
void PlayerHandler_00_Ground_3() { // 8781a0
|
||||
g_ApplyLinksMovementToCamera_called = false;
|
||||
|
||||
link_z_coord = 0xffff;
|
||||
link_actual_vel_z = 0xff;
|
||||
link_recoilmode_timer = 0;
|
||||
|
||||
if (!Link_HandleToss()) {
|
||||
Link_HandleAPress();
|
||||
if ((link_state_bits | link_grabbing_wall) == 0 && link_unk_master_sword == 0 && link_player_handler_state != kPlayerState_StartDash) {
|
||||
if ((link_state_bits | link_grabbing_wall) == 0 && link_unk_master_sword == 0 && link_player_handler_state != 17) {
|
||||
Link_HandleYItem();
|
||||
// Ensure we're not handling potions. Things further
|
||||
// down don't assume this and change the module indexes randomly.
|
||||
// This also fixes a bug where bombos, ether, quake get aborted if you use spin attack at the same time.
|
||||
if ((enhanced_features0 & kFeatures0_MiscBugFixes) && (
|
||||
main_module_index == 14 && submodule_index != 2 ||
|
||||
link_player_handler_state == kPlayerState_Bombos ||
|
||||
link_player_handler_state == kPlayerState_Ether ||
|
||||
link_player_handler_state == kPlayerState_Quake))
|
||||
goto getout_clear_vel;
|
||||
if (sram_progress_indicator != 0) {
|
||||
Link_HandleSwordCooldown();
|
||||
if (link_player_handler_state == 3)
|
||||
goto getout_clear_vel;
|
||||
if (link_player_handler_state == 3) {
|
||||
link_x_vel = link_y_vel = 0;
|
||||
goto getout_dostuff;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -295,7 +284,7 @@ void PlayerHandler_00_Ground_3() { // 8781a0
|
||||
if ((dir = (force_move_any_direction & 0xf)) == 0) {
|
||||
if (link_grabbing_wall & 2)
|
||||
goto endif_3;
|
||||
if ((dir = (joypad1H_last & kJoypadH_AnyDir)) == 0) {
|
||||
if ((dir = (joypad1H_last & 0xf)) == 0) {
|
||||
link_x_vel = 0;
|
||||
link_y_vel = 0;
|
||||
link_direction = 0;
|
||||
@@ -323,18 +312,11 @@ endif_3:
|
||||
Link_HandleVelocity();
|
||||
Link_HandleCardinalCollision();
|
||||
Link_HandleMovingAnimation_FullLongEntry();
|
||||
if (link_unk_master_sword) getout_clear_vel: {
|
||||
if (link_unk_master_sword)
|
||||
link_y_vel = link_x_vel = 0;
|
||||
}
|
||||
|
||||
getout_dostuff:
|
||||
fallhole_var1 = 0;
|
||||
|
||||
// HandleIndoorCameraAndDoors must not be called twice in the same frame,
|
||||
// this might mess up camera positioning. For example when using spin attack
|
||||
// in between bumpers.
|
||||
if (g_ApplyLinksMovementToCamera_called && (enhanced_features0 & kFeatures0_MiscBugFixes))
|
||||
return;
|
||||
|
||||
HandleIndoorCameraAndDoors();
|
||||
}
|
||||
|
||||
@@ -445,7 +427,7 @@ void Link_TempBunny_Func2() { // 8783fa
|
||||
ResetAllAcceleration();
|
||||
Link_HandleYItem();
|
||||
uint8 dir;
|
||||
if (!(dir = force_move_any_direction & 0xf) && !(dir = joypad1H_last & kJoypadH_AnyDir)) {
|
||||
if (!(dir = force_move_any_direction & 0xf) && !(dir = joypad1H_last & 0xf)) {
|
||||
link_x_vel = link_y_vel = 0;
|
||||
link_direction = link_direction_last = 0;
|
||||
link_animation_steps = 0;
|
||||
@@ -523,7 +505,7 @@ void LinkState_HoldingBigRock() { // 878481
|
||||
}
|
||||
|
||||
Link_HandleAPress();
|
||||
if (!(joypad1H_last & kJoypadH_AnyDir)) {
|
||||
if (!(joypad1H_last & 0xf)) {
|
||||
link_y_vel = 0;
|
||||
link_x_vel = 0;
|
||||
link_direction = 0;
|
||||
@@ -533,7 +515,7 @@ void LinkState_HoldingBigRock() { // 878481
|
||||
link_timer_push_get_tired = 32;
|
||||
link_timer_jump_ledge = 19;
|
||||
} else {
|
||||
link_direction = joypad1H_last & kJoypadH_AnyDir;
|
||||
link_direction = joypad1H_last & 0xf;
|
||||
if (link_direction != link_direction_last) {
|
||||
link_direction_last = link_direction;
|
||||
link_subpixel_x = 0;
|
||||
@@ -755,7 +737,7 @@ lbl_jump_into_middle:
|
||||
if ((link_direction & 0xc) == 0)
|
||||
link_actual_vel_y = 0;
|
||||
}
|
||||
Link_MovePosition(); // not
|
||||
LinkHop_FindArbitraryLandingSpot(); // not
|
||||
timer_running:
|
||||
if (link_player_handler_state != 6) {
|
||||
Link_HandleCardinalCollision(); // not
|
||||
@@ -765,7 +747,7 @@ timer_running:
|
||||
if (BYTE(link_z_coord) == 0 || BYTE(link_z_coord) >= 0xe0) {
|
||||
Player_TileDetectNearby();
|
||||
if ((tiledetect_pit_tile & 0xf) == 0xf) {
|
||||
link_player_handler_state = kPlayerState_FallingIntoHole;
|
||||
link_player_handler_state = 1;
|
||||
link_speed_setting = 4;
|
||||
}
|
||||
}
|
||||
@@ -809,15 +791,13 @@ void LinkHop_HoppingSouthOW() { // 87894e
|
||||
link_actual_vel_z_copy = link_actual_vel_z_copy_mirror;
|
||||
link_z_coord = link_z_coord_mirror;
|
||||
link_actual_vel_z -= 2;
|
||||
Link_MovePosition();
|
||||
LinkHop_FindArbitraryLandingSpot();
|
||||
if (sign8(link_actual_vel_z)) {
|
||||
if (link_actual_vel_z < 0xa0)
|
||||
link_actual_vel_z = 0xa0;
|
||||
if (link_z_coord >= 0xfff0) {
|
||||
link_z_coord = 0;
|
||||
Link_SplashUponLanding();
|
||||
// This is the place that caused the water walking bug after bonk,
|
||||
// player_near_pit_state was not reset.
|
||||
if (player_near_pit_state)
|
||||
link_player_handler_state = kPlayerState_FallingIntoHole;
|
||||
if (link_player_handler_state != kPlayerState_Swimming &&
|
||||
@@ -847,7 +827,7 @@ void LinkState_HandlingJump() { // 878a05
|
||||
link_actual_vel_z_copy = link_actual_vel_z_copy_mirror;
|
||||
BYTE(link_z_coord) = link_z_coord_mirror;
|
||||
link_actual_vel_z -= 2;
|
||||
Link_MovePosition();
|
||||
LinkHop_FindArbitraryLandingSpot();
|
||||
if (sign8(link_actual_vel_z)) {
|
||||
if (link_actual_vel_z < 0xa0)
|
||||
link_actual_vel_z = 0xa0;
|
||||
@@ -1034,7 +1014,7 @@ finish:
|
||||
void LinkState_HoppingDiagonallyUpOW() { // 878dc6
|
||||
draw_water_ripples_or_grass = 0;
|
||||
Player_ChangeZ(2);
|
||||
Link_MovePosition();
|
||||
LinkHop_FindArbitraryLandingSpot();
|
||||
if (sign8(link_z_coord)) {
|
||||
Link_SplashUponLanding();
|
||||
if (link_player_handler_state != kPlayerState_Swimming && !link_is_in_deep_water)
|
||||
@@ -1061,15 +1041,9 @@ void LinkState_HoppingDiagonallyDownOW() { // 878e15
|
||||
LinkHop_FindLandingSpotDiagonallyDown();
|
||||
link_x_coord = old_x;
|
||||
|
||||
static const uint8 kLedgeVelX[] = {
|
||||
4, 4, 4, 10, 10, 10, 11, 18,
|
||||
18, 18, 20, 20, 20, 20, 22, 22,
|
||||
26, 26, 26, 26, 28, 28, 28, 28
|
||||
};
|
||||
static const uint8 kLedgeVelX[] = { 4, 4, 4, 10, 10, 10, 11, 18, 18, 18, 20, 20, 20, 20, 22, 22, 26, 26, 26, 26, 28, 28, 28, 28 };
|
||||
|
||||
int t = (uint16)(link_y_coord - link_y_coord_original);
|
||||
// Fix out of bounds read
|
||||
int8 velx = kLedgeVelX[IntMin(t >> 3, 23)];
|
||||
int8 velx = kLedgeVelX[(uint16)(link_y_coord - link_y_coord_original) >> 3];
|
||||
link_actual_vel_x = (dir != 2) ? velx : -velx;
|
||||
if (!player_is_indoors)
|
||||
link_is_on_lower_level = 2;
|
||||
@@ -1193,7 +1167,7 @@ void LinkState_Dashing() { // 878f86
|
||||
follower_indicator = kTagalongArr2[follower_indicator];
|
||||
} else {
|
||||
index_of_dashing_sfx = 0;
|
||||
if (!(joypad1L_last & kJoypadL_A)) {
|
||||
if (!(joypad1L_last & 0x80)) {
|
||||
link_animation_steps = 0;
|
||||
link_countdown_for_dash = 0;
|
||||
link_speed_setting = 0;
|
||||
@@ -1208,7 +1182,7 @@ void LinkState_Dashing() { // 878f86
|
||||
link_dash_ctr = 64;
|
||||
link_speed_setting = 16;
|
||||
uint8 dir;
|
||||
if (button_mask_b_y & 0x80 || is_standing_in_doorway || (dir = joypad1H_last & kJoypadH_AnyDir) == 0)
|
||||
if (button_mask_b_y & 0x80 || is_standing_in_doorway || (dir = joypad1H_last & 0xf) == 0)
|
||||
dir = kDashTab2[link_direction_facing >> 1];
|
||||
link_some_direction_bits = link_direction = link_direction_last = dir;
|
||||
link_moving_against_diag_tile = 0;
|
||||
@@ -1252,12 +1226,12 @@ void LinkState_Dashing() { // 878f86
|
||||
bool want_stop_dash = false;
|
||||
|
||||
if (enhanced_features0 & kFeatures0_TurnWhileDashing) {
|
||||
if (!(joypad1L_last & kJoypadL_A)) {
|
||||
if (!(joypad1L_last & 0x80)) {
|
||||
link_countdown_for_dash = 0x11;
|
||||
want_stop_dash = true;
|
||||
} else {
|
||||
static const uint8 kDashCtrlsToDir[16] = { 0, 1, 2, 0, 4, 4, 4, 0, 8, 8, 8, 0, 0, 0, 0, 0 };
|
||||
uint8 t = kDashCtrlsToDir[joypad1H_last & kJoypadH_AnyDir];
|
||||
uint8 t = kDashCtrlsToDir[joypad1H_last & 0xf];
|
||||
if (t != 0 && t != link_direction_last) {
|
||||
link_direction = link_direction_last = t;
|
||||
link_some_direction_bits = t;
|
||||
@@ -1265,7 +1239,7 @@ void LinkState_Dashing() { // 878f86
|
||||
}
|
||||
}
|
||||
} else {
|
||||
want_stop_dash = (joypad1H_last & kJoypadH_AnyDir) && (joypad1H_last & kJoypadH_AnyDir) != kDashTab2[link_direction_facing >> 1];
|
||||
want_stop_dash = (joypad1H_last & 0xf) && (joypad1H_last & 0xf) != kDashTab2[link_direction_facing >> 1];
|
||||
}
|
||||
|
||||
if (want_stop_dash) {
|
||||
@@ -1276,10 +1250,6 @@ void LinkState_Dashing() { // 878f86
|
||||
LinkState_ExitingDash();
|
||||
return;
|
||||
}
|
||||
|
||||
if (link_speed_setting == 0 && (enhanced_features0 & kFeatures0_TurnWhileDashing))
|
||||
link_speed_setting = 16;
|
||||
|
||||
uint8 dir = force_move_any_direction & 0xf;
|
||||
if (dir == 0)
|
||||
dir = kDashTab2[link_direction_facing >> 1];
|
||||
@@ -1294,7 +1264,7 @@ void LinkState_Dashing() { // 878f86
|
||||
|
||||
void LinkState_ExitingDash() { // 87915e
|
||||
CacheCameraPropertiesIfOutdoors();
|
||||
if (joypad1H_last & kJoypadH_AnyDir || link_countdown_for_dash >= 16) {
|
||||
if (joypad1H_last & 0xf || link_countdown_for_dash >= 16) {
|
||||
link_countdown_for_dash = 0;
|
||||
link_speed_setting = 0;
|
||||
link_player_handler_state = kPlayerState_Ground;
|
||||
@@ -1389,29 +1359,19 @@ void LinkState_Pits() { // 8792d3
|
||||
} else {
|
||||
if (!link_is_running)
|
||||
goto aux_state;
|
||||
// If you use a turbo controller to perfectly spam the dash button,
|
||||
// the check for Link being in a hole is endlessly skipped and you
|
||||
// can levitate across chasms.
|
||||
// Fix this by ensuring that the dash button is held down before proceeding to the dash state.
|
||||
if (link_countdown_for_dash &&
|
||||
(!(enhanced_features0 & kFeatures0_MiscBugFixes) || (joypad1L_last & kJoypadL_A))) {
|
||||
if (link_countdown_for_dash) {
|
||||
LinkState_Dashing();
|
||||
return;
|
||||
}
|
||||
if (joypad1H_last & kJoypadH_AnyDir && !(joypad1H_last & kJoypadH_AnyDir & link_direction)) {
|
||||
if (joypad1H_last & 0xf && !(joypad1H_last & 0xf & link_direction)) {
|
||||
Link_CancelDash();
|
||||
aux_state:
|
||||
if (link_auxiliary_state != 1)
|
||||
link_direction = joypad1H_last & kJoypadH_AnyDir;
|
||||
link_direction = joypad1H_last & 0xF;
|
||||
}
|
||||
}
|
||||
TileDetect_MainHandler(4);
|
||||
if (!(tiledetect_pit_tile & 1)) {
|
||||
// Reset player_near_pit_state if we're no longer near a hole.
|
||||
// This fixes a bug where you could walk on water
|
||||
if (enhanced_features0 & kFeatures0_MiscBugFixes)
|
||||
player_near_pit_state = 0;
|
||||
|
||||
if (link_is_running) {
|
||||
LinkState_Dashing();
|
||||
return;
|
||||
@@ -1484,8 +1444,6 @@ endif_1:
|
||||
ApplyLinksMovementToCamera();
|
||||
return;
|
||||
}
|
||||
|
||||
// Initiate fall down
|
||||
if (player_near_pit_state != 2) {
|
||||
if (link_item_moon_pearl) {
|
||||
link_need_for_poof_for_transform = 0;
|
||||
@@ -1589,11 +1547,6 @@ void HandleDungeonLandingFromPit() { // 879520
|
||||
if (tiledetect_which_y_pos[0] >= link_y_coord)
|
||||
return;
|
||||
}
|
||||
//exploration glitch could also be armed without quitting
|
||||
//by jumping off a dungeon ledge into an access pit
|
||||
if (enhanced_features0 & kFeatures0_MiscBugFixes) {
|
||||
about_to_jump_off_ledge = 0;
|
||||
}
|
||||
link_y_coord = tiledetect_which_y_pos[0];
|
||||
link_animation_steps = 0;
|
||||
link_speed_modifier = 0;
|
||||
@@ -1680,7 +1633,7 @@ void PlayerHandler_04_Swimming() { // 87963b
|
||||
|
||||
if (!link_swim_hard_stroke) {
|
||||
uint8 t;
|
||||
if (!(swimcoll_var7[0] | swimcoll_var7[1]) || (t = ((filtered_joypad_L & kJoypadL_A) | filtered_joypad_H) & 0xc0) == 0) {
|
||||
if (!(swimcoll_var7[0] | swimcoll_var7[1]) || (t = ((filtered_joypad_L & 0x80) | filtered_joypad_H) & 0xc0) == 0) {
|
||||
Link_HandleSwimMovements();
|
||||
return;
|
||||
}
|
||||
@@ -1704,7 +1657,7 @@ void PlayerHandler_04_Swimming() { // 87963b
|
||||
void Link_HandleSwimMovements() { // 879715
|
||||
uint8 t;
|
||||
|
||||
if (!(t = force_move_any_direction & 0xf) && !(t = joypad1H_last & kJoypadH_AnyDir)) {
|
||||
if (!(t = force_move_any_direction & 0xf) && !(t = joypad1H_last & 0xf)) {
|
||||
link_y_vel = link_x_vel = 0;
|
||||
Link_FlagMaxAccels();
|
||||
if (link_flag_moving) {
|
||||
@@ -1761,7 +1714,7 @@ void Link_SetIceMaxAccel() { // 8797a6
|
||||
}
|
||||
|
||||
void Link_SetMomentum() { // 8797c7
|
||||
uint8 joy = joypad1H_last & kJoypadH_AnyDir;
|
||||
uint8 joy = joypad1H_last & 0xf;
|
||||
uint8 mask = 12, bit = 8;
|
||||
for (int i = 0; i < 2; i++, mask >>= 2, bit >>= 2) {
|
||||
if (joy & mask) {
|
||||
@@ -1991,20 +1944,16 @@ void Link_HandleYItem() { // 879b0e
|
||||
}
|
||||
|
||||
uint8 old_down = joypad1H_last, old_pressed = filtered_joypad_H, old_bottle = link_item_bottle_index;
|
||||
if ((link_item_in_hand | link_position_mode) == 0 && !(old_down & kJoypadH_Y)) {
|
||||
// Is any special key held down?
|
||||
int btn_index = GetCurrentItemButtonIndex();
|
||||
if (btn_index != 0) {
|
||||
uint8 *cur_item_ptr = GetCurrentItemButtonPtr(btn_index);
|
||||
if (*cur_item_ptr) {
|
||||
if (*cur_item_ptr >= kHudItem_Bottle1)
|
||||
link_item_bottle_index = *cur_item_ptr - kHudItem_Bottle1 + 1;
|
||||
item = Hud_LookupInventoryItem(*cur_item_ptr);
|
||||
// Pretend it's actually Y that's down
|
||||
joypad1H_last = old_down | kJoypadH_Y;
|
||||
static const uint8 kButtonIndexKeys[4] = { 0, kJoypadL_X, kJoypadL_L, kJoypadL_R };
|
||||
filtered_joypad_H = old_pressed | ((filtered_joypad_L & kButtonIndexKeys[btn_index]) ? kJoypadH_Y : 0);
|
||||
}
|
||||
if ((link_item_in_hand | link_position_mode) == 0) {
|
||||
// Is X held down?
|
||||
if (joypad1L_last & 0x40 && !(old_down & 0x40) && hud_cur_item_x != 0) {
|
||||
|
||||
if (hud_cur_item_x >= kHudItem_Bottle1)
|
||||
link_item_bottle_index = hud_cur_item_x - kHudItem_Bottle1 + 1;
|
||||
item = Hud_LookupInventoryItem(hud_cur_item_x);
|
||||
// Pretend it's actually Y that's down
|
||||
joypad1H_last = old_down | 0x40;
|
||||
filtered_joypad_H = old_pressed | filtered_joypad_L & 0x40;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2130,6 +2079,10 @@ void Link_APress_PerformBasic(uint8 action_x2) { // 879c5f
|
||||
default:
|
||||
assert(0);
|
||||
}
|
||||
|
||||
// Zelda vanilla bug: Disallow dragging while in cape mode, it uses no magic.
|
||||
if (link_cape_mode && enhanced_features0 & kFeatures0_MiscBugFixes)
|
||||
link_grabbing_wall = 0;
|
||||
}
|
||||
|
||||
void HandleSwordSfxAndBeam() { // 879c66
|
||||
@@ -2171,7 +2124,7 @@ void Link_CheckForSwordSwing() { // 879cd9
|
||||
link_animation_steps = 0;
|
||||
}
|
||||
|
||||
if (!(joypad1H_last & kJoypadH_B))
|
||||
if (!(joypad1H_last & 0x80))
|
||||
button_mask_b_y |= 1;
|
||||
HaltLinkWhenUsingItems();
|
||||
link_direction &= ~0xf;
|
||||
@@ -2186,7 +2139,7 @@ void Link_CheckForSwordSwing() { // 879cd9
|
||||
AncillaAdd_SwordSwingSparkle(0x26, 4);
|
||||
if (link_sword_type != 0 && link_sword_type != 0xff)
|
||||
TileDetect_MainHandler(link_sword_type == 1 ? 1 : 6);
|
||||
} else if (button_b_frames >= 4 && (button_mask_b_y & 1) && (joypad1H_last & kJoypadH_B)) {
|
||||
} else if (button_b_frames >= 4 && (button_mask_b_y & 1) && (joypad1H_last & 0x80)) {
|
||||
button_mask_b_y &= ~1;
|
||||
HandleSwordSfxAndBeam();
|
||||
return;
|
||||
@@ -2196,7 +2149,7 @@ void Link_CheckForSwordSwing() { // 879cd9
|
||||
}
|
||||
|
||||
void HandleSwordControls() { // 879d72
|
||||
if (joypad1H_last & kJoypadH_B) {
|
||||
if (joypad1H_last & 0x80) {
|
||||
Player_Sword_SpinAttackJerks_HoldDown();
|
||||
} else {
|
||||
if (link_spin_attack_step_counter < 48) {
|
||||
@@ -2312,7 +2265,7 @@ void LinkItem_Hammer() { // 879f7b
|
||||
if (link_item_in_hand & 0x10)
|
||||
return;
|
||||
if (!(button_mask_b_y & 0x40)) {
|
||||
if (is_standing_in_doorway || !(filtered_joypad_H & kJoypadH_Y))
|
||||
if (is_standing_in_doorway || !(filtered_joypad_H & 0x40))
|
||||
return;
|
||||
button_mask_b_y |= 0x40;
|
||||
link_delay_timer_spin_attack = kHammerAnimDelays[0];
|
||||
@@ -2408,7 +2361,7 @@ void LinkItem_Boomerang() { // 87a0bb
|
||||
}
|
||||
|
||||
if (!s0) {
|
||||
link_direction_last = joypad1H_last & kJoypadH_AnyDir;
|
||||
link_direction_last = joypad1H_last & 0xf;
|
||||
} else {
|
||||
link_cant_change_direction |= 1;
|
||||
}
|
||||
@@ -2687,8 +2640,7 @@ void LinkState_UsingEther() { // 87a50f
|
||||
} else if (step_counter_for_spin_attack == 12) {
|
||||
step_counter_for_spin_attack = 10;
|
||||
}
|
||||
const uint8 *table = (enhanced_features0 & kFeatures0_DimFlashes) ? kEtherAnimDelaysNoFlash : kEtherAnimDelays;
|
||||
link_delay_timer_spin_attack = table[step_counter_for_spin_attack];
|
||||
link_delay_timer_spin_attack = kEtherAnimDelays[step_counter_for_spin_attack];
|
||||
state_for_spin_attack = kEtherAnimStates[step_counter_for_spin_attack];
|
||||
if (!byte_7E0324 && step_counter_for_spin_attack == 10) {
|
||||
byte_7E0324 = 1;
|
||||
@@ -2784,7 +2736,7 @@ void LinkState_UsingQuake() { // 87a6d6
|
||||
BYTE(link_z_coord) = link_z_coord_mirror;
|
||||
link_auxiliary_state = 2;
|
||||
Player_ChangeZ(2);
|
||||
Link_MovePosition();
|
||||
LinkHop_FindArbitraryLandingSpot();
|
||||
link_actual_vel_z_mirror = link_actual_vel_z;
|
||||
link_actual_vel_z_copy_mirror = link_actual_vel_z_copy;
|
||||
BYTE(link_z_coord_mirror) = link_z_coord;
|
||||
@@ -2897,7 +2849,7 @@ void LinkState_SpinAttack() { // 87a804
|
||||
state_for_spin_attack = 0;
|
||||
step_counter_for_spin_attack = 0;
|
||||
if (link_player_handler_state != kPlayerState_SpinAttackMotion) {
|
||||
button_mask_b_y = (button_b_frames) ? (joypad1H_last & kJoypadH_B) : 0; // wtf, it's zero,
|
||||
button_mask_b_y = (button_b_frames) ? (joypad1H_last & 0x80) : 0; // wtf, it's zero,
|
||||
}
|
||||
link_player_handler_state = kPlayerState_Ground;
|
||||
} else {
|
||||
@@ -3192,7 +3144,7 @@ void LinkState_Hookshotting() { // 87ab7c
|
||||
}
|
||||
return;
|
||||
loc_87AD49:
|
||||
Link_MovePosition();
|
||||
LinkHop_FindArbitraryLandingSpot();
|
||||
TileDetect_MainHandler(5);
|
||||
if (player_is_indoors) {
|
||||
uint8 x = tiledetect_vertical_ledge >> 4 | tiledetect_vertical_ledge | detection_of_ledge_tiles_horiz_uphoriz;
|
||||
@@ -3245,16 +3197,14 @@ void LinkItem_Cape() { // 87adc1
|
||||
link_direction &= ~0xf;
|
||||
if (!--cape_decrement_counter) {
|
||||
cape_decrement_counter = kCapeDepletionTimers[link_magic_consumption];
|
||||
// Avoid magic underflow if an anti-fairy consumes magic.
|
||||
if (link_magic_power == 0 && (enhanced_features0 & kFeatures0_MiscBugFixes) ||
|
||||
!--link_magic_power) {
|
||||
if (!--link_magic_power) {
|
||||
Link_ForceUnequipCape();
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (sign8(--link_bunny_transform_timer)) {
|
||||
link_bunny_transform_timer = 0;
|
||||
if (filtered_joypad_H & kJoypadH_Y)
|
||||
if (filtered_joypad_H & 0x40)
|
||||
Link_ForceUnequipCape();
|
||||
}
|
||||
}
|
||||
@@ -3287,8 +3237,7 @@ void HaltLinkWhenUsingItems() { // 87ae65
|
||||
}
|
||||
|
||||
void Link_HandleCape_passive_LiftCheck() { // 87ae88
|
||||
//bugfix: grabbing or pulling while wearing cape didn't drain magic
|
||||
if (link_state_bits & 0x80 || (enhanced_features0 & kFeatures0_MiscBugFixes && link_grabbing_wall))
|
||||
if (link_state_bits & 0x80)
|
||||
Player_CheckHandleCapeStuff();
|
||||
}
|
||||
|
||||
@@ -3311,30 +3260,15 @@ void LinkItem_CaneOfSomaria() { // 87aec0
|
||||
if (player_on_somaria_platform || is_standing_in_doorway || !CheckYButtonPress())
|
||||
return;
|
||||
int i = 4;
|
||||
bool did_charge_magic = false;
|
||||
|
||||
while (ancilla_type[i] != 0x2c) {
|
||||
if (--i < 0) {
|
||||
if (!LinkCheckMagicCost(4)) {
|
||||
// If you use the Cane of Somaria with an empty magic meter,
|
||||
// then quickly switch to the mushroom or magic powder after
|
||||
// the "no magic" prompt, you will automatically sprinkle magic powder
|
||||
// despite pressing no button and having no magic.
|
||||
if (enhanced_features0 & kFeatures0_MiscBugFixes)
|
||||
goto out;
|
||||
if (!LinkCheckMagicCost(4))
|
||||
return;
|
||||
}
|
||||
did_charge_magic = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
link_debug_value_2 = 1;
|
||||
if (AncillaAdd_SomariaBlock(0x2c, 1) < 0) {
|
||||
// If you use the Cane of Somaria while two bombs and the boomerang are active,
|
||||
// magic will be refunded instead of used.
|
||||
if (did_charge_magic || !(enhanced_features0 & kFeatures0_MiscBugFixes))
|
||||
Refund_Magic(4);
|
||||
}
|
||||
AncillaAdd_SomariaBlock(44, 1);
|
||||
link_delay_timer_spin_attack = kRodAnimDelays[0];
|
||||
link_animation_steps = 0;
|
||||
player_handler_timer = 0;
|
||||
@@ -3355,9 +3289,8 @@ void LinkItem_CaneOfSomaria() { // 87aec0
|
||||
player_handler_timer = 0;
|
||||
link_delay_timer_spin_attack = 0;
|
||||
link_debug_value_2 = 0;
|
||||
link_position_mode &= ~8;
|
||||
out:
|
||||
button_mask_b_y &= ~0x40;
|
||||
link_position_mode &= ~8;
|
||||
}
|
||||
|
||||
void LinkItem_CaneOfByrna() { // 87af3e
|
||||
@@ -3443,7 +3376,7 @@ void LinkItem_Net() { // 87aff8
|
||||
}
|
||||
|
||||
bool CheckYButtonPress() { // 87b073
|
||||
if (button_mask_b_y & 0x40 || link_incapacitated_timer || !(filtered_joypad_H & kJoypadH_Y))
|
||||
if (button_mask_b_y & 0x40 || link_incapacitated_timer || !(filtered_joypad_H & 0x40))
|
||||
return false;
|
||||
button_mask_b_y |= 0x40;
|
||||
return true;
|
||||
@@ -3508,7 +3441,7 @@ void Link_PerformThrow() { // 87b11c
|
||||
|
||||
flag_is_sprite_to_pick_up = 1;
|
||||
Sprite_SpawnThrowableTerrain(i, pt.x, pt.y);
|
||||
filtered_joypad_L &= ~kJoypadL_A;
|
||||
filtered_joypad_L &= ~0x80;
|
||||
player_handler_timer = 0;
|
||||
}
|
||||
} else {
|
||||
@@ -3556,8 +3489,8 @@ void Link_APress_LiftCarryThrow() { // 87b1ca
|
||||
} else {
|
||||
static const uint8 kLiftTab0[10] = { 8, 24, 8, 24, 8, 32, 6, 8, 13, 13 };
|
||||
static const uint8 kLiftTab1[10] = { 0, 1, 0, 1, 0, 1, 0, 1, 2, 3 };
|
||||
static const uint8 kLiftTab2[29] = { 6, 7, 7, 5, 10, 0, 23, 0, 18, 0, 18, 0, 8, 0, 8, 0, 254, 255, 17, 0,
|
||||
0x54, 0x52, 0x50, 0xFF, 0x51, 0x53, 0x55, 0x56, 0x57 };
|
||||
static const uint8 kLiftTab2[] = { 6, 7, 7, 5, 10, 0, 23, 0, 18, 0, 18, 0, 8, 0, 8, 0, 254, 255, 17, 0,
|
||||
0x54, 0x52, 0x50, 0xFF, 0x51, 0x53, 0x55, 0x56, 0x57};
|
||||
|
||||
if (player_handler_timer != 0) {
|
||||
if (player_handler_timer + 1 != 9) {
|
||||
@@ -3571,14 +3504,13 @@ void Link_APress_LiftCarryThrow() { // 87b1ca
|
||||
link_player_handler_state = 24;
|
||||
flag_is_sprite_to_pick_up = 1;
|
||||
Sprite_SpawnThrowableTerrain((what & 0xf) + 1, pt.x, pt.y);
|
||||
filtered_joypad_L &= ~kJoypadL_A;
|
||||
filtered_joypad_L &= ~0x80;
|
||||
}
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
// fix OOB read triggered when lifting for too long
|
||||
if (some_animation_timer_steps >= sizeof(kLiftTab2) - 1)
|
||||
return;
|
||||
|
||||
// todo: This is an OOB read triggered when lifting for too long
|
||||
some_animation_timer = kLiftTab2[++some_animation_timer_steps];
|
||||
assert(some_animation_timer_steps < arraysize(kLiftTab2));
|
||||
if (some_animation_timer_steps != 3)
|
||||
@@ -3641,7 +3573,7 @@ set:
|
||||
some_animation_timer = kGrabWall_AnimTimer[link_var30d];
|
||||
}
|
||||
|
||||
if (!(joypad1L_last & kJoypadL_A)) {
|
||||
if (!(joypad1L_last & 0x80)) {
|
||||
link_var30d = 0;
|
||||
some_animation_timer_steps = 0;
|
||||
link_grabbing_wall = 0;
|
||||
@@ -3676,7 +3608,7 @@ void Link_APress_StatueDrag() { // 87b389
|
||||
some_animation_timer_steps = kGrabWall_AnimSteps[link_var30d];
|
||||
some_animation_timer = kGrabWall_AnimTimer[link_var30d];
|
||||
skip_set:
|
||||
if (!(joypad1L_last & kJoypadL_A)) {
|
||||
if (!(joypad1L_last & 0x80)) {
|
||||
link_speed_setting = 0;
|
||||
link_is_near_moveable_statue = 0;
|
||||
link_var30d = 0;
|
||||
@@ -3713,7 +3645,7 @@ void LinkState_TreePull() { // 87b416
|
||||
|
||||
if (link_grabbing_wall) {
|
||||
if (!button_mask_b_y) {
|
||||
if (!(joypad1L_last & kJoypadL_A)) {
|
||||
if (!(joypad1L_last & 0x80)) {
|
||||
link_grabbing_wall = 0;
|
||||
link_var30d = 0;
|
||||
some_animation_timer = 2;
|
||||
@@ -3723,7 +3655,7 @@ void LinkState_TreePull() { // 87b416
|
||||
LinkState_Default();
|
||||
return;
|
||||
}
|
||||
if (!(joypad1H_last & kJoypadH_Down))
|
||||
if (!(joypad1H_last & 4))
|
||||
goto out;
|
||||
button_mask_b_y = 4;
|
||||
Ancilla_Sfx2_Near(0x22);
|
||||
@@ -3754,7 +3686,7 @@ reset_to_normal:
|
||||
return;
|
||||
}
|
||||
if (link_var30d == 9) {
|
||||
if (!(filtered_joypad_H & kJoypadH_AnyDir))
|
||||
if (!(filtered_joypad_H & 0xf))
|
||||
goto out2;
|
||||
link_player_handler_state = kPlayerState_Ground;
|
||||
LinkState_Default();
|
||||
@@ -3776,7 +3708,7 @@ reset_to_normal:
|
||||
if (!(link_direction & 0xc))
|
||||
link_actual_vel_y = 0;
|
||||
out:
|
||||
Link_MovePosition();
|
||||
LinkHop_FindArbitraryLandingSpot();
|
||||
out2:
|
||||
Link_HandleCardinalCollision();
|
||||
HandleIndoorCameraAndDoors();
|
||||
@@ -3816,14 +3748,14 @@ void Link_PerformOpenChest() { // 87b574
|
||||
}
|
||||
|
||||
bool Link_CheckNewAPress() { // 87b5c0
|
||||
if (bitfield_for_a_button & 0x80 || link_incapacitated_timer || !(filtered_joypad_L & kJoypadL_A))
|
||||
if (bitfield_for_a_button & 0x80 || link_incapacitated_timer || !(filtered_joypad_L & 0x80))
|
||||
return false;
|
||||
bitfield_for_a_button |= 0x80;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Link_HandleToss() { // 87b5d6
|
||||
if (!(bitfield_for_a_button & 0x80) || !(filtered_joypad_L & kJoypadL_A) || (link_picking_throw_state & 1))
|
||||
if (!(bitfield_for_a_button & 0x80) || !(filtered_joypad_L & 0x80) || (link_picking_throw_state & 1))
|
||||
return false;
|
||||
link_var30d = 0;
|
||||
link_var30e = 0;
|
||||
@@ -3985,7 +3917,7 @@ xy: RunSlopeCollisionChecks_HorizontalFirst();
|
||||
if (st != 19 && st != 8 && st != 9 && st != 10 && st != 3) {
|
||||
Player_TileDetectNearby();
|
||||
if (tiledetect_pit_tile & 0xf) {
|
||||
link_player_handler_state = kPlayerState_FallingIntoHole;
|
||||
link_player_handler_state = 1;
|
||||
if (!link_is_running)
|
||||
link_speed_setting = 4;
|
||||
}
|
||||
@@ -4096,7 +4028,7 @@ void StartMovementCollisionChecks_Y_HandleIndoors() { // 87ba35
|
||||
} // endif_6
|
||||
|
||||
is_standing_in_doorway = 1;
|
||||
link_on_conveyor_belt = 0;
|
||||
byte_7E03F3 = 0;
|
||||
if ((R14 & 0x70) != 0x70) {
|
||||
if (R14 & 5) { // if_7
|
||||
link_moving_against_diag_tile = 0;
|
||||
@@ -4133,7 +4065,7 @@ endif_1b:
|
||||
label_3:
|
||||
|
||||
if ((R14 & 7) == 0 && (R12 & 5) != 0) {
|
||||
link_on_conveyor_belt = 0;
|
||||
byte_7E03F3 = 0;
|
||||
FlagMovingIntoSlopes_Y();
|
||||
if ((link_moving_against_diag_tile & 0xf) != 0)
|
||||
return;
|
||||
@@ -4176,12 +4108,12 @@ label_3:
|
||||
} // endif_12_norupee
|
||||
|
||||
if (tiledetect_var4 & 0x22) {
|
||||
link_on_conveyor_belt = tiledetect_var4 & 0x20 ? 2 : 1;
|
||||
byte_7E03F3 = tiledetect_var4 & 0x20 ? 2 : 1;
|
||||
} else if (tiledetect_var4 & 0x2200) {
|
||||
link_on_conveyor_belt = tiledetect_var4 & 0x2000 ? 4 : 3;
|
||||
byte_7E03F3 = tiledetect_var4 & 0x2000 ? 4 : 3;
|
||||
} else {
|
||||
if (!(bitfield_spike_cactus_tiles & 7) && !(R14 & 2))
|
||||
link_on_conveyor_belt = 0;
|
||||
byte_7E03F3 = 0;
|
||||
} // endif_15
|
||||
|
||||
if ((tiledetect_vertical_ledge & 7) == 7 && RunLedgeHopTimer()) {
|
||||
@@ -4265,7 +4197,7 @@ endif_19:
|
||||
byte_7E005C = 9;
|
||||
link_this_controls_sprite_oam = 0;
|
||||
player_near_pit_state = 1;
|
||||
link_player_handler_state = kPlayerState_FallingIntoHole;
|
||||
link_player_handler_state = 1;
|
||||
return;
|
||||
} // endif_23
|
||||
|
||||
@@ -4406,7 +4338,7 @@ void StartMovementCollisionChecks_Y_HandleOutdoors() { // 87beaf
|
||||
byte_7E005C = 9;
|
||||
link_this_controls_sprite_oam = 0;
|
||||
player_near_pit_state = 1;
|
||||
link_player_handler_state = kPlayerState_FallingIntoHole;
|
||||
link_player_handler_state = 1;
|
||||
}
|
||||
return;
|
||||
}
|
||||
@@ -4782,7 +4714,7 @@ void StartMovementCollisionChecks_X_HandleIndoors() { // 87c4ff
|
||||
} // endif_6
|
||||
|
||||
is_standing_in_doorway = 2;
|
||||
link_on_conveyor_belt = 0;
|
||||
byte_7E03F3 = 0;
|
||||
if ((R14 & 0x70) != 0x70) {
|
||||
if (R14 & 7) { // if_7
|
||||
link_moving_against_diag_tile = 0;
|
||||
@@ -4812,7 +4744,7 @@ else_7:
|
||||
label_3:
|
||||
|
||||
if ((R14 & 2) == 0 && (R12 & 5) != 0) {
|
||||
link_on_conveyor_belt = 0;
|
||||
byte_7E03F3 = 0;
|
||||
FlagMovingIntoSlopes_X();
|
||||
if ((link_moving_against_diag_tile & 0xf) != 0)
|
||||
return;
|
||||
@@ -4848,12 +4780,12 @@ label_3:
|
||||
} // endif_12_norupee
|
||||
|
||||
if (tiledetect_var4 & 0x22) {
|
||||
link_on_conveyor_belt = tiledetect_var4 & 0x20 ? 2 : 1;
|
||||
byte_7E03F3 = tiledetect_var4 & 0x20 ? 2 : 1;
|
||||
} else if (tiledetect_var4 & 0x2200) {
|
||||
link_on_conveyor_belt = tiledetect_var4 & 0x2000 ? 4 : 3;
|
||||
byte_7E03F3 = tiledetect_var4 & 0x2000 ? 4 : 3;
|
||||
} else {
|
||||
if (!(bitfield_spike_cactus_tiles & 7) && !(R14 & 2))
|
||||
link_on_conveyor_belt = 0;
|
||||
byte_7E03F3 = 0;
|
||||
} // endif_15
|
||||
|
||||
if ((detection_of_ledge_tiles_horiz_uphoriz & 7) == 7 && RunLedgeHopTimer()) {
|
||||
@@ -4905,7 +4837,7 @@ endif_19:
|
||||
byte_7E005C = 9;
|
||||
link_this_controls_sprite_oam = 0;
|
||||
player_near_pit_state = 1;
|
||||
link_player_handler_state = kPlayerState_FallingIntoHole;
|
||||
link_player_handler_state = 1;
|
||||
return;
|
||||
} // endif_23
|
||||
|
||||
@@ -5045,7 +4977,7 @@ void StartMovementCollisionChecks_X_HandleOutdoors() { // 87c8e9
|
||||
byte_7E005C = 9;
|
||||
link_this_controls_sprite_oam = 0;
|
||||
player_near_pit_state = 1;
|
||||
link_player_handler_state = kPlayerState_FallingIntoHole;
|
||||
link_player_handler_state = 1;
|
||||
}
|
||||
return;
|
||||
}
|
||||
@@ -5167,17 +5099,11 @@ void StartMovementCollisionChecks_X_HandleOutdoors() { // 87c8e9
|
||||
return;
|
||||
} // endif_8
|
||||
|
||||
// If force facing down (hold B button), while turboing on the Run key, we'll never
|
||||
// reach FlagMovingIntoSlopes_X causing a Dash Buffering glitch.
|
||||
// Fix by always calling it, not sure why you wouldn't always want to call it.
|
||||
if ((R14 & 2) == 0 && (R12 & 5) != 0) {
|
||||
bool skip_check = link_is_running && !(link_direction_facing & 4);
|
||||
if (!skip_check || (enhanced_features0 & kFeatures0_MiscBugFixes)) {
|
||||
FlagMovingIntoSlopes_X();
|
||||
if ((link_moving_against_diag_tile & 0xf) != 0)
|
||||
return;
|
||||
} // endif_9
|
||||
}
|
||||
if ((R14 & 2) == 0 && (R12 & 5) != 0 && (!link_is_running || (link_direction_facing & 4))) {
|
||||
FlagMovingIntoSlopes_X();
|
||||
if ((link_moving_against_diag_tile & 0xf) != 0)
|
||||
return;
|
||||
} // endif_9
|
||||
|
||||
link_moving_against_diag_tile = 0;
|
||||
|
||||
@@ -5288,9 +5214,8 @@ void Link_HandleDiagonalKickback() { // 87ccab
|
||||
static const int8 x1[] = { 0, -1, -1, -1, -2, -2, -2, -3, -3, -3 };
|
||||
link_x_coord += sign8(link_x_vel) ? x1[-(int8)link_x_vel] : x0[link_x_vel];
|
||||
|
||||
static const int8 y0[10] = { 0, 0, 0, 1, 1, 1, 2, 2, 2, 3 };
|
||||
// Bug in zelda, might read index 15
|
||||
static const int8 y1[16] = { 0, 1, 1, 2, 2, 2, 3, 3, 3, 3, 0xa5, 0x30, 0xf0, 0x04, 0xa5, 0x31 };
|
||||
static const int8 y0[] = { 0, 0, 0, 1, 1, 1, 2, 2, 2, 3 };
|
||||
static const int8 y1[] = { 0, 1, 1, 2, 2, 2, 3, 3, 3, 3 };
|
||||
link_y_coord += sign8(link_y_vel) ? y1[-(int8)link_y_vel] : y0[link_y_vel];
|
||||
} else {
|
||||
noHorizOrNoVertical:
|
||||
@@ -5586,25 +5511,12 @@ void FlagMovingIntoSlopes_Y() { // 87e076
|
||||
if (tiledetect_diagonal_tile & 5) {
|
||||
int8 ym = tiledetect_which_y_pos[0] & 7;
|
||||
|
||||
if (enhanced_features0 & kFeatures0_MiscBugFixes) {
|
||||
if (tiledetect_diag_state & 2) {
|
||||
ym = -ym;
|
||||
} else {
|
||||
ym = kAvoidJudder1[o] - (8 - ym);
|
||||
}
|
||||
if (!(tiledetect_diag_state & 2)) {
|
||||
ym = 8 - ym;
|
||||
} else {
|
||||
// This code is bad because it could cause the player
|
||||
// to move up to 15 pixels, causing an array out bounds read.
|
||||
// Not sure how it works, but changed it to look more like the X version.
|
||||
if (!(tiledetect_diag_state & 2)) {
|
||||
ym = 8 - ym; // 0 to 8
|
||||
} else {
|
||||
ym += 8; // 8 to 15
|
||||
}
|
||||
// -15 to 7
|
||||
ym = kAvoidJudder1[o] - ym;
|
||||
ym += 8;
|
||||
}
|
||||
|
||||
ym = kAvoidJudder1[o] - ym;
|
||||
if (link_y_vel == 0)
|
||||
return;
|
||||
if (sign8(link_y_vel))
|
||||
@@ -5636,9 +5548,12 @@ void FlagMovingIntoSlopes_X() { // 87e112
|
||||
int8 xm = link_x_coord & 7;
|
||||
|
||||
if (tiledetect_diag_state != 4 && tiledetect_diag_state != 6) {
|
||||
o ^= 7;
|
||||
xm = -xm;
|
||||
} else {
|
||||
xm = kAvoidJudder1[o] - (8 - xm);
|
||||
xm -= 8;
|
||||
xm = -xm;
|
||||
xm = kAvoidJudder1[o] - xm;
|
||||
} // endif_5
|
||||
if (link_x_vel == 0)
|
||||
return;
|
||||
@@ -5762,10 +5677,10 @@ void Link_HandleVelocity() { // 87e245
|
||||
link_actual_vel_z = 0xff;
|
||||
link_z_coord = 0xffff;
|
||||
link_subpixel_z = 0;
|
||||
Link_MovePosition();
|
||||
LinkHop_FindArbitraryLandingSpot();
|
||||
}
|
||||
|
||||
void Link_MovePosition() { // 87e370
|
||||
void LinkHop_FindArbitraryLandingSpot() { // 87e370
|
||||
uint16 x = link_x_coord, y = link_y_coord;
|
||||
link_y_coord_safe_return_lo = link_y_coord;
|
||||
link_y_coord_safe_return_hi = link_y_coord >> 8;
|
||||
@@ -5904,14 +5819,14 @@ void Link_ApplyConveyor() { // 87e5f0
|
||||
static const int8 kMovingBeltY[4] = { -8, 8, 0, 0 };
|
||||
static const int8 kMovingBeltX[4] = { 0, 0, -8, 8 };
|
||||
|
||||
if (!link_on_conveyor_belt)
|
||||
if (!byte_7E03F3)
|
||||
return;
|
||||
if (BYTE(link_z_coord) != 0 && BYTE(link_z_coord) != 0xff)
|
||||
return;
|
||||
if (link_grabbing_wall & 1 || link_player_handler_state == kPlayerState_Hookshot || link_auxiliary_state)
|
||||
return;
|
||||
|
||||
int j = link_on_conveyor_belt - 1;
|
||||
int j = byte_7E03F3 - 1;
|
||||
if (link_is_running && link_dash_ctr == 32 && (link_direction & kMovePosDirFlag[j]))
|
||||
return;
|
||||
|
||||
@@ -5976,7 +5891,7 @@ void Link_HandleMovingAnimation_StartWithDash() { // 87e704
|
||||
if (link_speed_setting == 6) {
|
||||
x += 4;
|
||||
} else if (link_flag_moving) {
|
||||
if (!(joypad1H_last & kJoypadH_AnyDir)) {
|
||||
if (!(joypad1H_last & 0xf)) {
|
||||
link_animation_steps = 0;
|
||||
return;
|
||||
}
|
||||
@@ -5986,8 +5901,7 @@ void Link_HandleMovingAnimation_StartWithDash() { // 87e704
|
||||
static const uint8 tab2[16] = { 4, 4, 4, 4, 1, 1, 1, 1, 2, 2, 2, 2, 8, 8, 8, 8 };
|
||||
static const uint8 tab3[24] = { 1, 2, 3, 2, 2, 2, 3, 2, 1, 1, 2, 1, 1, 1, 2, 1, 2, 2, 3, 2, 2, 2, 3, 2 };
|
||||
|
||||
//bugfix: tempbunny animation steps are wrong due to missing check
|
||||
if (link_player_handler_state == 23 || (enhanced_features0 & kFeatures0_MiscBugFixes && link_player_handler_state == 28)) { // bunny states
|
||||
if (link_player_handler_state == 23) { // kPlayerState_PermaBunny
|
||||
if (link_animation_steps < 4 && player_on_somaria_platform != 2) {
|
||||
if (++link_counter_var1 >= tab2[x]) {
|
||||
link_counter_var1 = 0;
|
||||
@@ -6101,14 +6015,6 @@ void HandleDoorTransitions() { // 87e901
|
||||
link_x_page_movement_delta = 0;
|
||||
link_y_page_movement_delta = 0;
|
||||
|
||||
// Using a potion might have changed us into a different module, and the routines
|
||||
// below just increment the submodule value, causing all kinds of havoc.
|
||||
// There's an added return to catch the same behavior a bit up, but this one catches more cases,
|
||||
// at the expense of link already having done his movement, so by returning here we might
|
||||
// miss handling the door causing other kinds of issues.
|
||||
if ((enhanced_features0 & kFeatures0_MiscBugFixes) && !(main_module_index == 7 && submodule_index == 0))
|
||||
return;
|
||||
|
||||
if (link_direction_last & 0xC && is_standing_in_doorway == 1) {
|
||||
if (link_direction_last & 4) {
|
||||
if (((t = link_y_coord + 28) & 0xfc) == 0)
|
||||
@@ -6151,10 +6057,6 @@ void HandleDoorTransitions() { // 87e901
|
||||
}
|
||||
|
||||
void ApplyLinksMovementToCamera() { // 87e9d3
|
||||
// Sometimes, when using spin attack, this routine will end up getting
|
||||
// called twice in the same frame, which messes up things.
|
||||
g_ApplyLinksMovementToCamera_called = true;
|
||||
|
||||
link_y_page_movement_delta = (link_y_coord >> 8) - link_y_coord_safe_return_hi;
|
||||
link_x_page_movement_delta = (link_x_coord >> 8) - link_x_coord_safe_return_hi;
|
||||
|
||||
@@ -6260,29 +6162,9 @@ void Link_Initialize() { // 87f13c
|
||||
player_on_somaria_platform = 0;
|
||||
link_spin_attack_step_counter = 0;
|
||||
|
||||
if (enhanced_features0 & kFeatures0_MiscBugFixes) {
|
||||
// If you quit while jumping from a ledge and get hit on a platform you can go under solid layers
|
||||
// This fixes the jump ledge exploration glitch
|
||||
if (enhanced_features0 & kFeatures0_MiscBugFixes)
|
||||
about_to_jump_off_ledge = 0;
|
||||
|
||||
// If you use the mirror near a moveable statue you can pull thin air and glitch the camera
|
||||
link_is_near_moveable_statue = 0;
|
||||
|
||||
// If you use the mirror on a conveyor belt you will retain momentum and clip into the entrance wall
|
||||
link_on_conveyor_belt = 0;
|
||||
|
||||
// bugfix: you use the mirror on ice, you retain momentum
|
||||
link_flag_moving = 0;
|
||||
|
||||
// If you quit in the middle of red armos knight stomp the lumberjack tree will fall on its own
|
||||
bg1_y_offset = bg1_x_offset = 0;
|
||||
//bugfix: if you die in a dungeon as a permabunny and continue, you revert back to link
|
||||
if (!link_item_moon_pearl && savegame_is_darkworld) {
|
||||
link_player_handler_state = kPlayerState_PermaBunny;
|
||||
link_is_bunny = 1;
|
||||
link_is_bunny_mirror = 1;
|
||||
LoadGearPalettes_bunny();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Link_ResetProperties_A() { // 87f1a3
|
||||
@@ -6323,11 +6205,6 @@ void Link_ResetProperties_B() { // 87f1e6
|
||||
}
|
||||
|
||||
void Link_ResetProperties_C() { // 87f1fa
|
||||
if (enhanced_features0 & kFeatures0_MiscBugFixes) {
|
||||
// Fix save menu lockout when dying after medallion cast (#126)
|
||||
flag_custom_spell_anim_active = 0;
|
||||
}
|
||||
|
||||
tile_action_index = 0;
|
||||
state_for_spin_attack = 0;
|
||||
step_counter_for_spin_attack = 0;
|
||||
@@ -6388,7 +6265,7 @@ void SomariaBlock_HandlePlayerInteraction(int k) { // 88e7e6
|
||||
if (!ancilla_H[k]) {
|
||||
if (link_auxiliary_state || (link_state_bits & 1) || ancilla_z[k] != 0 && ancilla_z[k] != 0xff || ancilla_K[k] || ancilla_L[k])
|
||||
return;
|
||||
if (!(joypad1H_last & kJoypadH_AnyDir)) {
|
||||
if (!(joypad1H_last & 0xf)) {
|
||||
ancilla_arr3[k] = 0;
|
||||
bitmask_of_dragstate = 0;
|
||||
ancilla_A[k] = 255;
|
||||
@@ -6396,11 +6273,11 @@ void SomariaBlock_HandlePlayerInteraction(int k) { // 88e7e6
|
||||
link_speed_setting = 0;
|
||||
return;
|
||||
}
|
||||
} else if ((joypad1H_last & kJoypadH_AnyDir) == ancilla_arr3[k]) {
|
||||
} else if ((joypad1H_last & 0xf) == ancilla_arr3[k]) {
|
||||
if (link_speed_setting == 18)
|
||||
bitmask_of_dragstate |= 0x81;
|
||||
} else {
|
||||
ancilla_arr3[k] = (joypad1H_last & kJoypadH_AnyDir);
|
||||
ancilla_arr3[k] = (joypad1H_last & 0xf);
|
||||
link_speed_setting = 0;
|
||||
}
|
||||
|
||||
@@ -6412,7 +6289,7 @@ void SomariaBlock_HandlePlayerInteraction(int k) { // 88e7e6
|
||||
uint8 t;
|
||||
ancilla_x_vel[k] = 0;
|
||||
ancilla_y_vel[k] = 0;
|
||||
ancilla_arr3[k] = t = joypad1H_last & kJoypadH_AnyDir;
|
||||
ancilla_arr3[k] = t = joypad1H_last & 15;
|
||||
if (t & 3) {
|
||||
ancilla_x_vel[k] = t & 1 ? 16 : -16;
|
||||
ancilla_dir[k] = t & 1 ? 3 : 2;
|
||||
@@ -6576,10 +6453,10 @@ void AncillaAdd_Hookshot(uint8 a, uint8 y) { // 899b10
|
||||
void ResetSomeThingsAfterDeath(uint8 a) { // 8bffbf
|
||||
link_is_in_deep_water = 0;
|
||||
link_speed_setting = a;
|
||||
link_on_conveyor_belt = 0;
|
||||
byte_7E03F3 = 0;
|
||||
byte_7E0322 = 0;
|
||||
flag_is_link_immobilized = 0;
|
||||
palette_swap_flag = 0;
|
||||
overworld_palette_swap_flag = 0;
|
||||
player_unk1 = 0;
|
||||
link_give_damage = 0;
|
||||
link_actual_vel_y = 0;
|
||||
@@ -207,7 +207,7 @@ void FlagMovingIntoSlopes_X();
|
||||
void Link_HandleRecoiling();
|
||||
void Player_HandleIncapacitated_Inner2();
|
||||
void Link_HandleVelocity();
|
||||
void Link_MovePosition();
|
||||
void LinkHop_FindArbitraryLandingSpot();
|
||||
void Link_HandleVelocityAndSandDrag(uint16 x, uint16 y);
|
||||
void HandleSwimStrokeAndSubpixels();
|
||||
void Player_SomethingWithVelocity_TiredOrSwim(uint16 xvel, uint16 yvel);
|
||||
@@ -922,11 +922,12 @@ continue_after_set:
|
||||
BYTE(index_of_interacting_tile) = rt;
|
||||
|
||||
int dir = link_direction_facing >> 1;
|
||||
int scratch_1_var = dir * 14;
|
||||
|
||||
int r2 = kPlayerOamOtherOffs[dir * 40 + yt] + rt;
|
||||
int r4loc = kPlayerOamSpriteLocs[r2];
|
||||
|
||||
link_palette_bits_of_oam = palette_swap_flag ? 0 : 0xe00;
|
||||
link_palette_bits_of_oam = overworld_palette_swap_flag ? 0 : 0xe00;
|
||||
link_dma_var1 = link_dma_var2 = 0;
|
||||
|
||||
int xt = FindInByteArray(kPlayerOam_Tab5, yt, 7);
|
||||
@@ -1096,23 +1097,19 @@ continue_after_set:
|
||||
}
|
||||
|
||||
uint16 t;
|
||||
bool hide_shadow = true;
|
||||
bool skip_erase = true;
|
||||
if (is_standing_in_doorway && ((t = link_x_coord - BG2HOFS_copy2) < 4 || t >= 252 || (t = link_y_coord - BG2VOFS_copy2) < 4 || t >= 224) ||
|
||||
(hide_shadow = false,
|
||||
(skip_erase = false,
|
||||
submodule_index == 0 && countdown_for_blink && --countdown_for_blink >= 4 && (countdown_for_blink & 1) == 0 ||
|
||||
link_visibility_status == 12 ||
|
||||
link_cape_mode != 0)) {
|
||||
int shadow_oam_pos = (!hide_shadow && link_visibility_status != 12) ?
|
||||
(scratch_0_var ? kShadow_oam_indexes_1 : kShadow_oam_indexes_0)[r4loc] >> 2 : -10;
|
||||
|
||||
// This appears to hide link by setting the extended bits of the oam to hide them from the screen.
|
||||
// It doesn't really play well with the widescreen modes, so change how it's done.
|
||||
if (enhanced_features0 & kFeatures0_WidescreenVisualFixes) {
|
||||
OamEnt *oam = &oam_buf[sort_sprites_offset_into_oam_buffer >> 2];
|
||||
for (int i = 0; i < 12; i++) {
|
||||
if (i < shadow_oam_pos || i > shadow_oam_pos + 1)
|
||||
oam[i].y = 0xf0;
|
||||
}
|
||||
oam[0].y = oam[1].y = oam[2].y = oam[3].y = 0xf0;
|
||||
oam[4].y = oam[5].y = oam[6].y = oam[7].y = 0xf0;
|
||||
oam[8].y = oam[9].y = oam[10].y = oam[11].y = 0xf0;
|
||||
} else {
|
||||
uint8 *p = &bytewise_extended_oam[sort_sprites_offset_into_oam_buffer >> 2];
|
||||
WORD(p[0]) = 0x101;
|
||||
@@ -1121,9 +1118,10 @@ continue_after_set:
|
||||
WORD(p[6]) = 0x101;
|
||||
WORD(p[8]) = 0x101;
|
||||
WORD(p[10]) = 0x101;
|
||||
// Clear the bit again for the shadow oam so it's not hidden?
|
||||
if (shadow_oam_pos >= 0)
|
||||
WORD(p[shadow_oam_pos]) = 0;
|
||||
}
|
||||
if (link_visibility_status != 12 && !skip_erase) {
|
||||
int oam_pos = ((scratch_0_var ? kShadow_oam_indexes_1 : kShadow_oam_indexes_0)[r4loc] + sort_sprites_offset_into_oam_buffer)>>2;
|
||||
WORD(bytewise_extended_oam[oam_pos]) = 0;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1250,6 +1248,7 @@ void LinkOam_DrawFootObject(int r4loc, uint8 oam_x, uint8 oam_y) { // 8daed1
|
||||
oam_y += kOffsToShadowGivenDir_Y[i];
|
||||
|
||||
int oam_pos = (kShadow_oam_indexes_1[r4loc] + sort_sprites_offset_into_oam_buffer)>>2;
|
||||
uint8 animst = secondary_water_grass_timer;
|
||||
|
||||
uint8 yv;
|
||||
if (draw_water_ripples_or_grass == 2) {
|
||||
370
poly.c
Normal file
370
poly.c
Normal file
@@ -0,0 +1,370 @@
|
||||
#include "poly.h"
|
||||
#include "zelda_rtl.h"
|
||||
#include "variables.h"
|
||||
|
||||
static const int8 kPolySinCos[320] = {
|
||||
0, 2, 3, 5, 6, 8, 9, 11, 12, 14, 16, 17, 19, 20, 22, 23,
|
||||
24, 26, 27, 29, 30, 32, 33, 34, 36, 37, 38, 39, 41, 42, 43, 44,
|
||||
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,
|
||||
64, 64, 64, 64, 64, 64, 63, 63, 63, 62, 62, 62, 61, 61, 60, 60,
|
||||
59, 59, 58, 57, 56, 56, 55, 54, 53, 52, 51, 50, 49, 48, 47, 46,
|
||||
45, 44, 43, 42, 41, 39, 38, 37, 36, 34, 33, 32, 30, 29, 27, 26,
|
||||
24, 23, 22, 20, 19, 17, 16, 14, 12, 11, 9, 8, 6, 5, 3, 2,
|
||||
0, -2, -3, -5, -6, -8, -9, -11, -12, -14, -16, -17, -19, -20, -22, -23,
|
||||
-24, -26, -27, -29, -30, -32, -33, -34, -36, -37, -38, -39, -41, -42, -43, -44,
|
||||
-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,
|
||||
-64, -64, -64, -64, -64, -64, -63, -63, -63, -62, -62, -62, -61, -61, -60, -60,
|
||||
-59, -59, -58, -57, -56, -56, -55, -54, -53, -52, -51, -50, -49, -48, -47, -46,
|
||||
-45, -44, -43, -42, -41, -39, -38, -37, -36, -34, -33, -32, -30, -29, -27, -26,
|
||||
-24, -23, -22, -20, -19, -17, -16, -14, -12, -11, -9, -8, -6, -5, -3, -2,
|
||||
0, 2, 3, 5, 6, 8, 9, 11, 12, 14, 16, 17, 19, 20, 22, 23,
|
||||
24, 26, 27, 29, 30, 32, 33, 34, 36, 37, 38, 39, 41, 42, 43, 44,
|
||||
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,
|
||||
};
|
||||
|
||||
typedef struct Vertex3 {
|
||||
int8 x, y, z;
|
||||
} Vertex3;
|
||||
|
||||
static const Vertex3 kPoly0_Vtx[6] = {
|
||||
{ 0, 65, 0},
|
||||
{ 0, -65, 0},
|
||||
{ 0, 0, -40},
|
||||
{-40, 0, 0},
|
||||
{ 0, 0, 40},
|
||||
{ 40, 0, 0},
|
||||
};
|
||||
|
||||
static const uint8 kPoly0_Polys[40] = {
|
||||
3, 0, 5, 2, 4,
|
||||
3, 0, 2, 3, 1,
|
||||
3, 0, 3, 4, 2,
|
||||
3, 0, 4, 5, 3,
|
||||
3, 1, 2, 5, 4,
|
||||
3, 1, 3, 2, 1,
|
||||
3, 1, 4, 3, 2,
|
||||
3, 1, 5, 4, 3,
|
||||
};
|
||||
|
||||
static const Vertex3 kPoly1_Vtx[6] = {
|
||||
{ 0, 40, 10},
|
||||
{ 40, -40, 10},
|
||||
{-40, -40, 10},
|
||||
{ 0, 40, -10},
|
||||
{-40, -40, -10},
|
||||
{ 40, -40, -10},
|
||||
};
|
||||
|
||||
static const uint8 kPoly1_Polys[28] = {
|
||||
3, 0, 1, 2, 7,
|
||||
3, 3, 4, 5, 6,
|
||||
4, 0, 3, 5, 1, 5,
|
||||
4, 1, 5, 4, 2, 4,
|
||||
4, 3, 0, 2, 4, 3,
|
||||
};
|
||||
|
||||
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},
|
||||
};
|
||||
|
||||
static uint16 Poly_Divide(uint16 a, uint16 b);
|
||||
static void Polyhedral_SetShapePointer();
|
||||
static void Polyhedral_SetRotationMatrix();
|
||||
static void Polyhedral_OperateRotation();
|
||||
static void Polyhedral_RotatePoint();
|
||||
static void Polyhedral_ProjectPoint();
|
||||
static void Polyhedral_DrawPolyhedron();
|
||||
static int Polyhedral_CalcForegroundColor(int16 facing);
|
||||
static void Polyhedral_EmptyBitMapBuffer();
|
||||
|
||||
static uint16 Poly_Divide(uint16 a, uint16 b) {
|
||||
uint16 tmp1 = sign16(a) ? -a : a;
|
||||
uint16 tmp0 = b;
|
||||
while (tmp0 >= 256)
|
||||
tmp0 >>= 1, tmp1 >>= 1;
|
||||
int q = tmp1 / tmp0;
|
||||
return sign16(a) ? -q : q;
|
||||
}
|
||||
|
||||
void Poly_RunFrame() {
|
||||
Polyhedral_EmptyBitMapBuffer();
|
||||
Polyhedral_SetShapePointer();
|
||||
Polyhedral_SetRotationMatrix();
|
||||
Polyhedral_OperateRotation();
|
||||
Polyhedral_DrawPolyhedron();
|
||||
}
|
||||
|
||||
static void Polyhedral_SetShapePointer() { // 89f83d
|
||||
poly_var1 = poly_config1 * 2 + 0x80;
|
||||
poly_tmp0 = poly_which_model * 2;
|
||||
|
||||
const PolyConfig *poly_config = &kPolyConfigs[poly_which_model];
|
||||
poly_config_num_vertex = poly_config->num_vtx;
|
||||
poly_config_num_polys = poly_config->num_poly;
|
||||
poly_fromlut_ptr2 = poly_config->vtx_val;
|
||||
poly_fromlut_ptr4 = poly_config->polys_val;
|
||||
}
|
||||
|
||||
static void Polyhedral_SetRotationMatrix() { // 89f864
|
||||
poly_sin_a = kPolySinCos[poly_a];
|
||||
poly_cos_a = kPolySinCos[poly_a + 64];
|
||||
poly_sin_b = kPolySinCos[poly_b];
|
||||
poly_cos_b = kPolySinCos[poly_b + 64];
|
||||
poly_e0 = (int16)poly_sin_b * (int8)poly_sin_a >> 8 << 2;
|
||||
poly_e1 = (int16)poly_cos_b * (int8)poly_cos_a >> 8 << 2;
|
||||
poly_e2 = (int16)poly_cos_b * (int8)poly_sin_a >> 8 << 2;
|
||||
poly_e3 = (int16)poly_sin_b * (int8)poly_cos_a >> 8 << 2;
|
||||
}
|
||||
|
||||
static void Polyhedral_OperateRotation() { // 89f8fb
|
||||
const PolyConfig *poly_config = &kPolyConfigs[poly_which_model];
|
||||
const int8 *src = &poly_config->vertex[0].x;
|
||||
int i = poly_config_num_vertex;
|
||||
src += i * 3;
|
||||
do {
|
||||
src -= 3, i -= 1;
|
||||
poly_fromlut_x = src[2];
|
||||
poly_fromlut_y = src[1];
|
||||
poly_fromlut_z = src[0];
|
||||
Polyhedral_RotatePoint();
|
||||
Polyhedral_ProjectPoint();
|
||||
poly_arr_x[i] = poly_base_x + poly_f0;
|
||||
poly_arr_y[i] = poly_base_y - poly_f1;
|
||||
} while (i);
|
||||
}
|
||||
|
||||
static void Polyhedral_RotatePoint() { // 89f931
|
||||
int x = (int8)poly_fromlut_x;
|
||||
int y = (int8)poly_fromlut_y;
|
||||
int z = (int8)poly_fromlut_z;
|
||||
|
||||
poly_f0 = (int16)poly_cos_b * z - (int16)poly_sin_b * x;
|
||||
poly_f1 = (int16)poly_e0 * z + (int16)poly_cos_a * y + (int16)poly_e2 * x;
|
||||
poly_f2 = ((int16)poly_e3 * z >> 8) - ((int16)poly_sin_a * y >> 8) + ((int16)poly_e1 * x >> 8) + poly_var1;
|
||||
}
|
||||
|
||||
static void Polyhedral_ProjectPoint() { // 89f9d6
|
||||
poly_f0 = Poly_Divide(poly_f0, poly_f2);
|
||||
poly_f1 = Poly_Divide(poly_f1, poly_f2);
|
||||
}
|
||||
|
||||
struct PolyPoint {
|
||||
uint8 x, y;
|
||||
};
|
||||
|
||||
static int16 Polyhedral_CalculateCrossProduct(struct PolyPoint *pt) { // 89fb24
|
||||
int tmp0 = (pt[1].x - pt[0].x) * (pt[2].y - pt[1].y);
|
||||
tmp0 -= (pt[2].x - pt[1].x) * (pt[1].y - pt[0].y);
|
||||
return tmp0;
|
||||
}
|
||||
|
||||
static void Polyhedral_DrawFace(int color, struct PolyPoint *pt, int npt, bool x2);
|
||||
|
||||
static void Polyhedral_DrawPolyhedron() { // 89fa4f
|
||||
const PolyConfig *poly_config = &kPolyConfigs[poly_which_model];
|
||||
const uint8 *src = poly_config->poly;
|
||||
struct PolyPoint points[16];
|
||||
do {
|
||||
int n = *src++;
|
||||
for(int i = 0; i < n; i++) {
|
||||
int j = *src++;
|
||||
points[i].x = poly_arr_x[j];
|
||||
points[i].y = poly_arr_y[j];
|
||||
}
|
||||
poly_raster_color_config = *src++;
|
||||
int16 facing = Polyhedral_CalculateCrossProduct(points);
|
||||
if (facing > 0) {
|
||||
int color = Polyhedral_CalcForegroundColor(facing);
|
||||
Polyhedral_DrawFace(color, points, n, false);
|
||||
if (kPpuUpsample2x2) {
|
||||
for (int i = 0; i < n; i++)
|
||||
points[i].x *= 2, points[i].y *= 2;
|
||||
Polyhedral_DrawFace(color, points, n, true);
|
||||
}
|
||||
}
|
||||
} while (--poly_config_num_polys);
|
||||
}
|
||||
|
||||
static int Polyhedral_CalcForegroundColor(int16 facing) { // 89faca
|
||||
uint8 t = poly_which_model ? (poly_config1 >> 5) : 0;
|
||||
uint8 a = (facing << (t + 1)) >> 8;
|
||||
return a <= 1 ? 1 : a >= 7 ? 7 : a;
|
||||
}
|
||||
|
||||
static void Polyhedral_EmptyBitMapBuffer() { // 89fd04
|
||||
memset(polyhedral_buffer, 0, 0x800);
|
||||
if (kPpuUpsample2x2)
|
||||
memset(&g_zenv.ext_vram[GET_SPRITE_ADDR_X2(0x5800)], 0, 0x800 * 4);
|
||||
}
|
||||
|
||||
struct PolyFaceRender {
|
||||
struct PolyPoint *pt;
|
||||
uint8 npt;
|
||||
uint8 color;
|
||||
uint8 total_num_steps;
|
||||
|
||||
struct PolyPoint target0, target1;
|
||||
struct PolyPoint cur0, cur1;
|
||||
|
||||
uint16 x0_frac, x0_step;
|
||||
uint8 cur_vertex_idx0, cur_vertex_idx1;
|
||||
uint16 x1_frac, x1_step;
|
||||
};
|
||||
|
||||
static void Polyhedral_FillLine(struct PolyFaceRender *poly, int y);
|
||||
static void Polyhedral_FillLineX2(struct PolyFaceRender *poly, int y);
|
||||
static bool Polyhedral_StepLeft(struct PolyFaceRender *poly);
|
||||
static bool Polyhedral_StepRight(struct PolyFaceRender *poly);
|
||||
|
||||
static void Polyhedral_DrawFace(int color, struct PolyPoint *pt, int npt, bool x2) { // 89fd1e
|
||||
struct PolyFaceRender poly;
|
||||
|
||||
poly.pt = pt;
|
||||
poly.color = color;
|
||||
poly.total_num_steps = poly.npt = npt;
|
||||
|
||||
uint8 min_y = pt[npt - 1].y;
|
||||
int min_idx = npt - 1;
|
||||
while (--npt) {
|
||||
if (pt[npt - 1].y < min_y)
|
||||
min_y = pt[npt - 1].y, min_idx = npt - 1;
|
||||
}
|
||||
int y = min_y;
|
||||
poly.cur_vertex_idx0 = poly.cur_vertex_idx1 = min_idx;
|
||||
poly.cur1 = poly.cur0 = pt[min_idx];
|
||||
if (Polyhedral_StepLeft(&poly) || Polyhedral_StepRight(&poly))
|
||||
return;
|
||||
for (;;) {
|
||||
x2 ? Polyhedral_FillLineX2(&poly, y) : Polyhedral_FillLine(&poly, y);
|
||||
y++;
|
||||
if (poly.cur0.y == poly.target0.y) {
|
||||
poly.cur0.x = poly.target0.x;
|
||||
if (Polyhedral_StepLeft(&poly))
|
||||
return;
|
||||
}
|
||||
poly.cur0.y++;
|
||||
if (poly.cur1.y == poly.target1.y) {
|
||||
poly.cur1.x = poly.target1.x;
|
||||
if (Polyhedral_StepRight(&poly))
|
||||
return;
|
||||
}
|
||||
poly.cur1.y++;
|
||||
poly.x0_frac += poly.x0_step;
|
||||
poly.x1_frac += poly.x1_step;
|
||||
}
|
||||
}
|
||||
|
||||
static void Polyhedral_FillLine(struct PolyFaceRender *poly, int y) { // 89fdcf
|
||||
static const uint16 kPoly_LeftSideMask[8] = { 0xffff, 0x7f7f, 0x3f3f, 0x1f1f, 0xf0f, 0x707, 0x303, 0x101 };
|
||||
static const uint16 kPoly_RightSideMask[8] = { 0x8080, 0xc0c0, 0xe0e0, 0xf0f0, 0xf8f8, 0xfcfc, 0xfefe, 0xffff };
|
||||
static const uint32 kPoly_RasterColors[16] = {
|
||||
0x00, 0xff, 0xff00, 0xffff,
|
||||
0xff0000, 0xff00ff, 0xffff00, 0xffffff,
|
||||
0xff000000, 0xff0000ff, 0xff00ff00, 0xff00ffff,
|
||||
0xffff0000, 0xffff00ff, 0xffffff00, 0xffffffff,
|
||||
};
|
||||
uint32 v = kPoly_RasterColors[poly->color];
|
||||
uint16 color0 = v, color1 = v >> 16;
|
||||
uint16 leftmask = kPoly_LeftSideMask[(poly->x0_frac >> 8) & 7];
|
||||
uint16 rightmask = kPoly_RightSideMask[(poly->x1_frac >> 8) & 7];
|
||||
int left = (poly->x0_frac >> 11) & 7;
|
||||
int right = (poly->x1_frac >> 11) & 7;
|
||||
int tile_index = (y >> 3 & 3) * 16 + (y & 0x20 ? 8 : 0) + right;
|
||||
uint16 *ptr = (uint16*)&g_ram[0xe800 + tile_index * 32 + (y & 7) * 2];
|
||||
if ((right -= left) == 0) {
|
||||
ptr[0] ^= (ptr[0] ^ color0) & (leftmask & rightmask);
|
||||
ptr[8] ^= (ptr[8] ^ color1) & (leftmask & rightmask);
|
||||
} else if (right >= 0) {
|
||||
ptr[0] ^= (ptr[0] ^ color0) & rightmask;
|
||||
ptr[8] ^= (ptr[8] ^ color1) & rightmask;
|
||||
ptr -= 32 / 2;
|
||||
while (--right) {
|
||||
ptr[0] = color0;
|
||||
ptr[8] = color1;
|
||||
ptr -= 32 / 2;
|
||||
}
|
||||
ptr[0] ^= (ptr[0] ^ color0) & leftmask;
|
||||
ptr[8] ^= (ptr[8] ^ color1) & leftmask;
|
||||
}
|
||||
}
|
||||
|
||||
static void Polyhedral_FillLineX2(struct PolyFaceRender *poly, int y) { // 89fdcf
|
||||
uint32 x0_frac = poly->x0_frac, x1_frac = poly->x1_frac;
|
||||
int tile_index = (y >> 4 & 3) * 16 + (y & 0x40 ? 8 : 0);
|
||||
uint64 *ptr = (uint64 *)(&g_zenv.ext_vram[GET_SPRITE_ADDR_X2(tile_index * 16 + 0x5800)]) + (y & 15);
|
||||
uint64 color = poly->color * 0x1111111111111111ull;
|
||||
uint64 lmask = 0xffffffffffffffffull << (((x0_frac >> 8) & 15) * 4);
|
||||
uint64 rmask = 0xffffffffffffffffull >> ((15 - ((x1_frac >> 8) & 15)) * 4);
|
||||
int left = (x0_frac >> 12) & 7;
|
||||
int right = (x1_frac >> 12) & 7;
|
||||
ptr += left * 16;
|
||||
if ((right -= left) == 0) {
|
||||
ptr[0] ^= (ptr[0] ^ color) & (lmask & rmask);
|
||||
} else if (right >= 0) {
|
||||
ptr[0] ^= (ptr[0] ^ color) & lmask;
|
||||
ptr += 128 / 8;
|
||||
while (--right) {
|
||||
ptr[0] = color;
|
||||
ptr += 128 / 8;
|
||||
}
|
||||
ptr[0] ^= (ptr[0] ^ color) & rmask;
|
||||
}
|
||||
}
|
||||
|
||||
static uint16 PolyDivide2(int num, uint8 denom) {
|
||||
int t = num, u = t;
|
||||
if (t < 0)
|
||||
t = -t;
|
||||
t = ((t & 0xff) << 8) / denom;
|
||||
return (u < 0) ? -t : t;
|
||||
}
|
||||
|
||||
static bool Polyhedral_StepLeft(struct PolyFaceRender *poly) { // 89feb4
|
||||
int i;
|
||||
for (;;) {
|
||||
if (sign8(--poly->total_num_steps))
|
||||
return true;
|
||||
i = (poly->cur_vertex_idx0 == 0 ? poly->npt : poly->cur_vertex_idx0) - 1;
|
||||
poly->cur_vertex_idx0 = i;
|
||||
if (poly->pt[i].y < poly->cur0.y)
|
||||
return true;
|
||||
if (poly->pt[i].y != poly->cur0.y)
|
||||
break;
|
||||
poly->cur0.x = poly->pt[i].x;
|
||||
}
|
||||
poly->x0_frac = (poly->cur0.x << 8) | 0x80;
|
||||
poly->target0 = poly->pt[i];
|
||||
poly->x0_step = PolyDivide2(poly->target0.x - poly->cur0.x, poly->target0.y - poly->cur0.y);
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool Polyhedral_StepRight(struct PolyFaceRender *poly) { // 89ff1e
|
||||
int i;
|
||||
for (;;) {
|
||||
if (sign8(--poly->total_num_steps))
|
||||
return true;
|
||||
i = (poly->cur_vertex_idx1 + 1 == poly->npt) ? 0 : poly->cur_vertex_idx1 + 1;
|
||||
poly->cur_vertex_idx1 = i;
|
||||
if (poly->pt[i].y < poly->cur1.y)
|
||||
return true;
|
||||
if (poly->pt[i].y != poly->cur1.y)
|
||||
break;
|
||||
poly->cur1.x = poly->pt[i].x;
|
||||
}
|
||||
poly->x1_frac = (poly->cur1.x << 8) | 0x80;
|
||||
poly->target1 = poly->pt[i];
|
||||
poly->x1_step = PolyDivide2(poly->target1.x - poly->cur1.x, poly->target1.y - poly->cur1.y);
|
||||
return false;
|
||||
}
|
||||
|
||||
4
poly.h
Normal file
4
poly.h
Normal file
@@ -0,0 +1,4 @@
|
||||
#pragma once
|
||||
#include "types.h"
|
||||
|
||||
void Poly_RunFrame();
|
||||
@@ -1,11 +1,11 @@
|
||||
@echo off
|
||||
|
||||
set SDL2=third_party\SDL2-2.26.3
|
||||
set SDL2=third_party\SDL2-2.24.0
|
||||
|
||||
IF NOT EXIST "third_party\tcc\tcc.exe" (
|
||||
ECHO:
|
||||
ECHO ERROR: third_party\tcc\tcc.exe doesn't exist. Please verify that you have put it in the right location.
|
||||
ECHO Download it from https://github.com/FitzRoyX/tinycc/releases/download/tcc_20221020/tcc_20221020.zip
|
||||
ECHO Download it from http://download.savannah.gnu.org/releases/tinycc/tcc-0.9.27-win64-bin.zip
|
||||
ECHO It needs to be the 64-bit version.
|
||||
ECHO:
|
||||
PAUSE
|
||||
@@ -17,7 +17,7 @@ IF NOT EXIST "third_party\tcc\tcc.exe" (
|
||||
IF NOT EXIST "%SDL2%\lib\x64\SDL2.dll" (
|
||||
ECHO:
|
||||
ECHO ERROR: SDL is not unzipped properly into %SDL2%
|
||||
ECHO Download it from https://github.com/libsdl-org/SDL/releases/download/release-2.26.3/SDL2-devel-2.26.3-VC.zip
|
||||
ECHO Download it from https://github.com/libsdl-org/SDL/releases/download/release-2.24.0/SDL2-devel-2.24.0-VC.zip
|
||||
ECHO:
|
||||
PAUSE
|
||||
EXIT /B 1
|
||||
@@ -25,9 +25,9 @@ IF NOT EXIST "%SDL2%\lib\x64\SDL2.dll" (
|
||||
REM
|
||||
)
|
||||
|
||||
IF NOT EXIST "zelda3_assets.dat" (
|
||||
IF NOT EXIST "tables\zelda3_assets.dat" (
|
||||
ECHO:
|
||||
ECHO ERROR: zelda3_assets.dat was not found.
|
||||
ECHO ERROR: tables\zelda3_assets.dat was not found.
|
||||
ECHO You need to extract assets from the ROM first, or get this file from a friend. Please see README.md
|
||||
ECHO:
|
||||
PAUSE
|
||||
@@ -38,7 +38,7 @@ IF NOT EXIST "zelda3_assets.dat" (
|
||||
|
||||
|
||||
echo Building with TCC...
|
||||
third_party\tcc\tcc.exe -ozelda3.exe -DCOMPILER_TCC=1 -DSTBI_NO_SIMD=1 -DHAVE_STDINT_H=1 -D_HAVE_STDINT_H=1 -DSYSTEM_VOLUME_MIXER_AVAILABLE=0 -I%SDL2%/include -L%SDL2%/lib/x64 -lSDL2 -I. src/*.c snes/*.c third_party/gl_core/gl_core_3_1.c third_party/opus-1.3.1-stripped/opus_decoder_amalgam.c
|
||||
third_party\tcc\tcc.exe -ozelda3.exe -DHAVE_STDINT_H=1 -D_HAVE_STDINT_H=1 -DSYSTEM_VOLUME_MIXER_AVAILABLE=0 -I%SDL2%/include -L%SDL2%/lib/x64 -lSDL2 *.c snes/*.c
|
||||
IF ERRORLEVEL 1 goto GETOUT
|
||||
|
||||
copy %SDL2%\lib\x64\SDL2.dll .
|
||||
|
||||
@@ -165,14 +165,17 @@ void Intro_FixCksum(uint8 *s) {
|
||||
void LoadFileSelectGraphics() { // 80e4e9
|
||||
Decomp_spr(&g_ram[0x14000], 0x5e);
|
||||
Do3To4High(&g_zenv.vram[0x5000], &g_ram[0x14000]);
|
||||
CONVERT_SPRITE_TO_X2(0x5000, 64);
|
||||
|
||||
Decomp_spr(&g_ram[0x14000], 0x5f);
|
||||
Do3To4High(&g_zenv.vram[0x5400], &g_ram[0x14000]);
|
||||
CONVERT_SPRITE_TO_X2(0x5400, 64);
|
||||
|
||||
TransferFontToVRAM();
|
||||
|
||||
Decomp_spr(&g_ram[0x14000], 0x6b);
|
||||
memcpy(&g_zenv.vram[0x7800], &g_ram[0x14000], 0x300 * sizeof(uint16));
|
||||
CONVERT_HUD_TO_X2(0x7800, 96);
|
||||
}
|
||||
|
||||
void Intro_ValidateSram() { // 828054
|
||||
@@ -211,7 +214,7 @@ void Module_SelectFile_0() { // 8ccd9d
|
||||
music_control = 11;
|
||||
submodule_index++;
|
||||
overworld_palette_aux_or_main = 0x200;
|
||||
palette_main_indoors = 6;
|
||||
dung_hdr_palette_1 = 6;
|
||||
nmi_disable_core_updates = 6;
|
||||
Palette_Load_DungeonSet();
|
||||
Palette_Load_OWBG3();
|
||||
@@ -735,16 +735,10 @@ static void cpu_trb(Cpu* cpu, uint32_t low, uint32_t high) {
|
||||
void HookedFunctionRts(int is_long);
|
||||
|
||||
static void cpu_doOpcode(Cpu* cpu, uint8_t opcode) {
|
||||
RESTART:
|
||||
switch(opcode) {
|
||||
case 0x00: { // brk imp
|
||||
uint32_t addr = (cpu->k << 16) | cpu->pc;
|
||||
switch (addr - 1) {
|
||||
case 0x7B269: // Link_APress_LiftCarryThrow reads OOB
|
||||
if ((cpu->x & 0xff) >= 28)
|
||||
cpu->pc = 0xB280; // RTS
|
||||
opcode = 0xE8;
|
||||
goto RESTART;
|
||||
|
||||
// Uncle_AtHome case 3 will read random memory.
|
||||
case 0x5DEC7:
|
||||
|
||||
870
snes/ppu.c
870
snes/ppu.c
File diff suppressed because it is too large
Load Diff
40
snes/ppu.h
40
snes/ppu.h
@@ -7,10 +7,10 @@
|
||||
#include <string.h>
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include "snes/saveload.h"
|
||||
#include "saveload.h"
|
||||
typedef struct Ppu Ppu;
|
||||
|
||||
#include "src/types.h"
|
||||
#include "../types.h"
|
||||
|
||||
typedef struct BgLayer {
|
||||
uint16_t hScroll;
|
||||
@@ -21,10 +21,13 @@ typedef struct BgLayer {
|
||||
uint16_t tilemapAdr;
|
||||
// -- snapshot ends here
|
||||
uint16_t tileAdr;
|
||||
|
||||
uint32 tileAdrX2;
|
||||
} BgLayer;
|
||||
|
||||
enum {
|
||||
kPpuXPixels = 256 + kPpuExtraLeftRight * 2,
|
||||
kPpuCgramSize = 256 + 256,
|
||||
};
|
||||
|
||||
typedef uint16_t PpuZbufType;
|
||||
@@ -34,6 +37,13 @@ typedef struct PpuPixelPrioBufs {
|
||||
PpuZbufType data[kPpuXPixels];
|
||||
} PpuPixelPrioBufs;
|
||||
|
||||
// This is also the pixel prios but upsampled 2x2
|
||||
typedef struct PpuPixelPrioBufs2x2 {
|
||||
// This holds the prio in the upper 8 bits and the color in the lower 8 bits.
|
||||
PpuZbufType data[kPpuXPixels * 4];
|
||||
} PpuPixelPrioBufs2x2;
|
||||
|
||||
|
||||
enum {
|
||||
kPpuRenderFlags_NewRenderer = 1,
|
||||
// Render mode7 upsampled by 4x4
|
||||
@@ -44,9 +54,9 @@ enum {
|
||||
kPpuRenderFlags_NoSpriteLimits = 8,
|
||||
};
|
||||
|
||||
|
||||
struct Ppu {
|
||||
bool lineHasSprites;
|
||||
bool cgramDirty;
|
||||
uint8_t lastBrightnessMult;
|
||||
uint8_t lastMosaicModulo;
|
||||
uint8_t renderFlags;
|
||||
@@ -61,8 +71,6 @@ struct Ppu {
|
||||
uint8 mosaicEnabled;
|
||||
uint8 mosaicSize;
|
||||
// object/sprites
|
||||
uint16_t objTileAdr1;
|
||||
uint16_t objTileAdr2;
|
||||
uint8_t objSize;
|
||||
// Window
|
||||
uint8_t window1left;
|
||||
@@ -79,6 +87,9 @@ struct Ppu {
|
||||
bool halfColor;
|
||||
uint8 mathEnabled;
|
||||
uint8_t fixedColorR, fixedColorG, fixedColorB;
|
||||
uint32_t fixedColor;
|
||||
uint8 *halfColorMap;
|
||||
|
||||
// settings
|
||||
bool forcedBlank;
|
||||
uint8_t brightness;
|
||||
@@ -119,26 +130,31 @@ struct Ppu {
|
||||
// store 31 extra entries to remove the need for clamp
|
||||
uint8_t brightnessMult[32 + 31];
|
||||
uint8_t brightnessMultHalf[32 * 2];
|
||||
uint16_t cgram[0x100];
|
||||
uint16_t cgram[kPpuCgramSize];
|
||||
uint32_t cgramWithBrightness[kPpuCgramSize];
|
||||
uint8_t mosaicModulo[kPpuXPixels];
|
||||
uint32_t colorMapRgb[256];
|
||||
PpuPixelPrioBufs bgBuffers[2];
|
||||
PpuPixelPrioBufs objBuffer;
|
||||
|
||||
PpuPixelPrioBufs2x2 objBuffer2x2;
|
||||
|
||||
PpuPixelPrioBufs2x2 bgBuffers2x2[2];
|
||||
|
||||
uint16_t vram[0x8000];
|
||||
|
||||
// Provides 512kb of additional vram
|
||||
uint16_t extendedVram[0x20000];
|
||||
|
||||
};
|
||||
|
||||
Ppu* ppu_init();
|
||||
void ppu_free(Ppu* ppu);
|
||||
void ppu_reset(Ppu* ppu);
|
||||
void ppu_handleVblank(Ppu* ppu);
|
||||
void ppu_runLine(Ppu* ppu, int line);
|
||||
uint8_t ppu_read(Ppu* ppu, uint8_t adr);
|
||||
void ppu_write(Ppu* ppu, uint8_t adr, uint8_t val);
|
||||
void ppu_saveload(Ppu *ppu, SaveLoadFunc *func, void *ctx);
|
||||
void PpuBeginDrawing(Ppu *ppu, uint8_t *buffer, size_t pitch, uint32_t render_flags);
|
||||
|
||||
// Returns the current render scale, 1x = 256px, 2x=512px, 4x=1024px
|
||||
int PpuGetCurrentRenderScale(Ppu *ppu, uint32_t render_flags);
|
||||
bool PpuBeginDrawing(Ppu *ppu, uint8_t *buffer, size_t pitch, uint32_t render_flags);
|
||||
|
||||
void PpuSetMode7PerspectiveCorrection(Ppu *ppu, int low, int high);
|
||||
void PpuSetExtraSideSpace(Ppu *ppu, int left, int right, int bottom);
|
||||
|
||||
20
snes/snes.c
20
snes/snes.c
@@ -18,6 +18,9 @@
|
||||
#include "tracing.h"
|
||||
#include "snes_regs.h"
|
||||
|
||||
static const double apuCyclesPerMaster = (32040 * 32) / (1364 * 262 * 60.0);
|
||||
|
||||
static void snes_runCpu(Snes* snes);
|
||||
static void snes_catchupApu(Snes* snes);
|
||||
static uint8_t snes_readReg(Snes* snes, uint16_t adr);
|
||||
static void snes_writeReg(Snes* snes, uint16_t adr, uint8_t val);
|
||||
@@ -101,6 +104,8 @@ void snes_reset(Snes* snes, bool hard) {
|
||||
snes->openBus = 0;
|
||||
}
|
||||
|
||||
static uint8_t g_last_module;
|
||||
|
||||
void snes_printCpuLine(Snes *snes) {
|
||||
if (snes->debug_cycles) {
|
||||
static FILE *fout;
|
||||
@@ -116,6 +121,21 @@ void snes_printCpuLine(Snes *snes) {
|
||||
}
|
||||
}
|
||||
|
||||
static void snes_runCpu(Snes* snes) {
|
||||
if(snes->cpuCyclesLeft == 0) {
|
||||
snes->cpuMemOps = 0;
|
||||
uint32_t pc = snes->cpu->pc | snes->cpu->k << 16;
|
||||
if (snes->debug_cycles) {
|
||||
char line[80];
|
||||
getProcessorStateCpu(snes, line);
|
||||
puts(line);
|
||||
}
|
||||
int cycles = cpu_runOpcode(snes->cpu);
|
||||
snes->cpuCyclesLeft += (cycles - snes->cpuMemOps) * 6;
|
||||
}
|
||||
snes->cpuCyclesLeft -= 2;
|
||||
}
|
||||
|
||||
static void snes_catchupApu(Snes* snes) {
|
||||
int catchupCycles = (int) snes->apuCatchupCycles;
|
||||
for(int i = 0; i < catchupCycles; i++) {
|
||||
|
||||
@@ -780,7 +780,7 @@ static void PlayNote(SpcPlayer *p, Channel *c, uint8 note) {
|
||||
if (note >= 0xc8 || p->is_chan_on & p->cur_chan_bit)
|
||||
return;
|
||||
|
||||
//static const char *const kNoteNames[] = { "C-", "C#", "D-", "D#", "E-", "F-", "F#", "G-", "G#", "A-", "A#", "B-" };
|
||||
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);
|
||||
|
||||
@@ -1119,6 +1119,8 @@ static void ReadPortFromSnes(SpcPlayer *p, int port) {
|
||||
}
|
||||
|
||||
static void Spc_Loop_Part1(SpcPlayer *p) {
|
||||
static const uint8 kRegAddrs0[10] = {EVOLL, EVOLR, EFB, EON, FLG, KON, KOF, NON, PMON, KOF};
|
||||
|
||||
Dsp_Write(p, KOF, p->key_OFF);
|
||||
Dsp_Write(p, PMON, p->reg_PMON);
|
||||
Dsp_Write(p, NON, p->reg_NON);
|
||||
@@ -1427,14 +1427,8 @@ bool SpriteDraw_AbsorbableTransient(int k, bool transient) { // 86d22f
|
||||
return false;
|
||||
if (sprite_delay_aux2[k] != 0)
|
||||
Oam_AllocateFromRegionC(12);
|
||||
if (sprite_E[k] != 0) {
|
||||
// This code runs when an absorbable is hidden under say a rock.
|
||||
// sprite_B holds the sprite that grabbed us with a hookshot.
|
||||
// Cancel the grab if we're hidden.
|
||||
if (enhanced_features0 & kFeatures0_MiscBugFixes)
|
||||
sprite_B[k] = 0;
|
||||
if (sprite_E[k] != 0)
|
||||
return true;
|
||||
}
|
||||
uint8 j = sprite_type[k];
|
||||
assert(j >= 0xd8 && j < 0xd8 + 19);
|
||||
uint8 a = kAbsorbable_Tab2[j - 0xd8];
|
||||
@@ -73,6 +73,58 @@ static const uint8 kWishPondItemData[50] = {
|
||||
0x09, 0x13, 0x14, 0x4a, 0x21, 0x1d, 0x15, 0x18, 0x19, 0x31, 0x1a, 0x1a, 0x1b, 0x1c, 0x4b, 0x1e,
|
||||
0x1f, 0x49, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x22, 0x23, 0x29, 0x16, 0x2b, 0x2c, 0x2d, 0x3d, 0x3c, 0x48
|
||||
};
|
||||
static const DrawMultipleData kUncleDraw_Table[48] = {
|
||||
{ 0, -10, 0x0e00, 2},
|
||||
{ 0, 0, 0x0c06, 2},
|
||||
{ 0, -10, 0x0e00, 2},
|
||||
{ 0, 0, 0x0c06, 2},
|
||||
{ 0, -10, 0x0e00, 2},
|
||||
{ 0, 0, 0x0c06, 2},
|
||||
{ 0, -10, 0x0e02, 2},
|
||||
{ 0, 0, 0x0c06, 2},
|
||||
{ 0, -10, 0x0e02, 2},
|
||||
{ 0, 0, 0x0c06, 2},
|
||||
{ 0, -10, 0x0e02, 2},
|
||||
{ 0, 0, 0x0c06, 2},
|
||||
{ -7, 2, 0x0d07, 2},
|
||||
{ -7, 2, 0x0d07, 2},
|
||||
{ 10, 12, 0x8d05, 0},
|
||||
{ 10, 4, 0x8d15, 0},
|
||||
{ 0, -10, 0x0e00, 2},
|
||||
{ 0, 0, 0x0c04, 2},
|
||||
{ -7, 1, 0x0d07, 2},
|
||||
{ -7, 1, 0x0d07, 2},
|
||||
{ 10, 13, 0x8d05, 0},
|
||||
{ 10, 5, 0x8d15, 0},
|
||||
{ 0, -9, 0x0e00, 2},
|
||||
{ 0, 1, 0x4c04, 2},
|
||||
{ -7, 8, 0x8d05, 0},
|
||||
{ 1, 8, 0x8d06, 0},
|
||||
{ 0, -10, 0x0e02, 2},
|
||||
{ -6, -1, 0x4d07, 2},
|
||||
{ 0, 0, 0x0c23, 2},
|
||||
{ 0, 0, 0x0c23, 2},
|
||||
{ -9, 7, 0x8d05, 0},
|
||||
{ -1, 7, 0x8d06, 0},
|
||||
{ 0, -9, 0x0e02, 2},
|
||||
{ -6, 0, 0x4d07, 2},
|
||||
{ 0, 1, 0x0c25, 2},
|
||||
{ 0, 1, 0x0c25, 2},
|
||||
{-10, -17, 0x0d07, 2},
|
||||
{ 15, -12, 0x8d15, 0},
|
||||
{ 15, -4, 0x8d05, 0},
|
||||
{ 0, -28, 0x0e08, 2},
|
||||
{ -8, -19, 0x0c20, 2},
|
||||
{ 8, -19, 0x4c20, 2},
|
||||
{ 0, -28, 0x0e08, 2},
|
||||
{ 0, -28, 0x0e08, 2},
|
||||
{ -8, -19, 0x0c20, 2},
|
||||
{ 8, -19, 0x4c20, 2},
|
||||
{ -8, -19, 0x0c20, 2},
|
||||
{ 8, -19, 0x4c20, 2},
|
||||
};
|
||||
static const uint8 kUncleDraw_Dma3[8] = {8, 8, 0, 0, 6, 6, 0, 0};
|
||||
static const uint8 kUncleDraw_Dma4[8] = {0, 0, 0, 0, 4, 4, 0, 0x8b}; // wtf
|
||||
static const uint8 kUncle_LeaveHouse_Delay[2] = {64, 224};
|
||||
static const uint8 kUncle_LeaveHouse_Dir[2] = {2, 1};
|
||||
static const int8 kUncle_LeaveHouse_Xvel[4] = {0, 0, -12, 12};
|
||||
@@ -483,7 +535,7 @@ static HandlerFuncK *const kSpriteActiveRoutines[243] = {
|
||||
&Sprite_11_Hinox,
|
||||
&Sprite_12_Moblin,
|
||||
&Sprite_13_MiniHelmasaur,
|
||||
&Sprite_14_ThievesTownGrate,
|
||||
&Sprite_14_ThievesTownGrate_bounce,
|
||||
&Sprite_15_Antifairy,
|
||||
&Sprite_16_Elder_bounce,
|
||||
&Sprite_17_Hoarder,
|
||||
@@ -500,7 +552,7 @@ static HandlerFuncK *const kSpriteActiveRoutines[243] = {
|
||||
&Sprite_22_Ropa,
|
||||
&Sprite_23_RedBari,
|
||||
&Sprite_23_RedBari,
|
||||
&Sprite_25_TalkingTree,
|
||||
&Sprite_25_TalkingTree_bounce,
|
||||
&Sprite_26_HardhatBeetle,
|
||||
&Sprite_27_Deadrock,
|
||||
&Sprite_28_DarkWorldHintNPC,
|
||||
@@ -508,32 +560,32 @@ static HandlerFuncK *const kSpriteActiveRoutines[243] = {
|
||||
&Sprite_SweepingLady,
|
||||
&Sprite_2B_Hobo,
|
||||
&Sprite_Lumberjacks,
|
||||
&Sprite_2D_TelepathicTile,
|
||||
&Sprite_2D_TelepathicTile_bounce,
|
||||
&Sprite_2E_FluteKid,
|
||||
&Sprite_MazeGameLady,
|
||||
&Sprite_MazeGameGuy,
|
||||
&Sprite_FortuneTeller,
|
||||
&Sprite_QuarrelBros,
|
||||
&Sprite_33_RupeePull,
|
||||
&Sprite_33_RupeePull_bounce,
|
||||
&Sprite_YoungSnitchLady,
|
||||
&Sprite_InnKeeper,
|
||||
&Sprite_Witch,
|
||||
&Sprite_37_Waterfall,
|
||||
&Sprite_38_EyeStatue,
|
||||
&Sprite_37_Waterfall_bounce,
|
||||
&Sprite_38_EyeStatue_bounce,
|
||||
&Sprite_39_Locksmith,
|
||||
&Sprite_3A_MagicBat,
|
||||
&Sprite_3A_MagicBat_bounce,
|
||||
&Sprite_DashItem,
|
||||
&Sprite_TroughBoy,
|
||||
&Sprite_OldSnitchLady,
|
||||
&Sprite_17_Hoarder,
|
||||
&Sprite_TutorialGuardOrBarrier,
|
||||
&Sprite_TutorialGuardOrBarrier,
|
||||
&Sprite_TutorialGuardOrBarrier_bounce,
|
||||
&Sprite_TutorialGuardOrBarrier_bounce,
|
||||
// Trampoline 48 entries
|
||||
&Sprite_41_BlueGuard,
|
||||
&Sprite_41_BlueGuard,
|
||||
&Sprite_41_BlueGuard,
|
||||
&Sprite_44_BluesainBolt,
|
||||
&Sprite_45_HogSpearMan,
|
||||
&Sprite_45_UsainBolt,
|
||||
&Sprite_46_BlueArcher,
|
||||
&Sprite_47_GreenBushGuard,
|
||||
&Sprite_48_RedJavelinGuard,
|
||||
@@ -571,20 +623,20 @@ static HandlerFuncK *const kSpriteActiveRoutines[243] = {
|
||||
&Sprite_66_WallCannonVerticalLeft,
|
||||
&Sprite_66_WallCannonVerticalLeft,
|
||||
&Sprite_6A_BallNChain,
|
||||
&Sprite_6B_CannonTrooper,
|
||||
&Sprite_CannonTrooper,
|
||||
&Sprite_6C_MirrorPortal,
|
||||
&Sprite_6D_Rat,
|
||||
&Sprite_6E_Rope,
|
||||
&Sprite_6F_Keese,
|
||||
&Sprite_70_KingHelmasaurFireball,
|
||||
&Sprite_70_KingHelmasaurFireball_bounce,
|
||||
&Sprite_71_Leever,
|
||||
&Sprite_72_FairyPond,
|
||||
&Sprite_73_UncleAndPriest,
|
||||
&Sprite_73_UncleAndPriest_bounce,
|
||||
&Sprite_RunningMan,
|
||||
&Sprite_BottleVendor,
|
||||
&Sprite_76_Zelda,
|
||||
&Sprite_15_Antifairy,
|
||||
&Sprite_78_MrsSahasrahla,
|
||||
&Sprite_78_MrsSahasrahla_bounce,
|
||||
// Trampoline 68 entries
|
||||
&Sprite_79_Bee,
|
||||
&Sprite_7A_Agahnim,
|
||||
@@ -697,18 +749,18 @@ static HandlerFuncK *const kSpriteActiveRoutines[243] = {
|
||||
&Sprite_E4_SmallKey,
|
||||
&Sprite_E4_SmallKey,
|
||||
&Sprite_D9_GreenRupee,
|
||||
&Sprite_E7_Mushroom,
|
||||
&Sprite_E8_FakeSword,
|
||||
&Sprite_E9_PotionShop,
|
||||
&Sprite_Mushroom,
|
||||
&Sprite_FakeSword,
|
||||
&Sprite_PotionShop,
|
||||
&Sprite_HeartContainer,
|
||||
&Sprite_HeartPiece,
|
||||
&Sprite_EC_ThrownItem,
|
||||
&Sprite_ED_SomariaPlatform,
|
||||
&Sprite_EE_MovableMantle,
|
||||
&Sprite_ED_SomariaPlatform,
|
||||
&Sprite_ED_SomariaPlatform,
|
||||
&Sprite_ED_SomariaPlatform,
|
||||
&Sprite_F2_MedallionTablet,
|
||||
&Sprite_SomariaPlatform,
|
||||
&Sprite_MovableMantleTrampoline,
|
||||
&Sprite_SomariaPlatform,
|
||||
&Sprite_SomariaPlatform,
|
||||
&Sprite_SomariaPlatform,
|
||||
&Sprite_F2_MedallionTablet_bounce,
|
||||
};
|
||||
static HandlerFuncK *const kSpritePrep_Main[243] = {
|
||||
&SpritePrep_Raven,
|
||||
@@ -720,7 +772,7 @@ static HandlerFuncK *const kSpritePrep_Main[243] = {
|
||||
&SpritePrep_Switch,
|
||||
&SpritePrep_SwitchFacingUp,
|
||||
&SpritePrep_Octorok,
|
||||
&SpritePrep_Moldorm,
|
||||
&SpritePrep_Moldorm_bounce,
|
||||
&SpritePrep_Octorok,
|
||||
&SpritePrep_DoNothingA,
|
||||
&SpritePrep_DoNothingA,
|
||||
@@ -795,7 +847,7 @@ static HandlerFuncK *const kSpritePrep_Main[243] = {
|
||||
&SpritePrep_DoNothingD,
|
||||
&SpritePrep_KingZora,
|
||||
&SpritePrep_ArmosKnight,
|
||||
&SpritePrep_Lanmolas,
|
||||
&SpritePrep_Lanmolas_bounce,
|
||||
&SpritePrep_SwimmingZora,
|
||||
&SpritePrep_WalkingZora,
|
||||
&SpritePrep_DesertStatue,
|
||||
@@ -1058,8 +1110,8 @@ void FortuneTeller_LightOrDarkWorld(int k, bool dark_world) {
|
||||
if (!dark_world)
|
||||
sprite_graphics[k] = 0;
|
||||
j = kFortuneTeller_Prices[sprite_A[k]>>1];
|
||||
dialogue_number[0] = (j / 10) | (j % 10)<< 4 ;
|
||||
dialogue_number[1] = 0;
|
||||
byte_7E1CF2[0] = (j / 10) | (j % 10)<< 4 ;
|
||||
byte_7E1CF2[1] = 0;
|
||||
Sprite_ShowMessageUnconditional(0xf4);
|
||||
sprite_ai_state[k]++;
|
||||
break;
|
||||
@@ -1319,7 +1371,7 @@ void HeartUpgrade_CheckIfAlreadyObtained(int k) {
|
||||
}
|
||||
}
|
||||
|
||||
void Sprite_EE_MovableMantle(int k) {
|
||||
void Sprite_MovableMantleTrampoline(int k) {
|
||||
MovableMantle_Draw(k);
|
||||
if (Sprite_ReturnIfInactive(k))
|
||||
return;
|
||||
@@ -1415,7 +1467,7 @@ void ChainBallTrooper_Draw(int k) {
|
||||
SpriteDraw_Shadow_custom(k, &info, kSoldier_DrawShadow[sprite_D[k]]);
|
||||
}
|
||||
|
||||
void Sprite_6B_CannonTrooper(int k) {
|
||||
void Sprite_CannonTrooper(int k) {
|
||||
if (sprite_C[k] != 0) {
|
||||
Sprite_Cannonball(k);
|
||||
return;
|
||||
@@ -1664,7 +1716,7 @@ void Sprite_SpawnSparkleGarnish(int k) { // 858008
|
||||
garnish_countdown[j] = 15;
|
||||
}
|
||||
|
||||
void Sprite_70_KingHelmasaurFireball(int k) { // 85807f
|
||||
void Sprite_70_KingHelmasaurFireball_bounce(int k) { // 85807f
|
||||
static const uint8 kHelmasaurFireball_Char[3] = {0xcc, 0xcc, 0xca};
|
||||
static const uint8 kHelmasaurFireball_Flags[2] = {0x33, 0x73};
|
||||
static const uint8 kHelmasaurFireball_Gfx[4] = {2, 2, 1, 0};
|
||||
@@ -2538,6 +2590,8 @@ void SpriteDraw_Beamos_Eyeball(int k, PrepOamCoordsRet *info) { // 859151
|
||||
}
|
||||
|
||||
void Sprite_Beamos_Laser(int k) { // 8591b5
|
||||
|
||||
|
||||
if (sprite_delay_aux1[k])
|
||||
return;
|
||||
BeamosLaser_Draw(k);
|
||||
@@ -3971,6 +4025,7 @@ void SpriteDraw_BNCFlail(int k, PrepOamCoordsRet *info) { // 85b468
|
||||
uint8 r12 = kFlailTrooperWeapon_Tab1[sprite_D[k]];
|
||||
uint8 r13 = kFlailTrooperWeapon_Tab2[sprite_D[k]];
|
||||
|
||||
uint16 r10 = (r0 & 0x1ff) >> 6;
|
||||
uint16 r2 = (r0 + 0x80) & 0x1ff;
|
||||
|
||||
uint8 r14 = ChainBallMult(kSinusLookupTable[r0 & 0xff], qq);
|
||||
@@ -4863,7 +4918,7 @@ void Guard_AnimateWeapon(int k, const PrepOamCoordsRet *poc) { // 85cb64
|
||||
}
|
||||
}
|
||||
|
||||
void Sprite_45_HogSpearMan(int k) { // 85cbe0
|
||||
void Sprite_45_UsainBolt(int k) { // 85cbe0
|
||||
Guard_HandleAllAnimation(k);
|
||||
if (Sprite_ReturnIfInactive(k))
|
||||
return;
|
||||
@@ -6372,7 +6427,7 @@ void SpritePrep_Mushroom(int k) { // 85ee53
|
||||
}
|
||||
}
|
||||
|
||||
void Sprite_E7_Mushroom(int k) { // 85ee78
|
||||
void Sprite_Mushroom(int k) { // 85ee78
|
||||
SpriteDraw_SingleLarge(k);
|
||||
if (Sprite_CheckIfLinkIsBusy())
|
||||
return;
|
||||
@@ -6390,7 +6445,7 @@ void Sprite_E7_Mushroom(int k) { // 85ee78
|
||||
}
|
||||
}
|
||||
|
||||
void Sprite_E8_FakeSword(int k) { // 85eeaf
|
||||
void Sprite_FakeSword(int k) { // 85eeaf
|
||||
FakeSword_Draw(k);
|
||||
if (Sprite_ReturnIfPaused(k))
|
||||
return;
|
||||
@@ -6771,7 +6826,7 @@ void MagicShopAssistant_SpawnRedCauldron(int k) { // 85f5f0
|
||||
sprite_defl_bits[j] |= 0x20;
|
||||
}
|
||||
|
||||
void Sprite_E9_PotionShop(int k) { // 85f633
|
||||
void Sprite_PotionShop(int k) { // 85f633
|
||||
switch(sprite_subtype2[k]) {
|
||||
case 0: Sprite_MagicShopAssistant_Main(k); return;
|
||||
case 1: Sprite_BagOfPowder(k); return;
|
||||
@@ -7941,14 +7996,14 @@ void SpritePrep_Octorok(int k) { // 868f71
|
||||
sprite_delay_main[k] = GetRandomNumber() & 127;
|
||||
}
|
||||
|
||||
void SpritePrep_Moldorm(int k) { // 868f8a
|
||||
void SpritePrep_Moldorm_bounce(int k) { // 868f8a
|
||||
if (Sprite_ReturnIfBossFinished(k))
|
||||
return;
|
||||
sprite_ignore_projectile[k]++;
|
||||
Sprite_InitializedSegmented(k);
|
||||
}
|
||||
|
||||
void SpritePrep_Lanmolas(int k) { // 868f95
|
||||
void SpritePrep_Lanmolas_bounce(int k) { // 868f95
|
||||
static const uint8 kLanmola_InitDelay[3] = {128, 192, 255};
|
||||
|
||||
if (Sprite_ReturnIfBossFinished(k))
|
||||
@@ -9700,6 +9755,8 @@ void Sprite_28_DarkWorldHintNPC(int k) { // 86ad6f
|
||||
if (!sprite_delay_main[k])
|
||||
sprite_graphics[k] = frame_counter >> 4 & 1;
|
||||
|
||||
int type = sprite_subtype2[k];
|
||||
|
||||
switch(sprite_subtype2[k]) {
|
||||
case 0:
|
||||
switch (sprite_ai_state[k]) {
|
||||
@@ -10529,7 +10586,7 @@ void PushSwitch_Draw(int k) { // 86bb22
|
||||
if (Sprite_PrepOamCoordOrDoubleRet(k, &info))
|
||||
return;
|
||||
uint8 flags;
|
||||
sprite_oam_flags[k] = flags = palette_swap_flag ? sprite_oam_flags[k] | 0xe : sprite_oam_flags[k] & ~0xe;
|
||||
sprite_oam_flags[k] = flags = overworld_palette_swap_flag ? sprite_oam_flags[k] | 0xe : sprite_oam_flags[k] & ~0xe;
|
||||
uint8 r1 = sprite_B[k] >> 2 & 3;
|
||||
|
||||
oam_cur_ptr += 4, oam_ext_cur_ptr += 1;
|
||||
@@ -10842,7 +10899,7 @@ void Hobo_SpawnSmoke(int k) { // 86bfaf
|
||||
}
|
||||
}
|
||||
|
||||
void Sprite_73_UncleAndPriest(int k) { // 86bfe0
|
||||
void Sprite_73_UncleAndPriest_bounce(int k) { // 86bfe0
|
||||
switch (sprite_E[k]) {
|
||||
case 0:
|
||||
Sprite_Uncle(k);
|
||||
@@ -10923,7 +10980,7 @@ void SpritePrep_OldMan_bounce(int k) { // 86bff9
|
||||
}
|
||||
}
|
||||
|
||||
void Sprite_TutorialGuardOrBarrier(int k) { // 86bffe
|
||||
void Sprite_TutorialGuardOrBarrier_bounce(int k) { // 86bffe
|
||||
if (sprite_type[k] == 0x40) {
|
||||
Sprite_EvilBarrier(k);
|
||||
return;
|
||||
@@ -10956,7 +11013,7 @@ void Sprite_TutorialGuardOrBarrier(int k) { // 86bffe
|
||||
}
|
||||
}
|
||||
|
||||
void Sprite_F2_MedallionTablet(int k) { // 86c00d
|
||||
void Sprite_F2_MedallionTablet_bounce(int k) { // 86c00d
|
||||
switch (sprite_subtype2[k]) {
|
||||
case 0:
|
||||
MedallionTablet_Main(k);
|
||||
@@ -10967,7 +11024,7 @@ void Sprite_F2_MedallionTablet(int k) { // 86c00d
|
||||
}
|
||||
}
|
||||
|
||||
void Sprite_33_RupeePull(int k) { // 86c017
|
||||
void Sprite_33_RupeePull_bounce(int k) { // 86c017
|
||||
PrepOamCoordsRet info;
|
||||
Sprite_PrepOamCoord(k, &info);
|
||||
if (Sprite_ReturnIfInactive(k))
|
||||
@@ -10989,7 +11046,7 @@ void Sprite_33_RupeePull(int k) { // 86c017
|
||||
}
|
||||
}
|
||||
|
||||
void Sprite_14_ThievesTownGrate(int k) { // 86c01c
|
||||
void Sprite_14_ThievesTownGrate_bounce(int k) { // 86c01c
|
||||
PrepOamCoordsRet info;
|
||||
Sprite_PrepOamCoord(k, &info);
|
||||
if (Sprite_ReturnIfInactive(k))
|
||||
@@ -11022,14 +11079,14 @@ void SpritePrep_Snitch_bounce_3(int k) { // 86c030
|
||||
SpritePrep_Snitches(k);
|
||||
}
|
||||
|
||||
void Sprite_37_Waterfall(int k) { // 86c03a
|
||||
void Sprite_37_Waterfall_bounce(int k) { // 86c03a
|
||||
switch (sprite_subtype2[k]) {
|
||||
case 0: Waterfall(k); break;
|
||||
case 1: Sprite_BatCrash(k); break;
|
||||
}
|
||||
}
|
||||
|
||||
void Sprite_38_EyeStatue(int k) { // 86c03f
|
||||
void Sprite_38_EyeStatue_bounce(int k) { // 86c03f
|
||||
if (!sprite_B[k]) {
|
||||
PrepOamCoordsRet info;
|
||||
Sprite_PrepOamCoord(k, &info);
|
||||
@@ -11042,7 +11099,7 @@ void Sprite_38_EyeStatue(int k) { // 86c03f
|
||||
}
|
||||
}
|
||||
|
||||
void Sprite_3A_MagicBat(int k) { // 86c044
|
||||
void Sprite_3A_MagicBat_bounce(int k) { // 86c044
|
||||
if (sprite_head_dir[k]) {
|
||||
Sprite_MadBatterBolt(k);
|
||||
return;
|
||||
@@ -11149,7 +11206,7 @@ void SpritePrep_Zelda_bounce(int k) { // 86c06c
|
||||
}
|
||||
}
|
||||
|
||||
void Sprite_78_MrsSahasrahla(int k) { // 86c071
|
||||
void Sprite_78_MrsSahasrahla_bounce(int k) { // 86c071
|
||||
ElderWife_Draw(k);
|
||||
if (Sprite_ReturnIfInactive(k))
|
||||
return;
|
||||
@@ -11202,11 +11259,11 @@ void SpritePrep_HeartPiece(int k) { // 86c0a8
|
||||
HeartUpgrade_CheckIfAlreadyObtained(k);
|
||||
}
|
||||
|
||||
void Sprite_2D_TelepathicTile(int k) { // 86c0b2
|
||||
void Sprite_2D_TelepathicTile_bounce(int k) { // 86c0b2
|
||||
assert(0);
|
||||
}
|
||||
|
||||
void Sprite_25_TalkingTree(int k) { // 86c0d5
|
||||
void Sprite_25_TalkingTree_bounce(int k) { // 86c0d5
|
||||
switch (sprite_subtype2[k]) {
|
||||
case 0: TalkingTree_Mouth(k); break;
|
||||
case 1: TalkingTree_Eye(k); break;
|
||||
@@ -11396,7 +11453,7 @@ void Sprite_HappinessPond(int k) { // 86c44c
|
||||
if (choice_in_multiselect_box == 0) {
|
||||
int i = (link_bomb_upgrades | link_arrow_upgrades) != 0;
|
||||
sprite_graphics[k] = i * 2;
|
||||
WORD(dialogue_number[0]) = WORD(kHappinessPondCostHex[i * 2]);
|
||||
WORD(byte_7E1CF2[0]) = WORD(kHappinessPondCostHex[i * 2]);
|
||||
Sprite_ShowMessageUnconditional(0x14e);
|
||||
sprite_ai_state[k] = 2;
|
||||
flag_is_link_immobilized = 1;
|
||||
@@ -11409,7 +11466,7 @@ show_later_msg:
|
||||
break;
|
||||
case 2: {
|
||||
int i = sprite_graphics[k] + choice_in_multiselect_box;
|
||||
dialogue_number[1] = kHappinessPondCostHex[i];
|
||||
byte_7E1CF2[1] = kHappinessPondCostHex[i];
|
||||
if (link_rupees_goal < kHappinessPondCost[i]) {
|
||||
goto show_later_msg;
|
||||
} else {
|
||||
@@ -11430,7 +11487,7 @@ show_later_msg:
|
||||
sprite_ai_state[k] = 5;
|
||||
return;
|
||||
}
|
||||
dialogue_number[0] = (link_rupees_in_pond / 10) * 16 + (link_rupees_in_pond % 10);
|
||||
byte_7E1CF2[0] = (link_rupees_in_pond / 10) * 16 + (link_rupees_in_pond % 10);
|
||||
sprite_ai_state[k] = 4;
|
||||
break;
|
||||
}
|
||||
@@ -11481,7 +11538,7 @@ show_later_msg:
|
||||
int i = link_bomb_upgrades + 1;
|
||||
if (i != 8) {
|
||||
link_bomb_upgrades = i;
|
||||
dialogue_number[0] = link_bomb_filler = kMaxBombsForLevelHex[i];
|
||||
byte_7E1CF2[0] = link_bomb_filler = kMaxBombsForLevelHex[i];
|
||||
Sprite_ShowMessageUnconditional(0x96);
|
||||
} else {
|
||||
link_rupees_goal += 100;
|
||||
@@ -11518,7 +11575,7 @@ show_later_msg:
|
||||
int i = link_arrow_upgrades + 1;
|
||||
if (i != 8) {
|
||||
link_arrow_upgrades = i;
|
||||
dialogue_number[0] = link_arrow_filler = kMaxArrowsForLevelHex[i];
|
||||
byte_7E1CF2[0] = link_arrow_filler = kMaxArrowsForLevelHex[i];
|
||||
Sprite_ShowMessageUnconditional(0x97);
|
||||
} else {
|
||||
link_rupees_goal += 100;
|
||||
@@ -11747,12 +11804,6 @@ void Sprite_D8_Heart(int k) { // 86cec0
|
||||
if (Sprite_ReturnIfInactive(k))
|
||||
return;
|
||||
Sprite_CheckAbsorptionByPlayer(k);
|
||||
|
||||
// Avoid calling Sprite_HandleAbsorptionByPlayer twice, it's called
|
||||
// also from within Sprite_HandleDraggingByAncilla
|
||||
if (sprite_state[k] == 0 && (enhanced_features0 & kFeatures0_MiscBugFixes))
|
||||
return;
|
||||
|
||||
if (Sprite_HandleDraggingByAncilla(k))
|
||||
return;
|
||||
Sprite_MoveXY(k);
|
||||
@@ -11820,10 +11871,6 @@ void Sprite_E3_Fairy(int k) { // 86cf94
|
||||
return;
|
||||
}
|
||||
}
|
||||
// Avoid calling Sprite_HandleAbsorptionByPlayer twice, it's called
|
||||
// also from within Sprite_HandleDraggingByAncilla
|
||||
if (sprite_state[k] == 0 && (enhanced_features0 & kFeatures0_MiscBugFixes))
|
||||
return;
|
||||
if (Sprite_HandleDraggingByAncilla(k))
|
||||
return;
|
||||
Faerie_HandleMovement(k);
|
||||
@@ -12947,8 +12994,8 @@ void Sprite_MazeGameGuy(int k) { // 8dcbf2
|
||||
t %= 60;
|
||||
int c = t / 10;
|
||||
t %= 10;
|
||||
dialogue_number[0] = t | c << 4;
|
||||
dialogue_number[1] = b | a << 4;
|
||||
byte_7E1CF2[0] = t | c << 4;
|
||||
byte_7E1CF2[1] = b | a << 4;
|
||||
t = Sprite_ShowMessageOnContact(k, 0xcb);
|
||||
if (t & 0x100) {
|
||||
sprite_D[k] = sprite_head_dir[k] = (uint8)t;
|
||||
@@ -13123,59 +13170,6 @@ void MedallionTablet_Draw(int k) { // 8dd1e2
|
||||
}
|
||||
|
||||
void Uncle_Draw(int k) { // 8dd391
|
||||
static const DrawMultipleData kUncleDraw_Table[48] = {
|
||||
{ 0, -10, 0x0e00, 2},
|
||||
{ 0, 0, 0x0c06, 2},
|
||||
{ 0, -10, 0x0e00, 2},
|
||||
{ 0, 0, 0x0c06, 2},
|
||||
{ 0, -10, 0x0e00, 2},
|
||||
{ 0, 0, 0x0c06, 2},
|
||||
{ 0, -10, 0x0e02, 2},
|
||||
{ 0, 0, 0x0c06, 2},
|
||||
{ 0, -10, 0x0e02, 2},
|
||||
{ 0, 0, 0x0c06, 2},
|
||||
{ 0, -10, 0x0e02, 2},
|
||||
{ 0, 0, 0x0c06, 2},
|
||||
{ -7, 2, 0x0d07, 2},
|
||||
{ -7, 2, 0x0d07, 2},
|
||||
{ 10, 12, 0x8d05, 0},
|
||||
{ 10, 4, 0x8d15, 0},
|
||||
{ 0, -10, 0x0e00, 2},
|
||||
{ 0, 0, 0x0c04, 2},
|
||||
{ -7, 1, 0x0d07, 2},
|
||||
{ -7, 1, 0x0d07, 2},
|
||||
{ 10, 13, 0x8d05, 0},
|
||||
{ 10, 5, 0x8d15, 0},
|
||||
{ 0, -9, 0x0e00, 2},
|
||||
{ 0, 1, 0x4c04, 2},
|
||||
{ -7, 8, 0x8d05, 0},
|
||||
{ 1, 8, 0x8d06, 0},
|
||||
{ 0, -10, 0x0e02, 2},
|
||||
{ -6, -1, 0x4d07, 2},
|
||||
{ 0, 0, 0x0c23, 2},
|
||||
{ 0, 0, 0x0c23, 2},
|
||||
{ -9, 7, 0x8d05, 0},
|
||||
{ -1, 7, 0x8d06, 0},
|
||||
{ 0, -9, 0x0e02, 2},
|
||||
{ -6, 0, 0x4d07, 2},
|
||||
{ 0, 1, 0x0c25, 2},
|
||||
{ 0, 1, 0x0c25, 2},
|
||||
{-10, -17, 0x0d07, 2},
|
||||
{ 15, -12, 0x8d15, 0},
|
||||
{ 15, -4, 0x8d05, 0},
|
||||
{ 0, -28, 0x0e08, 2},
|
||||
{ -8, -19, 0x0c20, 2},
|
||||
{ 8, -19, 0x4c20, 2},
|
||||
{ 0, -28, 0x0e08, 2},
|
||||
{ 0, -28, 0x0e08, 2},
|
||||
{ -8, -19, 0x0c20, 2},
|
||||
{ 8, -19, 0x4c20, 2},
|
||||
{ -8, -19, 0x0c20, 2},
|
||||
{ 8, -19, 0x4c20, 2},
|
||||
};
|
||||
static const uint8 kUncleDraw_Dma3[8] = { 8, 8, 0, 0, 6, 6, 0, 0 };
|
||||
static const uint8 kUncleDraw_Dma4[8] = { 0, 0, 0, 0, 4, 4, 0, 0x8b }; // wtf
|
||||
|
||||
Oam_AllocateFromRegionB(0x18);
|
||||
const DrawMultipleData *src = &kUncleDraw_Table[sprite_D[k] * 12 + sprite_graphics[k] * 6];
|
||||
|
||||
@@ -13825,7 +13819,7 @@ void BottleMerchant_BuyBee(int k) { // 9afe88
|
||||
SpriteSfx_QueueSfx3WithPan(k, 0x13);
|
||||
tmp_counter = 4;
|
||||
do {
|
||||
int j = Sprite_SpawnDynamically(k, 0xdb, &info);
|
||||
int j = Sprite_SpawnDynamically(k, 0xd8, &info);
|
||||
if (j >= 0) {
|
||||
Sprite_SetSpawnedCoordinates(j, &info);
|
||||
sprite_x_lo[j] = info.r0_x + 4;
|
||||
@@ -18036,6 +18030,7 @@ void SpriteDraw_Moldorm_Eyeballs(int k, PrepOamCoordsRet *info) { // 9ddb9e
|
||||
static const uint8 kGiantMoldorm_Eye_Char[16] = {0xaa, 0xaa, 0xa8, 0xa8, 0x8a, 0x8a, 0xa8, 0xa8, 0xaa, 0xaa, 0xa8, 0xa8, 0x8a, 0x8a, 0xa8, 0xa8};
|
||||
static const uint8 kGiantMoldorm_Eye_Flags[16] = {0, 0, 0, 0, 0x80, 0x80, 0x40, 0x40, 0x40, 0x40, 0xc0, 0xc0, 0, 0, 0x80, 0x80};
|
||||
OamEnt *oam = GetOamCurPtr();
|
||||
uint8 yoff = kBadPullSwitch_Tab5[kBadPullSwitch_Tab4[sprite_graphics[k]]];
|
||||
int r7 = sprite_F[k] ? frame_counter : 0;
|
||||
int r6 = sprite_D[k] - 1;
|
||||
for (int i = 1; i >= 0; i--, oam++, r6 += 2) {
|
||||
@@ -20944,7 +20939,7 @@ void Sprite_9A_Kyameron(int k) { // 9e9e7b
|
||||
}
|
||||
if (sign8(--sprite_subtype2[k])) {
|
||||
sprite_subtype2[k] = 5;
|
||||
sprite_graphics[k] = (sprite_graphics[k] + 1 & 3) + 8;
|
||||
sprite_graphics[k] = (++sprite_graphics[k] & 3) + 8;
|
||||
}
|
||||
break;
|
||||
case 2: { // coagulate
|
||||
@@ -22815,7 +22810,7 @@ void Sprite_85_YellowStalfos(int k) { // 9ec37f
|
||||
}
|
||||
|
||||
sprite_obj_prio[k] |= kYellowStalfos_ObjPrio[sprite_ai_state[k]];
|
||||
YellowStalfos_Draw(k);
|
||||
YellowStalfos_Draw(k);
|
||||
if (Sprite_ReturnIfInactive(k))
|
||||
return;
|
||||
if (link_sword_type >= 3) {
|
||||
@@ -25465,7 +25460,7 @@ void SomariaPlatform_LocatePath(int k) { // 9ef640
|
||||
sprite_graphics[k]++;
|
||||
}
|
||||
|
||||
void Sprite_ED_SomariaPlatform(int k) { // 9ef6d4
|
||||
void Sprite_SomariaPlatform(int k) { // 9ef6d4
|
||||
switch(sprite_graphics[k]) {
|
||||
case 0: {
|
||||
SomariaPlatform_LocatePath(k);
|
||||
@@ -20,10 +20,10 @@ void Garnish_SetY(int k, uint16 y);
|
||||
void Sprite_WishPond3(int k);
|
||||
int Sprite_SpawnSmallSplash(int k);
|
||||
void HeartUpgrade_CheckIfAlreadyObtained(int k);
|
||||
void Sprite_EE_MovableMantle(int k);
|
||||
void Sprite_MovableMantleTrampoline(int k);
|
||||
void Sprite_GoodOrBadArcheryTarget(int k);
|
||||
void ChainBallTrooper_Draw(int k);
|
||||
void Sprite_6B_CannonTrooper(int k);
|
||||
void Sprite_CannonTrooper(int k);
|
||||
void Bee_PutInBottle(int k);
|
||||
void Sprite_Wizzbeam(int k);
|
||||
void Kiki_LyingInwait(int k);
|
||||
@@ -37,7 +37,7 @@ void Hobo_Draw(int k);
|
||||
bool Landmine_CheckDetonationFromHammer(int k);
|
||||
void Sprite_DrawLargeWaterTurbulence(int k);
|
||||
void Sprite_SpawnSparkleGarnish(int k);
|
||||
void Sprite_70_KingHelmasaurFireball(int k);
|
||||
void Sprite_70_KingHelmasaurFireball_bounce(int k);
|
||||
void Sprite_66_WallCannonVerticalLeft(int k);
|
||||
void Sprite_65_ArcheryGame(int k);
|
||||
void ArcheryGame_Host(int k);
|
||||
@@ -142,7 +142,7 @@ void Guard_HandleAllAnimation(int k);
|
||||
void Guard_AnimateHead(int k, int oam_offs, const PrepOamCoordsRet *poc);
|
||||
void Guard_AnimateBody(int k, int oam_idx, const PrepOamCoordsRet *poc);
|
||||
void Guard_AnimateWeapon(int k, const PrepOamCoordsRet *poc);
|
||||
void Sprite_45_HogSpearMan(int k);
|
||||
void Sprite_45_UsainBolt(int k);
|
||||
void BoltGuard_TriggerChaseTheme(int k);
|
||||
void Sprite_44_BluesainBolt(int k);
|
||||
void PsychoTrooper_Draw(int k);
|
||||
@@ -200,8 +200,8 @@ void Zelda_InCell(int k);
|
||||
void Zelda_EnteringSanctuary(int k);
|
||||
void Zelda_AtSanctuary(int k);
|
||||
void SpritePrep_Mushroom(int k);
|
||||
void Sprite_E7_Mushroom(int k);
|
||||
void Sprite_E8_FakeSword(int k);
|
||||
void Sprite_Mushroom(int k);
|
||||
void Sprite_FakeSword(int k);
|
||||
void FakeSword_Draw(int k);
|
||||
void SpritePrep_HeartContainer(int k);
|
||||
void Sprite_HeartContainer(int k);
|
||||
@@ -222,7 +222,7 @@ void MagicShopAssistant_SpawnPowder(int k);
|
||||
void MagicShopAssistant_SpawnGreenCauldron(int k);
|
||||
void MagicShopAssistant_SpawnBlueCauldron(int k);
|
||||
void MagicShopAssistant_SpawnRedCauldron(int k);
|
||||
void Sprite_E9_PotionShop(int k);
|
||||
void Sprite_PotionShop(int k);
|
||||
void Sprite_BagOfPowder(int k);
|
||||
void MagicPowderItem_Draw(int k);
|
||||
void Sprite_GreenCauldron(int k);
|
||||
@@ -331,8 +331,8 @@ void SpritePrep_ArmosKnight(int k);
|
||||
void SpritePrep_DesertStatue(int k);
|
||||
void SpritePrep_DoNothingD(int k);
|
||||
void SpritePrep_Octorok(int k);
|
||||
void SpritePrep_Moldorm(int k);
|
||||
void SpritePrep_Lanmolas(int k);
|
||||
void SpritePrep_Moldorm_bounce(int k);
|
||||
void SpritePrep_Lanmolas_bounce(int k);
|
||||
void SpritePrep_BigSpike(int k);
|
||||
void SpritePrep_SwimmingZora(int k);
|
||||
void SpritePrep_Geldman(int k);
|
||||
@@ -459,24 +459,24 @@ void Sprite_Hobo_Fire(int k);
|
||||
void SpritePrep_Hobo_SpawnFire(int k);
|
||||
void Sprite_Hobo_Smoke(int k);
|
||||
void Hobo_SpawnSmoke(int k);
|
||||
void Sprite_73_UncleAndPriest(int k);
|
||||
void Sprite_73_UncleAndPriest_bounce(int k);
|
||||
void SpritePrep_UncleAndPriest_bounce(int k);
|
||||
void SpritePrep_OldMan_bounce(int k);
|
||||
void Sprite_TutorialGuardOrBarrier(int k);
|
||||
void Sprite_F2_MedallionTablet(int k);
|
||||
void Sprite_33_RupeePull(int k);
|
||||
void Sprite_14_ThievesTownGrate(int k);
|
||||
void Sprite_TutorialGuardOrBarrier_bounce(int k);
|
||||
void Sprite_F2_MedallionTablet_bounce(int k);
|
||||
void Sprite_33_RupeePull_bounce(int k);
|
||||
void Sprite_14_ThievesTownGrate_bounce(int k);
|
||||
void SpritePrep_Snitch_bounce_2(int k);
|
||||
void SpritePrep_Snitch_bounce_3(int k);
|
||||
void Sprite_37_Waterfall(int k);
|
||||
void Sprite_38_EyeStatue(int k);
|
||||
void Sprite_3A_MagicBat(int k);
|
||||
void Sprite_37_Waterfall_bounce(int k);
|
||||
void Sprite_38_EyeStatue_bounce(int k);
|
||||
void Sprite_3A_MagicBat_bounce(int k);
|
||||
void SpritePrep_Zelda_bounce(int k);
|
||||
void Sprite_78_MrsSahasrahla(int k);
|
||||
void Sprite_78_MrsSahasrahla_bounce(int k);
|
||||
void Sprite_16_Elder_bounce(int k);
|
||||
void SpritePrep_HeartPiece(int k);
|
||||
void Sprite_2D_TelepathicTile(int k);
|
||||
void Sprite_25_TalkingTree(int k);
|
||||
void Sprite_2D_TelepathicTile_bounce(int k);
|
||||
void Sprite_25_TalkingTree_bounce(int k);
|
||||
void Sprite_1C_Statue(int k);
|
||||
bool Statue_CheckForSwitch(int k);
|
||||
void MovableStatue_Draw(int k);
|
||||
@@ -939,7 +939,7 @@ void SpawnApple(int k);
|
||||
void Sprite_Apple(int k);
|
||||
void Sprite_BC_Drunkard(int k);
|
||||
void SomariaPlatform_LocatePath(int k);
|
||||
void Sprite_ED_SomariaPlatform(int k);
|
||||
void Sprite_SomariaPlatform(int k);
|
||||
void SomariaPlatformAndPipe_HandleMovement(int k);
|
||||
uint8 SomariaPlatformAndPipe_CheckTile(int k);
|
||||
void SomariaPlatform_Draw(int k);
|
||||
542
src/audio.c
542
src/audio.c
@@ -1,542 +0,0 @@
|
||||
#include "audio.h"
|
||||
#include "zelda_rtl.h"
|
||||
#include "variables.h"
|
||||
#include "features.h"
|
||||
#include "snes/snes_regs.h"
|
||||
#include "spc_player.h"
|
||||
#include "third_party/opus-1.3.1-stripped/opus.h"
|
||||
#include "config.h"
|
||||
#include "assets.h"
|
||||
|
||||
// This needs to hold a lot more things than with just PCM
|
||||
typedef struct MsuPlayerResumeInfo {
|
||||
uint32 tag;
|
||||
uint32 offset;
|
||||
uint32 samples_until_repeat;
|
||||
uint16 range_cur, range_repeat;
|
||||
uint64 initial_packet_bytes; // To verify we seeked right
|
||||
uint8 orig_track; // Using the old zelda track numbers
|
||||
uint8 actual_track; // The MSU track index we're playing (Different if using msu deluxe)
|
||||
} MsuPlayerResumeInfo;
|
||||
|
||||
enum {
|
||||
kMsuState_Idle = 0,
|
||||
kMsuState_FinishedPlaying = 1,
|
||||
kMsuState_Resuming = 2,
|
||||
kMsuState_Playing = 3,
|
||||
};
|
||||
|
||||
typedef struct MsuPlayer {
|
||||
FILE *f;
|
||||
uint32 buffer_size, buffer_pos;
|
||||
uint32 preskip, samples_until_repeat;
|
||||
uint32 total_samples_in_file, repeat_position;
|
||||
uint32 cur_file_offs;
|
||||
MsuPlayerResumeInfo resume_info;
|
||||
uint8 enabled;
|
||||
uint8 state;
|
||||
float volume, volume_step, volume_target;
|
||||
uint16 range_cur, range_repeat;
|
||||
OpusDecoder *opus;
|
||||
int16 buffer[960 * 2];
|
||||
} MsuPlayer;
|
||||
|
||||
static MsuPlayer g_msu_player;
|
||||
|
||||
static void MsuPlayer_Open(MsuPlayer *mp, int orig_track, bool resume_from_snapshot);
|
||||
|
||||
static const uint8 kMsuTracksWithRepeat[48] = {
|
||||
1,0,1,1,1,1,1,1,0,1,0,1,1,1,1,0,
|
||||
1,1,1,0,1,1,1,1,1,1,1,1,1,0,1,1,
|
||||
1,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,
|
||||
};
|
||||
|
||||
static const uint8 kIsMusicOwOrDungeon[] = {
|
||||
0, 0, 1, 0, 0, 1, 0, 1, 0, 1, 0, 0, 0, 1, 0, 0, // 1 = ow songs : 2 = lw, 5 = forest, 7 = town, 9 = dark world, 13 = mountain
|
||||
2, 2, 2, 0, 0, 0, 2, 2, 0, 0, 0, 2, 0, 0, 0, 0, // 2 = indoor songs : 16, 17, 18, 22, 23, 27
|
||||
};
|
||||
|
||||
|
||||
static const uint8 kMsuDeluxe_OW_Songs[] = {
|
||||
37, 37, 42, 38, 38, 38, 38, 39, 37, 37, 42, 38, 38, 38, 38, 41, // lw
|
||||
42, 42, 42, 42, 42, 42, 40, 40, 43, 43, 42, 47, 47, 42, 45, 45,
|
||||
43, 43, 43, 47, 47, 42, 45, 45, 112, 112, 48, 42, 42, 42, 42, 45,
|
||||
44, 44, 48, 48, 48, 46, 46, 46, 44, 44, 44, 48, 48, 46, 46, 46,
|
||||
49, 49, 51, 50, 50, 50, 50, 50, 49, 49, 51, 50, 50, 50, 50, 51, // dw
|
||||
51, 51, 51, 51, 51, 51, 51, 51, 52, 52, 51, 56, 56, 51, 54, 54,
|
||||
52, 52, 52, 56, 56, 51, 54, 54, 58, 52, 57, 51, 51, 51, 51, 54,
|
||||
53, 53, 57, 57, 57, 55, 55, 110, 53, 53, 57, 57, 57, 55, 55, 110,
|
||||
37, 41, 41, 42, 42, 42, 42, 42, 42, 41, 41, 42, 42, 42, 42, 42, // special
|
||||
42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42,
|
||||
};
|
||||
|
||||
static const uint8 kMsuDeluxe_Entrance_Songs[] = {
|
||||
59, 59, 60, 61, 61, 61, 62, 62, 63, 64, 64, 64, 105, 65, 65, 66,
|
||||
66, 62, 67, 62, 62, 68, 62, 62, 68, 68, 62, 62, 62, 62, 62, 62,
|
||||
62, 62, 62, 62, 69, 70, 71, 72, 73, 73, 73, 106, 102, 74, 62, 62,
|
||||
75, 75, 76, 77, 78, 68, 79, 80, 81, 62, 62, 62, 82, 75, 242, 59,
|
||||
59, 76, 242, 242, 242, 96, 83, 99, 59, 242, 242, 242, 84, 95, 104, 62,
|
||||
85, 62, 62, 86, 242, 67, 103, 83, 83, 87, 76, 88, 81, 98, 81, 88,
|
||||
83, 89, 75, 97, 90, 91, 91, 100, 92, 93, 92, 242, 93, 107, 62, 75,
|
||||
62, 67, 62, 242, 242, 242, 73, 73, 73, 73, 102, 114, 81, 76, 62, 67,
|
||||
62, 61, 94, 62, 103,
|
||||
};
|
||||
|
||||
|
||||
// Remap an track number into a potentially different track number (used for msu deluxe)
|
||||
static uint8 RemapMsuDeluxeTrack(MsuPlayer *mp, uint8 track) {
|
||||
if (!(mp->enabled & kMsuEnabled_MsuDeluxe) || track >= sizeof(kIsMusicOwOrDungeon))
|
||||
return track;
|
||||
switch (kIsMusicOwOrDungeon[track]) {
|
||||
case 1:
|
||||
return BYTE(overworld_area_index) < sizeof(kMsuDeluxe_OW_Songs) ? kMsuDeluxe_OW_Songs[BYTE(overworld_area_index)] : track;
|
||||
case 2:
|
||||
if (which_entrance >= sizeof(kMsuDeluxe_Entrance_Songs) || kMsuDeluxe_Entrance_Songs[which_entrance] == 242)
|
||||
return track;
|
||||
return kMsuDeluxe_Entrance_Songs[which_entrance];
|
||||
default:
|
||||
return track;
|
||||
}
|
||||
}
|
||||
|
||||
bool ZeldaIsPlayingMusicTrack(uint8 track) {
|
||||
MsuPlayer *mp = &g_msu_player;
|
||||
if (mp->state != kMsuState_Idle && mp->enabled & kMsuEnabled_MsuDeluxe)
|
||||
return RemapMsuDeluxeTrack(mp, track) == mp->resume_info.actual_track;
|
||||
else
|
||||
return track == music_unk1;
|
||||
}
|
||||
|
||||
bool ZeldaIsPlayingMusicTrackWithBug(uint8 track) {
|
||||
MsuPlayer *mp = &g_msu_player;
|
||||
if (mp->state != kMsuState_Idle && mp->enabled & kMsuEnabled_MsuDeluxe)
|
||||
return RemapMsuDeluxeTrack(mp, track) == mp->resume_info.actual_track;
|
||||
else
|
||||
return track == (enhanced_features0 & kFeatures0_MiscBugFixes ? music_unk1 : last_music_control);
|
||||
}
|
||||
|
||||
uint8 ZeldaGetEntranceMusicTrack(int i) {
|
||||
MsuPlayer *mp = &g_msu_player;
|
||||
uint8 rv = kEntranceData_musicTrack[i];
|
||||
|
||||
// For some entrances the original performs a fade out, while msu deluxe has new tracks.
|
||||
if (mp->state != kMsuState_Idle && mp->enabled & kMsuEnabled_MsuDeluxe) {
|
||||
if (rv == 242 && kMsuDeluxe_Entrance_Songs[which_entrance] != 242)
|
||||
rv = 16;
|
||||
}
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
static const uint8 kVolumeTransitionTarget[4] = { 0, 64, 255, 255};
|
||||
static const uint8 kVolumeTransitionStep[4] = { 7, 3, 3, 24};
|
||||
// These are precomputed in the config parse
|
||||
static float kVolumeTransitionStepFloat[4];
|
||||
static float kVolumeTransitionTargetFloat[4];
|
||||
|
||||
void ZeldaPlayMsuAudioTrack(uint8 music_ctrl) {
|
||||
MsuPlayer *mp = &g_msu_player;
|
||||
if (!mp->enabled) {
|
||||
mp->resume_info.tag = 0;
|
||||
zelda_apu_write(APUI00, music_ctrl);
|
||||
return;
|
||||
}
|
||||
ZeldaApuLock();
|
||||
if ((music_ctrl & 0xf0) != 0xf0)
|
||||
MsuPlayer_Open(mp, music_ctrl, false);
|
||||
else if (music_ctrl >= 0xf1 && music_ctrl <= 0xf3) {
|
||||
mp->volume_target = kVolumeTransitionTargetFloat[music_ctrl - 0xf1];
|
||||
mp->volume_step = kVolumeTransitionStepFloat[music_ctrl - 0xf1];
|
||||
}
|
||||
|
||||
if (mp->state == 0) {
|
||||
zelda_apu_write(APUI00, music_ctrl);
|
||||
} else {
|
||||
zelda_apu_write(APUI00, 0xf0); // pause spc player
|
||||
}
|
||||
ZeldaApuUnlock();
|
||||
}
|
||||
|
||||
static void MsuPlayer_CloseFile(MsuPlayer *mp) {
|
||||
if (mp->f)
|
||||
fclose(mp->f);
|
||||
opus_decoder_destroy(mp->opus);
|
||||
mp->opus = NULL;
|
||||
mp->f = NULL;
|
||||
if (mp->state != kMsuState_FinishedPlaying)
|
||||
mp->state = kMsuState_Idle;
|
||||
memset(&mp->resume_info, 0, sizeof(mp->resume_info));
|
||||
}
|
||||
|
||||
static void MsuPlayer_Open(MsuPlayer *mp, int orig_track, bool resume_from_snapshot) {
|
||||
MsuPlayerResumeInfo resume;
|
||||
int actual_track = RemapMsuDeluxeTrack(mp, orig_track);
|
||||
|
||||
if (!resume_from_snapshot) {
|
||||
resume.tag = 0;
|
||||
// Attempt to resume MSU playback when exiting back to the overworld.
|
||||
if (main_module_index == 9 &&
|
||||
actual_track == ((MsuPlayerResumeInfo *)msu_resume_info_alt)->actual_track && g_config.resume_msu) {
|
||||
memcpy(&resume, msu_resume_info_alt, sizeof(mp->resume_info));
|
||||
}
|
||||
if (mp->state >= kMsuState_Resuming)
|
||||
memcpy(msu_resume_info_alt, &mp->resume_info, sizeof(mp->resume_info));
|
||||
} else {
|
||||
memcpy(&resume, msu_resume_info, sizeof(mp->resume_info));
|
||||
}
|
||||
|
||||
mp->volume_target = kVolumeTransitionTargetFloat[3];
|
||||
mp->volume_step = kVolumeTransitionStepFloat[3];
|
||||
|
||||
mp->state = kMsuState_Idle;
|
||||
MsuPlayer_CloseFile(mp);
|
||||
if (actual_track == 0)
|
||||
return;
|
||||
char fname[256], buf[8];
|
||||
snprintf(fname, sizeof(fname), "%s%d.%s", g_config.msu_path ? g_config.msu_path : "", actual_track, mp->enabled & kMsuEnabled_Opuz ? "opuz" : "pcm");
|
||||
printf("Loading MSU %s\n", fname);
|
||||
mp->f = fopen(fname, "rb");
|
||||
if (mp->f == NULL)
|
||||
goto READ_ERROR;
|
||||
setvbuf(mp->f, NULL, _IOFBF, 16384);
|
||||
if (fread(buf, 1, 8, mp->f) != 8) READ_ERROR: {
|
||||
fprintf(stderr, "Unable to read MSU file %s\n", fname);
|
||||
MsuPlayer_CloseFile(mp);
|
||||
return;
|
||||
}
|
||||
uint32 file_tag = *(uint32 *)(buf + 0);
|
||||
mp->repeat_position = *(uint32 *)(buf + 4);
|
||||
mp->state = (resume.actual_track == actual_track && resume.tag == file_tag) ? kMsuState_Resuming : kMsuState_Playing;
|
||||
if (mp->state == kMsuState_Resuming) {
|
||||
memcpy(&mp->resume_info, &resume, sizeof(mp->resume_info));
|
||||
} else {
|
||||
mp->resume_info.orig_track = orig_track;
|
||||
mp->resume_info.actual_track = actual_track;
|
||||
mp->resume_info.tag = file_tag;
|
||||
mp->resume_info.range_cur = 8;
|
||||
}
|
||||
mp->cur_file_offs = mp->resume_info.offset;
|
||||
mp->samples_until_repeat = mp->resume_info.samples_until_repeat;
|
||||
mp->range_cur = mp->resume_info.range_cur;
|
||||
mp->range_repeat = mp->resume_info.range_repeat;
|
||||
mp->buffer_size = mp->buffer_pos = 0;
|
||||
mp->preskip = 0;
|
||||
if (file_tag == (('Z' << 24) | ('U' << 16) | ('P' << 8) | 'O')) {
|
||||
mp->opus = opus_decoder_create(48000, 2, NULL);
|
||||
if (!mp->opus)
|
||||
goto READ_ERROR;
|
||||
if (mp->state == kMsuState_Resuming)
|
||||
fseek(mp->f, mp->cur_file_offs, SEEK_SET);
|
||||
} else if (file_tag == (('1' << 24) | ('U' << 16) | ('S' << 8) | 'M')) {
|
||||
fseek(mp->f, 0, SEEK_END);
|
||||
mp->total_samples_in_file = (ftell(mp->f) - 8) / 4;
|
||||
mp->samples_until_repeat = mp->total_samples_in_file - mp->cur_file_offs;
|
||||
fseek(mp->f, mp->cur_file_offs * 4 + 8, SEEK_SET);
|
||||
} else {
|
||||
goto READ_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
static void MixToBufferWithVolume(int16 *dst, const int16 *src, size_t n, float volume) {
|
||||
if (volume == 1.0f) {
|
||||
for (size_t i = 0; i < n; i++) {
|
||||
dst[i * 2 + 0] += src[i * 2 + 0];
|
||||
dst[i * 2 + 1] += src[i * 2 + 1];
|
||||
}
|
||||
} else {
|
||||
uint32 vol = (int32)(65536 * volume);
|
||||
for (size_t i = 0; i < n; i++) {
|
||||
dst[i * 2 + 0] += src[i * 2 + 0] * vol >> 16;
|
||||
dst[i * 2 + 1] += src[i * 2 + 1] * vol >> 16;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void MixToBufferWithVolumeRamp(int16 *dst, const int16 *src, size_t n, float volume, float volume_step, float ideal_target) {
|
||||
int64 vol = volume * 281474976710656.0f;
|
||||
int64 step = volume_step * 281474976710656.0f;
|
||||
for (size_t i = 0; i < n; i++) {
|
||||
uint32 v = (vol >> 32);
|
||||
dst[i * 2 + 0] += src[i * 2 + 0] * v >> 16;
|
||||
dst[i * 2 + 1] += src[i * 2 + 1] * v >> 16;
|
||||
vol += step;
|
||||
}
|
||||
}
|
||||
|
||||
static void MixToBuffer(MsuPlayer *mp, int16 *dst, const int16 *src, uint32 n) {
|
||||
if (mp->volume != mp->volume_target) {
|
||||
float step = mp->volume < mp->volume_target ? mp->volume_step : -mp->volume_step;
|
||||
float new_vol = mp->volume + step * n;
|
||||
uint32 curn = n;
|
||||
if (step >= 0 ? new_vol >= mp->volume_target : new_vol < mp->volume_target) {
|
||||
uint32 maxn = (uint32)((mp->volume_target - mp->volume) / step);
|
||||
curn = UintMin(maxn, curn);
|
||||
new_vol = mp->volume_target;
|
||||
}
|
||||
float vol = mp->volume;
|
||||
mp->volume = new_vol;
|
||||
MixToBufferWithVolumeRamp(dst, src, curn, vol, step, new_vol);
|
||||
dst += curn * 2, src += curn * 2, n -= curn;
|
||||
}
|
||||
MixToBufferWithVolume(dst, src, n, mp->volume);
|
||||
}
|
||||
|
||||
void MsuPlayer_Mix(MsuPlayer *mp, int16 *audio_buffer, int audio_samples) {
|
||||
int r;
|
||||
|
||||
do {
|
||||
if (mp->buffer_size - mp->buffer_pos == 0) {
|
||||
if (mp->opus != NULL) {
|
||||
if (mp->samples_until_repeat == 0) {
|
||||
if (mp->range_cur == 0) FINISHED_PLAYING: {
|
||||
mp->state = kMsuState_FinishedPlaying;
|
||||
MsuPlayer_CloseFile(mp);
|
||||
return;
|
||||
}
|
||||
opus_decoder_ctl(mp->opus, OPUS_RESET_STATE);
|
||||
fseek(mp->f, mp->range_cur, SEEK_SET);
|
||||
uint8 *file_data = (uint8 *)mp->buffer;
|
||||
if (fread(file_data, 1, 10, mp->f) != 10) READ_ERROR: {
|
||||
fprintf(stderr, "MSU read/decode error!\n");
|
||||
zelda_apu_write(APUI00, mp->resume_info.orig_track);
|
||||
MsuPlayer_CloseFile(mp);
|
||||
return;
|
||||
}
|
||||
uint32 file_offs = *(uint32 *)&file_data[0];
|
||||
assert((file_offs & 0xF0000000) == 0);
|
||||
uint32 samples_until_repeat = *(uint32 *)&file_data[4];
|
||||
uint16 preskip = *(uint32 *)&file_data[8];
|
||||
mp->samples_until_repeat = samples_until_repeat;
|
||||
mp->preskip = preskip & 0x3fff;
|
||||
if (preskip & 0x4000)
|
||||
mp->range_repeat = mp->range_cur;
|
||||
mp->range_cur = (preskip & 0x8000) ? mp->range_repeat : mp->range_cur + 10;
|
||||
mp->cur_file_offs = file_offs;
|
||||
mp->resume_info.range_repeat = mp->range_repeat;
|
||||
mp->resume_info.range_cur = mp->range_cur;
|
||||
fseek(mp->f, file_offs, SEEK_SET);
|
||||
}
|
||||
assert(mp->samples_until_repeat != 0);
|
||||
for (;;) {
|
||||
uint8 *file_data = (uint8 *)mp->buffer;
|
||||
*(uint64 *)file_data = 0;
|
||||
if (fread(file_data, 1, 2, mp->f) != 2)
|
||||
goto READ_ERROR;
|
||||
int size = *(uint16 *)file_data & 0x7fff;
|
||||
if (size > 1275)
|
||||
goto READ_ERROR;
|
||||
int n = (*(uint16 *)file_data >> 15);
|
||||
if (fread(&file_data[2], 1, size, mp->f) != size)
|
||||
goto READ_ERROR;
|
||||
// Verify if the snapshot matches the file on disk.
|
||||
uint64 initial_file_data = *(uint64 *)file_data;
|
||||
if (mp->state == kMsuState_Resuming) {
|
||||
mp->state = kMsuState_Playing;
|
||||
if (mp->resume_info.initial_packet_bytes != initial_file_data)
|
||||
goto READ_ERROR;
|
||||
}
|
||||
mp->resume_info.initial_packet_bytes = initial_file_data;
|
||||
mp->resume_info.samples_until_repeat = mp->samples_until_repeat + mp->preskip;
|
||||
mp->resume_info.offset = mp->cur_file_offs;
|
||||
mp->cur_file_offs += 2 + size;
|
||||
file_data[1] = 0xfc;
|
||||
r = opus_decode(mp->opus, &file_data[2 - n], size + n, mp->buffer, 960, 0);
|
||||
if (r <= 0)
|
||||
goto READ_ERROR;
|
||||
if (r > mp->preskip)
|
||||
break;
|
||||
mp->preskip -= r;
|
||||
}
|
||||
} else {
|
||||
if (mp->samples_until_repeat == 0) {
|
||||
if (mp->resume_info.actual_track < sizeof(kMsuTracksWithRepeat) && !kMsuTracksWithRepeat[mp->resume_info.actual_track])
|
||||
goto FINISHED_PLAYING;
|
||||
mp->samples_until_repeat = mp->total_samples_in_file - mp->repeat_position;
|
||||
if (mp->samples_until_repeat == 0)
|
||||
goto READ_ERROR; // impossible to make progress
|
||||
mp->cur_file_offs = mp->repeat_position;
|
||||
fseek(mp->f, mp->cur_file_offs * 4 + 8, SEEK_SET);
|
||||
}
|
||||
r = UintMin(960, mp->samples_until_repeat);
|
||||
if (fread(mp->buffer, 4, r, mp->f) != r)
|
||||
goto READ_ERROR;
|
||||
mp->resume_info.offset = mp->cur_file_offs;
|
||||
mp->cur_file_offs += r;
|
||||
}
|
||||
uint32 n = UintMin(r - mp->preskip, mp->samples_until_repeat);
|
||||
mp->samples_until_repeat -= n;
|
||||
mp->buffer_pos = mp->preskip;
|
||||
mp->buffer_size = mp->buffer_pos + n;
|
||||
mp->preskip = 0;
|
||||
}
|
||||
#if 0
|
||||
if (mp->samples_to_play > 44100 * 5) {
|
||||
mp->buffer_pos = mp->buffer_size;
|
||||
}
|
||||
#endif
|
||||
int nr = IntMin(audio_samples, mp->buffer_size - mp->buffer_pos);
|
||||
int16 *buf = mp->buffer + mp->buffer_pos * 2;
|
||||
mp->buffer_pos += nr;
|
||||
|
||||
#if 0
|
||||
static int t;
|
||||
for (int i = 0; i < nr; i++) {
|
||||
buf[i * 2 + 0] = buf[i * 2 + 1] = 5000 * sinf(2 * 3.1415 * t++ / 440);
|
||||
}
|
||||
#endif
|
||||
MixToBuffer(mp, audio_buffer, buf, nr);
|
||||
|
||||
#if 0
|
||||
static FILE *f;
|
||||
if (!f)f = fopen("out.pcm", "wb");
|
||||
fwrite(audio_buffer, 4, nr, f);
|
||||
fflush(f);
|
||||
#endif
|
||||
audio_samples -= nr, audio_buffer += nr * 2;
|
||||
} while (audio_samples != 0);
|
||||
}
|
||||
|
||||
// Maintain a queue cause the snes and audio callback are not in sync.
|
||||
struct ApuWriteEnt {
|
||||
uint8 ports[4];
|
||||
};
|
||||
static struct ApuWriteEnt g_apu_write_ents[16], g_apu_write;
|
||||
static uint8 g_apu_write_ent_pos, g_apu_write_count, g_apu_total_write;
|
||||
void zelda_apu_write(uint32_t adr, uint8_t val) {
|
||||
g_apu_write.ports[adr & 0x3] = val;
|
||||
}
|
||||
|
||||
void ZeldaPushApuState() {
|
||||
ZeldaApuLock();
|
||||
g_apu_write_ents[g_apu_write_ent_pos++ & 0xf] = g_apu_write;
|
||||
if (g_apu_write_count < 16)
|
||||
g_apu_write_count++;
|
||||
g_apu_total_write++;
|
||||
ZeldaApuUnlock();
|
||||
}
|
||||
|
||||
static void ZeldaPopApuState() {
|
||||
if (g_apu_write_count != 0)
|
||||
memcpy(g_zenv.player->input_ports, &g_apu_write_ents[(g_apu_write_ent_pos - g_apu_write_count--) & 0xf], 4);
|
||||
}
|
||||
|
||||
void ZeldaDiscardUnusedAudioFrames() {
|
||||
if (g_apu_write_count != 0 && memcmp(g_zenv.player->input_ports, &g_apu_write_ents[(g_apu_write_ent_pos - g_apu_write_count) & 0xf], 4) == 0) {
|
||||
if (g_apu_total_write >= 16) {
|
||||
g_apu_total_write = 14;
|
||||
g_apu_write_count--;
|
||||
}
|
||||
} else {
|
||||
g_apu_total_write = 0;
|
||||
}
|
||||
}
|
||||
|
||||
static void ZeldaResetApuQueue() {
|
||||
g_apu_write_ent_pos = g_apu_total_write = g_apu_write_count = 0;
|
||||
}
|
||||
|
||||
uint8_t zelda_read_apui00() {
|
||||
// This needs to be here because the ancilla code reads
|
||||
// from the apu and we don't want to make the core code
|
||||
// dependent on the apu timings, so relocated this value
|
||||
// to 0x648.
|
||||
return g_ram[kRam_APUI00];
|
||||
}
|
||||
|
||||
uint8_t zelda_apu_read(uint32_t adr) {
|
||||
return g_zenv.player->port_to_snes[adr & 0x3];
|
||||
}
|
||||
|
||||
void ZeldaRenderAudio(int16 *audio_buffer, int samples, int channels) {
|
||||
ZeldaApuLock();
|
||||
ZeldaPopApuState();
|
||||
SpcPlayer_GenerateSamples(g_zenv.player);
|
||||
dsp_getSamples(g_zenv.player->dsp, audio_buffer, samples, channels);
|
||||
if (g_msu_player.f && channels == 2)
|
||||
MsuPlayer_Mix(&g_msu_player, audio_buffer, samples);
|
||||
ZeldaApuUnlock();
|
||||
}
|
||||
|
||||
bool ZeldaIsMusicPlaying() {
|
||||
if (g_msu_player.state != kMsuState_Idle) {
|
||||
return g_msu_player.state != kMsuState_FinishedPlaying;
|
||||
} else {
|
||||
return g_zenv.player->port_to_snes[0] != 0;
|
||||
}
|
||||
}
|
||||
|
||||
void ZeldaRestoreMusicAfterLoad_Locked(bool is_reset) {
|
||||
// Restore spc variables from the ram dump.
|
||||
SpcPlayer_CopyVariablesFromRam(g_zenv.player);
|
||||
// This is not stored in the snapshot
|
||||
g_zenv.player->timer_cycles = 0;
|
||||
|
||||
// Restore input ports state
|
||||
SpcPlayer *spc_player = g_zenv.player;
|
||||
memcpy(spc_player->input_ports, &spc_player->ram[0x410], 4);
|
||||
memcpy(g_apu_write.ports, spc_player->input_ports, 4);
|
||||
|
||||
if (is_reset) {
|
||||
SpcPlayer_Initialize(g_zenv.player);
|
||||
}
|
||||
|
||||
MsuPlayer *mp = &g_msu_player;
|
||||
if (mp->enabled) {
|
||||
mp->volume = 0.0;
|
||||
MsuPlayer_Open(mp, (music_unk1 == 0xf1) ? ((MsuPlayerResumeInfo*)msu_resume_info)->orig_track :
|
||||
music_unk1, true);
|
||||
|
||||
// If resuming in the middle of a transition, then override
|
||||
// the volume with that of the transition.
|
||||
if (last_music_control >= 0xf1 && last_music_control <= 0xf3) {
|
||||
uint8 target = kVolumeTransitionTarget[last_music_control - 0xf1];
|
||||
if (target != msu_volume) {
|
||||
float f = kVolumeTransitionTargetFloat[3] * (1.0f / 255);
|
||||
mp->volume = msu_volume * f;
|
||||
mp->volume_target = target * f;
|
||||
mp->volume_step = kVolumeTransitionStepFloat[last_music_control - 0xf1];
|
||||
}
|
||||
}
|
||||
|
||||
if (g_msu_player.state)
|
||||
zelda_apu_write(APUI00, 0xf0); // pause spc player
|
||||
}
|
||||
ZeldaResetApuQueue();
|
||||
}
|
||||
|
||||
void ZeldaSaveMusicStateToRam_Locked() {
|
||||
SpcPlayer_CopyVariablesToRam(g_zenv.player);
|
||||
// SpcPlayer.input_ports is not saved to the SpcPlayer ram by SpcPlayer_CopyVariablesToRam,
|
||||
// in any case, we want to save the most recently written data, and that might still
|
||||
// be in the queue. 0x410 is a free memory location in the SPC ram, so store it there.
|
||||
SpcPlayer *spc_player = g_zenv.player;
|
||||
memcpy(&spc_player->ram[0x410], g_apu_write.ports, 4);
|
||||
|
||||
msu_volume = g_msu_player.volume * 255;
|
||||
memcpy(msu_resume_info, &g_msu_player.resume_info, sizeof(g_msu_player.resume_info));
|
||||
}
|
||||
|
||||
void ZeldaEnableMsu(uint8 enable) {
|
||||
g_msu_player.volume = 1.0f;
|
||||
g_msu_player.enabled = enable;
|
||||
if (enable & kMsuEnabled_Opuz) {
|
||||
if (g_config.audio_freq != 48000)
|
||||
fprintf(stderr, "Warning: MSU Opuz requires: AudioFreq = 48000\n");
|
||||
} else if (enable) {
|
||||
if (g_config.audio_freq != 44100)
|
||||
fprintf(stderr, "Warning: MSU requires: AudioFreq = 44100\n");
|
||||
}
|
||||
|
||||
float volscale = g_config.msuvolume * (1.0f / 255 / 100);
|
||||
float stepscale = g_config.msuvolume * (60.0f / 256 / 100) / g_config.audio_freq;
|
||||
for (size_t i = 0; i != countof(kVolumeTransitionStepFloat); i++) {
|
||||
kVolumeTransitionStepFloat[i] = kVolumeTransitionStep[i] * stepscale;
|
||||
kVolumeTransitionTargetFloat[i] = kVolumeTransitionTarget[i] * volscale;
|
||||
}
|
||||
}
|
||||
|
||||
void LoadSongBank(const uint8 *p) { // 808888
|
||||
ZeldaApuLock();
|
||||
SpcPlayer_Upload(g_zenv.player, p);
|
||||
ZeldaApuUnlock();
|
||||
}
|
||||
20
src/audio.h
20
src/audio.h
@@ -1,20 +0,0 @@
|
||||
#ifndef ZELDA3_AUDIO_H_
|
||||
#define ZELDA3_AUDIO_H_
|
||||
|
||||
#include "types.h"
|
||||
|
||||
// Things for msu
|
||||
bool ZeldaIsPlayingMusicTrack(uint8 track);
|
||||
bool ZeldaIsPlayingMusicTrackWithBug(uint8 track);
|
||||
void ZeldaPlayMsuAudioTrack(uint8 track);
|
||||
bool ZeldaIsMusicPlaying();
|
||||
|
||||
void ZeldaEnableMsu(uint8 enable);
|
||||
|
||||
void ZeldaRenderAudio(int16 *audio_buffer, int samples, int channels);
|
||||
void ZeldaDiscardUnusedAudioFrames();
|
||||
void ZeldaRestoreMusicAfterLoad_Locked(bool is_reset);
|
||||
void ZeldaSaveMusicStateToRam_Locked();
|
||||
void ZeldaPushApuState();
|
||||
|
||||
#endif // ZELDA3_AUDIO_H_
|
||||
@@ -1,659 +0,0 @@
|
||||
// This file is heavily influenced by Snes9x
|
||||
#include "third_party/gl_core/gl_core_3_1.h"
|
||||
#include "glsl_shader.h"
|
||||
#include "util.h"
|
||||
#include "config.h"
|
||||
#include <stdio.h>
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
#define STB_IMAGE_IMPLEMENTATION
|
||||
#define STBI_NO_THREAD_LOCALS
|
||||
#define STBI_ONLY_PNG
|
||||
#define STBI_MAX_DIMENSIONS 4096
|
||||
#define STBI_NO_FAILURE_STRINGS
|
||||
#include "third_party/stb/stb_image.h"
|
||||
|
||||
static GlslPass *ParseConfigKeyPass(GlslShader *gs, const char *key, const char *match) {
|
||||
char *endp;
|
||||
for (; *match; key++, match++) {
|
||||
if (*key != *match)
|
||||
return NULL;
|
||||
}
|
||||
if ((uint8)(*key - '0') >= 10)
|
||||
return NULL;
|
||||
uint pass = strtoul(key, &endp, 10);
|
||||
if (pass >= gs->n_pass || *endp != 0)
|
||||
return NULL;
|
||||
return gs->pass + pass + 1;
|
||||
}
|
||||
|
||||
static uint8 ParseScaleType(const char *s) {
|
||||
return StringEqualsNoCase(s, "source") ? GLSL_SOURCE :
|
||||
StringEqualsNoCase(s, "viewport") ? GLSL_VIEWPORT :
|
||||
StringEqualsNoCase(s, "absolute") ? GLSL_ABSOLUTE : GLSL_NONE;
|
||||
}
|
||||
|
||||
static uint ParseWrapMode(const char *s) {
|
||||
return StringEqualsNoCase(s, "repeat") ? GL_REPEAT :
|
||||
StringEqualsNoCase(s, "clamp_to_edge") ? GL_CLAMP_TO_EDGE :
|
||||
StringEqualsNoCase(s, "clamp") ? GL_CLAMP : GL_CLAMP_TO_BORDER;
|
||||
}
|
||||
|
||||
static void GlslPass_Initialize(GlslPass *pass) {
|
||||
pass->scale_x = 1.0f;
|
||||
pass->scale_y = 1.0f;
|
||||
pass->wrap_mode = GL_CLAMP_TO_BORDER;
|
||||
}
|
||||
|
||||
static void ParseTextures(GlslShader *gs, char *value) {
|
||||
char *id;
|
||||
GlslTexture **nextp = &gs->first_texture;
|
||||
for (int num = 0; (id = NextDelim(&value, ';')) != NULL && num < kGlslMaxTextures; num++) {
|
||||
GlslTexture *t = calloc(sizeof(GlslTexture), 1);
|
||||
t->id = strdup(id);
|
||||
t->wrap_mode = GL_CLAMP_TO_BORDER;
|
||||
t->filter = GL_NEAREST;
|
||||
*nextp = t;
|
||||
nextp = &t->next;
|
||||
}
|
||||
}
|
||||
|
||||
static bool ParseTextureKeyValue(GlslShader *gs, const char *key, const char *value) {
|
||||
for (GlslTexture *t = gs->first_texture; t != NULL; t = t->next) {
|
||||
const char *key2 = SkipPrefix(key, t->id);
|
||||
if (!key2) continue;
|
||||
if (*key2 == 0) {
|
||||
StrSet(&t->filename, value);
|
||||
return true;
|
||||
} else if (!strcmp(key2, "_wrap_mode")) {
|
||||
t->wrap_mode = ParseWrapMode(value);
|
||||
return true;
|
||||
} else if (!strcmp(key2, "_mipmap")) {
|
||||
t->mipmap = ParseBool(value, NULL);
|
||||
return true;
|
||||
} else if (!strcmp(key2, "_linear")) {
|
||||
t->filter = ParseBool(value, NULL) ? GL_LINEAR : GL_NEAREST;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static GlslParam *GlslShader_GetParam(GlslShader *gs, const char *id) {
|
||||
GlslParam **pp = &gs->first_param;
|
||||
for (; (*pp) != NULL; pp = &(*pp)->next)
|
||||
if (!strcmp((*pp)->id, id))
|
||||
return *pp;
|
||||
GlslParam *p = (GlslParam *)calloc(1, sizeof(GlslParam));
|
||||
*pp = p;
|
||||
p->id = strdup(id);
|
||||
return p;
|
||||
}
|
||||
|
||||
static void ParseParameters(GlslShader *gs, char *value) {
|
||||
char *id;
|
||||
while ((id = NextDelim(&value, ';')) != NULL)
|
||||
GlslShader_GetParam(gs, id);
|
||||
}
|
||||
|
||||
static bool ParseParameterKeyValue(GlslShader *gs, const char *key, const char *value) {
|
||||
for (GlslParam *p = gs->first_param; p != NULL; p = p->next) {
|
||||
if (strcmp(p->id, key) == 0) {
|
||||
p->value = atof(value);
|
||||
p->has_value = true;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static void GlslShader_InitializePasses(GlslShader *gs, int passes) {
|
||||
gs->n_pass = passes;
|
||||
gs->pass = (GlslPass *)calloc(gs->n_pass + 1, sizeof(GlslPass));
|
||||
for (int i = 0; i < gs->n_pass; i++)
|
||||
GlslPass_Initialize(gs->pass + i + 1);
|
||||
}
|
||||
|
||||
static bool GlslShader_ReadPresetFile(GlslShader *gs, const char *filename) {
|
||||
char *data = (char *)ReadWholeFile(filename, NULL), *data_org = data, *line;
|
||||
GlslPass *pass;
|
||||
if (data == NULL)
|
||||
return false;
|
||||
for (int lineno = 1; (line = NextLineStripComments(&data)) != NULL; lineno++) {
|
||||
char *value = SplitKeyValue(line), *t;
|
||||
if (value == NULL) {
|
||||
if (*line)
|
||||
fprintf(stderr, "%s:%d: Expecting key=value\n", filename, lineno);
|
||||
continue;
|
||||
}
|
||||
if (*value == '"') {
|
||||
for (t = ++value; *t && *t != '"'; t++);
|
||||
if (*t) *t = 0;
|
||||
}
|
||||
|
||||
if (gs->n_pass == 0) {
|
||||
if (strcmp(line, "shaders") != 0) {
|
||||
fprintf(stderr, "%s:%d: Expecting 'shaders'\n", filename, lineno);
|
||||
break;
|
||||
}
|
||||
int passes = strtoul(value, NULL, 10);
|
||||
if (passes < 1 || passes > kGlslMaxPasses)
|
||||
break;
|
||||
GlslShader_InitializePasses(gs, passes);
|
||||
continue;
|
||||
}
|
||||
if ((pass = ParseConfigKeyPass(gs, line, "filter_linear")) != NULL)
|
||||
pass->filter = ParseBool(value, NULL) ? GL_LINEAR : GL_NEAREST;
|
||||
else if ((pass = ParseConfigKeyPass(gs, line, "scale_type")) != NULL)
|
||||
pass->scale_type_x = pass->scale_type_y = ParseScaleType(value);
|
||||
else if ((pass = ParseConfigKeyPass(gs, line, "scale_type_x")) != NULL)
|
||||
pass->scale_type_x = ParseScaleType(value);
|
||||
else if ((pass = ParseConfigKeyPass(gs, line, "scale_type_y")) != NULL)
|
||||
pass->scale_type_y = ParseScaleType(value);
|
||||
else if ((pass = ParseConfigKeyPass(gs, line, "scale")) != NULL)
|
||||
pass->scale_x = pass->scale_y = atof(value);
|
||||
else if ((pass = ParseConfigKeyPass(gs, line, "scale_x")) != NULL)
|
||||
pass->scale_x = atof(value);
|
||||
else if ((pass = ParseConfigKeyPass(gs, line, "scale_y")) != NULL)
|
||||
pass->scale_y = atof(value);
|
||||
else if ((pass = ParseConfigKeyPass(gs, line, "shader")) != NULL)
|
||||
StrSet(&pass->filename, value);
|
||||
else if ((pass = ParseConfigKeyPass(gs, line, "wrap_mode")) != NULL)
|
||||
pass->wrap_mode = ParseWrapMode(value);
|
||||
else if ((pass = ParseConfigKeyPass(gs, line, "mipmap_input")) != NULL)
|
||||
pass->mipmap_input = ParseBool(value, NULL);
|
||||
else if ((pass = ParseConfigKeyPass(gs, line, "frame_count_mod")) != NULL)
|
||||
pass->frame_count_mod = atoi(value);
|
||||
else if ((pass = ParseConfigKeyPass(gs, line, "float_framebuffer")) != NULL)
|
||||
pass->float_framebuffer = ParseBool(value, NULL);
|
||||
else if ((pass = ParseConfigKeyPass(gs, line, "srgb_framebuffer")) != NULL)
|
||||
pass->srgb_framebuffer = ParseBool(value, NULL);
|
||||
else if ((pass = ParseConfigKeyPass(gs, line, "alias")) != NULL)
|
||||
;
|
||||
else if (strcmp(line, "textures") == 0 && gs->first_texture == NULL)
|
||||
ParseTextures(gs, value);
|
||||
else if (strcmp(line, "parameters") == 0)
|
||||
ParseParameters(gs, value);
|
||||
else if (!ParseTextureKeyValue(gs, line, value) && !ParseParameterKeyValue(gs, line, value))
|
||||
fprintf(stderr, "%s:%d: Unknown key '%s'\n", filename, lineno, line);
|
||||
}
|
||||
free(data_org);
|
||||
return gs->n_pass != 0;
|
||||
}
|
||||
|
||||
void GlslShader_ReadShaderFile(GlslShader *gs, const char *filename, ByteArray *result) {
|
||||
char *data = (char *)ReadWholeFile(filename, NULL), *data_org = data, *line;
|
||||
if (data == NULL) {
|
||||
fprintf(stderr, "Unable to read file '%s'\n", filename);
|
||||
return;
|
||||
}
|
||||
while ((line = NextDelim(&data, '\n')) != NULL) {
|
||||
size_t linelen = strlen(line);
|
||||
if (linelen >= 8 && memcmp(line, "#include", 8) == 0) {
|
||||
char *tt = line + 8;
|
||||
char *new_filename = ReplaceFilenameWithNewPath(filename, NextPossiblyQuotedString(&tt));
|
||||
GlslShader_ReadShaderFile(gs, new_filename, result);
|
||||
free(new_filename);
|
||||
} else if (linelen >= 17 && memcmp(line, "#pragma parameter", 17) == 0) {
|
||||
char *tt = line + 17;
|
||||
GlslParam *param = GlslShader_GetParam(gs, NextPossiblyQuotedString(&tt));
|
||||
NextPossiblyQuotedString(&tt); // skip name
|
||||
float value = atof(NextPossiblyQuotedString(&tt));
|
||||
if (!param->has_value)
|
||||
param->value = value;
|
||||
param->min = atof(NextPossiblyQuotedString(&tt));
|
||||
param->max = atof(NextPossiblyQuotedString(&tt));
|
||||
// skip step
|
||||
} else {
|
||||
line[linelen] = '\n';
|
||||
ByteArray_AppendData(result, (uint8 *)line, linelen + 1);
|
||||
}
|
||||
}
|
||||
free(data_org);
|
||||
}
|
||||
|
||||
size_t LengthOfInitialComments(const uint8 *data, size_t size) {
|
||||
const uint8 *data_org = data, *data_end = data + size;
|
||||
while (data != data_end) {
|
||||
uint8 c = *data++;
|
||||
if (c == ' ' || c == '\t' || c == '\r' || c == '\n')
|
||||
continue;
|
||||
if (c != '/' || data == data_end) {
|
||||
data--;
|
||||
break;
|
||||
}
|
||||
c = *data++;
|
||||
if (c == '/') {
|
||||
while (data != data_end && *data++ != '\n') {}
|
||||
} else if (c == '*') {
|
||||
do {
|
||||
if (data_end - data < 2)
|
||||
return 0;
|
||||
} while (*data++ != '*' || data[0] != '/');
|
||||
data++;
|
||||
} else {
|
||||
data--;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return data - data_org;
|
||||
}
|
||||
|
||||
static bool GlslPass_Compile(GlslPass *p, uint type, const uint8 *data, size_t size, bool use_opengl_es) {
|
||||
static const char kVertexPrefix[] = "#define VERTEX\n#define PARAMETER_UNIFORM\n";
|
||||
static const char kFragmentPrefixCore[] = "#define FRAGMENT\n#define PARAMETER_UNIFORM\n";
|
||||
static const char kFragmentPrefixEs[] = "#define FRAGMENT\n#define PARAMETER_UNIFORM\n" \
|
||||
"precision mediump float;";
|
||||
const GLchar *strings[3];
|
||||
GLint lengths[3];
|
||||
char buffer[256];
|
||||
GLint compile_status = 0;
|
||||
size_t skip = 0;
|
||||
|
||||
size_t commsize = LengthOfInitialComments(data, size);
|
||||
data += commsize, size -= commsize;
|
||||
|
||||
if (size < 8 || memcmp(data, "#version", 8) != 0) {
|
||||
if (!use_opengl_es) {
|
||||
strings[0] = "#version 330\n";
|
||||
lengths[0] = sizeof("#version 330\n") - 1;
|
||||
} else {
|
||||
strings[0] = "#version 300 es\n";
|
||||
lengths[0] = sizeof("#version 300 es\n") - 1;
|
||||
}
|
||||
} else {
|
||||
while (skip < size && data[skip++] != '\n') {}
|
||||
strings[0] = (char*)data;
|
||||
lengths[0] = (int)skip;
|
||||
}
|
||||
if (type == GL_VERTEX_SHADER) {
|
||||
strings[1] = (char *)kVertexPrefix;
|
||||
lengths[1] = sizeof(kVertexPrefix) - 1;
|
||||
} else {
|
||||
strings[1] = (use_opengl_es) ? (char *)kFragmentPrefixEs : kFragmentPrefixCore;
|
||||
lengths[1] = (use_opengl_es) ? sizeof(kFragmentPrefixEs) - 1 : sizeof(kFragmentPrefixCore) - 1;
|
||||
}
|
||||
strings[2] = (GLchar *)data + skip;
|
||||
lengths[2] = (int)(size - skip);
|
||||
uint shader = glCreateShader(type);
|
||||
glShaderSource(shader, 3, strings, lengths);
|
||||
glCompileShader(shader);
|
||||
glGetShaderiv(shader, GL_COMPILE_STATUS, &compile_status);
|
||||
buffer[0] = 0;
|
||||
glGetShaderInfoLog(shader, sizeof(buffer), NULL, buffer);
|
||||
if (compile_status != GL_TRUE || buffer[0]) {
|
||||
fprintf(stderr, "%s compiling %s shader in file '%s':\n%s\n",
|
||||
compile_status != GL_TRUE ? "Error" : "While",
|
||||
type == GL_VERTEX_SHADER ? "vertex" : "fragment", p->filename, buffer);
|
||||
}
|
||||
if (compile_status == GL_TRUE)
|
||||
glAttachShader(p->gl_program, shader);
|
||||
glDeleteShader(shader);
|
||||
return (compile_status == GL_TRUE);
|
||||
}
|
||||
|
||||
static void GlslTextureUniform_Read(uint program, const char *prefix, int i, GlslTextureUniform *result) {
|
||||
char buf[40];
|
||||
char *e = &buf[snprintf(buf, sizeof(buf), i >= 0 ? "%s%u" : "%s", prefix, i)];
|
||||
memcpy(e, "Texture", 8);
|
||||
result->Texture = glGetUniformLocation(program, buf);
|
||||
memcpy(e, "InputSize", 10);
|
||||
result->InputSize = glGetUniformLocation(program, buf);
|
||||
memcpy(e, "TextureSize", 12);
|
||||
result->TextureSize = glGetUniformLocation(program, buf);
|
||||
memcpy(e, "TexCoord", 9);
|
||||
result->TexCoord = glGetAttribLocation(program, buf);
|
||||
}
|
||||
|
||||
static const float kMvpMatrixOrtho[16] = {
|
||||
2.0f, 0.0f, 0.0f, 0.0f,
|
||||
0.0f, 2.0f, 0.0f, 0.0f,
|
||||
0.0f, 0.0f, -1.0f, 0.0f,
|
||||
-1.0f, -1.0f, 0.0f, 1.0f
|
||||
};
|
||||
|
||||
static void GlslShader_GetUniforms(GlslShader *gs) {
|
||||
int pass_idx = 1;
|
||||
gs->max_prev_frame = 0;
|
||||
for (GlslPass *p = gs->pass + 1, *p_end = p + gs->n_pass; p < p_end; p++, pass_idx++) {
|
||||
uint program = p->gl_program;
|
||||
glUseProgram(program);
|
||||
|
||||
GLint MVPMatrix = glGetUniformLocation(program, "MVPMatrix");
|
||||
if (MVPMatrix >= 0)
|
||||
glUniformMatrix4fv(MVPMatrix, 1, GL_FALSE, kMvpMatrixOrtho);
|
||||
|
||||
GlslTextureUniform_Read(program, "", -1, &p->unif.Top);
|
||||
p->unif.OutputSize = glGetUniformLocation(program, "OutputSize");
|
||||
p->unif.FrameCount = glGetUniformLocation(program, "FrameCount");
|
||||
p->unif.FrameDirection = glGetUniformLocation(program, "FrameDirection");
|
||||
p->unif.LUTTexCoord = glGetAttribLocation(program, "LUTTexCoord");
|
||||
p->unif.VertexCoord = glGetAttribLocation(program, "VertexCoord");
|
||||
GlslTextureUniform_Read(program, "Orig", -1, &p->unif.Orig);
|
||||
for (int j = 0; j < 7; j++) {
|
||||
GlslTextureUniform_Read(program, "Prev", j ? j : -1, &p->unif.Prev[j]);
|
||||
if (p->unif.Prev[j].Texture >= 0)
|
||||
gs->max_prev_frame = j + 1;
|
||||
}
|
||||
for (int j = 0; j < gs->n_pass; j++) {
|
||||
GlslTextureUniform_Read(program, "Pass", j, &p->unif.Pass[j]);
|
||||
GlslTextureUniform_Read(program, "PassPrev", j, &p->unif.PassPrev[j]);
|
||||
}
|
||||
GlslTexture *t = gs->first_texture;
|
||||
for (int j = 0; t != NULL; t = t->next, j++)
|
||||
p->unif.Texture[j] = glGetUniformLocation(program, t->id);
|
||||
for (GlslParam *pa = gs->first_param; pa != NULL; pa = pa->next)
|
||||
pa->uniform[pass_idx] = glGetUniformLocation(program, pa->id);
|
||||
}
|
||||
glUseProgram(0);
|
||||
}
|
||||
|
||||
static bool IsGlslFilename(const char *filename) {
|
||||
size_t len = strlen(filename);
|
||||
return len >= 5 && memcmp(filename + len - 5, ".glsl", 5) == 0;
|
||||
}
|
||||
|
||||
GlslShader *GlslShader_CreateFromFile(const char *filename, bool opengl_es) {
|
||||
char buffer[256];
|
||||
GLint link_status;
|
||||
ByteArray shader_code = { 0 };
|
||||
bool success = false;
|
||||
GlslShader *gs = (GlslShader *)calloc(sizeof(GlslShader), 1);
|
||||
if (!gs)
|
||||
return gs;
|
||||
|
||||
if (IsGlslFilename(filename)) {
|
||||
GlslShader_InitializePasses(gs, 1);
|
||||
gs->pass[1].filename = strdup(filename);
|
||||
filename = "";
|
||||
} else {
|
||||
if (!GlslShader_ReadPresetFile(gs, filename)) {
|
||||
fprintf(stderr, "Unable to read file '%s'\n", filename);
|
||||
goto FAIL;
|
||||
}
|
||||
}
|
||||
for (int i = 1; i <= gs->n_pass; i++) {
|
||||
GlslPass *p = gs->pass + i;
|
||||
shader_code.size = 0;
|
||||
|
||||
if (p->filename == NULL) {
|
||||
fprintf(stderr, "shader%d attribute missing\n", i - 1);
|
||||
goto FAIL;
|
||||
}
|
||||
|
||||
char *new_filename = ReplaceFilenameWithNewPath(filename, p->filename);
|
||||
GlslShader_ReadShaderFile(gs, new_filename, &shader_code);
|
||||
free(new_filename);
|
||||
|
||||
if (shader_code.size == 0) {
|
||||
fprintf(stderr, "Couldn't read shader in file '%s'\n", p->filename);
|
||||
goto FAIL;
|
||||
}
|
||||
p->gl_program = glCreateProgram();
|
||||
if (!GlslPass_Compile(p, GL_VERTEX_SHADER, shader_code.data, shader_code.size, opengl_es) ||
|
||||
!GlslPass_Compile(p, GL_FRAGMENT_SHADER, shader_code.data, shader_code.size, opengl_es)) {
|
||||
goto FAIL;
|
||||
}
|
||||
glLinkProgram(p->gl_program);
|
||||
glGetProgramiv(p->gl_program, GL_LINK_STATUS, &link_status);
|
||||
buffer[0] = 0;
|
||||
glGetProgramInfoLog(p->gl_program, sizeof(buffer), NULL, buffer);
|
||||
if (link_status != GL_TRUE || buffer[0])
|
||||
fprintf(stderr, "%s linking shader in file '%s':\n%s\n",
|
||||
link_status != GL_TRUE ? "Error" : "While", p->filename, buffer);
|
||||
if (link_status != GL_TRUE)
|
||||
goto FAIL;
|
||||
glGenFramebuffers(1, &p->gl_fbo);
|
||||
glGenTextures(1, &p->gl_texture);
|
||||
}
|
||||
for (GlslTexture *t = gs->first_texture; t; t = t->next) {
|
||||
glGenTextures(1, &t->gl_texture);
|
||||
glBindTexture(GL_TEXTURE_2D, t->gl_texture);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, t->wrap_mode);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, t->wrap_mode);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, t->filter);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, t->mipmap ?
|
||||
(t->filter == GL_LINEAR ? GL_LINEAR_MIPMAP_LINEAR : GL_NEAREST_MIPMAP_NEAREST) : t->filter);
|
||||
if (t->filename) {
|
||||
char *new_filename = ReplaceFilenameWithNewPath(filename, t->filename);
|
||||
int imw, imh, imn;
|
||||
unsigned char *data = stbi_load(new_filename, &imw, &imh, &imn, 0);
|
||||
if (!data) {
|
||||
fprintf(stderr, "Unable to read PNG '%s'\n", new_filename);
|
||||
} else {
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, imw, imh, 0,
|
||||
(imn == 4) ? GL_RGBA : (imn == 3) ? GL_RGB : GL_LUMINANCE, GL_UNSIGNED_BYTE, data);
|
||||
}
|
||||
free(data);
|
||||
free(new_filename);
|
||||
}
|
||||
if (t->mipmap)
|
||||
glGenerateMipmap(GL_TEXTURE_2D);
|
||||
}
|
||||
for (GlslParam *p = gs->first_param; p; p = p->next)
|
||||
p->value = (p->value < p->min) ? p->min : (p->value > p->max) ? p->max : p->value;
|
||||
|
||||
GlslShader_GetUniforms(gs);
|
||||
|
||||
for (int i = 0; i < gs->max_prev_frame; i++)
|
||||
glGenTextures(1, &gs->prev_frame[-i - 1 & 7].gl_texture);
|
||||
|
||||
static const GLfloat kTexCoords[16] = {
|
||||
0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f,
|
||||
0.0f, 1.0f, 1.0f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f
|
||||
};
|
||||
glGenBuffers(1, &gs->gl_vbo);
|
||||
glBindBuffer(GL_ARRAY_BUFFER, gs->gl_vbo);
|
||||
glBufferData(GL_ARRAY_BUFFER, sizeof(kTexCoords), kTexCoords, GL_STATIC_DRAW);
|
||||
glBindBuffer(GL_ARRAY_BUFFER, 0);
|
||||
|
||||
success = true;
|
||||
FAIL:
|
||||
if (!success) {
|
||||
GlslShader_Destroy(gs);
|
||||
gs = NULL;
|
||||
}
|
||||
ByteArray_Destroy(&shader_code);
|
||||
return gs;
|
||||
}
|
||||
|
||||
void GlslShader_Destroy(GlslShader *gs) {
|
||||
for (GlslPass *p = gs->pass + 1, *p_end = p + gs->n_pass; p < p_end; p++) {
|
||||
glDeleteProgram(p->gl_program);
|
||||
glDeleteTextures(1, &p->gl_texture);
|
||||
glDeleteFramebuffers(1, &p->gl_fbo);
|
||||
free(p->filename);
|
||||
}
|
||||
free(gs->pass);
|
||||
GlslTexture *t;
|
||||
while ((t = gs->first_texture) != NULL) {
|
||||
gs->first_texture = t->next;
|
||||
glDeleteTextures(1, &t->gl_texture);
|
||||
free(t->id);
|
||||
free(t->filename);
|
||||
free(t);
|
||||
}
|
||||
GlslParam *pp;
|
||||
while ((pp = gs->first_param) != NULL) {
|
||||
gs->first_param = pp->next;
|
||||
free(pp->id);
|
||||
free(pp);
|
||||
}
|
||||
for (int i = 0; i < 8; i++)
|
||||
glDeleteTextures(1, &gs->prev_frame[i].gl_texture);
|
||||
glDeleteBuffers(1, &gs->gl_vbo);
|
||||
free(gs);
|
||||
}
|
||||
|
||||
enum {
|
||||
kMaxVaosInRenderCtx = 11 + kGlslMaxPasses * 2
|
||||
};
|
||||
|
||||
typedef struct RenderCtx {
|
||||
uint texture_unit;
|
||||
uint offset;
|
||||
uint num_vaos;
|
||||
uint vaos[kMaxVaosInRenderCtx];
|
||||
} RenderCtx;
|
||||
|
||||
static void RenderCtx_SetTexture(RenderCtx *ctx, int textureu, uint texture_id) {
|
||||
if (textureu >= 0) {
|
||||
glActiveTexture(GL_TEXTURE0 + ctx->texture_unit);
|
||||
glBindTexture(GL_TEXTURE_2D, texture_id);
|
||||
glUniform1i(textureu, ctx->texture_unit++);
|
||||
}
|
||||
}
|
||||
|
||||
static void RenderCtx_SetTexCoords(RenderCtx *ctx, int tex_coord, uintptr_t offset) {
|
||||
if (tex_coord >= 0) {
|
||||
assert(ctx->num_vaos < kMaxVaosInRenderCtx);
|
||||
ctx->vaos[ctx->num_vaos++] = tex_coord;
|
||||
glVertexAttribPointer(tex_coord, 2, GL_FLOAT, GL_FALSE, 0, (void *)offset);
|
||||
glEnableVertexAttribArray(tex_coord);
|
||||
}
|
||||
}
|
||||
|
||||
static void RenderCtx_SetGlslTextureUniform(RenderCtx *ctx, GlslTextureUniform *u,
|
||||
int width, int height, uint texture) {
|
||||
float size[2] = { width, height };
|
||||
RenderCtx_SetTexture(ctx, u->Texture, texture);
|
||||
if (u->InputSize >= 0)
|
||||
glUniform2fv(u->InputSize, 1, size);
|
||||
if (u->TextureSize >= 0)
|
||||
glUniform2fv(u->TextureSize, 1, size);
|
||||
RenderCtx_SetTexCoords(ctx, u->TexCoord, ctx->offset);
|
||||
}
|
||||
|
||||
static void GlslShader_SetShaderVars(GlslShader *gs, RenderCtx *ctx, int pass) {
|
||||
GlslPass *p = &gs->pass[pass];
|
||||
|
||||
RenderCtx_SetGlslTextureUniform(ctx, &p->unif.Top, p[-1].width, p[-1].height, p[-1].gl_texture);
|
||||
if (p->unif.OutputSize >= 0) {
|
||||
float output_size[2] = { (float)p[0].width, (float)p[0].height };
|
||||
glUniform2fv(p->unif.OutputSize, 1, output_size);
|
||||
}
|
||||
if (p->unif.FrameCount >= 0)
|
||||
glUniform1i(p->unif.FrameCount, p->frame_count_mod ?
|
||||
gs->frame_count % p->frame_count_mod : gs->frame_count);
|
||||
if (p->unif.FrameDirection >= 0)
|
||||
glUniform1i(p->unif.FrameDirection, 1);
|
||||
RenderCtx_SetTexCoords(ctx, p->unif.LUTTexCoord, ctx->offset);
|
||||
RenderCtx_SetTexCoords(ctx, p->unif.VertexCoord, 0);
|
||||
RenderCtx_SetGlslTextureUniform(ctx, &p->unif.Orig, gs->pass[0].width, gs->pass[0].height, gs->pass[0].gl_texture);
|
||||
// Prev, Prev1-Prev6 uniforms
|
||||
for (int i = 0; i < gs->max_prev_frame; i++) {
|
||||
GlTextureWithSize *t = &gs->prev_frame[(gs->frame_count - 1 - i) & 7];
|
||||
assert(t->gl_texture != 0);
|
||||
if (t->width)
|
||||
RenderCtx_SetGlslTextureUniform(ctx, &p->unif.Prev[i], t->width, t->height, t->gl_texture);
|
||||
}
|
||||
// Texture uniforms
|
||||
int tctr = 0;
|
||||
for (GlslTexture *t = gs->first_texture; t; t = t->next, tctr++)
|
||||
RenderCtx_SetTexture(ctx, p->unif.Texture[tctr], t->gl_texture);
|
||||
// PassX uniforms
|
||||
for (int i = 1; i < pass; i++)
|
||||
RenderCtx_SetGlslTextureUniform(ctx, &p->unif.Pass[i], gs->pass[i].width, gs->pass[i].height, gs->pass[i].gl_texture);
|
||||
// PassPrevX uniforms
|
||||
for (int i = 1; i < pass; i++)
|
||||
RenderCtx_SetGlslTextureUniform(ctx, &p->unif.PassPrev[pass - i], gs->pass[i].width, gs->pass[i].height, gs->pass[i].gl_texture);
|
||||
// #parameter uniforms
|
||||
for (GlslParam *pa = gs->first_param; pa != NULL; pa = pa->next)
|
||||
if (pa->uniform[pass] >= 0)
|
||||
glUniform1f(pa->uniform[pass], pa->value);
|
||||
|
||||
glActiveTexture(GL_TEXTURE0);
|
||||
}
|
||||
|
||||
void GlslShader_Render(GlslShader *gs, GlTextureWithSize *tex, int viewport_x, int viewport_y, int viewport_width, int viewport_height) {
|
||||
gs->pass[0].gl_texture = tex->gl_texture;
|
||||
gs->pass[0].width = tex->width;
|
||||
gs->pass[0].height = tex->height;
|
||||
|
||||
GLint previous_framebuffer;
|
||||
glGetIntegerv(GL_FRAMEBUFFER_BINDING, &previous_framebuffer);
|
||||
glBindBuffer(GL_ARRAY_BUFFER, gs->gl_vbo);
|
||||
|
||||
for (int pass = 1; pass <= gs->n_pass; pass++) {
|
||||
bool last_pass = pass == gs->n_pass;
|
||||
GlslPass *p = gs->pass + pass;
|
||||
|
||||
switch (p->scale_type_x) {
|
||||
case GLSL_ABSOLUTE: p->width = (uint16)p->scale_x; break;
|
||||
case GLSL_SOURCE: p->width = (uint16)(p[-1].width * p->scale_x); break;
|
||||
case GLSL_VIEWPORT: p->width = (uint16)(viewport_width * p->scale_x); break;
|
||||
default: p->width = (uint16)(last_pass ? viewport_width : (p[-1].width * p->scale_x)); break;
|
||||
}
|
||||
|
||||
switch (p->scale_type_y) {
|
||||
case GLSL_ABSOLUTE: p->height = (uint16)p->scale_y; break;
|
||||
case GLSL_SOURCE: p->height = (uint16)(p[-1].height * p->scale_y); break;
|
||||
case GLSL_VIEWPORT: p->height = (uint16)(viewport_height * p->scale_y); break;
|
||||
default: p->height = (uint16)(last_pass ? viewport_height : (p[-1].height * p->scale_y)); break;
|
||||
}
|
||||
|
||||
if (!last_pass) {
|
||||
// output to a texture
|
||||
glBindTexture(GL_TEXTURE_2D, p->gl_texture);
|
||||
if (p->srgb_framebuffer) {
|
||||
glEnable(GL_FRAMEBUFFER_SRGB);
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_SRGB8_ALPHA8, p->width, p->height, 0, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8, NULL);
|
||||
} else {
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, p->float_framebuffer ? GL_RGBA32F : GL_RGBA,
|
||||
p->width, p->height, 0, GL_RGBA,
|
||||
p->float_framebuffer ? GL_FLOAT : GL_UNSIGNED_INT_8_8_8_8, NULL);
|
||||
}
|
||||
glViewport(0, 0, p->width, p->height);
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, p->gl_fbo);
|
||||
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, p->gl_texture, 0);
|
||||
} else {
|
||||
// output to screen
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, previous_framebuffer);
|
||||
glViewport(viewport_x, viewport_y, viewport_width, viewport_height);
|
||||
}
|
||||
|
||||
glBindTexture(GL_TEXTURE_2D, p[-1].gl_texture);
|
||||
|
||||
uint filter = p->filter ? p->filter : (last_pass && g_config.linear_filtering) ? GL_LINEAR : GL_NEAREST;
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, filter);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, p->mipmap_input ?
|
||||
(filter == GL_LINEAR ? GL_LINEAR_MIPMAP_LINEAR : GL_NEAREST_MIPMAP_NEAREST) : filter);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, p->wrap_mode);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, p->wrap_mode);
|
||||
if (p->mipmap_input)
|
||||
glGenerateMipmap(GL_TEXTURE_2D);
|
||||
glUseProgram(p->gl_program);
|
||||
|
||||
RenderCtx ctx;
|
||||
ctx.texture_unit = ctx.num_vaos = 0;
|
||||
ctx.offset = last_pass ? sizeof(float) * 8 : 0;
|
||||
GlslShader_SetShaderVars(gs, &ctx, pass);
|
||||
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
|
||||
for(int i = 0; i < ctx.num_vaos; i++)
|
||||
glDisableVertexAttribArray(ctx.vaos[i]);
|
||||
if (p->srgb_framebuffer)
|
||||
glDisable(GL_FRAMEBUFFER_SRGB);
|
||||
}
|
||||
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, previous_framebuffer);
|
||||
glUseProgram(0);
|
||||
glBindTexture(GL_TEXTURE_2D, 0);
|
||||
glBindBuffer(GL_ARRAY_BUFFER, 0);
|
||||
|
||||
// Store the input frame in the prev array, and extract the next one.
|
||||
if (gs->max_prev_frame != 0) {
|
||||
// 01234567
|
||||
// 43210
|
||||
// ^-- store pos
|
||||
// ^-- load pos
|
||||
GlTextureWithSize *store_pos = &gs->prev_frame[gs->frame_count & 7];
|
||||
GlTextureWithSize *load_pos = &gs->prev_frame[gs->frame_count - gs->max_prev_frame & 7];
|
||||
assert(store_pos->gl_texture == 0);
|
||||
*store_pos = *tex;
|
||||
*tex = *load_pos;
|
||||
memset(load_pos, 0, sizeof(GlTextureWithSize));
|
||||
}
|
||||
|
||||
gs->frame_count++;
|
||||
}
|
||||
|
||||
@@ -1,99 +0,0 @@
|
||||
#ifndef ZELDA3_GLSL_SHADER_H_
|
||||
#define ZELDA3_GLSL_SHADER_H_
|
||||
|
||||
#include "types.h"
|
||||
|
||||
enum {
|
||||
kGlslMaxPasses = 20,
|
||||
kGlslMaxTextures = 10,
|
||||
};
|
||||
|
||||
enum GLSLScaleType {
|
||||
GLSL_NONE,
|
||||
GLSL_SOURCE,
|
||||
GLSL_VIEWPORT,
|
||||
GLSL_ABSOLUTE
|
||||
};
|
||||
|
||||
typedef struct GlslTextureUniform {
|
||||
int Texture;
|
||||
int InputSize;
|
||||
int TextureSize;
|
||||
int TexCoord;
|
||||
} GlslTextureUniform;
|
||||
|
||||
typedef struct GlslUniforms {
|
||||
GlslTextureUniform Top;
|
||||
int OutputSize;
|
||||
int FrameCount, FrameDirection;
|
||||
int LUTTexCoord;
|
||||
int VertexCoord;
|
||||
GlslTextureUniform Orig;
|
||||
GlslTextureUniform Prev[7];
|
||||
GlslTextureUniform Pass[kGlslMaxPasses];
|
||||
GlslTextureUniform PassPrev[kGlslMaxPasses];
|
||||
int Texture[kGlslMaxTextures];
|
||||
} GlslUniforms;
|
||||
|
||||
typedef struct GlslPass {
|
||||
char *filename;
|
||||
uint8 scale_type_x, scale_type_y;
|
||||
bool float_framebuffer;
|
||||
bool srgb_framebuffer;
|
||||
bool mipmap_input;
|
||||
float scale_x, scale_y;
|
||||
uint wrap_mode;
|
||||
uint frame_count_mod;
|
||||
uint frame_count;
|
||||
uint gl_program, gl_fbo;
|
||||
uint filter;
|
||||
uint gl_texture;
|
||||
uint16 width, height;
|
||||
GlslUniforms unif;
|
||||
} GlslPass;
|
||||
|
||||
typedef struct GlTextureWithSize {
|
||||
uint gl_texture;
|
||||
uint16 width, height;
|
||||
} GlTextureWithSize;
|
||||
|
||||
typedef struct GlslTexture {
|
||||
struct GlslTexture *next;
|
||||
char *id;
|
||||
char *filename;
|
||||
uint filter;
|
||||
uint gl_texture;
|
||||
uint wrap_mode;
|
||||
bool mipmap;
|
||||
int width;
|
||||
int height;
|
||||
} GlslTexture;
|
||||
|
||||
typedef struct GlslParam {
|
||||
struct GlslParam *next;
|
||||
char *id;
|
||||
bool has_value;
|
||||
float value;
|
||||
float min;
|
||||
float max;
|
||||
uint uniform[kGlslMaxPasses];
|
||||
} GlslParam;
|
||||
|
||||
typedef struct GlslShader {
|
||||
int n_pass;
|
||||
GlslPass *pass;
|
||||
GlslParam *first_param;
|
||||
GlslTexture *first_texture;
|
||||
uint *gl_vao;
|
||||
uint gl_vbo;
|
||||
uint frame_count;
|
||||
int max_prev_frame;
|
||||
GlTextureWithSize prev_frame[8];
|
||||
} GlslShader;
|
||||
|
||||
GlslShader *GlslShader_CreateFromFile(const char *filename, bool opengl_es);
|
||||
void GlslShader_Destroy(GlslShader *gs);
|
||||
void GlslShader_Render(GlslShader *gs, GlTextureWithSize *tex, int viewport_x, int viewport_y, int viewport_width, int viewport_height);
|
||||
|
||||
|
||||
#endif // ZELDA3_GLSL_SHADER_H_
|
||||
266
src/opengl.c
266
src/opengl.c
@@ -1,266 +0,0 @@
|
||||
#include "third_party/gl_core/gl_core_3_1.h"
|
||||
#include <SDL.h>
|
||||
#include <stdio.h>
|
||||
#include <stdbool.h>
|
||||
#include "types.h"
|
||||
#include "util.h"
|
||||
#include "glsl_shader.h"
|
||||
#include "config.h"
|
||||
|
||||
#define CODE(...) #__VA_ARGS__
|
||||
|
||||
static SDL_Window *g_window;
|
||||
static uint8 *g_screen_buffer;
|
||||
static size_t g_screen_buffer_size;
|
||||
static int g_draw_width, g_draw_height;
|
||||
static unsigned int g_program, g_VAO;
|
||||
static GlTextureWithSize g_texture;
|
||||
static GlslShader *g_glsl_shader;
|
||||
static bool g_opengl_es;
|
||||
|
||||
static void GL_APIENTRY MessageCallback(GLenum source,
|
||||
GLenum type,
|
||||
GLuint id,
|
||||
GLenum severity,
|
||||
GLsizei length,
|
||||
const GLchar *message,
|
||||
const void *userParam) {
|
||||
if (type == GL_DEBUG_TYPE_OTHER)
|
||||
return;
|
||||
|
||||
fprintf(stderr, "GL CALLBACK: %s type = 0x%x, severity = 0x%x, message = %s\n",
|
||||
(type == GL_DEBUG_TYPE_ERROR ? "** GL ERROR **" : ""),
|
||||
type, severity, message);
|
||||
if (type == GL_DEBUG_TYPE_ERROR)
|
||||
Die("OpenGL error!\n");
|
||||
}
|
||||
|
||||
static bool OpenGLRenderer_Init(SDL_Window *window) {
|
||||
g_window = window;
|
||||
SDL_GLContext context = SDL_GL_CreateContext(window);
|
||||
(void)context;
|
||||
|
||||
SDL_GL_SetSwapInterval(1);
|
||||
ogl_LoadFunctions();
|
||||
|
||||
if (!g_opengl_es) {
|
||||
if (!ogl_IsVersionGEQ(3, 3))
|
||||
Die("You need OpenGL 3.3");
|
||||
} else {
|
||||
int majorVersion = 0, minorVersion = 0;
|
||||
SDL_GL_GetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, &majorVersion);
|
||||
SDL_GL_GetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, &minorVersion);
|
||||
if (majorVersion < 3)
|
||||
Die("You need OpenGL ES 3.0");
|
||||
|
||||
}
|
||||
|
||||
if (kDebugFlag) {
|
||||
glEnable(GL_DEBUG_OUTPUT);
|
||||
glEnable(GL_DEBUG_OUTPUT_SYNCHRONOUS);
|
||||
glDebugMessageCallback(MessageCallback, 0);
|
||||
}
|
||||
|
||||
glGenTextures(1, &g_texture.gl_texture);
|
||||
|
||||
static const float kVertices[] = {
|
||||
// positions // texture coords
|
||||
-1.0f, 1.0f, 0.0f, 0.0f, 0.0f, // top left
|
||||
-1.0f, -1.0f, 0.0f, 0.0f, 1.0f, // bottom left
|
||||
1.0f, 1.0f, 0.0f, 1.0f, 0.0f, // top right
|
||||
1.0f, -1.0f, 0.0f, 1.0f, 1.0f, // bottom right
|
||||
};
|
||||
|
||||
// create a vertex buffer object
|
||||
unsigned int vbo;
|
||||
glGenBuffers(1, &vbo);
|
||||
|
||||
// vertex array object
|
||||
glGenVertexArrays(1, &g_VAO);
|
||||
// 1. bind Vertex Array Object
|
||||
glBindVertexArray(g_VAO);
|
||||
// 2. copy our vertices array in a buffer for OpenGL to use
|
||||
glBindBuffer(GL_ARRAY_BUFFER, vbo);
|
||||
glBufferData(GL_ARRAY_BUFFER, sizeof(kVertices), kVertices, GL_STATIC_DRAW);
|
||||
// position attribute
|
||||
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 5 * sizeof(float), (void *)0);
|
||||
glEnableVertexAttribArray(0);
|
||||
// texture coord attribute
|
||||
glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 5 * sizeof(float), (void *)(3 * sizeof(float)));
|
||||
glEnableVertexAttribArray(1);
|
||||
|
||||
// vertex shader
|
||||
const GLchar *vs_code_core = "#version 330 core\n" CODE(
|
||||
layout(location = 0) in vec3 aPos;
|
||||
layout(location = 1) in vec2 aTexCoord;
|
||||
out vec2 TexCoord;
|
||||
void main() {
|
||||
gl_Position = vec4(aPos, 1.0);
|
||||
TexCoord = vec2(aTexCoord.x, aTexCoord.y);
|
||||
}
|
||||
);
|
||||
|
||||
const GLchar *vs_code_es = "#version 300 es\n" CODE(
|
||||
layout(location = 0) in vec3 aPos;
|
||||
layout(location = 1) in vec2 aTexCoord;
|
||||
out vec2 TexCoord;
|
||||
void main() {
|
||||
gl_Position = vec4(aPos, 1.0);
|
||||
TexCoord = vec2(aTexCoord.x, aTexCoord.y);
|
||||
}
|
||||
);
|
||||
|
||||
const GLchar *vs_code = g_opengl_es ? vs_code_es : vs_code_core;
|
||||
unsigned int vs = glCreateShader(GL_VERTEX_SHADER);
|
||||
glShaderSource(vs, 1, &vs_code, NULL);
|
||||
glCompileShader(vs);
|
||||
|
||||
int success;
|
||||
char infolog[512];
|
||||
glGetShaderiv(vs, GL_COMPILE_STATUS, &success);
|
||||
if (!success) {
|
||||
glGetShaderInfoLog(vs, 512, NULL, infolog);
|
||||
printf("%s\n", infolog);
|
||||
}
|
||||
|
||||
// fragment shader
|
||||
const GLchar *fs_code_core = "#version 330 core\n" CODE(
|
||||
out vec4 FragColor;
|
||||
in vec2 TexCoord;
|
||||
// texture samplers
|
||||
uniform sampler2D texture1;
|
||||
void main() {
|
||||
FragColor = texture(texture1, TexCoord);
|
||||
}
|
||||
);
|
||||
|
||||
const GLchar *fs_code_es = "#version 300 es\n" CODE(
|
||||
precision mediump float;
|
||||
out vec4 FragColor;
|
||||
in vec2 TexCoord;
|
||||
// texture samplers
|
||||
uniform sampler2D texture1;
|
||||
void main() {
|
||||
FragColor = texture(texture1, TexCoord);
|
||||
}
|
||||
);
|
||||
|
||||
|
||||
const GLchar *fs_code = g_opengl_es ? fs_code_es : fs_code_core;
|
||||
unsigned int fs = glCreateShader(GL_FRAGMENT_SHADER);
|
||||
glShaderSource(fs, 1, &fs_code, NULL);
|
||||
glCompileShader(fs);
|
||||
|
||||
glGetShaderiv(fs, GL_COMPILE_STATUS, &success);
|
||||
if (!success) {
|
||||
glGetShaderInfoLog(fs, 512, NULL, infolog);
|
||||
printf("%s\n", infolog);
|
||||
}
|
||||
|
||||
// create program
|
||||
int program = g_program = glCreateProgram();
|
||||
glAttachShader(program, vs);
|
||||
glAttachShader(program, fs);
|
||||
glLinkProgram(program);
|
||||
glGetProgramiv(program, GL_LINK_STATUS, &success);
|
||||
|
||||
if (!success) {
|
||||
glGetProgramInfoLog(program, 512, NULL, infolog);
|
||||
printf("%s\n", infolog);
|
||||
}
|
||||
|
||||
if (g_config.shader)
|
||||
g_glsl_shader = GlslShader_CreateFromFile(g_config.shader, g_opengl_es);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static void OpenGLRenderer_Destroy() {
|
||||
}
|
||||
|
||||
static void OpenGLRenderer_BeginDraw(int width, int height, uint8 **pixels, int *pitch) {
|
||||
int size = width * height;
|
||||
|
||||
if (size > g_screen_buffer_size) {
|
||||
g_screen_buffer_size = size;
|
||||
free(g_screen_buffer);
|
||||
g_screen_buffer = malloc(size * 4);
|
||||
}
|
||||
|
||||
g_draw_width = width;
|
||||
g_draw_height = height;
|
||||
*pixels = g_screen_buffer;
|
||||
*pitch = width * 4;
|
||||
}
|
||||
|
||||
static void OpenGLRenderer_EndDraw() {
|
||||
int drawable_width, drawable_height;
|
||||
|
||||
SDL_GL_GetDrawableSize(g_window, &drawable_width, &drawable_height);
|
||||
|
||||
int viewport_width = drawable_width, viewport_height = drawable_height;
|
||||
|
||||
if (!g_config.ignore_aspect_ratio) {
|
||||
if (viewport_width * g_draw_height < viewport_height * g_draw_width)
|
||||
viewport_height = viewport_width * g_draw_height / g_draw_width; // limit height
|
||||
else
|
||||
viewport_width = viewport_height * g_draw_width / g_draw_height; // limit width
|
||||
}
|
||||
|
||||
int viewport_x = (drawable_width - viewport_width) >> 1;
|
||||
int viewport_y = (viewport_height - viewport_height) >> 1;
|
||||
|
||||
glBindTexture(GL_TEXTURE_2D, g_texture.gl_texture);
|
||||
if (g_draw_width == g_texture.width && g_draw_height == g_texture.height) {
|
||||
if (!g_opengl_es)
|
||||
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, g_draw_width, g_draw_height, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, g_screen_buffer);
|
||||
else
|
||||
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, g_draw_width, g_draw_height, GL_BGRA, GL_UNSIGNED_BYTE, g_screen_buffer);
|
||||
} else {
|
||||
g_texture.width = g_draw_width;
|
||||
g_texture.height = g_draw_height;
|
||||
if (!g_opengl_es)
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, g_draw_width, g_draw_height, 0, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, g_screen_buffer);
|
||||
else
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, g_draw_width, g_draw_height, 0, GL_BGRA, GL_UNSIGNED_BYTE, g_screen_buffer);
|
||||
}
|
||||
|
||||
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
|
||||
glClear(GL_COLOR_BUFFER_BIT);
|
||||
|
||||
if (g_glsl_shader == NULL) {
|
||||
glViewport(viewport_x, viewport_y, viewport_width, viewport_height);
|
||||
glUseProgram(g_program);
|
||||
int filter = g_config.linear_filtering ? GL_LINEAR : GL_NEAREST;
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, filter);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, filter);
|
||||
glBindVertexArray(g_VAO);
|
||||
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
|
||||
} else {
|
||||
GlslShader_Render(g_glsl_shader, &g_texture, viewport_x, viewport_y, viewport_width, viewport_height);
|
||||
}
|
||||
|
||||
SDL_GL_SwapWindow(g_window);
|
||||
}
|
||||
|
||||
static const struct RendererFuncs kOpenGLRendererFuncs = {
|
||||
&OpenGLRenderer_Init,
|
||||
&OpenGLRenderer_Destroy,
|
||||
&OpenGLRenderer_BeginDraw,
|
||||
&OpenGLRenderer_EndDraw,
|
||||
};
|
||||
|
||||
void OpenGLRenderer_Create(struct RendererFuncs *funcs, bool use_opengl_es) {
|
||||
g_opengl_es = use_opengl_es;
|
||||
if (!g_opengl_es) {
|
||||
SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE);
|
||||
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3);
|
||||
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 3);
|
||||
} else {
|
||||
SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_ES);
|
||||
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3);
|
||||
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 0);
|
||||
}
|
||||
*funcs = kOpenGLRendererFuncs;
|
||||
}
|
||||
|
||||
323
src/poly.c
323
src/poly.c
@@ -1,323 +0,0 @@
|
||||
#include "poly.h"
|
||||
#include "zelda_rtl.h"
|
||||
#include "variables.h"
|
||||
|
||||
static const int8 kPolySinCos[320] = {
|
||||
0, 2, 3, 5, 6, 8, 9, 11, 12, 14, 16, 17, 19, 20, 22, 23,
|
||||
24, 26, 27, 29, 30, 32, 33, 34, 36, 37, 38, 39, 41, 42, 43, 44,
|
||||
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,
|
||||
64, 64, 64, 64, 64, 64, 63, 63, 63, 62, 62, 62, 61, 61, 60, 60,
|
||||
59, 59, 58, 57, 56, 56, 55, 54, 53, 52, 51, 50, 49, 48, 47, 46,
|
||||
45, 44, 43, 42, 41, 39, 38, 37, 36, 34, 33, 32, 30, 29, 27, 26,
|
||||
24, 23, 22, 20, 19, 17, 16, 14, 12, 11, 9, 8, 6, 5, 3, 2,
|
||||
0, -2, -3, -5, -6, -8, -9, -11, -12, -14, -16, -17, -19, -20, -22, -23,
|
||||
-24, -26, -27, -29, -30, -32, -33, -34, -36, -37, -38, -39, -41, -42, -43, -44,
|
||||
-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,
|
||||
-64, -64, -64, -64, -64, -64, -63, -63, -63, -62, -62, -62, -61, -61, -60, -60,
|
||||
-59, -59, -58, -57, -56, -56, -55, -54, -53, -52, -51, -50, -49, -48, -47, -46,
|
||||
-45, -44, -43, -42, -41, -39, -38, -37, -36, -34, -33, -32, -30, -29, -27, -26,
|
||||
-24, -23, -22, -20, -19, -17, -16, -14, -12, -11, -9, -8, -6, -5, -3, -2,
|
||||
0, 2, 3, 5, 6, 8, 9, 11, 12, 14, 16, 17, 19, 20, 22, 23,
|
||||
24, 26, 27, 29, 30, 32, 33, 34, 36, 37, 38, 39, 41, 42, 43, 44,
|
||||
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,
|
||||
};
|
||||
typedef struct Vertex3 {
|
||||
int8 x, y, z;
|
||||
} Vertex3;
|
||||
static const Vertex3 kPoly0_Vtx[6] = {
|
||||
{ 0, 65, 0},
|
||||
{ 0, -65, 0},
|
||||
{ 0, 0, -40},
|
||||
{-40, 0, 0},
|
||||
{ 0, 0, 40},
|
||||
{ 40, 0, 0},
|
||||
};
|
||||
static const uint8 kPoly0_Polys[40] = {
|
||||
3, 0, 5, 2, 4,
|
||||
3, 0, 2, 3, 1,
|
||||
3, 0, 3, 4, 2,
|
||||
3, 0, 4, 5, 3,
|
||||
3, 1, 2, 5, 4,
|
||||
3, 1, 3, 2, 1,
|
||||
3, 1, 4, 3, 2,
|
||||
3, 1, 5, 4, 3,
|
||||
};
|
||||
static const Vertex3 kPoly1_Vtx[6] = {
|
||||
{ 0, 40, 10},
|
||||
{ 40, -40, 10},
|
||||
{-40, -40, 10},
|
||||
{ 0, 40, -10},
|
||||
{-40, -40, -10},
|
||||
{ 40, -40, -10},
|
||||
};
|
||||
static const uint8 kPoly1_Polys[28] = {
|
||||
3, 0, 1, 2, 7,
|
||||
3, 3, 4, 5, 6,
|
||||
4, 0, 3, 5, 1, 5,
|
||||
4, 1, 5, 4, 2, 4,
|
||||
4, 3, 0, 2, 4, 3,
|
||||
};
|
||||
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},
|
||||
};
|
||||
static const uint32 kPoly_RasterColors[16] = {
|
||||
0x00, 0xff, 0xff00, 0xffff,
|
||||
0xff0000, 0xff00ff, 0xffff00, 0xffffff,
|
||||
0xff000000, 0xff0000ff, 0xff00ff00, 0xff00ffff,
|
||||
0xffff0000, 0xffff00ff, 0xffffff00, 0xffffffff,
|
||||
};
|
||||
static const uint16 kPoly_LeftSideMask[8] = {0xffff, 0x7f7f, 0x3f3f, 0x1f1f, 0xf0f, 0x707, 0x303, 0x101};
|
||||
static const uint16 kPoly_RightSideMask[8] = {0x8080, 0xc0c0, 0xe0e0, 0xf0f0, 0xf8f8, 0xfcfc, 0xfefe, 0xffff};
|
||||
uint16 Poly_Divide(uint16 a, uint16 b) {
|
||||
poly_tmp1 = sign16(a) ? -a : a;
|
||||
poly_tmp0 = b;
|
||||
while (poly_tmp0 >= 256)
|
||||
poly_tmp0 >>= 1, poly_tmp1 >>= 1;
|
||||
int q = poly_tmp1 / poly_tmp0;
|
||||
return sign16(a) ? -q : q;
|
||||
}
|
||||
|
||||
void Poly_RunFrame() {
|
||||
Polyhedral_EmptyBitMapBuffer();
|
||||
Polyhedral_SetShapePointer();
|
||||
Polyhedral_SetRotationMatrix();
|
||||
Polyhedral_OperateRotation();
|
||||
Polyhedral_DrawPolyhedron();
|
||||
}
|
||||
|
||||
void Polyhedral_SetShapePointer() { // 89f83d
|
||||
poly_var1 = poly_config1 * 2 + 0x80;
|
||||
poly_tmp0 = poly_which_model * 2;
|
||||
|
||||
const PolyConfig *poly_config = &kPolyConfigs[poly_which_model];
|
||||
poly_config_num_vertex = poly_config->num_vtx;
|
||||
poly_config_num_polys = poly_config->num_poly;
|
||||
poly_fromlut_ptr2 = poly_config->vtx_val;
|
||||
poly_fromlut_ptr4 = poly_config->polys_val;
|
||||
}
|
||||
|
||||
void Polyhedral_SetRotationMatrix() { // 89f864
|
||||
poly_sin_a = kPolySinCos[poly_a];
|
||||
poly_cos_a = kPolySinCos[poly_a + 64];
|
||||
poly_sin_b = kPolySinCos[poly_b];
|
||||
poly_cos_b = kPolySinCos[poly_b + 64];
|
||||
poly_e0 = (int16)poly_sin_b * (int8)poly_sin_a >> 8 << 2;
|
||||
poly_e1 = (int16)poly_cos_b * (int8)poly_cos_a >> 8 << 2;
|
||||
poly_e2 = (int16)poly_cos_b * (int8)poly_sin_a >> 8 << 2;
|
||||
poly_e3 = (int16)poly_sin_b * (int8)poly_cos_a >> 8 << 2;
|
||||
}
|
||||
|
||||
void Polyhedral_OperateRotation() { // 89f8fb
|
||||
const PolyConfig *poly_config = &kPolyConfigs[poly_which_model];
|
||||
const int8 *src = &poly_config->vertex[0].x;
|
||||
int i = poly_config_num_vertex;
|
||||
src += i * 3;
|
||||
do {
|
||||
src -= 3, i -= 1;
|
||||
poly_fromlut_x = src[2];
|
||||
poly_fromlut_y = src[1];
|
||||
poly_fromlut_z = src[0];
|
||||
Polyhedral_RotatePoint();
|
||||
Polyhedral_ProjectPoint();
|
||||
poly_arr_x[i] = poly_base_x + poly_f0;
|
||||
poly_arr_y[i] = poly_base_y - poly_f1;
|
||||
} while (i);
|
||||
}
|
||||
|
||||
void Polyhedral_RotatePoint() { // 89f931
|
||||
int x = (int8)poly_fromlut_x;
|
||||
int y = (int8)poly_fromlut_y;
|
||||
int z = (int8)poly_fromlut_z;
|
||||
|
||||
poly_f0 = (int16)poly_cos_b * z - (int16)poly_sin_b * x;
|
||||
poly_f1 = (int16)poly_e0 * z + (int16)poly_cos_a * y + (int16)poly_e2 * x;
|
||||
poly_f2 = ((int16)poly_e3 * z >> 8) - ((int16)poly_sin_a * y >> 8) + ((int16)poly_e1 * x >> 8) + poly_var1;
|
||||
}
|
||||
|
||||
void Polyhedral_ProjectPoint() { // 89f9d6
|
||||
poly_f0 = Poly_Divide(poly_f0, poly_f2);
|
||||
poly_f1 = Poly_Divide(poly_f1, poly_f2);
|
||||
}
|
||||
|
||||
void Polyhedral_DrawPolyhedron() { // 89fa4f
|
||||
const PolyConfig *poly_config = &kPolyConfigs[poly_which_model];
|
||||
const uint8 *src = poly_config->poly;
|
||||
do {
|
||||
poly_num_vertex_in_poly = *src++;
|
||||
BYTE(poly_tmp0) = poly_num_vertex_in_poly;
|
||||
poly_xy_coords[0] = poly_num_vertex_in_poly * 2;
|
||||
|
||||
int i = 1;
|
||||
do {
|
||||
int j = *src++;
|
||||
poly_xy_coords[i + 0] = poly_arr_x[j];
|
||||
poly_xy_coords[i + 1] = poly_arr_y[j];
|
||||
i += 2;
|
||||
} while (--BYTE(poly_tmp0));
|
||||
|
||||
poly_raster_color_config = *src++;
|
||||
int order = Polyhedral_CalculateCrossProduct();
|
||||
if (order > 0) {
|
||||
Polyhedral_SetForegroundColor();
|
||||
Polyhedral_DrawFace();
|
||||
}
|
||||
} while (--poly_config_num_polys);
|
||||
}
|
||||
|
||||
void Polyhedral_SetForegroundColor() { // 89faca
|
||||
uint8 t = poly_which_model ? (poly_config1 >> 5) : 0;
|
||||
uint8 a = (poly_tmp0 << (t + 1)) >> 8;
|
||||
Polyhedral_SetColorMask(a <= 1 ? 1 : a >= 7 ? 7 : a);
|
||||
}
|
||||
|
||||
int16 Polyhedral_CalculateCrossProduct() { // 89fb24
|
||||
int16 a = poly_xy_coords[3] - poly_xy_coords[1];
|
||||
poly_tmp0 = a * (int8)(poly_xy_coords[6] - poly_xy_coords[4]);
|
||||
a = poly_xy_coords[5] - poly_xy_coords[3];
|
||||
poly_tmp0 -= a * (int8)(poly_xy_coords[4] - poly_xy_coords[2]);
|
||||
return poly_tmp0;
|
||||
}
|
||||
|
||||
void Polyhedral_SetColorMask(int c) { // 89fcae
|
||||
uint32 v = kPoly_RasterColors[c];
|
||||
poly_raster_color0 = v;
|
||||
poly_raster_color1 = v >> 16;
|
||||
}
|
||||
|
||||
void Polyhedral_EmptyBitMapBuffer() { // 89fd04
|
||||
memset(polyhedral_buffer, 0, 0x800);
|
||||
}
|
||||
|
||||
void Polyhedral_DrawFace() { // 89fd1e
|
||||
int n = poly_xy_coords[0];
|
||||
uint8 min_y = poly_xy_coords[n];
|
||||
int min_idx = n;
|
||||
while (n -= 2) {
|
||||
if (poly_xy_coords[n] < min_y)
|
||||
min_y = poly_xy_coords[n], min_idx = n;
|
||||
}
|
||||
poly_raster_dst_ptr = 0xe800 + (((min_y & 0x38) ^ (min_y & 0x20 ? 0x24 : 0)) << 6) + (min_y & 7) * 2;
|
||||
poly_cur_vertex_idx0 = poly_cur_vertex_idx1 = min_idx;
|
||||
poly_total_num_steps = poly_xy_coords[0] >> 1;
|
||||
poly_y0_cur = poly_y1_cur = poly_xy_coords[min_idx];
|
||||
poly_x0_cur = poly_x1_cur = poly_xy_coords[min_idx - 1];
|
||||
if (Polyhedral_SetLeft() || Polyhedral_SetRight())
|
||||
return;
|
||||
for (;;) {
|
||||
Polyhedral_FillLine();
|
||||
if (BYTE(poly_raster_dst_ptr) != 0xe) {
|
||||
poly_raster_dst_ptr += 2;
|
||||
} else {
|
||||
uint8 a = HIBYTE(poly_raster_dst_ptr) + 2;
|
||||
poly_raster_dst_ptr = (a ^ ((a & 8) ? 0 : 0x19)) << 8;
|
||||
}
|
||||
if (poly_y0_cur == poly_y0_trig) {
|
||||
poly_x0_cur = poly_x0_target;
|
||||
if (Polyhedral_SetLeft())
|
||||
return;
|
||||
}
|
||||
poly_y0_cur++;
|
||||
if (poly_y1_cur == poly_y1_trig) {
|
||||
poly_x1_cur = poly_x1_target;
|
||||
if (Polyhedral_SetRight())
|
||||
return;
|
||||
}
|
||||
poly_y1_cur++;
|
||||
poly_x0_frac += poly_x0_step;
|
||||
poly_x1_frac += poly_x1_step;
|
||||
}
|
||||
}
|
||||
|
||||
void Polyhedral_FillLine() { // 89fdcf
|
||||
uint16 left = kPoly_LeftSideMask[(poly_x0_frac >> 8) & 7];
|
||||
uint16 right = kPoly_RightSideMask[(poly_x1_frac >> 8) & 7];
|
||||
poly_tmp2 = (poly_x0_frac >> 8) & 0x38;
|
||||
int d0 = ((poly_x1_frac >> 8) & 0x38);
|
||||
uint16 *ptr = (uint16*)&g_ram[poly_raster_dst_ptr + d0 * 4];
|
||||
if ((d0 -= poly_tmp2) == 0) {
|
||||
poly_tmp1 = left & right;
|
||||
ptr[0] ^= (ptr[0] ^ poly_raster_color0) & poly_tmp1;
|
||||
ptr[8] ^= (ptr[8] ^ poly_raster_color1) & poly_tmp1;
|
||||
return;
|
||||
}
|
||||
if (d0 < 0)
|
||||
return;
|
||||
int n = d0 >> 3;
|
||||
ptr[0] ^= (ptr[0] ^ poly_raster_color0) & right;
|
||||
ptr[8] ^= (ptr[8] ^ poly_raster_color1) & right;
|
||||
ptr -= 0x10;
|
||||
while (--n) {
|
||||
ptr[0] = poly_raster_color0;
|
||||
ptr[8] = poly_raster_color1;
|
||||
ptr -= 0x10;
|
||||
}
|
||||
ptr[0] ^= (ptr[0] ^ poly_raster_color0) & left;
|
||||
ptr[8] ^= (ptr[8] ^ poly_raster_color1) & left;
|
||||
poly_tmp1 = left, poly_raster_numfull = 0;
|
||||
}
|
||||
|
||||
bool Polyhedral_SetLeft() { // 89feb4
|
||||
int i;
|
||||
for (;;) {
|
||||
if (sign8(--poly_total_num_steps))
|
||||
return true;
|
||||
i = poly_cur_vertex_idx0 - 2;
|
||||
if (i == 0)
|
||||
i = poly_xy_coords[0];
|
||||
if (poly_xy_coords[i] < poly_y0_cur)
|
||||
return true;
|
||||
if (poly_xy_coords[i] != poly_y0_cur)
|
||||
break;
|
||||
poly_x0_cur = poly_xy_coords[i - 1];
|
||||
poly_cur_vertex_idx0 = i;
|
||||
}
|
||||
poly_y0_trig = poly_xy_coords[i];
|
||||
poly_x0_target = poly_xy_coords[i - 1];
|
||||
poly_cur_vertex_idx0 = i;
|
||||
int t = poly_x0_target - poly_x0_cur, u = t;
|
||||
if (t < 0)
|
||||
t = -t;
|
||||
t = ((t & 0xff) << 8) / (uint8)(poly_y0_trig - poly_y0_cur);
|
||||
poly_x0_frac = (poly_x0_cur << 8) | 0x80;
|
||||
poly_x0_step = (u < 0) ? -t : t;
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Polyhedral_SetRight() { // 89ff1e
|
||||
int i;
|
||||
for (;;) {
|
||||
if (sign8(--poly_total_num_steps))
|
||||
return true;
|
||||
i = poly_cur_vertex_idx1;
|
||||
if (i == poly_xy_coords[0])
|
||||
i = 0;
|
||||
i += 2;
|
||||
if (poly_xy_coords[i] < poly_y1_cur)
|
||||
return true;
|
||||
if (poly_xy_coords[i] != poly_y1_cur)
|
||||
break;
|
||||
poly_x1_cur = poly_xy_coords[i - 1];
|
||||
poly_cur_vertex_idx1 = i;
|
||||
}
|
||||
poly_y1_trig = poly_xy_coords[i];
|
||||
poly_x1_target = poly_xy_coords[i - 1];
|
||||
poly_cur_vertex_idx1 = i;
|
||||
int t = poly_x1_target - poly_x1_cur, u = t;
|
||||
if (t < 0)
|
||||
t = -t;
|
||||
t = ((t & 0xff) << 8) / (uint8)(poly_y1_trig - poly_y1_cur);
|
||||
poly_x1_frac = (poly_x1_cur << 8) | 0x80;
|
||||
poly_x1_step = (u < 0) ? -t : t;
|
||||
return false;
|
||||
}
|
||||
|
||||
19
src/poly.h
19
src/poly.h
@@ -1,19 +0,0 @@
|
||||
#pragma once
|
||||
#include "types.h"
|
||||
|
||||
uint16 Poly_Divide(uint16 a, uint16 b);
|
||||
void Poly_RunFrame();
|
||||
void Polyhedral_SetShapePointer();
|
||||
void Polyhedral_SetRotationMatrix();
|
||||
void Polyhedral_OperateRotation();
|
||||
void Polyhedral_RotatePoint();
|
||||
void Polyhedral_ProjectPoint();
|
||||
void Polyhedral_DrawPolyhedron();
|
||||
void Polyhedral_SetForegroundColor();
|
||||
int16 Polyhedral_CalculateCrossProduct();
|
||||
void Polyhedral_SetColorMask(int c);
|
||||
void Polyhedral_EmptyBitMapBuffer();
|
||||
void Polyhedral_DrawFace();
|
||||
void Polyhedral_FillLine();
|
||||
bool Polyhedral_SetLeft();
|
||||
bool Polyhedral_SetRight();
|
||||
195
src/util.c
195
src/util.c
@@ -1,195 +0,0 @@
|
||||
#include "util.h"
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdarg.h>
|
||||
|
||||
char *NextDelim(char **s, int sep) {
|
||||
char *r = *s;
|
||||
if (r) {
|
||||
while (r[0] == ' ' || r[0] == '\t')
|
||||
r++;
|
||||
char *t = strchr(r, sep);
|
||||
*s = t ? (*t++ = 0, t) : NULL;
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
static inline int ToLower(int a) {
|
||||
return a + (a >= 'A' && a <= 'Z') * 32;
|
||||
}
|
||||
|
||||
bool StringEqualsNoCase(const char *a, const char *b) {
|
||||
for (;;) {
|
||||
int aa = ToLower(*a++), bb = ToLower(*b++);
|
||||
if (aa != bb)
|
||||
return false;
|
||||
if (aa == 0)
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
const char *StringStartsWithNoCase(const char *a, const char *b) {
|
||||
for (;; a++, b++) {
|
||||
int aa = ToLower(*a), bb = ToLower(*b);
|
||||
if (bb == 0)
|
||||
return a;
|
||||
if (aa != bb)
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
uint8 *ReadWholeFile(const char *name, size_t *length) {
|
||||
FILE *f = fopen(name, "rb");
|
||||
if (f == NULL)
|
||||
return NULL;
|
||||
fseek(f, 0, SEEK_END);
|
||||
size_t size = ftell(f);
|
||||
rewind(f);
|
||||
uint8 *buffer = (uint8 *)malloc(size + 1);
|
||||
if (!buffer) Die("malloc failed");
|
||||
// Always zero terminate so this function can be used also for strings.
|
||||
buffer[size] = 0;
|
||||
if (fread(buffer, 1, size, f) != size)
|
||||
Die("fread failed");
|
||||
fclose(f);
|
||||
if (length) *length = size;
|
||||
return buffer;
|
||||
}
|
||||
|
||||
char *NextLineStripComments(char **s) {
|
||||
char *p = *s;
|
||||
if (p == NULL)
|
||||
return NULL;
|
||||
// find end of line
|
||||
char *eol = strchr(p, '\n');
|
||||
*s = eol ? eol + 1 : NULL;
|
||||
eol = eol ? eol : p + strlen(p);
|
||||
// strip comments
|
||||
char *comment = memchr(p, '#', eol - p);
|
||||
eol = comment ? comment : eol;
|
||||
// strip trailing whitespace
|
||||
while (eol > p && (eol[-1] == '\r' || eol[-1] == ' ' || eol[-1] == '\t'))
|
||||
eol--;
|
||||
*eol = 0;
|
||||
// strip leading whitespace
|
||||
while (p[0] == ' ' || p[0] == '\t')
|
||||
p++;
|
||||
return p;
|
||||
}
|
||||
|
||||
// Return the next possibly quoted string, space separated, or the empty string
|
||||
char *NextPossiblyQuotedString(char **s) {
|
||||
char *r = *s, *t;
|
||||
while (*r == ' ' || *r == '\t')
|
||||
r++;
|
||||
if (*r == '"') {
|
||||
for (t = ++r; *t && *t != '"'; t++);
|
||||
} else {
|
||||
for (t = r; *t && *t != ' ' && *t != '\t'; t++);
|
||||
}
|
||||
if (*t) *t++ = 0;
|
||||
while (*t == ' ' || *t == '\t')
|
||||
t++;
|
||||
*s = t;
|
||||
return r;
|
||||
}
|
||||
|
||||
char *ReplaceFilenameWithNewPath(const char *old_path, const char *new_path) {
|
||||
size_t olen = strlen(old_path);
|
||||
size_t nlen = strlen(new_path) + 1;
|
||||
while (olen && old_path[olen - 1] != '/' && old_path[olen - 1] != '\\')
|
||||
olen--;
|
||||
char *result = malloc(olen + nlen);
|
||||
memcpy(result, old_path, olen);
|
||||
memcpy(result + olen, new_path, nlen);
|
||||
return result;
|
||||
}
|
||||
|
||||
char *SplitKeyValue(char *p) {
|
||||
char *equals = strchr(p, '=');
|
||||
if (equals == NULL)
|
||||
return NULL;
|
||||
char *kr = equals;
|
||||
while (kr > p && (kr[-1] == ' ' || kr[-1] == '\t'))
|
||||
kr--;
|
||||
*kr = 0;
|
||||
char *v = equals + 1;
|
||||
while (v[0] == ' ' || v[0] == '\t')
|
||||
v++;
|
||||
return v;
|
||||
}
|
||||
|
||||
const char *SkipPrefix(const char *big, const char *little) {
|
||||
for (; *little; big++, little++) {
|
||||
if (*little != *big)
|
||||
return NULL;
|
||||
}
|
||||
return big;
|
||||
}
|
||||
|
||||
void StrSet(char **rv, const char *s) {
|
||||
char *news = strdup(s);
|
||||
char *old = *rv;
|
||||
*rv = news;
|
||||
free(old);
|
||||
}
|
||||
|
||||
char *StrFmt(const char *fmt, ...) {
|
||||
char buf[4096];
|
||||
va_list va;
|
||||
va_start(va, fmt);
|
||||
int n = vsnprintf(buf, sizeof(buf), fmt, va);
|
||||
if (n < 0 || n >= sizeof(buf)) Die("vsnprintf failed");
|
||||
va_end(va);
|
||||
return strdup(buf);
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
void ByteArray_Destroy(ByteArray *arr) {
|
||||
void *data = arr->data;
|
||||
arr->data = NULL;
|
||||
free(data);
|
||||
}
|
||||
|
||||
void ByteArray_AppendData(ByteArray *arr, const uint8 *data, size_t data_size) {
|
||||
ByteArray_Resize(arr, arr->size + data_size);
|
||||
memcpy(arr->data + arr->size - data_size, data, data_size);
|
||||
}
|
||||
|
||||
void ByteArray_AppendByte(ByteArray *arr, uint8 v) {
|
||||
ByteArray_Resize(arr, arr->size + 1);
|
||||
arr->data[arr->size - 1] = v;
|
||||
}
|
||||
|
||||
// Automatically selects between 16 or 32 bit indexes. Can hold up to 8192 elements in 16-bit mode.
|
||||
MemBlk FindIndexInMemblk(MemBlk data, size_t i) {
|
||||
if (data.size < 2)
|
||||
return (MemBlk) { 0, 0 };
|
||||
size_t end = data.size - 2, left_off, right_off;
|
||||
size_t mx = *(uint16 *)(data.ptr + end);
|
||||
if (mx < 8192) {
|
||||
if (i > mx || mx * 2 > end)
|
||||
return (MemBlk) { 0, 0 };
|
||||
left_off = ((i == 0) ? mx * 2 : mx * 2 + *(uint16 *)(data.ptr + i * 2 - 2));
|
||||
right_off = (i == mx) ? end : mx * 2 + *(uint16 *)(data.ptr + i * 2);
|
||||
} else {
|
||||
mx -= 8192;
|
||||
if (i > mx || mx * 4 > end)
|
||||
return (MemBlk) { 0, 0 };
|
||||
left_off = ((i == 0) ? mx * 4 : mx * 4 + *(uint32 *)(data.ptr + i * 4 - 4));
|
||||
right_off = (i == mx) ? end : mx * 4 + *(uint32 *)(data.ptr + i * 4);
|
||||
}
|
||||
if (left_off > right_off || right_off > end)
|
||||
return (MemBlk) { 0, 0 };
|
||||
return (MemBlk) { data.ptr + left_off, right_off - left_off };
|
||||
}
|
||||
39
src/util.h
39
src/util.h
@@ -1,39 +0,0 @@
|
||||
#ifndef ZELDA3_UTIL_H_
|
||||
#define ZELDA3_UTIL_H_
|
||||
|
||||
#include "types.h"
|
||||
|
||||
typedef struct SDL_Window SDL_Window;
|
||||
|
||||
struct RendererFuncs {
|
||||
bool (*Initialize)(SDL_Window *window);
|
||||
void (*Destroy)();
|
||||
void (*BeginDraw)(int width, int height, uint8 **pixels, int *pitch);
|
||||
void (*EndDraw)();
|
||||
};
|
||||
|
||||
|
||||
typedef struct ByteArray {
|
||||
uint8 *data;
|
||||
size_t size, capacity;
|
||||
} ByteArray;
|
||||
|
||||
void ByteArray_Resize(ByteArray *arr, size_t new_size);
|
||||
void ByteArray_Destroy(ByteArray *arr);
|
||||
void ByteArray_AppendData(ByteArray *arr, const uint8 *data, size_t data_size);
|
||||
void ByteArray_AppendByte(ByteArray *arr, uint8 v);
|
||||
|
||||
uint8 *ReadWholeFile(const char *name, size_t *length);
|
||||
char *NextDelim(char **s, int sep);
|
||||
char *NextLineStripComments(char **s);
|
||||
char *NextPossiblyQuotedString(char **s);
|
||||
char *SplitKeyValue(char *p);
|
||||
bool StringEqualsNoCase(const char *a, const char *b);
|
||||
const char *StringStartsWithNoCase(const char *a, const char *b);
|
||||
bool ParseBool(const char *value, bool *result);
|
||||
const char *SkipPrefix(const char *big, const char *little);
|
||||
void StrSet(char **rv, const char *s);
|
||||
char *StrFmt(const char *fmt, ...);
|
||||
char *ReplaceFilenameWithNewPath(const char *old_path, const char *new_path);
|
||||
|
||||
#endif // ZELDA3_UTIL_H_
|
||||
7
assets/.gitignore → tables/.gitignore
vendored
7
assets/.gitignore → tables/.gitignore
vendored
@@ -1,5 +1,5 @@
|
||||
zelda3.sfc
|
||||
dialogue*.txt
|
||||
dialogue.txt
|
||||
generated_*.h
|
||||
linksprite.png
|
||||
map32_to_map16.txt
|
||||
@@ -8,7 +8,4 @@ sfx.txt
|
||||
/sound
|
||||
sound_ending.txt
|
||||
sound_indoor.txt
|
||||
sound_intro.txt
|
||||
/hud_icons.png
|
||||
/font*.png
|
||||
|
||||
sound_intro.txt
|
||||
@@ -6,10 +6,10 @@ import yaml
|
||||
import tables
|
||||
import compile_music
|
||||
import array, hashlib, struct
|
||||
from util import cache
|
||||
import sprite_sheets
|
||||
import argparse
|
||||
import os
|
||||
|
||||
print_int_array = util.print_int_array
|
||||
|
||||
PATH = ''
|
||||
|
||||
def flatten(xss):
|
||||
return [x for xs in xss for x in xs]
|
||||
@@ -19,6 +19,7 @@ def invert_dict(xs):
|
||||
|
||||
assets = {}
|
||||
|
||||
|
||||
def add_asset_uint8(name, data):
|
||||
assert name not in assets
|
||||
assets[name] = ('uint8', bytes(array.array('B', data)))
|
||||
@@ -35,13 +36,9 @@ def add_asset_int16(name, data):
|
||||
assert name not in assets
|
||||
assets[name] = ('int16', bytes(array.array('h', data)))
|
||||
|
||||
def add_asset_packed(name, data):
|
||||
assert name not in assets
|
||||
assets[name] = ('packed', pack_arrays(data))
|
||||
|
||||
def print_map32_to_map16():
|
||||
tab = {}
|
||||
for line in open('map32_to_map16.txt'):
|
||||
for line in open(PATH + 'map32_to_map16.txt'):
|
||||
line = line.strip()
|
||||
x, xs = line.split(':', 1)
|
||||
tab[int(x)] = [int(t) for t in xs.split(',')]
|
||||
@@ -66,12 +63,22 @@ def print_map32_to_map16():
|
||||
add_asset_uint8('kMap32ToMap16_2', res[2])
|
||||
add_asset_uint8('kMap32ToMap16_3', res[3])
|
||||
|
||||
def compress_dialogue(fname, lang):
|
||||
lines = []
|
||||
for line in open(fname, encoding='utf8').read().splitlines():
|
||||
|
||||
def print_dialogue():
|
||||
new_r = []
|
||||
offs = []
|
||||
for line in open(PATH + 'dialogue.txt'):
|
||||
line = line.strip('\n')
|
||||
a, b = line.split(': ', 1)
|
||||
lines.append(b)
|
||||
return text_compression.compress_strings(lines, lang)
|
||||
index = int(a)
|
||||
offs.append(len(new_r))
|
||||
r = text_compression.compress_string(b)
|
||||
new_r.extend(r)
|
||||
|
||||
add_asset_uint16('kDialogueOffs', offs)
|
||||
add_asset_uint8('kDialogueText', new_r)
|
||||
|
||||
ROM = util.LoadedRom(sys.argv[1] if len(sys.argv) >= 2 else None)
|
||||
|
||||
def compress_store(r):
|
||||
rr = []
|
||||
@@ -85,73 +92,40 @@ def compress_store(r):
|
||||
rr.append(0xff)
|
||||
return rr
|
||||
|
||||
# Pack arrays and determine automatically the index size
|
||||
def pack_arrays(arr):
|
||||
if len(arr) == 0:
|
||||
return b''
|
||||
all_offs, offs = [], 0
|
||||
for i in range(len(arr) - 1):
|
||||
offs += len(arr[i])
|
||||
def pack_u32_arrays(arr):
|
||||
all_offs, offs = [], len(arr) * 4
|
||||
for i in range(len(arr)):
|
||||
all_offs.append(offs)
|
||||
if offs < 65536 and len(arr) <= 8192:
|
||||
return b''.join([struct.pack('H', i) for i in all_offs] + arr + [struct.pack('H', len(arr) - 1)])
|
||||
else:
|
||||
return b''.join([struct.pack('I', i) for i in all_offs] + arr + [struct.pack('H', 8192 + len(arr) - 1)])
|
||||
|
||||
def print_images(args):
|
||||
sprsheet = sprite_sheets.load_sprite_sheets() if args.sprites_from_png else None
|
||||
offs += len(arr[i])
|
||||
return b''.join([struct.pack('I', i) for i in all_offs] + arr)
|
||||
|
||||
def print_images():
|
||||
lengths = b''
|
||||
all = []
|
||||
for i in range(108):
|
||||
if sprsheet != None and i < 103:
|
||||
all.append(sprsheet.encode_sheet_in_snes_format(i))
|
||||
elif i < 12:
|
||||
all.append(bytes(ROM.get_bytes(tables.kCompSpritePtrs[i], 0x600)))
|
||||
else:
|
||||
decomp, comp_len = util.decomp(tables.kCompSpritePtrs[i], ROM.get_byte, False, True)
|
||||
all.append(bytes(ROM.get_bytes(tables.kCompSpritePtrs[i], comp_len)))
|
||||
add_asset_packed('kSprGfx', all)
|
||||
for i in range(12):
|
||||
all.append(bytes(ROM.get_bytes(tables.kCompSpritePtrs[i], 0x600)))
|
||||
for i in range(12, 108):
|
||||
decomp, comp_len = util.decomp(tables.kCompSpritePtrs[i], ROM.get_byte, False, True)
|
||||
all.append(bytes(ROM.get_bytes(tables.kCompSpritePtrs[i], comp_len)))
|
||||
add_asset_uint8('kSprGfx', pack_u32_arrays(all))
|
||||
|
||||
all = []
|
||||
for i in range(len(tables.kCompBgPtrs)):
|
||||
decomp, comp_len = util.decomp(tables.kCompBgPtrs[i], ROM.get_byte, False, True)
|
||||
all.append(bytes(ROM.get_bytes(tables.kCompBgPtrs[i], comp_len)))
|
||||
add_asset_packed('kBgGfx', all)
|
||||
add_asset_uint8('kBgGfx', pack_u32_arrays(all))
|
||||
|
||||
def print_dialogue(args):
|
||||
from text_compression import dialogue_filename, kLanguages
|
||||
|
||||
languages = ['us']
|
||||
if args.languages:
|
||||
for a in args.languages.split(','):
|
||||
if a in languages or a not in kLanguages:
|
||||
raise Exception(f'Language {a} is not valid')
|
||||
name = dialogue_filename(a)
|
||||
if not os.path.exists(name):
|
||||
raise Exception(f'{name} not found. You need to extract it with --extract-dialogue using the ROM of that language.')
|
||||
languages.append(a)
|
||||
|
||||
all_langs, all_fonts, mappings = [], [], []
|
||||
for i, lang in enumerate(languages):
|
||||
dict_packed = pack_arrays(text_compression.encode_dictionary(lang))
|
||||
dialogue_packed = pack_arrays(compress_dialogue(dialogue_filename(lang), lang))
|
||||
all_langs.append(pack_arrays([dict_packed, dialogue_packed]))
|
||||
font_data, font_width = sprite_sheets.encode_font_from_png(lang)
|
||||
all_fonts.append(pack_arrays([font_data, font_width]))
|
||||
flags = text_compression.uses_new_format(lang)
|
||||
if i != 0: flags |= 2 # no us rom match?
|
||||
mappings.append(pack_arrays([lang.encode('utf8'), bytearray([i, i, flags])]))
|
||||
add_asset_packed('kDialogue', all_langs)
|
||||
add_asset_packed('kDialogueFont', all_fonts)
|
||||
add_asset_packed('kDialogueMap', mappings)
|
||||
|
||||
def print_misc(args):
|
||||
def print_misc():
|
||||
add_asset_uint8('kOverworldMapGfx', ROM.get_bytes(0x18c000, 0x4000))
|
||||
add_asset_uint8('kLightOverworldTilemap', ROM.get_bytes(0xac727, 4096))
|
||||
add_asset_uint8('kDarkOverworldTilemap', ROM.get_bytes(0xaD727, 1024))
|
||||
|
||||
add_asset_uint16('kPredefinedTileData', ROM.get_words(0x9B52, 6438))
|
||||
|
||||
add_asset_uint16('kFontData', ROM.get_words(0xe8000, 2048))
|
||||
|
||||
add_asset_uint16('kMap16ToMap8', ROM.get_words(0x8f8000, 3752 * 4))
|
||||
|
||||
add_asset_uint8('kGeneratedWishPondItem', ROM.get_bytes(0x888450, 256))
|
||||
@@ -167,7 +141,7 @@ def print_misc(args):
|
||||
add_asset_uint16('kPalette_DungBgMain', ROM.get_words(0x9BD734, 1800))
|
||||
add_asset_uint16('kPalette_MainSpr', ROM.get_words(0x9BD218, 120))
|
||||
|
||||
add_asset_uint16('kPalette_ArmorAndGloves', sprite_sheets.override_armor_palette or ROM.get_words(0x9BD308, 75))
|
||||
add_asset_uint16('kPalette_ArmorAndGloves', ROM.get_words(0x9BD308, 75))
|
||||
add_asset_uint16('kPalette_Sword', ROM.get_words(0x9BD630, 12))
|
||||
add_asset_uint16('kPalette_Shield', ROM.get_words(0x9BD648, 12))
|
||||
|
||||
@@ -184,24 +158,27 @@ def print_misc(args):
|
||||
|
||||
add_asset_uint16('kOverworldMapPaletteData', ROM.get_words(0x8ADB27, 256))
|
||||
|
||||
@cache
|
||||
g_overworld_yaml_cache = {}
|
||||
def load_overworld_yaml(room):
|
||||
return yaml.safe_load(open('overworld/overworld-%d.yaml' % room, 'r'))
|
||||
if room not in g_overworld_yaml_cache:
|
||||
g_overworld_yaml_cache[room] = yaml.safe_load(open(PATH+'overworld/overworld-%d.yaml' % room, 'r'))
|
||||
return g_overworld_yaml_cache[room]
|
||||
|
||||
|
||||
def print_overworld():
|
||||
r = []
|
||||
for i in range(160):
|
||||
addr = ROM.get_24(0x82F94D + i * 3)
|
||||
decomp, comp_len = util.decomp(addr, ROM.get_byte, True, True)
|
||||
r.append(bytes(ROM.get_bytes(addr, comp_len)))
|
||||
add_asset_packed('kOverworld_Hibytes_Comp', r)
|
||||
add_asset_uint8('kOverworld_Hibytes_Comp', pack_u32_arrays(r))
|
||||
|
||||
r = []
|
||||
for i in range(160):
|
||||
addr = ROM.get_24(0x82FB2D + i * 3)
|
||||
decomp, comp_len = util.decomp(addr, ROM.get_byte, True, True)
|
||||
r.append(bytes(ROM.get_bytes(addr, comp_len)))
|
||||
add_asset_packed('kOverworld_Lobytes_Comp', r)
|
||||
add_asset_uint8('kOverworld_Lobytes_Comp', pack_u32_arrays(r))
|
||||
|
||||
def is_area_head(i):
|
||||
return i >= 128 or ROM.get_byte(0x82A5EC + (i & 63)) == (i & 63)
|
||||
@@ -449,13 +426,15 @@ def print_dungeon_map():
|
||||
b = ROM.get_bytes(addr, nonzero_bytes)
|
||||
r2.append(bytes(b))
|
||||
|
||||
add_asset_packed('kDungMap_FloorLayout', r)
|
||||
add_asset_packed('kDungMap_Tiles', r2)
|
||||
add_asset_uint8('kDungMap_FloorLayout', pack_u32_arrays(r))
|
||||
add_asset_uint8('kDungMap_Tiles', pack_u32_arrays(r2))
|
||||
|
||||
|
||||
@cache
|
||||
g_dungeon_yaml_cache = {}
|
||||
def load_dungeon_yaml(room):
|
||||
return yaml.safe_load(open('dungeon/dungeon-%d.yaml' % room, 'r'))
|
||||
if room not in g_dungeon_yaml_cache:
|
||||
g_dungeon_yaml_cache[room] = yaml.safe_load(open(PATH+'dungeon/dungeon-%d.yaml' % room, 'r'))
|
||||
return g_dungeon_yaml_cache[room]
|
||||
|
||||
def print_dungeon_sprites():
|
||||
offsets=[0 for i in range(320)]
|
||||
@@ -686,7 +665,7 @@ def print_dungeon_rooms():
|
||||
|
||||
data = []
|
||||
offsets = [0] * 8
|
||||
default_yaml = yaml.safe_load(open('dungeon/default_rooms.yaml', 'r'))
|
||||
default_yaml = yaml.safe_load(open(PATH+'dungeon/default_rooms.yaml', 'r'))
|
||||
for i in range(len(offsets)):
|
||||
offsets[i] = len(data)
|
||||
print_layer(default_yaml['Default%d' % i], None)
|
||||
@@ -695,7 +674,7 @@ def print_dungeon_rooms():
|
||||
|
||||
data = []
|
||||
offsets = [0] * 19
|
||||
overlay_yaml = yaml.safe_load(open('dungeon/overlay_rooms.yaml', 'r'))
|
||||
overlay_yaml = yaml.safe_load(open(PATH+'dungeon/overlay_rooms.yaml', 'r'))
|
||||
for i in range(len(offsets)):
|
||||
offsets[i] = len(data)
|
||||
print_layer(overlay_yaml['Overlay%d' % i], None)
|
||||
@@ -710,6 +689,8 @@ def print_dungeon_rooms():
|
||||
add_asset_uint16('kMovableBlockDataInit', ROM.get_words(0x84f1de, 198))
|
||||
add_asset_uint16('kTorchDataInit', ROM.get_words(0x84F36A, 144))
|
||||
add_asset_uint16('kTorchDataJunk', ROM.get_words(0x84F48a, 48))
|
||||
|
||||
|
||||
|
||||
def print_enemy_damage_data():
|
||||
decomp, comp_len = util.decomp(0x83e800, ROM.get_byte, True, True)
|
||||
@@ -730,7 +711,7 @@ def print_tilemaps():
|
||||
add_asset_uint8('kBgTilemap_%d' % i, ROM.get_bytes(s, l))
|
||||
|
||||
def print_link_graphics():
|
||||
image = Image.open('linksprite.png')
|
||||
image = Image.open(PATH+'linksprite.png')
|
||||
data = image.tobytes()
|
||||
def encode_4bit_sprite(data, offset, pitch):
|
||||
b = [0] * 32
|
||||
@@ -753,21 +734,25 @@ def print_sound_banks():
|
||||
name, data = compile_music.print_song(song)
|
||||
add_asset_uint8(name, data)
|
||||
|
||||
def print_all(args):
|
||||
|
||||
def print_all():
|
||||
print_sound_banks()
|
||||
print_dungeon_rooms()
|
||||
print_enemy_damage_data()
|
||||
print_link_graphics()
|
||||
print_dungeon_sprites()
|
||||
print_map32_to_map16()
|
||||
print_images(args)
|
||||
print_misc(args)
|
||||
print_dialogue(args)
|
||||
print_dialogue()
|
||||
print_images()
|
||||
print_misc()
|
||||
print_dungeon_map()
|
||||
print_tilemaps()
|
||||
print_overworld()
|
||||
print_overworld_tables()
|
||||
|
||||
print_all()
|
||||
|
||||
|
||||
def write_assets_to_file(print_header = False):
|
||||
key_sig = b''
|
||||
all_data = []
|
||||
@@ -779,17 +764,12 @@ enum {
|
||||
kNumberOfAssets = %d
|
||||
};
|
||||
extern const uint8 *g_asset_ptrs[kNumberOfAssets];
|
||||
extern uint32 g_asset_sizes[kNumberOfAssets];
|
||||
extern MemBlk FindInAssetArray(int asset, int idx);
|
||||
''' % len(assets))
|
||||
extern uint32 g_asset_sizes[kNumberOfAssets];''' % len(assets))
|
||||
|
||||
for i, (k, (tp, data)) in enumerate(assets.items()):
|
||||
if print_header:
|
||||
if tp == 'packed':
|
||||
print('#define %s(idx) FindInAssetArray(%d, idx)' % (k, i))
|
||||
else:
|
||||
print('#define %s ((%s*)g_asset_ptrs[%d])' % (k, tp, i))
|
||||
print('#define %s_SIZE (g_asset_sizes[%d])' % (k, i))
|
||||
print('#define %s ((%s*)g_asset_ptrs[%d])' % (k, tp, i))
|
||||
print('#define %s_SIZE (g_asset_sizes[%d])' % (k, i))
|
||||
key_sig += k.encode('utf8') + b'\0'
|
||||
all_data.append(data)
|
||||
|
||||
@@ -809,19 +789,8 @@ extern MemBlk FindInAssetArray(int asset, int idx);
|
||||
file_data += b'\0'
|
||||
file_data += v
|
||||
|
||||
open('../zelda3_assets.dat', 'wb').write(file_data)
|
||||
open('zelda3_assets.dat', 'wb').write(file_data)
|
||||
|
||||
def main(args):
|
||||
print_all(args)
|
||||
write_assets_to_file(args.print_assets_header)
|
||||
write_assets_to_file(False)
|
||||
|
||||
if __name__ == "__main__":
|
||||
ROM = util.load_rom(sys.argv[1] if len(sys.argv) >= 2 else None)
|
||||
class DefaultArgs:
|
||||
sprites_from_png = False
|
||||
languages = None
|
||||
print_assets_header = False
|
||||
main(DefaultArgs())
|
||||
else:
|
||||
ROM = util.ROM
|
||||
|
||||
@@ -2,12 +2,39 @@ from ast import literal_eval as make_tuple
|
||||
import sys
|
||||
import text_compression
|
||||
import util
|
||||
from util import get_bytes, get_words, get_byte, get_word, get_int8, get_int16, cache
|
||||
from PIL import Image
|
||||
import tables
|
||||
import yaml
|
||||
import extract_music
|
||||
import os
|
||||
import sprite_sheets
|
||||
import array
|
||||
import argparse
|
||||
|
||||
PATH=''
|
||||
|
||||
parser = argparse.ArgumentParser(description='Extract resources.')
|
||||
parser.add_argument('rom', nargs='?', help='the rom file')
|
||||
parser.add_argument('--convert-x2-icons', action='store_true', help='convert X2 icons')
|
||||
parser.add_argument('--fix-palette', action='store_true', help='only fix palette')
|
||||
args = parser.parse_args()
|
||||
|
||||
|
||||
ROM = util.LoadedRom(args.rom)
|
||||
|
||||
get_byte = ROM.get_byte
|
||||
get_word = ROM.get_word
|
||||
get_bytes = ROM.get_bytes
|
||||
|
||||
def get_int8(ea):
|
||||
b = get_byte(ea)
|
||||
if b & 0x80: b -= 256
|
||||
return b
|
||||
|
||||
def get_int16(ea):
|
||||
b = get_word(ea)
|
||||
if b & 0x8000: b -= 65536
|
||||
return b
|
||||
|
||||
|
||||
def print_map32_to_map16(f):
|
||||
for i in range(2218):
|
||||
@@ -26,7 +53,6 @@ def print_map32_to_map16(f):
|
||||
for j in range(4):
|
||||
print('%5d: %4d, %4d, %4d, %4d' % (i * 4 + j, t0[j], t1[j], t2[j], t3[j]), file = f)
|
||||
|
||||
@cache
|
||||
def get_exit_datas():
|
||||
r = {}
|
||||
for i in range(79):
|
||||
@@ -82,6 +108,7 @@ def get_exit_datas():
|
||||
y['door'] = ['palace' if fdoor & 0x8000 else 'sanctuary', (fdoor & 0x7e) >> 1, (fdoor & 0x3f80) >> 7]
|
||||
r.setdefault(screen_index, []).append(y)
|
||||
return r
|
||||
EXIT_DATAS = get_exit_datas()
|
||||
|
||||
def get_loadoffs(c, d):
|
||||
x, y = c[0] >> 4, c[1] >> 4
|
||||
@@ -89,7 +116,6 @@ def get_loadoffs(c, d):
|
||||
y += d[1]
|
||||
return (y&0x3f) << 7 | (x&0x3f) << 1
|
||||
|
||||
@cache
|
||||
def get_ow_travel_infos():
|
||||
r = {}
|
||||
for i in range(17):
|
||||
@@ -121,8 +147,8 @@ def get_ow_travel_infos():
|
||||
y['unk'] = [unk1, unk3]
|
||||
r.setdefault(screen_index, []).append(y)
|
||||
return r
|
||||
OW_TRAVEL_INFOS = get_ow_travel_infos()
|
||||
|
||||
@cache
|
||||
def get_ow_entrance_info():
|
||||
r = {}
|
||||
for i in range(129):
|
||||
@@ -131,8 +157,8 @@ def get_ow_entrance_info():
|
||||
entrance_id = get_byte(0x9BBB73 + i)
|
||||
r.setdefault(area, []).append({'index' : i, 'x' : (pos >> 1) & 0x3f, 'y' : (pos >> 7) & 0x3f, 'entrance_id' : entrance_id})
|
||||
return r
|
||||
OW_ENTRANCE_INFO = get_ow_entrance_info()
|
||||
|
||||
@cache
|
||||
def get_hole_infos():
|
||||
r = {}
|
||||
for i in range(19):
|
||||
@@ -141,6 +167,7 @@ def get_hole_infos():
|
||||
entrance_id = get_byte(0x9BB84C + i)
|
||||
r.setdefault(area, []).append({'x' : (pos >> 1) & 0x3f, 'y' : (pos >> 7) & 0x3f, 'entrance_id' : entrance_id})
|
||||
return r
|
||||
HOLE_INFO = get_hole_infos()
|
||||
|
||||
def print_overworld_area(overworld_area):
|
||||
is_small = get_bytes(0x82F88D, 192)
|
||||
@@ -186,12 +213,11 @@ def print_overworld_area(overworld_area):
|
||||
}
|
||||
|
||||
y['Header'] = header
|
||||
y['Travel'] = get_ow_travel_infos().get(overworld_area, [])
|
||||
y['Entrances'] = get_ow_entrance_info().get(overworld_area, [])
|
||||
hole_infos = get_hole_infos()
|
||||
if overworld_area in hole_infos:
|
||||
y['Holes'] = hole_infos[overworld_area]
|
||||
y['Exits'] = get_exit_datas().get(overworld_area, [])
|
||||
y['Travel'] = OW_TRAVEL_INFOS.get(overworld_area, [])
|
||||
y['Entrances'] = OW_ENTRANCE_INFO.get(overworld_area, [])
|
||||
if overworld_area in HOLE_INFO:
|
||||
y['Holes'] = HOLE_INFO[overworld_area]
|
||||
y['Exits'] = EXIT_DATAS.get(overworld_area, [])
|
||||
y['Items'] = get_items()
|
||||
|
||||
def decode_sprites(base_addr):
|
||||
@@ -230,17 +256,130 @@ def print_overworld_area(overworld_area):
|
||||
}
|
||||
|
||||
s = yaml.dump(y, default_flow_style=None, sort_keys=False)
|
||||
open('overworld/overworld-%d.yaml' % overworld_area, 'w').write(s)
|
||||
open(PATH+'overworld/overworld-%d.yaml' % overworld_area, 'w').write(s)
|
||||
|
||||
def print_all_overworld_areas():
|
||||
area_heads = get_bytes(0x82A5EC, 64)
|
||||
area_heads = ROM.get_bytes(0x82A5EC, 64)
|
||||
|
||||
for i in range(160):
|
||||
if i >= 128 or area_heads[i&63] == (i&63):
|
||||
print_overworld_area(i)
|
||||
|
||||
def print_dialogue():
|
||||
text_compression.print_strings(util.ROM, file = open(text_compression.dialogue_filename(util.ROM.language), 'w', encoding='utf8'))
|
||||
text_compression.print_strings(open('dialogue.txt', 'w'), get_byte)
|
||||
|
||||
def decomp_one_spr_3bit(data, offs, target, toffs, pitch):
|
||||
for y in range(8):
|
||||
d0, d1, d2 = data[offs + y * 2], data[offs + y * 2 + 1], data[offs + y + 16]
|
||||
for x in range(8):
|
||||
t = ((d0 >> x) & 1) * 1 + ((d1 >> x) & 1) * 2 + ((d2 >> x) & 1) * 4
|
||||
target[toffs + y * pitch + (7 - x)] = t * 255 // 7
|
||||
|
||||
def decomp_one_spr_4bit(data, offs, target, toffs, pitch):
|
||||
for y in range(8):
|
||||
d0, d1, d2, d3 = data[offs + y * 2 + 0], data[offs + y * 2 + 1], data[offs + y * 2 + 16], data[offs + y * 2 + 17]
|
||||
for x in range(8):
|
||||
t = ((d0 >> x) & 1) * 1 + ((d1 >> x) & 1) * 2 + ((d2 >> x) & 1) * 4 + ((d3 >> x) & 1) * 8
|
||||
target[toffs + y * pitch + (7 - x)] = t
|
||||
|
||||
def save_array_as_image(dimensions, data, fname, palette = None):
|
||||
img = Image.new('L' if palette == None else 'P', dimensions)
|
||||
img.putdata(data)
|
||||
if palette != None:
|
||||
img.putpalette(palette)
|
||||
img.save(fname)
|
||||
|
||||
|
||||
def decomp_save(data, fname, func, step, height=32, palette=None):
|
||||
dst=[0]*128*height
|
||||
for i in range(16*height//8):
|
||||
x = i % 16
|
||||
y = i // 16
|
||||
func(data, i * step, dst, x * 8 + y * 8 * 128, 128)
|
||||
save_array_as_image((128, height), dst, fname, palette)
|
||||
|
||||
def convert_snes_palette(v):
|
||||
res=[]
|
||||
for i,x in enumerate(v):
|
||||
r, g, b = (x & 0x1f), (x >> 5 & 0x1f), (x >> 10 & 0x1f)
|
||||
if (i & 15) == 0:
|
||||
# transparent color
|
||||
res.extend((0, 0x80, 0x80))
|
||||
else:
|
||||
res.extend((r << 3 | r >> 2, g << 3 | g >> 2, b << 3 | b >> 2))
|
||||
return res
|
||||
|
||||
|
||||
def decode_link_sprites():
|
||||
kLinkPalette = [0, 0x7fff, 0x237e, 0x11b7, 0x369e, 0x14a5, 0x1ff, 0x1078, 0x599d, 0x3647, 0x3b68, 0xa4a, 0x12ef, 0x2a5c, 0x1571, 0x7a18]
|
||||
decomp_save(get_bytes(0x108000, 32768), PATH+'linksprite.png',
|
||||
decomp_one_spr_4bit, 32, 448, convert_snes_palette(kLinkPalette))
|
||||
|
||||
def decomp_one_spr_2bit(data, offs, target, toffs, pitch, palette_base):
|
||||
for y in range(8):
|
||||
d0, d1 = data[offs + y * 2], data[offs + y * 2 + 1]
|
||||
for x in range(8):
|
||||
t = ((d0 >> x) & 1) * 1 + ((d1 >> x) & 1) * 2
|
||||
target[toffs + y * pitch + (7 - x)] = t + palette_base
|
||||
|
||||
|
||||
gfx_desc = {
|
||||
0x00 : ('3bpp_np', 'Half slot images'),
|
||||
|
||||
0x01 : ('3bpp_np', 'Half slot images'),
|
||||
0x02 : ('3bpp_np', 'Half slot images'),
|
||||
0x03 : ('3bpp_np', 'Half slot images'),
|
||||
0x04 : ('3bpp_np', 'Half slot images'),
|
||||
0x05 : ('3bpp_np', 'Half slot images'),
|
||||
|
||||
0x06 : ('3bpp_np', 'common spr gfx'),
|
||||
0x07 : ('3bpp_np', 'common spr gfx'),
|
||||
|
||||
0x08 : ('3bpp_np', 'Half slot images'),
|
||||
0x09 : ('3bpp_np', 'Half slot images'),
|
||||
0x0a : ('3bpp_np', 'Half slot images'),
|
||||
0x0b : ('3bpp_np', 'Half slot images'),
|
||||
|
||||
88 : ('3bpp', 'Tagalong gfx'),
|
||||
89 : ('3bpp', 'Tagalong gfx'),
|
||||
|
||||
90 : ('3bpp', 'animated sprites'),
|
||||
91 : ('3bpp', 'animated sprites'),
|
||||
92 : ('3bpp', 'animated sprites'),
|
||||
93 : ('3bpp', 'animated sprites'),
|
||||
94 : ('3bpp', 'sword/shield gfx'),
|
||||
95 : ('3bpp', 'sword/shield gfx'),
|
||||
|
||||
|
||||
100 : ('3bpp', 'Tagalong gfx'),
|
||||
101 : ('3bpp', 'Tagalong gfx'),
|
||||
102 : ('3bpp', 'Tagalong gfx'),
|
||||
|
||||
103 : ('2bpp', 'Attract images loaded to 7800'),
|
||||
104 : ('2bpp', 'empty'),
|
||||
105 : ('2bpp', 'hud icons loaded to 7800'),
|
||||
106 : ('2bpp', 'hud icons loaded to 7000'),
|
||||
107 : ('2bpp', 'hud icons loaded to 7400'),
|
||||
}
|
||||
|
||||
def decomp_generic(k, mode, fname_add = "misc"):
|
||||
fname = 'img/%.3d - %s.png' % (k, fname_add)
|
||||
if mode == '2bpp':
|
||||
r = util.decomp(tables.kCompSpritePtrs[k], get_byte, False)
|
||||
decomp_save_2bit(r, fname, k)
|
||||
elif mode == '3bpp_np':
|
||||
print(k)
|
||||
r = get_bytes(tables.kCompSpritePtrs[k], 0x600)
|
||||
decomp_save(r, fname, decomp_one_spr_3bit, 24)
|
||||
elif mode== '3bpp':
|
||||
r = util.decomp(tables.kCompSpritePtrs[k], get_byte, False)
|
||||
decomp_save(r, fname, decomp_one_spr_3bit, 24)
|
||||
else:
|
||||
assert 0
|
||||
|
||||
if 0:
|
||||
for k, v in gfx_desc.items():
|
||||
decomp_generic(k, v[0])
|
||||
|
||||
def decode_room_objects(p):
|
||||
objs = []
|
||||
@@ -283,7 +422,6 @@ def decode_room_objects(p):
|
||||
doors.append({'type':get_byte(p + 1), 'pos' : get_byte(p) >> 4, 'dir' : A & 3})
|
||||
p += 2
|
||||
|
||||
@cache
|
||||
def get_chest_info():
|
||||
ea = 0x81e96e
|
||||
all = {}
|
||||
@@ -293,6 +431,8 @@ def get_chest_info():
|
||||
all.setdefault(room & 0x7fff, []).append((data, (room & 0x8000) != 0))
|
||||
return all
|
||||
|
||||
CHEST_INFO = get_chest_info()
|
||||
|
||||
def _get_entrance_info_one(i, set):
|
||||
def get_exit_door(i):
|
||||
x = get_word((0x82D724, 0x82DC32)[set] + i * 2)
|
||||
@@ -362,7 +502,7 @@ def _get_entrance_info_one(i, set):
|
||||
y['associated_entrance_index'] = get_word(0x82DC40 + i * 2)
|
||||
return room, y
|
||||
|
||||
@cache
|
||||
|
||||
def get_entrance_info(set):
|
||||
r = {}
|
||||
for i in range(133 if set == 0 else 7):
|
||||
@@ -370,9 +510,9 @@ def get_entrance_info(set):
|
||||
r.setdefault(room, []).append(y)
|
||||
return r
|
||||
|
||||
@cache
|
||||
def pits_hurt_player():
|
||||
return set(get_word(0x80990C + i * 2) for i in range(57))
|
||||
ENTRANCE_INFO = get_entrance_info(0)
|
||||
STARTING_POINT_INFO = get_entrance_info(1)
|
||||
PITS_HURT_PLAYER = set(get_word(0x80990C + i * 2) for i in range(57))
|
||||
|
||||
def print_room(room_index):
|
||||
p = 0x1f8000 + room_index * 3
|
||||
@@ -409,7 +549,7 @@ def print_room(room_index):
|
||||
'stair3_dest' : [get_byte(p+13),p8 & 3],
|
||||
'tele_msg' : get_word(0x87F61D+room_index*2),
|
||||
'sort_sprites' : sort_sprites_setting,
|
||||
'pits_hurt_player' : room_index in pits_hurt_player()
|
||||
'pits_hurt_player' : room_index in PITS_HURT_PLAYER
|
||||
}
|
||||
|
||||
def get_sprites():
|
||||
@@ -451,7 +591,7 @@ def print_room(room_index):
|
||||
|
||||
def get_chests():
|
||||
r = []
|
||||
for data, big in get_chest_info().get(room_index, []):
|
||||
for data, big in CHEST_INFO.get(room_index, []):
|
||||
if big:
|
||||
r.append('%d!' % data)
|
||||
else:
|
||||
@@ -462,9 +602,9 @@ def print_room(room_index):
|
||||
secrets = get_secrets()
|
||||
|
||||
data = {'Header' : header, 'Sprites' : sprites, 'Secrets' : secrets, 'Chests' : get_chests()}
|
||||
data['Entrances'] = get_entrance_info(0).get(room_index, [])
|
||||
if room_index in get_entrance_info(1):
|
||||
data['StartingPoints'] = get_entrance_info(1)[room_index]
|
||||
data['Entrances'] = ENTRANCE_INFO.get(room_index, [])
|
||||
if room_index in STARTING_POINT_INFO:
|
||||
data['StartingPoints'] = STARTING_POINT_INFO[room_index]
|
||||
|
||||
p = room_addr + 2
|
||||
p, objs, doors = decode_room_objects(p)
|
||||
@@ -479,12 +619,13 @@ def print_room(room_index):
|
||||
data['Layer3'] = objs
|
||||
if doors: data['Layer3.doors'] = doors
|
||||
|
||||
|
||||
return yaml.dump(data, default_flow_style=None, sort_keys=False)
|
||||
|
||||
def print_all_dungeon_rooms():
|
||||
for i in range(320):
|
||||
s = print_room(i)
|
||||
open( 'dungeon/dungeon-%d.yaml' % i, 'w').write(s)
|
||||
open(PATH + 'dungeon/dungeon-%d.yaml' % i, 'w').write(s)
|
||||
|
||||
def print_default_rooms():
|
||||
def print_default_room(idx):
|
||||
@@ -497,7 +638,7 @@ def print_default_rooms():
|
||||
for i in range(8):
|
||||
default_rooms['Default%d' % i] = print_default_room(i)
|
||||
s = yaml.dump(default_rooms, default_flow_style=None, sort_keys=False)
|
||||
open('dungeon/default_rooms.yaml', 'w').write(s)
|
||||
open(PATH + 'dungeon/default_rooms.yaml', 'w').write(s)
|
||||
|
||||
def print_overlay_rooms():
|
||||
def print_overlay_room(idx):
|
||||
@@ -510,32 +651,150 @@ def print_overlay_rooms():
|
||||
for i in range(19):
|
||||
default_rooms['Overlay%d' % i] = print_overlay_room(i)
|
||||
s = yaml.dump(default_rooms, default_flow_style=None, sort_keys=False)
|
||||
open('dungeon/overlay_rooms.yaml', 'w').write(s)
|
||||
open(PATH + 'dungeon/overlay_rooms.yaml', 'w').write(s)
|
||||
|
||||
def make_directories():
|
||||
def print_all():
|
||||
os.makedirs('img', exist_ok=True)
|
||||
os.makedirs('overworld', exist_ok=True)
|
||||
os.makedirs('dungeon', exist_ok=True)
|
||||
os.makedirs('sound', exist_ok=True)
|
||||
|
||||
def print_all_text_stuff():
|
||||
print_all_overworld_areas()
|
||||
decode_link_sprites()
|
||||
print_all_dungeon_rooms()
|
||||
print_overlay_rooms()
|
||||
print_default_rooms()
|
||||
print_dialogue()
|
||||
print_map32_to_map16(open('map32_to_map16.txt', 'w'))
|
||||
print_map32_to_map16(open(PATH+'map32_to_map16.txt', 'w'))
|
||||
extract_music.extract_sound_data(ROM)
|
||||
|
||||
def main():
|
||||
make_directories()
|
||||
print_all_text_stuff()
|
||||
extract_music.extract_sound_data(util.ROM)
|
||||
sprite_sheets.decode_link_sprites()
|
||||
sprite_sheets.decode_sprite_sheets()
|
||||
sprite_sheets.decode_hud_icons()
|
||||
sprite_sheets.decode_font()
|
||||
|
||||
if __name__ == "__main__":
|
||||
util.load_rom(sys.argv[1] if len(sys.argv) >= 2 else None)
|
||||
main()
|
||||
def get_hud_snes_palette():
|
||||
hud_palette = ROM.get_words(0x9BD660, 64)
|
||||
palette = [(31 << 10 | 31) for i in range(256)]
|
||||
for i in range(16):
|
||||
for j in range(1, 4):
|
||||
palette[i * 16 + j] = hud_palette[i * 4 + j]
|
||||
return palette
|
||||
|
||||
class PaletteUsage:
|
||||
def __init__(self):
|
||||
self.data = open('palette_usage.bin', 'rb').read()
|
||||
def get(self, icon):
|
||||
usage = self.data[icon]
|
||||
for j in range(8):
|
||||
if usage & (1 << j):
|
||||
return j
|
||||
return 0
|
||||
|
||||
def decode_hud_icons():
|
||||
kBasePal = { 105 : 2, 106 : 0, 107 : 1 }
|
||||
pu = PaletteUsage()
|
||||
|
||||
dst=[0]*128*64*3
|
||||
for image_set, slot in kBasePal.items():
|
||||
data = util.decomp(tables.kCompSpritePtrs[image_set], get_byte, False)
|
||||
|
||||
for i in range(128):
|
||||
pal_base = pu.get(slot * 128 + i) * 16
|
||||
x, y = i % 16, i // 16
|
||||
decomp_one_spr_2bit(data, i * 16, dst, x * 8 + y * 8 * 128 + slot * 128 * 64, 128, pal_base)
|
||||
save_array_as_image((128, 64 * 3), dst, 'hud_icons.png', convert_snes_palette(get_hud_snes_palette()[:128]))
|
||||
|
||||
def decode_font():
|
||||
data = ROM.get_bytes(0xe8000, 2048 * 2)
|
||||
dst=[0]*128*64*2
|
||||
for i in range(256):
|
||||
x, y = i % 16, i // 16
|
||||
pal_base = 6 * 16
|
||||
decomp_one_spr_2bit(data, i * 16, dst, x * 8 + y * 8 * 128, 128, pal_base)
|
||||
save_array_as_image((128, 64 * 2), dst, 'font.png', convert_snes_palette(get_hud_snes_palette()[:128]))
|
||||
|
||||
def fix_palette(iconfile):
|
||||
img_orig = Image.open('hud_icons_orig.png').tobytes()
|
||||
img = Image.open(iconfile)
|
||||
data = bytearray(img.tobytes())
|
||||
palette = img.palette.tobytes()
|
||||
|
||||
colors = {}
|
||||
snespal = ROM.get_words(0x9BD660, 32)
|
||||
for i in range(8):
|
||||
for j in range(1, 4):
|
||||
colors[(i, snespal[i * 4 + j])] = j
|
||||
|
||||
def fix_pixel(pos, x, y, pal):
|
||||
i = data[pos]
|
||||
r, g, b = palette[i * 3 + 0], palette[i * 3 + 1], palette[i * 3 + 2]
|
||||
if r == 0 and g == 0x80 and b == 0x80:
|
||||
return pal * 16
|
||||
sv = (b >> 3) << 10 | (g >> 3) << 5 | (r >> 3)
|
||||
vv = colors.get((pal, sv))
|
||||
# in the first version both black and transparent had the same color value
|
||||
# if it was transparent in the original, make it transparent here too, otherwise black
|
||||
if (r|g|b) == 0:
|
||||
if vv == None or (img_orig[pos] & 0xf) == 0:
|
||||
return pal * 16
|
||||
if vv == None:
|
||||
raise Exception('pixel at (%d, %d) with value (%d,%d,%d) palette %d not found %s' % (xo + x, yo + y, r, g, b, pal, (pal, sv)))
|
||||
data[pos] = pal * 16 + vv
|
||||
|
||||
def fix_single_16x16(pos, pal):
|
||||
for y in range(16):
|
||||
for x in range(16):
|
||||
fix_pixel(pos + y * 256 + x, x, y, pal)
|
||||
|
||||
pu = PaletteUsage()
|
||||
for yo in range(24):
|
||||
for xo in range(16):
|
||||
fix_single_16x16(yo * 256 * 16 + xo * 16, pu.get(yo * 16 + xo))
|
||||
|
||||
save_array_as_image((256, 128 * 3), data, 'hud_icons_fixed.png', convert_snes_palette(get_hud_snes_palette()[:128]))
|
||||
|
||||
def append_4bpp_tiles_to_binary_x2(data, tiles_w, tiles_h):
|
||||
def pack_pixels_4bpp(a):
|
||||
r = []
|
||||
for i in range(len(a) // 2):
|
||||
r.append(a[i * 2 + 0] & 0xf | (a[i * 2 + 1] & 0xf) << 4)
|
||||
return r
|
||||
def convert_single_16x16(data, pos):
|
||||
pixels = []
|
||||
for y in range(16):
|
||||
for x in range(16):
|
||||
pixels.append(data[pos + y * 256 + x])
|
||||
return pack_pixels_4bpp(pixels)
|
||||
out = []
|
||||
for yo in range(tiles_h):
|
||||
for xo in range(tiles_w):
|
||||
out.extend(convert_single_16x16(data, yo * 256 * 16 + xo * 16))
|
||||
return out
|
||||
|
||||
def convert_x2_icons_to_binary():
|
||||
# Convert to the new 4bpp ppu format
|
||||
out = []
|
||||
|
||||
img = Image.open('hud_icons_x2.png')
|
||||
img_data, img_palette = img.tobytes(), img.palette.tobytes() # need to call palette last, wtf
|
||||
out.extend(append_4bpp_tiles_to_binary_x2(img_data, 16, 24))
|
||||
|
||||
img = Image.open('hud_font_x2.png')
|
||||
out.extend(append_4bpp_tiles_to_binary_x2(img.tobytes(), 16, 16))
|
||||
|
||||
img = Image.open('linksprite_x2.png')
|
||||
out.extend(append_4bpp_tiles_to_binary_x2(img.tobytes(), 16, 56))
|
||||
|
||||
open('x2_icons.bin', 'wb').write(bytes(out))
|
||||
|
||||
snespal = []
|
||||
for i in range(128):
|
||||
r, g, b = img_palette[i * 3 + 0], img_palette[i * 3 + 1], img_palette[i * 3 + 2]
|
||||
snespal.append((b >> 3) << 10 | (g >> 3) << 5 | (r >> 3))
|
||||
snespal.extend(get_hud_snes_palette()[128:])
|
||||
open('x2_icons.pal', 'wb').write(array.array('H', snespal).tobytes())
|
||||
|
||||
|
||||
if args.convert_x2_icons:
|
||||
convert_x2_icons_to_binary()
|
||||
elif args.fix_palette:
|
||||
# decode_hud_icons()
|
||||
fix_palette('hud_icons_broken.png')
|
||||
else:
|
||||
#print_all()
|
||||
decode_font()
|
||||
@@ -881,6 +881,7 @@ def get_secret_names():
|
||||
kSecretNames = get_secret_names()
|
||||
kSecretNamesRev = invert_dict(kSecretNames)
|
||||
|
||||
|
||||
kCompSpritePtrs = [
|
||||
0x10f000,0x10f600,0x10fc00,0x118200,0x118800,0x118e00,0x119400,0x119a00,
|
||||
0x11a000,0x11a600,0x11ac00,0x11b200,0x14fffc,0x1585d4,0x158ab6,0x158fbe,
|
||||
@@ -914,219 +915,4 @@ kCompBgPtrs = [
|
||||
0x14fa55,0x14ff8c,0x14ff93,0x14ff9a,0x14ffa1,0x14ffa8,0x14ffaf,0x14ffb6,
|
||||
0x14ffbd,0x14ffc4,0x14ffcb,0x14ffd2,0x14ffd9,0x14ffe0,0x14ffe7,0x14ffee,
|
||||
0x14fff5,0x18b520,0x18b953,
|
||||
]
|
||||
|
||||
kSpriteTilesets = [
|
||||
( 0, 73, 0, 0),
|
||||
(70, 73, 12, 29),
|
||||
(72, 73, 19, 29),
|
||||
(70, 73, 19, 14),
|
||||
(72, 73, 12, 17),
|
||||
(72, 73, 12, 16),
|
||||
(79, 73, 74, 80),
|
||||
(14, 73, 74, 17),
|
||||
(70, 73, 18, 0),
|
||||
( 0, 73, 0, 80),
|
||||
( 0, 73, 0, 17),
|
||||
(72, 73, 12, 0),
|
||||
( 0, 0, 55, 54),
|
||||
(72, 73, 76, 17),
|
||||
(93, 44, 12, 68),
|
||||
( 0, 0, 78, 0),
|
||||
(15, 0, 18, 16),
|
||||
( 0, 0, 0, 76),
|
||||
( 0, 13, 23, 0),
|
||||
(22, 13, 23, 27),
|
||||
(22, 13, 23, 20),
|
||||
(21, 13, 23, 21),
|
||||
(22, 13, 24, 25),
|
||||
(22, 13, 23, 25),
|
||||
(22, 13, 0, 0),
|
||||
(22, 13, 24, 27),
|
||||
(15, 73, 74, 17),
|
||||
(75, 42, 92, 21),
|
||||
(22, 73, 23, 29),
|
||||
( 0, 0, 0, 21),
|
||||
(22, 13, 23, 16),
|
||||
(22, 73, 18, 0),
|
||||
(22, 73, 12, 17),
|
||||
( 0, 0, 18, 16),
|
||||
(22, 13, 0, 17),
|
||||
(22, 73, 12, 0),
|
||||
(22, 13, 76, 17),
|
||||
(14, 13, 74, 17),
|
||||
(22, 26, 23, 27),
|
||||
(79, 52, 74, 80),
|
||||
(53, 77, 101, 54),
|
||||
(74, 52, 78, 0),
|
||||
(14, 52, 74, 17),
|
||||
(81, 52, 93, 89),
|
||||
(75, 73, 76, 17),
|
||||
(45, 0, 0, 0),
|
||||
(93, 0, 18, 89),
|
||||
( 0, 0, 0, 0),
|
||||
( 0, 0, 0, 0),
|
||||
( 0, 0, 0, 0),
|
||||
( 0, 0, 0, 0),
|
||||
( 0, 0, 0, 0),
|
||||
( 0, 0, 0, 0),
|
||||
( 0, 0, 0, 0),
|
||||
( 0, 0, 0, 0),
|
||||
( 0, 0, 0, 0),
|
||||
( 0, 0, 0, 0),
|
||||
( 0, 0, 0, 0),
|
||||
( 0, 0, 0, 0),
|
||||
( 0, 0, 0, 0),
|
||||
( 0, 0, 0, 0),
|
||||
( 0, 0, 0, 0),
|
||||
( 0, 0, 0, 0),
|
||||
( 0, 0, 0, 0),
|
||||
(71, 73, 43, 45),
|
||||
(70, 73, 28, 82),
|
||||
( 0, 73, 28, 82),
|
||||
(93, 73, 0, 82),
|
||||
(70, 73, 19, 82),
|
||||
(75, 77, 74, 90),
|
||||
(71, 73, 28, 82),
|
||||
(75, 77, 57, 54),
|
||||
(31, 44, 46, 82),
|
||||
(31, 44, 46, 29),
|
||||
(47, 44, 46, 82),
|
||||
(47, 44, 46, 49),
|
||||
(31, 30, 48, 82),
|
||||
(81, 73, 19, 0),
|
||||
(79, 73, 19, 80),
|
||||
(79, 77, 74, 80),
|
||||
(75, 73, 76, 43),
|
||||
(31, 32, 34, 83),
|
||||
(85, 61, 66, 67),
|
||||
(31, 30, 35, 82),
|
||||
(31, 30, 57, 58),
|
||||
(31, 30, 58, 62),
|
||||
(31, 30, 60, 61),
|
||||
(64, 30, 39, 63),
|
||||
(85, 26, 66, 67),
|
||||
(31, 30, 42, 82),
|
||||
(31, 30, 56, 82),
|
||||
(31, 32, 40, 82),
|
||||
(31, 32, 38, 82),
|
||||
(31, 44, 37, 82),
|
||||
(31, 32, 39, 82),
|
||||
(31, 30, 41, 82),
|
||||
(31, 44, 59, 82),
|
||||
(70, 73, 36, 82),
|
||||
(33, 65, 69, 51),
|
||||
(31, 44, 40, 49),
|
||||
(31, 13, 41, 82),
|
||||
(31, 30, 39, 82),
|
||||
(31, 32, 39, 83),
|
||||
(72, 73, 19, 82),
|
||||
(14, 30, 74, 80),
|
||||
(31, 32, 38, 83),
|
||||
(21, 0, 0, 0),
|
||||
(31, 0, 42, 82),
|
||||
( 0, 0, 0, 0),
|
||||
( 0, 0, 0, 0),
|
||||
( 0, 0, 0, 0),
|
||||
( 0, 0, 0, 0),
|
||||
( 0, 0, 0, 0),
|
||||
( 0, 0, 0, 0),
|
||||
( 0, 0, 0, 0),
|
||||
( 0, 0, 0, 0),
|
||||
( 0, 0, 0, 0),
|
||||
( 0, 0, 0, 0),
|
||||
( 0, 0, 0, 0),
|
||||
( 0, 0, 0, 0),
|
||||
( 0, 0, 0, 0),
|
||||
( 0, 0, 0, 0),
|
||||
( 0, 0, 0, 0),
|
||||
( 0, 0, 0, 0),
|
||||
( 0, 0, 0, 0),
|
||||
(50, 0, 0, 8),
|
||||
(93, 73, 0, 82),
|
||||
(85, 73, 66, 67),
|
||||
(97, 98, 99, 80),
|
||||
(97, 98, 99, 80),
|
||||
(97, 98, 99, 80),
|
||||
(97, 98, 99, 80),
|
||||
(97, 98, 99, 80),
|
||||
(97, 98, 99, 80),
|
||||
(97, 86, 87, 80),
|
||||
(97, 98, 99, 80),
|
||||
(97, 98, 99, 80),
|
||||
(97, 86, 87, 80),
|
||||
(97, 86, 99, 80),
|
||||
(97, 86, 87, 80),
|
||||
(97, 86, 51, 80),
|
||||
(97, 86, 87, 80),
|
||||
(97, 98, 99, 80),
|
||||
(97, 98, 99, 80),
|
||||
]
|
||||
|
||||
kDungPalinfos = [
|
||||
( 0, 0, 3, 1),
|
||||
( 2, 0, 3, 1),
|
||||
( 4, 0, 10, 1),
|
||||
( 6, 0, 1, 7),
|
||||
(10, 2, 2, 7),
|
||||
( 4, 4, 3, 10),
|
||||
(12, 5, 8, 20),
|
||||
(14, 0, 3, 10),
|
||||
( 2, 0, 15, 20),
|
||||
(10, 2, 0, 7),
|
||||
( 2, 0, 15, 12),
|
||||
( 6, 0, 6, 7),
|
||||
( 0, 0, 14, 18),
|
||||
(18, 5, 5, 11),
|
||||
(18, 0, 2, 12),
|
||||
(16, 5, 10, 7),
|
||||
(16, 0, 16, 12),
|
||||
(22, 7, 2, 7),
|
||||
(22, 0, 7, 15),
|
||||
( 8, 0, 4, 12),
|
||||
( 8, 0, 4, 9),
|
||||
( 4, 0, 3, 1),
|
||||
(20, 0, 4, 4),
|
||||
(20, 0, 20, 12),
|
||||
(24, 5, 7, 11),
|
||||
(24, 6, 16, 12),
|
||||
(26, 5, 8, 20),
|
||||
(26, 2, 0, 7),
|
||||
( 6, 0, 3, 10),
|
||||
(28, 0, 3, 1),
|
||||
(30, 0, 11, 17),
|
||||
( 4, 0, 11, 17),
|
||||
(14, 0, 0, 2),
|
||||
(32, 8, 19, 13),
|
||||
(10, 0, 3, 10),
|
||||
(20, 0, 4, 4),
|
||||
(26, 2, 2, 7),
|
||||
(26, 10, 0, 0),
|
||||
( 0, 0, 3, 2),
|
||||
(14, 0, 3, 7),
|
||||
(26, 5, 5, 11),
|
||||
]
|
||||
|
||||
kOwSprPalInfo = [
|
||||
-1, -1, 3, 10, 3, 6, 3, 1, 0, 2, 3, 14, 3, 2, 19, 1, 11, 12, 17, 1, 7, 5, 17, 0,
|
||||
9, 11, 15, 5, 3, 5, 3, 7, 15, 2, 10, 2, 5, 1, 12, 14,
|
||||
]
|
||||
|
||||
kSpriteInit_Flags3 = [
|
||||
0x19, 0xb, 0x1b, 0x4b, 0x41, 0x41, 0x41, 0x4d, 0x1d, 1, 0x1d, 0x19, 0x8d, 0x1b, 9, 0x9d,
|
||||
0x3d, 1, 9, 0x11, 0x40, 1, 0x4d, 0x19, 7, 0x1d, 0x59, 0x80, 0x4d, 0x40, 1, 0x49,
|
||||
0x1b, 0x41, 3, 0x13, 0x15, 0x41, 0x18, 0x1b, 0x41, 0x47, 0xf, 0x49, 0x4b, 0x4d, 0x41, 0x47,
|
||||
0x49, 0x4d, 0x49, 0x40, 0x4d, 0x47, 0x49, 0x41, 0x74, 0x47, 0x5b, 0x58, 0x51, 0x49, 0x1d, 0x5d,
|
||||
3, 0x19, 0x1b, 0x17, 0x19, 0x17, 0x19, 0x1b, 0x17, 0x17, 0x17, 0x1b, 0xd, 9, 0x19, 0x19,
|
||||
0x49, 0x5d, 0x5b, 0x49, 0xd, 3, 0x13, 0x41, 0x1b, 0x5b, 0x5d, 0x43, 0x43, 0x4d, 0x4d, 0x4d,
|
||||
0x4d, 0x4d, 0x49, 1, 0, 0x41, 0x4d, 0x4d, 0x4d, 0x4d, 0x1d, 9, 0xc4, 0xd, 0xd, 9,
|
||||
3, 3, 0x4b, 0x47, 0x47, 0x49, 0x49, 0x41, 0x47, 0x36, 0x8b, 0x49, 0x1d, 0x49, 0x43, 0x43,
|
||||
0x43, 0xb, 0x41, 0xd, 7, 0xb, 0x1d, 0x43, 0xd, 0x43, 0xd, 0x1d, 0x4d, 0x4d, 0x1b, 0x1b,
|
||||
0xa, 0xb, 0, 5, 0xd, 1, 1, 1, 1, 0xb, 5, 1, 1, 1, 7, 0x17,
|
||||
0x19, 0xd, 0xd, 0x80, 0x4d, 0x19, 0x17, 0x19, 0xb, 9, 0xd, 0x4a, 0x12, 0x49, 0xc3, 0xc3,
|
||||
0xc3, 0xc3, 0x76, 0x40, 0x59, 0x41, 0x58, 0x4f, 0x73, 0x5b, 0x44, 0x41, 0x51, 0xa, 0xb, 0xb,
|
||||
0x4b, 0, 0x40, 0x5b, 0xd, 0, 0, 0xd, 0x4b, 0xb, 0x59, 0x41, 0xb, 0xd, 1, 0xd,
|
||||
0xd, 0, 0x50, 0x4c, 0x44, 0x51, 1, 1, 0xf2, 0xf8, 0xf4, 0xf2, 0xd4, 0xd4, 0xd4, 0xf8,
|
||||
0xf8, 0xf4, 0xf4, 0xd8, 0xf8, 0xd8, 0xdf, 0xc8, 0x69, 0xc1, 0xd2, 0xd2, 0xdc, 0xc7, 0xc1, 0xc7,
|
||||
0xc7, 0xc7, 0xc1,
|
||||
]
|
||||
]
|
||||
255
tables/text_compression.py
Normal file
255
tables/text_compression.py
Normal file
@@ -0,0 +1,255 @@
|
||||
|
||||
kTextAlphabet = [
|
||||
"A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M",
|
||||
"N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z",
|
||||
|
||||
"a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m",
|
||||
"n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z",
|
||||
|
||||
"0", "1", "2", "3", "4", "5", "6", "7", "8", "9",
|
||||
|
||||
# codes 0x3E and up
|
||||
"!", "?", "-", ".", ",",
|
||||
|
||||
# codes 0x43 and up
|
||||
"[...]", ">", "(", ")",
|
||||
|
||||
# codes 0x47 and up
|
||||
"[Ankh]", "[Waves]", "[Snake]", "[LinkL]", "[LinkR]",
|
||||
"\"", "[Up]", "[Down]", "[Left]", "[Right]", "'",
|
||||
|
||||
# codes 0x52 and up
|
||||
"[1HeartL]", "[1HeartR]", "[2HeartL]", "[3HeartL]",
|
||||
"[3HeartR]", "[4HeartL]", "[4HeartR]",
|
||||
|
||||
" ", "<", "[A]", "[B]", "[X]", "[Y]",
|
||||
]
|
||||
|
||||
kText_CommandLengths = [1, 1, 1, 1, 2, 2, 2, 2, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 1, 1, 1, 1, 1, ]
|
||||
kText_CommandNames = [
|
||||
"NextPic",
|
||||
"Choose",
|
||||
"Item",
|
||||
"Name",
|
||||
"Window",
|
||||
"Number",
|
||||
"Position",
|
||||
"ScrollSpd",
|
||||
"Selchg",
|
||||
"Crash",
|
||||
"Choose3",
|
||||
"Choose2",
|
||||
"Scroll",
|
||||
"1",
|
||||
"2",
|
||||
"3",
|
||||
"Color",
|
||||
"Wait",
|
||||
"Sound",
|
||||
"Speed",
|
||||
"Mark",
|
||||
"Mark2",
|
||||
"Clear",
|
||||
"Waitkey",
|
||||
"EndMessage"
|
||||
]
|
||||
|
||||
kTextDictionary = [ 0x59, 0x59, 0x59, 0x59,
|
||||
0x59, 0x59, 0x59,
|
||||
0x59, 0x59,
|
||||
0x51, 0x2c, 0x59,
|
||||
0x1a, 0x27, 0x1d, 0x59,
|
||||
0x1a, 0x2b, 0x1e, 0x59,
|
||||
0x1a, 0x25, 0x25, 0x59,
|
||||
0x1a, 0x22, 0x27,
|
||||
0x1a, 0x27, 0x1d,
|
||||
0x1a, 0x2d, 0x59,
|
||||
0x1a, 0x2c, 0x2d,
|
||||
0x1a, 0x27,
|
||||
0x1a, 0x2d,
|
||||
0x1b, 0x25, 0x1e,
|
||||
0x1b, 0x1a,
|
||||
0x1b, 0x1e,
|
||||
0x1b, 0x28,
|
||||
0x1c, 0x1a, 0x27, 0x59,
|
||||
0x1c, 0x21, 0x1e,
|
||||
0x1c, 0x28, 0x26,
|
||||
0x1c, 0x24,
|
||||
0x1d, 0x1e, 0x2c,
|
||||
0x1d, 0x22,
|
||||
0x1d, 0x28,
|
||||
0x1e, 0x27, 0x59,
|
||||
0x1e, 0x2b, 0x59,
|
||||
0x1e, 0x1a, 0x2b,
|
||||
0x1e, 0x27, 0x2d,
|
||||
0x1e, 0x1d, 0x59,
|
||||
0x1e, 0x27,
|
||||
0x1e, 0x2b,
|
||||
0x1e, 0x2f,
|
||||
0x1f, 0x28, 0x2b,
|
||||
0x1f, 0x2b, 0x28,
|
||||
0x20, 0x22, 0x2f, 0x1e, 0x59,
|
||||
0x20, 0x1e, 0x2d,
|
||||
0x20, 0x28,
|
||||
0x21, 0x1a, 0x2f, 0x1e,
|
||||
0x21, 0x1a, 0x2c,
|
||||
0x21, 0x1e, 0x2b,
|
||||
0x21, 0x22,
|
||||
0x21, 0x1a,
|
||||
0x22, 0x20, 0x21, 0x2d, 0x59,
|
||||
0x22, 0x27, 0x20, 0x59,
|
||||
0x22, 0x27,
|
||||
0x22, 0x2c,
|
||||
0x22, 0x2d,
|
||||
0x23, 0x2e, 0x2c, 0x2d,
|
||||
0x24, 0x27, 0x28, 0x30,
|
||||
0x25, 0x32, 0x59,
|
||||
0x25, 0x1a,
|
||||
0x25, 0x28,
|
||||
0x26, 0x1a, 0x27,
|
||||
0x26, 0x1a,
|
||||
0x26, 0x1e,
|
||||
0x26, 0x2e,
|
||||
0x27, 0x51, 0x2d, 0x59,
|
||||
0x27, 0x28, 0x27,
|
||||
0x27, 0x28, 0x2d,
|
||||
0x28, 0x29, 0x1e, 0x27,
|
||||
0x28, 0x2e, 0x27, 0x1d,
|
||||
0x28, 0x2e, 0x2d, 0x59,
|
||||
0x28, 0x1f,
|
||||
0x28, 0x27,
|
||||
0x28, 0x2b,
|
||||
0x29, 0x1e, 0x2b,
|
||||
0x29, 0x25, 0x1e,
|
||||
0x29, 0x28, 0x30,
|
||||
0x29, 0x2b, 0x28,
|
||||
0x2b, 0x1e, 0x59,
|
||||
0x2b, 0x1e,
|
||||
0x2c, 0x28, 0x26, 0x1e,
|
||||
0x2c, 0x1e,
|
||||
0x2c, 0x21,
|
||||
0x2c, 0x28,
|
||||
0x2c, 0x2d,
|
||||
0x2d, 0x1e, 0x2b, 0x59,
|
||||
0x2d, 0x21, 0x22, 0x27,
|
||||
0x2d, 0x1e, 0x2b,
|
||||
0x2d, 0x21, 0x1a,
|
||||
0x2d, 0x21, 0x1e,
|
||||
0x2d, 0x21, 0x22,
|
||||
0x2d, 0x28,
|
||||
0x2d, 0x2b,
|
||||
0x2e, 0x29,
|
||||
0x2f, 0x1e, 0x2b,
|
||||
0x30, 0x22, 0x2d, 0x21,
|
||||
0x30, 0x1a,
|
||||
0x30, 0x1e,
|
||||
0x30, 0x21,
|
||||
0x30, 0x22,
|
||||
0x32, 0x28, 0x2e,
|
||||
0x7, 0x1e, 0x2b,
|
||||
0x13, 0x21, 0x1a,
|
||||
0x13, 0x21, 0x1e,
|
||||
0x13, 0x21, 0x22,
|
||||
0x18, 0x28, 0x2e,
|
||||
]
|
||||
|
||||
kTextDictionary_Idx = [
|
||||
0, 4, 7, 9, 12, 16, 20, 24, 27, 30, 33, 36, 38, 40, 43, 45, 47, 49, 53, 56, 59, 61, 64, 66, 68, 71, 74, 77, 80, 83, 85, 87, 89, 92, 95, 100, 103, 105, 109, 112, 115, 117, 119, 124, 128, 130, 132, 134, 138, 142, 145, 147, 149, 152, 154, 156, 158, 162, 165, 168, 172, 176, 180, 182, 184, 186, 189, 192, 195, 198, 201, 203, 207, 209, 211, 213, 215, 219, 223, 226, 229, 232, 235, 237, 239, 241, 244, 248, 250, 252, 254, 256, 259, 262, 265, 268, 271, 274
|
||||
]
|
||||
|
||||
def make_dict():
|
||||
r, rinv = {}, {}
|
||||
for i in range(len(kTextDictionary_Idx) - 1):
|
||||
ln = kTextDictionary_Idx[i + 1] - kTextDictionary_Idx[i]
|
||||
idx = kTextDictionary_Idx[i]
|
||||
s = "".join(kTextAlphabet[kTextDictionary[idx+i]] for i in range(ln))
|
||||
r[i] = s
|
||||
rinv[s] = i
|
||||
return r, rinv
|
||||
|
||||
kTextDictionary_Ascii, kTextDictionary_AsciiBack = make_dict()
|
||||
|
||||
def decode_strings(get_byte):
|
||||
p = 0x9c8000
|
||||
result = []
|
||||
while True:
|
||||
org_p = p
|
||||
#print('0x%x' % p)
|
||||
s = ''
|
||||
srcdata = []
|
||||
while True:
|
||||
c = get_byte(p)
|
||||
srcdata.append(c)
|
||||
l = kText_CommandLengths[c - 0x67] if c >= 0x67 and c < 0x80 else 1
|
||||
p += l
|
||||
if c == 0x7f:
|
||||
break
|
||||
if c < 0x67:
|
||||
s += kTextAlphabet[c]
|
||||
elif c < 0x80:
|
||||
if l == 2:
|
||||
srcdata.append(get_byte(p-1))
|
||||
s += '[%s %.2d]' % (kText_CommandNames[c - 0x67], get_byte(p-1))
|
||||
else:
|
||||
s += '[%s]' % kText_CommandNames[c - 0x67]
|
||||
elif c == 0x80:
|
||||
p = 0x8edf40
|
||||
s = None
|
||||
break
|
||||
elif c > 0x80 and c < 0x88:
|
||||
assert 0
|
||||
elif c == 0xff:
|
||||
return result
|
||||
else:
|
||||
s += kTextDictionary_Ascii[c - 0x88]
|
||||
if s != None:
|
||||
result.append((s, srcdata))
|
||||
|
||||
def print_strings(f, get_byte):
|
||||
for i, s in enumerate(decode_strings(get_byte)):
|
||||
print('%s: %s' % (i + 1, s[0]), file = f)
|
||||
|
||||
def find_string_char_at(s, i):
|
||||
a = s[i:]
|
||||
|
||||
for k, v in kTextDictionary_AsciiBack.items():
|
||||
if a.startswith(k):
|
||||
return [v + 0x88], len(k)
|
||||
|
||||
for i, s in enumerate(kTextAlphabet):
|
||||
if a.startswith(s):
|
||||
return [i], len(s)
|
||||
|
||||
if a.startswith('['):
|
||||
cmd = a[1:a.index(']')]
|
||||
if cmd in kText_CommandNames:
|
||||
i = kText_CommandNames.index(cmd)
|
||||
return [i + 0x67], len(cmd) + 2
|
||||
|
||||
for i, s in enumerate(kText_CommandNames):
|
||||
if kText_CommandLengths[i] == 2 and cmd.startswith(s):
|
||||
e = cmd[len(s):].strip()
|
||||
return [i + 0x67, int(e)], len(cmd) + 2
|
||||
|
||||
print('substr %s not found' % a)
|
||||
assert 0
|
||||
|
||||
def compress_string(s):
|
||||
# find the greedy best match
|
||||
i = 0
|
||||
r = []
|
||||
while i < len(s):
|
||||
what, num = find_string_char_at(s, i)
|
||||
r.extend(what)
|
||||
i += num
|
||||
r.append(0x7f)
|
||||
return r
|
||||
|
||||
def verify(get_byte):
|
||||
for i, (decoded, original) in enumerate(decode_strings(get_byte)):
|
||||
c = compress_string(decoded)
|
||||
if c != original:
|
||||
print('String %s not match: %s, %s' % (decoded, c, original))
|
||||
break
|
||||
else:
|
||||
pass
|
||||
@@ -2,89 +2,19 @@ import array
|
||||
import sys
|
||||
import hashlib
|
||||
import os
|
||||
from functools import lru_cache
|
||||
|
||||
def cache(user_function):
|
||||
'Simple lightweight unbounded cache. Sometimes called "memoize".'
|
||||
return lru_cache(maxsize=None)(user_function)
|
||||
|
||||
# Both are common SNES rom extensions. For Zelda3 (NA), they are equivalent files.
|
||||
COMMON_ROM_NAMES = ['zelda3.sfc', 'zelda3.smc']
|
||||
DEFAULT_ROM_DIRECTORY = os.path.join(os.path.dirname(__file__), '..')
|
||||
|
||||
ZELDA3_SHA1_US = '6D4F10A8B10E10DBE624CB23CF03B88BB8252973'
|
||||
ZELDA3_SHA1 = {
|
||||
ZELDA3_SHA1_US : ('us', 'Legend of Zelda, The - A Link to the Past (USA)'),
|
||||
'2E62494967FB0AFDF5DA1635607F9641DF7C6559' : ('de', 'Legend of Zelda, The - A Link to the Past (Germany)'),
|
||||
'229364A1B92A05167CD38609B1AA98F7041987CC' : ('fr', 'Legend of Zelda, The - A Link to the Past (France)'),
|
||||
'C1C6C7F76FFF936C534FF11F87A54162FC0AA100' : ('fr-c', 'Legend of Zelda, The - A Link to the Past (Canada)'),
|
||||
'7C073A222569B9B8E8CA5FCB5DFEC3B5E31DA895' : ('en', 'Legend of Zelda, The - A Link to the Past (Europe)'),
|
||||
'461FCBD700D1332009C0E85A7A136E2A8E4B111E' : ('es', 'Spanish - https://www.romhacking.net/translations/2195/'),
|
||||
'3C4D605EEFDA1D76F101965138F238476655B11D' : ('pl', 'Polish - https://www.romhacking.net/translations/5760/'),
|
||||
'D0D09ED41F9C373FE6AFDCCAFBF0DA8C88D3D90D' : ('pt', 'Portuguese - https://www.romhacking.net/translations/6530/'),
|
||||
'B2A07A59E64C498BC1B2F28728F9BF4014C8D582' : ('redux', 'English Redux - https://www.romhacking.net/translations/6657/'),
|
||||
'9325C22EB0A2A1F0017157C8B620BC3A605CEDE1' : ('redux', 'English Redux - https://www.romhacking.net/hacks/2594/'),
|
||||
'FA8ADFDBA2697C9A54D583A1284A22AC764C7637' : ('nl', 'Dutch - https://www.romhacking.net/translations/1124/'),
|
||||
'43CD3438469B2C3FE879EA2F410B3EF3CB3F1CA4' : ('sv', 'Swedish - https://www.romhacking.net/translations/982/'),
|
||||
}
|
||||
|
||||
def load_rom(filename, support_multilanguage = False):
|
||||
global ROM
|
||||
ROM = LoadedRom(filename, support_multilanguage)
|
||||
return ROM
|
||||
|
||||
def get_byte(addr):
|
||||
return ROM.get_byte(addr)
|
||||
|
||||
@cache
|
||||
def get_bytes(addr, n):
|
||||
return ROM.get_bytes(addr, n)
|
||||
|
||||
@cache
|
||||
def get_words(addr, n):
|
||||
return ROM.get_words(addr, n)
|
||||
|
||||
def get_int8(ea):
|
||||
b = get_byte(ea)
|
||||
if b & 0x80: b -= 256
|
||||
return b
|
||||
|
||||
def get_int16(ea):
|
||||
b = get_word(ea)
|
||||
if b & 0x8000: b -= 65536
|
||||
return b
|
||||
|
||||
def get_word(addr):
|
||||
return ROM.get_word(addr)
|
||||
|
||||
DEFAULT_ROM_DIRECTORY = os.path.dirname(__file__)
|
||||
ZELDA3_SHA256 = '66871d66be19ad2c34c927d6b14cd8eb6fc3181965b6e517cb361f7316009cfb'
|
||||
|
||||
class LoadedRom:
|
||||
def __init__(self, path = None, support_multilanguage = False):
|
||||
def __init__(self, path = None):
|
||||
rom_path = self.__get_rom_path(path)
|
||||
self.ROM = open(rom_path, 'rb').read()
|
||||
|
||||
# Remove the SMC header?
|
||||
if (len(self.ROM) & 0xfffff) == 0x200:
|
||||
self.ROM = self.ROM[0x200:]
|
||||
|
||||
hash = hashlib.sha1(self.ROM).hexdigest().upper()
|
||||
entry = ZELDA3_SHA1.get(hash)
|
||||
self.language = entry[0] if entry != None else None
|
||||
|
||||
# Workaround for swedish rom with broken size
|
||||
if self.language == 'sv' and len(self.ROM) == 0x10083b:
|
||||
self.ROM = self.ROM[0x200:]
|
||||
|
||||
if support_multilanguage:
|
||||
if self.language == None:
|
||||
msg = f"\n\nROM with hash {hash} not supported.\n\nYou need one of the following ROMs to extract the resources:\n"
|
||||
for k, v in ZELDA3_SHA1.items():
|
||||
msg += '%5s: %s: %s\n' % (v[0], k, v[1])
|
||||
raise Exception(msg)
|
||||
print('Identified ROM as: %s - "%s"' % entry)
|
||||
else:
|
||||
if self.language != 'us':
|
||||
raise Exception(f"\n\nROM with hash {hash} not supported.\n\nExpected {ZELDA3_SHA1_US}.\nPlease verify your ROM is \"Legend of Zelda, The - A Link to the Past (USA)\"");
|
||||
hash = hashlib.sha256(self.ROM).hexdigest()
|
||||
if hash != ZELDA3_SHA256:
|
||||
raise Exception(f"ROM with hash {hash} not supported. Expected {ZELDA3_SHA256}. Please verify your ROM is the NA 1.0 version.");
|
||||
|
||||
def get_byte(self, ea):
|
||||
assert (ea & 0x8000)
|
||||
@@ -98,7 +28,7 @@ class LoadedRom:
|
||||
return self.get_byte(ea) + self.get_byte(ea + 1) * 256 + self.get_byte(ea + 2) * 65536
|
||||
|
||||
def get_bytes(self, addr, n):
|
||||
r = bytearray()
|
||||
r = []
|
||||
for i in range(n):
|
||||
r.append(self.get_byte(addr))
|
||||
addr += 1
|
||||
@@ -426,23 +426,8 @@ void Follower_NotFollowing() { // 89a2b2
|
||||
Tagalong_Draw();
|
||||
} else {
|
||||
if (follower_indicator == 13 && !player_is_indoors && !super_bomb_indicator_unk2) {
|
||||
// Fixed so we wait a little bit if we can't spawn the ancilla
|
||||
if (AncillaAdd_SuperBombExplosion(0x3a, 0) >= 0) {
|
||||
follower_dropped = 0;
|
||||
|
||||
// A ticking super bomb will cancel and teleport back to you as a follower if you do
|
||||
// any of these at count 0: (1) change screen via walking, mirror, or bird travel
|
||||
// (2) fill all ancillary slots
|
||||
// (3) die with a bottled faerie.
|
||||
// Fixed this by clearing the follower indicator here, instead of in the ancilla
|
||||
// bomb code.
|
||||
if (enhanced_features0 & kFeatures0_MiscBugFixes) {
|
||||
follower_indicator = 0;
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
super_bomb_indicator_unk1 = 1;
|
||||
}
|
||||
AncillaAdd_SuperBombExplosion(0x3a, 0);
|
||||
follower_dropped = 0;
|
||||
}
|
||||
Follower_DoLayers();
|
||||
}
|
||||
@@ -662,14 +647,11 @@ incr:
|
||||
uint8 pal;
|
||||
skip_first_sprites:
|
||||
pal = kTagalongDraw_Pals[follower_indicator];
|
||||
if (pal == 7 && palette_swap_flag)
|
||||
if (pal == 7 && overworld_palette_swap_flag)
|
||||
pal = 0;
|
||||
|
||||
if (follower_indicator == 13) {
|
||||
// Display colorful superbomb palette also on frame 0.
|
||||
if (enhanced_features0 & kFeatures0_MiscBugFixes ? (super_bomb_indicator_unk2 <= 1) : (super_bomb_indicator_unk2 == 1))
|
||||
pal = (frame_counter & 7);
|
||||
}
|
||||
if (follower_indicator == 13 && super_bomb_indicator_unk2 == 1)
|
||||
pal = (frame_counter & 7);
|
||||
|
||||
const TagalongSprXY *sprd = kTagalongDraw_SprXY + frame + (kTagalongDraw_Offs[follower_indicator] >> 3);
|
||||
const TagalongDmaFlags *sprf = kTagalongDmaAndFlags + frame;
|
||||
3
third_party/.gitignore
vendored
3
third_party/.gitignore
vendored
@@ -1,3 +0,0 @@
|
||||
/tcc/
|
||||
/SDL2-2.*/
|
||||
/gl_core/*.o
|
||||
3189
third_party/gl_core/gl_core_3_1.c
vendored
3189
third_party/gl_core/gl_core_3_1.c
vendored
File diff suppressed because it is too large
Load Diff
3703
third_party/gl_core/gl_core_3_1.h
vendored
3703
third_party/gl_core/gl_core_3_1.h
vendored
File diff suppressed because it is too large
Load Diff
1
third_party/opus-1.3.1-stripped/.gitignore
vendored
1
third_party/opus-1.3.1-stripped/.gitignore
vendored
@@ -1 +0,0 @@
|
||||
/opus_decoder_amalgam.o
|
||||
6
third_party/opus-1.3.1-stripped/AUTHORS
vendored
6
third_party/opus-1.3.1-stripped/AUTHORS
vendored
@@ -1,6 +0,0 @@
|
||||
Jean-Marc Valin (jmvalin@jmvalin.ca)
|
||||
Koen Vos (koenvos74@gmail.com)
|
||||
Timothy Terriberry (tterribe@xiph.org)
|
||||
Karsten Vandborg Sorensen (karsten.vandborg.sorensen@skype.net)
|
||||
Soren Skak Jensen (ssjensen@gn.com)
|
||||
Gregory Maxwell (greg@xiph.org)
|
||||
44
third_party/opus-1.3.1-stripped/COPYING
vendored
44
third_party/opus-1.3.1-stripped/COPYING
vendored
@@ -1,44 +0,0 @@
|
||||
Copyright 2001-2011 Xiph.Org, Skype Limited, Octasic,
|
||||
Jean-Marc Valin, Timothy B. Terriberry,
|
||||
CSIRO, Gregory Maxwell, Mark Borgerding,
|
||||
Erik de Castro Lopo
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions
|
||||
are met:
|
||||
|
||||
- Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
|
||||
- Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
|
||||
- Neither the name of Internet Society, IETF or IETF Trust, nor the
|
||||
names of specific contributors, may be used to endorse or promote
|
||||
products derived from this software without specific prior written
|
||||
permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
|
||||
OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
Opus is subject to the royalty-free patent licenses which are
|
||||
specified at:
|
||||
|
||||
Xiph.Org Foundation:
|
||||
https://datatracker.ietf.org/ipr/1524/
|
||||
|
||||
Microsoft Corporation:
|
||||
https://datatracker.ietf.org/ipr/1914/
|
||||
|
||||
Broadcom Corporation:
|
||||
https://datatracker.ietf.org/ipr/1526/
|
||||
161
third_party/opus-1.3.1-stripped/README
vendored
161
third_party/opus-1.3.1-stripped/README
vendored
@@ -1,161 +0,0 @@
|
||||
== Opus audio codec ==
|
||||
|
||||
Opus is a codec for interactive speech and audio transmission over the Internet.
|
||||
|
||||
Opus can handle a wide range of interactive audio applications, including
|
||||
Voice over IP, videoconferencing, in-game chat, and even remote live music
|
||||
performances. It can scale from low bit-rate narrowband speech to very high
|
||||
quality stereo music.
|
||||
|
||||
Opus, when coupled with an appropriate container format, is also suitable
|
||||
for non-realtime stored-file applications such as music distribution, game
|
||||
soundtracks, portable music players, jukeboxes, and other applications that
|
||||
have historically used high latency formats such as MP3, AAC, or Vorbis.
|
||||
|
||||
Opus is specified by IETF RFC 6716:
|
||||
https://tools.ietf.org/html/rfc6716
|
||||
|
||||
The Opus format and this implementation of it are subject to the royalty-
|
||||
free patent and copyright licenses specified in the file COPYING.
|
||||
|
||||
This package implements a shared library for encoding and decoding raw Opus
|
||||
bitstreams. Raw Opus bitstreams should be used over RTP according to
|
||||
https://tools.ietf.org/html/rfc7587
|
||||
|
||||
The package also includes a number of test tools used for testing the
|
||||
correct operation of the library. The bitstreams read/written by these
|
||||
tools should not be used for Opus file distribution: They include
|
||||
additional debugging data and cannot support seeking.
|
||||
|
||||
Opus stored in files should use the Ogg encapsulation for Opus which is
|
||||
described at:
|
||||
https://tools.ietf.org/html/rfc7845
|
||||
|
||||
An opus-tools package is available which provides encoding and decoding of
|
||||
Ogg encapsulated Opus files and includes a number of useful features.
|
||||
|
||||
Opus-tools can be found at:
|
||||
https://git.xiph.org/?p=opus-tools.git
|
||||
or on the main Opus website:
|
||||
https://opus-codec.org/
|
||||
|
||||
== Compiling libopus ==
|
||||
|
||||
To build from a distribution tarball, you only need to do the following:
|
||||
|
||||
% ./configure
|
||||
% make
|
||||
|
||||
To build from the git repository, the following steps are necessary:
|
||||
|
||||
0) Set up a development environment:
|
||||
|
||||
On an Ubuntu or Debian family Linux distribution:
|
||||
|
||||
% sudo apt-get install git autoconf automake libtool gcc make
|
||||
|
||||
On a Fedora/Redhat based Linux:
|
||||
|
||||
% sudo dnf install git autoconf automake libtool gcc make
|
||||
|
||||
Or for older Redhat/Centos Linux releases:
|
||||
|
||||
% sudo yum install git autoconf automake libtool gcc make
|
||||
|
||||
On Apple macOS, install Xcode and brew.sh, then in the Terminal enter:
|
||||
|
||||
% brew install autoconf automake libtool
|
||||
|
||||
1) Clone the repository:
|
||||
|
||||
% git clone https://git.xiph.org/opus.git
|
||||
% cd opus
|
||||
|
||||
2) Compiling the source
|
||||
|
||||
% ./autogen.sh
|
||||
% ./configure
|
||||
% make
|
||||
|
||||
3) Install the codec libraries (optional)
|
||||
|
||||
% sudo make install
|
||||
|
||||
Once you have compiled the codec, there will be a opus_demo executable
|
||||
in the top directory.
|
||||
|
||||
Usage: opus_demo [-e] <application> <sampling rate (Hz)> <channels (1/2)>
|
||||
<bits per second> [options] <input> <output>
|
||||
opus_demo -d <sampling rate (Hz)> <channels (1/2)> [options]
|
||||
<input> <output>
|
||||
|
||||
mode: voip | audio | restricted-lowdelay
|
||||
options:
|
||||
-e : only runs the encoder (output the bit-stream)
|
||||
-d : only runs the decoder (reads the bit-stream as input)
|
||||
-cbr : enable constant bitrate; default: variable bitrate
|
||||
-cvbr : enable constrained variable bitrate; default:
|
||||
unconstrained
|
||||
-bandwidth <NB|MB|WB|SWB|FB>
|
||||
: audio bandwidth (from narrowband to fullband);
|
||||
default: sampling rate
|
||||
-framesize <2.5|5|10|20|40|60>
|
||||
: frame size in ms; default: 20
|
||||
-max_payload <bytes>
|
||||
: maximum payload size in bytes, default: 1024
|
||||
-complexity <comp>
|
||||
: complexity, 0 (lowest) ... 10 (highest); default: 10
|
||||
-inbandfec : enable SILK inband FEC
|
||||
-forcemono : force mono encoding, even for stereo input
|
||||
-dtx : enable SILK DTX
|
||||
-loss <perc> : simulate packet loss, in percent (0-100); default: 0
|
||||
|
||||
input and output are little-endian signed 16-bit PCM files or opus
|
||||
bitstreams with simple opus_demo proprietary framing.
|
||||
|
||||
== Testing ==
|
||||
|
||||
This package includes a collection of automated unit and system tests
|
||||
which SHOULD be run after compiling the package especially the first
|
||||
time it is run on a new platform.
|
||||
|
||||
To run the integrated tests:
|
||||
|
||||
% make check
|
||||
|
||||
There is also collection of standard test vectors which are not
|
||||
included in this package for size reasons but can be obtained from:
|
||||
https://opus-codec.org/docs/opus_testvectors-rfc8251.tar.gz
|
||||
|
||||
To run compare the code to these test vectors:
|
||||
|
||||
% curl -OL https://opus-codec.org/docs/opus_testvectors-rfc8251.tar.gz
|
||||
% tar -zxf opus_testvectors-rfc8251.tar.gz
|
||||
% ./tests/run_vectors.sh ./ opus_newvectors 48000
|
||||
|
||||
== Portability notes ==
|
||||
|
||||
This implementation uses floating-point by default but can be compiled to
|
||||
use only fixed-point arithmetic by setting --enable-fixed-point (if using
|
||||
autoconf) or by defining the FIXED_POINT macro (if building manually).
|
||||
The fixed point implementation has somewhat lower audio quality and is
|
||||
slower on platforms with fast FPUs, it is normally only used in embedded
|
||||
environments.
|
||||
|
||||
The implementation can be compiled with either a C89 or a C99 compiler.
|
||||
While it does not rely on any _undefined behavior_ as defined by C89 or
|
||||
C99, it relies on common _implementation-defined behavior_ for two's
|
||||
complement architectures:
|
||||
|
||||
o Right shifts of negative values are consistent with two's
|
||||
complement arithmetic, so that a>>b is equivalent to
|
||||
floor(a/(2^b)),
|
||||
|
||||
o For conversion to a signed integer of N bits, the value is reduced
|
||||
modulo 2^N to be within range of the type,
|
||||
|
||||
o The result of integer division of a negative value is truncated
|
||||
towards zero, and
|
||||
|
||||
o The compiler provides a 64-bit integer type (a C99 requirement
|
||||
which is supported by most C89 compilers).
|
||||
182
third_party/opus-1.3.1-stripped/_kiss_fft_guts.h
vendored
182
third_party/opus-1.3.1-stripped/_kiss_fft_guts.h
vendored
@@ -1,182 +0,0 @@
|
||||
/*Copyright (c) 2003-2004, Mark Borgerding
|
||||
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright notice,
|
||||
this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
POSSIBILITY OF SUCH DAMAGE.*/
|
||||
|
||||
#ifndef KISS_FFT_GUTS_H
|
||||
#define KISS_FFT_GUTS_H
|
||||
|
||||
#define MIN(a,b) ((a)<(b) ? (a):(b))
|
||||
#define MAX(a,b) ((a)>(b) ? (a):(b))
|
||||
|
||||
/* kiss_fft.h
|
||||
defines kiss_fft_scalar as either short or a float type
|
||||
and defines
|
||||
typedef struct { kiss_fft_scalar r; kiss_fft_scalar i; }kiss_fft_cpx; */
|
||||
#include "kiss_fft.h"
|
||||
|
||||
/*
|
||||
Explanation of macros dealing with complex math:
|
||||
|
||||
C_MUL(m,a,b) : m = a*b
|
||||
C_FIXDIV( c , div ) : if a fixed point impl., c /= div. noop otherwise
|
||||
C_SUB( res, a,b) : res = a - b
|
||||
C_SUBFROM( res , a) : res -= a
|
||||
C_ADDTO( res , a) : res += a
|
||||
* */
|
||||
#ifdef FIXED_POINT
|
||||
#include "arch.h"
|
||||
|
||||
|
||||
#define SAMP_MAX 2147483647
|
||||
#define TWID_MAX 32767
|
||||
#define TRIG_UPSCALE 1
|
||||
|
||||
#define SAMP_MIN -SAMP_MAX
|
||||
|
||||
|
||||
# define S_MUL(a,b) MULT16_32_Q15(b, a)
|
||||
|
||||
# define C_MUL(m,a,b) \
|
||||
do{ (m).r = SUB32_ovflw(S_MUL((a).r,(b).r) , S_MUL((a).i,(b).i)); \
|
||||
(m).i = ADD32_ovflw(S_MUL((a).r,(b).i) , S_MUL((a).i,(b).r)); }while(0)
|
||||
|
||||
# define C_MULC(m,a,b) \
|
||||
do{ (m).r = ADD32_ovflw(S_MUL((a).r,(b).r) , S_MUL((a).i,(b).i)); \
|
||||
(m).i = SUB32_ovflw(S_MUL((a).i,(b).r) , S_MUL((a).r,(b).i)); }while(0)
|
||||
|
||||
# define C_MULBYSCALAR( c, s ) \
|
||||
do{ (c).r = S_MUL( (c).r , s ) ;\
|
||||
(c).i = S_MUL( (c).i , s ) ; }while(0)
|
||||
|
||||
# define DIVSCALAR(x,k) \
|
||||
(x) = S_MUL( x, (TWID_MAX-((k)>>1))/(k)+1 )
|
||||
|
||||
# define C_FIXDIV(c,div) \
|
||||
do { DIVSCALAR( (c).r , div); \
|
||||
DIVSCALAR( (c).i , div); }while (0)
|
||||
|
||||
#define C_ADD( res, a,b)\
|
||||
do {(res).r=ADD32_ovflw((a).r,(b).r); (res).i=ADD32_ovflw((a).i,(b).i); \
|
||||
}while(0)
|
||||
#define C_SUB( res, a,b)\
|
||||
do {(res).r=SUB32_ovflw((a).r,(b).r); (res).i=SUB32_ovflw((a).i,(b).i); \
|
||||
}while(0)
|
||||
#define C_ADDTO( res , a)\
|
||||
do {(res).r = ADD32_ovflw((res).r, (a).r); (res).i = ADD32_ovflw((res).i,(a).i);\
|
||||
}while(0)
|
||||
|
||||
#define C_SUBFROM( res , a)\
|
||||
do {(res).r = ADD32_ovflw((res).r,(a).r); (res).i = SUB32_ovflw((res).i,(a).i); \
|
||||
}while(0)
|
||||
|
||||
#if defined(OPUS_ARM_INLINE_ASM)
|
||||
#include "arm/kiss_fft_armv4.h"
|
||||
#endif
|
||||
|
||||
#if defined(OPUS_ARM_INLINE_EDSP)
|
||||
#include "arm/kiss_fft_armv5e.h"
|
||||
#endif
|
||||
#if defined(MIPSr1_ASM)
|
||||
#include "mips/kiss_fft_mipsr1.h"
|
||||
#endif
|
||||
|
||||
#else /* not FIXED_POINT*/
|
||||
|
||||
# define S_MUL(a,b) ( (a)*(b) )
|
||||
#define C_MUL(m,a,b) \
|
||||
do{ (m).r = (a).r*(b).r - (a).i*(b).i;\
|
||||
(m).i = (a).r*(b).i + (a).i*(b).r; }while(0)
|
||||
#define C_MULC(m,a,b) \
|
||||
do{ (m).r = (a).r*(b).r + (a).i*(b).i;\
|
||||
(m).i = (a).i*(b).r - (a).r*(b).i; }while(0)
|
||||
|
||||
#define C_MUL4(m,a,b) C_MUL(m,a,b)
|
||||
|
||||
# define C_FIXDIV(c,div) /* NOOP */
|
||||
# define C_MULBYSCALAR( c, s ) \
|
||||
do{ (c).r *= (s);\
|
||||
(c).i *= (s); }while(0)
|
||||
#endif
|
||||
|
||||
#ifndef CHECK_OVERFLOW_OP
|
||||
# define CHECK_OVERFLOW_OP(a,op,b) /* noop */
|
||||
#endif
|
||||
|
||||
#ifndef C_ADD
|
||||
#define C_ADD( res, a,b)\
|
||||
do { \
|
||||
CHECK_OVERFLOW_OP((a).r,+,(b).r)\
|
||||
CHECK_OVERFLOW_OP((a).i,+,(b).i)\
|
||||
(res).r=(a).r+(b).r; (res).i=(a).i+(b).i; \
|
||||
}while(0)
|
||||
#define C_SUB( res, a,b)\
|
||||
do { \
|
||||
CHECK_OVERFLOW_OP((a).r,-,(b).r)\
|
||||
CHECK_OVERFLOW_OP((a).i,-,(b).i)\
|
||||
(res).r=(a).r-(b).r; (res).i=(a).i-(b).i; \
|
||||
}while(0)
|
||||
#define C_ADDTO( res , a)\
|
||||
do { \
|
||||
CHECK_OVERFLOW_OP((res).r,+,(a).r)\
|
||||
CHECK_OVERFLOW_OP((res).i,+,(a).i)\
|
||||
(res).r += (a).r; (res).i += (a).i;\
|
||||
}while(0)
|
||||
|
||||
#define C_SUBFROM( res , a)\
|
||||
do {\
|
||||
CHECK_OVERFLOW_OP((res).r,-,(a).r)\
|
||||
CHECK_OVERFLOW_OP((res).i,-,(a).i)\
|
||||
(res).r -= (a).r; (res).i -= (a).i; \
|
||||
}while(0)
|
||||
#endif /* C_ADD defined */
|
||||
|
||||
#ifdef FIXED_POINT
|
||||
/*# define KISS_FFT_COS(phase) TRIG_UPSCALE*floor(MIN(32767,MAX(-32767,.5+32768 * cos (phase))))
|
||||
# define KISS_FFT_SIN(phase) TRIG_UPSCALE*floor(MIN(32767,MAX(-32767,.5+32768 * sin (phase))))*/
|
||||
# define KISS_FFT_COS(phase) floor(.5+TWID_MAX*cos (phase))
|
||||
# define KISS_FFT_SIN(phase) floor(.5+TWID_MAX*sin (phase))
|
||||
# define HALF_OF(x) ((x)>>1)
|
||||
#elif defined(USE_SIMD)
|
||||
# define KISS_FFT_COS(phase) _mm_set1_ps( cos(phase) )
|
||||
# define KISS_FFT_SIN(phase) _mm_set1_ps( sin(phase) )
|
||||
# define HALF_OF(x) ((x)*_mm_set1_ps(.5f))
|
||||
#else
|
||||
# define KISS_FFT_COS(phase) (kiss_fft_scalar) cos(phase)
|
||||
# define KISS_FFT_SIN(phase) (kiss_fft_scalar) sin(phase)
|
||||
# define HALF_OF(x) ((x)*.5f)
|
||||
#endif
|
||||
|
||||
#define kf_cexp(x,phase) \
|
||||
do{ \
|
||||
(x)->r = KISS_FFT_COS(phase);\
|
||||
(x)->i = KISS_FFT_SIN(phase);\
|
||||
}while(0)
|
||||
|
||||
#define kf_cexp2(x,phase) \
|
||||
do{ \
|
||||
(x)->r = TRIG_UPSCALE*celt_cos_norm((phase));\
|
||||
(x)->i = TRIG_UPSCALE*celt_cos_norm((phase)-32768);\
|
||||
}while(0)
|
||||
|
||||
#endif /* KISS_FFT_GUTS_H */
|
||||
288
third_party/opus-1.3.1-stripped/arch.h
vendored
288
third_party/opus-1.3.1-stripped/arch.h
vendored
@@ -1,288 +0,0 @@
|
||||
/* Copyright (c) 2003-2008 Jean-Marc Valin
|
||||
Copyright (c) 2007-2008 CSIRO
|
||||
Copyright (c) 2007-2009 Xiph.Org Foundation
|
||||
Written by Jean-Marc Valin */
|
||||
/**
|
||||
@file arch.h
|
||||
@brief Various architecture definitions for CELT
|
||||
*/
|
||||
/*
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions
|
||||
are met:
|
||||
|
||||
- Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
|
||||
- Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
|
||||
OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#ifndef ARCH_H
|
||||
#define ARCH_H
|
||||
|
||||
#include "opus_types.h"
|
||||
#include "opus_defines.h"
|
||||
|
||||
# if !defined(__GNUC_PREREQ)
|
||||
# if defined(__GNUC__)&&defined(__GNUC_MINOR__)
|
||||
# define __GNUC_PREREQ(_maj,_min) \
|
||||
((__GNUC__<<16)+__GNUC_MINOR__>=((_maj)<<16)+(_min))
|
||||
# else
|
||||
# define __GNUC_PREREQ(_maj,_min) 0
|
||||
# endif
|
||||
# endif
|
||||
|
||||
#if OPUS_GNUC_PREREQ(3, 0)
|
||||
#define opus_likely(x) (__builtin_expect(!!(x), 1))
|
||||
#define opus_unlikely(x) (__builtin_expect(!!(x), 0))
|
||||
#else
|
||||
#define opus_likely(x) (!!(x))
|
||||
#define opus_unlikely(x) (!!(x))
|
||||
#endif
|
||||
|
||||
#define CELT_SIG_SCALE 32768.f
|
||||
|
||||
#define CELT_FATAL(str) celt_fatal(str, __FILE__, __LINE__);
|
||||
|
||||
#if defined(ENABLE_ASSERTIONS) || defined(ENABLE_HARDENING)
|
||||
#ifdef __GNUC__
|
||||
__attribute__((noreturn))
|
||||
#endif
|
||||
void celt_fatal(const char *str, const char *file, int line);
|
||||
|
||||
#if defined(CELT_C) && !defined(OVERRIDE_celt_fatal)
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#ifdef __GNUC__
|
||||
__attribute__((noreturn))
|
||||
#endif
|
||||
void celt_fatal(const char *str, const char *file, int line)
|
||||
{
|
||||
fprintf (stderr, "Fatal (internal) error in %s, line %d: %s\n", file, line, str);
|
||||
abort();
|
||||
}
|
||||
#endif
|
||||
|
||||
#define celt_assert(cond) {if (!(cond)) {CELT_FATAL("assertion failed: " #cond);}}
|
||||
#define celt_assert2(cond, message) {if (!(cond)) {CELT_FATAL("assertion failed: " #cond "\n" message);}}
|
||||
#define MUST_SUCCEED(call) celt_assert((call) == OPUS_OK)
|
||||
#else
|
||||
#define celt_assert(cond)
|
||||
#define celt_assert2(cond, message)
|
||||
#define MUST_SUCCEED(call) do {if((call) != OPUS_OK) {RESTORE_STACK; return OPUS_INTERNAL_ERROR;} } while (0)
|
||||
#endif
|
||||
|
||||
#if defined(ENABLE_ASSERTIONS)
|
||||
#define celt_sig_assert(cond) {if (!(cond)) {CELT_FATAL("signal assertion failed: " #cond);}}
|
||||
#else
|
||||
#define celt_sig_assert(cond)
|
||||
#endif
|
||||
|
||||
#define IMUL32(a,b) ((a)*(b))
|
||||
|
||||
#define MIN16(a,b) ((a) < (b) ? (a) : (b)) /**< Minimum 16-bit value. */
|
||||
#define MAX16(a,b) ((a) > (b) ? (a) : (b)) /**< Maximum 16-bit value. */
|
||||
#define MIN32(a,b) ((a) < (b) ? (a) : (b)) /**< Minimum 32-bit value. */
|
||||
#define MAX32(a,b) ((a) > (b) ? (a) : (b)) /**< Maximum 32-bit value. */
|
||||
#define IMIN(a,b) ((a) < (b) ? (a) : (b)) /**< Minimum int value. */
|
||||
#define IMAX(a,b) ((a) > (b) ? (a) : (b)) /**< Maximum int value. */
|
||||
#define UADD32(a,b) ((a)+(b))
|
||||
#define USUB32(a,b) ((a)-(b))
|
||||
|
||||
/* Set this if opus_int64 is a native type of the CPU. */
|
||||
/* Assume that all LP64 architectures have fast 64-bit types; also x86_64
|
||||
(which can be ILP32 for x32) and Win64 (which is LLP64). */
|
||||
#if defined(__x86_64__) || defined(__LP64__) || defined(_WIN64)
|
||||
#define OPUS_FAST_INT64 1
|
||||
#else
|
||||
#define OPUS_FAST_INT64 0
|
||||
#endif
|
||||
|
||||
#define PRINT_MIPS(file)
|
||||
|
||||
#ifdef FIXED_POINT
|
||||
|
||||
typedef opus_int16 opus_val16;
|
||||
typedef opus_int32 opus_val32;
|
||||
typedef opus_int64 opus_val64;
|
||||
|
||||
typedef opus_val32 celt_sig;
|
||||
typedef opus_val16 celt_norm;
|
||||
typedef opus_val32 celt_ener;
|
||||
|
||||
#define celt_isnan(x) 0
|
||||
|
||||
#define Q15ONE 32767
|
||||
|
||||
#define SIG_SHIFT 12
|
||||
/* Safe saturation value for 32-bit signals. Should be less than
|
||||
2^31*(1-0.85) to avoid blowing up on DC at deemphasis.*/
|
||||
#define SIG_SAT (300000000)
|
||||
|
||||
#define NORM_SCALING 16384
|
||||
|
||||
#define DB_SHIFT 10
|
||||
|
||||
#define EPSILON 1
|
||||
#define VERY_SMALL 0
|
||||
#define VERY_LARGE16 ((opus_val16)32767)
|
||||
#define Q15_ONE ((opus_val16)32767)
|
||||
|
||||
#define SCALEIN(a) (a)
|
||||
#define SCALEOUT(a) (a)
|
||||
|
||||
#define ABS16(x) ((x) < 0 ? (-(x)) : (x))
|
||||
#define ABS32(x) ((x) < 0 ? (-(x)) : (x))
|
||||
|
||||
static OPUS_INLINE opus_int16 SAT16(opus_int32 x) {
|
||||
return x > 32767 ? 32767 : x < -32768 ? -32768 : (opus_int16)x;
|
||||
}
|
||||
|
||||
#ifdef FIXED_DEBUG
|
||||
#include "fixed_debug.h"
|
||||
#else
|
||||
|
||||
#include "fixed_generic.h"
|
||||
|
||||
#ifdef OPUS_ARM_PRESUME_AARCH64_NEON_INTR
|
||||
#include "arm/fixed_arm64.h"
|
||||
#elif defined (OPUS_ARM_INLINE_EDSP)
|
||||
#include "arm/fixed_armv5e.h"
|
||||
#elif defined (OPUS_ARM_INLINE_ASM)
|
||||
#include "arm/fixed_armv4.h"
|
||||
#elif defined (BFIN_ASM)
|
||||
#include "fixed_bfin.h"
|
||||
#elif defined (TI_C5X_ASM)
|
||||
#include "fixed_c5x.h"
|
||||
#elif defined (TI_C6X_ASM)
|
||||
#include "fixed_c6x.h"
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
#else /* FIXED_POINT */
|
||||
|
||||
typedef float opus_val16;
|
||||
typedef float opus_val32;
|
||||
typedef float opus_val64;
|
||||
|
||||
typedef float celt_sig;
|
||||
typedef float celt_norm;
|
||||
typedef float celt_ener;
|
||||
|
||||
#ifdef FLOAT_APPROX
|
||||
/* This code should reliably detect NaN/inf even when -ffast-math is used.
|
||||
Assumes IEEE 754 format. */
|
||||
static OPUS_INLINE int celt_isnan(float x)
|
||||
{
|
||||
union {float f; opus_uint32 i;} in;
|
||||
in.f = x;
|
||||
return ((in.i>>23)&0xFF)==0xFF && (in.i&0x007FFFFF)!=0;
|
||||
}
|
||||
#else
|
||||
#ifdef __FAST_MATH__
|
||||
#error Cannot build libopus with -ffast-math unless FLOAT_APPROX is defined. This could result in crashes on extreme (e.g. NaN) input
|
||||
#endif
|
||||
#define celt_isnan(x) ((x)!=(x))
|
||||
#endif
|
||||
|
||||
#define Q15ONE 1.0f
|
||||
|
||||
#define NORM_SCALING 1.f
|
||||
|
||||
#define EPSILON 1e-15f
|
||||
#define VERY_SMALL 1e-30f
|
||||
#define VERY_LARGE16 1e15f
|
||||
#define Q15_ONE ((opus_val16)1.f)
|
||||
|
||||
/* This appears to be the same speed as C99's fabsf() but it's more portable. */
|
||||
#define ABS16(x) ((float)fabs(x))
|
||||
#define ABS32(x) ((float)fabs(x))
|
||||
|
||||
#define QCONST16(x,bits) (x)
|
||||
#define QCONST32(x,bits) (x)
|
||||
|
||||
#define NEG16(x) (-(x))
|
||||
#define NEG32(x) (-(x))
|
||||
#define NEG32_ovflw(x) (-(x))
|
||||
#define EXTRACT16(x) (x)
|
||||
#define EXTEND32(x) (x)
|
||||
#define SHR16(a,shift) (a)
|
||||
#define SHL16(a,shift) (a)
|
||||
#define SHR32(a,shift) (a)
|
||||
#define SHL32(a,shift) (a)
|
||||
#define PSHR32(a,shift) (a)
|
||||
#define VSHR32(a,shift) (a)
|
||||
|
||||
#define PSHR(a,shift) (a)
|
||||
#define SHR(a,shift) (a)
|
||||
#define SHL(a,shift) (a)
|
||||
#define SATURATE(x,a) (x)
|
||||
#define SATURATE16(x) (x)
|
||||
|
||||
#define ROUND16(a,shift) (a)
|
||||
#define SROUND16(a,shift) (a)
|
||||
#define HALF16(x) (.5f*(x))
|
||||
#define HALF32(x) (.5f*(x))
|
||||
|
||||
#define ADD16(a,b) ((a)+(b))
|
||||
#define SUB16(a,b) ((a)-(b))
|
||||
#define ADD32(a,b) ((a)+(b))
|
||||
#define SUB32(a,b) ((a)-(b))
|
||||
#define ADD32_ovflw(a,b) ((a)+(b))
|
||||
#define SUB32_ovflw(a,b) ((a)-(b))
|
||||
#define MULT16_16_16(a,b) ((a)*(b))
|
||||
#define MULT16_16(a,b) ((opus_val32)(a)*(opus_val32)(b))
|
||||
#define MAC16_16(c,a,b) ((c)+(opus_val32)(a)*(opus_val32)(b))
|
||||
|
||||
#define MULT16_32_Q15(a,b) ((a)*(b))
|
||||
#define MULT16_32_Q16(a,b) ((a)*(b))
|
||||
|
||||
#define MULT32_32_Q31(a,b) ((a)*(b))
|
||||
|
||||
#define MAC16_32_Q15(c,a,b) ((c)+(a)*(b))
|
||||
#define MAC16_32_Q16(c,a,b) ((c)+(a)*(b))
|
||||
|
||||
#define MULT16_16_Q11_32(a,b) ((a)*(b))
|
||||
#define MULT16_16_Q11(a,b) ((a)*(b))
|
||||
#define MULT16_16_Q13(a,b) ((a)*(b))
|
||||
#define MULT16_16_Q14(a,b) ((a)*(b))
|
||||
#define MULT16_16_Q15(a,b) ((a)*(b))
|
||||
#define MULT16_16_P15(a,b) ((a)*(b))
|
||||
#define MULT16_16_P13(a,b) ((a)*(b))
|
||||
#define MULT16_16_P14(a,b) ((a)*(b))
|
||||
#define MULT16_32_P16(a,b) ((a)*(b))
|
||||
|
||||
#define DIV32_16(a,b) (((opus_val32)(a))/(opus_val16)(b))
|
||||
#define DIV32(a,b) (((opus_val32)(a))/(opus_val32)(b))
|
||||
|
||||
#define SCALEIN(a) ((a)*CELT_SIG_SCALE)
|
||||
#define SCALEOUT(a) ((a)*(1/CELT_SIG_SCALE))
|
||||
|
||||
#define SIG2WORD16(x) (x)
|
||||
|
||||
#endif /* !FIXED_POINT */
|
||||
|
||||
#ifndef GLOBAL_STACK_SIZE
|
||||
#ifdef FIXED_POINT
|
||||
#define GLOBAL_STACK_SIZE 120000
|
||||
#else
|
||||
#define GLOBAL_STACK_SIZE 120000
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#endif /* ARCH_H */
|
||||
353
third_party/opus-1.3.1-stripped/arm/arm2gnu.pl
vendored
353
third_party/opus-1.3.1-stripped/arm/arm2gnu.pl
vendored
@@ -1,353 +0,0 @@
|
||||
#!/usr/bin/perl
|
||||
# Copyright (C) 2002-2013 Xiph.org Foundation
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without
|
||||
# modification, are permitted provided that the following conditions
|
||||
# are met:
|
||||
#
|
||||
# - Redistributions of source code must retain the above copyright
|
||||
# notice, this list of conditions and the following disclaimer.
|
||||
#
|
||||
# - Redistributions in binary form must reproduce the above copyright
|
||||
# notice, this list of conditions and the following disclaimer in the
|
||||
# documentation and/or other materials provided with the distribution.
|
||||
#
|
||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
# ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
|
||||
# OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
# LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
# NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
my $bigend; # little/big endian
|
||||
my $nxstack;
|
||||
my $apple = 0;
|
||||
my $symprefix = "";
|
||||
|
||||
$nxstack = 0;
|
||||
|
||||
eval 'exec /usr/local/bin/perl -S $0 ${1+"$@"}'
|
||||
if $running_under_some_shell;
|
||||
|
||||
while ($ARGV[0] =~ /^-/) {
|
||||
$_ = shift;
|
||||
last if /^--$/;
|
||||
if (/^-n$/) {
|
||||
$nflag++;
|
||||
next;
|
||||
}
|
||||
if (/^--apple$/) {
|
||||
$apple = 1;
|
||||
$symprefix = "_";
|
||||
next;
|
||||
}
|
||||
die "I don't recognize this switch: $_\\n";
|
||||
}
|
||||
$printit++ unless $nflag;
|
||||
|
||||
$\ = "\n"; # automatically add newline on print
|
||||
$n=0;
|
||||
|
||||
$thumb = 0; # ARM mode by default, not Thumb.
|
||||
@proc_stack = ();
|
||||
|
||||
printf (" .syntax unified\n");
|
||||
|
||||
LINE:
|
||||
while (<>) {
|
||||
|
||||
# For ADRLs we need to add a new line after the substituted one.
|
||||
$addPadding = 0;
|
||||
|
||||
# First, we do not dare to touch *anything* inside double quotes, do we?
|
||||
# Second, if you want a dollar character in the string,
|
||||
# insert two of them -- that's how ARM C and assembler treat strings.
|
||||
s/^([A-Za-z_]\w*)[ \t]+DCB[ \t]*\"/$1: .ascii \"/ && do { s/\$\$/\$/g; next };
|
||||
s/\bDCB\b[ \t]*\"/.ascii \"/ && do { s/\$\$/\$/g; next };
|
||||
s/^(\S+)\s+RN\s+(\S+)/$1 .req r$2/ && do { s/\$\$/\$/g; next };
|
||||
# If there's nothing on a line but a comment, don't try to apply any further
|
||||
# substitutions (this is a cheap hack to avoid mucking up the license header)
|
||||
s/^([ \t]*);/$1@/ && do { s/\$\$/\$/g; next };
|
||||
# If substituted -- leave immediately !
|
||||
|
||||
s/@/,:/;
|
||||
s/;/@/;
|
||||
while ( /@.*'/ ) {
|
||||
s/(@.*)'/$1/g;
|
||||
}
|
||||
s/\{FALSE\}/0/g;
|
||||
s/\{TRUE\}/1/g;
|
||||
s/\{(\w\w\w\w+)\}/$1/g;
|
||||
s/\bINCLUDE[ \t]*([^ \t\n]+)/.include \"$1\"/;
|
||||
s/\bGET[ \t]*([^ \t\n]+)/.include \"${ my $x=$1; $x =~ s|\.s|-gnu.S|; \$x }\"/;
|
||||
s/\bIMPORT\b/.extern/;
|
||||
s/\bEXPORT\b\s*/.global $symprefix/;
|
||||
s/^(\s+)\[/$1IF/;
|
||||
s/^(\s+)\|/$1ELSE/;
|
||||
s/^(\s+)\]/$1ENDIF/;
|
||||
s/IF *:DEF:/ .ifdef/;
|
||||
s/IF *:LNOT: *:DEF:/ .ifndef/;
|
||||
s/ELSE/ .else/;
|
||||
s/ENDIF/ .endif/;
|
||||
|
||||
if( /\bIF\b/ ) {
|
||||
s/\bIF\b/ .if/;
|
||||
s/=/==/;
|
||||
}
|
||||
if ( $n == 2) {
|
||||
s/\$/\\/g;
|
||||
}
|
||||
if ($n == 1) {
|
||||
s/\$//g;
|
||||
s/label//g;
|
||||
$n = 2;
|
||||
}
|
||||
if ( /MACRO/ ) {
|
||||
s/MACRO *\n/.macro/;
|
||||
$n=1;
|
||||
}
|
||||
if ( /\bMEND\b/ ) {
|
||||
s/\bMEND\b/.endm/;
|
||||
$n=0;
|
||||
}
|
||||
|
||||
# ".rdata" doesn't work in 'as' version 2.13.2, as it is ".rodata" there.
|
||||
#
|
||||
if ( /\bAREA\b/ ) {
|
||||
my $align;
|
||||
$align = "2";
|
||||
if ( /ALIGN=(\d+)/ ) {
|
||||
$align = $1;
|
||||
}
|
||||
if ( /CODE/ ) {
|
||||
$nxstack = 1;
|
||||
}
|
||||
s/^(.+)CODE(.+)READONLY(.*)/ .text/;
|
||||
s/^(.+)DATA(.+)READONLY(.*)/ .section .rdata/;
|
||||
s/^(.+)\|\|\.data\|\|(.+)/ .data/;
|
||||
s/^(.+)\|\|\.bss\|\|(.+)/ .bss/;
|
||||
s/$/; .p2align $align/;
|
||||
# Enable NEON instructions but don't produce a binary that requires
|
||||
# ARMv7. RVCT does not have equivalent directives, so we just do this
|
||||
# for all CODE areas.
|
||||
if ( /.text/ ) {
|
||||
# Separating .arch, .fpu, etc., by semicolons does not work (gas
|
||||
# thinks the semicolon is part of the arch name, even when there's
|
||||
# whitespace separating them). Sadly this means our line numbers
|
||||
# won't match the original source file (we could use the .line
|
||||
# directive, which is documented to be obsolete, but then gdb will
|
||||
# show the wrong line in the translated source file).
|
||||
s/$/; .arch armv7-a\n .fpu neon\n .object_arch armv4t/ unless ($apple);
|
||||
}
|
||||
}
|
||||
|
||||
s/\|\|\.constdata\$(\d+)\|\|/.L_CONST$1/; # ||.constdata$3||
|
||||
s/\|\|\.bss\$(\d+)\|\|/.L_BSS$1/; # ||.bss$2||
|
||||
s/\|\|\.data\$(\d+)\|\|/.L_DATA$1/; # ||.data$2||
|
||||
s/\|\|([a-zA-Z0-9_]+)\@([a-zA-Z0-9_]+)\|\|/@ $&/;
|
||||
s/^(\s+)\%(\s)/ .space $1/;
|
||||
|
||||
s/\|(.+)\.(\d+)\|/\.$1_$2/; # |L80.123| -> .L80_123
|
||||
s/\bCODE32\b/.code 32/ && do {$thumb = 0};
|
||||
s/\bCODE16\b/.code 16/ && do {$thumb = 1};
|
||||
if (/\bPROC\b/)
|
||||
{
|
||||
my $prefix;
|
||||
my $proc;
|
||||
/^([A-Za-z_\.]\w+)\b/;
|
||||
$proc = $1;
|
||||
$prefix = "";
|
||||
if ($proc)
|
||||
{
|
||||
$prefix = $prefix.sprintf("\t.type\t%s, %%function", $proc) unless ($apple);
|
||||
# Make sure we $prefix isn't empty here (for the $apple case).
|
||||
# We handle mangling the label here, make sure it doesn't match
|
||||
# the label handling below (if $prefix would be empty).
|
||||
$prefix = $prefix."; ";
|
||||
push(@proc_stack, $proc);
|
||||
s/^[A-Za-z_\.]\w+/$symprefix$&:/;
|
||||
}
|
||||
$prefix = $prefix."\t.thumb_func; " if ($thumb);
|
||||
s/\bPROC\b/@ $&/;
|
||||
$_ = $prefix.$_;
|
||||
}
|
||||
s/^(\s*)(S|Q|SH|U|UQ|UH)ASX\b/$1$2ADDSUBX/;
|
||||
s/^(\s*)(S|Q|SH|U|UQ|UH)SAX\b/$1$2SUBADDX/;
|
||||
if (/\bENDP\b/)
|
||||
{
|
||||
my $proc;
|
||||
s/\bENDP\b/@ $&/;
|
||||
$proc = pop(@proc_stack);
|
||||
$_ = "\t.size $proc, .-$proc".$_ if ($proc && !$apple);
|
||||
}
|
||||
s/\bSUBT\b/@ $&/;
|
||||
s/\bDATA\b/@ $&/; # DATA directive is deprecated -- Asm guide, p.7-25
|
||||
s/\bKEEP\b/@ $&/;
|
||||
s/\bEXPORTAS\b/@ $&/;
|
||||
s/\|\|(.)+\bEQU\b/@ $&/;
|
||||
s/\|\|([\w\$]+)\|\|/$1/;
|
||||
s/\bENTRY\b/@ $&/;
|
||||
s/\bASSERT\b/@ $&/;
|
||||
s/\bGBLL\b/@ $&/;
|
||||
s/\bGBLA\b/@ $&/;
|
||||
s/^\W+OPT\b/@ $&/;
|
||||
s/:OR:/|/g;
|
||||
s/:SHL:/<</g;
|
||||
s/:SHR:/>>/g;
|
||||
s/:AND:/&/g;
|
||||
s/:LAND:/&&/g;
|
||||
s/CPSR/cpsr/;
|
||||
s/SPSR/spsr/;
|
||||
s/ALIGN$/.balign 4/;
|
||||
s/ALIGN\s+([0-9x]+)$/.balign $1/;
|
||||
s/psr_cxsf/psr_all/;
|
||||
s/LTORG/.ltorg/;
|
||||
s/^([A-Za-z_]\w*)[ \t]+EQU/ .set $1,/;
|
||||
s/^([A-Za-z_]\w*)[ \t]+SETL/ .set $1,/;
|
||||
s/^([A-Za-z_]\w*)[ \t]+SETA/ .set $1,/;
|
||||
s/^([A-Za-z_]\w*)[ \t]+\*/ .set $1,/;
|
||||
|
||||
# {PC} + 0xdeadfeed --> . + 0xdeadfeed
|
||||
s/\{PC\} \+/ \. +/;
|
||||
|
||||
# Single hex constant on the line !
|
||||
#
|
||||
# >>> NOTE <<<
|
||||
# Double-precision floats in gcc are always mixed-endian, which means
|
||||
# bytes in two words are little-endian, but words are big-endian.
|
||||
# So, 0x0000deadfeed0000 would be stored as 0x0000dead at low address
|
||||
# and 0xfeed0000 at high address.
|
||||
#
|
||||
s/\bDCFD\b[ \t]+0x([a-fA-F0-9]{8})([a-fA-F0-9]{8})/.long 0x$1, 0x$2/;
|
||||
# Only decimal constants on the line, no hex !
|
||||
s/\bDCFD\b[ \t]+([0-9\.\-]+)/.double $1/;
|
||||
|
||||
# Single hex constant on the line !
|
||||
# s/\bDCFS\b[ \t]+0x([a-f0-9]{8})([a-f0-9]{8})/.long 0x$1, 0x$2/;
|
||||
# Only decimal constants on the line, no hex !
|
||||
# s/\bDCFS\b[ \t]+([0-9\.\-]+)/.double $1/;
|
||||
s/\bDCFS[ \t]+0x/.word 0x/;
|
||||
s/\bDCFS\b/.float/;
|
||||
|
||||
s/^([A-Za-z_]\w*)[ \t]+DCD/$1 .word/;
|
||||
s/\bDCD\b/.word/;
|
||||
s/^([A-Za-z_]\w*)[ \t]+DCW/$1 .short/;
|
||||
s/\bDCW\b/.short/;
|
||||
s/^([A-Za-z_]\w*)[ \t]+DCB/$1 .byte/;
|
||||
s/\bDCB\b/.byte/;
|
||||
s/^([A-Za-z_]\w*)[ \t]+\%/.comm $1,/;
|
||||
s/^[A-Za-z_\.]\w+/$&:/;
|
||||
s/^(\d+)/$1:/;
|
||||
s/\%(\d+)/$1b_or_f/;
|
||||
s/\%[Bb](\d+)/$1b/;
|
||||
s/\%[Ff](\d+)/$1f/;
|
||||
s/\%[Ff][Tt](\d+)/$1f/;
|
||||
s/&([\dA-Fa-f]+)/0x$1/;
|
||||
if ( /\b2_[01]+\b/ ) {
|
||||
s/\b2_([01]+)\b/conv$1&&&&/g;
|
||||
while ( /[01][01][01][01]&&&&/ ) {
|
||||
s/0000&&&&/&&&&0/g;
|
||||
s/0001&&&&/&&&&1/g;
|
||||
s/0010&&&&/&&&&2/g;
|
||||
s/0011&&&&/&&&&3/g;
|
||||
s/0100&&&&/&&&&4/g;
|
||||
s/0101&&&&/&&&&5/g;
|
||||
s/0110&&&&/&&&&6/g;
|
||||
s/0111&&&&/&&&&7/g;
|
||||
s/1000&&&&/&&&&8/g;
|
||||
s/1001&&&&/&&&&9/g;
|
||||
s/1010&&&&/&&&&A/g;
|
||||
s/1011&&&&/&&&&B/g;
|
||||
s/1100&&&&/&&&&C/g;
|
||||
s/1101&&&&/&&&&D/g;
|
||||
s/1110&&&&/&&&&E/g;
|
||||
s/1111&&&&/&&&&F/g;
|
||||
}
|
||||
s/000&&&&/&&&&0/g;
|
||||
s/001&&&&/&&&&1/g;
|
||||
s/010&&&&/&&&&2/g;
|
||||
s/011&&&&/&&&&3/g;
|
||||
s/100&&&&/&&&&4/g;
|
||||
s/101&&&&/&&&&5/g;
|
||||
s/110&&&&/&&&&6/g;
|
||||
s/111&&&&/&&&&7/g;
|
||||
s/00&&&&/&&&&0/g;
|
||||
s/01&&&&/&&&&1/g;
|
||||
s/10&&&&/&&&&2/g;
|
||||
s/11&&&&/&&&&3/g;
|
||||
s/0&&&&/&&&&0/g;
|
||||
s/1&&&&/&&&&1/g;
|
||||
s/conv&&&&/0x/g;
|
||||
}
|
||||
|
||||
if ( /commandline/)
|
||||
{
|
||||
if( /-bigend/)
|
||||
{
|
||||
$bigend=1;
|
||||
}
|
||||
}
|
||||
|
||||
if ( /\bDCDU\b/ )
|
||||
{
|
||||
my $cmd=$_;
|
||||
my $value;
|
||||
my $prefix;
|
||||
my $w1;
|
||||
my $w2;
|
||||
my $w3;
|
||||
my $w4;
|
||||
|
||||
s/\s+DCDU\b/@ $&/;
|
||||
|
||||
$cmd =~ /\bDCDU\b\s+0x(\d+)/;
|
||||
$value = $1;
|
||||
$value =~ /(\w\w)(\w\w)(\w\w)(\w\w)/;
|
||||
$w1 = $1;
|
||||
$w2 = $2;
|
||||
$w3 = $3;
|
||||
$w4 = $4;
|
||||
|
||||
if( $bigend ne "")
|
||||
{
|
||||
# big endian
|
||||
$prefix = "\t.byte\t0x".$w1.";".
|
||||
"\t.byte\t0x".$w2.";".
|
||||
"\t.byte\t0x".$w3.";".
|
||||
"\t.byte\t0x".$w4."; ";
|
||||
}
|
||||
else
|
||||
{
|
||||
# little endian
|
||||
$prefix = "\t.byte\t0x".$w4.";".
|
||||
"\t.byte\t0x".$w3.";".
|
||||
"\t.byte\t0x".$w2.";".
|
||||
"\t.byte\t0x".$w1."; ";
|
||||
}
|
||||
$_=$prefix.$_;
|
||||
}
|
||||
|
||||
if ( /\badrl\b/i )
|
||||
{
|
||||
s/\badrl\s+(\w+)\s*,\s*(\w+)/ldr $1,=$2/i;
|
||||
$addPadding = 1;
|
||||
}
|
||||
s/\bEND\b/@ END/;
|
||||
} continue {
|
||||
printf ("%s", $_) if $printit;
|
||||
if ($addPadding != 0)
|
||||
{
|
||||
printf (" mov r0,r0\n");
|
||||
$addPadding = 0;
|
||||
}
|
||||
}
|
||||
#If we had a code section, mark that this object doesn't need an executable
|
||||
# stack.
|
||||
if ($nxstack && !$apple) {
|
||||
printf (" .section\t.note.GNU-stack,\"\",\%\%progbits\n");
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user