4.6 KiB
4.6 KiB
#include <libgte.h>
#include <libgpu.h>
#include <libgs.h>
/* Структуры данных */
typedef struct {
uint* next_block; // Следующий блок DMA (младшие 24 бита)
uint control; // Контрольные биты DMA
uint data_addr; // Адрес данных
uint tpage_params; // Параметры текстуры (TPage)
} DMAControlBlock;
typedef struct {
short x, y; // Позиция символа
short char_width; // Ширина символа
ushort flags; // Флаги рендеринга
uint* dma_head; // Голова DMA-цепочки
uint* dma_tail; // Хвост DMA-цепочки
} TextRenderState;
/* Константы */
#define SCRAM_BASE 0x1F800000 // Быстрая память PS1
#define DMA_MAGIC 0x80000000 // Флаг DMA-блока
#define CHAR_OFFSET 0x20 // Смещение ASCII-кодов
/* Глобальные переменные (аналоги оригинальных) */
static uint* gDmaNextFree = (uint*)0x8007CE68;
static uint* gDmaCounter = (uint*)0x8007CE6C;
static uint gActiveTpage = 0x8007CA0C;
/* Основная функция рендеринга текста */
DMAControlBlock* RenderTextSystem(
const char* text,
short base_x,
short base_y,
uint color_flags,
uint tpage_config
) {
/* Инициализация состояния */
TextRenderState state;
state.x = base_x;
state.y = base_y;
state.flags = color_flags;
/* Получаем начальный DMA-блок из SCRAM */
DMAControlBlock* dma_blocks = (DMAControlBlock*)(*gDmaNextFree | DMA_MAGIC);
state.dma_head = state.dma_tail = (uint*)dma_blocks;
/* Настройка параметров рендеринга */
StoreColorToAddr(color_flags, tpage_config);
InitDMAtextChain(dma_blocks, base_x, base_y);
/* Основной цикл обработки текста */
while (*text) {
char current_char = *text++;
ushort char_code = current_char - CHAR_OFFSET;
/* Особые обработки для специфичных символов */
switch (current_char) {
case 'g': case 'j': case 'p': case 'q': case 'y':
base_y += 2; // Смещение для символов с хвостами
break;
}
/* Позиционирование символа */
short render_x, render_y;
if (state.flags & 0x10) {
/* Альтернативный режим позиционирования */
render_x = base_x - GetCharLeftOffset(char_code);
render_y = base_y + GetCharBaseline(char_code);
} else {
/* Стандартный режим */
render_x = base_x + dma_blocks->char_width * 8;
render_y = base_y;
}
/* Генерация DMA-команд для символа */
DMAControlBlock* new_block = AllocDMABlock();
SetupCharRender(new_block, char_code, render_x, render_y);
/* Добавление в цепочку */
state.dma_tail->next_block = (uint)new_block & 0xFFFFFF;
state.dma_tail = new_block;
/* Обновление позиции */
if (dma_blocks->char_width == 0) {
state.dma_head = new_block;
dma_blocks->char_width = 1;
}
}
/* Финализация текстурных страниц */
for (int i = 0; i < 2; i++) {
DMAControlBlock* tpage_block = AllocDMABlock();
SetDrawTPage(tpage_block, 0, 1, gActiveTpage);
state.dma_tail->next_block = (uint)tpage_block & 0xFFFFFF;
state.dma_tail = tpage_block;
}
return dma_blocks;
}
/* Вспомогательные функции */
static DMAControlBlock* AllocDMABlock() {
DMAControlBlock* block = (DMAControlBlock*)(*gDmaNextFree | DMA_MAGIC);
(*gDmaCounter)--;
*gDmaNextFree = (*gDmaNextFree & 0xFF000000) | (*block & 0xFFFFFF);
return block;
}
static void SetupCharRender(DMAControlBlock* block, ushort char_code, short x, short y) {
/* Настройка параметров символа */
block->control = 0x1000000; // Базовые флаги DMA
block->data_addr = GetCharDataAddr(char_code);
block->tpage_params = (y << 16) | x; // Позиция
/* Дополнительные настройки из оригинального кода */
block->control |= 0x64808080; // Специфичные флаги PS1
}