diff --git a/.github/workflows/cmake.yml b/.github/workflows/cmake.yml
index 80384621..c7d32cae 100644
--- a/.github/workflows/cmake.yml
+++ b/.github/workflows/cmake.yml
@@ -37,6 +37,12 @@ jobs:
- name: Install Abseil-cpp
run: sudo apt install libabsl-dev
+ - name: Install Boost and Boost Python
+ run: sudo apt install libboost-all-dev libboost-python-dev
+
+ - name: Install CPython headers
+ run: sudo apt install python3-dev libpython3-dev
+
- name: Configure CMake
# Configure CMake in a 'build' subdirectory. `CMAKE_BUILD_TYPE` is only required if you are using a single-configuration generator such as make.
# See https://cmake.org/cmake/help/latest/variable/CMAKE_BUILD_TYPE.html?highlight=cmake_build_type
diff --git a/.gitignore b/.gitignore
index a22892fc..b52d20e7 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,6 +1,9 @@
+.favorites.json
build/
.cache/
.vscode/
+disasm/
+src/etc
src/lib/SDL2
src/lib/cmake
src/lib/GL
@@ -17,3 +20,13 @@ src/lib/libSDL2main.a
checks.json
assets/lib/libasar.dll
cmake/yaze.plist.in
+etc/
+latex/
+html/
+docs/zarby_algos.md
+docs/overworld-expansion.md
+assets/asm/EditorCore.asm
+src/app/emu/cpu/internal/old_cpu.cc
+build-windows
+src/lib/libpng
+src/lib/zlib
\ No newline at end of file
diff --git a/CMakeLists.txt b/CMakeLists.txt
index d3428b8d..eea47774 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -1,11 +1,23 @@
-# CMake Specifications
-cmake_minimum_required(VERSION 3.10)
-
# Yet Another Zelda3 Editor
# by scawful
-project(yaze VERSION 0.10)
+cmake_minimum_required(VERSION 3.10)
+project(yaze VERSION 0.2.0
+ DESCRIPTION "Yet Another Zelda3 Editor"
+ LANGUAGES CXX)
-# C++ Standard Specifications
+# Build Flags
+set(YAZE_BUILD_APP ON)
+set(YAZE_BUILD_LIB ON)
+set(YAZE_BUILD_EMU ON)
+set(YAZE_BUILD_Z3ED ON)
+set(YAZE_BUILD_PYTHON OFF)
+set(YAZE_BUILD_TESTS ON)
+set(YAZE_INSTALL_LIB OFF)
+
+# libpng features in bitmap.cc
+add_definitions("-DYAZE_LIB_PNG=1")
+
+# C++ Standard and CMake Specifications
set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib)
@@ -14,21 +26,24 @@ set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin)
set(CMAKE_POSITION_INDEPENDENT_CODE ON)
set(CMAKE_MODULE_PATH "${PROJECT_SOURCE_DIR}/cmake" ${CMAKE_MODULE_PATH})
set(BUILD_SHARED_LIBS OFF)
-set(CMAKE_FIND_FRAMEWORK LAST)
+set(CMAKE_FIND_FRAMEWORK LAST)
+set(CMAKE_SHARED_MODULE_PREFIX "")
+set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Dlinux -Dstricmp=strcasecmp")
+if (MACOS)
+ set(CMAKE_INSTALL_PREFIX /usr/local)
+endif()
# Abseil Standard Specifications
include(cmake/absl.cmake)
-# Video Libraries
-find_package(PNG REQUIRED)
+# SDL2 and PNG
include(cmake/sdl2.cmake)
-# Asar
-add_subdirectory(src/lib/asar/src)
+# Asar
include(cmake/asar.cmake)
# ImGui
include(cmake/imgui.cmake)
# Project Files
-add_subdirectory(src)
\ No newline at end of file
+add_subdirectory(src)
diff --git a/Doxyfile b/Doxyfile
index 22d0cd5a..0dfa6199 100644
--- a/Doxyfile
+++ b/Doxyfile
@@ -48,7 +48,7 @@ PROJECT_NAME = "yaze"
# could be handy for archiving the generated documentation or if some version
# control system is used.
-PROJECT_NUMBER =
+PROJECT_NUMBER = "0.2.0"
# Using the PROJECT_BRIEF tag one can provide an optional one line description
# for a project that appears at the top of each page and should give viewer a
@@ -61,20 +61,20 @@ PROJECT_BRIEF = Link to the Past ROM Editor
# pixels and the maximum width should not exceed 200 pixels. Doxygen will copy
# the logo to the output directory.
-PROJECT_LOGO =
+PROJECT_LOGO = "src/win32/yaze.ico"
# With the PROJECT_ICON tag one can specify an icon that is included in the tabs
# when the HTML document is shown. Doxygen will copy the logo to the output
# directory.
-PROJECT_ICON =
+PROJECT_ICON = "src/win32/yaze.ico"
# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) path
# into which the generated documentation will be written. If a relative path is
# entered, it will be relative to the location where doxygen was started. If
# left blank the current directory will be used.
-OUTPUT_DIRECTORY =
+OUTPUT_DIRECTORY =
# If the CREATE_SUBDIRS tag is set to YES then doxygen will create up to 4096
# sub-directories (in 2 levels) under the output directory of each output format
@@ -1168,7 +1168,7 @@ FILTER_SOURCE_PATTERNS =
# (index.html). This can be useful if you have a project on for instance GitHub
# and want to reuse the introduction page also for the doxygen output.
-USE_MDFILE_AS_MAINPAGE =
+USE_MDFILE_AS_MAINPAGE = getting-started.md
# The Fortran standard specifies that for fixed formatted Fortran code all
# characters from position 72 are to be considered as comment. A common
@@ -1190,7 +1190,7 @@ FORTRAN_COMMENT_AFTER = 72
# also VERBATIM_HEADERS is set to NO.
# The default value is: NO.
-SOURCE_BROWSER = NO
+SOURCE_BROWSER = YES
# Setting the INLINE_SOURCES tag to YES will include the body of functions,
# multi-line macros, enums or list initialized variables directly into the
@@ -1402,7 +1402,7 @@ HTML_COLORSTYLE = AUTO_LIGHT
# Minimum value: 0, maximum value: 359, default value: 220.
# This tag requires that the tag GENERATE_HTML is set to YES.
-HTML_COLORSTYLE_HUE = 220
+HTML_COLORSTYLE_HUE = 280
# The HTML_COLORSTYLE_SAT tag controls the purity (or saturation) of the colors
# in the HTML output. For a value of 0 the output will use gray-scales only. A
@@ -2798,7 +2798,7 @@ PLANTUML_INCLUDE_PATH =
# Minimum value: 0, maximum value: 10000, default value: 50.
# This tag requires that the tag HAVE_DOT is set to YES.
-DOT_GRAPH_MAX_NODES = 50
+DOT_GRAPH_MAX_NODES = 25
# The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the graphs
# generated by dot. A depth value of 3 means that only nodes reachable from the
@@ -2810,7 +2810,7 @@ DOT_GRAPH_MAX_NODES = 50
# Minimum value: 0, maximum value: 1000, default value: 0.
# This tag requires that the tag HAVE_DOT is set to YES.
-MAX_DOT_GRAPH_DEPTH = 0
+MAX_DOT_GRAPH_DEPTH = 2
# Set the DOT_MULTI_TARGETS tag to YES to allow dot to generate multiple output
# files in one run (i.e. multiple -o and -T options on the command line). This
diff --git a/LICENSE b/LICENSE
index 9a09e586..435fc725 100644
--- a/LICENSE
+++ b/LICENSE
@@ -1,4 +1,4 @@
-Copyright (C) 2022 Justin Scofield
+Copyright (C) 2024 Justin Scofield
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -13,3 +13,7 @@ GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see .
+Dependencies:
+- SDL2
+- ImGui
+- Abseil
\ No newline at end of file
diff --git a/README.md b/README.md
index ed1eae11..29c4f795 100644
--- a/README.md
+++ b/README.md
@@ -1,12 +1,14 @@
# Yet Another Zelda3 Editor
-- Platform: Windows, macOS, GNU/Linux
-- Dependencies: SDL2, ImGui
+- Platform: Windows, macOS, iOS, GNU/Linux
+- Dependencies: SDL2, ImGui, abseil-cpp
## Description
General purpose editor for The Legend of Zelda: A Link to the Past for the Super Nintendo.
+Provides bindings in C and Python for building custom tools and utilities.
+
Takes heavy inspiration from ALTTP community efforts such as [Hyrule Magic](https://www.romhacking.net/utilities/200/) and [ZScream](https://github.com/Zarby89/ZScreamDungeon)
Building and installation
@@ -14,23 +16,27 @@ Building and installation
[CMake](http://www.cmake.org "CMake") is required to build yaze
1. Clone the repository
+2. Create the build directory and configuration
+3. Build and run the application
+4. (Optional) Run the tests
```
git clone --recurse-submodules https://github.com/scawful/yaze.git
-```
-
-2. Create the build directory and configuration
-
-```
cmake -S . -B build
-```
-
-3. Build and run.
-
-```
cmake --build build
```
+By default this will build all targets.
+
+- **yaze**: Editor Application
+- **yaze_c**: C Library
+- **yaze_emu**: SNES Emulator
+- **yaze_py**: Python Module
+- **yaze_test**: Unit Tests
+- **z3ed**: Command Line Interface
+
+Dependencies are included as submodules and will be built automatically. For those who want to reduce compile times, consider installing the dependencies on your system. See [build-instructions.md](docs/build-instructions.md) for more information.
+
## Documentation
- For users, please refer to [getting_started.md](docs/getting-started.md) for instructions on how to use yaze.
diff --git a/assets/asm/ZSCustomOverworld.asm b/assets/asm/ZSCustomOverworld.asm
new file mode 100644
index 00000000..fd305071
--- /dev/null
+++ b/assets/asm/ZSCustomOverworld.asm
@@ -0,0 +1,3563 @@
+; ==============================================================================
+; ZScream Custom Overworld ASM
+; ==============================================================================
+; ==============================================================================
+; Non-Expanded Space
+; ==============================================================================
+
+pushpc
+
+AnimatedTileGFXSet = $0FC0
+TransGFXModuleIndex = $0CF3
+
+Sound_LoadLightWorldSongBank = $808913
+DecompOwAnimatedTiles = $80D394
+GetAnimatedSpriteTile = $80D4DB
+GetAnimatedSpriteTile_variable = $80D4ED
+LoadTransAuxGFX_sprite_continue = $00D706
+Do3To4High16Bit = $80DF4F
+Do3To4Low16Bit = $00DFB8
+InitTilesets = $80E19B
+CopyFontToVram = $80E556
+Decomp_bg_variable = $00E78F
+
+DeleteCertainAncillaeStopDashing = $828B0C
+Overworld_FinishTransGfx_firstHalf_Retrun = $02ABC5
+Overworld_LoadSubscreenAndSilenceSFX1 = $82AF19
+Dungeon_LoadPalettes_cacheSettings = $82C65F
+LoadSubscreenOverlay = $82FD0D
+
+Link_ItemReset_FromOverworldThings = $87B107
+
+Tagalong_Init = $899EFC
+Sprite_ReinitWarpVortex = $89AF89
+Sprite_ResetAll = $89C44E
+Sprite_OverworldReloadAll = $89C499
+
+Overworld_SetFixedColorAndScroll = $8BFE70
+
+Overworld_LoadPalettes = $8ED5A8
+Palette_SetOwBgColor_Long = $8ED618
+LoadGearPalettes_bunny = $8ED6DD
+
+Palette_SpriteAux3 = $9BEC77
+Palette_MainSpr = $9BEC9E
+Palette_SpriteAux1 = $9BECC5
+Palette_SpriteAux2 = $9BECE4
+Palette_Sword = $9BED03
+Palette_Shield = $9BED29
+Palette_MiscSpr = $9BED6E
+Palette_ArmorAndGloves = $9BEDF9
+Palette_Hud = $9BEE52
+Palette_OverworldBgMain = $9BEEC7
+
+; ==============================================================================
+; Fixing old hooks:
+; ==============================================================================
+
+; Loads the transparent color under some load conditions.
+org $0BFEB6
+ STA.l $7EC500
+
+; Main Palette loading routine.
+org $0ED5E7
+ JSL $9BEEA8 ; Palette_OverworldBgAux3
+
+; After leaving special areas like Zora's and the Master Sword area.
+org $02E94A
+ JSL $8ED5A8 ; Overworld_LoadPalettes
+
+; ==============================================================================
+; Expanded Space
+; ==============================================================================
+
+; Reserved ZS space.
+; Avoid moving this at all costs. If you do, you will have to change where ZS
+; saves this data as well and previous data will be lost or corrupted.
+org $288000 ; $140000
+Pool:
+{
+ .BGColorTable ; $140000
+ ; Valid values:
+ ; 555 color value $0000 to $7FFF.
+
+ ; LW
+ ;dw $0000, $0000, $0000, $0000, $0000, $0000, $0000, $0000
+ ;dw $0000, $0000, $0000, $0000, $0000, $0000, $0000, $0000
+ ;dw $0000, $0000, $0000, $0000, $0000, $0000, $0000, $0000
+ ;dw $0000, $0000, $0000, $0000, $0000, $0000, $0000, $0000
+ ;dw $0000, $0000, $0000, $0000, $0000, $0000, $0000, $0000
+ ;dw $0000, $0000, $0000, $0000, $0000, $0000, $0000, $0000
+ ;dw $0000, $0000, $0000, $0000, $0000, $0000, $0000, $0000
+ ;dw $0000, $0000, $0000, $0000, $0000, $0000, $0000, $0000
+ ; DW
+ ;dw $0000, $0000, $0000, $0000, $0000, $0000, $0000, $0000
+ ;dw $0000, $0000, $0000, $0000, $0000, $0000, $0000, $0000
+ ;dw $0000, $0000, $0000, $0000, $0000, $0000, $0000, $0000
+ ;dw $0000, $0000, $0000, $0000, $0000, $0000, $0000, $0000
+ ;dw $0000, $0000, $0000, $0000, $0000, $0000, $0000, $0000
+ ;dw $0000, $0000, $0000, $0000, $0000, $0000, $0000, $0000
+ ;dw $0000, $0000, $0000, $0000, $0000, $0000, $0000, $0000
+ ;dw $0000, $0000, $0000, $0000, $0000, $0000, $0000, $0000
+ ; SW
+ ;dw $0000, $0000, $0000, $0000, $0000, $0000, $0000, $0000
+ ;dw $0000, $0000, $0000, $0000, $0000, $0000, $0000, $0000
+ ;dw $0000, $0000, $0000, $0000, $0000, $0000, $0000, $0000
+ ;dw $0000, $0000, $0000, $0000, $0000, $0000, $0000, $0000
+ warnpc $288140
+
+ org $288140 ; $140140
+ .EnableTable ; 0x20
+ ; Valid values:
+ ; $00 - Disabled
+ ; $FF - Enabled
+
+ org $288140 ; $140140
+ .EnableBGColor ; 0x01
+ db $01
+
+ org $288141 ; $140141
+ .EnableMainPalette ; 0x01
+ db $01
+
+ org $288142 ; $140142
+ .EnableMosaic ; 0x01 Unused for now.
+ db $01
+
+ ; When non 0 this will allow animated tiles to be updated between OW
+ ; transitions. Default is $FF.
+ org $288143 ; $140143
+ .EnableAnimated ; 0x01
+ db $01
+
+ ; When non 0 this will allow Subscreen Overlays to be updated between OW
+ ; transitions. Default is $FF.
+ org $288144 ; $140144
+ .EnableSubScreenOverlay ; 0x01
+ db $01
+
+ ; This is a reserved value that ZS will write to when it has applied the
+ ; ASM. That way the next time ZS loads the ROM it knows to read the custom
+ ; values instead of using the default ones. The current version is 02.
+ org $288145 ; $140145
+ .ZSAppliedASM ; 0x01
+ db $02
+
+ ; When non 0 this will cause rain to appear on all areas in the beginning
+ ; phase. Default is $FF.
+ org $288146 ; $140146
+ .EnableBeginningRain ; 0x01
+ db $FF
+
+ ; When non 0 this will disable the ambiant sound that plays in the mire
+ ; area after the event is triggered. Default is $FF.
+ org $288147 ; $140147
+ .EnableRainMireEvent ; 0x01
+ db $FF
+
+ ; When non 0 this will make the game reload all gfx in between OW
+ ; transitions. Default is $FF.
+ org $288148 ; $140143
+ .EnableTransitionGFXGroupLoad ; 0x01
+ db $01
+
+ ; The bridge color is different from the Master Sword area so we are going to
+ ; hard code it here for now.
+ org $288149 ; $140149
+ .BGColorTable_Bridge ; 0x02
+ dw $2669 ; Defualt vanilla LW green.
+
+ ; The rest of these are extra bytes that can be used for anything else
+ ; later on.
+ ;db $00, $00, $00, $00, $00, $00, $00, $00
+ ;db $00, $00, $00, $00, $00, $00, $00, $00
+ ;db $00, $00, $00, $00, $00
+ warnpc $288160
+
+ org $288160 ; $140160
+ .MainPaletteTable ; 0xA0
+ ; Valid values:
+ ; Main overworld palette index $00 to $05.
+ ; $00 is the normal light world palette.
+ ; $01 is the normal dark world palette.
+ ; $02 is the normal light world death mountain palette.
+ ; $03 is the normal dark world death mountain palette.
+ ; $04 is the Triforce room palette.
+ ; $05 is the title screen palette?
+
+ ; LW
+ ;db $00, $00, $00, $02, $00, $20, $00, $20
+ ;db $00, $00, $00, $00, $00, $00, $00, $00
+ ;db $00, $00, $00, $00, $00, $00, $00, $00
+ ;db $00, $00, $00, $00, $00, $00, $00, $00
+ ;db $00, $00, $00, $00, $00, $00, $00, $00
+ ;db $00, $00, $00, $00, $00, $00, $00, $00
+ ;db $00, $00, $00, $00, $00, $00, $00, $00
+ ;db $00, $00, $00, $00, $00, $00, $00, $00
+ ; DW
+ ;db $01, $01, $01, $03, $01, $03, $01, $03
+ ;db $01, $01, $01, $01, $01, $01, $01, $01
+ ;db $01, $01, $01, $01, $01, $01, $01, $01
+ ;db $01, $01, $01, $01, $01, $01, $01, $01
+ ;db $01, $01, $01, $01, $01, $01, $01, $01
+ ;db $01, $01, $01, $01, $01, $01, $01, $01
+ ;db $01, $01, $01, $01, $01, $01, $01, $01
+ ;db $01, $01, $01, $01, $01, $01, $01, $01
+ ; SW
+ ;db $00, $00, $00, $00, $00, $00, $00, $00
+ ;db $04, $00, $00, $00, $00, $00, $00, $00
+ ;db $00, $00, $00, $00, $00, $00, $00, $00
+ ;db $00, $00, $00, $00, $00, $00, $00, $00
+ warnpc $288200
+
+ org $288200 ; $140200
+ .MosaicTable ; 0xA0
+ ; Valid values:
+ ; $01 to enable mosaic, $00 to disable.
+
+ ; LW
+ ;db $01, $00, $00, $00, $00, $00, $00, $00
+ ;db $00, $00, $00, $00, $00, $00, $00, $00
+ ;db $00, $00, $00, $00, $00, $00, $00, $00
+ ;db $00, $00, $00, $00, $00, $00, $00, $00
+ ;db $00, $00, $00, $00, $00, $00, $00, $00
+ ;db $00, $00, $00, $00, $00, $00, $00, $00
+ ;db $00, $00, $00, $00, $00, $00, $00, $00
+ ;db $00, $00, $00, $00, $00, $00, $00, $00
+ ; DW
+ ;db $01, $00, $00, $00, $00, $00, $00, $00
+ ;db $00, $00, $00, $00, $00, $00, $00, $00
+ ;db $00, $00, $00, $00, $00, $00, $00, $00
+ ;db $00, $00, $00, $00, $00, $00, $00, $00
+ ;db $00, $00, $00, $00, $00, $00, $00, $00
+ ;db $00, $00, $00, $00, $00, $00, $00, $00
+ ;db $00, $00, $00, $00, $00, $00, $00, $00
+ ;db $00, $00, $00, $00, $00, $00, $00, $00
+ ; SW
+ ;db $01, $01, $00, $00, $00, $00, $00, $00
+ ;db $01, $00, $00, $00, $00, $00, $00, $00
+ ;db $00, $00, $00, $00, $00, $00, $00, $00
+ ;db $00, $00, $00, $00, $00, $00, $00, $00
+ warnpc $2882A0
+
+ ; Not the same as OWGFXGroupTable_sheet7. The game uses a combination of $59
+ ; and $5B to create the sheet in sheet #7. This is done by first transfering
+ ; all the gfx that is needed for the bottom half of the sheet (the door
+ ; frames for example) which is different depending on whether we are in the
+ ; LW or DW. It then loads the actual animated tile frames into a buffer
+ ; where it can transfer over from durring NMI based on whether we are on
+ ; Death Mountain or not (LW or DW). This table is to control the latter.
+ org $2882A0 ; $1402A0
+ .AnimatedTable ; 0xA0
+ ; Valid values:
+ ; GFX index $00 to $FF.
+ ; In vanilla, $59 are the DW door frames and clouds and $5B are the Lw door
+ ; frames and the regular water tiles.
+
+ ; LW
+ ;db $5B, $5B, $5B, $59, $5B, $59, $5B, $59
+ ;db $5B, $5B, $5B, $5B, $5B, $5B, $5B, $5B
+ ;db $5B, $5B, $5B, $5B, $5B, $5B, $5B, $5B
+ ;db $5B, $5B, $5B, $5B, $5B, $5B, $5B, $5B
+ ;db $5B, $5B, $5B, $5B, $5B, $5B, $5B, $5B
+ ;db $5B, $5B, $5B, $5B, $5B, $5B, $5B, $5B
+ ;db $5B, $5B, $5B, $5B, $5B, $5B, $5B, $5B
+ ;db $5B, $5B, $5B, $5B, $5B, $5B, $5B, $5B
+ ; DW
+ ;db $5B, $5B, $5B, $59, $5B, $59, $5B, $59
+ ;db $5B, $5B, $5B, $5B, $5B, $5B, $5B, $5B
+ ;db $5B, $5B, $5B, $5B, $5B, $5B, $5B, $5B
+ ;db $5B, $5B, $5B, $5B, $5B, $5B, $5B, $5B
+ ;db $5B, $5B, $5B, $5B, $5B, $5B, $5B, $5B
+ ;db $5B, $5B, $5B, $5B, $5B, $5B, $5B, $5B
+ ;db $5B, $5B, $5B, $5B, $5B, $5B, $5B, $5B
+ ;db $5B, $5B, $5B, $5B, $5B, $5B, $5B, $5B
+ ; SW
+ ;db $5B, $5B, $5B, $5B, $5B, $5B, $5B, $5B
+ ;db $5B, $5B, $5B, $5B, $5B, $5B, $5B, $5B
+ ;db $5B, $5B, $5B, $5B, $5B, $5B, $5B, $5B
+ ;db $5B, $5B, $5B, $5B, $5B, $5B, $5B, $5B
+ warnpc $288340
+
+ org $288340 ; $140340
+ .OverlayTable ; 0x140
+ ; Valid values:
+ ; Can be any value $00 to $FF but is stored as 2 bytes instead of one to
+ ; help the code out below. $FF is for no overlay area. Hopefully no crazy
+ ; person decides to expand their overworld to $FF areas.
+
+ ; $0093 is the triforce room curtain overlay.
+ ; $0094 is the under the bridge overlay.
+ ; $0095 is the sky background overlay.
+ ; $0096 is the pyramid background overlay.
+ ; $0097 is the first fog overlay.
+
+ ; $009C is the lava background overlay.
+ ; $009D is the second fog overlay.
+ ; $009E is the tree canopy overlay.
+ ; $009F is the rain overlay.
+
+ ; LW
+ ;dw $009D, $00FF, $00FF, $0095, $00FF, $0095, $00FF, $0095
+ ;dw $00FF, $00FF, $00FF, $00FF, $00FF, $00FF, $00FF, $00FF
+ ;dw $00FF, $00FF, $00FF, $00FF, $00FF, $00FF, $00FF, $00FF
+ ;dw $00FF, $00FF, $00FF, $00FF, $00FF, $00FF, $00FF, $00FF
+ ;dw $00FF, $00FF, $00FF, $00FF, $00FF, $00FF, $00FF, $00FF
+ ;dw $00FF, $00FF, $00FF, $00FF, $00FF, $00FF, $00FF, $00FF
+ ;dw $00FF, $00FF, $00FF, $00FF, $00FF, $00FF, $00FF, $00FF
+ ;dw $00FF, $00FF, $00FF, $00FF, $00FF, $00FF, $00FF, $00FF
+ ; DW
+ ;dw $009D, $00FF, $00FF, $009C, $00FF, $009C, $00FF, $009C
+ ;dw $00FF, $00FF, $00FF, $00FF, $00FF, $00FF, $00FF, $00FF
+ ;dw $00FF, $00FF, $00FF, $00FF, $00FF, $00FF, $00FF, $00FF
+ ;dw $00FF, $00FF, $00FF, $0096, $00FF, $00FF, $00FF, $00FF
+ ;dw $00FF, $00FF, $00FF, $00FF, $00FF, $00FF, $00FF, $00FF
+ ;dw $00FF, $00FF, $00FF, $00FF, $00FF, $00FF, $00FF, $00FF
+ ;dw $009F, $00FF, $00FF, $00FF, $00FF, $00FF, $00FF, $00FF
+ ;dw $00FF, $00FF, $00FF, $00FF, $00FF, $00FF, $00FF, $00FF
+ ; SP
+ ;dw $0097, $00FF, $00FF, $00FF, $00FF, $00FF, $00FF, $00FF
+ ;dw $0093, $00FF, $00FF, $00FF, $00FF, $00FF, $00FF, $00FF
+ ;dw $00FF, $00FF, $00FF, $00FF, $00FF, $00FF, $00FF, $00FF
+ ;dw $00FF, $00FF, $00FF, $00FF, $00FF, $00FF, $00FF, $00FF
+ warnpc $288480
+
+ org $288480 ; $140480
+ .OWGFXGroupTable ; 0x500 (0xA0 * 0x08)
+
+ ; 0xFF is used instead of 0x00 as the "don't change the sheet" value. That
+ ; way, we can actually use sheet 00 if we want. Just in case 0xFF is used
+ ; and there is no sheet to load when warping using the bird, unloading the
+ ; map, or exiting a dungeon, the DefaultGFXGroups values are used.
+
+ ; LW
+ org $288480 ; $140480
+ .OWGFXGroupTable_sheet0
+ ;db $3A ; 0x00 sheet 0
+
+ org $288481 ; $140481
+ .OWGFXGroupTable_sheet1
+ ;db $3B ; 0x00 sheet 1
+
+ org $288482 ; $140482
+ .OWGFXGroupTable_sheet2
+ ;db $3C ; 0x00 sheet 2
+
+ org $288483 ; $140483
+ .OWGFXGroupTable_sheet3
+ ;db $3D ; 0x00 sheet 3
+
+ org $288484 ; $140484
+ .OWGFXGroupTable_sheet4
+ ;db $57 ; 0x00 sheet 4
+
+ org $288485 ; $140485
+ .OWGFXGroupTable_sheet5
+ ;db $4C ; 0x00 sheet 5
+
+ org $288486 ; $140486
+ .OWGFXGroupTable_sheet6
+ ;db $3E ; 0x00 sheet 6
+
+ org $288487 ; $140487
+ .OWGFXGroupTable_sheet7
+ ;db $5B ; 0x00 sheet 7
+
+ ;db $3A, $3B, $3C, $3D, $57, $4C, $3E, $5B ; 0x01
+ ;db $3A, $3B, $3C, $3D, $57, $4C, $3E, $5B ; 0x02
+ ;db $3A, $3B, $3C, $3D, $56, $4F, $3E, $5B ; 0x03
+ ;db $3A, $3B, $3C, $3D, $56, $4F, $3E, $5B ; 0x04
+ ;db $3A, $3B, $3C, $3D, $56, $4F, $3E, $5B ; 0x05
+ ;db $3A, $3B, $3C, $3D, $56, $4F, $3E, $5B ; 0x06
+ ;db $3A, $3B, $3C, $3D, $56, $4F, $3E, $5B ; 0x07
+
+ ;db $3A, $3B, $3C, $3D, $57, $4C, $3E, $5B ; 0x08
+ ;db $3A, $3B, $3C, $3D, $57, $4C, $3E, $5B ; 0x09
+ ;db $3A, $3B, $3C, $3D, $57, $4C, $3E, $5B ; 0x0A
+ ;db $3A, $3B, $3C, $3D, $56, $4F, $3E, $5B ; 0x0B
+ ;db $3A, $3B, $3C, $3D, $56, $4F, $3E, $5B ; 0x0C
+ ;db $3A, $3B, $3C, $3D, $56, $4F, $3E, $5B ; 0x0D
+ ;db $3A, $3B, $3C, $3D, $56, $4F, $3E, $5B ; 0x0E
+ ;db $3A, $3B, $3C, $3D, $51, $4E, $3E, $5B ; 0x0F
+
+ ;db $3A, $3B, $3C, $3D, $53, $4D, $3E, $5B ; 0x10
+ ;db $3A, $3B, $3C, $3D, $53, $4D, $3E, $5B ; 0x11
+ ;db $3A, $3B, $3C, $3D, $53, $4D, $3E, $5B ; 0x12
+ ;db $3A, $3B, $3C, $3D, $50, $4B, $3E, $5B ; 0x13
+ ;db $3A, $3B, $3C, $3D, $50, $4B, $3E, $5B ; 0x14
+ ;db $3A, $3B, $3C, $3D, $53, $4D, $3E, $5B ; 0x15
+ ;db $3A, $3B, $3C, $3D, $50, $4B, $3E, $5B ; 0x16
+ ;db $3A, $3B, $3C, $3D, $50, $4B, $3E, $5B ; 0x17
+
+ ;db $3A, $3B, $3C, $3D, $53, $4D, $3E, $5B ; 0x18
+ ;db $3A, $3B, $3C, $3D, $53, $4D, $3E, $5B ; 0x19
+ ;db $3A, $3B, $3C, $3D, $53, $4D, $3E, $5B ; 0x1A
+ ;db $3A, $3B, $3C, $3D, $52, $49, $3E, $5B ; 0x1B
+ ;db $3A, $3B, $3C, $3D, $52, $49, $3E, $5B ; 0x1C
+ ;db $3A, $3B, $3C, $3D, $51, $4E, $3E, $5B ; 0x1D
+ ;db $3A, $3B, $3C, $3D, $55, $4A, $3E, $5B ; 0x1E
+ ;db $3A, $3B, $3C, $3D, $55, $4A, $3E, $5B ; 0x1F
+
+ ;db $3A, $3B, $3C, $3D, $53, $4D, $3E, $5B ; 0x20
+ ;db $3A, $3B, $3C, $3D, $53, $4D, $3E, $5B ; 0x21
+ ;db $3A, $3B, $3C, $3D, $53, $4D, $3E, $5B ; 0x22
+ ;db $3A, $3B, $3C, $3D, $52, $49, $3E, $5B ; 0x23
+ ;db $3A, $3B, $3C, $3D, $52, $49, $3E, $5B ; 0x24
+ ;db $3A, $3B, $3C, $3D, $53, $4D, $3E, $5B ; 0x25
+ ;db $3A, $3B, $3C, $3D, $55, $4A, $3E, $5B ; 0x26
+ ;db $3A, $3B, $3C, $3D, $55, $4A, $3E, $5B ; 0x27
+
+ ;db $3A, $3B, $3C, $3D, $53, $4D, $3E, $5B ; 0x28
+ ;db $3A, $3B, $3C, $3D, $50, $4B, $3E, $5B ; 0x29
+ ;db $3A, $3B, $3C, $3D, $57, $4C, $3E, $5B ; 0x2A
+ ;db $3A, $3B, $3C, $3D, $53, $4D, $3E, $5B ; 0x2B
+ ;db $3A, $3B, $3C, $3D, $53, $4D, $3E, $5B ; 0x2C
+ ;db $3A, $3B, $3C, $3D, $51, $4E, $3E, $5B ; 0x2D
+ ;db $3A, $3B, $3C, $3D, $53, $4D, $3E, $5B ; 0x2E
+ ;db $3A, $3B, $3C, $3D, $55, $4A, $3E, $5B ; 0x2F
+
+ ;db $3A, $3B, $3C, $3D, $55, $54, $3E, $5B ; 0x30
+ ;db $3A, $3B, $3C, $3D, $55, $54, $3E, $5B ; 0x31
+ ;db $3A, $3B, $3C, $3D, $53, $4D, $3E, $5B ; 0x32
+ ;db $3A, $3B, $3C, $3D, $51, $4E, $3E, $5B ; 0x33
+ ;db $3A, $3B, $3C, $3D, $51, $4E, $3E, $5B ; 0x34
+ ;db $3A, $3B, $3C, $3D, $51, $4E, $3E, $5B ; 0x35
+ ;db $3A, $3B, $3C, $3D, $51, $4E, $3E, $5B ; 0x36
+ ;db $3A, $3B, $3C, $3D, $51, $4E, $3E, $5B ; 0x37
+
+ ;db $3A, $3B, $3C, $3D, $55, $54, $3E, $5B ; 0x38
+ ;db $3A, $3B, $3C, $3D, $55, $54, $3E, $5B ; 0x39
+ ;db $3A, $3B, $3C, $3D, $53, $4D, $3E, $5B ; 0x3A
+ ;db $3A, $3B, $3C, $3D, $51, $4E, $3E, $5B ; 0x3B
+ ;db $3A, $3B, $3C, $3D, $51, $4E, $3E, $5B ; 0x3C
+ ;db $3A, $3B, $3C, $3D, $51, $4E, $3E, $5B ; 0x3D
+ ;db $3A, $3B, $3C, $3D, $51, $4E, $3E, $5B ; 0x3E
+ ;db $3A, $3B, $3C, $3D, $51, $4E, $3E, $5B ; 0x3F
+
+ ; DW
+ ;db $42, $43, $44, $45, $2D, $2E, $3F, $59 ; 0x40
+ ;db $42, $43, $44, $45, $2D, $2E, $3F, $59 ; 0x41
+ ;db $42, $43, $44, $45, $2D, $2E, $3F, $59 ; 0x42
+ ;db $42, $43, $44, $45, $33, $34, $3F, $59 ; 0x43
+ ;db $42, $43, $44, $45, $33, $34, $3F, $59 ; 0x44
+ ;db $42, $43, $44, $45, $33, $34, $3F, $59 ; 0x45
+ ;db $42, $43, $44, $45, $33, $34, $3F, $59 ; 0x46
+ ;db $42, $43, $44, $45, $60, $34, $3F, $59 ; 0x47
+
+ ;db $42, $43, $44, $45, $2D, $2E, $3F, $59 ; 0x48
+ ;db $42, $43, $44, $45, $2D, $2E, $3F, $59 ; 0x49
+ ;db $42, $43, $44, $45, $2D, $2E, $3F, $59 ; 0x4A
+ ;db $42, $43, $44, $45, $33, $34, $3F, $59 ; 0x4B
+ ;db $42, $43, $44, $45, $33, $34, $3F, $59 ; 0x4C
+ ;db $42, $43, $44, $45, $33, $34, $3F, $59 ; 0x4D
+ ;db $42, $43, $44, $45, $33, $34, $3F, $59 ; 0x4E
+ ;db $42, $43, $44, $45, $37, $38, $3F, $59 ; 0x4F
+
+ ;db $42, $43, $44, $45, $2F, $30, $3F, $59 ; 0x50
+ ;db $42, $43, $44, $45, $2F, $30, $3F, $59 ; 0x51
+ ;db $42, $43, $44, $45, $20, $2B, $3F, $59 ; 0x52
+ ;db $42, $43, $44, $45, $37, $38, $3F, $59 ; 0x53
+ ;db $42, $43, $44, $45, $37, $38, $3F, $59 ; 0x54
+ ;db $42, $43, $44, $45, $20, $2B, $3F, $59 ; 0x55
+ ;db $42, $43, $44, $45, $37, $38, $3F, $59 ; 0x56
+ ;db $42, $43, $44, $45, $20, $2B, $3F, $59 ; 0x57
+
+ ;db $42, $43, $44, $45, $2F, $30, $3F, $59 ; 0x58
+ ;db $42, $43, $44, $45, $2F, $30, $3F, $59 ; 0x59
+ ;db $42, $43, $44, $45, $20, $2B, $3F, $59 ; 0x5A
+ ;db $42, $43, $44, $45, $35, $36, $3F, $59 ; 0x5B
+ ;db $42, $43, $44, $45, $35, $36, $3F, $59 ; 0x5C
+ ;db $42, $43, $44, $45, $37, $38, $3F, $59 ; 0x5D
+ ;db $42, $43, $44, $45, $2B, $2C, $3F, $59 ; 0x5E
+ ;db $42, $43, $44, $45, $2B, $2C, $3F, $59 ; 0x5F
+
+ ;db $42, $43, $44, $45, $2F, $30, $3F, $59 ; 0x60
+ ;db $42, $43, $44, $45, $2F, $30, $3F, $59 ; 0x61
+ ;db $42, $43, $44, $45, $2F, $30, $3F, $59 ; 0x62
+ ;db $42, $43, $44, $45, $35, $36, $3F, $59 ; 0x63
+ ;db $42, $43, $44, $45, $35, $36, $3F, $59 ; 0x64
+ ;db $42, $43, $44, $45, $37, $38, $3F, $59 ; 0x65
+ ;db $42, $43, $44, $45, $2B, $2C, $3F, $59 ; 0x66
+ ;db $42, $43, $44, $45, $2B, $2C, $3F, $59 ; 0x67
+
+ ;db $42, $43, $44, $45, $2F, $30, $3F, $59 ; 0x68
+ ;db $42, $43, $44, $45, $2F, $30, $3F, $59 ; 0x69
+ ;db $42, $43, $44, $45, $20, $2B, $3F, $59 ; 0x6A
+ ;db $42, $43, $44, $45, $20, $2B, $3F, $59 ; 0x6B
+ ;db $42, $43, $44, $45, $20, $2B, $3F, $59 ; 0x6C
+ ;db $42, $43, $44, $45, $20, $2B, $3F, $59 ; 0x6D
+ ;db $42, $43, $44, $45, $20, $2B, $3F, $59 ; 0x6E
+ ;db $42, $43, $44, $45, $2B, $2C, $3F, $59 ; 0x6F
+
+ ;db $42, $43, $44, $45, $31, $32, $3F, $59 ; 0x70
+ ;db $42, $43, $44, $45, $31, $32, $3F, $59 ; 0x71
+ ;db $42, $43, $44, $45, $20, $2B, $3F, $59 ; 0x72
+ ;db $42, $43, $44, $45, $37, $38, $3F, $59 ; 0x73
+ ;db $42, $43, $44, $45, $37, $38, $3F, $59 ; 0x74
+ ;db $42, $43, $44, $45, $31, $32, $3F, $59 ; 0x75
+ ;db $42, $43, $44, $45, $31, $32, $3F, $59 ; 0x76
+ ;db $42, $43, $44, $45, $37, $38, $3F, $59 ; 0x77
+
+ ;db $42, $43, $44, $45, $31, $32, $3F, $59 ; 0x78
+ ;db $42, $43, $44, $45, $31, $32, $3F, $59 ; 0x79
+ ;db $42, $43, $44, $45, $20, $2B, $3F, $59 ; 0x7A
+ ;db $42, $43, $44, $45, $37, $38, $3F, $59 ; 0x7B
+ ;db $42, $43, $44, $45, $37, $38, $3F, $59 ; 0x7C
+ ;db $42, $43, $44, $45, $31, $32, $3F, $59 ; 0x7D
+ ;db $42, $43, $44, $45, $31, $32, $3F, $59 ; 0x7E
+ ;db $42, $43, $44, $45, $20, $2B, $3F, $59 ; 0x7F
+
+ ; SW
+ ;db $3A, $3B, $3C, $3D, $47, $48, $3E, $5B ; 0x80
+ ;db $3A, $3B, $3C, $3D, $47, $48, $3E, $5B ; 0x81
+ ;db $3A, $3B, $3C, $3D, $47, $48, $3E, $5B ; 0x82
+ ;db $3A, $3B, $3C, $3D, $53, $4D, $3E, $5B ; 0x83
+ ;db $3A, $3B, $3C, $3D, $53, $4D, $3E, $5B ; 0x84
+ ;db $3A, $3B, $3C, $3D, $53, $4D, $3E, $5B ; 0x85
+ ;db $3A, $3B, $3C, $3D, $53, $4D, $3E, $5B ; 0x86
+ ;db $3A, $3B, $3C, $3D, $53, $4D, $3E, $5B ; 0x87
+
+ ;db $3A, $3B, $3C, $17, $40, $41, $39, $5B ; 0x88
+ ;db $3A, $3B, $3C, $3D, $47, $48, $3E, $5B ; 0x89
+ ;db $3A, $3B, $3C, $3D, $47, $48, $3E, $5B ; 0x8A
+ ;db $3A, $3B, $3C, $3D, $53, $4D, $3E, $5B ; 0x8B
+ ;db $3A, $3B, $3C, $3D, $53, $4D, $3E, $5B ; 0x8C
+ ;db $3A, $3B, $3C, $3D, $53, $4D, $3E, $5B ; 0x8D
+ ;db $3A, $3B, $3C, $3D, $53, $4D, $3E, $5B ; 0x8E
+ ;db $3A, $3B, $3C, $3D, $53, $4D, $3E, $5B ; 0x8F
+
+ ;db $3A, $3B, $3C, $08, $00, $22, $1B, $5B ; 0x90
+ ;db $3A, $3B, $3C, $08, $00, $22, $1B, $5B ; 0x91
+ ;db $3A, $3B, $3C, $06, $53, $1F, $18, $5B ; 0x92
+ ;db $3A, $3B, $3C, $08, $53, $22, $1B, $5B ; 0x93
+ ;db $3A, $3B, $3C, $3D, $53, $47, $48, $5B ; 0x94
+ ;db $3A, $3B, $3C, $3D, $53, $56, $4F, $5B ; 0x95
+ ;db $3A, $3B, $3C, $3D, $35, $36, $3E, $5B ; 0x96
+ ;db $3A, $3B, $3C, $3D, $57, $4C, $3E, $5B ; 0x97
+
+ ;db $3A, $3B, $3C, $08, $00, $22, $1B, $5B ; 0x98
+ ;db $3A, $3B, $3C, $08, $00, $22, $1B, $5B ; 0x99
+ ;db $3A, $3B, $3C, $06, $53, $1F, $18, $5B ; 0x9A
+ ;db $3A, $3B, $3C, $06, $53, $1F, $18, $5B ; 0x9B
+ ;db $3A, $3B, $3C, $3D, $53, $33, $34, $5B ; 0x9C
+ ;db $3A, $3B, $3C, $3D, $53, $57, $4C, $5B ; 0x9D
+ ;db $3A, $3B, $3C, $3D, $57, $4C, $3E, $5B ; 0x9E
+ ;db $3A, $3B, $3C, $3D, $53, $4D, $3E, $5B ; 0x9F
+ warnpc $288980
+
+ ; TODO: Add a way to edit these within ZS? Unsure.
+ org $288980 ; $140980
+ .DefaultGFXGroups
+
+ ; LW
+ org $288980 ; $140980
+ .DefaultGFXGroups_sheet0
+ db $3A ; Sheet 0
+
+ org $288981 ; $140981
+ .DefaultGFXGroups_sheet1
+ db $3B ; Sheet 1
+
+ org $288982 ; $140982
+ .DefaultGFXGroups_sheet2
+ db $3C ; Sheet 2
+
+ org $288983 ; $140983
+ .DefaultGFXGroups_sheet3
+ db $3D ; Sheet 3
+
+ org $288984 ; $140984
+ .DefaultGFXGroups_sheet4
+ db $53 ; Sheet 4
+
+ org $288985 ; $140985
+ .DefaultGFXGroups_sheet5
+ db $4D ; Sheet 5
+
+ org $288986 ; $140986
+ .DefaultGFXGroups_sheet6
+ db $3E ; Sheet 6
+
+ org $288987 ; $140987
+ .DefaultGFXGroups_sheet7
+ db $5B ; Sheet 7
+
+ ; DW
+ db $42, $43, $44, $45, $2F, $30, $3F, $59
+
+ ; SW
+ db $3A, $3B, $3C, $3D, $47, $48, $3E, $5B
+
+ warnpc $288998 ; $140998
+}
+
+; Debug addresses:
+; $00D8D5 ; W7 Animated tiles on warp.
+!Func00D8D5 = $01
+; $00DA63 ; W8 Enable/Disable subscreen.
+!Func00DA63 = $01
+; $00E133 ; T2.5 (probably) ; Makes the game decompress the 3 static OW tile
+; sheets on transition.
+!Func00E133 = $01
+; $006221 ; Changes the InitTilesets function to call from the long tables.
+!Func00E221 = $01
+; $00EEBB ; Zeros out the BG color when mirror warping to the pyramid area.
+!Func00EEBB = $01
+; $00FF7C ; W9 BG scrolling for HC and the pyramid area.
+!Func00FF7C = $01
+
+; Some old but now unused addresses:
+; $028027
+; $029C0C
+; $029D1E
+; $029F82
+
+; $0283EE ; E2 ; Changes the function that loads overworld properties when
+; exiting a dungeon. Includes removing asm that plays music in certain areas
+; and changing how animated tiles are loaded.
+!Func0283EE = $01
+; $028632 ; Changes a function that loads animated tiles under certain
+; conditions.
+!Func028632 = $01
+; $029AA6 ; E1 ; Changes part of a function that changes the sub mask color
+; when leaving dungeons.
+!Func029AA6 = $01
+; $02AF58 ; T2 S2 W2 Main subscreen loading function.
+!Func02AF58 = $01
+; $02B2D4 ; W1 turns on subscreen for pyramid.
+!Func02B2D4 = $01
+; $02B3A1 ; W6 Activate subscreen durring pyramid warp.
+!Func02B3A1 = $01
+; $02BC44 ; Controls overworld vertical subscreen movement for the pyramid BG.
+!Func02BC44 = $01
+; $02C02D ; T4 Changes how the pyramid BG scrolls durring transition.
+!Func02C02D = $01
+; $02C692 ; W3 Main palette loading routine.
+!Func02C692 = $01
+; $02A4CD ; Rain animation code.
+!Func02A4CD = $01
+; $02AADB ; T1 Mosaic.
+!Func02AADB = $01
+; $02ABBE ; T3 transition animated and main palette.
+!Func02ABBE = $01
+; $0ABC5A ; Loads the animated tiles after the overworld map is closed.
+!Func0ABC5A = $01
+; $0AB8F5 ; Loads different animated tiles when returning from bird travel.
+!Func0AB8F5 = $01
+; $0BFEC6 ; W5 Load overlay, fixed color, and BG color.
+!Func0BFEC6 = $01
+; $0ED627 ; S1 W4 Transparent color durring warp and during special area enter.
+!Func0ED627 = $01
+; $0ED8AE ; Resets the area special color after the screen flashes.
+!Func0ED8AE = $01
+
+; Start of expanded space.
+org $2892B8 ; $1412B8
+pushpc
+
+; ==============================================================================
+
+if !Func00D8D5 = 1
+
+; Replaces a function that decompresses animated tiles in certain mirror warp
+; conditions.
+org $00D8D5 ; $0058D5
+AnimateMirrorWarp_DecompressAnimatedTiles:
+{
+ PHX
+
+ ; The decompression function increases it by 1 so subtract 1 here.
+ JSL ReadAnimatedTable : DEC : TAY
+
+ PLX
+
+ JSL DecompOwAnimatedTiles
+
+ RTL
+}
+warnpc $00D8EE
+
+else
+
+; Undo the function above:
+org $00D8D5 ; $0058D5
+db $A0, $58, $A5, $8A, $29, $BF, $C9, $03
+db $F0, $0A, $C9, $05, $F0, $06, $C9, $07
+db $F0, $02, $A0, $5A, $22, $94, $D3, $00
+db $6B
+
+endif
+
+pullpc
+ReadAnimatedTable:
+{
+ PHB : PHK : PLB
+
+ REP #$30 ; Set A, X, and Y in 16bit mode.
+ LDA.b $8A : TAX
+ AND.w #$00C0 : LSR #3 : TAY ; (Area / 8) = LW, DW, or SW *8
+ SEP #$20 ; Set A in 8bit mode.
+
+ ; $00 crashes the game so just double check that.
+ LDA.w Pool_AnimatedTable, X : BNE .not007
+ LDA.w Pool_DefaultGFXGroups_sheet7, Y
+
+ BRA .notFF7
+
+ .not007
+
+ ; Load the default sheet if the value is FF.
+ CMP.b #$FF : BNE .notFF7
+ LDA.w Pool_DefaultGFXGroups_sheet7, Y
+
+ .notFF7
+
+ SEP #$10 ; Set X and Y in 8bit mode.
+
+ PLB
+
+ RTL
+}
+pushpc
+
+; ==============================================================================
+
+if !Func00DA63 = 1
+
+org $00D8F4 ; $0058F4
+ SheetsTable_0AA4:
+
+; The first half of this function enables or disables BG1 for subscreen overlay
+; use depending on the area. The second half reloads global sprite #2 sheet
+; (rock vs skulls, different bush gfx, fish vs bone fish, etc.) based on what
+; world we are in.
+org $00DA63 ; $005A63
+AnimateMirrorWarp_LoadSubscreen:
+{
+ JSL ActivateSubScreen
+
+ ; From this point on it is the vanilla function.
+ PHB : PHK : PLB
+
+ ; TODO: Eventually un-hardcode this.
+ ; X = 0 for LW, 8 for DW
+ LDA.l SheetsTable_0AA4, X : TAY
+
+ ; Get the pointer for one of the 2 Global sprite #2 sheets.
+ LDA.w $D1B1, Y : STA.b $00
+ LDA.w $D0D2, Y : STA.b $01
+ LDA.w $CFF3, Y : STA.b $02 : STA.b $05
+
+ PLB
+
+ REP #$31 ; Set A, X, and Y in 16bit mode. +1 no idea.
+
+ ; Source address is determined above, number of tiles is 0x0040, base
+ ; target address is $7F0000.
+ LDX.w #$0000
+ LDY.w #$0040
+
+ LDA.b $00
+
+ JSR.w Do3To4High16Bit
+
+ SEP #$30 ; Set A, X, and Y in 8bit mode.
+
+ RTL
+}
+warnpc $00DABB
+
+else
+
+; Undo the function above:
+org $00DA63 ; $005A63
+db $64, $1D, $A5, $8A, $F0, $24, $C9, $70
+db $F0, $20, $C9, $40, $F0, $1C, $C9, $5B
+db $F0, $18, $C9, $03, $F0, $14, $C9, $05
+db $F0, $10, $C9, $07, $F0, $0C, $C9, $43
+db $F0, $08, $C9, $45, $F0, $04, $C9, $47
+db $D0, $04, $A9, $01, $85, $1D, $8B, $4B
+db $AB, $BF, $F4, $D8, $00, $A8, $B9, $B1
+db $D1, $85, $00, $B9, $D2, $D0, $85, $01
+db $B9, $F3, $CF, $85, $02, $85, $05, $AB
+db $C2, $31, $A2, $00, $00, $A0, $40, $00
+db $A5, $00, $20, $4F, $DF, $E2, $30, $6B
+
+endif
+
+pullpc
+ActivateSubScreen:
+{
+ STZ.b $1D
+
+ PHX
+
+ REP #$20 ; Set A in 16bit mode.
+
+ LDA.b $8A : BNE .notForest
+ ; Check if we have the master sword.
+ LDA.l $7EF300 : AND.w #$0040 : BEQ .notForest
+ ; The forest canopy overlay.
+ BRA .turnOn
+
+ .notForest
+
+ ; Check if we need to disable the rain in the misery mire.
+ LDA.w Pool_EnableRainMireEvent : BEQ .notMire
+ LDA.b $8A : CMP.w #$0070 : BNE .notMire
+ ; Has Misery Mire been triggered yet?
+ LDA.l $7EF2F0 : AND.w #$0020 : BNE .notMire
+ BRA .turnOn
+
+ .notMire
+
+ ; Check if we are in the beginning phase, if not, no rain.
+ ; If $7EF3C5 >= 0x02.
+ LDA.l $7EF3C5 : AND.w #$00FF : CMP.w #$0002 : BCS .noRain
+ BRA .turnOn
+
+ .noRain
+
+ ; Get the overlay value for this overworld area.
+ JSL ReadOverlayArray : CMP.w #$00FF : BEQ .normal
+ ; If not $FF, assume we want an overlay.
+
+ .turnOn
+ SEP #$20 ; Set A in 8bit mode.
+
+ ; Turn on BG1.
+ LDA.b #$01 : STA.b $1D
+
+ .normal
+
+ SEP #$20 ; Set A in 8bit mode.
+
+ PLX
+
+ RTL
+}
+pushpc
+
+; ==============================================================================
+
+if !Func00EEBB = 1
+
+; Zeros out the BG color when mirror warping to the pyramid area.
+; TODO: This is done in the vanilla I think as just a precaution at the apex of
+; the fade to white to make sure all of the colors truly are white but it may
+; not actually be needed.
+org $00EEBB ; $006EBB
+Func00EEBB:
+{
+ ; Check if we are warping to an area with the pyramid BG.
+ JSL ReadOverlayArray : CMP.w #$0096 : BNE .notHyruleCastle
+ ; This is annoying but I just needed a little bit of extra space.
+ JSL EraseBGColors
+
+ .notHyruleCastle
+
+ SEP #$20 ; Set A in 8bit mode.
+
+ LDA.b #$08 : STA.w $06BB
+
+ STZ.w $06BA
+
+ RTL
+}
+warnpc $00EEE0
+
+else
+
+; Undo the function above:
+org $00EEBB ; $006EBB
+db $A5, $8A, $C9, $1B, $00, $D0, $13, $A9
+db $00, $00, $8F, $00, $C3, $7E, $8F, $40
+db $C3, $7E, $8F, $00, $C5, $7E, $8F, $40
+db $C5. $7E, $E2, $20, $A9, $08, $8D, $BB
+db $06, $9C, $BA, $06, $6B
+
+endif
+
+pullpc
+EraseBGColors:
+{
+ LDA.w #$0000 : STA.l $7EC300 : STA.l $7EC340 : STA.l $7EC500 : STA.l $7EC540
+
+ RTL
+}
+pushpc
+
+; ==============================================================================
+
+if !Func00FF7C = 1
+
+; Controls the BG scrolling for HC and the pyramid area.
+org $00FF7C ; $007F7C
+Func00FF7C:
+{
+ LDA.w $1C80 : ORA.w $1C90 : ORA.w $1CA0 : ORA.w $1CB0 : CMP.b $E2 : BNE .BRANCH_DELTA
+ SEP #$30 ; Set A, X, and Y in 8bit mode.
+
+ STZ.b $9B
+
+ INC.b $B0
+
+ JSL Overworld_SetFixedColorAndScroll
+
+ REP #$30 ; Set A, X, and Y in 16bit mode.
+
+ ; Check if we are warping to an area with the pyramid BG.
+ JSL ReadOverlayArray : CMP.w #$0096 : BEQ .dont_align_bgs
+ LDA.b $E2 : STA.b $E0 : STA.w $0120 : STA.w $011E
+ LDA.b $E8 : STA.b $E6 : STA.w $0122 : STA.w $0124
+
+ .dont_align_bgs
+ .BRANCH_DELTA
+
+ SEP #$30 ; Set A, X, and Y in 8bit mode.
+
+ RTL
+}
+; This end point also uses up a null block at the end of the function.
+warnpc $00FFC0
+
+else
+
+; Undo the function above:
+org $00FF7C ; $007F7C
+db $AD, $80, $1C, $0D, $90, $1C, $0D, $A0
+db $1C, $0D, $B0, $1C, $C5, $E2, $D0, $28
+db $E2, $20, $64, $9B, $E6, $B0, $22, $70
+db $FE, $0B, $A5, $8A, $29, $3F, $C9, $1B
+db $F0, $16, $C2, $20, $A5, $E2, $85, $E0
+db $8D, $20, $01, $8D, $1E, $01, $A5, $E8
+db $85, $E6, $8D, $22, $01, $8D, $24, $01
+db $E2, $30, $6B, $FF, $FF, $FF, $FF, $FF
+db $FF, $FF, $FF, $FF
+
+endif
+
+; ==============================================================================
+
+if !Func0283EE = 1
+
+; Replaces a bunch of calls to a shared function.
+; Intro_SetupScreen:
+org $028027 ; $010027
+ JSR.w PreOverworld_LoadProperties_LoadMain_LoadMusicIfNeeded
+
+warnpc $02802B
+
+; Dungeon_LoadSongBankIfNeeded:
+org $029C0C ; $011C0C
+ JMP PreOverworld_LoadProperties_LoadMain_LoadMusicIfNeeded
+
+warnpc $029C0F
+
+; Mirror_LoadMusic:
+org $029D1E ; $011D1E
+ JSR.w PreOverworld_LoadProperties_LoadMain_LoadMusicIfNeeded
+
+warnpc $029D21
+
+; GanonEmerges_LOadPyramidArea:
+org $029F82 ; $011F82
+ JSR.w PreOverworld_LoadProperties_LoadMain_LoadMusicIfNeeded
+
+warnpc $029F85
+
+; Changes the function that loads overworld properties when exiting a dungeon.
+; Includes removing asm that plays music in certain areas and changing how
+; animated tiles are loaded.
+org $0283EE ; $0103EE
+PreOverworld_LoadProperties_LoadMain:
+{
+ LDX.b #$F3
+
+ ; If the volume was set to half, set it back to full.
+ LDA.w $0132 : CMP.b #$F2 : BEQ .setToFull
+ ; If we're in the dark world
+ ; If area number is < 0x40 or >= 80 we are not in the dark world.
+ LDA.b $8A : CMP.b #$40 : BCC .setNormalSong
+ CMP.b #$80 : BCS .setNormalSong
+ ; Does Link have a moon pearl?
+ LDA.l $7EF357 : BNE .setNormalSong
+ ; If not, play the music that plays when you're a bunny in the
+ ; Dark World.
+ LDX.b #$04
+
+ BRA .setToFull
+
+ .setNormalSong
+
+ LDX.b $8A
+ LDA.l $7F5B00, X : AND.b #$0F : TAX
+
+ .setToFull
+
+ ; The value written here will take effect during NMI.
+ STX.w $0132
+
+ ; Set the ambient sound. Removed becuase this is also done later on.
+ ;LDX.b $8A
+ ;LDA.l $7F5B00, X : LSR #4 : STA.w $012D
+
+ ; The decompression function increases it by 1 so subtract 1 here.
+ JSL ReadAnimatedTable : DEC : TAY
+
+ JSL DecompOwAnimatedTiles
+
+ ; Decompress all other graphics.
+ JSL InitTilesets
+
+ ; Load palettes for overworld.
+ JSR.w Overworld_LoadAreaPalettes
+
+ LDX.b $8A
+
+ LDA.l $7EFD40, X : STA.b $00
+
+ LDA.l $00FD1C, X
+
+ ; Load some other palettes.
+ JSL Overworld_LoadPalettes
+
+ ; Sets the background color (changes depending on area).
+ JSL Palette_SetOwBgColor_Long
+
+ LDA.b $10 : CMP.b #$08 : BNE .specialArea2
+ ; Copies $7EC300[0x200] to $7EC500[0x200].
+ JSR.w Dungeon_LoadPalettes_cacheSettings
+
+ BRA .normalArea2
+
+ .specialArea2
+
+ ; Apparently special overworld handles palettes a bit differently?
+ JSR.w $C6EB ; $0146EB IN ROM
+
+ .normalArea2
+
+ ; Sets fixed colors and scroll values.
+ JSL Overworld_SetFixedColorAndScroll
+
+ ; Set darkness level to zero for the overworld.
+ LDA.b #$00 : STA.l $7EC017
+
+ ; Sets up properties in the event a tagalong shows up.
+ JSL Tagalong_Init
+
+ ; Set animated sprite gfx for area 0x00 and 0x40.
+ LDA.b $8A : AND.b #$3F : BNE .notForestArea
+ LDA.b #$1E
+
+ JSL GetAnimatedSpriteTile_variable
+
+ .notForestArea
+
+ ; Cache the overworld mode 0x09.
+ LDA.b #$09 : STA.w $010C
+ JSL Sprite_OverworldReloadAll ; $09C499
+
+ ; Are we in the dark world? If so, there's no warp vortex there.
+ LDA.b $8A : AND.b #$40 : BNE .noWarpVortex
+ JSL Sprite_ReinitWarpVortex
+
+ .noWarpVortex
+
+ ; Check if Blind disguised as a crystal maiden was following us when
+ ; we left the dungeon area.
+ LDA.l $7EF3CC : CMP.b #$06 : BNE .notBlindGirl
+ ; If it is Blind, kill her!
+ LDA.b #$00 : STA.l $7EF3CC
+
+ .notBlindGirl
+
+ ; Reset player variables.
+ STZ.b $6C ; In doorway flag
+ STZ.b $3A ; BY Bitfield
+ STZ.b $3C ; B Button timer
+ STZ.b $50 ; Link strafe
+ STZ.b $5E ; Link speed handler
+ STZ.w $0351 ; Link feet gfx fx
+
+ ; Reinitialize many of Link's gameplay variables.
+ JSR.w $8B0C ; $010B0C IN ROM
+
+ LDA.l $7EF357 : BNE .notBunny
+ LDA.l $7EF3CA : BEQ .notBunny
+ LDA.b #$01 : STA.w $02E0 : STA.b $56
+
+ LDA.b #$17 : STA.b $5D
+
+ JSL LoadGearPalettes_bunny
+
+ .notBunny
+
+ ; Set screen to mode 1 with BG3 priority.
+ LDA.b #$09 : STA.b $94
+
+ LDA.b #$00 : STA.l $7EC005
+
+ STZ.w $046C ; Collision BG1 flag
+ STZ.b $EE ; Reset Link layer to BG2
+ STZ.w $0476 ; Another layer flag
+
+ INC.b $11 ; SCAWFUL: We should verify what submodule this is moving to.
+ INC.b $16 ; NMI HUD Update flag
+
+ STZ.w $0402 : STZ.w $0403
+
+ ; Vanilla alternate entry point. Called in 4 different locations all of
+ ; which are overwritten above.
+ .LoadMusicIfNeeded
+
+ LDA.w $0136 : BEQ .no_music_load_needed
+ SEI
+
+ ; Shut down NMI until music loads.
+ STZ.w $4200
+
+ ; Stop all HDMA.
+ STZ.w $420C
+
+ STZ.w $0136
+
+ LDA.b #$FF : STA.w $2140
+
+ JSL Sound_LoadLightWorldSongBank
+
+ ; Re-enable NMI and joypad.
+ LDA.b #$81 : STA.w $4200
+
+ .no_music_load_needed
+
+ ; PLACE CUSTOM GFX LOAD HERE!
+ ;JSL CheckForChangeGraphicsNormalLoadCastle
+
+ RTS
+}
+warnpc $02856A ; $01056A
+
+else
+
+; Undo the function above:
+org $028027 ; $010027
+ db $20, $4C, $85, $C2
+
+org $029C0C ; $011C0C
+ db $4C, $4C, $85, $A5
+
+org $029D1E ; $011D1E
+ db $20, $4C, $85
+
+org $029F82 ; $011F82
+ db $20, $4C, $85
+
+org $0283EE ; $0103EE
+db $A0, $58, $A2, $02, $A5, $8A, $C9, $03
+db $F0, $6F, $C9, $05, $F0, $6B, $C9, $07
+db $F0, $67, $A2, $09, $A5, $8A, $C9, $43
+db $F0, $5F, $C9, $45, $F0, $5B, $C9, $47
+db $F0, $57, $A0, $5A, $A5, $8A, $C9, $40
+db $B0, $3A, $A2, $07, $AF, $C5, $F3, $7E
+db $C9, $03, $90, $02, $A2, $02, $A5, $A0
+db $C9, $E3, $F0, $3D, $C9, $18, $F0, $39
+db $C9, $2F, $F0, $35, $A5, $A0, $C9, $1F
+db $D0, $06, $A5, $8A, $C9, $18, $F0, $29
+db $A2, $05, $AF, $00, $F3, $7E, $29, $40
+db $F0, $02, $A2, $02, $A5, $A0, $F0, $19
+db $C9, $E1, $F0, $15, $A2, $F3, $AD, $32
+db $01, $C9, $F2, $F0, $30, $A2, $02, $AF
+db $C5, $F3, $7E, $C9, $02, $B0, $02, $A2
+db $03, $AF, $CA, $F3, $7E, $F0, $1E, $A2
+db $0D, $A5, $8A, $C9, $40, $F0, $0E, $C9
+db $43, $F0, $0A, $C9, $45, $F0, $06, $C9
+db $47, $F0, $02, $A2, $09, $AF, $57, $F3
+db $7E, $D0, $02, $A2, $04, $8E, $32, $01
+db $22, $94, $D3, $00, $22, $9B, $E1, $00
+db $20, $92, $C6, $A6, $8A, $BF, $40, $FD
+db $7E, $85, $00, $BF, $1C, $FD, $00, $22
+db $A8, $D5, $0E, $22, $18, $D6, $0E, $A5
+db $10, $C9, $08, $D0, $05, $20, $5F, $C6
+db $80, $03, $20, $EB, $C6, $22, $70, $FE
+db $0B, $A9, $00, $8F, $17, $C0, $7E, $22
+db $FC, $9E, $09, $A5, $8A, $29, $3F, $D0
+db $06, $A9, $1E, $22, $ED, $D4, $00, $A9
+db $09, $8D, $0C, $01, $22, $99, $C4, $09
+db $A5, $8A, $29, $40, $D0, $04, $22, $89
+db $AF, $09, $A2, $05, $AF, $C5, $F3, $7E
+db $C9, $02, $B0, $02, $A2, $01, $8E, $2D
+db $01, $AF, $CC, $F3, $7E, $C9, $06, $D0
+db $06, $A9, $00, $8F, $CC, $F3, $7E, $64
+db $6C, $64, $3A, $64, $3C, $64, $50, $64
+db $5E, $9C, $51, $03, $20, $0C, $8B, $AF
+db $57, $F3, $7E, $D0, $15, $AF, $CA, $F3
+db $7E, $F0, $0F, $A9, $01, $8D, $E0, $02
+db $85, $56, $A9, $17, $85, $5D, $22, $DD
+db $D6, $0E, $A9, $09, $85, $94, $A9, $00
+db $8F, $05, $C0, $7E, $9C, $6C, $04, $64
+db $EE, $9C, $76, $04, $E6, $11, $E6, $16
+db $9C, $02, $04, $9C, $03, $04, $AD, $36
+db $01, $F0, $18, $78, $9C, $00, $42, $9C
+db $0C, $42, $9C, $36, $01, $A9, $FF, $8D
+db $40, $21, $22, $13, $89, $00, $A9, $81
+db $8D, $00, $42, $60
+
+endif
+
+; ==============================================================================
+
+if !Func028632 = 1
+
+; Changes a function that loads animated tiles under certain conditions.
+org $028632 ; $010632
+Func028632:
+{
+ ; The decompression function increases it by 1 so subtract 1 here.
+ JSL ReadAnimatedTable : DEC : TAY
+
+ JSL DecompOwAnimatedTiles
+
+ ; SCAWFUL: Verify the submodule ID being manipulated here.
+ LDA.b $11 : LSR A : TAX
+
+ ; SCAWFUL: Spriteset1 $0AA3 is being modified, let's verify the table.
+ LDA.l $0285E2, X : STA.w $0AA3
+
+ LDA.l $0285F3, X : PHA
+
+ JSL InitTilesets
+
+ ; Load Palettes.
+ JSR.w Overworld_LoadAreaPalettes
+ PLA : STA.b $00
+
+ LDX.b $8A
+
+ LDA.l $00FD1C, X
+
+ JSL Overworld_LoadPalettes
+
+ LDA.b #$01 : STA.w $0AB2
+
+ JSL Palette_Hud
+
+ LDA.l $11 : BNE .BRANCH_4
+ JSL CopyFontToVram
+
+ .BRANCH_4
+
+ JSR.w Dungeon_LoadPalettes_cacheSettings
+ JSL Overworld_SetFixedColorAndScroll
+
+ LDA.l $8A : CMP.b #$80 : BCC .BRANCH_5
+ JSL Palette_SetOwBgColor_Long
+
+ .BRANCH_5
+
+ LDA.b #$09 : STA.b $94
+
+ INC.b $B0
+
+ RTS
+}
+warnpc $028697
+
+else
+
+; Undo the function above:
+org $028632 ; $010632
+db $A0, $58, $A5, $8A, $29, $BF, $C9, $03
+db $F0, $0A, $C9, $05, $F0, $06, $C9, $07
+db $F0, $02, $A0, $5A, $22, $94, $D3, $00
+db $A5, $11, $4A, $AA, $BF, $E2, $85, $02
+db $8D, $A3, $0A, $BF, $F3, $85, $02, $48
+db $22, $9B, $E1, $00, $20, $92, $C6, $68
+db $85, $00, $A6, $8A, $BF, $1C, $FD, $00
+db $22, $A8, $D5, $0E, $A9, $01, $8D, $B2
+db $0A, $22, $52, $EE, $1B, $A5, $11, $D0
+db $04, $22, $56, $E5, $00, $20, $5F, $C6
+db $22, $70, $FE, $0B, $A5, $8A, $C9, $80
+db $90, $04, $22, $18, $D6, $0E, $A9, $09
+db $85, $94, $E6, $B0, $60
+
+endif
+
+; ==============================================================================
+
+if !Func029AA6 = 1
+
+; Changes part of a function that changes the sub mask color when leaving
+; dungeons.
+org $029AA6 ; $011AA6
+Func029AA6:
+{
+ ; Setup fixed color values based on area number.
+ LDX.w #$4C26
+ LDY.w #$8C4C
+
+ ; TODO: Wtf why is this 0x00?
+ ; Check for LW death mountain.
+ JSL ReadOverlayArray : CMP.w #$0095 : BEQ .mountain
+ LDX.w #$4A26 : LDY.w #$874A
+
+ ; Check for DW death mountain.
+ CMP.w #$009C : BEQ .mountain
+ BRA .other
+
+ .mountain
+
+ STX.b $9C : STY.b $9D
+
+ .other
+
+ SEP #$30 ; Set A, X, and Y in 8bit mode.
+
+ RTS
+}
+warnpc $029AD3
+
+else
+
+; Undo the function above:
+org $029AA6 ; $011AA6
+db $A5, $8A, $C9, $03, $00, $F0, $1F, $C9
+db $05, $00, $F0, $1A, $C9, $07, $00, $F0
+db $15, $A2, $26, $4A, $A0, $4A, $87, $C9
+db $43, $00, $F0, $0A, $C9, $45, $00, $F0
+db $05, $C9, $47, $00, $D0, $04, $86, $9C
+db $84, $9D, $E2, $30, $60
+
+endif
+
+; ==============================================================================
+
+if !Func02AF58 = 1
+
+; Main subscreen overlay loading function. Changed so that they will load
+; from a table. This does not change the event overlays like the lost woods
+; changing to the tree canopy, the master sword area or the misery mire rain.
+; This also does not change the overlay for under the bridge because it shares
+; an area with the master sword.
+org $02AF58 ; $012F58
+CustomOverworld_LoadSubscreenOverlay_PostInit:
+{
+ SEP #$20 ; Set A in 8bit mode.
+
+ ; Check to see if we are using the mirror so that our $A0 doesn't
+ ; accidentally persist and we trigger rain sounds when we don't want them.
+ LDA.b $11 : CMP.b #$23 : BEQ .mirrorWarp
+ CMP.b #$24 : BEQ .mirrorWarp
+ CMP.b #$2C : BEQ .mirrorWarp
+ ; We can't warp to or from a special area anyway so this is fine.
+
+ REP #$20 ; Set A in 16bit mode.
+
+ ; Check to see if we are in a SW overworld area.
+ LDA.b $8A : CMP.w #$0080 : BCC .notExtendedArea
+ ; $0182 is the exit room number used for getting to Zora's Domain.
+ LDA.b $A0 : CMP.w #$0182 : BNE .notZoraFalls
+ SEP #$20 ; Set A in 8bit mode.
+
+ ; Play rain (waterfall) sound.
+ ; TODO: Write a patch to change/disable this.
+ LDA.b #$01 : STA.w $012D
+
+ REP #$20 ; Set A in 16bit mode.
+
+ .notZoraFalls
+
+ ; Check for exit rooms (the faked way of getting from one overworld
+ ; area to another). $0180 is the exit room number used for getting
+ ; into the mastersword area.
+ LDA.b $A0 : CMP.w #$0180 : BNE .notMasterSwordArea
+ ; If the Master sword is retrieved, don't do the mist overlay.
+ LDA.l $7EF300 : AND.w #$0040 : BNE .masterSwordRecieved
+ JSL ReadOverlayArray : TAX
+
+ .loadOverlayShortcut
+
+ ; Save the overlay for later.
+ PHX
+
+ JMP .loadSubScreenOverlay
+
+ .masterSwordRecieved
+
+ ; TODO: Write a patch to change what overlay is loaded here?
+ BRA .noSubscreenOverlay
+
+ .notMasterSwordArea
+
+ ; TODO: Write a patch to change what overlay is loaded here?
+ ; The second mastersword/under the bridge area.
+ LDX.w #$0094
+
+ ; $0181 is the exit room number used for getting into the under the
+ ; bridge area.
+ LDA.b $A0 : CMP.w #$0181 : BEQ .loadOverlayShortcut
+ ; TODO: Write a patch to change what overlay is loaded here?
+ ; The second Triforce room area.
+ LDX.w #$0093
+
+ ; $0189 is the exit room number used for getting to the
+ ; Triforce room.
+ CMP.w #$0189 : BEQ .loadOverlayShortcut
+ .noSubscreenOverlay
+
+ SEP #$30 ; Set A, X, and Y in 8bit mode.
+
+ STZ.b $1D ; Clear TSQ PPU Register, to be handled in NMI.
+
+ INC.b $11 ; SCAWFUL: Verify the submodule we are moving to.
+
+ RTS
+
+ .notExtendedArea
+ .mirrorWarp
+
+ REP #$20 ; Set A in 16bit mode.
+
+ JSL ReadOverlayArray : TAX
+
+ LDA.b $8A : BNE .notForest
+ ; Check if we have the master sword.
+ LDA.l $7EF300 : AND.w #$0040 : BEQ .notForest
+ ; TODO: Write a patch to change this?
+ ; The forest canopy overlay.
+ LDX.w #$009E
+
+ .notForest
+
+ ; Check if we need to disable the rain in the misery mire.
+ LDA.l Pool_EnableRainMireEvent : BEQ .notMire
+ LDA.b $8A : CMP.w #$0070 : BNE .notMire
+ ; Has Misery Mire been triggered yet?
+ LDA.l $7EF2F0 : AND.w #$0020 : BNE .notMire
+ ; The rain overlay.
+ LDX.w #$009F
+
+ SEP #$20 ; Set A in 8bit mode.
+
+ ; Load the rain sound effect.
+ ; This is done here because of some jank in the vanilla code in
+ ; this function a bit further down. Basically it loads the
+ ; overlay's ambient sound instead of the acutal areas, which
+ ; only seems to benefit us here.
+ LDA.b #$01 : STA.w $012D
+
+ REP #$20 ; Set A in 16bit mode.
+
+ .notMire
+
+ ; Check if we are in the beginning phase, if not, no rain.
+ ; If $7EF3C5 >= 0x02.
+ LDA.l Pool_EnableBeginningRain : BEQ .noRain
+ LDA.l $7EF3C5 : AND.w #$00FF : CMP.w #$0002 : BCS .noRain
+ ; The rain overlay.
+ LDX.w #$009F
+
+ .noRain
+
+ ; Store the overlay for later.
+ PHX
+
+ ; If the value is 0xFF that means we didn't set any overlay so load the
+ ; pyramid one by default.
+ CPX.w #$00FF : BNE .notFF
+ ; The pyramid background.
+ LDX.w #$0096
+
+ .notFF
+
+ ; $01300B ALTERNATE ENTRY POINT ; TODO: Verify this. If it is an alternate
+ ; entry I can't find where it is referenced anywhere.
+ .loadSubScreenOverlay
+ STY.b $84
+
+ STX.b $8A : STX.b $8C
+
+ ; Overworld map16 buffer manipulation during scrolling.
+ LDA.b $84 : SEC : SBC.w #$0400 : AND.w #$0F80 : ASL A : XBA : STA.b $88
+ LDA.b $84 : SEC : SBC.w #$0010 : AND.w #$003E : LSR A : STA.b $86
+
+ STZ.w $0418 : STZ.w $0410 : STZ.w $0416
+
+ SEP #$30 ; Set A, X, and Y in 8bit mode.
+
+ ; Color +/- buffered register.
+ LDA.b #$82 : STA.b $99
+
+ ; Puts OBJ, BG2, and BG3 on the main screen.
+ LDA.b #$16 : STA.b $1C
+
+ ; Puts BG1 on the subscreen.
+ LDA.b #$01 : STA.b $1D
+
+ ; Pull the 16 bit overlay from earlier and just discard the high byte.
+ PLX : PLA
+
+ ; One possible configuration for $2131 (CGADSUB).
+ LDA.b #$72
+
+ ; Comparing different screen types.
+ CPX.b #$97 : BEQ .loadOverlay ; Fog 1
+ CPX.b #$94 : BEQ .loadOverlay ; Master sword/bridge 2
+ CPX.b #$93 : BEQ .loadOverlay ; Triforce room 2
+ CPX.b #$9D : BEQ .loadOverlay ; Fog 2
+ CPX.b #$9E : BEQ .loadOverlay ; Tree canopy
+ CPX.b #$9F : BEQ .loadOverlay ; Rain
+ ; Alternative setting for CGADSUB (only background is enabled on
+ ; subscreen).
+ LDA.b #$20
+
+ CPX.b #$95 : BEQ .loadOverlay ; Sky
+ CPX.b #$9C : BEQ .loadOverlay ; Lava
+ CPX.b #$96 : BEQ .loadOverlay ; Pyramid BG
+ LDX.b $11
+
+ ; TODO: Investigate what these checks are for.
+ CPX.b #$23 : BEQ .loadOverlay
+ CPX.b #$2C : BEQ .loadOverlay
+ STZ.b $1D
+
+ .loadOverlay
+
+ ; Apply the selected settings to CGADSUB's mirror ($9A).
+ STA.b $9A
+
+ JSR.w LoadSubscreenOverlay
+
+ ; This is the "under the bridge" area.
+ LDA.b $8C : CMP.b #$94 : BNE .notUnderBridge
+ ; All this is doing is setting the X coordinate of BG1 to 0x0100
+ ; rather than 0x0000. (this area uses the second half of the data only,
+ ; similar to the master sword area).
+ LDA.b $E7 : ORA.b #$01 : STA.b $E7
+
+ .notUnderBridge
+
+ REP #$20 ; Set A in 16bit mode.
+
+ ; We were pretending to be in a different area to load the subscreen
+ ; overlay, so we're restoring all those settings.
+ LDA.l $7EC213 : STA.b $8A
+ LDA.l $7EC215 : STA.b $84
+ LDA.l $7EC217 : STA.b $88
+ LDA.l $7EC219 : STA.b $86
+
+ LDA.l $7EC21B : STA.w $0418
+ LDA.l $7EC21D : STA.w $0410
+ LDA.l $7EC21F : STA.w $0416
+
+ SEP #$20 ; Set A in 8bit mode.
+
+ RTS
+}
+warnpc $02B0D2 ; $0130D2
+
+else
+
+; Undo the function above:
+org $02AF58 ; $012F58
+db $A5, $8A, $C9, $80, $00, $90, $44, $A2
+db $97, $00, $A5, $A0, $C9, $80, $01, $D0
+db $12, $A2, $80, $00, $BF, $80, $F2, $7E
+db $A2, $97, $00, $29, $40, $00, $D0, $24
+db $4C, $0B, $B0, $A2, $94, $00, $C9, $81
+db $01, $F0, $F5, $A2, $93, $00, $C9, $89
+db $01, $F0, $ED, $C9, $82, $01, $F0, $05
+db $C9, $83, $01, $D0, $07, $E2, $30, $A9
+db $01, $8D, $2D, $01, $E2, $30, $64, $1D
+db $E6, $11, $60, $29, $3F, $00, $D0, $1B
+db $A5, $8A, $29, $40, $00, $D0, $0F, $A2
+db $80, $00, $BF, $80, $F2, $7E, $A2, $9E
+db $00, $29, $40, $00, $D0, $4D, $A2, $9D
+db $00, $80, $48, $A2, $95, $00, $A5, $8A
+db $C9, $03, $00, $F0, $3E, $C9, $05, $00
+db $F0, $39, $C9, $07, $00, $F0, $34, $A2
+db $9C, $00, $C9, $43, $00, $F0, $2C, $C9
+db $45, $00, $F0, $27, $C9, $47, $00, $F0
+db $22, $C9, $70, $00, $D0, $0B, $AF, $F0
+db $F2, $7E, $29, $20, $00, $D0, $14, $80
+db $0F, $A2, $96, $00, $AF, $C5, $F3, $7E
+db $29, $FF, $00, $C9, $02, $00, $B0, $03
+db $A2, $9F, $00, $84, $84, $86, $8A, $86
+db $8C, $A5, $84, $38, $E9, $00, $04, $29
+db $80, $0F, $0A, $EB, $85, $88, $A5, $84
+db $38, $E9, $10, $00, $29, $3E, $00, $4A
+db $85, $86, $9C, $18, $04, $9C, $10, $04
+db $9C, $16, $04, $E2, $30, $A9, $82, $85
+db $99, $A9, $16, $85, $1C, $A9, $01, $85
+db $1D, $DA, $A6, $8A, $BF, $00, $5B, $7F
+db $4A, $4A, $4A, $4A, $8D, $2D, $01, $FA
+db $A9, $72, $E0, $97, $F0, $39, $E0, $94
+db $F0, $35, $E0, $93, $F0, $31, $E0, $9D
+db $F0, $2D, $E0, $9E, $F0, $29, $E0, $9F
+db $F0, $25, $A9, $20, $E0, $95, $F0, $1F
+db $E0, $9C, $F0, $1B, $AF, $13, $C2, $7E
+db $AA, $A9, $20, $E0, $5B, $F0, $10, $E0
+db $1B, $D0, $0A, $A6, $11, $E0, $23, $F0
+db $06, $E0, $2C, $F0, $02, $64, $1D, $85
+db $9A, $20, $0D, $FD, $A5, $8C, $C9, $94
+db $D0, $06, $A5, $E7, $09, $01, $85, $E7
+db $C2, $20, $AF, $13, $C2, $7E, $85, $8A
+db $AF, $15, $C2, $7E, $85, $84, $AF, $17
+db $C2, $7E, $85, $88, $AF, $19, $C2, $7E
+db $85, $86, $AF, $1B, $C2, $7E, $8D, $18
+db $04, $AF, $1D, $C2, $7E, $8D, $10, $04
+db $AF, $1F, $C2, $7E, $8D, $16, $04, $E2
+db $20, $60
+
+endif
+
+; ==============================================================================
+
+if !Func02B2D4 = 1
+
+; Turns on the subscreen if the pyramid is loaded.
+org $02B2D4 ; $0132D4
+Func02B2D4:
+{
+ JSR.w Overworld_LoadSubscreenAndSilenceSFX1
+
+ ; In vanilla a check for the overlay is done here but we don't need
+ ; it at all. It is handled in Func02B3A1 later on.
+ ;JSL EnableSubScreenCheckForPyramid
+
+ RTL
+}
+warnpc $02B2E6 ; $0132E6
+
+else
+
+; Undo the function above:
+org $02B2D4 ; $0132D4
+db $20, $19, $AF, $A5, $8A, $C9, $1B, $F0
+db $04, $C9, $5B, $D0, $04, $A9, $01, $85
+db $1D, $6B
+
+endif
+
+pullpc
+EnableSubScreenCheckForPyramid:
+{
+ REP #$20 ; Set A in 16bit mode.
+
+ JSL ReadOverlayArray
+
+ CMP.w #$0096 : BNE .notPyramidOrCastle
+ SEP #$20 ; Set A in 8bit mode.
+ LDA.b #$01 : STA.b $1D
+
+ .notPyramidOrCastle
+
+ SEP #$20 ; Set A in 8bit mode.
+
+ RTL
+}
+pushpc
+
+; ==============================================================================
+
+if !Func02B3A1 = 1
+
+; Handles activating the subscreen and special BG color when warping to an area
+; with the pyramid BG.
+org $02B3A1 ; $0133A1
+Func02B3A1:
+{
+ JSL EnableSubScreenCheckForPyramid
+
+ REP #$20 ; Set A in 16bit mode.
+
+ LDX.b #$00
+
+ LDA.w #$7FFF
+
+ .setBgPalettesToWhite
+ STA.l $7EC540, X
+ STA.l $7EC560, X
+ STA.l $7EC580, X
+
+ STA.l $7EC5A0, X
+ STA.l $7EC5C0, X
+ STA.l $7EC5E0, X
+
+ INX #2 : CPX.b #$20 : BNE .setBgPalettesToWhite
+
+ ; Also set the background color to white.
+ STA.l $7EC500
+
+ JSL ReadOverlayArray
+
+ ; This sets the color to transparent so that we don't see an additional
+ ; white layer on top of the pyramid bg.
+ CMP.w #$0096 : BNE .notPyramidOfPower
+ LDA.w #$0000 : STA.l $7EC500 : STA.l $7EC540
+
+ .notPyramidOfPower
+
+ SEP #$20 ; Set A in 8bit mode.
+
+ JSL Sprite_ResetAll
+ JSL Sprite_OverworldReloadAll
+ JSL Link_ItemReset_FromOverworldThings
+ JSR.w DeleteCertainAncillaeStopDashing
+
+ LDA.b #$14 : STA.b $5D
+
+ LDA.b $8A : AND.b #$40 : BNE .darkWorld
+ JSL Sprite_ReinitWarpVortex
+
+ .darkWorld
+
+ RTL
+}
+warnpc $02B40A ; $01340A
+
+else
+
+; Undo the function above:
+org $02B3A1 ; $0133A1
+db $A5, $8A, $C9, $1B, $F0, $04, $C9, $5B
+db $D0, $04, $A9, $01, $85, $1D, $C2, $20
+db $A2, $00, $A9, $FF, $7F, $9F, $40, $C5
+db $7E, $9F, $60, $C5, $7E, $9F, $80, $C5
+db $7E, $9F, $A0, $C5, $7E, $9F, $C0, $C5
+db $7E, $9F, $E0, $C5, $7E, $E8, $E8, $E0
+db $20, $D0, $E2, $8F, $00, $C5, $7E, $A5
+db $8A, $C9, $5B, $00, $D0, $0B, $A9, $00
+db $00, $8F, $00, $C5, $7E, $8F, $40, $C5
+db $7E, $E2, $20, $22, $4E, $C4, $09, $22
+db $99, $C4, $09, $22, $07, $B1, $07, $20
+db $0C, $8B, $A9, $14, $85, $5D, $A5, $8A
+db $29, $40, $D0, $04, $22, $89, $AF, $09
+db $6B
+
+endif
+
+; ==============================================================================
+
+if !Func02BC44 = 1
+
+; Controls overworld vertical subscreen movement for the pyramid BG.
+org $02BC44 ; $013C44
+Func02BC44:
+{
+ ; Check for the pyramid BG.
+ JSL ReadOverlayArray : CMP.w #$0096 : BNE .BRANCH_IOTA
+ JSL BGControl
+ BRA .BRANCH_IOTA
+
+ warnpc $02BC60 ; $013C60
+
+ org $02BC60 ; $013C60
+ .BRANCH_IOTA
+}
+warnpc $02BC60
+
+else
+
+; Undo the function above:
+org $02BC44 ; $013C44
+db $A5, $8A, $29, $3F, $00, $C9, $1B, $00
+db $D0, $12, $A9, $00, $06, $C5, $E6, $90
+db $02, $85, $E6, $A9, $C0, $06, $C5, $E6
+db $B0, $02, $85, $E6
+
+endif
+
+pullpc
+ReadOverlayArray:
+{
+ PHB : PHK : PLB
+
+ LDA.b $8A : ASL : TAX
+ LDA.w Pool_OverlayTable, X
+
+ PLB
+
+ RTL
+}
+
+BGControl:
+{
+ ; TODO: These comparison values will need to be calulated somehow or set
+ ; depending on the area. Right now they are hardcoded to work with the
+ ; pyramid area.
+
+ ; Check link's Y position. This will need to be changed per area and per
+ ; need.
+ LDA.b $20 : CMP.w #$08E0 : BCC .startShowingMountains
+ ; Lock the position so that nothing shows through the trees.
+ LDA.w #$06C0 : STA.b $E6
+
+ RTL
+
+ .startShowingMountains
+
+ ; Don't let the BG scroll down further than the "top" of the bg when
+ ; walking up.
+ LDA.w #$0600 : CMP.b $E6 : BCC .dontLock ; #$0600
+ STA.b $E6
+
+ .dontLock
+
+ ; Don't let the BG scroll up further than the "bottom" of the bg when
+ ; walking down.
+ LDA.w #$06C0 : CMP.b $E6 : BCS .dontLock2 ; #$06C0
+ STA.b $E6 ; $TODO: I had this at $E2 for some reason.
+
+ .dontLock2
+
+ RTL
+}
+pushpc
+
+; ==============================================================================
+
+if !Func02C02D = 1
+
+; Changes how the pyramid BG scrolls durring transition.
+org $02C02D ; $01402D
+Func02C02D:
+{
+ PHA
+ JSL ReadOverlayArray2
+ PLA
+
+ ; Check for the pyramid BG.
+ CPY.b #$96 : BEQ .dontMoveBg1
+ ; This shifts the BG over by a half small area's width. This is to
+ ; line up the mountain with the tower in the distance at the appropriate
+ ; location when coming into the pyramid area from the right.
+ STA.b $E0, X
+
+ ; NOTE: There is currently a bug in vanilla where if you exit a dungeon
+ ; into the LW death mountain the sky background will become miss-aligned
+ ; and this movement will cause the sky to flicker or jump when moving to
+ ; another area. In order to fix this you would have to find the
+ ; alignment exit code and change how the game aligns BG2 when exiting.
+ ; Possibly when using the bird too.
+
+ .dontMoveBg1
+}
+warnpc $02C039 ; $014039
+
+else
+
+; Undo the function above:
+org $02C02D ; $01402D
+db $A4, $8A, $C0, $1B, $F0, $06, $C0, $5B
+db $F0, $02, $95, $E0
+
+endif
+
+pullpc
+ReadOverlayArray2:
+{
+ PHX
+
+ ; A is already 16 bit here.
+ REP #$10 ; Set X and Y in 16bit mode.
+
+ JSL ReadOverlayArray : TAY
+
+ SEP #$10 ; Set X and Y in 8bit mode.
+
+ PLX
+
+ RTL
+}
+pushpc
+
+; ==============================================================================
+
+if !Func02C692 = 1
+
+; Replaces a call to a shared function. Normally this is goes to .lightworld
+; to change the main color palette manually but we change it here so that it
+; just uses the same table as everything else.
+org $02A07A ; $01207A
+ JSR.w Overworld_LoadAreaPalettes
+
+warnpc $02A07D ; $01207D
+
+; The main overworld palette loading routine un-hardcoded to load the custom
+; main palette.
+org $02C692 ; $014692
+Overworld_LoadAreaPalettes:
+{
+ LDX.b $8A
+ LDA.l Pool_MainPaletteTable, X
+
+ ; $0AB3 =
+ ; 0 - LW
+ ; 1 - DW
+ ; 2 - LW death mountain
+ ; 3 - DW death mountain
+ ; 4 - triforce room
+ STA.w $0AB3
+
+ ; Reset pal buffer high byte.
+ STZ.w $0AA9
+
+ ; Load SP1 through SP4.
+ JSL Palette_MainSpr
+
+ ; Load SP0 (2nd half) and SP6 (2nd half).
+ JSL Palette_MiscSpr
+
+ ; Load SP5 (1st half).
+ JSL Palette_SpriteAux1
+
+ ; Load SP6 (1st half).
+ JSL Palette_SpriteAux2
+
+ ; Load SP5 (2nd half, 1st 3 colors), which is the sword palette.
+ JSL Palette_Sword
+
+ ; Load SP5 (2nd half, next 4 colors), which is the shield.
+ JSL Palette_Shield
+
+ ; Load SP7 (full) Link's whole palette, including Armor.
+ JSL Palette_ArmorAndGloves
+
+ LDX.b #$01
+
+ ; Changes the Palette_SpriteAux3 load depending on if we are in the LW or
+ ; not. Will probably need it own custom table in the future? not sure.
+ LDA.l $7EF3CA : AND.b #$40 : BEQ .lightWorld2
+ LDX.b #$03
+
+ .lightWorld2
+
+ ; Reset pal buffer0.
+ STX.w $0AAC
+
+ ; Load SP0 (first half) (or SP7 (first half)).
+ JSL Palette_SpriteAux3
+
+ ; Load BP0 and BP1 (first halves).
+ JSL Palette_Hud
+
+ ; Load BP2 through BP5 (first halves).
+ JSL Palette_OverworldBgMain
+
+ RTS
+}
+warnpc $02C6EB ; $0146EB
+
+else
+
+; Undo the function above:
+org $02A07A ; $01207A
+db $20, $AD, $C6
+
+org $02C692 ; $14692
+db $A2, $02, $A5, $8A, $29, $3F, $C9, $03
+db $F0, $0A, $C9, $05, $F0, $06, $C9, $07
+db $F0, $02, $A2, $00, $A5, $8A, $29, $40
+db $F0, $01, $E8, $8E, $B3, $0A, $9C, $A9
+db $0A, $22, $9E, $EC, $1B, $22, $6E, $ED
+db $1B, $22, $C5, $EC, $1B, $22, $E4, $EC
+db $1B, $22, $03, $ED, $1B, $22, $29, $ED
+db $1B, $22, $F9, $ED, $1B, $A2, $01, $AF
+db $CA, $F3, $7E, $29, $40, $F0, $02, $A2
+db $03, $8E, $AC, $0A, $22, $77, $EC, $1B
+db $22, $52, $EE, $1B, $22, $C7, $EE, $1B
+db $60
+
+endif
+
+; ==============================================================================
+
+if !Func02A4CD = 1
+
+; Rain animation code. Just replaces a single check that checks for the
+; misery mire to instead check the current overlay to see if it's rain.
+org $02A4CD ; $0124CD
+RainAnimation:
+{
+ LDA.b $8C : CMP.b #$9F : BEQ .rainOverlaySet
+ ; Check the progress indicator.
+ LDA.l $7EF3C5 : CMP.b #$02 : BRA .skipMovement
+ .rainOverlaySet
+
+ ; If misery mire has been opened already, we're done.
+ ;LDA.l $7EF2F0 : AND.b #$20 : BNE .skipMovement
+ ; Check the frame counter.
+ ; On the third frame do a flash of lightning.
+ LDA.b $1A
+
+ ; On the 0x03rd frame, cue the lightning.
+ CMP.b #$03 : BEQ .lightning
+ ; On the 0x05th frame, normal light level.
+ CMP.b #$05 : BEQ .normalLight
+ ; On the 0x24th frame, cue the thunder.
+ CMP.b #$24 : BEQ .thunder
+ ; On the 0x2Cth frame, normal light level.
+ CMP.b #$2C : BEQ .normalLight
+ ; On the 0x58th frame, cue the lightning.
+ CMP.b #$58 : BEQ .lightning
+ ; On the 0x5Ath frame, normal light level.
+ CMP.b #$5A : BNE .moveOverlay
+
+ .normalLight
+
+ ; Keep the screen semi-dark.
+ LDA.b #$72
+
+ BRA .setBrightness
+
+ .thunder
+
+ ; Play the thunder sound when outdoors.
+ LDX.b #$36 : STX.w $012E
+
+ .lightning
+
+ ; Make the screen flash with lightning.
+ LDA.b #$32
+
+ .setBrightness
+
+ STA.b $9A
+
+ .moveOverlay
+
+ ; Overlay is only moved every 4th frame.
+ LDA.b $1A : AND.b #$03 : BNE .skipMovement
+ LDA.w $0494 : INC A : AND.b #$03 : STA.w $0494 : TAX
+
+ LDA.b $E1 : CLC : ADC.l $02A46D, X : STA.b $E1
+ LDA.b $E7 : CLC : ADC.l $02A471, X : STA.b $E7
+
+ .skipMovement
+
+ RTL
+}
+warnpc $02A52D ; $01252D
+
+else
+
+; Undo the function above:
+org $02A4CD ; $0124CD
+db $A5, $8A, $C9, $70, $F0, $08, $AF, $C5
+db $F3, $7E, $C9, $02, $B0, $51, $AF, $F0
+db $F2, $7E, $29, $20, $D0, $49, $A5, $1A
+db $C9, $03, $F0, $1D, $C9, $05, $F0, $10
+db $C9, $24, $F0, $10, $C9, $2C, $F0, $08
+db $C9, $58, $F0, $0D, $C9, $5A, $D0, $0D
+db $A9, $72, $80, $07, $A2, $36, $8E, $2E
+db $01, $A9, $32, $85, $9A, $A5, $1A, $29
+db $03, $D0, $1C, $AD, $94, $04, $1A, $29
+db $03, $8D, $94, $04, $AA, $A5, $E1, $18
+db $7F, $6D, $A4, $02, $85, $E1, $A5, $E7
+db $18, $7F, $71, $A4, $02, $85, $E7, $6B
+
+endif
+
+; ==============================================================================
+
+if !Func02AADB = 1
+
+; Main Mosaic Hook. Changes it to use a table instead of hardcoded to the woods
+; areas.
+org $02AADB ; $012ADB
+ JML MosaicAreaCheck
+
+warnpc $02AADF ; $012ADF
+
+else
+
+; Undo the function above:
+org $02AADB ; $012ADB
+db $29, $3F, $F0, $06
+
+endif
+
+pullpc
+MosaicAreaCheck:
+{
+ PHB : PHK : PLB
+
+ ; Check if the area we are in needs a mosaic.
+ TAX
+ LDA.w Pool_MosaicTable, X
+
+ BEQ .noMosaic1
+ PLB
+ JML $02AAE5
+
+ .noMosaic1
+
+ ; Check if the area we are going to needs a mosaic.
+ LDX.b $8A
+ LDA.w Pool_MosaicTable, X
+
+ BEQ .noMosaic2
+ PLB
+ JML $02AAE5
+
+ .noMosaic2
+
+ PLB
+ JML $02AAF4
+}
+pushpc
+
+; ==============================================================================
+
+; Repairs an old ZS call.
+org $02ABB8 ; $012BB8
+db $A9, $09, $80, $02
+
+if !Func02ABBE = 1
+
+org $02ABBE ; $012BBE
+ JSL NewOverworld_FinishTransGfx
+ NOP : NOP : NOP
+
+warnpc $02ABC5 ; $012BC5
+
+else
+
+; Undo the function above:
+org $02ABBE ; $012BBE
+db $85, $17, $8D, $10, $07, $E6, $11
+
+endif
+
+pullpc
+; Loads the animated tiles after most of the transition gfx changes take place.
+NewOverworld_FinishTransGfx:
+{
+ PHB : PHK : PLB
+
+ ; First frame
+ LDA.w TransGFXModuleIndex : BNE .notLoad
+ JSR CheckForChangeGraphicsTransitionLoad
+
+ ; Trigger NMI module: NMI_UpdateBgChrSlots_3_to_4.
+ LDA.b #$09
+
+ ; Signal for a graphics transfer in the NMI routine later.
+ STA.b $17 : STA.w $0710
+
+ ; Move on to next submodule.
+ INC.b $11
+
+ ; Move on to next subsubmodule.
+ INC.w TransGFXModuleIndex
+
+ BRA .return
+
+ .notLoad
+
+ ; Second frame
+ CMP.b #$01 : BNE .notFinish
+ ; Trigger NMI module: NMI_UpdateBgChrSlots_5_to_6.
+ LDA.b #$0A
+
+ ; Signal for a graphics transfer in the NMI routine later.
+ STA.b $17 : STA.w $0710
+
+ ; Don't move on to the next submodule yet.
+
+ ; Move on to next subsubmodule.
+ INC.w TransGFXModuleIndex
+
+ BRA .return
+
+ .notFinish
+
+ ; Third frame
+ CMP.b #$02 : BNE .notMain1
+ LDA.w Pool_EnableTransitionGFXGroupLoad : BEQ .moveOn
+ ; Prep the new static gfx tile sets.
+ JSR LoadTransMainGFX
+ JSR NewPrepTransAuxGFX
+
+ ; Trigger NMI module: NMI_UpdateChr_Bg0.
+ LDA.b #$0E
+
+ ; Signal for a graphics transfer in the NMI routine later.
+ STA.b $17 : STA.w $0710
+
+ ; Move on to next subsubmodule.
+ INC.w TransGFXModuleIndex
+
+ BRA .return
+
+ .notMain1
+
+ ; Fourth frame
+ LDA.w Pool_EnableTransitionGFXGroupLoad : BEQ .moveOn
+ ; Trigger NMI module: NMI_DoNothing which we replaced with
+ ; NMI_UpdateChr_Bg2HalfAndAnimated down below.
+ LDA.b #$06
+
+ ; Signal for a graphics transfer in the NMI routine later.
+ STA.b $17 : STA.w $0710
+
+ .moveOn
+
+ ; Move on to next submodule.
+ INC.b $11
+
+ .return
+
+ PLB
+
+ RTL
+}
+
+CheckForChangeGraphicsTransitionLoad:
+{
+ ; Are we currently in a mosaic?
+ LDA.b $11 : CMP.b #$0F : BEQ .mosaic
+ ; Are we entering a special area?
+ CMP.b #$1A : BEQ .mosaic
+ ; Are we leaving a special area?
+ CMP.b #$26 : BEQ .mosaic
+ ; Just a normal transition, Not a mosaic.
+ LDA.l Pool_EnableAnimated : BEQ .dontUpdateAnimated1
+ ; Check to see if we need to update the animated tiles
+ ; by checking what was previously loaded.
+ JSL ReadAnimatedTable : CMP.w AnimatedTileGFXSet : BEQ .dontUpdateAnimated1
+ STA.w AnimatedTileGFXSet : DEC : TAY
+
+ ; This forces the game toupdate the animated tiles
+ ; when going from one area to another.
+ JSL DecompOwAnimatedTiles
+
+ .dontUpdateAnimated1
+
+ LDA.w Pool_EnableMainPalette : BEQ .dontUpdateMain1
+ ; Check to see if we need to update the main palette by
+ ; checking what was previously loaded.
+ LDX.b $8A
+ LDA.w Pool_MainPaletteTable, X : CMP.w $0AB3 : BEQ .dontUpdateMain1
+ STA.w $0AB3
+
+ ; Run the modified routine that loads the buffer
+ ; and normal color ram.
+ JSL Palette_OverworldBgMain2
+
+ .dontUpdateMain1
+
+ LDA.w Pool_EnableBGColor : BEQ .dontUpdateBGColor1
+ REP #$30 ; Set A, X, and Y in 16bit mode.
+
+ LDA.b $8A : ASL : TAX ; Get area code and times it by 2.
+
+ ; Where ZS saves the array of palettes
+ LDA.w Pool_BGColorTable, X
+ STA.l $7EC300 : STA.l $7EC500
+ STA.l $7EC540 : STA.l $7EC340
+
+ SEP #$30 ; Set A, X, and Y in 8bit mode.
+
+ ; Don't update the CRAM until later when the overlays are
+ ; loaded so that way the BG overlays have a chance to hide
+ ; the cracks.
+ ;INC.b $15
+
+ .dontUpdateBGColor1
+
+ RTS
+
+ .mosaic
+
+ ; Check to see if we need to update the animated tiles by checking what
+ ; was previously loaded.
+ JSL ReadAnimatedTable : CMP.w AnimatedTileGFXSet : BEQ .dontUpdateAnimated2
+ STA.w AnimatedTileGFXSet : DEC : TAY
+
+ ; This forces the game to update the animated tiles when going
+ ; from one area to another.
+ JSL DecompOwAnimatedTiles
+
+ .dontUpdateAnimated2
+
+ ; Check to see if we need to update the main palette by checking
+ ; what was previously loaded.
+ LDX.b $8A
+ LDA.w Pool_MainPaletteTable, X : CMP.w $0AB3 : BEQ .dontUpdateMain2
+ STA.w $0AB3
+
+ ; Run the vanilla routine that only loads the buffer.
+ JSL Palette_OverworldBgMain
+
+ .dontUpdateMain2
+
+ REP #$30 ; Set A, X, and Y in 16bit mode.
+
+ ; $0181 is the exit room number used for getting into the under the bridge
+ ; area.
+ LDA.b $A0 : CMP.w #$0181 : BNE .notBridge
+ LDA.w Pool_BGColorTable_Bridge
+
+ BRA .storeColor
+
+ .notBridge
+
+ LDA.b $8A : ASL : TAX ; Get area code and times it by 2.
+
+ LDA.w Pool_BGColorTable, X ; Where ZS saves the array of palettes.
+
+ .storeColor
+
+ ; Set transparent color. only set the buffer so it fades in right
+ ; during mosaic transition.
+ STA.l $7EC300 : STA.l $7EC340
+
+ LDX.w #$4020 : STX.b $9C
+ LDX.w #$8040 : STX.b $9D
+
+ LDX.w #$4F33
+ LDY.w #$894F
+
+ ; Change the fixed color depending on our sub screen overlay.
+ ; Lost woods and skull woods.
+ JSL ReadOverlayArray : CMP.w #$009D : BEQ .noSpecialColor
+ CMP.w #$0040 : BEQ .noSpecialColor
+ ; Pyramid area.
+ CMP.w #$0096 : BEQ .specialColor
+ LDX.w #$4C26
+ LDY.w #$8C4C
+
+ ; LW death mountain.
+ CMP.w #$0095 : BEQ .specialColor
+ LDX.w #$4A26
+ LDY.w #$874A
+
+ ; DW death mountain.
+ CMP.w #$009C : BEQ .specialColor
+ BRA .noSpecialColor
+
+ .specialColor
+
+ STX.b $9C
+ STY.b $9D
+
+ .noSpecialColor
+
+ SEP #$30 ; Set A, X, and Y in 8bit mode.
+
+ ; Don't update the CRAM until later when the overlays are loaded so
+ ; that way the BG overlays have a chance to hide the cracks.
+ ;INC.b $15
+
+ ; PLACE CUSTOM GFX LOAD HERE!
+ ;JML CheckForChangeGraphicsTransitionLoadCastle
+
+ CheckForChangeGraphicsTransitionLoadRetrun:
+
+ RTS
+
+ SkipOverworld_FinishTransGfx_firstHalf:
+
+ ; Move on to next submodule.
+ INC.b $11
+
+ RTS
+}
+
+; The following 2 functions are copied from the palettes.asm but they only
+; copied colors into the buffer so these copy colors into the normal ram as
+; well.
+Palette_OverworldBgMain2:
+{
+ REP #$21
+
+ LDA.w $0AB3 : ASL A : TAX
+
+ LDA.l $1BEC3B, X : ADC.w #$E6C8 : STA.b $00
+
+ REP #$10
+
+ ; Target BP2 through BP6 (first halves).
+ ; Each palette has 7 colors.
+ ; Load 5 palettes.
+ LDA.w #$0042
+ LDX.w #$0006
+ LDY.w #$0004
+
+ JSR.w Palette_MultiLoad2
+
+ SEP #$30
+
+ RTL
+}
+
+Palette_MultiLoad2:
+{
+ ; Description: Generally used to load multiple palettes for BGs.
+ ; Upon close inspection, one sees that this algorithm is almost the same as
+ ; the last subroutine.
+ ; Name = Palette_MultiLoad(A, X, Y).
+
+ ; Parameters: X = (number of colors in the palette - 1).
+ ; A = offset to add to $7EC300, in other words, where to write
+ ; in palette memory.
+ ; Y = (number of palettes to load - 1).
+
+ STA.b $04 ; Save the values for future reference.
+ STX.b $06
+ STY.b $08
+
+ ; The absolute address at $00 was planted in the calling function. This
+ ; value is the bank #$1B ( => D in Rom) The address is found from $0AB6 and
+ ; of course, store it at $02.
+ LDA.w #$001B : STA.b $02
+
+ .nextPalette
+ ; $0AA8 + A parameter will be the X value.
+ LDA.w $0AA8 : CLC : ADC.b $04 : TAX
+
+ LDY.b $06 ; Tell me how long the palette is.
+
+ .copyColors
+ ; We're loading A from the address set up in the calling function.
+ LDA.b [$00] : STA.l $7EC300, X : STA.l $7EC500, X
+
+ ; Increment the absolute portion of the address by two, and
+ ; decrease the color count by one.
+ INC.b $00 : INC.b $00
+
+ INX #2
+
+ ; So basically loop (Y+1) times, taking (Y * 2 bytes) to $7EC300, X.
+ DEY : BPL .copyColors
+
+ ; This technique bumps us up to the next 4bpp (16 color) palette.
+ LDA.b $04 : CLC : ADC.w #$0020 : STA.b $04
+
+ ; Decrease the number of palettes we have to load.
+ DEC.b $08
+
+ BPL .nextPalette
+
+ ; We're done loading palettes.
+
+ RTS
+}
+
+LoadTransMainGFX:
+{
+ ; Setup the decompression buffer address.
+ ; $00[3] = $7E6000
+ STZ.b $00
+ LDA.b #$40 : STA.b $01
+ LDA.b #$7E : STA.b $02
+
+ REP #$30
+ ; $0E = $8A * 8
+ LDA.b $8A : AND.w #$00FF : ASL #3 : STA.b $0E
+ SEP #$20
+
+ ; Sheet 0 (static 0)
+ LDX.b $0E
+ LDA.w Pool_OWGFXGroupTable_sheet0, X : CMP.b #$FF : BEQ .noBgGfxChange0
+ SEP #$10
+
+ TAY
+
+ JSL Decomp_bg_variableLONG
+
+ .noBgGfxChange0
+
+ SEP #$10
+ ; Increment buffer address by 0x0600.
+ LDA.b $01 : CLC : ADC.b #$06 : STA.b $01
+ REP #$10
+
+ ; Sheet 1 (static 1)
+ LDX.b $0E
+ LDA.w Pool_OWGFXGroupTable_sheet1, X : CMP.b #$FF : BEQ .noBgGfxChange1
+ SEP #$10
+
+ TAY
+
+ JSL Decomp_bg_variableLONG
+
+ .noBgGfxChange1
+
+ SEP #$10
+ ; Increment buffer address by 0x0600.
+ LDA.b $01 : CLC : ADC.b #$06 : STA.b $01
+ REP #$10
+
+ ; Sheet 2 (static 2)
+ LDX.b $0E
+ LDA.w Pool_OWGFXGroupTable_sheet2, X : CMP.b #$FF : BEQ .noBgGfxChange2
+ SEP #$10
+
+ TAY
+
+ JSL Decomp_bg_variableLONG
+
+ .noBgGfxChange2
+
+ SEP #$10
+ ; Increment buffer address by 0x0600.
+ LDA.b $01 : CLC : ADC.b #$06 : STA.b $01
+ REP #$10
+
+ ; Sheet 7 (animated)
+ LDX.b $0E
+ LDA.w Pool_OWGFXGroupTable_sheet7, X : CMP.b #$FF : BEQ .noBgGfxChange7
+ SEP #$10
+
+ TAY
+
+ JSL Decomp_bg_variableLONG
+
+ .noBgGfxChange7
+
+ RTS
+}
+
+NewPrepTransAuxGFX:
+{
+ ; Prepares the transition graphics to be transferred to VRAM during NMI.
+ ; This could occur either during this frame or any subsequent frame.
+
+ ; Set bank for source address.
+ LDA.b #$7E : STA.b $02 : STA.b $05
+
+ REP #$31
+
+ ; Source address is $7E6000, number of tiles is 0x40,
+ ; base address is $7F0000.
+ LDX.w #$0000
+ LDY.w #$0040
+ LDA.w #$4000
+
+ ; The first graphics pack always uses the higher 8 palette values.
+ JSL Do3To4High16BitLONG
+
+ ; Number of tiles for next set is 0xC0.
+ LDY.w #$00C0
+
+ LDA.b $03
+
+ JSL Do3To4Low16BitLONG
+
+ SEP #$30
+
+ RTS
+}
+pushpc
+
+; ==============================================================================
+
+if !Func0ABC5A = 1
+
+org $0ABC5A ; $053C5A
+ JSL CheckForChangeGraphicsNormalLoad
+
+warnpc $0ABC5E ; $053C5E
+
+else
+
+; Undo the function above:
+org $0ABC5A ; $053C5A
+db $22, $9B, $E1, $00
+
+endif
+
+; Loads the animated tiles after the overworld map is closed.
+pullpc
+CheckForChangeGraphicsNormalLoad:
+{
+ PHB : PHK : PLB
+
+ JSL InitTilesets ; Replaced code.
+
+ JSL ReadAnimatedTable : STA.w AnimatedTileGFXSet : DEC : TAY
+
+ ; This function is not needed here and is handled somewhere else. This
+ ; forces the game to update the animated tiles when going from one area to
+ ; another.
+ ;JSL DecompOwAnimatedTiles
+
+ ; PLACE CUSTOM GFX LOAD HERE!
+ ;JSL CheckForChangeGraphicsNormalLoadCastle
+
+ PLB
+
+ RTL
+}
+pushpc
+
+; ==============================================================================
+
+if !Func0AB8F5 = 1
+
+; Loads different animated tiles when returning from bird travel.
+org $0AB8F5 ; $0538F5
+Func0AB8F5:
+{
+ JSL ReadAnimatedTable : STA.w AnimatedTileGFXSet : DEC : TAY
+
+ ; From this point on it is the vanilla function.
+ JSL DecompOwAnimatedTiles
+ JSL Overworld_SetFixedColorAndScroll
+
+ STZ.w $0AA9
+ STZ.w $0AB2
+
+ JSL InitTilesets
+
+ INC.w $0200 ; SCAWFUL: Verify the interface submodule ID being used here.
+ ; Provides context on where in the jump table we're at.
+
+ STZ.b $B2
+
+ JSL $02B1F4 ; $0131F4 IN ROM
+
+ ; Play sound effect indicating we're coming out of map mode.
+ LDA.b #$10 : STA.w $012F
+
+ JSL LoadAmbientSound
+
+ ; If it's a different music track than was playing where we came from,
+ ; simply change to it (as opposed to setting volume back to full).
+ LDA.l $7F5B00, X : AND.b #$0F : TAX : CPX.w $0130 : BNE .different_music
+ ; Otherwise, just set the volume back to full.
+ LDX.b #$F3
+
+ .different_music
+
+ STX.w $012C
+
+ ; PLACE CUSTOM GFX LOAD HERE!
+ ;JSL CheckForChangeGraphicsNormalLoadCastle
+
+ RTL
+}
+warnpc $0AB948 ; $053948
+
+else
+
+; Undo the function above:
+org $0AB8F5 ; $0538F5
+db $A0, $58, $A5, $8A, $29, $BF, $C9, $03
+db $F0, $0A, $C9, $05, $F0, $06, $C9, $07
+db $F0, $02, $A0, $5A, $22, $94, $D3, $00
+db $22, $70, $FE, $0B, $9C, $A9, $0A, $9C
+db $B2, $0A, $22, $9B, $E1, $00, $EE, $00
+db $02, $64, $B2, $22, $F4, $B1, $02, $A9
+db $10, $8D, $2F, $01, $A6, $8A, $BF, $00
+db $5B, $7F, $4A, $4A, $4A, $4A, $8D, $2D
+db $01, $BF, $00, $5B, $7F, $29, $0F, $AA
+db $EC, $30, $01, $D0, $02, $A2, $F3, $8E
+db $2C, $01, $6B
+
+endif
+
+pullpc
+LoadAmbientSound:
+{
+ PHB : PHK : PLB
+
+ ; Reset the ambient sound effect to what it was.
+ LDX.b $8A : LDA.l $7F5B00, X : LSR #4 : STA.w $012D
+
+ ; Check if we need to stop the rain sound in the misery mire.
+ LDA.w Pool_EnableRainMireEvent : BEQ .disableRainSound
+ LDA.b $8A : CMP.b #$70 : BNE .disableRainSound
+ ; Has Misery Mire been triggered yet?
+ LDA.l $7EF2F0 : AND.b #$20 : BNE .disableRainSound
+ LDA.b #$01 : STA.w $012D
+
+ .disableRainSound
+
+ PLB
+
+ RTL
+}
+pushpc
+
+; ==============================================================================
+
+if !Func0BFEC6 = 1
+
+; Loads different special transparent colors and overlay speeds based on the
+; overlay duringtransition and under other certain cases. Exact cases need to be
+; investigated. When leaving dungeon.
+org $0BFEC6 ; $05FEC6
+Overworld_LoadBGColorAndSubscreenOverlay:
+{
+ JSL ReplaceBGColor
+
+ ; Set fixed color to neutral.
+ LDA.w #$4020 : STA.b $9C
+ LDA.w #$8040 : STA.b $9D
+
+ ; Check if we need to load the rain in the misery mire.
+ LDA.l Pool_EnableRainMireEvent : BEQ .notMire
+ LDA.b $8A : CMP.w #$0070 : BNE .notMire
+ ; Has Misery Mire been triggered yet?
+ LDA.l $7EF2F0 : AND.w #$0020 : BNE .notMire
+ JMP .subscreenOnAndReturn
+
+ .notMire
+
+ JSL ReadOverlayArray
+
+ ; Check for misery mire.
+ CMP.w #$009F : BNE .notRain
+ JMP .subscreenOnAndReturn
+
+ .notRain
+
+ ; Change the fixed color depending on our sub screen overlay.
+ ; Check for lost woods?, skull woods, and pyramid area.
+ CMP.w #$009D : BEQ .noCustomFixedColor
+ CMP.w #$0096 : BEQ .noCustomFixedColor
+ LDX.w #$4C26
+ LDY.w #$8C4C
+
+ ; Check for LW Death mountain.
+ CMP.w #$0095 : BEQ .setCustomFixedColor
+ LDX.w #$4A26
+ LDY.w #$874A
+
+ ; Check for DW Death mountain. (not turtle rock?).
+ CMP.w #$009C : BEQ .setCustomFixedColor
+ SEP #$30 ; Set A, X, and Y in 8bit mode.
+
+ ; Update CGRAM this frame.
+ INC.b $15
+
+ RTL
+
+ .setCustomFixedColor
+
+ STX.b $9C
+ STY.b $9D ; Set the fixed color addition color values.
+
+ .noCustomFixedColor
+
+ LDA.b $11 : AND.w #$00FF : CMP.w #$0004 : BEQ .BRANCH_11
+ ; Make sure BG2 and BG1 Y scroll values are synchronized.
+ ; Same for X scroll.
+ LDA.b $E8 : STA.b $E6
+ LDA.b $E2 : STA.b $E0
+
+ ; Just because I need a bit more space.
+ JSL ReadOverlayArray
+
+ ; Are we at Hyrule Castle or Pyramid of Power?
+ CMP.w #$0096 : BNE .subscreenOnAndReturn
+ JSL NeedSomeSpaceForWhateverThisIs
+
+ BRA .subscreenOnAndReturn
+
+ .BRANCH_11
+
+ ; Check for the pyramid BG.
+ JSL ReadOverlayArray : CMP.w #$0096 : BNE .subscreenOnAndReturn
+ ; Synchronize Y scrolls on BG0 and BG1. Same for X scrolls.
+ LDA.b $E8 : STA.b $E6
+ LDA.b $E2 : STA.b $E0
+
+ LDA.w $0410 : AND.w #$00FF : CMP.w #$0008 : BEQ .BRANCH_12
+ ; Handles scroll for special areas maybe?
+ LDA.w #$0838 : STA.b $E0
+
+ .BRANCH_12
+
+ LDA.w #$06C0 : STA.b $E6
+
+ .subscreenOnAndReturn
+
+ SEP #$30 ; Set A, X, and Y in 8bit mode.
+
+ ; Put BG0 on the subscreen.
+ LDA.b #$01 : STA.b $1D
+
+ ; Update palette.
+ INC.b $15
+
+ RTL
+}
+warnpc $0BFFA8 ; $05FFA8
+
+else
+
+; Undo the function above:
+org $0BFEC6 ; $05FEC6
+db $A9, $20, $40, $85, $9C, $A9, $40, $80
+db $85, $9D, $A5, $8A, $F0, $40, $C9, $70
+db $00, $D0, $03, $4C, $9D, $FF, $C9, $40
+db $00, $F0, $33, $C9, $5B, $00, $F0, $2E
+db $A2, $26, $4C, $A0, $4C, $8C, $C9, $03
+db $00, $F0, $1F, $C9, $05, $00, $F0, $1A
+db $C9, $07, $00, $F0, $15, $A2, $26, $4A
+db $A0, $4A, $87, $C9, $43, $00, $F0, $0A
+db $C9, $45, $00, $F0, $05, $E2, $30, $E6
+db $15, $6B, $86, $9C, $84, $9D, $A5, $11
+db $29, $FF, $00, $C9, $04, $00, $F0, $58
+db $A5, $E8, $85, $E6, $A5, $E2, $85, $E0
+db $A5, $8A, $29, $3F, $00, $C9, $1B, $00
+db $D0, $6D, $A5, $E2, $38, $E9, $78, $07
+db $4A, $A8, $29, $00, $40, $F0, $05, $98
+db $09, $00, $80, $A8, $84, $00, $A5, $E2
+db $38, $E5, $00, $85, $E0, $A5, $E6, $C9
+db $C0, $06, $90, $17, $38, $E9, $00, $06
+db $29, $FF, $03, $C9, $80, $01, $B0, $06
+db $4A, $09, $00, $06, $80, $0E, $A9, $C0
+db $06, $80, $09, $A5, $E6, $29, $FF, $00
+db $4A, $09, $00, $06, $85, $E6, $80, $27
+db $A5, $8A, $29, $3F, $00, $C9, $1B, $00
+db $D0, $1D, $A5, $E8, $85, $E6, $A5, $E2
+db $85, $E0, $AD, $10, $04, $29, $FF, $00
+db $C9, $08, $00, $F0, $05, $A9, $38, $08
+db $85, $E0, $A9, $C0, $06, $85, $E6, $E2
+db $20, $A9, $01, $85, $1D, $E2, $30, $E6
+db $15, $6B
+
+endif
+
+pullpc
+ReplaceBGColor:
+{
+ PHB : PHK : PLB
+
+ ; TODO: This may need to check if we are in a warp and then if so load the
+ ; custom color. If not, then chceck if its enabled or not.
+ ;SEP #$20 ; Set A in 8bit mode.
+
+ ;LDA.w Pool_EnableBGColor : BNE .custom
+ ;REP #$20 ; Set A in 16bit mode.
+
+ ;PLB
+
+ ;RTL
+
+ ;.custom
+
+ ;REP #$20 ; Set A in 16bit mode.
+
+ LDA.b $8A : ASL : TAX ; Get area code and times it by 2.
+ LDA.w Pool_BGColorTable, X ; Get the color.
+
+ ;STA.l $7EC300 : STA.l $7EC340 ; Set the BG color.
+ STA.l $7EC500 : STA.l $7EC540
+
+ PLB
+
+ RTL
+}
+
+; TODO: Doccument this better and fiture out what it actually does.
+NeedSomeSpaceForWhateverThisIs:
+{
+ LDA.b $E2 : SEC : SBC.w #$0778 : LSR A : TAY : AND.w #$4000 : BEQ .BRANCH_7
+ TYA : ORA.w #$8000 : TAY
+
+ .BRANCH_7
+
+ STY.b $00
+
+ LDA.b $E2 : SEC : SBC.b $00 : STA.b $E0
+
+ LDA.b $E6 : CMP.w #$06C0 : BCC .BRANCH_9
+ SEC : SBC.w #$0600 : AND.w #$03FF : CMP.w #$0180 : BCS .BRANCH_8
+ LSR A : ORA.w #$0600
+
+ BRA .BRANCH_10
+
+ .BRANCH_8
+
+ LDA.w #$06C0
+
+ BRA .BRANCH_10
+
+ .BRANCH_9
+
+ LDA.b $E6 : AND.w #$00FF : LSR A : ORA.w #$0600
+
+ .BRANCH_10
+
+ ; Set BG1 vertical scroll.
+ STA.b $E6
+
+ RTL
+}
+pushpc
+
+; ==============================================================================
+
+if !Func0ED627 = 1
+
+; Loads the transparent color under some load conditions such as the mirror\
+; warp.
+; TODO: Investigate the other conditions. Exiting dungeons.
+org $0ED627 ; $075627
+ JML InitColorLoad2
+ NOP
+
+warnpc $0ED62C ; $07562C
+
+else
+
+; Undo the function above:
+org $0ED627 ; $075627
+db $A5, $8A, $C9, $80, $00
+
+endif
+
+org $0ED652
+InitColorLoad2_Return:
+
+pullpc
+InitColorLoad2:
+{
+ PHB : PHK : PLB
+
+ ; $0181 is the exit room number used for getting into the under the bridge
+ ; area.
+ LDA.b $A0 : CMP.w #$0181 : BNE .notBridge
+ LDA.w Pool_BGColorTable_Bridge
+
+ BRA .storeColor
+
+ .notBridge
+
+ LDA.b $8A : ASL : TAX ; Get area code and times it by 2.
+ LDA.w Pool_BGColorTable, X ; Get the color.
+
+ .storeColor
+
+ STA.l $7EC300 : STA.l $7EC340 ; Set transparent color.
+ ;STA.l $7EC500 : STA.l $7EC540
+
+ INC.b $15
+
+ PLB
+
+ JML InitColorLoad2_Return
+}
+pushpc
+
+; ==============================================================================
+
+if !Func0ED8AE = 1
+
+; Resets the area special color after the screen flashes.
+org $0ED8AE ; $0758AE
+Func0ED8AE:
+{
+ LDA.b $1B : BNE .noSpecialColor
+ REP #$30 ; Set A, X, and Y in 16bit mode.
+
+ LDX.w #$4020 : STX.b $9C
+ LDX.w #$8040 : STX.b $9D
+
+ LDX.w #$4F33
+ LDY.w #$894F
+
+ ; Change the fixed color depending on our sub screen overlay.
+ ; Lost woods and skull woods.
+ JSL ReadOverlayArray : CMP.w #$009D : BEQ .noSpecialColor
+ CMP.w #$0040 : BEQ .noSpecialColor
+ ; Pyramid area.
+ CMP.w #$0096 : BEQ .specialColor
+ LDX.w #$4C26
+ LDY.w #$8C4C
+
+ ; LW death mountain.
+ CMP.w #$0095 : BEQ .specialColor
+ LDX.w #$4A26
+ LDY.w #$874A
+
+ ; DW death mountain.
+ CMP.w #$009C : BEQ .specialColor
+ BRA .noSpecialColor
+
+ .specialColor
+
+ STX.b $9C
+ STY.b $9D
+
+ .noSpecialColor
+
+ SEP #$30 ; Set A, X, and Y in 8bit mode.
+
+ RTL
+}
+warnpc $0ED8FB ; $0758FB
+
+else
+
+; Undo the function above:
+org $0ED8AE ; $0758AE
+db $A5, $1B, $D0, $46, $C2, $10, $A2, $20
+db $40, $86, $9C, $A2, $40, $80, $86, $9D
+db $A2, $33, $4F, $A0, $4F, $89, $A5, $8A
+db $F0, $30, $C9, $40, $F0, $2C, $C9, $5B
+db $F0, $24, $A2, $26, $4C, $A0, $4C, $8C
+db $C9, $03, $F0, $1A, $C9, $05, $F0, $16
+db $C9, $07, $F0, $12, $A2, $26, $4A, $A0
+db $4A, $87, $C9, $43, $F0, $08, $C9, $45
+db $F0, $04, $C9, $47, $D0, $04, $86, $9C
+db $84, $9D, $E2, $10, $6B
+
+endif
+
+; ==============================================================================
+
+if !Func00E133 = 1
+
+; Interupts the vanilla LoadTransAuxGFX function
+org $00D673 ; $005673
+ JML NewLoadTransAuxGFX
+
+warnpc $00D677 ; $005677
+
+org $008C8A ; $000C8A
+dw NMI_UpdateChr_Bg2HalfAndAnimated
+
+warnpc $00D677 ; $005677
+
+; Replaces the UNREACHABLE_00D585 which is unused.
+org $00D585 ; $005585
+Decomp_bg_variableLONG:
+{
+ PHB : PHK : PLB
+
+ JSR Decomp_bg_variable
+
+ PLB
+
+ RTL
+}
+
+Do3To4Low16BitLONG:
+{
+ PHB : PHK : PLB
+
+ JSR.w Do3To4Low16Bit
+
+ PLB
+
+ RTL
+}
+
+Do3To4High16BitLONG:
+{
+ PHB : PHK : PLB
+
+ JSR.w Do3To4High16Bit
+
+ PLB
+
+ RTL
+}
+
+NMI_UpdateChr_Bg2HalfAndAnimated:
+{
+ JSL NMI_UpdateChr_Bg2HalfAndAnimatedLONG
+
+ RTS
+}
+
+warnpc $00D5CB ; $0055CB
+
+else
+
+; Undo the functions above:
+org $00D673 ; $005673
+db $A9, $60, $85, $01
+
+org $008C8A ; $000C8A
+db $4B, $8E
+
+; This is the old 0AA1 table. Will eventually be unused so I'll leave this here
+; for future use.
+org $00E073 ; $006073
+db $00, $01, $10, $06, $0E, $1F, $18, $0F
+db $00, $01, $10, $08, $0E, $22, $1B, $0F
+db $00, $01, $10, $06, $0E, $1F, $18, $0F
+db $00, $01, $13, $07, $0E, $23, $1C, $0F
+db $00, $01, $10, $07, $0E, $21, $18, $0F
+db $00, $01, $10, $09, $0E, $20, $19, $0F
+db $02, $03, $12, $0B, $0E, $21, $1A, $0F
+db $00, $01, $11, $0C, $0E, $24, $1B, $0F
+db $00, $01, $11, $08, $0E, $22, $1B, $0F
+db $00, $01, $11, $0C, $0E, $25, $1A, $0F
+db $00, $01, $11, $0C, $0E, $26, $1B, $0F
+db $00, $01, $14, $0A, $0E, $27, $1D, $0F
+db $00, $01, $11, $0A, $0E, $28, $1E, $0F
+db $02, $03, $12, $0B, $0E, $29, $16, $0F
+db $00, $01, $15, $0D, $0E, $2A, $18, $0F
+db $00, $01, $10, $07, $0E, $23, $1C, $0F
+db $00, $01, $13, $07, $0E, $04, $05, $0F
+db $00, $01, $13, $07, $0E, $04, $05, $0F
+db $00, $01, $10, $09, $0E, $20, $1B, $0F
+db $00, $01, $10, $09, $0E, $2A, $17, $0F
+db $02, $03, $12, $0B, $0E, $21, $1C, $0F
+db $00, $08, $11, $1B, $22, $2E, $5D, $5B
+db $00, $08, $10, $18, $20, $2B, $5D, $5B
+db $00, $08, $10, $18, $20, $2B, $5D, $5B
+db $3A, $3B, $3C, $3D, $53, $4D, $3E, $5B
+db $42, $43, $44, $45, $20, $2B, $3F, $5D
+db $00, $08, $10, $18, $20, $2B, $5D, $5B
+db $00, $08, $10, $18, $20, $2B, $5D, $5B
+db $00, $08, $10, $18, $20, $2B, $5D, $5B
+db $00, $08, $10, $18, $20, $2B, $5D, $5B
+db $00, $08, $10, $18, $20, $2B, $5D, $5B
+db $71, $72, $71, $72, $20, $2B, $5D, $5B
+db $3A, $3B, $3C, $3D, $53, $4D, $3E, $5B
+db $42, $43, $44, $45, $20, $2B, $3F, $59
+db $00, $72, $71, $72, $20, $2B, $5D, $0F
+db $16, $39, $1D, $17, $40, $41, $39, $1E
+db $00, $46, $39, $72, $40, $41, $39, $0F
+
+org $00D585 ; $005585
+db $A0, $08, $00, $84, $0E, $85, $00, $18
+db $69, $10, $00, $85, $03, $A0, $07, $00
+db $A7, $00, $9F, $00, $90, $7E, $E6, $00
+db $E6, $00, $A7, $03, $29, $FF, $00, $9F
+db $10, $90, $7E, $E6, $03, $E8, $E8, $88
+db $10, $E6, $8A, $18, $69, $10, $00, $AA
+db $A5, $03, $29, $78, $00, $D0, $08, $A5
+db $03, $18, $69, $80, $01, $85, $03, $A5
+db $03, $C6, $0E, $D0, $C0, $60
+
+endif
+
+pullpc
+
+NewLoadTransAuxGFX:
+{
+ PHB : PHK : PLB
+
+ LDA.w Pool_EnableTransitionGFXGroupLoad : BNE .notNormalLoad
+ ; Replaced code:
+ LDA.b #$60 : STA.b $01
+
+ PLB
+
+ JML $00D677 ; $005677 Return to regular code.
+
+ .notNormalLoad
+
+ ; Setup the decompression buffer address.
+ ; $00[3] = $7E6000
+ STZ.b $00
+ LDA.b #$60 : STA.b $01
+ LDA.b #$7E : STA.b $02
+
+ REP #$30
+ ; $0E = $8A * 8
+ LDA.b $8A : AND.w #$00FF : ASL #3 : STA.b $0E
+ SEP #$20
+
+ ; Sheet 3 (variable 0)
+ LDX.b $0E
+ LDA.w Pool_OWGFXGroupTable_sheet3, X : CMP.b #$FF : BEQ .noBgGfxChange3
+ SEP #$10
+
+ TAY
+
+ JSL Decomp_bg_variableLONG
+
+ .noBgGfxChange3
+
+ SEP #$10
+ ; Increment buffer address by 0x0600.
+ LDA.b $01 : CLC : ADC.b #$06 : STA.b $01
+ REP #$10
+
+ ; Sheet 4 (variable 1)
+ LDX.b $0E
+ LDA.w Pool_OWGFXGroupTable_sheet4, X : CMP.b #$FF : BEQ .noBgGfxChange4
+ SEP #$10
+
+ TAY
+
+ JSL Decomp_bg_variableLONG
+
+ .noBgGfxChange4
+
+ SEP #$10
+ ; Increment buffer address by 0x0600.
+ LDA.b $01 : CLC : ADC.b #$06 : STA.b $01
+ REP #$10
+
+ ; Sheet 5 (variable 2)
+ LDX.b $0E
+ LDA.w Pool_OWGFXGroupTable_sheet5, X : CMP.b #$FF : BEQ .noBgGfxChange5
+ SEP #$10
+
+ TAY
+
+ JSL Decomp_bg_variableLONG
+
+ .noBgGfxChange5
+
+ SEP #$10
+ ; Increment buffer address by 0x0600.
+ LDA.b $01 : CLC : ADC.b #$06 : STA.b $01
+ REP #$10
+
+ ; Sheet 6 (variable 3)
+ LDX.b $0E
+ LDA.w Pool_OWGFXGroupTable_sheet6, X : CMP.b #$FF : BEQ .noBgGfxChange6
+ SEP #$10
+
+ TAY
+
+ JSL Decomp_bg_variableLONG
+
+ .noBgGfxChange6
+
+ SEP #$10
+ ; Increment buffer address by 0x0600.
+ LDA.b $01 : CLC : ADC.b #$06 : STA.b $01
+ REP #$10
+
+ STZ.w TransGFXModuleIndex
+
+ PLB
+
+ JML LoadTransAuxGFX_sprite_continue ; $005706 Return to regular code.
+}
+
+NMI_UpdateChr_Bg2HalfAndAnimatedLONG:
+{
+ PHB : PHK : PLB
+
+ REP #$20
+
+ ; Sheet 1
+ ; Set VRAM target to $3000 (word).
+ LDA.w #$2800 : STA.w $2116
+
+ ; Increment on writes to $2119.
+ LDY.b #$80 : STY.w $2115
+
+ ; Target is $2118, write two registers once ($2118 / $2119).
+ LDA.w #$1801 : STA.w $4300
+
+ ; Source address is $7F1000.
+ LDA.w #$1000 : STA.w $4302
+ LDY.b #$7F : STY.w $4304
+
+ ; Write 0x0800 bytes.
+ LDA.w #$0800 : STA.w $4305
+
+ ; Transfer data on channel 1.
+ LDY.b #$01 : STY.w $420B
+
+ ; Sheet 2
+ ; Set VRAM target to $3000 (word).
+ LDA.w #$3E00 : STA.w $2116
+
+ ; Increment on writes to $2119.
+ LDY.b #$80 : STY.w $2115
+
+ ; Target is $2118, write two registers once ($2118 / $2119).
+ LDA.w #$1801 : STA.w $4300
+
+ ; Only copy the latter half of the sheet to prevent the animated tiles from
+ ; flickering on transition.
+ ; Source address is $7F1C00.
+ LDA.w #$1C00 : STA.w $4302
+ LDY.b #$7F : STY.w $4304
+
+ ; Write 0x08400 bytes.
+ LDA.w #$0400 : STA.w $4305
+
+ ; Transfer data on channel 1.
+ LDY.b #$01 : STY.w $420B
+
+ SEP #$20
+
+ STZ.w $0710
+
+ PLB
+
+ RTL
+}
+pushpc
+
+; ==============================================================================
+
+if !Func00E221 = 1
+
+org $00E221 ; $006221
+ JML InitTilesetsLongCalls
+
+warnpc $00E225 ; $006225
+
+org $00D904 ; $005904
+ JML AnimateMirrorWarp_DecompressNewTileSetsLongCalls
+
+warnpc $00D908 ; $005908
+
+org $00D97D ; $00597D
+ JML AnimateMirrorWarp_DecompressNewTileSetsLongCalls2
+
+warnpc $00D981 ; $005981
+
+org $00D9BC ; $0059BC
+ JML AnimateMirrorWarp_DecompressBackgroundsALongCalls
+
+warnpc $00D9C1 ; $0059C1
+
+org $00DA2F ; $005A2F
+ JML AnimateMirrorWarp_DecompressBackgroundsCLongCalls
+
+else
+
+; Undo the functions above:
+org $00E221 ; $006221
+db $AD, $A1, $0A, $29
+
+org $00D904 ; $005904
+db $AD, $A1, $0A, $29
+
+org $00D97D ; $00597D
+db $BF, $EF, $D8, $00
+
+org $00D9BC ; $0059BC
+db $BF, $F1, $D8, $00
+
+org $00DA2F ; $005A2F
+db $BF, $F3, $D8, $00
+
+endif
+
+pullpc
+InitTilesetsLongCalls:
+{
+ PHB : PHK : PLB
+
+ SEP #$20
+ ; TODO: This will eventually be changed when changing the dungeon GFX.
+ ; Only trigger the new code when in the:
+ LDA.b $10 : CMP.b #$08 : BEQ .outdoors ; Pre-overworld main module
+ CMP.b #$0E : BEQ .outdoors ; Text Mode/Item Screen/Map module
+ REP #$30
+ LDA.w $0AA1 : AND.w #$00FF ; Replaced code.
+
+ PLB
+
+ JML $00E227 ; $006227 Return to normal code.
+
+ .outdoors
+
+ REP #$30
+ LDA.b $8A : AND.w #$00FF : ASL #3 : TAX
+ LDA.b $8A : AND.w #$00C0 : LSR #3 : TAY ; (Area / 8) = LW, DW, or SW *8
+ SEP #$20
+
+ LDA.w Pool_OWGFXGroupTable_sheet0, X : CMP.b #$FF : BNE .notFF0
+ LDA.w Pool_DefaultGFXGroups_sheet0, Y
+
+ .notFF0
+
+ STA.b $0D
+
+ LDA.w Pool_OWGFXGroupTable_sheet1, X : CMP.b #$FF : BNE .notFF1
+ LDA.w Pool_DefaultGFXGroups_sheet1, Y
+
+ .notFF1
+
+ STA.b $0C
+
+ LDA.w Pool_OWGFXGroupTable_sheet2, X : CMP.b #$FF : BNE .notFF2
+ LDA.w Pool_DefaultGFXGroups_sheet2, Y
+
+ .notFF2
+
+ STA.b $0B
+
+ LDA.w Pool_OWGFXGroupTable_sheet3, X : CMP.b #$FF : BNE .notFF3
+ LDA.w Pool_DefaultGFXGroups_sheet3, Y
+
+ .notFF3
+
+ STA.l $7EC2F8 : STA.b $0A
+
+ LDA.w Pool_OWGFXGroupTable_sheet4, X : CMP.b #$FF : BNE .notFF4
+ LDA.w Pool_DefaultGFXGroups_sheet4, Y
+
+ .notFF4
+
+ STA.l $7EC2F9 : STA.b $09
+
+ LDA.w Pool_OWGFXGroupTable_sheet5, X : CMP.b #$FF : BNE .notFF5
+ LDA.w Pool_DefaultGFXGroups_sheet5, Y
+
+ .notFF5
+
+ STA.l $7EC2FA : STA.b $08
+
+ LDA.w Pool_OWGFXGroupTable_sheet6, X : CMP.b #$FF : BNE .notFF6
+ LDA.w Pool_DefaultGFXGroups_sheet6, Y
+
+ .notFF6
+
+ STA.l $7EC2FB : STA.b $07
+
+ LDA.w Pool_OWGFXGroupTable_sheet7, X : CMP.b #$FF : BNE .notFF7
+ LDA.w Pool_DefaultGFXGroups_sheet7, Y
+
+ .notFF7
+
+ STA.b $06
+
+ PLB
+
+ JML $00E282 ; $006282 Skip normal sheet load.
+}
+
+AnimateMirrorWarp_DecompressNewTileSetsLongCalls:
+{
+ PHB : PHK : PLB
+
+ LDA.b $8A : AND.w #$00FF : ASL #3 : TAX
+ LDA.b $8A : AND.w #$00C0 : LSR #3 : TAY ; (Area / 8) = LW, DW, or SW *8
+ SEP #$20
+
+ LDA.w Pool_OWGFXGroupTable_sheet3, X : CMP.b #$FF : BNE .notFF3
+ LDA.w Pool_DefaultGFXGroups_sheet3, Y
+
+ .notFF3
+
+ STA.l $7EC2F8
+
+ LDA.w Pool_OWGFXGroupTable_sheet4, X : CMP.b #$FF : BNE .notFF4
+ LDA.w Pool_DefaultGFXGroups_sheet4, Y
+
+ .notFF4
+
+ STA.l $7EC2F9
+
+ LDA.w Pool_OWGFXGroupTable_sheet5, X : CMP.b #$FF : BNE .notFF5
+ LDA.w Pool_DefaultGFXGroups_sheet5, Y
+
+ .notFF5
+
+ STA.l $7EC2FA
+
+ LDA.w Pool_OWGFXGroupTable_sheet6, X : CMP.b #$FF : BNE .notFF6
+ LDA.w Pool_DefaultGFXGroups_sheet6, Y
+
+ .notFF6
+
+ STA.l $7EC2FB
+
+ PLB
+
+ JML $00D949 ; $005949 Skip normal sheet load.
+}
+
+AnimateMirrorWarp_DecompressNewTileSetsLongCalls2:
+{
+ PHB : PHK : PLB
+
+ REP #$30
+ LDA.b $8A : AND.w #$00FF : ASL #3 : TAX
+ LDA.b $8A : AND.w #$00C0 : LSR #3 : TAY ; (Area / 8) = LW, DW, or SW *8
+ SEP #$20
+
+ LDA.w Pool_OWGFXGroupTable_sheet1, X : CMP.b #$FF : BNE .notFF1
+ LDA.w Pool_DefaultGFXGroups_sheet1, Y
+
+ .notFF1
+
+ STA.b $08
+
+ LDA.w Pool_OWGFXGroupTable_sheet0, X : CMP.b #$FF : BNE .notFF0
+ LDA.w Pool_DefaultGFXGroups_sheet0, Y
+
+ .notFF0
+
+ TAY
+
+ SEP #$10
+
+ PLB
+
+ JML $00D988 ; $005988 Skip normal sheet load.
+}
+
+AnimateMirrorWarp_DecompressBackgroundsALongCalls:
+{
+ PHB : PHK : PLB
+
+ REP #$30
+ LDA.b $8A : AND.w #$00FF : ASL #3 : TAX
+ LDA.b $8A : AND.w #$00C0 : LSR #3 : TAY ; (Area / 8) = LW, DW, or SW *8
+ SEP #$20
+
+ LDA.w Pool_OWGFXGroupTable_sheet3, X : CMP.b #$FF : BNE .notFF3
+ LDA.w Pool_DefaultGFXGroups_sheet3, Y
+
+ .notFF3
+
+ STA.b $08
+
+ LDA.w Pool_OWGFXGroupTable_sheet2, X : CMP.b #$FF : BNE .notFF2
+ LDA.w Pool_DefaultGFXGroups_sheet2, Y
+
+ .notFF2
+
+ TAY
+
+ SEP #$10
+
+ PLB
+
+ JML $00D9C7 ; $0059C7 Skip normal sheet load.
+}
+
+AnimateMirrorWarp_DecompressBackgroundsCLongCalls:
+{
+ PHB : PHK : PLB
+
+ REP #$30
+ LDA.b $8A : AND.w #$00FF : ASL #3 : TAX
+ LDA.b $8A : AND.w #$00C0 : LSR #3 : TAY ; (Area / 8) = LW, DW, or SW *8
+ SEP #$20
+
+ LDA.w Pool_OWGFXGroupTable_sheet7, X : CMP.b #$FF : BNE .notFF7
+ LDA.w Pool_DefaultGFXGroups_sheet7, Y
+
+ .notFF7
+
+ STA.b $08 : STA AnimatedTileGFXSet
+
+ LDA.w Pool_OWGFXGroupTable_sheet6, X : CMP.b #$FF : BNE .notFF6
+ LDA.w Pool_DefaultGFXGroups_sheet6, Y
+
+ .notFF6
+
+ TAY
+
+ SEP #$10
+
+ PLB
+
+ JML $00DA3A ; $005A3A Skip normal sheet load.
+}
+pushpc
+
+; ==============================================================================
+
+; A second pullpc is needed here just in case someone incorperates this ASM into
+; their own code base.
+pullpc
+pullpc
diff --git a/assets/asm/yaze.asm b/assets/asm/yaze.asm
new file mode 100644
index 00000000..1bfd2eb2
--- /dev/null
+++ b/assets/asm/yaze.asm
@@ -0,0 +1,22 @@
+; =========================================================
+; yaze custom assembly code
+; =========================================================
+
+namespace yaze
+{
+
+!YAZE_CUSTOM_MOSAIC = 1
+
+
+if !YAZE_CUSTOM_MOSAIC != 0
+ incsrc "mosaic_change.asm"
+endif
+
+!ZS_CUSTOM_OVERWORLD = 1
+
+if !ZS_CUSTOM_OVERWORLD != 0
+ incsrc "ZSCustomOverworld.asm"
+endif
+
+}
+namespace off
\ No newline at end of file
diff --git a/assets/yaze.png b/assets/yaze.png
new file mode 100644
index 00000000..02214576
Binary files /dev/null and b/assets/yaze.png differ
diff --git a/cmake/absl.cmake b/cmake/absl.cmake
index 42fed470..fed9eb81 100644
--- a/cmake/absl.cmake
+++ b/cmake/absl.cmake
@@ -1,4 +1,8 @@
+if (MINGW)
+ add_subdirectory(src/lib/abseil-cpp)
+else()
find_package(absl)
+endif()
set(ABSL_PROPAGATE_CXX_STD ON)
set(ABSL_CXX_STANDARD 17)
set(ABSL_USE_GOOGLETEST_HEAD ON)
diff --git a/cmake/asar.cmake b/cmake/asar.cmake
index 278a264a..26548cdb 100644
--- a/cmake/asar.cmake
+++ b/cmake/asar.cmake
@@ -1,33 +1,39 @@
-get_target_property(ASAR_INCLUDE_DIR asar-static INCLUDE_DIRECTORIES)
-target_include_directories(asar-static PRIVATE ${ASAR_INCLUDE_DIR})
+# Asar Assembler for 65816 SNES Assembly
+add_subdirectory(src/lib/asar/src)
+
set(ASAR_GEN_EXE OFF)
set(ASAR_GEN_DLL ON)
set(ASAR_GEN_LIB ON)
set(ASAR_GEN_EXE_TEST OFF)
set(ASAR_GEN_DLL_TEST OFF)
+set(ASAR_STATIC_SRC_DIR "${CMAKE_SOURCE_DIR}/src/lib/asar/src/asar")
+
+get_target_property(ASAR_INCLUDE_DIR asar-static INCLUDE_DIRECTORIES)
+list(APPEND ASAR_INCLUDE_DIR "${CMAKE_SOURCE_DIR}/src/lib/asar/src")
+target_include_directories(asar-static PRIVATE ${ASAR_INCLUDE_DIR})
set(ASAR_STATIC_SRC
- "../src/lib/asar/src/asar/interface-lib.cpp"
- "../src/lib/asar/src/asar/addr2line.cpp"
- "../src/lib/asar/src/asar/arch-65816.cpp"
- "../src/lib/asar/src/asar/arch-spc700.cpp"
- "../src/lib/asar/src/asar/arch-superfx.cpp"
- "../src/lib/asar/src/asar/assembleblock.cpp"
- "../src/lib/asar/src/asar/crc32.cpp"
- "../src/lib/asar/src/asar/libcon.cpp"
- "../src/lib/asar/src/asar/libsmw.cpp"
- "../src/lib/asar/src/asar/libstr.cpp"
- "../src/lib/asar/src/asar/macro.cpp"
- "../src/lib/asar/src/asar/main.cpp"
- "../src/lib/asar/src/asar/asar_math.cpp"
- "../src/lib/asar/src/asar/virtualfile.cpp"
- "../src/lib/asar/src/asar/warnings.cpp"
- "../src/lib/asar/src/asar/errors.cpp"
- "../src/lib/asar/src/asar/platform/file-helpers.cpp"
+ "${ASAR_STATIC_SRC_DIR}/interface-lib.cpp"
+ "${ASAR_STATIC_SRC_DIR}/addr2line.cpp"
+ "${ASAR_STATIC_SRC_DIR}/arch-65816.cpp"
+ "${ASAR_STATIC_SRC_DIR}/arch-spc700.cpp"
+ "${ASAR_STATIC_SRC_DIR}/arch-superfx.cpp"
+ "${ASAR_STATIC_SRC_DIR}/assembleblock.cpp"
+ "${ASAR_STATIC_SRC_DIR}/crc32.cpp"
+ "${ASAR_STATIC_SRC_DIR}/libcon.cpp"
+ "${ASAR_STATIC_SRC_DIR}/libsmw.cpp"
+ "${ASAR_STATIC_SRC_DIR}/libstr.cpp"
+ "${ASAR_STATIC_SRC_DIR}/macro.cpp"
+ "${ASAR_STATIC_SRC_DIR}/main.cpp"
+ "${ASAR_STATIC_SRC_DIR}/asar_math.cpp"
+ "${ASAR_STATIC_SRC_DIR}/virtualfile.cpp"
+ "${ASAR_STATIC_SRC_DIR}/warnings.cpp"
+ "${ASAR_STATIC_SRC_DIR}/errors.cpp"
+ "${ASAR_STATIC_SRC_DIR}/platform/file-helpers.cpp"
)
if(WIN32 OR MINGW)
- list(APPEND ASAR_STATIC_SRC "../src/lib/asar/src/asar/platform/windows/file-helpers-win32.cpp")
+ list(APPEND ASAR_STATIC_SRC "${ASAR_STATIC_SRC_DIR}/platform/windows/file-helpers-win32.cpp")
else()
- list(APPEND ASAR_STATIC_SRC "../src/lib/asar/src/asar/platform/linux/file-helpers-linux.cpp")
+ list(APPEND ASAR_STATIC_SRC "${ASAR_STATIC_SRC_DIR}/platform/linux/file-helpers-linux.cpp")
endif()
\ No newline at end of file
diff --git a/cmake/gtest.cmake b/cmake/gtest.cmake
new file mode 100644
index 00000000..6d534a93
--- /dev/null
+++ b/cmake/gtest.cmake
@@ -0,0 +1,11 @@
+# GoogleTest ------------------------------------------------------------------
+include(FetchContent)
+FetchContent_Declare(
+ googletest
+ URL https://github.com/google/googletest/archive/03597a01ee50ed33e9dfd640b249b4be3799d395.zip
+)
+
+# For Windows: Prevent overriding the parent project's compiler/linker settings
+set(gtest_force_shared_crt ON CACHE BOOL "" FORCE)
+FetchContent_MakeAvailable(googletest)
+enable_testing()
\ No newline at end of file
diff --git a/cmake/imgui.cmake b/cmake/imgui.cmake
index 2e7cf648..bde97cee 100644
--- a/cmake/imgui.cmake
+++ b/cmake/imgui.cmake
@@ -10,24 +10,18 @@ target_compile_definitions(ImGui PUBLIC
set(IMGUI_FILE_DLG_PATH ${CMAKE_SOURCE_DIR}/src/lib/ImGuiFileDialog)
file(GLOB IMGUI_FILE_DLG_SOURCES ${IMGUI_FILE_DLG_PATH}/*.cpp)
add_library("ImGuiFileDialog" STATIC ${IMGUI_FILE_DLG_SOURCES})
-target_include_directories(ImGuiFileDialog PUBLIC ${IMGUI_PATH})
-target_compile_definitions(ImGuiFileDialog PUBLIC
- IMGUI_IMPL_OPENGL_LOADER_CUSTOM= GL_GLEXT_PROTOTYPES=1)
+target_include_directories(ImGuiFileDialog PUBLIC ${IMGUI_PATH} ${CMAKE_SOURCE_DIR}/src/lib)
set(IMGUI_COLOR_TEXT_EDIT_PATH ${CMAKE_SOURCE_DIR}/src/lib/ImGuiColorTextEdit)
file(GLOB IMGUI_COLOR_TEXT_EDIT_SOURCES ${IMGUI_COLOR_TEXT_EDIT_PATH}/*.cpp)
add_library("ImGuiColorTextEdit" STATIC ${IMGUI_COLOR_TEXT_EDIT_SOURCES})
-target_include_directories(ImGuiColorTextEdit PUBLIC ${IMGUI_PATH})
-target_compile_definitions(ImGuiColorTextEdit PUBLIC
- IMGUI_IMPL_OPENGL_LOADER_CUSTOM= GL_GLEXT_PROTOTYPES=1)
+target_include_directories(ImGuiColorTextEdit PUBLIC ${IMGUI_PATH} ${CMAKE_SOURCE_DIR}/src/lib)
set(IMGUI_TEST_ENGINE_PATH ${CMAKE_SOURCE_DIR}/src/lib/imgui_test_engine/imgui_test_engine)
file(GLOB IMGUI_TEST_ENGINE_SOURCES ${IMGUI_TEST_ENGINE_PATH}/*.cpp)
add_library("ImGuiTestEngine" STATIC ${IMGUI_TEST_ENGINE_SOURCES})
-target_include_directories(ImGuiTestEngine PUBLIC ${IMGUI_PATH})
+target_include_directories(ImGuiTestEngine PUBLIC ${IMGUI_PATH} ${CMAKE_SOURCE_DIR}/src/lib)
target_link_libraries(ImGuiTestEngine PUBLIC ImGui)
-target_compile_definitions(ImGuiTestEngine PUBLIC
- IMGUI_IMPL_OPENGL_LOADER_CUSTOM= GL_GLEXT_PROTOTYPES=1)
set(
IMGUI_SRC
@@ -40,4 +34,7 @@ set(
${IMGUI_PATH}/misc/cpp/imgui_stdlib.cpp
${IMGUI_FILE_DLG_PATH}/ImGuiFileDialog.cpp
${IMGUI_COLOR_TEXT_EDIT_PATH}/TextEditor.cpp
-)
\ No newline at end of file
+)
+
+# For integration test
+add_definitions("-DIMGUI_ENABLE_TEST_ENGINE -DIMGUI_TEST_ENGINE_ENABLE_COROUTINE_STDTHREAD_IMPL=1")
\ No newline at end of file
diff --git a/cmake/mingw64.cmake b/cmake/mingw64.cmake
new file mode 100644
index 00000000..a172fdf1
--- /dev/null
+++ b/cmake/mingw64.cmake
@@ -0,0 +1,21 @@
+# cmake -DCMAKE_TOOLCHAIN_FILE=./cmake/mingw64.cmake -B build/build-windows && cmake --build ./build/build-windows
+
+set(CMAKE_SYSTEM_NAME Windows)
+set(CMAKE_SYSTEM_VERSION 1)
+set(CMAKE_SYSTEM_PROCESSOR x86_64)
+
+set(CMAKE_C_COMPILER x86_64-w64-mingw32-gcc)
+set(CMAKE_CXX_COMPILER x86_64-w64-mingw32-g++)
+set(CMAKE_RC_COMPILER x86_64-w64-mingw32-windres)
+
+set(CMAKE_FIND_ROOT_PATH /usr/local/opt/mingw-w64)
+
+set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
+set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
+set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)
+set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY)
+
+set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -O2 -pipe -g -feliminate-unused-debug-types")
+
+# Static link the C++ standard library
+set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -static -static-libgcc -static-libstdc++")
\ No newline at end of file
diff --git a/cmake/sdl2.cmake b/cmake/sdl2.cmake
index d3464c60..94cc6531 100644
--- a/cmake/sdl2.cmake
+++ b/cmake/sdl2.cmake
@@ -1,6 +1,27 @@
# SDL2
-if (UNIX)
+if (UNIX OR MINGW)
add_subdirectory(src/lib/SDL)
else()
find_package(SDL2)
+endif()
+
+set(SDL_TARGETS SDL2::SDL2)
+
+if(WIN32 OR MINGW)
+ list(PREPEND SDL_TARGETS SDL2::SDL2main ws2_32)
+ add_definitions(-DSDL_MAIN_HANDLED)
+endif()
+
+# libpng
+if (MINGW)
+ set(ZLIB_ROOT ${CMAKE_SOURCE_DIR}/build-windows/src/lib/zlib)
+ set(ZLIB_LIBRARY ${CMAKE_SOURCE_DIR}/build-windows/lib/libzlib.dll.a)
+ include_directories(${CMAKE_SOURCE_DIR}/src/lib/zlib ${CMAKE_SOURCE_DIR}/src/lib/libpng)
+ set(ZLIB_INCLUDE_DIR ${CMAKE_SOURCE_DIR}/src/lib/zlib)
+ set(YAZE_BUILD_PYTHON OFF)
+ set(YAZE_BUILD_EXTENSIONS OFF)
+ add_subdirectory(src/lib/zlib)
+ add_subdirectory(src/lib/libpng)
+else()
+ find_package(PNG REQUIRED)
endif()
\ No newline at end of file
diff --git a/docs/asm-style-guide.md b/docs/asm-style-guide.md
new file mode 100644
index 00000000..1d2d94b2
--- /dev/null
+++ b/docs/asm-style-guide.md
@@ -0,0 +1,244 @@
+# Asm Style Guide
+
+65816 Assembly is the assembly language used by the Super Nintendo Entertainment System (SNES) and its Ricoh 5A22 processor. This style guide provides conventions and best practices for writing 65816 assembly code in the context of the yaze project. Following these guidelines will help maintain consistency and readability across the codebase.
+
+This guide is based primarily on the [Oracle of Secrets](https://github.com/scawful/Oracle-of-Secrets) codebase and is meant for the [Asar](https://github.com/RPGHacker/asar) assembler and derives influence from the [Asar 1.9 Manual](https://rpghacker.github.io/asar/asar_19/manual/).
+
+Custom assembly code applied to the game should be included through the `yaze.asm` file found in `assets/asm`. This file can be applied to the ROM by the editor using the Asar library or included into a projects codebase for use with the Asar assembler.
+
+## Table of Contents
+
+- [File Structure](#file-structure)
+- [Labels and Symbols](#labels-and-symbols)
+- [Comments](#comments)
+- [Directives](#directives)
+- [Instructions](#instructions)
+- [Macros](#macros)
+- [Loops and Branching](#loops-and-branching)
+- [Data Structures](#data-structures)
+- [Code Organization](#code-organization)
+- [Custom Code](#custom-code)
+
+
+## File Structure
+
+- **File Extension**: Use `.asm` as the file extension for 65816 assembly files.
+- **Header Comments**: Include a header comment at the beginning of each file describing its purpose and the author.
+
+Example:
+
+```asm
+; =========================================================
+; File: my_file.asm
+; Purpose: [Brief description of the file’s functionality]
+; Author: [Your Name]
+; =========================================================
+```
+
+- **Section Headers**: Use clear and consistent section headers to divide code into logical blocks. Each major section (e.g., sprite properties, main logic, subroutines) should start with a delineated header.
+
+Example:
+
+```asm
+; =========================================================
+; Minecart Sprite Properties
+; =========================================================
+```
+
+- **Macro Definitions and Includes**: Place macros and include directives at the beginning of the file to keep them organized and easily accessible.
+
+## Labels and Symbols
+
+- **Naming Conventions**:
+ - **Global Labels**: Use descriptive names in `PascalCase` for global labels (e.g., `Sprite_Minecart_Main`).
+ - **Local Labels**: Prefix local labels with a dot (`.`) to indicate their limited scope (e.g., `.check_direction`).
+ - **Constants and Flags**: Use `ALL_CAPS_WITH_UNDERSCORES` for constants and flags (e.g., `!MINECART_SPEED`, `!HARVESTING_FLAG`).
+ - **Variables**: Use `CamelCase` for variable names to maintain readability (e.g., `LinkInCart`, `SpriteDirection`).
+
+- **Alignment**: Align labels to the left margin for better readability. Indent instructions and comments to separate them from labels.
+
+Example:
+
+```asm
+Sprite_Minecart_Main:
+{
+ JSR HandleTileDirections
+ JSR HandleDynamicSwitchTileDirections
+ RTS
+}
+```
+
+
+## Comments
+
+- **Purpose**: Comments should explain why the code exists and what it is intended to do, especially for complex logic.
+- **Placement**:
+ - Comments can be placed above the code block they describe for longer explanations.
+ - Inline comments can be used for single lines of code where the purpose might not be immediately clear.
+- **Clarity**: Avoid stating the obvious. Focus on explaining the logic rather than restating the code.
+
+Example:
+
+```asm
+LDA $22 : SEC : SBC $3F : STA $31 ; Adjust X position for camera movement
+```
+
+## Directives
+
+- **Organization**: Use `%macro`, `include`, and other Asar directives in a structured manner, keeping related directives grouped together.
+- **Usage**: Ensure all directives are used consistently throughout the codebase, following the naming conventions and formatting rules established.
+
+Example:
+
+```asm
+%macro InitMovement
+ LDA.b $22 : STA.b $3F
+ LDA.b $23 : STA.b $41
+ LDA.b $20 : STA.b $3E
+ LDA.b $21 : STA.b $40
+endmacro
+```
+
+## Instructions
+
+- **Single Line Instructions**: Combine multiple instructions on a single line using colons (`:`) where appropriate for related operations.
+- **Separation**: Use line breaks to separate distinct sections of code logically, improving readability.
+- **Optimization**: Always consider the most efficient instruction for the task at hand, especially in performance-critical sections.
+
+Example:
+
+```asm
+LDA #$01 : STA !LinkInCart ; Set Link in cart flag
+```
+
+## Macros
+
+- **Naming**: Use `PascalCase` for macro names, with the first letter of each word capitalized (e.g., `InitMovement`, `MoveCart`).
+- **Parameters**: Clearly define and document parameters within macros to ensure they are used correctly.
+- **Reuse**: Encourage the reuse of macros to avoid code duplication and simplify maintenance.
+
+Example:
+
+```asm
+%macro HandlePlayerCamera
+ LDA $22 : SEC : SBC $3F : STA $31
+ LDA $20 : SEC : SBC $3E : STA $30
+ JSL Link_HandleMovingAnimation_FullLongEntry
+ JSL HandleIndoorCameraAndDoors
+ RTS
+endmacro
+```
+
+## Loops and Branching
+
+- **Branch Labels**: Use meaningful names for branch labels, prefixed with a dot (`.`) for local branches.
+- **Optimization**: Minimize the number of instructions within loops and branches to improve performance.
+
+Example:
+
+```asm
+.loop_start
+ LDA $00 : CMP #$10 : BEQ .end_loop
+ INC $00
+ BRA .loop_start
+.end_loop
+ RTS
+```
+
+## Data Structures
+
+- **Alignment**: Align data tables and structures clearly, and use comments to describe the purpose and layout of each.
+- **Access**: Ensure that data structures are accessed consistently, with clear boundaries between read and write operations.
+
+Example:
+
+```asm
+.DirectionTileLookup
+{
+ db $02, $00, $04, $00 ; North
+ db $00, $00, $03, $01 ; East
+ db $00, $02, $00, $04 ; South
+ db $03, $01, $00, $00 ; West
+}
+```
+
+- **Structs**: Use structs to group related data together, improving readability and maintainability.
+
+Example:
+
+```asm
+struct AncillaAdd_HookshotData $099AF8
+ .speed_y: skip 4
+ .speed_x: skip 4
+ .offset_y: skip 8
+ .offset_x: skip 8
+endstruct
+
+AncillaAdd_Hookshot:
+ ; $099AF0
+ .speed_y
+ db -64 ; up
+ db 64 ; down
+ db 0 ; left
+ db 0 ; right
+ ; $099AFC
+ .speed_x
+ db 0 ; up
+ db 0 ; down
+ db -64 ; left
+ db 64 ; right
+ ; $099B00
+ .offset_y
+ dw 4 ; up
+ dw 20 ; down
+ dw 8 ; left
+ dw 8 ; right
+ ; $099B08
+ .offset_x
+ dw 0 ; up
+ dw 0 ; down
+ dw -4 ; left
+ dw 11 ; right
+```
+
+## Code Organization
+
+- **Logical Grouping**: Organize code into logical sections, with related routines and macros grouped together.
+- **Separation of Concerns**: Ensure that each section of code is responsible for a specific task or set of related tasks, avoiding tightly coupled code.
+- **Modularity**: Write code in a modular way, making it easier to reuse and maintain.
+- **Status Registers and Stack Operations**: Indent code blocks when using status register operations (REP, SEP, PHX, PLX, etc.) to improve readability.
+
+Example:
+
+```asm
+; =========================================================
+; Minecart Sprite Logic
+; =========================================================
+Sprite_Minecart_Main:
+{
+ JSR HandleTileDirections
+ JSR HandleDynamicSwitchTileDirections
+ PHX
+ JSR HandleMinecartMovement
+ PLX
+
+ REP #$20
+ LDA !SpriteDirection : STA $00
+ SEP #$20
+ RTS
+}
+```
+
+## Custom Code
+
+- **Integration**: Include custom assembly code in the `yaze.asm` file to ensure it is applied correctly to the ROM. The module should include a define and conditional statement to allow users to disable the module if needed.
+
+Example:
+
+```asm
+!YAZE_CUSTOM_MOSAIC = 1
+
+if !YAZE_CUSTOM_MOSAIC != 0
+ incsrc "mosaic_change.asm"
+endif
+```
\ No newline at end of file
diff --git a/docs/build-instructions.md b/docs/build-instructions.md
index d562c2fc..b24d4526 100644
--- a/docs/build-instructions.md
+++ b/docs/build-instructions.md
@@ -1,27 +1,58 @@
# Build Instructions
+For VSCode users, use the following CMake extensions
+
+- https://marketplace.visualstudio.com/items?itemName=twxs.cmake
+- https://marketplace.visualstudio.com/items?itemName=ms-vscode.cmake-tools
+
+Yaze uses CMake to build the project. If you are unexperienced with CMake, please refer to the [CMake documentation](https://cmake.org/documentation/).
+
+The gui editor is built using SDL2 and ImGui. For reference on how to use ImGui, see the [Getting Started](https://github.com/ocornut/imgui/wiki/Getting-Started) guide. For SDL2, see the [SDL2 documentation](https://wiki.libsdl.org/).
+
+For those who want to reduce compile times, consider installing the dependencies on your system.
+
## Windows
-For VSCode users, use the following CMake extensions with MinGW-w64
-
-https://marketplace.visualstudio.com/items?itemName=twxs.cmake
-https://marketplace.visualstudio.com/items?itemName=ms-vscode.cmake-tools
-
-https://www.msys2.org/
+Recommended to use [msys2](https://www.msys2.org/) for a Unix-like environment on Windows.
Add to environment variables `C:\msys64\mingw64\bin`
Install the following packages using `pacman -S `
-`mingw-w64-x86_64-gcc`
-`mingw-w64-x86_64-gcc-libs`
-`mingw-w64-x86_64-cmake`
-`mingw-w64-x86_64-glew`
-`mingw-w64-x86_64-lib-png`
+- `mingw-w64-x86_64-gcc`
+- `mingw-w64-x86_64-gcc-libs`
+- `mingw-w64-x86_64-cmake`
+- `mingw-w64-x86_64-sdl2`
+- `mingw-w64-x86_64-libpng`
+- `mingw-w64-x86_64-abseil-cpp`
+
+For `yaze_py` you will need Boost Python
+
+- `mingw-w64-x86_64-boost`
# macOS
-- Clang 15.0.1 x86_64-apple-darrwin22.5.0
-- SDL2 Source v2.26.5
-- Removed snes_spc
-- Removed asar_static
\ No newline at end of file
+Prefer to use clang provided with XCode command line tools over gcc.
+
+Install the following packages using `brew install `
+
+- `cmake`
+- `sdl2`
+- `zlib`
+- `libpng`
+- `abseil`
+- `boost-python3`
+
+# iOS
+
+Xcode is required to build for iOS. Currently testing with iOS 18 on iPad Pro.
+
+The xcodeproject file is located in the `ios` directory.
+
+You will need to link `SDL2.framework` and `libpng.a` to the project.
+
+# GNU/Linux
+
+You can use your package manager to install the same dependencies as macOS.
+
+I trust you know how to use your package manager.
\ No newline at end of file
diff --git a/docs/changelog.md b/docs/changelog.md
new file mode 100644
index 00000000..63caeec4
--- /dev/null
+++ b/docs/changelog.md
@@ -0,0 +1,47 @@
+# Changelog
+
+## 0.0.1 (06-08-2022)
+
+- Started project
+- Added ImGui
+- Added SDL2
+- Added yaze_test target with gtest
+
+## 0.0.2 - 0.0.4
+
+- TODO: Track changes over this time
+
+## 0.0.5 (11-21-2023)
+
+- DungeonEditor
+- DungeonObjectRenderer
+
+## 0.0.6 (11-22-2023)
+
+- ScreenEditor DungeonMap
+- Tile16 Editor
+- Canvas updates
+
+## 0.0.7 (01-27-2024)
+
+- OverworldEntities
+ - Entrances
+ - Exits
+ - Items
+ - Sprites
+
+## 0.1.0 (05-11-2024)
+
+- TODO: Track changes over this time
+
+## 0.2.0 (07-20-2024)
+
+- iOS app support
+- Graphics Sheet Browser
+- Project Files
+
+## 0.2.1 (08-20-2024)
+
+- Improved MessageEditor parsing
+- Added integration test window
+- Bitmap bug fixes
diff --git a/docs/compression.md b/docs/compression.md
deleted file mode 100644
index 6395426d..00000000
--- a/docs/compression.md
+++ /dev/null
@@ -1,66 +0,0 @@
-# LC_LZ2 Compression
-
-The compression algorithm has multiple implementations with varying levels of quality, based primarily on the implementations made in skarsnik/sneshacking, Zarby89/ZScreamDungeon and ZCompress with optimizations made for C++.
-
-Currently, the Compress and Uncompress methods from Hyrule Magic are used and all other compression methods are considered deprecated.
-
-## Key Definitions
-
-### Constants and Macros:
-- `BUILD_HEADER(command, length)`: Macro to build a header from a command and a length.
-- Command Constants: Constants to represent different commands like `kCommandDirectCopy`, `kCommandByteFill`, etc.
-- Length and Mode Constants: Such as `kMaxLengthNormalHeader`, `kNintendoMode1`, etc.
-
-### Data Structures:
-
-#### 1. CompressionCommand:
- - **arguments**: 2D array representing the command arguments for each possible command.
- - **cmd_size**: Array storing the size of each possible command.
- - **data_size**: Array storing the size of the data processed by each possible command.
-
-#### 2. CompressionPiece:
- - **command**: Represents the compression command.
- - **length**: Length of the compressed data piece.
- - **argument_length**: Length of the argument.
- - **argument**: Argument as a string.
- - **next**: Pointer to the next compression piece.
-
-#### 3. CompressionContext (for Compression V3):
- - Contains vectors to store raw and compressed data, compression pieces, and compression string.
- - Various counters and flags for compression control.
- - Current compression command details.
-
-## Compression Functions
-
-### Version 1:
-- **Byte Repeat**: `CheckByteRepeat`
-- **Word Repeat**: `CheckWordRepeat`
-- **Increasing Byte**: `CheckIncByte`
-- **Intra Copy**: `CheckIntraCopy`
-- **Validation and Alternatives**: `ValidateForByteGain` & `CompressionCommandAlternative`
-
-### Version 2:
-- **Byte Repeat**: `CheckByteRepeatV2`
-- **Word Repeat**: `CheckWordRepeatV2`
-- **Increasing Byte**: `CheckIncByteV2`
-- **Intra Copy**: `CheckIntraCopyV2`
-- **Validation and Alternatives**: `ValidateForByteGainV2` & `CompressionCommandAlternativeV2`
-
-### Version 3:
-Using `CompressionContext` to handle compression.
-- **Initialization**: `InitializeCompression`
-- **Command Checks**: Such as `CheckByteRepeatV3`
-- **Determining Best Compression**: `DetermineBestCompression`
-- **Handling Direct Copy**: `HandleDirectCopy`
-- **Adding Compression to Chain**: `AddCompressionToChain`
-
-## Decompression Functions:
-- `SetBuffer`: Prepares a buffer from data.
-- `memfill`: Fills memory.
-- **Decompression**: Such as `DecompressV2`, `DecompressGraphics`, and `DecompressOverworld`.
-
-## Utility Functions:
-- **Printing**: Such as `PrintCompressionPiece` and `PrintCompressionChain`.
-- **Compression String Creation**: `CreateCompressionString`
-- **Compression Result Validation**: Such as `ValidateCompressionResult` and its V3 variant.
-- **Compression Piece Manipulation**: Like `SplitCompressionPiece` and its V3 variant.
diff --git a/docs/contributing.md b/docs/contributing.md
new file mode 100644
index 00000000..4531e184
--- /dev/null
+++ b/docs/contributing.md
@@ -0,0 +1,139 @@
+# Contributing
+
+This project is looking for contributors to help improve the software and enhance the user experience. If you are interested in contributing, please read the following guidelines and suggestions for areas where you can make a difference.
+
+Discussion on the editor and its development can be found on the [Oracle of Secrets Discord](https://discord.gg/MBFkMTPEmk) server.
+
+## Style Guide
+
+When contributing to the project, please follow these guidelines to ensure consistency and readability across the codebase:
+
+C++ Code should follow the [Google C++ Style Guide](https://google.github.io/styleguide/cppguide.html) with the following exceptions:
+
+- Boost libraries are allowed, but require cross platform compatibility.
+
+Objective-C Code should follow the [Google Objective-C Style Guide](https://google.github.io/styleguide/objcguide.html).
+
+Python Code should follow the [PEP 8 Style Guide](https://pep8.org/).
+
+Assembly code should follow the [65816 Style Guide](docs/asm-style-guide.md).
+
+## Testing Facilities
+
+The project includes the `yaze_test` target which defines unit tests and an integration test window. The unit tests make use of GoogleTest and GoogleMock. The integration test window is an ImGui window build out of the yaze::app::core::Controller and yaze::test::integration::TestEditor. The integration test window can be accessed by passing the argument `integration` to the target.
+
+New modules should define unit tests in the `src/test` directory and integration tests in the `src/test/integration` directory. The `yaze_test` target will automatically include all tests in these directories.
+
+## Key Areas of Contribution
+
+### 1. Extensions System
+
+Yaze *(stylized as yaze)* emphasizes extensibility. The `yaze_ext` library allows developers to build and integrate extensions using C, C++, or Python. This system is central to yaze's modular design, enabling new features, custom editors, or tools to be added without modifying the core codebase.
+
+- C/C++ Extensions: Utilize the `yaze_extension` interface to integrate custom functionality into the editor. You can add new tabs, manipulate ROM data, or extend the editor’s capabilities with custom tools.
+- Python Extensions: Currently unimplemented, Python extensions will allow developers to write scripts that interact with the editor, modify ROM data, or automate repetitive tasks.
+
+Examples of Extensions:
+
+- UI enhancements like additional menus, panels, or status displays.
+- Rom manipulation tools for editing data structures, such as the overworld maps or dungeon objects.
+- Custom editors for specific tasks, like file format conversion, data visualization, or event scripting.
+
+### 2. Sprite Builder System
+
+The sprite builder system in yaze is based on the [ZSpriteMaker](https://github.com/Zarby89/ZSpriteMaker/) project and allows users to create custom sprites for use in ROM hacks. The goal is to support ZSM files and provide an intuitive interface for editing sprites without the need for writing assembly code. Contributions to the sprite builder system might include:
+
+- Implementing new features for sprite editing, such as palette management, animation preview, or tileset manipulation.
+- Extending the sprite builder interface by writing assembly code for sprite behavior.
+
+### 3. Emulator Subsystem
+
+yaze includes an emulator subsystem that allows developers to test their modifications directly within the editor. The emulator can currently run certain test ROMs but lacks the ability to play any complex games with audio because of timing issues with the APU and Spc700. Contributions to the emulator subsystem might include:
+
+- Improving the accuracy and performance of the emulator to support more games and features.
+- Implementing new debugging tools, such as memory viewers, breakpoints, or trace logs.
+- Extending the emulator to support additional features, such as save states, cheat codes, or multiplayer modes.
+
+### 4. Editor Management
+
+The `EditorManager` class manages the core functionalities of YAZE, including rendering the UI, handling user input, and managing multiple editors. While this class is central to yaze's operations, it has many responsibilities. You can help by:
+
+- Refactoring `EditorManager` to delegate responsibilities to specialized managers (e.g., `MenuManager`, `TabManager`, `StatusManager`).
+- Optimizing the rendering and update loop to improve performance, especially when handling large textures or complex editors.
+- Implementing new features that streamline the editing process, such as better keyboard shortcuts, command palette integration, or project management tools.
+
+### 5. User Interface and UX
+
+yaze's UI is built with ImGui, offering a flexible and customizable interface. Contributions to the UI might include:
+
+- Designing and implementing new themes or layouts to improve the user experience.
+- Adding new UI components, such as toolbars, context menus, or customizable panels.
+- Improving the accessibility of the editor, ensuring it is usable by a wide range of users, including those with disabilities.
+
+### 6. ROM Manipulation
+
+The `Rom` class is at the heart of yaze's ability to modify and interact with ROM data. Contributions here might involve:
+
+- Optimizing the loading and saving processes to handle larger ROMs or more complex modifications efficiently.
+- Extensions should be able to change the way the `Rom` class interacts with the ROM data with custom pointers to expanded data structures.
+
+### 7. Testing and Documentation
+
+Quality assurance and documentation are critical to yaze's success. Contributions in this area include:
+
+- Writing unit tests for new and existing features to ensure they work correctly and remain stable over time.
+- Contributing to the documentation, both for end-users and developers, to make yaze easier to use and extend.
+- Creating tutorials or guides that help new developers get started with building extensions or contributing to the project.
+
+## Building the Project
+
+For detailed instructions on building YAZE, including its dependencies and supported platforms, refer to [build-instructions.md](docs/build-instructions.md).
+
+## Getting Started
+
+1. Clone the Repository:
+
+```bash
+git clone https://github.com/yourusername/yaze.git
+cd yaze
+```
+
+2. Initialize the Submodules:
+
+```bash
+git submodule update --init --recursive
+```
+
+3. Build the Project:
+
+Follow the instructions in the [build-instructions.md](docs/build-instructions.md). file to configure and build the project on your target platform.
+
+4. Run the Application:
+
+After building, you can run the application on your chosen platform and start exploring the existing features.
+
+## Contributing your Changes
+
+1. Fork the Repository:
+
+Create a fork of the project on GitHub and clone your fork to your local machine.
+
+2. Create a Branch:
+
+Create a new branch for your feature or bugfix.
+
+```bash
+git checkout -b feature/my-new-feature
+```
+
+3. Implement Your Changes:
+
+Follow the guidelines above to implement new features, extensions, or improvements.
+
+4. Test Your Changes:
+
+Ensure your changes don’t introduce new bugs or regressions. Write unit tests where applicable.
+
+5. Submit a Pull Request:
+
+Push your changes to your fork and submit a pull request to the main repository. Provide a clear description of your changes and why they are beneficial.
diff --git a/docs/getting-started.md b/docs/getting-started.md
index 736c3815..a7105228 100644
--- a/docs/getting-started.md
+++ b/docs/getting-started.md
@@ -1,23 +1,60 @@
-# Getting Started with YAZE
+# Getting Started
-This software allows you to modify the classic SNES game "The Legend of Zelda: A Link to the Past" by editing its ROM file. With this editor, you can change various aspects of the game, such as the maps, sprites, items, and more.
+This software allows you to modify "The Legend of Zelda: A Link to the Past" (US or JP) ROMs.
+
+This editor is built to be compatible with ZScream projects and is designed to be cross platform.
Please note that this project is currently a work in progress, and some features may not be fully implemented or may be subject to change.
-## Prerequisites
-Before you start using YAZE, make sure you have the following:
+## General Tips
-- A copy of "The Legend of Zelda: A Link to the Past" ROM file (US or JP)
-- Basic knowledge of hexadecimal and binary data
+- Experiment flags determine whether certain features are enabled or not. To change your flags, go to `File` > `Options` > `Experiment Flags` or in the Settings tab.
+- Backup files are enabled by default. Each save will produce a timestamped copy of your ROM before you last saved. You can disable this feature in the settings.
-## Usage
-To use the Link to the Past ROM Editor, follow these steps:
+## Extending Functionality
-Open the ROM file using the "File" menu.
+In addition to the built-in features, this software provides a pure C library interface and a Python module that can be used for building extensions and custom sprites without assembly. In the editor these can be loaded under the `Extensions` menu.
-...
+This feature is still in development and is not yet fully documented.
+
+## Supported Features
+
+| Feature | Status | Details |
+|---------|--------|-------------|
+| Overworld Maps | Done | Edit and save tile32 data. |
+| Overworld Map Properties | Done | Edit and save map properties. |
+| Overworld Entrances | Done | Edit and save entrance data. |
+| Overworld Exits | Done | Edit and save exit data. |
+| Overworld Sprites | In Progress | Edit sprite positions, add and remove sprites. |
+| Tile16 Editing | Todo | Edit and save tile16 data. |
+| Dungeon | In Progress | View dungeon room metadata and edit room data. |
+| Palette | In Progress | Edit and save palettes, palette groups. |
+| Graphics Sheets | In Progress | Edit and save graphics sheets. |
+| Graphics Groups | Done | Edit and save graphics groups. |
+| Sprite | Todo | View-only sprite data. |
+| Custom Sprites | Todo | Edit and create custom sprite data. |
+| Music | Todo | Edit music data. |
+| Dungeon Maps | Todo | Edit dungeon maps. |
+| Scad Format | Done-ish | Open and view scad files (SCR, CGX, COL) |
+| Hex Editing | Done | View and edit ROM data in hex. |
+| Asar Patching | In Progress | Apply Asar patches to your ROM or Project. |
+
+## Command Line Interface
+
+Included with the editor is a command line interface (CLI) that allows you to perform various operations on your ROMs from the command line. This aims to reduce the need for multiple tools in zelda3 hacking like Zcompress, LunarExpand, LunarAddress, Asar, and others.
+
+| Command | Arg | Params | Status |
+|---------|-----|--------|--------|
+| Apply BPS Patch | -a | rom_file bps_file | In progress |
+| Create BPS Patch | -c | bps_file src_file modified_file | Not started |
+| Asar Patch | -asar | asm_file rom_file | In progress |
+| Open ROM | -o | rom_file | Complete |
+| Backup ROM | -b | rom_file [new_file] | In progress |
+| Expand ROM | -x | rom_file file_size | Not started |
+| Transfer Tile16 | -t | src_rom dest_rom tile32_id_list(csv) | Complete |
+| Export Graphics | -e | rom_file bin_file | In progress |
+| Import Graphics | -i | bin_file rom_file | Not started |
+| SNES to PC Address | -s | address | Complete |
+| PC to SNES Address | -p | address | Complete |
-Save your changes using the "File" menu.
-Backup files are enabled by default. Each save will produce a timestamped copy of your ROM before you last saved.
-That's it! With these instructions, you should be able to get started with using YAZE. Happy editing!
\ No newline at end of file
diff --git a/docs/infrastructure.md b/docs/infrastructure.md
index 0b53b3f8..61e70294 100644
--- a/docs/infrastructure.md
+++ b/docs/infrastructure.md
@@ -1,140 +1,86 @@
-# YAZE Infrastructure Overview
+# Infrastructure Overview
For developers to reference.
+The goal of yaze is to build a cross platform editor for the Legend of Zelda: A Link to the Past. The project is built using C++20, SDL2, and ImGui. The project is built using CMake and is designed to be modular and extensible. The project is designed to be built on Windows, macOS, iOS, and Linux.
+
+## Targets
+
+- **yaze**: Desktop application for Windows/macOS/Linux
+- **z3ed**: Command Line Interface
+- **yaze_c**: C Library
+- **yaze_py**: Python Module
+- **yaze_test**: Unit test executable
+- **yaze_ios**: iOS application
+
## Directory Structure
-- **.github/workflows**: Contains `yaze_test` workflow config.
-- **assets**: Hosts assets like fonts.
+- **assets**: Hosts assets like fonts, icons, assembly source, etc.
- **cmake**: Contains CMake configurations.
- **docs**: Contains documentation for users and developers.
- - [Getting Started](./getting-started.md)
- - [LC_LZ2 Compression](./compression.md)
- **src**: Contains source files.
- - **app**: Contains the GUI editor `yaze`
- - **cli**: Contains the command line interface `z3ed`
- - **lib**: Contains git submodule dependencies.
- - Abseil-cpp
- - Asar
- - ImGui
- - ImGuiFileDialog
- - ImGuiColorTextEdit
- - imgui_memory_editor
- - SDL2
-- **test**: Contains testing interface `yaze_test`
+ - **app**: Contains the GUI editor `yaze`
+ - **app/emu**: Contains a standalone Snes emulator application `yaze_emu`
+ - **cli**: Contains the command line interface `z3ed`
+ - **incl**: Contains the data headers for `yaze_c`
+ - **ios**: Contains the iOS application `yaze_ios`
+ - **lib**: Contains the dependencies as git submodules
+ - **py**: Contains the Python module `yaze_py`
+ - **test**: Contains testing interface `yaze_test`
+ - **win32**: Contains Windows resource file and icon
-### Flow of Control
+## Dependencies
-- [app/yaze.cc](../src/app/yaze.cc)
+See [build-instructions.md](docs/build-instructions.md) for more information.
+
+- **SDL2**: Graphics library
+- **ImGui**: GUI library
+- **Abseil**: C++ library
+- **libpng**: Image library
+- **Boost**: Python library
+
+## Flow of Control
+
+- app/yaze.cc
- Initializes `absl::FailureSignalHandler` for stack tracing.
- Runs the `core::Controller` loop.
-- [app/core/controller.cc](../src/app/core/controller.cc)
+- app/core/controller.cc
- Initializes SDLRenderer and SDLWindow
- Initializes ImGui, fonts, themes, and clipboard.
- Handles user input from keyboard and mouse.
- - Updates `editor::MasterEditor`
- Renders the output to the screen.
- Handles the teardown of SDL and ImGui resources.
-- [app/editor/master_editor.cc](../src/app/editor/master_editor.cc)
- - Handles the main menu bar.
- - File
- - Open - [app::ROM::LoadFromFile](../src/app/rom.cc#l=90)
- - Save - [app::ROM::SaveToFile](../src/app/rom.cc#l=301)
- - Edit
- - View
- - Emulator
- - HEX Editor
- - ASM Editor
- - Palette Editor
- - Memory Viewer
- - ImGui Demo
- - GUI Tools
- - Runtime Metrics
- - Style Editor
- - Help
+- app/editor/editor_manager.cc
+ - Handles the main menu bar
- Handles `absl::Status` errors as popups delivered to the user.
+ - Dispatches messages to the various editors.
- Update all the editors in a tab view.
- - [app/editor/assembly_editor.cc](../src/app/editor/assembly_editor.cc)
- - [app/editor/dungeon_editor.cc](../src/app/editor/dungeon_editor.cc)
- - [app/editor/graphics_editor.cc](../src/app/editor/graphics_editor.cc)
- - [app/editor/music_editor.cc](../src/app/editor/music_editor.cc)
- - [app/editor/overworld_editor.cc](../src/app/editor/overworld_editor.cc)
- - [app/editor/screen_editor.cc](../src/app/editor/screen_editor.cc)
- - [app/editor/sprite_editor.cc](../src/app/editor/sprite_editor.cc)
+ - app/editor/code/assembly_editor.cc
+ - app/editor/dungeon/dungeon_editor.cc
+ - app/editor/graphics/graphics_editor.cc
+ - app/editor/graphics/gfx_group_editor.cc
+ - app/editor/graphics/palette_editor.cc
+ - app/editor/graphics/tile16_editor.cc
+ - app/editor/message/message_editor.cc
+ - app/editor/music/music_editor.cc
+ - app/editor/overworld/overworld_editor.cc
+ - app/editor/graphics/screen_editor.cc
+ - app/editor/sprite/sprite_editor.cc
+ - app/editor/system/settings_editor.cc
-## ROM
-- [app/rom.cc](../src/app/rom.cc)
-- [app/rom.h](../src/app/rom.h)
----
+## Rom
-This `ROM` class provides methods to manipulate and access data from a ROM.
+- app/rom.cc
+- app/rom.h
-- **Key Methods**:
- - `Load2BppGraphics()`: Loads 2BPP graphics data from specified sheets.
- - `LoadAllGraphicsData()`: Loads all graphics data, both compressed and uncompressed, converting where necessary.
- - `LoadFromFile(const absl::string_view& filename, bool z3_load)`: Loads ROM data from a file. It also handles headers and Zelda 3 specific data if requested.
- - `LoadFromPointer(uchar* data, size_t length)`: Loads ROM data from a provided pointer.
- - `LoadFromBytes(const Bytes& data)`: Loads ROM data from bytes.
- - `LoadAllPalettes()`: Loads all color palettes used in the ROM. This includes palettes for various elements like sprites, shields, swords, etc.
- - `UpdatePaletteColor(...)`: Updates a specific color within a named palette group.
+The Rom class provides methods to manipulate and access data from a ROM.
-- **Internal Data Structures**:
- - `rom_data_`: A container that holds the ROM data.
- - `graphics_bin_`: Holds the graphics data.
- - `palette_groups_`: A map containing various palette groups, each having its own set of color palettes.
-
-- **Special Notes**:
- - The class interacts with various external functionalities, such as decompression algorithms (`gfx::DecompressV2`) and color conversion (`gfx::SnesTo8bppSheet`).
- - Headers in the ROM data, if present, are identified and removed.
- - Specific Zelda 3 data can be loaded if specified.
- - Palettes are categorized into multiple groups (e.g., `ow_main`, `ow_aux`, `hud`, etc.) and loaded accordingly.
+Currently implemented as a singleton with SharedRom which is not great but has helped with development velocity. Potential room for improvement is to refactor the editors to take the ROM as a parameter.
## Bitmap
-- [app/gfx/bitmap.cc](../src/app/gfx/bitmap.cc)
-- [app/gfx/bitmap.h](../src/app/gfx/bitmap.cc)
----
+- app/gfx/bitmap.cc
+- app/gfx/bitmap.h
-This class is responsible for creating, managing, and manipulating bitmap data, which can be displayed on the screen using the ImGui library.
+This class is responsible for creating, managing, and manipulating bitmap data, which can be displayed on the screen using SDL2 Textures and the ImGui draw list. It also provides functions for exporting these bitmaps to the clipboard in PNG format using libpng.
-### Key Attributes:
-
-1. **Width, Height, Depth, and Data Size**: These represent the dimensions and data size of the bitmap.
-2. **Pixel Data**: Points to the raw data of the bitmap.
-3. **Texture and Surface**: Use SDL to manage the graphical representation of the bitmap data. Both these attributes have custom deleters, ensuring proper resource management.
-
-### Main Functions:
-
-1. **Constructors**: Multiple constructors allow for different ways to create a Bitmap instance, like specifying width, height, depth, and data.
-2. **Create**: This set of overloaded functions provides ways to create a bitmap from different data sources.
-3. **CreateFromSurface**: Allows for the creation of a bitmap from an SDL_Surface.
-4. **Apply**: Changes the bitmap's data to a new set of Bytes.
-5. **Texture Operations**:
- - **CreateTexture**: Creates an SDL_Texture from the bitmap's data for rendering.
- - **UpdateTexture**: Updates the SDL_Texture with the latest bitmap data.
-6. **SaveSurfaceToFile**: Saves the SDL_Surface to a file.
-7. **SetSurface**: Assigns a new SDL_Surface to the bitmap.
-8. **Palette Functions**:
- - **ApplyPalette (Overloaded)**: This allows for the application of a SNESPalette or a standard SDL_Color palette to the bitmap.
-9. **WriteToPixel**: Directly writes a value to a specified position in the pixel data.
-
-## Z3ED cli
-
-| Command | Arg | Params | Status |
-|---------|-----|--------|--------|
-| Apply BPS Patch | -a | rom_file bps_file | In progress |
-| Create BPS Patch | -c | bps_file src_file modified_file | Not started |
-| Open ROM | -o | rom_file | Complete |
-| Backup ROM | -b | rom_file [new_file] | In progress |
-| Expand ROM | -x | rom_file file_size | Not started |
-| Transfer Tile16 | -t | src_rom dest_rom tile32_id_list(csv) | Complete |
-| Export Graphics | -e | rom_file bin_file | In progress |
-| Import Graphics | -i | bin_file rom_file | Not started |
-| SNES to PC Address | -s | address | Complete |
-| PC to SNES Address | -p | address | Complete |
-
-
-## Further Development Ideas
-- Extend `zelda3` namespace with additional functionalities.
-- Optimize program performance.
-- Introduce new features in the GUI editor.
diff --git a/docs/yaze.org b/docs/yaze.org
new file mode 100644
index 00000000..df78744e
--- /dev/null
+++ b/docs/yaze.org
@@ -0,0 +1,63 @@
+#+TITLE: yaze todo
+#+SUBTITLE: yet another zelda3 editor todo list
+#+AUTHOR: @scawful
+#+TODO: TODO ACTIVE FEEDBACK VERIFY | DONE
+
+* Daily Log
+
+<2024-11-14 Thu>
+Been making lots of adjustments and cleaning up old code. Primarily improving the dungeon map editor and supporting bin graphics for my Oracle of Secrets dungeon maps. Additionally, working to support saving for resources like graphics sheets and expanded the project file system.
+
+<2024-09-07 Sat>
+Various header cleanup using the LSP in emacs to detect unused includes.
+Making adjustments to font loading so the editor can be opened from terminal/emacs.
+Currently the font files and the zeml files require the binary to be relative to `assets/layouts` and `assets/fonts`
+I've set it up so that the macOS app bundles the resources into the `yaze.app` so that the binary can be run from anywhere. This will need to be adjusted for other platforms.
+
+<2024-09-02 Mon>
+Extracted the DisplayPalette function out of the PaletteEditor and into its own standalone function.
+
+<2024-09-01 Sun>
+Started learning spacemacs and org-mode.
+
+* Editors
+** Overworld
+*** TODO ZSCustomOverworld implementation.
+**** DONE Custom Overworld Map Settings Inputs
+**** DONE Load ZSCOW data from ROM in OverworldMap
+**** TODO Add Main Palette support
+**** TODO Add Custom Area BG Color support
+
+*** TODO Fix sprite icon draw positions
+*** TODO Fix exit icon draw positions
+
+** Dungeon
+*** TODO Draw dungeon objects
+
+** Graphics
+*** TODO Tile16 Editor
+- [ ] Draw tile8 to tile16 quadrant.
+
+*** TODO Fix graphics sheet pencil drawing
+
+** Message
+*** TODO Fix Message Parsing
+
+** Palette
+*** TODO Persist color changes for saving to ROM.
+
+** Screens
+*** ACTIVE Dungeon Maps
+*** ACTIVE Inventory Menu
+*** TODO Overworld Map
+*** TODO Title Screen
+*** TODO Naming Screen
+
+* Infrastructure
+** File Handling
+*** TODO Update recent files manager to bundle the recent files list with the application
+*** DONE Create a util for handling file operations from the bundled resources.
+** Font Loading
+*** TODO Make font sizes variables so they can be reloaded by the user.
+** ZEML
+*** DONE Package layout files with the executable to avoid relative file lookup
diff --git a/incl/dungeon.h b/incl/dungeon.h
new file mode 100644
index 00000000..d476e1f7
--- /dev/null
+++ b/incl/dungeon.h
@@ -0,0 +1,61 @@
+#ifndef YAZE_BASE_DUNGEON_H_
+#define YAZE_BASE_DUNGEON_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include
+#include
+
+typedef struct z3_object_door {
+ short id;
+ uint8_t x;
+ uint8_t y;
+ uint8_t size;
+ uint8_t type;
+ uint8_t layer;
+} z3_object_door;
+
+typedef struct z3_dungeon_destination {
+ uint8_t index;
+ uint8_t target;
+ uint8_t target_layer;
+} z3_dungeon_destination;
+
+typedef struct z3_staircase {
+ uint8_t id;
+ uint8_t room;
+ const char *label;
+} z3_staircase;
+
+typedef struct z3_chest {
+ uint8_t x;
+ uint8_t y;
+ uint8_t item;
+ bool picker;
+ bool big_chest;
+} z3_chest;
+
+typedef struct z3_chest_data {
+ uint8_t id;
+ bool size;
+} z3_chest_data;
+
+typedef enum z3_dungeon_background2 {
+ Off,
+ Parallax,
+ Dark,
+ OnTop,
+ Translucent,
+ Addition,
+ Normal,
+ Transparent,
+ DarkRoom
+} z3_dungeon_background2;
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // YAZE_BASE_DUNGEON_H_
diff --git a/incl/overworld.h b/incl/overworld.h
new file mode 100644
index 00000000..086857db
--- /dev/null
+++ b/incl/overworld.h
@@ -0,0 +1,47 @@
+#ifndef YAZE_OVERWORLD_H
+#define YAZE_OVERWORLD_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include
+
+#include "sprite.h"
+
+/**
+ * @brief Primitive of an overworld map.
+ */
+typedef struct z3_overworld_map {
+ uint8_t id; /**< ID of the overworld map. */
+ uint8_t parent_id;
+ uint8_t quadrant_id;
+ uint8_t world_id;
+ uint8_t game_state;
+ uint8_t area_graphics;
+ uint8_t area_palette;
+
+ uint8_t sprite_graphics[3];
+ uint8_t sprite_palette[3];
+ uint8_t area_music[4];
+ uint8_t static_graphics[16];
+} z3_overworld_map;
+
+/**
+ * @brief Primitive of the overworld.
+ */
+typedef struct z3_overworld {
+ void *impl; // yaze::app::Overworld*
+
+ uint8_t *tile32_data; /**< Pointer to the 32x32 tile data. */
+ uint8_t *tile16_data; /**< Pointer to the 16x16 tile data. */
+
+ z3_sprite **sprites; /**< Pointer to the sprites per map. */
+ z3_overworld_map **maps; /**< Pointer to the overworld maps. */
+} z3_overworld;
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // YAZE_OVERWORLD_H
diff --git a/incl/snes_color.h b/incl/snes_color.h
new file mode 100644
index 00000000..db23db0c
--- /dev/null
+++ b/incl/snes_color.h
@@ -0,0 +1,32 @@
+#ifndef YAZE_BASE_SNES_COLOR_H_
+#define YAZE_BASE_SNES_COLOR_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include
+
+/**
+ * @brief Primitive of 16-bit RGB SNES color.
+ */
+typedef struct snes_color {
+ uint16_t red; /**< Red component of the color. */
+ uint16_t blue; /**< Blue component of the color. */
+ uint16_t green; /**< Green component of the color. */
+} snes_color;
+
+/**
+ * @brief Primitive of a SNES color palette.
+ */
+typedef struct snes_palette {
+ unsigned int id; /**< ID of the palette. */
+ unsigned int size; /**< Size of the palette. */
+ snes_color* colors; /**< Pointer to the colors in the palette. */
+} snes_palette;
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // YAZE_BASE_SNES_COLOR_H_
diff --git a/incl/snes_tile.h b/incl/snes_tile.h
new file mode 100644
index 00000000..922bbf4d
--- /dev/null
+++ b/incl/snes_tile.h
@@ -0,0 +1,40 @@
+#ifndef YAZE_INCL_SNES_TILE_H
+#define YAZE_INCL_SNES_TILE_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include
+#include
+
+typedef struct snes_tile8 {
+ uint32_t id;
+ uint32_t palette_id;
+ uint8_t data[64];
+} snes_tile8;
+
+typedef struct snes_tile_info {
+ uint16_t id;
+ uint8_t palette;
+ bool priority;
+ bool vertical_mirror;
+ bool horizontal_mirror;
+} snes_tile_info;
+
+typedef struct snes_tile16 {
+ snes_tile_info tiles[4];
+} snes_tile16;
+
+typedef struct snes_tile32 {
+ uint16_t t0;
+ uint16_t t1;
+ uint16_t t2;
+ uint16_t t3;
+} snes_tile32;
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/incl/sprite.h b/incl/sprite.h
new file mode 100644
index 00000000..fc7761bd
--- /dev/null
+++ b/incl/sprite.h
@@ -0,0 +1,23 @@
+#ifndef YAZE_BASE_SPRITE_H_
+#define YAZE_BASE_SPRITE_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include
+
+/**
+ * @brief Primitive of a sprite.
+ */
+typedef struct z3_sprite {
+ const char* name; /**< Name of the sprite. */
+ uint8_t id; /**< ID of the sprite. */
+ uint8_t subtype; /**< Subtype of the sprite. */
+} z3_sprite;
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // YAZE_BASE_SPRITE_H_
\ No newline at end of file
diff --git a/incl/system/extension.h b/incl/system/extension.h
new file mode 100644
index 00000000..7c9e2f60
--- /dev/null
+++ b/incl/system/extension.h
@@ -0,0 +1,91 @@
+#ifndef EXTENSION_INTERFACE_H
+#define EXTENSION_INTERFACE_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "yaze.h"
+
+typedef void (*yaze_initialize_func)(yaze_editor_context* context);
+typedef void (*yaze_cleanup_func)(void);
+typedef void (*yaze_extend_ui_func)(yaze_editor_context* context);
+typedef void (*yaze_manipulate_rom_func)(z3_rom* rom);
+typedef void (*yaze_command_func)(void);
+typedef void (*yaze_event_hook_func)(void);
+
+typedef enum {
+ YAZE_EVENT_ROM_LOADED,
+ YAZE_EVENT_ROM_SAVED,
+ YAZE_EVENT_SPRITE_MODIFIED,
+ YAZE_EVENT_PALETTE_CHANGED,
+} yaze_event_type;
+
+/**
+ * @brief Extension interface for Yaze.
+ *
+ * @details Yaze extensions can be written in C or Python.
+ */
+typedef struct yaze_extension {
+ const char* name;
+ const char* version;
+
+ /**
+ * @brief Function to initialize the extension.
+ *
+ * @details This function is called when the extension is loaded. It can be
+ * used to set up any resources or state needed by the extension.
+ */
+ yaze_initialize_func initialize;
+
+ /**
+ * @brief Function to clean up the extension.
+ *
+ * @details This function is called when the extension is unloaded. It can be
+ * used to clean up any resources or state used by the extension.
+ */
+ yaze_cleanup_func cleanup;
+
+ /**
+ * @brief Function to manipulate the ROM.
+ *
+ * @param rom The ROM to manipulate.
+ *
+ */
+ yaze_manipulate_rom_func manipulate_rom;
+
+ /**
+ * @brief Function to extend the UI.
+ *
+ * @param context The editor context.
+ *
+ * @details This function is called when the extension is loaded. It can be
+ * used to add custom UI elements to the editor. The context parameter
+ * provides access to the project, command registry, event dispatcher, and
+ * ImGui context.
+ */
+ yaze_extend_ui_func extend_ui;
+
+ /**
+ * @brief Register commands in the yaze_command_registry.
+ */
+ yaze_command_func register_commands;
+
+ /**
+ * @brief Register custom tools in the yaze_command_registry.
+ */
+ yaze_command_func register_custom_tools;
+
+ /**
+ * @brief Register event hooks in the yaze_event_dispatcher.
+ */
+ void (*register_event_hooks)(yaze_event_type event,
+ yaze_event_hook_func hook);
+
+} yaze_extension;
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // EXTENSION_INTERFACE_H
diff --git a/incl/yaze.h b/incl/yaze.h
new file mode 100644
index 00000000..a0a9a0a9
--- /dev/null
+++ b/incl/yaze.h
@@ -0,0 +1,125 @@
+#ifndef YAZE_H
+#define YAZE_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include
+#include
+
+#include "dungeon.h"
+#include "overworld.h"
+#include "snes_color.h"
+#include "sprite.h"
+
+typedef struct z3_rom z3_rom;
+
+typedef struct yaze_project yaze_project;
+typedef struct yaze_command_registry yaze_command_registry;
+typedef struct yaze_event_dispatcher yaze_event_dispatcher;
+
+/**
+ * @brief Extension editor context.
+ */
+typedef struct yaze_editor_context {
+ z3_rom* rom;
+ yaze_project* project;
+
+ yaze_command_registry* command_registry;
+ yaze_event_dispatcher* event_dispatcher;
+} yaze_editor_context;
+
+/**
+ * @brief Initialize the Yaze library.
+ */
+int yaze_init(yaze_editor_context*);
+
+/**
+ * @brief Clean up the Yaze library.
+ */
+void yaze_cleanup(yaze_editor_context*);
+
+/**
+ * @brief Primitive of a Yaze project.
+ */
+struct yaze_project {
+ const char* name;
+ const char* filepath;
+ const char* rom_filename;
+ const char* code_folder;
+ const char* labels_filename;
+};
+
+yaze_project yaze_load_project(const char* filename);
+
+/**
+ * @brief Primitive of a Zelda3 ROM.
+ */
+struct z3_rom {
+ const char* filename;
+ const uint8_t* data;
+ size_t size;
+ void* impl; // yaze::app::Rom*
+};
+
+/**
+ * @brief Load a Zelda3 ROM from a file.
+ */
+z3_rom* yaze_load_rom(const char* filename);
+
+/**
+ * @brief Unload a Zelda3 ROM.
+ */
+void yaze_unload_rom(z3_rom* rom);
+
+/**
+ * @brief Primitive of a Bitmap
+ */
+typedef struct yaze_bitmap {
+ int width;
+ int height;
+ uint8_t bpp;
+ uint8_t* data;
+} yaze_bitmap;
+
+/**
+ * @brief Load a bitmap from a file.
+ */
+yaze_bitmap yaze_load_bitmap(const char* filename);
+
+/**
+ * @brief Get a color from a palette set.
+ */
+snes_color yaze_get_color_from_paletteset(const z3_rom* rom, int palette_set,
+ int palette, int color);
+
+/**
+ * @brief Load the overworld from a Zelda3 ROM.
+ */
+z3_overworld* yaze_load_overworld(const z3_rom* rom);
+
+/**
+ * @brief Check the version of the Yaze library.
+ */
+void yaze_check_version(const char* version);
+
+/**
+ * @brief Command registry.
+ */
+struct yaze_command_registry {
+ void (*register_command)(const char* name, void (*command)(void));
+};
+
+/**
+ * @brief Event dispatcher.
+ */
+struct yaze_event_dispatcher {
+ void (*register_event_hook)(void (*event_hook)(void));
+};
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // YAZE_H
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 81fd937b..58c2a023 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -1,32 +1,3 @@
-set(
- YAZE_APP_CORE_SRC
- app/core/common.cc
- app/core/controller.cc
- app/core/labeling.cc
- app/emu/emulator.cc
-)
-
-set(
- YAZE_APP_GFX_SRC
- app/gfx/bitmap.cc
- app/gfx/compression.cc
- app/gfx/scad_format.cc
- app/gfx/snes_palette.cc
- app/gfx/snes_tile.cc
- app/gfx/snes_color.cc
- app/gfx/tilesheet.cc
-)
-
-set(
- YAZE_GUI_SRC
- app/gui/asset_browser.cc
- app/gui/canvas.cc
- app/gui/input.cc
- app/gui/style.cc
- app/gui/color.cc
- app/gui/zeml.cc
-)
-
set(
YAZE_APP_EMU_SRC
app/emu/audio/apu.cc
@@ -43,70 +14,125 @@ set(
app/emu/snes.cc
)
-set(SDL_TARGETS SDL2::SDL2)
+set(YAZE_RESOURCE_FILES
+ ${CMAKE_SOURCE_DIR}/assets/layouts/overworld.zeml
+ ${CMAKE_SOURCE_DIR}/assets/font/Karla-Regular.ttf
+ ${CMAKE_SOURCE_DIR}/assets/font/Roboto-Medium.ttf
+ ${CMAKE_SOURCE_DIR}/assets/font/Cousine-Regular.ttf
+ ${CMAKE_SOURCE_DIR}/assets/font/DroidSans.ttf
+ ${CMAKE_SOURCE_DIR}/assets/font/NotoSansJP.ttf
+ ${CMAKE_SOURCE_DIR}/assets/font/IBMPlexSansJP-Bold.ttf
+ ${CMAKE_SOURCE_DIR}/assets/font/MaterialIcons-Regular.ttf
+)
-if(WIN32 OR MINGW)
- list(PREPEND SDL_TARGETS SDL2::SDL2main ws2_32)
- add_definitions(-DSDL_MAIN_HANDLED)
-endif()
-
-if (WIN32 OR MINGW OR UNIX AND NOT APPLE)
- list(APPEND YAZE_APP_CORE_SRC
- app/core/platform/font_loader.cc
- app/core/platform/clipboard.cc
+foreach (FILE ${YAZE_RESOURCE_FILES})
+ file(RELATIVE_PATH NEW_FILE "${CMAKE_SOURCE_DIR}/assets" ${FILE})
+ get_filename_component(NEW_FILE_PATH ${NEW_FILE} DIRECTORY)
+ set_source_files_properties(${FILE}
+ PROPERTIES
+ MACOSX_PACKAGE_LOCATION "Resources/${NEW_FILE_PATH}"
)
+endforeach()
+
+if (YAZE_BUILD_APP)
+ include(app/app.cmake)
+endif()
+if (YAZE_BUILD_EMU)
+ include(app/emu/emu.cmake)
+endif()
+if (YAZE_BUILD_Z3ED)
+ include(cli/z3ed.cmake)
+endif()
+if (YAZE_BUILD_PYTHON)
+ include(cli/python/yaze_py.cmake)
+endif()
+if (YAZE_BUILD_TESTS)
+ include(test/CMakeLists.txt)
endif()
-if(APPLE)
- list(APPEND YAZE_APP_CORE_SRC
- app/core/platform/file_dialog.mm
- app/core/platform/app_delegate.mm
- app/core/platform/font_loader.mm
- app/core/platform/clipboard.mm
- app/core/platform/file_path.mm
- )
-
- find_library(COCOA_LIBRARY Cocoa)
- if(NOT COCOA_LIBRARY)
- message(FATAL_ERROR "Cocoa not found")
- endif()
- set(CMAKE_EXE_LINKER_FLAGS "-framework ServiceManagement -framework Foundation -framework Cocoa")
-endif()
-
-include(app/CMakeLists.txt)
-# include(cli/CMakeLists.txt) Excluded for now, macOS include breaks action build
-
-if (UNIX)
- target_compile_definitions(yaze PRIVATE "linux")
- target_compile_definitions(yaze PRIVATE "stricmp=strcasecmp")
-endif()
-
-if(MACOS)
- set(MACOSX_BUNDLE_ICON_FILE ${CMAKE_SOURCE_DIR}/yaze.ico)
- set_target_properties(yaze
- PROPERTIES
- BUNDLE True
- ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/lib"
- LIBRARY_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/lib"
- RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin"
- MACOSX_BUNDLE_INFO_PLIST ${CMAKE_SOURCE_DIR}/cmake/yaze.plist.in
+if(MACOS)
+ set(MACOSX_BUNDLE_ICON_FILE ${CMAKE_SOURCE_DIR}/win32/yaze.ico)
+ set_target_properties(yaze
+ PROPERTIES
+ BUNDLE True
+ ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/lib"
+ LIBRARY_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/lib"
+ RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin"
+ MACOSX_BUNDLE_INFO_PLIST ${CMAKE_SOURCE_DIR}/cmake/yaze.plist.in
+ RESOURCE ${YAZE_RESOURCE_FILES}
)
elseif(UNIX)
- set_target_properties(yaze
+ set_target_properties(yaze
PROPERTIES
BUNDLE True
ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/lib"
LIBRARY_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/lib"
RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin"
)
+ target_compile_definitions(yaze PRIVATE "linux")
+ target_compile_definitions(yaze PRIVATE "stricmp=strcasecmp")
else()
-set_target_properties(yaze
+ set_target_properties(yaze
PROPERTIES
ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/lib"
LIBRARY_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/lib"
RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin"
- LINK_FLAGS "${CMAKE_CURRENT_SOURCE_DIR}/yaze.res"
-)
+ LINK_FLAGS "${CMAKE_CURRENT_SOURCE_DIR}/win32/yaze.res"
+ )
endif()
-add_subdirectory(test)
\ No newline at end of file
+# Yaze C API
+if (YAZE_BUILD_LIB)
+ add_library(
+ yaze_c SHARED
+ ./yaze.cc
+ app/rom.cc
+ ${YAZE_APP_EMU_SRC}
+ ${YAZE_APP_CORE_SRC}
+ ${YAZE_APP_EDITOR_SRC}
+ ${YAZE_APP_GFX_SRC}
+ ${YAZE_APP_ZELDA3_SRC}
+ ${YAZE_GUI_SRC}
+ ${IMGUI_SRC}
+ ${IMGUI_TEST_ENGINE_SOURCES}
+ )
+
+ target_include_directories(
+ yaze_c PUBLIC
+ lib/
+ app/
+ ${CMAKE_SOURCE_DIR}/incl/
+ ${CMAKE_SOURCE_DIR}/src/
+ ${PNG_INCLUDE_DIRS}
+ ${SDL2_INCLUDE_DIR}
+ )
+
+ target_link_libraries(
+ yaze_c PRIVATE
+ ${ABSL_TARGETS}
+ ${SDL_TARGETS}
+ ${PNG_LIBRARIES}
+ ${CMAKE_DL_LIBS}
+ ImGuiTestEngine
+ ImGui
+ )
+
+ if (YAZE_INSTALL_LIB)
+ install(TARGETS yaze_c
+ RUNTIME DESTINATION bin
+ LIBRARY DESTINATION lib
+ ARCHIVE DESTINATION lib/static)
+
+ install(
+ FILES
+ yaze.h
+ incl/sprite.h
+ incl/snes_tile.h
+ incl/snes_color.h
+ incl/overworld.h
+ incl/dungeon.h
+ DESTINATION
+ include
+ )
+ endif()
+endif()
diff --git a/src/app/CMakeLists.txt b/src/app/CMakeLists.txt
deleted file mode 100644
index 6f6dd4eb..00000000
--- a/src/app/CMakeLists.txt
+++ /dev/null
@@ -1,40 +0,0 @@
-include(app/editor/CMakeLists.txt)
-include(app/zelda3/CMakeLists.txt)
-
-add_executable(
- yaze
- app/yaze.cc
- app/rom.cc
- ${YAZE_APP_EMU_SRC}
- ${YAZE_APP_CORE_SRC}
- ${YAZE_APP_EDITOR_SRC}
- ${YAZE_APP_GFX_SRC}
- ${YAZE_APP_ZELDA3_SRC}
- ${YAZE_GUI_SRC}
- ${IMGUI_SRC}
- ${IMGUI_TEST_ENGINE_SOURCES}
-)
-
-target_include_directories(
- yaze PUBLIC
- lib/
- app/
- ${CMAKE_SOURCE_DIR}/src/
- ${CMAKE_SOURCE_DIR}/src/lib/imgui_test_engine
- ${PNG_INCLUDE_DIRS}
- ${SDL2_INCLUDE_DIR}
-)
-
-target_link_libraries(
- yaze PUBLIC
- ${ABSL_TARGETS}
- ${SDL_TARGETS}
- ${PNG_LIBRARIES}
- ${CMAKE_DL_LIBS}
- ImGuiTestEngine
- ImGui
-)
-
-if (APPLE)
- target_link_libraries(yaze PUBLIC ${COCOA_LIBRARY})
-endif()
\ No newline at end of file
diff --git a/src/app/app.cmake b/src/app/app.cmake
new file mode 100644
index 00000000..a00f9c96
--- /dev/null
+++ b/src/app/app.cmake
@@ -0,0 +1,73 @@
+include(app/core/core.cmake)
+include(app/editor/editor.cmake)
+include(app/gfx/gfx.cmake)
+include(app/gui/gui.cmake)
+include(app/zelda3/zelda3.cmake)
+
+if (APPLE)
+add_executable(
+ yaze
+ MACOSX_BUNDLE
+ app/main.cc
+ app/rom.cc
+ ${YAZE_APP_EMU_SRC}
+ ${YAZE_APP_CORE_SRC}
+ ${YAZE_APP_EDITOR_SRC}
+ ${YAZE_APP_GFX_SRC}
+ ${YAZE_APP_ZELDA3_SRC}
+ ${YAZE_GUI_SRC}
+ ${IMGUI_SRC}
+
+ # Bundled Resources
+ ${YAZE_RESOURCE_FILES}
+)
+else()
+add_executable(
+ yaze
+ app/main.cc
+ app/rom.cc
+ ${YAZE_APP_EMU_SRC}
+ ${YAZE_APP_CORE_SRC}
+ ${YAZE_APP_EDITOR_SRC}
+ ${YAZE_APP_GFX_SRC}
+ ${YAZE_APP_ZELDA3_SRC}
+ ${YAZE_GUI_SRC}
+ ${IMGUI_SRC}
+)
+endif()
+
+target_include_directories(
+ yaze PUBLIC
+ lib/
+ app/
+ ${ASAR_INCLUDE_DIR}
+ ${CMAKE_SOURCE_DIR}/incl/
+ ${CMAKE_SOURCE_DIR}/src/
+ ${CMAKE_SOURCE_DIR}/src/lib/imgui_test_engine
+ ${PNG_INCLUDE_DIRS}
+ ${SDL2_INCLUDE_DIR}
+)
+
+target_link_libraries(
+ yaze PUBLIC
+ asar-static
+ ${ABSL_TARGETS}
+ ${SDL_TARGETS}
+ ${PNG_LIBRARIES}
+ ${CMAKE_DL_LIBS}
+ ImGui
+ ImGuiTestEngine
+)
+
+if (APPLE)
+ target_link_libraries(yaze PUBLIC ${COCOA_LIBRARY})
+endif()
+
+if (WIN32 OR MINGW)
+ target_link_libraries(
+ yaze PUBLIC
+ ${CMAKE_SOURCE_DIR}/build/build-windows/bin/libpng16.dll
+ zlib
+ mingw32
+ ws2_32)
+endif()
diff --git a/src/app/core/common.cc b/src/app/core/common.cc
index d751994d..4cfe1919 100644
--- a/src/app/core/common.cc
+++ b/src/app/core/common.cc
@@ -1,107 +1,60 @@
#include "common.h"
-#include "imgui/imgui.h"
+#include
-#include
#include
-#include
+#include
#include
-#include
#include
+#include
+#include "absl/status/statusor.h"
#include "absl/strings/str_format.h"
+#include "absl/strings/string_view.h"
namespace yaze {
namespace app {
namespace core {
-std::shared_ptr ExperimentFlags::flags_;
+namespace {
-std::string UppercaseHexByte(uint8_t byte, bool leading) {
- if (leading) {
- std::string result = absl::StrFormat("0x%02X", byte);
- return result;
- }
- std::string result = absl::StrFormat("%02X", byte);
- return result;
-}
-std::string UppercaseHexWord(uint16_t word) {
- std::string result = absl::StrFormat("0x%04X", word);
- return result;
-}
-std::string UppercaseHexLong(uint32_t dword) {
- std::string result = absl::StrFormat("0x%06X", dword);
- return result;
-}
-
-uint32_t SnesToPc(uint32_t addr) {
- if (addr >= 0x808000) {
- addr -= 0x808000;
- }
- uint32_t temp = (addr & 0x7FFF) + ((addr / 2) & 0xFF8000);
- return (temp + 0x0);
-}
-
-uint32_t PcToSnes(uint32_t addr) {
- uint8_t *b = reinterpret_cast(&addr);
- b[2] = static_cast(b[2] * 2);
-
- if (b[1] >= 0x80) {
- b[2] += 1;
- } else {
- b[1] += 0x80;
- }
-
- return addr;
-}
-
-uint32_t MapBankToWordAddress(uint8_t bank, uint16_t addr) {
- uint32_t result = 0;
- result = (bank << 16) | addr;
- return result;
-}
-
-int AddressFromBytes(uint8_t addr1, uint8_t addr2, uint8_t addr3) {
- return (addr1 << 16) | (addr2 << 8) | addr3;
-}
-
-// hextodec has been imported from SNESDisasm to parse hex numbers
-int HexToDec(char *input, int length) {
- int result = 0;
- int value;
- int ceiling = length - 1;
- int power16 = 16;
-
- int j = ceiling;
-
- for (; j >= 0; j--) {
- if (input[j] >= 'A' && input[j] <= 'F') {
- value = input[j] - 'F';
- value += 15;
- } else {
- value = input[j] - '9';
- value += 9;
+void encode(uint64_t data, std::vector &output) {
+ while (true) {
+ uint8_t x = data & 0x7f;
+ data >>= 7;
+ if (data == 0) {
+ output.push_back(0x80 | x);
+ break;
}
-
- if (j == ceiling) {
- result += value;
- continue;
- }
-
- result += (value * power16);
- power16 *= 16;
+ output.push_back(x);
+ data--;
}
-
- return result;
}
-bool StringReplace(std::string &str, const std::string &from,
- const std::string &to) {
- size_t start = str.find(from);
- if (start == std::string::npos) return false;
+uint64_t decode(const std::vector &input, size_t &offset) {
+ uint64_t data = 0;
+ uint64_t shift = 1;
+ while (true) {
+ uint8_t x = input[offset++];
+ data += (x & 0x7f) * shift;
+ if (x & 0x80) break;
+ shift <<= 7;
+ data += shift;
+ }
+ return data;
+}
- str.replace(start, from.length(), to);
- return true;
+uint32_t crc32(const std::vector &data) {
+ uint32_t crc = ::crc32(0L, Z_NULL, 0);
+ return ::crc32(crc, data.data(), data.size());
+}
+
+// "load little endian value at the given byte offset and shift to get its
+// value relative to the base offset (powers of 256, essentially)"
+unsigned ldle(uint8_t const *const p_arr, unsigned const p_index) {
+ uint32_t v = p_arr[p_index];
+ v <<= (8 * p_index);
+ return v;
}
void stle(uint8_t *const p_arr, size_t const p_index, unsigned const p_val) {
@@ -125,25 +78,6 @@ void stle2(uint8_t *const p_arr, unsigned const p_val) {
void stle3(uint8_t *const p_arr, unsigned const p_val) {
stle(p_arr, 3, p_val);
}
-void stle16b(uint8_t *const p_arr, uint16_t const p_val) {
- stle0(p_arr, p_val);
- stle1(p_arr, p_val);
-}
-// "Store little endian 16-bit value using a byte pointer, offset by an
-// index before dereferencing"
-void stle16b_i(uint8_t *const p_arr, size_t const p_index,
- uint16_t const p_val) {
- stle16b(p_arr + (p_index * 2), p_val);
-}
-// "load little endian value at the given byte offset and shift to get its
-// value relative to the base offset (powers of 256, essentially)"
-unsigned ldle(uint8_t const *const p_arr, unsigned const p_index) {
- uint32_t v = p_arr[p_index];
-
- v <<= (8 * p_index);
-
- return v;
-}
// Helper function to get the first byte in a little endian number
uint32_t ldle0(uint8_t const *const p_arr) { return ldle(p_arr, 0); }
@@ -156,32 +90,244 @@ uint32_t ldle2(uint8_t const *const p_arr) { return ldle(p_arr, 2); }
// Helper function to get the third byte in a little endian number
uint32_t ldle3(uint8_t const *const p_arr) { return ldle(p_arr, 3); }
-// Load little endian halfword (16-bit) dereferenced from
-uint16_t ldle16b(uint8_t const *const p_arr) {
- uint16_t v = 0;
- v |= (ldle0(p_arr) | ldle1(p_arr));
+} // namespace
- return v;
+std::string UppercaseHexByte(uint8_t byte, bool leading) {
+ if (leading) {
+ std::string result = absl::StrFormat("0x%02X", byte);
+ return result;
+ }
+ std::string result = absl::StrFormat("%02X", byte);
+ return result;
}
-// Load little endian halfword (16-bit) dereferenced from an arrays of bytes.
-// This version provides an index that will be multiplied by 2 and added to the
-// base address.
-uint16_t ldle16b_i(uint8_t const *const p_arr, size_t const p_index) {
- return ldle16b(p_arr + (2 * p_index));
+std::string UppercaseHexWord(uint16_t word, bool leading) {
+ if (leading) {
+ std::string result = absl::StrFormat("0x%04X", word);
+ return result;
+ }
+ std::string result = absl::StrFormat("%04X", word);
+ return result;
+}
+std::string UppercaseHexLong(uint32_t dword) {
+ std::string result = absl::StrFormat("0x%06X", dword);
+ return result;
+}
+std::string UppercaseHexLongLong(uint64_t qword) {
+ std::string result = absl::StrFormat("0x%08X", qword);
+ return result;
}
-// Initialize the static member
-std::stack ImGuiIdIssuer::idStack;
+bool StringReplace(std::string &str, const std::string &from,
+ const std::string &to) {
+ size_t start = str.find(from);
+ if (start == std::string::npos) return false;
+
+ str.replace(start, from.length(), to);
+ return true;
+}
+
+std::shared_ptr ExperimentFlags::flags_;
uint32_t Get24LocalFromPC(uint8_t *data, int addr, bool pc) {
- uint32_t ret = (PcToSnes(addr) & 0xFF0000) | (data[addr + 1] << 8) | data[addr];
+ uint32_t ret =
+ (PcToSnes(addr) & 0xFF0000) | (data[addr + 1] << 8) | data[addr];
if (pc) {
return SnesToPc(ret);
}
return ret;
}
+void stle16b_i(uint8_t *const p_arr, size_t const p_index,
+ uint16_t const p_val) {
+ stle16b(p_arr + (p_index * 2), p_val);
+}
+
+void stle16b(uint8_t *const p_arr, uint16_t const p_val) {
+ stle0(p_arr, p_val);
+ stle1(p_arr, p_val);
+}
+
+uint16_t ldle16b(uint8_t const *const p_arr) {
+ uint16_t v = 0;
+ v |= (ldle0(p_arr) | ldle1(p_arr));
+ return v;
+}
+
+uint16_t ldle16b_i(uint8_t const *const p_arr, size_t const p_index) {
+ return ldle16b(p_arr + (2 * p_index));
+}
+
+void CreateBpsPatch(const std::vector &source,
+ const std::vector &target,
+ std::vector &patch) {
+ patch.clear();
+ patch.insert(patch.end(), {'B', 'P', 'S', '1'});
+
+ encode(source.size(), patch);
+ encode(target.size(), patch);
+ encode(0, patch); // No metadata
+
+ size_t source_offset = 0;
+ size_t target_offset = 0;
+ int64_t source_rel_offset = 0;
+ int64_t target_rel_offset = 0;
+
+ while (target_offset < target.size()) {
+ if (source_offset < source.size() &&
+ source[source_offset] == target[target_offset]) {
+ size_t length = 0;
+ while (source_offset + length < source.size() &&
+ target_offset + length < target.size() &&
+ source[source_offset + length] == target[target_offset + length]) {
+ length++;
+ }
+ encode((length - 1) << 2 | 0, patch); // SourceRead
+ source_offset += length;
+ target_offset += length;
+ } else {
+ size_t length = 0;
+ while (
+ target_offset + length < target.size() &&
+ (source_offset + length >= source.size() ||
+ source[source_offset + length] != target[target_offset + length])) {
+ length++;
+ }
+ if (length > 0) {
+ encode((length - 1) << 2 | 1, patch); // TargetRead
+ for (size_t i = 0; i < length; i++) {
+ patch.push_back(target[target_offset + i]);
+ }
+ target_offset += length;
+ }
+ }
+
+ // SourceCopy
+ if (source_offset < source.size()) {
+ size_t length = 0;
+ int64_t offset = source_offset - source_rel_offset;
+ while (source_offset + length < source.size() &&
+ target_offset + length < target.size() &&
+ source[source_offset + length] == target[target_offset + length]) {
+ length++;
+ }
+ if (length > 0) {
+ encode((length - 1) << 2 | 2, patch);
+ encode((offset < 0 ? 1 : 0) | (abs(offset) << 1), patch);
+ source_offset += length;
+ target_offset += length;
+ source_rel_offset = source_offset;
+ }
+ }
+
+ // TargetCopy
+ if (target_offset > 0) {
+ size_t length = 0;
+ int64_t offset = target_offset - target_rel_offset;
+ while (target_offset + length < target.size() &&
+ target[target_offset - 1] == target[target_offset + length]) {
+ length++;
+ }
+ if (length > 0) {
+ encode((length - 1) << 2 | 3, patch);
+ encode((offset < 0 ? 1 : 0) | (abs(offset) << 1), patch);
+ target_offset += length;
+ target_rel_offset = target_offset;
+ }
+ }
+ }
+
+ patch.resize(patch.size() + 12); // Make space for the checksums
+ uint32_t source_checksum = crc32(source);
+ uint32_t target_checksum = crc32(target);
+ uint32_t patch_checksum = crc32(patch);
+
+ memcpy(patch.data() + patch.size() - 12, &source_checksum, sizeof(uint32_t));
+ memcpy(patch.data() + patch.size() - 8, &target_checksum, sizeof(uint32_t));
+ memcpy(patch.data() + patch.size() - 4, &patch_checksum, sizeof(uint32_t));
+}
+
+void ApplyBpsPatch(const std::vector &source,
+ const std::vector &patch,
+ std::vector &target) {
+ if (patch.size() < 4 || patch[0] != 'B' || patch[1] != 'P' ||
+ patch[2] != 'S' || patch[3] != '1') {
+ throw std::runtime_error("Invalid patch format");
+ }
+
+ size_t patch_offset = 4;
+ uint64_t target_size = decode(patch, patch_offset);
+ uint64_t metadata_size = decode(patch, patch_offset);
+ patch_offset += metadata_size;
+
+ target.resize(target_size);
+ size_t source_offset = 0;
+ size_t target_offset = 0;
+ int64_t source_rel_offset = 0;
+ int64_t target_rel_offset = 0;
+
+ while (patch_offset < patch.size() - 12) {
+ uint64_t data = decode(patch, patch_offset);
+ uint64_t command = data & 3;
+ uint64_t length = (data >> 2) + 1;
+
+ switch (command) {
+ case 0: // SourceRead
+ while (length--) {
+ target[target_offset++] = source[source_offset++];
+ }
+ break;
+ case 1: // TargetRead
+ while (length--) {
+ target[target_offset++] = patch[patch_offset++];
+ }
+ break;
+ case 2: // SourceCopy
+ {
+ int64_t offsetData = decode(patch, patch_offset);
+ source_rel_offset += (offsetData & 1 ? -1 : +1) * (offsetData >> 1);
+ while (length--) {
+ target[target_offset++] = source[source_rel_offset++];
+ }
+ } break;
+ case 3: // TargetCopy
+ {
+ uint64_t offsetData = decode(patch, patch_offset);
+ target_rel_offset += (offsetData & 1 ? -1 : +1) * (offsetData >> 1);
+ while (length--) {
+ target[target_offset++] = target[target_rel_offset++];
+ }
+ }
+ default:
+ throw std::runtime_error("Invalid patch command");
+ }
+ }
+
+ uint32_t source_checksum;
+ uint32_t target_checksum;
+ uint32_t patch_checksum;
+ memcpy(&source_checksum, patch.data() + patch.size() - 12, sizeof(uint32_t));
+ memcpy(&target_checksum, patch.data() + patch.size() - 8, sizeof(uint32_t));
+ memcpy(&patch_checksum, patch.data() + patch.size() - 4, sizeof(uint32_t));
+
+ if (source_checksum != crc32(source) || target_checksum != crc32(target) ||
+ patch_checksum !=
+ crc32(std::vector(patch.begin(), patch.end() - 4))) {
+ throw std::runtime_error("Checksum mismatch");
+ }
+}
+
+absl::StatusOr CheckVersion(const char *version) {
+ std::string version_string = version;
+ if (version_string != kYazeVersion) {
+ std::string message =
+ absl::StrFormat("Yaze version mismatch: expected %s, got %s",
+ kYazeVersion.data(), version_string.c_str());
+ return absl::InvalidArgumentError(message);
+ }
+ return version_string;
+}
+
} // namespace core
} // namespace app
} // namespace yaze
diff --git a/src/app/core/common.h b/src/app/core/common.h
index 0fd56f7b..f4f0791d 100644
--- a/src/app/core/common.h
+++ b/src/app/core/common.h
@@ -1,17 +1,16 @@
#ifndef YAZE_CORE_COMMON_H
#define YAZE_CORE_COMMON_H
-#include "imgui/imgui.h"
-
-#include
#include
#include
-#include
#include
#include
-#include
#include
+#include "absl/status/statusor.h"
+#include "absl/strings/str_format.h"
+#include "absl/container/flat_hash_map.h"
+
namespace yaze {
namespace app {
@@ -21,16 +20,21 @@ namespace app {
*/
namespace core {
+std::string UppercaseHexByte(uint8_t byte, bool leading = false);
+std::string UppercaseHexWord(uint16_t word, bool leading = false);
+std::string UppercaseHexLong(uint32_t dword);
+std::string UppercaseHexLongLong(uint64_t qword);
+
+bool StringReplace(std::string &str, const std::string &from,
+ const std::string &to);
+
/**
* @class ExperimentFlags
* @brief A class to manage experimental feature flags.
*/
class ExperimentFlags {
- public:
+public:
struct Flags {
- // Bitmap manager abstraction to manage graphics bin of Rom.
- bool kUseBitmapManager = true;
-
// Log instructions to the GUI debugger.
bool kLogInstructions = true;
@@ -56,22 +60,18 @@ class ExperimentFlags {
// Use the new platform specific file dialog wrappers.
bool kNewFileDialogWrapper = true;
- // Platform specific loading of fonts from the system. Currently
- // only supports macOS.
- bool kLoadSystemFonts = true;
-
// Uses texture streaming from SDL for my dynamic updates.
bool kLoadTexturesAsStreaming = true;
// Save dungeon map edits to the Rom.
bool kSaveDungeonMaps = false;
+ // Save graphics sheet to the Rom.
+ bool kSaveGraphicsSheet = false;
+
// Log to the console.
bool kLogToConsole = false;
- // Load audio device for emulator
- bool kLoadAudioDevice = false;
-
// Overworld flags
struct Overworld {
// Load and render overworld sprites to the screen. Unstable.
@@ -91,6 +91,9 @@ class ExperimentFlags {
// Save overworld properties to the Rom.
bool kSaveOverworldProperties = true;
+
+ // Load custom overworld data from the ROM and enable UI.
+ bool kLoadCustomOverworld = false;
} overworld;
};
@@ -109,8 +112,44 @@ class ExperimentFlags {
}
return flags_.get();
}
+ std::string Serialize() const {
+ std::string result;
+ result +=
+ "kLogInstructions: " + std::to_string(flags_->kLogInstructions) + "\n";
+ result +=
+ "kUseNewImGuiInput: " + std::to_string(flags_->kUseNewImGuiInput) +
+ "\n";
+ result +=
+ "kSaveAllPalettes: " + std::to_string(flags_->kSaveAllPalettes) + "\n";
+ result +=
+ "kSaveGfxGroups: " + std::to_string(flags_->kSaveGfxGroups) + "\n";
+ result += "kSaveWithChangeQueue: " +
+ std::to_string(flags_->kSaveWithChangeQueue) + "\n";
+ result += "kDrawDungeonRoomGraphics: " +
+ std::to_string(flags_->kDrawDungeonRoomGraphics) + "\n";
+ result += "kNewFileDialogWrapper: " +
+ std::to_string(flags_->kNewFileDialogWrapper) + "\n";
+ result += "kLoadTexturesAsStreaming: " +
+ std::to_string(flags_->kLoadTexturesAsStreaming) + "\n";
+ result +=
+ "kSaveDungeonMaps: " + std::to_string(flags_->kSaveDungeonMaps) + "\n";
+ result += "kLogToConsole: " + std::to_string(flags_->kLogToConsole) + "\n";
+ result += "kDrawOverworldSprites: " +
+ std::to_string(flags_->overworld.kDrawOverworldSprites) + "\n";
+ result += "kSaveOverworldMaps: " +
+ std::to_string(flags_->overworld.kSaveOverworldMaps) + "\n";
+ result += "kSaveOverworldEntrances: " +
+ std::to_string(flags_->overworld.kSaveOverworldEntrances) + "\n";
+ result += "kSaveOverworldExits: " +
+ std::to_string(flags_->overworld.kSaveOverworldExits) + "\n";
+ result += "kSaveOverworldItems: " +
+ std::to_string(flags_->overworld.kSaveOverworldItems) + "\n";
+ result += "kSaveOverworldProperties: " +
+ std::to_string(flags_->overworld.kSaveOverworldProperties) + "\n";
+ return result;
+ }
- private:
+private:
static std::shared_ptr flags_;
};
@@ -119,9 +158,8 @@ class ExperimentFlags {
* @brief A class to manage a value that can be modified and notify when it
* changes.
*/
-template
-class NotifyValue {
- public:
+template class NotifyValue {
+public:
NotifyValue() : value_(), modified_(false), temp_value_() {}
NotifyValue(const T &value)
: value_(value), modified_(false), temp_value_() {}
@@ -154,69 +192,108 @@ class NotifyValue {
bool modified() const { return modified_; }
- private:
+private:
T value_;
bool modified_;
T temp_value_;
};
-class ImGuiIdIssuer {
- private:
- static std::stack idStack;
+static bool log_to_console = false;
+static std::string log_file_out = "log.txt";
- public:
- // Generate and push a new ID onto the stack
- static ImGuiID GetNewID() {
- static int counter = 1; // Start from 1 to ensure uniqueness
- ImGuiID child_id = ImGui::GetID((void *)(intptr_t)counter++);
- idStack.push(child_id);
- return child_id;
+template
+static void logf(const absl::FormatSpec &format, const Args &...args) {
+ std::string message = absl::StrFormat(format, args...);
+ if (log_to_console) {
+ std::cout << message << std::endl;
}
+ static std::ofstream fout(log_file_out, std::ios::out | std::ios::app);
+ fout << message << std::endl;
+}
- // Pop all IDs from the stack (can be called explicitly or upon program exit)
- static void Cleanup() {
- while (!idStack.empty()) {
- idStack.pop();
- }
- }
+struct StructuredLog {
+ std::string raw_message;
+ std::string category;
};
+static absl::flat_hash_map> log_categories;
+
+template
+static void logm(const std::string &category,
+ const absl::FormatSpec &format, const Args &...args) {
+ std::string message = absl::StrFormat(format, args...);
+ if (log_to_console) {
+ std::cout << category << ": " << message << std::endl;
+ }
+ if (log_categories.contains(category)) {
+ log_categories[category].push_back(message);
+ } else {
+ log_categories[category] = {message};
+ }
+}
+
class Logger {
- public:
+public:
static void log(std::string message) {
- static std::ofstream fout("log.txt", std::ios::out | std::ios::app);
+ static std::ofstream fout(log_file_out, std::ios::out | std::ios::app);
fout << message << std::endl;
}
-
- // log to console
- static void logc(std::string message) { logs.emplace_back(message); }
-
- static std::vector logs;
};
-std::string UppercaseHexByte(uint8_t byte, bool leading = false);
-std::string UppercaseHexWord(uint16_t word);
-std::string UppercaseHexLong(uint32_t dword);
+constexpr uint32_t kFastRomRegion = 0x808000;
-uint32_t SnesToPc(uint32_t addr);
-uint32_t PcToSnes(uint32_t addr);
+inline uint32_t SnesToPc(uint32_t addr) noexcept {
+ if (addr >= kFastRomRegion) {
+ addr -= kFastRomRegion;
+ }
+ uint32_t temp = (addr & 0x7FFF) + ((addr / 2) & 0xFF8000);
+ return (temp + 0x0);
+}
-uint32_t MapBankToWordAddress(uint8_t bank, uint16_t addr);
+inline uint32_t PcToSnes(uint32_t addr) {
+ uint8_t *b = reinterpret_cast(&addr);
+ b[2] = static_cast(b[2] * 2);
-int AddressFromBytes(uint8_t addr1, uint8_t addr2, uint8_t addr3);
-int HexToDec(char *input, int length);
+ if (b[1] >= 0x80) {
+ b[2] += 1;
+ } else {
+ b[1] += 0x80;
+ }
-bool StringReplace(std::string &str, const std::string &from,
- const std::string &to);
+ return addr;
+}
+inline int AddressFromBytes(uint8_t bank, uint8_t high, uint8_t low) noexcept {
+ return (bank << 16) | (high << 8) | low;
+}
+
+inline uint32_t MapBankToWordAddress(uint8_t bank, uint16_t addr) noexcept {
+ uint32_t result = 0;
+ result = (bank << 16) | addr;
+ return result;
+}
+
+uint32_t Get24LocalFromPC(uint8_t *data, int addr, bool pc = true);
+
+/**
+ * @brief Store little endian 16-bit value using a byte pointer, offset by an
+ * index before dereferencing
+ */
void stle16b_i(uint8_t *const p_arr, size_t const p_index,
uint16_t const p_val);
-uint16_t ldle16b_i(uint8_t const *const p_arr, size_t const p_index);
-
-uint16_t ldle16b(uint8_t const *const p_arr);
void stle16b(uint8_t *const p_arr, uint16_t const p_val);
+/**
+ * @brief Load little endian halfword (16-bit) dereferenced from an arrays of
+ * bytes. This version provides an index that will be multiplied by 2 and added
+ * to the base address.
+ */
+uint16_t ldle16b_i(uint8_t const *const p_arr, size_t const p_index);
+
+// Load little endian halfword (16-bit) dereferenced from
+uint16_t ldle16b(uint8_t const *const p_arr);
+
struct FolderItem {
std::string name;
std::vector subfolders;
@@ -225,10 +302,20 @@ struct FolderItem {
typedef struct FolderItem FolderItem;
-uint32_t Get24LocalFromPC(uint8_t *data, int addr, bool pc = true);
+void CreateBpsPatch(const std::vector &source,
+ const std::vector &target,
+ std::vector &patch);
-} // namespace core
-} // namespace app
-} // namespace yaze
+void ApplyBpsPatch(const std::vector &source,
+ const std::vector &patch,
+ std::vector &target);
+
+constexpr std::string_view kYazeVersion = "0.2.1";
+
+absl::StatusOr CheckVersion(const char *version);
+
+} // namespace core
+} // namespace app
+} // namespace yaze
#endif
diff --git a/src/app/core/constants.h b/src/app/core/constants.h
index a5e37676..4594aae6 100644
--- a/src/app/core/constants.h
+++ b/src/app/core/constants.h
@@ -1,15 +1,6 @@
#ifndef YAZE_APP_CORE_CONSTANTS_H
#define YAZE_APP_CORE_CONSTANTS_H
-#include
-
-#include "absl/strings/string_view.h"
-
-#define TAB_BAR(w) if (ImGui::BeginTabBar(w)) {
-#define END_TAB_BAR() \
- ImGui::EndTabBar(); \
- }
-
#define TAB_ITEM(w) if (ImGui::BeginTabItem(w)) {
#define END_TAB_ITEM() \
ImGui::EndTabItem(); \
@@ -121,732 +112,5 @@
using ushort = unsigned short;
using uint = unsigned int;
using uchar = unsigned char;
-using Bytes = std::vector;
-
-using OWBlockset = std::vector>;
-struct OWMapTiles {
- OWBlockset light_world; // 64 maps
- OWBlockset dark_world; // 64 maps
- OWBlockset special_world; // 32 maps
-};
-using OWMapTiles = struct OWMapTiles;
-
-namespace yaze {
-namespace app {
-namespace core {
-
-constexpr uint32_t kRedPen = 0xFF0000FF;
-constexpr float kYazeVersion = 0.2;
-
-// ============================================================================
-// Magic numbers
-// ============================================================================
-
-/// Bit set for object priority
-constexpr ushort TilePriorityBit = 0x2000;
-
-/// Bit set for object hflip
-constexpr ushort TileHFlipBit = 0x4000;
-
-/// Bit set for object vflip
-constexpr ushort TileVFlipBit = 0x8000;
-
-/// Bits used for tile name
-constexpr ushort TileNameMask = 0x03FF;
-
-constexpr int Uncompressed3BPPSize = 0x0600;
-constexpr int UncompressedSheetSize = 0x0800;
-
-constexpr int NumberOfRooms = 296;
-
-constexpr int NumberOfColors = 3143;
-
-// ============================================================================
-// Game Graphics
-// ============================================================================
-
-constexpr int tile_address = 0x1B52; // JP = Same
-constexpr int tile_address_floor = 0x1B5A; // JP = Same
-constexpr int subtype1_tiles = 0x8000; // JP = Same
-constexpr int subtype2_tiles = 0x83F0; // JP = Same
-constexpr int subtype3_tiles = 0x84F0; // JP = Same
-constexpr int gfx_animated_pointer = 0x10275; // JP 0x10624 //long pointer
-
-constexpr int hud_palettes = 0xDD660;
-constexpr int maxGfx = 0xC3FB5;
-
-constexpr int kTilesheetWidth = 128;
-constexpr int kTilesheetHeight = 32;
-constexpr int kTilesheetDepth = 8;
-
-// TEXT EDITOR RELATED CONSTANTS
-constexpr int gfx_font = 0x70000; // 2bpp format
-constexpr int text_data = 0xE0000;
-constexpr int text_data2 = 0x75F40;
-constexpr int pointers_dictionaries = 0x74703;
-constexpr int characters_width = 0x74ADF;
-
-constexpr int entrance_gfx_group = 0x5D97;
-
-// ============================================================================
-// Gravestones related variables
-// ============================================================================
-
-constexpr int GravesYTilePos = 0x49968; // short (0x0F entries)
-constexpr int GravesXTilePos = 0x49986; // short (0x0F entries)
-constexpr int GravesTilemapPos = 0x499A4; // short (0x0F entries)
-constexpr int GravesGFX = 0x499C2; // short (0x0F entries)
-
-constexpr int GravesXPos = 0x4994A; // short (0x0F entries)
-constexpr int GravesYLine = 0x4993A; // short (0x08 entries)
-constexpr int GravesCountOnY = 0x499E0; // Byte 0x09 entries
-
-constexpr int GraveLinkSpecialHole = 0x46DD9; // short
-constexpr int GraveLinkSpecialStairs = 0x46DE0; // short
-
-// ============================================================================
-// Names
-// ============================================================================
-
-static const std::string RoomEffect[] = {"Nothing",
- "Nothing",
- "Moving Floor",
- "Moving Water",
- "Trinexx Shell",
- "Red Flashes",
- "Light Torch to See Floor",
- "Ganon's Darkness"};
-
-static const std::string RoomTag[] = {"Nothing",
-
- "NW Kill Enemy to Open",
- "NE Kill Enemy to Open",
- "SW Kill Enemy to Open",
- "SE Kill Enemy to Open",
- "W Kill Enemy to Open",
- "E Kill Enemy to Open",
- "N Kill Enemy to Open",
- "S Kill Enemy to Open",
- "Clear Quadrant to Open",
- "Clear Full Tile to Open",
-
- "NW Push Block to Open",
- "NE Push Block to Open",
- "SW Push Block to Open",
- "SE Push Block to Open",
- "W Push Block to Open",
- "E Push Block to Open",
- "N Push Block to Open",
- "S Push Block to Open",
- "Push Block to Open",
- "Pull Lever to Open",
- "Collect Prize to Open",
-
- "Hold Switch Open Door",
- "Toggle Switch to Open Door",
- "Turn off Water",
- "Turn on Water",
- "Water Gate",
- "Water Twin",
- "Moving Wall Right",
- "Moving Wall Left",
- "Crash",
- "Crash",
- "Push Switch Exploding Wall",
- "Holes 0",
- "Open Chest (Holes 0)",
- "Holes 1",
- "Holes 2",
- "Defeat Boss for Dungeon Prize",
-
- "SE Kill Enemy to Push Block",
- "Trigger Switch Chest",
- "Pull Lever Exploding Wall",
- "NW Kill Enemy for Chest",
- "NE Kill Enemy for Chest",
- "SW Kill Enemy for Chest",
- "SE Kill Enemy for Chest",
- "W Kill Enemy for Chest",
- "E Kill Enemy for Chest",
- "N Kill Enemy for Chest",
- "S Kill Enemy for Chest",
- "Clear Quadrant for Chest",
- "Clear Full Tile for Chest",
-
- "Light Torches to Open",
- "Holes 3",
- "Holes 4",
- "Holes 5",
- "Holes 6",
- "Agahnim Room",
- "Holes 7",
- "Holes 8",
- "Open Chest for Holes 8",
- "Push Block for Chest",
- "Clear Room for Triforce Door",
- "Light Torches for Chest",
- "Kill Boss Again"};
-
-static const std::string SecretItemNames[] = {
- "Nothing", "Green Rupee", "Rock hoarder", "Bee", "Health pack",
- "Bomb", "Heart ", "Blue Rupee",
-
- "Key", "Arrow", "Bomb", "Heart", "Magic",
- "Full Magic", "Cucco", "Green Soldier", "Bush Stal", "Blue Soldier",
-
- "Landmine", "Heart", "Fairy", "Heart",
- "Nothing ", // 22
-
- "Hole", "Warp", "Staircase", "Bombable", "Switch"};
-
-static const std::string TileTypeNames[] = {
- "$00 Nothing (standard floor)",
- "$01 Collision",
- "$02 Collision",
- "$03 Collision",
- "$04 Collision",
- "$05 Nothing (unused?)",
- "$06 Nothing (unused?)",
- "$07 Nothing (unused?)",
- "$08 Deep water",
- "$09 Shallow water",
- "$0A Unknown? Possibly unused",
- "$0B Collision (different in Overworld and unknown)",
- "$0C Overlay mask",
- "$0D Spike floor",
- "$0E GT ice",
- "$0F Ice palace ice",
- "$10 Slope ◤",
- "$11 Slope ◥",
- "$12 Slope ◣",
- "$13 Slope ◢",
- "$14 Nothing (unused?)",
- "$15 Nothing (unused?)",
- "$16 Nothing (unused?)",
- "$17 Nothing (unused?)",
- "$18 Slope ◤",
- "$19 Slope ◥",
- "$1A Slope ◣",
- "$1B Slope ◢",
- "$1C Layer 2 overlay",
- "$1D North single-layer auto stairs",
- "$1E North layer-swap auto stairs",
- "$1F North layer-swap auto stairs",
- "$20 Pit",
- "$21 Nothing (unused?)",
- "$22 Manual stairs",
- "$23 Pot switch",
- "$24 Pressure switch",
- "$25 Nothing (unused but referenced by somaria blocks)",
- "$26 Collision (near stairs?)",
- "$27 Brazier/Fence/Statue/Block/General hookable things",
- "$28 North ledge",
- "$29 South ledge",
- "$2A East ledge",
- "$2B West ledge",
- "$2C ◤ ledge",
- "$2D ◣ ledge",
- "$2E ◥ ledge",
- "$2F ◢ ledge",
- "$30 Straight inter-room stairs south/up 0",
- "$31 Straight inter-room stairs south/up 1",
- "$32 Straight inter-room stairs south/up 2",
- "$33 Straight inter-room stairs south/up 3",
- "$34 Straight inter-room stairs north/down 0",
- "$35 Straight inter-room stairs north/down 1",
- "$36 Straight inter-room stairs north/down 2",
- "$37 Straight inter-room stairs north/down 3",
- "$38 Straight inter-room stairs north/down edge",
- "$39 Straight inter-room stairs south/up edge",
- "$3A Star tile (inactive on load)",
- "$3B Star tile (active on load)",
- "$3C Nothing (unused?)",
- "$3D South single-layer auto stairs",
- "$3E South layer-swap auto stairs",
- "$3F South layer-swap auto stairs",
- "$40 Thick grass",
- "$41 Nothing (unused?)",
- "$42 Gravestone / Tower of hera ledge shadows??",
- "$43 Skull Woods entrance/Hera columns???",
- "$44 Spike",
- "$45 Nothing (unused?)",
- "$46 Desert Tablet",
- "$47 Nothing (unused?)",
- "$48 Diggable ground",
- "$49 Nothing (unused?)",
- "$4A Diggable ground",
- "$4B Warp tile",
- "$4C Nothing (unused?) | Something unknown in overworld",
- "$4D Nothing (unused?) | Something unknown in overworld",
- "$4E Square corners in EP overworld",
- "$4F Square corners in EP overworld",
- "$50 Green bush",
- "$51 Dark bush",
- "$52 Gray rock",
- "$53 Black rock",
- "$54 Hint tile/Sign",
- "$55 Big gray rock",
- "$56 Big black rock",
- "$57 Bonk rocks",
- "$58 Chest 0",
- "$59 Chest 1",
- "$5A Chest 2",
- "$5B Chest 3",
- "$5C Chest 4",
- "$5D Chest 5",
- "$5E Spiral stairs",
- "$5F Spiral stairs",
- "$60 Rupee tile",
- "$61 Nothing (unused?)",
- "$62 Bombable floor",
- "$63 Minigame chest",
- "$64 Nothing (unused?)",
- "$65 Nothing (unused?)",
- "$66 Crystal peg down",
- "$67 Crystal peg up",
- "$68 Upwards conveyor",
- "$69 Downwards conveyor",
- "$6A Leftwards conveyor",
- "$6B Rightwards conveyor",
- "$6C North vines",
- "$6D South vines",
- "$6E West vines",
- "$6F East vines",
- "$70 Pot/Hammer peg/Push block 00",
- "$71 Pot/Hammer peg/Push block 01",
- "$72 Pot/Hammer peg/Push block 02",
- "$73 Pot/Hammer peg/Push block 03",
- "$74 Pot/Hammer peg/Push block 04",
- "$75 Pot/Hammer peg/Push block 05",
- "$76 Pot/Hammer peg/Push block 06",
- "$77 Pot/Hammer peg/Push block 07",
- "$78 Pot/Hammer peg/Push block 08",
- "$79 Pot/Hammer peg/Push block 09",
- "$7A Pot/Hammer peg/Push block 0A",
- "$7B Pot/Hammer peg/Push block 0B",
- "$7C Pot/Hammer peg/Push block 0C",
- "$7D Pot/Hammer peg/Push block 0D",
- "$7E Pot/Hammer peg/Push block 0E",
- "$7F Pot/Hammer peg/Push block 0F",
- "$80 North/South door",
- "$81 East/West door",
- "$82 North/South shutter door",
- "$83 East/West shutter door",
- "$84 North/South layer 2 door",
- "$85 East/West layer 2 door",
- "$86 North/South layer 2 shutter door",
- "$87 East/West layer 2 shutter door",
- "$88 Some type of door (?)",
- "$89 East/West transport door",
- "$8A Some type of door (?)",
- "$8B Some type of door (?)",
- "$8C Some type of door (?)",
- "$8D Some type of door (?)",
- "$8E Entrance door",
- "$8F Entrance door",
- "$90 Layer toggle shutter door (?)",
- "$91 Layer toggle shutter door (?)",
- "$92 Layer toggle shutter door (?)",
- "$93 Layer toggle shutter door (?)",
- "$94 Layer toggle shutter door (?)",
- "$95 Layer toggle shutter door (?)",
- "$96 Layer toggle shutter door (?)",
- "$97 Layer toggle shutter door (?)",
- "$98 Layer+Dungeon toggle shutter door (?)",
- "$99 Layer+Dungeon toggle shutter door (?)",
- "$9A Layer+Dungeon toggle shutter door (?)",
- "$9B Layer+Dungeon toggle shutter door (?)",
- "$9C Layer+Dungeon toggle shutter door (?)",
- "$9D Layer+Dungeon toggle shutter door (?)",
- "$9E Layer+Dungeon toggle shutter door (?)",
- "$9F Layer+Dungeon toggle shutter door (?)",
- "$A0 North/South Dungeon swap door",
- "$A1 Dungeon toggle door (?)",
- "$A2 Dungeon toggle door (?)",
- "$A3 Dungeon toggle door (?)",
- "$A4 Dungeon toggle door (?)",
- "$A5 Dungeon toggle door (?)",
- "$A6 Nothing (unused?)",
- "$A7 Nothing (unused?)",
- "$A8 Layer+Dungeon toggle shutter door (?)",
- "$A9 Layer+Dungeon toggle shutter door (?)",
- "$AA Layer+Dungeon toggle shutter door (?)",
- "$AB Layer+Dungeon toggle shutter door (?)",
- "$AC Layer+Dungeon toggle shutter door (?)",
- "$AD Layer+Dungeon toggle shutter door (?)",
- "$AE Layer+Dungeon toggle shutter door (?)",
- "$AF Layer+Dungeon toggle shutter door (?)",
- "$B0 Somaria ─",
- "$B1 Somaria │",
- "$B2 Somaria ┌",
- "$B3 Somaria └",
- "$B4 Somaria ┐",
- "$B5 Somaria ┘",
- "$B6 Somaria ⍰ 1 way",
- "$B7 Somaria ┬",
- "$B8 Somaria ┴",
- "$B9 Somaria ├",
- "$BA Somaria ┤",
- "$BB Somaria ┼",
- "$BC Somaria ⍰ 2 way",
- "$BD Somaria ┼ crossover",
- "$BE Pipe entrance",
- "$BF Nothing (unused?)",
- "$C0 Torch 00",
- "$C1 Torch 01",
- "$C2 Torch 02",
- "$C3 Torch 03",
- "$C4 Torch 04",
- "$C5 Torch 05",
- "$C6 Torch 06",
- "$C7 Torch 07",
- "$C8 Torch 08",
- "$C9 Torch 09",
- "$CA Torch 0A",
- "$CB Torch 0B",
- "$CC Torch 0C",
- "$CD Torch 0D",
- "$CE Torch 0E",
- "$CF Torch 0F",
- "$D0 Nothing (unused?)",
- "$D1 Nothing (unused?)",
- "$D2 Nothing (unused?)",
- "$D3 Nothing (unused?)",
- "$D4 Nothing (unused?)",
- "$D5 Nothing (unused?)",
- "$D6 Nothing (unused?)",
- "$D7 Nothing (unused?)",
- "$D8 Nothing (unused?)",
- "$D9 Nothing (unused?)",
- "$DA Nothing (unused?)",
- "$DB Nothing (unused?)",
- "$DC Nothing (unused?)",
- "$DD Nothing (unused?)",
- "$DE Nothing (unused?)",
- "$DF Nothing (unused?)",
- "$E0 Nothing (unused?)",
- "$E1 Nothing (unused?)",
- "$E2 Nothing (unused?)",
- "$E3 Nothing (unused?)",
- "$E4 Nothing (unused?)",
- "$E5 Nothing (unused?)",
- "$E6 Nothing (unused?)",
- "$E7 Nothing (unused?)",
- "$E8 Nothing (unused?)",
- "$E9 Nothing (unused?)",
- "$EA Nothing (unused?)",
- "$EB Nothing (unused?)",
- "$EC Nothing (unused?)",
- "$ED Nothing (unused?)",
- "$EE Nothing (unused?)",
- "$EF Nothing (unused?)",
- "$F0 Door 0 bottom",
- "$F1 Door 1 bottom",
- "$F2 Door 2 bottom",
- "$F3 Door 3 bottom",
- "$F4 Door X bottom? (unused?)",
- "$F5 Door X bottom? (unused?)",
- "$F6 Door X bottom? (unused?)",
- "$F7 Door X bottom? (unused?)",
- "$F8 Door 0 top",
- "$F9 Door 1 top",
- "$FA Door 2 top",
- "$FB Door 3 top",
- "$FC Door X top? (unused?)",
- "$FD Door X top? (unused?)",
- "$FE Door X top? (unused?)",
- "$FF Door X top? (unused?)"};
-
-static const std::string kSpriteDefaultNames[]{
- "00 Raven",
- "01 Vulture",
- "02 Flying Stalfos Head",
- "03 No Pointer (Empty",
- "04 Pull Switch (good",
- "05 Pull Switch (unused",
- "06 Pull Switch (bad",
- "07 Pull Switch (unused",
- "08 Octorock (one way",
- "09 Moldorm (Boss",
- "0A Octorock (four way",
- "0B Chicken",
- "0C Octorock (?",
- "0D Buzzblock",
- "0E Snapdragon",
- "0F Octoballoon",
- "10 Octoballon Hatchlings",
- "11 Hinox",
- "12 Moblin",
- "13 Mini Helmasaure",
- "14 Gargoyle's Domain Gate",
- "15 Antifairy",
- "16 Sahasrahla / Aginah",
- "17 Bush Hoarder",
- "18 Mini Moldorm",
- "19 Poe",
- "1A Dwarves",
- "1B Arrow in wall",
- "1C Statue",
- "1D Weathervane",
- "1E Crystal Switch",
- "1F Bug-Catching Kid",
- "20 Sluggula",
- "21 Push Switch",
- "22 Ropa",
- "23 Red Bari",
- "24 Blue Bari",
- "25 Talking Tree",
- "26 Hardhat Beetle",
- "27 Deadrock",
- "28 Storytellers",
- "29 Blind Hideout attendant",
- "2A Sweeping Lady",
- "2B Storytellers",
- "2C Lumberjacks",
- "2D Telepathic Stones",
- "2E Multipurpose Sprite",
- "2F Race Npc",
- "30 Person?",
- "31 Fortune Teller",
- "32 Angry Brothers",
- "33 Pull for items",
- "34 Scared Girl",
- "35 Innkeeper",
- "36 Witch",
- "37 Waterfall",
- "38 Arrow Target",
- "39 Average Middle",
- "3A Half Magic Bat",
- "3B Dash Item",
- "3C Village Kid",
- "3D Signs? Chicken lady also showed up / Scared ladies outside houses.",
- "3E Rock Hoarder",
- "3F Tutorial Soldier",
- "40 Lightning Lock",
- "41 Blue Sword Soldier / Used by guards to detect player",
- "42 Green Sword Soldier",
- "43 Red Spear Soldier",
- "44 Assault Sword Soldier",
- "45 Green Spear Soldier",
- "46 Blue Archer",
- "47 Green Archer",
- "48 Red Javelin Soldier",
- "49 Red Javelin Soldier 2",
- "4A Red Bomb Soldiers",
- "4B Green Soldier Recruits",
- "4C Geldman",
- "4D Rabbit",
- "4E Popo",
- "4F Popo 2",
- "50 Cannon Balls",
- "51 Armos",
- "52 Giant Zora",
- "53 Armos Knights (Boss",
- "54 Lanmolas (Boss",
- "55 Fireball Zora",
- "56 Walking Zora",
- "57 Desert Palace Barriers",
- "58 Crab",
- "59 Bird",
- "5A Squirrel",
- "5B Spark (Left to Right",
- "5C Spark (Right to Left",
- "5D Roller (vertical moving",
- "5E Roller (vertical moving",
- "5F Roller",
- "60 Roller (horizontal moving",
- "61 Beamos",
- "62 Master Sword",
- "63 Devalant (Non",
- "64 Devalant (Shooter",
- "65 Shooting Gallery Proprietor",
- "66 Moving Cannon Ball Shooters (Right",
- "67 Moving Cannon Ball Shooters (Left",
- "68 Moving Cannon Ball Shooters (Down",
- "69 Moving Cannon Ball Shooters (Up",
- "6A Ball N' Chain Trooper",
- "6B Cannon Soldier",
- "6C Mirror Portal",
- "6D Rat",
- "6E Rope",
- "6F Keese",
- "70 Helmasaur King Fireball",
- "71 Leever",
- "72 Activator for the ponds (where you throw in items",
- "73 Uncle / Priest",
- "74 Running Man",
- "75 Bottle Salesman",
- "76 Princess Zelda",
- "77 Antifairy (Alternate",
- "78 Village Elder",
- "79 Bee",
- "7A Agahnim",
- "7B Agahnim Energy Ball",
- "7C Hyu",
- "7D Big Spike Trap",
- "7E Guruguru Bar (Clockwise",
- "7F Guruguru Bar (Counter Clockwise",
- "80 Winder",
- "81 Water Tektite",
- "82 Antifairy Circle",
- "83 Green Eyegore",
- "84 Red Eyegore",
- "85 Yellow Stalfos",
- "86 Kodongos",
- "87 Flames",
- "88 Mothula (Boss",
- "89 Mothula's Beam",
- "8A Spike Trap",
- "8B Gibdo",
- "8C Arrghus (Boss",
- "8D Arrghus spawn",
- "8E Terrorpin",
- "8F Slime",
- "90 Wallmaster",
- "91 Stalfos Knight",
- "92 Helmasaur King",
- "93 Bumper",
- "94 Swimmers",
- "95 Eye Laser (Right",
- "96 Eye Laser (Left",
- "97 Eye Laser (Down",
- "98 Eye Laser (Up",
- "99 Pengator",
- "9A Kyameron",
- "9B Wizzrobe",
- "9C Tadpoles",
- "9D Tadpoles",
- "9E Ostrich (Haunted Grove",
- "9F Flute",
- "A0 Birds (Haunted Grove",
- "A1 Freezor",
- "A2 Kholdstare (Boss",
- "A3 Kholdstare's Shell",
- "A4 Falling Ice",
- "A5 Zazak Fireball",
- "A6 Red Zazak",
- "A7 Stalfos",
- "A8 Bomber Flying Creatures from Darkworld",
- "A9 Bomber Flying Creatures from Darkworld",
- "AA Pikit",
- "AB Maiden",
- "AC Apple",
- "AD Lost Old Man",
- "AE Down Pipe",
- "AF Up Pipe",
- "B0 Right Pip",
- "B1 Left Pipe",
- "B2 Good bee again?",
- "B3 Hylian Inscription",
- "B4 Thief?s chest (not the one that follows you",
- "B5 Bomb Salesman",
- "B6 Kiki",
- "B7 Maiden following you in Blind Dungeon",
- "B8 Monologue Testing Sprite",
- "B9 Feuding Friends on Death Mountain",
- "BA Whirlpool",
- "BB Salesman / chestgame guy / 300 rupee giver guy / Chest game thief",
- "BC Drunk in the inn",
- "BD Vitreous (Large Eyeball",
- "BE Vitreous (Small Eyeball",
- "BF Vitreous' Lightning",
- "C0 Monster in Lake of Ill Omen / Quake Medallion",
- "C1 Agahnim teleporting Zelda to dark world",
- "C2 Boulders",
- "C3 Gibo",
- "C4 Thief",
- "C5 Medusa",
- "C6 Four Way Fireball Spitters (spit when you use your sword",
- "C7 Hokku",
- "C8 Big Fairy who heals you",
- "C9 Tektite",
- "CA Chain Chomp",
- "CB Trinexx",
- "CC Another part of trinexx",
- "CD Yet another part of trinexx",
- "CE Blind The Thief (Boss)",
- "CF Swamola",
- "D0 Lynel",
- "D1 Bunny Beam",
- "D2 Flopping fish",
- "D3 Stal",
- "D4 Landmine",
- "D5 Digging Game Proprietor",
- "D6 Ganon",
- "D7 Copy of Ganon",
- "D8 Heart",
- "D9 Green Rupee",
- "DA Blue Rupee",
- "DB Red Rupee",
- "DC Bomb Refill (1)",
- "DD Bomb Refill (4)",
- "DE Bomb Refill (8)",
- "DF Small Magic Refill",
- "E0 Full Magic Refill",
- "E1 Arrow Refill (5)",
- "E2 Arrow Refill (10)",
- "E3 Fairy",
- "E4 Key",
- "E5 Big Key",
- "E6 Shield",
- "E7 Mushroom",
- "E8 Fake Master Sword",
- "E9 Magic Shop dude / His items",
- "EA Heart Container",
- "EB Heart Piece",
- "EC Bushes",
- "ED Cane Of Somaria Platform",
- "EE Mantle",
- "EF Cane of Somaria Platform (Unused)",
- "F0 Cane of Somaria Platform (Unused)",
- "F1 Cane of Somaria Platform (Unused)",
- "F2 Medallion Tablet",
- "F3",
- "F4 Falling Rocks",
- "F5",
- "F6",
- "F7",
- "F8",
- "F9",
- "FA",
- "FB",
- "FC",
- "FD",
- "FE",
- "FF",
-};
-
-static const std::string overlordnames[] = {
- "Overlord_SpritePositionTarget",
- "Overlord_AllDirectionMetalBallFactory",
- "Overlord_CascadeMetalBallFactory",
- "Overlord_StalfosFactory",
- "Overlord_StalfosTrap",
- "Overlord_SnakeTrap",
- "Overlord_MovingFloor",
- "Overlord_ZolFactory",
- "Overlord_WallMasterFactory",
- "Overlord_CrumbleTilePath 1",
- "Overlord_CrumbleTilePath 2",
- "Overlord_CrumbleTilePath 3",
- "Overlord_CrumbleTilePath 4",
- "Overlord_CrumbleTilePath 5",
- "Overlord_CrumbleTilePath 6",
- "Overlord_PirogusuFactory 1",
- "Overlord_PirogusuFactory 2",
- "Overlord_PirogusuFactory 3",
- "Overlord_PirogusuFactory 4",
- "Overlord_FlyingTileFactory",
- "Overlord_WizzrobeFactory",
- "Overlord_ZoroFactory",
- "Overlord_StalfosTrapTriggerWindow",
- "Overlord_RedStalfosTrap",
- "Overlord_ArmosCoordinator",
- "Overlord_BombTrap",
-};
-
-} // namespace core
-} // namespace app
-} // namespace yaze
#endif
\ No newline at end of file
diff --git a/src/app/core/controller.cc b/src/app/core/controller.cc
index 12f843bf..7da81fa3 100644
--- a/src/app/core/controller.cc
+++ b/src/app/core/controller.cc
@@ -2,30 +2,18 @@
#include
-#include "imgui/backends/imgui_impl_sdl2.h"
-#include "imgui/backends/imgui_impl_sdlrenderer2.h"
-#include "imgui/imgui.h"
-#include "imgui/imgui_internal.h"
-
-#if defined(__APPLE__) && defined(__MACH__)
-#include
-#if TARGET_IPHONE_SIMULATOR == 1
-#include "imgui/backends/imgui_impl_metal.h"
-#elif TARGET_OS_IPHONE == 1
-#include "imgui/backends/imgui_impl_metal.h"
-#elif TARGET_OS_MAC == 1
-
-#endif
-#endif
-
+#include
#include
#include "absl/status/status.h"
#include "absl/strings/str_format.h"
#include "app/core/platform/font_loader.h"
-#include "app/editor/master_editor.h"
-#include "app/gui/icons.h"
+#include "app/editor/editor_manager.h"
#include "app/gui/style.h"
+#include "core/utils/file_util.h"
+#include "imgui/backends/imgui_impl_sdl2.h"
+#include "imgui/backends/imgui_impl_sdlrenderer2.h"
+#include "imgui/imgui.h"
namespace yaze {
namespace app {
@@ -38,7 +26,6 @@ constexpr ImGuiWindowFlags kMainEditorFlags =
ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_MenuBar |
ImGuiWindowFlags_NoBringToFrontOnFocus | ImGuiWindowFlags_NoTitleBar;
-using ::ImVec2;
using ImGui::Begin;
using ImGui::End;
using ImGui::GetIO;
@@ -61,205 +48,66 @@ void NewMasterFrame() {
}
}
-void InitializeKeymap() {
+} // namespace
+
+absl::Status Controller::OnEntry(std::string filename) {
+#if defined(__APPLE__) && defined(__MACH__)
+#if TARGET_IPHONE_SIMULATOR == 1 || TARGET_OS_IPHONE == 1
+ platform_ = Platform::kiOS;
+#elif TARGET_OS_MAC == 1
+ platform_ = Platform::kMacOS;
+#endif
+#elif defined(_WIN32)
+ platform_ = Platform::kWindows;
+#elif defined(__linux__)
+ platform_ = Platform::kLinux;
+#else
+ platform_ = Platform::kUnknown;
+#endif
+ RETURN_IF_ERROR(CreateWindow())
+ RETURN_IF_ERROR(CreateRenderer())
+ RETURN_IF_ERROR(CreateGuiContext())
+ RETURN_IF_ERROR(LoadAudioDevice())
+ editor_manager_.SetupScreen(filename);
+ active_ = true;
+ return absl::OkStatus();
+}
+
+void Controller::OnInput() {
+ int wheel = 0;
+ SDL_Event event;
ImGuiIO &io = ImGui::GetIO();
- io.KeyMap[ImGuiKey_LeftSuper] = SDL_GetScancodeFromKey(SDLK_LGUI);
- io.KeyMap[ImGuiKey_Backspace] = SDL_GetScancodeFromKey(SDLK_BACKSPACE);
- io.KeyMap[ImGuiKey_LeftShift] = SDL_GetScancodeFromKey(SDLK_LSHIFT);
- io.KeyMap[ImGuiKey_Enter] = SDL_GetScancodeFromKey(SDLK_RETURN);
- io.KeyMap[ImGuiKey_UpArrow] = SDL_GetScancodeFromKey(SDLK_UP);
- io.KeyMap[ImGuiKey_DownArrow] = SDL_GetScancodeFromKey(SDLK_DOWN);
- io.KeyMap[ImGuiKey_LeftArrow] = SDL_GetScancodeFromKey(SDLK_LEFT);
- io.KeyMap[ImGuiKey_RightArrow] = SDL_GetScancodeFromKey(SDLK_RIGHT);
- io.KeyMap[ImGuiKey_Delete] = SDL_GetScancodeFromKey(SDLK_DELETE);
- io.KeyMap[ImGuiKey_Escape] = SDL_GetScancodeFromKey(SDLK_ESCAPE);
- io.KeyMap[ImGuiKey_Tab] = SDL_GetScancodeFromKey(SDLK_TAB);
- io.KeyMap[ImGuiKey_LeftCtrl] = SDL_GetScancodeFromKey(SDLK_LCTRL);
- io.KeyMap[ImGuiKey_PageUp] = SDL_GetScancodeFromKey(SDLK_PAGEUP);
- io.KeyMap[ImGuiKey_PageDown] = SDL_GetScancodeFromKey(SDLK_PAGEDOWN);
- io.KeyMap[ImGuiKey_Home] = SDL_GetScancodeFromKey(SDLK_HOME);
- io.KeyMap[ImGuiKey_Space] = SDL_GetScancodeFromKey(SDLK_SPACE);
- io.KeyMap[ImGuiKey_1] = SDL_GetScancodeFromKey(SDLK_1);
- io.KeyMap[ImGuiKey_2] = SDL_GetScancodeFromKey(SDLK_2);
- io.KeyMap[ImGuiKey_3] = SDL_GetScancodeFromKey(SDLK_3);
- io.KeyMap[ImGuiKey_4] = SDL_GetScancodeFromKey(SDLK_4);
- io.KeyMap[ImGuiKey_5] = SDL_GetScancodeFromKey(SDLK_5);
- io.KeyMap[ImGuiKey_6] = SDL_GetScancodeFromKey(SDLK_6);
- io.KeyMap[ImGuiKey_7] = SDL_GetScancodeFromKey(SDLK_7);
- io.KeyMap[ImGuiKey_8] = SDL_GetScancodeFromKey(SDLK_8);
- io.KeyMap[ImGuiKey_9] = SDL_GetScancodeFromKey(SDLK_9);
- io.KeyMap[ImGuiKey_0] = SDL_GetScancodeFromKey(SDLK_0);
- io.KeyMap[ImGuiKey_A] = SDL_GetScancodeFromKey(SDLK_a);
- io.KeyMap[ImGuiKey_B] = SDL_GetScancodeFromKey(SDLK_b);
- io.KeyMap[ImGuiKey_C] = SDL_GetScancodeFromKey(SDLK_c);
- io.KeyMap[ImGuiKey_D] = SDL_GetScancodeFromKey(SDLK_d);
- io.KeyMap[ImGuiKey_E] = SDL_GetScancodeFromKey(SDLK_e);
- io.KeyMap[ImGuiKey_F] = SDL_GetScancodeFromKey(SDLK_f);
- io.KeyMap[ImGuiKey_G] = SDL_GetScancodeFromKey(SDLK_g);
- io.KeyMap[ImGuiKey_H] = SDL_GetScancodeFromKey(SDLK_h);
- io.KeyMap[ImGuiKey_I] = SDL_GetScancodeFromKey(SDLK_i);
- io.KeyMap[ImGuiKey_J] = SDL_GetScancodeFromKey(SDLK_j);
- io.KeyMap[ImGuiKey_K] = SDL_GetScancodeFromKey(SDLK_k);
- io.KeyMap[ImGuiKey_L] = SDL_GetScancodeFromKey(SDLK_l);
- io.KeyMap[ImGuiKey_M] = SDL_GetScancodeFromKey(SDLK_m);
- io.KeyMap[ImGuiKey_N] = SDL_GetScancodeFromKey(SDLK_n);
- io.KeyMap[ImGuiKey_O] = SDL_GetScancodeFromKey(SDLK_o);
- io.KeyMap[ImGuiKey_P] = SDL_GetScancodeFromKey(SDLK_p);
- io.KeyMap[ImGuiKey_Q] = SDL_GetScancodeFromKey(SDLK_q);
- io.KeyMap[ImGuiKey_R] = SDL_GetScancodeFromKey(SDLK_r);
- io.KeyMap[ImGuiKey_S] = SDL_GetScancodeFromKey(SDLK_s);
- io.KeyMap[ImGuiKey_T] = SDL_GetScancodeFromKey(SDLK_t);
- io.KeyMap[ImGuiKey_U] = SDL_GetScancodeFromKey(SDLK_u);
- io.KeyMap[ImGuiKey_V] = SDL_GetScancodeFromKey(SDLK_v);
- io.KeyMap[ImGuiKey_W] = SDL_GetScancodeFromKey(SDLK_w);
- io.KeyMap[ImGuiKey_X] = SDL_GetScancodeFromKey(SDLK_x);
- io.KeyMap[ImGuiKey_Y] = SDL_GetScancodeFromKey(SDLK_y);
- io.KeyMap[ImGuiKey_Z] = SDL_GetScancodeFromKey(SDLK_z);
- io.KeyMap[ImGuiKey_F1] = SDL_GetScancodeFromKey(SDLK_F1);
- io.KeyMap[ImGuiKey_F2] = SDL_GetScancodeFromKey(SDLK_F2);
- io.KeyMap[ImGuiKey_F3] = SDL_GetScancodeFromKey(SDLK_F3);
- io.KeyMap[ImGuiKey_F4] = SDL_GetScancodeFromKey(SDLK_F4);
- io.KeyMap[ImGuiKey_F5] = SDL_GetScancodeFromKey(SDLK_F5);
- io.KeyMap[ImGuiKey_F6] = SDL_GetScancodeFromKey(SDLK_F6);
- io.KeyMap[ImGuiKey_F7] = SDL_GetScancodeFromKey(SDLK_F7);
- io.KeyMap[ImGuiKey_F8] = SDL_GetScancodeFromKey(SDLK_F8);
- io.KeyMap[ImGuiKey_F9] = SDL_GetScancodeFromKey(SDLK_F9);
- io.KeyMap[ImGuiKey_F10] = SDL_GetScancodeFromKey(SDLK_F10);
- io.KeyMap[ImGuiKey_F11] = SDL_GetScancodeFromKey(SDLK_F11);
- io.KeyMap[ImGuiKey_F12] = SDL_GetScancodeFromKey(SDLK_F12);
-}
-void ImGui_ImplSDL2_SetClipboardText(void *user_data, const char *text) {
- SDL_SetClipboardText(text);
-}
-
-const char *ImGui_ImplSDL2_GetClipboardText(void *user_data) {
- return SDL_GetClipboardText();
-}
-
-void InitializeClipboard() {
- ImGuiIO &io = ImGui::GetIO();
- io.SetClipboardTextFn = ImGui_ImplSDL2_SetClipboardText;
- io.GetClipboardTextFn = ImGui_ImplSDL2_GetClipboardText;
- io.ClipboardUserData = nullptr;
-}
-
-void HandleKeyDown(SDL_Event &event, editor::MasterEditor &editor) {
- ImGuiIO &io = ImGui::GetIO();
- io.KeysDown[event.key.keysym.scancode] = (event.type == SDL_KEYDOWN);
- io.KeyShift = ((SDL_GetModState() & KMOD_SHIFT) != 0);
- io.KeyCtrl = ((SDL_GetModState() & KMOD_CTRL) != 0);
- io.KeyAlt = ((SDL_GetModState() & KMOD_ALT) != 0);
- io.KeySuper = ((SDL_GetModState() & KMOD_GUI) != 0);
-
- switch (event.key.keysym.sym) {
- case SDLK_BACKSPACE:
- case SDLK_LSHIFT:
- case SDLK_LCTRL:
- case SDLK_TAB:
- io.KeysDown[event.key.keysym.scancode] = (event.type == SDL_KEYDOWN);
- break;
- case SDLK_z:
- editor.emulator().snes().SetButtonState(1, 0, true);
- break;
- case SDLK_a:
- editor.emulator().snes().SetButtonState(1, 1, true);
- break;
- case SDLK_RSHIFT:
- editor.emulator().snes().SetButtonState(1, 2, true);
- break;
- case SDLK_RETURN:
- editor.emulator().snes().SetButtonState(1, 3, true);
- break;
- case SDLK_UP:
- editor.emulator().snes().SetButtonState(1, 4, true);
- break;
- case SDLK_DOWN:
- editor.emulator().snes().SetButtonState(1, 5, true);
- break;
- case SDLK_LEFT:
- editor.emulator().snes().SetButtonState(1, 6, true);
- break;
- case SDLK_RIGHT:
- editor.emulator().snes().SetButtonState(1, 7, true);
- break;
- case SDLK_x:
- editor.emulator().snes().SetButtonState(1, 8, true);
- break;
- case SDLK_s:
- editor.emulator().snes().SetButtonState(1, 9, true);
- break;
- case SDLK_d:
- editor.emulator().snes().SetButtonState(1, 10, true);
- break;
- case SDLK_c:
- editor.emulator().snes().SetButtonState(1, 11, true);
- break;
- default:
- break;
+ while (SDL_PollEvent(&event)) {
+ ImGui_ImplSDL2_ProcessEvent(&event);
+ switch (event.type) {
+ case SDL_KEYDOWN:
+ case SDL_KEYUP: {
+ ImGuiIO &io = ImGui::GetIO();
+ io.KeyShift = ((SDL_GetModState() & KMOD_SHIFT) != 0);
+ io.KeyCtrl = ((SDL_GetModState() & KMOD_CTRL) != 0);
+ io.KeyAlt = ((SDL_GetModState() & KMOD_ALT) != 0);
+ io.KeySuper = ((SDL_GetModState() & KMOD_GUI) != 0);
+ break;
+ }
+ case SDL_WINDOWEVENT:
+ switch (event.window.event) {
+ case SDL_WINDOWEVENT_CLOSE:
+ active_ = false;
+ break;
+ case SDL_WINDOWEVENT_SIZE_CHANGED:
+ io.DisplaySize.x = static_cast(event.window.data1);
+ io.DisplaySize.y = static_cast(event.window.data2);
+ break;
+ default:
+ break;
+ }
+ break;
+ default:
+ break;
+ }
}
-}
-void HandleKeyUp(SDL_Event &event, editor::MasterEditor &editor) {
- ImGuiIO &io = ImGui::GetIO();
- int key = event.key.keysym.scancode;
- IM_ASSERT(key >= 0 && key < IM_ARRAYSIZE(io.KeysDown));
- io.KeysDown[key] = (event.type == SDL_KEYDOWN);
- io.KeyShift = ((SDL_GetModState() & KMOD_SHIFT) != 0);
- io.KeyCtrl = ((SDL_GetModState() & KMOD_CTRL) != 0);
- io.KeyAlt = ((SDL_GetModState() & KMOD_ALT) != 0);
- io.KeySuper = ((SDL_GetModState() & KMOD_GUI) != 0);
-
- switch (event.key.keysym.sym) {
- case SDLK_z:
- editor.emulator().snes().SetButtonState(1, 0, false);
- break;
- case SDLK_a:
- editor.emulator().snes().SetButtonState(1, 1, false);
- break;
- case SDLK_RSHIFT:
- editor.emulator().snes().SetButtonState(1, 2, false);
- break;
- case SDLK_RETURN:
- editor.emulator().snes().SetButtonState(1, 3, false);
- break;
- case SDLK_UP:
- editor.emulator().snes().SetButtonState(1, 4, false);
- break;
- case SDLK_DOWN:
- editor.emulator().snes().SetButtonState(1, 5, false);
- break;
- case SDLK_LEFT:
- editor.emulator().snes().SetButtonState(1, 6, false);
- break;
- case SDLK_RIGHT:
- editor.emulator().snes().SetButtonState(1, 7, false);
- break;
- case SDLK_x:
- editor.emulator().snes().SetButtonState(1, 8, false);
- break;
- case SDLK_s:
- editor.emulator().snes().SetButtonState(1, 9, false);
- break;
- case SDLK_d:
- editor.emulator().snes().SetButtonState(1, 10, false);
- break;
- case SDLK_c:
- editor.emulator().snes().SetButtonState(1, 11, false);
- break;
- default:
- break;
- }
-}
-
-void ChangeWindowSizeEvent(SDL_Event &event) {
- ImGuiIO &io = ImGui::GetIO();
- io.DisplaySize.x = static_cast(event.window.data1);
- io.DisplaySize.y = static_cast(event.window.data2);
-}
-
-void HandleMouseMovement(int &wheel) {
- ImGuiIO &io = ImGui::GetIO();
int mouseX;
int mouseY;
const int buttons = SDL_GetMouseState(&mouseX, &mouseY);
@@ -272,169 +120,80 @@ void HandleMouseMovement(int &wheel) {
io.MouseWheel = static_cast(wheel);
}
-} // namespace
-
-absl::Status Controller::OnEntry(std::string filename) {
-#if defined(__APPLE__) && defined(__MACH__)
-#if TARGET_IPHONE_SIMULATOR == 1
- /* iOS in Xcode simulator */
- platform_ = Platform::kiOS;
-#elif TARGET_OS_IPHONE == 1
- /* iOS */
- platform_ = Platform::kiOS;
-#elif TARGET_OS_MAC == 1
- /* macOS */
- platform_ = Platform::kMacOS;
-#endif
-#elif defined(_WIN32)
- platform_ = Platform::kWindows;
-#elif defined(__linux__)
- platform_ = Platform::kLinux;
-#else
- platform_ = Platform::kUnknown;
-#endif
-
- RETURN_IF_ERROR(CreateSDL_Window())
- RETURN_IF_ERROR(CreateRenderer())
- RETURN_IF_ERROR(CreateGuiContext())
- if (flags()->kLoadAudioDevice) {
- RETURN_IF_ERROR(LoadAudioDevice())
- master_editor_.emulator().set_audio_buffer(audio_buffer_);
- master_editor_.emulator().set_audio_device_id(audio_device_);
+absl::Status Controller::OnLoad() {
+ if (editor_manager_.quit()) {
+ active_ = false;
}
- InitializeKeymap();
- master_editor_.SetupScreen(renderer_, filename);
- active_ = true;
+#if TARGET_OS_IPHONE != 1
+ if (platform_ != Platform::kiOS) {
+ NewMasterFrame();
+ }
+#endif
+ RETURN_IF_ERROR(editor_manager_.Update());
+#if TARGET_OS_IPHONE != 1
+ if (platform_ != Platform::kiOS) {
+ End();
+ }
+#endif
return absl::OkStatus();
}
-void Controller::OnInput() {
- int wheel = 0;
- SDL_Event event;
- ImGuiIO &io = ImGui::GetIO();
-
- while (SDL_PollEvent(&event)) {
- switch (event.type) {
- case SDL_KEYDOWN:
- HandleKeyDown(event, master_editor_);
- break;
- case SDL_KEYUP:
- HandleKeyUp(event, master_editor_);
- break;
- case SDL_TEXTINPUT:
- io.AddInputCharactersUTF8(event.text.text);
- break;
- case SDL_MOUSEWHEEL:
- wheel = event.wheel.y;
- break;
- case SDL_WINDOWEVENT:
- switch (event.window.event) {
- case SDL_WINDOWEVENT_CLOSE:
- CloseWindow();
- break;
- case SDL_WINDOWEVENT_SIZE_CHANGED:
- ChangeWindowSizeEvent(event);
- break;
- default:
- break;
- }
- break;
- default:
- break;
- }
- }
-
- HandleMouseMovement(wheel);
-}
-
-absl::Status Controller::OnLoad() {
- if (master_editor_.quit()) {
- active_ = false;
- }
- NewMasterFrame();
- RETURN_IF_ERROR(master_editor_.Update());
+absl::Status Controller::OnTestLoad() {
+ RETURN_IF_ERROR(test_editor_->Update());
return absl::OkStatus();
}
void Controller::DoRender() const {
ImGui::Render();
- SDL_RenderClear(renderer_.get());
- ImGui_ImplSDLRenderer2_RenderDrawData(ImGui::GetDrawData(), renderer_.get());
- SDL_RenderPresent(renderer_.get());
+ SDL_RenderClear(Renderer::GetInstance().renderer());
+ ImGui_ImplSDLRenderer2_RenderDrawData(ImGui::GetDrawData(),
+ Renderer::GetInstance().renderer());
+ SDL_RenderPresent(Renderer::GetInstance().renderer());
}
void Controller::OnExit() {
- ImGui::DestroyContext();
- if (flags()->kLoadAudioDevice) {
- SDL_PauseAudioDevice(audio_device_, 1);
- SDL_CloseAudioDevice(audio_device_);
- delete audio_buffer_;
- }
- switch (platform_) {
- case Platform::kMacOS:
- case Platform::kWindows:
- case Platform::kLinux:
- ImGui_ImplSDLRenderer2_Shutdown();
- ImGui_ImplSDL2_Shutdown();
- break;
- case Platform::kiOS:
- // Deferred
- break;
- default:
- break;
- }
+ SDL_PauseAudioDevice(audio_device_, 1);
+ SDL_CloseAudioDevice(audio_device_);
+ ImGui_ImplSDLRenderer2_Shutdown();
+ ImGui_ImplSDL2_Shutdown();
ImGui::DestroyContext();
SDL_Quit();
}
-absl::Status Controller::CreateSDL_Window() {
- auto sdl_flags = SDL_INIT_VIDEO | SDL_INIT_TIMER;
+absl::Status Controller::CreateWindow() {
+ auto sdl_flags = SDL_INIT_VIDEO | SDL_INIT_AUDIO | SDL_INIT_TIMER;
if (flags()->kUseNewImGuiInput) {
sdl_flags |= SDL_INIT_GAMECONTROLLER;
}
- if (flags()->kLoadAudioDevice) {
- sdl_flags |= SDL_INIT_AUDIO;
- }
-
if (SDL_Init(sdl_flags) != 0) {
return absl::InternalError(
absl::StrFormat("SDL_Init: %s\n", SDL_GetError()));
- } else {
- SDL_DisplayMode displayMode;
- SDL_GetCurrentDisplayMode(0, &displayMode);
- int screenWidth = displayMode.w * 0.8;
- int screenHeight = displayMode.h * 0.8;
-
- window_ = std::unique_ptr(
- SDL_CreateWindow("Yet Another Zelda3 Editor", // window title
- SDL_WINDOWPOS_UNDEFINED, // initial x position
- SDL_WINDOWPOS_UNDEFINED, // initial y position
- screenWidth, // width, in pixels
- screenHeight, // height, in pixels
- SDL_WINDOW_RESIZABLE),
- sdl_deleter());
- if (window_ == nullptr) {
- return absl::InternalError(
- absl::StrFormat("SDL_CreateWindow: %s\n", SDL_GetError()));
- }
}
+
+ SDL_DisplayMode display_mode;
+ SDL_GetCurrentDisplayMode(0, &display_mode);
+ int screen_width = display_mode.w * 0.8;
+ int screen_height = display_mode.h * 0.8;
+
+ window_ = std::unique_ptr(
+ SDL_CreateWindow("Yet Another Zelda3 Editor", // window title
+ SDL_WINDOWPOS_UNDEFINED, // initial x position
+ SDL_WINDOWPOS_UNDEFINED, // initial y position
+ screen_width, // width, in pixels
+ screen_height, // height, in pixels
+ SDL_WINDOW_RESIZABLE),
+ core::SDL_Deleter());
+ if (window_ == nullptr) {
+ return absl::InternalError(
+ absl::StrFormat("SDL_CreateWindow: %s\n", SDL_GetError()));
+ }
+
return absl::OkStatus();
}
absl::Status Controller::CreateRenderer() {
- renderer_ = std::unique_ptr(
- SDL_CreateRenderer(window_.get(), -1,
- SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC),
- sdl_deleter());
- if (renderer_ == nullptr) {
- return absl::InternalError(
- absl::StrFormat("SDL_CreateRenderer: %s\n", SDL_GetError()));
- } else {
- SDL_SetRenderDrawBlendMode(renderer_.get(), SDL_BLENDMODE_BLEND);
- SDL_SetRenderDrawColor(renderer_.get(), 0x00, 0x00, 0x00, 0x00);
- }
- return absl::OkStatus();
+ return Renderer::GetInstance().CreateRenderer(window_.get());
}
absl::Status Controller::CreateGuiContext() {
@@ -443,20 +202,16 @@ absl::Status Controller::CreateGuiContext() {
ImGuiIO &io = ImGui::GetIO();
io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard;
-
if (flags()->kUseNewImGuiInput) {
io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad;
}
- // Initialize ImGui for SDL
- ImGui_ImplSDL2_InitForSDLRenderer(window_.get(), renderer_.get());
- ImGui_ImplSDLRenderer2_Init(renderer_.get());
+ // Initialize ImGui based on the backend
+ ImGui_ImplSDL2_InitForSDLRenderer(window_.get(),
+ Renderer::GetInstance().renderer());
+ ImGui_ImplSDLRenderer2_Init(Renderer::GetInstance().renderer());
- if (flags()->kLoadSystemFonts) {
- LoadSystemFonts();
- } else {
- RETURN_IF_ERROR(LoadFontFamilies());
- }
+ RETURN_IF_ERROR(LoadFontFamilies());
// Set the default style
gui::ColorsYaze();
@@ -469,94 +224,8 @@ absl::Status Controller::CreateGuiContext() {
}
absl::Status Controller::LoadFontFamilies() const {
- ImGuiIO &io = ImGui::GetIO();
-
- const char *font_path = "assets/font/";
- static const char *KARLA_REGULAR = "Karla-Regular.ttf";
- static const char *ROBOTO_MEDIUM = "Roboto-Medium.ttf";
- static const char *COUSINE_REGULAR = "Cousine-Regular.ttf";
- static const char *DROID_SANS = "DroidSans.ttf";
- static const char *NOTO_SANS_JP = "NotoSansJP.ttf";
- static const char *IBM_PLEX_JP = "IBMPlexSansJP-Bold.ttf";
- static const float FONT_SIZE_DEFAULT = 14.0f;
- static const float FONT_SIZE_DROID_SANS = 16.0f;
- static const float ICON_FONT_SIZE = 18.0f;
-
- // Icon configuration
- static const ImWchar icons_ranges[] = {ICON_MIN_MD, 0xf900, 0};
- ImFontConfig icons_config;
- icons_config.MergeMode = true;
- icons_config.GlyphOffset.y = 5.0f;
- icons_config.GlyphMinAdvanceX = 13.0f;
- icons_config.PixelSnapH = true;
-
- // Japanese font configuration
- ImFontConfig japanese_font_config;
- japanese_font_config.MergeMode = true;
- icons_config.GlyphOffset.y = 5.0f;
- icons_config.GlyphMinAdvanceX = 13.0f;
- icons_config.PixelSnapH = true;
-
- // List of fonts to be loaded
- std::vector font_paths = {KARLA_REGULAR, ROBOTO_MEDIUM,
- COUSINE_REGULAR, IBM_PLEX_JP};
-
- // Load fonts with associated icon and Japanese merges
- for (const auto &font_path : font_paths) {
- float font_size =
- (font_path == DROID_SANS) ? FONT_SIZE_DROID_SANS : FONT_SIZE_DEFAULT;
-
- std::string actual_font_path;
-#ifdef __APPLE__
-#if TARGET_OS_IOS == 1
- const std::string kBundlePath = GetBundleResourcePath();
- actual_font_path = kBundlePath + font_path;
-#else
- actual_font_path = std::filesystem::absolute(font_path).string();
-#endif
-#else
- actual_font_path = font_path;
-#endif
-
- if (!io.Fonts->AddFontFromFileTTF(actual_font_path.data(), font_size)) {
- return absl::InternalError(
- absl::StrFormat("Failed to load font from %s", actual_font_path));
- }
-
- // Merge icon set
- std::string actual_icon_font_path = "";
- const char *icon_font_path = FONT_ICON_FILE_NAME_MD;
-#if defined(__APPLE__) && defined(__MACH__)
-#if TARGET_OS_IOS == 1
- const std::string kIconBundlePath = GetBundleResourcePath();
- actual_icon_font_path = kIconBundlePath + "MaterialIcons-Regular.ttf";
-#else
- actual_icon_font_path = std::filesystem::absolute(icon_font_path).string();
-#endif
-#else
-#endif
- io.Fonts->AddFontFromFileTTF(actual_icon_font_path.data(), ICON_FONT_SIZE,
- &icons_config, icons_ranges);
-
- // Merge Japanese font
- std::string actual_japanese_font_path = "";
- const char *japanese_font_path = NOTO_SANS_JP;
-#if defined(__APPLE__) && defined(__MACH__)
-#if TARGET_OS_IOS == 1
- const std::string kJapaneseBundlePath = GetBundleResourcePath();
- actual_japanese_font_path = kJapaneseBundlePath + japanese_font_path;
-#else
- actual_japanese_font_path =
- std::filesystem::absolute(japanese_font_path).string();
-#endif
-#else
-#endif
- io.Fonts->AddFontFromFileTTF(actual_japanese_font_path.data(), 18.0f,
- &japanese_font_config,
- io.Fonts->GetGlyphRangesJapanese());
- }
-
- return absl::OkStatus();
+ // LoadSystemFonts();
+ return LoadPackageFonts();
}
absl::Status Controller::LoadAudioDevice() {
@@ -572,9 +241,39 @@ absl::Status Controller::LoadAudioDevice() {
return absl::InternalError(
absl::StrFormat("Failed to open audio: %s\n", SDL_GetError()));
}
- audio_buffer_ = new int16_t[audio_frequency_ / 50 * 4];
- master_editor_.emulator().set_audio_buffer(audio_buffer_);
+ // audio_buffer_ = new int16_t[audio_frequency_ / 50 * 4];
+ audio_buffer_ = std::make_shared(audio_frequency_ / 50 * 4);
SDL_PauseAudioDevice(audio_device_, 0);
+ editor_manager_.emulator().set_audio_buffer(audio_buffer_.get());
+ editor_manager_.emulator().set_audio_device_id(audio_device_);
+ return absl::OkStatus();
+}
+
+absl::Status Controller::LoadConfigFiles() {
+ // Create and load a dotfile for the application
+ // This will store the user's preferences and settings
+ std::string config_directory = GetConfigDirectory(platform_);
+
+ // Create the directory if it doesn't exist
+ if (!std::filesystem::exists(config_directory)) {
+ if (!std::filesystem::create_directory(config_directory)) {
+ return absl::InternalError(absl::StrFormat(
+ "Failed to create config directory %s", config_directory));
+ }
+ }
+
+ // Check if the config file exists
+ std::string config_file = config_directory + "yaze.cfg";
+ if (!std::filesystem::exists(config_file)) {
+ // Create the file if it doesn't exist
+ std::ofstream file(config_file);
+ if (!file.is_open()) {
+ return absl::InternalError(
+ absl::StrFormat("Failed to create config file %s", config_file));
+ }
+ file.close();
+ }
+
return absl::OkStatus();
}
diff --git a/src/app/core/controller.h b/src/app/core/controller.h
index 6df680d0..726189d2 100644
--- a/src/app/core/controller.h
+++ b/src/app/core/controller.h
@@ -2,20 +2,18 @@
#define YAZE_APP_CORE_CONTROLLER_H
#include
-#include "imgui/backends/imgui_impl_sdl2.h"
-#include "imgui/backends/imgui_impl_sdlrenderer2.h"
-#include "imgui/imconfig.h"
-#include "imgui/imgui.h"
-#include "imgui/imgui_internal.h"
#include
#include "absl/status/status.h"
-#include "app/core/common.h"
-#include "app/editor/master_editor.h"
-#include "app/editor/utils/editor.h"
-#include "app/gui/icons.h"
-#include "app/gui/style.h"
+#include "app/core/platform/renderer.h"
+#include "app/core/utils/file_util.h"
+#include "app/editor/editor_manager.h"
+#include "app/editor/editor.h"
+#include "imgui/backends/imgui_impl_sdl2.h"
+#include "imgui/backends/imgui_impl_sdlrenderer2.h"
+#include "imgui/imconfig.h"
+#include "imgui/imgui.h"
int main(int argc, char **argv);
@@ -23,8 +21,6 @@ namespace yaze {
namespace app {
namespace core {
-enum class Platform { kUnknown, kMacOS, kiOS, kWindows, kLinux };
-
/**
* @brief Main controller for the application.
*
@@ -37,45 +33,40 @@ class Controller : public ExperimentFlags {
absl::Status OnEntry(std::string filename = "");
void OnInput();
absl::Status OnLoad();
+ absl::Status OnTestLoad();
void DoRender() const;
void OnExit();
- absl::Status CreateSDL_Window();
+ absl::Status CreateWindow();
absl::Status CreateRenderer();
absl::Status CreateGuiContext();
absl::Status LoadFontFamilies() const;
absl::Status LoadAudioDevice();
+ absl::Status LoadConfigFiles();
- auto master_editor() -> editor::MasterEditor & { return master_editor_; }
+ void SetupScreen(std::string filename = "") {
+ editor_manager_.SetupScreen(filename);
+ }
+ auto editor_manager() -> editor::EditorManager & { return editor_manager_; }
+ auto renderer() -> SDL_Renderer * {
+ return Renderer::GetInstance().renderer();
+ }
+ auto window() -> SDL_Window * { return window_.get(); }
+ void init_test_editor(editor::Editor *editor) { test_editor_ = editor; }
+ void set_active(bool active) { active_ = active; }
private:
- struct sdl_deleter {
- void operator()(SDL_Window *p) const {
- if (p) {
- SDL_DestroyWindow(p);
- }
- }
- void operator()(SDL_Renderer *p) const {
- if (p) {
- SDL_DestroyRenderer(p);
- }
- }
- void operator()(SDL_Texture *p) const { SDL_DestroyTexture(p); }
- };
-
- void CloseWindow() { active_ = false; }
-
friend int ::main(int argc, char **argv);
bool active_;
Platform platform_;
- editor::MasterEditor master_editor_;
+ editor::Editor *test_editor_;
+ editor::EditorManager editor_manager_;
int audio_frequency_ = 48000;
- int16_t *audio_buffer_;
SDL_AudioDeviceID audio_device_;
+ std::shared_ptr audio_buffer_;
std::shared_ptr window_;
- std::shared_ptr renderer_;
};
} // namespace core
diff --git a/src/app/core/core.cmake b/src/app/core/core.cmake
new file mode 100644
index 00000000..ebb8bcc2
--- /dev/null
+++ b/src/app/core/core.cmake
@@ -0,0 +1,33 @@
+set(
+ YAZE_APP_CORE_SRC
+ app/core/common.cc
+ app/core/controller.cc
+ app/emu/emulator.cc
+ app/core/project.cc
+ app/core/utils/file_util.cc
+)
+
+if (WIN32 OR MINGW OR UNIX AND NOT APPLE)
+ list(APPEND YAZE_APP_CORE_SRC
+ app/core/platform/font_loader.cc
+ app/core/platform/clipboard.cc
+ app/core/platform/file_dialog.cc
+ )
+endif()
+
+if(APPLE)
+ list(APPEND YAZE_APP_CORE_SRC
+ app/core/platform/file_dialog.mm
+ app/core/platform/app_delegate.mm
+ app/core/platform/font_loader.cc
+ app/core/platform/font_loader.mm
+ app/core/platform/clipboard.mm
+ app/core/platform/file_path.mm
+ )
+
+ find_library(COCOA_LIBRARY Cocoa)
+ if(NOT COCOA_LIBRARY)
+ message(FATAL_ERROR "Cocoa not found")
+ endif()
+ set(CMAKE_EXE_LINKER_FLAGS "-framework ServiceManagement -framework Foundation -framework Cocoa")
+endif()
diff --git a/src/app/core/labeling.h b/src/app/core/labeling.h
deleted file mode 100644
index 95787bdf..00000000
--- a/src/app/core/labeling.h
+++ /dev/null
@@ -1,53 +0,0 @@
-#ifndef YAZE_APP_CORE_LABELING_H_
-#define YAZE_APP_CORE_LABELING_H_
-
-#include
-#include
-#include
-#include
-#include
-#include
-
-#include "absl/status/status.h"
-#include "absl/status/statusor.h"
-#include "app/core/common.h"
-#include "app/core/constants.h"
-
-namespace yaze {
-namespace app {
-namespace core {
-
-// Default types
-static constexpr absl::string_view kDefaultTypes[] = {
- "Dungeon Names", "Dungeon Room Names", "Overworld Map Names"};
-
-struct ResourceLabelManager {
- bool LoadLabels(const std::string& filename);
- bool SaveLabels();
- void DisplayLabels(bool* p_open);
- void EditLabel(const std::string& type, const std::string& key,
- const std::string& newValue);
- void SelectableLabelWithNameEdit(bool selected, const std::string& type,
- const std::string& key,
- const std::string& defaultValue);
- std::string CreateOrGetLabel(const std::string& type, const std::string& key,
- const std::string& defaultValue);
- std::string CreateOrGetLabel(const std::string& type, const std::string& key,
- const absl::string_view& defaultValue);
-
- bool labels_loaded_ = false;
- std::string filename_;
- struct ResourceType {
- std::string key_name;
- std::string display_description;
- };
-
- std::unordered_map>
- labels_;
-};
-
-} // namespace core
-} // namespace app
-} // namespace yaze
-
-#endif // YAZE_APP_CORE_LABELING_H_
\ No newline at end of file
diff --git a/src/app/core/platform/app_delegate.h b/src/app/core/platform/app_delegate.h
index aab3df42..159aa3d7 100644
--- a/src/app/core/platform/app_delegate.h
+++ b/src/app/core/platform/app_delegate.h
@@ -1,13 +1,57 @@
#ifndef YAZE_APP_CORE_PLATFORM_APP_DELEGATE_H
#define YAZE_APP_CORE_PLATFORM_APP_DELEGATE_H
-#ifdef TARGET_OS_MAC
+#if defined(__APPLE__) && defined(__MACH__)
+/* Apple OSX and iOS (Darwin). */
+#import
+#include
+
+#if TARGET_IPHONE_SIMULATOR == 1 || TARGET_OS_IPHONE == 1
+/* iOS in Xcode simulator */
+#import
+#import
+
+@interface AppDelegate : UIResponder
+@property(strong, nonatomic) UIWindow *window;
+
+@property UIDocumentPickerViewController *documentPicker;
+@property(nonatomic, copy) void (^completionHandler)(NSString *selectedFile);
+- (void)PresentDocumentPickerWithCompletionHandler:
+ (void (^)(NSString *selectedFile))completionHandler;
+
+// TODO: Setup a tab bar controller for multiple yaze instances
+@property(nonatomic) UITabBarController *tabBarController;
+
+// TODO: Setup a font picker for the text editor and display settings
+@property(nonatomic) UIFontPickerViewController *fontPicker;
+
+// TODO: Setup the pencil kit for drawing
+@property PKToolPicker *toolPicker;
+@property PKCanvasView *canvasView;
+
+// TODO: Setup the file manager for file operations
+@property NSFileManager *fileManager;
+
+@end
+
+#elif TARGET_OS_MAC == 1
#ifdef __cplusplus
extern "C" {
#endif
-void InitializeCocoa();
+/**
+ * @brief Initialize the Cocoa application.
+ */
+void yaze_initialize_cocoa();
+
+/**
+ * @brief Run the Cocoa application delegate.
+ */
+void yaze_run_cocoa_app_delegate(const char *filename);
#ifdef __cplusplus
} // extern "C"
@@ -15,4 +59,6 @@ void InitializeCocoa();
#endif // TARGET_OS_MAC
+#endif // defined(__APPLE__) && defined(__MACH__)
+
#endif // YAZE_APP_CORE_PLATFORM_APP_DELEGATE_H
diff --git a/src/app/core/platform/app_delegate.mm b/src/app/core/platform/app_delegate.mm
index 141df9e8..e03dec3c 100644
--- a/src/app/core/platform/app_delegate.mm
+++ b/src/app/core/platform/app_delegate.mm
@@ -2,7 +2,7 @@
#import "app/core/platform/app_delegate.h"
#import "app/core/controller.h"
#import "app/core/platform/file_dialog.h"
-#import "app/editor/utils/editor.h"
+#import "app/editor/editor.h"
#import "app/rom.h"
#if defined(__APPLE__) && defined(__MACH__)
@@ -11,12 +11,9 @@
#import
-#if TARGET_IPHONE_SIMULATOR == 1
+#if TARGET_IPHONE_SIMULATOR == 1 || TARGET_OS_IPHONE == 1
/* iOS in Xcode simulator */
-#elif TARGET_OS_IPHONE == 1
-/* iOS */
-
#elif TARGET_OS_MAC == 1
/* macOS */
#import
@@ -209,7 +206,8 @@
}
- (void)openFileAction:(id)sender {
- if (!yaze::app::SharedRom::shared_rom_->LoadFromFile(FileDialogWrapper::ShowOpenFileDialog())
+ if (!yaze::app::SharedRom::shared_rom_
+ ->LoadFromFile(yaze::app::core::FileDialogWrapper::ShowOpenFileDialog())
.ok()) {
NSAlert *alert = [[NSAlert alloc] init];
[alert setMessageText:@"Error"];
@@ -227,7 +225,9 @@
NSLog(@"Open Recent File action triggered");
}
-extern "C" void InitializeCocoa() {
+@end
+
+extern "C" void yaze_initialize_cococa() {
@autoreleasepool {
AppDelegate *delegate = [[AppDelegate alloc] init];
[NSApplication sharedApplication];
@@ -236,7 +236,21 @@ extern "C" void InitializeCocoa() {
}
}
-@end
+extern "C" void yaze_run_cocoa_app_delegate(const char *filename) {
+ yaze_initialize_cococa();
+ yaze::app::core::Controller controller;
+ RETURN_VOID_IF_ERROR(controller.OnEntry(filename));
+ while (controller.IsActive()) {
+ @autoreleasepool {
+ controller.OnInput();
+ if (auto status = controller.OnLoad(); !status.ok()) {
+ break;
+ }
+ controller.DoRender();
+ }
+ }
+ controller.OnExit();
+}
#endif
diff --git a/src/app/core/platform/clipboard.cc b/src/app/core/platform/clipboard.cc
index 5c22b209..5607bd0c 100644
--- a/src/app/core/platform/clipboard.cc
+++ b/src/app/core/platform/clipboard.cc
@@ -3,6 +3,14 @@
#include
#include
+namespace yaze {
+namespace app {
+namespace core {
+
void CopyImageToClipboard(const std::vector& data) {}
void GetImageFromClipboard(std::vector& data, int& width,
- int& height) {}
\ No newline at end of file
+ int& height) {}
+
+} // namespace core
+} // namespace app
+} // namespace yaze
\ No newline at end of file
diff --git a/src/app/core/platform/clipboard.h b/src/app/core/platform/clipboard.h
index 4c02f592..325808ba 100644
--- a/src/app/core/platform/clipboard.h
+++ b/src/app/core/platform/clipboard.h
@@ -1,27 +1,18 @@
#ifndef YAZE_APP_CORE_PLATFORM_CLIPBOARD_H
#define YAZE_APP_CORE_PLATFORM_CLIPBOARD_H
-#ifdef _WIN32
-
-void CopyImageToClipboard(const std::vector& data);
-void GetImageFromClipboard(std::vector& data, int& width, int& height);
-
-#elif defined(__APPLE__)
-
#include
#include
-void CopyImageToClipboard(const std::vector& data);
-void GetImageFromClipboard(std::vector& data, int& width, int& height);
+namespace yaze {
+namespace app {
+namespace core {
-#elif defined(__linux__)
+void CopyImageToClipboard(const std::vector &data);
+void GetImageFromClipboard(std::vector &data, int &width, int &height);
-#include
-#include
-
-void CopyImageToClipboard(const std::vector& data);
-void GetImageFromClipboard(std::vector& data, int& width, int& height);
-
-#endif
+} // namespace core
+} // namespace app
+} // namespace yaze
#endif // YAZE_APP_CORE_PLATFORM_CLIPBOARD_H
\ No newline at end of file
diff --git a/src/app/core/platform/clipboard.mm b/src/app/core/platform/clipboard.mm
index bdaba4a7..e756b29e 100644
--- a/src/app/core/platform/clipboard.mm
+++ b/src/app/core/platform/clipboard.mm
@@ -6,7 +6,7 @@
#ifdef TARGET_OS_MAC
#import
-void CopyImageToClipboard(const std::vector& pngData) {
+void yaze::app::core::CopyImageToClipboard(const std::vector& pngData) {
NSData* data = [NSData dataWithBytes:pngData.data() length:pngData.size()];
NSImage* image = [[NSImage alloc] initWithData:data];
@@ -15,7 +15,7 @@ void CopyImageToClipboard(const std::vector& pngData) {
[pasteboard writeObjects:@[ image ]];
}
-void GetImageFromClipboard(std::vector& pixel_data, int& width, int& height) {
+void yaze::app::core::GetImageFromClipboard(std::vector& pixel_data, int& width, int& height) {
NSPasteboard* pasteboard = [NSPasteboard generalPasteboard];
NSArray* classArray = [NSArray arrayWithObject:[NSImage class]];
NSDictionary* options = [NSDictionary dictionary];
diff --git a/src/app/core/platform/file_dialog.cc b/src/app/core/platform/file_dialog.cc
new file mode 100644
index 00000000..1643d1dd
--- /dev/null
+++ b/src/app/core/platform/file_dialog.cc
@@ -0,0 +1,145 @@
+#include "file_dialog.h"
+
+#ifdef _WIN32
+// Include Windows-specific headers
+#include
+#include
+#endif // _WIN32
+
+namespace yaze {
+namespace app {
+namespace core {
+
+#ifdef _WIN32
+
+ std::string FileDialogWrapper::ShowOpenFileDialog() {
+ CoInitializeEx(NULL, COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE);
+
+ IFileDialog *pfd = NULL;
+ HRESULT hr =
+ CoCreateInstance(CLSID_FileOpenDialog, NULL, CLSCTX_ALL, IID_IFileDialog,
+ reinterpret_cast(&pfd));
+ std::string file_path_windows;
+ if (SUCCEEDED(hr)) {
+ // Show the dialog
+ hr = pfd->Show(NULL);
+ if (SUCCEEDED(hr)) {
+ IShellItem *psiResult;
+ hr = pfd->GetResult(&psiResult);
+ if (SUCCEEDED(hr)) {
+ // Get the file path
+ PWSTR pszFilePath;
+ psiResult->GetDisplayName(SIGDN_FILESYSPATH, &pszFilePath);
+ char str[128];
+ wcstombs(str, pszFilePath, 128);
+ file_path_windows = str;
+ psiResult->Release();
+ CoTaskMemFree(pszFilePath);
+ }
+ }
+ pfd->Release();
+ }
+
+ CoUninitialize();
+ return file_path_windows;
+}
+
+ std::string FileDialogWrapper::ShowOpenFolderDialog() {
+ CoInitializeEx(NULL, COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE);
+
+ IFileDialog *pfd = NULL;
+ HRESULT hr =
+ CoCreateInstance(CLSID_FileOpenDialog, NULL, CLSCTX_ALL, IID_IFileDialog,
+ reinterpret_cast(&pfd));
+ std::string folder_path_windows;
+ if (SUCCEEDED(hr)) {
+ // Show the dialog
+ DWORD dwOptions;
+ hr = pfd->GetOptions(&dwOptions);
+ if (SUCCEEDED(hr)) {
+ hr = pfd->SetOptions(dwOptions | FOS_PICKFOLDERS);
+ if (SUCCEEDED(hr)) {
+ hr = pfd->Show(NULL);
+ if (SUCCEEDED(hr)) {
+ IShellItem *psiResult;
+ hr = pfd->GetResult(&psiResult);
+ if (SUCCEEDED(hr)) {
+ // Get the folder path
+ PWSTR pszFolderPath;
+ psiResult->GetDisplayName(SIGDN_FILESYSPATH, &pszFolderPath);
+ char str[128];
+ wcstombs(str, pszFolderPath, 128);
+ folder_path_windows = str;
+ psiResult->Release();
+ CoTaskMemFree(pszFolderPath);
+ }
+ }
+ }
+ }
+ pfd->Release();
+ }
+
+ CoUninitialize();
+ return folder_path_windows;
+}
+
+std::vector FileDialogWrapper::GetSubdirectoriesInFolder(
+ const std::string &folder_path) {
+ std::vector subdirectories;
+ WIN32_FIND_DATA findFileData;
+ HANDLE hFind = FindFirstFile((folder_path + "\\*").c_str(), &findFileData);
+ if (hFind != INVALID_HANDLE_VALUE) {
+ do {
+ if (findFileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
+ if (strcmp(findFileData.cFileName, ".") != 0 &&
+ strcmp(findFileData.cFileName, "..") != 0) {
+ subdirectories.push_back(findFileData.cFileName);
+ }
+ }
+ } while (FindNextFile(hFind, &findFileData) != 0);
+ FindClose(hFind);
+ }
+ return subdirectories;
+}
+
+std::vector FileDialogWrapper::GetFilesInFolder(
+ const std::string &folder_path) {
+ std::vector files;
+ WIN32_FIND_DATA findFileData;
+ HANDLE hFind = FindFirstFile((folder_path + "\\*").c_str(), &findFileData);
+ if (hFind != INVALID_HANDLE_VALUE) {
+ do {
+ if (!(findFileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) {
+ files.push_back(findFileData.cFileName);
+ }
+ } while (FindNextFile(hFind, &findFileData) != 0);
+ FindClose(hFind);
+ }
+ return files;
+}
+
+#elif defined(__linux__)
+
+std::string FileDialogWrapper::ShowOpenFileDialog() {
+ return "Linux: Open file dialog";
+}
+
+std::string FileDialogWrapper::ShowOpenFolderDialog() {
+ return "Linux: Open folder dialog";
+}
+
+std::vector FileDialogWrapper::GetSubdirectoriesInFolder(
+ const std::string& folder_path) {
+ return {"Linux: Subdirectories in folder"};
+}
+
+std::vector FileDialogWrapper::GetFilesInFolder(
+ const std::string& folder_path) {
+ return {"Linux: Files in folder"};
+}
+
+#endif
+
+} // namespace core
+} // namespace app
+} // namespace yaze
\ No newline at end of file
diff --git a/src/app/core/platform/file_dialog.h b/src/app/core/platform/file_dialog.h
index 8d7257c2..6276f86e 100644
--- a/src/app/core/platform/file_dialog.h
+++ b/src/app/core/platform/file_dialog.h
@@ -1,61 +1,25 @@
#ifndef YAZE_APP_CORE_PLATFORM_FILE_DIALOG_H
#define YAZE_APP_CORE_PLATFORM_FILE_DIALOG_H
-#include
-
-#ifdef _WIN32
-// Include Windows-specific headers
-#include
-#include
-
-class FileDialogWrapper {
- public:
- static std::string ShowOpenFileDialog() {
- CoInitializeEx(NULL, COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE);
-
- IFileDialog *pfd = NULL;
- HRESULT hr =
- CoCreateInstance(CLSID_FileOpenDialog, NULL, CLSCTX_ALL,
- IID_IFileDialog, reinterpret_cast(&pfd));
- std::string file_path_windows;
- if (SUCCEEDED(hr)) {
- // Show the dialog
- hr = pfd->Show(NULL);
- if (SUCCEEDED(hr)) {
- IShellItem *psiResult;
- hr = pfd->GetResult(&psiResult);
- if (SUCCEEDED(hr)) {
- // Get the file path
- PWSTR pszFilePath;
- psiResult->GetDisplayName(SIGDN_FILESYSPATH, &pszFilePath);
- char str[128];
- wcstombs(str, pszFilePath, 128);
- file_path_windows = str;
- psiResult->Release();
- CoTaskMemFree(pszFilePath);
- }
- }
- pfd->Release();
- }
-
- CoUninitialize();
- return file_path_windows;
- }
-};
-
-#elif defined(__APPLE__)
-
-#include "TargetConditionals.h"
-
#include
#include
-#ifdef TARGET_OS_MAC
-// Other kinds of Mac OS
+namespace yaze {
+namespace app {
+namespace core {
class FileDialogWrapper {
public:
+ /**
+ * @brief ShowOpenFileDialog opens a file dialog and returns the selected
+ * filepath.
+ */
static std::string ShowOpenFileDialog();
+
+ /**
+ * @brief ShowOpenFolderDialog opens a file dialog and returns the selected
+ * folder path.
+ */
static std::string ShowOpenFolderDialog();
static std::vector GetSubdirectoriesInFolder(
const std::string& folder_path);
@@ -63,34 +27,8 @@ class FileDialogWrapper {
const std::string& folder_path);
};
-#elif TARGET_OS_IPHONE
-
-// iOS
-class FileDialogWrapper {
- public:
- static std::string ShowOpenFileDialog();
- static std::string ShowOpenFolderDialog();
- static std::vector GetSubdirectoriesInFolder(
- const std::string& folder_path);
- static std::vector GetFilesInFolder(
- const std::string& folder_path);
-};
-
-#endif
-
-#elif defined(__linux__)
-
-class FileDialogWrapper {
- public:
- static std::string ShowOpenFileDialog() {
- // Linux-specific file dialog implementation using GTK
- // ...
- return "file_path_linux";
- }
-};
-
-#else
-#error "Unsupported platform."
-#endif
+} // namespace core
+} // namespace app
+} // namespace yaze
#endif // YAZE_APP_CORE_PLATFORM_FILE_DIALOG_H
diff --git a/src/app/core/platform/file_dialog.mm b/src/app/core/platform/file_dialog.mm
index a81a47cb..8b11a3d6 100644
--- a/src/app/core/platform/file_dialog.mm
+++ b/src/app/core/platform/file_dialog.mm
@@ -1,8 +1,8 @@
+#include "app/core/platform/file_dialog.h"
#include
#include
-
-#include "app/core/platform/file_dialog.h"
+#include "imgui/imgui.h"
#if defined(__APPLE__) && defined(__MACH__)
/* Apple OSX and iOS (Darwin). */
@@ -10,32 +10,48 @@
#import
-#if TARGET_IPHONE_SIMULATOR == 1
+#if TARGET_IPHONE_SIMULATOR == 1 || TARGET_OS_IPHONE == 1
/* iOS in Xcode simulator */
-std::string FileDialogWrapper::ShowOpenFileDialog() { return ""; }
+#import
+#import
-std::string FileDialogWrapper::ShowOpenFolderDialog() { return ""; }
+#include "app/core/platform/app_delegate.h"
-std::vector FileDialogWrapper::GetFilesInFolder(const std::string& folder) {
+namespace {
+static std::string selectedFile;
+
+void ShowOpenFileDialogImpl(void (^completionHandler)(std::string)) {
+ AppDelegate *appDelegate = (AppDelegate *)[UIApplication sharedApplication].delegate;
+ [appDelegate PresentDocumentPickerWithCompletionHandler:^(NSString *filePath) {
+ selectedFile = std::string([filePath UTF8String]);
+ completionHandler(selectedFile);
+ }];
+}
+
+std::string ShowOpenFileDialogSync() {
+ __block std::string result;
+
+ ShowOpenFileDialogImpl(^(std::string filePath) {
+ result = filePath;
+ });
+
+ return result;
+}
+}
+
+std::string yaze::app::core::FileDialogWrapper::ShowOpenFileDialog() {
+ return ShowOpenFileDialogSync();
+}
+
+std::string yaze::app::core::FileDialogWrapper::ShowOpenFolderDialog() { return ""; }
+
+std::vector yaze::app::core::FileDialogWrapper::GetFilesInFolder(
+ const std::string &folder) {
return {};
}
-std::vector FileDialogWrapper::GetSubdirectoriesInFolder(const std::string& folder) {
- return {};
-}
-
-#elif TARGET_OS_IPHONE == 1
-/* iOS */
-
-std::string FileDialogWrapper::ShowOpenFileDialog() { return ""; }
-
-std::string FileDialogWrapper::ShowOpenFolderDialog() { return ""; }
-
-std::vector FileDialogWrapper::GetFilesInFolder(const std::string& folder) {
- return {};
-}
-
-std::vector FileDialogWrapper::GetSubdirectoriesInFolder(const std::string& folder) {
+std::vector yaze::app::core::FileDialogWrapper::GetSubdirectoriesInFolder(
+ const std::string &folder) {
return {};
}
@@ -44,7 +60,7 @@ std::vector FileDialogWrapper::GetSubdirectoriesInFolder(const std:
#import
-std::string FileDialogWrapper::ShowOpenFileDialog() {
+std::string yaze::app::core::FileDialogWrapper::ShowOpenFileDialog() {
NSOpenPanel* openPanel = [NSOpenPanel openPanel];
[openPanel setCanChooseFiles:YES];
[openPanel setCanChooseDirectories:NO];
@@ -59,7 +75,7 @@ std::string FileDialogWrapper::ShowOpenFileDialog() {
return "";
}
-std::string FileDialogWrapper::ShowOpenFolderDialog() {
+std::string yaze::app::core::FileDialogWrapper::ShowOpenFolderDialog() {
NSOpenPanel* openPanel = [NSOpenPanel openPanel];
[openPanel setCanChooseFiles:NO];
[openPanel setCanChooseDirectories:YES];
@@ -74,7 +90,8 @@ std::string FileDialogWrapper::ShowOpenFolderDialog() {
return "";
}
-std::vector FileDialogWrapper::GetFilesInFolder(const std::string& folder) {
+std::vector yaze::app::core::FileDialogWrapper::GetFilesInFolder(
+ const std::string& folder) {
std::vector filenames;
NSFileManager* fileManager = [NSFileManager defaultManager];
NSDirectoryEnumerator* enumerator =
@@ -89,7 +106,8 @@ std::vector FileDialogWrapper::GetFilesInFolder(const std::string&
return filenames;
}
-std::vector FileDialogWrapper::GetSubdirectoriesInFolder(const std::string& folder) {
+std::vector yaze::app::core::FileDialogWrapper::GetSubdirectoriesInFolder(
+ const std::string& folder) {
std::vector subdirectories;
NSFileManager* fileManager = [NSFileManager defaultManager];
NSDirectoryEnumerator* enumerator =
@@ -113,4 +131,4 @@ std::vector FileDialogWrapper::GetSubdirectoriesInFolder(const std:
// Unsupported platform
#endif // TARGET_OS_MAC
-#endif // __APPLE__ && __MACH__
+#endif // __APPLE__ && __MACH__
\ No newline at end of file
diff --git a/src/app/core/platform/file_path.h b/src/app/core/platform/file_path.h
index 5cffc9de..c06ef3e3 100644
--- a/src/app/core/platform/file_path.h
+++ b/src/app/core/platform/file_path.h
@@ -1,6 +1,20 @@
#ifndef YAZE_APP_CORE_PLATFORM_FILE_PATH_H
#define YAZE_APP_CORE_PLATFORM_FILE_PATH_H
+#include
+
+namespace yaze {
+namespace app {
+namespace core {
+
+/**
+ * @brief GetBundleResourcePath returns the path to the bundle resource
+ * directory. Specific to MacOS.
+ */
std::string GetBundleResourcePath();
+} // namespace core
+} // namespace app
+} // namespace yaze
+
#endif // YAZE_APP_CORE_PLATFORM_FILE_PATH_H
diff --git a/src/app/core/platform/file_path.mm b/src/app/core/platform/file_path.mm
index 74dccef7..617e80cf 100644
--- a/src/app/core/platform/file_path.mm
+++ b/src/app/core/platform/file_path.mm
@@ -1,3 +1,5 @@
+#include "file_path.h"
+
#include
#include
@@ -5,17 +7,15 @@
#include
#include
-#if TARGET_IPHONE_SIMULATOR == 1
-std::string GetBundleResourcePath() {}
-#elif TARGET_OS_IPHONE == 1
-std::string GetBundleResourcePath() {
+#if TARGET_IPHONE_SIMULATOR == 1 || TARGET_OS_IPHONE == 1
+std::string yaze::app::core::GetBundleResourcePath() {
NSBundle* bundle = [NSBundle mainBundle];
NSString* resourceDirectoryPath = [bundle bundlePath];
NSString* path = [resourceDirectoryPath stringByAppendingString:@"/"];
return [path UTF8String];
}
#elif TARGET_OS_MAC == 1
-std::string GetBundleResourcePath() {
+std::string yaze::app::core::GetBundleResourcePath() {
NSBundle* bundle = [NSBundle mainBundle];
NSString* resourceDirectoryPath = [bundle bundlePath];
NSString* path = [resourceDirectoryPath stringByAppendingString:@"/"];
diff --git a/src/app/core/platform/font_loader.cc b/src/app/core/platform/font_loader.cc
index 0c87a33b..18d8c3a3 100644
--- a/src/app/core/platform/font_loader.cc
+++ b/src/app/core/platform/font_loader.cc
@@ -1,17 +1,124 @@
#include "app/core/platform/font_loader.h"
+#include
+#include
+#include
+#include
+
+#include "absl/status/status.h"
+#include "absl/strings/str_cat.h"
+#include "absl/strings/str_format.h"
+#include "app/core/platform/file_path.h"
+#include "app/gui/icons.h"
#include "imgui/imgui.h"
-#include
-#include
+namespace yaze {
+namespace app {
+namespace core {
+
+absl::Status LoadPackageFonts() {
+ ImGuiIO &io = ImGui::GetIO();
+
+ static const char *KARLA_REGULAR = "Karla-Regular.ttf";
+ static const char *ROBOTO_MEDIUM = "Roboto-Medium.ttf";
+ static const char *COUSINE_REGULAR = "Cousine-Regular.ttf";
+ static const char *DROID_SANS = "DroidSans.ttf";
+ static const char *NOTO_SANS_JP = "NotoSansJP.ttf";
+ static const char *IBM_PLEX_JP = "IBMPlexSansJP-Bold.ttf";
+ static const float FONT_SIZE_DEFAULT = 16.0f;
+ static const float FONT_SIZE_DROID_SANS = 18.0f;
+ static const float ICON_FONT_SIZE = 18.0f;
+
+ // Icon configuration
+ static const ImWchar icons_ranges[] = {ICON_MIN_MD, 0xf900, 0};
+ ImFontConfig icons_config;
+ icons_config.MergeMode = true;
+ icons_config.GlyphOffset.y = 5.0f;
+ icons_config.GlyphMinAdvanceX = 13.0f;
+ icons_config.PixelSnapH = true;
+
+ // Japanese font configuration
+ ImFontConfig japanese_font_config;
+ japanese_font_config.MergeMode = true;
+ icons_config.GlyphOffset.y = 5.0f;
+ icons_config.GlyphMinAdvanceX = 13.0f;
+ icons_config.PixelSnapH = true;
+
+ // List of fonts to be loaded
+ std::vector font_paths = {
+ KARLA_REGULAR, ROBOTO_MEDIUM, COUSINE_REGULAR, IBM_PLEX_JP, DROID_SANS};
+
+ // Load fonts with associated icon and Japanese merges
+ for (const auto &font_path : font_paths) {
+ float font_size =
+ (font_path == DROID_SANS) ? FONT_SIZE_DROID_SANS : FONT_SIZE_DEFAULT;
+
+ std::string actual_font_path;
+#ifdef __APPLE__
+#if TARGET_OS_IOS == 1
+ const std::string kBundlePath = GetBundleResourcePath();
+ actual_font_path = kBundlePath + font_path;
+#else
+ actual_font_path = absl::StrCat(GetBundleResourcePath(),
+ "Contents/Resources/font/", font_path);
+#endif
+#else
+ actual_font_path = std::filesystem::absolute(font_path).string();
+#endif
+
+ if (!io.Fonts->AddFontFromFileTTF(actual_font_path.data(), font_size)) {
+ return absl::InternalError(
+ absl::StrFormat("Failed to load font from %s", actual_font_path));
+ }
+
+ // Merge icon set
+ std::string actual_icon_font_path = "";
+ const char *icon_font_path = FONT_ICON_FILE_NAME_MD;
+#if defined(__APPLE__) && defined(__MACH__)
+#if TARGET_OS_IOS == 1
+ const std::string kIconBundlePath = GetBundleResourcePath();
+ actual_icon_font_path = kIconBundlePath + "MaterialIcons-Regular.ttf";
+#else
+ actual_icon_font_path =
+ absl::StrCat(GetBundleResourcePath(),
+ "Contents/Resources/font/MaterialIcons-Regular.ttf");
+#endif
+#else
+ actual_icon_font_path = std::filesystem::absolute(icon_font_path).string();
+#endif
+ io.Fonts->AddFontFromFileTTF(actual_icon_font_path.data(), ICON_FONT_SIZE,
+ &icons_config, icons_ranges);
+
+ // Merge Japanese font
+ std::string actual_japanese_font_path = "";
+ const char *japanese_font_path = NOTO_SANS_JP;
+#if defined(__APPLE__) && defined(__MACH__)
+#if TARGET_OS_IOS == 1
+ const std::string kJapaneseBundlePath = GetBundleResourcePath();
+ actual_japanese_font_path = kJapaneseBundlePath + japanese_font_path;
+#else
+ actual_japanese_font_path =
+ absl::StrCat(GetBundleResourcePath(), "Contents/Resources/font/",
+ japanese_font_path);
+#endif
+#else
+ actual_japanese_font_path =
+ std::filesystem::absolute(japanese_font_path).string();
+#endif
+ io.Fonts->AddFontFromFileTTF(actual_japanese_font_path.data(), 18.0f,
+ &japanese_font_config,
+ io.Fonts->GetGlyphRangesJapanese());
+ }
+ return absl::OkStatus();
+}
#ifdef _WIN32
#include
-int CALLBACK EnumFontFamExProc(const LOGFONT* lpelfe, const TEXTMETRIC* lpntme,
+int CALLBACK EnumFontFamExProc(const LOGFONT *lpelfe, const TEXTMETRIC *lpntme,
DWORD FontType, LPARAM lParam) {
// Step 3: Load the font into ImGui
- ImGuiIO& io = ImGui::GetIO();
+ ImGuiIO &io = ImGui::GetIO();
io.Fonts->AddFontFromFileTTF(lpelfe->lfFaceName, 16.0f);
return 1;
@@ -34,8 +141,8 @@ void LoadSystemFonts() {
RegQueryInfoKey(hKey, NULL, NULL, NULL, NULL, NULL, NULL, &valueCount,
&maxValueNameSize, &maxValueDataSize, NULL, NULL);
- char* valueName = new char[maxValueNameSize + 1]; // +1 for null terminator
- BYTE* valueData = new BYTE[maxValueDataSize + 1]; // +1 for null terminator
+ char *valueName = new char[maxValueNameSize + 1]; // +1 for null terminator
+ BYTE *valueData = new BYTE[maxValueDataSize + 1]; // +1 for null terminator
// Enumerate all font entries
for (DWORD i = 0; i < valueCount; i++) {
@@ -52,7 +159,9 @@ void LoadSystemFonts() {
valueData, &valueDataSize) == ERROR_SUCCESS) {
if (valueType == REG_SZ) {
// Add the font file path to the vector
- std::string fontPath((char*)valueData);
+ std::string fontPath(reinterpret_cast(valueData),
+ valueDataSize);
+
fontPaths.push_back(fontPath);
}
}
@@ -64,10 +173,57 @@ void LoadSystemFonts() {
RegCloseKey(hKey);
}
- ImGuiIO& io = ImGui::GetIO();
+ ImGuiIO &io = ImGui::GetIO();
- for (const auto& fontPath : fontPaths) {
- io.Fonts->AddFontFromFileTTF(fontPath.c_str(), 16.0f);
+ // List of common font face names
+ static const std::unordered_set commonFontFaceNames = {
+ "arial",
+ "times",
+ "cour",
+ "verdana",
+ "tahoma",
+ "comic",
+ "Impact",
+ "ariblk",
+ "Trebuchet MS",
+ "Georgia",
+ "Palatino Linotype",
+ "Lucida Sans Unicode",
+ "Tahoma",
+ "Lucida Console"};
+
+ for (auto &fontPath : fontPaths) {
+ // Check if the font path has a "C:\" prefix
+ if (fontPath.substr(0, 2) != "C:") {
+ // Add "C:\Windows\Fonts\" prefix to the font path
+ fontPath = absl::StrFormat("C:\\Windows\\Fonts\\%s", fontPath.c_str());
+ }
+
+ // Check if the font file has a .ttf or .TTF extension
+ std::string extension = fontPath.substr(fontPath.find_last_of(".") + 1);
+ if (extension == "ttf" || extension == "TTF") {
+ // Get the font face name from the font path
+ std::string fontFaceName =
+ fontPath.substr(fontPath.find_last_of("\\/") + 1);
+ fontFaceName = fontFaceName.substr(0, fontFaceName.find_last_of("."));
+
+ // Check if the font face name is in the common font face names list
+ if (commonFontFaceNames.find(fontFaceName) != commonFontFaceNames.end()) {
+ io.Fonts->AddFontFromFileTTF(fontPath.c_str(), 16.0f);
+
+ // Merge icon set
+ // Icon configuration
+ static const ImWchar icons_ranges[] = {ICON_MIN_MD, 0xf900, 0};
+ ImFontConfig icons_config;
+ static const float ICON_FONT_SIZE = 18.0f;
+ icons_config.MergeMode = true;
+ icons_config.GlyphOffset.y = 5.0f;
+ icons_config.GlyphMinAdvanceX = 13.0f;
+ icons_config.PixelSnapH = true;
+ io.Fonts->AddFontFromFileTTF(FONT_ICON_FILE_NAME_MD, ICON_FONT_SIZE,
+ &icons_config, icons_ranges);
+ }
+ }
}
}
@@ -78,4 +234,8 @@ void LoadSystemFonts() {
// ...
}
-#endif
\ No newline at end of file
+#endif
+
+} // namespace core
+} // namespace app
+} // namespace yaze
\ No newline at end of file
diff --git a/src/app/core/platform/font_loader.h b/src/app/core/platform/font_loader.h
index a0752dda..685dcc2b 100644
--- a/src/app/core/platform/font_loader.h
+++ b/src/app/core/platform/font_loader.h
@@ -1,26 +1,17 @@
-// FontLoader.h
-#ifndef FONTLOADER_H
-#define FONTLOADER_H
+#ifndef YAZE_APP_CORE_PLATFORM_FONTLOADER_H
+#define YAZE_APP_CORE_PLATFORM_FONTLOADER_H
-#include "TargetConditionals.h"
+#include "absl/status/status.h"
-#ifdef _WIN32
-#include
-// Windows specific function declaration for loading system fonts into ImGui
-int CALLBACK EnumFontFamExProc(const LOGFONT* lpelfe, const TEXTMETRIC* lpntme,
- DWORD FontType, LPARAM lParam);
-#elif __APPLE__
-
-#ifdef TARGET_OS_MAC
+namespace yaze {
+namespace app {
+namespace core {
void LoadSystemFonts();
+absl::Status LoadPackageFonts();
-#elif TARGET_OS_IPHONE
+} // namespace core
+} // namespace app
+} // namespace yaze
-void LoadSystemFonts();
-
-#endif
-
-#endif
-
-#endif // FONTLOADER_H
+#endif // YAZE_APP_CORE_PLATFORM_FONTLOADER_H
diff --git a/src/app/core/platform/font_loader.mm b/src/app/core/platform/font_loader.mm
index 3b8e0a7d..48e0a258 100644
--- a/src/app/core/platform/font_loader.mm
+++ b/src/app/core/platform/font_loader.mm
@@ -1,32 +1,20 @@
-// FontLoader.mm
#include "app/core/platform/font_loader.h"
-#include "imgui/imgui.h"
-
-#include "app/gui/icons.h"
-
-#if defined(__APPLE__) && defined(__MACH__)
-/* Apple OSX and iOS (Darwin). */
+#import
#include
-#import
+#include "app/gui/icons.h"
+#include "imgui/imgui.h"
-#if TARGET_IPHONE_SIMULATOR == 1
-/* iOS in Xcode simulator */
-void LoadSystemFonts() {}
-
-#elif TARGET_OS_IPHONE == 1
+#if TARGET_OS_IPHONE == 1 || TARGET_IPHONE_SIMULATOR == 1
/* iOS */
-void LoadSystemFonts() {}
+void yaze::app::core::LoadSystemFonts() {}
#elif TARGET_OS_MAC == 1
/* macOS */
-
#import
-// MacOS Implementation
-void LoadSystemFonts() {
- // List of common macOS system fonts
+void yaze::app::core::LoadSystemFonts() {
NSArray *fontNames = @[ @"Helvetica", @"Times New Roman", @"Courier", @"Arial", @"Verdana" ];
for (NSString *fontName in fontNames) {
@@ -67,8 +55,5 @@ void LoadSystemFonts() {
}
}
}
-#else
-// Unsupported platform
-#endif
#endif
diff --git a/src/app/core/platform/renderer.h b/src/app/core/platform/renderer.h
new file mode 100644
index 00000000..1f173fa9
--- /dev/null
+++ b/src/app/core/platform/renderer.h
@@ -0,0 +1,83 @@
+#ifndef YAZE_APP_CORE_PLATFORM_RENDERER_H
+#define YAZE_APP_CORE_PLATFORM_RENDERER_H
+
+#include
+
+#include
+
+#include "absl/status/status.h"
+#include "absl/strings/str_format.h"
+#include "app/core/utils/sdl_deleter.h"
+#include "app/gfx/bitmap.h"
+
+namespace yaze {
+namespace app {
+namespace core {
+
+/**
+ * @class Renderer
+ * @brief The Renderer class represents the renderer for the Yaze application.
+ *
+ * This class is a singleton that provides functionality for creating and
+ * rendering bitmaps to the screen. It also includes methods for updating
+ * bitmaps on the screen.
+ */
+class Renderer {
+ public:
+ static Renderer &GetInstance() {
+ static Renderer instance;
+ return instance;
+ }
+
+ absl::Status CreateRenderer(SDL_Window *window) {
+ renderer_ = std::unique_ptr(SDL_CreateRenderer(
+ window, -1, SDL_RENDERER_PRESENTVSYNC | SDL_RENDERER_ACCELERATED));
+ if (renderer_ == nullptr) {
+ return absl::InternalError(
+ absl::StrFormat("SDL_CreateRenderer: %s\n", SDL_GetError()));
+ }
+ SDL_SetRenderDrawBlendMode(renderer_.get(), SDL_BLENDMODE_BLEND);
+ SDL_SetRenderDrawColor(renderer_.get(), 0x00, 0x00, 0x00, 0x00);
+ return absl::OkStatus();
+ }
+
+ auto renderer() -> SDL_Renderer * { return renderer_.get(); }
+
+ /**
+ * @brief Used to render a bitmap to the screen.
+ */
+ void RenderBitmap(gfx::Bitmap *bitmap) {
+ bitmap->CreateTexture(renderer_.get());
+ }
+
+ /**
+ * @brief Used to update a bitmap on the screen.
+ */
+ void UpdateBitmap(gfx::Bitmap *bitmap) {
+ bitmap->UpdateTexture(renderer_.get());
+ }
+
+ absl::Status CreateAndRenderBitmap(int width, int height, int depth,
+ const std::vector &data,
+ gfx::Bitmap &bitmap,
+ gfx::SnesPalette &palette) {
+ bitmap.Create(width, height, depth, data);
+ RETURN_IF_ERROR(bitmap.ApplyPalette(palette));
+ RenderBitmap(&bitmap);
+ return absl::OkStatus();
+ }
+
+ private:
+ Renderer() = default;
+
+ std::unique_ptr renderer_;
+
+ Renderer(const Renderer &) = delete;
+ Renderer &operator=(const Renderer &) = delete;
+};
+
+} // namespace core
+} // namespace app
+} // namespace yaze
+
+#endif
diff --git a/src/app/core/platform/view_controller.h b/src/app/core/platform/view_controller.h
new file mode 100644
index 00000000..7c4318a3
--- /dev/null
+++ b/src/app/core/platform/view_controller.h
@@ -0,0 +1,29 @@
+#ifndef YAZE_APP_CORE_PLATFORM_VIEW_CONTROLLER_H
+#define YAZE_APP_CORE_PLATFORM_VIEW_CONTROLLER_H
+
+#ifdef __APPLE__
+#include
+
+#if TARGET_OS_OSX
+#include "imgui_impl_osx.h"
+@interface AppViewController : NSViewController
+@end
+#else
+@interface AppViewController : UIViewController
+@property(nonatomic) yaze::app::core::Controller *controller;
+@property(nonatomic) UIHoverGestureRecognizer *hoverGestureRecognizer;
+@property(nonatomic) UIPinchGestureRecognizer *pinchRecognizer;
+@property(nonatomic) UISwipeGestureRecognizer *swipeRecognizer;
+@property(nonatomic) UILongPressGestureRecognizer *longPressRecognizer;
+@end
+#endif
+
+@interface AppViewController ()
+@property(nonatomic, readonly) MTKView *mtkView;
+@property(nonatomic, strong) id device;
+@property(nonatomic, strong) id commandQueue;
+@end
+
+#endif // __APPLE__
+
+#endif // YAZE_APP_CORE_PLATFORM_APP_VIEW_CONTROLLER_H
diff --git a/src/app/core/labeling.cc b/src/app/core/project.cc
similarity index 73%
rename from src/app/core/labeling.cc
rename to src/app/core/project.cc
index db464091..98051bf2 100644
--- a/src/app/core/labeling.cc
+++ b/src/app/core/project.cc
@@ -1,22 +1,66 @@
-#include "app/core/labeling.h"
+#include "project.h"
+#include
+#include
+
+#include "app/core/constants.h"
+#include "app/gui/icons.h"
#include "imgui/imgui.h"
#include "imgui/misc/cpp/imgui_stdlib.h"
-#include
-#include
-#include
-#include
-#include
-#include
-
-#include "app/core/common.h"
-#include "app/core/constants.h"
-#include "app/gui/icons.h"
-
namespace yaze {
namespace app {
-namespace core {
+
+absl::Status Project::Open(const std::string& project_path) {
+ filepath = project_path;
+ name = project_path.substr(project_path.find_last_of("/") + 1);
+
+ std::ifstream in(project_path);
+
+ if (!in.good()) {
+ return absl::InternalError("Could not open project file.");
+ }
+
+ std::string line;
+ std::getline(in, name);
+ std::getline(in, filepath);
+ std::getline(in, rom_filename_);
+ std::getline(in, code_folder_);
+ std::getline(in, labels_filename_);
+ std::getline(in, keybindings_file);
+
+ while (std::getline(in, line)) {
+ if (line == kEndOfProjectFile) {
+ break;
+ }
+ }
+
+ in.close();
+
+ return absl::OkStatus();
+}
+
+absl::Status Project::Save() {
+ RETURN_IF_ERROR(CheckForEmptyFields());
+
+ std::ofstream out(filepath + "/" + name + ".yaze");
+ if (!out.good()) {
+ return absl::InternalError("Could not open project file.");
+ }
+
+ out << name << std::endl;
+ out << filepath << std::endl;
+ out << rom_filename_ << std::endl;
+ out << code_folder_ << std::endl;
+ out << labels_filename_ << std::endl;
+ out << keybindings_file << std::endl;
+
+ out << kEndOfProjectFile << std::endl;
+
+ out.close();
+
+ return absl::OkStatus();
+}
bool ResourceLabelManager::LoadLabels(const std::string& filename) {
std::ifstream file(filename);
@@ -124,6 +168,11 @@ void ResourceLabelManager::SelectableLabelWithNameEdit(
}
}
+std::string ResourceLabelManager::GetLabel(const std::string& type,
+ const std::string& key) {
+ return labels_[type][key];
+}
+
std::string ResourceLabelManager::CreateOrGetLabel(
const std::string& type, const std::string& key,
const std::string& defaultValue) {
@@ -136,6 +185,5 @@ std::string ResourceLabelManager::CreateOrGetLabel(
return labels_[type][key];
}
-} // namespace core
} // namespace app
} // namespace yaze
diff --git a/src/app/core/project.h b/src/app/core/project.h
index 113716f3..cfe21019 100644
--- a/src/app/core/project.h
+++ b/src/app/core/project.h
@@ -1,26 +1,20 @@
#ifndef YAZE_APP_CORE_PROJECT_H
#define YAZE_APP_CORE_PROJECT_H
-#include "absl/strings/match.h"
-
+#include
#include
#include
-#include
#include
#include "absl/status/status.h"
-#include "absl/strings/string_view.h"
#include "app/core/common.h"
+#include "app/core/utils/file_util.h"
namespace yaze {
namespace app {
-constexpr absl::string_view kProjectFileExtension = ".yaze";
-constexpr absl::string_view kProjectFileFilter =
- "Yaze Project Files (*.yaze)\0*.yaze\0";
-constexpr absl::string_view kPreviousRomFilenameDelimiter =
- "PreviousRomFilename";
-constexpr absl::string_view kEndOfProjectFile = "EndOfProjectFile";
+const std::string kRecentFilesFilename = "recent_files.txt";
+constexpr char kEndOfProjectFile[] = "EndOfProjectFile";
/**
* @struct Project
@@ -31,81 +25,12 @@ constexpr absl::string_view kEndOfProjectFile = "EndOfProjectFile";
* user can have different rom file names for a single project and keep track of
* backups.
*/
-
struct Project : public core::ExperimentFlags {
- /**
- * @brief Creates a new project.
- *
- * @param project_name The name of the project.
- * @param project_path The path to the project.
- * @return An absl::Status indicating the success or failure of the project
- * creation.
- */
- absl::Status Create(const std::string &project_name) {
+ absl::Status Create(const std::string& project_name) {
name = project_name;
project_opened_ = true;
return absl::OkStatus();
}
-
- absl::Status Open(const std::string &project_path) {
- filepath = project_path;
- name = project_path.substr(project_path.find_last_of("/") + 1);
-
- std::ifstream in(project_path);
-
- if (!in.good()) {
- return absl::InternalError("Could not open project file.");
- }
-
- std::string line;
- std::getline(in, name);
- std::getline(in, filepath);
- std::getline(in, rom_filename_);
- std::getline(in, code_folder_);
- std::getline(in, labels_filename_);
-
- while (std::getline(in, line)) {
- if (line == kEndOfProjectFile) {
- break;
- }
-
- if (absl::StrContains(line, kPreviousRomFilenameDelimiter)) {
- previous_rom_filenames_.push_back(
- line.substr(line.find(kPreviousRomFilenameDelimiter) +
- kPreviousRomFilenameDelimiter.size() + 1));
- }
- }
-
- in.close();
-
- return absl::OkStatus();
- }
-
- absl::Status Save() {
- RETURN_IF_ERROR(CheckForEmptyFields());
-
- std::ofstream out(filepath + "/" + name + ".yaze");
- if (!out.good()) {
- return absl::InternalError("Could not open project file.");
- }
-
- out << name << std::endl;
- out << filepath << std::endl;
- out << rom_filename_ << std::endl;
- out << code_folder_ << std::endl;
- out << labels_filename_ << std::endl;
-
- for (const auto &filename : previous_rom_filenames_) {
- out << kPreviousRomFilenameDelimiter << " " << filename << std::endl;
- }
-
- out << kEndOfProjectFile << std::endl;
-
- out.close();
-
- return absl::OkStatus();
- }
-
absl::Status CheckForEmptyFields() {
if (name.empty() || filepath.empty() || rom_filename_.empty() ||
code_folder_.empty() || labels_filename_.empty()) {
@@ -116,20 +41,93 @@ struct Project : public core::ExperimentFlags {
return absl::OkStatus();
}
-
- absl::Status SerializeExperimentFlags() {
- auto flags = mutable_flags();
- // TODO: Serialize flags
- return absl::OkStatus();
- }
+ absl::Status Open(const std::string &project_path);
+ absl::Status Save();
bool project_opened_ = false;
std::string name;
+ std::string flags = "";
std::string filepath;
std::string rom_filename_ = "";
std::string code_folder_ = "";
std::string labels_filename_ = "";
- std::vector previous_rom_filenames_;
+ std::string keybindings_file = "";
+};
+
+// Default types
+static constexpr absl::string_view kDefaultTypes[] = {
+ "Dungeon Names", "Dungeon Room Names", "Overworld Map Names"};
+
+struct ResourceLabelManager {
+ bool LoadLabels(const std::string& filename);
+ bool SaveLabels();
+ void DisplayLabels(bool* p_open);
+ void EditLabel(const std::string& type, const std::string& key,
+ const std::string& newValue);
+ void SelectableLabelWithNameEdit(bool selected, const std::string& type,
+ const std::string& key,
+ const std::string& defaultValue);
+ std::string GetLabel(const std::string& type, const std::string& key);
+ std::string CreateOrGetLabel(const std::string& type, const std::string& key,
+ const std::string& defaultValue);
+
+ bool labels_loaded_ = false;
+ std::string filename_;
+ struct ResourceType {
+ std::string key_name;
+ std::string display_description;
+ };
+
+ std::unordered_map>
+ labels_;
+};
+
+class RecentFilesManager {
+ public:
+ RecentFilesManager() : RecentFilesManager(kRecentFilesFilename) {}
+ RecentFilesManager(const std::string& filename) : filename_(filename) {}
+
+ void AddFile(const std::string& file_path) {
+ // Add a file to the list, avoiding duplicates
+ auto it = std::find(recent_files_.begin(), recent_files_.end(), file_path);
+ if (it == recent_files_.end()) {
+ recent_files_.push_back(file_path);
+ }
+ }
+
+ void Save() {
+ std::ofstream file(filename_);
+ if (!file.is_open()) {
+ return; // Handle the error appropriately
+ }
+
+ for (const auto& file_path : recent_files_) {
+ file << file_path << std::endl;
+ }
+ }
+
+ void Load() {
+ std::ifstream file(filename_);
+ if (!file.is_open()) {
+ return;
+ }
+
+ recent_files_.clear();
+ std::string line;
+ while (std::getline(file, line)) {
+ if (!line.empty()) {
+ recent_files_.push_back(line);
+ }
+ }
+ }
+
+ const std::vector& GetRecentFiles() const {
+ return recent_files_;
+ }
+
+ private:
+ std::string filename_;
+ std::vector recent_files_;
};
} // namespace app
diff --git a/src/app/core/testable.h b/src/app/core/testable.h
deleted file mode 100644
index 1199f3de..00000000
--- a/src/app/core/testable.h
+++ /dev/null
@@ -1,19 +0,0 @@
-#ifndef YAZE_APP_CORE_TESTABLE_H
-#define YAZE_APP_CORE_TESTABLE_H
-
-#include
-
-namespace yaze {
-namespace app {
-namespace core {
-class GuiTestable {
- public:
- virtual void RegisterTests(ImGuiTestEngine* e) = 0;
-
- ImGuiTestEngine* test_engine;
-};
-} // namespace core
-} // namespace app
-} // namespace yaze
-
-#endif // YAZE_APP_CORE_TESTABLE_H
\ No newline at end of file
diff --git a/src/app/core/utils/file_util.cc b/src/app/core/utils/file_util.cc
new file mode 100644
index 00000000..1b332a9a
--- /dev/null
+++ b/src/app/core/utils/file_util.cc
@@ -0,0 +1,74 @@
+#include "file_util.h"
+
+#if defined(_WIN32)
+#include
+#else
+#include
+#include
+#endif
+
+#include
+#include
+
+namespace yaze {
+namespace app {
+namespace core {
+
+std::string GetFileExtension(const std::string &filename) {
+ size_t dot = filename.find_last_of(".");
+ if (dot == std::string::npos) {
+ return "";
+ }
+ return filename.substr(dot + 1);
+}
+
+std::string GetFileName(const std::string &filename) {
+ size_t slash = filename.find_last_of("/");
+ if (slash == std::string::npos) {
+ return filename;
+ }
+ return filename.substr(slash + 1);
+}
+
+std::string LoadFile(const std::string &filename, Platform platform) {
+ std::string contents;
+ std::string filepath = GetConfigDirectory(platform) + "/" + filename;
+ std::ifstream file(filepath);
+ if (file.is_open()) {
+ std::stringstream buffer;
+ buffer << file.rdbuf();
+ contents = buffer.str();
+ file.close();
+ }
+ return contents;
+}
+
+void SaveFile(const std::string &filename, const std::string &contents,
+ Platform platform) {
+ std::string filepath = GetConfigDirectory(platform) + "/" + filename;
+ std::ofstream file(filepath);
+ if (file.is_open()) {
+ file << contents;
+ file.close();
+ }
+}
+
+std::string GetConfigDirectory(Platform platform) {
+ std::string config_directory = ".yaze";
+ switch (platform) {
+ case Platform::kWindows:
+ config_directory = "~/AppData/Roaming/yaze";
+ break;
+ case Platform::kMacOS:
+ case Platform::kLinux:
+ config_directory = "~/.config/yaze";
+ break;
+ default:
+ break;
+ }
+ return config_directory;
+}
+
+} // namespace core
+} // namespace app
+} // namespace yaze
diff --git a/src/app/core/utils/file_util.h b/src/app/core/utils/file_util.h
new file mode 100644
index 00000000..67dd7ff1
--- /dev/null
+++ b/src/app/core/utils/file_util.h
@@ -0,0 +1,24 @@
+#ifndef YAZE_APP_CORE_UTILS_FILE_UTIL_H
+#define YAZE_APP_CORE_UTILS_FILE_UTIL_H
+
+#include
+
+namespace yaze {
+namespace app {
+namespace core {
+
+enum class Platform { kUnknown, kMacOS, kiOS, kWindows, kLinux };
+
+std::string GetFileExtension(const std::string &filename);
+std::string GetFileName(const std::string &filename);
+std::string LoadFile(const std::string &filename, Platform platform);
+std::string GetConfigDirectory(Platform platform);
+
+void SaveFile(const std::string &filename, const std::string &data,
+ Platform platform);
+
+} // namespace core
+} // namespace app
+} // namespace yaze
+
+#endif // YAZE_APP_CORE_UTILS_FILE_UTIL_H
diff --git a/src/app/core/utils/sdl_deleter.h b/src/app/core/utils/sdl_deleter.h
new file mode 100644
index 00000000..8fd4100d
--- /dev/null
+++ b/src/app/core/utils/sdl_deleter.h
@@ -0,0 +1,40 @@
+#ifndef YAZE_APP_CORE_UTILS_SDL_DELETER_H_
+#define YAZE_APP_CORE_UTILS_SDL_DELETER_H_
+
+#include
+
+namespace yaze {
+namespace app {
+namespace core {
+
+/**
+ * @brief Deleter for SDL_Window and SDL_Renderer.
+ */
+struct SDL_Deleter {
+ void operator()(SDL_Window *p) const { SDL_DestroyWindow(p); }
+ void operator()(SDL_Renderer *p) const { SDL_DestroyRenderer(p); }
+};
+
+/**
+ * @brief Deleter for SDL_Texture.
+ */
+struct SDL_Texture_Deleter {
+ void operator()(SDL_Texture *p) const {
+ SDL_DestroyTexture(p);
+ }
+};
+
+/**
+ * @brief Deleter for SDL_Surface.
+ */
+struct SDL_Surface_Deleter {
+ void operator()(SDL_Surface *p) const {
+ SDL_FreeSurface(p);
+ }
+};
+
+} // namespace core
+} // namespace app
+} // namespace yaze
+
+#endif // YAZE_APP_CORE_UTILS_SDL_DELETER_H_
diff --git a/src/app/editor/code/assembly_editor.cc b/src/app/editor/code/assembly_editor.cc
index 2f783b8e..d833ce05 100644
--- a/src/app/editor/code/assembly_editor.cc
+++ b/src/app/editor/code/assembly_editor.cc
@@ -1,16 +1,17 @@
#include "assembly_editor.h"
#include "ImGuiColorTextEdit/TextEditor.h"
-
+#include "ImGuiFileDialog/ImGuiFileDialog.h"
+#include "absl/strings/str_cat.h"
#include "app/core/platform/file_dialog.h"
#include "app/gui/icons.h"
-#include "app/gui/input.h"
-#include "core/constants.h"
namespace yaze {
namespace app {
namespace editor {
+using core::FileDialogWrapper;
+
namespace {
std::vector RemoveIgnoredFiles(
@@ -57,11 +58,11 @@ core::FolderItem LoadFolder(const std::string& folder) {
auto root_files = FileDialogWrapper::GetFilesInFolder(current_folder.name);
current_folder.files = RemoveIgnoredFiles(root_files, ignored_files);
- for (const auto& folder :
+ for (const auto& subfolder :
FileDialogWrapper::GetSubdirectoriesInFolder(current_folder.name)) {
core::FolderItem folder_item;
- folder_item.name = folder;
- std::string full_folder = current_folder.name + "/" + folder;
+ folder_item.name = subfolder;
+ std::string full_folder = current_folder.name + "/" + subfolder;
auto folder_files = FileDialogWrapper::GetFilesInFolder(full_folder);
for (const auto& files : folder_files) {
// Remove subdirectory files
diff --git a/src/app/editor/code/assembly_editor.h b/src/app/editor/code/assembly_editor.h
index 35022c99..192f9327 100644
--- a/src/app/editor/code/assembly_editor.h
+++ b/src/app/editor/code/assembly_editor.h
@@ -1,15 +1,11 @@
#ifndef YAZE_APP_EDITOR_ASSEMBLY_EDITOR_H
#define YAZE_APP_EDITOR_ASSEMBLY_EDITOR_H
-#include "ImGuiColorTextEdit/TextEditor.h"
-#include "ImGuiFileDialog/ImGuiFileDialog.h"
-
-#include
-#include
#include
+#include "ImGuiColorTextEdit/TextEditor.h"
#include "app/core/common.h"
-#include "app/editor/utils/editor.h"
+#include "app/editor/editor.h"
#include "app/gui/style.h"
namespace yaze {
diff --git a/src/app/editor/code/memory_editor.h b/src/app/editor/code/memory_editor.h
index 7b49cbea..01ecc981 100644
--- a/src/app/editor/code/memory_editor.h
+++ b/src/app/editor/code/memory_editor.h
@@ -1,27 +1,20 @@
#ifndef YAZE_APP_EDITOR_CODE_MEMORY_EDITOR_H
#define YAZE_APP_EDITOR_CODE_MEMORY_EDITOR_H
-#include "imgui/imgui.h"
-#include "imgui/misc/cpp/imgui_stdlib.h"
-#include "imgui_memory_editor.h"
-
#include "absl/status/status.h"
-#include "app/core/common.h"
#include "app/core/constants.h"
#include "app/core/platform/file_dialog.h"
#include "app/core/project.h"
#include "app/editor/code/assembly_editor.h"
#include "app/editor/code/memory_editor.h"
#include "app/editor/dungeon/dungeon_editor.h"
+#include "app/editor/editor.h"
#include "app/editor/graphics/graphics_editor.h"
#include "app/editor/graphics/palette_editor.h"
#include "app/editor/graphics/screen_editor.h"
#include "app/editor/music/music_editor.h"
-#include "app/editor/overworld_editor.h"
+#include "app/editor/overworld/overworld_editor.h"
#include "app/editor/sprite/sprite_editor.h"
-#include "app/editor/utils/editor.h"
-#include "app/editor/utils/gfx_context.h"
-#include "app/editor/utils/recent_files.h"
#include "app/emu/emulator.h"
#include "app/gfx/snes_palette.h"
#include "app/gfx/snes_tile.h"
@@ -30,6 +23,9 @@
#include "app/gui/input.h"
#include "app/gui/style.h"
#include "app/rom.h"
+#include "imgui/imgui.h"
+#include "imgui/misc/cpp/imgui_stdlib.h"
+#include "imgui_memory_editor.h"
namespace yaze {
namespace app {
@@ -46,7 +42,7 @@ struct MemoryEditorWithDiffChecker : public SharedRom {
static Rom comparison_rom;
ImGui::Begin("Hex Editor", &show_memory_editor);
if (ImGui::Button("Compare Rom")) {
- auto file_name = FileDialogWrapper::ShowOpenFileDialog();
+ auto file_name = core::FileDialogWrapper::ShowOpenFileDialog();
PRINT_IF_ERROR(comparison_rom.LoadFromFile(file_name));
show_compare_rom = true;
}
diff --git a/src/app/editor/dungeon/dungeon_editor.cc b/src/app/editor/dungeon/dungeon_editor.cc
index c23e5d47..b6b42d3d 100644
--- a/src/app/editor/dungeon/dungeon_editor.cc
+++ b/src/app/editor/dungeon/dungeon_editor.cc
@@ -1,9 +1,7 @@
#include "dungeon_editor.h"
-#include "imgui/imgui.h"
-
-#include "app/core/common.h"
-#include "app/core/labeling.h"
+#include "absl/container/flat_hash_map.h"
+#include "app/core/platform/renderer.h"
#include "app/gfx/snes_palette.h"
#include "app/gui/canvas.h"
#include "app/gui/color.h"
@@ -11,13 +9,15 @@
#include "app/gui/input.h"
#include "app/rom.h"
#include "app/zelda3/dungeon/object_names.h"
-#include "app/zelda3/dungeon/room_names.h"
+#include "imgui/imgui.h"
#include "zelda3/dungeon/room.h"
namespace yaze {
namespace app {
namespace editor {
+using core::Renderer;
+
using ImGui::BeginChild;
using ImGui::BeginTabBar;
using ImGui::BeginTabItem;
@@ -50,21 +50,22 @@ absl::Status DungeonEditor::Update() {
refresh_graphics_ = false;
}
- TAB_BAR("##DungeonEditorTabBar")
- TAB_ITEM("Room Editor")
- status_ = UpdateDungeonRoomView();
- END_TAB_ITEM()
- TAB_ITEM("Usage Statistics")
- if (is_loaded_) {
- static bool calc_stats = false;
- if (!calc_stats) {
- CalculateUsageStats();
- calc_stats = true;
+ if (ImGui::BeginTabBar("##DungeonEditorTabBar")) {
+ TAB_ITEM("Room Editor")
+ status_ = UpdateDungeonRoomView();
+ END_TAB_ITEM()
+ TAB_ITEM("Usage Statistics")
+ if (is_loaded_) {
+ static bool calc_stats = false;
+ if (!calc_stats) {
+ CalculateUsageStats();
+ calc_stats = true;
+ }
+ DrawUsageStats();
}
- DrawUsageStats();
+ END_TAB_ITEM()
+ ImGui::EndTabBar();
}
- END_TAB_ITEM()
- END_TAB_BAR()
return absl::OkStatus();
}
@@ -72,7 +73,7 @@ absl::Status DungeonEditor::Update() {
absl::Status DungeonEditor::Initialize() {
auto dungeon_man_pal_group = rom()->palette_group().dungeon_main;
for (int i = 0; i < 0x100 + 40; i++) {
- rooms_.emplace_back(zelda3::dungeon::Room(i));
+ rooms_.emplace_back(zelda3::dungeon::Room(/*room_id=*/i));
rooms_[i].LoadHeader();
rooms_[i].LoadRoomFromROM();
if (flags()->kDrawDungeonRoomGraphics) {
@@ -107,7 +108,7 @@ absl::Status DungeonEditor::Initialize() {
ASSIGN_OR_RETURN(current_palette_group_,
gfx::CreatePaletteGroupFromLargePalette(full_palette_));
- graphics_bin_ = *rom()->mutable_bitmap_manager();
+ graphics_bin_ = rom()->gfx_sheets();
// Create a vector of pointers to the current block bitmaps
for (int block : rooms_[current_room_id_].blocks()) {
room_gfx_sheets_.emplace_back(&graphics_bin_[block]);
@@ -120,14 +121,14 @@ absl::Status DungeonEditor::RefreshGraphics() {
int block = rooms_[current_room_id_].blocks()[i];
RETURN_IF_ERROR(graphics_bin_[block].ApplyPaletteWithTransparent(
current_palette_group_[current_palette_id_], 0));
- rom()->UpdateBitmap(&graphics_bin_[block], true);
+ Renderer::GetInstance().UpdateBitmap(&graphics_bin_[block]);
}
auto sprites_aux1_pal_group = rom()->palette_group().sprites_aux1;
for (int i = 9; i < 16; i++) {
int block = rooms_[current_room_id_].blocks()[i];
RETURN_IF_ERROR(graphics_bin_[block].ApplyPaletteWithTransparent(
sprites_aux1_pal_group[current_palette_id_], 0));
- rom()->UpdateBitmap(&graphics_bin_[block], true);
+ Renderer::GetInstance().UpdateBitmap(&graphics_bin_[block]);
}
return absl::OkStatus();
}
@@ -195,14 +196,15 @@ absl::Status DungeonEditor::UpdateDungeonRoomView() {
TableNextRow();
TableNextColumn();
- TAB_BAR("##DungeonRoomTabBar");
- TAB_ITEM("Rooms");
- DrawRoomSelector();
- END_TAB_ITEM();
- TAB_ITEM("Entrances");
- DrawEntranceSelector();
- END_TAB_ITEM();
- END_TAB_BAR();
+ if (ImGui::BeginTabBar("##DungeonRoomTabBar")) {
+ TAB_ITEM("Rooms");
+ DrawRoomSelector();
+ END_TAB_ITEM();
+ TAB_ITEM("Entrances");
+ DrawEntranceSelector();
+ END_TAB_ITEM();
+ ImGui::EndTabBar();
+ }
TableNextColumn();
DrawDungeonTabView();
@@ -320,7 +322,7 @@ void DungeonEditor::DrawRoomSelector() {
for (const auto each_room_name : zelda3::dungeon::kRoomNames) {
rom()->resource_label()->SelectableLabelWithNameEdit(
current_room_id_ == i, "Dungeon Room Names",
- core::UppercaseHexByte(i), zelda3::dungeon::kRoomNames[i].data());
+ core::UppercaseHexByte(i), each_room_name.data());
if (ImGui::IsItemClicked()) {
// TODO: Jump to tab if room is already open
current_room_id_ = i;
@@ -498,7 +500,7 @@ void DungeonEditor::DrawRoomGraphics() {
top_left_y = room_gfx_canvas_.zero_point().y + height * current_block;
}
room_gfx_canvas_.draw_list()->AddImage(
- (void*)graphics_bin_[block].texture(),
+ (ImTextureID)(intptr_t)graphics_bin_[block].texture(),
ImVec2(room_gfx_canvas_.zero_point().x + 2, top_left_y),
ImVec2(room_gfx_canvas_.zero_point().x + 0x100,
room_gfx_canvas_.zero_point().y + offset));
@@ -547,7 +549,7 @@ void DungeonEditor::DrawObjectRenderer() {
current_object_ = i;
object_renderer_.LoadObject(i,
rooms_[current_room_id_].mutable_blocks());
- rom()->RenderBitmap(object_renderer_.bitmap());
+ Renderer::GetInstance().RenderBitmap(object_renderer_.bitmap());
object_loaded_ = true;
}
i += 1;
@@ -827,4 +829,4 @@ void DungeonEditor::DrawUsageGrid() {
} // namespace editor
} // namespace app
-} // namespace yaze
\ No newline at end of file
+} // namespace yaze
diff --git a/src/app/editor/dungeon/dungeon_editor.h b/src/app/editor/dungeon/dungeon_editor.h
index fdf52529..4a8aea3d 100644
--- a/src/app/editor/dungeon/dungeon_editor.h
+++ b/src/app/editor/dungeon/dungeon_editor.h
@@ -1,16 +1,14 @@
#ifndef YAZE_APP_EDITOR_DUNGEONEDITOR_H
#define YAZE_APP_EDITOR_DUNGEONEDITOR_H
-#include "imgui/imgui.h"
-
#include "app/core/common.h"
-#include "app/core/labeling.h"
+#include "absl/container/flat_hash_map.h"
#include "app/editor/graphics/gfx_group_editor.h"
#include "app/editor/graphics/palette_editor.h"
-#include "app/editor/utils/editor.h"
+#include "app/editor/editor.h"
#include "app/gui/canvas.h"
-#include "app/gui/icons.h"
#include "app/rom.h"
+#include "imgui/imgui.h"
#include "zelda3/dungeon/room.h"
#include "zelda3/dungeon/room_entrance.h"
#include "zelda3/dungeon/room_object.h"
@@ -100,7 +98,6 @@ class DungeonEditor : public Editor,
bool object_loaded_ = false;
bool palette_showing_ = false;
bool refresh_graphics_ = false;
- bool show_object_render_ = false;
uint16_t current_entrance_id_ = 0;
uint16_t current_room_id_ = 0;
@@ -120,12 +117,11 @@ class DungeonEditor : public Editor,
gui::Canvas object_canvas_;
gfx::Bitmap room_gfx_bmp_;
- gfx::BitmapManager graphics_bin_;
+ std::array graphics_bin_;
std::vector room_gfx_sheets_;
std::vector rooms_;
std::vector entrances_;
- std::vector room_graphics_;
zelda3::dungeon::DungeonObjectRenderer object_renderer_;
absl::flat_hash_map spriteset_usage_;
diff --git a/src/app/editor/editor.cc b/src/app/editor/editor.cc
new file mode 100644
index 00000000..44055a1b
--- /dev/null
+++ b/src/app/editor/editor.cc
@@ -0,0 +1,52 @@
+#include "editor.h"
+
+#include "app/core/constants.h"
+#include "imgui/imgui.h"
+
+namespace yaze {
+namespace app {
+namespace editor {
+
+absl::Status DrawEditor(EditorLayoutParams *params) {
+ if (params->editor == nullptr) {
+ return absl::InternalError("Editor is not initialized");
+ }
+
+ // Draw the editors based on h_split and v_split in a recursive manner
+ if (params->v_split) {
+ ImGui::BeginTable("##VerticalSplitTable", 2);
+
+ ImGui::TableNextColumn();
+ if (params->left)
+ RETURN_IF_ERROR(DrawEditor(params->left));
+
+ ImGui::TableNextColumn();
+ if (params->right)
+ RETURN_IF_ERROR(DrawEditor(params->right));
+
+ ImGui::EndTable();
+ } else if (params->h_split) {
+ ImGui::BeginTable("##HorizontalSplitTable", 1);
+
+ ImGui::TableNextColumn();
+ if (params->top)
+ RETURN_IF_ERROR(DrawEditor(params->top));
+
+ ImGui::TableNextColumn();
+ if (params->bottom)
+ RETURN_IF_ERROR(DrawEditor(params->bottom));
+
+ ImGui::EndTable();
+ } else {
+ // No split, just draw the single editor
+ ImGui::Text("%s Editor",
+ kEditorNames[static_cast(params->editor->type())]);
+ RETURN_IF_ERROR(params->editor->Update());
+ }
+
+ return absl::OkStatus();
+}
+
+} // namespace editor
+} // namespace app
+} // namespace yaze
diff --git a/src/app/editor/CMakeLists.txt b/src/app/editor/editor.cmake
similarity index 63%
rename from src/app/editor/CMakeLists.txt
rename to src/app/editor/editor.cmake
index 30b76767..2a0516f3 100644
--- a/src/app/editor/CMakeLists.txt
+++ b/src/app/editor/editor.cmake
@@ -1,21 +1,21 @@
set(
YAZE_APP_EDITOR_SRC
+ app/editor/editor.cc
+ app/editor/editor_manager.cc
app/editor/dungeon/dungeon_editor.cc
- app/editor/master_editor.cc
- app/editor/master_editor_test.cc
- app/editor/settings_editor.cc
- app/editor/overworld_editor.cc
+ app/editor/overworld/overworld_editor.cc
app/editor/sprite/sprite_editor.cc
app/editor/music/music_editor.cc
app/editor/message/message_editor.cc
- app/editor/message/message_editor_test.cc
+ app/editor/message/message_data.cc
app/editor/code/assembly_editor.cc
app/editor/graphics/screen_editor.cc
app/editor/graphics/graphics_editor.cc
app/editor/graphics/palette_editor.cc
app/editor/graphics/tile16_editor.cc
app/editor/graphics/gfx_group_editor.cc
- app/editor/utils/gfx_context.cc
- app/editor/overworld/refresh.cc
app/editor/overworld/entity.cc
-)
\ No newline at end of file
+ app/editor/system/settings_editor.cc
+ app/editor/system/command_manager.cc
+ app/editor/system/extension_manager.cc
+)
diff --git a/src/app/editor/utils/editor.h b/src/app/editor/editor.h
similarity index 50%
rename from src/app/editor/utils/editor.h
rename to src/app/editor/editor.h
index 872388ed..685b487b 100644
--- a/src/app/editor/utils/editor.h
+++ b/src/app/editor/editor.h
@@ -1,7 +1,14 @@
#ifndef YAZE_APP_CORE_EDITOR_H
#define YAZE_APP_CORE_EDITOR_H
+#include
+
#include "absl/status/status.h"
+#include "app/editor/system/command_manager.h"
+#include "app/editor/system/constant_manager.h"
+#include "app/editor/system/extension_manager.h"
+#include "app/editor/system/history_manager.h"
+#include "app/editor/system/resource_manager.h"
namespace yaze {
namespace app {
@@ -12,6 +19,14 @@ namespace app {
*/
namespace editor {
+struct EditorContext {
+ ConstantManager constant_manager;
+ CommandManager command_manager;
+ ExtensionManager extension_manager;
+ HistoryManager history_manager;
+ ResourceManager resource_manager;
+};
+
enum class EditorType {
kAssembly,
kDungeon,
@@ -25,7 +40,7 @@ enum class EditorType {
kSettings,
};
-constexpr std::array kEditorNames = {
+constexpr std::array kEditorNames = {
"Assembly", "Dungeon", "Graphics", "Music", "Overworld",
"Palette", "Screen", "Sprite", "Message", "Settings",
};
@@ -56,10 +71,35 @@ class Editor {
protected:
EditorType type_;
+ EditorContext context_;
};
+/**
+ * @brief Dynamic Editor Layout Parameters
+ */
+typedef struct EditorLayoutParams {
+ bool v_split;
+ bool h_split;
+ int v_split_pos;
+ int h_split_pos;
+ Editor *editor = nullptr;
+ EditorLayoutParams *left = nullptr;
+ EditorLayoutParams *right = nullptr;
+ EditorLayoutParams *top = nullptr;
+ EditorLayoutParams *bottom = nullptr;
+
+ EditorLayoutParams() {
+ v_split = false;
+ h_split = false;
+ v_split_pos = 0;
+ h_split_pos = 0;
+ }
+} EditorLayoutParams;
+
+absl::Status DrawEditor(EditorLayoutParams *params);
+
} // namespace editor
} // namespace app
} // namespace yaze
-#endif // YAZE_APP_CORE_EDITOR_H
\ No newline at end of file
+#endif // YAZE_APP_CORE_EDITOR_H
diff --git a/src/app/editor/master_editor.cc b/src/app/editor/editor_manager.cc
similarity index 79%
rename from src/app/editor/master_editor.cc
rename to src/app/editor/editor_manager.cc
index 94bce89d..c1e0eb80 100644
--- a/src/app/editor/master_editor.cc
+++ b/src/app/editor/editor_manager.cc
@@ -1,48 +1,39 @@
-#include "master_editor.h"
-
-#include "ImGuiColorTextEdit/TextEditor.h"
-#include "ImGuiFileDialog/ImGuiFileDialog.h"
-#include "abseil-cpp/absl/strings/match.h"
-#include "imgui/backends/imgui_impl_sdl2.h"
-#include "imgui/backends/imgui_impl_sdlrenderer2.h"
-#include "imgui/imgui.h"
-#include "imgui/misc/cpp/imgui_stdlib.h"
-#include "imgui_internal.h"
-#include "imgui_memory_editor.h"
+#include "editor_manager.h"
#include "absl/status/status.h"
-#include "app/core/common.h"
+#include "absl/strings/match.h"
#include "app/core/constants.h"
#include "app/core/platform/file_dialog.h"
+#include "app/core/project.h"
#include "app/editor/code/assembly_editor.h"
#include "app/editor/dungeon/dungeon_editor.h"
#include "app/editor/graphics/graphics_editor.h"
#include "app/editor/graphics/palette_editor.h"
#include "app/editor/graphics/screen_editor.h"
#include "app/editor/music/music_editor.h"
-#include "app/editor/overworld_editor.h"
+#include "app/editor/overworld/overworld_editor.h"
#include "app/editor/sprite/sprite_editor.h"
-#include "app/editor/utils/flags.h"
-#include "app/editor/utils/recent_files.h"
+#include "app/editor/system/flags.h"
#include "app/emu/emulator.h"
-#include "app/gfx/snes_palette.h"
-#include "app/gfx/snes_tile.h"
-#include "app/gui/canvas.h"
#include "app/gui/icons.h"
#include "app/gui/input.h"
#include "app/gui/style.h"
#include "app/rom.h"
+#include "editor/editor.h"
+#include "imgui/imgui.h"
+#include "imgui/misc/cpp/imgui_stdlib.h"
namespace yaze {
namespace app {
namespace editor {
using namespace ImGui;
+using core::FileDialogWrapper;
namespace {
-
-bool BeginCentered(const char *name) {
- ImGuiIO const &io = GetIO();
+
+bool BeginCentered(const char* name) {
+ ImGuiIO const& io = GetIO();
ImVec2 pos(io.DisplaySize.x * 0.5f, io.DisplaySize.y * 0.5f);
SetNextWindowPos(pos, ImGuiCond_Always, ImVec2(0.5f, 0.5f));
ImGuiWindowFlags flags =
@@ -58,21 +49,18 @@ bool IsEditorActive(Editor* editor, std::vector& active_editors) {
} // namespace
-void MasterEditor::SetupScreen(std::shared_ptr renderer,
- std::string filename) {
- sdl_renderer_ = renderer;
- rom()->SetupRenderer(renderer);
+void EditorManager::SetupScreen(std::string filename) {
if (!filename.empty()) {
PRINT_IF_ERROR(rom()->LoadFromFile(filename));
}
overworld_editor_.InitializeZeml();
+ InitializeCommands();
}
-absl::Status MasterEditor::Update() {
+absl::Status EditorManager::Update() {
ManageKeyboardShortcuts();
DrawYazeMenu();
- DrawFileDialog();
DrawStatusPopup();
DrawAboutPopup();
DrawInfoPopup();
@@ -85,12 +73,15 @@ absl::Status MasterEditor::Update() {
rom_assets_loaded_ = true;
}
- ManageActiveEditors();
+ if (dynamic_layout_)
+ RETURN_IF_ERROR(DrawDynamicLayout())
+ else
+ ManageActiveEditors();
return absl::OkStatus();
}
-void MasterEditor::ManageActiveEditors() {
+void EditorManager::ManageActiveEditors() {
// Show popup pane to select an editor to add
static bool show_add_editor = false;
if (show_add_editor) OpenPopup("AddEditor");
@@ -253,16 +244,23 @@ void MasterEditor::ManageActiveEditors() {
}
}
-void MasterEditor::ManageKeyboardShortcuts() {
+absl::Status EditorManager::DrawDynamicLayout() {
+ // Dynamic layout for multiple editors to be open at once
+ // Allows for tiling and resizing of editors using ImGui
+ return DrawEditor(&root_layout_);
+}
+
+void EditorManager::ManageKeyboardShortcuts() {
bool ctrl_or_super = (GetIO().KeyCtrl || GetIO().KeySuper);
+ editor_context_.command_manager.ShowWhichKey();
+
// If CMD + R is pressed, reload the top result of recent files
if (IsKeyDown(ImGuiKey_R) && ctrl_or_super) {
static RecentFilesManager manager("recent_files.txt");
manager.Load();
if (!manager.GetRecentFiles().empty()) {
auto front = manager.GetRecentFiles().front();
- std::cout << "Reloading: " << front << std::endl;
OpenRomOrProject(front);
}
}
@@ -311,55 +309,113 @@ void MasterEditor::ManageKeyboardShortcuts() {
}
}
-void MasterEditor::DrawFileDialog() {
- gui::FileDialogPipeline("ChooseFileDlgKey", ".sfc,.smc", std::nullopt, [&]() {
- std::string filePathName = ImGuiFileDialog::Instance()->GetFilePathName();
- status_ = rom()->LoadFromFile(filePathName);
- static RecentFilesManager manager("recent_files.txt");
+void EditorManager::InitializeCommands() {
+ if (root_layout_.editor == nullptr) {
+ root_layout_.editor = &overworld_editor_;
+ }
- // Load existing recent files
- manager.Load();
+ // New editor popup for window management commands
+ static EditorLayoutParams new_layout;
+ if (ImGui::BeginPopup("NewEditor")) {
+ ImGui::Text("New Editor");
+ ImGui::Separator();
+ if (ImGui::Button("Overworld")) {
+ new_layout.editor = &overworld_editor_;
+ ImGui::CloseCurrentPopup();
+ }
+ if (ImGui::Button("Dungeon")) {
+ new_layout.editor = &dungeon_editor_;
+ ImGui::CloseCurrentPopup();
+ }
+ if (ImGui::Button("Graphics")) {
+ new_layout.editor = &graphics_editor_;
+ ImGui::CloseCurrentPopup();
+ }
+ if (ImGui::Button("Music")) {
+ new_layout.editor = &music_editor_;
+ ImGui::CloseCurrentPopup();
+ }
+ if (ImGui::Button("Palette")) {
+ new_layout.editor = &palette_editor_;
+ ImGui::CloseCurrentPopup();
+ }
+ if (ImGui::Button("Screen")) {
+ new_layout.editor = &screen_editor_;
+ ImGui::CloseCurrentPopup();
+ }
+ if (ImGui::Button("Sprite")) {
+ new_layout.editor = &sprite_editor_;
+ ImGui::CloseCurrentPopup();
+ }
+ if (ImGui::Button("Code")) {
+ new_layout.editor = &assembly_editor_;
+ ImGui::CloseCurrentPopup();
+ }
+ if (ImGui::Button("Settings")) {
+ new_layout.editor = &settings_editor_;
+ ImGui::CloseCurrentPopup();
+ }
+ if (ImGui::Button("Message")) {
+ new_layout.editor = &message_editor_;
+ ImGui::CloseCurrentPopup();
+ }
+ ImGui::EndPopup();
+ }
- // Add a new file
- manager.AddFile(filePathName);
-
- // Save the updated list
- manager.Save();
- });
+ editor_context_.command_manager.RegisterPrefix("window", 'w',
+ "window management", "");
+ editor_context_.command_manager.RegisterSubcommand(
+ "window", "vsplit", '/', "vertical split",
+ "split windows vertically and place editor in new window", [this]() {
+ ImGui::OpenPopup("NewEditor");
+ root_layout_.v_split = true;
+ });
+ editor_context_.command_manager.RegisterSubcommand(
+ "window", "hsplit", '-', "horizontal split",
+ "split windows horizontally and place editor in new window", [this]() {
+ ImGui::OpenPopup("NewEditor");
+ root_layout_.h_split = true;
+ });
+ editor_context_.command_manager.RegisterSubcommand(
+ "window", "close", 'd', "close", "close the current editor", [this]() {
+ if (root_layout_.editor != nullptr) {
+ root_layout_.editor = nullptr;
+ }
+ });
}
-void MasterEditor::DrawStatusPopup() {
+void EditorManager::DrawStatusPopup() {
+ static absl::Status prev_status;
if (!status_.ok()) {
show_status_ = true;
- prev_status_ = status_;
+ prev_status = status_;
}
if (show_status_ && (BeginCentered("StatusWindow"))) {
Text("%s", ICON_MD_ERROR);
- Text("%s", prev_status_.ToString().c_str());
+ Text("%s", prev_status.ToString().c_str());
Spacing();
NextColumn();
Columns(1);
Separator();
NewLine();
SameLine(128);
- if (Button("OK", gui::kDefaultModalSize) ||
- IsKeyPressed(GetKeyIndex(ImGuiKey_Space))) {
+ if (Button("OK", gui::kDefaultModalSize) || IsKeyPressed(ImGuiKey_Space)) {
show_status_ = false;
status_ = absl::OkStatus();
}
SameLine();
if (Button(ICON_MD_CONTENT_COPY, ImVec2(50, 0))) {
- SetClipboardText(prev_status_.ToString().c_str());
+ SetClipboardText(prev_status.ToString().c_str());
}
End();
}
}
-void MasterEditor::DrawAboutPopup() {
+void EditorManager::DrawAboutPopup() {
if (about_) OpenPopup("About");
if (BeginPopupModal("About", nullptr, ImGuiWindowFlags_AlwaysAutoResize)) {
- Text("Yet Another Zelda3 Editor - v%.2f", core::kYazeVersion);
+ Text("Yet Another Zelda3 Editor - v%s", core::kYazeVersion.data());
Text("Written by: scawful");
Spacing();
Text("Special Thanks: Zarby89, JaredBrian");
@@ -373,15 +429,15 @@ void MasterEditor::DrawAboutPopup() {
}
}
-void MasterEditor::DrawInfoPopup() {
+void EditorManager::DrawInfoPopup() {
if (rom_info_) OpenPopup("ROM Information");
if (BeginPopupModal("ROM Information", nullptr,
ImGuiWindowFlags_AlwaysAutoResize)) {
- Text("Title: %s", rom()->title());
- Text("ROM Size: %ld", rom()->size());
+ Text("Title: %s", rom()->title().c_str());
+ Text("ROM Size: %s", core::UppercaseHexLongLong(rom()->size()).c_str());
if (Button("Close", gui::kDefaultModalSize) ||
- IsKeyPressed(GetKeyIndex(ImGuiKey_Space))) {
+ IsKeyPressed(ImGuiKey_Escape)) {
rom_info_ = false;
CloseCurrentPopup();
}
@@ -389,33 +445,19 @@ void MasterEditor::DrawInfoPopup() {
}
}
-void MasterEditor::DrawYazeMenu() {
+void EditorManager::DrawYazeMenu() {
static bool show_display_settings = false;
- static bool show_command_line_interface = false;
if (BeginMenuBar()) {
- DrawFileMenu();
- DrawEditMenu();
- DrawViewMenu();
- DrawTestMenu();
- DrawProjectMenu();
- DrawHelpMenu();
-
+ DrawYazeMenuBar();
SameLine(GetWindowWidth() - GetStyle().ItemSpacing.x -
- CalcTextSize(ICON_MD_DISPLAY_SETTINGS).x - 150);
- // Modify the style of the button to have no background color
+ CalcTextSize(ICON_MD_DISPLAY_SETTINGS).x - 110);
PushStyleColor(ImGuiCol_Button, ImVec4(0, 0, 0, 0));
if (Button(ICON_MD_DISPLAY_SETTINGS)) {
show_display_settings = !show_display_settings;
}
-
- if (Button(ICON_MD_TERMINAL)) {
- show_command_line_interface = !show_command_line_interface;
- }
PopStyleColor();
-
- Text("%s", absl::StrCat("yaze v", core::kYazeVersion).c_str());
-
+ Text("yaze v%s", core::kYazeVersion.data());
EndMenuBar();
}
@@ -424,27 +466,9 @@ void MasterEditor::DrawYazeMenu() {
gui::DrawDisplaySettings();
End();
}
-
- if (show_command_line_interface) {
- Begin("Command Line Interface", &show_command_line_interface,
- ImGuiWindowFlags_None);
- Text("Enter a command:");
- End();
- }
}
-void MasterEditor::OpenRomOrProject(const std::string& filename) {
- if (absl::StrContains(filename, ".yaze")) {
- status_ = current_project_.Open(filename);
- if (status_.ok()) {
- status_ = OpenProject();
- }
- } else {
- status_ = rom()->LoadFromFile(filename);
- }
-}
-
-void MasterEditor::DrawFileMenu() {
+void EditorManager::DrawYazeMenuBar() {
static bool save_as_menu = false;
static bool new_project_menu = false;
@@ -476,7 +500,6 @@ void MasterEditor::DrawFileMenu() {
MENU_ITEM("Save As..") { save_as_menu = true; }
if (rom()->is_loaded()) {
- MENU_ITEM("Reload") { status_ = rom()->Reload(); }
MENU_ITEM("Close") {
status_ = rom()->Close();
rom_assets_loaded_ = false;
@@ -492,8 +515,8 @@ void MasterEditor::DrawFileMenu() {
}
if (MenuItem("Open Project")) {
// Open an existing project
- status_ =
- current_project_.Open(FileDialogWrapper::ShowOpenFileDialog());
+ status_ = current_project_.Open(
+ core::FileDialogWrapper::ShowOpenFileDialog());
if (status_.ok()) {
status_ = OpenProject();
}
@@ -582,9 +605,7 @@ void MasterEditor::DrawFileMenu() {
}
End();
}
-}
-void MasterEditor::DrawEditMenu() {
if (BeginMenu("Edit")) {
MENU_ITEM2("Undo", "Ctrl+Z") { status_ = current_editor_->Undo(); }
MENU_ITEM2("Redo", "Ctrl+Y") { status_ = current_editor_->Redo(); }
@@ -593,12 +614,10 @@ void MasterEditor::DrawEditMenu() {
MENU_ITEM2("Copy", "Ctrl+C") { status_ = current_editor_->Copy(); }
MENU_ITEM2("Paste", "Ctrl+V") { status_ = current_editor_->Paste(); }
Separator();
- MENU_ITEM2("Find", "Ctrl+F") {}
+ MENU_ITEM2("Find", "Ctrl+F") { status_ = current_editor_->Find(); }
EndMenu();
}
-}
-void MasterEditor::DrawViewMenu() {
static bool show_imgui_metrics = false;
static bool show_memory_editor = false;
static bool show_asm_editor = false;
@@ -606,28 +625,17 @@ void MasterEditor::DrawViewMenu() {
static bool show_palette_editor = false;
static bool show_emulator = false;
+ if (show_imgui_demo) ShowDemoWindow();
+ if (show_imgui_metrics) ShowMetricsWindow(&show_imgui_metrics);
+ if (show_memory_editor) memory_editor_.Update(show_memory_editor);
+ if (show_asm_editor) assembly_editor_.Update(show_asm_editor);
+
if (show_emulator) {
Begin("Emulator", &show_emulator, ImGuiWindowFlags_MenuBar);
emulator_.Run();
End();
}
- if (show_imgui_metrics) {
- ShowMetricsWindow(&show_imgui_metrics);
- }
-
- if (show_memory_editor) {
- memory_editor_.Update(show_memory_editor);
- }
-
- if (show_imgui_demo) {
- ShowDemoWindow();
- }
-
- if (show_asm_editor) {
- assembly_editor_.Update(show_asm_editor);
- }
-
if (show_palette_editor) {
Begin("Palette Editor", &show_palette_editor);
status_ = palette_editor_.Update();
@@ -635,6 +643,7 @@ void MasterEditor::DrawViewMenu() {
}
if (BeginMenu("View")) {
+ MenuItem("Dynamic Layout", nullptr, &dynamic_layout_);
MenuItem("Emulator", nullptr, &show_emulator);
Separator();
MenuItem("Memory Editor", nullptr, &show_memory_editor);
@@ -647,23 +656,9 @@ void MasterEditor::DrawViewMenu() {
MenuItem("ImGui Metrics", nullptr, &show_imgui_metrics);
EndMenu();
}
-}
-void MasterEditor::DrawTestMenu() {
- static bool show_tests_ = false;
-
- if (BeginMenu("Tests")) {
- MenuItem("Run Tests", nullptr, &show_tests_);
- EndMenu();
- }
-
-}
-
-void MasterEditor::DrawProjectMenu() {
static bool show_resource_label_manager = false;
-
if (current_project_.project_opened_) {
- // Project Menu
if (BeginMenu("Project")) {
Text("Name: %s", current_project_.name.c_str());
Text("ROM: %s", current_project_.rom_filename_.c_str());
@@ -674,16 +669,7 @@ void MasterEditor::DrawProjectMenu() {
EndMenu();
}
}
- if (show_resource_label_manager) {
- rom()->resource_label()->DisplayLabels(&show_resource_label_manager);
- if (current_project_.project_opened_ &&
- !current_project_.labels_filename_.empty()) {
- current_project_.labels_filename_ = rom()->resource_label()->filename_;
- }
- }
-}
-void MasterEditor::DrawHelpMenu() {
static bool open_rom_help = false;
static bool open_supported_features = false;
static bool open_manage_project = false;
@@ -752,11 +738,10 @@ void MasterEditor::DrawHelpMenu() {
Text("Project Menu");
Text("Create a new project or open an existing one.");
Text("Save the project to save the current state of the project.");
- Text(
+ TextWrapped(
"To save a project, you need to first open a ROM and initialize your "
"code path and labels file. Label resource manager can be found in "
- "the "
- "View menu. Code path is set in the Code editor after opening a "
+ "the View menu. Code path is set in the Code editor after opening a "
"folder.");
if (Button("Close", gui::kDefaultModalSize)) {
@@ -765,58 +750,51 @@ void MasterEditor::DrawHelpMenu() {
}
EndPopup();
}
+
+ if (show_resource_label_manager) {
+ rom()->resource_label()->DisplayLabels(&show_resource_label_manager);
+ if (current_project_.project_opened_ &&
+ !current_project_.labels_filename_.empty()) {
+ current_project_.labels_filename_ = rom()->resource_label()->filename_;
+ }
+ }
}
-void MasterEditor::LoadRom() {
- if (flags()->kNewFileDialogWrapper) {
- auto file_name = FileDialogWrapper::ShowOpenFileDialog();
- PRINT_IF_ERROR(rom()->LoadFromFile(file_name));
+void EditorManager::LoadRom() {
+ auto file_name = FileDialogWrapper::ShowOpenFileDialog();
+ auto load_rom = rom()->LoadFromFile(file_name);
+ if (load_rom.ok()) {
static RecentFilesManager manager("recent_files.txt");
manager.Load();
manager.AddFile(file_name);
manager.Save();
- } else {
- ImGuiFileDialog::Instance()->OpenDialog("ChooseFileDlgKey", "Open ROM",
- ".sfc,.smc", ".");
}
}
-void MasterEditor::SaveRom() {
+void EditorManager::SaveRom() {
if (flags()->kSaveDungeonMaps) {
status_ = screen_editor_.SaveDungeonMaps();
RETURN_VOID_IF_ERROR(status_);
}
- if (flags()->overworld.kSaveOverworldMaps) {
- RETURN_VOID_IF_ERROR(
- status_ = overworld_editor_.overworld()->CreateTile32Tilemap());
- status_ = overworld_editor_.overworld()->SaveMap32Tiles();
- RETURN_VOID_IF_ERROR(status_);
- status_ = overworld_editor_.overworld()->SaveMap16Tiles();
- RETURN_VOID_IF_ERROR(status_);
- status_ = overworld_editor_.overworld()->SaveOverworldMaps();
- RETURN_VOID_IF_ERROR(status_);
- }
- if (flags()->overworld.kSaveOverworldEntrances) {
- status_ = overworld_editor_.overworld()->SaveEntrances();
- RETURN_VOID_IF_ERROR(status_);
- }
- if (flags()->overworld.kSaveOverworldExits) {
- status_ = overworld_editor_.overworld()->SaveExits();
- RETURN_VOID_IF_ERROR(status_);
- }
- if (flags()->overworld.kSaveOverworldItems) {
- status_ = overworld_editor_.overworld()->SaveItems();
- RETURN_VOID_IF_ERROR(status_);
- }
- if (flags()->overworld.kSaveOverworldProperties) {
- status_ = overworld_editor_.overworld()->SaveMapProperties();
- RETURN_VOID_IF_ERROR(status_);
- }
+
+ status_ = overworld_editor_.Save();
+ RETURN_VOID_IF_ERROR(status_);
status_ = rom()->SaveToFile(backup_rom_, save_new_auto_);
}
-absl::Status MasterEditor::OpenProject() {
+void EditorManager::OpenRomOrProject(const std::string& filename) {
+ if (absl::StrContains(filename, ".yaze")) {
+ status_ = current_project_.Open(filename);
+ if (status_.ok()) {
+ status_ = OpenProject();
+ }
+ } else {
+ status_ = rom()->LoadFromFile(filename);
+ }
+}
+
+absl::Status EditorManager::OpenProject() {
RETURN_IF_ERROR(rom()->LoadFromFile(current_project_.rom_filename_));
if (!rom()->resource_label()->LoadLabels(current_project_.labels_filename_)) {
diff --git a/src/app/editor/master_editor.h b/src/app/editor/editor_manager.h
similarity index 52%
rename from src/app/editor/master_editor.h
rename to src/app/editor/editor_manager.h
index cc5e495f..cdb23019 100644
--- a/src/app/editor/master_editor.h
+++ b/src/app/editor/editor_manager.h
@@ -1,17 +1,9 @@
-#ifndef YAZE_APP_EDITOR_MASTER_EDITOR_H
-#define YAZE_APP_EDITOR_MASTER_EDITOR_H
+#ifndef YAZE_APP_EDITOR_EDITOR_MANAGER_H
+#define YAZE_APP_EDITOR_EDITOR_MANAGER_H
#define IMGUI_DEFINE_MATH_OPERATORS
-#include "ImGuiColorTextEdit/TextEditor.h"
-#include "ImGuiFileDialog/ImGuiFileDialog.h"
-#include "imgui/imgui.h"
-#include "imgui/misc/cpp/imgui_stdlib.h"
-#include "imgui_memory_editor.h"
-
#include "absl/status/status.h"
-#include "app/core/common.h"
-#include "app/core/constants.h"
#include "app/core/project.h"
#include "app/editor/code/assembly_editor.h"
#include "app/editor/code/memory_editor.h"
@@ -21,15 +13,10 @@
#include "app/editor/graphics/screen_editor.h"
#include "app/editor/message/message_editor.h"
#include "app/editor/music/music_editor.h"
-#include "app/editor/overworld_editor.h"
-#include "app/editor/settings_editor.h"
+#include "app/editor/overworld/overworld_editor.h"
#include "app/editor/sprite/sprite_editor.h"
-#include "app/editor/utils/gfx_context.h"
+#include "app/editor/system/settings_editor.h"
#include "app/emu/emulator.h"
-#include "app/gfx/snes_palette.h"
-#include "app/gfx/snes_tile.h"
-#include "app/gui/canvas.h"
-#include "app/gui/icons.h"
#include "app/gui/input.h"
#include "app/rom.h"
@@ -38,28 +25,19 @@ namespace app {
namespace editor {
/**
- * @class MasterEditor
- * @brief The MasterEditor class represents the main editor for a Rom in the
- * Yaze application.
+ * @class EditorManager
+ * @brief The EditorManager controls the main editor window and manages the
+ * various editor classes.
*
- * This class inherits from SharedRom, GfxContext, and ExperimentFlags, and
- * provides functionality for setting up the screen, updating the editor, and
- * shutting down the editor. It also includes methods for drawing various menus
- * and popups, saving the Rom, and managing editor-specific flags.
- *
- * The MasterEditor class contains instances of various editor classes such as
+ * The EditorManager class contains instances of various editor classes such as
* AssemblyEditor, DungeonEditor, GraphicsEditor, MusicEditor, OverworldEditor,
* PaletteEditor, ScreenEditor, and SpriteEditor. The current_editor_ member
* variable points to the currently active editor in the tab view.
*
- * @note This class assumes the presence of an SDL_Renderer object for rendering
- * graphics.
*/
-class MasterEditor : public SharedRom,
- public context::GfxContext,
- public core::ExperimentFlags {
+class EditorManager : public SharedRom, public core::ExperimentFlags {
public:
- MasterEditor() {
+ EditorManager() {
current_editor_ = &overworld_editor_;
active_editors_.push_back(&overworld_editor_);
active_editors_.push_back(&dungeon_editor_);
@@ -67,37 +45,33 @@ class MasterEditor : public SharedRom,
active_editors_.push_back(&palette_editor_);
active_editors_.push_back(&sprite_editor_);
active_editors_.push_back(&message_editor_);
+ active_editors_.push_back(&screen_editor_);
}
- void SetupScreen(std::shared_ptr renderer,
- std::string filename = "");
+ void SetupScreen(std::string filename = "");
absl::Status Update();
- auto emulator() -> emu::Emulator& { return emulator_; }
+ auto emulator() -> emu::Emulator & { return emulator_; }
auto quit() { return quit_; }
- auto overworld_editor() -> OverworldEditor& { return overworld_editor_; }
private:
void ManageActiveEditors();
- void ManageKeyboardShortcuts();
- void OpenRomOrProject(const std::string& filename);
+ absl::Status DrawDynamicLayout();
+
+ void ManageKeyboardShortcuts();
+ void InitializeCommands();
- void DrawFileDialog();
void DrawStatusPopup();
void DrawAboutPopup();
void DrawInfoPopup();
void DrawYazeMenu();
- void DrawFileMenu();
- void DrawEditMenu();
- void DrawViewMenu();
- void DrawTestMenu();
- void DrawProjectMenu();
- void DrawHelpMenu();
+ void DrawYazeMenuBar();
void LoadRom();
void SaveRom();
+ void OpenRomOrProject(const std::string &filename);
absl::Status OpenProject();
bool quit_ = false;
@@ -107,16 +81,21 @@ class MasterEditor : public SharedRom,
bool save_new_auto_ = true;
bool show_status_ = false;
bool rom_assets_loaded_ = false;
+ bool dynamic_layout_ = false;
absl::Status status_;
- absl::Status prev_status_;
-
- std::shared_ptr sdl_renderer_;
emu::Emulator emulator_;
- Project current_project_;
+ std::vector active_editors_;
+ std::vector active_layouts_;
+ EditorLayoutParams root_layout_;
+
+ Project current_project_;
+ EditorContext editor_context_;
+
+ Editor *current_editor_ = nullptr;
AssemblyEditor assembly_editor_;
DungeonEditor dungeon_editor_;
GraphicsEditor graphics_editor_;
@@ -128,14 +107,10 @@ class MasterEditor : public SharedRom,
SettingsEditor settings_editor_;
MessageEditor message_editor_;
MemoryEditorWithDiffChecker memory_editor_;
-
- ImVector active_tabs_;
- std::vector active_editors_;
- Editor* current_editor_ = nullptr;
};
} // namespace editor
} // namespace app
} // namespace yaze
-#endif // YAZE_APP_EDITOR_MASTER_EDITOR_H
+#endif // YAZE_APP_EDITOR_EDITOR_MANAGER_H
diff --git a/src/app/editor/graphics/gfx_group_editor.cc b/src/app/editor/graphics/gfx_group_editor.cc
index c5905cf2..b3bd3c75 100644
--- a/src/app/editor/graphics/gfx_group_editor.cc
+++ b/src/app/editor/graphics/gfx_group_editor.cc
@@ -1,22 +1,14 @@
#include "gfx_group_editor.h"
-#include "imgui/imgui.h"
-
-#include
-
#include "absl/status/status.h"
-#include "absl/status/statusor.h"
-#include "app/editor/graphics/palette_editor.h"
-#include "app/editor/utils/editor.h"
+#include "absl/strings/str_cat.h"
#include "app/gfx/bitmap.h"
#include "app/gfx/snes_palette.h"
-#include "app/gfx/snes_tile.h"
#include "app/gui/canvas.h"
#include "app/gui/color.h"
-#include "app/gui/icons.h"
#include "app/gui/input.h"
#include "app/rom.h"
-#include "app/zelda3/overworld/overworld.h"
+#include "imgui/imgui.h"
namespace yaze {
namespace app {
@@ -27,7 +19,6 @@ using ImGui::BeginGroup;
using ImGui::BeginTabBar;
using ImGui::BeginTabItem;
using ImGui::BeginTable;
-using ImGui::ColorButton;
using ImGui::EndChild;
using ImGui::EndGroup;
using ImGui::EndTabBar;
@@ -39,7 +30,6 @@ using ImGui::IsItemClicked;
using ImGui::PopID;
using ImGui::PushID;
using ImGui::SameLine;
-using ImGui::Selectable;
using ImGui::Separator;
using ImGui::SetNextItemWidth;
using ImGui::TableHeadersRow;
@@ -123,7 +113,7 @@ void GfxGroupEditor::DrawBlocksetViewer(bool sheet_only) {
BeginGroup();
for (int i = 0; i < 8; i++) {
int sheet_id = rom()->main_blockset_ids[selected_blockset_][i];
- auto sheet = rom()->bitmap_manager()[sheet_id];
+ auto sheet = rom()->gfx_sheets().at(sheet_id);
gui::BitmapCanvasPipeline(blockset_canvas_, sheet, 256, 0x10 * 0x04,
0x20, true, false, 22);
}
@@ -176,7 +166,7 @@ void GfxGroupEditor::DrawRoomsetViewer() {
BeginGroup();
for (int i = 0; i < 4; i++) {
int sheet_id = rom()->room_blockset_ids[selected_roomset_][i];
- auto sheet = rom()->bitmap_manager()[sheet_id];
+ auto sheet = rom()->gfx_sheets().at(sheet_id);
gui::BitmapCanvasPipeline(roomset_canvas_, sheet, 256, 0x10 * 0x04,
0x20, true, false, 23);
}
@@ -214,7 +204,7 @@ void GfxGroupEditor::DrawSpritesetViewer(bool sheet_only) {
BeginGroup();
for (int i = 0; i < 4; i++) {
int sheet_id = rom()->spriteset_ids[selected_spriteset_][i];
- auto sheet = rom()->bitmap_manager()[115 + sheet_id];
+ auto sheet = rom()->gfx_sheets().at(115 + sheet_id);
gui::BitmapCanvasPipeline(spriteset_canvas_, sheet, 256, 0x10 * 0x04,
0x20, true, false, 24);
}
@@ -229,7 +219,7 @@ void DrawPaletteFromPaletteGroup(gfx::SnesPalette &palette) {
if (palette.empty()) {
return;
}
- for (int n = 0; n < palette.size(); n++) {
+ for (size_t n = 0; n < palette.size(); n++) {
PushID(n);
if ((n % 8) != 0) SameLine(0.0f, GetStyle().ItemSpacing.y);
diff --git a/src/app/editor/graphics/gfx_group_editor.h b/src/app/editor/graphics/gfx_group_editor.h
index fb4d8d40..14cd4c0c 100644
--- a/src/app/editor/graphics/gfx_group_editor.h
+++ b/src/app/editor/graphics/gfx_group_editor.h
@@ -1,21 +1,10 @@
#ifndef YAZE_APP_EDITOR_GFX_GROUP_EDITOR_H
#define YAZE_APP_EDITOR_GFX_GROUP_EDITOR_H
-#include "imgui/imgui.h"
-
-#include
-
#include "absl/status/status.h"
-#include "absl/status/statusor.h"
-#include "app/editor/utils/editor.h"
-#include "app/gfx/bitmap.h"
#include "app/gfx/snes_palette.h"
-#include "app/gfx/snes_tile.h"
#include "app/gui/canvas.h"
-#include "app/gui/icons.h"
-#include "app/gui/style.h"
#include "app/rom.h"
-#include "app/zelda3/overworld/overworld.h"
namespace yaze {
namespace app {
@@ -40,13 +29,7 @@ class GfxGroupEditor : public SharedRom {
selected_spriteset_ = spriteset;
}
- void InitBlockset(gfx::Bitmap* tile16_blockset) {
- tile16_blockset_bmp_ = tile16_blockset;
- }
-
private:
- int preview_palette_id_ = 0;
- int last_sheet_id_ = 0;
uint8_t selected_blockset_ = 0;
uint8_t selected_roomset_ = 0;
uint8_t selected_spriteset_ = 0;
@@ -57,17 +40,9 @@ class GfxGroupEditor : public SharedRom {
gui::Canvas spriteset_canvas_;
gfx::SnesPalette palette_;
- gfx::PaletteGroup palette_group_;
- gfx::Bitmap* tile16_blockset_bmp_;
-
- std::vector tile16_individual_data_;
- std::vector tile16_individual_;
-
- gui::BitmapViewer gfx_group_viewer_;
- zelda3::overworld::Overworld overworld_;
};
} // namespace editor
} // namespace app
} // namespace yaze
-#endif // YAZE_APP_EDITOR_GFX_GROUP_EDITOR_H
\ No newline at end of file
+#endif // YAZE_APP_EDITOR_GFX_GROUP_EDITOR_H
diff --git a/src/app/editor/graphics/graphics_editor.cc b/src/app/editor/graphics/graphics_editor.cc
index fe238599..d43a751b 100644
--- a/src/app/editor/graphics/graphics_editor.cc
+++ b/src/app/editor/graphics/graphics_editor.cc
@@ -1,30 +1,33 @@
#include "graphics_editor.h"
#include "ImGuiFileDialog/ImGuiFileDialog.h"
-#include "imgui/imgui.h"
-#include "imgui/misc/cpp/imgui_stdlib.h"
-#include "imgui_memory_editor.h"
-
#include "absl/status/status.h"
#include "absl/status/statusor.h"
#include "app/core/platform/clipboard.h"
+#include "app/core/platform/renderer.h"
#include "app/editor/graphics/palette_editor.h"
#include "app/gfx/bitmap.h"
#include "app/gfx/compression.h"
#include "app/gfx/scad_format.h"
#include "app/gfx/snes_palette.h"
#include "app/gfx/snes_tile.h"
-#include "app/gui/asset_browser.h"
#include "app/gui/canvas.h"
#include "app/gui/color.h"
+#include "app/gui/icons.h"
#include "app/gui/input.h"
+#include "app/gui/modules/asset_browser.h"
#include "app/gui/style.h"
#include "app/rom.h"
+#include "imgui/imgui.h"
+#include "imgui/misc/cpp/imgui_stdlib.h"
+#include "imgui_memory_editor.h"
namespace yaze {
namespace app {
namespace editor {
+using core::Renderer;
+
using gfx::kPaletteGroupAddressesKeys;
using ImGui::Button;
using ImGui::InputInt;
@@ -32,70 +35,56 @@ using ImGui::InputText;
using ImGui::SameLine;
using ImGui::TableNextColumn;
-static constexpr absl::string_view kGfxToolsetColumnNames[] = {
- "#memoryEditor",
- "##separator_gfx1",
-};
-
constexpr ImGuiTableFlags kGfxEditTableFlags =
ImGuiTableFlags_Borders | ImGuiTableFlags_Resizable |
ImGuiTableFlags_Reorderable | ImGuiTableFlags_Hideable |
ImGuiTableFlags_SizingFixedFit;
-constexpr ImGuiTabBarFlags kGfxEditTabBarFlags =
- ImGuiTabBarFlags_AutoSelectNewTabs | ImGuiTabBarFlags_Reorderable |
- ImGuiTabBarFlags_FittingPolicyResizeDown |
- ImGuiTabBarFlags_TabListPopupButton;
-
-constexpr ImGuiTableFlags kGfxEditFlags = ImGuiTableFlags_Reorderable |
- ImGuiTableFlags_Resizable |
- ImGuiTableFlags_SizingStretchSame;
-
absl::Status GraphicsEditor::Update() {
- TAB_BAR("##TabBar")
- status_ = UpdateGfxEdit();
- TAB_ITEM("Sheet Browser")
- if (asset_browser_.Initialized == false) {
- asset_browser_.Initialize(rom()->mutable_bitmap_manager());
+ if (ImGui::BeginTabBar("##TabBar")) {
+ status_ = UpdateGfxEdit();
+ TAB_ITEM("Sheet Browser")
+ if (asset_browser_.Initialized == false) {
+ asset_browser_.Initialize(rom()->gfx_sheets());
+ }
+ asset_browser_.Draw(rom()->gfx_sheets());
+ END_TAB_ITEM()
+ status_ = UpdateScadView();
+ status_ = UpdateLinkGfxView();
+ ImGui::EndTabBar();
}
- asset_browser_.Draw(rom()->mutable_bitmap_manager());
-
- END_TAB_ITEM()
- status_ = UpdateScadView();
- status_ = UpdateLinkGfxView();
- END_TAB_BAR()
CLEAR_AND_RETURN_STATUS(status_)
return absl::OkStatus();
}
absl::Status GraphicsEditor::UpdateGfxEdit() {
- TAB_ITEM("Sheet Editor")
+ if (ImGui::BeginTabItem("Sheet Editor")) {
+ if (ImGui::BeginTable("##GfxEditTable", 3, kGfxEditTableFlags,
+ ImVec2(0, 0))) {
+ for (const auto& name :
+ {"Tilesheets", "Current Graphics", "Palette Controls"})
+ ImGui::TableSetupColumn(name);
- if (ImGui::BeginTable("##GfxEditTable", 3, kGfxEditTableFlags,
- ImVec2(0, 0))) {
- for (const auto& name :
- {"Tilesheets", "Current Graphics", "Palette Controls"})
- ImGui::TableSetupColumn(name);
+ ImGui::TableHeadersRow();
- ImGui::TableHeadersRow();
+ NEXT_COLUMN();
+ status_ = UpdateGfxSheetList();
- NEXT_COLUMN();
- status_ = UpdateGfxSheetList();
+ NEXT_COLUMN();
+ if (rom()->is_loaded()) {
+ DrawGfxEditToolset();
+ status_ = UpdateGfxTabView();
+ }
- NEXT_COLUMN();
- if (rom()->is_loaded()) {
- DrawGfxEditToolset();
- status_ = UpdateGfxTabView();
+ NEXT_COLUMN();
+ if (rom()->is_loaded()) {
+ status_ = UpdatePaletteColumn();
+ }
}
+ ImGui::EndTable();
- NEXT_COLUMN();
- if (rom()->is_loaded()) {
- status_ = UpdatePaletteColumn();
- }
+ ImGui::EndTabItem();
}
- ImGui::EndTable();
-
- END_TAB_ITEM()
return absl::OkStatus();
}
@@ -127,8 +116,8 @@ void GraphicsEditor::DrawGfxEditToolset() {
TableNextColumn();
if (Button(ICON_MD_CONTENT_COPY)) {
std::vector png_data =
- rom()->bitmap_manager().shared_bitmap(current_sheet_).GetPngData();
- CopyImageToClipboard(png_data);
+ rom()->gfx_sheets().at(current_sheet_).GetPngData();
+ core::CopyImageToClipboard(png_data);
}
HOVER_HINT("Copy to Clipboard");
@@ -136,14 +125,14 @@ void GraphicsEditor::DrawGfxEditToolset() {
if (Button(ICON_MD_CONTENT_PASTE)) {
std::vector png_data;
int width, height;
- GetImageFromClipboard(png_data, width, height);
+ core::GetImageFromClipboard(png_data, width, height);
if (png_data.size() > 0) {
rom()
- ->mutable_bitmap_manager()
- ->mutable_bitmap(current_sheet_)
- ->Create(width, height, 8, png_data);
- rom()->UpdateBitmap(
- rom()->mutable_bitmap_manager()->mutable_bitmap(current_sheet_));
+ ->mutable_gfx_sheets()
+ ->at(current_sheet_)
+ .Create(width, height, 8, png_data);
+ Renderer::GetInstance().UpdateBitmap(
+ &rom()->mutable_gfx_sheets()->at(current_sheet_));
}
}
HOVER_HINT("Paste from Clipboard");
@@ -163,7 +152,7 @@ void GraphicsEditor::DrawGfxEditToolset() {
}
TableNextColumn();
- auto bitmap = rom()->bitmap_manager()[current_sheet_];
+ auto bitmap = rom()->gfx_sheets()[current_sheet_];
auto palette = bitmap.palette();
for (int i = 0; i < 8; i++) {
ImGui::SameLine();
@@ -192,28 +181,28 @@ absl::Status GraphicsEditor::UpdateGfxSheetList() {
static ImGuiSelectionBasicStorage selection;
ImGuiMultiSelectFlags flags =
ImGuiMultiSelectFlags_ClearOnEscape | ImGuiMultiSelectFlags_BoxSelect1d;
- ImGuiMultiSelectIO* ms_io = ImGui::BeginMultiSelect(
- flags, selection.Size, rom()->bitmap_manager().size());
+ ImGuiMultiSelectIO* ms_io =
+ ImGui::BeginMultiSelect(flags, selection.Size, kNumGfxSheets);
selection.ApplyRequests(ms_io);
ImGuiListClipper clipper;
- clipper.Begin(rom()->bitmap_manager().size());
+ clipper.Begin(kNumGfxSheets);
if (ms_io->RangeSrcItem != -1)
clipper.IncludeItemByIndex(
(int)ms_io->RangeSrcItem); // Ensure RangeSrc item is not clipped.
- for (auto& [key, value] : rom()->bitmap_manager()) {
+ int key = 0;
+ for (auto& value : rom()->gfx_sheets()) {
ImGui::BeginChild(absl::StrFormat("##GfxSheet%02X", key).c_str(),
ImVec2(0x100 + 1, 0x40 + 1), true,
ImGuiWindowFlags_NoDecoration);
ImGui::PopStyleVar();
- gui::Canvas graphics_bin_canvas_;
graphics_bin_canvas_.DrawBackground(ImVec2(0x100 + 1, 0x40 + 1));
graphics_bin_canvas_.DrawContextMenu();
if (value.is_active()) {
auto texture = value.texture();
graphics_bin_canvas_.draw_list()->AddImage(
- (void*)texture,
+ (ImTextureID)(intptr_t)texture,
ImVec2(graphics_bin_canvas_.zero_point().x + 2,
graphics_bin_canvas_.zero_point().y + 2),
ImVec2(graphics_bin_canvas_.zero_point().x +
@@ -240,6 +229,8 @@ absl::Status GraphicsEditor::UpdateGfxSheetList() {
graphics_bin_canvas_.draw_list()->AddText(
text_pos, IM_COL32(125, 255, 125, 255),
absl::StrFormat("%02X", key).c_str());
+
+ key++;
}
graphics_bin_canvas_.DrawGrid(16.0f);
graphics_bin_canvas_.DrawOverlay();
@@ -256,6 +247,10 @@ absl::Status GraphicsEditor::UpdateGfxSheetList() {
absl::Status GraphicsEditor::UpdateGfxTabView() {
static int next_tab_id = 0;
+ constexpr ImGuiTabBarFlags kGfxEditTabBarFlags =
+ ImGuiTabBarFlags_AutoSelectNewTabs | ImGuiTabBarFlags_Reorderable |
+ ImGuiTabBarFlags_FittingPolicyResizeDown |
+ ImGuiTabBarFlags_TabListPopupButton;
if (ImGui::BeginTabBar("##GfxEditTabBar", kGfxEditTabBarFlags)) {
if (ImGui::TabItemButton(ICON_MD_ADD, ImGuiTabItemFlags_Trailing |
@@ -285,18 +280,17 @@ absl::Status GraphicsEditor::UpdateGfxTabView() {
ImGuiWindowFlags_AlwaysVerticalScrollbar |
ImGuiWindowFlags_AlwaysHorizontalScrollbar);
- gfx::Bitmap& current_bitmap =
- *rom()->mutable_bitmap_manager()->mutable_bitmap(sheet_id);
+ gfx::Bitmap& current_bitmap = rom()->mutable_gfx_sheets()->at(sheet_id);
auto draw_tile_event = [&]() {
current_sheet_canvas_.DrawTileOnBitmap(tile_size_, ¤t_bitmap,
current_color_);
- rom()->UpdateBitmap(¤t_bitmap, true);
+ Renderer::GetInstance().UpdateBitmap(¤t_bitmap);
};
current_sheet_canvas_.UpdateColorPainter(
- rom()->bitmap_manager()[sheet_id], current_color_, draw_tile_event,
- tile_size_, current_scale_);
+ rom()->mutable_gfx_sheets()->at(sheet_id), current_color_,
+ draw_tile_event, tile_size_, current_scale_);
ImGui::EndChild();
ImGui::EndTabItem();
@@ -328,7 +322,7 @@ absl::Status GraphicsEditor::UpdateGfxTabView() {
current_sheet_ = id;
// ImVec2(0x100, 0x40),
current_sheet_canvas_.UpdateColorPainter(
- rom()->bitmap_manager()[id], current_color_,
+ rom()->mutable_gfx_sheets()->at(id), current_color_,
[&]() {
},
@@ -365,11 +359,12 @@ absl::Status GraphicsEditor::UpdatePaletteColumn() {
if (refresh_graphics_ && !open_sheets_.empty()) {
RETURN_IF_ERROR(
- rom()->bitmap_manager()[current_sheet_].ApplyPaletteWithTransparent(
- palette, edit_palette_sub_index_));
- rom()->UpdateBitmap(
- rom()->mutable_bitmap_manager()->mutable_bitmap(current_sheet_),
- true);
+ rom()
+ ->mutable_gfx_sheets()
+ ->data()[current_sheet_]
+ .ApplyPaletteWithTransparent(palette, edit_palette_sub_index_));
+ Renderer::GetInstance().UpdateBitmap(
+ &rom()->mutable_gfx_sheets()->data()[current_sheet_]);
refresh_graphics_ = false;
}
}
@@ -391,9 +386,9 @@ absl::Status GraphicsEditor::UpdateLinkGfxView() {
link_canvas_.DrawGrid(16.0f);
int i = 0;
- for (auto [key, link_sheet] : *rom()->mutable_link_graphics()) {
+ for (auto link_sheet : *rom()->mutable_link_graphics()) {
int x_offset = 0;
- int y_offset = core::kTilesheetHeight * i * 4;
+ int y_offset = gfx::kTilesheetHeight * i * 4;
link_canvas_.DrawContextMenu(&link_sheet);
link_canvas_.DrawBitmap(link_sheet, x_offset, y_offset, 4);
i++;
@@ -435,6 +430,10 @@ absl::Status GraphicsEditor::UpdateScadView() {
ImGui::End();
}
+ constexpr ImGuiTableFlags kGfxEditFlags = ImGuiTableFlags_Reorderable |
+ ImGuiTableFlags_Resizable |
+ ImGuiTableFlags_SizingStretchSame;
+
BEGIN_TABLE("#gfxEditTable", 4, kGfxEditFlags)
SETUP_COLUMN("File Import (BIN, CGX, ROM)")
SETUP_COLUMN("Palette (COL)")
@@ -458,17 +457,18 @@ absl::Status GraphicsEditor::UpdateScadView() {
NEXT_COLUMN()
if (super_donkey_) {
- if (refresh_graphics_) {
- for (int i = 0; i < graphics_bin_.size(); i++) {
- status_ = graphics_bin_[i].ApplyPalette(
- col_file_palette_group_[current_palette_index_]);
- rom()->UpdateBitmap(&graphics_bin_[i]);
- }
- refresh_graphics_ = false;
- }
+ // TODO: Implement the Super Donkey 1 graphics decompression
+ // if (refresh_graphics_) {
+ // for (int i = 0; i < kNumGfxSheets; i++) {
+ // status_ = graphics_bin_[i].ApplyPalette(
+ // col_file_palette_group_[current_palette_index_]);
+ // Renderer::GetInstance().UpdateBitmap(&graphics_bin_[i]);
+ // }
+ // refresh_graphics_ = false;
+ // }
// Load the full graphics space from `super_donkey_1.bin`
- gui::GraphicsBinCanvasPipeline(0x100, 0x40, 0x20, num_sheets_to_load_, 3,
- super_donkey_, graphics_bin_);
+ // gui::GraphicsBinCanvasPipeline(0x100, 0x40, 0x20, num_sheets_to_load_, 3,
+ // super_donkey_, graphics_bin_);
} else if (cgx_loaded_ && col_file_) {
// Load the CGX graphics
gui::BitmapCanvasPipeline(import_canvas_, cgx_bitmap_, 0x100, 16384, 0x20,
@@ -485,6 +485,11 @@ absl::Status GraphicsEditor::UpdateScadView() {
}
absl::Status GraphicsEditor::DrawToolset() {
+ static constexpr absl::string_view kGfxToolsetColumnNames[] = {
+ "#memoryEditor",
+ "##separator_gfx1",
+ };
+
if (ImGui::BeginTable("GraphicsToolset", 2, ImGuiTableFlags_SizingFixedFit,
ImVec2(0, 0))) {
for (const auto& name : kGfxToolsetColumnNames)
@@ -535,7 +540,7 @@ absl::Status GraphicsEditor::DrawCgxImport() {
cgx_bitmap_.Create(0x80, 0x200, 8, decoded_cgx_);
if (col_file_) {
cgx_bitmap_.ApplyPalette(decoded_col_);
- rom()->RenderBitmap(&cgx_bitmap_);
+ Renderer::GetInstance().RenderBitmap(&cgx_bitmap_);
}
}
@@ -570,7 +575,7 @@ absl::Status GraphicsEditor::DrawScrImport() {
scr_bitmap_.Create(0x100, 0x100, 8, decoded_scr_data_);
if (scr_loaded_) {
scr_bitmap_.ApplyPalette(decoded_col_);
- rom()->RenderBitmap(&scr_bitmap_);
+ Renderer::GetInstance().RenderBitmap(&scr_bitmap_);
}
}
@@ -594,7 +599,7 @@ absl::Status GraphicsEditor::DrawPaletteControls() {
/*z3_load=*/false);
auto col_data_ = gfx::GetColFileData(temp_rom_.data());
if (col_file_palette_group_.size() != 0) {
- col_file_palette_group_.Clear();
+ col_file_palette_group_.clear();
}
auto col_file_palette_group_status =
gfx::CreatePaletteGroupFromColFile(col_data_);
@@ -707,7 +712,8 @@ absl::Status GraphicsEditor::DrawClipboardImport() {
if (Button("Paste From Clipboard")) {
const char* text = ImGui::GetClipboardText();
if (text) {
- const auto clipboard_data = Bytes(text, text + strlen(text));
+ const auto clipboard_data =
+ std::vector(text, text + strlen(text));
ImGui::MemFree((void*)text);
status_ = temp_rom_.LoadFromBytes(clipboard_data);
is_open_ = true;
@@ -762,7 +768,7 @@ absl::Status GraphicsEditor::DecompressImportData(int size) {
temp_rom_.data(), current_offset_, size))
auto converted_sheet = gfx::SnesTo8bppSheet(import_data_, 3);
- bin_bitmap_.Create(core::kTilesheetWidth, 0x2000, core::kTilesheetDepth,
+ bin_bitmap_.Create(gfx::kTilesheetWidth, 0x2000, gfx::kTilesheetDepth,
converted_sheet);
if (rom()->is_loaded()) {
@@ -775,7 +781,7 @@ absl::Status GraphicsEditor::DecompressImportData(int size) {
}
}
- rom()->RenderBitmap(&bin_bitmap_);
+ Renderer::GetInstance().RenderBitmap(&bin_bitmap_);
gfx_loaded_ = true;
return absl::OkStatus();
@@ -790,11 +796,10 @@ absl::Status GraphicsEditor::DecompressSuperDonkey() {
auto decompressed_data,
gfx::lc_lz2::DecompressV2(temp_rom_.data(), offset_value, 0x1000))
auto converted_sheet = gfx::SnesTo8bppSheet(decompressed_data, 3);
- graphics_bin_[i] =
- gfx::Bitmap(core::kTilesheetWidth, core::kTilesheetHeight,
- core::kTilesheetDepth, converted_sheet);
+ gfx_sheets_[i] = gfx::Bitmap(gfx::kTilesheetWidth, gfx::kTilesheetHeight,
+ gfx::kTilesheetDepth, converted_sheet);
if (col_file_) {
- status_ = graphics_bin_[i].ApplyPalette(
+ status_ = gfx_sheets_[i].ApplyPalette(
col_file_palette_group_[current_palette_index_]);
} else {
// ROM palette
@@ -802,10 +807,10 @@ absl::Status GraphicsEditor::DecompressSuperDonkey() {
auto palette_group = rom()->palette_group().get_group(
kPaletteGroupAddressesKeys[current_palette_]);
z3_rom_palette_ = *palette_group->mutable_palette(current_palette_index_);
- status_ = graphics_bin_[i].ApplyPalette(z3_rom_palette_);
+ status_ = gfx_sheets_[i].ApplyPalette(z3_rom_palette_);
}
- rom()->RenderBitmap(&graphics_bin_[i]);
+ Renderer::GetInstance().RenderBitmap(&gfx_sheets_[i]);
i++;
}
@@ -816,21 +821,20 @@ absl::Status GraphicsEditor::DecompressSuperDonkey() {
auto decompressed_data,
gfx::lc_lz2::DecompressV2(temp_rom_.data(), offset_value, 0x1000))
auto converted_sheet = gfx::SnesTo8bppSheet(decompressed_data, 3);
- graphics_bin_[i] =
- gfx::Bitmap(core::kTilesheetWidth, core::kTilesheetHeight,
- core::kTilesheetDepth, converted_sheet);
+ gfx_sheets_[i] = gfx::Bitmap(gfx::kTilesheetWidth, gfx::kTilesheetHeight,
+ gfx::kTilesheetDepth, converted_sheet);
if (col_file_) {
- status_ = graphics_bin_[i].ApplyPalette(
+ status_ = gfx_sheets_[i].ApplyPalette(
col_file_palette_group_[current_palette_index_]);
} else {
// ROM palette
auto palette_group = rom()->palette_group().get_group(
kPaletteGroupAddressesKeys[current_palette_]);
z3_rom_palette_ = *palette_group->mutable_palette(current_palette_index_);
- status_ = graphics_bin_[i].ApplyPalette(z3_rom_palette_);
+ status_ = gfx_sheets_[i].ApplyPalette(z3_rom_palette_);
}
- rom()->RenderBitmap(&graphics_bin_[i]);
+ Renderer::GetInstance().RenderBitmap(&gfx_sheets_[i]);
i++;
}
super_donkey_ = true;
diff --git a/src/app/editor/graphics/graphics_editor.h b/src/app/editor/graphics/graphics_editor.h
index 99974792..2d6d7a52 100644
--- a/src/app/editor/graphics/graphics_editor.h
+++ b/src/app/editor/graphics/graphics_editor.h
@@ -1,22 +1,19 @@
#ifndef YAZE_APP_EDITOR_GRAPHICS_EDITOR_H
#define YAZE_APP_EDITOR_GRAPHICS_EDITOR_H
-#include "ImGuiFileDialog/ImGuiFileDialog.h"
-#include "imgui/imgui.h"
-#include "imgui/misc/cpp/imgui_stdlib.h"
-#include "imgui_memory_editor.h"
+#include
#include "absl/status/status.h"
-#include "absl/status/statusor.h"
#include "app/editor/graphics/palette_editor.h"
-#include "app/editor/utils/editor.h"
+#include "app/editor/editor.h"
#include "app/gfx/bitmap.h"
#include "app/gfx/snes_tile.h"
-#include "app/gui/asset_browser.h"
+#include "app/gui/modules/asset_browser.h"
#include "app/gui/canvas.h"
-#include "app/gui/input.h"
#include "app/rom.h"
#include "app/zelda3/overworld/overworld.h"
+#include "imgui/imgui.h"
+#include "imgui_memory_editor.h"
namespace yaze {
namespace app {
@@ -167,8 +164,8 @@ class GraphicsEditor : public SharedRom, public Editor {
MemoryEditor cgx_memory_editor_;
MemoryEditor col_memory_editor_;
PaletteEditor palette_editor_;
- Bytes import_data_;
- Bytes graphics_buffer_;
+ std::vector