CMoviePlayer initial implementation

This commit is contained in:
Jack Andersen 2016-03-06 17:12:32 -10:00
parent 9cfe73d278
commit 6e160560fa
30 changed files with 1706 additions and 208 deletions

View File

@ -41,11 +41,12 @@ target_link_libraries(urde
RuntimeCommonInput
RuntimeCommonParticle
RuntimeCommonGraphics
RuntimeCommonAudio
RuntimeCommon
DNAMP3 DNAMP2 DNAMP1
DNACommon specter specter-fonts freetype ${DATA_SPEC_LIBS}
hecl-database hecl-backend hecl-frontend hecl-blender hecl-runtime hecl-common athena-core nod
logvisor athena-libyaml boo ${PNG_LIB} squish xxhash zeus
logvisor athena-libyaml boo ${PNG_LIB} libjpeg-turbo squish xxhash zeus
${ZLIB_LIBRARIES} ${LZO_LIB}
${BOO_SYS_LIBS})

View File

@ -47,9 +47,13 @@ bool ProjectManager::newProject(const hecl::SystemString& path)
return false;
}
m_vm.ProjectChanged(*m_proj);
m_vm.SetupEditorView();
saveProject();
m_vm.m_mainWindow->setTitle(m_proj->getProjectRootPath().getLastComponent());
hecl::SystemString windowTitle(m_proj->getProjectRootPath().getLastComponent());
windowTitle += _S(" - URDE");
m_vm.m_mainWindow->setTitle(windowTitle.c_str());
m_vm.DismissSplash();
m_vm.FadeInEditors();
@ -96,11 +100,17 @@ bool ProjectManager::openProject(const hecl::SystemString& path)
}
fclose(fp);
m_vm.ProjectChanged(*m_proj);
m_vm.SetupEditorView(r);
IndexMP1Resources();
m_vm.BuildTestPART(m_objStore);
m_vm.m_mainWindow->setTitle(m_proj->getProjectRootPath().getLastComponent());
{
hecl::SystemString windowTitle(m_proj->getProjectRootPath().getLastComponent());
windowTitle += _S(" - URDE");
m_vm.m_mainWindow->setTitle(windowTitle.c_str());
}
m_vm.DismissSplash();
m_vm.FadeInEditors();
@ -109,12 +119,15 @@ bool ProjectManager::openProject(const hecl::SystemString& path)
return true;
makeDefault:
m_vm.ProjectChanged(*m_proj);
m_vm.SetupEditorView();
saveProject();
hecl::SystemString windowTitle(m_proj->getProjectRootPath().getLastComponent());
windowTitle += _S(" - URDE");
m_vm.m_mainWindow->setTitle(windowTitle.c_str());
{
hecl::SystemString windowTitle(m_proj->getProjectRootPath().getLastComponent());
windowTitle += _S(" - URDE");
m_vm.m_mainWindow->setTitle(windowTitle.c_str());
}
m_vm.DismissSplash();
m_vm.FadeInEditors();
return true;

View File

@ -21,13 +21,17 @@ namespace urde
void ViewManager::BuildTestPART(urde::IObjectStore& objStore)
{
//m_partGenDesc = objStore.GetObj({hecl::FOURCC('PART'), 0x972A5CD2});
/*
m_partGenDesc = objStore.GetObj("BusterSparks");
m_partGen.reset(new urde::CElementGen(m_partGenDesc,
urde::CElementGen::EModelOrientationType::Normal,
urde::CElementGen::EOptionalSystemFlags::None));
m_partGen->SetGlobalScale({5.f, 5.f, 5.f});
m_particleView.reset(new ParticleView(*this, m_viewResources, *m_rootView));
m_lineRenderer.reset(new urde::CLineRenderer(urde::CLineRenderer::EPrimitiveMode::LineStrip, 4, nullptr, true));
*/
m_particleView.reset(new ParticleView(*this, m_viewResources, *m_rootView));
hecl::ProjectPath thpPath(m_projManager.project()->getProjectWorkingPath(), _S("out/MP1/Video/creditBG.thp"));
m_moviePlayer.reset(new CMoviePlayer(thpPath.getAbsolutePathUTF8().c_str(), 0.f, true, true));
//m_rootView->accessContentViews().clear();
m_rootView->accessContentViews().push_back(m_particleView.get());
@ -98,6 +102,12 @@ void ViewManager::RootSpaceViewBuilt(specter::View *view)
m_rootView->updateSize();
}
void ViewManager::ProjectChanged(hecl::Database::Project& proj)
{
CDvdFile::Shutdown();
CDvdFile::Initialize(hecl::ProjectPath(proj.getProjectWorkingPath(), _S("out/MP1")));
}
void ViewManager::SetupEditorView()
{
m_rootSpace.reset(new RootSpace(*this));
@ -224,9 +234,10 @@ void ViewManager::init(boo::IApplication* app)
m_mainWindow->setWaitCursor(false);
urde::CGraphics::InitializeBoo(gf, m_mainWindow->getCommandQueue(), root->renderTex());
urde::CElementGen::Initialize();
urde::CLineRenderer::Initialize();
CGraphics::InitializeBoo(gf, m_mainWindow->getCommandQueue(), root->renderTex());
CElementGen::Initialize();
CMoviePlayer::Initialize();
CLineRenderer::Initialize();
}
bool ViewManager::proc()
@ -277,8 +288,9 @@ bool ViewManager::proc()
void ViewManager::stop()
{
urde::CElementGen::Shutdown();
urde::CLineRenderer::Shutdown();
CElementGen::Shutdown();
CMoviePlayer::Shutdown();
CLineRenderer::Shutdown();
m_iconsToken.doDestroy();
m_viewResources.destroyResData();
m_fontCache.destroyAtlases();

View File

@ -7,6 +7,7 @@
#include "Runtime/Particle/CElementGen.hpp"
#include "Runtime/Graphics/CLineRenderer.hpp"
#include "Runtime/Graphics/CMoviePlayer.hpp"
namespace urde
{
@ -47,6 +48,7 @@ class ViewManager : public specter::IViewManager
urde::TLockedToken<urde::CGenDescription> m_partGenDesc;
std::unique_ptr<urde::CElementGen> m_partGen;
std::unique_ptr<urde::CLineRenderer> m_lineRenderer;
std::unique_ptr<urde::CMoviePlayer> m_moviePlayer;
hecl::SystemString m_recentProjectsPath;
std::vector<hecl::SystemString> m_recentProjects;
@ -60,6 +62,7 @@ class ViewManager : public specter::IViewManager
specter::RootView* SetupRootView();
SplashScreen* SetupSplashView();
void RootSpaceViewBuilt(specter::View* view);
void ProjectChanged(hecl::Database::Project& proj);
void SetupEditorView();
void SetupEditorView(ConfigReader& r);
void SaveEditorView(ConfigWriter& w);

View File

@ -1,6 +1,6 @@
The MIT License
Copyright (c) 2015 PathShagged Contributors
Copyright (c) 2015-2016 URDE Contributors
Original Authors: Jack Andersen and Phillip "Antidote" Stephens
Permission is hereby granted, free of charge, to any person obtaining a copy

View File

@ -2,4 +2,6 @@ add_library(RuntimeCommonAudio
CAudioSys.hpp CAudioSys.cpp
CAudioStateWin.hpp CAudioStateWin.cpp
CSfxManager.hpp CSfxManager.cpp
CSfxHandle.hpp CSfxHandle.cpp)
CSfxHandle.hpp CSfxHandle.cpp
dsp.c dsp.h
g721.c g721.h)

92
Runtime/Audio/dsp.c Normal file
View File

@ -0,0 +1,92 @@
#include "dsp.h"
static const int NibbleToInt[16] = {0,1,2,3,4,5,6,7,-8,-7,-6,-5,-4,-3,-2,-1};
static inline short SampClamp(int val)
{
if (val < -32768) val = -32768;
if (val > 32767) val = 32767;
return val;
}
void DSPDecompressFrame(int16_t* out, const uint8_t* in,
const int16_t coefs[8][2], int16_t* prev1, int16_t* prev2,
unsigned lastSample)
{
uint8_t cIdx = (in[0]>>4) & 0xf;
int16_t factor1 = coefs[cIdx][0];
int16_t factor2 = coefs[cIdx][1];
uint8_t exp = in[0] & 0xf;
for (int s=0 ; s<14 && s<lastSample ; ++s)
{
int sampleData = (s&1)?
NibbleToInt[(in[s/2+1])&0xf]:
NibbleToInt[(in[s/2+1]>>4)&0xf];
sampleData <<= exp;
sampleData <<= 11;
sampleData += 1024;
sampleData +=
factor1 * *prev1 +
factor2 * *prev2;
sampleData >>= 11;
sampleData = SampClamp(sampleData);
out[s] = sampleData;
*prev2 = *prev1;
*prev1 = sampleData;
}
}
void DSPDecompressFrameStereoStride(int16_t* out, const uint8_t* in,
const int16_t coefs[8][2], int16_t* prev1, int16_t* prev2,
unsigned lastSample)
{
uint8_t cIdx = (in[0]>>4) & 0xf;
int16_t factor1 = coefs[cIdx][0];
int16_t factor2 = coefs[cIdx][1];
uint8_t exp = in[0] & 0xf;
for (int s=0 ; s<14 && s<lastSample ; ++s)
{
int sampleData = (s&1)?
NibbleToInt[(in[s/2+1])&0xf]:
NibbleToInt[(in[s/2+1]>>4)&0xf];
sampleData <<= exp;
sampleData <<= 11;
sampleData += 1024;
sampleData +=
factor1 * *prev1 +
factor2 * *prev2;
sampleData >>= 11;
sampleData = SampClamp(sampleData);
out[s*2] = sampleData;
*prev2 = *prev1;
*prev1 = sampleData;
}
}
void DSPDecompressFrameStereoDupe(int16_t* out, const uint8_t* in,
const int16_t coefs[8][2], int16_t* prev1, int16_t* prev2,
unsigned lastSample)
{
uint8_t cIdx = (in[0]>>4) & 0xf;
int16_t factor1 = coefs[cIdx][0];
int16_t factor2 = coefs[cIdx][1];
uint8_t exp = in[0] & 0xf;
for (int s=0 ; s<14 && s<lastSample ; ++s)
{
int sampleData = (s&1)?
NibbleToInt[(in[s/2+1])&0xf]:
NibbleToInt[(in[s/2+1]>>4)&0xf];
sampleData <<= exp;
sampleData <<= 11;
sampleData += 1024;
sampleData +=
factor1 * *prev1 +
factor2 * *prev2;
sampleData >>= 11;
sampleData = SampClamp(sampleData);
out[s*2] = sampleData;
out[s*2+1] = sampleData;
*prev2 = *prev1;
*prev1 = sampleData;
}
}

24
Runtime/Audio/dsp.h Normal file
View File

@ -0,0 +1,24 @@
#ifndef _DSP_h
#define _DSP_h
#ifdef __cplusplus
extern "C" {
#endif
#include <stdint.h>
void DSPDecompressFrame(int16_t* out, const uint8_t* in,
const int16_t coefs[8][2], int16_t* prev1, int16_t* prev2,
unsigned lastSample);
void DSPDecompressFrameStereoStride(int16_t* out, const uint8_t* in,
const int16_t coefs[8][2], int16_t* prev1, int16_t* prev2,
unsigned lastSample);
void DSPDecompressFrameStereoDupe(int16_t* out, const uint8_t* in,
const int16_t coefs[8][2], int16_t* prev1, int16_t* prev2,
unsigned lastSample);
#ifdef __cplusplus
}
#endif
#endif // _DSP_h

457
Runtime/Audio/g721.c Normal file
View File

@ -0,0 +1,457 @@
/* G.721 decoder, from Sun's public domain CCITT-ADPCM sources,
* retrieved from ftp://ftp.cwi.nl/pub/audio/ccitt-adpcm.tar.gz
*
* For reference, here's the original license:
*
* This source code is a product of Sun Microsystems, Inc. and is provided
* for unrestricted use. Users may copy or modify this source code without
* charge.
*
* SUN SOURCE CODE IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING
* THE WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR
* PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE.
*
* Sun source code is provided with no support and without any obligation on
* the part of Sun Microsystems, Inc. to assist in its use, correction,
* modification or enhancement.
*
* SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE
* INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY THIS SOFTWARE
* OR ANY PART THEREOF.
*
* In no event will Sun Microsystems, Inc. be liable for any lost revenue
* or profits or other special, indirect and consequential damages, even if
* Sun has been advised of the possibility of such damages.
*
* Sun Microsystems, Inc.
* 2550 Garcia Avenue
* Mountain View, California 94043
*
*/
#include <stdlib.h>
#include "g721.h"
static short power2[15] = {1, 2, 4, 8, 0x10, 0x20, 0x40, 0x80,
0x100, 0x200, 0x400, 0x800, 0x1000, 0x2000, 0x4000};
/*
* quan()
*
* quantizes the input val against the table of size short integers.
* It returns i if table[i - 1] <= val < table[i].
*
* Using linear search for simple coding.
*/
static int
quan(
int val,
short *table,
int size)
{
int i;
for (i = 0; i < size; i++)
if (val < *table++)
break;
return (i);
}
/*
* fmult()
*
* returns the integer product of the 14-bit integer "an" and
* "floating point" representation (4-bit exponent, 6-bit mantessa) "srn".
*/
static int
fmult(
int an,
int srn)
{
short anmag, anexp, anmant;
short wanexp, wanmant;
short retval;
anmag = (an > 0) ? an : ((-an) & 0x1FFF);
anexp = quan(anmag, power2, 15) - 6;
anmant = (anmag == 0) ? 32 :
(anexp >= 0) ? anmag >> anexp : anmag << -anexp;
wanexp = anexp + ((srn >> 6) & 0xF) - 13;
wanmant = (anmant * (srn & 077) + 0x30) >> 4;
retval = (wanexp >= 0) ? ((wanmant << wanexp) & 0x7FFF) :
(wanmant >> -wanexp);
return (((an ^ srn) < 0) ? -retval : retval);
}
/*
* g72x_init_state()
*
* This routine initializes and/or resets the g72x_state structure
* pointed to by 'state_ptr'.
* All the initial state values are specified in the CCITT G.721 document.
*/
void
g72x_init_state(struct g72x_state *state_ptr)
{
int cnta;
state_ptr->yl = 34816;
state_ptr->yu = 544;
state_ptr->dms = 0;
state_ptr->dml = 0;
state_ptr->ap = 0;
for (cnta = 0; cnta < 2; cnta++) {
state_ptr->a[cnta] = 0;
state_ptr->pk[cnta] = 0;
state_ptr->sr[cnta] = 32;
}
for (cnta = 0; cnta < 6; cnta++) {
state_ptr->b[cnta] = 0;
state_ptr->dq[cnta] = 32;
}
state_ptr->td = 0;
}
/*
* predictor_zero()
*
* computes the estimated signal from 6-zero predictor.
*
*/
static int
predictor_zero(
struct g72x_state *state_ptr)
{
int i;
int sezi;
sezi = fmult(state_ptr->b[0] >> 2, state_ptr->dq[0]);
for (i = 1; i < 6; i++) /* ACCUM */
sezi += fmult(state_ptr->b[i] >> 2, state_ptr->dq[i]);
return (sezi);
}
/*
* predictor_pole()
*
* computes the estimated signal from 2-pole predictor.
*
*/
static int
predictor_pole(
struct g72x_state *state_ptr)
{
return (fmult(state_ptr->a[1] >> 2, state_ptr->sr[1]) +
fmult(state_ptr->a[0] >> 2, state_ptr->sr[0]));
}
/*
* step_size()
*
* computes the quantization step size of the adaptive quantizer.
*
*/
static long
step_size(
struct g72x_state *state_ptr)
{
long y;
long dif;
long al;
if (state_ptr->ap >= 256)
return (state_ptr->yu);
else {
y = state_ptr->yl >> 6;
dif = state_ptr->yu - y;
al = state_ptr->ap >> 2;
if (dif > 0)
y += (dif * al) >> 6;
else if (dif < 0)
y += (dif * al + 0x3F) >> 6;
return (y);
}
}
/*
* reconstruct()
*
* Returns reconstructed difference signal 'dq' obtained from
* codeword 'i' and quantization step size scale factor 'y'.
* Multiplication is performed in log base 2 domain as addition.
*/
static int
reconstruct(
int sign, /* 0 for non-negative value */
int dqln, /* G.72x codeword */
int y) /* Step size multiplier */
{
short dql; /* Log of 'dq' magnitude */
short dex; /* Integer part of log */
short dqt;
short dq; /* Reconstructed difference signal sample */
dql = dqln + (y >> 2); /* ADDA */
if (dql < 0) {
return ((sign) ? -0x8000 : 0);
} else { /* ANTILOG */
dex = (dql >> 7) & 15;
dqt = 128 + (dql & 127);
dq = (dqt << 7) >> (14 - dex);
return ((sign) ? (dq - 0x8000) : dq);
}
}
/*
* update()
*
* updates the state variables for each output code
*/
static void
update(
/*int code_size,*/ /* distinguish 723_40 with others */
int y, /* quantizer step size */
int wi, /* scale factor multiplier */
int fi, /* for long/short term energies */
int dq, /* quantized prediction difference */
int sr, /* reconstructed signal */
int dqsez, /* difference from 2-pole predictor */
struct g72x_state *state_ptr) /* coder state pointer */
{
int cnt;
short mag, exp; /* Adaptive predictor, FLOAT A */
short a2p; /* LIMC */
short a1ul; /* UPA1 */
short pks1; /* UPA2 */
short fa1;
char tr; /* tone/transition detector */
short ylint, thr2, dqthr;
short ylfrac, thr1;
short pk0;
pk0 = (dqsez < 0) ? 1 : 0; /* needed in updating predictor poles */
mag = dq & 0x7FFF; /* prediction difference magnitude */
/* TRANS */
ylint = state_ptr->yl >> 15; /* exponent part of yl */
ylfrac = (state_ptr->yl >> 10) & 0x1F; /* fractional part of yl */
thr1 = (32 + ylfrac) << ylint; /* threshold */
thr2 = (ylint > 9) ? 31 << 10 : thr1; /* limit thr2 to 31 << 10 */
dqthr = (thr2 + (thr2 >> 1)) >> 1; /* dqthr = 0.75 * thr2 */
if (state_ptr->td == 0) /* signal supposed voice */
tr = 0;
else if (mag <= dqthr) /* supposed data, but small mag */
tr = 0; /* treated as voice */
else /* signal is data (modem) */
tr = 1;
/*
* Quantizer scale factor adaptation.
*/
/* FUNCTW & FILTD & DELAY */
/* update non-steady state step size multiplier */
state_ptr->yu = y + ((wi - y) >> 5);
/* LIMB */
if (state_ptr->yu < 544) /* 544 <= yu <= 5120 */
state_ptr->yu = 544;
else if (state_ptr->yu > 5120)
state_ptr->yu = 5120;
/* FILTE & DELAY */
/* update steady state step size multiplier */
state_ptr->yl += state_ptr->yu + ((-state_ptr->yl) >> 6);
/*
* Adaptive predictor coefficients.
*/
if (tr == 1) { /* reset a's and b's for modem signal */
state_ptr->a[0] = 0;
state_ptr->a[1] = 0;
state_ptr->b[0] = 0;
state_ptr->b[1] = 0;
state_ptr->b[2] = 0;
state_ptr->b[3] = 0;
state_ptr->b[4] = 0;
state_ptr->b[5] = 0;
a2p=0; /* won't be used, clear warning */
} else { /* update a's and b's */
pks1 = pk0 ^ state_ptr->pk[0]; /* UPA2 */
/* update predictor pole a[1] */
a2p = state_ptr->a[1] - (state_ptr->a[1] >> 7);
if (dqsez != 0) {
fa1 = (pks1) ? state_ptr->a[0] : -state_ptr->a[0];
if (fa1 < -8191) /* a2p = function of fa1 */
a2p -= 0x100;
else if (fa1 > 8191)
a2p += 0xFF;
else
a2p += fa1 >> 5;
if (pk0 ^ state_ptr->pk[1])
/* LIMC */
if (a2p <= -12160)
a2p = -12288;
else if (a2p >= 12416)
a2p = 12288;
else
a2p -= 0x80;
else if (a2p <= -12416)
a2p = -12288;
else if (a2p >= 12160)
a2p = 12288;
else
a2p += 0x80;
}
/* TRIGB & DELAY */
state_ptr->a[1] = a2p;
/* UPA1 */
/* update predictor pole a[0] */
state_ptr->a[0] -= state_ptr->a[0] >> 8;
if (dqsez != 0) {
if (pks1 == 0)
state_ptr->a[0] += 192;
else
state_ptr->a[0] -= 192;
}
/* LIMD */
a1ul = 15360 - a2p;
if (state_ptr->a[0] < -a1ul)
state_ptr->a[0] = -a1ul;
else if (state_ptr->a[0] > a1ul)
state_ptr->a[0] = a1ul;
/* UPB : update predictor zeros b[6] */
for (cnt = 0; cnt < 6; cnt++) {
/*if (code_size == 5)*/ /* for 40Kbps G.723 */
/* state_ptr->b[cnt] -= state_ptr->b[cnt] >> 9;*/
/*else*/ /* for G.721 and 24Kbps G.723 */
state_ptr->b[cnt] -= state_ptr->b[cnt] >> 8;
if (dq & 0x7FFF) { /* XOR */
if ((dq ^ state_ptr->dq[cnt]) >= 0)
state_ptr->b[cnt] += 128;
else
state_ptr->b[cnt] -= 128;
}
}
}
for (cnt = 5; cnt > 0; cnt--)
state_ptr->dq[cnt] = state_ptr->dq[cnt-1];
/* FLOAT A : convert dq[0] to 4-bit exp, 6-bit mantissa f.p. */
if (mag == 0) {
state_ptr->dq[0] = (dq >= 0) ? 0x20 : 0xFC20;
} else {
exp = quan(mag, power2, 15);
state_ptr->dq[0] = (dq >= 0) ?
(exp << 6) + ((mag << 6) >> exp) :
(exp << 6) + ((mag << 6) >> exp) - 0x400;
}
state_ptr->sr[1] = state_ptr->sr[0];
/* FLOAT B : convert sr to 4-bit exp., 6-bit mantissa f.p. */
if (sr == 0) {
state_ptr->sr[0] = 0x20;
} else if (sr > 0) {
exp = quan(sr, power2, 15);
state_ptr->sr[0] = (exp << 6) + ((sr << 6) >> exp);
} else if (sr > -32768) {
mag = -sr;
exp = quan(mag, power2, 15);
state_ptr->sr[0] = (exp << 6) + ((mag << 6) >> exp) - 0x400;
} else
state_ptr->sr[0] = 0xFC20;
/* DELAY A */
state_ptr->pk[1] = state_ptr->pk[0];
state_ptr->pk[0] = pk0;
/* TONE */
if (tr == 1) /* this sample has been treated as data */
state_ptr->td = 0; /* next one will be treated as voice */
else if (a2p < -11776) /* small sample-to-sample correlation */
state_ptr->td = 1; /* signal may be data */
else /* signal is voice */
state_ptr->td = 0;
/*
* Adaptation speed control.
*/
state_ptr->dms += (fi - state_ptr->dms) >> 5; /* FILTA */
state_ptr->dml += (((fi << 2) - state_ptr->dml) >> 7); /* FILTB */
if (tr == 1)
state_ptr->ap = 256;
else if (y < 1536) /* SUBTC */
state_ptr->ap += (0x200 - state_ptr->ap) >> 4;
else if (state_ptr->td == 1)
state_ptr->ap += (0x200 - state_ptr->ap) >> 4;
else if (abs((state_ptr->dms << 2) - state_ptr->dml) >=
(state_ptr->dml >> 3))
state_ptr->ap += (0x200 - state_ptr->ap) >> 4;
else
state_ptr->ap += (-state_ptr->ap) >> 4;
}
/*
* Maps G.721 code word to reconstructed scale factor normalized log
* magnitude values.
*/
static short _dqlntab[16] = {-2048, 4, 135, 213, 273, 323, 373, 425,
425, 373, 323, 273, 213, 135, 4, -2048};
/* Maps G.721 code word to log of scale factor multiplier. */
static short _witab[16] = {-12, 18, 41, 64, 112, 198, 355, 1122,
1122, 355, 198, 112, 64, 41, 18, -12};
/*
* Maps G.721 code words to a set of values whose long and short
* term averages are computed and then compared to give an indication
* how stationary (steady state) the signal is.
*/
static short _fitab[16] = {0, 0, 0, 0x200, 0x200, 0x200, 0x600, 0xE00,
0xE00, 0x600, 0x200, 0x200, 0x200, 0, 0, 0};
/*
* g721_decoder()
*
* Description:
*
* Decodes a 4-bit code of G.721 encoded data of i and
* returns the resulting linear PCM, A-law or u-law value.
* return -1 for unknown out_coding value.
*/
int
g721_decoder(int i,
struct g72x_state *state_ptr)
{
short sezi, sei, sez, se; /* ACCUM */
short y; /* MIX */
short sr; /* ADDB */
short dq;
short dqsez;
i &= 0x0f; /* mask to get proper bits */
sezi = predictor_zero(state_ptr);
sez = sezi >> 1;
sei = sezi + predictor_pole(state_ptr);
se = sei >> 1; /* se = estimated signal */
y = step_size(state_ptr); /* dynamic quantizer step size */
dq = reconstruct(i & 0x08, _dqlntab[i], y); /* quantized diff. */
sr = (dq < 0) ? (se - (dq & 0x3FFF)) : se + dq; /* reconst. signal */
dqsez = sr - se + sez; /* pole prediction diff. */
update(y, _witab[i] << 5, _fitab[i], dq, sr, dqsez, state_ptr);
return (sr << 2); /* sr was 14-bit dynamic range */
}

45
Runtime/Audio/g721.h Normal file
View File

@ -0,0 +1,45 @@
#ifndef _g721_h
#define _g721_h
#ifdef __cplusplus
extern "C" {
#endif
struct g72x_state {
long yl; /* Locked or steady state step size multiplier. */
short yu; /* Unlocked or non-steady state step size multiplier. */
short dms; /* Short term energy estimate. */
short dml; /* Long term energy estimate. */
short ap; /* Linear weighting coefficient of 'yl' and 'yu'. */
short a[2]; /* Coefficients of pole portion of prediction filter. */
short b[6]; /* Coefficients of zero portion of prediction filter. */
short pk[2]; /*
* Signs of previous two samples of a partially
* reconstructed signal.
*/
short dq[6]; /*
* Previous 6 samples of the quantized difference
* signal represented in an internal floating point
* format.
*/
short sr[2]; /*
* Previous 2 samples of the quantized difference
* signal represented in an internal floating point
* format.
*/
char td; /* delayed tone detect, new in 1988 version */
};
void
g72x_init_state(struct g72x_state *state_ptr);
int
g721_decoder(int i,
struct g72x_state *state_ptr);
#ifdef __cplusplus
}
#endif
#endif

118
Runtime/CDvdFile.cpp Normal file
View File

@ -0,0 +1,118 @@
#include "CDvdFile.hpp"
#include "CDvdRequest.hpp"
namespace urde
{
hecl::ProjectPath CDvdFile::m_DvdRoot;
class CFileDvdRequest : public IDvdRequest
{
CDvdFile& m_dvdFile;
void* m_buf;
u32 m_len;
ESeekOrigin m_whence;
int m_offset;
bool m_cancel = false;
bool m_complete = false;
public:
~CFileDvdRequest()
{
PostCancelRequest();
}
void WaitUntilComplete()
{
while (!m_complete && !m_cancel)
{
std::unique_lock<std::mutex> lk(CDvdFile::m_WorkerMutex);
}
}
bool IsComplete() {return m_complete;}
void PostCancelRequest()
{
std::unique_lock<std::mutex> lk(CDvdFile::m_WorkerMutex);
m_cancel = true;
}
EMediaType GetMediaType() const
{
return EMediaType::File;
}
CFileDvdRequest(CDvdFile& file, void* buf, u32 len, ESeekOrigin whence, int off)
: m_dvdFile(file), m_buf(buf), m_len(len), m_whence(whence), m_offset(off) {}
void DoRequest()
{
if (m_cancel)
return;
if (m_whence == ESeekOrigin::Cur && m_offset == 0)
m_dvdFile.m_reader.readBytesToBuf(m_buf, m_len);
else
{
m_dvdFile.m_reader.seek(m_offset, athena::SeekOrigin(m_whence));
m_dvdFile.m_reader.readBytesToBuf(m_buf, m_len);
}
m_complete = true;
}
};
std::thread CDvdFile::m_WorkerThread;
std::mutex CDvdFile::m_WorkerMutex;
std::condition_variable CDvdFile::m_WorkerCV;
bool CDvdFile::m_WorkerRun = false;
std::vector<std::shared_ptr<IDvdRequest>> CDvdFile::m_RequestQueue;
void CDvdFile::WorkerProc()
{
while (m_WorkerRun)
{
std::unique_lock<std::mutex> lk(CDvdFile::m_WorkerMutex);
if (CDvdFile::m_RequestQueue.size())
{
std::vector<std::shared_ptr<IDvdRequest>> swapQueue;
swapQueue.swap(CDvdFile::m_RequestQueue);
lk.unlock();
for (std::shared_ptr<IDvdRequest>& req : swapQueue)
{
CFileDvdRequest& concreteReq = static_cast<CFileDvdRequest&>(*req);
concreteReq.DoRequest();
}
lk.lock();
}
m_WorkerCV.wait(lk);
}
}
std::shared_ptr<IDvdRequest> CDvdFile::AsyncSeekRead(void* buf, u32 len, ESeekOrigin whence, int off)
{
CFileDvdRequest* req = new CFileDvdRequest(*this, buf, len, whence, off);
std::shared_ptr<IDvdRequest> ret(req);
std::unique_lock<std::mutex> lk(CDvdFile::m_WorkerMutex);
m_RequestQueue.emplace_back(ret);
lk.unlock();
m_WorkerCV.notify_one();
return ret;
}
void CDvdFile::Initialize(const hecl::ProjectPath& path)
{
m_DvdRoot = path;
if (m_WorkerRun)
return;
m_WorkerRun = true;
m_WorkerThread = std::thread(WorkerProc);
}
void CDvdFile::Shutdown()
{
if (!m_WorkerRun)
return;
m_WorkerRun = false;
m_WorkerCV.notify_one();
if (m_WorkerThread.joinable())
m_WorkerThread.join();
m_RequestQueue.clear();
}
}

View File

@ -3,6 +3,10 @@
#include "RetroTypes.hpp"
#include <thread>
#include <mutex>
#include <condition_variable>
namespace urde
{
@ -24,18 +28,51 @@ class IDvdRequest;
class CDvdFile
{
friend class CResLoader;
std::string x18_name;
friend class CFileDvdRequest;
static hecl::ProjectPath m_DvdRoot;
static std::thread m_WorkerThread;
static std::mutex m_WorkerMutex;
static std::condition_variable m_WorkerCV;
static bool m_WorkerRun;
static std::vector<std::shared_ptr<IDvdRequest>> m_RequestQueue;
static void WorkerProc();
std::string x18_path;
athena::io::FileReader m_reader;
public:
CDvdFile(const char*) {}
void UpdateFilePos(int) {}
void CalcFileOffset(int, ESeekOrigin) {}
static void Initialize(const hecl::ProjectPath& path);
static void Shutdown();
CDvdFile(const char* path)
: x18_path(path), m_reader(hecl::ProjectPath(m_DvdRoot, path).getAbsolutePath()) {}
void UpdateFilePos(int pos)
{
m_reader.seek(pos, athena::SeekOrigin::Begin);
}
static void internalCallback(s32, DVDFileInfo*) {}
static bool FileExists(const char*) {return false;}
void CloseFile() {}
IDvdRequest* AsyncSeekRead(void*, u32, ESeekOrigin, int) {return nullptr;}
void SyncSeekRead(void*, u32, ESeekOrigin, int) {}
IDvdRequest* AsyncRead(void*, u32) {return nullptr;}
void SyncRead(void*, u32) {}
static bool FileExists(const char* path)
{
return hecl::ProjectPath(m_DvdRoot, path).getPathType() != hecl::ProjectPath::Type::File;
}
void CloseFile()
{
m_reader.close();
}
std::shared_ptr<IDvdRequest> AsyncSeekRead(void* buf, u32 len, ESeekOrigin whence, int off);
void SyncSeekRead(void* buf, u32 len, ESeekOrigin whence, int offset)
{
m_reader.seek(offset, athena::SeekOrigin(whence));
m_reader.readBytesToBuf(buf, len);
}
std::shared_ptr<IDvdRequest> AsyncRead(void* buf, u32 len)
{
return AsyncSeekRead(buf, len, ESeekOrigin::Cur, 0);
}
void SyncRead(void* buf, u32 len)
{
m_reader.readBytesToBuf(buf, len);
}
void StallForARAMFile() {}
void StartARAMFileLoad() {}
void PopARAMFileLoad() {}

View File

@ -1,17 +0,0 @@
#include "CDvdRequest.hpp"
namespace urde
{
void CDvdRequest::WaitUntilComplete()
{
}
bool CDvdRequest::IsComplete()
{
return false;
}
void CDvdRequest::PostCancelRequest()
{
}
}

View File

@ -7,6 +7,8 @@ namespace urde
class IDvdRequest
{
public:
virtual ~IDvdRequest() = default;
virtual void WaitUntilComplete()=0;
virtual bool IsComplete()=0;
virtual void PostCancelRequest()=0;
@ -15,28 +17,12 @@ public:
{
ARAM = 0,
Real = 1,
NOD = 2
File = 2,
NOD = 3
};
virtual EMediaType GetMediaType() const=0;
};
class CNODDvdRequest : public IDvdRequest
{
public:
void WaitUntilComplete();
bool IsComplete();
void PostCancelRequest();
EMediaType GetMediaType() const {return EMediaType::NOD;}
};
class CDvdRequest : public IDvdRequest
{
void WaitUntilComplete();
bool IsComplete();
void PostCancelRequest();
EMediaType GetMediaType() const { return EMediaType::Real; }
};
}
#endif // __PSHAG_CDVDREQUEST_HPP__

View File

@ -32,8 +32,8 @@ add_library(RuntimeCommon
CRandom16.hpp CRandom16.cpp
CResFactory.hpp CResFactory.cpp
CResLoader.hpp CResLoader.cpp
CDvdRequest.hpp CNODDvdRequest.cpp
CDvdFile.hpp CNODDvdFile.cpp
CDvdRequest.hpp
CDvdFile.hpp CDvdFile.cpp
CVirtualDvdFile.hpp CVirtualDvdFile.cpp
IObjectStore.hpp
CSimplePool.hpp CSimplePool.cpp
@ -63,7 +63,6 @@ add_library(RuntimeCommon
CMFGameBase.hpp
CInGameTweakManagerBase.hpp
CPlayMovieBase.hpp
CMoviePlayer.hpp CMoviePlayer.cpp
CGameDebug.hpp CGameDebug.cpp
rstl.hpp rstl.cpp
GameGlobalObjects.hpp

View File

@ -1,75 +0,0 @@
#include "CMoviePlayer.hpp"
namespace urde
{
CMoviePlayer::CMoviePlayer(const char* path, float startTime, bool flag)
: CDvdFile(path)
{
}
void CMoviePlayer::VerifyCallbackStatus()
{
}
void CMoviePlayer::DisableStaticAudio()
{
}
void CMoviePlayer::SetStaticAudioVolume(int vol)
{
}
void CMoviePlayer::SetStaticAudio(const void* data, u32 length, u32 loopStart, u32 loopEnd)
{
}
void CMoviePlayer::MixAudio(short* out, const short* in, u32 length)
{
}
void CMoviePlayer::MixStaticAudio(short* out, const short* in, u32 length)
{
}
void CMoviePlayer::StaticMyAudioCallback()
{
}
void CMoviePlayer::Rewind()
{
}
bool CMoviePlayer::GetIsMovieFinishedPlaying() const
{
return false;
}
bool CMoviePlayer::GetIsFullyCached() const
{
return false;
}
float CMoviePlayer::GetPlayedSeconds() const
{
return 0.0;
}
float CMoviePlayer::GetTotalSeconds() const
{
return 0.0;
}
void CMoviePlayer::SetPlayMode(EPlayMode mode)
{
}
void CMoviePlayer::DrawFrame(const CVector3f& a, const CVector3f& b,
const CVector3f& c, const CVector3f& d)
{
}
void CMoviePlayer::Update(float dt)
{
}
void CMoviePlayer::DecodeFromRead(const void* data)
{
}
void CMoviePlayer::ReadCompleted()
{
}
void CMoviePlayer::PostDVDReadRequestIfNeeded()
{
}
void CMoviePlayer::InitializeTextures()
{
}
}

View File

@ -1,44 +0,0 @@
#ifndef __PSHAG_CMOVIEPLAYER_HPP__
#define __PSHAG_CMOVIEPLAYER_HPP__
#include "RetroTypes.hpp"
#include "CDvdFile.hpp"
namespace urde
{
class CVector3f;
class CMoviePlayer : public CDvdFile
{
public:
enum class EPlayMode
{
};
CMoviePlayer(const char* path, float startTime, bool flag);
static void VerifyCallbackStatus();
static void DisableStaticAudio();
static void SetStaticAudioVolume(int vol);
static void SetStaticAudio(const void* data, u32 length, u32 loopStart, u32 loopEnd);
void MixAudio(short* out, const short* in, u32 length);
static void MixStaticAudio(short* out, const short* in, u32 length);
static void StaticMyAudioCallback();
void Rewind();
bool GetIsMovieFinishedPlaying() const;
bool GetIsFullyCached() const;
float GetPlayedSeconds() const;
float GetTotalSeconds() const;
void SetPlayMode(EPlayMode);
void DrawFrame(const CVector3f& a, const CVector3f& b, const CVector3f& c, const CVector3f& d);
void Update(float dt);
void DecodeFromRead(const void* data);
void ReadCompleted();
void PostDVDReadRequestIfNeeded();
void InitializeTextures();
};
}
#endif // __PSHAG_CMOVIEPLAYER_HPP__

View File

@ -1,17 +0,0 @@
#include "CDvdRequest.hpp"
namespace urde
{
void CNODDvdRequest::WaitUntilComplete()
{
}
bool CNODDvdRequest::IsComplete()
{
return false;
}
void CNODDvdRequest::PostCancelRequest()
{
}
}

View File

@ -2,7 +2,7 @@
#define __PSHAG_CPLAYMOVIEBASE_HPP__
#include "CIOWin.hpp"
#include "CMoviePlayer.hpp"
#include "Graphics/CMoviePlayer.hpp"
namespace urde
{
@ -12,7 +12,7 @@ class CPlayMovieBase : public CIOWin
CMoviePlayer x18_moviePlayer;
public:
CPlayMovieBase(const char* iowName, const char* path)
: CIOWin(iowName), x18_moviePlayer(path, 0.0, false) {}
: CIOWin(iowName), x18_moviePlayer(path, 0.0, false, false) {}
EMessageReturn OnMessage(const CArchitectureMessage&, CArchitectureQueue&) {return EMessageReturn::Normal;}
void Draw() const {}
};

View File

@ -10,7 +10,7 @@ const std::vector<u32>* CResLoader::GetTagListForFile(const std::string& name) c
{
std::string namePak = name + ".pak";
for (const std::unique_ptr<CPakFile>& pak : x1c_pakLoadedList)
if (!CStringExtras::CompareCaseInsensitive(namePak, pak->x18_name))
if (!CStringExtras::CompareCaseInsensitive(namePak, pak->x18_path))
return &pak->GetDepList();
return nullptr;
}
@ -100,13 +100,13 @@ CInputStream* CResLoader::LoadNewResourceSync(const SObjectTag& tag, void* extBu
return newStrm;
}
IDvdRequest* CResLoader::LoadResourcePartAsync(const SObjectTag& tag, int offset, int length, void* buf)
std::shared_ptr<IDvdRequest> CResLoader::LoadResourcePartAsync(const SObjectTag& tag, int offset, int length, void* buf)
{
return FindResourceForLoad(tag.id)->AsyncSeekRead(buf, length,
ESeekOrigin::Begin, x50_cachedResInfo->x4_offset + offset);
}
IDvdRequest* CResLoader::LoadResourceAsync(const SObjectTag& tag, void* buf)
std::shared_ptr<IDvdRequest> CResLoader::LoadResourceAsync(const SObjectTag& tag, void* buf)
{
return FindResourceForLoad(tag.id)->AsyncSeekRead(buf, ROUND_UP_32(x50_cachedResInfo->x8_size),
ESeekOrigin::Begin, x50_cachedResInfo->x4_offset);

View File

@ -29,8 +29,8 @@ public:
void LoadMemResourceSync(const SObjectTag& tag, void** bufOut, int* sizeOut);
CInputStream* LoadResourceFromMemorySync(const SObjectTag& tag, const void* buf);
CInputStream* LoadNewResourceSync(const SObjectTag& tag, void* extBuf=nullptr);
IDvdRequest* LoadResourcePartAsync(const SObjectTag& tag, int offset, int length, void* buf);
IDvdRequest* LoadResourceAsync(const SObjectTag& tag, void* buf);
std::shared_ptr<IDvdRequest> LoadResourcePartAsync(const SObjectTag& tag, int offset, int length, void* buf);
std::shared_ptr<IDvdRequest> LoadResourceAsync(const SObjectTag& tag, void* buf);
bool GetResourceCompression(const SObjectTag& tag);
u32 ResourceSize(const SObjectTag& tag);
bool ResourceExists(const SObjectTag& tag);

View File

@ -10,6 +10,10 @@ namespace urde
CGraphics::CProjectionState CGraphics::g_Proj;
float CGraphics::g_ProjAspect = 1.f;
u32 CGraphics::g_NumLightsActive = 0;
u32 CGraphics::g_NumBreakpointsWaiting = 0;
u32 CGraphics::g_FlippingState;
bool CGraphics::g_LastFrameUsedAbove = false;
bool CGraphics::g_InterruptLastFrameUsedAbove = false;
ERglLight CGraphics::g_LightActive = ERglLight::None;
ERglLight CGraphics::g_LightsWereOn = ERglLight::None;
zeus::CTransform CGraphics::g_GXModelView;
@ -61,6 +65,15 @@ void CGraphics::SetCullMode(ERglCullMode)
{
}
void CGraphics::EndScene()
{
/* Spinwait until g_NumBreakpointsWaiting is 0 */
/* ++g_NumBreakpointsWaiting; */
/* GXCopyDisp to g_CurrenFrameBuf with clear enabled */
/* Register next breakpoint with GP FIFO */
/* g_LastFrameUsedAbove = g_InterruptLastFrameUsedAbove; */
}
void CGraphics::SetAlphaCompare(ERglAlphaFunc comp0, u8 ref0, ERglAlphaOp op, ERglAlphaFunc comp1, u8 ref1)
{
}

View File

@ -150,6 +150,10 @@ public:
static CProjectionState g_Proj;
static float g_ProjAspect;
static u32 g_NumLightsActive;
static u32 g_NumBreakpointsWaiting;
static u32 g_FlippingState;
static bool g_LastFrameUsedAbove;
static bool g_InterruptLastFrameUsedAbove;
static ERglLight g_LightActive;
static ERglLight g_LightsWereOn;
static zeus::CTransform g_GXModelView;
@ -169,6 +173,7 @@ public:
static void SetDepthWriteMode(bool test, ERglEnum comp, bool write);
static void SetBlendMode(ERglBlendMode, ERglBlendFactor, ERglBlendFactor, ERglLogicOp);
static void SetCullMode(ERglCullMode);
static void EndScene();
static void SetAlphaCompare(ERglAlphaFunc comp0, u8 ref0, ERglAlphaOp op, ERglAlphaFunc comp1, u8 ref1);
static void SetViewPointMatrix(const zeus::CTransform& xf);
static void SetViewMatrix();

View File

@ -4,6 +4,8 @@ elseif(APPLE)
set(PLAT_SRCS CLineRendererShadersMetal.cpp)
endif()
include_directories(${LIBJPEG_INCLUDE_DIR})
add_library(RuntimeCommonGraphics
IRenderer.hpp
CBooRenderer.hpp CBooRenderer.cpp
@ -13,5 +15,6 @@ add_library(RuntimeCommonGraphics
CLight.hpp CLight.cpp
CTexture.hpp CTextureBoo.cpp
CModel.hpp CModelBoo.cpp
CMoviePlayer.hpp CMoviePlayer.cpp
CGraphics.hpp CGraphics.cpp
${PLAT_SRCS})

View File

@ -0,0 +1,665 @@
#include "CMoviePlayer.hpp"
#include "boo/graphicsdev/GLSLMacros.hpp"
#include "specter/View.hpp"
#include "CGraphics.hpp"
#include "Audio/g721.h"
#include "Audio/dsp.h"
#include "CDvdRequest.hpp"
#include <turbojpeg.h>
#define THP_BUFFER_FRAMES 30
namespace urde
{
static const char* VS_GLSL_YUV =
"#version 330\n"
BOO_GLSL_BINDING_HEAD
"layout(location=0) in vec3 posIn;\n"
"layout(location=1) in vec2 uvIn;\n"
SPECTER_GLSL_VIEW_VERT_BLOCK
"struct VertToFrag\n"
"{\n"
" vec4 color;\n"
" vec2 uv;\n"
"};\n"
"out VertToFrag vtf;\n"
"void main()\n"
"{\n"
" vtf.uv = uvIn;\n"
" vtf.color = mulColor;\n"
" gl_Position = mv * vec4(posIn, 1.0);\n"
"}\n";
static const char* FS_GLSL_YUV =
"#version 330\n"
BOO_GLSL_BINDING_HEAD
"struct VertToFrag\n"
"{\n"
" vec4 color;\n"
" vec2 uv;\n"
"};\n"
"in VertToFrag vtf;\n"
"TBINDING0 uniform sampler2D texs[3];\n"
"layout(location=0) out vec4 colorOut;\n"
"void main()\n"
"{\n"
" vec3 yuv;\n"
" yuv.r = texture(texs[0], vtf.uv).r;\n"
" yuv.g = texture(texs[1], vtf.uv).r;\n"
" yuv.b = texture(texs[2], vtf.uv).r;\n"
" yuv.r = 1.1643*(yuv.r-0.0625);\n"
" yuv.g = yuv.g-0.5;\n"
" yuv.b = yuv.b-0.5;\n"
" colorOut = vec4(yuv.r+1.5958*yuv.b, yuv.r-0.39173*yuv.g-0.81290*yuv.b, yuv.r+2.017*yuv.g, 1.0) * vtf.color;\n"
"}\n";
static const char* VS_HLSL_YUV =
"struct VertData\n"
"{\n"
" float3 posIn : POSITION;\n"
" float2 uvIn : UV;\n"
"};\n"
SPECTER_HLSL_VIEW_VERT_BLOCK
"struct VertToFrag\n"
"{\n"
" float4 position : SV_Position;\n"
" float4 color : COLOR;\n"
" float2 uv : UV;\n"
"};\n"
"VertToFrag main(in VertData v)\n"
"{\n"
" VertToFrag vtf;\n"
" vtf.uv = v.uvIn;\n"
" vtf.color = mulColor;\n"
" vtf.position = mul(mv, float4(v.posIn, 1.0));\n"
" return vtf;\n"
"}\n";
static const char* FS_HLSL_YUV =
"struct VertToFrag\n"
"{\n"
" float4 position : SV_Position;\n"
" float4 color : COLOR;\n"
" float2 uv : UV;\n"
"};\n"
"Texture2D texs[3] : register(t0);\n"
"SamplerState samp : register(s0);\n"
"float4 main(in VertToFrag vtf) : SV_Target0\n"
"{\n"
" float3 yuv;\n"
" yuv.r = texs[0].Sample(samp, vtf.uv).r;\n"
" yuv.g = texs[1].Sample(samp, vtf.uv).r;\n"
" yuv.b = texs[2].Sample(samp, vtf.uv).r;\n"
" yuv.r = 1.1643*(yuv.r-0.0625);\n"
" yuv.g = yuv.g-0.5;\n"
" yuv.b = yuv.b-0.5;\n"
" return float4(yuv.r+1.5958*yuv.b, yuv.r-0.39173*yuv.g-0.81290*yuv.b, yuv.r+2.017*yuv.g, 1.0) * vtf.color;\n"
"}\n";
static const char* VS_METAL_YUV =
"#include <metal_stdlib>\n"
"using namespace metal;\n"
"struct VertData\n"
"{\n"
" float3 posIn [[ attribute(0) ]];\n"
" float2 uvIn [[ attribute(1) ]];\n"
"};\n"
SPECTER_METAL_VIEW_VERT_BLOCK
"struct VertToFrag\n"
"{\n"
" float4 position [[ position ]];\n"
" float4 color;\n"
" float2 uv;\n"
"};\n"
"vertex VertToFrag vmain(VertData v [[ stage_in ]], constant SpecterViewBlock& view [[ buffer(2) ]])\n"
"{\n"
" VertToFrag vtf;\n"
" vtf.uv = v.uvIn;\n"
" vtf.color = view.mulColor;\n"
" vtf.position = view.mv * float4(v.posIn, 1.0);\n"
" return vtf;\n"
"}\n";
static const char* FS_METAL_YUV =
"#include <metal_stdlib>\n"
"using namespace metal;\n"
"constexpr sampler samp(address::repeat);\n"
"struct VertToFrag\n"
"{\n"
" float4 position [[ position ]];\n"
" float4 color;\n"
" float2 uv;\n"
"};\n"
"fragment float4 fmain(VertToFrag vtf [[ stage_in ]],"
" texture2d<float> tex0 [[ texture(0) ]],\n"
" texture2d<float> tex1 [[ texture(1) ]],\n"
" texture2d<float> tex2 [[ texture(2) ]])\n"
"{\n"
" float3 yuv;\n"
" yuv.r = tex0.sample(samp, vtf.uv).r;\n"
" yuv.g = tex1.sample(samp, vtf.uv).r;\n"
" yuv.b = tex2.sample(samp, vtf.uv).r;\n"
" yuv.r = 1.1643*(yuv.r-0.0625);\n"
" yuv.g = yuv.g-0.5;\n"
" yuv.b = yuv.b-0.5;\n"
" return float4(yuv.r+1.5958*yuv.b, yuv.r-0.39173*yuv.g-0.81290*yuv.b, yuv.r+2.017*yuv.g, 1.0) * vtf.color;\n"
"}\n";
static boo::GraphicsDataToken GraphicsData;
static boo::IVertexFormat* YUVVTXFmt = nullptr;
static boo::IShaderPipeline* YUVShaderPipeline = nullptr;
static tjhandle TjHandle = nullptr;
void CMoviePlayer::Initialize()
{
static const char* BlockNames[] = {"SpecterViewBlock"};
if (!CGraphics::g_BooFactory->bindingNeedsVertexFormat())
{
boo::VertexElementDescriptor texvdescs[] =
{
{nullptr, nullptr, boo::VertexSemantic::Position4},
{nullptr, nullptr, boo::VertexSemantic::UV4}
};
YUVVTXFmt = CGraphics::g_BooFactory->newVertexFormat(2, texvdescs);
}
switch (CGraphics::g_BooFactory->platform())
{
case boo::IGraphicsDataFactory::Platform::OGL:
YUVShaderPipeline = static_cast<boo::GLDataFactory*>(CGraphics::g_BooFactory)->newShaderPipeline
(VS_GLSL_YUV, FS_GLSL_YUV, 3, "texs", 1, BlockNames,
boo::BlendFactor::SrcAlpha, boo::BlendFactor::InvSrcAlpha,
false, false, false);
break;
#if _WIN32
case boo::IGraphicsDataFactory::Platform::D3D11:
case boo::IGraphicsDataFactory::Platform::D3D12:
YUVShaderPipeline = static_cast<boo::ID3DDataFactory*>(CGraphics::g_BooFactory)->newShaderPipeline
(VS_HLSL_YUV, FS_HLSL_YUV, ComPtr<ID3DBlob>(), ComPtr<ID3DBlob>(), ComPtr<ID3DBlob>(), YUVVTXFmt,
boo::BlendFactor::SrcAlpha, boo::BlendFactor::InvSrcAlpha,
false, false, false);
break;
#endif
#if BOO_HAS_METAL
case boo::IGraphicsDataFactory::Platform::Metal:
YUVShaderPipeline = static_cast<boo::MetalDataFactory*>(CGraphics::g_BooFactory)->newShaderPipeline
(VS_METAL_YUV, FS_METAL_YUV, YUVVTXFmt, 1,
boo::BlendFactor::SrcAlpha, boo::BlendFactor::InvSrcAlpha,
false, false, false);
break;
#endif
#if BOO_HAS_VULKAN
case boo::IGraphicsDataFactory::Platform::Vulkan:
YUVShaderPipeline = static_cast<boo::VulkanDataFactory*>(CGraphics::g_BooFactory)->newShaderPipeline
(VS_GLSL_YUV, FS_GLSL_YUV, YUVVTXFmt,
boo::BlendFactor::SrcAlpha, boo::BlendFactor::InvSrcAlpha,
false, false, false);
break;
#endif
default: break;
}
GraphicsData = CGraphics::CommitResources();
TjHandle = tjInitDecompress();
}
void CMoviePlayer::Shutdown()
{
GraphicsData.doDestroy();
tjDestroy(TjHandle);
}
void CMoviePlayer::THPHeader::swapBig()
{
magic = hecl::SBig(magic);
version = hecl::SBig(version);
maxBufferSize = hecl::SBig(maxBufferSize);
maxAudioSamples = hecl::SBig(maxAudioSamples);
fps = hecl::SBig(fps);
numFrames = hecl::SBig(numFrames);
firstFrameSize = hecl::SBig(firstFrameSize);
dataSize = hecl::SBig(dataSize);
componentDataOffset = hecl::SBig(componentDataOffset);
offsetsDataOffset = hecl::SBig(offsetsDataOffset);
firstFrameOffset = hecl::SBig(firstFrameOffset);
lastFrameOffset = hecl::SBig(lastFrameOffset);
}
void CMoviePlayer::THPComponents::swapBig()
{
numComponents = hecl::SBig(numComponents);
}
void CMoviePlayer::THPVideoInfo::swapBig()
{
width = hecl::SBig(width);
height = hecl::SBig(height);
}
void CMoviePlayer::THPAudioInfo::swapBig()
{
numChannels = hecl::SBig(numChannels);
sampleRate = hecl::SBig(sampleRate);
numSamples = hecl::SBig(numSamples);
}
void CMoviePlayer::THPFrameHeader::swapBig()
{
nextSize = hecl::SBig(nextSize);
prevSize = hecl::SBig(prevSize);
imageSize = hecl::SBig(imageSize);
audioSize = hecl::SBig(audioSize);
}
void CMoviePlayer::THPAudioFrameHeader::swapBig()
{
channelSize = hecl::SBig(channelSize);
numSamples = hecl::SBig(numSamples);
for (int i=0 ; i<2 ; ++i)
{
for (int j=0 ; j<8 ; ++j)
{
channelCoefs[i][j][0] = hecl::SBig(channelCoefs[i][j][0]);
channelCoefs[i][j][1] = hecl::SBig(channelCoefs[i][j][1]);
}
channelPrevs[i][0] = hecl::SBig(channelPrevs[i][0]);
channelPrevs[i][1] = hecl::SBig(channelPrevs[i][1]);
}
}
/* Slightly modified THPAudioDecode present in SDK, always interleaves */
u32 CMoviePlayer::THPAudioDecode(s16* buffer, const u8* audioFrame, bool stereo)
{
THPAudioFrameHeader header = *((const THPAudioFrameHeader*)audioFrame);
header.swapBig();
audioFrame += sizeof(THPAudioFrameHeader);
if (stereo)
{
for (int i=0 ; i<2 ; ++i)
{
unsigned samples = header.numSamples;
s16* bufferCur = buffer+i;
const u8* audioFrameCur = audioFrame;
int16_t prev1 = header.channelPrevs[i][0];
int16_t prev2 = header.channelPrevs[i][1];
for (int f=0 ; f<header.channelSize/8 ; ++f)
{
DSPDecompressFrameStereoStride(bufferCur, audioFrameCur,
header.channelCoefs[i], &prev1, &prev2, samples);
samples -= 14;
bufferCur += 28;
audioFrameCur += 8;
}
}
}
else
{
unsigned samples = header.numSamples;
s16* bufferCur = buffer;
const u8* audioFrameCur = audioFrame;
int16_t prev1 = header.channelPrevs[0][0];
int16_t prev2 = header.channelPrevs[0][1];
for (int f=0 ; f<header.channelSize/8 ; ++f)
{
DSPDecompressFrameStereoDupe(bufferCur, audioFrameCur,
header.channelCoefs[0], &prev1, &prev2, samples);
samples -= 14;
bufferCur += 28;
audioFrameCur += 8;
}
}
return header.numSamples;
}
CMoviePlayer::CMoviePlayer(const char* path, float preLoadSeconds, bool loop, bool deinterlace)
: CDvdFile(path), xec_preLoadSeconds(preLoadSeconds), xf4_24_loop(loop), m_deinterlace(deinterlace)
{
u8 buf[64];
SyncRead(buf, 64);
memcpy(&x28_thpHead, buf, 48);
x28_thpHead.swapBig();
u32 cur = x28_thpHead.componentDataOffset;
SyncSeekRead(buf, 32, ESeekOrigin::Begin, cur);
memcpy(&x58_thpComponents, buf, 20);
cur += 20;
x58_thpComponents.swapBig();
for (int i=0 ; i<x58_thpComponents.numComponents ; ++i)
{
switch (x58_thpComponents.comps[i])
{
case THPComponents::Type::Video:
SyncSeekRead(buf, 32, ESeekOrigin::Begin, cur);
memcpy(&x6c_videoInfo, buf, 8);
cur += 8;
x6c_videoInfo.swapBig();
break;
case THPComponents::Type::Audio:
SyncSeekRead(buf, 32, ESeekOrigin::Begin, cur);
memcpy(&x74_audioInfo, buf, 12);
cur += 12;
x74_audioInfo.swapBig();
xf4_25_hasAudio = true;
break;
default: break;
}
}
xe4_totalSeconds = x28_thpHead.numFrames / x28_thpHead.fps;
if (xec_preLoadSeconds < 0.f)
{
/* Load whole video */
xec_preLoadSeconds = xe4_totalSeconds;
xf0_preLoadFrames = x28_thpHead.numFrames;
}
else if (xec_preLoadSeconds > 0.f)
{
/* Pre-load video portion */
u32 frame = xec_preLoadSeconds * x28_thpHead.fps;
xf0_preLoadFrames = std::min(frame, x28_thpHead.numFrames);
xec_preLoadSeconds = std::min(xe4_totalSeconds, xec_preLoadSeconds);
}
if (xf0_preLoadFrames > 0)
xa0_bufferQueue.reserve(xf0_preLoadFrames);
m_blockBuf = CGraphics::g_BooFactory->newDynamicBuffer(boo::BufferUse::Vertex, sizeof(m_viewVertBlock), 1);
boo::IVertexFormat* vtxFmt = YUVVTXFmt;
if (CGraphics::g_BooFactory->bindingNeedsVertexFormat())
{
boo::VertexElementDescriptor texvdescs[] =
{
{m_blockBuf, nullptr, boo::VertexSemantic::Position4},
{m_blockBuf, nullptr, boo::VertexSemantic::UV4}
};
vtxFmt = CGraphics::g_BooFactory->newVertexFormat(2, texvdescs);
}
x80_textures.reserve(3);
for (int i=0 ; i<3 ; ++i)
{
x80_textures.emplace_back();
CTHPTextureSet& set = x80_textures.back();
if (deinterlace)
{
set.Y[0] = CGraphics::g_BooFactory->newDynamicTexture(x6c_videoInfo.width,
x6c_videoInfo.height / 2,
boo::TextureFormat::I8);
set.Y[1] = CGraphics::g_BooFactory->newDynamicTexture(x6c_videoInfo.width,
x6c_videoInfo.height / 2,
boo::TextureFormat::I8);
set.U = CGraphics::g_BooFactory->newDynamicTexture(x6c_videoInfo.width / 2,
x6c_videoInfo.height / 2,
boo::TextureFormat::I8);
set.V = CGraphics::g_BooFactory->newDynamicTexture(x6c_videoInfo.width / 2,
x6c_videoInfo.height / 2,
boo::TextureFormat::I8);
boo::IGraphicsBuffer* bufs[] = {m_blockBuf};
for (int j=0 ; j<2 ; ++j)
{
boo::ITexture* texs[] = {set.Y[j], set.U, set.V};
set.binding[j] = CGraphics::g_BooFactory->newShaderDataBinding(YUVShaderPipeline, vtxFmt, m_blockBuf,
nullptr, nullptr, 1, bufs, 3, texs);
}
}
else
{
set.Y[0] = CGraphics::g_BooFactory->newDynamicTexture(x6c_videoInfo.width,
x6c_videoInfo.height,
boo::TextureFormat::I8);
set.U = CGraphics::g_BooFactory->newDynamicTexture(x6c_videoInfo.width / 2,
x6c_videoInfo.height / 2,
boo::TextureFormat::I8);
set.V = CGraphics::g_BooFactory->newDynamicTexture(x6c_videoInfo.width / 2,
x6c_videoInfo.height / 2,
boo::TextureFormat::I8);
boo::IGraphicsBuffer* bufs[] = {m_blockBuf};
boo::ITexture* texs[] = {set.Y[0], set.U, set.V};
set.binding[0] = CGraphics::g_BooFactory->newShaderDataBinding(YUVShaderPipeline, vtxFmt, m_blockBuf,
nullptr, nullptr, 1, bufs, 3, texs);
}
if (xf4_25_hasAudio)
set.audioBuf.reset(new s16[x28_thpHead.maxAudioSamples * 2]);
}
m_yuvBuf.reset(new uint8_t[tjBufSizeYUV(x6c_videoInfo.width, x6c_videoInfo.height, TJ_420)]);
m_token = CGraphics::CommitResources();
PostDVDReadRequestIfNeeded();
}
void CMoviePlayer::VerifyCallbackStatus()
{
}
void CMoviePlayer::DisableStaticAudio()
{
}
void CMoviePlayer::SetStaticAudioVolume(int vol)
{
}
void CMoviePlayer::SetStaticAudio(const void* data, u32 length, u32 loopStart, u32 loopEnd)
{
}
void CMoviePlayer::MixAudio(short* out, const short* in, u32 length)
{
}
void CMoviePlayer::MixStaticAudio(short* out, const short* in, u32 length)
{
}
void CMoviePlayer::StaticMyAudioCallback()
{
}
void CMoviePlayer::Rewind()
{
if (x98_request)
{
x98_request->PostCancelRequest();
x98_request.reset();
}
xb0_nextReadSize = x28_thpHead.firstFrameSize;
xb4_nextReadOff = x28_thpHead.firstFrameOffset;
xb8_readSize = x28_thpHead.firstFrameSize;
xbc_readOff = x28_thpHead.firstFrameOffset;
xc0_loadedFrames = 0;
xc4_ = 0;
xc8_curFrame = 0;
xcc_decodedTexSlot = 0;
xd0_ = -1;
xd4_ = -1;
xd8_ = 0;
xdc_frameRem = 0.f;
xe8_curSeconds = 0.f;
}
void CMoviePlayer::DrawFrame(const CVector3f& a, const CVector3f& b,
const CVector3f& c, const CVector3f& d)
{
}
void CMoviePlayer::Update(float dt)
{
if (xc0_loadedFrames < xf0_preLoadFrames)
{
if (x98_request && x98_request->IsComplete())
{
ReadCompleted();
if (xc0_loadedFrames >= xa0_bufferQueue.size() &&
xc0_loadedFrames < xf0_preLoadFrames &&
xa0_bufferQueue.size() < x28_thpHead.numFrames)
{
PostDVDReadRequestIfNeeded();
}
}
}
else
{
if (x98_request)
{
bool flag = false;
if (xc4_ >= xa0_bufferQueue.size() && xc0_loadedFrames >= xa0_bufferQueue.size())
flag = true;
if (x98_request->IsComplete() && xd8_ < 2 && flag)
{
DecodeFromRead(x90_requestBuf.get());
ReadCompleted();
PostDVDReadRequestIfNeeded();
++xd8_;
++xc4_;
if (xc4_ >= x28_thpHead.numFrames && xf4_24_loop)
xc4_ = 0;
}
}
}
if (!x98_request && xe0_playMode == EPlayMode::Playing &&
xa0_bufferQueue.size() < x28_thpHead.numFrames)
{
PostDVDReadRequestIfNeeded();
}
if (xd8_ < 2)
{
if (xe0_playMode == EPlayMode::Playing && xc4_ < xf0_preLoadFrames)
{
u32 minFrame = std::min(u32(xa0_bufferQueue.size()) - 1, xc4_);
if (minFrame == -1)
return;
std::unique_ptr<uint8_t[]>& frameData = xa0_bufferQueue[minFrame];
DecodeFromRead(frameData.get());
++xd8_;
++xc4_;
if (xc4_ >= x28_thpHead.numFrames && xf4_24_loop)
xc4_ = 0;
}
}
if (xd8_ <= 0 || xe0_playMode != EPlayMode::Playing)
return;
xe8_curSeconds += dt;
if (xf4_24_loop)
xe8_curSeconds = std::fmod(xe8_curSeconds, xe4_totalSeconds);
else
xe8_curSeconds = std::min(xe4_totalSeconds, xe8_curSeconds);
float frameDt = 1.f / x28_thpHead.fps;
float rem = xdc_frameRem - dt;
if (rem <= 0.f)
{
if (!xf4_26_fieldFlip)
{
++xd0_;
if (xd0_ >= x80_textures.size())
xd0_ = 0;
if (xd4_ == -1)
xd4_ = 0;
--xd8_;
++xc8_curFrame;
if (xc8_curFrame == x28_thpHead.numFrames && xf4_24_loop)
xc8_curFrame = 0;
rem += frameDt;
xfc_ = 0;
}
else
{
rem += dt;
xf4_26_fieldFlip = false;
}
}
xdc_frameRem = rem;
}
void CMoviePlayer::DecodeFromRead(const void* data)
{
const u8* inptr = (u8*)data;
CTHPTextureSet& tex = x80_textures[xcc_decodedTexSlot];
THPFrameHeader frameHeader = *static_cast<const THPFrameHeader*>(data);
frameHeader.swapBig();
inptr += 8 + x58_thpComponents.numComponents * 4;
for (int i=0 ; i<x58_thpComponents.numComponents ; ++i)
{
switch (x58_thpComponents.comps[i])
{
case THPComponents::Type::Video:
{
tjDecompressToYUV(TjHandle, (u8*)inptr, frameHeader.imageSize, m_yuvBuf.get(), 0);
inptr += frameHeader.imageSize;
uintptr_t planeSize = x6c_videoInfo.width * x6c_videoInfo.height;
uintptr_t planeSizeHalf = planeSize / 2;
uintptr_t planeSizeQuarter = planeSizeHalf / 2;
if (m_deinterlace)
{
/* Deinterlace into 2 discrete 60-fps half-res textures */
u8* mappedData = (u8*)tex.Y[0]->map(planeSizeHalf);
for (unsigned y=0 ; y<x6c_videoInfo.height/2 ; ++y)
{
memcpy(mappedData + x6c_videoInfo.width*y,
m_yuvBuf.get() + x6c_videoInfo.width*(y*2),
x6c_videoInfo.width);
}
tex.Y[0]->unmap();
mappedData = (u8*)tex.Y[1]->map(planeSizeHalf);
for (unsigned y=0 ; y<x6c_videoInfo.height/2 ; ++y)
{
memcpy(mappedData + x6c_videoInfo.width*y,
m_yuvBuf.get() + x6c_videoInfo.width*(y*2+1),
x6c_videoInfo.width);
}
tex.Y[1]->unmap();
tex.U->load(m_yuvBuf.get() + planeSize, planeSizeQuarter);
tex.V->load(m_yuvBuf.get() + planeSize + planeSizeQuarter, planeSizeQuarter);
}
else
{
/* Direct planar load */
tex.Y[0]->load(m_yuvBuf.get(), planeSize);
tex.U->load(m_yuvBuf.get() + planeSize, planeSizeQuarter);
tex.V->load(m_yuvBuf.get() + planeSize + planeSizeQuarter, planeSizeQuarter);
}
break;
}
case THPComponents::Type::Audio:
THPAudioDecode(tex.audioBuf.get(), (u8*)inptr, x74_audioInfo.numChannels == 2);
inptr += frameHeader.audioSize;
break;
default: break;
}
}
++xcc_decodedTexSlot;
if (xcc_decodedTexSlot == x80_textures.size())
xcc_decodedTexSlot = 0;
}
void CMoviePlayer::ReadCompleted()
{
}
void CMoviePlayer::PostDVDReadRequestIfNeeded()
{
if (xc0_loadedFrames < x28_thpHead.numFrames)
{
x90_requestBuf.reset(new uint8_t[xb0_nextReadSize]);
x98_request = AsyncSeekRead(x90_requestBuf.get(), xb0_nextReadSize,
ESeekOrigin::Begin, xb4_nextReadOff);
}
}
}

View File

@ -0,0 +1,176 @@
#ifndef __PSHAG_CMOVIEPLAYER_HPP__
#define __PSHAG_CMOVIEPLAYER_HPP__
#include "RetroTypes.hpp"
#include "CDvdFile.hpp"
#include "boo/graphicsdev/IGraphicsDataFactory.hpp"
#include "specter/View.hpp"
namespace urde
{
class CVector3f;
class CMoviePlayer : public CDvdFile
{
public:
enum class EPlayMode
{
Stopped,
Playing
};
private:
struct THPHeader
{
u32 magic;
u32 version;
u32 maxBufferSize;
u32 maxAudioSamples;
float fps;
u32 numFrames;
u32 firstFrameSize;
u32 dataSize;
u32 componentDataOffset;
u32 offsetsDataOffset;
u32 firstFrameOffset;
u32 lastFrameOffset;
void swapBig();
} x28_thpHead;
struct THPComponents
{
u32 numComponents;
enum class Type : u8
{
Video = 0x0,
Audio = 0x1,
None = 0xff
} comps[16];
void swapBig();
} x58_thpComponents;
struct THPVideoInfo
{
u32 width;
u32 height;
void swapBig();
} x6c_videoInfo;
struct THPAudioInfo
{
u32 numChannels;
u32 sampleRate;
u32 numSamples;
void swapBig();
} x74_audioInfo;
struct THPFrameHeader
{
u32 nextSize;
u32 prevSize;
u32 imageSize;
u32 audioSize;
void swapBig();
};
struct THPAudioFrameHeader
{
u32 channelSize;
u32 numSamples;
s16 channelCoefs[2][8][2];
s16 channelPrevs[2][2];
void swapBig();
};
struct CTHPTextureSet
{
boo::ITextureD* Y[2] = {};
boo::ITextureD* U = nullptr;
boo::ITextureD* V = nullptr;
std::unique_ptr<s16[]> audioBuf;
boo::IShaderDataBinding* binding[2] = {};
};
std::vector<CTHPTextureSet> x80_textures;
std::unique_ptr<uint8_t[]> x90_requestBuf;
std::shared_ptr<IDvdRequest> x98_request;
std::vector<std::unique_ptr<uint8_t[]>> xa0_bufferQueue;
u32 xb0_nextReadSize = 0;
u32 xb4_nextReadOff = 0;
u32 xb8_readSize = 0;
u32 xbc_readOff = 0;
u32 xc0_loadedFrames = 0;
u32 xc4_ = 0;
u32 xc8_curFrame = 0;
u32 xcc_decodedTexSlot = 0;
u32 xd0_ = 0;
u32 xd4_ = 0;
s32 xd8_ = 0;
float xdc_frameRem = 0.f;
EPlayMode xe0_playMode = EPlayMode::Playing;
float xe4_totalSeconds = 0.f;
float xe8_curSeconds = 0.f;
float xec_preLoadSeconds;
u32 xf0_preLoadFrames = 0;
u32 xf8_ = 0;
u32 xfc_ = 0;
boo::GraphicsDataToken m_token;
std::unique_ptr<uint8_t[]> m_yuvBuf;
union
{
struct
{
bool xf4_24_loop : 1; bool xf4_25_hasAudio : 1;
bool xf4_26_fieldFlip : 1; bool m_deinterlace : 1;
};
u8 m_dummy = 0;
};
struct ViewBlock
{
zeus::CMatrix4f m_mv;
zeus::CColor m_color = zeus::CColor::skWhite;
} m_viewVertBlock;
boo::IGraphicsBufferD* m_blockBuf;
uint64_t m_loadedFrameCount = 0;
uint64_t m_playedFrameCount = 0;
public:
CMoviePlayer(const char* path, float preLoadSeconds, bool loop, bool deinterlace);
static u32 THPAudioDecode(s16* buffer, const u8* audioFrame, bool stereo);
static void VerifyCallbackStatus();
static void DisableStaticAudio();
static void SetStaticAudioVolume(int vol);
static void SetStaticAudio(const void* data, u32 length, u32 loopStart, u32 loopEnd);
void MixAudio(short* out, const short* in, u32 length);
static void MixStaticAudio(short* out, const short* in, u32 length);
static void StaticMyAudioCallback();
void Rewind();
bool GetIsMovieFinishedPlaying() const
{
if (xf4_24_loop)
return false;
return xc8_curFrame == x28_thpHead.numFrames;
}
bool GetIsFullyCached() const {return xa0_bufferQueue.size() >= xf0_preLoadFrames;}
float GetPlayedSeconds() const {return xdc_frameRem + xe8_curSeconds;}
float GetTotalSeconds() const {return xe4_totalSeconds;}
void SetPlayMode(EPlayMode mode) {xe0_playMode = mode;}
void DrawFrame(const CVector3f& a, const CVector3f& b, const CVector3f& c, const CVector3f& d);
void Update(float dt);
void DecodeFromRead(const void* data);
void ReadCompleted();
void PostDVDReadRequestIfNeeded();
static void Initialize();
static void Shutdown();
};
}
#endif // __PSHAG_CMOVIEPLAYER_HPP__

2
hecl

@ -1 +1 @@
Subproject commit 0df19b29fbc5024511d3bb444e9b9f7ef57d766d
Subproject commit 3e3e34414087d9427bf98cef992dad279ebd988e

2
nod

@ -1 +1 @@
Subproject commit a98927d4a00926a04caca202a2da88763c08ac7c
Subproject commit 473607ebe7b4b30b5e9936fa572ae18e1a063e3f

@ -1 +1 @@
Subproject commit d6a6032dbaf4500acad4900b9e8ba172cf75788c
Subproject commit 3b1128bbfa4602888e7d08d1b58ed64737f12640