Read .exe and set window icon

Read the game's executable file, extract icons from it, and set the
window icon.

(Ported from ai5-sdl2.)
This commit is contained in:
Nunuhara Cabbage
2025-09-06 16:30:18 -07:00
parent 6cada80d34
commit 44c73f644f
7 changed files with 822 additions and 0 deletions
+1
View File
@@ -57,6 +57,7 @@ target_sources(xsystem4 PRIVATE
src/format.c
src/hacks.c
src/heap.c
src/icon.c
src/id_pool.c
src/input.c
src/json.c
+26
View File
@@ -0,0 +1,26 @@
/* Copyright (C) 2025 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/>.
*/
#ifndef SYSTEM4_ICON_H
#define SYSTEM4_ICON_H
#ifdef __ANDROID__
#define icon_init()
#else
void icon_init(void);
#endif
#endif /* SYSTEM4_ICON_H */
+1
View File
@@ -27,6 +27,7 @@ enum resume_save_format {
struct config {
char *game_name;
char *boot_name;
char *ain_filename;
char *vm_name;
char *game_dir;
+788
View File
@@ -0,0 +1,788 @@
/* 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/>.
*/
#include <stdlib.h>
#include <assert.h>
#include <string.h>
#include <SDL.h>
#include "system4.h"
#include "system4/buffer.h"
#include "system4/cg.h"
#include "system4/file.h"
#include "system4/little_endian.h"
#include "xsystem4.h"
#include "gfx/private.h"
#pragma pack(1)
#define NR_DIRECTORY_ENTRIES 16
#define SECTION_NAME_SIZE 8
#define DIRECTORY_ENTRY_RESOURCE 2
#define IMAGE_SCN_CNT_UNINITIALIZED_DATA 0x80
struct dos_header {
uint16_t magic;
uint16_t cblp;
uint16_t cp;
uint16_t crlc;
uint16_t cparhdr;
uint16_t minalloc;
uint16_t maxalloc;
uint16_t ss;
uint16_t sp;
uint16_t csum;
uint16_t ip;
uint16_t cs;
uint16_t lfarlc;
uint16_t ovno;
uint16_t res[4];
uint16_t oemid;
uint16_t oeminfo;
uint16_t res2[10];
uint32_t lfanew;
};
// COFF header
struct coff_header {
uint16_t machine;
uint16_t number_of_sections;
uint32_t time_date_stamp;
uint32_t pointer_to_symbol_table;
uint32_t number_of_symbols;
uint16_t size_of_optional_header;
uint16_t characteristics;
};
struct data_directory {
uint32_t virtual_address;
uint32_t size;
};
// PE optional header
struct optional_header {
uint16_t magic;
uint8_t major_linker_version;
uint8_t minor_linker_version;
uint32_t size_of_code;
uint32_t size_of_initialized_data;
uint32_t size_of_uninitialized_data;
uint32_t address_of_entry_point;
uint32_t base_of_code;
uint32_t base_of_data;
uint32_t image_base;
uint32_t section_alignment;
uint32_t file_alignment;
uint16_t major_operating_system_version;
uint16_t minor_operating_system_version;
uint16_t major_image_version;
uint16_t minor_image_version;
uint16_t major_subsystem_version;
uint16_t minor_subsystem_version;
uint32_t win32_version_value;
uint32_t size_of_image;
uint32_t size_of_headers;
uint32_t checksum;
uint16_t subsystem;
uint16_t dll_characteristics;
uint32_t size_of_stack_reserve;
uint32_t size_of_stack_commit;
uint32_t size_of_heap_reserve;
uint32_t size_of_heap_commit;
uint32_t loader_flags;
uint32_t number_of_rva_and_sizes;
struct data_directory data_directory[NR_DIRECTORY_ENTRIES];
};
struct pe_header {
uint32_t signature;
struct coff_header coff;
struct optional_header opt;
};
// header describing section in executable image
struct section_header {
uint8_t name[SECTION_NAME_SIZE];
union {
uint32_t physical_address;
uint32_t virtual_size;
} misc;
uint32_t virtual_address;
uint32_t size_of_raw_data;
uint32_t pointer_to_raw_data;
uint32_t pointer_to_relocations;
uint32_t pointer_to_linenumbers;
uint16_t number_of_relocations;
uint16_t number_of_linenumbers;
uint32_t characteristics;
};
// resource directory table
struct res_dir_table {
uint32_t characteristics;
uint32_t time_date_stamp;
uint16_t major_version;
uint16_t minor_version;
uint16_t number_of_named_entries;
uint16_t number_of_id_entries;
};
// entry in resource directory table
struct res_dir_entry {
uint32_t id;
uint32_t off;
};
// leaf data of resource directory table
struct res_data_entry {
uint32_t offset_to_data;
uint32_t size;
uint32_t code_page;
uint32_t resource_handle;
};
// group_cursor resource data (table header)
struct cursor_dir {
uint16_t reserved;
uint16_t type;
uint16_t count;
};
// entry in group_cursor table
struct cursor_dir_entry {
uint16_t width;
uint16_t height;
uint16_t plane_count;
uint16_t bit_count;
uint32_t bytes_in_res;
uint16_t res_id;
};
// group_icon resource data (table header)
struct icon_dir {
uint16_t reserved;
uint16_t type;
uint16_t count;
};
// entry in group_icon table
struct icon_dir_entry {
uint8_t width;
uint8_t height;
uint8_t color_count;
uint8_t reserved;
uint16_t plane_count;
uint16_t bit_count;
uint32_t bytes_in_res;
uint16_t res_id;
};
// BITMAPINFOHEADER
struct bitmap_info {
uint32_t size;
uint32_t width;
uint32_t height;
uint16_t planes;
uint16_t bpp;
uint32_t compression;
uint32_t size_image;
uint32_t ppm_x;
uint32_t ppm_y;
uint32_t colors_used;
uint32_t colors_important;
};
struct color_map {
struct { uint8_t b, g, r, u; } colors[256];
};
// cursor resource data
struct cursor_data {
uint16_t hotspot_x;
uint16_t hotspot_y;
struct bitmap_info bm_info;
struct color_map colors;
};
// icon resource data
struct icon_data {
struct bitmap_info bm_info;
struct color_map colors;
};
struct resource {
uint32_t id;
struct resource *children;
unsigned nr_children;
struct {
uint32_t addr;
uint32_t size;
} leaf;
};
struct vma {
uint32_t virt_addr;
uint32_t size;
uint32_t raw_addr;
bool initialized;
};
// TODO: use this abstraction for memory reads on executable image
struct vmm {
struct vma *vmas;
unsigned nr_vmas;
struct buffer exe;
};
uint8_t *vmm_lookup(uint32_t ptr);
#pragma pack()
static bool buffer_expect_string(struct buffer *buf, const char *magic, size_t magic_size)
{
if (buffer_remaining(buf) < magic_size)
return false;
return !strncmp(buffer_strdata(buf), magic, magic_size);
}
static uint16_t buffer_read_u16_at(struct buffer *buf, size_t off)
{
size_t tmp = buf->index;
buf->index = off;
uint32_t v = buffer_read_u16(buf);
buf->index = tmp;
return v;
}
static uint32_t buffer_read_u32_at(struct buffer *buf, size_t off)
{
size_t tmp = buf->index;
buf->index = off;
uint32_t v = buffer_read_int32(buf);
buf->index = tmp;
return v;
}
#define read_member16(b, s, m) \
buffer_read_u16_at(b, b->index + offsetof(s, m));
#define read_member32(b, s, m) \
buffer_read_u32_at(b, b->index + offsetof(s, m));
static bool read_pe_header(struct buffer *buf, uint32_t *addr_out, uint32_t *size_out,
struct vma **vma_out, uint16_t *nr_vmas_out)
{
possibly_unused size_t pe_loc = buf->index;
if (buffer_remaining(buf) < sizeof(struct pe_header))
return false;
if (buffer_read_int32(buf) != 0x4550)
return false;
uint16_t nr_sections = read_member16(buf, struct coff_header, number_of_sections);
uint16_t opt_size = read_member16(buf, struct coff_header, size_of_optional_header);
buffer_skip(buf, sizeof(struct coff_header));
assert(buf->index == pe_loc + offsetof(struct pe_header, opt));
size_t opt_pos = buf->index;
buffer_skip(buf, offsetof(struct optional_header, data_directory));
buffer_skip(buf, sizeof(struct data_directory) * DIRECTORY_ENTRY_RESOURCE);
*addr_out = read_member32(buf, struct data_directory, virtual_address);
*size_out = read_member32(buf, struct data_directory, size);
buffer_seek(buf, opt_pos + opt_size);
if (buffer_remaining(buf) < nr_sections * sizeof(struct section_header))
return false;
struct vma *vma = xmalloc(nr_sections * sizeof(struct vma));
for (int i = 0; i < nr_sections; i++) {
vma[i].virt_addr = read_member32(buf, struct section_header, virtual_address);
vma[i].size = read_member32(buf, struct section_header, size_of_raw_data);
vma[i].raw_addr = read_member32(buf, struct section_header, pointer_to_raw_data);
uint32_t characteristics = read_member32(buf, struct section_header, characteristics);
vma[i].initialized = !(characteristics & IMAGE_SCN_CNT_UNINITIALIZED_DATA);
buffer_skip(buf, sizeof(struct section_header));
}
*vma_out = vma;
*nr_vmas_out = nr_sections;
return true;
}
static bool read_res_dir_table(struct buffer *b, struct res_dir_entry **entry_out,
uint16_t *nr_named_out, uint16_t *nr_id_out)
{
if (buffer_remaining(b) < sizeof(struct res_dir_table))
return false;
uint16_t nr_named = read_member16(b, struct res_dir_table, number_of_named_entries);
uint16_t nr_id = read_member16(b, struct res_dir_table, number_of_id_entries);
unsigned nr_entries = nr_named + nr_id;
buffer_skip(b, sizeof(struct res_dir_table));
if (buffer_remaining(b) < nr_entries * sizeof(struct res_dir_entry))
return false;
struct res_dir_entry *entry = xmalloc(nr_entries * sizeof(struct res_dir_entry));
for (unsigned i = 0; i < nr_entries; i++) {
entry[i].id = buffer_read_int32(b);
entry[i].off = buffer_read_int32(b);
}
*entry_out = entry;
*nr_named_out = nr_named;
*nr_id_out = nr_id;
return true;
}
bool read_resource_data(struct buffer *b, struct resource *node)
{
if (buffer_remaining(b) < sizeof(struct res_data_entry))
return false;
node->leaf.addr = buffer_read_int32(b);
node->leaf.size = buffer_read_int32(b);
return true;
}
bool read_resource_table(struct buffer *b, struct resource *node, uint32_t res_base)
{
uint16_t nr_named, nr_id;
struct res_dir_entry *entry = NULL;
if (!read_res_dir_table(b, &entry, &nr_named, &nr_id))
goto error;
node->nr_children = nr_named + nr_id;
node->children = xcalloc(node->nr_children, sizeof(struct resource));
for (unsigned i = 0; i < node->nr_children; i++) {
node->children[i].id = entry[i].id;
buffer_seek(b, res_base + (entry[i].off & 0x7fffffff));
if (entry[i].off & 0x80000000) {
if (!read_resource_table(b, &node->children[i], res_base))
goto error;
} else {
if (!read_resource_data(b, &node->children[i]))
goto error;
}
}
free(entry);
return true;
error:
free(entry);
free(node->children);
return false;
}
struct resource *read_resources(struct buffer *b)
{
struct resource *root = xcalloc(1, sizeof(struct resource));
if (read_resource_table(b, root, b->index))
return root;
free(root);
return NULL;
}
void _free_resources(struct resource *res)
{
for (unsigned i = 0; i < res->nr_children; i++) {
_free_resources(&res->children[i]);
}
free(res->children);
}
void free_resources(struct resource *root)
{
_free_resources(root);
free(root);
}
void read_bitmap_info(struct buffer *b, struct bitmap_info *bm_info)
{
bm_info->size = buffer_read_int32(b);
bm_info->width = buffer_read_int32(b);
bm_info->height = buffer_read_int32(b);
bm_info->planes = buffer_read_u16(b);
bm_info->bpp = buffer_read_u16(b);
bm_info->compression = buffer_read_int32(b);
bm_info->size_image = buffer_read_int32(b);
bm_info->ppm_x = buffer_read_int32(b);
bm_info->ppm_y = buffer_read_int32(b);
bm_info->colors_used = buffer_read_int32(b);
bm_info->colors_important = buffer_read_int32(b);
}
static struct cg *cg_alloc_direct(unsigned w, unsigned h)
{
struct cg *cg = xcalloc(1, sizeof(struct cg));
cg->metrics.w = w;
cg->metrics.h = h;
cg->metrics.bpp = 32;
cg->metrics.has_pixel = true;
cg->metrics.has_alpha = true;
cg->metrics.pixel_pitch = w * 4;
cg->metrics.alpha_pitch = 1;
cg->pixels = xcalloc(w * 4, h);
return cg;
}
static struct cg *load_bmp_8bpp(struct bitmap_info *bm_info, struct color_map *colors,
uint8_t *pixels, uint8_t *bitmask)
{
unsigned w = bm_info->width;
unsigned h = bm_info->height / 2;
// expand mask
uint8_t *mask = xmalloc(w * h);
for (int row = 0; row < h; row++) {
uint8_t *dst = mask + row * w;
uint8_t *src = bitmask + row * (w/8);
for (int bit = 0x80, col = 0; col < w; bit >>= 1, col++, dst++) {
if (bit == 0) {
bit = 0x80;
src++;
}
*dst = (*src & bit) ? 0 : 255;
}
}
// convert pixels/mask to RGBA CG
struct cg *cg = cg_alloc_direct(w, h);
for (unsigned row = 0; row < h; row++) {
uint8_t *dst = cg->pixels + row * w * 4;
uint8_t *p_src = pixels + (h - (row + 1)) * w;
uint8_t *m_src = mask + (h - (row + 1)) * w;
for (unsigned col = 0; col < w; col++, p_src++, m_src++, dst += 4) {
uint8_t c = *p_src;
dst[0] = colors->colors[c].r;
dst[1] = colors->colors[c].g;
dst[2] = colors->colors[c].b;
dst[3] = *m_src;
}
}
free(mask);
return cg;
}
static struct cg *load_bmp_4bpp(struct bitmap_info *bm_info, struct color_map *colors,
uint8_t *pixels, uint8_t *bitmask)
{
unsigned w = bm_info->width;
unsigned h = bm_info->height / 2;
// expand mask
uint8_t *mask = xmalloc(w * h);
for (int row = 0; row < h; row++) {
uint8_t *dst = mask + row * w;
uint8_t *src = bitmask + row * (((w/8) + 3) & ~3);
for (int bit = 0x80, col = 0; col < w; bit >>= 1, col++, dst++) {
if (bit == 0) {
bit = 0x80;
src++;
}
*dst = (*src & bit) ? 0 : 255;
}
}
// convert pixels/mask to RGBA CG
struct cg *cg = cg_alloc_direct(w, h);
for (unsigned row = 0; row < h; row++) {
uint8_t *dst = cg->pixels + row * w * 4;
uint8_t *p_src = pixels + (h - (row + 1)) * w / 2;
uint8_t *m_src = mask + (h - (row + 1)) * w;
for (unsigned col = 0; col < w; col += 2, p_src++, m_src += 2, dst += 8) {
uint8_t c = *p_src >> 4;
dst[0] = colors->colors[c].r;
dst[1] = colors->colors[c].g;
dst[2] = colors->colors[c].b;
dst[3] = m_src[0];
c = *p_src & 0xf;
dst[4] = colors->colors[c].r;
dst[5] = colors->colors[c].g;
dst[6] = colors->colors[c].b;
dst[7] = m_src[1];
}
}
free(mask);
return cg;
}
static bool read_icon_data(struct buffer *b, struct icon_data *icon, uint8_t **image, uint8_t **bitmask)
{
if (buffer_remaining(b) < sizeof(struct bitmap_info))
return false;
read_bitmap_info(b, &icon->bm_info);
if (icon->bm_info.planes != 1)
return false;
if (icon->bm_info.bpp != 8 && icon->bm_info.bpp != 4)
return false;
if (icon->bm_info.compression != 0)
return false;
int nr_colors = icon->bm_info.colors_used;
if (!nr_colors) {
if (icon->bm_info.bpp == 4)
nr_colors = 16;
else
nr_colors = 256;
}
if (buffer_remaining(b) < 4 * nr_colors)
return false;
int image_size = icon->bm_info.width * (icon->bm_info.height / 2);
if (icon->bm_info.bpp == 4)
image_size /= 2;
for (unsigned i = 0; i < nr_colors; i++) {
icon->colors.colors[i].b = buffer_read_u8(b);
icon->colors.colors[i].g = buffer_read_u8(b);
icon->colors.colors[i].r = buffer_read_u8(b);
icon->colors.colors[i].u = buffer_read_u8(b);
}
*image = (uint8_t*)buffer_strdata(b);
buffer_skip(b, image_size);
*bitmask = (uint8_t*)buffer_strdata(b);
return true;
}
static SDL_Surface *read_icon(struct buffer *b, struct resource *res, struct cg **cg_out)
{
struct icon_data icon;
uint8_t *image;
uint8_t *bitmask;
buffer_seek(b, res->leaf.addr);
if (!read_icon_data(b, &icon, &image, &bitmask)) {
//WARNING("failed to read icon data");
return NULL;
}
struct cg *cg;
if (icon.bm_info.bpp == 8) {
cg = load_bmp_8bpp(&icon.bm_info, &icon.colors, image, bitmask);
} else if (icon.bm_info.bpp == 4) {
cg = load_bmp_4bpp(&icon.bm_info, &icon.colors, image, bitmask);
} else {
WARNING("invalid bpp: %d", icon.bm_info.bpp);
return NULL;
}
SDL_Surface *s = SDL_CreateRGBSurfaceWithFormatFrom(cg->pixels, cg->metrics.w,
cg->metrics.h, 32, cg->metrics.w * 4, SDL_PIXELFORMAT_RGBA32);
if (!s)
ERROR("SDL_CreateRGBSurfaceWithFormatFrom: %s", SDL_GetError());
*cg_out = cg;
return s;
}
static int read_group_icon(struct buffer *b, struct resource *res)
{
buffer_seek(b, res->leaf.addr);
if (buffer_remaining(b) < sizeof(struct icon_dir))
return -1;
uint16_t count = read_member16(b, struct icon_dir, count);
if (count < 1) {
WARNING("group_icon directory contains no entries");
return -1;
}
//if (count > 1)
// WARNING("Ignoring additional entries in group_icon directory");
buffer_skip(b, sizeof(struct icon_dir));
buffer_skip(b, offsetof(struct icon_dir_entry, res_id));
return buffer_read_u16(b);
}
static int nr_icons = 0;
static SDL_Surface **icons = NULL;
static struct cg **icon_cgs = NULL;
void read_icons(struct buffer *b, struct resource *root)
{
struct resource *icon = NULL;
struct resource *group_icon = NULL;
for (unsigned i = 0; i < root->nr_children; i++) {
if (root->children[i].id == 3) {
icon = &root->children[i];
} else if (root->children[i].id == 14) {
group_icon = &root->children[i];
}
}
nr_icons = group_icon->nr_children;
icons = xcalloc(nr_icons, sizeof(struct SDL_Surface*));
icon_cgs = xcalloc(nr_icons, sizeof(struct cg*));
for (unsigned i = 0; i < group_icon->nr_children; i++) {
struct resource *child = &group_icon->children[i];
if (child->nr_children != 1 || child->children[0].nr_children != 0) {
WARNING("Unexpected resource layout (icon %d)", i);
continue;
}
int res_id = read_group_icon(b, &child->children[0]);
if (res_id < 0)
continue;
// get the corresponding icon resource
struct resource *icon_res = NULL;
for (unsigned j = 0; j < icon->nr_children; j++) {
struct resource *child = &icon->children[j];
if (child->id == res_id) {
if (child->nr_children != 1 || child->children[0].nr_children != 0) {
WARNING("Unexpected resource layout (icon %d)", i);
continue;
}
icon_res = &child->children[0];
}
}
if (!icon_res) {
WARNING("Couldn't find icon for group_icon %d", i);
continue;
}
icons[i] = read_icon(b, icon_res, &icon_cgs[i]);
}
}
SDL_Surface *icon_get(unsigned no)
{
return no < nr_icons ? icons[no] : NULL;
}
bool _icon_init(const char *exe_path)
{
struct buffer buf;
struct vma *vma = NULL;
struct res_dir_entry *entry = NULL;
buf.buf = file_read(exe_path, &buf.size);
buf.index = 0;
if (!buf.buf) {
WARNING("Failed to read executable: %s", exe_path);
return false;
}
// expect DOS header
if (!buffer_expect_string(&buf, "MZ", 2))
goto error;
if (buffer_remaining(&buf) < sizeof(struct dos_header))
goto error;
// seek to PE header
buffer_skip(&buf, offsetof(struct dos_header, lfanew));
buffer_seek(&buf, buffer_read_int32(&buf));
if (!buffer_expect_string(&buf, "PE\0\0", 4))
goto error;
// read relevant data from PE header
uint32_t res_virt_addr;
uint32_t res_virt_size;
uint16_t nr_vmas;
if (!read_pe_header(&buf, &res_virt_addr, &res_virt_size, &vma, &nr_vmas))
goto error;
// calculate size of expanded process image
size_t vma_size = 0;
for (int i = 0; i < nr_vmas; i++) {
vma_size = max(vma_size, vma[i].virt_addr + vma[i].size);
}
// load sections into memory at correct offsets
// TODO: use an abstraction for virtual memory lookups that doesn't involve
// copying memory
buf.buf = realloc(buf.buf, vma_size);
buf.size = vma_size;
for (int i = nr_vmas - 1; i >= 0; i--) {
if (!vma[i].initialized)
continue;
if (vma[i].virt_addr + vma[i].size > buf.size)
goto error;
if (vma[i].raw_addr + vma[i].size > buf.size)
goto error;
if (vma[i].virt_addr != vma[i].raw_addr) {
memmove(buf.buf + vma[i].virt_addr, buf.buf + vma[i].raw_addr, vma[i].size);
}
}
if (res_virt_addr + res_virt_size > buf.size)
goto error;
buffer_seek(&buf, res_virt_addr);
struct resource *root = read_resources(&buf);
if (!root)
goto error;
read_icons(&buf, root);
free_resources(root);
free(buf.buf);
free(vma);
free(entry);
SDL_Surface *icon = icon_get(0);
if (icon)
SDL_SetWindowIcon(sdl.window, icon);
return true;
error:
WARNING("Invalid/unexpected executable format: %s", exe_path);
free(buf.buf);
free(vma);
free(entry);
return false;
}
static char *get_exe_path(const char *name)
{
char filename[256];
snprintf(filename, 256, "%s.exe", name);
char *path = gamedir_path(filename);
if (file_exists(path))
return path;
char *i_path = path_get_icase(path);
free(path);
if (i_path)
return i_path;
return NULL;
}
static const char *exe_names[] = {
"System40",
"Rance01", // XXX: MG deleted BootName from AliceStart.ini...
"daibanchou",
"galzoo",
"Hoken",
"ojou",
"paschacpp",
"toushin3",
"vanish",
"yokubori",
};
void icon_init(void)
{
char *path = NULL;
if (config.boot_name)
path = get_exe_path(config.boot_name);
for (int i = 0; !path && i < sizeof(exe_names)/sizeof(*exe_names); i++) {
path = get_exe_path(exe_names[i]);
}
if (path) {
_icon_init(path);
free(path);
}
}
+1
View File
@@ -19,6 +19,7 @@ xsystem4 = [version_h,
'format.c',
'hacks.c',
'heap.c',
'icon.c',
'id_pool.c',
'input.c',
'json.c',
+3
View File
@@ -48,6 +48,7 @@ void apply_game_specific_hacks(struct ain *ain);
struct config config = {
.game_name = NULL,
.boot_name = NULL,
.ain_filename = NULL,
.vm_name = NULL,
.game_dir = NULL,
@@ -132,6 +133,8 @@ static bool read_config(const char *path)
for (int i = 0; i < ini_size; i++) {
if (!strcmp(ini[i].name->text, "GameName")) {
config.game_name = strdup(ini_string(&ini[i])->text);
} else if (!strcmp(ini[i].name->text, "BootName")) {
config.boot_name = strdup(ini_string(&ini[i])->text);
} else if (!strcmp(ini[i].name->text, "CodeName")) {
config.ain_filename = strdup(ini_string(&ini[i])->text);
} else if (!strcmp(ini[i].name->text, "MainVM")) {
+2
View File
@@ -29,6 +29,7 @@
#include "gfx/gfx.h"
#include "gfx/private.h"
#include "icon.h"
#include "xsystem4.h"
struct sdl_private sdl;
@@ -278,6 +279,7 @@ int gfx_init(void)
init_window_size();
atexit(gfx_fini);
gfx_clear();
icon_init();
gfx_initialized = true;
return 0;
}