mirror of
https://github.com/FWGS/xash3d-fwgs.git
synced 2026-08-05 03:24:56 +08:00
public: add swaplib
This commit is contained in:
127
public/swaplib.h
Normal file
127
public/swaplib.h
Normal 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
|
||||||
235
public/tests/test_swapstruct.c
Normal file
235
public/tests/test_swapstruct.c
Normal 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;
|
||||||
|
}
|
||||||
@@ -158,6 +158,7 @@ def build(bld):
|
|||||||
'atoi': 'tests/test_atoi.c',
|
'atoi': 'tests/test_atoi.c',
|
||||||
'parsefile': 'tests/test_parsefile.c',
|
'parsefile': 'tests/test_parsefile.c',
|
||||||
'atlas': 'tests/test_atlas.c',
|
'atlas': 'tests/test_atlas.c',
|
||||||
|
'swapstruct': 'tests/test_swapstruct.c',
|
||||||
}
|
}
|
||||||
|
|
||||||
for i in tests:
|
for i in tests:
|
||||||
|
|||||||
Reference in New Issue
Block a user