Bunch of updates refactoring adding some sneshacking code some gui stuff
This commit is contained in:
@@ -9,6 +9,45 @@ namespace Application {
|
||||
namespace Core {
|
||||
namespace Constants {
|
||||
|
||||
using ushort = unsigned short;
|
||||
|
||||
//===========================================================================================
|
||||
// Magic numbers
|
||||
//===========================================================================================
|
||||
/// <summary>
|
||||
/// Bit set for object priority
|
||||
/// </summary>
|
||||
constexpr ushort TilePriorityBit = 0x2000;
|
||||
|
||||
/// <summary>
|
||||
/// Bit set for object hflip
|
||||
/// </summary>
|
||||
constexpr ushort TileHFlipBit = 0x4000;
|
||||
|
||||
/// <summary>
|
||||
/// Bit set for object vflip
|
||||
/// </summary>
|
||||
constexpr ushort TileVFlipBit = 0x8000;
|
||||
|
||||
/// <summary>
|
||||
/// Bits used for tile name
|
||||
/// </summary>
|
||||
constexpr ushort TileNameMask = 0x03FF;
|
||||
|
||||
constexpr int Uncompressed3BPPSize = 0x0600;
|
||||
constexpr int UncompressedSheetSize = 0x0800;
|
||||
|
||||
constexpr int NumberOfSheets = 223;
|
||||
constexpr int LimitOfMap32 = 8864;
|
||||
constexpr int NumberOfRooms = 296;
|
||||
|
||||
constexpr int NumberOfOWMaps = 160;
|
||||
constexpr int Map32PerScreen = 256;
|
||||
constexpr int NumberOfMap16 = 3752; // 4096
|
||||
constexpr int NumberOfMap32 = Map32PerScreen * NumberOfOWMaps;
|
||||
constexpr int NumberOfOWSprites = 352;
|
||||
constexpr int NumberOfColors = 3143;
|
||||
|
||||
// ===========================================================================================
|
||||
// Graphics
|
||||
// ===========================================================================================
|
||||
@@ -20,7 +59,8 @@ namespace Constants {
|
||||
constexpr int subtype3_tiles = 0x84F0; // JP = Same
|
||||
constexpr int gfx_animated_pointer = 0x10275; // JP 0x10624 //long pointer
|
||||
constexpr int overworldgfxGroups2 = 0x6073; // 0x60B3
|
||||
constexpr int gfx_1_pointer = 0x6790; //2byte pointer bank 00 pc -> 0x4320 CF80 ; 004F80
|
||||
constexpr int gfx_1_pointer =
|
||||
0x6790; // 2byte pointer bank 00 pc -> 0x4320 CF80 ; 004F80
|
||||
constexpr int gfx_2_pointer = 0x6795; // D05F ; 00505F
|
||||
constexpr int gfx_3_pointer = 0x679A; // D13E ; 00513E
|
||||
constexpr int hud_palettes = 0xDD660;
|
||||
@@ -75,8 +115,10 @@ namespace Constants {
|
||||
constexpr int overworldEntranceAllowedTilesLeft = 0xDB8C1;
|
||||
constexpr int overworldEntranceAllowedTilesRight = 0xDB917;
|
||||
|
||||
constexpr int overworldMapSize = 0x12844; //0x00 = small maps, 0x20 = large maps
|
||||
constexpr int overworldMapSizeHighByte = 0x12884; //0x01 = small maps, 0x03 = large maps
|
||||
constexpr int overworldMapSize = 0x12844; // 0x00 = small maps, 0x20 = large
|
||||
// maps
|
||||
constexpr int overworldMapSizeHighByte =
|
||||
0x12884; // 0x01 = small maps, 0x03 = large maps
|
||||
|
||||
// relative to the WORLD + 0x200 per map
|
||||
// large map that are not == parent id = same position as their parent!
|
||||
@@ -112,9 +154,12 @@ namespace Constants {
|
||||
constexpr int OWEntranceMap = 0xDB96F;
|
||||
constexpr int OWEntrancePos = 0xDBA71;
|
||||
constexpr int OWEntranceEntranceId = 0xDBB73;
|
||||
constexpr int OWHolePos = 0xDB800;//(0x13 entries, 2 bytes each) modified(less 0x400) map16 coordinates for each hole
|
||||
constexpr int OWHoleArea = 0xDB826;//(0x13 entries, 2 bytes each) corresponding area numbers for each hole
|
||||
constexpr int OWHoleEntrance = 0xDB84C;//(0x13 entries, 1 byte each) corresponding entrance numbers
|
||||
constexpr int OWHolePos = 0xDB800; //(0x13 entries, 2 bytes each) modified(less
|
||||
// 0x400) map16 coordinates for each hole
|
||||
constexpr int OWHoleArea = 0xDB826; //(0x13 entries, 2 bytes each) corresponding
|
||||
// area numbers for each hole
|
||||
constexpr int OWHoleEntrance =
|
||||
0xDB84C; //(0x13 entries, 1 byte each) corresponding entrance numbers
|
||||
|
||||
constexpr int OWExitMapIdWhirlpool = 0x16AE5; // JP = ;016849
|
||||
constexpr int OWExitVramWhirlpool = 0x16B07; // JP = ;01686B
|
||||
@@ -134,7 +179,8 @@ namespace Constants {
|
||||
// That could be turned into a pointer :
|
||||
constexpr int dungeons_palettes_groups = 0x75460; // JP 0x67DD0
|
||||
constexpr int dungeons_main_bg_palette_pointers = 0xDEC4B; // JP Same
|
||||
constexpr int dungeons_palettes = 0xDD734; //JP Same (where all dungeons palettes are)
|
||||
constexpr int dungeons_palettes =
|
||||
0xDD734; // JP Same (where all dungeons palettes are)
|
||||
|
||||
// That could be turned into a pointer :
|
||||
constexpr int room_items_pointers = 0xDB69; // JP 0xDB67
|
||||
@@ -150,8 +196,9 @@ namespace Constants {
|
||||
|
||||
constexpr int chests_length_pointer = 0xEBF6;
|
||||
constexpr int chests_data_pointer1 = 0xEBFB;
|
||||
//constexpr int chests_data_pointer2 = 0xEC0A; //Disabled for now could be used for expansion
|
||||
//constexpr int chests_data_pointer3 = 0xEC10; //Disabled for now could be used for expansion
|
||||
// constexpr int chests_data_pointer2 = 0xEC0A; //Disabled for now could be used
|
||||
// for expansion constexpr int chests_data_pointer3 = 0xEC10; //Disabled for now
|
||||
// could be used for expansion
|
||||
|
||||
constexpr int blocks_length = 0x8896; // word value
|
||||
constexpr int blocks_pointer1 = 0x15AFA;
|
||||
@@ -163,7 +210,8 @@ namespace Constants {
|
||||
constexpr int torches_length_pointer = 0x88C1;
|
||||
|
||||
constexpr int sprite_blockset_pointer = 0x5B57;
|
||||
constexpr int sprites_data = 0x4D8B0;//It use the unused pointers to have more space //Save purpose
|
||||
constexpr int sprites_data =
|
||||
0x4D8B0; // It use the unused pointers to have more space //Save purpose
|
||||
constexpr int sprites_data_empty_room = 0x4D8AE;
|
||||
constexpr int sprites_end_data = 0x4EC9E;
|
||||
|
||||
@@ -196,7 +244,8 @@ namespace Constants {
|
||||
// Dungeon Entrances Related Variables
|
||||
//===========================================================================================
|
||||
constexpr int entrance_room = 0x14813; // 0x14577 //word value for each room
|
||||
constexpr int entrance_scrolledge = 0x1491D; //0x14681 //8 bytes per room, HU, FU, HD, FD, HL, FL, HR, FR
|
||||
constexpr int entrance_scrolledge =
|
||||
0x1491D; // 0x14681 //8 bytes per room, HU, FU, HD, FD, HL, FL, HR, FR
|
||||
constexpr int entrance_yscroll = 0x14D45; // 0x14AA9 //2bytes each room
|
||||
constexpr int entrance_xscroll = 0x14E4F; // 0x14BB3 //2bytes
|
||||
constexpr int entrance_yposition = 0x14F59; // 0x14CBD 2bytes
|
||||
@@ -209,14 +258,17 @@ namespace Constants {
|
||||
constexpr int entrance_floor = 0x15406; // 0x1516A 1byte
|
||||
constexpr int entrance_dungeon = 0x1548B; // 0x151EF 1byte (dungeon id)
|
||||
constexpr int entrance_door = 0x15510; // 0x15274 1byte
|
||||
constexpr int entrance_ladderbg = 0x15595; //0x152F9 //1 byte, ---b ---a b = bg2, a = need to check -_-
|
||||
constexpr int entrance_ladderbg =
|
||||
0x15595; // 0x152F9 //1 byte, ---b ---a b = bg2, a = need to check -_-
|
||||
constexpr int entrance_scrolling = 0x1561A; // 0x1537E //1byte --h- --v-
|
||||
constexpr int entrance_scrollquadrant = 0x1569F; // 0x15403 1byte
|
||||
constexpr int entrance_exit = 0x15724; // 0x15488 //2byte word
|
||||
constexpr int entrance_music = 0x1582E; // 0x15592
|
||||
|
||||
constexpr int startingentrance_room = 0x15B6E; //0x158D2 //word value for each room
|
||||
constexpr int startingentrance_scrolledge = 0x15B7C; //0x158E0 //8 bytes per room, HU, FU, HD, FD, HL, FL, HR, FR
|
||||
constexpr int startingentrance_room =
|
||||
0x15B6E; // 0x158D2 //word value for each room
|
||||
constexpr int startingentrance_scrolledge =
|
||||
0x15B7C; // 0x158E0 //8 bytes per room, HU, FU, HD, FD, HL, FL, HR, FR
|
||||
constexpr int startingentrance_yscroll = 0x15BB4; // 0x14AA9 //2bytes each room
|
||||
constexpr int startingentrance_xscroll = 0x15BC2; // 0x14BB3 //2bytes
|
||||
constexpr int startingentrance_yposition = 0x15BD0; // 0x14CBD 2bytes
|
||||
@@ -230,7 +282,8 @@ namespace Constants {
|
||||
|
||||
constexpr int startingentrance_door = 0x15C2B; // 0x15274 1byte
|
||||
|
||||
constexpr int startingentrance_ladderbg = 0x15C1D; //0x152F9 //1 byte, ---b ---a b = bg2, a = need to check -_-
|
||||
constexpr int startingentrance_ladderbg =
|
||||
0x15C1D; // 0x152F9 //1 byte, ---b ---a b = bg2, a = need to check -_-
|
||||
constexpr int startingentrance_scrolling = 0x15C24; // 0x1537E //1byte --h- --v-
|
||||
constexpr int startingentrance_scrollquadrant = 0x15C2B; // 0x15403 1byte
|
||||
constexpr int startingentrance_exit = 0x15C32; // 0x15488 //2byte word
|
||||
@@ -242,7 +295,8 @@ namespace Constants {
|
||||
constexpr int initial_equipement = 0x271A6;
|
||||
constexpr int messages_id_dungeon = 0x3F61D;
|
||||
|
||||
constexpr int chests_backupitems = 0x3B528; //item id you get instead if you already have that item
|
||||
constexpr int chests_backupitems =
|
||||
0x3B528; // item id you get instead if you already have that item
|
||||
constexpr int chests_yoffset = 0x4836C;
|
||||
constexpr int chests_xoffset = 0x4836C + (76 * 1);
|
||||
constexpr int chests_itemsgfx = 0x4836C + (76 * 2);
|
||||
@@ -261,10 +315,12 @@ namespace Constants {
|
||||
constexpr int bedPositionX = 0x039A37; // short value
|
||||
constexpr int bedPositionY = 0x039A32; // short value
|
||||
|
||||
constexpr int bedPositionResetXLow = 0x02DE53; //short value(on 2 different bytes)
|
||||
constexpr int bedPositionResetXLow =
|
||||
0x02DE53; // short value(on 2 different bytes)
|
||||
constexpr int bedPositionResetXHigh = 0x02DE58; //^^^^^^
|
||||
|
||||
constexpr int bedPositionResetYLow = 0x02DE5D; //short value(on 2 different bytes)
|
||||
constexpr int bedPositionResetYLow =
|
||||
0x02DE5D; // short value(on 2 different bytes)
|
||||
constexpr int bedPositionResetYHigh = 0x02DE62; //^^^^^^
|
||||
|
||||
constexpr int bedSheetPositionX = 0x0480BD; // short value
|
||||
@@ -294,7 +350,8 @@ namespace Constants {
|
||||
constexpr int overworldPaletteAnimated = 0xDE604;
|
||||
constexpr int globalSpritePalettesLW = 0xDD218;
|
||||
constexpr int globalSpritePalettesDW = 0xDD290;
|
||||
constexpr int armorPalettes = 0xDD308; // Green, Blue, Red, Bunny, Electrocuted (15 colors each)
|
||||
constexpr int armorPalettes =
|
||||
0xDD308; // Green, Blue, Red, Bunny, Electrocuted (15 colors each)
|
||||
constexpr int spritePalettesAux1 = 0xDD39E; // 7 colors each
|
||||
constexpr int spritePalettesAux2 = 0xDD446; // 7 colors each
|
||||
constexpr int spritePalettesAux3 = 0xDD4E0; // 7 colors each
|
||||
@@ -315,13 +372,15 @@ namespace Constants {
|
||||
constexpr int dungeonMap_floors = 0x575D9; // 14 words values
|
||||
|
||||
constexpr int dungeonMap_gfx_ptr = 0x57BE4; // 14 pointers of gfx data
|
||||
constexpr int dungeonMap_datastart = 0x57039; //data start for floors/gfx MUST skip 575D9 to 57621 (pointers)
|
||||
constexpr int dungeonMap_datastart =
|
||||
0x57039; // data start for floors/gfx MUST skip 575D9 to 57621 (pointers)
|
||||
|
||||
|
||||
constexpr int dungeonMap_expCheck = 0x56652; //IF Byte = 0xB9 dungeon maps are not expanded
|
||||
constexpr int dungeonMap_expCheck =
|
||||
0x56652; // IF Byte = 0xB9 dungeon maps are not expanded
|
||||
constexpr int dungeonMap_tile16 = 0x57009;
|
||||
constexpr int dungeonMap_tile16Exp = 0x109010;
|
||||
constexpr int dungeonMap_bossrooms = 0x56807; //14 words values 0x000F = no boss
|
||||
constexpr int dungeonMap_bossrooms = 0x56807; // 14 words values 0x000F = no
|
||||
// boss
|
||||
|
||||
constexpr int triforceVertices = 0x04FFD2; // group of 3, X, Y ,Z
|
||||
constexpr int TriforceFaces = 0x04FFE4; // group of 5
|
||||
@@ -331,21 +390,16 @@ namespace Constants {
|
||||
//===========================================================================================
|
||||
// Names
|
||||
//===========================================================================================
|
||||
static const std::string RoomEffect[] =
|
||||
{
|
||||
"Nothing",
|
||||
static const std::string RoomEffect[] = {"Nothing",
|
||||
"Nothing",
|
||||
"Moving Floor",
|
||||
"Moving Water",
|
||||
"Trinexx Shell",
|
||||
"Red Flashes",
|
||||
"Light Torch to See Floor",
|
||||
"Ganon's Darkness"
|
||||
};
|
||||
"Ganon's Darkness"};
|
||||
|
||||
static const std::string RoomTag[] =
|
||||
{
|
||||
"Nothing",
|
||||
static const std::string RoomTag[] = {"Nothing",
|
||||
|
||||
"NW Kill Enemy to Open",
|
||||
"NE Kill Enemy to Open",
|
||||
@@ -413,47 +467,21 @@ namespace Constants {
|
||||
"Push Block for Chest",
|
||||
"Clear Room for Triforce Door",
|
||||
"Light Torches for Chest",
|
||||
"Kill Boss Again"
|
||||
};
|
||||
"Kill Boss Again"};
|
||||
|
||||
static const std::string SecretItemNames[] =
|
||||
{
|
||||
"Nothing",
|
||||
"Green Rupee",
|
||||
"Rock hoarder",
|
||||
"Bee",
|
||||
"Health pack",
|
||||
"Bomb",
|
||||
"Heart ",
|
||||
"Blue Rupee",
|
||||
static const std::string SecretItemNames[] = {
|
||||
"Nothing", "Green Rupee", "Rock hoarder", "Bee", "Health pack",
|
||||
"Bomb", "Heart ", "Blue Rupee",
|
||||
|
||||
"Key",
|
||||
"Arrow",
|
||||
"Bomb",
|
||||
"Heart",
|
||||
"Magic",
|
||||
"Full Magic",
|
||||
"Cucco",
|
||||
"Green Soldier",
|
||||
"Bush Stal",
|
||||
"Blue Soldier",
|
||||
"Key", "Arrow", "Bomb", "Heart", "Magic",
|
||||
"Full Magic", "Cucco", "Green Soldier", "Bush Stal", "Blue Soldier",
|
||||
|
||||
"Landmine",
|
||||
"Heart",
|
||||
"Fairy",
|
||||
"Heart",
|
||||
"Landmine", "Heart", "Fairy", "Heart",
|
||||
"Nothing ", // 22
|
||||
|
||||
"Hole",
|
||||
"Warp",
|
||||
"Staircase",
|
||||
"Bombable",
|
||||
"Switch"
|
||||
};
|
||||
"Hole", "Warp", "Staircase", "Bombable", "Switch"};
|
||||
|
||||
|
||||
static const std::string Type1RoomObjectNames[] =
|
||||
{
|
||||
static const std::string Type1RoomObjectNames[] = {
|
||||
"Ceiling ↔",
|
||||
"Wall (top, north) ↔",
|
||||
"Wall (top, south) ↔",
|
||||
@@ -704,8 +732,7 @@ namespace Constants {
|
||||
"Nothing",
|
||||
};
|
||||
|
||||
static const std::string Type2RoomObjectNames[] =
|
||||
{
|
||||
static const std::string Type2RoomObjectNames[] = {
|
||||
"Corner (top, concave) ▛",
|
||||
"Corner (top, concave) ▙",
|
||||
"Corner (top, concave) ▜",
|
||||
@@ -772,10 +799,7 @@ namespace Constants {
|
||||
"Magic bat altar",
|
||||
};
|
||||
|
||||
|
||||
|
||||
static const std::string Type3RoomObjectNames[] =
|
||||
{
|
||||
static const std::string Type3RoomObjectNames[] = {
|
||||
"Waterfall face (empty)",
|
||||
"Waterfall face (short)",
|
||||
"Waterfall face (long)",
|
||||
@@ -906,8 +930,7 @@ namespace Constants {
|
||||
"Nothing",
|
||||
};
|
||||
|
||||
static const std::string TileTypeNames[] =
|
||||
{
|
||||
static const std::string TileTypeNames[] = {
|
||||
"$00 Nothing (standard floor)",
|
||||
"$01 Collision",
|
||||
"$02 Collision",
|
||||
@@ -1163,13 +1186,11 @@ namespace Constants {
|
||||
"$FC Door X top? (unused?)",
|
||||
"$FD Door X top? (unused?)",
|
||||
"$FE Door X top? (unused?)",
|
||||
"$FF Door X top? (unused?)"
|
||||
};
|
||||
"$FF Door X top? (unused?)"};
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} // namespace Constants
|
||||
} // namespace Core
|
||||
} // namespace Application
|
||||
} // namespace yaze
|
||||
|
||||
#endif
|
||||
@@ -19,8 +19,8 @@ Overworld::~Overworld() {
|
||||
free(allmapsTilesDW);
|
||||
free(allmapsTilesSP);
|
||||
|
||||
delete overworldMapPointer;
|
||||
delete owactualMapPointer;
|
||||
delete[] overworldMapPointer;
|
||||
delete[] owactualMapPointer;
|
||||
}
|
||||
|
||||
static TileInfo GetTilesInfo(ushort tile) {
|
||||
|
||||
@@ -22,20 +22,10 @@ void Editor::UpdateScreen() {
|
||||
|
||||
DrawYazeMenu();
|
||||
|
||||
if (isLoaded) {
|
||||
if (!doneLoaded) {
|
||||
overworld.Load(rom);
|
||||
overworld_texture = &overworld.owactualMapTexture;
|
||||
doneLoaded = true;
|
||||
}
|
||||
// ImGui::Image((void*)(intptr_t)overworld_texture,
|
||||
// ImVec2(overworld.overworldMapBitmap->GetWidth(),
|
||||
// overworld.overworldMapBitmap->GetHeight()));
|
||||
}
|
||||
|
||||
if (ImGui::BeginTabBar("##TabBar")) {
|
||||
DrawOverworldEditor();
|
||||
DrawDungeonEditor();
|
||||
DrawSpriteEditor();
|
||||
DrawScreenEditor();
|
||||
DrawROMInfo();
|
||||
ImGui::EndTabBar();
|
||||
@@ -50,6 +40,7 @@ void Editor::DrawYazeMenu() {
|
||||
DrawFileMenu();
|
||||
DrawEditMenu();
|
||||
DrawViewMenu();
|
||||
DrawHelpMenu();
|
||||
ImGui::EndMenuBar();
|
||||
}
|
||||
|
||||
@@ -60,7 +51,8 @@ void Editor::DrawYazeMenu() {
|
||||
std::string filePathName = ImGuiFileDialog::Instance()->GetFilePathName();
|
||||
std::string filePath = ImGuiFileDialog::Instance()->GetCurrentPath();
|
||||
rom.LoadFromFile(filePathName);
|
||||
isLoaded = true;
|
||||
owEditor.SetRom(rom);
|
||||
rom_data_ = (void *)rom.GetRawData();
|
||||
}
|
||||
|
||||
// close
|
||||
@@ -137,10 +129,16 @@ void Editor::DrawEditMenu() const {
|
||||
void Editor::DrawViewMenu() const {
|
||||
static bool show_imgui_metrics = false;
|
||||
static bool show_imgui_style_editor = false;
|
||||
static bool show_memory_editor = false;
|
||||
if (show_imgui_metrics) {
|
||||
ImGui::ShowMetricsWindow(&show_imgui_metrics);
|
||||
}
|
||||
|
||||
if (show_memory_editor) {
|
||||
static MemoryEditor mem_edit;
|
||||
mem_edit.DrawWindow("Memory Editor", rom_data_, rom.getSize());
|
||||
}
|
||||
|
||||
if (show_imgui_style_editor) {
|
||||
ImGui::Begin("Style Editor (ImGui)", &show_imgui_style_editor);
|
||||
ImGui::ShowStyleEditor();
|
||||
@@ -154,6 +152,8 @@ void Editor::DrawViewMenu() const {
|
||||
ImGui::EndMenu();
|
||||
}
|
||||
|
||||
ImGui::MenuItem("HEX Editor", nullptr, &show_memory_editor);
|
||||
|
||||
ImGui::Separator();
|
||||
if (ImGui::BeginMenu("GUI Tools")) {
|
||||
ImGui::MenuItem("Metrics (ImGui)", nullptr, &show_imgui_metrics);
|
||||
@@ -165,6 +165,14 @@ void Editor::DrawViewMenu() const {
|
||||
}
|
||||
}
|
||||
|
||||
void Editor::DrawHelpMenu() const {
|
||||
if (ImGui::BeginMenu("Help")) {
|
||||
if (ImGui::MenuItem("About")) {
|
||||
}
|
||||
ImGui::EndMenu();
|
||||
}
|
||||
}
|
||||
|
||||
// first step would be to decompress all graphics data from the game
|
||||
// (in alttp that's easy they're all located in the same location all the
|
||||
// same sheet size 128x32) have a code that convert PC address to SNES and
|
||||
@@ -235,6 +243,12 @@ void Editor::DrawDungeonEditor() {
|
||||
}
|
||||
}
|
||||
|
||||
void Editor::DrawSpriteEditor() {
|
||||
if (ImGui::BeginTabItem("Sprites")) {
|
||||
ImGui::EndTabItem();
|
||||
}
|
||||
}
|
||||
|
||||
void Editor::DrawScreenEditor() {
|
||||
if (ImGui::BeginTabItem("Screens")) {
|
||||
ImGui::EndTabItem();
|
||||
@@ -243,7 +257,7 @@ void Editor::DrawScreenEditor() {
|
||||
|
||||
void Editor::DrawROMInfo() {
|
||||
if (ImGui::BeginTabItem("ROM Info")) {
|
||||
if (isLoaded) {
|
||||
if (rom.isLoaded()) {
|
||||
ImGui::Text("Title: %s", rom.getTitle());
|
||||
ImGui::Text("Version: %d", rom.getVersion());
|
||||
ImGui::Text("ROM Size: %ld", rom.getSize());
|
||||
|
||||
@@ -4,7 +4,6 @@
|
||||
#include <memory>
|
||||
|
||||
#include "Core/Icons.h"
|
||||
#include "Data/Overworld.h"
|
||||
#include "OverworldEditor.h"
|
||||
#include "ImGuiFileDialog/ImGuiFileDialog.h"
|
||||
#include "Utils/ROM.h"
|
||||
@@ -13,6 +12,7 @@
|
||||
#include "imgui/imgui.h"
|
||||
#include "imgui/imgui_internal.h"
|
||||
#include "imgui/misc/cpp/imgui_stdlib.h"
|
||||
#include "imgui/imgui_memory_editor.h"
|
||||
|
||||
namespace yaze {
|
||||
namespace Application {
|
||||
@@ -27,20 +27,20 @@ class Editor {
|
||||
void DrawFileMenu() const;
|
||||
void DrawEditMenu() const;
|
||||
void DrawViewMenu() const;
|
||||
void DrawHelpMenu() const;
|
||||
|
||||
void DrawOverworldEditor();
|
||||
void DrawDungeonEditor();
|
||||
void DrawSpriteEditor();
|
||||
void DrawScreenEditor();
|
||||
void DrawROMInfo();
|
||||
|
||||
bool isLoaded = false;
|
||||
bool doneLoaded = false;
|
||||
GLuint *overworld_texture;
|
||||
Data::Overworld overworld;
|
||||
::yaze::Application::Editor::OverworldEditor owEditor;
|
||||
OverworldEditor owEditor;
|
||||
Utils::ROM rom;
|
||||
|
||||
void* rom_data_;
|
||||
|
||||
bool isLoaded = true;
|
||||
ImGuiTableFlags toolset_table_flags = ImGuiTableFlags_SizingFixedFit;
|
||||
};
|
||||
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
#include "OverworldEditor.h"
|
||||
#include "Core/Icons.h"
|
||||
#include "Graphics/Bitmap.h"
|
||||
#include "Graphics/Tile.h"
|
||||
#include "imgui.h"
|
||||
#include <cmath>
|
||||
|
||||
@@ -7,6 +9,28 @@ namespace yaze {
|
||||
namespace Application {
|
||||
namespace Editor {
|
||||
void OverworldEditor::Update() {
|
||||
|
||||
if (rom_.isLoaded()) {
|
||||
if (!doneLoaded) {
|
||||
overworld.Load(rom_);
|
||||
Graphics::CreateAllGfxData(rom_.GetRawData(), allGfx16Ptr);
|
||||
|
||||
// allgfxBitmap.LoadBitmapFromROM(allGfx16Ptr, allgfx_texture,
|
||||
// &allgfx_width,
|
||||
// &allgfx_height);
|
||||
doneLoaded = true;
|
||||
}
|
||||
// Graphics::tile8 all_tiles;
|
||||
// all_tiles.id = 1;
|
||||
// all_tiles.data =
|
||||
// Graphics::export_tile_to_png(tile8 rawtile, const r_palette pal, const
|
||||
// char *filename)
|
||||
}
|
||||
|
||||
if (show_changelist_) {
|
||||
DrawChangelist();
|
||||
}
|
||||
|
||||
DrawToolset();
|
||||
ImGui::Separator();
|
||||
if (ImGui::BeginTable("#owEditTable", 2, ow_edit_flags, ImVec2(0, 0))) {
|
||||
@@ -21,7 +45,7 @@ void OverworldEditor::Update() {
|
||||
}
|
||||
|
||||
void OverworldEditor::DrawToolset() {
|
||||
if (ImGui::BeginTable("Toolset", 12, toolset_table_flags, ImVec2(0, 0))) {
|
||||
if (ImGui::BeginTable("Toolset", 14, toolset_table_flags, ImVec2(0, 0))) {
|
||||
|
||||
ImGui::TableSetupColumn("#undoTool");
|
||||
ImGui::TableSetupColumn("#redoTool");
|
||||
@@ -31,10 +55,12 @@ void OverworldEditor::DrawToolset() {
|
||||
ImGui::TableSetupColumn("#zoomInTool");
|
||||
ImGui::TableSetupColumn("#separator");
|
||||
ImGui::TableSetupColumn("#history");
|
||||
ImGui::TableSetupColumn("#entranceExitTool");
|
||||
ImGui::TableSetupColumn("#entranceTool");
|
||||
ImGui::TableSetupColumn("#exitTool");
|
||||
ImGui::TableSetupColumn("#itemTool");
|
||||
ImGui::TableSetupColumn("#spriteTool");
|
||||
ImGui::TableSetupColumn("#transportTool");
|
||||
ImGui::TableSetupColumn("#musicTool");
|
||||
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Button(ICON_MD_UNDO);
|
||||
@@ -43,7 +69,12 @@ void OverworldEditor::DrawToolset() {
|
||||
ImGui::Button(ICON_MD_REDO);
|
||||
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Button(ICON_MD_MANAGE_HISTORY);
|
||||
if (ImGui::Button(ICON_MD_MANAGE_HISTORY)) {
|
||||
if (!show_changelist_)
|
||||
show_changelist_ = true;
|
||||
else
|
||||
show_changelist_ = false;
|
||||
}
|
||||
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text(ICON_MD_MORE_VERT);
|
||||
@@ -61,7 +92,10 @@ void OverworldEditor::DrawToolset() {
|
||||
ImGui::Button(ICON_MD_DRAW);
|
||||
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Button(ICON_MD_SENSOR_DOOR);
|
||||
ImGui::Button(ICON_MD_DOOR_FRONT);
|
||||
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Button(ICON_MD_DOOR_BACK);
|
||||
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Button(ICON_MD_GRASS);
|
||||
@@ -72,6 +106,9 @@ void OverworldEditor::DrawToolset() {
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Button(ICON_MD_ADD_LOCATION);
|
||||
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Button(ICON_MD_MUSIC_NOTE);
|
||||
|
||||
ImGui::EndTable();
|
||||
}
|
||||
}
|
||||
@@ -222,18 +259,35 @@ void OverworldEditor::DrawOverworldCanvas() {
|
||||
}
|
||||
void OverworldEditor::DrawTileSelector() {
|
||||
if (ImGui::BeginTabBar("##TabBar")) {
|
||||
if (ImGui::BeginTabItem("Tile8")) {
|
||||
if (rom_.isLoaded()) {
|
||||
ImGui::Image((void *)(intptr_t)overworld_texture,
|
||||
ImVec2(overworld.overworldMapBitmap->GetWidth(),
|
||||
overworld.overworldMapBitmap->GetHeight()));
|
||||
}
|
||||
|
||||
ImGui::EndTabItem();
|
||||
}
|
||||
if (ImGui::BeginTabItem("Tile16")) {
|
||||
|
||||
ImGui::EndTabItem();
|
||||
}
|
||||
if (ImGui::BeginTabItem("Tile8")) {
|
||||
|
||||
ImGui::EndTabItem();
|
||||
}
|
||||
ImGui::EndTabBar();
|
||||
}
|
||||
}
|
||||
|
||||
void OverworldEditor::DrawChangelist() {
|
||||
if (!ImGui::Begin("Changelist")) {
|
||||
|
||||
ImGui::End();
|
||||
}
|
||||
|
||||
|
||||
ImGui::Text("Test");
|
||||
ImGui::End();
|
||||
}
|
||||
|
||||
} // namespace Editor
|
||||
} // namespace Application
|
||||
} // namespace yaze
|
||||
@@ -2,25 +2,50 @@
|
||||
#define YAZE_APPLICATION_EDITOR_OVERWORLDEDITOR_H
|
||||
|
||||
#include "Core/Icons.h"
|
||||
#include "Data/Overworld.h"
|
||||
#include "Utils/Compression.h"
|
||||
#include "imgui/imgui.h"
|
||||
#include "imgui/misc/cpp/imgui_stdlib.h"
|
||||
|
||||
namespace yaze {
|
||||
namespace Application {
|
||||
namespace Editor {
|
||||
|
||||
using byte = unsigned char;
|
||||
|
||||
class OverworldEditor {
|
||||
public:
|
||||
void Update();
|
||||
|
||||
void SetRom(Utils::ROM & rom) { rom_ = rom; }
|
||||
|
||||
private:
|
||||
void DrawToolset();
|
||||
void DrawOverworldMapSettings();
|
||||
void DrawOverworldCanvas();
|
||||
void DrawTileSelector();
|
||||
|
||||
void DrawChangelist();
|
||||
|
||||
bool show_changelist_ = false;
|
||||
|
||||
Utils::ROM rom_;
|
||||
Data::Overworld overworld;
|
||||
Utils::ALTTPCompression alttp_compressor_;
|
||||
Graphics::Bitmap allgfxBitmap;
|
||||
int allgfx_width = 0;
|
||||
int allgfx_height = 0;
|
||||
GLuint *allgfx_texture = nullptr;
|
||||
|
||||
byte* allGfx16Ptr = new byte[(128 * 7136) / 2];
|
||||
|
||||
GLuint *overworld_texture;
|
||||
|
||||
ImGuiTableFlags toolset_table_flags = ImGuiTableFlags_SizingFixedFit;
|
||||
ImGuiTableFlags ow_map_settings_flags = ImGuiTableFlags_Borders;
|
||||
ImGuiTableFlags ow_edit_flags = ImGuiTableFlags_Reorderable | ImGuiTableFlags_Resizable | ImGuiTableFlags_SizingStretchSame;
|
||||
ImGuiTableFlags ow_edit_flags = ImGuiTableFlags_Reorderable |
|
||||
ImGuiTableFlags_Resizable |
|
||||
ImGuiTableFlags_SizingStretchSame;
|
||||
|
||||
float canvas_table_ratio = 30.f;
|
||||
|
||||
@@ -32,6 +57,9 @@ private:
|
||||
|
||||
int current_world_ = 0;
|
||||
|
||||
bool isLoaded = false;
|
||||
bool doneLoaded = false;
|
||||
|
||||
constexpr static int kByteSize = 3;
|
||||
constexpr static int kMessageIdSize = 5;
|
||||
constexpr static float kInputFieldSize = 30.f;
|
||||
|
||||
@@ -1,9 +1,174 @@
|
||||
#include "Bitmap.h"
|
||||
#include "Utils/ROM.h"
|
||||
#include "Utils/Compression.h"
|
||||
|
||||
namespace yaze {
|
||||
namespace Application {
|
||||
namespace Graphics {
|
||||
|
||||
int GetPCGfxAddress(byte *romData, byte id) {
|
||||
char** info1, **info2,** info3, **info4;
|
||||
int gfxPointer1 =
|
||||
Utils::lorom_snes_to_pc((romData[Constants::gfx_1_pointer + 1] << 8) +
|
||||
(romData[Constants::gfx_1_pointer]), info1);
|
||||
int gfxPointer2 =
|
||||
Utils::lorom_snes_to_pc((romData[Constants::gfx_2_pointer + 1] << 8) +
|
||||
(romData[Constants::gfx_2_pointer]), info2);
|
||||
int gfxPointer3 =
|
||||
Utils::lorom_snes_to_pc((romData[Constants::gfx_3_pointer + 1] << 8) +
|
||||
(romData[Constants::gfx_3_pointer]), info3);
|
||||
|
||||
byte gfxGamePointer1 = romData[gfxPointer1 + id];
|
||||
byte gfxGamePointer2 = romData[gfxPointer2 + id];
|
||||
byte gfxGamePointer3 = romData[gfxPointer3 + id];
|
||||
|
||||
return Utils::lorom_snes_to_pc(Utils::AddressFromBytes(gfxGamePointer1, gfxGamePointer2,
|
||||
gfxGamePointer3), info4);
|
||||
}
|
||||
|
||||
byte *CreateAllGfxDataRaw(byte *romData) {
|
||||
// 0-112 -> compressed 3bpp bgr -> (decompressed each) 0x600 bytes
|
||||
// 113-114 -> compressed 2bpp -> (decompressed each) 0x800 bytes
|
||||
// 115-126 -> uncompressed 3bpp sprites -> (each) 0x600 bytes
|
||||
// 127-217 -> compressed 3bpp sprites -> (decompressed each) 0x600 bytes
|
||||
// 218-222 -> compressed 2bpp -> (decompressed each) 0x800 bytes
|
||||
|
||||
Utils::ALTTPCompression alttp_compressor_;
|
||||
|
||||
byte *buffer = new byte[346624];
|
||||
int bufferPos = 0;
|
||||
byte *data = new byte[2048];
|
||||
unsigned int uncompressedSize = 0;
|
||||
unsigned int compressedSize = 0;
|
||||
|
||||
for (int i = 0; i < Constants::NumberOfSheets; i++) {
|
||||
isbpp3[i] = ((i >= 0 && i <= 112) || // Compressed 3bpp bg
|
||||
(i >= 115 && i <= 126) || // Uncompressed 3bpp sprites
|
||||
(i >= 127 && i <= 217) // Compressed 3bpp sprites
|
||||
);
|
||||
|
||||
// uncompressed sheets
|
||||
if (i >= 115 && i <= 126) {
|
||||
data = new byte[Constants::Uncompressed3BPPSize];
|
||||
int startAddress = GetPCGfxAddress(romData, (byte)i);
|
||||
for (int j = 0; j < Constants::Uncompressed3BPPSize; j++) {
|
||||
data[j] = romData[j + startAddress];
|
||||
}
|
||||
} else {
|
||||
data = alttp_compressor_.DecompressGfx(
|
||||
romData, GetPCGfxAddress(romData, (byte)i),
|
||||
Constants::UncompressedSheetSize, &uncompressedSize, &compressedSize);
|
||||
}
|
||||
|
||||
for (int j = 0; j < sizeof(data); j++) {
|
||||
buffer[j + bufferPos] = data[j];
|
||||
}
|
||||
|
||||
bufferPos += sizeof(data);
|
||||
}
|
||||
|
||||
return buffer;
|
||||
}
|
||||
|
||||
void CreateAllGfxData(byte *romData, byte* allgfx16Ptr) {
|
||||
byte* data = CreateAllGfxDataRaw(romData);
|
||||
byte* newData =
|
||||
new byte[0x6F800]; // NEED TO GET THE APPROPRIATE SIZE FOR THAT
|
||||
byte* mask = new byte[]{0x80, 0x40, 0x20, 0x10, 0x08, 0x04, 0x02, 0x01};
|
||||
int sheetPosition = 0;
|
||||
|
||||
// 8x8 tile
|
||||
for (int s = 0; s < Constants::NumberOfSheets; s++) // Per Sheet
|
||||
{
|
||||
for (int j = 0; j < 4; j++) // Per Tile Line Y
|
||||
{
|
||||
for (int i = 0; i < 16; i++) // Per Tile Line X
|
||||
{
|
||||
for (int y = 0; y < 8; y++) // Per Pixel Line
|
||||
{
|
||||
if (isbpp3[s]) {
|
||||
byte lineBits0 =
|
||||
data[(y * 2) + (i * 24) + (j * 384) + sheetPosition];
|
||||
byte lineBits1 =
|
||||
data[(y * 2) + (i * 24) + (j * 384) + 1 + sheetPosition];
|
||||
byte lineBits2 =
|
||||
data[(y) + (i * 24) + (j * 384) + 16 + sheetPosition];
|
||||
|
||||
for (int x = 0; x < 4; x++) // Per Pixel X
|
||||
{
|
||||
byte pixdata = 0;
|
||||
byte pixdata2 = 0;
|
||||
|
||||
if ((lineBits0 & mask[(x * 2)]) == mask[(x * 2)]) {
|
||||
pixdata += 1;
|
||||
}
|
||||
if ((lineBits1 & mask[(x * 2)]) == mask[(x * 2)]) {
|
||||
pixdata += 2;
|
||||
}
|
||||
if ((lineBits2 & mask[(x * 2)]) == mask[(x * 2)]) {
|
||||
pixdata += 4;
|
||||
}
|
||||
|
||||
if ((lineBits0 & mask[(x * 2) + 1]) == mask[(x * 2) + 1]) {
|
||||
pixdata2 += 1;
|
||||
}
|
||||
if ((lineBits1 & mask[(x * 2) + 1]) == mask[(x * 2) + 1]) {
|
||||
pixdata2 += 2;
|
||||
}
|
||||
if ((lineBits2 & mask[(x * 2) + 1]) == mask[(x * 2) + 1]) {
|
||||
pixdata2 += 4;
|
||||
}
|
||||
|
||||
newData[(y * 64) + (x) + (i * 4) + (j * 512) + (s * 2048)] =
|
||||
(byte)((pixdata << 4) | pixdata2);
|
||||
}
|
||||
} else {
|
||||
byte lineBits0 =
|
||||
data[(y * 2) + (i * 16) + (j * 256) + sheetPosition];
|
||||
byte lineBits1 =
|
||||
data[(y * 2) + (i * 16) + (j * 256) + 1 + sheetPosition];
|
||||
|
||||
for (int x = 0; x < 4; x++) // Per Pixel X
|
||||
{
|
||||
byte pixdata = 0;
|
||||
byte pixdata2 = 0;
|
||||
|
||||
if ((lineBits0 & mask[(x * 2)]) == mask[(x * 2)]) {
|
||||
pixdata += 1;
|
||||
}
|
||||
if ((lineBits1 & mask[(x * 2)]) == mask[(x * 2)]) {
|
||||
pixdata += 2;
|
||||
}
|
||||
|
||||
if ((lineBits0 & mask[(x * 2) + 1]) == mask[(x * 2) + 1]) {
|
||||
pixdata2 += 1;
|
||||
}
|
||||
if ((lineBits1 & mask[(x * 2) + 1]) == mask[(x * 2) + 1]) {
|
||||
pixdata2 += 2;
|
||||
}
|
||||
|
||||
newData[(y * 64) + (x) + (i * 4) + (j * 512) + (s * 2048)] =
|
||||
(byte)((pixdata << 4) | pixdata2);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (isbpp3[s]) {
|
||||
sheetPosition += Constants::Uncompressed3BPPSize;
|
||||
} else {
|
||||
sheetPosition += Constants::UncompressedSheetSize;
|
||||
}
|
||||
}
|
||||
|
||||
byte *allgfx16Data = (byte *) allgfx16Ptr;
|
||||
|
||||
for (int i = 0; i < 0x6F800; i++) {
|
||||
allgfx16Data[i] = newData[i];
|
||||
}
|
||||
}
|
||||
|
||||
Bitmap::Bitmap(int width, int height, byte *data)
|
||||
: width_(width), height_(height), pixel_data_(data) {}
|
||||
|
||||
@@ -45,12 +210,8 @@ void Bitmap::Create(GLuint* out_texture) {
|
||||
*out_texture = image_texture;
|
||||
}
|
||||
|
||||
int Bitmap::GetWidth() {
|
||||
return width_;
|
||||
}
|
||||
int Bitmap::GetHeight() {
|
||||
return height_;
|
||||
}
|
||||
int Bitmap::GetWidth() { return width_; }
|
||||
int Bitmap::GetHeight() { return height_; }
|
||||
|
||||
// Simple helper function to load an image into a OpenGL texture with common
|
||||
// settings
|
||||
@@ -59,7 +220,8 @@ bool Bitmap::LoadBitmapFromROM(unsigned char* texture_data, GLuint* out_texture,
|
||||
// Load from file
|
||||
int image_width = 0;
|
||||
int image_height = 0;
|
||||
if (texture_data == NULL) return false;
|
||||
if (texture_data == NULL)
|
||||
return false;
|
||||
|
||||
// Create a OpenGL texture identifier
|
||||
GLuint image_texture;
|
||||
|
||||
@@ -14,6 +14,7 @@ namespace Application {
|
||||
namespace Graphics {
|
||||
|
||||
using byte = unsigned char;
|
||||
using namespace Core;
|
||||
|
||||
class Bitmap {
|
||||
public:
|
||||
@@ -31,8 +32,14 @@ private:
|
||||
int width_;
|
||||
int height_;
|
||||
byte *pixel_data_;
|
||||
SDL_PixelFormat pixel_format_;
|
||||
};
|
||||
|
||||
static bool isbpp3[Constants::NumberOfSheets];
|
||||
|
||||
int GetPCGfxAddress(byte* romData, byte id);
|
||||
byte* CreateAllGfxDataRaw(byte* romData);
|
||||
void CreateAllGfxData(byte* romData, byte* allgfx16Ptr);
|
||||
|
||||
} // namespace Graphics
|
||||
} // namespace Application
|
||||
} // namespace yaze
|
||||
|
||||
@@ -189,14 +189,17 @@ byte* pack_bpp_tile(tile8 tile, const unsigned int bpp, unsigned int* size) {
|
||||
for (unsigned int col = 0; col < 8; col++) {
|
||||
for (unsigned int row = 0; row < 8; row++) {
|
||||
byte color = tile.data[col * 8 + row];
|
||||
if (color > maxcolor) return NULL;
|
||||
if (color > maxcolor)
|
||||
return NULL;
|
||||
|
||||
if (bpp == 1) output[col] += (byte)((color & 1) << (7 - row));
|
||||
if (bpp == 1)
|
||||
output[col] += (byte)((color & 1) << (7 - row));
|
||||
if (bpp >= 2) {
|
||||
output[col * 2] += (byte)((color & 1) << (7 - row));
|
||||
output[col * 2 + 1] += (byte)(((color & 2) == 2) << (7 - row));
|
||||
}
|
||||
if (bpp == 3) output[16 + col] += (byte)(((color & 4) == 4) << (7 - row));
|
||||
if (bpp == 3)
|
||||
output[16 + col] += (byte)(((color & 4) == 4) << (7 - row));
|
||||
if (bpp >= 4) {
|
||||
output[16 + col * 2] += (byte)(((color & 4) == 4) << (7 - row));
|
||||
output[16 + col * 2 + 1] += (byte)(((color & 8) == 8) << (7 - row));
|
||||
|
||||
@@ -250,6 +250,7 @@ void ROM::LoadFromFile(const std::string& path) {
|
||||
fclose(file);
|
||||
|
||||
memcpy(title, current_rom_, 21);
|
||||
|
||||
type = LoROM;
|
||||
fastrom = (current_rom_[21] & 0b00110000) == 0b00110000;
|
||||
if (current_rom_[21] & 1)
|
||||
@@ -265,6 +266,11 @@ void ROM::LoadFromFile(const std::string& path) {
|
||||
make_sense = false;
|
||||
if ((checksum ^ checksum_comp) == 0xFFFF)
|
||||
make_sense = true;
|
||||
|
||||
loaded = true;
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
int ROM::SnesToPc(int addr) {
|
||||
@@ -293,7 +299,7 @@ int ROM::PcToSnes(int addr) {
|
||||
return ((addr * 2) & 0xFF0000) + (addr & 0x7FFF) + 0x8000;
|
||||
}
|
||||
|
||||
int ROM::AddressFromBytes(byte addr1, byte addr2, byte addr3) {
|
||||
int AddressFromBytes(byte addr1, byte addr2, byte addr3) {
|
||||
return (addr1 << 16) | (addr2 << 8) | addr3;
|
||||
}
|
||||
|
||||
|
||||
@@ -48,11 +48,12 @@ int hirom_pc_to_snes(const unsigned int pc_addr);
|
||||
int hirom_sram_pc_to_snes(const unsigned int pc_addr);
|
||||
}
|
||||
|
||||
int AddressFromBytes(byte addr1, byte addr2, byte addr3);
|
||||
|
||||
class ROM {
|
||||
public:
|
||||
int SnesToPc(int addr);
|
||||
int PcToSnes(int addr);
|
||||
int AddressFromBytes(byte addr1, byte addr2, byte addr3);
|
||||
short AddressFromBytes(byte addr1, byte addr2);
|
||||
ushort ReadShort(int addr);
|
||||
void Write(int addr, byte value);
|
||||
@@ -69,10 +70,14 @@ class ROM {
|
||||
unsigned int getSize() const { return size; }
|
||||
char getVersion() const { return version; }
|
||||
|
||||
bool isLoaded() const { return loaded; }
|
||||
|
||||
private:
|
||||
std::vector<char> original_rom_;
|
||||
std::vector<char> working_rom_;
|
||||
|
||||
bool loaded = false;
|
||||
|
||||
byte* current_rom_;
|
||||
|
||||
enum rom_type type;
|
||||
|
||||
Reference in New Issue
Block a user