diff --git a/CMakeLists.txt b/CMakeLists.txt index e5ea313..98aa296 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -33,14 +33,14 @@ target_compile_options(allcing_config INTERFACE # ---- library header add_library(allcing INTERFACE) target_sources(allcing INTERFACE allcing.h) -target_link_libraries(allcing INTERFACE allcing_config) +target_include_directories(allcing SYSTEM INTERFACE .) # ---- test binary if (BUILD_TESTING) add_executable(allcing_test) target_sources(allcing_test PRIVATE test.c) - target_link_libraries(allcing_test PRIVATE allcing) + target_link_libraries(allcing_test PRIVATE allcing allcing_config) add_test(NAME allcing_test COMMAND $) endif() @@ -48,4 +48,8 @@ endif() # ---- example binary add_executable(allcing_example EXCLUDE_FROM_ALL) target_sources(allcing_example PRIVATE example.c) -target_link_libraries(allcing_example PRIVATE allcing) +target_link_libraries(allcing_example PRIVATE allcing allcing_config) + + +# ---- tool subdirs +add_subdirectory(tool EXCLUDE_FROM_ALL) diff --git a/example.c b/example.c index b29934f..410444f 100644 --- a/example.c +++ b/example.c @@ -6,10 +6,12 @@ // ---- +#define ACG_NO_READ_FUNCTIONS #include "allcing.h" -static inline bool acg_flush(struct acg_stream* s) { +static inline bool acg_stream_flush(struct acg_stream* s) { assert(NULL != s); + acg_stream_write_chunk_pad(s); if (s->cursor_bits < 8U) { return false; } @@ -21,11 +23,19 @@ static inline bool acg_flush(struct acg_stream* s) { abort(); } } + if (1U != fwrite(s->buffer, s->cursor_bits/8U, 1U, *fp)) { return false; } - memset(s->buffer, 0U, (s->cursor_bits+7U)/8U); - s->cursor_bits = 0; + + uint8_t frag = 0U; + if (s->cursor_bits%8U > 0U) { + frag = s->buffer[s->cursor_bits/8U]; + } + + memset(s->buffer, 0U, s->size_bytes); + s->buffer[0] = frag; + s->cursor_bits %= 8U; if (0U != fflush(*fp)) { return false; @@ -35,7 +45,7 @@ static inline bool acg_flush(struct acg_stream* s) { #undef ACG_LOC_FULL #define ACG_LOC_FULL ACG_LOC_SHORT #undef ACG_FLUSH -#define ACG_FLUSH(s) acg_flush((s)) +#define ACG_FLUSH(s) acg_stream_flush((s)) struct acg_stream logstream = { .buffer = (uint8_t[1024U]) {0U}, diff --git a/tool/CMakeLists.txt b/tool/CMakeLists.txt new file mode 100644 index 0000000..31201b0 --- /dev/null +++ b/tool/CMakeLists.txt @@ -0,0 +1,4 @@ +# ---- allcing_unpack: converts binary format to human readable text +add_executable(allcing_unpack) +target_sources(allcing_unpack PRIVATE unpack.c) +target_link_libraries(allcing_unpack PRIVATE allcing allcing_config) diff --git a/tool/unpack.c b/tool/unpack.c new file mode 100644 index 0000000..052d3ff --- /dev/null +++ b/tool/unpack.c @@ -0,0 +1,135 @@ +// No copyright +#include +#include +#include + +#include + + +#define READ_CHUNK_SIZE 1024U + +static inline bool acg_stream_refill(struct acg_stream* s) { + assert(NULL != s); + + FILE* fp = s->udata; + assert(NULL != fp); + + uint8_t* const buf = s->buffer; + uint64_t* const c = &s->cursor_bits; + uint64_t* const size = &s->size_bytes; + + const uint64_t remain = *size - *c/8U; + memmove(buf, &buf[*c/8U], remain); + *c %= 8U; + + const uint64_t expect_size = READ_CHUNK_SIZE - remain; + const uint64_t actual_size = fread(&buf[remain], 1U, expect_size, fp); + + *size = remain + actual_size; + return true; +} + +int main(int argc, char** argv) { + if (argc != 2U) { + fprintf(stderr, "invalid args\n"); + return EXIT_FAILURE; + } + + FILE* fp = fopen(argv[1], "rb"); + if (NULL == fp) { + fprintf(stderr, "failed to open: %s\n", argv[1]); + return EXIT_FAILURE; + } + struct acg_stream s = { + .buffer = (uint8_t[READ_CHUNK_SIZE]) {0U}, + .udata = fp, + }; + + uint64_t last_event_chunk_id = 0U; + uint32_t indent = 0U; +# define INDENT_PRINT(FMT, ...) \ + printf("%*s" FMT "\n", indent, "" __VA_OPT__(,) __VA_ARGS__) + + while (true) { + if (!acg_stream_refill(&s)) { + fprintf(stderr, "failed to read: %s\n", argv[1]); + return EXIT_FAILURE; + } + if (0U == s.size_bytes) { + break; + } + + uint64_t chunk_id; + if (!acg_stream_read_uint(&s, &chunk_id, ACG_UNITBITS_CHUNK_ID)) { + fprintf(stderr, "failed to read chunk id\n"); + return EXIT_FAILURE; + } + + const bool is_event_chunk = chunk_id >= ACG_CHUNK_BEGIN; + if (is_event_chunk) { + if (ACG_CHUNK_BEGIN == last_event_chunk_id) { + ++indent; + } else if (ACG_CHUNK_END == last_event_chunk_id) { + --indent; + } + last_event_chunk_id = chunk_id; + } + + switch (chunk_id) { + case ACG_CHUNK_PAD: + acg_stream_read_pad(&s); + break; + case ACG_CHUNK_BLOB: { + uint64_t size; + uint8_t* buf; + if (!acg_stream_read_blob(&s, &size, &buf)) { + fprintf(stderr, "failed to read BLOB in BLOB chunk\n"); + return EXIT_FAILURE; + } + INDENT_PRINT("-BLOB: %.*s", (int) size, (const char*) buf); + } break; + case ACG_CHUNK_CTX: { + uint64_t time; + if (!acg_stream_read_uint(&s, &time, 64U)) { + fprintf(stderr, "failed to read TIME in CTX chunk\n"); + return EXIT_FAILURE; + } + uint64_t tid; + if (!acg_stream_read_uint(&s, &tid, 16U)) { + fprintf(stderr, "failed to read TID in CTX chunk\n"); + return EXIT_FAILURE; + } + INDENT_PRINT("-CTX: TIME=%" PRIu64 " / TID=%" PRIx64, time, tid); + } break; + case ACG_CHUNK_LOC: { + uint64_t size; + uint8_t* buf; + if (!acg_stream_read_blob(&s, &size, &buf)) { + fprintf(stderr, "failed to read BLOB in BLOB chunk\n"); + return EXIT_FAILURE; + } + INDENT_PRINT("-LOC: %.*s", (int) size, (const char*) buf); + } break; + + case ACG_CHUNK_BEGIN: + INDENT_PRINT("BEGIN:"); + break; + case ACG_CHUNK_END: + if (0U == indent) { + fprintf(stderr, "too many END chunk\n"); + return EXIT_FAILURE; + } + INDENT_PRINT("END:"); + break; + case ACG_CHUNK_CHECK: + INDENT_PRINT("CHECK:"); + break; + + default: + fprintf(stderr, "unknown chunk id: %" PRIu64 "\n", chunk_id); + return EXIT_FAILURE; + } + } + fclose(fp); + return EXIT_SUCCESS; +}