Created new functions for serialization and deserialization

This commit is contained in:
Sergio Martin 2024-01-18 19:56:30 +01:00
parent 17bb8c99d8
commit 4530713b01
10 changed files with 404 additions and 84 deletions

View File

@ -1,32 +1,33 @@
quickerNES
============
-----------
quickerNES is an attempt to modernizing and improving the performance of quickNES, the fastest NES emulator in the interwebs (as far as I know). The goals for this project are, in order of importance:
quickerNES is an attempt to modernizing and improving the performance of [quickNES](https://github.com/kode54/QuickNES). The goals for this project are, in order of importance:
- Improve overall emulation performance even more
- Improve overall emulation performance for modern (x86) CPUs
- Modernize the code base with best programming practices, including CI tests, benchmarks, and coverage analysis
- Add support for more mappers, controllers, and features supported by other emulators
- Improve accuracy, if possible
The main aim is to improve the performance of skip (non-rendering, no-audio) frame advances for brute force botting. (See: [JaffarPlus](https://github.com/SergioMartin86/jaffarPlus)). However, if this work might help with homebrew emulation and other people having more fun, then much better!
The main aim is to improve the performance of headless re-recording for TASing and botting (See: [JaffarPlus](https://github.com/SergioMartin86/jaffarPlus)) purposes. However, if this work can help regular play emulation, then much better.
Changes
=========
--------
- Optimizations made in the CPU emulation core, including:
+ Forced alignment at the start of a page to prevent crossing cache line boundaries
+ Simplifying instruction decode
+ Simplifying the 6502 CPU instruction fetching and decoding
- Minimize compiled code size to reduce pressure on L1i cache
- Reduce heap allocations
- General code reorganization (make it header only to help compiler optimizations)
Credits
=========
---------
- quickNES was originally by Shay Green (a.k.a. [Blaarg](http://www.slack.net/~ant/)) under the GNU GPLv2 license. The source code is still located [here](https://github.com/kode54/QuickNES)
- The code was later improved and maintained by Christopher Snowhill (a.k.a. [kode54](https://kode54.net/))
- I could trace further contributions (e.g., new mappers) by retrowertz, CaH4e3, some adaptations from the [FCEUX emulator](https://github.com/TASEmulators/fceux) (see mapper021)
- The latest version of the code is maintained by Libretro's community [here](https://github.com/libretro/QuickNES_Core)
- For the interactive player, this project uses a modified version of [HeadlessQuickNES (HQN)](https://github.com/Bindernews/HeadlessQuickNes) by Drew (Binder News)
- For the interactive player, this project drew some code from [HeadlessQuickNES (HQN)](https://github.com/Bindernews/HeadlessQuickNes) by Drew (Binder News)
- We use some of the [NES test rom set](https://github.com/christopherpow/nes-test-roms) made by multiple authors and gathered by Christopher Pow et al.
- We also use some movies from the [TASVideos](tasvideos.org) website for testing. These movies are copied into this repository with authorization under the Creative Commons Attribution 2.0 license.

View File

@ -86,15 +86,13 @@ class EmuInstance
}
inline size_t getStateSize() const { return _stateSize; }
inline size_t getLiteStateSize() const { return _liteStateSize; }
inline std::string getRomSHA1() const { return _romSHA1String; }
inline hash_t getStateHash() const
{
MetroHash128 hash;
uint8_t stateData[_stateSize];
serializeState(stateData);
hash.Update(getLowMem(), _LOW_MEM_SIZE);
hash.Update(getHighMem(), _HIGH_MEM_SIZE);
hash.Update(getNametableMem(), _NAMETABLES_MEM_SIZE);
@ -137,8 +135,11 @@ inline void loadROMFile(const std::string& romFilePath)
status = loadROMFileImpl(_romData);
if (status == false) EXIT_WITH_ERROR("Could not process ROM file: %s\n", romFilePath.c_str());
// Detecting state size
// Detecting full state size
_stateSize = getStateSizeImpl();
// Detecting lite state size
_liteStateSize = getLiteStateSizeImpl();
}
// Virtual functions
@ -153,16 +154,20 @@ inline void loadROMFile(const std::string& romFilePath)
virtual void serializeState(uint8_t* state) const = 0;
virtual void deserializeState(const uint8_t* state) = 0;
virtual size_t getStateSizeImpl() const = 0;
virtual size_t getLiteStateSizeImpl() const { return getStateSizeImpl(); };
virtual void doSoftReset() = 0;
virtual void doHardReset() = 0;
virtual std::string getCoreName() const = 0;
virtual void* getInternalEmulatorPointer() const = 0;
protected:
EmuInstance() = default;
// Storage for the state
// Storage for the light state size
size_t _liteStateSize;
// Storage for the full state size
size_t _stateSize;
// Flag to determine whether to enable/disable rendering

View File

@ -16,13 +16,14 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
// Nes_Emu 0.7.0
#include <cstdio>
#include <string>
#include "blargg_common.h"
#include "Nes_Apu.h"
#include "Nes_Cpu.h"
#include "Nes_Ppu.h"
#include "Nes_Mapper.h"
#include "Nes_State.h"
#include <cstdio>
/*
New mapping distribution by Sergio Martin (eien86)
@ -216,12 +217,321 @@ public:
return 0;
}
size_t getLightweightStateSize()
size_t getLiteStateSize() const
{
size_t size = 0;
size += sizeof(nes_state_t);
size += sizeof(registers_t);
size += sizeof(ppu_state_t);
size += sizeof(Nes_Apu::apu_state_t);
size += sizeof(joypad_state_t);
size += mapper->state_size;
size += low_ram_size;
size += Nes_Ppu::spr_ram_size;
size_t nametable_size = 0x800;
if (ppu.nt_banks [3] >= &ppu.impl->nt_ram [0xC00] ) nametable_size = 0x1000;
size += nametable_size;
if ( ppu.chr_is_writable ) size += ppu.chr_size;
if ( sram_present ) size += impl->sram_size;
return size;
}
size_t getStateSize() const
{
size_t size = 0;
size += sizeof(char[4]); // NESS Block
size += sizeof(uint32_t); // Block Size
size += sizeof(char[4]); // TIME Block
size += sizeof(uint32_t); // Block Size
size += sizeof(nes_state_t);
size += sizeof(char[4]); // CPUR Block
size += sizeof(uint32_t); // Block Size
size += sizeof(registers_t);
size += sizeof(char[4]); // PPUR Block
size += sizeof(uint32_t); // Block Size
size += sizeof(ppu_state_t);
size += sizeof(char[4]); // APUR Block
size += sizeof(uint32_t); // Block Size
size += sizeof(Nes_Apu::apu_state_t);
size += sizeof(char[4]); // CTRL Block
size += sizeof(uint32_t); // Block Size
size += sizeof(joypad_state_t);
size += sizeof(char[4]); // MAPR Block
size += sizeof(uint32_t); // Block Size
size += mapper->state_size;
size += sizeof(char[4]); // LRAM Block
size += sizeof(uint32_t); // Block Size
size += low_ram_size;
size += sizeof(char[4]); // SPRT Block
size += sizeof(uint32_t); // Block Size
size += Nes_Ppu::spr_ram_size;
size += sizeof(char[4]); // NTAB Block
size += sizeof(uint32_t); // Block Size
size_t nametable_size = 0x800;
if (ppu.nt_banks [3] >= &ppu.impl->nt_ram [0xC00] ) nametable_size = 0x1000;
size += nametable_size;
if ( ppu.chr_is_writable )
{
size += sizeof(char[4]); // CHRR Block
size += sizeof(uint32_t); // Block Size
size += ppu.chr_size;
}
if ( sram_present )
{
size += sizeof(char[4]); // SRAM Block
size += sizeof(uint32_t); // Block Size
size += impl->sram_size;
}
size += sizeof(char[4]); // gend Block
size += sizeof(uint32_t); // Block Size
return size;
}
size_t serializeState(uint8_t* buffer) const
{
size_t pos = 0;
std::string headerCode;
const uint32_t headerSize = sizeof(char) * 4;
uint32_t blockSize = 0;
void* dataSource;
headerCode = "NESS"; // NESS Block
blockSize = 0xFFFFFFFF;
memcpy(&buffer[pos], headerCode.data(), headerSize); pos += headerSize;
memcpy(&buffer[pos], &blockSize, headerSize); pos += headerSize;
headerCode = "TIME"; // TIME Block
nes_state_t state = nes;
state.timestamp *= 5;
blockSize = sizeof(nes_state_t);
dataSource = (void*) &state;
memcpy(&buffer[pos], headerCode.data(), headerSize); pos += headerSize;
memcpy(&buffer[pos], &blockSize, headerSize); pos += headerSize;
memcpy(&buffer[pos], dataSource, blockSize); pos += blockSize;
headerCode = "CPUR"; // CPUR Block
cpu_state_t s;
memset( &s, 0, sizeof s );
s.pc = r.pc;
s.s = r.sp;
s.a = r.a;
s.x = r.x;
s.y = r.y;
s.p = r.status;
blockSize = sizeof(cpu_state_t);
dataSource = (void*) &s;
memcpy(&buffer[pos], headerCode.data(), headerSize); pos += headerSize;
memcpy(&buffer[pos], &blockSize, headerSize); pos += headerSize;
memcpy(&buffer[pos], dataSource, blockSize); pos += blockSize;
headerCode = "PPUR"; // PPUR Block
blockSize = sizeof(ppu_state_t);
dataSource = (void*) &ppu;
memcpy(&buffer[pos], headerCode.data(), headerSize); pos += headerSize;
memcpy(&buffer[pos], &blockSize, headerSize); pos += headerSize;
memcpy(&buffer[pos], dataSource, blockSize); pos += blockSize;
headerCode = "APUR"; // APUR Block
Nes_Apu::apu_state_t apuState;
impl->apu.save_state(&apuState);
blockSize = sizeof(Nes_Apu::apu_state_t);
memcpy(&buffer[pos], headerCode.data(), headerSize); pos += headerSize;
memcpy(&buffer[pos], &blockSize, headerSize); pos += headerSize;
memcpy(&buffer[pos], &apuState, blockSize); pos += blockSize;
headerCode = "CTRL"; // CTRL Block
blockSize = sizeof(joypad_state_t);
dataSource = (void*) &joypad;
memcpy(&buffer[pos], headerCode.data(), headerSize); pos += headerSize;
memcpy(&buffer[pos], &blockSize, headerSize); pos += headerSize;
memcpy(&buffer[pos], dataSource, blockSize); pos += blockSize;
headerCode = "MAPR"; // MAPR Block
blockSize = mapper->state_size;
dataSource = (void*) mapper->state;
memcpy(&buffer[pos], headerCode.data(), headerSize); pos += headerSize;
memcpy(&buffer[pos], &blockSize, headerSize); pos += headerSize;
memcpy(&buffer[pos], dataSource, blockSize); pos += blockSize;
headerCode = "LRAM"; // LRAM Block
blockSize = low_ram_size;
dataSource = (void*) low_mem;
memcpy(&buffer[pos], headerCode.data(), headerSize); pos += headerSize;
memcpy(&buffer[pos], &blockSize, headerSize); pos += headerSize;
memcpy(&buffer[pos], dataSource, blockSize); pos += blockSize;
headerCode = "SPRT"; // SPRT Block
blockSize = Nes_Ppu::spr_ram_size;
dataSource = (void*) ppu.spr_ram;
memcpy(&buffer[pos], headerCode.data(), headerSize); pos += headerSize;
memcpy(&buffer[pos], &blockSize, headerSize); pos += headerSize;
memcpy(&buffer[pos], dataSource, blockSize); pos += blockSize;
headerCode = "NTAB"; // NTAB Block
size_t nametable_size = 0x800;
if (ppu.nt_banks [3] >= &ppu.impl->nt_ram [0xC00] ) nametable_size = 0x1000;
blockSize = nametable_size;
dataSource = (void*) ppu.impl->nt_ram;
memcpy(&buffer[pos], headerCode.data(), headerSize); pos += headerSize;
memcpy(&buffer[pos], &blockSize, headerSize); pos += headerSize;
memcpy(&buffer[pos], dataSource, blockSize); pos += blockSize;
if ( ppu.chr_is_writable )
{
headerCode = "CHRR"; // CHRR Block
blockSize = ppu.chr_size;
dataSource = (void*) ppu.impl->chr_ram;
memcpy(&buffer[pos], headerCode.data(), headerSize); pos += headerSize;
memcpy(&buffer[pos], &blockSize, headerSize); pos += headerSize;
memcpy(&buffer[pos], dataSource, blockSize); pos += blockSize;
}
if ( sram_present )
{
headerCode = "SRAM"; // SRAM Block
blockSize = impl->sram_size;
dataSource = (void*) impl->sram;
memcpy(&buffer[pos], headerCode.data(), headerSize); pos += headerSize;
memcpy(&buffer[pos], &blockSize, headerSize); pos += headerSize;
memcpy(&buffer[pos], dataSource, blockSize); pos += blockSize;
}
headerCode = "gend"; // gend Block
blockSize = 0;
memcpy(&buffer[pos], headerCode.data(), headerSize); pos += headerSize;
memcpy(&buffer[pos], &blockSize, headerSize); pos += headerSize;
return pos; // Bytes written
}
size_t deserializeState(const uint8_t* buffer)
{
disable_rendering();
error_count = 0;
ppu.burst_phase = 0; // avoids shimmer when seeking to same time over and over
size_t pos = 0;
const uint32_t headerSize = sizeof(char) * 4;
uint32_t blockSize = 0;
// NESS Block
pos += headerSize;
pos += headerSize;
// TIME Block
nes_state_t nesState;
pos += headerSize;
pos += headerSize;
blockSize = sizeof(nes_state_t);
memcpy(&nesState, &buffer[pos], blockSize); pos += blockSize;
nes = nesState;
nes.timestamp /= 5;
// CPUR Block
cpu_state_t s;
blockSize = sizeof(cpu_state_t);
pos += headerSize;
pos += headerSize;
memcpy((void*) &s, &buffer[pos], blockSize); pos += blockSize;
r.pc = s.pc;
r.sp = s.s;
r.a = s.a;
r.x = s.x;
r.y = s.y;
r.status = s.p;
// PPUR Block
blockSize = sizeof(ppu_state_t);
pos += headerSize;
pos += headerSize;
memcpy((void*) &ppu, &buffer[pos], blockSize); pos += blockSize;
// APUR Block
Nes_Apu::apu_state_t apuState;
blockSize = sizeof(Nes_Apu::apu_state_t);
pos += headerSize;
pos += headerSize;
memcpy(&apuState, &buffer[pos], blockSize);
pos += blockSize;
impl->apu.load_state(apuState);
impl->apu.end_frame( -(int) nes.timestamp / ppu_overclock );
// CTRL Block
blockSize = sizeof(joypad_state_t);
pos += headerSize;
pos += headerSize;
memcpy((void*) &joypad, &buffer[pos], blockSize); pos += blockSize;
// MAPR Block
mapper->default_reset_state();
blockSize = mapper->state_size;
pos += headerSize;
pos += headerSize;
memcpy((void*) mapper->state, &buffer[pos], blockSize); pos += blockSize;
mapper->apply_mapping();
// LRAM Block
blockSize = low_ram_size;
pos += headerSize;
pos += headerSize;
memcpy((void*) low_mem, &buffer[pos], blockSize); pos += blockSize;
// SPRT Block
blockSize = Nes_Ppu::spr_ram_size;
pos += headerSize;
pos += headerSize;
memcpy((void*) ppu.spr_ram, &buffer[pos], blockSize); pos += blockSize;
// NTAB Block
size_t nametable_size = 0x800;
if (ppu.nt_banks [3] >= &ppu.impl->nt_ram [0xC00] ) nametable_size = 0x1000;
blockSize = nametable_size;
pos += headerSize;
pos += headerSize;
memcpy((void*) ppu.impl->nt_ram, &buffer[pos], blockSize); pos += blockSize;
if ( ppu.chr_is_writable )
{
// CHRR Block
blockSize = ppu.chr_size;
pos += headerSize;
pos += headerSize;
memcpy((void*) ppu.impl->chr_ram, &buffer[pos], blockSize); pos += blockSize;
}
if ( sram_present )
{
// SRAM Block
blockSize = impl->sram_size;
pos += headerSize;
pos += headerSize;
memcpy((void*) impl->sram, &buffer[pos], blockSize); pos += blockSize;
enable_sram(true);
}
// headerCode = "gend"; // gend Block
pos += headerSize;
pos += headerSize;
return pos; // Bytes read
}
void reset( bool full_reset, bool erase_battery_ram )
{
if ( full_reset )
@ -347,50 +657,50 @@ public:
void load_state( Nes_State_ const& in )
{
disable_rendering();
error_count = 0;
// disable_rendering();
// error_count = 0;
if ( in.nes_valid )
nes = in.nes;
// if ( in.nes_valid )
// nes = in.nes;
// always use frame count
ppu.burst_phase = 0; // avoids shimmer when seeking to same time over and over
nes.frame_count = in.nes.frame_count;
if ( (frame_count_t) nes.frame_count == invalid_frame_count )
nes.frame_count = 0;
// // always use frame count
// ppu.burst_phase = 0; // avoids shimmer when seeking to same time over and over
// nes.frame_count = in.nes.frame_count;
// if ( (frame_count_t) nes.frame_count == invalid_frame_count )
// nes.frame_count = 0;
if ( in.cpu_valid )
cpu::r = *in.cpu;
// if ( in.cpu_valid )
// cpu::r = *in.cpu;
if ( in.joypad_valid )
joypad = *in.joypad;
// if ( in.joypad_valid )
// joypad = *in.joypad;
if ( in.apu_valid )
{
impl->apu.load_state( *in.apu );
// prevent apu from running extra at beginning of frame
impl->apu.end_frame( -(int) nes.timestamp / ppu_overclock );
}
else
{
impl->apu.reset();
}
// if ( in.apu_valid )
// {
// impl->apu.load_state( *in.apu );
// // prevent apu from running extra at beginning of frame
// impl->apu.end_frame( -(int) nes.timestamp / ppu_overclock );
// }
// else
// {
// impl->apu.reset();
// }
ppu.load_state( in );
// ppu.load_state( in );
if ( in.ram_valid )
memcpy( cpu::low_mem, in.ram, in.ram_size );
// if ( in.ram_valid )
// memcpy( cpu::low_mem, in.ram, in.ram_size );
sram_present = false;
if ( in.sram_size )
{
sram_present = true;
memcpy( impl->sram, in.sram, min( (int) in.sram_size, (int) sizeof impl->sram ) );
enable_sram( true ); // mapper can override (read-only, unmapped, etc.)
}
// sram_present = false;
// if ( in.sram_size )
// {
// sram_present = true;
// // memcpy( impl->sram, in.sram, min( (int) in.sram_size, (int) sizeof impl->sram ) );
// enable_sram( true ); // mapper can override (read-only, unmapped, etc.)
// }
if ( in.mapper_valid ) // restore last since it might reconfigure things
mapper->load_state( *in.mapper );
// if ( in.mapper_valid ) // restore last since it might reconfigure things
// mapper->load_state( *in.mapper );
}
void irq_changed()

View File

@ -42,7 +42,8 @@ public:
const uint8_t* getHostPixels () const { return emu.ppu.host_pixels; }
size_t getLightweightStateSize() { return emu.getLightweightStateSize(); }
size_t getLiteStateSize() const { return emu.getLiteStateSize(); }
size_t getStateSize() const { return emu.getStateSize(); }
// Basic emulation
@ -140,6 +141,8 @@ public:
// Save emulator state
void save_state( Nes_State* s ) const { emu.save_state( s ); }
const char * save_state( Auto_File_Writer ) const;
size_t serializeState (uint8_t* buffer) const { return emu.serializeState(buffer); }
size_t deserializeState (const uint8_t* buffer) { return emu.deserializeState(buffer); }
// Load state into emulator
void load_state( Nes_State const& );

View File

@ -68,6 +68,9 @@ public:
// $2006 and $2007 accesses (but not due to PPU scanline rendering).
virtual void a12_clocked();
void* state;
unsigned state_size;
protected:
// Services provided for derived mapper classes
Nes_Mapper();
@ -147,9 +150,7 @@ protected:
// apply_mapping().
virtual void read_state( mapper_state_t const& );
// Apply current mapping state to hardware. Called after reading mapper state
// from a snapshot.
virtual void apply_mapping() = 0;
// Called by default reset() before apply_mapping() is called.
virtual void reset_state() { }
@ -161,11 +162,9 @@ protected:
Nes_Cart const* cart_;
Nes_Core* emu_;
private:
void* state;
unsigned state_size;
// Apply current mapping state to hardware. Called after reading mapper state
// from a snapshot.
virtual void apply_mapping() = 0;
void default_reset_state();
};

View File

@ -3,6 +3,7 @@
#include "Nes_Ppu_Impl.h"
#include <cstdio>
#include <string.h>
#include "blargg_endian.h"
#include "Nes_State.h"

View File

@ -30,6 +30,10 @@ public:
static const uint16_t buffer_height = image_height;
enum { spr_ram_size = 0x100 };
uint8_t* nt_banks [4];
bool chr_is_writable;
long chr_size;
int write_2007( int );
// Host palette
@ -67,9 +71,10 @@ public:
uint8_t* getSpriteRAM () { return spr_ram; }
uint16_t getSpriteRAMSize () { return spr_ram_size; }
protected:
uint8_t spr_ram [spr_ram_size];
void all_tiles_modified();
protected:
void begin_frame();
void run_hblank( int );
int sprite_height() const { return (w2000 >> 2 & 8) + 8; }
@ -90,7 +95,6 @@ protected: //friend class Nes_Ppu_Rendering; private:
void capture_palette();
bool any_tiles_modified;
bool chr_is_writable;
void update_tiles( int first_tile );
typedef uint32_t cache_t;
@ -128,7 +132,7 @@ private:
return ret;
}
uint8_t* nt_banks [4];
bool mmc24_enabled;
uint8_t mmc24_latched [2];
@ -136,7 +140,6 @@ private:
// CHR data
uint8_t const* chr_data; // points to chr ram when there is no read-only data
uint8_t* chr_ram; // always points to impl->chr_ram; makes write_2007() faster
long chr_size;
uint8_t const* map_chr( int addr ) { return &chr_data [map_chr_addr( addr )]; }
// CHR cache
@ -147,7 +150,7 @@ private:
uint8_t modified_tiles [chr_tile_count / 8];
uint32_t align_;
};
void all_tiles_modified();
void update_tile( int index );
};

View File

@ -113,7 +113,7 @@ const char * Nes_State_::write_blocks( Nes_File_Writer& out ) const
s.p = cpu->status;
RETURN_ERR( write_nes_state( out, s ) );
}
if ( ppu_valid )
{
ppu_state_t s = *ppu;

View File

@ -26,9 +26,6 @@ class QuickerNESInstance : public EmuInstance
{
// Loading rom data
auto result = _nes->load_ines((uint8_t*)romData.data());
// printf("Lightweight size: %lu vs Full Size: %lu\n", _nes->getLightweightStateSize(), getStateSizeImpl());
// exit(0);
return result == 0;
}
@ -40,16 +37,12 @@ class QuickerNESInstance : public EmuInstance
void serializeState(uint8_t* state) const
{
Mem_Writer w(state, _stateSize, 0);
Auto_File_Writer a(w);
_nes->save_state(a);
_nes->serializeState(state);
}
void deserializeState(const uint8_t* state)
{
Mem_File_Reader r(state, _stateSize);
Auto_File_Reader a(r);
_nes->load_state(a);
_nes->deserializeState(state);
}
void advanceStateImpl(const inputType controller1, const inputType controller2) override
@ -66,13 +59,14 @@ class QuickerNESInstance : public EmuInstance
private:
inline size_t getStateSizeImpl() const
inline size_t getStateSizeImpl() const override
{
// Using dry writer to just obtain the state size
Dry_Writer w;
Auto_File_Writer a(w);
_nes->save_state(a);
return w.size();
return _nes->getStateSize();
}
inline size_t getLiteStateSizeImpl() const override
{
return _nes->getLiteStateSize();
}
// Video buffer

View File

@ -92,9 +92,12 @@ int main(int argc, char *argv[])
// Getting initial hash
auto initialHash = e.getStateHash();
// Getting state size
// Getting full state size
const auto stateSize = e.getStateSize();
// Getting lite state size
const auto liteStateSize = e.getLiteStateSize();
// Getting actual ROM SHA1
auto romSHA1 = e.getRomSHA1();
@ -124,7 +127,8 @@ int main(int argc, char *argv[])
printf("[] Sequence File: '%s'\n", sequenceFilePath.c_str());
printf("[] Sequence Length: %lu\n", sequenceLength);
printf("[] Initial State Hash: 0x%lX%lX\n", initialHash.first, initialHash.second);
printf("[] State Size: %lu bytes\n", stateSize);
printf("[] Full State Size: %lu bytes\n", stateSize);
printf("[] Lite State Size: %lu bytes\n", liteStateSize);
printf("[] ********** Running Test **********\n");
fflush(stdout);