Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Generate unique mempaks ID when formatting them #919

Merged
merged 2 commits into from Apr 12, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
154 changes: 112 additions & 42 deletions src/device/controllers/paks/mempak.c
Expand Up @@ -23,61 +23,131 @@

#include "backends/api/storage_backend.h"
#include "device/controllers/game_controller.h"
#include "main/util.h"

#include <assert.h>
#include <stddef.h>
#include <stdint.h>
#include <string.h>

/* Serialized representation of ID Block
* Only used to ease offsets/pointers computation
* DO NOT DEREFERENCE
*/
#pragma pack(push, 1)
struct id_block_serialized {
uint32_t serial[6];
uint16_t device_id;
uint8_t banks;
uint8_t version;
uint16_t sum;
uint16_t isum;
};
#pragma pack(pop)
#if defined(static_assert)
static_assert(sizeof(struct id_block_serialized) == 32, "id_block_serialized must have a size of 32 bytes");
#endif

void format_mempak(uint8_t* mem)
static void checksum_id_block(unsigned char* ptr,
uint16_t* sum, uint16_t* isum)
{
enum { MPK_PAGE_SIZE = 256 };
size_t i;
uint16_t accu = 0;
for (i = 0; i < offsetof(struct id_block_serialized, sum); i += 2) {
accu += load_beu16((void*)&ptr[i]);
}
*sum = accu;
*isum = UINT16_C(0xfff2) - accu;
}

static const uint8_t page_0[MPK_PAGE_SIZE] =
{
/* Label area */
0x81,0x01,0x02,0x03, 0x04,0x05,0x06,0x07, 0x08,0x09,0x0a,0x0b, 0x0c,0x0d,0x0e,0x0f,
0x10,0x11,0x12,0x13, 0x14,0x15,0x16,0x17, 0x18,0x19,0x1a,0x1b, 0x1c,0x1d,0x1e,0x1f,
/* Main ID area */
0xff,0xff,0xff,0xff, 0x05,0x1a,0x5f,0x13, 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,
0xff,0xff,0xff,0xff, 0xff,0xff,0xff,0xff, 0xff,0xff,0x01,0xff, 0x66,0x25,0x99,0xcd,
/* Unused */
0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,
/* ID area backup #1 */
0xff,0xff,0xff,0xff, 0x05,0x1a,0x5f,0x13, 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,
0xff,0xff,0xff,0xff, 0xff,0xff,0xff,0xff, 0xff,0xff,0x01,0xff, 0x66,0x25,0x99,0xcd,
/* ID area backup #2 */
0xff,0xff,0xff,0xff, 0x05,0x1a,0x5f,0x13, 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,
0xff,0xff,0xff,0xff, 0xff,0xff,0xff,0xff, 0xff,0xff,0x01,0xff, 0x66,0x25,0x99,0xcd,
/* Unused */
0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,
/* ID area backup #3 */
0xff,0xff,0xff,0xff, 0x05,0x1a,0x5f,0x13, 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,
0xff,0xff,0xff,0xff, 0xff,0xff,0xff,0xff, 0xff,0xff,0x01,0xff, 0x66,0x25,0x99,0xcd,
/* Unused */
0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00
};

/* Fill Page 0 with pre-initialized content */
memcpy(mem, page_0, MPK_PAGE_SIZE);

/* Fill INODE page 1 and update it's checkum */
static uint8_t checksum_index_table(size_t count, unsigned char* ptr)
{
unsigned sum = 0;
while (count != 0) {
sum += load_beu8((void*)ptr);
++ptr;
--count;
}

return (uint8_t)sum;
}

static void serialize_id_block(unsigned char *ptr, const uint32_t serial[6], uint16_t device_id, uint8_t banks, uint8_t version) {

size_t i;
/* _ should never be dereferenced - it is only used to ease pointer/offsets computation */
struct id_block_serialized* const _ = (struct id_block_serialized*)ptr;


for (i = 0; i < 6; ++i) {
store_beu32(serial[i], (void*)&_->serial[i]);
}
store_beu16(device_id, (void*)&_->device_id);
store_beu8(banks, (void*)&_->banks);
store_beu8(version, (void*)&_->version);

uint16_t sum, isum;
checksum_id_block(ptr, &sum, &isum);

store_beu16(sum, (void*)&_->sum);
store_beu16(isum, (void*)&_->isum);
}



void format_mempak(uint8_t* mem,
const uint32_t serial[6],
uint16_t device_id,
uint8_t banks,
uint8_t version)
{
enum { MPK_PAGE_SIZE = 256 };

uint8_t* const page_0 = mem + 0*MPK_PAGE_SIZE;
uint8_t* const page_1 = mem + 1*MPK_PAGE_SIZE;
uint8_t* const page_2 = mem + 2*MPK_PAGE_SIZE;
uint8_t* const page_3 = mem + 3*MPK_PAGE_SIZE;

/* Page 0 is divided in 8 x 32-byte blocks:
* 0. reserved
* 1. ID
* 2. reserved
* 3. ID backup #1
* 4. ID backup #2
* 5. reserved
* 6. ID backup #3
* 7. reserved
*/
serialize_id_block(page_0 + 1*32, serial, device_id, banks, version);

memset(page_0 + 0*32, 0, 32);
memset(page_0 + 2*32, 0, 32);
memset(page_0 + 5*32, 0, 32);
memset(page_0 + 7*32, 0, 32);

memcpy(page_0 + 3*32, page_0 + 1*32, 32);
memcpy(page_0 + 4*32, page_0 + 1*32, 32);
memcpy(page_0 + 6*32, page_0 + 1*32, 32);

/* Page 1 holds the index table.
* The first 5 inodes are reserved because the first 5 pages are reserved.
* The first inode page index holds the checksum of the 123 normal nodes.
* The remaining 123 pages are marked empty.
*/
size_t start_page = 5;
memset(mem + 1*MPK_PAGE_SIZE, 0, 2*start_page);
for (i = 1*MPK_PAGE_SIZE+2*start_page; i < 2*MPK_PAGE_SIZE; i += 2) {
mem[i+0] = 0x00;
mem[i+1] = 0x03;
size_t last_page = 128;
size_t i;
memset(page_1, 0, 2*start_page);
for(i = start_page; i < last_page; ++i) {
store_beu16(UINT16_C(0x0003), page_1 + 2*i);
}
mem[1*MPK_PAGE_SIZE + 1] = 0x71;
page_1[1] = checksum_index_table(2*(last_page-start_page), page_1 + 2*start_page);

/* Page 2 is identical to page 1 */
memcpy(mem + 2*MPK_PAGE_SIZE, mem + 1*MPK_PAGE_SIZE, MPK_PAGE_SIZE);
/* Page 2 is a backup of Page 1 */
memcpy(page_2, page_1, MPK_PAGE_SIZE);

/* Remaining pages DIR+DATA (3...) are initialized with 0x00 */
memset(mem + 3*MPK_PAGE_SIZE, 0, MEMPAK_SIZE - 3*MPK_PAGE_SIZE);
memset(page_3, 0, MEMPAK_SIZE - 3*MPK_PAGE_SIZE);
}


Expand Down
10 changes: 9 additions & 1 deletion src/device/controllers/paks/mempak.h
Expand Up @@ -27,6 +27,10 @@

struct storage_backend_interface;

#define DEFAULT_MEMPAK_DEVICEID UINT16_C(0x0001)
#define DEFAULT_MEMPAK_BANKS UINT8_C(0x01)
#define DEFAULT_MEMPAK_VERSION UINT8_C(0x00)

struct mempak
{
void* storage;
Expand All @@ -35,7 +39,11 @@ struct mempak

enum { MEMPAK_SIZE = 0x8000 };

void format_mempak(uint8_t* mem);
void format_mempak(uint8_t* mem,
const uint32_t serial[6],
uint16_t device_id,
uint8_t banks,
uint8_t version);

void init_mempak(struct mempak* mpk,
void* storage,
Expand Down
20 changes: 19 additions & 1 deletion src/main/main.c
Expand Up @@ -134,6 +134,9 @@ static void* l_paks[GAME_CONTROLLERS_COUNT][PAK_MAX_SIZE];
static const struct pak_interface* l_ipaks[PAK_MAX_SIZE];
static size_t l_pak_type_idx[6];

/* PRNG state - used for Mempaks ID generation */
static struct xoshiro256pp_state l_mpk_idgen;

/*********************************************************************************************************
* static functions
*/
Expand Down Expand Up @@ -1013,7 +1016,19 @@ static void open_mpk_file(struct file_storage* fstorage)
if (ret == (int)file_open_error) {
/* if file doesn't exists provide default content */
for(i = 0; i < GAME_CONTROLLERS_COUNT; ++i) {
format_mempak(fstorage->data + i * MEMPAK_SIZE);

/* Generate a random serial ID */
uint32_t serial[6];
size_t k;
for (k = 0; k < 6; ++k) {
serial[k] = xoshiro256pp_next(&l_mpk_idgen);
}

format_mempak(fstorage->data + i * MEMPAK_SIZE,
serial,
DEFAULT_MEMPAK_DEVICEID,
DEFAULT_MEMPAK_BANKS,
DEFAULT_MEMPAK_VERSION);
}
}
}
Expand Down Expand Up @@ -1470,6 +1485,9 @@ m64p_error main_run(void)
break;
}

/* Seed MPK ID gen using current time */
l_mpk_idgen = xoshiro256pp_seed((uint64_t)time(NULL));

/* take the r4300 emulator mode from the config file at this point and cache it in a global variable */
emumode = ConfigGetParamInt(g_CoreConfig, "R4300Emulator");

Expand Down