Compare commits

...

6 Commits

Author SHA1 Message Date
michaelangel007 c6b6d387ba Bugfix: Sapling file size can be larger then blocks used on disk. Pad with zeroes when extracting 2023-06-12 23:25:01 -07:00
michaelangel007 741a5cc36f Cleanup 2023-06-12 23:24:15 -07:00
michaelangel007 abb0c97467 Cleanup: Fix missing newline 2023-06-12 23:24:01 -07:00
michaelangel007 23ebbf0331 Add -boot to load/save the boot sector 2023-06-12 23:23:47 -07:00
michaelangel007 0839ab0a3e Load/Save file attrib _META when cp and get 2023-06-12 23:16:55 -07:00
michaelangel007 6a84f16409 Add temp zero sector 2023-06-12 23:13:05 -07:00
3 changed files with 217 additions and 54 deletions

View File

@ -25,6 +25,7 @@
size_t gnDskSize = 0; size_t gnDskSize = 0;
uint8_t gaDsk[ DSK_SIZE_32M ]; uint8_t gaDsk[ DSK_SIZE_32M ];
uint8_t gaTmp[ DSK_SECTOR_SIZE * 2 ];
uint16_t DskGet16( int offset ) uint16_t DskGet16( int offset )
{ {

View File

@ -94,7 +94,7 @@ Is this still needed?
" File type will auto-detected based on filename extension.\n" " File type will auto-detected based on filename extension.\n"
" i.e. BAS, BIN, FNT, TXT,SYS, etc.\n" " i.e. BAS, BIN, FNT, TXT,SYS, etc.\n"
" -access=$## Set access flags\n" " -access=$## Set access flags\n"
" NOTE: Defaults to $C3" " NOTE: Defaults to $C3\n"
" $80 Volume/file can be destroyed\n" " $80 Volume/file can be destroyed\n"
" $40 Volume/file can be renamed\n" " $40 Volume/file can be renamed\n"
" $20 Volume/file changed since last backup\n" " $20 Volume/file changed since last backup\n"
@ -122,16 +122,18 @@ Is this still needed?
, //CAT_LONG2 , //CAT_LONG2
" This is an alias for 'catalog'\n" " This is an alias for 'catalog'\n"
,// FILE_GET ,// FILE_GET
" [-boot=<file>] Optional: extract boot sector to file\n"
" <path> Path of virtual file to extract\n" " <path> Path of virtual file to extract\n"
" NOTES:\n" " NOTES:\n"
" The file remains on the virtual volume\n" " The file remains on the virtual volume\n"
" To delete a file see ........: rm\n" " To delete a file see ........: rm\n"
" To delete a sub-directory see: rmdir\n" " To delete a sub-directory see: rmdir\n"
, // VOL__INIT , // VOL__INIT
" <path> Name of virtual volume.\n" " [-boot=<file>] Optional: replace boot sector with file\n"
" -size=140 Format 140 KB (5 1/4\")\n" " -size=140 Format 140 KB (5 1/4\")\n"
" -size=800 Format 800 KB (3 1/2\")\n" " -size=800 Format 800 KB (3 1/2\")\n"
" -size=32 Format 32 MB (Hard Disk)\n" " -size=32 Format 32 MB (Hard Disk)\n"
" <path> Name of virtual volume.\n"
, // CAT__NAMES , // CAT__NAMES
" [<path>] Path to sub-directory to view\n" " [<path>] Path to sub-directory to view\n"
" Defaults to: /\n" " Defaults to: /\n"
@ -154,6 +156,7 @@ Is this still needed?
" (Normally a sub-directory must be empty)\n" " (Normally a sub-directory must be empty)\n"
, // DIR__DELETE2 , // DIR__DELETE2
" Alias for rmdir\n" " Alias for rmdir\n"
" NOTE: Not implemented yet!\n"
, NULL , NULL
}; };
@ -207,6 +210,12 @@ int usage()
" prodosfs test.dsk init -size=800 /TEST312 # 3 1/2\" (800 KB)\n" " prodosfs test.dsk init -size=800 /TEST312 # 3 1/2\" (800 KB)\n"
" prodosfs test.dsk init -size=32 /TEST32M #HardDisk ( 32 MB)\n" " prodosfs test.dsk init -size=32 /TEST32M #HardDisk ( 32 MB)\n"
"\n" "\n"
"To put a (512) boot sector file use -boot with the init command:\n"
" prodosfs test.dsk init -boot=bootsector.bin -size=140 /TEST514\n"
"\n"
"To get a (512) boot sector file use -boot with the cp command:\n"
" prodosfs test.dsk cp -boot=bootsector.bin /TEST514\n"
"\n"
"Examples:\n" "Examples:\n"
"\n" "\n"
" prodosfs test.dsk ls\n" " prodosfs test.dsk ls\n"
@ -515,6 +524,8 @@ bool doCopy( ProDOS_FileHeader_t *entry, const char *filename )
} }
} }
prodos_MetaLoad( &gEntry );
#if DEBUG_MAIN #if DEBUG_MAIN
printf( "File Access: $%02X\n", gEntry.access ); printf( "File Access: $%02X\n", gEntry.access );
#endif #endif
@ -755,16 +766,14 @@ int main( const int nArg, const char *aArg[] )
#endif #endif
if( pBootSectorFileName ) if( pBootSectorFileName )
{
ProDOS_ExtractBootSector( pBootSectorFileName ); ProDOS_ExtractBootSector( pBootSectorFileName );
// loaded = ProDOS_ReplaceBootSector( pBootSectorFileName );
// if( loaded ) DskSave();
}
else else
ProDOS_FileExtract( gpPath ); // pathname_filename ProDOS_FileExtract( gpPath ); // pathname_filename
break; break;
} }
case DISK_COMMAND_VOL_INIT: case DISK_COMMAND_VOL_INIT:
{
gnDskSize = DSK_SIZE_312; // TODO: --size=140 --size=800 --size=32 gnDskSize = DSK_SIZE_312; // TODO: --size=140 --size=800 --size=32
if( !DskGetInterleave( gpDskName ) ) if( !DskGetInterleave( gpDskName ) )
errorBadInterleave(); errorBadInterleave();
@ -772,6 +781,7 @@ int main( const int nArg, const char *aArg[] )
#if DEBUG_MAIN #if DEBUG_MAIN
printf( "iArg: %d / %d\n", iArg, nArg ); printf( "iArg: %d / %d\n", iArg, nArg );
#endif #endif
const char *pBootSectorFileName = NULL;
for( ; iArg < nArg; iArg++ ) for( ; iArg < nArg; iArg++ )
{ {
@ -783,6 +793,14 @@ int main( const int nArg, const char *aArg[] )
if( pArg[0] == '-' ) if( pArg[0] == '-' )
{ {
if( strncmp( pArg+1,"boot=", 5 ) == 0 )
{
if( pBootSectorFileName )
printf( "ERROR: Already have boot sector filename. Skipping.\n" );
else
pBootSectorFileName = pArg + 6;
}
else
if( strncmp( pArg+1,"size=", 5 ) == 0 ) if( strncmp( pArg+1,"size=", 5 ) == 0 )
{ {
int size = atoi( pArg + 6 ); int size = atoi( pArg + 6 );
@ -812,12 +830,19 @@ int main( const int nArg, const char *aArg[] )
if( gpPath ) if( gpPath )
{ {
ProDOS_Init( gpPath ); ProDOS_Init( gpPath );
if( pBootSectorFileName )
{
bool bReplaced = ProDOS_ReplaceBootSector( pBootSectorFileName );
if( !bReplaced )
printf( "ERROR: Couldn't replace boot sector\n" );
}
DskSave(); DskSave();
} }
else else
return printf( "ERROR: Need virtual volume name. e.g. /TEST\n" ); return printf( "ERROR: Need virtual volume name. e.g. /TEST\n" );
break; break;
}
default: default:
if( (nArg < 2) || !pCommand ) if( (nArg < 2) || !pCommand )

View File

@ -1,4 +1,5 @@
#define DEBUG_ADD 0 #define DEBUG_ADD 0
#define DEBUG_ATTRIB 0
#define DEBUG_BITMAP 0 #define DEBUG_BITMAP 0
#define DEBUG_DATE 0 #define DEBUG_DATE 0
#define DEBUG_DIR 0 #define DEBUG_DIR 0
@ -16,7 +17,8 @@
ProDOS_* Public functions ProDOS_* Public functions
*/ */
#define min(a,b) ((a < b) ? a : b)
#define max(a,b) ((a < b) ? b : a)
// --- ProDOS crap --- // --- ProDOS crap ---
@ -164,7 +166,7 @@ if( block == PRODOS_ROOT_BLOCK )
struct ProDOS_FileHeader_t struct ProDOS_FileHeader_t
{ ; //Rel Size Hex { ; //Rel Size Hex
uint8_t kind ; // +0 1 $00 \ Hi nibble Storage Type uint8_t kind ; // +0 1 $00 \ Hi nibble Storage Type
uint8_t len ; // +0 / Lo nibble uint8_t len ; // +0 / Lo nibble Filename Length
char name[ 16 ] ; // +1 15 $05 15 on disk but we NULL terminate for convenience char name[ 16 ] ; // +1 15 $05 15 on disk but we NULL terminate for convenience
// --- diff from volume --- // --- diff from volume ---
uint8_t type ; //+16 1 $10 User Type uint8_t type ; //+16 1 $10 User Type
@ -670,6 +672,95 @@ if( bitmap )
} }
// ------------------------------------------------------------------------
void prodos_MetaGetFileName( ProDOS_FileHeader_t *pEntry, char sAttrib[ PRODOS_MAX_PATH ] )
{
if( !pEntry )
return;
// Attribute meta-data
const char sExt[] = "._META";
const size_t nExt = strlen( sExt );
int nAttrib = string_CopyUpper( sAttrib + 0, pEntry->name, pEntry->len );
/* */ string_CopyUpper( sAttrib + nAttrib, sExt , nExt );
}
// ------------------------------------------------------------------------
bool prodos_MetaLoad(ProDOS_FileHeader_t* pEntry)
{
char sAttrib[ PRODOS_MAX_PATH ];
prodos_MetaGetFileName( pEntry, sAttrib );
printf( "Loading meta... %s\n", sAttrib );
FILE *pFileMeta = fopen( sAttrib, "r" );
if( !pFileMeta )
{
printf( "INFO.: Couldn't open attribute file for reads: %s\n", sAttrib );
return false;
}
int value;
fscanf( pFileMeta, "access = $%X\n", &value ); pEntry->access = value; // 02
fscanf( pFileMeta, "aux = $%X\n", &value ); pEntry->aux = value; // 04
fscanf( pFileMeta, "type = $%X\n", &value ); pEntry->type = value; // 02
fscanf( pFileMeta, "kind = $%X\n", &value ); pEntry->kind = value; // 02
fscanf( pFileMeta, "date = $%X\n", &value ); pEntry->date = value; // 04
fscanf( pFileMeta, "time = $%X\n", &value ); pEntry->time = value; // 04
fscanf( pFileMeta, "version = $%X\n", &value ); pEntry->cur_ver = value; // 02
fscanf( pFileMeta, "minver = $%X\n", &value ); pEntry->min_ver = value; // 02
fscanf( pFileMeta, "moddate = $%X\n", &value ); pEntry->mod_date = value; // 04
fscanf( pFileMeta, "modtime = $%X\n", &value ); pEntry->mod_time = value; // 04
#if DEBUG_ATTRIB
printf( "access = $%02X\n", pEntry->access );
printf( "aux = $%04X\n", pEntry->aux );
printf( "type = $%02X\n", pEntry->type );
printf( "kind = $%02X\n", pEntry->kind );
printf( "date = $%04X\n", pEntry->date );
printf( "time = $%04X\n", pEntry->time );
printf( "version = $%02X\n", pEntry->cur_ver );
printf( "minver = $%02X\n", pEntry->min_ver );
printf( "moddate = $%04X\n", pEntry->mod_date );
printf( "modtime = $%04X\n", pEntry->mod_time );
#endif
fclose( pFileMeta );
return true;
}
// ------------------------------------------------------------------------
bool prodos_MetaSave( ProDOS_FileHeader_t *pEntry )
{
char sAttrib[ PRODOS_MAX_PATH ];
prodos_MetaGetFileName( pEntry, sAttrib );
printf( "Saving meta... %s\n", sAttrib );
FILE *pFileMeta = fopen( sAttrib, "w+b" );
if( !pFileMeta )
{
printf( "ERROR: Couldnt' open attribute file for writing: %s\n", sAttrib );
return false;
}
fprintf( pFileMeta, "access = $%02X\n", pEntry->access );
fprintf( pFileMeta, "aux = $%04X\n", pEntry->aux );
fprintf( pFileMeta, "type = $%02X\n", pEntry->type );
fprintf( pFileMeta, "kind = $%02X\n", pEntry->kind );
fprintf( pFileMeta, "date = $%04X\n", pEntry->date );
fprintf( pFileMeta, "time = $%04X\n", pEntry->time );
fprintf( pFileMeta, "version = $%02X\n", pEntry->cur_ver );
fprintf( pFileMeta, "minver = $%02X\n", pEntry->min_ver );
fprintf( pFileMeta, "moddate = $%04X\n", pEntry->mod_date );
fprintf( pFileMeta, "modtime = $%04X\n", pEntry->mod_time );
fclose( pFileMeta );
return true;
}
// ------------------------------------------------------------------------ // ------------------------------------------------------------------------
void prodos_Summary( ProDOS_VolumeHeader_t *volume, int files, int iFirstFree ) void prodos_Summary( ProDOS_VolumeHeader_t *volume, int files, int iFirstFree )
{ {
@ -1117,6 +1208,9 @@ bool ProDOS_FileAdd( const char *to_path, const char *from_filename, ProDOS_File
int iIndexBase = 0; // Single Index int iIndexBase = 0; // Single Index
int iMasterIndex = 0; // master block points to N IndexBlocks int iMasterIndex = 0; // master block points to N IndexBlocks
#if DEBUG_ATTRIB
printf( "Source File Size: %06X (%d)\n", nSrcSize, nSrcSize );
#endif
if( nSrcSize > gnDskSize ) if( nSrcSize > gnDskSize )
{ {
@ -1304,7 +1398,7 @@ void ProDOS_FileDelete( const char *path )
// Copy a file from the virtual file system back to the host // Copy a file from the virtual file system back to the host
// ProDOS attributes are saved in file.prodos_meta // ProDOS attributes are saved in <file>._META
// ======================================================================== // ========================================================================
bool ProDOS_FileExtract( const char *path ) bool ProDOS_FileExtract( const char *path )
{ {
@ -1343,34 +1437,8 @@ bool ProDOS_FileExtract( const char *path )
return false; return false;
} }
const char sExt[] = "._meta"; if( !prodos_MetaSave( pEntry ) )
const size_t nExt = strlen( sExt );
/* */ char sAttrib[ PRODOS_MAX_PATH ];
int nAttrib = string_CopyUpper( sAttrib + 0, pEntry->name, pEntry->len );
/* */ string_CopyUpper( sAttrib + nAttrib, sExt , nExt );
printf( "Saving meta... %s\n", sAttrib );
FILE *pFileMeta = fopen( sAttrib, "w+b" );
if( !pFileMeta )
{
printf( "ERROR: Couldnt' open attribute file for writing: %s\n", sAttrib );
return false; return false;
}
// TODO: Sync these up with <file>._META ProDOS_FileExtract() and getCopyConfig()
fprintf( pFileMeta, "access = $%02X\n", pEntry->access );
fprintf( pFileMeta, "aux = $%04X\n", pEntry->aux );
fprintf( pFileMeta, "type = $%02X\n", pEntry->type );
fprintf( pFileMeta, "kind = $%02X\n", pEntry->kind );
fprintf( pFileMeta, "date = $%04X\n", pEntry->date );
fprintf( pFileMeta, "time = $%04X\n", pEntry->time );
fprintf( pFileMeta, "version = $%02X\n", pEntry->cur_ver );
fprintf( pFileMeta, "minver = $%02X\n", pEntry->min_ver );
fprintf( pFileMeta, "moddate = $%04X\n", pEntry->mod_date );
fprintf( pFileMeta, "modtime = $%04X\n", pEntry->mod_time );
fclose( pFileMeta );
int addr = pEntry->inode * PRODOS_BLOCK_SIZE; int addr = pEntry->inode * PRODOS_BLOCK_SIZE;
int size = pEntry->size; int size = pEntry->size;
@ -1385,7 +1453,7 @@ bool ProDOS_FileExtract( const char *path )
printf( "ERROR: Couldn't open data file for writing: %s\n", pEntry->name ); printf( "ERROR: Couldn't open data file for writing: %s\n", pEntry->name );
return false; return false;
} }
else
{ {
switch( kind ) switch( kind )
{ {
@ -1397,15 +1465,36 @@ bool ProDOS_FileExtract( const char *path )
case ProDOS_KIND_SAPL: // <= 128 KB case ProDOS_KIND_SAPL: // <= 128 KB
{ {
int nBlock = pEntry->blocks - 1; // 1st block is index block int nBlock = pEntry->blocks - 1; // 1st block is index block
int nBytes = size;
#if DEBUG_EXTRACT
printf( "ProDOS File Size: $%06X (%d)\n", size, size );
printf( "i-node (8-bit) : @ $%04X\n" , pEntry->inode );
// printf( "Filename Length : %04X\n", pEntry->len );
printf( "File Blocks : $%04X (%d)\n", pEntry->blocks, pEntry->blocks ); // Includes i-nodes
printf( "Total Blocks : $%04X (%d)\n", nBlock , nBlock );
#endif
for( int iBlock = 0; iBlock < nBlock; iBlock++ ) for( int iBlock = 0; iBlock < nBlock; iBlock++ )
{ {
int iDataBlock = DskGetIndexBlock( addr, iBlock ); int iDataBlock = DskGetIndexBlock( addr, iBlock );
int iDataOffset = iDataBlock * PRODOS_BLOCK_SIZE; int iDataOffset = iDataBlock * PRODOS_BLOCK_SIZE;
int nSlack = min( nBytes, PRODOS_BLOCK_SIZE);
nBytes -= PRODOS_BLOCK_SIZE;
#if DEBUG_EXTRACT
int bLastBlock = (iBlock == (nBlock - 1));
printf( "Block: %02X/%02X @ %04X, LastBlock? %d, Bytes: %6d, Slack: %3d\n", iBlock, nBlock-1, iDataBlock, bLastBlock, nBytes + PRODOS_BLOCK_SIZE, nSlack );
#endif
fwrite( &gaDsk[ iDataOffset ], 1, nSlack, pFileData );
}
if( iBlock != nBlock - 1 ) // File size is larger then blocks used on disk?!
fwrite( &gaDsk[ iDataOffset ], 1, PRODOS_BLOCK_SIZE, pFileData ); if( nBytes > 0 )
else {
fwrite( &gaDsk[ iDataOffset ], 1, pEntry->size % PRODOS_BLOCK_SIZE, pFileData ); // pad with zeroes
#if DEBUG_EXTRACT
int nSlack = min( nBytes, PRODOS_BLOCK_SIZE);
printf( "PADDING extra ZERO Bytes: %6d, Slack: %3d\n", nBytes, nSlack );
#endif
fwrite( &gaTmp, 1, nBytes, pFileData );
} }
break; break;
} }
@ -1432,11 +1521,11 @@ void ProDOS_Init( const char *path )
{ {
// Zero disk // Zero disk
memset( gaDsk, 0, gnDskSize ); memset( gaDsk, 0, gnDskSize );
memset( gaTmp, 0, PRODOS_BLOCK_SIZE );
// Copy Boot Sector // Copy Boot Sector
// TODO: Use ProDOS 2.4.1 boot sector // TODO: Use ProDOS 2.4.1 boot sector
// Create blocks for root directory // Create blocks for root directory
int nRootDirBlocks = 4; int nRootDirBlocks = 4;
int iPrevDirBlock = 0; int iPrevDirBlock = 0;
@ -1558,7 +1647,7 @@ void ProDOS_Init( const char *path )
// Read T0S0 and save it to a file on the host // Read T0S0 and save it to a file on the host
// @returns 0 if succes // @returns true if succes
// ======================================================================== // ========================================================================
bool ProDOS_ExtractBootSector( const char *pBootSectorFileName ) bool ProDOS_ExtractBootSector( const char *pBootSectorFileName )
{ {
@ -1570,7 +1659,24 @@ bool ProDOS_ExtractBootSector( const char *pBootSectorFileName )
return false; return false;
} }
fwrite( &gaDsk[ 0 ], 1, 256, pDstFile ); const size_t BLOCK_0_BEG = 0x0*DSK_SECTOR_SIZE;
const size_t BLOCK_0_END = 0x1*DSK_SECTOR_SIZE;
fwrite( &gaDsk[ BLOCK_0_BEG ], 1, 256, pDstFile );
fwrite( &gaDsk[ BLOCK_0_END ], 1, 256, pDstFile );
#if _DEBUG
for( int sector = 0; sector < 16; ++sector )
{
printf( "T0S%1X: ", sector );
for( int byte = 0; byte < 4; ++byte )
{
printf( "%02X ", gaDsk[ sector*DSK_SECTOR_SIZE + byte ] );
}
printf( "\n" );
}
#endif
fclose( pDstFile ); fclose( pDstFile );
return true; return true;
@ -1592,22 +1698,53 @@ bool ProDOS_ReplaceBootSector( const char *pBootSectorFileName )
size_t size = File_Size( pSrcFile ); size_t size = File_Size( pSrcFile );
// size < 256 const size_t BLOCK_0_BEG = 0x0*DSK_SECTOR_SIZE;
memset( &gaDsk[ 0 ], 0, 256 ); const size_t BLOCK_0_END = 0x1*DSK_SECTOR_SIZE;
if( size < 256 )
printf( "INFO.: Boot sector < 256 bytes. Padding boot sector with zeroes.\n" );
// size >= 256 memset( &gaDsk[ BLOCK_0_BEG ], 0, 256 ); // Block 0 Beg = T0S0
if( size > 255 ) memset( &gaDsk[ BLOCK_0_END ], 0, 256 ); // Block 0 End = T0SE
if( size < 512 )
printf( "INFO.: Boot sector < 512 bytes. Padding boot sector with zeroes.\n" );
if( size > 512 )
{ {
printf( "WARNING: Boot sector > 255 bytes. Truncating to first 256 bytes.\n" ); printf( "WARNING: Boot sector > 512 bytes. Truncating to first 512 bytes.\n" );
size = 256; size = 512;
} }
fread( &gaDsk[ 0 ], 1, size, pSrcFile ); const size_t prefix = min( size , 256 );
const size_t suffix = min( size-256, 256 );
#if _DEBUG
printf( "prefix: $%02X (#%3d)\n", (int) prefix & 0xFF, (int) prefix );
printf( "suffix: $%02X (#%3d)\n", (int) prefix & 0xFF, (int) suffix );
#endif
if( prefix ) fread( &gaDsk[ BLOCK_0_BEG ], 1, prefix, pSrcFile );
if( suffix ) fread( &gaDsk[ BLOCK_0_END ], 1, suffix, pSrcFile );
#if 0
if (size <= 256)
{
fread( &gaDsk[ BLOCK_0_BEG ], 1, size, pSrcFile );
}
else
{
// First 256 bytes to T0S0
fread( &gaDsk[ BLOCK_0_BEG ], 1, 256, pSrcFile );
// Second 256 bytes to T0SE
if( size < 512 )
fread( &gaDsk[ BLOCK_0_END ], 1, size, pSrcFile );
else
fread( &gaDsk[ BLOCK_0_END ], 1, 512, pSrcFile );
}
#endif
fclose( pSrcFile ); fclose( pSrcFile );
// Caller will do: DskSave(); // NOTE: Caller will do: DskSave();
return true; return true;
} }