From 12242de1e1cd29946a7fe46c5e5e6df83c56a1cd Mon Sep 17 00:00:00 2001 From: Matthias Blankertz Date: Sat, 25 Apr 2015 19:16:36 +0200 Subject: [PATCH] Working mvedecode with player --- Makefile | 2 +- MveDecoder.cc | 515 ++++++++++++++++++++++++++++++++++++++++++++++---- MveDecoder.hh | 21 +- TreFile.cc | 4 +- mvedecode.cc | 25 ++- util.cc | 12 ++ util.hh | 3 + 7 files changed, 530 insertions(+), 52 deletions(-) diff --git a/Makefile b/Makefile index d585f62..005bc34 100644 --- a/Makefile +++ b/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 diff --git a/MveDecoder.cc b/MveDecoder.cc index ae0ae96..bb8ff73 100644 --- a/MveDecoder.cc +++ b/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 PALTs; + // Store branch offsets, resolve to shots later + std::vector 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 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 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 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 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 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 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(-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 + +struct AudioCBData { + SDL_AudioSpec spec; + MveDecoder::Shot const *cur, *next; + std::vector::const_iterator aCur; + + size_t pos; +}; + +void audioCB_(void* userdata, uint8_t *buf, int len) +{ + AudioCBData *cbdata = static_cast(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 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 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 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())); +} + diff --git a/MveDecoder.hh b/MveDecoder.hh index 4109925..3369951 100644 --- a/MveDecoder.hh +++ b/MveDecoder.hh @@ -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; struct Frame { + std::vector pixels; }; struct Shot { Palette const& palt; std::vector AUDIs; - std::vector VGAs; + std::vector VGAs; }; - - std::vector shots_; - std::vector palts_; - static Frame parseVGA(IffFile::Object const& vga, Frame const* prev); + unsigned width_, height_; + + //std::vector shots_; + std::vector palts_; + std::vector > branches_; + + Frame parseVGA(IffFile::Object const& vga, Frame const* prev) const; + + friend struct AudioCBData; }; #endif diff --git a/TreFile.cc b/TreFile.cc index 6a1d0d7..fd91676 100644 --- a/TreFile.cc +++ b/TreFile.cc @@ -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)) diff --git a/mvedecode.cc b/mvedecode.cc index d7c5869..a2f7467 100644 --- a/mvedecode.cc +++ b/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(object.data(), object.size()); } else mve = std::make_unique(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()); diff --git a/util.cc b/util.cc index 13cb0f3..28b18d8 100644 --- a/util.cc +++ b/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<