public: add swaplib

This commit is contained in:
Alibek Omarov
2026-04-21 23:56:16 +05:00
committed by a1batross
parent 1df6a040ec
commit 489c1c9dd5
3 changed files with 363 additions and 0 deletions

127
public/swaplib.h Normal file
View File

@@ -0,0 +1,127 @@
/*
swaplib.h - byte-swapping for on-disk structures
Copyright (C) 2026 Alibek Omarov
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
*/
#ifndef SWAPLIB_H
#define SWAPLIB_H
#include "build.h"
#include "xash3d_types.h"
typedef struct swap_struct_def_s
{
uint32_t offset;
int32_t size;
struct swap_struct_def_s *subdef;
uint32_t count;
uint32_t stride;
} swap_struct_def_t;
static inline void swap_two_bytes_( byte *a, byte *b )
{
byte tmp = *a;
*a = *b;
*b = tmp;
}
static inline void swap_field_( byte *p, int32_t size )
{
switch( size )
{
case 2:
swap_two_bytes_( &p[0], &p[1] );
break;
case 4:
swap_two_bytes_( &p[0], &p[3] );
swap_two_bytes_( &p[1], &p[2] );
break;
case 8:
swap_two_bytes_( &p[0], &p[7] );
swap_two_bytes_( &p[1], &p[6] );
swap_two_bytes_( &p[2], &p[5] );
swap_two_bytes_( &p[3], &p[4] );
break;
default:
break;
}
}
static inline void swap_struct_( const swap_struct_def_t *def, size_t len, byte *data )
{
for( size_t i = 0; i < len; i++ )
{
if( def[i].size < 0 )
{
uint32_t count = def[i].count ? def[i].count : 1;
for( uint32_t j = 0; j < count; j++ )
swap_struct_( def[i].subdef, -def[i].size, &data[def[i].offset + j * def[i].stride] );
continue;
}
if( def[i].count > 0 )
{
for( uint32_t j = 0; j < def[i].count; j++ )
swap_field_( &data[def[i].offset + j * def[i].stride], def[i].size );
continue;
}
swap_field_( &data[def[i].offset], def[i].size );
}
}
#define swap_struct( swapdef, ptr ) swap_struct_(( swapdef ), sizeof( swapdef ) / sizeof( swapdef[0] ), (byte *)( ptr ))
#define swap_struct_begin( swapdef ) static swap_struct_def_t swapdef[] = {
#define swap_struct_field( record, field ) { .offset = offsetof( record, field ), .size = sizeof(((record *)0)->field) },
#define swap_struct_child( record, field, child ) { .offset = offsetof( record, field ), .size = -(int32_t)(sizeof( child ) / sizeof( child[0] )), .subdef = child },
#define swap_struct_array( record, field, cnt ) { .offset = offsetof( record, field ), .size = sizeof(((record *)0)->field[0]), .count = cnt, .stride = sizeof(((record *)0)->field[0]) },
#define swap_struct_array_child( record, field, child, cnt ) { .offset = offsetof( record, field ), .size = -(int32_t)(sizeof( child ) / sizeof( child[0] )), .subdef = child, .count = cnt, .stride = sizeof(((record *)0)->field[0]) },
#define swap_struct_end() }
// this is done in macros so we can completely avoid defining this
// in little endian targets
#ifdef XASH_LITTLE_ENDIAN
#define be_struct_begin( x ) swap_struct_begin( x )
#define be_struct_field( x, y ) swap_struct_field( x, y )
#define be_struct_child( x, y, z ) swap_struct_child( x, y, z )
#define be_struct_array( x, y, z ) swap_struct_array( x, y, z )
#define be_struct_array_child( x, y, z, w ) swap_struct_array_child( x, y, z, w )
#define be_struct_end() swap_struct_end()
#define be_struct_swap( x, y ) swap_struct( x, y )
#define le_struct_begin( x )
#define le_struct_field( x, y )
#define le_struct_child( x, y, z )
#define le_struct_array( x, y, z )
#define le_struct_array_child( x, y, z, w )
#define le_struct_end()
#define le_struct_swap( x, y ) (void)(y)
#else
#define le_struct_begin( x ) swap_struct_begin( x )
#define le_struct_field( x, y ) swap_struct_field( x, y )
#define le_struct_child( x, y, z ) swap_struct_child( x, y, z )
#define le_struct_array( x, y, z ) swap_struct_array( x, y, z )
#define le_struct_array_child( x, y, z, w ) swap_struct_array_child( x, y, z, w )
#define le_struct_end() swap_struct_end()
#define le_struct_swap( x, y ) swap_struct( x, y )
#define be_struct_begin( x )
#define be_struct_field( x, y )
#define be_struct_child( x, y, z )
#define be_struct_array( x, y, z )
#define be_struct_array_child( x, y, z, w )
#define be_struct_end()
#define be_struct_swap( x, y ) (void)(y)
#endif
#endif // SWAPLIB_H

View File

@@ -0,0 +1,235 @@
#include <stddef.h>
#include "swaplib.h"
typedef struct
{
uint16_t x;
uint16_t y;
} inner_t;
typedef struct
{
uint8_t a;
uint16_t b;
uint32_t c;
inner_t inner;
} outer_t;
swap_struct_begin( inner_def )
swap_struct_field( inner_t, x )
swap_struct_field( inner_t, y )
swap_struct_end();
swap_struct_begin( outer_def )
swap_struct_field( outer_t, b )
swap_struct_field( outer_t, c )
swap_struct_child( outer_t, inner, inner_def )
swap_struct_end();
typedef struct
{
uint32_t version;
inner_t items[4];
} array_outer_t;
swap_struct_begin( array_outer_def )
swap_struct_field( array_outer_t, version )
swap_struct_array_child( array_outer_t, items, inner_def, 4 )
swap_struct_end();
typedef struct
{
uint16_t tag;
uint32_t values[3];
} int_array_t;
swap_struct_begin( int_array_def )
swap_struct_field( int_array_t, tag )
swap_struct_array( int_array_t, values, 3 )
swap_struct_end();
static int Test_SwapField( void )
{
inner_t data = { .x = 0x0102, .y = 0x0304 };
swap_struct( inner_def, &data );
if( data.x != 0x0201 )
return 1;
if( data.y != 0x0403 )
return 2;
// swap again, should restore original
swap_struct( inner_def, &data );
if( data.x != 0x0102 )
return 3;
if( data.y != 0x0304 )
return 4;
return 0;
}
static int Test_SwapChild( void )
{
outer_t data =
{
.a = 0xAA,
.b = 0x0102,
.c = 0x01020304,
.inner = { .x = 0x0506, .y = 0x0708 },
};
swap_struct( outer_def, &data );
// a is uint8_t, must be untouched
if( data.a != 0xAA )
return 1;
// b must be byte-swapped
if( data.b != 0x0201 )
return 2;
// c must be byte-swapped
if( data.c != 0x04030201 )
return 3;
// child fields must be byte-swapped
if( data.inner.x != 0x0605 )
return 4;
if( data.inner.y != 0x0807 )
return 5;
// swap back
swap_struct( outer_def, &data );
if( data.b != 0x0102 )
return 6;
if( data.c != 0x01020304 )
return 7;
if( data.inner.x != 0x0506 )
return 8;
if( data.inner.y != 0x0708 )
return 9;
return 0;
}
static int Test_SwapArrayChild( void )
{
array_outer_t data =
{
.version = 0x01020304,
.items =
{
{ .x = 0x0102, .y = 0x0304 },
{ .x = 0x0506, .y = 0x0708 },
{ .x = 0x090A, .y = 0x0B0C },
{ .x = 0x0D0E, .y = 0x0F10 },
},
};
swap_struct( array_outer_def, &data );
if( data.version != 0x04030201 )
return 1;
if( data.items[0].x != 0x0201 || data.items[0].y != 0x0403 )
return 2;
if( data.items[1].x != 0x0605 || data.items[1].y != 0x0807 )
return 3;
if( data.items[2].x != 0x0A09 || data.items[2].y != 0x0C0B )
return 4;
if( data.items[3].x != 0x0E0D || data.items[3].y != 0x100F )
return 5;
// swap back
swap_struct( array_outer_def, &data );
if( data.version != 0x01020304 )
return 6;
if( data.items[0].x != 0x0102 || data.items[0].y != 0x0304 )
return 7;
if( data.items[3].x != 0x0D0E || data.items[3].y != 0x0F10 )
return 8;
return 0;
}
static int Test_SwapArray( void )
{
int_array_t data =
{
.tag = 0x0102,
.values = { 0x01020304, 0x05060708, 0x090A0B0C },
};
swap_struct( int_array_def, &data );
if( data.tag != 0x0201 )
return 1;
if( data.values[0] != 0x04030201 )
return 2;
if( data.values[1] != 0x08070605 )
return 3;
if( data.values[2] != 0x0C0B0A09 )
return 4;
// swap back
swap_struct( int_array_def, &data );
if( data.tag != 0x0102 )
return 5;
if( data.values[0] != 0x01020304 )
return 6;
if( data.values[1] != 0x05060708 )
return 7;
if( data.values[2] != 0x090A0B0C )
return 8;
return 0;
}
int main( void )
{
int ret;
ret = Test_SwapField();
if( ret > 0 )
return ret;
ret = Test_SwapChild();
if( ret > 0 )
return ret + 32;
ret = Test_SwapArrayChild();
if( ret > 0 )
return ret + 64;
ret = Test_SwapArray();
if( ret > 0 )
return ret + 96;
return 0;
}

View File

@@ -158,6 +158,7 @@ def build(bld):
'atoi': 'tests/test_atoi.c',
'parsefile': 'tests/test_parsefile.c',
'atlas': 'tests/test_atlas.c',
'swapstruct': 'tests/test_swapstruct.c',
}
for i in tests: