Skip to content

Commit

Permalink
Merge pull request #216 from libretro/nvraminit
Browse files Browse the repository at this point in the history
Always format the NVRAM if no saved NVRAM exists or is blank
  • Loading branch information
trapexit authored May 6, 2024
2 parents cacba23 + 4991ac1 commit 5121aa6
Show file tree
Hide file tree
Showing 6 changed files with 200 additions and 63 deletions.
95 changes: 95 additions & 0 deletions libopera/discdata.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
#include "extern_c.h"

#include <stdint.h>

EXTERN_C_BEGIN

/*
Define the position of the primary label on each Opera disc, the
block offset between avatars, and the index of the last avatar
(i.e. the avatar count minus one). The latter figure *must* match
the ROOT_HIGHEST_AVATAR figure from "filesystem.h", as the same
File structure is use to read the label at boot time, and to provide
access to the root directory.
*/

#define DISC_LABEL_RECORD_TYPE 1
#define DISC_BLOCK_SIZE 2048
#define DISC_LABEL_OFFSET 225
#define DISC_LABEL_AVATAR_DELTA 32786
#define DISC_LABEL_HIGHEST_AVATAR 7
#define DISC_TOTAL_BLOCKS 330000

#define ROOT_HIGHEST_AVATAR 7
#define FILESYSTEM_MAX_NAME_LEN 32

#define VOLUME_STRUCTURE_OPERA_READONLY 1
#define VOLUME_STRUCTURE_LINKED_MEM 2

#define NVRAM_VOLUME_UNIQUE_ID -1
#define NVRAM_ROOT_UNIQUE_ID -2
#define VOLUME_SYNC_BYTE 'Z'
#define VOLUME_SYNC_BYTE_LEN 5
#define VOLUME_COM_LEN 32
#define VOLUME_ID_LEN 32

/*
// This disc won't necessarily cause a reboot when inserted. This flag is
// advisory ONLY. Only by checking with cdromdipir can you be really sure.
// Place in dl_VolumeFlags. Note: the first volume gets this flag also.
*/
#define VOLUME_FLAGS_DATADISC 0x01

/*
Data structures written on CD disc (Compact Disc disc?)
*/
typedef struct DiscLabel DiscLabel;
struct DiscLabel
{
uint8_t dl_RecordType; /* Should contain 1 */
uint8_t dl_VolumeSyncBytes[VOLUME_SYNC_BYTE_LEN]; /* Synchronization byte */
uint8_t dl_VolumeStructureVersion; /* Should contain 1 */
uint8_t dl_VolumeFlags; /* Should contain 0 */
uint8_t dl_VolumeCommentary[VOLUME_COM_LEN]; /* Random comments about volume */
uint8_t dl_VolumeIdentifier[VOLUME_ID_LEN]; /* Should contain disc name */
uint32_t dl_VolumeUniqueIdentifier; /* Roll a billion-sided die */
uint32_t dl_VolumeBlockSize; /* Usually contains 2048 */
uint32_t dl_VolumeBlockCount; /* # of blocks on disc */
uint32_t dl_RootUniqueIdentifier; /* Roll a billion-sided die */
uint32_t dl_RootDirectoryBlockCount; /* # of blocks in root */
uint32_t dl_RootDirectoryBlockSize; /* usually same as vol blk size */
uint32_t dl_RootDirectoryLastAvatarIndex; /* should contain 7 */
uint32_t dl_RootDirectoryAvatarList[ROOT_HIGHEST_AVATAR+1];
};

typedef struct DirectoryHeader DirectoryHeader;
struct DirectoryHeader
{
int32_t dh_NextBlock;
int32_t dh_PrevBlock;
uint32_t dh_Flags;
uint32_t dh_FirstFreeByte;
uint32_t dh_FirstEntryOffset;
};

#define DIRECTORYRECORD(AVATARCOUNT) \
uint32_t dir_Flags; \
uint32_t dir_UniqueIdentifier; \
uint32_t dir_Type; \
uint32_t dir_BlockSize; \
uint32_t dir_ByteCount; \
uint32_t dir_BlockCount; \
uint32_t dir_Burst; \
uint32_t dir_Gap; \
char dir_FileName[FILESYSTEM_MAX_NAME_LEN]; \
uint32_t dir_LastAvatarIndex; \
uint32_t dir_AvatarList[AVATARCOUNT];

typedef struct DirectoryRecord {
DIRECTORYRECORD(1)
} DirectoryRecord;

#define DIRECTORY_LAST_IN_DIR 0x80000000
#define DIRECTORY_LAST_IN_BLOCK 0x40000000

EXTERN_C_END
5 changes: 5 additions & 0 deletions libopera/endianness.h
Original file line number Diff line number Diff line change
Expand Up @@ -75,11 +75,14 @@
#if IS_BIG_ENDIAN

#define swap32_if_little_endian(X) (X)
#define swap32_if_le(X) (X)
#define swap32_array_if_little_endian(X,Y)
#define swap32_array_if_le(X,Y)

#else

#define swap32_if_little_endian(X) (SWAP32(X))
#define swap32_if_le(X) (SWAP32(X))

static
INLINE
Expand All @@ -93,6 +96,8 @@ swap32_array_if_little_endian(uint32_t *array_,
array_[i] = SWAP32(array_[i]);
}

#define swap32_array_if_le(X,Y) swap32_array_if_little_endian(X,Y)

#endif /* IS_BIG_ENDIAN */

#endif /* LIBOPERA_ENDIANNESS_H_INCLUDED */
27 changes: 27 additions & 0 deletions libopera/linkedmemblock.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
#pragma once

#include <stdint.h>

/*
LinkedMemDisk defines a high-level "disk" which consists of a
doubly-linked list of storage blocks in memory (RAM, ROM, NVRAM,
or out on a gamesaver cartridge. LinkedMemDisks have a standard
Opera label at offset 0, with a type code of 2. The linked list
normally begins immediately after the label; its offset is given
by the zero'th avatar of the root directory. LinkedMemDisks are,
by definition, flat file systems and cannot contain directories.
*/

#define FINGERPRINT_FILEBLOCK 0xBE4F32A6
#define FINGERPRINT_FREEBLOCK 0x7AA565BD
#define FINGERPRINT_ANCHORBLOCK 0x855A02B6

typedef struct LinkedMemBlock LinkedMemBlock;
struct LinkedMemBlock
{
uint32_t fingerprint;
uint32_t flinkoffset;
uint32_t blinkoffset;
uint32_t blockcount;
uint32_t headerblockcount;
};
126 changes: 64 additions & 62 deletions libopera/opera_nvram.c
Original file line number Diff line number Diff line change
@@ -1,79 +1,81 @@
#include "discdata.h"
#include "linkedmemblock.h"

#include "opera_mem.h"

#include "endianness.h"

#include "boolean.h"

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

#pragma pack(push,1)
#define NVRAM_BLOCKSIZE 1
#define NVRAM_BLOCKCOUNT (32 * 1024)

typedef struct nvram_header_t nvram_header_t;
struct nvram_header_t
bool
opera_nvram_initialized(void *buf_,
const int bufsize_)
{
uint8_t record_type;
uint8_t sync_bytes[5];
uint8_t record_version;
uint8_t flags;
uint8_t comment[32];
uint8_t label[32];
uint32_t id;
uint32_t block_size;
uint32_t block_count;
uint32_t root_dir_id;
uint32_t root_dir_blocks;
uint32_t root_dir_block_size;
uint32_t last_root_dir_copy;
uint32_t root_dir_copies[8];
int i;
DiscLabel *dl;

uint32_t unknown_value0;
uint32_t unknown_value1;
uint32_t unknown_value2;
uint32_t unknown_value3;
uint32_t unknown_value4;
uint32_t unknown_value5;
uint32_t unknown_value6;
uint32_t unknown_value7;
uint32_t blocks_remaining;
uint32_t unknown_value8;
};
dl = (DiscLabel*)buf_;

#pragma pack(pop)
if(dl->dl_RecordType != DISC_LABEL_RECORD_TYPE)
return false;
if(dl->dl_VolumeStructureVersion != VOLUME_STRUCTURE_LINKED_MEM)
return false;
for(i = 0; i < VOLUME_SYNC_BYTE_LEN; i++)
{
if(dl->dl_VolumeSyncBytes[i] != VOLUME_SYNC_BYTE)
return false;
}

return true;
}

// The below code mimics the official 3DO formatting tool "format"
// https://github.com/trapexit/portfolio_os/blob/master/src/filesystem/format.c
// https://github.com/trapexit/portfolio_os/blob/master/src/filesystem/lmadm.c
void
opera_nvram_init(void)
opera_nvram_init(void *buf_,
const int bufsize_)
{
nvram_header_t *nvram_hdr = (nvram_header_t*)NVRAM;
DiscLabel *disc_label;
LinkedMemBlock *anchor_block;
LinkedMemBlock *free_block;

disc_label = (DiscLabel*)buf_;
anchor_block = (LinkedMemBlock*)&disc_label[1];
free_block = &anchor_block[1];

memset(buf_,0,bufsize_);

disc_label->dl_RecordType = DISC_LABEL_RECORD_TYPE;
memset(disc_label->dl_VolumeSyncBytes,VOLUME_SYNC_BYTE,VOLUME_SYNC_BYTE_LEN);
disc_label->dl_VolumeStructureVersion = VOLUME_STRUCTURE_LINKED_MEM;
disc_label->dl_VolumeFlags = 0;
strncpy((char*)disc_label->dl_VolumeCommentary,"opera formatted", VOLUME_COM_LEN);
strncpy((char*)disc_label->dl_VolumeIdentifier,"nvram", VOLUME_ID_LEN);
disc_label->dl_VolumeUniqueIdentifier = swap32_if_le(NVRAM_VOLUME_UNIQUE_ID); // ???
disc_label->dl_VolumeBlockSize = swap32_if_le(NVRAM_BLOCKSIZE);
disc_label->dl_VolumeBlockCount = swap32_if_le(bufsize_);
disc_label->dl_RootUniqueIdentifier = swap32_if_le(NVRAM_ROOT_UNIQUE_ID);
disc_label->dl_RootDirectoryBlockCount = 0;
disc_label->dl_RootDirectoryBlockSize = swap32_if_le(NVRAM_BLOCKSIZE);
disc_label->dl_RootDirectoryLastAvatarIndex = 0;
disc_label->dl_RootDirectoryAvatarList[0] = swap32_if_le(sizeof(DiscLabel));

memset(nvram_hdr,0,sizeof(nvram_header_t));
anchor_block->fingerprint = swap32_if_le(FINGERPRINT_ANCHORBLOCK);
anchor_block->flinkoffset = swap32_if_le(sizeof(DiscLabel) + sizeof(LinkedMemBlock));
anchor_block->blinkoffset = swap32_if_le(sizeof(DiscLabel) + sizeof(LinkedMemBlock));
anchor_block->blockcount = swap32_if_le(sizeof(LinkedMemBlock));
anchor_block->headerblockcount = swap32_if_le(sizeof(LinkedMemBlock));

nvram_hdr->record_type = 0x01;
nvram_hdr->sync_bytes[0] = 'Z';
nvram_hdr->sync_bytes[1] = 'Z';
nvram_hdr->sync_bytes[2] = 'Z';
nvram_hdr->sync_bytes[3] = 'Z';
nvram_hdr->sync_bytes[4] = 'Z';
nvram_hdr->record_version = 0x02;
nvram_hdr->label[0] = 'N';
nvram_hdr->label[1] = 'V';
nvram_hdr->label[2] = 'R';
nvram_hdr->label[3] = 'A';
nvram_hdr->label[4] = 'M';
nvram_hdr->id = swap32_if_little_endian(0xFFFFFFFF);
nvram_hdr->block_size = swap32_if_little_endian(0x00000001);
nvram_hdr->block_count = swap32_if_little_endian(0x00008000);
nvram_hdr->root_dir_id = swap32_if_little_endian(0xFFFFFFFE);
nvram_hdr->root_dir_blocks = swap32_if_little_endian(0x00000000);
nvram_hdr->root_dir_block_size = swap32_if_little_endian(0x00000001);
nvram_hdr->last_root_dir_copy = swap32_if_little_endian(0x00000000);
nvram_hdr->root_dir_copies[0] = swap32_if_little_endian(0x00000084);
nvram_hdr->unknown_value0 = swap32_if_little_endian(0x855A02B6);
nvram_hdr->unknown_value1 = swap32_if_little_endian(0x00000098);
nvram_hdr->unknown_value2 = swap32_if_little_endian(0x00000098);
nvram_hdr->unknown_value3 = swap32_if_little_endian(0x00000014);
nvram_hdr->unknown_value4 = swap32_if_little_endian(0x00000014);
nvram_hdr->unknown_value5 = swap32_if_little_endian(0x7AA565BD);
nvram_hdr->unknown_value6 = swap32_if_little_endian(0x00000084);
nvram_hdr->unknown_value7 = swap32_if_little_endian(0x00000084);
nvram_hdr->blocks_remaining = swap32_if_little_endian(0x00007F68);
nvram_hdr->unknown_value8 = swap32_if_little_endian(0x00000014);
free_block->fingerprint = swap32_if_le(FINGERPRINT_FREEBLOCK);
free_block->flinkoffset = swap32_if_le(sizeof(DiscLabel));
free_block->blinkoffset = swap32_if_le(sizeof(DiscLabel));
free_block->blockcount = swap32_if_le(bufsize_ - sizeof(DiscLabel) - sizeof(LinkedMemBlock));
free_block->headerblockcount = swap32_if_le(sizeof(LinkedMemBlock));
}
5 changes: 4 additions & 1 deletion libopera/opera_nvram.h
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
#ifndef OPERA_NVRAM_H_INCLUDED
#define OPERA_NVRAM_H_INCLUDED

void opera_nvram_init(void);
#include "boolean.h"

bool opera_nvram_initialized(void *buf, const int bufsize);
void opera_nvram_init(void *buf, const int bufsize);

#endif
5 changes: 5 additions & 0 deletions opera_lr_nvram.c
Original file line number Diff line number Diff line change
Expand Up @@ -377,6 +377,8 @@ opera_lr_nvram_save(const char *gamepath_,
}
}

// Try to load. If it fails ensure the NVRAM is initialized.
// Not all games will force a format if the NVRAM is corrupt.
void
opera_lr_nvram_load(const char *gamepath_,
const bool shared_,
Expand All @@ -400,4 +402,7 @@ opera_lr_nvram_load(const char *gamepath_,

opera_lr_nvram_load_pergame(NVRAM,NVRAM_SIZE,filename,version_);
}

if(!opera_nvram_initialized(NVRAM,NVRAM_SIZE))
opera_nvram_init(NVRAM,NVRAM_SIZE);
}

0 comments on commit 5121aa6

Please sign in to comment.