backend-infra-engineer: Pre-0.2.2 2024 Q2 snapshot
This commit is contained in:
45
.github/workflows/doxy.yml
vendored
Normal file
45
.github/workflows/doxy.yml
vendored
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
name: Doxygen Action
|
||||||
|
|
||||||
|
# Controls when the action will run. Triggers the workflow on push or pull request
|
||||||
|
# events but only for the master branch
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches: [ master ]
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# A workflow run is made up of one or more jobs that can run sequentially or in parallel
|
||||||
|
jobs:
|
||||||
|
# This workflow contains a single job called "build"
|
||||||
|
build:
|
||||||
|
# The type of runner that the job will run on
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
# Steps represent a sequence of tasks that will be executed as part of the job
|
||||||
|
steps:
|
||||||
|
# Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it
|
||||||
|
- uses: actions/checkout@v2
|
||||||
|
|
||||||
|
# Delete the html directory if it exists
|
||||||
|
- name: Delete html directory
|
||||||
|
run: rm -rf html
|
||||||
|
|
||||||
|
# Installs graphviz for DOT graphs
|
||||||
|
- name: Install graphviz
|
||||||
|
run: sudo apt-get install graphviz
|
||||||
|
|
||||||
|
- name: Doxygen Action
|
||||||
|
uses: mattnotmitt/doxygen-action@v1.1.0
|
||||||
|
with:
|
||||||
|
# Path to Doxyfile
|
||||||
|
doxyfile-path: "./Doxyfile" # default is ./Doxyfile
|
||||||
|
# Working directory
|
||||||
|
working-directory: "." # default is .
|
||||||
|
|
||||||
|
- name: Deploy
|
||||||
|
uses: peaceiris/actions-gh-pages@v3
|
||||||
|
with:
|
||||||
|
github_token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
# Default Doxyfile build documentation to html directory.
|
||||||
|
# Change the directory if changes in Doxyfile
|
||||||
|
publish_dir: ./html
|
||||||
@@ -1,17 +1,27 @@
|
|||||||
|
# Build Instructions
|
||||||
For VSCode users, use the following CMake extensions with MinGW-w64
|
|
||||||
|
## Windows
|
||||||
https://marketplace.visualstudio.com/items?itemName=twxs.cmake
|
|
||||||
https://marketplace.visualstudio.com/items?itemName=ms-vscode.cmake-tools
|
For VSCode users, use the following CMake extensions with MinGW-w64
|
||||||
|
|
||||||
https://www.msys2.org/
|
https://marketplace.visualstudio.com/items?itemName=twxs.cmake
|
||||||
|
https://marketplace.visualstudio.com/items?itemName=ms-vscode.cmake-tools
|
||||||
Add to environment variables `C:\msys64\mingw64\bin`
|
|
||||||
|
https://www.msys2.org/
|
||||||
Install the following packages using `pacman -S <package-name>`
|
|
||||||
|
Add to environment variables `C:\msys64\mingw64\bin`
|
||||||
`mingw-w64-x86_64-gcc`
|
|
||||||
`mingw-w64-x86_64-gcc-libs`
|
Install the following packages using `pacman -S <package-name>`
|
||||||
`mingw-w64-x86_64-cmake`
|
|
||||||
`mingw-w64-x86_64-glew`
|
`mingw-w64-x86_64-gcc`
|
||||||
`mingw-w64-x86_64-lib-png`
|
`mingw-w64-x86_64-gcc-libs`
|
||||||
|
`mingw-w64-x86_64-cmake`
|
||||||
|
`mingw-w64-x86_64-glew`
|
||||||
|
`mingw-w64-x86_64-lib-png`
|
||||||
|
|
||||||
|
# macOS
|
||||||
|
|
||||||
|
- Clang 15.0.1 x86_64-apple-darrwin22.5.0
|
||||||
|
- SDL2 Source v2.26.5
|
||||||
|
- Removed snes_spc
|
||||||
|
- Removed asar_static
|
||||||
@@ -2,6 +2,8 @@
|
|||||||
|
|
||||||
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++.
|
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
|
## Key Definitions
|
||||||
|
|
||||||
### Constants and Macros:
|
### Constants and Macros:
|
||||||
@@ -62,6 +64,3 @@ Using `CompressionContext` to handle compression.
|
|||||||
- **Compression String Creation**: `CreateCompressionString`
|
- **Compression String Creation**: `CreateCompressionString`
|
||||||
- **Compression Result Validation**: Such as `ValidateCompressionResult` and its V3 variant.
|
- **Compression Result Validation**: Such as `ValidateCompressionResult` and its V3 variant.
|
||||||
- **Compression Piece Manipulation**: Like `SplitCompressionPiece` and its V3 variant.
|
- **Compression Piece Manipulation**: Like `SplitCompressionPiece` and its V3 variant.
|
||||||
|
|
||||||
## Final Notes
|
|
||||||
The YAZE's LC_LZ2 compression scheme provides three versions of compression methodologies with comprehensive support for various commands and modes. It ensures versatility and adaptability for different compression needs.
|
|
||||||
|
|||||||
@@ -10,19 +10,10 @@ Before you start using YAZE, make sure you have the following:
|
|||||||
- A copy of "The Legend of Zelda: A Link to the Past" ROM file (US or JP)
|
- A copy of "The Legend of Zelda: A Link to the Past" ROM file (US or JP)
|
||||||
- Basic knowledge of hexadecimal and binary data
|
- Basic knowledge of hexadecimal and binary data
|
||||||
|
|
||||||
## Installation
|
|
||||||
To install YAZE, follow these steps based on your platform:
|
|
||||||
|
|
||||||
### Windows
|
|
||||||
|
|
||||||
### MacOS
|
|
||||||
|
|
||||||
### GNU/Linux
|
|
||||||
|
|
||||||
## Usage
|
## Usage
|
||||||
To use the Link to the Past ROM Editor, follow these steps:
|
To use the Link to the Past ROM Editor, follow these steps:
|
||||||
|
|
||||||
Open the "ALTTP.sfc" ROM file using the "File" menu.
|
Open the ROM file using the "File" menu.
|
||||||
|
|
||||||
...
|
...
|
||||||
|
|
||||||
|
|||||||
@@ -23,59 +23,6 @@ For developers to reference.
|
|||||||
- SDL2
|
- SDL2
|
||||||
- **test**: Contains testing interface `yaze_test`
|
- **test**: Contains testing interface `yaze_test`
|
||||||
|
|
||||||
## App Organization
|
|
||||||
|
|
||||||
- **Core Namespace**:
|
|
||||||
- Contains fundamental functionalities.
|
|
||||||
- [Common](../src/app/core/common.h)
|
|
||||||
- [Constants](../src/app/core/constants.h)
|
|
||||||
- [Controller](../src/app/core/controller.h)
|
|
||||||
- [Editor](../src/app/core/editor.h)
|
|
||||||
- [Pipeline](../src/app/gui/pipeline.h)
|
|
||||||
- **Editor Namespace**:
|
|
||||||
- Editors are responsible for representing the GUI view and handling user input.
|
|
||||||
- These classes are all controlled by [MasterEditor](../src/app/editor/master_editor.h)
|
|
||||||
- [DungeonEditor](../src/app/editor/dungeon_editor.h)
|
|
||||||
- [GraphicsEditor](../src/app/editor/graphics_editor.h)
|
|
||||||
- [OverworldEditor](../src/app/editor/overworld_editor.h)
|
|
||||||
- [ScreenEditor](../src/app/editor/screen_editor.h)
|
|
||||||
- [SpriteEditor](../src/app/editor/sprite_editor.h)
|
|
||||||
- **Modules**
|
|
||||||
- [AssemblyEditor](../src/app/editor/modules/assembly_editor.h)
|
|
||||||
- [MusicEditor](../src/app/editor/modules/music_editor.h)
|
|
||||||
- [GfxGroupEditor](../src/app/editor/modules/gfx_group_editor.h)
|
|
||||||
- [Tile16Editor](../src/app/editor/modules/tile16_editor.h)
|
|
||||||
- **Emu Namespace**:
|
|
||||||
- Contains business logic for `core::emulator`
|
|
||||||
- [Audio](../src/app/emu/audio/)
|
|
||||||
- [Debug](../src/app/emu/debug/)
|
|
||||||
- [Memory](../src/app/emu/memory/)
|
|
||||||
- [Video](../src/app/emu/video/)
|
|
||||||
- [Emulator](../src/app/emu/emulator.h)
|
|
||||||
- **Gfx Namespace**:
|
|
||||||
- Handles graphics related tasks.
|
|
||||||
- [Bitmap](../src/app/gfx/bitmap.h)
|
|
||||||
- [Compression](../src/app/gfx/compression.h)
|
|
||||||
- [SCAD Format](../src/app/gfx/scad_format.h)
|
|
||||||
- [SNES Palette](../src/app/gfx/snes_palette.h)
|
|
||||||
- [SNES Tile](../src/app/gfx/snes_tile.h)
|
|
||||||
- **Gui Namespace**:
|
|
||||||
- Manages GUI elements.
|
|
||||||
- [Canvas](../src/app/gui/canvas.h)
|
|
||||||
- [Color](../src/app/gui/color.h)
|
|
||||||
- [Icons](../src/app/gui/icons.h)
|
|
||||||
- [Input](../src/app/gui/input.h)
|
|
||||||
- [Style](../src/app/gui/style.h)
|
|
||||||
- [Widgets](../src/app/gui/widgets.h)
|
|
||||||
- **Zelda3 Namespace**:
|
|
||||||
- Holds business logic specific to Zelda3.
|
|
||||||
- [Dungeon](../src/app/zelda3/dungeon/)
|
|
||||||
- [Music](../src/app/zelda3/music/)
|
|
||||||
- [Screen](../src/app/zelda3/screen/)
|
|
||||||
- [Sprite](../src/app/zelda3/sprite/)
|
|
||||||
- [OverworldMap](../src/app/zelda3/overworld_map.h)
|
|
||||||
- [Overworld](../src/app/zelda3/overworld.h)
|
|
||||||
|
|
||||||
### Flow of Control
|
### Flow of Control
|
||||||
|
|
||||||
- [app/yaze.cc](../src/app/yaze.cc)
|
- [app/yaze.cc](../src/app/yaze.cc)
|
||||||
@@ -142,22 +89,6 @@ This `ROM` class provides methods to manipulate and access data from a ROM.
|
|||||||
- Specific Zelda 3 data can be loaded if specified.
|
- 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.
|
- Palettes are categorized into multiple groups (e.g., `ow_main`, `ow_aux`, `hud`, etc.) and loaded accordingly.
|
||||||
|
|
||||||
|
|
||||||
## Overworld
|
|
||||||
|
|
||||||
- [app/zelda3/overworld.cc](../src/app/zelda3/overworld.cc)
|
|
||||||
- [app/zelda3/overworld.h](../src/app/zelda3/overworld.h)
|
|
||||||
- [app/zelda3/overworld_map.cc](../src/app/zelda3/overworld_map.cc)
|
|
||||||
- [app/zelda3/overworld_map.h](../src/app/zelda3/overworld_map.h)
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
- **Construction of Tile16 and Tile32**
|
|
||||||
- **Save and Load Resources**
|
|
||||||
- Sprites
|
|
||||||
- Entrances
|
|
||||||
- Tilemaps
|
|
||||||
|
|
||||||
## Bitmap
|
## Bitmap
|
||||||
|
|
||||||
- [app/gfx/bitmap.cc](../src/app/gfx/bitmap.cc)
|
- [app/gfx/bitmap.cc](../src/app/gfx/bitmap.cc)
|
||||||
|
|||||||
@@ -1,6 +0,0 @@
|
|||||||
# macOS Build Settings
|
|
||||||
|
|
||||||
- Clang 15.0.1 x86_64-apple-darrwin22.5.0
|
|
||||||
- SDL2 Source v2.26.5
|
|
||||||
- Removed snes_spc
|
|
||||||
- Removed asar_static
|
|
||||||
@@ -29,12 +29,13 @@ set(
|
|||||||
app/gfx/snes_palette.cc
|
app/gfx/snes_palette.cc
|
||||||
app/gfx/snes_tile.cc
|
app/gfx/snes_tile.cc
|
||||||
app/gfx/snes_color.cc
|
app/gfx/snes_color.cc
|
||||||
|
app/gfx/tilesheet.cc
|
||||||
)
|
)
|
||||||
|
|
||||||
set(
|
set(
|
||||||
YAZE_APP_ZELDA3_SRC
|
YAZE_APP_ZELDA3_SRC
|
||||||
app/zelda3/overworld_map.cc
|
app/zelda3/overworld/overworld_map.cc
|
||||||
app/zelda3/overworld.cc
|
app/zelda3/overworld/overworld.cc
|
||||||
app/zelda3/screen/inventory.cc
|
app/zelda3/screen/inventory.cc
|
||||||
app/zelda3/screen/title_screen.cc
|
app/zelda3/screen/title_screen.cc
|
||||||
app/zelda3/sprite/sprite.cc
|
app/zelda3/sprite/sprite.cc
|
||||||
|
|||||||
@@ -14,12 +14,21 @@
|
|||||||
|
|
||||||
namespace yaze {
|
namespace yaze {
|
||||||
namespace app {
|
namespace app {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @namespace yaze::app::core
|
||||||
|
* @brief Core application logic and utilities.
|
||||||
|
*/
|
||||||
namespace core {
|
namespace core {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @class ExperimentFlags
|
||||||
|
* @brief A class to manage experimental feature flags.
|
||||||
|
*/
|
||||||
class ExperimentFlags {
|
class ExperimentFlags {
|
||||||
public:
|
public:
|
||||||
struct Flags {
|
struct Flags {
|
||||||
// Bitmap manager abstraction to manage graphics bin of ROM.
|
// Bitmap manager abstraction to manage graphics bin of Rom.
|
||||||
bool kUseBitmapManager = true;
|
bool kUseBitmapManager = true;
|
||||||
|
|
||||||
// Log instructions to the GUI debugger.
|
// Log instructions to the GUI debugger.
|
||||||
@@ -30,11 +39,11 @@ class ExperimentFlags {
|
|||||||
// ported away from that eventually.
|
// ported away from that eventually.
|
||||||
bool kUseNewImGuiInput = false;
|
bool kUseNewImGuiInput = false;
|
||||||
|
|
||||||
// Flag to enable the saving of all palettes to the ROM.
|
// Flag to enable the saving of all palettes to the Rom.
|
||||||
bool kSaveAllPalettes = false;
|
bool kSaveAllPalettes = false;
|
||||||
|
|
||||||
// Flag to enable the change queue, which could have any anonymous
|
// Flag to enable the change queue, which could have any anonymous
|
||||||
// save routine for the ROM. In practice, just the overworld tilemap
|
// save routine for the Rom. In practice, just the overworld tilemap
|
||||||
// and tile32 save.
|
// and tile32 save.
|
||||||
bool kSaveWithChangeQueue = false;
|
bool kSaveWithChangeQueue = false;
|
||||||
|
|
||||||
@@ -51,7 +60,7 @@ class ExperimentFlags {
|
|||||||
// Uses texture streaming from SDL for my dynamic updates.
|
// Uses texture streaming from SDL for my dynamic updates.
|
||||||
bool kLoadTexturesAsStreaming = true;
|
bool kLoadTexturesAsStreaming = true;
|
||||||
|
|
||||||
// Save dungeon map edits to the ROM.
|
// Save dungeon map edits to the Rom.
|
||||||
bool kSaveDungeonMaps = false;
|
bool kSaveDungeonMaps = false;
|
||||||
|
|
||||||
// Log to the console.
|
// Log to the console.
|
||||||
@@ -62,19 +71,19 @@ class ExperimentFlags {
|
|||||||
// Load and render overworld sprites to the screen. Unstable.
|
// Load and render overworld sprites to the screen. Unstable.
|
||||||
bool kDrawOverworldSprites = false;
|
bool kDrawOverworldSprites = false;
|
||||||
|
|
||||||
// Save overworld map edits to the ROM.
|
// Save overworld map edits to the Rom.
|
||||||
bool kSaveOverworldMaps = true;
|
bool kSaveOverworldMaps = true;
|
||||||
|
|
||||||
// Save overworld entrances to the ROM.
|
// Save overworld entrances to the Rom.
|
||||||
bool kSaveOverworldEntrances = true;
|
bool kSaveOverworldEntrances = true;
|
||||||
|
|
||||||
// Save overworld exits to the ROM.
|
// Save overworld exits to the Rom.
|
||||||
bool kSaveOverworldExits = true;
|
bool kSaveOverworldExits = true;
|
||||||
|
|
||||||
// Save overworld items to the ROM.
|
// Save overworld items to the Rom.
|
||||||
bool kSaveOverworldItems = true;
|
bool kSaveOverworldItems = true;
|
||||||
|
|
||||||
// Save overworld properties to the ROM.
|
// Save overworld properties to the Rom.
|
||||||
bool kSaveOverworldProperties = true;
|
bool kSaveOverworldProperties = true;
|
||||||
} overworld;
|
} overworld;
|
||||||
};
|
};
|
||||||
@@ -99,6 +108,11 @@ class ExperimentFlags {
|
|||||||
static std::shared_ptr<Flags> flags_;
|
static std::shared_ptr<Flags> flags_;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @class NotifyValue
|
||||||
|
* @brief A class to manage a value that can be modified and notify when it
|
||||||
|
* changes.
|
||||||
|
*/
|
||||||
template <typename T>
|
template <typename T>
|
||||||
class NotifyValue {
|
class NotifyValue {
|
||||||
public:
|
public:
|
||||||
@@ -140,81 +154,6 @@ class NotifyValue {
|
|||||||
T temp_value_;
|
T temp_value_;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct TaskCheckpoint {
|
|
||||||
int task_index = 0;
|
|
||||||
bool complete = false;
|
|
||||||
// You can add more internal data or state-related variables here as needed
|
|
||||||
};
|
|
||||||
|
|
||||||
class TaskTimer {
|
|
||||||
public:
|
|
||||||
// Starts the timer
|
|
||||||
void StartTimer() { start_time_ = std::chrono::steady_clock::now(); }
|
|
||||||
|
|
||||||
// Checks if the task should finish based on the given timeout in seconds
|
|
||||||
bool ShouldFinishTask(int timeout_seconds) {
|
|
||||||
auto current_time = std::chrono::steady_clock::now();
|
|
||||||
auto elapsed_time = std::chrono::duration_cast<std::chrono::seconds>(
|
|
||||||
current_time - start_time_);
|
|
||||||
return elapsed_time.count() >= timeout_seconds;
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
std::chrono::steady_clock::time_point start_time_;
|
|
||||||
};
|
|
||||||
|
|
||||||
template <typename TFunc>
|
|
||||||
class TaskManager {
|
|
||||||
public:
|
|
||||||
TaskManager() = default;
|
|
||||||
~TaskManager() = default;
|
|
||||||
|
|
||||||
TaskManager(int totalTasks, int timeoutSeconds)
|
|
||||||
: total_tasks_(totalTasks),
|
|
||||||
timeout_seconds_(timeoutSeconds),
|
|
||||||
task_index_(0),
|
|
||||||
task_complete_(false) {}
|
|
||||||
|
|
||||||
void ExecuteTasks(const TFunc &taskFunc) {
|
|
||||||
if (task_complete_) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
StartTimer();
|
|
||||||
|
|
||||||
for (; task_index_ < total_tasks_; ++task_index_) {
|
|
||||||
taskFunc(task_index_);
|
|
||||||
|
|
||||||
if (ShouldFinishTask()) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (task_index_ == total_tasks_) {
|
|
||||||
task_complete_ = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool IsTaskComplete() const { return task_complete_; }
|
|
||||||
void SetTimeout(int timeout) { timeout_seconds_ = timeout; }
|
|
||||||
|
|
||||||
private:
|
|
||||||
int total_tasks_;
|
|
||||||
int timeout_seconds_;
|
|
||||||
int task_index_;
|
|
||||||
bool task_complete_;
|
|
||||||
std::chrono::steady_clock::time_point start_time_;
|
|
||||||
|
|
||||||
void StartTimer() { start_time_ = std::chrono::steady_clock::now(); }
|
|
||||||
|
|
||||||
bool ShouldFinishTask() {
|
|
||||||
auto current_time = std::chrono::steady_clock::now();
|
|
||||||
auto elapsed_time = std::chrono::duration_cast<std::chrono::seconds>(
|
|
||||||
current_time - start_time_);
|
|
||||||
return elapsed_time.count() >= timeout_seconds_;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
class ImGuiIdIssuer {
|
class ImGuiIdIssuer {
|
||||||
private:
|
private:
|
||||||
static std::stack<ImGuiID> idStack;
|
static std::stack<ImGuiID> idStack;
|
||||||
|
|||||||
@@ -256,7 +256,7 @@ absl::Status Controller::CreateGuiContext() {
|
|||||||
|
|
||||||
// Build a new ImGui frame
|
// Build a new ImGui frame
|
||||||
ImGui_ImplSDLRenderer2_NewFrame();
|
ImGui_ImplSDLRenderer2_NewFrame();
|
||||||
ImGui_ImplSDL2_NewFrame(window_.get());
|
ImGui_ImplSDL2_NewFrame();
|
||||||
|
|
||||||
return absl::OkStatus();
|
return absl::OkStatus();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,8 +11,8 @@
|
|||||||
|
|
||||||
#include "absl/status/status.h"
|
#include "absl/status/status.h"
|
||||||
#include "app/core/common.h"
|
#include "app/core/common.h"
|
||||||
#include "app/core/editor.h"
|
|
||||||
#include "app/editor/master_editor.h"
|
#include "app/editor/master_editor.h"
|
||||||
|
#include "app/editor/utils/editor.h"
|
||||||
#include "app/gui/icons.h"
|
#include "app/gui/icons.h"
|
||||||
#include "app/gui/style.h"
|
#include "app/gui/style.h"
|
||||||
|
|
||||||
@@ -22,6 +22,12 @@ namespace yaze {
|
|||||||
namespace app {
|
namespace app {
|
||||||
namespace core {
|
namespace core {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Main controller for the application.
|
||||||
|
*
|
||||||
|
* This class is responsible for managing the main window and the
|
||||||
|
* main editor. It is the main entry point for the application.
|
||||||
|
*/
|
||||||
class Controller : public ExperimentFlags {
|
class Controller : public ExperimentFlags {
|
||||||
public:
|
public:
|
||||||
bool IsActive() const { return active_; }
|
bool IsActive() const { return active_; }
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
#import <Cocoa/Cocoa.h>
|
#import <Cocoa/Cocoa.h>
|
||||||
|
|
||||||
#import "app/core/controller.h"
|
#import "app/core/controller.h"
|
||||||
#import "app/core/editor.h"
|
#import "app/editor/utils/editor.h"
|
||||||
#import "app/core/platform/app_delegate.h"
|
#import "app/core/platform/app_delegate.h"
|
||||||
#import "app/core/platform/file_dialog.h"
|
#import "app/core/platform/file_dialog.h"
|
||||||
#import "app/rom.h"
|
#import "app/rom.h"
|
||||||
@@ -195,7 +195,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
- (void)openFileAction:(id)sender {
|
- (void)openFileAction:(id)sender {
|
||||||
yaze::app::SharedROM::shared_rom_->LoadFromFile(FileDialogWrapper::ShowOpenFileDialog());
|
yaze::app::SharedRom::shared_rom_->LoadFromFile(FileDialogWrapper::ShowOpenFileDialog());
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)cutAction:(id)sender {
|
- (void)cutAction:(id)sender {
|
||||||
|
|||||||
42
src/app/editor/context/entrance_context.h
Normal file
42
src/app/editor/context/entrance_context.h
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
#ifndef YAZE_APP_EDITOR_CONTEXT_ENTRANCE_CONTEXT_H_
|
||||||
|
#define YAZE_APP_EDITOR_CONTEXT_ENTRANCE_CONTEXT_H_
|
||||||
|
|
||||||
|
#include <cstdint>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include "absl/status/status.h"
|
||||||
|
#include "app/rom.h"
|
||||||
|
|
||||||
|
namespace yaze {
|
||||||
|
namespace app {
|
||||||
|
namespace editor {
|
||||||
|
namespace context {
|
||||||
|
|
||||||
|
class EntranceContext {
|
||||||
|
public:
|
||||||
|
absl::Status LoadEntranceTileTypes(Rom& rom) {
|
||||||
|
int offset_low = 0xDB8BF;
|
||||||
|
int offset_high = 0xDB917;
|
||||||
|
|
||||||
|
for (int i = 0; i < 0x2C; i++) {
|
||||||
|
// Load entrance tile types
|
||||||
|
ASSIGN_OR_RETURN(auto value_low, rom.ReadWord(offset_low + i));
|
||||||
|
entrance_tile_types_low_.push_back(value_low);
|
||||||
|
ASSIGN_OR_RETURN(auto value_high, rom.ReadWord(offset_high + i));
|
||||||
|
entrance_tile_types_low_.push_back(value_high);
|
||||||
|
}
|
||||||
|
|
||||||
|
return absl::OkStatus();
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::vector<uint16_t> entrance_tile_types_low_;
|
||||||
|
std::vector<uint16_t> entrance_tile_types_high_;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace context
|
||||||
|
} // namespace editor
|
||||||
|
} // namespace app
|
||||||
|
} // namespace yaze
|
||||||
|
|
||||||
|
#endif // YAZE_APP_EDITOR_CONTEXT_ENTRANCE_CONTEXT_H_
|
||||||
@@ -4,23 +4,24 @@
|
|||||||
|
|
||||||
#include <cmath>
|
#include <cmath>
|
||||||
|
|
||||||
#include "app/core/editor.h"
|
#include "app/editor/utils/editor.h"
|
||||||
#include "app/gui/pipeline.h"
|
|
||||||
#include "app/editor/modules/palette_editor.h"
|
#include "app/editor/modules/palette_editor.h"
|
||||||
#include "app/gfx/bitmap.h"
|
#include "app/gfx/bitmap.h"
|
||||||
#include "app/gfx/snes_palette.h"
|
#include "app/gfx/snes_palette.h"
|
||||||
#include "app/gfx/snes_tile.h"
|
#include "app/gfx/snes_tile.h"
|
||||||
#include "app/gui/canvas.h"
|
#include "app/gui/canvas.h"
|
||||||
#include "app/gui/icons.h"
|
#include "app/gui/icons.h"
|
||||||
|
#include "app/gui/pipeline.h"
|
||||||
#include "app/rom.h"
|
#include "app/rom.h"
|
||||||
|
|
||||||
namespace yaze {
|
namespace yaze {
|
||||||
namespace app {
|
namespace app {
|
||||||
namespace editor {
|
namespace editor {
|
||||||
|
namespace context {
|
||||||
|
|
||||||
std::unordered_map<uint8_t, gfx::Paletteset> GfxContext::palettesets_;
|
std::unordered_map<uint8_t, gfx::Paletteset> GfxContext::palettesets_;
|
||||||
|
|
||||||
|
}
|
||||||
} // namespace editor
|
} // namespace editor
|
||||||
} // namespace app
|
} // namespace app
|
||||||
} // namespace yaze
|
} // namespace yaze
|
||||||
@@ -6,7 +6,7 @@
|
|||||||
#include <cmath>
|
#include <cmath>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#include "app/core/editor.h"
|
#include "app/editor/utils/editor.h"
|
||||||
#include "app/editor/modules/palette_editor.h"
|
#include "app/editor/modules/palette_editor.h"
|
||||||
#include "app/gfx/bitmap.h"
|
#include "app/gfx/bitmap.h"
|
||||||
#include "app/gfx/snes_palette.h"
|
#include "app/gfx/snes_palette.h"
|
||||||
@@ -19,7 +19,11 @@
|
|||||||
namespace yaze {
|
namespace yaze {
|
||||||
namespace app {
|
namespace app {
|
||||||
namespace editor {
|
namespace editor {
|
||||||
|
namespace context {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Shared graphical context across editors.
|
||||||
|
*/
|
||||||
class GfxContext {
|
class GfxContext {
|
||||||
public:
|
public:
|
||||||
absl::Status Update();
|
absl::Status Update();
|
||||||
@@ -29,6 +33,7 @@ class GfxContext {
|
|||||||
static std::unordered_map<uint8_t, gfx::Paletteset> palettesets_;
|
static std::unordered_map<uint8_t, gfx::Paletteset> palettesets_;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
} // namespace context
|
||||||
} // namespace editor
|
} // namespace editor
|
||||||
} // namespace app
|
} // namespace app
|
||||||
} // namespace yaze
|
} // namespace yaze
|
||||||
|
|||||||
@@ -30,60 +30,12 @@ using ImGui::TableSetupColumn;
|
|||||||
|
|
||||||
absl::Status DungeonEditor::Update() {
|
absl::Status DungeonEditor::Update() {
|
||||||
if (!is_loaded_ && rom()->is_loaded()) {
|
if (!is_loaded_ && rom()->is_loaded()) {
|
||||||
for (int i = 0; i < 0x100 + 40; i++) {
|
RETURN_IF_ERROR(Initialize());
|
||||||
rooms_.emplace_back(zelda3::dungeon::Room(i));
|
|
||||||
rooms_[i].LoadHeader();
|
|
||||||
rooms_[i].LoadRoomFromROM();
|
|
||||||
if (flags()->kDrawDungeonRoomGraphics) {
|
|
||||||
rooms_[i].LoadRoomGraphics();
|
|
||||||
}
|
|
||||||
|
|
||||||
room_size_pointers_.push_back(rooms_[i].room_size_ptr());
|
|
||||||
if (rooms_[i].room_size_ptr() != 0x0A8000) {
|
|
||||||
room_size_addresses_[i] = rooms_[i].room_size_ptr();
|
|
||||||
}
|
|
||||||
|
|
||||||
auto dungeon_palette_ptr = rom()->paletteset_ids[rooms_[i].palette][0];
|
|
||||||
ASSIGN_OR_RETURN(auto palette_id,
|
|
||||||
rom()->ReadWord(0xDEC4B + dungeon_palette_ptr));
|
|
||||||
int p_id = palette_id / 180;
|
|
||||||
auto color = rom()->palette_group("dungeon_main")[p_id][3];
|
|
||||||
|
|
||||||
room_palette_[rooms_[i].palette] = color.rgb();
|
|
||||||
}
|
|
||||||
|
|
||||||
LoadDungeonRoomSize();
|
|
||||||
LoadRoomEntrances();
|
|
||||||
|
|
||||||
// Load the palette group and palette for the dungeon
|
|
||||||
full_palette_ =
|
|
||||||
rom()->palette_group("dungeon_main")[current_palette_group_id_];
|
|
||||||
ASSIGN_OR_RETURN(current_palette_group_,
|
|
||||||
gfx::CreatePaletteGroupFromLargePalette(full_palette_));
|
|
||||||
|
|
||||||
graphics_bin_ = *rom()->mutable_bitmap_manager();
|
|
||||||
// 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].get());
|
|
||||||
}
|
|
||||||
|
|
||||||
is_loaded_ = true;
|
is_loaded_ = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (refresh_graphics_) {
|
if (refresh_graphics_) {
|
||||||
for (int i = 0; i < 8; i++) {
|
RETURN_IF_ERROR(RefreshGraphics());
|
||||||
int block = rooms_[current_room_id_].blocks()[i];
|
|
||||||
graphics_bin_[block].get()->ApplyPaletteWithTransparent(
|
|
||||||
current_palette_group_[current_palette_id_], 0);
|
|
||||||
rom()->UpdateBitmap(graphics_bin_[block].get(), true);
|
|
||||||
}
|
|
||||||
for (int i = 9; i < 16; i++) {
|
|
||||||
int block = rooms_[current_room_id_].blocks()[i];
|
|
||||||
graphics_bin_[block].get()->ApplyPaletteWithTransparent(
|
|
||||||
rom()->palette_group("sprites_aux1")[current_palette_id_], 0);
|
|
||||||
rom()->UpdateBitmap(graphics_bin_[block].get(), true);
|
|
||||||
}
|
|
||||||
|
|
||||||
refresh_graphics_ = false;
|
refresh_graphics_ = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -106,6 +58,62 @@ absl::Status DungeonEditor::Update() {
|
|||||||
return absl::OkStatus();
|
return absl::OkStatus();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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_[i].LoadHeader();
|
||||||
|
rooms_[i].LoadRoomFromROM();
|
||||||
|
if (flags()->kDrawDungeonRoomGraphics) {
|
||||||
|
rooms_[i].LoadRoomGraphics();
|
||||||
|
}
|
||||||
|
|
||||||
|
room_size_pointers_.push_back(rooms_[i].room_size_ptr());
|
||||||
|
if (rooms_[i].room_size_ptr() != 0x0A8000) {
|
||||||
|
room_size_addresses_[i] = rooms_[i].room_size_ptr();
|
||||||
|
}
|
||||||
|
|
||||||
|
auto dungeon_palette_ptr = rom()->paletteset_ids[rooms_[i].palette][0];
|
||||||
|
ASSIGN_OR_RETURN(auto palette_id,
|
||||||
|
rom()->ReadWord(0xDEC4B + dungeon_palette_ptr));
|
||||||
|
int p_id = palette_id / 180;
|
||||||
|
auto color = dungeon_man_pal_group[p_id][3];
|
||||||
|
room_palette_[rooms_[i].palette] = color.rgb();
|
||||||
|
}
|
||||||
|
|
||||||
|
LoadDungeonRoomSize();
|
||||||
|
LoadRoomEntrances();
|
||||||
|
|
||||||
|
// Load the palette group and palette for the dungeon
|
||||||
|
full_palette_ = dungeon_man_pal_group[current_palette_group_id_];
|
||||||
|
ASSIGN_OR_RETURN(current_palette_group_,
|
||||||
|
gfx::CreatePaletteGroupFromLargePalette(full_palette_));
|
||||||
|
|
||||||
|
graphics_bin_ = *rom()->mutable_bitmap_manager();
|
||||||
|
// 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].get());
|
||||||
|
}
|
||||||
|
return absl::OkStatus();
|
||||||
|
}
|
||||||
|
|
||||||
|
absl::Status DungeonEditor::RefreshGraphics() {
|
||||||
|
for (int i = 0; i < 8; i++) {
|
||||||
|
int block = rooms_[current_room_id_].blocks()[i];
|
||||||
|
RETURN_IF_ERROR(graphics_bin_[block].get()->ApplyPaletteWithTransparent(
|
||||||
|
current_palette_group_[current_palette_id_], 0));
|
||||||
|
rom()->UpdateBitmap(graphics_bin_[block].get(), true);
|
||||||
|
}
|
||||||
|
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];
|
||||||
|
graphics_bin_[block].get()->ApplyPaletteWithTransparent(
|
||||||
|
sprites_aux1_pal_group[current_palette_id_], 0);
|
||||||
|
rom()->UpdateBitmap(graphics_bin_[block].get(), true);
|
||||||
|
}
|
||||||
|
return absl::OkStatus();
|
||||||
|
}
|
||||||
|
|
||||||
void DungeonEditor::LoadDungeonRoomSize() {
|
void DungeonEditor::LoadDungeonRoomSize() {
|
||||||
std::map<int, std::vector<int>> rooms_by_bank;
|
std::map<int, std::vector<int>> rooms_by_bank;
|
||||||
for (const auto& room : room_size_addresses_) {
|
for (const auto& room : room_size_addresses_) {
|
||||||
@@ -148,13 +156,13 @@ void DungeonEditor::LoadDungeonRoomSize() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void DungeonEditor::UpdateDungeonRoomView() {
|
absl::Status DungeonEditor::UpdateDungeonRoomView() {
|
||||||
DrawToolset();
|
DrawToolset();
|
||||||
|
|
||||||
if (palette_showing_) {
|
if (palette_showing_) {
|
||||||
ImGui::Begin("Palette Editor", &palette_showing_, 0);
|
ImGui::Begin("Palette Editor", &palette_showing_, 0);
|
||||||
current_palette_ =
|
auto dungeon_main_pal_group = rom()->palette_group().dungeon_main;
|
||||||
rom()->palette_group("dungeon_main")[current_palette_group_id_];
|
current_palette_ = dungeon_main_pal_group[current_palette_group_id_];
|
||||||
gui::SelectablePalettePipeline(current_palette_id_, refresh_graphics_,
|
gui::SelectablePalettePipeline(current_palette_id_, refresh_graphics_,
|
||||||
current_palette_);
|
current_palette_);
|
||||||
ImGui::End();
|
ImGui::End();
|
||||||
@@ -186,6 +194,7 @@ void DungeonEditor::UpdateDungeonRoomView() {
|
|||||||
DrawTileSelector();
|
DrawTileSelector();
|
||||||
ImGui::EndTable();
|
ImGui::EndTable();
|
||||||
}
|
}
|
||||||
|
return absl::OkStatus();
|
||||||
}
|
}
|
||||||
|
|
||||||
void DungeonEditor::DrawToolset() {
|
void DungeonEditor::DrawToolset() {
|
||||||
@@ -483,7 +492,7 @@ void DungeonEditor::DrawDungeonCanvas(int room_id) {
|
|||||||
|
|
||||||
void DungeonEditor::DrawRoomGraphics() {
|
void DungeonEditor::DrawRoomGraphics() {
|
||||||
const auto height = 0x40;
|
const auto height = 0x40;
|
||||||
room_gfx_canvas_.DrawBackground(ImVec2(256 + 1, 0x10 * 0x40 + 1));
|
room_gfx_canvas_.DrawBackground(ImVec2(0x100 + 1, 0x10 * 0x40 + 1));
|
||||||
room_gfx_canvas_.DrawContextMenu();
|
room_gfx_canvas_.DrawContextMenu();
|
||||||
room_gfx_canvas_.DrawTileSelector(32);
|
room_gfx_canvas_.DrawTileSelector(32);
|
||||||
if (is_loaded_) {
|
if (is_loaded_) {
|
||||||
@@ -593,26 +602,19 @@ void DungeonEditor::LoadRoomEntrances() {
|
|||||||
// ============================================================================
|
// ============================================================================
|
||||||
|
|
||||||
void DungeonEditor::CalculateUsageStats() {
|
void DungeonEditor::CalculateUsageStats() {
|
||||||
// Create a hash map of the usage for elements of each Dungeon Room such as
|
|
||||||
// the blockset, spriteset, palette, etc. This is so we can keep track of
|
|
||||||
// which graphics sets and palette sets are in use and which are not.
|
|
||||||
|
|
||||||
for (const auto& room : rooms_) {
|
for (const auto& room : rooms_) {
|
||||||
// Blockset
|
|
||||||
if (blockset_usage_.find(room.blockset) == blockset_usage_.end()) {
|
if (blockset_usage_.find(room.blockset) == blockset_usage_.end()) {
|
||||||
blockset_usage_[room.blockset] = 1;
|
blockset_usage_[room.blockset] = 1;
|
||||||
} else {
|
} else {
|
||||||
blockset_usage_[room.blockset] += 1;
|
blockset_usage_[room.blockset] += 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Spriteset
|
|
||||||
if (spriteset_usage_.find(room.spriteset) == spriteset_usage_.end()) {
|
if (spriteset_usage_.find(room.spriteset) == spriteset_usage_.end()) {
|
||||||
spriteset_usage_[room.spriteset] = 1;
|
spriteset_usage_[room.spriteset] = 1;
|
||||||
} else {
|
} else {
|
||||||
spriteset_usage_[room.spriteset] += 1;
|
spriteset_usage_[room.spriteset] += 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Palette
|
|
||||||
if (palette_usage_.find(room.palette) == palette_usage_.end()) {
|
if (palette_usage_.find(room.palette) == palette_usage_.end()) {
|
||||||
palette_usage_[room.palette] = 1;
|
palette_usage_[room.palette] = 1;
|
||||||
} else {
|
} else {
|
||||||
@@ -756,22 +758,14 @@ void DungeonEditor::DrawUsageStats() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void DungeonEditor::DrawUsageGrid() {
|
void DungeonEditor::DrawUsageGrid() {
|
||||||
// Create a grid of 295 small squares which is 16 squares wide
|
|
||||||
// Each square represents a room in the game
|
|
||||||
// When you hover a square it should show a hover tooltip with the properties
|
|
||||||
// of the room such as the blockset, spriteset, palette, etc. Calculate the
|
|
||||||
// number of rows
|
|
||||||
int totalSquares = 296;
|
int totalSquares = 296;
|
||||||
int squaresWide = 16;
|
int squaresWide = 16;
|
||||||
int squaresTall = (totalSquares + squaresWide - 1) /
|
int squaresTall = (totalSquares + squaresWide - 1) /
|
||||||
squaresWide; // Ceiling of totalSquares/squaresWide
|
squaresWide; // Ceiling of totalSquares/squaresWide
|
||||||
|
|
||||||
// Loop through each row
|
|
||||||
for (int row = 0; row < squaresTall; ++row) {
|
for (int row = 0; row < squaresTall; ++row) {
|
||||||
// Start a new line for each row
|
|
||||||
ImGui::NewLine();
|
ImGui::NewLine();
|
||||||
|
|
||||||
// Loop through each column in the row
|
|
||||||
for (int col = 0; col < squaresWide; ++col) {
|
for (int col = 0; col < squaresWide; ++col) {
|
||||||
// Check if we have reached 295 squares
|
// Check if we have reached 295 squares
|
||||||
if (row * squaresWide + col >= totalSquares) {
|
if (row * squaresWide + col >= totalSquares) {
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
#include <imgui/imgui.h>
|
#include <imgui/imgui.h>
|
||||||
|
|
||||||
#include "app/core/common.h"
|
#include "app/core/common.h"
|
||||||
#include "app/core/editor.h"
|
#include "app/editor/utils/editor.h"
|
||||||
#include "app/core/labeling.h"
|
#include "app/core/labeling.h"
|
||||||
#include "app/editor/modules/gfx_group_editor.h"
|
#include "app/editor/modules/gfx_group_editor.h"
|
||||||
#include "app/editor/modules/palette_editor.h"
|
#include "app/editor/modules/palette_editor.h"
|
||||||
@@ -32,8 +32,18 @@ constexpr ImGuiTableFlags kDungeonTableFlags =
|
|||||||
ImGuiTableFlags_Hideable | ImGuiTableFlags_BordersOuter |
|
ImGuiTableFlags_Hideable | ImGuiTableFlags_BordersOuter |
|
||||||
ImGuiTableFlags_BordersV;
|
ImGuiTableFlags_BordersV;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief DungeonEditor class for editing dungeons.
|
||||||
|
*
|
||||||
|
* This class is currently a work in progress and is used for editing dungeons.
|
||||||
|
* It provides various functions for updating, cutting, copying, pasting,
|
||||||
|
* undoing, and redoing. It also includes methods for drawing the toolset, room
|
||||||
|
* selector, entrance selector, dungeon tab view, dungeon canvas, room graphics,
|
||||||
|
* tile selector, and object renderer. Additionally, it handles loading room
|
||||||
|
* entrances, calculating usage statistics, and rendering set usage.
|
||||||
|
*/
|
||||||
class DungeonEditor : public Editor,
|
class DungeonEditor : public Editor,
|
||||||
public SharedROM,
|
public SharedRom,
|
||||||
public core::ExperimentFlags {
|
public core::ExperimentFlags {
|
||||||
public:
|
public:
|
||||||
absl::Status Update() override;
|
absl::Status Update() override;
|
||||||
@@ -46,9 +56,12 @@ class DungeonEditor : public Editor,
|
|||||||
void add_room(int i) { active_rooms_.push_back(i); }
|
void add_room(int i) { active_rooms_.push_back(i); }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
absl::Status Initialize();
|
||||||
|
absl::Status RefreshGraphics();
|
||||||
|
|
||||||
void LoadDungeonRoomSize();
|
void LoadDungeonRoomSize();
|
||||||
|
|
||||||
void UpdateDungeonRoomView();
|
absl::Status UpdateDungeonRoomView();
|
||||||
|
|
||||||
void DrawToolset();
|
void DrawToolset();
|
||||||
void DrawRoomSelector();
|
void DrawRoomSelector();
|
||||||
|
|||||||
@@ -51,12 +51,13 @@ absl::Status GraphicsEditor::Update() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
absl::Status GraphicsEditor::UpdateGfxEdit() {
|
absl::Status GraphicsEditor::UpdateGfxEdit() {
|
||||||
TAB_ITEM("Graphics Editor")
|
TAB_ITEM("Sheet Editor")
|
||||||
|
|
||||||
if (ImGui::BeginTable("##GfxEditTable", 3, kGfxEditTableFlags,
|
if (ImGui::BeginTable("##GfxEditTable", 3, kGfxEditTableFlags,
|
||||||
ImVec2(0, 0))) {
|
ImVec2(0, 0))) {
|
||||||
for (const auto& name : kGfxEditColumnNames)
|
for (const auto& name :
|
||||||
ImGui::TableSetupColumn(name.data());
|
{"Tilesheets", "Current Graphics", "Palette Controls"})
|
||||||
|
ImGui::TableSetupColumn(name);
|
||||||
|
|
||||||
ImGui::TableHeadersRow();
|
ImGui::TableHeadersRow();
|
||||||
|
|
||||||
@@ -321,10 +322,10 @@ absl::Status GraphicsEditor::UpdateGfxTabView() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
absl::Status GraphicsEditor::UpdatePaletteColumn() {
|
absl::Status GraphicsEditor::UpdatePaletteColumn() {
|
||||||
auto palette_group = rom()->palette_group(
|
auto palette_group = *rom()->palette_group().get_group(
|
||||||
kPaletteGroupAddressesKeys[edit_palette_group_name_index_]);
|
kPaletteGroupAddressesKeys[edit_palette_group_name_index_]);
|
||||||
|
|
||||||
auto palette = palette_group[edit_palette_index_];
|
auto palette = palette_group.palette(edit_palette_index_);
|
||||||
|
|
||||||
if (rom()->is_loaded()) {
|
if (rom()->is_loaded()) {
|
||||||
gui::TextWithSeparators("ROM Palette");
|
gui::TextWithSeparators("ROM Palette");
|
||||||
@@ -339,11 +340,13 @@ absl::Status GraphicsEditor::UpdatePaletteColumn() {
|
|||||||
gui::SelectablePalettePipeline(edit_palette_sub_index_, refresh_graphics_,
|
gui::SelectablePalettePipeline(edit_palette_sub_index_, refresh_graphics_,
|
||||||
palette);
|
palette);
|
||||||
|
|
||||||
if (refresh_graphics_) {
|
if (refresh_graphics_ && !open_sheets_.empty()) {
|
||||||
rom()->bitmap_manager()[current_sheet_]->ApplyPaletteWithTransparent(
|
RETURN_IF_ERROR(
|
||||||
palette, edit_palette_sub_index_);
|
rom()->bitmap_manager()[current_sheet_]->ApplyPaletteWithTransparent(
|
||||||
|
palette, edit_palette_sub_index_));
|
||||||
rom()->UpdateBitmap(
|
rom()->UpdateBitmap(
|
||||||
rom()->mutable_bitmap_manager()->mutable_bitmap(current_sheet_).get());
|
rom()->mutable_bitmap_manager()->mutable_bitmap(current_sheet_).get(),
|
||||||
|
true);
|
||||||
refresh_graphics_ = false;
|
refresh_graphics_ = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -353,22 +356,44 @@ absl::Status GraphicsEditor::UpdatePaletteColumn() {
|
|||||||
absl::Status GraphicsEditor::UpdateLinkGfxView() {
|
absl::Status GraphicsEditor::UpdateLinkGfxView() {
|
||||||
TAB_ITEM("Player Animations")
|
TAB_ITEM("Player Animations")
|
||||||
|
|
||||||
const auto link_gfx_offset = 0x80000;
|
if (ImGui::BeginTable("##PlayerAnimationTable", 3, kGfxEditTableFlags,
|
||||||
const auto link_gfx_length = 0x7000;
|
ImVec2(0, 0))) {
|
||||||
|
for (const auto& name : {"Canvas", "Animation Steps", "Properties"})
|
||||||
|
ImGui::TableSetupColumn(name);
|
||||||
|
|
||||||
// TODO: Finish Rom::LoadLinkGraphics and implement this
|
ImGui::TableHeadersRow();
|
||||||
if (ImGui::Button("Load Link Graphics (Experimental)")) {
|
|
||||||
if (rom()->is_loaded()) {
|
|
||||||
// Load Links graphics from the ROM
|
|
||||||
rom()->LoadLinkGraphics();
|
|
||||||
|
|
||||||
// Split it into the pose data frames
|
NEXT_COLUMN();
|
||||||
// Create an animation step display for the poses
|
link_canvas_.DrawBackground();
|
||||||
// Allow the user to modify the frames used in an anim step
|
link_canvas_.DrawGrid(16.0f);
|
||||||
// LinkOAM_AnimationSteps:
|
int i = 0;
|
||||||
// #_0D85FB
|
for (auto [key, link_sheet] : rom()->link_graphics()) {
|
||||||
|
int x_offset = 0;
|
||||||
|
int y_offset = core::kTilesheetHeight * i * 4;
|
||||||
|
link_canvas_.DrawBitmap(link_sheet, x_offset, y_offset, 4);
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
link_canvas_.DrawOverlay();
|
||||||
|
link_canvas_.DrawGrid();
|
||||||
|
|
||||||
|
NEXT_COLUMN();
|
||||||
|
ImGui::Text("Placeholder");
|
||||||
|
|
||||||
|
NEXT_COLUMN();
|
||||||
|
if (ImGui::Button("Load Link Graphics (Experimental)")) {
|
||||||
|
if (rom()->is_loaded()) {
|
||||||
|
// Load Links graphics from the ROM
|
||||||
|
RETURN_IF_ERROR(rom()->LoadLinkGraphics());
|
||||||
|
|
||||||
|
// Split it into the pose data frames
|
||||||
|
// Create an animation step display for the poses
|
||||||
|
// Allow the user to modify the frames used in an anim step
|
||||||
|
// LinkOAM_AnimationSteps:
|
||||||
|
// #_0D85FB
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
ImGui::EndTable();
|
||||||
|
|
||||||
END_TAB_ITEM()
|
END_TAB_ITEM()
|
||||||
return absl::OkStatus();
|
return absl::OkStatus();
|
||||||
@@ -477,8 +502,8 @@ absl::Status GraphicsEditor::DrawCgxImport() {
|
|||||||
[this]() { ImGui::SetClipboardText(cgx_file_path_); });
|
[this]() { ImGui::SetClipboardText(cgx_file_path_); });
|
||||||
|
|
||||||
gui::ButtonPipe("Load CGX Data", [this]() {
|
gui::ButtonPipe("Load CGX Data", [this]() {
|
||||||
status_ = gfx::LoadCgx(current_bpp_, cgx_file_path_, cgx_data_,
|
status_ = gfx::scad_format::LoadCgx(current_bpp_, cgx_file_path_, cgx_data_,
|
||||||
decoded_cgx_, extra_cgx_data_);
|
decoded_cgx_, extra_cgx_data_);
|
||||||
|
|
||||||
cgx_bitmap_.InitializeFromData(0x80, 0x200, 8, decoded_cgx_);
|
cgx_bitmap_.InitializeFromData(0x80, 0x200, 8, decoded_cgx_);
|
||||||
if (col_file_) {
|
if (col_file_) {
|
||||||
@@ -508,11 +533,12 @@ absl::Status GraphicsEditor::DrawScrImport() {
|
|||||||
InputInt("SCR Mod", &scr_mod_value_);
|
InputInt("SCR Mod", &scr_mod_value_);
|
||||||
|
|
||||||
gui::ButtonPipe("Load Scr Data", [this]() {
|
gui::ButtonPipe("Load Scr Data", [this]() {
|
||||||
status_ = gfx::LoadScr(scr_file_path_, scr_mod_value_, scr_data_);
|
status_ =
|
||||||
|
gfx::scad_format::LoadScr(scr_file_path_, scr_mod_value_, scr_data_);
|
||||||
|
|
||||||
decoded_scr_data_.resize(0x100 * 0x100);
|
decoded_scr_data_.resize(0x100 * 0x100);
|
||||||
status_ = gfx::DrawScrWithCgx(current_bpp_, scr_data_, decoded_scr_data_,
|
status_ = gfx::scad_format::DrawScrWithCgx(current_bpp_, scr_data_,
|
||||||
decoded_cgx_);
|
decoded_scr_data_, decoded_cgx_);
|
||||||
|
|
||||||
scr_bitmap_.InitializeFromData(0x100, 0x100, 8, decoded_scr_data_);
|
scr_bitmap_.InitializeFromData(0x100, 0x100, 8, decoded_scr_data_);
|
||||||
if (scr_loaded_) {
|
if (scr_loaded_) {
|
||||||
@@ -551,7 +577,7 @@ absl::Status GraphicsEditor::DrawPaletteControls() {
|
|||||||
col_file_palette_ = gfx::SnesPalette(col_data_);
|
col_file_palette_ = gfx::SnesPalette(col_data_);
|
||||||
|
|
||||||
// gigaleak dev format based code
|
// gigaleak dev format based code
|
||||||
decoded_col_ = gfx::DecodeColFile(col_file_path_);
|
decoded_col_ = gfx::scad_format::DecodeColFile(col_file_path_);
|
||||||
col_file_ = true;
|
col_file_ = true;
|
||||||
is_open_ = true;
|
is_open_ = true;
|
||||||
});
|
});
|
||||||
@@ -711,7 +737,7 @@ absl::Status GraphicsEditor::DecompressImportData(int size) {
|
|||||||
converted_sheet);
|
converted_sheet);
|
||||||
|
|
||||||
if (rom()->is_loaded()) {
|
if (rom()->is_loaded()) {
|
||||||
auto palette_group = rom()->palette_group("ow_main");
|
auto palette_group = rom()->palette_group().overworld_animated;
|
||||||
z3_rom_palette_ = palette_group[current_palette_];
|
z3_rom_palette_ = palette_group[current_palette_];
|
||||||
if (col_file_) {
|
if (col_file_) {
|
||||||
bin_bitmap_.ApplyPalette(col_file_palette_);
|
bin_bitmap_.ApplyPalette(col_file_palette_);
|
||||||
@@ -743,9 +769,10 @@ absl::Status GraphicsEditor::DecompressSuperDonkey() {
|
|||||||
col_file_palette_group_[current_palette_index_]);
|
col_file_palette_group_[current_palette_index_]);
|
||||||
} else {
|
} else {
|
||||||
// ROM palette
|
// ROM palette
|
||||||
auto palette_group =
|
|
||||||
rom()->palette_group(kPaletteGroupAddressesKeys[current_palette_]);
|
auto palette_group = rom()->palette_group().get_group(
|
||||||
z3_rom_palette_ = palette_group[current_palette_index_];
|
kPaletteGroupAddressesKeys[current_palette_]);
|
||||||
|
z3_rom_palette_ = *palette_group->mutable_palette(current_palette_index_);
|
||||||
graphics_bin_[i].ApplyPalette(z3_rom_palette_);
|
graphics_bin_[i].ApplyPalette(z3_rom_palette_);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -768,9 +795,9 @@ absl::Status GraphicsEditor::DecompressSuperDonkey() {
|
|||||||
col_file_palette_group_[current_palette_index_]);
|
col_file_palette_group_[current_palette_index_]);
|
||||||
} else {
|
} else {
|
||||||
// ROM palette
|
// ROM palette
|
||||||
auto palette_group =
|
auto palette_group = rom()->palette_group().get_group(
|
||||||
rom()->palette_group(kPaletteGroupAddressesKeys[current_palette_]);
|
kPaletteGroupAddressesKeys[current_palette_]);
|
||||||
z3_rom_palette_ = palette_group[current_palette_index_];
|
z3_rom_palette_ = *palette_group->mutable_palette(current_palette_index_);
|
||||||
graphics_bin_[i].ApplyPalette(z3_rom_palette_);
|
graphics_bin_[i].ApplyPalette(z3_rom_palette_);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -15,7 +15,7 @@
|
|||||||
#include "app/gui/input.h"
|
#include "app/gui/input.h"
|
||||||
#include "app/gui/pipeline.h"
|
#include "app/gui/pipeline.h"
|
||||||
#include "app/rom.h"
|
#include "app/rom.h"
|
||||||
#include "app/zelda3/overworld.h"
|
#include "app/zelda3/overworld/overworld.h"
|
||||||
|
|
||||||
namespace yaze {
|
namespace yaze {
|
||||||
namespace app {
|
namespace app {
|
||||||
@@ -50,9 +50,6 @@ constexpr const char* kPaletteGroupAddressesKeys[] = {
|
|||||||
"grass", "3d_object", "ow_mini_map",
|
"grass", "3d_object", "ow_mini_map",
|
||||||
};
|
};
|
||||||
|
|
||||||
static constexpr std::string_view kGfxEditColumnNames[] = {
|
|
||||||
"Tilesheets", "Current Graphics", "Palette Controls"};
|
|
||||||
|
|
||||||
static constexpr absl::string_view kGfxToolsetColumnNames[] = {
|
static constexpr absl::string_view kGfxToolsetColumnNames[] = {
|
||||||
"#memoryEditor",
|
"#memoryEditor",
|
||||||
"##separator_gfx1",
|
"##separator_gfx1",
|
||||||
@@ -62,7 +59,21 @@ constexpr ImGuiTableFlags kGfxEditFlags = ImGuiTableFlags_Reorderable |
|
|||||||
ImGuiTableFlags_Resizable |
|
ImGuiTableFlags_Resizable |
|
||||||
ImGuiTableFlags_SizingStretchSame;
|
ImGuiTableFlags_SizingStretchSame;
|
||||||
|
|
||||||
class GraphicsEditor : public SharedROM {
|
/**
|
||||||
|
* @class GraphicsEditor
|
||||||
|
* @brief Allows the user to edit graphics sheets from the game or view
|
||||||
|
* prototype graphics.
|
||||||
|
*
|
||||||
|
* The GraphicsEditor class is responsible for providing functionality to edit
|
||||||
|
* graphics sheets from the game or view prototype graphics of Link to the Past
|
||||||
|
* from the CGX, SCR, and OBJ formats. It provides various methods to update
|
||||||
|
* different components of the graphics editor, such as the graphics edit tab,
|
||||||
|
* link graphics view, and prototype graphics viewer. It also includes import
|
||||||
|
* functions for different file formats, as well as other utility functions for
|
||||||
|
* drawing toolsets, palette controls, clipboard imports, experimental features,
|
||||||
|
* and memory editor.
|
||||||
|
*/
|
||||||
|
class GraphicsEditor : public SharedRom {
|
||||||
public:
|
public:
|
||||||
absl::Status Update();
|
absl::Status Update();
|
||||||
|
|
||||||
@@ -154,9 +165,9 @@ class GraphicsEditor : public SharedROM {
|
|||||||
|
|
||||||
GfxEditMode gfx_edit_mode_ = GfxEditMode::kSelect;
|
GfxEditMode gfx_edit_mode_ = GfxEditMode::kSelect;
|
||||||
|
|
||||||
ROM temp_rom_;
|
Rom temp_rom_;
|
||||||
ROM tilemap_rom_;
|
Rom tilemap_rom_;
|
||||||
zelda3::Overworld overworld_;
|
zelda3::overworld::Overworld overworld_;
|
||||||
MemoryEditor cgx_memory_editor_;
|
MemoryEditor cgx_memory_editor_;
|
||||||
MemoryEditor col_memory_editor_;
|
MemoryEditor col_memory_editor_;
|
||||||
PaletteEditor palette_editor_;
|
PaletteEditor palette_editor_;
|
||||||
@@ -184,6 +195,9 @@ class GraphicsEditor : public SharedROM {
|
|||||||
gui::Canvas super_donkey_canvas_;
|
gui::Canvas super_donkey_canvas_;
|
||||||
gui::Canvas current_sheet_canvas_{ImVec2(0x80, 0x20),
|
gui::Canvas current_sheet_canvas_{ImVec2(0x80, 0x20),
|
||||||
gui::CanvasGridSize::k8x8};
|
gui::CanvasGridSize::k8x8};
|
||||||
|
gui::Canvas link_canvas_{
|
||||||
|
ImVec2(core::kTilesheetWidth * 4, core::kTilesheetHeight * 0x10 * 4),
|
||||||
|
gui::CanvasGridSize::k16x16};
|
||||||
absl::Status status_;
|
absl::Status status_;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -151,6 +151,8 @@ absl::Status MasterEditor::Update() {
|
|||||||
DrawInfoPopup();
|
DrawInfoPopup();
|
||||||
|
|
||||||
if (rom()->is_loaded() && !rom_assets_loaded_) {
|
if (rom()->is_loaded() && !rom_assets_loaded_) {
|
||||||
|
// Load all of the graphics data from the game.
|
||||||
|
RETURN_IF_ERROR(rom()->LoadAllGraphicsData())
|
||||||
// Initialize overworld graphics, maps, and palettes
|
// Initialize overworld graphics, maps, and palettes
|
||||||
RETURN_IF_ERROR(overworld_editor_.LoadGraphics());
|
RETURN_IF_ERROR(overworld_editor_.LoadGraphics());
|
||||||
rom_assets_loaded_ = true;
|
rom_assets_loaded_ = true;
|
||||||
@@ -464,7 +466,43 @@ void MasterEditor::DrawViewMenu() {
|
|||||||
|
|
||||||
if (show_memory_editor) {
|
if (show_memory_editor) {
|
||||||
static MemoryEditor mem_edit;
|
static MemoryEditor mem_edit;
|
||||||
mem_edit.DrawWindow("Memory Editor", (void*)&(*rom()), rom()->size());
|
static MemoryEditor comp_edit;
|
||||||
|
static bool show_compare_rom = false;
|
||||||
|
static Rom comparison_rom;
|
||||||
|
ImGui::Begin("Hex Editor", &show_memory_editor);
|
||||||
|
if (ImGui::Button("Compare Rom")) {
|
||||||
|
auto file_name = FileDialogWrapper::ShowOpenFileDialog();
|
||||||
|
PRINT_IF_ERROR(comparison_rom.LoadFromFile(file_name));
|
||||||
|
show_compare_rom = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint64_t convert_address = 0;
|
||||||
|
gui::InputHex("SNES to PC", (int*)&convert_address, 6, 200.f);
|
||||||
|
ImGui::SameLine();
|
||||||
|
ImGui::Text("%x", core::SnesToPc(convert_address));
|
||||||
|
|
||||||
|
// mem_edit.DrawWindow("Memory Editor", (void*)&(*rom()), rom()->size());
|
||||||
|
BEGIN_TABLE("Memory Comparison", 2, ImGuiTableFlags_Resizable);
|
||||||
|
SETUP_COLUMN("Source")
|
||||||
|
SETUP_COLUMN("Dest")
|
||||||
|
|
||||||
|
NEXT_COLUMN()
|
||||||
|
ImGui::Text("%s", rom()->filename().data());
|
||||||
|
mem_edit.DrawContents((void*)&(*rom()), rom()->size());
|
||||||
|
|
||||||
|
NEXT_COLUMN()
|
||||||
|
if (show_compare_rom) {
|
||||||
|
comp_edit.SetComparisonData((void*)&(*rom()));
|
||||||
|
ImGui::BeginGroup();
|
||||||
|
ImGui::BeginChild("Comparison ROM");
|
||||||
|
ImGui::Text("%s", comparison_rom.filename().data());
|
||||||
|
comp_edit.DrawContents((void*)&(comparison_rom), comparison_rom.size());
|
||||||
|
ImGui::EndChild();
|
||||||
|
ImGui::EndGroup();
|
||||||
|
}
|
||||||
|
END_TABLE()
|
||||||
|
|
||||||
|
ImGui::End();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (show_imgui_demo) {
|
if (show_imgui_demo) {
|
||||||
|
|||||||
@@ -12,7 +12,6 @@
|
|||||||
#include "absl/status/status.h"
|
#include "absl/status/status.h"
|
||||||
#include "app/core/common.h"
|
#include "app/core/common.h"
|
||||||
#include "app/core/constants.h"
|
#include "app/core/constants.h"
|
||||||
#include "app/gui/pipeline.h"
|
|
||||||
#include "app/editor/context/gfx_context.h"
|
#include "app/editor/context/gfx_context.h"
|
||||||
#include "app/editor/dungeon_editor.h"
|
#include "app/editor/dungeon_editor.h"
|
||||||
#include "app/editor/graphics_editor.h"
|
#include "app/editor/graphics_editor.h"
|
||||||
@@ -28,14 +27,33 @@
|
|||||||
#include "app/gui/canvas.h"
|
#include "app/gui/canvas.h"
|
||||||
#include "app/gui/icons.h"
|
#include "app/gui/icons.h"
|
||||||
#include "app/gui/input.h"
|
#include "app/gui/input.h"
|
||||||
|
#include "app/gui/pipeline.h"
|
||||||
#include "app/rom.h"
|
#include "app/rom.h"
|
||||||
|
|
||||||
namespace yaze {
|
namespace yaze {
|
||||||
namespace app {
|
namespace app {
|
||||||
namespace editor {
|
namespace editor {
|
||||||
|
|
||||||
class MasterEditor : public SharedROM,
|
/**
|
||||||
public GfxContext,
|
* @class MasterEditor
|
||||||
|
* @brief The MasterEditor class represents the main editor for a Rom in the
|
||||||
|
* Yaze application.
|
||||||
|
*
|
||||||
|
* 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
|
||||||
|
* 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 {
|
public core::ExperimentFlags {
|
||||||
public:
|
public:
|
||||||
MasterEditor() { current_editor_ = &overworld_editor_; }
|
MasterEditor() { current_editor_ = &overworld_editor_; }
|
||||||
|
|||||||
@@ -12,6 +12,10 @@ namespace yaze {
|
|||||||
namespace app {
|
namespace app {
|
||||||
namespace editor {
|
namespace editor {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @class AssemblyEditor
|
||||||
|
* @brief Text editor for modifying assembly code.
|
||||||
|
*/
|
||||||
class AssemblyEditor {
|
class AssemblyEditor {
|
||||||
public:
|
public:
|
||||||
AssemblyEditor();
|
AssemblyEditor();
|
||||||
|
|||||||
@@ -6,8 +6,8 @@
|
|||||||
|
|
||||||
#include "absl/status/status.h"
|
#include "absl/status/status.h"
|
||||||
#include "absl/status/statusor.h"
|
#include "absl/status/statusor.h"
|
||||||
#include "app/core/editor.h"
|
|
||||||
#include "app/editor/modules/palette_editor.h"
|
#include "app/editor/modules/palette_editor.h"
|
||||||
|
#include "app/editor/utils/editor.h"
|
||||||
#include "app/gfx/bitmap.h"
|
#include "app/gfx/bitmap.h"
|
||||||
#include "app/gfx/snes_palette.h"
|
#include "app/gfx/snes_palette.h"
|
||||||
#include "app/gfx/snes_tile.h"
|
#include "app/gfx/snes_tile.h"
|
||||||
@@ -18,7 +18,7 @@
|
|||||||
#include "app/gui/pipeline.h"
|
#include "app/gui/pipeline.h"
|
||||||
#include "app/gui/widgets.h"
|
#include "app/gui/widgets.h"
|
||||||
#include "app/rom.h"
|
#include "app/rom.h"
|
||||||
#include "app/zelda3/overworld.h"
|
#include "app/zelda3/overworld/overworld.h"
|
||||||
|
|
||||||
namespace yaze {
|
namespace yaze {
|
||||||
namespace app {
|
namespace app {
|
||||||
@@ -220,29 +220,20 @@ void GfxGroupEditor::DrawPaletteViewer() {
|
|||||||
gui::InputHexByte("Dungeon Spr Pal 2", &dungeon_spr_pal_2_val);
|
gui::InputHexByte("Dungeon Spr Pal 2", &dungeon_spr_pal_2_val);
|
||||||
gui::InputHexByte("Dungeon Spr Pal 3", &dungeon_spr_pal_3_val);
|
gui::InputHexByte("Dungeon Spr Pal 3", &dungeon_spr_pal_3_val);
|
||||||
|
|
||||||
auto &palette =
|
auto &palette = *rom()->mutable_palette_group()->dungeon_main.mutable_palette(
|
||||||
*rom()
|
rom()->paletteset_ids[selected_paletteset][0]);
|
||||||
->mutable_palette_group(
|
|
||||||
"dungeon_main")[rom()->paletteset_ids[selected_paletteset][0]]
|
|
||||||
.mutable_palette(0);
|
|
||||||
DrawPaletteFromPaletteGroup(palette);
|
DrawPaletteFromPaletteGroup(palette);
|
||||||
auto &spr_aux_pal1 =
|
auto &spr_aux_pal1 =
|
||||||
*rom()
|
*rom()->mutable_palette_group()->sprites_aux1.mutable_palette(
|
||||||
->mutable_palette_group(
|
rom()->paletteset_ids[selected_paletteset][1]);
|
||||||
"sprites_aux1")[rom()->paletteset_ids[selected_paletteset][1]]
|
|
||||||
.mutable_palette(0);
|
|
||||||
DrawPaletteFromPaletteGroup(spr_aux_pal1);
|
DrawPaletteFromPaletteGroup(spr_aux_pal1);
|
||||||
auto &spr_aux_pal2 =
|
auto &spr_aux_pal2 =
|
||||||
*rom()
|
*rom()->mutable_palette_group()->sprites_aux2.mutable_palette(
|
||||||
->mutable_palette_group(
|
rom()->paletteset_ids[selected_paletteset][2]);
|
||||||
"sprites_aux2")[rom()->paletteset_ids[selected_paletteset][2]]
|
|
||||||
.mutable_palette(0);
|
|
||||||
DrawPaletteFromPaletteGroup(spr_aux_pal2);
|
DrawPaletteFromPaletteGroup(spr_aux_pal2);
|
||||||
auto &spr_aux_pal3 =
|
auto &spr_aux_pal3 =
|
||||||
*rom()
|
*rom()->mutable_palette_group()->sprites_aux3.mutable_palette(
|
||||||
->mutable_palette_group(
|
rom()->paletteset_ids[selected_paletteset][3]);
|
||||||
"sprites_aux3")[rom()->paletteset_ids[selected_paletteset][3]]
|
|
||||||
.mutable_palette(0);
|
|
||||||
DrawPaletteFromPaletteGroup(spr_aux_pal3);
|
DrawPaletteFromPaletteGroup(spr_aux_pal3);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -7,7 +7,7 @@
|
|||||||
|
|
||||||
#include "absl/status/status.h"
|
#include "absl/status/status.h"
|
||||||
#include "absl/status/statusor.h"
|
#include "absl/status/statusor.h"
|
||||||
#include "app/core/editor.h"
|
#include "app/editor/utils/editor.h"
|
||||||
#include "app/editor/modules/palette_editor.h"
|
#include "app/editor/modules/palette_editor.h"
|
||||||
#include "app/gfx/bitmap.h"
|
#include "app/gfx/bitmap.h"
|
||||||
#include "app/gfx/snes_palette.h"
|
#include "app/gfx/snes_palette.h"
|
||||||
@@ -17,13 +17,17 @@
|
|||||||
#include "app/gui/pipeline.h"
|
#include "app/gui/pipeline.h"
|
||||||
#include "app/gui/widgets.h"
|
#include "app/gui/widgets.h"
|
||||||
#include "app/rom.h"
|
#include "app/rom.h"
|
||||||
#include "app/zelda3/overworld.h"
|
#include "app/zelda3/overworld/overworld.h"
|
||||||
|
|
||||||
namespace yaze {
|
namespace yaze {
|
||||||
namespace app {
|
namespace app {
|
||||||
namespace editor {
|
namespace editor {
|
||||||
|
|
||||||
class GfxGroupEditor : public SharedROM {
|
/**
|
||||||
|
* @class GfxGroupEditor
|
||||||
|
* @brief Manage graphics group configurations in a Rom.
|
||||||
|
*/
|
||||||
|
class GfxGroupEditor : public SharedRom {
|
||||||
public:
|
public:
|
||||||
absl::Status Update();
|
absl::Status Update();
|
||||||
|
|
||||||
@@ -61,7 +65,7 @@ class GfxGroupEditor : public SharedROM {
|
|||||||
std::vector<gfx::Bitmap> tile16_individual_;
|
std::vector<gfx::Bitmap> tile16_individual_;
|
||||||
|
|
||||||
gui::BitmapViewer gfx_group_viewer_;
|
gui::BitmapViewer gfx_group_viewer_;
|
||||||
zelda3::Overworld overworld_;
|
zelda3::overworld::Overworld overworld_;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace editor
|
} // namespace editor
|
||||||
|
|||||||
@@ -54,7 +54,12 @@ static const char* kGameSongs[] = {"Title",
|
|||||||
static constexpr absl::string_view kSongNotes[] = {
|
static constexpr absl::string_view kSongNotes[] = {
|
||||||
"C", "C#", "D", "D#", "E", "F", "F#", "G", "G#", "A", "A#", "B", "C",
|
"C", "C#", "D", "D#", "E", "F", "F#", "G", "G#", "A", "A#", "B", "C",
|
||||||
"C#", "D", "D#", "E", "F", "F#", "G", "G#", "A", "A#", "B", "C"};
|
"C#", "D", "D#", "E", "F", "F#", "G", "G#", "A", "A#", "B", "C"};
|
||||||
class MusicEditor : public SharedROM {
|
|
||||||
|
/**
|
||||||
|
* @class MusicEditor
|
||||||
|
* @brief A class for editing music data in a Rom.
|
||||||
|
*/
|
||||||
|
class MusicEditor : public SharedRom {
|
||||||
public:
|
public:
|
||||||
void Update();
|
void Update();
|
||||||
|
|
||||||
@@ -65,7 +70,7 @@ class MusicEditor : public SharedROM {
|
|||||||
void DrawSongToolset();
|
void DrawSongToolset();
|
||||||
void DrawToolset();
|
void DrawToolset();
|
||||||
|
|
||||||
zelda3::Tracker music_tracker_;
|
zelda3::music::Tracker music_tracker_;
|
||||||
|
|
||||||
// Mix_Music* current_song_ = NULL;
|
// Mix_Music* current_song_ = NULL;
|
||||||
|
|
||||||
|
|||||||
@@ -75,30 +75,32 @@ absl::Status PaletteEditor::Update() {
|
|||||||
return absl::OkStatus();
|
return absl::OkStatus();
|
||||||
}
|
}
|
||||||
|
|
||||||
void PaletteEditor::EditColorInPalette(gfx::SnesPalette& palette, int index) {
|
absl::Status PaletteEditor::EditColorInPalette(gfx::SnesPalette& palette,
|
||||||
|
int index) {
|
||||||
if (index >= palette.size()) {
|
if (index >= palette.size()) {
|
||||||
// Handle error: the index is out of bounds
|
return absl::InvalidArgumentError("Index out of bounds");
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get the current color
|
// Get the current color
|
||||||
auto currentColor = palette.GetColor(index).rgb();
|
ASSIGN_OR_RETURN(auto color, palette.GetColor(index));
|
||||||
|
auto currentColor = color.rgb();
|
||||||
if (ImGui::ColorPicker4("Color Picker", (float*)&palette[index])) {
|
if (ImGui::ColorPicker4("Color Picker", (float*)&palette[index])) {
|
||||||
// The color was modified, update it in the palette
|
// The color was modified, update it in the palette
|
||||||
palette(index, currentColor);
|
palette(index, currentColor);
|
||||||
}
|
}
|
||||||
|
return absl::OkStatus();
|
||||||
}
|
}
|
||||||
|
|
||||||
void PaletteEditor::ResetColorToOriginal(
|
absl::Status PaletteEditor::ResetColorToOriginal(
|
||||||
gfx::SnesPalette& palette, int index,
|
gfx::SnesPalette& palette, int index,
|
||||||
const gfx::SnesPalette& originalPalette) {
|
const gfx::SnesPalette& originalPalette) {
|
||||||
if (index >= palette.size() || index >= originalPalette.size()) {
|
if (index >= palette.size() || index >= originalPalette.size()) {
|
||||||
// Handle error: the index is out of bounds
|
return absl::InvalidArgumentError("Index out of bounds");
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
ASSIGN_OR_RETURN(auto color, originalPalette.GetColor(index));
|
||||||
auto originalColor = originalPalette.GetColor(index).rgb();
|
auto originalColor = color.rgb();
|
||||||
palette(index, originalColor);
|
palette(index, originalColor);
|
||||||
|
return absl::OkStatus();
|
||||||
}
|
}
|
||||||
|
|
||||||
absl::Status PaletteEditor::DrawPaletteGroup(int category) {
|
absl::Status PaletteEditor::DrawPaletteGroup(int category) {
|
||||||
@@ -106,17 +108,17 @@ absl::Status PaletteEditor::DrawPaletteGroup(int category) {
|
|||||||
return absl::NotFoundError("ROM not open, no palettes to display");
|
return absl::NotFoundError("ROM not open, no palettes to display");
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto size =
|
std::string group_name = kPaletteGroupNames[category].data();
|
||||||
rom()->palette_group(kPaletteGroupNames[category].data()).size();
|
auto palette_group = *rom()->palette_group().get_group(group_name);
|
||||||
auto palettes =
|
const auto size = palette_group.size();
|
||||||
rom()->mutable_palette_group(kPaletteGroupNames[category].data());
|
|
||||||
static bool edit_color = false;
|
static bool edit_color = false;
|
||||||
for (int j = 0; j < size; j++) {
|
for (int j = 0; j < size; j++) {
|
||||||
// ImGui::Text("%d", j);
|
// ImGui::Text("%d", j);
|
||||||
rom()->resource_label()->SelectableLabelWithNameEdit(
|
rom()->resource_label()->SelectableLabelWithNameEdit(
|
||||||
false, "Palette Group Name", std::to_string(j),
|
false, "Palette Group Name", std::to_string(j),
|
||||||
std::string(kPaletteGroupNames[category]));
|
std::string(kPaletteGroupNames[category]));
|
||||||
auto palette = palettes->mutable_palette(j);
|
auto palette = palette_group.mutable_palette(j);
|
||||||
auto pal_size = palette->size();
|
auto pal_size = palette->size();
|
||||||
|
|
||||||
for (int n = 0; n < pal_size; n++) {
|
for (int n = 0; n < pal_size; n++) {
|
||||||
@@ -129,7 +131,7 @@ absl::Status PaletteEditor::DrawPaletteGroup(int category) {
|
|||||||
// Small icon of the color in the palette
|
// Small icon of the color in the palette
|
||||||
if (gui::SnesColorButton(popup_id, *palette->mutable_color(n),
|
if (gui::SnesColorButton(popup_id, *palette->mutable_color(n),
|
||||||
palette_button_flags)) {
|
palette_button_flags)) {
|
||||||
current_color_ = palette->GetColor(n);
|
ASSIGN_OR_RETURN(current_color_, palette->GetColor(n));
|
||||||
// EditColorInPalette(*palette, n);
|
// EditColorInPalette(*palette, n);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -152,8 +154,7 @@ absl::Status PaletteEditor::HandleColorPopup(gfx::SnesPalette& palette, int i,
|
|||||||
int j, int n) {
|
int j, int n) {
|
||||||
auto col = gfx::ToFloatArray(palette[n]);
|
auto col = gfx::ToFloatArray(palette[n]);
|
||||||
if (gui::SnesColorEdit4("Edit Color", palette[n], color_popup_flags)) {
|
if (gui::SnesColorEdit4("Edit Color", palette[n], color_popup_flags)) {
|
||||||
RETURN_IF_ERROR(rom()->UpdatePaletteColor(kPaletteGroupNames[i].data(), j,
|
// TODO: Implement new update color function
|
||||||
n, palette[n]))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ImGui::Button("Copy as..", ImVec2(-1, 0))) ImGui::OpenPopup("Copy");
|
if (ImGui::Button("Copy as..", ImVec2(-1, 0))) ImGui::OpenPopup("Copy");
|
||||||
|
|||||||
@@ -25,6 +25,7 @@ static constexpr absl::string_view kPaletteGroupNames[] = {
|
|||||||
"ow_aux", "global_sprites", "dungeon_main", "ow_mini_map",
|
"ow_aux", "global_sprites", "dungeon_main", "ow_mini_map",
|
||||||
"ow_mini_map", "3d_object", "3d_object"};
|
"ow_mini_map", "3d_object", "3d_object"};
|
||||||
|
|
||||||
|
namespace palette_internal {
|
||||||
struct PaletteChange {
|
struct PaletteChange {
|
||||||
std::string group_name;
|
std::string group_name;
|
||||||
size_t palette_index;
|
size_t palette_index;
|
||||||
@@ -74,15 +75,20 @@ class PaletteEditorHistory {
|
|||||||
std::deque<PaletteChange> recentChanges;
|
std::deque<PaletteChange> recentChanges;
|
||||||
static const size_t maxHistorySize = 50; // or any other number you deem fit
|
static const size_t maxHistorySize = 50; // or any other number you deem fit
|
||||||
};
|
};
|
||||||
|
} // namespace palette_internal
|
||||||
|
|
||||||
class PaletteEditor : public SharedROM {
|
/**
|
||||||
|
* @class PaletteEditor
|
||||||
|
* @brief Allows the user to view and edit in game palettes.
|
||||||
|
*/
|
||||||
|
class PaletteEditor : public SharedRom {
|
||||||
public:
|
public:
|
||||||
absl::Status Update();
|
absl::Status Update();
|
||||||
absl::Status DrawPaletteGroups();
|
absl::Status DrawPaletteGroups();
|
||||||
|
|
||||||
void EditColorInPalette(gfx::SnesPalette& palette, int index);
|
absl::Status EditColorInPalette(gfx::SnesPalette& palette, int index);
|
||||||
void ResetColorToOriginal(gfx::SnesPalette& palette, int index,
|
absl::Status ResetColorToOriginal(gfx::SnesPalette& palette, int index,
|
||||||
const gfx::SnesPalette& originalPalette);
|
const gfx::SnesPalette& originalPalette);
|
||||||
void DisplayPalette(gfx::SnesPalette& palette, bool loaded);
|
void DisplayPalette(gfx::SnesPalette& palette, bool loaded);
|
||||||
void DrawPortablePalette(gfx::SnesPalette& palette);
|
void DrawPortablePalette(gfx::SnesPalette& palette);
|
||||||
absl::Status DrawPaletteGroup(int category);
|
absl::Status DrawPaletteGroup(int category);
|
||||||
@@ -90,18 +96,20 @@ class PaletteEditor : public SharedROM {
|
|||||||
private:
|
private:
|
||||||
absl::Status HandleColorPopup(gfx::SnesPalette& palette, int i, int j, int n);
|
absl::Status HandleColorPopup(gfx::SnesPalette& palette, int i, int j, int n);
|
||||||
|
|
||||||
void InitializeSavedPalette(const gfx::SnesPalette& palette) {
|
absl::Status InitializeSavedPalette(const gfx::SnesPalette& palette) {
|
||||||
for (int n = 0; n < palette.size(); n++) {
|
for (int n = 0; n < palette.size(); n++) {
|
||||||
saved_palette_[n].x = palette.GetColor(n).rgb().x / 255;
|
ASSIGN_OR_RETURN(auto color, palette.GetColor(n));
|
||||||
saved_palette_[n].y = palette.GetColor(n).rgb().y / 255;
|
saved_palette_[n].x = color.rgb().x / 255;
|
||||||
saved_palette_[n].z = palette.GetColor(n).rgb().z / 255;
|
saved_palette_[n].y = color.rgb().y / 255;
|
||||||
|
saved_palette_[n].z = color.rgb().z / 255;
|
||||||
saved_palette_[n].w = 255; // Alpha
|
saved_palette_[n].w = 255; // Alpha
|
||||||
}
|
}
|
||||||
|
return absl::OkStatus();
|
||||||
}
|
}
|
||||||
|
|
||||||
absl::Status status_;
|
absl::Status status_;
|
||||||
|
|
||||||
PaletteEditorHistory history_;
|
palette_internal::PaletteEditorHistory history_;
|
||||||
|
|
||||||
ImVec4 saved_palette_[256] = {};
|
ImVec4 saved_palette_[256] = {};
|
||||||
gfx::SnesColor current_color_;
|
gfx::SnesColor current_color_;
|
||||||
|
|||||||
@@ -6,11 +6,12 @@
|
|||||||
|
|
||||||
#include "absl/status/status.h"
|
#include "absl/status/status.h"
|
||||||
#include "absl/status/statusor.h"
|
#include "absl/status/statusor.h"
|
||||||
#include "app/core/editor.h"
|
|
||||||
#include "app/editor/modules/palette_editor.h"
|
#include "app/editor/modules/palette_editor.h"
|
||||||
|
#include "app/editor/utils/editor.h"
|
||||||
#include "app/gfx/bitmap.h"
|
#include "app/gfx/bitmap.h"
|
||||||
#include "app/gfx/snes_palette.h"
|
#include "app/gfx/snes_palette.h"
|
||||||
#include "app/gfx/snes_tile.h"
|
#include "app/gfx/snes_tile.h"
|
||||||
|
#include "app/gfx/tilesheet.h"
|
||||||
#include "app/gui/canvas.h"
|
#include "app/gui/canvas.h"
|
||||||
#include "app/gui/icons.h"
|
#include "app/gui/icons.h"
|
||||||
#include "app/gui/input.h"
|
#include "app/gui/input.h"
|
||||||
@@ -18,7 +19,7 @@
|
|||||||
#include "app/gui/style.h"
|
#include "app/gui/style.h"
|
||||||
#include "app/gui/widgets.h"
|
#include "app/gui/widgets.h"
|
||||||
#include "app/rom.h"
|
#include "app/rom.h"
|
||||||
#include "app/zelda3/overworld.h"
|
#include "app/zelda3/overworld/overworld.h"
|
||||||
|
|
||||||
namespace yaze {
|
namespace yaze {
|
||||||
namespace app {
|
namespace app {
|
||||||
@@ -50,6 +51,7 @@ absl::Status Tile16Editor::Update() {
|
|||||||
*tile8_source_canvas_.custom_labels_enabled() = true;
|
*tile8_source_canvas_.custom_labels_enabled() = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
RETURN_IF_ERROR(DrawMenu());
|
||||||
if (BeginTabBar("Tile16 Editor Tabs")) {
|
if (BeginTabBar("Tile16 Editor Tabs")) {
|
||||||
RETURN_IF_ERROR(DrawTile16Editor());
|
RETURN_IF_ERROR(DrawTile16Editor());
|
||||||
RETURN_IF_ERROR(UpdateTile16Transfer());
|
RETURN_IF_ERROR(UpdateTile16Transfer());
|
||||||
@@ -59,11 +61,25 @@ absl::Status Tile16Editor::Update() {
|
|||||||
return absl::OkStatus();
|
return absl::OkStatus();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
absl::Status Tile16Editor::DrawMenu() {
|
||||||
|
if (ImGui::BeginMenuBar()) {
|
||||||
|
if (ImGui::BeginMenu("View")) {
|
||||||
|
ImGui::Checkbox("Show Collision Types",
|
||||||
|
tile8_source_canvas_.custom_labels_enabled());
|
||||||
|
ImGui::EndMenu();
|
||||||
|
}
|
||||||
|
|
||||||
|
ImGui::EndMenuBar();
|
||||||
|
}
|
||||||
|
|
||||||
|
return absl::OkStatus();
|
||||||
|
}
|
||||||
|
|
||||||
absl::Status Tile16Editor::DrawTile16Editor() {
|
absl::Status Tile16Editor::DrawTile16Editor() {
|
||||||
if (BeginTabItem("Tile16 Editing")) {
|
if (BeginTabItem("Tile16 Editing")) {
|
||||||
if (BeginTable("#Tile16EditorTable", 2, TABLE_BORDERS_RESIZABLE,
|
if (BeginTable("#Tile16EditorTable", 2, TABLE_BORDERS_RESIZABLE,
|
||||||
ImVec2(0, 0))) {
|
ImVec2(0, 0))) {
|
||||||
TableSetupColumn("Tiles", ImGuiTableColumnFlags_WidthFixed,
|
TableSetupColumn("Blockset", ImGuiTableColumnFlags_WidthFixed,
|
||||||
ImGui::GetContentRegionAvail().x);
|
ImGui::GetContentRegionAvail().x);
|
||||||
TableSetupColumn("Properties", ImGuiTableColumnFlags_WidthStretch,
|
TableSetupColumn("Properties", ImGuiTableColumnFlags_WidthStretch,
|
||||||
ImGui::GetContentRegionAvail().x);
|
ImGui::GetContentRegionAvail().x);
|
||||||
@@ -74,31 +90,7 @@ absl::Status Tile16Editor::DrawTile16Editor() {
|
|||||||
|
|
||||||
TableNextColumn();
|
TableNextColumn();
|
||||||
RETURN_IF_ERROR(UpdateTile16Edit());
|
RETURN_IF_ERROR(UpdateTile16Edit());
|
||||||
|
RETURN_IF_ERROR(DrawTileEditControls());
|
||||||
ImGui::EndTable();
|
|
||||||
}
|
|
||||||
|
|
||||||
ImGui::EndTabItem();
|
|
||||||
}
|
|
||||||
return absl::OkStatus();
|
|
||||||
}
|
|
||||||
|
|
||||||
absl::Status Tile16Editor::UpdateTile16Transfer() {
|
|
||||||
if (BeginTabItem("Tile16 Transfer")) {
|
|
||||||
if (BeginTable("#Tile16TransferTable", 2, TABLE_BORDERS_RESIZABLE,
|
|
||||||
ImVec2(0, 0))) {
|
|
||||||
TableSetupColumn("Current ROM Tiles", ImGuiTableColumnFlags_WidthFixed,
|
|
||||||
ImGui::GetContentRegionAvail().x / 2);
|
|
||||||
TableSetupColumn("Transfer ROM Tiles", ImGuiTableColumnFlags_WidthFixed,
|
|
||||||
ImGui::GetContentRegionAvail().x / 2);
|
|
||||||
TableHeadersRow();
|
|
||||||
TableNextRow();
|
|
||||||
|
|
||||||
TableNextColumn();
|
|
||||||
RETURN_IF_ERROR(UpdateBlockset());
|
|
||||||
|
|
||||||
TableNextColumn();
|
|
||||||
RETURN_IF_ERROR(UpdateTransferTileCanvas());
|
|
||||||
|
|
||||||
ImGui::EndTable();
|
ImGui::EndTable();
|
||||||
}
|
}
|
||||||
@@ -113,12 +105,14 @@ absl::Status Tile16Editor::UpdateBlockset() {
|
|||||||
gui::BeginChildWithScrollbar("##Tile16EditorBlocksetScrollRegion");
|
gui::BeginChildWithScrollbar("##Tile16EditorBlocksetScrollRegion");
|
||||||
blockset_canvas_.DrawBackground();
|
blockset_canvas_.DrawBackground();
|
||||||
gui::EndPadding();
|
gui::EndPadding();
|
||||||
blockset_canvas_.DrawContextMenu();
|
{
|
||||||
blockset_canvas_.DrawTileSelector(32);
|
blockset_canvas_.DrawContextMenu();
|
||||||
blockset_canvas_.DrawBitmap(tile16_blockset_bmp_, 0, map_blockset_loaded_);
|
blockset_canvas_.DrawTileSelector(32);
|
||||||
blockset_canvas_.DrawGrid();
|
blockset_canvas_.DrawBitmap(tile16_blockset_bmp_, 0, map_blockset_loaded_);
|
||||||
blockset_canvas_.DrawOverlay();
|
blockset_canvas_.DrawGrid();
|
||||||
ImGui::EndChild();
|
blockset_canvas_.DrawOverlay();
|
||||||
|
ImGui::EndChild();
|
||||||
|
}
|
||||||
|
|
||||||
if (!blockset_canvas_.points().empty()) {
|
if (!blockset_canvas_.points().empty()) {
|
||||||
uint16_t x = blockset_canvas_.points().front().x / 32;
|
uint16_t x = blockset_canvas_.points().front().x / 32;
|
||||||
@@ -130,8 +124,9 @@ absl::Status Tile16Editor::UpdateBlockset() {
|
|||||||
if (notify_tile16.modified()) {
|
if (notify_tile16.modified()) {
|
||||||
current_tile16_ = notify_tile16.get();
|
current_tile16_ = notify_tile16.get();
|
||||||
current_tile16_bmp_ = tile16_individual_[notify_tile16];
|
current_tile16_bmp_ = tile16_individual_[notify_tile16];
|
||||||
current_tile16_bmp_.ApplyPalette(
|
auto ow_main_pal_group = rom()->palette_group().overworld_main;
|
||||||
rom()->palette_group("ow_main")[current_palette_]);
|
RETURN_IF_ERROR(current_tile16_bmp_.ApplyPalette(
|
||||||
|
ow_main_pal_group[current_palette_]));
|
||||||
rom()->RenderBitmap(¤t_tile16_bmp_);
|
rom()->RenderBitmap(¤t_tile16_bmp_);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -173,40 +168,41 @@ absl::Status Tile16Editor::DrawToCurrentTile16(ImVec2 click_position) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
absl::Status Tile16Editor::UpdateTile16Edit() {
|
absl::Status Tile16Editor::UpdateTile16Edit() {
|
||||||
|
auto ow_main_pal_group = rom()->palette_group().overworld_main;
|
||||||
|
|
||||||
if (ImGui::BeginChild("Tile8 Selector",
|
if (ImGui::BeginChild("Tile8 Selector",
|
||||||
ImVec2(ImGui::GetContentRegionAvail().x, 0x175),
|
ImVec2(ImGui::GetContentRegionAvail().x, 0x175),
|
||||||
true)) {
|
true)) {
|
||||||
tile8_source_canvas_.DrawBackground(
|
tile8_source_canvas_.DrawBackground();
|
||||||
ImVec2(core::kTilesheetWidth * 4, core::kTilesheetHeight * 0x10 * 4));
|
tile8_source_canvas_.DrawContextMenu(¤t_gfx_bmp_);
|
||||||
tile8_source_canvas_.DrawContextMenu();
|
|
||||||
if (tile8_source_canvas_.DrawTileSelector(32)) {
|
if (tile8_source_canvas_.DrawTileSelector(32)) {
|
||||||
current_gfx_individual_[current_tile8_].ApplyPaletteWithTransparent(
|
RETURN_IF_ERROR(
|
||||||
rom()->palette_group("ow_main")[0], current_palette_);
|
current_gfx_individual_[current_tile8_].ApplyPaletteWithTransparent(
|
||||||
|
ow_main_pal_group[0], current_palette_));
|
||||||
rom()->UpdateBitmap(¤t_gfx_individual_[current_tile8_]);
|
rom()->UpdateBitmap(¤t_gfx_individual_[current_tile8_]);
|
||||||
}
|
}
|
||||||
tile8_source_canvas_.DrawBitmap(current_gfx_bmp_, 0, 0, 4.0f);
|
tile8_source_canvas_.DrawBitmap(current_gfx_bmp_, 0, 0, 4.0f);
|
||||||
tile8_source_canvas_.DrawGrid(32.0f);
|
tile8_source_canvas_.DrawGrid();
|
||||||
tile8_source_canvas_.DrawOverlay();
|
tile8_source_canvas_.DrawOverlay();
|
||||||
}
|
}
|
||||||
ImGui::EndChild();
|
ImGui::EndChild();
|
||||||
|
|
||||||
|
// The user selected a tile8
|
||||||
if (!tile8_source_canvas_.points().empty()) {
|
if (!tile8_source_canvas_.points().empty()) {
|
||||||
uint16_t x = tile8_source_canvas_.points().front().x / 16;
|
uint16_t x = tile8_source_canvas_.points().front().x / 16;
|
||||||
uint16_t y = tile8_source_canvas_.points().front().y / 16;
|
uint16_t y = tile8_source_canvas_.points().front().y / 16;
|
||||||
|
|
||||||
current_tile8_ = x + (y * 8);
|
current_tile8_ = x + (y * 8);
|
||||||
current_gfx_individual_[current_tile8_].ApplyPaletteWithTransparent(
|
RETURN_IF_ERROR(
|
||||||
rom()->palette_group("ow_main")[0], current_palette_);
|
current_gfx_individual_[current_tile8_].ApplyPaletteWithTransparent(
|
||||||
|
ow_main_pal_group[0], current_palette_));
|
||||||
rom()->UpdateBitmap(¤t_gfx_individual_[current_tile8_]);
|
rom()->UpdateBitmap(¤t_gfx_individual_[current_tile8_]);
|
||||||
}
|
}
|
||||||
|
|
||||||
ImGui::Text("Tile16 ID: %d", current_tile16_);
|
|
||||||
ImGui::Text("Tile8 ID: %d", current_tile8_);
|
|
||||||
|
|
||||||
if (ImGui::BeginChild("Tile16 Editor Options",
|
if (ImGui::BeginChild("Tile16 Editor Options",
|
||||||
ImVec2(ImGui::GetContentRegionAvail().x, 0x50), true)) {
|
ImVec2(ImGui::GetContentRegionAvail().x, 0x50), true)) {
|
||||||
tile16_edit_canvas_.DrawBackground(ImVec2(0x40, 0x40));
|
tile16_edit_canvas_.DrawBackground();
|
||||||
tile16_edit_canvas_.DrawContextMenu();
|
tile16_edit_canvas_.DrawContextMenu(¤t_tile16_bmp_);
|
||||||
tile16_edit_canvas_.DrawBitmap(current_tile16_bmp_, 0, 0, 4.0f);
|
tile16_edit_canvas_.DrawBitmap(current_tile16_bmp_, 0, 0, 4.0f);
|
||||||
if (!tile8_source_canvas_.points().empty()) {
|
if (!tile8_source_canvas_.points().empty()) {
|
||||||
if (tile16_edit_canvas_.DrawTilePainter(
|
if (tile16_edit_canvas_.DrawTilePainter(
|
||||||
@@ -216,17 +212,17 @@ absl::Status Tile16Editor::UpdateTile16Edit() {
|
|||||||
rom()->UpdateBitmap(¤t_tile16_bmp_);
|
rom()->UpdateBitmap(¤t_tile16_bmp_);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
tile16_edit_canvas_.DrawGrid(64.0f);
|
tile16_edit_canvas_.DrawGrid();
|
||||||
tile16_edit_canvas_.DrawOverlay();
|
tile16_edit_canvas_.DrawOverlay();
|
||||||
}
|
}
|
||||||
ImGui::EndChild();
|
ImGui::EndChild();
|
||||||
DrawTileEditControls();
|
|
||||||
|
|
||||||
return absl::OkStatus();
|
return absl::OkStatus();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Tile16Editor::DrawTileEditControls() {
|
absl::Status Tile16Editor::DrawTileEditControls() {
|
||||||
ImGui::Separator();
|
ImGui::Separator();
|
||||||
|
ImGui::Text("Tile16 ID: %d", current_tile16_);
|
||||||
|
ImGui::Text("Tile8 ID: %d", current_tile8_);
|
||||||
ImGui::Text("Options:");
|
ImGui::Text("Options:");
|
||||||
gui::InputHexByte("Palette", ¬ify_palette.mutable_get());
|
gui::InputHexByte("Palette", ¬ify_palette.mutable_get());
|
||||||
notify_palette.apply_changes();
|
notify_palette.apply_changes();
|
||||||
@@ -242,8 +238,10 @@ void Tile16Editor::DrawTileEditControls() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (value > 0x00) {
|
if (value > 0x00) {
|
||||||
current_gfx_bmp_.ApplyPaletteWithTransparent(palette, value);
|
RETURN_IF_ERROR(
|
||||||
current_tile16_bmp_.ApplyPaletteWithTransparent(palette, value);
|
current_gfx_bmp_.ApplyPaletteWithTransparent(palette, value));
|
||||||
|
RETURN_IF_ERROR(
|
||||||
|
current_tile16_bmp_.ApplyPaletteWithTransparent(palette, value));
|
||||||
rom()->UpdateBitmap(¤t_gfx_bmp_);
|
rom()->UpdateBitmap(¤t_gfx_bmp_);
|
||||||
rom()->UpdateBitmap(¤t_tile16_bmp_);
|
rom()->UpdateBitmap(¤t_tile16_bmp_);
|
||||||
}
|
}
|
||||||
@@ -252,6 +250,82 @@ void Tile16Editor::DrawTileEditControls() {
|
|||||||
ImGui::Checkbox("X Flip", &x_flip);
|
ImGui::Checkbox("X Flip", &x_flip);
|
||||||
ImGui::Checkbox("Y Flip", &y_flip);
|
ImGui::Checkbox("Y Flip", &y_flip);
|
||||||
ImGui::Checkbox("Priority Tile", &priority_tile);
|
ImGui::Checkbox("Priority Tile", &priority_tile);
|
||||||
|
|
||||||
|
return absl::OkStatus();
|
||||||
|
}
|
||||||
|
|
||||||
|
absl::Status Tile16Editor::LoadTile8() {
|
||||||
|
auto ow_main_pal_group = rom()->palette_group().overworld_main;
|
||||||
|
|
||||||
|
current_gfx_individual_.reserve(1024);
|
||||||
|
|
||||||
|
for (int index = 0; index < 1024; index++) {
|
||||||
|
std::vector<uint8_t> tile_data(0x40, 0x00);
|
||||||
|
|
||||||
|
// Copy the pixel data for the current tile into the vector
|
||||||
|
for (int ty = 0; ty < 8; ty++) {
|
||||||
|
for (int tx = 0; tx < 8; tx++) {
|
||||||
|
// Current Gfx Data is 16 sheets of 8x8 tiles ordered 16 wide by 4 tall
|
||||||
|
|
||||||
|
// Calculate the position in the tile data vector
|
||||||
|
int position = tx + (ty * 0x08);
|
||||||
|
|
||||||
|
// Calculate the position in the current gfx data
|
||||||
|
int num_columns = current_gfx_bmp_.width() / 8;
|
||||||
|
int num_rows = current_gfx_bmp_.height() / 8;
|
||||||
|
int x = (index % num_columns) * 8 + tx;
|
||||||
|
int y = (index / num_columns) * 8 + ty;
|
||||||
|
int gfx_position = x + (y * 0x100);
|
||||||
|
|
||||||
|
// Get the pixel value from the current gfx data
|
||||||
|
uint8_t value = current_gfx_bmp_.data()[gfx_position];
|
||||||
|
|
||||||
|
if (value & 0x80) {
|
||||||
|
value -= 0x88;
|
||||||
|
}
|
||||||
|
|
||||||
|
tile_data[position] = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
current_gfx_individual_.emplace_back();
|
||||||
|
current_gfx_individual_[index].Create(0x08, 0x08, 0x08, tile_data);
|
||||||
|
RETURN_IF_ERROR(current_gfx_individual_[index].ApplyPaletteWithTransparent(
|
||||||
|
ow_main_pal_group[0], current_palette_));
|
||||||
|
rom()->RenderBitmap(¤t_gfx_individual_[index]);
|
||||||
|
}
|
||||||
|
|
||||||
|
map_blockset_loaded_ = true;
|
||||||
|
|
||||||
|
return absl::OkStatus();
|
||||||
|
}
|
||||||
|
|
||||||
|
// ============================================================================
|
||||||
|
// Tile16 Transfer
|
||||||
|
|
||||||
|
absl::Status Tile16Editor::UpdateTile16Transfer() {
|
||||||
|
if (BeginTabItem("Tile16 Transfer")) {
|
||||||
|
if (BeginTable("#Tile16TransferTable", 2, TABLE_BORDERS_RESIZABLE,
|
||||||
|
ImVec2(0, 0))) {
|
||||||
|
TableSetupColumn("Current ROM Tiles", ImGuiTableColumnFlags_WidthFixed,
|
||||||
|
ImGui::GetContentRegionAvail().x / 2);
|
||||||
|
TableSetupColumn("Transfer ROM Tiles", ImGuiTableColumnFlags_WidthFixed,
|
||||||
|
ImGui::GetContentRegionAvail().x / 2);
|
||||||
|
TableHeadersRow();
|
||||||
|
TableNextRow();
|
||||||
|
|
||||||
|
TableNextColumn();
|
||||||
|
RETURN_IF_ERROR(UpdateBlockset());
|
||||||
|
|
||||||
|
TableNextColumn();
|
||||||
|
RETURN_IF_ERROR(UpdateTransferTileCanvas());
|
||||||
|
|
||||||
|
ImGui::EndTable();
|
||||||
|
}
|
||||||
|
|
||||||
|
ImGui::EndTabItem();
|
||||||
|
}
|
||||||
|
return absl::OkStatus();
|
||||||
}
|
}
|
||||||
|
|
||||||
absl::Status Tile16Editor::UpdateTransferTileCanvas() {
|
absl::Status Tile16Editor::UpdateTransferTileCanvas() {
|
||||||
@@ -279,9 +353,9 @@ absl::Status Tile16Editor::UpdateTransferTileCanvas() {
|
|||||||
palette_ = transfer_overworld_.AreaPalette();
|
palette_ = transfer_overworld_.AreaPalette();
|
||||||
|
|
||||||
// Create the tile16 blockset image
|
// Create the tile16 blockset image
|
||||||
gui::BuildAndRenderBitmapPipeline(0x80, 0x2000, 0x80,
|
RETURN_IF_ERROR(rom()->CreateAndRenderBitmap(0x80, 0x2000, 0x80,
|
||||||
transfer_overworld_.Tile16Blockset(),
|
transfer_overworld_.Tile16Blockset(),
|
||||||
*rom(), transfer_blockset_bmp_, palette_);
|
transfer_blockset_bmp_, palette_));
|
||||||
transfer_blockset_loaded_ = true;
|
transfer_blockset_loaded_ = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -293,62 +367,6 @@ absl::Status Tile16Editor::UpdateTransferTileCanvas() {
|
|||||||
return absl::OkStatus();
|
return absl::OkStatus();
|
||||||
}
|
}
|
||||||
|
|
||||||
absl::Status Tile16Editor::InitBlockset(
|
|
||||||
const gfx::Bitmap& tile16_blockset_bmp, gfx::Bitmap current_gfx_bmp,
|
|
||||||
const std::vector<gfx::Bitmap>& tile16_individual,
|
|
||||||
uint8_t all_tiles_types[0x200]) {
|
|
||||||
all_tiles_types_ = all_tiles_types;
|
|
||||||
tile16_blockset_bmp_ = tile16_blockset_bmp;
|
|
||||||
tile16_individual_ = tile16_individual;
|
|
||||||
current_gfx_bmp_ = current_gfx_bmp;
|
|
||||||
tile8_gfx_data_ = current_gfx_bmp_.vector();
|
|
||||||
return absl::OkStatus();
|
|
||||||
}
|
|
||||||
|
|
||||||
absl::Status Tile16Editor::LoadTile8() {
|
|
||||||
current_gfx_individual_.reserve(1024);
|
|
||||||
|
|
||||||
for (int index = 0; index < 1024; index++) {
|
|
||||||
std::vector<uint8_t> tile_data(0x40, 0x00);
|
|
||||||
|
|
||||||
// Copy the pixel data for the current tile into the vector
|
|
||||||
for (int ty = 0; ty < 8; ty++) {
|
|
||||||
for (int tx = 0; tx < 8; tx++) {
|
|
||||||
// Current Gfx Data is 16 sheets of 8x8 tiles ordered 16 wide by 4 tall
|
|
||||||
|
|
||||||
// Calculate the position in the tile data vector
|
|
||||||
int position = tx + (ty * 0x08);
|
|
||||||
|
|
||||||
// Calculate the position in the current gfx data
|
|
||||||
int num_columns = current_gfx_bmp_.width() / 8;
|
|
||||||
int num_rows = current_gfx_bmp_.height() / 8;
|
|
||||||
int x = (index % num_columns) * 8 + tx;
|
|
||||||
int y = (index / num_columns) * 8 + ty;
|
|
||||||
int gfx_position = x + (y * 0x100);
|
|
||||||
|
|
||||||
// Get the pixel value from the current gfx data
|
|
||||||
uint8_t value = tile8_gfx_data_[gfx_position];
|
|
||||||
|
|
||||||
if (value & 0x80) {
|
|
||||||
value -= 0x88;
|
|
||||||
}
|
|
||||||
|
|
||||||
tile_data[position] = value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
current_gfx_individual_.emplace_back();
|
|
||||||
current_gfx_individual_[index].Create(0x08, 0x08, 0x08, tile_data);
|
|
||||||
current_gfx_individual_[index].ApplyPaletteWithTransparent(
|
|
||||||
rom()->palette_group("ow_main")[0], current_palette_);
|
|
||||||
rom()->RenderBitmap(¤t_gfx_individual_[index]);
|
|
||||||
}
|
|
||||||
|
|
||||||
map_blockset_loaded_ = true;
|
|
||||||
|
|
||||||
return absl::OkStatus();
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace editor
|
} // namespace editor
|
||||||
} // namespace app
|
} // namespace app
|
||||||
} // namespace yaze
|
} // namespace yaze
|
||||||
@@ -7,25 +7,31 @@
|
|||||||
|
|
||||||
#include "absl/status/status.h"
|
#include "absl/status/status.h"
|
||||||
#include "absl/status/statusor.h"
|
#include "absl/status/statusor.h"
|
||||||
#include "app/core/editor.h"
|
|
||||||
#include "app/editor/context/gfx_context.h"
|
#include "app/editor/context/gfx_context.h"
|
||||||
#include "app/editor/modules/palette_editor.h"
|
#include "app/editor/modules/palette_editor.h"
|
||||||
|
#include "app/editor/utils/editor.h"
|
||||||
#include "app/gfx/bitmap.h"
|
#include "app/gfx/bitmap.h"
|
||||||
#include "app/gfx/snes_palette.h"
|
#include "app/gfx/snes_palette.h"
|
||||||
#include "app/gfx/snes_tile.h"
|
#include "app/gfx/snes_tile.h"
|
||||||
|
#include "app/gfx/tilesheet.h"
|
||||||
#include "app/gui/canvas.h"
|
#include "app/gui/canvas.h"
|
||||||
#include "app/gui/icons.h"
|
#include "app/gui/icons.h"
|
||||||
#include "app/gui/pipeline.h"
|
#include "app/gui/pipeline.h"
|
||||||
#include "app/rom.h"
|
#include "app/rom.h"
|
||||||
#include "app/zelda3/overworld.h"
|
#include "app/zelda3/overworld/overworld.h"
|
||||||
|
|
||||||
namespace yaze {
|
namespace yaze {
|
||||||
namespace app {
|
namespace app {
|
||||||
namespace editor {
|
namespace editor {
|
||||||
|
|
||||||
class Tile16Editor : public GfxContext, public SharedROM {
|
/**
|
||||||
|
* @brief Popup window to edit Tile16 data
|
||||||
|
*/
|
||||||
|
class Tile16Editor : public context::GfxContext, public SharedRom {
|
||||||
public:
|
public:
|
||||||
absl::Status Update();
|
absl::Status Update();
|
||||||
|
absl::Status DrawMenu();
|
||||||
|
|
||||||
absl::Status DrawTile16Editor();
|
absl::Status DrawTile16Editor();
|
||||||
absl::Status UpdateTile16Transfer();
|
absl::Status UpdateTile16Transfer();
|
||||||
absl::Status UpdateBlockset();
|
absl::Status UpdateBlockset();
|
||||||
@@ -34,23 +40,31 @@ class Tile16Editor : public GfxContext, public SharedROM {
|
|||||||
|
|
||||||
absl::Status UpdateTile16Edit();
|
absl::Status UpdateTile16Edit();
|
||||||
|
|
||||||
void DrawTileEditControls();
|
absl::Status DrawTileEditControls();
|
||||||
|
|
||||||
absl::Status UpdateTransferTileCanvas();
|
absl::Status UpdateTransferTileCanvas();
|
||||||
|
|
||||||
absl::Status InitBlockset(const gfx::Bitmap& tile16_blockset_bmp,
|
void InitBlockset(const gfx::Bitmap& tile16_blockset_bmp,
|
||||||
gfx::Bitmap current_gfx_bmp,
|
gfx::Bitmap current_gfx_bmp,
|
||||||
const std::vector<gfx::Bitmap>& tile16_individual,
|
const std::vector<gfx::Bitmap>& tile16_individual,
|
||||||
uint8_t all_tiles_types[0x200]);
|
uint8_t all_tiles_types[0x200]) {
|
||||||
|
all_tiles_types_ = all_tiles_types;
|
||||||
|
tile16_blockset_bmp_ = tile16_blockset_bmp;
|
||||||
|
tile16_individual_ = tile16_individual;
|
||||||
|
current_gfx_bmp_ = current_gfx_bmp;
|
||||||
|
tile8_gfx_data_ = current_gfx_bmp_.vector();
|
||||||
|
}
|
||||||
|
|
||||||
absl::Status LoadTile8();
|
absl::Status LoadTile8();
|
||||||
|
|
||||||
auto set_tile16(int id) {
|
absl::Status set_tile16(int id) {
|
||||||
current_tile16_ = id;
|
current_tile16_ = id;
|
||||||
current_tile16_bmp_ = tile16_individual_[id];
|
current_tile16_bmp_ = tile16_individual_[id];
|
||||||
current_tile16_bmp_.ApplyPalette(
|
auto ow_main_pal_group = rom()->palette_group().overworld_main;
|
||||||
rom()->palette_group("ow_main")[current_palette_]);
|
RETURN_IF_ERROR(
|
||||||
|
current_tile16_bmp_.ApplyPalette(ow_main_pal_group[current_palette_]));
|
||||||
rom()->RenderBitmap(¤t_tile16_bmp_);
|
rom()->RenderBitmap(¤t_tile16_bmp_);
|
||||||
|
return absl::OkStatus();
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
@@ -78,7 +92,7 @@ class Tile16Editor : public GfxContext, public SharedROM {
|
|||||||
bool priority_tile;
|
bool priority_tile;
|
||||||
int tile_size;
|
int tile_size;
|
||||||
|
|
||||||
uint8_t *all_tiles_types_;
|
uint8_t* all_tiles_types_;
|
||||||
|
|
||||||
// Tile16 blockset for selecting the tile to edit
|
// Tile16 blockset for selecting the tile to edit
|
||||||
gui::Canvas blockset_canvas_{ImVec2(0x100, 0x4000),
|
gui::Canvas blockset_canvas_{ImVec2(0x100, 0x4000),
|
||||||
@@ -86,13 +100,17 @@ class Tile16Editor : public GfxContext, public SharedROM {
|
|||||||
gfx::Bitmap tile16_blockset_bmp_;
|
gfx::Bitmap tile16_blockset_bmp_;
|
||||||
|
|
||||||
// Canvas for editing the selected tile
|
// Canvas for editing the selected tile
|
||||||
gui::Canvas tile16_edit_canvas_;
|
gui::Canvas tile16_edit_canvas_{ImVec2(0x40, 0x40),
|
||||||
|
gui::CanvasGridSize::k64x64};
|
||||||
gfx::Bitmap current_tile16_bmp_;
|
gfx::Bitmap current_tile16_bmp_;
|
||||||
gfx::Bitmap current_tile8_bmp_;
|
gfx::Bitmap current_tile8_bmp_;
|
||||||
|
|
||||||
// Tile8 canvas to get the tile to drawing in the tile16_edit_canvas_
|
// Tile8 canvas to get the tile to drawing in the tile16_edit_canvas_
|
||||||
gui::Canvas tile8_source_canvas_;
|
gui::Canvas tile8_source_canvas_{
|
||||||
|
ImVec2(core::kTilesheetWidth * 4, core::kTilesheetHeight * 0x10 * 4),
|
||||||
|
gui::CanvasGridSize::k32x32};
|
||||||
gfx::Bitmap current_gfx_bmp_;
|
gfx::Bitmap current_gfx_bmp_;
|
||||||
|
std::vector<gfx::Tilesheet> current_tilesheets_;
|
||||||
|
|
||||||
gui::Canvas transfer_canvas_;
|
gui::Canvas transfer_canvas_;
|
||||||
gfx::Bitmap transfer_blockset_bmp_;
|
gfx::Bitmap transfer_blockset_bmp_;
|
||||||
@@ -110,14 +128,12 @@ class Tile16Editor : public GfxContext, public SharedROM {
|
|||||||
PaletteEditor palette_editor_;
|
PaletteEditor palette_editor_;
|
||||||
|
|
||||||
gfx::SnesPalette palette_;
|
gfx::SnesPalette palette_;
|
||||||
zelda3::Overworld transfer_overworld_;
|
zelda3::overworld::Overworld transfer_overworld_;
|
||||||
|
|
||||||
gfx::BitmapTable graphics_bin_;
|
gfx::BitmapTable graphics_bin_;
|
||||||
|
|
||||||
ROM transfer_rom_;
|
Rom transfer_rom_;
|
||||||
absl::Status transfer_status_;
|
absl::Status transfer_status_;
|
||||||
|
|
||||||
core::TaskManager<std::function<void(int)>> task_manager_;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace editor
|
} // namespace editor
|
||||||
|
|||||||
@@ -23,7 +23,7 @@
|
|||||||
#include "app/gui/style.h"
|
#include "app/gui/style.h"
|
||||||
#include "app/gui/widgets.h"
|
#include "app/gui/widgets.h"
|
||||||
#include "app/rom.h"
|
#include "app/rom.h"
|
||||||
#include "app/zelda3/overworld.h"
|
#include "app/zelda3/overworld/overworld.h"
|
||||||
|
|
||||||
namespace yaze {
|
namespace yaze {
|
||||||
namespace app {
|
namespace app {
|
||||||
@@ -44,52 +44,17 @@ using ImGui::Text;
|
|||||||
|
|
||||||
absl::Status OverworldEditor::Update() {
|
absl::Status OverworldEditor::Update() {
|
||||||
if (rom()->is_loaded() && !all_gfx_loaded_) {
|
if (rom()->is_loaded() && !all_gfx_loaded_) {
|
||||||
RETURN_IF_ERROR(tile16_editor_.InitBlockset(
|
tile16_editor_.InitBlockset(tile16_blockset_bmp_, current_gfx_bmp_,
|
||||||
tile16_blockset_bmp_, current_gfx_bmp_, tile16_individual_,
|
tile16_individual_,
|
||||||
*overworld_.mutable_all_tiles_types()));
|
*overworld_.mutable_all_tiles_types());
|
||||||
gfx_group_editor_.InitBlockset(tile16_blockset_bmp_);
|
gfx_group_editor_.InitBlockset(tile16_blockset_bmp_);
|
||||||
|
RETURN_IF_ERROR(LoadEntranceTileTypes(*rom()));
|
||||||
all_gfx_loaded_ = true;
|
all_gfx_loaded_ = true;
|
||||||
} else if (!rom()->is_loaded() && all_gfx_loaded_) {
|
} else if (!rom()->is_loaded() && all_gfx_loaded_) {
|
||||||
// TODO: Destroy the overworld graphics canvas.
|
|
||||||
Shutdown();
|
Shutdown();
|
||||||
overworld_.Destroy();
|
|
||||||
all_gfx_loaded_ = false;
|
|
||||||
map_blockset_loaded_ = false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Setup pan tool with middle mouse button
|
RETURN_IF_ERROR(UpdateFullscreenCanvas());
|
||||||
// if (ImGui::IsMouseDragging(ImGuiMouseButton_Middle)) {
|
|
||||||
// previous_mode = current_mode;
|
|
||||||
// current_mode = EditingMode::PAN;
|
|
||||||
// ow_map_canvas_.set_draggable(true);
|
|
||||||
// middle_mouse_dragging_ = true;
|
|
||||||
// }
|
|
||||||
// if (ImGui::IsMouseReleased(ImGuiMouseButton_Middle) &&
|
|
||||||
// current_mode == EditingMode::PAN && middle_mouse_dragging_) {
|
|
||||||
// current_mode = previous_mode;
|
|
||||||
// ow_map_canvas_.set_draggable(false);
|
|
||||||
// middle_mouse_dragging_ = false;
|
|
||||||
// }
|
|
||||||
|
|
||||||
if (overworld_canvas_fullscreen_) {
|
|
||||||
static bool use_work_area = true;
|
|
||||||
static ImGuiWindowFlags flags = ImGuiWindowFlags_NoDecoration |
|
|
||||||
ImGuiWindowFlags_NoMove |
|
|
||||||
ImGuiWindowFlags_NoSavedSettings;
|
|
||||||
const ImGuiViewport *viewport = ImGui::GetMainViewport();
|
|
||||||
ImGui::SetNextWindowPos(use_work_area ? viewport->WorkPos : viewport->Pos);
|
|
||||||
ImGui::SetNextWindowSize(use_work_area ? viewport->WorkSize
|
|
||||||
: viewport->Size);
|
|
||||||
|
|
||||||
if (ImGui::Begin("Example: Fullscreen window",
|
|
||||||
&overworld_canvas_fullscreen_, flags)) {
|
|
||||||
// Draws the toolset for editing the Overworld.
|
|
||||||
RETURN_IF_ERROR(DrawToolset())
|
|
||||||
DrawOverworldCanvas();
|
|
||||||
}
|
|
||||||
ImGui::End();
|
|
||||||
return absl::OkStatus();
|
|
||||||
}
|
|
||||||
|
|
||||||
TAB_BAR("##OWEditorTabBar")
|
TAB_BAR("##OWEditorTabBar")
|
||||||
TAB_ITEM("Map Editor")
|
TAB_ITEM("Map Editor")
|
||||||
@@ -117,12 +82,35 @@ absl::Status OverworldEditor::UpdateOverworldEdit() {
|
|||||||
TableNextColumn();
|
TableNextColumn();
|
||||||
DrawOverworldCanvas();
|
DrawOverworldCanvas();
|
||||||
TableNextColumn();
|
TableNextColumn();
|
||||||
DrawTileSelector();
|
RETURN_IF_ERROR(DrawTileSelector());
|
||||||
ImGui::EndTable();
|
ImGui::EndTable();
|
||||||
}
|
}
|
||||||
return absl::OkStatus();
|
return absl::OkStatus();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
absl::Status OverworldEditor::UpdateFullscreenCanvas() {
|
||||||
|
if (overworld_canvas_fullscreen_) {
|
||||||
|
static bool use_work_area = true;
|
||||||
|
static ImGuiWindowFlags flags = ImGuiWindowFlags_NoDecoration |
|
||||||
|
ImGuiWindowFlags_NoMove |
|
||||||
|
ImGuiWindowFlags_NoSavedSettings;
|
||||||
|
const ImGuiViewport *viewport = ImGui::GetMainViewport();
|
||||||
|
ImGui::SetNextWindowPos(use_work_area ? viewport->WorkPos : viewport->Pos);
|
||||||
|
ImGui::SetNextWindowSize(use_work_area ? viewport->WorkSize
|
||||||
|
: viewport->Size);
|
||||||
|
|
||||||
|
if (ImGui::Begin("Example: Fullscreen window",
|
||||||
|
&overworld_canvas_fullscreen_, flags)) {
|
||||||
|
// Draws the toolset for editing the Overworld.
|
||||||
|
RETURN_IF_ERROR(DrawToolset())
|
||||||
|
DrawOverworldCanvas();
|
||||||
|
}
|
||||||
|
ImGui::End();
|
||||||
|
return absl::OkStatus();
|
||||||
|
}
|
||||||
|
return absl::OkStatus();
|
||||||
|
}
|
||||||
|
|
||||||
absl::Status OverworldEditor::DrawToolset() {
|
absl::Status OverworldEditor::DrawToolset() {
|
||||||
static bool show_gfx_group = false;
|
static bool show_gfx_group = false;
|
||||||
static bool show_properties = false;
|
static bool show_properties = false;
|
||||||
@@ -258,7 +246,8 @@ absl::Status OverworldEditor::DrawToolset() {
|
|||||||
|
|
||||||
if (show_tile16_editor_) {
|
if (show_tile16_editor_) {
|
||||||
// Create a table in ImGui for the Tile16 Editor
|
// Create a table in ImGui for the Tile16 Editor
|
||||||
ImGui::Begin("Tile16 Editor", &show_tile16_editor_);
|
ImGui::Begin("Tile16 Editor", &show_tile16_editor_,
|
||||||
|
ImGuiWindowFlags_MenuBar);
|
||||||
RETURN_IF_ERROR(tile16_editor_.Update())
|
RETURN_IF_ERROR(tile16_editor_.Update())
|
||||||
ImGui::End();
|
ImGui::End();
|
||||||
}
|
}
|
||||||
@@ -358,21 +347,24 @@ void OverworldEditor::RefreshOverworldMap() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Palette throws out of bounds error unexpectedly.
|
// TODO: Palette throws out of bounds error unexpectedly.
|
||||||
void OverworldEditor::RefreshMapPalette() {
|
absl::Status OverworldEditor::RefreshMapPalette() {
|
||||||
|
const auto current_map_palette =
|
||||||
|
overworld_.overworld_map(current_map_)->current_palette();
|
||||||
|
|
||||||
if (overworld_.overworld_map(current_map_)->is_large_map()) {
|
if (overworld_.overworld_map(current_map_)->is_large_map()) {
|
||||||
// We need to update the map and its siblings if it's a large map
|
// We need to update the map and its siblings if it's a large map
|
||||||
for (int i = 1; i < 4; i++) {
|
for (int i = 1; i < 4; i++) {
|
||||||
int sibling_index = overworld_.overworld_map(current_map_)->parent() + i;
|
int sibling_index = overworld_.overworld_map(current_map_)->parent() + i;
|
||||||
if (i >= 2) sibling_index += 6;
|
if (i >= 2) sibling_index += 6;
|
||||||
overworld_.mutable_overworld_map(sibling_index)->LoadPalette();
|
RETURN_IF_ERROR(
|
||||||
maps_bmp_[sibling_index].ApplyPalette(
|
overworld_.mutable_overworld_map(sibling_index)->LoadPalette());
|
||||||
*overworld_.mutable_overworld_map(sibling_index)
|
RETURN_IF_ERROR(
|
||||||
->mutable_current_palette());
|
maps_bmp_[sibling_index].ApplyPalette(current_map_palette));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
maps_bmp_[current_map_].ApplyPalette(
|
|
||||||
*overworld_.mutable_overworld_map(current_map_)
|
RETURN_IF_ERROR(maps_bmp_[current_map_].ApplyPalette(current_map_palette));
|
||||||
->mutable_current_palette());
|
return absl::OkStatus();
|
||||||
}
|
}
|
||||||
|
|
||||||
void OverworldEditor::RefreshMapProperties() {
|
void OverworldEditor::RefreshMapProperties() {
|
||||||
@@ -396,17 +388,13 @@ void OverworldEditor::RefreshMapProperties() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: Add @JaredBrian per map mosaic feature
|
||||||
void OverworldEditor::DrawOverworldMapSettings() {
|
void OverworldEditor::DrawOverworldMapSettings() {
|
||||||
if (BeginTable(kOWMapTable.data(), 8, kOWMapFlags, ImVec2(0, 0), -1)) {
|
if (BeginTable(kOWMapTable.data(), 7, kOWMapFlags, ImVec2(0, 0), -1)) {
|
||||||
for (const auto &name :
|
for (const auto &name : {"##1stCol", "##gfxCol", "##palCol", "##sprgfxCol",
|
||||||
{"##mapIdCol", "##1stCol", "##gfxCol", "##palCol", "##sprgfxCol",
|
"##sprpalCol", "##msgidCol", "##2ndCol"})
|
||||||
"##sprpalCol", "##msgidCol", "##2ndCol"})
|
|
||||||
ImGui::TableSetupColumn(name);
|
ImGui::TableSetupColumn(name);
|
||||||
|
|
||||||
TableNextColumn();
|
|
||||||
ImGui::Text("Parent/Map ID:%#x, %#x",
|
|
||||||
overworld_.overworld_map(current_map_)->parent(), current_map_);
|
|
||||||
|
|
||||||
TableNextColumn();
|
TableNextColumn();
|
||||||
ImGui::SetNextItemWidth(120.f);
|
ImGui::SetNextItemWidth(120.f);
|
||||||
ImGui::Combo("##world", ¤t_world_, kWorldList.data(), 3);
|
ImGui::Combo("##world", ¤t_world_, kWorldList.data(), 3);
|
||||||
@@ -429,7 +417,8 @@ void OverworldEditor::DrawOverworldMapSettings() {
|
|||||||
->mutable_area_palette(),
|
->mutable_area_palette(),
|
||||||
kInputFieldSize)) {
|
kInputFieldSize)) {
|
||||||
RefreshMapProperties();
|
RefreshMapProperties();
|
||||||
RefreshMapPalette();
|
status_ = RefreshMapPalette();
|
||||||
|
RefreshOverworldMap();
|
||||||
}
|
}
|
||||||
ImGui::EndGroup();
|
ImGui::EndGroup();
|
||||||
|
|
||||||
@@ -643,7 +632,7 @@ void OverworldEditor::CheckForSelectRectangle() {
|
|||||||
ow_map_canvas_.DrawBitmapGroup(tile16_ids, tile16_individual_, 0x10);
|
ow_map_canvas_.DrawBitmapGroup(tile16_ids, tile16_individual_, 0x10);
|
||||||
}
|
}
|
||||||
|
|
||||||
void OverworldEditor::CheckForCurrentMap() {
|
absl::Status OverworldEditor::CheckForCurrentMap() {
|
||||||
// 4096x4096, 512x512 maps and some are larges maps 1024x1024
|
// 4096x4096, 512x512 maps and some are larges maps 1024x1024
|
||||||
auto mouse_position = ImGui::GetIO().MousePos;
|
auto mouse_position = ImGui::GetIO().MousePos;
|
||||||
constexpr int small_map_size = 512;
|
constexpr int small_map_size = 512;
|
||||||
@@ -686,28 +675,48 @@ void OverworldEditor::CheckForCurrentMap() {
|
|||||||
if (maps_bmp_[current_map_].modified() ||
|
if (maps_bmp_[current_map_].modified() ||
|
||||||
ImGui::IsMouseClicked(ImGuiMouseButton_Right)) {
|
ImGui::IsMouseClicked(ImGuiMouseButton_Right)) {
|
||||||
RefreshOverworldMap();
|
RefreshOverworldMap();
|
||||||
RefreshTile16Blockset();
|
RETURN_IF_ERROR(RefreshTile16Blockset());
|
||||||
rom()->UpdateBitmap(&maps_bmp_[current_map_]);
|
rom()->UpdateBitmap(&maps_bmp_[current_map_]);
|
||||||
maps_bmp_[current_map_].set_modified(false);
|
maps_bmp_[current_map_].set_modified(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return absl::OkStatus();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Overworld Editor canvas
|
void OverworldEditor::CheckForMousePan() {
|
||||||
// Allows the user to make changes to the overworld map.
|
if (ImGui::IsMouseDragging(ImGuiMouseButton_Middle)) {
|
||||||
|
previous_mode = current_mode;
|
||||||
|
current_mode = EditingMode::PAN;
|
||||||
|
ow_map_canvas_.set_draggable(true);
|
||||||
|
middle_mouse_dragging_ = true;
|
||||||
|
}
|
||||||
|
if (ImGui::IsMouseReleased(ImGuiMouseButton_Middle) &&
|
||||||
|
current_mode == EditingMode::PAN && middle_mouse_dragging_) {
|
||||||
|
current_mode = previous_mode;
|
||||||
|
ow_map_canvas_.set_draggable(false);
|
||||||
|
middle_mouse_dragging_ = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Add @JaredBrian ZSCustomOverworld features to OverworldEditor
|
||||||
void OverworldEditor::DrawOverworldCanvas() {
|
void OverworldEditor::DrawOverworldCanvas() {
|
||||||
if (all_gfx_loaded_) {
|
if (all_gfx_loaded_) {
|
||||||
DrawOverworldMapSettings();
|
DrawOverworldMapSettings();
|
||||||
Separator();
|
Separator();
|
||||||
}
|
}
|
||||||
|
|
||||||
gui::BeginNoPadding();
|
gui::BeginNoPadding();
|
||||||
gui::BeginChildBothScrollbars(7);
|
gui::BeginChildBothScrollbars(7);
|
||||||
ow_map_canvas_.DrawBackground();
|
ow_map_canvas_.DrawBackground();
|
||||||
gui::EndNoPadding();
|
gui::EndNoPadding();
|
||||||
|
|
||||||
|
CheckForMousePan();
|
||||||
if (current_mode == EditingMode::PAN) {
|
if (current_mode == EditingMode::PAN) {
|
||||||
ow_map_canvas_.DrawContextMenu();
|
ow_map_canvas_.DrawContextMenu();
|
||||||
} else {
|
} else {
|
||||||
ow_map_canvas_.set_draggable(false);
|
ow_map_canvas_.set_draggable(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (overworld_.is_loaded()) {
|
if (overworld_.is_loaded()) {
|
||||||
DrawOverworldMaps();
|
DrawOverworldMaps();
|
||||||
DrawOverworldExits(ow_map_canvas_.zero_point(), ow_map_canvas_.scrolling());
|
DrawOverworldExits(ow_map_canvas_.zero_point(), ow_map_canvas_.scrolling());
|
||||||
@@ -716,40 +725,47 @@ void OverworldEditor::DrawOverworldCanvas() {
|
|||||||
DrawOverworldItems();
|
DrawOverworldItems();
|
||||||
DrawOverworldSprites();
|
DrawOverworldSprites();
|
||||||
CheckForOverworldEdits();
|
CheckForOverworldEdits();
|
||||||
if (ImGui::IsItemHovered()) CheckForCurrentMap();
|
if (ImGui::IsItemHovered()) status_ = CheckForCurrentMap();
|
||||||
}
|
}
|
||||||
|
|
||||||
ow_map_canvas_.DrawGrid();
|
ow_map_canvas_.DrawGrid();
|
||||||
ow_map_canvas_.DrawOverlay();
|
ow_map_canvas_.DrawOverlay();
|
||||||
ImGui::EndChild();
|
ImGui::EndChild();
|
||||||
}
|
}
|
||||||
|
|
||||||
void OverworldEditor::DrawTile16Selector() {
|
absl::Status OverworldEditor::DrawTile16Selector() {
|
||||||
gui::BeginPadding(3);
|
gui::BeginPadding(3);
|
||||||
ImGui::BeginGroup();
|
ImGui::BeginGroup();
|
||||||
gui::BeginChildWithScrollbar("##Tile16SelectorScrollRegion");
|
gui::BeginChildWithScrollbar("##Tile16SelectorScrollRegion");
|
||||||
blockset_canvas_.DrawBackground();
|
blockset_canvas_.DrawBackground();
|
||||||
gui::EndNoPadding();
|
gui::EndNoPadding();
|
||||||
blockset_canvas_.DrawContextMenu();
|
{
|
||||||
blockset_canvas_.DrawBitmap(tile16_blockset_bmp_, /*border_offset=*/2,
|
blockset_canvas_.DrawContextMenu();
|
||||||
map_blockset_loaded_);
|
blockset_canvas_.DrawBitmap(tile16_blockset_bmp_, /*border_offset=*/2,
|
||||||
if (blockset_canvas_.DrawTileSelector(32.0f)) {
|
map_blockset_loaded_);
|
||||||
// Open the tile16 editor to the tile
|
|
||||||
auto tile_pos = blockset_canvas_.points().front();
|
if (blockset_canvas_.DrawTileSelector(32.0f)) {
|
||||||
int grid_x = static_cast<int>(tile_pos.x / 32);
|
// Open the tile16 editor to the tile
|
||||||
int grid_y = static_cast<int>(tile_pos.y / 32);
|
auto tile_pos = blockset_canvas_.points().front();
|
||||||
int id = grid_x + grid_y * 8;
|
int grid_x = static_cast<int>(tile_pos.x / 32);
|
||||||
tile16_editor_.set_tile16(id);
|
int grid_y = static_cast<int>(tile_pos.y / 32);
|
||||||
show_tile16_editor_ = true;
|
int id = grid_x + grid_y * 8;
|
||||||
|
RETURN_IF_ERROR(tile16_editor_.set_tile16(id));
|
||||||
|
show_tile16_editor_ = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ImGui::IsItemClicked() && !blockset_canvas_.points().empty()) {
|
||||||
|
int x = blockset_canvas_.points().front().x / 32;
|
||||||
|
int y = blockset_canvas_.points().front().y / 32;
|
||||||
|
current_tile16_ = x + (y * 8);
|
||||||
|
}
|
||||||
|
|
||||||
|
blockset_canvas_.DrawGrid();
|
||||||
|
blockset_canvas_.DrawOverlay();
|
||||||
}
|
}
|
||||||
if (ImGui::IsItemClicked() && !blockset_canvas_.points().empty()) {
|
|
||||||
int x = blockset_canvas_.points().front().x / 32;
|
|
||||||
int y = blockset_canvas_.points().front().y / 32;
|
|
||||||
current_tile16_ = x + (y * 8);
|
|
||||||
}
|
|
||||||
blockset_canvas_.DrawGrid();
|
|
||||||
blockset_canvas_.DrawOverlay();
|
|
||||||
ImGui::EndChild();
|
ImGui::EndChild();
|
||||||
ImGui::EndGroup();
|
ImGui::EndGroup();
|
||||||
|
return absl::OkStatus();
|
||||||
}
|
}
|
||||||
|
|
||||||
void OverworldEditor::DrawTile8Selector() {
|
void OverworldEditor::DrawTile8Selector() {
|
||||||
@@ -774,15 +790,14 @@ void OverworldEditor::DrawTile8Selector() {
|
|||||||
graphics_bin_canvas_.DrawOverlay();
|
graphics_bin_canvas_.DrawOverlay();
|
||||||
}
|
}
|
||||||
|
|
||||||
void OverworldEditor::DrawAreaGraphics() {
|
absl::Status OverworldEditor::DrawAreaGraphics() {
|
||||||
if (overworld_.is_loaded()) {
|
if (overworld_.is_loaded()) {
|
||||||
if (current_graphics_set_.count(current_map_) == 0) {
|
if (current_graphics_set_.count(current_map_) == 0) {
|
||||||
overworld_.set_current_map(current_map_);
|
overworld_.set_current_map(current_map_);
|
||||||
palette_ = overworld_.AreaPalette();
|
palette_ = overworld_.AreaPalette();
|
||||||
gfx::Bitmap bmp;
|
gfx::Bitmap bmp;
|
||||||
gui::BuildAndRenderBitmapPipeline(0x80, 0x200, 0x08,
|
RETURN_IF_ERROR(rom()->CreateAndRenderBitmap(
|
||||||
overworld_.current_graphics(), *rom(),
|
0x80, 0x200, 0x08, overworld_.current_graphics(), bmp, palette_));
|
||||||
bmp, palette_);
|
|
||||||
current_graphics_set_[current_map_] = bmp;
|
current_graphics_set_[current_map_] = bmp;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -792,21 +807,24 @@ void OverworldEditor::DrawAreaGraphics() {
|
|||||||
gui::BeginChildWithScrollbar("##AreaGraphicsScrollRegion");
|
gui::BeginChildWithScrollbar("##AreaGraphicsScrollRegion");
|
||||||
current_gfx_canvas_.DrawBackground();
|
current_gfx_canvas_.DrawBackground();
|
||||||
gui::EndPadding();
|
gui::EndPadding();
|
||||||
current_gfx_canvas_.DrawContextMenu();
|
{
|
||||||
current_gfx_canvas_.DrawBitmap(current_graphics_set_[current_map_],
|
current_gfx_canvas_.DrawContextMenu();
|
||||||
/*border_offset=*/2, overworld_.is_loaded());
|
current_gfx_canvas_.DrawBitmap(current_graphics_set_[current_map_],
|
||||||
current_gfx_canvas_.DrawTileSelector(32.0f);
|
/*border_offset=*/2, overworld_.is_loaded());
|
||||||
current_gfx_canvas_.DrawGrid();
|
current_gfx_canvas_.DrawTileSelector(32.0f);
|
||||||
current_gfx_canvas_.DrawOverlay();
|
current_gfx_canvas_.DrawGrid();
|
||||||
|
current_gfx_canvas_.DrawOverlay();
|
||||||
|
}
|
||||||
ImGui::EndChild();
|
ImGui::EndChild();
|
||||||
ImGui::EndGroup();
|
ImGui::EndGroup();
|
||||||
|
return absl::OkStatus();
|
||||||
}
|
}
|
||||||
|
|
||||||
void OverworldEditor::DrawTileSelector() {
|
absl::Status OverworldEditor::DrawTileSelector() {
|
||||||
if (BeginTabBar(kTileSelectorTab.data(),
|
if (BeginTabBar(kTileSelectorTab.data(),
|
||||||
ImGuiTabBarFlags_FittingPolicyScroll)) {
|
ImGuiTabBarFlags_FittingPolicyScroll)) {
|
||||||
if (BeginTabItem("Tile16")) {
|
if (BeginTabItem("Tile16")) {
|
||||||
DrawTile16Selector();
|
RETURN_IF_ERROR(DrawTile16Selector());
|
||||||
EndTabItem();
|
EndTabItem();
|
||||||
}
|
}
|
||||||
if (BeginTabItem("Tile8")) {
|
if (BeginTabItem("Tile8")) {
|
||||||
@@ -823,6 +841,7 @@ void OverworldEditor::DrawTileSelector() {
|
|||||||
}
|
}
|
||||||
EndTabBar();
|
EndTabBar();
|
||||||
}
|
}
|
||||||
|
return absl::OkStatus();
|
||||||
}
|
}
|
||||||
|
|
||||||
// ----------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------
|
||||||
@@ -940,7 +959,9 @@ bool DrawEntranceInserterPopup() {
|
|||||||
return set_done;
|
return set_done;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool DrawOverworldEntrancePopup(zelda3::OverworldEntrance &entrance) {
|
// TODO: Implement deleting OverworldEntrance objects, currently only hides them
|
||||||
|
bool DrawOverworldEntrancePopup(
|
||||||
|
zelda3::overworld::OverworldEntrance &entrance) {
|
||||||
static bool set_done = false;
|
static bool set_done = false;
|
||||||
if (set_done) {
|
if (set_done) {
|
||||||
set_done = false;
|
set_done = false;
|
||||||
@@ -1045,6 +1066,7 @@ void OverworldEditor::DrawOverworldEntrances(ImVec2 canvas_p0, ImVec2 scrolling,
|
|||||||
|
|
||||||
namespace exit_internal {
|
namespace exit_internal {
|
||||||
|
|
||||||
|
// TODO: Implement deleting OverworldExit objects
|
||||||
void DrawExitInserterPopup() {
|
void DrawExitInserterPopup() {
|
||||||
if (ImGui::BeginPopup("Exit Inserter")) {
|
if (ImGui::BeginPopup("Exit Inserter")) {
|
||||||
static int exit_id = 0;
|
static int exit_id = 0;
|
||||||
@@ -1063,7 +1085,7 @@ void DrawExitInserterPopup() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool DrawExitEditorPopup(zelda3::OverworldExit &exit) {
|
bool DrawExitEditorPopup(zelda3::overworld::OverworldExit &exit) {
|
||||||
static bool set_done = false;
|
static bool set_done = false;
|
||||||
if (set_done) {
|
if (set_done) {
|
||||||
set_done = false;
|
set_done = false;
|
||||||
@@ -1259,8 +1281,8 @@ void DrawItemInsertPopup() {
|
|||||||
ImGui::Text("Add Item");
|
ImGui::Text("Add Item");
|
||||||
ImGui::BeginChild("ScrollRegion", ImVec2(150, 150), true,
|
ImGui::BeginChild("ScrollRegion", ImVec2(150, 150), true,
|
||||||
ImGuiWindowFlags_AlwaysVerticalScrollbar);
|
ImGuiWindowFlags_AlwaysVerticalScrollbar);
|
||||||
for (int i = 0; i < zelda3::kSecretItemNames.size(); i++) {
|
for (int i = 0; i < zelda3::overworld::kSecretItemNames.size(); i++) {
|
||||||
if (ImGui::Selectable(zelda3::kSecretItemNames[i].c_str(),
|
if (ImGui::Selectable(zelda3::overworld::kSecretItemNames[i].c_str(),
|
||||||
i == new_item_id)) {
|
i == new_item_id)) {
|
||||||
new_item_id = i;
|
new_item_id = i;
|
||||||
}
|
}
|
||||||
@@ -1282,7 +1304,8 @@ void DrawItemInsertPopup() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool DrawItemEditorPopup(zelda3::OverworldItem &item) {
|
// TODO: Implement deleting OverworldItem objects, currently only hides them
|
||||||
|
bool DrawItemEditorPopup(zelda3::overworld::OverworldItem &item) {
|
||||||
static bool set_done = false;
|
static bool set_done = false;
|
||||||
if (set_done) {
|
if (set_done) {
|
||||||
set_done = false;
|
set_done = false;
|
||||||
@@ -1292,8 +1315,8 @@ bool DrawItemEditorPopup(zelda3::OverworldItem &item) {
|
|||||||
ImGui::BeginChild("ScrollRegion", ImVec2(150, 150), true,
|
ImGui::BeginChild("ScrollRegion", ImVec2(150, 150), true,
|
||||||
ImGuiWindowFlags_AlwaysVerticalScrollbar);
|
ImGuiWindowFlags_AlwaysVerticalScrollbar);
|
||||||
ImGui::BeginGroup();
|
ImGui::BeginGroup();
|
||||||
for (int i = 0; i < zelda3::kSecretItemNames.size(); i++) {
|
for (int i = 0; i < zelda3::overworld::kSecretItemNames.size(); i++) {
|
||||||
if (ImGui::Selectable(zelda3::kSecretItemNames[i].c_str(),
|
if (ImGui::Selectable(zelda3::overworld::kSecretItemNames[i].c_str(),
|
||||||
item.id == i)) {
|
item.id == i)) {
|
||||||
item.id = i;
|
item.id = i;
|
||||||
}
|
}
|
||||||
@@ -1326,7 +1349,7 @@ void OverworldEditor::DrawOverworldItems() {
|
|||||||
// Get the item's bitmap and real X and Y positions
|
// Get the item's bitmap and real X and Y positions
|
||||||
if (item.room_map_id < 0x40 + (current_world_ * 0x40) &&
|
if (item.room_map_id < 0x40 + (current_world_ * 0x40) &&
|
||||||
item.room_map_id >= (current_world_ * 0x40) && !item.deleted) {
|
item.room_map_id >= (current_world_ * 0x40) && !item.deleted) {
|
||||||
std::string item_name = zelda3::kSecretItemNames[item.id];
|
std::string item_name = zelda3::overworld::kSecretItemNames[item.id];
|
||||||
|
|
||||||
ow_map_canvas_.DrawRect(item.x_, item.y_, 16, 16, ImVec4(255, 0, 0, 150));
|
ow_map_canvas_.DrawRect(item.x_, item.y_, 16, 16, ImVec4(255, 0, 0, 150));
|
||||||
|
|
||||||
@@ -1461,6 +1484,7 @@ void DrawSpriteTable(std::function<void(int)> onSpriteSelect) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: Implement deleting OverworldSprite objects
|
||||||
void DrawSpriteInserterPopup() {
|
void DrawSpriteInserterPopup() {
|
||||||
if (ImGui::BeginPopup("Sprite Inserter")) {
|
if (ImGui::BeginPopup("Sprite Inserter")) {
|
||||||
static int new_sprite_id = 0;
|
static int new_sprite_id = 0;
|
||||||
@@ -1576,8 +1600,6 @@ void OverworldEditor::DrawOverworldSprites() {
|
|||||||
// ----------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------
|
||||||
|
|
||||||
absl::Status OverworldEditor::LoadGraphics() {
|
absl::Status OverworldEditor::LoadGraphics() {
|
||||||
// Load all of the graphics data from the game.
|
|
||||||
PRINT_IF_ERROR(rom()->LoadAllGraphicsData())
|
|
||||||
graphics_bin_ = rom()->graphics_bin();
|
graphics_bin_ = rom()->graphics_bin();
|
||||||
|
|
||||||
// Load the Link to the Past overworld.
|
// Load the Link to the Past overworld.
|
||||||
@@ -1585,14 +1607,12 @@ absl::Status OverworldEditor::LoadGraphics() {
|
|||||||
palette_ = overworld_.AreaPalette();
|
palette_ = overworld_.AreaPalette();
|
||||||
|
|
||||||
// Create the area graphics image
|
// Create the area graphics image
|
||||||
gui::BuildAndRenderBitmapPipeline(0x80, 0x200, 0x40,
|
rom()->CreateAndRenderBitmap(0x80, 0x200, 0x40, overworld_.current_graphics(),
|
||||||
overworld_.current_graphics(), *rom(),
|
current_gfx_bmp_, palette_);
|
||||||
current_gfx_bmp_, palette_);
|
|
||||||
|
|
||||||
// Create the tile16 blockset image
|
// Create the tile16 blockset image
|
||||||
gui::BuildAndRenderBitmapPipeline(0x80, 0x2000, 0x08,
|
rom()->CreateAndRenderBitmap(0x80, 0x2000, 0x08, overworld_.Tile16Blockset(),
|
||||||
overworld_.Tile16Blockset(), *rom(),
|
tile16_blockset_bmp_, palette_);
|
||||||
tile16_blockset_bmp_, palette_);
|
|
||||||
map_blockset_loaded_ = true;
|
map_blockset_loaded_ = true;
|
||||||
|
|
||||||
// Copy the tile16 data into individual tiles.
|
// Copy the tile16 data into individual tiles.
|
||||||
@@ -1620,18 +1640,17 @@ absl::Status OverworldEditor::LoadGraphics() {
|
|||||||
// Render the bitmaps of each tile.
|
// Render the bitmaps of each tile.
|
||||||
for (int id = 0; id < 4096; id++) {
|
for (int id = 0; id < 4096; id++) {
|
||||||
tile16_individual_.emplace_back();
|
tile16_individual_.emplace_back();
|
||||||
gui::BuildAndRenderBitmapPipeline(0x10, 0x10, 0x80,
|
RETURN_IF_ERROR(rom()->CreateAndRenderBitmap(
|
||||||
tile16_individual_data_[id], *rom(),
|
0x10, 0x10, 0x80, tile16_individual_data_[id], tile16_individual_[id],
|
||||||
tile16_individual_[id], palette_);
|
palette_));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Render the overworld maps loaded from the ROM.
|
// Render the overworld maps loaded from the ROM.
|
||||||
for (int i = 0; i < zelda3::kNumOverworldMaps; ++i) {
|
for (int i = 0; i < zelda3::overworld::kNumOverworldMaps; ++i) {
|
||||||
overworld_.set_current_map(i);
|
overworld_.set_current_map(i);
|
||||||
auto palette = overworld_.AreaPalette();
|
auto palette = overworld_.AreaPalette();
|
||||||
gui::BuildAndRenderBitmapPipeline(0x200, 0x200, 0x200,
|
RETURN_IF_ERROR(rom()->CreateAndRenderBitmap(
|
||||||
overworld_.BitmapData(), *rom(),
|
0x200, 0x200, 0x200, overworld_.BitmapData(), maps_bmp_[i], palette));
|
||||||
maps_bmp_[i], palette);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (flags()->overworld.kDrawOverworldSprites) {
|
if (flags()->overworld.kDrawOverworldSprites) {
|
||||||
@@ -1641,19 +1660,19 @@ absl::Status OverworldEditor::LoadGraphics() {
|
|||||||
return absl::OkStatus();
|
return absl::OkStatus();
|
||||||
}
|
}
|
||||||
|
|
||||||
void OverworldEditor::RefreshTile16Blockset() {
|
absl::Status OverworldEditor::RefreshTile16Blockset() {
|
||||||
if (current_blockset_ ==
|
if (current_blockset_ ==
|
||||||
overworld_.overworld_map(current_map_)->area_graphics()) {
|
overworld_.overworld_map(current_map_)->area_graphics()) {
|
||||||
return;
|
return absl::OkStatus();
|
||||||
}
|
}
|
||||||
current_blockset_ = overworld_.overworld_map(current_map_)->area_graphics();
|
current_blockset_ = overworld_.overworld_map(current_map_)->area_graphics();
|
||||||
|
|
||||||
overworld_.set_current_map(current_map_);
|
overworld_.set_current_map(current_map_);
|
||||||
palette_ = overworld_.AreaPalette();
|
palette_ = overworld_.AreaPalette();
|
||||||
// Create the tile16 blockset image
|
// Create the tile16 blockset image
|
||||||
gui::BuildAndRenderBitmapPipeline(0x80, 0x2000, 0x08,
|
RETURN_IF_ERROR(rom()->CreateAndRenderBitmap(0x80, 0x2000, 0x08,
|
||||||
overworld_.Tile16Blockset(), *rom(),
|
overworld_.Tile16Blockset(),
|
||||||
tile16_blockset_bmp_, palette_);
|
tile16_blockset_bmp_, palette_));
|
||||||
|
|
||||||
// Copy the tile16 data into individual tiles.
|
// Copy the tile16 data into individual tiles.
|
||||||
auto tile16_data = overworld_.Tile16Blockset();
|
auto tile16_data = overworld_.Tile16Blockset();
|
||||||
@@ -1691,9 +1710,11 @@ void OverworldEditor::RefreshTile16Blockset() {
|
|||||||
|
|
||||||
// Render the bitmaps of each tile.
|
// Render the bitmaps of each tile.
|
||||||
for (int id = 0; id < 4096; id++) {
|
for (int id = 0; id < 4096; id++) {
|
||||||
tile16_individual_[id].ApplyPalette(palette_);
|
RETURN_IF_ERROR(tile16_individual_[id].ApplyPalette(palette_));
|
||||||
rom()->UpdateBitmap(&tile16_individual_[id]);
|
rom()->UpdateBitmap(&tile16_individual_[id]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return absl::OkStatus();
|
||||||
}
|
}
|
||||||
|
|
||||||
absl::Status OverworldEditor::LoadSpriteGraphics() {
|
absl::Status OverworldEditor::LoadSpriteGraphics() {
|
||||||
@@ -1705,7 +1726,7 @@ absl::Status OverworldEditor::LoadSpriteGraphics() {
|
|||||||
int depth = 0x40;
|
int depth = 0x40;
|
||||||
auto spr_gfx = sprite.PreviewGraphics();
|
auto spr_gfx = sprite.PreviewGraphics();
|
||||||
sprite_previews_[sprite.id()].Create(width, height, depth, spr_gfx);
|
sprite_previews_[sprite.id()].Create(width, height, depth, spr_gfx);
|
||||||
sprite_previews_[sprite.id()].ApplyPalette(palette_);
|
RETURN_IF_ERROR(sprite_previews_[sprite.id()].ApplyPalette(palette_));
|
||||||
rom()->RenderBitmap(&(sprite_previews_[sprite.id()]));
|
rom()->RenderBitmap(&(sprite_previews_[sprite.id()]));
|
||||||
}
|
}
|
||||||
return absl::OkStatus();
|
return absl::OkStatus();
|
||||||
@@ -1880,15 +1901,15 @@ void OverworldEditor::DrawUsageGrid() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void OverworldEditor::LoadAnimatedMaps() {
|
absl::Status OverworldEditor::LoadAnimatedMaps() {
|
||||||
int world_index = 0;
|
int world_index = 0;
|
||||||
static std::vector<bool> animated_built(0x40, false);
|
static std::vector<bool> animated_built(0x40, false);
|
||||||
if (!animated_built[world_index]) {
|
if (!animated_built[world_index]) {
|
||||||
animated_maps_[world_index] = maps_bmp_[world_index];
|
animated_maps_[world_index] = maps_bmp_[world_index];
|
||||||
auto &map = *overworld_.mutable_overworld_map(world_index);
|
auto &map = *overworld_.mutable_overworld_map(world_index);
|
||||||
map.DrawAnimatedTiles();
|
map.DrawAnimatedTiles();
|
||||||
map.BuildTileset();
|
RETURN_IF_ERROR(map.BuildTileset());
|
||||||
map.BuildTiles16Gfx(overworld_.tiles16().size());
|
RETURN_IF_ERROR(map.BuildTiles16Gfx(overworld_.tiles16().size()));
|
||||||
OWBlockset blockset;
|
OWBlockset blockset;
|
||||||
if (current_world_ == 0) {
|
if (current_world_ == 0) {
|
||||||
blockset = overworld_.map_tiles().light_world;
|
blockset = overworld_.map_tiles().light_world;
|
||||||
@@ -1897,14 +1918,16 @@ void OverworldEditor::LoadAnimatedMaps() {
|
|||||||
} else {
|
} else {
|
||||||
blockset = overworld_.map_tiles().special_world;
|
blockset = overworld_.map_tiles().special_world;
|
||||||
}
|
}
|
||||||
map.BuildBitmap(blockset);
|
RETURN_IF_ERROR(map.BuildBitmap(blockset));
|
||||||
|
|
||||||
gui::BuildAndRenderBitmapPipeline(0x200, 0x200, 0x200, map.bitmap_data(),
|
RETURN_IF_ERROR(rom()->CreateAndRenderBitmap(
|
||||||
*rom(), animated_maps_[world_index],
|
0x200, 0x200, 0x200, map.bitmap_data(), animated_maps_[world_index],
|
||||||
*map.mutable_current_palette());
|
*map.mutable_current_palette()));
|
||||||
|
|
||||||
animated_built[world_index] = true;
|
animated_built[world_index] = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return absl::OkStatus();
|
||||||
}
|
}
|
||||||
|
|
||||||
// ----------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------
|
||||||
|
|||||||
@@ -12,11 +12,12 @@
|
|||||||
#include "absl/status/statusor.h"
|
#include "absl/status/statusor.h"
|
||||||
#include "absl/strings/str_format.h"
|
#include "absl/strings/str_format.h"
|
||||||
#include "app/core/common.h"
|
#include "app/core/common.h"
|
||||||
#include "app/core/editor.h"
|
#include "app/editor/context/entrance_context.h"
|
||||||
#include "app/editor/context/gfx_context.h"
|
#include "app/editor/context/gfx_context.h"
|
||||||
#include "app/editor/modules/gfx_group_editor.h"
|
#include "app/editor/modules/gfx_group_editor.h"
|
||||||
#include "app/editor/modules/palette_editor.h"
|
#include "app/editor/modules/palette_editor.h"
|
||||||
#include "app/editor/modules/tile16_editor.h"
|
#include "app/editor/modules/tile16_editor.h"
|
||||||
|
#include "app/editor/utils/editor.h"
|
||||||
#include "app/gfx/bitmap.h"
|
#include "app/gfx/bitmap.h"
|
||||||
#include "app/gfx/snes_palette.h"
|
#include "app/gfx/snes_palette.h"
|
||||||
#include "app/gfx/snes_tile.h"
|
#include "app/gfx/snes_tile.h"
|
||||||
@@ -24,7 +25,7 @@
|
|||||||
#include "app/gui/icons.h"
|
#include "app/gui/icons.h"
|
||||||
#include "app/gui/pipeline.h"
|
#include "app/gui/pipeline.h"
|
||||||
#include "app/rom.h"
|
#include "app/rom.h"
|
||||||
#include "app/zelda3/overworld.h"
|
#include "app/zelda3/overworld/overworld.h"
|
||||||
|
|
||||||
namespace yaze {
|
namespace yaze {
|
||||||
namespace app {
|
namespace app {
|
||||||
@@ -61,9 +62,26 @@ constexpr absl::string_view kTileSelectorTab = "##TileSelectorTabBar";
|
|||||||
constexpr absl::string_view kOWEditTable = "##OWEditTable";
|
constexpr absl::string_view kOWEditTable = "##OWEditTable";
|
||||||
constexpr absl::string_view kOWMapTable = "#MapSettingsTable";
|
constexpr absl::string_view kOWMapTable = "#MapSettingsTable";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @class OverworldEditor
|
||||||
|
* @brief Manipulates the Overworld and OverworldMap data in a Rom.
|
||||||
|
*
|
||||||
|
* The `OverworldEditor` class is responsible for managing the editing and
|
||||||
|
* manipulation of the overworld in a game. The user can drag and drop tiles,
|
||||||
|
* modify OverworldEntrance, OverworldExit, Sprite, and OverworldItem
|
||||||
|
* as well as change the gfx and palettes used in each overworld map.
|
||||||
|
*
|
||||||
|
* The Overworld itself is a series of bitmap images which exist inside each
|
||||||
|
* OverworldMap object. The drawing of the overworld is done using the Canvas
|
||||||
|
* class in conjunction with these underlying Bitmap objects.
|
||||||
|
*
|
||||||
|
* Provides access to the GfxGroupEditor and Tile16Editor through popup windows.
|
||||||
|
*
|
||||||
|
*/
|
||||||
class OverworldEditor : public Editor,
|
class OverworldEditor : public Editor,
|
||||||
public SharedROM,
|
public SharedRom,
|
||||||
public GfxContext,
|
public context::GfxContext,
|
||||||
|
public context::EntranceContext,
|
||||||
public core::ExperimentFlags {
|
public core::ExperimentFlags {
|
||||||
public:
|
public:
|
||||||
absl::Status Update() final;
|
absl::Status Update() final;
|
||||||
@@ -75,6 +93,9 @@ class OverworldEditor : public Editor,
|
|||||||
|
|
||||||
auto overworld() { return &overworld_; }
|
auto overworld() { return &overworld_; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief
|
||||||
|
*/
|
||||||
int jump_to_tab() { return jump_to_tab_; }
|
int jump_to_tab() { return jump_to_tab_; }
|
||||||
int jump_to_tab_ = -1;
|
int jump_to_tab_ = -1;
|
||||||
|
|
||||||
@@ -91,21 +112,33 @@ class OverworldEditor : public Editor,
|
|||||||
for (auto& [i, bmp] : current_graphics_set_) {
|
for (auto& [i, bmp] : current_graphics_set_) {
|
||||||
bmp.Cleanup();
|
bmp.Cleanup();
|
||||||
}
|
}
|
||||||
|
maps_bmp_.clear();
|
||||||
|
overworld_.Destroy();
|
||||||
|
all_gfx_loaded_ = false;
|
||||||
|
map_blockset_loaded_ = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Load the Bitmap objects for each OverworldMap.
|
||||||
|
*
|
||||||
|
* Calls the Overworld class to load the image data and palettes from the Rom,
|
||||||
|
* then renders the area graphics and tile16 blockset Bitmap objects before
|
||||||
|
* assembling the OverworldMap Bitmap objects.
|
||||||
|
*/
|
||||||
absl::Status LoadGraphics();
|
absl::Status LoadGraphics();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
absl::Status UpdateOverworldEdit();
|
absl::Status UpdateOverworldEdit();
|
||||||
|
absl::Status UpdateFullscreenCanvas();
|
||||||
|
|
||||||
absl::Status DrawToolset();
|
absl::Status DrawToolset();
|
||||||
void DrawOverworldMapSettings();
|
void DrawOverworldMapSettings();
|
||||||
|
|
||||||
void RefreshChildMap(int i);
|
void RefreshChildMap(int i);
|
||||||
void RefreshOverworldMap();
|
void RefreshOverworldMap();
|
||||||
void RefreshMapPalette();
|
absl::Status RefreshMapPalette();
|
||||||
void RefreshMapProperties();
|
void RefreshMapProperties();
|
||||||
void RefreshTile16Blockset();
|
absl::Status RefreshTile16Blockset();
|
||||||
|
|
||||||
void DrawOverworldEntrances(ImVec2 canvas_p, ImVec2 scrolling,
|
void DrawOverworldEntrances(ImVec2 canvas_p, ImVec2 scrolling,
|
||||||
bool holes = false);
|
bool holes = false);
|
||||||
@@ -118,14 +151,19 @@ class OverworldEditor : public Editor,
|
|||||||
void RenderUpdatedMapBitmap(const ImVec2& click_position,
|
void RenderUpdatedMapBitmap(const ImVec2& click_position,
|
||||||
const Bytes& tile_data);
|
const Bytes& tile_data);
|
||||||
void CheckForOverworldEdits();
|
void CheckForOverworldEdits();
|
||||||
void CheckForCurrentMap();
|
|
||||||
void CheckForSelectRectangle();
|
void CheckForSelectRectangle();
|
||||||
|
absl::Status CheckForCurrentMap();
|
||||||
|
void CheckForMousePan();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Allows the user to make changes to the overworld map.
|
||||||
|
*/
|
||||||
void DrawOverworldCanvas();
|
void DrawOverworldCanvas();
|
||||||
|
|
||||||
void DrawTile16Selector();
|
absl::Status DrawTile16Selector();
|
||||||
void DrawTile8Selector();
|
void DrawTile8Selector();
|
||||||
void DrawAreaGraphics();
|
absl::Status DrawAreaGraphics();
|
||||||
void DrawTileSelector();
|
absl::Status DrawTileSelector();
|
||||||
|
|
||||||
absl::Status LoadSpriteGraphics();
|
absl::Status LoadSpriteGraphics();
|
||||||
|
|
||||||
@@ -137,7 +175,7 @@ class OverworldEditor : public Editor,
|
|||||||
void DrawUsageGrid();
|
void DrawUsageGrid();
|
||||||
void CalculateUsageStats();
|
void CalculateUsageStats();
|
||||||
|
|
||||||
void LoadAnimatedMaps();
|
absl::Status LoadAnimatedMaps();
|
||||||
void DrawDebugWindow();
|
void DrawDebugWindow();
|
||||||
|
|
||||||
auto gfx_group_editor() const { return gfx_group_editor_; }
|
auto gfx_group_editor() const { return gfx_group_editor_; }
|
||||||
@@ -197,11 +235,11 @@ class OverworldEditor : public Editor,
|
|||||||
zelda3::OverworldEntity* current_entity_;
|
zelda3::OverworldEntity* current_entity_;
|
||||||
|
|
||||||
int current_entrance_id_ = 0;
|
int current_entrance_id_ = 0;
|
||||||
zelda3::OverworldEntrance current_entrance_;
|
zelda3::overworld::OverworldEntrance current_entrance_;
|
||||||
int current_exit_id_ = 0;
|
int current_exit_id_ = 0;
|
||||||
zelda3::OverworldExit current_exit_;
|
zelda3::overworld::OverworldExit current_exit_;
|
||||||
int current_item_id_ = 0;
|
int current_item_id_ = 0;
|
||||||
zelda3::OverworldItem current_item_;
|
zelda3::overworld::OverworldItem current_item_;
|
||||||
int current_sprite_id_ = 0;
|
int current_sprite_id_ = 0;
|
||||||
zelda3::Sprite current_sprite_;
|
zelda3::Sprite current_sprite_;
|
||||||
|
|
||||||
@@ -219,7 +257,7 @@ class OverworldEditor : public Editor,
|
|||||||
Tile16Editor tile16_editor_;
|
Tile16Editor tile16_editor_;
|
||||||
GfxGroupEditor gfx_group_editor_;
|
GfxGroupEditor gfx_group_editor_;
|
||||||
PaletteEditor palette_editor_;
|
PaletteEditor palette_editor_;
|
||||||
zelda3::Overworld overworld_;
|
zelda3::overworld::Overworld overworld_;
|
||||||
|
|
||||||
gui::Canvas ow_map_canvas_{ImVec2(0x200 * 8, 0x200 * 8),
|
gui::Canvas ow_map_canvas_{ImVec2(0x200 * 8, 0x200 * 8),
|
||||||
gui::CanvasGridSize::k64x64};
|
gui::CanvasGridSize::k64x64};
|
||||||
|
|||||||
@@ -117,25 +117,30 @@ absl::Status ScreenEditor::LoadDungeonMaps() {
|
|||||||
for (int d = 0; d < 14; d++) {
|
for (int d = 0; d < 14; d++) {
|
||||||
current_floor_rooms_d.clear();
|
current_floor_rooms_d.clear();
|
||||||
current_floor_gfx_d.clear();
|
current_floor_gfx_d.clear();
|
||||||
ASSIGN_OR_RETURN(int ptr,
|
ASSIGN_OR_RETURN(
|
||||||
rom()->ReadWord(zelda3::kDungeonMapRoomsPtr + (d * 2)));
|
int ptr,
|
||||||
ASSIGN_OR_RETURN(int ptrGFX,
|
rom()->ReadWord(zelda3::screen::kDungeonMapRoomsPtr + (d * 2)));
|
||||||
rom()->ReadWord(zelda3::kDungeonMapRoomsPtr + (d * 2)));
|
ASSIGN_OR_RETURN(
|
||||||
|
int ptrGFX,
|
||||||
|
rom()->ReadWord(zelda3::screen::kDungeonMapRoomsPtr + (d * 2)));
|
||||||
ptr |= 0x0A0000; // Add bank to the short ptr
|
ptr |= 0x0A0000; // Add bank to the short ptr
|
||||||
ptrGFX |= 0x0A0000; // Add bank to the short ptr
|
ptrGFX |= 0x0A0000; // Add bank to the short ptr
|
||||||
int pcPtr = core::SnesToPc(ptr); // Contains data for the next 25 rooms
|
int pcPtr = core::SnesToPc(ptr); // Contains data for the next 25 rooms
|
||||||
int pcPtrGFX =
|
int pcPtrGFX =
|
||||||
core::SnesToPc(ptrGFX); // Contains data for the next 25 rooms
|
core::SnesToPc(ptrGFX); // Contains data for the next 25 rooms
|
||||||
|
|
||||||
ASSIGN_OR_RETURN(ushort bossRoomD,
|
ASSIGN_OR_RETURN(
|
||||||
rom()->ReadWord(zelda3::kDungeonMapBossRooms + (d * 2)));
|
ushort bossRoomD,
|
||||||
|
rom()->ReadWord(zelda3::screen::kDungeonMapBossRooms + (d * 2)));
|
||||||
|
|
||||||
ASSIGN_OR_RETURN(nbr_basement_d,
|
ASSIGN_OR_RETURN(
|
||||||
rom()->ReadByte(zelda3::kDungeonMapFloors + (d * 2)));
|
nbr_basement_d,
|
||||||
|
rom()->ReadByte(zelda3::screen::kDungeonMapFloors + (d * 2)));
|
||||||
nbr_basement_d &= 0x0F;
|
nbr_basement_d &= 0x0F;
|
||||||
|
|
||||||
ASSIGN_OR_RETURN(nbr_floor_d,
|
ASSIGN_OR_RETURN(
|
||||||
rom()->ReadByte(zelda3::kDungeonMapFloors + (d * 2)));
|
nbr_floor_d,
|
||||||
|
rom()->ReadByte(zelda3::screen::kDungeonMapFloors + (d * 2)));
|
||||||
nbr_floor_d &= 0xF0;
|
nbr_floor_d &= 0xF0;
|
||||||
nbr_floor_d = nbr_floor_d >> 4;
|
nbr_floor_d = nbr_floor_d >> 4;
|
||||||
|
|
||||||
@@ -179,8 +184,8 @@ absl::Status ScreenEditor::LoadDungeonMaps() {
|
|||||||
|
|
||||||
absl::Status ScreenEditor::SaveDungeonMaps() {
|
absl::Status ScreenEditor::SaveDungeonMaps() {
|
||||||
for (int d = 0; d < 14; d++) {
|
for (int d = 0; d < 14; d++) {
|
||||||
int ptr = zelda3::kDungeonMapRoomsPtr + (d * 2);
|
int ptr = zelda3::screen::kDungeonMapRoomsPtr + (d * 2);
|
||||||
int ptrGFX = zelda3::kDungeonMapGfxPtr + (d * 2);
|
int ptrGFX = zelda3::screen::kDungeonMapGfxPtr + (d * 2);
|
||||||
int pcPtr = core::SnesToPc(ptr);
|
int pcPtr = core::SnesToPc(ptr);
|
||||||
int pcPtrGFX = core::SnesToPc(ptrGFX);
|
int pcPtrGFX = core::SnesToPc(ptrGFX);
|
||||||
|
|
||||||
@@ -208,9 +213,9 @@ absl::Status ScreenEditor::LoadDungeonMapTile16() {
|
|||||||
tile16_sheet_.Init(256, 192, gfx::TileType::Tile16);
|
tile16_sheet_.Init(256, 192, gfx::TileType::Tile16);
|
||||||
|
|
||||||
for (int i = 0; i < 186; i++) {
|
for (int i = 0; i < 186; i++) {
|
||||||
int addr = zelda3::kDungeonMapTile16;
|
int addr = zelda3::screen::kDungeonMapTile16;
|
||||||
if (rom()->data()[zelda3::kDungeonMapExpCheck] != 0xB9) {
|
if (rom()->data()[zelda3::screen::kDungeonMapExpCheck] != 0xB9) {
|
||||||
addr = zelda3::kDungeonMapTile16Expanded;
|
addr = zelda3::screen::kDungeonMapTile16Expanded;
|
||||||
}
|
}
|
||||||
|
|
||||||
ASSIGN_OR_RETURN(auto tl, rom()->ReadWord(addr + (i * 8)));
|
ASSIGN_OR_RETURN(auto tl, rom()->ReadWord(addr + (i * 8)));
|
||||||
|
|||||||
@@ -22,7 +22,21 @@ namespace yaze {
|
|||||||
namespace app {
|
namespace app {
|
||||||
namespace editor {
|
namespace editor {
|
||||||
|
|
||||||
class ScreenEditor : public SharedROM {
|
/**
|
||||||
|
* @brief The ScreenEditor class allows the user to edit a variety of screens in
|
||||||
|
* the game or create a custom menu.
|
||||||
|
*
|
||||||
|
* This class is currently a work in progress (WIP) and provides functionality
|
||||||
|
* for updating the screens, saving dungeon maps, drawing different types of
|
||||||
|
* screens, loading dungeon maps, and managing various properties related to the
|
||||||
|
* editor.
|
||||||
|
*
|
||||||
|
* The screens that can be edited include the title screen, naming screen,
|
||||||
|
* overworld map, inventory menu, and more.
|
||||||
|
*
|
||||||
|
* The class inherits from the SharedRom class.
|
||||||
|
*/
|
||||||
|
class ScreenEditor : public SharedRom {
|
||||||
public:
|
public:
|
||||||
ScreenEditor();
|
ScreenEditor();
|
||||||
void Update();
|
void Update();
|
||||||
@@ -43,7 +57,7 @@ class ScreenEditor : public SharedROM {
|
|||||||
void DrawDungeonMapsTabs();
|
void DrawDungeonMapsTabs();
|
||||||
void DrawDungeonMapsEditor();
|
void DrawDungeonMapsEditor();
|
||||||
|
|
||||||
std::vector<zelda3::DungeonMap> dungeon_maps_;
|
std::vector<zelda3::screen::DungeonMap> dungeon_maps_;
|
||||||
std::vector<std::vector<std::array<std::string, 25>>> dungeon_map_labels_;
|
std::vector<std::vector<std::array<std::string, 25>>> dungeon_map_labels_;
|
||||||
|
|
||||||
std::unordered_map<int, gfx::Bitmap> tile16_individual_;
|
std::unordered_map<int, gfx::Bitmap> tile16_individual_;
|
||||||
@@ -60,7 +74,7 @@ class ScreenEditor : public SharedROM {
|
|||||||
bool paste_button_pressed = false;
|
bool paste_button_pressed = false;
|
||||||
|
|
||||||
Bytes all_gfx_;
|
Bytes all_gfx_;
|
||||||
zelda3::Inventory inventory_;
|
zelda3::screen::Inventory inventory_;
|
||||||
gfx::SnesPalette palette_;
|
gfx::SnesPalette palette_;
|
||||||
gui::Canvas screen_canvas_;
|
gui::Canvas screen_canvas_;
|
||||||
gui::Canvas tilesheet_canvas_;
|
gui::Canvas tilesheet_canvas_;
|
||||||
|
|||||||
@@ -9,17 +9,41 @@ namespace yaze {
|
|||||||
namespace app {
|
namespace app {
|
||||||
namespace editor {
|
namespace editor {
|
||||||
|
|
||||||
class SpriteEditor : public SharedROM {
|
/**
|
||||||
|
* @class SpriteEditor
|
||||||
|
* @brief Allows the user to edit sprites.
|
||||||
|
*
|
||||||
|
* This class provides functionality for updating the sprite editor, drawing the
|
||||||
|
* editor table, drawing the sprite canvas, and drawing the current sheets.
|
||||||
|
*/
|
||||||
|
class SpriteEditor : public SharedRom {
|
||||||
public:
|
public:
|
||||||
|
/**
|
||||||
|
* @brief Updates the sprite editor.
|
||||||
|
*
|
||||||
|
* @return An absl::Status indicating the success or failure of the update.
|
||||||
|
*/
|
||||||
absl::Status Update();
|
absl::Status Update();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
/**
|
||||||
|
* @brief Draws the editor table.
|
||||||
|
*/
|
||||||
void DrawEditorTable();
|
void DrawEditorTable();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Draws the sprite canvas.
|
||||||
|
*/
|
||||||
void DrawSpriteCanvas();
|
void DrawSpriteCanvas();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Draws the current sheets.
|
||||||
|
*/
|
||||||
void DrawCurrentSheets();
|
void DrawCurrentSheets();
|
||||||
|
|
||||||
uint8_t current_sheets_[8];
|
uint8_t current_sheets_[8]; /**< Array to store the current sheets. */
|
||||||
bool sheets_loaded_ = false;
|
bool sheets_loaded_ =
|
||||||
|
false; /**< Flag indicating whether the sheets are loaded or not. */
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace editor
|
} // namespace editor
|
||||||
|
|||||||
@@ -3,6 +3,21 @@
|
|||||||
|
|
||||||
#include "absl/status/status.h"
|
#include "absl/status/status.h"
|
||||||
|
|
||||||
|
namespace yaze {
|
||||||
|
namespace app {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @namespace yaze::app::editor
|
||||||
|
* @brief Editors are the view controllers for the application.
|
||||||
|
*/
|
||||||
|
namespace editor {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @class Editor
|
||||||
|
* @brief Interface for editor classes.
|
||||||
|
*
|
||||||
|
* Provides basic editing operations that each editor should implement.
|
||||||
|
*/
|
||||||
class Editor {
|
class Editor {
|
||||||
public:
|
public:
|
||||||
Editor() = default;
|
Editor() = default;
|
||||||
@@ -18,4 +33,8 @@ class Editor {
|
|||||||
virtual absl::Status Update() = 0;
|
virtual absl::Status Update() = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
} // namespace editor
|
||||||
|
} // namespace app
|
||||||
|
} // namespace yaze
|
||||||
|
|
||||||
#endif // YAZE_APP_CORE_EDITOR_H
|
#endif // YAZE_APP_CORE_EDITOR_H
|
||||||
@@ -13,8 +13,9 @@
|
|||||||
namespace yaze {
|
namespace yaze {
|
||||||
namespace app {
|
namespace app {
|
||||||
namespace emu {
|
namespace emu {
|
||||||
|
namespace audio {
|
||||||
|
|
||||||
void APU::Init() {
|
void Apu::Init() {
|
||||||
// Set the clock frequency
|
// Set the clock frequency
|
||||||
clock_.SetFrequency(kApuClockSpeed);
|
clock_.SetFrequency(kApuClockSpeed);
|
||||||
|
|
||||||
@@ -27,17 +28,17 @@ void APU::Init() {
|
|||||||
[this](int16_t sample) { this->PushToAudioBuffer(sample); });
|
[this](int16_t sample) { this->PushToAudioBuffer(sample); });
|
||||||
}
|
}
|
||||||
|
|
||||||
void APU::Reset() {
|
void Apu::Reset() {
|
||||||
clock_.ResetAccumulatedTime();
|
clock_.ResetAccumulatedTime();
|
||||||
spc700_.Reset();
|
spc700_.Reset();
|
||||||
dsp_.Reset();
|
dsp_.Reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
void APU::Update() {
|
void Apu::Update() {
|
||||||
auto cycles_to_run = clock_.GetCycleCount();
|
auto cycles_to_run = clock_.GetCycleCount();
|
||||||
|
|
||||||
for (auto i = 0; i < cycles_to_run; ++i) {
|
for (auto i = 0; i < cycles_to_run; ++i) {
|
||||||
// Update the APU
|
// Update the Apu
|
||||||
UpdateChannelSettings();
|
UpdateChannelSettings();
|
||||||
|
|
||||||
// Update the SPC700
|
// Update the SPC700
|
||||||
@@ -49,14 +50,14 @@ void APU::Update() {
|
|||||||
ProcessSamples();
|
ProcessSamples();
|
||||||
}
|
}
|
||||||
|
|
||||||
void APU::Notify(uint32_t address, uint8_t data) {
|
void Apu::Notify(uint32_t address, uint8_t data) {
|
||||||
if (address < 0x2140 || address > 0x2143) {
|
if (address < 0x2140 || address > 0x2143) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
auto offset = address - 0x2140;
|
auto offset = address - 0x2140;
|
||||||
spc700_.write(offset, data);
|
spc700_.write(offset, data);
|
||||||
|
|
||||||
// HACK - This is a temporary solution to get the APU to play audio
|
// HACK - This is a temporary solution to get the Apu to play audio
|
||||||
ports_[address - 0x2140] = data;
|
ports_[address - 0x2140] = data;
|
||||||
switch (address) {
|
switch (address) {
|
||||||
case 0x2140:
|
case 0x2140:
|
||||||
@@ -76,7 +77,7 @@ void APU::Notify(uint32_t address, uint8_t data) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void APU::ProcessSamples() {
|
void Apu::ProcessSamples() {
|
||||||
// Fetch sample data from AudioRam
|
// Fetch sample data from AudioRam
|
||||||
// Iterate over all voices
|
// Iterate over all voices
|
||||||
for (uint8_t voice_num = 0; voice_num < 8; voice_num++) {
|
for (uint8_t voice_num = 0; voice_num < 8; voice_num++) {
|
||||||
@@ -91,17 +92,17 @@ void APU::ProcessSamples() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
uint8_t APU::FetchSampleForVoice(uint8_t voice_num) {
|
uint8_t Apu::FetchSampleForVoice(uint8_t voice_num) {
|
||||||
uint16_t address = CalculateAddressForVoice(voice_num);
|
uint16_t address = CalculateAddressForVoice(voice_num);
|
||||||
return aram_.read(address);
|
return aram_.read(address);
|
||||||
}
|
}
|
||||||
|
|
||||||
uint16_t APU::CalculateAddressForVoice(uint8_t voice_num) {
|
uint16_t Apu::CalculateAddressForVoice(uint8_t voice_num) {
|
||||||
// TODO: Calculate the address for the specified voice
|
// TODO: Calculate the address for the specified voice
|
||||||
return voice_num;
|
return voice_num;
|
||||||
}
|
}
|
||||||
|
|
||||||
int16_t APU::GetNextSample() {
|
int16_t Apu::GetNextSample() {
|
||||||
if (!audio_samples_.empty()) {
|
if (!audio_samples_.empty()) {
|
||||||
int16_t sample = audio_samples_.front();
|
int16_t sample = audio_samples_.front();
|
||||||
audio_samples_.erase(audio_samples_.begin());
|
audio_samples_.erase(audio_samples_.begin());
|
||||||
@@ -110,30 +111,31 @@ int16_t APU::GetNextSample() {
|
|||||||
return 0; // TODO: Return the last sample instead of 0.
|
return 0; // TODO: Return the last sample instead of 0.
|
||||||
}
|
}
|
||||||
|
|
||||||
const std::vector<int16_t>& APU::GetAudioSamples() const {
|
const std::vector<int16_t>& Apu::GetAudioSamples() const {
|
||||||
return audio_samples_;
|
return audio_samples_;
|
||||||
}
|
}
|
||||||
|
|
||||||
void APU::UpdateChannelSettings() {
|
void Apu::UpdateChannelSettings() {
|
||||||
// TODO: Implement this method to update the channel settings.
|
// TODO: Implement this method to update the channel settings.
|
||||||
}
|
}
|
||||||
|
|
||||||
int16_t APU::GenerateSample(int channel) {
|
int16_t Apu::GenerateSample(int channel) {
|
||||||
// TODO: Implement this method to generate a sample for the specified channel.
|
// TODO: Implement this method to generate a sample for the specified channel.
|
||||||
}
|
}
|
||||||
|
|
||||||
void APU::ApplyEnvelope(int channel) {
|
void Apu::ApplyEnvelope(int channel) {
|
||||||
// TODO: Implement this method to apply an envelope to the specified channel.
|
// TODO: Implement this method to apply an envelope to the specified channel.
|
||||||
}
|
}
|
||||||
|
|
||||||
uint8_t APU::ReadDspMemory(uint16_t address) {
|
uint8_t Apu::ReadDspMemory(uint16_t address) {
|
||||||
return dsp_.ReadGlobalReg(address);
|
return dsp_.ReadGlobalReg(address);
|
||||||
}
|
}
|
||||||
|
|
||||||
void APU::WriteDspMemory(uint16_t address, uint8_t value) {
|
void Apu::WriteDspMemory(uint16_t address, uint8_t value) {
|
||||||
dsp_.WriteGlobalReg(address, value);
|
dsp_.WriteGlobalReg(address, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
} // namespace audio
|
||||||
} // namespace emu
|
} // namespace emu
|
||||||
} // namespace app
|
} // namespace app
|
||||||
} // namespace yaze
|
} // namespace yaze
|
||||||
@@ -13,13 +13,34 @@
|
|||||||
namespace yaze {
|
namespace yaze {
|
||||||
namespace app {
|
namespace app {
|
||||||
namespace emu {
|
namespace emu {
|
||||||
|
namespace audio {
|
||||||
|
|
||||||
|
using namespace memory;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
|
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
const int kApuClockSpeed = 1024000; // 1.024 MHz
|
||||||
|
const int apuSampleRate = 32000; // 32 KHz
|
||||||
|
const int apuClocksPerSample = 64; // 64 clocks per sample
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @class Apu
|
||||||
|
* @brief The Apu class represents the Audio Processing Unit (APU) of a system.
|
||||||
|
*
|
||||||
|
* The Apu class is responsible for generating audio samples and managing the
|
||||||
|
* APU state. It interacts with the Memory, AudioRam, and Clock classes to
|
||||||
|
* read/write data and update the clock. The class also implements the Observer
|
||||||
|
* interface to receive notifications from the system.
|
||||||
|
*
|
||||||
|
* @par IPL ROM Info
|
||||||
* 64 kilobytes of RAM are mapped across the 16-bit memory space of the SPC-700.
|
* 64 kilobytes of RAM are mapped across the 16-bit memory space of the SPC-700.
|
||||||
* Some regions of this space are overlaid with special hardware functions.
|
* Some regions of this space are overlaid with special hardware functions.
|
||||||
*
|
*
|
||||||
* Range Note
|
* @par Range Note
|
||||||
* $0000-00EF Zero Page RAM
|
* $0000-00EF Zero Page RAM
|
||||||
* $00F0-00FF Sound CPU Registers
|
* $00F0-00FF Sound CPU Registers
|
||||||
* $0100-01FF Stack Page RAM
|
* $0100-01FF Stack Page RAM
|
||||||
@@ -30,16 +51,10 @@ namespace emu {
|
|||||||
* underlying RAM can always be written to, and the high bit of the Control
|
* underlying RAM can always be written to, and the high bit of the Control
|
||||||
* register $F1 can be cleared to unmap the IPL ROM and allow read access to
|
* register $F1 can be cleared to unmap the IPL ROM and allow read access to
|
||||||
* this RAM.
|
* this RAM.
|
||||||
*
|
|
||||||
*/
|
*/
|
||||||
|
class Apu : public Observer {
|
||||||
const int kApuClockSpeed = 1024000; // 1.024 MHz
|
|
||||||
const int apuSampleRate = 32000; // 32 KHz
|
|
||||||
const int apuClocksPerSample = 64; // 64 clocks per sample
|
|
||||||
|
|
||||||
class APU : public Observer {
|
|
||||||
public:
|
public:
|
||||||
APU(MemoryImpl &memory, AudioRam &aram, Clock &clock)
|
Apu(MemoryImpl &memory, AudioRam &aram, Clock &clock)
|
||||||
: aram_(aram), clock_(clock), memory_(memory) {}
|
: aram_(aram), clock_(clock), memory_(memory) {}
|
||||||
|
|
||||||
void Init();
|
void Init();
|
||||||
@@ -130,6 +145,7 @@ class APU : public Observer {
|
|||||||
std::function<void()> ready_callback_;
|
std::function<void()> ready_callback_;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
} // namespace audio
|
||||||
} // namespace emu
|
} // namespace emu
|
||||||
} // namespace app
|
} // namespace app
|
||||||
} // namespace yaze
|
} // namespace yaze
|
||||||
|
|||||||
@@ -5,6 +5,7 @@
|
|||||||
namespace yaze {
|
namespace yaze {
|
||||||
namespace app {
|
namespace app {
|
||||||
namespace emu {
|
namespace emu {
|
||||||
|
namespace audio {
|
||||||
|
|
||||||
void DigitalSignalProcessor::Reset() {}
|
void DigitalSignalProcessor::Reset() {}
|
||||||
|
|
||||||
@@ -37,7 +38,8 @@ uint8_t DigitalSignalProcessor::ReadVoiceReg(uint8_t voice, uint8_t reg) const {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void DigitalSignalProcessor::WriteVoiceReg(uint8_t voice, uint8_t reg, uint8_t value) {
|
void DigitalSignalProcessor::WriteVoiceReg(uint8_t voice, uint8_t reg,
|
||||||
|
uint8_t value) {
|
||||||
voice %= kNumVoices;
|
voice %= kNumVoices;
|
||||||
switch (reg % kNumVoiceRegs) {
|
switch (reg % kNumVoiceRegs) {
|
||||||
case 0:
|
case 0:
|
||||||
@@ -69,9 +71,13 @@ void DigitalSignalProcessor::WriteVoiceReg(uint8_t voice, uint8_t reg, uint8_t v
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Set the callbacks
|
// Set the callbacks
|
||||||
void DigitalSignalProcessor::SetSampleFetcher(SampleFetcher fetcher) { sample_fetcher_ = fetcher; }
|
void DigitalSignalProcessor::SetSampleFetcher(SampleFetcher fetcher) {
|
||||||
|
sample_fetcher_ = fetcher;
|
||||||
|
}
|
||||||
|
|
||||||
void DigitalSignalProcessor::SetSamplePusher(SamplePusher pusher) { sample_pusher_ = pusher; }
|
void DigitalSignalProcessor::SetSamplePusher(SamplePusher pusher) {
|
||||||
|
sample_pusher_ = pusher;
|
||||||
|
}
|
||||||
|
|
||||||
int16_t DigitalSignalProcessor::DecodeSample(uint8_t voice_num) {
|
int16_t DigitalSignalProcessor::DecodeSample(uint8_t voice_num) {
|
||||||
Voice const& voice = voices_[voice_num];
|
Voice const& voice = voices_[voice_num];
|
||||||
@@ -82,7 +88,8 @@ int16_t DigitalSignalProcessor::DecodeSample(uint8_t voice_num) {
|
|||||||
return sample;
|
return sample;
|
||||||
}
|
}
|
||||||
|
|
||||||
int16_t DigitalSignalProcessor::ProcessSample(uint8_t voice_num, int16_t sample) {
|
int16_t DigitalSignalProcessor::ProcessSample(uint8_t voice_num,
|
||||||
|
int16_t sample) {
|
||||||
Voice const& voice = voices_[voice_num];
|
Voice const& voice = voices_[voice_num];
|
||||||
|
|
||||||
// Adjust the pitch (for simplicity, we're just adjusting the sample value)
|
// Adjust the pitch (for simplicity, we're just adjusting the sample value)
|
||||||
@@ -276,6 +283,7 @@ void DigitalSignalProcessor::process_envelope(uint8_t voice_num) {
|
|||||||
apply_envelope_to_output(voice_num);
|
apply_envelope_to_output(voice_num);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
} // namespace audio
|
||||||
} // namespace emu
|
} // namespace emu
|
||||||
} // namespace app
|
} // namespace app
|
||||||
} // namespace yaze
|
} // namespace yaze
|
||||||
@@ -10,12 +10,12 @@
|
|||||||
namespace yaze {
|
namespace yaze {
|
||||||
namespace app {
|
namespace app {
|
||||||
namespace emu {
|
namespace emu {
|
||||||
|
namespace audio {
|
||||||
|
|
||||||
using SampleFetcher = std::function<uint8_t(uint16_t)>;
|
using SampleFetcher = std::function<uint8_t(uint16_t)>;
|
||||||
using SamplePusher = std::function<void(int16_t)>;
|
using SamplePusher = std::function<void(int16_t)>;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
|
||||||
* The S-DSP is a digital signal processor generating the sound data.
|
* The S-DSP is a digital signal processor generating the sound data.
|
||||||
*
|
*
|
||||||
* A DSP register can be selected with $F2, after which it can be read or
|
* A DSP register can be selected with $F2, after which it can be read or
|
||||||
@@ -34,21 +34,19 @@ using SamplePusher = std::function<void(int16_t)>;
|
|||||||
* There are 8 voices, numbered 0 to 7.
|
* There are 8 voices, numbered 0 to 7.
|
||||||
* Each voice X has 10 registers in the range $X0-$X9.
|
* Each voice X has 10 registers in the range $X0-$X9.
|
||||||
*
|
*
|
||||||
* Name Address Bits Notes
|
* | Name | Address | Bits | Notes |
|
||||||
* VOL (L) $X0 SVVV VVVV Left channel volume, signed.
|
* |---------|---------|-----------|--------------------------------------------------------|
|
||||||
* VOL (R) $X1 SVVV VVVV Right channel volume, signed.
|
* | VOL (L) | $X0 | SVVV VVVV | Left channel volume, signed. |
|
||||||
* P (L) $X2 LLLL LLLL Low 8 bits of sample pitch.
|
* | VOL (R) | $X1 | SVVV VVVV | Right channel volume, signed. |
|
||||||
* P (H) $X3 --HH HHHH High 6 bits of sample pitch.
|
* | P (L) | $X2 | LLLL LLLL | Low 8 bits of sample pitch. |
|
||||||
* SCRN $X4 SSSS SSSS Selects a sample source entry from the
|
* | P (H) | $X3 | --HH HHHH | High 6 bits of sample pitch. |
|
||||||
* directory ADSR (1) $X5 EDDD AAAA ADSR enable (E), decay rate (D),
|
* | SCRN | $X4 | SSSS SSSS | Selects a sample source entry from the directory. |
|
||||||
* attack rate (A).
|
* | ADSR (1)| $X5 | EDDD AAAA | ADSR enable (E), decay rate (D), attack rate (A). |
|
||||||
* ADSR (2) $X6 SSSR RRRR Sustain level (S), release rate (R).
|
* | ADSR (2)| $X6 | SSSR RRRR | Sustain level (S), release rate (R). |
|
||||||
* GAIN $X7 0VVV VVVV 1MMV VVVV Mode (M), value (V).
|
* | GAIN | $X7 | 0VVV VVVV 1MMV VVVV | Mode (M), value (V). |
|
||||||
* ENVX $X8 0VVV VVVV Reads current 7-bit value of ADSR/GAIN
|
* | ENVX | $X8 | 0VVV VVVV | Reads current 7-bit value of ADSR/GAIN envelope. |
|
||||||
* envelope.
|
* | OUTX | $X9 | SVVV VVVV | Reads signed 8-bit value of current sample wave |
|
||||||
* OUTX $X9 SVVV VVVV Reads signed 8-bit value of current
|
* | | | | multiplied by ENVX, before applying VOL. |
|
||||||
* sample wave multiplied by ENVX, before applying VOL.
|
|
||||||
*
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
class DigitalSignalProcessor {
|
class DigitalSignalProcessor {
|
||||||
@@ -308,6 +306,8 @@ class DigitalSignalProcessor {
|
|||||||
// and apply the envelope to the audio output.
|
// and apply the envelope to the audio output.
|
||||||
void process_envelope(uint8_t voice_num);
|
void process_envelope(uint8_t voice_num);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
} // namespace audio
|
||||||
} // namespace emu
|
} // namespace emu
|
||||||
} // namespace app
|
} // namespace app
|
||||||
} // namespace yaze
|
} // namespace yaze
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
namespace yaze {
|
namespace yaze {
|
||||||
namespace app {
|
namespace app {
|
||||||
namespace emu {
|
namespace emu {
|
||||||
|
namespace audio {
|
||||||
|
|
||||||
// Immediate
|
// Immediate
|
||||||
uint8_t Spc700::imm() {
|
uint8_t Spc700::imm() {
|
||||||
@@ -89,6 +90,7 @@ uint16_t Spc700::addr_plus_i_indexed() {
|
|||||||
return read(addr) | (read(addr + 1) << 8);
|
return read(addr) | (read(addr + 1) << 8);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
} // namespace audio
|
||||||
} // namespace emu
|
} // namespace emu
|
||||||
} // namespace app
|
} // namespace app
|
||||||
} // namespace yaze
|
} // namespace yaze
|
||||||
@@ -3,6 +3,7 @@
|
|||||||
namespace yaze {
|
namespace yaze {
|
||||||
namespace app {
|
namespace app {
|
||||||
namespace emu {
|
namespace emu {
|
||||||
|
namespace audio {
|
||||||
|
|
||||||
void Spc700::MOV(uint8_t& dest, uint8_t operand) {
|
void Spc700::MOV(uint8_t& dest, uint8_t operand) {
|
||||||
dest = operand;
|
dest = operand;
|
||||||
@@ -356,6 +357,7 @@ void Spc700::SLEEP() {}
|
|||||||
|
|
||||||
void Spc700::STOP() {}
|
void Spc700::STOP() {}
|
||||||
|
|
||||||
|
} // namespace audio
|
||||||
} // namespace emu
|
} // namespace emu
|
||||||
} // namespace app
|
} // namespace app
|
||||||
} // namespace yaze
|
} // namespace yaze
|
||||||
@@ -11,6 +11,7 @@
|
|||||||
namespace yaze {
|
namespace yaze {
|
||||||
namespace app {
|
namespace app {
|
||||||
namespace emu {
|
namespace emu {
|
||||||
|
namespace audio {
|
||||||
|
|
||||||
void Spc700::Reset() {
|
void Spc700::Reset() {
|
||||||
PC = 0;
|
PC = 0;
|
||||||
@@ -952,6 +953,7 @@ void Spc700::LogInstruction(uint16_t initial_pc, uint8_t opcode) {
|
|||||||
log_.push_back(log_entry);
|
log_.push_back(log_entry);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
} // namespace audio
|
||||||
} // namespace emu
|
} // namespace emu
|
||||||
} // namespace app
|
} // namespace app
|
||||||
} // namespace yaze
|
} // namespace yaze
|
||||||
|
|||||||
@@ -9,7 +9,11 @@
|
|||||||
namespace yaze {
|
namespace yaze {
|
||||||
namespace app {
|
namespace app {
|
||||||
namespace emu {
|
namespace emu {
|
||||||
|
namespace audio {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief AudioRam is an interface for the Audio RAM used by the SPC700.
|
||||||
|
*/
|
||||||
class AudioRam {
|
class AudioRam {
|
||||||
public:
|
public:
|
||||||
virtual ~AudioRam() = default;
|
virtual ~AudioRam() = default;
|
||||||
@@ -19,6 +23,9 @@ class AudioRam {
|
|||||||
virtual void write(uint16_t address, uint8_t value) = 0;
|
virtual void write(uint16_t address, uint8_t value) = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief AudioRamImpl is an implementation of the AudioRam interface.
|
||||||
|
*/
|
||||||
class AudioRamImpl : public AudioRam {
|
class AudioRamImpl : public AudioRam {
|
||||||
static const int ARAM_SIZE = 0x10000;
|
static const int ARAM_SIZE = 0x10000;
|
||||||
std::vector<uint8_t> ram = std::vector<uint8_t>(ARAM_SIZE, 0);
|
std::vector<uint8_t> ram = std::vector<uint8_t>(ARAM_SIZE, 0);
|
||||||
@@ -40,6 +47,17 @@ class AudioRamImpl : public AudioRam {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @class Spc700
|
||||||
|
* @brief The Spc700 class represents the SPC700 processor.
|
||||||
|
*
|
||||||
|
* The Spc700 class provides the functionality to execute instructions, read and
|
||||||
|
* write memory, and handle various addressing modes. It also contains registers
|
||||||
|
* and flags specific to the SPC700.
|
||||||
|
*
|
||||||
|
* @note This class assumes the existence of an `AudioRam` object for memory
|
||||||
|
* access.
|
||||||
|
*/
|
||||||
class Spc700 {
|
class Spc700 {
|
||||||
private:
|
private:
|
||||||
AudioRam& aram_;
|
AudioRam& aram_;
|
||||||
@@ -261,6 +279,7 @@ class Spc700 {
|
|||||||
// CBNE DBNZ
|
// CBNE DBNZ
|
||||||
};
|
};
|
||||||
|
|
||||||
|
} // namespace audio
|
||||||
} // namespace emu
|
} // namespace emu
|
||||||
} // namespace app
|
} // namespace app
|
||||||
} // namespace yaze
|
} // namespace yaze
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ namespace yaze {
|
|||||||
namespace app {
|
namespace app {
|
||||||
namespace emu {
|
namespace emu {
|
||||||
|
|
||||||
void CPU::Update(UpdateMode mode, int stepCount) {
|
void Cpu::Update(UpdateMode mode, int stepCount) {
|
||||||
int cycles = (mode == UpdateMode::Run) ? clock.GetCycleCount() : stepCount;
|
int cycles = (mode == UpdateMode::Run) ? clock.GetCycleCount() : stepCount;
|
||||||
|
|
||||||
// Execute the calculated number of cycles
|
// Execute the calculated number of cycles
|
||||||
@@ -31,7 +31,7 @@ void CPU::Update(UpdateMode mode, int stepCount) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void CPU::ExecuteInstruction(uint8_t opcode) {
|
void Cpu::ExecuteInstruction(uint8_t opcode) {
|
||||||
uint8_t cycles = 0;
|
uint8_t cycles = 0;
|
||||||
uint8_t instruction_length = 0;
|
uint8_t instruction_length = 0;
|
||||||
uint32_t operand = 0;
|
uint32_t operand = 0;
|
||||||
@@ -1475,7 +1475,7 @@ void CPU::ExecuteInstruction(uint8_t opcode) {
|
|||||||
UpdatePC(instruction_length);
|
UpdatePC(instruction_length);
|
||||||
}
|
}
|
||||||
|
|
||||||
void CPU::LogInstructions(uint16_t PC, uint8_t opcode, uint16_t operand,
|
void Cpu::LogInstructions(uint16_t PC, uint8_t opcode, uint16_t operand,
|
||||||
bool immediate, bool accumulator_mode) {
|
bool immediate, bool accumulator_mode) {
|
||||||
if (flags()->kLogInstructions) {
|
if (flags()->kLogInstructions) {
|
||||||
std::ostringstream oss;
|
std::ostringstream oss;
|
||||||
@@ -1583,7 +1583,7 @@ void CPU::LogInstructions(uint16_t PC, uint8_t opcode, uint16_t operand,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
uint8_t CPU::GetInstructionLength(uint8_t opcode) {
|
uint8_t Cpu::GetInstructionLength(uint8_t opcode) {
|
||||||
switch (opcode) {
|
switch (opcode) {
|
||||||
case 0x00: // BRK
|
case 0x00: // BRK
|
||||||
case 0x02: // COP
|
case 0x02: // COP
|
||||||
@@ -1928,7 +1928,7 @@ uint8_t CPU::GetInstructionLength(uint8_t opcode) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Implement 65816 interrupts.
|
// TODO: Implement 65816 interrupts.
|
||||||
void CPU::HandleInterrupts() {
|
void Cpu::HandleInterrupts() {
|
||||||
if (GetInterruptFlag()) {
|
if (GetInterruptFlag()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -38,9 +38,11 @@ class InstructionEntry {
|
|||||||
|
|
||||||
const int kCpuClockSpeed = 21477272; // 21.477272 MHz
|
const int kCpuClockSpeed = 21477272; // 21.477272 MHz
|
||||||
|
|
||||||
class CPU : public Memory, public Loggable, public core::ExperimentFlags {
|
class Cpu : public memory::Memory,
|
||||||
|
public Loggable,
|
||||||
|
public core::ExperimentFlags {
|
||||||
public:
|
public:
|
||||||
explicit CPU(Memory& mem, Clock& vclock) : memory(mem), clock(vclock) {}
|
explicit Cpu(Memory& mem, Clock& vclock) : memory(mem), clock(vclock) {}
|
||||||
enum class UpdateMode { Run, Step, Pause };
|
enum class UpdateMode { Run, Step, Pause };
|
||||||
|
|
||||||
void Init(bool verbose = false) { clock.SetFrequency(kCpuClockSpeed); }
|
void Init(bool verbose = false) { clock.SetFrequency(kCpuClockSpeed); }
|
||||||
|
|||||||
@@ -4,92 +4,92 @@ namespace yaze {
|
|||||||
namespace app {
|
namespace app {
|
||||||
namespace emu {
|
namespace emu {
|
||||||
|
|
||||||
uint32_t CPU::Absolute(CPU::AccessType access_type) {
|
uint32_t Cpu::Absolute(Cpu::AccessType access_type) {
|
||||||
auto operand = FetchWord();
|
auto operand = FetchWord();
|
||||||
uint32_t bank =
|
uint32_t bank =
|
||||||
(access_type == CPU::AccessType::Data) ? (DB << 16) : (PB << 16);
|
(access_type == Cpu::AccessType::Data) ? (DB << 16) : (PB << 16);
|
||||||
return bank | (operand & 0xFFFF);
|
return bank | (operand & 0xFFFF);
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t CPU::AbsoluteIndexedX() {
|
uint32_t Cpu::AbsoluteIndexedX() {
|
||||||
uint16_t address = memory.ReadWord((PB << 16) | (PC + 1));
|
uint16_t address = memory.ReadWord((PB << 16) | (PC + 1));
|
||||||
uint32_t effective_address = (DB << 16) | ((address + X) & 0xFFFF);
|
uint32_t effective_address = (DB << 16) | ((address + X) & 0xFFFF);
|
||||||
return effective_address;
|
return effective_address;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t CPU::AbsoluteIndexedY() {
|
uint32_t Cpu::AbsoluteIndexedY() {
|
||||||
uint16_t address = memory.ReadWord((PB << 16) | (PC + 1));
|
uint16_t address = memory.ReadWord((PB << 16) | (PC + 1));
|
||||||
uint32_t effective_address = (DB << 16) | address + Y;
|
uint32_t effective_address = (DB << 16) | address + Y;
|
||||||
return effective_address;
|
return effective_address;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint16_t CPU::AbsoluteIndexedIndirect() {
|
uint16_t Cpu::AbsoluteIndexedIndirect() {
|
||||||
uint16_t address = FetchWord() + X;
|
uint16_t address = FetchWord() + X;
|
||||||
return memory.ReadWord((DB << 16) | address & 0xFFFF);
|
return memory.ReadWord((DB << 16) | address & 0xFFFF);
|
||||||
}
|
}
|
||||||
|
|
||||||
uint16_t CPU::AbsoluteIndirect() {
|
uint16_t Cpu::AbsoluteIndirect() {
|
||||||
uint16_t address = FetchWord();
|
uint16_t address = FetchWord();
|
||||||
return memory.ReadWord((PB << 16) | address);
|
return memory.ReadWord((PB << 16) | address);
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t CPU::AbsoluteIndirectLong() {
|
uint32_t Cpu::AbsoluteIndirectLong() {
|
||||||
uint16_t address = FetchWord();
|
uint16_t address = FetchWord();
|
||||||
return memory.ReadWordLong((PB << 16) | address);
|
return memory.ReadWordLong((PB << 16) | address);
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t CPU::AbsoluteLong() { return FetchLong(); }
|
uint32_t Cpu::AbsoluteLong() { return FetchLong(); }
|
||||||
|
|
||||||
uint32_t CPU::AbsoluteLongIndexedX() { return FetchLong() + X; }
|
uint32_t Cpu::AbsoluteLongIndexedX() { return FetchLong() + X; }
|
||||||
|
|
||||||
void CPU::BlockMove(uint16_t source, uint16_t dest, uint16_t length) {
|
void Cpu::BlockMove(uint16_t source, uint16_t dest, uint16_t length) {
|
||||||
for (int i = 0; i < length; i++) {
|
for (int i = 0; i < length; i++) {
|
||||||
memory.WriteByte(dest + i, memory.ReadByte(source + i));
|
memory.WriteByte(dest + i, memory.ReadByte(source + i));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
uint16_t CPU::DirectPage() {
|
uint16_t Cpu::DirectPage() {
|
||||||
uint8_t dp = FetchByte();
|
uint8_t dp = FetchByte();
|
||||||
return D + dp;
|
return D + dp;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint16_t CPU::DirectPageIndexedX() {
|
uint16_t Cpu::DirectPageIndexedX() {
|
||||||
uint8_t operand = FetchByte();
|
uint8_t operand = FetchByte();
|
||||||
uint16_t x_by_mode = GetAccumulatorSize() ? X : X & 0xFF;
|
uint16_t x_by_mode = GetAccumulatorSize() ? X : X & 0xFF;
|
||||||
return D + operand + x_by_mode;
|
return D + operand + x_by_mode;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint16_t CPU::DirectPageIndexedY() {
|
uint16_t Cpu::DirectPageIndexedY() {
|
||||||
uint8_t operand = FetchByte();
|
uint8_t operand = FetchByte();
|
||||||
return (operand + Y) & 0xFF;
|
return (operand + Y) & 0xFF;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint16_t CPU::DirectPageIndexedIndirectX() {
|
uint16_t Cpu::DirectPageIndexedIndirectX() {
|
||||||
uint8_t operand = FetchByte();
|
uint8_t operand = FetchByte();
|
||||||
uint16_t indirect_address = D + operand + X;
|
uint16_t indirect_address = D + operand + X;
|
||||||
uint16_t effective_address = memory.ReadWord(indirect_address & 0xFFFF);
|
uint16_t effective_address = memory.ReadWord(indirect_address & 0xFFFF);
|
||||||
return effective_address;
|
return effective_address;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint16_t CPU::DirectPageIndirect() {
|
uint16_t Cpu::DirectPageIndirect() {
|
||||||
uint8_t dp = FetchByte();
|
uint8_t dp = FetchByte();
|
||||||
uint16_t effective_address = D + dp;
|
uint16_t effective_address = D + dp;
|
||||||
return memory.ReadWord(effective_address);
|
return memory.ReadWord(effective_address);
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t CPU::DirectPageIndirectLong() {
|
uint32_t Cpu::DirectPageIndirectLong() {
|
||||||
uint8_t dp = FetchByte();
|
uint8_t dp = FetchByte();
|
||||||
uint16_t effective_address = D + dp;
|
uint16_t effective_address = D + dp;
|
||||||
return memory.ReadWordLong((0x00 << 0x10) | effective_address);
|
return memory.ReadWordLong((0x00 << 0x10) | effective_address);
|
||||||
}
|
}
|
||||||
|
|
||||||
uint16_t CPU::DirectPageIndirectIndexedY() {
|
uint16_t Cpu::DirectPageIndirectIndexedY() {
|
||||||
uint8_t operand = FetchByte();
|
uint8_t operand = FetchByte();
|
||||||
uint16_t indirect_address = D + operand;
|
uint16_t indirect_address = D + operand;
|
||||||
return memory.ReadWord(indirect_address) + Y;
|
return memory.ReadWord(indirect_address) + Y;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t CPU::DirectPageIndirectLongIndexedY() {
|
uint32_t Cpu::DirectPageIndirectLongIndexedY() {
|
||||||
uint8_t operand = FetchByte();
|
uint8_t operand = FetchByte();
|
||||||
uint16_t indirect_address = D + operand;
|
uint16_t indirect_address = D + operand;
|
||||||
uint16_t y_by_mode = GetAccumulatorSize() ? Y : Y & 0xFF;
|
uint16_t y_by_mode = GetAccumulatorSize() ? Y : Y & 0xFF;
|
||||||
@@ -98,7 +98,7 @@ uint32_t CPU::DirectPageIndirectLongIndexedY() {
|
|||||||
return effective_address;
|
return effective_address;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint16_t CPU::Immediate(bool index_size) {
|
uint16_t Cpu::Immediate(bool index_size) {
|
||||||
bool bit_mode = index_size ? GetIndexSize() : GetAccumulatorSize();
|
bool bit_mode = index_size ? GetIndexSize() : GetAccumulatorSize();
|
||||||
if (bit_mode) {
|
if (bit_mode) {
|
||||||
return memory.ReadByte((PB << 16) | PC + 1);
|
return memory.ReadByte((PB << 16) | PC + 1);
|
||||||
@@ -107,13 +107,13 @@ uint16_t CPU::Immediate(bool index_size) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
uint16_t CPU::StackRelative() {
|
uint16_t Cpu::StackRelative() {
|
||||||
uint8_t sr = FetchByte();
|
uint8_t sr = FetchByte();
|
||||||
uint16_t effective_address = SP() + sr;
|
uint16_t effective_address = SP() + sr;
|
||||||
return effective_address;
|
return effective_address;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t CPU::StackRelativeIndirectIndexedY() {
|
uint32_t Cpu::StackRelativeIndirectIndexedY() {
|
||||||
uint8_t sr = FetchByte();
|
uint8_t sr = FetchByte();
|
||||||
return (DB << 0x10) | (memory.ReadWord(SP() + sr) + Y);
|
return (DB << 0x10) | (memory.ReadWord(SP() + sr) + Y);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ namespace emu {
|
|||||||
* TODO: STP, WDM
|
* TODO: STP, WDM
|
||||||
*/
|
*/
|
||||||
|
|
||||||
void CPU::ADC(uint16_t operand) {
|
void Cpu::ADC(uint16_t operand) {
|
||||||
bool C = GetCarryFlag();
|
bool C = GetCarryFlag();
|
||||||
if (GetAccumulatorSize()) { // 8-bit mode
|
if (GetAccumulatorSize()) { // 8-bit mode
|
||||||
uint16_t result = static_cast<uint16_t>(A & 0xFF) +
|
uint16_t result = static_cast<uint16_t>(A & 0xFF) +
|
||||||
@@ -47,7 +47,7 @@ void CPU::ADC(uint16_t operand) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void CPU::AND(uint32_t value, bool isImmediate) {
|
void Cpu::AND(uint32_t value, bool isImmediate) {
|
||||||
uint16_t operand;
|
uint16_t operand;
|
||||||
if (GetAccumulatorSize()) { // 8-bit mode
|
if (GetAccumulatorSize()) { // 8-bit mode
|
||||||
operand = isImmediate ? value : memory.ReadByte(value);
|
operand = isImmediate ? value : memory.ReadByte(value);
|
||||||
@@ -63,14 +63,14 @@ void CPU::AND(uint32_t value, bool isImmediate) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// New function for absolute long addressing mode
|
// New function for absolute long addressing mode
|
||||||
void CPU::ANDAbsoluteLong(uint32_t address) {
|
void Cpu::ANDAbsoluteLong(uint32_t address) {
|
||||||
uint32_t operand32 = memory.ReadWordLong(address);
|
uint32_t operand32 = memory.ReadWordLong(address);
|
||||||
A &= operand32;
|
A &= operand32;
|
||||||
SetZeroFlag(A == 0);
|
SetZeroFlag(A == 0);
|
||||||
SetNegativeFlag(A & 0x8000);
|
SetNegativeFlag(A & 0x8000);
|
||||||
}
|
}
|
||||||
|
|
||||||
void CPU::ASL(uint16_t address) {
|
void Cpu::ASL(uint16_t address) {
|
||||||
uint8_t value = memory.ReadByte(address);
|
uint8_t value = memory.ReadByte(address);
|
||||||
SetCarryFlag(!(value & 0x80)); // Set carry flag if bit 7 is set
|
SetCarryFlag(!(value & 0x80)); // Set carry flag if bit 7 is set
|
||||||
value <<= 1; // Shift left
|
value <<= 1; // Shift left
|
||||||
@@ -80,53 +80,53 @@ void CPU::ASL(uint16_t address) {
|
|||||||
SetZeroFlag(value);
|
SetZeroFlag(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
void CPU::BCC(int8_t offset) {
|
void Cpu::BCC(int8_t offset) {
|
||||||
if (!GetCarryFlag()) { // If the carry flag is clear
|
if (!GetCarryFlag()) { // If the carry flag is clear
|
||||||
next_pc_ = offset;
|
next_pc_ = offset;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void CPU::BCS(int8_t offset) {
|
void Cpu::BCS(int8_t offset) {
|
||||||
if (GetCarryFlag()) { // If the carry flag is set
|
if (GetCarryFlag()) { // If the carry flag is set
|
||||||
next_pc_ = offset;
|
next_pc_ = offset;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void CPU::BEQ(int8_t offset) {
|
void Cpu::BEQ(int8_t offset) {
|
||||||
if (GetZeroFlag()) { // If the zero flag is set
|
if (GetZeroFlag()) { // If the zero flag is set
|
||||||
next_pc_ = offset;
|
next_pc_ = offset;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void CPU::BIT(uint16_t address) {
|
void Cpu::BIT(uint16_t address) {
|
||||||
uint8_t value = memory.ReadByte(address);
|
uint8_t value = memory.ReadByte(address);
|
||||||
SetNegativeFlag(value & 0x80);
|
SetNegativeFlag(value & 0x80);
|
||||||
SetOverflowFlag(value & 0x40);
|
SetOverflowFlag(value & 0x40);
|
||||||
SetZeroFlag((A & value) == 0);
|
SetZeroFlag((A & value) == 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
void CPU::BMI(int8_t offset) {
|
void Cpu::BMI(int8_t offset) {
|
||||||
if (GetNegativeFlag()) { // If the negative flag is set
|
if (GetNegativeFlag()) { // If the negative flag is set
|
||||||
next_pc_ = offset;
|
next_pc_ = offset;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void CPU::BNE(int8_t offset) {
|
void Cpu::BNE(int8_t offset) {
|
||||||
if (!GetZeroFlag()) { // If the zero flag is clear
|
if (!GetZeroFlag()) { // If the zero flag is clear
|
||||||
// PC += offset;
|
// PC += offset;
|
||||||
next_pc_ = offset;
|
next_pc_ = offset;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void CPU::BPL(int8_t offset) {
|
void Cpu::BPL(int8_t offset) {
|
||||||
if (!GetNegativeFlag()) { // If the negative flag is clear
|
if (!GetNegativeFlag()) { // If the negative flag is clear
|
||||||
next_pc_ = offset;
|
next_pc_ = offset;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void CPU::BRA(int8_t offset) { next_pc_ = offset; }
|
void Cpu::BRA(int8_t offset) { next_pc_ = offset; }
|
||||||
|
|
||||||
void CPU::BRK() {
|
void Cpu::BRK() {
|
||||||
next_pc_ = PC + 2; // Increment the program counter by 2
|
next_pc_ = PC + 2; // Increment the program counter by 2
|
||||||
memory.PushWord(next_pc_);
|
memory.PushWord(next_pc_);
|
||||||
memory.PushByte(status);
|
memory.PushByte(status);
|
||||||
@@ -138,32 +138,32 @@ void CPU::BRK() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void CPU::BRL(int16_t offset) { next_pc_ = offset; }
|
void Cpu::BRL(int16_t offset) { next_pc_ = offset; }
|
||||||
|
|
||||||
void CPU::BVC(int8_t offset) {
|
void Cpu::BVC(int8_t offset) {
|
||||||
if (!GetOverflowFlag()) { // If the overflow flag is clear
|
if (!GetOverflowFlag()) { // If the overflow flag is clear
|
||||||
next_pc_ = offset;
|
next_pc_ = offset;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void CPU::BVS(int8_t offset) {
|
void Cpu::BVS(int8_t offset) {
|
||||||
if (GetOverflowFlag()) { // If the overflow flag is set
|
if (GetOverflowFlag()) { // If the overflow flag is set
|
||||||
next_pc_ = offset;
|
next_pc_ = offset;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void CPU::CLC() { status &= ~0x01; }
|
void Cpu::CLC() { status &= ~0x01; }
|
||||||
|
|
||||||
void CPU::CLD() { status &= ~0x08; }
|
void Cpu::CLD() { status &= ~0x08; }
|
||||||
|
|
||||||
void CPU::CLI() { status &= ~0x04; }
|
void Cpu::CLI() { status &= ~0x04; }
|
||||||
|
|
||||||
void CPU::CLV() { status &= ~0x40; }
|
void Cpu::CLV() { status &= ~0x40; }
|
||||||
|
|
||||||
// n Set if MSB of result is set; else cleared
|
// n Set if MSB of result is set; else cleared
|
||||||
// z Set if result is zero; else cleared
|
// z Set if result is zero; else cleared
|
||||||
// c Set if no borrow; else cleared
|
// c Set if no borrow; else cleared
|
||||||
void CPU::CMP(uint32_t value, bool isImmediate) {
|
void Cpu::CMP(uint32_t value, bool isImmediate) {
|
||||||
if (GetAccumulatorSize()) { // 8-bit
|
if (GetAccumulatorSize()) { // 8-bit
|
||||||
uint8_t result;
|
uint8_t result;
|
||||||
if (isImmediate) {
|
if (isImmediate) {
|
||||||
@@ -189,7 +189,7 @@ void CPU::CMP(uint32_t value, bool isImmediate) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void CPU::COP() {
|
void Cpu::COP() {
|
||||||
next_pc_ += 2; // Increment the program counter by 2
|
next_pc_ += 2; // Increment the program counter by 2
|
||||||
memory.PushWord(next_pc_);
|
memory.PushWord(next_pc_);
|
||||||
memory.PushByte(status);
|
memory.PushByte(status);
|
||||||
@@ -202,7 +202,7 @@ void CPU::COP() {
|
|||||||
SetDecimalFlag(false);
|
SetDecimalFlag(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
void CPU::CPX(uint32_t value, bool isImmediate) {
|
void Cpu::CPX(uint32_t value, bool isImmediate) {
|
||||||
if (GetIndexSize()) { // 8-bit
|
if (GetIndexSize()) { // 8-bit
|
||||||
uint8_t memory_value = isImmediate ? value : memory.ReadByte(value);
|
uint8_t memory_value = isImmediate ? value : memory.ReadByte(value);
|
||||||
compare(X, memory_value);
|
compare(X, memory_value);
|
||||||
@@ -212,7 +212,7 @@ void CPU::CPX(uint32_t value, bool isImmediate) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void CPU::CPY(uint32_t value, bool isImmediate) {
|
void Cpu::CPY(uint32_t value, bool isImmediate) {
|
||||||
if (GetIndexSize()) { // 8-bit
|
if (GetIndexSize()) { // 8-bit
|
||||||
uint8_t memory_value = isImmediate ? value : memory.ReadByte(value);
|
uint8_t memory_value = isImmediate ? value : memory.ReadByte(value);
|
||||||
compare(Y, memory_value);
|
compare(Y, memory_value);
|
||||||
@@ -222,7 +222,7 @@ void CPU::CPY(uint32_t value, bool isImmediate) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void CPU::DEC(uint32_t address, bool accumulator) {
|
void Cpu::DEC(uint32_t address, bool accumulator) {
|
||||||
if (accumulator) {
|
if (accumulator) {
|
||||||
if (GetAccumulatorSize()) { // 8-bit
|
if (GetAccumulatorSize()) { // 8-bit
|
||||||
A = (A - 1) & 0xFF;
|
A = (A - 1) & 0xFF;
|
||||||
@@ -251,7 +251,7 @@ void CPU::DEC(uint32_t address, bool accumulator) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void CPU::DEX() {
|
void Cpu::DEX() {
|
||||||
if (GetIndexSize()) { // 8-bit
|
if (GetIndexSize()) { // 8-bit
|
||||||
X = static_cast<uint8_t>(X - 1);
|
X = static_cast<uint8_t>(X - 1);
|
||||||
SetZeroFlag(X == 0);
|
SetZeroFlag(X == 0);
|
||||||
@@ -263,7 +263,7 @@ void CPU::DEX() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void CPU::DEY() {
|
void Cpu::DEY() {
|
||||||
if (GetIndexSize()) { // 8-bit
|
if (GetIndexSize()) { // 8-bit
|
||||||
Y = static_cast<uint8_t>(Y - 1);
|
Y = static_cast<uint8_t>(Y - 1);
|
||||||
SetZeroFlag(Y == 0);
|
SetZeroFlag(Y == 0);
|
||||||
@@ -275,7 +275,7 @@ void CPU::DEY() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void CPU::EOR(uint32_t address, bool isImmediate) {
|
void Cpu::EOR(uint32_t address, bool isImmediate) {
|
||||||
if (GetAccumulatorSize()) {
|
if (GetAccumulatorSize()) {
|
||||||
A ^= isImmediate ? address : memory.ReadByte(address);
|
A ^= isImmediate ? address : memory.ReadByte(address);
|
||||||
SetZeroFlag(A == 0);
|
SetZeroFlag(A == 0);
|
||||||
@@ -287,7 +287,7 @@ void CPU::EOR(uint32_t address, bool isImmediate) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void CPU::INC(uint32_t address, bool accumulator) {
|
void Cpu::INC(uint32_t address, bool accumulator) {
|
||||||
if (accumulator) {
|
if (accumulator) {
|
||||||
if (GetAccumulatorSize()) { // 8-bit
|
if (GetAccumulatorSize()) { // 8-bit
|
||||||
A = (A + 1) & 0xFF;
|
A = (A + 1) & 0xFF;
|
||||||
@@ -316,7 +316,7 @@ void CPU::INC(uint32_t address, bool accumulator) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void CPU::INX() {
|
void Cpu::INX() {
|
||||||
if (GetIndexSize()) { // 8-bit
|
if (GetIndexSize()) { // 8-bit
|
||||||
X = static_cast<uint8_t>(X + 1);
|
X = static_cast<uint8_t>(X + 1);
|
||||||
SetZeroFlag(X == 0);
|
SetZeroFlag(X == 0);
|
||||||
@@ -328,7 +328,7 @@ void CPU::INX() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void CPU::INY() {
|
void Cpu::INY() {
|
||||||
if (GetIndexSize()) { // 8-bit
|
if (GetIndexSize()) { // 8-bit
|
||||||
Y = static_cast<uint8_t>(Y + 1);
|
Y = static_cast<uint8_t>(Y + 1);
|
||||||
SetZeroFlag(Y == 0);
|
SetZeroFlag(Y == 0);
|
||||||
@@ -340,28 +340,28 @@ void CPU::INY() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void CPU::JMP(uint16_t address) {
|
void Cpu::JMP(uint16_t address) {
|
||||||
next_pc_ = address; // Set program counter to the new address
|
next_pc_ = address; // Set program counter to the new address
|
||||||
}
|
}
|
||||||
|
|
||||||
void CPU::JML(uint32_t address) {
|
void Cpu::JML(uint32_t address) {
|
||||||
next_pc_ = static_cast<uint16_t>(address & 0xFFFF);
|
next_pc_ = static_cast<uint16_t>(address & 0xFFFF);
|
||||||
// Set the PBR to the upper 8 bits of the address
|
// Set the PBR to the upper 8 bits of the address
|
||||||
PB = static_cast<uint8_t>((address >> 16) & 0xFF);
|
PB = static_cast<uint8_t>((address >> 16) & 0xFF);
|
||||||
}
|
}
|
||||||
|
|
||||||
void CPU::JSR(uint16_t address) {
|
void Cpu::JSR(uint16_t address) {
|
||||||
memory.PushWord(PC); // Push the program counter onto the stack
|
memory.PushWord(PC); // Push the program counter onto the stack
|
||||||
next_pc_ = address; // Set program counter to the new address
|
next_pc_ = address; // Set program counter to the new address
|
||||||
}
|
}
|
||||||
|
|
||||||
void CPU::JSL(uint32_t address) {
|
void Cpu::JSL(uint32_t address) {
|
||||||
memory.PushLong(PC); // Push the program counter onto the stack as a long
|
memory.PushLong(PC); // Push the program counter onto the stack as a long
|
||||||
// value (24 bits)
|
// value (24 bits)
|
||||||
next_pc_ = address; // Set program counter to the new address
|
next_pc_ = address; // Set program counter to the new address
|
||||||
}
|
}
|
||||||
|
|
||||||
void CPU::LDA(uint16_t address, bool isImmediate, bool direct_page, bool data_bank) {
|
void Cpu::LDA(uint16_t address, bool isImmediate, bool direct_page, bool data_bank) {
|
||||||
uint8_t bank = PB;
|
uint8_t bank = PB;
|
||||||
if (direct_page) {
|
if (direct_page) {
|
||||||
bank = 0;
|
bank = 0;
|
||||||
@@ -377,7 +377,7 @@ void CPU::LDA(uint16_t address, bool isImmediate, bool direct_page, bool data_ba
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void CPU::LDX(uint16_t address, bool isImmediate) {
|
void Cpu::LDX(uint16_t address, bool isImmediate) {
|
||||||
if (GetIndexSize()) {
|
if (GetIndexSize()) {
|
||||||
X = isImmediate ? address : memory.ReadByte(address);
|
X = isImmediate ? address : memory.ReadByte(address);
|
||||||
SetZeroFlag(X == 0);
|
SetZeroFlag(X == 0);
|
||||||
@@ -389,7 +389,7 @@ void CPU::LDX(uint16_t address, bool isImmediate) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void CPU::LDY(uint16_t address, bool isImmediate) {
|
void Cpu::LDY(uint16_t address, bool isImmediate) {
|
||||||
if (GetIndexSize()) {
|
if (GetIndexSize()) {
|
||||||
Y = isImmediate ? address : memory.ReadByte(address);
|
Y = isImmediate ? address : memory.ReadByte(address);
|
||||||
SetZeroFlag(Y == 0);
|
SetZeroFlag(Y == 0);
|
||||||
@@ -401,7 +401,7 @@ void CPU::LDY(uint16_t address, bool isImmediate) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void CPU::LSR(uint16_t address, bool accumulator) {
|
void Cpu::LSR(uint16_t address, bool accumulator) {
|
||||||
if (accumulator) {
|
if (accumulator) {
|
||||||
if (GetAccumulatorSize()) { // 8-bit
|
if (GetAccumulatorSize()) { // 8-bit
|
||||||
SetCarryFlag(A & 0x01);
|
SetCarryFlag(A & 0x01);
|
||||||
@@ -424,7 +424,7 @@ void CPU::LSR(uint16_t address, bool accumulator) {
|
|||||||
SetZeroFlag(value == 0);
|
SetZeroFlag(value == 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
void CPU::MVN(uint16_t source, uint16_t dest, uint16_t length) {
|
void Cpu::MVN(uint16_t source, uint16_t dest, uint16_t length) {
|
||||||
for (uint16_t i = 0; i < length; i++) {
|
for (uint16_t i = 0; i < length; i++) {
|
||||||
memory.WriteByte(dest, memory.ReadByte(source));
|
memory.WriteByte(dest, memory.ReadByte(source));
|
||||||
source++;
|
source++;
|
||||||
@@ -432,7 +432,7 @@ void CPU::MVN(uint16_t source, uint16_t dest, uint16_t length) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void CPU::MVP(uint16_t source, uint16_t dest, uint16_t length) {
|
void Cpu::MVP(uint16_t source, uint16_t dest, uint16_t length) {
|
||||||
for (uint16_t i = 0; i < length; i++) {
|
for (uint16_t i = 0; i < length; i++) {
|
||||||
memory.WriteByte(dest, memory.ReadByte(source));
|
memory.WriteByte(dest, memory.ReadByte(source));
|
||||||
source--;
|
source--;
|
||||||
@@ -440,11 +440,11 @@ void CPU::MVP(uint16_t source, uint16_t dest, uint16_t length) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void CPU::NOP() {
|
void Cpu::NOP() {
|
||||||
// Do nothing
|
// Do nothing
|
||||||
}
|
}
|
||||||
|
|
||||||
void CPU::ORA(uint16_t address, bool isImmediate) {
|
void Cpu::ORA(uint16_t address, bool isImmediate) {
|
||||||
if (GetAccumulatorSize()) {
|
if (GetAccumulatorSize()) {
|
||||||
A |= isImmediate ? address : memory.ReadByte(address);
|
A |= isImmediate ? address : memory.ReadByte(address);
|
||||||
SetZeroFlag(A == 0);
|
SetZeroFlag(A == 0);
|
||||||
@@ -456,22 +456,22 @@ void CPU::ORA(uint16_t address, bool isImmediate) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void CPU::PEA() {
|
void Cpu::PEA() {
|
||||||
uint16_t address = FetchWord();
|
uint16_t address = FetchWord();
|
||||||
memory.PushWord(address);
|
memory.PushWord(address);
|
||||||
}
|
}
|
||||||
|
|
||||||
void CPU::PEI() {
|
void Cpu::PEI() {
|
||||||
uint16_t address = FetchWord();
|
uint16_t address = FetchWord();
|
||||||
memory.PushWord(memory.ReadWord(address));
|
memory.PushWord(memory.ReadWord(address));
|
||||||
}
|
}
|
||||||
|
|
||||||
void CPU::PER() {
|
void Cpu::PER() {
|
||||||
uint16_t address = FetchWord();
|
uint16_t address = FetchWord();
|
||||||
memory.PushWord(PC + address);
|
memory.PushWord(PC + address);
|
||||||
}
|
}
|
||||||
|
|
||||||
void CPU::PHA() {
|
void Cpu::PHA() {
|
||||||
if (GetAccumulatorSize()) {
|
if (GetAccumulatorSize()) {
|
||||||
memory.PushByte(static_cast<uint8_t>(A));
|
memory.PushByte(static_cast<uint8_t>(A));
|
||||||
} else {
|
} else {
|
||||||
@@ -479,15 +479,15 @@ void CPU::PHA() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void CPU::PHB() { memory.PushByte(DB); }
|
void Cpu::PHB() { memory.PushByte(DB); }
|
||||||
|
|
||||||
void CPU::PHD() { memory.PushWord(D); }
|
void Cpu::PHD() { memory.PushWord(D); }
|
||||||
|
|
||||||
void CPU::PHK() { memory.PushByte(PB); }
|
void Cpu::PHK() { memory.PushByte(PB); }
|
||||||
|
|
||||||
void CPU::PHP() { memory.PushByte(status); }
|
void Cpu::PHP() { memory.PushByte(status); }
|
||||||
|
|
||||||
void CPU::PHX() {
|
void Cpu::PHX() {
|
||||||
if (GetIndexSize()) {
|
if (GetIndexSize()) {
|
||||||
memory.PushByte(static_cast<uint8_t>(X));
|
memory.PushByte(static_cast<uint8_t>(X));
|
||||||
} else {
|
} else {
|
||||||
@@ -495,7 +495,7 @@ void CPU::PHX() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void CPU::PHY() {
|
void Cpu::PHY() {
|
||||||
if (GetIndexSize()) {
|
if (GetIndexSize()) {
|
||||||
memory.PushByte(static_cast<uint8_t>(Y));
|
memory.PushByte(static_cast<uint8_t>(Y));
|
||||||
} else {
|
} else {
|
||||||
@@ -503,7 +503,7 @@ void CPU::PHY() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void CPU::PLA() {
|
void Cpu::PLA() {
|
||||||
if (GetAccumulatorSize()) {
|
if (GetAccumulatorSize()) {
|
||||||
A = memory.PopByte();
|
A = memory.PopByte();
|
||||||
SetNegativeFlag((A & 0x80) != 0);
|
SetNegativeFlag((A & 0x80) != 0);
|
||||||
@@ -514,23 +514,23 @@ void CPU::PLA() {
|
|||||||
SetZeroFlag(A == 0);
|
SetZeroFlag(A == 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
void CPU::PLB() {
|
void Cpu::PLB() {
|
||||||
DB = memory.PopByte();
|
DB = memory.PopByte();
|
||||||
SetNegativeFlag((DB & 0x80) != 0);
|
SetNegativeFlag((DB & 0x80) != 0);
|
||||||
SetZeroFlag(DB == 0);
|
SetZeroFlag(DB == 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Pull Direct Page Register from Stack
|
// Pull Direct Page Register from Stack
|
||||||
void CPU::PLD() {
|
void Cpu::PLD() {
|
||||||
D = memory.PopWord();
|
D = memory.PopWord();
|
||||||
SetNegativeFlag((D & 0x8000) != 0);
|
SetNegativeFlag((D & 0x8000) != 0);
|
||||||
SetZeroFlag(D == 0);
|
SetZeroFlag(D == 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Pull Processor Status Register from Stack
|
// Pull Processor Status Register from Stack
|
||||||
void CPU::PLP() { status = memory.PopByte(); }
|
void Cpu::PLP() { status = memory.PopByte(); }
|
||||||
|
|
||||||
void CPU::PLX() {
|
void Cpu::PLX() {
|
||||||
if (GetIndexSize()) {
|
if (GetIndexSize()) {
|
||||||
X = memory.PopByte();
|
X = memory.PopByte();
|
||||||
SetNegativeFlag((A & 0x80) != 0);
|
SetNegativeFlag((A & 0x80) != 0);
|
||||||
@@ -542,7 +542,7 @@ void CPU::PLX() {
|
|||||||
SetZeroFlag(X == 0);
|
SetZeroFlag(X == 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
void CPU::PLY() {
|
void Cpu::PLY() {
|
||||||
if (GetIndexSize()) {
|
if (GetIndexSize()) {
|
||||||
Y = memory.PopByte();
|
Y = memory.PopByte();
|
||||||
SetNegativeFlag((A & 0x80) != 0);
|
SetNegativeFlag((A & 0x80) != 0);
|
||||||
@@ -553,12 +553,12 @@ void CPU::PLY() {
|
|||||||
SetZeroFlag(Y == 0);
|
SetZeroFlag(Y == 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
void CPU::REP() {
|
void Cpu::REP() {
|
||||||
auto byte = FetchByte();
|
auto byte = FetchByte();
|
||||||
status &= ~byte;
|
status &= ~byte;
|
||||||
}
|
}
|
||||||
|
|
||||||
void CPU::ROL(uint32_t address, bool accumulator) {
|
void Cpu::ROL(uint32_t address, bool accumulator) {
|
||||||
if (accumulator) {
|
if (accumulator) {
|
||||||
if (GetAccumulatorSize()) { // 8-bit
|
if (GetAccumulatorSize()) { // 8-bit
|
||||||
uint8_t carry = GetCarryFlag() ? 0x01 : 0x00;
|
uint8_t carry = GetCarryFlag() ? 0x01 : 0x00;
|
||||||
@@ -588,7 +588,7 @@ void CPU::ROL(uint32_t address, bool accumulator) {
|
|||||||
SetZeroFlag(value == 0);
|
SetZeroFlag(value == 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
void CPU::ROR(uint32_t address, bool accumulator) {
|
void Cpu::ROR(uint32_t address, bool accumulator) {
|
||||||
if (accumulator) {
|
if (accumulator) {
|
||||||
if (GetAccumulatorSize()) { // 8-bit
|
if (GetAccumulatorSize()) { // 8-bit
|
||||||
uint8_t carry = GetCarryFlag() ? 0x80 : 0x00;
|
uint8_t carry = GetCarryFlag() ? 0x80 : 0x00;
|
||||||
@@ -618,21 +618,21 @@ void CPU::ROR(uint32_t address, bool accumulator) {
|
|||||||
SetZeroFlag(value == 0);
|
SetZeroFlag(value == 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
void CPU::RTI() {
|
void Cpu::RTI() {
|
||||||
status = memory.PopByte();
|
status = memory.PopByte();
|
||||||
PC = memory.PopWord();
|
PC = memory.PopWord();
|
||||||
}
|
}
|
||||||
|
|
||||||
void CPU::RTL() {
|
void Cpu::RTL() {
|
||||||
next_pc_ = memory.PopWord();
|
next_pc_ = memory.PopWord();
|
||||||
PB = memory.PopByte();
|
PB = memory.PopByte();
|
||||||
}
|
}
|
||||||
|
|
||||||
void CPU::RTS() {
|
void Cpu::RTS() {
|
||||||
last_call_frame_ = memory.PopWord();
|
last_call_frame_ = memory.PopWord();
|
||||||
}
|
}
|
||||||
|
|
||||||
void CPU::SBC(uint32_t value, bool isImmediate) {
|
void Cpu::SBC(uint32_t value, bool isImmediate) {
|
||||||
uint16_t operand;
|
uint16_t operand;
|
||||||
if (!GetAccumulatorSize()) { // 16-bit mode
|
if (!GetAccumulatorSize()) { // 16-bit mode
|
||||||
operand = isImmediate ? value : memory.ReadWord(value);
|
operand = isImmediate ? value : memory.ReadWord(value);
|
||||||
@@ -665,18 +665,18 @@ void CPU::SBC(uint32_t value, bool isImmediate) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void CPU::SEC() { status |= 0x01; }
|
void Cpu::SEC() { status |= 0x01; }
|
||||||
|
|
||||||
void CPU::SED() { status |= 0x08; }
|
void Cpu::SED() { status |= 0x08; }
|
||||||
|
|
||||||
void CPU::SEI() { status |= 0x04; }
|
void Cpu::SEI() { status |= 0x04; }
|
||||||
|
|
||||||
void CPU::SEP() {
|
void Cpu::SEP() {
|
||||||
auto byte = FetchByte();
|
auto byte = FetchByte();
|
||||||
status |= byte;
|
status |= byte;
|
||||||
}
|
}
|
||||||
|
|
||||||
void CPU::STA(uint32_t address) {
|
void Cpu::STA(uint32_t address) {
|
||||||
if (GetAccumulatorSize()) {
|
if (GetAccumulatorSize()) {
|
||||||
memory.WriteByte(address, static_cast<uint8_t>(A));
|
memory.WriteByte(address, static_cast<uint8_t>(A));
|
||||||
} else {
|
} else {
|
||||||
@@ -686,12 +686,12 @@ void CPU::STA(uint32_t address) {
|
|||||||
|
|
||||||
// TODO: Make this work with the Clock class of the CPU
|
// TODO: Make this work with the Clock class of the CPU
|
||||||
|
|
||||||
void CPU::STP() {
|
void Cpu::STP() {
|
||||||
// During the next phase 2 clock cycle, stop the processors oscillator input
|
// During the next phase 2 clock cycle, stop the processors oscillator input
|
||||||
// The processor is effectively shut down until a reset occurs (RES` pin).
|
// The processor is effectively shut down until a reset occurs (RES` pin).
|
||||||
}
|
}
|
||||||
|
|
||||||
void CPU::STX(uint16_t address) {
|
void Cpu::STX(uint16_t address) {
|
||||||
if (GetIndexSize()) {
|
if (GetIndexSize()) {
|
||||||
memory.WriteByte(address, static_cast<uint8_t>(X));
|
memory.WriteByte(address, static_cast<uint8_t>(X));
|
||||||
} else {
|
} else {
|
||||||
@@ -699,7 +699,7 @@ void CPU::STX(uint16_t address) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void CPU::STY(uint16_t address) {
|
void Cpu::STY(uint16_t address) {
|
||||||
if (GetIndexSize()) {
|
if (GetIndexSize()) {
|
||||||
memory.WriteByte(address, static_cast<uint8_t>(Y));
|
memory.WriteByte(address, static_cast<uint8_t>(Y));
|
||||||
} else {
|
} else {
|
||||||
@@ -707,7 +707,7 @@ void CPU::STY(uint16_t address) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void CPU::STZ(uint16_t address) {
|
void Cpu::STZ(uint16_t address) {
|
||||||
if (GetAccumulatorSize()) {
|
if (GetAccumulatorSize()) {
|
||||||
memory.WriteByte(address, 0x00);
|
memory.WriteByte(address, 0x00);
|
||||||
} else {
|
} else {
|
||||||
@@ -715,79 +715,79 @@ void CPU::STZ(uint16_t address) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void CPU::TAX() {
|
void Cpu::TAX() {
|
||||||
X = A;
|
X = A;
|
||||||
SetZeroFlag(X == 0);
|
SetZeroFlag(X == 0);
|
||||||
SetNegativeFlag(X & 0x80);
|
SetNegativeFlag(X & 0x80);
|
||||||
}
|
}
|
||||||
|
|
||||||
void CPU::TAY() {
|
void Cpu::TAY() {
|
||||||
Y = A;
|
Y = A;
|
||||||
SetZeroFlag(Y == 0);
|
SetZeroFlag(Y == 0);
|
||||||
SetNegativeFlag(Y & 0x80);
|
SetNegativeFlag(Y & 0x80);
|
||||||
}
|
}
|
||||||
|
|
||||||
void CPU::TCD() {
|
void Cpu::TCD() {
|
||||||
D = A;
|
D = A;
|
||||||
SetZeroFlag(D == 0);
|
SetZeroFlag(D == 0);
|
||||||
SetNegativeFlag(D & 0x80);
|
SetNegativeFlag(D & 0x80);
|
||||||
}
|
}
|
||||||
|
|
||||||
void CPU::TCS() { memory.SetSP(A); }
|
void Cpu::TCS() { memory.SetSP(A); }
|
||||||
|
|
||||||
void CPU::TDC() {
|
void Cpu::TDC() {
|
||||||
A = D;
|
A = D;
|
||||||
SetZeroFlag(A == 0);
|
SetZeroFlag(A == 0);
|
||||||
SetNegativeFlag(A & 0x80);
|
SetNegativeFlag(A & 0x80);
|
||||||
}
|
}
|
||||||
|
|
||||||
void CPU::TRB(uint16_t address) {
|
void Cpu::TRB(uint16_t address) {
|
||||||
uint8_t value = memory.ReadByte(address);
|
uint8_t value = memory.ReadByte(address);
|
||||||
SetZeroFlag((A & value) == 0);
|
SetZeroFlag((A & value) == 0);
|
||||||
value &= ~A;
|
value &= ~A;
|
||||||
memory.WriteByte(address, value);
|
memory.WriteByte(address, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
void CPU::TSB(uint16_t address) {
|
void Cpu::TSB(uint16_t address) {
|
||||||
uint8_t value = memory.ReadByte(address);
|
uint8_t value = memory.ReadByte(address);
|
||||||
SetZeroFlag((A & value) == 0);
|
SetZeroFlag((A & value) == 0);
|
||||||
value |= A;
|
value |= A;
|
||||||
memory.WriteByte(address, value);
|
memory.WriteByte(address, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
void CPU::TSC() {
|
void Cpu::TSC() {
|
||||||
A = SP();
|
A = SP();
|
||||||
SetZeroFlag(A == 0);
|
SetZeroFlag(A == 0);
|
||||||
SetNegativeFlag(A & 0x80);
|
SetNegativeFlag(A & 0x80);
|
||||||
}
|
}
|
||||||
|
|
||||||
void CPU::TSX() {
|
void Cpu::TSX() {
|
||||||
X = SP();
|
X = SP();
|
||||||
SetZeroFlag(X == 0);
|
SetZeroFlag(X == 0);
|
||||||
SetNegativeFlag(X & 0x80);
|
SetNegativeFlag(X & 0x80);
|
||||||
}
|
}
|
||||||
|
|
||||||
void CPU::TXA() {
|
void Cpu::TXA() {
|
||||||
A = X;
|
A = X;
|
||||||
SetZeroFlag(A == 0);
|
SetZeroFlag(A == 0);
|
||||||
SetNegativeFlag(A & 0x80);
|
SetNegativeFlag(A & 0x80);
|
||||||
}
|
}
|
||||||
|
|
||||||
void CPU::TXS() { memory.SetSP(X); }
|
void Cpu::TXS() { memory.SetSP(X); }
|
||||||
|
|
||||||
void CPU::TXY() {
|
void Cpu::TXY() {
|
||||||
Y = X;
|
Y = X;
|
||||||
SetZeroFlag(X == 0);
|
SetZeroFlag(X == 0);
|
||||||
SetNegativeFlag(X & 0x80);
|
SetNegativeFlag(X & 0x80);
|
||||||
}
|
}
|
||||||
|
|
||||||
void CPU::TYA() {
|
void Cpu::TYA() {
|
||||||
A = Y;
|
A = Y;
|
||||||
SetZeroFlag(A == 0);
|
SetZeroFlag(A == 0);
|
||||||
SetNegativeFlag(A & 0x80);
|
SetNegativeFlag(A & 0x80);
|
||||||
}
|
}
|
||||||
|
|
||||||
void CPU::TYX() {
|
void Cpu::TYX() {
|
||||||
X = Y;
|
X = Y;
|
||||||
SetZeroFlag(Y == 0);
|
SetZeroFlag(Y == 0);
|
||||||
SetNegativeFlag(Y & 0x80);
|
SetNegativeFlag(Y & 0x80);
|
||||||
@@ -795,20 +795,20 @@ void CPU::TYX() {
|
|||||||
|
|
||||||
// TODO: Make this communicate with the SNES class
|
// TODO: Make this communicate with the SNES class
|
||||||
|
|
||||||
void CPU::WAI() {
|
void Cpu::WAI() {
|
||||||
// Pull the RDY pin low
|
// Pull the RDY pin low
|
||||||
// Power consumption is reduced(?)
|
// Power consumption is reduced(?)
|
||||||
// RDY remains low until an external hardware interupt
|
// RDY remains low until an external hardware interupt
|
||||||
// (NMI, IRQ, ABORT, or RESET) is received from the SNES class
|
// (NMI, IRQ, ABORT, or RESET) is received from the SNES class
|
||||||
}
|
}
|
||||||
|
|
||||||
void CPU::XBA() {
|
void Cpu::XBA() {
|
||||||
uint8_t lowByte = A & 0xFF;
|
uint8_t lowByte = A & 0xFF;
|
||||||
uint8_t highByte = (A >> 8) & 0xFF;
|
uint8_t highByte = (A >> 8) & 0xFF;
|
||||||
A = (lowByte << 8) | highByte;
|
A = (lowByte << 8) | highByte;
|
||||||
}
|
}
|
||||||
|
|
||||||
void CPU::XCE() {
|
void Cpu::XCE() {
|
||||||
uint8_t carry = status & 0x01;
|
uint8_t carry = status & 0x01;
|
||||||
status &= ~0x01;
|
status &= ~0x01;
|
||||||
status |= E;
|
status |= E;
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ class Debugger {
|
|||||||
public:
|
public:
|
||||||
Debugger() = default;
|
Debugger() = default;
|
||||||
// Attach the debugger to the emulator
|
// Attach the debugger to the emulator
|
||||||
// Debugger(CPU &cpu, PPU &ppu, APU &apu);
|
// Debugger(CPU &cpu, PPU &ppu, Apu &apu);
|
||||||
|
|
||||||
// Set a breakpoint
|
// Set a breakpoint
|
||||||
void SetBreakpoint(uint16_t address);
|
void SetBreakpoint(uint16_t address);
|
||||||
@@ -43,7 +43,7 @@ class Debugger {
|
|||||||
// References to the emulator's components
|
// References to the emulator's components
|
||||||
// CPU &cpu;
|
// CPU &cpu;
|
||||||
// PPU &ppu;
|
// PPU &ppu;
|
||||||
// APU &apu;
|
// Apu &apu;
|
||||||
|
|
||||||
// Breakpoints, watchpoints, etc.
|
// Breakpoints, watchpoints, etc.
|
||||||
// ...
|
// ...
|
||||||
|
|||||||
@@ -259,7 +259,7 @@ void Emulator::RenderBreakpointList() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Emulator::RenderCpuState(CPU& cpu) {
|
void Emulator::RenderCpuState(Cpu& cpu) {
|
||||||
if (ImGui::CollapsingHeader("Register Values",
|
if (ImGui::CollapsingHeader("Register Values",
|
||||||
ImGuiTreeNodeFlags_DefaultOpen)) {
|
ImGuiTreeNodeFlags_DefaultOpen)) {
|
||||||
ImGui::Columns(2, "RegistersColumns");
|
ImGui::Columns(2, "RegistersColumns");
|
||||||
|
|||||||
@@ -11,9 +11,18 @@
|
|||||||
|
|
||||||
namespace yaze {
|
namespace yaze {
|
||||||
namespace app {
|
namespace app {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @namespace yaze::app::emu
|
||||||
|
* @brief SNES Emulation and debugging tools.
|
||||||
|
*/
|
||||||
namespace emu {
|
namespace emu {
|
||||||
|
|
||||||
class Emulator : public SharedROM {
|
/**
|
||||||
|
* @class Emulator
|
||||||
|
* @brief A class for emulating and debugging SNES games.
|
||||||
|
*/
|
||||||
|
class Emulator : public SharedRom {
|
||||||
public:
|
public:
|
||||||
void Run();
|
void Run();
|
||||||
|
|
||||||
@@ -24,7 +33,7 @@ class Emulator : public SharedROM {
|
|||||||
void RenderEmulator();
|
void RenderEmulator();
|
||||||
void RenderSnesPpu();
|
void RenderSnesPpu();
|
||||||
void RenderBreakpointList();
|
void RenderBreakpointList();
|
||||||
void RenderCpuState(CPU& cpu);
|
void RenderCpuState(Cpu& cpu);
|
||||||
void RenderMemoryViewer();
|
void RenderMemoryViewer();
|
||||||
|
|
||||||
struct Bookmark {
|
struct Bookmark {
|
||||||
|
|||||||
@@ -5,8 +5,9 @@
|
|||||||
namespace yaze {
|
namespace yaze {
|
||||||
namespace app {
|
namespace app {
|
||||||
namespace emu {
|
namespace emu {
|
||||||
|
namespace memory {
|
||||||
|
|
||||||
void DMA::StartDMATransfer(uint8_t channelMask) {
|
void DirectMemoryAccess::StartDMATransfer(uint8_t channelMask) {
|
||||||
for (int i = 0; i < 8; ++i) {
|
for (int i = 0; i < 8; ++i) {
|
||||||
if ((channelMask & (1 << i)) != 0) {
|
if ((channelMask & (1 << i)) != 0) {
|
||||||
Channel& ch = channels[i];
|
Channel& ch = channels[i];
|
||||||
@@ -20,8 +21,9 @@ void DMA::StartDMATransfer(uint8_t channelMask) {
|
|||||||
// Determine the transfer size based on the DMAPn register
|
// Determine the transfer size based on the DMAPn register
|
||||||
bool transferTwoBytes = (ch.DMAPn & 0x40) != 0;
|
bool transferTwoBytes = (ch.DMAPn & 0x40) != 0;
|
||||||
|
|
||||||
// Perform the DMA transfer based on the channel parameters
|
// Perform the DirectMemoryAccess transfer based on the channel parameters
|
||||||
std::cout << "Starting DMA transfer for channel " << i << std::endl;
|
std::cout << "Starting DirectMemoryAccess transfer for channel " << i
|
||||||
|
<< std::endl;
|
||||||
|
|
||||||
for (uint16_t j = 0; j < ch.DASn; ++j) {
|
for (uint16_t j = 0; j < ch.DASn; ++j) {
|
||||||
// Read a byte or two bytes from memory based on the transfer size
|
// Read a byte or two bytes from memory based on the transfer size
|
||||||
@@ -46,7 +48,7 @@ void DMA::StartDMATransfer(uint8_t channelMask) {
|
|||||||
MDMAEN = channelMask; // Set the MDMAEN register to the channel mask
|
MDMAEN = channelMask; // Set the MDMAEN register to the channel mask
|
||||||
}
|
}
|
||||||
|
|
||||||
void DMA::EnableHDMATransfers(uint8_t channelMask) {
|
void DirectMemoryAccess::EnableHDMATransfers(uint8_t channelMask) {
|
||||||
for (int i = 0; i < 8; ++i) {
|
for (int i = 0; i < 8; ++i) {
|
||||||
if ((channelMask & (1 << i)) != 0) {
|
if ((channelMask & (1 << i)) != 0) {
|
||||||
Channel& ch = channels[i];
|
Channel& ch = channels[i];
|
||||||
@@ -70,6 +72,7 @@ void DMA::EnableHDMATransfers(uint8_t channelMask) {
|
|||||||
HDMAEN = channelMask; // Set the HDMAEN register to the channel mask
|
HDMAEN = channelMask; // Set the HDMAEN register to the channel mask
|
||||||
}
|
}
|
||||||
|
|
||||||
|
} // namespace memory
|
||||||
} // namespace emu
|
} // namespace emu
|
||||||
} // namespace app
|
} // namespace app
|
||||||
} // namespace yaze
|
} // namespace yaze
|
||||||
@@ -6,11 +6,11 @@
|
|||||||
namespace yaze {
|
namespace yaze {
|
||||||
namespace app {
|
namespace app {
|
||||||
namespace emu {
|
namespace emu {
|
||||||
|
namespace memory {
|
||||||
|
|
||||||
// Direct Memory Address
|
class DirectMemoryAccess {
|
||||||
class DMA {
|
|
||||||
public:
|
public:
|
||||||
DMA() {
|
DirectMemoryAccess() {
|
||||||
// Initialize DMA and HDMA channels
|
// Initialize DMA and HDMA channels
|
||||||
for (int i = 0; i < 8; ++i) {
|
for (int i = 0; i < 8; ++i) {
|
||||||
channels[i].DMAPn = 0;
|
channels[i].DMAPn = 0;
|
||||||
@@ -52,6 +52,8 @@ class DMA {
|
|||||||
uint8_t MDMAEN = 0; // Start DMA transfer
|
uint8_t MDMAEN = 0; // Start DMA transfer
|
||||||
uint8_t HDMAEN = 0; // Enable HDMA transfers
|
uint8_t HDMAEN = 0; // Enable HDMA transfers
|
||||||
};
|
};
|
||||||
|
|
||||||
|
} // namespace memory
|
||||||
} // namespace emu
|
} // namespace emu
|
||||||
} // namespace app
|
} // namespace app
|
||||||
} // namespace yaze
|
} // namespace yaze
|
||||||
|
|||||||
@@ -12,6 +12,7 @@
|
|||||||
namespace yaze {
|
namespace yaze {
|
||||||
namespace app {
|
namespace app {
|
||||||
namespace emu {
|
namespace emu {
|
||||||
|
namespace memory {
|
||||||
|
|
||||||
void DrawSnesMemoryMapping(const MemoryImpl& memory) {
|
void DrawSnesMemoryMapping(const MemoryImpl& memory) {
|
||||||
// Using those as a base value to create width/height that are factor of the
|
// Using those as a base value to create width/height that are factor of the
|
||||||
@@ -77,6 +78,7 @@ void DrawSnesMemoryMapping(const MemoryImpl& memory) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
} // namespace memory
|
||||||
} // namespace emu
|
} // namespace emu
|
||||||
} // namespace app
|
} // namespace app
|
||||||
} // namespace yaze
|
} // namespace yaze
|
||||||
@@ -28,12 +28,13 @@
|
|||||||
namespace yaze {
|
namespace yaze {
|
||||||
namespace app {
|
namespace app {
|
||||||
namespace emu {
|
namespace emu {
|
||||||
|
namespace memory {
|
||||||
|
|
||||||
enum ROMSpeed { SLOW_ROM = 0x00, FAST_ROM = 0x07 };
|
enum RomSpeed { SLOW_ROM = 0x00, FAST_ROM = 0x07 };
|
||||||
|
|
||||||
enum BankSize { LOW_ROM = 0x00, HI_ROM = 0x01 };
|
enum BankSize { LOW_ROM = 0x00, HI_ROM = 0x01 };
|
||||||
|
|
||||||
enum ROMType {
|
enum RomType {
|
||||||
ROM_DEFAULT = 0x00,
|
ROM_DEFAULT = 0x00,
|
||||||
ROM_RAM = 0x01,
|
ROM_RAM = 0x01,
|
||||||
ROM_SRAM = 0x02,
|
ROM_SRAM = 0x02,
|
||||||
@@ -43,7 +44,7 @@ enum ROMType {
|
|||||||
FX = 0x06
|
FX = 0x06
|
||||||
};
|
};
|
||||||
|
|
||||||
enum ROMSize {
|
enum RomSize {
|
||||||
SIZE_2_MBIT = 0x08,
|
SIZE_2_MBIT = 0x08,
|
||||||
SIZE_4_MBIT = 0x09,
|
SIZE_4_MBIT = 0x09,
|
||||||
SIZE_8_MBIT = 0x0A,
|
SIZE_8_MBIT = 0x0A,
|
||||||
@@ -51,7 +52,7 @@ enum ROMSize {
|
|||||||
SIZE_32_MBIT = 0x0C
|
SIZE_32_MBIT = 0x0C
|
||||||
};
|
};
|
||||||
|
|
||||||
enum SRAMSize {
|
enum SramSize {
|
||||||
NO_SRAM = 0x00,
|
NO_SRAM = 0x00,
|
||||||
SRAM_16_KBIT = 0x01,
|
SRAM_16_KBIT = 0x01,
|
||||||
SRAM_32_KBIT = 0x02,
|
SRAM_32_KBIT = 0x02,
|
||||||
@@ -73,14 +74,14 @@ enum License {
|
|||||||
// ... and other licenses
|
// ... and other licenses
|
||||||
};
|
};
|
||||||
|
|
||||||
class ROMInfo {
|
class RomInfo {
|
||||||
public:
|
public:
|
||||||
std::string title;
|
std::string title;
|
||||||
ROMSpeed romSpeed;
|
RomSpeed romSpeed;
|
||||||
BankSize bankSize;
|
BankSize bankSize;
|
||||||
ROMType romType;
|
RomType romType;
|
||||||
ROMSize romSize;
|
RomSize romSize;
|
||||||
SRAMSize sramSize;
|
SramSize sramSize;
|
||||||
CountryCode countryCode;
|
CountryCode countryCode;
|
||||||
License license;
|
License license;
|
||||||
uint8_t version;
|
uint8_t version;
|
||||||
@@ -105,7 +106,9 @@ constexpr uint32_t kVRAMSize = 0x10000;
|
|||||||
constexpr uint32_t kOAMStart = 0x218000;
|
constexpr uint32_t kOAMStart = 0x218000;
|
||||||
constexpr uint32_t kOAMSize = 0x220;
|
constexpr uint32_t kOAMSize = 0x220;
|
||||||
|
|
||||||
// memory.h
|
/**
|
||||||
|
* @brief Memory interface
|
||||||
|
*/
|
||||||
class Memory {
|
class Memory {
|
||||||
public:
|
public:
|
||||||
virtual ~Memory() = default;
|
virtual ~Memory() = default;
|
||||||
@@ -137,6 +140,29 @@ class Memory {
|
|||||||
|
|
||||||
enum class MemoryMapping { SNES_LOROM = 0, PC_ADDRESS = 1 };
|
enum class MemoryMapping { SNES_LOROM = 0, PC_ADDRESS = 1 };
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @class MemoryImpl
|
||||||
|
* @brief Implementation of the Memory interface for emulating memory in a SNES
|
||||||
|
* system.
|
||||||
|
*
|
||||||
|
* The MemoryImpl class provides methods for initializing and accessing memory
|
||||||
|
* in a SNES system. It implements the Memory interface and inherits from the
|
||||||
|
* Loggable class.
|
||||||
|
*
|
||||||
|
* The class supports different memory mappings, including LoROM and PC_ADDRESS
|
||||||
|
* mappings. It provides methods for reading and writing bytes, words, and longs
|
||||||
|
* from/to memory. It also supports stack operations for pushing and popping
|
||||||
|
* values.
|
||||||
|
*
|
||||||
|
* The class maintains separate vectors for ROM, RAM, VRAM, and OAM memory
|
||||||
|
* regions. It provides methods for accessing these memory regions and
|
||||||
|
* retrieving their sizes.
|
||||||
|
*
|
||||||
|
* The class also allows adding observers to be notified when memory is read or
|
||||||
|
* written.
|
||||||
|
*
|
||||||
|
* @note This class assumes a 16-bit address space.
|
||||||
|
*/
|
||||||
class MemoryImpl : public Memory, public Loggable {
|
class MemoryImpl : public Memory, public Loggable {
|
||||||
public:
|
public:
|
||||||
void Initialize(const std::vector<uint8_t>& romData, bool verbose = false,
|
void Initialize(const std::vector<uint8_t>& romData, bool verbose = false,
|
||||||
@@ -396,6 +422,7 @@ class MemoryImpl : public Memory, public Loggable {
|
|||||||
|
|
||||||
void DrawSnesMemoryMapping(const MemoryImpl& memory);
|
void DrawSnesMemoryMapping(const MemoryImpl& memory);
|
||||||
|
|
||||||
|
} // namespace memory
|
||||||
} // namespace emu
|
} // namespace emu
|
||||||
} // namespace app
|
} // namespace app
|
||||||
} // namespace yaze
|
} // namespace yaze
|
||||||
|
|||||||
@@ -8,10 +8,14 @@
|
|||||||
#include "app/emu/cpu/cpu.h"
|
#include "app/emu/cpu/cpu.h"
|
||||||
#include "app/emu/memory/memory.h"
|
#include "app/emu/memory/memory.h"
|
||||||
|
|
||||||
using yaze::app::emu::Clock;
|
namespace yaze {
|
||||||
using yaze::app::emu::CPU;
|
namespace app {
|
||||||
using yaze::app::emu::Memory;
|
namespace emu {
|
||||||
|
namespace memory {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Mock CPU class for testing
|
||||||
|
*/
|
||||||
class MockClock : public Clock {
|
class MockClock : public Clock {
|
||||||
public:
|
public:
|
||||||
MOCK_METHOD(void, UpdateClock, (double delta), (override));
|
MOCK_METHOD(void, UpdateClock, (double delta), (override));
|
||||||
@@ -21,9 +25,23 @@ class MockClock : public Clock {
|
|||||||
MOCK_METHOD(float, GetFrequency, (), (const, override));
|
MOCK_METHOD(float, GetFrequency, (), (const, override));
|
||||||
};
|
};
|
||||||
|
|
||||||
// 0x1000000 is 16 MB, simplifying the memory layout for testing
|
/**
|
||||||
// 2 MB is = 0x200000
|
* @class MockMemory
|
||||||
|
* @brief A mock implementation of the Memory class.
|
||||||
|
*
|
||||||
|
* This class is used for testing purposes and provides a mock implementation of
|
||||||
|
* the Memory class. It allows for reading and writing bytes, words, and longs
|
||||||
|
* to memory, as well as pushing and popping values onto and from the stack. It
|
||||||
|
* also provides methods for setting and getting the stack pointer, initializing
|
||||||
|
* memory with ROM data, and clearing memory.
|
||||||
|
*
|
||||||
|
* The mock memory is represented by a vector of uint8_t values, where each
|
||||||
|
* element represents a byte of memory. The memory can be accessed using the []
|
||||||
|
* operator, and its contents can be set using the SetMemoryContents() method.
|
||||||
|
*
|
||||||
|
* @note This class is intended for testing purposes only and should not be used
|
||||||
|
* in production code.
|
||||||
|
*/
|
||||||
class MockMemory : public Memory {
|
class MockMemory : public Memory {
|
||||||
public:
|
public:
|
||||||
MOCK_CONST_METHOD1(ReadByte, uint8_t(uint32_t address));
|
MOCK_CONST_METHOD1(ReadByte, uint8_t(uint32_t address));
|
||||||
@@ -85,6 +103,8 @@ class MockMemory : public Memory {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 16MB = 0x1000000
|
||||||
|
// 02MB = 0x200000
|
||||||
void Initialize(const std::vector<uint8_t>& romData) {
|
void Initialize(const std::vector<uint8_t>& romData) {
|
||||||
// 16 MB, simplifying the memory layout for testing
|
// 16 MB, simplifying the memory layout for testing
|
||||||
memory_.resize(0x1000000);
|
memory_.resize(0x1000000);
|
||||||
@@ -186,4 +206,9 @@ class MockMemory : public Memory {
|
|||||||
uint16_t SP_ = 0x01FF;
|
uint16_t SP_ = 0x01FF;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
} // namespace memory
|
||||||
|
} // namespace emu
|
||||||
|
} // namespace app
|
||||||
|
} // namespace yaze
|
||||||
|
|
||||||
#endif // YAZE_TEST_MOCK_MOCK_MEMORY_H
|
#endif // YAZE_TEST_MOCK_MOCK_MEMORY_H
|
||||||
@@ -46,7 +46,7 @@ uint16_t GetHeaderOffset(const Memory& memory) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void audio_callback(void* userdata, uint8_t* stream, int len) {
|
void audio_callback(void* userdata, uint8_t* stream, int len) {
|
||||||
auto* apu = static_cast<APU*>(userdata);
|
auto* apu = static_cast<audio::Apu*>(userdata);
|
||||||
auto* buffer = reinterpret_cast<int16_t*>(stream);
|
auto* buffer = reinterpret_cast<int16_t*>(stream);
|
||||||
|
|
||||||
for (int i = 0; i < len / 2; i++) { // Assuming 16-bit samples
|
for (int i = 0; i < len / 2; i++) { // Assuming 16-bit samples
|
||||||
@@ -57,8 +57,8 @@ void audio_callback(void* userdata, uint8_t* stream, int len) {
|
|||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
ROMInfo SNES::ReadRomHeader(uint32_t offset) {
|
RomInfo SNES::ReadRomHeader(uint32_t offset) {
|
||||||
ROMInfo romInfo;
|
RomInfo romInfo;
|
||||||
|
|
||||||
// Read cartridge title
|
// Read cartridge title
|
||||||
char title[22];
|
char title[22];
|
||||||
@@ -70,17 +70,17 @@ ROMInfo SNES::ReadRomHeader(uint32_t offset) {
|
|||||||
|
|
||||||
// Read ROM speed and memory map mode
|
// Read ROM speed and memory map mode
|
||||||
uint8_t romSpeedAndMapMode = cpu_.ReadByte(offset + 0x15);
|
uint8_t romSpeedAndMapMode = cpu_.ReadByte(offset + 0x15);
|
||||||
romInfo.romSpeed = (ROMSpeed)(romSpeedAndMapMode & 0x07);
|
romInfo.romSpeed = (RomSpeed)(romSpeedAndMapMode & 0x07);
|
||||||
romInfo.bankSize = (BankSize)((romSpeedAndMapMode >> 5) & 0x01);
|
romInfo.bankSize = (BankSize)((romSpeedAndMapMode >> 5) & 0x01);
|
||||||
|
|
||||||
// Read ROM type
|
// Read ROM type
|
||||||
romInfo.romType = (ROMType)cpu_.ReadByte(offset + 0x16);
|
romInfo.romType = (RomType)cpu_.ReadByte(offset + 0x16);
|
||||||
|
|
||||||
// Read ROM size
|
// Read ROM size
|
||||||
romInfo.romSize = (ROMSize)cpu_.ReadByte(offset + 0x17);
|
romInfo.romSize = (RomSize)cpu_.ReadByte(offset + 0x17);
|
||||||
|
|
||||||
// Read RAM size
|
// Read RAM size
|
||||||
romInfo.sramSize = (SRAMSize)cpu_.ReadByte(offset + 0x18);
|
romInfo.sramSize = (SramSize)cpu_.ReadByte(offset + 0x18);
|
||||||
|
|
||||||
// Read country code
|
// Read country code
|
||||||
romInfo.countryCode = (CountryCode)cpu_.ReadByte(offset + 0x19);
|
romInfo.countryCode = (CountryCode)cpu_.ReadByte(offset + 0x19);
|
||||||
@@ -106,7 +106,7 @@ ROMInfo SNES::ReadRomHeader(uint32_t offset) {
|
|||||||
return romInfo;
|
return romInfo;
|
||||||
}
|
}
|
||||||
|
|
||||||
void SNES::Init(ROM& rom) {
|
void SNES::Init(Rom& rom) {
|
||||||
// Perform a long jump into a FastROM bank (if the ROM speed is FastROM)
|
// Perform a long jump into a FastROM bank (if the ROM speed is FastROM)
|
||||||
// Disable the emulation flag (switch to 65816 native mode)
|
// Disable the emulation flag (switch to 65816 native mode)
|
||||||
cpu_.E = 0;
|
cpu_.E = 0;
|
||||||
@@ -257,7 +257,7 @@ void SNES::Run() {
|
|||||||
void SNES::StepRun() {
|
void SNES::StepRun() {
|
||||||
// Update the CPU
|
// Update the CPU
|
||||||
cpu_.UpdateClock(0.0);
|
cpu_.UpdateClock(0.0);
|
||||||
cpu_.Update(CPU::UpdateMode::Step);
|
cpu_.Update(Cpu::UpdateMode::Step);
|
||||||
|
|
||||||
// Update the PPU
|
// Update the PPU
|
||||||
ppu_.UpdateClock(0.0);
|
ppu_.UpdateClock(0.0);
|
||||||
|
|||||||
@@ -20,15 +20,17 @@ namespace yaze {
|
|||||||
namespace app {
|
namespace app {
|
||||||
namespace emu {
|
namespace emu {
|
||||||
|
|
||||||
class SNES : public DMA {
|
using namespace memory;
|
||||||
|
|
||||||
|
class SNES : public DirectMemoryAccess {
|
||||||
public:
|
public:
|
||||||
SNES() = default;
|
SNES() = default;
|
||||||
~SNES() = default;
|
~SNES() = default;
|
||||||
|
|
||||||
ROMInfo ReadRomHeader(uint32_t offset);
|
RomInfo ReadRomHeader(uint32_t offset);
|
||||||
|
|
||||||
// Initialization
|
// Initialization
|
||||||
void Init(ROM& rom);
|
void Init(Rom& rom);
|
||||||
|
|
||||||
// Main emulation loop
|
// Main emulation loop
|
||||||
void Run();
|
void Run();
|
||||||
@@ -61,16 +63,16 @@ class SNES : public DMA {
|
|||||||
|
|
||||||
bool running() const { return running_; }
|
bool running() const { return running_; }
|
||||||
|
|
||||||
auto cpu() -> CPU& { return cpu_; }
|
auto cpu() -> Cpu& { return cpu_; }
|
||||||
auto ppu() -> Ppu& { return ppu_; }
|
auto ppu() -> video::Ppu& { return ppu_; }
|
||||||
auto Memory() -> MemoryImpl* { return &memory_; }
|
auto Memory() -> MemoryImpl* { return &memory_; }
|
||||||
|
|
||||||
void SetCpuMode(int mode) { cpu_mode_ = mode; }
|
void SetCpuMode(int mode) { cpu_mode_ = mode; }
|
||||||
CPU::UpdateMode GetCpuMode() const {
|
Cpu::UpdateMode GetCpuMode() const {
|
||||||
return static_cast<CPU::UpdateMode>(cpu_mode_);
|
return static_cast<Cpu::UpdateMode>(cpu_mode_);
|
||||||
}
|
}
|
||||||
|
|
||||||
void SetupMemory(ROM& rom) {
|
void SetupMemory(Rom& rom) {
|
||||||
// Setup observers for the memory space
|
// Setup observers for the memory space
|
||||||
memory_.AddObserver(&apu_);
|
memory_.AddObserver(&apu_);
|
||||||
memory_.AddObserver(&ppu_);
|
memory_.AddObserver(&ppu_);
|
||||||
@@ -88,14 +90,14 @@ class SNES : public DMA {
|
|||||||
// Components of the SNES
|
// Components of the SNES
|
||||||
MemoryImpl memory_;
|
MemoryImpl memory_;
|
||||||
ClockImpl clock_;
|
ClockImpl clock_;
|
||||||
AudioRamImpl audio_ram_;
|
audio::AudioRamImpl audio_ram_;
|
||||||
|
|
||||||
CPU cpu_{memory_, clock_};
|
Cpu cpu_{memory_, clock_};
|
||||||
Ppu ppu_{memory_, clock_};
|
video::Ppu ppu_{memory_, clock_};
|
||||||
APU apu_{memory_, audio_ram_, clock_};
|
audio::Apu apu_{memory_, audio_ram_, clock_};
|
||||||
|
|
||||||
// Helper classes
|
// Helper classes
|
||||||
ROMInfo rom_info_;
|
RomInfo rom_info_;
|
||||||
Debugger debugger;
|
Debugger debugger;
|
||||||
|
|
||||||
// Currently loaded ROM
|
// Currently loaded ROM
|
||||||
|
|||||||
@@ -9,6 +9,7 @@
|
|||||||
namespace yaze {
|
namespace yaze {
|
||||||
namespace app {
|
namespace app {
|
||||||
namespace emu {
|
namespace emu {
|
||||||
|
namespace video {
|
||||||
|
|
||||||
using namespace PpuRegisters;
|
using namespace PpuRegisters;
|
||||||
|
|
||||||
@@ -56,8 +57,8 @@ void Ppu::RenderScanline() {
|
|||||||
|
|
||||||
// Fetch the tile data from VRAM, tile map data from memory, and palette data
|
// Fetch the tile data from VRAM, tile map data from memory, and palette data
|
||||||
// from CGRAM
|
// from CGRAM
|
||||||
// UpdateTileData(); // Fetches the tile data from VRAM and stores it in an
|
// UpdateTileData(); // Fetches the tile data from VRAM and stores it in
|
||||||
// internal buffer
|
// an internal buffer
|
||||||
UpdateTileMapData(); // Fetches the tile map data from memory and stores it
|
UpdateTileMapData(); // Fetches the tile map data from memory and stores it
|
||||||
// in an internal buffer
|
// in an internal buffer
|
||||||
UpdatePaletteData(); // Fetches the palette data from CGRAM and stores it in
|
UpdatePaletteData(); // Fetches the palette data from CGRAM and stores it in
|
||||||
@@ -430,6 +431,7 @@ void Ppu::DisplayFrameBuffer() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
} // namespace video
|
||||||
} // namespace emu
|
} // namespace emu
|
||||||
} // namespace app
|
} // namespace app
|
||||||
} // namespace yaze
|
} // namespace yaze
|
||||||
|
|||||||
@@ -13,8 +13,10 @@
|
|||||||
namespace yaze {
|
namespace yaze {
|
||||||
namespace app {
|
namespace app {
|
||||||
namespace emu {
|
namespace emu {
|
||||||
|
namespace video {
|
||||||
|
|
||||||
using namespace yaze::app::emu::PpuRegisters;
|
using namespace PpuRegisters;
|
||||||
|
using namespace memory;
|
||||||
|
|
||||||
class PpuInterface {
|
class PpuInterface {
|
||||||
public:
|
public:
|
||||||
@@ -263,10 +265,10 @@ struct BackgroundLayer {
|
|||||||
|
|
||||||
const int kPpuClockSpeed = 5369318; // 5.369318 MHz
|
const int kPpuClockSpeed = 5369318; // 5.369318 MHz
|
||||||
|
|
||||||
class Ppu : public Observer, public SharedROM {
|
class Ppu : public Observer, public SharedRom {
|
||||||
public:
|
public:
|
||||||
// Initializes the PPU with the necessary resources and dependencies
|
// Initializes the PPU with the necessary resources and dependencies
|
||||||
Ppu(Memory& memory, Clock& clock) : memory_(memory), clock_(clock) {}
|
Ppu(memory::Memory& memory, Clock& clock) : memory_(memory), clock_(clock) {}
|
||||||
|
|
||||||
// Initialize the frame buffer
|
// Initialize the frame buffer
|
||||||
void Init() {
|
void Init() {
|
||||||
@@ -386,6 +388,7 @@ class Ppu : public Observer, public SharedROM {
|
|||||||
const int visibleScanlines = 224; // SNES PPU renders 224 visible scanlines
|
const int visibleScanlines = 224; // SNES PPU renders 224 visible scanlines
|
||||||
};
|
};
|
||||||
|
|
||||||
|
} // namespace video
|
||||||
} // namespace emu
|
} // namespace emu
|
||||||
} // namespace app
|
} // namespace app
|
||||||
} // namespace yaze
|
} // namespace yaze
|
||||||
|
|||||||
@@ -8,7 +8,7 @@
|
|||||||
namespace yaze {
|
namespace yaze {
|
||||||
namespace app {
|
namespace app {
|
||||||
namespace emu {
|
namespace emu {
|
||||||
|
namespace video {
|
||||||
namespace PpuRegisters {
|
namespace PpuRegisters {
|
||||||
|
|
||||||
constexpr uint16_t INIDISP = 0x2100;
|
constexpr uint16_t INIDISP = 0x2100;
|
||||||
@@ -414,7 +414,7 @@ struct STAT78 {
|
|||||||
};
|
};
|
||||||
|
|
||||||
} // namespace PpuRegisters
|
} // namespace PpuRegisters
|
||||||
|
} // namespace video
|
||||||
} // namespace emu
|
} // namespace emu
|
||||||
} // namespace app
|
} // namespace app
|
||||||
} // namespace yaze
|
} // namespace yaze
|
||||||
|
|||||||
@@ -325,54 +325,76 @@ void Bitmap::LoadFromPngData(const std::vector<uint8_t> &png_data, int width,
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Convert SNESPalette to SDL_Palette for surface.
|
// Convert SNESPalette to SDL_Palette for surface.
|
||||||
void Bitmap::ApplyPalette(const SnesPalette &palette) {
|
absl::Status Bitmap::ApplyPalette(const SnesPalette &palette) {
|
||||||
|
if (surface_ == nullptr) {
|
||||||
|
return absl::FailedPreconditionError("Surface is null");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (surface_->format == nullptr || surface_->format->palette == nullptr) {
|
||||||
|
return absl::FailedPreconditionError("Surface format or palette is null");
|
||||||
|
}
|
||||||
|
|
||||||
palette_ = palette;
|
palette_ = palette;
|
||||||
|
|
||||||
|
SDL_Palette *sdlPalette = surface_->format->palette;
|
||||||
|
if (sdlPalette == nullptr) {
|
||||||
|
return absl::InternalError("Failed to get SDL palette");
|
||||||
|
}
|
||||||
|
|
||||||
SDL_UnlockSurface(surface_.get());
|
SDL_UnlockSurface(surface_.get());
|
||||||
|
|
||||||
for (int i = 0; i < palette.size(); ++i) {
|
for (int i = 0; i < palette.size(); ++i) {
|
||||||
if (palette.GetColor(i).is_transparent()) {
|
ASSIGN_OR_RETURN(gfx::SnesColor pal_color, palette.GetColor(i));
|
||||||
surface_->format->palette->colors[i].r = 0;
|
if (pal_color.is_transparent()) {
|
||||||
surface_->format->palette->colors[i].g = 0;
|
sdlPalette->colors[i].r = 0;
|
||||||
surface_->format->palette->colors[i].b = 0;
|
sdlPalette->colors[i].g = 0;
|
||||||
surface_->format->palette->colors[i].a = 0;
|
sdlPalette->colors[i].b = 0;
|
||||||
|
sdlPalette->colors[i].a = 0;
|
||||||
} else {
|
} else {
|
||||||
surface_->format->palette->colors[i].r = palette.GetColor(i).rgb().x;
|
sdlPalette->colors[i].r = pal_color.rgb().x;
|
||||||
surface_->format->palette->colors[i].g = palette.GetColor(i).rgb().y;
|
sdlPalette->colors[i].g = pal_color.rgb().y;
|
||||||
surface_->format->palette->colors[i].b = palette.GetColor(i).rgb().z;
|
sdlPalette->colors[i].b = pal_color.rgb().z;
|
||||||
surface_->format->palette->colors[i].a = palette.GetColor(i).rgb().w;
|
sdlPalette->colors[i].a = pal_color.rgb().w;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
SDL_LockSurface(surface_.get());
|
SDL_LockSurface(surface_.get());
|
||||||
|
|
||||||
|
return absl::OkStatus();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Bitmap::ApplyPaletteFromPaletteGroup(const SnesPalette &palette,
|
absl::Status Bitmap::ApplyPaletteFromPaletteGroup(const SnesPalette &palette,
|
||||||
int palette_id) {
|
int palette_id) {
|
||||||
auto start_index = palette_id * 8;
|
auto start_index = palette_id * 8;
|
||||||
palette_ = palette.sub_palette(start_index, start_index + 8);
|
palette_ = palette.sub_palette(start_index, start_index + 8);
|
||||||
SDL_UnlockSurface(surface_.get());
|
SDL_UnlockSurface(surface_.get());
|
||||||
for (int i = 0; i < palette_.size(); ++i) {
|
for (int i = 0; i < palette_.size(); ++i) {
|
||||||
if (palette_.GetColor(i).is_transparent()) {
|
ASSIGN_OR_RETURN(auto pal_color, palette_.GetColor(i));
|
||||||
|
if (pal_color.is_transparent()) {
|
||||||
surface_->format->palette->colors[i].r = 0;
|
surface_->format->palette->colors[i].r = 0;
|
||||||
surface_->format->palette->colors[i].g = 0;
|
surface_->format->palette->colors[i].g = 0;
|
||||||
surface_->format->palette->colors[i].b = 0;
|
surface_->format->palette->colors[i].b = 0;
|
||||||
surface_->format->palette->colors[i].a = 0;
|
surface_->format->palette->colors[i].a = 0;
|
||||||
} else {
|
} else {
|
||||||
surface_->format->palette->colors[i].r = palette_.GetColor(i).rgb().x;
|
surface_->format->palette->colors[i].r = pal_color.rgb().x;
|
||||||
surface_->format->palette->colors[i].g = palette_.GetColor(i).rgb().y;
|
surface_->format->palette->colors[i].g = pal_color.rgb().y;
|
||||||
surface_->format->palette->colors[i].b = palette_.GetColor(i).rgb().z;
|
surface_->format->palette->colors[i].b = pal_color.rgb().z;
|
||||||
surface_->format->palette->colors[i].a = palette_.GetColor(i).rgb().w;
|
surface_->format->palette->colors[i].a = pal_color.rgb().w;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
SDL_LockSurface(surface_.get());
|
SDL_LockSurface(surface_.get());
|
||||||
|
return absl::OkStatus();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Bitmap::ApplyPaletteWithTransparent(const SnesPalette &palette, int index,
|
absl::Status Bitmap::ApplyPaletteWithTransparent(const SnesPalette &palette,
|
||||||
int length) {
|
int index, int length) {
|
||||||
auto start_index = index * 7;
|
auto start_index = index * 7;
|
||||||
palette_ = palette.sub_palette(start_index, start_index + 7);
|
palette_ = palette.sub_palette(start_index, start_index + 7);
|
||||||
std::vector<ImVec4> colors;
|
std::vector<ImVec4> colors;
|
||||||
colors.push_back(ImVec4(0, 0, 0, 0));
|
colors.push_back(ImVec4(0, 0, 0, 0));
|
||||||
for (int i = start_index; i < start_index + 7; ++i) {
|
for (int i = start_index; i < start_index + 7; ++i) {
|
||||||
colors.push_back(palette.GetColor(i).rgb());
|
ASSIGN_OR_RETURN(auto pal_color, palette.GetColor(i));
|
||||||
|
colors.push_back(pal_color.rgb());
|
||||||
}
|
}
|
||||||
|
|
||||||
SDL_UnlockSurface(surface_.get());
|
SDL_UnlockSurface(surface_.get());
|
||||||
@@ -385,6 +407,7 @@ void Bitmap::ApplyPaletteWithTransparent(const SnesPalette &palette, int index,
|
|||||||
i++;
|
i++;
|
||||||
}
|
}
|
||||||
SDL_LockSurface(surface_.get());
|
SDL_LockSurface(surface_.get());
|
||||||
|
return absl::OkStatus();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Bitmap::ApplyPalette(const std::vector<SDL_Color> &palette) {
|
void Bitmap::ApplyPalette(const std::vector<SDL_Color> &palette) {
|
||||||
|
|||||||
@@ -15,11 +15,32 @@
|
|||||||
|
|
||||||
namespace yaze {
|
namespace yaze {
|
||||||
namespace app {
|
namespace app {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @namespace yaze::app::gfx
|
||||||
|
* @brief Contains classes for handling graphical data.
|
||||||
|
*/
|
||||||
namespace gfx {
|
namespace gfx {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Convert SDL_Surface to PNG image data.
|
||||||
|
*/
|
||||||
bool ConvertSurfaceToPNG(SDL_Surface *surface, std::vector<uint8_t> &buffer);
|
bool ConvertSurfaceToPNG(SDL_Surface *surface, std::vector<uint8_t> &buffer);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Convert PNG image data to SDL_Surface.
|
||||||
|
*/
|
||||||
void ConvertPngToSurface(const std::vector<uint8_t> &png_data,
|
void ConvertPngToSurface(const std::vector<uint8_t> &png_data,
|
||||||
SDL_Surface **outSurface);
|
SDL_Surface **outSurface);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Represents a bitmap image.
|
||||||
|
*
|
||||||
|
* The `Bitmap` class provides functionality to create, manipulate, and display
|
||||||
|
* bitmap images. It supports various operations such as creating a bitmap
|
||||||
|
* object, creating and updating textures, applying palettes, and accessing
|
||||||
|
* pixel data.
|
||||||
|
*/
|
||||||
class Bitmap {
|
class Bitmap {
|
||||||
public:
|
public:
|
||||||
Bitmap() = default;
|
Bitmap() = default;
|
||||||
@@ -29,14 +50,41 @@ class Bitmap {
|
|||||||
: width_(width), height_(height), depth_(depth), data_(data) {
|
: width_(width), height_(height), depth_(depth), data_(data) {
|
||||||
InitializeFromData(width, height, depth, data);
|
InitializeFromData(width, height, depth, data);
|
||||||
}
|
}
|
||||||
|
Bitmap(int width, int height, int depth, const Bytes &data,
|
||||||
|
const SnesPalette &palette)
|
||||||
|
: width_(width),
|
||||||
|
height_(height),
|
||||||
|
depth_(depth),
|
||||||
|
data_(data),
|
||||||
|
palette_(palette) {
|
||||||
|
InitializeFromData(width, height, depth, data);
|
||||||
|
ApplyPalette(palette);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Creates a bitmap object and reserves space for graphical data.
|
||||||
|
*/
|
||||||
void Create(int width, int height, int depth, int data_size);
|
void Create(int width, int height, int depth, int data_size);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Creates a bitmap object with the provided graphical data.
|
||||||
|
*/
|
||||||
void Create(int width, int height, int depth, const Bytes &data);
|
void Create(int width, int height, int depth, const Bytes &data);
|
||||||
|
|
||||||
void InitializeFromData(uint32_t width, uint32_t height, uint32_t depth,
|
void InitializeFromData(uint32_t width, uint32_t height, uint32_t depth,
|
||||||
const Bytes &data);
|
const Bytes &data);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Creates the underlying SDL_Texture to be displayed.
|
||||||
|
*
|
||||||
|
* Converts the surface from a RGB to ARGB format.
|
||||||
|
* Uses SDL_TEXTUREACCESS_STREAMING to allow for live updates.
|
||||||
|
*/
|
||||||
void CreateTexture(std::shared_ptr<SDL_Renderer> renderer);
|
void CreateTexture(std::shared_ptr<SDL_Renderer> renderer);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Updates the underlying SDL_Texture when it already exists.
|
||||||
|
*/
|
||||||
void UpdateTexture(std::shared_ptr<SDL_Renderer> renderer);
|
void UpdateTexture(std::shared_ptr<SDL_Renderer> renderer);
|
||||||
void CreateTexture(SDL_Renderer *renderer);
|
void CreateTexture(SDL_Renderer *renderer);
|
||||||
void UpdateTexture(SDL_Renderer *renderer, bool use_sdl_update = false);
|
void UpdateTexture(SDL_Renderer *renderer, bool use_sdl_update = false);
|
||||||
@@ -47,11 +95,15 @@ class Bitmap {
|
|||||||
void LoadFromPngData(const std::vector<uint8_t> &png_data, int width,
|
void LoadFromPngData(const std::vector<uint8_t> &png_data, int width,
|
||||||
int height);
|
int height);
|
||||||
|
|
||||||
void ApplyPalette(const SnesPalette &palette);
|
/**
|
||||||
void ApplyPaletteWithTransparent(const SnesPalette &palette, int index,
|
* @brief Copy color data from the SnesPalette into the SDL_Palette
|
||||||
int length = 7);
|
*/
|
||||||
|
absl::Status ApplyPalette(const SnesPalette &palette);
|
||||||
|
absl::Status ApplyPaletteWithTransparent(const SnesPalette &palette,
|
||||||
|
int index, int length = 7);
|
||||||
void ApplyPalette(const std::vector<SDL_Color> &palette);
|
void ApplyPalette(const std::vector<SDL_Color> &palette);
|
||||||
void ApplyPaletteFromPaletteGroup(const SnesPalette &palette, int palette_id);
|
absl::Status ApplyPaletteFromPaletteGroup(const SnesPalette &palette,
|
||||||
|
int palette_id);
|
||||||
|
|
||||||
void WriteToPixel(int position, uchar value) {
|
void WriteToPixel(int position, uchar value) {
|
||||||
if (pixel_data_ == nullptr) {
|
if (pixel_data_ == nullptr) {
|
||||||
@@ -236,6 +288,9 @@ class Bitmap {
|
|||||||
|
|
||||||
using BitmapTable = std::unordered_map<int, gfx::Bitmap>;
|
using BitmapTable = std::unordered_map<int, gfx::Bitmap>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Hash map container of shared pointers to Bitmaps.
|
||||||
|
*/
|
||||||
class BitmapManager {
|
class BitmapManager {
|
||||||
private:
|
private:
|
||||||
std::unordered_map<int, std::shared_ptr<gfx::Bitmap>> bitmap_cache_;
|
std::unordered_map<int, std::shared_ptr<gfx::Bitmap>> bitmap_cache_;
|
||||||
|
|||||||
@@ -474,7 +474,7 @@ Bytes CreateCompressionString(CompressionPiecePointer& start, int mode) {
|
|||||||
absl::Status ValidateCompressionResult(CompressionPiecePointer& chain_head,
|
absl::Status ValidateCompressionResult(CompressionPiecePointer& chain_head,
|
||||||
int mode, int start, int src_data_pos) {
|
int mode, int start, int src_data_pos) {
|
||||||
if (chain_head->next != nullptr) {
|
if (chain_head->next != nullptr) {
|
||||||
ROM temp_rom;
|
Rom temp_rom;
|
||||||
RETURN_IF_ERROR(
|
RETURN_IF_ERROR(
|
||||||
temp_rom.LoadFromBytes(CreateCompressionString(chain_head->next, mode)))
|
temp_rom.LoadFromBytes(CreateCompressionString(chain_head->next, mode)))
|
||||||
ASSIGN_OR_RETURN(auto decomp_data,
|
ASSIGN_OR_RETURN(auto decomp_data,
|
||||||
@@ -1173,7 +1173,7 @@ void AddCompressionToChain(CompressionContext& context) {
|
|||||||
|
|
||||||
absl::Status ValidateCompressionResultV3(const CompressionContext& context) {
|
absl::Status ValidateCompressionResultV3(const CompressionContext& context) {
|
||||||
if (!context.compressed_data.empty()) {
|
if (!context.compressed_data.empty()) {
|
||||||
ROM temp_rom;
|
Rom temp_rom;
|
||||||
RETURN_IF_ERROR(temp_rom.LoadFromBytes(context.compressed_data));
|
RETURN_IF_ERROR(temp_rom.LoadFromBytes(context.compressed_data));
|
||||||
ASSIGN_OR_RETURN(auto decomp_data,
|
ASSIGN_OR_RETURN(auto decomp_data,
|
||||||
DecompressV2(temp_rom.data(), 0, temp_rom.size()))
|
DecompressV2(temp_rom.data(), 0, temp_rom.size()))
|
||||||
|
|||||||
@@ -15,6 +15,12 @@ namespace yaze {
|
|||||||
namespace app {
|
namespace app {
|
||||||
namespace gfx {
|
namespace gfx {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @namespace yaze::app::gfx::lc_lz2
|
||||||
|
* @brief Contains the LC_LZ2 compression algorithm.
|
||||||
|
*/
|
||||||
|
namespace lc_lz2 {
|
||||||
|
|
||||||
const int D_NINTENDO_C_MODE1 = 0;
|
const int D_NINTENDO_C_MODE1 = 0;
|
||||||
const int D_NINTENDO_C_MODE2 = 1;
|
const int D_NINTENDO_C_MODE2 = 1;
|
||||||
|
|
||||||
@@ -29,15 +35,6 @@ const int D_MAX_LENGTH = 1024;
|
|||||||
|
|
||||||
const int INITIAL_ALLOC_SIZE = 1024;
|
const int INITIAL_ALLOC_SIZE = 1024;
|
||||||
|
|
||||||
namespace lc_lz2 {
|
|
||||||
|
|
||||||
absl::StatusOr<Bytes> ZS_Compress(const std::vector<uint8_t>& data,
|
|
||||||
const int start, const int length,
|
|
||||||
int mode = 1, bool check = false);
|
|
||||||
|
|
||||||
absl::StatusOr<Bytes> ZS_CompressOverworld(const std::vector<uint8_t> data,
|
|
||||||
const int pos, const int length);
|
|
||||||
|
|
||||||
constexpr int kCommandDirectCopy = 0;
|
constexpr int kCommandDirectCopy = 0;
|
||||||
constexpr int kCommandByteFill = 1;
|
constexpr int kCommandByteFill = 1;
|
||||||
constexpr int kCommandWordFill = 2;
|
constexpr int kCommandWordFill = 2;
|
||||||
@@ -141,6 +138,10 @@ void CompressionCommandAlternativeV2(const uchar* data,
|
|||||||
uint& src_pos, uint& comp_accumulator,
|
uint& src_pos, uint& comp_accumulator,
|
||||||
uint& cmd_with_max, uint& max_win);
|
uint& cmd_with_max, uint& max_win);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Compresses a buffer of data using the LC_LZ2 algorithm.
|
||||||
|
* \deprecated Use Compress and Uncompress instead.
|
||||||
|
*/
|
||||||
absl::StatusOr<Bytes> CompressV2(const uchar* data, const int start,
|
absl::StatusOr<Bytes> CompressV2(const uchar* data, const int start,
|
||||||
const int length, int mode = 1,
|
const int length, int mode = 1,
|
||||||
bool check = false);
|
bool check = false);
|
||||||
@@ -208,6 +209,10 @@ absl::StatusOr<CompressionPiece> SplitCompressionPieceV3(
|
|||||||
CompressionPiece& piece, int mode);
|
CompressionPiece& piece, int mode);
|
||||||
void FinalizeCompression(CompressionContext& context);
|
void FinalizeCompression(CompressionContext& context);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Compresses a buffer of data using the LC_LZ2 algorithm.
|
||||||
|
* \deprecated Use Compress and Uncompress instead.
|
||||||
|
*/
|
||||||
absl::StatusOr<Bytes> CompressV3(const std::vector<uint8_t>& data,
|
absl::StatusOr<Bytes> CompressV3(const std::vector<uint8_t>& data,
|
||||||
const int start, const int length,
|
const int start, const int length,
|
||||||
int mode = 1, bool check = false);
|
int mode = 1, bool check = false);
|
||||||
@@ -227,6 +232,10 @@ std::string SetBuffer(const uchar* data, int src_pos, int comp_accumulator);
|
|||||||
void memfill(const uchar* data, Bytes& buffer, int buffer_pos, int offset,
|
void memfill(const uchar* data, Bytes& buffer, int buffer_pos, int offset,
|
||||||
int length);
|
int length);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Decompresses a buffer of data using the LC_LZ2 algorithm.
|
||||||
|
* \deprecated Use Compress and Uncompress instead.
|
||||||
|
*/
|
||||||
absl::StatusOr<Bytes> DecompressV2(const uchar* data, int offset,
|
absl::StatusOr<Bytes> DecompressV2(const uchar* data, int offset,
|
||||||
int size = 0x800, int mode = 1);
|
int size = 0x800, int mode = 1);
|
||||||
absl::StatusOr<Bytes> DecompressGraphics(const uchar* data, int pos, int size);
|
absl::StatusOr<Bytes> DecompressGraphics(const uchar* data, int pos, int size);
|
||||||
|
|||||||
@@ -15,6 +15,7 @@
|
|||||||
namespace yaze {
|
namespace yaze {
|
||||||
namespace app {
|
namespace app {
|
||||||
namespace gfx {
|
namespace gfx {
|
||||||
|
namespace scad_format {
|
||||||
|
|
||||||
void FindMetastamp() {
|
void FindMetastamp() {
|
||||||
int matching_position = -1;
|
int matching_position = -1;
|
||||||
@@ -276,6 +277,7 @@ absl::Status DecodeObjFile(
|
|||||||
return absl::OkStatus();
|
return absl::OkStatus();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
} // namespace scad_format
|
||||||
} // namespace gfx
|
} // namespace gfx
|
||||||
} // namespace app
|
} // namespace app
|
||||||
} // namespace yaze
|
} // namespace yaze
|
||||||
|
|||||||
@@ -22,18 +22,27 @@ namespace yaze {
|
|||||||
namespace app {
|
namespace app {
|
||||||
namespace gfx {
|
namespace gfx {
|
||||||
|
|
||||||
// キャラクタ(.SCH)ファイル
|
/**
|
||||||
// ヘッダー情報
|
* @namespace yaze::app::gfx::scad_format
|
||||||
// アドレス 説明
|
* @brief Loading from prototype SCAD format
|
||||||
// 00000 - 00003 ファイルタイプ "SCH"
|
*/
|
||||||
// 00004 - 00008 ビットモード "?BIT"
|
namespace scad_format {
|
||||||
// 00009 - 00013 バージョンナンバー "Ver-????\n"
|
|
||||||
// 00014 - 00017 ヘッダーサイズ
|
/**
|
||||||
// 00018 - 0001B ハード名 "SFC" or "CGB" or "GB"
|
* @brief Cgx file header
|
||||||
// 0001C - 0001C BG/OBJフラグ(AGBの時)
|
* キャラクタ(.SCH)ファイル
|
||||||
// 0001D - 0001D Color Pallette Number
|
* ヘッダー情報
|
||||||
// 0001D - 000FF 予約
|
* アドレス 説明
|
||||||
// 00100 - 001FF Color Path
|
* 00000 - 00003 ファイルタイプ "SCH"
|
||||||
|
* 00004 - 00008 ビットモード "?BIT"
|
||||||
|
* 00009 - 00013 バージョンナンバー "Ver-????\n"
|
||||||
|
* 00014 - 00017 ヘッダーサイズ
|
||||||
|
* 00018 - 0001B ハード名 "SFC" or "CGB" or "GB"
|
||||||
|
* 0001C - 0001C BG/OBJフラグ(AGBの時)
|
||||||
|
* 0001D - 0001D Color Pallette Number
|
||||||
|
* 0001D - 000FF 予約
|
||||||
|
* 00100 - 001FF Color Path
|
||||||
|
*/
|
||||||
struct CgxHeader {
|
struct CgxHeader {
|
||||||
char file_type[4];
|
char file_type[4];
|
||||||
char bit_mode[5];
|
char bit_mode[5];
|
||||||
@@ -49,28 +58,47 @@ struct CgxHeader {
|
|||||||
constexpr uint16_t kMatchedBytes[] = {0x4E, 0x41, 0x4B, 0x31, 0x39, 0x38, 0x39};
|
constexpr uint16_t kMatchedBytes[] = {0x4E, 0x41, 0x4B, 0x31, 0x39, 0x38, 0x39};
|
||||||
constexpr uint16_t kOffsetFromMatchedBytesEnd = 0x1D;
|
constexpr uint16_t kOffsetFromMatchedBytesEnd = 0x1D;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Find metastamp in CGX file
|
||||||
|
*/
|
||||||
void FindMetastamp();
|
void FindMetastamp();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Load Scr file (screen data)
|
||||||
|
*/
|
||||||
absl::Status LoadScr(std::string_view filename, uint8_t input_value,
|
absl::Status LoadScr(std::string_view filename, uint8_t input_value,
|
||||||
std::vector<uint8_t>& map_data);
|
std::vector<uint8_t>& map_data);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Load Cgx file (graphical content)
|
||||||
|
*/
|
||||||
absl::Status LoadCgx(uint8_t bpp, std::string_view filename,
|
absl::Status LoadCgx(uint8_t bpp, std::string_view filename,
|
||||||
std::vector<uint8_t>& cgx_data,
|
std::vector<uint8_t>& cgx_data,
|
||||||
std::vector<uint8_t>& cgx_loaded,
|
std::vector<uint8_t>& cgx_loaded,
|
||||||
std::vector<uint8_t>& cgx_header);
|
std::vector<uint8_t>& cgx_header);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Draw screen tilemap with graphical data
|
||||||
|
*/
|
||||||
absl::Status DrawScrWithCgx(uint8_t bpp, std::vector<uint8_t>& map_bitmap_data,
|
absl::Status DrawScrWithCgx(uint8_t bpp, std::vector<uint8_t>& map_bitmap_data,
|
||||||
std::vector<uint8_t>& map_data,
|
std::vector<uint8_t>& map_data,
|
||||||
std::vector<uint8_t>& cgx_loaded);
|
std::vector<uint8_t>& cgx_loaded);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Decode color file
|
||||||
|
*/
|
||||||
std::vector<SDL_Color> DecodeColFile(const std::string_view filename);
|
std::vector<SDL_Color> DecodeColFile(const std::string_view filename);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Decode obj file
|
||||||
|
*/
|
||||||
absl::Status DecodeObjFile(
|
absl::Status DecodeObjFile(
|
||||||
std::string_view filename, std::vector<uint8_t>& obj_data,
|
std::string_view filename, std::vector<uint8_t>& obj_data,
|
||||||
std::vector<uint8_t> actual_obj_data,
|
std::vector<uint8_t> actual_obj_data,
|
||||||
std::unordered_map<std::string, std::vector<uint8_t>> decoded_obj,
|
std::unordered_map<std::string, std::vector<uint8_t>> decoded_obj,
|
||||||
std::vector<uint8_t>& decoded_extra_obj, int& obj_loaded);
|
std::vector<uint8_t>& decoded_extra_obj, int& obj_loaded);
|
||||||
|
|
||||||
|
} // namespace scad_format
|
||||||
} // namespace gfx
|
} // namespace gfx
|
||||||
} // namespace app
|
} // namespace app
|
||||||
} // namespace yaze
|
} // namespace yaze
|
||||||
|
|||||||
@@ -10,6 +10,9 @@ namespace yaze {
|
|||||||
namespace app {
|
namespace app {
|
||||||
namespace gfx {
|
namespace gfx {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Primitive of 16-bit RGB SNES color.
|
||||||
|
*/
|
||||||
struct snes_color {
|
struct snes_color {
|
||||||
uint16_t red; /**< Red component of the color. */
|
uint16_t red; /**< Red component of the color. */
|
||||||
uint16_t blue; /**< Blue component of the color. */
|
uint16_t blue; /**< Blue component of the color. */
|
||||||
@@ -26,6 +29,16 @@ std::vector<snes_color> Extract(const char* data, unsigned int offset,
|
|||||||
|
|
||||||
std::vector<char> Convert(const std::vector<snes_color>& palette);
|
std::vector<char> Convert(const std::vector<snes_color>& palette);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief SNES Color container
|
||||||
|
*
|
||||||
|
* Used for displaying the color to the screen and writing
|
||||||
|
* the color to the Rom file in the correct format.
|
||||||
|
*
|
||||||
|
* SNES colors may be represented in one of three formats:
|
||||||
|
* - Color data from the rom in a snes_color struct
|
||||||
|
* - Color data for displaying to the UI via ImVec4
|
||||||
|
*/
|
||||||
class SnesColor {
|
class SnesColor {
|
||||||
public:
|
public:
|
||||||
SnesColor() : rgb_(0.f, 0.f, 0.f, 0.f), snes_(0) {}
|
SnesColor() : rgb_(0.f, 0.f, 0.f, 0.f), snes_(0) {}
|
||||||
@@ -53,6 +66,7 @@ class SnesColor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
ImVec4 rgb() const { return rgb_; }
|
ImVec4 rgb() const { return rgb_; }
|
||||||
|
|
||||||
void set_rgb(const ImVec4 val) {
|
void set_rgb(const ImVec4 val) {
|
||||||
rgb_.x = val.x / 255;
|
rgb_.x = val.x / 255;
|
||||||
rgb_.y = val.y / 255;
|
rgb_.y = val.y / 255;
|
||||||
@@ -65,6 +79,7 @@ class SnesColor {
|
|||||||
snes_ = ConvertRGBtoSNES(color);
|
snes_ = ConvertRGBtoSNES(color);
|
||||||
modified = true;
|
modified = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void set_snes(uint16_t val) {
|
void set_snes(uint16_t val) {
|
||||||
snes_ = val;
|
snes_ = val;
|
||||||
snes_color col = ConvertSNEStoRGB(val);
|
snes_color col = ConvertSNEStoRGB(val);
|
||||||
|
|||||||
@@ -20,6 +20,169 @@ namespace yaze {
|
|||||||
namespace app {
|
namespace app {
|
||||||
namespace gfx {
|
namespace gfx {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @namespace yaze::app::gfx::palette_group_internal
|
||||||
|
* @brief Internal functions for loading palettes by group.
|
||||||
|
*/
|
||||||
|
namespace palette_group_internal {
|
||||||
|
absl::Status LoadOverworldMainPalettes(const Bytes& rom_data,
|
||||||
|
gfx::PaletteGroupMap& palette_groups) {
|
||||||
|
auto data = rom_data.data();
|
||||||
|
for (int i = 0; i < 6; i++) {
|
||||||
|
RETURN_IF_ERROR(palette_groups.overworld_main.AddPalette(
|
||||||
|
gfx::ReadPaletteFromRom(core::overworldPaletteMain + (i * (35 * 2)),
|
||||||
|
/*num_colors*/ 35, data)))
|
||||||
|
}
|
||||||
|
return absl::OkStatus();
|
||||||
|
}
|
||||||
|
|
||||||
|
absl::Status LoadOverworldAuxiliaryPalettes(
|
||||||
|
const Bytes& rom_data, gfx::PaletteGroupMap& palette_groups) {
|
||||||
|
auto data = rom_data.data();
|
||||||
|
for (int i = 0; i < 20; i++) {
|
||||||
|
RETURN_IF_ERROR(
|
||||||
|
palette_groups.overworld_aux.AddPalette(gfx::ReadPaletteFromRom(
|
||||||
|
core::overworldPaletteAuxialiary + (i * (21 * 2)),
|
||||||
|
/*num_colors*/ 21, data)))
|
||||||
|
}
|
||||||
|
return absl::OkStatus();
|
||||||
|
}
|
||||||
|
|
||||||
|
absl::Status LoadOverworldAnimatedPalettes(
|
||||||
|
const Bytes& rom_data, gfx::PaletteGroupMap& palette_groups) {
|
||||||
|
auto data = rom_data.data();
|
||||||
|
for (int i = 0; i < 14; i++) {
|
||||||
|
RETURN_IF_ERROR(
|
||||||
|
palette_groups.overworld_animated.AddPalette(gfx::ReadPaletteFromRom(
|
||||||
|
core::overworldPaletteAnimated + (i * (7 * 2)), 7, data)))
|
||||||
|
}
|
||||||
|
return absl::OkStatus();
|
||||||
|
}
|
||||||
|
|
||||||
|
absl::Status LoadHUDPalettes(const Bytes& rom_data,
|
||||||
|
gfx::PaletteGroupMap& palette_groups) {
|
||||||
|
auto data = rom_data.data();
|
||||||
|
for (int i = 0; i < 2; i++) {
|
||||||
|
RETURN_IF_ERROR(palette_groups.hud.AddPalette(
|
||||||
|
gfx::ReadPaletteFromRom(core::hudPalettes + (i * 64), 32, data)))
|
||||||
|
}
|
||||||
|
return absl::OkStatus();
|
||||||
|
}
|
||||||
|
|
||||||
|
absl::Status LoadGlobalSpritePalettes(const Bytes& rom_data,
|
||||||
|
gfx::PaletteGroupMap& palette_groups) {
|
||||||
|
auto data = rom_data.data();
|
||||||
|
RETURN_IF_ERROR(palette_groups.global_sprites.AddPalette(
|
||||||
|
gfx::ReadPaletteFromRom(core::globalSpritePalettesLW, 60, data)))
|
||||||
|
RETURN_IF_ERROR(palette_groups.global_sprites.AddPalette(
|
||||||
|
gfx::ReadPaletteFromRom(core::globalSpritePalettesDW, 60, data)))
|
||||||
|
return absl::OkStatus();
|
||||||
|
}
|
||||||
|
|
||||||
|
absl::Status LoadArmorPalettes(const Bytes& rom_data,
|
||||||
|
gfx::PaletteGroupMap& palette_groups) {
|
||||||
|
auto data = rom_data.data();
|
||||||
|
for (int i = 0; i < 5; i++) {
|
||||||
|
RETURN_IF_ERROR(palette_groups.armors.AddPalette(
|
||||||
|
gfx::ReadPaletteFromRom(core::armorPalettes + (i * 30), 15, data)))
|
||||||
|
}
|
||||||
|
return absl::OkStatus();
|
||||||
|
}
|
||||||
|
|
||||||
|
absl::Status LoadSwordPalettes(const Bytes& rom_data,
|
||||||
|
gfx::PaletteGroupMap& palette_groups) {
|
||||||
|
auto data = rom_data.data();
|
||||||
|
for (int i = 0; i < 4; i++) {
|
||||||
|
RETURN_IF_ERROR(palette_groups.swords.AddPalette(
|
||||||
|
gfx::ReadPaletteFromRom(core::swordPalettes + (i * 6), 3, data)))
|
||||||
|
}
|
||||||
|
return absl::OkStatus();
|
||||||
|
}
|
||||||
|
|
||||||
|
absl::Status LoadShieldPalettes(const Bytes& rom_data,
|
||||||
|
gfx::PaletteGroupMap& palette_groups) {
|
||||||
|
auto data = rom_data.data();
|
||||||
|
for (int i = 0; i < 3; i++) {
|
||||||
|
RETURN_IF_ERROR(palette_groups.shields.AddPalette(
|
||||||
|
gfx::ReadPaletteFromRom(core::shieldPalettes + (i * 8), 4, data)))
|
||||||
|
}
|
||||||
|
return absl::OkStatus();
|
||||||
|
}
|
||||||
|
|
||||||
|
absl::Status LoadSpriteAux1Palettes(const Bytes& rom_data,
|
||||||
|
gfx::PaletteGroupMap& palette_groups) {
|
||||||
|
auto data = rom_data.data();
|
||||||
|
for (int i = 0; i < 12; i++) {
|
||||||
|
RETURN_IF_ERROR(palette_groups.sprites_aux1.AddPalette(
|
||||||
|
gfx::ReadPaletteFromRom(core::spritePalettesAux1 + (i * 14), 7, data)))
|
||||||
|
}
|
||||||
|
return absl::OkStatus();
|
||||||
|
}
|
||||||
|
|
||||||
|
absl::Status LoadSpriteAux2Palettes(const Bytes& rom_data,
|
||||||
|
gfx::PaletteGroupMap& palette_groups) {
|
||||||
|
auto data = rom_data.data();
|
||||||
|
for (int i = 0; i < 11; i++) {
|
||||||
|
RETURN_IF_ERROR(palette_groups.sprites_aux2.AddPalette(
|
||||||
|
gfx::ReadPaletteFromRom(core::spritePalettesAux2 + (i * 14), 7, data)))
|
||||||
|
}
|
||||||
|
return absl::OkStatus();
|
||||||
|
}
|
||||||
|
|
||||||
|
absl::Status LoadSpriteAux3Palettes(const Bytes& rom_data,
|
||||||
|
gfx::PaletteGroupMap& palette_groups) {
|
||||||
|
auto data = rom_data.data();
|
||||||
|
for (int i = 0; i < 24; i++) {
|
||||||
|
RETURN_IF_ERROR(palette_groups.sprites_aux3.AddPalette(
|
||||||
|
gfx::ReadPaletteFromRom(core::spritePalettesAux3 + (i * 14), 7, data)))
|
||||||
|
}
|
||||||
|
return absl::OkStatus();
|
||||||
|
}
|
||||||
|
|
||||||
|
absl::Status LoadDungeonMainPalettes(const Bytes& rom_data,
|
||||||
|
gfx::PaletteGroupMap& palette_groups) {
|
||||||
|
auto data = rom_data.data();
|
||||||
|
for (int i = 0; i < 20; i++) {
|
||||||
|
RETURN_IF_ERROR(
|
||||||
|
palette_groups.dungeon_main.AddPalette(gfx::ReadPaletteFromRom(
|
||||||
|
core::dungeonMainPalettes + (i * 180), 90, data)))
|
||||||
|
}
|
||||||
|
return absl::OkStatus();
|
||||||
|
}
|
||||||
|
|
||||||
|
absl::Status LoadGrassColors(const Bytes& rom_data,
|
||||||
|
gfx::PaletteGroupMap& palette_groups) {
|
||||||
|
RETURN_IF_ERROR(palette_groups.grass.AddColor(
|
||||||
|
gfx::ReadColorFromRom(core::hardcodedGrassLW, rom_data.data())))
|
||||||
|
RETURN_IF_ERROR(palette_groups.grass.AddColor(
|
||||||
|
gfx::ReadColorFromRom(core::hardcodedGrassDW, rom_data.data())))
|
||||||
|
RETURN_IF_ERROR(palette_groups.grass.AddColor(
|
||||||
|
gfx::ReadColorFromRom(core::hardcodedGrassSpecial, rom_data.data())))
|
||||||
|
return absl::OkStatus();
|
||||||
|
}
|
||||||
|
|
||||||
|
absl::Status Load3DObjectPalettes(const Bytes& rom_data,
|
||||||
|
gfx::PaletteGroupMap& palette_groups) {
|
||||||
|
auto data = rom_data.data();
|
||||||
|
RETURN_IF_ERROR(palette_groups.object_3d.AddPalette(
|
||||||
|
gfx::ReadPaletteFromRom(core::triforcePalette, 8, data)))
|
||||||
|
RETURN_IF_ERROR(palette_groups.object_3d.AddPalette(
|
||||||
|
gfx::ReadPaletteFromRom(core::crystalPalette, 8, data)))
|
||||||
|
return absl::OkStatus();
|
||||||
|
}
|
||||||
|
|
||||||
|
absl::Status LoadOverworldMiniMapPalettes(
|
||||||
|
const Bytes& rom_data, gfx::PaletteGroupMap& palette_groups) {
|
||||||
|
auto data = rom_data.data();
|
||||||
|
for (int i = 0; i < 2; i++) {
|
||||||
|
RETURN_IF_ERROR(
|
||||||
|
palette_groups.overworld_mini_map.AddPalette(gfx::ReadPaletteFromRom(
|
||||||
|
core::overworldMiniMapPalettes + (i * 256), 128, data)))
|
||||||
|
}
|
||||||
|
return absl::OkStatus();
|
||||||
|
}
|
||||||
|
} // namespace palette_group_internal
|
||||||
|
|
||||||
const absl::flat_hash_map<std::string, uint32_t> kPaletteGroupAddressMap = {
|
const absl::flat_hash_map<std::string, uint32_t> kPaletteGroupAddressMap = {
|
||||||
{"ow_main", core::overworldPaletteMain},
|
{"ow_main", core::overworldPaletteMain},
|
||||||
{"ow_aux", core::overworldPaletteAuxialiary},
|
{"ow_aux", core::overworldPaletteAuxialiary},
|
||||||
@@ -194,6 +357,7 @@ absl::StatusOr<PaletteGroup> CreatePaletteGroupFromLargePalette(
|
|||||||
for (int i = 0; i < palette.size(); i += 8) {
|
for (int i = 0; i < palette.size(); i += 8) {
|
||||||
SnesPalette new_palette;
|
SnesPalette new_palette;
|
||||||
if (i + 8 < palette.size()) {
|
if (i + 8 < palette.size()) {
|
||||||
|
// new_palette.AddColor(SnesColor(ImVec4(0,0,0,0)));
|
||||||
for (int j = 0; j < 8; j++) {
|
for (int j = 0; j < 8; j++) {
|
||||||
new_palette.AddColor(palette[i + j]);
|
new_palette.AddColor(palette[i + j]);
|
||||||
}
|
}
|
||||||
@@ -204,6 +368,27 @@ absl::StatusOr<PaletteGroup> CreatePaletteGroupFromLargePalette(
|
|||||||
return toret;
|
return toret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
using namespace palette_group_internal;
|
||||||
|
|
||||||
|
absl::Status LoadAllPalettes(const Bytes& rom_data, PaletteGroupMap& groups) {
|
||||||
|
RETURN_IF_ERROR(LoadOverworldMainPalettes(rom_data, groups))
|
||||||
|
RETURN_IF_ERROR(LoadOverworldAuxiliaryPalettes(rom_data, groups))
|
||||||
|
RETURN_IF_ERROR(LoadOverworldAnimatedPalettes(rom_data, groups))
|
||||||
|
RETURN_IF_ERROR(LoadHUDPalettes(rom_data, groups))
|
||||||
|
RETURN_IF_ERROR(LoadGlobalSpritePalettes(rom_data, groups))
|
||||||
|
RETURN_IF_ERROR(LoadArmorPalettes(rom_data, groups))
|
||||||
|
RETURN_IF_ERROR(LoadSwordPalettes(rom_data, groups))
|
||||||
|
RETURN_IF_ERROR(LoadShieldPalettes(rom_data, groups))
|
||||||
|
RETURN_IF_ERROR(LoadSpriteAux1Palettes(rom_data, groups))
|
||||||
|
RETURN_IF_ERROR(LoadSpriteAux2Palettes(rom_data, groups))
|
||||||
|
RETURN_IF_ERROR(LoadSpriteAux3Palettes(rom_data, groups))
|
||||||
|
RETURN_IF_ERROR(LoadDungeonMainPalettes(rom_data, groups))
|
||||||
|
RETURN_IF_ERROR(LoadGrassColors(rom_data, groups))
|
||||||
|
RETURN_IF_ERROR(Load3DObjectPalettes(rom_data, groups))
|
||||||
|
RETURN_IF_ERROR(LoadOverworldMiniMapPalettes(rom_data, groups))
|
||||||
|
return absl::OkStatus();
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace gfx
|
} // namespace gfx
|
||||||
} // namespace app
|
} // namespace app
|
||||||
} // namespace yaze
|
} // namespace yaze
|
||||||
@@ -21,6 +21,9 @@ namespace yaze {
|
|||||||
namespace app {
|
namespace app {
|
||||||
namespace gfx {
|
namespace gfx {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Primitive of a SNES color palette.
|
||||||
|
*/
|
||||||
struct snes_palette {
|
struct snes_palette {
|
||||||
uint id; /**< ID of the palette. */
|
uint id; /**< ID of the palette. */
|
||||||
uint size; /**< Size of the palette. */
|
uint size; /**< Size of the palette. */
|
||||||
@@ -31,6 +34,18 @@ using snes_palette = struct snes_palette;
|
|||||||
uint32_t GetPaletteAddress(const std::string& group_name, size_t palette_index,
|
uint32_t GetPaletteAddress(const std::string& group_name, size_t palette_index,
|
||||||
size_t color_index);
|
size_t color_index);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Represents a palette of colors for the Super Nintendo Entertainment
|
||||||
|
* System (SNES).
|
||||||
|
*
|
||||||
|
* The `SnesPalette` class provides functionality to create, modify, and access
|
||||||
|
* colors in an SNES palette. It supports various constructors to initialize the
|
||||||
|
* palette with different types of data. The palette can be modified by adding
|
||||||
|
* or changing colors, and it can be cleared to remove all colors. Colors in the
|
||||||
|
* palette can be accessed using index-based access or through the `GetColor`
|
||||||
|
* method. The class also provides a method to create a sub-palette by selecting
|
||||||
|
* a range of colors from the original palette.
|
||||||
|
*/
|
||||||
class SnesPalette {
|
class SnesPalette {
|
||||||
public:
|
public:
|
||||||
template <typename T>
|
template <typename T>
|
||||||
@@ -69,10 +84,9 @@ class SnesPalette {
|
|||||||
size_++;
|
size_++;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto GetColor(int i) const {
|
absl::StatusOr<SnesColor> GetColor(int i) const {
|
||||||
if (i > size_) {
|
if (i > size_) {
|
||||||
std::cout << "SNESPalette: Index out of bounds" << std::endl;
|
return absl::InvalidArgumentError("SnesPalette: Index out of bounds");
|
||||||
return colors[0];
|
|
||||||
}
|
}
|
||||||
return colors[i];
|
return colors[i];
|
||||||
}
|
}
|
||||||
@@ -126,13 +140,17 @@ SnesPalette ReadPaletteFromRom(int offset, int num_colors, const uint8_t* rom);
|
|||||||
|
|
||||||
std::array<float, 4> ToFloatArray(const SnesColor& color);
|
std::array<float, 4> ToFloatArray(const SnesColor& color);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Represents a group of palettes.
|
||||||
|
*
|
||||||
|
* Supports adding palettes and colors, clearing the group, and accessing
|
||||||
|
* palettes and colors by index.
|
||||||
|
*/
|
||||||
struct PaletteGroup {
|
struct PaletteGroup {
|
||||||
PaletteGroup() = default;
|
PaletteGroup() = default;
|
||||||
|
|
||||||
explicit PaletteGroup(uint8_t mSize);
|
explicit PaletteGroup(uint8_t mSize);
|
||||||
|
|
||||||
auto mutable_palette(int i) { return &palettes[i]; }
|
|
||||||
|
|
||||||
absl::Status AddPalette(SnesPalette pal) {
|
absl::Status AddPalette(SnesPalette pal) {
|
||||||
palettes.emplace_back(pal);
|
palettes.emplace_back(pal);
|
||||||
size_ = palettes.size();
|
size_ = palettes.size();
|
||||||
@@ -152,7 +170,10 @@ struct PaletteGroup {
|
|||||||
size_ = 0;
|
size_ = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
auto name() const { return name_; }
|
||||||
auto size() const { return palettes.size(); }
|
auto size() const { return palettes.size(); }
|
||||||
|
auto mutable_palette(int i) { return &palettes[i]; }
|
||||||
|
auto palette(int i) const { return palettes[i]; }
|
||||||
|
|
||||||
SnesPalette operator[](int i) {
|
SnesPalette operator[](int i) {
|
||||||
if (i > size_) {
|
if (i > size_) {
|
||||||
@@ -188,17 +209,128 @@ struct PaletteGroup {
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
int size_ = 0;
|
int size_ = 0;
|
||||||
|
std::string name_;
|
||||||
std::vector<SnesPalette> palettes;
|
std::vector<SnesPalette> palettes;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Represents a mapping of palette groups.
|
||||||
|
*
|
||||||
|
* Originally, this was an actual std::unordered_map but since the palette
|
||||||
|
* groups supported never change, it was changed to a struct with a method to
|
||||||
|
* get the group by name.
|
||||||
|
*/
|
||||||
|
struct PaletteGroupMap {
|
||||||
|
PaletteGroup overworld_main;
|
||||||
|
PaletteGroup overworld_aux;
|
||||||
|
PaletteGroup overworld_animated;
|
||||||
|
PaletteGroup hud;
|
||||||
|
PaletteGroup global_sprites;
|
||||||
|
PaletteGroup armors;
|
||||||
|
PaletteGroup swords;
|
||||||
|
PaletteGroup shields;
|
||||||
|
PaletteGroup sprites_aux1;
|
||||||
|
PaletteGroup sprites_aux2;
|
||||||
|
PaletteGroup sprites_aux3;
|
||||||
|
PaletteGroup dungeon_main;
|
||||||
|
PaletteGroup grass;
|
||||||
|
PaletteGroup object_3d;
|
||||||
|
PaletteGroup overworld_mini_map;
|
||||||
|
|
||||||
|
auto get_group(const std::string& group_name) {
|
||||||
|
if (group_name == "ow_main") {
|
||||||
|
return &overworld_main;
|
||||||
|
} else if (group_name == "ow_aux") {
|
||||||
|
return &overworld_aux;
|
||||||
|
} else if (group_name == "ow_animated") {
|
||||||
|
return &overworld_animated;
|
||||||
|
} else if (group_name == "hud") {
|
||||||
|
return &hud;
|
||||||
|
} else if (group_name == "global_sprites") {
|
||||||
|
return &global_sprites;
|
||||||
|
} else if (group_name == "armors") {
|
||||||
|
return &armors;
|
||||||
|
} else if (group_name == "swords") {
|
||||||
|
return &swords;
|
||||||
|
} else if (group_name == "shields") {
|
||||||
|
return &shields;
|
||||||
|
} else if (group_name == "sprites_aux1") {
|
||||||
|
return &sprites_aux1;
|
||||||
|
} else if (group_name == "sprites_aux2") {
|
||||||
|
return &sprites_aux2;
|
||||||
|
} else if (group_name == "sprites_aux3") {
|
||||||
|
return &sprites_aux3;
|
||||||
|
} else if (group_name == "dungeon_main") {
|
||||||
|
return &dungeon_main;
|
||||||
|
} else if (group_name == "grass") {
|
||||||
|
return &grass;
|
||||||
|
} else if (group_name == "3d_object") {
|
||||||
|
return &object_3d;
|
||||||
|
} else if (group_name == "ow_mini_map") {
|
||||||
|
return &overworld_mini_map;
|
||||||
|
} else {
|
||||||
|
throw std::out_of_range("PaletteGroupMap: Group not found");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Func>
|
||||||
|
void for_each(Func&& func) {
|
||||||
|
func(overworld_main);
|
||||||
|
func(overworld_aux);
|
||||||
|
func(overworld_animated);
|
||||||
|
func(hud);
|
||||||
|
func(global_sprites);
|
||||||
|
func(armors);
|
||||||
|
func(swords);
|
||||||
|
func(shields);
|
||||||
|
func(sprites_aux1);
|
||||||
|
func(sprites_aux2);
|
||||||
|
func(sprites_aux3);
|
||||||
|
func(dungeon_main);
|
||||||
|
func(grass);
|
||||||
|
func(object_3d);
|
||||||
|
func(overworld_mini_map);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
absl::StatusOr<PaletteGroup> CreatePaletteGroupFromColFile(
|
absl::StatusOr<PaletteGroup> CreatePaletteGroupFromColFile(
|
||||||
std::vector<SnesColor>& colors);
|
std::vector<SnesColor>& colors);
|
||||||
|
|
||||||
absl::StatusOr<PaletteGroup> CreatePaletteGroupFromLargePalette(
|
absl::StatusOr<PaletteGroup> CreatePaletteGroupFromLargePalette(
|
||||||
SnesPalette& palette);
|
SnesPalette& palette);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Loads all the palettes for the game.
|
||||||
|
*
|
||||||
|
* This function loads all the palettes for the game, including overworld,
|
||||||
|
* HUD, armor, swords, shields, sprites, dungeon, grass, and 3D object
|
||||||
|
* palettes. It also adds the loaded palettes to their respective palette
|
||||||
|
* groups.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
absl::Status LoadAllPalettes(const Bytes& rom_data, PaletteGroupMap& groups);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Represents a set of palettes used in a SNES graphics system.
|
||||||
|
*/
|
||||||
struct Paletteset {
|
struct Paletteset {
|
||||||
|
/**
|
||||||
|
* @brief Default constructor for Paletteset.
|
||||||
|
*/
|
||||||
Paletteset() = default;
|
Paletteset() = default;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Constructor for Paletteset.
|
||||||
|
* @param main The main palette.
|
||||||
|
* @param animated The animated palette.
|
||||||
|
* @param aux1 The first auxiliary palette.
|
||||||
|
* @param aux2 The second auxiliary palette.
|
||||||
|
* @param background The background color.
|
||||||
|
* @param hud The HUD palette.
|
||||||
|
* @param spr The sprite palette.
|
||||||
|
* @param spr2 The second sprite palette.
|
||||||
|
* @param comp The composite palette.
|
||||||
|
*/
|
||||||
Paletteset(gfx::SnesPalette main, gfx::SnesPalette animated,
|
Paletteset(gfx::SnesPalette main, gfx::SnesPalette animated,
|
||||||
gfx::SnesPalette aux1, gfx::SnesPalette aux2,
|
gfx::SnesPalette aux1, gfx::SnesPalette aux2,
|
||||||
gfx::SnesColor background, gfx::SnesPalette hud,
|
gfx::SnesColor background, gfx::SnesPalette hud,
|
||||||
@@ -212,15 +344,16 @@ struct Paletteset {
|
|||||||
spr(spr),
|
spr(spr),
|
||||||
spr2(spr2),
|
spr2(spr2),
|
||||||
composite(comp) {}
|
composite(comp) {}
|
||||||
gfx::SnesPalette main;
|
|
||||||
gfx::SnesPalette animated;
|
gfx::SnesPalette main; /**< The main palette. */
|
||||||
gfx::SnesPalette aux1;
|
gfx::SnesPalette animated; /**< The animated palette. */
|
||||||
gfx::SnesPalette aux2;
|
gfx::SnesPalette aux1; /**< The first auxiliary palette. */
|
||||||
gfx::SnesColor background;
|
gfx::SnesPalette aux2; /**< The second auxiliary palette. */
|
||||||
gfx::SnesPalette hud;
|
gfx::SnesColor background; /**< The background color. */
|
||||||
gfx::SnesPalette spr;
|
gfx::SnesPalette hud; /**< The HUD palette. */
|
||||||
gfx::SnesPalette spr2;
|
gfx::SnesPalette spr; /**< The sprite palette. */
|
||||||
gfx::SnesPalette composite;
|
gfx::SnesPalette spr2; /**< The second sprite palette. */
|
||||||
|
gfx::SnesPalette composite; /**< The composite palette. */
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace gfx
|
} // namespace gfx
|
||||||
|
|||||||
@@ -135,7 +135,7 @@ std::vector<uint8_t> Convert4bppTo3bpp(const std::vector<uint8_t>& tiles) {
|
|||||||
return ConvertBpp(tiles, 4, 3);
|
return ConvertBpp(tiles, 4, 3);
|
||||||
}
|
}
|
||||||
|
|
||||||
Bytes SnesTo8bppSheet(Bytes sheet, int bpp) {
|
Bytes SnesTo8bppSheet(const Bytes& sheet, int bpp) {
|
||||||
int xx = 0; // positions where we are at on the sheet
|
int xx = 0; // positions where we are at on the sheet
|
||||||
int yy = 0;
|
int yy = 0;
|
||||||
int pos = 0;
|
int pos = 0;
|
||||||
@@ -148,6 +148,11 @@ Bytes SnesTo8bppSheet(Bytes sheet, int bpp) {
|
|||||||
buffer_size = 0x2000;
|
buffer_size = 0x2000;
|
||||||
} else if (bpp == 3) {
|
} else if (bpp == 3) {
|
||||||
bpp = 24;
|
bpp = 24;
|
||||||
|
} else if (bpp == 4) {
|
||||||
|
bpp = 32;
|
||||||
|
buffer_size = 0x4000;
|
||||||
|
} else if (bpp == 8) {
|
||||||
|
bpp = 64;
|
||||||
}
|
}
|
||||||
Bytes sheet_buffer_out(buffer_size);
|
Bytes sheet_buffer_out(buffer_size);
|
||||||
|
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ namespace gfx {
|
|||||||
constexpr uint8_t kGraphicsBitmap[8] = {0x80, 0x40, 0x20, 0x10,
|
constexpr uint8_t kGraphicsBitmap[8] = {0x80, 0x40, 0x20, 0x10,
|
||||||
0x08, 0x04, 0x02, 0x01};
|
0x08, 0x04, 0x02, 0x01};
|
||||||
|
|
||||||
Bytes SnesTo8bppSheet(Bytes sheet, int bpp);
|
Bytes SnesTo8bppSheet(const Bytes& sheet, int bpp);
|
||||||
Bytes Bpp8SnesToIndexed(Bytes data, uint64_t bpp = 0);
|
Bytes Bpp8SnesToIndexed(Bytes data, uint64_t bpp = 0);
|
||||||
|
|
||||||
struct tile8 {
|
struct tile8 {
|
||||||
@@ -35,9 +35,14 @@ std::vector<uint8_t> ConvertBpp(const std::vector<uint8_t>& tiles,
|
|||||||
std::vector<uint8_t> Convert3bppTo4bpp(const std::vector<uint8_t>& tiles);
|
std::vector<uint8_t> Convert3bppTo4bpp(const std::vector<uint8_t>& tiles);
|
||||||
std::vector<uint8_t> Convert4bppTo3bpp(const std::vector<uint8_t>& tiles);
|
std::vector<uint8_t> Convert4bppTo3bpp(const std::vector<uint8_t>& tiles);
|
||||||
|
|
||||||
// vhopppcc cccccccc
|
/**
|
||||||
// [0, 1]
|
* @brief SNES 16-bit tile metadata container
|
||||||
// [2, 3]
|
*
|
||||||
|
* Format:
|
||||||
|
* vhopppcc cccccccc
|
||||||
|
* [0, 1]
|
||||||
|
* [2, 3]
|
||||||
|
*/
|
||||||
class TileInfo {
|
class TileInfo {
|
||||||
public:
|
public:
|
||||||
uint16_t id_;
|
uint16_t id_;
|
||||||
@@ -67,6 +72,9 @@ uint16_t TileInfoToShort(TileInfo tile_info);
|
|||||||
|
|
||||||
TileInfo GetTilesInfo(uint16_t tile);
|
TileInfo GetTilesInfo(uint16_t tile);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Tile composition of four 16x16 tiles.
|
||||||
|
*/
|
||||||
class Tile32 {
|
class Tile32 {
|
||||||
public:
|
public:
|
||||||
uint16_t tile0_;
|
uint16_t tile0_;
|
||||||
@@ -113,6 +121,9 @@ class Tile32 {
|
|||||||
bool operator!=(const Tile32& other) const { return !(*this == other); }
|
bool operator!=(const Tile32& other) const { return !(*this == other); }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Tile composition of four 8x8 tiles.
|
||||||
|
*/
|
||||||
class Tile16 {
|
class Tile16 {
|
||||||
public:
|
public:
|
||||||
TileInfo tile0_;
|
TileInfo tile0_;
|
||||||
@@ -138,7 +149,10 @@ class Tile16 {
|
|||||||
bool operator!=(const Tile16& other) const { return !(*this == other); }
|
bool operator!=(const Tile16& other) const { return !(*this == other); }
|
||||||
};
|
};
|
||||||
|
|
||||||
class OAMTile {
|
/**
|
||||||
|
* @brief Object Attribute Memory tile abstraction container
|
||||||
|
*/
|
||||||
|
class OamTile {
|
||||||
public:
|
public:
|
||||||
int x_;
|
int x_;
|
||||||
int y_;
|
int y_;
|
||||||
@@ -146,8 +160,8 @@ class OAMTile {
|
|||||||
int my_;
|
int my_;
|
||||||
int pal_;
|
int pal_;
|
||||||
uint16_t tile_;
|
uint16_t tile_;
|
||||||
OAMTile() = default;
|
OamTile() = default;
|
||||||
OAMTile(int x, int y, uint16_t tile, int pal, bool upper = false, int mx = 0,
|
OamTile(int x, int y, uint16_t tile, int pal, bool upper = false, int mx = 0,
|
||||||
int my = 0)
|
int my = 0)
|
||||||
: x_(x), y_(y), mx_(mx), my_(my), pal_(pal) {
|
: x_(x), y_(y), mx_(mx), my_(my), pal_(pal) {
|
||||||
if (upper) {
|
if (upper) {
|
||||||
|
|||||||
@@ -0,0 +1,114 @@
|
|||||||
|
#include "app/gfx/tilesheet.h"
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include "app/gfx/bitmap.h"
|
||||||
|
#include "app/gfx/snes_color.h"
|
||||||
|
#include "app/gfx/snes_palette.h"
|
||||||
|
#include "app/gfx/snes_tile.h"
|
||||||
|
|
||||||
|
namespace yaze {
|
||||||
|
namespace app {
|
||||||
|
namespace gfx {
|
||||||
|
|
||||||
|
absl::StatusOr<Tilesheet> CreateTilesheetFromGraphicsBuffer(
|
||||||
|
const uint8_t* graphics_buffer, int width, int height, TileType tile_type,
|
||||||
|
int sheet_id) {
|
||||||
|
Tilesheet tilesheet;
|
||||||
|
|
||||||
|
// Calculate the offset in the graphics buffer based on the sheet ID
|
||||||
|
int sheet_offset = sheet_id * width * height;
|
||||||
|
|
||||||
|
// Initialize the tilesheet with the specified width, height, and tile type
|
||||||
|
tilesheet.Init(width, height, tile_type);
|
||||||
|
|
||||||
|
// Iterate over the tiles in the sheet and copy them into the tilesheet
|
||||||
|
for (int row = 0; row < height; ++row) {
|
||||||
|
for (int col = 0; col < width; ++col) {
|
||||||
|
// Calculate the index of the current tile in the graphics buffer
|
||||||
|
int tile_index = sheet_offset + (row * width + col) * 64;
|
||||||
|
|
||||||
|
// Copy the tile data into the tilesheet
|
||||||
|
for (int y = 0; y < 8; ++y) {
|
||||||
|
for (int x = 0; x < 8; ++x) {
|
||||||
|
int srcIndex = tile_index + (y * 8 + x);
|
||||||
|
int destX = col * 8 + x;
|
||||||
|
int destY = row * 8 + y;
|
||||||
|
int destIndex = (destY * width * 8) + destX;
|
||||||
|
tilesheet.mutable_bitmap()->mutable_data()[destIndex] =
|
||||||
|
graphics_buffer[srcIndex];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return tilesheet;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Tilesheet::Init(int width, int height, TileType tile_type) {
|
||||||
|
bitmap_ = std::make_shared<Bitmap>(width, height, 8, 0x20000);
|
||||||
|
internal_data_.resize(0x20000);
|
||||||
|
tile_type_ = tile_type;
|
||||||
|
if (tile_type_ == TileType::Tile8) {
|
||||||
|
tile_width_ = 8;
|
||||||
|
tile_height_ = 8;
|
||||||
|
} else {
|
||||||
|
tile_width_ = 16;
|
||||||
|
tile_height_ = 16;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Tilesheet::ComposeTile16(const std::vector<uint8_t>& graphics_buffer,
|
||||||
|
const TileInfo& top_left,
|
||||||
|
const TileInfo& top_right,
|
||||||
|
const TileInfo& bottom_left,
|
||||||
|
const TileInfo& bottom_right) {
|
||||||
|
// Calculate the base position for this Tile16 in the full-size bitmap
|
||||||
|
int tiles_per_row = bitmap_->width() / tile_width_;
|
||||||
|
int tile16_row = num_tiles_ / tiles_per_row;
|
||||||
|
int tile16_column = num_tiles_ % tiles_per_row;
|
||||||
|
int baseX = tile16_column * tile_width_;
|
||||||
|
int baseY = tile16_row * tile_height_;
|
||||||
|
|
||||||
|
// Compose and place each part of the Tile16
|
||||||
|
ComposeAndPlaceTilePart(graphics_buffer, top_left, baseX, baseY);
|
||||||
|
ComposeAndPlaceTilePart(graphics_buffer, top_right, baseX + 8, baseY);
|
||||||
|
ComposeAndPlaceTilePart(graphics_buffer, bottom_left, baseX, baseY + 8);
|
||||||
|
ComposeAndPlaceTilePart(graphics_buffer, bottom_right, baseX + 8, baseY + 8);
|
||||||
|
|
||||||
|
tile_info_.push_back({top_left, top_right, bottom_left, bottom_right});
|
||||||
|
|
||||||
|
num_tiles_++;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Tilesheet::ComposeAndPlaceTilePart(
|
||||||
|
const std::vector<uint8_t>& graphics_buffer, const TileInfo& tile_info,
|
||||||
|
int baseX, int baseY) {
|
||||||
|
std::vector<uint8_t> tile_data =
|
||||||
|
FetchTileDataFromGraphicsBuffer(graphics_buffer, tile_info.id_);
|
||||||
|
|
||||||
|
if (tile_info.vertical_mirror_) {
|
||||||
|
MirrorTileDataVertically(tile_data);
|
||||||
|
}
|
||||||
|
if (tile_info.horizontal_mirror_) {
|
||||||
|
MirrorTileDataHorizontally(tile_data);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Place the tile data into the full-size bitmap at the calculated position
|
||||||
|
for (int y = 0; y < 8; ++y) {
|
||||||
|
for (int x = 0; x < 8; ++x) {
|
||||||
|
int srcIndex = y * 8 + x;
|
||||||
|
int destX = baseX + x;
|
||||||
|
int destY = baseY + y;
|
||||||
|
int destIndex = (destY * bitmap_->width()) + destX;
|
||||||
|
internal_data_[destIndex] = tile_data[srcIndex];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bitmap_->set_data(internal_data_);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace gfx
|
||||||
|
} // namespace app
|
||||||
|
} // namespace yaze
|
||||||
|
|||||||
@@ -1,10 +1,13 @@
|
|||||||
#ifndef YAZE_APP_GFX_TILESHEET_H
|
#ifndef YAZE_APP_GFX_TILESHEET_H
|
||||||
#define YAZE_APP_GFX_TILESHEET_H
|
#define YAZE_APP_GFX_TILESHEET_H
|
||||||
|
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#include "app/gfx/bitmap.h"
|
#include "app/gfx/bitmap.h"
|
||||||
|
#include "app/gfx/snes_color.h"
|
||||||
#include "app/gfx/snes_palette.h"
|
#include "app/gfx/snes_palette.h"
|
||||||
|
#include "app/gfx/snes_tile.h"
|
||||||
|
|
||||||
namespace yaze {
|
namespace yaze {
|
||||||
namespace app {
|
namespace app {
|
||||||
@@ -12,6 +15,14 @@ namespace gfx {
|
|||||||
|
|
||||||
enum class TileType { Tile8, Tile16 };
|
enum class TileType { Tile8, Tile16 };
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @class Tilesheet
|
||||||
|
* @brief Represents a tilesheet, which is a collection of tiles stored in a
|
||||||
|
* bitmap.
|
||||||
|
*
|
||||||
|
* The Tilesheet class provides methods to manipulate and extract tiles from the
|
||||||
|
* tilesheet. It also supports copying and mirroring tiles within the tilesheet.
|
||||||
|
*/
|
||||||
class Tilesheet {
|
class Tilesheet {
|
||||||
public:
|
public:
|
||||||
Tilesheet() = default;
|
Tilesheet() = default;
|
||||||
@@ -22,68 +33,14 @@ class Tilesheet {
|
|||||||
tile_height_(tileHeight),
|
tile_height_(tileHeight),
|
||||||
tile_type_(tile_type) {}
|
tile_type_(tile_type) {}
|
||||||
|
|
||||||
void Init(int width, int height, TileType tile_type) {
|
void Init(int width, int height, TileType tile_type);
|
||||||
bitmap_ = std::make_shared<Bitmap>(width, height, 8, 0x20000);
|
|
||||||
internal_data_.resize(0x20000);
|
|
||||||
tile_type_ = tile_type;
|
|
||||||
if (tile_type_ == TileType::Tile8) {
|
|
||||||
tile_width_ = 8;
|
|
||||||
tile_height_ = 8;
|
|
||||||
} else {
|
|
||||||
tile_width_ = 16;
|
|
||||||
tile_height_ = 16;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void ComposeTile16(const std::vector<uint8_t>& graphics_buffer,
|
void ComposeTile16(const std::vector<uint8_t>& graphics_buffer,
|
||||||
const TileInfo& top_left, const TileInfo& top_right,
|
const TileInfo& top_left, const TileInfo& top_right,
|
||||||
const TileInfo& bottom_left,
|
const TileInfo& bottom_left, const TileInfo& bottom_right);
|
||||||
const TileInfo& bottom_right) {
|
|
||||||
// Calculate the base position for this Tile16 in the full-size bitmap
|
|
||||||
int tiles_per_row = bitmap_->width() / tile_width_;
|
|
||||||
int tile16_row = num_tiles_ / tiles_per_row;
|
|
||||||
int tile16_column = num_tiles_ % tiles_per_row;
|
|
||||||
int baseX = tile16_column * tile_width_;
|
|
||||||
int baseY = tile16_row * tile_height_;
|
|
||||||
|
|
||||||
// Compose and place each part of the Tile16
|
|
||||||
ComposeAndPlaceTilePart(graphics_buffer, top_left, baseX, baseY);
|
|
||||||
ComposeAndPlaceTilePart(graphics_buffer, top_right, baseX + 8, baseY);
|
|
||||||
ComposeAndPlaceTilePart(graphics_buffer, bottom_left, baseX, baseY + 8);
|
|
||||||
ComposeAndPlaceTilePart(graphics_buffer, bottom_right, baseX + 8,
|
|
||||||
baseY + 8);
|
|
||||||
|
|
||||||
tile_info_.push_back({top_left, top_right, bottom_left, bottom_right});
|
|
||||||
|
|
||||||
num_tiles_++;
|
|
||||||
}
|
|
||||||
|
|
||||||
void ComposeAndPlaceTilePart(const std::vector<uint8_t>& graphics_buffer,
|
void ComposeAndPlaceTilePart(const std::vector<uint8_t>& graphics_buffer,
|
||||||
const TileInfo& tile_info, int baseX,
|
const TileInfo& tile_info, int baseX, int baseY);
|
||||||
int baseY) {
|
|
||||||
std::vector<uint8_t> tile_data =
|
|
||||||
FetchTileDataFromGraphicsBuffer(graphics_buffer, tile_info.id_);
|
|
||||||
|
|
||||||
if (tile_info.vertical_mirror_) {
|
|
||||||
MirrorTileDataVertically(tile_data);
|
|
||||||
}
|
|
||||||
if (tile_info.horizontal_mirror_) {
|
|
||||||
MirrorTileDataHorizontally(tile_data);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Place the tile data into the full-size bitmap at the calculated position
|
|
||||||
for (int y = 0; y < 8; ++y) {
|
|
||||||
for (int x = 0; x < 8; ++x) {
|
|
||||||
int srcIndex = y * 8 + x;
|
|
||||||
int destX = baseX + x;
|
|
||||||
int destY = baseY + y;
|
|
||||||
int destIndex = (destY * bitmap_->width()) + destX;
|
|
||||||
internal_data_[destIndex] = tile_data[srcIndex];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bitmap_->set_data(internal_data_);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Extracts a tile from the tilesheet
|
// Extracts a tile from the tilesheet
|
||||||
Bitmap GetTile(int tileX, int tileY, int bmp_width, int bmp_height) {
|
Bitmap GetTile(int tileX, int tileY, int bmp_width, int bmp_height) {
|
||||||
@@ -230,6 +187,10 @@ class Tilesheet {
|
|||||||
TileType tile_type_;
|
TileType tile_type_;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
absl::StatusOr<Tilesheet> CreateTilesheetFromGraphicsBuffer(
|
||||||
|
const uint8_t* graphics_buffer, int width, int height, TileType tile_type,
|
||||||
|
int sheet_id);
|
||||||
|
|
||||||
} // namespace gfx
|
} // namespace gfx
|
||||||
} // namespace app
|
} // namespace app
|
||||||
} // namespace yaze
|
} // namespace yaze
|
||||||
|
|||||||
@@ -115,7 +115,7 @@ void Canvas::DrawContextMenu(gfx::Bitmap *bitmap) {
|
|||||||
scrolling_.y = 0;
|
scrolling_.y = 0;
|
||||||
}
|
}
|
||||||
ImGui::MenuItem("Show Grid", nullptr, &enable_grid_);
|
ImGui::MenuItem("Show Grid", nullptr, &enable_grid_);
|
||||||
ImGui::Selectable("Show Labels", &enable_hex_tile_labels_);
|
ImGui::Selectable("Show Position Labels", &enable_hex_tile_labels_);
|
||||||
if (ImGui::BeginMenu("Canvas Properties")) {
|
if (ImGui::BeginMenu("Canvas Properties")) {
|
||||||
ImGui::Text("Canvas Size: %.0f x %.0f", canvas_sz_.x, canvas_sz_.y);
|
ImGui::Text("Canvas Size: %.0f x %.0f", canvas_sz_.x, canvas_sz_.y);
|
||||||
ImGui::Text("Global Scale: %.1f", global_scale_);
|
ImGui::Text("Global Scale: %.1f", global_scale_);
|
||||||
@@ -127,6 +127,10 @@ void Canvas::DrawContextMenu(gfx::Bitmap *bitmap) {
|
|||||||
ImGui::Text("Size: %.0f x %.0f", scaled_sz.x, scaled_sz.y);
|
ImGui::Text("Size: %.0f x %.0f", scaled_sz.x, scaled_sz.y);
|
||||||
ImGui::Text("Pitch: %s",
|
ImGui::Text("Pitch: %s",
|
||||||
absl::StrFormat("%d", bitmap->surface()->pitch).c_str());
|
absl::StrFormat("%d", bitmap->surface()->pitch).c_str());
|
||||||
|
ImGui::Text("BitsPerPixel: %d",
|
||||||
|
bitmap->surface()->format->BitsPerPixel);
|
||||||
|
ImGui::Text("BytesPerPixel: %d",
|
||||||
|
bitmap->surface()->format->BytesPerPixel);
|
||||||
ImGui::EndMenu();
|
ImGui::EndMenu();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,6 +11,11 @@
|
|||||||
|
|
||||||
namespace yaze {
|
namespace yaze {
|
||||||
namespace app {
|
namespace app {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @namespace yaze::app::gui
|
||||||
|
* @brief Graphical User Interface (GUI) components for the application.
|
||||||
|
*/
|
||||||
namespace gui {
|
namespace gui {
|
||||||
|
|
||||||
using app::gfx::Bitmap;
|
using app::gfx::Bitmap;
|
||||||
@@ -20,6 +25,14 @@ enum class CanvasType { kTile, kBlock, kMap };
|
|||||||
enum class CanvasMode { kPaint, kSelect };
|
enum class CanvasMode { kPaint, kSelect };
|
||||||
enum class CanvasGridSize { k8x8, k16x16, k32x32, k64x64 };
|
enum class CanvasGridSize { k8x8, k16x16, k32x32, k64x64 };
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @class Canvas
|
||||||
|
* @brief Represents a canvas for drawing and manipulating graphics.
|
||||||
|
*
|
||||||
|
* The Canvas class provides various functions for updating and drawing graphics
|
||||||
|
* on a canvas. It supports features such as bitmap drawing, context menu
|
||||||
|
* handling, tile painting, custom grid, and more.
|
||||||
|
*/
|
||||||
class Canvas {
|
class Canvas {
|
||||||
public:
|
public:
|
||||||
Canvas() = default;
|
Canvas() = default;
|
||||||
|
|||||||
@@ -52,7 +52,7 @@ IMGUI_API bool SnesColorEdit4(absl::string_view label, SnesColor& color,
|
|||||||
return pressed;
|
return pressed;
|
||||||
}
|
}
|
||||||
|
|
||||||
void DisplayPalette(app::gfx::SnesPalette& palette, bool loaded) {
|
absl::Status DisplayPalette(app::gfx::SnesPalette& palette, bool loaded) {
|
||||||
static ImVec4 color = ImVec4(0, 0, 0, 255.f);
|
static ImVec4 color = ImVec4(0, 0, 0, 255.f);
|
||||||
ImGuiColorEditFlags misc_flags = ImGuiColorEditFlags_AlphaPreview |
|
ImGuiColorEditFlags misc_flags = ImGuiColorEditFlags_AlphaPreview |
|
||||||
ImGuiColorEditFlags_NoDragDrop |
|
ImGuiColorEditFlags_NoDragDrop |
|
||||||
@@ -63,9 +63,10 @@ void DisplayPalette(app::gfx::SnesPalette& palette, bool loaded) {
|
|||||||
static ImVec4 saved_palette[32] = {};
|
static ImVec4 saved_palette[32] = {};
|
||||||
if (loaded && !init) {
|
if (loaded && !init) {
|
||||||
for (int n = 0; n < palette.size(); n++) {
|
for (int n = 0; n < palette.size(); n++) {
|
||||||
saved_palette[n].x = palette.GetColor(n).rgb().x / 255;
|
ASSIGN_OR_RETURN(auto color, palette.GetColor(n));
|
||||||
saved_palette[n].y = palette.GetColor(n).rgb().y / 255;
|
saved_palette[n].x = color.rgb().x / 255;
|
||||||
saved_palette[n].z = palette.GetColor(n).rgb().z / 255;
|
saved_palette[n].y = color.rgb().y / 255;
|
||||||
|
saved_palette[n].z = color.rgb().z / 255;
|
||||||
saved_palette[n].w = 255; // Alpha
|
saved_palette[n].w = 255; // Alpha
|
||||||
}
|
}
|
||||||
init = true;
|
init = true;
|
||||||
|
|||||||
@@ -6,6 +6,7 @@
|
|||||||
#include <cmath>
|
#include <cmath>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
|
#include "absl/status/status.h"
|
||||||
#include "app/gfx/bitmap.h"
|
#include "app/gfx/bitmap.h"
|
||||||
#include "app/gfx/snes_palette.h"
|
#include "app/gfx/snes_palette.h"
|
||||||
|
|
||||||
@@ -27,7 +28,7 @@ IMGUI_API bool SnesColorButton(absl::string_view id, SnesColor& color,
|
|||||||
IMGUI_API bool SnesColorEdit4(absl::string_view label, SnesColor& color,
|
IMGUI_API bool SnesColorEdit4(absl::string_view label, SnesColor& color,
|
||||||
ImGuiColorEditFlags flags = 0);
|
ImGuiColorEditFlags flags = 0);
|
||||||
|
|
||||||
void DisplayPalette(app::gfx::SnesPalette& palette, bool loaded);
|
absl::Status DisplayPalette(app::gfx::SnesPalette& palette, bool loaded);
|
||||||
|
|
||||||
} // namespace gui
|
} // namespace gui
|
||||||
} // namespace app
|
} // namespace app
|
||||||
|
|||||||
@@ -15,7 +15,6 @@
|
|||||||
#include "app/gui/canvas.h"
|
#include "app/gui/canvas.h"
|
||||||
#include "app/gui/color.h"
|
#include "app/gui/color.h"
|
||||||
#include "app/gui/input.h"
|
#include "app/gui/input.h"
|
||||||
#include "app/rom.h"
|
|
||||||
|
|
||||||
namespace yaze {
|
namespace yaze {
|
||||||
namespace app {
|
namespace app {
|
||||||
@@ -158,14 +157,6 @@ void BitmapCanvasPipeline(gui::Canvas& canvas, const gfx::Bitmap& bitmap,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void BuildAndRenderBitmapPipeline(int width, int height, int depth, Bytes data,
|
|
||||||
ROM& z3_rom, gfx::Bitmap& bitmap,
|
|
||||||
gfx::SnesPalette& palette) {
|
|
||||||
bitmap.Create(width, height, depth, data);
|
|
||||||
bitmap.ApplyPalette(palette);
|
|
||||||
z3_rom.RenderBitmap(&bitmap);
|
|
||||||
}
|
|
||||||
|
|
||||||
void FileDialogPipeline(absl::string_view display_key,
|
void FileDialogPipeline(absl::string_view display_key,
|
||||||
absl::string_view file_extensions,
|
absl::string_view file_extensions,
|
||||||
std::optional<absl::string_view> button_text,
|
std::optional<absl::string_view> button_text,
|
||||||
|
|||||||
@@ -14,7 +14,6 @@
|
|||||||
#include "app/gfx/bitmap.h"
|
#include "app/gfx/bitmap.h"
|
||||||
#include "app/gfx/snes_palette.h"
|
#include "app/gfx/snes_palette.h"
|
||||||
#include "app/gui/canvas.h"
|
#include "app/gui/canvas.h"
|
||||||
#include "app/rom.h"
|
|
||||||
|
|
||||||
namespace yaze {
|
namespace yaze {
|
||||||
namespace app {
|
namespace app {
|
||||||
@@ -38,10 +37,6 @@ void GraphicsManagerCanvasPipeline(int width, int height, int tile_size,
|
|||||||
bool is_loaded,
|
bool is_loaded,
|
||||||
const gfx::BitmapManager& graphics_manager);
|
const gfx::BitmapManager& graphics_manager);
|
||||||
|
|
||||||
void BuildAndRenderBitmapPipeline(int width, int height, int depth, Bytes data,
|
|
||||||
ROM& z3_rom, gfx::Bitmap& bitmap,
|
|
||||||
gfx::SnesPalette& palette);
|
|
||||||
|
|
||||||
void FileDialogPipeline(absl::string_view display_key,
|
void FileDialogPipeline(absl::string_view display_key,
|
||||||
absl::string_view file_extensions,
|
absl::string_view file_extensions,
|
||||||
std::optional<absl::string_view> button_text,
|
std::optional<absl::string_view> button_text,
|
||||||
|
|||||||
276
src/app/rom.cc
276
src/app/rom.cc
@@ -29,165 +29,7 @@
|
|||||||
namespace yaze {
|
namespace yaze {
|
||||||
namespace app {
|
namespace app {
|
||||||
|
|
||||||
namespace {
|
absl::StatusOr<Bytes> Rom::Load2BppGraphics() {
|
||||||
absl::Status LoadOverworldMainPalettes(const Bytes& rom_data,
|
|
||||||
PaletteGroupMap& palette_groups) {
|
|
||||||
auto data = rom_data.data();
|
|
||||||
for (int i = 0; i < 6; i++) {
|
|
||||||
RETURN_IF_ERROR(palette_groups["ow_main"].AddPalette(
|
|
||||||
gfx::ReadPaletteFromRom(core::overworldPaletteMain + (i * (35 * 2)),
|
|
||||||
/*num_colors*/ 35, data)))
|
|
||||||
}
|
|
||||||
return absl::OkStatus();
|
|
||||||
}
|
|
||||||
|
|
||||||
absl::Status LoadOverworldAuxiliaryPalettes(const Bytes& rom_data,
|
|
||||||
PaletteGroupMap& palette_groups) {
|
|
||||||
auto data = rom_data.data();
|
|
||||||
for (int i = 0; i < 20; i++) {
|
|
||||||
RETURN_IF_ERROR(palette_groups["ow_aux"].AddPalette(gfx::ReadPaletteFromRom(
|
|
||||||
core::overworldPaletteAuxialiary + (i * (21 * 2)),
|
|
||||||
/*num_colors*/ 21, data)))
|
|
||||||
}
|
|
||||||
return absl::OkStatus();
|
|
||||||
}
|
|
||||||
|
|
||||||
absl::Status LoadOverworldAnimatedPalettes(const Bytes& rom_data,
|
|
||||||
PaletteGroupMap& palette_groups) {
|
|
||||||
auto data = rom_data.data();
|
|
||||||
for (int i = 0; i < 14; i++) {
|
|
||||||
RETURN_IF_ERROR(
|
|
||||||
palette_groups["ow_animated"].AddPalette(gfx::ReadPaletteFromRom(
|
|
||||||
core::overworldPaletteAnimated + (i * (7 * 2)), 7, data)))
|
|
||||||
}
|
|
||||||
return absl::OkStatus();
|
|
||||||
}
|
|
||||||
|
|
||||||
absl::Status LoadHUDPalettes(const Bytes& rom_data,
|
|
||||||
PaletteGroupMap& palette_groups) {
|
|
||||||
auto data = rom_data.data();
|
|
||||||
for (int i = 0; i < 2; i++) {
|
|
||||||
RETURN_IF_ERROR(palette_groups["hud"].AddPalette(
|
|
||||||
gfx::ReadPaletteFromRom(core::hudPalettes + (i * 64), 32, data)))
|
|
||||||
}
|
|
||||||
return absl::OkStatus();
|
|
||||||
}
|
|
||||||
|
|
||||||
absl::Status LoadGlobalSpritePalettes(const Bytes& rom_data,
|
|
||||||
PaletteGroupMap& palette_groups) {
|
|
||||||
auto data = rom_data.data();
|
|
||||||
RETURN_IF_ERROR(palette_groups["global_sprites"].AddPalette(
|
|
||||||
gfx::ReadPaletteFromRom(core::globalSpritePalettesLW, 60, data)))
|
|
||||||
RETURN_IF_ERROR(palette_groups["global_sprites"].AddPalette(
|
|
||||||
gfx::ReadPaletteFromRom(core::globalSpritePalettesDW, 60, data)))
|
|
||||||
return absl::OkStatus();
|
|
||||||
}
|
|
||||||
|
|
||||||
absl::Status LoadArmorPalettes(const Bytes& rom_data,
|
|
||||||
PaletteGroupMap& palette_groups) {
|
|
||||||
auto data = rom_data.data();
|
|
||||||
for (int i = 0; i < 5; i++) {
|
|
||||||
RETURN_IF_ERROR(palette_groups["armors"].AddPalette(
|
|
||||||
gfx::ReadPaletteFromRom(core::armorPalettes + (i * 30), 15, data)))
|
|
||||||
}
|
|
||||||
return absl::OkStatus();
|
|
||||||
}
|
|
||||||
|
|
||||||
absl::Status LoadSwordPalettes(const Bytes& rom_data,
|
|
||||||
PaletteGroupMap& palette_groups) {
|
|
||||||
auto data = rom_data.data();
|
|
||||||
for (int i = 0; i < 4; i++) {
|
|
||||||
RETURN_IF_ERROR(palette_groups["swords"].AddPalette(
|
|
||||||
gfx::ReadPaletteFromRom(core::swordPalettes + (i * 6), 3, data)))
|
|
||||||
}
|
|
||||||
return absl::OkStatus();
|
|
||||||
}
|
|
||||||
|
|
||||||
absl::Status LoadShieldPalettes(const Bytes& rom_data,
|
|
||||||
PaletteGroupMap& palette_groups) {
|
|
||||||
auto data = rom_data.data();
|
|
||||||
for (int i = 0; i < 3; i++) {
|
|
||||||
RETURN_IF_ERROR(palette_groups["shields"].AddPalette(
|
|
||||||
gfx::ReadPaletteFromRom(core::shieldPalettes + (i * 8), 4, data)))
|
|
||||||
}
|
|
||||||
return absl::OkStatus();
|
|
||||||
}
|
|
||||||
|
|
||||||
absl::Status LoadSpriteAux1Palettes(const Bytes& rom_data,
|
|
||||||
PaletteGroupMap& palette_groups) {
|
|
||||||
auto data = rom_data.data();
|
|
||||||
for (int i = 0; i < 12; i++) {
|
|
||||||
RETURN_IF_ERROR(palette_groups["sprites_aux1"].AddPalette(
|
|
||||||
gfx::ReadPaletteFromRom(core::spritePalettesAux1 + (i * 14), 7, data)))
|
|
||||||
}
|
|
||||||
return absl::OkStatus();
|
|
||||||
}
|
|
||||||
|
|
||||||
absl::Status LoadSpriteAux2Palettes(const Bytes& rom_data,
|
|
||||||
PaletteGroupMap& palette_groups) {
|
|
||||||
auto data = rom_data.data();
|
|
||||||
for (int i = 0; i < 11; i++) {
|
|
||||||
RETURN_IF_ERROR(palette_groups["sprites_aux2"].AddPalette(
|
|
||||||
gfx::ReadPaletteFromRom(core::spritePalettesAux2 + (i * 14), 7, data)))
|
|
||||||
}
|
|
||||||
return absl::OkStatus();
|
|
||||||
}
|
|
||||||
|
|
||||||
absl::Status LoadSpriteAux3Palettes(const Bytes& rom_data,
|
|
||||||
PaletteGroupMap& palette_groups) {
|
|
||||||
auto data = rom_data.data();
|
|
||||||
for (int i = 0; i < 24; i++) {
|
|
||||||
RETURN_IF_ERROR(palette_groups["sprites_aux3"].AddPalette(
|
|
||||||
gfx::ReadPaletteFromRom(core::spritePalettesAux3 + (i * 14), 7, data)))
|
|
||||||
}
|
|
||||||
return absl::OkStatus();
|
|
||||||
}
|
|
||||||
|
|
||||||
absl::Status LoadDungeonMainPalettes(const Bytes& rom_data,
|
|
||||||
PaletteGroupMap& palette_groups) {
|
|
||||||
auto data = rom_data.data();
|
|
||||||
for (int i = 0; i < 20; i++) {
|
|
||||||
RETURN_IF_ERROR(
|
|
||||||
palette_groups["dungeon_main"].AddPalette(gfx::ReadPaletteFromRom(
|
|
||||||
core::dungeonMainPalettes + (i * 180), 90, data)))
|
|
||||||
}
|
|
||||||
return absl::OkStatus();
|
|
||||||
}
|
|
||||||
|
|
||||||
absl::Status LoadGrassColors(const Bytes& rom_data,
|
|
||||||
PaletteGroupMap& palette_groups) {
|
|
||||||
RETURN_IF_ERROR(palette_groups["grass"].AddColor(
|
|
||||||
gfx::ReadColorFromRom(core::hardcodedGrassLW, rom_data.data())))
|
|
||||||
RETURN_IF_ERROR(palette_groups["grass"].AddColor(
|
|
||||||
gfx::ReadColorFromRom(core::hardcodedGrassDW, rom_data.data())))
|
|
||||||
RETURN_IF_ERROR(palette_groups["grass"].AddColor(
|
|
||||||
gfx::ReadColorFromRom(core::hardcodedGrassSpecial, rom_data.data())))
|
|
||||||
return absl::OkStatus();
|
|
||||||
}
|
|
||||||
|
|
||||||
absl::Status Load3DObjectPalettes(const Bytes& rom_data,
|
|
||||||
PaletteGroupMap& palette_groups) {
|
|
||||||
auto data = rom_data.data();
|
|
||||||
RETURN_IF_ERROR(palette_groups["3d_object"].AddPalette(
|
|
||||||
gfx::ReadPaletteFromRom(core::triforcePalette, 8, data)))
|
|
||||||
RETURN_IF_ERROR(palette_groups["3d_object"].AddPalette(
|
|
||||||
gfx::ReadPaletteFromRom(core::crystalPalette, 8, data)))
|
|
||||||
return absl::OkStatus();
|
|
||||||
}
|
|
||||||
|
|
||||||
absl::Status LoadOverworldMiniMapPalettes(const Bytes& rom_data,
|
|
||||||
PaletteGroupMap& palette_groups) {
|
|
||||||
auto data = rom_data.data();
|
|
||||||
for (int i = 0; i < 2; i++) {
|
|
||||||
RETURN_IF_ERROR(
|
|
||||||
palette_groups["ow_mini_map"].AddPalette(gfx::ReadPaletteFromRom(
|
|
||||||
core::overworldMiniMapPalettes + (i * 256), 128, data)))
|
|
||||||
}
|
|
||||||
return absl::OkStatus();
|
|
||||||
}
|
|
||||||
} // namespace
|
|
||||||
|
|
||||||
absl::StatusOr<Bytes> ROM::Load2BppGraphics() {
|
|
||||||
Bytes sheet;
|
Bytes sheet;
|
||||||
const uint8_t sheets[] = {113, 114, 218, 219, 220, 221};
|
const uint8_t sheets[] = {113, 114, 218, 219, 220, 221};
|
||||||
|
|
||||||
@@ -203,10 +45,10 @@ absl::StatusOr<Bytes> ROM::Load2BppGraphics() {
|
|||||||
return sheet;
|
return sheet;
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Load Links graphics from the ROM
|
absl::Status Rom::LoadLinkGraphics() {
|
||||||
absl::Status ROM::LoadLinkGraphics() {
|
const auto link_gfx_offset = 0x80000; // $10:8000
|
||||||
const auto link_gfx_offset = 81920; // $10:8000
|
const auto link_gfx_length = 0x800; // 0x4000 or 0x7000?
|
||||||
const auto link_gfx_length = 0x800;
|
link_palette_ = palette_groups_.armors[0];
|
||||||
|
|
||||||
// Load Links graphics from the ROM
|
// Load Links graphics from the ROM
|
||||||
for (int i = 0; i < 14; i++) {
|
for (int i = 0; i < 14; i++) {
|
||||||
@@ -214,17 +56,22 @@ absl::Status ROM::LoadLinkGraphics() {
|
|||||||
auto link_sheet_data,
|
auto link_sheet_data,
|
||||||
ReadByteVector(/*offset=*/link_gfx_offset + (i * link_gfx_length),
|
ReadByteVector(/*offset=*/link_gfx_offset + (i * link_gfx_length),
|
||||||
/*length=*/link_gfx_length))
|
/*length=*/link_gfx_length))
|
||||||
auto link_sheet_8bpp = gfx::SnesTo8bppSheet(link_sheet_data, /*bpp=*/4);
|
// auto link_sheet_8bpp = gfx::SnesTo8bppSheet(link_sheet_data, /*bpp=*/4);
|
||||||
|
// Convert to 3bpp, then from 3bpp to 8bpp before creating bitmap.
|
||||||
|
auto link_sheet_3bpp = gfx::Convert4bppTo3bpp(link_sheet_data);
|
||||||
|
auto link_sheet_8bpp = gfx::SnesTo8bppSheet(link_sheet_3bpp, /*bpp=*/3);
|
||||||
link_graphics_[i].Create(core::kTilesheetWidth, core::kTilesheetHeight,
|
link_graphics_[i].Create(core::kTilesheetWidth, core::kTilesheetHeight,
|
||||||
core::kTilesheetDepth, link_sheet_8bpp);
|
core::kTilesheetDepth, link_sheet_8bpp);
|
||||||
link_graphics_[i].ApplyPalette(link_palette_);
|
RETURN_IF_ERROR(
|
||||||
|
link_graphics_[i].ApplyPaletteWithTransparent(link_palette_, 0));
|
||||||
RenderBitmap(&link_graphics_[i]);
|
RenderBitmap(&link_graphics_[i]);
|
||||||
}
|
}
|
||||||
|
|
||||||
return absl::OkStatus();
|
return absl::OkStatus();
|
||||||
}
|
}
|
||||||
|
|
||||||
absl::Status ROM::LoadAllGraphicsData() {
|
absl::Status Rom::LoadAllGraphicsData() {
|
||||||
|
constexpr uint32_t kNumGfxSheets = 223;
|
||||||
Bytes sheet;
|
Bytes sheet;
|
||||||
bool bpp3 = false;
|
bool bpp3 = false;
|
||||||
|
|
||||||
@@ -252,11 +99,11 @@ absl::Status ROM::LoadAllGraphicsData() {
|
|||||||
core::kTilesheetDepth);
|
core::kTilesheetDepth);
|
||||||
if (i > 115) {
|
if (i > 115) {
|
||||||
// Apply sprites palette
|
// Apply sprites palette
|
||||||
graphics_manager_[i]->ApplyPaletteWithTransparent(
|
RETURN_IF_ERROR(graphics_manager_[i]->ApplyPaletteWithTransparent(
|
||||||
palette_groups_["global_sprites"][0], 0);
|
palette_groups_.global_sprites[0], 0));
|
||||||
} else {
|
} else {
|
||||||
graphics_manager_[i]->ApplyPaletteWithTransparent(
|
RETURN_IF_ERROR(graphics_manager_[i]->ApplyPaletteWithTransparent(
|
||||||
palette_groups_["dungeon_main"][0], 0);
|
palette_groups_.dungeon_main[0], 0));
|
||||||
}
|
}
|
||||||
graphics_manager_[i]->CreateTexture(renderer_);
|
graphics_manager_[i]->CreateTexture(renderer_);
|
||||||
}
|
}
|
||||||
@@ -279,26 +126,7 @@ absl::Status ROM::LoadAllGraphicsData() {
|
|||||||
return absl::OkStatus();
|
return absl::OkStatus();
|
||||||
}
|
}
|
||||||
|
|
||||||
absl::Status ROM::LoadAllPalettes() {
|
absl::Status Rom::LoadFromFile(const absl::string_view& filename,
|
||||||
RETURN_IF_ERROR(LoadOverworldMainPalettes(rom_data_, palette_groups_))
|
|
||||||
RETURN_IF_ERROR(LoadOverworldAuxiliaryPalettes(rom_data_, palette_groups_))
|
|
||||||
RETURN_IF_ERROR(LoadOverworldAnimatedPalettes(rom_data_, palette_groups_))
|
|
||||||
RETURN_IF_ERROR(LoadHUDPalettes(rom_data_, palette_groups_))
|
|
||||||
RETURN_IF_ERROR(LoadGlobalSpritePalettes(rom_data_, palette_groups_))
|
|
||||||
RETURN_IF_ERROR(LoadArmorPalettes(rom_data_, palette_groups_))
|
|
||||||
RETURN_IF_ERROR(LoadSwordPalettes(rom_data_, palette_groups_))
|
|
||||||
RETURN_IF_ERROR(LoadShieldPalettes(rom_data_, palette_groups_))
|
|
||||||
RETURN_IF_ERROR(LoadSpriteAux1Palettes(rom_data_, palette_groups_))
|
|
||||||
RETURN_IF_ERROR(LoadSpriteAux2Palettes(rom_data_, palette_groups_))
|
|
||||||
RETURN_IF_ERROR(LoadSpriteAux3Palettes(rom_data_, palette_groups_))
|
|
||||||
RETURN_IF_ERROR(LoadDungeonMainPalettes(rom_data_, palette_groups_))
|
|
||||||
RETURN_IF_ERROR(LoadGrassColors(rom_data_, palette_groups_))
|
|
||||||
RETURN_IF_ERROR(Load3DObjectPalettes(rom_data_, palette_groups_))
|
|
||||||
RETURN_IF_ERROR(LoadOverworldMiniMapPalettes(rom_data_, palette_groups_))
|
|
||||||
return absl::OkStatus();
|
|
||||||
}
|
|
||||||
|
|
||||||
absl::Status ROM::LoadFromFile(const absl::string_view& filename,
|
|
||||||
bool z3_load) {
|
bool z3_load) {
|
||||||
// Set filename
|
// Set filename
|
||||||
filename_ = filename;
|
filename_ = filename;
|
||||||
@@ -338,13 +166,15 @@ absl::Status ROM::LoadFromFile(const absl::string_view& filename,
|
|||||||
// Load Zelda 3 specific data if requested
|
// Load Zelda 3 specific data if requested
|
||||||
if (z3_load) {
|
if (z3_load) {
|
||||||
// Copy ROM title
|
// Copy ROM title
|
||||||
|
constexpr uint32_t kTitleStringOffset = 0x7FC0;
|
||||||
|
constexpr uint32_t kTitleStringLength = 20;
|
||||||
memcpy(title_, rom_data_.data() + kTitleStringOffset, kTitleStringLength);
|
memcpy(title_, rom_data_.data() + kTitleStringOffset, kTitleStringLength);
|
||||||
if (rom_data_[kTitleStringOffset + 0x19] == 0) {
|
if (rom_data_[kTitleStringOffset + 0x19] == 0) {
|
||||||
version_ = Z3_Version::JP;
|
version_ = Z3_Version::JP;
|
||||||
} else {
|
} else {
|
||||||
version_ = Z3_Version::US;
|
version_ = Z3_Version::US;
|
||||||
}
|
}
|
||||||
RETURN_IF_ERROR(LoadAllPalettes())
|
RETURN_IF_ERROR(gfx::LoadAllPalettes(rom_data_, palette_groups_));
|
||||||
LoadGfxGroups();
|
LoadGfxGroups();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -361,7 +191,7 @@ absl::Status ROM::LoadFromFile(const absl::string_view& filename,
|
|||||||
return absl::OkStatus();
|
return absl::OkStatus();
|
||||||
}
|
}
|
||||||
|
|
||||||
absl::Status ROM::LoadFromPointer(uchar* data, size_t length) {
|
absl::Status Rom::LoadFromPointer(uchar* data, size_t length) {
|
||||||
if (!data)
|
if (!data)
|
||||||
return absl::InvalidArgumentError(
|
return absl::InvalidArgumentError(
|
||||||
"Could not load ROM: parameter `data` is empty.");
|
"Could not load ROM: parameter `data` is empty.");
|
||||||
@@ -371,7 +201,7 @@ absl::Status ROM::LoadFromPointer(uchar* data, size_t length) {
|
|||||||
return absl::OkStatus();
|
return absl::OkStatus();
|
||||||
}
|
}
|
||||||
|
|
||||||
absl::Status ROM::LoadFromBytes(const Bytes& data) {
|
absl::Status Rom::LoadFromBytes(const Bytes& data) {
|
||||||
if (data.empty()) {
|
if (data.empty()) {
|
||||||
return absl::InvalidArgumentError(
|
return absl::InvalidArgumentError(
|
||||||
"Could not load ROM: parameter `data` is empty.");
|
"Could not load ROM: parameter `data` is empty.");
|
||||||
@@ -382,7 +212,7 @@ absl::Status ROM::LoadFromBytes(const Bytes& data) {
|
|||||||
return absl::OkStatus();
|
return absl::OkStatus();
|
||||||
}
|
}
|
||||||
|
|
||||||
absl::Status ROM::SaveToFile(bool backup, bool save_new, std::string filename) {
|
absl::Status Rom::SaveToFile(bool backup, bool save_new, std::string filename) {
|
||||||
absl::Status non_firing_status;
|
absl::Status non_firing_status;
|
||||||
if (rom_data_.empty()) {
|
if (rom_data_.empty()) {
|
||||||
return absl::InternalError("ROM data is empty.");
|
return absl::InternalError("ROM data is empty.");
|
||||||
@@ -477,60 +307,32 @@ absl::Status ROM::SaveToFile(bool backup, bool save_new, std::string filename) {
|
|||||||
return absl::OkStatus();
|
return absl::OkStatus();
|
||||||
}
|
}
|
||||||
|
|
||||||
void ROM::SavePalette(int index, const std::string& group_name,
|
absl::Status Rom::SavePalette(int index, const std::string& group_name,
|
||||||
gfx::SnesPalette& palette) {
|
gfx::SnesPalette& palette) {
|
||||||
// Iterate through all colors in the palette
|
// Iterate through all colors in the palette
|
||||||
for (size_t j = 0; j < palette.size(); ++j) {
|
for (size_t j = 0; j < palette.size(); ++j) {
|
||||||
gfx::SnesColor color = palette[j];
|
gfx::SnesColor color = palette[j];
|
||||||
// If the color is modified, save the color to the ROM
|
// If the color is modified, save the color to the ROM
|
||||||
if (color.is_modified()) {
|
if (color.is_modified()) {
|
||||||
WriteColor(gfx::GetPaletteAddress(group_name, index, j), color);
|
RETURN_IF_ERROR(
|
||||||
|
WriteColor(gfx::GetPaletteAddress(group_name, index, j), color));
|
||||||
color.set_modified(false); // Reset the modified flag after saving
|
color.set_modified(false); // Reset the modified flag after saving
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
void ROM::SaveAllPalettes() {
|
|
||||||
// Iterate through all palette_groups_
|
|
||||||
for (auto& [group_name, palettes] : palette_groups_) {
|
|
||||||
// Iterate through all palettes in the group
|
|
||||||
for (size_t i = 0; i < palettes.size(); ++i) {
|
|
||||||
auto palette = palettes[i];
|
|
||||||
SavePalette(i, group_name, palette);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
absl::Status ROM::UpdatePaletteColor(const std::string& groupName,
|
|
||||||
size_t paletteIndex, size_t colorIndex,
|
|
||||||
const gfx::SnesColor& newColor) {
|
|
||||||
// Check if the groupName exists in the palette_groups_ map
|
|
||||||
if (palette_groups_.find(groupName) != palette_groups_.end()) {
|
|
||||||
// Check if the paletteIndex is within the range of available palettes in
|
|
||||||
// the group
|
|
||||||
if (paletteIndex < palette_groups_[groupName].size()) {
|
|
||||||
// Check if the colorIndex is within the range of available colors in the
|
|
||||||
// palette
|
|
||||||
if (colorIndex < palette_groups_[groupName][paletteIndex].size()) {
|
|
||||||
// Update the color value in the palette
|
|
||||||
palette_groups_[groupName][paletteIndex][colorIndex] = newColor;
|
|
||||||
palette_groups_[groupName][paletteIndex][colorIndex].set_modified(true);
|
|
||||||
} else {
|
|
||||||
return absl::AbortedError(
|
|
||||||
"Error: Invalid color index in UpdatePaletteColor.");
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
return absl::AbortedError(
|
|
||||||
"Error: Invalid palette index in UpdatePaletteColor.");
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
return absl::AbortedError(
|
|
||||||
"Error: Invalid group name in UpdatePaletteColor");
|
|
||||||
}
|
|
||||||
return absl::OkStatus();
|
return absl::OkStatus();
|
||||||
}
|
}
|
||||||
|
|
||||||
std::shared_ptr<ROM> SharedROM::shared_rom_ = nullptr;
|
absl::Status Rom::SaveAllPalettes() {
|
||||||
|
palette_groups_.for_each([&](gfx::PaletteGroup& group) {
|
||||||
|
for (size_t i = 0; i < group.size(); ++i) {
|
||||||
|
SavePalette(i, group.name(), *group.mutable_palette(i));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return absl::OkStatus();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::shared_ptr<Rom> SharedRom::shared_rom_ = nullptr;
|
||||||
|
|
||||||
} // namespace app
|
} // namespace app
|
||||||
} // namespace yaze
|
} // namespace yaze
|
||||||
280
src/app/rom.h
280
src/app/rom.h
@@ -2,7 +2,7 @@
|
|||||||
#define YAZE_APP_ROM_H
|
#define YAZE_APP_ROM_H
|
||||||
|
|
||||||
#include <SDL.h>
|
#include <SDL.h>
|
||||||
#include <asar/src/asar/interface-lib.h>
|
#include <asar/src/asar/interface-shared.h>
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <chrono>
|
#include <chrono>
|
||||||
@@ -40,17 +40,17 @@
|
|||||||
namespace yaze {
|
namespace yaze {
|
||||||
namespace app {
|
namespace app {
|
||||||
|
|
||||||
using PaletteGroupMap = std::unordered_map<std::string, gfx::PaletteGroup>;
|
|
||||||
|
|
||||||
// Define an enum class for the different versions of the game
|
// Define an enum class for the different versions of the game
|
||||||
enum class Z3_Version {
|
enum class Z3_Version {
|
||||||
US = 1,
|
US = 1, // US version
|
||||||
JP = 2,
|
JP = 2, // JP version
|
||||||
SD = 3,
|
SD = 3, // Super Donkey Proto (Experimental)
|
||||||
RANDO = 4,
|
RANDO = 4, // Randomizer (Unimplemented)
|
||||||
};
|
};
|
||||||
|
|
||||||
// Define a struct to hold the version-specific constants
|
/**
|
||||||
|
* @brief A struct to hold version constants for each version of the game.
|
||||||
|
*/
|
||||||
struct VersionConstants {
|
struct VersionConstants {
|
||||||
uint32_t kGfxAnimatedPointer;
|
uint32_t kGfxAnimatedPointer;
|
||||||
uint32_t kOverworldGfxGroups1;
|
uint32_t kOverworldGfxGroups1;
|
||||||
@@ -72,7 +72,9 @@ struct VersionConstants {
|
|||||||
uint32_t kDungeonPalettesGroups;
|
uint32_t kDungeonPalettesGroups;
|
||||||
};
|
};
|
||||||
|
|
||||||
// Define a map to hold the version constants for each version
|
/**
|
||||||
|
* @brief A map of version constants for each version of the game.
|
||||||
|
*/
|
||||||
static const std::map<Z3_Version, VersionConstants> kVersionConstantsMap = {
|
static const std::map<Z3_Version, VersionConstants> kVersionConstantsMap = {
|
||||||
{Z3_Version::US,
|
{Z3_Version::US,
|
||||||
{
|
{
|
||||||
@@ -117,130 +119,53 @@ static const std::map<Z3_Version, VersionConstants> kVersionConstantsMap = {
|
|||||||
0x67DD0, // kDungeonPalettesGroups
|
0x67DD0, // kDungeonPalettesGroups
|
||||||
}}};
|
}}};
|
||||||
|
|
||||||
// Define some constants used throughout the ROM class
|
|
||||||
constexpr uint32_t kOverworldGraphicsPos1 = 0x4F80;
|
|
||||||
constexpr uint32_t kOverworldGraphicsPos2 = 0x505F;
|
|
||||||
constexpr uint32_t kOverworldGraphicsPos3 = 0x513E;
|
|
||||||
constexpr uint32_t kTile32Num = 4432;
|
|
||||||
constexpr uint32_t kTitleStringOffset = 0x7FC0;
|
|
||||||
constexpr uint32_t kTitleStringLength = 20;
|
|
||||||
constexpr uint32_t kNumGfxSheets = 223;
|
|
||||||
constexpr uint32_t kNormalGfxSpaceStart = 0x87000;
|
constexpr uint32_t kNormalGfxSpaceStart = 0x87000;
|
||||||
constexpr uint32_t kNormalGfxSpaceEnd = 0xC4200;
|
constexpr uint32_t kNormalGfxSpaceEnd = 0xC4200;
|
||||||
constexpr uint32_t kLinkSpriteLocation = 0x80000;
|
|
||||||
constexpr uint32_t kFontSpriteLocation = 0x70000;
|
constexpr uint32_t kFontSpriteLocation = 0x70000;
|
||||||
constexpr uint32_t gfx_groups_pointer = 0x6237;
|
constexpr uint32_t kGfxGroupsPointer = 0x6237;
|
||||||
|
|
||||||
struct WriteAction {
|
/**
|
||||||
int address;
|
* @brief The Rom class is used to load, save, and modify Rom data.
|
||||||
std::variant<int, uint8_t, uint16_t, short, std::vector<uint8_t>,
|
*/
|
||||||
gfx::SnesColor, std::vector<gfx::SnesColor>>
|
class Rom : public core::ExperimentFlags {
|
||||||
value;
|
|
||||||
};
|
|
||||||
|
|
||||||
class ROM : public core::ExperimentFlags {
|
|
||||||
public:
|
public:
|
||||||
template <typename... Args>
|
|
||||||
absl::Status RunTransaction(Args... args) {
|
|
||||||
absl::Status status;
|
|
||||||
// Fold expression to apply the Write function on each argument
|
|
||||||
((status = WriteHelper(args)), ...);
|
|
||||||
return status;
|
|
||||||
}
|
|
||||||
|
|
||||||
absl::Status WriteHelper(const WriteAction& action) {
|
|
||||||
if (std::holds_alternative<uint8_t>(action.value)) {
|
|
||||||
return Write(action.address, std::get<uint8_t>(action.value));
|
|
||||||
} else if (std::holds_alternative<uint16_t>(action.value) ||
|
|
||||||
std::holds_alternative<short>(action.value)) {
|
|
||||||
return WriteShort(action.address, std::get<uint16_t>(action.value));
|
|
||||||
} else if (std::holds_alternative<std::vector<uint8_t>>(action.value)) {
|
|
||||||
return WriteVector(action.address,
|
|
||||||
std::get<std::vector<uint8_t>>(action.value));
|
|
||||||
} else if (std::holds_alternative<gfx::SnesColor>(action.value)) {
|
|
||||||
return WriteColor(action.address, std::get<gfx::SnesColor>(action.value));
|
|
||||||
} else if (std::holds_alternative<std::vector<gfx::SnesColor>>(
|
|
||||||
action.value)) {
|
|
||||||
return absl::UnimplementedError(
|
|
||||||
"WriteHelper: std::vector<gfx::SnesColor>");
|
|
||||||
}
|
|
||||||
auto error_message = absl::StrFormat("Invalid write argument type: %s",
|
|
||||||
typeid(action.value).name());
|
|
||||||
throw std::runtime_error(error_message);
|
|
||||||
return absl::InvalidArgumentError(error_message);
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename T, typename... Args>
|
|
||||||
absl::Status ReadTransaction(T& var, int address, Args&&... args) {
|
|
||||||
absl::Status status = ReadHelper<T>(var, address);
|
|
||||||
if (!status.ok()) {
|
|
||||||
return status;
|
|
||||||
}
|
|
||||||
|
|
||||||
if constexpr (sizeof...(args) > 0) {
|
|
||||||
status = ReadTransaction(std::forward<Args>(args)...);
|
|
||||||
}
|
|
||||||
|
|
||||||
return status;
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
absl::Status ReadHelper(T& var, int address) {
|
|
||||||
if constexpr (std::is_same_v<T, uint8_t>) {
|
|
||||||
ASSIGN_OR_RETURN(auto result, ReadByte(address));
|
|
||||||
var = result;
|
|
||||||
} else if constexpr (std::is_same_v<T, uint16_t>) {
|
|
||||||
ASSIGN_OR_RETURN(auto result, ReadWord(address));
|
|
||||||
var = result;
|
|
||||||
} else if constexpr (std::is_same_v<T, std::vector<uint8_t>>) {
|
|
||||||
ASSIGN_OR_RETURN(auto result, ReadByteVector(address, var.size()));
|
|
||||||
var = result;
|
|
||||||
}
|
|
||||||
return absl::OkStatus();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Loads 2bpp graphics from ROM data.
|
* @brief Loads 2bpp graphics from Rom data.
|
||||||
*
|
*
|
||||||
* This function loads 2bpp graphics from ROM data by iterating over a list of
|
* This function loads 2bpp graphics from Rom data by iterating over a list of
|
||||||
* sheet IDs, decompressing the sheet data, converting it to 8bpp format, and
|
* sheet IDs, decompressing the sheet data, converting it to 8bpp format, and
|
||||||
* appending the converted sheet data to a byte vector.
|
* appending the converted sheet data to a byte vector.
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
absl::StatusOr<Bytes> Load2BppGraphics();
|
absl::StatusOr<Bytes> Load2BppGraphics();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Loads the players 4bpp graphics sheet from Rom data.
|
||||||
|
*/
|
||||||
absl::Status LoadLinkGraphics();
|
absl::Status LoadLinkGraphics();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This function iterates over all graphics sheets in the ROM and loads them
|
* @brief This function iterates over all graphics sheets in the Rom and loads
|
||||||
* into memory. Depending on the sheet's index, it may be uncompressed or
|
* them into memory. Depending on the sheet's index, it may be uncompressed or
|
||||||
* compressed using the LC-LZ2 algorithm. The uncompressed sheets are 3 bits
|
* compressed using the LC-LZ2 algorithm. The uncompressed sheets are 3 bits
|
||||||
* per pixel (BPP), while the compressed sheets are 4 BPP. The loaded graphics
|
* per pixel (BPP), while the compressed sheets are 4 BPP. The loaded graphics
|
||||||
* data is converted to 8 BPP and stored in a bitmap.
|
* data is converted to 8 BPP and stored in a bitmap.
|
||||||
*
|
*
|
||||||
* The graphics sheets are divided into the following ranges:
|
* The graphics sheets are divided into the following ranges:
|
||||||
* 0-112 -> compressed 3bpp bgr -> (decompressed each) 0x600 chars
|
*
|
||||||
* 113-114 -> compressed 2bpp -> (decompressed each) 0x800 chars
|
* | Range | Compression Type | Decompressed Size | Number of Chars |
|
||||||
* 115-126 -> uncompressed 3bpp sprites -> (each) 0x600 chars
|
* |---------|------------------|------------------|-----------------|
|
||||||
* 127-217 -> compressed 3bpp sprites -> (decompressed each) 0x600 chars
|
* | 0-112 | Compressed 3bpp BGR | 0x600 chars | Decompressed each |
|
||||||
* 218-222 -> compressed 2bpp -> (decompressed each) 0x800 chars
|
* | 113-114 | Compressed 2bpp | 0x800 chars | Decompressed each |
|
||||||
|
* | 115-126 | Uncompressed 3bpp sprites | 0x600 chars | Each |
|
||||||
|
* | 127-217 | Compressed 3bpp sprites | 0x600 chars | Decompressed each |
|
||||||
|
* | 218-222 | Compressed 2bpp | 0x800 chars | Decompressed each |
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
absl::Status LoadAllGraphicsData();
|
absl::Status LoadAllGraphicsData();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Loads all the palettes for the game.
|
* Load Rom data from a file.
|
||||||
*
|
|
||||||
* This function loads all the palettes for the game, including overworld,
|
|
||||||
* HUD, armor, swords, shields, sprites, dungeon, grass, and 3D object
|
|
||||||
* palettes. It also adds the loaded palettes to their respective palette
|
|
||||||
* groups.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
absl::Status LoadAllPalettes();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Load ROM data from a file.
|
|
||||||
*
|
*
|
||||||
* @param filename The name of the file to load.
|
* @param filename The name of the file to load.
|
||||||
* @param z3_load Whether to load data specific to Zelda 3.
|
* @param z3_load Whether to load data specific to Zelda 3.
|
||||||
@@ -252,10 +177,10 @@ class ROM : public core::ExperimentFlags {
|
|||||||
absl::Status LoadFromBytes(const Bytes& data);
|
absl::Status LoadFromBytes(const Bytes& data);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Saves the ROM data to a file
|
* @brief Saves the Rom data to a file
|
||||||
*
|
*
|
||||||
* @param backup If true, creates a backup file with timestamp in its name
|
* @param backup If true, creates a backup file with timestamp in its name
|
||||||
* @param filename The name of the file to save the ROM data to
|
* @param filename The name of the file to save the Rom data to
|
||||||
*
|
*
|
||||||
* @return absl::Status Returns an OK status if the save was successful,
|
* @return absl::Status Returns an OK status if the save was successful,
|
||||||
* otherwise returns an error status
|
* otherwise returns an error status
|
||||||
@@ -264,22 +189,22 @@ class ROM : public core::ExperimentFlags {
|
|||||||
std::string filename = "");
|
std::string filename = "");
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Saves the given palette to the ROM if any of its colors have been modified.
|
* Saves the given palette to the Rom if any of its colors have been modified.
|
||||||
*
|
*
|
||||||
* @param index The index of the palette to save.
|
* @param index The index of the palette to save.
|
||||||
* @param group_name The name of the group containing the palette.
|
* @param group_name The name of the group containing the palette.
|
||||||
* @param palette The palette to save.
|
* @param palette The palette to save.
|
||||||
*/
|
*/
|
||||||
void SavePalette(int index, const std::string& group_name,
|
absl::Status SavePalette(int index, const std::string& group_name,
|
||||||
gfx::SnesPalette& palette);
|
gfx::SnesPalette& palette);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Saves all palettes in the ROM.
|
* @brief Saves all palettes in the Rom.
|
||||||
*
|
*
|
||||||
* This function iterates through all palette groups and all palettes in each
|
* This function iterates through all palette groups and all palettes in each
|
||||||
* group, and saves each palette using the SavePalette() function.
|
* group, and saves each palette using the SavePalette() function.
|
||||||
*/
|
*/
|
||||||
void SaveAllPalettes();
|
absl::Status SaveAllPalettes();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Updates a color in a specified palette group.
|
* @brief Updates a color in a specified palette group.
|
||||||
@@ -337,7 +262,7 @@ class ROM : public core::ExperimentFlags {
|
|||||||
return absl::InvalidArgumentError("Offset and length out of range");
|
return absl::InvalidArgumentError("Offset and length out of range");
|
||||||
}
|
}
|
||||||
std::vector<uint8_t> result;
|
std::vector<uint8_t> result;
|
||||||
for (int i = offset; i < length; i++) {
|
for (int i = offset; i < offset + length; i++) {
|
||||||
result.push_back(rom_data_[i]);
|
result.push_back(rom_data_[i]);
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
@@ -462,6 +387,28 @@ class ROM : public core::ExperimentFlags {
|
|||||||
return WriteShort(address, bgr);
|
return WriteShort(address, bgr);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template <typename... Args>
|
||||||
|
absl::Status WriteTransaction(Args... args) {
|
||||||
|
absl::Status status;
|
||||||
|
// Fold expression to apply the Write function on each argument
|
||||||
|
((status = WriteHelper(args)), ...);
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T, typename... Args>
|
||||||
|
absl::Status ReadTransaction(T& var, int address, Args&&... args) {
|
||||||
|
absl::Status status = ReadHelper<T>(var, address);
|
||||||
|
if (!status.ok()) {
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
|
if constexpr (sizeof...(args) > 0) {
|
||||||
|
status = ReadTransaction(std::forward<Args>(args)...);
|
||||||
|
}
|
||||||
|
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
void Expand(int size) {
|
void Expand(int size) {
|
||||||
rom_data_.resize(size);
|
rom_data_.resize(size);
|
||||||
size_ = size;
|
size_ = size;
|
||||||
@@ -497,15 +444,11 @@ class ROM : public core::ExperimentFlags {
|
|||||||
return core::SnesToPc(snes_addr);
|
return core::SnesToPc(snes_addr);
|
||||||
}
|
}
|
||||||
|
|
||||||
gfx::PaletteGroup palette_group(const std::string& group) {
|
auto palette_group() { return palette_groups_; }
|
||||||
return palette_groups_[group];
|
auto mutable_palette_group() { return &palette_groups_; }
|
||||||
}
|
auto dungeon_palette(int i) { return palette_groups_.dungeon_main[i]; }
|
||||||
auto mutable_palette_group(const std::string& group) {
|
|
||||||
return &palette_groups_[group];
|
|
||||||
}
|
|
||||||
auto dungeon_palette(int i) { return palette_groups_["dungeon_main"][i]; }
|
|
||||||
auto mutable_dungeon_palette(int i) {
|
auto mutable_dungeon_palette(int i) {
|
||||||
return palette_groups_["dungeon_main"].mutable_palette(i);
|
return palette_groups_.dungeon_main.mutable_palette(i);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Full graphical data for the game
|
// Full graphical data for the game
|
||||||
@@ -518,6 +461,7 @@ class ROM : public core::ExperimentFlags {
|
|||||||
}
|
}
|
||||||
auto bitmap_manager() { return graphics_manager_; }
|
auto bitmap_manager() { return graphics_manager_; }
|
||||||
auto mutable_bitmap_manager() { return &graphics_manager_; }
|
auto mutable_bitmap_manager() { return &graphics_manager_; }
|
||||||
|
auto link_graphics() { return link_graphics_; }
|
||||||
|
|
||||||
auto title() const { return title_; }
|
auto title() const { return title_; }
|
||||||
auto size() const { return size_; }
|
auto size() const { return size_; }
|
||||||
@@ -552,6 +496,18 @@ class ROM : public core::ExperimentFlags {
|
|||||||
renderer_ = renderer;
|
renderer_ = renderer;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
absl::Status CreateAndRenderBitmap(int width, int height, int depth,
|
||||||
|
const Bytes& data, gfx::Bitmap& bitmap,
|
||||||
|
gfx::SnesPalette& palette) {
|
||||||
|
bitmap.Create(width, height, depth, data);
|
||||||
|
RETURN_IF_ERROR(bitmap.ApplyPalette(palette));
|
||||||
|
RenderBitmap(&bitmap);
|
||||||
|
return absl::OkStatus();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Used to render a bitmap to the screen.
|
||||||
|
*/
|
||||||
void RenderBitmap(gfx::Bitmap* bitmap) {
|
void RenderBitmap(gfx::Bitmap* bitmap) {
|
||||||
if (flags()->kLoadTexturesAsStreaming) {
|
if (flags()->kLoadTexturesAsStreaming) {
|
||||||
bitmap->CreateTexture(renderer_.get());
|
bitmap->CreateTexture(renderer_.get());
|
||||||
@@ -560,6 +516,9 @@ class ROM : public core::ExperimentFlags {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Used to update a bitmap on the screen.
|
||||||
|
*/
|
||||||
void UpdateBitmap(gfx::Bitmap* bitmap, bool use_sdl_update = false) {
|
void UpdateBitmap(gfx::Bitmap* bitmap, bool use_sdl_update = false) {
|
||||||
if (flags()->kLoadTexturesAsStreaming) {
|
if (flags()->kLoadTexturesAsStreaming) {
|
||||||
bitmap->UpdateTexture(renderer_.get(), use_sdl_update);
|
bitmap->UpdateTexture(renderer_.get(), use_sdl_update);
|
||||||
@@ -579,8 +538,8 @@ class ROM : public core::ExperimentFlags {
|
|||||||
spriteset_ids.resize(144, std::vector<uint8_t>(4));
|
spriteset_ids.resize(144, std::vector<uint8_t>(4));
|
||||||
paletteset_ids.resize(72, std::vector<uint8_t>(4));
|
paletteset_ids.resize(72, std::vector<uint8_t>(4));
|
||||||
|
|
||||||
int gfxPointer = (rom_data_[gfx_groups_pointer + 1] << 8) +
|
int gfxPointer =
|
||||||
rom_data_[gfx_groups_pointer];
|
(rom_data_[kGfxGroupsPointer + 1] << 8) + rom_data_[kGfxGroupsPointer];
|
||||||
gfxPointer = core::SnesToPc(gfxPointer);
|
gfxPointer = core::SnesToPc(gfxPointer);
|
||||||
|
|
||||||
for (int i = 0; i < 37; i++) {
|
for (int i = 0; i < 37; i++) {
|
||||||
@@ -611,9 +570,9 @@ class ROM : public core::ExperimentFlags {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool SaveGroupsToROM() {
|
bool SaveGroupsToRom() {
|
||||||
int gfxPointer = (rom_data_[gfx_groups_pointer + 1] << 8) +
|
int gfxPointer =
|
||||||
rom_data_[gfx_groups_pointer];
|
(rom_data_[kGfxGroupsPointer + 1] << 8) + rom_data_[kGfxGroupsPointer];
|
||||||
gfxPointer = core::SnesToPc(gfxPointer);
|
gfxPointer = core::SnesToPc(gfxPointer);
|
||||||
|
|
||||||
for (int i = 0; i < 37; i++) {
|
for (int i = 0; i < 37; i++) {
|
||||||
@@ -649,6 +608,50 @@ class ROM : public core::ExperimentFlags {
|
|||||||
auto resource_label() { return &resource_label_manager_; }
|
auto resource_label() { return &resource_label_manager_; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
struct WriteAction {
|
||||||
|
int address;
|
||||||
|
std::variant<int, uint8_t, uint16_t, short, std::vector<uint8_t>,
|
||||||
|
gfx::SnesColor, std::vector<gfx::SnesColor>>
|
||||||
|
value;
|
||||||
|
};
|
||||||
|
|
||||||
|
absl::Status WriteHelper(const WriteAction& action) {
|
||||||
|
if (std::holds_alternative<uint8_t>(action.value)) {
|
||||||
|
return Write(action.address, std::get<uint8_t>(action.value));
|
||||||
|
} else if (std::holds_alternative<uint16_t>(action.value) ||
|
||||||
|
std::holds_alternative<short>(action.value)) {
|
||||||
|
return WriteShort(action.address, std::get<uint16_t>(action.value));
|
||||||
|
} else if (std::holds_alternative<std::vector<uint8_t>>(action.value)) {
|
||||||
|
return WriteVector(action.address,
|
||||||
|
std::get<std::vector<uint8_t>>(action.value));
|
||||||
|
} else if (std::holds_alternative<gfx::SnesColor>(action.value)) {
|
||||||
|
return WriteColor(action.address, std::get<gfx::SnesColor>(action.value));
|
||||||
|
} else if (std::holds_alternative<std::vector<gfx::SnesColor>>(
|
||||||
|
action.value)) {
|
||||||
|
return absl::UnimplementedError(
|
||||||
|
"WriteHelper: std::vector<gfx::SnesColor>");
|
||||||
|
}
|
||||||
|
auto error_message = absl::StrFormat("Invalid write argument type: %s",
|
||||||
|
typeid(action.value).name());
|
||||||
|
throw std::runtime_error(error_message);
|
||||||
|
return absl::InvalidArgumentError(error_message);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
absl::Status ReadHelper(T& var, int address) {
|
||||||
|
if constexpr (std::is_same_v<T, uint8_t>) {
|
||||||
|
ASSIGN_OR_RETURN(auto result, ReadByte(address));
|
||||||
|
var = result;
|
||||||
|
} else if constexpr (std::is_same_v<T, uint16_t>) {
|
||||||
|
ASSIGN_OR_RETURN(auto result, ReadWord(address));
|
||||||
|
var = result;
|
||||||
|
} else if constexpr (std::is_same_v<T, std::vector<uint8_t>>) {
|
||||||
|
ASSIGN_OR_RETURN(auto result, ReadByteVector(address, var.size()));
|
||||||
|
var = result;
|
||||||
|
}
|
||||||
|
return absl::OkStatus();
|
||||||
|
}
|
||||||
|
|
||||||
long size_ = 0;
|
long size_ = 0;
|
||||||
bool is_loaded_ = false;
|
bool is_loaded_ = false;
|
||||||
bool has_header_ = false;
|
bool has_header_ = false;
|
||||||
@@ -663,35 +666,38 @@ class ROM : public core::ExperimentFlags {
|
|||||||
gfx::BitmapManager graphics_manager_;
|
gfx::BitmapManager graphics_manager_;
|
||||||
gfx::BitmapTable link_graphics_;
|
gfx::BitmapTable link_graphics_;
|
||||||
gfx::SnesPalette link_palette_;
|
gfx::SnesPalette link_palette_;
|
||||||
PaletteGroupMap palette_groups_;
|
gfx::PaletteGroupMap palette_groups_;
|
||||||
core::ResourceLabelManager resource_label_manager_;
|
core::ResourceLabelManager resource_label_manager_;
|
||||||
|
|
||||||
std::stack<std::function<void()>> changes_;
|
std::stack<std::function<void()>> changes_;
|
||||||
std::shared_ptr<SDL_Renderer> renderer_;
|
std::shared_ptr<SDL_Renderer> renderer_;
|
||||||
};
|
};
|
||||||
|
|
||||||
class SharedROM {
|
/**
|
||||||
|
* @brief A class to hold a shared pointer to a Rom object.
|
||||||
|
*/
|
||||||
|
class SharedRom {
|
||||||
public:
|
public:
|
||||||
SharedROM() = default;
|
SharedRom() = default;
|
||||||
virtual ~SharedROM() = default;
|
virtual ~SharedRom() = default;
|
||||||
|
|
||||||
std::shared_ptr<ROM> shared_rom() {
|
std::shared_ptr<Rom> shared_rom() {
|
||||||
if (!shared_rom_) {
|
if (!shared_rom_) {
|
||||||
shared_rom_ = std::make_shared<ROM>();
|
shared_rom_ = std::make_shared<Rom>();
|
||||||
}
|
}
|
||||||
return shared_rom_;
|
return shared_rom_;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto rom() {
|
auto rom() {
|
||||||
if (!shared_rom_) {
|
if (!shared_rom_) {
|
||||||
shared_rom_ = std::make_shared<ROM>();
|
shared_rom_ = std::make_shared<Rom>();
|
||||||
}
|
}
|
||||||
ROM* rom = shared_rom_.get();
|
Rom* rom = shared_rom_.get();
|
||||||
return rom;
|
return rom;
|
||||||
}
|
}
|
||||||
|
|
||||||
// private:
|
// private:
|
||||||
static std::shared_ptr<ROM> shared_rom_;
|
static std::shared_ptr<Rom> shared_rom_;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace app
|
} // namespace app
|
||||||
|
|||||||
@@ -8,6 +8,15 @@
|
|||||||
#include "absl/debugging/symbolize.h"
|
#include "absl/debugging/symbolize.h"
|
||||||
#include "app/core/controller.h"
|
#include "app/core/controller.h"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @namespace yaze::app
|
||||||
|
* @brief Main namespace for the ImGui application.
|
||||||
|
*/
|
||||||
|
using namespace yaze::app;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Main entry point for the application.
|
||||||
|
*/
|
||||||
int main(int argc, char** argv) {
|
int main(int argc, char** argv) {
|
||||||
absl::InitializeSymbolizer(argv[0]);
|
absl::InitializeSymbolizer(argv[0]);
|
||||||
|
|
||||||
@@ -16,7 +25,7 @@ int main(int argc, char** argv) {
|
|||||||
options.alarm_on_failure_secs = true;
|
options.alarm_on_failure_secs = true;
|
||||||
absl::InstallFailureSignalHandler(options);
|
absl::InstallFailureSignalHandler(options);
|
||||||
|
|
||||||
yaze::app::core::Controller controller;
|
core::Controller controller;
|
||||||
EXIT_IF_ERROR(controller.OnEntry())
|
EXIT_IF_ERROR(controller.OnEntry())
|
||||||
|
|
||||||
#ifdef __APPLE__
|
#ifdef __APPLE__
|
||||||
|
|||||||
@@ -3,8 +3,16 @@
|
|||||||
|
|
||||||
namespace yaze {
|
namespace yaze {
|
||||||
namespace app {
|
namespace app {
|
||||||
|
/**
|
||||||
|
* @namespace yaze::app::zelda3
|
||||||
|
* @brief Zelda 3 specific classes and functions.
|
||||||
|
*/
|
||||||
namespace zelda3 {
|
namespace zelda3 {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @class OverworldEntity
|
||||||
|
* @brief Base class for all overworld entities.
|
||||||
|
*/
|
||||||
class OverworldEntity {
|
class OverworldEntity {
|
||||||
public:
|
public:
|
||||||
enum EntityType {
|
enum EntityType {
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ struct SubtypeInfo {
|
|||||||
uint32_t routine_ptr;
|
uint32_t routine_ptr;
|
||||||
};
|
};
|
||||||
|
|
||||||
class DungeonObjectRenderer : public SharedROM {
|
class DungeonObjectRenderer : public SharedRom {
|
||||||
public:
|
public:
|
||||||
DungeonObjectRenderer() = default;
|
DungeonObjectRenderer() = default;
|
||||||
|
|
||||||
@@ -46,10 +46,10 @@ class DungeonObjectRenderer : public SharedROM {
|
|||||||
std::vector<uint8_t> tilemap_;
|
std::vector<uint8_t> tilemap_;
|
||||||
uint16_t pc_with_rts_;
|
uint16_t pc_with_rts_;
|
||||||
std::vector<uint8_t> rom_data_;
|
std::vector<uint8_t> rom_data_;
|
||||||
emu::MemoryImpl memory_;
|
emu::memory::MemoryImpl memory_;
|
||||||
emu::ClockImpl clock_;
|
emu::ClockImpl clock_;
|
||||||
emu::CPU cpu{memory_, clock_};
|
emu::Cpu cpu{memory_, clock_};
|
||||||
emu::Ppu ppu{memory_, clock_};
|
emu::video::Ppu ppu{memory_, clock_};
|
||||||
gfx::Bitmap bitmap_;
|
gfx::Bitmap bitmap_;
|
||||||
PseudoVram vram_;
|
PseudoVram vram_;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -116,7 +116,7 @@ struct ChestData {
|
|||||||
|
|
||||||
struct StaircaseRooms {};
|
struct StaircaseRooms {};
|
||||||
|
|
||||||
class Room : public SharedROM, public core::ExperimentFlags {
|
class Room : public SharedRom, public core::ExperimentFlags {
|
||||||
public:
|
public:
|
||||||
Room() = default;
|
Room() = default;
|
||||||
Room(int room_id) : room_id_(room_id) {}
|
Room(int room_id) : room_id_(room_id) {}
|
||||||
|
|||||||
@@ -103,7 +103,7 @@ class RoomEntrance {
|
|||||||
public:
|
public:
|
||||||
RoomEntrance() = default;
|
RoomEntrance() = default;
|
||||||
|
|
||||||
RoomEntrance(ROM& rom, uint8_t entrance_id, bool is_spawn_point = false)
|
RoomEntrance(Rom& rom, uint8_t entrance_id, bool is_spawn_point = false)
|
||||||
: entrance_id_(entrance_id) {
|
: entrance_id_(entrance_id) {
|
||||||
room_ =
|
room_ =
|
||||||
static_cast<short>((rom[entrance_room + (entrance_id * 2) + 1] << 8) +
|
static_cast<short>((rom[entrance_room + (entrance_id * 2) + 1] << 8) +
|
||||||
@@ -209,7 +209,7 @@ class RoomEntrance {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Save(ROM& rom, int entrance_id, bool is_spawn_point = false) {
|
void Save(Rom& rom, int entrance_id, bool is_spawn_point = false) {
|
||||||
if (!is_spawn_point) {
|
if (!is_spawn_point) {
|
||||||
rom.WriteShort(entrance_room + (entrance_id * 2), room_);
|
rom.WriteShort(entrance_room + (entrance_id * 2), room_);
|
||||||
rom.WriteShort(entrance_yposition + (entrance_id * 2), y_position_);
|
rom.WriteShort(entrance_yposition + (entrance_id * 2), y_position_);
|
||||||
|
|||||||
@@ -58,7 +58,7 @@ enum ObjectOption {
|
|||||||
Stairs = 32
|
Stairs = 32
|
||||||
};
|
};
|
||||||
|
|
||||||
class RoomObject : public SharedROM {
|
class RoomObject : public SharedRom {
|
||||||
public:
|
public:
|
||||||
enum LayerType { BG1 = 0, BG2 = 1, BG3 = 2 };
|
enum LayerType { BG1 = 0, BG2 = 1, BG3 = 2 };
|
||||||
|
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ namespace zelda3 {
|
|||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
void AddSPCReloc(SongSPCBlock *sbl, short addr) {
|
void AddSPCReloc(music::SongSPCBlock *sbl, short addr) {
|
||||||
sbl->relocs[sbl->relnum++] = addr;
|
sbl->relocs[sbl->relnum++] = addr;
|
||||||
if (sbl->relnum == sbl->relsz) {
|
if (sbl->relnum == sbl->relsz) {
|
||||||
sbl->relsz += 16;
|
sbl->relsz += 16;
|
||||||
@@ -38,8 +38,7 @@ void AddSPCReloc(SongSPCBlock *sbl, short addr) {
|
|||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
// =============================================================================
|
namespace music {
|
||||||
|
|
||||||
SongSPCBlock *Tracker::AllocSPCBlock(int len, int bank) {
|
SongSPCBlock *Tracker::AllocSPCBlock(int len, int bank) {
|
||||||
SongSPCBlock *sbl;
|
SongSPCBlock *sbl;
|
||||||
if (!len) {
|
if (!len) {
|
||||||
@@ -65,7 +64,7 @@ SongSPCBlock *Tracker::AllocSPCBlock(int len, int bank) {
|
|||||||
|
|
||||||
// =============================================================================
|
// =============================================================================
|
||||||
|
|
||||||
unsigned char *Tracker::GetSPCAddr(ROM &rom, unsigned short addr, short bank) {
|
unsigned char *Tracker::GetSPCAddr(Rom &rom, unsigned short addr, short bank) {
|
||||||
unsigned char *rom_ptr;
|
unsigned char *rom_ptr;
|
||||||
unsigned short a;
|
unsigned short a;
|
||||||
unsigned short b;
|
unsigned short b;
|
||||||
@@ -127,7 +126,7 @@ short Tracker::AllocSPCCommand() {
|
|||||||
|
|
||||||
// =============================================================================
|
// =============================================================================
|
||||||
|
|
||||||
short Tracker::GetBlockTime(ROM &rom, short num, short prevtime) {
|
short Tracker::GetBlockTime(Rom &rom, short num, short prevtime) {
|
||||||
SPCCommand *spc_command = current_spc_command_;
|
SPCCommand *spc_command = current_spc_command_;
|
||||||
SPCCommand *spc_command2;
|
SPCCommand *spc_command2;
|
||||||
|
|
||||||
@@ -221,7 +220,7 @@ short Tracker::GetBlockTime(ROM &rom, short num, short prevtime) {
|
|||||||
|
|
||||||
// =============================================================================
|
// =============================================================================
|
||||||
|
|
||||||
short Tracker::LoadSPCCommand(ROM &rom, unsigned short addr, short bank,
|
short Tracker::LoadSPCCommand(Rom &rom, unsigned short addr, short bank,
|
||||||
int t) {
|
int t) {
|
||||||
int b = 0;
|
int b = 0;
|
||||||
int c = 0;
|
int c = 0;
|
||||||
@@ -392,7 +391,7 @@ short Tracker::LoadSPCCommand(ROM &rom, unsigned short addr, short bank,
|
|||||||
|
|
||||||
// =============================================================================
|
// =============================================================================
|
||||||
|
|
||||||
void Tracker::LoadSongs(ROM &rom) {
|
void Tracker::LoadSongs(Rom &rom) {
|
||||||
unsigned char *b;
|
unsigned char *b;
|
||||||
unsigned char *c;
|
unsigned char *c;
|
||||||
unsigned char *d;
|
unsigned char *d;
|
||||||
@@ -662,7 +661,7 @@ void Tracker::LoadSongs(ROM &rom) {
|
|||||||
w_modf = 0;
|
w_modf = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
short Tracker::SaveSPCCommand(ROM &rom, short num, short songtime,
|
short Tracker::SaveSPCCommand(Rom &rom, short num, short songtime,
|
||||||
short endtr) {
|
short endtr) {
|
||||||
SPCCommand *spc_command = current_spc_command_;
|
SPCCommand *spc_command = current_spc_command_;
|
||||||
SPCCommand *spc_command2;
|
SPCCommand *spc_command2;
|
||||||
@@ -785,7 +784,7 @@ short Tracker::SaveSPCCommand(ROM &rom, short num, short songtime,
|
|||||||
|
|
||||||
// =============================================================================
|
// =============================================================================
|
||||||
|
|
||||||
int Tracker::WriteSPCData(ROM &rom, void *buf, int len, int addr, int spc,
|
int Tracker::WriteSPCData(Rom &rom, void *buf, int len, int addr, int spc,
|
||||||
int limit) {
|
int limit) {
|
||||||
unsigned char *rom_data = rom.data();
|
unsigned char *rom_data = rom.data();
|
||||||
|
|
||||||
@@ -815,7 +814,7 @@ int Tracker::WriteSPCData(ROM &rom, void *buf, int len, int addr, int spc,
|
|||||||
|
|
||||||
// =============================================================================
|
// =============================================================================
|
||||||
|
|
||||||
void Tracker::SaveSongs(ROM &rom) {
|
void Tracker::SaveSongs(Rom &rom) {
|
||||||
int i;
|
int i;
|
||||||
int j;
|
int j;
|
||||||
int k;
|
int k;
|
||||||
@@ -1265,7 +1264,7 @@ void Tracker::SaveSongs(ROM &rom) {
|
|||||||
|
|
||||||
// =============================================================================
|
// =============================================================================
|
||||||
|
|
||||||
void Tracker::EditTrack(ROM &rom, short i) {
|
void Tracker::EditTrack(Rom &rom, short i) {
|
||||||
int j, k, l;
|
int j, k, l;
|
||||||
SongRange *sr = song_range_;
|
SongRange *sr = song_range_;
|
||||||
SPCCommand *spc_command;
|
SPCCommand *spc_command;
|
||||||
@@ -1315,7 +1314,7 @@ void Tracker::EditTrack(ROM &rom, short i) {
|
|||||||
// CRITICAL_SECTION cs_song;
|
// CRITICAL_SECTION cs_song;
|
||||||
// =============================================================================
|
// =============================================================================
|
||||||
|
|
||||||
void Tracker::NewSR(ROM &rom, int bank) {
|
void Tracker::NewSR(Rom &rom, int bank) {
|
||||||
SPCCommand *spc_command;
|
SPCCommand *spc_command;
|
||||||
SongRange *sr;
|
SongRange *sr;
|
||||||
|
|
||||||
@@ -1337,8 +1336,7 @@ void Tracker::NewSR(ROM &rom, int bank) {
|
|||||||
EditTrack(rom, sr->first);
|
EditTrack(rom, sr->first);
|
||||||
}
|
}
|
||||||
|
|
||||||
// =============================================================================
|
} // namespace music
|
||||||
|
|
||||||
} // namespace zelda3
|
} // namespace zelda3
|
||||||
} // namespace app
|
} // namespace app
|
||||||
} // namespace yaze
|
} // namespace yaze
|
||||||
|
|||||||
@@ -18,6 +18,14 @@ namespace yaze {
|
|||||||
namespace app {
|
namespace app {
|
||||||
namespace zelda3 {
|
namespace zelda3 {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @namespace yaze::app::zelda3::music
|
||||||
|
* @brief Contains classes and functions for handling music data in Zelda 3.
|
||||||
|
*
|
||||||
|
* Based off of the HyruleMagic tracker code.
|
||||||
|
*/
|
||||||
|
namespace music {
|
||||||
|
|
||||||
// bank 19, 1A, 1B
|
// bank 19, 1A, 1B
|
||||||
// iirc 1A is OW, 1B is dungeon
|
// iirc 1A is OW, 1B is dungeon
|
||||||
// 19 is general spc stuff like samples, ects
|
// 19 is general spc stuff like samples, ects
|
||||||
@@ -84,7 +92,7 @@ using Song = struct {
|
|||||||
short numparts;
|
short numparts;
|
||||||
short lopst;
|
short lopst;
|
||||||
unsigned short addr;
|
unsigned short addr;
|
||||||
bool in_use; // true
|
bool in_use; // true
|
||||||
};
|
};
|
||||||
// =============================================================================
|
// =============================================================================
|
||||||
|
|
||||||
@@ -168,24 +176,24 @@ class Tracker {
|
|||||||
public:
|
public:
|
||||||
SongSPCBlock *AllocSPCBlock(int len, int bank);
|
SongSPCBlock *AllocSPCBlock(int len, int bank);
|
||||||
|
|
||||||
unsigned char *GetSPCAddr(ROM &rom, unsigned short addr, short bank);
|
unsigned char *GetSPCAddr(Rom &rom, unsigned short addr, short bank);
|
||||||
|
|
||||||
short AllocSPCCommand();
|
short AllocSPCCommand();
|
||||||
|
|
||||||
short GetBlockTime(ROM &rom, short num, short prevtime);
|
short GetBlockTime(Rom &rom, short num, short prevtime);
|
||||||
|
|
||||||
short SaveSPCCommand(ROM &rom, short num, short songtime, short endtr);
|
short SaveSPCCommand(Rom &rom, short num, short songtime, short endtr);
|
||||||
short LoadSPCCommand(ROM &rom, unsigned short addr, short bank, int t);
|
short LoadSPCCommand(Rom &rom, unsigned short addr, short bank, int t);
|
||||||
|
|
||||||
void SaveSongs(ROM &rom);
|
void SaveSongs(Rom &rom);
|
||||||
|
|
||||||
void LoadSongs(ROM &rom);
|
void LoadSongs(Rom &rom);
|
||||||
|
|
||||||
int WriteSPCData(ROM &rom, void *buf, int len, int addr, int spc, int limit);
|
int WriteSPCData(Rom &rom, void *buf, int len, int addr, int spc, int limit);
|
||||||
|
|
||||||
void EditTrack(ROM &rom, short i);
|
void EditTrack(Rom &rom, short i);
|
||||||
|
|
||||||
void NewSR(ROM &rom, int bank);
|
void NewSR(Rom &rom, int bank);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// A "modified" flag
|
// A "modified" flag
|
||||||
@@ -252,6 +260,7 @@ class Tracker {
|
|||||||
|
|
||||||
// =============================================================================
|
// =============================================================================
|
||||||
|
|
||||||
|
} // namespace music
|
||||||
} // namespace zelda3
|
} // namespace zelda3
|
||||||
} // namespace app
|
} // namespace app
|
||||||
} // namespace yaze
|
} // namespace yaze
|
||||||
|
|||||||
@@ -17,12 +17,13 @@
|
|||||||
#include "app/gfx/compression.h"
|
#include "app/gfx/compression.h"
|
||||||
#include "app/gfx/snes_tile.h"
|
#include "app/gfx/snes_tile.h"
|
||||||
#include "app/rom.h"
|
#include "app/rom.h"
|
||||||
#include "app/zelda3/overworld_map.h"
|
#include "app/zelda3/overworld/overworld_map.h"
|
||||||
#include "app/zelda3/sprite/sprite.h"
|
#include "app/zelda3/sprite/sprite.h"
|
||||||
|
|
||||||
namespace yaze {
|
namespace yaze {
|
||||||
namespace app {
|
namespace app {
|
||||||
namespace zelda3 {
|
namespace zelda3 {
|
||||||
|
namespace overworld {
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
@@ -79,7 +80,7 @@ absl::flat_hash_map<int, MapData> parseFile(const std::string &filename) {
|
|||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
absl::Status Overworld::Load(ROM &rom) {
|
absl::Status Overworld::Load(Rom &rom) {
|
||||||
rom_ = rom;
|
rom_ = rom;
|
||||||
|
|
||||||
AssembleMap32Tiles();
|
AssembleMap32Tiles();
|
||||||
@@ -311,11 +312,11 @@ absl::Status Overworld::LoadOverworldMaps() {
|
|||||||
} else if (i >= 0x80) {
|
} else if (i >= 0x80) {
|
||||||
world_type = 2;
|
world_type = 2;
|
||||||
}
|
}
|
||||||
futures.emplace_back(
|
auto task_function = [this, i, size, world_type]() {
|
||||||
std::async(std::launch::async, [this, i, size, world_type]() {
|
return overworld_maps_[i].BuildMap(size, game_state_, world_type,
|
||||||
return overworld_maps_[i].BuildMap(size, game_state_, world_type,
|
GetMapTiles(world_type));
|
||||||
GetMapTiles(world_type));
|
};
|
||||||
}));
|
futures.emplace_back(std::async(std::launch::async, task_function));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Wait for all tasks to complete and check their results
|
// Wait for all tasks to complete and check their results
|
||||||
@@ -427,7 +428,7 @@ absl::Status Overworld::LoadExits() {
|
|||||||
|
|
||||||
absl::Status Overworld::LoadItems() {
|
absl::Status Overworld::LoadItems() {
|
||||||
ASSIGN_OR_RETURN(uint32_t pointer,
|
ASSIGN_OR_RETURN(uint32_t pointer,
|
||||||
rom()->ReadLong(zelda3::overworldItemsAddress));
|
rom()->ReadLong(zelda3::overworld::kOverworldItemsAddress));
|
||||||
uint32_t pointer_pc = core::SnesToPc(pointer); // 1BC2F9 -> 0DC2F9
|
uint32_t pointer_pc = core::SnesToPc(pointer); // 1BC2F9 -> 0DC2F9
|
||||||
for (int i = 0; i < 128; i++) {
|
for (int i = 0; i < 128; i++) {
|
||||||
ASSIGN_OR_RETURN(uint16_t word_address,
|
ASSIGN_OR_RETURN(uint16_t word_address,
|
||||||
@@ -526,7 +527,7 @@ absl::Status Overworld::LoadSpritesFromMap(int sprite_start, int sprite_count,
|
|||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
absl::Status Overworld::Save(ROM &rom) {
|
absl::Status Overworld::Save(Rom &rom) {
|
||||||
rom_ = rom;
|
rom_ = rom;
|
||||||
|
|
||||||
RETURN_IF_ERROR(SaveMap16Tiles())
|
RETURN_IF_ERROR(SaveMap16Tiles())
|
||||||
@@ -1512,7 +1513,7 @@ absl::Status Overworld::DecompressProtoMapTiles(const std::string &filename) {
|
|||||||
return absl::OkStatus();
|
return absl::OkStatus();
|
||||||
}
|
}
|
||||||
|
|
||||||
absl::Status Overworld::LoadPrototype(ROM &rom,
|
absl::Status Overworld::LoadPrototype(Rom &rom,
|
||||||
const std::string &tilemap_filename) {
|
const std::string &tilemap_filename) {
|
||||||
rom_ = rom;
|
rom_ = rom;
|
||||||
|
|
||||||
@@ -1568,6 +1569,7 @@ OWBlockset &Overworld::GetMapTiles(int world_type) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
} // namespace overworld
|
||||||
} // namespace zelda3
|
} // namespace zelda3
|
||||||
} // namespace app
|
} // namespace app
|
||||||
} // namespace yaze
|
} // namespace yaze
|
||||||
@@ -15,13 +15,19 @@
|
|||||||
#include "app/gfx/snes_tile.h"
|
#include "app/gfx/snes_tile.h"
|
||||||
#include "app/rom.h"
|
#include "app/rom.h"
|
||||||
#include "app/zelda3/common.h"
|
#include "app/zelda3/common.h"
|
||||||
#include "app/zelda3/overworld_map.h"
|
#include "app/zelda3/overworld/overworld_map.h"
|
||||||
#include "app/zelda3/sprite/sprite.h"
|
#include "app/zelda3/sprite/sprite.h"
|
||||||
|
|
||||||
namespace yaze {
|
namespace yaze {
|
||||||
namespace app {
|
namespace app {
|
||||||
namespace zelda3 {
|
namespace zelda3 {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @namespace yaze::app::zelda3::overworld
|
||||||
|
* @brief Represents the Overworld data.
|
||||||
|
*/
|
||||||
|
namespace overworld {
|
||||||
|
|
||||||
// List of secret item names
|
// List of secret item names
|
||||||
const std::vector<std::string> kSecretItemNames = {
|
const std::vector<std::string> kSecretItemNames = {
|
||||||
"Nothing", // 0
|
"Nothing", // 0
|
||||||
@@ -55,7 +61,7 @@ const std::vector<std::string> kSecretItemNames = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
constexpr int overworldItemsPointers = 0xDC2F9;
|
constexpr int overworldItemsPointers = 0xDC2F9;
|
||||||
constexpr int overworldItemsAddress = 0xDC8B9; // 1BC2F9
|
constexpr int kOverworldItemsAddress = 0xDC8B9; // 1BC2F9
|
||||||
constexpr int overworldItemsBank = 0xDC8BF;
|
constexpr int overworldItemsBank = 0xDC8BF;
|
||||||
constexpr int overworldItemsEndData = 0xDC89C; // 0DC89E
|
constexpr int overworldItemsEndData = 0xDC89C; // 0DC89E
|
||||||
|
|
||||||
@@ -468,10 +474,16 @@ struct MapData {
|
|||||||
std::vector<uint8_t> lowData;
|
std::vector<uint8_t> lowData;
|
||||||
};
|
};
|
||||||
|
|
||||||
class Overworld : public SharedROM, public core::ExperimentFlags {
|
/**
|
||||||
|
* @brief Represents the full Overworld data, light and dark world.
|
||||||
|
*
|
||||||
|
* This class is responsible for loading and saving the overworld data,
|
||||||
|
* as well as creating the tilesets and tilemaps for the overworld.
|
||||||
|
*/
|
||||||
|
class Overworld : public SharedRom, public core::ExperimentFlags {
|
||||||
public:
|
public:
|
||||||
OWBlockset &GetMapTiles(int world_type);
|
OWBlockset &GetMapTiles(int world_type);
|
||||||
absl::Status Load(ROM &rom);
|
absl::Status Load(Rom &rom);
|
||||||
absl::Status LoadOverworldMaps();
|
absl::Status LoadOverworldMaps();
|
||||||
void LoadTileTypes();
|
void LoadTileTypes();
|
||||||
void LoadEntrances();
|
void LoadEntrances();
|
||||||
@@ -482,7 +494,7 @@ class Overworld : public SharedROM, public core::ExperimentFlags {
|
|||||||
absl::Status LoadSpritesFromMap(int spriteStart, int spriteCount,
|
absl::Status LoadSpritesFromMap(int spriteStart, int spriteCount,
|
||||||
int spriteIndex);
|
int spriteIndex);
|
||||||
|
|
||||||
absl::Status Save(ROM &rom);
|
absl::Status Save(Rom &rom);
|
||||||
absl::Status SaveOverworldMaps();
|
absl::Status SaveOverworldMaps();
|
||||||
absl::Status SaveLargeMaps();
|
absl::Status SaveLargeMaps();
|
||||||
absl::Status SaveEntrances();
|
absl::Status SaveEntrances();
|
||||||
@@ -494,7 +506,7 @@ class Overworld : public SharedROM, public core::ExperimentFlags {
|
|||||||
absl::Status SaveMap32Tiles();
|
absl::Status SaveMap32Tiles();
|
||||||
|
|
||||||
absl::Status SaveMapProperties();
|
absl::Status SaveMapProperties();
|
||||||
absl::Status LoadPrototype(ROM &rom_, const std::string &tilemap_filename);
|
absl::Status LoadPrototype(Rom &rom_, const std::string &tilemap_filename);
|
||||||
|
|
||||||
void Destroy() {
|
void Destroy() {
|
||||||
for (auto &map : overworld_maps_) {
|
for (auto &map : overworld_maps_) {
|
||||||
@@ -585,7 +597,7 @@ class Overworld : public SharedROM, public core::ExperimentFlags {
|
|||||||
int current_map_ = 0;
|
int current_map_ = 0;
|
||||||
uchar map_parent_[160];
|
uchar map_parent_[160];
|
||||||
|
|
||||||
ROM rom_;
|
Rom rom_;
|
||||||
OWMapTiles map_tiles_;
|
OWMapTiles map_tiles_;
|
||||||
|
|
||||||
uint8_t all_tiles_types_[0x200];
|
uint8_t all_tiles_types_[0x200];
|
||||||
@@ -618,6 +630,7 @@ class Overworld : public SharedROM, public core::ExperimentFlags {
|
|||||||
absl::flat_hash_map<int, MapData> proto_map_data_;
|
absl::flat_hash_map<int, MapData> proto_map_data_;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
} // namespace overworld
|
||||||
} // namespace zelda3
|
} // namespace zelda3
|
||||||
} // namespace app
|
} // namespace app
|
||||||
} // namespace yaze
|
} // namespace yaze
|
||||||
@@ -13,13 +13,14 @@
|
|||||||
#include "app/gfx/bitmap.h"
|
#include "app/gfx/bitmap.h"
|
||||||
#include "app/gfx/snes_tile.h"
|
#include "app/gfx/snes_tile.h"
|
||||||
#include "app/rom.h"
|
#include "app/rom.h"
|
||||||
#include "app/zelda3/overworld.h"
|
#include "app/zelda3/overworld/overworld.h"
|
||||||
|
|
||||||
namespace yaze {
|
namespace yaze {
|
||||||
namespace app {
|
namespace app {
|
||||||
namespace zelda3 {
|
namespace zelda3 {
|
||||||
|
namespace overworld {
|
||||||
|
|
||||||
OverworldMap::OverworldMap(int index, ROM& rom,
|
OverworldMap::OverworldMap(int index, Rom& rom,
|
||||||
std::vector<gfx::Tile16>& tiles16)
|
std::vector<gfx::Tile16>& tiles16)
|
||||||
: parent_(index), index_(index), rom_(rom), tiles16_(tiles16) {
|
: parent_(index), index_(index), rom_(rom), tiles16_(tiles16) {
|
||||||
LoadAreaInfo();
|
LoadAreaInfo();
|
||||||
@@ -49,7 +50,7 @@ absl::Status OverworldMap::BuildMap(int count, int game_state, int world,
|
|||||||
LoadAreaGraphics();
|
LoadAreaGraphics();
|
||||||
RETURN_IF_ERROR(BuildTileset())
|
RETURN_IF_ERROR(BuildTileset())
|
||||||
RETURN_IF_ERROR(BuildTiles16Gfx(count))
|
RETURN_IF_ERROR(BuildTiles16Gfx(count))
|
||||||
LoadPalette();
|
RETURN_IF_ERROR(LoadPalette());
|
||||||
RETURN_IF_ERROR(BuildBitmap(world_blockset))
|
RETURN_IF_ERROR(BuildBitmap(world_blockset))
|
||||||
built_ = true;
|
built_ = true;
|
||||||
return absl::OkStatus();
|
return absl::OkStatus();
|
||||||
@@ -229,11 +230,11 @@ void OverworldMap::LoadAreaGraphics() {
|
|||||||
|
|
||||||
namespace palette_internal {
|
namespace palette_internal {
|
||||||
|
|
||||||
void SetColorsPalette(ROM& rom, int index, gfx::SnesPalette& current,
|
absl::Status SetColorsPalette(Rom& rom, int index, gfx::SnesPalette& current,
|
||||||
gfx::SnesPalette main, gfx::SnesPalette animated,
|
gfx::SnesPalette main, gfx::SnesPalette animated,
|
||||||
gfx::SnesPalette aux1, gfx::SnesPalette aux2,
|
gfx::SnesPalette aux1, gfx::SnesPalette aux2,
|
||||||
gfx::SnesPalette hud, gfx::SnesColor bgrcolor,
|
gfx::SnesPalette hud, gfx::SnesColor bgrcolor,
|
||||||
gfx::SnesPalette spr, gfx::SnesPalette spr2) {
|
gfx::SnesPalette spr, gfx::SnesPalette spr2) {
|
||||||
// Palettes infos, color 0 of a palette is always transparent (the arrays
|
// Palettes infos, color 0 of a palette is always transparent (the arrays
|
||||||
// contains 7 colors width wide) There is 16 color per line so 16*Y
|
// contains 7 colors width wide) There is 16 color per line so 16*Y
|
||||||
|
|
||||||
@@ -289,7 +290,8 @@ void SetColorsPalette(ROM& rom, int index, gfx::SnesPalette& current,
|
|||||||
k = 0;
|
k = 0;
|
||||||
for (int y = 8; y < 9; y++) {
|
for (int y = 8; y < 9; y++) {
|
||||||
for (int x = 1; x < 8; x++) {
|
for (int x = 1; x < 8; x++) {
|
||||||
new_palette[x + (16 * y)] = rom.palette_group("sprites_aux1")[1][k];
|
auto pal_group = rom.palette_group().sprites_aux1;
|
||||||
|
new_palette[x + (16 * y)] = pal_group[1][k];
|
||||||
k++;
|
k++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -298,7 +300,8 @@ void SetColorsPalette(ROM& rom, int index, gfx::SnesPalette& current,
|
|||||||
k = 0;
|
k = 0;
|
||||||
for (int y = 8; y < 9; y++) {
|
for (int y = 8; y < 9; y++) {
|
||||||
for (int x = 9; x < 16; x++) {
|
for (int x = 9; x < 16; x++) {
|
||||||
new_palette[x + (16 * y)] = rom.palette_group("sprites_aux3")[0][k];
|
auto pal_group = rom.palette_group().sprites_aux3;
|
||||||
|
new_palette[x + (16 * y)] = pal_group[0][k];
|
||||||
k++;
|
k++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -307,7 +310,8 @@ void SetColorsPalette(ROM& rom, int index, gfx::SnesPalette& current,
|
|||||||
k = 0;
|
k = 0;
|
||||||
for (int y = 9; y < 13; y++) {
|
for (int y = 9; y < 13; y++) {
|
||||||
for (int x = 1; x < 16; x++) {
|
for (int x = 1; x < 16; x++) {
|
||||||
new_palette[x + (16 * y)] = rom.palette_group("global_sprites")[0][k];
|
auto pal_group = rom.palette_group().global_sprites;
|
||||||
|
new_palette[x + (16 * y)] = pal_group[0][k];
|
||||||
k++;
|
k++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -334,7 +338,8 @@ void SetColorsPalette(ROM& rom, int index, gfx::SnesPalette& current,
|
|||||||
k = 0;
|
k = 0;
|
||||||
for (int y = 15; y < 16; y++) {
|
for (int y = 15; y < 16; y++) {
|
||||||
for (int x = 1; x < 16; x++) {
|
for (int x = 1; x < 16; x++) {
|
||||||
new_palette[x + (16 * y)] = rom.palette_group("armors")[0][k];
|
auto pal_group = rom.palette_group().armors;
|
||||||
|
new_palette[x + (16 * y)] = pal_group[0][k];
|
||||||
k++;
|
k++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -343,27 +348,26 @@ void SetColorsPalette(ROM& rom, int index, gfx::SnesPalette& current,
|
|||||||
for (int i = 0; i < 256; i++) {
|
for (int i = 0; i < 256; i++) {
|
||||||
current[(i / 16) * 16].set_transparent(true);
|
current[(i / 16) * 16].set_transparent(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return absl::OkStatus();
|
||||||
}
|
}
|
||||||
} // namespace palette_internal
|
} // namespace palette_internal
|
||||||
|
|
||||||
// New helper function to get a palette from the ROM.
|
// New helper function to get a palette from the Rom.
|
||||||
gfx::SnesPalette OverworldMap::GetPalette(const std::string& group, int index,
|
absl::StatusOr<gfx::SnesPalette> OverworldMap::GetPalette(
|
||||||
int previousIndex, int limit) {
|
const gfx::PaletteGroup& palette_group, int index, int previous_index,
|
||||||
|
int limit) {
|
||||||
if (index == 255) {
|
if (index == 255) {
|
||||||
index = rom_[rom_.version_constants().overworldMapPaletteGroup +
|
index = rom_[rom_.version_constants().overworldMapPaletteGroup +
|
||||||
(previousIndex * 4)];
|
(previous_index * 4)];
|
||||||
}
|
}
|
||||||
if (index != 255) {
|
if (index >= limit) {
|
||||||
if (index >= limit) {
|
index = limit - 1;
|
||||||
index = limit - 1;
|
|
||||||
}
|
|
||||||
return rom_.palette_group(group)[index];
|
|
||||||
} else {
|
|
||||||
return rom_.palette_group(group)[0];
|
|
||||||
}
|
}
|
||||||
|
return palette_group[index];
|
||||||
}
|
}
|
||||||
|
|
||||||
void OverworldMap::LoadPalette() {
|
absl::Status OverworldMap::LoadPalette() {
|
||||||
int previousPalId = index_ > 0 ? rom_[overworldMapPalette + parent_ - 1] : 0;
|
int previousPalId = index_ > 0 ? rom_[overworldMapPalette + parent_ - 1] : 0;
|
||||||
int previousSprPalId =
|
int previousSprPalId =
|
||||||
index_ > 0 ? rom_[overworldSpritePalette + parent_ - 1] : 0;
|
index_ > 0 ? rom_[overworldSpritePalette + parent_ - 1] : 0;
|
||||||
@@ -382,45 +386,64 @@ void OverworldMap::LoadPalette() {
|
|||||||
uchar pal5 = rom_[overworldSpritePaletteGroup +
|
uchar pal5 = rom_[overworldSpritePaletteGroup +
|
||||||
(sprite_palette_[game_state_] * 2) + 1];
|
(sprite_palette_[game_state_] * 2) + 1];
|
||||||
|
|
||||||
gfx::SnesColor bgr = rom_.palette_group("grass")[0].GetColor(0);
|
auto grass_pal_group = rom_.palette_group().grass;
|
||||||
|
ASSIGN_OR_RETURN(gfx::SnesColor bgr, grass_pal_group[0].GetColor(0));
|
||||||
|
|
||||||
gfx::SnesPalette aux1 = GetPalette("ow_aux", pal1, previousPalId, 20);
|
auto ow_aux_pal_group = rom_.palette_group().overworld_aux;
|
||||||
gfx::SnesPalette aux2 = GetPalette("ow_aux", pal2, previousPalId, 20);
|
ASSIGN_OR_RETURN(gfx::SnesPalette aux1,
|
||||||
|
GetPalette(ow_aux_pal_group, pal1, previousPalId, 20));
|
||||||
|
ASSIGN_OR_RETURN(gfx::SnesPalette aux2,
|
||||||
|
GetPalette(ow_aux_pal_group, pal2, previousPalId, 20));
|
||||||
|
|
||||||
// Additional handling of `pal3` and `parent_`
|
// Additional handling of `pal3` and `parent_`
|
||||||
if (pal3 == 255) {
|
if (pal3 == 255) {
|
||||||
pal3 = rom_[rom_.version_constants().overworldMapPaletteGroup +
|
pal3 = rom_[rom_.version_constants().overworldMapPaletteGroup +
|
||||||
(previousPalId * 4) + 2];
|
(previousPalId * 4) + 2];
|
||||||
}
|
}
|
||||||
|
|
||||||
if (parent_ < 0x40) {
|
if (parent_ < 0x40) {
|
||||||
pal0 = parent_ == 0x03 || parent_ == 0x05 || parent_ == 0x07 ? 2 : 0;
|
pal0 = parent_ == 0x03 || parent_ == 0x05 || parent_ == 0x07 ? 2 : 0;
|
||||||
bgr = rom_.palette_group("grass")[0].GetColor(0);
|
ASSIGN_OR_RETURN(bgr, grass_pal_group[0].GetColor(0));
|
||||||
} else if (parent_ >= 0x40 && parent_ < 0x80) {
|
} else if (parent_ >= 0x40 && parent_ < 0x80) {
|
||||||
pal0 = parent_ == 0x43 || parent_ == 0x45 || parent_ == 0x47 ? 3 : 1;
|
pal0 = parent_ == 0x43 || parent_ == 0x45 || parent_ == 0x47 ? 3 : 1;
|
||||||
bgr = rom_.palette_group("grass")[0].GetColor(1);
|
ASSIGN_OR_RETURN(bgr, grass_pal_group[0].GetColor(1));
|
||||||
} else if (parent_ >= 128 && parent_ < kNumOverworldMaps) {
|
} else if (parent_ >= 128 && parent_ < kNumOverworldMaps) {
|
||||||
pal0 = 0;
|
pal0 = 0;
|
||||||
bgr = rom_.palette_group("grass")[0].GetColor(2);
|
ASSIGN_OR_RETURN(bgr, grass_pal_group[0].GetColor(2));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (parent_ == 0x88) {
|
if (parent_ == 0x88) {
|
||||||
pal0 = 4;
|
pal0 = 4;
|
||||||
}
|
}
|
||||||
gfx::SnesPalette main = GetPalette("ow_main", pal0, previousPalId, 255);
|
|
||||||
gfx::SnesPalette animated =
|
|
||||||
GetPalette("ow_animated", std::min((int)pal3, 13), previousPalId, 14);
|
|
||||||
gfx::SnesPalette hud = rom_.palette_group("hud")[0];
|
|
||||||
|
|
||||||
gfx::SnesPalette spr = GetPalette("sprites_aux3", pal4, previousSprPalId, 24);
|
auto ow_main_pal_group = rom_.palette_group().overworld_main;
|
||||||
gfx::SnesPalette spr2 =
|
ASSIGN_OR_RETURN(gfx::SnesPalette main,
|
||||||
GetPalette("sprites_aux3", pal5, previousSprPalId, 24);
|
GetPalette(ow_main_pal_group, pal0, previousPalId, 255));
|
||||||
|
auto ow_animated_pal_group = rom_.palette_group().overworld_animated;
|
||||||
|
ASSIGN_OR_RETURN(gfx::SnesPalette animated,
|
||||||
|
GetPalette(ow_animated_pal_group, std::min((int)pal3, 13),
|
||||||
|
previousPalId, 14));
|
||||||
|
|
||||||
palette_internal::SetColorsPalette(rom_, parent_, current_palette_, main,
|
auto hud_pal_group = rom_.palette_group().hud;
|
||||||
animated, aux1, aux2, hud, bgr, spr, spr2);
|
gfx::SnesPalette hud = hud_pal_group[0];
|
||||||
|
|
||||||
|
ASSIGN_OR_RETURN(gfx::SnesPalette spr,
|
||||||
|
GetPalette(rom_.palette_group().sprites_aux3, pal4,
|
||||||
|
previousSprPalId, 24));
|
||||||
|
ASSIGN_OR_RETURN(gfx::SnesPalette spr2,
|
||||||
|
GetPalette(rom_.palette_group().sprites_aux3, pal5,
|
||||||
|
previousSprPalId, 24));
|
||||||
|
|
||||||
|
RETURN_IF_ERROR(palette_internal::SetColorsPalette(
|
||||||
|
rom_, parent_, current_palette_, main, animated, aux1, aux2, hud, bgr,
|
||||||
|
spr, spr2));
|
||||||
|
|
||||||
if (palettesets_.count(area_palette_) == 0) {
|
if (palettesets_.count(area_palette_) == 0) {
|
||||||
palettesets_[area_palette_] = gfx::Paletteset{
|
palettesets_[area_palette_] = gfx::Paletteset{
|
||||||
main, animated, aux1, aux2, bgr, hud, spr, spr2, current_palette_};
|
main, animated, aux1, aux2, bgr, hud, spr, spr2, current_palette_};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return absl::OkStatus();
|
||||||
}
|
}
|
||||||
|
|
||||||
// New helper function to process graphics buffer.
|
// New helper function to process graphics buffer.
|
||||||
@@ -535,6 +558,7 @@ absl::Status OverworldMap::BuildBitmap(OWBlockset& world_blockset) {
|
|||||||
return absl::OkStatus();
|
return absl::OkStatus();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
} // namespace overworld
|
||||||
} // namespace zelda3
|
} // namespace zelda3
|
||||||
} // namespace app
|
} // namespace app
|
||||||
} // namespace yaze
|
} // namespace yaze
|
||||||
@@ -20,21 +20,23 @@
|
|||||||
namespace yaze {
|
namespace yaze {
|
||||||
namespace app {
|
namespace app {
|
||||||
namespace zelda3 {
|
namespace zelda3 {
|
||||||
|
namespace overworld {
|
||||||
|
|
||||||
static constexpr int kTileOffsets[] = {0, 8, 4096, 4104};
|
static constexpr int kTileOffsets[] = {0, 8, 4096, 4104};
|
||||||
|
|
||||||
using editor::GfxContext;
|
/**
|
||||||
|
* @brief Represents a single Overworld map screen.
|
||||||
class OverworldMap : public GfxContext {
|
*/
|
||||||
|
class OverworldMap : public editor::context::GfxContext {
|
||||||
public:
|
public:
|
||||||
OverworldMap() = default;
|
OverworldMap() = default;
|
||||||
OverworldMap(int index, ROM& rom, std::vector<gfx::Tile16>& tiles16);
|
OverworldMap(int index, Rom& rom, std::vector<gfx::Tile16>& tiles16);
|
||||||
|
|
||||||
absl::Status BuildMap(int count, int game_state, int world,
|
absl::Status BuildMap(int count, int game_state, int world,
|
||||||
OWBlockset& world_blockset);
|
OWBlockset& world_blockset);
|
||||||
|
|
||||||
void LoadAreaGraphics();
|
void LoadAreaGraphics();
|
||||||
void LoadPalette();
|
absl::Status LoadPalette();
|
||||||
absl::Status BuildTileset();
|
absl::Status BuildTileset();
|
||||||
absl::Status BuildTiles16Gfx(int count);
|
absl::Status BuildTiles16Gfx(int count);
|
||||||
absl::Status BuildBitmap(OWBlockset& world_blockset);
|
absl::Status BuildBitmap(OWBlockset& world_blockset);
|
||||||
@@ -108,8 +110,9 @@ class OverworldMap : public GfxContext {
|
|||||||
void LoadDeathMountainGFX();
|
void LoadDeathMountainGFX();
|
||||||
|
|
||||||
void ProcessGraphicsBuffer(int index, int static_graphics_offset, int size);
|
void ProcessGraphicsBuffer(int index, int static_graphics_offset, int size);
|
||||||
gfx::SnesPalette GetPalette(const std::string& group, int index,
|
absl::StatusOr<gfx::SnesPalette> GetPalette(const gfx::PaletteGroup& group,
|
||||||
int previousIndex, int limit);
|
int index, int previous_index,
|
||||||
|
int limit);
|
||||||
|
|
||||||
bool built_ = false;
|
bool built_ = false;
|
||||||
bool large_map_ = false;
|
bool large_map_ = false;
|
||||||
@@ -131,7 +134,7 @@ class OverworldMap : public GfxContext {
|
|||||||
uchar area_music_[4];
|
uchar area_music_[4];
|
||||||
uchar static_graphics_[16];
|
uchar static_graphics_[16];
|
||||||
|
|
||||||
ROM rom_;
|
Rom rom_;
|
||||||
Bytes all_gfx_;
|
Bytes all_gfx_;
|
||||||
Bytes current_blockset_;
|
Bytes current_blockset_;
|
||||||
Bytes current_gfx_;
|
Bytes current_gfx_;
|
||||||
@@ -142,6 +145,7 @@ class OverworldMap : public GfxContext {
|
|||||||
std::vector<gfx::Tile16> tiles16_;
|
std::vector<gfx::Tile16> tiles16_;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
} // namespace overworld
|
||||||
} // namespace zelda3
|
} // namespace zelda3
|
||||||
} // namespace app
|
} // namespace app
|
||||||
} // namespace yaze
|
} // namespace yaze
|
||||||
@@ -7,6 +7,7 @@
|
|||||||
namespace yaze {
|
namespace yaze {
|
||||||
namespace app {
|
namespace app {
|
||||||
namespace zelda3 {
|
namespace zelda3 {
|
||||||
|
namespace screen {
|
||||||
|
|
||||||
constexpr int kDungeonMapRoomsPtr = 0x57605; // 14 pointers of map data
|
constexpr int kDungeonMapRoomsPtr = 0x57605; // 14 pointers of map data
|
||||||
constexpr int kDungeonMapFloors = 0x575D9; // 14 words values
|
constexpr int kDungeonMapFloors = 0x575D9; // 14 words values
|
||||||
@@ -47,8 +48,9 @@ class DungeonMap {
|
|||||||
floor_gfx(floor_gfx) {}
|
floor_gfx(floor_gfx) {}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
} // namespace screen
|
||||||
} // namespace zelda3
|
} // namespace zelda3
|
||||||
} // namespace app
|
} // namespace app
|
||||||
} // namespace yaze
|
} // namespace yaze
|
||||||
|
|
||||||
#endif // YAZE_APP_ZELDA3_SCREEN_DUNGEON_MAP_H
|
#endif // YAZE_APP_ZELDA3_SCREEN_DUNGEON_MAP_H
|
||||||
|
|||||||
@@ -8,6 +8,7 @@
|
|||||||
namespace yaze {
|
namespace yaze {
|
||||||
namespace app {
|
namespace app {
|
||||||
namespace zelda3 {
|
namespace zelda3 {
|
||||||
|
namespace screen {
|
||||||
|
|
||||||
void Inventory::Create() {
|
void Inventory::Create() {
|
||||||
data_.reserve(256 * 256);
|
data_.reserve(256 * 256);
|
||||||
@@ -79,12 +80,14 @@ absl::Status Inventory::BuildTileset() {
|
|||||||
test_.push_back(tilesheets_[i]);
|
test_.push_back(tilesheets_[i]);
|
||||||
}
|
}
|
||||||
tilesheets_bmp_.Create(128, 0x130, 64, test_);
|
tilesheets_bmp_.Create(128, 0x130, 64, test_);
|
||||||
palette_ = rom()->palette_group("hud")[0];
|
auto hud_pal_group = rom()->palette_group().hud;
|
||||||
tilesheets_bmp_.ApplyPalette(palette_);
|
palette_ = hud_pal_group[0];
|
||||||
|
RETURN_IF_ERROR(tilesheets_bmp_.ApplyPalette(palette_))
|
||||||
rom()->RenderBitmap(&tilesheets_bmp_);
|
rom()->RenderBitmap(&tilesheets_bmp_);
|
||||||
return absl::OkStatus();
|
return absl::OkStatus();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
} // namespace screen
|
||||||
} // namespace zelda3
|
} // namespace zelda3
|
||||||
} // namespace app
|
} // namespace app
|
||||||
} // namespace yaze
|
} // namespace yaze
|
||||||
@@ -2,19 +2,20 @@
|
|||||||
#define YAZE_APP_ZELDA3_INVENTORY_H
|
#define YAZE_APP_ZELDA3_INVENTORY_H
|
||||||
|
|
||||||
#include "app/gfx/bitmap.h"
|
#include "app/gfx/bitmap.h"
|
||||||
#include "app/gfx/snes_tile.h"
|
|
||||||
#include "app/gfx/snes_palette.h"
|
#include "app/gfx/snes_palette.h"
|
||||||
#include "app/rom.h"
|
#include "app/gfx/snes_tile.h"
|
||||||
#include "app/gui/canvas.h"
|
#include "app/gui/canvas.h"
|
||||||
|
#include "app/rom.h"
|
||||||
|
|
||||||
namespace yaze {
|
namespace yaze {
|
||||||
namespace app {
|
namespace app {
|
||||||
namespace zelda3 {
|
namespace zelda3 {
|
||||||
|
namespace screen {
|
||||||
|
|
||||||
constexpr int kInventoryStart = 0x6564A;
|
constexpr int kInventoryStart = 0x6564A;
|
||||||
constexpr int kBowItemPos = 0x6F631;
|
constexpr int kBowItemPos = 0x6F631;
|
||||||
|
|
||||||
class Inventory : public SharedROM {
|
class Inventory : public SharedRom {
|
||||||
public:
|
public:
|
||||||
auto Bitmap() const { return bitmap_; }
|
auto Bitmap() const { return bitmap_; }
|
||||||
auto Tilesheet() const { return tilesheets_bmp_; }
|
auto Tilesheet() const { return tilesheets_bmp_; }
|
||||||
@@ -37,8 +38,9 @@ class Inventory : public SharedROM {
|
|||||||
std::vector<gfx::TileInfo> tiles_;
|
std::vector<gfx::TileInfo> tiles_;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
} // namespace screen
|
||||||
} // namespace zelda3
|
} // namespace zelda3
|
||||||
} // namespace app
|
} // namespace app
|
||||||
} // namespace yaze
|
} // namespace yaze
|
||||||
|
|
||||||
#endif
|
#endif // YAZE_APP_ZELDA3_INVENTORY_H
|
||||||
|
|||||||
@@ -10,6 +10,7 @@
|
|||||||
namespace yaze {
|
namespace yaze {
|
||||||
namespace app {
|
namespace app {
|
||||||
namespace zelda3 {
|
namespace zelda3 {
|
||||||
|
namespace screen {
|
||||||
|
|
||||||
void TitleScreen::Create() {
|
void TitleScreen::Create() {
|
||||||
tiles8Bitmap.Create(128, 512, 8, 0x20000);
|
tiles8Bitmap.Create(128, 512, 8, 0x20000);
|
||||||
@@ -124,6 +125,7 @@ void TitleScreen::LoadTitleScreen() {
|
|||||||
pal_selected_ = 2;
|
pal_selected_ = 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
} // namespace screen
|
||||||
} // namespace zelda3
|
} // namespace zelda3
|
||||||
} // namespace app
|
} // namespace app
|
||||||
} // namespace yaze
|
} // namespace yaze
|
||||||
|
|||||||
@@ -11,6 +11,7 @@
|
|||||||
namespace yaze {
|
namespace yaze {
|
||||||
namespace app {
|
namespace app {
|
||||||
namespace zelda3 {
|
namespace zelda3 {
|
||||||
|
namespace screen {
|
||||||
|
|
||||||
class TitleScreen {
|
class TitleScreen {
|
||||||
public:
|
public:
|
||||||
@@ -62,11 +63,11 @@ class TitleScreen {
|
|||||||
bool mouseDown = false;
|
bool mouseDown = false;
|
||||||
bool mdown = false;
|
bool mdown = false;
|
||||||
|
|
||||||
ROM rom_;
|
Rom rom_;
|
||||||
|
|
||||||
gfx::OAMTile oam_data[10];
|
gfx::OamTile oam_data[10];
|
||||||
gfx::OAMTile selected_oam_tile;
|
gfx::OamTile selected_oam_tile;
|
||||||
gfx::OAMTile last_selected_oam_tile;
|
gfx::OamTile last_selected_oam_tile;
|
||||||
|
|
||||||
gfx::Bitmap tilesBG1Bitmap; // 0x80000
|
gfx::Bitmap tilesBG1Bitmap; // 0x80000
|
||||||
gfx::Bitmap tilesBG2Bitmap; // 0x80000
|
gfx::Bitmap tilesBG2Bitmap; // 0x80000
|
||||||
@@ -74,8 +75,9 @@ class TitleScreen {
|
|||||||
gfx::Bitmap tiles8Bitmap; // 0x20000
|
gfx::Bitmap tiles8Bitmap; // 0x20000
|
||||||
};
|
};
|
||||||
|
|
||||||
|
} // namespace screen
|
||||||
} // namespace zelda3
|
} // namespace zelda3
|
||||||
} // namespace app
|
} // namespace app
|
||||||
} // namespace yaze
|
} // namespace yaze
|
||||||
|
|
||||||
#endif
|
#endif // YAZE_APP_ZELDA3_SCREEN_H
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
#include "sprite.h"
|
#include "sprite.h"
|
||||||
|
|
||||||
|
#include "app/zelda3/overworld/overworld.h"
|
||||||
|
|
||||||
namespace yaze {
|
namespace yaze {
|
||||||
namespace app {
|
namespace app {
|
||||||
namespace zelda3 {
|
namespace zelda3 {
|
||||||
|
|||||||
@@ -19,6 +19,10 @@ namespace yaze {
|
|||||||
namespace app {
|
namespace app {
|
||||||
namespace zelda3 {
|
namespace zelda3 {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @class Sprite
|
||||||
|
* @brief A class for managing sprites in the overworld and underworld.
|
||||||
|
*/
|
||||||
class Sprite : public OverworldEntity {
|
class Sprite : public OverworldEntity {
|
||||||
public:
|
public:
|
||||||
Sprite() = default;
|
Sprite() = default;
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user