/*------------------------------------------------------------------------------ -----------------
Copyright J.Hubert 2015
This file is part of demOS
demOS is free software: you can redistribute it and/or modify it under the terms of
the GNU Lesser General Public License as published by the Free Software Foundation,
either version 3 of the License, or (at your option) any later version.
demOS 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 Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License along with demOS.
If not, see .
------------------------------------------------------------------------------------------------- */
#include "TOOLS\IMAGER\IMAGER.H"
#include "DEMOSDK\LOAD.H"
#include
#include
struct FileEntry
{
u32 nbsectors;
u32 startsector;
u32 side;
u32 track;
s32 metadataIndex;
u32 padding;
std::string name;
};
struct FileMetaData
{
u32 offset;
u32 size;
s32 originalsize;
std::string name;
std::string filename;
};
class OutputFile
{
protected:
FILE* m_file;
std::string m_fileid;
std::string m_filename;
OutputFile(const char* _filename, const char* _ext, const char* _fileid)
: m_fileid (_fileid)
, m_filename (_filename)
{
char filename[256];
sprintf(filename, "%s.%s", _filename, _ext);
m_file = fopen (filename, "w");
}
~OutputFile()
{
if (m_file != NULL)
{
fclose(m_file);
m_file = NULL;
}
}
public:
bool isValid() const { return m_file != NULL; }
};
class HeaderFile : public OutputFile
{
public:
HeaderFile (const char* _filename, const char* _fileid)
: OutputFile(_filename, "H", _fileid)
{}
void write(std::vector& _fileEntries, std::vector& _metaData)
{
assert (m_file != NULL);
const char* fileid = m_fileid.c_str();
fprintf (m_file, "#ifndef %s_H\n#define %s_H\n\n#include \"DEMOSDK\\LOAD.H\"\n\n", fileid, fileid);
fprintf (m_file, "ENUM(RSC_%s_ENTRIES)\n{\n", m_fileid.c_str());
for (u32 t = 0 ; t < _fileEntries.size() ; t++)
{
fprintf (m_file, " RSC_%s_%s, /* %d bytes */\n",
m_fileid.c_str(),
_fileEntries[t].name.c_str(),
_fileEntries[t].nbsectors * 512);
}
fprintf (m_file, " RSC_%s_NBENTRIES\n};\n\n", fileid);
// Files metadata list
fprintf (m_file, "ENUM(RSC_%s_METADATA)\n{\n", fileid);
for (std::vector::const_iterator metaData = _metaData.begin() ; metaData != _metaData.end() ; metaData++)
{
fprintf (m_file, " RSC_%s_METADATA_%s", fileid, metaData->name.c_str());
fprintf (m_file, ",");
fprintf (m_file, " /* @%ld %ld bytes */\n", metaData->offset, metaData->size);
}
fprintf (m_file, " RSC_%s_NBMETADATA\n", fileid);
fprintf (m_file, "};\n\n");
fprintf (m_file, "#ifndef %s_C\n", fileid);
fprintf (m_file, "extern LOADdisk RSC_%s;\n", fileid);
fprintf (m_file, "#endif\n\n");
fprintf (m_file, "#endif\n");
}
};
class BodyFile : public OutputFile
{
public:
BodyFile (const char* _filename, const char* _fileid)
: OutputFile(_filename, "C", _fileid)
{}
void write(std::vector& _fileEntries, std::vector& _metaData, s32 _delta, u16 _preferedUnit)
{
assert (m_file != NULL);
const char* fileid = m_fileid.c_str();
fprintf (m_file, "#define %s_C\n\n", fileid);
fprintf (m_file, "#include \"%s.H\"\n\n", m_filename.c_str());
fprintf (m_file, "static LOADresource RSC_%s_FAT[] =\n", fileid);
fprintf (m_file, "{ /* nbSectors @sector @side @track metaDataIndex */\n");
for (std::vector::const_iterator fileEntry = _fileEntries.begin() ; fileEntry != _fileEntries.end() ; fileEntry++)
{
fprintf (m_file, " { %8ld, %8ld, %8ld, %8ld, %6ld } %c /* lost bytes = %d */\n",
fileEntry->nbsectors,
fileEntry->startsector,
fileEntry->side,
fileEntry->track,
fileEntry->metadataIndex,
((fileEntry + 1) != _fileEntries.end()) ? ',' : ' ',
fileEntry->padding
);
}
fprintf (m_file, "};\n\n");
fprintf (m_file, "static LOADmetaData RSC_%s_MetaData[] =\n", fileid);
fprintf (m_file, "{ /* offset size originalsize */\n");
for (std::vector::const_iterator metaData = _metaData.begin() ; metaData != _metaData.end() ; metaData++)
{
fprintf (m_file, " { %8ldUL, %8ldUL, %8ldL } %c\n",
metaData->offset,
metaData->size,
metaData->originalsize,
((metaData + 1) != _metaData.end()) ? ',' : ' ');
}
fprintf (m_file, "};\n\n");
fprintf (m_file, "LOADdisk RSC_%s = { RSC_%s_FAT, RSC_%s_NBENTRIES, RSC_%s_MetaData, RSC_%s_NBMETADATA, %d\n", fileid, fileid, fileid, fileid, fileid, _preferedUnit);
fprintf (m_file, "# ifdef DEMOS_DEBUG\n ,\"%s.ST\", NULL\n# endif\n};\n", m_filename.c_str());
fprintf (m_file, "\n/* %d bytes left on floppy */\n", _delta);
}
};
static u32 STDswap32(u32 _v)
{
u32 v;
u8* s = (u8*) &_v;
u8* d = (u8*) &v;
d[0] = s[3];
d[1] = s[2];
d[2] = s[1];
d[3] = s[0];
return v;
}
static u16 STDswap16(u16 _v)
{
u16 v;
u8* s = (u8*) &_v;
u8* d = (u8*) &v;
d[0] = s[1];
d[1] = s[0];
return v;
}
static void makeId (char* _source, char* _dest)
{
while (*_source)
{
if ((((*_source) >= 'A') && ((*_source) <= 'Z')) ||
(((*_source) >= 'a') && ((*_source) <= 'z')) ||
(((*_source) >= '0') && ((*_source) <= '9')))
{
*_dest = *_source;
}
else
{
*_dest = '_';
}
_source++;
_dest++;
}
(*_dest) = 0;
}
struct FileType
{
enum Enum
{
NORMAL,
ARJ,
BOOTSECTOR
};
};
static void printPrgInfo (const char* _subdir, const char* _name)
{
char temp[256];
sprintf(temp, "%s%s%s%s", "DATABIN\\", _subdir, _name, ".PRG");
FILE* file = fopen (temp, "rb");
if ( file != NULL )
{
u32 value = 0;
u32 total = 0;
fseek(file, 2, SEEK_SET);
printf ("Program file detected : %s\n", temp);
fread (&value, sizeof(value), 1, file);
value = PCENDIANSWAP32(value);
printf ("text size: %d\n", value);
total += value;
fread (&value, sizeof(value), 1, file);
value = PCENDIANSWAP32(value);
printf ("data size: %d\n", value);
total += value;
fread (&value, sizeof(value), 1, file);
value = PCENDIANSWAP32(value);
printf ("bss size: %d\n", value);
total += value;
fread (&value, sizeof(value), 1, file);
value = PCENDIANSWAP32(value);
printf ("symbols size: %d\n", value);
total += value;
printf ("total size: %d bytes\n", total);
fclose(file);
}
}
static void* loadDataFile( const char* _currentName, u32& _filesize, s32& _originalsize, bool _executableBootSector)
{
char filename[256];
u8* buffer = NULL;
sprintf (filename, "DATABIN\\%s", _currentName);
{
FILE* data = fopen (filename, "rb");
if ( data == NULL )
{
printf ("ERROR: can not open file %s\n", filename);
goto Error;
}
fseek (data, 0, SEEK_END);
_filesize = ftell(data);
fseek (data, 0, SEEK_SET);
buffer = (u8*) malloc (_filesize);
assert(buffer != NULL);
u32 result = fread (buffer, 1, _filesize, data);
assert(result == _filesize);
fclose (data);
}
{
char ext[256];
char subdir[256];
char name[256];
_splitpath (_currentName, NULL, subdir, name, ext);
printPrgInfo (subdir, name);
FileType::Enum fileType = FileType::NORMAL;
if ( _strcmpi(ext, ".ARJ") == 0 )
fileType = FileType::ARJ;
else if ( _strcmpi(ext, ".BOT") == 0 )
fileType = FileType::BOOTSECTOR;
if (fileType == FileType::ARJ)
{
printf ("ARJ file detected\n");
if (( buffer[0] != 0x60 ) || ( buffer[1] != 0xEA ))
{
printf ("Error: '%s' is not an ARJ file\n", filename);
goto Error;
}
else
{
u8* p = &buffer[2];
u16 headerSize = *(u16*)p;
p += sizeof(headerSize);
p += headerSize;
p += 4; // skip crc
u16 exHeaderSize = *(u16*)p;
p += sizeof(exHeaderSize);
if (exHeaderSize > 0)
{
p += exHeaderSize;
p += 4; // skip crc
}
if (( p[0] != 0x60 ) || ( p[1] != 0xEA ))
{
printf ("Error: '%s' is not an ARJ file\n", filename);
goto Error;
}
p += 2;
if ( p[7] != 4 )
{
printf ("demOS expect to have ARJ file compressed in mode 4 (current is %d)\n", p[7]);
}
u32 compressedSize = *(u32*)&p[14];
_originalsize = *(u32*)&p[18];
printf ("sizes of ARJ : compressed=%d - uncompressed=%d (ratio=%d%%)\n", compressedSize, _originalsize, compressedSize * 100 / _originalsize);
u16 localHeaderSize = *(u16*)p;
p += sizeof(localHeaderSize);
if (localHeaderSize > 0)
{
p += localHeaderSize;
p += 4; // skip crc
}
u16 exLocalHeaderSize = *(u16*)p;
p += sizeof(exLocalHeaderSize);
if (exLocalHeaderSize > 0)
{
p += localHeaderSize;
p += 4; // skip crc
}
u32 shrinkSize = p - buffer;
shrinkSize -= 4; // leave place to store size
printf ("image strip headers for %d bytes\n", shrinkSize);
_filesize -= shrinkSize;
memmove (&buffer[4], p, _filesize);
*(u32*)buffer = STDswap32(_originalsize);
{
sprintf (filename, "DATABIN\\%sX", _currentName);
FILE* data = fopen (filename, "wb");
if ( data == NULL )
{
printf ("ERROR: can not open file %s\n", filename);
goto Error;
}
u32 result = fwrite (buffer, 1, _filesize, data);
assert(result == _filesize);
fclose (data);
}
}
}
else if (fileType == FileType::BOOTSECTOR)
{
_filesize -= 28;
memmove (buffer, buffer + 28, _filesize);
_filesize -= 2;
if (_filesize > 512)
{
printf ("bootsector size exceeds 512 bytes\n");
goto Error;
}
if ( _executableBootSector )
{
u16 checksum = 0;
for (u32 t = 0 ; t < (_filesize - 2) ; t += 2)
{
checksum += STDswap16( *(u16*)&buffer[t] );
}
*(u16*)&buffer[_filesize - 2] = STDswap16(0x1234 - checksum);
}
}
}
return buffer;
Error:
if ( buffer != NULL )
{
free (buffer);
}
return NULL;
}
bool writeST (char* _filename, u16 _nbSectorsPerTrack, u16 _nbsides, u16 _nbtracks, char** _filesList, u16 _preferedUnit, bool _executableBootSector)
{
bool success = false;
char filename[256];
char fileid [256];
u16 sides = _nbsides - 1;
u16 tracks = _nbtracks - 1;
sprintf(filename, "%s.ST", _filename);
FILE* fileImage = fopen (filename, "wb");
makeId (_filename, fileid);
HeaderFile headerFile (_filename, fileid);
BodyFile bodyFile (_filename, fileid);
if ( ( fileImage != NULL ) && headerFile.isValid() && bodyFile.isValid() )
{
char temp [4096];
s32 currentsector = 0;
std::vector fileEntries;
std::vector fileMetaData;
while (*_filesList)
{
FileEntry fileEntry;
char* s1 = *_filesList++;
char* s2 = *_filesList++;
char* filesToProcess;
char* p;
bool singleFile;
u32 size = 0;
u16 metadataIndexBak = fileMetaData.size();
makeId (s1, filename);
fileEntry.name = filename;
singleFile = s2 == NULL;
filesToProcess = singleFile ? s1 : s2;
strcpy (temp, filesToProcess);
p = strtok (temp, ";");
while (p != NULL)
{
u32 fsize = 0;
s32 originalsize = LOAD_NOTPACKED;
{
void* buffer = loadDataFile(p, fsize, originalsize, _executableBootSector );
if (buffer == NULL)
{
return false;
}
printf ("write %s at %d\n", filename, ftell(fileImage));
u32 result = fwrite (buffer, 1, fsize, fileImage);
assert(result == fsize);
free(buffer);
}
sprintf (filename, "%s_%s", fileid, p);
makeId (p, filename);
{
FileMetaData metaData;
metaData.name = filename;
metaData.filename = p;
metaData.offset = size;
metaData.size = fsize;
metaData.originalsize = originalsize;
if (originalsize != LOAD_NOTPACKED )
{
metaData.filename += 'X';
metaData.name += 'X';
if (size == 0)
{
fileEntry.name += 'X';
}
}
fileMetaData.push_back(metaData);
}
size += fsize;
p = strtok (NULL, ";");
}
u32 padding = (512 - (size & 0x1FF)) & 511;
if ( padding > 0 )
{
memset(temp, 0, padding);
fwrite (temp, 1, padding, fileImage);
}
u32 sector = currentsector % (_nbSectorsPerTrack * _nbsides);
fileEntry.nbsectors = (size + 511) / 512;
fileEntry.track = currentsector / (_nbSectorsPerTrack * _nbsides);
fileEntry.side = sector >= _nbSectorsPerTrack;
fileEntry.startsector = sector % _nbSectorsPerTrack;
fileEntry.padding = padding;
fileEntry.metadataIndex = metadataIndexBak;
fileEntries.push_back(fileEntry);
currentsector += fileEntry.nbsectors;
if ( currentsector > (_nbSectorsPerTrack * _nbtracks * _nbsides ))
{
printf ("ERROR: size exceeds support with file %s\n", *(_filesList - 2));
goto end;
}
}
u32 imagesize = ((u32)_nbSectorsPerTrack * (u32)_nbsides * (u32)_nbtracks) * 512;
u32 pos = ftell(fileImage);
if (pos <= imagesize)
{
u32 delta = imagesize - pos;
void* buf = malloc (delta);
assert (buf != NULL);
memset (buf, 0, delta);
fwrite (buf, delta, 1, fileImage);
free(buf);
headerFile.write(fileEntries, fileMetaData);
bodyFile.write(fileEntries, fileMetaData, delta, _preferedUnit);
success = true;
}
}
end:
if ( fileImage != NULL )
{
fclose (fileImage);
}
return success;
}