Merge pull request #304 from kichikuou/pascha3-hll

Implement HLLs for Pastel Chime 3
This commit is contained in:
Nunuhara Cabbage
2026-02-28 08:17:58 -08:00
committed by GitHub
9 changed files with 570 additions and 2 deletions
+3
View File
@@ -156,6 +156,7 @@ target_sources(xsystem4 PRIVATE
src/hll/InputString.c
src/hll/KiwiSoundEngine.c
src/hll/LoadCG.c
src/hll/MADLoader.c
src/hll/MainEXFile.c
src/hll/MainSurface.c
src/hll/MamanyoDemo.c
@@ -171,6 +172,8 @@ target_sources(xsystem4 PRIVATE
src/hll/MusicSystem.c
src/hll/NewFont.c
src/hll/OutputLog.c
src/hll/P3MapSprite.c
src/hll/P3SquareSprite.c
src/hll/PassRegister.c
src/hll/PastelChime2.c
src/hll/PartsEngine.c
+2
View File
@@ -184,6 +184,8 @@ void gfx_copy_with_alpha_map(Texture *dst, int dx, int dy, Texture *src, int sx,
void gfx_fill_with_alpha(Texture *dst, int x, int y, int w, int h, int r, int g, int b, int a);
void gfx_copy_stretch_with_alpha_map(Texture *dst, int dx, int dy, int dw, int dh, Texture *src, int sx, int sy, int sw, int sh);
void gfx_copy_grayscale(Texture *dst, int dx, int dy, Texture *src, int sx, int sy, int w, int h);
void gfx_copy_grayscale_with_alpha_map(Texture *dst, int dx, int dy, Texture *src, int sx, int sy, int w, int h);
void gfx_copy_grayscale_reverse_LR_with_alpha_map(Texture *dst, int dx, int dy, Texture *src, int sx, int sy, int w, int h);
void gfx_draw_line(Texture *dst, int x0, int y0, int x1, int y1, int r, int g, int b);
void gfx_draw_line_to_amap(Texture *dst, int x0, int y0, int x1, int y1, int a);
void gfx_draw_glyph(Texture *dst, float dx, int dy, Texture *glyph, SDL_Color color, float scale_x, float bold_width, bool blend);
+3 -2
View File
@@ -20,6 +20,7 @@ in vec2 tex_coord;
out vec4 frag_color;
void main() {
float gray = dot(texture(tex, tex_coord).rgb, vec3(0.299, 0.587, 0.114));
frag_color = vec4(vec3(gray), 1);
vec4 texel = texture(tex, tex_coord);
float gray = dot(texel.rgb, vec3(0.299, 0.587, 0.114));
frag_color = vec4(vec3(gray), texel.a);
}
+27
View File
@@ -1094,6 +1094,33 @@ void gfx_copy_grayscale(Texture *dst, int dx, int dy, Texture *src, int sx, int
restore_blend_mode();
}
void gfx_copy_grayscale_with_alpha_map(Texture *dst, int dx, int dy, Texture *src, int sx, int sy, int w, int h)
{
glBlendFuncSeparate(GL_ONE, GL_ZERO, GL_ONE, GL_ZERO);
struct copy_data data = COPY_DATA(dx, dy, sx, sy, w, h);
run_copy_shader(&copy_grayscale_shader.s, dst, src, &data);
restore_blend_mode();
}
void gfx_copy_grayscale_reverse_LR_with_alpha_map(Texture *dst, int dx, int dy, Texture *src, int sx, int sy, int w, int h)
{
mat4 mw_transform = MAT4(
-src->w, 0, 0, sx + w,
0, src->h, 0, -sy,
0, 0, 1, 0,
0, 0, 0, 1);
mat4 wv_transform = WV_TRANSFORM(w, h);
glBlendFuncSeparate(GL_ONE, GL_ZERO, GL_ONE, GL_ZERO);
struct copy_data data = COPY_DATA(dx, dy, sx, sy, w, h);
run_draw_shader(&copy_grayscale_shader.s, dst, src, mw_transform, wv_transform, &data);
restore_blend_mode();
}
static void draw_line(Texture *dst, int x0, int y0, int x1, int y1, struct copy_data *data)
{
GLfloat w = dst->w;
+6
View File
@@ -469,6 +469,7 @@ extern struct static_library lib_InputDevice;
extern struct static_library lib_InputString;
extern struct static_library lib_KiwiSoundEngine;
extern struct static_library lib_LoadCG;
extern struct static_library lib_MADLoader;
extern struct static_library lib_MainEXFile;
extern struct static_library lib_MainSurface;
extern struct static_library lib_MamanyoDemo;
@@ -484,6 +485,8 @@ extern struct static_library lib_MsgSkip;
extern struct static_library lib_MusicSystem;
extern struct static_library lib_NewFont;
extern struct static_library lib_OutputLog;
extern struct static_library lib_P3MapSprite;
extern struct static_library lib_P3SquareSprite;
extern struct static_library lib_PassRegister;
extern struct static_library lib_PastelChime2;
extern struct static_library lib_PartsEngine;
@@ -587,6 +590,7 @@ static struct static_library *static_libraries[] = {
&lib_InputString,
&lib_KiwiSoundEngine,
&lib_LoadCG,
&lib_MADLoader,
&lib_MainEXFile,
&lib_MainSurface,
&lib_MamanyoDemo,
@@ -602,6 +606,8 @@ static struct static_library *static_libraries[] = {
&lib_MusicSystem,
&lib_NewFont,
&lib_OutputLog,
&lib_P3MapSprite,
&lib_P3SquareSprite,
&lib_PassRegister,
&lib_PastelChime2,
&lib_PartsEngine,
+336
View File
@@ -0,0 +1,336 @@
/* Copyright (C) 2026 kichikuou <KichikuouChrome@gmail.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, see <http://gnu.org/licenses/>.
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "system4/little_endian.h"
#include "system4/file.h"
#include "system4/string.h"
#include "system4/cg.h"
#include "hll.h"
#include "id_pool.h"
#include "xsystem4.h"
#include "gfx/gfx.h"
#include "sact.h"
// A MAD file consists of this header, followed by `nr_images` QNT images, each
// prefixed with its size.
struct MADHeader {
char magic[4]; // "MAD\0"
unsigned char unknown1[8];
int width;
int height;
int total_time;
int nr_images;
int draw_method;
int is_loop;
unsigned char unknown2[8];
int keep_last_frame;
};
struct MAD {
struct MADHeader header;
Texture textures[];
};
static struct id_pool pool;
static void MADLoader_ModuleInit(void)
{
id_pool_init(&pool, 0);
}
static void MADLoader_Close(int handle)
{
struct MAD *mad = id_pool_release(&pool, handle);
if (!mad)
return;
for (int i = 0; i < mad->header.nr_images; i++) {
if (mad->textures[i].handle)
gfx_delete_texture(&mad->textures[i]);
}
free(mad);
}
static void MADLoader_Delete(void)
{
for (int i = id_pool_get_first(&pool); i >= 0; i = id_pool_get_next(&pool, i))
MADLoader_Close(i);
}
static void MADLoader_ModuleFini(void)
{
MADLoader_Delete();
id_pool_delete(&pool);
}
static FILE *mad_open(struct string *filename, struct MADHeader *header)
{
char *path = gamedir_path(filename->text);
FILE *fp = file_open_utf8(path, "rb");
free(path);
if (!fp)
return NULL;
uint8_t buf[48];
if (fread(buf, sizeof buf, 1, fp) != 1 || memcmp(buf, "MAD", 4)) {
fclose(fp);
return NULL;
}
memcpy(header->magic, buf, 4);
memcpy(header->unknown1, buf + 4, 8);
header->width = LittleEndian_getDW(buf, 12);
header->height = LittleEndian_getDW(buf, 16);
header->total_time = LittleEndian_getDW(buf, 20);
header->nr_images = LittleEndian_getDW(buf, 24);
header->draw_method = LittleEndian_getDW(buf, 28);
header->is_loop = LittleEndian_getDW(buf, 32);
memcpy(header->unknown2, buf + 36, 8);
header->keep_last_frame = LittleEndian_getDW(buf, 44);
return fp;
}
static bool mad_read_header(struct string *filename, struct MADHeader *header)
{
FILE *fp = mad_open(filename, header);
fclose(fp);
return fp != NULL;
}
static int MADLoader_Open(struct string *filename)
{
struct MADHeader header;
FILE *fp = mad_open(filename, &header);
if (!fp)
return -1;
struct MAD *mad = xcalloc(1, sizeof(struct MAD) + header.nr_images * sizeof(Texture));
mad->header = header;
for (int i = 0; i < mad->header.nr_images; i++) {
uint8_t buf[4];
if (fread(buf, 4, 1, fp) != 1)
VM_ERROR("MADLoader: failed to read image size %d of %s", i, display_sjis0(filename->text));
uint32_t image_size = LittleEndian_getDW(buf, 0);
uint8_t *image_data = xmalloc(image_size);
if (fread(image_data, image_size, 1, fp) != 1)
VM_ERROR("MADLoader: failed to read image data %d of %s", i, display_sjis0(filename->text));
struct cg *cg = cg_load_buffer(image_data, image_size);
if (!cg)
VM_ERROR("MADLoader: failed to load cg %d of %s", i, display_sjis0(filename->text));
gfx_init_texture_with_cg(&mad->textures[i], cg);
cg_free(cg);
free(image_data);
}
fclose(fp);
int handle = id_pool_get_unused(&pool);
id_pool_set(&pool, handle, mad);
return handle;
}
static bool MADLoader_GetSize(int handle, int *width, int *height)
{
struct MAD *mad = id_pool_get(&pool, handle);
if (!mad)
return false;
*width = mad->header.width;
*height = mad->header.height;
return true;
}
static int MADLoader_GetNumof(int handle)
{
struct MAD *mad = id_pool_get(&pool, handle);
return mad ? mad->header.nr_images : -1;
}
static int MADLoader_GetTotalTime(int handle)
{
struct MAD *mad = id_pool_get(&pool, handle);
return mad ? mad->header.total_time : -1;
}
static int MADLoader_GetDrawMethod(int handle)
{
struct MAD *mad = id_pool_get(&pool, handle);
return mad ? mad->header.draw_method : -1;
}
static bool MADLoader_IsLoop(int handle)
{
struct MAD *mad = id_pool_get(&pool, handle);
return mad && mad->header.is_loop == 1;
}
static bool MADLoader_IsKeepLastFrame(int handle)
{
struct MAD *mad = id_pool_get(&pool, handle);
return mad && mad->header.keep_last_frame == 1;
}
static bool MADLoader_IsLoading(int handle)
{
// We don't support background loading.
return false;
}
static int MADLoader_GetLoadNumof(int handle)
{
struct MAD *mad = id_pool_get(&pool, handle);
return mad ? mad->header.nr_images : 0;
}
static bool MADLoader_GetSizeFromFile(struct string *filename, int *width, int *height)
{
struct MADHeader header;
if (!mad_read_header(filename, &header))
return false;
*width = header.width;
*height = header.height;
return true;
}
static int MADLoader_GetNumofFromFile(struct string *filename)
{
struct MADHeader header;
if (!mad_read_header(filename, &header))
return -1;
return header.nr_images;
}
static int MADLoader_GetTotalTimeFromFile(struct string *filename)
{
struct MADHeader header;
if (!mad_read_header(filename, &header))
return -1;
return header.total_time;
}
static int MADLoader_GetDrawMethodFromFile(struct string *filename)
{
struct MADHeader header;
if (!mad_read_header(filename, &header))
return -1;
return header.draw_method;
}
static bool MADLoader_IsLoopFromFile(struct string *filename)
{
struct MADHeader header;
if (!mad_read_header(filename, &header))
return false;
return header.is_loop == 1;
}
static bool MADLoader_IsKeepLastFrameFromFile(struct string *filename)
{
struct MADHeader header;
if (!mad_read_header(filename, &header))
return false;
return header.keep_last_frame == 1;
}
static Texture *get_mad_texture(int handle, int index)
{
struct MAD *mad = id_pool_get(&pool, handle);
if (!mad || index < 0 || index >= mad->header.nr_images)
return NULL;
return &mad->textures[index];
}
static Texture *get_dest_texture(int sp_no)
{
struct sact_sprite *sp = sact_get_sprite(sp_no);
if (!sp)
return NULL;
sprite_dirty(sp);
return sprite_get_texture(sp);
}
static bool MADLoader_Copy(int handle, int sprite, int index)
{
Texture *src = get_mad_texture(handle, index);
Texture *dst = get_dest_texture(sprite);
if (!src || !dst)
return false;
gfx_copy_with_alpha_map(dst, 0, 0, src, 0, 0, src->w, src->h);
return true;
}
static bool MADLoader_CopyReverseLR(int handle, int sprite, int index)
{
Texture *src = get_mad_texture(handle, index);
Texture *dst = get_dest_texture(sprite);
if (!src || !dst)
return false;
gfx_copy_reverse_LR_with_alpha_map(dst, 0, 0, src, 0, 0, src->w, src->h);
return true;
}
static bool MADLoader_CopyGrayscale(int handle, int sprite, int index)
{
Texture *src = get_mad_texture(handle, index);
Texture *dst = get_dest_texture(sprite);
if (!src || !dst)
return false;
gfx_copy_grayscale_with_alpha_map(dst, 0, 0, src, 0, 0, src->w, src->h);
return true;
}
static bool MADLoader_CopyGrayscaleReverseLR(int handle, int sprite, int index)
{
Texture *src = get_mad_texture(handle, index);
Texture *dst = get_dest_texture(sprite);
if (!src || !dst)
return false;
gfx_copy_grayscale_reverse_LR_with_alpha_map(dst, 0, 0, src, 0, 0, src->w, src->h);
return true;
}
HLL_LIBRARY(MADLoader,
HLL_EXPORT(_ModuleInit, MADLoader_ModuleInit),
HLL_EXPORT(_ModuleFini, MADLoader_ModuleFini),
HLL_EXPORT(Delete, MADLoader_Delete),
HLL_EXPORT(Open, MADLoader_Open),
HLL_EXPORT(Close, MADLoader_Close),
HLL_EXPORT(GetSize, MADLoader_GetSize),
HLL_EXPORT(GetNumof, MADLoader_GetNumof),
HLL_EXPORT(GetTotalTime, MADLoader_GetTotalTime),
HLL_EXPORT(GetDrawMethod, MADLoader_GetDrawMethod),
HLL_EXPORT(IsLoop, MADLoader_IsLoop),
HLL_EXPORT(IsKeepLastFrame, MADLoader_IsKeepLastFrame),
HLL_EXPORT(IsLoading, MADLoader_IsLoading),
HLL_EXPORT(GetLoadNumof, MADLoader_GetLoadNumof),
HLL_EXPORT(GetSizeFromFile, MADLoader_GetSizeFromFile),
HLL_EXPORT(GetNumofFromFile, MADLoader_GetNumofFromFile),
HLL_EXPORT(GetTotalTimeFromFile, MADLoader_GetTotalTimeFromFile),
HLL_EXPORT(GetDrawMethodFromFile, MADLoader_GetDrawMethodFromFile),
HLL_EXPORT(IsLoopFromFile, MADLoader_IsLoopFromFile),
HLL_EXPORT(IsKeepLastFrameFromFile, MADLoader_IsKeepLastFrameFromFile),
HLL_EXPORT(Copy, MADLoader_Copy),
HLL_EXPORT(CopyReverseLR, MADLoader_CopyReverseLR),
HLL_EXPORT(CopyGrayscale, MADLoader_CopyGrayscale),
HLL_EXPORT(CopyGrayscaleReverseLR, MADLoader_CopyGrayscaleReverseLR)
);
+121
View File
@@ -0,0 +1,121 @@
/* Copyright (C) 2026 kichikuou <KichikuouChrome@gmail.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, see <http://gnu.org/licenses/>.
*/
#include "gfx/gfx.h"
#include "vm/page.h"
#include "hll.h"
#include "sact.h"
#include "xsystem4.h"
static struct {
Texture bg;
Texture grid;
int blend_rate;
} p3map;
// Initializes the grid texture with isometric tiles based on the provided array.
static void init_grid_texture(Texture *tile, int offset_x, int offset_y, int rows, int cols, struct page *array)
{
if (array->nr_vars != rows * cols)
VM_ERROR("Expected %d variables in array, got %d", rows * cols, array->nr_vars);
for (int row = 0; row < rows; row++) {
for (int col = 0; col < cols; col++) {
if (array->values[row * cols + col].i != 0)
continue;
// Isometric Coordinates.
int x = ((col - row) - 1) * (tile->w / 2) + offset_x;
int y = (col + row) * (tile->h / 2) + offset_y;
gfx_copy_with_alpha_map(&p3map.grid, x, y, tile, 0, 0, tile->w, tile->h);
}
}
}
// Creates the map background and grid textures.
static bool P3MapSprite_Create(int src_sprite, int square_sprite, int x, int y, int line, int column, int blend_rate, struct page *array)
{
struct sact_sprite *src = sact_try_get_sprite(src_sprite);
struct sact_sprite *square = sact_try_get_sprite(square_sprite);
if (!src || !square)
return false;
Texture *tx_src = sprite_get_texture(src);
Texture *tx_square = sprite_get_texture(square);
int width = tx_src->w;
int height = tx_src->h;
if (p3map.bg.handle)
gfx_delete_texture(&p3map.bg);
gfx_init_texture_blank(&p3map.bg, width, height);
gfx_copy(&p3map.bg, 0, 0, tx_src, 0, 0, width, height);
if (p3map.grid.handle)
gfx_delete_texture(&p3map.grid);
gfx_init_texture_rgba(&p3map.grid, width, height, COLOR(0, 0, 0, 0));
init_grid_texture(tx_square, x, y, line, column, array);
p3map.blend_rate = blend_rate;
return true;
}
// Deletes the map background and grid textures.
static void P3MapSprite_Delete(void)
{
if (p3map.grid.handle)
gfx_delete_texture(&p3map.grid);
if (p3map.bg.handle)
gfx_delete_texture(&p3map.bg);
}
// Copies the map background and optionally blends the grid to a destination sprite.
static bool P3MapSprite_Copy(int dest_sprite, int src_x, int src_y, bool copy_square)
{
struct sact_sprite *dst = sact_get_sprite(dest_sprite);
if (!dst)
return false;
Texture *tx_dst = sprite_get_texture(dst);
int w = sprite_get_width(dst);
int h = sprite_get_height(dst);
gfx_copy(tx_dst, 0, 0, &p3map.bg, src_x, src_y, w, h);
if (copy_square)
gfx_blend_amap_alpha(tx_dst, 0, 0, &p3map.grid, src_x, src_y, w, h, p3map.blend_rate);
return true;
}
// Sets the blend rate for map rendering.
static bool P3MapSprite_CreateMatrixPixel(int blend_rate)
{
p3map.blend_rate = blend_rate;
return true;
}
// Blends the grid texture onto a destination sprite.
static bool P3MapSprite_CopyMatrix(int dest_sprite, int dest_x, int dest_y, int src_x, int src_y, int width, int height)
{
struct sact_sprite *dst = sact_get_sprite(dest_sprite);
if (!dst)
return false;
Texture *tx_dst = sprite_get_texture(dst);
gfx_blend_amap(tx_dst, dest_x, dest_y, &p3map.grid, src_x, src_y, width, height);
gfx_copy_amap_max(tx_dst, dest_x, dest_y, &p3map.grid, src_x, src_y, width, height);
return true;
}
HLL_LIBRARY(P3MapSprite,
HLL_EXPORT(Create, P3MapSprite_Create),
HLL_EXPORT(Delete, P3MapSprite_Delete),
HLL_EXPORT(Copy, P3MapSprite_Copy),
HLL_EXPORT(CreateMatrixPixel, P3MapSprite_CreateMatrixPixel),
HLL_EXPORT(CopyMatrix, P3MapSprite_CopyMatrix)
);
+69
View File
@@ -0,0 +1,69 @@
/* Copyright (C) 2026 kichikuou <KichikuouChrome@gmail.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, see <http://gnu.org/licenses/>.
*/
#include "sact.h"
#include "gfx/gfx.h"
#include "vm/page.h"
#include "hll.h"
#include "xsystem4.h"
static bool P3SquareSprite_Copy(int dest_handle, int src_handle, int size, struct page *matrix)
{
struct sact_sprite *dest = sact_try_get_sprite(dest_handle);
struct sact_sprite *src = sact_try_get_sprite(src_handle);
if (!dest || !src)
return false;
Texture *dest_tex = sprite_get_texture(dest);
Texture *src_tex = sprite_get_texture(src);
int tile_w = src_tex->w;
int tile_h = src_tex->h;
int half_w = tile_w / 2;
int half_h = tile_h / 2;
int canvas_center_x = dest_tex->w / 2;
int canvas_center_y = dest_tex->h / 2;
int side_count = size * 2 + 1;
if (matrix->nr_vars != side_count * side_count)
VM_ERROR("Expected %d variables in array, got %d", side_count * side_count, matrix->nr_vars);
int index = 0;
for (int y = -size; y <= size; ++y) {
for (int x = -size; x <= size; ++x) {
// Check if we should draw this tile
if (matrix->values[index++].i == 0)
continue;
// Isometric coordinate conversion
int screen_pos_x = canvas_center_x + (x - y - 1) * half_w;
int screen_pos_y = canvas_center_y + (x + y - 1) * half_h;
// Draw the sprite
gfx_copy_use_amap_border(dest_tex, screen_pos_x, screen_pos_y, src_tex, 0, 0, tile_w, tile_h, 1);
gfx_copy_amap_max(dest_tex, screen_pos_x, screen_pos_y, src_tex, 0, 0, tile_w, tile_h);
}
}
return true;
}
HLL_LIBRARY(P3SquareSprite,
HLL_EXPORT(Copy, P3SquareSprite_Copy)
);
+3
View File
@@ -117,6 +117,7 @@ xsystem4 = [version_h,
'hll/InputString.c',
'hll/KiwiSoundEngine.c',
'hll/LoadCG.c',
'hll/MADLoader.c',
'hll/MainEXFile.c',
'hll/MainSurface.c',
'hll/MamanyoDemo.c',
@@ -132,6 +133,8 @@ xsystem4 = [version_h,
'hll/MusicSystem.c',
'hll/NewFont.c',
'hll/OutputLog.c',
'hll/P3MapSprite.c',
'hll/P3SquareSprite.c',
'hll/PassRegister.c',
'hll/PastelChime2.c',
'hll/PartsEngine.c',