From ee38b71c3bba93b25d51f094e00bbefb3f84ded5 Mon Sep 17 00:00:00 2001 From: Rachel Date: Fri, 27 Dec 2024 00:06:09 -0800 Subject: [PATCH] feat: Implement narc_strerror, narc_load --- .clang-format | 1 + .gitignore | 3 ++ include/narc.h | 71 +++++++++++++++++++++++++++++++++++++++++++++ src/cli.c | 50 +++++++++++++++++++++++++++++++ src/narc_load.c | 54 ++++++++++++++++++++++++++++++++++ src/narc_strerror.c | 22 ++++++++++++++ 6 files changed, 201 insertions(+) create mode 100644 include/narc.h create mode 100644 src/cli.c create mode 100644 src/narc_load.c create mode 100644 src/narc_strerror.c diff --git a/.clang-format b/.clang-format index edd1b22..e85ef43 100644 --- a/.clang-format +++ b/.clang-format @@ -1,6 +1,7 @@ --- BasedOnStyle: LLVM Language: Cpp +AlignConsecutiveMacros: Consecutive AlignOperands: false BraceWrapping: AfterCaseLabel: false diff --git a/.gitignore b/.gitignore index 2734f58..e2ba98d 100644 --- a/.gitignore +++ b/.gitignore @@ -13,3 +13,6 @@ compile_commands.json # Valgrind output valgrind.log + +# Testing files +*.narc diff --git a/include/narc.h b/include/narc.h new file mode 100644 index 0000000..48cfb13 --- /dev/null +++ b/include/narc.h @@ -0,0 +1,71 @@ +/* + * Copyright 2024 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef NARC_H +#define NARC_H + +#include + +enum narc_error { + NARCERR_NONE = 0, + + NARCERR_MAGIC, + NARCERR_BOM, + NARCERR_VERSION, + NARCERR_HEADER_SIZE, + NARCERR_NUM_SECTIONS, + + NARCERR_ERRNO = 0xFF, +}; + +struct narc { + uint32_t magic; + uint16_t bom; + uint16_t version; + uint32_t size; + uint16_t header_size; + uint16_t num_sections; + unsigned char data[]; +}; + +/* + * Convert a `narc_error` value into a corresponding human-readable message. + */ +const char *narc_strerror(enum narc_error error); + +/* + * Load a NARC from an existing file at the given path. If the file contents + * validate as a NARC, then `out_narc` will be set to an address with sufficient + * memory allocation to hold the entire file. The calling client is responsible + * for freeing this allocation. + * + * Any of the following error codes may be emitted: + * + * - `NARCERR_NONE` - No error; `narc` contains good data. + * - `NARCERR_MAGIC` - The magic ID in the file's header did not match + * expectations. + * - `NARCERR_BOM` - The byte-order marker in the file's header did + * not match expectations. + * - `NARCERR_VERSION` - The version marker in the file's header did not + * match expectations. + * - `NARCERR_HEADER_SIZE` - The file's header reports an invalid size for + * itself. + * - `NARCERR_NUM_SECTIONS` - The number of sections expected by the file's + * header did not match expectations. + * - `NARCERR_ERRNO` - A system-level error occurred; consult `errno`. + */ +enum narc_error narc_load(const char *file_path, struct narc **out_narc); + +#endif // NARC_H diff --git a/src/cli.c b/src/cli.c new file mode 100644 index 0000000..f75604e --- /dev/null +++ b/src/cli.c @@ -0,0 +1,50 @@ +/* + * Copyright 2024 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include +#include +#include +#include + +#include "narc.h" + +int main(void) +{ + struct narc *narc = NULL; + enum narc_error err = narc_load("test.narc", &narc); + if (err == NARCERR_ERRNO) { + fprintf(stderr, "System error: %s\n", strerror(errno)); + fflush(stderr); + return EXIT_FAILURE; + } else if (err != NARCERR_NONE) { + fprintf(stderr, "NARC error: %s\n", narc_strerror(err)); + fflush(stderr); + return EXIT_FAILURE; + } + + printf("Loaded NARC successfully!\n"); + printf("NARC size: %d\n", narc->size); + printf("NARC data sample:"); + for (int i = 0; i < 16; i++) { + printf(" %02x", narc->data[i]); + } + printf("\n"); + + free(narc); + return EXIT_SUCCESS; +} diff --git a/src/narc_load.c b/src/narc_load.c new file mode 100644 index 0000000..5732d04 --- /dev/null +++ b/src/narc_load.c @@ -0,0 +1,54 @@ +#include "narc.h" + +#include +#include +#include +#include + +#define NARC_MAGIC 0x4352414E +#define LE_BOM 0xFFFE +#define VERSION_MARKER 0x0100 +#define HEADER_SIZE 16 +#define NUM_SECTIONS 3 + +#define ERROR_NEQ(expect, actual, err, file) \ + { \ + if ((expect) != (actual)) { \ + fclose(file); \ + return err; \ + } \ + } + +enum narc_error narc_load(const char *file_path, struct narc **out_narc) +{ + FILE *f = fopen(file_path, "rb"); + if (f == NULL) { + return NARCERR_ERRNO; + } + + fseek(f, 0, SEEK_END); + size_t fsize = ftell(f); + fseek(f, 0, SEEK_SET); + + // Validate the header. + struct narc header; + fread(&header, sizeof(char), HEADER_SIZE, f); + + ERROR_NEQ(NARC_MAGIC, header.magic, NARCERR_MAGIC, f); + ERROR_NEQ(LE_BOM, header.bom, NARCERR_BOM, f); + ERROR_NEQ(VERSION_MARKER, header.version, NARCERR_VERSION, f); + ERROR_NEQ(HEADER_SIZE, header.header_size, NARCERR_HEADER_SIZE, f); + ERROR_NEQ(NUM_SECTIONS, header.num_sections, NARCERR_NUM_SECTIONS, f); + + // Header is good; allocate enough memory to hold the whole file. + *out_narc = malloc(fsize); + if (*out_narc == NULL) { + fclose(f); + return NARCERR_ERRNO; + } + + memcpy(*out_narc, &header, sizeof(header)); + fread((*out_narc)->data, sizeof(char), fsize - sizeof(header), f); + fclose(f); + return NARCERR_NONE; +} diff --git a/src/narc_strerror.c b/src/narc_strerror.c new file mode 100644 index 0000000..7217c09 --- /dev/null +++ b/src/narc_strerror.c @@ -0,0 +1,22 @@ +#include "narc.h" + +const char *narc_strerror(enum narc_error error) +{ + switch (error) { + default: + case NARCERR_NONE: + return "(null)"; + case NARCERR_MAGIC: + return "Invalid magic marker in NARC header"; + case NARCERR_BOM: + return "Invalid byte-order marker in NARC header"; + case NARCERR_VERSION: + return "Invalid version marker in NARC header"; + case NARCERR_HEADER_SIZE: + return "Invalid NARC header size"; + case NARCERR_NUM_SECTIONS: + return "Invalid section count in NARC header"; + case NARCERR_ERRNO: + return "Standard error; refer to errno for details"; + } +}