121 lines
4.6 KiB
Markdown
121 lines
4.6 KiB
Markdown
``` c
|
||
#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
|
||
}
|
||
``` |