From 5dd08175b0c2d4cf77ef552c872ae47c12c44919 Mon Sep 17 00:00:00 2001 From: nn <53490794+nn357@users.noreply.github.com> Date: Fri, 8 May 2026 03:21:54 +0900 Subject: [PATCH 1/3] add maprando options to rom for autotracker use. single byte ordinal (no bitflags) [u8]0xdfff09 (Skill setting) 0x00 = custom 0x01 = basic 0x02 = medium 0x03 = hard 0x04 = very hard 0x05 = expert 0x06 = expert+ 0x07 = extreme 0x08 = extreme+ 0x09 = insane 0x10 = insane+ [u8] 0xdfff0a (prog setting) 0x00 = custom 0x01 = normal 0x02 = tricky 0x03 = technical 0x04 = challenge 0x05 = desolate [u8] 0xdfff0b (qol setting) 0x00 = custom 0x01 = off 0x02 = low 0x03 = default 0x04 = high 0x05 = max [u8] 0xdfff0c (objectives setting) 0x00 = custom 0x01 = none 0x02 = bosses 0x03 = minibosses 0x04 = chozos 0x05 = pirates 0x06 = metroids 0x07 = random [u8] 0xdfff0d (map layout) 0x01 = vanilla 0x02 = small 0x03 = standard 0x04 = wild --- patches/rom_map/Bank DF.txt | 3 + rust/maprando/src/patch.rs | 80 +++++++++++++++++- rust/maprando/src/settings.rs | 147 ++++++++++++++++++++++++++++++++++ 3 files changed, 228 insertions(+), 2 deletions(-) diff --git a/patches/rom_map/Bank DF.txt b/patches/rom_map/Bank DF.txt index 8c2fb2474..9dd4efbc0 100644 --- a/patches/rom_map/Bank DF.txt +++ b/patches/rom_map/Bank DF.txt @@ -6,4 +6,7 @@ FEF0 - FEFF: seed name (null-terminated ASCII string) FF04 - FF04: [FREE] FF05 - FF06: randomizer settings: - bitmask $0001: walljump-boots item enabled + - bitmask $0002: split speedbooster item enabled FF07 - FF08: number of frames of artificial lag to add to the unpause black screen +FF09 - FF0D: randomizer settings: skill/progression/qol/objectives/maplayout (autotracker use) + - stored as single byte ordinal numbers, if a setting has a custom option then byte is set to 0 diff --git a/rust/maprando/src/patch.rs b/rust/maprando/src/patch.rs index e42f257ee..63d35b36f 100644 --- a/rust/maprando/src/patch.rs +++ b/rust/maprando/src/patch.rs @@ -19,8 +19,9 @@ use crate::{ randomize::{LockedDoor, Randomization, get_starting_items}, settings::{ AreaAssignmentPreset, CrashFixes, CrashFixesPreset, DisableETankSetting, ETankRefill, - EnemyDrops, Fanfares, FixMode, ItemCount, MotherBrainFight, Objective, ObjectiveScreen, - RandomizerSettings, SaveAnimals, SpeedBooster, StartLocationMode, WallJump, + EnemyDrops, Fanfares, FixMode, ItemCount, MapPreset, MotherBrainFight, ObjPreset, + Objective, ObjectiveScreen, ProgressionPreset, QolPreset, RandomizerSettings, SaveAnimals, + SkillPreset, SpeedBooster, StartLocationMode, WallJump, }, }; use anyhow::{Context, Result, bail, ensure}; @@ -747,6 +748,7 @@ impl Patcher<'_> { // For now this is just to indicate if walljump-boots exists as an item, // and if Speed Booster is split into Blue Booster and Spark Booster. let mut settings_flag = 0x0000; + if self.settings.other_settings.wall_jump == WallJump::Collectible { settings_flag |= 0x0001; } @@ -1987,6 +1989,75 @@ impl Patcher<'_> { Ok(()) } + fn write_skill_mask(&mut self) -> Result<()> { + let settings = &self.settings.skill_assumption_settings; + + let value: u8 = settings + .preset + .as_deref() + .and_then(SkillPreset::from_preset) + .map(SkillPreset::to_byte) + .unwrap_or(0); + + self.rom.write_u8(snes2pc(0xdfff09), value as isize)?; + + Ok(()) + } + + fn write_progression_mask(&mut self) -> Result<()> { + let settings = &self.settings.item_progression_settings; + + let value: u8 = settings + .preset + .as_deref() + .and_then(ProgressionPreset::from_preset) + .map(ProgressionPreset::to_byte) + .unwrap_or(0); + + self.rom.write_u8(snes2pc(0xdfff0a), value as isize)?; + + Ok(()) + } + + fn write_qol_mask(&mut self) -> Result<()> { + let settings = &self.settings.quality_of_life_settings; + + let value: u8 = settings + .preset + .as_deref() + .and_then(QolPreset::from_preset) + .map(QolPreset::to_byte) + .unwrap_or(0); + + self.rom.write_u8(snes2pc(0xdfff0b), value as isize)?; + + Ok(()) + } + + fn write_obj_mask(&mut self) -> Result<()> { + let settings = &self.settings.objective_settings; + + let value: u8 = settings + .preset + .as_deref() + .and_then(ObjPreset::from_preset) + .map(ObjPreset::to_byte) + .unwrap_or(0); + + self.rom.write_u8(snes2pc(0xdfff0c), value as isize)?; + + Ok(()) + } + + fn write_maplayout_mask(&mut self) -> Result<()> { + let value: u8 = MapPreset::from_preset(&self.settings.map_layout) + .map(MapPreset::to_byte) + .unwrap_or(0); + self.rom.write_u8(snes2pc(0xdfff0d), value as isize)?; + + Ok(()) + } + fn apply_seed_identifiers(&mut self) -> Result<()> { let cartridge_name = "SUPERMETROID MAPRANDO"; self.rom.write_n(0x7FC0, cartridge_name.as_bytes())?; @@ -3642,6 +3713,11 @@ pub fn make_rom( patcher.apply_mother_brain_fight_patches()?; patcher.write_custom_item_graphics()?; patcher.write_objective_data()?; + patcher.write_skill_mask()?; + patcher.write_progression_mask()?; + patcher.write_maplayout_mask()?; + patcher.write_qol_mask()?; + patcher.write_obj_mask()?; patcher.apply_seed_identifiers()?; patcher.apply_credits()?; if randomizer_settings.quality_of_life_settings.hazard_markers { diff --git a/rust/maprando/src/settings.rs b/rust/maprando/src/settings.rs index e3e2cb292..4ad550248 100644 --- a/rust/maprando/src/settings.rs +++ b/rust/maprando/src/settings.rs @@ -103,6 +103,153 @@ pub struct ItemProgressionSettings { pub filler_items: Vec, } +#[repr(u8)] +#[derive(Serialize, Deserialize, Clone, PartialEq)] +pub enum SkillPreset { + Basic = 1, + Medium = 2, + Hard = 3, + VeryHard = 4, + Expert = 5, + ExpertPlus = 6, + Extreme = 7, + ExtremePlus = 8, + Insane = 9, + InsanePlus = 10, +} + +impl SkillPreset { + pub fn from_preset(s: &str) -> Option { + match s.trim().to_lowercase().as_str() { + "basic" => Some(Self::Basic), + "medium" => Some(Self::Medium), + "hard" => Some(Self::Hard), + "very hard" => Some(Self::VeryHard), + "expert" => Some(Self::Expert), + "expert+" => Some(Self::ExpertPlus), + "extreme" => Some(Self::Extreme), + "extreme+" => Some(Self::ExtremePlus), + "insane" => Some(Self::Insane), + "insane+" => Some(Self::InsanePlus), + _ => None, + } + } + + pub fn to_byte(self) -> u8 { + self as u8 + } +} + +#[repr(u8)] +#[derive(Serialize, Deserialize, Clone, PartialEq)] +pub enum QolPreset { + Off = 1, + Low = 2, + Default = 3, + High = 4, + Max = 5, +} + +impl QolPreset { + pub fn from_preset(s: &str) -> Option { + match s.trim().to_lowercase().as_str() { + "off" => Some(Self::Off), + "low" => Some(Self::Low), + "default" => Some(Self::Default), + "high" => Some(Self::High), + "max" => Some(Self::Max), + _ => None, + } + } + + pub fn to_byte(self) -> u8 { + self as u8 + } +} + +#[repr(u8)] +#[derive(Serialize, Deserialize, Clone, PartialEq)] +pub enum ObjPreset { + None = 1, + Bosses = 2, + MiniBosses = 3, + Chozos = 4, + Pirates = 5, + Metroids = 6, + Random = 7, +} + +impl ObjPreset { + pub fn from_preset(s: &str) -> Option { + match s.trim().to_lowercase().as_str() { + "none" => Some(Self::None), + "bosses" => Some(Self::Bosses), + "minibosses" => Some(Self::MiniBosses), + "chozos" => Some(Self::Chozos), + "pirates" => Some(Self::Pirates), + "metroids" => Some(Self::Metroids), + "random" => Some(Self::Random), + _ => None, + } + } + + pub fn to_byte(self) -> u8 { + self as u8 + } +} + +#[repr(u8)] +#[derive(Serialize, Deserialize, Clone, PartialEq)] +pub enum ProgressionPreset { + Normal = 1, + Tricky = 2, + Technical = 3, + Challenge = 4, + Desolate = 5, +} + +impl ProgressionPreset { + pub fn from_preset(s: &str) -> Option { + match s.trim().to_lowercase().as_str() { + "normal" => Some(Self::Normal), + "tricky" => Some(Self::Tricky), + "technical" => Some(Self::Technical), + "challenge" => Some(Self::Challenge), + "desolate" => Some(Self::Desolate), + _ => None, + } + } + + pub fn to_byte(self) -> u8 { + self as u8 + } +} + +#[repr(u8)] +#[derive(Serialize, Deserialize, Clone, PartialEq)] +pub enum MapPreset { + Vanilla = 1, + Small = 2, + Standard = 3, + Wild = 4, +} + +impl MapPreset { + pub fn from_preset(s: &str) -> Option { + match s.trim().to_lowercase().as_str() { + "vanilla" => Some(Self::Vanilla), + "small" => Some(Self::Small), + "standard" => Some(Self::Standard), + "wild" => Some(Self::Wild), + _ => None, + } + } + + pub fn to_byte(self) -> u8 { + self as u8 + } +} + #[derive(Serialize, Deserialize, Clone, PartialEq)] pub struct ItemCount { pub item: Item, From 224cc3f298086d670e69cebd1084346598a2822a Mon Sep 17 00:00:00 2001 From: nn <53490794+nn357@users.noreply.github.com> Date: Fri, 8 May 2026 03:31:45 +0900 Subject: [PATCH 2/3] clearer naming --- rust/maprando/src/patch.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/rust/maprando/src/patch.rs b/rust/maprando/src/patch.rs index 63d35b36f..b15e7c058 100644 --- a/rust/maprando/src/patch.rs +++ b/rust/maprando/src/patch.rs @@ -2034,7 +2034,7 @@ impl Patcher<'_> { Ok(()) } - fn write_obj_mask(&mut self) -> Result<()> { + fn write_objectives_mask(&mut self) -> Result<()> { let settings = &self.settings.objective_settings; let value: u8 = settings @@ -2049,7 +2049,7 @@ impl Patcher<'_> { Ok(()) } - fn write_maplayout_mask(&mut self) -> Result<()> { + fn write_map_layout_mask(&mut self) -> Result<()> { let value: u8 = MapPreset::from_preset(&self.settings.map_layout) .map(MapPreset::to_byte) .unwrap_or(0); @@ -3715,9 +3715,9 @@ pub fn make_rom( patcher.write_objective_data()?; patcher.write_skill_mask()?; patcher.write_progression_mask()?; - patcher.write_maplayout_mask()?; patcher.write_qol_mask()?; - patcher.write_obj_mask()?; + patcher.write_objectives_mask()?; + patcher.write_map_layout_mask()?; patcher.apply_seed_identifiers()?; patcher.apply_credits()?; if randomizer_settings.quality_of_life_settings.hazard_markers { From 8d5c52fbcc38bfffb90a972c7ff7fb0af8016f0a Mon Sep 17 00:00:00 2001 From: nn <53490794+nn357@users.noreply.github.com> Date: Fri, 8 May 2026 14:08:59 +0900 Subject: [PATCH 3/3] rename functions for readabiltiy --- rust/maprando/src/patch.rs | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/rust/maprando/src/patch.rs b/rust/maprando/src/patch.rs index b15e7c058..7313defed 100644 --- a/rust/maprando/src/patch.rs +++ b/rust/maprando/src/patch.rs @@ -1989,7 +1989,7 @@ impl Patcher<'_> { Ok(()) } - fn write_skill_mask(&mut self) -> Result<()> { + fn write_autotracker_skill_setting(&mut self) -> Result<()> { let settings = &self.settings.skill_assumption_settings; let value: u8 = settings @@ -2004,7 +2004,7 @@ impl Patcher<'_> { Ok(()) } - fn write_progression_mask(&mut self) -> Result<()> { + fn write_autotracker_item_progression(&mut self) -> Result<()> { let settings = &self.settings.item_progression_settings; let value: u8 = settings @@ -2019,7 +2019,7 @@ impl Patcher<'_> { Ok(()) } - fn write_qol_mask(&mut self) -> Result<()> { + fn write_autotracker_qol_setting(&mut self) -> Result<()> { let settings = &self.settings.quality_of_life_settings; let value: u8 = settings @@ -2034,7 +2034,7 @@ impl Patcher<'_> { Ok(()) } - fn write_objectives_mask(&mut self) -> Result<()> { + fn write_autotracker_objective_setting(&mut self) -> Result<()> { let settings = &self.settings.objective_settings; let value: u8 = settings @@ -2049,7 +2049,7 @@ impl Patcher<'_> { Ok(()) } - fn write_map_layout_mask(&mut self) -> Result<()> { + fn write_autotracker_map_layout(&mut self) -> Result<()> { let value: u8 = MapPreset::from_preset(&self.settings.map_layout) .map(MapPreset::to_byte) .unwrap_or(0); @@ -3713,11 +3713,11 @@ pub fn make_rom( patcher.apply_mother_brain_fight_patches()?; patcher.write_custom_item_graphics()?; patcher.write_objective_data()?; - patcher.write_skill_mask()?; - patcher.write_progression_mask()?; - patcher.write_qol_mask()?; - patcher.write_objectives_mask()?; - patcher.write_map_layout_mask()?; + patcher.write_autotracker_skill_setting()?; + patcher.write_autotracker_item_progression()?; + patcher.write_autotracker_qol_setting()?; + patcher.write_autotracker_objective_setting()?; + patcher.write_autotracker_map_layout()?; patcher.apply_seed_identifiers()?; patcher.apply_credits()?; if randomizer_settings.quality_of_life_settings.hazard_markers {