mirror of
https://github.com/AppleWin/AppleWin.git
synced 2025-07-16 20:24:03 +00:00
* Move ProDOS utils to separate file. Signed-off-by: Andrea Odetti <mariofutire@gmail.com> * Use std::vector to fix and avoid memory leaks. Signed-off-by: Andrea Odetti <mariofutire@gmail.com> * Remove TEXT and TCHAR. Signed-off-by: Andrea Odetti <mariofutire@gmail.com> --------- Signed-off-by: Andrea Odetti <mariofutire@gmail.com>
573 lines
20 KiB
C++
573 lines
20 KiB
C++
/*
|
|
AppleWin : An Apple //e emulator for Windows
|
|
|
|
Copyright (C) 1994-1996, Michael O'Brien
|
|
Copyright (C) 1999-2001, Oliver Schmidt
|
|
Copyright (C) 2002-2005, Tom Charlesworth
|
|
Copyright (C) 2006-2014, Tom Charlesworth, Michael Pohoreski
|
|
|
|
AppleWin is free software; you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License as published by
|
|
the Free Software Foundation; either version 2 of the License, or
|
|
(at your option) any later version.
|
|
|
|
AppleWin is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with AppleWin; if not, write to the Free Software
|
|
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
*/
|
|
|
|
/* Description: Add ProDOS File System support for formatting disk images
|
|
* Author: Michael Pohoreski
|
|
*/
|
|
|
|
// --- ProDOS Consts ---
|
|
|
|
const size_t PRODOS_BLOCK_SIZE = 0x200; // 512 bytes/block
|
|
const size_t PRODOS_ROOT_BLOCK = 2;
|
|
const int PRODOS_ROOT_OFFSET = PRODOS_ROOT_BLOCK * PRODOS_BLOCK_SIZE;
|
|
|
|
const int PRODOS_MAX_FILENAME = 15;
|
|
const int PRODOS_MAX_PATH = 64; // TODO: Verify
|
|
|
|
const uint8_t ACCESS_D = 0x80; // Can destroy
|
|
const uint8_t ACCESS_N = 0x40; // Can rename
|
|
const uint8_t ACCESS_B = 0x20; // Can backup
|
|
const uint8_t ACCESS_Z4= 0x10; // not used - must be zero?
|
|
const uint8_t ACCESS_Z3= 0x08; // not used - must be zero?
|
|
const uint8_t ACCESS_I = 0x04; // invisible
|
|
const uint8_t ACCESS_W = 0x02; // Can write
|
|
const uint8_t ACCESS_R = 0x01; // Can read
|
|
|
|
enum PRODOS_Kind_e
|
|
{
|
|
PRODOS_KIND_DEL = 0x0, // Deleted file or unused
|
|
PRODOS_KIND_SEED = 0x1, // No meta block, single block contains the data
|
|
PRODOS_KIND_SAPL = 0x2, // 1st Block is allocated blocks
|
|
PRODOS_KIND_TREE = 0x3, // Multiple meta blocks
|
|
PRODOS_KIND_PAS = 0x4, // http://www.1000bit.it/support/manuali/apple/technotes/pdos/tn.pdos.25.html
|
|
PRODOS_KIND_GSOS = 0x5, // http://www.1000bit.it/support/manuali/apple/technotes/pdos/tn.pdos.25.html
|
|
PRODOS_KIND_DIR = 0xD, // parent block entry for sub-directory
|
|
PRODOS_KIND_SUB = 0xE, // subdir header entry to find parent directory; meta to reach parent
|
|
PRODOS_KIND_ROOT = 0xF, // volume header entry for root directory
|
|
NUM_PRODOS_KIND = 16
|
|
};
|
|
|
|
// --- ProDOS Structs ---
|
|
struct ProDOS_SubDirInfo_t
|
|
{
|
|
uint8_t res75 ; // $75 - magic number
|
|
uint8_t pad7[7];
|
|
};
|
|
|
|
struct ProDOS_VolumeExtra_t
|
|
{
|
|
uint16_t bitmap_block;
|
|
uint16_t total_blocks;
|
|
};
|
|
|
|
struct ProDOS_SubDirExtra_t
|
|
{
|
|
uint16_t parent_block;
|
|
uint8_t parent_entry_num;
|
|
uint8_t parent_entry_len;
|
|
};
|
|
|
|
// Due to default compiler packing/alignment this may NOT be byte-size identical on disk but it is in the correct order.
|
|
// NOTE: This does NOT need to be packed/aligned since fields are read/written on a per entry basis.
|
|
// See: ProDOS_GetVolumeHeader(), ProDOS_SetVolumeHeader()
|
|
struct ProDOS_VolumeHeader_t
|
|
{ ; //Rel Size Abs
|
|
uint8_t kind ; // +0 1 $04 \ Hi nibble Storage Type
|
|
uint8_t len ; // +0 / Lo nibble
|
|
char name[ 16 ] ; // +1 15 $05 15 on disk but we NULL terminate for convenience
|
|
// --- diff from file ---
|
|
union
|
|
{
|
|
uint8_t pad8[8]; //+16 8 $14 only in root
|
|
ProDOS_SubDirInfo_t info ; // only in sub-dir
|
|
} ;
|
|
// --- same as file ---
|
|
uint16_t date ; //+24 2 $1C
|
|
uint16_t time ; //+26 2 $1E
|
|
uint8_t cur_ver ; //+28 1 $20
|
|
uint8_t min_ver ; //+29 1 $21
|
|
uint8_t access ; //+30 1 $22
|
|
uint8_t entry_len ; //+31 1 $23 Size of each directory entry in bytes
|
|
uint8_t entry_num ; //+32 1 $24 Number of directory entries per block
|
|
uint16_t file_count ; //+33 2 $25 Active entries including directories, excludes volume header
|
|
union // --
|
|
{ // --
|
|
ProDOS_VolumeExtra_t meta ; //+34 2 $27
|
|
ProDOS_SubDirExtra_t subdir; //+36 2 $29
|
|
} ; //============
|
|
} ; // 38 $2B
|
|
|
|
// Due to default compiler packing/alignment this may NOT be byte-size identical on disk but it is in the correct order.
|
|
// NOTE: This does NOT need to be packed/aligned since fields are read/written on a per-entry basis.
|
|
// See: ProDOS_GetFileHeader(), ProDOS_SetFileHeader()
|
|
struct ProDOS_FileHeader_t
|
|
{ ; //Rel Size Hex
|
|
uint8_t kind ; // +0 1 $00 \ Hi nibble Storage Type
|
|
uint8_t len ; // +0 / Lo nibble Filename Length
|
|
char name[ 16 ] ; // +1 15 $05 15 on disk but we NULL terminate for convenience
|
|
// --- diff from volume ---
|
|
uint8_t type ; //+16 1 $10 User Type
|
|
uint16_t inode ; //+17 2 $11
|
|
uint16_t blocks ; //+19 2 $13
|
|
uint32_t size ; //+21 3 $15 EOF address - on disk is 3 bytes, but 32-bit for convenience
|
|
// --- same as volume ---
|
|
uint16_t date ; //+24 2 $18
|
|
uint16_t time ; //+26 2 $1A
|
|
uint8_t cur_ver ; //+28 1 $1C
|
|
uint8_t min_ver ; //+29 1 $1D // 0 = ProDOS 1.0
|
|
uint8_t access ; //+30 1 $1E
|
|
// --- diff from subdir ---
|
|
uint16_t aux ; //+31 2 $1F Load Address for Binary
|
|
// --- diff from volume ---
|
|
uint16_t mod_date ; //+33 2 $21
|
|
uint16_t mod_time ; //+35 2 $23
|
|
uint16_t dir_block ; //+37 2 $25 pointer to directory block
|
|
; //============
|
|
}; ; // 39 $27
|
|
|
|
|
|
// --- Prototypes ---
|
|
|
|
void ProDOS_GetFileHeader( uint8_t *pDiskBytes, int nOffset, ProDOS_FileHeader_t *pFileHeader_ );
|
|
|
|
// --- Date/Time ---
|
|
|
|
// | Byte 1 | Byte 0 | Bytes
|
|
// +-------------------------------+-------------------------------+
|
|
// | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | Bits
|
|
// +---------------------------------------------------------------+
|
|
// <-----------Year-----------> <----Month----> <-------Day-------> Date
|
|
uint16_t ProDOS_PackDate ( int year, int month, int day )
|
|
{
|
|
uint16_t date = 0
|
|
| ((year & 0x7F) << 9)
|
|
| ((month & 0x0F) << 5)
|
|
| ((day & 0x1F) << 0)
|
|
;
|
|
return date;
|
|
}
|
|
|
|
// | Byte 1 | Byte 0 | Bytes
|
|
// +---------------------------------------------------------------+
|
|
// | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | Bits
|
|
// +---------------------------------------------------------------+
|
|
// <----0----> <------Hours------> <--0--> <-------Minutes-------> Time
|
|
uint16_t ProDOS_PackTime (int hours, int minutes)
|
|
{
|
|
uint16_t time = 0
|
|
| ((hours & 0x1F) << 8)
|
|
| ((minutes & 0x3F) << 0)
|
|
;
|
|
return time;
|
|
}
|
|
|
|
// --- ProDOS Utility Byte Functions ---
|
|
|
|
// Helpers for 8 bit, 16-bit, 24-bit values
|
|
// ------------------------------------------------------------------------
|
|
inline uint16_t ProDOS_Get16 ( uint8_t *pDiskBytes, int offset )
|
|
{
|
|
uint16_t n = 0
|
|
| (((uint16_t)pDiskBytes[ offset + 0 ]) << 0)
|
|
| (((uint16_t)pDiskBytes[ offset + 1 ]) << 8)
|
|
;
|
|
return n;
|
|
}
|
|
|
|
// ------------------------------------------------------------------------
|
|
inline uint32_t ProDOS_Get24 ( uint8_t *pDiskBytes, int offset )
|
|
{
|
|
uint32_t n = 0
|
|
| (((uint32_t)pDiskBytes[ offset + 0 ]) << 0)
|
|
| (((uint32_t)pDiskBytes[ offset + 1 ]) << 8)
|
|
| (((uint32_t)pDiskBytes[ offset + 2 ]) << 16)
|
|
;
|
|
return n;
|
|
}
|
|
|
|
// ------------------------------------------------------------------------
|
|
inline void ProDOS_Put16 ( uint8_t *pDiskBytes, int offset, int val )
|
|
{
|
|
pDiskBytes[ offset + 0 ] = (val >> 0) & 0xFF;
|
|
pDiskBytes[ offset + 1 ] = (val >> 8) & 0xFF;
|
|
}
|
|
|
|
// ------------------------------------------------------------------------
|
|
inline void ProDOS_Put24 ( uint8_t *pDiskBytes, int offset, int val )
|
|
{
|
|
pDiskBytes[ offset + 0 ] = (val >> 0) & 0xFF;
|
|
pDiskBytes[ offset + 1 ] = (val >> 8) & 0xFF;
|
|
pDiskBytes[ offset + 2 ] = (val >>16) & 0xFF;
|
|
}
|
|
|
|
// --- ProDOS Block Functions ---
|
|
|
|
// ------------------------------------------------------------------------
|
|
inline int ProDOS_BlockGetDirectoryBlockCount ( uint8_t *pDiskBytes, int nOffset )
|
|
{
|
|
int nBlocks = 0;
|
|
int nNextBlock = 0;
|
|
|
|
do
|
|
{
|
|
nBlocks++;
|
|
nNextBlock = ProDOS_Get16( pDiskBytes, nOffset + 2 );
|
|
nOffset = nNextBlock * PRODOS_BLOCK_SIZE;
|
|
} while( nNextBlock );
|
|
|
|
return nBlocks;
|
|
}
|
|
|
|
// ------------------------------------------------------------------------
|
|
int ProDOS_BlockGetFirstFree ( uint8_t *pDiskBytes, size_t nDiskSize, ProDOS_VolumeHeader_t *pVolume )
|
|
{
|
|
if( !pVolume )
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
int bitmap = pVolume->meta.bitmap_block;
|
|
int offset = bitmap * PRODOS_BLOCK_SIZE;
|
|
int size = (nDiskSize + 7) / 8;
|
|
int block = 0;
|
|
|
|
for( int byte = 0; byte < size; byte++ )
|
|
{
|
|
int mask = 0x80;
|
|
do
|
|
{
|
|
if( pDiskBytes[ offset + byte ] & mask )
|
|
return block;
|
|
|
|
mask >>= 1;
|
|
block++;
|
|
}
|
|
while( mask );
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
// ------------------------------------------------------------------------
|
|
int ProDOS_BlockGetPathOffset ( uint8_t *pDiskBytes, ProDOS_VolumeHeader_t *pVolume, const char *pProDOSPath )
|
|
{
|
|
int nOffset = PRODOS_ROOT_OFFSET; // Block 2 * 0x200 Bytes/Block = 0x400 abs offset
|
|
|
|
#if _DEBUG
|
|
ProDOS_VolumeHeader_t tVolume;
|
|
memset( &tVolume, 0, sizeof(tVolume) );
|
|
|
|
int iDirBlock = nOffset / PRODOS_BLOCK_SIZE;
|
|
if (!pVolume)
|
|
pVolume = &tVolume;
|
|
|
|
int nDirBlocks = ProDOS_BlockGetDirectoryBlockCount( pDiskBytes, nOffset );
|
|
LogOutput( "Directory Entry Bytes : $%04X\n", pVolume->entry_len );
|
|
LogOutput( "Directory Entries/Block: %d\n" , pVolume->entry_num );
|
|
LogOutput( "VOLUME Directory Blocks: %d\n" , nDirBlocks );
|
|
// LogOutput( "...Alt. max dir files : %d\n" ,(nDirBlocks * PRODOS_BLOCK_SIZE) / pVolume->entry_len ); // 2nd way to verify
|
|
#endif
|
|
|
|
if( !pProDOSPath )
|
|
return nOffset;
|
|
|
|
if (strcmp( pProDOSPath, "/" ) == 0)
|
|
return nOffset;
|
|
|
|
// NOT IMPLEMENTED
|
|
// return ProDOS_FindFile( pDiskBytes, pVolume, pProDOSPath, nOffset );
|
|
return 0;
|
|
}
|
|
|
|
// ------------------------------------------------------------------------
|
|
int ProDOS_BlockInitFree ( uint8_t *pDiskBytes, size_t nDiskSize, ProDOS_VolumeHeader_t *volume )
|
|
{
|
|
int bitmap = volume->meta.bitmap_block;
|
|
int offset = bitmap * PRODOS_BLOCK_SIZE;
|
|
int blocks = (nDiskSize + PRODOS_BLOCK_SIZE - 1) / PRODOS_BLOCK_SIZE;
|
|
int size = (blocks + 7) / 8;
|
|
|
|
memset( &pDiskBytes[ offset ], 0xFF, size );
|
|
|
|
volume->meta.total_blocks = blocks;
|
|
return (size + PRODOS_BLOCK_SIZE - 1) / PRODOS_BLOCK_SIZE;
|
|
}
|
|
|
|
// ------------------------------------------------------------------------
|
|
bool ProDOS_BlockSetUsed ( uint8_t *pDiskBytes, ProDOS_VolumeHeader_t *pVolume, int block )
|
|
{
|
|
if( !pVolume )
|
|
{
|
|
return false;
|
|
}
|
|
|
|
int bitmap = pVolume->meta.bitmap_block;
|
|
int offset = bitmap * PRODOS_BLOCK_SIZE;
|
|
|
|
int byte = block / 8;
|
|
int bits = block % 8;
|
|
int mask = 0x80 >> bits;
|
|
|
|
pDiskBytes[ offset + byte ] |= mask;
|
|
pDiskBytes[ offset + byte ] ^= mask;
|
|
|
|
return true;
|
|
}
|
|
|
|
// ------------------------------------------------------------------------
|
|
inline int ProDOS_GetIndexBlock ( uint8_t *pDiskBytes, int offset, int index )
|
|
{
|
|
int block = 0
|
|
| (pDiskBytes[ offset + index + 0 ] << 0)
|
|
| (pDiskBytes[ offset + index + 256 ] << 8)
|
|
;
|
|
return block;
|
|
}
|
|
|
|
/*
|
|
000:lo0 lo1 lo2 ...
|
|
100:hi0 hi1 hi2 ...
|
|
*/
|
|
// ------------------------------------------------------------------------
|
|
inline void ProDOS_PutIndexBlock ( uint8_t *pDiskBytes, int offset, int index, int block )
|
|
{
|
|
#if 0
|
|
std::string message;
|
|
message = StrFormat( "File Bitmap @ Block $%04X: $%02X: Block $%04X\n", offset/PRODOS_BLOCK_SIZE, index, block );
|
|
OutputDebugStringA( message.c_str() );
|
|
#endif
|
|
pDiskBytes[ offset + index + 0 ] = (block >> 0) & 0xFF;
|
|
pDiskBytes[ offset + index + 256 ] = (block >> 8) & 0xFF;
|
|
}
|
|
|
|
// --- ProDOS Directory Functions ---
|
|
|
|
// @param nBase DiskImageOffset of directory
|
|
// returns DiskImageOffset
|
|
// ------------------------------------------------------------------------
|
|
int ProDOS_DirGetFirstFreeEntryOffset ( uint8_t *pDiskBytes, ProDOS_VolumeHeader_t *pVolume, int nBase )
|
|
{
|
|
int iNextBlock;
|
|
int iPrevBlock;
|
|
int iOffset = nBase + 4; // Prev Block, Next Block
|
|
|
|
// Try to find a free file entry in this directory
|
|
do
|
|
{
|
|
iPrevBlock = ProDOS_Get16( pDiskBytes, nBase + 0 );
|
|
iNextBlock = ProDOS_Get16( pDiskBytes, nBase + 2 );
|
|
|
|
for( int iFile = 0; iFile < pVolume->entry_num; iFile++ )
|
|
{
|
|
ProDOS_FileHeader_t file;
|
|
ProDOS_GetFileHeader( pDiskBytes, iOffset, &file );
|
|
|
|
if (file.kind == PRODOS_KIND_DEL)
|
|
return iOffset;
|
|
|
|
if (file.len == 0)
|
|
return iOffset;
|
|
|
|
iOffset += pVolume->entry_len;
|
|
}
|
|
|
|
nBase = iNextBlock * PRODOS_BLOCK_SIZE;
|
|
iOffset = nBase + 4;
|
|
|
|
} while( iNextBlock );
|
|
|
|
return 0;
|
|
}
|
|
|
|
// --- ProDOS Volume Functions ---
|
|
|
|
// ------------------------------------------------------------------------
|
|
void ProDOS_GetVolumeHeader( uint8_t *pDiskBytes, ProDOS_VolumeHeader_t *pVolumeHeader_, int iBlock )
|
|
{
|
|
int base = iBlock*PRODOS_BLOCK_SIZE + 4; // skip prev/next dir block double linked list
|
|
ProDOS_VolumeHeader_t info;
|
|
|
|
info.kind = (pDiskBytes[ base + 0 ] >> 4) & 0xF;
|
|
info.len = (pDiskBytes[ base + 0 ] >> 0) & 0xF;
|
|
|
|
for( int i = 0; i < PRODOS_MAX_FILENAME+1; i++ )
|
|
info.name[ i ] = 0;
|
|
|
|
for( int i = 0; i < info.len; i++ )
|
|
info.name[ i ] = pDiskBytes[ base+i+ 1 ];
|
|
|
|
for( int i = 0; i < 8; i++ )
|
|
info.pad8[ i ] = pDiskBytes[ base+i+16 ];
|
|
|
|
info.date = ProDOS_Get16( pDiskBytes, base + 24 );
|
|
info.time = ProDOS_Get16( pDiskBytes, base + 26 );
|
|
info.cur_ver = pDiskBytes[ base + 28 ];
|
|
info.min_ver = pDiskBytes[ base + 29 ];
|
|
info.access = pDiskBytes[ base + 30 ];
|
|
info.entry_len = pDiskBytes[ base + 31 ];
|
|
info.entry_num = pDiskBytes[ base + 32 ];
|
|
info.file_count = ProDOS_Get16( pDiskBytes, base + 33 );
|
|
|
|
info.meta.bitmap_block = ProDOS_Get16( pDiskBytes, base + 35 );
|
|
info.meta.total_blocks = ProDOS_Get16( pDiskBytes, base + 37 );
|
|
|
|
#if _DEBUG
|
|
LogOutput( "PRODOS: VOLUME: name: %s\n", info.name );
|
|
LogOutput( "PRODOS: VOLUME: date: %04X\n", info.date );
|
|
LogOutput( "PRODOS: VOLUME: time: %04X\n", info.time );
|
|
LogOutput( "PRODOS: VOLUME: cVer: %02X\n", info.cur_ver );
|
|
LogOutput( "PRODOS: VOLUME: mVer: %02X\n", info.min_ver );
|
|
LogOutput( "PRODOS: VOLUME:acces: %02X\n", info.access );
|
|
LogOutput( "PRODOS: VOLUME:e.len: %02X\n", info.entry_len );
|
|
LogOutput( "PRODOS: VOLUME:e.num: %02X\n", info.entry_num );
|
|
LogOutput( "PRODOS: VOLUME:files: %04X ", info.file_count );
|
|
LogOutput( "(%d)\n", info.file_count );
|
|
|
|
if (iBlock == PRODOS_ROOT_BLOCK)
|
|
{
|
|
LogOutput( "PRODOS: ROOT: bitmap: %04X\n", info.meta.bitmap_block );
|
|
LogOutput( "PRODOS: ROOT: blocks: %04X\n", info.meta.total_blocks );
|
|
}
|
|
else
|
|
{
|
|
LogOutput( "PRODOS: SUBDIR: parent: %04X\n", info.subdir.parent_block );
|
|
LogOutput( "PRODOS: SUBDIR: pa.len: %02X\n", info.subdir.parent_entry_num );
|
|
LogOutput( "PRODOS: SUBDIR: pa.num: %02X\n", info.subdir.parent_entry_len );
|
|
}
|
|
#endif
|
|
|
|
if ( pVolumeHeader_ )
|
|
*pVolumeHeader_ = info;
|
|
};
|
|
|
|
// ------------------------------------------------------------------------
|
|
void ProDOS_SetVolumeHeader ( uint8_t *pDiskBytes, ProDOS_VolumeHeader_t *pVolume, int iBlock )
|
|
{
|
|
if( !pVolume )
|
|
return;
|
|
|
|
int base = iBlock*PRODOS_BLOCK_SIZE + 4; // skip prev/next dir block double linked list
|
|
|
|
uint8_t KindLen = 0
|
|
| ((pVolume->kind & 0xF) << 4)
|
|
| ((pVolume->len & 0xF) << 0)
|
|
;
|
|
|
|
pDiskBytes[ base + 0 ] = KindLen;
|
|
|
|
for( int i = 0; i < pVolume->len; i++ )
|
|
pDiskBytes[ base+ 1+i ] = pVolume->name[ i ];
|
|
|
|
for( int i = 0; i < 8; i++ )
|
|
pDiskBytes[ base+16+i ] = pVolume->pad8[ i ];
|
|
|
|
ProDOS_Put16( pDiskBytes, base + 24 , pVolume->date );
|
|
ProDOS_Put16( pDiskBytes, base + 26 , pVolume->time );
|
|
pDiskBytes[ base + 28 ] = pVolume->cur_ver ;
|
|
pDiskBytes[ base + 29 ] = pVolume->min_ver ;
|
|
pDiskBytes[ base + 30 ] = pVolume->access ;
|
|
pDiskBytes[ base + 31 ] = pVolume->entry_len;
|
|
pDiskBytes[ base + 32 ] = pVolume->entry_num;
|
|
ProDOS_Put16( pDiskBytes, base + 33 , pVolume->file_count );
|
|
ProDOS_Put16( pDiskBytes, base + 35 , pVolume->meta.bitmap_block );
|
|
ProDOS_Put16( pDiskBytes, base + 37 , pVolume->meta.total_blocks );
|
|
}
|
|
|
|
// --- ProDOS File Functions ---
|
|
|
|
// ------------------------------------------------------------
|
|
void ProDOS_GetFileHeader ( uint8_t *pDiskBytes, int nOffset, ProDOS_FileHeader_t *pFileHeader_ )
|
|
{
|
|
ProDOS_FileHeader_t info;
|
|
|
|
info.kind = (pDiskBytes[ nOffset + 0 ] >> 4) & 0xF;
|
|
info.len = (pDiskBytes[ nOffset + 0 ] >> 0) & 0xF;
|
|
|
|
for( int i = 0; i < PRODOS_MAX_FILENAME+1; i++ )
|
|
info.name[ i ] = 0;
|
|
|
|
for( int i = 0; i < info.len; i++ )
|
|
info.name[ i ] = pDiskBytes[ nOffset + 1+i];
|
|
|
|
info.type = pDiskBytes[ nOffset + 16 ];
|
|
info.inode = ProDOS_Get16( pDiskBytes, nOffset + 17 );
|
|
info.blocks = ProDOS_Get16( pDiskBytes, nOffset + 19 );
|
|
info.size = ProDOS_Get24( pDiskBytes, nOffset + 21 );
|
|
info.date = ProDOS_Get16( pDiskBytes, nOffset + 24 );
|
|
info.time = ProDOS_Get16( pDiskBytes, nOffset + 26 );
|
|
info.cur_ver = pDiskBytes[ nOffset + 28 ];
|
|
info.min_ver = pDiskBytes[ nOffset + 29 ];
|
|
info.access = pDiskBytes[ nOffset + 30 ];
|
|
info.aux = ProDOS_Get16( pDiskBytes, nOffset + 31 );
|
|
info.mod_date = ProDOS_Get16( pDiskBytes, nOffset + 33 );
|
|
info.mod_time = ProDOS_Get16( pDiskBytes, nOffset + 35 );
|
|
info.dir_block = ProDOS_Get16( pDiskBytes, nOffset + 37 );
|
|
|
|
if ( pFileHeader_ )
|
|
*pFileHeader_ = info;
|
|
}
|
|
|
|
// ------------------------------------------------------------------------
|
|
void ProDOS_PutFileHeader ( uint8_t *pDiskBytes, int nOffset, ProDOS_FileHeader_t *pMeta )
|
|
{
|
|
int base = nOffset;
|
|
|
|
uint8_t KindLen = 0
|
|
| ((pMeta->kind & 0xF) << 4)
|
|
| ((pMeta->len & 0xF) << 0)
|
|
;
|
|
|
|
pDiskBytes [ base + 0 ] = KindLen;
|
|
|
|
for( int i = 0; i < pMeta->len; i++ )
|
|
pDiskBytes [ base + 1+i] = pMeta->name[ i ];
|
|
|
|
pDiskBytes[ base + 16 ] = pMeta->type;
|
|
ProDOS_Put16( pDiskBytes, base + 17 , pMeta->inode );
|
|
ProDOS_Put16( pDiskBytes, base + 19 , pMeta->blocks );
|
|
ProDOS_Put24( pDiskBytes, base + 21 , pMeta->size );
|
|
ProDOS_Put16( pDiskBytes, base + 24 , pMeta->date );
|
|
ProDOS_Put16( pDiskBytes, base + 26 , pMeta->time );
|
|
pDiskBytes[ base + 28 ] = pMeta->cur_ver ;
|
|
pDiskBytes[ base + 29 ] = pMeta->min_ver ;
|
|
pDiskBytes[ base + 30 ] = pMeta->access ;
|
|
ProDOS_Put16( pDiskBytes, base + 31 , pMeta->aux );
|
|
ProDOS_Put16( pDiskBytes, base + 33 , pMeta->mod_date );
|
|
ProDOS_Put16( pDiskBytes, base + 35 , pMeta->mod_time );
|
|
ProDOS_Put16( pDiskBytes, base + 37 , pMeta->dir_block );
|
|
}
|
|
|
|
// ------------------------------------------------------------------------
|
|
size_t ProDOS_String_CopyUpper( char *pDst, const char *pSrc, int nLen = 0 )
|
|
{
|
|
char *pBeg = pDst;
|
|
if( !nLen )
|
|
nLen = strlen( pSrc );
|
|
|
|
for( int i = 0; i < nLen; i++ )
|
|
{
|
|
char c = *pSrc++;
|
|
|
|
if( c == 0 ) // Don't copy past end-of-string
|
|
break;
|
|
|
|
if((c >= 'a') && (c <= 'z' ))
|
|
c -= ('a' - 'A');
|
|
|
|
*pDst++ = c;
|
|
}
|
|
|
|
*pDst = 0;
|
|
return (pDst - pBeg);
|
|
}
|