S3M+IT: fix reading, IT: untested support for message writing

IT: reading was off starting with global volume because of wrong read size.
S3M+IT: correctly read the number of used patterns.
IT: fixed reading of message tag and implemented writing of message tag
(not tested yet).

I also added S3M+IT test files. TODO: Unit tests using them.
This commit is contained in:
Mathias Panzenböck 2011-06-23 05:41:23 +02:00
parent e202c658f0
commit 7236ef4d0f
9 changed files with 127 additions and 22 deletions

View File

@ -123,6 +123,56 @@ bool IT::File::save()
writeString(String::null, 26);
}
// write rest as message:
StringList messageLines;
for(uint i = instrumentCount + sampleCount; i < lines.size(); ++ i)
messageLines.append(lines[i]);
ByteVector message = messageLines.toString("\r").data(String::Latin1);
ushort special = 0;
ushort messageLength = 0;
ulong messageOffset = 0;
seek(46);
if(!readU16L(special))
return false;
long fileSize = this->length();
if(special & 0x1)
{
seek(54);
if(!readU16L(messageLength) || !readU32L(messageOffset))
return false;
if(messageLength == 0)
messageOffset = fileSize;
}
else
{
messageOffset = fileSize;
seek(46);
writeU16L(special | 0x1);
}
if((messageOffset + messageLength) >= fileSize)
{
// append new message
seek(54);
writeU16L(message.size());
writeU32L(messageOffset);
seek(messageOffset);
writeBlock(message);
truncate(messageOffset + message.size());
}
else
{
// Only overwrite existing message.
// I'd need to parse (understand!) the whole file for more.
// Although I could just move the message to the end of file
// and let the existing one be, but that would waste space.
message.resize(messageLength, 0);
seek(messageOffset);
writeBlock(message);
}
return true;
}
@ -141,7 +191,6 @@ void IT::File::read(bool)
READ_U16L_AS(instrumentCount);
READ_U16L_AS(sampleCount);
d->properties.setTableLength(length);
d->properties.setInstrumentCount(instrumentCount);
d->properties.setSampleCount(sampleCount);
READ_U16L(d->properties.setPatternCount);
@ -150,31 +199,30 @@ void IT::File::read(bool)
READ_U16L(d->properties.setFlags);
READ_U16L_AS(special);
d->properties.setSpecial(special);
READ_U16L(d->properties.setGlobalVolume);
READ_U16L(d->properties.setMixVolume);
READ_BYTE(d->properties.setGlobalVolume);
READ_BYTE(d->properties.setMixVolume);
READ_BYTE(d->properties.setBpmSpeed);
READ_BYTE(d->properties.setTempo);
READ_BYTE(d->properties.setPanningSeparation);
READ_BYTE(d->properties.setPitchWheelDepth);
/*
* While the message would be a sorta comment tag, I don't
* see any IT files in the wild that use this or set the
* offset/length to a correct value.
*
* In all files I found where the message bit was set the
* offset was either 0 or a ridiculous high value and the
* length wasn't much better.
*
// IT supports some kind of comment tag. Still, the
// sample/instrument names are abused as comments so
// I just add all together.
String message;
if(special & 0x1)
{
READ_U16L_AS(messageLength);
READ_U32L_AS(messageOffset);
seek(messageOffset);
READ_STRING_AS(message, messageLength);
debug("Message: \""+message+"\"");
ByteVector messageBytes = readBlock(messageLength);
READ_ASSERT(messageBytes.size() == messageLength);
int index = messageBytes.find((char) 0);
if(index > -1)
messageBytes.resize(index, 0);
messageBytes.replace('\r', '\n');
message = messageBytes;
}
*/
seek(64);
@ -192,6 +240,16 @@ void IT::File::read(bool)
if(pannings[i] < 128 && volumes[i] > 0) ++ channels;
}
d->properties.setChannels(channels);
// real length might be shorter because of skips and terminator
ushort realLength = 0;
for(ushort i = 0; i < length; ++ i)
{
READ_BYTE_AS(order);
if(order == 255) break;
if(order != 254) ++ realLength;
}
d->properties.setTableLength(realLength);
StringList comment;
// Note: I found files that have nil characters somewhere
@ -249,6 +307,11 @@ void IT::File::read(bool)
comment.append(sampleName);
}
d->tag.setComment(comment.toString("\n"));
if(comment.size() > 0 && message.size() > 0)
d->tag.setComment(comment.toString("\n") + "\n" + message);
else if(comment.size() > 0)
d->tag.setComment(comment.toString("\n"));
else
d->tag.setComment(message);
d->tag.setTrackerName("Impulse Tracker");
}

View File

@ -55,6 +55,32 @@ bool Mod::FileBase::readString(String &s, ulong size)
return true;
}
void Mod::FileBase::writeByte(uchar byte)
{
ByteVector data(1, byte);
writeBlock(data);
}
void Mod::FileBase::writeU16L(ushort number)
{
writeBlock(ByteVector::fromShort(number, false));
}
void Mod::FileBase::writeU32L(ulong number)
{
writeBlock(ByteVector::fromUInt(number, false));
}
void Mod::FileBase::writeU16B(ushort number)
{
writeBlock(ByteVector::fromShort(number, true));
}
void Mod::FileBase::writeU32B(ulong number)
{
writeBlock(ByteVector::fromUInt(number, true));
}
bool Mod::FileBase::readByte(uchar &byte)
{
ByteVector data(readBlock(1));

View File

@ -39,6 +39,12 @@ namespace TagLib {
FileBase(IOStream *stream);
void writeString(const String &s, ulong size, char padding = 0);
void writeByte(uchar byte);
void writeU16L(ushort number);
void writeU32L(ulong number);
void writeU16B(ushort number);
void writeU32B(ulong number);
bool readString(String &s, ulong size);
bool readByte(uchar &byte);
bool readU16L(ushort &number);

View File

@ -143,7 +143,6 @@ void S3M::File::read(bool)
READ_U16L_AS(length);
READ_U16L_AS(sampleCount);
d->properties.setTableLength(length);
d->properties.setSampleCount(sampleCount);
READ_U16L(d->properties.setPatternCount);
@ -177,12 +176,23 @@ void S3M::File::read(bool)
if(setting != 0xff) ++ channels;
}
d->properties.setChannels(channels);
seek(96);
ushort realLength = 0;
for(ushort i = 0; i < length; ++ i)
{
READ_BYTE_AS(order);
if(order == 255) break;
if(order != 254) ++ realLength;
}
d->properties.setTableLength(realLength);
seek(channels, Current);
// Note: The S3M spec mentions instruments, but I could
// not figure out where these can be found. They are
// similar to samples, though (SCRI instead of SCRS).
// Note: The S3M spec mentions samples and instruments, but in
// the header there are only pointers to instruments.
// However, there I never found instruments (SCRI) but
// instead samples (SCRS).
StringList comment;
for(ushort i = 0; i < sampleCount; ++ i)
{
@ -209,7 +219,7 @@ void S3M::File::read(bool)
READ_STRING_AS(sampleName, 28);
// The next 4 bytes should be "SCRS", but I've found
// otherwise ok files with 4 nils instead.
// files that are otherwise ok with 4 nils instead.
// READ_ASSERT(readBlock(4) == "SCRS");
comment.append(sampleName);

View File

@ -50,9 +50,9 @@ namespace TagLib {
ushort bpmSpeed() const;
protected:
void setTableLength(ushort tableLength);
void setChannels(int channels);
void setTableLength(ushort tableLength);
void setVersion(ushort version);
void setRestartPosition(ushort restartPosition);
void setPatternCount(ushort patternCount);

BIN
tests/data/changed.it Normal file

Binary file not shown.

BIN
tests/data/changed.s3m Normal file

Binary file not shown.

BIN
tests/data/test.it Normal file

Binary file not shown.

BIN
tests/data/test.s3m Normal file

Binary file not shown.