193 lines
4.6 KiB
Odin
193 lines
4.6 KiB
Odin
package chunks
|
|
|
|
import "base:runtime"
|
|
import "core:fmt"
|
|
import "core:io"
|
|
import "core:os"
|
|
|
|
import "src:file"
|
|
|
|
// -----------------------------------------
|
|
|
|
write_assets :: proc(
|
|
w: ^Writer,
|
|
output: ^os.File,
|
|
entries: []file.File_Entry,
|
|
size_per_chunk: u64 = SIZE_PER_CHUNK,
|
|
compression: u16 = COMPRESSION,
|
|
allocator := context.allocator,
|
|
) {
|
|
output_path := os.name(output)
|
|
chunk_index := 0
|
|
asset_index := 0
|
|
chunk_offset: u64 = 0
|
|
asset_offset: u64 = 0
|
|
total_offset: u64 = w.asset_table.data_offset
|
|
number_of_assets := len(entries)
|
|
|
|
if size_per_chunk < w.asset_table.data_offset {
|
|
fmt.eprintln(
|
|
"error: chunk size too small to hold asset index:",
|
|
w.asset_table.data_offset,
|
|
)
|
|
os.exit(1)
|
|
}
|
|
|
|
// ----------------------------------------
|
|
|
|
// Create new chunk
|
|
chunk_stream := create_new_chunk(w, output_path, chunk_index, allocator)
|
|
|
|
// 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 0 starts after metadata
|
|
if (chunk_index == 0) {
|
|
chunk_offset = w.asset_table.data_offset
|
|
}
|
|
|
|
// ----------------------------------------
|
|
|
|
// Open asset
|
|
asset_stream, asset_size := open_asset(w, entries[asset_index].relpath)
|
|
w.asset_table.asset_offsets[asset_index] = total_offset
|
|
w.asset_table.asset_sizes[asset_index] = asset_size
|
|
|
|
// ----------------------------------------
|
|
|
|
for {
|
|
chunk_remaining := size_per_chunk - chunk_offset
|
|
asset_remaining := asset_size - asset_offset
|
|
|
|
io.seek(chunk_stream, cast(i64)chunk_offset, .Start)
|
|
io.seek(asset_stream, cast(i64)asset_offset, .Start)
|
|
|
|
if asset_remaining < chunk_remaining {
|
|
|
|
// Write asset to chunk
|
|
write_chunk(chunk_stream, asset_stream, asset_remaining)
|
|
chunk_offset += asset_remaining
|
|
total_offset += asset_remaining
|
|
|
|
// Open next asset
|
|
asset_index += 1
|
|
asset_offset = 0
|
|
if asset_index == number_of_assets do break
|
|
asset_stream, asset_size = open_asset(
|
|
w,
|
|
entries[asset_index].relpath,
|
|
)
|
|
w.asset_table.asset_offsets[asset_index] = total_offset
|
|
w.asset_table.asset_sizes[asset_index] = asset_size
|
|
|
|
} else if asset_remaining == chunk_remaining {
|
|
|
|
// Write asset to chunk
|
|
write_chunk(chunk_stream, asset_stream, asset_remaining)
|
|
total_offset += asset_remaining
|
|
|
|
// Open next asset
|
|
asset_index += 1
|
|
asset_offset = 0
|
|
if asset_index == number_of_assets do break
|
|
asset_stream, asset_size = open_asset(
|
|
w,
|
|
entries[asset_index].relpath,
|
|
)
|
|
w.asset_table.asset_offsets[asset_index] = total_offset
|
|
w.asset_table.asset_sizes[asset_index] = asset_size
|
|
|
|
// Create new chunk
|
|
chunk_index += 1
|
|
chunk_offset = 0
|
|
chunk_stream = create_new_chunk(
|
|
w,
|
|
output_path,
|
|
chunk_index,
|
|
allocator,
|
|
)
|
|
} else { // spillover to next chunk
|
|
write_chunk(chunk_stream, asset_stream, chunk_remaining)
|
|
asset_offset += chunk_remaining
|
|
total_offset += chunk_remaining
|
|
|
|
// Create new chunk
|
|
chunk_index += 1
|
|
chunk_offset = 0
|
|
chunk_stream = create_new_chunk(
|
|
w,
|
|
output_path,
|
|
chunk_index,
|
|
allocator,
|
|
)
|
|
}
|
|
}
|
|
}
|
|
|
|
@(private)
|
|
create_new_chunk :: proc(
|
|
w: ^Writer,
|
|
output_path: string,
|
|
chunk_index: int,
|
|
allocator: runtime.Allocator,
|
|
) -> io.Stream {
|
|
// Remove old chunk
|
|
chunk_path := compute_chunk_path(output_path, chunk_index, allocator)
|
|
defer delete(chunk_path)
|
|
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)
|
|
}
|
|
}
|
|
|
|
if w.chunk_file != nil do os.close(w.chunk_file) // close previous
|
|
|
|
// Create new chunk
|
|
cf_err: os.Error
|
|
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)
|
|
}
|
|
|
|
chunk_stream := os.to_stream(w.chunk_file)
|
|
|
|
return chunk_stream
|
|
}
|
|
|
|
@(private)
|
|
open_asset :: proc(w: ^Writer, path: string) -> (io.Stream, u64) {
|
|
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)
|
|
}
|
|
|
|
// Asset size
|
|
asset_stream := os.to_stream(asset)
|
|
asset_size, size_err := io.size(asset_stream)
|
|
if size_err != nil {
|
|
fmt.eprintln("error: asset size failed:", size_err)
|
|
os.exit(1)
|
|
}
|
|
|
|
return asset_stream, cast(u64)asset_size
|
|
}
|
|
|
|
@(private)
|
|
write_chunk :: proc(dst: io.Stream, src: io.Stream, n: u64) {
|
|
w, err := io.copy_n(dst, src, cast(i64)n)
|
|
if err != nil {
|
|
fmt.eprintln("error: chunk write error:", err)
|
|
os.exit(1)
|
|
}
|
|
}
|