Skip to content

Instantly share code, notes, and snippets.

@trishume
Last active October 5, 2019 03:43
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save trishume/138ef8f6c66fabd2d76b9fdf8d5c4c67 to your computer and use it in GitHub Desktop.
Save trishume/138ef8f6c66fabd2d76b9fdf8d5c4c67 to your computer and use it in GitHub Desktop.
Unity Asset Hex Fiend Template
# Hex Fiend Template for Unity Asset Files
# By Tristan Hume
# Based on inspection and reading the code for https://github.com/Perfare/AssetStudio
# See https://github.com/ridiculousfish/HexFiend/blob/master/templates/Tutorial.md
# Only supports the Unity version Beat Saber uses and Beat Saber's MonoBehaviors
# Match Unity Version 2018.
requires 20 "32 30 31 38 2E"
proc align_stream {align} {
set misalign [expr {$align - [pos] % $align}]
if {$misalign < $align} {
move $misalign
}
}
proc c_string {name} {
set length 1
while {[uint8] != 0} {
incr length
}
move -$length
ascii $length $name
}
proc aligned_string {name} {
set length [uint32]
if {$length > 0} {
str $length "utf8" $name
align_stream 4
} else {
move -4
bytes 4 $name
}
}
proc object_ref {name} {
section $name {
# index in export table
uint32 "File ID"
# this is 1 if exterior, object index+1 if interior
uint64 "Path ID"
}
}
proc beatmap_difficulties {} {
set difficulties_count [uint32 "Difficulties Count"]
for {set i 0} {$i < $difficulties_count} {incr i} {
section "Difficulty $i" {
uint32 "Difficulty"
uint32 "Rank"
float "Note Jump Movement Speed"
uint32 "Note Jump Start Beat Offset"
object_ref "Beatmap Data"
}
}
}
proc beatmap_level {} {
aligned_string "Level ID"
aligned_string "Song Name"
aligned_string "Sub Name"
aligned_string "Author Name"
aligned_string "Level Author"
object_ref "Audio Clip"
float "BPM"
float "Time Offset"
float "Shuffle"
float "Shuffle Period"
float "Preview Start Time"
float "Preview Duration"
# Dunno why song duration is missing in the data
# float "Song Duration"
object_ref "Cover Image"
object_ref "Environment"
set sets_count [uint32 "Beatmap Set Count"]
for {set i 0} {$i < $sets_count} {incr i} {
section "Set $i" {
object_ref "Characteristic"
beatmap_difficulties
}
}
}
proc beatmap_data {} {
aligned_string "JSON Data"
set sig_len [uint32 "Signature Length"]
hex $sig_len "Signature"
set data_len [uint32 "Data Length"]
bytes $data_len "Projected Data"
}
proc level_collection {} {
set sets_count [uint32 "Level Count"]
for {set i 0} {$i < $sets_count} {incr i} {
object_ref "Level $i"
}
}
proc audio_clip {} {
aligned_string "Name"
uint32 "Load Type"
uint32 "Channels"
uint32 "Frequency"
uint32 "Bits Per Sample"
float "Length"
uint8 "Is Tracker"
align_stream 4
uint32 "Subsound Index"
uint8 "Preload Audio"
uint8 "Background Load"
uint8 "Legacy 3D"
align_stream 4
aligned_string "Source"
uint64 "Offset"
uint64 "Size"
# 0 = PCM, 1 = Vorbis, 3 = MP3
uint32 "Compression Format"
}
big_endian
section "Header" {
set meta_s [uint32 "Metadata Size"]
set file_size [uint32 "File Size"]
uint32 "Generation"
set data_offset [uint32 "Data Offset"]
uint32 "Is Big Endian"
}
little_endian
section "Metadata" {
# ascii 12 "Unity Version"
c_string "Unity Version"
uint32 "Target Platform"
uint8 "Enable Type Tree"
set type_count [uint32 "Type Count"]
set types_classes [dict create]
section "Types" {
for {set i 0} {$i < $type_count} {incr i} {
section [expr $i + 1] {
set class_id [uint32 "Class ID"]
dict set types_classes $i $class_id
uint8 "Is Stripped"
int16 "Script Type Index"
if {$class_id == 114} {
hex 16 "Script ID"
}
hex 16 "Type Hash"
}
}
}
set object_count [uint32 "Object Count"]
set objects [list]
section "Objects" {
for {set i 0} {$i < $object_count} {incr i} {
section [expr $i] {
align_stream 4
uint64 "Path ID"
set offset [uint32 "Byte Offset"]
uint32 "Byte Size"
set type_id [uint32 "Type ID"]
set class_id [dict get $types_classes $type_id]
entry "Class ID" $class_id
lappend objects [dict create offset $offset id $i class_id $class_id]
}
}
}
set script_count [uint32 "Script Count"]
section "Scripts" {
for {set i 0} {$i < $script_count} {incr i} {
section [expr $i] {
uint32 "File Index"
align_stream 4
uint64 "In-File ID"
}
}
}
set externals_count [uint32 "Externals Count"]
section "Externals" {
for {set i 0} {$i < $externals_count} {incr i} {
section [expr $i] {
c_string "Temp Empty"
hex 16 "GUID"
uint32 "Type"
c_string "Path Name"
}
}
}
}
goto $data_offset
set data_len [expr {$file_size - $data_offset}]
section "Data" {
# bytes $data_len "Data"
foreach object $objects {
set object_offset [dict get $object offset]
set object_id [dict get $object id]
set object_class [dict get $object class_id]
goto [expr {$data_offset + $object_offset}]
if {$object_class == 114} {
section "$object_id: MonoBehavior" {
object_ref "GameObject"
uint32 "Enabled"
uint32 "Script Export ID"
set kind [uint64 "Kind"]
# TODO
# section "Script" {
# uint32 "Execution Order"
# hex 16 "Properties Hash"
# aligned_string "Class Name"
# }
aligned_string "Name"
# BeatmapLevelSO
if {$kind == 644} {
beatmap_level
} elseif {$kind == 762} {
level_collection
} elseif {$kind == 1552} {
beatmap_data
}
}
} elseif {$object_class == 83} {
section "$object_id: AudioClip" {
audio_clip
}
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment