mirror of
https://github.com/fadden/ciderpress.git
synced 2024-11-26 17:49:21 +00:00
279 lines
6.7 KiB
C++
279 lines
6.7 KiB
C++
/*
|
|
* CiderPress
|
|
* Copyright (C) 2007 by faddenSoft, LLC. All Rights Reserved.
|
|
* See the file LICENSE for distribution terms.
|
|
*/
|
|
/*
|
|
* Support for the VolumeUsage sub-class in DiskFS.
|
|
*/
|
|
#include "StdAfx.h"
|
|
#include "DiskImgPriv.h"
|
|
|
|
|
|
/*
|
|
* Initialize structures for a block-structured disk.
|
|
*/
|
|
DIError
|
|
DiskFS::VolumeUsage::Create(long numBlocks)
|
|
{
|
|
if (numBlocks <= 0 || numBlocks > 32*1024*1024) // 16GB
|
|
return kDIErrInvalidArg;
|
|
|
|
fByBlocks = true;
|
|
fNumSectors = -1;
|
|
fTotalChunks = numBlocks;
|
|
fListSize = numBlocks;
|
|
fList = new unsigned char[fListSize];
|
|
if (fList == nil)
|
|
return kDIErrMalloc;
|
|
|
|
memset(fList, 0, fListSize);
|
|
|
|
return kDIErrNone;
|
|
}
|
|
|
|
/*
|
|
* Initialize structures for a track/sector-structured disk.
|
|
*/
|
|
DIError
|
|
DiskFS::VolumeUsage::Create(long numTracks, long numSectors)
|
|
{
|
|
long count = numTracks * numSectors;
|
|
if (numTracks <= 0 || count <= 0 || count > 32*1024*1024)
|
|
return kDIErrInvalidArg;
|
|
|
|
fByBlocks = false;
|
|
fNumSectors = numSectors;
|
|
fTotalChunks = count;
|
|
fListSize = count;
|
|
fList = new unsigned char[fListSize];
|
|
if (fList == nil)
|
|
return kDIErrMalloc;
|
|
|
|
memset(fList, 0, fListSize);
|
|
|
|
return kDIErrNone;
|
|
}
|
|
|
|
/*
|
|
* Return the state of a particular chunk.
|
|
*/
|
|
DIError
|
|
DiskFS::VolumeUsage::GetChunkState(long block, ChunkState* pState) const
|
|
{
|
|
if (!fByBlocks)
|
|
return kDIErrInvalidArg;
|
|
return GetChunkStateIdx(block, pState);
|
|
}
|
|
DIError
|
|
DiskFS::VolumeUsage::GetChunkState(long track, long sector,
|
|
ChunkState* pState) const
|
|
{
|
|
if (fByBlocks)
|
|
return kDIErrInvalidArg;
|
|
if (track < 0 || sector < 0 || sector >= fNumSectors)
|
|
return kDIErrInvalidArg;
|
|
return GetChunkStateIdx(track * fNumSectors + sector, pState);
|
|
}
|
|
DIError
|
|
DiskFS::VolumeUsage::GetChunkStateIdx(int idx, ChunkState* pState) const
|
|
{
|
|
if (fList == nil || idx < 0 || idx >= fListSize) {
|
|
assert(false);
|
|
return kDIErrInvalidArg;
|
|
}
|
|
|
|
unsigned char val = fList[idx];
|
|
pState->isUsed = (val & kChunkUsedFlag) != 0;
|
|
pState->isMarkedUsed = (val & kChunkMarkedUsedFlag) != 0;
|
|
pState->purpose = (ChunkPurpose)(val & kChunkPurposeMask);
|
|
|
|
return kDIErrNone;
|
|
}
|
|
|
|
/*
|
|
* Set the state of a particular chunk.
|
|
*/
|
|
DIError
|
|
DiskFS::VolumeUsage::SetChunkState(long block, const ChunkState* pState)
|
|
{
|
|
if (!fByBlocks)
|
|
return kDIErrInvalidArg;
|
|
return SetChunkStateIdx(block, pState);
|
|
}
|
|
DIError
|
|
DiskFS::VolumeUsage::SetChunkState(long track, long sector,
|
|
const ChunkState* pState)
|
|
{
|
|
if (fByBlocks)
|
|
return kDIErrInvalidArg;
|
|
if (track < 0 || sector < 0 || sector >= fNumSectors)
|
|
return kDIErrInvalidArg;
|
|
return SetChunkStateIdx(track * fNumSectors + sector, pState);
|
|
}
|
|
DIError
|
|
DiskFS::VolumeUsage::SetChunkStateIdx(int idx, const ChunkState* pState)
|
|
{
|
|
if (fList == nil || idx < 0 || idx >= fListSize) {
|
|
assert(false);
|
|
return kDIErrInvalidArg;
|
|
}
|
|
|
|
unsigned char val = 0;
|
|
if (pState->isUsed) {
|
|
if ((pState->purpose & ~kChunkPurposeMask) != 0) {
|
|
assert(false);
|
|
return kDIErrInvalidArg;
|
|
}
|
|
val |= kChunkUsedFlag;
|
|
val |= (int)pState->purpose;
|
|
}
|
|
if (pState->isMarkedUsed)
|
|
val |= kChunkMarkedUsedFlag;
|
|
|
|
fList[idx] = val;
|
|
|
|
return kDIErrNone;
|
|
}
|
|
|
|
|
|
/*
|
|
* Count up the #of free chunks.
|
|
*/
|
|
long
|
|
DiskFS::VolumeUsage::GetActualFreeChunks(void) const
|
|
{
|
|
ChunkState cstate; // could probably do this bitwise...
|
|
int freeCount = 0;
|
|
int funkyCount = 0;
|
|
|
|
for (int i = 0; i < fTotalChunks; i++) {
|
|
if (GetChunkStateIdx(i, &cstate) != kDIErrNone) {
|
|
assert(false);
|
|
return -1;
|
|
}
|
|
|
|
if (!cstate.isUsed && !cstate.isMarkedUsed)
|
|
freeCount++;
|
|
|
|
if ((!cstate.isUsed && cstate.isMarkedUsed) ||
|
|
(cstate.isUsed && !cstate.isMarkedUsed) ||
|
|
(cstate.isUsed && cstate.purpose == kChunkPurposeConflict))
|
|
{
|
|
funkyCount++;
|
|
}
|
|
}
|
|
|
|
WMSG3(" VU total=%ld free=%d funky=%d\n",
|
|
fTotalChunks, freeCount, funkyCount);
|
|
|
|
return freeCount;
|
|
}
|
|
|
|
|
|
/*
|
|
* Convert a ChunkState into a single, hopefully meaningful, character.
|
|
*
|
|
* Possible states:
|
|
* '.' - !inuse, !marked (free space)
|
|
* 'X' - !inuse, marked (could be embedded volume)
|
|
* '!' - inuse, !marked (danger!)
|
|
* '#' - inuse, marked, used by more than one thing
|
|
* 'S' - inuse, marked, used by system (directories, volume bit map)
|
|
* 'I' - inuse, marked, used by file structure (index block)
|
|
* 'F' - inuse, marked, used by file
|
|
*/
|
|
inline char
|
|
DiskFS::VolumeUsage::StateToChar(ChunkState* pState) const
|
|
{
|
|
if (!pState->isUsed && !pState->isMarkedUsed)
|
|
return '.';
|
|
if (!pState->isUsed && pState->isMarkedUsed)
|
|
return 'X';
|
|
if (pState->isUsed && !pState->isMarkedUsed)
|
|
return '!';
|
|
assert(pState->isUsed && pState->isMarkedUsed);
|
|
if (pState->purpose == kChunkPurposeUnknown)
|
|
return '?';
|
|
if (pState->purpose == kChunkPurposeConflict)
|
|
return '#';
|
|
if (pState->purpose == kChunkPurposeSystem)
|
|
return 'S';
|
|
if (pState->purpose == kChunkPurposeVolumeDir)
|
|
return 'V';
|
|
if (pState->purpose == kChunkPurposeSubdir)
|
|
return 'D';
|
|
if (pState->purpose == kChunkPurposeUserData)
|
|
return 'F';
|
|
if (pState->purpose == kChunkPurposeFileStruct)
|
|
return 'I';
|
|
if (pState->purpose == kChunkPurposeEmbedded)
|
|
return 'E';
|
|
|
|
assert(false);
|
|
return '?';
|
|
}
|
|
|
|
/*
|
|
* Dump the list.
|
|
*/
|
|
void
|
|
DiskFS::VolumeUsage::Dump(void) const
|
|
{
|
|
#define kMapInit "--------------------------------"
|
|
if (fList == nil) {
|
|
WMSG0(" VU asked to dump empty list?\n");
|
|
return;
|
|
}
|
|
|
|
WMSG1(" VU VolumeUsage dump (%ld free chunks):\n",
|
|
GetActualFreeChunks());
|
|
|
|
if (fByBlocks) {
|
|
ChunkState cstate;
|
|
char freemap[32+1] = kMapInit;
|
|
int block;
|
|
const int kEntriesPerLine = 32; // use 20 to match Copy][+
|
|
|
|
for (block = 0; block < fTotalChunks; block++) {
|
|
if (GetChunkState(block, &cstate) != kDIErrNone) {
|
|
assert(false);
|
|
return;
|
|
}
|
|
|
|
freemap[block % kEntriesPerLine] = StateToChar(&cstate);
|
|
if ((block % kEntriesPerLine) == kEntriesPerLine-1) {
|
|
WMSG2(" 0x%04x: %s\n", block-(kEntriesPerLine-1), freemap);
|
|
}
|
|
}
|
|
if ((block % kEntriesPerLine) != 0) {
|
|
memset(freemap + (block % kEntriesPerLine), '-',
|
|
kEntriesPerLine - (block % kEntriesPerLine));
|
|
WMSG2(" 0x%04x: %s\n", block-(kEntriesPerLine-1), freemap);
|
|
}
|
|
} else {
|
|
ChunkState cstate;
|
|
char freemap[32+1] = kMapInit;
|
|
long numTracks = fTotalChunks / fNumSectors;
|
|
int track, sector;
|
|
|
|
if (fNumSectors > 32) {
|
|
WMSG1(" VU too many sectors (%ld)\n", fNumSectors);
|
|
return;
|
|
}
|
|
|
|
WMSG0(" map 0123456789abcdef\n");
|
|
|
|
for (track = 0; track < numTracks; track++) {
|
|
for (sector = 0; sector < fNumSectors; sector++) {
|
|
if (GetChunkState(track, sector, &cstate) != kDIErrNone) {
|
|
assert(false);
|
|
return;
|
|
}
|
|
freemap[sector] = StateToChar(&cstate);
|
|
}
|
|
WMSG2(" %2d: %s\n", track, freemap);
|
|
}
|
|
}
|
|
}
|