Working mvedecode with player
This commit is contained in:
2
Makefile
2
Makefile
@@ -12,7 +12,7 @@ font2png_CXXSRCS ::= font2png.cc
|
||||
font2png_LIBS ::= -lpng
|
||||
|
||||
mvedecode_CXXSRCS ::= mvedecode.cc TreFile.cc IffFile.cc util.cc MveDecoder.cc exceptions.cc
|
||||
mvedecode_LIBS ::=
|
||||
mvedecode_LIBS ::= -lSDL2
|
||||
|
||||
progs ::= iffexplore treexplore mvedecode
|
||||
|
||||
|
||||
515
MveDecoder.cc
515
MveDecoder.cc
@@ -12,7 +12,7 @@ struct PCChunk {
|
||||
} __attribute__((__packed__));
|
||||
|
||||
MveDecoder::MveDecoder(char const* base, size_t length)
|
||||
: iff_(base, length)
|
||||
: iff_(base, length), width_(320), height_(165)
|
||||
{
|
||||
//iff_.printStructure();
|
||||
|
||||
@@ -48,9 +48,12 @@ MveDecoder::MveDecoder(char const* base, size_t length)
|
||||
throw FormatException{"Unexpected SOND value"};
|
||||
|
||||
// Parse data
|
||||
int curSHOT = -1;
|
||||
unsigned curPALT = 0;
|
||||
unsigned curPALT = 0, curBRCH = 0;
|
||||
bool haveIndex = false;
|
||||
std::vector<IffFile::Object const*> PALTs;
|
||||
// Store branch offsets, resolve to shots later
|
||||
std::vector<uint32_t> branches;
|
||||
Shot *curSHOT = nullptr;
|
||||
|
||||
for (;it != rootForm.childrenEnd();++it) {
|
||||
if (it->getType() == "PALT") {
|
||||
@@ -64,40 +67,57 @@ MveDecoder::MveDecoder(char const* base, size_t length)
|
||||
[](const char& in) -> uint8_t {return (in << 2) | ((in >> 6)&0x3);});
|
||||
++curPALT;
|
||||
} else if (it->getType() == "SHOT") {
|
||||
if (!haveIndex) {
|
||||
branches_.resize(1);
|
||||
curBRCH = 0;
|
||||
}
|
||||
|
||||
const uint32_t SHOTc = readU32LE(it->begin());
|
||||
if (SHOTc > PC_PALTcount)
|
||||
throw FormatException{"SHOT refers to PALT outside amount specified in _PC_"};
|
||||
++curSHOT;
|
||||
Shot shot{palts_.at(SHOTc), {}, {}};
|
||||
shots_.push_back(shot);
|
||||
branches_[curBRCH].push_back(shot);
|
||||
curSHOT = &branches_[curBRCH].back();
|
||||
} else if (it->getType() == "VGA ") {
|
||||
if (curSHOT < 0)
|
||||
if (!curSHOT)
|
||||
throw FormatException{"VGA outside SHOT"};
|
||||
shots_.back().VGAs.push_back(parseVGA(*it, shots_.back().VGAs.empty()?nullptr:&shots_.back().VGAs.back()));
|
||||
curSHOT->VGAs.push_back(&*it);
|
||||
} else if (it->getType() == "AUDI") {
|
||||
if (curSHOT < 0)
|
||||
if (!curSHOT)
|
||||
throw FormatException{"AUDI outside SHOT"};
|
||||
if (it->getSize() != 2940)
|
||||
throw FormatException{"Unexpected AUDI size"};
|
||||
shots_.back().AUDIs.push_back(&*it);
|
||||
} else if ((it->getType() == "INDX") ||
|
||||
(it->getType() == "BRCH")) {
|
||||
// Index/branches NYI
|
||||
curSHOT->AUDIs.push_back(&*it);
|
||||
} else if (it->getType() == "INDX") {
|
||||
if (haveIndex)
|
||||
throw FormatException{"Multiple INDX"};
|
||||
haveIndex = true;
|
||||
|
||||
for(unsigned i = 0;i < it->getSize()/4;++i)
|
||||
branches.push_back(readU32LE(it->begin()+i*4));
|
||||
branches_.resize(branches.size());
|
||||
} else if (it->getType() == "BRCH") {
|
||||
size_t ofs = it->begin()-base;
|
||||
auto idxIt = std::find(branches.begin(), branches.end(), ofs-8);
|
||||
if (idxIt == branches.end())
|
||||
throw FormatException{"Could not resolve branch " + std::to_string(ofs-8)};
|
||||
curBRCH = (idxIt-branches.begin());
|
||||
} else if (it->getType() == "TEXT") {
|
||||
// Subtitle NYI
|
||||
} else
|
||||
throw FormatException{"Encountered unexpected chunk: " + it->getType()};
|
||||
}
|
||||
|
||||
unsigned i = 0;
|
||||
for(auto& shot : shots_) {
|
||||
#ifndef NDEBUG
|
||||
printf("Shot %u: Palette %ld, %lu video frames, %lu audio blocks\n",
|
||||
i++, std::find(palts_.begin(), palts_.end(), shot.palt)-palts_.begin(),
|
||||
shot.VGAs.size(), shot.AUDIs.size());
|
||||
for (auto& brch : branches_) {
|
||||
for (auto& shot : brch) {
|
||||
#ifdef VIDDEBUG_
|
||||
printf("Shot %u: Palette %ld, %lu video frames, %lu audio blocks\n",
|
||||
i++, std::find(palts_.begin(), palts_.end(), shot.palt)-palts_.begin(),
|
||||
shot.VGAs.size(), shot.AUDIs.size());
|
||||
#endif
|
||||
if (shot.VGAs.size() != shot.AUDIs.size())
|
||||
throw FormatException{"Video/audio block count mismatch"};
|
||||
if (shot.VGAs.size() != shot.AUDIs.size())
|
||||
throw FormatException{"Video/audio block count mismatch"};
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
char *binify(uint8_t a) {
|
||||
@@ -196,7 +216,9 @@ std::vector<uint8_t> parsePixels_(char const* data, size_t len)
|
||||
unsigned size = 0, replSize = 0;
|
||||
unsigned replOfs = 0;
|
||||
if (!(b&0x80)) {
|
||||
//printf("Code: Repl1 %.2hhx\n", b);
|
||||
#ifdef PIXELDEBUG_
|
||||
printf("Code: Repl1 %.2hhx\n", b);
|
||||
#endif
|
||||
if (pos >= len)
|
||||
throw FormatException{"Pixel data overrun"};
|
||||
|
||||
@@ -208,7 +230,9 @@ std::vector<uint8_t> parsePixels_(char const* data, size_t len)
|
||||
replSize = ((b&0x1c)>>2) + 3;
|
||||
replOfs = ret.size()-(((b&0x60)<<3)+ofs+1)+size;
|
||||
} else if (!(b&0x40)) {
|
||||
//printf("Code: Repl3 %.2hhx\n", b);
|
||||
#ifdef PIXELDEBUG_
|
||||
printf("Code: Repl2 %.2hhx\n", b);
|
||||
#endif
|
||||
if (pos+1 >= len)
|
||||
throw FormatException{"Pixel data overrun"};
|
||||
|
||||
@@ -222,7 +246,9 @@ std::vector<uint8_t> parsePixels_(char const* data, size_t len)
|
||||
replSize = (b&0x3f)+4;
|
||||
replOfs = ret.size()-(((b1&0x3f)<<8)+b2+1)+size;
|
||||
} else if (!(b&0x20)) {
|
||||
//printf("Code: Repl2 %.2hhx\n", b);
|
||||
#ifdef PIXELDEBUG_
|
||||
printf("Code: Repl3 %.2hhx\n", b);
|
||||
#endif
|
||||
if (pos+2 >= len)
|
||||
throw FormatException{"Pixel data overrun"};
|
||||
|
||||
@@ -237,20 +263,35 @@ std::vector<uint8_t> parsePixels_(char const* data, size_t len)
|
||||
replSize = b3+5+((b&0xc)<<6);
|
||||
replOfs = ret.size()-(((b&0x10)<<12)+1+(b1<<8)+b2)+size;
|
||||
}
|
||||
for (unsigned i = 0;i < size;++i)
|
||||
ret.push_back(*(data+pos++));
|
||||
std::copy(data+pos, data+pos+size,
|
||||
std::back_inserter(ret));
|
||||
pos += size;
|
||||
|
||||
for (unsigned i = 0;i < replSize;++i)
|
||||
ret.push_back(ret.at(replOfs+i));
|
||||
} else {
|
||||
//printf("Code: Copy %.2hhx\n", b);
|
||||
#ifdef PIXELDEBUG_
|
||||
printf("Code: Copy %.2hhx\n", b);
|
||||
#endif
|
||||
unsigned size = (b&0x1f)*4+4;
|
||||
if (size > 0x70)
|
||||
if (size > 0x70) {
|
||||
if (pos+(b&0x3) > len)
|
||||
throw FormatException{"Pixel data overrun"};
|
||||
std::copy(data+pos, data+pos+(b&0x3),
|
||||
std::back_inserter(ret));
|
||||
pos += (b&0x3);
|
||||
#ifndef NDEBUG
|
||||
if (pos < len)
|
||||
printf("%lu unparsed bytes in pixel data\n", len-pos);
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
|
||||
if (pos+size > len)
|
||||
throw FormatException{"Pixel data overrun"};
|
||||
for (unsigned i = 0;i < size;++i)
|
||||
ret.push_back(*(data+pos++));
|
||||
std::copy(data+pos, data+pos+size,
|
||||
std::back_inserter(ret));
|
||||
pos += size;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -260,7 +301,7 @@ std::vector<uint8_t> parsePixels_(char const* data, size_t len)
|
||||
return ret;
|
||||
}
|
||||
|
||||
MveDecoder::Frame MveDecoder::parseVGA(IffFile::Object const& vga, Frame const* prev)
|
||||
MveDecoder::Frame MveDecoder::parseVGA(IffFile::Object const& vga, Frame const* prev) const
|
||||
{
|
||||
// Parse header
|
||||
if (vga.getSize() < 8)
|
||||
@@ -274,23 +315,415 @@ MveDecoder::Frame MveDecoder::parseVGA(IffFile::Object const& vga, Frame const*
|
||||
}
|
||||
|
||||
// Parse segment 1
|
||||
if ((segOfs[1] - segOfs[0]) < 45)
|
||||
if (!segOfs[0] ||
|
||||
((segOfs[1] && (segOfs[1] - segOfs[0]) < 45)) ||
|
||||
((segOfs[2] && (segOfs[2] - segOfs[0]) < 45)) ||
|
||||
((segOfs[3] && (segOfs[3] - segOfs[0]) < 45)) ||
|
||||
(vga.getSize() - segOfs[0] < 45))
|
||||
throw FormatException{"Segment 1 too small"};
|
||||
|
||||
auto commands = parseHuff_(vga.begin()+segOfs[0], segOfs[1]-segOfs[0]);
|
||||
|
||||
std::vector<uint8_t> pixels;
|
||||
if (segOfs[3] > 0)
|
||||
if (segOfs[3])
|
||||
pixels = parsePixels_(vga.begin()+segOfs[3], vga.getSize()-segOfs[3]);
|
||||
|
||||
if (pixels.size() == 52800) {
|
||||
FILEUPtr outFile{fopen("pixel.data", "wb")};
|
||||
if (!outFile)
|
||||
throw POSIXException{errno, "Could not open pixel.data"};
|
||||
#ifdef VIDDEBUG_
|
||||
printf("%lu commands, %lu pixel data, %u mvecs, %lu size data\n",
|
||||
commands.size(), pixels.size(), (segOfs[2]?(segOfs[3]-segOfs[2]):0),
|
||||
segOfs[2]?(segOfs[2]-segOfs[1]):(segOfs[3]?(segOfs[3]-segOfs[1]):(vga.getSize()-segOfs[1])));
|
||||
#endif
|
||||
|
||||
// Interpret command stream to render frame
|
||||
Frame ret;
|
||||
ret.pixels.reserve(width_*height_);
|
||||
|
||||
bool flag = false;
|
||||
size_t seg2Idx = 0;
|
||||
size_t seg3Idx = 0;
|
||||
size_t seg4Idx = 0;
|
||||
unsigned size = 0;
|
||||
auto it = commands.begin();
|
||||
while (it != commands.end()) {
|
||||
auto cmd = *it++;
|
||||
#ifdef CMDDEBUG_
|
||||
printf("CMD %u ", cmd);
|
||||
#endif
|
||||
|
||||
switch(cmd) {
|
||||
case 0:
|
||||
flag = !flag;
|
||||
continue;
|
||||
case 1:
|
||||
case 2:
|
||||
case 3:
|
||||
case 4:
|
||||
case 5:
|
||||
case 6:
|
||||
case 7:
|
||||
case 8:
|
||||
size = cmd;
|
||||
break;
|
||||
case 12:
|
||||
case 13:
|
||||
case 14:
|
||||
case 15:
|
||||
case 16:
|
||||
case 17:
|
||||
case 18:
|
||||
size = (cmd - 10);
|
||||
break;
|
||||
case 9:
|
||||
case 19:
|
||||
if (!segOfs[1] ||
|
||||
(segOfs[2] && (segOfs[1]+seg2Idx >= segOfs[2])) ||
|
||||
(segOfs[3] && (segOfs[1]+seg2Idx >= segOfs[3])) ||
|
||||
(segOfs[1]+seg2Idx >= vga.getSize()))
|
||||
throw FormatException{"Segment 2 overrun"};
|
||||
size = vga.begin()[segOfs[1]+seg2Idx++]&0xff;
|
||||
break;
|
||||
case 10:
|
||||
case 20:
|
||||
if (!segOfs[1] ||
|
||||
(segOfs[2] && (segOfs[1]+seg2Idx+1 >= segOfs[2])) ||
|
||||
(segOfs[3] && (segOfs[1]+seg2Idx >= segOfs[3])) ||
|
||||
(segOfs[1]+seg2Idx+1 >= vga.getSize()))
|
||||
throw FormatException{"Segment 2 overrun"};
|
||||
size = readU16BE(vga.begin()+segOfs[1]+seg2Idx);
|
||||
seg2Idx += 2;
|
||||
break;
|
||||
case 11:
|
||||
case 21:
|
||||
if (!segOfs[1] ||
|
||||
(segOfs[2] && (segOfs[1]+seg2Idx+2 >= segOfs[2])) ||
|
||||
(segOfs[3] && (segOfs[1]+seg2Idx >= segOfs[3])) ||
|
||||
(segOfs[1]+seg2Idx+2 >= vga.getSize()))
|
||||
throw FormatException{"Segment 2 overrun"};
|
||||
size = readU24BE(vga.begin()+segOfs[1]+seg2Idx);
|
||||
seg2Idx += 3;
|
||||
break;
|
||||
default:
|
||||
throw FormatException{"Invalid command"};
|
||||
}
|
||||
|
||||
if (fwrite(pixels.data(), pixels.size(), 1, outFile.get()) != 1)
|
||||
throw POSIXException{errno, "Could not write"};
|
||||
if (cmd < 12) {
|
||||
flag = !flag;
|
||||
if (flag) {
|
||||
#ifdef CMDDEBUG_
|
||||
printf("PrevCopy: %u\n", size);
|
||||
#endif
|
||||
if (!prev)
|
||||
throw FormatException{"Reference to non-existing prev. frame"};
|
||||
if (ret.pixels.size()+size > prev->pixels.size())
|
||||
throw FormatException{"Copy from prev exceeds frame"};
|
||||
|
||||
std::copy(prev->pixels.begin()+ret.pixels.size(),
|
||||
prev->pixels.begin()+ret.pixels.size()+size,
|
||||
std::back_inserter(ret.pixels));
|
||||
} else {
|
||||
#ifdef CMDDEBUG_
|
||||
printf("DataCopy: %u @%lu\n", size, seg4Idx);
|
||||
#endif
|
||||
if (seg4Idx+size > pixels.size())
|
||||
throw FormatException{"Copy from seg4 exceeds bounds"};
|
||||
|
||||
// exit(0);
|
||||
std::copy(pixels.begin()+seg4Idx,
|
||||
pixels.begin()+seg4Idx+size,
|
||||
std::back_inserter(ret.pixels));
|
||||
seg4Idx += size;
|
||||
}
|
||||
} else {
|
||||
#ifdef CMDDEBUG_
|
||||
printf("MvecCopy: %u\n", size);
|
||||
#endif
|
||||
if (!segOfs[2] ||
|
||||
(segOfs[3] && (segOfs[2]+seg3Idx > segOfs[3])) ||
|
||||
(segOfs[2]+seg3Idx > vga.getSize()))
|
||||
throw FormatException{"Segment 3 overrun"};
|
||||
if (!prev)
|
||||
throw FormatException{"Reference to non-existing prev. frame"};
|
||||
|
||||
const uint8_t mpos = vga.begin()[segOfs[2]+seg3Idx++];
|
||||
const signed x = sextend(mpos>>4, 4), y = sextend(mpos&0xf,4);
|
||||
const signed delta = y*width_+x;
|
||||
if ((delta < 0) && (ret.pixels.size() < static_cast<size_t>(-delta)))
|
||||
throw FormatException{"Motion vector outside frame"};
|
||||
|
||||
const unsigned ref = ret.pixels.size()+delta;
|
||||
#ifdef CMDDEBUG_
|
||||
printf("@%lu %.2hhx -> (%d, %d) -> d %d -> %u\n", seg3Idx-1, mpos, x, y, delta, ref);
|
||||
#endif
|
||||
|
||||
if (ref+size > prev->pixels.size())
|
||||
throw FormatException{"Copy from prev exceeds frame"};
|
||||
|
||||
std::copy(prev->pixels.begin()+ref,
|
||||
prev->pixels.begin()+ref+size,
|
||||
std::back_inserter(ret.pixels));
|
||||
flag = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (ret.pixels.size() != width_*height_)
|
||||
throw FormatException{"Decoded frame dimension mismatch"};
|
||||
|
||||
#ifndef NDEBUG
|
||||
if (seg4Idx < pixels.size())
|
||||
printf("%lu unused pixel data\n", pixels.size()-seg4Idx);
|
||||
#endif
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
#include <SDL2/SDL.h>
|
||||
|
||||
struct AudioCBData {
|
||||
SDL_AudioSpec spec;
|
||||
MveDecoder::Shot const *cur, *next;
|
||||
std::vector<IffFile::Object const*>::const_iterator aCur;
|
||||
|
||||
size_t pos;
|
||||
};
|
||||
|
||||
void audioCB_(void* userdata, uint8_t *buf, int len)
|
||||
{
|
||||
AudioCBData *cbdata = static_cast<AudioCBData*>(userdata);
|
||||
|
||||
if (!cbdata->cur && cbdata->next) {
|
||||
cbdata->cur = cbdata->next;
|
||||
cbdata->next = nullptr;
|
||||
cbdata->aCur = cbdata->cur->AUDIs.begin();
|
||||
cbdata->pos = 0;
|
||||
}
|
||||
|
||||
while ((len > 0) && cbdata->cur) {
|
||||
if (cbdata->pos >= (*cbdata->aCur)->getSize()) {
|
||||
cbdata->pos = 0;
|
||||
++cbdata->aCur;
|
||||
if (cbdata->aCur == cbdata->cur->AUDIs.end()) {
|
||||
cbdata->cur = cbdata->next;
|
||||
if (cbdata->next) {
|
||||
cbdata->next = nullptr;
|
||||
cbdata->aCur = cbdata->cur->AUDIs.begin();
|
||||
continue;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
auto& aCur = *cbdata->aCur;
|
||||
size_t toCopy = len;
|
||||
if (cbdata->pos+toCopy > aCur->getSize())
|
||||
toCopy = aCur->getSize()-cbdata->pos;
|
||||
std::copy(aCur->begin()+cbdata->pos, aCur->begin()+cbdata->pos+toCopy,
|
||||
buf);
|
||||
len -= toCopy;
|
||||
buf += toCopy;
|
||||
cbdata->pos += toCopy;
|
||||
}
|
||||
|
||||
if (len) {
|
||||
memset(buf, cbdata->spec.silence, len);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void MveDecoder::play(unsigned branch) const
|
||||
{
|
||||
SDL_Window *window;
|
||||
if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO) < 0) {
|
||||
printf("Could not init SDL: %s\n", SDL_GetError());
|
||||
return;
|
||||
}
|
||||
|
||||
window = SDL_CreateWindow("SDL2 Test",
|
||||
SDL_WINDOWPOS_UNDEFINED,
|
||||
SDL_WINDOWPOS_UNDEFINED,
|
||||
width_*2,
|
||||
height_*2,
|
||||
0);
|
||||
|
||||
if (!window) {
|
||||
printf("Could not create window: %s\n", SDL_GetError());
|
||||
SDL_Quit();
|
||||
return;
|
||||
}
|
||||
|
||||
SDL_Surface *screen = SDL_GetWindowSurface(window);
|
||||
if (!screen) {
|
||||
printf("Could not get window surface: %s\n", SDL_GetError());
|
||||
SDL_DestroyWindow(window);
|
||||
SDL_Quit();
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
|
||||
SDL_Palette *palette = SDL_AllocPalette(256);
|
||||
if(!palette) {
|
||||
printf("Could not create palette: %s\n", SDL_GetError());
|
||||
SDL_DestroyWindow(window);
|
||||
SDL_Quit();
|
||||
return;
|
||||
}
|
||||
|
||||
auto shot = branches_.at(branch).begin();
|
||||
auto curShot = shot++;
|
||||
|
||||
std::array<SDL_Color, 256> colors;
|
||||
for(unsigned i = 0;i < 256;++i) {
|
||||
colors[i].r = curShot->palt[i*3];
|
||||
colors[i].g = curShot->palt[i*3+1];
|
||||
colors[i].b = curShot->palt[i*3+2];
|
||||
colors[i].a = 255;
|
||||
}
|
||||
|
||||
if (SDL_SetPaletteColors(palette, colors.data(), 0, 256) != 0) {
|
||||
printf("Could not set palette: %s\n", SDL_GetError());
|
||||
SDL_FreePalette(palette);
|
||||
SDL_DestroyWindow(window);
|
||||
SDL_Quit();
|
||||
return;
|
||||
}
|
||||
|
||||
AudioCBData audioCBData;
|
||||
audioCBData.cur = &*curShot;
|
||||
audioCBData.next = nullptr;
|
||||
audioCBData.aCur = curShot->AUDIs.cbegin();
|
||||
audioCBData.pos = 0;
|
||||
|
||||
SDL_AudioSpec want;
|
||||
want.freq = 22050;
|
||||
want.format = AUDIO_S16LSB;
|
||||
want.channels = 1;
|
||||
want.samples = 4096;
|
||||
want.callback = &audioCB_;
|
||||
want.userdata = &audioCBData;
|
||||
|
||||
auto audioDev = SDL_OpenAudioDevice(nullptr, false, &want, &audioCBData.spec, 0);
|
||||
if (!audioDev) {
|
||||
printf("Failed to open audio: %s\n", SDL_GetError());
|
||||
SDL_FreePalette(palette);
|
||||
SDL_DestroyWindow(window);
|
||||
SDL_Quit();
|
||||
return;
|
||||
}
|
||||
|
||||
auto vga = curShot->VGAs.begin();
|
||||
uint32_t last = 0;
|
||||
Frame frame = parseVGA(**vga++, nullptr);
|
||||
|
||||
std::vector<decltype(last)> frameTimes;
|
||||
|
||||
bool close = false;
|
||||
|
||||
SDL_PauseAudioDevice(audioDev, 0);
|
||||
while (!close) {
|
||||
SDL_Event event;
|
||||
while (SDL_PollEvent(&event)) {
|
||||
switch(event.type) {
|
||||
case SDL_KEYDOWN:
|
||||
close = true;
|
||||
break;
|
||||
case SDL_WINDOWEVENT:
|
||||
if (event.window.event == SDL_WINDOWEVENT_CLOSE)
|
||||
close = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
auto now = SDL_GetTicks();
|
||||
if (now-last >= 67) {
|
||||
if (last)
|
||||
frameTimes.push_back(now-last);
|
||||
SDL_Surface *vidSurf = SDL_CreateRGBSurfaceFrom((void*)frame.pixels.data(), width_, height_, 8, width_, 0, 0, 0, 0);
|
||||
if (!vidSurf) {
|
||||
printf("Could not get video surface: %s\n", SDL_GetError());
|
||||
SDL_CloseAudioDevice(audioDev);
|
||||
SDL_FreePalette(palette);
|
||||
SDL_DestroyWindow(window);
|
||||
SDL_Quit();
|
||||
return;
|
||||
}
|
||||
|
||||
if (SDL_SetSurfacePalette(vidSurf, palette) != 0) {
|
||||
printf("Could not set surface palette: %s\n", SDL_GetError());
|
||||
SDL_FreeSurface(vidSurf);
|
||||
SDL_CloseAudioDevice(audioDev);
|
||||
SDL_FreePalette(palette);
|
||||
SDL_DestroyWindow(window);
|
||||
SDL_Quit();
|
||||
return;
|
||||
}
|
||||
|
||||
SDL_Surface *vidSurfRGB = SDL_ConvertSurface(vidSurf, screen->format, 0);
|
||||
if (!vidSurfRGB) {
|
||||
printf("Could not get video surface: %s\n", SDL_GetError());
|
||||
SDL_FreeSurface(vidSurf);
|
||||
SDL_CloseAudioDevice(audioDev);
|
||||
SDL_FreePalette(palette);
|
||||
SDL_DestroyWindow(window);
|
||||
SDL_Quit();
|
||||
return;
|
||||
}
|
||||
|
||||
if (SDL_BlitScaled(vidSurfRGB, nullptr, screen, nullptr) != 0) {
|
||||
printf("Could not blit video surface: %s\n", SDL_GetError());
|
||||
SDL_FreeSurface(vidSurfRGB);
|
||||
SDL_FreeSurface(vidSurf);
|
||||
SDL_CloseAudioDevice(audioDev);
|
||||
SDL_FreePalette(palette);
|
||||
SDL_DestroyWindow(window);
|
||||
SDL_Quit();
|
||||
return;
|
||||
}
|
||||
|
||||
SDL_UpdateWindowSurface(window);
|
||||
last = now;
|
||||
SDL_FreeSurface(vidSurfRGB);
|
||||
SDL_FreeSurface(vidSurf);
|
||||
|
||||
// Decode next frame
|
||||
if (vga == curShot->VGAs.end()) {
|
||||
if (shot == branches_.at(branch).end())
|
||||
break;
|
||||
else {
|
||||
curShot = shot++;
|
||||
vga = curShot->VGAs.begin();
|
||||
|
||||
std::array<SDL_Color, 256> colors;
|
||||
for(unsigned i = 0;i < 256;++i) {
|
||||
colors[i].r = curShot->palt[i*3];
|
||||
colors[i].g = curShot->palt[i*3+1];
|
||||
colors[i].b = curShot->palt[i*3+2];
|
||||
colors[i].a = 255;
|
||||
}
|
||||
|
||||
if (SDL_SetPaletteColors(palette, colors.data(), 0, 256) != 0) {
|
||||
printf("Could not set palette: %s\n", SDL_GetError());
|
||||
SDL_FreePalette(palette);
|
||||
SDL_DestroyWindow(window);
|
||||
SDL_Quit();
|
||||
return;
|
||||
}
|
||||
SDL_LockAudioDevice(audioDev);
|
||||
audioCBData.next = &*curShot;
|
||||
SDL_UnlockAudioDevice(audioDev);
|
||||
}
|
||||
}
|
||||
frame = parseVGA(**vga++, &frame);
|
||||
}
|
||||
}
|
||||
|
||||
SDL_PauseAudioDevice(audioDev, 1);
|
||||
SDL_CloseAudioDevice(audioDev);
|
||||
SDL_FreePalette(palette);
|
||||
SDL_DestroyWindow(window);
|
||||
SDL_Quit();
|
||||
|
||||
printf("Frame times: [%u, %u]\n",
|
||||
*std::min_element(frameTimes.begin(), frameTimes.end()),
|
||||
*std::max_element(frameTimes.begin(), frameTimes.end()));
|
||||
}
|
||||
|
||||
|
||||
@@ -10,24 +10,35 @@ class MveDecoder {
|
||||
public:
|
||||
MveDecoder(char const* base, size_t length);
|
||||
|
||||
size_t numBranches() const {
|
||||
return branches_.size();
|
||||
}
|
||||
|
||||
void play(unsigned branch) const;
|
||||
private:
|
||||
IffFile iff_;
|
||||
|
||||
using Palette = std::array<uint8_t, 768>;
|
||||
|
||||
struct Frame {
|
||||
std::vector<uint8_t> pixels;
|
||||
};
|
||||
|
||||
struct Shot {
|
||||
Palette const& palt;
|
||||
std::vector<IffFile::Object const*> AUDIs;
|
||||
std::vector<Frame> VGAs;
|
||||
std::vector<IffFile::Object const*> VGAs;
|
||||
};
|
||||
|
||||
std::vector<Shot> shots_;
|
||||
std::vector<Palette> palts_;
|
||||
|
||||
static Frame parseVGA(IffFile::Object const& vga, Frame const* prev);
|
||||
unsigned width_, height_;
|
||||
|
||||
//std::vector<Shot> shots_;
|
||||
std::vector<Palette> palts_;
|
||||
std::vector<std::vector<Shot> > branches_;
|
||||
|
||||
Frame parseVGA(IffFile::Object const& vga, Frame const* prev) const;
|
||||
|
||||
friend struct AudioCBData;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
@@ -68,8 +68,8 @@ TreFile::TreFile(char const* base, size_t length)
|
||||
char const* const entryBase = base_+header.table3Ofs+i*table3EntrySize;
|
||||
|
||||
const uint32_t dataPtr = readU32LE(entryBase);
|
||||
const uint32_t length = readU24LE(entryBase+4);
|
||||
const uint8_t flags = *(entryBase+7);
|
||||
const uint32_t length = readU32LE(entryBase+4)&0x0fffffffu;
|
||||
const uint8_t flags = *(entryBase+7)&0xf0;
|
||||
|
||||
if ((dataPtr > length_) ||
|
||||
(dataPtr < header.dataOfs))
|
||||
|
||||
25
mvedecode.cc
25
mvedecode.cc
@@ -16,19 +16,31 @@
|
||||
void usage(char *argv0) {
|
||||
fprintf(stderr, "Usage: %s [-h] (tre-file name/crc)/iff-file\n", argv0);
|
||||
fprintf(stderr, "\tAttempt to decode the movie stored in iff-file, or in the\n\tiff-object \"name\"/\"crc\" contained in tre-file\n");
|
||||
fprintf(stderr, "\t-p [id]\tPlay branch id, or all branches if no id given\n");
|
||||
fprintf(stderr, "\t-h\tPrint this help\n");
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
std::string inFile, objectSpec;
|
||||
bool useTre = false;
|
||||
int branch = -1;
|
||||
bool useTre = false, play = false;
|
||||
{
|
||||
int opt;
|
||||
while ((opt = getopt(argc, argv, "h")) != -1) {
|
||||
while ((opt = getopt(argc, argv, "hp::")) != -1) {
|
||||
switch (opt) {
|
||||
case 'h':
|
||||
usage(argv[0]);
|
||||
return 0;
|
||||
case 'p':
|
||||
play = true;
|
||||
if (optarg) {
|
||||
try {
|
||||
branch = std::stoi(optarg, nullptr, 10);
|
||||
} catch (std::invalid_argument &ex) {
|
||||
} catch (std::out_of_range &ex) {
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
usage(argv[0]);
|
||||
return 1;
|
||||
@@ -74,7 +86,14 @@ int main(int argc, char *argv[]) {
|
||||
mve = std::make_unique<MveDecoder>(object.data(), object.size());
|
||||
} else
|
||||
mve = std::make_unique<MveDecoder>(mmap.data(), mmap.size());
|
||||
|
||||
|
||||
if (play) {
|
||||
if (branch >= 0)
|
||||
mve->play(branch);
|
||||
else
|
||||
for(unsigned i = 0;i < mve->numBranches();++i)
|
||||
mve->play(i);
|
||||
}
|
||||
} catch (POSIXException &ex) {
|
||||
fflush(stdout);
|
||||
fprintf(stderr, "%s\n", ex.toString().c_str());
|
||||
|
||||
12
util.cc
12
util.cc
@@ -198,3 +198,15 @@ uint32_t readU32LE(char const* data)
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int sextend(unsigned b, int msb)
|
||||
{
|
||||
if (msb >= sizeof(unsigned)*8)
|
||||
throw std::logic_error("sextend: msb out of range");
|
||||
|
||||
if (b&(1<<(msb-1))) { // msb is 1
|
||||
return ~((1<<msb)-1) | (b&((1<<msb)-1));
|
||||
} else { // msb is 0
|
||||
return b&((1<<msb)-1);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user