nw4r ut almost matching

This commit is contained in:
elijah-thomas774
2024-05-05 22:27:50 -04:00
parent 126c5db943
commit 344348bc05
39 changed files with 2477 additions and 346 deletions
+2 -2
View File
@@ -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);
+260
View File
@@ -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
+180
View File
@@ -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
+93
View File
@@ -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
+25
View File
@@ -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
+1 -1
View File
@@ -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;
}
+104
View File
@@ -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
+178
View File
@@ -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
+121
View File
@@ -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
+221
View File
@@ -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
+226
View File
@@ -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
+494
View File
@@ -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