52 static std::string patch_file;
53 static std::string base_file;
55 auto patch_file_input = Input(&patch_file,
"Patch file path");
56 auto base_file_input = Input(&base_file,
"Base file path");
59 auto apply_button = Button(
"Apply Patch", [&] {
60 std::vector<uint8_t> source = app_context.rom.vector();
64 std::vector<uint8_t> patch;
66 std::copy(patch_contents.begin(), patch_contents.end(),
67 std::back_inserter(patch));
68 std::vector<uint8_t> patched;
72 }
catch (
const std::runtime_error &e) {
73 app_context.error_message = e.what();
80 auto dot_pos = base_file.find_last_of(
'.');
81 auto patched_file = base_file.substr(0, dot_pos) +
"_patched" +
82 base_file.substr(dot_pos, base_file.size() - dot_pos);
83 std::ofstream file(patched_file, std::ios::binary);
84 if (!file.is_open()) {
85 app_context.error_message =
"Could not open file for writing.";
90 file.write(
reinterpret_cast<const char *
>(patched.data()), patched.size());
97 auto return_button = Button(
"Back to Main Menu", [&] {
102 auto container = Container::Vertical({
109 auto renderer =
Renderer(container, [&] {
110 return vbox({text(
"Apply BPS Patch") | center, separator(),
111 text(
"Enter Patch File:"), patch_file_input->Render(),
112 text(
"Enter Base File:"), base_file_input->Render(),
115 apply_button->Render() | center,
117 return_button->Render() | center,
122 screen.Loop(renderer);
129 const static std::vector<std::string> items = {
"Bow",
154 constexpr size_t kNumItems = 28;
155 std::array<bool, kNumItems> values = {};
156 auto checkboxes = Container::Vertical({});
157 for (
size_t i = 0; i < items.size(); i += 4) {
158 auto row = Container::Horizontal({});
159 for (
size_t j = 0; j < 4 && (i + j) < items.size(); ++j) {
161 Checkbox(absl::StrCat(items[i + j],
" ").data(), &values[i + j]));
163 checkboxes->Add(row);
170 static int sword = 0;
171 static int shield = 0;
172 static int armor = 0;
174 const std::vector<std::string> sword_items = {
"Fighter",
"Master",
"Tempered",
176 const std::vector<std::string> shield_items = {
"Small",
"Fire",
"Mirror"};
177 const std::vector<std::string> armor_items = {
"Green",
"Blue",
"Red"};
179 auto sword_radiobox = Radiobox(&sword_items, &sword);
180 auto shield_radiobox = Radiobox(&shield_items, &shield);
181 auto armor_radiobox = Radiobox(&armor_items, &armor);
182 auto equipment_container = Container::Vertical({
188 auto save_button = Button(
"Generate Save File", [&] {
198 auto container = Container::Vertical({
205 auto renderer =
Renderer(container, [&] {
206 return vbox({text(
"Generate Save File") | center, separator(),
207 text(
"Select items to include in the save file:"),
208 checkboxes->Render(), separator(),
209 equipment_container->Render(), separator(),
211 save_button->Render() | center,
213 back_button->Render() | center,
218 screen.Loop(renderer);
222 static std::string rom_file;
223 auto rom_file_input = Input(&rom_file,
"ROM file path");
225 auto load_button = Button(
"Load ROM", [&] {
227 auto rom_status = app_context.rom.LoadFromFile(rom_file);
228 if (!rom_status.ok()) {
229 app_context.error_message = rom_status.message();
240 auto container = Container::Vertical({
246 auto renderer =
Renderer(container, [&] {
247 return vbox({text(
"Load ROM") | center, separator(),
248 text(
"Enter ROM File:"), rom_file_input->Render(), separator(),
250 load_button->Render() | center,
252 back_button->Render() | center,
257 screen.Loop(renderer);
270 static auto palette_groups = app_context.rom.palette_group();
271 static std::vector<gfx::PaletteGroup> ftx_palettes = {
272 palette_groups.swords,
273 palette_groups.shields,
274 palette_groups.armors,
275 palette_groups.overworld_main,
276 palette_groups.overworld_aux,
277 palette_groups.global_sprites,
278 palette_groups.sprites_aux1,
279 palette_groups.sprites_aux2,
280 palette_groups.sprites_aux3,
281 palette_groups.dungeon_main,
282 palette_groups.overworld_mini_map,
283 palette_groups.grass,
284 palette_groups.object_3d,
288 static int selected_palette_group = 0;
289 static std::vector<std::string> palette_group_names;
290 if (palette_group_names.empty()) {
291 for (
size_t i = 0; i < 14; ++i) {
292 palette_group_names.push_back(gfx::kPaletteCategoryNames[i].data());
296 static bool show_palette_editor =
false;
297 static std::vector<std::vector<Element>> palette_elements;
299 const auto load_palettes_from_current_group = [&]() {
300 auto palette_group = ftx_palettes[selected_palette_group];
301 palette_elements.clear();
303 for (
size_t i = 0; i < palette_group.size(); ++i) {
304 palette_elements.push_back(std::vector<Element>());
305 for (
size_t j = 0; j < palette_group[i].size(); ++j) {
306 auto color = palette_group[i][j];
307 palette_elements[i].push_back(
308 ColorBox(Color::RGB(color.rgb().x, color.rgb().y, color.rgb().z)));
313 if (show_palette_editor) {
314 if (palette_elements.empty()) {
315 load_palettes_from_current_group();
318 auto palette_grid = Container::Vertical({});
319 for (
const auto &element : palette_elements) {
320 auto row = Container::Horizontal({});
321 for (
const auto &color : element) {
322 row->Add(
Renderer([color] {
return color; }));
324 palette_grid->Add(row);
328 auto save_button = Button(
"Save Changes", [&] {
335 auto back_button = Button(
"Back", [&] {
336 show_palette_editor =
false;
337 screen.ExitLoopClosure()();
340 auto palette_editor_container = Container::Vertical({
346 auto palette_editor_renderer =
Renderer(palette_editor_container, [&] {
347 return vbox({text(gfx::kPaletteCategoryNames[selected_palette_group]
350 separator(), palette_grid->Render(), separator(),
352 save_button->Render() | center,
354 back_button->Render() | center,
358 screen.Loop(palette_editor_renderer);
360 auto palette_list = Menu(&palette_group_names, &selected_palette_group);
361 palette_list = CatchEvent(palette_list, [&](Event event) {
362 if (event == Event::Return) {
365 show_palette_editor =
true;
366 screen.ExitLoopClosure()();
367 load_palettes_from_current_group();
373 auto container = Container::Vertical({
377 auto renderer =
Renderer(container, [&] {
378 return vbox({text(
"Palette Editor") | center, separator(),
379 palette_list->Render(), separator(),
380 back_button->Render() | center}) |
383 screen.Loop(renderer);
502 static int selected = 0;
504 option.focused_entry = &selected;
507 menu, [&](Event event) {
return HandleInput(screen, event, selected); });
509 std::string rom_information =
"ROM not loaded";
510 if (app_context.rom.is_loaded()) {
511 rom_information = app_context.rom.title();
514 auto title = border(hbox({
515 text(
"z3ed") | bold | color(Color::Blue1),
517 text(
"v0.1.0") | bold | color(Color::Green1),
519 text(rom_information) | bold | color(Color::Red1),
522 auto renderer =
Renderer(menu, [&] {
527 menu->Render() | center,
532 auto main_component = CatchEvent(renderer, [&](Event event) {
533 if (event == Event::Return) {
556 if (event == Event::Character(
'q')) {
563 screen.Loop(main_component);