mirror of
https://github.com/nunuhara/xsystem4.git
synced 2026-09-22 23:18:01 +03:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
baae8f5237 | ||
|
|
8437a87b54 | ||
|
|
b11a173b67 | ||
|
|
0e18f69cbf | ||
|
|
c3f9d93917 | ||
|
|
ac411754a0 | ||
|
|
45ebaa2c7f | ||
|
|
a923b0ce23 | ||
|
|
1be2cffd22 | ||
|
|
50af8bd3ff | ||
|
|
32ef29ee20 | ||
|
|
8620be452e | ||
|
|
cc79f9caee | ||
|
|
6a2eae7274 | ||
|
|
66002f7ceb | ||
|
|
d1b18f39dd | ||
|
|
dbd1e54c46 | ||
|
|
3c0ee51714 | ||
|
|
d4f7ab06cf | ||
|
|
66d8def3f8 | ||
|
|
8f29146c28 | ||
|
|
e53dc6ad0e | ||
|
|
90a96eb8bb | ||
|
|
3ff2c33d3c | ||
|
|
2237bb814c | ||
|
|
6b62e1db23 |
@@ -47,6 +47,70 @@ Finally install it to your system (optional),
|
||||
|
||||
ninja -C build install
|
||||
|
||||
### Windows
|
||||
|
||||
xsystem4 can be built on Windows using MSYS2.
|
||||
|
||||
First install MSYS2, and then open the MINGW64 shell and run the following command,
|
||||
|
||||
pacman -S flex bison \
|
||||
mingw-w64-x86_64-gcc \
|
||||
mingw-w64-x86_64-meson \
|
||||
mingw-w64-x86_64-pkg-config \
|
||||
mingw-w64-x86_64-SDL2 \
|
||||
mingw-w64-x86_64-freetype \
|
||||
mingw-w64-x86_64-libjpeg-turbo \
|
||||
mingw-w64-x86_64-libwebp \
|
||||
mingw-w64-x86_64-libsndfile \
|
||||
mingw-w64-x86_64-glew
|
||||
|
||||
To build with FFmpeg support, you must compile FFmpeg as a static library:
|
||||
|
||||
git clone https://github.com/FFmpeg/FFmpeg.git
|
||||
cd FFmpeg
|
||||
git checkout n6.0
|
||||
./configure --disable-everything \
|
||||
--enable-decoder=mpegvideo \
|
||||
--enable-decoder=mpeg1video \
|
||||
--enable-decoder=mpeg2video \
|
||||
--enable-decoder=mp2 \
|
||||
--enable-parser=mpegaudio \
|
||||
--enable-parser=mpegvideo \
|
||||
--enable-demuxer=mpegps \
|
||||
--enable-demuxer=mpegts \
|
||||
--enable-demuxer=mpegtsraw \
|
||||
--enable-demuxer=mpegvideo \
|
||||
--enable-decoder=vc1 \
|
||||
--enable-decoder=wmapro \
|
||||
--enable-parser=vc1 \
|
||||
--enable-hwaccel=vc1_d3d11va \
|
||||
--enable-hwaccel=vc1_d3d11va2 \
|
||||
--enable-hwaccel=vc1_dxva2 \
|
||||
--enable-demuxer=asf \
|
||||
--enable-protocol=file \
|
||||
--enable-filter=scale \
|
||||
--enable-static \
|
||||
--disable-shared \
|
||||
--extra-libs=-static \
|
||||
--extra-cflags=--static
|
||||
make
|
||||
make install
|
||||
cd ..
|
||||
export PKG_CONFIG_PATH="/usr/local/lib/pkgconfig:$PKG_CONFIG_PATH"
|
||||
|
||||
Then build the xsystem4 executable with meson,
|
||||
|
||||
mkdir build
|
||||
meson build
|
||||
ninja -C build
|
||||
|
||||
To create a portable executable, it is neccessary to copy some DLLs into the same directory as xsystem4.exe.
|
||||
You can determine the required DLLs with the following command,
|
||||
|
||||
ldd build/src/xsystem4.exe | grep mingw64
|
||||
|
||||
The `fonts` and `shaders` directories must also be shipped together with xsystem4.exe.
|
||||
|
||||
Installing
|
||||
----------
|
||||
|
||||
|
||||
Generated
+26
@@ -0,0 +1,26 @@
|
||||
{
|
||||
"nodes": {
|
||||
"nixpkgs": {
|
||||
"locked": {
|
||||
"lastModified": 1678137616,
|
||||
"narHash": "sha256-T+lWTRdcYaOnZQW+Ehdlg+YldC2l9cq2GXJFPq22Nxc=",
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "7edcdf7b169c33cd3eef9aba50521ce93ee666b8",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"id": "nixpkgs",
|
||||
"ref": "nixos-22.11",
|
||||
"type": "indirect"
|
||||
}
|
||||
},
|
||||
"root": {
|
||||
"inputs": {
|
||||
"nixpkgs": "nixpkgs"
|
||||
}
|
||||
}
|
||||
},
|
||||
"root": "root",
|
||||
"version": 7
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
{
|
||||
description = "Open source implementation of AliceSoft's System 4 game engine for unix-like operating systems";
|
||||
|
||||
# Nixpkgs / NixOS version to use.
|
||||
inputs.nixpkgs.url = "nixpkgs/nixos-22.11";
|
||||
|
||||
outputs = { self, nixpkgs }:
|
||||
let
|
||||
|
||||
# System types to support.
|
||||
supportedSystems = [ "x86_64-linux" "x86_64-darwin" "aarch64-linux" "aarch64-darwin" ];
|
||||
|
||||
# Helper function to generate an attrset '{ x86_64-linux = f "x86_64-linux"; ... }'.
|
||||
forAllSystems = nixpkgs.lib.genAttrs supportedSystems;
|
||||
|
||||
# Nixpkgs instantiated for supported system types.
|
||||
nixpkgsFor = forAllSystems (system: import nixpkgs { inherit system; });
|
||||
|
||||
in {
|
||||
|
||||
# Set up xsystem4 development environment:
|
||||
# nix develop # create shell environment with dependencies available
|
||||
devShell = forAllSystems (system:
|
||||
let pkgs = nixpkgsFor.${system}; in with pkgs; pkgs.mkShell {
|
||||
nativeBuildInputs = [ meson ninja pkg-config ];
|
||||
buildInputs = [
|
||||
cglm
|
||||
SDL2
|
||||
freetype
|
||||
libffi
|
||||
libjpeg
|
||||
libwebp
|
||||
libsndfile
|
||||
glew
|
||||
readline81
|
||||
chibi
|
||||
flex
|
||||
bison
|
||||
ffmpeg
|
||||
];
|
||||
}
|
||||
);
|
||||
|
||||
# Build xsystem4:
|
||||
# nix build .?submodules=1 # build xsystem4 (outputs to ./result/)
|
||||
# nix shell .?submodules=1 # create shell environment with xsystem4 available
|
||||
# Install xsystem4 to user profile:
|
||||
# nix profile install .?submodules=1
|
||||
packages = forAllSystems (system:
|
||||
let pkgs = nixpkgsFor.${system}; in {
|
||||
default = with pkgs; stdenv.mkDerivation rec {
|
||||
name = "xsystem4";
|
||||
src = ./.;
|
||||
mesonAutoFeatures = "auto";
|
||||
nativeBuildInputs = [ meson ninja pkg-config ];
|
||||
buildInputs = [
|
||||
cglm
|
||||
SDL2
|
||||
freetype
|
||||
libffi
|
||||
libjpeg
|
||||
libwebp
|
||||
libsndfile
|
||||
glew
|
||||
readline81
|
||||
chibi
|
||||
flex
|
||||
bison
|
||||
ffmpeg
|
||||
];
|
||||
};
|
||||
}
|
||||
);
|
||||
};
|
||||
}
|
||||
+2
-3
@@ -39,8 +39,6 @@ typedef struct texture {
|
||||
mat4 world_transform;
|
||||
int w, h;
|
||||
bool has_alpha;
|
||||
int alpha_mod;
|
||||
enum draw_method draw_method;
|
||||
} Texture;
|
||||
|
||||
struct gfx_render_job;
|
||||
@@ -52,7 +50,6 @@ typedef struct shader {
|
||||
GLuint view_transform;
|
||||
GLuint texture;
|
||||
GLuint vertex;
|
||||
GLuint alpha_mod;
|
||||
void (*prepare)(struct gfx_render_job *, void *);
|
||||
} Shader;
|
||||
|
||||
@@ -82,6 +79,7 @@ void gfx_swap(void);
|
||||
void gfx_prepare_job(struct gfx_render_job *job);
|
||||
void gfx_run_job(struct gfx_render_job *job);
|
||||
void gfx_render(struct gfx_render_job *job);
|
||||
void _gfx_render_texture(struct shader *s, struct texture *t, Rectangle *r, void *data);
|
||||
void gfx_render_texture(struct texture *t, Rectangle *r);
|
||||
|
||||
// texture management
|
||||
@@ -92,6 +90,7 @@ void gfx_init_texture_rgb(struct texture *t, int w, int h, SDL_Color color);
|
||||
void gfx_init_texture_with_pixels(struct texture *t, int w, int h, void *pixels);
|
||||
void gfx_init_texture_amap(struct texture *t, int w, int h, uint8_t *amap, SDL_Color color);
|
||||
void gfx_init_texture_rmap(struct texture *t, int w, int h, uint8_t *rmap);
|
||||
void gfx_update_texture_with_pixels(struct texture *t, void *pixels);
|
||||
void gfx_copy_main_surface(struct texture *dst);
|
||||
void gfx_delete_texture(struct texture *t);
|
||||
GLuint gfx_set_framebuffer(GLenum target, Texture *t, int x, int y, int w, int h);
|
||||
|
||||
@@ -31,6 +31,8 @@ void PE_SetDelegateIndex(int parts_no, int delegate_index);
|
||||
int PE_GetDelegateIndex(int parts_no);
|
||||
bool PE_SetPartsCG(int parts_no, struct string *cg_name, int sprite_deform, int state);
|
||||
bool PE_SetPartsCG_by_index(int parts_no, int cg_no, int sprite_deform, int state);
|
||||
bool PE_SetPartsCG_by_string_index(int parts_no, struct string *cg_no,
|
||||
int sprite_deform, int state);
|
||||
void PE_GetPartsCGName(int parts_no, struct string **cg_name, int state);
|
||||
bool PE_SetPartsCGSurfaceArea(int parts_no, int x, int y, int w, int h, int state);
|
||||
int PE_GetPartsCGNumber(int parts_no, int state);
|
||||
|
||||
+4
-2
@@ -24,7 +24,7 @@ struct sact_sprite *sact_get_sprite(int sp);
|
||||
struct sact_sprite *sact_try_get_sprite(int sp);
|
||||
struct sact_sprite *sact_create_sprite(int sp_no, int width, int height, int r, int g, int b, int a);
|
||||
void sact_ModuleFini(void);
|
||||
int sact_Init(void *_, int cg_cache_size);
|
||||
int sact_init(int cg_cache_size, bool chipmunk);
|
||||
#define sact_SetWP scene_set_wp
|
||||
#define sact_SetWP_Color scene_set_wp_color
|
||||
int sact_GetScreenWidth(void);
|
||||
@@ -50,7 +50,7 @@ int sact_SP_SetY(int sp_no, int y);
|
||||
int sact_SP_SetZ(int sp, int z);
|
||||
int sact_SP_GetBlendRate(int sp_no);
|
||||
int sact_SP_SetBlendRate(int sp_no, int rate);
|
||||
int sact_SP_SetShow(int sp_no, bool show);
|
||||
int sact_SP_SetShow(int sp_no, int show);
|
||||
int sact_SP_SetDrawMethod(int sp_no, int method);
|
||||
int sact_SP_GetDrawMethod(int sp_no);
|
||||
int sact_SP_IsUsing(int sp_no);
|
||||
@@ -100,6 +100,8 @@ void sact_CG_BlendAMapBin(int dst, int dx, int dy, int src, int sx, int sy, int
|
||||
int sact_TRANS_Begin(int type);
|
||||
int sact_TRANS_Update(float rate);
|
||||
int sact_TRANS_End(void);
|
||||
bool sact_VIEW_SetMode(int mode);
|
||||
int sact_VIEW_GetMode(void);
|
||||
|
||||
extern struct text_style text_sprite_ts;
|
||||
bool StoatSpriteEngine_SP_SetTextSprite(int sp_no, struct string *text);
|
||||
|
||||
+1
-2
@@ -26,8 +26,7 @@ void json_load_page(struct page *page, cJSON *vars, bool call_dtors);
|
||||
union vm_value json_to_vm_value(enum ain_data_type type, enum ain_data_type struct_type, int array_rank, cJSON *json);
|
||||
|
||||
int save_json(const char *filename, cJSON *json);
|
||||
int save_globals(const char *keyname, const char *filename);
|
||||
int save_group(const char *keyname, const char *filename, const char *group_name, int *n);
|
||||
int save_globals(const char *keyname, const char *filename, const char *group_name, int *n);
|
||||
int load_globals(const char *keyname, const char *filename, const char *group_name, int *n);
|
||||
int delete_save_file(const char *filename);
|
||||
|
||||
|
||||
+25
-2
@@ -35,8 +35,16 @@ struct sact_sprite {
|
||||
struct texture texture;
|
||||
// If no CG is attached to the sprite, the solid color to fill with.
|
||||
SDL_Color color;
|
||||
// Shader params
|
||||
int blend_rate;
|
||||
SDL_Color multiply_color;
|
||||
SDL_Color add_color;
|
||||
// Draw method (blend mode)
|
||||
enum draw_method draw_method;
|
||||
// The position and dimensions of the sprite.
|
||||
Rectangle rect;
|
||||
// The visible area of the sprite
|
||||
Rectangle surface_area;
|
||||
// Arbitrary text can be attached to the sprite. This is rendered on a
|
||||
// separate texture and overlayed.
|
||||
struct {
|
||||
@@ -55,6 +63,17 @@ struct sact_sprite {
|
||||
struct draw_plugin *plugin;
|
||||
};
|
||||
|
||||
/*
|
||||
* XXX: There are two different sprite renderers:
|
||||
* * SACT2 - only shader param is blend rate
|
||||
* * ChipmunkSpriteEngine - blend rate, add color, multiply color, surface area
|
||||
* (uses parts shader)
|
||||
*
|
||||
* The initialization function determines which renderer is used.
|
||||
*/
|
||||
void sprite_init_sact(void);
|
||||
void sprite_init_chipmunk(void);
|
||||
|
||||
static inline void sprite_dirty(struct sact_sprite *sp)
|
||||
{
|
||||
scene_sprite_dirty(&sp->sp);
|
||||
@@ -69,14 +88,18 @@ int sprite_set_cg_2x_from_asset(struct sact_sprite *sp, int cg_no);
|
||||
int sprite_set_cg_by_name(struct sact_sprite *sp, const char *name);
|
||||
int sprite_set_cg_from_file(struct sact_sprite *sp, const char *path);
|
||||
int sprite_save_cg(struct sact_sprite *sp, const char *path);
|
||||
void sprite_init(struct sact_sprite *sp, int w, int h, int r, int g, int b, int a);
|
||||
void sprite_init(struct sact_sprite *sp);
|
||||
void sprite_init_color(struct sact_sprite *sp, int w, int h, int r, int g, int b, int a);
|
||||
void sprite_set_pos(struct sact_sprite *sp, int x, int y);
|
||||
void sprite_set_x(struct sact_sprite *sp, int x);
|
||||
void sprite_set_y(struct sact_sprite *sp, int y);
|
||||
static inline void sprite_set_z(struct sact_sprite *sp, int z) { scene_set_sprite_z(&sp->sp, z); }
|
||||
static inline void sprite_set_z2(struct sact_sprite *sp, int z, int z2) { scene_set_sprite_z2(&sp->sp, z, z2); }
|
||||
int sprite_get_blend_rate(struct sact_sprite *sp);
|
||||
static inline int sprite_get_blend_rate(struct sact_sprite *sp) { return sp->blend_rate; }
|
||||
void sprite_set_blend_rate(struct sact_sprite *sp, int rate);
|
||||
void sprite_set_multiply_color(struct sact_sprite *sp, int r, int g, int b);
|
||||
void sprite_set_add_color(struct sact_sprite *sp, int r, int g, int b);
|
||||
void sprite_set_surface_area(struct sact_sprite *sp, int x, int y, int w, int h);
|
||||
static inline void sprite_set_show(struct sact_sprite *sp, bool show) { scene_set_sprite_show(&sp->sp, show); }
|
||||
int sprite_set_draw_method(struct sact_sprite *sp, enum draw_method method);
|
||||
enum draw_method sprite_get_draw_method(struct sact_sprite *sp);
|
||||
|
||||
@@ -106,6 +106,7 @@ union vm_value vm_copy(union vm_value v, enum ain_data_type type);
|
||||
int vm_execute_ain(struct ain *program);
|
||||
void vm_call(int fno, int struct_page);
|
||||
int vm_time(void);
|
||||
void vm_sleep(int ms);
|
||||
|
||||
void hll_call(int libno, int fno);
|
||||
bool library_exists(int libno);
|
||||
|
||||
@@ -75,6 +75,7 @@ extern bool game_daibanchou_en;
|
||||
extern bool game_rance02_mg;
|
||||
extern bool game_rance6_mg;
|
||||
extern bool game_rance7_mg;
|
||||
extern bool game_rance8;
|
||||
|
||||
extern bool id_indexed_afa;
|
||||
|
||||
|
||||
+22
-11
@@ -1,18 +1,29 @@
|
||||
project('system4', 'c',
|
||||
meson_version: '>= 0.59',
|
||||
default_options : ['c_std=c11'])
|
||||
add_project_arguments('-D_DEFAULT_SOURCE', language : 'c')
|
||||
add_project_arguments('-DXSYS4_DATA_DIR="' + get_option('prefix') / get_option('datadir') / 'xsystem4"', language : 'c')
|
||||
|
||||
zlib = dependency('zlib')
|
||||
static_libs = false
|
||||
if host_machine.system() == 'windows'
|
||||
static_libs = true
|
||||
endif
|
||||
|
||||
zlib = dependency('zlib', static : static_libs)
|
||||
libm = meson.get_compiler('c').find_library('m', required: false)
|
||||
sdl2 = dependency('sdl2')
|
||||
ft2 = dependency('freetype2')
|
||||
ffi = dependency('libffi')
|
||||
tj = dependency('libturbojpeg')
|
||||
webp = dependency('libwebp')
|
||||
png = dependency('libpng')
|
||||
cglm = dependency('cglm', fallback : ['cglm', 'cglm_dep'])
|
||||
sndfile = dependency('sndfile')
|
||||
sdl2 = dependency('sdl2', static : static_libs)
|
||||
ft2 = dependency('freetype2', static : static_libs)
|
||||
ffi = dependency('libffi', static : static_libs)
|
||||
tj = dependency('libturbojpeg', static : static_libs)
|
||||
webp = dependency('libwebp', static : static_libs)
|
||||
png = dependency('libpng', static : static_libs)
|
||||
cglm = dependency('cglm', version : '>=0.8.3', fallback : ['cglm', 'cglm_dep'])
|
||||
sndfile = dependency('sndfile', static : static_libs)
|
||||
|
||||
avcodec = dependency('libavcodec', required: false, static : static_libs)
|
||||
avformat = dependency('libavformat', required: false, static : static_libs)
|
||||
avutil = dependency('libavutil', required: false, static : static_libs)
|
||||
swscale = dependency('libswscale', required: false, static : static_libs)
|
||||
|
||||
gles = dependency('glesv2', version : '>=3', required: get_option('opengles'))
|
||||
if gles.found()
|
||||
@@ -24,8 +35,8 @@ else
|
||||
gl_deps = [gl, glew]
|
||||
endif
|
||||
|
||||
chibi = dependency('chibi-scheme', required : false)
|
||||
readline = dependency('readline', required : false)
|
||||
chibi = dependency('chibi-scheme', required : false, static : static_libs)
|
||||
readline = dependency('readline', required : false, static : static_libs)
|
||||
|
||||
flex = find_program('flex')
|
||||
bison = find_program('bison')
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
*/
|
||||
|
||||
uniform sampler2D tex;
|
||||
uniform float alpha_mod;
|
||||
uniform float blend_rate;
|
||||
uniform vec2 bot_left;
|
||||
uniform vec2 top_right;
|
||||
uniform vec3 add_color;
|
||||
@@ -36,5 +36,5 @@ void main() {
|
||||
|
||||
vec4 tex_color = texture(tex, tex_coord);
|
||||
vec3 mod_color = (tex_color.rgb + add_color) * multiply_color;
|
||||
frag_color = vec4(mod_color, tex_color.a * alpha_mod) * point_in_rect(tex_coord, bl, tr);
|
||||
frag_color = vec4(mod_color, tex_color.a * blend_rate) * point_in_rect(tex_coord, bl, tr);
|
||||
}
|
||||
|
||||
@@ -15,12 +15,10 @@
|
||||
*/
|
||||
|
||||
uniform sampler2D tex;
|
||||
uniform float alpha_mod;
|
||||
|
||||
in vec2 tex_coord;
|
||||
out vec4 frag_color;
|
||||
|
||||
void main() {
|
||||
frag_color = texture(tex, tex_coord);
|
||||
frag_color.a *= alpha_mod;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
/* Copyright (C) 2023 Nunuhara Cabbage <nunuhara@haniwa.technology>
|
||||
*
|
||||
* 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/>.
|
||||
*/
|
||||
|
||||
uniform sampler2D tex;
|
||||
uniform float blend_rate;
|
||||
|
||||
in vec2 tex_coord;
|
||||
out vec4 frag_color;
|
||||
|
||||
void main() {
|
||||
frag_color = texture(tex, tex_coord);
|
||||
frag_color.a *= blend_rate;
|
||||
}
|
||||
+45
-22
@@ -18,7 +18,6 @@
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <ctype.h>
|
||||
#include <libgen.h>
|
||||
#include <dirent.h>
|
||||
#include <limits.h>
|
||||
#include <assert.h>
|
||||
@@ -206,7 +205,8 @@ static bool afa_load_archive(struct asset_manager *_manager, const char *name)
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool _afa_get_by_id(struct asset_manager *_manager, int id, struct afa_archive **ar_out, int *no_out)
|
||||
static bool _afa_get_by_index(struct asset_manager *_manager, int id,
|
||||
struct afa_archive **ar_out, int *no_out)
|
||||
{
|
||||
struct asset_manager_afa *manager = (struct asset_manager_afa*)_manager;
|
||||
uint32_t no = id - 1;
|
||||
@@ -221,25 +221,45 @@ static bool _afa_get_by_id(struct asset_manager *_manager, int id, struct afa_ar
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool afa_exists_by_id(struct asset_manager *manager, int id)
|
||||
// XXX: id-indexed afa
|
||||
static bool afa_exists_by_id(struct asset_manager *_manager, int id)
|
||||
{
|
||||
int no;
|
||||
struct afa_archive *ar;
|
||||
return _afa_get_by_id(manager, id, &ar, &no);
|
||||
struct asset_manager_afa *manager = (struct asset_manager_afa*)_manager;
|
||||
for (int i = 0; i < MAX_ARCHIVES && manager->archives[i]; i++) {
|
||||
if (archive_exists(&manager->archives[i]->ar, id - 1))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static struct archive_data *afa_get_by_id(struct asset_manager *manager, int id)
|
||||
// XXX: string-indexed afa
|
||||
static bool afa_exists_by_index(struct asset_manager *manager, int index)
|
||||
{
|
||||
int no;
|
||||
struct afa_archive *ar;
|
||||
if (!_afa_get_by_id(manager, id, &ar, &no))
|
||||
return _afa_get_by_index(manager, index, &ar, &no);
|
||||
}
|
||||
|
||||
// XXX: id-indexed afa
|
||||
static struct archive_data *afa_get_by_id(struct asset_manager *_manager, int id)
|
||||
{
|
||||
struct asset_manager_afa *manager = (struct asset_manager_afa*)_manager;
|
||||
for (int i = 0; i < MAX_ARCHIVES && manager->archives[i]; i++) {
|
||||
struct archive_data *data = archive_get(&manager->archives[i]->ar, id - 1);
|
||||
if (data)
|
||||
return data;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// XXX: string-indexed afa
|
||||
static struct archive_data *afa_get_by_index(struct asset_manager *manager, int index)
|
||||
{
|
||||
int no;
|
||||
struct afa_archive *ar;
|
||||
if (!_afa_get_by_index(manager, index, &ar, &no))
|
||||
return NULL;
|
||||
// XXX: we don't use archive_get here because it will return the wrong thing
|
||||
// for AFAv1 archives when not using integer indices
|
||||
struct archive_data *data = afa_entry_to_descriptor(ar, &ar->files[no]);
|
||||
if (data)
|
||||
archive_load_file(data);
|
||||
return data;
|
||||
return archive_get(&ar->ar, no);
|
||||
}
|
||||
|
||||
static void afa_index_archive(struct hash_table *index, struct afa_archive *ar, unsigned base)
|
||||
@@ -285,7 +305,7 @@ static bool _afa_get_by_name(struct asset_manager *_manager, const char *_name,
|
||||
|
||||
if (id_out)
|
||||
*id_out = id;
|
||||
return _afa_get_by_id(_manager, id, ar_out, no_out);
|
||||
return _afa_get_by_index(_manager, id, ar_out, no_out);
|
||||
}
|
||||
|
||||
static bool afa_exists_by_name(struct asset_manager *manager, const char *name, int *id_out)
|
||||
@@ -347,18 +367,21 @@ static void afa_init(enum asset_type type, char *file)
|
||||
if (!ar)
|
||||
ERROR("Failed to open AFA file: %s", archive_strerror(error));
|
||||
|
||||
struct asset_manager_afa *manager = xcalloc(1, sizeof(struct asset_manager_afa));
|
||||
manager->manager.load_archive = afa_load_archive;
|
||||
if (id_indexed_afa) {
|
||||
_ald_init(type, &ar->ar);
|
||||
} else {
|
||||
struct asset_manager_afa *manager = xcalloc(1, sizeof(struct asset_manager_afa));
|
||||
manager->manager.load_archive = afa_load_archive;
|
||||
manager->manager.exists_by_id = afa_exists_by_id;
|
||||
manager->manager.exists_by_name = afa_exists_by_name;
|
||||
manager->manager.get_by_id = afa_get_by_id;
|
||||
manager->manager.exists_by_name = NULL;
|
||||
manager->manager.get_by_name = NULL;
|
||||
} else {
|
||||
manager->manager.exists_by_name = afa_exists_by_name;
|
||||
manager->manager.get_by_name = afa_get_by_name;
|
||||
manager->archives[0] = ar;
|
||||
assets[type] = &manager->manager;
|
||||
manager->manager.exists_by_id = afa_exists_by_index;
|
||||
manager->manager.get_by_id = afa_get_by_index;
|
||||
}
|
||||
manager->archives[0] = ar;
|
||||
assets[type] = &manager->manager;
|
||||
|
||||
free(file);
|
||||
}
|
||||
|
||||
+5
-1
@@ -35,6 +35,7 @@ bool game_daibanchou_en = false;
|
||||
bool game_rance02_mg = false;
|
||||
bool game_rance6_mg = false;
|
||||
bool game_rance7_mg = false;
|
||||
bool game_rance8 = false;
|
||||
|
||||
bool id_indexed_afa = false;
|
||||
|
||||
@@ -113,7 +114,7 @@ static void apply_rance02_hacks(struct ain *ain)
|
||||
|
||||
// multiply by config.text_x_scale (if not 1.0)
|
||||
buffer_write_int32_at(&out, jump_addr + 2, out.index);
|
||||
if (fabsf(1.0 - config.text_x_scale) > 0.01) {
|
||||
if (fabsf(1.0f - config.text_x_scale) > 0.01) {
|
||||
union { int32_t i; float f; } cast = { .f = config.text_x_scale };
|
||||
write_instruction0(&out, ITOF);
|
||||
write_instruction1(&out, F_PUSH, cast.i);
|
||||
@@ -171,6 +172,9 @@ void apply_game_specific_hacks(struct ain *ain)
|
||||
id_indexed_afa = true;
|
||||
} else if (!strcmp(game_name, "大帝国")) {
|
||||
id_indexed_afa = true;
|
||||
} else if (!strcmp(game_name, "ランス・クエスト") || !strcmp(game_name, "Rance Quest")) {
|
||||
id_indexed_afa = true;
|
||||
game_rance8 = true;
|
||||
}
|
||||
free(game_name);
|
||||
}
|
||||
|
||||
@@ -106,6 +106,7 @@ int CharSpriteManager_CreateHandle(void)
|
||||
int handle = get_handle();
|
||||
assert(!chars.sprites[handle]);
|
||||
chars.sprites[handle] = xcalloc(1, sizeof(struct charsprite));
|
||||
sprite_init(&chars.sprites[handle]->sp);
|
||||
return handle;
|
||||
}
|
||||
|
||||
@@ -178,11 +179,10 @@ static void charsprite_render(struct charsprite *cs)
|
||||
if (isascii(ch[0]))
|
||||
w /= 2;
|
||||
|
||||
sprite_init(&cs->sp, w, h, 0, 0, 0, 0);
|
||||
sprite_init_color(&cs->sp, w, h, 0, 0, 0, 0);
|
||||
sprite_get_texture(&cs->sp); // XXX: force initialization of texture
|
||||
gfx_render_text(&cs->sp.texture, 0.0f, 0, ch, &cs->ts);
|
||||
sprite_dirty(&cs->sp);
|
||||
|
||||
}
|
||||
|
||||
void CharSpriteManager_Rebuild(void)
|
||||
|
||||
@@ -37,9 +37,14 @@ static void ChipmunkSpriteEngine_ModuleFini(void)
|
||||
sact_ModuleFini();
|
||||
}
|
||||
|
||||
static int ChipmunkSpriteEngine_Init(void *imain_system)
|
||||
static int ChipmunkSpriteEngine_Init(possibly_unused void *imain_system)
|
||||
{
|
||||
return sact_Init(imain_system, 16);
|
||||
return sact_init(16, true);
|
||||
}
|
||||
|
||||
static int ChipmunkSpriteEngine_Init_with_size(possibly_unused void *imain_system, int cg_cache_size)
|
||||
{
|
||||
return sact_init(cg_cache_size, true);
|
||||
}
|
||||
|
||||
static int ChipmunkSpriteEngine_SP_SetCG(int sp_no, struct string *cg_name)
|
||||
@@ -49,24 +54,53 @@ static int ChipmunkSpriteEngine_SP_SetCG(int sp_no, struct string *cg_name)
|
||||
return sprite_set_cg_by_name(sp, cg_name->text);
|
||||
}
|
||||
|
||||
// XXX: Rance Quest
|
||||
static int ChipmunkSpriteEngine_SP_SetCG_by_string_id(int sp_no, struct string *cg_no)
|
||||
{
|
||||
struct sact_sprite *sp = sact_get_sprite(sp_no);
|
||||
if (!sp) return 0;
|
||||
return sprite_set_cg_from_asset(sp, atoi(cg_no->text));
|
||||
}
|
||||
|
||||
static int ChipmunkSpriteEngine_CG_IsExist(struct string *cg_name)
|
||||
{
|
||||
return asset_exists_by_name(ASSET_CG, cg_name->text, NULL);
|
||||
}
|
||||
|
||||
static int ChipmunkSpriteEngine_CG_GetMetrics(struct string *cg_name, struct page **page)
|
||||
// XXX: Rance Quest
|
||||
static int ChipmunkSpriteEngine_CG_IsExist_by_string_id(struct string *cg_no)
|
||||
{
|
||||
return asset_exists(ASSET_CG, atoi(cg_no->text));
|
||||
}
|
||||
|
||||
static void set_cg_metrics(struct cg_metrics *metrics, struct page **page)
|
||||
{
|
||||
union vm_value *cgm = (*page)->values;
|
||||
cgm[0].i = metrics->w;
|
||||
cgm[1].i = metrics->h;
|
||||
cgm[2].i = metrics->bpp;
|
||||
cgm[3].i = metrics->has_pixel;
|
||||
cgm[4].i = metrics->has_alpha;
|
||||
cgm[5].i = metrics->pixel_pitch;
|
||||
cgm[6].i = metrics->alpha_pitch;
|
||||
}
|
||||
|
||||
static int ChipmunkSpriteEngine_CG_GetMetrics(struct string *cg_name, struct page **page)
|
||||
{
|
||||
struct cg_metrics metrics;
|
||||
if (!asset_cg_get_metrics_by_name(cg_name->text, &metrics))
|
||||
return 0;
|
||||
cgm[0].i = metrics.w;
|
||||
cgm[1].i = metrics.h;
|
||||
cgm[2].i = metrics.bpp;
|
||||
cgm[3].i = metrics.has_pixel;
|
||||
cgm[4].i = metrics.has_alpha;
|
||||
cgm[5].i = metrics.pixel_pitch;
|
||||
cgm[6].i = metrics.alpha_pitch;
|
||||
set_cg_metrics(&metrics, page);
|
||||
return 1;
|
||||
}
|
||||
|
||||
// XXX: Rance Quest
|
||||
static int ChipmunkSpriteEngine_CG_GetMetrics_by_string_id(struct string *cg_no, struct page **page)
|
||||
{
|
||||
struct cg_metrics metrics;
|
||||
if (!asset_cg_get_metrics(atoi(cg_no->text), &metrics))
|
||||
return 0;
|
||||
set_cg_metrics(&metrics, page);
|
||||
return 1;
|
||||
}
|
||||
|
||||
@@ -87,10 +121,42 @@ static void ChipmunkSpriteEngine_Sleep(void)
|
||||
}
|
||||
|
||||
//static bool ChipmunkSpriteEngine_SP_SetCutCG(int sp_no, int cg_no, int cut_x, int cut_y, int cut_w, int cut_h);
|
||||
//static bool ChipmunkSpriteEngine_SP_SetMultipleColor(int sp_no, int r, int g, int b);
|
||||
//static bool ChipmunkSpriteEngine_SP_SetAddColor(int sp_no, int r, int g, int b, int a);
|
||||
//static bool ChipmunkSpriteEngine_SP_SetSurfaceArea(int sp_no, int x, int y, int w, int h);
|
||||
//static bool ChipmunkSpriteEngine_SP_GetSurfaceArea(int sp_no, int *x, int *y, int *w, int *h);
|
||||
|
||||
static bool ChipmunkSpriteEngine_SP_SetMultipleColor(int sp_no, int r, int g, int b)
|
||||
{
|
||||
struct sact_sprite *sp = sact_get_sprite(sp_no);
|
||||
if (!sp) return false;
|
||||
sprite_set_multiply_color(sp, r, g, b);
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool ChipmunkSpriteEngine_SP_SetAddColor(int sp_no, int r, int g, int b, int a)
|
||||
{
|
||||
struct sact_sprite *sp = sact_get_sprite(sp_no);
|
||||
if (!sp) return false;
|
||||
sprite_set_add_color(sp, r, g, b);
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool ChipmunkSpriteEngine_SP_SetSurfaceArea(int sp_no, int x, int y, int w, int h)
|
||||
{
|
||||
struct sact_sprite *sp = sact_get_sprite(sp_no);
|
||||
if (!sp) return false;
|
||||
sprite_set_surface_area(sp, x, y, w, h);
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool ChipmunkSpriteEngine_SP_GetSurfaceArea(int sp_no, int *x, int *y, int *w, int *h)
|
||||
{
|
||||
struct sact_sprite *sp = sact_get_sprite(sp_no);
|
||||
if (!sp) return false;
|
||||
*x = sp->surface_area.x;
|
||||
*y = sp->surface_area.y;
|
||||
*w = sp->surface_area.w;
|
||||
*h = sp->surface_area.h;
|
||||
return true;
|
||||
}
|
||||
|
||||
//static bool ChipmunkSpriteEngine_SP_SetSpriteTransformMode(int sp_no, int transform_mode);
|
||||
//static bool ChipmunkSpriteEngine_SP_SetSpriteTransformPos(int sp_no, float x0, float y0, float z0, float w0, float u0, float v0, float x1, float y1, float z1, float w1, float u1, float v1, float x2, float y2, float z2, float w2, float u2, float v2, float x3, float y3, float z3, float w3, float u3, float v3);
|
||||
//static bool ChipmunkSpriteEngine_Sprite_SetTextureFilerType(int sp_no, int texture_filter_type);
|
||||
@@ -146,12 +212,12 @@ HLL_LIBRARY(ChipmunkSpriteEngine,
|
||||
HLL_EXPORT(SP_SetY, sact_SP_SetY),
|
||||
HLL_EXPORT(SP_SetZ, sact_SP_SetZ),
|
||||
HLL_EXPORT(SP_SetBlendRate, sact_SP_SetBlendRate),
|
||||
HLL_TODO_EXPORT(SP_SetMultipleColor, ChipmunkSpriteEngine_SP_SetMultipleColor),
|
||||
HLL_TODO_EXPORT(SP_SetAddColor, ChipmunkSpriteEngine_SP_SetAddColor),
|
||||
HLL_EXPORT(SP_SetMultipleColor, ChipmunkSpriteEngine_SP_SetMultipleColor),
|
||||
HLL_EXPORT(SP_SetAddColor, ChipmunkSpriteEngine_SP_SetAddColor),
|
||||
HLL_EXPORT(SP_SetShow, sact_SP_SetShow),
|
||||
HLL_EXPORT(SP_SetDrawMethod, sact_SP_SetDrawMethod),
|
||||
HLL_TODO_EXPORT(SP_SetSurfaceArea, ChipmunkSpriteEngine_SP_SetSurfaceArea),
|
||||
HLL_TODO_EXPORT(SP_GetSurfaceArea, ChipmunkSpriteEngine_SP_GetSurfaceArea),
|
||||
HLL_EXPORT(SP_SetSurfaceArea, ChipmunkSpriteEngine_SP_SetSurfaceArea),
|
||||
HLL_EXPORT(SP_GetSurfaceArea, ChipmunkSpriteEngine_SP_GetSurfaceArea),
|
||||
HLL_TODO_EXPORT(SP_SetSpriteTransformMode, ChipmunkSpriteEngine_SP_SetSpriteTransformMode),
|
||||
HLL_TODO_EXPORT(SP_SetSpriteTransformPos, ChipmunkSpriteEngine_SP_SetSpriteTransformPos),
|
||||
HLL_TODO_EXPORT(Sprite_SetTextureFilerType, ChipmunkSpriteEngine_Sprite_SetTextureFilerType),
|
||||
@@ -222,8 +288,8 @@ HLL_LIBRARY(ChipmunkSpriteEngine,
|
||||
HLL_EXPORT(TRANS_Begin, sact_TRANS_Begin),
|
||||
HLL_EXPORT(TRANS_Update, sact_TRANS_Update),
|
||||
HLL_EXPORT(TRANS_End, sact_TRANS_End),
|
||||
HLL_TODO_EXPORT(VIEW_SetMode, SACTDX_VIEW_SetMode),
|
||||
HLL_TODO_EXPORT(VIEW_GetMode, SACTDX_VIEW_GetMode),
|
||||
HLL_EXPORT(VIEW_SetMode, sact_VIEW_SetMode),
|
||||
HLL_EXPORT(VIEW_GetMode, sact_VIEW_GetMode),
|
||||
HLL_EXPORT(VIEW_SetOffsetPos, ChipmunkSpriteEngine_VIEW_SetOffsetPos),
|
||||
HLL_TODO_EXPORT(DX_GetUsePower2Texture, SACTDX_DX_GetUsePower2Texture),
|
||||
HLL_TODO_EXPORT(DX_SetUsePower2Texture, SACTDX_DX_SetUsePower2Texture),
|
||||
@@ -282,23 +348,32 @@ static void ChipmunkSpriteEngine_PreLink(void)
|
||||
|
||||
fun = get_fun(libno, "Init");
|
||||
if (fun && fun->nr_arguments == 2) {
|
||||
static_library_replace(&lib_ChipmunkSpriteEngine, "Init", sact_Init);
|
||||
static_library_replace(&lib_ChipmunkSpriteEngine, "Init", ChipmunkSpriteEngine_Init_with_size);
|
||||
}
|
||||
|
||||
fun = get_fun(libno, "SP_SetCG");
|
||||
if (fun && fun->arguments[1].type.data == AIN_INT) {
|
||||
if (fun && game_rance8) {
|
||||
static_library_replace(&lib_ChipmunkSpriteEngine, "SP_SetCG",
|
||||
ChipmunkSpriteEngine_SP_SetCG_by_string_id);
|
||||
} else if (fun && fun->arguments[1].type.data == AIN_INT) {
|
||||
static_library_replace(&lib_ChipmunkSpriteEngine, "SP_SetCG", sact_SP_SetCG);
|
||||
}
|
||||
|
||||
// TODO: SP_SetCutCG
|
||||
|
||||
fun = get_fun(libno, "CG_IsExist");
|
||||
if (fun && fun->arguments[0].type.data == AIN_INT) {
|
||||
if (fun && game_rance8) {
|
||||
static_library_replace(&lib_ChipmunkSpriteEngine, "CG_IsExist",
|
||||
ChipmunkSpriteEngine_CG_IsExist_by_string_id);
|
||||
} else if (fun && fun->arguments[0].type.data == AIN_INT) {
|
||||
static_library_replace(&lib_ChipmunkSpriteEngine, "CG_IsExist", sact_CG_IsExist);
|
||||
}
|
||||
|
||||
fun = get_fun(libno, "CG_GetMetrics");
|
||||
if (fun && fun->arguments[0].type.data == AIN_INT) {
|
||||
if (fun && game_rance8) {
|
||||
static_library_replace(&lib_ChipmunkSpriteEngine, "CG_GetMetrics",
|
||||
ChipmunkSpriteEngine_CG_GetMetrics_by_string_id);
|
||||
} else if (fun && fun->arguments[0].type.data == AIN_INT) {
|
||||
static_library_replace(&lib_ChipmunkSpriteEngine, "CG_GetMetrics", sact_CG_GetMetrics);
|
||||
}
|
||||
|
||||
|
||||
@@ -408,6 +408,7 @@ HLL_LIBRARY(DrawDungeon,
|
||||
DRAW_DUNGEON_EXPORTS
|
||||
);
|
||||
|
||||
HLL_WARN_UNIMPLEMENTED( , void, DrawDungeon14, SetRasterScroll, int surface, int type);
|
||||
HLL_WARN_UNIMPLEMENTED( , void, DrawDungeon14, SetRasterAmp, int nSurface, float fAmp);
|
||||
HLL_WARN_UNIMPLEMENTED( , void, DrawDungeon14, SetLapFilter, int surface, int type, int r, int g, int b);
|
||||
|
||||
@@ -431,7 +432,7 @@ HLL_LIBRARY(DrawDungeon14,
|
||||
HLL_EXPORT(Init, DrawDungeon14_Init),
|
||||
DRAW_DUNGEON_EXPORTS,
|
||||
HLL_EXPORT(GetWalked, dungeon_get_walked),
|
||||
HLL_TODO_EXPORT(SetRasterScroll, DrawDungeon14_SetRasterScroll),
|
||||
HLL_EXPORT(SetRasterScroll, DrawDungeon14_SetRasterScroll),
|
||||
HLL_EXPORT(SetRasterAmp, DrawDungeon14_SetRasterAmp),
|
||||
HLL_EXPORT(SetLapFilter, DrawDungeon14_SetLapFilter),
|
||||
HLL_EXPORT(GetDrawMapObjectFlag, DrawDungeon14_GetDrawMapObjectFlag),
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
|
||||
#include "asset_manager.h"
|
||||
#include "parts.h"
|
||||
#include "xsystem4.h"
|
||||
#include "hll.h"
|
||||
|
||||
static void GoatGUIEngine_PreLink(void);
|
||||
@@ -67,8 +68,8 @@ HLL_LIBRARY(GoatGUIEngine,
|
||||
HLL_EXPORT(SetShow, PE_SetShow),
|
||||
HLL_EXPORT(SetAlpha, PE_SetAlpha),
|
||||
HLL_TODO_EXPORT(SetPartsDrawFilter, PE_SetPartsDrawFilter),
|
||||
HLL_TODO_EXPORT(SetAddColor, PE_SetAddColor),
|
||||
HLL_TODO_EXPORT(SetMultiplyColor, PE_SetMultiplyColor),
|
||||
HLL_EXPORT(SetAddColor, PE_SetAddColor),
|
||||
HLL_EXPORT(SetMultiplyColor, PE_SetMultiplyColor),
|
||||
HLL_EXPORT(SetClickable, PE_SetClickable),
|
||||
HLL_EXPORT(GetPartsX, PE_GetPartsX),
|
||||
HLL_EXPORT(GetPartsY, PE_GetPartsY),
|
||||
@@ -155,7 +156,10 @@ static void GoatGUIEngine_PreLink(void)
|
||||
assert(libno >= 0);
|
||||
|
||||
fun = get_fun(libno, "SetPartsCG");
|
||||
if (fun && fun->arguments[1].type.data == AIN_INT) {
|
||||
if (fun && game_rance8) {
|
||||
static_library_replace(&lib_GoatGUIEngine, "SetPartsCG", PE_SetPartsCG_by_string_index);
|
||||
}
|
||||
else if (fun && fun->arguments[1].type.data == AIN_INT) {
|
||||
static_library_replace(&lib_GoatGUIEngine, "SetPartsCG", PE_SetPartsCG_by_index);
|
||||
}
|
||||
|
||||
|
||||
@@ -14,12 +14,11 @@
|
||||
* along with this program; if not, see <http://gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <time.h>
|
||||
|
||||
#include "system4/string.h"
|
||||
|
||||
#include "hll.h"
|
||||
#include "input.h"
|
||||
#include "vm.h"
|
||||
|
||||
static void handle_events_throttled(void)
|
||||
{
|
||||
@@ -28,11 +27,7 @@ static void handle_events_throttled(void)
|
||||
static int count;
|
||||
if (++count == 30) {
|
||||
count = 0;
|
||||
struct timespec ts = {
|
||||
.tv_sec = 0,
|
||||
.tv_nsec = 10000000L
|
||||
};
|
||||
nanosleep(&ts, NULL);
|
||||
vm_sleep(10);
|
||||
}
|
||||
handle_events();
|
||||
}
|
||||
|
||||
+32
-20
@@ -25,6 +25,7 @@
|
||||
#include "system4/string.h"
|
||||
#include "system4/utfsjis.h"
|
||||
|
||||
#include "little_endian.h"
|
||||
#include "vm.h"
|
||||
#include "xsystem4.h"
|
||||
|
||||
@@ -43,38 +44,49 @@ static void msgskip_save(void)
|
||||
if (!f)
|
||||
ERROR("fopen: '%s': %s", display_utf0(save_path), strerror(errno));
|
||||
|
||||
fwrite(flags, nr_flags, 1, f);
|
||||
uint8_t header[4];
|
||||
LittleEndian_putDW(header, 0, nr_flags);
|
||||
fwrite(header, sizeof(header), 1, f);
|
||||
fwrite(flags, (nr_flags + 7) / 8, 1, f);
|
||||
fclose(f);
|
||||
free(flags);
|
||||
}
|
||||
|
||||
static bool msgskip_initialized = false;
|
||||
|
||||
static int MsgSkip_Init(struct string *name)
|
||||
{
|
||||
if (msgskip_initialized)
|
||||
if (flags)
|
||||
return 1;
|
||||
|
||||
nr_flags = ain->nr_messages;
|
||||
flags = xcalloc((nr_flags + 7) / 8, 1);
|
||||
|
||||
save_path = unix_path(name->text);
|
||||
|
||||
uint8_t *data;
|
||||
size_t data_size;
|
||||
if (file_exists(save_path)) {
|
||||
data = file_read(save_path, &data_size);
|
||||
} else {
|
||||
data = xcalloc(ain->nr_messages, 1);
|
||||
data_size = ain->nr_messages;
|
||||
}
|
||||
if (data_size != (size_t)ain->nr_messages) {
|
||||
WARNING("Incorrect file size for MsgSkip file '%s'", display_utf0(save_path));
|
||||
if (data_size < (size_t)ain->nr_messages) {
|
||||
data = xrealloc_array(data, data_size, ain->nr_messages, 1);
|
||||
uint8_t *data = file_read(save_path, &data_size);
|
||||
if (data) {
|
||||
if (data_size == (size_t)ain->nr_messages) {
|
||||
// Migrate from the old format.
|
||||
for (int i = 0; i < data_size; i++) {
|
||||
if (data[i])
|
||||
flags[i >> 3] |= 0x80 >> (i & 7);
|
||||
}
|
||||
} else {
|
||||
int n = LittleEndian_getDW(data, 0);
|
||||
if ((n + 7) / 8 != data_size - 4) {
|
||||
WARNING("Corrupted MsgSkip file '%s'", display_utf0(save_path));
|
||||
} else if (n != nr_flags) {
|
||||
WARNING("Incorrect size for MsgSkip file '%s'", display_utf0(save_path));
|
||||
if (n > nr_flags)
|
||||
n = nr_flags;
|
||||
memcpy(flags, data + 4, (n + 7) / 8);
|
||||
} else {
|
||||
memcpy(flags, data + 4, (nr_flags + 7) / 8);
|
||||
}
|
||||
}
|
||||
free(data);
|
||||
}
|
||||
flags = data;
|
||||
nr_flags = ain->nr_messages;
|
||||
atexit(msgskip_save);
|
||||
msgskip_initialized = true;
|
||||
return 1;
|
||||
}
|
||||
|
||||
@@ -82,14 +94,14 @@ static void MsgSkip_SetFlag(int msgnum)
|
||||
{
|
||||
if (msgnum < 0 || msgnum >= nr_flags)
|
||||
return;
|
||||
flags[msgnum] = 1;
|
||||
flags[msgnum >> 3] |= 0x80 >> (msgnum & 7);
|
||||
}
|
||||
|
||||
static int MsgSkip_GetFlag(int msgnum)
|
||||
{
|
||||
if (msgnum < 0 || msgnum >= nr_flags)
|
||||
return 0;
|
||||
return !!flags[msgnum];
|
||||
return !!(flags[msgnum >> 3] & 0x80 >> (msgnum & 7));
|
||||
}
|
||||
|
||||
static void MsgSkip_SetEnable(int enable)
|
||||
|
||||
+22
-9
@@ -46,6 +46,7 @@ static struct sact_sprite *sact_alloc_sprite(int sp)
|
||||
sprites[sp] = xcalloc(1, sizeof(struct sact_sprite));
|
||||
sprites[sp]->no = sp;
|
||||
sprites[sp]->sp.z2 = sp;
|
||||
sprite_init(sprites[sp]);
|
||||
sprite_dirty(sprites[sp]);
|
||||
return sprites[sp];
|
||||
}
|
||||
@@ -92,7 +93,7 @@ struct sact_sprite *sact_try_get_sprite(int sp)
|
||||
return sprites[sp];
|
||||
}
|
||||
|
||||
int sact_Init(possibly_unused void *_, possibly_unused int cg_cache_size)
|
||||
int sact_init(possibly_unused int cg_cache_size, bool chipmunk)
|
||||
{
|
||||
// already initialized
|
||||
if (sprites)
|
||||
@@ -112,9 +113,21 @@ int sact_Init(possibly_unused void *_, possibly_unused int cg_cache_size)
|
||||
sp->texture = *t; // XXX: textures normally shouldn't be copied like this...
|
||||
|
||||
sprites++;
|
||||
|
||||
// initialize sprite renderer
|
||||
if (chipmunk)
|
||||
sprite_init_chipmunk();
|
||||
else
|
||||
sprite_init_sact();
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int sact_Init(possibly_unused void *imain_system, int cg_cache_size)
|
||||
{
|
||||
return sact_init(cg_cache_size, false);
|
||||
}
|
||||
|
||||
void sact_ModuleFini(void)
|
||||
{
|
||||
sact_SP_DeleteAll();
|
||||
@@ -267,7 +280,7 @@ struct sact_sprite *sact_create_sprite(int sp_no, int width, int height, int r,
|
||||
{
|
||||
struct sact_sprite *sp = sact_get_sprite(sp_no);
|
||||
if (!sp) return NULL;
|
||||
sprite_init(sp, width, height, r, g, b, a);
|
||||
sprite_init_color(sp, width, height, r, g, b, a);
|
||||
return sp;
|
||||
}
|
||||
|
||||
@@ -348,11 +361,11 @@ int sact_SP_SetBlendRate(int sp_no, int rate)
|
||||
return 1;
|
||||
}
|
||||
|
||||
int sact_SP_SetShow(int sp_no, bool show)
|
||||
int sact_SP_SetShow(int sp_no, int show)
|
||||
{
|
||||
struct sact_sprite *sp = sact_get_sprite(sp_no);
|
||||
if (!sp) return 0;
|
||||
sprite_set_show(sp, show);
|
||||
sprite_set_show(sp, !!show);
|
||||
return 1;
|
||||
}
|
||||
|
||||
@@ -508,7 +521,7 @@ int _sact_SP_TextDraw(int sp_no, struct string *text, struct text_style *ts)
|
||||
// XXX: In AliceSoft's implementation, this function succeeds even with
|
||||
// a negative sp_no...
|
||||
struct sact_sprite *sp = sact_get_sprite(sp_no);
|
||||
if (!sp) return 0;
|
||||
if (!sp || !sp->rect.w || !sp->rect.h) return 0;
|
||||
sprite_text_draw(sp, text, ts);
|
||||
return 1;
|
||||
}
|
||||
@@ -945,13 +958,13 @@ HLL_WARN_UNIMPLEMENTED( , void, SACTDX, SetVolumeMixerBGMGroupNum, int n);
|
||||
//static void SACTDX_Key_ClearFlagNoCtrl(void);
|
||||
//static void SACTDX_Key_ClearFlagOne(int nKeyCode);
|
||||
|
||||
static bool SACTDX_VIEW_SetMode(int mode)
|
||||
bool sact_VIEW_SetMode(int mode)
|
||||
{
|
||||
view_mode = mode;
|
||||
return true;
|
||||
}
|
||||
|
||||
static int SACTDX_VIEW_GetMode(void)
|
||||
int sact_VIEW_GetMode(void)
|
||||
{
|
||||
return view_mode;
|
||||
}
|
||||
@@ -982,8 +995,8 @@ static void SACTDX_DX_SetUsePower2Texture(bool use)
|
||||
HLL_EXPORT(TRANS_Begin, sact_TRANS_Begin), \
|
||||
HLL_EXPORT(TRANS_Update, sact_TRANS_Update), \
|
||||
HLL_EXPORT(TRANS_End, sact_TRANS_End), \
|
||||
HLL_EXPORT(VIEW_SetMode, SACTDX_VIEW_SetMode), \
|
||||
HLL_EXPORT(VIEW_GetMode, SACTDX_VIEW_GetMode), \
|
||||
HLL_EXPORT(VIEW_SetMode, sact_VIEW_SetMode), \
|
||||
HLL_EXPORT(VIEW_GetMode, sact_VIEW_GetMode), \
|
||||
HLL_EXPORT(DX_GetUsePower2Texture, SACTDX_DX_GetUsePower2Texture), \
|
||||
HLL_EXPORT(DX_SetUsePower2Texture, SACTDX_DX_SetUsePower2Texture)
|
||||
|
||||
|
||||
@@ -252,6 +252,7 @@ static struct multisprite *multisprite_alloc(int type, int n)
|
||||
ms->ts.space_scale_x = 1.0f;
|
||||
ms->ts.font_spacing = 0.0f;
|
||||
ms->ts.font_size = NULL;
|
||||
sprite_init(&ms->sp);
|
||||
sprite_dirty(&ms->sp);
|
||||
return ms;
|
||||
}
|
||||
@@ -273,7 +274,7 @@ static struct multisprite *multisprite_get(int type, int n)
|
||||
}
|
||||
if (!sp_types[type].sprites[n]) {
|
||||
multisprite_alloc(type, n);
|
||||
sprite_init(&sp_types[type].sprites[n]->sp, 1, 1, 0, 0, 0, 255);
|
||||
sprite_init_color(&sp_types[type].sprites[n]->sp, 1, 1, 0, 0, 0, 255);
|
||||
}
|
||||
return sp_types[type].sprites[n];
|
||||
}
|
||||
@@ -298,7 +299,7 @@ static void multisprite_reset_cg(struct multisprite *sp)
|
||||
|
||||
static int StoatSpriteEngine_Init(void *imain_system, int cg_cache_size)
|
||||
{
|
||||
sact_Init(imain_system, cg_cache_size);
|
||||
sact_init(cg_cache_size, false);
|
||||
for (int i = 0; i < NR_SP_TYPES; i++) {
|
||||
sp_types[i].nr_sprites = 16;
|
||||
sp_types[i].sprites = xcalloc(16, sizeof(struct multisprite*));
|
||||
@@ -451,7 +452,7 @@ static bool StoatSpriteEngine_MultiSprite_SetText(int type, int n, struct string
|
||||
|
||||
int w = text->size * (ms->ts.size/2);
|
||||
int h = ms->ts.size;
|
||||
sprite_init(&ms->sp, w, h, 0, 0, 0, 0);
|
||||
sprite_init_color(&ms->sp, w, h, 0, 0, 0, 0);
|
||||
sprite_get_texture(&ms->sp); // XXX: force initialization of texture
|
||||
gfx_render_text(&ms->sp.texture, 0.0f, 0, text->text, &ms->ts);
|
||||
sprite_dirty(&ms->sp);
|
||||
|
||||
+2
-5
@@ -17,6 +17,7 @@
|
||||
#include <time.h>
|
||||
|
||||
#include "hll.h"
|
||||
#include "vm.h"
|
||||
|
||||
HLL_WARN_UNIMPLEMENTED(1, int, Timer, Init, void *imainsystem);
|
||||
|
||||
@@ -28,11 +29,7 @@ static int Timer_Get(void)
|
||||
|
||||
static void Timer_Wait(int time)
|
||||
{
|
||||
struct timespec ts = {
|
||||
.tv_sec = time / 1000,
|
||||
.tv_nsec = (time % 1000) * 1000000L
|
||||
};
|
||||
nanosleep(&ts, NULL);
|
||||
vm_sleep(time);
|
||||
}
|
||||
|
||||
static void Timer_GetDayTime(int *year, int *month, int *day_of_week, int *day,
|
||||
|
||||
+13
-1
@@ -20,7 +20,6 @@ xsystem4 = [version_h,
|
||||
'heap.c',
|
||||
'id_pool.c',
|
||||
'input.c',
|
||||
'movie.c',
|
||||
'page.c',
|
||||
'resume.c',
|
||||
'savedata.c',
|
||||
@@ -125,6 +124,13 @@ xsystem4 = [version_h,
|
||||
|
||||
xsystem4_deps = [libm, zlib, sdl2, ft2, ffi, cglm, sndfile, libsys4_dep] + gl_deps
|
||||
|
||||
if avcodec.found() and avformat.found() and avutil.found() and swscale.found()
|
||||
xsystem4 += 'movie_ffmpeg.c'
|
||||
xsystem4_deps += [avcodec, avformat, avutil, swscale]
|
||||
else
|
||||
xsystem4 += 'movie_plmpeg.c'
|
||||
endif
|
||||
|
||||
if get_option('debugger').allowed()
|
||||
add_project_arguments('-DDEBUGGER_ENABLED', language : 'c')
|
||||
xsystem4 += 'debug.c'
|
||||
@@ -140,8 +146,14 @@ if get_option('debugger').allowed()
|
||||
endif
|
||||
endif
|
||||
|
||||
static_link_args = []
|
||||
if host_machine.system() == 'windows'
|
||||
static_link_args = ['-static', '-lstdc++']
|
||||
endif
|
||||
|
||||
executable('xsystem4', xsystem4,
|
||||
dependencies : xsystem4_deps,
|
||||
c_args : ['-Wno-unused-parameter'],
|
||||
link_args : static_link_args,
|
||||
include_directories : incdir,
|
||||
install : true)
|
||||
|
||||
@@ -0,0 +1,401 @@
|
||||
/* Copyright (C) 2023 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 <assert.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <SDL.h>
|
||||
#include <libavcodec/avcodec.h>
|
||||
#include <libavformat/avformat.h>
|
||||
#include <libavutil/fifo.h>
|
||||
#include <libavutil/imgutils.h>
|
||||
#include <libswscale/swscale.h>
|
||||
|
||||
#include "system4.h"
|
||||
|
||||
#include "gfx/gfx.h"
|
||||
#include "movie.h"
|
||||
#include "mixer.h"
|
||||
#include "sprite.h"
|
||||
#include "sts_mixer.h"
|
||||
#include "xsystem4.h"
|
||||
|
||||
#define QUEUE_SIZE 10
|
||||
|
||||
struct decoder {
|
||||
AVStream *stream;
|
||||
AVCodecContext *ctx;
|
||||
AVFrame *frame;
|
||||
AVFifoBuffer *queue;
|
||||
bool finished;
|
||||
};
|
||||
|
||||
struct movie_context {
|
||||
AVFormatContext *format_ctx;
|
||||
bool format_eof;
|
||||
SDL_mutex *format_mutex; // also protects video.queue and audio.queue
|
||||
|
||||
struct decoder video;
|
||||
struct decoder audio;
|
||||
|
||||
struct texture temp_surface;
|
||||
|
||||
bool has_pending_video_frame;
|
||||
struct SwsContext *sws_ctx;
|
||||
AVFrame *sws_frame;
|
||||
void *sws_buf;
|
||||
|
||||
sts_mixer_stream_t sts_stream;
|
||||
int bytes_per_sample;
|
||||
int voice;
|
||||
int volume;
|
||||
|
||||
// Time keeping data. Written by audio handler and referenced by video
|
||||
// handler (i.e. we sync the video to the audio).
|
||||
double stream_time; // in seconds
|
||||
uint32_t wall_time_ms;
|
||||
SDL_mutex *timer_mutex;
|
||||
};
|
||||
|
||||
static void free_decoder(struct decoder *dec)
|
||||
{
|
||||
if (dec->ctx)
|
||||
avcodec_free_context(&dec->ctx);
|
||||
if (dec->frame)
|
||||
av_frame_free(&dec->frame);
|
||||
if (dec->queue) {
|
||||
while (av_fifo_size(dec->queue) > 0) {
|
||||
AVPacket *packet;
|
||||
av_fifo_generic_read(dec->queue, &packet, sizeof(AVPacket*), NULL);
|
||||
av_packet_free(&packet);
|
||||
}
|
||||
av_fifo_freep(&dec->queue);
|
||||
}
|
||||
}
|
||||
|
||||
static bool init_decoder(struct decoder *dec, AVStream *stream)
|
||||
{
|
||||
if (!stream)
|
||||
return false;
|
||||
dec->stream = stream;
|
||||
const AVCodec *codec = avcodec_find_decoder(stream->codecpar->codec_id);
|
||||
if (!codec)
|
||||
return false;
|
||||
dec->ctx = avcodec_alloc_context3(codec);
|
||||
if (avcodec_parameters_to_context(dec->ctx, stream->codecpar) < 0)
|
||||
return false;
|
||||
if (avcodec_open2(dec->ctx, codec, NULL) != 0)
|
||||
return false;
|
||||
dec->frame = av_frame_alloc();
|
||||
dec->queue = av_fifo_alloc(sizeof(AVPacket*) * QUEUE_SIZE);
|
||||
return true;
|
||||
}
|
||||
|
||||
static void read_packet(struct movie_context *mc)
|
||||
{
|
||||
if (mc->format_eof)
|
||||
return;
|
||||
|
||||
AVPacket *packet = av_packet_alloc();
|
||||
|
||||
while (av_read_frame(mc->format_ctx, packet) == 0) {
|
||||
if (packet->stream_index == mc->video.stream->index) {
|
||||
if (av_fifo_space(mc->video.queue) < (int)sizeof(AVPacket*))
|
||||
av_fifo_grow(mc->video.queue, sizeof(AVPacket*) * QUEUE_SIZE);
|
||||
av_fifo_generic_write(mc->video.queue, &packet, sizeof(AVPacket *), NULL);
|
||||
return;
|
||||
} else if (packet->stream_index == mc->audio.stream->index) {
|
||||
if (av_fifo_space(mc->audio.queue) < (int)sizeof(AVPacket*))
|
||||
av_fifo_grow(mc->audio.queue, sizeof(AVPacket*) * QUEUE_SIZE);
|
||||
av_fifo_generic_write(mc->audio.queue, &packet, sizeof(AVPacket *), NULL);
|
||||
return;
|
||||
}
|
||||
av_packet_unref(packet);
|
||||
}
|
||||
av_packet_free(&packet);
|
||||
packet = NULL;
|
||||
mc->format_eof = true;
|
||||
av_fifo_generic_write(mc->video.queue, &packet, sizeof(AVPacket *), NULL);
|
||||
av_fifo_generic_write(mc->audio.queue, &packet, sizeof(AVPacket *), NULL);
|
||||
}
|
||||
|
||||
static void preload_packets(struct movie_context *mc)
|
||||
{
|
||||
SDL_LockMutex(mc->format_mutex);
|
||||
while (!mc->format_eof && (av_fifo_size(mc->video.queue) + av_fifo_size(mc->audio.queue) < (int)(QUEUE_SIZE * sizeof(AVPacket*))))
|
||||
read_packet(mc);
|
||||
SDL_UnlockMutex(mc->format_mutex);
|
||||
}
|
||||
|
||||
static bool decode_frame(struct decoder *dec, struct movie_context *mc)
|
||||
{
|
||||
int ret;
|
||||
while ((ret = avcodec_receive_frame(dec->ctx, dec->frame)) == AVERROR(EAGAIN)) {
|
||||
SDL_LockMutex(mc->format_mutex);
|
||||
AVPacket *packet;
|
||||
while (av_fifo_size(dec->queue) == 0)
|
||||
read_packet(mc);
|
||||
av_fifo_generic_read(dec->queue, &packet, sizeof(AVPacket*), NULL);
|
||||
SDL_UnlockMutex(mc->format_mutex);
|
||||
|
||||
if ((ret = avcodec_send_packet(dec->ctx, packet)) != 0) {
|
||||
WARNING("avcodec_send_packet failed: %d", ret);
|
||||
return false;
|
||||
}
|
||||
av_packet_free(&packet);
|
||||
}
|
||||
if (ret == AVERROR_EOF)
|
||||
dec->finished = true;
|
||||
else if (ret)
|
||||
WARNING("avcodec_receive_frame failed: %d", ret);
|
||||
return ret == 0;
|
||||
}
|
||||
|
||||
static int audio_callback(sts_mixer_sample_t *sample, void *data)
|
||||
{
|
||||
struct movie_context *mc = data;
|
||||
assert(sample == &mc->sts_stream.sample);
|
||||
|
||||
if (!decode_frame(&mc->audio, mc)) {
|
||||
free(sample->data);
|
||||
sample->length = 0;
|
||||
sample->data = NULL;
|
||||
mc->voice = -1;
|
||||
return STS_STREAM_COMPLETE;
|
||||
}
|
||||
const int nr_channels = 2;
|
||||
unsigned samples = mc->audio.frame->linesize[0] / mc->bytes_per_sample;
|
||||
if (samples * nr_channels != sample->length) {
|
||||
free(sample->data);
|
||||
sample->length = samples * nr_channels;
|
||||
sample->data = xmalloc(sample->length * mc->bytes_per_sample);
|
||||
}
|
||||
// Interleave.
|
||||
uint8_t *l = mc->audio.frame->data[0];
|
||||
uint8_t *r = mc->audio.frame->data[mc->audio.ctx->channels > 1 ? 1 : 0];
|
||||
uint8_t *out = sample->data;
|
||||
for (unsigned i = 0; i < samples; i++) {
|
||||
memcpy(out, l, mc->bytes_per_sample);
|
||||
out += mc->bytes_per_sample;
|
||||
l += mc->bytes_per_sample;
|
||||
memcpy(out, r, mc->bytes_per_sample);
|
||||
out += mc->bytes_per_sample;
|
||||
r += mc->bytes_per_sample;
|
||||
}
|
||||
|
||||
// Update the timestamp.
|
||||
SDL_LockMutex(mc->timer_mutex);
|
||||
mc->stream_time += (double)samples / mc->audio.ctx->sample_rate;
|
||||
mc->wall_time_ms = SDL_GetTicks();
|
||||
SDL_UnlockMutex(mc->timer_mutex);
|
||||
return STS_STREAM_CONTINUE;
|
||||
}
|
||||
|
||||
struct movie_context *movie_load(const char *filename)
|
||||
{
|
||||
struct movie_context *mc = xcalloc(1, sizeof(struct movie_context));
|
||||
char *path = gamedir_path(filename);
|
||||
int ret;
|
||||
if ((ret = avformat_open_input(&mc->format_ctx, path, NULL, NULL)) != 0) {
|
||||
WARNING("%s: avformat_open_input failed: %d", path, ret);
|
||||
free(path);
|
||||
movie_free(mc);
|
||||
return NULL;
|
||||
}
|
||||
free(path);
|
||||
|
||||
if ((ret = avformat_find_stream_info(mc->format_ctx, NULL)) < 0) {
|
||||
WARNING("avformat_find_stream_info failed: %d", ret);
|
||||
movie_free(mc);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
AVStream *video_stream = NULL;
|
||||
AVStream *audio_stream = NULL;
|
||||
for (int i = 0; i < (int)mc->format_ctx->nb_streams; ++i) {
|
||||
switch (mc->format_ctx->streams[i]->codecpar->codec_type) {
|
||||
case AVMEDIA_TYPE_VIDEO:
|
||||
if (!video_stream)
|
||||
video_stream = mc->format_ctx->streams[i];
|
||||
break;
|
||||
case AVMEDIA_TYPE_AUDIO:
|
||||
if (!audio_stream)
|
||||
audio_stream = mc->format_ctx->streams[i];
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!init_decoder(&mc->video, video_stream)) {
|
||||
WARNING("Cannot initialize video decoder");
|
||||
movie_free(mc);
|
||||
return NULL;
|
||||
}
|
||||
if (!init_decoder(&mc->audio, audio_stream)) {
|
||||
WARNING("Cannot initialize audio decoder");
|
||||
movie_free(mc);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
NOTICE("video: %d x %d, %s", mc->video.ctx->width, mc->video.ctx->height, av_get_pix_fmt_name(mc->video.ctx->pix_fmt));
|
||||
NOTICE("audio: %d hz, %d channels, %s", mc->audio.ctx->sample_rate, mc->audio.ctx->channels, av_get_sample_fmt_name(mc->audio.ctx->sample_fmt));
|
||||
|
||||
mc->format_mutex = SDL_CreateMutex();
|
||||
mc->timer_mutex = SDL_CreateMutex();
|
||||
mc->voice = -1;
|
||||
mc->volume = 100;
|
||||
|
||||
preload_packets(mc);
|
||||
return mc;
|
||||
}
|
||||
|
||||
void movie_free(struct movie_context *mc)
|
||||
{
|
||||
if (mc->voice >= 0)
|
||||
mixer_stream_stop(mc->voice);
|
||||
if (mc->sts_stream.sample.data)
|
||||
free(mc->sts_stream.sample.data);
|
||||
|
||||
if (mc->format_ctx)
|
||||
avformat_close_input(&mc->format_ctx);
|
||||
if (mc->format_mutex)
|
||||
SDL_DestroyMutex(mc->format_mutex);
|
||||
if (mc->timer_mutex)
|
||||
SDL_DestroyMutex(mc->timer_mutex);
|
||||
|
||||
free_decoder(&mc->video);
|
||||
free_decoder(&mc->audio);
|
||||
|
||||
if (mc->temp_surface.handle)
|
||||
gfx_delete_texture(&mc->temp_surface);
|
||||
if (mc->sws_ctx)
|
||||
sws_freeContext(mc->sws_ctx);
|
||||
if (mc->sws_frame)
|
||||
av_frame_free(&mc->sws_frame);
|
||||
if (mc->sws_buf)
|
||||
av_free(mc->sws_buf);
|
||||
|
||||
free(mc);
|
||||
}
|
||||
|
||||
bool movie_play(struct movie_context *mc)
|
||||
{
|
||||
// Start the audio stream.
|
||||
mc->stream_time = 0.0;
|
||||
mc->wall_time_ms = SDL_GetTicks();
|
||||
mc->sts_stream.userdata = mc;
|
||||
mc->sts_stream.callback = audio_callback;
|
||||
mc->sts_stream.sample.frequency = mc->audio.ctx->sample_rate;
|
||||
switch (mc->audio.ctx->sample_fmt) {
|
||||
case AV_SAMPLE_FMT_S16P:
|
||||
mc->sts_stream.sample.audio_format = STS_MIXER_SAMPLE_FORMAT_16;
|
||||
mc->bytes_per_sample = 2;
|
||||
break;
|
||||
case AV_SAMPLE_FMT_S32P:
|
||||
mc->sts_stream.sample.audio_format = STS_MIXER_SAMPLE_FORMAT_32;
|
||||
mc->bytes_per_sample = 4;
|
||||
break;
|
||||
case AV_SAMPLE_FMT_FLTP:
|
||||
mc->sts_stream.sample.audio_format = STS_MIXER_SAMPLE_FORMAT_FLOAT;
|
||||
mc->bytes_per_sample = 4;
|
||||
break;
|
||||
default:
|
||||
WARNING("Unsupported audio format %d", mc->audio.ctx->sample_fmt);
|
||||
return false;
|
||||
}
|
||||
mc->voice = mixer_stream_play(&mc->sts_stream, mc->volume);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool movie_draw(struct movie_context *mc, struct sact_sprite *sprite)
|
||||
{
|
||||
struct texture *texture;
|
||||
if (sprite) {
|
||||
texture = sprite_get_texture(sprite);
|
||||
} else {
|
||||
if (!mc->temp_surface.handle)
|
||||
gfx_init_texture_blank(&mc->temp_surface, config.view_width, config.view_height);
|
||||
texture = &mc->temp_surface;
|
||||
}
|
||||
|
||||
if (!mc->sws_ctx) {
|
||||
mc->sws_ctx = sws_getContext(
|
||||
mc->video.ctx->width, mc->video.ctx->height, mc->video.ctx->pix_fmt,
|
||||
texture->w, texture->h, AV_PIX_FMT_RGBA, SWS_BICUBIC, NULL, NULL, NULL);
|
||||
mc->sws_frame = av_frame_alloc();
|
||||
int size = av_image_get_buffer_size(AV_PIX_FMT_RGBA, texture->w, texture->h, 1);
|
||||
mc->sws_buf = av_malloc(size);
|
||||
av_image_fill_arrays(
|
||||
mc->sws_frame->data, mc->sws_frame->linesize,
|
||||
mc->sws_buf, AV_PIX_FMT_RGBA, texture->w, texture->h, 1);
|
||||
}
|
||||
|
||||
// Decode a frame, unless we already have one.
|
||||
if (!mc->has_pending_video_frame && !decode_frame(&mc->video, mc))
|
||||
return mc->video.finished;
|
||||
|
||||
// If the frame's timestamp is in the future, save the frame and return.
|
||||
double pts = av_q2d(mc->video.stream->time_base) * mc->video.frame->best_effort_timestamp;
|
||||
SDL_LockMutex(mc->timer_mutex);
|
||||
double now = mc->stream_time + (SDL_GetTicks() - mc->wall_time_ms) / 1000.0;
|
||||
SDL_UnlockMutex(mc->timer_mutex);
|
||||
if (pts > now) {
|
||||
mc->has_pending_video_frame = true;
|
||||
// We have spare time, let's buffer some packets.
|
||||
preload_packets(mc);
|
||||
return true;
|
||||
}
|
||||
|
||||
// Convert to RGB and update the texture.
|
||||
sws_scale(mc->sws_ctx, (const uint8_t **)mc->video.frame->data, mc->video.frame->linesize,
|
||||
0, mc->video.ctx->height, mc->sws_frame->data, mc->sws_frame->linesize);
|
||||
gfx_update_texture_with_pixels(texture, mc->sws_buf);
|
||||
mc->has_pending_video_frame = false;
|
||||
|
||||
if (sprite) {
|
||||
sprite_dirty(sprite);
|
||||
} else {
|
||||
// Draw directly to the main framebuffer.
|
||||
gfx_clear();
|
||||
gfx_render_texture(&mc->temp_surface, NULL);
|
||||
gfx_swap();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool movie_is_end(struct movie_context *mc)
|
||||
{
|
||||
return mc->video.finished && mc->audio.finished;
|
||||
}
|
||||
|
||||
int movie_get_position(struct movie_context *mc)
|
||||
{
|
||||
SDL_LockMutex(mc->timer_mutex);
|
||||
int ms = mc->wall_time_ms ? mc->stream_time * 1000 + SDL_GetTicks() - mc->wall_time_ms : 0;
|
||||
SDL_UnlockMutex(mc->timer_mutex);
|
||||
return ms;
|
||||
}
|
||||
|
||||
bool movie_set_volume(struct movie_context *mc, int volume)
|
||||
{
|
||||
mc->volume = volume;
|
||||
if (mc->voice >= 0)
|
||||
mixer_stream_set_volume(mc->voice, volume);
|
||||
return true;
|
||||
}
|
||||
@@ -873,6 +873,15 @@ bool PE_SetPartsCG_by_index(int parts_no, int cg_no, possibly_unused int sprite_
|
||||
return parts_set_cg_by_index(parts_get(parts_no), cg_no, state);
|
||||
}
|
||||
|
||||
// XXX: Rance Quest
|
||||
bool PE_SetPartsCG_by_string_index(int parts_no, struct string *cg_no,
|
||||
possibly_unused int sprite_deform, int state)
|
||||
{
|
||||
if (!parts_state_valid(--state))
|
||||
return false;
|
||||
return parts_set_cg_by_index(parts_get(parts_no), atoi(cg_no->text), state);
|
||||
}
|
||||
|
||||
void PE_GetPartsCGName(int parts_no, struct string **cg_name, int state)
|
||||
{
|
||||
if (!parts_state_valid(--state))
|
||||
|
||||
+3
-3
@@ -31,7 +31,7 @@ static struct sprite goat_sprite;
|
||||
|
||||
static struct {
|
||||
struct shader shader;
|
||||
GLint alpha_mod;
|
||||
GLint blend_rate;
|
||||
GLint bot_left;
|
||||
GLint top_right;
|
||||
GLint add_color;
|
||||
@@ -84,7 +84,7 @@ static void parts_render_cg(struct parts *parts, struct parts_common *common)
|
||||
}
|
||||
|
||||
gfx_prepare_job(&job);
|
||||
glUniform1f(parts_shader.alpha_mod, parts->global.alpha / 255.0);
|
||||
glUniform1f(parts_shader.blend_rate, parts->global.alpha / 255.0);
|
||||
glUniform2f(parts_shader.bot_left, r.x, r.y);
|
||||
glUniform2f(parts_shader.top_right, r.x + r.w, r.y + r.h);
|
||||
glUniform3f(parts_shader.add_color, parts->global.add_color.r / 255.0f,
|
||||
@@ -162,7 +162,7 @@ void parts_render_init(void)
|
||||
scene_register_sprite(&goat_sprite);
|
||||
|
||||
gfx_load_shader(&parts_shader.shader, "shaders/render.v.glsl", "shaders/parts.f.glsl");
|
||||
parts_shader.alpha_mod = glGetUniformLocation(parts_shader.shader.program, "alpha_mod");
|
||||
parts_shader.blend_rate = glGetUniformLocation(parts_shader.shader.program, "blend_rate");
|
||||
parts_shader.bot_left = glGetUniformLocation(parts_shader.shader.program, "bot_left");
|
||||
parts_shader.top_right = glGetUniformLocation(parts_shader.shader.program, "top_right");
|
||||
parts_shader.add_color = glGetUniformLocation(parts_shader.shader.program, "add_color");
|
||||
|
||||
+298
-65
@@ -14,6 +14,7 @@
|
||||
* along with this program; if not, see <http://gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
@@ -23,6 +24,7 @@
|
||||
#include "system4.h"
|
||||
#include "system4/ain.h"
|
||||
#include "system4/file.h"
|
||||
#include "system4/savefile.h"
|
||||
#include "system4/string.h"
|
||||
|
||||
#include "savedata.h"
|
||||
@@ -77,27 +79,6 @@ cJSON *vm_value_to_json(enum ain_data_type type, union vm_value val)
|
||||
}
|
||||
}
|
||||
|
||||
struct global_save_data {
|
||||
cJSON *save;
|
||||
cJSON *globals;
|
||||
};
|
||||
|
||||
static void init_global_save_data(struct global_save_data *data, const char *keyname)
|
||||
{
|
||||
data->save = cJSON_CreateObject();
|
||||
data->globals = cJSON_CreateArray();
|
||||
cJSON_AddStringToObject(data->save, "key", keyname);
|
||||
cJSON_AddItemToObject(data->save, "globals", data->globals);
|
||||
}
|
||||
|
||||
static void add_global(struct global_save_data *data, int global, union vm_value val)
|
||||
{
|
||||
cJSON *obj = cJSON_CreateObject();
|
||||
cJSON_AddNumberToObject(obj, "index", global);
|
||||
cJSON_AddItemToObject(obj, "value", vm_value_to_json(ain->globals[global].type.data, val));
|
||||
cJSON_AddItemToArray(data->globals, obj);
|
||||
}
|
||||
|
||||
int save_json(const char *filename, cJSON *json)
|
||||
{
|
||||
char *path = savedir_path(filename);
|
||||
@@ -126,25 +107,6 @@ int save_json(const char *filename, cJSON *json)
|
||||
|
||||
}
|
||||
|
||||
static int write_save_data(struct global_save_data *data, const char *filename)
|
||||
{
|
||||
int r = save_json(filename, data->save);
|
||||
cJSON_Delete(data->save);
|
||||
return r;
|
||||
}
|
||||
|
||||
int save_globals(const char *keyname, const char *filename)
|
||||
{
|
||||
struct global_save_data data;
|
||||
init_global_save_data(&data, keyname);
|
||||
|
||||
for (int i = 0; i < ain->nr_globals; i++) {
|
||||
add_global(&data, i, global_get(i));
|
||||
}
|
||||
|
||||
return write_save_data(&data, filename);
|
||||
}
|
||||
|
||||
static int get_group_index(const char *name)
|
||||
{
|
||||
for (int i = 0; i < ain->nr_global_groups; i++) {
|
||||
@@ -154,27 +116,145 @@ static int get_group_index(const char *name)
|
||||
return -1;
|
||||
}
|
||||
|
||||
int save_group(const char *keyname, const char *filename, const char *group_name, int *n)
|
||||
{
|
||||
int group;
|
||||
static int32_t add_value_to_gsave(enum ain_data_type type, union vm_value val, struct gsave *save);
|
||||
|
||||
if ((group = get_group_index(group_name)) < 0) {
|
||||
WARNING("Unregistered global group: %s", display_sjis0(group_name));
|
||||
static struct gsave_flat_array *collect_flat_arrays(struct page *page, struct gsave_flat_array *fa, struct gsave *save)
|
||||
{
|
||||
if (page->array.rank > 1) {
|
||||
for (int i = 0; i < page->nr_vars; i++)
|
||||
fa = collect_flat_arrays(heap_get_page(page->values[i].i), fa, save);
|
||||
return fa;
|
||||
}
|
||||
fa->nr_values = page->nr_vars;
|
||||
fa->values = xcalloc(fa->nr_values, sizeof(struct gsave_array_value));
|
||||
for (int i = 0; i < page->nr_vars; i++) {
|
||||
enum ain_data_type type = variable_type(page, i, NULL, NULL);
|
||||
fa->values[i].type = type;
|
||||
fa->values[i].value = add_value_to_gsave(type, page->values[i], save);
|
||||
}
|
||||
return ++fa;
|
||||
}
|
||||
|
||||
static int32_t add_value_to_gsave(enum ain_data_type type, union vm_value val, struct gsave *save)
|
||||
{
|
||||
switch (type) {
|
||||
case AIN_VOID:
|
||||
case AIN_INT:
|
||||
case AIN_BOOL:
|
||||
case AIN_FUNC_TYPE:
|
||||
case AIN_DELEGATE:
|
||||
case AIN_LONG_INT:
|
||||
case AIN_FLOAT:
|
||||
return val.i;
|
||||
case AIN_STRING:
|
||||
return gsave_add_string(save, heap[val.i].s);
|
||||
case AIN_STRUCT:
|
||||
{
|
||||
struct page *page = heap_get_page(val.i);
|
||||
assert(page->type == STRUCT_PAGE);
|
||||
struct ain_struct *st = &ain->structures[page->index];
|
||||
assert(st->nr_members == page->nr_vars);
|
||||
struct gsave_record rec = {
|
||||
.type = GSAVE_RECORD_STRUCT,
|
||||
.struct_name = strdup(st->name),
|
||||
.nr_indices = st->nr_members,
|
||||
.indices = xcalloc(st->nr_members, sizeof(int32_t)),
|
||||
};
|
||||
for (int i = 0; i < page->nr_vars; i++) {
|
||||
enum ain_data_type type = st->members[i].type.data;
|
||||
int32_t value = add_value_to_gsave(type, page->values[i], save);
|
||||
struct gsave_keyval kv = {
|
||||
.name = strdup(st->members[i].name),
|
||||
.type = type,
|
||||
.value = value,
|
||||
};
|
||||
rec.indices[i] = gsave_add_keyval(save, &kv);
|
||||
}
|
||||
return gsave_add_record(save, &rec);
|
||||
}
|
||||
case AIN_ARRAY_TYPE:
|
||||
{
|
||||
struct page *page = heap_get_page(val.i);
|
||||
if (!page) {
|
||||
struct gsave_array array = { .rank = -1 };
|
||||
return gsave_add_array(save, &array);
|
||||
}
|
||||
assert(page->type == ARRAY_PAGE);
|
||||
struct gsave_array array = {
|
||||
.rank = page->array.rank,
|
||||
.dimensions = xcalloc(page->array.rank, sizeof(int32_t)),
|
||||
.nr_flat_arrays = 1
|
||||
};
|
||||
for (struct page *p = page;; p = heap_get_page(page->values[0].i)) {
|
||||
array.dimensions[p->array.rank - 1] = p->nr_vars;
|
||||
if (p->array.rank == 1)
|
||||
break;
|
||||
array.nr_flat_arrays *= p->nr_vars;
|
||||
}
|
||||
array.flat_arrays = xcalloc(array.nr_flat_arrays, sizeof(struct gsave_flat_array));
|
||||
collect_flat_arrays(page, array.flat_arrays, save);
|
||||
return gsave_add_array(save, &array);
|
||||
}
|
||||
case AIN_REF_TYPE:
|
||||
return -1;
|
||||
default:
|
||||
WARNING("Unhandled type: %s", ain_strtype(ain, type, -1));
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
int save_globals(const char *keyname, const char *filename, const char *group_name, int *n_out)
|
||||
{
|
||||
int group = -1;
|
||||
int nr_vars;
|
||||
if (group_name) {
|
||||
if ((group = get_group_index(group_name)) < 0) {
|
||||
WARNING("Unregistered global group: %s", display_sjis0(group_name));
|
||||
return 0;
|
||||
}
|
||||
nr_vars = 0;
|
||||
for (int i = 0; i < ain->nr_globals; i++) {
|
||||
if (ain->globals[i].group_index == group)
|
||||
nr_vars++;
|
||||
}
|
||||
} else {
|
||||
nr_vars = ain->nr_globals;
|
||||
}
|
||||
|
||||
int gsave_version = AIN_VERSION_GTE(ain, 5, 0) ? 5 : 4;
|
||||
struct gsave *save = gsave_create(gsave_version, keyname, ain->nr_globals, group_name);
|
||||
gsave_add_globals_record(save, nr_vars);
|
||||
|
||||
struct gsave_global *global = save->globals;
|
||||
for (int i = 0; i < ain->nr_globals; i++) {
|
||||
if (group >= 0 && ain->globals[i].group_index != group)
|
||||
continue;
|
||||
global->name = strdup(ain->globals[i].name);
|
||||
global->type = ain->globals[i].type.data;
|
||||
global->value = add_value_to_gsave(global->type, global_get(i), save);
|
||||
global++;
|
||||
}
|
||||
|
||||
char *path = savedir_path(filename);
|
||||
FILE *fp = file_open_utf8(path, "wb");
|
||||
if (!fp) {
|
||||
WARNING("Failed to open save file %s: %s", display_utf0(path), strerror(errno));
|
||||
free(path);
|
||||
gsave_free(save);
|
||||
return 0;
|
||||
}
|
||||
free(path);
|
||||
|
||||
struct global_save_data data;
|
||||
init_global_save_data(&data, keyname);
|
||||
|
||||
*n = 0;
|
||||
for (int i = 0; i < ain->nr_globals; i++) {
|
||||
if (ain->globals[i].group_index != group)
|
||||
continue;
|
||||
add_global(&data, i, global_get(i));
|
||||
(*n)++;
|
||||
}
|
||||
|
||||
return write_save_data(&data, filename);
|
||||
bool encrypt = !AIN_VERSION_GTE(ain, 6, 0);
|
||||
int compression_level = AIN_VERSION_GTE(ain, 6, 0) ? 1 : 9;
|
||||
enum savefile_error error = gsave_write(save, fp, encrypt, compression_level);
|
||||
if (error != SAVEFILE_SUCCESS)
|
||||
WARNING("Failed to write save file: %s", savefile_strerror(error));
|
||||
fclose(fp);
|
||||
gsave_free(save);
|
||||
if (n_out)
|
||||
*n_out = nr_vars;
|
||||
return error == SAVEFILE_SUCCESS;
|
||||
}
|
||||
|
||||
union vm_value json_to_vm_value(enum ain_data_type type, enum ain_data_type struct_type, int array_rank, cJSON *json);
|
||||
@@ -273,19 +353,16 @@ union vm_value json_to_vm_value(enum ain_data_type type, enum ain_data_type stru
|
||||
}
|
||||
}
|
||||
|
||||
static cJSON *read_save_file(const char *filename)
|
||||
static cJSON *read_save_file(const char *path)
|
||||
{
|
||||
FILE *f;
|
||||
long len;
|
||||
char *buf;
|
||||
char *path = savedir_path(filename);
|
||||
|
||||
if (!(f = file_open_utf8(path, "rb"))) {
|
||||
WARNING("Failed to open save file: %s: %s", display_utf0(filename), strerror(errno));
|
||||
free(path);
|
||||
WARNING("Failed to open save file: %s: %s", display_utf0(path), strerror(errno));
|
||||
return NULL;
|
||||
}
|
||||
free(path);
|
||||
|
||||
fseek(f, 0, SEEK_END);
|
||||
len = ftell(f);
|
||||
@@ -294,7 +371,7 @@ static cJSON *read_save_file(const char *filename)
|
||||
buf = xmalloc(len+1);
|
||||
buf[len] = '\0';
|
||||
if (fread(buf, len, 1, f) != 1) {
|
||||
WARNING("Failed to read save file: %s", display_utf0(filename));
|
||||
WARNING("Failed to read save file: %s", display_utf0(path));
|
||||
free(buf);
|
||||
return 0;
|
||||
}
|
||||
@@ -305,10 +382,10 @@ static cJSON *read_save_file(const char *filename)
|
||||
return r;
|
||||
}
|
||||
|
||||
int load_globals(const char *keyname, const char *filename, const char *group_name, int *n)
|
||||
static int load_globals_from_json(const char *path, const char *keyname, const char *group_name, int *n)
|
||||
{
|
||||
int retval = 0;
|
||||
cJSON *save = read_save_file(filename);
|
||||
cJSON *save = read_save_file(path);
|
||||
if (!save)
|
||||
return 0;
|
||||
if (!cJSON_IsObject(save)) {
|
||||
@@ -365,6 +442,162 @@ cleanup:
|
||||
return retval;
|
||||
}
|
||||
|
||||
static int struct_member_index(struct ain_struct *st, const char *name)
|
||||
{
|
||||
for (int i = 0; i < st->nr_members; i++) {
|
||||
if (!strcmp(st->members[i].name, name))
|
||||
return i;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
static union vm_value gsave_to_vm_value(struct gsave *save, enum ain_data_type type, int struct_type, int array_rank, int32_t value);
|
||||
|
||||
static struct gsave_flat_array *gsave_load_array(struct gsave *save, struct page *page, struct gsave_flat_array *flat_array)
|
||||
{
|
||||
assert(page->type == ARRAY_PAGE);
|
||||
if (page->array.rank > 1) {
|
||||
for (int i = 0; i < page->nr_vars; i++)
|
||||
flat_array = gsave_load_array(save, heap_get_page(page->values[i].i), flat_array);
|
||||
return flat_array;
|
||||
}
|
||||
|
||||
if (page->nr_vars != flat_array->nr_values)
|
||||
VM_ERROR("Bad save file: unexpected number of array elements");
|
||||
for (int i = 0; i < page->nr_vars; i++) {
|
||||
int struct_type, array_rank;
|
||||
enum ain_data_type data_type = variable_type(page, i, &struct_type, &array_rank);
|
||||
if (data_type != flat_array->values[i].type)
|
||||
VM_ERROR("Bad save file: unexpected array element type");
|
||||
union vm_value val = gsave_to_vm_value(save, data_type, struct_type, array_rank, flat_array->values[i].value);
|
||||
page->values[i] = val;
|
||||
}
|
||||
return flat_array + 1;
|
||||
}
|
||||
|
||||
static union vm_value gsave_to_vm_value(struct gsave *save, enum ain_data_type type, int struct_type, int array_rank, int32_t value)
|
||||
{
|
||||
switch (type) {
|
||||
case AIN_VOID:
|
||||
case AIN_INT:
|
||||
case AIN_BOOL:
|
||||
case AIN_LONG_INT:
|
||||
case AIN_FLOAT:
|
||||
return vm_int(value);
|
||||
case AIN_STRING:
|
||||
{
|
||||
int slot = heap_alloc_slot(VM_STRING);
|
||||
heap[slot].s = string_ref(save->strings[value]);
|
||||
return vm_int(slot);
|
||||
}
|
||||
case AIN_STRUCT:
|
||||
{
|
||||
struct gsave_record *rec = &save->records[value];
|
||||
struct ain_struct *st = &ain->structures[struct_type];
|
||||
if (strcmp(rec->struct_name, st->name))
|
||||
VM_ERROR("Bad save file: structure name mismatch");
|
||||
int slot = alloc_struct(struct_type);
|
||||
struct page *page = heap[slot].page;
|
||||
for (int i = 0; i < rec->nr_indices; i++) {
|
||||
struct gsave_keyval *kv = &save->keyvals[rec->indices[i]];
|
||||
int index = struct_member_index(st, kv->name);
|
||||
if (index < 0) {
|
||||
WARNING("structure %s has no member named %s", display_sjis0(rec->struct_name), display_sjis1(kv->name));
|
||||
continue;
|
||||
}
|
||||
int struct_type, array_rank;
|
||||
enum ain_data_type data_type = variable_type(page, index, &struct_type, &array_rank);
|
||||
if (data_type != kv->type)
|
||||
VM_ERROR("Bad save file: structure member type mismatch");
|
||||
union vm_value val = gsave_to_vm_value(save, data_type, struct_type, array_rank, kv->value);
|
||||
page->values[index] = val;
|
||||
}
|
||||
return vm_int(slot);
|
||||
}
|
||||
case AIN_ARRAY_TYPE:
|
||||
{
|
||||
struct gsave_array *array = &save->arrays[value];
|
||||
int slot = heap_alloc_slot(VM_PAGE);
|
||||
if (array->rank == -1) {
|
||||
heap[slot].page = NULL;
|
||||
return vm_int(slot);
|
||||
}
|
||||
if (array_rank != array->rank)
|
||||
VM_ERROR("Bad save file: array rank mismatch");
|
||||
union vm_value *dims = xmalloc(sizeof(union vm_value) * array_rank);
|
||||
for (int i = 0; i < array_rank; i++)
|
||||
dims[i].i = array->dimensions[array_rank - 1 - i];
|
||||
struct page *page = alloc_array(array_rank, dims, type, struct_type, false);
|
||||
heap[slot].page = page;
|
||||
free(dims);
|
||||
gsave_load_array(save, page, array->flat_arrays);
|
||||
return vm_int(slot);
|
||||
}
|
||||
case AIN_REF_TYPE:
|
||||
return vm_int(-1);
|
||||
default:
|
||||
WARNING("Unhandled data type: %s", ain_strtype(ain, type, -1));
|
||||
return vm_int(-1);
|
||||
}
|
||||
}
|
||||
|
||||
int load_globals_from_gsave(struct gsave *save, const char *keyname, const char *group_name, int *n)
|
||||
{
|
||||
if (strcmp(keyname, save->key))
|
||||
VM_ERROR("Attempted to load save data with wrong key: %s", display_sjis0(keyname));
|
||||
if (!group_name)
|
||||
group_name = "";
|
||||
if (!save->group)
|
||||
save->group = strdup("");
|
||||
if (strcmp(group_name, save->group))
|
||||
VM_ERROR("Attempted to load save data with wrong group name: '%s'", display_sjis0(group_name));
|
||||
|
||||
for (struct gsave_global *g = save->globals; g < save->globals + save->nr_globals; g++) {
|
||||
int global_index = ain_get_global(ain, g->name);
|
||||
if (global_index < 0) {
|
||||
WARNING("invalid global name %s", display_sjis0(g->name));
|
||||
return 0;
|
||||
}
|
||||
struct ain_type *type = &ain->globals[global_index].type;
|
||||
if (type->data != g->type) {
|
||||
WARNING("%s: type mismatch", display_sjis0(g->name));
|
||||
return 0;
|
||||
}
|
||||
bool call_dtors = false; // Destructors for old objects should not be called.
|
||||
global_set(global_index, gsave_to_vm_value(save, type->data, type->struc, type->rank, g->value), call_dtors);
|
||||
if (n)
|
||||
(*n)++;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
int load_globals(const char *keyname, const char *filename, const char *group_name, int *n)
|
||||
{
|
||||
char *path = savedir_path(filename);
|
||||
int retval;
|
||||
|
||||
// First, try reading as a gsave.
|
||||
enum savefile_error error;
|
||||
struct gsave *save = gsave_read(path, &error);
|
||||
switch (error) {
|
||||
case SAVEFILE_SUCCESS:
|
||||
free(path);
|
||||
retval = load_globals_from_gsave(save, keyname, group_name, n);
|
||||
gsave_free(save);
|
||||
return retval;
|
||||
case SAVEFILE_INVALID_SIGNATURE:
|
||||
// If not a System4 save file, try reading as a json.
|
||||
retval = load_globals_from_json(path, keyname, group_name, n);
|
||||
free(path);
|
||||
return retval;
|
||||
default:
|
||||
WARNING("%s: %s", display_utf0(path), savefile_strerror(error));
|
||||
free(path);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
int delete_save_file(const char *filename)
|
||||
{
|
||||
char *path = savedir_path(filename);
|
||||
|
||||
+142
-31
@@ -40,6 +40,14 @@ bool sact_dirty = true;
|
||||
LIST_HEAD(listhead, sact_sprite) sprites_with_plugins =
|
||||
LIST_HEAD_INITIALIZER(sprites_with_plugins);
|
||||
|
||||
// XXX: sprites should typically be set to zero and then initialized with this
|
||||
// function upon allocation.
|
||||
void sprite_init(struct sact_sprite *sp)
|
||||
{
|
||||
sp->blend_rate = 255;
|
||||
sp->multiply_color = (SDL_Color){255,255,255,255};
|
||||
}
|
||||
|
||||
void sprite_free(struct sact_sprite *sp)
|
||||
{
|
||||
scene_unregister_sprite(&sp->sp);
|
||||
@@ -47,7 +55,9 @@ void sprite_free(struct sact_sprite *sp)
|
||||
gfx_delete_texture(&sp->text.texture);
|
||||
if (sp->plugin)
|
||||
LIST_REMOVE(sp, entry);
|
||||
// restore to initial state
|
||||
memset(sp, 0, sizeof(struct sact_sprite));
|
||||
sprite_init(sp);
|
||||
}
|
||||
|
||||
static void sprite_init_texture(struct sact_sprite *sp)
|
||||
@@ -60,14 +70,103 @@ static void sprite_init_texture(struct sact_sprite *sp)
|
||||
}
|
||||
}
|
||||
|
||||
static struct sprite_shader {
|
||||
struct shader s;
|
||||
GLuint blend_rate;
|
||||
GLuint multiply_color;
|
||||
GLuint add_color;
|
||||
GLuint bot_left;
|
||||
GLuint top_right;
|
||||
} sprite_shader = {0};
|
||||
|
||||
static void prepare_sact_shader(struct gfx_render_job *job, void *data)
|
||||
{
|
||||
struct sprite_shader *s = (struct sprite_shader*)job->shader;
|
||||
struct sact_sprite *sp = (struct sact_sprite*)data;
|
||||
glUniform1f(s->blend_rate, sp->blend_rate / 255.0f);
|
||||
}
|
||||
|
||||
static void prepare_chipmunk_shader(struct gfx_render_job *job, void *data)
|
||||
{
|
||||
struct sprite_shader *s = (struct sprite_shader*)job->shader;
|
||||
struct sact_sprite *sp = (struct sact_sprite*)data;
|
||||
glUniform1f(s->blend_rate, sp->blend_rate / 255.0f);
|
||||
glUniform3f(s->multiply_color,
|
||||
sp->multiply_color.r / 255.0f,
|
||||
sp->multiply_color.g / 255.0f,
|
||||
sp->multiply_color.b / 255.0f);
|
||||
glUniform3f(s->add_color,
|
||||
sp->add_color.r / 255.0f,
|
||||
sp->add_color.g / 255.0f,
|
||||
sp->add_color.b / 255.0f);
|
||||
|
||||
Rectangle r = sp->surface_area;
|
||||
if (!r.w && !r.h) {
|
||||
r = (Rectangle) { 0, 0, sp->rect.w, sp->rect.h };
|
||||
}
|
||||
|
||||
glUniform2f(s->bot_left, r.x, r.y);
|
||||
glUniform2f(s->top_right, r.x + r.w, r.y + r.h);
|
||||
}
|
||||
|
||||
void sprite_init_sact(void)
|
||||
{
|
||||
if (sprite_shader.s.prepare) {
|
||||
if (sprite_shader.s.prepare != prepare_sact_shader) {
|
||||
WARNING("mixed SACT2/ChipmunkSpriteEngine initialization");
|
||||
return;
|
||||
}
|
||||
}
|
||||
gfx_load_shader(&sprite_shader.s, "shaders/render.v.glsl", "shaders/sprite.f.glsl");
|
||||
sprite_shader.blend_rate = glGetUniformLocation(sprite_shader.s.program, "blend_rate");
|
||||
sprite_shader.s.prepare = prepare_sact_shader;
|
||||
|
||||
}
|
||||
|
||||
void sprite_init_chipmunk(void)
|
||||
{
|
||||
if (sprite_shader.s.prepare) {
|
||||
if (sprite_shader.s.prepare != prepare_chipmunk_shader) {
|
||||
WARNING("mixed SACT2/ChipmunkSpriteEngine initialization");
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
}
|
||||
gfx_load_shader(&sprite_shader.s, "shaders/render.v.glsl", "shaders/parts.f.glsl");
|
||||
sprite_shader.blend_rate = glGetUniformLocation(sprite_shader.s.program, "blend_rate");
|
||||
sprite_shader.multiply_color = glGetUniformLocation(sprite_shader.s.program, "multiply_color");
|
||||
sprite_shader.add_color = glGetUniformLocation(sprite_shader.s.program, "add_color");
|
||||
sprite_shader.bot_left = glGetUniformLocation(sprite_shader.s.program, "bot_left");
|
||||
sprite_shader.top_right = glGetUniformLocation(sprite_shader.s.program, "top_right");
|
||||
sprite_shader.s.prepare = prepare_chipmunk_shader;
|
||||
}
|
||||
|
||||
static void sprite_render(struct sprite *_sp)
|
||||
{
|
||||
struct sact_sprite *sp = (struct sact_sprite*)_sp;
|
||||
sprite_init_texture(sp);
|
||||
gfx_render_texture(&sp->texture, &sp->rect);
|
||||
if (sp->text.texture.handle) {
|
||||
gfx_render_texture(&sp->text.texture, &sp->rect);
|
||||
|
||||
switch (sp->draw_method) {
|
||||
case DRAW_METHOD_SCREEN:
|
||||
glBlendFuncSeparate(GL_ONE, GL_ONE_MINUS_SRC_COLOR, GL_ZERO, GL_ONE);
|
||||
break;
|
||||
case DRAW_METHOD_MULTIPLY:
|
||||
glBlendFuncSeparate(GL_DST_COLOR, GL_ZERO, GL_ZERO, GL_ONE);
|
||||
break;
|
||||
case DRAW_METHOD_ADDITIVE:
|
||||
glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE, GL_ZERO, GL_ONE);
|
||||
break;
|
||||
case DRAW_METHOD_NORMAL:
|
||||
break;
|
||||
}
|
||||
|
||||
_gfx_render_texture(&sprite_shader.s, &sp->texture, &sp->rect, sp);
|
||||
if (sp->text.texture.handle) {
|
||||
_gfx_render_texture(&sprite_shader.s, &sp->text.texture, &sp->rect, sp);
|
||||
}
|
||||
|
||||
if (sp->draw_method != DRAW_METHOD_NORMAL)
|
||||
glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ZERO);
|
||||
}
|
||||
|
||||
struct texture *sprite_get_texture(struct sact_sprite *sp)
|
||||
@@ -76,6 +175,11 @@ struct texture *sprite_get_texture(struct sact_sprite *sp)
|
||||
return &sp->texture;
|
||||
}
|
||||
|
||||
static void _sprite_print(struct sprite *sp)
|
||||
{
|
||||
sprite_print((struct sact_sprite*)sp);
|
||||
}
|
||||
|
||||
void sprite_set_cg(struct sact_sprite *sp, struct cg *cg)
|
||||
{
|
||||
gfx_delete_texture(&sp->texture);
|
||||
@@ -85,6 +189,7 @@ void sprite_set_cg(struct sact_sprite *sp, struct cg *cg)
|
||||
sp->sp.has_pixel = true;
|
||||
sp->sp.has_alpha = cg->metrics.has_alpha;
|
||||
sp->sp.render = sprite_render;
|
||||
sp->sp.debug_print = _sprite_print;
|
||||
sprite_dirty(sp);
|
||||
}
|
||||
|
||||
@@ -113,6 +218,7 @@ void sprite_set_cg_2x(struct sact_sprite *sp, struct cg *cg)
|
||||
sp->sp.has_pixel = true;
|
||||
sp->sp.has_alpha = cg->metrics.has_alpha;
|
||||
sp->sp.render = sprite_render;
|
||||
sp->sp.debug_print = _sprite_print;
|
||||
sprite_dirty(sp);
|
||||
gfx_delete_texture(&tmp);
|
||||
}
|
||||
@@ -164,15 +270,10 @@ int sprite_save_cg(struct sact_sprite *sp, const char *path)
|
||||
return gfx_save_texture(sprite_get_texture(sp), path, ALCG_QNT);
|
||||
}
|
||||
|
||||
static void _sprite_print(struct sprite *sp)
|
||||
{
|
||||
sprite_print((struct sact_sprite*)sp);
|
||||
}
|
||||
|
||||
/*
|
||||
* Attach pixel data to a sprite. The texture is initialized lazily.
|
||||
*/
|
||||
void sprite_init(struct sact_sprite *sp, int w, int h, int r, int g, int b, int a)
|
||||
void sprite_init_color(struct sact_sprite *sp, int w, int h, int r, int g, int b, int a)
|
||||
{
|
||||
sp->color = (SDL_Color) { .r = r, .g = g, .b = b, .a = (a >= 0 ? a : 255) };
|
||||
sp->rect.w = w;
|
||||
@@ -205,30 +306,41 @@ void sprite_set_y(struct sact_sprite *sp, int y)
|
||||
sprite_dirty(sp);
|
||||
}
|
||||
|
||||
int sprite_get_blend_rate(struct sact_sprite *sp)
|
||||
{
|
||||
return sprite_get_texture(sp)->alpha_mod;
|
||||
}
|
||||
|
||||
void sprite_set_blend_rate(struct sact_sprite *sp, int rate)
|
||||
{
|
||||
rate = max(0, min(255, rate));
|
||||
sprite_get_texture(sp)->alpha_mod = rate;
|
||||
sp->text.texture.alpha_mod = rate;
|
||||
sp->blend_rate = max(0, min(255, rate));
|
||||
sprite_dirty(sp);
|
||||
}
|
||||
|
||||
void sprite_set_multiply_color(struct sact_sprite *sp, int r, int g, int b)
|
||||
{
|
||||
sp->multiply_color.r = max(0, min(255, r));
|
||||
sp->multiply_color.g = max(0, min(255, g));
|
||||
sp->multiply_color.b = max(0, min(255, b));
|
||||
}
|
||||
|
||||
void sprite_set_add_color(struct sact_sprite *sp, int r, int g, int b)
|
||||
{
|
||||
sp->add_color.r = max(0, min(255, r));
|
||||
sp->add_color.g = max(0, min(255, g));
|
||||
sp->add_color.b = max(0, min(255, b));
|
||||
}
|
||||
|
||||
void sprite_set_surface_area(struct sact_sprite *sp, int x, int y, int w, int h)
|
||||
{
|
||||
sp->surface_area = (Rectangle) { x, y, w, h };
|
||||
}
|
||||
|
||||
int sprite_set_draw_method(struct sact_sprite *sp, enum draw_method method)
|
||||
{
|
||||
sprite_get_texture(sp)->draw_method = method;
|
||||
sp->text.texture.draw_method = method;
|
||||
sp->draw_method = method;
|
||||
sprite_dirty(sp);
|
||||
return 1;
|
||||
}
|
||||
|
||||
enum draw_method sprite_get_draw_method(struct sact_sprite *sp)
|
||||
{
|
||||
return sprite_get_texture(sp)->draw_method;
|
||||
return sp->draw_method;
|
||||
}
|
||||
|
||||
void sprite_set_text_home(struct sact_sprite *sp, int x, int y)
|
||||
@@ -265,9 +377,6 @@ void sprite_text_draw(struct sact_sprite *sp, struct string *text, struct text_s
|
||||
c = ts->color;
|
||||
c.a = 0;
|
||||
gfx_init_texture_rgba(&sp->text.texture, sp->rect.w, sp->rect.h, c);
|
||||
Texture *sp_t = sprite_get_texture(sp);
|
||||
sp->text.texture.alpha_mod = sp_t->alpha_mod;
|
||||
sp->text.texture.draw_method = sp_t->draw_method;
|
||||
}
|
||||
|
||||
ts->font_spacing = sp->text.char_space;
|
||||
@@ -378,14 +487,6 @@ void gfx_print_texture(struct texture *t, int indent)
|
||||
indent_printf(indent+1, "initialized = %s,\n", t->handle ? "true" : "false");
|
||||
indent_printf(indent+1, "size = (%d,%d),\n", t->w, t->h);
|
||||
indent_printf(indent+1, "has_alpha = %s,\n", t->has_alpha ? "true" : "false");
|
||||
indent_printf(indent+1, "alpha_mod = %d,\n", t->alpha_mod);
|
||||
switch (t->draw_method) {
|
||||
case DRAW_METHOD_NORMAL: indent_printf(indent+1, "draw_method = normal\n"); break;
|
||||
case DRAW_METHOD_SCREEN: indent_printf(indent+1, "draw_method = screen\n"); break;
|
||||
case DRAW_METHOD_MULTIPLY: indent_printf(indent+1, "draw_method = multiply\n"); break;
|
||||
case DRAW_METHOD_ADDITIVE: indent_printf(indent+1, "draw_method = additive\n"); break;
|
||||
default: indent_printf(indent+1, "draw_method = unknown\n"); break;
|
||||
}
|
||||
indent_printf(indent, "}");
|
||||
}
|
||||
|
||||
@@ -407,6 +508,16 @@ void sprite_print(struct sact_sprite *sp)
|
||||
|
||||
indent_printf(indent, "texture = "); gfx_print_texture(&sp->texture, 1); printf(",\n");
|
||||
indent_printf(indent, "color = "); gfx_print_color(&sp->color); printf(",\n");
|
||||
indent_printf(indent, "blend_rate = %d,\n", sp->blend_rate);
|
||||
indent_printf(indent, "multiply_color = "); gfx_print_color(&sp->multiply_color); printf(",\n");
|
||||
indent_printf(indent, "add_color = "); gfx_print_color(&sp->add_color); printf(",\n");
|
||||
switch (sp->draw_method) {
|
||||
case DRAW_METHOD_NORMAL: indent_printf(indent, "draw_method = normal,\n"); break;
|
||||
case DRAW_METHOD_SCREEN: indent_printf(indent, "draw_method = screen,\n"); break;
|
||||
case DRAW_METHOD_MULTIPLY: indent_printf(indent, "draw_method = multiply,\n"); break;
|
||||
case DRAW_METHOD_ADDITIVE: indent_printf(indent, "draw_method = additive,\n"); break;
|
||||
default: indent_printf(indent, "draw_method = unknown,\n"); break;
|
||||
}
|
||||
indent_printf(indent, "rect = "); gfx_print_rectangle(&sp->rect); printf(",\n");
|
||||
|
||||
indent_printf(indent, "text = {\n");
|
||||
|
||||
+6
-12
@@ -18,7 +18,6 @@
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <ctype.h>
|
||||
#include <libgen.h>
|
||||
#include <dirent.h>
|
||||
#include <ctype.h>
|
||||
#include <getopt.h>
|
||||
@@ -100,10 +99,9 @@ static void read_mixer_channels(struct ini_entry *entry)
|
||||
config.mixer_volumes = xcalloc(entry->value.list_size, sizeof(int));
|
||||
for (size_t i = 0; i < entry->value.list_size; i++) {
|
||||
if (entry->value.list[i].type == INI_NULL) {
|
||||
WARNING("No name given for VolumeValancer[%d]", i);
|
||||
continue;
|
||||
}
|
||||
if (entry->value.list[i].type == INI_STRING) {
|
||||
config.mixer_channels[i] = strdup("Unnamed");
|
||||
config.mixer_volumes[i] = 0;
|
||||
} else if (entry->value.list[i].type == INI_STRING) {
|
||||
config.mixer_channels[i] = strdup(entry->value.list[i].s->text);
|
||||
// XXX: assumes default_volume was already initialized
|
||||
config.mixer_volumes[i] = config.default_volume;
|
||||
@@ -266,7 +264,7 @@ static bool config_init_with_ini(const char *ini_path)
|
||||
if (!config.ain_filename)
|
||||
ERROR("No AIN filename specified in %s", ini_path);
|
||||
char *tmp = strdup(ini_path);
|
||||
config.game_dir = strdup(dirname(tmp));
|
||||
config.game_dir = strdup(path_dirname(tmp));
|
||||
free(tmp);
|
||||
config_init();
|
||||
return true;
|
||||
@@ -286,12 +284,8 @@ static bool config_init_with_dir(const char *dir)
|
||||
|
||||
static void config_init_with_ain(const char *ain_path)
|
||||
{
|
||||
char *basename_tmp = strdup(ain_path);
|
||||
char *dirname_tmp = strdup(ain_path);
|
||||
config.ain_filename = strdup(basename(basename_tmp));
|
||||
config.game_dir = strdup(dirname(dirname_tmp));
|
||||
free(basename_tmp);
|
||||
free(dirname_tmp);
|
||||
config.ain_filename = strdup(path_basename(ain_path));
|
||||
config.game_dir = strdup(path_dirname(ain_path));
|
||||
config_init();
|
||||
}
|
||||
|
||||
|
||||
+18
-48
@@ -61,22 +61,11 @@ static mat4 world_view_transform = MAT4(
|
||||
0, 0, 1, -1,
|
||||
0, 0, 0, 1);
|
||||
|
||||
struct default_shader {
|
||||
struct shader s;
|
||||
GLuint alpha_mod;
|
||||
} default_shader;
|
||||
static struct shader default_shader;
|
||||
|
||||
GLuint main_surface_fb;
|
||||
struct texture main_surface;
|
||||
|
||||
void prepare_default_shader(struct gfx_render_job *job, void *data)
|
||||
{
|
||||
struct default_shader *s = (struct default_shader*)job->shader;
|
||||
struct texture *t = (struct texture*)data;
|
||||
|
||||
glUniform1f(s->alpha_mod, t->alpha_mod / 255.0);
|
||||
}
|
||||
|
||||
static GLchar *read_shader_file(const char *path)
|
||||
{
|
||||
GLchar *source = file_read(path, NULL);
|
||||
@@ -133,15 +122,12 @@ void gfx_load_shader(struct shader *dst, const char *vertex_shader_path, const c
|
||||
dst->view_transform = glGetUniformLocation(program, "view_transform");
|
||||
dst->texture = glGetUniformLocation(program, "tex");
|
||||
dst->vertex = glGetAttribLocation(program, "vertex_pos");
|
||||
dst->alpha_mod = glGetAttribLocation(program, "alpha_mod");
|
||||
dst->prepare = NULL;
|
||||
}
|
||||
|
||||
static int gl_initialize(void)
|
||||
{
|
||||
gfx_load_shader(&default_shader.s, "shaders/render.v.glsl", "shaders/render.f.glsl");
|
||||
default_shader.alpha_mod = glGetUniformLocation(default_shader.s.program, "alpha_mod");
|
||||
default_shader.s.prepare = prepare_default_shader;
|
||||
gfx_load_shader(&default_shader, "shaders/render.v.glsl", "shaders/render.f.glsl");
|
||||
|
||||
glClearColor(0.f, 0.f, 0.f, 1.f);
|
||||
|
||||
@@ -243,7 +229,7 @@ int gfx_init(void)
|
||||
|
||||
void gfx_fini(void)
|
||||
{
|
||||
glDeleteProgram(default_shader.s.program);
|
||||
glDeleteProgram(default_shader.program);
|
||||
SDL_DestroyWindow(sdl.window);
|
||||
SDL_FreeFormat(sdl.format);
|
||||
SDL_Quit();
|
||||
@@ -268,8 +254,6 @@ static void main_surface_init(int w, int h)
|
||||
main_surface.w = w;
|
||||
main_surface.h = h;
|
||||
main_surface.has_alpha = true;
|
||||
main_surface.alpha_mod = 255;
|
||||
main_surface.draw_method = DRAW_METHOD_NORMAL;
|
||||
|
||||
glGenFramebuffers(1, &main_surface_fb);
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, main_surface_fb);
|
||||
@@ -349,7 +333,7 @@ void gfx_swap(void)
|
||||
0, 0, 1, 0,
|
||||
0, 0, 0, 1);
|
||||
struct gfx_render_job job = {
|
||||
.shader = &default_shader.s,
|
||||
.shader = &default_shader,
|
||||
.texture = main_surface.handle,
|
||||
.world_transform = mw_transform[0],
|
||||
.view_transform = wv_transform[0],
|
||||
@@ -402,7 +386,7 @@ void gfx_run_job(struct gfx_render_job *job)
|
||||
void gfx_render(struct gfx_render_job *job)
|
||||
{
|
||||
if (!job->shader) {
|
||||
job->shader = &default_shader.s;
|
||||
job->shader = &default_shader;
|
||||
gfx_prepare_job(job);
|
||||
gfx_run_job(job);
|
||||
job->shader = NULL;
|
||||
@@ -412,46 +396,29 @@ void gfx_render(struct gfx_render_job *job)
|
||||
}
|
||||
}
|
||||
|
||||
void gfx_render_texture(struct texture *t, Rectangle *r)
|
||||
void _gfx_render_texture(struct shader *s, struct texture *t, Rectangle *r, void *data)
|
||||
{
|
||||
if (!t->handle) {
|
||||
WARNING("Attempted to render uninitialized texture");
|
||||
return;
|
||||
}
|
||||
// set blend mode
|
||||
switch (t->draw_method) {
|
||||
case DRAW_METHOD_SCREEN:
|
||||
glBlendFuncSeparate(GL_ONE, GL_ONE_MINUS_SRC_COLOR, GL_ZERO, GL_ONE);
|
||||
break;
|
||||
case DRAW_METHOD_MULTIPLY:
|
||||
glBlendFuncSeparate(GL_DST_COLOR, GL_ZERO, GL_ZERO, GL_ONE);
|
||||
break;
|
||||
case DRAW_METHOD_ADDITIVE:
|
||||
glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE, GL_ZERO, GL_ONE);
|
||||
break;
|
||||
default:
|
||||
// FIXME: why doesn't this work?
|
||||
//if (t->alpha_mod != 255) {
|
||||
// glBlendColor(0, 0, 0, t->alpha_mod / 255.0);
|
||||
// glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_CONSTANT_ALPHA, GL_ZERO);
|
||||
//}
|
||||
break;
|
||||
}
|
||||
|
||||
t->world_transform[3][0] = r ? r->x : 0;
|
||||
t->world_transform[3][1] = r ? r->y : 0;
|
||||
|
||||
struct gfx_render_job job = {
|
||||
.shader = &default_shader.s,
|
||||
.shader = s,
|
||||
.texture = t->handle,
|
||||
.world_transform = t->world_transform[0],
|
||||
.view_transform = world_view_transform[0],
|
||||
.data = t
|
||||
.data = data
|
||||
};
|
||||
gfx_render(&job);
|
||||
}
|
||||
|
||||
if (t->draw_method != DRAW_METHOD_NORMAL || t->alpha_mod != 255)
|
||||
glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ZERO);
|
||||
void gfx_render_texture(struct texture *t, Rectangle *r)
|
||||
{
|
||||
_gfx_render_texture(&default_shader, t, r, t);
|
||||
}
|
||||
|
||||
static void init_texture(struct texture *t, int w, int h)
|
||||
@@ -473,9 +440,6 @@ static void init_texture(struct texture *t, int w, int h)
|
||||
t->h = h;
|
||||
|
||||
t->has_alpha = true;
|
||||
t->alpha_mod = 255;
|
||||
t->draw_method = DRAW_METHOD_NORMAL;
|
||||
|
||||
}
|
||||
|
||||
void gfx_init_texture_with_pixels(struct texture *t, int w, int h, void *pixels)
|
||||
@@ -484,6 +448,12 @@ void gfx_init_texture_with_pixels(struct texture *t, int w, int h, void *pixels)
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, w, h, 0, GL_RGBA, GL_UNSIGNED_BYTE, pixels);
|
||||
}
|
||||
|
||||
void gfx_update_texture_with_pixels(struct texture *t, void *pixels)
|
||||
{
|
||||
glBindTexture(GL_TEXTURE_2D, t->handle);
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, t->w, t->h, 0, GL_RGBA, GL_UNSIGNED_BYTE, pixels);
|
||||
}
|
||||
|
||||
void gfx_init_texture_with_cg(struct texture *t, struct cg *cg)
|
||||
{
|
||||
gfx_init_texture_with_pixels(t, cg->metrics.w, cg->metrics.h, cg->pixels);
|
||||
|
||||
@@ -429,7 +429,7 @@ static void system_call(enum syscall_code code)
|
||||
case SYS_GLOBAL_SAVE: { // system.GlobalSave(string szKeyName, string szFileName)
|
||||
int filename = stack_pop().i;
|
||||
int keyname = stack_pop().i;
|
||||
stack_push(save_globals(heap_get_string(keyname)->text, heap_get_string(filename)->text));
|
||||
stack_push(save_globals(heap_get_string(keyname)->text, heap_get_string(filename)->text, NULL, NULL));
|
||||
heap_unref(filename);
|
||||
heap_unref(keyname);
|
||||
break;
|
||||
@@ -570,11 +570,7 @@ static void system_call(enum syscall_code code)
|
||||
}
|
||||
case SYS_SLEEP: {// system.Sleep(int nSleep)
|
||||
int ms = stack_pop().i;
|
||||
struct timespec ts = {
|
||||
.tv_sec = ms / 1000,
|
||||
.tv_nsec = (ms % 1000) * 1000000L
|
||||
};
|
||||
nanosleep(&ts, NULL);
|
||||
vm_sleep(ms);
|
||||
break;
|
||||
}
|
||||
case SYS_RESUME_READ_COMMENT: {// system.ResumeReadComment(string szKeyName, string szFileName, ref array@string aszComment)
|
||||
@@ -607,7 +603,7 @@ static void system_call(enum syscall_code code)
|
||||
int groupname = stack_pop().i;
|
||||
int filename = stack_pop().i;
|
||||
int keyname = stack_pop().i;
|
||||
stack_push(save_group(heap_get_string(keyname)->text,
|
||||
stack_push(save_globals(heap_get_string(keyname)->text,
|
||||
heap_get_string(filename)->text,
|
||||
heap_get_string(groupname)->text,
|
||||
&n->i));
|
||||
@@ -2374,6 +2370,11 @@ int vm_time(void)
|
||||
return (ts.tv_sec * 1000) + (ts.tv_nsec / 1000000);
|
||||
}
|
||||
|
||||
void vm_sleep(int ms)
|
||||
{
|
||||
SDL_Delay(ms);
|
||||
}
|
||||
|
||||
_Noreturn void vm_exit(int code)
|
||||
{
|
||||
vm_free();
|
||||
|
||||
+1
-1
Submodule subprojects/libsys4 updated: 481581b3b5...fa66424923
Reference in New Issue
Block a user