Add yaze_config.h for version management and update version check logic
This commit is contained in:
@@ -4,6 +4,7 @@ cmake_minimum_required(VERSION 3.10)
|
|||||||
project(yaze VERSION 0.2.0
|
project(yaze VERSION 0.2.0
|
||||||
DESCRIPTION "Yet Another Zelda3 Editor"
|
DESCRIPTION "Yet Another Zelda3 Editor"
|
||||||
LANGUAGES CXX)
|
LANGUAGES CXX)
|
||||||
|
configure_file(src/yaze_config.h.in yaze_config.h)
|
||||||
|
|
||||||
# Build Flags
|
# Build Flags
|
||||||
set(YAZE_BUILD_APP ON)
|
set(YAZE_BUILD_APP ON)
|
||||||
|
|||||||
@@ -5,35 +5,34 @@ include(app/gui/gui.cmake)
|
|||||||
include(app/zelda3/zelda3.cmake)
|
include(app/zelda3/zelda3.cmake)
|
||||||
|
|
||||||
if (APPLE)
|
if (APPLE)
|
||||||
add_executable(
|
add_executable(
|
||||||
yaze
|
yaze
|
||||||
MACOSX_BUNDLE
|
MACOSX_BUNDLE
|
||||||
app/main.cc
|
app/main.cc
|
||||||
app/rom.cc
|
app/rom.cc
|
||||||
${YAZE_APP_EMU_SRC}
|
${YAZE_APP_EMU_SRC}
|
||||||
${YAZE_APP_CORE_SRC}
|
${YAZE_APP_CORE_SRC}
|
||||||
${YAZE_APP_EDITOR_SRC}
|
${YAZE_APP_EDITOR_SRC}
|
||||||
${YAZE_APP_GFX_SRC}
|
${YAZE_APP_GFX_SRC}
|
||||||
${YAZE_APP_ZELDA3_SRC}
|
${YAZE_APP_ZELDA3_SRC}
|
||||||
${YAZE_GUI_SRC}
|
${YAZE_GUI_SRC}
|
||||||
${IMGUI_SRC}
|
${IMGUI_SRC}
|
||||||
|
# Bundled Resources
|
||||||
# Bundled Resources
|
${YAZE_RESOURCE_FILES}
|
||||||
${YAZE_RESOURCE_FILES}
|
)
|
||||||
)
|
|
||||||
else()
|
else()
|
||||||
add_executable(
|
add_executable(
|
||||||
yaze
|
yaze
|
||||||
app/main.cc
|
app/main.cc
|
||||||
app/rom.cc
|
app/rom.cc
|
||||||
${YAZE_APP_EMU_SRC}
|
${YAZE_APP_EMU_SRC}
|
||||||
${YAZE_APP_CORE_SRC}
|
${YAZE_APP_CORE_SRC}
|
||||||
${YAZE_APP_EDITOR_SRC}
|
${YAZE_APP_EDITOR_SRC}
|
||||||
${YAZE_APP_GFX_SRC}
|
${YAZE_APP_GFX_SRC}
|
||||||
${YAZE_APP_ZELDA3_SRC}
|
${YAZE_APP_ZELDA3_SRC}
|
||||||
${YAZE_GUI_SRC}
|
${YAZE_GUI_SRC}
|
||||||
${IMGUI_SRC}
|
${IMGUI_SRC}
|
||||||
)
|
)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
target_include_directories(
|
target_include_directories(
|
||||||
@@ -46,6 +45,7 @@ target_include_directories(
|
|||||||
${CMAKE_SOURCE_DIR}/src/lib/imgui_test_engine
|
${CMAKE_SOURCE_DIR}/src/lib/imgui_test_engine
|
||||||
${PNG_INCLUDE_DIRS}
|
${PNG_INCLUDE_DIRS}
|
||||||
${SDL2_INCLUDE_DIR}
|
${SDL2_INCLUDE_DIR}
|
||||||
|
${PROJECT_BINARY_DIR}
|
||||||
)
|
)
|
||||||
|
|
||||||
target_link_libraries(
|
target_link_libraries(
|
||||||
|
|||||||
@@ -361,17 +361,5 @@ void ApplyBpsPatch(const std::vector<uint8_t> &source,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
absl::StatusOr<std::string> CheckVersion(const char *version) {
|
|
||||||
std::string version_string = version;
|
|
||||||
if (version_string != kYazeVersion) {
|
|
||||||
std::string message =
|
|
||||||
absl::StrFormat("Yaze version mismatch: expected %s, got %s",
|
|
||||||
kYazeVersion.data(), version_string.c_str());
|
|
||||||
return absl::InvalidArgumentError(message);
|
|
||||||
}
|
|
||||||
return version_string;
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace core
|
} // namespace core
|
||||||
|
|
||||||
} // namespace yaze
|
} // namespace yaze
|
||||||
|
|||||||
@@ -313,12 +313,7 @@ void ApplyBpsPatch(const std::vector<uint8_t> &source,
|
|||||||
const std::vector<uint8_t> &patch,
|
const std::vector<uint8_t> &patch,
|
||||||
std::vector<uint8_t> &target);
|
std::vector<uint8_t> &target);
|
||||||
|
|
||||||
constexpr std::string_view kYazeVersion = "0.2.1";
|
} // namespace core
|
||||||
|
} // namespace yaze
|
||||||
absl::StatusOr<std::string> CheckVersion(const char *version);
|
|
||||||
|
|
||||||
} // namespace core
|
|
||||||
|
|
||||||
} // namespace yaze
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -46,7 +46,7 @@ bool IsEditorActive(Editor *editor, std::vector<Editor *> &active_editors) {
|
|||||||
active_editors.end();
|
active_editors.end();
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
void EditorManager::Initialize(std::string filename) {
|
void EditorManager::Initialize(std::string filename) {
|
||||||
if (!filename.empty()) {
|
if (!filename.empty()) {
|
||||||
@@ -77,8 +77,7 @@ absl::Status EditorManager::Update() {
|
|||||||
void EditorManager::ManageActiveEditors() {
|
void EditorManager::ManageActiveEditors() {
|
||||||
// Show popup pane to select an editor to add
|
// Show popup pane to select an editor to add
|
||||||
static bool show_add_editor = false;
|
static bool show_add_editor = false;
|
||||||
if (show_add_editor)
|
if (show_add_editor) OpenPopup("AddEditor");
|
||||||
OpenPopup("AddEditor");
|
|
||||||
|
|
||||||
if (BeginPopup("AddEditor", ImGuiWindowFlags_AlwaysAutoResize)) {
|
if (BeginPopup("AddEditor", ImGuiWindowFlags_AlwaysAutoResize)) {
|
||||||
if (MenuItem("Overworld", nullptr, false,
|
if (MenuItem("Overworld", nullptr, false,
|
||||||
@@ -143,85 +142,85 @@ void EditorManager::ManageActiveEditors() {
|
|||||||
for (auto editor : active_editors_) {
|
for (auto editor : active_editors_) {
|
||||||
bool open = true;
|
bool open = true;
|
||||||
switch (editor->type()) {
|
switch (editor->type()) {
|
||||||
case EditorType::kOverworld:
|
case EditorType::kOverworld:
|
||||||
if (overworld_editor_.jump_to_tab() == -1) {
|
if (overworld_editor_.jump_to_tab() == -1) {
|
||||||
if (BeginTabItem("Overworld", &open)) {
|
if (BeginTabItem("Overworld", &open)) {
|
||||||
current_editor_ = &overworld_editor_;
|
current_editor_ = &overworld_editor_;
|
||||||
status_ = overworld_editor_.Update();
|
status_ = overworld_editor_.Update();
|
||||||
|
EndTabItem();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case EditorType::kDungeon:
|
||||||
|
if (BeginTabItem("Dungeon", &open)) {
|
||||||
|
current_editor_ = &dungeon_editor_;
|
||||||
|
status_ = dungeon_editor_.Update();
|
||||||
|
if (overworld_editor_.jump_to_tab() != -1) {
|
||||||
|
dungeon_editor_.add_room(overworld_editor_.jump_to_tab());
|
||||||
|
overworld_editor_.jump_to_tab_ = -1;
|
||||||
|
}
|
||||||
EndTabItem();
|
EndTabItem();
|
||||||
}
|
}
|
||||||
}
|
break;
|
||||||
break;
|
case EditorType::kGraphics:
|
||||||
case EditorType::kDungeon:
|
if (BeginTabItem("Graphics", &open)) {
|
||||||
if (BeginTabItem("Dungeon", &open)) {
|
current_editor_ = &graphics_editor_;
|
||||||
current_editor_ = &dungeon_editor_;
|
status_ = graphics_editor_.Update();
|
||||||
status_ = dungeon_editor_.Update();
|
EndTabItem();
|
||||||
if (overworld_editor_.jump_to_tab() != -1) {
|
|
||||||
dungeon_editor_.add_room(overworld_editor_.jump_to_tab());
|
|
||||||
overworld_editor_.jump_to_tab_ = -1;
|
|
||||||
}
|
}
|
||||||
EndTabItem();
|
break;
|
||||||
}
|
case EditorType::kMusic:
|
||||||
break;
|
if (BeginTabItem("Music", &open)) {
|
||||||
case EditorType::kGraphics:
|
current_editor_ = &music_editor_;
|
||||||
if (BeginTabItem("Graphics", &open)) {
|
|
||||||
current_editor_ = &graphics_editor_;
|
|
||||||
status_ = graphics_editor_.Update();
|
|
||||||
EndTabItem();
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case EditorType::kMusic:
|
|
||||||
if (BeginTabItem("Music", &open)) {
|
|
||||||
current_editor_ = &music_editor_;
|
|
||||||
|
|
||||||
status_ = music_editor_.Update();
|
status_ = music_editor_.Update();
|
||||||
EndTabItem();
|
EndTabItem();
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case EditorType::kPalette:
|
case EditorType::kPalette:
|
||||||
if (BeginTabItem("Palette", &open)) {
|
if (BeginTabItem("Palette", &open)) {
|
||||||
current_editor_ = &palette_editor_;
|
current_editor_ = &palette_editor_;
|
||||||
status_ = palette_editor_.Update();
|
status_ = palette_editor_.Update();
|
||||||
EndTabItem();
|
EndTabItem();
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case EditorType::kScreen:
|
case EditorType::kScreen:
|
||||||
if (BeginTabItem("Screen", &open)) {
|
if (BeginTabItem("Screen", &open)) {
|
||||||
current_editor_ = &screen_editor_;
|
current_editor_ = &screen_editor_;
|
||||||
status_ = screen_editor_.Update();
|
status_ = screen_editor_.Update();
|
||||||
EndTabItem();
|
EndTabItem();
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case EditorType::kSprite:
|
case EditorType::kSprite:
|
||||||
if (BeginTabItem("Sprite", &open)) {
|
if (BeginTabItem("Sprite", &open)) {
|
||||||
current_editor_ = &sprite_editor_;
|
current_editor_ = &sprite_editor_;
|
||||||
status_ = sprite_editor_.Update();
|
status_ = sprite_editor_.Update();
|
||||||
EndTabItem();
|
EndTabItem();
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case EditorType::kAssembly:
|
case EditorType::kAssembly:
|
||||||
if (BeginTabItem("Code", &open)) {
|
if (BeginTabItem("Code", &open)) {
|
||||||
current_editor_ = &assembly_editor_;
|
current_editor_ = &assembly_editor_;
|
||||||
assembly_editor_.UpdateCodeView();
|
assembly_editor_.UpdateCodeView();
|
||||||
EndTabItem();
|
EndTabItem();
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case EditorType::kSettings:
|
case EditorType::kSettings:
|
||||||
if (BeginTabItem("Settings", &open)) {
|
if (BeginTabItem("Settings", &open)) {
|
||||||
current_editor_ = &settings_editor_;
|
current_editor_ = &settings_editor_;
|
||||||
status_ = settings_editor_.Update();
|
status_ = settings_editor_.Update();
|
||||||
EndTabItem();
|
EndTabItem();
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case EditorType::kMessage:
|
case EditorType::kMessage:
|
||||||
if (BeginTabItem("Message", &open)) {
|
if (BeginTabItem("Message", &open)) {
|
||||||
current_editor_ = &message_editor_;
|
current_editor_ = &message_editor_;
|
||||||
status_ = message_editor_.Update();
|
status_ = message_editor_.Update();
|
||||||
EndTabItem();
|
EndTabItem();
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (!open) {
|
if (!open) {
|
||||||
active_editors_.erase(
|
active_editors_.erase(
|
||||||
@@ -326,10 +325,9 @@ void EditorManager::DrawStatusPopup() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void EditorManager::DrawAboutPopup() {
|
void EditorManager::DrawAboutPopup() {
|
||||||
if (about_)
|
if (about_) OpenPopup("About");
|
||||||
OpenPopup("About");
|
|
||||||
if (BeginPopupModal("About", nullptr, ImGuiWindowFlags_AlwaysAutoResize)) {
|
if (BeginPopupModal("About", nullptr, ImGuiWindowFlags_AlwaysAutoResize)) {
|
||||||
Text("Yet Another Zelda3 Editor - v%s", core::kYazeVersion.data());
|
Text("Yet Another Zelda3 Editor - v%s", version_.c_str());
|
||||||
Text("Written by: scawful");
|
Text("Written by: scawful");
|
||||||
Spacing();
|
Spacing();
|
||||||
Text("Special Thanks: Zarby89, JaredBrian");
|
Text("Special Thanks: Zarby89, JaredBrian");
|
||||||
@@ -344,8 +342,7 @@ void EditorManager::DrawAboutPopup() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void EditorManager::DrawInfoPopup() {
|
void EditorManager::DrawInfoPopup() {
|
||||||
if (rom_info_)
|
if (rom_info_) OpenPopup("ROM Information");
|
||||||
OpenPopup("ROM Information");
|
|
||||||
if (BeginPopupModal("ROM Information", nullptr,
|
if (BeginPopupModal("ROM Information", nullptr,
|
||||||
ImGuiWindowFlags_AlwaysAutoResize)) {
|
ImGuiWindowFlags_AlwaysAutoResize)) {
|
||||||
Text("Title: %s", rom()->title().c_str());
|
Text("Title: %s", rom()->title().c_str());
|
||||||
@@ -372,7 +369,7 @@ void EditorManager::DrawYazeMenu() {
|
|||||||
show_display_settings = !show_display_settings;
|
show_display_settings = !show_display_settings;
|
||||||
}
|
}
|
||||||
PopStyleColor();
|
PopStyleColor();
|
||||||
Text("yaze v%s", core::kYazeVersion.data());
|
Text("yaze v%s", version_.c_str());
|
||||||
EndMenuBar();
|
EndMenuBar();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -540,14 +537,10 @@ void EditorManager::DrawYazeMenuBar() {
|
|||||||
static bool show_palette_editor = false;
|
static bool show_palette_editor = false;
|
||||||
static bool show_emulator = false;
|
static bool show_emulator = false;
|
||||||
|
|
||||||
if (show_imgui_demo)
|
if (show_imgui_demo) ShowDemoWindow();
|
||||||
ShowDemoWindow();
|
if (show_imgui_metrics) ShowMetricsWindow(&show_imgui_metrics);
|
||||||
if (show_imgui_metrics)
|
if (show_memory_editor) memory_editor_.Update(show_memory_editor);
|
||||||
ShowMetricsWindow(&show_imgui_metrics);
|
if (show_asm_editor) assembly_editor_.Update(show_asm_editor);
|
||||||
if (show_memory_editor)
|
|
||||||
memory_editor_.Update(show_memory_editor);
|
|
||||||
if (show_asm_editor)
|
|
||||||
assembly_editor_.Update(show_asm_editor);
|
|
||||||
|
|
||||||
if (show_emulator) {
|
if (show_emulator) {
|
||||||
Begin("Emulator", &show_emulator, ImGuiWindowFlags_MenuBar);
|
Begin("Emulator", &show_emulator, ImGuiWindowFlags_MenuBar);
|
||||||
@@ -592,20 +585,15 @@ void EditorManager::DrawYazeMenuBar() {
|
|||||||
static bool open_supported_features = false;
|
static bool open_supported_features = false;
|
||||||
static bool open_manage_project = false;
|
static bool open_manage_project = false;
|
||||||
if (BeginMenu("Help")) {
|
if (BeginMenu("Help")) {
|
||||||
if (MenuItem("How to open a ROM"))
|
if (MenuItem("How to open a ROM")) open_rom_help = true;
|
||||||
open_rom_help = true;
|
if (MenuItem("Supported Features")) open_supported_features = true;
|
||||||
if (MenuItem("Supported Features"))
|
if (MenuItem("How to manage a project")) open_manage_project = true;
|
||||||
open_supported_features = true;
|
|
||||||
if (MenuItem("How to manage a project"))
|
|
||||||
open_manage_project = true;
|
|
||||||
|
|
||||||
if (MenuItem("About", "F1"))
|
if (MenuItem("About", "F1")) about_ = true;
|
||||||
about_ = true;
|
|
||||||
EndMenu();
|
EndMenu();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (open_supported_features)
|
if (open_supported_features) OpenPopup("Supported Features");
|
||||||
OpenPopup("Supported Features");
|
|
||||||
if (BeginPopupModal("Supported Features", nullptr,
|
if (BeginPopupModal("Supported Features", nullptr,
|
||||||
ImGuiWindowFlags_AlwaysAutoResize)) {
|
ImGuiWindowFlags_AlwaysAutoResize)) {
|
||||||
Text("Overworld");
|
Text("Overworld");
|
||||||
@@ -638,8 +626,7 @@ void EditorManager::DrawYazeMenuBar() {
|
|||||||
EndPopup();
|
EndPopup();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (open_rom_help)
|
if (open_rom_help) OpenPopup("Open a ROM");
|
||||||
OpenPopup("Open a ROM");
|
|
||||||
if (BeginPopupModal("Open a ROM", nullptr,
|
if (BeginPopupModal("Open a ROM", nullptr,
|
||||||
ImGuiWindowFlags_AlwaysAutoResize)) {
|
ImGuiWindowFlags_AlwaysAutoResize)) {
|
||||||
Text("File -> Open");
|
Text("File -> Open");
|
||||||
@@ -656,8 +643,7 @@ void EditorManager::DrawYazeMenuBar() {
|
|||||||
EndPopup();
|
EndPopup();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (open_manage_project)
|
if (open_manage_project) OpenPopup("Manage Project");
|
||||||
OpenPopup("Manage Project");
|
|
||||||
if (BeginPopupModal("Manage Project", nullptr,
|
if (BeginPopupModal("Manage Project", nullptr,
|
||||||
ImGuiWindowFlags_AlwaysAutoResize)) {
|
ImGuiWindowFlags_AlwaysAutoResize)) {
|
||||||
Text("Project Menu");
|
Text("Project Menu");
|
||||||
@@ -740,5 +726,5 @@ absl::Status EditorManager::OpenProject() {
|
|||||||
return absl::OkStatus();
|
return absl::OkStatus();
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace editor
|
} // namespace editor
|
||||||
} // namespace yaze
|
} // namespace yaze
|
||||||
|
|||||||
@@ -19,6 +19,7 @@
|
|||||||
#include "app/emu/emulator.h"
|
#include "app/emu/emulator.h"
|
||||||
#include "app/gui/input.h"
|
#include "app/gui/input.h"
|
||||||
#include "app/rom.h"
|
#include "app/rom.h"
|
||||||
|
#include "yaze_config.h"
|
||||||
|
|
||||||
namespace yaze {
|
namespace yaze {
|
||||||
namespace editor {
|
namespace editor {
|
||||||
@@ -45,6 +46,10 @@ class EditorManager : public SharedRom, public core::ExperimentFlags {
|
|||||||
active_editors_.push_back(&sprite_editor_);
|
active_editors_.push_back(&sprite_editor_);
|
||||||
active_editors_.push_back(&message_editor_);
|
active_editors_.push_back(&message_editor_);
|
||||||
active_editors_.push_back(&screen_editor_);
|
active_editors_.push_back(&screen_editor_);
|
||||||
|
std::stringstream ss;
|
||||||
|
ss << YAZE_VERSION_MAJOR << "." << YAZE_VERSION_MINOR << "."
|
||||||
|
<< YAZE_VERSION_PATCH;
|
||||||
|
ss >> version_;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Initialize(std::string filename = "");
|
void Initialize(std::string filename = "");
|
||||||
@@ -78,6 +83,8 @@ class EditorManager : public SharedRom, public core::ExperimentFlags {
|
|||||||
bool show_status_ = false;
|
bool show_status_ = false;
|
||||||
bool rom_assets_loaded_ = false;
|
bool rom_assets_loaded_ = false;
|
||||||
|
|
||||||
|
std::string version_ = "";
|
||||||
|
|
||||||
absl::Status status_;
|
absl::Status status_;
|
||||||
emu::Emulator emulator_;
|
emu::Emulator emulator_;
|
||||||
std::vector<Editor *> active_editors_;
|
std::vector<Editor *> active_editors_;
|
||||||
|
|||||||
16
src/yaze.cc
16
src/yaze.cc
@@ -1,19 +1,25 @@
|
|||||||
#include "yaze.h"
|
#include "yaze.h"
|
||||||
|
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
#include <sstream>
|
||||||
|
|
||||||
#include "app/rom.h"
|
#include "app/rom.h"
|
||||||
#include "app/zelda3/overworld/overworld.h"
|
#include "app/zelda3/overworld/overworld.h"
|
||||||
#include "dungeon.h"
|
#include "dungeon.h"
|
||||||
|
#include "yaze_config.h"
|
||||||
|
|
||||||
void yaze_check_version(const char *version) {
|
void yaze_check_version(const char *version) {
|
||||||
std::cout << "Yaze version: " << version << std::endl;
|
std::string current_version;
|
||||||
auto version_check = yaze::core::CheckVersion(version);
|
std::stringstream ss;
|
||||||
if (!version_check.ok()) {
|
ss << YAZE_VERSION_MAJOR << "." << YAZE_VERSION_MINOR << "."
|
||||||
std::cout << version_check.status().message() << std::endl;
|
<< YAZE_VERSION_PATCH;
|
||||||
|
ss >> current_version;
|
||||||
|
|
||||||
|
if (version != current_version) {
|
||||||
|
std::cout << "Yaze version mismatch: expected " << current_version
|
||||||
|
<< ", got " << version << std::endl;
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int yaze_init(yaze_editor_context *yaze_ctx) {
|
int yaze_init(yaze_editor_context *yaze_ctx) {
|
||||||
|
|||||||
4
src/yaze_config.h.in
Normal file
4
src/yaze_config.h.in
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
// yaze config file
|
||||||
|
#define YAZE_VERSION_MAJOR @yaze_VERSION_MAJOR@
|
||||||
|
#define YAZE_VERSION_MINOR @yaze_VERSION_MINOR@
|
||||||
|
#define YAZE_VERSION_PATCH @yaze_VERSION_PATCH@
|
||||||
Reference in New Issue
Block a user