Merge branch 'nightly' into feature/discordrpc

This commit is contained in:
Jan 2020-06-09 17:16:42 +02:00 committed by GitHub
commit 233c2d8f72
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
97 changed files with 4843 additions and 2912 deletions

View file

@ -10,6 +10,8 @@ default: all
# These options can either be changed by modifying the makefile, or # These options can either be changed by modifying the makefile, or
# by building with 'make SETTING=value'. 'make clean' may be required. # by building with 'make SETTING=value'. 'make clean' may be required.
# Build debug version (default)
DEBUG ?= 1
# Version of the game to build # Version of the game to build
VERSION ?= us VERSION ?= us
# Graphics microcode used # Graphics microcode used
@ -56,6 +58,11 @@ NO_LDIV ?= 0
LEGACY_GL ?= 0 LEGACY_GL ?= 0
# Misc settings for EXTERNAL_DATA
BASEDIR ?= res
BASEPACK ?= base.zip
# Automatic settings for PC port(s) # Automatic settings for PC port(s)
NON_MATCHING := 1 NON_MATCHING := 1
@ -75,9 +82,9 @@ else
endif endif
ifeq ($(TARGET_WEB),0) ifeq ($(TARGET_WEB),0)
ifeq ($(HOST_OS),Windows) ifeq ($(HOST_OS),Windows)
WINDOWS_BUILD := 1 WINDOWS_BUILD := 1
endif endif
endif endif
# MXE overrides # MXE overrides
@ -96,8 +103,6 @@ endif
ifneq ($(TARGET_BITS),0) ifneq ($(TARGET_BITS),0)
BITS := -m$(TARGET_BITS) BITS := -m$(TARGET_BITS)
else
BITS :=
endif endif
# Release (version) flag defs # Release (version) flag defs
@ -268,7 +273,7 @@ LEVEL_DIRS := $(patsubst levels/%,%,$(dir $(wildcard levels/*/header.h)))
# Directories containing source files # Directories containing source files
# Hi, I'm a PC # Hi, I'm a PC
SRC_DIRS := src src/engine src/game src/audio src/menu src/buffers actors levels bin data assets src/pc src/pc/gfx src/pc/audio src/pc/controller SRC_DIRS := src src/engine src/game src/audio src/menu src/buffers actors levels bin data assets src/pc src/pc/gfx src/pc/audio src/pc/controller src/pc/fs src/pc/fs/packtypes
ASM_DIRS := ASM_DIRS :=
ifeq ($(DISCORDRPC),1) ifeq ($(DISCORDRPC),1)
@ -286,15 +291,11 @@ GODDARD_SRC_DIRS := src/goddard src/goddard/dynlists
MIPSISET := -mips2 MIPSISET := -mips2
MIPSBIT := -32 MIPSBIT := -32
ifeq ($(VERSION),eu) ifeq ($(DEBUG),1)
OPT_FLAGS := -O2 OPT_FLAGS := -g
else
ifeq ($(VERSION),sh)
OPT_FLAGS := -O2
else else
OPT_FLAGS := -O2 OPT_FLAGS := -O2
endif endif
endif
# Set BITS (32/64) to compile for # Set BITS (32/64) to compile for
OPT_FLAGS += $(BITS) OPT_FLAGS += $(BITS)
@ -303,10 +304,6 @@ ifeq ($(TARGET_WEB),1)
OPT_FLAGS := -O2 -g4 --source-map-base http://localhost:8080/ OPT_FLAGS := -O2 -g4 --source-map-base http://localhost:8080/
endif endif
# Use a default opt flag for gcc, then override if RPi
# OPT_FLAGS := -O2 # "Whole-compile optimization flag" Breaks sound on x86.
ifeq ($(TARGET_RPI),1) ifeq ($(TARGET_RPI),1)
machine = $(shell sh -c 'uname -m 2>/dev/null || echo unknown') machine = $(shell sh -c 'uname -m 2>/dev/null || echo unknown')
# Raspberry Pi B+, Zero, etc # Raspberry Pi B+, Zero, etc
@ -352,10 +349,6 @@ GODDARD_C_FILES := $(foreach dir,$(GODDARD_SRC_DIRS),$(wildcard $(dir)/*.c))
GENERATED_C_FILES := $(BUILD_DIR)/assets/mario_anim_data.c $(BUILD_DIR)/assets/demo_data.c \ GENERATED_C_FILES := $(BUILD_DIR)/assets/mario_anim_data.c $(BUILD_DIR)/assets/demo_data.c \
$(addprefix $(BUILD_DIR)/bin/,$(addsuffix _skybox.c,$(notdir $(basename $(wildcard textures/skyboxes/*.png))))) $(addprefix $(BUILD_DIR)/bin/,$(addsuffix _skybox.c,$(notdir $(basename $(wildcard textures/skyboxes/*.png)))))
ifeq ($(WINDOWS_BUILD),0)
CXX_FILES :=
endif
# We need to keep this for now # We need to keep this for now
# If we're not N64 use below # If we're not N64 use below
@ -594,10 +587,11 @@ endif
# Load external textures # Load external textures
ifeq ($(EXTERNAL_DATA),1) ifeq ($(EXTERNAL_DATA),1)
CC_CHECK += -DEXTERNAL_DATA CC_CHECK += -DEXTERNAL_DATA -DFS_BASEDIR="\"$(BASEDIR)\""
CFLAGS += -DEXTERNAL_DATA CFLAGS += -DEXTERNAL_DATA -DFS_BASEDIR="\"$(BASEDIR)\""
# tell skyconv to write names instead of actual texture data and save the split tiles so we can use them later # tell skyconv to write names instead of actual texture data and save the split tiles so we can use them later
SKYCONV_ARGS := --store-names --write-tiles "$(BUILD_DIR)/textures/skybox_tiles" SKYTILE_DIR := $(BUILD_DIR)/textures/skybox_tiles
SKYCONV_ARGS := --store-names --write-tiles "$(SKYTILE_DIR)"
endif endif
ASFLAGS := -I include -I $(BUILD_DIR) $(VERSION_ASFLAGS) ASFLAGS := -I include -I $(BUILD_DIR) $(VERSION_ASFLAGS)
@ -667,20 +661,31 @@ endif
ifeq ($(EXTERNAL_DATA),1) ifeq ($(EXTERNAL_DATA),1)
# depend on resources as well BASEPACK_PATH := $(BUILD_DIR)/$(BASEDIR)/$(BASEPACK)
all: res BASEPACK_LST := $(BUILD_DIR)/basepack.lst
# prepares the resource folder for external data # depend on resources as well
res: $(EXE) all: $(BASEPACK_PATH)
@mkdir -p $(BUILD_DIR)/res/sound
@$(CP) -r -f textures/ $(BUILD_DIR)/res/ # phony target for building resources
@$(CP) -r -f $(BUILD_DIR)/textures/skybox_tiles/ $(BUILD_DIR)/res/textures/ res: $(BASEPACK_PATH)
@$(CP) -f $(SOUND_BIN_DIR)/sound_data.ctl $(BUILD_DIR)/res/sound/
@$(CP) -f $(SOUND_BIN_DIR)/sound_data.tbl $(BUILD_DIR)/res/sound/ # prepares the basepack.lst
@$(CP) -f $(SOUND_BIN_DIR)/sequences.bin $(BUILD_DIR)/res/sound/ $(BASEPACK_LST): $(EXE)
@$(CP) -f $(SOUND_BIN_DIR)/bank_sets $(BUILD_DIR)/res/sound/ @mkdir -p $(BUILD_DIR)/$(BASEDIR)
@find actors -name \*.png -exec $(CP) --parents {} $(BUILD_DIR)/res/ \; @echo -n > $(BASEPACK_LST)
@find levels -name \*.png -exec $(CP) --parents {} $(BUILD_DIR)/res/ \; @echo "$(BUILD_DIR)/sound/bank_sets sound/bank_sets" >> $(BASEPACK_LST)
@echo "$(BUILD_DIR)/sound/sequences.bin sound/sequences.bin" >> $(BASEPACK_LST)
@echo "$(BUILD_DIR)/sound/sound_data.ctl sound/sound_data.ctl" >> $(BASEPACK_LST)
@echo "$(BUILD_DIR)/sound/sound_data.tbl sound/sound_data.tbl" >> $(BASEPACK_LST)
@$(foreach f, $(wildcard $(SKYTILE_DIR)/*), echo $(f) gfx/$(f:$(BUILD_DIR)/%=%) >> $(BASEPACK_LST);)
@find actors -name \*.png -exec echo "{} gfx/{}" >> $(BASEPACK_LST) \;
@find levels -name \*.png -exec echo "{} gfx/{}" >> $(BASEPACK_LST) \;
@find textures -name \*.png -exec echo "{} gfx/{}" >> $(BASEPACK_LST) \;
# prepares the resource ZIP with base data
$(BASEPACK_PATH): $(BASEPACK_LST)
@$(PYTHON) $(TOOLS_DIR)/mkzip.py $(BASEPACK_LST) $(BASEPACK_PATH)
endif endif

View file

@ -22,6 +22,9 @@ Please contribute **first** to the [nightly branch](https://github.com/sm64pc/sm
* Disabling the HUD. * Disabling the HUD.
* Cheats menu in Options. (Activate with `--cheats`) Please note that if a cheat asks you to press "L" it's referring to the N64 button. Check your bindings and make sure you have the "L" button mapped to a button in your controller. * Cheats menu in Options. (Activate with `--cheats`) Please note that if a cheat asks you to press "L" it's referring to the N64 button. Check your bindings and make sure you have the "L" button mapped to a button in your controller.
* Text-based save support. (Activate with `make TEXTSAVES=1`.) * Text-based save support. (Activate with `make TEXTSAVES=1`.)
* Recent changes in Nightly have moved the save and configuration file path to `%HOMEPATH%\AppData\Roaming\sm64pc` on Windows and `$HOME/.local/share/sm64pc` on Linux. This behaviour can be changed with the `--savepath` CLI option.
For example `--savepath .` will read saves from the current directory (which not always matches the exe directory, but most of the time it does);
`--savepath '!'` will read saves from the executable directory.
## Building ## Building
For building instructions, please refer to the [wiki](https://github.com/sm64pc/sm64pc/wiki). For building instructions, please refer to the [wiki](https://github.com/sm64pc/sm64pc/wiki).

View file

@ -7,8 +7,8 @@ LIBAFLA=libaudiofile.la
AUDDIR=./tools/audiofile-0.3.6 AUDDIR=./tools/audiofile-0.3.6
# Command line options # Command line options
OPTIONS=("Analog Camera" "No Draw Distance" "Text-saves" "Smoke Texture Fix" "Clean build") OPTIONS=("Analog Camera" "No Draw Distance" "Text-saves" "Smoke Texture Fix" "Release build" "Clean build")
EXTRA=("BETTERCAMERA=1" "NODRAWINGDISTANCE=1" "TEXTSAVES=1" "TEXTURE_FIX=1" "clean") EXTRA=("BETTERCAMERA=1" "NODRAWINGDISTANCE=1" "TEXTSAVES=1" "TEXTURE_FIX=1" "DEBUG=0" "clean")
# Colors # Colors
RED=$(tput setaf 1) RED=$(tput setaf 1)

View file

@ -26,10 +26,6 @@ This allows you to draw 3D boxes for debugging purposes.
Call the `debug_box` function whenever you want to draw one. `debug_box` by default takes two arguments: a center and bounds vec3f. This will draw a box starting from the point (center - bounds) to (center + bounds). Call the `debug_box` function whenever you want to draw one. `debug_box` by default takes two arguments: a center and bounds vec3f. This will draw a box starting from the point (center - bounds) to (center + bounds).
Use `debug_box_rot` to draw a box rotated in the xz-plane. If you want to draw a box by specifying min and max points, use `debug_box_pos` instead. Use `debug_box_rot` to draw a box rotated in the xz-plane. If you want to draw a box by specifying min and max points, use `debug_box_pos` instead.
## FPS Counter - `fps.patch`
This patch provides an in-game FPS counter to measure the frame rate.
## iQue Player Support - `ique_support.patch` ## iQue Player Support - `ique_support.patch`
This enhancement allows the same ROM to work on both the Nintendo 64 and the iQue Player. This enhancement allows the same ROM to work on both the Nintendo 64 and the iQue Player.

View file

@ -59,11 +59,15 @@
// Convenience macros for endian conversions // Convenience macros for endian conversions
#if IS_BIG_ENDIAN #if IS_BIG_ENDIAN
#define BE_TO_HOST16(x) (x) # define BE_TO_HOST16(x) (x)
#define BE_TO_HOST32(x) (x) # define BE_TO_HOST32(x) (x)
# define LE_TO_HOST16(x) BSWAP16(x)
# define LE_TO_HOST32(x) BSWAP32(x)
#else #else
#define BE_TO_HOST16(x) BSWAP16(x) # define BE_TO_HOST16(x) BSWAP16(x)
#define BE_TO_HOST32(x) BSWAP32(x) # define BE_TO_HOST32(x) BSWAP32(x)
# define LE_TO_HOST16(x) (x)
# define LE_TO_HOST32(x) (x)
#endif #endif
#endif #endif

724
include/tinfl.h Normal file
View file

@ -0,0 +1,724 @@
/* tinfl.c v1.11 - public domain inflate with zlib header parsing/adler32 checking (inflate-only subset of miniz.c)
See "unlicense" statement at the end of this file.
Rich Geldreich <richgel99@gmail.com>, last updated May 20, 2011
Implements RFC 1950: https://www.ietf.org/rfc/rfc1950.txt and RFC 1951: https://www.ietf.org/rfc/rfc1951.txt
The entire decompressor coroutine is implemented in tinfl_decompress(). The other functions are optional high-level helpers.
*/
#ifndef TINFL_HEADER_INCLUDED
#define TINFL_HEADER_INCLUDED
#include <stdint.h>
typedef uint8_t mz_uint8;
typedef int16_t mz_int16;
typedef uint16_t mz_uint16;
typedef uint32_t mz_uint32;
typedef unsigned int mz_uint;
typedef uint64_t mz_uint64;
/* For more compatibility with zlib, miniz.c uses unsigned long for some parameters/struct members. */
typedef unsigned long mz_ulong;
/* Heap allocation callbacks. */
typedef void *(*mz_alloc_func)(void *opaque, unsigned int items, unsigned int size);
typedef void (*mz_free_func)(void *opaque, void *address);
#if defined(_M_IX86) || defined(_M_X64)
/* Set MINIZ_USE_UNALIGNED_LOADS_AND_STORES to 1 if integer loads and stores to unaligned addresses are acceptable on the target platform (slightly faster). */
#define MINIZ_USE_UNALIGNED_LOADS_AND_STORES 1
/* Set MINIZ_LITTLE_ENDIAN to 1 if the processor is little endian. */
#define MINIZ_LITTLE_ENDIAN 1
#endif
#if defined(_WIN64) || defined(__MINGW64__) || defined(_LP64) || defined(__LP64__)
/* Set MINIZ_HAS_64BIT_REGISTERS to 1 if the processor has 64-bit general purpose registers (enables 64-bit bitbuffer in inflator) */
#define MINIZ_HAS_64BIT_REGISTERS 1
#endif
/* Works around MSVC's spammy "warning C4127: conditional expression is constant" message. */
#ifdef _MSC_VER
#define MZ_MACRO_END while (0, 0)
#else
#define MZ_MACRO_END while (0)
#endif
/* Decompression flags. */
enum
{
TINFL_FLAG_PARSE_ZLIB_HEADER = 1,
TINFL_FLAG_HAS_MORE_INPUT = 2,
TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF = 4,
TINFL_FLAG_COMPUTE_ADLER32 = 8
};
struct tinfl_decompressor_tag; typedef struct tinfl_decompressor_tag tinfl_decompressor;
/* Max size of LZ dictionary. */
#define TINFL_LZ_DICT_SIZE 32768
/* Return status. */
typedef enum
{
TINFL_STATUS_BAD_PARAM = -3,
TINFL_STATUS_ADLER32_MISMATCH = -2,
TINFL_STATUS_FAILED = -1,
TINFL_STATUS_DONE = 0,
TINFL_STATUS_NEEDS_MORE_INPUT = 1,
TINFL_STATUS_HAS_MORE_OUTPUT = 2
} tinfl_status;
/* Initializes the decompressor to its initial state. */
#define tinfl_init(r) do { (r)->m_state = 0; } MZ_MACRO_END
#define tinfl_get_adler32(r) (r)->m_check_adler32
/* Main low-level decompressor coroutine function. This is the only function actually needed for decompression. All the other functions are just high-level helpers for improved usability. */
/* This is a universal API, i.e. it can be used as a building block to build any desired higher level decompression API. In the limit case, it can be called once per every byte input or output. */
static tinfl_status tinfl_decompress(tinfl_decompressor *r, const mz_uint8 *pIn_buf_next, size_t *pIn_buf_size, mz_uint8 *pOut_buf_start, mz_uint8 *pOut_buf_next, size_t *pOut_buf_size, const mz_uint32 decomp_flags);
/* Internal/private bits follow. */
enum
{
TINFL_MAX_HUFF_TABLES = 3, TINFL_MAX_HUFF_SYMBOLS_0 = 288, TINFL_MAX_HUFF_SYMBOLS_1 = 32, TINFL_MAX_HUFF_SYMBOLS_2 = 19,
TINFL_FAST_LOOKUP_BITS = 10, TINFL_FAST_LOOKUP_SIZE = 1 << TINFL_FAST_LOOKUP_BITS
};
typedef struct
{
mz_uint8 m_code_size[TINFL_MAX_HUFF_SYMBOLS_0];
mz_int16 m_look_up[TINFL_FAST_LOOKUP_SIZE], m_tree[TINFL_MAX_HUFF_SYMBOLS_0 * 2];
} tinfl_huff_table;
#if MINIZ_HAS_64BIT_REGISTERS
#define TINFL_USE_64BIT_BITBUF 1
#endif
#if TINFL_USE_64BIT_BITBUF
typedef mz_uint64 tinfl_bit_buf_t;
#define TINFL_BITBUF_SIZE (64)
#else
typedef mz_uint32 tinfl_bit_buf_t;
#define TINFL_BITBUF_SIZE (32)
#endif
struct tinfl_decompressor_tag
{
mz_uint32 m_state, m_num_bits, m_zhdr0, m_zhdr1, m_z_adler32, m_final, m_type, m_check_adler32, m_dist, m_counter, m_num_extra, m_table_sizes[TINFL_MAX_HUFF_TABLES];
tinfl_bit_buf_t m_bit_buf;
size_t m_dist_from_out_buf_start;
tinfl_huff_table m_tables[TINFL_MAX_HUFF_TABLES];
mz_uint8 m_raw_header[4], m_len_codes[TINFL_MAX_HUFF_SYMBOLS_0 + TINFL_MAX_HUFF_SYMBOLS_1 + 137];
};
#endif /* #ifdef TINFL_HEADER_INCLUDED */
/* ------------------- End of Header: Implementation follows. (If you only want the header, define MINIZ_HEADER_FILE_ONLY.) */
#ifndef TINFL_HEADER_FILE_ONLY
#ifdef MINIZ_NO_MALLOC
#define MZ_MALLOC(x) NULL
#define MZ_FREE(x) x, ((void)0)
#define MZ_REALLOC(p, x) NULL
#else
#define MZ_MALLOC(x) malloc(x)
#define MZ_FREE(x) free(x)
#define MZ_REALLOC(p, x) realloc(p, x)
#endif
#define MZ_MAX(a,b) (((a)>(b))?(a):(b))
#define MZ_MIN(a,b) (((a)<(b))?(a):(b))
#define MZ_CLEAR_OBJ(obj) memset(&(obj), 0, sizeof(obj))
#if MINIZ_USE_UNALIGNED_LOADS_AND_STORES && MINIZ_LITTLE_ENDIAN
#define MZ_READ_LE16(p) *((const mz_uint16 *)(p))
#define MZ_READ_LE32(p) *((const mz_uint32 *)(p))
#else
#define MZ_READ_LE16(p) ((mz_uint32)(((const mz_uint8 *)(p))[0]) | ((mz_uint32)(((const mz_uint8 *)(p))[1]) << 8U))
#define MZ_READ_LE32(p) ((mz_uint32)(((const mz_uint8 *)(p))[0]) | ((mz_uint32)(((const mz_uint8 *)(p))[1]) << 8U) | ((mz_uint32)(((const mz_uint8 *)(p))[2]) << 16U) | ((mz_uint32)(((const mz_uint8 *)(p))[3]) << 24U))
#endif
#define TINFL_MEMCPY(d, s, l) memcpy(d, s, l)
#define TINFL_MEMSET(p, c, l) memset(p, c, l)
#define TINFL_CR_BEGIN switch(r->m_state) { case 0:
#define TINFL_CR_RETURN(state_index, result) do { status = result; r->m_state = state_index; goto common_exit; case state_index:; } MZ_MACRO_END
#define TINFL_CR_RETURN_FOREVER(state_index, result) do { for ( ; ; ) { TINFL_CR_RETURN(state_index, result); } } MZ_MACRO_END
#define TINFL_CR_FINISH }
/* TODO: If the caller has indicated that there's no more input, and we attempt to read beyond the input buf, then something is wrong with the input because the inflator never */
/* reads ahead more than it needs to. Currently TINFL_GET_BYTE() pads the end of the stream with 0's in this scenario. */
#define TINFL_GET_BYTE(state_index, c) do { \
if (pIn_buf_cur >= pIn_buf_end) { \
for ( ; ; ) { \
if (decomp_flags & TINFL_FLAG_HAS_MORE_INPUT) { \
TINFL_CR_RETURN(state_index, TINFL_STATUS_NEEDS_MORE_INPUT); \
if (pIn_buf_cur < pIn_buf_end) { \
c = *pIn_buf_cur++; \
break; \
} \
} else { \
c = 0; \
break; \
} \
} \
} else c = *pIn_buf_cur++; } MZ_MACRO_END
#define TINFL_NEED_BITS(state_index, n) do { mz_uint c; TINFL_GET_BYTE(state_index, c); bit_buf |= (((tinfl_bit_buf_t)c) << num_bits); num_bits += 8; } while (num_bits < (mz_uint)(n))
#define TINFL_SKIP_BITS(state_index, n) do { if (num_bits < (mz_uint)(n)) { TINFL_NEED_BITS(state_index, n); } bit_buf >>= (n); num_bits -= (n); } MZ_MACRO_END
#define TINFL_GET_BITS(state_index, b, n) do { if (num_bits < (mz_uint)(n)) { TINFL_NEED_BITS(state_index, n); } b = bit_buf & ((1 << (n)) - 1); bit_buf >>= (n); num_bits -= (n); } MZ_MACRO_END
/* TINFL_HUFF_BITBUF_FILL() is only used rarely, when the number of bytes remaining in the input buffer falls below 2. */
/* It reads just enough bytes from the input stream that are needed to decode the next Huffman code (and absolutely no more). It works by trying to fully decode a */
/* Huffman code by using whatever bits are currently present in the bit buffer. If this fails, it reads another byte, and tries again until it succeeds or until the */
/* bit buffer contains >=15 bits (deflate's max. Huffman code size). */
#define TINFL_HUFF_BITBUF_FILL(state_index, pHuff) \
do { \
temp = (pHuff)->m_look_up[bit_buf & (TINFL_FAST_LOOKUP_SIZE - 1)]; \
if (temp >= 0) { \
code_len = temp >> 9; \
if ((code_len) && (num_bits >= code_len)) \
break; \
} else if (num_bits > TINFL_FAST_LOOKUP_BITS) { \
code_len = TINFL_FAST_LOOKUP_BITS; \
do { \
temp = (pHuff)->m_tree[~temp + ((bit_buf >> code_len++) & 1)]; \
} while ((temp < 0) && (num_bits >= (code_len + 1))); if (temp >= 0) break; \
} TINFL_GET_BYTE(state_index, c); bit_buf |= (((tinfl_bit_buf_t)c) << num_bits); num_bits += 8; \
} while (num_bits < 15);
/* TINFL_HUFF_DECODE() decodes the next Huffman coded symbol. It's more complex than you would initially expect because the zlib API expects the decompressor to never read */
/* beyond the final byte of the deflate stream. (In other words, when this macro wants to read another byte from the input, it REALLY needs another byte in order to fully */
/* decode the next Huffman code.) Handling this properly is particularly important on raw deflate (non-zlib) streams, which aren't followed by a byte aligned adler-32. */
/* The slow path is only executed at the very end of the input buffer. */
#define TINFL_HUFF_DECODE(state_index, sym, pHuff) do { \
int temp; mz_uint code_len, c; \
if (num_bits < 15) { \
if ((pIn_buf_end - pIn_buf_cur) < 2) { \
TINFL_HUFF_BITBUF_FILL(state_index, pHuff); \
} else { \
bit_buf |= (((tinfl_bit_buf_t)pIn_buf_cur[0]) << num_bits) | (((tinfl_bit_buf_t)pIn_buf_cur[1]) << (num_bits + 8)); pIn_buf_cur += 2; num_bits += 16; \
} \
} \
if ((temp = (pHuff)->m_look_up[bit_buf & (TINFL_FAST_LOOKUP_SIZE - 1)]) >= 0) \
code_len = temp >> 9, temp &= 511; \
else { \
code_len = TINFL_FAST_LOOKUP_BITS; do { temp = (pHuff)->m_tree[~temp + ((bit_buf >> code_len++) & 1)]; } while (temp < 0); \
} sym = temp; bit_buf >>= code_len; num_bits -= code_len; } MZ_MACRO_END
static void tinfl_def_free_func(void *opaque, void *address) {
(void)opaque, (void)address;
MZ_FREE(address);
}
static void *tinfl_def_alloc_func(void *opaque, unsigned int items, unsigned int size) {
(void)opaque, (void)items, (void)size;
return MZ_MALLOC(items * size);
}
static tinfl_status tinfl_decompress(tinfl_decompressor *r, const mz_uint8 *pIn_buf_next, size_t *pIn_buf_size, mz_uint8 *pOut_buf_start, mz_uint8 *pOut_buf_next, size_t *pOut_buf_size, const mz_uint32 decomp_flags)
{
static const int s_length_base[31] = { 3,4,5,6,7,8,9,10,11,13, 15,17,19,23,27,31,35,43,51,59, 67,83,99,115,131,163,195,227,258,0,0 };
static const int s_length_extra[31]= { 0,0,0,0,0,0,0,0,1,1,1,1,2,2,2,2,3,3,3,3,4,4,4,4,5,5,5,5,0,0,0 };
static const int s_dist_base[32] = { 1,2,3,4,5,7,9,13,17,25,33,49,65,97,129,193, 257,385,513,769,1025,1537,2049,3073,4097,6145,8193,12289,16385,24577,0,0};
static const int s_dist_extra[32] = { 0,0,0,0,1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,10,10,11,11,12,12,13,13};
static const mz_uint8 s_length_dezigzag[19] = { 16,17,18,0,8,7,9,6,10,5,11,4,12,3,13,2,14,1,15 };
static const int s_min_table_sizes[3] = { 257, 1, 4 };
tinfl_status status = TINFL_STATUS_FAILED; mz_uint32 num_bits, dist, counter, num_extra; tinfl_bit_buf_t bit_buf;
const mz_uint8 *pIn_buf_cur = pIn_buf_next, *const pIn_buf_end = pIn_buf_next + *pIn_buf_size;
mz_uint8 *pOut_buf_cur = pOut_buf_next, *const pOut_buf_end = pOut_buf_next + *pOut_buf_size;
size_t out_buf_size_mask = (decomp_flags & TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF) ? (size_t)-1 : ((pOut_buf_next - pOut_buf_start) + *pOut_buf_size) - 1, dist_from_out_buf_start;
/* Ensure the output buffer's size is a power of 2, unless the output buffer is large enough to hold the entire output file (in which case it doesn't matter). */
if (((out_buf_size_mask + 1) & out_buf_size_mask) || (pOut_buf_next < pOut_buf_start)) { *pIn_buf_size = *pOut_buf_size = 0; return TINFL_STATUS_BAD_PARAM; }
num_bits = r->m_num_bits; bit_buf = r->m_bit_buf; dist = r->m_dist; counter = r->m_counter; num_extra = r->m_num_extra; dist_from_out_buf_start = r->m_dist_from_out_buf_start;
TINFL_CR_BEGIN
bit_buf = num_bits = dist = counter = num_extra = r->m_zhdr0 = r->m_zhdr1 = 0; r->m_z_adler32 = r->m_check_adler32 = 1;
if (decomp_flags & TINFL_FLAG_PARSE_ZLIB_HEADER)
{
TINFL_GET_BYTE(1, r->m_zhdr0); TINFL_GET_BYTE(2, r->m_zhdr1);
counter = (((r->m_zhdr0 * 256 + r->m_zhdr1) % 31 != 0) || (r->m_zhdr1 & 32) || ((r->m_zhdr0 & 15) != 8));
if (!(decomp_flags & TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF)) counter |= (((1U << (8U + (r->m_zhdr0 >> 4))) > 32768U) || ((out_buf_size_mask + 1) < (size_t)(1U << (8U + (r->m_zhdr0 >> 4)))));
if (counter) { TINFL_CR_RETURN_FOREVER(36, TINFL_STATUS_FAILED); }
}
do
{
TINFL_GET_BITS(3, r->m_final, 3); r->m_type = r->m_final >> 1;
if (r->m_type == 0)
{
TINFL_SKIP_BITS(5, num_bits & 7);
for (counter = 0; counter < 4; ++counter) { if (num_bits) TINFL_GET_BITS(6, r->m_raw_header[counter], 8); else TINFL_GET_BYTE(7, r->m_raw_header[counter]); }
if ((counter = (r->m_raw_header[0] | (r->m_raw_header[1] << 8))) != (mz_uint)(0xFFFF ^ (r->m_raw_header[2] | (r->m_raw_header[3] << 8)))) { TINFL_CR_RETURN_FOREVER(39, TINFL_STATUS_FAILED); }
while ((counter) && (num_bits))
{
TINFL_GET_BITS(51, dist, 8);
while (pOut_buf_cur >= pOut_buf_end) { TINFL_CR_RETURN(52, TINFL_STATUS_HAS_MORE_OUTPUT); }
*pOut_buf_cur++ = (mz_uint8)dist;
counter--;
}
while (counter)
{
size_t n; while (pOut_buf_cur >= pOut_buf_end) { TINFL_CR_RETURN(9, TINFL_STATUS_HAS_MORE_OUTPUT); }
while (pIn_buf_cur >= pIn_buf_end)
{
if (decomp_flags & TINFL_FLAG_HAS_MORE_INPUT)
{
TINFL_CR_RETURN(38, TINFL_STATUS_NEEDS_MORE_INPUT);
}
else
{
TINFL_CR_RETURN_FOREVER(40, TINFL_STATUS_FAILED);
}
}
n = MZ_MIN(MZ_MIN((size_t)(pOut_buf_end - pOut_buf_cur), (size_t)(pIn_buf_end - pIn_buf_cur)), counter);
TINFL_MEMCPY(pOut_buf_cur, pIn_buf_cur, n); pIn_buf_cur += n; pOut_buf_cur += n; counter -= (mz_uint)n;
}
}
else if (r->m_type == 3)
{
TINFL_CR_RETURN_FOREVER(10, TINFL_STATUS_FAILED);
}
else
{
if (r->m_type == 1)
{
mz_uint8 *p = r->m_tables[0].m_code_size; mz_uint i;
r->m_table_sizes[0] = 288; r->m_table_sizes[1] = 32; TINFL_MEMSET(r->m_tables[1].m_code_size, 5, 32);
for ( i = 0; i <= 143; ++i) *p++ = 8;
for ( ; i <= 255; ++i) *p++ = 9;
for ( ; i <= 279; ++i) *p++ = 7;
for ( ; i <= 287; ++i) *p++ = 8;
}
else
{
for (counter = 0; counter < 3; counter++) { TINFL_GET_BITS(11, r->m_table_sizes[counter], "\05\05\04"[counter]); r->m_table_sizes[counter] += s_min_table_sizes[counter]; }
MZ_CLEAR_OBJ(r->m_tables[2].m_code_size); for (counter = 0; counter < r->m_table_sizes[2]; counter++) { mz_uint s; TINFL_GET_BITS(14, s, 3); r->m_tables[2].m_code_size[s_length_dezigzag[counter]] = (mz_uint8)s; }
r->m_table_sizes[2] = 19;
}
for ( ; (int)r->m_type >= 0; r->m_type--)
{
int tree_next, tree_cur; tinfl_huff_table *pTable;
mz_uint i, j, used_syms, total, sym_index, next_code[17], total_syms[16]; pTable = &r->m_tables[r->m_type]; MZ_CLEAR_OBJ(total_syms); MZ_CLEAR_OBJ(pTable->m_look_up); MZ_CLEAR_OBJ(pTable->m_tree);
for (i = 0; i < r->m_table_sizes[r->m_type]; ++i) total_syms[pTable->m_code_size[i]]++;
used_syms = 0, total = 0; next_code[0] = next_code[1] = 0;
for (i = 1; i <= 15; ++i) { used_syms += total_syms[i]; next_code[i + 1] = (total = ((total + total_syms[i]) << 1)); }
if ((65536 != total) && (used_syms > 1))
{
TINFL_CR_RETURN_FOREVER(35, TINFL_STATUS_FAILED);
}
for (tree_next = -1, sym_index = 0; sym_index < r->m_table_sizes[r->m_type]; ++sym_index)
{
mz_uint rev_code = 0, l, cur_code, code_size = pTable->m_code_size[sym_index]; if (!code_size) continue;
cur_code = next_code[code_size]++; for (l = code_size; l > 0; l--, cur_code >>= 1) rev_code = (rev_code << 1) | (cur_code & 1);
if (code_size <= TINFL_FAST_LOOKUP_BITS) { mz_int16 k = (mz_int16)((code_size << 9) | sym_index); while (rev_code < TINFL_FAST_LOOKUP_SIZE) { pTable->m_look_up[rev_code] = k; rev_code += (1 << code_size); } continue; }
if (0 == (tree_cur = pTable->m_look_up[rev_code & (TINFL_FAST_LOOKUP_SIZE - 1)])) { pTable->m_look_up[rev_code & (TINFL_FAST_LOOKUP_SIZE - 1)] = (mz_int16)tree_next; tree_cur = tree_next; tree_next -= 2; }
rev_code >>= (TINFL_FAST_LOOKUP_BITS - 1);
for (j = code_size; j > (TINFL_FAST_LOOKUP_BITS + 1); j--)
{
tree_cur -= ((rev_code >>= 1) & 1);
if (!pTable->m_tree[-tree_cur - 1]) { pTable->m_tree[-tree_cur - 1] = (mz_int16)tree_next; tree_cur = tree_next; tree_next -= 2; } else tree_cur = pTable->m_tree[-tree_cur - 1];
}
tree_cur -= ((rev_code >>= 1) & 1); pTable->m_tree[-tree_cur - 1] = (mz_int16)sym_index;
}
if (r->m_type == 2)
{
for (counter = 0; counter < (r->m_table_sizes[0] + r->m_table_sizes[1]); )
{
mz_uint s; TINFL_HUFF_DECODE(16, dist, &r->m_tables[2]); if (dist < 16) { r->m_len_codes[counter++] = (mz_uint8)dist; continue; }
if ((dist == 16) && (!counter))
{
TINFL_CR_RETURN_FOREVER(17, TINFL_STATUS_FAILED);
}
num_extra = "\02\03\07"[dist - 16]; TINFL_GET_BITS(18, s, num_extra); s += "\03\03\013"[dist - 16];
TINFL_MEMSET(r->m_len_codes + counter, (dist == 16) ? r->m_len_codes[counter - 1] : 0, s); counter += s;
}
if ((r->m_table_sizes[0] + r->m_table_sizes[1]) != counter)
{
TINFL_CR_RETURN_FOREVER(21, TINFL_STATUS_FAILED);
}
TINFL_MEMCPY(r->m_tables[0].m_code_size, r->m_len_codes, r->m_table_sizes[0]); TINFL_MEMCPY(r->m_tables[1].m_code_size, r->m_len_codes + r->m_table_sizes[0], r->m_table_sizes[1]);
}
}
for ( ; ; )
{
mz_uint8 *pSrc;
for ( ; ; )
{
if (((pIn_buf_end - pIn_buf_cur) < 4) || ((pOut_buf_end - pOut_buf_cur) < 2))
{
TINFL_HUFF_DECODE(23, counter, &r->m_tables[0]);
if (counter >= 256)
break;
while (pOut_buf_cur >= pOut_buf_end) { TINFL_CR_RETURN(24, TINFL_STATUS_HAS_MORE_OUTPUT); }
*pOut_buf_cur++ = (mz_uint8)counter;
}
else
{
int sym2; mz_uint code_len;
#if TINFL_USE_64BIT_BITBUF
if (num_bits < 30) { bit_buf |= (((tinfl_bit_buf_t)MZ_READ_LE32(pIn_buf_cur)) << num_bits); pIn_buf_cur += 4; num_bits += 32; }
#else
if (num_bits < 15) { bit_buf |= (((tinfl_bit_buf_t)MZ_READ_LE16(pIn_buf_cur)) << num_bits); pIn_buf_cur += 2; num_bits += 16; }
#endif
if ((sym2 = r->m_tables[0].m_look_up[bit_buf & (TINFL_FAST_LOOKUP_SIZE - 1)]) >= 0)
code_len = sym2 >> 9;
else
{
code_len = TINFL_FAST_LOOKUP_BITS; do { sym2 = r->m_tables[0].m_tree[~sym2 + ((bit_buf >> code_len++) & 1)]; } while (sym2 < 0);
}
counter = sym2; bit_buf >>= code_len; num_bits -= code_len;
if (counter & 256)
break;
#if !TINFL_USE_64BIT_BITBUF
if (num_bits < 15) { bit_buf |= (((tinfl_bit_buf_t)MZ_READ_LE16(pIn_buf_cur)) << num_bits); pIn_buf_cur += 2; num_bits += 16; }
#endif
if ((sym2 = r->m_tables[0].m_look_up[bit_buf & (TINFL_FAST_LOOKUP_SIZE - 1)]) >= 0)
code_len = sym2 >> 9;
else
{
code_len = TINFL_FAST_LOOKUP_BITS; do { sym2 = r->m_tables[0].m_tree[~sym2 + ((bit_buf >> code_len++) & 1)]; } while (sym2 < 0);
}
bit_buf >>= code_len; num_bits -= code_len;
pOut_buf_cur[0] = (mz_uint8)counter;
if (sym2 & 256)
{
pOut_buf_cur++;
counter = sym2;
break;
}
pOut_buf_cur[1] = (mz_uint8)sym2;
pOut_buf_cur += 2;
}
}
if ((counter &= 511) == 256) break;
num_extra = s_length_extra[counter - 257]; counter = s_length_base[counter - 257];
if (num_extra) { mz_uint extra_bits; TINFL_GET_BITS(25, extra_bits, num_extra); counter += extra_bits; }
TINFL_HUFF_DECODE(26, dist, &r->m_tables[1]);
num_extra = s_dist_extra[dist]; dist = s_dist_base[dist];
if (num_extra) { mz_uint extra_bits; TINFL_GET_BITS(27, extra_bits, num_extra); dist += extra_bits; }
dist_from_out_buf_start = pOut_buf_cur - pOut_buf_start;
if ((dist > dist_from_out_buf_start) && (decomp_flags & TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF))
{
TINFL_CR_RETURN_FOREVER(37, TINFL_STATUS_FAILED);
}
pSrc = pOut_buf_start + ((dist_from_out_buf_start - dist) & out_buf_size_mask);
if ((MZ_MAX(pOut_buf_cur, pSrc) + counter) > pOut_buf_end)
{
while (counter--)
{
while (pOut_buf_cur >= pOut_buf_end) { TINFL_CR_RETURN(53, TINFL_STATUS_HAS_MORE_OUTPUT); }
*pOut_buf_cur++ = pOut_buf_start[(dist_from_out_buf_start++ - dist) & out_buf_size_mask];
}
continue;
}
#if MINIZ_USE_UNALIGNED_LOADS_AND_STORES
else if ((counter >= 9) && (counter <= dist))
{
const mz_uint8 *pSrc_end = pSrc + (counter & ~7);
do
{
((mz_uint32 *)pOut_buf_cur)[0] = ((const mz_uint32 *)pSrc)[0];
((mz_uint32 *)pOut_buf_cur)[1] = ((const mz_uint32 *)pSrc)[1];
pOut_buf_cur += 8;
} while ((pSrc += 8) < pSrc_end);
if ((counter &= 7) < 3)
{
if (counter)
{
pOut_buf_cur[0] = pSrc[0];
if (counter > 1)
pOut_buf_cur[1] = pSrc[1];
pOut_buf_cur += counter;
}
continue;
}
}
#endif
do
{
pOut_buf_cur[0] = pSrc[0];
pOut_buf_cur[1] = pSrc[1];
pOut_buf_cur[2] = pSrc[2];
pOut_buf_cur += 3; pSrc += 3;
} while ((int)(counter -= 3) > 2);
if ((int)counter > 0)
{
pOut_buf_cur[0] = pSrc[0];
if ((int)counter > 1)
pOut_buf_cur[1] = pSrc[1];
pOut_buf_cur += counter;
}
}
}
} while (!(r->m_final & 1));
if (decomp_flags & TINFL_FLAG_PARSE_ZLIB_HEADER)
{
TINFL_SKIP_BITS(32, num_bits & 7); for (counter = 0; counter < 4; ++counter) { mz_uint s; if (num_bits) TINFL_GET_BITS(41, s, 8); else TINFL_GET_BYTE(42, s); r->m_z_adler32 = (r->m_z_adler32 << 8) | s; }
}
TINFL_CR_RETURN_FOREVER(34, TINFL_STATUS_DONE);
TINFL_CR_FINISH
common_exit:
r->m_num_bits = num_bits; r->m_bit_buf = bit_buf; r->m_dist = dist; r->m_counter = counter; r->m_num_extra = num_extra; r->m_dist_from_out_buf_start = dist_from_out_buf_start;
*pIn_buf_size = pIn_buf_cur - pIn_buf_next; *pOut_buf_size = pOut_buf_cur - pOut_buf_next;
if ((decomp_flags & (TINFL_FLAG_PARSE_ZLIB_HEADER | TINFL_FLAG_COMPUTE_ADLER32)) && (status >= 0))
{
const mz_uint8 *ptr = pOut_buf_next; size_t buf_len = *pOut_buf_size;
mz_uint32 i, s1 = r->m_check_adler32 & 0xffff, s2 = r->m_check_adler32 >> 16; size_t block_len = buf_len % 5552;
while (buf_len)
{
for (i = 0; i + 7 < block_len; i += 8, ptr += 8)
{
s1 += ptr[0], s2 += s1; s1 += ptr[1], s2 += s1; s1 += ptr[2], s2 += s1; s1 += ptr[3], s2 += s1;
s1 += ptr[4], s2 += s1; s1 += ptr[5], s2 += s1; s1 += ptr[6], s2 += s1; s1 += ptr[7], s2 += s1;
}
for ( ; i < block_len; ++i) s1 += *ptr++, s2 += s1;
s1 %= 65521U, s2 %= 65521U; buf_len -= block_len; block_len = 5552;
}
r->m_check_adler32 = (s2 << 16) + s1; if ((status == TINFL_STATUS_DONE) && (decomp_flags & TINFL_FLAG_PARSE_ZLIB_HEADER) && (r->m_check_adler32 != r->m_z_adler32)) status = TINFL_STATUS_ADLER32_MISMATCH;
}
return status;
}
/* Flush values. For typical usage you only need MZ_NO_FLUSH and MZ_FINISH. The other stuff is for advanced use. */
enum { MZ_NO_FLUSH = 0, MZ_PARTIAL_FLUSH = 1, MZ_SYNC_FLUSH = 2, MZ_FULL_FLUSH = 3, MZ_FINISH = 4, MZ_BLOCK = 5 };
/* Return status codes. MZ_PARAM_ERROR is non-standard. */
enum { MZ_OK = 0, MZ_STREAM_END = 1, MZ_NEED_DICT = 2, MZ_ERRNO = -1, MZ_STREAM_ERROR = -2, MZ_DATA_ERROR = -3, MZ_MEM_ERROR = -4, MZ_BUF_ERROR = -5, MZ_VERSION_ERROR = -6, MZ_PARAM_ERROR = -10000 };
/* Compression levels. */
enum { MZ_NO_COMPRESSION = 0, MZ_BEST_SPEED = 1, MZ_BEST_COMPRESSION = 9, MZ_DEFAULT_COMPRESSION = -1 };
/* Window bits */
#define MZ_DEFAULT_WINDOW_BITS 15
struct mz_internal_state;
/* Compression/decompression stream struct. */
typedef struct mz_stream_s
{
const unsigned char *next_in; /* pointer to next byte to read */
unsigned int avail_in; /* number of bytes available at next_in */
mz_ulong total_in; /* total number of bytes consumed so far */
unsigned char *next_out; /* pointer to next byte to write */
unsigned int avail_out; /* number of bytes that can be written to next_out */
mz_ulong total_out; /* total number of bytes produced so far */
char *msg; /* error msg (unused) */
struct mz_internal_state *state; /* internal state, allocated by zalloc/zfree */
mz_alloc_func zalloc; /* optional heap allocation function (defaults to malloc) */
mz_free_func zfree; /* optional heap free function (defaults to free) */
void *opaque; /* heap alloc function user pointer */
int data_type; /* data_type (unused) */
mz_ulong adler; /* adler32 of the source or uncompressed data */
mz_ulong reserved; /* not used */
} mz_stream;
typedef mz_stream *mz_streamp;
typedef struct
{
tinfl_decompressor m_decomp;
mz_uint m_dict_ofs, m_dict_avail, m_first_call, m_has_flushed; int m_window_bits;
mz_uint8 m_dict[TINFL_LZ_DICT_SIZE];
tinfl_status m_last_status;
} inflate_state;
static int mz_inflateInit2(mz_streamp pStream, int window_bits)
{
inflate_state *pDecomp;
if (!pStream) return MZ_STREAM_ERROR;
if ((window_bits != MZ_DEFAULT_WINDOW_BITS) && (-window_bits != MZ_DEFAULT_WINDOW_BITS)) return MZ_PARAM_ERROR;
pStream->data_type = 0;
pStream->adler = 0;
pStream->msg = NULL;
pStream->total_in = 0;
pStream->total_out = 0;
pStream->reserved = 0;
if (!pStream->zalloc) pStream->zalloc = tinfl_def_alloc_func;
if (!pStream->zfree) pStream->zfree = tinfl_def_free_func;
pDecomp = (inflate_state*)pStream->zalloc(pStream->opaque, 1, sizeof(inflate_state));
if (!pDecomp) return MZ_MEM_ERROR;
pStream->state = (struct mz_internal_state *)pDecomp;
tinfl_init(&pDecomp->m_decomp);
pDecomp->m_dict_ofs = 0;
pDecomp->m_dict_avail = 0;
pDecomp->m_last_status = TINFL_STATUS_NEEDS_MORE_INPUT;
pDecomp->m_first_call = 1;
pDecomp->m_has_flushed = 0;
pDecomp->m_window_bits = window_bits;
return MZ_OK;
}
static int mz_inflate(mz_streamp pStream, int flush)
{
inflate_state* pState;
mz_uint n, first_call, decomp_flags = TINFL_FLAG_COMPUTE_ADLER32;
size_t in_bytes, out_bytes, orig_avail_in;
tinfl_status status;
if ((!pStream) || (!pStream->state)) return MZ_STREAM_ERROR;
if (flush == MZ_PARTIAL_FLUSH) flush = MZ_SYNC_FLUSH;
if ((flush) && (flush != MZ_SYNC_FLUSH) && (flush != MZ_FINISH)) return MZ_STREAM_ERROR;
pState = (inflate_state*)pStream->state;
if (pState->m_window_bits > 0) decomp_flags |= TINFL_FLAG_PARSE_ZLIB_HEADER;
orig_avail_in = pStream->avail_in;
first_call = pState->m_first_call; pState->m_first_call = 0;
if (pState->m_last_status < 0) return MZ_DATA_ERROR;
if (pState->m_has_flushed && (flush != MZ_FINISH)) return MZ_STREAM_ERROR;
pState->m_has_flushed |= (flush == MZ_FINISH);
if ((flush == MZ_FINISH) && (first_call))
{
/* MZ_FINISH on the first call implies that the input and output buffers are large enough to hold the entire compressed/decompressed file. */
decomp_flags |= TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF;
in_bytes = pStream->avail_in; out_bytes = pStream->avail_out;
status = tinfl_decompress(&pState->m_decomp, pStream->next_in, &in_bytes, pStream->next_out, pStream->next_out, &out_bytes, decomp_flags);
pState->m_last_status = status;
pStream->next_in += (mz_uint)in_bytes; pStream->avail_in -= (mz_uint)in_bytes; pStream->total_in += (mz_uint)in_bytes;
pStream->adler = tinfl_get_adler32(&pState->m_decomp);
pStream->next_out += (mz_uint)out_bytes; pStream->avail_out -= (mz_uint)out_bytes; pStream->total_out += (mz_uint)out_bytes;
if (status < 0)
return MZ_DATA_ERROR;
else if (status != TINFL_STATUS_DONE)
{
pState->m_last_status = TINFL_STATUS_FAILED;
return MZ_BUF_ERROR;
}
return MZ_STREAM_END;
}
/* flush != MZ_FINISH then we must assume there's more input. */
if (flush != MZ_FINISH) decomp_flags |= TINFL_FLAG_HAS_MORE_INPUT;
if (pState->m_dict_avail)
{
n = MZ_MIN(pState->m_dict_avail, pStream->avail_out);
memcpy(pStream->next_out, pState->m_dict + pState->m_dict_ofs, n);
pStream->next_out += n; pStream->avail_out -= n; pStream->total_out += n;
pState->m_dict_avail -= n; pState->m_dict_ofs = (pState->m_dict_ofs + n) & (TINFL_LZ_DICT_SIZE - 1);
return ((pState->m_last_status == TINFL_STATUS_DONE) && (!pState->m_dict_avail)) ? MZ_STREAM_END : MZ_OK;
}
for ( ; ; )
{
in_bytes = pStream->avail_in;
out_bytes = TINFL_LZ_DICT_SIZE - pState->m_dict_ofs;
status = tinfl_decompress(&pState->m_decomp, pStream->next_in, &in_bytes, pState->m_dict, pState->m_dict + pState->m_dict_ofs, &out_bytes, decomp_flags);
pState->m_last_status = status;
pStream->next_in += (mz_uint)in_bytes; pStream->avail_in -= (mz_uint)in_bytes;
pStream->total_in += (mz_uint)in_bytes; pStream->adler = tinfl_get_adler32(&pState->m_decomp);
pState->m_dict_avail = (mz_uint)out_bytes;
n = MZ_MIN(pState->m_dict_avail, pStream->avail_out);
memcpy(pStream->next_out, pState->m_dict + pState->m_dict_ofs, n);
pStream->next_out += n; pStream->avail_out -= n; pStream->total_out += n;
pState->m_dict_avail -= n; pState->m_dict_ofs = (pState->m_dict_ofs + n) & (TINFL_LZ_DICT_SIZE - 1);
if (status < 0)
return MZ_DATA_ERROR; /* Stream is corrupted (there could be some uncompressed data left in the output dictionary - oh well). */
else if ((status == TINFL_STATUS_NEEDS_MORE_INPUT) && (!orig_avail_in))
return MZ_BUF_ERROR; /* Signal caller that we can't make forward progress without supplying more input or by setting flush to MZ_FINISH. */
else if (flush == MZ_FINISH)
{
/* The output buffer MUST be large to hold the remaining uncompressed data when flush==MZ_FINISH. */
if (status == TINFL_STATUS_DONE)
return pState->m_dict_avail ? MZ_BUF_ERROR : MZ_STREAM_END;
/* status here must be TINFL_STATUS_HAS_MORE_OUTPUT, which means there's at least 1 more byte on the way. If there's no more room left in the output buffer then something is wrong. */
else if (!pStream->avail_out)
return MZ_BUF_ERROR;
}
else if ((status == TINFL_STATUS_DONE) || (!pStream->avail_in) || (!pStream->avail_out) || (pState->m_dict_avail))
break;
}
return ((status == TINFL_STATUS_DONE) && (!pState->m_dict_avail)) ? MZ_STREAM_END : MZ_OK;
}
static int mz_inflateEnd(mz_streamp pStream)
{
if (!pStream)
return MZ_STREAM_ERROR;
if (pStream->state)
{
pStream->zfree(pStream->opaque, pStream->state);
pStream->state = NULL;
}
return MZ_OK;
}
/* make this a drop-in replacement for zlib... */
#define voidpf void*
#define uInt unsigned int
#define z_stream mz_stream
#define inflateInit2 mz_inflateInit2
#define inflate mz_inflate
#define inflateEnd mz_inflateEnd
#define Z_SYNC_FLUSH MZ_SYNC_FLUSH
#define Z_FINISH MZ_FINISH
#define Z_OK MZ_OK
#define Z_STREAM_END MZ_STREAM_END
#define Z_NEED_DICT MZ_NEED_DICT
#define Z_ERRNO MZ_ERRNO
#define Z_STREAM_ERROR MZ_STREAM_ERROR
#define Z_DATA_ERROR MZ_DATA_ERROR
#define Z_MEM_ERROR MZ_MEM_ERROR
#define Z_BUF_ERROR MZ_BUF_ERROR
#define Z_VERSION_ERROR MZ_VERSION_ERROR
#define MAX_WBITS 15
#endif /* #ifndef TINFL_HEADER_FILE_ONLY */
/*
This is free and unencumbered software released into the public domain.
Anyone is free to copy, modify, publish, use, compile, sell, or
distribute this software, either in source code form or as a compiled
binary, for any purpose, commercial or non-commercial, and by any
means.
In jurisdictions that recognize copyright laws, the author or authors
of this software dedicate any and all copyright interest in the
software to the public domain. We make this dedication for the benefit
of the public at large and to the detriment of our heirs and
successors. We intend this dedication to be an overt act of
relinquishment in perpetuity of all present and future rights to this
software under copyright law.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
OTHER DEALINGS IN THE SOFTWARE.
For more information, please refer to <https://unlicense.org/>
*/

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View file

@ -1,18 +0,0 @@
.set noreorder # don't insert nops after branches
.set gp=64
.include "macros.inc"
.section .text, "ax"
glabel __osDisableInt
mfc0 $t0, $12
and $t1, $t0, -2
mtc0 $t1, $12
andi $v0, $t0, 1
nop
jr $ra
nop

File diff suppressed because it is too large Load diff

View file

@ -1,15 +0,0 @@
.set noreorder # don't insert nops after branches
.set gp=64
.include "macros.inc"
.section .text, "ax"
glabel __osGetCause
mfc0 $v0, $13
jr $ra
nop
nop

View file

@ -1,15 +0,0 @@
.set noreorder # don't insert nops after branches
.set gp=64
.include "macros.inc"
.section .text, "ax"
glabel __osGetSR
mfc0 $v0, $12
jr $ra
nop
nop

View file

@ -1,64 +0,0 @@
.set noat # allow manual use of $at
.set noreorder # don't insert nops after branches
.set gp=64
.include "macros.inc"
.section .text, "ax"
glabel __osProbeTLB
mfc0 $t0, $10
andi $t1, $t0, 0xff
li $at, -8192
and $t2, $a0, $at
or $t1, $t1, $t2
mtc0 $t1, $10
nop
nop
nop
tlbp
nop
nop
mfc0 $t3, $0
lui $at, 0x8000
and $t3, $t3, $at
bnez $t3, .L8032A0D8
nop
tlbr
nop
nop
nop
mfc0 $t3, $5
addi $t3, $t3, 0x2000
srl $t3, $t3, 1
and $t4, $t3, $a0
bnez $t4, .L8032A0A8
addi $t3, $t3, -1
mfc0 $v0, $2
b .L8032A0AC
nop
.L8032A0A8:
mfc0 $v0, $3
.L8032A0AC:
andi $t5, $v0, 2
beqz $t5, .L8032A0D8
nop
lui $at, (0x3FFFFFC0 >> 16) # lui $at, 0x3fff
ori $at, (0x3FFFFFC0 & 0xFFFF) # ori $at, $at, 0xffc0
and $v0, $v0, $at
sll $v0, $v0, 6
and $t5, $a0, $t3
add $v0, $v0, $t5
b .L8032A0DC
nop
.L8032A0D8:
li $v0, -1
.L8032A0DC:
mtc0 $t0, $10
jr $ra
nop
nop
nop

View file

@ -1,19 +0,0 @@
.set noreorder # don't insert nops after branches
.set gp=64
.include "macros.inc"
.section .text, "ax"
glabel __osRestoreInt
mfc0 $t0, $12
or $t0, $t0, $a0
mtc0 $t0, $12
nop
nop
jr $ra
nop
nop

View file

@ -1,15 +0,0 @@
.set noreorder # don't insert nops after branches
.set gp=64
.include "macros.inc"
.section .text, "ax"
glabel __osSetCompare
mtc0 $a0, $11
jr $ra
nop
nop

View file

@ -1,14 +0,0 @@
.set noreorder # don't insert nops after branches
.set gp=64
.include "macros.inc"
.section .text, "ax"
glabel __osSetFpcCsr
cfc1 $v0, $31
ctc1 $a0, $31
jr $ra
nop

View file

@ -1,14 +0,0 @@
.set noreorder # don't insert nops after branches
.set gp=64
.include "macros.inc"
.section .text, "ax"
glabel __osSetSR
mtc0 $a0, $12
nop
jr $ra
nop

View file

@ -1,22 +0,0 @@
.set noreorder # don't insert nops after branches
.set gp=64
.set noat
.include "macros.inc"
.section .text, "ax"
# cache related
glabel __os_eu_802ef550
lui $t0,0x8000
li $t2,0x2000
addu $t1,$t0,$t2
addiu $t1,$t1,-0x10
.L: cache 0x1,0($t0)
sltu $at,$t0,$t1
bnez $at,.L
addiu $t0,$t0,0x10
jr $ra
nop
nop
nop

View file

@ -1,232 +0,0 @@
.set noat # allow manual use of $at
.set noreorder # don't insert nops after branches
.set gp=64
.include "macros.inc"
.section .text, "ax"
glabel bcopy
beqz $a2, .L80323A4C
move $a3, $a1
beq $a0, $a1, .L80323A4C
slt $at, $a1, $a0
bnezl $at, .L80323A14
slti $at, $a2, 0x10
add $v0, $a0, $a2
slt $at, $a1, $v0
beql $at, $zero, .L80323A14
slti $at, $a2, 0x10
b .L80323B78
slti $at, $a2, 0x10
slti $at, $a2, 0x10
.L80323A14:
bnez $at, .L80323A2C
nop
andi $v0, $a0, 3
andi $v1, $a1, 3
beq $v0, $v1, .L80323A54
nop
.L80323A2C:
beqz $a2, .L80323A4C
nop
addu $v1, $a0, $a2
.L80323A38:
lb $v0, ($a0)
addiu $a0, $a0, 1
addiu $a1, $a1, 1
bne $a0, $v1, .L80323A38
sb $v0, -1($a1)
.L80323A4C:
jr $ra
move $v0, $a3
.L80323A54:
beqz $v0, .L80323AB8
li $at, 1
beq $v0, $at, .L80323A9C
li $at, 2
beql $v0, $at, .L80323A88
lh $v0, ($a0)
lb $v0, ($a0)
addiu $a0, $a0, 1
addiu $a1, $a1, 1
addiu $a2, $a2, -1
b .L80323AB8
sb $v0, -1($a1)
lh $v0, ($a0)
.L80323A88:
addiu $a0, $a0, 2
addiu $a1, $a1, 2
addiu $a2, $a2, -2
b .L80323AB8
sh $v0, -2($a1)
.L80323A9C:
lb $v0, ($a0)
lh $v1, 1($a0)
addiu $a0, $a0, 3
addiu $a1, $a1, 3
addiu $a2, $a2, -3
sb $v0, -3($a1)
sh $v1, -2($a1)
.L80323AB8:
slti $at, $a2, 0x20
bnezl $at, .L80323B18
slti $at, $a2, 0x10
lw $v0, ($a0)
lw $v1, 4($a0)
lw $t0, 8($a0)
lw $t1, 0xc($a0)
lw $t2, 0x10($a0)
lw $t3, 0x14($a0)
lw $t4, 0x18($a0)
lw $t5, 0x1c($a0)
addiu $a0, $a0, 0x20
addiu $a1, $a1, 0x20
addiu $a2, $a2, -0x20
sw $v0, -0x20($a1)
sw $v1, -0x1c($a1)
sw $t0, -0x18($a1)
sw $t1, -0x14($a1)
sw $t2, -0x10($a1)
sw $t3, -0xc($a1)
sw $t4, -8($a1)
b .L80323AB8
sw $t5, -4($a1)
.L80323B14:
slti $at, $a2, 0x10
.L80323B18:
bnezl $at, .L80323B54
slti $at, $a2, 4
lw $v0, ($a0)
lw $v1, 4($a0)
lw $t0, 8($a0)
lw $t1, 0xc($a0)
addiu $a0, $a0, 0x10
addiu $a1, $a1, 0x10
addiu $a2, $a2, -0x10
sw $v0, -0x10($a1)
sw $v1, -0xc($a1)
sw $t0, -8($a1)
b .L80323B14
sw $t1, -4($a1)
.L80323B50:
slti $at, $a2, 4
.L80323B54:
bnez $at, .L80323A2C
nop
lw $v0, ($a0)
addiu $a0, $a0, 4
addiu $a1, $a1, 4
addiu $a2, $a2, -4
b .L80323B50
sw $v0, -4($a1)
slti $at, $a2, 0x10
.L80323B78:
add $a0, $a0, $a2
bnez $at, .L80323B94
add $a1, $a1, $a2
andi $v0, $a0, 3
andi $v1, $a1, 3
beq $v0, $v1, .L80323BC4
nop
.L80323B94:
beqz $a2, .L80323A4C
nop
addiu $a0, $a0, -1
addiu $a1, $a1, -1
subu $v1, $a0, $a2
.L80323BA8:
lb $v0, ($a0)
addiu $a0, $a0, -1
addiu $a1, $a1, -1
bne $a0, $v1, .L80323BA8
sb $v0, 1($a1)
jr $ra
move $v0, $a3
.L80323BC4:
beqz $v0, .L80323C28
li $at, 3
beq $v0, $at, .L80323C0C
li $at, 2
beql $v0, $at, .L80323BF8
lh $v0, -2($a0)
lb $v0, -1($a0)
addiu $a0, $a0, -1
addiu $a1, $a1, -1
addiu $a2, $a2, -1
b .L80323C28
sb $v0, ($a1)
lh $v0, -2($a0)
.L80323BF8:
addiu $a0, $a0, -2
addiu $a1, $a1, -2
addiu $a2, $a2, -2
b .L80323C28
sh $v0, ($a1)
.L80323C0C:
lb $v0, -1($a0)
lh $v1, -3($a0)
addiu $a0, $a0, -3
addiu $a1, $a1, -3
addiu $a2, $a2, -3
sb $v0, 2($a1)
sh $v1, ($a1)
.L80323C28:
slti $at, $a2, 0x20
bnezl $at, .L80323C88
slti $at, $a2, 0x10
lw $v0, -4($a0)
lw $v1, -8($a0)
lw $t0, -0xc($a0)
lw $t1, -0x10($a0)
lw $t2, -0x14($a0)
lw $t3, -0x18($a0)
lw $t4, -0x1c($a0)
lw $t5, -0x20($a0)
addiu $a0, $a0, -0x20
addiu $a1, $a1, -0x20
addiu $a2, $a2, -0x20
sw $v0, 0x1c($a1)
sw $v1, 0x18($a1)
sw $t0, 0x14($a1)
sw $t1, 0x10($a1)
sw $t2, 0xc($a1)
sw $t3, 8($a1)
sw $t4, 4($a1)
b .L80323C28
sw $t5, ($a1)
.L80323C84:
slti $at, $a2, 0x10
.L80323C88:
bnezl $at, .L80323CC4
slti $at, $a2, 4
lw $v0, -4($a0)
lw $v1, -8($a0)
lw $t0, -0xc($a0)
lw $t1, -0x10($a0)
addiu $a0, $a0, -0x10
addiu $a1, $a1, -0x10
addiu $a2, $a2, -0x10
sw $v0, 0xc($a1)
sw $v1, 8($a1)
sw $t0, 4($a1)
b .L80323C84
sw $t1, ($a1)
.L80323CC0:
slti $at, $a2, 4
.L80323CC4:
bnez $at, .L80323B94
nop
lw $v0, -4($a0)
addiu $a0, $a0, -4
addiu $a1, $a1, -4
addiu $a2, $a2, -4
b .L80323CC0
sw $v0, ($a1)
nop
nop
nop

View file

@ -1,53 +0,0 @@
.set noreorder # don't insert nops after branches
.set gp=64
.include "macros.inc"
#this file is probably handwritten
.section .text, "ax"
glabel bzero
blt $a1, 0xc, .L803236BC
negu $v1, $a0
andi $v1, $v1, 3
beqz $v1, .L80323660
subu $a1, $a1, $v1
swl $zero, ($a0)
addu $a0, $a0, $v1
.L80323660:
and $a3, $a1, -32
beqz $a3, .L8032369C
subu $a1, $a1, $a3
addu $a3, $a3, $a0
.L80323674:
addiu $a0, $a0, 0x20
sw $zero, -0x20($a0)
sw $zero, -0x1c($a0)
sw $zero, -0x18($a0)
sw $zero, -0x14($a0)
sw $zero, -0x10($a0)
sw $zero, -0xc($a0)
sw $zero, -8($a0)
bne $a0, $a3, .L80323674
sw $zero, -4($a0)
.L8032369C:
and $a3, $a1, -4
beqz $a3, .L803236BC
subu $a1, $a1, $a3
addu $a3, $a3, $a0
.L803236B0:
addiu $a0, $a0, 4
bne $a0, $a3, .L803236B0
sw $zero, -4($a0)
.L803236BC:
blez $a1, .L803236D4
nop
addu $a1, $a1, $a0
.L803236C8:
addiu $a0, $a0, 1
bne $a0, $a1, .L803236C8
sb $zero, -1($a0)
.L803236D4:
jr $ra

View file

@ -1,100 +0,0 @@
# assembler directives
.set noat # allow manual use of $at
.set noreorder # don't insert nops after branches
.set gp=64
.include "macros.inc"
.section .text, "ax"
/* -------------------------------------------------------------------------------------- */
/* need to asm these functions because lib32gcc-7-dev-mips-cross does not exist so we */
/* cannot naturally link a libgcc variant for this target given this architecture and */
/* compiler. Until we have a good workaround with a gcc target that doesn't involve */
/* assuming a 32-bit to 64-bit change, we have to encode these functions as raw assembly */
/* for it to compile. */
/* -------------------------------------------------------------------------------------- */
/* TODO: Is there a non-insane way to fix this hack that doesn't involve the user compiling */
/* a library themselves? */
glabel __umoddi3
sw $a0, ($sp)
sw $a1, 4($sp)
sw $a2, 8($sp)
sw $a3, 0xc($sp)
ld $t7, 8($sp)
ld $t6, ($sp)
ddivu $zero, $t6, $t7
bnez $t7, .L80324144
nop
break 7
.L80324144:
mfhi $v0
dsll32 $v1, $v0, 0
dsra32 $v1, $v1, 0
jr $ra
dsra32 $v0, $v0, 0
glabel __udivdi3
sw $a0, ($sp)
sw $a1, 4($sp)
sw $a2, 8($sp)
sw $a3, 0xc($sp)
ld $t7, 8($sp)
ld $t6, ($sp)
ddivu $zero, $t6, $t7
bnez $t7, .L80324180
nop
break 7
.L80324180:
mflo $v0
dsll32 $v1, $v0, 0
dsra32 $v1, $v1, 0
jr $ra
dsra32 $v0, $v0, 0
glabel __moddi3
sw $a0, ($sp)
sw $a1, 4($sp)
sw $a2, 8($sp)
sw $a3, 0xc($sp)
ld $t7, 8($sp)
ld $t6, ($sp)
ddivu $zero, $t6, $t7
bnez $t7, .L803241E8
nop
break 7
.L803241E8:
mfhi $v0
dsll32 $v1, $v0, 0
dsra32 $v1, $v1, 0
jr $ra
dsra32 $v0, $v0, 0
glabel __divdi3
sw $a0, ($sp)
sw $a1, 4($sp)
sw $a2, 8($sp)
sw $a3, 0xc($sp)
ld $t7, 8($sp)
ld $t6, ($sp)
ddiv $zero, $t6, $t7
nop
bnez $t7, .L80324228
nop
break 7
.L80324228:
daddiu $at, $zero, -1
bne $t7, $at, .L80324244
daddiu $at, $zero, 1
dsll32 $at, $at, 0x1f
bne $t6, $at, .L80324244
nop
break 6
.L80324244:
mflo $v0
dsll32 $v1, $v0, 0
dsra32 $v1, $v1, 0
jr $ra
dsra32 $v0, $v0, 0

View file

@ -1,15 +0,0 @@
.set noreorder # don't insert nops after branches
.set gp=64
.include "macros.inc"
.section .text, "ax"
glabel osGetCount
mfc0 $v0, $9
jr $ra
nop
nop

View file

@ -1,59 +0,0 @@
.set noat # allow manual use of $at
.set noreorder # don't insert nops after branches
.set gp=64
.include "macros.inc"
.section .text, "ax"
glabel osInvalDCache
blez $a1, .L80323500
nop
li $t3, 8192
sltu $at, $a1, $t3
beqz $at, .L80323508
nop
move $t0, $a0
addu $t1, $a0, $a1
sltu $at, $t0, $t1
beqz $at, .L80323500
nop
andi $t2, $t0, 0xf
beqz $t2, .L803234D0
addiu $t1, $t1, -0x10
subu $t0, $t0, $t2
cache 0x15, ($t0)
sltu $at, $t0, $t1
beqz $at, .L80323500
nop
addiu $t0, $t0, 0x10
.L803234D0:
andi $t2, $t1, 0xf
beqz $t2, .L803234F0
nop
subu $t1, $t1, $t2
cache 0x15, 0x10($t1)
sltu $at, $t1, $t0
bnez $at, .L80323500
nop
.L803234F0:
cache 0x11, ($t0)
sltu $at, $t0, $t1
bnez $at, .L803234F0
addiu $t0, $t0, 0x10
.L80323500:
jr $ra
nop
.L80323508:
li $t0, K0BASE
addu $t1, $t0, $t3
addiu $t1, $t1, -0x10
.L80323514:
cache 1, ($t0)
sltu $at, $t0, $t1
bnez $at, .L80323514
addiu $t0, $t0, 0x10
jr $ra
nop

View file

@ -1,44 +0,0 @@
.set noat # allow manual use of $at
.set noreorder # don't insert nops after branches
.set gp=64
.include "macros.inc"
.section .text, "ax"
glabel osInvalICache
blez $a1, .L80323728
nop
li $t3, 16384
sltu $at, $a1, $t3
beqz $at, .L80323730
nop
move $t0, $a0
addu $t1, $a0, $a1
sltu $at, $t0, $t1
beqz $at, .L80323728
nop
andi $t2, $t0, 0x1f
addiu $t1, $t1, -0x20
subu $t0, $t0, $t2
.L80323718:
cache 0x10, ($t0)
sltu $at, $t0, $t1
bnez $at, .L80323718
addiu $t0, $t0, 0x20
.L80323728:
jr $ra
nop
.L80323730:
li $t0, K0BASE
addu $t1, $t0, $t3
addiu $t1, $t1, -0x20
.L8032373C:
cache 0, ($t0)
sltu $at, $t0, $t1
bnez $at, .L8032373C
addiu $t0, $t0, 0x20
jr $ra
nop

View file

@ -1,63 +0,0 @@
.set noreorder # don't insert nops after branches
.set gp=64
.include "macros.inc"
.section .text, "ax"
# This file is handwritten
#void osMapTLB(s32 index, OSPageMask pm, void *vaddr, u32 evenpaddr, u32 oddpaddr, s32 asid);
glabel osMapTLB
mfc0 $t0, $10
mtc0 $a0, $0
mtc0 $a1, $5
lw $t1, 0x14($sp) #asid
beq $t1, -1, .L803214D8
li $t4, 1
li $t2, 30
b .L803214DC
or $a2, $a2, $t1 #vaddr
.L803214D8:
li $t2, 31
.L803214DC:
mtc0 $a2, $10 #vaddr
beq $a3, -1, .L80321500 #even paddr
nop
srl $t3, $a3, 6 #evenpaddr
or $t3, $t3, $t2
mtc0 $t3, $2
b .L80321504
nop
.L80321500:
mtc0 $t4, $2
.L80321504:
lw $t3, 0x10($sp) #oddpaddr
beq $t3, -1, .L80321528
nop
srl $t3, $t3, 6
or $t3, $t3, $t2
mtc0 $t3, $3
b .L80321540
nop
.L80321528:
mtc0 $t4, $3
bne $a3, -1, .L80321540 #evenpaddr
nop
lui $t3, 0x8000
mtc0 $t3, $10
.L80321540:
nop
tlbwi
nop
nop
nop
nop
mtc0 $t0, $10
jr $ra
nop #file gets padded but
nop
nop
nop

View file

@ -1,36 +0,0 @@
.set noat # allow manual use of $at
.set noreorder # don't insert nops after branches
.set gp=64
.include "macros.inc"
.section .text, "ax"
glabel osMapTLBRdb
mfc0 $t0, $10
li $t1, 31
mtc0 $t1, $0
mtc0 $zero, $5
li $t2, 23
lui $t1, 0xc000
mtc0 $t1, $10
lui $t1, 0x8000
srl $t3, $t1, 6
or $t3, $t3, $t2
mtc0 $t3, $2
li $t1, 1
mtc0 $t1, $3
nop
tlbwi
nop
nop
nop
nop
mtc0 $t0, $10
jr $ra
nop
nop
nop

View file

@ -1,138 +0,0 @@
.set noat # allow manual use of $at
.set noreorder # don't insert nops after branches
.set gp=64
.include "macros.inc"
.eqv MI_INTR_MASK_REG, 0xA430000C
.section .text, "ax"
glabel osSetIntMask
.ifndef VERSION_EU
mfc0 $t1, $12
andi $v0, $t1, 0xff01
.else
mfc0 $t4, $12
andi $v0, $t4, 0xff01
lui $t0, %hi(D_8030208C) # $t0, 0x8030
addiu $t0, %lo(D_8030208C) # addiu $t0, $t0, 0x208c
lw $t3, ($t0)
li $at, -1
xor $t0, $t3, $at
andi $t0, $t0, 0xff00
or $v0, $v0, $t0
.endif
lui $t2, %hi(MI_INTR_MASK_REG) # $t2, 0xa430
lw $t2, %lo(MI_INTR_MASK_REG)($t2)
.ifdef VERSION_EU
beqz $t2, .L80200074
srl $t1, $t3, 0x10
li $at, -1
xor $t1, $t1, $at
andi $t1, $t1, 0x3f
or $t2, $t2, $t1
.L80200074:
.endif
sll $t2, $t2, 0x10
or $v0, $v0, $t2
lui $at, 0x3f
and $t0, $a0, $at
.ifdef VERSION_EU
and $t0, $t0, $t3
.endif
srl $t0, $t0, 0xf
lui $t2, %hi(D_803386D0)
addu $t2, $t2, $t0
lhu $t2, %lo(D_803386D0)($t2)
lui $at, %hi(MI_INTR_MASK_REG) # $at, 0xa430
sw $t2, %lo(MI_INTR_MASK_REG)($at)
andi $t0, $a0, 0xff01
.ifdef VERSION_EU
andi $t1, $t3, 0xff00
and $t0, $t0, $t1
.endif
lui $at, (0xFFFF00FF >> 16) # lui $at, 0xffff
ori $at, (0xFFFF00FF & 0xFFFF) # ori $at, $at, 0xff
.ifndef VERSION_EU
and $t1, $t1, $at
or $t1, $t1, $t0
mtc0 $t1, $12
.else
and $t4, $t4, $at
or $t4, $t4, $t0
mtc0 $t4, $12
.endif
nop
nop
jr $ra
nop
.section .rodata
glabel D_803386D0
.half 0x0555
.half 0x0556
.half 0x0559
.half 0x055A
.half 0x0565
.half 0x0566
.half 0x0569
.half 0x056A
.half 0x0595
.half 0x0596
.half 0x0599
.half 0x059A
.half 0x05A5
.half 0x05A6
.half 0x05A9
.half 0x05AA
.half 0x0655
.half 0x0656
.half 0x0659
.half 0x065A
.half 0x0665
.half 0x0666
.half 0x0669
.half 0x066A
.half 0x0695
.half 0x0696
.half 0x0699
.half 0x069A
.half 0x06A5
.half 0x06A6
.half 0x06A9
.half 0x06AA
.half 0x0955
.half 0x0956
.half 0x0959
.half 0x095A
.half 0x0965
.half 0x0966
.half 0x0969
.half 0x096A
.half 0x0995
.half 0x0996
.half 0x0999
.half 0x099A
.half 0x09A5
.half 0x09A6
.half 0x09A9
.half 0x09AA
.half 0x0A55
.half 0x0A56
.half 0x0A59
.half 0x0A5A
.half 0x0A65
.half 0x0A66
.half 0x0A69
.half 0x0A6A
.half 0x0A95
.half 0x0A96
.half 0x0A99
.half 0x0A9A
.half 0x0AA5
.half 0x0AA6
.half 0x0AA9
.half 0x0AAA

View file

@ -1,32 +0,0 @@
.set noreorder # don't insert nops after branches
.set gp=64
.include "macros.inc"
.section .text, "ax"
glabel osUnmapTLBAll
mfc0 $t0, $10
li $t1, 31
lui $t2, 0x8000
mtc0 $t2, $10
mtc0 $zero, $2
mtc0 $zero, $3
.L80321588:
mtc0 $t1, $0
nop
tlbwi
nop
nop
addi $t1, $t1, -1
bnezl $t1, .L80321588 #bnezl, bnez but with likely hint
nop
mtc0 $t0, $10
jr $ra
nop
nop
nop
nop

View file

@ -1,39 +0,0 @@
.set noreorder # don't insert nops after branches
.set gp=64
.include "macros.inc"
.section .text, "ax"
glabel osWritebackDCache
blez $a1, .osWritebackDCacheReturn
nop
li $t3, 8192
bgeu $a1, $t3, .L80324E40
nop
move $t0, $a0
addu $t1, $a0, $a1
bgeu $t0, $t1, .osWritebackDCacheReturn
nop
andi $t2, $t0, 0xf
addiu $t1, $t1, -0x10
subu $t0, $t0, $t2
.L80324E28:
cache 0x19, ($t0)
bltu $t0, $t1, .L80324E28
addiu $t0, $t0, 0x10
.osWritebackDCacheReturn:
jr $ra
nop
.L80324E40:
lui $t0, 0x8000
addu $t1, $t0, $t3
addiu $t1, $t1, -0x10
.L80324E4C:
cache 1, ($t0)
bltu $t0, $t1, .L80324E4C
addiu $t0, 0x10 # addiu $t0, $t0, 0x10
jr $ra
nop

View file

@ -1,24 +0,0 @@
.set noat # allow manual use of $at
.set noreorder # don't insert nops after branches
.set gp=64
.include "macros.inc"
.section .text, "ax"
glabel osWritebackDCacheAll
li $t0, K0BASE
li $t2, 8192
addu $t1, $t0, $t2
addiu $t1, $t1, -0x10
.L80322020:
cache 1, ($t0)
sltu $at, $t0, $t1
bnez $at, .L80322020
addiu $t0, $t0, 0x10
jr $ra
nop
nop
nop

View file

@ -1,18 +0,0 @@
.macro gsymbol sym addr
.global \sym
.set \sym, \addr
.ifndef VERSION_JP
nop
nop
.endif
.endm
.text
gsymbol osTvType 0x80000300
gsymbol osRomType 0x80000304
gsymbol osRomBase 0x80000308
gsymbol osResetType 0x8000030C
gsymbol osCiCId 0x80000310
gsymbol osVersion 0x80000314
gsymbol osMemSize 0x80000318
gsymbol osAppNmiBuffer 0x8000031C

252
lib/rsp.s
View file

@ -1,252 +0,0 @@
.include "macros.inc"
.set UCODE_SIZE, 0x800
.section .text
.balign 16
glabel rspF3DBootStart
.ifndef VERSION_EU
.incbin "lib/PR/boot/F3D_boot.bin"
.else
.incbin "lib/PR/boot/F3D_boot_eu.bin"
.half 0
.endif
glabel rspF3DBootEnd
.balign 16
.ifndef F3DEX_GBI_SHARED
glabel rspF3DStart /* Use regular Fast3D bins (default) */
.ifndef F3D_OLD
.incbin "lib/PR/f3d/new/F3D.bin" /* OS 2.0H (J2 and IQ) */
.else
.incbin "lib/PR/f3d/old/F3D.bin" /* OS 2.0D (US and JP) */
.endif
glabel rspF3DEnd
.else /* Use one of the Fast3DEX series grucodes. */
glabel rspF3DStart
.if F3DEX_GBI_2 == 1
.incbin "lib/PR/f3dex2/F3DEX2.bin"
.elseif F3DEX_GBI == 1
.incbin "lib/PR/f3dex/F3DEX.bin"
.else /* Fast3DZEX */
.incbin "lib/PR/f3dex2/F3DZEX.bin"
.endif
glabel rspF3DEnd
.endif
/* Audio Bins */
.balign 16
glabel rspAspMainStart
.incbin "lib/PR/audio/aspMain.bin"
glabel rspAspMainEnd
/*
* LESS COMMON MICROCODES
* These are setup to be loaded by G_LOAD_UCODE
*/
/* Fast3DEX NoN Text */
.ifdef F3DEX_NON_GBI
glabel rspF3DEXNoNStart
.balign 16
.incbin "lib/PR/f3dex/F3DEX_NoN.bin"
glabel rspF3DEXNoNEnd
.endif
/* Fast3DLX Text */
.ifdef F3DLX_GBI
glabel rspF3DLXStart
.incbin "lib/PR/f3dex/F3DLX.bin"
glabel rspF3DLXEnd
.endif
/* Fast3DLX NoN Text */
.ifdef F3DLX_NON_GBI
glabel rspF3DLXNoNStart
.balign 16
.incbin "lib/PR/f3dex/F3DLX_NoN.bin"
glabel rspF3DLXNoNEnd
.endif
/* Fast3DLX Rej Text */
.ifdef F3DLX_REJ_GBI
glabel rspF3DLXRejStart
.balign 16
.incbin "lib/PR/f3dex/F3DLX_Rej.bin"
glabel rspF3DLXRejEnd
.endif
/* Line3DEX Text */
.ifdef L3DEX_GBI
glabel rspL3DEXStart
.balign 16
.incbin "lib/PR/f3dex/L3DEX.bin"
glabel rspL3DEXEnd
.endif
/* S2DEX Text */
.ifdef S2DEX_GBI
glabel rspS2DEXStart
.balign 16
.incbin "lib/PR/s2dex/S2DEX.bin"
glabel rspS2DEXEnd
.endif
/* Fast3DEX2 series */
/* Fast3DEX2 NoN Text */
.ifdef F3DEX2_NON_GBI
.balign 16
glabel rspF3DEX2NoNStart
.incbin "lib/PR/f3dex2/F3DEX2_NoN.bin"
glabel rspF3DEX2NoNEnd
.endif
/* Fast3DEX2 Rej Text */
.ifdef F3DEX2_REJ_GBI
.balign 16
glabel rspF3DEX2RejStart
.incbin "lib/PR/f3dex2/F3DEX2_Rej.bin"
glabel rspF3DEX2RejEnd
.endif
/* Line3DEX2 Text */
.ifdef L3DEX2_GBI
.balign 16
glabel rspL3DEX2Start
.incbin "lib/PR/f3dex2/L3DEX2.bin"
glabel rspL3DEX2End
.endif
/* S2DEX2 Text */
.ifdef S2DEX_GBI_2
.balign 16
glabel rspS2DEXStart
.incbin "lib/PR/s2dex/S2DEX2.bin"
glabel rspS2DEXEnd
.endif
/* DATA SECTION START */
.section .rodata
.balign 16
.ifndef F3DEX_GBI_SHARED /* Use regular Fast3D data (default) */
glabel rspF3DDataStart
.ifndef F3D_OLD /* OS 2.0H (J2 and IQ) */
.ifdef VERSION_EU
.incbin "lib/PR/f3d/new/F3D_data_EU.bin"
.else
.incbin "lib/PR/f3d/new/F3D_data.bin"
.endif
.else /* OS 2.0D (US and JP) */
.incbin "lib/PR/f3d/old/F3D_data.bin"
.endif
glabel rspF3DDataEnd
.else /* Using one of the Fast3DEX series grucodes */
glabel rspF3DDataStart
.if F3DEX_GBI_2 == 1
.incbin "lib/PR/f3dex2/F3DEX2_data.bin"
.elseif F3DEX_GBI == 1
.incbin "lib/PR/f3dex/F3DEX_data.bin"
.else /* Fast3DZEX */
.incbin "lib/PR/f3dex2/F3DZEX_data.bin"
.endif
glabel rspF3DDataEnd
.endif
/* Audio Data */
.balign 16
glabel rspAspMainDataStart
.incbin "lib/PR/audio/aspMain_data.bin"
glabel rspAspMainDataEnd
/* LESS COMMON MICROCODES */
/* Fast3DEX Series */
/* Fast3DEX NoN Data */
.ifdef F3DEX_NON_GBI
.balign 16
glabel rspF3DEXNoNDataStart
.incbin "lib/PR/f3dex/F3DEX_NoN_data.bin"
glabel rspF3DEXNoNDataEnd
.endif
/* Fast3DLX Data */
.ifdef F3DLX_GBI
.balign 16
glabel rspF3DLXDataStart
.incbin "lib/PR/f3dex/F3DLX_data.bin"
glabel rspF3DLXDataEnd
.endif
/* Fast3DLX NoN Data */
.ifdef F3DLX_NON_GBI
.balign 16
glabel rspF3DLXNoNDataStart
.incbin "lib/PR/f3dex/F3DLX_NoN_data.bin"
glabel rspF3DLXNoNDataEnd
.endif
/* Fast3DLX Rej Data */
.ifdef F3DLX_REJ_GBI
.balign 16
glabel rspF3DLXRejDataStart
.incbin "lib/PR/f3dex/F3DLX_Rej_data.bin"
glabel rspF3DLXRejDataEnd
.endif
/* Line3DEX Data */
.ifdef L3DEX_GBI
.balign 16
glabel rspL3DEXDataStart
.incbin "lib/PR/f3dex/L3DEX_data.bin"
glabel rspL3DEXDataEnd
.endif
/* S2DEX Data */
.ifdef S2DEX_GBI
.balign 16
glabel rspS2DEXDataStart
.incbin "lib/PR/s2dex/S2DEX_data.bin"
glabel rspS2DEXDataEnd
.endif
/* Fast3DEX2 Series */
/* Fast3DEX2 NoN Data */
.ifdef F3DEX2_NON_GBI
.balign 16
glabel rspF3DEX2NoNStart
.incbin "lib/PR/f3dex2/F3DEX2_NoN_data.bin"
glabel rspF3DEX2NoNEnd
.endif
/* Fast3DEX2 Rej Data */
.ifdef F3DEX2_REJ_GBI
.balign 16
glabel rspF3DEX2RejStart
.incbin "lib/PR/f3dex2/F3DEX2_Rej_data.bin"
glabel rspF3DEX2RejEnd
.endif
/* Line3DEX2 Data */
.ifdef L3DEX2_GBI
.balign 16
glabel rspL3DEX2Start
.incbin "lib/PR/f3dex2/L3DEX2_data.bin"
glabel rspL3DEX2End
.endif
/* S2DEX2 Data */
.ifdef S2DEX_GBI_2
.balign 16
glabel rspS2DEXStart
.incbin "lib/PR/s2dex/S2DEX2_data.bin"
glabel rspS2DEXEnd
.endif

View file

@ -7,6 +7,7 @@
#include "seqplayer.h" #include "seqplayer.h"
#include "../pc/platform.h" #include "../pc/platform.h"
#include "../pc/fs/fs.h"
#define ALIGN16(val) (((val) + 0xF) & ~0xF) #define ALIGN16(val) (((val) + 0xF) & ~0xF)
@ -875,11 +876,8 @@ void load_sequence_internal(u32 player, u32 seqId, s32 loadAsync) {
# include <stdio.h> # include <stdio.h>
# include <stdlib.h> # include <stdlib.h>
static inline void *load_sound_res(const char *path) { static inline void *load_sound_res(const char *path) {
void *data = sys_load_res(path); void *data = fs_load_file(path, NULL);
if (!data) { if (!data) sys_fatal("could not load sound data from '%s'", path);
fprintf(stderr, "could not load sound data from '%s'\n", path);
abort();
}
// FIXME: figure out where it is safe to free this shit // FIXME: figure out where it is safe to free this shit
// can't free it immediately after in audio_init() // can't free it immediately after in audio_init()
return data; return data;

View file

@ -391,7 +391,6 @@ BAD_RETURN(s32) save_file_copy(s32 srcFileIndex, s32 destFileIndex) {
void save_file_load_all(void) { void save_file_load_all(void) {
s32 file; s32 file;
s32 validSlots;
gMainMenuDataModified = FALSE; gMainMenuDataModified = FALSE;
gSaveFileModified = FALSE; gSaveFileModified = FALSE;
@ -405,6 +404,7 @@ void save_file_load_all(void) {
gSaveFileModified = TRUE; gSaveFileModified = TRUE;
gMainMenuDataModified = TRUE; gMainMenuDataModified = TRUE;
#else #else
s32 validSlots;
read_eeprom_data(&gSaveBuffer, sizeof(gSaveBuffer)); read_eeprom_data(&gSaveBuffer, sizeof(gSaveBuffer));
if (save_file_need_bswap(&gSaveBuffer)) if (save_file_need_bswap(&gSaveBuffer))

View file

@ -3,44 +3,41 @@
#include <time.h> #include <time.h>
#include "course_table.h" #include "course_table.h"
#include "pc/ini.h" #include "pc/ini.h"
#include "pc/platform.h"
#include "pc/fs/fs.h"
#define FILENAME_FORMAT "save_file_%d.sav" #define FILENAME_FORMAT "%s/sm64_save_file_%d.sav"
#define NUM_COURSES 15 #define NUM_COURSES 15
#define NUM_BONUS_COURSES 10 #define NUM_BONUS_COURSES 10
#define NUM_FLAGS 21 #define NUM_FLAGS 21
#define NUM_CAP_ON 4 #define NUM_CAP_ON 4
/* Flag keys */
const char *sav_flags[NUM_FLAGS] = { const char *sav_flags[NUM_FLAGS] = {
"file_exists", "wing_cap", "metal_cap", "vanish_cap", "key_1", "key_2", "file_exists", "wing_cap", "metal_cap", "vanish_cap", "key_1", "key_2",
"basement_door", "upstairs_door", "ddd_moved_back", "moat_drained", "basement_door", "upstairs_door", "ddd_moved_back", "moat_drained",
"pps_door", "wf_door", "ccm_door", "jrb_door", "bitdw_door", "pps_door", "wf_door", "ccm_door", "jrb_door", "bitdw_door",
"bitfs_door", "", "", "", "", "50star_door" "bitfs_door", "", "", "", "", "50star_door" // 4 Cap flags are processed in their own section
}; };
/* Main course keys */
const char *sav_courses[NUM_COURSES] = { const char *sav_courses[NUM_COURSES] = {
"bob", "wf", "jrb", "ccm", "bbh", "hmc", "lll", "bob", "wf", "jrb", "ccm", "bbh", "hmc", "lll",
"ssl", "ddd", "sl", "wdw", "ttm", "thi", "ttc", "rr" "ssl", "ddd", "sl", "wdw", "ttm", "thi", "ttc", "rr"
}; };
/* Bonus courses keys (including Castle Course) */
const char *sav_bonus_courses[NUM_BONUS_COURSES] = { const char *sav_bonus_courses[NUM_BONUS_COURSES] = {
"hub", "bitdw", "bitfs", "bits", "pss", "cotmc", "bitdw", "bitfs", "bits", "pss", "cotmc",
"totwc", "vcutm", "wmotr", "sa", "totwc", "vcutm", "wmotr", "sa", "hub" // hub is Castle Grounds
}; };
/* Mario's cap type keys */
const char *cap_on_types[NUM_CAP_ON] = { const char *cap_on_types[NUM_CAP_ON] = {
"ground", "klepto", "ukiki", "mrblizzard" "ground", "klepto", "ukiki", "mrblizzard"
}; };
/* Sound modes */
const char *sound_modes[3] = { const char *sound_modes[3] = {
"stereo", "mono", "headset" "stereo", "mono", "headset"
}; };
/* Get current timestamp */ /* Get current timestamp string */
static void get_timestamp(char* buffer) { static void get_timestamp(char* buffer) {
time_t timer; time_t timer;
struct tm* tm_info; struct tm* tm_info;
@ -76,104 +73,107 @@ static u32 int_to_bin(u32 n) {
} }
/** /**
* Write SaveFile and MainMenuSaveData structs to a text-based savefile. * Write SaveFile and MainMenuSaveData structs to a text-based savefile
*/ */
static s32 write_text_save(s32 fileIndex) { static s32 write_text_save(s32 fileIndex) {
FILE* file; FILE* file;
struct SaveFile *savedata; struct SaveFile *savedata;
struct MainMenuSaveData *menudata; struct MainMenuSaveData *menudata;
char filename[32] = { 0 }; char filename[SYS_MAX_PATH] = { 0 };
char value[32] = { 0 }; char value[64];
u32 i, bit, flags, coins, stars, starFlags; u32 i, bit, flags, coins, stars, starFlags;
/* Define savefile's name */ if (snprintf(filename, sizeof(filename), FILENAME_FORMAT, fs_writepath, fileIndex) < 0)
if (sprintf(filename, FILENAME_FORMAT, fileIndex) < 0)
return -1; return -1;
file = fopen(filename, "wt"); file = fopen(filename, "wt");
if (file == NULL) if (file == NULL) {
printf("Savefile '%s' not found!\n", filename);
return -1; return -1;
else } else
printf("Updating savefile in '%s'\n", filename); printf("Saving updated progress to '%s'\n", filename);
/* Write header */
fprintf(file, "# Super Mario 64 save file\n"); fprintf(file, "# Super Mario 64 save file\n");
fprintf(file, "# Comment starts with #\n"); fprintf(file, "# Comment starts with #\n");
fprintf(file, "# True = 1, False = 0\n"); fprintf(file, "# True = 1, False = 0\n");
/* Write current timestamp */
get_timestamp(value); get_timestamp(value);
fprintf(file, "# %s\n", value); fprintf(file, "# %s\n", value);
/* Write MainMenuSaveData info */
menudata = &gSaveBuffer.menuData[0]; menudata = &gSaveBuffer.menuData[0];
fprintf(file, "\n[menu]\n"); fprintf(file, "\n[menu]\n");
fprintf(file, "coin_score_age = %d\n", menudata->coinScoreAges[fileIndex]); fprintf(file, "coin_score_age = %d\n", menudata->coinScoreAges[fileIndex]);
/* Sound mode */
if (menudata->soundMode == 0) { if (menudata->soundMode == 0) {
fprintf(file, "sound_mode = %s\n", sound_modes[0]); // stereo fprintf(file, "sound_mode = %s\n", sound_modes[0]); // stereo
} }
else if (menudata->soundMode == 3) { else if (menudata->soundMode == 3) {
fprintf(file, "sound_mode = %s\n", sound_modes[1]); // mono fprintf(file, "sound_mode = %s\n", sound_modes[1]); // mono
} }
else if (menudata->soundMode == 1) { else if (menudata->soundMode == 1) {
fprintf(file, "sound_mode = %s\n", sound_modes[2]); // headset fprintf(file, "sound_mode = %s\n", sound_modes[2]); // headset
} }
else { else {
printf("Undefined sound mode!"); printf("Undefined sound mode!");
return -1; return -1;
} }
/* Write all flags */
fprintf(file, "\n[flags]\n"); fprintf(file, "\n[flags]\n");
for (i = 1; i < NUM_FLAGS; i++) { for (i = 1; i < NUM_FLAGS; i++) {
if (strcmp(sav_flags[i], "")) { if (strcmp(sav_flags[i], "")) {
flags = save_file_get_flags(); flags = save_file_get_flags();
flags = (flags & (1 << i)); /* Get a specific bit */ flags = (flags & (1 << i)); // Get 'star' flag bit
flags = (flags) ? 1 : 0; /* Determine if bit is set or not */ flags = (flags) ? 1 : 0;
fprintf(file, "%s = %d\n", sav_flags[i], flags); fprintf(file, "%s = %d\n", sav_flags[i], flags);
} }
} }
/* Write coin count and star flags from each course (except Castle Grounds) */
fprintf(file, "\n[courses]\n"); fprintf(file, "\n[courses]\n");
for (i = 0; i < NUM_COURSES; i++) { for (i = 0; i < NUM_COURSES; i++) {
stars = save_file_get_star_flags(fileIndex, i); stars = save_file_get_star_flags(fileIndex, i);
coins = save_file_get_course_coin_score(fileIndex, i); coins = save_file_get_course_coin_score(fileIndex, i);
starFlags = int_to_bin(stars); /* 63 -> 111111 */ starFlags = int_to_bin(stars); // 63 -> 111111
fprintf(file, "%s = \"%d, %07d\"\n", sav_courses[i], coins, starFlags); fprintf(file, "%s = \"%d, %07d\"\n", sav_courses[i], coins, starFlags);
} }
/* Write star flags from each bonus cource (including Castle Grounds) */
fprintf(file, "\n[bonus]\n"); fprintf(file, "\n[bonus]\n");
for (i = 0; i < NUM_BONUS_COURSES; i++) { for (i = 0; i < NUM_BONUS_COURSES; i++) {
if (i == 0) { char *format;
stars = save_file_get_star_flags(fileIndex, -1);
} else {
stars = save_file_get_star_flags(fileIndex, i+15);
}
starFlags = int_to_bin(stars);
fprintf(file, "%s = %d\n", sav_bonus_courses[i], starFlags); if (i == NUM_BONUS_COURSES-1) {
// Process Castle Grounds
stars = save_file_get_star_flags(fileIndex, -1);
format = "%05d";
} else if (i == 3) {
// Process Princess's Secret Slide
stars = save_file_get_star_flags(fileIndex, 18);
format = "%02d";
} else {
// Process bonus courses
stars = save_file_get_star_flags(fileIndex, i+15);
format = "%d";
}
starFlags = int_to_bin(stars);
if (sprintf(value, format, starFlags) < 0)
return -1;
fprintf(file, "%s = %s\n", sav_bonus_courses[i], value);
} }
/* Write who steal Mario's cap */
fprintf(file, "\n[cap]\n"); fprintf(file, "\n[cap]\n");
for (i = 0; i < NUM_CAP_ON; i++) { for (i = 0; i < NUM_CAP_ON; i++) {
flags = save_file_get_flags(); // Read all flags flags = save_file_get_flags();
bit = (1 << (i+16)); // Determine current flag bit = (1 << (i+16)); // Determine current flag
flags = (flags & bit); // Get `cap` flag flags = (flags & bit); // Get 'cap' flag bit
flags = (flags) ? 1 : 0; // Determine if bit is set or not flags = (flags) ? 1 : 0;
if (flags) { if (flags) {
fprintf(file, "type = %s\n", cap_on_types[i]); fprintf(file, "type = %s\n", cap_on_types[i]);
break; break;
} }
} }
/* Write in what course and area Mario losted its cap, and cap's position */
savedata = &gSaveBuffer.files[fileIndex][0]; savedata = &gSaveBuffer.files[fileIndex][0];
switch(savedata->capLevel) { switch(savedata->capLevel) {
case COURSE_SSL: case COURSE_SSL:
@ -186,32 +186,34 @@ static s32 write_text_save(s32 fileIndex) {
fprintf(file, "level = %s\n", "ttm"); fprintf(file, "level = %s\n", "ttm");
break; break;
default: default:
fprintf(file, "level = %s\n", "none");
break; break;
} }
fprintf(file, "area = %d\n", savedata->capArea); if (savedata->capLevel) {
fprintf(file, "area = %d\n", savedata->capArea);
}
// Backup is nessecary for saving recent progress after gameover
bcopy(&gSaveBuffer.files[fileIndex][0], &gSaveBuffer.files[fileIndex][1],
sizeof(gSaveBuffer.files[fileIndex][1]));
fclose(file); fclose(file);
return 1; return 1;
} }
/** /**
* Read gSaveBuffer data from a text-based savefile. * Read gSaveBuffer data from a text-based savefile
*/ */
static s32 read_text_save(s32 fileIndex) { static s32 read_text_save(s32 fileIndex) {
char filename[32] = { 0 }; char filename[SYS_MAX_PATH] = { 0 };
char temp[32] = { 0 };
const char *value; const char *value;
ini_t *savedata; ini_t *savedata;
u32 i, flag, coins, stars, starFlags; u32 i, flag, coins, stars, starFlags;
u32 capArea; u32 capArea;
/* Define savefile's name */ if (snprintf(filename, sizeof(filename), FILENAME_FORMAT, fs_writepath, fileIndex) < 0)
if (sprintf(filename, FILENAME_FORMAT, fileIndex) < 0)
return -1; return -1;
/* Try to open the file */
savedata = ini_load(filename); savedata = ini_load(filename);
if (savedata == NULL) { if (savedata == NULL) {
return -1; return -1;
@ -219,7 +221,6 @@ static s32 read_text_save(s32 fileIndex) {
printf("Loading savefile from '%s'\n", filename); printf("Loading savefile from '%s'\n", filename);
} }
/* Read coin score age for selected file and sound mode */
ini_sget(savedata, "menu", "coin_score_age", "%d", ini_sget(savedata, "menu", "coin_score_age", "%d",
&gSaveBuffer.menuData[0].coinScoreAges[fileIndex]); &gSaveBuffer.menuData[0].coinScoreAges[fileIndex]);
@ -240,33 +241,28 @@ static s32 read_text_save(s32 fileIndex) {
return -1; return -1;
} }
/* Parse main flags */
for (i = 1; i < NUM_FLAGS; i++) { for (i = 1; i < NUM_FLAGS; i++) {
value = ini_get(savedata, "flags", sav_flags[i]); value = ini_get(savedata, "flags", sav_flags[i]);
if (value) { if (value) {
flag = strtol(value, &temp, 10); flag = value[0] - '0'; // Flag should be 0 or 1
if (flag) { if (flag) {
flag = 1 << i; /* Look #define in header.. */ flag = 1 << i; // Flags defined in 'save_file' header
gSaveBuffer.files[fileIndex][0].flags |= flag; gSaveBuffer.files[fileIndex][0].flags |= flag;
} }
} }
} }
/* Parse coin and star values for each main course */
for (i = 0; i < NUM_COURSES; i++) { for (i = 0; i < NUM_COURSES; i++) {
value = ini_get(savedata, "courses", sav_courses[i]); value = ini_get(savedata, "courses", sav_courses[i]);
if (value) { if (value) {
sscanf(value, "%d, %d", &coins, &stars); sscanf(value, "%d, %d", &coins, &stars);
starFlags = bin_to_int(stars); /* 111111 -> 63 */ starFlags = bin_to_int(stars); // 111111 -> 63
save_file_set_star_flags(fileIndex, i, starFlags); save_file_set_star_flags(fileIndex, i, starFlags);
gSaveBuffer.files[fileIndex][0].courseCoinScores[i] = coins; gSaveBuffer.files[fileIndex][0].courseCoinScores[i] = coins;
} }
} }
/* Parse star values for each bonus course */
for (i = 0; i < NUM_BONUS_COURSES; i++) { for (i = 0; i < NUM_BONUS_COURSES; i++) {
value = ini_get(savedata, "bonus", sav_bonus_courses[i]); value = ini_get(savedata, "bonus", sav_bonus_courses[i]);
if (value) { if (value) {
@ -274,19 +270,18 @@ static s32 read_text_save(s32 fileIndex) {
starFlags = bin_to_int(stars); starFlags = bin_to_int(stars);
if (strlen(value) == 5) { if (strlen(value) == 5) {
/* Process Castle Grounds */ // Process Castle Grounds
save_file_set_star_flags(fileIndex, -1, starFlags); save_file_set_star_flags(fileIndex, -1, starFlags);
} else if (strlen(value) == 2) { } else if (strlen(value) == 2) {
/* Process Princess's Secret Slide */ // Process Princess's Secret Slide
save_file_set_star_flags(fileIndex, COURSE_PSS, starFlags); save_file_set_star_flags(fileIndex, 18, starFlags);
} else { } else {
/* Process another shitty bonus course */ // Process bonus courses
save_file_set_star_flags(fileIndex, i+15, starFlags); save_file_set_star_flags(fileIndex, i+15, starFlags);
} }
} }
} }
/* Find, who steal Mario's cap ... */
for (i = 0; i < NUM_CAP_ON; i++) { for (i = 0; i < NUM_CAP_ON; i++) {
value = ini_get(savedata, "cap", "type"); value = ini_get(savedata, "cap", "type");
if (value) { if (value) {
@ -298,20 +293,16 @@ static s32 read_text_save(s32 fileIndex) {
} }
} }
/* ... it's level ... */
value = ini_get(savedata, "cap", "level"); value = ini_get(savedata, "cap", "level");
if (value) { if (value) {
if (strcmp(value, "ssl") == 0) { if (strcmp(value, "ssl") == 0) {
gSaveBuffer.files[fileIndex][0].capLevel = 8; // ssl gSaveBuffer.files[fileIndex][0].capLevel = COURSE_SSL; // ssl
} }
else if (strcmp(value, "sl") == 0) { else if (strcmp(value, "sl") == 0) {
gSaveBuffer.files[fileIndex][0].capLevel = 10; // sl gSaveBuffer.files[fileIndex][0].capLevel = COURSE_SL; // sl
} }
else if (strcmp(value, "ttm") == 0) { else if (strcmp(value, "ttm") == 0) {
gSaveBuffer.files[fileIndex][0].capLevel = 12; // ttm gSaveBuffer.files[fileIndex][0].capLevel = COURSE_TTM; // ttm
}
else if (strcmp(value, "none") == 0) {
gSaveBuffer.files[fileIndex][0].capLevel = 0;
} }
else { else {
printf("Invalid 'cap:level' flag!\n"); printf("Invalid 'cap:level' flag!\n");
@ -319,7 +310,6 @@ static s32 read_text_save(s32 fileIndex) {
} }
} }
/* ... and it's area */
value = ini_get(savedata, "cap", "area"); value = ini_get(savedata, "cap", "area");
if (value) { if (value) {
sscanf(value, "%d", &capArea); sscanf(value, "%d", &capArea);
@ -332,14 +322,13 @@ static s32 read_text_save(s32 fileIndex) {
} }
} }
/* Good, file exists for gSaveBuffer */ // Good, file exists for gSaveBuffer
gSaveBuffer.files[fileIndex][0].flags |= SAVE_FLAG_FILE_EXISTS; gSaveBuffer.files[fileIndex][0].flags |= SAVE_FLAG_FILE_EXISTS;
/* Make a backup */ // Backup is nessecary for saving recent progress after gameover
bcopy(&gSaveBuffer.files[fileIndex][0], &gSaveBuffer.files[fileIndex][1], bcopy(&gSaveBuffer.files[fileIndex][0], &gSaveBuffer.files[fileIndex][1],
sizeof(gSaveBuffer.files[fileIndex][1])); sizeof(gSaveBuffer.files[fileIndex][1]));
/* Cleaning up after ourselves */
ini_free(savedata); ini_free(savedata);
return 0; return 0;
} }

View file

@ -15,5 +15,6 @@ extern void func_sh_8024CA04(void);
extern void cancel_rumble(void); extern void cancel_rumble(void);
extern void create_thread_6(void); extern void create_thread_6(void);
extern void rumble_thread_update_vi(void); extern void rumble_thread_update_vi(void);
extern void thread6_rumble_loop(void *a0);
#endif // _THREAD_6_H #endif // _THREAD_6_H

View file

@ -16,7 +16,7 @@ static void print_help(void) {
printf("Super Mario 64 PC Port\n"); printf("Super Mario 64 PC Port\n");
printf("%-20s\tEnables the cheat menu.\n", "--cheats"); printf("%-20s\tEnables the cheat menu.\n", "--cheats");
printf("%-20s\tSaves the configuration file as CONFIGNAME.\n", "--configfile CONFIGNAME"); printf("%-20s\tSaves the configuration file as CONFIGNAME.\n", "--configfile CONFIGNAME");
printf("%-20s\tOverrides the default read-only data path ('!' expands to executable path).\n", "--datapath DATAPATH"); printf("%-20s\tSets additional data directory name (only 'res' is used by default).\n", "--gamedir DIRNAME");
printf("%-20s\tOverrides the default save/config path ('!' expands to executable path).\n", "--savepath SAVEPATH"); printf("%-20s\tOverrides the default save/config path ('!' expands to executable path).\n", "--savepath SAVEPATH");
printf("%-20s\tStarts the game in full screen mode.\n", "--fullscreen"); printf("%-20s\tStarts the game in full screen mode.\n", "--fullscreen");
printf("%-20s\tSkips the Peach and Castle intro when starting a new game.\n", "--skip-intro"); printf("%-20s\tSkips the Peach and Castle intro when starting a new game.\n", "--skip-intro");
@ -54,8 +54,8 @@ void parse_cli_opts(int argc, char* argv[]) {
else if (strcmp(argv[i], "--configfile") == 0 && (i + 1) < argc) else if (strcmp(argv[i], "--configfile") == 0 && (i + 1) < argc)
arg_string("--configfile", argv[++i], gCLIOpts.ConfigFile); arg_string("--configfile", argv[++i], gCLIOpts.ConfigFile);
else if (strcmp(argv[i], "--datapath") == 0 && (i + 1) < argc) else if (strcmp(argv[i], "--gamedir") == 0 && (i + 1) < argc)
arg_string("--datapath", argv[++i], gCLIOpts.DataPath); arg_string("--gamedir", argv[++i], gCLIOpts.GameDir);
else if (strcmp(argv[i], "--savepath") == 0 && (i + 1) < argc) else if (strcmp(argv[i], "--savepath") == 0 && (i + 1) < argc)
arg_string("--savepath", argv[++i], gCLIOpts.SavePath); arg_string("--savepath", argv[++i], gCLIOpts.SavePath);

View file

@ -8,7 +8,7 @@ struct PCCLIOptions {
unsigned int FullScreen; unsigned int FullScreen;
char ConfigFile[SYS_MAX_PATH]; char ConfigFile[SYS_MAX_PATH];
char SavePath[SYS_MAX_PATH]; char SavePath[SYS_MAX_PATH];
char DataPath[SYS_MAX_PATH]; char GameDir[SYS_MAX_PATH];
}; };
extern struct PCCLIOptions gCLIOpts; extern struct PCCLIOptions gCLIOpts;

View file

@ -5,13 +5,20 @@
#include <string.h> #include <string.h>
#include <assert.h> #include <assert.h>
#include <ctype.h> #include <ctype.h>
#include <SDL2/SDL.h>
#if USE_SDL == 2
# include <SDL2/SDL.h>
# define WINDOWPOS_CENTERED SDL_WINDOWPOS_CENTERED
#else
# define WINDOWPOS_CENTERED 0
#endif
#include "platform.h" #include "platform.h"
#include "configfile.h" #include "configfile.h"
#include "cliopts.h" #include "cliopts.h"
#include "gfx/gfx_screen_config.h" #include "gfx/gfx_screen_config.h"
#include "controller/controller_api.h" #include "controller/controller_api.h"
#include "fs/fs.h"
#define ARRAY_LEN(arr) (sizeof(arr) / sizeof(arr[0])) #define ARRAY_LEN(arr) (sizeof(arr) / sizeof(arr[0]))
@ -38,8 +45,8 @@ struct ConfigOption {
// Video/audio stuff // Video/audio stuff
ConfigWindow configWindow = { ConfigWindow configWindow = {
.x = SDL_WINDOWPOS_CENTERED, .x = WINDOWPOS_CENTERED,
.y = SDL_WINDOWPOS_CENTERED, .y = WINDOWPOS_CENTERED,
.w = DESIRED_SCREEN_WIDTH, .w = DESIRED_SCREEN_WIDTH,
.h = DESIRED_SCREEN_HEIGHT, .h = DESIRED_SCREEN_HEIGHT,
.vsync = 1, .vsync = 1,
@ -136,7 +143,7 @@ static const struct ConfigOption options[] = {
// Reads an entire line from a file (excluding the newline character) and returns an allocated string // Reads an entire line from a file (excluding the newline character) and returns an allocated string
// Returns NULL if no lines could be read from the file // Returns NULL if no lines could be read from the file
static char *read_file_line(FILE *file) { static char *read_file_line(fs_file_t *file) {
char *buffer; char *buffer;
size_t bufferSize = 8; size_t bufferSize = 8;
size_t offset = 0; // offset in buffer to write size_t offset = 0; // offset in buffer to write
@ -144,7 +151,7 @@ static char *read_file_line(FILE *file) {
buffer = malloc(bufferSize); buffer = malloc(bufferSize);
while (1) { while (1) {
// Read a line from the file // Read a line from the file
if (fgets(buffer + offset, bufferSize - offset, file) == NULL) { if (fs_readline(file, buffer + offset, bufferSize - offset) == NULL) {
free(buffer); free(buffer);
return NULL; // Nothing could be read. return NULL; // Nothing could be read.
} }
@ -157,7 +164,7 @@ static char *read_file_line(FILE *file) {
break; break;
} }
if (feof(file)) // EOF was reached if (fs_eof(file)) // EOF was reached
break; break;
// If no newline or EOF was reached, then the whole line wasn't read. // If no newline or EOF was reached, then the whole line wasn't read.
@ -211,24 +218,17 @@ static unsigned int tokenize_string(char *str, int maxTokens, char **tokens) {
// Gets the config file path and caches it // Gets the config file path and caches it
const char *configfile_name(void) { const char *configfile_name(void) {
static char cfgpath[SYS_MAX_PATH] = { 0 }; return (gCLIOpts.ConfigFile[0]) ? gCLIOpts.ConfigFile : CONFIGFILE_DEFAULT;
if (!cfgpath[0]) {
if (gCLIOpts.ConfigFile[0])
snprintf(cfgpath, sizeof(cfgpath), "%s", gCLIOpts.ConfigFile);
else
snprintf(cfgpath, sizeof(cfgpath), "%s/%s", sys_save_path(), CONFIGFILE_DEFAULT);
}
return cfgpath;
} }
// Loads the config file specified by 'filename' // Loads the config file specified by 'filename'
void configfile_load(const char *filename) { void configfile_load(const char *filename) {
FILE *file; fs_file_t *file;
char *line; char *line;
printf("Loading configuration from '%s'\n", filename); printf("Loading configuration from '%s'\n", filename);
file = fopen(filename, "r"); file = fs_open(filename);
if (file == NULL) { if (file == NULL) {
// Create a new config file and save defaults // Create a new config file and save defaults
printf("Config file '%s' not found. Creating it.\n", filename); printf("Config file '%s' not found. Creating it.\n", filename);
@ -292,7 +292,7 @@ void configfile_load(const char *filename) {
free(line); free(line);
} }
fclose(file); fs_close(file);
} }
// Writes the config file to 'filename' // Writes the config file to 'filename'
@ -301,7 +301,7 @@ void configfile_save(const char *filename) {
printf("Saving configuration to '%s'\n", filename); printf("Saving configuration to '%s'\n", filename);
file = fopen(filename, "w"); file = fopen(fs_get_write_path(filename), "w");
if (file == NULL) { if (file == NULL) {
// error // error
return; return;

View file

@ -16,18 +16,24 @@
#include <ultra64.h> #include <ultra64.h>
struct ControllerAPI { struct ControllerAPI {
const u32 vkbase; // base number in the virtual keyspace (e.g. keyboard is 0x0000-0x1000) const u32 vkbase; // base number in the virtual keyspace (e.g. keyboard is 0x0000-0x1000)
void (*init)(void); // call once, also calls reconfig() void (*init)(void); // call once, also calls reconfig()
void (*read)(OSContPad *pad); // read controller and update N64 pad values void (*read)(OSContPad *pad); // read controller and update N64 pad values
u32 (*rawkey)(void); // returns last pressed virtual key or VK_INVALID if none u32 (*rawkey)(void); // returns last pressed virtual key or VK_INVALID if none
void (*reconfig)(void); // (optional) call when bindings have changed void (*rumble_play)(float str, float time); // (optional) rumble with intensity `str` (0 - 1) for `time` seconds
void (*shutdown)(void); // (optional) call in osContReset void (*rumble_stop)(void); // (optional) stop any ongoing haptic feedback
void (*reconfig)(void); // (optional) call when bindings have changed
void (*shutdown)(void); // (optional) call in osContReset
}; };
// used for binding keys // used for binding keys
u32 controller_get_raw_key(void); u32 controller_get_raw_key(void);
void controller_reconfigure(void); void controller_reconfigure(void);
// rumbles all controllers with rumble support
void controller_rumble_play(float str, float time);
void controller_rumble_stop(void);
// calls the shutdown() function of all controller subsystems // calls the shutdown() function of all controller subsystems
void controller_shutdown(void); void controller_shutdown(void);

View file

@ -1,5 +1,6 @@
#include "lib/src/libultra_internal.h" #include "lib/src/libultra_internal.h"
#include "lib/src/osContInternal.h" #include "lib/src/osContInternal.h"
#include "macros.h"
#include "../configfile.h" #include "../configfile.h"
@ -21,7 +22,7 @@ static struct ControllerAPI *controller_implementations[] = {
&controller_keyboard, &controller_keyboard,
}; };
s32 osContInit(OSMesgQueue *mq, u8 *controllerBits, OSContStatus *status) { s32 osContInit(UNUSED OSMesgQueue *mq, u8 *controllerBits, UNUSED OSContStatus *status) {
for (size_t i = 0; i < sizeof(controller_implementations) / sizeof(struct ControllerAPI *); i++) { for (size_t i = 0; i < sizeof(controller_implementations) / sizeof(struct ControllerAPI *); i++) {
controller_implementations[i]->init(); controller_implementations[i]->init();
} }
@ -29,20 +30,23 @@ s32 osContInit(OSMesgQueue *mq, u8 *controllerBits, OSContStatus *status) {
return 0; return 0;
} }
s32 osMotorStart(void *pfs) { s32 osMotorStart(UNUSED void *pfs) {
// Since rumble stops by osMotorStop, its duration is not nessecary. // Since rumble stops by osMotorStop, its duration is not nessecary.
return controller_rumble_play(configRumbleStrength / 100.0, 50); // Set it to 5 seconds and hope osMotorStop() is called in time.
controller_rumble_play(configRumbleStrength / 100.0f, 5.0f);
return 0;
} }
s32 osMotorStop(void *pfs) { s32 osMotorStop(UNUSED void *pfs) {
return controller_rumble_stop(); controller_rumble_stop();
return 0;
} }
u32 osMotorInit(OSMesgQueue *mq, void *pfs, s32 port) { u32 osMotorInit(UNUSED OSMesgQueue *mq, UNUSED void *pfs, UNUSED s32 port) {
return controller_rumble_init(); return 0; // rumble is initialized in the specific backend's init function
} }
s32 osContStartReadData(OSMesgQueue *mesg) { s32 osContStartReadData(UNUSED OSMesgQueue *mesg) {
return 0; return 0;
} }
@ -93,3 +97,17 @@ void controller_reconfigure(void) {
controller_implementations[i]->reconfig(); controller_implementations[i]->reconfig();
} }
} }
void controller_rumble_play(float str, float time) {
for (size_t i = 0; i < sizeof(controller_implementations) / sizeof(struct ControllerAPI *); i++) {
if (controller_implementations[i]->rumble_play)
controller_implementations[i]->rumble_play(str, time);
}
}
void controller_rumble_stop(void) {
for (size_t i = 0; i < sizeof(controller_implementations) / sizeof(struct ControllerAPI *); i++) {
if (controller_implementations[i]->rumble_stop)
controller_implementations[i]->rumble_stop();
}
}

View file

@ -113,6 +113,8 @@ struct ControllerAPI controller_keyboard = {
keyboard_init, keyboard_init,
keyboard_read, keyboard_read,
keyboard_rawkey, keyboard_rawkey,
NULL,
NULL,
keyboard_bindkeys, keyboard_bindkeys,
keyboard_shutdown keyboard_shutdown
}; };

View file

@ -39,6 +39,8 @@ struct ControllerAPI controller_recorded_tas = {
tas_init, tas_init,
tas_read, tas_read,
tas_rawkey, tas_rawkey,
NULL, // no rumble_play
NULL, // no rumble_stop
NULL, // no rebinding NULL, // no rebinding
tas_shutdown tas_shutdown
}; };

View file

@ -14,6 +14,7 @@
#include "controller_sdl.h" #include "controller_sdl.h"
#include "../configfile.h" #include "../configfile.h"
#include "../platform.h" #include "../platform.h"
#include "../fs/fs.h"
#include "game/level_update.h" #include "game/level_update.h"
@ -92,15 +93,17 @@ static void controller_sdl_init(void) {
} }
// try loading an external gamecontroller mapping file // try loading an external gamecontroller mapping file
char gcpath[SYS_MAX_PATH]; uint64_t gcsize = 0;
snprintf(gcpath, sizeof(gcpath), "%s/gamecontrollerdb.txt", sys_save_path()); void *gcdata = fs_load_file("gamecontrollerdb.txt", &gcsize);
int nummaps = SDL_GameControllerAddMappingsFromFile(gcpath); if (gcdata && gcsize) {
if (nummaps < 0) { SDL_RWops *rw = SDL_RWFromConstMem(gcdata, gcsize);
snprintf(gcpath, sizeof(gcpath), "%s/gamecontrollerdb.txt", sys_data_path()); if (rw) {
nummaps = SDL_GameControllerAddMappingsFromFile(gcpath); int nummaps = SDL_GameControllerAddMappingsFromRW(rw, SDL_TRUE);
if (nummaps >= 0)
printf("loaded %d controller mappings from 'gamecontrollerdb.txt'\n", nummaps);
}
free(gcdata);
} }
if (nummaps >= 0)
printf("loaded %d controller mappings from '%s'\n", nummaps, gcpath);
#ifdef BETTERCAMERA #ifdef BETTERCAMERA
if (newcam_mouse == 1) if (newcam_mouse == 1)
@ -113,6 +116,24 @@ static void controller_sdl_init(void) {
init_ok = true; init_ok = true;
} }
static SDL_Haptic *controller_sdl_init_haptics(const int joy) {
SDL_Haptic *hap = SDL_HapticOpen(joy);
if (!hap) return NULL;
if (SDL_HapticRumbleSupported(hap) != SDL_TRUE) {
SDL_HapticClose(hap);
return NULL;
}
if (SDL_HapticRumbleInit(hap) != 0) {
SDL_HapticClose(hap);
return NULL;
}
printf("controller %s has haptics support, rumble enabled\n", SDL_JoystickNameForIndex(joy));
return hap;
}
static void controller_sdl_read(OSContPad *pad) { static void controller_sdl_read(OSContPad *pad) {
if (!init_ok) { if (!init_ok) {
return; return;
@ -138,15 +159,18 @@ static void controller_sdl_read(OSContPad *pad) {
SDL_GameControllerUpdate(); SDL_GameControllerUpdate();
if (sdl_cntrl != NULL && !SDL_GameControllerGetAttached(sdl_cntrl)) { if (sdl_cntrl != NULL && !SDL_GameControllerGetAttached(sdl_cntrl)) {
SDL_HapticClose(sdl_haptic);
SDL_GameControllerClose(sdl_cntrl); SDL_GameControllerClose(sdl_cntrl);
sdl_cntrl = NULL; sdl_cntrl = NULL;
sdl_haptic = NULL;
} }
if (sdl_cntrl == NULL) { if (sdl_cntrl == NULL) {
for (int i = 0; i < SDL_NumJoysticks(); i++) { for (int i = 0; i < SDL_NumJoysticks(); i++) {
if (SDL_IsGameController(i)) { if (SDL_IsGameController(i)) {
sdl_cntrl = SDL_GameControllerOpen(i); sdl_cntrl = SDL_GameControllerOpen(i);
sdl_haptic = SDL_HapticOpen(i);
if (sdl_cntrl != NULL) { if (sdl_cntrl != NULL) {
sdl_haptic = controller_sdl_init_haptics(i);
break; break;
} }
} }
@ -220,6 +244,16 @@ static void controller_sdl_read(OSContPad *pad) {
} }
} }
static void controller_sdl_rumble_play(f32 strength, f32 length) {
if (sdl_haptic)
SDL_HapticRumblePlay(sdl_haptic, strength, (u32)(length * 1000.0f));
}
static void controller_sdl_rumble_stop(void) {
if (sdl_haptic)
SDL_HapticRumbleStop(sdl_haptic);
}
static u32 controller_sdl_rawkey(void) { static u32 controller_sdl_rawkey(void) {
if (last_joybutton != VK_INVALID) { if (last_joybutton != VK_INVALID) {
const u32 ret = last_joybutton; const u32 ret = last_joybutton;
@ -252,42 +286,13 @@ static void controller_sdl_shutdown(void) {
init_ok = false; init_ok = false;
} }
u32 controller_rumble_init(void) {
if (SDL_HapticRumbleSupported(sdl_haptic) != SDL_TRUE) {
// printf("Controller does not support haptics! %s\n", SDL_GetError());
return 1;
}
if (SDL_HapticRumbleInit(sdl_haptic) != 0) {
printf("Unable to initialize rumble! %s\n", SDL_GetError());
return 1;
}
return 0;
}
s32 controller_rumble_play(f32 strength, u32 length) {
if (SDL_HapticRumblePlay(sdl_haptic, strength, length) != 0) {
printf("Unable to start rumble! %s\n", SDL_GetError());
return -1;
} else {
return 0;
}
}
s32 controller_rumble_stop(void) {
if (SDL_HapticRumbleStop(sdl_haptic) != 0) {
printf("Unable to stop rumble! %s\n", SDL_GetError());
return -1;
} else {
return 0;
}
}
struct ControllerAPI controller_sdl = { struct ControllerAPI controller_sdl = {
VK_BASE_SDL_GAMEPAD, VK_BASE_SDL_GAMEPAD,
controller_sdl_init, controller_sdl_init,
controller_sdl_read, controller_sdl_read,
controller_sdl_rawkey, controller_sdl_rawkey,
controller_sdl_rumble_play,
controller_sdl_rumble_stop,
controller_sdl_bind, controller_sdl_bind,
controller_sdl_shutdown controller_sdl_shutdown
}; };

View file

@ -7,8 +7,4 @@
extern struct ControllerAPI controller_sdl; extern struct ControllerAPI controller_sdl;
u32 controller_rumble_init(void);
s32 controller_rumble_play(f32 strength, u32 length);
s32 controller_rumble_stop(void);
#endif #endif

137
src/pc/fs/dirtree.c Normal file
View file

@ -0,0 +1,137 @@
#include <stdlib.h>
#include <stdbool.h>
#include <stdint.h>
#include <string.h>
#include "../platform.h"
#include "fs.h"
#include "dirtree.h"
static inline uint32_t dirtree_hash(const char *s, size_t len) {
// djb hash
uint32_t hash = 5381;
while (len--) hash = ((hash << 5) + hash) ^ *(s++);
return hash & (FS_NUMBUCKETS - 1);
}
bool fs_dirtree_init(fs_dirtree_t *tree, const size_t entry_len) {
memset(tree, 0, sizeof(*tree));
tree->root = malloc(entry_len);
if (!tree->root) return false;
tree->root->name = ""; // root
tree->root->is_dir = true;
tree->entry_len = entry_len;
return true;
}
void fs_dirtree_free(fs_dirtree_t *tree) {
if (!tree) return;
if (tree->root) free(tree->root);
for (int i = 0; i < FS_NUMBUCKETS; ++i) {
fs_dirtree_entry_t *ent, *next;
for (ent = tree->buckets[i]; ent; ent = next) {
next = ent->next_hash;
free(ent);
}
}
}
static inline fs_dirtree_entry_t *dirtree_add_ancestors(fs_dirtree_t *tree, char *name) {
fs_dirtree_entry_t *ent = tree->root;
// look for parent directory
char *last_sep = strrchr(name, '/');
if (!last_sep) return ent;
*last_sep = 0;
ent = fs_dirtree_find(tree, name);
if (ent) {
*last_sep = '/'; // put the separator back
return ent; // parent directory already in tree
}
// add the parent directory
ent = fs_dirtree_add(tree, name, true);
*last_sep = '/';
return ent;
}
fs_dirtree_entry_t *fs_dirtree_add(fs_dirtree_t *tree, char *name, const bool is_dir) {
fs_dirtree_entry_t *ent = fs_dirtree_find(tree, name);
if (ent) return ent;
// add the parent directory into the tree first
fs_dirtree_entry_t *parent = dirtree_add_ancestors(tree, name);
if (!parent) return NULL;
// we'll plaster the name at the end of the allocated chunk, after the actual entry
const size_t name_len = strlen(name);
const size_t allocsize = tree->entry_len + name_len + 1;
ent = calloc(1, allocsize);
if (!ent) return NULL;
ent->name = (const char *)ent + tree->entry_len;
strcpy((char *)ent->name, name);
const uint32_t hash = dirtree_hash(name, name_len);
ent->next_hash = tree->buckets[hash];
tree->buckets[hash] = ent;
ent->next_sibling = parent->next_child;
ent->is_dir = is_dir;
parent->next_child = ent;
return ent;
}
fs_dirtree_entry_t *fs_dirtree_find(fs_dirtree_t *tree, const char *name) {
if (!name) return NULL;
if (!*name) return tree->root;
const uint32_t hash = dirtree_hash(name, strlen(name));
fs_dirtree_entry_t *ent, *prev = NULL;
for (ent = tree->buckets[hash]; ent; ent = ent->next_hash) {
if (!strcmp(ent->name, name)) {
// if this path is not in front of the hash list, move it to the front
// in case of reccurring searches
if (prev) {
prev->next_hash = ent->next_hash;
ent->next_hash = tree->buckets[hash];
tree->buckets[hash] = ent;
}
return ent;
}
prev = ent;
}
return NULL;
}
static fs_walk_result_t dirtree_walk_impl(fs_dirtree_entry_t *ent, walk_fn_t walkfn, void *user, const bool recur) {
fs_walk_result_t res = FS_WALK_SUCCESS;;
ent = ent->next_child;
while (ent && (res == FS_WALK_SUCCESS)) {
if (ent->is_dir) {
if (recur && ent->next_child)
res = dirtree_walk_impl(ent, walkfn, user, recur);
} else if (!walkfn(user, ent->name)) {
res = FS_WALK_INTERRUPTED;
break;
}
ent = ent->next_sibling;
}
return res;
}
fs_walk_result_t fs_dirtree_walk(void *pack, const char *base, walk_fn_t walkfn, void *user, const bool recur) {
fs_dirtree_t *tree = (fs_dirtree_t *)pack;
fs_dirtree_entry_t *ent = fs_dirtree_find(tree, base);
if (!ent) return FS_WALK_NOTFOUND;
return dirtree_walk_impl(ent, walkfn, user, recur);
}

32
src/pc/fs/dirtree.h Normal file
View file

@ -0,0 +1,32 @@
#ifndef _SM64_DIRTREE_H_
#define _SM64_DIRTREE_H_
#include <stdlib.h>
#include <stdbool.h>
#include "fs.h"
#define FS_NUMBUCKETS 64
typedef struct fs_dirtree_entry_s {
const char *name;
bool is_dir;
struct fs_dirtree_entry_s *next_hash, *next_child, *next_sibling;
} fs_dirtree_entry_t;
typedef struct {
fs_dirtree_entry_t *root;
fs_dirtree_entry_t *buckets[FS_NUMBUCKETS];
size_t entry_len;
} fs_dirtree_t;
bool fs_dirtree_init(fs_dirtree_t *tree, const size_t entry_len);
void fs_dirtree_free(fs_dirtree_t *tree);
fs_dirtree_entry_t *fs_dirtree_add(fs_dirtree_t *tree, char *name, const bool is_dir);
fs_dirtree_entry_t *fs_dirtree_find(fs_dirtree_t *tree, const char *name);
// the first arg is void* so this could be used in walk() methods of various packtypes
fs_walk_result_t fs_dirtree_walk(void *tree, const char *base, walk_fn_t walkfn, void *user, const bool recur);
#endif // _SM64_DIRTREE_H_

443
src/pc/fs/fs.c Normal file
View file

@ -0,0 +1,443 @@
#include <stdlib.h>
#include <stdio.h>
#include <stdbool.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <dirent.h>
#include <ctype.h>
#ifdef _WIN32
#include <direct.h>
#endif
#include "macros.h"
#include "../platform.h"
#include "fs.h"
char fs_gamedir[SYS_MAX_PATH] = "";
char fs_writepath[SYS_MAX_PATH] = "";
struct fs_dir_s {
void *pack;
const char *realpath;
fs_packtype_t *packer;
struct fs_dir_s *prev, *next;
};
extern fs_packtype_t fs_packtype_dir;
extern fs_packtype_t fs_packtype_zip;
static fs_packtype_t *fs_packers[] = {
&fs_packtype_dir,
&fs_packtype_zip,
};
static fs_dir_t *fs_searchpaths = NULL;
static inline fs_dir_t *fs_find_dir(const char *realpath) {
for (fs_dir_t *dir = fs_searchpaths; dir; dir = dir->next)
if (!sys_strcasecmp(realpath, dir->realpath))
return dir;
return NULL;
}
static int mount_cmp(const void *p1, const void *p2) {
const char *s1 = sys_file_name(*(const char **)p1);
const char *s2 = sys_file_name(*(const char **)p2);
// check if one or both of these are basepacks
const int plen = strlen(FS_BASEPACK_PREFIX);
const bool is_base1 = !strncmp(s1, FS_BASEPACK_PREFIX, plen);
const bool is_base2 = !strncmp(s2, FS_BASEPACK_PREFIX, plen);
// if both are basepacks, compare the postfixes only
if (is_base1 && is_base2) return strcmp(s1 + plen, s2 + plen);
// if only one is a basepack, it goes first
if (is_base1) return -1;
if (is_base2) return 1;
// otherwise strcmp order
return strcmp(s1, s2);
}
static void scan_path_dir(const char *ropath, const char *dir) {
char dirpath[SYS_MAX_PATH];
snprintf(dirpath, sizeof(dirpath), "%s/%s", ropath, dir);
if (!fs_sys_dir_exists(dirpath)) return;
// since filename order in readdir() isn't guaranteed, collect paths and sort them in strcmp() order
// (but with basepacks first)
fs_pathlist_t plist = fs_sys_enumerate(dirpath, false);
if (plist.paths) {
qsort(plist.paths, plist.numpaths, sizeof(char *), mount_cmp);
for (int i = 0; i < plist.numpaths; ++i)
fs_mount(plist.paths[i]);
fs_pathlist_free(&plist);
}
// mount the directory itself
fs_mount(dirpath);
}
bool fs_init(const char **rodirs, const char *gamedir, const char *writepath) {
char buf[SYS_MAX_PATH];
// expand and remember the write path
strncpy(fs_writepath, fs_convert_path(buf, sizeof(buf), writepath), sizeof(fs_writepath));
fs_writepath[sizeof(fs_writepath)-1] = 0;
printf("fs: writepath set to `%s`\n", fs_writepath);
// remember the game directory name
strncpy(fs_gamedir, gamedir, sizeof(fs_gamedir));
fs_gamedir[sizeof(fs_gamedir)-1] = 0;
printf("fs: gamedir set to `%s`\n", fs_gamedir);
// first, scan all possible paths and mount all basedirs in them
for (const char **p = rodirs; p && *p; ++p)
scan_path_dir(fs_convert_path(buf, sizeof(buf), *p), FS_BASEDIR);
scan_path_dir(fs_writepath, FS_BASEDIR);
// then mount all the gamedirs in them, if the game dir isn't the same
if (sys_strcasecmp(FS_BASEDIR, fs_gamedir)) {
for (const char **p = rodirs; p && *p; ++p)
scan_path_dir(fs_convert_path(buf, sizeof(buf), *p), fs_gamedir);
scan_path_dir(fs_writepath, fs_gamedir);
}
// as a special case, mount writepath itself
fs_mount(fs_writepath);
return true;
}
bool fs_mount(const char *realpath) {
if (fs_find_dir(realpath))
return false; // already mounted
const char *ext = sys_file_extension(realpath);
void *pack = NULL;
fs_packtype_t *packer = NULL;
bool tried = false;
for (unsigned int i = 0; i < sizeof(fs_packers) / sizeof(fs_packers[0]); ++i) {
if (ext && sys_strcasecmp(ext, fs_packers[i]->extension))
continue;
tried = true;
pack = fs_packers[i]->mount(realpath);
if (pack) {
packer = fs_packers[i];
break;
}
}
if (!pack || !packer) {
if (tried)
fprintf(stderr, "fs: could not mount '%s'\n", realpath);
return false;
}
fs_dir_t *dir = calloc(1, sizeof(fs_dir_t));
if (!dir) {
packer->unmount(pack);
return false;
}
dir->pack = pack;
dir->realpath = sys_strdup(realpath);
dir->packer = packer;
dir->prev = NULL;
dir->next = fs_searchpaths;
if (fs_searchpaths)
fs_searchpaths->prev = dir;
fs_searchpaths = dir;
printf("fs: mounting '%s'\n", realpath);
return true;
}
bool fs_unmount(const char *realpath) {
fs_dir_t *dir = fs_find_dir(realpath);
if (dir) {
dir->packer->unmount(dir->pack);
free((void *)dir->realpath);
if (dir->prev) dir->prev->next = dir->next;
if (dir->next) dir->next->prev = dir->prev;
if (dir == fs_searchpaths) fs_searchpaths = dir->next;
free(dir);
return true;
}
return false;
}
fs_walk_result_t fs_walk(const char *base, walk_fn_t walkfn, void *user, const bool recur) {
bool found = false;
for (fs_dir_t *dir = fs_searchpaths; dir; dir = dir->next) {
fs_walk_result_t res = dir->packer->walk(dir->pack, base, walkfn, user, recur);
if (res == FS_WALK_INTERRUPTED)
return res;
if (res != FS_WALK_NOTFOUND)
found = true;
}
return found ? FS_WALK_SUCCESS : FS_WALK_NOTFOUND;
}
bool fs_is_file(const char *fname) {
for (fs_dir_t *dir = fs_searchpaths; dir; dir = dir->next) {
if (dir->packer->is_file(dir->pack, fname))
return true;
}
return false;
}
bool fs_is_dir(const char *fname) {
for (fs_dir_t *dir = fs_searchpaths; dir; dir = dir->next) {
if (dir->packer->is_dir(dir->pack, fname))
return true;
}
return false;
}
fs_file_t *fs_open(const char *vpath) {
for (fs_dir_t *dir = fs_searchpaths; dir; dir = dir->next) {
fs_file_t *f = dir->packer->open(dir->pack, vpath);
if (f) {
f->parent = dir;
return f;
}
}
return NULL;
}
void fs_close(fs_file_t *file) {
if (!file) return;
file->parent->packer->close(file->parent->pack, file);
}
int64_t fs_read(fs_file_t *file, void *buf, const uint64_t size) {
if (!file) return -1;
return file->parent->packer->read(file->parent->pack, file, buf, size);
}
bool fs_seek(fs_file_t *file, const int64_t ofs) {
if (!file) return -1;
return file->parent->packer->seek(file->parent->pack, file, ofs);
}
int64_t fs_tell(fs_file_t *file) {
if (!file) return -1;
return file->parent->packer->tell(file->parent->pack, file);
}
int64_t fs_size(fs_file_t *file) {
if (!file) return -1;
return file->parent->packer->size(file->parent->pack, file);
}
bool fs_eof(fs_file_t *file) {
if (!file) return true;
return file->parent->packer->eof(file->parent->pack, file);
}
struct matchdata_s {
const char *prefix;
size_t prefix_len;
char *dst;
size_t dst_len;
};
static bool match_walk(void *user, const char *path) {
struct matchdata_s *data = (struct matchdata_s *)user;
if (!strncmp(path, data->prefix, data->prefix_len)) {
// found our lad, copy path to destination and terminate
strncpy(data->dst, path, data->dst_len);
data->dst[data->dst_len - 1] = 0;
return false;
}
return true;
}
const char *fs_match(char *outname, const size_t outlen, const char *prefix) {
struct matchdata_s data = {
.prefix = prefix,
.prefix_len = strlen(prefix),
.dst = outname,
.dst_len = outlen,
};
if (fs_walk("", match_walk, &data, true) == FS_WALK_INTERRUPTED)
return outname;
return NULL;
}
static bool enumerate_walk(void *user, const char *path) {
fs_pathlist_t *data = (fs_pathlist_t *)user;
if (data->listcap == data->numpaths) {
data->listcap *= 2;
char **newpaths = realloc(data->paths, data->listcap * sizeof(char *));
if (!newpaths) return false;
data->paths = newpaths;
}
data->paths[data->numpaths++] = sys_strdup(path);
return true;
}
fs_pathlist_t fs_enumerate(const char *base, const bool recur) {
char **paths = malloc(sizeof(char *) * 32);
fs_pathlist_t pathlist = { paths, 0, 32 };
if (!paths) return pathlist;
if (fs_walk(base, enumerate_walk, &pathlist, recur) == FS_WALK_INTERRUPTED)
fs_pathlist_free(&pathlist);
return pathlist;
}
void fs_pathlist_free(fs_pathlist_t *pathlist) {
if (!pathlist || !pathlist->paths) return;
for (int i = 0; i < pathlist->numpaths; ++i)
free(pathlist->paths[i]);
free(pathlist->paths);
pathlist->paths = NULL;
pathlist->numpaths = 0;
}
const char *fs_readline(fs_file_t *file, char *dst, uint64_t size) {
int64_t rx = 0;
char chr, *p;
// assume we got buffered input
for (p = dst, size--; size > 0; size--) {
if ((rx = fs_read(file, &chr, 1)) <= 0)
break;
*p++ = chr;
if (chr == '\n')
break;
}
*p = 0;
if (p == dst || rx <= 0)
return NULL;
return p;
}
void *fs_load_file(const char *vpath, uint64_t *outsize) {
fs_file_t *f = fs_open(vpath);
if (!f) return NULL;
int64_t size = fs_size(f);
if (size <= 0) {
fs_close(f);
return NULL;
}
void *buf = malloc(size);
if (!buf) {
fs_close(f);
return NULL;
}
int64_t rx = fs_read(f, buf, size);
fs_close(f);
if (rx < size) {
free(buf);
return NULL;
}
if (outsize) *outsize = size;
return buf;
}
const char *fs_get_write_path(const char *vpath) {
static char path[SYS_MAX_PATH];
snprintf(path, sizeof(path), "%s/%s", fs_writepath, vpath);
return path;
}
const char *fs_convert_path(char *buf, const size_t bufsiz, const char *path) {
// ! means "executable directory"
if (path[0] == '!') {
snprintf(buf, bufsiz, "%s%s", sys_exe_path(), path + 1);
} else {
strncpy(buf, path, bufsiz);
buf[bufsiz-1] = 0;
}
// change all backslashes
for (char *p = buf; *p; ++p)
if (*p == '\\') *p = '/';
return buf;
}
/* these operate on the real file system */
bool fs_sys_file_exists(const char *name) {
struct stat st;
return (stat(name, &st) == 0 && S_ISREG(st.st_mode));
}
bool fs_sys_dir_exists(const char *name) {
struct stat st;
return (stat(name, &st) == 0 && S_ISDIR(st.st_mode));
}
bool fs_sys_walk(const char *base, walk_fn_t walk, void *user, const bool recur) {
char fullpath[SYS_MAX_PATH];
DIR *dir;
struct dirent *ent;
if (!(dir = opendir(base))) {
fprintf(stderr, "fs_dir_walk(): could not open `%s`\n", base);
return false;
}
bool ret = true;
while ((ent = readdir(dir)) != NULL) {
if (ent->d_name[0] == 0 || ent->d_name[0] == '.') continue; // skip ./.. and hidden files
snprintf(fullpath, sizeof(fullpath), "%s/%s", base, ent->d_name);
if (fs_sys_dir_exists(fullpath)) {
if (recur) {
if (!fs_sys_walk(fullpath, walk, user, recur)) {
ret = false;
break;
}
}
} else {
if (!walk(user, fullpath)) {
ret = false;
break;
}
}
}
closedir(dir);
return ret;
}
fs_pathlist_t fs_sys_enumerate(const char *base, const bool recur) {
char **paths = malloc(sizeof(char *) * 32);
fs_pathlist_t pathlist = { paths, 0, 32 };
if (!paths) return pathlist;
if (!fs_sys_walk(base, enumerate_walk, &pathlist, recur))
fs_pathlist_free(&pathlist);
return pathlist;
}
bool fs_sys_mkdir(const char *name) {
#ifdef _WIN32
return _mkdir(name) == 0;
#else
return mkdir(name, 0777) == 0;
#endif
}

135
src/pc/fs/fs.h Normal file
View file

@ -0,0 +1,135 @@
#ifndef _SM64_FS_H_
#define _SM64_FS_H_
#include <stdlib.h>
#include <stdbool.h>
#include <stdint.h>
#include <stddef.h>
#include "../platform.h"
// FS_BASEDIR is usually defined in the build script
#ifndef FS_BASEDIR
# define FS_BASEDIR "res"
#endif
#ifndef FS_BASEPACK_PREFIX
# define FS_BASEPACK_PREFIX "base"
#endif
#define FS_TEXTUREDIR "gfx"
#define FS_SOUNDDIR "sound"
extern char fs_gamedir[];
extern char fs_writepath[];
// receives the full path
// should return `true` if traversal should continue
// first arg is user data
typedef bool (*walk_fn_t)(void *, const char *);
typedef enum {
FS_WALK_SUCCESS = 0,
FS_WALK_INTERRUPTED = 1,
FS_WALK_NOTFOUND = 2,
FS_WALK_ERROR = 4,
} fs_walk_result_t;
// opaque searchpath directory type
typedef struct fs_dir_s fs_dir_t;
// virtual file handle
typedef struct fs_file_s {
void *handle; // opaque packtype-defined data
fs_dir_t *parent; // directory containing this file
} fs_file_t;
// list of paths; returned by fs_enumerate()
typedef struct {
char **paths;
int numpaths;
int listcap;
} fs_pathlist_t;
typedef struct {
const char *extension; // file extensions of this pack type
void *(*mount)(const char *rpath); // open and initialize pack at real path `rpath`
void (*unmount)(void *pack); // free pack
// walks the specified directory inside this pack, calling walkfn for each file
// returns FS_WALK_SUCCESS if the directory was successfully opened and walk() didn't ever return false
// returns FS_WALK_INTERRUPTED if the traversal started but walk() returned false at some point
// if recur is true, will recurse into subfolders
fs_walk_result_t (*walk)(void *pack, const char *base, walk_fn_t walkfn, void *user, const bool recur);
bool (*is_file)(void *pack, const char *path); // returns true if `path` exists in this pack and is a file
bool (*is_dir)(void *pack, const char *path); // returns true if `path` exists in this pack and is a directory
// file I/O functions; paths are virtual
fs_file_t *(*open)(void *pack, const char *path); // opens a virtual file contained in this pack for reading, returns NULL in case of error
int64_t (*read)(void *pack, fs_file_t *file, void *buf, const uint64_t size); // returns -1 in case of error
bool (*seek)(void *pack, fs_file_t *file, const int64_t ofs); // returns true if seek succeeded
int64_t (*tell)(void *pack, fs_file_t *file); // returns -1 in case of error, current virtual file position otherwise
int64_t (*size)(void *pack, fs_file_t *file); // returns -1 in case of error, size of the (uncompressed) file otherwise
bool (*eof)(void *pack, fs_file_t *file); // returns true if there's nothing more to read
void (*close)(void *pack, fs_file_t *file); // closes a virtual file previously opened with ->open()
} fs_packtype_t;
// takes the supplied NULL-terminated list of read-only directories and mounts all the packs in them,
// then mounts the directories themselves, then mounts all the packs in `gamedir`, then mounts `gamedir` itself,
// then does the same with `userdir`
// initializes the `fs_gamedir` and `fs_userdir` variables
bool fs_init(const char **rodirs, const char *gamedir, const char *userdir);
// mounts the pack at physical path `realpath` to the root of the filesystem
// packs mounted later take priority over packs mounted earlier
bool fs_mount(const char *realpath);
// removes the pack at physical path from the virtual filesystem
bool fs_unmount(const char *realpath);
/* generalized filesystem functions that call matching packtype functions for each pack in the searchpath */
// FIXME: this can walk in unorthodox patterns, since it goes through mountpoints linearly
fs_walk_result_t fs_walk(const char *base, walk_fn_t walkfn, void *user, const bool recur);
// returns a list of files in the `base` directory
fs_pathlist_t fs_enumerate(const char *base, const bool recur);
// call this on a list returned by fs_enumerate() to free it
void fs_pathlist_free(fs_pathlist_t *pathlist);
bool fs_is_file(const char *fname);
bool fs_is_dir(const char *fname);
fs_file_t *fs_open(const char *vpath);
void fs_close(fs_file_t *file);
int64_t fs_read(fs_file_t *file, void *buf, const uint64_t size);
const char *fs_readline(fs_file_t *file, char *dst, const uint64_t size);
bool fs_seek(fs_file_t *file, const int64_t ofs);
int64_t fs_tell(fs_file_t *file);
int64_t fs_size(fs_file_t *file);
bool fs_eof(fs_file_t *file);
void *fs_load_file(const char *vpath, uint64_t *outsize);
const char *fs_readline(fs_file_t *file, char *dst, uint64_t size);
// tries to find the first file with the filename that starts with `prefix`
// puts full filename into `outname` and returns it or returns NULL if nothing matches
const char *fs_match(char *outname, const size_t outlen, const char *prefix);
// takes a virtual path and prepends the write path to it
const char *fs_get_write_path(const char *vpath);
// expands special chars in paths and changes backslashes to forward slashes
const char *fs_convert_path(char *buf, const size_t bufsiz, const char *path);
/* these operate on the real filesystem and are used by fs_packtype_dir */
bool fs_sys_walk(const char *base, walk_fn_t walk, void *user, const bool recur);
fs_pathlist_t fs_sys_enumerate(const char *base, const bool recur);
bool fs_sys_file_exists(const char *name);
bool fs_sys_dir_exists(const char *name);
bool fs_sys_mkdir(const char *name); // creates with 0777 by default
#endif // _SM64_FS_H_

117
src/pc/fs/fs_packtype_dir.c Normal file
View file

@ -0,0 +1,117 @@
#include <stdlib.h>
#include <stdbool.h>
#include <stdint.h>
#include <stddef.h>
#include <stdio.h>
#include "macros.h"
#include "../platform.h"
#include "fs.h"
static void *pack_dir_mount(const char *realpath) {
if (!fs_sys_dir_exists(realpath))
return NULL;
// the pack is actually just the real folder path
void *pack = (void *)sys_strdup(realpath);
return pack;
}
static void pack_dir_unmount(void *pack) {
free(pack);
}
struct walkdata_s {
size_t baselen;
walk_fn_t userwalk;
void *userdata;
};
// wrap the actual user walk function to return virtual paths instead of real paths
static bool packdir_walkfn(void *userdata, const char *path) {
struct walkdata_s *walk = (struct walkdata_s *)userdata;
return walk->userwalk(walk->userdata, path + walk->baselen);
}
static fs_walk_result_t pack_dir_walk(void *pack, const char *base, walk_fn_t walkfn, void *user, const bool recur) {
char path[SYS_MAX_PATH];
snprintf(path, SYS_MAX_PATH, "%s/%s", (const char *)pack, base);
if (!fs_sys_dir_exists(path))
return FS_WALK_NOTFOUND;
struct walkdata_s walkdata = { strlen((const char *)pack) + 1, walkfn, user };
return fs_sys_walk(path, packdir_walkfn, &walkdata, recur);
}
static bool pack_dir_is_file(void *pack, const char *fname) {
char path[SYS_MAX_PATH];
snprintf(path, sizeof(path), "%s/%s", (const char *)pack, fname);
return fs_sys_dir_exists(path);
}
static bool pack_dir_is_dir(void *pack, const char *fname) {
char path[SYS_MAX_PATH];
snprintf(path, sizeof(path), "%s/%s", (const char *)pack, fname);
return fs_sys_file_exists(path);
}
static fs_file_t *pack_dir_open(void *pack, const char *vpath) {
char path[SYS_MAX_PATH];
snprintf(path, sizeof(path), "%s/%s", (const char *)pack, vpath);
FILE *f = fopen(path, "rb");
if (!f) return NULL;
fs_file_t *fsfile = malloc(sizeof(fs_file_t));
if (!fsfile) { fclose(f); return NULL; }
fsfile->parent = NULL;
fsfile->handle = f;
return fsfile;
}
static void pack_dir_close(UNUSED void *pack, fs_file_t *file) {
fclose((FILE *)file->handle);
free(file);
}
static int64_t pack_dir_read(UNUSED void *pack, fs_file_t *file, void *buf, const uint64_t size) {
return fread(buf, 1, size, (FILE *)file->handle);
}
static bool pack_dir_seek(UNUSED void *pack, fs_file_t *file, const int64_t ofs) {
return fseek((FILE *)file->handle, ofs, SEEK_SET) == 0;
}
static int64_t pack_dir_tell(UNUSED void *pack, fs_file_t *file) {
return ftell((FILE *)file->handle);
}
static int64_t pack_dir_size(UNUSED void *pack, fs_file_t *file) {
int64_t oldofs = ftell((FILE *)file->handle);
fseek((FILE *)file->handle, 0, SEEK_END);
int64_t size = ftell((FILE *)file->handle);
fseek((FILE *)file->handle, oldofs, SEEK_SET);
return size;
}
static bool pack_dir_eof(UNUSED void *pack, fs_file_t *file) {
return feof((FILE *)file->handle);
}
fs_packtype_t fs_packtype_dir = {
"",
pack_dir_mount,
pack_dir_unmount,
pack_dir_walk,
pack_dir_is_file,
pack_dir_is_dir,
pack_dir_open,
pack_dir_read,
pack_dir_seek,
pack_dir_tell,
pack_dir_size,
pack_dir_eof,
pack_dir_close,
};

486
src/pc/fs/fs_packtype_zip.c Normal file
View file

@ -0,0 +1,486 @@
#include <stdlib.h>
#include <stdbool.h>
#include <stdint.h>
#include <stddef.h>
#include <stdio.h>
#include <string.h>
#include <tinfl.h>
#include "macros.h"
#include "../platform.h"
#include "fs.h"
#include "dirtree.h"
#define ZIP_BUFSIZE 16384
#define ZIP_EOCD_BUFSIZE 65578
#define ZIP_LFH_SIG 0x04034b50
#define ZIP_CDH_SIG 0x02014b50
#define ZIP_EOCD_SIG 0x06054b50
typedef struct {
fs_dirtree_t tree; // this should always be first, so this could be used as a dirtree root
const char *realpath; // physical path to the zip file
FILE *zipf; // open zip file handle, if any
} zip_pack_t;
typedef struct {
fs_dirtree_entry_t tree; // this should always be first, so this could be used as a dirtree entry
uint64_t ofs; // offset to compressed data in zip
uint16_t bits; // general purpose zip flags
uint16_t comptype; // compression method
uint32_t crc; // CRC-32
uint64_t comp_size; // size of compressed data in zip
uint64_t uncomp_size; // size of decompressed data
uint16_t attr_int; // internal attributes
uint32_t attr_ext; // external attributes
bool ofs_fixed; // if true, `ofs` points to the file data, otherwise to LFH
} zip_entry_t;
typedef struct {
zip_entry_t *entry; // the dirtree entry of this file
uint32_t comp_pos; // read position in compressed data
uint32_t uncomp_pos; // read position in uncompressed data
uint8_t *buffer; // decompression buffer (if compressed)
z_stream zstream; // tinfl zlib stream
FILE *fstream; // duplicate of zipf of the parent zip file
} zip_file_t;
static int64_t zip_find_eocd(FILE *f, int64_t *outlen) {
// the EOCD is somewhere in the last 65557 bytes of the file
// get the total file size
fseek(f, 0, SEEK_END);
const int64_t fsize = ftell(f);
if (fsize <= 16) return -1; // probably not a zip
const int64_t rx = (fsize < ZIP_EOCD_BUFSIZE ? fsize : ZIP_EOCD_BUFSIZE);
uint8_t *buf = malloc(rx);
if (!buf) return -1;
// read that entire chunk and search for EOCD backwards from the end
fseek(f, fsize - rx, SEEK_SET);
if (fread(buf, rx, 1, f)) {
for (int64_t i = rx - 8; i >= 0; --i) {
if ((buf[i + 0] == 0x50) && (buf[i + 1] == 0x4B) &&
(buf[i + 2] == 0x05) && (buf[i + 3] == 0x06)) {
// gotem
free(buf);
if (outlen) *outlen = fsize;
return fsize - rx + i;
}
}
}
free(buf);
return -1;
}
static bool zip_parse_eocd(FILE *f, uint64_t *cdir_ofs, uint64_t *data_ofs, uint64_t *count) {
int64_t fsize = 0;
// EOCD record struct
struct eocd_s {
uint32_t sig;
uint16_t this_disk;
uint16_t cdir_disk;
uint16_t disk_entry_count;
uint16_t total_entry_count;
uint32_t cdir_size;
uint32_t cdir_ofs;
uint16_t comment_len;
// zip comment follows
} __attribute__((__packed__));
struct eocd_s eocd;
// find the EOCD and seek to it
int64_t pos = zip_find_eocd(f, &fsize);
if (pos < 0) return false;
fseek(f, pos, SEEK_SET);
// read it
if (!fread(&eocd, sizeof(eocd), 1, f)) return false;
// double check the sig
if (LE_TO_HOST32(eocd.sig) != ZIP_EOCD_SIG) return false;
// disks should all be 0
if (eocd.this_disk || eocd.cdir_disk) return false;
// total entry count should be the same as disk entry count
if (eocd.disk_entry_count != eocd.total_entry_count) return false;
*count = LE_TO_HOST16(eocd.total_entry_count);
*cdir_ofs = LE_TO_HOST32(eocd.cdir_ofs);
eocd.cdir_size = LE_TO_HOST32(eocd.cdir_size);
// end of central dir can't be before central dir
if ((uint64_t)pos < *cdir_ofs + eocd.cdir_size) return false;
*data_ofs = (uint64_t)(pos - (*cdir_ofs + eocd.cdir_size));
*cdir_ofs += *data_ofs;
// make sure end of comment matches end of file
eocd.comment_len = LE_TO_HOST16(eocd.comment_len);
return ((pos + 22 + eocd.comment_len) == fsize);
}
static bool zip_fixup_offset(zip_file_t *zipfile) {
// LFH record struct
struct lfh_s {
uint32_t sig;
uint16_t version_required;
uint16_t bits;
uint16_t comptype;
uint16_t mod_time;
uint16_t mod_date;
uint32_t crc;
uint32_t comp_size;
uint32_t uncomp_size;
uint16_t fname_len;
uint16_t extra_len;
// file name, extra field and data follow
} __attribute__((__packed__));
struct lfh_s lfh;
zip_entry_t *ent = zipfile->entry;
fseek(zipfile->fstream, ent->ofs, SEEK_SET);
if (!fread(&lfh, sizeof(lfh), 1, zipfile->fstream)) return false;
// we only need these two
lfh.fname_len = LE_TO_HOST16(lfh.fname_len);
lfh.extra_len = LE_TO_HOST16(lfh.extra_len);
// ofs will now point to actual data
ent->ofs += sizeof(lfh) + lfh.fname_len + lfh.extra_len;
ent->ofs_fixed = true; // only need to do this once
return true;
}
static zip_entry_t *zip_load_entry(FILE *f, fs_dirtree_t *tree, const uint64_t data_ofs) {
// CDH record struct
struct cdh_s {
uint32_t sig;
uint16_t version_used;
uint16_t version_required;
uint16_t bits;
uint16_t comptype;
uint16_t mod_time;
uint16_t mod_date;
uint32_t crc;
uint32_t comp_size;
uint32_t uncomp_size;
uint16_t fname_len;
uint16_t extra_len;
uint16_t comment_len;
uint16_t start_disk;
uint16_t attr_int;
uint32_t attr_ext;
uint32_t lfh_ofs;
// file name, extra field and comment follow
} __attribute__((__packed__));
struct cdh_s cdh;
zip_entry_t zipent;
memset(&zipent, 0, sizeof(zipent));
if (!fread(&cdh, sizeof(cdh), 1, f)) return NULL;
// check cdir entry header signature
if (LE_TO_HOST32(cdh.sig) != ZIP_CDH_SIG) return NULL;
// byteswap and copy some important fields
zipent.bits = LE_TO_HOST16(cdh.bits);
zipent.comptype = LE_TO_HOST16(cdh.comptype);
zipent.crc = LE_TO_HOST32(cdh.crc);
zipent.comp_size = LE_TO_HOST32(cdh.comp_size);
zipent.uncomp_size = LE_TO_HOST32(cdh.uncomp_size);
zipent.ofs = LE_TO_HOST32(cdh.lfh_ofs);
zipent.attr_int = LE_TO_HOST16(cdh.attr_int);
zipent.attr_ext = LE_TO_HOST32(cdh.attr_ext);
cdh.fname_len = LE_TO_HOST16(cdh.fname_len);
cdh.comment_len = LE_TO_HOST16(cdh.comment_len);
cdh.extra_len = LE_TO_HOST16(cdh.extra_len);
// read the name
char *name = calloc(1, cdh.fname_len + 1);
if (!name) return NULL;
if (!fread(name, cdh.fname_len, 1, f)) { free(name); return NULL; }
// this is a directory if the name ends in a path separator
bool is_dir = false;
if (name[cdh.fname_len - 1] == '/') {
is_dir = true;
name[cdh.fname_len - 1] = 0;
}
name[cdh.fname_len] = 0;
// add to directory tree
zip_entry_t *retent = (zip_entry_t *)fs_dirtree_add(tree, name, is_dir);
free(name);
if (!retent) return NULL;
// copy the data we read into the new entry
zipent.tree = retent->tree;
memcpy(retent, &zipent, sizeof(zipent));
// this points to the LFH now; will be fixed up on file open
// while the CDH includes an "extra field length" field, it's usually different
retent->ofs += data_ofs;
// skip to the next CDH
fseek(f, cdh.extra_len + cdh.comment_len, SEEK_CUR);
return retent;
}
static inline bool zip_load_entries(FILE *f, fs_dirtree_t *tree, const uint64_t cdir_ofs, const uint64_t data_ofs, const uint64_t count) {
fseek(f, cdir_ofs, SEEK_SET);
for (uint64_t i = 0; i < count; ++i) {
if (!zip_load_entry(f, tree, data_ofs))
return false;
}
return true;
}
static inline bool is_zip(FILE *f) {
uint32_t sig = 0;
if (fread(&sig, sizeof(sig), 1, f)) {
// the first LFH might be at the start of the zip
if (LE_TO_HOST32(sig) == ZIP_LFH_SIG)
return true;
// no signature, might still be a zip because fuck you
// the only way now is to try and find the end of central directory
return zip_find_eocd(f, NULL) >= 0;
}
return false;
}
static void *pack_zip_mount(const char *realpath) {
uint64_t cdir_ofs, data_ofs, count;
zip_pack_t *pack = NULL;
FILE *f = NULL;
f = fopen(realpath, "rb");
if (!f) goto _fail;
if (!is_zip(f)) goto _fail;
pack = calloc(1, sizeof(zip_pack_t));
if (!pack) goto _fail;
if (!zip_parse_eocd(f, &cdir_ofs, &data_ofs, &count))
goto _fail;
if (!fs_dirtree_init(&pack->tree, sizeof(zip_entry_t)))
goto _fail;
if (!zip_load_entries(f, &pack->tree, cdir_ofs, data_ofs, count))
goto _fail;
pack->realpath = sys_strdup(realpath);
pack->zipf = f;
return pack;
_fail:
if (f) fclose(f);
if (pack) free(pack);
return NULL;
}
static void pack_zip_unmount(void *pack) {
zip_pack_t *zip = (zip_pack_t *)pack;
fs_dirtree_free(&zip->tree);
if (zip->realpath) free((void *)zip->realpath);
if (zip->zipf) fclose(zip->zipf);
free(zip);
}
static bool pack_zip_is_file(void *pack, const char *fname) {
zip_entry_t *ent = (zip_entry_t *)fs_dirtree_find((fs_dirtree_t *)pack, fname);
return ent && !ent->tree.is_dir;
}
static bool pack_zip_is_dir(void *pack, const char *fname) {
zip_entry_t *ent = (zip_entry_t *)fs_dirtree_find((fs_dirtree_t *)pack, fname);
return ent && ent->tree.is_dir;
}
static inline void pack_zip_close_zipfile(zip_file_t *zipfile) {
if (zipfile->buffer) {
inflateEnd(&zipfile->zstream);
free(zipfile->buffer);
}
if (zipfile->fstream) fclose(zipfile->fstream);
free(zipfile);
}
static fs_file_t *pack_zip_open(void *pack, const char *vpath) {
fs_file_t *fsfile = NULL;
zip_file_t *zipfile = NULL;
zip_pack_t *zip = (zip_pack_t *)pack;
zip_entry_t *ent = (zip_entry_t *)fs_dirtree_find((fs_dirtree_t *)zip, vpath);
if (!ent || ent->tree.is_dir) goto _fail; // we're expecting a fucking file here
zipfile = calloc(1, sizeof(zip_file_t));
if (!zipfile) goto _fail;
zipfile->entry = ent;
// obtain an additional file descriptor
// fdopen(dup(fileno())) is not very portable and might not create separate state
zipfile->fstream = fopen(zip->realpath, "rb");
if (!zipfile->fstream) goto _fail;
// make ent->ofs point to the actual file data if it doesn't already
if (!ent->ofs_fixed)
if (!zip_fixup_offset(zipfile))
goto _fail; // this shouldn't generally happen but oh well
// if there's compression, assume it's zlib
if (ent->comptype != 0) {
zipfile->buffer = malloc(ZIP_BUFSIZE);
if (!zipfile->buffer)
goto _fail;
if (inflateInit2(&zipfile->zstream, -MAX_WBITS) != Z_OK)
goto _fail;
}
fsfile = malloc(sizeof(fs_file_t));
if (!fsfile) goto _fail;
fsfile->handle = zipfile;
fsfile->parent = NULL;
// point to the start of the file data
fseek(zipfile->fstream, ent->ofs, SEEK_SET);
return fsfile;
_fail:
if (zipfile) pack_zip_close_zipfile(zipfile);
if (fsfile) free(fsfile);
return NULL;
}
static void pack_zip_close(UNUSED void *pack, fs_file_t *file) {
if (!file) return;
zip_file_t *zipfile = (zip_file_t *)file->handle;
if (zipfile) pack_zip_close_zipfile(zipfile);
free(file);
}
static int64_t pack_zip_read(UNUSED void *pack, fs_file_t *file, void *buf, const uint64_t size) {
zip_file_t *zipfile = (zip_file_t *)file->handle;
zip_entry_t *ent = zipfile->entry;
int64_t avail = ent->uncomp_size - zipfile->uncomp_pos;
int64_t max_read = ((int64_t)size > avail) ? avail : (int64_t)size;
int64_t rx = 0;
int err = 0;
if (max_read == 0) return 0;
if (ent->comptype == 0) {
// no compression, just read
rx = fread(buf, 1, size, zipfile->fstream);
} else {
zipfile->zstream.next_out = buf;
zipfile->zstream.avail_out = (unsigned int)max_read;
while (rx < max_read) {
const uint32_t before = (uint32_t)zipfile->zstream.total_out;
// check if we ran out of compressed bytes and read more if we did
if (zipfile->zstream.avail_in == 0) {
int32_t comp_rx = ent->comp_size - zipfile->comp_pos;
if (comp_rx > 0) {
if (comp_rx > ZIP_BUFSIZE) comp_rx = ZIP_BUFSIZE;
comp_rx = fread(zipfile->buffer, 1, comp_rx, zipfile->fstream);
if (comp_rx == 0) break;
zipfile->comp_pos += (uint32_t)comp_rx;
zipfile->zstream.next_in = zipfile->buffer;
zipfile->zstream.avail_in = (unsigned int)comp_rx;
}
}
// inflate
err = inflate(&zipfile->zstream, Z_SYNC_FLUSH);
rx += zipfile->zstream.total_out - before;
if (err != Z_OK) break;
}
}
zipfile->uncomp_pos += rx;
return rx;
}
static bool pack_zip_seek(UNUSED void *pack, fs_file_t *file, const int64_t ofs) {
zip_file_t *zipfile = (zip_file_t *)file->handle;
zip_entry_t *ent = zipfile->entry;
uint8_t buf[512];
if (ofs > (int64_t)ent->uncomp_size) return false;
if (ent->comptype == 0) {
if (fseek(zipfile->fstream, ofs + ent->ofs, SEEK_SET) == 0)
zipfile->uncomp_pos = ofs;
} else {
// if seeking backwards, gotta redecode the stream from the start until that point
// so we make a copy of the zstream and clear it with a new one
if (ofs < zipfile->uncomp_pos) {
z_stream zstream;
memset(&zstream, 0, sizeof(zstream));
if (inflateInit2(&zstream, -MAX_WBITS) != Z_OK)
return false;
// reset the underlying file handle back to the start
if (fseek(zipfile->fstream, ent->ofs, SEEK_SET) != 0)
return false;
// free and replace the old one
inflateEnd(&zipfile->zstream);
memcpy(&zipfile->zstream, &zstream, sizeof(zstream));
zipfile->uncomp_pos = zipfile->comp_pos = 0;
}
// continue decoding the stream until we hit the new offset
while (zipfile->uncomp_pos != ofs) {
uint32_t max_read = (uint32_t)(ofs - zipfile->uncomp_pos);
if (max_read > sizeof(buf)) max_read = sizeof(buf);
if (pack_zip_read(pack, file, buf, max_read) != max_read)
return false;
}
}
return true;
}
static int64_t pack_zip_tell(UNUSED void *pack, fs_file_t *file) {
return ((zip_file_t *)file->handle)->uncomp_pos;
}
static int64_t pack_zip_size(UNUSED void *pack, fs_file_t *file) {
zip_file_t *zipfile = (zip_file_t *)file->handle;
return zipfile->entry->uncomp_size;
}
static bool pack_zip_eof(UNUSED void *pack, fs_file_t *file) {
zip_file_t *zipfile = (zip_file_t *)file->handle;
return zipfile->uncomp_pos >= zipfile->entry->uncomp_size;
}
fs_packtype_t fs_packtype_zip = {
"zip",
pack_zip_mount,
pack_zip_unmount,
fs_dirtree_walk,
pack_zip_is_file,
pack_zip_is_dir,
pack_zip_open,
pack_zip_read,
pack_zip_seek,
pack_zip_tell,
pack_zip_size,
pack_zip_eof,
pack_zip_close,
};

View file

@ -33,6 +33,7 @@
#endif #endif
#include "../platform.h"
#include "gfx_cc.h" #include "gfx_cc.h"
#include "gfx_rendering_api.h" #include "gfx_rendering_api.h"
@ -317,7 +318,7 @@ static struct ShaderProgram *gfx_opengl_create_and_load_new_shader(uint32_t shad
fprintf(stderr, "Vertex shader compilation failed\n"); fprintf(stderr, "Vertex shader compilation failed\n");
glGetShaderInfoLog(vertex_shader, max_length, &max_length, &error_log[0]); glGetShaderInfoLog(vertex_shader, max_length, &max_length, &error_log[0]);
fprintf(stderr, "%s\n", &error_log[0]); fprintf(stderr, "%s\n", &error_log[0]);
abort(); sys_fatal("vertex shader compilation failed (see terminal)");
} }
GLuint fragment_shader = glCreateShader(GL_FRAGMENT_SHADER); GLuint fragment_shader = glCreateShader(GL_FRAGMENT_SHADER);
@ -331,7 +332,7 @@ static struct ShaderProgram *gfx_opengl_create_and_load_new_shader(uint32_t shad
fprintf(stderr, "Fragment shader compilation failed\n"); fprintf(stderr, "Fragment shader compilation failed\n");
glGetShaderInfoLog(fragment_shader, max_length, &max_length, &error_log[0]); glGetShaderInfoLog(fragment_shader, max_length, &max_length, &error_log[0]);
fprintf(stderr, "%s\n", &error_log[0]); fprintf(stderr, "%s\n", &error_log[0]);
abort(); sys_fatal("fragment shader compilation failed (see terminal)");
} }
GLuint shader_program = glCreateProgram(); GLuint shader_program = glCreateProgram();

View file

@ -40,6 +40,7 @@ static PFNMGLFOGCOORDPOINTERPROC mglFogCoordPointer = NULL;
#define GL_FOG_COORD 0x8451 #define GL_FOG_COORD 0x8451
#define GL_FOG_COORD_ARRAY 0x8457 #define GL_FOG_COORD_ARRAY 0x8457
#include "../platform.h"
#include "gfx_cc.h" #include "gfx_cc.h"
#include "gfx_rendering_api.h" #include "gfx_rendering_api.h"
#include "macros.h" #include "macros.h"
@ -516,17 +517,16 @@ static void gfx_opengl_init(void) {
int vmajor, vminor; int vmajor, vminor;
bool is_es = false; bool is_es = false;
gl_get_version(&vmajor, &vminor, &is_es); gl_get_version(&vmajor, &vminor, &is_es);
if (vmajor < 2 && vminor < 2 && !is_es) { if (vmajor < 2 && vminor < 2 && !is_es)
fprintf(stderr, "OpenGL 1.2+ is required. Reported version: %s%d.%d\n", is_es ? "ES" : "", vmajor, vminor); sys_fatal("OpenGL 1.2+ is required. Reported version: %s%d.%d\n", is_es ? "ES" : "", vmajor, vminor);
abort();
}
// check extensions that we need // check extensions that we need
const bool supported = const bool supported =
gl_check_ext("GL_ARB_multitexture") && gl_check_ext("GL_ARB_multitexture") &&
gl_check_ext("GL_ARB_texture_env_combine"); gl_check_ext("GL_ARB_texture_env_combine");
if (!supported) abort(); if (!supported)
sys_fatal("required GL extensions are not supported");
gl_adv_fog = false; gl_adv_fog = false;

View file

@ -26,6 +26,7 @@
#include "../platform.h" #include "../platform.h"
#include "../configfile.h" #include "../configfile.h"
#include "../fs/fs.h"
#define SUPPORT_CHECK(x) assert(x) #define SUPPORT_CHECK(x) assert(x)
@ -494,6 +495,28 @@ static void import_texture_ci8(int tile) {
#else // EXTERNAL_DATA #else // EXTERNAL_DATA
static inline void load_texture(const char *fullpath) {
int w, h;
u64 imgsize = 0;
u8 *imgdata = fs_load_file(fullpath, &imgsize);
if (!imgdata) {
fprintf(stderr, "could not open texture: `%s`\n", fullpath);
return;
}
// TODO: implement stbi_callbacks or some shit instead of loading the whole texture
u8 *data = stbi_load_from_memory(imgdata, imgsize, &w, &h, NULL, 4);
free(imgdata);
if (!data) {
fprintf(stderr, "could not load texture: `%s`\n", fullpath);
return;
}
gfx_rapi->upload_texture(data, w, h);
stbi_image_free(data); // don't need this anymore
}
// this is taken straight from n64graphics // this is taken straight from n64graphics
static bool texname_to_texformat(const char *name, u8 *fmt, u8 *siz) { static bool texname_to_texformat(const char *name, u8 *fmt, u8 *siz) {
static const struct { static const struct {
@ -531,7 +554,7 @@ static bool texname_to_texformat(const char *name, u8 *fmt, u8 *siz) {
// calls import_texture() on every texture in the res folder // calls import_texture() on every texture in the res folder
// we can get the format and size from the texture files // we can get the format and size from the texture files
// and then cache them using gfx_texture_cache_lookup // and then cache them using gfx_texture_cache_lookup
static bool preload_texture(const char *path) { static bool preload_texture(void *user, const char *path) {
// strip off the extension // strip off the extension
char texname[SYS_MAX_PATH]; char texname[SYS_MAX_PATH];
strncpy(texname, path, sizeof(texname)); strncpy(texname, path, sizeof(texname));
@ -546,55 +569,20 @@ static bool preload_texture(const char *path) {
return true; // just skip it, might be a stray skybox or something return true; // just skip it, might be a stray skybox or something
} }
// strip off the data path char *actualname = texname;
const char *datapath = sys_data_path(); // strip off the prefix // TODO: make a fs_ function for this shit
const unsigned int datalen = strlen(datapath); if (!strncmp(FS_TEXTUREDIR "/", actualname, 4)) actualname += 4;
const char *actualname = (!strncmp(texname, datapath, datalen)) ?
texname + datalen + 1 : texname;
// skip any separators
while (*actualname == '/' || *actualname == '\\') ++actualname;
// this will be stored in the hashtable, so make a copy // this will be stored in the hashtable, so make a copy
actualname = sys_strdup(actualname); actualname = sys_strdup(actualname);
assert(actualname); assert(actualname);
struct TextureHashmapNode *n; struct TextureHashmapNode *n;
if (!gfx_texture_cache_lookup(0, &n, actualname, fmt, siz)) { if (!gfx_texture_cache_lookup(0, &n, actualname, fmt, siz))
// new texture, load it load_texture(path); // new texture, load it
int w, h;
u8 *data = stbi_load(path, &w, &h, NULL, 4);
if (!data) {
fprintf(stderr, "could not load texture: `%s`\n", path);
return false;
}
// upload it
gfx_rapi->upload_texture(data, w, h);
stbi_image_free(data);
}
return true; return true;
} }
static inline void load_texture(const char *name) {
static char fpath[SYS_MAX_PATH];
int w, h;
const char *texname = name;
if (!texname[0]) {
fprintf(stderr, "empty texture name at %p\n", texname);
return;
}
snprintf(fpath, sizeof(fpath), "%s/%s.png", sys_data_path(), texname);
u8 *data = stbi_load(fpath, &w, &h, NULL, 4);
if (!data) {
fprintf(stderr, "could not load texture: `%s`\n", fpath);
return;
}
gfx_rapi->upload_texture(data, w, h);
stbi_image_free(data); // don't need this anymore
}
#endif // EXTERNAL_DATA #endif // EXTERNAL_DATA
static void import_texture(int tile) { static void import_texture(int tile) {
@ -614,7 +602,9 @@ static void import_texture(int tile) {
#ifdef EXTERNAL_DATA #ifdef EXTERNAL_DATA
// the "texture data" is actually a C string with the path to our texture in it // the "texture data" is actually a C string with the path to our texture in it
// load it from an external image in our data path // load it from an external image in our data path
load_texture((const char*)rdp.loaded_texture[tile].addr); char texname[SYS_MAX_PATH];
snprintf(texname, sizeof(texname), FS_TEXTUREDIR "/%s.png", (const char*)rdp.loaded_texture[tile].addr);
load_texture(texname);
#else #else
// the texture data is actual texture data // the texture data is actual texture data
int t0 = get_time(); int t0 = get_time();
@ -625,7 +615,7 @@ static void import_texture(int tile) {
else if (siz == G_IM_SIZ_16b) { else if (siz == G_IM_SIZ_16b) {
import_texture_rgba16(tile); import_texture_rgba16(tile);
} else { } else {
abort(); sys_fatal("unsupported RGBA texture size: %u", siz);
} }
} else if (fmt == G_IM_FMT_IA) { } else if (fmt == G_IM_FMT_IA) {
if (siz == G_IM_SIZ_4b) { if (siz == G_IM_SIZ_4b) {
@ -635,7 +625,7 @@ static void import_texture(int tile) {
} else if (siz == G_IM_SIZ_16b) { } else if (siz == G_IM_SIZ_16b) {
import_texture_ia16(tile); import_texture_ia16(tile);
} else { } else {
abort(); sys_fatal("unsupported IA texture size: %u", siz);
} }
} else if (fmt == G_IM_FMT_CI) { } else if (fmt == G_IM_FMT_CI) {
if (siz == G_IM_SIZ_4b) { if (siz == G_IM_SIZ_4b) {
@ -643,7 +633,7 @@ static void import_texture(int tile) {
} else if (siz == G_IM_SIZ_8b) { } else if (siz == G_IM_SIZ_8b) {
import_texture_ci8(tile); import_texture_ci8(tile);
} else { } else {
abort(); sys_fatal("unsupported CI texture size: %u", siz);
} }
} else if (fmt == G_IM_FMT_I) { } else if (fmt == G_IM_FMT_I) {
if (siz == G_IM_SIZ_4b) { if (siz == G_IM_SIZ_4b) {
@ -651,10 +641,10 @@ static void import_texture(int tile) {
} else if (siz == G_IM_SIZ_8b) { } else if (siz == G_IM_SIZ_8b) {
import_texture_i8(tile); import_texture_i8(tile);
} else { } else {
abort(); sys_fatal("unsupported I texture size: %u", siz);
} }
} else { } else {
abort(); sys_fatal("unsupported texture format: %u", fmt);
} }
int t1 = get_time(); int t1 = get_time();
//printf("Time diff: %d\n", t1 - t0); //printf("Time diff: %d\n", t1 - t0);
@ -1758,18 +1748,18 @@ void gfx_init(struct GfxWindowManagerAPI *wapi, struct GfxRenderingAPI *rapi) {
0x05200200, 0x05200200,
0x03200200 0x03200200
}; };
for (size_t i = 0; i < sizeof(precomp_shaders) / sizeof(uint32_t); i++) {
for (size_t i = 0; i < sizeof(precomp_shaders) / sizeof(uint32_t); i++)
gfx_lookup_or_create_shader_program(precomp_shaders[i]); gfx_lookup_or_create_shader_program(precomp_shaders[i]);
}
#ifdef EXTERNAL_DATA
// preload all textures if needed
if (configPrecacheRes) {
printf("Precaching textures from `%s`\n", sys_data_path());
sys_dir_walk(sys_data_path(), preload_texture, true);
}
#endif
} }
#ifdef EXTERNAL_DATA
void gfx_precache_textures(void) {
// preload all textures
fs_walk(FS_TEXTUREDIR, preload_texture, NULL, true);
}
#endif
void gfx_start_frame(void) { void gfx_start_frame(void) {
gfx_wapi->handle_events(); gfx_wapi->handle_events();
gfx_wapi->get_dimensions(&gfx_current_dimensions.width, &gfx_current_dimensions.height); gfx_wapi->get_dimensions(&gfx_current_dimensions.width, &gfx_current_dimensions.height);

View file

@ -15,6 +15,7 @@ void gfx_init(struct GfxWindowManagerAPI *wapi, struct GfxRenderingAPI *rapi);
void gfx_start_frame(void); void gfx_start_frame(void);
void gfx_run(Gfx *commands); void gfx_run(Gfx *commands);
void gfx_end_frame(void); void gfx_end_frame(void);
void gfx_precache_textures(void);
void gfx_shutdown(void); void gfx_shutdown(void);
#endif #endif

View file

@ -1,4 +1,5 @@
#include <stdlib.h> #include <stdlib.h>
#include <stdio.h>
#ifdef TARGET_WEB #ifdef TARGET_WEB
#include <emscripten.h> #include <emscripten.h>
@ -22,7 +23,9 @@
#include "cliopts.h" #include "cliopts.h"
#include "configfile.h" #include "configfile.h"
#include "controller/controller_api.h" #include "controller/controller_api.h"
#include "fs/fs.h"
#include "game/game_init.h"
#include "game/main.h" #include "game/main.h"
#include "game/thread6.h" #include "game/thread6.h"
@ -67,7 +70,7 @@ void send_display_list(struct SPTask *spTask) {
void produce_one_frame(void) { void produce_one_frame(void) {
gfx_start_frame(); gfx_start_frame();
game_loop_one_iteration(); game_loop_one_iteration();
thread6_rumble_loop(); thread6_rumble_loop(NULL);
int samples_left = audio_api->buffered(); int samples_left = audio_api->buffered();
u32 num_audio_samples = samples_left < audio_api->get_desired_buffered() ? 544 : 528; u32 num_audio_samples = samples_left < audio_api->get_desired_buffered() ? 544 : 528;
@ -153,6 +156,10 @@ void main_func(void) {
main_pool_init(pool, pool + sizeof(pool) / sizeof(pool[0])); main_pool_init(pool, pool + sizeof(pool) / sizeof(pool[0]));
gEffectsMemoryPool = mem_pool_init(0x4000, MEMORY_POOL_LEFT); gEffectsMemoryPool = mem_pool_init(0x4000, MEMORY_POOL_LEFT);
const char *gamedir = gCLIOpts.GameDir[0] ? gCLIOpts.GameDir : FS_BASEDIR;
const char *userpath = gCLIOpts.SavePath[0] ? gCLIOpts.SavePath : sys_user_path();
fs_init(sys_ropaths, gamedir, userpath);
configfile_load(configfile_name()); configfile_load(configfile_name());
wm_api = &gfx_sdl; wm_api = &gfx_sdl;
@ -170,9 +177,18 @@ void main_func(void) {
sound_init(); sound_init();
thread5_game_loop(NULL); thread5_game_loop(NULL);
inited = true; inited = true;
#ifdef EXTERNAL_DATA
// precache data if needed
if (configPrecacheRes) {
printf("precaching data\n");
fflush(stdout);
gfx_precache_textures();
}
#endif
#ifdef TARGET_WEB #ifdef TARGET_WEB
emscripten_set_main_loop(em_main_loop, 0, 0); emscripten_set_main_loop(em_main_loop, 0, 0);
request_anim_frame(on_anim_frame); request_anim_frame(on_anim_frame);

View file

@ -1,17 +1,26 @@
#include <stdlib.h> #include <stdlib.h>
#include <stdio.h> #include <stdio.h>
#include <stdbool.h> #include <stdbool.h>
#include <stdarg.h>
#include <string.h> #include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <dirent.h>
#include <ctype.h> #include <ctype.h>
#ifdef _WIN32
#include <direct.h>
#endif
#include "cliopts.h" #include "cliopts.h"
#include "fs/fs.h"
/* NULL terminated list of platform specific read-only data paths */
/* priority is top first */
const char *sys_ropaths[] = {
".", // working directory
"!", // executable directory
#if defined(__linux__) || defined(__unix__)
// some common UNIX directories for read only stuff
"/usr/local/share/sm64pc",
"/usr/share/sm64pc",
"/opt/sm64pc",
#endif
NULL,
};
/* these are not available on some platforms, so might as well */ /* these are not available on some platforms, so might as well */
@ -40,81 +49,34 @@ int sys_strcasecmp(const char *s1, const char *s2) {
return result; return result;
} }
/* file system stuff */ const char *sys_file_extension(const char *fpath) {
const char *fname = sys_file_name(fpath);
bool sys_file_exists(const char *name) { const char *dot = strrchr(fname, '.');
struct stat st; if (!dot || !dot[1]) return NULL; // no dot
return (stat(name, &st) == 0 && S_ISREG(st.st_mode)); if (dot == fname) return NULL; // dot is the first char (e.g. .local)
return dot + 1;
} }
bool sys_dir_exists(const char *name) { const char *sys_file_name(const char *fpath) {
struct stat st; const char *sep1 = strrchr(fpath, '/');
return (stat(name, &st) == 0 && S_ISDIR(st.st_mode)); const char *sep2 = strrchr(fpath, '\\');
const char *sep = sep1 > sep2 ? sep1 : sep2;
if (!sep) return fpath;
return sep + 1;
} }
bool sys_dir_walk(const char *base, walk_fn_t walk, const bool recur) { /* this calls a platform-specific impl function after forming the error message */
char fullpath[SYS_MAX_PATH];
DIR *dir;
struct dirent *ent;
if (!(dir = opendir(base))) { static void sys_fatal_impl(const char *msg) __attribute__ ((noreturn));
fprintf(stderr, "sys_dir_walk(): could not open `%s`\n", base);
return false;
}
bool ret = true; void sys_fatal(const char *fmt, ...) {
static char msg[2048];
while ((ent = readdir(dir)) != NULL) { va_list args;
if (ent->d_name[0] == 0 || ent->d_name[0] == '.') continue; // skip ./.. and hidden files va_start(args, fmt);
snprintf(fullpath, sizeof(fullpath), "%s/%s", base, ent->d_name); vsnprintf(msg, sizeof(msg), fmt, args);
if (sys_dir_exists(fullpath)) { va_end(args);
if (recur) { fflush(stdout); // push all crap out
if (!sys_dir_walk(fullpath, walk, recur)) { sys_fatal_impl(msg);
ret = false;
break;
}
}
} else {
if (!walk(fullpath)) {
ret = false;
break;
}
}
}
closedir(dir);
return ret;
}
void *sys_load_res(const char *name) {
char path[SYS_MAX_PATH] = { 0 };
snprintf(path, sizeof(path), "%s/%s", sys_data_path(), name);
FILE *f = fopen(path, "rb");
if (!f) return NULL;
fseek(f, 0, SEEK_END);
size_t size = ftell(f);
fseek(f, 0, SEEK_SET);
void *buf = malloc(size);
if (!buf) {
fclose(f);
return NULL;
}
fread(buf, 1, size, f);
fclose(f);
return buf;
}
bool sys_mkdir(const char *name) {
#ifdef _WIN32
return _mkdir(name) == 0;
#else
return mkdir(name, 0777) == 0;
#endif
} }
#if USE_SDL #if USE_SDL
@ -122,125 +84,50 @@ bool sys_mkdir(const char *name) {
// we can just ask SDL for most of this shit if we have it // we can just ask SDL for most of this shit if we have it
#include <SDL2/SDL.h> #include <SDL2/SDL.h>
const char *sys_data_path(void) { const char *sys_user_path(void) {
static char path[SYS_MAX_PATH] = { 0 }; static char path[SYS_MAX_PATH] = { 0 };
// get it from SDL
if (!path[0]) { char *sdlpath = SDL_GetPrefPath("", "sm64pc");
// prefer the override, if it is set if (sdlpath) {
// "!" expands to executable path const unsigned int len = strlen(sdlpath);
if (gCLIOpts.DataPath[0]) { strncpy(path, sdlpath, sizeof(path));
if (gCLIOpts.DataPath[0] == '!') path[sizeof(path)-1] = 0;
snprintf(path, sizeof(path), "%s%s", sys_exe_path(), gCLIOpts.DataPath + 1); SDL_free(sdlpath);
else if (path[len-1] == '/' || path[len-1] == '\\')
snprintf(path, sizeof(path), "%s", gCLIOpts.DataPath); path[len-1] = 0; // strip the trailing separator
if (sys_dir_exists(path)) return path; if (!fs_sys_dir_exists(path) && !fs_sys_mkdir(path))
printf("Warning: Specified data path ('%s') doesn't exist\n", path); path[0] = 0;
}
// then the executable directory
snprintf(path, sizeof(path), "%s/" DATADIR, sys_exe_path());
if (sys_dir_exists(path)) return path;
// then the save path
snprintf(path, sizeof(path), "%s/" DATADIR, sys_save_path());
if (sys_dir_exists(path)) return path;
#if defined(__linux__) || defined(__unix__)
// on Linux/BSD try some common paths for read-only data
const char *try[] = {
"/usr/local/share/sm64pc/" DATADIR,
"/usr/share/sm64pc/" DATADIR,
"/opt/sm64pc/" DATADIR,
};
for (unsigned i = 0; i < sizeof(try) / sizeof(try[0]); ++i) {
if (sys_dir_exists(try[i])) {
strcpy(path, try[i]);
return path;
}
}
#endif
// hope for the best
strcpy(path, "./" DATADIR);
} }
return path;
}
const char *sys_save_path(void) {
static char path[SYS_MAX_PATH] = { 0 };
if (!path[0]) {
// if the override is set, use that
// "!" expands to executable path
if (gCLIOpts.SavePath[0]) {
if (gCLIOpts.SavePath[0] == '!')
snprintf(path, sizeof(path), "%s%s", sys_exe_path(), gCLIOpts.SavePath + 1);
else
snprintf(path, sizeof(path), "%s", gCLIOpts.SavePath);
if (!sys_dir_exists(path) && !sys_mkdir(path)) {
printf("Warning: Specified save path ('%s') doesn't exist and can't be created\n", path);
path[0] = 0; // doesn't exist and no write access
}
}
// didn't work? get it from SDL
if (!path[0]) {
char *sdlpath = SDL_GetPrefPath("", "sm64pc");
if (sdlpath) {
const unsigned int len = strlen(sdlpath);
strncpy(path, sdlpath, sizeof(path));
path[sizeof(path)-1] = 0;
SDL_free(sdlpath);
if (path[len-1] == '/' || path[len-1] == '\\')
path[len-1] = 0; // strip the trailing separator
if (!sys_dir_exists(path) && !sys_mkdir(path))
path[0] = 0;
}
}
// if all else fails, just store near the EXE
if (!path[0])
strcpy(path, sys_exe_path());
printf("Save path set to '%s'\n", path);
}
return path; return path;
} }
const char *sys_exe_path(void) { const char *sys_exe_path(void) {
static char path[SYS_MAX_PATH] = { 0 }; static char path[SYS_MAX_PATH] = { 0 };
char *sdlpath = SDL_GetBasePath();
if (!path[0]) { if (sdlpath && sdlpath[0]) {
char *sdlpath = SDL_GetBasePath(); // use the SDL path if it exists
if (sdlpath) { const unsigned int len = strlen(sdlpath);
// use the SDL path if it exists strncpy(path, sdlpath, sizeof(path));
const unsigned int len = strlen(sdlpath); path[sizeof(path)-1] = 0;
strncpy(path, sdlpath, sizeof(path)); SDL_free(sdlpath);
path[sizeof(path)-1] = 0; if (path[len-1] == '/' || path[len-1] == '\\')
SDL_free(sdlpath); path[len-1] = 0; // strip the trailing separator
if (path[len-1] == '/' || path[len-1] == '\\')
path[len-1] = 0; // strip the trailing separator
} else {
// hope for the best
strcpy(path, ".");
}
printf("Executable path set to '%s'\n", path);
} }
return path; return path;
} }
static void sys_fatal_impl(const char *msg) {
SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR , "Fatal error", msg, NULL);
fprintf(stderr, "FATAL ERROR:\n%s\n", msg);
fflush(stderr);
exit(1);
}
#else #else
#warning "You might want to implement these functions for your platform" #warning "You might want to implement these functions for your platform"
const char *sys_data_path(void) { const char *sys_user_path(void) {
return ".";
}
const char *sys_save_path(void) {
return "."; return ".";
} }
@ -248,4 +135,10 @@ const char *sys_exe_path(void) {
return "."; return ".";
} }
static void sys_fatal_impl(const char *msg) {
fprintf(stderr, "FATAL ERROR:\n%s\n", msg);
fflush(stderr);
exit(1);
}
#endif // platform switch #endif // platform switch

View file

@ -7,29 +7,23 @@
/* Platform-specific functions and whatnot */ /* Platform-specific functions and whatnot */
#define DATADIR "res"
#define SYS_MAX_PATH 1024 // FIXME: define this on different platforms #define SYS_MAX_PATH 1024 // FIXME: define this on different platforms
// NULL terminated list of platform specific read-only data paths
extern const char *sys_ropaths[];
// crossplatform impls of misc stuff // crossplatform impls of misc stuff
char *sys_strdup(const char *src); char *sys_strdup(const char *src);
char *sys_strlwr(char *src); char *sys_strlwr(char *src);
int sys_strcasecmp(const char *s1, const char *s2); int sys_strcasecmp(const char *s1, const char *s2);
// filesystem stuff
bool sys_mkdir(const char *name); // creates with 0777 by default
bool sys_file_exists(const char *name);
bool sys_dir_exists(const char *name);
void *sys_load_res(const char *name);
// receives the full path
// should return `true` if traversal should continue
typedef bool (*walk_fn_t)(const char *);
// returns `true` if the directory was successfully opened and walk() didn't ever return false
bool sys_dir_walk(const char *base, walk_fn_t walk, const bool recur);
// path stuff // path stuff
const char *sys_data_path(void); const char *sys_user_path(void);
const char *sys_save_path(void);
const char *sys_exe_path(void); const char *sys_exe_path(void);
const char *sys_file_extension(const char *fpath);
const char *sys_file_name(const char *fpath);
// shows an error message in some way and terminates the game
void sys_fatal(const char *fmt, ...) __attribute__ ((noreturn));
#endif // _SM64_PLATFORM_H_ #endif // _SM64_PLATFORM_H_

View file

@ -3,6 +3,9 @@
#include "lib/src/libultra_internal.h" #include "lib/src/libultra_internal.h"
#include "macros.h" #include "macros.h"
#include "platform.h" #include "platform.h"
#include "fs/fs.h"
#define SAVE_FILENAME "sm64_save_file.bin"
#ifdef TARGET_WEB #ifdef TARGET_WEB
#include <emscripten.h> #include <emscripten.h>
@ -120,17 +123,15 @@ s32 osEepromLongRead(UNUSED OSMesgQueue *mq, u8 address, u8 *buffer, int nbytes)
ret = 0; ret = 0;
} }
#else #else
char save_path[SYS_MAX_PATH] = { 0 }; fs_file_t *fp = fs_open(SAVE_FILENAME);
snprintf(save_path, sizeof(save_path), "%s/sm64_save_file.bin", sys_save_path());
FILE *fp = fopen(save_path, "rb");
if (fp == NULL) { if (fp == NULL) {
return -1; return -1;
} }
if (fread(content, 1, 512, fp) == 512) { if (fs_read(fp, content, 512) == 512) {
memcpy(buffer, content + address * 8, nbytes); memcpy(buffer, content + address * 8, nbytes);
ret = 0; ret = 0;
} }
fclose(fp); fs_close(fp);
#endif #endif
return ret; return ret;
} }
@ -152,9 +153,7 @@ s32 osEepromLongWrite(UNUSED OSMesgQueue *mq, u8 address, u8 *buffer, int nbytes
}, content); }, content);
s32 ret = 0; s32 ret = 0;
#else #else
char save_path[SYS_MAX_PATH] = { 0 }; FILE *fp = fopen(fs_get_write_path(SAVE_FILENAME), "wb");
snprintf(save_path, sizeof(save_path), "%s/sm64_save_file.bin", sys_save_path());
FILE *fp = fopen(save_path, "wb");
if (fp == NULL) { if (fp == NULL) {
return -1; return -1;
} }

View file

@ -1,17 +1,26 @@
UNAME := $(shell uname) UNAME := $(shell uname)
DEBUG ?= 0
ifeq ($(UNAME),Darwin) ifeq ($(UNAME),Darwin)
OSX_BUILD := -DOSX_BUILD OSX_BUILD := -DOSX_BUILD
endif
ifeq ($(DEBUG),1)
OPT_FLAG := -g
else
OPT_FLAG := -O2
endif endif
CC := gcc CC := gcc
CFLAGS := -Llib -Iinclude -I../include -I . -Wall -Wextra -Wno-unused-parameter $(OSX_BUILD) -pedantic -std=c99 -O3 -s CXX := g++
CFLAGS := -Llib -Iinclude -I../include -I . -Wall -Wextra -Wno-unused-parameter $(OSX_BUILD) -pedantic -std=c99 $(OPT_FLAG) -s
PROGRAMS := n64graphics n64graphics_ci mio0 n64cksum textconv patch_libultra_math iplfontutil aifc_decode aiff_extract_codebook vadpcm_enc tabledesign extract_data_for_mio skyconv PROGRAMS := n64graphics n64graphics_ci mio0 n64cksum textconv patch_libultra_math iplfontutil aifc_decode aiff_extract_codebook vadpcm_enc tabledesign extract_data_for_mio skyconv
n64graphics_SOURCES := n64graphics.c utils.c n64graphics_SOURCES := n64graphics.c utils.c
n64graphics_CFLAGS := -DN64GRAPHICS_STANDALONE n64graphics_CFLAGS := -DN64GRAPHICS_STANDALONE
n64graphics_ci_SOURCES := n64graphics_ci_dir/n64graphics_ci.c n64graphics_ci_dir/exoquant/exoquant.c n64graphics_ci_dir/utils.c n64graphics_ci_SOURCES := n64graphics_ci_dir/n64graphics_ci.c n64graphics_ci_dir/exoquant/exoquant.c n64graphics_ci_dir/utils.c
n64graphics_ci_CFLAGS := -O2 # 3s faster compile time n64graphics_ci_CFLAGS := $(OPT_FLAG)
mio0_SOURCES := libmio0.c mio0_SOURCES := libmio0.c
mio0_CFLAGS := -DMIO0_STANDALONE mio0_CFLAGS := -DMIO0_STANDALONE
@ -22,10 +31,10 @@ textconv_SOURCES := textconv.c utf8.c hashtable.c
patch_libultra_math_SOURCES := patch_libultra_math.c patch_libultra_math_SOURCES := patch_libultra_math.c
iplfontutil_SOURCES := iplfontutil.c iplfontutil_SOURCES := iplfontutil.c
iplfontutil_CFLAGS := -O2 # faster compile time iplfontutil_CFLAGS := $(OPT_FLAG)
aifc_decode_SOURCES := aifc_decode.c aifc_decode_SOURCES := aifc_decode.c
aifc_decode_CFLAGS := -O2 # both runs and compiles faster than -O3 aifc_decode_CFLAGS := $(OPT_FLAG)
aiff_extract_codebook_SOURCES := aiff_extract_codebook.c aiff_extract_codebook_SOURCES := aiff_extract_codebook.c
@ -36,19 +45,24 @@ vadpcm_enc_SOURCES := sdk-tools/adpcm/vadpcm_enc.c sdk-tools/adpcm/vpredictor.c
vadpcm_enc_CFLAGS := -Wno-unused-result -Wno-uninitialized -Wno-sign-compare -Wno-absolute-value vadpcm_enc_CFLAGS := -Wno-unused-result -Wno-uninitialized -Wno-sign-compare -Wno-absolute-value
extract_data_for_mio_SOURCES := extract_data_for_mio.c extract_data_for_mio_SOURCES := extract_data_for_mio.c
extract_data_for_mio_CFLAGS := -O2 extract_data_for_mio_CFLAGS := $(OPT_FLAG)
skyconv_SOURCES := skyconv.c n64graphics.c utils.c skyconv_SOURCES := skyconv.c n64graphics.c utils.c
skyconv_CFLAGS := -O2 -lm skyconv_CFLAGS := $(OPT_FLAG) -lm
all: $(PROGRAMS) all: $(PROGRAMS)
clean: clean:
$(RM) $(PROGRAMS) $(RM) $(PROGRAMS)
$(RM) gen_asset_list
define COMPILE define COMPILE
$(1): $($1_SOURCES) $(1): $($1_SOURCES)
$(CC) $(CFLAGS) $(OSX_BUILD) $$^ -lm -o $$@ $($1_CFLAGS) $(CC) $(CFLAGS) $(OSX_BUILD) $$^ -lm -o $$@ $($1_CFLAGS)
endef endef
$(foreach p,$(PROGRAMS),$(eval $(call COMPILE,$(p)))) # Separate build for debugging gen_asset_list.cpp
gen_asset_list:
$(CXX) -std=c++17 gen_asset_list.cpp -lstdc++fs $(OPT_FLAG) -Wall -o gen_asset_list
$(foreach p,$(PROGRAMS),$(eval $(call COMPILE,$(p))))

View file

@ -1235,3 +1235,534 @@
0x359500dd, textures/water/jrb_textures.0A000.rgba16.png 0x359500dd, textures/water/jrb_textures.0A000.rgba16.png
0xd4e87a88, textures/water/jrb_textures.0A800.rgba16.png 0xd4e87a88, textures/water/jrb_textures.0A800.rgba16.png
0x742e8b1b, textures/water/jrb_textures.0B800.rgba16.png 0x742e8b1b, textures/water/jrb_textures.0B800.rgba16.png
0x0c2a29d1, textures/skybox_tiles/bbh.10.rgba16.png
0x127129a3, textures/skybox_tiles/bbh.35.rgba16.png
0x20a848ef, textures/skybox_tiles/bbh.17.rgba16.png
0x27d1fd7b, textures/skybox_tiles/bbh.9.rgba16.png
0x284ca0c5, textures/skybox_tiles/bbh.18.rgba16.png
0x3696290e, textures/skybox_tiles/bbh.8.rgba16.png
0x3cfb08d4, textures/skybox_tiles/bbh.22.rgba16.png
0x46155549, textures/skybox_tiles/bbh.12.rgba16.png
0x4951ce15, textures/skybox_tiles/bbh.20.rgba16.png
0x4d7bb2d4, textures/skybox_tiles/bbh.26.rgba16.png
0x4dd2efbe, textures/skybox_tiles/bbh.34.rgba16.png
0x5363d67e, textures/skybox_tiles/bbh.5.rgba16.png
0x006b2ac4, textures/skybox_tiles/bbh.6.rgba16.png
0x8a53d88f, textures/skybox_tiles/bbh.7.rgba16.png
0x56a63453, textures/skybox_tiles/bbh.27.rgba16.png
0x6b0abf55, textures/skybox_tiles/bbh.38.rgba16.png
0x72ad931e, textures/skybox_tiles/bbh.39.rgba16.png
0x76a045ea, textures/skybox_tiles/bbh.21.rgba16.png
0x76f1d4a5, textures/skybox_tiles/bbh.29.rgba16.png
0x79c3d825, textures/skybox_tiles/bbh.33.rgba16.png
0x7b1f009f, textures/skybox_tiles/bbh.37.rgba16.png
0x810b81b8, textures/skybox_tiles/bbh.31.rgba16.png
0x83d66711, textures/skybox_tiles/bbh.19.rgba16.png
0x370b42dd, textures/skybox_tiles/bbh.3.rgba16.png
0xad4d5fd6, textures/skybox_tiles/bbh.4.rgba16.png
0xae64c178, textures/skybox_tiles/bbh.24.rgba16.png
0xc31cd553, textures/skybox_tiles/bbh.25.rgba16.png
0xcc6f4232, textures/skybox_tiles/bbh.14.rgba16.png
0xda13b24f, textures/skybox_tiles/bbh.36.rgba16.png
0xe6f9d8a1, textures/skybox_tiles/bbh.28.rgba16.png
0xe7f8bc17, textures/skybox_tiles/bbh.11.rgba16.png
0xe98970f2, textures/skybox_tiles/bbh.32.rgba16.png
0xede2ca8c, textures/skybox_tiles/bbh.40.rgba16.png
0xf5ecf7b1, textures/skybox_tiles/bbh.30.rgba16.png
0xf7e57129, textures/skybox_tiles/bbh.23.rgba16.png
0xf8ba6541, textures/skybox_tiles/bbh.13.rgba16.png
0xf8fda326, textures/skybox_tiles/bbh.16.rgba16.png
0xf97cb7a0, textures/skybox_tiles/bbh.15.rgba16.png
0xf2dec30f, textures/skybox_tiles/bbh.0.rgba16.png
0x043f32a3, textures/skybox_tiles/bidw.13.rgba16.png
0x05ace106, textures/skybox_tiles/bidw.53.rgba16.png
0x0ddbc44f, textures/skybox_tiles/bidw.28.rgba16.png
0x167558ec, textures/skybox_tiles/bidw.51.rgba16.png
0x1ce3eb88, textures/skybox_tiles/bidw.8.rgba16.png
0x2aaa4518, textures/skybox_tiles/bidw.50.rgba16.png
0x3063e9c2, textures/skybox_tiles/bidw.26.rgba16.png
0x318d4424, textures/skybox_tiles/bidw.19.rgba16.png
0x3a4b1e75, textures/skybox_tiles/bidw.42.rgba16.png
0x3a748e08, textures/skybox_tiles/bidw.12.rgba16.png
0x3cb88a7c, textures/skybox_tiles/bidw.18.rgba16.png
0x4741a3f3, textures/skybox_tiles/bidw.10.rgba16.png
0x49be9263, textures/skybox_tiles/bidw.40.rgba16.png
0x4e671fe7, textures/skybox_tiles/bidw.11.rgba16.png
0x5d0c512e, textures/skybox_tiles/bidw.24.rgba16.png
0x672333be, textures/skybox_tiles/bidw.32.rgba16.png
0x6764441d, textures/skybox_tiles/bidw.9.rgba16.png
0x6fd9fab0, textures/skybox_tiles/bidw.23.rgba16.png
0x7609e579, textures/skybox_tiles/bidw.20.rgba16.png
0x7a714bd5, textures/skybox_tiles/bidw.44.rgba16.png
0x7da6e5fc, textures/skybox_tiles/bidw.55.rgba16.png
0x802d3753, textures/skybox_tiles/bidw.27.rgba16.png
0x900d54e9, textures/skybox_tiles/bidw.46.rgba16.png
0x9f7d3512, textures/skybox_tiles/bidw.54.rgba16.png
0xb200de26, textures/skybox_tiles/bidw.34.rgba16.png
0xb867da06, textures/skybox_tiles/bidw.33.rgba16.png
0xc4a3952c, textures/skybox_tiles/bidw.14.rgba16.png
0xc75a4473, textures/skybox_tiles/bidw.22.rgba16.png
0xc9cd900f, textures/skybox_tiles/bidw.47.rgba16.png
0xca24b001, textures/skybox_tiles/bidw.29.rgba16.png
0xcdd80dd0, textures/skybox_tiles/bidw.21.rgba16.png
0xd09450f4, textures/skybox_tiles/bidw.45.rgba16.png
0xd4ae6278, textures/skybox_tiles/bidw.25.rgba16.png
0xd6ada2fc, textures/skybox_tiles/bidw.30.rgba16.png
0xdd5c0937, textures/skybox_tiles/bidw.31.rgba16.png
0xde97d2e7, textures/skybox_tiles/bidw.39.rgba16.png
0xe5ff061e, textures/skybox_tiles/bidw.17.rgba16.png
0xe9f5bc54, textures/skybox_tiles/bidw.49.rgba16.png
0xea4f97d6, textures/skybox_tiles/bidw.43.rgba16.png
0xeb07e789, textures/skybox_tiles/bidw.15.rgba16.png
0xf2fd9c92, textures/skybox_tiles/bidw.41.rgba16.png
0xf3c86fb5, textures/skybox_tiles/bidw.38.rgba16.png
0xf6cc133c, textures/skybox_tiles/bidw.35.rgba16.png
0xf72670a5, textures/skybox_tiles/bidw.48.rgba16.png
0xf852bb6a, textures/skybox_tiles/bidw.16.rgba16.png
0xf9e32a8a, textures/skybox_tiles/bidw.37.rgba16.png
0xfb944dd9, textures/skybox_tiles/bidw.52.rgba16.png
0xfc7d260b, textures/skybox_tiles/bidw.36.rgba16.png
0x066561d2, textures/skybox_tiles/bitfs.9.rgba16.png
0x09b2a8df, textures/skybox_tiles/bitfs.26.rgba16.png
0x0c27c245, textures/skybox_tiles/bitfs.10.rgba16.png
0x0e64a624, textures/skybox_tiles/bitfs.46.rgba16.png
0x12450331, textures/skybox_tiles/bitfs.23.rgba16.png
0x1797df80, textures/skybox_tiles/bitfs.22.rgba16.png
0x19cae2a3, textures/skybox_tiles/bitfs.13.rgba16.png
0x2075bfa7, textures/skybox_tiles/bitfs.27.rgba16.png
0x28043a91, textures/skybox_tiles/bitfs.25.rgba16.png
0x29fe97c9, textures/skybox_tiles/bitfs.40.rgba16.png
0x3fc1264a, textures/skybox_tiles/bitfs.42.rgba16.png
0x4bf7b907, textures/skybox_tiles/bitfs.19.rgba16.png
0x4c0be564, textures/skybox_tiles/bitfs.8.rgba16.png
0x60962f15, textures/skybox_tiles/bitfs.38.rgba16.png
0x6a32e308, textures/skybox_tiles/bitfs.47.rgba16.png
0x74d4dbc0, textures/skybox_tiles/bitfs.34.rgba16.png
0x74f316eb, textures/skybox_tiles/bitfs.37.rgba16.png
0x78005ae7, textures/skybox_tiles/bitfs.30.rgba16.png
0x7a2a0af3, textures/skybox_tiles/bitfs.14.rgba16.png
0x81be4a28, textures/skybox_tiles/bitfs.20.rgba16.png
0x834bcea3, textures/skybox_tiles/bitfs.36.rgba16.png
0x83ea86ce, textures/skybox_tiles/bitfs.31.rgba16.png
0x8bb1fff4, textures/skybox_tiles/bitfs.16.rgba16.png
0x90c37df4, textures/skybox_tiles/bitfs.33.rgba16.png
0xa90a0dcd, textures/skybox_tiles/bitfs.17.rgba16.png
0xac12907e, textures/skybox_tiles/bitfs.35.rgba16.png
0xb82fa554, textures/skybox_tiles/bitfs.24.rgba16.png
0xbe08284c, textures/skybox_tiles/bitfs.29.rgba16.png
0xc02e764f, textures/skybox_tiles/bitfs.44.rgba16.png
0xc676ba59, textures/skybox_tiles/bitfs.39.rgba16.png
0xc69dd89b, textures/skybox_tiles/bitfs.28.rgba16.png
0xce098128, textures/skybox_tiles/bitfs.15.rgba16.png
0xd9e73ad5, textures/skybox_tiles/bitfs.41.rgba16.png
0xe1a812a2, textures/skybox_tiles/bitfs.11.rgba16.png
0xe674aa35, textures/skybox_tiles/bitfs.45.rgba16.png
0xeb4f600c, textures/skybox_tiles/bitfs.43.rgba16.png
0xece60402, textures/skybox_tiles/bitfs.32.rgba16.png
0xede2ca8c, textures/skybox_tiles/bitfs.48.rgba16.png
0xf0253766, textures/skybox_tiles/bitfs.12.rgba16.png
0xfefbfdf7, textures/skybox_tiles/bitfs.21.rgba16.png
0xff7ec174, textures/skybox_tiles/bitfs.18.rgba16.png
0x03ea51ab, textures/skybox_tiles/bits.22.rgba16.png
0x098bdb85, textures/skybox_tiles/bits.42.rgba16.png
0x241e859e, textures/skybox_tiles/bits.55.rgba16.png
0x257fedb8, textures/skybox_tiles/bits.37.rgba16.png
0x2768a71a, textures/skybox_tiles/bits.9.rgba16.png
0x277de620, textures/skybox_tiles/bits.24.rgba16.png
0x2acfffac, textures/skybox_tiles/bits.13.rgba16.png
0x3e7f575b, textures/skybox_tiles/bits.19.rgba16.png
0x41ba5076, textures/skybox_tiles/bits.41.rgba16.png
0x448d1f89, textures/skybox_tiles/bits.54.rgba16.png
0x449f8333, textures/skybox_tiles/bits.16.rgba16.png
0x4d6b3cd7, textures/skybox_tiles/bits.23.rgba16.png
0x5fb1c383, textures/skybox_tiles/bits.49.rgba16.png
0x6011b5ce, textures/skybox_tiles/bits.35.rgba16.png
0x6119b10d, textures/skybox_tiles/bits.48.rgba16.png
0x62533808, textures/skybox_tiles/bits.14.rgba16.png
0x661d9503, textures/skybox_tiles/bits.52.rgba16.png
0x68049672, textures/skybox_tiles/bits.17.rgba16.png
0x688cceba, textures/skybox_tiles/bits.20.rgba16.png
0x7357d6ee, textures/skybox_tiles/bits.50.rgba16.png
0x76c54c50, textures/skybox_tiles/bits.34.rgba16.png
0x7a754f12, textures/skybox_tiles/bits.15.rgba16.png
0x7b2fd37b, textures/skybox_tiles/bits.44.rgba16.png
0x7bb53e7a, textures/skybox_tiles/bits.12.rgba16.png
0x83af50a7, textures/skybox_tiles/bits.11.rgba16.png
0x85b0081b, textures/skybox_tiles/bits.47.rgba16.png
0x93693dc5, textures/skybox_tiles/bits.40.rgba16.png
0x965a0f36, textures/skybox_tiles/bits.25.rgba16.png
0x968b4216, textures/skybox_tiles/bits.26.rgba16.png
0x9bdfce3c, textures/skybox_tiles/bits.31.rgba16.png
0x9de8c051, textures/skybox_tiles/bits.18.rgba16.png
0xa15ad0c6, textures/skybox_tiles/bits.32.rgba16.png
0xa174b0ea, textures/skybox_tiles/bits.33.rgba16.png
0xa4f6be99, textures/skybox_tiles/bits.8.rgba16.png
0xaae75253, textures/skybox_tiles/bits.27.rgba16.png
0xb20701aa, textures/skybox_tiles/bits.21.rgba16.png
0xb43807fd, textures/skybox_tiles/bits.51.rgba16.png
0xb91449bf, textures/skybox_tiles/bits.29.rgba16.png
0xc0348ee3, textures/skybox_tiles/bits.10.rgba16.png
0xc2422c9f, textures/skybox_tiles/bits.36.rgba16.png
0xc56ea37d, textures/skybox_tiles/bits.46.rgba16.png
0xc6caf08d, textures/skybox_tiles/bits.30.rgba16.png
0xd6487ede, textures/skybox_tiles/bits.28.rgba16.png
0xe0a91e79, textures/skybox_tiles/bits.43.rgba16.png
0xecf9eeb6, textures/skybox_tiles/bits.45.rgba16.png
0xf0b8ce1b, textures/skybox_tiles/bits.38.rgba16.png
0xf2ee53ee, textures/skybox_tiles/bits.53.rgba16.png
0xf811fd31, textures/skybox_tiles/bits.39.rgba16.png
0x01a3bc1a, textures/skybox_tiles/cake.30.rgba16.png
0x0aae8e95, textures/skybox_tiles/cake.5.rgba16.png
0x11001a4a, textures/skybox_tiles/cake.31.rgba16.png
0x1106c725, textures/skybox_tiles/cake.19.rgba16.png
0x21cb1a4e, textures/skybox_tiles/cake.15.rgba16.png
0x27c5fa3c, textures/skybox_tiles/cake.0.rgba16.png
0x2ff059ea, textures/skybox_tiles/cake.2.rgba16.png
0x32f30afe, textures/skybox_tiles/cake.4.rgba16.png
0x33a85e50, textures/skybox_tiles/cake.40.rgba16.png
0x3f7975e7, textures/skybox_tiles/cake.27.rgba16.png
0x4112f982, textures/skybox_tiles/cake.6.rgba16.png
0x4ffd88dc, textures/skybox_tiles/cake.10.rgba16.png
0x550a63af, textures/skybox_tiles/cake.26.rgba16.png
0x565f0a85, textures/skybox_tiles/cake.17.rgba16.png
0x5ebb63ae, textures/skybox_tiles/cake.20.rgba16.png
0x650c8963, textures/skybox_tiles/cake.42.rgba16.png
0x6584dbe9, textures/skybox_tiles/cake.38.rgba16.png
0x659d7fe1, textures/skybox_tiles/cake.29.rgba16.png
0x6741387d, textures/skybox_tiles/cake.12.rgba16.png
0x6b37da50, textures/skybox_tiles/cake.14.rgba16.png
0x70cb2193, textures/skybox_tiles/cake.45.rgba16.png
0x75952848, textures/skybox_tiles/cake.43.rgba16.png
0x775f1e4b, textures/skybox_tiles/cake.36.rgba16.png
0x7bcb5633, textures/skybox_tiles/cake.32.rgba16.png
0x7beb1bb0, textures/skybox_tiles/cake.41.rgba16.png
0x83ad1b6f, textures/skybox_tiles/cake.8.rgba16.png
0x866cfdfa, textures/skybox_tiles/cake.3.rgba16.png
0x8ca33ecb, textures/skybox_tiles/cake.46.rgba16.png
0x9118bb8e, textures/skybox_tiles/cake.24.rgba16.png
0xa282f3ab, textures/skybox_tiles/cake.37.rgba16.png
0xb878f75e, textures/skybox_tiles/cake.13.rgba16.png
0xbcbb2bed, textures/skybox_tiles/cake.9.rgba16.png
0xc5e3c848, textures/skybox_tiles/cake.22.rgba16.png
0xc856d784, textures/skybox_tiles/cake.7.rgba16.png
0xca3e4daa, textures/skybox_tiles/cake.16.rgba16.png
0xcda231cf, textures/skybox_tiles/cake.18.rgba16.png
0xd3cda257, textures/skybox_tiles/cake.21.rgba16.png
0xd6acd732, textures/skybox_tiles/cake.35.rgba16.png
0xda4dded5, textures/skybox_tiles/cake.34.rgba16.png
0xdb34e935, textures/skybox_tiles/cake.28.rgba16.png
0xe4659004, textures/skybox_tiles/cake.33.rgba16.png
0xe7fa94ea, textures/skybox_tiles/cake.39.rgba16.png
0xe9ad4123, textures/skybox_tiles/cake.25.rgba16.png
0xf6d7be9d, textures/skybox_tiles/cake.23.rgba16.png
0xff3e3d09, textures/skybox_tiles/cake.11.rgba16.png
0xffa98d17, textures/skybox_tiles/cake.47.rgba16.png
0x055e28ac, textures/skybox_tiles/ccm.2.rgba16.png
0x08383434, textures/skybox_tiles/ccm.51.rgba16.png
0x0b58f779, textures/skybox_tiles/ccm.11.rgba16.png
0x0cc5b13c, textures/skybox_tiles/ccm.22.rgba16.png
0x168d74ac, textures/skybox_tiles/ccm.32.rgba16.png
0x29ae84cd, textures/skybox_tiles/ccm.55.rgba16.png
0x30bd11c3, textures/skybox_tiles/ccm.19.rgba16.png
0x31b35122, textures/skybox_tiles/ccm.54.rgba16.png
0x357312e7, textures/skybox_tiles/ccm.17.rgba16.png
0x36c31293, textures/skybox_tiles/ccm.47.rgba16.png
0x3a0a5f41, textures/skybox_tiles/ccm.43.rgba16.png
0x3cb609ce, textures/skybox_tiles/ccm.14.rgba16.png
0x44e0cd6e, textures/skybox_tiles/ccm.33.rgba16.png
0x50e38d1d, textures/skybox_tiles/ccm.40.rgba16.png
0x578dd0c2, textures/skybox_tiles/ccm.29.rgba16.png
0x599ac9f6, textures/skybox_tiles/ccm.42.rgba16.png
0x59ba83bc, textures/skybox_tiles/ccm.24.rgba16.png
0x64636bec, textures/skybox_tiles/ccm.52.rgba16.png
0x6f828465, textures/skybox_tiles/ccm.28.rgba16.png
0x730366a4, textures/skybox_tiles/ccm.53.rgba16.png
0x78a3267b, textures/skybox_tiles/ccm.30.rgba16.png
0x7944c340, textures/skybox_tiles/ccm.35.rgba16.png
0x7e45011d, textures/skybox_tiles/ccm.49.rgba16.png
0x7ebb4a6e, textures/skybox_tiles/ccm.48.rgba16.png
0x8011bee2, textures/skybox_tiles/ccm.34.rgba16.png
0x8090c3cf, textures/skybox_tiles/ccm.39.rgba16.png
0x85cb24f9, textures/skybox_tiles/ccm.13.rgba16.png
0x92ab7f1d, textures/skybox_tiles/ccm.18.rgba16.png
0x93e1cb7d, textures/skybox_tiles/ccm.21.rgba16.png
0x942a429d, textures/skybox_tiles/ccm.16.rgba16.png
0x95399033, textures/skybox_tiles/ccm.26.rgba16.png
0xa3b76b7a, textures/skybox_tiles/ccm.38.rgba16.png
0xa4f39ec5, textures/skybox_tiles/ccm.12.rgba16.png
0xa799d0e9, textures/skybox_tiles/ccm.37.rgba16.png
0xaf1b0302, textures/skybox_tiles/ccm.3.rgba16.png
0xb4fa1977, textures/skybox_tiles/ccm.25.rgba16.png
0xb724fba4, textures/skybox_tiles/ccm.41.rgba16.png
0xc09f8f08, textures/skybox_tiles/ccm.27.rgba16.png
0xc2afe2ee, textures/skybox_tiles/ccm.44.rgba16.png
0xc9babf94, textures/skybox_tiles/ccm.10.rgba16.png
0xca599652, textures/skybox_tiles/ccm.20.rgba16.png
0xd91118e0, textures/skybox_tiles/ccm.15.rgba16.png
0xe68e0b89, textures/skybox_tiles/ccm.31.rgba16.png
0xe86658ce, textures/skybox_tiles/ccm.9.rgba16.png
0xeef719d5, textures/skybox_tiles/ccm.46.rgba16.png
0xf7f5501b, textures/skybox_tiles/ccm.4.rgba16.png
0xa4f8a848, textures/skybox_tiles/ccm.5.rgba16.png
0xfaad1902, textures/skybox_tiles/ccm.50.rgba16.png
0xfabbf62b, textures/skybox_tiles/ccm.23.rgba16.png
0xfdd2a734, textures/skybox_tiles/ccm.8.rgba16.png
0xfe3ebb6f, textures/skybox_tiles/ccm.45.rgba16.png
0xfe5d53be, textures/skybox_tiles/ccm.36.rgba16.png
0x08f08935, textures/skybox_tiles/clouds.14.rgba16.png
0x0e6243cb, textures/skybox_tiles/clouds.0.rgba16.png
0x141df5ab, textures/skybox_tiles/clouds.2.rgba16.png
0x1901cc3c, textures/skybox_tiles/clouds.34.rgba16.png
0x1b635fef, textures/skybox_tiles/clouds.23.rgba16.png
0x205dcfbc, textures/skybox_tiles/clouds.27.rgba16.png
0x2654ca98, textures/skybox_tiles/clouds.11.rgba16.png
0x31e58403, textures/skybox_tiles/clouds.9.rgba16.png
0x337f44cd, textures/skybox_tiles/clouds.4.rgba16.png
0x351f61ca, textures/skybox_tiles/clouds.20.rgba16.png
0x409b75a9, textures/skybox_tiles/clouds.16.rgba16.png
0x428df875, textures/skybox_tiles/clouds.7.rgba16.png
0x4620fe63, textures/skybox_tiles/clouds.35.rgba16.png
0x498518c8, textures/skybox_tiles/clouds.12.rgba16.png
0x69857ccd, textures/skybox_tiles/clouds.17.rgba16.png
0x742348b6, textures/skybox_tiles/clouds.10.rgba16.png
0x748b76df, textures/skybox_tiles/clouds.5.rgba16.png
0x77ea8d67, textures/skybox_tiles/clouds.32.rgba16.png
0x7ca8732d, textures/skybox_tiles/clouds.8.rgba16.png
0x82b44cf5, textures/skybox_tiles/clouds.28.rgba16.png
0x8425521b, textures/skybox_tiles/clouds.36.rgba16.png
0x850e8b93, textures/skybox_tiles/clouds.19.rgba16.png
0x9cf0dd58, textures/skybox_tiles/clouds.18.rgba16.png
0xa51f3a7f, textures/skybox_tiles/clouds.38.rgba16.png
0xa6f401d3, textures/skybox_tiles/clouds.21.rgba16.png
0xb416ef38, textures/skybox_tiles/clouds.22.rgba16.png
0xc3701e1f, textures/skybox_tiles/clouds.39.rgba16.png
0xc407c4ce, textures/skybox_tiles/clouds.1.rgba16.png
0xc7cbdfa9, textures/skybox_tiles/clouds.26.rgba16.png
0xca8dfd7d, textures/skybox_tiles/clouds.13.rgba16.png
0xd66bcf0e, textures/skybox_tiles/clouds.37.rgba16.png
0xdfa4ba1b, textures/skybox_tiles/clouds.30.rgba16.png
0xe4090da9, textures/skybox_tiles/clouds.6.rgba16.png
0xe52ec690, textures/skybox_tiles/clouds.29.rgba16.png
0xe6525598, textures/skybox_tiles/clouds.33.rgba16.png
0xed76d6f5, textures/skybox_tiles/clouds.31.rgba16.png
0xee5ef53b, textures/skybox_tiles/clouds.24.rgba16.png
0xefceb811, textures/skybox_tiles/clouds.15.rgba16.png
0xf4aaa9e9, textures/skybox_tiles/clouds.25.rgba16.png
0xf86ba27d, textures/skybox_tiles/clouds.40.rgba16.png
0x02170d3b, textures/skybox_tiles/cloud_floor.4.rgba16.png
0x03dceb01, textures/skybox_tiles/cloud_floor.50.rgba16.png
0x03ea3079, textures/skybox_tiles/cloud_floor.18.rgba16.png
0x1a14e17e, textures/skybox_tiles/cloud_floor.25.rgba16.png
0x244d5841, textures/skybox_tiles/cloud_floor.54.rgba16.png
0x28303f4e, textures/skybox_tiles/cloud_floor.3.rgba16.png
0x3171e773, textures/skybox_tiles/cloud_floor.21.rgba16.png
0x32fd7d7b, textures/skybox_tiles/cloud_floor.39.rgba16.png
0x37876087, textures/skybox_tiles/cloud_floor.33.rgba16.png
0x37d2ed5d, textures/skybox_tiles/cloud_floor.29.rgba16.png
0x3b42ebe0, textures/skybox_tiles/cloud_floor.22.rgba16.png
0x3c1887d9, textures/skybox_tiles/cloud_floor.32.rgba16.png
0x4ac6cbe7, textures/skybox_tiles/cloud_floor.53.rgba16.png
0x4acbd2fd, textures/skybox_tiles/cloud_floor.28.rgba16.png
0x4fa6d582, textures/skybox_tiles/cloud_floor.40.rgba16.png
0x5bcfce7a, textures/skybox_tiles/cloud_floor.9.rgba16.png
0x5d9fcb17, textures/skybox_tiles/cloud_floor.23.rgba16.png
0x6106ec02, textures/skybox_tiles/cloud_floor.45.rgba16.png
0x630a785d, textures/skybox_tiles/cloud_floor.15.rgba16.png
0x64bb3d5d, textures/skybox_tiles/cloud_floor.10.rgba16.png
0x66f77672, textures/skybox_tiles/cloud_floor.49.rgba16.png
0x69291043, textures/skybox_tiles/cloud_floor.37.rgba16.png
0x6e0b85e4, textures/skybox_tiles/cloud_floor.35.rgba16.png
0x6f4aa02c, textures/skybox_tiles/cloud_floor.16.rgba16.png
0x7086d27f, textures/skybox_tiles/cloud_floor.30.rgba16.png
0x70ac8f57, textures/skybox_tiles/cloud_floor.1.rgba16.png
0x7fd9be0f, textures/skybox_tiles/cloud_floor.14.rgba16.png
0x821268df, textures/skybox_tiles/cloud_floor.17.rgba16.png
0x83aead23, textures/skybox_tiles/cloud_floor.11.rgba16.png
0x8549ef4a, textures/skybox_tiles/cloud_floor.12.rgba16.png
0x873e1572, textures/skybox_tiles/cloud_floor.52.rgba16.png
0x88dddbd5, textures/skybox_tiles/cloud_floor.47.rgba16.png
0x8db179ed, textures/skybox_tiles/cloud_floor.31.rgba16.png
0x91920a81, textures/skybox_tiles/cloud_floor.26.rgba16.png
0x96c5ec13, textures/skybox_tiles/cloud_floor.5.rgba16.png
0x96e706ed, textures/skybox_tiles/cloud_floor.8.rgba16.png
0x9c8eb32f, textures/skybox_tiles/cloud_floor.46.rgba16.png
0xa17cd54d, textures/skybox_tiles/cloud_floor.6.rgba16.png
0xa5aaaafc, textures/skybox_tiles/cloud_floor.38.rgba16.png
0xb25aabb9, textures/skybox_tiles/cloud_floor.48.rgba16.png
0xb5f978a7, textures/skybox_tiles/cloud_floor.19.rgba16.png
0xba37aa0b, textures/skybox_tiles/cloud_floor.42.rgba16.png
0xc7e7c9e4, textures/skybox_tiles/cloud_floor.13.rgba16.png
0xc94a832f, textures/skybox_tiles/cloud_floor.27.rgba16.png
0xcb05d655, textures/skybox_tiles/cloud_floor.36.rgba16.png
0xd2fb14ef, textures/skybox_tiles/cloud_floor.51.rgba16.png
0xd35408ed, textures/skybox_tiles/cloud_floor.34.rgba16.png
0xdad5f068, textures/skybox_tiles/cloud_floor.44.rgba16.png
0xe4742281, textures/skybox_tiles/cloud_floor.20.rgba16.png
0xf23d736c, textures/skybox_tiles/cloud_floor.43.rgba16.png
0xf27d7297, textures/skybox_tiles/cloud_floor.2.rgba16.png
0xf3e70377, textures/skybox_tiles/cloud_floor.24.rgba16.png
0xf9522d0c, textures/skybox_tiles/cloud_floor.41.rgba16.png
0xfecd3a6e, textures/skybox_tiles/cloud_floor.55.rgba16.png
0x00c6ac78, textures/skybox_tiles/ssl.18.rgba16.png
0x0668c5ac, textures/skybox_tiles/ssl.22.rgba16.png
0x08de5518, textures/skybox_tiles/ssl.31.rgba16.png
0x138a4d7a, textures/skybox_tiles/ssl.12.rgba16.png
0x1e2a18d7, textures/skybox_tiles/ssl.0.rgba16.png
0x2587baea, textures/skybox_tiles/ssl.47.rgba16.png
0x2b9772a8, textures/skybox_tiles/ssl.46.rgba16.png
0x2d35b5e2, textures/skybox_tiles/ssl.24.rgba16.png
0x2f9648f3, textures/skybox_tiles/ssl.40.rgba16.png
0x307cb980, textures/skybox_tiles/ssl.52.rgba16.png
0x4160cce0, textures/skybox_tiles/ssl.23.rgba16.png
0x42069678, textures/skybox_tiles/ssl.13.rgba16.png
0x44e1e374, textures/skybox_tiles/ssl.26.rgba16.png
0x45fe5da4, textures/skybox_tiles/ssl.42.rgba16.png
0x4c315f58, textures/skybox_tiles/ssl.50.rgba16.png
0x4dc86b03, textures/skybox_tiles/ssl.48.rgba16.png
0x514c13e3, textures/skybox_tiles/ssl.49.rgba16.png
0x54f66e8c, textures/skybox_tiles/ssl.25.rgba16.png
0x628aec7f, textures/skybox_tiles/ssl.51.rgba16.png
0x642a4df8, textures/skybox_tiles/ssl.20.rgba16.png
0x6893e588, textures/skybox_tiles/ssl.17.rgba16.png
0x6e8f7556, textures/skybox_tiles/ssl.39.rgba16.png
0x765518a1, textures/skybox_tiles/ssl.38.rgba16.png
0x7d7fc52a, textures/skybox_tiles/ssl.8.rgba16.png
0x7e036561, textures/skybox_tiles/ssl.45.rgba16.png
0x8622dde8, textures/skybox_tiles/ssl.54.rgba16.png
0x87c3cd18, textures/skybox_tiles/ssl.16.rgba16.png
0x89ce2386, textures/skybox_tiles/ssl.27.rgba16.png
0x9273b37c, textures/skybox_tiles/ssl.44.rgba16.png
0x99c39562, textures/skybox_tiles/ssl.7.rgba16.png
0x9c329960, textures/skybox_tiles/ssl.34.rgba16.png
0x9c6384f2, textures/skybox_tiles/ssl.55.rgba16.png
0xaa8831f6, textures/skybox_tiles/ssl.10.rgba16.png
0xb1eb1dd2, textures/skybox_tiles/ssl.30.rgba16.png
0xb24a3c7f, textures/skybox_tiles/ssl.29.rgba16.png
0xb5c53f3b, textures/skybox_tiles/ssl.35.rgba16.png
0xb9d7eed9, textures/skybox_tiles/ssl.43.rgba16.png
0xbc9a5752, textures/skybox_tiles/ssl.14.rgba16.png
0xbd947cf5, textures/skybox_tiles/ssl.1.rgba16.png
0xbfd6d3d6, textures/skybox_tiles/ssl.32.rgba16.png
0xc3e8ef7d, textures/skybox_tiles/ssl.2.rgba16.png
0xcc108f3a, textures/skybox_tiles/ssl.19.rgba16.png
0xd125bb65, textures/skybox_tiles/ssl.37.rgba16.png
0xe24e24cb, textures/skybox_tiles/ssl.15.rgba16.png
0xe47f6782, textures/skybox_tiles/ssl.53.rgba16.png
0xe61795d5, textures/skybox_tiles/ssl.41.rgba16.png
0xe8a2effc, textures/skybox_tiles/ssl.9.rgba16.png
0xea44258d, textures/skybox_tiles/ssl.36.rgba16.png
0xedd3be31, textures/skybox_tiles/ssl.21.rgba16.png
0xee3a78b4, textures/skybox_tiles/ssl.28.rgba16.png
0xf6017084, textures/skybox_tiles/ssl.33.rgba16.png
0xfe0052b0, textures/skybox_tiles/ssl.11.rgba16.png
0x05fbe999, textures/skybox_tiles/water.9.rgba16.png
0x15384ca8, textures/skybox_tiles/water.29.rgba16.png
0x15e589af, textures/skybox_tiles/water.32.rgba16.png
0x19c98c91, textures/skybox_tiles/water.22.rgba16.png
0x2b27b879, textures/skybox_tiles/water.39.rgba16.png
0x2efc6316, textures/skybox_tiles/water.8.rgba16.png
0x3529a790, textures/skybox_tiles/water.40.rgba16.png
0x42b58453, textures/skybox_tiles/water.12.rgba16.png
0x43d0bcfd, textures/skybox_tiles/water.27.rgba16.png
0x455081bb, textures/skybox_tiles/water.38.rgba16.png
0x4d2998ed, textures/skybox_tiles/water.33.rgba16.png
0x4e6390f2, textures/skybox_tiles/water.42.rgba16.png
0x5396e075, textures/skybox_tiles/water.30.rgba16.png
0x55109629, textures/skybox_tiles/water.52.rgba16.png
0x56e0d9c2, textures/skybox_tiles/water.24.rgba16.png
0x57098f04, textures/skybox_tiles/water.55.rgba16.png
0x57a74c47, textures/skybox_tiles/water.51.rgba16.png
0x5fcc8e05, textures/skybox_tiles/water.46.rgba16.png
0x65da8d91, textures/skybox_tiles/water.53.rgba16.png
0x68a54a56, textures/skybox_tiles/water.44.rgba16.png
0x69cc1850, textures/skybox_tiles/water.4.rgba16.png
0x6baa4dfb, textures/skybox_tiles/water.54.rgba16.png
0x6e5dc962, textures/skybox_tiles/water.20.rgba16.png
0x6ff74194, textures/skybox_tiles/water.7.rgba16.png
0x721ba1d7, textures/skybox_tiles/water.13.rgba16.png
0x7484f94f, textures/skybox_tiles/water.26.rgba16.png
0x7e94ae66, textures/skybox_tiles/water.10.rgba16.png
0x8161c698, textures/skybox_tiles/water.0.rgba16.png
0x826ca923, textures/skybox_tiles/water.37.rgba16.png
0x8bd7f18f, textures/skybox_tiles/water.17.rgba16.png
0x996b1ac0, textures/skybox_tiles/water.16.rgba16.png
0x998ccb46, textures/skybox_tiles/water.18.rgba16.png
0x9b9c72dd, textures/skybox_tiles/water.21.rgba16.png
0x9d89ee1a, textures/skybox_tiles/water.14.rgba16.png
0xaa696520, textures/skybox_tiles/water.3.rgba16.png
0xac6b51e7, textures/skybox_tiles/water.1.rgba16.png
0xb42352f6, textures/skybox_tiles/water.19.rgba16.png
0xbc01ae82, textures/skybox_tiles/water.23.rgba16.png
0xbd02c9df, textures/skybox_tiles/water.15.rgba16.png
0xbdac0e79, textures/skybox_tiles/water.50.rgba16.png
0xc08c0b84, textures/skybox_tiles/water.2.rgba16.png
0xc51e6cda, textures/skybox_tiles/water.11.rgba16.png
0xc61b4b9d, textures/skybox_tiles/water.36.rgba16.png
0xc9566f19, textures/skybox_tiles/water.48.rgba16.png
0xce3eff27, textures/skybox_tiles/water.43.rgba16.png
0xd82b465d, textures/skybox_tiles/water.41.rgba16.png
0xd9a7fca5, textures/skybox_tiles/water.47.rgba16.png
0xe35888ec, textures/skybox_tiles/water.31.rgba16.png
0xe51733ce, textures/skybox_tiles/water.28.rgba16.png
0xe5813083, textures/skybox_tiles/water.35.rgba16.png
0x2459fc0d, textures/skybox_tiles/water.5.rgba16.png
0xeb8264d8, textures/skybox_tiles/water.6.rgba16.png
0xf12c7d18, textures/skybox_tiles/water.45.rgba16.png
0xf9cdcba9, textures/skybox_tiles/water.25.rgba16.png
0xfadd22b9, textures/skybox_tiles/water.49.rgba16.png
0xff7aa543, textures/skybox_tiles/water.34.rgba16.png
0x04398d1a, textures/skybox_tiles/wdw.39.rgba16.png
0x04f329d4, textures/skybox_tiles/wdw.20.rgba16.png
0x05785641, textures/skybox_tiles/wdw.19.rgba16.png
0x08e6bb94, textures/skybox_tiles/wdw.48.rgba16.png
0x09f747c7, textures/skybox_tiles/wdw.42.rgba16.png
0x0a612720, textures/skybox_tiles/wdw.53.rgba16.png
0x183c80fa, textures/skybox_tiles/wdw.11.rgba16.png
0x1adca2bd, textures/skybox_tiles/wdw.50.rgba16.png
0x20be7b7f, textures/skybox_tiles/wdw.30.rgba16.png
0x2149c1f6, textures/skybox_tiles/wdw.38.rgba16.png
0x23d9d57c, textures/skybox_tiles/wdw.18.rgba16.png
0x23e53b53, textures/skybox_tiles/wdw.21.rgba16.png
0x270c3681, textures/skybox_tiles/wdw.45.rgba16.png
0x29c249bb, textures/skybox_tiles/wdw.13.rgba16.png
0x2da97b90, textures/skybox_tiles/wdw.43.rgba16.png
0x395577cc, textures/skybox_tiles/wdw.4.rgba16.png
0x4588a1fa, textures/skybox_tiles/wdw.22.rgba16.png
0x45d13c50, textures/skybox_tiles/wdw.26.rgba16.png
0x46215c68, textures/skybox_tiles/wdw.23.rgba16.png
0x4a9c1f83, textures/skybox_tiles/wdw.37.rgba16.png
0x4fb40efe, textures/skybox_tiles/wdw.35.rgba16.png
0x516dd9d6, textures/skybox_tiles/wdw.41.rgba16.png
0x5ad88b52, textures/skybox_tiles/wdw.17.rgba16.png
0x5cd0e0e8, textures/skybox_tiles/wdw.10.rgba16.png
0x6c2d7234, textures/skybox_tiles/wdw.51.rgba16.png
0x729b23e6, textures/skybox_tiles/wdw.25.rgba16.png
0x73a1ddf7, textures/skybox_tiles/wdw.54.rgba16.png
0x75251318, textures/skybox_tiles/wdw.46.rgba16.png
0x85f64761, textures/skybox_tiles/wdw.44.rgba16.png
0x8b2ca649, textures/skybox_tiles/wdw.1.rgba16.png
0x95c7ec28, textures/skybox_tiles/wdw.16.rgba16.png
0x99faf781, textures/skybox_tiles/wdw.27.rgba16.png
0x9d9f818d, textures/skybox_tiles/wdw.14.rgba16.png
0x9f5b79c1, textures/skybox_tiles/wdw.9.rgba16.png
0xa01da5fc, textures/skybox_tiles/wdw.29.rgba16.png
0xa08f6cf7, textures/skybox_tiles/wdw.47.rgba16.png
0xa114940d, textures/skybox_tiles/wdw.2.rgba16.png
0xb59bc572, textures/skybox_tiles/wdw.36.rgba16.png
0xb5c189f8, textures/skybox_tiles/wdw.40.rgba16.png
0xb77c6504, textures/skybox_tiles/wdw.49.rgba16.png
0xb95f40a9, textures/skybox_tiles/wdw.55.rgba16.png
0xbf1724e6, textures/skybox_tiles/wdw.31.rgba16.png
0xc0d56e70, textures/skybox_tiles/wdw.52.rgba16.png
0x42b3e0b5, textures/skybox_tiles/wdw.5.rgba16.png
0x4c61073d, textures/skybox_tiles/wdw.6.rgba16.png
0x7e90bb9f, textures/skybox_tiles/wdw.7.rgba16.png
0xd6d33f3f, textures/skybox_tiles/wdw.8.rgba16.png
0xdc871416, textures/skybox_tiles/wdw.28.rgba16.png
0xdcf416d6, textures/skybox_tiles/wdw.3.rgba16.png
0xdeddb605, textures/skybox_tiles/wdw.24.rgba16.png
0xe4274af3, textures/skybox_tiles/wdw.15.rgba16.png
0xe503f102, textures/skybox_tiles/wdw.12.rgba16.png
0xed75bba0, textures/skybox_tiles/wdw.32.rgba16.png
0xee13a838, textures/skybox_tiles/wdw.33.rgba16.png
0xf1591c2a, textures/skybox_tiles/wdw.34.rgba16.png

View file

@ -222,7 +222,7 @@ tuple<string, string, vector<string>> compileSoundData(const string& lang) {
} }
int main() { int main() {
intentional syntax error; // (see comment at top of file) //intentional syntax error; // (see comment at top of file)
map<string, string> assets; map<string, string> assets;
map<string, vector<pair<string, int>>> soundAssets; map<string, vector<pair<string, int>>> soundAssets;

22
tools/mkzip.py Normal file
View file

@ -0,0 +1,22 @@
#!/usr/bin/env python3
import sys
import os
import zipfile
if len(sys.argv) < 3:
print('usage: mkzip <lstfile> <zipfile>')
sys.exit(1)
lst = []
with open(sys.argv[1], 'r') as f:
for line in f:
line = line.strip()
if line == '' or line[0] == '#':
continue
tok = line.split()
lst.append((tok[0], tok[1]))
with zipfile.ZipFile(sys.argv[2], 'w', allowZip64=False) as zipf:
for (fname, aname) in lst:
zipf.write(fname, arcname=aname)

1700
tools/rice_crcmap.txt Normal file

File diff suppressed because it is too large Load diff

View file

@ -53,7 +53,7 @@ try:
crc = int(crcstr[2:], 16) crc = int(crcstr[2:], 16)
else: else:
crc = int(crcstr) crc = int(crcstr)
crcmap.append((crc, os.path.join(outpath, tok[1].strip()))) crcmap.append((crc, os.path.join(outpath, 'gfx', tok[1].strip())))
except OSError as e: except OSError as e:
print('could not open {0}: {1}'.format(mapfname, e)) print('could not open {0}: {1}'.format(mapfname, e))
except ValueError as e: except ValueError as e:

95
tools/unpak.py Normal file
View file

@ -0,0 +1,95 @@
#!/usr/bin/env python3
# requires Pillow and zstandard for Python
# on msys, install Pillow with
# pacman -S mingw-w64-x86_64-python-pillow
# zstd needs to be compiled, because the one in pip3 fails to do so:
# pacman -S mingw-w64-x86_64-python-setuptools mingw-w64-x86_64-zstd
# git clone https://github.com/indygreg/python-zstandard.git --recursive && cd python-zstandard
# python setup.py build_ext --external clean
# run like this:
# ./unpak.py pakfile.pak outdir tools/default_crcmap.txt
# any files not found in crcmap will go to the "nonmatching" folder
import os
import sys
import zstd
import struct
from PIL import Image
PAK_MAGIC = b'\x11\xde\x37\x10\x68\x75\xb6\xe8'
if len(sys.argv) < 3:
print('usage: unpak <input.pak> <outdir> [<crcmap>]')
sys.exit(1)
pakfname = sys.argv[1]
outpath = sys.argv[2]
mapfname = "crcmap.txt"
if len(sys.argv) > 3:
mapfname = sys.argv[3]
# load the CRC map
crcmap = dict()
try:
with open(mapfname, 'r') as f:
for line in f:
line = line.strip()
if line == '' or line[0] == '#':
continue
tok = line.split(',')
crcstr = tok[0].strip()
if crcstr.startswith('0x'):
crc = int(crcstr[2:], 16)
else:
crc = int(crcstr)
path = os.path.join(outpath, tok[1].strip())
if crc in crcmap:
crcmap[crc].append(path)
else:
crcmap[crc] = [path]
except OSError as e:
print('could not open {0}: {1}'.format(mapfname, e))
sys.exit(2)
except ValueError as e:
print('invalid integer in {0}: {1}'.format(mapfname, e))
sys.exit(2)
unmatchdir = os.path.join(outpath, "nonmatching")
if not os.path.exists(unmatchdir):
os.makedirs(unmatchdir)
# read the PAK
try:
texlist = []
with open(pakfname, "rb") as f:
magic = f.read(len(PAK_MAGIC))
if magic != PAK_MAGIC:
print('invalid magic in PAK ' + pakfname)
sys.exit(3)
texcount = int.from_bytes(f.read(8), byteorder='little')
print('reading {0} textures from {1}'.format(texcount, pakfname))
for i in range(texcount):
crc = int.from_bytes(f.read(4), byteorder='little')
size = int.from_bytes(f.read(4), byteorder='little')
offset = int.from_bytes(f.read(8), byteorder='little')
width = int.from_bytes(f.read(8), byteorder='little')
height = int.from_bytes(f.read(8), byteorder='little')
texlist.append((crc, size, offset, width, height))
for (crc, size, ofs, w, h) in texlist:
f.seek(ofs)
data = f.read(size)
img = Image.frombytes('RGBA', (w, h), zstd.decompress(data))
if crc in crcmap:
for path in crcmap[crc]:
[fpath, fname] = os.path.split(path)
if not os.path.exists(fpath):
os.makedirs(fpath)
img.save(path)
else:
print('unknown crc: {0:08x}'.format(crc))
path = os.path.join(unmatchdir, "{0:08x}.png".format(crc))
img.save(path)
except OSError as e:
print('could not open {0}: {1}'.format(pakfname, e))
sys.exit(3)