aboutsummaryrefslogtreecommitdiff
path: root/tools/compile_assets/aseprite/read_chunk.odin
diff options
context:
space:
mode:
authorXander Swan <[hidden email]>2026-01-07 23:12:22 -0500
committerXander Swan <[hidden email]>2026-01-07 23:12:22 -0500
commit0988ab832bfc7a1b1c851125b6172cf68c6d9cb9 (patch)
tree460bc2d9f0bce463af273d6b2b2c20faa880ac29 /tools/compile_assets/aseprite/read_chunk.odin
parentade0dc4d257d053b7064184f193f8168c496e308 (diff)
doesn't compile but i'm commiting anywya
Diffstat (limited to 'tools/compile_assets/aseprite/read_chunk.odin')
-rw-r--r--tools/compile_assets/aseprite/read_chunk.odin423
1 files changed, 423 insertions, 0 deletions
diff --git a/tools/compile_assets/aseprite/read_chunk.odin b/tools/compile_assets/aseprite/read_chunk.odin
new file mode 100644
index 0000000..9f7839e
--- /dev/null
+++ b/tools/compile_assets/aseprite/read_chunk.odin
@@ -0,0 +1,423 @@
+package aseprite_file_handler
+
+import "base:intrinsics"
+import "core:io"
+import "core:log"
+import "core:bytes"
+import "core:compress/zlib"
+
+
+read_file_header :: proc(r: io.Reader, rt: ^int) -> (h: File_Header, err: Unmarshal_Error) {
+ h.size = read_dword(r, rt) or_return
+
+ if io.Stream_Mode.Size in io.query(r) {
+ stream_size := io.size(r) or_return
+ if stream_size != i64(h.size) {
+ return {}, .Data_Size_Not_Equal_To_Header
+ }
+ }
+
+ magic := read_word(r, rt) or_return
+ if magic != FILE_MAGIC_NUM {
+ return {}, .Bad_File_Magic_Number
+ }
+
+ h.frames = read_word(r, rt) or_return
+
+ h.width = read_word(r, rt) or_return
+ h.height = read_word(r, rt) or_return
+ h.color_depth = Color_Depth(read_word(r, rt) or_return)
+
+ h.flags = transmute(File_Flags)read_dword(r, rt) or_return
+ h.speed = read_word(r, rt) or_return
+ read_skip(r, 4+4, rt) or_return
+
+ h.transparent_index = read_byte(r, rt) or_return
+ read_skip(r, 3, rt) or_return
+
+ h.num_of_colors = read_word(r, rt) or_return
+ h.ratio_width = read_byte(r, rt) or_return
+ h.ratio_height = read_byte(r, rt) or_return
+
+ h.x = read_short(r, rt) or_return
+ h.y = read_short(r, rt) or_return
+
+ h.grid_width = read_word(r, rt) or_return
+ h.grid_height = read_word(r, rt) or_return
+ read_skip(r, 84, rt) or_return
+
+ return
+}
+
+
+read_old_palette_256 :: proc(r: io.Reader, rt: ^int, allocator := context.allocator) -> (chunk: Old_Palette_256_Chunk, err: Unmarshal_Error) {
+ op_size := cast(int)read_word(r, rt) or_return
+ chunk = make(Old_Palette_256_Chunk, op_size, allocator) or_return
+
+ for &packet in chunk {
+ packet.entries_to_skip = read_byte(r, rt) or_return
+ packet.num_colors = read_byte(r, rt) or_return
+ count := int(packet.num_colors)
+ if count == 0 {
+ count = 256
+ }
+
+ packet.colors = make([]Color_RGB, count, allocator) or_return
+ for &c in packet.colors {
+ read_bytes(r, c[:], rt) or_return
+ }
+ }
+ return
+}
+
+read_old_palette_64 :: proc(r: io.Reader, rt: ^int, allocator := context.allocator) -> (chunk: Old_Palette_64_Chunk, err: Unmarshal_Error) {
+ op_size := cast(int)read_word(r, rt) or_return
+ chunk = make(Old_Palette_64_Chunk, op_size, allocator) or_return
+
+ for &packet in chunk {
+ packet.entries_to_skip = read_byte(r, rt) or_return
+ packet.num_colors = read_byte(r, rt) or_return
+ count := int(packet.num_colors)
+ if count == 0 {
+ count = 256
+ }
+
+ packet.colors = make([]Color_RGB, count, allocator) or_return
+ for &c in packet.colors {
+ read_bytes(r, c[:], rt) or_return
+ }
+ }
+ return
+}
+
+read_layer :: proc(r: io.Reader, rt: ^int, has_uuid: bool, allocator := context.allocator) -> (chunk: Layer_Chunk, err: Unmarshal_Error) {
+ chunk.flags = transmute(Layer_Chunk_Flags)read_word(r, rt) or_return
+ chunk.type = Layer_Types(read_word(r, rt) or_return)
+ chunk.child_level = read_word(r, rt) or_return
+ chunk.default_width = read_word(r, rt) or_return
+ chunk.default_height = read_word(r, rt) or_return
+ chunk.blend_mode = Layer_Blend_Mode(read_word(r, rt) or_return)
+ chunk.opacity = read_byte(r, rt) or_return
+ read_skip(r, 3, rt) or_return
+ chunk.name = read_string(r, rt, allocator) or_return
+
+ if chunk.type == .Tilemap {
+ chunk.tileset_index = read_dword(r, rt) or_return
+ }
+ if has_uuid {
+ chunk.uuid = read_uuid(r, rt) or_return
+ }
+
+ return
+}
+
+read_cel :: proc(r: io.Reader, rt: ^int, color_depth: int, c_size: int, allocator := context.allocator) -> (chunk: Cel_Chunk, err: Unmarshal_Error) {
+ context.allocator = allocator
+ chunk.layer_index = read_word(r, rt) or_return
+ chunk.x = read_short(r, rt) or_return
+ chunk.y = read_short(r, rt) or_return
+ chunk.opacity_level = read_byte(r, rt) or_return
+ chunk.type = Cel_Types(read_word(r, rt) or_return)
+ chunk.z_index = read_short(r, rt) or_return
+ read_skip(r, 5, rt) or_return
+
+ switch chunk.type {
+ case .Raw:
+ cel: Raw_Cel
+ cel.width = read_word(r, rt) or_return
+ cel.height = read_word(r, rt) or_return
+ cel.pixels = make([]PIXEL, int(cel.width * cel.height)) or_return
+ read_bytes(r, cel.pixels[:], rt) or_return
+ chunk.cel = cel
+
+ case .Linked_Cel:
+ chunk.cel = Linked_Cel(read_word(r, rt) or_return)
+
+ case .Compressed_Image:
+ cel: Com_Image_Cel
+ cel.width = read_word(r, rt) or_return
+ cel.height = read_word(r, rt) or_return
+
+ com_size := c_size-26
+ if com_size <= 0 {
+ err = .Invalid_Compression_Size
+ return
+ }
+
+ buf: bytes.Buffer
+ defer bytes.buffer_destroy(&buf)
+
+ data := make([]byte, com_size) or_return
+ defer delete(data)
+
+ read_bytes(r, data[:], rt) or_return
+
+ exp_size := color_depth / 8 * int(cel.height) * int(cel.width)
+ zlib.inflate(data[:], &buf, expected_output_size=exp_size) or_return
+
+ cel.pixels = make([]byte, exp_size) or_return
+ copy(cel.pixels[:], buf.buf[:])
+
+ chunk.cel = cel
+
+ case .Compressed_Tilemap:
+ cel: Com_Tilemap_Cel
+ cel.width = read_word(r, rt) or_return
+ cel.height = read_word(r, rt) or_return
+ cel.bits_per_tile = read_word(r, rt) or_return
+ cel.bitmask_id = Tile_ID(read_dword(r, rt) or_return)
+ cel.bitmask_x = read_dword(r, rt) or_return
+ cel.bitmask_y = read_dword(r, rt) or_return
+ cel.bitmask_diagonal = read_dword(r, rt) or_return
+ read_skip(r, 10, rt) or_return
+
+ buf: bytes.Buffer
+ defer bytes.buffer_destroy(&buf)
+ // size_of(DWORD*5, WORD*6, SHORT*3, BYTE, SKIPED*15)-1
+ com_size := c_size-54
+ if com_size <= 0 {
+ err = .Invalid_Compression_Size
+ return
+ }
+
+ data := make([]byte, com_size) or_return
+ defer delete(data)
+ read_bytes(r, data[:], rt) or_return
+ exp_size := color_depth / 8 * int(cel.height) * int(cel.width)
+ zlib.inflate(data[:], &buf, expected_output_size=exp_size) or_return
+
+ br: bytes.Reader
+ bytes.reader_init(&br, buf.buf[:])
+ rr, ok := io.to_reader(bytes.reader_to_stream(&br))
+ if !ok { err = .Unable_Make_Reader; return }
+
+ cel.tiles = make([]TILE, cel.height * cel.width) or_return
+ read_tiles(rr, cel.tiles[:], cel.bitmask_id, rt) or_return
+
+ chunk.cel = cel
+
+ case:
+ err = .Invalid_Cel_Type
+ return
+ }
+ return
+}
+
+read_cel_extra :: proc(r: io.Reader, rt: ^int) -> (chunk: Cel_Extra_Chunk, err: Unmarshal_Error) {
+ chunk.flags = transmute(Cel_Extra_Flags)read_word(r, rt) or_return
+ chunk.x = read_fixed(r, rt) or_return
+ chunk.y = read_fixed(r, rt) or_return
+ chunk.width = read_fixed(r, rt) or_return
+ chunk.height = read_fixed(r, rt) or_return
+ return
+}
+
+read_color_profile :: proc(r: io.Reader, rt: ^int, warned: ^bool, allocator := context.allocator) -> (chunk: Color_Profile_Chunk, err: Unmarshal_Error) {
+ chunk.type = Color_Profile_Type(read_word(r, rt) or_return)
+ chunk.flags = transmute(Color_Profile_Flags)read_word(r, rt) or_return
+ chunk.fixed_gamma = read_fixed(r, rt) or_return
+ read_skip(r, 8, rt) or_return
+
+ if chunk.type == .ICC {
+ icc_size := cast(int)read_dword(r, rt) or_return
+ chunk.icc = make(ICC_Profile, icc_size, allocator) or_return
+ read_bytes(r, cast([]u8)chunk.icc.(ICC_Profile)[:], rt) or_return
+ if !warned^ {
+ log.warn("Embedded ICC Color Profiles are currently not supported.")
+ warned^ = true
+ }
+ }
+ return
+}
+
+read_external_files :: proc(r: io.Reader, rt: ^int, allocator := context.allocator) -> (chunk: External_Files_Chunk, err: Unmarshal_Error) {
+ entries := read_dword(r, rt) or_return
+ chunk = make([]External_Files_Entry, entries, allocator) or_return
+ read_skip(r, 8, rt) or_return
+
+ for &entry in chunk {
+ entry.id = read_dword(r, rt) or_return
+ entry.type = ExF_Entry_Type(read_byte(r, rt) or_return)
+ read_skip(r, 7, rt) or_return
+ entry.file_name_or_id = read_string(r, rt, allocator) or_return
+ }
+ return
+}
+
+read_mask :: proc(r: io.Reader, rt: ^int, allocator := context.allocator) -> (chunk: Mask_Chunk, err: Unmarshal_Error) {
+ chunk.x = read_short(r, rt) or_return
+ chunk.y = read_short(r, rt) or_return
+ chunk.width = read_word(r, rt) or_return
+ chunk.height = read_word(r, rt) or_return
+ read_skip(r, 8, rt) or_return
+ chunk.name = read_string(r, rt, allocator) or_return
+
+ size := int(chunk.height) * ((int(chunk.width) + 7) / 8)
+ chunk.bit_map_data = make([]BYTE, size, allocator) or_return
+ read_bytes(r, chunk.bit_map_data[:], rt) or_return
+ return
+}
+
+read_path :: proc() -> (chunk: Path_Chunk) {
+ return
+}
+
+read_tags :: proc(r: io.Reader, rt: ^int, allocator := context.allocator) -> (chunk: Tags_Chunk, err: Unmarshal_Error) {
+ size := cast(int)read_word(r, rt) or_return
+ chunk = make([]Tag, size, allocator) or_return
+ read_skip(r, 8, rt) or_return
+
+ for &tag in chunk {
+ tag.from_frame = read_word(r, rt) or_return
+ tag.to_frame = read_word(r, rt) or_return
+ tag.loop_direction = Tag_Loop_Dir(read_byte(r, rt) or_return)
+ tag.repeat = read_word(r, rt) or_return
+
+ read_skip(r, 6, rt) or_return
+ read_bytes(r, tag.tag_color[:], rt) or_return
+ read_byte(r, rt) or_return
+ tag.name = read_string(r, rt, allocator) or_return
+ }
+
+ return
+}
+
+read_palette :: proc(r: io.Reader, rt: ^int, allocator := context.allocator) -> (chunk: Palette_Chunk, err: Unmarshal_Error) {
+ chunk.size = read_dword(r, rt) or_return
+ chunk.first_index = read_dword(r, rt) or_return
+ chunk.last_index = read_dword(r, rt) or_return
+ size := int(chunk.last_index - chunk.first_index + 1)
+ chunk.entries = make([]Palette_Entry, size, allocator) or_return
+ read_skip(r, 8, rt) or_return
+
+ for &entry in chunk.entries {
+ pf := transmute(Pal_Flags)read_word(r, rt) or_return
+ read_bytes(r, entry.color[:], rt) or_return
+
+ if .Has_Name in pf {
+ entry.name = read_string(r, rt, allocator) or_return
+ }
+ }
+
+ return
+}
+
+read_user_data :: proc(r: io.Reader, rt: ^int, allocator := context.allocator) -> (chunk: User_Data_Chunk, err: Unmarshal_Error) {
+ flags := transmute(UD_Flags)read_dword(r, rt) or_return
+
+ if .Text in flags {
+ chunk.text = read_string(r, rt) or_return
+ }
+
+ if .Color in flags {
+ colour: Color_RGBA
+ read_bytes(r, colour[:], rt) or_return
+ chunk.color = colour
+ }
+
+ if .Properties in flags {
+ //map_size := read_dword(r, rt) or_return
+ read_skip(r, 4, rt) or_return
+ map_num := read_dword(r, rt) or_return
+ maps := make(Properties_Map, map_num) or_return
+
+ for _ in 0..<map_num {
+ key := read_dword(r, rt) or_return
+
+ prop_num := int(read_dword(r, rt) or_return)
+ val := make(Properties, prop_num) or_return
+
+ for _ in 0..<prop_num {
+ name := read_string(r, rt) or_return
+ defer delete(name)
+ type := Property_Type(read_word(r, rt) or_return)
+ val[name] = read_ud_value(r, type, rt) or_return
+ }
+
+ maps[key] = val
+ }
+ chunk.maps = maps
+ }
+
+ return
+}
+
+read_slice :: proc(r: io.Reader, rt: ^int, alloc := context.allocator) -> (chunk: Slice_Chunk, err: Unmarshal_Error) {
+ context.allocator = alloc
+ keys := int(read_dword(r, rt) or_return)
+ chunk.flags = transmute(Slice_Flags)read_dword(r, rt) or_return
+ read_dword(r, rt) or_return
+ chunk.name = read_string(r, rt) or_return
+ chunk.keys = make([]Slice_Key, keys) or_return
+
+ for &key in chunk.keys {
+ key.frame_num = read_dword(r, rt) or_return
+ key.x = read_long(r, rt) or_return
+ key.y = read_long(r, rt) or_return
+ key.width = read_dword(r, rt) or_return
+ key.height = read_dword(r, rt) or_return
+
+ if .Patched_slice in chunk.flags {
+ cen: Slice_Center
+ cen.x = read_long(r, rt) or_return
+ cen.y = read_long(r, rt) or_return
+ cen.width = read_dword(r, rt) or_return
+ cen.height = read_dword(r, rt) or_return
+ key.center = cen
+ }
+
+ if .Pivot_Information in chunk.flags {
+ p: Slice_Pivot
+ p.x = read_long(r, rt) or_return
+ p.y = read_long(r, rt) or_return
+ key.pivot = p
+ }
+ }
+
+ return
+}
+
+read_tileset :: proc(r: io.Reader, rt: ^int, allocator := context.allocator) -> (chunk: Tileset_Chunk, err: Unmarshal_Error) {
+ chunk.id = read_dword(r, rt) or_return
+ chunk.flags = transmute(Tileset_Flags)read_dword(r, rt) or_return
+ chunk.num_of_tiles = read_dword(r, rt) or_return
+ chunk.width = read_word(r, rt) or_return
+ chunk.height = read_word(r, rt) or_return
+ chunk.base_index = read_short(r, rt) or_return
+ read_skip(r, 14, rt)
+ chunk.name = read_string(r, rt) or_return
+
+ if .Include_Link_To_External_File in chunk.flags {
+ ex: Tileset_External
+ ex.file_id = read_dword(r, rt) or_return
+ ex.tileset_id = read_dword(r, rt) or_return
+ chunk.external = ex
+ }
+
+ if .Include_Tiles_Inside_This_File in chunk.flags {
+ size := int(read_dword(r, rt) or_return)
+
+ buf: bytes.Buffer
+
+ data := make([]byte, size, allocator) or_return
+ defer delete(data)
+
+ read_bytes(r, data, rt) or_return
+
+ zlib.inflate_from_byte_array(data, &buf) or_return
+
+ res := len(buf.buf)-buf.off
+ exp_size := int(chunk.width) * int(chunk.height) * int(chunk.num_of_tiles)
+
+ if (res != exp_size) && (res != exp_size*2) && (res != exp_size*4) {
+ fast_log(.Error, "Expected size not equal to uncompressed size")
+ return chunk, Read_Error(Read_Errors.Comp_Tileset_Not_Expected_Size)
+ }
+
+ chunk.compressed = (Tileset_Compressed)(buf.buf[buf.off:])
+
+ }
+
+ return
+}
+