Audio playback in movie playback; movie decoder now uses pre-allocated buffers
This commit is contained in:
8
Makefile
8
Makefile
@@ -2,9 +2,11 @@ CXX=g++
|
||||
CXXOPTS=-Og -ggdb -fvar-tracking-assignments -Wall -Wextra -Wno-unused-function -pedantic -std=c++14 -march=native -fstack-protector-strong --param=ssp-buffer-size=4 -flto -I.
|
||||
LDOPTS=-Wl,--sort-common,--as-needed
|
||||
|
||||
render_CXXSRCS ::= render/Renderer.cc render/GlResource.cc render/ProgramProvider.cc render/renderutil.cc render/Overlay.cc render/VBOManager.cc render/Object.cc
|
||||
render_CXXSRCS ::= render/Renderer.cc render/GlResource.cc render/ProgramProvider.cc render/renderutil.cc render/Overlay.cc render/VBOManager.cc render/Object.cc render/AlResource.cc render/AudioStream.cc
|
||||
render_LIBS ::= -lSDL2 -lSDL2_ttf -lglbinding -lopenal
|
||||
game_CXXSRCS ::= game/GSMvePlay.cc game/GSShowObject.cc
|
||||
|
||||
|
||||
iffexplore_CXXSRCS ::= iffexplore.cc IffFile.cc util.cc exceptions.cc
|
||||
iffexplore_LIBS ::=
|
||||
|
||||
@@ -15,10 +17,10 @@ font2png_CXXSRCS ::= font2png.cc
|
||||
font2png_LIBS ::= -lpng
|
||||
|
||||
mvedecode_CXXSRCS ::= mvedecode.cc TreFile.cc IffFile.cc util.cc MveDecoder.cc exceptions.cc decompress.cc $(render_CXXSRCS) $(game_CXXSRCS)
|
||||
mvedecode_LIBS ::= -lSDL2 -lSDL2_ttf -lglbinding
|
||||
mvedecode_LIBS ::= $(render_LIBS)
|
||||
|
||||
objdecode_CXXSRCS ::= objdecode.cc TreFile.cc IffFile.cc util.cc ObjDecoder.cc exceptions.cc decompress.cc PaletteDecoder.cc $(render_CXXSRCS) $(game_CXXSRCS)
|
||||
objdecode_LIBS ::= -lSDL2 -lSDL2_ttf -lglbinding
|
||||
objdecode_LIBS ::= $(render_LIBS)
|
||||
|
||||
progs ::= iffexplore treexplore mvedecode objdecode
|
||||
|
||||
|
||||
127
MveDecoder.cc
127
MveDecoder.cc
@@ -2,6 +2,7 @@
|
||||
#include <cstring>
|
||||
|
||||
#include "common.hh"
|
||||
#include "compiler.hh"
|
||||
#include "util.hh"
|
||||
#include "IffFile.hh"
|
||||
#include "decompress.hh"
|
||||
@@ -130,15 +131,17 @@ MveDecoder::MveDecoder(uint8_t const* base, size_t length)
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<char> parseHuff_(uint8_t const* data, size_t len)
|
||||
template<typename OutputIt>
|
||||
size_t parseHuff_(uint8_t const* data, size_t len, OutputIt commands, size_t maxOut)
|
||||
{
|
||||
uint8_t numHuffVals;
|
||||
memcpy(&numHuffVals, data, 1);
|
||||
const uint8_t numHuffVals = *data;
|
||||
if (numHuffVals != 22)
|
||||
throw FormatException{"Unexpected huffman tree size"};
|
||||
|
||||
std::array<uint8_t, 44> huffTree;
|
||||
memcpy(huffTree.data(), data+1, 44);
|
||||
std::copy(data+1, data+45,
|
||||
huffTree.begin());
|
||||
// memcpy(huffTree.data(), data+1, 44);
|
||||
|
||||
#ifdef HUFFDEBUG_
|
||||
printf("Coded data length: %d\nTree:\n", segOfs[1]-(segOfs[0]+45));
|
||||
@@ -157,8 +160,8 @@ std::vector<char> parseHuff_(uint8_t const* data, size_t len)
|
||||
int byteValid = 0;
|
||||
uint8_t byteBuf = 0;
|
||||
unsigned bytePos = 45;
|
||||
std::vector<char> commands;
|
||||
unsigned huffIdx = huffTree.size();
|
||||
size_t count = 0;
|
||||
while (huffIdx != 22) {
|
||||
// Read next bit
|
||||
unsigned bit;
|
||||
@@ -179,31 +182,28 @@ std::vector<char> parseHuff_(uint8_t const* data, size_t len)
|
||||
|
||||
|
||||
if (huffIdx < 22) {
|
||||
commands.push_back(huffIdx);
|
||||
*commands++ = huffIdx;
|
||||
if (++count >= maxOut)
|
||||
throw Exception{"Output buffer overflow"};
|
||||
huffIdx = huffTree.size();
|
||||
}
|
||||
}
|
||||
|
||||
if (commands.empty())
|
||||
if (count == 0)
|
||||
throw FormatException{"No commands decoded"};
|
||||
|
||||
#ifdef HUFFDEBUG_
|
||||
for(auto ent : commands)
|
||||
printf("%.2hhx ", ent);
|
||||
printf("\n");
|
||||
#endif
|
||||
|
||||
return commands;
|
||||
return count;
|
||||
}
|
||||
|
||||
std::vector<uint8_t> parsePixels_(uint8_t const* data, size_t len)
|
||||
size_t parsePixels_(uint8_t const* RESTRICT data, size_t len, uint8_t * RESTRICT out, size_t maxOut)
|
||||
{
|
||||
if (*data == 0x02) {
|
||||
return decompressLZ(data+1, len-1);
|
||||
return decompressLZInto(data+1, len-1, out, maxOut);
|
||||
} else {
|
||||
std::vector<uint8_t> ret;
|
||||
std::copy(data+1, data+len, std::back_inserter(ret));
|
||||
return ret;
|
||||
if (len-1 > maxOut)
|
||||
throw Exception{"Output buffer overflow"};
|
||||
std::copy(data+1, data+len, out);
|
||||
return len-1;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -221,8 +221,19 @@ MveDecoder::Movie::Movie(std::vector<Shot>::const_iterator shotIt,
|
||||
: shotIt_(shotIt), shotEnd_(shotEnd),
|
||||
frameIt_(shotIt_->VGAs.cbegin()),
|
||||
audioIt_(shotIt_->AUDIs.cbegin()),
|
||||
mve_(mve)
|
||||
mve_(mve), maxPixel_(0), maxCommand_(0)
|
||||
{
|
||||
pixelBuf_.resize(mve_.width_*mve_.height_);
|
||||
commandBuf_.reserve(32768);
|
||||
frameBuf_.reserve(mve_.width_*mve_.height_);
|
||||
frame_.reserve(mve_.width_*mve_.height_);
|
||||
}
|
||||
|
||||
MveDecoder::Movie::~Movie()
|
||||
{
|
||||
#ifndef NDEBUG
|
||||
printf("Maximum command buffer: %zu\n", maxCommand_);
|
||||
#endif
|
||||
}
|
||||
|
||||
bool MveDecoder::Movie::hasNext() const {
|
||||
@@ -234,7 +245,7 @@ bool MveDecoder::Movie::hasNext() const {
|
||||
|
||||
std::tuple<MveDecoder::Frame const&, MveDecoder::Palette const&> MveDecoder::Movie::decodeNext()
|
||||
{
|
||||
frame_ = mve_.parseVGA(**frameIt_++, &frame_);
|
||||
parseVGA(**frameIt_++);
|
||||
auto& palette = shotIt_->palt;
|
||||
|
||||
if (frameIt_ == shotIt_->VGAs.cend()) {
|
||||
@@ -256,12 +267,12 @@ bool MveDecoder::Movie::hasAudio() const
|
||||
MveDecoder::Audio MveDecoder::Movie::getNextAudio()
|
||||
{
|
||||
MveDecoder::Audio ret;
|
||||
if (audioIt_ != shotIt_->AUDIs.cend()) {
|
||||
if ((shotIt_ != shotEnd_) && (audioIt_ != shotIt_->AUDIs.cend())) {
|
||||
ret.reserve((shotIt_->AUDIs.cend()-audioIt_)*2940);
|
||||
|
||||
for(;audioIt_ != shotIt_->AUDIs.cend();++audioIt_)
|
||||
std::copy((*audioIt_)->begin(), (*audioIt_)->end(),
|
||||
std::back_inserter(ret));
|
||||
for(auto it = (*audioIt_)->begin();it != (*audioIt_)->end();it += 2)
|
||||
ret.push_back(readU16LE(it));
|
||||
}
|
||||
|
||||
return ret;
|
||||
@@ -277,7 +288,7 @@ unsigned MveDecoder::Movie::getHeight() const
|
||||
return mve_.height_;
|
||||
}
|
||||
|
||||
MveDecoder::Frame MveDecoder::parseVGA(IffFile::Object const& vga, Frame const* prev) const
|
||||
void MveDecoder::Movie::parseVGA(IffFile::Object const& vga)
|
||||
{
|
||||
// Parse header
|
||||
if (vga.getSize() < 8)
|
||||
@@ -298,11 +309,17 @@ MveDecoder::Frame MveDecoder::parseVGA(IffFile::Object const& vga, Frame const*
|
||||
(vga.getSize() - segOfs[0] < 45))
|
||||
throw FormatException{"Segment 1 too small"};
|
||||
|
||||
auto commands = parseHuff_(vga.begin()+segOfs[0], segOfs[1]-segOfs[0]);
|
||||
commandBuf_.clear();
|
||||
parseHuff_(vga.begin()+segOfs[0], segOfs[1]-segOfs[0],
|
||||
std::back_inserter(commandBuf_), commandBuf_.capacity());
|
||||
maxCommand_ = std::max(maxCommand_, commandBuf_.size());
|
||||
|
||||
std::vector<uint8_t> pixels;
|
||||
if (segOfs[3])
|
||||
pixels = parsePixels_(vga.begin()+segOfs[3], vga.getSize()-segOfs[3]);
|
||||
size_t pixelCount = 0;
|
||||
if (segOfs[3]) {
|
||||
pixelCount = parsePixels_(vga.begin()+segOfs[3], vga.getSize()-segOfs[3],
|
||||
pixelBuf_.data(), pixelBuf_.size());
|
||||
maxPixel_ = std::max(maxPixel_, pixelCount);
|
||||
}
|
||||
|
||||
#ifdef VIDDEBUG_
|
||||
printf("%lu commands, %lu pixel data, %u mvecs, %lu size data\n",
|
||||
@@ -311,17 +328,13 @@ MveDecoder::Frame MveDecoder::parseVGA(IffFile::Object const& vga, Frame const*
|
||||
#endif
|
||||
|
||||
// Interpret command stream to render frame
|
||||
Frame ret;
|
||||
ret.reserve(width_*height_);
|
||||
|
||||
frameBuf_.clear();
|
||||
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++;
|
||||
for (auto cmd : commandBuf_) {
|
||||
#ifdef CMDDEBUG_
|
||||
printf("CMD %u ", cmd);
|
||||
#endif
|
||||
@@ -388,24 +401,22 @@ MveDecoder::Frame MveDecoder::parseVGA(IffFile::Object const& vga, Frame const*
|
||||
#ifdef CMDDEBUG_
|
||||
printf("PrevCopy: %u\n", size);
|
||||
#endif
|
||||
if (!prev)
|
||||
throw FormatException{"Reference to non-existing prev. frame"};
|
||||
if (ret.size()+size > prev->size())
|
||||
if (frameBuf_.size()+size > frame_.size())
|
||||
throw FormatException{"Copy from prev exceeds frame"};
|
||||
|
||||
std::copy(prev->begin()+ret.size(),
|
||||
prev->begin()+ret.size()+size,
|
||||
std::back_inserter(ret));
|
||||
std::copy(frame_.begin()+frameBuf_.size(),
|
||||
frame_.begin()+frameBuf_.size()+size,
|
||||
std::back_inserter(frameBuf_));
|
||||
} else {
|
||||
#ifdef CMDDEBUG_
|
||||
printf("DataCopy: %u @%lu\n", size, seg4Idx);
|
||||
#endif
|
||||
if (seg4Idx+size > pixels.size())
|
||||
if (seg4Idx+size > pixelCount)
|
||||
throw FormatException{"Copy from seg4 exceeds bounds"};
|
||||
|
||||
std::copy(pixels.begin()+seg4Idx,
|
||||
pixels.begin()+seg4Idx+size,
|
||||
std::back_inserter(ret));
|
||||
std::copy(pixelBuf_.begin()+seg4Idx,
|
||||
pixelBuf_.begin()+seg4Idx+size,
|
||||
std::back_inserter(frameBuf_));
|
||||
seg4Idx += size;
|
||||
}
|
||||
} else {
|
||||
@@ -416,39 +427,37 @@ MveDecoder::Frame MveDecoder::parseVGA(IffFile::Object const& vga, Frame const*
|
||||
(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.size() < static_cast<size_t>(-delta)))
|
||||
const signed delta = y*mve_.width_+x;
|
||||
if ((delta < 0) && (frameBuf_.size() < static_cast<size_t>(-delta)))
|
||||
throw FormatException{"Motion vector outside frame"};
|
||||
|
||||
const unsigned ref = ret.size()+delta;
|
||||
const unsigned ref = frameBuf_.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->size())
|
||||
if (ref+size > frame_.size())
|
||||
throw FormatException{"Copy from prev exceeds frame"};
|
||||
|
||||
std::copy(prev->begin()+ref,
|
||||
prev->begin()+ref+size,
|
||||
std::back_inserter(ret));
|
||||
std::copy(frame_.begin()+ref,
|
||||
frame_.begin()+ref+size,
|
||||
std::back_inserter(frameBuf_));
|
||||
flag = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (ret.size() != width_*height_)
|
||||
if (frameBuf_.size() != mve_.width_*mve_.height_)
|
||||
throw FormatException{"Decoded frame dimension mismatch"};
|
||||
|
||||
#ifndef NDEBUG
|
||||
if (seg4Idx < pixels.size())
|
||||
printf("%lu unused pixel data\n", pixels.size()-seg4Idx);
|
||||
if (seg4Idx < pixelCount)
|
||||
printf("%lu unused pixel data\n", pixelCount-seg4Idx);
|
||||
#endif
|
||||
|
||||
return ret;
|
||||
frame_.swap(frameBuf_);
|
||||
}
|
||||
/*
|
||||
#include <SDL2/SDL.h>
|
||||
|
||||
@@ -15,7 +15,7 @@ public:
|
||||
}
|
||||
|
||||
using Frame = std::vector<uint8_t>;
|
||||
using Audio = std::vector<uint8_t>;
|
||||
using Audio = std::vector<int16_t>;
|
||||
using Palette = std::array<uint8_t, 768>;
|
||||
private:
|
||||
using AUDIVec = std::vector<IffFile::Object const*>;
|
||||
@@ -32,7 +32,8 @@ public:
|
||||
public:
|
||||
bool hasNext() const;
|
||||
std::tuple<Frame const&, Palette const&> decodeNext();
|
||||
|
||||
|
||||
~Movie();
|
||||
|
||||
bool hasAudio() const;
|
||||
Audio getNextAudio();
|
||||
@@ -44,12 +45,23 @@ public:
|
||||
Movie(std::vector<Shot>::const_iterator shotIt,
|
||||
std::vector<Shot>::const_iterator shotEnd,
|
||||
MveDecoder const& mve);
|
||||
|
||||
|
||||
void parseVGA(IffFile::Object const& vga);
|
||||
|
||||
std::vector<Shot>::const_iterator shotIt_, shotEnd_;
|
||||
VGAVec::const_iterator frameIt_;
|
||||
AUDIVec::const_iterator audioIt_;
|
||||
Frame frame_;
|
||||
MveDecoder const& mve_;
|
||||
|
||||
// Buffers for frame decoding
|
||||
std::vector<uint8_t> pixelBuf_;
|
||||
Frame frameBuf_;
|
||||
std::vector<char> commandBuf_;
|
||||
|
||||
// Buffer usage stats
|
||||
size_t maxPixel_, maxCommand_;
|
||||
|
||||
friend class MveDecoder;
|
||||
};
|
||||
|
||||
@@ -63,8 +75,6 @@ private:
|
||||
std::vector<Palette> palts_;
|
||||
std::vector<std::vector<Shot> > branches_;
|
||||
|
||||
Frame parseVGA(IffFile::Object const& vga, Frame const* prev) const;
|
||||
|
||||
friend struct AudioCBData;
|
||||
};
|
||||
|
||||
|
||||
@@ -171,8 +171,8 @@ TreFile::TreFile(uint8_t const* base, size_t length)
|
||||
crc, table3Ptr);
|
||||
continue;
|
||||
}
|
||||
printf("Table1->Table2 pointer resolved: Table 2 %.8x -> Table 3 %.8x\n",
|
||||
table3Ptr, it->second);
|
||||
// printf("Table1->Table2 pointer resolved: Table 2 %.8x -> Table 3 %.8x\n",
|
||||
// table3Ptr, it->second);
|
||||
table3Ptr = it->second;
|
||||
}
|
||||
|
||||
|
||||
14
compiler.hh
Normal file
14
compiler.hh
Normal file
@@ -0,0 +1,14 @@
|
||||
#ifndef WC3RE_COMPILER_HH__
|
||||
#define WC3RE_COMPILER_HH__
|
||||
|
||||
#ifdef __GNUG__
|
||||
#define RESTRICT __restrict__
|
||||
#else
|
||||
#ifdef __MSC_VER
|
||||
#define RESTRICT __restrict
|
||||
#else
|
||||
#define RESTRICT
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -1,4 +1,5 @@
|
||||
#include "common.hh"
|
||||
#include "compiler.hh"
|
||||
|
||||
#include "decompress.hh"
|
||||
|
||||
@@ -80,3 +81,92 @@ std::vector<uint8_t> decompressLZ(uint8_t const* data, size_t len)
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
size_t decompressLZInto(uint8_t const* RESTRICT data, size_t len, uint8_t * RESTRICT out, size_t maxOut)
|
||||
{
|
||||
size_t pos = 0, outPos = 0;
|
||||
while (pos < len) {
|
||||
uint8_t b = *(data+pos++);
|
||||
if (!((b&0xe0)==0xe0)) {
|
||||
unsigned size = 0, replSize = 0;
|
||||
unsigned replOfs = 0;
|
||||
if (!(b&0x80)) {
|
||||
if (pos >= len)
|
||||
throw FormatException{"Compressed stream overrun"};
|
||||
|
||||
uint8_t ofs = *(data+pos++);
|
||||
size = b&0x3;
|
||||
|
||||
replSize = ((b&0x1c)>>2) + 3;
|
||||
replOfs = ((b&0x60)<<3)+ofs+1;
|
||||
} else if (!(b&0x40)) {
|
||||
if (pos+1 >= len)
|
||||
throw FormatException{"Compressed stream overrun"};
|
||||
|
||||
uint8_t b1 = *(data+pos++);
|
||||
uint8_t b2 = *(data+pos++);
|
||||
|
||||
size = (b1&0xc0)>>6;
|
||||
|
||||
replSize = (b&0x3f)+4;
|
||||
replOfs = ((b1&0x3f)<<8)+b2+1;
|
||||
} else if (!(b&0x20)) {
|
||||
if (pos+2 >= len)
|
||||
throw FormatException{"Compressed stream overrun"};
|
||||
|
||||
uint8_t b1 = *(data+pos++);
|
||||
uint8_t b2 = *(data+pos++);
|
||||
uint8_t b3 = *(data+pos++);
|
||||
|
||||
size = b&0x3;
|
||||
|
||||
replSize = b3+5+((b&0xc)<<6);
|
||||
replOfs = ((b&0x10)<<12)+1+(b1<<8)+b2;
|
||||
}
|
||||
if (pos+size >= len)
|
||||
throw FormatException{"Compressed stream overrun"};
|
||||
if (outPos+size > maxOut)
|
||||
throw Exception{"Output buffer overrun"};
|
||||
std::copy(data+pos, data+pos+size,
|
||||
out+outPos);
|
||||
pos += size;
|
||||
outPos += size;
|
||||
|
||||
if (replOfs > outPos)
|
||||
throw FormatException{"Replication offset exceeds buffer"};
|
||||
if (outPos+replSize > maxOut)
|
||||
throw Exception{"Output buffer overrun"};
|
||||
unsigned start = outPos-replOfs;
|
||||
for (unsigned i = 0;i < replSize;++i)
|
||||
out[outPos++] = out[start+i];
|
||||
} else {
|
||||
unsigned size = (b&0x1f)*4+4;
|
||||
if (size > 0x70) {
|
||||
if (pos+(b&0x3) > len)
|
||||
throw FormatException{"Compressed stream overrun"};
|
||||
if (outPos+(b&0x03) > maxOut)
|
||||
throw Exception{"Output buffer overrun"};
|
||||
std::copy(data+pos, data+pos+(b&0x3),
|
||||
out+outPos);
|
||||
pos += (b&0x3);
|
||||
outPos += (b&0x3);
|
||||
#ifndef NDEBUG
|
||||
if (pos < len)
|
||||
printf("%lu unparsed bytes in compressed data\n", len-pos);
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
|
||||
if (pos+size > len)
|
||||
throw FormatException{"Compressed stream overrun"};
|
||||
if (outPos+size > maxOut)
|
||||
throw Exception{"Output buffer overrun"};
|
||||
std::copy(data+pos, data+pos+size,
|
||||
out+outPos);
|
||||
pos += size;
|
||||
outPos += size;
|
||||
}
|
||||
}
|
||||
|
||||
return outPos;
|
||||
}
|
||||
|
||||
@@ -9,6 +9,11 @@
|
||||
|
||||
/* LZ77-like format consisting of codes specifing copies from the input stream
|
||||
and/or replication of previously output data */
|
||||
|
||||
/* Decompress compressed data in 'data', return decompressed data */
|
||||
std::vector<uint8_t> decompressLZ(uint8_t const* data, size_t len);
|
||||
/* Decompress compressed data in 'data' into pre-allocated buffer 'out' of size 'maxOut',
|
||||
return size of decompressed data */
|
||||
size_t decompressLZInto(uint8_t const* data, size_t len, uint8_t * out, size_t maxOut);
|
||||
|
||||
#endif
|
||||
|
||||
@@ -3,12 +3,13 @@
|
||||
#include "GSMvePlay.hh"
|
||||
#include "render/Overlay.hh"
|
||||
#include "render/Renderer.hh"
|
||||
#include "render/AudioStream.hh"
|
||||
|
||||
namespace game {
|
||||
GSMvePlay::GSMvePlay(render::Renderer& renderer, MveDecoder::Movie& movie)
|
||||
: GameState(renderer), movie_(movie),
|
||||
overlay_(nullptr), delta_(0),
|
||||
nextFT_(1000/15.0f), frameRGB_()
|
||||
nextFT_(1000/15.0f), frameRGBA_(), decoded_(false), firstFrame_(true)
|
||||
{
|
||||
int scrWidth = renderer_.getWidth(), scrHeight = renderer_.getHeight();
|
||||
const float mvePAR = 1.2f;
|
||||
@@ -30,12 +31,13 @@ namespace game {
|
||||
|
||||
overlay_ = std::make_unique<render::Overlay>(renderer_, scrWidth, scrHeight, left, top,
|
||||
mveWidth, mveHeight);
|
||||
|
||||
audio_ = std::make_unique<render::AudioStream>();
|
||||
|
||||
auto align4Width = (mveWidth%4)?(mveWidth+(4-(mveWidth%4))):mveWidth;
|
||||
frameRGB_.resize(align4Width*mveHeight*3);
|
||||
|
||||
frameRGBA_.resize(mveWidth*mveHeight*4);
|
||||
|
||||
decode_();
|
||||
overlay_->setContentRGB8(frameRGB_.data());
|
||||
overlay_->setContentBGRA8(frameRGBA_.data());
|
||||
decode_();
|
||||
}
|
||||
|
||||
@@ -45,6 +47,10 @@ namespace game {
|
||||
|
||||
void GSMvePlay::decode_()
|
||||
{
|
||||
if (movie_.hasAudio()) {
|
||||
auto samples = movie_.getNextAudio();
|
||||
audio_->queueSamples(1, 22050, samples.data(), samples.size()*2);
|
||||
}
|
||||
if (movie_.hasNext()) {
|
||||
auto dec = movie_.decodeNext();
|
||||
auto& frame = std::get<0>(dec);
|
||||
@@ -55,29 +61,46 @@ namespace game {
|
||||
for (unsigned y = 0;y < height;++y) {
|
||||
for (unsigned x = 0;x < width;++x) {
|
||||
auto idx = frame[y*width+x];
|
||||
std::copy(&palette[idx*3], &palette[idx*3+3],
|
||||
&frameRGB_[i]);
|
||||
i += 3;
|
||||
|
||||
frameRGBA_[i] = palette[idx*3+2];
|
||||
frameRGBA_[i+1] = palette[idx*3+1];
|
||||
frameRGBA_[i+2] = palette[idx*3];
|
||||
frameRGBA_[i+3] = 255u;
|
||||
|
||||
i += 4;
|
||||
}
|
||||
if ((i%4) != 0)
|
||||
i += 4-(i%4);
|
||||
}
|
||||
} else
|
||||
frameRGB_.resize(0);
|
||||
frameRGBA_.resize(0);
|
||||
}
|
||||
|
||||
bool GSMvePlay::handleEvent(SDL_Event& event)
|
||||
{
|
||||
switch (event.type) {
|
||||
case SDL_KEYDOWN:
|
||||
if (event.key.keysym.sym == SDLK_ESCAPE) {
|
||||
renderer_.popGS();
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void GSMvePlay::draw(unsigned delta_ms)
|
||||
{
|
||||
const float frameTime = 1000/15.0f;
|
||||
if (firstFrame_) {
|
||||
firstFrame_ = false;
|
||||
audio_->play();
|
||||
}
|
||||
|
||||
delta_ += delta_ms;
|
||||
if (delta_ >= nextFT_) {
|
||||
if (!frameRGB_.size()) {
|
||||
if (!decoded_)
|
||||
decode_();
|
||||
if (!frameRGBA_.size()) {
|
||||
renderer_.popGS();
|
||||
return;
|
||||
}
|
||||
@@ -89,11 +112,14 @@ namespace game {
|
||||
|
||||
delta_ = 0;
|
||||
|
||||
overlay_->setContentRGB8(frameRGB_.data());
|
||||
overlay_->setContentBGRA8(frameRGBA_.data());
|
||||
|
||||
// Decode next frame
|
||||
decoded_ = false;
|
||||
} else if (!decoded_) {
|
||||
decode_();
|
||||
decoded_ = true;
|
||||
}
|
||||
|
||||
overlay_->draw();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
class MveDecoder;
|
||||
namespace render {
|
||||
class Overlay;
|
||||
class AudioStream;
|
||||
}
|
||||
|
||||
namespace game {
|
||||
@@ -20,9 +21,11 @@ namespace game {
|
||||
private:
|
||||
MveDecoder::Movie& movie_;
|
||||
std::unique_ptr<render::Overlay> overlay_;
|
||||
std::unique_ptr<render::AudioStream> audio_;
|
||||
unsigned delta_;
|
||||
float nextFT_;
|
||||
std::vector<uint8_t> frameRGB_;
|
||||
std::vector<uint8_t> frameRGBA_;
|
||||
bool decoded_, firstFrame_;
|
||||
|
||||
void decode_();
|
||||
};
|
||||
|
||||
33
render/AlResource.cc
Normal file
33
render/AlResource.cc
Normal file
@@ -0,0 +1,33 @@
|
||||
#include "AlResource.hh"
|
||||
|
||||
namespace render {
|
||||
void ALCDeviceDeleter::operator() (ALCdevice *dev) const
|
||||
{
|
||||
alcCloseDevice(dev);
|
||||
}
|
||||
|
||||
void ALCContextDeleter::operator() (ALCcontext *ctx) const
|
||||
{
|
||||
alcDestroyContext(ctx);
|
||||
}
|
||||
|
||||
void ALSourceDeleter::operator() (ALuint src) const
|
||||
{
|
||||
alDeleteSources(1, &src);
|
||||
}
|
||||
|
||||
void ALSourceDeleter::operator() (ALsizei count, ALuint src[]) const
|
||||
{
|
||||
alDeleteSources(count, src);
|
||||
}
|
||||
|
||||
void ALBufferDeleter::operator() (ALuint buf) const
|
||||
{
|
||||
alDeleteBuffers(1, &buf);
|
||||
}
|
||||
|
||||
void ALBufferDeleter::operator() (ALsizei count, ALuint buf[]) const
|
||||
{
|
||||
alDeleteBuffers(count, buf);
|
||||
}
|
||||
}
|
||||
39
render/AlResource.hh
Normal file
39
render/AlResource.hh
Normal file
@@ -0,0 +1,39 @@
|
||||
#ifndef WC3RE_RENDER_ALRESOURCE_HH__
|
||||
#define WC3RE_RENDER_ALRESOURCE_HH__
|
||||
|
||||
#include <AL/al.h>
|
||||
#include <AL/alc.h>
|
||||
#include <memory>
|
||||
|
||||
#include "GlResource.hh"
|
||||
|
||||
namespace render {
|
||||
struct ALCDeviceDeleter {
|
||||
void operator() (ALCdevice *dev) const;
|
||||
};
|
||||
|
||||
struct ALCContextDeleter {
|
||||
void operator() (ALCcontext *ctx) const;
|
||||
};
|
||||
|
||||
struct ALSourceDeleter {
|
||||
void operator() (ALuint src) const;
|
||||
void operator() (ALsizei count, ALuint src[]) const;
|
||||
};
|
||||
|
||||
struct ALBufferDeleter {
|
||||
void operator() (ALuint buf) const;
|
||||
void operator() (ALsizei count, ALuint buf[]) const;
|
||||
};
|
||||
|
||||
using ALCDeviceUPtr = std::unique_ptr<ALCdevice, ALCDeviceDeleter>;
|
||||
using ALCContextUPtr = std::unique_ptr<ALCcontext, ALCContextDeleter>;
|
||||
|
||||
template<class Deleter>
|
||||
using AlResource = GlResource<Deleter, ALuint>;
|
||||
|
||||
using ALSourceResource = AlResource<ALSourceDeleter>;
|
||||
using ALBufferResource = AlResource<ALBufferDeleter>;
|
||||
}
|
||||
|
||||
#endif
|
||||
62
render/AudioStream.cc
Normal file
62
render/AudioStream.cc
Normal file
@@ -0,0 +1,62 @@
|
||||
#include "AudioStream.hh"
|
||||
#include "exceptions.hh"
|
||||
|
||||
namespace render {
|
||||
AudioStream::AudioStream()
|
||||
{
|
||||
alGenSources(1, &alSource_.get());
|
||||
|
||||
alSourcef(alSource_, AL_MIN_GAIN, 1.0f);
|
||||
}
|
||||
|
||||
AudioStream::~AudioStream()
|
||||
{
|
||||
#ifndef NDEBUG
|
||||
printf("%zu buffers at destruction\n", alBufs_.size());
|
||||
#endif
|
||||
}
|
||||
|
||||
void AudioStream::play()
|
||||
{
|
||||
alSourcePlay(alSource_);
|
||||
}
|
||||
|
||||
void AudioStream::pause()
|
||||
{
|
||||
alSourceStop(alSource_);
|
||||
}
|
||||
|
||||
bool AudioStream::isPlaying() const
|
||||
{
|
||||
ALenum state;
|
||||
alGetSourcei(alSource_, AL_SOURCE_STATE, &state);
|
||||
return (state == AL_PLAYING);
|
||||
}
|
||||
void AudioStream::queueSamples(unsigned channels, unsigned freq, int16_t const* data, size_t len)
|
||||
{
|
||||
// Reuse a processed buffer if possible, or create a new one
|
||||
ALint doneBufs;
|
||||
alGetSourcei(alSource_, AL_BUFFERS_PROCESSED, &doneBufs);
|
||||
ALuint buf;
|
||||
if (doneBufs > 0)
|
||||
alSourceUnqueueBuffers(alSource_, 1, &buf);
|
||||
else {
|
||||
alGenBuffers(1, &buf);
|
||||
alBufs_.push_back(buf);
|
||||
}
|
||||
|
||||
// Fill buffer with data
|
||||
ALenum format;
|
||||
if (channels == 1)
|
||||
format = AL_FORMAT_MONO16;
|
||||
else if (channels == 2)
|
||||
format = AL_FORMAT_STEREO16;
|
||||
else
|
||||
throw Exception{"More than 2 channels not supported"};
|
||||
|
||||
alBufferData(buf, format, data, len, freq);
|
||||
|
||||
// Enqueue buffer
|
||||
alSourceQueueBuffers(alSource_, 1, &buf);
|
||||
}
|
||||
}
|
||||
26
render/AudioStream.hh
Normal file
26
render/AudioStream.hh
Normal file
@@ -0,0 +1,26 @@
|
||||
#ifndef WC3RE_RENDER_AUDIOSTREAM_HH__
|
||||
#define WC3RE_RENDER_AUDIOSTREAM_HH__
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include "AlResource.hh"
|
||||
|
||||
namespace render {
|
||||
class AudioStream {
|
||||
public:
|
||||
AudioStream();
|
||||
~AudioStream();
|
||||
|
||||
void play();
|
||||
void pause();
|
||||
bool isPlaying() const;
|
||||
|
||||
void queueSamples(unsigned channels, unsigned freq, int16_t const* data, size_t len);
|
||||
|
||||
private:
|
||||
ALSourceResource alSource_;
|
||||
std::vector<ALBufferResource> alBufs_;
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -71,15 +71,15 @@ namespace render {
|
||||
glDrawArrays(GL_TRIANGLES, 0, 6);
|
||||
}
|
||||
|
||||
void Overlay::clear()
|
||||
{
|
||||
if (SDL_GL_ExtensionSupported("GL_ARB_clear_texture"))
|
||||
glClearTexImage(texture_, 0, GL_BGRA, GL_UNSIGNED_BYTE, nullptr);
|
||||
else {
|
||||
std::vector<uint8_t> zeros(intWidth_*intHeight_*4, 0u);
|
||||
setContentBGRA8(zeros.data());
|
||||
}
|
||||
}
|
||||
// void Overlay::clear()
|
||||
// {
|
||||
// if (SDL_GL_ExtensionSupported("GL_ARB_clear_texture"))
|
||||
// glClearTexImage(texture_, 0, GL_BGRA, GL_UNSIGNED_BYTE, nullptr);
|
||||
// else {
|
||||
// std::vector<uint8_t> zeros(intWidth_*intHeight_*4, 0u);
|
||||
// setContentBGRA8(zeros.data());
|
||||
// }
|
||||
// }
|
||||
|
||||
void Overlay::setContent(SDL_Surface *content)
|
||||
{
|
||||
@@ -91,6 +91,7 @@ namespace render {
|
||||
glBindTexture(GL_TEXTURE_2D, texture_);
|
||||
|
||||
if (content->format->format != SDL_PIXELFORMAT_ARGB8888) {
|
||||
printf("Warning: Format conversion in Overlay::setContent\n");
|
||||
SDLSurfaceUPtr tmpSurf(SDL_ConvertSurfaceFormat(content, SDL_PIXELFORMAT_ARGB8888, 0));
|
||||
if (!tmpSurf)
|
||||
throw SDLException{};
|
||||
|
||||
@@ -18,7 +18,7 @@ namespace render {
|
||||
|
||||
void draw() override;
|
||||
|
||||
void clear();
|
||||
// void clear();
|
||||
|
||||
void setContent(SDL_Surface *content);
|
||||
void setContentRGB8(void *data);
|
||||
|
||||
@@ -61,6 +61,7 @@ namespace render {
|
||||
Renderer::Renderer()
|
||||
: sdlInit_(), ttfInit_(), window_(), context_()
|
||||
{
|
||||
// Initialize OpenGL
|
||||
glbinding::Binding::initialize();
|
||||
|
||||
SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 16);
|
||||
@@ -98,6 +99,21 @@ namespace render {
|
||||
glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
|
||||
glViewport(0, 0, width_, height_);
|
||||
|
||||
|
||||
// Initialize OpenAL
|
||||
alcDevice_.reset(alcOpenDevice(nullptr));
|
||||
if (!alcDevice_)
|
||||
throw Exception{"OpenAL init failed"};
|
||||
alcContext_.reset(alcCreateContext(alcDevice_.get(), nullptr));
|
||||
if (!alcContext_)
|
||||
throw Exception{"OpenAL init failed"};
|
||||
alcMakeContextCurrent(alcContext_.get());
|
||||
|
||||
alListener3f(AL_POSITION, 0.0f, 0.0f, 0.0f);
|
||||
alListener3f(AL_VELOCITY, 0.0f, 0.0f, 0.0f);
|
||||
alListener3f(AL_ORIENTATION, 0.0f, 0.0f, -1.0f);
|
||||
|
||||
// Setup framerate display
|
||||
fpsFont_.reset(TTF_OpenFont("DejaVuSans.ttf", 10));
|
||||
if (!fpsFont_)
|
||||
throw TTFException{};
|
||||
@@ -110,10 +126,26 @@ namespace render {
|
||||
SDLSurfaceUPtr fpsSurf(TTF_RenderUTF8_Blended(fpsFont_.get(), "0 FPS", white));
|
||||
if (!fpsSurf)
|
||||
throw TTFException{};
|
||||
|
||||
const unsigned fpsWidth = fpsSurf->w*2;
|
||||
const unsigned fpsHeight = fpsSurf->h;
|
||||
fpsOverlay_ = std::make_unique<Overlay>(*this, fpsWidth, fpsHeight, 0, 0, fpsWidth, fpsHeight);
|
||||
{
|
||||
int bpp;
|
||||
uint32_t rmask, gmask, bmask, amask;
|
||||
if (!SDL_PixelFormatEnumToMasks(SDL_PIXELFORMAT_ARGB8888,
|
||||
&bpp,
|
||||
&rmask, &gmask, &bmask, &amask))
|
||||
throw SDLException{};
|
||||
|
||||
fpsOverlaySurf_.reset(SDL_CreateRGBSurface(0, fpsWidth, fpsHeight, bpp,
|
||||
rmask, gmask, bmask, amask));
|
||||
}
|
||||
|
||||
fpsOverlay_ = std::make_unique<Overlay>(*this, fpsSurf->w*2, fpsSurf->h, 0, 0, fpsSurf->w*2, fpsSurf->h);
|
||||
fpsOverlay_->clear();
|
||||
fpsOverlay_->setContent(fpsSurf.get());
|
||||
if (SDL_BlitSurface(fpsSurf.get(), nullptr, fpsOverlaySurf_.get(), nullptr) != 0)
|
||||
throw SDLException{};
|
||||
|
||||
fpsOverlay_->setContent(fpsOverlaySurf_.get());
|
||||
}
|
||||
|
||||
Renderer::~Renderer()
|
||||
@@ -129,7 +161,8 @@ namespace render {
|
||||
|
||||
while (!close && gamestates_.size()) {
|
||||
SDL_Event event;
|
||||
while (SDL_PollEvent(&event)) {
|
||||
bool changeGS = false;
|
||||
while (SDL_PollEvent(&event) && !changeGS) {
|
||||
switch(event.type) {
|
||||
case SDL_KEYDOWN:
|
||||
if (event.key.keysym.sym == SDLK_q) {
|
||||
@@ -144,10 +177,10 @@ namespace render {
|
||||
}
|
||||
break;
|
||||
}
|
||||
if (gamestates_.back()->handleEvent(event))
|
||||
continue;
|
||||
break;
|
||||
changeGS = gamestates_.back()->handleEvent(event);
|
||||
}
|
||||
if (changeGS)
|
||||
continue;
|
||||
|
||||
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
|
||||
|
||||
@@ -159,12 +192,18 @@ namespace render {
|
||||
if (fpsTime+delta > 1000) {
|
||||
if (fpsCount+1 != lastFps) {
|
||||
std::string fpsText{std::to_string(fpsCount+1) + " FPS"};
|
||||
|
||||
SDLSurfaceUPtr fpsSurf(TTF_RenderUTF8_Blended(fpsFont_.get(), fpsText.c_str(),
|
||||
SDL_Color({255, 255, 255, 255})));
|
||||
if (!fpsSurf)
|
||||
throw TTFException{};
|
||||
fpsOverlay_->clear();
|
||||
fpsOverlay_->setContent(fpsSurf.get());
|
||||
|
||||
if ((SDL_FillRect(fpsOverlaySurf_.get(), nullptr, 0x00000000u) != 0) ||
|
||||
(SDL_BlitSurface(fpsSurf.get(), nullptr, fpsOverlaySurf_.get(), nullptr) != 0))
|
||||
throw SDLException{};
|
||||
|
||||
fpsOverlay_->setContent(fpsOverlaySurf_.get());
|
||||
|
||||
lastFps = fpsCount+1;
|
||||
}
|
||||
fpsCount = 0;
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
#include <memory>
|
||||
|
||||
#include "sdlutil.hh"
|
||||
#include "AlResource.hh"
|
||||
|
||||
namespace game {
|
||||
class GameState;
|
||||
@@ -31,8 +32,14 @@ namespace render {
|
||||
SDLGLContext context_;
|
||||
std::vector<std::unique_ptr<game::GameState> > gamestates_;
|
||||
int width_, height_;
|
||||
|
||||
ALCDeviceUPtr alcDevice_;
|
||||
ALCContextUPtr alcContext_;
|
||||
|
||||
TTFFontUPtr fpsFont_;
|
||||
std::unique_ptr<Overlay> fpsOverlay_;
|
||||
SDLSurfaceUPtr fpsOverlaySurf_;
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -101,7 +101,7 @@ namespace render {
|
||||
// RAII wrapper for SDL_Init
|
||||
class SDLInit {
|
||||
public:
|
||||
SDLInit(uint32_t flags = SDL_INIT_VIDEO | SDL_INIT_AUDIO) {
|
||||
SDLInit(uint32_t flags = SDL_INIT_VIDEO) {
|
||||
if (SDL_Init(flags) < 0)
|
||||
throw SDLException{};
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user