diff options
| author | elijah-thomas774 <elijahthomas774@gmail.com> | 2024-05-05 22:27:50 -0400 |
|---|---|---|
| committer | elijah-thomas774 <elijahthomas774@gmail.com> | 2024-05-05 22:27:50 -0400 |
| commit | 344348bc0508cc132b77bfb81404bfdb99ae3fcf (patch) | |
| tree | 1a1eafebc8ad18fba08570d3f15b0031b25d733a /src | |
| parent | 126c5db9431bef2b6f032bce673d7c3d61e62aa3 (diff) | |
nw4r ut almost matching
Diffstat (limited to 'src')
| -rw-r--r-- | src/egg/core/eggAsyncDisplay.cpp | 4 | ||||
| -rw-r--r-- | src/nw4r/ut/ut_CharWriter.cpp | 260 | ||||
| -rw-r--r-- | src/nw4r/ut/ut_DvdFileStream.cpp | 180 | ||||
| -rw-r--r-- | src/nw4r/ut/ut_DvdLockedFileStream.cpp | 93 | ||||
| -rw-r--r-- | src/nw4r/ut/ut_Font.cpp | 25 | ||||
| -rw-r--r-- | src/nw4r/ut/ut_IOStream.cpp | 2 | ||||
| -rw-r--r-- | src/nw4r/ut/ut_LockedCache.cpp | 104 | ||||
| -rw-r--r-- | src/nw4r/ut/ut_NandFileStream.cpp | 178 | ||||
| -rw-r--r-- | src/nw4r/ut/ut_ResFont.cpp | 121 | ||||
| -rw-r--r-- | src/nw4r/ut/ut_ResFontBase.cpp | 221 | ||||
| -rw-r--r-- | src/nw4r/ut/ut_RomFont.cpp | 226 | ||||
| -rw-r--r-- | src/nw4r/ut/ut_TextWriterBase.cpp | 494 |
12 files changed, 1905 insertions, 3 deletions
diff --git a/src/egg/core/eggAsyncDisplay.cpp b/src/egg/core/eggAsyncDisplay.cpp index 0901271f..d2b4bf81 100644 --- a/src/egg/core/eggAsyncDisplay.cpp +++ b/src/egg/core/eggAsyncDisplay.cpp @@ -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); diff --git a/src/nw4r/ut/ut_CharWriter.cpp b/src/nw4r/ut/ut_CharWriter.cpp index e69de29b..1281dc01 100644 --- a/src/nw4r/ut/ut_CharWriter.cpp +++ b/src/nw4r/ut/ut_CharWriter.cpp @@ -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 diff --git a/src/nw4r/ut/ut_DvdFileStream.cpp b/src/nw4r/ut/ut_DvdFileStream.cpp index e69de29b..317bbc40 100644 --- a/src/nw4r/ut/ut_DvdFileStream.cpp +++ b/src/nw4r/ut/ut_DvdFileStream.cpp @@ -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 diff --git a/src/nw4r/ut/ut_DvdLockedFileStream.cpp b/src/nw4r/ut/ut_DvdLockedFileStream.cpp index e69de29b..eb18ac54 100644 --- a/src/nw4r/ut/ut_DvdLockedFileStream.cpp +++ b/src/nw4r/ut/ut_DvdLockedFileStream.cpp @@ -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 diff --git a/src/nw4r/ut/ut_Font.cpp b/src/nw4r/ut/ut_Font.cpp index e69de29b..0032e911 100644 --- a/src/nw4r/ut/ut_Font.cpp +++ b/src/nw4r/ut/ut_Font.cpp @@ -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 diff --git a/src/nw4r/ut/ut_IOStream.cpp b/src/nw4r/ut/ut_IOStream.cpp index dec6c090..01c7fed0 100644 --- a/src/nw4r/ut/ut_IOStream.cpp +++ b/src/nw4r/ut/ut_IOStream.cpp @@ -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; } diff --git a/src/nw4r/ut/ut_LockedCache.cpp b/src/nw4r/ut/ut_LockedCache.cpp index e69de29b..3972e471 100644 --- a/src/nw4r/ut/ut_LockedCache.cpp +++ b/src/nw4r/ut/ut_LockedCache.cpp @@ -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 diff --git a/src/nw4r/ut/ut_NandFileStream.cpp b/src/nw4r/ut/ut_NandFileStream.cpp index e69de29b..29c7a0b8 100644 --- a/src/nw4r/ut/ut_NandFileStream.cpp +++ b/src/nw4r/ut/ut_NandFileStream.cpp @@ -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 diff --git a/src/nw4r/ut/ut_ResFont.cpp b/src/nw4r/ut/ut_ResFont.cpp index e69de29b..1dfc9f3b 100644 --- a/src/nw4r/ut/ut_ResFont.cpp +++ b/src/nw4r/ut/ut_ResFont.cpp @@ -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 diff --git a/src/nw4r/ut/ut_ResFontBase.cpp b/src/nw4r/ut/ut_ResFontBase.cpp index e69de29b..40068a17 100644 --- a/src/nw4r/ut/ut_ResFontBase.cpp +++ b/src/nw4r/ut/ut_ResFontBase.cpp @@ -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 diff --git a/src/nw4r/ut/ut_RomFont.cpp b/src/nw4r/ut/ut_RomFont.cpp new file mode 100644 index 00000000..f3cb29f6 --- /dev/null +++ b/src/nw4r/ut/ut_RomFont.cpp @@ -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 diff --git a/src/nw4r/ut/ut_TextWriterBase.cpp b/src/nw4r/ut/ut_TextWriterBase.cpp index e69de29b..c3fc3032 100644 --- a/src/nw4r/ut/ut_TextWriterBase.cpp +++ b/src/nw4r/ut/ut_TextWriterBase.cpp @@ -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 |
