nkeynes@1034 | 1 | /**
|
nkeynes@1035 | 2 | * $Id$
|
nkeynes@1034 | 3 | *
|
nkeynes@1034 | 4 | * VMU volume (ie block device) support
|
nkeynes@1034 | 5 | *
|
nkeynes@1034 | 6 | * Copyright (c) 2009 Nathan Keynes.
|
nkeynes@1034 | 7 | *
|
nkeynes@1034 | 8 | * This program is free software; you can redistribute it and/or modify
|
nkeynes@1034 | 9 | * it under the terms of the GNU General Public License as published by
|
nkeynes@1034 | 10 | * the Free Software Foundation; either version 2 of the License, or
|
nkeynes@1034 | 11 | * (at your option) any later version.
|
nkeynes@1034 | 12 | *
|
nkeynes@1034 | 13 | * This program is distributed in the hope that it will be useful,
|
nkeynes@1034 | 14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
nkeynes@1034 | 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
nkeynes@1034 | 16 | * GNU General Public License for more details.
|
nkeynes@1034 | 17 | */
|
nkeynes@1034 | 18 |
|
nkeynes@1034 | 19 | #include <glib/gmem.h>
|
nkeynes@1034 | 20 | #include <glib/gstrfuncs.h>
|
nkeynes@1034 | 21 | #include <string.h>
|
nkeynes@1042 | 22 | #include <unistd.h>
|
nkeynes@1034 | 23 | #include <stdio.h>
|
nkeynes@1034 | 24 | #include <fcntl.h>
|
nkeynes@1034 | 25 | #include <errno.h>
|
nkeynes@1034 | 26 |
|
nkeynes@1034 | 27 | #include "vmu/vmuvol.h"
|
nkeynes@1034 | 28 | #include "dream.h"
|
nkeynes@1042 | 29 | #include "lxpaths.h"
|
nkeynes@1034 | 30 |
|
nkeynes@1034 | 31 | #define VMU_MAX_PARTITIONS 256
|
nkeynes@1034 | 32 | #define VMU_MAX_BLOCKS 65536 /* Actually slightly less than this, but it'll do */
|
nkeynes@1034 | 33 |
|
nkeynes@1034 | 34 | typedef struct vmu_partition {
|
nkeynes@1034 | 35 | struct vmu_volume_metadata metadata;
|
nkeynes@1034 | 36 | uint32_t block_count;
|
nkeynes@1034 | 37 | char *blocks;
|
nkeynes@1034 | 38 | } *vmu_partition_t;
|
nkeynes@1034 | 39 |
|
nkeynes@1034 | 40 | struct vmu_volume {
|
nkeynes@1034 | 41 | const gchar *display_name;
|
nkeynes@1034 | 42 | vmu_partnum_t part_count;
|
nkeynes@1034 | 43 | gboolean dirty;
|
nkeynes@1034 | 44 | struct vmu_partition part[0];
|
nkeynes@1034 | 45 | };
|
nkeynes@1034 | 46 |
|
nkeynes@1034 | 47 | /* On-VMU structures, courtesy of Marcus Comstedt */
|
nkeynes@1034 | 48 | struct vmu_superblock {
|
nkeynes@1034 | 49 | char magic[16];
|
nkeynes@1034 | 50 | uint8_t colour_flag;
|
nkeynes@1034 | 51 | uint8_t bgra[4];
|
nkeynes@1034 | 52 | uint8_t pad1[27];
|
nkeynes@1034 | 53 | char timestamp[8];
|
nkeynes@1034 | 54 | char pad2[8];
|
nkeynes@1034 | 55 | char unknown[6];
|
nkeynes@1034 | 56 | uint16_t fat_block;
|
nkeynes@1034 | 57 | uint16_t fat_size;
|
nkeynes@1034 | 58 | uint16_t dir_block;
|
nkeynes@1034 | 59 | uint16_t dir_size;
|
nkeynes@1034 | 60 | uint16_t icon_shape;
|
nkeynes@1034 | 61 | uint16_t user_size;
|
nkeynes@1034 | 62 | /* remainder unknown */
|
nkeynes@1034 | 63 | };
|
nkeynes@1034 | 64 |
|
nkeynes@1034 | 65 | struct vmu_direntry {
|
nkeynes@1034 | 66 | uint8_t filetype;
|
nkeynes@1034 | 67 | uint8_t copy_flag;
|
nkeynes@1034 | 68 | uint16_t blkno;
|
nkeynes@1034 | 69 | char filename[12];
|
nkeynes@1034 | 70 | char timestamp[8];
|
nkeynes@1034 | 71 | uint16_t blksize; /* Size in blocks*/
|
nkeynes@1034 | 72 | uint16_t hdroff; /* Header offset in blocks */
|
nkeynes@1034 | 73 | char pad[4];
|
nkeynes@1034 | 74 | };
|
nkeynes@1034 | 75 |
|
nkeynes@1034 | 76 | #define MD(vmu,ptno) ((vmu)->part[ptno].metadata)
|
nkeynes@1034 | 77 |
|
nkeynes@1034 | 78 | #define VMU_BLOCK(vmu,ptno,blkno) (&(vmu)->part[ptno].blocks[(blkno)*VMU_BLOCK_SIZE])
|
nkeynes@1034 | 79 |
|
nkeynes@1034 | 80 | #define VMU_FAT_ENTRY(vmu,pt,ent) ((uint16_t *)VMU_BLOCK(vmu, pt, (MD(vmu,pt).fat_block - ((ent)>>8))))[(ent)&0xFF]
|
nkeynes@1034 | 81 |
|
nkeynes@1034 | 82 | #define FAT_EMPTY 0xFFFC
|
nkeynes@1034 | 83 | #define FAT_EOF 0xFFFA
|
nkeynes@1034 | 84 |
|
nkeynes@1034 | 85 | static const struct vmu_volume_metadata default_metadata = { 255, 255, 254, 1, 253, 13, 0, 200, 31, 0, 128 };
|
nkeynes@1034 | 86 |
|
nkeynes@1034 | 87 | vmu_volume_t vmu_volume_new_default( const gchar *display_name )
|
nkeynes@1034 | 88 | {
|
nkeynes@1034 | 89 | vmu_volume_t vol = g_malloc0( sizeof(struct vmu_volume) + sizeof(struct vmu_partition) );
|
nkeynes@1034 | 90 | vol->part_count = 1;
|
nkeynes@1034 | 91 | vol->dirty = FALSE;
|
nkeynes@1034 | 92 | memcpy( &vol->part[0].metadata, &default_metadata, sizeof(struct vmu_volume_metadata) );
|
nkeynes@1034 | 93 | vol->part[0].block_count = VMU_DEFAULT_VOL_BLOCKS;
|
nkeynes@1034 | 94 | vol->part[0].blocks = g_malloc0( VMU_DEFAULT_VOL_BLOCKS * VMU_BLOCK_SIZE );
|
nkeynes@1034 | 95 | vol->display_name = display_name == NULL ? NULL : g_strdup(display_name);
|
nkeynes@1034 | 96 | vmu_volume_format( vol, 0, TRUE );
|
nkeynes@1034 | 97 | return vol;
|
nkeynes@1034 | 98 | }
|
nkeynes@1034 | 99 |
|
nkeynes@1034 | 100 | void vmu_volume_destroy( vmu_volume_t vol )
|
nkeynes@1034 | 101 | {
|
nkeynes@1034 | 102 | int i;
|
nkeynes@1034 | 103 | if( vol == NULL )
|
nkeynes@1034 | 104 | return;
|
nkeynes@1034 | 105 |
|
nkeynes@1034 | 106 | for( i=0; i<vol->part_count; i++ ) {
|
nkeynes@1034 | 107 | g_free( vol->part[i].blocks );
|
nkeynes@1034 | 108 | vol->part[i].blocks = NULL;
|
nkeynes@1034 | 109 | }
|
nkeynes@1034 | 110 | if( vol->display_name ) {
|
nkeynes@1034 | 111 | g_free( (char *)vol->display_name );
|
nkeynes@1034 | 112 | vol->display_name = NULL;
|
nkeynes@1034 | 113 | }
|
nkeynes@1034 | 114 | g_free(vol);
|
nkeynes@1034 | 115 | }
|
nkeynes@1034 | 116 |
|
nkeynes@1034 | 117 | void vmu_volume_format( vmu_volume_t vol, vmu_partnum_t pt, gboolean quick )
|
nkeynes@1034 | 118 | {
|
nkeynes@1034 | 119 | if( pt >= vol->part_count ) {
|
nkeynes@1034 | 120 | return;
|
nkeynes@1034 | 121 | }
|
nkeynes@1034 | 122 |
|
nkeynes@1034 | 123 | if( !quick ) {
|
nkeynes@1034 | 124 | /* Wipe it completely first */
|
nkeynes@1034 | 125 | memset( vol->part[pt].blocks, 0, (vol->part[pt].block_count) * VMU_BLOCK_SIZE );
|
nkeynes@1034 | 126 | }
|
nkeynes@1034 | 127 |
|
nkeynes@1034 | 128 | struct vmu_volume_metadata *meta = &vol->part[pt].metadata;
|
nkeynes@1034 | 129 | unsigned int fatblkno = meta->fat_block;
|
nkeynes@1034 | 130 | unsigned int dirblkno = meta->dir_block;
|
nkeynes@1034 | 131 |
|
nkeynes@1034 | 132 | /* Write superblock */
|
nkeynes@1034 | 133 | struct vmu_superblock *super = (struct vmu_superblock *)VMU_BLOCK(vol,pt, meta->super_block);
|
nkeynes@1034 | 134 | memset( super->magic, 0x55, 16 );
|
nkeynes@1034 | 135 | memset( &super->colour_flag, 0, 240 ); /* Blank the rest for now */
|
nkeynes@1034 | 136 | super->fat_block = meta->fat_block;
|
nkeynes@1034 | 137 | super->fat_size = meta->fat_size;
|
nkeynes@1034 | 138 | super->dir_block = meta->dir_block;
|
nkeynes@1034 | 139 | super->user_size = meta->user_size;
|
nkeynes@1034 | 140 |
|
nkeynes@1034 | 141 | /* Write file allocation tables */
|
nkeynes@1034 | 142 | int i,j;
|
nkeynes@1034 | 143 | for( j=0; j<meta->fat_size; j++ ) {
|
nkeynes@1034 | 144 | uint16_t *fat = (uint16_t *)VMU_BLOCK(vol,pt,fatblkno-j);
|
nkeynes@1034 | 145 | for( i=0; i<256; i++ ) {
|
nkeynes@1034 | 146 | fat[i] = FAT_EMPTY;
|
nkeynes@1034 | 147 | }
|
nkeynes@1034 | 148 | }
|
nkeynes@1034 | 149 |
|
nkeynes@1034 | 150 | /* Fill in the system allocations in the FAT */
|
nkeynes@1034 | 151 | for( i=0; i<meta->fat_size-1; i++ ) {
|
nkeynes@1034 | 152 | VMU_FAT_ENTRY(vol,pt,fatblkno-i) = fatblkno-i-1;
|
nkeynes@1034 | 153 | }
|
nkeynes@1034 | 154 | VMU_FAT_ENTRY(vol,pt,fatblkno - i) = FAT_EOF;
|
nkeynes@1034 | 155 | for( i=0; i<meta->dir_size-1; i++ ) {
|
nkeynes@1034 | 156 | VMU_FAT_ENTRY(vol,pt,dirblkno-i) = dirblkno-i-1;
|
nkeynes@1034 | 157 | }
|
nkeynes@1034 | 158 | VMU_FAT_ENTRY(vol,pt,dirblkno-i) = FAT_EOF;
|
nkeynes@1034 | 159 |
|
nkeynes@1034 | 160 | /* If quick-format, blank the directory. Otherwise it's already been done */
|
nkeynes@1034 | 161 | if( quick ) {
|
nkeynes@1034 | 162 | memset( VMU_BLOCK(vol,pt,dirblkno-meta->dir_size+1),
|
nkeynes@1034 | 163 | 0, meta->dir_size * VMU_BLOCK_SIZE );
|
nkeynes@1034 | 164 | }
|
nkeynes@1034 | 165 | }
|
nkeynes@1034 | 166 |
|
nkeynes@1034 | 167 | /*************************** File load/save ********************************/
|
nkeynes@1034 | 168 |
|
nkeynes@1034 | 169 | /**
|
nkeynes@1034 | 170 | * Current file has 1 META chunk for all volume metadata, followed by a
|
nkeynes@1034 | 171 | * DATA chunk for each partition's block data. The META chunk is required to
|
nkeynes@1034 | 172 | * occur before any DATA blocks.
|
nkeynes@1034 | 173 | * Unknown chunks are skipped to allow for forwards compatibility if/when
|
nkeynes@1034 | 174 | * we add the VMU runtime side of things
|
nkeynes@1034 | 175 | */
|
nkeynes@1034 | 176 |
|
nkeynes@1034 | 177 | struct vmu_file_header {
|
nkeynes@1034 | 178 | char magic[16];
|
nkeynes@1034 | 179 | uint32_t version;
|
nkeynes@1034 | 180 | uint32_t head_len;
|
nkeynes@1034 | 181 | uint32_t part_count;
|
nkeynes@1034 | 182 | uint32_t display_name_len;
|
nkeynes@1034 | 183 | char display_name[0];
|
nkeynes@1034 | 184 | };
|
nkeynes@1034 | 185 |
|
nkeynes@1034 | 186 | struct vmu_chunk_header {
|
nkeynes@1034 | 187 | char name[4];
|
nkeynes@1034 | 188 | uint32_t length;
|
nkeynes@1034 | 189 | };
|
nkeynes@1034 | 190 |
|
nkeynes@1034 | 191 |
|
nkeynes@1034 | 192 | gboolean vmu_volume_save( const gchar *filename, vmu_volume_t vol, gboolean create_only )
|
nkeynes@1034 | 193 | {
|
nkeynes@1034 | 194 | struct vmu_file_header head;
|
nkeynes@1034 | 195 | struct vmu_chunk_header chunk;
|
nkeynes@1034 | 196 | int i;
|
nkeynes@1042 | 197 |
|
nkeynes@1042 | 198 | gchar *tempfile = get_filename_at(filename, ".XXXXXXXX.vmu");
|
nkeynes@1042 | 199 | int fd = mkstemps( tempfile, 4 );
|
nkeynes@1042 | 200 | if( fd == -1 ) {
|
nkeynes@1042 | 201 | g_free(tempfile);
|
nkeynes@1034 | 202 | return FALSE;
|
nkeynes@1034 | 203 | }
|
nkeynes@1034 | 204 |
|
nkeynes@1042 | 205 | FILE *f = fdopen( fd, "w+" );
|
nkeynes@1042 | 206 |
|
nkeynes@1042 | 207 |
|
nkeynes@1034 | 208 | /* File header */
|
nkeynes@1034 | 209 | memcpy( head.magic, VMU_FILE_MAGIC, 16 );
|
nkeynes@1034 | 210 | head.version = VMU_FILE_VERSION;
|
nkeynes@1034 | 211 | head.part_count = vol->part_count;
|
nkeynes@1034 | 212 | head.display_name_len = vol->display_name == NULL ? 0 : (strlen(vol->display_name)+1);
|
nkeynes@1034 | 213 | head.head_len = sizeof(head) + head.display_name_len;
|
nkeynes@1034 | 214 | fwrite( &head, sizeof(head), 1, f );
|
nkeynes@1034 | 215 | if( vol->display_name != NULL ) {
|
nkeynes@1034 | 216 | fwrite( vol->display_name, head.display_name_len, 1, f );
|
nkeynes@1034 | 217 | }
|
nkeynes@1034 | 218 |
|
nkeynes@1034 | 219 | /* METAdata chunk */
|
nkeynes@1034 | 220 | memcpy( chunk.name, "META", 4 );
|
nkeynes@1034 | 221 | chunk.length = sizeof(struct vmu_volume_metadata) * vol->part_count;
|
nkeynes@1034 | 222 | fwrite( &chunk, sizeof(chunk), 1, f );
|
nkeynes@1034 | 223 | for( i=0; i < vol->part_count; i++ ) {
|
nkeynes@1034 | 224 | fwrite( &vol->part[i].metadata, sizeof(struct vmu_volume_metadata), 1, f );
|
nkeynes@1034 | 225 | }
|
nkeynes@1034 | 226 |
|
nkeynes@1034 | 227 | /* partition DATA chunks */
|
nkeynes@1034 | 228 | for( i=0; i< vol->part_count; i++ ) {
|
nkeynes@1034 | 229 | memcpy( chunk.name, "DATA", 4 );
|
nkeynes@1034 | 230 | chunk.length = 0;
|
nkeynes@1042 | 231 | if( fwrite( &chunk, sizeof(chunk), 1, f ) != 1 ) goto cleanup;
|
nkeynes@1034 | 232 | long posn = ftell(f);
|
nkeynes@1042 | 233 | if( fwrite( &vol->part[i].block_count, sizeof(vol->part[i].block_count), 1, f ) != 1 ) goto cleanup;
|
nkeynes@1034 | 234 | fwrite_gzip( vol->part[i].blocks, vol->part[i].block_count, VMU_BLOCK_SIZE, f );
|
nkeynes@1034 | 235 | long end = ftell(f);
|
nkeynes@1034 | 236 | fseek( f, posn - sizeof(chunk.length), SEEK_SET );
|
nkeynes@1034 | 237 | chunk.length = end-posn;
|
nkeynes@1042 | 238 | if( fwrite( &chunk.length, sizeof(chunk.length), 1, f ) != 1 ) goto cleanup;
|
nkeynes@1034 | 239 | fseek( f, end, SEEK_SET );
|
nkeynes@1034 | 240 | }
|
nkeynes@1034 | 241 | fclose(f);
|
nkeynes@1042 | 242 |
|
nkeynes@1042 | 243 | if( rename(tempfile, filename) != 0 )
|
nkeynes@1042 | 244 | goto cleanup;
|
nkeynes@1042 | 245 |
|
nkeynes@1042 | 246 | /* All good */
|
nkeynes@1034 | 247 | vol->dirty = FALSE;
|
nkeynes@1042 | 248 | g_free(tempfile);
|
nkeynes@1034 | 249 | return TRUE;
|
nkeynes@1042 | 250 |
|
nkeynes@1042 | 251 | cleanup:
|
nkeynes@1042 | 252 | fclose(f);
|
nkeynes@1042 | 253 | unlink(tempfile);
|
nkeynes@1042 | 254 | g_free(tempfile);
|
nkeynes@1042 | 255 | return FALSE;
|
nkeynes@1034 | 256 | }
|
nkeynes@1034 | 257 |
|
nkeynes@1034 | 258 | vmu_volume_t vmu_volume_load( const gchar *filename )
|
nkeynes@1034 | 259 | {
|
nkeynes@1034 | 260 | struct vmu_file_header head;
|
nkeynes@1034 | 261 | struct vmu_chunk_header chunk;
|
nkeynes@1034 | 262 | vmu_volume_t vol;
|
nkeynes@1034 | 263 | int i;
|
nkeynes@1034 | 264 |
|
nkeynes@1034 | 265 | FILE *f = fopen( filename, "ro" );
|
nkeynes@1034 | 266 | if( f == NULL ) {
|
nkeynes@1034 | 267 | ERROR( "Unable to open VMU file '%s': %s", filename, strerror(errno) );
|
nkeynes@1034 | 268 | return FALSE;
|
nkeynes@1034 | 269 | }
|
nkeynes@1034 | 270 |
|
nkeynes@1034 | 271 | if( fread( &head, sizeof(head), 1, f ) != 1 ||
|
nkeynes@1034 | 272 | memcmp(head.magic, VMU_FILE_MAGIC, 16) != 0 ||
|
nkeynes@1034 | 273 | head.part_count > VMU_MAX_PARTITIONS ||
|
nkeynes@1034 | 274 | head.head_len < (sizeof(head) + head.display_name_len) ) {
|
nkeynes@1034 | 275 | fclose(f);
|
nkeynes@1034 | 276 | ERROR( "Unable to load VMU '%s': bad file header", filename );
|
nkeynes@1034 | 277 | return NULL;
|
nkeynes@1034 | 278 | }
|
nkeynes@1034 | 279 |
|
nkeynes@1034 | 280 | vol = (vmu_volume_t)g_malloc0( sizeof(struct vmu_volume) + sizeof(struct vmu_partition)*head.part_count );
|
nkeynes@1034 | 281 | vol->part_count = head.part_count;
|
nkeynes@1034 | 282 | vol->dirty = FALSE;
|
nkeynes@1034 | 283 | if( head.display_name_len != 0 ) {
|
nkeynes@1034 | 284 | vol->display_name = g_malloc( head.display_name_len );
|
nkeynes@1034 | 285 | fread( (char *)vol->display_name, head.display_name_len, 1, f );
|
nkeynes@1034 | 286 | }
|
nkeynes@1034 | 287 | fseek( f, head.head_len, SEEK_SET );
|
nkeynes@1034 | 288 |
|
nkeynes@1034 | 289 | gboolean have_meta = FALSE;
|
nkeynes@1034 | 290 | int next_part = 0;
|
nkeynes@1034 | 291 | while( !feof(f) && fread( &chunk, sizeof(chunk), 1, f ) == 1 ) {
|
nkeynes@1034 | 292 | if( memcmp( &chunk.name, "META", 4 ) == 0 ) {
|
nkeynes@1034 | 293 | if( have_meta || chunk.length != head.part_count * sizeof(struct vmu_volume_metadata) ) {
|
nkeynes@1034 | 294 | vmu_volume_destroy(vol);
|
nkeynes@1034 | 295 | fclose(f);
|
nkeynes@1034 | 296 | ERROR( "Unable to load VMU '%s': bad metadata size (expected %d but was %d)", filename,
|
nkeynes@1034 | 297 | head.part_count * sizeof(struct vmu_volume_metadata), chunk.length );
|
nkeynes@1034 | 298 | return NULL;
|
nkeynes@1034 | 299 | }
|
nkeynes@1034 | 300 | for( i=0; i<head.part_count; i++ ) {
|
nkeynes@1034 | 301 | fread( &vol->part[i].metadata, sizeof(struct vmu_volume_metadata), 1, f );
|
nkeynes@1034 | 302 | }
|
nkeynes@1034 | 303 | have_meta = TRUE;
|
nkeynes@1034 | 304 | } else if( memcmp( &chunk.name, "DATA", 4 ) == 0 ) {
|
nkeynes@1034 | 305 | uint32_t block_count;
|
nkeynes@1034 | 306 | fread( &block_count, sizeof(block_count), 1, f );
|
nkeynes@1034 | 307 | if( next_part >= vol->part_count || block_count >= VMU_MAX_BLOCKS ) {
|
nkeynes@1034 | 308 | // Too many partitions / blocks
|
nkeynes@1034 | 309 | vmu_volume_destroy(vol);
|
nkeynes@1034 | 310 | fclose(f);
|
nkeynes@1034 | 311 | ERROR( "Unable to load VMU '%s': too large (%d/%d)", filename, next_part, block_count );
|
nkeynes@1034 | 312 | return NULL;
|
nkeynes@1034 | 313 | }
|
nkeynes@1034 | 314 | vol->part[next_part].block_count = block_count;
|
nkeynes@1034 | 315 | vol->part[next_part].blocks = g_malloc(block_count*VMU_BLOCK_SIZE);
|
nkeynes@1034 | 316 | fread_gzip(vol->part[next_part].blocks, VMU_BLOCK_SIZE, block_count, f );
|
nkeynes@1034 | 317 | next_part++;
|
nkeynes@1034 | 318 | } else {
|
nkeynes@1034 | 319 | // else skip unknown block
|
nkeynes@1034 | 320 | fseek( f, SEEK_CUR, chunk.length );
|
nkeynes@1034 | 321 | WARN( "Unexpected VMU data chunk: '%4.4s'", chunk.name );
|
nkeynes@1034 | 322 | }
|
nkeynes@1034 | 323 | }
|
nkeynes@1034 | 324 |
|
nkeynes@1034 | 325 | fclose(f);
|
nkeynes@1034 | 326 |
|
nkeynes@1034 | 327 | if( !have_meta || next_part != vol->part_count ) {
|
nkeynes@1034 | 328 | vmu_volume_destroy( vol );
|
nkeynes@1034 | 329 | return NULL;
|
nkeynes@1034 | 330 | }
|
nkeynes@1034 | 331 |
|
nkeynes@1034 | 332 | return vol;
|
nkeynes@1034 | 333 | }
|
nkeynes@1034 | 334 |
|
nkeynes@1034 | 335 | /*************************** Accessing data ********************************/
|
nkeynes@1034 | 336 | const char *vmu_volume_get_display_name( vmu_volume_t vol )
|
nkeynes@1034 | 337 | {
|
nkeynes@1034 | 338 | return vol->display_name;
|
nkeynes@1034 | 339 | }
|
nkeynes@1034 | 340 |
|
nkeynes@1034 | 341 | void vmu_volume_set_display_name( vmu_volume_t vol, const gchar *name )
|
nkeynes@1034 | 342 | {
|
nkeynes@1034 | 343 | if( vol->display_name != NULL ) {
|
nkeynes@1034 | 344 | g_free( (char *)vol->display_name );
|
nkeynes@1034 | 345 | }
|
nkeynes@1034 | 346 | if( name == NULL ) {
|
nkeynes@1034 | 347 | vol->display_name = NULL;
|
nkeynes@1034 | 348 | } else {
|
nkeynes@1034 | 349 | vol->display_name = g_strdup(name);
|
nkeynes@1034 | 350 | }
|
nkeynes@1034 | 351 | }
|
nkeynes@1034 | 352 |
|
nkeynes@1034 | 353 | gboolean vmu_volume_is_dirty( vmu_volume_t vol )
|
nkeynes@1034 | 354 | {
|
nkeynes@1034 | 355 | return vol->dirty;
|
nkeynes@1034 | 356 | }
|
nkeynes@1034 | 357 |
|
nkeynes@1034 | 358 | gboolean vmu_volume_read_block( vmu_volume_t vol, vmu_partnum_t pt, unsigned int block, unsigned char *out )
|
nkeynes@1034 | 359 | {
|
nkeynes@1034 | 360 | if( pt >= vol->part_count || block >= vol->part[pt].block_count ) {
|
nkeynes@1034 | 361 | return FALSE;
|
nkeynes@1034 | 362 | }
|
nkeynes@1034 | 363 |
|
nkeynes@1034 | 364 | memcpy( out, VMU_BLOCK(vol,pt,block), VMU_BLOCK_SIZE );
|
nkeynes@1034 | 365 | return TRUE;
|
nkeynes@1034 | 366 | }
|
nkeynes@1034 | 367 |
|
nkeynes@1034 | 368 | gboolean vmu_volume_write_block( vmu_volume_t vol, vmu_partnum_t pt, unsigned int block, unsigned char *in )
|
nkeynes@1034 | 369 | {
|
nkeynes@1034 | 370 | if( pt >= vol->part_count || block >= vol->part[pt].block_count ) {
|
nkeynes@1034 | 371 | return FALSE;
|
nkeynes@1034 | 372 | }
|
nkeynes@1034 | 373 | memcpy( VMU_BLOCK(vol,pt,block), in, VMU_BLOCK_SIZE );
|
nkeynes@1034 | 374 | vol->dirty = TRUE;
|
nkeynes@1034 | 375 | }
|
nkeynes@1034 | 376 |
|
nkeynes@1034 | 377 | gboolean vmu_volume_write_phase( vmu_volume_t vol, vmu_partnum_t pt, unsigned int block, unsigned int phase, unsigned char *in )
|
nkeynes@1034 | 378 | {
|
nkeynes@1034 | 379 | if( pt >= vol->part_count || block >= vol->part[pt].block_count || phase >= 4 ) {
|
nkeynes@1034 | 380 | return FALSE;
|
nkeynes@1034 | 381 | }
|
nkeynes@1034 | 382 | memcpy( VMU_BLOCK(vol,pt,block) + (phase*128), in, VMU_BLOCK_SIZE/4 );
|
nkeynes@1034 | 383 | vol->dirty = TRUE;
|
nkeynes@1034 | 384 | }
|
nkeynes@1034 | 385 |
|
nkeynes@1034 | 386 | const struct vmu_volume_metadata *vmu_volume_get_metadata( vmu_volume_t vol, vmu_partnum_t partition )
|
nkeynes@1034 | 387 | {
|
nkeynes@1034 | 388 | if( partition >= vol->part_count ) {
|
nkeynes@1034 | 389 | return NULL;
|
nkeynes@1034 | 390 | } else {
|
nkeynes@1034 | 391 | return &vol->part[partition].metadata;
|
nkeynes@1034 | 392 | }
|
nkeynes@1034 | 393 | }
|