1
// Copyright (c) 2012- PPSSPP Project.
3
// This program is free software: you can redistribute it and/or modify
4
// it under the terms of the GNU General Public License as published by
5
// the Free Software Foundation, version 2.0 or later versions.
7
// This program is distributed in the hope that it will be useful,
8
// but WITHOUT ANY WARRANTY; without even the implied warranty of
9
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10
// GNU General Public License 2.0 for more details.
12
// A copy of the GPL 2.0 should have been included with the program.
13
// If not, see http://www.gnu.org/licenses/
15
// Official git repository and contact information can be found at
16
// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.
20
#include "image/zim_load.h"
21
#include "image/png_load.h"
22
#include "util/text/utf8.h"
24
#include "Common/ChunkFile.h"
25
#include "Core/HDRemaster.h"
26
#include "Core/Host.h"
27
#include "GPU/ge_constants.h"
28
#include "GPU/GPUState.h"
29
#include "GPU/GPUInterface.h"
30
#include "Core/FileSystems/MetaFileSystem.h"
31
#include "Core/Util/PPGeDraw.h"
32
#include "Core/HLE/sceKernel.h"
33
#include "Core/HLE/sceKernelMemory.h"
34
#include "Core/HLE/sceGe.h"
35
#include "Core/MemMapHelpers.h"
36
#include "Core/System.h"
39
static int atlasWidth;
40
static int atlasHeight;
48
struct PPGeRemasterVertex {
54
static PSPPointer<PspGeListArgs> listArgs;
55
static u32 listArgsSize = sizeof(PspGeListArgs);
56
static u32 savedContextPtr;
57
static u32 savedContextSize = 512 * 4;
59
// Display list writer
61
static u32 dlWritePtr;
62
static u32 dlSize = 0x10000; // should be enough for a frame of gui...
65
static u32 dataWritePtr;
66
static u32 dataSize = 0x10000; // should be enough for a frame of gui...
68
static PSPPointer<u16_le> palette;
69
static u32 paletteSize = sizeof(u16) * 16;
72
static u32 vertexStart;
73
static u32 vertexCount;
75
// Used for formating text
76
struct AtlasCharVertex
83
struct AtlasTextMetrics
94
typedef std::vector<AtlasCharVertex> AtlasCharLine;
95
typedef std::vector<AtlasCharLine> AtlasLineArray;
97
static AtlasCharLine char_one_line;
98
static AtlasLineArray char_lines;
99
static AtlasTextMetrics char_lines_metrics;
101
//only 0xFFFFFF of data is used
102
static void WriteCmd(u8 cmd, u32 data) {
103
Memory::Write_U32((cmd << 24) | (data & 0xFFFFFF), dlWritePtr);
107
static void WriteCmdAddrWithBase(u8 cmd, u32 addr) {
108
WriteCmd(GE_CMD_BASE, (addr >> 8) & 0xFF0000);
109
WriteCmd(cmd, addr & 0xFFFFFF);
113
static void WriteCmdFloat(u8 cmd, float f) {
119
WriteCmd(cmd, conv.u >> 8);
122
static void BeginVertexData() {
124
vertexStart = dataWritePtr;
127
static void Vertex(float x, float y, float u, float v, int tw, int th, u32 color = 0xFFFFFFFF) {
128
if (g_RemasterMode) {
129
PPGeRemasterVertex vtx;
130
vtx.x = x - 0.5f; vtx.y = y - 0.5f; vtx.z = 0;
131
vtx.u = u * tw - 0.5f; vtx.v = v * th - 0.5f;
133
Memory::WriteStruct(dataWritePtr, &vtx);
134
dataWritePtr += sizeof(vtx);
137
vtx.x = x - 0.5f; vtx.y = y - 0.5f; vtx.z = 0;
138
vtx.u = u * tw - 0.5f; vtx.v = v * th - 0.5f;
140
Memory::WriteStruct(dataWritePtr, &vtx);
141
dataWritePtr += sizeof(vtx);
146
static void EndVertexDataAndDraw(int prim) {
147
WriteCmdAddrWithBase(GE_CMD_VADDR, vertexStart);
148
WriteCmd(GE_CMD_PRIM, (prim << 16) | vertexCount);
151
static u32 __PPGeDoAlloc(u32 &size, bool fromTop, const char *name) {
152
u32 ptr = kernelMemory.Alloc(size, fromTop, name);
159
void __PPGeSetupListArgs()
161
if (listArgs.IsValid())
164
listArgs = __PPGeDoAlloc(listArgsSize, false, "PPGe List Args");
165
if (listArgs.IsValid()) {
167
if (savedContextPtr == 0)
168
savedContextPtr = __PPGeDoAlloc(savedContextSize, false, "PPGe Saved Context");
169
listArgs->context = savedContextPtr;
175
// PPGe isn't really important for headless, and LoadZIM takes a long time.
176
if (PSP_CoreParameter().gpuCore == GPUCORE_NULL || host->ShouldSkipUI()) {
177
// Let's just not bother.
179
NOTICE_LOG(SCEGE, "Not initializing PPGe - GPU is NullGpu");
186
if (!LoadZIM("ppge_atlas.zim", width, height, &flags, imageData)) {
187
PanicAlert("Failed to load ppge_atlas.zim.\n\nPlace it in the directory \"assets\" under your PPSSPP directory.");
188
ERROR_LOG(SCEGE, "PPGe init failed - no atlas texture. PPGe stuff will not be drawn.");
192
u32 atlasSize = height[0] * width[0] / 2; // it's a 4-bit paletted texture in ram
193
atlasWidth = width[0];
194
atlasHeight = height[0];
195
dlPtr = __PPGeDoAlloc(dlSize, false, "PPGe Display List");
196
dataPtr = __PPGeDoAlloc(dataSize, false, "PPGe Vertex Data");
197
__PPGeSetupListArgs();
198
atlasPtr = __PPGeDoAlloc(atlasSize, false, "PPGe Atlas Texture");
199
palette = __PPGeDoAlloc(paletteSize, false, "PPGe Texture Palette");
201
// Generate 16-greyscale palette. All PPGe graphics are greyscale so we can use a tiny paletted texture.
202
for (int i = 0; i < 16; i++) {
204
palette[i] = (val << 12) | 0xFFF;
207
const u32_le *imagePtr = (u32_le *)imageData[0];
208
u8 *ramPtr = (u8 *)Memory::GetPointer(atlasPtr);
210
// Palettize to 4-bit, the easy way.
211
for (int i = 0; i < width[0] * height[0] / 2; i++) {
212
// Each pixel is 16 bits, so this loads two pixels.
214
// It's white anyway, so we only look at one channel of each pixel.
215
int a1 = (c & 0x0000000F) >> 0;
216
int a2 = (c & 0x000F0000) >> 16;
217
u8 cval = (a2 << 4) | a1;
223
DEBUG_LOG(SCEGE, "PPGe drawing library initialized. DL: %08x Data: %08x Atlas: %08x (%i) Args: %08x",
224
dlPtr, dataPtr, atlasPtr, atlasSize, listArgs.ptr);
227
void __PPGeDoState(PointerWrap &p)
229
auto s = p.Section("PPGeDraw", 1, 2);
238
p.Do(savedContextPtr);
239
p.Do(savedContextSize);
259
p.Do(char_lines_metrics);
262
void __PPGeShutdown()
265
kernelMemory.Free(atlasPtr);
267
kernelMemory.Free(dataPtr);
269
kernelMemory.Free(dlPtr);
270
if (listArgs.IsValid())
271
kernelMemory.Free(listArgs.ptr);
273
kernelMemory.Free(savedContextPtr);
275
kernelMemory.Free(palette.ptr);
289
// Reset write pointers to start of command and data buffers.
291
dataWritePtr = dataPtr;
293
// Set up the correct states for UI drawing
294
WriteCmd(GE_CMD_OFFSETADDR, 0);
295
WriteCmd(GE_CMD_ALPHABLENDENABLE, 1);
296
WriteCmd(GE_CMD_BLENDMODE, 2 | (3 << 4));
297
WriteCmd(GE_CMD_ALPHATESTENABLE, 0);
298
WriteCmd(GE_CMD_COLORTESTENABLE, 0);
299
WriteCmd(GE_CMD_ZTESTENABLE, 0);
300
WriteCmd(GE_CMD_LIGHTINGENABLE, 0);
301
WriteCmd(GE_CMD_FOGENABLE, 0);
302
WriteCmd(GE_CMD_STENCILTESTENABLE, 0);
303
WriteCmd(GE_CMD_CULLFACEENABLE, 0);
304
WriteCmd(GE_CMD_CLEARMODE, 0); // Normal mode
305
WriteCmd(GE_CMD_MASKRGB, 0);
306
WriteCmd(GE_CMD_MASKALPHA, 0);
308
PPGeSetDefaultTexture();
310
WriteCmd(GE_CMD_SCISSOR1, (0 << 10) | 0);
311
WriteCmd(GE_CMD_SCISSOR2, (271 << 10) | 479);
312
WriteCmd(GE_CMD_MINZ, 0);
313
WriteCmd(GE_CMD_MAXZ, 0xFFFF);
315
// Through mode, so we don't have to bother with matrices
316
if (g_RemasterMode) {
317
WriteCmd(GE_CMD_VERTEXTYPE, GE_VTYPE_TC_FLOAT | GE_VTYPE_COL_8888 | GE_VTYPE_POS_FLOAT | GE_VTYPE_THROUGH);
319
WriteCmd(GE_CMD_VERTEXTYPE, GE_VTYPE_TC_16BIT | GE_VTYPE_COL_8888 | GE_VTYPE_POS_FLOAT | GE_VTYPE_THROUGH);
328
WriteCmd(GE_CMD_FINISH, 0);
329
WriteCmd(GE_CMD_END, 0);
331
// Might've come from an old savestate.
332
__PPGeSetupListArgs();
334
if (dataWritePtr > dataPtr) {
335
// We actually drew something
336
gpu->EnableInterrupts(false);
337
u32 list = sceGeListEnQueue(dlPtr, dlWritePtr, -1, listArgs.ptr);
338
DEBUG_LOG(SCEGE, "PPGe enqueued display list %i", list);
339
gpu->EnableInterrupts(true);
343
static const AtlasChar *PPGeGetChar(const AtlasFont &atlasfont, unsigned int cval)
345
const AtlasChar *c = atlasfont.getChar(cval);
347
// Try to use a replacement character, these come from the below table.
348
// http://unicode.org/cldr/charts/supplemental/character_fallback_substitutions.html
350
case 0x00A0: // NO-BREAK SPACE
351
case 0x2000: // EN QUAD
352
case 0x2001: // EM QUAD
353
case 0x2002: // EN SPACE
354
case 0x2003: // EM SPACE
355
case 0x2004: // THREE-PER-EM SPACE
356
case 0x2005: // FOUR-PER-EM SPACE
357
case 0x2006: // SIX-PER-EM SPACE
358
case 0x2007: // FIGURE SPACE
359
case 0x2008: // PUNCTUATION SPACE
360
case 0x2009: // THIN SPACE
361
case 0x200A: // HAIR SPACE
362
case 0x202F: // NARROW NO-BREAK SPACE
363
case 0x205F: // MEDIUM MATHEMATICAL
364
case 0x3000: // IDEOGRAPHIC SPACE
365
c = atlasfont.getChar(0x0020);
369
c = atlasfont.getChar(0xFFFD);
373
c = atlasfont.getChar('?');
378
// Break a single text string into mutiple lines.
379
static AtlasTextMetrics BreakLines(const char *text, const AtlasFont &atlasfont, float x, float y,
380
int align, float scale, int wrapType, float wrapWidth, bool dryRun)
382
y += atlasfont.ascend * scale;
383
float sx = x, sy = y;
385
// TODO: Can we wrap it smartly on the screen edge?
386
if (wrapWidth <= 0) {
390
// used for replacing with ellipses
391
float wrapCutoff = 8.0f;
392
const AtlasChar *dot = PPGeGetChar(atlasfont, '.');
394
wrapCutoff = dot->wx * scale * 3.0f;
396
float threshold = sx + wrapWidth - wrapCutoff;
398
//const float wrapGreyZone = 2.0f; // Grey zone for punctuations at line ends
402
float lineHeight = atlasfont.height * scale;
403
for (UTF8 utf(text); !utf.end(); )
406
bool skipRest = false;
411
float spaceWidth = 0;
413
bool finished = false;
414
while (!utfWord.end() && !finished)
416
UTF8 utfPrev = utfWord;
417
u32 cval = utfWord.next();
418
const AtlasChar *ch = PPGeGetChar(atlasfont, cval);
424
// TODO: This list of punctuation is very incomplete.
431
case 0x3001: // IDEOGRAPHIC COMMA
432
case 0x3002: // IDEOGRAPHIC FULL STOP
433
case 0x06D4: // ARABIC FULL STOP
434
case 0xFF01: // FULLWIDTH EXCLAMATION MARK
435
case 0xFF09: // FULLWIDTH RIGHT PARENTHESIS
436
case 0xFF1F: // FULLWIDTH QUESTION MARK
437
// Count this character (punctuation is so clingy), but then we're done.
439
nextWidth += ch->wx * scale;
444
case 0x3000: // IDEOGRAPHIC SPACE
445
spaceWidth += ch->wx * scale;
452
// Ignore this character and we're done.
458
// CJK characters can be wrapped more freely.
459
bool isCJK = (cval >= 0x1100 && cval <= 0x11FF); // Hangul Jamo.
460
isCJK = isCJK || (cval >= 0x2E80 && cval <= 0x2FFF); // Kangxi Radicals etc.
462
isCJK = isCJK || (cval >= 0x3040 && cval <= 0x31FF); // Hiragana, Katakana, Hangul Compatibility Jamo etc.
463
isCJK = isCJK || (cval >= 0x3200 && cval <= 0x32FF); // CJK Enclosed
464
isCJK = isCJK || (cval >= 0x3300 && cval <= 0x33FF); // CJK Compatibility
465
isCJK = isCJK || (cval >= 0x3400 && cval <= 0x4DB5); // CJK Unified Ideographs Extension A
467
isCJK = isCJK || (cval >= 0x3040 && cval <= 0x4DB5); // Above collapsed
469
isCJK = isCJK || (cval >= 0x4E00 && cval <= 0x9FBB); // CJK Unified Ideographs
470
isCJK = isCJK || (cval >= 0xAC00 && cval <= 0xD7AF); // Hangul Syllables
471
isCJK = isCJK || (cval >= 0xF900 && cval <= 0xFAD9); // CJK Compatibility Ideographs
472
isCJK = isCJK || (cval >= 0x20000 && cval <= 0x2A6D6); // CJK Unified Ideographs Extension B
473
isCJK = isCJK || (cval >= 0x2F800 && cval <= 0x2FA1D); // CJK Compatibility Supplement
483
nextWidth += ch->wx * scale;
488
bool useEllipsis = false;
491
if (lineWidth + nextWidth > wrapWidth || skipRest)
493
if (wrapType & PPGE_LINE_WRAP_WORD) {
494
// TODO: Should check if we have had at least one other word instead.
500
if (wrapType & PPGE_LINE_USE_ELLIPSIS) {
504
} else if (nextWidth < wrapCutoff) {
505
// The word is too short, so just backspace!
510
lineWidth = wrapWidth;
514
for (int i = 0; i < numChars; ++i)
516
u32 cval = utf.next();
517
const AtlasChar *c = PPGeGetChar(atlasfont, cval);
520
if (useEllipsis && x >= threshold && dot)
525
// Replace the following part with an ellipsis.
526
cv.x = x + dot->ox * scale;
527
cv.y = y + dot->oy * scale;
529
char_one_line.push_back(cv);
530
cv.x += dot->wx * scale;
531
char_one_line.push_back(cv);
532
cv.x += dot->wx * scale;
533
char_one_line.push_back(cv);
541
cv.x = x + c->ox * scale;
542
cv.y = y + c->oy * scale;
544
char_one_line.push_back(cv);
549
lineWidth += nextWidth;
551
u32 cval = utf.next();
556
// No need to check c.
557
const AtlasChar *c = PPGeGetChar(atlasfont, cval);
559
cv.x = x + c->ox * scale;
560
cv.y = y + c->oy * scale;
562
char_one_line.push_back(cv);
565
lineWidth += spaceWidth;
566
if (wrapType > 0 && lineWidth > wrapWidth) {
567
lineWidth = wrapWidth;
570
else if (cval == '\n') {
578
if (lineWidth > maxw) {
583
char_lines.push_back(char_one_line);
584
char_one_line.clear();
588
const float w = maxw;
589
const float h = (float)numLines * lineHeight;
594
for (auto i = char_lines.begin(); i != char_lines.end(); ++i)
596
for (auto j = i->begin(); j != i->end(); ++j)
598
if (align & PPGE_ALIGN_HCENTER) j->x -= w / 2.0f;
599
else if (align & PPGE_ALIGN_RIGHT) j->x -= w;
601
if (align & PPGE_ALIGN_VCENTER) j->y -= h / 2.0f;
602
else if (align & PPGE_ALIGN_BOTTOM) j->y -= h;
606
if (align & PPGE_ALIGN_HCENTER) sx -= w / 2.0f;
607
else if (align & PPGE_ALIGN_RIGHT) sx -= w;
608
if (align & PPGE_ALIGN_VCENTER) sy -= h / 2.0f;
609
else if (align & PPGE_ALIGN_BOTTOM) sy -= h;
612
AtlasTextMetrics metrics = { sx, sy, w, lineHeight, scale, numLines };
616
void PPGeMeasureText(float *w, float *h, int *n,
617
const char *text, float scale, int WrapType, int wrapWidth)
619
const AtlasFont &atlasfont = *ppge_atlas.fonts[0];
620
AtlasTextMetrics metrics = BreakLines(text, atlasfont, 0, 0, 0, scale, WrapType, wrapWidth, true);
621
if (w) *w = metrics.maxWidth;
622
if (h) *h = metrics.lineHeight;
623
if (n) *n = metrics.numLines;
626
void PPGePrepareText(const char *text, float x, float y, int align, float scale, int WrapType, int wrapWidth)
628
const AtlasFont &atlasfont = *ppge_atlas.fonts[0];
629
char_lines_metrics = BreakLines(text, atlasfont, x, y, align, scale, WrapType, wrapWidth, false);
632
void PPGeMeasureCurrentText(float *x, float *y, float *w, float *h, int *n)
634
if (x) *x = char_lines_metrics.x;
635
if (y) *y = char_lines_metrics.y;
636
if (w) *w = char_lines_metrics.maxWidth;
637
if (h) *h = char_lines_metrics.lineHeight;
638
if (n) *n = char_lines_metrics.numLines;
641
// Draws some text using the one font we have.
643
void PPGeDrawCurrentText(u32 color)
647
float scale = char_lines_metrics.scale;
649
for (auto i = char_lines.begin(); i != char_lines.end(); ++i)
651
for (auto j = i->begin(); j != i->end(); ++j)
655
const AtlasChar &c = *j->c;
656
float cx2 = cx1 + c.pw * scale;
657
float cy2 = cy1 + c.ph * scale;
658
Vertex(cx1, cy1, c.sx, c.sy, atlasWidth, atlasHeight, color);
659
Vertex(cx2, cy2, c.ex, c.ey, atlasWidth, atlasHeight, color);
662
EndVertexDataAndDraw(GE_PRIM_RECTANGLES);
664
char_one_line.clear();
666
AtlasTextMetrics zeroBox = { 0 };
667
char_lines_metrics = zeroBox;
670
void PPGeDrawText(const char *text, float x, float y, int align, float scale, u32 color)
672
PPGePrepareText(text, x, y, align, scale, PPGE_LINE_USE_ELLIPSIS);
673
PPGeDrawCurrentText(color);
676
void PPGeDrawTextWrapped(const char *text, float x, float y, float wrapWidth, int align, float scale, u32 color)
678
PPGePrepareText(text, x, y, align, scale, PPGE_LINE_USE_ELLIPSIS | PPGE_LINE_WRAP_WORD, wrapWidth);
679
PPGeDrawCurrentText(color);
682
// Draws a "4-patch" for button-like things that can be resized
683
void PPGeDraw4Patch(int atlasImage, float x, float y, float w, float h, u32 color)
687
const AtlasImage &img = ppge_images[atlasImage];
688
float borderx = img.w / 20;
689
float bordery = img.h / 20;
690
float u1 = img.u1, uhalf = (img.u1 + img.u2) / 2, u2 = img.u2;
691
float v1 = img.v1, vhalf = (img.v1 + img.v2) / 2, v2 = img.v2;
692
float xmid1 = x + borderx;
693
float xmid2 = x + w - borderx;
694
float ymid1 = y + bordery;
695
float ymid2 = y + h - bordery;
700
Vertex(x, y, u1, v1, atlasWidth, atlasHeight, color);
701
Vertex(xmid1, ymid1, uhalf, vhalf, atlasWidth, atlasHeight, color);
702
Vertex(xmid1, y, uhalf, v1, atlasWidth, atlasHeight, color);
703
Vertex(xmid2, ymid1, uhalf, vhalf, atlasWidth, atlasHeight, color);
704
Vertex(xmid2, y, uhalf, v1, atlasWidth, atlasHeight, color);
705
Vertex(x2, ymid1, u2, vhalf, atlasWidth, atlasHeight, color);
707
Vertex(x, ymid1, u1, vhalf, atlasWidth, atlasHeight, color);
708
Vertex(xmid1, ymid2, uhalf, vhalf, atlasWidth, atlasHeight, color);
709
Vertex(xmid1, ymid1, uhalf, vhalf, atlasWidth, atlasHeight, color);
710
Vertex(xmid2, ymid2, uhalf, vhalf, atlasWidth, atlasHeight, color);
711
Vertex(xmid2, ymid1, uhalf, vhalf, atlasWidth, atlasHeight, color);
712
Vertex(x2, ymid2, u2, v2, atlasWidth, atlasHeight, color);
714
Vertex(x, ymid2, u1, vhalf, atlasWidth, atlasHeight, color);
715
Vertex(xmid1, y2, uhalf, v2, atlasWidth, atlasHeight, color);
716
Vertex(xmid1, ymid2, uhalf, vhalf, atlasWidth, atlasHeight, color);
717
Vertex(xmid2, y2, uhalf, v2, atlasWidth, atlasHeight, color);
718
Vertex(xmid2, ymid2, uhalf, vhalf, atlasWidth, atlasHeight, color);
719
Vertex(x2, y2, u2, v2, atlasWidth, atlasHeight, color);
720
EndVertexDataAndDraw(GE_PRIM_RECTANGLES);
723
void PPGeDrawRect(float x1, float y1, float x2, float y2, u32 color)
728
WriteCmd(GE_CMD_TEXTUREMAPENABLE, 0);
731
Vertex(x1, y1, 0, 0, 0, 0, color);
732
Vertex(x2, y2, 0, 0, 0, 0, color);
733
EndVertexDataAndDraw(GE_PRIM_RECTANGLES);
735
WriteCmd(GE_CMD_TEXTUREMAPENABLE, 1);
738
// Just blits an image to the screen, multiplied with the color.
739
void PPGeDrawImage(int atlasImage, float x, float y, int align, u32 color)
744
const AtlasImage &img = ppge_atlas.images[atlasImage];
748
Vertex(x, y, img.u1, img.v1, atlasWidth, atlasHeight, color);
749
Vertex(x + w, y + h, img.u2, img.v2, atlasWidth, atlasHeight, color);
750
EndVertexDataAndDraw(GE_PRIM_RECTANGLES);
753
void PPGeDrawImage(int atlasImage, float x, float y, float w, float h, int align, u32 color)
758
const AtlasImage &img = ppge_atlas.images[atlasImage];
760
Vertex(x, y, img.u1, img.v1, atlasWidth, atlasHeight, color);
761
Vertex(x + w, y + h, img.u2, img.v2, atlasWidth, atlasHeight, color);
762
EndVertexDataAndDraw(GE_PRIM_RECTANGLES);
765
void PPGeDrawImage(float x, float y, float w, float h, float u1, float v1, float u2, float v2, int tw, int th, u32 color)
770
Vertex(x, y, u1, v1, tw, th, color);
771
Vertex(x + w, y + h, u2, v2, tw, th, color);
772
EndVertexDataAndDraw(GE_PRIM_RECTANGLES);
775
// Return a value such that (1 << value) >= x
779
int ret = 31 - __builtin_clz(x|1);
783
while ((1 << ret) < x)
789
void PPGeSetDefaultTexture()
791
WriteCmd(GE_CMD_TEXTUREMAPENABLE, 1);
792
int wp2 = GetPow2(atlasWidth);
793
int hp2 = GetPow2(atlasHeight);
794
WriteCmd(GE_CMD_CLUTADDR, palette.ptr & 0xFFFFF0);
795
WriteCmd(GE_CMD_CLUTADDRUPPER, (palette.ptr & 0xFF000000) >> 8);
796
WriteCmd(GE_CMD_CLUTFORMAT, 0x00FF02);
797
WriteCmd(GE_CMD_LOADCLUT, 2);
798
WriteCmd(GE_CMD_TEXSIZE0, wp2 | (hp2 << 8));
799
WriteCmd(GE_CMD_TEXMAPMODE, 0 | (1 << 8));
800
WriteCmd(GE_CMD_TEXMODE, 0);
801
WriteCmd(GE_CMD_TEXFORMAT, GE_TFMT_CLUT4); // 4-bit CLUT
802
WriteCmd(GE_CMD_TEXFILTER, (1 << 8) | 1); // mag = LINEAR min = LINEAR
803
WriteCmd(GE_CMD_TEXWRAP, (1 << 8) | 1); // clamp texture wrapping
804
WriteCmd(GE_CMD_TEXFUNC, (0 << 16) | (1 << 8) | 0); // RGBA texture reads, modulate, no color doubling
805
WriteCmd(GE_CMD_TEXADDR0, atlasPtr & 0xFFFFF0);
806
WriteCmd(GE_CMD_TEXBUFWIDTH0, atlasWidth | ((atlasPtr & 0xFF000000) >> 8));
807
WriteCmd(GE_CMD_TEXFLUSH, 0);
810
void PPGeSetTexture(u32 dataAddr, int width, int height)
812
WriteCmd(GE_CMD_TEXTUREMAPENABLE, 1);
813
int wp2 = GetPow2(width);
814
int hp2 = GetPow2(height);
815
WriteCmd(GE_CMD_TEXSIZE0, wp2 | (hp2 << 8));
816
WriteCmd(GE_CMD_TEXMAPMODE, 0 | (1 << 8));
817
WriteCmd(GE_CMD_TEXMODE, 0);
818
WriteCmd(GE_CMD_TEXFORMAT, GE_TFMT_8888); // 4444
819
WriteCmd(GE_CMD_TEXFILTER, (1 << 8) | 1); // mag = LINEAR min = LINEAR
820
WriteCmd(GE_CMD_TEXWRAP, (1 << 8) | 1); // clamp texture wrapping
821
WriteCmd(GE_CMD_TEXFUNC, (0 << 16) | (1 << 8) | 0); // RGBA texture reads, modulate, no color doubling
822
WriteCmd(GE_CMD_TEXADDR0, dataAddr & 0xFFFFF0);
823
WriteCmd(GE_CMD_TEXBUFWIDTH0, width | ((dataAddr & 0xFF000000) >> 8));
824
WriteCmd(GE_CMD_TEXFLUSH, 0);
827
void PPGeDisableTexture()
829
WriteCmd(GE_CMD_TEXTUREMAPENABLE, 0);
832
std::vector<PPGeImage *> PPGeImage::loadedTextures_;
834
PPGeImage::PPGeImage(const std::string &pspFilename)
835
: filename_(pspFilename), texture_(0) {
838
PPGeImage::PPGeImage(u32 pngPointer, size_t pngSize)
839
: filename_(""), png_(pngPointer), size_(pngSize), texture_(0) {
842
PPGeImage::~PPGeImage() {
846
bool PPGeImage::Load() {
849
// In case it fails to load.
853
unsigned char *textureData;
855
if (filename_.empty()) {
856
success = pngLoadPtr(Memory::GetPointer(png_), size_, &width_, &height_, &textureData, false);
858
std::vector<u8> pngData;
859
if (pspFileSystem.ReadEntireFile(filename_, pngData) < 0) {
860
WARN_LOG(SCEGE, "Bad PPGeImage - cannot load file");
864
success = pngLoadPtr((const unsigned char *)&pngData[0], pngData.size(), &width_, &height_, &textureData, false);
867
WARN_LOG(SCEGE, "Bad PPGeImage - not a valid png");
871
u32 texSize = width_ * height_ * 4;
872
texture_ = __PPGeDoAlloc(texSize, true, "Savedata Icon");
875
WARN_LOG(SCEGE, "Bad PPGeImage - unable to allocate space for texture");
879
Memory::Memcpy(texture_, textureData, texSize);
882
lastFrame_ = gpuStats.numFlips;
883
loadedTextures_.push_back(this);
887
void PPGeImage::Free() {
889
kernelMemory.Free(texture_);
891
loadedTextures_.erase(std::remove(loadedTextures_.begin(), loadedTextures_.end(), this), loadedTextures_.end());
895
void PPGeImage::DoState(PointerWrap &p) {
896
auto s = p.Section("PPGeImage", 1);
909
void PPGeImage::CompatLoad(u32 texture, int width, int height) {
910
// Won't be reloadable, so don't add to loadedTextures_.
916
void PPGeImage::Decimate() {
917
static const int TOO_OLD_AGE = 30;
918
int tooOldFrame = gpuStats.numFlips - TOO_OLD_AGE;
919
for (size_t i = 0; i < loadedTextures_.size(); ++i) {
920
if (loadedTextures_[i]->lastFrame_ < tooOldFrame) {
921
loadedTextures_[i]->Free();
922
// That altered loadedTextures_.
928
void PPGeImage::SetTexture() {
935
lastFrame_ = gpuStats.numFlips;
936
PPGeSetTexture(texture_, width_, height_);
938
PPGeDisableTexture();