thunderbrew/src/ui/CSimpleMovieFrame.cpp

822 lines
24 KiB
C++

#include "ui/CSimpleMovieFrame.hpp"
#include "ui/CSimpleMovieFrameScript.hpp"
#include "util/SFile.hpp"
#include "gx/Coordinate.hpp"
#include "gx/Buffer.hpp"
#include "gx/RenderState.hpp"
#include "gx/Texture.hpp"
#include "gx/Transform.hpp"
#include "gx/Draw.hpp"
#include <common/Time.hpp>
#include <tempest/Matrix.hpp>
#define XVDEC_CALL
#if defined(WHOA_SYSTEM_WIN)
#include <windows.h>
#if defined(WHOA_ARCH_32)
#undef XVDEC_CALL
#define XVDEC_CALL __cdecl
#elif defined(__GNUC__) || defined(__clang__)
#undef XVDEC_CALL
#define XVDEC_CALL __attribute__((cdecl))
#endif
#define RTLD_LAZY 1
static inline void* dlopen(const char* filename, int flag) {
(void)flag;
return LoadLibraryA(filename);
}
static inline void* dlsym(void* handle, const char* symbol) {
return GetProcAddress(reinterpret_cast<HMODULE>(handle), symbol);
}
static inline int dlclose(void* handle) {
return static_cast<int>(FreeLibrary(reinterpret_cast<HMODULE>(handle)));
}
#else // !defined(WHOA_SYSTEM_WIN)
#include <dlfcn.h>
#endif
extern "C" {
typedef struct {
void* output;
void* input;
unsigned int input_size;
int update;
int zero0;
int zero1;
} decoder_data_t;
typedef int (XVDEC_CALL * INITIALIZEDIVXDECODER)(unsigned int index, unsigned int width, unsigned int height);
typedef int (XVDEC_CALL * SETOUTPUTFORMAT)(unsigned int index, unsigned int one, unsigned int width, unsigned int height);
typedef int (XVDEC_CALL * DIVXDECODE)(unsigned int index, void* data, unsigned int zero);
typedef int (XVDEC_CALL * UNINITIALIZEDIVXDECODER)(unsigned int index);
}
static void* s_decoderLibrary = nullptr;
static INITIALIZEDIVXDECODER InitializeDivxDecoder = nullptr;
static SETOUTPUTFORMAT SetOutputFormat = nullptr;
static DIVXDECODE DivxDecode = nullptr;
static UNINITIALIZEDIVXDECODER UnInitializeDivxDecoder = nullptr;
static const char* s_decoderNames[] = {
"DivxDecoder.dll",
"XvidDecoder.dll",
"libXvidDecoder.dylib",
"XvidDecoder.dylib",
"libXvidDecoder.so",
"XvidDecoder.so",
nullptr
};
static int32_t UnloadDivxDecoder() {
if (!s_decoderLibrary) {
return 0;
}
InitializeDivxDecoder = nullptr;
SetOutputFormat = nullptr;
DivxDecode = nullptr;
UnInitializeDivxDecoder = nullptr;
dlclose(s_decoderLibrary);
s_decoderLibrary = nullptr;
return 1;
}
static int32_t LoadDivxDecoder() {
if (InitializeDivxDecoder && SetOutputFormat && DivxDecode && UnInitializeDivxDecoder) {
return 1;
}
const char** decoderName = s_decoderNames;
while (!s_decoderLibrary && *decoderName) {
s_decoderLibrary = dlopen(*decoderName, RTLD_LAZY);
++decoderName;
}
if (!s_decoderLibrary) {
return 0;
}
InitializeDivxDecoder = reinterpret_cast<INITIALIZEDIVXDECODER>(dlsym(s_decoderLibrary, "InitializeDivxDecoder"));
SetOutputFormat = reinterpret_cast<SETOUTPUTFORMAT>(dlsym(s_decoderLibrary, "SetOutputFormat"));
DivxDecode = reinterpret_cast<DIVXDECODE>(dlsym(s_decoderLibrary, "DivxDecode"));
UnInitializeDivxDecoder = reinterpret_cast<UNINITIALIZEDIVXDECODER>(dlsym(s_decoderLibrary, "UnInitializeDivxDecoder"));
if (!InitializeDivxDecoder || !SetOutputFormat || !DivxDecode || !UnInitializeDivxDecoder) {
UnloadDivxDecoder();
return 0;
}
return 1;
}
static uint32_t s_decoderIndex = 0;
static uint32_t s_strideData[144] = {
512, 256, 0, 3200,
256, 256, 2048, 3200,
32, 256, 3072, 3200,
512, 128, 819200, 3200,
256, 128, 821248, 3200,
32, 128, 822272, 3200,
512, 512, 0, 4096,
512, 512, 2048, 4096,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
512, 512, 0, 3200,
256, 512, 2048, 3200,
32, 512, 3072, 3200,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
512, 512, 0, 4096,
512, 512, 2048, 4096,
512, 64, 2097152, 4096,
512, 64, 2099200, 4096,
0, 0, 0, 0,
0, 0, 0, 0,
512, 256, 0, 3200,
256, 256, 2048, 3200,
32, 256, 3072, 3200,
512, 128, 819200, 3200,
256, 128, 821248, 3200,
32, 128, 822272, 3200,
512, 512, 0, 4096,
512, 512, 2048, 4096,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0
};
static int32_t s_movieTextureUpdate[] = {
0, 6, 512, 256, // Format: 0, Texture: 0
0, 6, 256, 256, // Format: 0, Texture: 1
0, 6, 32, 256, // Format: 0, Texture: 2
0, 0, 512, 122, // Format: 0, Texture: 3
0, 0, 256, 122, // Format: 0, Texture: 4
0, 0, 32, 122, // Format: 0, Texture: 5
0, 24, 512, 512, // Format: 1, Texture: 0
0, 0, 512, 488, // Format: 1, Texture: 1
0, 0, 0, 0, // Format: 1, Texture: 2
0, 0, 0, 0, // Format: 1, Texture: 3
0, 0, 0, 0, // Format: 1, Texture: 4
0, 0, 0, 0, // Format: 1, Texture: 5
0, 32, 512, 480, // Format: 2, Texture: 0
0, 32, 256, 480, // Format: 2, Texture: 1
0, 32, 32, 480, // Format: 2, Texture: 2
0, 0, 0, 0, // Format: 2, Texture: 3
0, 0, 0, 0, // Format: 2, Texture: 4
0, 0, 0, 0, // Format: 2, Texture: 5
0, 0, 512, 512, // Format: 3, Texture: 0
0, 0, 512, 512, // Format: 3, Texture: 1
0, 0, 512, 64, // Format: 3, Texture: 2
0, 0, 512, 64, // Format: 3, Texture: 3
0, 0, 0, 0, // Format: 3, Texture: 4
0, 0, 0, 0, // Format: 3, Texture: 5
0, 21, 512, 256, // Format: 4, Texture: 0
0, 21, 256, 256, // Format: 4, Texture: 1
0, 21, 32, 256, // Format: 4, Texture: 2
0, 0, 512, 107, // Format: 4, Texture: 3
0, 0, 256, 107, // Format: 4, Texture: 4
0, 0, 32, 107, // Format: 4, Texture: 5
0, 38, 512, 512, // Format: 5, Texture: 0
0, 0, 512, 474, // Format: 5, Texture: 1
0, 0, 0, 0, // Format: 5, Texture: 2
0, 0, 0, 0, // Format: 5, Texture: 3
0, 0, 0, 0, // Format: 5, Texture: 4
0, 0, 0, 0 // Format: 5, Texture: 5
};
static float s_layout[144] = {
0.0f, 0.63999999f, 0.11f, -0.33000001f,
0.63999999f, 0.95999998f, 0.11f, -0.33000001f,
0.95999998f, 1.0f, 0.11f, -0.33000001f,
0.0f, 0.63999999f, 0.33000001f, 0.11f,
0.63999999f, 0.95999998f, 0.33000001f, 0.11f,
0.95999998f, 1.0f, 0.33000001f, 0.11f,
0.0f, 0.5f, 0.333f, -0.333f,
0.5f, 1.0f, 0.333f, -0.333f,
0.0f, 0.0f, 0.0f, 0.0f,
0.0f, 0.0f, 0.0f, 0.0f,
0.0f, 0.0f, 0.0f, 0.0f,
0.0f, 0.0f, 0.0f, 0.0f,
0.0f, 0.63999999f, 0.41999999f, -0.41999999f,
0.63999999f, 0.95999998f, 0.41999999f, -0.41999999f,
0.95999998f, 1.0f, 0.41999999f, -0.41999999f,
0.0f, 0.0f, 0.0f, 0.0f,
0.0f, 0.0f, 0.0f, 0.0f,
0.0f, 0.0f, 0.0f, 0.0f,
0.0f, 0.5f, 0.32659999f, -0.41999999f,
0.5f, 1.0f, 0.32659999f, -0.41999999f,
0.0f, 0.5f, 0.41999999f, 0.32659999f,
0.5f, 1.0f, 0.41999999f, 0.32659999f,
0.0f, 0.0f, 0.0f, 0.0f,
0.0f, 0.0f, 0.0f, 0.0f,
0.0f, 0.63999999f, 0.11f, -0.33000001f,
0.63999999f, 0.95999998f, 0.11f, -0.33000001f,
0.95999998f, 1.0f, 0.11f, -0.33000001f,
0.0f, 0.63999999f, 0.33000001f, 0.11f,
0.63999999f, 0.95999998f, 0.33000001f, 0.11f,
0.95999998f, 1.0f, 0.33000001f, 0.11f,
0.0f, 0.5f, 0.333f, -0.333f,
0.5f, 1.0f, 0.333f, -0.333f,
0.0f, 0.0f, 0.0f, 0.0f,
0.0f, 0.0f, 0.0f, 0.0f,
0.0f, 0.0f, 0.0f, 0.0f,
0.0f, 0.0f, 0.0f, 0.0f
};
static const uint32_t s_textureCountByFormat[6] = { 6, 2, 3, 4, 6, 2 };
static const uint32_t s_imageDataOffsets[6] = {
19200, 98304, 102400, 0, 67200, 155648
};
int32_t CSimpleMovieFrame::s_metatable;
int32_t CSimpleMovieFrame::s_objectType;
void CSimpleMovieFrame::CreateScriptMetaTable() {
lua_State* L = FrameScript_GetContext();
int32_t ref = FrameScript_Object::CreateScriptMetaTable(L, &CSimpleMovieFrame::RegisterScriptMethods);
CSimpleMovieFrame::s_metatable = ref;
}
int32_t CSimpleMovieFrame::GetObjectType() {
if (!CSimpleMovieFrame::s_objectType) {
CSimpleMovieFrame::s_objectType = ++FrameScript_Object::s_objectTypes;
}
return CSimpleMovieFrame::s_objectType;
}
void CSimpleMovieFrame::RegisterScriptMethods(lua_State* L) {
CSimpleFrame::RegisterScriptMethods(L);
FrameScript_Object::FillScriptMethodTable(L, SimpleMovieFrameMethods, NUM_SIMPLE_MOVIE_FRAME_SCRIPT_METHODS);
}
void CSimpleMovieFrame::RenderMovie(void* param) {
auto movieFrame = reinterpret_cast<CSimpleMovieFrame*>(param);
if (movieFrame->m_isPlaying) {
movieFrame->UpdateTiming();
movieFrame->Render();
}
}
void CSimpleMovieFrame::TextureCallback(EGxTexCommand command, uint32_t width, uint32_t height, uint32_t, uint32_t, void* userData, uint32_t& texelStrideInBytes, const void*& texels) {
if (command == GxTex_Latch) {
auto data = reinterpret_cast<CSimpleMovieFrame::TextureData*>(userData);
STORM_ASSERT(width == data->data[0]);
STORM_ASSERT(height == data->data[1]);
texels = &data->buffer[data->data[2]];
texelStrideInBytes = data->data[3];
}
}
FrameScript_Object::ScriptIx* CSimpleMovieFrame::GetScriptByName(const char* name, ScriptData& data) {
auto parentScript = CSimpleFrame::GetScriptByName(name, data);
if (parentScript) {
return parentScript;
}
if (!SStrCmpI(name, "OnMovieFinished", STORM_MAX_STR)) {
return &this->m_onMovieFinished;
}
if (!SStrCmpI(name, "OnMovieShowSubtitle", STORM_MAX_STR)) {
data.wrapper = "return function(self,text) %s end";
return &this->m_onMovieShowSubtitle;
}
if (!SStrCmpI(name, "OnMovieHideSubtitle", STORM_MAX_STR)) {
return &this->m_onMovieHideSubtitle;
}
return nullptr;
}
bool CSimpleMovieFrame::IsA(int32_t type) {
return type == CSimpleMovieFrame::s_objectType
|| type == CSimpleFrame::s_objectType
|| type == CScriptRegion::s_objectType
|| type == CScriptObject::s_objectType;
}
int32_t CSimpleMovieFrame::GetScriptMetaTable() {
return CSimpleMovieFrame::s_metatable;
}
void CSimpleMovieFrame::OnFrameRender(CRenderBatch* batch, uint32_t layer) {
this->CSimpleFrame::OnFrameRender(batch, layer);
if (layer == DRAWLAYER_ARTWORK) {
batch->QueueCallback(&CSimpleMovieFrame::RenderMovie, this);
}
}
CSimpleMovieFrame::CSimpleMovieFrame(CSimpleFrame* parent)
: CSimpleFrame(parent) {
}
int32_t CSimpleMovieFrame::StartMovie(const char* filename, int32_t volume) {
if (!LoadDivxDecoder()) {
return 0;
}
if (!this->ParseAVIFile(filename) || !this->OpenVideo()) {
return 0;
}
SStrCopy(this->m_filename, filename, 256);
this->m_volume = volume;
// this->OpenAudio(this, filename, volume, 0);
// this->OpenCaptions(this, filename);
this->m_isPlaying = 1;
return 1;
}
void CSimpleMovieFrame::StopMovie() {
if (!this->m_isPlaying) {
return;
}
// UnloadDivxDecoder
// CloseAudio
// CloseCaptions
this->m_isInterrupted = 0;
this->m_isPlaying = 0;
if (this->m_onMovieFinished.luaRef) {
this->RunScript(this->m_onMovieFinished, 0, nullptr);
}
}
int32_t CSimpleMovieFrame::ParseAVIFile(const char* filename) {
char path[STORM_MAX_PATH];
// WARNING(workaround): Remove "Data/enGB/" substring
SStrPrintf(path, STORM_MAX_PATH, "Data/enGB/%s.avi", filename);
SFile* videoFile = nullptr;
if (!SFile::OpenEx(nullptr, path, 1, &videoFile)) {
return 0;
}
// -- ParseAVIHeader --
#pragma pack(push, 1)
struct
{
char id[4];
uint32_t length;
char format[4];
} block;
#pragma pack(pop)
if (!SFile::Read(videoFile, &block, 12, nullptr, nullptr, nullptr) ||
SStrCmpI(block.id, "RIFF", 4) || SStrCmpI(block.format, "AVI ", 4)) {
SFile::Close(videoFile);
return 0;
}
uint32_t fileSize = SFile::GetFileSize(videoFile, nullptr);
char* data = nullptr;
uint32_t dataSize = 0;
uint8_t* indexData = nullptr;
uint32_t indexDataSize = 0;
uint32_t moviStart = 0;
while (true) {
uint32_t position = SFile::SetFilePointer(videoFile, 0, nullptr, 1);
if (position >= fileSize) {
break;
}
if (!SFile::Read(videoFile, &block, 8, nullptr, nullptr, nullptr)) {
break;
}
if (SStrCmpI(block.id, "LIST", 4)) {
if (SStrCmpI(block.id, "idx1", 4)) {
SFile::SetFilePointer(videoFile, block.length, nullptr, 1);
} else {
indexDataSize = block.length;
indexData = reinterpret_cast<uint8_t*>(alloca(indexDataSize));
if (!SFile::Read(videoFile, indexData, indexDataSize, nullptr, nullptr, nullptr)) {
break;
}
}
} else {
if (!SFile::Read(videoFile, &block, 4, nullptr, nullptr, nullptr)) {
break;
}
block.length -= 4;
if (SStrCmpI(block.id, "hdrl", 4)) {
if (SStrCmpI(block.id, "movi", 4)) {
SFile::SetFilePointer(videoFile, block.length, nullptr, 1);
} else {
moviStart = SFile::SetFilePointer(videoFile, 0, nullptr, 1);
SFile::SetFilePointer(videoFile, block.length, nullptr, 1);
}
} else {
dataSize = block.length;
data = reinterpret_cast<char*>(alloca(dataSize));
if (!SFile::Read(videoFile, data, dataSize, nullptr, nullptr, nullptr)) {
break;
}
}
}
}
int32_t v41 = 0;
int32_t v39 = -1;
int32_t v30 = -1;
uint32_t offset = 0;
while (offset < dataSize) {
if (!SStrCmpI(&data[offset], "LIST", 4)) {
offset += 12;
continue;
}
uint32_t length = *reinterpret_cast<uint32_t*>(&data[offset + 4]);
if (SStrCmpI(&data[offset], "strh", 4u)) {
if (SStrCmpI(&data[offset], "strf", 4)) {
offset += 8;
} else {
offset += 8;
if (v39 >= 0 && v30 < 0) {
this->m_videoWidth = *reinterpret_cast<uint32_t*>(&data[offset + 4]);
this->m_videoHeight = *reinterpret_cast<uint32_t*>(&data[offset + 8]);
}
}
} else {
offset += 8;
if (!SStrCmpI(&data[offset], "vids", 4)) {
uint32_t scale = *reinterpret_cast<uint32_t*>(&data[offset + 20]);
uint32_t rate = *reinterpret_cast<uint32_t*>(&data[offset + 24]);
this->m_frameRate = static_cast<float>(rate) / static_cast<float>(scale);
this->m_numFrames = *reinterpret_cast<uint32_t*>(&data[offset + 32]);
v39 = v41;
}
if (!SStrCmpI(&data[offset], "auds", 4)) {
v30 = v41;
}
++v41;
}
offset += length;
}
// -- ParseAVIIndex --
this->m_videoBytes = 0;
this->m_audioBytes = 0;
offset = 0;
while (offset < indexDataSize) {
// IsVideoChunk
if (indexData[offset + 2] == 100) {
this->m_videoBytes += *reinterpret_cast<uint32_t*>(&indexData[offset + 12]) + 4;
}
// IsAudioChunk
if (indexData[offset + 2] == 119) {
this->m_audioBytes += *reinterpret_cast<uint32_t*>(&indexData[offset + 12]);
}
offset += 16;
}
this->m_videoData = reinterpret_cast<char*>(ALLOC(this->m_videoBytes));
this->m_audioData = reinterpret_cast<char*>(ALLOC(this->m_audioBytes));
char* videoData = this->m_videoData;
char* audioData = this->m_audioData;
offset = 0;
while (offset < indexDataSize) {
// IsVideoChunk
if (indexData[offset + 2] == 100) {
uint32_t frameSize = *reinterpret_cast<uint32_t*>(&indexData[offset + 12]);
uint32_t frameOffset = *reinterpret_cast<uint32_t*>(&indexData[offset + 8]) + moviStart + 4;
memcpy(videoData, &frameSize, 4);
videoData += 4;
SFile::SetFilePointer(videoFile, frameOffset, nullptr, 0);
SFile::Read(videoFile, videoData, frameSize, nullptr, nullptr, nullptr);
videoData += frameSize;
}
// IsAudioChunk
if (indexData[offset + 2] == 119) {
uint32_t frameSize = *reinterpret_cast<uint32_t*>(&indexData[offset + 12]);
uint32_t frameOffset = *reinterpret_cast<uint32_t*>(&indexData[offset + 8]) + moviStart + 4;
SFile::SetFilePointer(videoFile, frameOffset, nullptr, 0);
SFile::Read(videoFile, audioData, frameSize, nullptr, nullptr, nullptr);
audioData += frameSize;
}
offset += 16;
}
this->m_currentFrameData = this->m_videoData;
SFile::Close(videoFile);
return dataSize > 0;
}
int32_t CSimpleMovieFrame::OpenVideo() {
this->m_startTime = OsGetAsyncTimeMs();
this->m_elapsedTime = 0;
this->m_decoder = ++s_decoderIndex;
this->m_currentFrame = 0;
this->m_prevFrame = -1;
this->m_lastKeyFrame = 0;
this->m_frameAudioSync = 0;
if (InitializeDivxDecoder(this->m_decoder, this->m_videoWidth, this->m_videoHeight) ||
SetOutputFormat(this->m_decoder, 1, this->m_videoWidth, this->m_videoHeight)) {
this->CloseVideo();
return 0;
}
if (this->m_videoWidth == 1024) {
if (this->m_videoHeight == 576) {
this->m_textureFormat = 3;
} else if (this->m_videoHeight == 436) {
this->m_textureFormat = 5;
} else {
this->m_textureFormat = 1;
}
} else if (this->m_videoWidth == 800) {
if (this->m_videoHeight == 448) {
this->m_textureFormat = 2;
} else if (this->m_videoHeight == 342 || this->m_videoHeight == 340) {
this->m_textureFormat = 4;
} else {
this->m_textureFormat = 0;
}
} else {
this->CloseVideo();
return 0;
}
const uint32_t widthByFormat[6] = { 800, 1024, 800, 1024, 800, 1024 };
const uint32_t heightByFormat[6] = { 384, 512, 512, 576, 384, 512 };
const uint32_t imageSize = widthByFormat[this->m_textureFormat] * heightByFormat[this->m_textureFormat] * 4;
this->m_imageData = reinterpret_cast<char*>(ALLOC_ZERO(imageSize));
if (!this->m_imageData) {
CloseVideo();
return 0;
}
int32_t hasTextures = 1;
for (uint32_t i = 0; i < s_textureCountByFormat[this->m_textureFormat]; ++i) {
uint32_t stride = (6 * this->m_textureFormat + i) * 4;
this->m_textureData[i].data = &s_strideData[stride];
this->m_textureData[i].buffer = this->m_imageData;
hasTextures &= GxTexCreate(
s_strideData[stride],
s_strideData[stride + 1],
GxTex_Argb8888,
CGxTexFlags(),
&this->m_textureData[i],
CSimpleMovieFrame::TextureCallback,
this->m_textures[i]);
}
if (hasTextures) {
for (uint32_t i = 0; i < s_textureCountByFormat[this->m_textureFormat]; ++i) {
GxTexUpdate(this->m_textures[i], 0, 0, 0, 0, 1);
}
}
return 1;
}
void CSimpleMovieFrame::CloseVideo() {
if (this->m_decoder) {
UnInitializeDivxDecoder(this->m_decoder);
--s_decoderIndex;
this->m_decoder = 0;
}
if (this->m_imageData) {
FREE(this->m_imageData);
this->m_imageData = nullptr;
}
if (this->m_videoData) {
FREE(this->m_videoData);
this->m_videoData = nullptr;
}
for (uint32_t i = 0; i < 6; ++i) {
GxTexDestroy(this->m_textures[i]);
}
}
int32_t CSimpleMovieFrame::UpdateTiming() {
bool isAudioPlaying = false; /* SE2::IsPlaying(this->m_audioChannel) */
if (isAudioPlaying) {
//this->m_elapsedTime = SE2::GetPositionInMS(this->m_audioChannel);
//this->m_startTime = OsGetAsyncTimeMs() - this->m_elapsedTime;
} else {
this->m_elapsedTime = OsGetAsyncTimeMs() - this->m_startTime;
}
int32_t currentFrame = static_cast<int32_t>(this->m_elapsedTime * this->m_frameRate * 0.001 + 0.5);
if (isAudioPlaying) {
// TODO
}
currentFrame += this->m_frameAudioSync;
if (currentFrame <= this->m_prevFrame) {
currentFrame = this->m_prevFrame;
}
this->m_currentFrame = currentFrame;
if (currentFrame >= this->m_numFrames) {
this->m_isInterrupted = 1;
}
if (this->m_isInterrupted) {
this->StopMovie();
return 0;
}
if (currentFrame == this->m_prevFrame) {
return 0;
}
if (currentFrame != this->m_prevFrame + 1) {
++this->m_lastKeyFrame;
}
while (this->m_prevFrame < this->m_currentFrame - 1) {
this->DecodeFrame(false);
++this->m_prevFrame;
}
if (!this->DecodeFrame(true)) {
this->m_isInterrupted = 1;
}
this->m_prevFrame = this->m_currentFrame;
// TODO: Subtitle stuff
return 1;
}
int32_t CSimpleMovieFrame::DecodeFrame(bool update) {
decoder_data_t frame;
frame.output = this->m_imageData + s_imageDataOffsets[this->m_textureFormat];
frame.input = this->m_currentFrameData + 4;
frame.input_size = *reinterpret_cast<uint32_t*>(this->m_currentFrameData);
frame.update = update ? 1 : 0;
frame.zero0 = 0;
frame.zero1 = 0;
this->m_currentFrameData += frame.input_size + 4;
if (DivxDecode(this->m_decoder, &frame, 0)) {
return 0;
}
if (!update) {
return 1;
}
for (uint32_t i = 0; i < s_textureCountByFormat[this->m_textureFormat]; ++i) {
uint32_t v9 = 4 * (i + 6 * this->m_textureFormat);
// WARNING(workaround): Uncomment the code below when GxTexUpdate will be working properly
/*
GxTexUpdate(
this->m_textures[i],
s_movieTextureUpdate[v9 + 0],
s_movieTextureUpdate[v9 + 1],
s_movieTextureUpdate[v9 + 2],
s_movieTextureUpdate[v9 + 3],
1);
*/
GxTexUpdate(this->m_textures[i], 0, 0, 0, 0, 1);
}
return 1;
}
void CSimpleMovieFrame::Render() {
float minX;
float maxX;
float minY;
float maxY;
float minZ;
float maxZ;
GxXformViewport(minX, maxX, minY, maxY, minZ, maxZ);
GxXformSetViewport(0.0f, 1.0f, 0.0f, 1.0f, 0.0f, 1.0f);
CImVector clearColor = { 0x00, 0x00, 0x00, 0xFF };
GxSceneClear(3, clearColor);
C44Matrix matrix;
GxuXformCreateOrtho(0.0, 1.0, -0.5, 0.5, 0.0, 500.0, matrix);
GxXformSetView(C44Matrix());
GxXformSetProjection(matrix);
static uint32_t s_movieRenderFlag = 0;
static C3Vector s_movieFrameNormalVec;
static C2Vector s_movieFrameTexVec[4];
if ((s_movieRenderFlag & 1) == 0) {
s_movieFrameNormalVec.x = 0.0;
s_movieFrameNormalVec.y = 0.0;
s_movieFrameNormalVec.z = 1.0;
s_movieRenderFlag |= 1;
}
if ((s_movieRenderFlag & 2) == 0) {
s_movieFrameTexVec[0].x = 0.0f;
s_movieFrameTexVec[0].y = 0.0f;
s_movieFrameTexVec[1].x = 1.0f;
s_movieFrameTexVec[1].y = 0.0f;
s_movieFrameTexVec[2].x = 0.0f;
s_movieFrameTexVec[2].y = 1.0f;
s_movieFrameTexVec[3].x = 1.0f;
s_movieFrameTexVec[3].y = 1.0f;
s_movieRenderFlag |= 2;
}
GxRsPush();
GxRsSet(GxRs_Lighting, 0);
GxRsSet(GxRs_Fog, 0);
GxRsSet(GxRs_BlendingMode, 0);
GxRsSetAlphaRef();
float aspectCompensation = CoordinateGetAspectCompensation();
for (uint32_t i = 0; i < s_textureCountByFormat[this->m_textureFormat]; ++i) {
float* rect = &s_layout[24 * this->m_textureFormat + 4 * i];
float v16 = rect[3] * aspectCompensation;
float v17 = rect[2] * aspectCompensation;
C3Vector position[] = {
{ rect[0], v16, 0.0f },
{ rect[1], v16, 0.0f },
{ rect[0], v17, 0.0f },
{ rect[1], v17, 0.0f }
};
GxPrimLockVertexPtrs(4, position, sizeof(C3Vector), &s_movieFrameNormalVec, 0, nullptr, 0, nullptr, 0, s_movieFrameTexVec, sizeof(C2Vector), nullptr, 0);
GxRsSet(GxRs_Texture0, this->m_textures[i]);
uint16_t indices[] = { 0, 1, 2, 3 };
GxDrawLockedElements(GxPrim_TriangleStrip, 4, indices);
GxPrimUnlockVertexPtrs();
}
GxRsPop();
GxXformSetViewport(minX, maxX, minY, maxY, minZ, maxZ);
}