Search
lxdream.org :: lxdream/src/vmu/vmuvol.c
lxdream 0.9.1
released Jun 29
Download Now
filename src/vmu/vmuvol.c
changeset 1071:182cfe43c09e
prev1056:d0896e6530d6
next1296:30ecee61f811
author nkeynes
date Tue Jul 21 20:21:52 2009 +1000 (12 years ago)
permissions -rw-r--r--
last change Fix assorted -Wall warnings
file annotate diff log raw
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@1071
    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@1045
   198
    gchar *tempfile = get_filename_at(filename, ".XXXXXXX");
nkeynes@1045
   199
    int fd = mkstemp( tempfile );
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@1056
   242
    f = NULL;
nkeynes@1042
   243
    
nkeynes@1042
   244
    if( rename(tempfile, filename) != 0 )
nkeynes@1042
   245
        goto cleanup;
nkeynes@1042
   246
    
nkeynes@1042
   247
    /* All good */
nkeynes@1034
   248
    vol->dirty = FALSE;
nkeynes@1042
   249
    g_free(tempfile);
nkeynes@1034
   250
    return TRUE;
nkeynes@1042
   251
    
nkeynes@1042
   252
cleanup:
nkeynes@1056
   253
    if( f != NULL )
nkeynes@1056
   254
        fclose(f);
nkeynes@1042
   255
    unlink(tempfile);
nkeynes@1042
   256
    g_free(tempfile);
nkeynes@1042
   257
    return FALSE;
nkeynes@1034
   258
}
nkeynes@1034
   259
nkeynes@1034
   260
vmu_volume_t vmu_volume_load( const gchar *filename )
nkeynes@1034
   261
{
nkeynes@1034
   262
    struct vmu_file_header head;
nkeynes@1034
   263
    struct vmu_chunk_header chunk;
nkeynes@1034
   264
    vmu_volume_t vol;
nkeynes@1034
   265
    int i;
nkeynes@1034
   266
    
nkeynes@1034
   267
    FILE *f = fopen( filename, "ro" );
nkeynes@1034
   268
    if( f == NULL ) {
nkeynes@1034
   269
        ERROR( "Unable to open VMU file '%s': %s", filename, strerror(errno) );
nkeynes@1034
   270
        return FALSE;
nkeynes@1034
   271
    }
nkeynes@1034
   272
    
nkeynes@1034
   273
    if( fread( &head, sizeof(head), 1, f ) != 1 ||
nkeynes@1034
   274
        memcmp(head.magic, VMU_FILE_MAGIC, 16) != 0 ||
nkeynes@1034
   275
        head.part_count > VMU_MAX_PARTITIONS || 
nkeynes@1034
   276
        head.head_len < (sizeof(head) + head.display_name_len) )  {
nkeynes@1034
   277
        fclose(f);
nkeynes@1034
   278
        ERROR( "Unable to load VMU '%s': bad file header", filename );
nkeynes@1034
   279
        return NULL;
nkeynes@1034
   280
    }
nkeynes@1034
   281
    
nkeynes@1034
   282
    vol = (vmu_volume_t)g_malloc0( sizeof(struct vmu_volume) + sizeof(struct vmu_partition)*head.part_count );
nkeynes@1034
   283
    vol->part_count = head.part_count;
nkeynes@1034
   284
    vol->dirty = FALSE;
nkeynes@1034
   285
    if( head.display_name_len != 0 ) {
nkeynes@1034
   286
        vol->display_name = g_malloc( head.display_name_len );
nkeynes@1034
   287
        fread( (char *)vol->display_name, head.display_name_len, 1, f );
nkeynes@1034
   288
    }
nkeynes@1034
   289
    fseek( f, head.head_len, SEEK_SET );
nkeynes@1034
   290
    
nkeynes@1034
   291
    gboolean have_meta = FALSE;
nkeynes@1034
   292
    int next_part = 0;
nkeynes@1034
   293
    while( !feof(f) && fread( &chunk, sizeof(chunk), 1, f ) == 1 ) {
nkeynes@1034
   294
        if( memcmp( &chunk.name, "META", 4 ) == 0 ) {
nkeynes@1034
   295
            if( have_meta || chunk.length != head.part_count * sizeof(struct vmu_volume_metadata) ) {
nkeynes@1034
   296
                vmu_volume_destroy(vol);
nkeynes@1034
   297
                fclose(f);
nkeynes@1034
   298
                ERROR( "Unable to load VMU '%s': bad metadata size (expected %d but was %d)", filename,
nkeynes@1034
   299
                       head.part_count * sizeof(struct vmu_volume_metadata), chunk.length );
nkeynes@1034
   300
                return NULL;
nkeynes@1034
   301
            }
nkeynes@1034
   302
            for( i=0; i<head.part_count; i++ ) {
nkeynes@1034
   303
                fread( &vol->part[i].metadata, sizeof(struct vmu_volume_metadata), 1, f );
nkeynes@1034
   304
            }
nkeynes@1034
   305
            have_meta = TRUE;
nkeynes@1034
   306
        } else if( memcmp( &chunk.name, "DATA", 4 ) == 0 ) {
nkeynes@1034
   307
            uint32_t block_count;
nkeynes@1034
   308
            fread( &block_count, sizeof(block_count), 1, f );
nkeynes@1034
   309
            if( next_part >= vol->part_count || block_count >= VMU_MAX_BLOCKS ) {
nkeynes@1034
   310
                // Too many partitions / blocks
nkeynes@1034
   311
                vmu_volume_destroy(vol);
nkeynes@1034
   312
                fclose(f);
nkeynes@1034
   313
                ERROR( "Unable to load VMU '%s': too large (%d/%d)", filename, next_part, block_count );
nkeynes@1034
   314
                return NULL;
nkeynes@1034
   315
            }
nkeynes@1034
   316
            vol->part[next_part].block_count = block_count;
nkeynes@1034
   317
            vol->part[next_part].blocks = g_malloc(block_count*VMU_BLOCK_SIZE);
nkeynes@1034
   318
            fread_gzip(vol->part[next_part].blocks, VMU_BLOCK_SIZE, block_count, f );
nkeynes@1034
   319
            next_part++;
nkeynes@1034
   320
        } else {
nkeynes@1034
   321
            // else skip unknown block
nkeynes@1034
   322
            fseek( f, SEEK_CUR, chunk.length );
nkeynes@1034
   323
            WARN( "Unexpected VMU data chunk: '%4.4s'", chunk.name );
nkeynes@1034
   324
        }
nkeynes@1034
   325
    }
nkeynes@1034
   326
    
nkeynes@1034
   327
    fclose(f);
nkeynes@1034
   328
    
nkeynes@1034
   329
    if( !have_meta || next_part != vol->part_count ) {
nkeynes@1034
   330
        vmu_volume_destroy( vol );
nkeynes@1034
   331
        return NULL;
nkeynes@1034
   332
    }
nkeynes@1034
   333
    
nkeynes@1034
   334
    return vol;
nkeynes@1034
   335
}
nkeynes@1034
   336
nkeynes@1034
   337
/*************************** Accessing data ********************************/
nkeynes@1034
   338
const char *vmu_volume_get_display_name( vmu_volume_t vol ) 
nkeynes@1034
   339
{
nkeynes@1034
   340
    return vol->display_name;
nkeynes@1034
   341
}
nkeynes@1034
   342
nkeynes@1034
   343
void vmu_volume_set_display_name( vmu_volume_t vol, const gchar *name )
nkeynes@1034
   344
{
nkeynes@1034
   345
    if( vol->display_name != NULL ) {
nkeynes@1034
   346
        g_free( (char *)vol->display_name );
nkeynes@1034
   347
    }
nkeynes@1034
   348
    if( name == NULL ) {
nkeynes@1034
   349
        vol->display_name = NULL;
nkeynes@1034
   350
    } else {
nkeynes@1034
   351
        vol->display_name = g_strdup(name);
nkeynes@1034
   352
    }
nkeynes@1034
   353
}
nkeynes@1034
   354
    
nkeynes@1034
   355
gboolean vmu_volume_is_dirty( vmu_volume_t vol )
nkeynes@1034
   356
{
nkeynes@1034
   357
    return vol->dirty;
nkeynes@1034
   358
}
nkeynes@1034
   359
nkeynes@1034
   360
gboolean vmu_volume_read_block( vmu_volume_t vol, vmu_partnum_t pt, unsigned int block, unsigned char *out )
nkeynes@1034
   361
{
nkeynes@1034
   362
    if( pt >= vol->part_count || block >= vol->part[pt].block_count ) {
nkeynes@1034
   363
        return FALSE;
nkeynes@1034
   364
    }
nkeynes@1034
   365
nkeynes@1034
   366
    memcpy( out, VMU_BLOCK(vol,pt,block), VMU_BLOCK_SIZE );
nkeynes@1034
   367
    return TRUE;
nkeynes@1034
   368
}
nkeynes@1034
   369
nkeynes@1034
   370
gboolean vmu_volume_write_block( vmu_volume_t vol, vmu_partnum_t pt, unsigned int block, unsigned char *in )
nkeynes@1034
   371
{
nkeynes@1034
   372
    if( pt >= vol->part_count || block >= vol->part[pt].block_count ) {
nkeynes@1034
   373
        return FALSE;
nkeynes@1034
   374
    }
nkeynes@1034
   375
    memcpy( VMU_BLOCK(vol,pt,block), in, VMU_BLOCK_SIZE );
nkeynes@1034
   376
    vol->dirty = TRUE;
nkeynes@1071
   377
    return TRUE;
nkeynes@1034
   378
}
nkeynes@1034
   379
nkeynes@1034
   380
gboolean vmu_volume_write_phase( vmu_volume_t vol, vmu_partnum_t pt, unsigned int block, unsigned int phase, unsigned char *in )
nkeynes@1034
   381
{
nkeynes@1034
   382
    if( pt >= vol->part_count || block >= vol->part[pt].block_count || phase >= 4 ) {
nkeynes@1034
   383
        return FALSE;
nkeynes@1034
   384
    }
nkeynes@1034
   385
    memcpy( VMU_BLOCK(vol,pt,block) + (phase*128), in, VMU_BLOCK_SIZE/4 );
nkeynes@1034
   386
    vol->dirty = TRUE;
nkeynes@1071
   387
    return TRUE;
nkeynes@1034
   388
}
nkeynes@1034
   389
nkeynes@1034
   390
const struct vmu_volume_metadata *vmu_volume_get_metadata( vmu_volume_t vol, vmu_partnum_t partition )
nkeynes@1034
   391
{
nkeynes@1034
   392
    if( partition >= vol->part_count ) {
nkeynes@1034
   393
        return NULL;
nkeynes@1034
   394
    } else {
nkeynes@1034
   395
        return &vol->part[partition].metadata;
nkeynes@1034
   396
    }
nkeynes@1034
   397
}
.