diff --git a/README.md b/README.md new file mode 100644 index 0000000..d87fc76 --- /dev/null +++ b/README.md @@ -0,0 +1,328 @@ +# MSBTools + +[![Version 0.1](https://img.shields.io/badge/version-v0.1-red)](todo!) + +A crate that allows manipulating Nintendo's proprietary MSBT format, with a library crate to parse those files. + +# Usage + +MSBTools has 4 different modes of operation: + +## Extract: + +This command converts an MSBT to TOML. + +Usage: +`msbtool extract file.msbt` + +This creates a "file.toml" file next to the executable, containing all the strings in the MSBT file ordered by internal index. + +## Create: + +This command converts a TOML to an MSBT file. + +Usage: +`msbtool create file.toml` + +This creates a "file.msbt" file next to the executable, containing all the strings in the TOML file. + +## Diff: + +This command creates a diff file between an MSBT file and multiple others. + +Usage: +`msbtool diff original.msbt edited1.msbt edited2.msbt ...` + +This creates a "original.msbd.txt" file next to the executable, containing the differences between the original file and all the edited files. + +## Patch: + +This command patches an MSBT file using a file formatted in the msbd format. + +Usage: +`msbtool patch original.msbt diff.msbd.txt` + +This creates a new MSBT file, named after the patch name found in the msbd file, with all the modifications specified by said msbd file. + +# The MSBD format + +MSBTool comes with its own diff format, made specifically for MSBT files. It is specified as: +``` +[File Name] +[Patch name] +[SHA256 of the original file] (optional) + +[+/-/~][label] +>[text] +>... + +[+/-/~][label] +>[text] +>... + +... +``` + +Each diff block being specified as: +- A state. Can be '+' for a new string, '-' for a deleted string, or '~' for an edited string. +- A label. Specifies the label of the string to add, delete or edit. +- A string. Specifies what the new string is. Deleted strings do not have this block. + +# Control codes + +Control codes have been escaped, for ease of use. For now, only one syntax is available, `[RawCmd Group.Type Argument1_Argument2_etc...]`. For instance, changing the text's colour would be `[RawCmd 0.3 RR_GG_BB_AA]`. + +To close a control code, simply type `[/RawCmd Group.Type]`. + +# Escape codes + +For ease of use, certain characters have been escaped. To use them, type `[!Escape_code]`. For the 3DS's A button, for instance, you'd type `[!A_button_3DS]`. + +The characters are as follow: + +| Name| Code| Corresponds to| +|--------------|---------|--------------------------------------------------| +|A_button_3DS|0xE000|| +|B_button_3DS|0xE001|| +|X_button_3DS|0xE002|| +|Y_button_3DS|0xE003|| +|L_button_3DS|0xE004|| +|R_button_3DS|0xE005|| +|D_pad_3DS|0xE006|| +|Clock_DS|0xE007|| +|Happy_face_DS|0xE008|| +|Angry_face_DS|0xE009|| +|Sad_face_DS|0xE00A|| +|Expressionless_face_DS|0xE00B|| +|Sun_DS|0xE00C|| +|Cloud_DS|0xE00D|| +|Umbrella_DS|0xE00E|| +|Snowman_DS|0xE00F|| +|Exclamation_mark_DS|0xE010|| +|Question_mark_DS|0xE011|| +|Letter_DS|0xE012|| +|Phone_DS|0xE013|| +|Calibration_DS|0xE014|| +|Spade_suit_DS|0xE015|| +|Diamond_suit_DS|0xE016|| +|Heart_DS|0xE017|| +|Clubs_suit_DS|0xE018|| +|Right_DS|0xE019|| +|Left_DS|0xE01A|| +|Up_DS|0xE01B|| +|Down_DS|0xE01C|| +|Target_DS|0xE01D|| +|Camera_DS|0xE01E|| +|Unkown_1_DS|0xE01F|| +|Top_left_corner_border_DS|0xE020|| +|Top_border_DS|0xE021|| +|Top_right_corner_border_DS|0xE022|| +|Right_border_DS|0xE023|| +|Bottom_right_border_DS|0xE024|| +|Bottom_border_DS|0xE025|| +|Bottom_left_border_DS|0xE026|| +|Left_border_DS|0xE027|| +|Cross_DS|0xE028|| +|Boxed_A_DS|0xE029|| +|Boxed_B_DS|0xE02A|| +|Boxed_C_DS|0xE02B|| +|Boxed_D_DS|0xE02C|| +|Boxed_A_inverted_DS|0xE02D|| +|Boxed_M_inverted_DS|0xE02E|| +|Unknown_2_DS|0xE02F|| +|P_DS|0xE030|| +|I_DS|0xE031|| +|C_DS|0xE032|| +|T_DS|0xE033|| +|H_DS|0xE034|| +|A_DS|0xE035|| +|Unknown_3_DS|0xE036|| +|Unknown_4_DS|0xE037|| +|Unknown_5_DS|0xE038|| +|Unknown_6_DS|0xE039|| +|Unknown_7_DS|0xE03A|| +|Unknown_8_DS|0xE03B|| +|Unknown_9_DS|0xE03C|| +|Unknown_10_DS|0xE03D|| +|Inverted_boxed_cross_DS|0xE03E|| +|Inverted_boxed_cross_big_DS|0xE03F|| +|Circle_pad_3DS|0xE077|| +|Power_button_3DS|0xE078|| +|D_pad_up_3DS|0xE079|| +|D_pad_down_3DS|0xE07A|| +|D_pad_left_3DS|0xE07B|| +|D_pad_right_3DS|0xE07C|| +|D_pad_up_down_3DS|0xE07D|| +|D_pad_left_right_3DS|0xE07E|| +|Camera_3DS|0xE01E|| +|Close_empty_3DS|0xE070|| +|Close_full_3DS|0xE071|| +|Back_3DS|0xE072|| +|Home_3DS|0xE073|| +|Steps_3DS|0xE074|| +|Play_coin_3DS|0xE075|| +|Video_3DS|0xE076|| +|Power_Wii|0xE040|| +|D_pad_Wii|0xE041|| +|A_button_Wii|0xE042|| +|B_button_Wii|0xE043|| +|Home_Wii|0xE044|| +|Plus_button_Wii|0xE045|| +|Minus_button_Wii|0xE046|| +|1_button_Wii|0xE047|| +|2_button_Wii|0xE048|| +|Nunchuk_stick_Wii|0xE049|| +|C_button_Wii|0xE04A|| +|Z_button_Wii|0xE04B|| +|A_button_classic_Wii|0xE04C|| +|B_button_classic_Wii|0xE04D|| +|X_button_classic_Wii|0xE04E|| +|Y_button_classic_Wii|0xE04F|| +|L_stick_classic_Wii|0xE050|| +|R_stick_classic_Wii|0xE051|| +|L_button_classic_Wii|0xE052|| +|R_button_classic_Wii|0xE053|| +|ZL_button_classic_Wii|0xE054|| +|ZR_button_classic_Wii|0xE055|| +|Newline_Wii|0xE056|| +|Space_Wii|0xE057|| +|Hand_pointing_Wii|0xE058|| +|Hand_pointing_1P_Wii|0xE059|| +|Hand_pointing_2P_Wii|0xE05A|| +|Hand_pointing_3P_Wii|0xE05B|| +|Hand_pointing_4P_Wii|0xE05C|| +|Hand_closed_Wii|0xE05D|| +|Hand_closed_1P_Wii|0xE05E|| +|Hand_closed_2P_Wii|0xE05F|| +|Hand_closed_3P_Wii|0xE060|| +|Hand_closed_4P_Wii|0xE061|| +|Hand_opened_Wii|0xE062|| +|Hand_opened_1P_Wii|0xE063|| +|Hand_opened_2P_Wii|0xE064|| +|Hand_opened_3P_Wii|0xE065|| +|Hand_opened_4P_Wii|0xE066|| +|Wii_Wii|0xE067|| +|er_Wii|0xE068|| +|re_Wii|0xE069|| +|e_Wii|0xE06A|| +|Question_mark_Wii|0xE06B|| +|A_button_Switch|0xE0A0|| +|B_button_Switch|0xE0A1|| +|X_button_Switch|0xE0A2|| +|Y_button_Switch|0xE0A3|| +|L_button_Switch|0xE0A4|| +|R_button_Switch|0xE0A5|| +|ZL_button_Switch|0xE0A6|| +|ZR_button_Switch|0xE0A7|| +|SL_button_Switch|0xE0A8|| +|SR_button_Switch|0xE0A9|| +|D_buttons_Switch|0xE0AA|| +|Right_button_side_Switch|0xE0AB|| +|Down_button_side_Switch|0xE0AC|| +|Up_button_side_Switch|0xE0AD|| +|Left_button_side_Switch|0xE0AE|| +|D_button_up_Switch|0xE0AF|| +|D_button_down_Switch|0xE0B0|| +|D_button_left_Switch|0xE0B1|| +|D_button_right_Switch|0xE0B2|| +|Vol_up_Switch|0xE0B3|| +|Vol_down_Switch|0xE0B4|| +|Plus_Switch|0xE0B5|| +|Minus_Switch|0xE0B6|| +|Power_Switch|0xE0B7|| +|Sleep_Switch|0xE0B8|| +|Home_Switch|0xE0B9|| +|Screenshot_Switch|0xE0BA|| +|Stick_Switch|0xE0C0|| +|Left_stick_Switch|0xE0C1|| +|Right_stick_Switch|0xE0C2|| +|Press_stick_Switch|0xE0C3|| +|Press_left_stick_Switch|0xE0C4|| +|Press_right_stick_Switch|0xE0C5|| +|Rotate_counter_clockwise_left_Switch|0xE0C6|| +|Rotate_counter_clockwise_right_Switch|0xE0C7|| +|Rotate_clockwise_left_Switch|0xE0C8|| +|Rotate_clockwise_right_Switch|0xE0C9|| +|D_pad_Switch|0xE0D0|| +|D_pad_up_Switch|0xE0D1|| +|D_pad_down_Switch|0xE0D2|| +|D_pad_left_Switch|0xE0D3|| +|D_pad_right_Switch|0xE0D4|| +|D_pad_up_down_Switch|0xE0D5|| +|D_pad_left_right_Switch|0xE0D6|| +|A_button_inverted_Switch|0xE0E0|| +|B_button_inverted_Switch|0xE0E1|| +|X_button_inverted_Switch|0xE0E2|| +|Y_button_inverted_Switch|0xE0E3|| +|L_button_inverted_Switch|0xE0E4|| +|R_button_inverted_Switch|0xE0E5|| +|ZL_button_inverted_Switch|0xE0E6|| +|ZR_button_inverted_Switch|0xE0E7|| +|SL_button_inverted_Switch|0xE0E8|| +|SR_button_inverted_Switch|0xE0E9|| +|D_buttons_inverted_Switch|0xE0EA|| +|D_button_up_inverted_Switch|0xE0EB|| +|D_button_down_inverted_Switch|0xE0EC|| +|D_button_left_inverted_Switch|0xE0ED|| +|D_button_right_inverted_Switch|0xE0EE|| +|Vol_up_inverted_Switch|0xE0EF|| +|Vol_down_inverted_Switch|0xE0F0|| +|Plus_inverted_Switch|0xE0F1|| +|Minus_inverted_Switch|0xE0F2|| +|Power_inverted_Switch|0xE0F3|| +|Home_inverted_Switch|0xE0F4|| +|Screenshot_inverted_Switch|0xE0F5|| +|Stick_inverted_Switch|0xE100|| +|Left_stick_inverted_Switch|0xE101|| +|Right_stick_inverted_Switch|0xE102|| +|Press_stick_inverted_Switch|0xE103|| +|Press_left_stick_inverted_Switch|0xE104|| +|Press_right_stick_inverted_Switch|0xE105|| +|D_pad_inverted_Switch|0xE110|| +|D_pad_up_inverted_Switch|0xE111|| +|D_pad_down_inverted_Switch|0xE112|| +|D_pad_left_inverted_Switch|0xE113|| +|D_pad_right_inverted_Switch|0xE114|| +|D_pad_up_down_inverted_Switch|0xE115|| +|D_pad_left_right_inverted_Switch|0xE116|| +|Handheld_controller_Switch|0xE121|| +|Both_joycons_controller_Switch|0xE122|| +|Left_joycon_controller_Switch|0xE123|| +|Right_joycon_controller_Switch|0xE124|| +|Left_joycon_with_minus_controller_Switch|0xE125|| +|Right_joycon_with_plus_controller_Switch|0xE126|| +|Joycon_side_controller_Switch|0xE127|| +|Left_joycon_with_minus_side_controller_Switch|0xE128|| +|Right_joycon_with_plus_side_controller_Switch|0xE129|| +|Both_joycons_grip_controller_Switch|0xE12A|| +|No_joycons_grip_controller_Switch|0xE12B|| +|Pro_controller_Switch|0xE12C|| +|Brightness_Switch|0xE130|| +|Friends_Switch|0xE131|| +|News_Switch|0xE132|| +|Eshop_Switch|0xE133|| +|Gallery_Switch|0xE134|| +|Apps_Switch|0xE135|| +|Controllers_Switch|0xE136|| +|Mail_Switch|0xE137|| +|Phone_Switch|0xE138|| +|PC_Switch|0xE139|| +|TV_Switch|0xE13A|| +|Headphone_Switch|0xE13B|| +|Sound_Switch|0xE13C|| +|Warning_Switch|0xE140|| +|Error_Switch|0xE141|| +|Tip_Switch|0xE142|| +|Up_Switch|0xE143|| +|Down_Switch|0xE144|| +|Left_Switch|0xE145|| +|Right_Switch|0xE146|| +|North_Switch|0xE147|| +|South_Switch|0xE148|| +|West_Switch|0xE149|| +|East_Switch|0xE14A|| +|Yes_Switch|0xE14B|| +|No_Switch|0xE14C|| +|Warning_inverted_Switch|0xE150|| +|Error_inverted_Switch|0xE151|| +|Tip_inverted_Switch|0xE152|| \ No newline at end of file diff --git a/src/structs/txt2.rs b/src/structs/txt2.rs index 7d1ea9e..97a6c6f 100644 --- a/src/structs/txt2.rs +++ b/src/structs/txt2.rs @@ -433,7 +433,7 @@ impl TXT2{ control_string += " "; for code in control_code.params{ control_string += &format!("{code:02X}"); - control_string += "."; + control_string += "_"; } control_string.truncate(control_string.len()-1); } @@ -563,7 +563,7 @@ impl TXT2{ if bare_content.len() > 2{ control_code.params_size = ((bare_content[2].len()+1)/3) as u16; - for byte in bare_content[2].split('.').collect::>() { + for byte in bare_content[2].split('_').collect::>() { control_code.params.push(u8::from_str_radix(byte, 16).unwrap()); } } else {