diff --git a/IffFile.cc b/IffFile.cc index 2943590..108ae7d 100644 --- a/IffFile.cc +++ b/IffFile.cc @@ -64,7 +64,7 @@ std::unique_ptr IffFile::parseObject(char const* base, size_t l if (!isprint(header.typeID[i])) throw FormatException{"Not an IFF chunk"}; - if (header.length > length-7) + if (header.length > length-8) throw FormatException{"length < size in header"}; if(memcmp(header.typeID, "FORM", 4) == 0) diff --git a/Makefile b/Makefile index 005bc34..065a2a8 100644 --- a/Makefile +++ b/Makefile @@ -1,6 +1,6 @@ CXX=g++ -CXXOPTS=-Og -ggdb -Wall -Wextra -pedantic -std=c++14 -flto -LDOPTS= +CXXOPTS=-Og -ggdb -fvar-tracking-assignments -Wall -Wextra -pedantic -std=c++14 -march=native -fstack-protector-strong --param=ssp-buffer-size=4 -flto +LDOPTS=-Wl,--sort-common,--as-needed iffexplore_CXXSRCS ::= iffexplore.cc IffFile.cc util.cc exceptions.cc iffexplore_LIBS ::= diff --git a/MveDecoder.cc b/MveDecoder.cc index bb8ff73..b1b42e9 100644 --- a/MveDecoder.cc +++ b/MveDecoder.cc @@ -6,11 +6,6 @@ #include "IffFile.hh" #include "MveDecoder.hh" -struct PCChunk { - uint32_t unknown[2]; - uint32_t PALTcount; -} __attribute__((__packed__)); - MveDecoder::MveDecoder(char const* base, size_t length) : iff_(base, length), width_(320), height_(165) { @@ -89,16 +84,18 @@ MveDecoder::MveDecoder(char const* base, size_t length) } else if (it->getType() == "INDX") { if (haveIndex) throw FormatException{"Multiple INDX"}; + if (curSHOT) + throw FormatException{"INDX after SHOT"}; 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); + size_t ofs = it->begin()-base-8; + auto idxIt = std::find(branches.begin(), branches.end(), ofs); if (idxIt == branches.end()) - throw FormatException{"Could not resolve branch " + std::to_string(ofs-8)}; + throw FormatException{"Could not resolve branch " + std::to_string(ofs)}; curBRCH = (idxIt-branches.begin()); } else if (it->getType() == "TEXT") { // Subtitle NYI @@ -106,7 +103,9 @@ MveDecoder::MveDecoder(char const* base, size_t length) throw FormatException{"Encountered unexpected chunk: " + it->getType()}; } + #ifdef VIDDEBUG_ unsigned i = 0; +#endif for (auto& brch : branches_) { for (auto& shot : brch) { #ifdef VIDDEBUG_ @@ -173,9 +172,7 @@ std::vector parseHuff_(char const* data, size_t len) } else { if (bytePos >= len) throw FormatException{"Huffman stream overrun"}; - memcpy(&byteBuf, data+bytePos, 1); - //printf("Byte: %.2hhx (%s)\n", byteBuf, binify(byteBuf)); - ++bytePos; + byteBuf = data[bytePos++]; bit = byteBuf&0x1; byteBuf >>= 1; byteValid = 7; @@ -183,11 +180,9 @@ std::vector parseHuff_(char const* data, size_t len) huffIdx = huffTree.at(huffIdx-(bit?1:23)); - //printf("step: %d %.2x\n", bit, huffIdx); if (huffIdx < 22) { commands.push_back(huffIdx); - //printf("out: %.2hhx\n", commands.back()); huffIdx = huffTree.size(); } } @@ -224,9 +219,7 @@ std::vector parsePixels_(char const* data, size_t len) uint8_t ofs = *(data+pos++); size = b&0x3; - if (pos+size >= len) - throw FormatException{"Pixel data overrun"}; - + replSize = ((b&0x1c)>>2) + 3; replOfs = ret.size()-(((b&0x60)<<3)+ofs+1)+size; } else if (!(b&0x40)) { @@ -240,8 +233,6 @@ std::vector parsePixels_(char const* data, size_t len) uint8_t b2 = *(data+pos++); size = (b1&0xc0)>>6; - if (pos+size >= len) - throw FormatException{"Pixel data overrun"}; replSize = (b&0x3f)+4; replOfs = ret.size()-(((b1&0x3f)<<8)+b2+1)+size; @@ -257,18 +248,20 @@ std::vector parsePixels_(char const* data, size_t len) uint8_t b3 = *(data+pos++); size = b&0x3; - if (pos+size >= len) - throw FormatException{"Pixel data overrun"}; replSize = b3+5+((b&0xc)<<6); replOfs = ret.size()-(((b&0x10)<<12)+1+(b1<<8)+b2)+size; } + if (pos+size >= len) + throw FormatException{"Pixel data overrun"}; std::copy(data+pos, data+pos+size, std::back_inserter(ret)); pos += size; - + + if (replOfs >= ret.size()) + throw FormatException{"Replication offset exceeds buffer"}; for (unsigned i = 0;i < replSize;++i) - ret.push_back(ret.at(replOfs+i)); + ret.push_back(ret[replOfs+i]); } else { #ifdef PIXELDEBUG_ printf("Code: Copy %.2hhx\n", b); @@ -541,7 +534,9 @@ void MveDecoder::play(unsigned branch) const SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, width_*2, - height_*2, + // 1.2 scale in height to compensate for non-square pixels in height + // in original MCGA/VGA 320x200 mode + height_*2*1.2, 0); if (!window) { @@ -612,9 +607,11 @@ void MveDecoder::play(unsigned branch) const auto vga = curShot->VGAs.begin(); uint32_t last = 0; + const double frameTime = 1000/15.0; + double nextFT = frameTime; Frame frame = parseVGA(**vga++, nullptr); - - std::vector frameTimes; + + decltype(last) minFT = std::numeric_limits::max(), maxFT = 0; bool close = false; @@ -634,9 +631,19 @@ void MveDecoder::play(unsigned branch) const } auto now = SDL_GetTicks(); - if (now-last >= 67) { - if (last) - frameTimes.push_back(now-last); + if (now-last >= nextFT) { + if (last) { + minFT = std::min(minFT, (now-last)); + maxFT = std::max(maxFT, (now-last)); + + if ((now-last)-nextFT <= frameTime) + nextFT = frameTime-((now-last)-nextFT); + else { + printf("NYI: Frame dropping\n"); + nextFT = frameTime; + } + } + 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()); @@ -713,7 +720,8 @@ void MveDecoder::play(unsigned branch) const } } frame = parseVGA(**vga++, &frame); - } + } else if ((nextFT-(now-last)) >= 10) + SDL_Delay(nextFT-(now-last)); } SDL_PauseAudioDevice(audioDev, 1); @@ -723,7 +731,6 @@ void MveDecoder::play(unsigned branch) const SDL_Quit(); printf("Frame times: [%u, %u]\n", - *std::min_element(frameTimes.begin(), frameTimes.end()), - *std::max_element(frameTimes.begin(), frameTimes.end())); + minFT, maxFT); } diff --git a/util.cc b/util.cc index 28b18d8..428fba9 100644 --- a/util.cc +++ b/util.cc @@ -92,16 +92,8 @@ uint16_t readU16BE(char const* data) if(!data) throw std::logic_error("readU16BE() called with nullptr"); - uint16_t ret; -#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ - ret = ((static_cast(*data)&0xff)<<8) | static_cast(*(data+1)); -#elif __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ - memcpy(&ret, data, 2); -#else -#error Unknown endianess -#endif - - return ret; + return ((static_cast(*data)&0xff)<<8) | + static_cast(*(data+1)); } uint32_t readU24BE(char const* data) @@ -109,18 +101,9 @@ uint32_t readU24BE(char const* data) if(!data) throw std::logic_error("readU24BE() called with nullptr"); - uint32_t ret = 0; -#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ - ret = ((static_cast(*data)&0xff)<<16) | + return ((static_cast(*data)&0xff)<<16) | ((static_cast(*(data+1))&0xff)<<8) | static_cast(*(data+2)); -#elif __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ - memcpy(static_cast(&ret)+1, data, 3); -#else -#error Unknown endianess -#endif - - return ret; } uint32_t readU32BE(char const* data) @@ -128,19 +111,10 @@ uint32_t readU32BE(char const* data) if(!data) throw std::logic_error("readU32BE() called with nullptr"); - uint32_t ret; -#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ - ret = ((static_cast(*data)&0xff)<<24) | + return ((static_cast(*data)&0xff)<<24) | ((static_cast(*(data+1))&0xff)<<16) | ((static_cast(*(data+2))&0xff)<<8) | static_cast(*(data+3)); -#elif __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ - memcpy(&ret, data, 4); -#else -#error Unknown endianess -#endif - - return ret; } uint16_t readU16LE(char const* data) @@ -148,16 +122,8 @@ uint16_t readU16LE(char const* data) if(!data) throw std::logic_error("readU16LE() called with nullptr"); - uint16_t ret; -#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ - memcpy(&ret, data, 2); -#elif __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ - ret = ((static_cast(*(data+1))&0xff)<<8) | static_cast(*data); -#else -#error Unknown endianess -#endif - - return ret; + return ((static_cast(*(data+1))&0xff)<<8) | + static_cast(*data); } uint32_t readU24LE(char const* data) @@ -165,18 +131,9 @@ uint32_t readU24LE(char const* data) if(!data) throw std::logic_error("readU24LE() called with nullptr"); - uint32_t ret = 0; -#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ - memcpy(&ret, data, 3); -#elif __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ - ret = ((static_cast(*(data+2))&0xff)<<16) | + return ((static_cast(*(data+2))&0xff)<<16) | ((static_cast(*(data+1))&0xff)<<8) | static_cast(*data); -#else -#error Unknown endianess -#endif - - return ret; } uint32_t readU32LE(char const* data) @@ -184,22 +141,13 @@ uint32_t readU32LE(char const* data) if(!data) throw std::logic_error("readU32LE() called with nullptr"); - uint32_t ret; -#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ - memcpy(&ret, data, 4); -#elif __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ - ret = ((static_cast(*(data+3))&0xff)<<24) | + return ((static_cast(*(data+3))&0xff)<<24) | ((static_cast(*(data+2))&0xff)<<16) | ((static_cast(*(data+1))&0xff)<<8) | static_cast(*data); -#else -#error Unknown endianess -#endif - - return ret; } -int sextend(unsigned b, int msb) +int sextend(unsigned b, unsigned msb) { if (msb >= sizeof(unsigned)*8) throw std::logic_error("sextend: msb out of range"); diff --git a/util.hh b/util.hh index ff8f8ee..327e185 100644 --- a/util.hh +++ b/util.hh @@ -40,6 +40,6 @@ uint32_t readU24LE(char const* data); uint32_t readU32LE(char const* data); // Sign-extend b starting at msb -int sextend(unsigned b, int msb); +int sextend(unsigned b, unsigned msb); #endif