int sfc_palette(int argc, char* argv[]) { SfcPalette::Settings settings = {}; bool verbose = false; bool col0_forced = false; rgba_t col0 = 0; try { bool help = false; std::string mode_str; bool dummy = false; Options options; options.IndentDescription = sfc::Constants::options_indent; options.Header = "Usage: superfamiconv palette [<options>]\n"; // clang-format off options.Add(settings.in_image, 'i', "in-image", "Input: image"); options.Add(settings.out_data, 'd', "out-data", "Output: native data"); options.Add(settings.out_act, 'a', "out-act", "Output: photoshop palette"); options.Add(settings.out_json, 'j', "out-json", "Output: json"); options.Add(settings.out_image, 'o', "out-image", "Output: image"); options.Add(mode_str, 'M', "mode", "Mode", std::string("snes"), "Settings"); options.Add(settings.palettes, 'P', "palettes", "Number of subpalettes", unsigned(8), "Settings"); options.Add(settings.colors, 'C', "colors", "Colors per subpalette", unsigned(16), "Settings"); options.Add(settings.tile_w, 'W', "tile-width", "Tile width", unsigned(8), "Settings"); options.Add(settings.tile_h, 'H', "tile-height", "Tile height", unsigned(8), "Settings"); options.AddSwitch(settings.no_remap, 'R', "no-remap", "Don't remap colors", false, "Settings"); options.Add(settings.color_zero, '0', "color-zero", "Set color #0", std::string(), "Settings"); options.AddSwitch(verbose, 'v', "verbose", "Verbose logging", false, "_"); options.AddSwitch(help, 'h', "help", "Show this help", false, "_"); options.AddSwitch(dummy, '9', std::string(), std::string(), false); // clang-format on if (!options.Parse(argc, argv)) return 1; if (argc <= 2 || help) { fmt::print(options.Usage()); return 0; } settings.mode = sfc::mode(mode_str); // Mode-specific defaults if (!options.WasSet("palettes")) settings.palettes = sfc::default_palette_count_for_mode(settings.mode); if (!options.WasSet("colors")) settings.colors = sfc::palette_size_at_bpp(sfc::default_bpp_for_mode(settings.mode)); if (!settings.color_zero.empty()) { col0 = sfc::from_hexstring(settings.color_zero); col0_forced = true; } } catch (const std::exception& e) { fmt::print(stderr, "Error: {}\n", e.what()); return 1; } try { if (settings.in_image.empty()) throw std::runtime_error("Input image required"); sfc::Image image(settings.in_image); if (verbose) fmt::print("Loaded image from \"{}\" ({})\n", settings.in_image, image.description()); sfc::Palette palette; if (settings.no_remap) { if (image.palette_size() == 0) throw std::runtime_error("no-remap requires indexed color image"); if (verbose) fmt::print("Mapping palette straight from indexed color image\n"); palette = sfc::Palette(settings.mode, 1, (unsigned)image.palette_size()); palette.add_colors(image.palette()); } else { if (verbose) fmt::print("Mapping optimized palette ({}x{} entries)\n", settings.palettes, settings.colors); palette = sfc::Palette(settings.mode, settings.palettes, settings.colors); col0 = col0_forced ? col0 : image.crop(0, 0, 1, 1).rgba_data()[0]; if (col0_forced || sfc::col0_is_shared_for_mode(settings.mode)) { if (verbose) fmt::print("Setting color zero to {}\n", sfc::to_hexstring(col0, true, true)); palette.set_col0(col0); } palette.add_images(image.crops(settings.tile_w, settings.tile_h)); } if (verbose) fmt::print("Created palette with {}\n", palette.description()); if (!settings.no_remap) { palette.sort(); } if (!settings.out_data.empty()) { palette.save(settings.out_data); if (verbose) fmt::print("Saved native palette data to \"{}\"\n", settings.out_data); } if (!settings.out_act.empty()) { palette.save_act(settings.out_act); if (verbose) fmt::print("Saved photoshop palette to \"{}\"\n", settings.out_act); } if (!settings.out_image.empty()) { sfc::Image palette_image(palette); palette_image.save(settings.out_image); if (verbose) fmt::print("Saved palette image to \"{}\"\n", settings.out_image); } if (!settings.out_json.empty()) { sfc::write_file(settings.out_json, palette.to_json()); if (verbose) fmt::print("Saved json data to \"{}\"\n", settings.out_json); } } catch (const std::exception& e) { fmt::print(stderr, "Error: {}\n", e.what()); return 1; } return 0; }
int sfc_map(int argc, char* argv[]) { SfcMap::Settings settings = {}; bool verbose = false; try { bool help = false; bool dummy = false; std::string mode_str; Options options; options.IndentDescription = sfc::Constants::options_indent; options.Header = "Usage: superfamiconv map [<options>]\n"; // clang-format off options.Add(settings.in_image, 'i', "in-image", "Input: image"); options.Add(settings.in_palette, 'p', "in-palette", "Input: palette (json/native)"); options.Add(settings.in_tileset, 't', "in-tiles", "Input: tiles (native)"); options.Add(settings.out_data, 'd', "out-data", "Output: native data"); options.Add(settings.out_json, 'j', "out-json", "Output: json"); options.Add(settings.out_m7_data, '7', "out-m7-data", "Output: interleaved map/tile data (snes_mode7)"); options.Add(settings.out_gbc_bank, '\0', "out-gbc-bank", "Output: banked map data (gbc)"); options.Add(mode_str, 'M', "mode", "Mode", std::string("snes"), "Settings"); options.Add(settings.bpp, 'B', "bpp", "Bits per pixel", unsigned(4), "Settings"); options.Add(settings.tile_w, 'W', "tile-width", "Tile width", unsigned(8), "Settings"); options.Add(settings.tile_h, 'H', "tile-height", "Tile height", unsigned(8), "Settings"); options.AddSwitch(settings.no_flip, 'F', "no-flip", "Don't use flipped tiles", false, "Settings"); options.Add(settings.map_w, '\0', "map-width", "Map width (in tiles)", unsigned(0), "Settings"); options.Add(settings.map_h, '\0', "map-height", "Map height (in tiles)", unsigned(0), "Settings"); options.Add(settings.map_split_w, '\0', "split-width", "Split output into columns of <tiles> width", unsigned(0), "Settings"); options.Add(settings.map_split_h, '\0', "split-height", "Split output into rows of <tiles> height", unsigned(0), "Settings"); options.AddSwitch(settings.column_order, '\0', "column-order", "Output data in column-major order", false, "Settings"); options.AddSwitch(verbose, 'v', "verbose", "Verbose logging", false, "_"); options.AddSwitch(help, 'h', "help", "Show this help", false, "_"); options.AddSwitch(dummy, '9', std::string(), std::string(), false); // clang-format on if (!options.Parse(argc, argv)) return 1; if (argc <= 2 || help) { fmt::print(options.Usage()); return 0; } settings.mode = sfc::mode(mode_str); // Mode-specific defaults if (!options.WasSet("bpp")) settings.bpp = sfc::default_bpp_for_mode(settings.mode); if (!sfc::bpp_allowed_for_mode(settings.bpp, settings.mode)) throw std::runtime_error("bpp setting not compatible with specified mode"); } catch (const std::exception& e) { fmt::print(stderr, "Error: {}\n", e.what()); return 1; } try { if (settings.in_image.empty()) throw std::runtime_error("input image required"); if (settings.in_palette.empty()) throw std::runtime_error("input palette required"); if (settings.in_tileset.empty()) throw std::runtime_error("input tileset required"); if (settings.map_split_w == 0) settings.map_split_w = sfc::default_map_size_for_mode(settings.mode); if (settings.map_split_h == 0) settings.map_split_h = sfc::default_map_size_for_mode(settings.mode); sfc::Image image(settings.in_image); if (verbose) fmt::print("Loaded image from \"{}\" ({})\n", settings.in_image, image.description()); if (settings.map_w == 0) settings.map_w = sfc::div_ceil(image.width(), settings.tile_w); if (settings.map_h == 0) settings.map_h = sfc::div_ceil(image.height(), settings.tile_h); if (settings.map_w * settings.tile_w != image.width() || settings.map_h * settings.tile_h != image.height()) { image = image.crop(0, 0, settings.map_w * settings.tile_w, settings.map_h * settings.tile_h); } sfc::Palette palette(settings.in_palette, settings.mode, sfc::palette_size_at_bpp(settings.bpp)); if (verbose) fmt::print("Loaded palette from \"{}\" ({})\n", settings.in_palette, palette.description()); sfc::Tileset tileset(sfc::read_binary(settings.in_tileset), settings.mode, settings.bpp, settings.tile_w, settings.tile_h, settings.no_flip); if (verbose) fmt::print("Loaded tiles from \"{}\" ({} entries)\n", settings.in_tileset, tileset.size()); std::vector<sfc::Image> crops = image.crops(settings.tile_w, settings.tile_h); if (verbose) fmt::print("Mapping {} {}x{}px tiles from image\n", crops.size(), settings.tile_w, settings.tile_h); sfc::Map map(settings.mode, settings.map_w, settings.map_h); for (unsigned i = 0; i < crops.size(); ++i) { map.add(crops[i], tileset, palette, settings.bpp, i % settings.map_w, i / settings.map_w); } if (verbose && settings.column_order) fmt::print("Using column-major order for output\n"); if (!settings.out_data.empty()) { map.save(settings.out_data, settings.column_order, settings.map_split_w, settings.map_split_h); if (verbose) fmt::print("Saved native map data to \"{}\"\n", settings.out_data); } if (!settings.out_json.empty()) { sfc::write_file(settings.out_json, map.to_json(settings.column_order, settings.map_split_w, settings.map_split_h)); if (verbose) fmt::print("Saved json map data to \"{}\"\n", settings.out_json); } if (settings.mode == sfc::Mode::snes_mode7 && !settings.out_m7_data.empty()) { sfc::write_file(settings.out_m7_data, map.snes_mode7_interleaved_data(tileset)); if (verbose) fmt::print("Saved interleaved SNES mode 7 data to \"{}\"\n", settings.out_m7_data); } if (settings.mode == sfc::Mode::gbc && !settings.out_gbc_bank.empty()) { sfc::write_file(settings.out_gbc_bank, map.gbc_banked_data()); if (verbose) fmt::print("Saved banked GBC map data to \"{}\"\n", settings.out_gbc_bank); } } catch (const std::exception& e) { fmt::print(stderr, "Error: {}\n", e.what()); return 1; } return 0; }