Add utils for working with endianness, more documentation, refactor CMake
Decided to update the docs, as well as adding few new features and small refactoring.
This commit is contained in:
2
.gitignore
vendored
2
.gitignore
vendored
@@ -66,7 +66,7 @@ _deps
|
|||||||
.vscode/
|
.vscode/
|
||||||
|
|
||||||
# Doxygen
|
# Doxygen
|
||||||
[Dd]ocs/[Hh]tml
|
[Dd]oc/[Gg]enerated
|
||||||
|
|
||||||
# Coverage
|
# Coverage
|
||||||
[Cc]overage
|
[Cc]overage
|
||||||
|
|||||||
@@ -47,22 +47,25 @@ include(CheckIncludeFile)
|
|||||||
include(CheckSymbolExists)
|
include(CheckSymbolExists)
|
||||||
|
|
||||||
# Unit testing and coverage configuration
|
# Unit testing and coverage configuration
|
||||||
set(TESTING ON CACHE BOOL "Enable unit-testing")
|
set(ENABLE_TESTING ON CACHE BOOL "Enable unit-testing")
|
||||||
set(COVERAGE OFF CACHE BOOL "Enable coverage")
|
set(ENABLE_COVERAGE OFF CACHE BOOL "Enable coverage")
|
||||||
|
set(ENABLE_EXAMPLES ON CACHE BOOL "Enable building examples")
|
||||||
|
set(ENABLE_LTO ON CACHE BOOL "Enable LTO support")
|
||||||
|
|
||||||
# Check for IPO/LTO
|
# Enable IPO/LTO
|
||||||
check_ipo_supported(RESULT LTO_SUPPORTED)
|
if(ENABLE_LTO)
|
||||||
|
check_ipo_supported(RESULT LTO_SUPPORTED)
|
||||||
if(LTO_SUPPORTED)
|
if(LTO_SUPPORTED)
|
||||||
message(STATUS "IPO/LTO enabled")
|
message(STATUS "IPO/LTO enabled")
|
||||||
set(CMAKE_INTERPROCEDURAL_OPTIMIZATION TRUE)
|
set(CMAKE_INTERPROCEDURAL_OPTIMIZATION TRUE)
|
||||||
|
endif()
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if(TESTING)
|
# Enable testing
|
||||||
# Enable testing
|
if(ENABLE_TESTING)
|
||||||
include(CTest)
|
include(CTest)
|
||||||
enable_testing()
|
enable_testing()
|
||||||
endif(TESTING)
|
endif()
|
||||||
|
|
||||||
# Set library code
|
# Set library code
|
||||||
set(BH_SOURCE
|
set(BH_SOURCE
|
||||||
@@ -87,6 +90,7 @@ set(BH_SOURCE
|
|||||||
src/Math/Vec4f.c
|
src/Math/Vec4f.c
|
||||||
src/Math/Vec4i.c
|
src/Math/Vec4i.c
|
||||||
src/Queue.c
|
src/Queue.c
|
||||||
|
src/Util.c
|
||||||
)
|
)
|
||||||
|
|
||||||
set(BH_HEADER
|
set(BH_HEADER
|
||||||
@@ -97,6 +101,7 @@ set(BH_HEADER
|
|||||||
include/BH/IO.h
|
include/BH/IO.h
|
||||||
include/BH/Math.h
|
include/BH/Math.h
|
||||||
include/BH/Queue.h
|
include/BH/Queue.h
|
||||||
|
include/BH/Util.h
|
||||||
)
|
)
|
||||||
|
|
||||||
set(BH_INCLUDE_DIRS
|
set(BH_INCLUDE_DIRS
|
||||||
@@ -141,13 +146,17 @@ else()
|
|||||||
endif()
|
endif()
|
||||||
|
|
||||||
# Coverage
|
# Coverage
|
||||||
if(COVERAGE)
|
if(ENABLE_COVERAGE)
|
||||||
target_compile_options(BHLib PRIVATE -coverage)
|
target_compile_options(BHLib PRIVATE -coverage)
|
||||||
target_link_options(BHLib PRIVATE -coverage)
|
target_link_options(BHLib PRIVATE -coverage)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
# Tests
|
# Tests
|
||||||
if(TESTING)
|
if(ENABLE_TESTING)
|
||||||
add_subdirectory(unit)
|
add_subdirectory(unit)
|
||||||
add_subdirectory(test)
|
add_subdirectory(test)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
if(ENABLE_EXAMPLES)
|
||||||
|
add_subdirectory(doc/Examples)
|
||||||
|
endif()
|
||||||
12
README
12
README
@@ -1,12 +0,0 @@
|
|||||||
BlankHex Library
|
|
||||||
|
|
||||||
Library with a bunch of stuff.
|
|
||||||
|
|
||||||
Currently implemented:
|
|
||||||
- I/O device abstraction
|
|
||||||
- File access (Windows and POSIX)
|
|
||||||
- Data structures (hashmap, queue)
|
|
||||||
- Algorithms (swap, sort, partition, heap)
|
|
||||||
- Geometry math (2D/3D/4D vectors, matrices, quaternions)
|
|
||||||
- Unit tests
|
|
||||||
|
|
||||||
30
README.md
Normal file
30
README.md
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
# BHLib
|
||||||
|
|
||||||
|
Library with a bunch of stuff
|
||||||
|
|
||||||
|
## About
|
||||||
|
|
||||||
|
Here is a short list of implemented features:
|
||||||
|
|
||||||
|
- Abstraction over input/output
|
||||||
|
- Basic data structures and algorithms (hashmap, queue, heaps, partitions)
|
||||||
|
- Geomtric primitives (vectors, matrices, quaternions, rays, boxes)
|
||||||
|
|
||||||
|
For more information about currently implemented and planned features checkout
|
||||||
|
[this page](doc/Features.md)
|
||||||
|
|
||||||
|
## Docs
|
||||||
|
|
||||||
|
The documentation for the functions is provided in the form of Doxygen comments.
|
||||||
|
|
||||||
|
The [HowTo guides](doc/HowTo.md) are also available.
|
||||||
|
|
||||||
|
## License
|
||||||
|
|
||||||
|
BHLib is licensed under [0BSD License](https://opensource.org/license/0bsd).
|
||||||
|
This means that you can:
|
||||||
|
|
||||||
|
- Use this library in personal or commercial projects
|
||||||
|
- Freely redistribute library in either source or binary forms without
|
||||||
|
copyright notice
|
||||||
|
- Modify and relicense your version/fork of the library
|
||||||
2737
doc/Doxyfile
Normal file
2737
doc/Doxyfile
Normal file
File diff suppressed because it is too large
Load Diff
3
doc/Examples/CMakeLists.txt
Normal file
3
doc/Examples/CMakeLists.txt
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
# PakReader
|
||||||
|
add_executable(PakReader PakReader.c)
|
||||||
|
target_link_libraries(PakReader BHLib)
|
||||||
223
doc/Examples/PakReader.c
Normal file
223
doc/Examples/PakReader.c
Normal file
@@ -0,0 +1,223 @@
|
|||||||
|
#include <BH/Args.h>
|
||||||
|
#include <BH/IO.h>
|
||||||
|
#include <BH/Util.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
|
||||||
|
/* PAK header and entry structures and some constants */
|
||||||
|
#define HEADER_SIZE 12
|
||||||
|
#define ENTRY_SIZE 64
|
||||||
|
|
||||||
|
typedef struct PakHeader
|
||||||
|
{
|
||||||
|
char id[4];
|
||||||
|
uint32_t offset;
|
||||||
|
uint32_t size;
|
||||||
|
} PakHeader;
|
||||||
|
|
||||||
|
|
||||||
|
typedef struct PakEntry
|
||||||
|
{
|
||||||
|
char name[56];
|
||||||
|
uint32_t offset;
|
||||||
|
uint32_t size;
|
||||||
|
} PakEntry;
|
||||||
|
|
||||||
|
|
||||||
|
static int ParseHeader(BH_IO *io,
|
||||||
|
PakHeader *header)
|
||||||
|
{
|
||||||
|
char buffer[HEADER_SIZE];
|
||||||
|
size_t actual;
|
||||||
|
|
||||||
|
if (BH_IORead(io, buffer, HEADER_SIZE, &actual) || actual != HEADER_SIZE)
|
||||||
|
return BH_ERROR;
|
||||||
|
|
||||||
|
if (memcmp(buffer, "PACK", 4))
|
||||||
|
return BH_ERROR;
|
||||||
|
|
||||||
|
memcpy(header->id, buffer, 4);
|
||||||
|
header->offset = BH_Read32LEu(buffer + 4);
|
||||||
|
header->size = BH_Read32LEu(buffer + 8);
|
||||||
|
|
||||||
|
return BH_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static int ParseEntry(BH_IO *io,
|
||||||
|
PakEntry *entry)
|
||||||
|
{
|
||||||
|
char buffer[ENTRY_SIZE];
|
||||||
|
size_t actual;
|
||||||
|
|
||||||
|
if (BH_IORead(io, buffer, ENTRY_SIZE, &actual) || actual != ENTRY_SIZE)
|
||||||
|
return BH_ERROR;
|
||||||
|
|
||||||
|
memcpy(entry->name, buffer, 56);
|
||||||
|
entry->offset = BH_Read32LEu(buffer + 56);
|
||||||
|
entry->size = BH_Read32LEu(buffer + 60);
|
||||||
|
entry->name[55] = 0;
|
||||||
|
|
||||||
|
return BH_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* Configuration and options */
|
||||||
|
typedef struct Config
|
||||||
|
{
|
||||||
|
char *file;
|
||||||
|
char *input;
|
||||||
|
char *output;
|
||||||
|
int list;
|
||||||
|
} Config;
|
||||||
|
|
||||||
|
|
||||||
|
static BH_ArgsOption options[] = {
|
||||||
|
{'h', "help", 0, "Display this help"},
|
||||||
|
{'l', "list", 0, "List files in the archive instead of reading"},
|
||||||
|
{'i', "input", BH_ARGS_VALUE, "Input file in archive"},
|
||||||
|
{'o', "output", BH_ARGS_VALUE, "Output file"},
|
||||||
|
{0, NULL, 0, NULL}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
static void PrintHelp(void)
|
||||||
|
{
|
||||||
|
printf("Usage: PakReader [options...] <file>\n");
|
||||||
|
BH_ArgsHelp(options, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static int OptionsCallback(int key,
|
||||||
|
char *arg,
|
||||||
|
void *data)
|
||||||
|
{
|
||||||
|
Config *config = (Config *)data;
|
||||||
|
|
||||||
|
switch (key)
|
||||||
|
{
|
||||||
|
case BH_ARGS_UNKNOWN: break;
|
||||||
|
case BH_ARGS_ARGUMENT: if (!config->file) config->file = arg; break;
|
||||||
|
case 'h': PrintHelp(); exit(0);
|
||||||
|
case 'l': config->list = 1; break;
|
||||||
|
case 'i': config->input = arg; break;
|
||||||
|
case 'o': config->output = arg; break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return BH_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* Copy data between two IO */
|
||||||
|
static int CopyData(BH_IO *from,
|
||||||
|
BH_IO *to,
|
||||||
|
size_t size)
|
||||||
|
{
|
||||||
|
size_t i, length, actual;
|
||||||
|
char tmp[512];
|
||||||
|
|
||||||
|
for (i = 0; i < size; i += sizeof(tmp))
|
||||||
|
{
|
||||||
|
length = size - i;
|
||||||
|
if (length > 512)
|
||||||
|
length = 512;
|
||||||
|
|
||||||
|
if (BH_IORead(from, tmp, length, &actual) || length != actual)
|
||||||
|
return BH_ERROR;
|
||||||
|
|
||||||
|
if (BH_IOWrite(to, tmp, length, &actual) || length != actual)
|
||||||
|
return BH_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
return BH_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* Process pack (list files or extract file) */
|
||||||
|
static int ProcessPack(Config *config,
|
||||||
|
BH_IO *io)
|
||||||
|
{
|
||||||
|
PakHeader header;
|
||||||
|
PakEntry entry;
|
||||||
|
BH_IO *output;
|
||||||
|
size_t i;
|
||||||
|
|
||||||
|
/* Read header and seek to begging of the file table */
|
||||||
|
if (ParseHeader(io, &header))
|
||||||
|
return BH_ERROR;
|
||||||
|
|
||||||
|
if (BH_IOSeek(io, header.offset, BH_IO_SEEK_SET))
|
||||||
|
return BH_ERROR;
|
||||||
|
|
||||||
|
/* Parse and output entries */
|
||||||
|
for (i = header.size / 64; i; i--)
|
||||||
|
{
|
||||||
|
if (ParseEntry(io, &entry))
|
||||||
|
return BH_ERROR;
|
||||||
|
|
||||||
|
if (config->list)
|
||||||
|
printf("%s %d\n", entry.name, entry.size);
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (strcmp(entry.name, config->input))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
output = BH_FileNew(config->output);
|
||||||
|
if (BH_IOOpen(output, BH_IO_WRITE) ||
|
||||||
|
BH_IOSeek(io, entry.offset, BH_IO_SEEK_SET) ||
|
||||||
|
CopyData(io, output, entry.size))
|
||||||
|
{
|
||||||
|
BH_IOFree(output);
|
||||||
|
return BH_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
BH_IOFree(output);
|
||||||
|
return BH_OK;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (config->list)
|
||||||
|
return BH_OK;
|
||||||
|
return BH_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* Main entry */
|
||||||
|
int main(int argc, char **argv)
|
||||||
|
{
|
||||||
|
Config config;
|
||||||
|
BH_IO *io;
|
||||||
|
int result;
|
||||||
|
|
||||||
|
/* Parse arguments */
|
||||||
|
memset(&config, 0, sizeof(config));
|
||||||
|
if (BH_ArgsParse(argc, argv, options, OptionsCallback, &config) || !config.file)
|
||||||
|
{
|
||||||
|
PrintHelp();
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Check required arguments */
|
||||||
|
if (!config.list && (!config.input || !config.output))
|
||||||
|
{
|
||||||
|
printf("Specify input and output files\n");
|
||||||
|
PrintHelp();
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Read and write */
|
||||||
|
io = BH_FileNew(config.file);
|
||||||
|
if (BH_IOOpen(io, BH_IO_READ | BH_IO_EXIST))
|
||||||
|
{
|
||||||
|
printf("Can't open file %s\n", config.file);
|
||||||
|
BH_IOFree(io);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
result = ProcessPack(&config, io);
|
||||||
|
BH_IOFree(io);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
BIN
doc/Examples/sample.pak
Normal file
BIN
doc/Examples/sample.pak
Normal file
Binary file not shown.
29
doc/Features.md
Normal file
29
doc/Features.md
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
# Features
|
||||||
|
|
||||||
|
## Implemented features
|
||||||
|
|
||||||
|
Currently implemented features:
|
||||||
|
|
||||||
|
- Abstraction over input/output
|
||||||
|
- Basic data structures (hashmap, queue)
|
||||||
|
- Basic algorithms (sorting, swapping, working with heap, partitioning)
|
||||||
|
- Geomtric primitives (vectors, matrices, quaternions, rays, boxes)
|
||||||
|
- Intersection calculation (ray, boxes, segments, lines, planes, triangles)
|
||||||
|
- Unit testing library (for internal usage)
|
||||||
|
|
||||||
|
## Planned features
|
||||||
|
|
||||||
|
Currently planned features:
|
||||||
|
|
||||||
|
- Command-line interface utilities
|
||||||
|
- Thread support (thread, mutex, cv, atomics, etc.)
|
||||||
|
- Image loading/processing/saving support
|
||||||
|
- Polygon rasterization (possibly canvas)
|
||||||
|
- UTF8 string support (BMP handling)
|
||||||
|
- Font rendering
|
||||||
|
- GUI (Windows GUI, X11)
|
||||||
|
- Deflate/Inflate implementation
|
||||||
|
- Audio loading/processing/saving support
|
||||||
|
- FFT/IFFT and complex numbers
|
||||||
|
- Basic 2D/3D physics engine (possibly as a separate library?)
|
||||||
|
- Configuration support (INI, JSON, Registry)
|
||||||
6
doc/HowTo.md
Normal file
6
doc/HowTo.md
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
# HowTo guides
|
||||||
|
|
||||||
|
For the time being there is only one HowTo guide:
|
||||||
|
|
||||||
|
- [Writing PACK reader utility](HowTo/PakReader.md) which covers the basics of
|
||||||
|
using IO, Args and Utils modules.
|
||||||
421
doc/HowTo/PakReader.md
Normal file
421
doc/HowTo/PakReader.md
Normal file
@@ -0,0 +1,421 @@
|
|||||||
|
# HowTo: Reading PACK (pak) Archives
|
||||||
|
|
||||||
|
## Overview
|
||||||
|
|
||||||
|
In this guide, we will be creating a program called `PakReader`, which will be
|
||||||
|
able to read PAK archives (list all files within the archive or extract a
|
||||||
|
specific file from the archive).
|
||||||
|
|
||||||
|
## Prerequisites
|
||||||
|
|
||||||
|
We want to implement a simple command-line utility that can:
|
||||||
|
|
||||||
|
- List the contents of the archive
|
||||||
|
- Extract a specific file from the archive
|
||||||
|
|
||||||
|
To extract the `Text.txt` file from the `sample.pak` archive to `output.txt`, we
|
||||||
|
would run the following command:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
./PakReader -i Text.txt -o output.txt sample.pak
|
||||||
|
```
|
||||||
|
|
||||||
|
To list files in the `sample.pak` archive, we would run the following command:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
./PakReader -l sample.pak
|
||||||
|
```
|
||||||
|
|
||||||
|
## PACK Archive Format
|
||||||
|
|
||||||
|
PACK archives are extremely simple. A PACK archive consists of three parts:
|
||||||
|
|
||||||
|
- Header at the beginning of the file
|
||||||
|
- File table with the name and size of the file
|
||||||
|
- File data
|
||||||
|
|
||||||
|
Header has the following structure:
|
||||||
|
|
||||||
|
| Name | Type | Information |
|
||||||
|
|--------|---------------|-------------------------------------|
|
||||||
|
| id | 4 byte string | Always contains the word "PACK" |
|
||||||
|
| offset | uint32 | Offset of the file table (in bytes) |
|
||||||
|
| size | uint32 | Size of the file table (in bytes) |
|
||||||
|
|
||||||
|
File table entry has the following structure:
|
||||||
|
|
||||||
|
| Name | Type | Information |
|
||||||
|
|--------|----------------|-------------------------------------|
|
||||||
|
| name | 54 byte string | Full file path (null-terminated) |
|
||||||
|
| offset | uint32 | Offset in the file (in bytes) |
|
||||||
|
| size | uint32 | Size of the file (in bytes) |
|
||||||
|
|
||||||
|
## Includes
|
||||||
|
|
||||||
|
To implement this utility, we are going to need to include the following headers:
|
||||||
|
|
||||||
|
- `BH/IO.h` to work with files (or input/output devices)
|
||||||
|
- `BH/Util.h` to read integers with specific width and endianness
|
||||||
|
- `BH/Args.h` to work with command-line arguments
|
||||||
|
|
||||||
|
## Working with Files
|
||||||
|
|
||||||
|
Working with files in BHLib is based around the IO device (called `BH_IO`).
|
||||||
|
Firstly, you need to create an IO device with the `BH_FileNew` function.
|
||||||
|
Secondly, you need to open the IO device with the `BH_IOOpen` function. While
|
||||||
|
opening the IO device, you can specify in which mode it will work: reading
|
||||||
|
(`BH_IO_READ`) or writing (`BH_IO_WRITE`). Additionally, we can specify whether
|
||||||
|
the IO device (or in our case, the file) should exist before opening
|
||||||
|
(`BH_IO_EXIST`), be truncated before opening (`BH_IO_TRUNCATE`), should it be
|
||||||
|
created (`BH_IO_CREATE`), or opened in append mode (`BH_IO_APPEND`).
|
||||||
|
|
||||||
|
Here is an example for opening an existing file in read-only mode:
|
||||||
|
|
||||||
|
```c
|
||||||
|
BH_IO *io = BH_FileNew("coolfile.dat");
|
||||||
|
if (BH_IOOpen(io, BH_IO_READ | BH_IO_EXIST))
|
||||||
|
{
|
||||||
|
printf("Can't open file 'coolfile.dat'\n", config.file);
|
||||||
|
BH_IOFree(io);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Reading Data from the File
|
||||||
|
|
||||||
|
In general, it's best to read some data into a temporary buffer, then parse the
|
||||||
|
data into variables/structure fields. This makes sure you will not run into
|
||||||
|
issues caused by misalignment (slowdowns or exceptions/crashes).
|
||||||
|
|
||||||
|
For reading or writing integers, it's recommended to use `BH_Read*` and
|
||||||
|
`BH_Write*`, respectively. Suffixes `16`, `32`, `64` denote integers' width in
|
||||||
|
bits. Suffixes `LE` and `BE` denote endianness (LittleEndian and BigEndian).
|
||||||
|
Suffixes `u` and `s` denote unsigned or signed integers.
|
||||||
|
|
||||||
|
For example, the function `BH_Read32LEs` reads a signed 32-bit integer that is
|
||||||
|
stored in LittleEndian format.
|
||||||
|
|
||||||
|
Below is an example for reading header data into the structure `header`:
|
||||||
|
|
||||||
|
```c
|
||||||
|
char buffer[HEADER_SIZE];
|
||||||
|
size_t actual;
|
||||||
|
|
||||||
|
if (BH_IORead(io, buffer, HEADER_SIZE, &actual) || actual != HEADER_SIZE)
|
||||||
|
return BH_ERROR;
|
||||||
|
|
||||||
|
if (memcmp(buffer, "PACK", 4))
|
||||||
|
return BH_ERROR;
|
||||||
|
|
||||||
|
memcpy(header->id, buffer, 4);
|
||||||
|
header->offset = BH_Read32LEu(buffer + 4);
|
||||||
|
header->size = BH_Read32LEu(buffer + 8);
|
||||||
|
```
|
||||||
|
|
||||||
|
## Working with Command-Line Arguments
|
||||||
|
|
||||||
|
Working (or parsing) with CMD arguments is somewhat similar to POSIX
|
||||||
|
`getopt_long` function. Firstly, you need to define what options (or switches)
|
||||||
|
are present in your program. To do this, create an array of `BH_ArgsOption`,
|
||||||
|
filled with option definitions and one empty element at the end of the array.
|
||||||
|
|
||||||
|
Each element consists of the following:
|
||||||
|
|
||||||
|
- Key
|
||||||
|
- Name
|
||||||
|
- Flags
|
||||||
|
- Description or documentation string
|
||||||
|
|
||||||
|
`Key` is used to identify options while processing them. Key value can be an
|
||||||
|
ASCII character or an integer. If key is an ASCII character, it becomes an
|
||||||
|
option's short name (key 'd' becomes `-d`).
|
||||||
|
|
||||||
|
`Name` is used to represent 'long' option names (name 'hello-world' becomes
|
||||||
|
`--hello-world`). You can omit `Name`.
|
||||||
|
|
||||||
|
`Flags` are used to specify whether an option requires a value (or if they are
|
||||||
|
optional).
|
||||||
|
|
||||||
|
`Description` is used in pair with the function `BH_ArgsHelp` to display
|
||||||
|
information about the option.
|
||||||
|
|
||||||
|
Then you need to define a `callback` function that will do something in response
|
||||||
|
to the option being parsed.
|
||||||
|
|
||||||
|
Array of options, option callback, and ARGC/ARGV is then passed to the function
|
||||||
|
`BH_ArgsParse`.
|
||||||
|
|
||||||
|
Here is the example of using the options:
|
||||||
|
|
||||||
|
```c
|
||||||
|
static BH_ArgsOption options[] = {
|
||||||
|
{'h', "help", 0, "Display this help"},
|
||||||
|
{'l', "list", 0, "List files in the archive instead of reading"},
|
||||||
|
{'i', "input", BH_ARGS_VALUE, "Input file in archive"},
|
||||||
|
{'o', "output", BH_ARGS_VALUE, "Output file"},
|
||||||
|
{0, NULL, 0, NULL}
|
||||||
|
};
|
||||||
|
|
||||||
|
static int callback(int key, char *arg, void *data)
|
||||||
|
{
|
||||||
|
Config *config = (Config *)data;
|
||||||
|
|
||||||
|
switch (key)
|
||||||
|
{
|
||||||
|
case BH_ARGS_UNKNOWN: break;
|
||||||
|
case BH_ARGS_ARGUMENT: if (!config->file) config->file = arg; break;
|
||||||
|
case 'h': PrintHelp(); exit(0);
|
||||||
|
case 'l': config->list = 1; break;
|
||||||
|
case 'i': config->input = arg; break;
|
||||||
|
case 'o': config->output = arg; break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return BH_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char **argv)
|
||||||
|
{
|
||||||
|
Config config;
|
||||||
|
|
||||||
|
/* Parse arguments */
|
||||||
|
memset(&config, 0, sizeof(config));
|
||||||
|
if (BH_ArgsParse(argc, argv, options, callback, &config) || !config.file)
|
||||||
|
{
|
||||||
|
PrintHelp();
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ... */
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Putting Everything Together
|
||||||
|
|
||||||
|
Now, let's put everything together and implement `PakReader`.
|
||||||
|
|
||||||
|
```c
|
||||||
|
#include <BH/Args.h>
|
||||||
|
#include <BH/IO.h>
|
||||||
|
#include <BH/Util.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
|
||||||
|
/* PAK header and entry structures and some constants */
|
||||||
|
#define HEADER_SIZE 12
|
||||||
|
#define ENTRY_SIZE 64
|
||||||
|
|
||||||
|
typedef struct PakHeader
|
||||||
|
{
|
||||||
|
char id[4];
|
||||||
|
uint32_t offset;
|
||||||
|
uint32_t size;
|
||||||
|
} PakHeader;
|
||||||
|
|
||||||
|
|
||||||
|
typedef struct PakEntry
|
||||||
|
{
|
||||||
|
char name[56];
|
||||||
|
uint32_t offset;
|
||||||
|
uint32_t size;
|
||||||
|
} PakEntry;
|
||||||
|
|
||||||
|
|
||||||
|
static int ParseHeader(BH_IO *io,
|
||||||
|
PakHeader *header)
|
||||||
|
{
|
||||||
|
char buffer[HEADER_SIZE];
|
||||||
|
size_t actual;
|
||||||
|
|
||||||
|
if (BH_IORead(io, buffer, HEADER_SIZE, &actual) || actual != HEADER_SIZE)
|
||||||
|
return BH_ERROR;
|
||||||
|
|
||||||
|
if (memcmp(buffer, "PACK", 4))
|
||||||
|
return BH_ERROR;
|
||||||
|
|
||||||
|
memcpy(header->id, buffer, 4);
|
||||||
|
header->offset = BH_Read32LEu(buffer + 4);
|
||||||
|
header->size = BH_Read32LEu(buffer + 8);
|
||||||
|
|
||||||
|
return BH_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static int ParseEntry(BH_IO *io,
|
||||||
|
PakEntry *entry)
|
||||||
|
{
|
||||||
|
char buffer[ENTRY_SIZE];
|
||||||
|
size_t actual;
|
||||||
|
|
||||||
|
if (BH_IORead(io, buffer, ENTRY_SIZE, &actual) || actual != ENTRY_SIZE)
|
||||||
|
return BH_ERROR;
|
||||||
|
|
||||||
|
memcpy(entry->name, buffer, 56);
|
||||||
|
entry->offset = BH_Read32LEu(buffer + 56);
|
||||||
|
entry->size = BH_Read32LEu(buffer + 60);
|
||||||
|
entry->name[55] = 0;
|
||||||
|
|
||||||
|
return BH_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* Configuration and options */
|
||||||
|
typedef struct Config
|
||||||
|
{
|
||||||
|
char *file;
|
||||||
|
char *input;
|
||||||
|
char *output;
|
||||||
|
int list;
|
||||||
|
} Config;
|
||||||
|
|
||||||
|
|
||||||
|
static BH_ArgsOption options[] = {
|
||||||
|
{'h', "help", 0, "Display this help"},
|
||||||
|
{'l', "list", 0, "List files in the archive instead of reading"},
|
||||||
|
{'i', "input", BH_ARGS_VALUE, "Input file in archive"},
|
||||||
|
{'o', "output", BH_ARGS_VALUE, "Output file"},
|
||||||
|
{0, NULL, 0, NULL}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
static void PrintHelp(void)
|
||||||
|
{
|
||||||
|
printf("Usage: PakReader [options...] <file>\n");
|
||||||
|
BH_ArgsHelp(options, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static int OptionsCallback(int key,
|
||||||
|
char *arg,
|
||||||
|
void *data)
|
||||||
|
{
|
||||||
|
Config *config = (Config *)data;
|
||||||
|
|
||||||
|
switch (key)
|
||||||
|
{
|
||||||
|
case BH_ARGS_UNKNOWN: break;
|
||||||
|
case BH_ARGS_ARGUMENT: if (!config->file) config->file = arg; break;
|
||||||
|
case 'h': PrintHelp(); exit(0);
|
||||||
|
case 'l': config->list = 1; break;
|
||||||
|
case 'i': config->input = arg; break;
|
||||||
|
case 'o': config->output = arg; break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return BH_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* Copy data between two IO */
|
||||||
|
static int CopyData(BH_IO *from,
|
||||||
|
BH_IO *to,
|
||||||
|
size_t size)
|
||||||
|
{
|
||||||
|
size_t i, length, actual;
|
||||||
|
char tmp[512];
|
||||||
|
|
||||||
|
for (i = 0; i < size; i += sizeof(tmp))
|
||||||
|
{
|
||||||
|
length = size - i;
|
||||||
|
if (length > 512)
|
||||||
|
length = 512;
|
||||||
|
|
||||||
|
if (BH_IORead(from, tmp, length, &actual) || length != actual)
|
||||||
|
return BH_ERROR;
|
||||||
|
|
||||||
|
if (BH_IOWrite(to, tmp, length, &actual) || length != actual)
|
||||||
|
return BH_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
return BH_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* Process pack (list files or extract file) */
|
||||||
|
static int ProcessPack(Config *config,
|
||||||
|
BH_IO *io)
|
||||||
|
{
|
||||||
|
PakHeader header;
|
||||||
|
PakEntry entry;
|
||||||
|
BH_IO *output;
|
||||||
|
size_t i;
|
||||||
|
|
||||||
|
/* Read header and seek to begging of the file table */
|
||||||
|
if (ParseHeader(io, &header))
|
||||||
|
return BH_ERROR;
|
||||||
|
|
||||||
|
if (BH_IOSeek(io, header.offset, BH_IO_SEEK_SET))
|
||||||
|
return BH_ERROR;
|
||||||
|
|
||||||
|
/* Parse and output entries */
|
||||||
|
for (i = header.size / 64; i; i--)
|
||||||
|
{
|
||||||
|
if (ParseEntry(io, &entry))
|
||||||
|
return BH_ERROR;
|
||||||
|
|
||||||
|
if (config->list)
|
||||||
|
printf("%s %d\n", entry.name, entry.size);
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (strcmp(entry.name, config->input))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
output = BH_FileNew(config->output);
|
||||||
|
if (BH_IOOpen(output, BH_IO_WRITE) ||
|
||||||
|
BH_IOSeek(io, entry.offset, BH_IO_SEEK_SET) ||
|
||||||
|
CopyData(io, output, entry.size))
|
||||||
|
{
|
||||||
|
BH_IOFree(output);
|
||||||
|
return BH_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
BH_IOFree(output);
|
||||||
|
return BH_OK;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (config->list)
|
||||||
|
return BH_OK;
|
||||||
|
return BH_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* Main entry */
|
||||||
|
int main(int argc, char **argv)
|
||||||
|
{
|
||||||
|
Config config;
|
||||||
|
BH_IO *io;
|
||||||
|
int result;
|
||||||
|
|
||||||
|
/* Parse arguments */
|
||||||
|
memset(&config, 0, sizeof(config));
|
||||||
|
if (BH_ArgsParse(argc, argv, options, OptionsCallback, &config) || !config.file)
|
||||||
|
{
|
||||||
|
PrintHelp();
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Check required arguments */
|
||||||
|
if (!config.list && (!config.input || !config.output))
|
||||||
|
{
|
||||||
|
printf("Specify input and output files\n");
|
||||||
|
PrintHelp();
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Read and write */
|
||||||
|
io = BH_FileNew(config.file);
|
||||||
|
if (BH_IOOpen(io, BH_IO_READ | BH_IO_EXIST))
|
||||||
|
{
|
||||||
|
printf("Can't open file %s\n", config.file);
|
||||||
|
BH_IOFree(io);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
result = ProcessPack(&config, io);
|
||||||
|
BH_IOFree(io);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
```
|
||||||
252
include/BH/Util.h
Normal file
252
include/BH/Util.h
Normal file
@@ -0,0 +1,252 @@
|
|||||||
|
#ifndef BH_UTIL_H
|
||||||
|
#define BH_UTIL_H
|
||||||
|
|
||||||
|
|
||||||
|
#include "Common.h"
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reads 16-bit unsigned integer from the \a buffer in little-endian format.
|
||||||
|
*
|
||||||
|
* \param buffer Buffer pointer
|
||||||
|
*
|
||||||
|
* \return Returns 16-bit unsigned integer value.
|
||||||
|
*/
|
||||||
|
uint16_t BH_Read16LEu(const char *buffer);
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reads 16-bit signed integer from the \a buffer in little-endian format.
|
||||||
|
*
|
||||||
|
* \param buffer Buffer pointer
|
||||||
|
*
|
||||||
|
* \return Returns 16-bit signed integer value.
|
||||||
|
*/
|
||||||
|
int16_t BH_Read16LEs(const char *buffer);
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reads 32-bit unsigned integer from the \a buffer in little-endian format.
|
||||||
|
*
|
||||||
|
* \param buffer Buffer pointer
|
||||||
|
*
|
||||||
|
* \return Returns 32-bit unsigned integer value.
|
||||||
|
*/
|
||||||
|
uint32_t BH_Read32LEu(const char *buffer);
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reads 32-bit signed integer from the \a buffer in little-endian format.
|
||||||
|
*
|
||||||
|
* \param buffer Buffer pointer
|
||||||
|
*
|
||||||
|
* \return Returns 32-bit signed integer value.
|
||||||
|
*/
|
||||||
|
int32_t BH_Read32LEs(const char *buffer);
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reads 64-bit unsigned integer from the \a buffer in little-endian format.
|
||||||
|
*
|
||||||
|
* \param buffer Buffer pointer
|
||||||
|
*
|
||||||
|
* \return Returns 64-bit unsigned integer value.
|
||||||
|
*/
|
||||||
|
uint64_t BH_Read64LEu(const char *buffer);
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reads 64-bit signed integer from the \a buffer in little-endian format.
|
||||||
|
*
|
||||||
|
* \param buffer Buffer pointer
|
||||||
|
*
|
||||||
|
* \return Returns 64-bit signed integer value.
|
||||||
|
*/
|
||||||
|
int64_t BH_Read64LEs(const char *buffer);
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reads 16-bit unsigned integer from the \a buffer in big-endian format.
|
||||||
|
*
|
||||||
|
* \param buffer Buffer pointer
|
||||||
|
*
|
||||||
|
* \return Returns 16-bit unsigned integer value.
|
||||||
|
*/
|
||||||
|
uint16_t BH_Read16BEu(const char *buffer);
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reads 16-bit signed integer from the \a buffer in big-endian format.
|
||||||
|
*
|
||||||
|
* \param buffer Buffer pointer
|
||||||
|
*
|
||||||
|
* \return Returns 16-bit signed integer value.
|
||||||
|
*/
|
||||||
|
int16_t BH_Read16BEs(const char *buffer);
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reads 32-bit unsigned integer from the \a buffer in big-endian format.
|
||||||
|
*
|
||||||
|
* \param buffer Buffer pointer
|
||||||
|
*
|
||||||
|
* \return Returns 32-bit unsigned integer value.
|
||||||
|
*/
|
||||||
|
uint32_t BH_Read32BEu(const char *buffer);
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reads 32-bit signed integer from the \a buffer in big-endian format.
|
||||||
|
*
|
||||||
|
* \param buffer Buffer pointer
|
||||||
|
*
|
||||||
|
* \return Returns 32-bit signed integer value.
|
||||||
|
*/
|
||||||
|
int32_t BH_Read32BEs(const char *buffer);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reads 64-bit unsigned integer from the \a buffer in big-endian format.
|
||||||
|
*
|
||||||
|
* \param buffer Buffer pointer
|
||||||
|
*
|
||||||
|
* \return Returns 64-bit unsigned integer value.
|
||||||
|
*/
|
||||||
|
uint64_t BH_Read64BEu(const char *buffer);
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reads 64-bit signed integer from the \a buffer in big-endian format.
|
||||||
|
*
|
||||||
|
* \param buffer Buffer pointer
|
||||||
|
*
|
||||||
|
* \return Returns 64-bit signed integer value.
|
||||||
|
*/
|
||||||
|
int64_t BH_Read64BEs(const char *buffer);
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Writes 16-bit unsigned integer to the \a buffer in little-endian format.
|
||||||
|
*
|
||||||
|
* \param buffer Buffer pointer
|
||||||
|
* \param value Value
|
||||||
|
*/
|
||||||
|
void BH_Write16LEu(char *buffer,
|
||||||
|
uint16_t value);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Writes 16-bit signed integer to the \a buffer in little-endian format.
|
||||||
|
*
|
||||||
|
* \param buffer Buffer pointer
|
||||||
|
* \param value Value
|
||||||
|
*/
|
||||||
|
void BH_Write16LEs(char *buffer,
|
||||||
|
int16_t value);
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Writes 32-bit unsigned integer to the \a buffer in little-endian format.
|
||||||
|
*
|
||||||
|
* \param buffer Buffer pointer\param buffer Buffer pointer
|
||||||
|
* \param value Value
|
||||||
|
*/
|
||||||
|
void BH_Write32LEu(char *buffer,
|
||||||
|
uint32_t value);
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Writes 32-bit signed integer to the \a buffer in little-endian format.
|
||||||
|
*
|
||||||
|
* \param buffer Buffer pointer
|
||||||
|
* \param value Value
|
||||||
|
*/
|
||||||
|
void BH_Write32LEs(char *buffer,
|
||||||
|
int32_t value);
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Writes 64-bit unsigned integer to the \a buffer in little-endian format.
|
||||||
|
*
|
||||||
|
* \param buffer Buffer pointer
|
||||||
|
* \param value Value
|
||||||
|
*/
|
||||||
|
void BH_Write64LEu(char *buffer,
|
||||||
|
uint64_t value);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Writes 64-bit signed integer to the \a buffer in little-endian format.
|
||||||
|
*
|
||||||
|
* \param buffer Buffer pointer
|
||||||
|
* \param value Value
|
||||||
|
*/
|
||||||
|
void BH_Write64LEs(char *buffer,
|
||||||
|
int64_t value);
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Writes 16-bit unsigned integer to the \a buffer in big-endian format.
|
||||||
|
*
|
||||||
|
* \param buffer Buffer pointer
|
||||||
|
* \param value Value
|
||||||
|
*/
|
||||||
|
void BH_Write16BEu(char *buffer,
|
||||||
|
uint16_t value);
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Writes 16-bit signed integer to the \a buffer in big-endian format.
|
||||||
|
*
|
||||||
|
* \param buffer Buffer pointer
|
||||||
|
* \param value Value
|
||||||
|
*/
|
||||||
|
void BH_Write16BEs(char *buffer,
|
||||||
|
int16_t value);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Writes 32-bit unsigned integer to the \a buffer in big-endian format.
|
||||||
|
*
|
||||||
|
* \param buffer Buffer pointer
|
||||||
|
* \param value Value
|
||||||
|
*/
|
||||||
|
void BH_Write32BEu(char *buffer,
|
||||||
|
uint32_t value);
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Writes 32-bit signed integer to the \a buffer in big-endian format.
|
||||||
|
*
|
||||||
|
* \param buffer Buffer pointer
|
||||||
|
* \param value Value
|
||||||
|
*/
|
||||||
|
void BH_Write32BEs(char *buffer,
|
||||||
|
int32_t value);
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Writes 64-bit unsigned integer to the \a buffer in big-endian format.
|
||||||
|
*
|
||||||
|
* \param buffer Buffer pointer
|
||||||
|
* \param value Value
|
||||||
|
*/
|
||||||
|
void BH_Write64BEu(char *buffer,
|
||||||
|
uint64_t value);
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Writes 64-bit signed integer to the \a buffer in big-endian format.
|
||||||
|
*
|
||||||
|
* \param buffer Buffer pointer
|
||||||
|
* \param value Value
|
||||||
|
*/
|
||||||
|
void BH_Write64BEs(char *buffer,
|
||||||
|
int64_t value);
|
||||||
|
|
||||||
|
|
||||||
|
#endif /* BH_UTIL_H */
|
||||||
14
plan.txt
14
plan.txt
@@ -1,14 +0,0 @@
|
|||||||
Future plans:
|
|
||||||
- More geometry stuff (boxes, rays, planes, lines, triangles, etc.)
|
|
||||||
- Thread support (thread, mutex, cv, atomics, etc.)
|
|
||||||
- Image loading/processing/saving support
|
|
||||||
- Polygon rasterization (possibly canvas)
|
|
||||||
- UTF8 string support (BMP handling)
|
|
||||||
- Font rendering
|
|
||||||
- GUI (Windows GUI oriented, X11)
|
|
||||||
- Deflate/Inflate implementation
|
|
||||||
- Audio loading/processing/saving support
|
|
||||||
- FFT/IFFT (with basic complex numbers)
|
|
||||||
- Basic 2D/3D physics engine (possibly as a separate library?)
|
|
||||||
- Configuration support (INI, JSON, Registry)
|
|
||||||
- More unit tests
|
|
||||||
1
src/IO.c
1
src/IO.c
@@ -4,6 +4,7 @@
|
|||||||
|
|
||||||
#define BUFFER_SIZE (sizeof(char *))
|
#define BUFFER_SIZE (sizeof(char *))
|
||||||
|
|
||||||
|
|
||||||
struct BH_IO
|
struct BH_IO
|
||||||
{
|
{
|
||||||
BH_IOCallback cb;
|
BH_IOCallback cb;
|
||||||
|
|||||||
323
src/Util.c
Normal file
323
src/Util.c
Normal file
@@ -0,0 +1,323 @@
|
|||||||
|
#include <BH/Util.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
|
||||||
|
union I16
|
||||||
|
{
|
||||||
|
uint16_t u;
|
||||||
|
int16_t s;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
union I32
|
||||||
|
{
|
||||||
|
uint32_t u;
|
||||||
|
int32_t s;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
union I64
|
||||||
|
{
|
||||||
|
uint64_t u;
|
||||||
|
int64_t s;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
uint16_t BH_Read16LEu(const char *buffer)
|
||||||
|
{
|
||||||
|
union I16 tmp;
|
||||||
|
tmp.u = (uint16_t)((unsigned char)buffer[0]);
|
||||||
|
tmp.u |= (uint16_t)((unsigned char)buffer[1]) << 8;
|
||||||
|
return tmp.u;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int16_t BH_Read16LEs(const char *buffer)
|
||||||
|
{
|
||||||
|
union I16 tmp;
|
||||||
|
tmp.u = (uint16_t)((unsigned char)buffer[0]);
|
||||||
|
tmp.u |= (uint16_t)((unsigned char)buffer[1]) << 8;
|
||||||
|
return tmp.s;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
uint32_t BH_Read32LEu(const char *buffer)
|
||||||
|
{
|
||||||
|
union I32 tmp;
|
||||||
|
tmp.u = (uint32_t)((unsigned char)buffer[0]);
|
||||||
|
tmp.u |= (uint32_t)((unsigned char)buffer[1]) << 8;
|
||||||
|
tmp.u |= (uint32_t)((unsigned char)buffer[2]) << 16;
|
||||||
|
tmp.u |= (uint32_t)((unsigned char)buffer[3]) << 24;
|
||||||
|
return tmp.u;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int32_t BH_Read32LEs(const char *buffer)
|
||||||
|
{
|
||||||
|
union I32 tmp;
|
||||||
|
tmp.u = (uint32_t)((unsigned char)buffer[0]);
|
||||||
|
tmp.u |= (uint32_t)((unsigned char)buffer[1]) << 8;
|
||||||
|
tmp.u |= (uint32_t)((unsigned char)buffer[2]) << 16;
|
||||||
|
tmp.u |= (uint32_t)((unsigned char)buffer[3]) << 24;
|
||||||
|
return tmp.s;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
uint64_t BH_Read64LEu(const char *buffer)
|
||||||
|
{
|
||||||
|
union I64 tmp;
|
||||||
|
tmp.u = (uint64_t)((unsigned char)buffer[0]);
|
||||||
|
tmp.u |= (uint64_t)((unsigned char)buffer[1]) << 8;
|
||||||
|
tmp.u |= (uint64_t)((unsigned char)buffer[2]) << 16;
|
||||||
|
tmp.u |= (uint64_t)((unsigned char)buffer[3]) << 24;
|
||||||
|
tmp.u |= (uint64_t)((unsigned char)buffer[4]) << 32;
|
||||||
|
tmp.u |= (uint64_t)((unsigned char)buffer[5]) << 40;
|
||||||
|
tmp.u |= (uint64_t)((unsigned char)buffer[6]) << 48;
|
||||||
|
tmp.u |= (uint64_t)((unsigned char)buffer[7]) << 56;
|
||||||
|
return tmp.u;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int64_t BH_Read64LEs(const char *buffer)
|
||||||
|
{
|
||||||
|
union I64 tmp;
|
||||||
|
tmp.u = (uint64_t)((unsigned char)buffer[0]);
|
||||||
|
tmp.u |= (uint64_t)((unsigned char)buffer[1]) << 8;
|
||||||
|
tmp.u |= (uint64_t)((unsigned char)buffer[2]) << 16;
|
||||||
|
tmp.u |= (uint64_t)((unsigned char)buffer[3]) << 24;
|
||||||
|
tmp.u |= (uint64_t)((unsigned char)buffer[4]) << 32;
|
||||||
|
tmp.u |= (uint64_t)((unsigned char)buffer[5]) << 40;
|
||||||
|
tmp.u |= (uint64_t)((unsigned char)buffer[6]) << 48;
|
||||||
|
tmp.u |= (uint64_t)((unsigned char)buffer[7]) << 56;
|
||||||
|
return tmp.s;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
uint16_t BH_Read16BEu(const char *buffer)
|
||||||
|
{
|
||||||
|
union I16 tmp;
|
||||||
|
tmp.u = (uint16_t)((unsigned char)buffer[0]) << 8;
|
||||||
|
tmp.u |= (uint16_t)((unsigned char)buffer[1]);
|
||||||
|
return tmp.u;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int16_t BH_Read16BEs(const char *buffer)
|
||||||
|
{
|
||||||
|
union I16 tmp;
|
||||||
|
tmp.u = (uint16_t)((unsigned char)buffer[0]) << 8;
|
||||||
|
tmp.u |= (uint16_t)((unsigned char)buffer[1]);
|
||||||
|
return tmp.s;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t BH_Read32BEu(const char *buffer)
|
||||||
|
{
|
||||||
|
union I32 tmp;
|
||||||
|
tmp.u = (uint32_t)((unsigned char)buffer[0]) << 24;
|
||||||
|
tmp.u |= (uint32_t)((unsigned char)buffer[1]) << 16;
|
||||||
|
tmp.u |= (uint32_t)((unsigned char)buffer[2]) << 8;
|
||||||
|
tmp.u |= (uint32_t)((unsigned char)buffer[3]);
|
||||||
|
return tmp.u;
|
||||||
|
}
|
||||||
|
|
||||||
|
int32_t BH_Read32BEs(const char *buffer)
|
||||||
|
{
|
||||||
|
union I32 tmp;
|
||||||
|
tmp.u = (uint32_t)((unsigned char)buffer[0]) << 24;
|
||||||
|
tmp.u |= (uint32_t)((unsigned char)buffer[1]) << 16;
|
||||||
|
tmp.u |= (uint32_t)((unsigned char)buffer[2]) << 8;
|
||||||
|
tmp.u |= (uint32_t)((unsigned char)buffer[3]);
|
||||||
|
return tmp.s;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint64_t BH_Read64BEu(const char *buffer)
|
||||||
|
{
|
||||||
|
union I64 tmp;
|
||||||
|
tmp.u = (uint64_t)((unsigned char)buffer[0]) << 56;
|
||||||
|
tmp.u |= (uint64_t)((unsigned char)buffer[1]) << 48;
|
||||||
|
tmp.u |= (uint64_t)((unsigned char)buffer[2]) << 40;
|
||||||
|
tmp.u |= (uint64_t)((unsigned char)buffer[3]) << 32;
|
||||||
|
tmp.u |= (uint64_t)((unsigned char)buffer[4]) << 24;
|
||||||
|
tmp.u |= (uint64_t)((unsigned char)buffer[5]) << 16;
|
||||||
|
tmp.u |= (uint64_t)((unsigned char)buffer[6]) << 8;
|
||||||
|
tmp.u |= (uint64_t)((unsigned char)buffer[7]);
|
||||||
|
return tmp.u;
|
||||||
|
}
|
||||||
|
|
||||||
|
int64_t BH_Read64BEs(const char *buffer)
|
||||||
|
{
|
||||||
|
union I64 tmp;
|
||||||
|
tmp.u = (uint64_t)((unsigned char)buffer[0]) << 56;
|
||||||
|
tmp.u |= (uint64_t)((unsigned char)buffer[1]) << 48;
|
||||||
|
tmp.u |= (uint64_t)((unsigned char)buffer[2]) << 40;
|
||||||
|
tmp.u |= (uint64_t)((unsigned char)buffer[3]) << 32;
|
||||||
|
tmp.u |= (uint64_t)((unsigned char)buffer[4]) << 24;
|
||||||
|
tmp.u |= (uint64_t)((unsigned char)buffer[5]) << 16;
|
||||||
|
tmp.u |= (uint64_t)((unsigned char)buffer[6]) << 8;
|
||||||
|
tmp.u |= (uint64_t)((unsigned char)buffer[7]);
|
||||||
|
return tmp.s;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void BH_Write16LEu(char *buffer,
|
||||||
|
uint16_t value)
|
||||||
|
{
|
||||||
|
union I16 tmp;
|
||||||
|
tmp.u = value;
|
||||||
|
|
||||||
|
((unsigned char *)buffer)[0] = (tmp.u) & 0xFF;
|
||||||
|
((unsigned char *)buffer)[1] = (tmp.u >> 8) & 0xFF;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void BH_Write16LEs(char *buffer,
|
||||||
|
int16_t value)
|
||||||
|
{
|
||||||
|
union I16 tmp;
|
||||||
|
tmp.s = value;
|
||||||
|
|
||||||
|
((unsigned char *)buffer)[0] = (tmp.u) & 0xFF;
|
||||||
|
((unsigned char *)buffer)[1] = (tmp.u >> 8) & 0xFF;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void BH_Write32LEu(char *buffer,
|
||||||
|
uint32_t value)
|
||||||
|
{
|
||||||
|
union I32 tmp;
|
||||||
|
tmp.u = value;
|
||||||
|
|
||||||
|
((unsigned char *)buffer)[0] = (tmp.u) & 0xFF;
|
||||||
|
((unsigned char *)buffer)[1] = (tmp.u >> 8) & 0xFF;
|
||||||
|
((unsigned char *)buffer)[2] = (tmp.u >> 16) & 0xFF;
|
||||||
|
((unsigned char *)buffer)[3] = (tmp.u >> 24) & 0xFF;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void BH_Write32LEs(char *buffer,
|
||||||
|
int32_t value)
|
||||||
|
{
|
||||||
|
union I32 tmp;
|
||||||
|
tmp.s = value;
|
||||||
|
|
||||||
|
((unsigned char *)buffer)[0] = (tmp.u) & 0xFF;
|
||||||
|
((unsigned char *)buffer)[1] = (tmp.u >> 8) & 0xFF;
|
||||||
|
((unsigned char *)buffer)[2] = (tmp.u >> 16) & 0xFF;
|
||||||
|
((unsigned char *)buffer)[3] = (tmp.u >> 24) & 0xFF;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void BH_Write64LEu(char *buffer,
|
||||||
|
uint64_t value)
|
||||||
|
{
|
||||||
|
union I64 tmp;
|
||||||
|
tmp.u = value;
|
||||||
|
|
||||||
|
((unsigned char *)buffer)[0] = (tmp.u) & 0xFF;
|
||||||
|
((unsigned char *)buffer)[1] = (tmp.u >> 8) & 0xFF;
|
||||||
|
((unsigned char *)buffer)[2] = (tmp.u >> 16) & 0xFF;
|
||||||
|
((unsigned char *)buffer)[3] = (tmp.u >> 24) & 0xFF;
|
||||||
|
((unsigned char *)buffer)[4] = (tmp.u >> 32) & 0xFF;
|
||||||
|
((unsigned char *)buffer)[5] = (tmp.u >> 40) & 0xFF;
|
||||||
|
((unsigned char *)buffer)[6] = (tmp.u >> 48) & 0xFF;
|
||||||
|
((unsigned char *)buffer)[7] = (tmp.u >> 56) & 0xFF;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void BH_Write64LEs(char *buffer,
|
||||||
|
int64_t value)
|
||||||
|
{
|
||||||
|
union I64 tmp;
|
||||||
|
tmp.s = value;
|
||||||
|
|
||||||
|
((unsigned char *)buffer)[0] = (tmp.u) & 0xFF;
|
||||||
|
((unsigned char *)buffer)[1] = (tmp.u >> 8) & 0xFF;
|
||||||
|
((unsigned char *)buffer)[2] = (tmp.u >> 16) & 0xFF;
|
||||||
|
((unsigned char *)buffer)[3] = (tmp.u >> 24) & 0xFF;
|
||||||
|
((unsigned char *)buffer)[4] = (tmp.u >> 32) & 0xFF;
|
||||||
|
((unsigned char *)buffer)[5] = (tmp.u >> 40) & 0xFF;
|
||||||
|
((unsigned char *)buffer)[6] = (tmp.u >> 48) & 0xFF;
|
||||||
|
((unsigned char *)buffer)[7] = (tmp.u >> 56) & 0xFF;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void BH_Write16BEu(char *buffer,
|
||||||
|
uint16_t value)
|
||||||
|
{
|
||||||
|
union I16 tmp;
|
||||||
|
tmp.u = value;
|
||||||
|
|
||||||
|
((unsigned char *)buffer)[0] = (tmp.u >> 8) & 0xFF;
|
||||||
|
((unsigned char *)buffer)[1] = (tmp.u) & 0xFF;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void BH_Write16BEs(char *buffer,
|
||||||
|
int16_t value)
|
||||||
|
{
|
||||||
|
union I16 tmp;
|
||||||
|
tmp.s = value;
|
||||||
|
|
||||||
|
((unsigned char *)buffer)[0] = (tmp.u >> 8) & 0xFF;
|
||||||
|
((unsigned char *)buffer)[1] = (tmp.u) & 0xFF;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void BH_Write32BEu(char *buffer,
|
||||||
|
uint32_t value)
|
||||||
|
{
|
||||||
|
union I32 tmp;
|
||||||
|
tmp.u = value;
|
||||||
|
|
||||||
|
((unsigned char *)buffer)[0] = (tmp.u >> 24) & 0xFF;
|
||||||
|
((unsigned char *)buffer)[1] = (tmp.u >> 16) & 0xFF;
|
||||||
|
((unsigned char *)buffer)[2] = (tmp.u >> 8) & 0xFF;
|
||||||
|
((unsigned char *)buffer)[3] = (tmp.u) & 0xFF;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void BH_Write32BEs(char *buffer,
|
||||||
|
int32_t value)
|
||||||
|
{
|
||||||
|
union I32 tmp;
|
||||||
|
tmp.s = value;
|
||||||
|
|
||||||
|
((unsigned char *)buffer)[0] = (tmp.u >> 24) & 0xFF;
|
||||||
|
((unsigned char *)buffer)[1] = (tmp.u >> 16) & 0xFF;
|
||||||
|
((unsigned char *)buffer)[2] = (tmp.u >> 8) & 0xFF;
|
||||||
|
((unsigned char *)buffer)[3] = (tmp.u) & 0xFF;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void BH_Write64BEu(char *buffer,
|
||||||
|
uint64_t value)
|
||||||
|
{
|
||||||
|
union I64 tmp;
|
||||||
|
tmp.u = value;
|
||||||
|
|
||||||
|
((unsigned char *)buffer)[0] = (tmp.u >> 56) & 0xFF;
|
||||||
|
((unsigned char *)buffer)[1] = (tmp.u >> 48) & 0xFF;
|
||||||
|
((unsigned char *)buffer)[2] = (tmp.u >> 40) & 0xFF;
|
||||||
|
((unsigned char *)buffer)[3] = (tmp.u >> 32) & 0xFF;
|
||||||
|
((unsigned char *)buffer)[4] = (tmp.u >> 24) & 0xFF;
|
||||||
|
((unsigned char *)buffer)[5] = (tmp.u >> 16) & 0xFF;
|
||||||
|
((unsigned char *)buffer)[6] = (tmp.u >> 8) & 0xFF;
|
||||||
|
((unsigned char *)buffer)[7] = (tmp.u) & 0xFF;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void BH_Write64BEs(char *buffer,
|
||||||
|
int64_t value)
|
||||||
|
{
|
||||||
|
union I64 tmp;
|
||||||
|
tmp.s = value;
|
||||||
|
|
||||||
|
((unsigned char *)buffer)[0] = (tmp.u >> 56) & 0xFF;
|
||||||
|
((unsigned char *)buffer)[1] = (tmp.u >> 48) & 0xFF;
|
||||||
|
((unsigned char *)buffer)[2] = (tmp.u >> 40) & 0xFF;
|
||||||
|
((unsigned char *)buffer)[3] = (tmp.u >> 32) & 0xFF;
|
||||||
|
((unsigned char *)buffer)[4] = (tmp.u >> 24) & 0xFF;
|
||||||
|
((unsigned char *)buffer)[5] = (tmp.u >> 16) & 0xFF;
|
||||||
|
((unsigned char *)buffer)[6] = (tmp.u >> 8) & 0xFF;
|
||||||
|
((unsigned char *)buffer)[7] = (tmp.u) & 0xFF;
|
||||||
|
}
|
||||||
@@ -24,7 +24,7 @@ foreach(TEST_FILENAME ${TEST_FILES})
|
|||||||
endif()
|
endif()
|
||||||
|
|
||||||
# Enable coverage
|
# Enable coverage
|
||||||
if(COVERAGE)
|
if(ENABLE_COVERAGE)
|
||||||
target_compile_options("${TEST_NAME}" PRIVATE -coverage)
|
target_compile_options("${TEST_NAME}" PRIVATE -coverage)
|
||||||
target_link_options("${TEST_NAME}" PRIVATE -coverage)
|
target_link_options("${TEST_NAME}" PRIVATE -coverage)
|
||||||
endif()
|
endif()
|
||||||
|
|||||||
@@ -385,6 +385,23 @@ BH_UNIT_TEST(InvalidLong)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
BH_UNIT_TEST(Aesthetics)
|
||||||
|
{
|
||||||
|
BH_ArgsOption options[] = {
|
||||||
|
{'a', "abc", 0, NULL},
|
||||||
|
{'b', "bcd", BH_ARGS_VALUE, "B option"},
|
||||||
|
{'i', "input", BH_ARGS_VALUE | BH_ARGS_OPTIONAL, "Input option"},
|
||||||
|
{1000, "long-option", 0, "Long options\nWith newline"},
|
||||||
|
{'p', NULL, 0, "P option"},
|
||||||
|
{0, NULL, 0, NULL},
|
||||||
|
};
|
||||||
|
|
||||||
|
BH_ArgsHelp(options, 0);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
int main(int argc, char **argv)
|
int main(int argc, char **argv)
|
||||||
{
|
{
|
||||||
(void)argc;
|
(void)argc;
|
||||||
@@ -398,6 +415,7 @@ int main(int argc, char **argv)
|
|||||||
BH_UNIT_ADD(Complex);
|
BH_UNIT_ADD(Complex);
|
||||||
BH_UNIT_ADD(InvalidShort);
|
BH_UNIT_ADD(InvalidShort);
|
||||||
BH_UNIT_ADD(InvalidLong);
|
BH_UNIT_ADD(InvalidLong);
|
||||||
|
BH_UNIT_ADD(Aesthetics);
|
||||||
|
|
||||||
return BH_UnitRun();
|
return BH_UnitRun();
|
||||||
}
|
}
|
||||||
|
|||||||
69
test/src/TestEndian.c
Normal file
69
test/src/TestEndian.c
Normal file
@@ -0,0 +1,69 @@
|
|||||||
|
#include <BH/Util.h>
|
||||||
|
#include <BH/Unit.h>
|
||||||
|
|
||||||
|
|
||||||
|
BH_UNIT_TEST(Int16)
|
||||||
|
{
|
||||||
|
char buffer[2];
|
||||||
|
|
||||||
|
BH_Write16LEs(buffer, -12345);
|
||||||
|
BH_VERIFY(BH_Read16LEs(buffer) == -12345);
|
||||||
|
BH_Write16BEs(buffer, BH_Read16BEs(buffer));
|
||||||
|
BH_VERIFY(BH_Read16LEs(buffer) == -12345);
|
||||||
|
|
||||||
|
BH_Write16LEu(buffer, 12345);
|
||||||
|
BH_VERIFY(BH_Read16LEu(buffer) == 12345);
|
||||||
|
BH_Write16BEu(buffer, BH_Read16BEu(buffer));
|
||||||
|
BH_VERIFY(BH_Read16LEu(buffer) == 12345);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
BH_UNIT_TEST(Int32)
|
||||||
|
{
|
||||||
|
char buffer[4];
|
||||||
|
|
||||||
|
BH_Write32LEs(buffer, -12345);
|
||||||
|
BH_VERIFY(BH_Read32LEs(buffer) == -12345);
|
||||||
|
BH_Write32BEs(buffer, BH_Read32BEs(buffer));
|
||||||
|
BH_VERIFY(BH_Read32LEs(buffer) == -12345);
|
||||||
|
|
||||||
|
BH_Write32LEu(buffer, 12345);
|
||||||
|
BH_VERIFY(BH_Read32LEu(buffer) == 12345);
|
||||||
|
BH_Write32BEu(buffer, BH_Read32BEu(buffer));
|
||||||
|
BH_VERIFY(BH_Read32LEu(buffer) == 12345);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
BH_UNIT_TEST(Int64)
|
||||||
|
{
|
||||||
|
char buffer[8];
|
||||||
|
|
||||||
|
BH_Write64LEs(buffer, -12345);
|
||||||
|
BH_VERIFY(BH_Read64LEs(buffer) == -12345);
|
||||||
|
BH_Write64BEs(buffer, BH_Read64BEs(buffer));
|
||||||
|
BH_VERIFY(BH_Read64LEs(buffer) == -12345);
|
||||||
|
|
||||||
|
BH_Write64LEu(buffer, 12345);
|
||||||
|
BH_VERIFY(BH_Read64LEu(buffer) == 12345);
|
||||||
|
BH_Write64BEu(buffer, BH_Read64BEu(buffer));
|
||||||
|
BH_VERIFY(BH_Read64LEu(buffer) == 12345);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int main(int argc, char **argv)
|
||||||
|
{
|
||||||
|
BH_UNUSED(argc);
|
||||||
|
BH_UNUSED(argv);
|
||||||
|
|
||||||
|
BH_UNIT_ADD(Int16);
|
||||||
|
BH_UNIT_ADD(Int32);
|
||||||
|
BH_UNIT_ADD(Int64);
|
||||||
|
|
||||||
|
return BH_UnitRun();
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user