Reformatting.
This commit is contained in:
@@ -11,38 +11,38 @@
|
|||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
|
|
||||||
int main(void) {
|
int main(void) {
|
||||||
FILE *fp;
|
FILE *fp;
|
||||||
errno_t ret = fopen_s(&fp, "Skyrim.esm", "rb");
|
errno_t ret = fopen_s(&fp, "Skyrim.esm", "rb");
|
||||||
|
|
||||||
if (ret || !fp)
|
if (ret || !fp)
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
fseek(fp, 0L, SEEK_END);
|
fseek(fp, 0L, SEEK_END);
|
||||||
size_t size = ftell(fp);
|
size_t size = ftell(fp);
|
||||||
rewind(fp);
|
rewind(fp);
|
||||||
|
|
||||||
char *buffer = malloc(size);
|
char *buffer = malloc(size);
|
||||||
if (!buffer)
|
if (!buffer)
|
||||||
return errno;
|
return errno;
|
||||||
|
|
||||||
size_t read = fread(buffer, sizeof(char), size, fp);
|
size_t read = fread(buffer, sizeof(char), size, fp);
|
||||||
assert(read == size);
|
assert(read == size);
|
||||||
|
|
||||||
size_t dc_size = espr_decompressed_size(buffer, size);
|
size_t dc_size = espr_decompressed_size(buffer, size);
|
||||||
|
|
||||||
char *decompressed = malloc(dc_size);
|
|
||||||
if (!decompressed)
|
|
||||||
return errno;
|
|
||||||
|
|
||||||
espr_decompress(buffer, size, decompressed, dc_size);
|
char *decompressed = malloc(dc_size);
|
||||||
|
if (!decompressed)
|
||||||
|
return errno;
|
||||||
|
|
||||||
free(buffer);
|
espr_decompress(buffer, size, decompressed, dc_size);
|
||||||
|
|
||||||
size_t formid_count = espr_formid_count(decompressed, dc_size);
|
free(buffer);
|
||||||
|
|
||||||
printf("FormID Count: %zu\n", formid_count);
|
size_t formid_count = espr_formid_count(decompressed, dc_size);
|
||||||
|
|
||||||
free(decompressed);
|
printf("FormID Count: %zu\n", formid_count);
|
||||||
|
|
||||||
return 0;
|
free(decompressed);
|
||||||
|
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -26,7 +26,7 @@
|
|||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include "msh.h"
|
#include "msh.h"
|
||||||
|
|
||||||
// Guards for C++ usage
|
// Guards for C++ usage
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
extern "C" {
|
extern "C" {
|
||||||
#endif
|
#endif
|
||||||
@@ -38,7 +38,7 @@ extern "C" {
|
|||||||
/* RT hash seed was externally calculated s.t. the fourcc codes perfectly hash into
|
/* RT hash seed was externally calculated s.t. the fourcc codes perfectly hash into
|
||||||
* indices between 0 and 511. That is, there are no hashing collisions. This allows
|
* indices between 0 and 511. That is, there are no hashing collisions. This allows
|
||||||
* for hard coded lookup tables for the fourcc codes in a relatively small space.
|
* for hard coded lookup tables for the fourcc codes in a relatively small space.
|
||||||
*
|
*
|
||||||
* A minimal perfect hash is also possible with an intermediate seed table, though
|
* A minimal perfect hash is also possible with an intermediate seed table, though
|
||||||
* I'm not sure which is faster, if it's worth trying to speed this up, etc.
|
* I'm not sure which is faster, if it's worth trying to speed this up, etc.
|
||||||
*/
|
*/
|
||||||
@@ -52,219 +52,216 @@ extern "C" {
|
|||||||
#define GTS_SIZE 10
|
#define GTS_SIZE 10
|
||||||
|
|
||||||
|
|
||||||
//
|
//
|
||||||
// === FORWARD DEFS ===
|
// === FORWARD DEFS ===
|
||||||
//
|
//
|
||||||
|
|
||||||
typedef union type4 Type4;
|
typedef union type4 Type4;
|
||||||
typedef struct timestamp Timestamp;
|
typedef struct timestamp Timestamp;
|
||||||
typedef struct group Group;
|
typedef struct group Group;
|
||||||
typedef struct record Record;
|
typedef struct record Record;
|
||||||
typedef struct field Field;
|
typedef struct field Field;
|
||||||
|
|
||||||
//
|
//
|
||||||
// === SIMPLE TYPES ===
|
// === SIMPLE TYPES ===
|
||||||
//
|
//
|
||||||
|
|
||||||
// Basic types
|
typedef uint32_t formid;
|
||||||
typedef uint32_t formid;
|
|
||||||
|
|
||||||
// char[4] with uint32_t access
|
// char[4] with uint32_t access
|
||||||
union type4 {
|
union type4 {
|
||||||
char bytes[4];
|
char bytes[4];
|
||||||
uint32_t uint;
|
uint32_t uint;
|
||||||
};
|
};
|
||||||
|
|
||||||
// indexed by flag bit
|
// indexed by flag bit
|
||||||
typedef const char *const rfs_inner[RFS_INNER_SIZE];
|
typedef const char *const rfs_inner[RFS_INNER_SIZE];
|
||||||
|
|
||||||
//
|
//
|
||||||
// === ENUMS ===
|
// === ENUMS ===
|
||||||
//
|
//
|
||||||
|
|
||||||
// Tag for generic node tagged union
|
// Tag for generic node tagged union
|
||||||
enum node_type { // NT_ prefix
|
enum node_type { // NT_ prefix
|
||||||
NT_GROUP,
|
NT_GROUP,
|
||||||
NT_RECORD,
|
NT_RECORD,
|
||||||
};
|
};
|
||||||
|
|
||||||
// Record type enum
|
// Record type enum
|
||||||
enum record_type {
|
enum record_type {
|
||||||
_NONE,
|
AACT, ACHR, ACTI, ADDN, ALCH, AMMO,
|
||||||
AACT, ACHR, ACTI, ADDN, ALCH, AMMO,
|
ANIO, APPA, ARMA, ARMO, ARTO, ASPC,
|
||||||
ANIO, APPA, ARMA, ARMO, ARTO, ASPC,
|
ASTP, AVIF, BOOK, BPTD, CAMS, CELL,
|
||||||
ASTP, AVIF, BOOK, BPTD, CAMS, CELL,
|
CLAS, CLDC, CLFM, CLMT, COBJ, COLL,
|
||||||
CLAS, CLDC, CLFM, CLMT, COBJ, COLL,
|
CONT, CPTH, CSTY, DEBR, DIAL, DLBR,
|
||||||
CONT, CPTH, CSTY, DEBR, DIAL, DLBR,
|
DLVW, DOBJ, DOOR, DUAL, ECZN, EFSH,
|
||||||
DLVW, DOBJ, DOOR, DUAL, ECZN, EFSH,
|
ENCH, EQUP, EXPL, EYES, FACT, FLOR,
|
||||||
ENCH, EQUP, EXPL, EYES, FACT, FLOR,
|
FLST, FSTP, FSTS, FURN, GLOB, GMST,
|
||||||
FLST, FSTP, FSTS, FURN, GLOB, GMST,
|
GRAS, GRUP, HAIR, HAZD, HDPT, IDLE,
|
||||||
GRAS, GRUP, HAIR, HAZD, HDPT, IDLE,
|
IDLM, IMAD, IMGS, INFO, INGR, IPCT,
|
||||||
IDLM, IMAD, IMGS, INFO, INGR, IPCT,
|
IPDS, KEYM, KYWD, LAND, LCRT, LCTN,
|
||||||
IPDS, KEYM, KYWD, LAND, LCRT, LCTN,
|
LGTM, LIGH, LSCR, LTEX, LVLI, LVLN,
|
||||||
LGTM, LIGH, LSCR, LTEX, LVLI, LVLN,
|
LVSP, MATO, MATT, MESG, MGEF, MISC,
|
||||||
LVSP, MATO, MATT, MESG, MGEF, MISC,
|
MOVT, MSTT, MUSC, MUST, NAVI, NAVM,
|
||||||
MOVT, MSTT, MUSC, MUST, NAVI, NAVM,
|
NOTE, NPC_, OTFT, PACK, PERK, PGRE,
|
||||||
NOTE, NPC_, OTFT, PACK, PERK, PGRE,
|
PHZD, PROJ, PWAT, QUST, RACE, REFR,
|
||||||
PHZD, PROJ, PWAT, QUST, RACE, REFR,
|
REGN, RELA, REVB, RFCT, RGDL, SCEN,
|
||||||
REGN, RELA, REVB, RFCT, RGDL, SCEN,
|
SCOL, SCPT, SCRL, SHOU, SLGM, SMBN,
|
||||||
SCOL, SCPT, SCRL, SHOU, SLGM, SMBN,
|
SMEN, SMQN, SNCT, SNDR, SOPM, SOUN,
|
||||||
SMEN, SMQN, SNCT, SNDR, SOPM, SOUN,
|
SPEL, SPGD, STAT, TACT, TES4, TREE,
|
||||||
SPEL, SPGD, STAT, TACT, TES4, TREE,
|
TXST, VTYP, WATR, WEAP, WOOP, WRLD,
|
||||||
TXST, VTYP, WATR, WEAP, WOOP, WRLD,
|
WTHR,
|
||||||
WTHR,
|
};
|
||||||
};
|
|
||||||
|
|
||||||
// GRUP type values
|
// GRUP type values
|
||||||
enum group_type { // GT_ prefix
|
enum group_type { // GT_ prefix
|
||||||
GT_TOP = 0,
|
GT_TOP = 0,
|
||||||
GT_WORLD_CHILDREN = 1,
|
GT_WORLD_CHILDREN = 1,
|
||||||
GT_INTERIOR_CELL_BLOCK = 2,
|
GT_INTERIOR_CELL_BLOCK = 2,
|
||||||
GT_INTERIOR_CELL_SUBBLOCK = 3,
|
GT_INTERIOR_CELL_SUBBLOCK = 3,
|
||||||
GT_EXTERIOR_CELL_BLOCK = 4,
|
GT_EXTERIOR_CELL_BLOCK = 4,
|
||||||
GT_EXTERIOR_CELL_SUBBLOCK = 5,
|
GT_EXTERIOR_CELL_SUBBLOCK = 5,
|
||||||
GT_CELL_CHILDREN = 6,
|
GT_CELL_CHILDREN = 6,
|
||||||
GT_TOPIC_CHILDREN = 7,
|
GT_TOPIC_CHILDREN = 7,
|
||||||
GT_CELL_PERSISTENT_CHILDREN = 8,
|
GT_CELL_PERSISTENT_CHILDREN = 8,
|
||||||
GT_CELL_TEMPORARY_CHILDREN = 9,
|
GT_CELL_TEMPORARY_CHILDREN = 9,
|
||||||
};
|
};
|
||||||
|
|
||||||
//
|
//
|
||||||
// === COMPOSITE TYPES ===
|
// === COMPOSITE TYPES ===
|
||||||
//
|
//
|
||||||
|
|
||||||
// Generic node
|
// Generic node
|
||||||
typedef struct node Node;
|
typedef struct node Node;
|
||||||
struct node {
|
struct node {
|
||||||
union {
|
union {
|
||||||
Group *group;
|
Group *group;
|
||||||
Record *record;
|
Record *record;
|
||||||
} header;
|
} header;
|
||||||
char *const data;
|
char *const data;
|
||||||
enum node_type type;
|
enum node_type type;
|
||||||
uint32_t _pad;
|
uint32_t _pad;
|
||||||
};
|
};
|
||||||
|
|
||||||
// calculated timestamp
|
// calculated timestamp
|
||||||
struct timestamp {
|
struct timestamp {
|
||||||
uint16_t year;
|
uint16_t year;
|
||||||
uint8_t month;
|
uint8_t month;
|
||||||
uint8_t day;
|
uint8_t day;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct walker_callbacks {
|
struct walker_callbacks {
|
||||||
void (*pre)(Node n, void *data, void **carry_out);
|
void (*pre)(Node n, void *data, void **carry_out);
|
||||||
void (*post)(Node n, void *data, void **carry_in);
|
void (*post)(Node n, void *data, void **carry_in);
|
||||||
void *data;
|
void *data;
|
||||||
};
|
};
|
||||||
|
|
||||||
//
|
//
|
||||||
// === BINARY DATA OVERLAYS ===
|
// === BINARY DATA OVERLAYS ===
|
||||||
//
|
//
|
||||||
|
|
||||||
#pragma pack(push, 1)
|
#pragma pack(push, 1)
|
||||||
|
|
||||||
// Group header overlay
|
// Group header overlay
|
||||||
struct group {
|
struct group {
|
||||||
Type4 grup; // always RT_GRUP
|
Type4 grup; // always RT_GRUP
|
||||||
uint32_t size; // uncludes the 24 byte group header
|
uint32_t size; // uncludes the 24 byte group header
|
||||||
union {
|
union {
|
||||||
Type4 type; // this may be mangled and should not be relied on
|
Type4 type; // this may be mangled and should not be relied on
|
||||||
formid formid;
|
formid formid;
|
||||||
int32_t number;
|
int32_t number;
|
||||||
int16_t coord[2];
|
int16_t coord[2];
|
||||||
} label; // access determined by the `type` below
|
} label; // access determined by the `type` below
|
||||||
int32_t type; // group_type enum
|
int32_t type; // group_type enum
|
||||||
uint16_t timestamp;
|
uint16_t timestamp;
|
||||||
uint16_t vcinfo;
|
uint16_t vcinfo;
|
||||||
uint32_t unknown;
|
uint32_t unknown;
|
||||||
};
|
};
|
||||||
|
|
||||||
// Record header overlay
|
// Record header overlay
|
||||||
struct record {
|
struct record {
|
||||||
Type4 type;
|
Type4 type;
|
||||||
uint32_t size;
|
uint32_t size;
|
||||||
uint32_t flags;
|
uint32_t flags;
|
||||||
uint32_t formid;
|
uint32_t formid;
|
||||||
uint16_t timestamp;
|
uint16_t timestamp;
|
||||||
uint16_t vcinfo;
|
uint16_t vcinfo;
|
||||||
uint16_t version;
|
uint16_t version;
|
||||||
uint16_t unknown;
|
uint16_t unknown;
|
||||||
};
|
};
|
||||||
|
|
||||||
// Field header overlay
|
// Field header overlay
|
||||||
struct field {
|
struct field {
|
||||||
Type4 type;
|
Type4 type;
|
||||||
uint16_t size;
|
uint16_t size;
|
||||||
};
|
};
|
||||||
|
|
||||||
#pragma pack(pop)
|
#pragma pack(pop)
|
||||||
|
|
||||||
//
|
//
|
||||||
// === LUTs ===
|
// === LUTs ===
|
||||||
//
|
//
|
||||||
|
|
||||||
// record type enum to fourcc value
|
// record type enum to fourcc value
|
||||||
extern const uint32_t rt[RT_SIZE];
|
extern const uint32_t rt[RT_SIZE];
|
||||||
|
|
||||||
// for converting between record_type and record_type_hash enums
|
// for converting between record_type and record_type_hash enums
|
||||||
extern const uint16_t rt2rth[RT_SIZE];
|
extern const uint16_t rt2rth[RT_SIZE];
|
||||||
extern const uint8_t rth2rt[RT_HASH_SIZE];
|
extern const uint8_t rth2rt[RT_HASH_SIZE];
|
||||||
|
|
||||||
// type -> flag mappings
|
// type -> flag mappings
|
||||||
// NULL table pointers indicate no flags
|
// NULL table pointers indicate no flags
|
||||||
// NULL string pointers indicate invalid flag
|
// NULL string pointers indicate invalid flag
|
||||||
extern rfs_inner *const rfs[RT_HASH_SIZE];
|
extern rfs_inner *const rfs[RT_HASH_SIZE];
|
||||||
|
|
||||||
extern rfs_inner *const rfs_refr[RT_HASH_SIZE];
|
extern rfs_inner *const rfs_refr[RT_HASH_SIZE];
|
||||||
|
|
||||||
// Expected (probably) order of top level groups in an esp/esm
|
// Expected (probably) order of top level groups in an esp/esm
|
||||||
extern const enum record_type group_order[GO_SIZE];
|
extern const enum record_type group_order[GO_SIZE];
|
||||||
|
|
||||||
// Printable strings for group types
|
// Printable strings for group types
|
||||||
extern const char *const group_type_strings[GTS_SIZE];
|
extern const char *const group_type_strings[GTS_SIZE];
|
||||||
|
|
||||||
|
//
|
||||||
|
// === FUNCTIONS ===
|
||||||
|
//
|
||||||
|
|
||||||
//
|
// hashes type value into RT_ hash value
|
||||||
// === FUNCTIONS ===
|
inline uint32_t rt_hash(uint32_t type) {
|
||||||
//
|
return uint32_t_msh(type, RT_HASH_BITS, RT_HASH_SEED);
|
||||||
|
}
|
||||||
|
|
||||||
// hashes type value into RT_ hash value
|
/* `espr_walk` walks through the tree structure of the esp/esm binary data
|
||||||
inline uint32_t rt_hash(uint32_t type) {
|
* starting at `data` of `size` bytes.
|
||||||
return uint32_t_msh(type, RT_HASH_BITS, RT_HASH_SEED);
|
*
|
||||||
}
|
* `cb` is a callback that takes a `Node` to process. `pt` is a pointer to
|
||||||
|
* arbitrary data that is passed on to `cb` whenever it is called.
|
||||||
|
*
|
||||||
|
* Data is walked sequentially. Nodes passed to `cb` will be strictly increasing
|
||||||
|
* in terms of memory location within the buffer.
|
||||||
|
*/
|
||||||
|
void espr_walk(char *data, size_t size, struct walker_callbacks cb);
|
||||||
|
|
||||||
/* `espr_walk` walks through the tree structure of the esp/esm binary data
|
/* `espr_print` prints the header of every group and record in the given
|
||||||
* starting at `data` of `size` bytes.
|
* esp/esm binary data.
|
||||||
*
|
*/
|
||||||
* `cb` is a callback that takes a `Node` to process. `pt` is a pointer to
|
void espr_print(char *data, size_t size);
|
||||||
* arbitrary data that is passed on to `cb` whenever it is called.
|
|
||||||
*
|
|
||||||
* Data is walked sequentially. Nodes passed to `cb` will be strictly increasing
|
|
||||||
* in terms of memory location within the buffer.
|
|
||||||
*/
|
|
||||||
void espr_walk(char *data, size_t size, struct walker_callbacks cb);
|
|
||||||
|
|
||||||
/* `espr_print` prints the header of every group and record in the given
|
/* Calculates the size of the esp data if all of the compressed records are
|
||||||
* esp/esm binary data.
|
* decompressed.
|
||||||
*/
|
*/
|
||||||
void espr_print(char *data, size_t size);
|
size_t espr_decompressed_size(char *data, size_t size);
|
||||||
|
|
||||||
/* Calculates the size of the esp data if all of the compressed records are
|
/* Counts the number of formids present in the esp/esm data. This should be
|
||||||
* decompressed.
|
* equal to the number of records.
|
||||||
*/
|
*/
|
||||||
size_t espr_decompressed_size(char *data, size_t size);
|
size_t espr_formid_count(char *data, size_t size);
|
||||||
|
|
||||||
/* Counts the number of formids present in the esp/esm data. This should be
|
/* Copies the data from `data` to `buf` decompressing compressed fields as
|
||||||
* equal to the number of records.
|
* it does so. buf_size should be the value returned from `espr_decompressed_size`,
|
||||||
*/
|
* and `buf` should be at least of that size.
|
||||||
size_t espr_formid_count(char *data, size_t size);
|
*/
|
||||||
|
void espr_decompress(char *data, size_t size, char *buf, size_t buf_size);
|
||||||
/* Copies the data from `data` to `buf` decompressing compressed fields as
|
|
||||||
* it does so. buf_size should be the value returned from `espr_decompressed_size`,
|
|
||||||
* and `buf` should be at least of that size.
|
|
||||||
*/
|
|
||||||
void espr_decompress(char *data, size_t size, char *buf, size_t buf_size);
|
|
||||||
|
|
||||||
// End C++ guard
|
// End C++ guard
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
|
|||||||
892
espReader/LUT.c
892
espReader/LUT.c
File diff suppressed because it is too large
Load Diff
@@ -60,83 +60,87 @@ void decompress_post(Node n, void *data, void **carry_in);
|
|||||||
//
|
//
|
||||||
|
|
||||||
void asserts(void) {
|
void asserts(void) {
|
||||||
// binary overlay size checks
|
// binary overlay size checks
|
||||||
assert(sizeof(Record) == 24); // Record struct incorrect size
|
assert(sizeof(Record) == 24); // Record struct incorrect size
|
||||||
assert(sizeof(Group) == 24); // Group struct incorrect size
|
assert(sizeof(Group) == 24); // Group struct incorrect size
|
||||||
assert(sizeof((Group) { 0 }.label) == 4); // Label union in group struct incorrect size
|
assert(sizeof((Group) { 0 }.label) == 4); // Label union in group struct incorrect size
|
||||||
assert(sizeof(Field) == 6); // Field struct incorrect size
|
assert(sizeof(Field) == 6); // Field struct incorrect size
|
||||||
assert(sizeof(uLongf) == sizeof(uint32_t)); // zlib compatability
|
|
||||||
|
// zlib compatability
|
||||||
|
assert(sizeof(uLongf) == sizeof(uint32_t));
|
||||||
|
assert(sizeof(Bytef) == sizeof(char));
|
||||||
}
|
}
|
||||||
|
|
||||||
void espr_walk(char *data, size_t size, struct walker_callbacks cb) {
|
void espr_walk(char *data, size_t size, struct walker_callbacks cb) {
|
||||||
// check assertions that cannot be checked at compile time
|
// check assertions that cannot be checked at compile time
|
||||||
asserts();
|
asserts();
|
||||||
|
|
||||||
char *data_start = data;
|
char *data_start = data;
|
||||||
|
|
||||||
// check that we are at the start of the file
|
// check that we are at the start of the file
|
||||||
const Type4 type = *(const Type4 *)data;
|
const Type4 type = *(const Type4 *)data;
|
||||||
assert(type.uint == rt[TES4]);
|
assert(type.uint == rt[TES4]);
|
||||||
|
|
||||||
data = walk_concat(data, size, cb);
|
data = walk_concat(data, size, cb);
|
||||||
assert(data == data_start + size);
|
assert(data == data_start + size);
|
||||||
}
|
}
|
||||||
|
|
||||||
void espr_print(char *data, size_t size) {
|
void espr_print(char *data, size_t size) {
|
||||||
struct walker_callbacks cb = { .pre = print_callback };
|
struct walker_callbacks cb = { .pre = print_callback };
|
||||||
espr_walk(data, size, cb);
|
espr_walk(data, size, cb);
|
||||||
}
|
}
|
||||||
|
|
||||||
void print_callback(Node n, void *data, void **carry_out) {
|
void print_callback(Node n, void *data, void **carry_out) {
|
||||||
(void)data;
|
(void)data;
|
||||||
(void)carry_out;
|
(void)carry_out;
|
||||||
switch (n.type) {
|
switch (n.type) {
|
||||||
case NT_GROUP:
|
case NT_GROUP:
|
||||||
print_group_header(n.header.group);
|
print_group_header(n.header.group);
|
||||||
break;
|
break;
|
||||||
case NT_RECORD:
|
case NT_RECORD:
|
||||||
print_record_header(n.header.record);
|
print_record_header(n.header.record);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
assert(false); // invalid node type
|
assert(false); // invalid node type
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t espr_decompressed_size(char *data, size_t size) {
|
size_t espr_decompressed_size(char *data, size_t size) {
|
||||||
size_t dc_size = 0;
|
size_t dc_size = 0;
|
||||||
struct walker_callbacks cb = { .pre = dc_size_cb, .data = &dc_size };
|
struct walker_callbacks cb = { .pre = dc_size_cb, .data = &dc_size };
|
||||||
espr_walk(data, size, cb);
|
espr_walk(data, size, cb);
|
||||||
return dc_size;
|
return dc_size;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Adds the size of every node up, reading decompressed size from compressed records.
|
// Adds the size of every node up, reading decompressed size from compressed records.
|
||||||
void dc_size_cb(Node n, void *data, void **carry_out) {
|
void dc_size_cb(Node n, void *data, void **carry_out) {
|
||||||
(void)carry_out;
|
(void)carry_out;
|
||||||
size_t *dc_size = data;
|
size_t *dc_size = data;
|
||||||
switch (n.type) {
|
switch (n.type) {
|
||||||
case NT_GROUP:
|
case NT_GROUP:
|
||||||
// Only add header size for groups, internals will be walked
|
// Only add header size for groups, internals will be walked
|
||||||
*dc_size += sizeof(Group);
|
*dc_size += sizeof(Group);
|
||||||
break;
|
break;
|
||||||
case NT_RECORD:
|
case NT_RECORD:
|
||||||
// Add the whole record and header, records are leaf-ish
|
// Add the whole record and header, records are leaf-ish
|
||||||
*dc_size += sizeof(Record);
|
*dc_size += sizeof(Record);
|
||||||
if (n.header.record->flags & COMPRESSED_FLAG) {
|
if (n.header.record->flags & COMPRESSED_FLAG) {
|
||||||
// Read decompressed size
|
// Read decompressed size
|
||||||
*dc_size += *((uint32_t *)n.data);
|
*dc_size += *((uint32_t *)n.data);
|
||||||
} else
|
}
|
||||||
*dc_size += n.header.record->size;
|
else
|
||||||
break;
|
*dc_size += n.header.record->size;
|
||||||
default:
|
break;
|
||||||
assert(false); // invalid node type
|
default:
|
||||||
}
|
assert(false); // invalid node type
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t espr_formid_count(char *data, size_t size) {
|
size_t espr_formid_count(char *data, size_t size) {
|
||||||
size_t count = 0;
|
size_t count = 0;
|
||||||
struct walker_callbacks cb = { .pre = formid_count_cb, .data = &count };
|
struct walker_callbacks cb = { .pre = formid_count_cb, .data = &count };
|
||||||
espr_walk(data, size, cb);
|
espr_walk(data, size, cb);
|
||||||
return count;
|
return count;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* FormID <-> Record relationship should be bijective. I do not believe
|
/* FormID <-> Record relationship should be bijective. I do not believe
|
||||||
@@ -144,97 +148,97 @@ size_t espr_formid_count(char *data, size_t size) {
|
|||||||
* otherwise there would be clashes in the id space.
|
* otherwise there would be clashes in the id space.
|
||||||
*/
|
*/
|
||||||
void formid_count_cb(Node n, void *data, void **carry_out) {
|
void formid_count_cb(Node n, void *data, void **carry_out) {
|
||||||
(void)carry_out;
|
(void)carry_out;
|
||||||
size_t *count = data;
|
size_t *count = data;
|
||||||
if (n.type == NT_RECORD) {
|
if (n.type == NT_RECORD) {
|
||||||
(*count)++;
|
(*count)++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct decom {
|
struct decom {
|
||||||
char *buf;
|
char *buf;
|
||||||
size_t remaining;
|
size_t remaining;
|
||||||
};
|
};
|
||||||
|
|
||||||
void espr_decompress(char *data, size_t size, char *buf, size_t buf_size) {
|
void espr_decompress(char *data, size_t size, char *buf, size_t buf_size) {
|
||||||
struct decom s = { .buf = buf, .remaining = buf_size };
|
struct decom s = { .buf = buf, .remaining = buf_size };
|
||||||
struct walker_callbacks cb = { .pre = decompress_pre, .post = decompress_post, .data = &s };
|
struct walker_callbacks cb = { .pre = decompress_pre, .post = decompress_post, .data = &s };
|
||||||
espr_walk(data, size, cb);
|
espr_walk(data, size, cb);
|
||||||
}
|
}
|
||||||
|
|
||||||
void decompress_pre(Node n, void *decom_ptr, void **carry_out) {
|
void decompress_pre(Node n, void *decom_ptr, void **carry_out) {
|
||||||
struct decom *d = decom_ptr;
|
struct decom *d = decom_ptr;
|
||||||
|
|
||||||
switch (n.type) {
|
switch (n.type) {
|
||||||
case NT_RECORD:
|
case NT_RECORD:
|
||||||
// compressed record
|
// compressed record
|
||||||
if (n.header.record->flags & COMPRESSED_FLAG) {
|
if (n.header.record->flags & COMPRESSED_FLAG) {
|
||||||
// copy header
|
// copy header
|
||||||
memcpy(d->buf, n.header.record, sizeof(Record));
|
memcpy(d->buf, n.header.record, sizeof(Record));
|
||||||
|
|
||||||
// copied header reference
|
// copied header reference
|
||||||
Record *header = (Record *)d->buf;
|
Record *header = (Record *)d->buf;
|
||||||
|
|
||||||
// update decom struct
|
// update decom struct
|
||||||
d->remaining -= sizeof(Record);
|
d->remaining -= sizeof(Record);
|
||||||
d->buf += sizeof(Record);
|
d->buf += sizeof(Record);
|
||||||
|
|
||||||
// decompress directly into buffer
|
// decompress directly into buffer
|
||||||
// first 4 bytes are the decompressed size
|
// first 4 bytes are the decompressed size
|
||||||
const uint32_t dc_size = *((uint32_t *)n.data);
|
const uint32_t dc_size = *((uint32_t *)n.data);
|
||||||
uint32_t to_copy = dc_size;
|
uint32_t to_copy = dc_size;
|
||||||
uint32_t cur_size = n.header.record->size - sizeof(uint32_t);
|
uint32_t cur_size = n.header.record->size - sizeof(uint32_t);
|
||||||
char *data_start = n.data + sizeof(uint32_t);
|
char *data_start = n.data + sizeof(uint32_t);
|
||||||
int ret = uncompress((Bytef *)d->buf, (uLongf *)&to_copy, (Bytef *)data_start, (uLong)cur_size);
|
int ret = uncompress((Bytef *)d->buf, (uLongf *)&to_copy, (Bytef *)data_start, (uLong)cur_size);
|
||||||
assert(ret == Z_OK);
|
assert(ret == Z_OK);
|
||||||
assert(to_copy == dc_size);
|
assert(to_copy == dc_size);
|
||||||
|
|
||||||
// update decom struct
|
// update decom struct
|
||||||
d->remaining -= dc_size;
|
d->remaining -= dc_size;
|
||||||
d->buf += dc_size;
|
d->buf += dc_size;
|
||||||
|
|
||||||
// update header data size
|
// update header data size
|
||||||
header->size = dc_size;
|
header->size = dc_size;
|
||||||
|
|
||||||
// unset compressed flag
|
// unset compressed flag
|
||||||
header->flags &= ~COMPRESSED_FLAG;
|
header->flags &= ~COMPRESSED_FLAG;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
// copy record
|
// copy record
|
||||||
size_t record_size = sizeof(Record) + n.header.record->size;
|
size_t record_size = sizeof(Record) + n.header.record->size;
|
||||||
memcpy(d->buf, n.header.record, record_size);
|
memcpy(d->buf, n.header.record, record_size);
|
||||||
|
|
||||||
// update decom
|
// update decom
|
||||||
d->remaining -= record_size;
|
d->remaining -= record_size;
|
||||||
d->buf += record_size;
|
d->buf += record_size;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case NT_GROUP:
|
case NT_GROUP:
|
||||||
// copy header, contents will be copied while walking
|
// copy header, contents will be copied while walking
|
||||||
memcpy(d->buf, n.header.group, sizeof(Group));
|
memcpy(d->buf, n.header.group, sizeof(Group));
|
||||||
|
|
||||||
// save copied header location for post-walk group size recalc
|
// save copied header location for post-walk group size recalc
|
||||||
*carry_out = (void *)d->buf;
|
*carry_out = (void *)d->buf;
|
||||||
|
|
||||||
// update decom
|
// update decom
|
||||||
d->buf += sizeof(Group);
|
d->buf += sizeof(Group);
|
||||||
d->remaining -= sizeof(Group);
|
d->remaining -= sizeof(Group);
|
||||||
|
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
assert(false); // invalid node type
|
assert(false); // invalid node type
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void decompress_post(Node n, void *decom_ptr, void **carry_in) {
|
void decompress_post(Node n, void *decom_ptr, void **carry_in) {
|
||||||
struct decom *d = decom_ptr;
|
struct decom *d = decom_ptr;
|
||||||
|
|
||||||
// only need to handle group resize
|
// only need to handle group resize
|
||||||
if (n.type == NT_GROUP) {
|
if (n.type == NT_GROUP) {
|
||||||
Group *g = (Group *)(*carry_in);
|
Group *g = (Group *)(*carry_in);
|
||||||
uint32_t new_size = (uint32_t)((char *)d->buf - (char *)g);
|
uint32_t new_size = (uint32_t)((char *)d->buf - (char *)g);
|
||||||
g->size = new_size;
|
g->size = new_size;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Unknown data will be some concatenation of groups and records.
|
/* Unknown data will be some concatenation of groups and records.
|
||||||
@@ -243,196 +247,198 @@ void decompress_post(Node n, void *decom_ptr, void **carry_in) {
|
|||||||
* for each segment of unknown data in this concatenation.
|
* for each segment of unknown data in this concatenation.
|
||||||
*/
|
*/
|
||||||
char *walk_concat(char *data, size_t size, struct walker_callbacks cb) {
|
char *walk_concat(char *data, size_t size, struct walker_callbacks cb) {
|
||||||
const char *end = data + size;
|
const char *end = data + size;
|
||||||
while (data != end) {
|
while (data != end) {
|
||||||
assert(data < end);
|
assert(data < end);
|
||||||
|
|
||||||
const Type4 *type = (Type4 *)data;
|
const Type4 *type = (Type4 *)data;
|
||||||
|
|
||||||
// check valid type
|
// check valid type
|
||||||
assert(rt[rth2rt[rt_hash(type->uint)]] == type->uint);
|
assert(rt[rth2rt[rt_hash(type->uint)]] == type->uint);
|
||||||
|
|
||||||
// only need to distinguish between groups and records
|
// only need to distinguish between groups and records
|
||||||
if (type->uint == rt[GRUP])
|
if (type->uint == rt[GRUP])
|
||||||
data = walk_group(data, cb);
|
data = walk_group(data, cb);
|
||||||
else
|
else
|
||||||
data = walk_record(data, cb);
|
data = walk_record(data, cb);
|
||||||
}
|
}
|
||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Walk a group record. Group records are containers for any other type of record,
|
/* Walk a group record. Group records are containers for any other type of record,
|
||||||
* including other group records.
|
* including other group records.
|
||||||
*
|
*
|
||||||
* This function will also call `cb` with the node constructed from this group record.
|
* This function will also call `cb` with the node constructed from this group record.
|
||||||
*/
|
*/
|
||||||
char *walk_group(char *data, struct walker_callbacks cb) {
|
char *walk_group(char *data, struct walker_callbacks cb) {
|
||||||
Group *const header = (Group *const)data;
|
Group *const header = (Group *const)data;
|
||||||
|
|
||||||
// The size in the group header includes the size of the header
|
// The size in the group header includes the size of the header
|
||||||
char *data_start = data + sizeof(Group);
|
char *data_start = data + sizeof(Group);
|
||||||
char *data_end = data + header->size;
|
char *data_end = data + header->size;
|
||||||
size_t data_size = data_end - data_start;
|
size_t data_size = data_end - data_start;
|
||||||
|
|
||||||
Node n = { .header.group = header, .data = data_start, .type = NT_GROUP };
|
Node n = { .header.group = header, .data = data_start, .type = NT_GROUP };
|
||||||
void *carry;
|
void *carry;
|
||||||
|
|
||||||
// Pre-walk callback
|
// Pre-walk callback
|
||||||
if (cb.pre)
|
if (cb.pre)
|
||||||
cb.pre(n, cb.data, &carry);
|
cb.pre(n, cb.data, &carry);
|
||||||
|
|
||||||
// Walk through the concatenation of data inside the group.
|
// Walk through the concatenation of data inside the group.
|
||||||
data = walk_concat(data_start, data_size, cb);
|
data = walk_concat(data_start, data_size, cb);
|
||||||
assert(data == data_end);
|
assert(data == data_end);
|
||||||
|
|
||||||
// Post-walk callback
|
// Post-walk callback
|
||||||
if (cb.post)
|
if (cb.post)
|
||||||
cb.post(n, cb.data, &carry);
|
cb.post(n, cb.data, &carry);
|
||||||
|
|
||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
|
|
||||||
char *walk_record(char *data, struct walker_callbacks cb) {
|
char *walk_record(char *data, struct walker_callbacks cb) {
|
||||||
Record *header = (Record *)data;
|
Record *header = (Record *)data;
|
||||||
assert(header->type.uint != rt[GRUP]);
|
assert(header->type.uint != rt[GRUP]);
|
||||||
|
|
||||||
char *data_start = data + sizeof(Record);
|
char *data_start = data + sizeof(Record);
|
||||||
|
|
||||||
Node n = { .header.record = header, .data = data_start, .type = NT_RECORD };
|
Node n = { .header.record = header, .data = data_start, .type = NT_RECORD };
|
||||||
void *carry;
|
void *carry;
|
||||||
|
|
||||||
/* Pre and post walk callbacks make less sense for record walking as records
|
/* Pre and post walk callbacks make less sense for record walking as records
|
||||||
* are leaf-ish, will still call both here for now as field walking may be
|
* are leaf-ish, will still call both here for now as field walking may be
|
||||||
* added in the future.
|
* added in the future.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
// Pre-walk callback
|
// Pre-walk callback
|
||||||
if (cb.pre)
|
if (cb.pre)
|
||||||
cb.pre(n, cb.data, &carry);
|
cb.pre(n, cb.data, &carry);
|
||||||
|
|
||||||
// Update data ptr based on record size.
|
// Update data ptr based on record size.
|
||||||
data += sizeof(Record) + header->size;
|
data += sizeof(Record) + header->size;
|
||||||
|
|
||||||
// Post-walk callback
|
// Post-walk callback
|
||||||
if (cb.post)
|
if (cb.post)
|
||||||
cb.post(n, cb.data, &carry);
|
cb.post(n, cb.data, &carry);
|
||||||
|
|
||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
|
|
||||||
void print_group_header(Group *header) {
|
void print_group_header(Group *header) {
|
||||||
printf("--- HEADER: GROUP ---\n");
|
printf("--- HEADER: GROUP ---\n");
|
||||||
print_type(header->grup);
|
print_type(header->grup);
|
||||||
printf("Size: %u\n", header->size);
|
printf("Size: %u\n", header->size);
|
||||||
print_group_label(header);
|
print_group_label(header);
|
||||||
assert(header->type < GTS_SIZE);
|
assert(header->type < GTS_SIZE);
|
||||||
printf("Group type: %s\n", group_type_strings[header->type]);
|
printf("Group type: %s\n", group_type_strings[header->type]);
|
||||||
print_timestamp(header->timestamp);
|
print_timestamp(header->timestamp);
|
||||||
printf("Version Control Info: %04x\n", header->vcinfo);
|
printf("Version Control Info: %04x\n", header->vcinfo);
|
||||||
printf("Unknown: %08x\n", header->unknown);
|
printf("Unknown: %08x\n", header->unknown);
|
||||||
}
|
}
|
||||||
|
|
||||||
void print_record_header(Record *header) {
|
void print_record_header(Record *header) {
|
||||||
printf("--- HEADER: RECORD ---\n");
|
printf("--- HEADER: RECORD ---\n");
|
||||||
print_type(header->type);
|
print_type(header->type);
|
||||||
print_record_flags(header);
|
print_record_flags(header);
|
||||||
printf("FormID: %x\n", header->formid);
|
printf("FormID: %x\n", header->formid);
|
||||||
print_timestamp(header->timestamp);
|
print_timestamp(header->timestamp);
|
||||||
printf("Version Control Info: %04x\n", header->vcinfo);
|
printf("Version Control Info: %04x\n", header->vcinfo);
|
||||||
printf("Version: %u\n", header->version);
|
printf("Version: %u\n", header->version);
|
||||||
printf("Unknown: %08x\n", header->unknown);
|
printf("Unknown: %08x\n", header->unknown);
|
||||||
}
|
}
|
||||||
|
|
||||||
void print_group_label(Group *header) {
|
void print_group_label(Group *header) {
|
||||||
printf("Label: ");
|
printf("Label: ");
|
||||||
switch (header->type) {
|
switch (header->type) {
|
||||||
case GT_TOP:
|
case GT_TOP:
|
||||||
print_type4(header->label.type);
|
print_type4(header->label.type);
|
||||||
break;
|
break;
|
||||||
case GT_INTERIOR_CELL_BLOCK:
|
case GT_INTERIOR_CELL_BLOCK:
|
||||||
case GT_INTERIOR_CELL_SUBBLOCK:
|
case GT_INTERIOR_CELL_SUBBLOCK:
|
||||||
printf("%d", header->label.number);
|
printf("%d", header->label.number);
|
||||||
case GT_EXTERIOR_CELL_BLOCK:
|
case GT_EXTERIOR_CELL_BLOCK:
|
||||||
case GT_EXTERIOR_CELL_SUBBLOCK:
|
case GT_EXTERIOR_CELL_SUBBLOCK:
|
||||||
printf("X: %d, Y: %d", header->label.coord[1], header->label.coord[0]);
|
printf("X: %d, Y: %d", header->label.coord[1], header->label.coord[0]);
|
||||||
case GT_WORLD_CHILDREN:
|
case GT_WORLD_CHILDREN:
|
||||||
case GT_CELL_CHILDREN:
|
case GT_CELL_CHILDREN:
|
||||||
case GT_TOPIC_CHILDREN:
|
case GT_TOPIC_CHILDREN:
|
||||||
case GT_CELL_PERSISTENT_CHILDREN:
|
case GT_CELL_PERSISTENT_CHILDREN:
|
||||||
case GT_CELL_TEMPORARY_CHILDREN:
|
case GT_CELL_TEMPORARY_CHILDREN:
|
||||||
printf("FormID[%x]", header->label.formid);
|
printf("FormID[%x]", header->label.formid);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
assert(false); // invalid group type
|
assert(false); // invalid group type
|
||||||
}
|
}
|
||||||
printf("\n");
|
printf("\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
void print_record_flags(Record *header) {
|
void print_record_flags(Record *header) {
|
||||||
printf("Flags:\n");
|
printf("Flags:\n");
|
||||||
|
|
||||||
uint32_t flags = header->flags;
|
uint32_t flags = header->flags;
|
||||||
const uint32_t type = header->type.uint;
|
const uint32_t type = header->type.uint;
|
||||||
|
|
||||||
// print flags
|
// print flags
|
||||||
if (type == rt[REFR]) {
|
if (type == rt[REFR]) {
|
||||||
// TODO
|
// TODO
|
||||||
// REFR requires FormID lookup
|
// REFR requires FormID lookup
|
||||||
flags = 0;
|
flags = 0;
|
||||||
} else {
|
}
|
||||||
rfs_inner *const flag_lut = rfs[rt_hash(type)];
|
else {
|
||||||
if (flag_lut) {
|
rfs_inner *const flag_lut = rfs[rt_hash(type)];
|
||||||
while (flags != 0) {
|
if (flag_lut) {
|
||||||
// will always be >= 0 as flags is not 0
|
while (flags != 0) {
|
||||||
int highest = 31 - __lzcnt(flags);
|
// will always be >= 0 as flags is not 0
|
||||||
assert(highest >= 0);
|
int highest = 31 - __lzcnt(flags);
|
||||||
const char *const str = (*flag_lut)[highest];
|
assert(highest >= 0);
|
||||||
if (str) {
|
const char *const str = (*flag_lut)[highest];
|
||||||
printf(" - %s\n", str);
|
if (str) {
|
||||||
flags -= ((uint32_t)1) << highest;
|
printf(" - %s\n", str);
|
||||||
} else
|
flags -= ((uint32_t)1) << highest;
|
||||||
break;
|
}
|
||||||
}
|
else
|
||||||
}
|
break;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (flags != 0) {
|
if (flags != 0) {
|
||||||
printf("\n\nOriginal flags: %08x\n", header->flags);
|
printf("\n\nOriginal flags: %08x\n", header->flags);
|
||||||
printf("Unhandled flags: %08x\n", flags);
|
printf("Unhandled flags: %08x\n", flags);
|
||||||
assert(false); // unhandled flags
|
assert(false); // unhandled flags
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// This is the Skyrim SE timestamp format
|
// This is the Skyrim SE timestamp format
|
||||||
void print_timestamp(uint16_t _ts) {
|
void print_timestamp(uint16_t _ts) {
|
||||||
Timestamp ts = convert_ts(_ts);
|
Timestamp ts = convert_ts(_ts);
|
||||||
printf("Timestamp: 20x%u-%02u-%02u\n", ts.year, ts.month, ts.day);
|
printf("Timestamp: 20x%u-%02u-%02u\n", ts.year, ts.month, ts.day);
|
||||||
}
|
}
|
||||||
|
|
||||||
void print_type(Type4 type) {
|
void print_type(Type4 type) {
|
||||||
printf("Type: ");
|
printf("Type: ");
|
||||||
print_type4(type);
|
print_type4(type);
|
||||||
printf("\n");
|
printf("\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
void print_type4(Type4 val) {
|
void print_type4(Type4 val) {
|
||||||
// invariant: printed i characters from val.bytes
|
// invariant: printed i characters from val.bytes
|
||||||
for (size_t i = 0; i != 4; i++)
|
for (size_t i = 0; i != 4; i++)
|
||||||
printf("%c", val.bytes[i]);
|
printf("%c", val.bytes[i]);
|
||||||
}
|
}
|
||||||
|
|
||||||
Timestamp convert_ts(uint16_t ts) {
|
Timestamp convert_ts(uint16_t ts) {
|
||||||
/*
|
/*
|
||||||
const uint8_t day = (uint8_t)(ts & day_mask);
|
const uint8_t day = (uint8_t)(ts & day_mask);
|
||||||
const uint8_t month = (uint8_t)((ts >> month_offset) & month_mask);
|
const uint8_t month = (uint8_t)((ts >> month_offset) & month_mask);
|
||||||
const uint16_t year = (ts >> year_offset) & year_mask;
|
const uint16_t year = (ts >> year_offset) & year_mask;
|
||||||
*/
|
*/
|
||||||
|
|
||||||
const uint8_t day = ts & 0xff;
|
const uint8_t day = ts & 0xff;
|
||||||
const uint8_t hb = (ts >> 8) & 0xff;
|
const uint8_t hb = (ts >> 8) & 0xff;
|
||||||
const uint8_t month = ((hb - 1) % 12) + 1;
|
const uint8_t month = ((hb - 1) % 12) + 1;
|
||||||
const uint8_t year = ((hb - 1) / 12 + 3) % 10;
|
const uint8_t year = ((hb - 1) / 12 + 3) % 10;
|
||||||
|
|
||||||
return (Timestamp){ year, month, day };
|
return (Timestamp) { year, month, day };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -25,58 +25,60 @@
|
|||||||
bool buf[NUM];
|
bool buf[NUM];
|
||||||
|
|
||||||
int main() {
|
int main() {
|
||||||
uint32_t seed = 1;
|
uint32_t seed = 1;
|
||||||
bool clash;
|
bool clash;
|
||||||
size_t max = 0;
|
size_t max = 0;
|
||||||
do {
|
do {
|
||||||
if ((seed - 1) % 1000000 == 0)
|
if ((seed - 1) % 1000000 == 0)
|
||||||
printf("Checked up to: %u\r", seed - 1);
|
printf("Checked up to: %u\r", seed - 1);
|
||||||
|
|
||||||
// reset for test
|
// reset for test
|
||||||
memset(buf, 0, sizeof(bool) * NUM);
|
memset(buf, 0, sizeof(bool) * NUM);
|
||||||
clash = false;
|
clash = false;
|
||||||
|
|
||||||
// check for collisions
|
// check for collisions
|
||||||
for (size_t i = 0; i != RT_SIZE; i++) {
|
for (size_t i = 0; i != RT_SIZE; i++) {
|
||||||
uint32_t index = uint32_t_msh(rt[i], BITS, seed);
|
uint32_t index = uint32_t_msh(rt[i], BITS, seed);
|
||||||
if (buf[index]) {
|
if (buf[index]) {
|
||||||
if (i > max) {
|
if (i > max) {
|
||||||
// printf("\nMax: %llu\n", i);
|
// printf("\nMax: %llu\n", i);
|
||||||
max = i;
|
max = i;
|
||||||
}
|
}
|
||||||
clash = true;
|
clash = true;
|
||||||
break;
|
break;
|
||||||
} else {
|
}
|
||||||
buf[index] = true;
|
else {
|
||||||
}
|
buf[index] = true;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// exit if non-clashing seed found
|
// exit if non-clashing seed found
|
||||||
if (!clash) {
|
if (!clash) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
// seed must be odd
|
|
||||||
seed += 2;
|
|
||||||
} while (seed != UINT32_MAX); // is odd, so will be hit
|
|
||||||
|
|
||||||
if (!clash) {
|
// seed must be odd
|
||||||
printf("\nSeed found: %u", seed);
|
seed += 2;
|
||||||
for (size_t i = 0; i != RT_SIZE; i++) {
|
} while (seed != UINT32_MAX); // is odd, so will be hit
|
||||||
char name[5];
|
|
||||||
memcpy(name, &rt[i], sizeof(uint32_t));
|
|
||||||
name[4] = '\0';
|
|
||||||
uint32_t index = uint32_t_msh(rt[i], BITS, seed);
|
|
||||||
if (i % PER_LINE == 0)
|
|
||||||
printf("\n");
|
|
||||||
else
|
|
||||||
printf(" ");
|
|
||||||
printf("RT_%s = %3u,", name, index);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
printf("Seed not found. Max: %llu\n", max);
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
if (!clash) {
|
||||||
|
printf("\nSeed found: %u", seed);
|
||||||
|
for (size_t i = 0; i != RT_SIZE; i++) {
|
||||||
|
char name[5];
|
||||||
|
memcpy(name, &rt[i], sizeof(uint32_t));
|
||||||
|
name[4] = '\0';
|
||||||
|
uint32_t index = uint32_t_msh(rt[i], BITS, seed);
|
||||||
|
if (i % PER_LINE == 0)
|
||||||
|
printf("\n");
|
||||||
|
else
|
||||||
|
printf(" ");
|
||||||
|
printf("RT_%s = %3u,", name, index);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
printf("Seed not found. Max: %llu\n", max);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user