mirror of
https://github.com/zeldaret/ss
synced 2026-06-10 20:58:38 -04:00
nw4r ut almost matching
This commit is contained in:
@@ -196,9 +196,9 @@ void AsyncDisplay::clearEFB(u16 fbWidth, u16 fbHeight, u16 x, u16 y, u16 width,
|
||||
GXSetTevColor(GX_TEVREG0, color);
|
||||
GXSetTevOrder(GX_TEVSTAGE0, GX_TEXCOORD0, GX_TEXMAP0, GX_COLOR_NULL);
|
||||
GXSetTevColorIn(GX_TEVSTAGE0, GX_CC_ZERO, GX_CC_ZERO, GX_CC_ZERO, GX_CC_C0);
|
||||
GXSetTevColorOp(GX_TEVSTAGE0, GX_TEV_ADD, GX_TB_ZERO, GX_TEV_SCALE_0, 1, GX_TEVPREV);
|
||||
GXSetTevColorOp(GX_TEVSTAGE0, GX_TEV_ADD, GX_TB_ZERO, GX_CS_SCALE_1, 1, GX_TEVPREV);
|
||||
GXSetTevAlphaIn(GX_TEVSTAGE0, GX_CA_ZERO, GX_CA_ZERO, GX_CA_ZERO, GX_CA_A0);
|
||||
GXSetTevAlphaOp(GX_TEVSTAGE0, GX_TEV_ADD, GX_TB_ZERO, GX_TEV_SCALE_0, 1, GX_TEVPREV);
|
||||
GXSetTevAlphaOp(GX_TEVSTAGE0, GX_TEV_ADD, GX_TB_ZERO, GX_CS_SCALE_1, 1, GX_TEVPREV);
|
||||
GXSetAlphaCompare(GX_ALWAYS, 0, GX_AOP_OR, GX_ALWAYS, 0);
|
||||
GXSetZTexture(2, GX_TF_Z24X8, 0);
|
||||
GXSetZCompLoc(false);
|
||||
|
||||
@@ -0,0 +1,260 @@
|
||||
|
||||
#include <nw4r/ut.h>
|
||||
|
||||
namespace {
|
||||
|
||||
static void SetupGXCommon() {
|
||||
static const nw4r::ut::Color fog = 0;
|
||||
|
||||
GXSetFog(GX_FOG_NONE, fog, 0.0f, 0.0f, 0.0f, 0.0f);
|
||||
GXSetTevSwapModeTable(GX_TEV_SWAP0, GX_CH_RED, GX_CH_GREEN, GX_CH_BLUE, GX_CH_ALPHA);
|
||||
GXSetZTexture(GX_ZT_DISABLE, GX_TF_Z8, 0);
|
||||
GXSetNumChans(1);
|
||||
GXSetChanCtrl(GX_COLOR0A0, FALSE, GX_SRC_REG, GX_SRC_VTX, GX_LIGHT_NULL, GX_DF_NONE, GX_AF_NONE);
|
||||
GXSetChanCtrl(GX_COLOR1A1, FALSE, GX_SRC_REG, GX_SRC_REG, GX_LIGHT_NULL, GX_DF_NONE, GX_AF_NONE);
|
||||
GXSetNumTexGens(1);
|
||||
GXSetTexCoordGen2(GX_TEXCOORD0, GX_TG_MTX2x4, GX_TG_TEX0, GX_TEXMTX_IDENT, FALSE, GX_DUALMTX_IDENT);
|
||||
GXSetNumIndStages(0);
|
||||
GXSetBlendMode(GX_BM_BLEND, GX_BL_SRCALPHA, GX_BL_INVSRCALPHA, GX_LO_SET);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
namespace nw4r {
|
||||
namespace ut {
|
||||
|
||||
CharWriter::CharWriter() : mAlpha(255), mIsWidthFixed(false), mFixedWidth(0.0f), mFont(NULL) {
|
||||
mLoadingTexture.Reset();
|
||||
ResetColorMapping();
|
||||
SetGradationMode(GRADMODE_NONE);
|
||||
SetTextColor(Color(0xFFFFFFFF));
|
||||
SetScale(1.0f, 1.0f);
|
||||
SetCursor(0.0f, 0.0f, 0.0f);
|
||||
EnableLinearFilter(true, true);
|
||||
}
|
||||
|
||||
CharWriter::~CharWriter() {}
|
||||
|
||||
void CharWriter::SetupGX() {
|
||||
ResetTextureCache();
|
||||
|
||||
if (mColorMapping.min != 0x00000000 || mColorMapping.max != 0xFFFFFFFF) {
|
||||
SetupGXWithColorMapping(mColorMapping.min, mColorMapping.max);
|
||||
} else if (mFont != NULL) {
|
||||
switch (mFont->GetTextureFormat()) {
|
||||
case GX_TF_I4:
|
||||
case GX_TF_I8:
|
||||
SetupGXForI();
|
||||
break;
|
||||
case GX_TF_IA4:
|
||||
case GX_TF_IA8:
|
||||
SetupGXDefault();
|
||||
break;
|
||||
case GX_TF_RGB565:
|
||||
case GX_TF_RGB5A3:
|
||||
case GX_TF_RGBA8:
|
||||
SetupGXForRGBA();
|
||||
break;
|
||||
default:
|
||||
SetupGXDefault();
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
SetupGXDefault();
|
||||
}
|
||||
}
|
||||
|
||||
void CharWriter::SetFontSize(f32 width, f32 height) {
|
||||
SetScale(width / mFont->GetWidth(), height / mFont->GetHeight());
|
||||
}
|
||||
|
||||
void CharWriter::SetFontSize(f32 height) {
|
||||
f32 scale = height / mFont->GetHeight();
|
||||
SetScale(scale, scale);
|
||||
}
|
||||
|
||||
f32 CharWriter::GetFontWidth() const {
|
||||
return mScale.x * mFont->GetWidth();
|
||||
}
|
||||
|
||||
f32 CharWriter::GetFontHeight() const {
|
||||
return mScale.y * mFont->GetHeight();
|
||||
}
|
||||
|
||||
f32 CharWriter::GetFontAscent() const {
|
||||
return mScale.y * mFont->GetAscent();
|
||||
}
|
||||
|
||||
f32 CharWriter::GetFontDescent() const {
|
||||
return mScale.y * mFont->GetDescent();
|
||||
}
|
||||
|
||||
void CharWriter::EnableLinearFilter(bool atSmall, bool atLarge) {
|
||||
mFilter.atSmall = atSmall ? GX_LINEAR : GX_NEAR;
|
||||
mFilter.atLarge = atLarge ? GX_LINEAR : GX_NEAR;
|
||||
}
|
||||
|
||||
f32 CharWriter::Print(u16 ch) {
|
||||
f32 width;
|
||||
f32 left;
|
||||
|
||||
Glyph glyph;
|
||||
mFont->GetGlyph(&glyph, ch);
|
||||
|
||||
if (mIsWidthFixed) {
|
||||
width = mFixedWidth;
|
||||
left = (width - glyph.widths.charWidth * mScale.x) / 2 + (glyph.widths.leftSpacing * mScale.x);
|
||||
} else {
|
||||
width = glyph.widths.charWidth * mScale.x;
|
||||
left = glyph.widths.leftSpacing * mScale.x;
|
||||
}
|
||||
|
||||
PrintGlyph(mCursorPos.x + left, mCursorPos.y, mCursorPos.z, glyph);
|
||||
mCursorPos.x += width;
|
||||
|
||||
return width;
|
||||
}
|
||||
|
||||
void CharWriter::PrintGlyph(f32 x, f32 y, f32 z, const Glyph &glyph) {
|
||||
f32 x2 = x + (glyph.widths.glyphWidth * mScale.x);
|
||||
f32 y2 = y + (glyph.height * mScale.y);
|
||||
|
||||
u32 posLeft = glyph.cellX;
|
||||
u16 texLeft = 0x8000 * posLeft / glyph.texWidth;
|
||||
|
||||
u32 posTop = glyph.cellY;
|
||||
u16 texTop = 0x8000 * posTop / glyph.texHeight;
|
||||
|
||||
u32 posRight = posLeft + glyph.widths.glyphWidth;
|
||||
u16 texRight = 0x8000 * posRight / glyph.texWidth;
|
||||
|
||||
u32 posBottom = posTop + glyph.height;
|
||||
u16 texBottom = 0x8000 * posBottom / glyph.texHeight;
|
||||
|
||||
LoadTexture(glyph, GX_TEXMAP0);
|
||||
|
||||
GXBegin(GX_QUADS, GX_VTXFMT0, 4);
|
||||
{
|
||||
GXPosition3f32(x, y, z);
|
||||
GXColor1u32(mVertexColor.tl);
|
||||
GXTexCoord2s16(texLeft, texTop);
|
||||
|
||||
GXPosition3f32(x2, y, z);
|
||||
GXColor1u32(mVertexColor.tr);
|
||||
GXTexCoord2s16(texRight, texTop);
|
||||
|
||||
GXPosition3f32(x2, y2, z);
|
||||
GXColor1u32(mVertexColor.br);
|
||||
GXTexCoord2s16(texRight, texBottom);
|
||||
|
||||
GXPosition3f32(x, y2, z);
|
||||
GXColor1u32(mVertexColor.bl);
|
||||
GXTexCoord2s16(texLeft, texBottom);
|
||||
}
|
||||
// GXEnd();
|
||||
}
|
||||
|
||||
void CharWriter::LoadTexture(const Glyph &glyph, GXTexMapID slot) {
|
||||
LoadingTexture loadingTexture;
|
||||
|
||||
loadingTexture.slot = slot;
|
||||
loadingTexture.texture = glyph.texture;
|
||||
loadingTexture.filter = mFilter;
|
||||
|
||||
if (loadingTexture != mLoadingTexture) {
|
||||
GXTexObj texObj;
|
||||
GXInitTexObj(&texObj, glyph.texture, glyph.texWidth, glyph.texHeight, glyph.format, GX_CLAMP, GX_CLAMP, FALSE);
|
||||
GXInitTexObjLOD(&texObj, mFilter.atSmall, mFilter.atLarge, 0.0f, 0.0f, 0.0f, FALSE, FALSE, GX_ANISO_1);
|
||||
GXLoadTexObj(&texObj, slot);
|
||||
|
||||
mLoadingTexture = loadingTexture;
|
||||
}
|
||||
}
|
||||
|
||||
void CharWriter::UpdateVertexColor() {
|
||||
// clang-format off
|
||||
mVertexColor.tl = mTextColor.start;
|
||||
mVertexColor.tr = mTextColor.gradMode != GRADMODE_H ? mTextColor.start : mTextColor.end;
|
||||
mVertexColor.bl = mTextColor.gradMode != GRADMODE_V ? mTextColor.start : mTextColor.end;
|
||||
mVertexColor.br = mTextColor.gradMode == GRADMODE_NONE ? mTextColor.start : mTextColor.end;
|
||||
// clang-format on
|
||||
|
||||
mVertexColor.tl.a = (mVertexColor.tl.a * mAlpha) / 255, mVertexColor.tr.a = (mVertexColor.tr.a * mAlpha) / 255;
|
||||
mVertexColor.bl.a = (mVertexColor.bl.a * mAlpha) / 255;
|
||||
mVertexColor.br.a = (mVertexColor.br.a * mAlpha) / 255;
|
||||
}
|
||||
|
||||
void CharWriter::SetupVertexFormat() {
|
||||
GXSetVtxAttrFmt(GX_VTXFMT0, GX_VA_POS, GX_POS_XYZ, GX_F32, 0);
|
||||
GXSetVtxAttrFmt(GX_VTXFMT0, GX_VA_CLR0, GX_CLR_RGBA, GX_RGBA8, 0);
|
||||
GXSetVtxAttrFmt(GX_VTXFMT0, GX_VA_TEX0, GX_TEX_ST, GX_U16, 15);
|
||||
|
||||
GXClearVtxDesc();
|
||||
GXSetVtxDesc(GX_VA_POS, GX_DIRECT);
|
||||
GXSetVtxDesc(GX_VA_CLR0, GX_DIRECT);
|
||||
GXSetVtxDesc(GX_VA_TEX0, GX_DIRECT);
|
||||
}
|
||||
|
||||
void CharWriter::SetupGXDefault() {
|
||||
SetupGXCommon();
|
||||
|
||||
GXSetNumTevStages(1);
|
||||
GXSetTevDirect(GX_TEVSTAGE0);
|
||||
GXSetTevSwapMode(GX_TEVSTAGE0, GX_TEV_SWAP0, GX_TEV_SWAP0);
|
||||
GXSetTevOrder(GX_TEVSTAGE0, GX_TEXCOORD0, GX_TEXMAP0, GX_COLOR0A0);
|
||||
GXSetTevOp(GX_TEVSTAGE0, GX_MODULATE);
|
||||
|
||||
SetupVertexFormat();
|
||||
}
|
||||
|
||||
void CharWriter::SetupGXWithColorMapping(Color min, Color max) {
|
||||
SetupGXCommon();
|
||||
|
||||
GXSetNumTevStages(2);
|
||||
GXSetTevDirect(GX_TEVSTAGE0);
|
||||
GXSetTevDirect(GX_TEVSTAGE1);
|
||||
|
||||
GXSetTevSwapMode(GX_TEVSTAGE0, GX_TEV_SWAP0, GX_TEV_SWAP0);
|
||||
GXSetTevSwapMode(GX_TEVSTAGE1, GX_TEV_SWAP0, GX_TEV_SWAP0);
|
||||
GXSetTevOrder(GX_TEVSTAGE0, GX_TEXCOORD0, GX_TEXMAP0, GX_COLOR_NULL);
|
||||
|
||||
GXSetTevColor(GX_TEVREG0, min);
|
||||
GXSetTevColor(GX_TEVREG1, max);
|
||||
|
||||
GXSetTevColorIn(GX_TEVSTAGE0, GX_CC_C0, GX_CC_C1, GX_CC_TEXC, GX_CC_ZERO);
|
||||
GXSetTevAlphaIn(GX_TEVSTAGE0, GX_CA_A0, GX_CA_A1, GX_CA_TEXA, GX_CA_ZERO);
|
||||
GXSetTevColorOp(GX_TEVSTAGE0, GX_TEV_ADD, GX_TB_ZERO, GX_CS_SCALE_1, TRUE, GX_TEVPREV);
|
||||
GXSetTevAlphaOp(GX_TEVSTAGE0, GX_TEV_ADD, GX_TB_ZERO, GX_CS_SCALE_1, TRUE, GX_TEVPREV);
|
||||
|
||||
GXSetTevOrder(GX_TEVSTAGE1, GX_TEXCOORD_NULL, GX_TEXMAP_NULL, GX_COLOR0A0);
|
||||
GXSetTevColorIn(GX_TEVSTAGE1, GX_CC_ZERO, GX_CC_CPREV, GX_CC_RASC, GX_CC_ZERO);
|
||||
GXSetTevAlphaIn(GX_TEVSTAGE1, GX_CA_ZERO, GX_CA_APREV, GX_CA_RASA, GX_CA_ZERO);
|
||||
GXSetTevColorOp(GX_TEVSTAGE1, GX_TEV_ADD, GX_TB_ZERO, GX_CS_SCALE_1, TRUE, GX_TEVPREV);
|
||||
GXSetTevAlphaOp(GX_TEVSTAGE1, GX_TEV_ADD, GX_TB_ZERO, GX_CS_SCALE_1, TRUE, GX_TEVPREV);
|
||||
|
||||
SetupVertexFormat();
|
||||
}
|
||||
|
||||
void CharWriter::SetupGXForI() {
|
||||
SetupGXCommon();
|
||||
|
||||
GXSetNumTevStages(1);
|
||||
GXSetTevDirect(GX_TEVSTAGE0);
|
||||
GXSetTevSwapMode(GX_TEVSTAGE0, GX_TEV_SWAP0, GX_TEV_SWAP0);
|
||||
GXSetTevOrder(GX_TEVSTAGE0, GX_TEXCOORD0, GX_TEXMAP0, GX_COLOR0A0);
|
||||
GXSetTevColorIn(GX_TEVSTAGE0, GX_CC_ZERO, GX_CC_ZERO, GX_CC_ZERO, GX_CC_RASC);
|
||||
GXSetTevAlphaIn(GX_TEVSTAGE0, GX_CA_ZERO, GX_CA_TEXA, GX_CA_RASA, GX_CA_ZERO);
|
||||
GXSetTevColorOp(GX_TEVSTAGE0, GX_TEV_ADD, GX_TB_ZERO, GX_CS_SCALE_1, TRUE, GX_TEVPREV);
|
||||
GXSetTevAlphaOp(GX_TEVSTAGE0, GX_TEV_ADD, GX_TB_ZERO, GX_CS_SCALE_1, TRUE, GX_TEVPREV);
|
||||
|
||||
SetupVertexFormat();
|
||||
}
|
||||
|
||||
void CharWriter::SetupGXForRGBA() {
|
||||
SetupGXDefault();
|
||||
}
|
||||
|
||||
CharWriter::LoadingTexture CharWriter::mLoadingTexture;
|
||||
|
||||
} // namespace ut
|
||||
} // namespace nw4r
|
||||
|
||||
@@ -0,0 +1,180 @@
|
||||
#include <nw4r/ut.h>
|
||||
|
||||
namespace nw4r {
|
||||
namespace ut {
|
||||
|
||||
NW4R_UT_RTTI_DEF_DERIVED(DvdFileStream, FileStream);
|
||||
|
||||
void DvdFileStream::DvdAsyncCallback_(long result, DVDFileInfo *info) {
|
||||
DvdFileStream *self = reinterpret_cast<AsyncContext *>(info)->stream;
|
||||
|
||||
self->mIsBusy = false;
|
||||
self->mResult = result;
|
||||
|
||||
if (self->mCallback != NULL) {
|
||||
self->mCallback(result, self, self->mCallbackArg);
|
||||
}
|
||||
}
|
||||
|
||||
void DvdFileStream::DvdCBAsyncCallback_(long result, DVDCommandBlock *block) {
|
||||
DvdFileStream *self = reinterpret_cast<AsyncContext *>(block)->stream;
|
||||
|
||||
self->mIsCancelling = false;
|
||||
|
||||
if (self->mCancelCallback != NULL) {
|
||||
self->mCancelCallback(result, self, self->mCancelCallbackArg);
|
||||
}
|
||||
}
|
||||
|
||||
void DvdFileStream::Initialize_() {
|
||||
mCloseOnDestroy = false;
|
||||
mAllowClose = false;
|
||||
mIsOpen = false;
|
||||
mPriority = DVD_PRIO_MEDIUM;
|
||||
mIsBusy = false;
|
||||
mCallback = NULL;
|
||||
mCallbackArg = NULL;
|
||||
mResult = DVD_RESULT_OK;
|
||||
mCancelCallback = NULL;
|
||||
mIsCancelling = false;
|
||||
mCancelCallbackArg = NULL;
|
||||
mAsyncContext.stream = this;
|
||||
}
|
||||
|
||||
DvdFileStream::DvdFileStream(long entrynum) {
|
||||
Initialize_();
|
||||
Open(entrynum);
|
||||
}
|
||||
|
||||
DvdFileStream::DvdFileStream(const DVDFileInfo *info, bool close) {
|
||||
Initialize_();
|
||||
Open(info, close);
|
||||
}
|
||||
|
||||
DvdFileStream::~DvdFileStream() {
|
||||
if (mCloseOnDestroy) {
|
||||
Close();
|
||||
}
|
||||
}
|
||||
|
||||
bool DvdFileStream::Open(s32 entrynum) {
|
||||
if (mCloseOnDestroy) {
|
||||
Close();
|
||||
}
|
||||
|
||||
if (DVDFastOpen(entrynum, &mAsyncContext.info)) {
|
||||
mFilePosition.SetFileSize(mAsyncContext.info.size);
|
||||
mFilePosition.Seek(0, SEEK_BEG);
|
||||
|
||||
mCloseOnDestroy = true;
|
||||
mAllowClose = true;
|
||||
mIsOpen = true;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool DvdFileStream::Open(const DVDFileInfo *info, bool close) {
|
||||
if (mCloseOnDestroy) {
|
||||
Close();
|
||||
}
|
||||
|
||||
mAsyncContext.info = *info;
|
||||
mFilePosition.SetFileSize(mAsyncContext.info.size);
|
||||
mFilePosition.Seek(0, SEEK_BEG);
|
||||
|
||||
mCloseOnDestroy = false;
|
||||
mAllowClose = close;
|
||||
mIsOpen = true;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void DvdFileStream::Close() {
|
||||
if (mAllowClose && mIsOpen) {
|
||||
DVDClose(&mAsyncContext.info);
|
||||
mIsOpen = false;
|
||||
}
|
||||
}
|
||||
|
||||
long DvdFileStream::Read(void *dst, unsigned long size) {
|
||||
size = AdjustReadLength_(size);
|
||||
|
||||
long result = DVDReadPrio(&mAsyncContext.info, dst, size, mFilePosition.Tell(), mPriority);
|
||||
|
||||
if (result > 0) {
|
||||
mFilePosition.Skip(result);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
bool DvdFileStream::ReadAsync(void *dst, unsigned long size, AsyncCallback callback, void *arg) {
|
||||
size = AdjustReadLength_(size);
|
||||
|
||||
bool success = DvdFileStream::PeekAsync(dst, size, callback, arg);
|
||||
|
||||
if (success) {
|
||||
mFilePosition.Skip(size);
|
||||
} else {
|
||||
mIsBusy = false;
|
||||
}
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
long DvdFileStream::Peek(void *dst, unsigned long size) {
|
||||
size = AdjustReadLength_(size);
|
||||
|
||||
return DVDReadPrio(&mAsyncContext.info, dst, size, mFilePosition.Tell(), mPriority);
|
||||
}
|
||||
|
||||
bool DvdFileStream::PeekAsync(void *dst, unsigned long size, AsyncCallback callback, void *arg) {
|
||||
mCallback = callback;
|
||||
mCallbackArg = arg;
|
||||
mIsBusy = true;
|
||||
|
||||
size = AdjustReadLength_(size);
|
||||
|
||||
return DVDReadAsyncPrio(&mAsyncContext.info, dst, size, mFilePosition.Tell(), DvdAsyncCallback_, mPriority);
|
||||
}
|
||||
|
||||
void DvdFileStream::Seek(long offset, unsigned long origin) {
|
||||
mFilePosition.Seek(offset, origin);
|
||||
}
|
||||
|
||||
void DvdFileStream::Cancel() {
|
||||
DVDCancel(&mAsyncContext.info.block);
|
||||
}
|
||||
|
||||
bool DvdFileStream::CancelAsync(AsyncCallback callback, void *arg) {
|
||||
mCancelCallback = callback;
|
||||
mCancelCallbackArg = arg;
|
||||
|
||||
BOOL success = DVDCancelAsync(&mAsyncContext.info.block, DvdCBAsyncCallback_);
|
||||
|
||||
if (success) {
|
||||
mIsCancelling = true;
|
||||
}
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
unsigned long DvdFileStream::AdjustReadLength_(unsigned long len) {
|
||||
u32 fileOffset = mFilePosition.Tell();
|
||||
u32 fileSize = mFilePosition.GetFileSize();
|
||||
|
||||
u32 alignSize = RoundUp(fileSize, 32);
|
||||
u32 alignPos = RoundUp(fileOffset + len, 32);
|
||||
|
||||
if (alignPos > alignSize) {
|
||||
len = RoundUp(fileSize - fileOffset, 32);
|
||||
}
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
} // namespace ut
|
||||
} // namespace nw4r
|
||||
|
||||
@@ -0,0 +1,93 @@
|
||||
#include <RVL/OS.h>
|
||||
#include <nw4r/ut.h>
|
||||
|
||||
namespace nw4r {
|
||||
namespace ut {
|
||||
|
||||
NW4R_UT_RTTI_DEF_DERIVED(DvdLockedFileStream, DvdFileStream);
|
||||
|
||||
OSThreadQueue DvdLockedFileStream::sThreadQueue;
|
||||
OSMutex DvdLockedFileStream::sMutex;
|
||||
bool DvdLockedFileStream::sInitialized = false;
|
||||
|
||||
void DvdLockedFileStream::InitMutex_() {
|
||||
BOOL enabled = OSDisableInterrupts();
|
||||
|
||||
if (!sInitialized) {
|
||||
OSInitMutex(&sMutex);
|
||||
OSInitThreadQueue(&sThreadQueue);
|
||||
sInitialized = true;
|
||||
}
|
||||
|
||||
OSRestoreInterrupts(enabled);
|
||||
}
|
||||
|
||||
DvdLockedFileStream::DvdLockedFileStream(long entrynum) : DvdFileStream(entrynum) {
|
||||
mCancelFlag = false;
|
||||
InitMutex_();
|
||||
}
|
||||
|
||||
DvdLockedFileStream::DvdLockedFileStream(const DVDFileInfo *info, bool close) : DvdFileStream(info, close) {
|
||||
mCancelFlag = false;
|
||||
InitMutex_();
|
||||
}
|
||||
|
||||
DvdLockedFileStream::~DvdLockedFileStream() {}
|
||||
|
||||
void DvdLockedFileStream::Close() {
|
||||
DvdFileStream::Close();
|
||||
mCancelFlag = false;
|
||||
}
|
||||
|
||||
long DvdLockedFileStream::Read(void *dst, unsigned long size) {
|
||||
if (!LockMutex()) {
|
||||
return DVD_RESULT_CANCELED;
|
||||
}
|
||||
long ret = DvdFileStream::Read(dst, size);
|
||||
UnlockMutex();
|
||||
return ret;
|
||||
}
|
||||
|
||||
long DvdLockedFileStream::Peek(void *dst, unsigned long size) {
|
||||
if (!LockMutex()) {
|
||||
return DVD_RESULT_CANCELED;
|
||||
}
|
||||
long ret = DvdFileStream::Peek(dst, size);
|
||||
UnlockMutex();
|
||||
return ret;
|
||||
}
|
||||
|
||||
void DvdLockedFileStream::Cancel() {
|
||||
CancelMutex();
|
||||
DvdFileStream::Cancel();
|
||||
}
|
||||
|
||||
bool DvdLockedFileStream::LockMutex() {
|
||||
BOOL enabled = OSDisableInterrupts();
|
||||
while (!OSTryLockMutex(&sMutex)) {
|
||||
OSSleepThread(&sThreadQueue);
|
||||
if (mCancelFlag) {
|
||||
OSRestoreInterrupts(enabled);
|
||||
return false;
|
||||
}
|
||||
};
|
||||
OSRestoreInterrupts(enabled);
|
||||
return true;
|
||||
}
|
||||
|
||||
void DvdLockedFileStream::UnlockMutex() {
|
||||
BOOL enabled = OSDisableInterrupts();
|
||||
OSUnlockMutex(&sMutex);
|
||||
OSWakeupThread(&sThreadQueue);
|
||||
OSRestoreInterrupts(enabled);
|
||||
}
|
||||
|
||||
void DvdLockedFileStream::CancelMutex() {
|
||||
BOOL enabled = OSDisableInterrupts();
|
||||
mCancelFlag = true;
|
||||
OSWakeupThread(&sThreadQueue);
|
||||
OSRestoreInterrupts(enabled);
|
||||
}
|
||||
|
||||
} // namespace ut
|
||||
} // namespace nw4r
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
#include <nw4r/ut.h>
|
||||
|
||||
namespace nw4r {
|
||||
namespace ut {
|
||||
|
||||
void Font::InitReaderFunc(FontEncoding encode) {
|
||||
switch (encode) {
|
||||
case FONT_ENCODE_UTF8:
|
||||
mReadFunc = &CharStrmReader::ReadNextCharUTF8;
|
||||
break;
|
||||
case FONT_ENCODE_UTF16:
|
||||
mReadFunc = &CharStrmReader::ReadNextCharUTF16;
|
||||
break;
|
||||
case FONT_ENCODE_SJIS:
|
||||
mReadFunc = &CharStrmReader::ReadNextCharSJIS;
|
||||
break;
|
||||
case FONT_ENCODE_CP1252:
|
||||
default:
|
||||
mReadFunc = &CharStrmReader::ReadNextCharCP1252;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace ut
|
||||
} // namespace nw4r
|
||||
|
||||
@@ -9,7 +9,7 @@ bool IOStream::ReadAsync(void *dst, unsigned long size, AsyncCallback callback,
|
||||
return false;
|
||||
}
|
||||
|
||||
bool IOStream::Write(const void *src, unsigned long size) {
|
||||
long IOStream::Write(const void *src, unsigned long size) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,104 @@
|
||||
#include <nw4r/ut.h>
|
||||
|
||||
namespace nw4r {
|
||||
namespace ut {
|
||||
namespace {
|
||||
|
||||
class LCImpl {
|
||||
public:
|
||||
class Lock_ {
|
||||
public:
|
||||
Lock_(LCImpl &lc) : mMutex(lc.mMutex) {
|
||||
OSLockMutex(&mMutex);
|
||||
}
|
||||
~Lock_() {
|
||||
OSUnlockMutex(&mMutex);
|
||||
}
|
||||
|
||||
private:
|
||||
OSMutex &mMutex; // at 0x0
|
||||
};
|
||||
|
||||
public:
|
||||
LCImpl() : mIsEnabled(false) {
|
||||
OSInitMutex(&mMutex);
|
||||
}
|
||||
|
||||
void Enable() {
|
||||
Lock_ lock(*this);
|
||||
|
||||
if (!mIsEnabled) {
|
||||
LCEnable();
|
||||
mIsEnabled = true;
|
||||
}
|
||||
}
|
||||
|
||||
void Disable() {
|
||||
Lock_ lock(*this);
|
||||
|
||||
if (mIsEnabled) {
|
||||
LC::QueueWaitEx(0);
|
||||
LCDisable();
|
||||
mIsEnabled = false;
|
||||
}
|
||||
}
|
||||
|
||||
bool Lock() {
|
||||
OSLockMutex(&mMutex);
|
||||
|
||||
if (mIsEnabled) {
|
||||
LC::QueueWaitEx(0);
|
||||
return true;
|
||||
}
|
||||
|
||||
OSUnlockMutex(&mMutex);
|
||||
return false;
|
||||
}
|
||||
|
||||
void Unlock() {
|
||||
LC::QueueWaitEx(0);
|
||||
OSUnlockMutex(&mMutex);
|
||||
}
|
||||
|
||||
private:
|
||||
bool mIsEnabled; // at 0x0
|
||||
OSMutex mMutex; // at 0x4
|
||||
};
|
||||
|
||||
static LCImpl sLCImpl;
|
||||
|
||||
} // namespace
|
||||
|
||||
namespace LC {
|
||||
|
||||
void Enable() {
|
||||
sLCImpl.Enable();
|
||||
}
|
||||
|
||||
void Disable() {
|
||||
sLCImpl.Disable();
|
||||
}
|
||||
|
||||
bool Lock() {
|
||||
return sLCImpl.Lock();
|
||||
}
|
||||
|
||||
void Unlock() {
|
||||
sLCImpl.Unlock();
|
||||
}
|
||||
|
||||
void LoadBlocks(void *dst, void *src, unsigned long size) {
|
||||
LCLoadBlocks(dst, src, size);
|
||||
}
|
||||
|
||||
void StoreBlocks(void *dst, void *src, unsigned long size) {
|
||||
LCStoreBlocks(dst, src, size);
|
||||
}
|
||||
|
||||
void StoreData(void *dst, void *src, unsigned long size) {
|
||||
LCStoreData(dst, src, size);
|
||||
}
|
||||
|
||||
} // namespace LC
|
||||
} // namespace ut
|
||||
} // namespace nw4r
|
||||
|
||||
@@ -0,0 +1,178 @@
|
||||
|
||||
#include <nw4r/ut.h>
|
||||
|
||||
namespace nw4r {
|
||||
namespace ut {
|
||||
|
||||
NW4R_UT_RTTI_DEF_DERIVED(NandFileStream, FileStream);
|
||||
|
||||
void NandFileStream::NandAsyncCallback_(long result, NANDCommandBlock *block) {
|
||||
NandFileStream *self = reinterpret_cast<AsyncContext *>(block)->stream;
|
||||
|
||||
self->mIsBusy = false;
|
||||
self->mResult = result;
|
||||
|
||||
if (self->mCallback != NULL) {
|
||||
self->mCallback(result, self, self->mCallbackArg);
|
||||
}
|
||||
}
|
||||
|
||||
void NandFileStream::Initialize_() {
|
||||
mCanRead = false;
|
||||
mCanWrite = false;
|
||||
mCloseOnDestroy = false;
|
||||
mAllowClose = false;
|
||||
mIsOpen = false;
|
||||
mIsBusy = false;
|
||||
mCallback = NULL;
|
||||
mCallbackArg = NULL;
|
||||
mResult = NAND_RESULT_OK;
|
||||
mAsyncContext.stream = this;
|
||||
}
|
||||
|
||||
NandFileStream::NandFileStream(const char *path, unsigned long access) {
|
||||
Initialize_();
|
||||
Open(path, access);
|
||||
}
|
||||
|
||||
NandFileStream::NandFileStream(const NANDFileInfo *info, unsigned long access, bool close) {
|
||||
Initialize_();
|
||||
Open(info, access, close);
|
||||
}
|
||||
|
||||
NandFileStream::~NandFileStream() {
|
||||
if (mCloseOnDestroy) {
|
||||
Close();
|
||||
}
|
||||
}
|
||||
|
||||
bool NandFileStream::Open(const char *path, unsigned long access) {
|
||||
if (mCloseOnDestroy) {
|
||||
Close();
|
||||
}
|
||||
|
||||
mCanRead = access & NAND_ACCESS_READ;
|
||||
mCanWrite = access & NAND_ACCESS_WRITE;
|
||||
|
||||
if (NANDOpen(path, &mAsyncContext.info, access) != NAND_RESULT_OK) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (mCanRead) {
|
||||
unsigned long fileSize;
|
||||
|
||||
if (NANDGetLength(&mAsyncContext.info, &fileSize) != NAND_RESULT_OK) {
|
||||
NANDClose(&mAsyncContext.info);
|
||||
return false;
|
||||
}
|
||||
|
||||
mFilePosition.SetFileSize(fileSize);
|
||||
}
|
||||
|
||||
mFilePosition.Seek(0, SEEK_BEG);
|
||||
|
||||
mCloseOnDestroy = true;
|
||||
mAllowClose = true;
|
||||
mIsOpen = true;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool NandFileStream::Open(const NANDFileInfo *info, unsigned long access, bool close) {
|
||||
if (mCloseOnDestroy) {
|
||||
Close();
|
||||
}
|
||||
|
||||
mCanRead = access & NAND_ACCESS_READ;
|
||||
mCanWrite = access & NAND_ACCESS_WRITE;
|
||||
|
||||
mAsyncContext.info = *info;
|
||||
|
||||
unsigned long fileSize;
|
||||
if (mCanRead) {
|
||||
if (NANDGetLength(&mAsyncContext.info, &fileSize) != NAND_RESULT_OK) {
|
||||
if (close) {
|
||||
NANDClose(&mAsyncContext.info);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
mFilePosition.SetFileSize(fileSize);
|
||||
}
|
||||
mFilePosition.Seek(0, SEEK_BEG);
|
||||
|
||||
mCloseOnDestroy = false;
|
||||
mAllowClose = close;
|
||||
mIsOpen = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
void NandFileStream::Close() {
|
||||
if (mAllowClose && mIsOpen) {
|
||||
NANDClose(&mAsyncContext.info);
|
||||
mIsOpen = false;
|
||||
}
|
||||
}
|
||||
|
||||
long NandFileStream::Read(void *dst, unsigned long size) {
|
||||
NANDSeek(&mAsyncContext.info, mFilePosition.Tell(), NAND_SEEK_BEG);
|
||||
|
||||
long result = NANDRead(&mAsyncContext.info, dst, size);
|
||||
if (result > 0) {
|
||||
mFilePosition.Skip(result);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
bool NandFileStream::ReadAsync(void *dst, unsigned long size, AsyncCallback callback, void *arg) {
|
||||
mCallback = callback;
|
||||
mCallbackArg = arg;
|
||||
mIsBusy = true;
|
||||
|
||||
NANDSeek(&mAsyncContext.info, mFilePosition.Tell(), NAND_SEEK_BEG);
|
||||
|
||||
bool success =
|
||||
NANDReadAsync(&mAsyncContext.info, dst, size, NandAsyncCallback_, &mAsyncContext.block) == NAND_RESULT_OK;
|
||||
|
||||
if (success) {
|
||||
mFilePosition.Skip(size);
|
||||
} else {
|
||||
mIsBusy = false;
|
||||
}
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
long NandFileStream::Write(const void *src, unsigned long size) {
|
||||
NANDSeek(&mAsyncContext.info, mFilePosition.Tell(), NAND_SEEK_BEG);
|
||||
long result = NANDWrite(&mAsyncContext.info, src, size);
|
||||
if (result > 0) {
|
||||
mFilePosition.Append(result);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
bool NandFileStream::WriteAsync(const void *src, unsigned long size, AsyncCallback callback, void *arg) {
|
||||
mCallback = callback;
|
||||
mCallbackArg = arg;
|
||||
mIsBusy = true;
|
||||
|
||||
NANDSeek(&mAsyncContext.info, mFilePosition.Tell(), NAND_SEEK_BEG);
|
||||
|
||||
long result = NANDWriteAsync(&mAsyncContext.info, src, size, NandAsyncCallback_, &mAsyncContext.block);
|
||||
|
||||
if (result == NAND_RESULT_OK) {
|
||||
mFilePosition.Append(size);
|
||||
} else {
|
||||
mIsBusy = false;
|
||||
}
|
||||
|
||||
return result == NAND_RESULT_OK;
|
||||
}
|
||||
|
||||
void NandFileStream::Seek(long offset, unsigned long origin) {
|
||||
mFilePosition.Seek(offset, origin);
|
||||
}
|
||||
|
||||
} // namespace ut
|
||||
} // namespace nw4r
|
||||
|
||||
@@ -0,0 +1,121 @@
|
||||
#include <nw4r/ut.h>
|
||||
|
||||
const u32 MAGIC_RESFONT = 'RFNT';
|
||||
const u32 MAGIC_UNPACKED = 'RFNU';
|
||||
|
||||
const u32 MAGIC_FONTINFO = 'FINF';
|
||||
const u32 MAGIC_TEXGLYPH = 'TGLP';
|
||||
const u32 MAGIC_CHARWIDTH = 'CWDH';
|
||||
const u32 MAGIC_CHARMAP = 'CMAP';
|
||||
const u32 MAGIC_GLGR = 'GLGR';
|
||||
|
||||
namespace nw4r {
|
||||
namespace ut {
|
||||
namespace {
|
||||
|
||||
template <typename T>
|
||||
void ResolveOffset(T *&ptr, void *base) {
|
||||
ptr = reinterpret_cast<T *>(static_cast<char *>(base) + reinterpret_cast<ptrdiff_t>(ptr));
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
ResFont::ResFont() {}
|
||||
|
||||
ResFont::~ResFont() {}
|
||||
|
||||
bool ResFont::SetResource(void *buffer) {
|
||||
BinaryFileHeader *header = static_cast<BinaryFileHeader *>(buffer);
|
||||
FontInformation *info = NULL;
|
||||
|
||||
if (!IsManaging(NULL)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (header->magic == MAGIC_UNPACKED) {
|
||||
BinaryBlockHeader *block =
|
||||
reinterpret_cast<BinaryBlockHeader *>(reinterpret_cast<char *>(header) + header->headerSize);
|
||||
|
||||
for (int i = 0; i < header->numBlocks; i++) {
|
||||
if (block->magic == MAGIC_FONTINFO) {
|
||||
info = reinterpret_cast<FontInformation *>(block + 1);
|
||||
break;
|
||||
}
|
||||
|
||||
block = reinterpret_cast<BinaryBlockHeader *>(reinterpret_cast<char *>(block) + block->length);
|
||||
}
|
||||
} else {
|
||||
if (header->version == NW4R_VERSION(1, 04)) {
|
||||
if (!IsValidBinaryFile(header, MAGIC_RESFONT, NW4R_VERSION(1, 04), 2)) {
|
||||
return false;
|
||||
}
|
||||
} else if (!IsValidBinaryFile(header, MAGIC_RESFONT, NW4R_VERSION(1, 02), 2)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
info = Rebuild(header);
|
||||
}
|
||||
|
||||
if (info == NULL) {
|
||||
return false;
|
||||
}
|
||||
|
||||
SetResourceBuffer(header, info);
|
||||
InitReaderFunc(GetEncoding());
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void *ResFont::RemoveResource() {
|
||||
return ResFontBase::RemoveResourceBuffer();
|
||||
}
|
||||
|
||||
FontInformation *ResFont::Rebuild(BinaryFileHeader *header) {
|
||||
BinaryBlockHeader *block =
|
||||
reinterpret_cast<BinaryBlockHeader *>(reinterpret_cast<char *>(header) + header->headerSize);
|
||||
FontInformation *info = NULL;
|
||||
|
||||
for (int i = 0; i < header->numBlocks; i++) {
|
||||
switch (block->magic) {
|
||||
case MAGIC_FONTINFO:
|
||||
info = reinterpret_cast<FontInformation *>(block + 1);
|
||||
ResolveOffset<FontTextureGlyph>(info->fontGlyph, header);
|
||||
|
||||
if (info->fontWidth != 0) {
|
||||
ResolveOffset<FontWidth>(info->fontWidth, header);
|
||||
}
|
||||
|
||||
if (info->fontMap != 0) {
|
||||
ResolveOffset<FontCodeMap>(info->fontMap, header);
|
||||
}
|
||||
break;
|
||||
case MAGIC_TEXGLYPH:
|
||||
ResolveOffset<u8>(reinterpret_cast<FontTextureGlyph *>(block + 1)->sheetImage, header);
|
||||
break;
|
||||
case MAGIC_CHARWIDTH:
|
||||
FontWidth *width = reinterpret_cast<FontWidth *>(block + 1);
|
||||
if (width->next != 0) {
|
||||
ResolveOffset<FontWidth>(width->next, header);
|
||||
}
|
||||
break;
|
||||
case MAGIC_CHARMAP:
|
||||
FontCodeMap *map = reinterpret_cast<FontCodeMap *>(block + 1);
|
||||
if (map->next != 0) {
|
||||
ResolveOffset<FontCodeMap>(map->next, header);
|
||||
}
|
||||
break;
|
||||
case MAGIC_GLGR:
|
||||
break;
|
||||
default:
|
||||
return NULL;
|
||||
}
|
||||
|
||||
block = reinterpret_cast<BinaryBlockHeader *>(reinterpret_cast<char *>(block) + block->length);
|
||||
}
|
||||
|
||||
header->magic = MAGIC_UNPACKED;
|
||||
return info;
|
||||
}
|
||||
|
||||
} // namespace ut
|
||||
} // namespace nw4r
|
||||
|
||||
@@ -0,0 +1,221 @@
|
||||
#include <nw4r/ut.h>
|
||||
|
||||
namespace nw4r {
|
||||
namespace ut {
|
||||
namespace detail {
|
||||
|
||||
ResFontBase::ResFontBase() : mResource(NULL), mFontInfo(NULL), mLastCharCode(0), mLastGlyphIndex(-1) {}
|
||||
|
||||
ResFontBase::~ResFontBase() {}
|
||||
|
||||
void ResFontBase::SetResourceBuffer(void *buffer, FontInformation *info) {
|
||||
mResource = buffer;
|
||||
mFontInfo = info;
|
||||
}
|
||||
|
||||
void *ResFontBase::RemoveResourceBuffer() {
|
||||
void *pUserData = mResource;
|
||||
mFontInfo = NULL;
|
||||
mResource = NULL;
|
||||
return pUserData;
|
||||
}
|
||||
|
||||
int ResFontBase::GetWidth() const {
|
||||
return mFontInfo->width;
|
||||
}
|
||||
|
||||
int ResFontBase::GetHeight() const {
|
||||
return mFontInfo->height;
|
||||
}
|
||||
|
||||
int ResFontBase::GetAscent() const {
|
||||
return mFontInfo->ascent;
|
||||
}
|
||||
|
||||
int ResFontBase::GetDescent() const {
|
||||
return mFontInfo->height - mFontInfo->ascent;
|
||||
}
|
||||
|
||||
int ResFontBase::GetBaselinePos() const {
|
||||
return mFontInfo->fontGlyph->baselinePos;
|
||||
}
|
||||
|
||||
int ResFontBase::GetCellHeight() const {
|
||||
return mFontInfo->fontGlyph->cellHeight;
|
||||
}
|
||||
|
||||
int ResFontBase::GetCellWidth() const {
|
||||
return mFontInfo->fontGlyph->cellWidth;
|
||||
}
|
||||
|
||||
int ResFontBase::GetMaxCharWidth() const {
|
||||
return mFontInfo->fontGlyph->maxCharWidth;
|
||||
}
|
||||
|
||||
Font::Type ResFontBase::GetType() const {
|
||||
return TYPE_RESOURCE;
|
||||
}
|
||||
|
||||
GXTexFmt ResFontBase::GetTextureFormat() const {
|
||||
return static_cast<GXTexFmt>(mFontInfo->fontGlyph->sheetFormat);
|
||||
}
|
||||
|
||||
int ResFontBase::GetLineFeed() const {
|
||||
return mFontInfo->lineFeed;
|
||||
}
|
||||
|
||||
CharWidths ResFontBase::GetDefaultCharWidths() const {
|
||||
return mFontInfo->defaultWidth;
|
||||
}
|
||||
|
||||
void ResFontBase::SetDefaultCharWidths(const CharWidths &widths) {
|
||||
mFontInfo->defaultWidth = widths;
|
||||
}
|
||||
|
||||
bool ResFontBase::SetAlternateChar(u16 ch) {
|
||||
u32 index = FindGlyphIndex(ch);
|
||||
if (index != 0xFFFF) {
|
||||
mFontInfo->alterCharIndex = index;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void ResFontBase::SetLineFeed(int lf) {
|
||||
mFontInfo->lineFeed = lf;
|
||||
}
|
||||
|
||||
int ResFontBase::GetCharWidth(u16 ch) const {
|
||||
return GetCharWidths(ch).charWidth;
|
||||
}
|
||||
|
||||
CharWidths ResFontBase::GetCharWidths(u16 ch) const {
|
||||
u16 index = GetGlyphIndex(ch);
|
||||
return GetCharWidthsFromIndex(index);
|
||||
}
|
||||
|
||||
void ResFontBase::GetGlyph(Glyph *out, u16 ch) const {
|
||||
u16 index = GetGlyphIndex(ch);
|
||||
GetGlyphFromIndex(out, index);
|
||||
}
|
||||
|
||||
bool ResFontBase::HasGlyph(u16 c) const {
|
||||
return FindGlyphIndex(c) != 0xFFFF;
|
||||
}
|
||||
|
||||
FontEncoding ResFontBase::GetEncoding() const {
|
||||
return static_cast<FontEncoding>(mFontInfo->encoding);
|
||||
}
|
||||
|
||||
u16 ResFontBase::GetGlyphIndex(u16 c) const {
|
||||
u16 index = FindGlyphIndex(c);
|
||||
return (index != 0xFFFF) ? index : mFontInfo->alterCharIndex;
|
||||
}
|
||||
|
||||
u16 ResFontBase::FindGlyphIndex(u16 c) const {
|
||||
if (c == mLastCharCode) {
|
||||
return mLastGlyphIndex;
|
||||
}
|
||||
mLastCharCode = c;
|
||||
|
||||
for (FontCodeMap *it = mFontInfo->fontMap; it != NULL; it = it->next) {
|
||||
if (it->firstChar <= c && c <= it->lastChar) {
|
||||
mLastGlyphIndex = FindGlyphIndex(it, c);
|
||||
return mLastGlyphIndex;
|
||||
}
|
||||
}
|
||||
|
||||
mLastGlyphIndex = 0xFFFF;
|
||||
return mLastGlyphIndex;
|
||||
}
|
||||
|
||||
u16 ResFontBase::FindGlyphIndex(const FontCodeMap *map, u16 c) const {
|
||||
struct CMapScanEntry {
|
||||
u16 code; // at 0x0
|
||||
u16 index; // at 0x2
|
||||
};
|
||||
|
||||
struct CMapInfoScan {
|
||||
u16 num; // at 0x0
|
||||
CMapScanEntry entries[]; // at 0x2
|
||||
};
|
||||
|
||||
u16 index = 0xFFFF;
|
||||
|
||||
switch (map->mappingMethod) {
|
||||
case FONT_MAPMETHOD_LINEAR:
|
||||
index = map->mapInfo[0] + (c - map->firstChar);
|
||||
break;
|
||||
case FONT_MAPMETHOD_ARRAY:
|
||||
index = map->mapInfo[c - map->firstChar];
|
||||
break;
|
||||
case FONT_MAPMETHOD_SCAN:
|
||||
const CMapInfoScan *info = reinterpret_cast<const CMapInfoScan *>(map->mapInfo);
|
||||
|
||||
const CMapScanEntry *s = info->entries;
|
||||
const CMapScanEntry *e = &info->entries[info->num - 1];
|
||||
|
||||
while (s <= e) {
|
||||
const CMapScanEntry *m = s + (e - s) / 2;
|
||||
|
||||
if (m->code < c) {
|
||||
s = m + 1;
|
||||
} else if (c < m->code) {
|
||||
e = m - 1;
|
||||
} else {
|
||||
return m->index;
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
return index;
|
||||
}
|
||||
|
||||
const CharWidths &ResFontBase::GetCharWidthsFromIndex(u16 index) const {
|
||||
for (const FontWidth *it = mFontInfo->fontWidth; it != NULL; it = it->next) {
|
||||
if (it->firstChar <= index && index <= it->lastChar) {
|
||||
return GetCharWidthsFromIndex(it, index);
|
||||
}
|
||||
}
|
||||
|
||||
return mFontInfo->defaultWidth;
|
||||
}
|
||||
|
||||
const CharWidths &ResFontBase::GetCharWidthsFromIndex(const FontWidth *width, u16 index) const {
|
||||
return width->widthTable[index - width->firstChar];
|
||||
}
|
||||
|
||||
void ResFontBase::GetGlyphFromIndex(Glyph *out, u16 index) const {
|
||||
const FontTextureGlyph *texGlyph = mFontInfo->fontGlyph;
|
||||
|
||||
u32 cellsInASheet = texGlyph->sheetRow * texGlyph->sheetLine;
|
||||
|
||||
u32 glyphCell = index % cellsInASheet;
|
||||
u32 glyphSheet = index / cellsInASheet;
|
||||
|
||||
u32 unitX = glyphCell % texGlyph->sheetRow;
|
||||
u32 unitY = glyphCell / texGlyph->sheetRow;
|
||||
|
||||
u32 pixelX = unitX * (texGlyph->cellWidth + 1);
|
||||
u32 pixelY = unitY * (texGlyph->cellHeight + 1);
|
||||
|
||||
out->texture = texGlyph->sheetImage + (glyphSheet * texGlyph->sheetSize);
|
||||
|
||||
out->widths = GetCharWidthsFromIndex(index);
|
||||
out->height = texGlyph->cellHeight;
|
||||
|
||||
out->format = static_cast<GXTexFmt>(texGlyph->sheetFormat);
|
||||
|
||||
out->texWidth = texGlyph->sheetWidth;
|
||||
out->texHeight = texGlyph->sheetHeight;
|
||||
|
||||
out->cellX = pixelX + 1;
|
||||
out->cellY = pixelY + 1;
|
||||
}
|
||||
|
||||
} // namespace detail
|
||||
} // namespace ut
|
||||
} // namespace nw4r
|
||||
|
||||
@@ -0,0 +1,226 @@
|
||||
#include <nw4r/ut.h>
|
||||
|
||||
namespace nw4r {
|
||||
namespace ut {
|
||||
|
||||
u16 RomFont::mFontEncode = 0xFFFF;
|
||||
|
||||
namespace {
|
||||
|
||||
bool IsCP1252Char(u16 c) {
|
||||
return c >= 0x20 && c <= 0xFF;
|
||||
}
|
||||
|
||||
bool IsSJISHalfWidthChar(u16 c) {
|
||||
if (c > 0xFF) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return (c >= 0x20 && c <= 0x7E) || (c >= 0xA1 && c <= 0xDF);
|
||||
}
|
||||
|
||||
bool IsSJISFullWidthChar(u16 c) {
|
||||
u8 hi = BitExtract<u16>(c, 8, 8);
|
||||
u8 lo = BitExtract<u16>(c, 0, 8);
|
||||
|
||||
return hi >= 0x81 && hi <= 0x98 && lo >= 0x40 && lo <= 0xFC;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
RomFont::RomFont() : mFontHeader(NULL), mAlternateChar('?') {
|
||||
mDefaultWidths.leftSpacing = 0;
|
||||
mDefaultWidths.glyphWidth = 0;
|
||||
mDefaultWidths.charWidth = 0;
|
||||
}
|
||||
|
||||
RomFont::~RomFont() {}
|
||||
|
||||
bool RomFont::Load(void *buffer) {
|
||||
if (mFontHeader != NULL) {
|
||||
return false;
|
||||
}
|
||||
|
||||
BOOL success = OSInitFont(static_cast<OSFontHeader *>(buffer));
|
||||
|
||||
if (success) {
|
||||
mFontEncode = OSGetFontEncode();
|
||||
mFontHeader = static_cast<OSFontHeader *>(buffer);
|
||||
|
||||
mDefaultWidths.leftSpacing = 0;
|
||||
mDefaultWidths.glyphWidth = GetCellWidth();
|
||||
mDefaultWidths.charWidth = GetMaxCharWidth();
|
||||
|
||||
InitReaderFunc(GetEncoding());
|
||||
}
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
u32 RomFont::GetRequireBufferSize() {
|
||||
switch (OSGetFontEncode()) {
|
||||
case OS_FONT_ENCODE_ANSI:
|
||||
return 0x00020120;
|
||||
case OS_FONT_ENCODE_SJIS:
|
||||
return 0x00120F00;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int RomFont::GetWidth() const {
|
||||
return mFontHeader->width;
|
||||
}
|
||||
|
||||
int RomFont::GetHeight() const {
|
||||
return GetAscent() + GetDescent();
|
||||
}
|
||||
|
||||
int RomFont::GetAscent() const {
|
||||
return mFontHeader->ascent;
|
||||
}
|
||||
|
||||
int RomFont::GetDescent() const {
|
||||
return mFontHeader->descent;
|
||||
}
|
||||
|
||||
int RomFont::GetBaselinePos() const {
|
||||
return mFontHeader->ascent;
|
||||
}
|
||||
|
||||
int RomFont::GetCellHeight() const {
|
||||
return mFontHeader->cellHeight;
|
||||
}
|
||||
|
||||
int RomFont::GetCellWidth() const {
|
||||
return mFontHeader->cellWidth;
|
||||
}
|
||||
|
||||
int RomFont::GetMaxCharWidth() const {
|
||||
return mFontHeader->width;
|
||||
}
|
||||
|
||||
Font::Type RomFont::GetType() const {
|
||||
return TYPE_ROM;
|
||||
}
|
||||
|
||||
GXTexFmt RomFont::GetTextureFormat() const {
|
||||
return GX_TF_I4;
|
||||
}
|
||||
|
||||
int RomFont::GetLineFeed() const {
|
||||
return mFontHeader->leading;
|
||||
}
|
||||
|
||||
CharWidths RomFont::GetDefaultCharWidths() const {
|
||||
return mDefaultWidths;
|
||||
}
|
||||
|
||||
void RomFont::SetDefaultCharWidths(const CharWidths &widths) {
|
||||
mDefaultWidths = widths;
|
||||
}
|
||||
|
||||
bool RomFont::SetAlternateChar(u16 c) {
|
||||
const u16 prev = mAlternateChar;
|
||||
mAlternateChar = 0xFFFF;
|
||||
|
||||
u16 undef = HandleUndefinedChar(c);
|
||||
if (undef != 0xFFFF) {
|
||||
mAlternateChar = c;
|
||||
return true;
|
||||
} else {
|
||||
mAlternateChar = prev;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
void RomFont::SetLineFeed(int lf) {
|
||||
mFontHeader->leading = lf;
|
||||
}
|
||||
|
||||
int RomFont::GetCharWidth(u16 c) const {
|
||||
u32 width;
|
||||
char buffer[4];
|
||||
|
||||
MakeCharPtr(buffer, c);
|
||||
OSGetFontWidth(buffer, &width);
|
||||
|
||||
return width;
|
||||
}
|
||||
|
||||
CharWidths RomFont::GetCharWidths(u16 c) const {
|
||||
int width = GetCharWidth(c);
|
||||
|
||||
CharWidths widths;
|
||||
widths.leftSpacing = 0;
|
||||
widths.glyphWidth = width;
|
||||
widths.charWidth = width;
|
||||
|
||||
return widths;
|
||||
}
|
||||
|
||||
void RomFont::GetGlyph(Glyph *out, u16 c) const {
|
||||
void *texture;
|
||||
u32 x, y, width;
|
||||
char buffer[4];
|
||||
|
||||
MakeCharPtr(buffer, c);
|
||||
OSGetFontTexture(buffer, &texture, &x, &y, &width);
|
||||
|
||||
out->texture = texture;
|
||||
|
||||
out->widths.leftSpacing = 0;
|
||||
out->widths.glyphWidth = width;
|
||||
out->widths.charWidth = width;
|
||||
|
||||
out->height = mFontHeader->cellHeight;
|
||||
out->format = GX_TF_I4;
|
||||
|
||||
out->texWidth = mFontHeader->sheetWidth;
|
||||
out->texHeight = mFontHeader->sheetHeight;
|
||||
|
||||
out->cellX = x;
|
||||
out->cellY = y;
|
||||
}
|
||||
|
||||
FontEncoding RomFont::GetEncoding() const {
|
||||
switch (mFontEncode) {
|
||||
case OS_FONT_ENCODE_ANSI:
|
||||
return FONT_ENCODE_CP1252;
|
||||
case OS_FONT_ENCODE_SJIS:
|
||||
return FONT_ENCODE_SJIS;
|
||||
}
|
||||
|
||||
return FONT_ENCODE_CP1252;
|
||||
}
|
||||
|
||||
void RomFont::MakeCharPtr(char *buffer, u16 c) const {
|
||||
c = HandleUndefinedChar(c);
|
||||
|
||||
if (BitExtract<u16>(c, 8, 8) == 0) {
|
||||
buffer[0] = c & 0x00FF;
|
||||
buffer[1] = '\0';
|
||||
} else {
|
||||
buffer[0] = BitExtract<u16>(c, 8, 8);
|
||||
buffer[1] = c & 0x00FF;
|
||||
buffer[2] = '\0';
|
||||
}
|
||||
}
|
||||
|
||||
u16 RomFont::HandleUndefinedChar(u16 c) const {
|
||||
bool valid;
|
||||
|
||||
switch (mFontEncode) {
|
||||
case OS_FONT_ENCODE_ANSI:
|
||||
valid = IsCP1252Char(c);
|
||||
break;
|
||||
case OS_FONT_ENCODE_SJIS:
|
||||
valid = IsSJISHalfWidthChar(c) || IsSJISFullWidthChar(c);
|
||||
break;
|
||||
}
|
||||
|
||||
return valid ? c : mAlternateChar;
|
||||
}
|
||||
|
||||
} // namespace ut
|
||||
} // namespace nw4r
|
||||
@@ -0,0 +1,494 @@
|
||||
|
||||
#include <Runtime.PPCEABI.H/__va_arg.h>
|
||||
#include <nw4r/ut.h>
|
||||
|
||||
namespace nw4r {
|
||||
namespace ut {
|
||||
|
||||
template <typename T>
|
||||
T *TextWriterBase<T>::mFormatBuffer;
|
||||
template <typename T>
|
||||
int TextWriterBase<T>::mFormatBufferSize = 0x100;
|
||||
|
||||
template <typename T>
|
||||
TagProcessorBase<T> TextWriterBase<T>::mDefaultTagProcessor;
|
||||
|
||||
template <typename T>
|
||||
TextWriterBase<T>::TextWriterBase()
|
||||
: mCharSpace(0.0f), mWidthLimit(NW4R_MATH_FLT_MAX), mLineSpace(0.0f), mTabWidth(4), mDrawFlag(0),
|
||||
mTagProcessor(&mDefaultTagProcessor) {}
|
||||
|
||||
template <typename T>
|
||||
TextWriterBase<T>::~TextWriterBase() {}
|
||||
|
||||
template <typename T>
|
||||
void TextWriterBase<T>::SetLineHeight(f32 height) {
|
||||
const Font *font = GetFont();
|
||||
int lf = font != NULL ? font->GetLineFeed() : 0;
|
||||
mLineSpace = height - lf * GetScaleV();
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
f32 TextWriterBase<T>::GetLineHeight() const {
|
||||
const Font *font = GetFont();
|
||||
int lf = font != NULL ? font->GetLineFeed() : 0;
|
||||
return mLineSpace + GetScaleV() * lf;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
f32 TextWriterBase<T>::CalcFormatStringWidth(const T *format, ...) const {
|
||||
Rect rect;
|
||||
_va_list_struct args;
|
||||
|
||||
va_start(args, format);
|
||||
CalcVStringRect(&rect, format, &args);
|
||||
va_end(args);
|
||||
|
||||
return rect.GetWidth();
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
f32 TextWriterBase<T>::CalcFormatStringHeight(const T *format, ...) const {
|
||||
Rect rect;
|
||||
_va_list_struct args;
|
||||
|
||||
va_start(args, format);
|
||||
CalcVStringRect(&rect, format, &args);
|
||||
va_end(args);
|
||||
|
||||
return rect.GetHeight();
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void TextWriterBase<T>::CalcFormatStringRect(Rect *rect, const T *format, ...) const {
|
||||
_va_list_struct args;
|
||||
|
||||
va_start(args, format);
|
||||
CalcVStringRect(rect, format, &args);
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void TextWriterBase<T>::CalcVStringRect(Rect *rect, const T *format, va_list args) const {
|
||||
T *pBuffer;
|
||||
|
||||
if (mFormatBuffer != NULL) {
|
||||
pBuffer = mFormatBuffer;
|
||||
} else {
|
||||
pBuffer = static_cast<T *>(__alloca(mFormatBufferSize));
|
||||
}
|
||||
|
||||
int len = VSNPrintf(pBuffer, mFormatBufferSize, format, args);
|
||||
len = Min(len, mFormatBufferSize - 1);
|
||||
|
||||
CalcStringRect(rect, pBuffer, len);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
f32 TextWriterBase<T>::CalcStringWidth(const T *str, int len) const {
|
||||
Rect rect;
|
||||
CalcStringRect(&rect, str, len);
|
||||
return rect.GetWidth();
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
f32 TextWriterBase<T>::CalcStringHeight(const T *str, int len) const {
|
||||
Rect rect;
|
||||
CalcStringRect(&rect, str, len);
|
||||
return rect.GetHeight();
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void TextWriterBase<T>::CalcStringRect(Rect *rect, const T *str, int len) const {
|
||||
TextWriterBase<T> clone(*this);
|
||||
clone.CalcStringRectImpl(rect, str, len);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
f32 TextWriterBase<T>::Printf(const T *format, ...) {
|
||||
va_list args;
|
||||
va_start(args[0], format);
|
||||
f32 width = VPrintf(format, args);
|
||||
va_end(args[0]);
|
||||
return width;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
f32 TextWriterBase<T>::VPrintf(const T *str, va_list args) {
|
||||
T *pBuffer;
|
||||
|
||||
if (mFormatBuffer != NULL) {
|
||||
pBuffer = mFormatBuffer;
|
||||
} else {
|
||||
pBuffer = static_cast<T *>(__alloca(mFormatBufferSize));
|
||||
}
|
||||
|
||||
int len = VSNPrintf(pBuffer, mFormatBufferSize, str, args);
|
||||
len = Min(len, mFormatBufferSize - 1);
|
||||
return Print(pBuffer, len);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
f32 TextWriterBase<T>::Print(const T *str, int len) {
|
||||
TextWriterBase<T> clone(*this);
|
||||
|
||||
f32 width = clone.PrintImpl(str, len, false);
|
||||
SetCursor(clone.GetCursorX(), clone.GetCursorY());
|
||||
|
||||
return width;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
f32 TextWriterBase<T>::PrintfMutable(const T *format, ...) {
|
||||
va_list args;
|
||||
va_start(args[0], format);
|
||||
f32 width = VPrintfMutable(format, args);
|
||||
va_end(args[0]);
|
||||
return width;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
f32 TextWriterBase<T>::VPrintfMutable(const T *format, va_list args) {
|
||||
T *pBuffer;
|
||||
|
||||
if (mFormatBuffer != NULL) {
|
||||
pBuffer = mFormatBuffer;
|
||||
} else {
|
||||
pBuffer = static_cast<T *>(__alloca(mFormatBufferSize));
|
||||
}
|
||||
|
||||
int len = VSNPrintf(pBuffer, mFormatBufferSize, format, args);
|
||||
len = Min(len, mFormatBufferSize - 1);
|
||||
return PrintMutable(pBuffer, len);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
f32 TextWriterBase<T>::PrintMutable(const T *str, int len) {
|
||||
return PrintImpl(str, len, true);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
f32 TextWriterBase<T>::CalcLineWidth(const T *str, int len) {
|
||||
Rect rect;
|
||||
TextWriterBase<T> clone(*this);
|
||||
|
||||
clone.SetCursor(0.0f, 0.0f);
|
||||
clone.CalcLineRectImpl(&rect, &str, len);
|
||||
|
||||
return rect.GetWidth();
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
bool TextWriterBase<T>::CalcLineRectImpl(Rect *rect, const T **str, int len) {
|
||||
const T *strBegin = *str;
|
||||
const T *strEnd = strBegin + len;
|
||||
const bool useLimit = mWidthLimit < NW4R_MATH_FLT_MAX;
|
||||
|
||||
PrintContext<T> context = {this, strBegin};
|
||||
|
||||
f32 x = 0.0f;
|
||||
bool charSpace = false;
|
||||
bool overLimit = false;
|
||||
|
||||
const T *prevStream = NULL;
|
||||
Rect prevRect;
|
||||
|
||||
CharStrmReader reader = GetFont()->GetCharStrmReader();
|
||||
|
||||
rect->left = 0.0f;
|
||||
rect->right = 0.0f;
|
||||
rect->top = Min(0.0f, GetLineHeight());
|
||||
rect->bottom = Max(0.0f, GetLineHeight());
|
||||
prevRect = *rect;
|
||||
|
||||
reader.Set(strBegin);
|
||||
prevStream = NULL;
|
||||
|
||||
u16 ch = reader.Next();
|
||||
|
||||
while (static_cast<const T *>(reader.GetCurrentPos()) <= strEnd) {
|
||||
if (ch < ' ') {
|
||||
Rect r(x, 0.0f, 0.0f, 0.0f);
|
||||
context.str = static_cast<const T *>(reader.GetCurrentPos());
|
||||
context.flags = charSpace ? 0 : PRINTFLAGS_CHARSPACE;
|
||||
SetCursorX(x);
|
||||
|
||||
if (useLimit && ch != '\n' && prevStream != NULL) {
|
||||
PrintContext<T> context2 = context;
|
||||
TextWriterBase<T> clone(*this);
|
||||
|
||||
Rect r;
|
||||
context2.writer = &clone;
|
||||
mTagProcessor->CalcRect(&r, ch, &context2);
|
||||
|
||||
if (r.GetWidth() > 0.0f && clone.GetCursorX() - context.x > mWidthLimit) {
|
||||
overLimit = true;
|
||||
ch = '\n';
|
||||
reader.Set(prevStream);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
Operation oper = mTagProcessor->CalcRect(&r, ch, &context);
|
||||
reader.Set(context.str);
|
||||
|
||||
rect->left = Min(rect->left, r.left);
|
||||
rect->top = Min(rect->top, r.top);
|
||||
rect->right = Max(rect->right, r.right);
|
||||
rect->bottom = Max(rect->bottom, r.bottom);
|
||||
|
||||
x = GetCursorX();
|
||||
|
||||
if (oper == OPERATION_END_DRAW) {
|
||||
*str += len;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (oper == OPERATION_NO_CHAR_SPACE) {
|
||||
charSpace = false;
|
||||
} else if (oper == OPERATION_CHAR_SPACE) {
|
||||
charSpace = true;
|
||||
} else if (oper == OPERATION_NEXT_LINE) {
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
f32 dx = 0.0f;
|
||||
|
||||
if (charSpace) {
|
||||
dx += GetCharSpace();
|
||||
}
|
||||
|
||||
if (IsWidthFixed()) {
|
||||
dx += GetFixedWidth();
|
||||
} else {
|
||||
dx += GetFont()->GetCharWidth(ch) * GetScaleH();
|
||||
}
|
||||
|
||||
if (useLimit && prevStream != NULL && x + dx > mWidthLimit) {
|
||||
overLimit = true;
|
||||
ch = '\n';
|
||||
reader.Set(prevStream);
|
||||
continue;
|
||||
}
|
||||
|
||||
x += dx;
|
||||
rect->left = Min(rect->left, x);
|
||||
rect->right = Max(rect->right, x);
|
||||
|
||||
charSpace = true;
|
||||
}
|
||||
|
||||
if (useLimit) {
|
||||
prevStream = static_cast<const T *>(reader.GetCurrentPos());
|
||||
}
|
||||
|
||||
ch = reader.Next();
|
||||
}
|
||||
|
||||
*str = static_cast<const T *>(reader.GetCurrentPos());
|
||||
return overLimit;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void TextWriterBase<T>::CalcStringRectImpl(Rect *rect, const T *str, int len) {
|
||||
const T *end = str + len;
|
||||
int remain = len;
|
||||
|
||||
rect->left = 0.0f;
|
||||
rect->right = 0.0f;
|
||||
rect->top = 0.0f;
|
||||
rect->bottom = 0.0f;
|
||||
|
||||
SetCursor(0.0f, 0.0f);
|
||||
|
||||
do {
|
||||
Rect r;
|
||||
CalcLineRectImpl(&r, &str, remain);
|
||||
remain = end - str;
|
||||
|
||||
rect->left = Min(rect->left, r.left);
|
||||
rect->top = Min(rect->top, r.top);
|
||||
rect->right = Max(rect->right, r.right);
|
||||
rect->bottom = Max(rect->bottom, r.bottom);
|
||||
} while (remain > 0);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
f32 TextWriterBase<T>::PrintImpl(const T *str, int len, bool m) {
|
||||
f32 cursorX = GetCursorX();
|
||||
f32 cursorY = GetCursorY();
|
||||
|
||||
bool useLimit = mWidthLimit < NW4R_MATH_FLT_MAX;
|
||||
|
||||
f32 orgCursorX = cursorX;
|
||||
f32 orgCursorY = cursorY;
|
||||
|
||||
f32 cursorXAdj = 0.0f;
|
||||
f32 cursorYAdj = 0.0f;
|
||||
|
||||
bool charSpace = false;
|
||||
|
||||
const T *prevStream = str;
|
||||
const T *prevNewline = str;
|
||||
|
||||
f32 textWidth = AdjustCursor(&cursorX, &cursorY, str, len);
|
||||
|
||||
cursorXAdj = orgCursorX - GetCursorX();
|
||||
cursorYAdj = orgCursorY - GetCursorY();
|
||||
|
||||
PrintContext<T> context = {this, str, cursorX, cursorY};
|
||||
|
||||
CharStrmReader reader = GetFont()->GetCharStrmReader();
|
||||
reader.Set(str);
|
||||
|
||||
Operation oper;
|
||||
u16 ch = reader.Next();
|
||||
|
||||
while (static_cast<const T *>(reader.GetCurrentPos()) - str <= len) {
|
||||
if (ch < ' ') {
|
||||
context.str = static_cast<const T *>(reader.GetCurrentPos());
|
||||
context.flags = charSpace ? 0 : PRINTFLAGS_CHARSPACE;
|
||||
|
||||
if (useLimit && ch != '\n' && prevStream != prevNewline) {
|
||||
PrintContext<T> context2 = context;
|
||||
TextWriterBase<T> clone(*this);
|
||||
Rect rect;
|
||||
|
||||
context2.writer = &clone;
|
||||
oper = mTagProcessor->CalcRect(&rect, ch, &context2);
|
||||
|
||||
if (rect.GetWidth() > 0.0f && clone.GetCursorX() - context.x > mWidthLimit) {
|
||||
ch = '\n';
|
||||
reader.Set(prevStream);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
oper = mTagProcessor->Process(ch, &context);
|
||||
if (oper == OPERATION_NEXT_LINE) {
|
||||
if (IsDrawFlagSet(0x3, 0x1)) {
|
||||
int remain = len - (context.str - str);
|
||||
f32 width = CalcLineWidth(context.str, remain);
|
||||
f32 offset = (textWidth - width) / 2.0f;
|
||||
SetCursorX(context.x + offset);
|
||||
} else if (IsDrawFlagSet(0x3, 0x2)) {
|
||||
int remain = len - (context.str - str);
|
||||
f32 width = CalcLineWidth(context.str, remain);
|
||||
f32 offset = textWidth - width;
|
||||
SetCursorX(context.x + offset);
|
||||
} else {
|
||||
f32 width = GetCursorX() - context.x;
|
||||
textWidth = Max(textWidth, width);
|
||||
SetCursorX(context.x);
|
||||
}
|
||||
|
||||
if (useLimit) {
|
||||
prevNewline = static_cast<const T *>(reader.GetCurrentPos());
|
||||
}
|
||||
|
||||
charSpace = false;
|
||||
} else if (oper == OPERATION_NO_CHAR_SPACE) {
|
||||
charSpace = false;
|
||||
} else if (oper == OPERATION_CHAR_SPACE) {
|
||||
charSpace = true;
|
||||
} else if (oper == OPERATION_END_DRAW) {
|
||||
break;
|
||||
}
|
||||
|
||||
reader.Set(context.str);
|
||||
} else {
|
||||
f32 baseY = GetCursorY();
|
||||
if (useLimit && prevStream != prevNewline) {
|
||||
f32 baseX = GetCursorX();
|
||||
f32 space = charSpace ? GetCharSpace() : 0.0f;
|
||||
f32 width = IsWidthFixed() ? GetFixedWidth() : GetFont()->GetCharWidth(ch) * GetScaleH();
|
||||
if (baseX - cursorX + space + width > mWidthLimit) {
|
||||
ch = '\n';
|
||||
reader.Set(prevStream);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if (charSpace) {
|
||||
MoveCursorX(GetCharSpace());
|
||||
}
|
||||
|
||||
charSpace = true;
|
||||
|
||||
const Font *font = GetFont();
|
||||
f32 adj = -font->GetBaselinePos() * GetScaleV();
|
||||
MoveCursorY(adj);
|
||||
CharWriter::Print(ch);
|
||||
SetCursorY(baseY);
|
||||
}
|
||||
|
||||
if (useLimit) {
|
||||
prevStream = static_cast<const T *>(reader.GetCurrentPos());
|
||||
}
|
||||
|
||||
ch = reader.Next();
|
||||
}
|
||||
|
||||
f32 width = GetCursorX() - context.x;
|
||||
textWidth = Max(textWidth, width);
|
||||
|
||||
if (IsDrawFlagSet(0x300, 0x100) || IsDrawFlagSet(0x300, 0x200)) {
|
||||
SetCursorY(orgCursorY);
|
||||
} else {
|
||||
MoveCursorY(cursorYAdj);
|
||||
}
|
||||
|
||||
return textWidth;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
f32 TextWriterBase<T>::AdjustCursor(f32 *x1, f32 *y1, const T *str, int len) {
|
||||
f32 textWidth = 0.0f;
|
||||
f32 textHeight = 0.0f;
|
||||
|
||||
if (!IsDrawFlagSet(0x333, 0x300) && !IsDrawFlagSet(0x333, 0)) {
|
||||
Rect rect;
|
||||
CalcStringRect(&rect, str, len);
|
||||
|
||||
textWidth = rect.left + rect.right;
|
||||
textHeight = rect.top + rect.bottom;
|
||||
|
||||
if (textWidth > mWidthLimit) {
|
||||
textWidth = mWidthLimit;
|
||||
}
|
||||
}
|
||||
|
||||
if (IsDrawFlagSet(0x30, 0x10)) {
|
||||
*x1 -= textWidth / 2;
|
||||
} else if (IsDrawFlagSet(0x30, 0x20)) {
|
||||
*x1 -= textWidth;
|
||||
}
|
||||
|
||||
if (IsDrawFlagSet(0x300, 0x100)) {
|
||||
*y1 -= textHeight / 2;
|
||||
} else if (IsDrawFlagSet(0x300, 0x200)) {
|
||||
*y1 -= textHeight;
|
||||
}
|
||||
|
||||
if (IsDrawFlagSet(0x3, 0x1)) {
|
||||
SetCursorX(*x1 + (textWidth - CalcLineWidth(str, len)) / 2);
|
||||
} else if (IsDrawFlagSet(0x3, 0x2)) {
|
||||
SetCursorX(*x1 + (textWidth - CalcLineWidth(str, len)));
|
||||
} else {
|
||||
SetCursorX(*x1);
|
||||
}
|
||||
|
||||
if (IsDrawFlagSet(0x300, 0x300)) {
|
||||
SetCursorY(*y1);
|
||||
} else {
|
||||
SetCursorY(*y1 + GetFontAscent());
|
||||
}
|
||||
|
||||
return textWidth;
|
||||
}
|
||||
|
||||
template struct TextWriterBase<char>;
|
||||
template struct TextWriterBase<wchar_t>;
|
||||
|
||||
} // namespace ut
|
||||
} // namespace nw4r
|
||||
|
||||
Reference in New Issue
Block a user