aboutsummaryrefslogtreecommitdiff
path: root/tools/compile_assets/aseprite/marshal.odin
diff options
context:
space:
mode:
Diffstat (limited to 'tools/compile_assets/aseprite/marshal.odin')
-rw-r--r--tools/compile_assets/aseprite/marshal.odin549
1 files changed, 549 insertions, 0 deletions
diff --git a/tools/compile_assets/aseprite/marshal.odin b/tools/compile_assets/aseprite/marshal.odin
new file mode 100644
index 0000000..1b56de1
--- /dev/null
+++ b/tools/compile_assets/aseprite/marshal.odin
@@ -0,0 +1,549 @@
+package aseprite_file_handler
+
+import "core:io"
+import "core:os"
+import "core:log"
+import "core:bytes"
+import "core:bufio"
+import "vendor:zlib"
+
+
+marshal_to_bytes_buff :: proc(doc: ^Document, b: ^bytes.Buffer, allocator := context.allocator)-> (file_size: int, err: Marshal_Error) {
+ w, ok := io.to_writer(bytes.buffer_to_stream(b))
+ if !ok {
+ return file_size, .Unable_Make_Writer
+ }
+ return marshal(doc, w, allocator)
+}
+
+marshal_to_handle :: proc(doc: ^Document, h: os.Handle, allocator := context.allocator)-> (file_size: int, err: Marshal_Error) {
+ w, ok := io.to_writer(os.stream_from_handle(h))
+ if !ok {
+ return file_size, .Unable_Make_Writer
+ }
+ return marshal(doc, w, allocator)
+}
+
+marshal_to_slice :: proc(doc: ^Document, b: []byte, allocator := context.allocator)-> (file_size: int, err: Marshal_Error) {
+ buf: bytes.Buffer
+ defer bytes.buffer_destroy(&buf)
+ file_size = marshal(doc, &buf, allocator) or_return
+ if len(b) < len(buf.buf[buf.off:]) {
+ return file_size, Marshal_Errors.Buffer_Not_Big_Enough
+ }
+ copy_slice(b[:], buf.buf[buf.off:])
+ return
+}
+
+marshal_to_dynamic :: proc(doc: ^Document, b: ^[dynamic]byte, allocator := context.allocator)-> (file_size: int, err: Marshal_Error) {
+ buf: bytes.Buffer
+ defer bytes.buffer_destroy(&buf)
+ file_size = marshal(doc, &buf, allocator) or_return
+ append(b, ..buf.buf[:])
+ return
+}
+
+marshal_to_bufio :: proc(doc: ^Document, w: ^bufio.Writer, allocator := context.allocator) -> (file_size: int, err: Marshal_Error) {
+ ww, ok := io.to_writer(bufio.writer_to_stream(w))
+ if !ok {
+ return file_size, .Unable_Make_Writer
+ }
+ return marshal(doc, ww, allocator)
+}
+
+marshal :: proc{
+ marshal_to_bytes_buff, marshal_to_slice, marshal_to_handle,
+ marshal_to_dynamic, marshal_to_bufio, marshal_to_writer,
+}
+
+marshal_to_writer :: proc(doc: ^Document, ww: io.Writer, allocator := context.allocator) -> (file_size: int, err: Marshal_Error) {
+ ud_map_warn: bool
+ s := &file_size
+ b: bytes.Buffer
+ defer bytes.buffer_destroy(&b)
+
+ w, ok := io.to_writer(bytes.buffer_to_stream(&b))
+ if !ok {
+ return file_size, .Unable_Make_Writer
+ }
+
+ write(w, FILE_MAGIC_NUM, s) or_return
+ write(w, WORD(len(doc.frames)), s) or_return
+ write(w, doc.header.width, s) or_return
+ write(w, doc.header.height, s) or_return
+ write(w, WORD(doc.header.color_depth), s) or_return
+ write(w, transmute(DWORD)doc.header.flags, s) or_return
+ write(w, doc.header.speed, s) or_return
+ write_skip(w, 8, s) or_return
+ write(w, doc.header.transparent_index, s) or_return
+ write_skip(w, 3, s) or_return
+ write(w, doc.header.num_of_colors, s) or_return
+ write(w, doc.header.ratio_width, s) or_return
+ write(w, doc.header.ratio_height, s) or_return
+ write(w, doc.header.x, s) or_return
+ write(w, doc.header.y, s) or_return
+ write(w, doc.header.grid_width, s) or_return
+ write(w, doc.header.grid_height, s) or_return
+ write_skip(w, 84, s) or_return
+
+ for frame in doc.frames {
+ fb: bytes.Buffer
+ defer bytes.buffer_destroy(&fb)
+
+ fw, ok2 := io.to_writer(bytes.buffer_to_stream(&fb))
+ if !ok2 {
+ return file_size, .Unable_Make_Writer
+ }
+
+ frame_size: int
+ fs := &frame_size
+
+ write(fw, FRAME_MAGIC_NUM, fs) or_return
+ write(fw, frame.header.old_num_of_chunks, fs) or_return
+ write(fw, frame.header.duration, fs) or_return
+ write_skip(fw, 2, fs) or_return
+ write(fw, frame.header.num_of_chunks, fs) or_return
+
+ for chunk in frame.chunks {
+ cb: bytes.Buffer
+ defer bytes.buffer_destroy(&cb)
+
+ cw, ok3 := io.to_writer(bytes.buffer_to_stream(&cb))
+ if !ok3 {
+ return file_size, .Unable_Make_Writer
+ }
+
+ chunk_size: int
+ cs := &chunk_size
+
+ chunk_type := get_chunk_type(chunk) or_return
+ write(cw, chunk_type, cs) or_return
+
+ switch val in chunk {
+ case Old_Palette_256_Chunk:
+ write(cw, WORD(len(val)), cs) or_return
+
+ for p in val {
+ write(cw, p.entries_to_skip, cs) or_return
+ if len(p.colors) > 256 {
+ return file_size, .Invalid_Old_Palette
+
+ } else if len(p.colors) == 256 {
+ write_byte(cw, 0, cs) or_return
+
+ } else {
+ write(cw, p.num_colors, cs) or_return
+ }
+
+ for c in p.colors {
+ write(cw, c[2], cs) or_return
+ write(cw, c[1], cs) or_return
+ write(cw, c[0], cs) or_return
+ }
+ }
+
+ case Old_Palette_64_Chunk:
+ write(cw, WORD(len(val)), cs) or_return
+
+ for p in val {
+ write(cw, p.entries_to_skip, cs) or_return
+
+ if len(p.colors) > 256 {
+ return file_size, .Invalid_Old_Palette
+
+ } else if len(p.colors) == 256 {
+ write_byte(cw, 0, cs) or_return
+
+ } else {
+ write(cw, p.num_colors, cs) or_return
+ }
+
+ for c in p.colors {
+ write(cw, c[2], cs) or_return
+ write(cw, c[1], cs) or_return
+ write(cw, c[0], cs) or_return
+ }
+ }
+ case Layer_Chunk:
+ write(cw, transmute(WORD)val.flags, cs) or_return
+ write(cw, WORD(val.type), cs) or_return
+ write(cw, val.child_level, cs) or_return
+ write(cw, val.default_width, cs) or_return
+ write(cw, val.default_height, cs) or_return
+ write(cw, WORD(val.blend_mode), cs) or_return
+ write(cw, val.opacity, cs) or_return
+ write_skip(cw, 3, cs) or_return
+ write(cw, val.name, cs) or_return
+
+ if val.type == .Tilemap {
+ write(cw, val.tileset_index, cs) or_return
+ }
+
+ case Cel_Chunk:
+ write(cw, val.layer_index, cs) or_return
+ write(cw, val.x, cs) or_return
+ write(cw, val.y, cs) or_return
+ write(cw, val.opacity_level, cs) or_return
+
+ cel_type := get_cel_type(val.cel) or_return
+ write(cw, cel_type, cs) or_return
+ write(cw, val.z_index, cs) or_return
+ write_skip(cw, 5, cs) or_return
+
+ switch cel in val.cel {
+ case Raw_Cel:
+ write(cw, cel.width, cs) or_return
+ write(cw, cel.height, cs) or_return
+ write(cw, cel.pixels[:], cs) or_return
+
+ case Linked_Cel:
+ write(cw, WORD(cel), cs) or_return
+
+ case Com_Image_Cel:
+ write(cw, cel.width, cs) or_return
+ write(cw, cel.height, cs) or_return
+
+ com_buf := make([]byte, len(cel.pixels)+64, allocator) or_return
+ defer delete(com_buf)
+
+ data_rd: [^]u8 = raw_data(cel.pixels[:])
+ com_buf_rd: [^]u8 = raw_data(com_buf[:])
+
+ config := zlib.z_stream {
+ avail_in=zlib.uInt(len(cel.pixels)),
+ next_in=&data_rd[0],
+ avail_out=zlib.uInt(len(com_buf)),
+ next_out=&com_buf_rd[0],
+ }
+
+ en := zlib.deflateInit(&config, zlib.DEFAULT_COMPRESSION)
+ if en < zlib.OK {
+ return file_size, ZLIB_Errors(en)
+ }
+
+ en = zlib.deflate(&config, zlib.FINISH)
+ if en < zlib.OK {
+ return file_size, ZLIB_Errors(en)
+ }
+
+ en = zlib.deflateEnd(&config)
+ if en < zlib.OK {
+ return file_size, ZLIB_Errors(en)
+ }
+
+ write(cw, com_buf[:int(config.total_out)], cs) or_return
+
+ case Com_Tilemap_Cel:
+ write(cw, cel.width, cs) or_return
+ write(cw, cel.height, cs) or_return
+ write(cw, cel.bits_per_tile, cs) or_return
+ write(cw, DWORD(cel.bitmask_id), cs) or_return
+ write(cw, cel.bitmask_x, cs) or_return
+ write(cw, cel.bitmask_y, cs) or_return
+ write(cw, cel.bitmask_diagonal, cs) or_return
+ write_skip(cw, 10, cs) or_return
+
+ buf := make([]u8, len(cel.tiles)*4, allocator) or_return
+ defer delete(buf)
+
+ n := tiles_to_u8(cel.tiles[:], buf[:]) or_return
+ com_buf := make([]byte, n+64, allocator) or_return
+ defer delete(com_buf)
+
+ data_rd: [^]u8 = raw_data(buf[:n])
+ com_buf_rd: [^]u8 = raw_data(com_buf[:])
+
+ config := zlib.z_stream {
+ avail_in=zlib.uInt(n),
+ next_in=&data_rd[0],
+ avail_out=zlib.uInt(len(com_buf)),
+ next_out=&com_buf_rd[0],
+ }
+
+ en := zlib.deflateInit(&config, zlib.DEFAULT_COMPRESSION)
+ if en < zlib.OK {
+ return file_size, ZLIB_Errors(en)
+ }
+
+ en = zlib.deflate(&config, zlib.FINISH)
+ if en < zlib.OK {
+ return file_size, ZLIB_Errors(en)
+ }
+
+ en = zlib.deflateEnd(&config)
+ if en < zlib.OK {
+ return file_size, ZLIB_Errors(en)
+ }
+
+ write(cw, com_buf[:int(config.total_out)], cs) or_return
+
+ case:
+ return file_size, .Invalid_Cel_Type
+ }
+
+ case Cel_Extra_Chunk:
+ write(cw, transmute(WORD)val.flags, cs) or_return
+ write(cw, val.x, cs) or_return
+ write(cw, val.y, cs) or_return
+ write(cw, val.width, cs) or_return
+ write(cw, val.width, cs) or_return
+ write_skip(cw, 16, cs) or_return
+
+ case Color_Profile_Chunk:
+ if val.icc != nil {
+ write(cw, WORD(Color_Profile_Type.ICC), cs) or_return
+ } else {
+ write(cw, WORD(val.type), cs) or_return
+ }
+
+ write(cw, transmute(WORD)val.flags, cs) or_return
+ write(cw, val.fixed_gamma, cs) or_return
+ write_skip(cw, 8, cs) or_return
+
+ #partial switch v in val.icc {
+ case ICC_Profile:
+ write(cw, DWORD(len(v)), cs) or_return
+ write(cw, cast([]u8)v[:], cs) or_return
+ }
+
+ case External_Files_Chunk:
+ write(cw, DWORD(len(val)), cs) or_return
+ write_skip(cw, 8, cs) or_return
+
+ for file in val {
+ write(cw, file.id, cs) or_return
+ write(cw, BYTE(file.type), cs) or_return
+ write_skip(cw, 7, cs) or_return
+ write(cw, file.file_name_or_id, cs) or_return
+ }
+
+ case Mask_Chunk:
+ write(cw, val.x, cs) or_return
+ write(cw, val.y, cs) or_return
+ write(cw, val.width, cs) or_return
+ write(cw, val.height, cs) or_return
+ write_skip(cw, 8, cs) or_return
+ write(cw, val.name, cs) or_return
+ write(cw, val.bit_map_data[:], cs) or_return
+
+ case Path_Chunk:
+
+ case Tags_Chunk:
+ write(cw, WORD(len(val)), cs) or_return
+ write_skip(cw, 8, cs) or_return
+
+ for tag in val {
+ write(cw, tag.from_frame, cs) or_return
+ write(cw, tag.to_frame, cs) or_return
+ write(cw, BYTE(tag.loop_direction), cs) or_return
+ write(cw, tag.repeat, cs) or_return
+ write_skip(cw, 6, cs) or_return
+ write(cw, tag.tag_color[2], cs) or_return
+ write(cw, tag.tag_color[1], cs) or_return
+ write(cw, tag.tag_color[0], cs) or_return
+ write(cw, BYTE(0), cs) or_return
+ write(cw, tag.name, cs) or_return
+ }
+
+ case Palette_Chunk:
+ write(cw, DWORD(len(val.entries)), cs) or_return
+ write(cw, val.first_index, cs) or_return
+ write(cw, val.last_index, cs) or_return
+ write_skip(cw, 8, cs) or_return
+
+ for entry in val.entries {
+ if entry.name != nil {
+ write(cw, WORD(1), cs) or_return
+ } else {
+ write(cw, WORD(0), cs) or_return
+ }
+
+ write(cw, entry.color[3], cs) or_return
+ write(cw, entry.color[2], cs) or_return
+ write(cw, entry.color[1], cs) or_return
+ write(cw, entry.color[0], cs) or_return
+
+ #partial switch v in entry.name {
+ case string:
+ write(cw, v, cs) or_return
+ }
+ }
+
+ case User_Data_Chunk:
+ flags: UD_Flags
+ if val.text != nil { flags += {.Text} }
+ if val.color != nil { flags += {.Color} }
+ if val.maps != nil { flags += {.Properties} }
+
+ write(cw, transmute(DWORD)flags, cs) or_return
+
+ switch v in val.text {
+ case string:
+ write(cw, v, cs) or_return
+ }
+
+ switch v in val.color {
+ case Color_RGBA:
+ write(cw, v[3], cs) or_return
+ write(cw, v[2], cs) or_return
+ write(cw, v[1], cs) or_return
+ write(cw, v[0], cs) or_return
+ }
+
+ switch m in val.maps {
+ case Properties_Map:
+ if !ud_map_warn {
+ log.warn("Writing User Data Maps may still have bugs.")
+ ud_map_warn = true
+ }
+
+ mb: bytes.Buffer
+ defer bytes.buffer_destroy(&mb)
+ mw, ok4 := io.to_writer(bytes.buffer_to_stream(&mb))
+ if !ok4 {
+ return file_size, .Unable_Make_Writer
+ }
+ map_size: int
+ ms := &map_size
+ write(mw, DWORD(len(m)), ms) or_return
+
+ for key, val in m {
+ write(mw, key, ms) or_return
+ val := val.(Properties)
+ write(mw, DWORD(len(val)), ms)
+ for name, type in val {
+ _, err = write(mw, name, ms)
+ if err != nil {
+ log.error("Failed to write key", key)
+ return
+ }
+ write(mw, get_property_type(type) or_return, ms)
+ write(mw, type, ms) or_return
+ }
+ }
+
+ map_size += 4
+ write(cw, DWORD(map_size), cs) or_return
+ write(cw, mb.buf[:map_size-4], cs) or_return
+ }
+
+ case Slice_Chunk:
+ write(cw, DWORD(len(val.keys)), cs) or_return
+
+ flags := val.flags
+ if len(val.keys) != 0 {
+ key := val.keys[0]
+ if key.center != nil {
+ flags += {.Patched_slice}
+ }
+ if key.pivot != nil {
+ flags += {.Pivot_Information}
+ }
+ }
+ write(cw, transmute(DWORD)flags, cs) or_return
+
+ write(cw, DWORD(0), cs) or_return
+ write(cw, val.name, cs) or_return
+
+ for key in val.keys {
+ write(cw, key.frame_num, cs) or_return
+ write(cw, key.x, cs) or_return
+ write(cw, key.y, cs) or_return
+ write(cw, key.width, cs) or_return
+ write(cw, key.height, cs) or_return
+
+ #partial switch v in key.center {
+ case Slice_Center:
+ write(cw, v.x, cs) or_return
+ write(cw, v.y, cs) or_return
+ write(cw, v.width, cs) or_return
+ write(cw, v.height, cs) or_return
+ }
+
+ #partial switch v in key.pivot {
+ case Slice_Pivot:
+ write(cw, v.x, cs) or_return
+ write(cw, v.y, cs) or_return
+ }
+ }
+
+ case Tileset_Chunk:
+ write(cw, val.id, cs) or_return
+
+ flags := val.flags
+ if val.compressed != nil {
+ flags += {.Include_Tiles_Inside_This_File}
+ }
+ if val.external != nil {
+ flags += {.Include_Link_To_External_File}
+ }
+ write(cw, transmute(DWORD)flags, cs) or_return
+
+ write(cw, val.num_of_tiles, cs) or_return
+ write(cw, val.width, cs) or_return
+ write(cw, val.height, cs) or_return
+ write(cw, val.base_index, cs) or_return
+ write_skip(cw, 14, cs) or_return
+ write(cw, val.name, cs) or_return
+
+ #partial switch v in val.external {
+ case Tileset_External:
+ write(cw, v.file_id, cs) or_return
+ write(cw, v.tileset_id, cs) or_return
+ }
+
+ #partial switch v in val.compressed {
+ case Tileset_Compressed:
+ com_buf := make([]byte, len(v), allocator) or_return
+ defer delete(com_buf)
+ data_rd: [^]u8 = raw_data(v[:])
+ com_buf_rd: [^]u8 = raw_data(com_buf[:])
+
+ config := zlib.z_stream {
+ avail_in = zlib.uInt(len(v)),
+ next_in = &data_rd[0],
+ avail_out = zlib.uInt(len(v)),
+ next_out = &com_buf_rd[0],
+ }
+
+ en := zlib.deflateInit(&config, zlib.DEFAULT_COMPRESSION)
+ if en < zlib.OK {
+ return file_size, ZLIB_Errors(en)
+ }
+
+ en = zlib.deflate(&config, zlib.FINISH)
+ if en < zlib.OK {
+ return file_size, ZLIB_Errors(en)
+ }
+
+ en = zlib.deflateEnd(&config)
+ if en < zlib.OK {
+ return file_size, ZLIB_Errors(en)
+ }
+
+ write(cw, DWORD(config.total_out), cs) or_return
+ write(cw, com_buf[:int(config.total_out)], cs) or_return
+ }
+
+ case:
+ return file_size, .Invalid_Chunk_Type
+ }
+
+ write(fw, DWORD(chunk_size + 4), fs) or_return
+ write(fw, cb.buf[:chunk_size], fs) or_return
+ }
+
+ write(w, DWORD(frame_size + 4), s) or_return
+ write(w, fb.buf[:frame_size], s) or_return
+ }
+
+ written: int
+ file_size += 4
+ write(ww, DWORD(file_size), &written) or_return
+ write(ww, b.buf[:file_size-4], &written) or_return
+
+ if written != file_size {
+ return file_size, Marshal_Errors.Wrong_Write_Size
+ }
+ return
+} \ No newline at end of file