Guest User

Untitled

a guest
Jan 22nd, 2026
172
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Rust 14.19 KB | None | 0 0
  1. //! A tool for reading and writing Garmin .SET files.
  2. //!
  3. //! # Usage
  4. //!
  5. //! Dump the contents of a .SET file:
  6. //! ```bash
  7. //! bazel run //device:set_file_tool -- path/to/settings.SET --dump
  8. //! ```
  9. //!
  10. //! Set a key/value pair in a .SET file:
  11. //! ```bash
  12. //! bazel run //device:set_file_tool -- path/to/settings.SET set "KeyName" "Value"
  13. //! ```
  14.  
  15. use anyhow::{anyhow, Context, Result};
  16. use clap::{Parser, Subcommand};
  17. use std::collections::BTreeMap;
  18. use std::fs::File;
  19. use std::io::{Read, Write};
  20. use std::path::PathBuf;
  21.  
  22. const STRING_BLOCK_SENTINEL: u32 = 0xABCDABCD;
  23. const DATA_BLOCK_SENTINEL: u32 = 0xDA7ADA7A;
  24.  
  25. const TYPE_NULL: u8 = 0;
  26. const TYPE_INT: u8 = 1;
  27. const TYPE_FLOAT: u8 = 2;
  28. const TYPE_STRING: u8 = 3;
  29. const TYPE_ARRAY: u8 = 5;
  30. const TYPE_BOOLEAN: u8 = 9;
  31. const TYPE_HASH: u8 = 11;
  32. const TYPE_LONG: u8 = 14;
  33. const TYPE_DOUBLE: u8 = 15;
  34. // const TYPE_CHAR: u8 = 19;
  35. // const TYPE_BYTEARRAY: u8 = 20;
  36.  
  37. #[derive(Debug, Clone)]
  38. enum MonkeyType {
  39.     Null,
  40.     Bool(bool),
  41.     Int(i32),
  42.     Float(f32),
  43.     Long(i64),
  44.     Double(f64),
  45.     String(i32, Option<String>), // offset, value
  46.     Array(Vec<MonkeyType>),
  47.     Hash(Vec<(MonkeyType, MonkeyType)>), // Preserves insertion order
  48. }
  49.  
  50. impl MonkeyType {
  51.     fn serialize(&self) -> Vec<u8> {
  52.         let mut buf = Vec::new();
  53.         match self {
  54.             MonkeyType::Null => {
  55.                 buf.push(TYPE_NULL);
  56.             }
  57.             MonkeyType::Bool(val) => {
  58.                 buf.push(TYPE_BOOLEAN);
  59.                 buf.push(if *val { 1 } else { 0 });
  60.             }
  61.             MonkeyType::Int(val) => {
  62.                 buf.push(TYPE_INT);
  63.                 buf.extend_from_slice(&val.to_be_bytes());
  64.             }
  65.             MonkeyType::Float(val) => {
  66.                 buf.push(TYPE_FLOAT);
  67.                 buf.extend_from_slice(&val.to_be_bytes());
  68.             }
  69.             MonkeyType::Long(val) => {
  70.                 buf.push(TYPE_LONG);
  71.                 buf.extend_from_slice(&val.to_be_bytes());
  72.             }
  73.             MonkeyType::Double(val) => {
  74.                 buf.push(TYPE_DOUBLE);
  75.                 buf.extend_from_slice(&val.to_be_bytes());
  76.             }
  77.             MonkeyType::String(offset, _) => {
  78.                 buf.push(TYPE_STRING);
  79.                 buf.extend_from_slice(&offset.to_be_bytes());
  80.             }
  81.             MonkeyType::Array(items) => {
  82.                 buf.push(TYPE_ARRAY);
  83.                 buf.extend_from_slice(&(items.len() as i32).to_be_bytes());
  84.                 for item in items {
  85.                     buf.extend(item.serialize());
  86.                 }
  87.             }
  88.             MonkeyType::Hash(items) => {
  89.                 buf.push(TYPE_HASH);
  90.                 buf.extend_from_slice(&(items.len() as i32).to_be_bytes());
  91.                 for (k, v) in items {
  92.                     buf.extend(k.serialize());
  93.                     buf.extend(v.serialize());
  94.                 }
  95.             }
  96.         }
  97.         buf
  98.     }
  99. }
  100.  
  101. struct StringBlock {
  102.     strings: BTreeMap<i32, String>,
  103. }
  104.  
  105. impl StringBlock {
  106.     fn parse(data: &[u8]) -> Result<Self> {
  107.         let mut strings = BTreeMap::new();
  108.         let mut offset = 0;
  109.         while offset < data.len() {
  110.             if offset + 2 > data.len() {
  111.                 break;
  112.             }
  113.             let size = u16::from_be_bytes([data[offset], data[offset + 1]]) as usize;
  114.             offset += 2;
  115.  
  116.             if offset + size > data.len() + 1 {
  117.                 // size includes null, so actual data is size-1
  118.                 break;
  119.             }
  120.  
  121.             // The key is the offset relative to the start of the String Block Data.
  122.  
  123.             let str_start = offset;
  124.             let str_len = if size > 0 { size - 1 } else { 0 };
  125.  
  126.             let s_bytes = &data[str_start..str_start + str_len];
  127.             let s = String::from_utf8_lossy(s_bytes).to_string();
  128.  
  129.             strings.insert((offset - 2) as i32, s);
  130.  
  131.             offset += size;
  132.         }
  133.         Ok(Self { strings })
  134.     }
  135.  
  136.     fn get(&self, offset: i32) -> Option<&String> {
  137.         self.strings.get(&offset)
  138.     }
  139.  
  140.     fn serialize(&self) -> Vec<u8> {
  141.         let mut buf = Vec::new();
  142.         for (_, s) in &self.strings {
  143.             let s_bytes = s.as_bytes();
  144.             let size = s_bytes.len() + 1;
  145.             buf.extend_from_slice(&(size as u16).to_be_bytes());
  146.             buf.extend_from_slice(s_bytes);
  147.             buf.push(0);
  148.         }
  149.         buf
  150.     }
  151.  
  152.     fn add_string(&mut self, s: String) -> i32 {
  153.         // Check if string already exists
  154.         for (&offset, existing_s) in &self.strings {
  155.             if existing_s == &s {
  156.                 return offset;
  157.             }
  158.         }
  159.  
  160.         let next_offset = if let Some((&last_offset, last_string)) = self.strings.iter().last() {
  161.             // entry size is 2 (header) + len + 1 (null).
  162.             last_offset + 2 + last_string.len() as i32 + 1
  163.         } else {
  164.             0
  165.         };
  166.         self.strings.insert(next_offset, s);
  167.         next_offset
  168.     }
  169. }
  170.  
  171. struct SetFile {
  172.     string_block: Option<StringBlock>,
  173.     data: Vec<MonkeyType>,
  174. }
  175.  
  176. impl SetFile {
  177.     fn add_string(&mut self, s: String) -> i32 {
  178.         if self.string_block.is_none() {
  179.             self.string_block = Some(StringBlock {
  180.                 strings: BTreeMap::new(),
  181.             });
  182.         }
  183.         self.string_block.as_mut().unwrap().add_string(s)
  184.     }
  185.  
  186.     fn parse(data: &[u8]) -> Result<Self> {
  187.         let mut offset = 0;
  188.         let mut string_block = None;
  189.  
  190.         // Optional String Block
  191.         if data.len() >= 8 {
  192.             let sentinel = u32::from_be_bytes(data[offset..offset + 4].try_into()?);
  193.             if sentinel == STRING_BLOCK_SENTINEL {
  194.                 offset += 4;
  195.                 let size = u32::from_be_bytes(data[offset..offset + 4].try_into()?) as usize;
  196.                 offset += 4;
  197.  
  198.                 let sb_data = &data[offset..offset + size];
  199.                 string_block = Some(StringBlock::parse(sb_data)?);
  200.                 offset += size;
  201.             }
  202.         }
  203.  
  204.         // Data Block
  205.         if data.len() < offset + 8 {
  206.             return Err(anyhow!("File too short for data block"));
  207.         }
  208.         let sentinel = u32::from_be_bytes(data[offset..offset + 4].try_into()?);
  209.         if sentinel != DATA_BLOCK_SENTINEL {
  210.             return Err(anyhow!(
  211.                 "Expected data block sentinel {:#X}, got {:#X}",
  212.                 DATA_BLOCK_SENTINEL,
  213.                 sentinel
  214.             ));
  215.         }
  216.         offset += 4;
  217.         let size = u32::from_be_bytes(data[offset..offset + 4].try_into()?) as usize;
  218.         offset += 4;
  219.  
  220.         let end = offset + size;
  221.         let mut data_objs = Vec::new();
  222.         while offset < end {
  223.             let (obj, bytes_read) = Self::parse_object(&data[offset..end], string_block.as_ref())?;
  224.             data_objs.push(obj);
  225.             offset += bytes_read;
  226.         }
  227.  
  228.         Ok(Self {
  229.             string_block,
  230.             data: data_objs,
  231.         })
  232.     }
  233.  
  234.     fn parse_object(
  235.         data: &[u8],
  236.         string_block: Option<&StringBlock>,
  237.     ) -> Result<(MonkeyType, usize)> {
  238.         let type_id = data[0];
  239.         match type_id {
  240.             TYPE_NULL => Ok((MonkeyType::Null, 1)),
  241.             TYPE_BOOLEAN => {
  242.                 let val = data[1] != 0;
  243.                 Ok((MonkeyType::Bool(val), 2))
  244.             }
  245.             TYPE_INT => {
  246.                 let val = i32::from_be_bytes(data[1..5].try_into()?);
  247.                 Ok((MonkeyType::Int(val), 5))
  248.             }
  249.             TYPE_LONG => {
  250.                 let val = i64::from_be_bytes(data[1..9].try_into()?);
  251.                 Ok((MonkeyType::Long(val), 9))
  252.             }
  253.             TYPE_FLOAT => {
  254.                 let val = f32::from_be_bytes(data[1..5].try_into()?);
  255.                 Ok((MonkeyType::Float(val), 5))
  256.             }
  257.             TYPE_DOUBLE => {
  258.                 let val = f64::from_be_bytes(data[1..9].try_into()?);
  259.                 Ok((MonkeyType::Double(val), 9))
  260.             }
  261.             TYPE_STRING => {
  262.                 let offset = i32::from_be_bytes(data[1..5].try_into()?);
  263.                 let val = string_block.and_then(|sb| sb.get(offset).cloned());
  264.                 Ok((MonkeyType::String(offset, val), 5))
  265.             }
  266.             TYPE_HASH => {
  267.                 let count = i32::from_be_bytes(data[1..5].try_into()?);
  268.                 let mut offset = 5;
  269.                 let mut items = Vec::new();
  270.                 for _ in 0..count {
  271.                     let (k, k_size) = Self::parse_object(&data[offset..], string_block)?;
  272.                     offset += k_size;
  273.                     let (v, v_size) = Self::parse_object(&data[offset..], string_block)?;
  274.                     offset += v_size;
  275.                     items.push((k, v));
  276.                 }
  277.                 Ok((MonkeyType::Hash(items), offset))
  278.             }
  279.             TYPE_ARRAY => {
  280.                 let count = i32::from_be_bytes(data[1..5].try_into()?);
  281.                 let mut offset = 5;
  282.                 let mut items = Vec::new();
  283.                 for _ in 0..count {
  284.                     let (item, item_size) = Self::parse_object(&data[offset..], string_block)?;
  285.                     offset += item_size;
  286.                     items.push(item);
  287.                 }
  288.                 Ok((MonkeyType::Array(items), offset))
  289.             }
  290.             _ => Err(anyhow!("Unknown type ID: {}", type_id)),
  291.         }
  292.     }
  293.  
  294.     fn serialize(&self) -> Vec<u8> {
  295.         let mut result = Vec::new();
  296.  
  297.         if let Some(sb) = &self.string_block {
  298.             let sb_data = sb.serialize();
  299.             if !sb_data.is_empty() {
  300.                 result.extend_from_slice(&STRING_BLOCK_SENTINEL.to_be_bytes());
  301.                 result.extend_from_slice(&(sb_data.len() as u32).to_be_bytes());
  302.                 result.extend(sb_data);
  303.             }
  304.         }
  305.  
  306.         let mut data_bytes = Vec::new();
  307.         for obj in &self.data {
  308.             data_bytes.extend(obj.serialize());
  309.         }
  310.  
  311.         result.extend_from_slice(&DATA_BLOCK_SENTINEL.to_be_bytes());
  312.         result.extend_from_slice(&(data_bytes.len() as u32).to_be_bytes());
  313.         result.extend(data_bytes);
  314.  
  315.         result
  316.     }
  317.  
  318.     fn print_structure(&self) {
  319.         println!("=== String Block ===");
  320.         if let Some(sb) = &self.string_block {
  321.             for (offset, s) in &sb.strings {
  322.                 println!("  {:4}: \"{}\"", offset, s);
  323.             }
  324.         } else {
  325.             println!("  (none)");
  326.         }
  327.  
  328.         println!("\n=== Data Block ===");
  329.         for (i, obj) in self.data.iter().enumerate() {
  330.             println!("  [{}] {:?}", i, obj);
  331.         }
  332.     }
  333.  
  334.     fn garbage_collect(&mut self) {
  335.         let mut new_sb = StringBlock {
  336.             strings: BTreeMap::new(),
  337.         };
  338.         for obj in &mut self.data {
  339.             Self::gc_visit(obj, &mut new_sb);
  340.         }
  341.         self.string_block = Some(new_sb);
  342.     }
  343.  
  344.     fn gc_visit(obj: &mut MonkeyType, sb: &mut StringBlock) {
  345.         match obj {
  346.             MonkeyType::String(offset, Some(s)) => {
  347.                 *offset = sb.add_string(s.clone());
  348.             }
  349.             MonkeyType::Array(items) => {
  350.                 for item in items {
  351.                     Self::gc_visit(item, sb);
  352.                 }
  353.             }
  354.             MonkeyType::Hash(items) => {
  355.                 for (k, v) in items {
  356.                     Self::gc_visit(k, sb);
  357.                     Self::gc_visit(v, sb);
  358.                 }
  359.             }
  360.             _ => {}
  361.         }
  362.     }
  363. }
  364.  
  365. #[derive(Parser)]
  366. #[command(author, version, about, long_about = None)]
  367. struct Cli {
  368.     #[arg(value_name = "FILE")]
  369.     filename: PathBuf,
  370.  
  371.     #[command(subcommand)]
  372.     command: Option<Commands>,
  373. }
  374.  
  375. #[derive(Subcommand)]
  376. enum Commands {
  377.     Dump,
  378.     Set { key: String, value: String },
  379. }
  380.  
  381. fn main() -> Result<()> {
  382.     let cli = Cli::parse();
  383.  
  384.     let mut file = File::open(&cli.filename).context("Failed to open file")?;
  385.     let mut data = Vec::new();
  386.     file.read_to_end(&mut data).context("Failed to read file")?;
  387.  
  388.     let mut parser = SetFile::parse(&data)?;
  389.  
  390.     match &cli.command {
  391.         Some(Commands::Dump) | None => {
  392.             parser.print_structure();
  393.         }
  394.  
  395.         Some(Commands::Set { key, value }) => {
  396.             if parser.data.is_empty() {
  397.                 return Err(anyhow!("Data is empty, cannot set value"));
  398.             }
  399.  
  400.             // Find the key in the first Hash object
  401.             let target_index = if let MonkeyType::Hash(items) = &parser.data[0] {
  402.                 items.iter().position(|(k, _)| {
  403.                     if let MonkeyType::String(_, Some(s)) = k {
  404.                         s == key
  405.                     } else {
  406.                         false
  407.                     }
  408.                 })
  409.             } else {
  410.                 return Err(anyhow!("Expected first data element to be a hash"));
  411.             };
  412.  
  413.             if let Some(idx) = target_index {
  414.                 let new_val = if value.eq_ignore_ascii_case("true") {
  415.                     MonkeyType::Bool(true)
  416.                 } else if value.eq_ignore_ascii_case("false") {
  417.                     MonkeyType::Bool(false)
  418.                 } else if let Ok(n) = value.parse::<i32>() {
  419.                     MonkeyType::Int(n)
  420.                 } else if let Ok(f) = value.parse::<f32>() {
  421.                     MonkeyType::Float(f)
  422.                 } else {
  423.                     let offset = parser.add_string(value.clone());
  424.                     MonkeyType::String(offset, Some(value.clone()))
  425.                 };
  426.  
  427.                 if let MonkeyType::Hash(items) = &mut parser.data[0] {
  428.                     items[idx].1 = new_val;
  429.                 }
  430.  
  431.                 println!("Updated {} = {}", key, value);
  432.  
  433.                 parser.garbage_collect();
  434.                 let output_data = parser.serialize();
  435.                 let mut outfile =
  436.                     File::create(&cli.filename).context("Failed to create output file")?;
  437.                 outfile
  438.                     .write_all(&output_data)
  439.                     .context("Failed to write output file")?;
  440.             } else {
  441.                 return Err(anyhow!("Key '{}' not found in settings", key));
  442.             }
  443.         }
  444.     }
  445.  
  446.     Ok(())
  447. }
Advertisement
Add Comment
Please, Sign In to add comment