mirror of
https://github.com/coop-deluxe/sm64coopdx.git
synced 2024-11-25 05:25:14 +00:00
Merge branch 'nightly' into feature/discordrpc
This commit is contained in:
commit
233c2d8f72
97 changed files with 4843 additions and 2912 deletions
77
Makefile
77
Makefile
|
@ -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
|
||||||
|
|
||||||
|
|
|
@ -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).
|
||||||
|
|
4
build.sh
4
build.sh
|
@ -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)
|
||||||
|
|
|
@ -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.
|
||||||
|
|
|
@ -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
724
include/tinfl.h
Normal 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.
|
@ -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
|
@ -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
|
|
||||||
|
|
|
@ -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
|
|
||||||
|
|
|
@ -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
|
|
||||||
|
|
|
@ -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
|
|
||||||
|
|
|
@ -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
|
|
||||||
|
|
|
@ -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
|
|
||||||
|
|
|
@ -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
|
|
||||||
|
|
|
@ -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
|
|
232
lib/asm/bcopy.s
232
lib/asm/bcopy.s
|
@ -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
|
|
||||||
|
|
|
@ -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
|
|
||||||
|
|
|
@ -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
|
|
|
@ -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
|
|
||||||
|
|
|
@ -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
|
|
|
@ -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
|
|
|
@ -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
|
|
||||||
|
|
|
@ -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
|
|
||||||
|
|
|
@ -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
|
|
|
@ -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
|
|
||||||
|
|
|
@ -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
|
|
|
@ -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
|
|
|
@ -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
252
lib/rsp.s
|
@ -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
|
|
|
@ -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;
|
||||||
|
|
|
@ -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))
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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);
|
||||||
|
|
||||||
|
|
|
@ -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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -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
|
||||||
};
|
};
|
||||||
|
|
|
@ -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
|
||||||
};
|
};
|
||||||
|
|
|
@ -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
|
||||||
};
|
};
|
||||||
|
|
|
@ -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
137
src/pc/fs/dirtree.c
Normal 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
32
src/pc/fs/dirtree.h
Normal 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
443
src/pc/fs/fs.c
Normal 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
135
src/pc/fs/fs.h
Normal 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
117
src/pc/fs/fs_packtype_dir.c
Normal 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
486
src/pc/fs/fs_packtype_zip.c
Normal 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,
|
||||||
|
};
|
|
@ -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();
|
||||||
|
|
|
@ -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;
|
||||||
|
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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_
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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))))
|
|
@ -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
|
||||||
|
|
|
@ -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
22
tools/mkzip.py
Normal 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
1700
tools/rice_crcmap.txt
Normal file
File diff suppressed because it is too large
Load diff
|
@ -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
95
tools/unpak.py
Normal 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)
|
Loading…
Reference in a new issue