diff --git a/src/chunks/chunks.odin b/src/chunks/chunks.odin index f26aeb5..e3f61ef 100644 --- a/src/chunks/chunks.odin +++ b/src/chunks/chunks.odin @@ -1,11 +1,8 @@ package chunks import "base:runtime" -import "core:fmt" import "core:io" import "core:os" -import "core:strconv" -import "core:strings" MAGIC_STRING :: 0x514D21 // SINDRI VERSION :: 1 @@ -38,64 +35,10 @@ Asset_Table :: struct { asset_sizes: [dynamic]u64, } -Writer :: struct { - chunk_file: ^os.File, - asset_file: ^os.File, - asset_table: Asset_Table, -} - Error :: union #shared_nil { runtime.Allocator_Error, io.Error, os.Error, Read_Error, -} - -// ----------------------------------------- - -writer_init :: proc( - number_of_assets: u64, - allocator := context.allocator, -) -> ( - w: Writer, - err: runtime.Allocator_Error, -) #optional_allocator_error { - // [ Header ][ []Asset Index ][ Data ] - w.asset_table.asset_index_offset = size_of(Header) - w.asset_table.data_offset = - size_of(Header) + (size_of(Asset_Index) * number_of_assets) - w.asset_table.asset_offsets = make([dynamic]u64, allocator) or_return - w.asset_table.asset_sizes = make([dynamic]u64, allocator) or_return - resize(&w.asset_table.asset_offsets, number_of_assets) - resize(&w.asset_table.asset_sizes, number_of_assets) - return w, nil -} - -writer_destroy :: proc(w: ^Writer) { - if w.chunk_file != nil do os.close(w.chunk_file) - if w.asset_file != nil do os.close(w.asset_file) - delete(w.asset_table.asset_offsets) - delete(w.asset_table.asset_sizes) -} - -@(private) -compute_chunk_path :: proc( - output_path: string, - chunk_index: int, - allocator: runtime.Allocator, -) -> string { - - @(static) buf: [20]u8 - - chunk_index_str := strconv.write_int(buf[:], cast(i64)chunk_index, 10) - chunk_name := strings.concatenate({CHUNK, chunk_index_str}, allocator) - defer delete(chunk_name, allocator) - - chunk_path, c_err := os.join_path({output_path, chunk_name}, allocator) - if c_err != nil { - fmt.eprintln("error: chunk join path failed:", c_err) - os.exit(1) - } - - return chunk_path // owning string + Write_Error, } diff --git a/src/chunks/read_assets.odin b/src/chunks/read_assets.odin index 8be3bc1..7b8fae9 100644 --- a/src/chunks/read_assets.odin +++ b/src/chunks/read_assets.odin @@ -63,7 +63,7 @@ read_file :: proc( output_path, cast(int)chunk_index, allocator, - ) + ) or_return defer delete(chunk_path, allocator) // Open chunk diff --git a/src/chunks/read_metadata.odin b/src/chunks/read_metadata.odin index 82537fc..ae628d1 100644 --- a/src/chunks/read_metadata.odin +++ b/src/chunks/read_metadata.odin @@ -25,7 +25,7 @@ read_header :: proc( } if r.header.compression != 0 { - return .Unimplemented + return Read_Error.Unimplemented } return nil diff --git a/src/chunks/reader.odin b/src/chunks/reader.odin index 009e566..fdc97fd 100644 --- a/src/chunks/reader.odin +++ b/src/chunks/reader.odin @@ -31,7 +31,7 @@ reader_init :: proc( output_path := os.name(output) // Chunk path - chunk_path := compute_chunk_path(output_path, 0, allocator) + chunk_path := compute_chunk_path(output_path, 0, allocator) or_return defer delete(chunk_path, allocator) // Chunk open diff --git a/src/chunks/write_assets.odin b/src/chunks/write_assets.odin index 51f2c81..dc5915a 100644 --- a/src/chunks/write_assets.odin +++ b/src/chunks/write_assets.odin @@ -16,7 +16,7 @@ write_assets :: proc( size_per_chunk: u64 = SIZE_PER_CHUNK, compression: u16 = COMPRESSION, allocator := context.allocator, -) { +) -> Error { output_path := os.name(output) chunk_index := 0 asset_index := 0 @@ -30,18 +30,28 @@ write_assets :: proc( "error: chunk size too small to hold asset index:", w.asset_table.data_offset, ) - os.exit(1) + return .Chunk_Size_Too_Small } // ---------------------------------------- // Create new chunk - chunk_stream := create_new_chunk(w, output_path, chunk_index, allocator) + chunk_stream := create_new_chunk( + w, + output_path, + chunk_index, + allocator, + ) or_return // Edge case of the chunk size being exactly the size of the data offset if size_per_chunk == w.asset_table.data_offset { chunk_index += 1 - chunk_stream = create_new_chunk(w, output_path, chunk_index, allocator) + chunk_stream = create_new_chunk( + w, + output_path, + chunk_index, + allocator, + ) or_return } // Chunk 0 starts after metadata @@ -52,7 +62,10 @@ write_assets :: proc( // ---------------------------------------- // Open asset - asset_stream, asset_size := open_asset(w, entries[asset_index].relpath) + asset_stream, asset_size := open_asset( + w, + entries[asset_index].relpath, + ) or_return w.asset_table.asset_offsets[asset_index] = total_offset w.asset_table.asset_sizes[asset_index] = asset_size @@ -79,7 +92,7 @@ write_assets :: proc( asset_stream, asset_size = open_asset( w, entries[asset_index].relpath, - ) + ) or_return w.asset_table.asset_offsets[asset_index] = total_offset w.asset_table.asset_sizes[asset_index] = asset_size @@ -96,7 +109,7 @@ write_assets :: proc( asset_stream, asset_size = open_asset( w, entries[asset_index].relpath, - ) + ) or_return w.asset_table.asset_offsets[asset_index] = total_offset w.asset_table.asset_sizes[asset_index] = asset_size @@ -108,7 +121,7 @@ write_assets :: proc( output_path, chunk_index, allocator, - ) + ) or_return } else { // spillover to next chunk write_chunk(chunk_stream, asset_stream, chunk_remaining) asset_offset += chunk_remaining @@ -122,9 +135,11 @@ write_assets :: proc( output_path, chunk_index, allocator, - ) + ) or_return } } + + return nil } @(private) @@ -133,15 +148,22 @@ create_new_chunk :: proc( output_path: string, chunk_index: int, allocator: runtime.Allocator, -) -> io.Stream { +) -> ( + chunk_stream: io.Stream, + err: Error, +) { // Remove old chunk - chunk_path := compute_chunk_path(output_path, chunk_index, allocator) + chunk_path := compute_chunk_path( + output_path, + chunk_index, + allocator, + ) or_return defer delete(chunk_path, allocator) if os.exists(chunk_path) { rm_err := os.remove(chunk_path) if rm_err != nil { fmt.eprintln("error: remove old chunk failed:", rm_err) - os.exit(1) + return {}, rm_err } } @@ -152,23 +174,23 @@ create_new_chunk :: proc( w.chunk_file, cf_err = os.create(chunk_path) if cf_err != nil { fmt.eprintln("error: create new chunk failed:", cf_err) - os.exit(1) + return {}, cf_err } - chunk_stream := os.to_stream(w.chunk_file) + chunk_stream = os.to_stream(w.chunk_file) - return chunk_stream + return chunk_stream, nil } @(private) -open_asset :: proc(w: ^Writer, path: string) -> (io.Stream, u64) { +open_asset :: proc(w: ^Writer, path: string) -> (io.Stream, u64, Error) { if w.asset_file != nil do os.close(w.asset_file) // close previous // Open asset asset, open_err := os.open(path, {.Read}) if open_err != nil { fmt.eprintln("error: asset open failed:", open_err) - os.exit(1) + return {}, {}, open_err } // Asset size @@ -176,17 +198,19 @@ open_asset :: proc(w: ^Writer, path: string) -> (io.Stream, u64) { asset_size, size_err := io.size(asset_stream) if size_err != nil { fmt.eprintln("error: asset size failed:", size_err) - os.exit(1) + return {}, {}, size_err } - return asset_stream, cast(u64)asset_size + return asset_stream, cast(u64)asset_size, nil } @(private) -write_chunk :: proc(dst: io.Stream, src: io.Stream, n: u64) { +write_chunk :: proc(dst: io.Stream, src: io.Stream, n: u64) -> Error { w, err := io.copy_n(dst, src, cast(i64)n) if err != nil { fmt.eprintln("error: chunk write error:", err) - os.exit(1) + return err } + + return nil } diff --git a/src/chunks/write_metadata.odin b/src/chunks/write_metadata.odin index 642a269..3346b86 100644 --- a/src/chunks/write_metadata.odin +++ b/src/chunks/write_metadata.odin @@ -16,24 +16,26 @@ write_metadata :: proc( size_per_chunk: u64 = SIZE_PER_CHUNK, compression: u16 = COMPRESSION, allocator := context.allocator, -) { +) -> Error { output_path := os.name(output) number_of_assets := cast(u32)len(entries) // ---------------------------------------- - chunk_path := compute_chunk_path(output_path, 0, allocator) + chunk_path := compute_chunk_path(output_path, 0, allocator) or_return defer delete(chunk_path, allocator) chunk_file, err := os.open(chunk_path, {.Read, .Write}) if err != nil { fmt.eprintln("error: open chunk failed: ", err) - os.exit(1) + return err } defer os.close(chunk_file) write_header(w, chunk_file, number_of_assets, size_per_chunk, compression) write_asset_index(w, chunk_file, entries, number_of_assets, allocator) + + return nil } @(private) @@ -43,7 +45,7 @@ write_header :: proc( number_of_assets: u32, size_per_chunk: u64 = SIZE_PER_CHUNK, compression: u16 = COMPRESSION, -) { +) -> Error { last_offset := w.asset_table.asset_offsets[len(w.asset_table.asset_offsets) - 1] last_size := w.asset_table.asset_sizes[len(w.asset_table.asset_sizes) - 1] @@ -63,8 +65,10 @@ write_header :: proc( n, err := os.write_at(chunk_file, header_bytes, 0) if err != nil { fmt.eprintln("error: chunk write error:", err) - os.exit(1) + return err } + + return nil } @(private) @@ -74,7 +78,7 @@ write_asset_index :: proc( entries: []file.File_Entry, number_of_assets: u32, allocator: runtime.Allocator, -) { +) -> Error { index_size: u64 = size_of(Asset_Index) * cast(u64)number_of_assets // ---------------------------------------- @@ -88,7 +92,7 @@ write_asset_index :: proc( path_length := len(f.relpath) if path_length > 512 { fmt.eprintln("error: path exeeds maximum of 512:", f.relpath) - os.exit(1) + return .Path_Too_Large } offset_in_bytes := size_of(Asset_Index) * cast(u64)i @@ -110,6 +114,8 @@ write_asset_index :: proc( ) if err != nil { fmt.eprintln("error: chunk write error:", err) - os.exit(1) + return err } + + return nil } diff --git a/src/chunks/writer.odin b/src/chunks/writer.odin new file mode 100644 index 0000000..d4a1e4d --- /dev/null +++ b/src/chunks/writer.odin @@ -0,0 +1,75 @@ +package chunks + +import "base:runtime" +import "core:fmt" +import "core:os" +import "core:strconv" +import "core:strings" + +// ----------------------------------------- + +Writer :: struct { + chunk_file: ^os.File, + asset_file: ^os.File, + asset_table: Asset_Table, +} + +Write_Error :: enum u8 { + None = 0, + Chunk_Size_Too_Small = 1, + Path_Too_Large = 2, + Unimplemented = 127, + Okay = None, +} + +// ----------------------------------------- + +writer_init :: proc( + number_of_assets: u64, + allocator := context.allocator, +) -> ( + w: Writer, + err: runtime.Allocator_Error, +) #optional_allocator_error { + // [ Header ][ []Asset Index ][ Data ] + w.asset_table.asset_index_offset = size_of(Header) + w.asset_table.data_offset = + size_of(Header) + (size_of(Asset_Index) * number_of_assets) + w.asset_table.asset_offsets = make([dynamic]u64, allocator) or_return + w.asset_table.asset_sizes = make([dynamic]u64, allocator) or_return + resize(&w.asset_table.asset_offsets, number_of_assets) + resize(&w.asset_table.asset_sizes, number_of_assets) + return w, nil +} + +writer_destroy :: proc(w: ^Writer) { + if w.chunk_file != nil do os.close(w.chunk_file) + if w.asset_file != nil do os.close(w.asset_file) + delete(w.asset_table.asset_offsets) + delete(w.asset_table.asset_sizes) +} + +@(private) +compute_chunk_path :: proc( + output_path: string, + chunk_index: int, + allocator: runtime.Allocator, +) -> ( + string, + Error, +) { + + @(static) buf: [20]u8 + + chunk_index_str := strconv.write_int(buf[:], cast(i64)chunk_index, 10) + chunk_name := strings.concatenate({CHUNK, chunk_index_str}, allocator) + defer delete(chunk_name, allocator) + + chunk_path, c_err := os.join_path({output_path, chunk_name}, allocator) + if c_err != nil { + fmt.eprintln("error: chunk join path failed:", c_err) + return {}, c_err + } + + return chunk_path, nil // owning string +} diff --git a/src/main.odin b/src/main.odin index b61d0c7..ae263b3 100644 --- a/src/main.odin +++ b/src/main.odin @@ -1,6 +1,5 @@ package main -import "core:fmt" import "core:os" import "src:chunks" @@ -25,13 +24,14 @@ main :: proc() { w := chunks.writer_init(cast(u64)len(entries)) defer chunks.writer_destroy(&w) - chunks.write_assets( + err := chunks.write_assets( &w, opts.output, entries[:], opts.size, opts.compression, ) + if err != nil do os.exit(1) chunks.write_metadata( &w, @@ -40,6 +40,7 @@ main :: proc() { opts.size, opts.compression, ) + if err != nil do os.exit(2) } // TODO: .chunkignore