Files
VirtualC64-Core/C64/NIBArchive.cpp
T
duckey77 033e23c72e Update to 1.4.2
Update VirtualC64 to 1.4.2
created autoload& run functionality
first attempt at working save states
2016-01-27 15:43:47 -07:00

370 lines
9.8 KiB
C++
Executable File

/*
* (C) 2015 Dirk W. Hoffmann. All rights reserved.
*
* This program 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, org
* (at your option) any later version.
*
* This program 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 this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "NIBArchive.h"
NIBArchive::NIBArchive()
{
setDescription("NIBArchive");
data = NULL;
dealloc();
for (unsigned i = 0; i < 85; i++) {
length[i] = 0;
memset(halftrack[i], 0, sizeof(halftrack[i]));
}
selectedtrack = 0;
fp = -1;
}
NIBArchive::~NIBArchive()
{
dealloc();
}
bool
NIBArchive::isNIBFile(const char *filename)
{
/* "MNIB-1541-RAW" */
int magic_bytes[] = { 0x4d, 0x4e, 0x49, 0x42, 0x2d, 0x31, 0x35, 0x34, 0x31, 0x2d, 0x52, 0x41, 0x57, EOF };
assert(filename != NULL);
if (!checkFileSuffix(filename, ".NIB") && !checkFileSuffix(filename, ".nib"))
return false;
// File size = 0x100 (header) + no_of_tracks * 0x2000
if (getSizeOfFile(filename) % 0x2000 != 0x100)
return false;
if (!checkFileHeader(filename, magic_bytes))
return false;
return true;
}
NIBArchive *
NIBArchive::archiveFromNIBFile(const char *filename)
{
NIBArchive *archive = new NIBArchive();
if (!archive->readFromFile(filename)) {
delete archive;
return NULL;
}
if (!archive->scan()) {
delete archive;
return NULL;
}
archive->debug(1, "NIB archive created from file %s.\n", filename);
return archive;
}
bool
NIBArchive::scan()
{
uint8_t bits[8 * 0x2000];
int start, end, gap;
// Iterate through all header entries
unsigned i, item;
for (i = 0x10, item = 0; i < 0x100; i += 2, item++) {
// Does item no 'item' exist in NIB file?
if (data[i] < 2 || data[i] > 83)
continue;
unsigned ht = data[i] + 1;
// Convert byte stream into a bit stream
unsigned j, startOfTrack = 0x100 + item * 0x2000;
for (j = 0; j < sizeof(bits); j++) {
bits[j] = (data[startOfTrack + j/8] << (j%8)) & 0x80 ? 1 : 0;
}
// Determine track bounds and alignment offset
if (!scanTrack(ht, bits, &start, &end, &gap))
continue;
// Copy track data into destination buffer
printf("Halftrack: %d Start: %d End: %d Length: %x Gap: %x\n",
ht, start, end, (end - start) / 8, gap / 8);
length[ht] = end - start;
size_t len1 = length[ht] - gap;
size_t len2 = gap;
memcpy(halftrack[ht], bits + start + gap, len1);
memcpy(halftrack[ht] + len1, bits + start, len2);
}
for (unsigned ht = 1; ht <= 84; ht++) {
printf("Halftrack %02d: ", ht);
for (unsigned b = 0; b < 64; b++) printf("%d", halftrack[ht][b]);
printf(" Length: %d\n", length[ht]);
}
return true;
}
bool
NIBArchive::scanTrack(unsigned ht, uint8_t *bits, int *start, int *end, int *gap)
{
// Find loop
if (!scanForLoop(bits, sizeof(bits), start, end)) {
printf("Halftrack: %d LOOP DETECTION FAILED.\n", ht);
return false;
}
// Find gap (for track alignment)
return scanForGap(bits + *start, *end - *start, gap);
}
bool
NIBArchive::scanForLoop(uint8_t *bits, int length, int *start, int *end)
{
uint8_t stripped[8 * 0x2000]; // Bit stream with shortened SYNC sequences
int index[8 * 0x2000]; // Index mapping from stripped bits to unstripped bits
int length_stripped; // Number of bits in shortened sequence
// Beware that the length of the SYNC sequences may differ in the repeated bit sequence.
// Therefore, we perform the matching operation with a copy of the original bit stream.
// The copy is a stripped version where all SYNC sequences are of the same size.
//
// The code below creates the following data structures:
//
// i: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
// bits[i]: 0 0 1 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 1 0 1 1
// stripped[i]: 0 0 1 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 1 0 1 1 ? ?
// index: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 20 21 22 23 24 25 ?? ??
// length: 24
int idx, onecnt; uint8_t bit;
for (length_stripped = idx = onecnt = 0; idx < 8 * 0x2000; idx++) {
// Read bit from raw data stream and count consecutive '1's
bit = bits[idx];
onecnt = bit ? onecnt + 1 : 0;
if (onecnt <= 10) {
stripped[length_stripped] = bit;
index[length_stripped] = idx;
length_stripped++;
}
}
// Now we are ready to search for the loop
int pos1, pos2, tracklength;
for (pos1 = 0, pos2 = 1; pos2 < length_stripped - 1024 /* minimum matching size */; pos2++) {
if (memcmp(stripped+pos1, stripped+pos2, length_stripped-pos2) == 0) {
*start = index[pos1];
*end = index[pos2];
tracklength = *end - *start;
/*
printf("Match found at (%d,%d)\n", pos1, pos2);
for (unsigned k = 0; k < 30; k++)
printf("%d", bits[pos1+k]);
printf("\n");
for (unsigned k = 0; k < 30; k++)
printf("%d", bits[pos2+k]);
printf("\n");
*/
// Check loop bounds
if (tracklength < 8 * MIN_TRACK_LENGTH) {
printf("Warning: Track is too short (%d bits). Discarding.\n", tracklength);
return false;
}
if (tracklength > 8 * MAX_TRACK_LENGTH) {
printf("Halftrack: Track is too long (%d bits). Discarding.\n", tracklength);
return false;
}
return true;
}
}
return false;
}
bool
NIBArchive::scanForGap(uint8_t *bits, int length, int *gap)
{
uint8_t tmpbuf[2 * 8 * 0x2000];
int nonsync[2 * 8 * 0x2000];
int i, onecnt, gapsize;
// Setup double buffer
assert(2 * length <= sizeof(tmpbuf));
memcpy(tmpbuf, bits, length);
memcpy(tmpbuf + length, bits, length);
// Count number of bits after SYNC sequences
// i: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
// tmpbuf[i]: 0 0 1 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 1 0 1 1
// nonsync[i]: 77 78 79 80 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 2 3 4 5 6
for (nonsync[0] = onecnt = 0, i = 1; i < 2 * length; i++) {
onecnt = tmpbuf[i] ? onecnt + 1 : 0;
if (onecnt >= 10) {
for (unsigned j = 0; j < 10; j++) nonsync[i - j] = 0;
} else {
nonsync[i] = nonsync[i - 1] + 1;
}
}
/*
for (unsigned k = 135; k < 190; k++) {
printf("%2d ", bits[k]);
}
printf("\n");
for (unsigned k = 135; k < 190; k++) {
printf("%2d ", nonsync[k] % 99);
}
printf("\n");
*/
// Search for biggest gap
for (i = gapsize = 0; i < 2 * length; i++) {
if (gapsize < nonsync[i]) {
*gap = (i % length) + 1;
gapsize = nonsync[i];
}
}
return true;
}
void NIBArchive::dealloc()
{
if (data) free(data);
data = NULL;
size = 0;
fp = -1;
}
bool
NIBArchive::fileIsValid(const char *filename)
{
return NIBArchive::isNIBFile(filename);
}
bool
NIBArchive::readFromBuffer(const uint8_t *buffer, unsigned length)
{
if ((data = (uint8_t *)malloc(length)) == NULL)
return false;
memcpy(data, buffer, length);
size = length;
return true;
}
unsigned
NIBArchive::writeToBuffer(uint8_t *buffer)
{
assert(data != NULL);
if (buffer) {
memcpy(buffer, data, size);
}
return size;
}
const char *
NIBArchive::getName()
{
return "NIB archive";
}
int
NIBArchive::getNumberOfItems()
{
return 84;
}
#if 0
int
NIBArchive::getStartOfItem(int n)
{
if (n < 0 || n >= 84)
return -1;
return halftrack[n + 1];
}
#endif
int
NIBArchive::getSizeOfItem(int n)
{
if (n < 0 || n >= 84)
return 0;
return length[n + 1];
}
const char *
NIBArchive::getNameOfItem(int n)
{
if (n < 0 || n >= 84)
return "";
if (n % 2 == 0) {
sprintf(name, "Track %d", (n / 2) + 1);
} else {
sprintf(name, "Track %d.5", (n / 2) + 1);
}
return name;
}
const char *
NIBArchive::getTypeOfItem(int n)
{
return ""; // (n % 2 == 0) ? "Full" : "Half";
}
void
NIBArchive::selectItem(int n)
{
if (n < 0 || n >= 84)
return;
selectedtrack = n + 1;
fp = 0;
}
int
NIBArchive::getByte()
{
if (fp < 0)
return -1;
int result = (halftrack[selectedtrack][fp++] << 7);
if (fp < length[selectedtrack]) result |= (halftrack[selectedtrack][fp++] << 6);
if (fp < length[selectedtrack]) result |= (halftrack[selectedtrack][fp++] << 5);
if (fp < length[selectedtrack]) result |= (halftrack[selectedtrack][fp++] << 4);
if (fp < length[selectedtrack]) result |= (halftrack[selectedtrack][fp++] << 3);
if (fp < length[selectedtrack]) result |= (halftrack[selectedtrack][fp++] << 2);
if (fp < length[selectedtrack]) result |= (halftrack[selectedtrack][fp++] << 1);
if (fp < length[selectedtrack]) result |= (halftrack[selectedtrack][fp++] << 0); else fp = -1;
return result;
}