#include <vector>
#include <fstream>
#include <cassert>
#include "MVILoader.h"
struct Header
{
int major;
int minor;
int width;
int height;
int num_entries;
int num_audio;
int num_video;
int fps;
int has_audio;
int audio_num_channels;
int audio_samplerate;
int audio_bits;
};
struct VideoDiffHead
{
int num_payload1;
int num_colors;//muss nur ungleich 1 sein um änderungen anzuzeigen
int num_payload2;
int num_runlengths;//immer 9601
};
struct VideoDiffSubHeader
{
int size_payload;
int num_unzipped;
};
struct Decoder
{
struct Entry
{
unsigned char value;
unsigned short parent;
unsigned short count;
Entry(unsigned char value=0, unsigned short parent=2, unsigned short count=1)
: value(value), parent(parent), count(count)
{}
};
const char* in;
size_t in_offset;
const unsigned char EoiCode;
unsigned short CodeSize;
std::vector<Entry> dict;
std::vector<unsigned char>& out;
Decoder(const char* in, std::vector<unsigned char> &out, size_t in_offset = 0)
: in(in), in_offset(in_offset), out(out), EoiCode(2), CodeSize(9)
{
dict.reserve(1<<CodeSize);
dict.insert(dict.begin(), 3, Entry());
//fill the dict with 3 dummies and the rest
for(unsigned short counter = 0; counter < 256; counter++)
dict.push_back(Entry(static_cast<unsigned char>(counter)));
}
unsigned short GetNextCode(void)
{
//get the code from a big endian, bitwise read input
unsigned int intCode;
char tempbuf[] = { in[in_offset/8+3], in[in_offset/8+2], in[in_offset/8+1], in[in_offset/8+0] };
memcpy((char*)&intCode, tempbuf, 4);
unsigned int l = intCode >> (32 - CodeSize -(in_offset%8));//big endian -> little endian
unsigned int Code = l & (0xFFFFFFFF >> (32 - CodeSize));//mask
in_offset += CodeSize; // Increase Bit Offset
return static_cast<unsigned short>(Code);
}
unsigned char WriteOut(unsigned short in)
{
out.insert(out.end(),dict[in].count, 0);
size_t offset = out.size();
unsigned char last_written;
while(in != EoiCode)
{
out[--offset] = dict[in].value;
last_written = dict[in].value;
in = dict[in].parent;
}
return last_written;
}
void Decode(void)
{
unsigned short Code;
unsigned short OldCode = GetNextCode();
WriteOut(OldCode);
while ((Code = GetNextCode()) != EoiCode)
{
if (Code == 0)//next mask
{
CodeSize++;
dict.reserve(1<<CodeSize);
if(in_offset%8) in_offset = (in_offset/8+1)*8;
}
else if(Code == 1)
{
assert(false);
}
else
{
unsigned char last_written = WriteOut(Code >= dict.size() ? OldCode : Code);
if(Code >= dict.size()) out.push_back(last_written);
dict.push_back(Entry(last_written, OldCode, dict[OldCode].count+1));
OldCode = Code;
}
}
}
};
void MVILoader::load(const char* _name)
{
Item item;
Header header;
std::string name_mvi(_name);
name_mvi += ".MVI";
file.open(name_mvi.c_str(), std::ios::out | std::ios::in | std::ios::binary);
if(!file.is_open())
throw std::exception(name_mvi.c_str());
file.read((char*)&header, sizeof(header));
items.clear();
items.reserve(header.num_entries);
imgdata.clear();
imgdata.insert(imgdata.end(), header.height*header.width, 0);
for(int counter = 0; counter < header.num_entries; counter++)
{
file.read((char*)&item, sizeof(item));
items.push_back(item);
}
size_t payload_offset = ((file.tellg() / 2048) + 1) * 2048;
file.seekg(payload_offset);
current = items.begin();
name = name_mvi;
width = header.width;
height = header.height;
fps = header.fps;
has_audio = header.has_audio == 1;
audio_num_channels = header.audio_num_channels;
audio_samplerate = header.audio_samplerate;
audio_bits = header.audio_bits;
}
void MVILoader::prefetch_sound(void)
{
sounddata.clear();
if(!has_audio)
return;
std::vector<unsigned char> left;
std::vector<unsigned char> right;
size_t payload_offset = file.tellg();
file.seekg(payload_offset);
for(std::vector<Item>::iterator it = items.begin(); it != items.end(); it++)
{
switch(it->type)
{
case 5: //palette
case 2: //video
case 1: //video
break;
case 3: //leftchannel
{
char *in = new char[it->length];
file.seekg(it->offset + payload_offset);
file.read(in, it->length);
left.insert(left.end(),in, in+it->length);
delete[] in;
}break;
case 4: //rightchannel
{
char *in = new char[it->length];
file.seekg(it->offset + payload_offset);
file.read(in, it->length);
right.insert(right.end(),in, in+it->length);
delete[] in;
}break;
default:
assert(false);
}
}
std::vector<unsigned char>::iterator it_left = left.begin();
std::vector<unsigned char>::iterator it_right = right.begin();
sounddata.reserve((left.size() + right.size()) * 2);
bool l,r;
while((l = (it_left != left.end())) && (r = (it_right != right.end())) && (l || r))
{
if(l)
{
sounddata.push_back(0);
sounddata.push_back(*it_left++);
}
if(r)
{
sounddata.push_back(0);
sounddata.push_back(*it_right++);
}
}
file.seekg(payload_offset);
}
void MVILoader::decode_next_frame(void)
{
for(bool finished = false; !finished && current != items.end(); current++)
{
char *in = new char[current->length];
file.read(in, current->length);
switch(current->type)
{
case 5: //palette
{
unsigned char* ptr = reinterpret_cast<unsigned char*>(in);
for(unsigned counter = 0; counter < 256; counter++)
{
size_t off = counter * 3 + (current->length == 772 ? 4 : 0);
palette[counter] = 0xFF000000 | (ptr[off] << 16) | (ptr[off+1] << 8) | ptr[off+2];
}//rest wird komplett ignoriert ->65k für nichts!
finished = true;
}break;
case 1: //video
{
out.clear();
out.reserve(height * width);
Decoder decoder(in, out, 8*8);
decoder.Decode();
assert(decoder.in_offset/8 <= current->length && current->length <= (decoder.in_offset+decoder.CodeSize)/8);
int pixel_step = (height * width) / out.size();
for(int counter = 0; counter < height * width; counter++)
imgdata[counter] = palette[out[counter/pixel_step]];
finished = true;
}break;
case 2: //video
{
VideoDiffHead head;
VideoDiffSubHeader subhead1;//wird ignoriert;
VideoDiffSubHeader subhead2;//wird ignoriert;
std::vector<unsigned char> data1;
std::vector<unsigned char> data2;
size_t byte_offset = 0;
memcpy(&head, in, sizeof(head)); byte_offset += sizeof(head);
if(current->length == 16)
{
finished = true;
break;
}
memcpy(&subhead1, in + byte_offset, sizeof(subhead1)); byte_offset += sizeof(subhead1);
{
size_t length = head.num_payload1 - sizeof(subhead1);
data1.reserve(length);
Decoder decoder(in + byte_offset, data1);
decoder.Decode();
assert(decoder.in_offset/8 <= length && length <= (decoder.CodeSize+decoder.in_offset)/8);
byte_offset += length;
}
if(head.num_payload2 < head.num_runlengths)
{
memcpy(&subhead2, in+byte_offset, sizeof(subhead2)); byte_offset += sizeof(subhead2);
size_t length = head.num_payload2 - sizeof(subhead2);
data2.reserve(length);
Decoder decoder(in + byte_offset, data2);
decoder.Decode();
assert(decoder.in_offset/8 <= length && length <= (decoder.CodeSize+decoder.in_offset)/8);
byte_offset += length;
}
else
{
data2.insert(data2.end(), in + byte_offset, in + byte_offset + head.num_payload2);
byte_offset += head.num_payload2;
}
assert(current->length >= sizeof(head) + sizeof(subhead1));
assert(current->length == sizeof(head) + head.num_payload1 + head.num_payload2);
assert(head.num_payload1 == sizeof(subhead1) + subhead1.size_payload + 8);
assert(head.num_payload2 == head.num_runlengths || head.num_payload2 == sizeof(subhead2) + subhead2.size_payload + 8);
assert(head.num_payload2 == head.num_runlengths || head.num_runlengths == subhead2.num_unzipped);
assert(head.num_colors == subhead1.num_unzipped);
assert(head.num_colors == data1.size());
assert(head.num_runlengths == data2.size());
int color_offset = 0;
int pixel_step = (height * width) / out.size();
for(int counter = 0; counter < head.num_runlengths-1; counter++)
{
unsigned char bitfield = data2[counter];
for(int shift = 0; bitfield;shift++)
{
if(bitfield & 128)
{
unsigned int color = palette[data1[color_offset++]];
unsigned int pixel = (counter*8 + shift) * pixel_step;
for(unsigned x = 0; x < pixel_step; x++)
imgdata[pixel + x] = color;
}
bitfield = bitfield << 1;
}
}
finished = true;
}break;
case 3: //leftchannel
case 4: //rightchannel
break;
default:
assert(false);
}
delete[] in;
}
}