awesome!!!

This commit is contained in:
veclavtalica 2024-07-08 03:44:20 +03:00
commit 206a5b7cad
529 changed files with 148340 additions and 0 deletions

3
.gitattributes vendored Normal file
View File

@ -0,0 +1,3 @@
*.png filter=lfs diff=lfs merge=lfs -text
*.ogg filter=lfs diff=lfs merge=lfs -text
*.xm filter=lfs diff=lfs merge=lfs -text

9
.gitignore vendored Normal file
View File

@ -0,0 +1,9 @@
.vs/
.vscode/
.idea/
.build/
build/
out/
# this is created when using Conan
CMakeUserPresets.json

148
CMakeLists.txt Normal file
View File

@ -0,0 +1,148 @@
cmake_minimum_required(VERSION 3.21)
project(emerald LANGUAGES C)
list(APPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/cmake)
# dependencies
find_package(SDL2 REQUIRED)
find_package(SDL2_image REQUIRED)
find_package(SDL2_ttf REQUIRED)
set(PHYSFS_BUILD_SHARED FALSE)
set(PHYSFS_DISABLE_INSTALL TRUE)
set(PHYSFS_TARGETNAME_UNINSTALL "physfs_uninstall")
add_subdirectory(third-party/physfs)
set(SOURCE_FILES
third-party/physfs/extras/physfsrwops.c
src/config.h
src/context.h
src/context.c
src/main.c
src/util.c src/util.h
src/rendering.c src/rendering.h
src/textures.c src/textures.h
src/input.c src/input.h
src/text.c src/text.h
src/game_api.h
src/game/game.c src/game/game.h
src/game/state.h
src/game/player.c src/game/player.h
src/game/world.c src/game/world.h
src/game/scenes/scene.c src/game/scenes/scene.h
src/game/scenes/title.c src/game/scenes/title.h
src/game/scenes/ingame.c src/game/scenes/ingame.h
)
# target
add_executable(${PROJECT_NAME} ${SOURCE_FILES})
source_group(TREE ${CMAKE_CURRENT_SOURCE_DIR} FILES ${SOURCE_FILES})
set_target_properties(${PROJECT_NAME} PROPERTIES
C_STANDARD 11
C_STANDARD_REQUIRED ON
C_EXTENSIONS OFF)
# distribution definitions
set(ORGANIZATION_NAME "wanp" CACHE STRING
"App developer/publisher's identifier")
set(APP_NAME "baron" CACHE STRING
"App identifier")
set(PACKAGE_EXTENSION "btw" CACHE STRING
"File extension used to look for data archives")
# build options
# LTO will be used on release builds
if(MSVC)
# close enough i say
target_compile_options(${PROJECT_NAME} PRIVATE
/W4
$<$<CONFIG:Release>:/GL>)
target_link_options(${PROJECT_NAME} PRIVATE
$<$<CONFIG:Release>:/LTCG>)
else()
set(WARNING_FLAGS
-Wall
-Wextra
-pedantic-errors
-Wshadow
-Wdouble-promotion
-Wconversion -Wno-sign-conversion
-Werror=vla
-Werror=alloca)
set(BUILD_FLAGS
# these SHOULDN'T break anything...
-fno-math-errno
-ffp-contract=fast
-fno-signed-zeros
-fno-trapping-math
-freciprocal-math)
set(BUILD_FLAGS_RELEASE
-flto)
set(BUILD_FLAGS_DEBUG
-g3
-fsanitize-trap=undefined)
target_compile_options(${PROJECT_NAME} PRIVATE
${WARNING_FLAGS}
${BUILD_FLAGS}
$<$<CONFIG:Release>:${BUILD_FLAGS_RELEASE}>
$<$<CONFIG:Debug>:${BUILD_FLAGS_DEBUG}>)
target_link_options(${PROJECT_NAME} PRIVATE
${BUILD_FLAGS}
$<$<CONFIG:Release>:${BUILD_FLAGS_RELEASE}>
$<$<CONFIG:Debug>:${BUILD_FLAGS_DEBUG}>)
endif()
target_compile_definitions(${PROJECT_NAME} PRIVATE
ORGANIZATION_NAME="${ORGANIZATION_NAME}"
APP_NAME="${APP_NAME}"
PACKAGE_EXTENSION="${PACKAGE_EXTENSION}")
target_include_directories(${PROJECT_NAME}
PRIVATE
third-party/physfs/src
third-party/physfs/extras
)
# header-only libraries should be marked as "system includes"
# to suppress compiler warnings in their code (it's not my problem after all)
target_include_directories(${PROJECT_NAME}
SYSTEM
PRIVATE
third-party/stb
)
# system libraries
find_library(MATH_LIBRARY m)
if(MATH_LIBRARY)
target_link_libraries(${PROJECT_NAME} PUBLIC ${MATH_LIBRARY})
endif()
# third-party libraries
target_link_libraries(${PROJECT_NAME} PUBLIC
SDL2::SDL2
SDL2::SDL2main
SDL2_image::SDL2_image
SDL2_ttf::SDL2_ttf
physfs-static
)
# copy dlls for baby windows
add_custom_command(TARGET ${PROJECT_NAME} POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy -t $<TARGET_FILE_DIR:${PROJECT_NAME}>
$<TARGET_RUNTIME_DLLS:${PROJECT_NAME}>
COMMAND_EXPAND_LISTS
)
# zip up assets
# currently, you must run cmake from the project root dir for this to work correctly
#file(ARCHIVE_CREATE OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/data.${PACKAGE_EXTENSION}
# PATHS ${PROJECT_SOURCE_DIR}/assets
# FORMAT zip
#)

23
conanfile.py Normal file
View File

@ -0,0 +1,23 @@
from conan import ConanFile
from conan.tools.cmake import cmake_layout, CMake
class Emerald(ConanFile):
settings = 'os', 'compiler', 'build_type', 'arch'
generators = 'CMakeToolchain', 'CMakeDeps'
def requirements(self):
self.requires('sdl/2.28.3')
self.requires('sdl_image/2.6.3')
self.requires('sdl_ttf/2.20.2')
def build_requirements(self):
self.tool_requires('cmake/3.22.6')
def layout(self):
cmake_layout(self)
def build(self):
cmake = CMake(self)
cmake.configure()
cmake.build()

BIN
data/assets/player/baron-walk.png (Stored with Git LFS) Normal file

Binary file not shown.

BIN
data/assets/red.png (Stored with Git LFS) Normal file

Binary file not shown.

BIN
data/assets/title.png (Stored with Git LFS) Normal file

Binary file not shown.

BIN
data/assets/white.png (Stored with Git LFS) Normal file

Binary file not shown.

31
src/config.h Normal file
View File

@ -0,0 +1,31 @@
#ifndef CONFIG_H
#define CONFIG_H
/*
* this file is for configuration values which are to be set at
* compile time. generally speaking, it's for things that would be unwise to
* change mid-development without considering the work it might take to
* adapt the game logic.
*
* if you're looking for distribution-related definitions like
* APP_NAME, you should know that they are set from CMake.
*/
#define TICKS_PER_SECOND 60
#define FIXED_DELTA_TIME (1.0 / TICKS_PER_SECOND)
#define RENDER_BASE_WIDTH 640
#define RENDER_BASE_HEIGHT 360
#define TEXTURE_ATLAS_SIZE 2048
#define TEXTURE_ATLAS_BIT_DEPTH 32
#define TEXTURE_ATLAS_FORMAT SDL_PIXELFORMAT_RGBA32
#define NUM_KEYBIND_SLOTS 8
/* 1024 * 1024 */
/* #define UMKA_STACK_SIZE 1048576 */
#endif

3
src/context.c Normal file
View File

@ -0,0 +1,3 @@
#include "context.h"
t_ctx ctx;

68
src/context.h Normal file
View File

@ -0,0 +1,68 @@
#ifndef CONTEXT_H
#define CONTEXT_H
#include "config.h"
#include "private/rendering.h"
#include "textures.h"
#include "input.h"
#include "game_api.h"
#include <SDL2/SDL.h>
#include <stdbool.h>
#include <stdint.h>
typedef struct context {
/* the program's actual argc and argv */
int argc;
char **argv;
struct texture_cache texture_cache;
struct input_state input;
struct sprite_primitive *render_queue_sprites;
struct rect_primitive *render_queue_rectangles;
struct circle_primitive *render_queue_circles;
struct audio_channel *audio_channels;
struct circle_radius_cache_item *circle_radius_hash;
/* main loop machinery */
int64_t clocks_per_second;
int64_t prev_frame_time;
int64_t desired_frametime; /* how long one tick should be */
int64_t frame_accumulator;
int64_t delta_averager_residual;
int64_t time_averager[4];
uint64_t tick_count;
uint64_t step_count;
/* set just once on startup */
uint64_t random_seed;
/* this should be a multiple of TICKS_PER_SECOND */
/* use it to simulate low framerate (e.g. at 60 tps, set to 2 for 30 fps) */
/* it can be changed at runtime; any resulting logic anomalies are bugs */
unsigned int update_multiplicity;
SDL_Renderer *renderer;
SDL_Window *window;
uint32_t window_id;
int window_w;
int window_h;
bool was_successful;
/* you may read from and write to these from game code */
void *udata;
bool debug;
bool is_running;
bool resync_flag;
} t_ctx;
extern t_ctx ctx;
#endif

70
src/game/game.c Normal file
View File

@ -0,0 +1,70 @@
#include "game.h"
#include "../textures.h"
#include "../game_api.h"
#include "state.h"
#include "scenes/scene.h"
#include "scenes/title.h"
#include <stdio.h>
#include <malloc.h>
#include <stdint.h>
void game_step(int64_t delta) {
(void)delta;
}
void game_tick(void) {
if (ctx.tick_count == 0) {
ctx.udata = ccalloc(1, sizeof (struct state));
struct state *state = ctx.udata;
state->ctx = &ctx;
state->scene = title_scene(state);
input_add_action(&ctx.input, "debug_dump_atlases");
input_bind_action_scancode(&ctx.input, "debug_dump_atlases", SDL_SCANCODE_HOME);
input_add_action(&ctx.input, "debug_toggle");
input_bind_action_scancode(&ctx.input, "debug_toggle", SDL_SCANCODE_BACKSPACE);
input_add_action(&ctx.input, "player_left");
input_bind_action_scancode(&ctx.input, "player_left", SDL_SCANCODE_LEFT);
input_add_action(&ctx.input, "player_right");
input_bind_action_scancode(&ctx.input, "player_right", SDL_SCANCODE_RIGHT);
input_add_action(&ctx.input, "player_jump");
input_bind_action_scancode(&ctx.input, "player_jump", SDL_SCANCODE_X);
input_add_action(&ctx.input, "ui_accept");
input_bind_action_scancode(&ctx.input, "ui_accept", SDL_SCANCODE_RETURN);
}
struct state *state = ctx.udata;
if (input_is_action_just_pressed(&ctx.input, "debug_dump_atlases")) {
textures_dump_atlases(&ctx.texture_cache);
}
if (input_is_action_just_pressed(&ctx.input, "debug_toggle")) {
ctx.debug = !ctx.debug;
}
state->scene->tick(state);
/* there's a scene switch pending, we can do it now that the tick is done */
if (state->next_scene != NULL) {
state->scene->end(state);
state->scene = state->next_scene;
state->is_scene_switching = false;
state->next_scene = NULL;
}
}
void game_end(void) {
struct state *state = ctx.udata;
state->scene->end(state);
free(state);
}

15
src/game/game.h Normal file
View File

@ -0,0 +1,15 @@
#ifndef GAME_H
#define GAME_H
#include "../game_api.h"
#include <stdint.h>
void game_step(int64_t delta);
void game_tick(void);
void game_end(void);
#endif

303
src/game/player.c Normal file
View File

@ -0,0 +1,303 @@
#include "player.h"
#include "world.h"
#include "../game_api.h"
#include <SDL2/SDL.h>
#include <stdbool.h>
#include <stdlib.h>
#include <tgmath.h>
static void update_timers(struct player *player) {
tick_timer(&player->jump_air_timer);
tick_timer(&player->jump_coyote_timer);
tick_timer(&player->jump_buffer_timer);
}
static void input_move(struct input_state *input, struct player *player) {
/* apply horizontal damping when the player stops moving */
/* in other words, make it decelerate to a standstill */
if (!input_is_action_pressed(input, "player_left") &&
!input_is_action_pressed(input, "player_right"))
{
player->dx *= player->horizontal_damping;
}
int input_dir = 0;
if (input_is_action_pressed(input, "player_left"))
input_dir = -1;
if (input_is_action_pressed(input, "player_right"))
input_dir = 1;
if (input_is_action_pressed(input, "player_left") &&
input_is_action_pressed(input, "player_right"))
input_dir = 0;
player->dx += (float)input_dir * player->run_horizontal_speed;
player->dx = SDL_clamp(player->dx, -player->max_dx, player->max_dx);
if (fabs(player->dx) < player->run_horizontal_speed) {
player->dx = 0;
}
}
static void jump(struct player *player) {
player->jump_coyote_timer = 0;
player->jump_buffer_timer = 0;
player->dy = player->jump_force_initial;
player->action = PLAYER_ACTION_JUMP;
player->jump_air_timer = player->jump_air_ticks;
}
static void input_jump(struct input_state *input, struct player *player) {
player->current_gravity_multiplier = player->jump_default_multiplier;
if (input_is_action_just_pressed(input, "player_jump")) {
player->jump_air_timer = 0;
player->jump_buffer_timer = player->jump_buffer_ticks;
if (player->action == PLAYER_ACTION_GROUND || player->jump_coyote_timer > 0) {
jump(player);
}
}
if (input_is_action_pressed(input, "player_jump")) {
if (player->action != PLAYER_ACTION_GROUND && player->jump_air_timer > 0) {
player->current_gravity_multiplier = player->jump_boosted_multiplier;
player->dy += player->jump_force_increase;
}
}
}
static void update_collider_x(struct player *player) {
player->collider_x.w = player->rect.w;
player->collider_x.h = player->rect.h - 8;
player->collider_x.x = player->rect.x;
player->collider_x.y = player->rect.y + ((player->rect.h - player->collider_x.h) / 2);
}
static void update_collider_y(struct player *player) {
player->collider_y.w = player->rect.w;
player->collider_y.h = player->rect.h;
player->collider_y.x = player->rect.x + ((player->rect.w - player->collider_y.w) / 2);
player->collider_y.y = player->rect.y;
}
static void apply_gravity(struct player *player, float gravity) {
player->dy -= gravity * player->current_gravity_multiplier;
player->dy = fmax(player->dy, -player->terminal_velocity);
if (player->dy < 0) {
/* just started falling */
if (player->action == PLAYER_ACTION_GROUND) {
player->jump_coyote_timer = player->jump_coyote_ticks;
}
player->action = PLAYER_ACTION_FALL;
}
}
/* returns whether or not a correction was applied */
static bool corner_correct(struct player *player, t_frect collision) {
/*
* somewhat of a hack here. we only want to do corner correction
* if the corner in question really is the corner of a "platform,"
* and not simply the edge of a single tile in a row of many
* tiles, as that would briefly clip the player into the ceiling,
* halting movement. the dumbest way i could think of to fix this
* is to simply ensure that there's no tile to possibly clip into
* before correcting, by checking if there's a tile right above
* the center of the player
*/
if (world_is_tile_at(player->world,
player->rect.x + (player->rect.w / 2),
player->rect.y - 1))
{
return false;
}
float player_center_x = player->collider_x.x + (player->collider_x.w / 2);
float collision_center_x = collision.x + (collision.w / 2);
float abs_difference = fabs(player_center_x - collision_center_x);
/* we're good, no correction needed */
if (abs_difference < player->jump_corner_correction_offset)
return false;
/* collision was on player's right side */
if (player_center_x < collision_center_x) {
player->rect.x -= collision.w / 2;
}
/* collision was on player's left side */
else if (player_center_x > collision_center_x) {
player->rect.x += collision.w / 2;
}
return true;
}
static void calc_collisions_x(struct player *player) {
t_frect collision;
bool is_colliding = world_find_intersect_frect(player->world, player->collider_x, &collision);
if (!is_colliding) return;
float player_center_x = player->collider_x.x + (player->collider_x.w / 2);
float collision_center_x = collision.x + (collision.w / 2);
enum collision_direction { COLLISION_LEFT = -1, COLLISION_RIGHT = 1 };
enum collision_direction dir_x =
player_center_x > collision_center_x ? COLLISION_LEFT : COLLISION_RIGHT;
player->rect.x -= collision.w * (float)dir_x;
player->dx = 0;
}
static void calc_collisions_y(struct player *player) {
t_frect collision;
bool is_colliding = world_find_intersect_frect(player->world, player->collider_y, &collision);
if (!is_colliding) return;
float player_center_y = player->collider_y.y + (player->collider_y.h / 2);
float collision_center_y = collision.y + (collision.h / 2);
enum collision_direction { COLLISION_ABOVE = -1, COLLISION_BELOW = 1 };
enum collision_direction dir_y =
player_center_y > collision_center_y ? COLLISION_ABOVE : COLLISION_BELOW;
/* before the resolution */
if (dir_y == COLLISION_ABOVE) {
if (corner_correct(player, collision)) {
return;
}
}
/* resolution */
player->rect.y -= collision.h * (float)dir_y;
player->dy = 0;
/* after the resolution */
if (dir_y == COLLISION_BELOW) {
/* bandaid fix for float precision-related jittering */
player->rect.y = ceilf(player->rect.y);
player->action = PLAYER_ACTION_GROUND;
if (player->jump_buffer_timer > 0) {
jump(player);
apply_gravity(player, player->world->gravity);
}
} else if (dir_y == COLLISION_ABOVE) {
player->jump_air_timer = 0;
}
}
struct player *player_create(struct world *world) {
struct player *player = cmalloc(sizeof *player);
*player = (struct player) {
.world = world,
.sprite_w = 48,
.sprite_h = 48,
.rect = (t_frect) {
.x = 92,
.y = 200,
.w = 16,
.h = 32,
},
.action = PLAYER_ACTION_GROUND,
.collider_thickness = 42,
.max_dx = 8,
.run_horizontal_speed = 0.5f,
.horizontal_damping = 0.9f,
.current_gravity_multiplier = 1,
.terminal_velocity = 30,
.jump_force_initial = 10,
.jump_force_increase = 1,
.jump_air_ticks = 18,
.jump_coyote_ticks = 8,
.jump_buffer_ticks = 8,
.jump_default_multiplier = 1.4f,
.jump_boosted_multiplier = 1,
.jump_corner_correction_offset = 16.0f,
};
return player;
}
static void drawdef(struct player *player) {
push_sprite("/assets/player/baron-walk.png",
(t_frect) {
.x = player->rect.x + ((player->rect.w - player->sprite_w) / 2),
.y = player->rect.y - 8,
.w = player->sprite_w,
.h = player->sprite_h,
});
push_circle((t_fvec2) { 256, 128 },
24,
(t_color) { 255, 0, 0, 255 });
}
static void drawdef_debug(struct player *player) {
if (!ctx.debug)
return;
/* const int info_separation = 24; */
/* const struct RectPrimitive info_theme = { */
/* .x = 8, */
/* .r = 0, .g = 0, .b = 0, .a = 255, */
/* }; */
push_rectangle(player->collider_x,
(t_color){ 0, 0, 255, 128 });
push_rectangle(player->collider_y,
(t_color){ 0, 0, 255, 128 });
}
void player_destroy(struct player *player) {
free(player);
}
void player_calc(struct player *player) {
update_timers(player);
input_move(&ctx.input, player);
input_jump(&ctx.input, player);
player->rect.x += player->dx;
update_collider_x(player);
calc_collisions_x(player);
apply_gravity(player, player->world->gravity);
player->rect.y -= player->dy;
update_collider_y(player);
calc_collisions_y(player);
update_collider_x(player);
update_collider_y(player);
drawdef(player);
drawdef_debug(player);
}

66
src/game/player.h Normal file
View File

@ -0,0 +1,66 @@
#ifndef PLAYER_H
#define PLAYER_H
#include "../game_api.h"
struct world;
enum player_action {
PLAYER_ACTION_GROUND,
PLAYER_ACTION_FALL,
PLAYER_ACTION_JUMP,
};
struct player {
struct world *world;
/* visual */
float sprite_w;
float sprite_h;
/* body */
t_frect rect;
/* state */
enum player_action action;
/* physics */
t_frect collider_x;
t_frect collider_y;
int collider_thickness;
float dx;
float dy;
float max_dx;
float run_horizontal_speed;
float horizontal_damping;
float current_gravity_multiplier;
float terminal_velocity;
/* jumping */
float jump_force_initial;
float jump_force_increase; /* aka "jump power", increment of dy per tick */
int jump_air_ticks;
int jump_air_timer;
int jump_coyote_ticks;
int jump_coyote_timer;
int jump_buffer_ticks;
int jump_buffer_timer;
/* gravity multipliers */
float jump_default_multiplier;
float jump_boosted_multiplier;
float jump_corner_correction_offset; /* from center */
};
struct player *player_create(struct world *world);
void player_destroy(struct player *player);
void player_calc(struct player *player);
#endif

32
src/game/scenes/ingame.c Normal file
View File

@ -0,0 +1,32 @@
#include "ingame.h"
#include "title.h"
#include "scene.h"
static void ingame_tick(struct state *state) {
struct scene_ingame *scn = (struct scene_ingame *)state->scene;
world_drawdef(scn->world);
player_calc(scn->player);
}
static void ingame_end(struct state *state) {
struct scene_ingame *scn = (struct scene_ingame *)state->scene;
player_destroy(scn->player);
world_destroy(scn->world);
}
struct scene *ingame_scene(struct state *state) {
(void)state;
struct scene_ingame *new_scene = ccalloc(1, sizeof *new_scene);
new_scene->base.tick = ingame_tick;
new_scene->base.end = ingame_end;
new_scene->world = world_create();
new_scene->player = player_create(new_scene->world);
return (struct scene *)new_scene;
}

23
src/game/scenes/ingame.h Normal file
View File

@ -0,0 +1,23 @@
#ifndef INGAME_H
#define INGAME_H
#include "../../game_api.h"
#include "../state.h"
#include "scene.h"
#include "../player.h"
#include "../world.h"
struct scene_ingame {
struct scene base;
struct world *world;
struct player *player;
};
struct scene *ingame_scene(struct state *state);
#endif

8
src/game/scenes/scene.c Normal file
View File

@ -0,0 +1,8 @@
#include "scene.h"
#include "../state.h"
void switch_to(struct state *state, struct scene *(*scene_func)(struct state *)) {
state->next_scene = scene_func(state);
state->is_scene_switching = true;
}

16
src/game/scenes/scene.h Normal file
View File

@ -0,0 +1,16 @@
#ifndef SCENE_H
#define SCENE_H
struct state;
struct scene {
char *id;
void (*tick)(struct state *);
void (*end)(struct state *);
};
void switch_to(struct state *state, struct scene *(*scene_func)(struct state *));
#endif

41
src/game/scenes/title.c Normal file
View File

@ -0,0 +1,41 @@
#include "title.h"
#include "ingame.h"
#include "../world.h"
#include "../player.h"
#include "../../game_api.h"
static void title_tick(struct state *state) {
struct scene_title *scn = (struct scene_title *)state->scene;
(void)scn;
if (input_is_action_just_pressed(&state->ctx->input, "ui_accept")) {
switch_to(state, ingame_scene);
}
push_sprite("/assets/title.png", (t_frect) {
(RENDER_BASE_WIDTH / 2) - (320 / 2), 64, 320, 128
});
}
static void title_end(struct state *state) {
struct scene_title *scn = (struct scene_title *)state->scene;
player_destroy(scn->player);
world_destroy(scn->world);
}
struct scene *title_scene(struct state *state) {
(void)state;
struct scene_title *new_scene = ccalloc(1, sizeof *new_scene);
new_scene->base.tick = title_tick;
new_scene->base.end = title_end;
new_scene->world = world_create();
new_scene->player = player_create(new_scene->world);
return (struct scene *)new_scene;
}

23
src/game/scenes/title.h Normal file
View File

@ -0,0 +1,23 @@
#ifndef TITLE_H
#define TITLE_H
#include "../../game_api.h"
#include "../state.h"
#include "scene.h"
#include "../player.h"
#include "../world.h"
struct scene_title {
struct scene base;
struct world *world;
struct player *player;
};
struct scene *title_scene(struct state *state);
#endif

16
src/game/state.h Normal file
View File

@ -0,0 +1,16 @@
#ifndef STATE_H
#define STATE_H
#include "../game_api.h"
struct state {
t_ctx *ctx;
struct scene *scene;
struct scene *next_scene;
bool is_scene_switching;
};
#endif

187
src/game/world.c Normal file
View File

@ -0,0 +1,187 @@
#include "world.h"
#include "../game_api.h"
#include <stdbool.h>
#include <stdlib.h>
#include <stdint.h>
#include <tgmath.h>
static void update_tiles(struct world *world) {
for (size_t row = 0; row < world->tilemap_height; ++row) {
for (size_t col = 0; col < world->tilemap_width; ++col) {
world->tiles[(row * world->tilemap_width) + col] = (struct tile) {
.rect = (t_rect) {
.x = (int)col * world->tile_size,
.y = (int)row * world->tile_size,
.w = world->tile_size,
.h = world->tile_size,
},
.type = world->tilemap[row][col],
};
}
}
}
static t_vec2 to_grid_location(struct world *world, float x, float y) {
return (t_vec2) {
.x = (int)floor(x / (float)world->tile_size),
.y = (int)floor(y / (float)world->tile_size),
};
}
static void drawdef_debug(struct world *world) {
if (!ctx.debug) return;
for (size_t i = 0; i < world->tilemap_height * world->tilemap_width; ++i) {
if (world->tiles[i].type == TILE_TYPE_VOID) continue;
push_rectangle(to_frect(world->tiles[i].rect),
(t_color) { 255, 0, 255, 128 });
}
}
struct world *world_create(void) {
struct world *world = cmalloc(sizeof *world);
*world = (struct world) {
.tiles = NULL,
.tile_size = 42,
.tilemap_width = 20,
.tilemap_height = 12,
.gravity = 1,
};
/* create the tilemap */
/* it simply stores what's in each tile as a 2d array */
/* on its own, it's entirely unrelated to drawing or logic */
world->tilemap = cmalloc(sizeof *world->tilemap * world->tilemap_height);
for (size_t i = 0; i < world->tilemap_height; ++i) {
world->tilemap[i] = cmalloc(sizeof **world->tilemap * world->tilemap_width);
for (size_t j = 0; j < world->tilemap_width; ++j) {
world->tilemap[i][j] = TILE_TYPE_VOID;
}
}
for (size_t i = 0; i < 12; ++i) {
world->tilemap[world->tilemap_height-2][2+i] = TILE_TYPE_SOLID;
}
world->tilemap[world->tilemap_height-3][8] = TILE_TYPE_SOLID;
world->tilemap[world->tilemap_height-4][8] = TILE_TYPE_SOLID;
world->tilemap[world->tilemap_height-5][10] = TILE_TYPE_SOLID;
world->tilemap[world->tilemap_height-6][10] = TILE_TYPE_SOLID;
for (size_t i = 0; i < 7; ++i) {
world->tilemap[world->tilemap_height-6][12+i] = TILE_TYPE_SOLID;
}
/* the tiles array contains data meant to be used by other logic */
/* most importantly, it is used to draw the tiles */
const size_t tile_count = world->tilemap_height * world->tilemap_width;
world->tiles = cmalloc(sizeof *world->tiles * tile_count);
update_tiles(world);
return world;
}
void world_destroy(struct world *world) {
free(world->tiles);
for (size_t i = 0; i < world->tilemap_height; ++i) {
free(world->tilemap[i]);
}
free(world->tilemap);
free(world);
}
void world_drawdef(struct world *world) {
for (size_t i = 0; i < world->tilemap_height * world->tilemap_width; ++i) {
if (world->tiles[i].type == TILE_TYPE_VOID)
continue;
push_sprite("/assets/white.png", to_frect(world->tiles[i].rect));
}
drawdef_debug(world);
}
bool world_find_intersect_frect(struct world *world, t_frect rect, t_frect *intersection) {
bool is_intersecting = false;
const size_t tile_count = world->tilemap_height * world->tilemap_width;
for (size_t i = 0; i < tile_count; ++i) {
if (world->tiles[i].type == TILE_TYPE_VOID)
continue;
t_frect tile_frect = {
.x = (float)(world->tiles[i].rect.x),
.y = (float)(world->tiles[i].rect.y),
.w = (float)(world->tiles[i].rect.w),
.h = (float)(world->tiles[i].rect.h),
};
if (intersection == NULL) {
t_frect temp;
is_intersecting = intersect_frect(&rect, &tile_frect, &temp);
} else {
is_intersecting = intersect_frect(&rect, &tile_frect, intersection);
}
if (is_intersecting)
break;
}
return is_intersecting;
}
bool world_find_intersect_rect(struct world *world, t_rect rect, t_rect *intersection) {
bool is_intersecting = false;
const size_t tile_count = world->tilemap_height * world->tilemap_width;
for (size_t i = 0; i < tile_count; ++i) {
if (world->tiles[i].type == TILE_TYPE_VOID)
continue;
t_rect *tile_rect = &world->tiles[i].rect;
if (intersection == NULL) {
t_rect temp;
is_intersecting = intersect_rect(&rect, tile_rect, &temp);
} else {
is_intersecting = intersect_rect(&rect, tile_rect, intersection);
}
if (is_intersecting)
break;
}
return is_intersecting;
}
bool world_is_tile_at(struct world *world, float x, float y) {
t_vec2 position_in_grid = to_grid_location(world, x, y);
return world->tilemap[position_in_grid.y][position_in_grid.x] != TILE_TYPE_VOID;
}
void world_place_tile(struct world *world, float x, float y) {
t_vec2 position_in_grid = to_grid_location(world, x, y);
world->tilemap[position_in_grid.y][position_in_grid.x] = TILE_TYPE_SOLID;
update_tiles(world);
}
void world_remove_tile(struct world *world, float x, float y) {
t_vec2 position_in_grid = to_grid_location(world, x, y);
world->tilemap[position_in_grid.y][position_in_grid.x] = TILE_TYPE_VOID;
update_tiles(world);
}

44
src/game/world.h Normal file
View File

@ -0,0 +1,44 @@
#ifndef WORLD_H
#define WORLD_H
#include "../game_api.h"
#include <stdint.h>
#include <stdbool.h>
enum tile_type {
TILE_TYPE_VOID,
TILE_TYPE_SOLID,
};
struct tile {
t_rect rect;
enum tile_type type;
};
struct world {
enum tile_type **tilemap;
struct tile *tiles;
int tile_size;
unsigned int tilemap_width;
unsigned int tilemap_height;
size_t tile_nonvoid_count;
float gravity;
};
struct world *world_create(void);
void world_destroy(struct world *world);
void world_drawdef(struct world *world);
bool world_find_intersect_frect(struct world *world, t_frect rect, t_frect *intersection);
bool world_find_intersect_rect(struct world *world, t_rect rect, t_rect *intersection);
bool world_is_tile_at(struct world *world, float x, float y);
void world_place_tile(struct world *world, float x, float y);
void world_remove_tile(struct world *world, float x, float y);
#endif

13
src/game_api.h Normal file
View File

@ -0,0 +1,13 @@
/* include this header in game code to get the usable parts of the engine */
#ifndef GAME_API_H
#define GAME_API_H
#include "context.h"
#include "rendering.h"
#include "util.h"
#include <SDL2/SDL.h>
#endif

296
src/input.c Normal file
View File

@ -0,0 +1,296 @@
#include "input.h"
#include "util.h"
#include <SDL2/SDL.h>
#include <stb_ds.h>
#include <stdbool.h>
#include <stdlib.h>
static void update_action_pressed_state(struct input_state *input, struct action *action) {
for (size_t i = 0; i < SDL_arraysize(action->bindings); ++i) {
switch (action->bindings[i].source) {
case BUTTON_SOURCE_NOT_SET:
break;
case BUTTON_SOURCE_KEYBOARD_PHYSICAL:
/* not pressed */
if (input->keyboard_state[action->bindings[i].code.scancode] == 0) {
action->just_changed = action->is_pressed;
action->is_pressed = false;
}
/* pressed */
else {
action->just_changed = !action->is_pressed;
action->is_pressed = true;
return;
}
break;
case BUTTON_SOURCE_MOUSE:
/* not pressed */
if ((input->mouse_state & action->bindings[i].code.mouse_button) == 0) {
action->just_changed = action->is_pressed;
action->is_pressed = false;
}
/* pressed */
else {
action->just_changed = !action->is_pressed;
action->is_pressed = true;
/*
* SDL_RenderWindowToLogical will turn window mouse
* coords into a position inside the logical render
* area. this has to be done to get an accurate point
* that can actually be used in game logic
*/
SDL_RenderWindowToLogical(input->renderer,
input->mouse_window_position.x,
input->mouse_window_position.y,
&action->position.x,
&action->position.y);
return;
}
break;
default:
break;
}
}
}
static void input_bind_code_to_action(struct input_state *input,
char *action_name,
enum button_source source,
union button_code code)
{
struct action_hash_item *action_item = shgetp_null(input->action_hash, action_name);
if (action_item == NULL) {
log_warn("(%s) Action \"%s\" does not exist.", __func__, action_name);
return;
}
struct action *action = &action_item->value;
/* check every binding to make sure this code isn't already bound */
for (size_t i = 0; i < SDL_arraysize(action->bindings); ++i) {
struct button *binding = &action->bindings[i];
if (binding->source != source)
break;
bool is_already_bound = false;
switch (binding->source) {
case BUTTON_SOURCE_NOT_SET:
break;
case BUTTON_SOURCE_KEYBOARD_PHYSICAL:
is_already_bound = binding->code.scancode == code.scancode;
break;
case BUTTON_SOURCE_KEYBOARD_CHARACTER:
is_already_bound = binding->code.keycode == code.keycode;
break;
case BUTTON_SOURCE_GAMEPAD:
is_already_bound = binding->code.gamepad_button == code.gamepad_button;
break;
case BUTTON_SOURCE_MOUSE:
is_already_bound = binding->code.mouse_button == code.mouse_button;
break;
}
if (is_already_bound) {
log_warn("(%s) Code already bound to action \"%s\".", __func__, action_name);
return;
}
}
/* if we're at max bindings, forget the first element and shift the rest */
if (action->num_bindings == SDL_arraysize(action->bindings)) {
--action->num_bindings;
size_t shifted_size = (sizeof action->bindings) - (sizeof action->bindings[0]);
memmove(action->bindings, action->bindings + 1, shifted_size);
}
action->bindings[action->num_bindings++] = (struct button) {
.source = source,
.code = code,
};
}
static void input_unbind_code_from_action(struct input_state *input,
char *action_name,
enum button_source source,
union button_code code)
{
struct action_hash_item *action_item = shgetp_null(input->action_hash, action_name);
if (action_item == NULL) {
log_warn("(%s) Action \"%s\" does not exist.", __func__, action_name);
return;
}
struct action *action = &action_item->value;
/* check every binding to make sure this code is bound */
size_t index = 0;
bool is_bound = false;
for (index = 0; index < SDL_arraysize(action->bindings); ++index) {
struct button *binding = &action->bindings[index];
if (binding->source != source)
continue;
switch (binding->source) {
case BUTTON_SOURCE_NOT_SET:
break;
case BUTTON_SOURCE_KEYBOARD_PHYSICAL:
is_bound = binding->code.scancode == code.scancode;
break;
case BUTTON_SOURCE_KEYBOARD_CHARACTER:
is_bound = binding->code.keycode == code.keycode;
break;
case BUTTON_SOURCE_GAMEPAD:
is_bound = binding->code.gamepad_button == code.gamepad_button;
break;
case BUTTON_SOURCE_MOUSE:
is_bound = binding->code.mouse_button == code.mouse_button;
break;
}
if (is_bound)
break;
}
if (!is_bound) {
log_warn("(%s) Code is not bound to action \"%s\".", __func__, action_name);
return;
}
/* remove the element to unbind and shift the rest so there isn't a gap */
if (action->num_bindings == SDL_arraysize(action->bindings)) {
size_t elements_after_index = action->num_bindings - index;
size_t shifted_size = elements_after_index * (sizeof action->bindings[0]);
memmove(action->bindings + index, action->bindings + index + 1, shifted_size);
--action->num_bindings;
}
}
void input_state_init(struct input_state *input, SDL_Renderer *renderer) {
sh_new_strdup(input->action_hash);
input->renderer = renderer;
}
void input_state_deinit(struct input_state *input) {
shfree(input->action_hash);
}
void input_state_update(struct input_state *input) {
input->keyboard_state = SDL_GetKeyboardState(NULL);
input->mouse_state = SDL_GetMouseState(&input->mouse_window_position.x,
&input->mouse_window_position.y);
for (size_t i = 0; i < shlenu(input->action_hash); ++i) {
struct action *action = &input->action_hash[i].value;
update_action_pressed_state(input, action);
}
}
void input_bind_action_scancode(struct input_state *input,
char *action_name,
SDL_Scancode scancode)
{
input_bind_code_to_action(input,
action_name,
BUTTON_SOURCE_KEYBOARD_PHYSICAL,
(union button_code) { .scancode = scancode });
}
void input_unbind_action_scancode(struct input_state *input,
char *action_name,
SDL_Scancode scancode)
{
input_unbind_code_from_action(input,
action_name,
BUTTON_SOURCE_KEYBOARD_PHYSICAL,
(union button_code) { .scancode = scancode });
}
void input_bind_action_mouse(struct input_state *input,
char *action_name,
uint8_t mouse_button)
{
input_bind_code_to_action(input,
action_name,
BUTTON_SOURCE_MOUSE,
(union button_code) { .mouse_button = mouse_button});
}
void input_unbind_action_mouse(struct input_state *input,
char *action_name,
uint8_t mouse_button)
{
input_unbind_code_from_action(input,
action_name,
BUTTON_SOURCE_MOUSE,
(union button_code) { .mouse_button = mouse_button});
}
void input_add_action(struct input_state *input, char *action_name) {
if (shgeti(input->action_hash, action_name) >= 0) {
log_warn("(%s) Action \"%s\" is already registered.", __func__, action_name);
return;
}
shput(input->action_hash, action_name, (struct action) { 0 });
}
void input_delete_action(struct input_state *input, char *action_name) {
if (shdel(input->action_hash, action_name) == 0)
log_warn("(%s) Action \"%s\" is not registered.", __func__, action_name);
}
bool input_is_action_pressed(struct input_state *input, char *action_name) {
struct action_hash_item *action = shgetp_null(input->action_hash, action_name);
if (action == NULL) {
log_warn("(%s) Action \"%s\" does not exist.", __func__, action_name);
return false;
}
return action->value.is_pressed;
}
bool input_is_action_just_pressed(struct input_state *input, char *action_name) {
struct action_hash_item *action = shgetp_null(input->action_hash, action_name);
if (action == NULL) {
log_warn("(%s) Action \"%s\" does not exist.", __func__, action_name);
return false;
}
return action->value.is_pressed && action->value.just_changed;
}
bool input_is_action_just_released(struct input_state *input, char *action_name) {
struct action_hash_item *action = shgetp_null(input->action_hash, action_name);
if (action == NULL) {
log_warn("(%s) Action \"%s\" does not exist.", __func__, action_name);
return false;
}
return !action->value.is_pressed && action->value.just_changed;
}
t_fvec2 input_get_action_position(struct input_state *input, char *action_name) {
struct action_hash_item *action = shgetp_null(input->action_hash, action_name);
if (action == NULL) {
log_warn("(%s) Action \"%s\" does not exist.", __func__, action_name);
return (t_fvec2) { 0 };
}
return action->value.position;
}

92
src/input.h Normal file
View File

@ -0,0 +1,92 @@
#ifndef INPUT_H
#define INPUT_H
#include "config.h"
#include "util.h"
#include <SDL2/SDL.h>
#include <stdint.h>
#include <stdbool.h>
enum button_source {
BUTTON_SOURCE_NOT_SET,
BUTTON_SOURCE_KEYBOARD_PHYSICAL,
BUTTON_SOURCE_KEYBOARD_CHARACTER,
BUTTON_SOURCE_GAMEPAD,
BUTTON_SOURCE_MOUSE,
};
union button_code {
SDL_Scancode scancode;
SDL_Keycode keycode;
SDL_GameControllerButton gamepad_button;
uint8_t mouse_button; /* SDL_BUTTON_ enum */
};
/* an input to which an action is bound */
/* it is not limited to literal buttons */
struct button {
enum button_source source;
union button_code code;
};
/* represents the collective state of a group of buttons */
/* that is, changes in the states of any of the bound buttons will affect it */
struct action {
size_t num_bindings;
struct button bindings[NUM_KEYBIND_SLOTS];
t_fvec2 position; /* set if applicable */
bool is_pressed;
bool just_changed;
};
struct action_hash_item {
char *key;
struct action value;
};
struct input_state {
struct action_hash_item *action_hash;
SDL_Renderer *renderer; /* some input relates to the screen in some way */
const uint8_t *keyboard_state; /* array of booleans indexed by scancode */
uint32_t mouse_state; /* SDL mouse button bitmask */
t_vec2 mouse_window_position;
enum button_source last_active_source;
bool is_anything_just_pressed;
};
void input_state_init(struct input_state *input, SDL_Renderer *renderer);
void input_state_deinit(struct input_state *input);
void input_state_update(struct input_state *input);
void input_bind_action_scancode(struct input_state *input,
char *action_name,
SDL_Scancode scancode);
void input_unbind_action_scancode(struct input_state *input,
char *action_name,
SDL_Scancode scancode);
void input_bind_action_mouse(struct input_state *input,
char *action_name,
uint8_t mouse_button);
void input_unbind_action_mouse(struct input_state *input,
char *action_name,
uint8_t mouse_button);
void input_add_action(struct input_state *input, char *action_name);
void input_delete_action(struct input_state *input, char *action_name);
bool input_is_action_pressed(struct input_state *input, char *action_name);
bool input_is_action_just_pressed(struct input_state *input, char *action_name);
bool input_is_action_just_released(struct input_state *input, char *action_name);
t_fvec2 input_get_action_position(struct input_state *input, char *action_name);
#endif

306
src/main.c Normal file
View File

@ -0,0 +1,306 @@
#include "context.h"
#include "rendering.h"
#include "input.h"
#include "util.h"
#include "textures.h"
#include "game/game.h"
#include <SDL2/SDL.h>
#include <SDL2/SDL_image.h>
#include <SDL2/SDL_ttf.h>
#include <physfs.h>
#include <stb_ds.h>
#include <stdlib.h>
#include <stdbool.h>
#include <stdio.h>
#include <inttypes.h>
#include <tgmath.h>
#include <limits.h>
#include <math.h>
static void poll_events(void) {
SDL_Event e;
while (SDL_PollEvent(&e)) {
switch (e.type) {
case SDL_QUIT:
ctx.is_running = false;
break;
case SDL_WINDOWEVENT:
if (e.window.windowID != ctx.window_id)
break;
switch (e.window.event) {
case SDL_WINDOWEVENT_RESIZED:
break;
}
break;
}
}
}
void main_loop(void) {
/*
if (!ctx.is_running) {
end(ctx);
clean_up(ctx);
emscripten_cancel_main_loop();
}
*/
/* frame timer */
int64_t current_frame_time = SDL_GetPerformanceCounter();
int64_t delta_time = current_frame_time - ctx.prev_frame_time;
ctx.prev_frame_time = current_frame_time;
/* handle unexpected timer anomalies (overflow, extra slow frames, etc) */
if (delta_time > ctx.desired_frametime * 8) { /* ignore extra-slow frames */
delta_time = ctx.desired_frametime;
}
delta_time = MAX(0, delta_time);
/* vsync time snapping */
/* get the refresh rate of the current display (it might not always be the same one) */
int display_framerate = 60; /* a reasonable guess */
SDL_DisplayMode current_display_mode;
int current_display_index = SDL_GetWindowDisplayIndex(ctx.window);
if (SDL_GetCurrentDisplayMode(current_display_index, &current_display_mode) == 0)
display_framerate = current_display_mode.refresh_rate;
int64_t snap_hz = display_framerate;
if (snap_hz <= 0)
snap_hz = 60;
/* these are to snap delta time to vsync values if it's close enough */
int64_t vsync_maxerror = (int64_t)((double)ctx.clocks_per_second * 0.0002);
size_t snap_frequency_count = 8;
for (size_t i = 0; i < snap_frequency_count; ++i) {
int64_t frequency = (ctx.clocks_per_second / snap_hz) * (i+1);
if (llabs(delta_time - frequency) < vsync_maxerror) {
delta_time = frequency;
break;
}
}
/* delta time averaging */
size_t time_averager_count = SDL_arraysize(ctx.time_averager);
for (size_t i = 0; i < time_averager_count - 1; ++i) {
ctx.time_averager[i] = ctx.time_averager[i+1];
}
ctx.time_averager[time_averager_count - 1] = delta_time;
int64_t averager_sum = 0;
for (size_t i = 0; i < time_averager_count; ++i) {
averager_sum += ctx.time_averager[i];
}
delta_time = averager_sum / time_averager_count;
ctx.delta_averager_residual += averager_sum % time_averager_count;
delta_time += ctx.delta_averager_residual / time_averager_count;
ctx.delta_averager_residual %= time_averager_count;
/* add to the accumulator */
ctx.frame_accumulator += delta_time;
/* spiral of death protection */
if (ctx.frame_accumulator > ctx.desired_frametime * 8) {
ctx.resync_flag = true;
}
/* timer resync if requested */
if (ctx.resync_flag) {
ctx.frame_accumulator = 0;
delta_time = ctx.desired_frametime;
ctx.resync_flag = false;
}
/* finally, let's get to work */
poll_events();
input_state_update(&ctx.input);
/* do we _always_ have to be dry? */
if (ctx.frame_accumulator >= ctx.desired_frametime * ctx.update_multiplicity) {
while (ctx.frame_accumulator >= ctx.desired_frametime * ctx.update_multiplicity) {
for (size_t i = 0; i < ctx.update_multiplicity; ++i) {
render_queue_clear();
game_tick();
ctx.frame_accumulator -= ctx.desired_frametime;
ctx.tick_count = (ctx.tick_count % ULLONG_MAX) + 1;
}
}
} else {
render_queue_clear();
}
/* the "step" will always run as long as there is time to spare. */
/* without interpolation, this is preferable to fixed ticks if the additional */
/* delta time calculations (like in physics code) are acceptable */
game_step(delta_time);
ctx.step_count = (ctx.step_count % ULLONG_MAX) + 1;
render();
}
static bool initialize(void) {
if (SDL_Init(SDL_INIT_EVERYTHING) == -1) {
CRY_SDL("SDL initialization failed.");
return false;
}
/* init got far enough to create a window */
ctx.window = SDL_CreateWindow("emerald",
SDL_WINDOWPOS_CENTERED,
SDL_WINDOWPOS_CENTERED,
RENDER_BASE_WIDTH,
RENDER_BASE_HEIGHT,
SDL_WINDOW_ALLOW_HIGHDPI | SDL_WINDOW_RESIZABLE);
if (ctx.window == NULL) {
CRY_SDL("Window creation failed.");
goto fail;
}
/* might need this to have multiple windows */
ctx.window_id = SDL_GetWindowID(ctx.window);
/* now that we have a window, we know a renderer can be created */
ctx.renderer = SDL_CreateRenderer(ctx.window, -1, SDL_RENDERER_PRESENTVSYNC);
/* SDL_SetHint(SDL_HINT_RENDER_LOGICAL_SIZE_MODE, "overscan"); */
/* SDL_RenderSetIntegerScale(ctx.renderer, SDL_TRUE); */
SDL_RenderSetLogicalSize(ctx.renderer, RENDER_BASE_WIDTH, RENDER_BASE_HEIGHT);
SDL_GetRendererOutputSize(ctx.renderer, &ctx.window_w, &ctx.window_h);
/* images */
if (IMG_Init(IMG_INIT_PNG) == 0) {
CRY_SDL("SDL_image initialization failed.");
goto fail;
}
/* filesystem time */
/* TODO: ANDROID: see the warning in physicsfs PHYSFS_init docs/header */
if (!PHYSFS_init(ctx.argv[0]) ||
!PHYSFS_setSaneConfig(ORGANIZATION_NAME, APP_NAME, PACKAGE_EXTENSION, false, true))
{
CRY_PHYSFS("Filesystem initialization failed.");
goto fail;
}
/* you could change this at runtime if you wanted */
ctx.update_multiplicity = 1;
/* debug mode _defaults_ to being enabled on debug builds. */
/* you should be able to enable it at runtime on any build */
#ifndef NDEBUG
ctx.debug = true;
#else
ctx.debug = false;
#endif
/* random seeding */
/* SDL_GetPerformanceCounter returns some platform-dependent number. */
/* it should vary between game instances. i checked! random enough for me. */
ctx.random_seed = SDL_GetPerformanceCounter();
srand((unsigned int)ctx.random_seed);
stbds_rand_seed(ctx.random_seed);
/* main loop machinery */
ctx.is_running = true;
ctx.resync_flag = true;
ctx.clocks_per_second = SDL_GetPerformanceFrequency();
ctx.prev_frame_time = SDL_GetPerformanceCounter();
ctx.desired_frametime = ctx.clocks_per_second / TICKS_PER_SECOND;
ctx.frame_accumulator = 0;
ctx.tick_count = 0;
ctx.step_count = 0;
/* delta time averaging */
ctx.delta_averager_residual = 0;
for (size_t i = 0; i < SDL_arraysize(ctx.time_averager); ++i) {
ctx.time_averager[i] = ctx.desired_frametime;
}
/* rendering */
/* these are dynamic arrays and will be allocated lazily by stb_ds */
ctx.render_queue_sprites = NULL;
ctx.render_queue_rectangles = NULL;
ctx.render_queue_circles = NULL;
ctx.circle_radius_hash = NULL;
textures_cache_init(&ctx.texture_cache, ctx.window);
if (TTF_Init() < 0) {
CRY_SDL("SDL_ttf initialization failed.");
goto fail;
}
/* input */
input_state_init(&ctx.input, ctx.renderer);
/* scripting */
/*
if (!scripting_init(ctx)) {
goto fail;
}
*/
return true;
fail:
SDL_Quit();
return false;
}
/* will not be called on an abnormal exit */
static void clean_up(void) {
/*
scripting_deinit(ctx);
*/
input_state_deinit(&ctx.input);
arrfree(ctx.render_queue_sprites);
arrfree(ctx.render_queue_rectangles);
textures_cache_deinit(&ctx.texture_cache);
PHYSFS_deinit();
TTF_Quit();
IMG_Quit();
SDL_Quit();
}
int main(int argc, char **argv) {
ctx.argc = argc;
ctx.argv = argv;
if (!initialize())
return EXIT_FAILURE;
for (int i = 1; i < (argc - 1); ++i) {
if (strcmp(argv[i], "--data-dir") == 0) {
if (!PHYSFS_mount(argv[i+1], NULL, true)) {
CRY_PHYSFS("Data dir mount override failed.");
return EXIT_FAILURE;
}
}
}
ctx.was_successful = true;
while (ctx.is_running)
main_loop();
game_end();
clean_up();
return ctx.was_successful ? EXIT_SUCCESS : EXIT_FAILURE;
}

34
src/private/rendering.h Normal file
View File

@ -0,0 +1,34 @@
#ifndef PRIVATE_RENDERING_H
#define PRIVATE_RENDERING_H
#include "../rendering.h"
#include "../util.h"
#include <SDL2/SDL.h>
#include <stdbool.h>
struct sprite_primitive {
t_frect rect;
t_color color;
double rotation;
char *path;
SDL_BlendMode blend_mode;
int atlas_index;
int layer;
bool flip_x;
bool flip_y;
};
struct rect_primitive {
t_frect rect;
t_color color;
};
struct circle_primitive {
float radius;
t_color color;
t_fvec2 position;
};
#endif

367
src/rendering.c Normal file
View File

@ -0,0 +1,367 @@
#include "private/rendering.h"
#include "context.h"
#include "config.h"
#include "textures.h"
#include <SDL2/SDL.h>
#include <stb_ds.h>
#include <stdlib.h>
#include <tgmath.h>
void render_queue_clear(void) {
/* since i don't intend to free the queues, */
/* it's faster and simpler to just "start over" */
/* and start overwriting the existing data */
arrsetlen(ctx.render_queue_sprites, 0);
arrsetlen(ctx.render_queue_rectangles, 0);
arrsetlen(ctx.render_queue_circles, 0);
}
/*
* an implementation note:
* try to avoid doing expensive work in the push functions,
* because they will be called multiple times in the main loop
* before anything is really rendered
*/
/* sprite */
void push_sprite(char *path, t_frect rect) {
textures_load(&ctx.texture_cache, path);
struct sprite_primitive sprite = {
.rect = rect,
.color = (t_color) { 255, 255, 255, 255 },
.path = path,
.rotation = 0.0,
.blend_mode = SDL_BLENDMODE_BLEND,
.atlas_index =
textures_get_atlas_index(&ctx.texture_cache, path),
.layer = 0,
.flip_x = false,
.flip_y = false,
};
arrput(ctx.render_queue_sprites, sprite);
}
void push_sprite_ex(t_frect rect, t_push_sprite_args args) {
textures_load(&ctx.texture_cache, args.path);
struct sprite_primitive sprite = {
.rect = rect,
.color = args.color,
.path = args.path,
.rotation = args.rotation,
.blend_mode = args.blend_mode,
.atlas_index =
textures_get_atlas_index(&ctx.texture_cache, args.path),
.layer = args.layer,
.flip_x = args.flip_x,
.flip_y = args.flip_y,
};
arrput(ctx.render_queue_sprites, sprite);
}
/* rectangle */
void push_rectangle(t_frect rect, t_color color) {
struct rect_primitive rectangle = {
.rect = rect,
.color = color,
};
arrput(ctx.render_queue_rectangles, rectangle);
}
/* circle */
void push_circle(t_fvec2 position, float radius, t_color color) {
struct circle_primitive circle = {
.radius = radius,
.color = color,
.position = position,
};
arrput(ctx.render_queue_circles, circle);
}
/* rendering */
static void render_background(void) {
SDL_SetRenderDrawColor(ctx.renderer, 230, 230, 230, 255);
SDL_Rect rect = {
0,
0,
ctx.window_w,
ctx.window_h
};
SDL_RenderFillRect(ctx.renderer, &rect);
SDL_SetRenderDrawColor(ctx.renderer, 255, 255, 255, 255);
}
/* compare functions for the sort in render_sprites */
static int cmp_atlases(const void *a, const void *b) {
int index_a = ((const struct sprite_primitive *)a)->atlas_index;
int index_b = ((const struct sprite_primitive *)b)->atlas_index;
return (index_a > index_b) - (index_a < index_b);
}
static int cmp_blend_modes(const void *a, const void *b) {
SDL_BlendMode mode_a = ((const struct sprite_primitive *)a)->blend_mode;
SDL_BlendMode mode_b = ((const struct sprite_primitive *)b)->blend_mode;
return (mode_a > mode_b) - (mode_a < mode_b);
}
static int cmp_colors(const void *a, const void *b) {
t_color color_a = ((const struct sprite_primitive *)a)->color;
t_color color_b = ((const struct sprite_primitive *)b)->color;
/* check reds */
if (color_a.r < color_b.r)
return -1;
else if (color_a.r > color_b.r)
return 1;
/* reds were equal, check greens */
else if (color_a.g < color_b.g)
return -1;
else if (color_a.g > color_b.g)
return 1;
/* greens were equal, check blues */
else if (color_a.b < color_b.b)
return -1;
else if (color_a.b > color_b.b)
return 1;
/* blues were equal, check alphas */
else if (color_a.a < color_b.a)
return -1;
else if (color_a.a > color_b.a)
return 1;
/* entirely equal */
else
return 0;
}
static int cmp_layers(const void *a, const void *b) {
int layer_a = ((const struct sprite_primitive *)a)->layer;
int layer_b = ((const struct sprite_primitive *)b)->layer;
return (layer_a > layer_b) - (layer_a < layer_b);
}
/* necessary to allow the renderer to draw in batches in the best case */
static void sort_sprites(struct sprite_primitive *sprites) {
size_t sprites_len = arrlenu(sprites);
qsort(sprites, sprites_len, sizeof *sprites, cmp_atlases);
qsort(sprites, sprites_len, sizeof *sprites, cmp_blend_modes);
qsort(sprites, sprites_len, sizeof *sprites, cmp_colors);
qsort(sprites, sprites_len, sizeof *sprites, cmp_layers);
}
static void render_sprite(struct sprite_primitive *sprite) {
SDL_Rect srcrect_value = { 0 };
SDL_Rect *srcrect = &srcrect_value;
SDL_Texture *texture = NULL;
/* loner */
if (sprite->atlas_index == -1) {
srcrect = NULL;
texture = textures_get_loner(&ctx.texture_cache, sprite->path);
} else {
*srcrect = textures_get_srcrect(&ctx.texture_cache, sprite->path);
texture = textures_get_atlas(&ctx.texture_cache, sprite->atlas_index);
}
SDL_RendererFlip flip = SDL_FLIP_NONE;
if (sprite->flip_x)
flip |= SDL_FLIP_HORIZONTAL;
if (sprite->flip_y)
flip |= SDL_FLIP_VERTICAL;
SDL_SetTextureColorMod(texture,
sprite->color.r,
sprite->color.g,
sprite->color.b);
SDL_SetTextureAlphaMod(texture, sprite->color.a);
SDL_SetTextureBlendMode(texture, sprite->blend_mode);
SDL_Rect rect = {
(int)sprite->rect.x,
(int)sprite->rect.y,
(int)sprite->rect.w,
(int)sprite->rect.h
};
SDL_RenderCopyEx(ctx.renderer,
texture,
srcrect,
&rect,
sprite->rotation,
NULL,
flip);
}
static void render_rectangle(struct rect_primitive *rectangle) {
SDL_SetRenderDrawColor(ctx.renderer,
rectangle->color.r,
rectangle->color.g,
rectangle->color.b,
rectangle->color.a);
SDL_Rect rect = {
(int)rectangle->rect.x,
(int)rectangle->rect.y,
(int)rectangle->rect.w,
(int)rectangle->rect.h
};
SDL_RenderFillRect(ctx.renderer, &rect);
SDL_SetRenderDrawColor(ctx.renderer, 255, 255, 255, 255);
}
/* vertices_out and indices_out MUST BE FREED */
static void create_circle_geometry(t_fvec2 position,
t_color color,
float radius,
size_t num_vertices,
SDL_Vertex **vertices_out,
int **indices_out)
{
SDL_Vertex *vertices = cmalloc(sizeof *vertices * (num_vertices + 1));
int *indices = cmalloc(sizeof *indices * (num_vertices * 3));
/* the angle (in radians) to rotate by on each iteration */
float seg_rotation_angle = (360.0f / (float)num_vertices) * ((float)M_PI / 180);
vertices[0].position.x = (float)position.x;
vertices[0].position.y = (float)position.y;
vertices[0].color.r = color.r;
vertices[0].color.g = color.g;
vertices[0].color.b = color.b;
vertices[0].color.a = color.a;
vertices[0].tex_coord = (SDL_FPoint){ 0, 0 };
/* this point will rotate around the center */
float start_x = 0.0f - radius;
float start_y = 0.0f;
for (size_t i = 1; i < num_vertices + 1; ++i) {
float final_seg_rotation_angle = (float)i * seg_rotation_angle;
vertices[i].position.x =
cos(final_seg_rotation_angle) * start_x -
sin(final_seg_rotation_angle) * start_y;
vertices[i].position.y =
cos(final_seg_rotation_angle) * start_y +
sin(final_seg_rotation_angle) * start_x;
vertices[i].position.x += position.x;
vertices[i].position.y += position.y;
vertices[i].color.r = color.r;
vertices[i].color.g = color.g;
vertices[i].color.b = color.b;
vertices[i].color.a = color.a;
vertices[i].tex_coord = (SDL_FPoint){ 0, 0 };
size_t triangle_offset = 3 * (i - 1);
/* center point index */
indices[triangle_offset] = 0;
/* generated point index */
indices[triangle_offset + 1] = (int)i;
size_t index = (i + 1) % num_vertices;
if (index == 0)
index = num_vertices;
indices[triangle_offset + 2] = (int)index;
}
*vertices_out = vertices;
*indices_out = indices;
}
static void render_circle(struct circle_primitive *circle) {
SDL_Vertex *vertices = NULL;
int *indices = NULL;
int num_vertices = (int)circle->radius;
create_circle_geometry(circle->position,
circle->color,
circle->radius,
num_vertices,
&vertices,
&indices);
SDL_RenderGeometry(ctx.renderer, NULL,
vertices,
num_vertices + 1, /* vertices + center vertex */
indices,
num_vertices * 3);
free(vertices);
free(indices);
}
static void render_sprites(void) {
if (ctx.texture_cache.is_dirty)
textures_update_current_atlas(&ctx.texture_cache);
sort_sprites(ctx.render_queue_sprites);
for (size_t i = 0; i < arrlenu(ctx.render_queue_sprites); ++i) {
render_sprite(&ctx.render_queue_sprites[i]);
}
}
static void render_rectangles(void) {
for (size_t i = 0; i < arrlenu(ctx.render_queue_rectangles); ++i) {
render_rectangle(&ctx.render_queue_rectangles[i]);
}
}
static void render_circles(void) {
for (size_t i = 0; i < arrlenu(ctx.render_queue_circles); ++i) {
render_circle(&ctx.render_queue_circles[i]);
}
}
void render(void) {
SDL_SetRenderDrawBlendMode(ctx.renderer, SDL_BLENDMODE_BLEND);
SDL_SetRenderDrawColor(ctx.renderer, 0, 0, 0, 255);
SDL_RenderClear(ctx.renderer);
SDL_SetRenderDrawColor(ctx.renderer, 255, 255, 255, 255);
render_background();
render_sprites();
render_rectangles();
render_circles();
SDL_RenderPresent(ctx.renderer);
}

41
src/rendering.h Normal file
View File

@ -0,0 +1,41 @@
#ifndef RENDERING_H
#define RENDERING_H
#include "util.h"
#include <SDL2/SDL.h>
#include <stdbool.h>
typedef struct push_sprite_args {
char *path;
int layer;
t_color color;
double rotation;
SDL_BlendMode blend_mode;
bool flip_x;
bool flip_y;
} t_push_sprite_args;
/* clears all render queues */
void render_queue_clear(void);
/* pushes a sprite onto the sprite render queue */
/* this is a simplified version of push_sprite_ex for the most common case. */
/* it assumes you want no color modulation, no rotation, no flip, layer 0, and alpha blending */
void push_sprite(char *path, t_frect rect);
/* pushes a sprite onto the sprite render queue */
/* note that if args is zeroed out, the color will be transparent black */
void push_sprite_ex(t_frect rect, t_push_sprite_args args);
/* pushes a filled rectangle onto the rectangle render queue */
void push_rectangle(t_frect rect, t_color color);
/* pushes a filled circle onto the circle render queue */
void push_circle(t_fvec2 position, float radius, t_color color);
/* renders the background, then the primitives in all render queues */
void render(void);
#endif

139
src/scripting.c Normal file
View File

@ -0,0 +1,139 @@
#include "scripting.h"
#include "util.h"
#include "context.h"
#include <SDL2/SDL.h>
#include <umka_api.h>
#include <physfs.h>
#include <stdbool.h>
#include <string.h>
#include <stdio.h>
static void msgbox(UmkaStackSlot *params, UmkaStackSlot *result) {
SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_INFORMATION,
params[1].ptrVal, params[0].ptrVal, NULL);
}
static void umka_log_info(UmkaStackSlot *params, UmkaStackSlot *result) {
log_info(params[0].ptrVal);
}
static void umka_log_critical(UmkaStackSlot *params, UmkaStackSlot *result) {
log_critical(params[0].ptrVal);
}
static void umka_log_warn(UmkaStackSlot *params, UmkaStackSlot *result) {
log_warn(params[0].ptrVal);
}
static void is_action_pressed(UmkaStackSlot *params, UmkaStackSlot *result) {
struct state *state = params[1].ptrVal;
t_ctx *ctx = state->hidden_ptr;
bool value = input_is_action_pressed(&ctx->input, params[0].ptrVal);
result->uintVal = value;
}
static void is_action_just_pressed(UmkaStackSlot *params, UmkaStackSlot *result) {
struct state *state = params[1].ptrVal;
t_ctx *ctx = state->hidden_ptr;
bool value = input_is_action_just_pressed(&ctx->input, params[0].ptrVal);
result->uintVal = value;
}
static void is_action_just_released(UmkaStackSlot *params, UmkaStackSlot *result) {
struct state *state = params[1].ptrVal;
t_ctx *ctx = state->hidden_ptr;
bool value = input_is_action_just_released(&ctx->input, params[0].ptrVal);
result->uintVal = value;
}
static void get_action_position(UmkaStackSlot *params, UmkaStackSlot *result) {
struct state *state = params[2].ptrVal;
t_ctx *ctx = state->hidden_ptr;
t_fvec2 *position = params[0].ptrVal;
*position = input_get_action_position(&ctx->input, params[1].ptrVal);
// the result is in a hidden result pointer allocated by Umka
result->ptrVal = params[0].ptrVal;
}
static void register_api(void *umka) {
umkaAddFunc(umka, "msgbox", msgbox);
umkaAddFunc(umka, "logInfo", umka_log_info);
umkaAddFunc(umka, "logCritical", umka_log_critical);
umkaAddFunc(umka, "logWarn", umka_log_warn);
umkaAddFunc(umka, "cImplIsActionPressed", is_action_pressed);
umkaAddFunc(umka, "cImplIsActionJustPressed", is_action_just_pressed);
umkaAddFunc(umka, "cImplIsActionJustReleased", is_action_just_released);
umkaAddFunc(umka, "getActionPosition", get_action_position);
}
bool scripting_init(t_ctx *ctx) {
if (!PHYSFS_exists("/scripts/main.um")) {
CRY("Failed to initialize scripting", "Could not find a main.um (we need it)");
return false;
}
ctx->umka = umkaAlloc();
char *main_script = file_to_str("/scripts/main.um");
bool umka_ok = umkaInit(ctx->umka,
"main.um",
main_script,
UMKA_STACK_SIZE,
NULL,
ctx->argc,
ctx->argv,
false,
false,
NULL);
free(main_script);
if (!umka_ok) {
CRY("Failed to initialize scripting", "Unknown Umka error");
return false;
}
/* all Umka files are compiled even if they're never used */
char **dir_file_names = PHYSFS_enumerateFiles("/scripts");
for (char **i = dir_file_names; *i != NULL; ++i) {
char *file_name = *i;
if (!strends(file_name, ".um"))
continue;
/* got this one already */
if (strcmp(file_name, "main.um") == 0)
continue;
/* need to figure out the actual path (as opposed to the lone file name) */
const char *path_prefix = "/scripts/";
size_t path_size = snprintf(NULL, 0, "%s%s", path_prefix, file_name) + 1;
char *path = cmalloc(path_size);
snprintf(path, path_size, "%s%s", path_prefix, file_name);
char *contents = file_to_str(path);
umkaAddModule(ctx->umka, file_name, contents);
free(path);
free(contents);
}
PHYSFS_freeList(dir_file_names);
register_api(ctx->umka);
if (!umkaCompile(ctx->umka)) {
cry_umka(ctx->umka);
return false;
}
return true;
}
void scripting_deinit(t_ctx *ctx) {
umkaFree(ctx->umka);
}

18
src/scripting.h Normal file
View File

@ -0,0 +1,18 @@
#ifndef SCRIPTING_H
#define SCRIPTING_H
#include <umka_api.h>
#include <stdint.h>
#include <stdbool.h>
typedef struct context t_ctx;
struct state {
t_ctx *hidden_ptr;
uint64_t tick_count;
};
bool scripting_init(void);
#endif

5
src/text.c Normal file
View File

@ -0,0 +1,5 @@
#include "text.h"
#include "SDL_ttf.h"

29
src/text.h Normal file
View File

@ -0,0 +1,29 @@
#ifndef TEXT_H
#define TEXT_H
#include "util.h"
#include "SDL.h"
#include "SDL_ttf.h"
struct text {
char *text;
t_color color;
int ptsize;
};
struct text_cache_item {
char *key;
struct text *value;
};
struct text_cache {
struct text_cache_item *hash;
};
#endif

357
src/textures.c Normal file
View File

@ -0,0 +1,357 @@
#include "textures.h"
#include "config.h"
#include "util.h"
#include <SDL2/SDL.h>
#include <SDL2/SDL_image.h>
#include <physfs.h>
#include <physfsrwops.h>
#include <stb_ds.h>
#include <stb_rect_pack.h>
#include <limits.h>
#include <stdbool.h>
#include <stdlib.h>
#include <stdio.h>
static SDL_Surface *image_to_surface(char *path) {
SDL_RWops *handle = PHYSFSRWOPS_openRead(path);
if (handle == NULL)
goto fail;
SDL_Surface *result = IMG_Load_RW(handle, true);
if (result == NULL)
goto fail;
SDL_SetSurfaceBlendMode(result, SDL_BLENDMODE_NONE);
SDL_SetSurfaceRLE(result, true);
return result;
fail:
CRY(path, "Failed to load image. Aborting...");
die_abruptly();
}
/* adds a new, blank atlas surface to the cache */
static void add_new_atlas(struct texture_cache *cache) {
SDL_PixelFormat *native_format =
SDL_AllocFormat(SDL_GetWindowPixelFormat(cache->window));
/* the window format won't have an alpha channel, so we figure this out */
#if SDL_BYTEORDER == SDL_BIG_ENDIAN
uint32_t a_mask = 0x000000FF;
#else
uint32_t a_mask = 0xFF000000;
#endif
SDL_Surface *new_atlas = SDL_CreateRGBSurface(0,
TEXTURE_ATLAS_SIZE,
TEXTURE_ATLAS_SIZE,
TEXTURE_ATLAS_BIT_DEPTH,
native_format->Rmask,
native_format->Gmask,
native_format->Bmask,
a_mask);
SDL_FreeFormat(native_format);
SDL_SetSurfaceRLE(new_atlas, true);
arrput(cache->atlas_surfaces, new_atlas);
SDL_Texture *new_atlas_texture =
SDL_CreateTextureFromSurface(cache->renderer, new_atlas);
arrput(cache->atlas_textures, new_atlas_texture);
}
static void recreate_current_atlas_texture(struct texture_cache *cache) {
/* TODO: figure out if SDL_UpdateTexture alone is faster than blitting */
SDL_Surface *atlas_surface = cache->atlas_surfaces[cache->atlas_index];
/* clear */
SDL_FillRect(atlas_surface, NULL, 0);
/* blit the texture surfaces onto the atlas */
for (size_t i = 0; i < shlenu(cache->hash); ++i) {
if (cache->hash[i].value.atlas_index != cache->atlas_index)
continue;
SDL_BlitSurface(cache->hash[i].value.data,
NULL,
atlas_surface,
&cache->hash[i].value.srcrect);
}
/* texturize it! */
SDL_LockSurface(atlas_surface);
SDL_UpdateTexture(cache->atlas_textures[cache->atlas_index],
NULL,
atlas_surface->pixels,
atlas_surface->pitch);
SDL_UnlockSurface(atlas_surface);
}
/* uses the textures currently in the cache to create an array of stbrp_rects */
static stbrp_rect *create_rects_from_cache(struct texture_cache *cache) {
stbrp_rect *rects = NULL;
for (size_t i = 0; i < shlenu(cache->hash); ++i) {
SDL_Surface *surface_data = cache->hash[i].value.data;
stbrp_rect new_rect = {
.w = surface_data->w,
.h = surface_data->h,
};
arrput(rects, new_rect);
}
return rects;
}
/* returns an array which contains a _copy_ of every unpacked rect in rects. */
/* each of these copies will have their original index in rects saved in */
/* their `id` field, which is an int. */
static stbrp_rect *filter_unpacked_rects(stbrp_rect *rects) {
stbrp_rect *unpacked_rects = NULL;
for (size_t i = 0; i < arrlenu(rects); ++i) {
/* already packed */
if (rects[i].was_packed)
continue;
arrput(unpacked_rects, rects[i]);
/* stb_rect_pack mercifully gives you a free userdata int */
/* the index is saved there so the original array can be updated later */
unpacked_rects[arrlenu(unpacked_rects)-1].id = (int)i;
}
return unpacked_rects;
}
/* updates the original rects array with the data from packed_rects */
/* returns true if all rects were packed successfully */
static bool update_rects(struct texture_cache *cache, stbrp_rect *rects, stbrp_rect *packed_rects) {
/* !!! do not grow either of the arrays !!! */
/* the reallocation will try to reassign the array pointer, to no effect. */
/* see stb_ds.h */
bool packed_all = true;
for (size_t i = 0; i < arrlenu(packed_rects); ++i) {
/* we can check if any rects failed to be packed right here */
/* it's not ideal, but it avoids another iteration */
if (!packed_rects[i].was_packed) {
packed_all = false;
continue;
}
rects[packed_rects[i].id] = packed_rects[i];
/* while the order of the elements in the hash map is unknown to us, */
/* their equivalents in `rects` are in that same (unknown) order, which means */
/* we can use the index we had saved to find the original texture struct */
cache->hash[packed_rects[i].id].value.atlas_index = cache->atlas_index;
}
return packed_all;
}
/* updates the atlas location of every rect in the cache */
static void update_texture_rects_in_atlas(struct texture_cache *cache, stbrp_rect *rects) {
for (size_t i = 0; i < arrlenu(rects); ++i) {
cache->hash[i].value.srcrect = (SDL_Rect) {
.x = rects[i].x,
.y = rects[i].y,
.w = rects[i].w,
.h = rects[i].h,
};
}
}
void textures_cache_init(struct texture_cache *cache, SDL_Window *window) {
cache->window = window;
cache->renderer = SDL_GetRenderer(window);
sh_new_arena(cache->hash);
sh_new_arena(cache->loner_hash);
cache->node_buffer = cmalloc(sizeof *cache->node_buffer * TEXTURE_ATLAS_SIZE);
add_new_atlas(cache);
recreate_current_atlas_texture(cache);
}
void textures_cache_deinit(struct texture_cache *cache) {
/* free atlas textures */
for (size_t i = 0; i < arrlenu(cache->atlas_textures); ++i) {
SDL_DestroyTexture(cache->atlas_textures[i]);
}
arrfree(cache->atlas_textures);
/* free atlas surfaces */
for (size_t i = 0; i < arrlenu(cache->atlas_surfaces); ++i) {
SDL_FreeSurface(cache->atlas_surfaces[i]);
}
arrfree(cache->atlas_surfaces);
/* free cache hashes */
for (size_t i = 0; i < shlenu(cache->hash); ++i) {
SDL_FreeSurface(cache->hash[i].value.data);
}
shfree(cache->hash);
for (size_t i = 0; i < shlenu(cache->loner_hash); ++i) {
SDL_FreeSurface(cache->loner_hash[i].value.data);
}
shfree(cache->loner_hash);
free(cache->node_buffer);
}
void textures_dump_atlases(struct texture_cache *cache) {
PHYSFS_mkdir("/dump");
const char string_template[] = "/dump/atlas%zd.png";
char buf[2048]; /* larger than will ever be necessary */
size_t i = 0;
for (; i < arrlenu(cache->atlas_surfaces); ++i) {
snprintf(buf, sizeof buf, string_template, i);
SDL_RWops *handle = PHYSFSRWOPS_openWrite(buf);
if (handle == NULL) {
CRY("Texture atlas dump failed.", "File could not be opened");
return;
}
IMG_SavePNG_RW(cache->atlas_surfaces[i], handle, true);
log_info("Dumped atlas %s", buf);
}
size_t num_loners = shlenu(cache->loner_hash);
log_info("%zd atlases dumped. %zd loners left undumped.", i, num_loners);
}
void textures_load(struct texture_cache *cache, char *path) {
/* no need to do anything if it was loaded already */
if (shgeti(cache->hash, path) >= 0 || shgeti(cache->loner_hash, path) >= 0)
return;
SDL_Surface *surface = image_to_surface(path);
struct texture new_texture;
new_texture.data = surface;
/* it's a "loner texture," it doesn't fit in an atlas so it's not in one */
if (surface->w > TEXTURE_ATLAS_SIZE || surface->h > TEXTURE_ATLAS_SIZE) {
new_texture.loner_data = SDL_CreateTextureFromSurface(cache->renderer, surface);
new_texture.atlas_index = -1;
shput(cache->loner_hash, path, new_texture);
} else {
new_texture.atlas_index = cache->atlas_index;
shput(cache->hash, path, new_texture);
cache->is_dirty = true;
}
}
void textures_update_current_atlas(struct texture_cache *cache) {
/* this function makes a lot more sense if you read stb_rect_pack.h */
stbrp_context pack_ctx; /* target info */
stbrp_init_target(&pack_ctx,
TEXTURE_ATLAS_SIZE,
TEXTURE_ATLAS_SIZE,
cache->node_buffer,
TEXTURE_ATLAS_SIZE);
stbrp_rect *rects = create_rects_from_cache(cache);
/* we have to keep packing, and creating atlases if necessary, */
/* until all rects have been packed. */
/* ideally, this will not iterate more than once. */
bool textures_remaining = true;
while (textures_remaining) {
stbrp_rect *rects_to_pack = filter_unpacked_rects(rects);
stbrp_pack_rects(&pack_ctx, rects_to_pack, (int)arrlen(rects_to_pack));
textures_remaining = !update_rects(cache, rects, rects_to_pack);
arrfree(rects_to_pack); /* got what we needed */
/* some textures couldn't be packed */
if (textures_remaining) {
update_texture_rects_in_atlas(cache, rects);
recreate_current_atlas_texture(cache);
/* need a new atlas for next time */
add_new_atlas(cache);
++cache->atlas_index;
}
};
update_texture_rects_in_atlas(cache, rects);
recreate_current_atlas_texture(cache);
cache->is_dirty = false;
arrfree(rects);
}
SDL_Rect textures_get_srcrect(struct texture_cache *cache, char *path) {
struct texture_cache_item *texture = shgetp_null(cache->hash, path);
if (texture == NULL) {
CRY("Texture lookup failed.",
"Tried to get texture that isn't loaded.");
return (SDL_Rect){ 0, 0, 0, 0 };
}
return texture->value.srcrect;
}
int textures_get_atlas_index(struct texture_cache *cache, char *path) {
struct texture_cache_item *texture = shgetp_null(cache->hash, path);
/* it might be a loner texture */
if (texture == NULL) {
texture = shgetp_null(cache->loner_hash, path);
/* never mind it's just not there at all */
if (texture == NULL) {
CRY("Texture atlas index lookup failed.",
"Tried to get atlas index of texture that isn't loaded.");
return INT_MIN;
}
}
return texture->value.atlas_index;
}
SDL_Texture *textures_get_atlas(struct texture_cache *cache, int index) {
/* out of bounds */
if (arrlen(cache->atlas_textures) < index + 1 || index < 0)
return NULL;
return cache->atlas_textures[index];
}
SDL_Texture *textures_get_loner(struct texture_cache *cache, char *path) {
struct texture_cache_item *texture = shgetp_null(cache->loner_hash, path);
if (texture == NULL) {
CRY("Loner texture lookup failed.",
"Tried to get texture that isn't loaded.");
return NULL;
}
return texture->value.loner_data;
}
size_t textures_get_num_atlases(struct texture_cache *cache) {
return cache->atlas_index + 1;
}

78
src/textures.h Normal file
View File

@ -0,0 +1,78 @@
#ifndef TEXTURES_H
#define TEXTURES_H
#include <SDL2/SDL.h>
#include <stb_rect_pack.h>
#include <stdbool.h>
struct texture {
SDL_Rect srcrect; /* position in atlas */
SDL_Surface *data; /* original image data */
SDL_Texture *loner_data; /* loner textures store their data directly */
int atlas_index; /* which atlas the texture is in */
int8_t layer;
};
struct texture_cache_item {
char *key;
struct texture value;
};
/* use the public API to create and manipulate instances of this structure */
struct texture_cache {
/* from context */
SDL_Window *window;
SDL_Renderer *renderer;
struct texture_cache_item *hash;
struct texture_cache_item *loner_hash;
stbrp_node *node_buffer; /* used internally by stb_rect_pack */
SDL_Surface **atlas_surfaces;
SDL_Texture **atlas_textures;
int atlas_index;
bool is_dirty; /* current atlas needs to be recreated */
};
void textures_cache_init(struct texture_cache *cache, SDL_Window *window);
void textures_cache_deinit(struct texture_cache *cache);
/* for debugging */
void textures_dump_atlases(struct texture_cache *cache);
/* loads an image if it isn't in the cache, otherwise a no-op. */
/* can be called from anywhere at any time after init, useful if you want to */
/* preload textures you know will definitely be used */
void textures_load(struct texture_cache *cache, char *path);
/* repacks the current texture atlas based on the texture cache */
void textures_update_current_atlas(struct texture_cache *cache);
/* returns a rect in a texture cache atlas based on a path, for drawing */
/* if the texture is not found, returns a zero-filled rect (so check w or h) */
SDL_Rect textures_get_srcrect(struct texture_cache *cache, char *path);
/* returns which atlas the texture in the path is in, starting from 0 */
/* if the texture is not found, returns INT_MIN */
int textures_get_atlas_index(struct texture_cache *cache, char *path);
/* returns a pointer to the atlas at `index` */
/* if the index is out of bounds, returns NULL. */
/* you can get the index via texture_get_atlas_index */
SDL_Texture *textures_get_atlas(struct texture_cache *cache, int index);
SDL_Texture *textures_get_loner(struct texture_cache *cache, char *path);
/* returns the number of atlases in the cache */
size_t textures_get_num_atlases(struct texture_cache *cache);
#endif

191
src/util.c Normal file
View File

@ -0,0 +1,191 @@
#include "util.h"
#include <SDL2/SDL.h>
#include <physfsrwops.h>
#define STB_DS_IMPLEMENTATION
#define STBDS_ASSERT SDL_assert
#define STBDS_REALLOC(c,p,s) crealloc(p, s)
#define STBDS_FREE(c,p) free(p)
#include <stb_ds.h>
#define STB_RECT_PACK_IMPLEMENTATION
#define STBRP_ASSERT SDL_assert
#include <stb_rect_pack.h>
#include <stdlib.h>
#include <stdarg.h>
#include <stdnoreturn.h>
#include <limits.h>
#include <string.h>
void cry_impl(const char *file, const int line, const char *title, const char *text) {
SDL_LogCritical(SDL_LOG_CATEGORY_APPLICATION,
"TEARS AT %s:%d: %s ... %s", file, line, title, text);
SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, title, text, NULL);
}
static void log_impl(const char *restrict format, va_list args, SDL_LogPriority priority) {
SDL_LogMessageV(SDL_LOG_CATEGORY_APPLICATION,
priority,
format,
args);
}
void log_info(const char *restrict format, ...) {
va_list args;
va_start(args, format);
log_impl(format, args, SDL_LOG_PRIORITY_INFO);
va_end(args);
}
void log_critical(const char *restrict format, ...) {
va_list args;
va_start(args, format);
log_impl(format, args, SDL_LOG_PRIORITY_CRITICAL);
va_end(args);
}
void log_warn(const char *restrict format, ...) {
va_list args;
va_start(args, format);
log_impl(format, args, SDL_LOG_PRIORITY_WARN);
va_end(args);
}
noreturn static void alloc_failure_death(void) {
log_critical("Allocation failure. Aborting NOW.");
SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR,
"MEMORY ALLOCATION FAILURE.",
"FAILED TO ALLOCATE MEMORY. "
"YOU MIGHT BE UNLUCKY. "
"THE GAME WILL EXIT NOW.",
NULL);
die_abruptly();
}
noreturn void die_abruptly(void) {
/* a zombie window will linger if we don't at least try to quit SDL */
SDL_Quit();
abort();
}
void *cmalloc(size_t size) {
void *ptr = malloc(size);
if (ptr == NULL)
alloc_failure_death();
return ptr;
}
void *crealloc(void *ptr, size_t size) {
void *out = realloc(ptr, size);
if (out == NULL)
alloc_failure_death();
return out;
}
void *ccalloc(size_t num, size_t size) {
void *ptr = calloc(num, size);
if (ptr == NULL)
alloc_failure_death();
return ptr;
}
int64_t file_to_bytes(char *path, unsigned char **buf_out) {
SDL_RWops *handle = PHYSFSRWOPS_openRead(path);
if (handle == NULL) {
return -1;
}
int64_t data_size = SDL_RWseek(handle, 0, RW_SEEK_END);
SDL_RWseek(handle, 0, RW_SEEK_SET); /* reset offset into data */
*buf_out = cmalloc(data_size);
SDL_RWread(handle, *buf_out, sizeof **buf_out, data_size / sizeof **buf_out);
SDL_RWclose(handle); /* we got all we needed from the stream */
return data_size;
}
char *file_to_str(char *path) {
SDL_RWops *handle = PHYSFSRWOPS_openRead(path);
if (handle == NULL) {
return NULL;
}
int64_t data_size = SDL_RWseek(handle, 0, RW_SEEK_END);
SDL_RWseek(handle, 0, RW_SEEK_SET); /* reset offset into data */
char *str_out = cmalloc(data_size + 1); /* data plus final null */
size_t len = data_size / sizeof *str_out;
SDL_RWread(handle, str_out, sizeof *str_out, len);
SDL_RWclose(handle); /* we got all we needed from the stream */
str_out[len] = '\0';
return str_out;
}
bool strends(const char *str, const char *suffix) {
size_t str_length = strlen(str);
size_t suffix_length = strlen(suffix);
if (suffix_length > str_length)
return false;
return memcmp((str + str_length) - suffix_length, suffix, suffix_length) == 0;
}
bool intersect_rect(const t_rect *a, const t_rect *b, t_rect *result) {
SDL_Rect a_sdl = { a->x, a->y, a->w, a->h };
SDL_Rect b_sdl = { b->x, b->y, b->w, b->h };
SDL_Rect result_sdl = { 0 };
bool intersection = SDL_IntersectRect(&a_sdl, &b_sdl, &result_sdl);
*result = (t_rect){ result_sdl.x, result_sdl.y, result_sdl.w, result_sdl.h };
return intersection;
}
bool intersect_frect(const t_frect *a, const t_frect *b, t_frect *result) {
SDL_FRect a_sdl = { a->x, a->y, a->w, a->h };
SDL_FRect b_sdl = { b->x, b->y, b->w, b->h };
SDL_FRect result_sdl = { 0 };
bool intersection = SDL_IntersectFRect(&a_sdl, &b_sdl, &result_sdl);
*result = (t_frect){ result_sdl.x, result_sdl.y, result_sdl.w, result_sdl.h };
return intersection;
}
t_frect to_frect(t_rect rect) {
return (t_frect) {
.h = (float)rect.h,
.w = (float)rect.w,
.x = (float)rect.x,
.y = (float)rect.y,
};
}
void tick_timer(int *value) {
*value = MAX(*value - 1, 0);
}

120
src/util.h Normal file
View File

@ -0,0 +1,120 @@
#ifndef UTIL_H
#define UTIL_H
#include <SDL2/SDL.h>
#include <physfs.h>
#include <stdlib.h>
#include <stdint.h>
#include <stdbool.h>
#include <math.h>
#include <stdnoreturn.h>
/* */
/* GENERAL UTILITIES */
/* */
void cry_impl(const char *file, const int line, const char *title, const char *text);
#define CRY(title, text) cry_impl(__FILE__, __LINE__, title, text)
#define CRY_SDL(title) cry_impl(__FILE__, __LINE__, title, SDL_GetError())
#define CRY_PHYSFS(title) \
cry_impl(__FILE__, __LINE__, title, PHYSFS_getErrorByCode(PHYSFS_getLastErrorCode()))
void log_info(const char *restrict format, ...);
void log_critical(const char *restrict format, ...);
void log_warn(const char *restrict format, ...);
/* for when there's absolutely no way to continue */
noreturn void die_abruptly(void);
/* "critical" allocation functions which will log and abort() on failure. */
/* if it is reasonable to handle this gracefully, use the standard versions. */
/* the stb implementations will be configured to use these */
void *cmalloc(size_t size);
void *crealloc(void *ptr, size_t size);
void *ccalloc(size_t num, size_t size);
/* DON'T FORGET ABOUT DOUBLE EVALUATION */
/* YOU THINK YOU WON'T, AND THEN YOU DO */
/* C23's typeof could fix it, so i will */
/* leave them as macros to be replaced. */
/* use the macro in tgmath.h for floats */
#define MAX SDL_max
#define MIN SDL_min
/* sets buf_out to a pointer to a byte buffer which must be freed. */
/* returns the size of this buffer. */
int64_t file_to_bytes(char *path, unsigned char **buf_out);
/* returns a pointer to a string which must be freed */
char *file_to_str(char *path);
/* returns true if str ends with suffix */
bool strends(const char *str, const char *suffix);
/* */
/* GAME LOGIC UTILITIES */
/* */
/* 32-bit color data */
typedef struct color {
uint8_t r;
uint8_t g;
uint8_t b;
uint8_t a;
} t_color;
/* a rectangle with the origin at the upper left (integer) */
typedef struct rect {
int x, y;
int w, h;
} t_rect;
bool intersect_rect(const t_rect *a, const t_rect *b, t_rect *result);
/* a rectangle with the origin at the upper left (floating point) */
typedef struct frect {
float x, y;
float w, h;
} t_frect;
bool intersect_frect(const t_frect *a, const t_frect *b, t_frect *result);
t_frect to_frect(t_rect rect);
/* a point in some space (integer) */
typedef struct vec2 {
int x, y;
} t_vec2;
/* a point in some space (floating point) */
typedef struct fvec2 {
float x, y;
} t_fvec2;
/* decrements an lvalue (which should be an int), stopping at 0 */
/* meant for tick-based timers in game logic */
/*
* example:
* tick_timer(&player->jump_air_timer);
*/
void tick_timer(int *value);
#endif

3
third-party/README.md vendored Normal file
View File

@ -0,0 +1,3 @@
libraries that don't make sense to get from the system or Conan.
avoid modifying the source if possible, it makes everything a mess later

View File

@ -0,0 +1,2 @@
github: [icculus]
patreon: icculus

View File

@ -0,0 +1,30 @@
name: Build
on: [push, pull_request]
jobs:
Build:
name: ${{ matrix.platform.name }}
runs-on: ${{ matrix.platform.os }}
strategy:
matrix:
platform: # !!! FIXME: figure out an efficient way to get SDL2 on the Windows/Mac bots.
- { name: Linux, os: ubuntu-20.04, flags: -GNinja }
- { name: MinGW, os: windows-latest, flags: -GNinja -DCMAKE_C_COMPILER=x86_64-w64-mingw32-gcc -DCMAKE_SYSTEM_NAME=Windows }
- { name: Windows, os: windows-latest }
- { name: MacOS, os: macos-latest }
steps:
- name: Setup Linux dependencies
if: runner.os == 'Linux'
run: |
sudo apt-get update
sudo apt-get install ninja-build
- name: Setup MinGW dependencies
if: contains(matrix.platform.name, 'MinGW')
run: choco install ninja
- name: Get PhysicsFS sources
uses: actions/checkout@v2
- name: Configure CMake
run: cmake -B build ${{ matrix.platform.flags }}
- name: Build
run: cmake --build build/

View File

@ -0,0 +1,20 @@
name: Build (OS/2)
on: [push, pull_request]
jobs:
os2:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: open-watcom/setup-watcom@v0
- name: Build physfs.dll
run: |
cd src
wmake -f Makefile.os2
cd ..
- name: distclean
run: |
cd src
wmake -f Makefile.os2 distclean
cd ..

2
third-party/physfs/.gitignore vendored Normal file
View File

@ -0,0 +1,2 @@
cmake-build

345
third-party/physfs/CMakeLists.txt vendored Normal file
View File

@ -0,0 +1,345 @@
# PhysicsFS; a portable, flexible file i/o abstraction.
#
# Please see the file LICENSE.txt in the source's root directory.
# The CMake project file is meant to get this compiling on all sorts of
# platforms quickly, and serve as the way Unix platforms and Linux distros
# package up official builds, but you don't _need_ to use this; we have
# built PhysicsFS to (hopefully) be able to drop into your project and
# compile, using preprocessor checks for platform-specific bits instead of
# testing in here.
set(PHYSFS_VERSION 3.2.0)
cmake_minimum_required(VERSION 3.0)
project(PhysicsFS VERSION ${PHYSFS_VERSION} LANGUAGES C )
include(GNUInstallDirs)
# Increment this if/when we break backwards compatibility.
set(PHYSFS_SOVERSION 1)
set(PHYSFS_M_SRCS)
set(PHYSFS_CPP_SRCS)
# I hate that they define "WIN32" ... we're about to move to Win64...I hope!
if(APPLE)
set(OTHER_LDFLAGS ${OTHER_LDFLAGS} "-framework IOKit -framework Foundation")
list(APPEND PHYSFS_M_SRCS src/physfs_platform_apple.m)
endif()
if(CMAKE_COMPILER_IS_GNUCC OR CMAKE_C_COMPILER_ID MATCHES "Clang")
add_compile_options(-Wall)
# Don't use -rpath.
set(CMAKE_SKIP_RPATH ON CACHE BOOL "Skip RPATH" FORCE)
endif()
if(CMAKE_C_COMPILER_ID STREQUAL "SunPro")
add_definitions(-erroff=E_EMPTY_TRANSLATION_UNIT)
add_definitions(-xldscope=hidden)
endif()
if(HAIKU)
# We add this explicitly, since we don't want CMake to think this
# is a C++ project unless we're on Haiku.
list(APPEND PHYSFS_CPP_SRCS src/physfs_platform_haiku.cpp)
find_library(BE_LIBRARY be)
find_library(ROOT_LIBRARY root)
list(APPEND OPTIONAL_LIBRARY_LIBS ${BE_LIBRARY} ${ROOT_LIBRARY})
endif()
if(CMAKE_SYSTEM_NAME STREQUAL "WindowsPhone" OR CMAKE_SYSTEM_NAME STREQUAL "WindowsStore")
set(WINRT TRUE)
endif()
if(WINRT)
list(APPEND PHYSFS_CPP_SRCS src/physfs_platform_winrt.cpp)
endif()
if(UNIX AND NOT WIN32 AND NOT APPLE) # (MingW and such might be UNIX _and_ WINDOWS!)
find_library(PTHREAD_LIBRARY pthread)
if(PTHREAD_LIBRARY)
set(OPTIONAL_LIBRARY_LIBS ${OPTIONAL_LIBRARY_LIBS} ${PTHREAD_LIBRARY})
endif()
endif()
if(PHYSFS_CPP_SRCS)
enable_language(CXX)
endif()
# Almost everything is "compiled" here, but things that don't apply to the
# build are #ifdef'd out. This is to make it easy to embed PhysicsFS into
# another project or bring up a new build system: just compile all the source
# code and #define the things you want.
set(PHYSFS_SRCS
src/physfs.c
src/physfs_byteorder.c
src/physfs_unicode.c
src/physfs_platform_posix.c
src/physfs_platform_unix.c
src/physfs_platform_windows.c
src/physfs_platform_os2.c
src/physfs_platform_qnx.c
src/physfs_platform_android.c
src/physfs_archiver_dir.c
src/physfs_archiver_unpacked.c
src/physfs_archiver_grp.c
src/physfs_archiver_hog.c
src/physfs_archiver_7z.c
src/physfs_archiver_mvl.c
src/physfs_archiver_qpak.c
src/physfs_archiver_wad.c
src/physfs_archiver_zip.c
src/physfs_archiver_slb.c
src/physfs_archiver_iso9660.c
src/physfs_archiver_vdf.c
${PHYSFS_CPP_SRCS}
${PHYSFS_M_SRCS}
)
# Archivers ...
# These are (mostly) on by default now, so these options are only useful for
# disabling them.
option(PHYSFS_ARCHIVE_ZIP "Enable ZIP support" TRUE)
if(NOT PHYSFS_ARCHIVE_ZIP)
add_definitions(-DPHYSFS_SUPPORTS_ZIP=0)
endif()
option(PHYSFS_ARCHIVE_7Z "Enable 7zip support" TRUE)
if(NOT PHYSFS_ARCHIVE_7Z)
add_definitions(-DPHYSFS_SUPPORTS_7Z=0)
endif()
option(PHYSFS_ARCHIVE_GRP "Enable Build Engine GRP support" TRUE)
if(NOT PHYSFS_ARCHIVE_GRP)
add_definitions(-DPHYSFS_SUPPORTS_GRP=0)
endif()
option(PHYSFS_ARCHIVE_WAD "Enable Doom WAD support" TRUE)
if(NOT PHYSFS_ARCHIVE_WAD)
add_definitions(-DPHYSFS_SUPPORTS_WAD=0)
endif()
option(PHYSFS_ARCHIVE_HOG "Enable Descent I/II HOG support" TRUE)
if(NOT PHYSFS_ARCHIVE_HOG)
add_definitions(-DPHYSFS_SUPPORTS_HOG=0)
endif()
option(PHYSFS_ARCHIVE_MVL "Enable Descent I/II MVL support" TRUE)
if(NOT PHYSFS_ARCHIVE_MVL)
add_definitions(-DPHYSFS_SUPPORTS_MVL=0)
endif()
option(PHYSFS_ARCHIVE_QPAK "Enable Quake I/II QPAK support" TRUE)
if(NOT PHYSFS_ARCHIVE_QPAK)
add_definitions(-DPHYSFS_SUPPORTS_QPAK=0)
endif()
option(PHYSFS_ARCHIVE_SLB "Enable I-War / Independence War SLB support" TRUE)
if(NOT PHYSFS_ARCHIVE_SLB)
add_definitions(-DPHYSFS_SUPPORTS_SLB=0)
endif()
option(PHYSFS_ARCHIVE_ISO9660 "Enable ISO9660 support" TRUE)
if(NOT PHYSFS_ARCHIVE_ISO9660)
add_definitions(-DPHYSFS_SUPPORTS_ISO9660=0)
endif()
option(PHYSFS_ARCHIVE_VDF "Enable Gothic I/II VDF archive support" TRUE)
if(NOT PHYSFS_ARCHIVE_VDF)
add_definitions(-DPHYSFS_SUPPORTS_VDF=0)
endif()
option(PHYSFS_BUILD_STATIC "Build static library" TRUE)
if(PHYSFS_BUILD_STATIC)
add_library(physfs-static STATIC ${PHYSFS_SRCS})
add_library(PhysFS::PhysFS-static ALIAS physfs-static)
set_target_properties(physfs-static PROPERTIES EXPORT_NAME PhysFS-static)
# Don't rename this on Windows, since DLLs will also produce an import
# library named "physfs.lib" which would conflict; Unix tend to like the
# same library name with a different extension for static libs, but
# Windows can just have a separate name.
if(NOT MSVC)
set_target_properties(physfs-static PROPERTIES OUTPUT_NAME "physfs")
endif()
if(WINRT)
# Ignore LNK4264 warnings; we don't author any WinRT components, just consume them, so we're okay in a static library.
set_target_properties(physfs-static PROPERTIES VS_WINRT_COMPONENT True)
set_target_properties(physfs-static PROPERTIES STATIC_LIBRARY_FLAGS "/ignore:4264")
endif()
if(WIN32 OR WINRT OR OS2)
# no dll exports from the static library
target_compile_definitions(physfs-static PRIVATE "PHYSFS_STATIC")
endif()
target_include_directories(physfs-static PUBLIC "$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/src>")
target_link_libraries(physfs-static PRIVATE ${OPTIONAL_LIBRARY_LIBS} ${OTHER_LDFLAGS})
set(PHYSFS_LIB_TARGET physfs-static)
list(APPEND PHYSFS_INSTALL_TARGETS "physfs-static")
endif()
option(PHYSFS_BUILD_SHARED "Build shared library" TRUE)
if(PHYSFS_BUILD_SHARED)
add_library(physfs SHARED ${PHYSFS_SRCS})
add_library(PhysFS::PhysFS ALIAS physfs)
set_target_properties(physfs PROPERTIES MACOSX_RPATH 1)
set_target_properties(physfs PROPERTIES VERSION ${PHYSFS_VERSION})
set_target_properties(physfs PROPERTIES SOVERSION ${PHYSFS_SOVERSION})
set_target_properties(physfs PROPERTIES EXPORT_NAME PhysFS)
if(WINRT)
set_target_properties(physfs PROPERTIES VS_WINRT_COMPONENT True)
endif()
if(OS2) # OS/2 does not support a DLL name longer than 8 characters.
set_target_properties(physfs PROPERTIES OUTPUT_NAME "physfs")
endif()
target_include_directories(physfs PUBLIC "$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/src>")
target_link_libraries(physfs PRIVATE ${OPTIONAL_LIBRARY_LIBS} ${OTHER_LDFLAGS})
set(PHYSFS_LIB_TARGET physfs)
list(APPEND PHYSFS_INSTALL_TARGETS "physfs")
endif()
if(NOT PHYSFS_BUILD_SHARED AND NOT PHYSFS_BUILD_STATIC)
message(FATAL "Both shared and static libraries are disabled!")
endif()
# CMake FAQ says I need this...
if(PHYSFS_BUILD_SHARED AND PHYSFS_BUILD_STATIC AND NOT WIN32)
set_target_properties(physfs PROPERTIES CLEAN_DIRECT_OUTPUT 1)
set_target_properties(physfs-static PROPERTIES CLEAN_DIRECT_OUTPUT 1)
endif()
option(PHYSFS_BUILD_TEST "Build stdio test program." TRUE)
mark_as_advanced(PHYSFS_BUILD_TEST)
if(PHYSFS_BUILD_TEST)
find_path(READLINE_H readline/readline.h)
find_path(HISTORY_H readline/history.h)
if(READLINE_H AND HISTORY_H)
find_library(CURSES_LIBRARY NAMES curses ncurses)
if(CURSES_LIBRARY)
set(CMAKE_REQUIRED_LIBRARIES ${CURSES_LIBRARY})
find_library(READLINE_LIBRARY readline)
if(READLINE_LIBRARY)
set(HAVE_SYSTEM_READLINE TRUE)
list(APPEND TEST_PHYSFS_LIBS ${READLINE_LIBRARY} ${CURSES_LIBRARY})
include_directories(SYSTEM ${READLINE_H} ${HISTORY_H})
add_definitions(-DPHYSFS_HAVE_READLINE=1)
endif()
endif()
endif()
add_executable(test_physfs test/test_physfs.c)
target_link_libraries(test_physfs PRIVATE ${PHYSFS_LIB_TARGET} ${TEST_PHYSFS_LIBS} ${OTHER_LDFLAGS})
list(APPEND PHYSFS_INSTALL_TARGETS test_physfs)
endif()
option(PHYSFS_DISABLE_INSTALL "Disable installing PhysFS" OFF)
if(NOT PHYSFS_DISABLE_INSTALL)
install(TARGETS ${PHYSFS_INSTALL_TARGETS} EXPORT PhysFSExport
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
INCLUDES DESTINATION ${CMAKE_INSTALL_INCLUDEDIR})
install(FILES src/physfs.h DESTINATION ${CMAKE_INSTALL_INCLUDEDIR})
install(EXPORT PhysFSExport
DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/PhysFS"
FILE PhysFSConfig.cmake
NAMESPACE PhysFS::
)
if(NOT MSVC)
configure_file(
"extras/physfs.pc.in"
"extras/physfs.pc"
@ONLY
)
install(
FILES "${CMAKE_CURRENT_BINARY_DIR}/extras/physfs.pc"
DESTINATION "${CMAKE_INSTALL_LIBDIR}/pkgconfig"
)
endif()
endif()
option(PHYSFS_BUILD_DOCS "Build doxygen based documentation" TRUE)
if(PHYSFS_BUILD_DOCS)
find_package(Doxygen)
if(DOXYGEN_FOUND)
set(PHYSFS_OUTPUT_DOXYFILE "${CMAKE_CURRENT_BINARY_DIR}/Doxyfile")
configure_file(
"${CMAKE_CURRENT_SOURCE_DIR}/docs/Doxyfile"
"${PHYSFS_OUTPUT_DOXYFILE}"
COPYONLY
)
file(APPEND "${PHYSFS_OUTPUT_DOXYFILE}" "\n\n# Below auto-generated by cmake...\n\n")
file(APPEND "${PHYSFS_OUTPUT_DOXYFILE}" "PROJECT_NUMBER = \"${PHYSFS_VERSION}\"\n")
file(APPEND "${PHYSFS_OUTPUT_DOXYFILE}" "OUTPUT_DIRECTORY = \"${CMAKE_CURRENT_BINARY_DIR}/docs\"\n")
file(APPEND "${PHYSFS_OUTPUT_DOXYFILE}" "\n# End auto-generated section.\n\n")
set(PHYSFS_TARGETNAME_DOCS "docs" CACHE STRING "Name of 'docs' build target")
add_custom_target(
${PHYSFS_TARGETNAME_DOCS}
${DOXYGEN_EXECUTABLE} "${PHYSFS_OUTPUT_DOXYFILE}"
WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}"
COMMENT "Building documentation in 'docs' directory..."
)
else()
message(STATUS "Doxygen not found. You won't be able to build documentation.")
endif()
endif()
if(UNIX)
set(PHYSFS_TARBALL "${CMAKE_CURRENT_SOURCE_DIR}/../physfs-${PHYSFS_VERSION}.tar.gz")
set(PHYSFS_TARGETNAME_DIST "dist" CACHE STRING "Name of 'dist' build target")
add_custom_target(
${PHYSFS_TARGETNAME_DIST}
git archive --prefix="physfs-${PHYSFS_VERSION}/" --output="${PHYSFS_TARBALL}" HEAD
WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}"
COMMENT "Building source tarball '${PHYSFS_TARBALL}'..."
)
set(PHYSFS_TARGETNAME_UNINSTALL "uninstall" CACHE STRING "Name of 'uninstall' build target")
add_custom_target(
${PHYSFS_TARGETNAME_UNINSTALL}
"${CMAKE_CURRENT_SOURCE_DIR}/extras/uninstall.sh"
WORKING_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}"
COMMENT "Uninstall the project..."
)
endif()
macro(message_bool_option _NAME _VALUE)
if(${_VALUE})
message(STATUS " ${_NAME}: enabled")
else()
message(STATUS " ${_NAME}: disabled")
endif()
endmacro()
message(STATUS "PhysicsFS will build with the following options:")
message_bool_option("ZIP support" PHYSFS_ARCHIVE_ZIP)
message_bool_option("7zip support" PHYSFS_ARCHIVE_7Z)
message_bool_option("GRP support" PHYSFS_ARCHIVE_GRP)
message_bool_option("WAD support" PHYSFS_ARCHIVE_WAD)
message_bool_option("HOG support" PHYSFS_ARCHIVE_HOG)
message_bool_option("MVL support" PHYSFS_ARCHIVE_MVL)
message_bool_option("QPAK support" PHYSFS_ARCHIVE_QPAK)
message_bool_option("SLB support" PHYSFS_ARCHIVE_SLB)
message_bool_option("VDF support" PHYSFS_ARCHIVE_VDF)
message_bool_option("ISO9660 support" PHYSFS_ARCHIVE_ISO9660)
message_bool_option("Build static library" PHYSFS_BUILD_STATIC)
message_bool_option("Build shared library" PHYSFS_BUILD_SHARED)
message_bool_option("Build stdio test program" PHYSFS_BUILD_TEST)
message_bool_option("Build Doxygen documentation" PHYSFS_BUILD_DOCS)
if(PHYSFS_BUILD_TEST)
message_bool_option(" Use readline in test program" HAVE_SYSTEM_READLINE)
endif()
# end of CMakeLists.txt ...

17
third-party/physfs/LICENSE.txt vendored Normal file
View File

@ -0,0 +1,17 @@
Copyright (c) 2001-2022 Ryan C. Gordon <icculus@icculus.org> and others.
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.

9
third-party/physfs/README.txt vendored Normal file
View File

@ -0,0 +1,9 @@
PhysicsFS; a portable, flexible file i/o abstraction.
https://icculus.org/physfs/
Please see the docs directory for documentation.
Please see LICENSE.txt for licensing information.

11
third-party/physfs/docs/CHANGELOG.txt vendored Normal file
View File

@ -0,0 +1,11 @@
The changelog is no longer maintained by hand. It made sense to have a single
timeline when we were using CVS, but modern revision control tools make this
redundant, at best.
If you want a list of changes, updated in real time, just point your web
browser here:
https://github.com/icculus/physfs/commits/

174
third-party/physfs/docs/CREDITS.txt vendored Normal file
View File

@ -0,0 +1,174 @@
Maintainer and general codemonkey:
Ryan C. Gordon
Tons of win32 help:
Adam Gates
More win32 hacking:
Gregory S. Read
Fixes for missing current working directories,
PHYSFS_setSaneConfig() improvements,
other bugfixes:
David Hedbor
Darwin support:
Patrick Stein
configure fixes,
RPM specfile:
Edward Rudd
GetLastModTime API,
other stuff:
John R. Hall
Various support, fixes and suggestions:
Alexander Pipelka
Russian translation,
QPAK archiver:
Ed Sinjiashvili
French translation:
Stéphane Peter
Debian package support:
Colin Bayer
"abs-file.h" in "extras" dir:
Adam D. Moss
WinCE port and other Win32 patches:
Corona688
German translation:
Michael Renner
Apple Project Builder support,
Mac OS X improvements:
Eric Wing
iPhone support:
Christian Gmeiner
WinRT support:
Martin Ahrnbom
HOG archiver,
MVL archiver:
Bradley Bell
MIX archiver:
Sebastian Steinhauer
Bug fixes:
Tolga Dalman
Initial PHYSFS_mount() work:
Philip D. Bober
Brazillian Portuguese translation:
Danny Angelo Carminati Grein
Spanish translation:
Pedro J. Pérez
MacOS Classic fixes,
MPW support,
bug fixes:
Chris Taylor
Mingw support,
General bug fixes:
Matze Braun
Haiku support:
scott mc
Bug fixes:
Jörg Walter
Bug fixes:
Olivier Boudeville
Bug fixes:
Henk Boom
Build system fixes:
Marc Kleine-Budde
Windows .rc file,
7zip/lzma archiver:
Dennis Schridde
OS/2 updates:
Dave Yeo
Bug fixes:
Patrice Mandin
PHYSFS_stat() API:
Christoph Nelles
Indy Sams
ISO9660 archiver:
Christoph Nelles
Bug fixes:
Steven Fuller
Bug fixes:
Tolga Dalman
Bug fixes:
Frank Becker
Bug fixes:
Norfanin
Bug fixes:
Evgeny Podjachev
Haiku fixes:
Chris Roberts
SLB archiver:
Aleksi Nurmi
Bug fixes:
Dmitry Marakasov
Bug fixes:
Andreas Karlsson
Bug fixes:
Michael Bacon
Bug fixes:
Xian Nox
Bug fixes:
Reto Schneider
pkg-config support:
Jonas Kulla
Bug fixes,
VDF archiver:
Francesco Bertolaccini
CMake fixes:
Tobias Markus
Bug fixes,
Rémi Verschelde
Bug fixes:
Rob Loach
Other stuff:
Your name here! Patches go to icculus@icculus.org ...
/* end of CREDITS.txt ... */

1081
third-party/physfs/docs/Doxyfile vendored Normal file

File diff suppressed because it is too large Load Diff

173
third-party/physfs/docs/INSTALL.txt vendored Normal file
View File

@ -0,0 +1,173 @@
The latest PhysicsFS information and releases can be found at:
https://icculus.org/physfs/
Building is (ahem) very easy.
ALL PLATFORMS:
Please read the text file LICENSE.txt in the root of the source tree.
The license is extremely liberal, even to closed-source, commercial
applications.
If you've got Doxygen (http://www.doxygen.org/) installed, you can run it
without any command line arguments in the root of the source tree to generate
the API reference (or build the "docs" target from your build system). This
is optional. You can browse the API docs online here:
https://icculus.org/physfs/docs/
BUILD IT WITH YOUR OWN PROGRAM:
If you don't care about formal packaging: just add everything in the "src"
directory to whatever you use to build your program and compile it along with
everything else, and you're done. It should compile with any reasonable
ANSI C compiler, should build cleanly even with excessive compiler warnings
enabled, needs no extra configuration, and allows static linking.
WinRT and Haiku need C++ compilers for their system APIs, but if you aren't on
these platforms and don't have a C++ compiler, don't build the .cpp files.
Likewise: Apple platforms (macOS, iOS, etc) need an Objective-C compiler, but
if you aren't on these platforms and don't have a Objective-C compiler, don't
build the .m file. Everything you need is in the .c sources.
If this all worked for your specific project, you can stop reading now.
Unix:
You will need CMake (https://www.cmake.org/) 2.4 or later installed.
Make a directory, wherever you like. This will be your build directory.
Chdir to your build directory. Run "cmake /where/i/unpacked/physfs" to
generate Makefiles. You can then run "ccmake ." and customize the build,
but the defaults are probably okay. You can have CMake generate KDevelop
or Ninja project files or whatever, if you prefer these.
Run "make". PhysicsFS will now build.
As root, run "make install".
If you get sick of the library, run "make uninstall" as root
and it will remove all traces of the library from the system paths.
Once you are satisfied, you can delete the build directory.
Primary Unix development is done with GNU/Linux, but PhysicsFS is known to
work out of the box with several flavors of Unix. It it doesn't work, patches
to get it running can be sent to icculus@icculus.org.
Windows:
If building with Cygwin, mingw32, MSYS, or something else that uses the GNU
toolchain, follow the Unix instructions, above.
If you want to use Visual Studio, nmake, or the Platform SDK, you will need
CMake (https://www.cmake.org/) 2.4 or later installed. Point CMake at the
CMakeLists.txt file in the root of the source directory and hit the
"Configure" button. After telling it what type of compiler you are targeting
(Borland, Visual Studio, etc), CMake will process for while and then give you
a list of options you can change (what archivers you want to support, etc).
If you aren't sure, the defaults are probably fine. Hit the "Configure"
button again, then "OK" once configuration has completed with options that
match your liking. Now project files for your favorite programming
environment will be generated for you in the directory you specified.
Go there and use them to build PhysicsFS.
PhysicsFS will only link directly against system libraries that have existed
since Windows NT 3.51. If there's a newer API we want to use, we try to
dynamically load it at runtime and fallback to a reasonable behaviour when
we can't find it. Note that Windows 98 and later _should_
work if you use the Microsoft Layer for Unicode (UNICOWS.DLL) to provide
some missing system APIs, but this is no longer tested as of PhysicsFS 2.1.0.
PhysicsFS 2.0.x is known to work with Windows 95 without UNICOWS.DLL.
PhysicsFS works on 32-bit and 64-bit Windows. There is no 16-bit Windows
support at all. Windows RT is covered below.
Windows RT:
Windows RT (Windows Phone, Windows Store, UWP) 8.0 and later are supported.
Make sure you include both physfs_platform_windows.c _and_
physfs_platform_winrt.cpp in your build, and that the C++ file has
"Consume Windows Runtime Extension" set to "Yes" in its Visual Studio
properties (from the command line, you want to compile this file with the
"/ZW" compiler switch). CMake can, in theory, generate a project file for
WinRT if you pick a recent Visual Studio target, choose manual cross-compile
options, and set the system name to "WindowsPhone" or "WindowsStore" and the
correct OS version (8.0 or later).
PocketPC/WindowsCE:
Support for PocketPC was removed in PhysicsFS 2.1.0. This was known to work
in the 1.0 releases, but wasn't tested in 2.0 and later. PhysicsFS should
work on modern Windows Phones (see "Windows RT" section).
macOS:
You will need CMake (https://www.cmake.org/) 2.4 or later installed.
You can either generate a Unix makefile with CMake, or generate an Xcode
project, whichever makes you more comfortable.
PowerPC and Intel Macs should both be supported.
MAC OS 8/9 ("Mac OS Classic"):
Classic Mac OS support has been dropped in PhysicsFS 2.0. Apple hasn't updated
pre-OSX versions in more than a decade at this point, none of the hardware
they've shipped will boot it for almost as many years, and finding
developer tools for it is becoming almost impossible. As the switch to Intel
hardware has removed the "Classic" emulation environment, it was time to
remove support from PhysicsFS. That being said, the PhysicsFS 1.0 branch can
still target back to Mac OS 8.5, so you can use that if you need support for
this legacy OS. We still very much support modern macOS, though: see above.
Emscripten:
Use the "Unix" instructions, above. You can install the Emscripten SDK and use
the extras/buildbot-emscripten.sh script to automate this for you.
BeOS, Zeta, YellowTab:
BeOS support was dropped in PhysicsFS 2.1.0. Consider installing Haiku, which
we still support.
Haiku:
Use the "Unix" instructions, above.
OS/2:
OS/2 is known to work with OpenWatcom and GCC-based compilers. I couldn't get
an OS/2 port of CMake to generate OpenWatcom project files (although it should
be able to do that in theory), it should be able to do Unix Makefiles with
GCC. It might be easier to just compile PhysicsFS along with the rest of
your project on this platform.
OTHER PLATFORMS:
Many Unix-like platforms might "just work" with CMake. Some of these platforms
are known to have worked at one time, but have not been heavily tested, if
tested at all. PhysicsFS is, as far as we know, 64-bit and byteorder clean,
and is known to compile on several compilers across many platforms. To
implement a new platform or archiver, please read the heavily-commented
physfs_internal.h and look at the physfs_platform_* and physfs_archiver_*
source files for examples.
--ryan. (icculus@icculus.org)

View File

@ -0,0 +1,18 @@
The API documentation is readable in a few ways:
- Read physfs.h; it's _heavily_ documented and the primary source of reference
documentation for the library.
- Run Doxygen over the header, which produces nicer-to-browse documentation in
HTML, LaTeX, manpage, etc formats. This is done for you if Doxygen is
installed and you build the "docs" target in whatever project files CMake
generated for you.
- Too much trouble? We generated the HTML reference for you, online here:
https://icculus.org/physfs/docs/
- We would love well-written tutorials for the latest version of PhysicsFS!
If you write one, we would love to list it here. Drop me a line about it:
icculus@icculus.org ... Thanks!
--ryan.

62
third-party/physfs/docs/TODO.txt vendored Normal file
View File

@ -0,0 +1,62 @@
Stuff that needs to be done and wishlist:
These are in no particular order.
Some might be dupes, some might be done already, some might be bad ideas.
From https://icculus.org/pipermail/physfs/2009-March/000698.html ...
- Write support for various archives. I haven't decided how to do this yet,
but I'd like to.
- Add an API to expose a file's extended attributes to the application?
- Deprecate PHYSFS_setSaneConfig(). It really should have been in the extras
directory.
- Clean up the sources to match my ever-changing coding style. :)
From https://icculus.org/pipermail/physfs/2010-January/000826.html ...
- Lua bindings
From https://icculus.org/pipermail/physfs/2010-January/000833.html ...
- SWIG bindings
From old TODO.txt...
- Other archivers: perhaps tar(.gz|.bz2), RPM, ARJ, etc. These are less
important, since streaming archives aren't of much value to games (which
is why zipfiles are king: random access), but it could have uses for, say,
an installer/updater.
- Do symlinks in zip archiver work when they point to dirs?
- Enable more warnings?
- Use __cdecl in physfs.h?
- Look for FIXMEs (many marked with "!!!" in comments).
- fscanf and fprintf support in extras dir.
- Sanity check byte order at runtime.
- Memory locking?
- General code audit.
- Multiple write dirs with mount points?
Other stuff I thought of...
- moar asserts!
- constify!
- Does iPhone work?
- Fix CMake vs Doxygen.
- Doxygen replacement? (manpages suck.)
- Fix coding standards to match.
- See if we can ditch some #include lines...
- LZMA support in zip archiver?
- bzip2 support in zip archiver?
- Reduce the BAIL and GOTO macro use. A lot of these don't add anything.
- Change the term "search path" to something less confusing.
Probably other stuff. Requests and recommendations are welcome.
// end of TODO.txt ...

View File

@ -0,0 +1,11 @@
There used to be C# bindings in this directory, but they have been
unmaintained for many years.
Instead, there is a more modern PhysicsFS wrapper for .NET available.
You can find it at https://github.com/frabert/SharpPhysFS
Thanks to Francesco Bertolaccini for his efforts on that project!
--ryan.

165
third-party/physfs/extras/abs-file.h vendored Normal file
View File

@ -0,0 +1,165 @@
/*
* stdio/physfs abstraction layer 2003-04-02
*
* Adam D. Moss <adam@gimp.org> <aspirin@icculus.org>
*
* These wrapper macros and functions are designed to allow a program
* to perform file I/O with identical semantics and syntax regardless
* of whether PhysicsFS is being used or not.
*/
#ifndef _ABS_FILE_H
#define _ABS_FILE_H
/*
PLEASE NOTE: This license applies to abs-file.h ONLY (to make it clear that
you may embed this wrapper code within commercial software); PhysicsFS itself
is (at the time of writing) released under a different license with
additional restrictions.
Copyright (C) 2002-2003 Adam D. Moss (the "Author"). All Rights Reserved.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is fur-
nished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FIT-
NESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHOR BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CON-
NECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
Except as contained in this notice, the name of the Author of the
Software shall not be used in advertising or otherwise to promote the sale,
use or other dealings in this Software without prior written authorization
from the Author.
*/
#include <stdlib.h>
#include <stdio.h>
/*
* API:
*
* Macro/function use like stdio equivalent...
* -------------- ----------------------------
* MY_FILETYPE FILE
* MY_OPEN_FOR_READ fopen(..., "rb")
* MY_READ fread(...)
* MY_CLOSE fclose(...)
* MY_GETC fgetc(...)
* MY_GETS fgets(...)
* MY_ATEOF feof(...)
* MY_TELL ftell(...)
* MY_SEEK fseek(..., SEEK_SET)
* MY_REWIND rewind(...)
* MY_SETBUFFER (not a standard for stdio, does nothing there)
*/
/*
* Important DEFINEs:
* It is important to define these consistantly across the various
* compilation modules of your program if you wish to exchange file
* handles between them.
*
* USE_PHYSFS: Define USE_PHYSFS if PhysicsFS is being used; note that if
* you do intend to use PhysicsFS then you will still need to initialize
* PhysicsFS yourself and set up its search-paths.
*
* Optional DEFINEs:
*
* PHYSFS_DEFAULT_READ_BUFFER <bytes>: If set then abs-file.h sets the
* PhysicsFS buffer size to this value whenever you open a file. You
* may over-ride this on a per-filehandle basis by using the
* MY_SETBUFFER() macro (which simply does nothing when not using
* PhysicsFS). If you have not defined this value explicitly then
* abs-file.h will default to the same default buffer size as used by
* stdio if it can be determined, or 8192 bytes otherwise.
*/
#ifndef PHYSFS_DEFAULT_READ_BUFFER
#ifdef BUFSIZ
#define PHYSFS_DEFAULT_READ_BUFFER BUFSIZ
#else
#define PHYSFS_DEFAULT_READ_BUFFER 8192
#endif
#endif
#ifdef USE_PHYSFS
#include <physfs.h>
#define MY_FILETYPE PHYSFS_File
#define MY_SETBUFFER(fp,size) PHYSFS_setBuffer(fp,size)
#define MY_READ(p,s,n,fp) PHYSFS_read(fp,p,s,n)
#if PHYSFS_DEFAULT_READ_BUFFER
static MY_FILETYPE* MY_OPEN_FOR_READ(const char *const filename)
{
MY_FILETYPE *const file = PHYSFS_openRead(filename);
if (file) {
MY_SETBUFFER(file, PHYSFS_DEFAULT_READ_BUFFER);
}
return file;
}
#else
#define MY_OPEN_FOR_READ(fn) PHYSFS_openRead(fn)
#endif
static int MY_GETC(MY_FILETYPE *const fp) {
unsigned char c;
/*if (PHYSFS_eof(fp)) {
return EOF;
}
MY_READ(&c, 1, 1, fp);*/
if (MY_READ(&c, 1, 1, fp) != 1) {
return EOF;
}
return c;
}
static char * MY_GETS(char * const str, const int size,
MY_FILETYPE *const fp) {
int i = 0;
int c;
do {
if (i == size-1) {
break;
}
c = MY_GETC(fp);
if (c == EOF) {
break;
}
str[i++] = c;
} while (c != '\0' &&
c != -1 &&
c != '\n');
str[i] = '\0';
if (i == 0) {
return NULL;
}
return str;
}
#define MY_CLOSE(fp) PHYSFS_close(fp)
#define MY_ATEOF(fp) PHYSFS_eof(fp)
#define MY_TELL(fp) PHYSFS_tell(fp)
#define MY_SEEK(fp,o) PHYSFS_seek(fp,o)
#define MY_REWIND(fp) MY_SEEK(fp,0)
#else
#define MY_FILETYPE FILE
#define MY_READ(p,s,n,fp) fread(p,s,n,fp)
#define MY_OPEN_FOR_READ(n) fopen(n, "rb")
#define MY_GETC(fp) fgetc(fp)
#define MY_GETS(str,size,fp) fgets(str,size,fp)
#define MY_CLOSE(fp) fclose(fp)
#define MY_ATEOF(fp) feof(fp)
#define MY_TELL(fp) ftell(fp)
#define MY_SEEK(fp,o) fseek(fp,o, SEEK_SET)
#define MY_REWIND(fp) rewind(fp)
/*static void MY_SETBUFFER(const MY_FILETYPE *const file, const int num) { }*/
#define MY_SETBUFFER(fp,size)
#endif
#endif

View File

@ -0,0 +1,59 @@
#!/bin/bash
# This is a script used by some Buildbot workers to push the project
# through Clang's static analyzer and prepare the output to be uploaded
# back to the buildmaster. You might find it useful too.
# Install Clang (you already have it on Mac OS X, apt-get install clang
# on Ubuntu, etc), Make sure "scan-build" is in your $PATH.
FINALDIR="$1"
set -x
set -e
cd `dirname "$0"`
cd ..
rm -rf checker-buildbot analysis
if [ ! -z "$FINALDIR" ]; then
rm -rf "$FINALDIR"
fi
mkdir checker-buildbot
cd checker-buildbot
# We turn off deprecated declarations, because we don't care about these warnings during static analysis.
# The -Wno-liblto is new since our checker-279 upgrade, I think; checker otherwise warns "libLTO.dylib relative to clang installed dir not found"
# You might want to do this for CMake-backed builds instead...
scan-build -o analysis cmake -G Ninja -Wno-dev -DPHYSFS_BUILD_SHARED=False -DCMAKE_BUILD_TYPE=Debug -DCMAKE_C_FLAGS="-Wno-deprecated-declarations" -DCMAKE_EXE_LINKER_FLAGS="-Wno-liblto" ..
rm -rf analysis
scan-build -o analysis cmake --build . --config Debug
if [ `ls -A analysis |wc -l` == 0 ] ; then
mkdir analysis/zarro
echo '<html><head><title>Zarro boogs</title></head><body>Static analysis: no issues to report.</body></html>' >analysis/zarro/index.html
fi
mv analysis/* ../analysis
rmdir analysis # Make sure this is empty.
cd ..
chmod -R a+r analysis
chmod -R go-w analysis
find analysis -type d -exec chmod a+x {} \;
if [ -x /usr/bin/xattr ]; then find analysis -exec /usr/bin/xattr -d com.apple.quarantine {} \; 2>/dev/null ; fi
if [ ! -z "$FINALDIR" ]; then
mv analysis "$FINALDIR"
else
FINALDIR=analysis
fi
rm -rf checker-buildbot
echo "Done. Final output is in '$FINALDIR' ..."
# end of checker-buildbot.sh ...

View File

@ -0,0 +1,52 @@
#!/bin/bash
if [ -z "$SDKDIR" ]; then
SDKDIR="/emsdk"
fi
ENVSCRIPT="$SDKDIR/emsdk_env.sh"
if [ ! -f "$ENVSCRIPT" ]; then
echo "ERROR: This script expects the Emscripten SDK to be in '$SDKDIR'." 1>&2
echo "ERROR: Set the \$SDKDIR environment variable to override this." 1>&2
exit 1
fi
TARBALL="$1"
if [ -z $1 ]; then
TARBALL=physfs-emscripten.tar.xz
fi
cd `dirname "$0"`
cd ..
PHYSFSBASE=`pwd`
echo "Setting up Emscripten SDK environment..."
source "$ENVSCRIPT"
echo "Setting up..."
cd "$PHYSFSBASE"
rm -rf buildbot
mkdir buildbot
cd buildbot
echo "Configuring..."
emcmake cmake -G "Ninja" -DPHYSFS_BUILD_SHARED=False -DCMAKE_BUILD_TYPE=MinSizeRel .. || exit $?
echo "Building..."
emmake cmake --build . --config MinSizeRel || exit $?
set -e
rm -rf "$TARBALL" physfs-emscripten
mkdir -p physfs-emscripten
echo "Archiving to '$TARBALL' ..."
cp ../src/physfs.h libphysfs.a physfs-emscripten
chmod -R a+r physfs-emscripten
chmod a+x physfs-emscripten
chmod -R go-w physfs-emscripten
tar -cJvvf "$TARBALL" physfs-emscripten
echo "Done."
exit 0
# end of emscripten-buildbot.sh ...

View File

@ -0,0 +1,87 @@
#!/bin/bash
# This is used by the buildbot to cross-compile for OS/2 from Linux, using
# OpenWatcom. In an ideal world, we wouldn't need this, but we need a few
# things to do this "properly" ...
#
# - OS/2 running as a VMware guest on the build machine
# - Buildbot-worker running on that OS/2 guest
# - CMake for OS/2 that...
# - ... has Open Watcom compiler support and...
# - ... a Watcom WMake project generator.
#
# Some of these things are doable (there is a CMake port for OS/2, you can
# use GCC with it), but since OpenWatcom will just target OS/2 on Linux just
# like it could on OS/2, it's easier and more efficient to just have a
# buildbot script compile it here.
#
# Note that we just blast all the C files through the wcc386 compiler and
# skip CMake entirely. We should fix this at some point.
set -e
ZIPFILE="$1"
if [ -z $ZIPFILE ]; then
ZIPFILE=physfs-os2.zip
fi
export WATCOM="/usr/local/share/watcom"
export PATH="$PATH:$WATCOM/binl"
CFLAGS="-i=\"$WATCOM/h;$WATCOM/h/os2;../src\" -wx -d0 -otexan -6r -zq -bt=os2 -fo=.obj -mf"
WLIBFLAGS="-b -c -n -q -p=512"
cd `dirname "$0"`
cd ..
mkdir -p buildbot
cd buildbot
rm -f test_physfs.obj
OKAY="1"
for src in ../src/*.c ; do
echo wcc386 $src $CFLAGS
wcc386 $src $CFLAGS || OKAY="0"
done
if [ "$OKAY" == "1" ]; then
echo wlib $WLIBFLAGS physfs.lib *.obj
wlib $WLIBFLAGS physfs.lib *.obj || OKAY="0"
fi
echo wcc386 ../test/test_physfs.c $CFLAGS
wcc386 ../test/test_physfs.c $CFLAGS || OKAY="0"
if [ "$OKAY" == "1" ]; then
LDFLAGS="name test_physfs d all sys os2v2 op m libr physfs op q op symf FIL test_physfs.obj"
echo wlink $LDFLAGS
wlink $LDFLAGS || OKAY="0"
fi
if [ "$OKAY" == "1" ]; then
F=`file test_physfs.exe`
echo "$F"
if [ "$F" != 'test_physfs.exe: MS-DOS executable, LX for OS/2 (console) i80386' ]; then
echo 1>&2 "ERROR: final binary doesn't appear to be OS/2 executable."
OKAY=0
fi
fi
if [ "$OKAY" == "1" ]; then
echo 1>&2 "Build succeeded."
set -e
rm -rf "$ZIPFILE" physfs-os2
mkdir -p physfs-os2
echo "Zipping to '$ZIPFILE' ..."
cp ../src/physfs.h physfs.lib physfs-os2
chmod -R a+r physfs-os2
chmod a+x physfs-os2
chmod -R go-w physfs-os2
zip -9r "$ZIPFILE" physfs-os2
echo "Done."
exit 0
else
echo 1>&2 "Build failed."
exit 1
fi

View File

@ -0,0 +1,56 @@
#!/bin/bash
# This is the script physfs-buildbot.icculus.org uses to cross-compile
# PhysicsFS from x86 Linux to Raspberry Pi. This script was originally from
# Simple Directmedia Layer ( https://www.libsdl.org/ ).
# The final tarball can be unpacked in the root directory of a RPi,
# so the PhysicsFS install lands in /usr/local. Run ldconfig, and then
# you should be able to build and run PhysicsFS-based software on your
# Pi. Standard configure scripts should be able to find PhysicsFS and
# build against it.
TARBALL="$1"
if [ -z $1 ]; then
TARBALL=physfs-raspberrypi.tar.xz
fi
BUILDBOTDIR="buildbot"
PARENTDIR="$PWD"
set -e
set -x
rm -f $TARBALL
rm -rf $BUILDBOTDIR
mkdir -p $BUILDBOTDIR
pushd $BUILDBOTDIR
# the '-G "Ninja"' can be '-G "Unix Makefiles"' if you prefer to use GNU Make.
SYSROOT="/opt/rpi-sysroot"
cmake -G "Ninja" \
-DCMAKE_C_COMPILER="/opt/rpi-tools/arm-bcm2708/gcc-linaro-arm-linux-gnueabihf-raspbian/bin/arm-linux-gnueabihf-gcc" \
-DCMAKE_BUILD_TYPE=MinSizeRel \
-DCMAKE_SYSROOT="$SYSROOT" \
-DCMAKE_FIND_ROOT_PATH="$SYSROOT" \
-DCMAKE_SYSTEM_NAME="Linux" \
-DCMAKE_SYSTEM_VERSION=1 \
-DCMAKE_FIND_ROOT_PATH_MODE_PROGRAM=NEVER \
-DCMAKE_FIND_ROOT_PATH_MODE_LIBRARY=ONLY \
-DCMAKE_FIND_ROOT_PATH_MODE_INCLUDE=ONLY \
..
cmake --build . --config MinSizeRel
rm -rf "$TARBALL" physfs-raspberrypi
mkdir -p physfs-raspberrypi
echo "Archiving to '$TARBALL' ..."
cp -a ../src/physfs.h libphysfs.a libphysfs.so* physfs-raspberrypi
chmod -R a+r physfs-raspberrypi
chmod a+x physfs-raspberrypi physfs-raspberrypi/*.so*
chmod -R go-w physfs-raspberrypi
tar -cJvvf "$TARBALL" physfs-raspberrypi
set +x
echo "Done."

1414
third-party/physfs/extras/casefolding.txt vendored Normal file

File diff suppressed because it is too large Load Diff

239
third-party/physfs/extras/globbing.c vendored Normal file
View File

@ -0,0 +1,239 @@
/** \file globbing.c */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <assert.h>
#include "globbing.h"
/**
* Please see globbing.h for details.
*
* License: this code is public domain. I make no warranty that it is useful,
* correct, harmless, or environmentally safe.
*
* This particular file may be used however you like, including copying it
* verbatim into a closed-source project, exploiting it commercially, and
* removing any trace of my name from the source (although I hope you won't
* do that). I welcome enhancements and corrections to this file, but I do
* not require you to send me patches if you make changes. This code has
* NO WARRANTY.
*
* Unless otherwise stated, the rest of PhysicsFS falls under the zlib license.
* Please see the file LICENSE.txt in the source's root directory.
*
* \author Ryan C. Gordon.
*/
static int matchesPattern(const char *fname, const char *wildcard,
int caseSensitive)
{
char x, y;
const char *fnameptr = fname;
const char *wildptr = wildcard;
while ((*wildptr) && (*fnameptr))
{
y = *wildptr;
if (y == '*')
{
do
{
wildptr++; /* skip multiple '*' in a row... */
} while (*wildptr == '*');
y = (caseSensitive) ? *wildptr : (char) tolower(*wildptr);
while (1)
{
x = (caseSensitive) ? *fnameptr : (char) tolower(*fnameptr);
if ((!x) || (x == y))
break;
else
fnameptr++;
} /* while */
} /* if */
else if (y == '?')
{
wildptr++;
fnameptr++;
} /* else if */
else
{
if (caseSensitive)
x = *fnameptr;
else
{
x = tolower(*fnameptr);
y = tolower(y);
} /* if */
wildptr++;
fnameptr++;
if (x != y)
return 0;
} /* else */
} /* while */
while (*wildptr == '*')
wildptr++;
return (*fnameptr == *wildptr);
} /* matchesPattern */
typedef struct
{
const PHYSFS_Allocator *allocator;
const char *wildcard;
int caseSensitive;
PHYSFS_EnumFilesCallback callback;
void *origData;
} WildcardCallbackData;
/*
* This callback sits between the enumerator and the enduser callback,
* filtering out files that don't match the wildcard pattern.
*/
static void wildcardCallback(void *_d, const char *origdir, const char *fname)
{
const WildcardCallbackData *data = (const WildcardCallbackData *) _d;
if (matchesPattern(fname, data->wildcard, data->caseSensitive))
data->callback(data->origData, origdir, fname);
} /* wildcardCallback */
void PHYSFSEXT_enumerateFilesCallbackWildcard(const char *dir,
const char *wildcard,
int caseSensitive,
PHYSFS_EnumFilesCallback c,
void *d)
{
WildcardCallbackData data;
data.allocator = PHYSFS_getAllocator();
data.wildcard = wildcard;
data.caseSensitive = caseSensitive;
data.callback = c;
data.origData = d;
PHYSFS_enumerateFilesCallback(dir, wildcardCallback, &data);
} /* PHYSFSEXT_enumerateFilesCallbackWildcard */
void PHYSFSEXT_freeEnumeration(char **list)
{
const PHYSFS_Allocator *allocator = PHYSFS_getAllocator();
int i;
if (list != NULL)
{
for (i = 0; list[i] != NULL; i++)
allocator->Free(list[i]);
allocator->Free(list);
} /* if */
} /* PHYSFSEXT_freeEnumeration */
char **PHYSFSEXT_enumerateFilesWildcard(const char *dir, const char *wildcard,
int caseSensitive)
{
const PHYSFS_Allocator *allocator = PHYSFS_getAllocator();
char **list = PHYSFS_enumerateFiles(dir);
char **retval = NULL;
int totalmatches = 0;
int matches = 0;
char **i;
for (i = list; *i != NULL; i++)
{
#if 0
printf("matchesPattern: '%s' vs '%s' (%s) ... %s\n", *i, wildcard,
caseSensitive ? "case" : "nocase",
matchesPattern(*i, wildcard, caseSensitive) ? "true" : "false");
#endif
if (matchesPattern(*i, wildcard, caseSensitive))
totalmatches++;
} /* for */
retval = (char **) allocator->Malloc(sizeof (char *) * (totalmatches+1));
if (retval != NULL)
{
for (i = list; ((matches < totalmatches) && (*i != NULL)); i++)
{
if (matchesPattern(*i, wildcard, caseSensitive))
{
retval[matches] = (char *) allocator->Malloc(strlen(*i) + 1);
if (retval[matches] == NULL)
{
while (matches--)
allocator->Free(retval[matches]);
allocator->Free(retval);
retval = NULL;
break;
} /* if */
strcpy(retval[matches], *i);
matches++;
} /* if */
} /* for */
if (retval != NULL)
{
assert(totalmatches == matches);
retval[matches] = NULL;
} /* if */
} /* if */
PHYSFS_freeList(list);
return retval;
} /* PHYSFSEXT_enumerateFilesWildcard */
#ifdef TEST_PHYSFSEXT_ENUMERATEFILESWILDCARD
int main(int argc, char **argv)
{
int rc;
char **flist;
char **i;
if (argc != 3)
{
printf("USAGE: %s <pattern> <caseSen>\n"
" where <caseSen> is 1 or 0.\n", argv[0]);
return 1;
} /* if */
if (!PHYSFS_init(argv[0]))
{
fprintf(stderr, "PHYSFS_init(): %s\n", PHYSFS_getErrorByCode(PHYSFS_getLastErrorCode()));
return 1;
} /* if */
if (!PHYSFS_addToSearchPath(".", 1))
{
fprintf(stderr, "PHYSFS_addToSearchPath(): %s\n", PHYSFS_getErrorByCode(PHYSFS_getLastErrorCode()));
PHYSFS_deinit();
return 1;
} /* if */
flist = PHYSFSEXT_enumerateFilesWildcard("/", argv[1], atoi(argv[2]));
rc = 0;
for (i = flist; *i; i++)
{
printf("%s\n", *i);
rc++;
} /* for */
printf("\n total %d files.\n\n", rc);
PHYSFSEXT_freeEnumeration(flist);
PHYSFS_deinit();
return 0;
} /* main */
#endif
/* end of globbing.c ... */

165
third-party/physfs/extras/globbing.h vendored Normal file
View File

@ -0,0 +1,165 @@
#ifndef INCL_PHYSFSEXT_GLOBBING_H
#define INCL_PHYSFSEXT_GLOBBING_H
/** \file globbing.h */
#include "physfs.h"
/**
* \mainpage PhysicsFS globbing
*
* This is an extension to PhysicsFS to let you search for files with basic
* wildcard matching, regardless of what sort of filesystem or archive they
* reside in. It does this by enumerating directories as needed and manually
* locating matching entries.
*
* Usage: Set up PhysicsFS as you normally would, then use
* PHYSFSEXT_enumerateFilesWildcard() when enumerating files. This is just
* like PHYSFS_enumerateFiles(), but it returns a subset that matches your
* wildcard pattern. You must call PHYSFSEXT_freeEnumeration() on the results,
* just PHYSFS_enumerateFiles() would do with PHYSFS_freeList().
*
* License: this code is public domain. I make no warranty that it is useful,
* correct, harmless, or environmentally safe.
*
* This particular file may be used however you like, including copying it
* verbatim into a closed-source project, exploiting it commercially, and
* removing any trace of my name from the source (although I hope you won't
* do that). I welcome enhancements and corrections to this file, but I do
* not require you to send me patches if you make changes. This code has
* NO WARRANTY.
*
* Unless otherwise stated, the rest of PhysicsFS falls under the zlib license.
* Please see LICENSE.txt in the source's "docs" directory.
*
* \author Ryan C. Gordon.
*/
#ifdef __cplusplus
extern "C" {
#endif
/**
* \fn char **PHYSFS_enumerateFilesWildcard(const char *dir, const char *wildcard, int caseSensitive)
* \brief Get a file listing of a search path's directory, filtered with a wildcard pattern.
*
* Matching directories are interpolated. That is, if "C:\mydir" is in the
* search path and contains a directory "savegames" that contains "x.sav",
* "y.Sav", and "z.txt", and there is also a "C:\userdir" in the search path
* that has a "savegames" subdirectory with "w.sav", then the following code:
*
* \code
* char **rc = PHYSFS_enumerateFilesWildcard("savegames", "*.sav", 0);
* char **i;
*
* for (i = rc; *i != NULL; i++)
* printf(" * We've got [%s].\n", *i);
*
* PHYSFS_freeList(rc);
* \endcode
*
* ...will print:
*
* \verbatim
* We've got [x.sav].
* We've got [y.Sav].
* We've got [w.sav].\endverbatim
*
* Feel free to sort the list however you like. We only promise there will
* be no duplicates, but not what order the final list will come back in.
*
* Wildcard strings can use the '*' and '?' characters, currently.
* Matches can be case-insensitive if you pass a zero for argument 3.
*
* Don't forget to call PHYSFSEXT_freeEnumerator() with the return value from
* this function when you are done with it. As we use PhysicsFS's allocator
* for this list, you must free it before calling PHYSFS_deinit().
* Do not use PHYSFS_freeList() on the returned value!
*
* \param dir directory in platform-independent notation to enumerate.
* \param wildcard Wildcard pattern to use for filtering.
* \param caseSensitive Zero for case-insensitive matching,
* non-zero for case-sensitive.
* \return Null-terminated array of null-terminated strings.
*
* \sa PHYSFSEXT_freeEnumeration
*/
PHYSFS_DECL char **PHYSFSEXT_enumerateFilesWildcard(const char *dir,
const char *wildcard,
int caseSensitive);
/**
* \fn void PHYSFSEXT_freeEnumeration(char **list)
* \brief Free data returned by PHYSFSEXT_enumerateFilesWildcard
*
* Conceptually, this works like PHYSFS_freeList(), but is used with data
* returned by PHYSFSEXT_enumerateFilesWildcard() only. Be sure to call this
* on any returned data from that function before
*
* \param list Pointer previously returned by
* PHYSFSEXT_enumerateFilesWildcard(). It is safe to pass a
* NULL here.
*
* \sa PHYSFSEXT_enumerateFilesWildcard
*/
PHYSFS_DECL void PHYSFSEXT_freeEnumeration(char **list);
/**
* \fn void PHYSFSEXT_enumerateFilesCallbackWildcard(const char *dir, const char *wildcard, int caseSensitive, PHYSFS_EnumFilesCallback c, void *d);
* \brief Get a file listing of a search path's directory, filtered with a wildcard pattern, using an application-defined callback.
*
* This function is equivalent to PHYSFSEXT_enumerateFilesWildcard(). It
* reports file listings, filtered by a wildcard pattern.
*
* Unlike PHYSFS_enumerateFiles(), this function does not return an array.
* Rather, it calls a function specified by the application once per
* element of the search path:
*
* \code
*
* static void printDir(void *data, const char *origdir, const char *fname)
* {
* printf(" * We've got [%s] in [%s].\n", fname, origdir);
* }
*
* // ...
* PHYSFS_enumerateFilesCallbackWildcard("savegames","*.sav",0,printDir,NULL);
* \endcode
*
* Items sent to the callback are not guaranteed to be in any order whatsoever.
* There is no sorting done at this level, and if you need that, you should
* probably use PHYSFS_enumerateFilesWildcard() instead, which guarantees
* alphabetical sorting. This form reports whatever is discovered in each
* archive before moving on to the next. Even within one archive, we can't
* guarantee what order it will discover data. <em>Any sorting you find in
* these callbacks is just pure luck. Do not rely on it.</em> As this walks
* the entire list of archives, you may receive duplicate filenames.
*
* Wildcard strings can use the '*' and '?' characters, currently.
* Matches can be case-insensitive if you pass a zero for argument 3.
*
* \param dir Directory, in platform-independent notation, to enumerate.
* \param wildcard Wildcard pattern to use for filtering.
* \param caseSensitive Zero for case-insensitive matching,
* non-zero for case-sensitive.
* \param c Callback function to notify about search path elements.
* \param d Application-defined data passed to callback. Can be NULL.
*
* \sa PHYSFS_EnumFilesCallback
* \sa PHYSFS_enumerateFiles
*/
PHYSFS_DECL void PHYSFSEXT_enumerateFilesCallbackWildcard(const char *dir,
const char *wildcard,
int caseSensitive,
PHYSFS_EnumFilesCallback c,
void *d);
#ifdef __cplusplus
}
#endif
#endif /* include-once blocker. */
/* end of globbing.h ... */

203
third-party/physfs/extras/ignorecase.c vendored Normal file
View File

@ -0,0 +1,203 @@
/** \file ignorecase.c */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include "physfs.h"
#include "ignorecase.h"
/**
* Please see ignorecase.h for details.
*
* License: this code is public domain. I make no warranty that it is useful,
* correct, harmless, or environmentally safe.
*
* This particular file may be used however you like, including copying it
* verbatim into a closed-source project, exploiting it commercially, and
* removing any trace of my name from the source (although I hope you won't
* do that). I welcome enhancements and corrections to this file, but I do
* not require you to send me patches if you make changes. This code has
* NO WARRANTY.
*
* Unless otherwise stated, the rest of PhysicsFS falls under the zlib license.
* Please see LICENSE.txt in the root of the source tree.
*
* \author Ryan C. Gordon.
*/
static int locateOneElement(char *buf)
{
char *ptr;
char **rc;
char **i;
if (PHYSFS_exists(buf))
return 1; /* quick rejection: exists in current case. */
ptr = strrchr(buf, '/'); /* find entry at end of path. */
if (ptr == NULL)
{
rc = PHYSFS_enumerateFiles("/");
ptr = buf;
} /* if */
else
{
*ptr = '\0';
rc = PHYSFS_enumerateFiles(buf);
*ptr = '/';
ptr++; /* point past dirsep to entry itself. */
} /* else */
if (rc != NULL)
{
for (i = rc; *i != NULL; i++)
{
if (PHYSFS_utf8stricmp(*i, ptr) == 0)
{
strcpy(ptr, *i); /* found a match. Overwrite with this case. */
PHYSFS_freeList(rc);
return 1;
} /* if */
} /* for */
PHYSFS_freeList(rc);
} /* if */
/* no match at all... */
return 0;
} /* locateOneElement */
int PHYSFSEXT_locateCorrectCase(char *buf)
{
int rc;
char *ptr;
while (*buf == '/') /* skip any '/' at start of string... */
buf++;
ptr = buf;
if (*ptr == '\0')
return 0; /* Uh...I guess that's success. */
while ( (ptr = strchr(ptr + 1, '/')) != NULL )
{
*ptr = '\0'; /* block this path section off */
rc = locateOneElement(buf);
*ptr = '/'; /* restore path separator */
if (!rc)
return -2; /* missing element in path. */
} /* while */
/* check final element... */
return locateOneElement(buf) ? 0 : -1;
} /* PHYSFSEXT_locateCorrectCase */
#ifdef TEST_PHYSFSEXT_LOCATECORRECTCASE
int main(int argc, char **argv)
{
int rc;
char buf[128];
PHYSFS_File *f;
if (!PHYSFS_init(argv[0]))
{
fprintf(stderr, "PHYSFS_init(): %s\n", PHYSFS_getErrorByCode(PHYSFS_getLastErrorCode()));
return 1;
} /* if */
if (!PHYSFS_addToSearchPath(".", 1))
{
fprintf(stderr, "PHYSFS_addToSearchPath(): %s\n", PHYSFS_getErrorByCode(PHYSFS_getLastErrorCode()));
PHYSFS_deinit();
return 1;
} /* if */
if (!PHYSFS_setWriteDir("."))
{
fprintf(stderr, "PHYSFS_setWriteDir(): %s\n", PHYSFS_getErrorByCode(PHYSFS_getLastErrorCode()));
PHYSFS_deinit();
return 1;
} /* if */
if (!PHYSFS_mkdir("/a/b/c"))
{
fprintf(stderr, "PHYSFS_mkdir(): %s\n", PHYSFS_getErrorByCode(PHYSFS_getLastErrorCode()));
PHYSFS_deinit();
return 1;
} /* if */
if (!PHYSFS_mkdir("/a/b/C"))
{
fprintf(stderr, "PHYSFS_mkdir(): %s\n", PHYSFS_getErrorByCode(PHYSFS_getLastErrorCode()));
PHYSFS_deinit();
return 1;
} /* if */
f = PHYSFS_openWrite("/a/b/c/x.txt");
PHYSFS_close(f);
if (f == NULL)
{
fprintf(stderr, "PHYSFS_openWrite(): %s\n", PHYSFS_getErrorByCode(PHYSFS_getLastErrorCode()));
PHYSFS_deinit();
return 1;
} /* if */
f = PHYSFS_openWrite("/a/b/C/X.txt");
PHYSFS_close(f);
if (f == NULL)
{
fprintf(stderr, "PHYSFS_openWrite(): %s\n", PHYSFS_getErrorByCode(PHYSFS_getLastErrorCode()));
PHYSFS_deinit();
return 1;
} /* if */
strcpy(buf, "/a/b/c/x.txt");
rc = PHYSFSEXT_locateCorrectCase(buf);
if ((rc != 0) || (strcmp(buf, "/a/b/c/x.txt") != 0))
printf("test 1 failed\n");
strcpy(buf, "/a/B/c/x.txt");
rc = PHYSFSEXT_locateCorrectCase(buf);
if ((rc != 0) || (strcmp(buf, "/a/b/c/x.txt") != 0))
printf("test 2 failed\n");
strcpy(buf, "/a/b/C/x.txt");
rc = PHYSFSEXT_locateCorrectCase(buf);
if ((rc != 0) || (strcmp(buf, "/a/b/C/X.txt") != 0))
printf("test 3 failed\n");
strcpy(buf, "/a/b/c/X.txt");
rc = PHYSFSEXT_locateCorrectCase(buf);
if ((rc != 0) || (strcmp(buf, "/a/b/c/x.txt") != 0))
printf("test 4 failed\n");
strcpy(buf, "/a/b/c/z.txt");
rc = PHYSFSEXT_locateCorrectCase(buf);
if ((rc != -1) || (strcmp(buf, "/a/b/c/z.txt") != 0))
printf("test 5 failed\n");
strcpy(buf, "/A/B/Z/z.txt");
rc = PHYSFSEXT_locateCorrectCase(buf);
if ((rc != -2) || (strcmp(buf, "/a/b/Z/z.txt") != 0))
printf("test 6 failed\n");
printf("Testing completed.\n");
printf(" If no errors were reported, you're good to go.\n");
PHYSFS_delete("/a/b/c/x.txt");
PHYSFS_delete("/a/b/C/X.txt");
PHYSFS_delete("/a/b/c");
PHYSFS_delete("/a/b/C");
PHYSFS_delete("/a/b");
PHYSFS_delete("/a");
PHYSFS_deinit();
return 0;
} /* main */
#endif
/* end of ignorecase.c ... */

86
third-party/physfs/extras/ignorecase.h vendored Normal file
View File

@ -0,0 +1,86 @@
#ifndef INCL_PHYSFSEXT_IGNORECASE_H
#define INCL_PHYSFSEXT_IGNORECASE_H
/** \file ignorecase.h */
/**
* \mainpage PhysicsFS ignorecase
*
* This is an extension to PhysicsFS to let you handle files in a
* case-insensitive manner, regardless of what sort of filesystem or
* archive they reside in. It does this by enumerating directories as
* needed and manually locating matching entries.
*
* Please note that this brings with it some caveats:
* - On filesystems that are case-insensitive to start with, such as those
* used on Windows or MacOS, you are adding extra overhead.
* - On filesystems that are case-sensitive, you might select the wrong dir
* or file (which brings security considerations and potential bugs). This
* code favours exact case matches, but you will lose access to otherwise
* duplicate filenames, or you might go down a wrong directory tree, etc.
* In practice, this is rarely a problem, but you need to be aware of it.
* - This doesn't do _anything_ with the write directory; you're on your
* own for opening the right files for writing. You can sort of get around
* this by adding your write directory to the search path, but then the
* interpolated directory tree can screw you up even more.
*
* This code should be considered an aid for legacy code. New development
* shouldn't do things that require this aid in the first place. :)
*
* Usage: Set up PhysicsFS as you normally would, then use
* PHYSFSEXT_locateCorrectCase() to get a "correct" pathname to pass to
* functions like PHYSFS_openRead(), etc.
*
* License: this code is public domain. I make no warranty that it is useful,
* correct, harmless, or environmentally safe.
*
* This particular file may be used however you like, including copying it
* verbatim into a closed-source project, exploiting it commercially, and
* removing any trace of my name from the source (although I hope you won't
* do that). I welcome enhancements and corrections to this file, but I do
* not require you to send me patches if you make changes. This code has
* NO WARRANTY.
*
* Unless otherwise stated, the rest of PhysicsFS falls under the zlib license.
* Please see LICENSE.txt in the root of the source tree.
*
* \author Ryan C. Gordon.
*/
#ifdef __cplusplus
extern "C" {
#endif
/**
* \fn int PHYSFSEXT_locateCorrectCase(char *buf)
* \brief Find an existing filename with matching case.
*
* This function will look for a path/filename that matches the passed in
* buffer. Each element of the buffer's path is checked for a
* case-insensitive match. The buffer must specify a null-terminated string
* in platform-independent notation.
*
* Please note results may be skewed differently depending on whether symlinks
* are enabled or not.
*
* Each element of the buffer is overwritten with the actual case of an
* existing match. If there is no match, the search aborts and reports an
* error. Exact matches are favored over case-insensitive matches.
*
* THIS IS RISKY. Please do not use this function for anything but legacy code.
*
* \param buf Buffer with null-terminated string of path/file to locate.
* This buffer will be modified by this function.
* \return zero if match was found, -1 if the final element (the file itself)
* is missing, -2 if one of the parent directories is missing.
*/
int PHYSFSEXT_locateCorrectCase(char *buf);
#ifdef __cplusplus
}
#endif
#endif /* include-once blocker. */
/* end of ignorecase.h ... */

View File

@ -0,0 +1,276 @@
#!/usr/bin/perl -w
use warnings;
use strict;
my $HASHBUCKETS1_16 = 256;
my $HASHBUCKETS1_32 = 16;
my $HASHBUCKETS2_16 = 16;
my $HASHBUCKETS3_16 = 4;
print <<__EOF__;
/*
* This file is part of PhysicsFS (https://icculus.org/physfs/)
*
* This data generated by physfs/extras/makecasefoldhashtable.pl ...
* Do not manually edit this file!
*
* Please see the file LICENSE.txt in the source's root directory.
*/
#ifndef _INCLUDE_PHYSFS_CASEFOLDING_H_
#define _INCLUDE_PHYSFS_CASEFOLDING_H_
#ifndef __PHYSICSFS_INTERNAL__
#error Do not include this header from your applications.
#endif
/* We build three simple hashmaps here: one that maps Unicode codepoints to
a one, two, or three lowercase codepoints. To retrieve this info: look at
case_fold_hashX, where X is 1, 2, or 3. Most foldable codepoints fold to one,
a few dozen fold to two, and a handful fold to three. If the codepoint isn't
in any of these hashes, it doesn't fold (no separate upper and lowercase).
Almost all these codepoints fit into 16 bits, so we hash them as such to save
memory. If a codepoint is > 0xFFFF, we have separate hashes for them,
since there are (currently) only about 120 of them and (currently) all of them
map to a single lowercase codepoint. */
typedef struct CaseFoldMapping1_32
{
PHYSFS_uint32 from;
PHYSFS_uint32 to0;
} CaseFoldMapping1_32;
typedef struct CaseFoldMapping1_16
{
PHYSFS_uint16 from;
PHYSFS_uint16 to0;
} CaseFoldMapping1_16;
typedef struct CaseFoldMapping2_16
{
PHYSFS_uint16 from;
PHYSFS_uint16 to0;
PHYSFS_uint16 to1;
} CaseFoldMapping2_16;
typedef struct CaseFoldMapping3_16
{
PHYSFS_uint16 from;
PHYSFS_uint16 to0;
PHYSFS_uint16 to1;
PHYSFS_uint16 to2;
} CaseFoldMapping3_16;
typedef struct CaseFoldHashBucket1_16
{
const CaseFoldMapping1_16 *list;
const PHYSFS_uint8 count;
} CaseFoldHashBucket1_16;
typedef struct CaseFoldHashBucket1_32
{
const CaseFoldMapping1_32 *list;
const PHYSFS_uint8 count;
} CaseFoldHashBucket1_32;
typedef struct CaseFoldHashBucket2_16
{
const CaseFoldMapping2_16 *list;
const PHYSFS_uint8 count;
} CaseFoldHashBucket2_16;
typedef struct CaseFoldHashBucket3_16
{
const CaseFoldMapping3_16 *list;
const PHYSFS_uint8 count;
} CaseFoldHashBucket3_16;
__EOF__
my @foldPairs1_16;
my @foldPairs2_16;
my @foldPairs3_16;
my @foldPairs1_32;
for (my $i = 0; $i < $HASHBUCKETS1_16; $i++) {
$foldPairs1_16[$i] = '';
}
for (my $i = 0; $i < $HASHBUCKETS1_32; $i++) {
$foldPairs1_32[$i] = '';
}
for (my $i = 0; $i < $HASHBUCKETS2_16; $i++) {
$foldPairs2_16[$i] = '';
}
for (my $i = 0; $i < $HASHBUCKETS3_16; $i++) {
$foldPairs3_16[$i] = '';
}
open(FH,'<','casefolding.txt') or die("failed to open casefolding.txt: $!\n");
while (<FH>) {
chomp;
# strip comments from textfile...
s/\#.*\Z//;
# strip whitespace...
s/\A\s+//;
s/\s+\Z//;
next if not /\A([a-fA-F0-9]+)\;\s*(.)\;\s*(.+)\;/;
my ($code, $status, $mapping) = ($1, $2, $3);
my $hexxed = hex($code);
#print("// code '$code' status '$status' mapping '$mapping'\n");
if (($status eq 'C') or ($status eq 'F')) {
my ($map1, $map2, $map3) = (undef, undef, undef);
$map1 = $1 if $mapping =~ s/\A([a-fA-F0-9]+)(\s*|\Z)//;
$map2 = $1 if $mapping =~ s/\A([a-fA-F0-9]+)(\s*|\Z)//;
$map3 = $1 if $mapping =~ s/\A([a-fA-F0-9]+)(\s*|\Z)//;
die("mapping space too small for '$code'\n") if ($mapping ne '');
die("problem parsing mapping for '$code'\n") if (not defined($map1));
if ($hexxed < 128) {
# Just ignore these, we'll handle the low-ASCII ones ourselves.
} elsif ($hexxed > 0xFFFF) {
# We just need to add the 32-bit 2 and/or 3 codepoint maps if this die()'s here.
die("Uhoh, a codepoint > 0xFFFF that folds to multiple codepoints! Fixme.") if defined($map2);
my $hashed = (($hexxed ^ ($hexxed >> 8)) & ($HASHBUCKETS1_32-1));
#print("// hexxed '$hexxed' hashed1 '$hashed'\n");
$foldPairs1_32[$hashed] .= " { 0x$code, 0x$map1 },\n";
} elsif (not defined($map2)) {
my $hashed = (($hexxed ^ ($hexxed >> 8)) & ($HASHBUCKETS1_16-1));
#print("// hexxed '$hexxed' hashed1 '$hashed'\n");
$foldPairs1_16[$hashed] .= " { 0x$code, 0x$map1 },\n";
} elsif (not defined($map3)) {
my $hashed = (($hexxed ^ ($hexxed >> 8)) & ($HASHBUCKETS2_16-1));
#print("// hexxed '$hexxed' hashed2 '$hashed'\n");
$foldPairs2_16[$hashed] .= " { 0x$code, 0x$map1, 0x$map2 },\n";
} else {
my $hashed = (($hexxed ^ ($hexxed >> 8)) & ($HASHBUCKETS3_16-1));
#print("// hexxed '$hexxed' hashed3 '$hashed'\n");
$foldPairs3_16[$hashed] .= " { 0x$code, 0x$map1, 0x$map2, 0x$map3 },\n";
}
}
}
close(FH);
for (my $i = 0; $i < $HASHBUCKETS1_16; $i++) {
$foldPairs1_16[$i] =~ s/,\n\Z//;
my $str = $foldPairs1_16[$i];
next if $str eq '';
my $num = '000' . $i;
$num =~ s/\A.*?(\d\d\d)\Z/$1/;
my $sym = "case_fold1_16_${num}";
print("static const CaseFoldMapping1_16 ${sym}[] = {\n$str\n};\n\n");
}
for (my $i = 0; $i < $HASHBUCKETS1_32; $i++) {
$foldPairs1_32[$i] =~ s/,\n\Z//;
my $str = $foldPairs1_32[$i];
next if $str eq '';
my $num = '000' . $i;
$num =~ s/\A.*?(\d\d\d)\Z/$1/;
my $sym = "case_fold1_32_${num}";
print("static const CaseFoldMapping1_32 ${sym}[] = {\n$str\n};\n\n");
}
for (my $i = 0; $i < $HASHBUCKETS2_16; $i++) {
$foldPairs2_16[$i] =~ s/,\n\Z//;
my $str = $foldPairs2_16[$i];
next if $str eq '';
my $num = '000' . $i;
$num =~ s/\A.*?(\d\d\d)\Z/$1/;
my $sym = "case_fold2_16_${num}";
print("static const CaseFoldMapping2_16 ${sym}[] = {\n$str\n};\n\n");
}
for (my $i = 0; $i < $HASHBUCKETS3_16; $i++) {
$foldPairs3_16[$i] =~ s/,\n\Z//;
my $str = $foldPairs3_16[$i];
next if $str eq '';
my $num = '000' . $i;
$num =~ s/\A.*?(\d\d\d)\Z/$1/;
my $sym = "case_fold3_16_${num}";
print("static const CaseFoldMapping3_16 ${sym}[] = {\n$str\n};\n\n");
}
print("static const CaseFoldHashBucket1_16 case_fold_hash1_16[] = {\n");
for (my $i = 0; $i < $HASHBUCKETS1_16; $i++) {
my $str = $foldPairs1_16[$i];
if ($str eq '') {
print(" { NULL, 0 },\n");
} else {
my $num = '000' . $i;
$num =~ s/\A.*?(\d\d\d)\Z/$1/;
my $sym = "case_fold1_16_${num}";
print(" { $sym, __PHYSFS_ARRAYLEN($sym) },\n");
}
}
print("};\n\n");
print("static const CaseFoldHashBucket1_32 case_fold_hash1_32[] = {\n");
for (my $i = 0; $i < $HASHBUCKETS1_32; $i++) {
my $str = $foldPairs1_32[$i];
if ($str eq '') {
print(" { NULL, 0 },\n");
} else {
my $num = '000' . $i;
$num =~ s/\A.*?(\d\d\d)\Z/$1/;
my $sym = "case_fold1_32_${num}";
print(" { $sym, __PHYSFS_ARRAYLEN($sym) },\n");
}
}
print("};\n\n");
print("static const CaseFoldHashBucket2_16 case_fold_hash2_16[] = {\n");
for (my $i = 0; $i < $HASHBUCKETS2_16; $i++) {
my $str = $foldPairs2_16[$i];
if ($str eq '') {
print(" { NULL, 0 },\n");
} else {
my $num = '000' . $i;
$num =~ s/\A.*?(\d\d\d)\Z/$1/;
my $sym = "case_fold2_16_${num}";
print(" { $sym, __PHYSFS_ARRAYLEN($sym) },\n");
}
}
print("};\n\n");
print("static const CaseFoldHashBucket3_16 case_fold_hash3_16[] = {\n");
for (my $i = 0; $i < $HASHBUCKETS3_16; $i++) {
my $str = $foldPairs3_16[$i];
if ($str eq '') {
print(" { NULL, 0 },\n");
} else {
my $num = '000' . $i;
$num =~ s/\A.*?(\d\d\d)\Z/$1/;
my $sym = "case_fold3_16_${num}";
print(" { $sym, __PHYSFS_ARRAYLEN($sym) },\n");
}
}
print("};\n\n");
print <<__EOF__;
#endif /* _INCLUDE_PHYSFS_CASEFOLDING_H_ */
/* end of physfs_casefolding.h ... */
__EOF__
exit 0;
# end of makecashfoldhashtable.pl ...

11
third-party/physfs/extras/physfs.pc.in vendored Normal file
View File

@ -0,0 +1,11 @@
prefix=@CMAKE_INSTALL_PREFIX@
exec_prefix=@CMAKE_INSTALL_PREFIX@
libdir=@CMAKE_INSTALL_FULL_LIBDIR@
includedir=@CMAKE_INSTALL_FULL_INCLUDEDIR@
Name: PhysicsFS
Description: PhysicsFS is a library to provide abstract access to various archives.
URL: https://icculus.org/physfs/
Version: @PHYSFS_VERSION@
Libs: -L${libdir} -lphysfs
Cflags: -I${includedir}

400
third-party/physfs/extras/physfshttpd.c vendored Normal file
View File

@ -0,0 +1,400 @@
/*
* This is a quick and dirty HTTP server that uses PhysicsFS to retrieve
* files. It is not robust at all, probably buggy, and definitely poorly
* designed. It's just meant to show that it can be done.
*
* Basically, you compile this code, and run it:
* ./physfshttpd archive1.zip archive2.zip /path/to/a/real/dir etc...
*
* The files are appended in order to the PhysicsFS search path, and when
* a client request comes in, it looks for the file in said search path.
*
* My goal was to make this work in less than 300 lines of C, so again, it's
* not to be used for any serious purpose. Patches to make this application
* suck less will be readily and gratefully accepted.
*
* Command line I used to build this on Linux:
* gcc -Wall -Werror -g -o bin/physfshttpd extras/physfshttpd.c -lphysfs
*
* License: this code is public domain. I make no warranty that it is useful,
* correct, harmless, or environmentally safe.
*
* This particular file may be used however you like, including copying it
* verbatim into a closed-source project, exploiting it commercially, and
* removing any trace of my name from the source (although I hope you won't
* do that). I welcome enhancements and corrections to this file, but I do
* not require you to send me patches if you make changes. This code has
* NO WARRANTY.
*
* Unless otherwise stated, the rest of PhysicsFS falls under the zlib license.
* Please see LICENSE.txt in the root of the source tree.
*
* This file was written by Ryan C. Gordon. (icculus@icculus.org).
*/
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <ctype.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#ifndef LACKING_SIGNALS
#include <signal.h>
#endif
#ifndef LACKING_PROTOENT
#include <netdb.h>
#endif
#include "physfs.h"
#define DEFAULT_PORTNUM 8080
typedef struct
{
int sock;
struct sockaddr *addr;
socklen_t addrlen;
} http_args;
#define txt404 \
"HTTP/1.0 404 Not Found\n" \
"Connection: close\n" \
"Content-Type: text/html; charset=utf-8\n" \
"\n" \
"<html><head><title>404 Not Found</title></head>\n" \
"<body>Can't find '%s'.</body></html>\n\n" \
#define txt200 \
"HTTP/1.0 200 OK\n" \
"Connection: close\n" \
"Content-Type: %s\n" \
"\n"
static const char *lastError(void)
{
return PHYSFS_getErrorByCode(PHYSFS_getLastErrorCode());
} /* lastError */
static int writeAll(const char *ipstr, const int sock, void *buf, const size_t len)
{
if (write(sock, buf, len) != len)
{
printf("%s: Write error to socket.\n", ipstr);
return 0;
} /* if */
return 1;
} /* writeAll */
static int writeString(const char *ipstr, const int sock, const char *fmt, ...)
{
/* none of this is robust against large strings or HTML escaping. */
char buffer[1024];
int len;
va_list ap;
va_start(ap, fmt);
len = vsnprintf(buffer, sizeof (buffer), fmt, ap);
va_end(ap);
if (len < 0)
{
printf("uhoh, vsnprintf() failed!\n");
return 0;
} /* if */
return writeAll(ipstr, sock, buffer, len);
} /* writeString */
static void feed_file_http(const char *ipstr, int sock, const char *fname)
{
PHYSFS_File *in = PHYSFS_openRead(fname);
if (in == NULL)
{
printf("%s: Can't open [%s]: %s.\n", ipstr, fname, lastError());
writeString(ipstr, sock, txt404, fname);
return;
} /* if */
/* !!! FIXME: mimetype */
if (writeString(ipstr, sock, txt200, "text/plain; charset=utf-8"))
{
do
{
char buffer[1024];
PHYSFS_sint64 br = PHYSFS_readBytes(in, buffer, sizeof (buffer));
if (br == -1)
{
printf("%s: Read error: %s.\n", ipstr, lastError());
break;
} /* if */
else if (!writeAll(ipstr, sock, buffer, (size_t) br))
{
break;
} /* else if */
} while (!PHYSFS_eof(in));
} /* if */
PHYSFS_close(in);
} /* feed_file_http */
static void feed_dirlist_http(const char *ipstr, int sock,
const char *dname, char **list)
{
int i;
if (!writeString(ipstr, sock, txt200, "text/html; charset=utf-8"))
return;
else if (!writeString(ipstr, sock,
"<html><head><title>Directory %s</title></head>"
"<body><p><h1>Directory %s</h1></p><p><ul>\n",
dname, dname))
return;
if (strcmp(dname, "/") == 0)
dname = "";
for (i = 0; list[i]; i++)
{
const char *fname = list[i];
if (!writeString(ipstr, sock,
"<li><a href='%s/%s'>%s</a></li>\n", dname, fname, fname))
break;
} /* for */
writeString(ipstr, sock, "</ul></body></html>\n");
} /* feed_dirlist_http */
static void feed_dir_http(const char *ipstr, int sock, const char *dname)
{
char **list = PHYSFS_enumerateFiles(dname);
if (list == NULL)
{
printf("%s: Can't enumerate directory [%s]: %s.\n",
ipstr, dname, lastError());
writeString(ipstr, sock, txt404, dname);
return;
} /* if */
feed_dirlist_http(ipstr, sock, dname, list);
PHYSFS_freeList(list);
} /* feed_dir_http */
static void feed_http_request(const char *ipstr, int sock, const char *fname)
{
PHYSFS_Stat statbuf;
printf("%s: requested [%s].\n", ipstr, fname);
if (!PHYSFS_stat(fname, &statbuf))
{
printf("%s: Can't stat [%s]: %s.\n", ipstr, fname, lastError());
writeString(ipstr, sock, txt404, fname);
return;
} /* if */
if (statbuf.filetype == PHYSFS_FILETYPE_DIRECTORY)
feed_dir_http(ipstr, sock, fname);
else
feed_file_http(ipstr, sock, fname);
} /* feed_http_request */
static void *do_http(void *_args)
{
http_args *args = (http_args *) _args;
char ipstr[128];
char buffer[512];
char *ptr;
strncpy(ipstr, inet_ntoa(((struct sockaddr_in *) args->addr)->sin_addr),
sizeof (ipstr));
ipstr[sizeof (ipstr) - 1] = '\0';
printf("%s: connected.\n", ipstr);
read(args->sock, buffer, sizeof (buffer));
buffer[sizeof (buffer) - 1] = '\0';
ptr = strchr(buffer, '\n');
if (!ptr)
printf("%s: potentially bogus request.\n", ipstr);
else
{
*ptr = '\0';
ptr = strchr(buffer, '\r');
if (ptr != NULL)
*ptr = '\0';
if ((toupper(buffer[0]) == 'G') &&
(toupper(buffer[1]) == 'E') &&
(toupper(buffer[2]) == 'T') &&
(toupper(buffer[3]) == ' ') &&
(toupper(buffer[4]) == '/'))
{
ptr = strchr(buffer + 5, ' ');
if (ptr != NULL)
*ptr = '\0';
feed_http_request(ipstr, args->sock, buffer + 4);
} /* if */
} /* else */
/* !!! FIXME: Time the transfer. */
printf("%s: closing connection.\n", ipstr);
close(args->sock);
free(args->addr);
free(args);
return NULL;
} /* do_http */
static void serve_http_request(int sock, struct sockaddr *addr,
socklen_t addrlen)
{
http_args *args = (http_args *) malloc(sizeof (http_args));
if (args == NULL)
{
printf("out of memory.\n");
return;
} /* if */
args->addr = (struct sockaddr *) malloc(addrlen);
if (args->addr == NULL)
{
free(args);
printf("out of memory.\n");
return;
} /* if */
args->sock = sock;
args->addrlen = addrlen;
memcpy(args->addr, addr, addrlen);
/* !!! FIXME: optionally spin a thread... */
do_http((void *) args);
} /* server_http_request */
static int create_listen_socket(short portnum)
{
int retval = -1;
int protocol = 0; /* pray this is right. */
#ifndef LACKING_PROTOENT
struct protoent *prot;
setprotoent(0);
prot = getprotobyname("tcp");
if (prot != NULL)
protocol = prot->p_proto;
#endif
retval = socket(PF_INET, SOCK_STREAM, protocol);
if (retval >= 0)
{
struct sockaddr_in addr;
addr.sin_family = AF_INET;
addr.sin_port = htons(portnum);
addr.sin_addr.s_addr = INADDR_ANY;
if ((bind(retval, (struct sockaddr *) &addr, (socklen_t) sizeof (addr)) == -1) ||
(listen(retval, 5) == -1))
{
close(retval);
retval = -1;
} /* if */
} /* if */
return retval;
} /* create_listen_socket */
static int listensocket = -1;
void at_exit_cleanup(void)
{
/*
* !!! FIXME: If thread support, signal threads to terminate and
* !!! FIXME: wait for them to clean up.
*/
if (listensocket >= 0)
close(listensocket);
if (!PHYSFS_deinit())
printf("PHYSFS_deinit() failed: %s\n", lastError());
} /* at_exit_cleanup */
int main(int argc, char **argv)
{
int i;
int portnum = DEFAULT_PORTNUM;
setbuf(stdout, NULL);
setbuf(stderr, NULL);
#ifndef LACKING_SIGNALS
/* I'm not sure if this qualifies as a cheap trick... */
signal(SIGTERM, exit);
signal(SIGINT, exit);
signal(SIGFPE, exit);
signal(SIGSEGV, exit);
signal(SIGPIPE, exit);
signal(SIGILL, exit);
#endif
if (argc == 1)
{
printf("USAGE: %s <archive1> [archive2 [... archiveN]]\n", argv[0]);
return 42;
} /* if */
if (!PHYSFS_init(argv[0]))
{
printf("PHYSFS_init() failed: %s\n", lastError());
return 42;
} /* if */
/* normally, this is bad practice, but oh well. */
atexit(at_exit_cleanup);
for (i = 1; i < argc; i++)
{
if (!PHYSFS_mount(argv[i], NULL, 1))
printf(" WARNING: failed to add [%s] to search path.\n", argv[i]);
} /* else */
listensocket = create_listen_socket(portnum);
if (listensocket < 0)
{
printf("listen socket failed to create.\n");
return 42;
} /* if */
while (1) /* infinite loop for now. */
{
struct sockaddr addr;
socklen_t len;
int s = accept(listensocket, &addr, &len);
if (s < 0)
{
printf("accept() failed: %s\n", strerror(errno));
close(listensocket);
return 42;
} /* if */
serve_http_request(s, &addr, len);
} /* while */
return 0;
} /* main */
/* end of physfshttpd.c ... */

251
third-party/physfs/extras/physfsrwops.c vendored Normal file
View File

@ -0,0 +1,251 @@
/*
* This code provides a glue layer between PhysicsFS and Simple Directmedia
* Layer's (SDL) RWops i/o abstraction.
*
* License: this code is public domain. I make no warranty that it is useful,
* correct, harmless, or environmentally safe.
*
* This particular file may be used however you like, including copying it
* verbatim into a closed-source project, exploiting it commercially, and
* removing any trace of my name from the source (although I hope you won't
* do that). I welcome enhancements and corrections to this file, but I do
* not require you to send me patches if you make changes. This code has
* NO WARRANTY.
*
* Unless otherwise stated, the rest of PhysicsFS falls under the zlib license.
* Please see LICENSE.txt in the root of the source tree.
*
* SDL 1.2 falls under the LGPL license. SDL 1.3+ is zlib, like PhysicsFS.
* You can get SDL at https://www.libsdl.org/
*
* This file was written by Ryan C. Gordon. (icculus@icculus.org).
*/
#include <stdio.h> /* used for SEEK_SET, SEEK_CUR, SEEK_END ... */
#include "physfsrwops.h"
/* SDL's RWOPS interface changed a little in SDL 2.0... */
#if defined(SDL_VERSION_ATLEAST)
#if SDL_VERSION_ATLEAST(2, 0, 0)
#define TARGET_SDL2 1
#endif
#endif
#if !TARGET_SDL2
#ifndef RW_SEEK_SET
#define RW_SEEK_SET SEEK_SET
#endif
#ifndef RW_SEEK_CUR
#define RW_SEEK_CUR SEEK_CUR
#endif
#ifndef RW_SEEK_END
#define RW_SEEK_END SEEK_END
#endif
#endif
#if TARGET_SDL2
static Sint64 SDLCALL physfsrwops_size(struct SDL_RWops *rw)
{
PHYSFS_File *handle = (PHYSFS_File *) rw->hidden.unknown.data1;
return (Sint64) PHYSFS_fileLength(handle);
} /* physfsrwops_size */
#endif
#if TARGET_SDL2
static Sint64 SDLCALL physfsrwops_seek(struct SDL_RWops *rw, Sint64 offset, int whence)
#else
static int physfsrwops_seek(SDL_RWops *rw, int offset, int whence)
#endif
{
PHYSFS_File *handle = (PHYSFS_File *) rw->hidden.unknown.data1;
PHYSFS_sint64 pos = 0;
if (whence == RW_SEEK_SET)
pos = (PHYSFS_sint64) offset;
else if (whence == RW_SEEK_CUR)
{
const PHYSFS_sint64 current = PHYSFS_tell(handle);
if (current == -1)
{
SDL_SetError("Can't find position in file: %s",
PHYSFS_getErrorByCode(PHYSFS_getLastErrorCode()));
return -1;
} /* if */
if (offset == 0) /* this is a "tell" call. We're done. */
{
#if TARGET_SDL2
return (Sint64) current;
#else
return (int) current;
#endif
} /* if */
pos = current + ((PHYSFS_sint64) offset);
} /* else if */
else if (whence == RW_SEEK_END)
{
const PHYSFS_sint64 len = PHYSFS_fileLength(handle);
if (len == -1)
{
SDL_SetError("Can't find end of file: %s", PHYSFS_getErrorByCode(PHYSFS_getLastErrorCode()));
return -1;
} /* if */
pos = len + ((PHYSFS_sint64) offset);
} /* else if */
else
{
SDL_SetError("Invalid 'whence' parameter.");
return -1;
} /* else */
if ( pos < 0 )
{
SDL_SetError("Attempt to seek past start of file.");
return -1;
} /* if */
if (!PHYSFS_seek(handle, (PHYSFS_uint64) pos))
{
SDL_SetError("PhysicsFS error: %s", PHYSFS_getErrorByCode(PHYSFS_getLastErrorCode()));
return -1;
} /* if */
#if TARGET_SDL2
return (Sint64) pos;
#else
return (int) pos;
#endif
} /* physfsrwops_seek */
#if TARGET_SDL2
static size_t SDLCALL physfsrwops_read(struct SDL_RWops *rw, void *ptr,
size_t size, size_t maxnum)
#else
static int physfsrwops_read(SDL_RWops *rw, void *ptr, int size, int maxnum)
#endif
{
PHYSFS_File *handle = (PHYSFS_File *) rw->hidden.unknown.data1;
const PHYSFS_uint64 readlen = (PHYSFS_uint64) (maxnum * size);
const PHYSFS_sint64 rc = PHYSFS_readBytes(handle, ptr, readlen);
if (rc != ((PHYSFS_sint64) readlen))
{
if (!PHYSFS_eof(handle)) /* not EOF? Must be an error. */
{
SDL_SetError("PhysicsFS error: %s", PHYSFS_getErrorByCode(PHYSFS_getLastErrorCode()));
#if TARGET_SDL2
return 0;
#else
return -1;
#endif
} /* if */
} /* if */
#if TARGET_SDL2
return (size_t) rc / size;
#else
return (int) rc / size;
#endif
} /* physfsrwops_read */
#if TARGET_SDL2
static size_t SDLCALL physfsrwops_write(struct SDL_RWops *rw, const void *ptr,
size_t size, size_t num)
#else
static int physfsrwops_write(SDL_RWops *rw, const void *ptr, int size, int num)
#endif
{
PHYSFS_File *handle = (PHYSFS_File *) rw->hidden.unknown.data1;
const PHYSFS_uint64 writelen = (PHYSFS_uint64) (num * size);
const PHYSFS_sint64 rc = PHYSFS_writeBytes(handle, ptr, writelen);
if (rc != ((PHYSFS_sint64) writelen))
SDL_SetError("PhysicsFS error: %s", PHYSFS_getErrorByCode(PHYSFS_getLastErrorCode()));
#if TARGET_SDL2
return (size_t) rc;
#else
return (int) rc;
#endif
} /* physfsrwops_write */
static int physfsrwops_close(SDL_RWops *rw)
{
PHYSFS_File *handle = (PHYSFS_File *) rw->hidden.unknown.data1;
if (!PHYSFS_close(handle))
{
SDL_SetError("PhysicsFS error: %s", PHYSFS_getErrorByCode(PHYSFS_getLastErrorCode()));
return -1;
} /* if */
SDL_FreeRW(rw);
return 0;
} /* physfsrwops_close */
static SDL_RWops *create_rwops(PHYSFS_File *handle)
{
SDL_RWops *retval = NULL;
if (handle == NULL)
SDL_SetError("PhysicsFS error: %s", PHYSFS_getErrorByCode(PHYSFS_getLastErrorCode()));
else
{
retval = SDL_AllocRW();
if (retval != NULL)
{
#if TARGET_SDL2
retval->size = physfsrwops_size;
#endif
retval->seek = physfsrwops_seek;
retval->read = physfsrwops_read;
retval->write = physfsrwops_write;
retval->close = physfsrwops_close;
retval->hidden.unknown.data1 = handle;
} /* if */
} /* else */
return retval;
} /* create_rwops */
SDL_RWops *PHYSFSRWOPS_makeRWops(PHYSFS_File *handle)
{
SDL_RWops *retval = NULL;
if (handle == NULL)
SDL_SetError("NULL pointer passed to PHYSFSRWOPS_makeRWops().");
else
retval = create_rwops(handle);
return retval;
} /* PHYSFSRWOPS_makeRWops */
SDL_RWops *PHYSFSRWOPS_openRead(const char *fname)
{
return create_rwops(PHYSFS_openRead(fname));
} /* PHYSFSRWOPS_openRead */
SDL_RWops *PHYSFSRWOPS_openWrite(const char *fname)
{
return create_rwops(PHYSFS_openWrite(fname));
} /* PHYSFSRWOPS_openWrite */
SDL_RWops *PHYSFSRWOPS_openAppend(const char *fname)
{
return create_rwops(PHYSFS_openAppend(fname));
} /* PHYSFSRWOPS_openAppend */
/* end of physfsrwops.c ... */

89
third-party/physfs/extras/physfsrwops.h vendored Normal file
View File

@ -0,0 +1,89 @@
/*
* This code provides a glue layer between PhysicsFS and Simple Directmedia
* Layer's (SDL) RWops i/o abstraction.
*
* License: this code is public domain. I make no warranty that it is useful,
* correct, harmless, or environmentally safe.
*
* This particular file may be used however you like, including copying it
* verbatim into a closed-source project, exploiting it commercially, and
* removing any trace of my name from the source (although I hope you won't
* do that). I welcome enhancements and corrections to this file, but I do
* not require you to send me patches if you make changes. This code has
* NO WARRANTY.
*
* Unless otherwise stated, the rest of PhysicsFS falls under the zlib license.
* Please see LICENSE.txt in the root of the source tree.
*
* SDL 1.2 falls under the LGPL license. SDL 1.3+ is zlib, like PhysicsFS.
* You can get SDL at https://www.libsdl.org/
*
* This file was written by Ryan C. Gordon. (icculus@icculus.org).
*/
#ifndef _INCLUDE_PHYSFSRWOPS_H_
#define _INCLUDE_PHYSFSRWOPS_H_
#include "physfs.h"
#include "SDL.h"
#ifdef __cplusplus
extern "C" {
#endif
/**
* Open a platform-independent filename for reading, and make it accessible
* via an SDL_RWops structure. The file will be closed in PhysicsFS when the
* RWops is closed. PhysicsFS should be configured to your liking before
* opening files through this method.
*
* @param filename File to open in platform-independent notation.
* @return A valid SDL_RWops structure on success, NULL on error. Specifics
* of the error can be gleaned from PHYSFS_getLastError().
*/
PHYSFS_DECL SDL_RWops *PHYSFSRWOPS_openRead(const char *fname);
/**
* Open a platform-independent filename for writing, and make it accessible
* via an SDL_RWops structure. The file will be closed in PhysicsFS when the
* RWops is closed. PhysicsFS should be configured to your liking before
* opening files through this method.
*
* @param filename File to open in platform-independent notation.
* @return A valid SDL_RWops structure on success, NULL on error. Specifics
* of the error can be gleaned from PHYSFS_getLastError().
*/
PHYSFS_DECL SDL_RWops *PHYSFSRWOPS_openWrite(const char *fname);
/**
* Open a platform-independent filename for appending, and make it accessible
* via an SDL_RWops structure. The file will be closed in PhysicsFS when the
* RWops is closed. PhysicsFS should be configured to your liking before
* opening files through this method.
*
* @param filename File to open in platform-independent notation.
* @return A valid SDL_RWops structure on success, NULL on error. Specifics
* of the error can be gleaned from PHYSFS_getLastError().
*/
PHYSFS_DECL SDL_RWops *PHYSFSRWOPS_openAppend(const char *fname);
/**
* Make a SDL_RWops from an existing PhysicsFS file handle. You should
* dispose of any references to the handle after successful creation of
* the RWops. The actual PhysicsFS handle will be destroyed when the
* RWops is closed.
*
* @param handle a valid PhysicsFS file handle.
* @return A valid SDL_RWops structure on success, NULL on error. Specifics
* of the error can be gleaned from PHYSFS_getLastError().
*/
PHYSFS_DECL SDL_RWops *PHYSFSRWOPS_makeRWops(PHYSFS_File *handle);
#ifdef __cplusplus
}
#endif
#endif /* include-once blocker */
/* end of physfsrwops.h ... */

181
third-party/physfs/extras/physfsunpack.c vendored Normal file
View File

@ -0,0 +1,181 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include "physfs.h"
static int failure = 0;
static void modTimeToStr(PHYSFS_sint64 modtime, char *modstr, size_t strsize)
{
const char *str = "unknown modtime";
if (modtime != -1)
{
time_t t = (time_t) modtime;
str = ctime(&t);
} /* if */
strncpy(modstr, str, strsize);
modstr[strsize-1] = '\0';
strsize = strlen(modstr);
while ((modstr[strsize-1] == '\n') || (modstr[strsize-1] == '\r'))
modstr[--strsize] = '\0';
} /* modTimeToStr */
static void fail(const char *what, const char *why)
{
if (why == NULL)
why = PHYSFS_getErrorByCode(PHYSFS_getLastErrorCode());
fprintf(stderr, "%s failed: %s\n", what, why);
failure = 1;
} /* fail */
static void dumpFile(const char *fname)
{
const int origfailure = failure;
PHYSFS_File *out = NULL;
PHYSFS_File *in = NULL;
failure = 0;
if ((in = PHYSFS_openRead(fname)) == NULL)
fail("\nPHYSFS_openRead", NULL);
else if ((out = PHYSFS_openWrite(fname)) == NULL)
fail("\nPHYSFS_openWrite", NULL);
else
{
char modstr[64];
PHYSFS_sint64 size = PHYSFS_fileLength(in);
printf("(");
if (size == -1)
printf("?");
else
printf("%lld", (long long) size);
printf(" bytes");
modTimeToStr(PHYSFS_getLastModTime(fname), modstr, sizeof (modstr));
printf(", %s)\n", modstr);
while ( (!failure) && (!PHYSFS_eof(in)) )
{
static char buf[64 * 1024];
PHYSFS_sint64 br = PHYSFS_read(in, buf, 1, sizeof (buf));
if (br == -1)
fail("PHYSFS_read", NULL);
else
{
PHYSFS_sint64 bw = PHYSFS_write(out, buf, 1, br);
if (bw != br)
fail("PHYSFS_write", NULL);
else
size -= bw;
} /* else */
} /* while */
if ((!failure) && (size != 0))
fail("PHYSFS_eof", "BUG! eof != PHYSFS_fileLength bytes!");
} /* else */
if (in != NULL)
PHYSFS_close(in);
if (out != NULL)
{
if (!PHYSFS_close(out))
fail("PHYSFS_close", NULL);
} /* if */
if (failure)
PHYSFS_delete(fname);
else
failure = origfailure;
} /* dumpFile */
static void unpackCallback(void *_depth, const char *origdir, const char *str)
{
int depth = *((int *) _depth);
const int len = strlen(origdir) + strlen(str) + 2;
char *fname = (char *) malloc(len);
if (fname == NULL)
fail("malloc", "Out of memory!");
else
{
if (strcmp(origdir, "/") == 0)
origdir = "";
snprintf(fname, len, "%s/%s", origdir, str);
printf("%s ", fname);
if (PHYSFS_isDirectory(fname))
{
depth++;
printf("(directory)\n");
if (!PHYSFS_mkdir(fname))
fail("PHYSFS_mkdir", NULL);
else
PHYSFS_enumerateFilesCallback(fname, unpackCallback, &depth);
} /* if */
else if (PHYSFS_isSymbolicLink(fname))
{
printf("(symlink)\n");
/* !!! FIXME: ? if (!symlink(fname, */
} /* else if */
else /* ...file. */
{
dumpFile(fname);
} /* else */
free(fname);
} /* else */
} /* unpackCallback */
int main(int argc, char **argv)
{
int zero = 0;
if (argc != 3)
{
fprintf(stderr, "USAGE: %s <archive> <unpackDirectory>\n", argv[0]);
return 1;
} /* if */
if (!PHYSFS_init(argv[0]))
{
fprintf(stderr, "PHYSFS_init() failed: %s\n", PHYSFS_getErrorByCode(PHYSFS_getLastErrorCode()));
return 2;
} /* if */
if (!PHYSFS_setWriteDir(argv[2]))
{
fprintf(stderr, "PHYSFS_setWriteDir('%s') failed: %s\n",
argv[2], PHYSFS_getErrorByCode(PHYSFS_getLastErrorCode()));
return 3;
} /* if */
if (!PHYSFS_mount(argv[1], NULL, 1))
{
fprintf(stderr, "PHYSFS_mount('%s') failed: %s\n",
argv[1], PHYSFS_getErrorByCode(PHYSFS_getLastErrorCode()));
return 4;
} /* if */
PHYSFS_permitSymbolicLinks(1);
PHYSFS_enumerateFilesCallback("/", unpackCallback, &zero);
PHYSFS_deinit();
if (failure)
return 5;
return 0;
} /* main */
/* end of physfsunpack.c ... */

66
third-party/physfs/extras/selfextract.c vendored Normal file
View File

@ -0,0 +1,66 @@
/*
* This code shows how to read a zipfile included in an app's binary.
*
* License: this code is public domain. I make no warranty that it is useful,
* correct, harmless, or environmentally safe.
*
* This particular file may be used however you like, including copying it
* verbatim into a closed-source project, exploiting it commercially, and
* removing any trace of my name from the source (although I hope you won't
* do that). I welcome enhancements and corrections to this file, but I do
* not require you to send me patches if you make changes. This code has
* NO WARRANTY.
*
* Unless otherwise stated, the rest of PhysicsFS falls under the zlib license.
* Please see LICENSE.txt in the root of the source tree.
*
* This file was written by Ryan C. Gordon. (icculus@icculus.org).
*/
/*
* Compile this program and then attach a .zip file to the end of the
* compiled binary.
*
* On Linux, something like this will build the final binary:
* gcc -o selfextract.tmp selfextract.c -lphysfs && \
* cat selfextract.tmp myzipfile.zip >> selfextract && \
* chmod a+x selfextract && \
* rm -f selfextract.tmp
*
* This may not work on all platforms, and it probably only works with
* .zip files, since they are designed to be appended to another file.
*/
#include <stdio.h>
#include "physfs.h"
int main(int argc, char **argv)
{
int rc = 0;
if (!PHYSFS_init(argv[0]))
{
printf("PHYSFS_init() failed: %s\n", PHYSFS_getErrorByCode(PHYSFS_getLastErrorCode()));
return 42;
} /* if */
rc = PHYSFS_addToSearchPath(argv[0], 0);
if (!rc)
{
printf("Couldn't find self-extract data: %s\n", PHYSFS_getErrorByCode(PHYSFS_getLastErrorCode()));
printf("This might mean you didn't append a zipfile to the binary.\n");
return 42;
} /* if */
char **files = PHYSFS_enumerateFiles("/");
char **i;
for (i = files; *i != NULL; i++)
{
const char *dirorfile = PHYSFS_isDirectory(*i) ? "Directory" : "File";
printf(" * %s '%s' is in root of attached data.\n", dirorfile, *i);
} /* for */
PHYSFS_freeList(files);
return 0;
} /* main */

10
third-party/physfs/extras/uninstall.sh vendored Normal file
View File

@ -0,0 +1,10 @@
#!/bin/sh
if [ ! -f "./install_manifest.txt" ]; then
echo "ERROR: This needs to be run from your CMake build directory after installing." 1>&2
exit 1
fi
xargs rm -vf < install_manifest.txt
exit 0

93
third-party/physfs/src/Makefile.os2 vendored Normal file
View File

@ -0,0 +1,93 @@
# Open Watcom makefile to build PhysicsFS for OS/2
# wmake -f Makefile.os2
LIBNAME = physfs
VERSION = 3.2.0
LIBFILE = $(LIBNAME).lib
DLLFILE = $(LIBNAME).dll
LNKFILE = $(LIBNAME).lnk
TITLENAME = $(LIBNAME) $(VERSION)
SRCS = physfs.c &
physfs_byteorder.c &
physfs_unicode.c &
physfs_platform_os2.c &
physfs_archiver_dir.c &
physfs_archiver_unpacked.c &
physfs_archiver_grp.c &
physfs_archiver_hog.c &
physfs_archiver_7z.c &
physfs_archiver_mvl.c &
physfs_archiver_qpak.c &
physfs_archiver_wad.c &
physfs_archiver_zip.c &
physfs_archiver_slb.c &
physfs_archiver_iso9660.c &
physfs_archiver_vdf.c
OBJS = $(SRCS:.c=.obj)
CFLAGS_BASE = -bt=os2 -d0 -q -bm -5s -fp5 -fpi87 -sg -oeatxh -ei -j
CFLAGS_BASE+= -DNDEBUG
# warnings:
CFLAGS_BASE+= -wx
# newer OpenWatcom versions enable W303 by default
CFLAGS_BASE+= -wcd=303
# include paths:
CFLAGS_BASE+= -I"$(%WATCOM)/h/os2" -I"$(%WATCOM)/h"
CFLAGS = $(CFLAGS_BASE)
# to build a dll:
CFLAGS+= -bd
.extensions:
.extensions: .lib .dll .obj .c
all: $(DLLFILE) test_physfs.exe
.c: decoders
.c: examples
$(LIBFILE): $(DLLFILE)
@echo * Create library: $@...
wlib -b -n -q -c -pa -s -t -zld -ii -io $@ $(DLLFILE)
$(DLLFILE): $(OBJS) $(MODPLIB) $(TIMILIB) $(LNKFILE)
@echo * Link: $@
wlink @$(LNKFILE)
$(LNKFILE):
@%create $@
@%append $@ SYSTEM os2v2_dll INITINSTANCE TERMINSTANCE
@%append $@ NAME $(LIBNAME)
@for %i in ($(OBJS)) do @%append $@ FILE %i
@%append $@ OPTION QUIET
@%append $@ OPTION DESCRIPTION '@$#icculus org:$(VERSION)$#@PhysicsFS'
@%append $@ OPTION MAP=$^&.map
@%append $@ OPTION ELIMINATE
@%append $@ OPTION MANYAUTODATA
@%append $@ OPTION OSNAME='OS/2 and eComStation'
@%append $@ OPTION SHOWDEAD
.c.obj:
wcc386 $(CFLAGS) -fo=$^@ $<
test_physfs.obj: "../test/test_physfs.c"
wcc386 $(CFLAGS_BASE) -fo=$^@ $<
test_physfs.exe: $(LIBFILE) test_physfs.obj
@echo * Link: $@
wlink SYS os2v2 LIBR {$(LIBFILE)} op q op el F {test_physfs.obj} N test_physfs.exe
clean: .SYMBOLIC
@echo * Clean: $(TITLENAME)
@if exist *.obj rm *.obj
@if exist *.err rm *.err
@if exist $(LNKFILE) rm $(LNKFILE)
distclean: .SYMBOLIC clean
@if exist $(DLLFILE) rm $(DLLFILE)
@if exist $(LIBFILE) rm $(LIBFILE)
@if exist *.map rm *.map
@if exist *.exe rm *.exe

3462
third-party/physfs/src/physfs.c vendored Normal file

File diff suppressed because it is too large Load Diff

3924
third-party/physfs/src/physfs.h vendored Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,433 @@
/*
* 7zip support routines for PhysicsFS.
*
* Please see the file LICENSE.txt in the source's root directory.
*
* This file was written by Ryan C. Gordon.
*/
#define __PHYSICSFS_INTERNAL__
#include "physfs_internal.h"
#if PHYSFS_SUPPORTS_7Z
#include "physfs_lzmasdk.h"
typedef struct
{
ISeekInStream seekStream; /* lzma sdk i/o interface (lower level). */
PHYSFS_Io *io; /* physfs i/o interface for this archive. */
CLookToRead lookStream; /* lzma sdk i/o interface (higher level). */
} SZIPLookToRead;
/* One SZIPentry is kept for each file in an open 7zip archive. */
typedef struct
{
__PHYSFS_DirTreeEntry tree; /* manages directory tree */
PHYSFS_uint32 dbidx; /* index into lzma sdk database */
} SZIPentry;
/* One SZIPinfo is kept for each open 7zip archive. */
typedef struct
{
__PHYSFS_DirTree tree; /* manages directory tree. */
PHYSFS_Io *io; /* physfs i/o interface for this archive. */
CSzArEx db; /* lzma sdk archive database object. */
} SZIPinfo;
static PHYSFS_ErrorCode szipErrorCode(const SRes rc)
{
switch (rc)
{
case SZ_OK: return PHYSFS_ERR_OK;
case SZ_ERROR_DATA: return PHYSFS_ERR_CORRUPT;
case SZ_ERROR_MEM: return PHYSFS_ERR_OUT_OF_MEMORY;
case SZ_ERROR_CRC: return PHYSFS_ERR_CORRUPT;
case SZ_ERROR_UNSUPPORTED: return PHYSFS_ERR_UNSUPPORTED;
case SZ_ERROR_INPUT_EOF: return PHYSFS_ERR_CORRUPT;
case SZ_ERROR_OUTPUT_EOF: return PHYSFS_ERR_IO;
case SZ_ERROR_READ: return PHYSFS_ERR_IO;
case SZ_ERROR_WRITE: return PHYSFS_ERR_IO;
case SZ_ERROR_ARCHIVE: return PHYSFS_ERR_CORRUPT;
case SZ_ERROR_NO_ARCHIVE: return PHYSFS_ERR_UNSUPPORTED;
default: break;
} /* switch */
return PHYSFS_ERR_OTHER_ERROR;
} /* szipErrorCode */
/* LZMA SDK's ISzAlloc interface ... */
static void *SZIP_ISzAlloc_Alloc(void *p, size_t size)
{
return allocator.Malloc(size ? size : 1);
} /* SZIP_ISzAlloc_Alloc */
static void SZIP_ISzAlloc_Free(void *p, void *address)
{
if (address)
allocator.Free(address);
} /* SZIP_ISzAlloc_Free */
static ISzAlloc SZIP_SzAlloc = {
SZIP_ISzAlloc_Alloc, SZIP_ISzAlloc_Free
};
/* we implement ISeekInStream, and then wrap that in LZMA SDK's CLookToRead,
which implements the higher-level ILookInStream on top of that, handling
buffering and such for us. */
/* LZMA SDK's ISeekInStream interface ... */
static SRes SZIP_ISeekInStream_Read(void *p, void *buf, size_t *size)
{
SZIPLookToRead *stream = (SZIPLookToRead *) p;
PHYSFS_Io *io = stream->io;
const PHYSFS_uint64 len = (PHYSFS_uint64) *size;
const PHYSFS_sint64 rc = (len == 0) ? 0 : io->read(io, buf, len);
if (rc < 0)
{
*size = 0;
return SZ_ERROR_READ;
} /* if */
*size = (size_t) rc;
return SZ_OK;
} /* SZIP_ISeekInStream_Read */
static SRes SZIP_ISeekInStream_Seek(void *p, Int64 *pos, ESzSeek origin)
{
SZIPLookToRead *stream = (SZIPLookToRead *) p;
PHYSFS_Io *io = stream->io;
PHYSFS_sint64 base;
PHYSFS_uint64 newpos;
switch (origin)
{
case SZ_SEEK_SET:
base = 0;
break;
case SZ_SEEK_CUR:
base = io->tell(io);
break;
case SZ_SEEK_END:
base = io->length(io);
break;
default:
return SZ_ERROR_FAIL;
} /* switch */
if (base < 0)
return SZ_ERROR_FAIL;
else if ((*pos < 0) && (((Int64) base) < -*pos))
return SZ_ERROR_FAIL;
newpos = (PHYSFS_uint64) (((Int64) base) + *pos);
if (!io->seek(io, newpos))
return SZ_ERROR_FAIL;
*pos = (Int64) newpos;
return SZ_OK;
} /* SZIP_ISeekInStream_Seek */
static void szipInitStream(SZIPLookToRead *stream, PHYSFS_Io *io)
{
stream->seekStream.Read = SZIP_ISeekInStream_Read;
stream->seekStream.Seek = SZIP_ISeekInStream_Seek;
stream->io = io;
/* !!! FIXME: can we use lookahead? Is there value to it? */
LookToRead_Init(&stream->lookStream);
LookToRead_CreateVTable(&stream->lookStream, False);
stream->lookStream.realStream = &stream->seekStream;
} /* szipInitStream */
/* Do this in a separate function so we can smallAlloc without looping. */
static int szipLoadEntry(SZIPinfo *info, const PHYSFS_uint32 idx)
{
const size_t utf16len = SzArEx_GetFileNameUtf16(&info->db, idx, NULL);
const size_t utf16buflen = utf16len * 2;
PHYSFS_uint16 *utf16 = (PHYSFS_uint16 *) __PHYSFS_smallAlloc(utf16buflen);
const size_t utf8buflen = utf16len * 4;
char *utf8 = (char *) __PHYSFS_smallAlloc(utf8buflen);
int retval = 0;
if (utf16 && utf8)
{
const int isdir = SzArEx_IsDir(&info->db, idx) != 0;
SZIPentry *entry;
SzArEx_GetFileNameUtf16(&info->db, idx, (UInt16 *) utf16);
PHYSFS_utf8FromUtf16(utf16, utf8, utf8buflen);
entry = (SZIPentry*) __PHYSFS_DirTreeAdd(&info->tree, utf8, isdir);
retval = (entry != NULL);
if (retval)
entry->dbidx = idx;
} /* if */
__PHYSFS_smallFree(utf8);
__PHYSFS_smallFree(utf16);
return retval;
} /* szipLoadEntry */
static int szipLoadEntries(SZIPinfo *info)
{
int retval = 0;
if (__PHYSFS_DirTreeInit(&info->tree, sizeof (SZIPentry), 1, 0))
{
const PHYSFS_uint32 count = info->db.NumFiles;
PHYSFS_uint32 i;
for (i = 0; i < count; i++)
BAIL_IF_ERRPASS(!szipLoadEntry(info, i), 0);
retval = 1;
} /* if */
return retval;
} /* szipLoadEntries */
static void SZIP_closeArchive(void *opaque)
{
SZIPinfo *info = (SZIPinfo *) opaque;
if (info)
{
if (info->io)
info->io->destroy(info->io);
SzArEx_Free(&info->db, &SZIP_SzAlloc);
__PHYSFS_DirTreeDeinit(&info->tree);
allocator.Free(info);
} /* if */
} /* SZIP_closeArchive */
static void *SZIP_openArchive(PHYSFS_Io *io, const char *name,
int forWriting, int *claimed)
{
static const PHYSFS_uint8 wantedsig[] = { '7','z',0xBC,0xAF,0x27,0x1C };
SZIPLookToRead stream;
ISzAlloc *alloc = &SZIP_SzAlloc;
SZIPinfo *info = NULL;
SRes rc;
PHYSFS_uint8 sig[6];
PHYSFS_sint64 pos;
BAIL_IF(forWriting, PHYSFS_ERR_READ_ONLY, NULL);
pos = io->tell(io);
BAIL_IF_ERRPASS(pos == -1, NULL);
BAIL_IF_ERRPASS(io->read(io, sig, 6) != 6, NULL);
*claimed = (memcmp(sig, wantedsig, 6) == 0);
BAIL_IF_ERRPASS(!io->seek(io, pos), NULL);
info = (SZIPinfo *) allocator.Malloc(sizeof (SZIPinfo));
BAIL_IF(!info, PHYSFS_ERR_OUT_OF_MEMORY, NULL);
memset(info, '\0', sizeof (*info));
SzArEx_Init(&info->db);
info->io = io;
szipInitStream(&stream, io);
rc = SzArEx_Open(&info->db, &stream.lookStream.s, alloc, alloc);
GOTO_IF(rc != SZ_OK, szipErrorCode(rc), failed);
GOTO_IF_ERRPASS(!szipLoadEntries(info), failed);
return info;
failed:
info->io = NULL; /* don't let cleanup destroy the PHYSFS_Io. */
SZIP_closeArchive(info);
return NULL;
} /* SZIP_openArchive */
static PHYSFS_Io *SZIP_openRead(void *opaque, const char *path)
{
/* !!! FIXME: the current lzma sdk C API only allows you to decompress
!!! FIXME: the entire file at once, which isn't ideal. Fix this in the
!!! FIXME: SDK and then convert this all to a streaming interface. */
SZIPinfo *info = (SZIPinfo *) opaque;
SZIPentry *entry = (SZIPentry *) __PHYSFS_DirTreeFind(&info->tree, path);
ISzAlloc *alloc = &SZIP_SzAlloc;
SZIPLookToRead stream;
PHYSFS_Io *retval = NULL;
PHYSFS_Io *io = NULL;
UInt32 blockIndex = 0xFFFFFFFF;
Byte *outBuffer = NULL;
size_t outBufferSize = 0;
size_t offset = 0;
size_t outSizeProcessed = 0;
void *buf = NULL;
SRes rc;
BAIL_IF_ERRPASS(!entry, NULL);
BAIL_IF(entry->tree.isdir, PHYSFS_ERR_NOT_A_FILE, NULL);
io = info->io->duplicate(info->io);
GOTO_IF_ERRPASS(!io, SZIP_openRead_failed);
szipInitStream(&stream, io);
rc = SzArEx_Extract(&info->db, &stream.lookStream.s, entry->dbidx,
&blockIndex, &outBuffer, &outBufferSize, &offset,
&outSizeProcessed, alloc, alloc);
GOTO_IF(rc != SZ_OK, szipErrorCode(rc), SZIP_openRead_failed);
GOTO_IF(outBuffer == NULL, PHYSFS_ERR_OUT_OF_MEMORY, SZIP_openRead_failed);
io->destroy(io);
io = NULL;
buf = allocator.Malloc(outSizeProcessed ? outSizeProcessed : 1);
GOTO_IF(buf == NULL, PHYSFS_ERR_OUT_OF_MEMORY, SZIP_openRead_failed);
if (outSizeProcessed > 0)
memcpy(buf, outBuffer + offset, outSizeProcessed);
alloc->Free(alloc, outBuffer);
outBuffer = NULL;
retval = __PHYSFS_createMemoryIo(buf, outSizeProcessed, allocator.Free);
GOTO_IF_ERRPASS(!retval, SZIP_openRead_failed);
return retval;
SZIP_openRead_failed:
if (io != NULL)
io->destroy(io);
if (buf)
allocator.Free(buf);
if (outBuffer)
alloc->Free(alloc, outBuffer);
return NULL;
} /* SZIP_openRead */
static PHYSFS_Io *SZIP_openWrite(void *opaque, const char *filename)
{
BAIL(PHYSFS_ERR_READ_ONLY, NULL);
} /* SZIP_openWrite */
static PHYSFS_Io *SZIP_openAppend(void *opaque, const char *filename)
{
BAIL(PHYSFS_ERR_READ_ONLY, NULL);
} /* SZIP_openAppend */
static int SZIP_remove(void *opaque, const char *name)
{
BAIL(PHYSFS_ERR_READ_ONLY, 0);
} /* SZIP_remove */
static int SZIP_mkdir(void *opaque, const char *name)
{
BAIL(PHYSFS_ERR_READ_ONLY, 0);
} /* SZIP_mkdir */
static inline PHYSFS_uint64 lzmasdkTimeToPhysfsTime(const CNtfsFileTime *t)
{
const PHYSFS_uint64 winEpochToUnixEpoch = __PHYSFS_UI64(0x019DB1DED53E8000);
const PHYSFS_uint64 nanosecToMillisec = __PHYSFS_UI64(10000000);
const PHYSFS_uint64 quad = (((PHYSFS_uint64) t->High) << 32) | t->Low;
return (quad - winEpochToUnixEpoch) / nanosecToMillisec;
} /* lzmasdkTimeToPhysfsTime */
static int SZIP_stat(void *opaque, const char *path, PHYSFS_Stat *stat)
{
SZIPinfo *info = (SZIPinfo *) opaque;
SZIPentry *entry;
PHYSFS_uint32 idx;
entry = (SZIPentry *) __PHYSFS_DirTreeFind(&info->tree, path);
BAIL_IF_ERRPASS(!entry, 0);
idx = entry->dbidx;
if (entry->tree.isdir)
{
stat->filesize = -1;
stat->filetype = PHYSFS_FILETYPE_DIRECTORY;
} /* if */
else
{
stat->filesize = (PHYSFS_sint64) SzArEx_GetFileSize(&info->db, idx);
stat->filetype = PHYSFS_FILETYPE_REGULAR;
} /* else */
if (info->db.MTime.Vals != NULL)
stat->modtime = lzmasdkTimeToPhysfsTime(&info->db.MTime.Vals[idx]);
else if (info->db.CTime.Vals != NULL)
stat->modtime = lzmasdkTimeToPhysfsTime(&info->db.CTime.Vals[idx]);
else
stat->modtime = -1;
if (info->db.CTime.Vals != NULL)
stat->createtime = lzmasdkTimeToPhysfsTime(&info->db.CTime.Vals[idx]);
else if (info->db.MTime.Vals != NULL)
stat->createtime = lzmasdkTimeToPhysfsTime(&info->db.MTime.Vals[idx]);
else
stat->createtime = -1;
stat->accesstime = -1;
stat->readonly = 1;
return 1;
} /* SZIP_stat */
void SZIP_global_init(void)
{
/* this just needs to calculate some things, so it only ever
has to run once, even after a deinit. */
static int generatedTable = 0;
if (!generatedTable)
{
generatedTable = 1;
CrcGenerateTable();
} /* if */
} /* SZIP_global_init */
const PHYSFS_Archiver __PHYSFS_Archiver_7Z =
{
CURRENT_PHYSFS_ARCHIVER_API_VERSION,
{
"7Z",
"7zip archives",
"Ryan C. Gordon <icculus@icculus.org>",
"https://icculus.org/physfs/",
0, /* supportsSymlinks */
},
SZIP_openArchive,
__PHYSFS_DirTreeEnumerate,
SZIP_openRead,
SZIP_openWrite,
SZIP_openAppend,
SZIP_remove,
SZIP_mkdir,
SZIP_stat,
SZIP_closeArchive
};
#endif /* defined PHYSFS_SUPPORTS_7Z */
/* end of physfs_archiver_7z.c ... */

View File

@ -0,0 +1,196 @@
/*
* Standard directory I/O support routines for PhysicsFS.
*
* Please see the file LICENSE.txt in the source's root directory.
*
* This file written by Ryan C. Gordon.
*/
#define __PHYSICSFS_INTERNAL__
#include "physfs_internal.h"
/* There's no PHYSFS_Io interface here. Use __PHYSFS_createNativeIo(). */
static char *cvtToDependent(const char *prepend, const char *path,
char *buf, const size_t buflen)
{
BAIL_IF(buf == NULL, PHYSFS_ERR_OUT_OF_MEMORY, NULL);
snprintf(buf, buflen, "%s%s", prepend ? prepend : "", path);
#if !__PHYSFS_STANDARD_DIRSEP
assert(__PHYSFS_platformDirSeparator != '/');
{
char *p;
for (p = strchr(buf, '/'); p != NULL; p = strchr(p + 1, '/'))
*p = __PHYSFS_platformDirSeparator;
} /* if */
#endif
return buf;
} /* cvtToDependent */
#define CVT_TO_DEPENDENT(buf, pre, dir) { \
const size_t len = ((pre) ? strlen((char *) pre) : 0) + strlen(dir) + 1; \
buf = cvtToDependent((char*)pre,dir,(char*)__PHYSFS_smallAlloc(len),len); \
}
static void *DIR_openArchive(PHYSFS_Io *io, const char *name,
int forWriting, int *claimed)
{
PHYSFS_Stat st;
const char dirsep = __PHYSFS_platformDirSeparator;
char *retval = NULL;
const size_t namelen = strlen(name);
const size_t seplen = 1;
assert(io == NULL); /* shouldn't create an Io for these. */
BAIL_IF_ERRPASS(!__PHYSFS_platformStat(name, &st, 1), NULL);
if (st.filetype != PHYSFS_FILETYPE_DIRECTORY)
BAIL(PHYSFS_ERR_UNSUPPORTED, NULL);
*claimed = 1;
retval = allocator.Malloc(namelen + seplen + 1);
BAIL_IF(retval == NULL, PHYSFS_ERR_OUT_OF_MEMORY, NULL);
strcpy(retval, name);
/* make sure there's a dir separator at the end of the string */
if (retval[namelen - 1] != dirsep)
{
retval[namelen] = dirsep;
retval[namelen + 1] = '\0';
} /* if */
return retval;
} /* DIR_openArchive */
static PHYSFS_EnumerateCallbackResult DIR_enumerate(void *opaque,
const char *dname, PHYSFS_EnumerateCallback cb,
const char *origdir, void *callbackdata)
{
char *d;
PHYSFS_EnumerateCallbackResult retval;
CVT_TO_DEPENDENT(d, opaque, dname);
BAIL_IF_ERRPASS(!d, PHYSFS_ENUM_ERROR);
retval = __PHYSFS_platformEnumerate(d, cb, origdir, callbackdata);
__PHYSFS_smallFree(d);
return retval;
} /* DIR_enumerate */
static PHYSFS_Io *doOpen(void *opaque, const char *name, const int mode)
{
PHYSFS_Io *io = NULL;
char *f = NULL;
CVT_TO_DEPENDENT(f, opaque, name);
BAIL_IF_ERRPASS(!f, NULL);
io = __PHYSFS_createNativeIo(f, mode);
if (io == NULL)
{
const PHYSFS_ErrorCode err = PHYSFS_getLastErrorCode();
PHYSFS_Stat statbuf;
__PHYSFS_platformStat(f, &statbuf, 0); /* !!! FIXME: why are we stating here? */
PHYSFS_setErrorCode(err);
} /* if */
__PHYSFS_smallFree(f);
return io;
} /* doOpen */
static PHYSFS_Io *DIR_openRead(void *opaque, const char *filename)
{
return doOpen(opaque, filename, 'r');
} /* DIR_openRead */
static PHYSFS_Io *DIR_openWrite(void *opaque, const char *filename)
{
return doOpen(opaque, filename, 'w');
} /* DIR_openWrite */
static PHYSFS_Io *DIR_openAppend(void *opaque, const char *filename)
{
return doOpen(opaque, filename, 'a');
} /* DIR_openAppend */
static int DIR_remove(void *opaque, const char *name)
{
int retval;
char *f;
CVT_TO_DEPENDENT(f, opaque, name);
BAIL_IF_ERRPASS(!f, 0);
retval = __PHYSFS_platformDelete(f);
__PHYSFS_smallFree(f);
return retval;
} /* DIR_remove */
static int DIR_mkdir(void *opaque, const char *name)
{
int retval;
char *f;
CVT_TO_DEPENDENT(f, opaque, name);
BAIL_IF_ERRPASS(!f, 0);
retval = __PHYSFS_platformMkDir(f);
__PHYSFS_smallFree(f);
return retval;
} /* DIR_mkdir */
static void DIR_closeArchive(void *opaque)
{
allocator.Free(opaque);
} /* DIR_closeArchive */
static int DIR_stat(void *opaque, const char *name, PHYSFS_Stat *stat)
{
int retval = 0;
char *d;
CVT_TO_DEPENDENT(d, opaque, name);
BAIL_IF_ERRPASS(!d, 0);
retval = __PHYSFS_platformStat(d, stat, 0);
__PHYSFS_smallFree(d);
return retval;
} /* DIR_stat */
const PHYSFS_Archiver __PHYSFS_Archiver_DIR =
{
CURRENT_PHYSFS_ARCHIVER_API_VERSION,
{
"",
"Non-archive, direct filesystem I/O",
"Ryan C. Gordon <icculus@icculus.org>",
"https://icculus.org/physfs/",
1, /* supportsSymlinks */
},
DIR_openArchive,
DIR_enumerate,
DIR_openRead,
DIR_openWrite,
DIR_openAppend,
DIR_remove,
DIR_mkdir,
DIR_stat,
DIR_closeArchive
};
/* end of physfs_archiver_dir.c ... */

View File

@ -0,0 +1,116 @@
/*
* GRP support routines for PhysicsFS.
*
* This driver handles BUILD engine archives ("groupfiles"). This format
* (but not this driver) was put together by Ken Silverman.
*
* The format is simple enough. In Ken's words:
*
* What's the .GRP file format?
*
* The ".grp" file format is just a collection of a lot of files stored
* into 1 big one. I tried to make the format as simple as possible: The
* first 12 bytes contains my name, "KenSilverman". The next 4 bytes is
* the number of files that were compacted into the group file. Then for
* each file, there is a 16 byte structure, where the first 12 bytes are
* the filename, and the last 4 bytes are the file's size. The rest of
* the group file is just the raw data packed one after the other in the
* same order as the list of files.
*
* (That info is from http://www.advsys.net/ken/build.htm ...)
*
* Please see the file LICENSE.txt in the source's root directory.
*
* This file written by Ryan C. Gordon.
*/
#define __PHYSICSFS_INTERNAL__
#include "physfs_internal.h"
#if PHYSFS_SUPPORTS_GRP
static int grpLoadEntries(PHYSFS_Io *io, const PHYSFS_uint32 count, void *arc)
{
PHYSFS_uint32 pos = 16 + (16 * count); /* past sig+metadata. */
PHYSFS_uint32 i;
for (i = 0; i < count; i++)
{
char *ptr;
char name[13];
PHYSFS_uint32 size;
BAIL_IF_ERRPASS(!__PHYSFS_readAll(io, name, 12), 0);
BAIL_IF_ERRPASS(!__PHYSFS_readAll(io, &size, 4), 0);
name[12] = '\0'; /* name isn't null-terminated in file. */
if ((ptr = strchr(name, ' ')) != NULL)
*ptr = '\0'; /* trim extra spaces. */
size = PHYSFS_swapULE32(size);
BAIL_IF_ERRPASS(!UNPK_addEntry(arc, name, 0, -1, -1, pos, size), 0);
pos += size;
} /* for */
return 1;
} /* grpLoadEntries */
static void *GRP_openArchive(PHYSFS_Io *io, const char *name,
int forWriting, int *claimed)
{
PHYSFS_uint8 buf[12];
PHYSFS_uint32 count = 0;
void *unpkarc = NULL;
assert(io != NULL); /* shouldn't ever happen. */
BAIL_IF(forWriting, PHYSFS_ERR_READ_ONLY, NULL);
BAIL_IF_ERRPASS(!__PHYSFS_readAll(io, buf, sizeof (buf)), NULL);
if (memcmp(buf, "KenSilverman", sizeof (buf)) != 0)
BAIL(PHYSFS_ERR_UNSUPPORTED, NULL);
*claimed = 1;
BAIL_IF_ERRPASS(!__PHYSFS_readAll(io, &count, sizeof(count)), NULL);
count = PHYSFS_swapULE32(count);
unpkarc = UNPK_openArchive(io, 0, 1);
BAIL_IF_ERRPASS(!unpkarc, NULL);
if (!grpLoadEntries(io, count, unpkarc))
{
UNPK_abandonArchive(unpkarc);
return NULL;
} /* if */
return unpkarc;
} /* GRP_openArchive */
const PHYSFS_Archiver __PHYSFS_Archiver_GRP =
{
CURRENT_PHYSFS_ARCHIVER_API_VERSION,
{
"GRP",
"Build engine Groupfile format",
"Ryan C. Gordon <icculus@icculus.org>",
"https://icculus.org/physfs/",
0, /* supportsSymlinks */
},
GRP_openArchive,
UNPK_enumerate,
UNPK_openRead,
UNPK_openWrite,
UNPK_openAppend,
UNPK_remove,
UNPK_mkdir,
UNPK_stat,
UNPK_closeArchive
};
#endif /* defined PHYSFS_SUPPORTS_GRP */
/* end of physfs_archiver_grp.c ... */

View File

@ -0,0 +1,170 @@
/*
* HOG support routines for PhysicsFS.
*
* This driver handles Descent I/II/III HOG archives.
*
* The Descent I/II format is very simple:
*
* The file always starts with the 3-byte signature "DHF" (Descent
* HOG file). After that the files of a HOG are just attached after
* another, divided by a 17 bytes header, which specifies the name
* and length (in bytes) of the forthcoming file! So you just read
* the header with its information of how big the following file is,
* and then skip exact that number of bytes to get to the next file
* in that HOG.
*
* char sig[3] = {'D', 'H', 'F'}; // "DHF"=Descent HOG File
*
* struct {
* char file_name[13]; // Filename, padded to 13 bytes with 0s
* int file_size; // filesize in bytes
* char data[file_size]; // The file data
* } FILE_STRUCT; // Repeated until the end of the file.
*
* (That info is from http://www.descent2.com/ddn/specs/hog/)
*
* Descent 3 moved to HOG2 format, which starts with the chars "HOG2",
* then 32-bits for the number of contained files, 32 bits for the offset
* to the first file's data, then 56 bytes of 0xFF (reserved?). Then for
* each file, there's 36 bytes for filename (null-terminated, rest of bytes
* are garbage), 32-bits unknown/reserved (always zero?), 32-bits of length
* of file data, 32-bits of time since Unix epoch. Then immediately following,
* for each file is their uncompressed content, you can find its offset
* by starting at the initial data offset and adding the filesize of each
* prior file.
*
* This information was found at:
* https://web.archive.org/web/20020213004051/http://descent-3.com/ddn/specs/hog/
*
*
* Please see the file LICENSE.txt in the source's root directory.
*
* This file written by Bradley Bell and Ryan C. Gordon.
*/
#define __PHYSICSFS_INTERNAL__
#include "physfs_internal.h"
#if PHYSFS_SUPPORTS_HOG
static int readui32(PHYSFS_Io *io, PHYSFS_uint32 *val)
{
PHYSFS_uint32 v;
BAIL_IF_ERRPASS(!__PHYSFS_readAll(io, &v, sizeof (v)), 0);
*val = PHYSFS_swapULE32(v);
return 1;
} /* readui32 */
static int hog1LoadEntries(PHYSFS_Io *io, void *arc)
{
const PHYSFS_uint64 iolen = io->length(io);
PHYSFS_uint32 pos = 3;
while (pos < iolen)
{
PHYSFS_uint32 size;
char name[13];
BAIL_IF_ERRPASS(!__PHYSFS_readAll(io, name, 13), 0);
BAIL_IF_ERRPASS(!readui32(io, &size), 0);
name[12] = '\0'; /* just in case. */
pos += 13 + 4;
BAIL_IF_ERRPASS(!UNPK_addEntry(arc, name, 0, -1, -1, pos, size), 0);
pos += size;
/* skip over entry */
BAIL_IF_ERRPASS(!io->seek(io, pos), 0);
} /* while */
return 1;
} /* hogLoadEntries */
static int hog2LoadEntries(PHYSFS_Io *io, void *arc)
{
PHYSFS_uint32 numfiles;
PHYSFS_uint32 pos;
PHYSFS_uint32 i;
BAIL_IF_ERRPASS(!readui32(io, &numfiles), 0);
BAIL_IF_ERRPASS(!readui32(io, &pos), 0);
BAIL_IF_ERRPASS(!io->seek(io, 68), 0); /* skip to end of header. */
for (i = 0; i < numfiles; i++) {
char name[37];
PHYSFS_uint32 reserved;
PHYSFS_uint32 size;
PHYSFS_uint32 mtime;
BAIL_IF_ERRPASS(!__PHYSFS_readAll(io, name, 36), 0);
BAIL_IF_ERRPASS(!readui32(io, &reserved), 0);
BAIL_IF_ERRPASS(!readui32(io, &size), 0);
BAIL_IF_ERRPASS(!readui32(io, &mtime), 0);
name[36] = '\0'; /* just in case */
BAIL_IF_ERRPASS(!UNPK_addEntry(arc, name, 0, mtime, mtime, pos, size), 0);
pos += size;
}
return 1;
} /* hog2LoadEntries */
static void *HOG_openArchive(PHYSFS_Io *io, const char *name,
int forWriting, int *claimed)
{
PHYSFS_uint8 buf[3];
void *unpkarc = NULL;
int hog1 = 0;
assert(io != NULL); /* shouldn't ever happen. */
BAIL_IF(forWriting, PHYSFS_ERR_READ_ONLY, NULL);
BAIL_IF_ERRPASS(!__PHYSFS_readAll(io, buf, 3), NULL);
if (memcmp(buf, "DHF", 3) == 0)
hog1 = 1; /* original HOG (Descent 1 and 2) archive */
else
{
BAIL_IF(memcmp(buf, "HOG", 3) != 0, PHYSFS_ERR_UNSUPPORTED, NULL); /* Not HOG2 */
BAIL_IF_ERRPASS(!__PHYSFS_readAll(io, buf, 1), NULL);
BAIL_IF(buf[0] != '2', PHYSFS_ERR_UNSUPPORTED, NULL); /* Not HOG2 */
} /* else */
*claimed = 1;
unpkarc = UNPK_openArchive(io, 0, 1);
BAIL_IF_ERRPASS(!unpkarc, NULL);
if (!(hog1 ? hog1LoadEntries(io, unpkarc) : hog2LoadEntries(io, unpkarc)))
{
UNPK_abandonArchive(unpkarc);
return NULL;
} /* if */
return unpkarc;
} /* HOG_openArchive */
const PHYSFS_Archiver __PHYSFS_Archiver_HOG =
{
CURRENT_PHYSFS_ARCHIVER_API_VERSION,
{
"HOG",
"Descent I/II/III HOG file format",
"Bradley Bell <btb@icculus.org>",
"https://icculus.org/physfs/",
0, /* supportsSymlinks */
},
HOG_openArchive,
UNPK_enumerate,
UNPK_openRead,
UNPK_openWrite,
UNPK_openAppend,
UNPK_remove,
UNPK_mkdir,
UNPK_stat,
UNPK_closeArchive
};
#endif /* defined PHYSFS_SUPPORTS_HOG */
/* end of physfs_archiver_hog.c ... */

View File

@ -0,0 +1,387 @@
/*
* ISO9660 support routines for PhysicsFS.
*
* Please see the file LICENSE.txt in the source's root directory.
*
* This file originally written by Christoph Nelles, but was largely
* rewritten by Ryan C. Gordon (so please harass Ryan about bugs and not
* Christoph).
*/
/*
* Handles CD-ROM disk images (and raw CD-ROM devices).
*
* Not supported:
* - Rock Ridge (needed for sparse files, device nodes and symlinks, etc).
* - Non 2048 Sectors
* - TRANS.TBL (maps 8.3 filenames on old discs to long filenames).
* - Multiextents (4gb max file size without it).
* - UDF
*
* Deviations from the standard
* - Ignores mandatory sort order
* - Allows various invalid file names
*
* Problems
* - Ambiguities in the standard
*/
#define __PHYSICSFS_INTERNAL__
#include "physfs_internal.h"
#if PHYSFS_SUPPORTS_ISO9660
#include <time.h>
/* ISO9660 often stores values in both big and little endian formats: little
first, followed by big. While technically there might be different values
in each, we just always use the littleendian ones and swap ourselves. The
fields aren't aligned anyhow, so you have to serialize them in any case
to avoid crashes on many CPU archs in any case. */
static int iso9660LoadEntries(PHYSFS_Io *io, const int joliet,
const char *base, const PHYSFS_uint64 dirstart,
const PHYSFS_uint64 dirend, void *unpkarc);
static int iso9660AddEntry(PHYSFS_Io *io, const int joliet, const int isdir,
const char *base, PHYSFS_uint8 *fname,
const int fnamelen, const PHYSFS_sint64 ts,
const PHYSFS_uint64 pos, const PHYSFS_uint64 len,
void *unpkarc)
{
char *fullpath;
char *fnamecpy;
size_t baselen;
size_t fullpathlen;
void *entry;
int i;
if (fnamelen == 1 && ((fname[0] == 0) || (fname[0] == 1)))
return 1; /* Magic that represents "." and "..", ignore */
BAIL_IF(fnamelen == 0, PHYSFS_ERR_CORRUPT, 0);
assert(fnamelen > 0);
assert(fnamelen <= 255);
BAIL_IF(joliet && (fnamelen % 2), PHYSFS_ERR_CORRUPT, 0);
/* Joliet is UCS-2, so at most UTF-8 will double the byte size */
baselen = strlen(base);
fullpathlen = baselen + (fnamelen * (joliet ? 2 : 1)) + 2;
fullpath = (char *) __PHYSFS_smallAlloc(fullpathlen);
BAIL_IF(!fullpath, PHYSFS_ERR_OUT_OF_MEMORY, 0);
fnamecpy = fullpath;
if (baselen > 0)
{
snprintf(fullpath, fullpathlen, "%s/", base);
fnamecpy += baselen + 1;
fullpathlen -= baselen - 1;
} /* if */
if (joliet)
{
PHYSFS_uint16 *ucs2 = (PHYSFS_uint16 *) fname;
int total = fnamelen / 2;
for (i = 0; i < total; i++)
ucs2[i] = PHYSFS_swapUBE16(ucs2[i]);
ucs2[total] = '\0';
PHYSFS_utf8FromUcs2(ucs2, fnamecpy, fullpathlen);
} /* if */
else
{
for (i = 0; i < fnamelen; i++)
{
/* We assume the filenames are low-ASCII; consider the archive
corrupt if we see something above 127, since we don't know the
encoding. (We can change this later if we find out these exist
and are intended to be, say, latin-1 or UTF-8 encoding). */
BAIL_IF(fname[i] > 127, PHYSFS_ERR_CORRUPT, 0);
fnamecpy[i] = fname[i];
} /* for */
fnamecpy[fnamelen] = '\0';
if (!isdir)
{
/* find last SEPARATOR2 */
char *ptr = strrchr(fnamecpy, ';');
if (ptr && (ptr != fnamecpy))
*(ptr--) = '\0';
else
ptr = fnamecpy + (fnamelen - 1);
/* chop out any trailing '.', as done in all implementations */
if (*ptr == '.')
*ptr = '\0';
} /* if */
} /* else */
entry = UNPK_addEntry(unpkarc, fullpath, isdir, ts, ts, pos, len);
if ((entry) && (isdir))
{
if (!iso9660LoadEntries(io, joliet, fullpath, pos, pos + len, unpkarc))
entry = NULL; /* so we report a failure later. */
} /* if */
__PHYSFS_smallFree(fullpath);
return entry != NULL;
} /* iso9660AddEntry */
static int iso9660LoadEntries(PHYSFS_Io *io, const int joliet,
const char *base, const PHYSFS_uint64 dirstart,
const PHYSFS_uint64 dirend, void *unpkarc)
{
PHYSFS_uint64 readpos = dirstart;
while (1)
{
PHYSFS_uint8 recordlen;
PHYSFS_uint8 extattrlen;
PHYSFS_uint32 extent;
PHYSFS_uint32 datalen;
PHYSFS_uint8 ignore[4];
PHYSFS_uint8 year, month, day, hour, minute, second, offset;
PHYSFS_uint8 flags;
PHYSFS_uint8 fnamelen;
PHYSFS_uint8 fname[256];
PHYSFS_sint64 timestamp;
struct tm t;
int isdir;
int multiextent;
BAIL_IF_ERRPASS(!io->seek(io, readpos), 0);
/* recordlen = 0 -> no more entries or fill entry */
BAIL_IF_ERRPASS(!__PHYSFS_readAll(io, &recordlen, 1), 0);
if (recordlen > 0)
readpos += recordlen; /* ready to seek to next record. */
else
{
PHYSFS_uint64 nextpos;
/* if we are in the last sector of the directory & it's 0 -> end */
if ((dirend - 2048) <= (readpos - 1))
break; /* finished */
/* else skip to the next sector & continue; */
nextpos = (((readpos - 1) / 2048) + 1) * 2048;
/* whoops, can't make forward progress! */
BAIL_IF(nextpos == readpos, PHYSFS_ERR_CORRUPT, 0);
readpos = nextpos;
continue; /* start back at upper loop. */
} /* else */
BAIL_IF_ERRPASS(!__PHYSFS_readAll(io, &extattrlen, 1), 0);
BAIL_IF_ERRPASS(!__PHYSFS_readAll(io, &extent, 4), 0);
BAIL_IF_ERRPASS(!__PHYSFS_readAll(io, ignore, 4), 0); /* extent be */
BAIL_IF_ERRPASS(!__PHYSFS_readAll(io, &datalen, 4), 0);
BAIL_IF_ERRPASS(!__PHYSFS_readAll(io, ignore, 4), 0); /* datalen be */
/* record timestamp */
BAIL_IF_ERRPASS(!__PHYSFS_readAll(io, &year, 1), 0);
BAIL_IF_ERRPASS(!__PHYSFS_readAll(io, &month, 1), 0);
BAIL_IF_ERRPASS(!__PHYSFS_readAll(io, &day, 1), 0);
BAIL_IF_ERRPASS(!__PHYSFS_readAll(io, &hour, 1), 0);
BAIL_IF_ERRPASS(!__PHYSFS_readAll(io, &minute, 1), 0);
BAIL_IF_ERRPASS(!__PHYSFS_readAll(io, &second, 1), 0);
BAIL_IF_ERRPASS(!__PHYSFS_readAll(io, &offset, 1), 0);
BAIL_IF_ERRPASS(!__PHYSFS_readAll(io, &flags, 1), 0);
isdir = (flags & (1 << 1)) != 0;
multiextent = (flags & (1 << 7)) != 0;
BAIL_IF(multiextent, PHYSFS_ERR_UNSUPPORTED, 0); /* !!! FIXME */
BAIL_IF_ERRPASS(!__PHYSFS_readAll(io, ignore, 1), 0); /* unit size */
BAIL_IF_ERRPASS(!__PHYSFS_readAll(io, ignore, 1), 0); /* interleave gap */
BAIL_IF_ERRPASS(!__PHYSFS_readAll(io, ignore, 2), 0); /* seqnum le */
BAIL_IF_ERRPASS(!__PHYSFS_readAll(io, ignore, 2), 0); /* seqnum be */
BAIL_IF_ERRPASS(!__PHYSFS_readAll(io, &fnamelen, 1), 0);
BAIL_IF_ERRPASS(!__PHYSFS_readAll(io, fname, fnamelen), 0);
t.tm_sec = second;
t.tm_min = minute;
t.tm_hour = hour;
t.tm_mday = day;
t.tm_mon = month - 1;
t.tm_year = year;
t.tm_wday = 0;
t.tm_yday = 0;
t.tm_isdst = -1;
timestamp = (PHYSFS_sint64) mktime(&t);
extent += extattrlen; /* skip extended attribute record. */
/* infinite loop, corrupt file? */
BAIL_IF((extent * 2048) == dirstart, PHYSFS_ERR_CORRUPT, 0);
if (!iso9660AddEntry(io, joliet, isdir, base, fname, fnamelen,
timestamp, extent * 2048, datalen, unpkarc))
{
return 0;
} /* if */
} /* while */
return 1;
} /* iso9660LoadEntries */
static int parseVolumeDescriptor(PHYSFS_Io *io, PHYSFS_uint64 *_rootpos,
PHYSFS_uint64 *_rootlen, int *_joliet,
int *_claimed)
{
PHYSFS_uint64 pos = 32768; /* start at the Primary Volume Descriptor */
int found = 0;
int done = 0;
*_joliet = 0;
while (!done)
{
PHYSFS_uint8 type;
PHYSFS_uint8 identifier[5];
PHYSFS_uint8 version;
PHYSFS_uint8 flags;
PHYSFS_uint8 escapeseqs[32];
PHYSFS_uint8 ignore[32];
PHYSFS_uint16 blocksize;
PHYSFS_uint32 extent;
PHYSFS_uint32 datalen;
BAIL_IF_ERRPASS(!io->seek(io, pos), 0);
pos += 2048; /* each volume descriptor is 2048 bytes */
BAIL_IF_ERRPASS(!__PHYSFS_readAll(io, &type, 1), 0);
BAIL_IF_ERRPASS(!__PHYSFS_readAll(io, identifier, 5), 0);
if (memcmp(identifier, "CD001", 5) != 0) /* maybe not an iso? */
{
BAIL_IF(!*_claimed, PHYSFS_ERR_UNSUPPORTED, 0);
continue; /* just skip this one */
} /* if */
*_claimed = 1; /* okay, this is probably an iso. */
BAIL_IF_ERRPASS(!__PHYSFS_readAll(io, &version, 1), 0); /* version */
BAIL_IF(version != 1, PHYSFS_ERR_UNSUPPORTED, 0);
BAIL_IF_ERRPASS(!__PHYSFS_readAll(io, &flags, 1), 0);
BAIL_IF_ERRPASS(!__PHYSFS_readAll(io, ignore, 32), 0); /* system id */
BAIL_IF_ERRPASS(!__PHYSFS_readAll(io, ignore, 32), 0); /* volume id */
BAIL_IF_ERRPASS(!__PHYSFS_readAll(io, ignore, 8), 0); /* reserved */
BAIL_IF_ERRPASS(!__PHYSFS_readAll(io, ignore, 4), 0); /* space le */
BAIL_IF_ERRPASS(!__PHYSFS_readAll(io, ignore, 4), 0); /* space be */
BAIL_IF_ERRPASS(!__PHYSFS_readAll(io, escapeseqs, 32), 0);
BAIL_IF_ERRPASS(!__PHYSFS_readAll(io, ignore, 2), 0); /* setsize le */
BAIL_IF_ERRPASS(!__PHYSFS_readAll(io, ignore, 2), 0); /* setsize be */
BAIL_IF_ERRPASS(!__PHYSFS_readAll(io, ignore, 2), 0); /* seq num le */
BAIL_IF_ERRPASS(!__PHYSFS_readAll(io, ignore, 2), 0); /* seq num be */
BAIL_IF_ERRPASS(!__PHYSFS_readAll(io, &blocksize, 2), 0);
BAIL_IF_ERRPASS(!__PHYSFS_readAll(io, ignore, 2), 0); /* blocklen be */
BAIL_IF_ERRPASS(!__PHYSFS_readAll(io, ignore, 4), 0); /* pthtablen le */
BAIL_IF_ERRPASS(!__PHYSFS_readAll(io, ignore, 4), 0); /* pthtablen be */
BAIL_IF_ERRPASS(!__PHYSFS_readAll(io, ignore, 4), 0); /* pthtabpos le */
BAIL_IF_ERRPASS(!__PHYSFS_readAll(io, ignore, 4), 0); /* optpthtabpos le */
BAIL_IF_ERRPASS(!__PHYSFS_readAll(io, ignore, 4), 0); /* pthtabpos be */
BAIL_IF_ERRPASS(!__PHYSFS_readAll(io, ignore, 4), 0); /* optpthtabpos be */
/* root directory record... */
BAIL_IF_ERRPASS(!__PHYSFS_readAll(io, ignore, 1), 0); /* len */
BAIL_IF_ERRPASS(!__PHYSFS_readAll(io, ignore, 1), 0); /* attr len */
BAIL_IF_ERRPASS(!__PHYSFS_readAll(io, &extent, 4), 0);
BAIL_IF_ERRPASS(!__PHYSFS_readAll(io, ignore, 4), 0); /* extent be */
BAIL_IF_ERRPASS(!__PHYSFS_readAll(io, &datalen, 4), 0);
BAIL_IF_ERRPASS(!__PHYSFS_readAll(io, ignore, 4), 0); /* datalen be */
/* !!! FIXME: deal with this properly. */
blocksize = PHYSFS_swapULE32(blocksize);
BAIL_IF(blocksize && (blocksize != 2048), PHYSFS_ERR_UNSUPPORTED, 0);
switch (type)
{
case 1: /* Primary Volume Descriptor */
case 2: /* Supplementary Volume Descriptor */
if (found < type)
{
*_rootpos = PHYSFS_swapULE32(extent) * 2048;
*_rootlen = PHYSFS_swapULE32(datalen);
found = type;
if (found == 2) /* possible Joliet volume */
{
const PHYSFS_uint8 *s = escapeseqs;
*_joliet = !(flags & 1) &&
(s[0] == 0x25) && (s[1] == 0x2F) &&
((s[2] == 0x40) || (s[2] == 0x43) || (s[2] == 0x45));
} /* if */
} /* if */
break;
case 255: /* type 255 terminates the volume descriptor list */
done = 1;
break;
default:
break; /* skip unknown types. */
} /* switch */
} /* while */
BAIL_IF(!found, PHYSFS_ERR_CORRUPT, 0);
return 1;
} /* parseVolumeDescriptor */
static void *ISO9660_openArchive(PHYSFS_Io *io, const char *filename,
int forWriting, int *claimed)
{
PHYSFS_uint64 rootpos = 0;
PHYSFS_uint64 len = 0;
int joliet = 0;
void *unpkarc = NULL;
assert(io != NULL); /* shouldn't ever happen. */
BAIL_IF(forWriting, PHYSFS_ERR_READ_ONLY, NULL);
if (!parseVolumeDescriptor(io, &rootpos, &len, &joliet, claimed))
return NULL;
/* !!! FIXME: check case_sensitive and only_usascii params for this archive. */
unpkarc = UNPK_openArchive(io, 1, 0);
BAIL_IF_ERRPASS(!unpkarc, NULL);
if (!iso9660LoadEntries(io, joliet, "", rootpos, rootpos + len, unpkarc))
{
UNPK_abandonArchive(unpkarc);
return NULL;
} /* if */
return unpkarc;
} /* ISO9660_openArchive */
const PHYSFS_Archiver __PHYSFS_Archiver_ISO9660 =
{
CURRENT_PHYSFS_ARCHIVER_API_VERSION,
{
"ISO",
"ISO9660 image file",
"Ryan C. Gordon <icculus@icculus.org>",
"https://icculus.org/physfs/",
0, /* supportsSymlinks */
},
ISO9660_openArchive,
UNPK_enumerate,
UNPK_openRead,
UNPK_openWrite,
UNPK_openAppend,
UNPK_remove,
UNPK_mkdir,
UNPK_stat,
UNPK_closeArchive
};
#endif /* defined PHYSFS_SUPPORTS_ISO9660 */
/* end of physfs_archiver_iso9660.c ... */

View File

@ -0,0 +1,110 @@
/*
* MVL support routines for PhysicsFS.
*
* This driver handles Descent II Movielib archives.
*
* The file format of MVL is quite easy...
*
* //MVL File format - Written by Heiko Herrmann
* char sig[4] = {'D','M', 'V', 'L'}; // "DMVL"=Descent MoVie Library
*
* int num_files; // the number of files in this MVL
*
* struct {
* char file_name[13]; // Filename, padded to 13 bytes with 0s
* int file_size; // filesize in bytes
* }DIR_STRUCT[num_files];
*
* struct {
* char data[file_size]; // The file data
* }FILE_STRUCT[num_files];
*
* (That info is from http://www.descent2.com/ddn/specs/mvl/)
*
* Please see the file LICENSE.txt in the source's root directory.
*
* This file written by Bradley Bell.
* Based on grp.c by Ryan C. Gordon.
*/
#define __PHYSICSFS_INTERNAL__
#include "physfs_internal.h"
#if PHYSFS_SUPPORTS_MVL
static int mvlLoadEntries(PHYSFS_Io *io, const PHYSFS_uint32 count, void *arc)
{
PHYSFS_uint32 pos = 8 + (17 * count); /* past sig+metadata. */
PHYSFS_uint32 i;
for (i = 0; i < count; i++)
{
PHYSFS_uint32 size;
char name[13];
BAIL_IF_ERRPASS(!__PHYSFS_readAll(io, name, 13), 0);
BAIL_IF_ERRPASS(!__PHYSFS_readAll(io, &size, 4), 0);
name[12] = '\0'; /* just in case. */
size = PHYSFS_swapULE32(size);
BAIL_IF_ERRPASS(!UNPK_addEntry(arc, name, 0, -1, -1, pos, size), 0);
pos += size;
} /* for */
return 1;
} /* mvlLoadEntries */
static void *MVL_openArchive(PHYSFS_Io *io, const char *name,
int forWriting, int *claimed)
{
PHYSFS_uint8 buf[4];
PHYSFS_uint32 count = 0;
void *unpkarc;
assert(io != NULL); /* shouldn't ever happen. */
BAIL_IF(forWriting, PHYSFS_ERR_READ_ONLY, NULL);
BAIL_IF_ERRPASS(!__PHYSFS_readAll(io, buf, 4), NULL);
BAIL_IF(memcmp(buf, "DMVL", 4) != 0, PHYSFS_ERR_UNSUPPORTED, NULL);
*claimed = 1;
BAIL_IF_ERRPASS(!__PHYSFS_readAll(io, &count, sizeof(count)), NULL);
count = PHYSFS_swapULE32(count);
unpkarc = UNPK_openArchive(io, 0, 1);
BAIL_IF_ERRPASS(!unpkarc, NULL);
if (!mvlLoadEntries(io, count, unpkarc))
{
UNPK_abandonArchive(unpkarc);
return NULL;
} /* if */
return unpkarc;
} /* MVL_openArchive */
const PHYSFS_Archiver __PHYSFS_Archiver_MVL =
{
CURRENT_PHYSFS_ARCHIVER_API_VERSION,
{
"MVL",
"Descent II Movielib format",
"Bradley Bell <btb@icculus.org>",
"https://icculus.org/physfs/",
0, /* supportsSymlinks */
},
MVL_openArchive,
UNPK_enumerate,
UNPK_openRead,
UNPK_openWrite,
UNPK_openAppend,
UNPK_remove,
UNPK_mkdir,
UNPK_stat,
UNPK_closeArchive
};
#endif /* defined PHYSFS_SUPPORTS_MVL */
/* end of physfs_archiver_mvl.c ... */

View File

@ -0,0 +1,127 @@
/*
* QPAK support routines for PhysicsFS.
*
* This archiver handles the archive format utilized by Quake 1 and 2.
* Quake3-based games use the PkZip/Info-Zip format (which our
* physfs_archiver_zip.c handles).
*
* ========================================================================
*
* This format info (in more detail) comes from:
* https://web.archive.org/web/20040209101748/http://debian.fmi.uni-sofia.bg/~sergei/cgsr/docs/pak.txt
*
* Quake PAK Format
*
* Header
* (4 bytes) signature = 'PACK'
* (4 bytes) directory offset
* (4 bytes) directory length
*
* Directory
* (56 bytes) file name
* (4 bytes) file position
* (4 bytes) file length
*
* ========================================================================
*
* Please see the file LICENSE.txt in the source's root directory.
*
* This file written by Ryan C. Gordon.
*/
#define __PHYSICSFS_INTERNAL__
#include "physfs_internal.h"
#if PHYSFS_SUPPORTS_QPAK
#define QPAK_SIG 0x4B434150 /* "PACK" in ASCII. */
static int qpakLoadEntries(PHYSFS_Io *io, const PHYSFS_uint32 count, void *arc)
{
PHYSFS_uint32 i;
for (i = 0; i < count; i++)
{
PHYSFS_uint32 size;
PHYSFS_uint32 pos;
char name[56];
BAIL_IF_ERRPASS(!__PHYSFS_readAll(io, name, 56), 0);
BAIL_IF_ERRPASS(!__PHYSFS_readAll(io, &pos, 4), 0);
BAIL_IF_ERRPASS(!__PHYSFS_readAll(io, &size, 4), 0);
size = PHYSFS_swapULE32(size);
pos = PHYSFS_swapULE32(pos);
BAIL_IF_ERRPASS(!UNPK_addEntry(arc, name, 0, -1, -1, pos, size), 0);
} /* for */
return 1;
} /* qpakLoadEntries */
static void *QPAK_openArchive(PHYSFS_Io *io, const char *name,
int forWriting, int *claimed)
{
PHYSFS_uint32 val = 0;
PHYSFS_uint32 pos = 0;
PHYSFS_uint32 count = 0;
void *unpkarc;
assert(io != NULL); /* shouldn't ever happen. */
BAIL_IF(forWriting, PHYSFS_ERR_READ_ONLY, NULL);
BAIL_IF_ERRPASS(!__PHYSFS_readAll(io, &val, 4), NULL);
if (PHYSFS_swapULE32(val) != QPAK_SIG)
BAIL(PHYSFS_ERR_UNSUPPORTED, NULL);
*claimed = 1;
BAIL_IF_ERRPASS(!__PHYSFS_readAll(io, &val, 4), NULL);
pos = PHYSFS_swapULE32(val); /* directory table offset. */
BAIL_IF_ERRPASS(!__PHYSFS_readAll(io, &val, 4), NULL);
count = PHYSFS_swapULE32(val);
/* corrupted archive? */
BAIL_IF((count % 64) != 0, PHYSFS_ERR_CORRUPT, NULL);
count /= 64;
BAIL_IF_ERRPASS(!io->seek(io, pos), NULL);
/* !!! FIXME: check case_sensitive and only_usascii params for this archive. */
unpkarc = UNPK_openArchive(io, 1, 0);
BAIL_IF_ERRPASS(!unpkarc, NULL);
if (!qpakLoadEntries(io, count, unpkarc))
{
UNPK_abandonArchive(unpkarc);
return NULL;
} /* if */
return unpkarc;
} /* QPAK_openArchive */
const PHYSFS_Archiver __PHYSFS_Archiver_QPAK =
{
CURRENT_PHYSFS_ARCHIVER_API_VERSION,
{
"PAK",
"Quake I/II format",
"Ryan C. Gordon <icculus@icculus.org>",
"https://icculus.org/physfs/",
0, /* supportsSymlinks */
},
QPAK_openArchive,
UNPK_enumerate,
UNPK_openRead,
UNPK_openWrite,
UNPK_openAppend,
UNPK_remove,
UNPK_mkdir,
UNPK_stat,
UNPK_closeArchive
};
#endif /* defined PHYSFS_SUPPORTS_QPAK */
/* end of physfs_archiver_qpak.c ... */

View File

@ -0,0 +1,136 @@
/*
* SLB support routines for PhysicsFS.
*
* This driver handles SLB archives ("slab files"). This uncompressed format
* is used in I-War / Independence War and Independence War: Defiance.
*
* The format begins with four zero bytes (version?), the file count and the
* location of the table of contents. Each ToC entry contains a 64-byte buffer
* containing a zero-terminated filename, the offset of the data, and its size.
* All the filenames begin with the separator character '\'.
*
* Please see the file LICENSE.txt in the source's root directory.
*
* This file written by Aleksi Nurmi, based on the GRP archiver by
* Ryan C. Gordon.
*/
#define __PHYSICSFS_INTERNAL__
#include "physfs_internal.h"
#if PHYSFS_SUPPORTS_SLB
static int slbLoadEntries(PHYSFS_Io *io, const PHYSFS_uint32 count, void *arc)
{
PHYSFS_uint32 i;
for (i = 0; i < count; i++)
{
PHYSFS_uint32 pos;
PHYSFS_uint32 size;
char name[64];
char backslash;
char *ptr;
/* don't include the '\' in the beginning */
BAIL_IF_ERRPASS(!__PHYSFS_readAll(io, &backslash, 1), 0);
BAIL_IF(backslash != '\\', PHYSFS_ERR_CORRUPT, 0);
/* read the rest of the buffer, 63 bytes */
BAIL_IF_ERRPASS(!__PHYSFS_readAll(io, name, 63), 0);
name[63] = '\0'; /* in case the name lacks the null terminator */
/* convert backslashes */
for (ptr = name; *ptr; ptr++)
{
if (*ptr == '\\')
*ptr = '/';
} /* for */
BAIL_IF_ERRPASS(!__PHYSFS_readAll(io, &pos, 4), 0);
pos = PHYSFS_swapULE32(pos);
BAIL_IF_ERRPASS(!__PHYSFS_readAll(io, &size, 4), 0);
size = PHYSFS_swapULE32(size);
BAIL_IF_ERRPASS(!UNPK_addEntry(arc, name, 0, -1, -1, pos, size), 0);
} /* for */
return 1;
} /* slbLoadEntries */
static void *SLB_openArchive(PHYSFS_Io *io, const char *name,
int forWriting, int *claimed)
{
PHYSFS_uint32 version;
PHYSFS_uint32 count;
PHYSFS_uint32 tocPos;
void *unpkarc;
/* There's no identifier on an SLB file, so we assume it's _not_ if the
file count or tocPos is zero. Beyond that, we'll assume it's
bogus/corrupt if the entries' filenames don't start with '\' or the
tocPos is past the end of the file (seek will fail). This probably
covers all meaningful cases where we would accidentally accept a non-SLB
file with this archiver. */
assert(io != NULL); /* shouldn't ever happen. */
BAIL_IF(forWriting, PHYSFS_ERR_READ_ONLY, NULL);
BAIL_IF_ERRPASS(!__PHYSFS_readAll(io, &version, sizeof (version)), NULL);
version = PHYSFS_swapULE32(version);
BAIL_IF(version != 0, PHYSFS_ERR_UNSUPPORTED, NULL);
BAIL_IF_ERRPASS(!__PHYSFS_readAll(io, &count, sizeof (count)), NULL);
count = PHYSFS_swapULE32(count);
BAIL_IF(!count, PHYSFS_ERR_UNSUPPORTED, NULL);
/* offset of the table of contents */
BAIL_IF_ERRPASS(!__PHYSFS_readAll(io, &tocPos, sizeof (tocPos)), NULL);
tocPos = PHYSFS_swapULE32(tocPos);
BAIL_IF(!tocPos, PHYSFS_ERR_UNSUPPORTED, NULL);
/* seek to the table of contents */
BAIL_IF_ERRPASS(!io->seek(io, tocPos), NULL);
/* !!! FIXME: check case_sensitive and only_usascii params for this archive. */
unpkarc = UNPK_openArchive(io, 1, 0);
BAIL_IF_ERRPASS(!unpkarc, NULL);
if (!slbLoadEntries(io, count, unpkarc))
{
UNPK_abandonArchive(unpkarc);
return NULL;
} /* if */
*claimed = 1; /* oh well. */
return unpkarc;
} /* SLB_openArchive */
const PHYSFS_Archiver __PHYSFS_Archiver_SLB =
{
CURRENT_PHYSFS_ARCHIVER_API_VERSION,
{
"SLB",
"I-War / Independence War Slab file",
"Aleksi Nurmi <aleksi.nurmi@gmail.com>",
"https://bitbucket.org/ahnurmi/",
0, /* supportsSymlinks */
},
SLB_openArchive,
UNPK_enumerate,
UNPK_openRead,
UNPK_openWrite,
UNPK_openAppend,
UNPK_remove,
UNPK_mkdir,
UNPK_stat,
UNPK_closeArchive
};
#endif /* defined PHYSFS_SUPPORTS_SLB */
/* end of physfs_archiver_slb.c ... */

View File

@ -0,0 +1,305 @@
/*
* High-level PhysicsFS archiver for simple unpacked file formats.
*
* This is a framework that basic archivers build on top of. It's for simple
* formats that can just hand back a list of files and the offsets of their
* uncompressed data. There are an alarming number of formats like this.
*
* RULES: Archive entries must be uncompressed. Dirs and files allowed, but no
* symlinks, etc. We can relax some of these rules as necessary.
*
* Please see the file LICENSE.txt in the source's root directory.
*
* This file written by Ryan C. Gordon.
*/
#define __PHYSICSFS_INTERNAL__
#include "physfs_internal.h"
typedef struct
{
__PHYSFS_DirTree tree;
PHYSFS_Io *io;
} UNPKinfo;
typedef struct
{
__PHYSFS_DirTreeEntry tree;
PHYSFS_uint64 startPos;
PHYSFS_uint64 size;
PHYSFS_sint64 ctime;
PHYSFS_sint64 mtime;
} UNPKentry;
typedef struct
{
PHYSFS_Io *io;
UNPKentry *entry;
PHYSFS_uint32 curPos;
} UNPKfileinfo;
void UNPK_closeArchive(void *opaque)
{
UNPKinfo *info = ((UNPKinfo *) opaque);
if (info)
{
__PHYSFS_DirTreeDeinit(&info->tree);
if (info->io)
info->io->destroy(info->io);
allocator.Free(info);
} /* if */
} /* UNPK_closeArchive */
void UNPK_abandonArchive(void *opaque)
{
UNPKinfo *info = ((UNPKinfo *) opaque);
if (info)
{
info->io = NULL;
UNPK_closeArchive(info);
} /* if */
} /* UNPK_abandonArchive */
static PHYSFS_sint64 UNPK_read(PHYSFS_Io *io, void *buffer, PHYSFS_uint64 len)
{
UNPKfileinfo *finfo = (UNPKfileinfo *) io->opaque;
const UNPKentry *entry = finfo->entry;
const PHYSFS_uint64 bytesLeft = (PHYSFS_uint64)(entry->size-finfo->curPos);
PHYSFS_sint64 rc;
if (bytesLeft < len)
len = bytesLeft;
rc = finfo->io->read(finfo->io, buffer, len);
if (rc > 0)
finfo->curPos += (PHYSFS_uint32) rc;
return rc;
} /* UNPK_read */
static PHYSFS_sint64 UNPK_write(PHYSFS_Io *io, const void *b, PHYSFS_uint64 len)
{
BAIL(PHYSFS_ERR_READ_ONLY, -1);
} /* UNPK_write */
static PHYSFS_sint64 UNPK_tell(PHYSFS_Io *io)
{
return ((UNPKfileinfo *) io->opaque)->curPos;
} /* UNPK_tell */
static int UNPK_seek(PHYSFS_Io *io, PHYSFS_uint64 offset)
{
UNPKfileinfo *finfo = (UNPKfileinfo *) io->opaque;
const UNPKentry *entry = finfo->entry;
int rc;
BAIL_IF(offset >= entry->size, PHYSFS_ERR_PAST_EOF, 0);
rc = finfo->io->seek(finfo->io, entry->startPos + offset);
if (rc)
finfo->curPos = (PHYSFS_uint32) offset;
return rc;
} /* UNPK_seek */
static PHYSFS_sint64 UNPK_length(PHYSFS_Io *io)
{
const UNPKfileinfo *finfo = (UNPKfileinfo *) io->opaque;
return ((PHYSFS_sint64) finfo->entry->size);
} /* UNPK_length */
static PHYSFS_Io *UNPK_duplicate(PHYSFS_Io *_io)
{
UNPKfileinfo *origfinfo = (UNPKfileinfo *) _io->opaque;
PHYSFS_Io *io = NULL;
PHYSFS_Io *retval = (PHYSFS_Io *) allocator.Malloc(sizeof (PHYSFS_Io));
UNPKfileinfo *finfo = (UNPKfileinfo *) allocator.Malloc(sizeof (UNPKfileinfo));
GOTO_IF(!retval, PHYSFS_ERR_OUT_OF_MEMORY, UNPK_duplicate_failed);
GOTO_IF(!finfo, PHYSFS_ERR_OUT_OF_MEMORY, UNPK_duplicate_failed);
io = origfinfo->io->duplicate(origfinfo->io);
if (!io) goto UNPK_duplicate_failed;
finfo->io = io;
finfo->entry = origfinfo->entry;
finfo->curPos = 0;
memcpy(retval, _io, sizeof (PHYSFS_Io));
retval->opaque = finfo;
return retval;
UNPK_duplicate_failed:
if (finfo != NULL) allocator.Free(finfo);
if (retval != NULL) allocator.Free(retval);
if (io != NULL) io->destroy(io);
return NULL;
} /* UNPK_duplicate */
static int UNPK_flush(PHYSFS_Io *io) { return 1; /* no write support. */ }
static void UNPK_destroy(PHYSFS_Io *io)
{
UNPKfileinfo *finfo = (UNPKfileinfo *) io->opaque;
finfo->io->destroy(finfo->io);
allocator.Free(finfo);
allocator.Free(io);
} /* UNPK_destroy */
static const PHYSFS_Io UNPK_Io =
{
CURRENT_PHYSFS_IO_API_VERSION, NULL,
UNPK_read,
UNPK_write,
UNPK_seek,
UNPK_tell,
UNPK_length,
UNPK_duplicate,
UNPK_flush,
UNPK_destroy
};
static inline UNPKentry *findEntry(UNPKinfo *info, const char *path)
{
return (UNPKentry *) __PHYSFS_DirTreeFind(&info->tree, path);
} /* findEntry */
PHYSFS_Io *UNPK_openRead(void *opaque, const char *name)
{
PHYSFS_Io *retval = NULL;
UNPKinfo *info = (UNPKinfo *) opaque;
UNPKfileinfo *finfo = NULL;
UNPKentry *entry = findEntry(info, name);
BAIL_IF_ERRPASS(!entry, NULL);
BAIL_IF(entry->tree.isdir, PHYSFS_ERR_NOT_A_FILE, NULL);
retval = (PHYSFS_Io *) allocator.Malloc(sizeof (PHYSFS_Io));
GOTO_IF(!retval, PHYSFS_ERR_OUT_OF_MEMORY, UNPK_openRead_failed);
finfo = (UNPKfileinfo *) allocator.Malloc(sizeof (UNPKfileinfo));
GOTO_IF(!finfo, PHYSFS_ERR_OUT_OF_MEMORY, UNPK_openRead_failed);
finfo->io = info->io->duplicate(info->io);
GOTO_IF_ERRPASS(!finfo->io, UNPK_openRead_failed);
if (!finfo->io->seek(finfo->io, entry->startPos))
goto UNPK_openRead_failed;
finfo->curPos = 0;
finfo->entry = entry;
memcpy(retval, &UNPK_Io, sizeof (*retval));
retval->opaque = finfo;
return retval;
UNPK_openRead_failed:
if (finfo != NULL)
{
if (finfo->io != NULL)
finfo->io->destroy(finfo->io);
allocator.Free(finfo);
} /* if */
if (retval != NULL)
allocator.Free(retval);
return NULL;
} /* UNPK_openRead */
PHYSFS_Io *UNPK_openWrite(void *opaque, const char *name)
{
BAIL(PHYSFS_ERR_READ_ONLY, NULL);
} /* UNPK_openWrite */
PHYSFS_Io *UNPK_openAppend(void *opaque, const char *name)
{
BAIL(PHYSFS_ERR_READ_ONLY, NULL);
} /* UNPK_openAppend */
int UNPK_remove(void *opaque, const char *name)
{
BAIL(PHYSFS_ERR_READ_ONLY, 0);
} /* UNPK_remove */
int UNPK_mkdir(void *opaque, const char *name)
{
BAIL(PHYSFS_ERR_READ_ONLY, 0);
} /* UNPK_mkdir */
int UNPK_stat(void *opaque, const char *path, PHYSFS_Stat *stat)
{
UNPKinfo *info = (UNPKinfo *) opaque;
const UNPKentry *entry = findEntry(info, path);
BAIL_IF_ERRPASS(!entry, 0);
if (entry->tree.isdir)
{
stat->filetype = PHYSFS_FILETYPE_DIRECTORY;
stat->filesize = 0;
} /* if */
else
{
stat->filetype = PHYSFS_FILETYPE_REGULAR;
stat->filesize = entry->size;
} /* else */
stat->modtime = entry->mtime;
stat->createtime = entry->ctime;
stat->accesstime = -1;
stat->readonly = 1;
return 1;
} /* UNPK_stat */
void *UNPK_addEntry(void *opaque, char *name, const int isdir,
const PHYSFS_sint64 ctime, const PHYSFS_sint64 mtime,
const PHYSFS_uint64 pos, const PHYSFS_uint64 len)
{
UNPKinfo *info = (UNPKinfo *) opaque;
UNPKentry *entry;
entry = (UNPKentry *) __PHYSFS_DirTreeAdd(&info->tree, name, isdir);
BAIL_IF_ERRPASS(!entry, NULL);
entry->startPos = isdir ? 0 : pos;
entry->size = isdir ? 0 : len;
entry->ctime = ctime;
entry->mtime = mtime;
return entry;
} /* UNPK_addEntry */
void *UNPK_openArchive(PHYSFS_Io *io, const int case_sensitive, const int only_usascii)
{
UNPKinfo *info = (UNPKinfo *) allocator.Malloc(sizeof (UNPKinfo));
BAIL_IF(!info, PHYSFS_ERR_OUT_OF_MEMORY, NULL);
if (!__PHYSFS_DirTreeInit(&info->tree, sizeof (UNPKentry), case_sensitive, only_usascii))
{
allocator.Free(info);
return NULL;
} /* if */
info->io = io;
return info;
} /* UNPK_openArchive */
/* end of physfs_archiver_unpacked.c ... */

View File

@ -0,0 +1,169 @@
/*
* VDF support routines for PhysicsFS.
*
* This driver handles Gothic I/II VDF archives.
* This format (but not this driver) was designed by Piranha Bytes for
* use wih the ZenGin engine.
*
* This file was written by Francesco Bertolaccini, based on the UNPK archiver
* by Ryan C. Gordon and the works of degenerated1123 and Nico Bendlin.
*/
#define __PHYSICSFS_INTERNAL__
#include "physfs_internal.h"
#if PHYSFS_SUPPORTS_VDF
#include <time.h>
#define VDF_COMMENT_LENGTH 256
#define VDF_SIGNATURE_LENGTH 16
#define VDF_ENTRY_NAME_LENGTH 64
#define VDF_ENTRY_DIR 0x80000000
static const char* VDF_SIGNATURE_G1 = "PSVDSC_V2.00\r\n\r\n";
static const char* VDF_SIGNATURE_G2 = "PSVDSC_V2.00\n\r\n\r";
static inline int readui32(PHYSFS_Io *io, PHYSFS_uint32 *val)
{
PHYSFS_uint32 v;
BAIL_IF_ERRPASS(!__PHYSFS_readAll(io, &v, sizeof (v)), 0);
*val = PHYSFS_swapULE32(v);
return 1;
} /* readui32 */
static PHYSFS_sint64 vdfDosTimeToEpoch(const PHYSFS_uint32 dostime)
{
/* VDF stores timestamps as 32bit DOS dates: the seconds are counted in
2-seconds intervals and the years are counted since 1 Jan. 1980 */
struct tm t;
memset(&t, '\0', sizeof (t));
t.tm_year = ((int) ((dostime >> 25) & 0x7F)) + 80; /* 1980 to 1900 */
t.tm_mon = ((int) ((dostime >> 21) & 0xF)) - 1; /* 1-12 to 0-11 */
t.tm_mday = (int) ((dostime >> 16) & 0x1F);
t.tm_hour = (int) ((dostime >> 11) & 0x1F);
t.tm_min = (int) ((dostime >> 5) & 0x3F);
t.tm_sec = ((int) ((dostime >> 0) & 0x1F)) * 2; /* 2 seconds to 1. */
return (PHYSFS_sint64) mktime(&t);
} /* vdfDosTimeToEpoch */
static int vdfLoadEntries(PHYSFS_Io *io, const PHYSFS_uint32 count,
const PHYSFS_sint64 ts, void *arc)
{
PHYSFS_uint32 i;
for (i = 0; i < count; i++)
{
char name[VDF_ENTRY_NAME_LENGTH + 1];
int namei;
PHYSFS_uint32 jump, size, type, attr;
BAIL_IF_ERRPASS(!__PHYSFS_readAll(io, name, sizeof (name) - 1), 0);
BAIL_IF_ERRPASS(!readui32(io, &jump), 0);
BAIL_IF_ERRPASS(!readui32(io, &size), 0);
BAIL_IF_ERRPASS(!readui32(io, &type), 0);
BAIL_IF_ERRPASS(!readui32(io, &attr), 0);
/* Trim whitespace off the end of the filename */
name[VDF_ENTRY_NAME_LENGTH] = '\0'; /* always null-terminated. */
for (namei = VDF_ENTRY_NAME_LENGTH - 1; namei >= 0; namei--)
{
/* We assume the filenames are low-ASCII; consider the archive
corrupt if we see something above 127, since we don't know the
encoding. (We can change this later if we find out these exist
and are intended to be, say, latin-1 or UTF-8 encoding). */
BAIL_IF(((PHYSFS_uint8) name[namei]) > 127, PHYSFS_ERR_CORRUPT, 0);
if (name[namei] == ' ')
name[namei] = '\0';
else
break;
} /* for */
BAIL_IF(!name[0], PHYSFS_ERR_CORRUPT, 0);
if (!(type & VDF_ENTRY_DIR)) {
BAIL_IF_ERRPASS(!UNPK_addEntry(arc, name, 0, ts, ts, jump, size), 0);
}
} /* for */
return 1;
} /* vdfLoadEntries */
static void *VDF_openArchive(PHYSFS_Io *io, const char *name,
int forWriting, int *claimed)
{
PHYSFS_uint8 ignore[16];
PHYSFS_uint8 sig[VDF_SIGNATURE_LENGTH];
PHYSFS_uint32 count, timestamp, version, dataSize, rootCatOffset;
void *unpkarc;
assert(io != NULL); /* shouldn't ever happen. */
BAIL_IF(forWriting, PHYSFS_ERR_READ_ONLY, NULL);
/* skip the 256-byte comment field. */
BAIL_IF_ERRPASS(!io->seek(io, VDF_COMMENT_LENGTH), NULL);
BAIL_IF_ERRPASS(!__PHYSFS_readAll(io, sig, sizeof (sig)), NULL);
if ((memcmp(sig, VDF_SIGNATURE_G1, VDF_SIGNATURE_LENGTH) != 0) &&
(memcmp(sig, VDF_SIGNATURE_G2, VDF_SIGNATURE_LENGTH) != 0))
{
BAIL(PHYSFS_ERR_UNSUPPORTED, NULL);
} /* if */
*claimed = 1;
BAIL_IF_ERRPASS(!readui32(io, &count), NULL);
BAIL_IF_ERRPASS(!__PHYSFS_readAll(io, ignore, 4), NULL); /* numFiles */
BAIL_IF_ERRPASS(!readui32(io, &timestamp), NULL);
BAIL_IF_ERRPASS(!readui32(io, &dataSize), NULL); /* dataSize */
BAIL_IF_ERRPASS(!readui32(io, &rootCatOffset), NULL); /* rootCatOff */
BAIL_IF_ERRPASS(!readui32(io, &version), NULL);
BAIL_IF(version != 0x50, PHYSFS_ERR_UNSUPPORTED, NULL);
BAIL_IF_ERRPASS(!io->seek(io, rootCatOffset), NULL);
/* !!! FIXME: check case_sensitive and only_usascii params for this archive. */
unpkarc = UNPK_openArchive(io, 1, 0);
BAIL_IF_ERRPASS(!unpkarc, NULL);
if (!vdfLoadEntries(io, count, vdfDosTimeToEpoch(timestamp), unpkarc))
{
UNPK_abandonArchive(unpkarc);
return NULL;
} /* if */
return unpkarc;
} /* VDF_openArchive */
const PHYSFS_Archiver __PHYSFS_Archiver_VDF =
{
CURRENT_PHYSFS_ARCHIVER_API_VERSION,
{
"VDF",
"Gothic I/II engine format",
"Francesco Bertolaccini <bertolaccinifrancesco@gmail.com>",
"https://github.com/frabert",
0, /* supportsSymlinks */
},
VDF_openArchive,
UNPK_enumerate,
UNPK_openRead,
UNPK_openWrite,
UNPK_openAppend,
UNPK_remove,
UNPK_mkdir,
UNPK_stat,
UNPK_closeArchive
};
#endif /* defined PHYSFS_SUPPORTS_VDF */
/* end of physfs_archiver_vdf.c ... */

View File

@ -0,0 +1,135 @@
/*
* WAD support routines for PhysicsFS.
*
* This driver handles DOOM engine archives ("wads").
* This format (but not this driver) was designed by id Software for use
* with the DOOM engine.
* The specs of the format are from the unofficial doom specs v1.666
* found here: http://www.gamers.org/dhs/helpdocs/dmsp1666.html
* The format of the archive: (from the specs)
*
* A WAD file has three parts:
* (1) a twelve-byte header
* (2) one or more "lumps"
* (3) a directory or "info table" that contains the names, offsets, and
* sizes of all the lumps in the WAD
*
* The header consists of three four-byte parts:
* (a) an ASCII string which must be either "IWAD" or "PWAD"
* (b) a uint32 which is the number of lumps in the wad
* (c) a uint32 which is the file offset to the start of
* the directory
*
* The directory has one 16-byte entry for every lump. Each entry consists
* of three parts:
*
* (a) a uint32, the file offset to the start of the lump
* (b) a uint32, the size of the lump in bytes
* (c) an 8-byte ASCII string, the name of the lump, padded with zeros.
* For example, the "DEMO1" entry in hexadecimal would be
* (44 45 4D 4F 31 00 00 00)
*
* Note that there is no way to tell if an opened WAD archive is a
* IWAD or PWAD with this archiver.
* I couldn't think of a way to provide that information, without being too
* hacky.
* I don't think it's really that important though.
*
*
* Please see the file LICENSE.txt in the source's root directory.
*
* This file written by Travis Wells, based on the GRP archiver by
* Ryan C. Gordon.
*/
#define __PHYSICSFS_INTERNAL__
#include "physfs_internal.h"
#if PHYSFS_SUPPORTS_WAD
static int wadLoadEntries(PHYSFS_Io *io, const PHYSFS_uint32 count, void *arc)
{
PHYSFS_uint32 i;
for (i = 0; i < count; i++)
{
PHYSFS_uint32 pos;
PHYSFS_uint32 size;
char name[9];
BAIL_IF_ERRPASS(!__PHYSFS_readAll(io, &pos, 4), 0);
BAIL_IF_ERRPASS(!__PHYSFS_readAll(io, &size, 4), 0);
BAIL_IF_ERRPASS(!__PHYSFS_readAll(io, name, 8), 0);
name[8] = '\0'; /* name might not be null-terminated in file. */
size = PHYSFS_swapULE32(size);
pos = PHYSFS_swapULE32(pos);
BAIL_IF_ERRPASS(!UNPK_addEntry(arc, name, 0, -1, -1, pos, size), 0);
} /* for */
return 1;
} /* wadLoadEntries */
static void *WAD_openArchive(PHYSFS_Io *io, const char *name,
int forWriting, int *claimed)
{
PHYSFS_uint8 buf[4];
PHYSFS_uint32 count;
PHYSFS_uint32 directoryOffset;
void *unpkarc;
assert(io != NULL); /* shouldn't ever happen. */
BAIL_IF(forWriting, PHYSFS_ERR_READ_ONLY, NULL);
BAIL_IF_ERRPASS(!__PHYSFS_readAll(io, buf, sizeof (buf)), NULL);
if ((memcmp(buf, "IWAD", 4) != 0) && (memcmp(buf, "PWAD", 4) != 0))
BAIL(PHYSFS_ERR_UNSUPPORTED, NULL);
*claimed = 1;
BAIL_IF_ERRPASS(!__PHYSFS_readAll(io, &count, sizeof (count)), NULL);
count = PHYSFS_swapULE32(count);
BAIL_IF_ERRPASS(!__PHYSFS_readAll(io, &directoryOffset, 4), 0);
directoryOffset = PHYSFS_swapULE32(directoryOffset);
BAIL_IF_ERRPASS(!io->seek(io, directoryOffset), 0);
unpkarc = UNPK_openArchive(io, 0, 1);
BAIL_IF_ERRPASS(!unpkarc, NULL);
if (!wadLoadEntries(io, count, unpkarc))
{
UNPK_abandonArchive(unpkarc);
return NULL;
} /* if */
return unpkarc;
} /* WAD_openArchive */
const PHYSFS_Archiver __PHYSFS_Archiver_WAD =
{
CURRENT_PHYSFS_ARCHIVER_API_VERSION,
{
"WAD",
"DOOM engine format",
"Travis Wells <traviswells@mchsi.com>",
"http://www.3dmm2.com/doom/",
0, /* supportsSymlinks */
},
WAD_openArchive,
UNPK_enumerate,
UNPK_openRead,
UNPK_openWrite,
UNPK_openAppend,
UNPK_remove,
UNPK_mkdir,
UNPK_stat,
UNPK_closeArchive
};
#endif /* defined PHYSFS_SUPPORTS_WAD */
/* end of physfs_archiver_wad.c ... */

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,137 @@
/**
* PhysicsFS; a portable, flexible file i/o abstraction.
*
* Documentation is in physfs.h. It's verbose, honest. :)
*
* Please see the file LICENSE.txt in the source's root directory.
*
* This file written by Ryan C. Gordon.
*/
#define __PHYSICSFS_INTERNAL__
#include "physfs_internal.h"
#ifndef PHYSFS_Swap16
static inline PHYSFS_uint16 PHYSFS_Swap16(PHYSFS_uint16 D)
{
return ((D<<8)|(D>>8));
}
#endif
#ifndef PHYSFS_Swap32
static inline PHYSFS_uint32 PHYSFS_Swap32(PHYSFS_uint32 D)
{
return ((D<<24)|((D<<8)&0x00FF0000)|((D>>8)&0x0000FF00)|(D>>24));
}
#endif
#ifndef PHYSFS_NO_64BIT_SUPPORT
#ifndef PHYSFS_Swap64
static inline PHYSFS_uint64 PHYSFS_Swap64(PHYSFS_uint64 val) {
PHYSFS_uint32 hi, lo;
/* Separate into high and low 32-bit values and swap them */
lo = (PHYSFS_uint32)(val&0xFFFFFFFF);
val >>= 32;
hi = (PHYSFS_uint32)(val&0xFFFFFFFF);
val = PHYSFS_Swap32(lo);
val <<= 32;
val |= PHYSFS_Swap32(hi);
return val;
}
#endif
#else
#ifndef PHYSFS_Swap64
/* This is mainly to keep compilers from complaining in PHYSFS code.
If there is no real 64-bit datatype, then compilers will complain about
the fake 64-bit datatype that PHYSFS provides when it compiles user code.
*/
#define PHYSFS_Swap64(X) (X)
#endif
#endif /* PHYSFS_NO_64BIT_SUPPORT */
/* Byteswap item from the specified endianness to the native endianness */
#if PHYSFS_BYTEORDER == PHYSFS_LIL_ENDIAN
PHYSFS_uint16 PHYSFS_swapULE16(PHYSFS_uint16 x) { return x; }
PHYSFS_sint16 PHYSFS_swapSLE16(PHYSFS_sint16 x) { return x; }
PHYSFS_uint32 PHYSFS_swapULE32(PHYSFS_uint32 x) { return x; }
PHYSFS_sint32 PHYSFS_swapSLE32(PHYSFS_sint32 x) { return x; }
PHYSFS_uint64 PHYSFS_swapULE64(PHYSFS_uint64 x) { return x; }
PHYSFS_sint64 PHYSFS_swapSLE64(PHYSFS_sint64 x) { return x; }
PHYSFS_uint16 PHYSFS_swapUBE16(PHYSFS_uint16 x) { return PHYSFS_Swap16(x); }
PHYSFS_sint16 PHYSFS_swapSBE16(PHYSFS_sint16 x) { return PHYSFS_Swap16(x); }
PHYSFS_uint32 PHYSFS_swapUBE32(PHYSFS_uint32 x) { return PHYSFS_Swap32(x); }
PHYSFS_sint32 PHYSFS_swapSBE32(PHYSFS_sint32 x) { return PHYSFS_Swap32(x); }
PHYSFS_uint64 PHYSFS_swapUBE64(PHYSFS_uint64 x) { return PHYSFS_Swap64(x); }
PHYSFS_sint64 PHYSFS_swapSBE64(PHYSFS_sint64 x) { return PHYSFS_Swap64(x); }
#else
PHYSFS_uint16 PHYSFS_swapULE16(PHYSFS_uint16 x) { return PHYSFS_Swap16(x); }
PHYSFS_sint16 PHYSFS_swapSLE16(PHYSFS_sint16 x) { return PHYSFS_Swap16(x); }
PHYSFS_uint32 PHYSFS_swapULE32(PHYSFS_uint32 x) { return PHYSFS_Swap32(x); }
PHYSFS_sint32 PHYSFS_swapSLE32(PHYSFS_sint32 x) { return PHYSFS_Swap32(x); }
PHYSFS_uint64 PHYSFS_swapULE64(PHYSFS_uint64 x) { return PHYSFS_Swap64(x); }
PHYSFS_sint64 PHYSFS_swapSLE64(PHYSFS_sint64 x) { return PHYSFS_Swap64(x); }
PHYSFS_uint16 PHYSFS_swapUBE16(PHYSFS_uint16 x) { return x; }
PHYSFS_sint16 PHYSFS_swapSBE16(PHYSFS_sint16 x) { return x; }
PHYSFS_uint32 PHYSFS_swapUBE32(PHYSFS_uint32 x) { return x; }
PHYSFS_sint32 PHYSFS_swapSBE32(PHYSFS_sint32 x) { return x; }
PHYSFS_uint64 PHYSFS_swapUBE64(PHYSFS_uint64 x) { return x; }
PHYSFS_sint64 PHYSFS_swapSBE64(PHYSFS_sint64 x) { return x; }
#endif
static inline int readAll(PHYSFS_File *file, void *val, const size_t len)
{
return (PHYSFS_readBytes(file, val, len) == len);
} /* readAll */
#define PHYSFS_BYTEORDER_READ(datatype, swaptype) \
int PHYSFS_read##swaptype(PHYSFS_File *file, PHYSFS_##datatype *val) { \
PHYSFS_##datatype in; \
BAIL_IF(val == NULL, PHYSFS_ERR_INVALID_ARGUMENT, 0); \
BAIL_IF_ERRPASS(!readAll(file, &in, sizeof (in)), 0); \
*val = PHYSFS_swap##swaptype(in); \
return 1; \
}
PHYSFS_BYTEORDER_READ(sint16, SLE16)
PHYSFS_BYTEORDER_READ(uint16, ULE16)
PHYSFS_BYTEORDER_READ(sint16, SBE16)
PHYSFS_BYTEORDER_READ(uint16, UBE16)
PHYSFS_BYTEORDER_READ(sint32, SLE32)
PHYSFS_BYTEORDER_READ(uint32, ULE32)
PHYSFS_BYTEORDER_READ(sint32, SBE32)
PHYSFS_BYTEORDER_READ(uint32, UBE32)
PHYSFS_BYTEORDER_READ(sint64, SLE64)
PHYSFS_BYTEORDER_READ(uint64, ULE64)
PHYSFS_BYTEORDER_READ(sint64, SBE64)
PHYSFS_BYTEORDER_READ(uint64, UBE64)
static inline int writeAll(PHYSFS_File *f, const void *val, const size_t len)
{
return (PHYSFS_writeBytes(f, val, len) == len);
} /* writeAll */
#define PHYSFS_BYTEORDER_WRITE(datatype, swaptype) \
int PHYSFS_write##swaptype(PHYSFS_File *file, PHYSFS_##datatype val) { \
const PHYSFS_##datatype out = PHYSFS_swap##swaptype(val); \
BAIL_IF_ERRPASS(!writeAll(file, &out, sizeof (out)), 0); \
return 1; \
}
PHYSFS_BYTEORDER_WRITE(sint16, SLE16)
PHYSFS_BYTEORDER_WRITE(uint16, ULE16)
PHYSFS_BYTEORDER_WRITE(sint16, SBE16)
PHYSFS_BYTEORDER_WRITE(uint16, UBE16)
PHYSFS_BYTEORDER_WRITE(sint32, SLE32)
PHYSFS_BYTEORDER_WRITE(uint32, ULE32)
PHYSFS_BYTEORDER_WRITE(sint32, SBE32)
PHYSFS_BYTEORDER_WRITE(uint32, UBE32)
PHYSFS_BYTEORDER_WRITE(sint64, SLE64)
PHYSFS_BYTEORDER_WRITE(uint64, ULE64)
PHYSFS_BYTEORDER_WRITE(sint64, SBE64)
PHYSFS_BYTEORDER_WRITE(uint64, UBE64)
/* end of physfs_byteorder.c ... */

File diff suppressed because it is too large Load Diff

776
third-party/physfs/src/physfs_internal.h vendored Normal file
View File

@ -0,0 +1,776 @@
/*
* Internal function/structure declaration. Do NOT include in your
* application.
*
* Please see the file LICENSE.txt in the source's root directory.
*
* This file written by Ryan C. Gordon.
*/
#ifndef _INCLUDE_PHYSFS_INTERNAL_H_
#define _INCLUDE_PHYSFS_INTERNAL_H_
#ifndef __PHYSICSFS_INTERNAL__
#error Do not include this header from your applications.
#endif
/* Turn off MSVC warnings that are aggressively anti-portability. */
#if defined(_MSC_VER) && !defined(_CRT_SECURE_NO_WARNINGS)
#define _CRT_SECURE_NO_WARNINGS 1
#endif
#include "physfs.h"
/* The holy trinity. */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "physfs_platforms.h"
#include <assert.h>
#define __PHYSFS_COMPILE_TIME_ASSERT(name, x) \
typedef int __PHYSFS_compile_time_assert_##name[(x) * 2 - 1]
/* !!! FIXME: remove this when revamping stack allocation code... */
#if defined(_MSC_VER) || defined(__MINGW32__) || defined(__WATCOMC__)
#include <malloc.h>
#endif
#if defined(PHYSFS_PLATFORM_SOLARIS) || defined(PHYSFS_PLATFORM_LINUX)
#include <alloca.h>
#endif
#ifdef __cplusplus
extern "C" {
#endif
#ifdef __GNUC__
#define PHYSFS_MINIMUM_GCC_VERSION(major, minor) \
( ((__GNUC__ << 16) + __GNUC_MINOR__) >= (((major) << 16) + (minor)) )
#else
#define PHYSFS_MINIMUM_GCC_VERSION(major, minor) (0)
#endif
#ifdef __cplusplus
/* C++ always has a real inline keyword. */
#elif (defined macintosh) && !(defined __MWERKS__)
# define inline
#elif (defined _MSC_VER)
# define inline __inline
#endif
#if defined(PHYSFS_PLATFORM_LINUX) && !defined(_FILE_OFFSET_BITS)
#define _FILE_OFFSET_BITS 64
#endif
/* All public APIs need to be in physfs.h with a PHYSFS_DECL.
All file-private symbols need to be marked "static".
Everything shared between PhysicsFS sources needs to be in this
file between the visibility pragma blocks. */
#if !defined(_WIN32) && (PHYSFS_MINIMUM_GCC_VERSION(4,0) || defined(__clang__))
#define PHYSFS_HAVE_PRAGMA_VISIBILITY 1
#endif
#if PHYSFS_HAVE_PRAGMA_VISIBILITY
#pragma GCC visibility push(hidden)
#endif
/* These are the build-in archivers. We list them all as "extern" here without
#ifdefs to keep it tidy, but obviously you need to make sure these are
wrapped in PHYSFS_SUPPORTS_* checks before actually referencing them. */
extern const PHYSFS_Archiver __PHYSFS_Archiver_DIR;
extern const PHYSFS_Archiver __PHYSFS_Archiver_ZIP;
extern const PHYSFS_Archiver __PHYSFS_Archiver_7Z;
extern const PHYSFS_Archiver __PHYSFS_Archiver_GRP;
extern const PHYSFS_Archiver __PHYSFS_Archiver_QPAK;
extern const PHYSFS_Archiver __PHYSFS_Archiver_HOG;
extern const PHYSFS_Archiver __PHYSFS_Archiver_MVL;
extern const PHYSFS_Archiver __PHYSFS_Archiver_WAD;
extern const PHYSFS_Archiver __PHYSFS_Archiver_SLB;
extern const PHYSFS_Archiver __PHYSFS_Archiver_ISO9660;
extern const PHYSFS_Archiver __PHYSFS_Archiver_VDF;
/* a real C99-compliant snprintf() is in Visual Studio 2015,
but just use this everywhere for binary compatibility. */
#if defined(_MSC_VER)
#include <stdarg.h>
int __PHYSFS_msvc_vsnprintf(char *outBuf, size_t size, const char *format, va_list ap);
int __PHYSFS_msvc_snprintf(char *outBuf, size_t size, const char *format, ...);
#define vsnprintf __PHYSFS_msvc_vsnprintf
#define snprintf __PHYSFS_msvc_snprintf
#endif
/* Some simple wrappers around WinRT C++ interfaces we can call from C. */
#ifdef PHYSFS_PLATFORM_WINRT
const void *__PHYSFS_winrtCalcBaseDir(void);
const void *__PHYSFS_winrtCalcPrefDir(void);
#endif
/* atomic operations. */
/* increment/decrement operations return the final incremented/decremented value. */
#if defined(_MSC_VER) && (_MSC_VER >= 1500)
#include <intrin.h>
__PHYSFS_COMPILE_TIME_ASSERT(LongEqualsInt, sizeof (int) == sizeof (long));
#define __PHYSFS_ATOMIC_INCR(ptrval) _InterlockedIncrement((long*)(ptrval))
#define __PHYSFS_ATOMIC_DECR(ptrval) _InterlockedDecrement((long*)(ptrval))
#elif defined(__clang__) || (defined(__GNUC__) && (((__GNUC__ * 10000) + (__GNUC_MINOR__ * 100)) >= 40100))
#define __PHYSFS_ATOMIC_INCR(ptrval) __sync_add_and_fetch(ptrval, 1)
#define __PHYSFS_ATOMIC_DECR(ptrval) __sync_add_and_fetch(ptrval, -1)
#elif defined(__WATCOMC__) && defined(__386__)
extern __inline int _xadd_watcom(volatile int *a, int v);
#pragma aux _xadd_watcom = \
"lock xadd [ecx], eax" \
parm [ecx] [eax] \
value [eax] \
modify exact [eax];
#define __PHYSFS_ATOMIC_INCR(ptrval) (_xadd_watcom(ptrval, 1)+1)
#define __PHYSFS_ATOMIC_DECR(ptrval) (_xadd_watcom(ptrval, -1)-1)
#else
#define PHYSFS_NEED_ATOMIC_OP_FALLBACK 1
int __PHYSFS_ATOMIC_INCR(int *ptrval);
int __PHYSFS_ATOMIC_DECR(int *ptrval);
#endif
/*
* Interface for small allocations. If you need a little scratch space for
* a throwaway buffer or string, use this. It will make small allocations
* on the stack if possible, and use allocator.Malloc() if they are too
* large. This helps reduce malloc pressure.
* There are some rules, though:
* NEVER return a pointer from this, as stack-allocated buffers go away
* when your function returns.
* NEVER allocate in a loop, as stack-allocated pointers will pile up. Call
* a function that uses smallAlloc from your loop, so the allocation can
* free each time.
* NEVER call smallAlloc with any complex expression (it's a macro that WILL
* have side effects...it references the argument multiple times). Use a
* variable or a literal.
* NEVER free a pointer from this with anything but smallFree. It will not
* be a valid pointer to the allocator, regardless of where the memory came
* from.
* NEVER realloc a pointer from this.
* NEVER forget to use smallFree: it may not be a pointer from the stack.
* NEVER forget to check for NULL...allocation can fail here, of course!
*/
#define __PHYSFS_SMALLALLOCTHRESHOLD 256
void *__PHYSFS_initSmallAlloc(void *ptr, const size_t len);
#define __PHYSFS_smallAlloc(bytes) ( \
__PHYSFS_initSmallAlloc( \
(((bytes) < __PHYSFS_SMALLALLOCTHRESHOLD) ? \
alloca((size_t)((bytes)+sizeof(void*))) : NULL), (bytes)) \
)
void __PHYSFS_smallFree(void *ptr);
/* Use the allocation hooks. */
#define malloc(x) Do not use malloc() directly.
#define realloc(x, y) Do not use realloc() directly.
#define free(x) Do not use free() directly.
/* !!! FIXME: add alloca check here. */
/* by default, enable things, so builds can opt out of a few things they
want to avoid. But you can build with this #defined to 0 if you would
like to turn off everything except a handful of things you opt into. */
#ifndef PHYSFS_SUPPORTS_DEFAULT
#define PHYSFS_SUPPORTS_DEFAULT 1
#endif
#ifndef PHYSFS_SUPPORTS_ZIP
#define PHYSFS_SUPPORTS_ZIP PHYSFS_SUPPORTS_DEFAULT
#endif
#ifndef PHYSFS_SUPPORTS_7Z
#define PHYSFS_SUPPORTS_7Z PHYSFS_SUPPORTS_DEFAULT
#endif
#ifndef PHYSFS_SUPPORTS_GRP
#define PHYSFS_SUPPORTS_GRP PHYSFS_SUPPORTS_DEFAULT
#endif
#ifndef PHYSFS_SUPPORTS_HOG
#define PHYSFS_SUPPORTS_HOG PHYSFS_SUPPORTS_DEFAULT
#endif
#ifndef PHYSFS_SUPPORTS_MVL
#define PHYSFS_SUPPORTS_MVL PHYSFS_SUPPORTS_DEFAULT
#endif
#ifndef PHYSFS_SUPPORTS_WAD
#define PHYSFS_SUPPORTS_WAD PHYSFS_SUPPORTS_DEFAULT
#endif
#ifndef PHYSFS_SUPPORTS_QPAK
#define PHYSFS_SUPPORTS_QPAK PHYSFS_SUPPORTS_DEFAULT
#endif
#ifndef PHYSFS_SUPPORTS_SLB
#define PHYSFS_SUPPORTS_SLB PHYSFS_SUPPORTS_DEFAULT
#endif
#ifndef PHYSFS_SUPPORTS_ISO9660
#define PHYSFS_SUPPORTS_ISO9660 PHYSFS_SUPPORTS_DEFAULT
#endif
#ifndef PHYSFS_SUPPORTS_VDF
#define PHYSFS_SUPPORTS_VDF PHYSFS_SUPPORTS_DEFAULT
#endif
#if PHYSFS_SUPPORTS_7Z
/* 7zip support needs a global init function called at startup (no deinit). */
extern void SZIP_global_init(void);
#endif
/* The latest supported PHYSFS_Io::version value. */
#define CURRENT_PHYSFS_IO_API_VERSION 0
/* The latest supported PHYSFS_Archiver::version value. */
#define CURRENT_PHYSFS_ARCHIVER_API_VERSION 0
/* This byteorder stuff was lifted from SDL. https://www.libsdl.org/ */
#define PHYSFS_LIL_ENDIAN 1234
#define PHYSFS_BIG_ENDIAN 4321
#ifdef __linux__
#include <endian.h>
#define PHYSFS_BYTEORDER __BYTE_ORDER
#elif defined(__OpenBSD__) || defined(__DragonFly__)
#include <endian.h>
#define PHYSFS_BYTEORDER BYTE_ORDER
#elif defined(__FreeBSD__) || defined(__NetBSD__)
#include <sys/endian.h>
#define PHYSFS_BYTEORDER BYTE_ORDER
/* predefs from newer gcc and clang versions: */
#elif defined(__ORDER_LITTLE_ENDIAN__) && defined(__ORDER_BIG_ENDIAN__) && defined(__BYTE_ORDER__)
#if (__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__)
#define PHYSFS_BYTEORDER PHYSFS_LIL_ENDIAN
#elif (__BYTE_ORDER__ == __ORDER_BIG_ENDIAN__)
#define PHYSFS_BYTEORDER PHYSFS_BIG_ENDIAN
#else
#error Unsupported endianness
#endif /**/
#else
#if defined(__hppa__) || \
defined(__m68k__) || defined(mc68000) || defined(_M_M68K) || \
(defined(__MIPS__) && defined(__MIPSEB__)) || \
defined(__ppc__) || defined(__POWERPC__) || defined(__powerpc__) || defined(__PPC__) || \
defined(__sparc__)
#define PHYSFS_BYTEORDER PHYSFS_BIG_ENDIAN
#else
#define PHYSFS_BYTEORDER PHYSFS_LIL_ENDIAN
#endif
#endif /* __linux__ */
/*
* When sorting the entries in an archive, we use a modified QuickSort.
* When there are less then PHYSFS_QUICKSORT_THRESHOLD entries left to sort,
* we switch over to a BubbleSort for the remainder. Tweak to taste.
*
* You can override this setting by defining PHYSFS_QUICKSORT_THRESHOLD
* before #including "physfs_internal.h".
*/
#ifndef PHYSFS_QUICKSORT_THRESHOLD
#define PHYSFS_QUICKSORT_THRESHOLD 4
#endif
/*
* Sort an array (or whatever) of (max) elements. This uses a mixture of
* a QuickSort and BubbleSort internally.
* (cmpfn) is used to determine ordering, and (swapfn) does the actual
* swapping of elements in the list.
*/
void __PHYSFS_sort(void *entries, size_t max,
int (*cmpfn)(void *, size_t, size_t),
void (*swapfn)(void *, size_t, size_t));
/* These get used all over for lessening code clutter. */
/* "ERRPASS" means "something else just set the error state for us" and is
just to make it clear where the responsibility for the error state lays. */
#define BAIL(e, r) do { if (e) PHYSFS_setErrorCode(e); return r; } while (0)
#define BAIL_ERRPASS(r) do { return r; } while (0)
#define BAIL_IF(c, e, r) do { if (c) { if (e) PHYSFS_setErrorCode(e); return r; } } while (0)
#define BAIL_IF_ERRPASS(c, r) do { if (c) { return r; } } while (0)
#define BAIL_MUTEX(e, m, r) do { if (e) PHYSFS_setErrorCode(e); __PHYSFS_platformReleaseMutex(m); return r; } while (0)
#define BAIL_MUTEX_ERRPASS(m, r) do { __PHYSFS_platformReleaseMutex(m); return r; } while (0)
#define BAIL_IF_MUTEX(c, e, m, r) do { if (c) { if (e) PHYSFS_setErrorCode(e); __PHYSFS_platformReleaseMutex(m); return r; } } while (0)
#define BAIL_IF_MUTEX_ERRPASS(c, m, r) do { if (c) { __PHYSFS_platformReleaseMutex(m); return r; } } while (0)
#define GOTO(e, g) do { if (e) PHYSFS_setErrorCode(e); goto g; } while (0)
#define GOTO_ERRPASS(g) do { goto g; } while (0)
#define GOTO_IF(c, e, g) do { if (c) { if (e) PHYSFS_setErrorCode(e); goto g; } } while (0)
#define GOTO_IF_ERRPASS(c, g) do { if (c) { goto g; } } while (0)
#define GOTO_MUTEX(e, m, g) do { if (e) PHYSFS_setErrorCode(e); __PHYSFS_platformReleaseMutex(m); goto g; } while (0)
#define GOTO_MUTEX_ERRPASS(m, g) do { __PHYSFS_platformReleaseMutex(m); goto g; } while (0)
#define GOTO_IF_MUTEX(c, e, m, g) do { if (c) { if (e) PHYSFS_setErrorCode(e); __PHYSFS_platformReleaseMutex(m); goto g; } } while (0)
#define GOTO_IF_MUTEX_ERRPASS(c, m, g) do { if (c) { __PHYSFS_platformReleaseMutex(m); goto g; } } while (0)
#define __PHYSFS_ARRAYLEN(x) ( (sizeof (x)) / (sizeof (x[0])) )
#ifdef PHYSFS_NO_64BIT_SUPPORT
#define __PHYSFS_SI64(x) ((PHYSFS_sint64) (x))
#define __PHYSFS_UI64(x) ((PHYSFS_uint64) (x))
#elif (defined __GNUC__)
#define __PHYSFS_SI64(x) x##LL
#define __PHYSFS_UI64(x) x##ULL
#elif (defined _MSC_VER)
#define __PHYSFS_SI64(x) x##i64
#define __PHYSFS_UI64(x) x##ui64
#else
#define __PHYSFS_SI64(x) ((PHYSFS_sint64) (x))
#define __PHYSFS_UI64(x) ((PHYSFS_uint64) (x))
#endif
/*
* Check if a ui64 will fit in the platform's address space.
* The initial sizeof check will optimize this macro out entirely on
* 64-bit (and larger?!) platforms, and the other condition will
* return zero or non-zero if the variable will fit in the platform's
* size_t, suitable to pass to malloc. This is kinda messy, but effective.
*/
#define __PHYSFS_ui64FitsAddressSpace(s) ( \
(sizeof (PHYSFS_uint64) <= sizeof (size_t)) || \
((s) < (__PHYSFS_UI64(0xFFFFFFFFFFFFFFFF) >> (64-(sizeof(size_t)*8)))) \
)
/*
* Like strdup(), but uses the current PhysicsFS allocator.
*/
char *__PHYSFS_strdup(const char *str);
/*
* Give a hash value for a C string (uses djb's xor hashing algorithm).
*/
PHYSFS_uint32 __PHYSFS_hashString(const char *str);
/*
* Give a hash value for a C string (uses djb's xor hashing algorithm), case folding as it goes.
*/
PHYSFS_uint32 __PHYSFS_hashStringCaseFold(const char *str);
/*
* Give a hash value for a C string (uses djb's xor hashing algorithm), case folding as it goes,
* assuming that this is only US-ASCII chars (one byte per char, only 'A' through 'Z' need folding).
*/
PHYSFS_uint32 __PHYSFS_hashStringCaseFoldUSAscii(const char *str);
/*
* The current allocator. Not valid before PHYSFS_init is called!
*/
extern PHYSFS_Allocator __PHYSFS_AllocatorHooks;
/* convenience macro to make this less cumbersome internally... */
#define allocator __PHYSFS_AllocatorHooks
/*
* Create a PHYSFS_Io for a file in the physical filesystem.
* This path is in platform-dependent notation. (mode) must be 'r', 'w', or
* 'a' for Read, Write, or Append.
*/
PHYSFS_Io *__PHYSFS_createNativeIo(const char *path, const int mode);
/*
* Create a PHYSFS_Io for a buffer of memory (READ-ONLY). If you already
* have one of these, just use its duplicate() method, and it'll increment
* its refcount without allocating a copy of the buffer.
*/
PHYSFS_Io *__PHYSFS_createMemoryIo(const void *buf, PHYSFS_uint64 len,
void (*destruct)(void *));
/*
* Read (len) bytes from (io) into (buf). Returns non-zero on success,
* zero on i/o error. Literally: "return (io->read(io, buf, len) == len);"
*/
int __PHYSFS_readAll(PHYSFS_Io *io, void *buf, const size_t len);
/* These are shared between some archivers. */
/* LOTS of legacy formats that only use US ASCII, not actually UTF-8, so let them optimize here. */
void *UNPK_openArchive(PHYSFS_Io *io, const int case_sensitive, const int only_usascii);
void UNPK_abandonArchive(void *opaque);
void UNPK_closeArchive(void *opaque);
void *UNPK_addEntry(void *opaque, char *name, const int isdir,
const PHYSFS_sint64 ctime, const PHYSFS_sint64 mtime,
const PHYSFS_uint64 pos, const PHYSFS_uint64 len);
PHYSFS_Io *UNPK_openRead(void *opaque, const char *name);
PHYSFS_Io *UNPK_openWrite(void *opaque, const char *name);
PHYSFS_Io *UNPK_openAppend(void *opaque, const char *name);
int UNPK_remove(void *opaque, const char *name);
int UNPK_mkdir(void *opaque, const char *name);
int UNPK_stat(void *opaque, const char *fn, PHYSFS_Stat *st);
#define UNPK_enumerate __PHYSFS_DirTreeEnumerate
/* Optional API many archivers use this to manage their directory tree. */
/* !!! FIXME: document this better. */
typedef struct __PHYSFS_DirTreeEntry
{
char *name; /* Full path in archive. */
struct __PHYSFS_DirTreeEntry *hashnext; /* next item in hash bucket. */
struct __PHYSFS_DirTreeEntry *children; /* linked list of kids, if dir. */
struct __PHYSFS_DirTreeEntry *sibling; /* next item in same dir. */
int isdir;
} __PHYSFS_DirTreeEntry;
typedef struct __PHYSFS_DirTree
{
__PHYSFS_DirTreeEntry *root; /* root of directory tree. */
__PHYSFS_DirTreeEntry **hash; /* all entries hashed for fast lookup. */
size_t hashBuckets; /* number of buckets in hash. */
size_t entrylen; /* size in bytes of entries (including subclass). */
int case_sensitive; /* non-zero to treat entries as case-sensitive in DirTreeFind */
int only_usascii; /* non-zero to treat paths as US ASCII only (one byte per char, only 'A' through 'Z' are considered for case folding). */
} __PHYSFS_DirTree;
/* LOTS of legacy formats that only use US ASCII, not actually UTF-8, so let them optimize here. */
int __PHYSFS_DirTreeInit(__PHYSFS_DirTree *dt, const size_t entrylen, const int case_sensitive, const int only_usascii);
void *__PHYSFS_DirTreeAdd(__PHYSFS_DirTree *dt, char *name, const int isdir);
void *__PHYSFS_DirTreeFind(__PHYSFS_DirTree *dt, const char *path);
PHYSFS_EnumerateCallbackResult __PHYSFS_DirTreeEnumerate(void *opaque,
const char *dname, PHYSFS_EnumerateCallback cb,
const char *origdir, void *callbackdata);
void __PHYSFS_DirTreeDeinit(__PHYSFS_DirTree *dt);
/*--------------------------------------------------------------------------*/
/*--------------------------------------------------------------------------*/
/*------------ ----------------*/
/*------------ You MUST implement the following functions ----------------*/
/*------------ if porting to a new platform. ----------------*/
/*------------ (see platform/unix.c for an example) ----------------*/
/*------------ ----------------*/
/*--------------------------------------------------------------------------*/
/*--------------------------------------------------------------------------*/
/*
* The dir separator; '/' on unix, '\\' on win32, ":" on MacOS, etc...
* Obviously, this isn't a function. If you need more than one char for this,
* you'll need to pull some old pieces of PhysicsFS out of revision control.
*/
#if defined(PHYSFS_PLATFORM_WINDOWS) || defined(PHYSFS_PLATFORM_OS2)
#define __PHYSFS_platformDirSeparator '\\'
#else
#define __PHYSFS_STANDARD_DIRSEP 1
#define __PHYSFS_platformDirSeparator '/'
#endif
/*
* Initialize the platform. This is called when PHYSFS_init() is called from
* the application.
*
* Return zero if there was a catastrophic failure (which prevents you from
* functioning at all), and non-zero otherwise.
*/
int __PHYSFS_platformInit(void);
/*
* Deinitialize the platform. This is called when PHYSFS_deinit() is called
* from the application. You can use this to clean up anything you've
* allocated in your platform driver.
*/
void __PHYSFS_platformDeinit(void);
/*
* Open a file for reading. (filename) is in platform-dependent notation. The
* file pointer should be positioned on the first byte of the file.
*
* The return value will be some platform-specific datatype that is opaque to
* the caller; it could be a (FILE *) under Unix, or a (HANDLE *) under win32.
*
* The same file can be opened for read multiple times, and each should have
* a unique file handle; this is frequently employed to prevent race
* conditions in the archivers.
*
* Call PHYSFS_setErrorCode() and return (NULL) if the file can't be opened.
*/
void *__PHYSFS_platformOpenRead(const char *filename);
/*
* Open a file for writing. (filename) is in platform-dependent notation. If
* the file exists, it should be truncated to zero bytes, and if it doesn't
* exist, it should be created as a zero-byte file. The file pointer should
* be positioned on the first byte of the file.
*
* The return value will be some platform-specific datatype that is opaque to
* the caller; it could be a (FILE *) under Unix, or a (HANDLE *) under win32,
* etc.
*
* Opening a file for write multiple times has undefined results.
*
* Call PHYSFS_setErrorCode() and return (NULL) if the file can't be opened.
*/
void *__PHYSFS_platformOpenWrite(const char *filename);
/*
* Open a file for appending. (filename) is in platform-dependent notation. If
* the file exists, the file pointer should be place just past the end of the
* file, so that the first write will be one byte after the current end of
* the file. If the file doesn't exist, it should be created as a zero-byte
* file. The file pointer should be positioned on the first byte of the file.
*
* The return value will be some platform-specific datatype that is opaque to
* the caller; it could be a (FILE *) under Unix, or a (HANDLE *) under win32,
* etc.
*
* Opening a file for append multiple times has undefined results.
*
* Call PHYSFS_setErrorCode() and return (NULL) if the file can't be opened.
*/
void *__PHYSFS_platformOpenAppend(const char *filename);
/*
* Read more data from a platform-specific file handle. (opaque) should be
* cast to whatever data type your platform uses. Read a maximum of (len)
* 8-bit bytes to the area pointed to by (buf). If there isn't enough data
* available, return the number of bytes read, and position the file pointer
* immediately after those bytes.
* On success, return (len) and position the file pointer immediately past
* the end of the last read byte. Return (-1) if there is a catastrophic
* error, and call PHYSFS_setErrorCode() to describe the problem; the file
* pointer should not move in such a case. A partial read is success; only
* return (-1) on total failure; presumably, the next read call after a
* partial read will fail as such.
*/
PHYSFS_sint64 __PHYSFS_platformRead(void *opaque, void *buf, PHYSFS_uint64 len);
/*
* Write more data to a platform-specific file handle. (opaque) should be
* cast to whatever data type your platform uses. Write a maximum of (len)
* 8-bit bytes from the area pointed to by (buffer). If there is a problem,
* return the number of bytes written, and position the file pointer
* immediately after those bytes. Return (-1) if there is a catastrophic
* error, and call PHYSFS_setErrorCode() to describe the problem; the file
* pointer should not move in such a case. A partial write is success; only
* return (-1) on total failure; presumably, the next write call after a
* partial write will fail as such.
*/
PHYSFS_sint64 __PHYSFS_platformWrite(void *opaque, const void *buffer,
PHYSFS_uint64 len);
/*
* Set the file pointer to a new position. (opaque) should be cast to
* whatever data type your platform uses. (pos) specifies the number
* of 8-bit bytes to seek to from the start of the file. Seeking past the
* end of the file is an error condition, and you should check for it.
*
* Not all file types can seek; this is to be expected by the caller.
*
* On error, call PHYSFS_setErrorCode() and return zero. On success, return
* a non-zero value.
*/
int __PHYSFS_platformSeek(void *opaque, PHYSFS_uint64 pos);
/*
* Get the file pointer's position, in an 8-bit byte offset from the start of
* the file. (opaque) should be cast to whatever data type your platform
* uses.
*
* Not all file types can "tell"; this is to be expected by the caller.
*
* On error, call PHYSFS_setErrorCode() and return -1. On success, return >= 0.
*/
PHYSFS_sint64 __PHYSFS_platformTell(void *opaque);
/*
* Determine the current size of a file, in 8-bit bytes, from an open file.
*
* The caller expects that this information may not be available for all
* file types on all platforms.
*
* Return -1 if you can't do it, and call PHYSFS_setErrorCode(). Otherwise,
* return the file length in 8-bit bytes.
*/
PHYSFS_sint64 __PHYSFS_platformFileLength(void *handle);
/*
* Read filesystem metadata for a specific path.
*
* This needs to fill in all the fields of (stat). For fields that might not
* mean anything on a platform (access time, perhaps), choose a reasonable
* default. if (follow), we want to follow symlinks and stat what they
* link to and not the link itself.
*
* Return zero on failure, non-zero on success.
*/
int __PHYSFS_platformStat(const char *fn, PHYSFS_Stat *stat, const int follow);
/*
* Flush any pending writes to disk. (opaque) should be cast to whatever data
* type your platform uses. Be sure to check for errors; the caller expects
* that this function can fail if there was a flushing error, etc.
*
* Return zero on failure, non-zero on success.
*/
int __PHYSFS_platformFlush(void *opaque);
/*
* Close file and deallocate resources. (opaque) should be cast to whatever
* data type your platform uses. This should close the file in any scenario:
* flushing is a separate function call, and this function should never fail.
*
* You should clean up all resources associated with (opaque); the pointer
* will be considered invalid after this call.
*/
void __PHYSFS_platformClose(void *opaque);
/*
* Platform implementation of PHYSFS_getCdRomDirsCallback()...
* CD directories are discovered and reported to the callback one at a time.
* Pointers passed to the callback are assumed to be invalid to the
* application after the callback returns, so you can free them or whatever.
* Callback does not assume results will be sorted in any meaningful way.
*/
void __PHYSFS_platformDetectAvailableCDs(PHYSFS_StringCallback cb, void *data);
/*
* Calculate the base dir, if your platform needs special consideration.
* Just return NULL if the standard routines will suffice. (see
* calculateBaseDir() in physfs.c ...)
* Your string must end with a dir separator if you don't return NULL.
* Caller will allocator.Free() the retval if it's not NULL.
*/
char *__PHYSFS_platformCalcBaseDir(const char *argv0);
/*
* Get the platform-specific user dir.
* As of PhysicsFS 2.1, returning NULL means fatal error.
* Your string must end with a dir separator if you don't return NULL.
* Caller will allocator.Free() the retval if it's not NULL.
*/
char *__PHYSFS_platformCalcUserDir(void);
/* This is the cached version from PHYSFS_init(). This is a fast call. */
const char *__PHYSFS_getUserDir(void); /* not deprecated internal version. */
/*
* Get the platform-specific pref dir.
* Returning NULL means fatal error.
* Your string must end with a dir separator if you don't return NULL.
* Caller will allocator.Free() the retval if it's not NULL.
* Caller will make missing directories if necessary; this just reports
* the final path.
*/
char *__PHYSFS_platformCalcPrefDir(const char *org, const char *app);
/*
* Return a pointer that uniquely identifies the current thread.
* On a platform without threading, (0x1) will suffice. These numbers are
* arbitrary; the only requirement is that no two threads have the same
* pointer.
*/
void *__PHYSFS_platformGetThreadID(void);
/*
* Enumerate a directory of files. This follows the rules for the
* PHYSFS_Archiver::enumerate() method, except that the (dirName) that is
* passed to this function is converted to platform-DEPENDENT notation by
* the caller. The PHYSFS_Archiver version uses platform-independent
* notation. Note that ".", "..", and other meta-entries should always
* be ignored.
*/
PHYSFS_EnumerateCallbackResult __PHYSFS_platformEnumerate(const char *dirname,
PHYSFS_EnumerateCallback callback,
const char *origdir, void *callbackdata);
/*
* Make a directory in the actual filesystem. (path) is specified in
* platform-dependent notation. On error, return zero and set the error
* message. Return non-zero on success.
*/
int __PHYSFS_platformMkDir(const char *path);
/*
* Remove a file or directory entry in the actual filesystem. (path) is
* specified in platform-dependent notation. Note that this deletes files
* _and_ directories, so you might need to do some determination.
* Non-empty directories should report an error and not delete themselves
* or their contents.
*
* Deleting a symlink should remove the link, not what it points to.
*
* On error, return zero and set the error message. Return non-zero on success.
*/
int __PHYSFS_platformDelete(const char *path);
/*
* Create a platform-specific mutex. This can be whatever datatype your
* platform uses for mutexes, but it is cast to a (void *) for abstractness.
*
* Return (NULL) if you couldn't create one. Systems without threads can
* return any arbitrary non-NULL value.
*/
void *__PHYSFS_platformCreateMutex(void);
/*
* Destroy a platform-specific mutex, and clean up any resources associated
* with it. (mutex) is a value previously returned by
* __PHYSFS_platformCreateMutex(). This can be a no-op on single-threaded
* platforms.
*/
void __PHYSFS_platformDestroyMutex(void *mutex);
/*
* Grab possession of a platform-specific mutex. Mutexes should be recursive;
* that is, the same thread should be able to call this function multiple
* times in a row without causing a deadlock. This function should block
* until a thread can gain possession of the mutex.
*
* Return non-zero if the mutex was grabbed, zero if there was an
* unrecoverable problem grabbing it (this should not be a matter of
* timing out! We're talking major system errors; block until the mutex
* is available otherwise.)
*
* _DO NOT_ call PHYSFS_setErrorCode() in here! Since setErrorCode calls this
* function, you'll cause an infinite recursion. This means you can't
* use the BAIL_*MACRO* macros, either.
*/
int __PHYSFS_platformGrabMutex(void *mutex);
/*
* Relinquish possession of the mutex when this method has been called
* once for each time that platformGrabMutex was called. Once possession has
* been released, the next thread in line to grab the mutex (if any) may
* proceed.
*
* _DO NOT_ call PHYSFS_setErrorCode() in here! Since setErrorCode calls this
* function, you'll cause an infinite recursion. This means you can't
* use the BAIL_*MACRO* macros, either.
*/
void __PHYSFS_platformReleaseMutex(void *mutex);
/* !!! FIXME: move to public API? */
PHYSFS_uint32 __PHYSFS_utf8codepoint(const char **_str);
#if PHYSFS_HAVE_PRAGMA_VISIBILITY
#pragma GCC visibility pop
#endif
#ifdef __cplusplus
}
#endif
#endif
/* end of physfs_internal.h ... */

6031
third-party/physfs/src/physfs_lzmasdk.h vendored Normal file

File diff suppressed because it is too large Load Diff

710
third-party/physfs/src/physfs_miniz.h vendored Normal file
View File

@ -0,0 +1,710 @@
/* 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
typedef PHYSFS_uint8 mz_uint8;
typedef PHYSFS_sint16 mz_int16;
typedef PHYSFS_uint16 mz_uint16;
typedef PHYSFS_uint32 mz_uint32;
typedef unsigned int mz_uint;
typedef PHYSFS_uint64 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);
#ifndef MINIZ_LITTLE_ENDIAN /* if not defined by PHYSFS */
#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
#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
#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))
#define MZ_CLEAR_ARR(obj) memset((obj), 0, sizeof(obj))
#define MZ_CLEAR_PTR(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 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_ARR(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_ARR(total_syms); MZ_CLEAR_ARR(pTable->m_look_up); MZ_CLEAR_ARR(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 = def_alloc_func; */
/* if (!pStream->zfree) pStream->zfree = 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/>
*/

View File

@ -0,0 +1,117 @@
/*
* Android support routines for PhysicsFS.
*
* Please see the file LICENSE.txt in the source's root directory.
*
* This file written by Ryan C. Gordon.
*/
#define __PHYSICSFS_INTERNAL__
#include "physfs_platforms.h"
#ifdef PHYSFS_PLATFORM_ANDROID
#include <jni.h>
#include <android/log.h>
#include "physfs_internal.h"
static char *prefpath = NULL;
int __PHYSFS_platformInit(void)
{
return 1; /* always succeed. */
} /* __PHYSFS_platformInit */
void __PHYSFS_platformDeinit(void)
{
if (prefpath)
{
allocator.Free(prefpath);
prefpath = NULL;
} /* if */
} /* __PHYSFS_platformDeinit */
void __PHYSFS_platformDetectAvailableCDs(PHYSFS_StringCallback cb, void *data)
{
/* no-op. */
} /* __PHYSFS_platformDetectAvailableCDs */
char *__PHYSFS_platformCalcBaseDir(const char *argv0)
{
/* as a cheat, we expect argv0 to be a PHYSFS_AndroidInit* on Android. */
PHYSFS_AndroidInit *ainit = (PHYSFS_AndroidInit *) argv0;
char *retval = NULL;
JNIEnv *jenv = NULL;
jobject jcontext;
if (ainit == NULL)
return __PHYSFS_strdup("/"); /* oh well. */
jenv = (JNIEnv *) ainit->jnienv;
jcontext = (jobject) ainit->context;
if ((*jenv)->PushLocalFrame(jenv, 16) >= 0)
{
jobject jfileobj = 0;
jmethodID jmeth = 0;
jthrowable jexception = 0;
jstring jstr = 0;
jmeth = (*jenv)->GetMethodID(jenv, (*jenv)->GetObjectClass(jenv, jcontext), "getPackageResourcePath", "()Ljava/lang/String;");
jstr = (jstring)(*jenv)->CallObjectMethod(jenv, jcontext, jmeth);
jexception = (*jenv)->ExceptionOccurred(jenv); /* this can't throw an exception, right? Just in case. */
if (jexception != NULL)
(*jenv)->ExceptionClear(jenv);
else
{
const char *path = (*jenv)->GetStringUTFChars(jenv, jstr, NULL);
retval = __PHYSFS_strdup(path);
(*jenv)->ReleaseStringUTFChars(jenv, jstr, path);
} /* else */
/* We only can rely on the Activity being valid during this function call,
so go ahead and grab the prefpath too. */
jmeth = (*jenv)->GetMethodID(jenv, (*jenv)->GetObjectClass(jenv, jcontext), "getFilesDir", "()Ljava/io/File;");
jfileobj = (*jenv)->CallObjectMethod(jenv, jcontext, jmeth);
if (jfileobj)
{
jmeth = (*jenv)->GetMethodID(jenv, (*jenv)->GetObjectClass(jenv, jfileobj), "getCanonicalPath", "()Ljava/lang/String;");
jstr = (jstring)(*jenv)->CallObjectMethod(jenv, jfileobj, jmeth);
jexception = (*jenv)->ExceptionOccurred(jenv);
if (jexception != NULL)
(*jenv)->ExceptionClear(jenv);
else
{
const char *path = (*jenv)->GetStringUTFChars(jenv, jstr, NULL);
const size_t len = strlen(path) + 2;
prefpath = allocator.Malloc(len);
if (prefpath)
snprintf(prefpath, len, "%s/", path);
(*jenv)->ReleaseStringUTFChars(jenv, jstr, path);
} /* else */
} /* if */
(*jenv)->PopLocalFrame(jenv, NULL);
} /* if */
/* we can't return NULL because then PhysicsFS will treat argv0 as a string, but it's a non-NULL jobject! */
if (retval == NULL)
retval = __PHYSFS_strdup("/"); /* we pray this works. */
return retval;
} /* __PHYSFS_platformCalcBaseDir */
char *__PHYSFS_platformCalcPrefDir(const char *org, const char *app)
{
return __PHYSFS_strdup(prefpath ? prefpath : "/");
} /* __PHYSFS_platformCalcPrefDir */
#endif /* PHYSFS_PLATFORM_ANDROID */
/* end of physfs_platform_android.c ... */

View File

@ -0,0 +1,201 @@
/*
* Apple platform (macOS, iOS, watchOS, etc) support routines for PhysicsFS.
*
* Please see the file LICENSE.txt in the source's root directory.
*
* This file written by Ryan C. Gordon.
*/
#define __PHYSICSFS_INTERNAL__
#include "physfs_platforms.h"
#ifdef PHYSFS_PLATFORM_APPLE
#include <Foundation/Foundation.h>
#include <dlfcn.h>
#include "physfs_internal.h"
int __PHYSFS_platformInit(void)
{
return 1; /* success. */
} /* __PHYSFS_platformInit */
void __PHYSFS_platformDeinit(void)
{
/* no-op */
} /* __PHYSFS_platformDeinit */
char *__PHYSFS_platformCalcBaseDir(const char *argv0)
{
@autoreleasepool
{
NSString *path = [[NSBundle mainBundle] bundlePath];
BAIL_IF(!path, PHYSFS_ERR_OS_ERROR, NULL);
size_t len = [path lengthOfBytesUsingEncoding:NSUTF8StringEncoding];
char *retval = (char *) allocator.Malloc(len + 2);
BAIL_IF(!retval, PHYSFS_ERR_OUT_OF_MEMORY, NULL);
[path getCString:retval maxLength:len+1 encoding:NSUTF8StringEncoding];
retval[len] = '/';
retval[len+1] = '\0';
return retval; /* whew. */
} /* @autoreleasepool */
} /* __PHYSFS_platformCalcBaseDir */
char *__PHYSFS_platformCalcPrefDir(const char *org, const char *app)
{
@autoreleasepool
{
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSApplicationSupportDirectory, NSUserDomainMask, TRUE);
BAIL_IF(!paths, PHYSFS_ERR_OS_ERROR, NULL);
NSString *path = (NSString *) [paths objectAtIndex:0];
BAIL_IF(!path, PHYSFS_ERR_OS_ERROR, NULL);
size_t len = [path lengthOfBytesUsingEncoding:NSUTF8StringEncoding];
const size_t applen = strlen(app);
char *retval = (char *) allocator.Malloc(len + applen + 3);
BAIL_IF(!retval, PHYSFS_ERR_OUT_OF_MEMORY, NULL);
[path getCString:retval maxLength:len+1 encoding:NSUTF8StringEncoding];
snprintf(retval + len, applen + 3, "/%s/", app);
return retval; /* whew. */
} /* @autoreleasepool */
} /* __PHYSFS_platformCalcPrefDir */
/* CD-ROM detection code... */
/*
* Code based on sample from Apple Developer Connection:
* https://developer.apple.com/samplecode/Sample_Code/Devices_and_Hardware/Disks/VolumeToBSDNode/VolumeToBSDNode.c.htm
*/
#if !defined(PHYSFS_NO_CDROM_SUPPORT)
#include <IOKit/IOKitLib.h>
#include <IOKit/storage/IOMedia.h>
#include <IOKit/storage/IOCDMedia.h>
#include <IOKit/storage/IODVDMedia.h>
#include <sys/mount.h>
static int darwinIsWholeMedia(io_service_t service)
{
int retval = 0;
CFTypeRef wholeMedia;
if (!IOObjectConformsTo(service, kIOMediaClass))
return 0;
wholeMedia = IORegistryEntryCreateCFProperty(service,
CFSTR(kIOMediaWholeKey),
NULL, 0);
if (wholeMedia == NULL)
return 0;
retval = CFBooleanGetValue(wholeMedia);
CFRelease(wholeMedia);
return retval;
} /* darwinIsWholeMedia */
static int darwinIsMountedDisc(char *bsdName, mach_port_t mainPort)
{
int retval = 0;
CFMutableDictionaryRef matchingDict;
kern_return_t rc;
io_iterator_t iter;
io_service_t service;
if ((matchingDict = IOBSDNameMatching(mainPort, 0, bsdName)) == NULL)
return 0;
rc = IOServiceGetMatchingServices(mainPort, matchingDict, &iter);
if ((rc != KERN_SUCCESS) || (!iter))
return 0;
service = IOIteratorNext(iter);
IOObjectRelease(iter);
if (!service)
return 0;
rc = IORegistryEntryCreateIterator(service, kIOServicePlane,
kIORegistryIterateRecursively | kIORegistryIterateParents, &iter);
if (!iter)
return 0;
if (rc != KERN_SUCCESS)
{
IOObjectRelease(iter);
return 0;
} /* if */
IOObjectRetain(service); /* add an extra object reference... */
do
{
if (darwinIsWholeMedia(service))
{
if ( (IOObjectConformsTo(service, kIOCDMediaClass)) ||
(IOObjectConformsTo(service, kIODVDMediaClass)) )
{
retval = 1;
} /* if */
} /* if */
IOObjectRelease(service);
} while ((service = IOIteratorNext(iter)) && (!retval));
IOObjectRelease(iter);
IOObjectRelease(service);
return retval;
} /* darwinIsMountedDisc */
#endif /* !defined(PHYSFS_NO_CDROM_SUPPORT) */
void __PHYSFS_platformDetectAvailableCDs(PHYSFS_StringCallback cb, void *data)
{
#if !defined(PHYSFS_NO_CDROM_SUPPORT)
/* macOS 12.0 changed "master" names to "main". */
typedef kern_return_t (*ioMainPortFn)(mach_port_t, mach_port_t *);
static ioMainPortFn ioMainPort = NULL;
const char *devPrefix = "/dev/";
const int prefixLen = strlen(devPrefix);
mach_port_t mainPort = 0;
struct statfs *mntbufp;
int i, mounts;
if (ioMainPort == NULL)
{
ioMainPort = (ioMainPortFn) dlsym(RTLD_DEFAULT, "IOMainPort");
if (!ioMainPort)
ioMainPort = (ioMainPortFn) dlsym(RTLD_DEFAULT, "IOMasterPort");
if (!ioMainPort)
return; /* oh well, no CD-ROMs for you. */
} /* if */
if (ioMainPort(MACH_PORT_NULL, &mainPort) != KERN_SUCCESS)
BAIL(PHYSFS_ERR_OS_ERROR, ) /*return void*/;
mounts = getmntinfo(&mntbufp, MNT_WAIT); /* NOT THREAD SAFE! */
for (i = 0; i < mounts; i++)
{
char *dev = mntbufp[i].f_mntfromname;
char *mnt = mntbufp[i].f_mntonname;
if (strncmp(dev, devPrefix, prefixLen) != 0) /* a virtual device? */
continue;
dev += prefixLen;
if (darwinIsMountedDisc(dev, mainPort))
cb(data, mnt);
} /* for */
#endif /* !defined(PHYSFS_NO_CDROM_SUPPORT) */
} /* __PHYSFS_platformDetectAvailableCDs */
#endif /* PHYSFS_PLATFORM_APPLE */
/* end of physfs_platform_apple.m ... */

View File

@ -0,0 +1,186 @@
/*
* Haiku platform-dependent support routines for PhysicsFS.
*
* Please see the file LICENSE.txt in the source's root directory.
*
* This file written by Ryan C. Gordon.
*/
#define __PHYSICSFS_INTERNAL__
#include "physfs_platforms.h"
#ifdef PHYSFS_PLATFORM_HAIKU
#include <os/kernel/OS.h>
#include <os/app/Roster.h>
#include <os/storage/Volume.h>
#include <os/storage/VolumeRoster.h>
#include <os/storage/Directory.h>
#include <os/storage/Entry.h>
#include <os/storage/Path.h>
#include <os/kernel/fs_info.h>
#include <os/device/scsi.h>
#include <errno.h>
#include <unistd.h>
#include "physfs_internal.h"
int __PHYSFS_platformInit(void)
{
return 1; /* always succeed. */
} /* __PHYSFS_platformInit */
void __PHYSFS_platformDeinit(void)
{
/* no-op */
} /* __PHYSFS_platformDeinit */
static char *getMountPoint(const char *devname, char *buf, size_t bufsize)
{
BVolumeRoster mounts;
BVolume vol;
mounts.Rewind();
while (mounts.GetNextVolume(&vol) == B_NO_ERROR)
{
fs_info fsinfo;
fs_stat_dev(vol.Device(), &fsinfo);
if (strcmp(devname, fsinfo.device_name) == 0)
{
BDirectory directory;
BEntry entry;
BPath path;
const char *str;
if ( (vol.GetRootDirectory(&directory) < B_OK) ||
(directory.GetEntry(&entry) < B_OK) ||
(entry.GetPath(&path) < B_OK) ||
( (str = path.Path()) == NULL) )
return NULL;
strncpy(buf, str, bufsize-1);
buf[bufsize-1] = '\0';
return buf;
} /* if */
} /* while */
return NULL;
} /* getMountPoint */
/*
* This function is lifted from Simple Directmedia Layer (SDL):
* https://www.libsdl.org/ ... this is zlib-licensed code, too.
*/
static void tryDir(const char *d, PHYSFS_StringCallback callback, void *data)
{
BDirectory dir;
dir.SetTo(d);
if (dir.InitCheck() != B_NO_ERROR)
return;
dir.Rewind();
BEntry entry;
while (dir.GetNextEntry(&entry) >= 0)
{
BPath path;
const char *name;
entry_ref e;
if (entry.GetPath(&path) != B_NO_ERROR)
continue;
name = path.Path();
if (entry.GetRef(&e) != B_NO_ERROR)
continue;
if (entry.IsDirectory())
{
if (strcmp(e.name, "floppy") != 0)
tryDir(name, callback, data);
continue;
} /* if */
const int devfd = open(name, O_RDONLY);
if (devfd < 0)
continue;
device_geometry g;
const int rc = ioctl(devfd, B_GET_GEOMETRY, &g, sizeof (g));
close(devfd);
if (rc < 0)
continue;
if (g.device_type != B_CD)
continue;
char mntpnt[B_FILE_NAME_LENGTH];
if (getMountPoint(name, mntpnt, sizeof (mntpnt)))
callback(data, mntpnt);
} /* while */
} /* tryDir */
void __PHYSFS_platformDetectAvailableCDs(PHYSFS_StringCallback cb, void *data)
{
tryDir("/dev/disk", cb, data);
} /* __PHYSFS_platformDetectAvailableCDs */
static team_id getTeamID(void)
{
thread_info info;
thread_id tid = find_thread(NULL);
get_thread_info(tid, &info);
return info.team;
} /* getTeamID */
char *__PHYSFS_platformCalcBaseDir(const char *argv0)
{
image_info info;
int32 cookie = 0;
while (get_next_image_info(0, &cookie, &info) == B_OK)
{
if (info.type == B_APP_IMAGE)
break;
} /* while */
BEntry entry(info.name, true);
BPath path;
status_t rc = entry.GetPath(&path); /* (path) now has binary's path. */
assert(rc == B_OK);
rc = path.GetParent(&path); /* chop filename, keep directory. */
assert(rc == B_OK);
const char *str = path.Path();
assert(str != NULL);
const size_t len = strlen(str);
char *retval = (char *) allocator.Malloc(len + 2);
BAIL_IF(!retval, PHYSFS_ERR_OUT_OF_MEMORY, NULL);
strcpy(retval, str);
retval[len] = '/';
retval[len+1] = '\0';
return retval;
} /* __PHYSFS_platformCalcBaseDir */
char *__PHYSFS_platformCalcPrefDir(const char *org, const char *app)
{
const char *userdir = __PHYSFS_getUserDir();
const char *append = "config/settings/";
const size_t len = strlen(userdir) + strlen(append) + strlen(app) + 2;
char *retval = (char *) allocator.Malloc(len);
BAIL_IF(!retval, PHYSFS_ERR_OUT_OF_MEMORY, NULL);
snprintf(retval, len, "%s%s%s/", userdir, append, app);
return retval;
} /* __PHYSFS_platformCalcPrefDir */
#endif /* PHYSFS_PLATFORM_HAIKU */
/* end of physfs_platform_haiku.cpp ... */

View File

@ -0,0 +1,812 @@
/*
* OS/2 support routines for PhysicsFS.
*
* Please see the file LICENSE.txt in the source's root directory.
*
* This file written by Ryan C. Gordon.
*/
#define __PHYSICSFS_INTERNAL__
#include "physfs_platforms.h"
#ifdef PHYSFS_PLATFORM_OS2
#define INCL_DOSMODULEMGR
#define INCL_DOSSEMAPHORES
#define INCL_DOSDATETIME
#define INCL_DOSFILEMGR
#define INCL_DOSMODULEMGR
#define INCL_DOSERRORS
#define INCL_DOSPROCESS
#define INCL_DOSDEVICES
#define INCL_DOSDEVIOCTL
#define INCL_DOSMISC
#include <os2.h>
#include <uconv.h>
#include <errno.h>
#include <time.h>
#include <ctype.h>
#include "physfs_internal.h"
static HMODULE uconvdll = 0;
static UconvObject uconv = 0;
static int (_System *pUniCreateUconvObject)(UniChar *, UconvObject *) = NULL;
static int (_System *pUniFreeUconvObject)(UconvObject *) = NULL;
static int (_System *pUniUconvToUcs)(UconvObject,void **,size_t *, UniChar**, size_t *, size_t *) = NULL;
static int (_System *pUniUconvFromUcs)(UconvObject,UniChar **,size_t *,void **,size_t *,size_t *) = NULL;
static PHYSFS_ErrorCode errcodeFromAPIRET(const APIRET rc)
{
switch (rc)
{
case NO_ERROR: return PHYSFS_ERR_OK; /* not an error. */
case ERROR_INTERRUPT: return PHYSFS_ERR_OK; /* not an error. */
case ERROR_TIMEOUT: return PHYSFS_ERR_OK; /* not an error. */
case ERROR_NOT_ENOUGH_MEMORY: return PHYSFS_ERR_OUT_OF_MEMORY;
case ERROR_FILE_NOT_FOUND: return PHYSFS_ERR_NOT_FOUND;
case ERROR_PATH_NOT_FOUND: return PHYSFS_ERR_NOT_FOUND;
case ERROR_ACCESS_DENIED: return PHYSFS_ERR_PERMISSION;
case ERROR_NOT_DOS_DISK: return PHYSFS_ERR_NOT_FOUND;
case ERROR_SHARING_VIOLATION: return PHYSFS_ERR_PERMISSION;
case ERROR_CANNOT_MAKE: return PHYSFS_ERR_IO; /* maybe this is wrong? */
case ERROR_DEVICE_IN_USE: return PHYSFS_ERR_BUSY;
case ERROR_OPEN_FAILED: return PHYSFS_ERR_IO; /* maybe this is wrong? */
case ERROR_DISK_FULL: return PHYSFS_ERR_NO_SPACE;
case ERROR_PIPE_BUSY: return PHYSFS_ERR_BUSY;
case ERROR_SHARING_BUFFER_EXCEEDED: return PHYSFS_ERR_IO;
case ERROR_FILENAME_EXCED_RANGE: return PHYSFS_ERR_BAD_FILENAME;
case ERROR_META_EXPANSION_TOO_LONG: return PHYSFS_ERR_BAD_FILENAME;
case ERROR_TOO_MANY_HANDLES: return PHYSFS_ERR_IO;
case ERROR_TOO_MANY_OPEN_FILES: return PHYSFS_ERR_IO;
case ERROR_NO_MORE_SEARCH_HANDLES: return PHYSFS_ERR_IO;
case ERROR_SEEK_ON_DEVICE: return PHYSFS_ERR_IO;
case ERROR_NEGATIVE_SEEK: return PHYSFS_ERR_INVALID_ARGUMENT;
case ERROR_WRITE_PROTECT: return PHYSFS_ERR_PERMISSION;
case ERROR_WRITE_FAULT: return PHYSFS_ERR_IO;
case ERROR_UNCERTAIN_MEDIA: return PHYSFS_ERR_IO;
case ERROR_PROTECTION_VIOLATION: return PHYSFS_ERR_IO;
case ERROR_BROKEN_PIPE: return PHYSFS_ERR_IO;
/* !!! FIXME: some of these might be PHYSFS_ERR_BAD_FILENAME, etc */
case ERROR_LOCK_VIOLATION:
case ERROR_GEN_FAILURE:
case ERROR_INVALID_PARAMETER:
case ERROR_INVALID_NAME:
case ERROR_INVALID_DRIVE:
case ERROR_INVALID_HANDLE:
case ERROR_INVALID_FUNCTION:
case ERROR_INVALID_LEVEL:
case ERROR_INVALID_CATEGORY:
case ERROR_DUPLICATE_NAME:
case ERROR_BUFFER_OVERFLOW:
case ERROR_BAD_LENGTH:
case ERROR_BAD_DRIVER_LEVEL:
case ERROR_DIRECT_ACCESS_HANDLE:
case ERROR_NOT_OWNER:
return PHYSFS_ERR_OS_ERROR;
default: break;
} /* switch */
return PHYSFS_ERR_OTHER_ERROR;
} /* errcodeFromAPIRET */
static char *cvtUtf8ToCodepage(const char *utf8str)
{
const size_t len = strlen(utf8str) + 1;
const size_t uc2buflen = len * sizeof (UniChar);
UniChar *uc2ptr = (UniChar *) __PHYSFS_smallAlloc(uc2buflen);
UniChar *uc2str = uc2ptr;
char *cpptr = NULL;
char *cpstr = NULL;
size_t subs = 0;
size_t unilen;
BAIL_IF(!uc2str, PHYSFS_ERR_OUT_OF_MEMORY, NULL);
PHYSFS_utf8ToUcs2(utf8str, (PHYSFS_uint16 *) uc2str, uc2buflen);
for (unilen = 0; uc2str[unilen]; unilen++) { /* spin */ }
unilen++; /* null terminator. */
if (!uconvdll)
{
/* There's really not much we can do on older OS/2s except pray this
is latin1-compatible. */
size_t i;
cpptr = (char *) allocator.Malloc(unilen);
cpstr = cpptr;
GOTO_IF(!cpptr, PHYSFS_ERR_OUT_OF_MEMORY, failed);
for (i = 0; i < unilen; i++)
{
const UniChar ch = uc2str[i];
GOTO_IF(ch > 0xFF, PHYSFS_ERR_BAD_FILENAME, failed);
cpptr[i] = (char) ((unsigned char) ch);
} /* for */
__PHYSFS_smallFree(uc2ptr);
return cpstr;
} /* if */
else
{
int rc;
size_t cplen = unilen * 4; /* overallocate, just in case. */
cpptr = (char *) allocator.Malloc(cplen);
GOTO_IF(!cpptr, PHYSFS_ERR_OUT_OF_MEMORY, failed);
cpstr = cpptr;
rc = pUniUconvFromUcs(uconv, &uc2str, &unilen, (void **) &cpstr, &cplen, &subs);
GOTO_IF(rc != ULS_SUCCESS, PHYSFS_ERR_BAD_FILENAME, failed);
GOTO_IF(subs > 0, PHYSFS_ERR_BAD_FILENAME, failed);
assert(unilen == 0);
__PHYSFS_smallFree(uc2ptr);
return cpptr;
} /* else */
failed:
__PHYSFS_smallFree(uc2ptr);
allocator.Free(cpptr);
return NULL;
} /* cvtUtf8ToCodepage */
static char *cvtCodepageToUtf8(const char *cpstr)
{
const size_t len = strlen(cpstr) + 1;
char *retvalbuf = (char *) allocator.Malloc(len * 4);
char *retval = NULL;
BAIL_IF(!retvalbuf, PHYSFS_ERR_OUT_OF_MEMORY, NULL);
if (!uconvdll)
{
/* There's really not much we can do on older OS/2s except pray this
is latin1-compatible. */
retval = retvalbuf;
PHYSFS_utf8FromLatin1(cpstr, retval, len * 4);
} /* if */
else
{
int rc;
size_t cplen = len;
size_t unilen = len;
size_t subs = 0;
UniChar *uc2ptr = __PHYSFS_smallAlloc(len * sizeof (UniChar));
UniChar *uc2str = uc2ptr;
BAIL_IF(!uc2ptr, PHYSFS_ERR_OUT_OF_MEMORY, NULL);
rc = pUniUconvToUcs(uconv, (void **) &cpstr, &cplen, &uc2str, &unilen, &subs);
GOTO_IF(rc != ULS_SUCCESS, PHYSFS_ERR_BAD_FILENAME, done);
GOTO_IF(subs > 0, PHYSFS_ERR_BAD_FILENAME, done);
assert(cplen == 0);
retval = retvalbuf;
PHYSFS_utf8FromUcs2((const PHYSFS_uint16 *) uc2ptr, retval, len * 4);
done:
__PHYSFS_smallFree(uc2ptr);
} /* else */
return retval;
} /* cvtCodepageToUtf8 */
/* (be gentle, this function isn't very robust.) */
static char *cvtPathToCorrectCase(char *buf)
{
char *retval = buf;
char *fname = buf + 3; /* point to first element. */
char *ptr = strchr(fname, '\\'); /* find end of first element. */
buf[0] = toupper(buf[0]); /* capitalize drive letter. */
/*
* Go through each path element, and enumerate its parent dir until
* a case-insensitive match is found. If one is (and it SHOULD be)
* then overwrite the original element with the correct case.
* If there's an error, or the path has vanished for some reason, it
* won't hurt to have the original case, so we just keep going.
*/
while ((fname != NULL) && (*fname != '\0'))
{
char spec[CCHMAXPATH];
FILEFINDBUF3 fb;
HDIR hdir = HDIR_CREATE;
ULONG count = 1;
APIRET rc;
*(fname - 1) = '\0'; /* isolate parent dir string. */
strcpy(spec, buf); /* copy isolated parent dir... */
strcat(spec, "\\*.*"); /* ...and add wildcard search spec. */
if (ptr != NULL) /* isolate element to find (fname is the start). */
*ptr = '\0';
rc = DosFindFirst(spec, &hdir, FILE_DIRECTORY,
&fb, sizeof (fb), &count, FIL_STANDARD);
if (rc == NO_ERROR)
{
while (count == 1) /* while still entries to enumerate... */
{
int cmp;
char *utf8 = cvtCodepageToUtf8(fb.achName);
if (!utf8) /* ugh, maybe we'll get lucky with the C runtime. */
cmp = stricmp(fb.achName, fname);
else
{
cmp = PHYSFS_utf8stricmp(utf8, fname);
allocator.Free(utf8);
} /* else */
if (cmp == 0)
{
strcpy(fname, fb.achName);
break; /* there it is. Overwrite and stop searching. */
} /* if */
DosFindNext(hdir, &fb, sizeof (fb), &count);
} /* while */
DosFindClose(hdir);
} /* if */
*(fname - 1) = '\\'; /* unisolate parent dir. */
fname = ptr; /* point to next element. */
if (ptr != NULL)
{
*ptr = '\\'; /* unisolate element. */
ptr = strchr(++fname, '\\'); /* find next element. */
} /* if */
} /* while */
return retval;
} /* cvtPathToCorrectCase */
static void prepUnicodeSupport(void)
{
/* really old OS/2 might not have Unicode support _at all_, so load
the system library and do without if it doesn't exist. */
int ok = 0;
char buf[CCHMAXPATH];
UniChar defstr[] = { 0 };
if (DosLoadModule(buf, sizeof (buf) - 1, "uconv", &uconvdll) == NO_ERROR)
{
#define LOAD(x) (DosQueryProcAddr(uconvdll,0,#x,(PFN*)&p##x)==NO_ERROR)
ok = LOAD(UniCreateUconvObject) &&
LOAD(UniFreeUconvObject) &&
LOAD(UniUconvToUcs) &&
LOAD(UniUconvFromUcs);
#undef LOAD
} /* else */
if (!ok || (pUniCreateUconvObject(defstr, &uconv) != ULS_SUCCESS))
{
/* oh well, live without it. */
if (uconvdll)
{
if (uconv)
pUniFreeUconvObject(uconv);
DosFreeModule(uconvdll);
uconvdll = 0;
} /* if */
} /* if */
} /* prepUnicodeSupport */
int __PHYSFS_platformInit(void)
{
prepUnicodeSupport();
return 1; /* ready to go! */
} /* __PHYSFS_platformInit */
void __PHYSFS_platformDeinit(void)
{
if (uconvdll)
{
pUniFreeUconvObject(uconv);
uconv = 0;
DosFreeModule(uconvdll);
uconvdll = 0;
} /* if */
} /* __PHYSFS_platformDeinit */
static int discIsInserted(ULONG drive)
{
int rc;
char buf[20];
DosError(FERR_DISABLEHARDERR | FERR_DISABLEEXCEPTION);
rc = DosQueryFSInfo(drive + 1, FSIL_VOLSER, buf, sizeof (buf));
DosError(FERR_ENABLEHARDERR | FERR_ENABLEEXCEPTION);
return (rc == NO_ERROR);
} /* is_cdrom_inserted */
/* looks like "CD01" in ASCII (littleendian)...used for an ioctl. */
#define CD01 0x31304443
static int isCdRomDrive(ULONG drive)
{
PHYSFS_uint32 param, data;
ULONG ul1, ul2;
APIRET rc;
HFILE hfile = NULLHANDLE;
char drivename[3] = { 0, ':', '\0' };
drivename[0] = 'A' + drive;
rc = DosOpen(drivename, &hfile, &ul1, 0, 0,
OPEN_ACTION_OPEN_IF_EXISTS | OPEN_ACTION_FAIL_IF_NEW,
OPEN_FLAGS_DASD | OPEN_FLAGS_FAIL_ON_ERROR |
OPEN_FLAGS_NOINHERIT | OPEN_SHARE_DENYNONE, NULL);
if (rc != NO_ERROR)
return 0;
data = 0;
param = PHYSFS_swapULE32(CD01);
ul1 = ul2 = sizeof (PHYSFS_uint32);
rc = DosDevIOCtl(hfile, IOCTL_CDROMDISK, CDROMDISK_GETDRIVER,
&param, sizeof (param), &ul1, &data, sizeof (data), &ul2);
DosClose(hfile);
return ((rc == NO_ERROR) && (PHYSFS_swapULE32(data) == CD01));
} /* isCdRomDrive */
void __PHYSFS_platformDetectAvailableCDs(PHYSFS_StringCallback cb, void *data)
{
ULONG dummy = 0;
ULONG drivemap = 0;
ULONG i, bit;
const APIRET rc = DosQueryCurrentDisk(&dummy, &drivemap);
BAIL_IF(rc != NO_ERROR, errcodeFromAPIRET(rc),);
for (i = 0, bit = 1; i < 26; i++, bit <<= 1)
{
if (drivemap & bit) /* this logical drive exists. */
{
if ((isCdRomDrive(i)) && (discIsInserted(i)))
{
char drive[4] = "x:\\";
drive[0] = ('A' + i);
cb(data, drive);
} /* if */
} /* if */
} /* for */
} /* __PHYSFS_platformDetectAvailableCDs */
char *__PHYSFS_platformCalcBaseDir(const char *argv0)
{
char *retval = NULL;
char buf[CCHMAXPATH];
APIRET rc;
PTIB ptib;
PPIB ppib;
PHYSFS_sint32 len;
rc = DosGetInfoBlocks(&ptib, &ppib);
BAIL_IF(rc != NO_ERROR, errcodeFromAPIRET(rc), 0);
rc = DosQueryModuleName(ppib->pib_hmte, sizeof (buf), (PCHAR) buf);
BAIL_IF(rc != NO_ERROR, errcodeFromAPIRET(rc), 0);
retval = cvtCodepageToUtf8(buf);
BAIL_IF_ERRPASS(!retval, NULL);
/* chop off filename, leave path. */
for (len = strlen(retval) - 1; len >= 0; len--)
{
if (retval[len] == '\\')
{
retval[len + 1] = '\0';
break;
} /* if */
} /* for */
assert(len > 0); /* should have been a "x:\\" on the front on string. */
/* The string is capitalized! Figure out the REAL case... */
return cvtPathToCorrectCase(retval);
} /* __PHYSFS_platformCalcBaseDir */
char *__PHYSFS_platformCalcUserDir(void)
{
return __PHYSFS_platformCalcBaseDir(NULL); /* !!! FIXME: ? */
} /* __PHYSFS_platformCalcUserDir */
char *__PHYSFS_platformCalcPrefDir(const char *org, const char *app)
{
return __PHYSFS_platformCalcBaseDir(NULL); /* !!! FIXME: ? */
} /* __PHYSFS_platformCalcPrefDir */
PHYSFS_EnumerateCallbackResult __PHYSFS_platformEnumerate(const char *dirname,
PHYSFS_EnumerateCallback callback,
const char *origdir, void *callbackdata)
{
PHYSFS_EnumerateCallbackResult retval = PHYSFS_ENUM_OK;
size_t utf8len = strlen(dirname);
char *utf8 = (char *) __PHYSFS_smallAlloc(utf8len + 5);
char *cpspec = NULL;
FILEFINDBUF3 fb;
HDIR hdir = HDIR_CREATE;
ULONG count = 1;
APIRET rc;
BAIL_IF(!utf8, PHYSFS_ERR_OUT_OF_MEMORY, PHYSFS_ENUM_ERROR);
strcpy(utf8, dirname);
if (utf8[utf8len - 1] != '\\')
strcpy(utf8 + utf8len, "\\*.*");
else
strcpy(utf8 + utf8len, "*.*");
cpspec = cvtUtf8ToCodepage(utf8);
__PHYSFS_smallFree(utf8);
BAIL_IF_ERRPASS(!cpspec, PHYSFS_ENUM_ERROR);
rc = DosFindFirst(cpspec, &hdir,
FILE_DIRECTORY | FILE_ARCHIVED |
FILE_READONLY | FILE_HIDDEN | FILE_SYSTEM,
&fb, sizeof (fb), &count, FIL_STANDARD);
allocator.Free(cpspec);
BAIL_IF(rc != NO_ERROR, errcodeFromAPIRET(rc), PHYSFS_ENUM_ERROR);
while (count == 1)
{
if ((strcmp(fb.achName, ".") != 0) && (strcmp(fb.achName, "..") != 0))
{
utf8 = cvtCodepageToUtf8(fb.achName);
if (!utf8)
retval = PHYSFS_ENUM_ERROR;
else
{
retval = callback(callbackdata, origdir, utf8);
allocator.Free(utf8);
if (retval == PHYSFS_ENUM_ERROR)
PHYSFS_setErrorCode(PHYSFS_ERR_APP_CALLBACK);
} /* else */
} /* if */
if (retval != PHYSFS_ENUM_OK)
break;
DosFindNext(hdir, &fb, sizeof (fb), &count);
} /* while */
DosFindClose(hdir);
return retval;
} /* __PHYSFS_platformEnumerate */
char *__PHYSFS_platformCurrentDir(void)
{
char *retval;
char *cpstr;
char *utf8;
ULONG currentDisk;
ULONG dummy;
ULONG pathSize = 0;
APIRET rc;
BYTE byte;
rc = DosQueryCurrentDisk(&currentDisk, &dummy);
BAIL_IF(rc != NO_ERROR, errcodeFromAPIRET(rc), NULL);
/* The first call just tells us how much space we need for the string. */
rc = DosQueryCurrentDir(currentDisk, &byte, &pathSize);
pathSize++; /* Add space for null terminator. */
cpstr = (char *) __PHYSFS_smallAlloc(pathSize);
BAIL_IF(cpstr == NULL, PHYSFS_ERR_OUT_OF_MEMORY, NULL);
/* Actually get the string this time. */
rc = DosQueryCurrentDir(currentDisk, (PBYTE) cpstr, &pathSize);
if (rc != NO_ERROR)
{
__PHYSFS_smallFree(cpstr);
BAIL(errcodeFromAPIRET(rc), NULL);
} /* if */
utf8 = cvtCodepageToUtf8(cpstr);
__PHYSFS_smallFree(cpstr);
BAIL_IF_ERRPASS(utf8 == NULL, NULL);
/* +4 for "x:\\" drive selector and null terminator. */
retval = (char *) allocator.Malloc(strlen(utf8) + 4);
if (retval == NULL)
{
allocator.Free(utf8);
BAIL(PHYSFS_ERR_OUT_OF_MEMORY, NULL);
} /* if */
retval[0] = ('A' + (currentDisk - 1));
retval[1] = ':';
retval[2] = '\\';
strcpy(retval + 3, utf8);
allocator.Free(utf8);
return retval;
} /* __PHYSFS_platformCurrentDir */
int __PHYSFS_platformMkDir(const char *filename)
{
APIRET rc;
char *cpstr = cvtUtf8ToCodepage(filename);
BAIL_IF_ERRPASS(!cpstr, 0);
rc = DosCreateDir(cpstr, NULL);
allocator.Free(cpstr);
BAIL_IF(rc != NO_ERROR, errcodeFromAPIRET(rc), 0);
return 1;
} /* __PHYSFS_platformMkDir */
static HFILE openFile(const char *filename, const ULONG flags, const ULONG mode)
{
char *cpfname = cvtUtf8ToCodepage(filename);
ULONG action = 0;
HFILE hfile = NULLHANDLE;
APIRET rc;
BAIL_IF_ERRPASS(!cpfname, 0);
rc = DosOpen(cpfname, &hfile, &action, 0, FILE_NORMAL, flags, mode, NULL);
allocator.Free(cpfname);
BAIL_IF(rc != NO_ERROR, errcodeFromAPIRET(rc), 0);
return hfile;
} /* openFile */
void *__PHYSFS_platformOpenRead(const char *filename)
{
/*
* File must be opened SHARE_DENYWRITE and ACCESS_READONLY, otherwise
* DosQueryFileInfo() will fail if we try to get a file length, etc.
*/
return (void *) openFile(filename,
OPEN_ACTION_OPEN_IF_EXISTS | OPEN_ACTION_FAIL_IF_NEW,
OPEN_FLAGS_FAIL_ON_ERROR | OPEN_FLAGS_NO_LOCALITY |
OPEN_FLAGS_NOINHERIT | OPEN_SHARE_DENYWRITE |
OPEN_ACCESS_READONLY);
} /* __PHYSFS_platformOpenRead */
void *__PHYSFS_platformOpenWrite(const char *filename)
{
return (void *) openFile(filename,
OPEN_ACTION_REPLACE_IF_EXISTS |
OPEN_ACTION_CREATE_IF_NEW,
OPEN_FLAGS_FAIL_ON_ERROR | OPEN_FLAGS_NO_LOCALITY |
OPEN_FLAGS_NOINHERIT | OPEN_SHARE_DENYWRITE);
} /* __PHYSFS_platformOpenWrite */
void *__PHYSFS_platformOpenAppend(const char *filename)
{
APIRET rc;
ULONG dummy = 0;
HFILE hfile;
/*
* File must be opened SHARE_DENYWRITE and ACCESS_READWRITE, otherwise
* DosQueryFileInfo() will fail if we try to get a file length, etc.
*/
hfile = openFile(filename,
OPEN_ACTION_OPEN_IF_EXISTS | OPEN_ACTION_CREATE_IF_NEW,
OPEN_FLAGS_FAIL_ON_ERROR | OPEN_FLAGS_NO_LOCALITY |
OPEN_FLAGS_NOINHERIT | OPEN_SHARE_DENYWRITE |
OPEN_ACCESS_READWRITE);
BAIL_IF_ERRPASS(!hfile, NULL);
rc = DosSetFilePtr(hfile, 0, FILE_END, &dummy);
if (rc != NO_ERROR)
{
DosClose(hfile);
BAIL(errcodeFromAPIRET(rc), NULL);
} /* if */
return ((void *) hfile);
} /* __PHYSFS_platformOpenAppend */
PHYSFS_sint64 __PHYSFS_platformRead(void *opaque, void *buf, PHYSFS_uint64 len)
{
ULONG br = 0;
APIRET rc;
BAIL_IF(!__PHYSFS_ui64FitsAddressSpace(len),PHYSFS_ERR_INVALID_ARGUMENT,-1);
rc = DosRead((HFILE) opaque, buf, (ULONG) len, &br);
BAIL_IF(rc != NO_ERROR, errcodeFromAPIRET(rc), (br > 0) ? ((PHYSFS_sint64) br) : -1);
return (PHYSFS_sint64) br;
} /* __PHYSFS_platformRead */
PHYSFS_sint64 __PHYSFS_platformWrite(void *opaque, const void *buf,
PHYSFS_uint64 len)
{
ULONG bw = 0;
APIRET rc;
BAIL_IF(!__PHYSFS_ui64FitsAddressSpace(len),PHYSFS_ERR_INVALID_ARGUMENT,-1);
rc = DosWrite((HFILE) opaque, (void *) buf, (ULONG) len, &bw);
BAIL_IF(rc != NO_ERROR, errcodeFromAPIRET(rc), (bw > 0) ? ((PHYSFS_sint64) bw) : -1);
return (PHYSFS_sint64) bw;
} /* __PHYSFS_platformWrite */
int __PHYSFS_platformSeek(void *opaque, PHYSFS_uint64 pos)
{
ULONG dummy;
HFILE hfile = (HFILE) opaque;
LONG dist = (LONG) pos;
APIRET rc;
/* hooray for 32-bit filesystem limits! :) */
BAIL_IF((PHYSFS_uint64) dist != pos, PHYSFS_ERR_INVALID_ARGUMENT, 0);
rc = DosSetFilePtr(hfile, dist, FILE_BEGIN, &dummy);
BAIL_IF(rc != NO_ERROR, errcodeFromAPIRET(rc), 0);
return 1;
} /* __PHYSFS_platformSeek */
PHYSFS_sint64 __PHYSFS_platformTell(void *opaque)
{
ULONG pos;
HFILE hfile = (HFILE) opaque;
const APIRET rc = DosSetFilePtr(hfile, 0, FILE_CURRENT, &pos);
BAIL_IF(rc != NO_ERROR, errcodeFromAPIRET(rc), -1);
return ((PHYSFS_sint64) pos);
} /* __PHYSFS_platformTell */
PHYSFS_sint64 __PHYSFS_platformFileLength(void *opaque)
{
FILESTATUS3 fs;
HFILE hfile = (HFILE) opaque;
const APIRET rc = DosQueryFileInfo(hfile, FIL_STANDARD, &fs, sizeof (fs));
BAIL_IF(rc != NO_ERROR, errcodeFromAPIRET(rc), -1);
return ((PHYSFS_sint64) fs.cbFile);
} /* __PHYSFS_platformFileLength */
int __PHYSFS_platformFlush(void *opaque)
{
const APIRET rc = DosResetBuffer((HFILE) opaque);
BAIL_IF(rc != NO_ERROR, errcodeFromAPIRET(rc), 0);
return 1;
} /* __PHYSFS_platformFlush */
void __PHYSFS_platformClose(void *opaque)
{
DosClose((HFILE) opaque); /* ignore errors. You should have flushed! */
} /* __PHYSFS_platformClose */
int __PHYSFS_platformDelete(const char *path)
{
char *cppath = cvtUtf8ToCodepage(path);
FILESTATUS3 fs;
APIRET rc;
int retval = 0;
BAIL_IF_ERRPASS(!cppath, 0);
rc = DosQueryPathInfo(cppath, FIL_STANDARD, &fs, sizeof (fs));
GOTO_IF(rc != NO_ERROR, errcodeFromAPIRET(rc), done);
rc = (fs.attrFile & FILE_DIRECTORY) ? DosDeleteDir(path) : DosDelete(path);
GOTO_IF(rc != NO_ERROR, errcodeFromAPIRET(rc), done);
retval = 1; /* success */
done:
allocator.Free(cppath);
return retval;
} /* __PHYSFS_platformDelete */
/* Convert to a format PhysicsFS can grok... */
PHYSFS_sint64 os2TimeToUnixTime(const FDATE *date, const FTIME *time)
{
struct tm tm;
tm.tm_sec = ((PHYSFS_uint32) time->twosecs) * 2;
tm.tm_min = time->minutes;
tm.tm_hour = time->hours;
tm.tm_mday = date->day;
tm.tm_mon = date->month;
tm.tm_year = ((PHYSFS_uint32) date->year) + 80;
tm.tm_wday = -1 /*st_localtz.wDayOfWeek*/;
tm.tm_yday = -1;
tm.tm_isdst = -1;
return (PHYSFS_sint64) mktime(&tm);
} /* os2TimeToUnixTime */
int __PHYSFS_platformStat(const char *filename, PHYSFS_Stat *stat, const int follow)
{
char *cpfname = cvtUtf8ToCodepage(filename);
FILESTATUS3 fs;
int retval = 0;
APIRET rc;
BAIL_IF_ERRPASS(!cpfname, 0);
rc = DosQueryPathInfo(cpfname, FIL_STANDARD, &fs, sizeof (fs));
GOTO_IF(rc != NO_ERROR, errcodeFromAPIRET(rc), done);
if (fs.attrFile & FILE_DIRECTORY)
{
stat->filetype = PHYSFS_FILETYPE_DIRECTORY;
stat->filesize = 0;
} /* if */
else
{
stat->filetype = PHYSFS_FILETYPE_REGULAR;
stat->filesize = fs.cbFile;
} /* else */
stat->modtime = os2TimeToUnixTime(&fs.fdateLastWrite, &fs.ftimeLastWrite);
if (stat->modtime < 0)
stat->modtime = 0;
stat->accesstime = os2TimeToUnixTime(&fs.fdateLastAccess, &fs.ftimeLastAccess);
if (stat->accesstime < 0)
stat->accesstime = 0;
stat->createtime = os2TimeToUnixTime(&fs.fdateCreation, &fs.ftimeCreation);
if (stat->createtime < 0)
stat->createtime = 0;
stat->readonly = ((fs.attrFile & FILE_READONLY) == FILE_READONLY);
return 1; /* success */
done:
allocator.Free(cpfname);
return retval;
} /* __PHYSFS_platformStat */
void *__PHYSFS_platformGetThreadID(void)
{
PTIB ptib;
PPIB ppib;
/*
* Allegedly, this API never fails, but we'll punt and return a
* default value (zero might as well do) if it does.
*/
const APIRET rc = DosGetInfoBlocks(&ptib, &ppib);
BAIL_IF(rc != NO_ERROR, errcodeFromAPIRET(rc), 0);
return ((void *) ptib->tib_ordinal);
} /* __PHYSFS_platformGetThreadID */
void *__PHYSFS_platformCreateMutex(void)
{
HMTX hmtx = NULLHANDLE;
const APIRET rc = DosCreateMutexSem(NULL, &hmtx, 0, 0);
BAIL_IF(rc != NO_ERROR, errcodeFromAPIRET(rc), NULL);
return ((void *) hmtx);
} /* __PHYSFS_platformCreateMutex */
void __PHYSFS_platformDestroyMutex(void *mutex)
{
DosCloseMutexSem((HMTX) mutex);
} /* __PHYSFS_platformDestroyMutex */
int __PHYSFS_platformGrabMutex(void *mutex)
{
/* Do _NOT_ set the physfs error message in here! */
return (DosRequestMutexSem((HMTX) mutex, SEM_INDEFINITE_WAIT) == NO_ERROR);
} /* __PHYSFS_platformGrabMutex */
void __PHYSFS_platformReleaseMutex(void *mutex)
{
DosReleaseMutexSem((HMTX) mutex);
} /* __PHYSFS_platformReleaseMutex */
#endif /* PHYSFS_PLATFORM_OS2 */
/* end of physfs_platform_os2.c ... */

View File

@ -0,0 +1,449 @@
/*
* Posix-esque support routines for PhysicsFS.
*
* Please see the file LICENSE.txt in the source's root directory.
*
* This file written by Ryan C. Gordon.
*/
#define __PHYSICSFS_INTERNAL__
#include "physfs_platforms.h"
#ifdef PHYSFS_PLATFORM_POSIX
#include <unistd.h>
#include <ctype.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <pwd.h>
#include <dirent.h>
#include <errno.h>
#include <fcntl.h>
#include <pthread.h>
#include "physfs_internal.h"
static PHYSFS_ErrorCode errcodeFromErrnoError(const int err)
{
switch (err)
{
case 0: return PHYSFS_ERR_OK;
case EACCES: return PHYSFS_ERR_PERMISSION;
case EPERM: return PHYSFS_ERR_PERMISSION;
case EDQUOT: return PHYSFS_ERR_NO_SPACE;
case EIO: return PHYSFS_ERR_IO;
case ELOOP: return PHYSFS_ERR_SYMLINK_LOOP;
case EMLINK: return PHYSFS_ERR_NO_SPACE;
case ENAMETOOLONG: return PHYSFS_ERR_BAD_FILENAME;
case ENOENT: return PHYSFS_ERR_NOT_FOUND;
case ENOSPC: return PHYSFS_ERR_NO_SPACE;
case ENOTDIR: return PHYSFS_ERR_NOT_FOUND;
case EISDIR: return PHYSFS_ERR_NOT_A_FILE;
case EROFS: return PHYSFS_ERR_READ_ONLY;
case ETXTBSY: return PHYSFS_ERR_BUSY;
case EBUSY: return PHYSFS_ERR_BUSY;
case ENOMEM: return PHYSFS_ERR_OUT_OF_MEMORY;
case ENOTEMPTY: return PHYSFS_ERR_DIR_NOT_EMPTY;
default: return PHYSFS_ERR_OS_ERROR;
} /* switch */
} /* errcodeFromErrnoError */
static inline PHYSFS_ErrorCode errcodeFromErrno(void)
{
return errcodeFromErrnoError(errno);
} /* errcodeFromErrno */
static char *getUserDirByUID(void)
{
uid_t uid = getuid();
struct passwd *pw;
char *retval = NULL;
pw = getpwuid(uid);
if ((pw != NULL) && (pw->pw_dir != NULL) && (*pw->pw_dir != '\0'))
{
const size_t dlen = strlen(pw->pw_dir);
const size_t add_dirsep = (pw->pw_dir[dlen-1] != '/') ? 1 : 0;
retval = (char *) allocator.Malloc(dlen + 1 + add_dirsep);
if (retval != NULL)
{
strcpy(retval, pw->pw_dir);
if (add_dirsep)
{
retval[dlen] = '/';
retval[dlen+1] = '\0';
} /* if */
} /* if */
} /* if */
return retval;
} /* getUserDirByUID */
char *__PHYSFS_platformCalcUserDir(void)
{
char *retval = NULL;
char *envr = getenv("HOME");
/* if the environment variable was set, make sure it's really a dir. */
if (envr != NULL)
{
struct stat statbuf;
if ((stat(envr, &statbuf) != -1) && (S_ISDIR(statbuf.st_mode)))
{
const size_t envrlen = strlen(envr);
const size_t add_dirsep = (envr[envrlen-1] != '/') ? 1 : 0;
retval = allocator.Malloc(envrlen + 1 + add_dirsep);
if (retval)
{
strcpy(retval, envr);
if (add_dirsep)
{
retval[envrlen] = '/';
retval[envrlen+1] = '\0';
} /* if */
} /* if */
} /* if */
} /* if */
if (retval == NULL)
retval = getUserDirByUID();
return retval;
} /* __PHYSFS_platformCalcUserDir */
PHYSFS_EnumerateCallbackResult __PHYSFS_platformEnumerate(const char *dirname,
PHYSFS_EnumerateCallback callback,
const char *origdir, void *callbackdata)
{
DIR *dir;
struct dirent *ent;
PHYSFS_EnumerateCallbackResult retval = PHYSFS_ENUM_OK;
dir = opendir(dirname);
BAIL_IF(dir == NULL, errcodeFromErrno(), PHYSFS_ENUM_ERROR);
while ((retval == PHYSFS_ENUM_OK) && ((ent = readdir(dir)) != NULL))
{
const char *name = ent->d_name;
if (name[0] == '.') /* ignore "." and ".." */
{
if ((name[1] == '\0') || ((name[1] == '.') && (name[2] == '\0')))
continue;
} /* if */
retval = callback(callbackdata, origdir, name);
if (retval == PHYSFS_ENUM_ERROR)
PHYSFS_setErrorCode(PHYSFS_ERR_APP_CALLBACK);
} /* while */
closedir(dir);
return retval;
} /* __PHYSFS_platformEnumerate */
int __PHYSFS_platformMkDir(const char *path)
{
const int rc = mkdir(path, S_IRWXU);
BAIL_IF(rc == -1, errcodeFromErrno(), 0);
return 1;
} /* __PHYSFS_platformMkDir */
#if !defined(O_CLOEXEC) && defined(FD_CLOEXEC)
static inline void set_CLOEXEC(int fildes)
{
int flags = fcntl(fildes, F_GETFD);
if (flags != -1) {
fcntl(fildes, F_SETFD, flags | FD_CLOEXEC);
}
}
#endif
static void *doOpen(const char *filename, int mode)
{
const int appending = (mode & O_APPEND);
int fd;
int *retval;
errno = 0;
/* O_APPEND doesn't actually behave as we'd like. */
mode &= ~O_APPEND;
#ifdef O_CLOEXEC
/* Add O_CLOEXEC if defined */
mode |= O_CLOEXEC;
#endif
do {
fd = open(filename, mode, S_IRUSR | S_IWUSR);
} while ((fd < 0) && (errno == EINTR));
BAIL_IF(fd < 0, errcodeFromErrno(), NULL);
#if !defined(O_CLOEXEC) && defined(FD_CLOEXEC)
set_CLOEXEC(fd);
#endif
if (appending)
{
if (lseek(fd, 0, SEEK_END) < 0)
{
const int err = errno;
close(fd);
BAIL(errcodeFromErrnoError(err), NULL);
} /* if */
} /* if */
retval = (int *) allocator.Malloc(sizeof (int));
if (!retval)
{
close(fd);
BAIL(PHYSFS_ERR_OUT_OF_MEMORY, NULL);
} /* if */
*retval = fd;
return ((void *) retval);
} /* doOpen */
void *__PHYSFS_platformOpenRead(const char *filename)
{
return doOpen(filename, O_RDONLY);
} /* __PHYSFS_platformOpenRead */
void *__PHYSFS_platformOpenWrite(const char *filename)
{
return doOpen(filename, O_WRONLY | O_CREAT | O_TRUNC);
} /* __PHYSFS_platformOpenWrite */
void *__PHYSFS_platformOpenAppend(const char *filename)
{
return doOpen(filename, O_WRONLY | O_CREAT | O_APPEND);
} /* __PHYSFS_platformOpenAppend */
PHYSFS_sint64 __PHYSFS_platformRead(void *opaque, void *buffer,
PHYSFS_uint64 len)
{
const int fd = *((int *) opaque);
ssize_t rc = 0;
if (!__PHYSFS_ui64FitsAddressSpace(len))
BAIL(PHYSFS_ERR_INVALID_ARGUMENT, -1);
do {
rc = read(fd, buffer, (size_t) len);
} while ((rc == -1) && (errno == EINTR));
BAIL_IF(rc == -1, errcodeFromErrno(), -1);
assert(rc >= 0);
assert(rc <= len);
return (PHYSFS_sint64) rc;
} /* __PHYSFS_platformRead */
PHYSFS_sint64 __PHYSFS_platformWrite(void *opaque, const void *buffer,
PHYSFS_uint64 len)
{
const int fd = *((int *) opaque);
ssize_t rc = 0;
if (!__PHYSFS_ui64FitsAddressSpace(len))
BAIL(PHYSFS_ERR_INVALID_ARGUMENT, -1);
do {
rc = write(fd, (void *) buffer, (size_t) len);
} while ((rc == -1) && (errno == EINTR));
BAIL_IF(rc == -1, errcodeFromErrno(), rc);
assert(rc >= 0);
assert(rc <= len);
return (PHYSFS_sint64) rc;
} /* __PHYSFS_platformWrite */
int __PHYSFS_platformSeek(void *opaque, PHYSFS_uint64 pos)
{
const int fd = *((int *) opaque);
const off_t rc = lseek(fd, (off_t) pos, SEEK_SET);
BAIL_IF(rc == -1, errcodeFromErrno(), 0);
return 1;
} /* __PHYSFS_platformSeek */
PHYSFS_sint64 __PHYSFS_platformTell(void *opaque)
{
const int fd = *((int *) opaque);
PHYSFS_sint64 retval;
retval = (PHYSFS_sint64) lseek(fd, 0, SEEK_CUR);
BAIL_IF(retval == -1, errcodeFromErrno(), -1);
return retval;
} /* __PHYSFS_platformTell */
PHYSFS_sint64 __PHYSFS_platformFileLength(void *opaque)
{
const int fd = *((int *) opaque);
struct stat statbuf;
BAIL_IF(fstat(fd, &statbuf) == -1, errcodeFromErrno(), -1);
return ((PHYSFS_sint64) statbuf.st_size);
} /* __PHYSFS_platformFileLength */
int __PHYSFS_platformFlush(void *opaque)
{
const int fd = *((int *) opaque);
int rc = -1;
if ((fcntl(fd, F_GETFL) & O_ACCMODE) != O_RDONLY) {
do {
rc = fsync(fd);
} while ((rc == -1) && (errno == EINTR));
BAIL_IF(rc == -1, errcodeFromErrno(), 0);
}
return 1;
} /* __PHYSFS_platformFlush */
void __PHYSFS_platformClose(void *opaque)
{
const int fd = *((int *) opaque);
int rc = -1;
do {
rc = close(fd); /* we don't check this. You should have used flush! */
} while ((rc == -1) && (errno == EINTR));
allocator.Free(opaque);
} /* __PHYSFS_platformClose */
int __PHYSFS_platformDelete(const char *path)
{
BAIL_IF(remove(path) == -1, errcodeFromErrno(), 0);
return 1;
} /* __PHYSFS_platformDelete */
int __PHYSFS_platformStat(const char *fname, PHYSFS_Stat *st, const int follow)
{
struct stat statbuf;
const int rc = follow ? stat(fname, &statbuf) : lstat(fname, &statbuf);
BAIL_IF(rc == -1, errcodeFromErrno(), 0);
if (S_ISREG(statbuf.st_mode))
{
st->filetype = PHYSFS_FILETYPE_REGULAR;
st->filesize = statbuf.st_size;
} /* if */
else if(S_ISDIR(statbuf.st_mode))
{
st->filetype = PHYSFS_FILETYPE_DIRECTORY;
st->filesize = 0;
} /* else if */
else if(S_ISLNK(statbuf.st_mode))
{
st->filetype = PHYSFS_FILETYPE_SYMLINK;
st->filesize = 0;
} /* else if */
else
{
st->filetype = PHYSFS_FILETYPE_OTHER;
st->filesize = statbuf.st_size;
} /* else */
st->modtime = statbuf.st_mtime;
st->createtime = statbuf.st_ctime;
st->accesstime = statbuf.st_atime;
st->readonly = (access(fname, W_OK) == -1);
return 1;
} /* __PHYSFS_platformStat */
typedef struct
{
pthread_mutex_t mutex;
pthread_t owner;
PHYSFS_uint32 count;
} PthreadMutex;
void *__PHYSFS_platformGetThreadID(void)
{
return ( (void *) ((size_t) pthread_self()) );
} /* __PHYSFS_platformGetThreadID */
void *__PHYSFS_platformCreateMutex(void)
{
int rc;
PthreadMutex *m = (PthreadMutex *) allocator.Malloc(sizeof (PthreadMutex));
BAIL_IF(!m, PHYSFS_ERR_OUT_OF_MEMORY, NULL);
rc = pthread_mutex_init(&m->mutex, NULL);
if (rc != 0)
{
allocator.Free(m);
BAIL(PHYSFS_ERR_OS_ERROR, NULL);
} /* if */
m->count = 0;
m->owner = (pthread_t) 0xDEADBEEF;
return ((void *) m);
} /* __PHYSFS_platformCreateMutex */
void __PHYSFS_platformDestroyMutex(void *mutex)
{
PthreadMutex *m = (PthreadMutex *) mutex;
/* Destroying a locked mutex is a bug, but we'll try to be helpful. */
if ((m->owner == pthread_self()) && (m->count > 0))
pthread_mutex_unlock(&m->mutex);
pthread_mutex_destroy(&m->mutex);
allocator.Free(m);
} /* __PHYSFS_platformDestroyMutex */
int __PHYSFS_platformGrabMutex(void *mutex)
{
PthreadMutex *m = (PthreadMutex *) mutex;
pthread_t tid = pthread_self();
if (m->owner != tid)
{
if (pthread_mutex_lock(&m->mutex) != 0)
return 0;
m->owner = tid;
} /* if */
m->count++;
return 1;
} /* __PHYSFS_platformGrabMutex */
void __PHYSFS_platformReleaseMutex(void *mutex)
{
PthreadMutex *m = (PthreadMutex *) mutex;
assert(m->owner == pthread_self()); /* catch programming errors. */
assert(m->count > 0); /* catch programming errors. */
if (m->owner == pthread_self())
{
if (--m->count == 0)
{
m->owner = (pthread_t) 0xDEADBEEF;
pthread_mutex_unlock(&m->mutex);
} /* if */
} /* if */
} /* __PHYSFS_platformReleaseMutex */
#endif /* PHYSFS_PLATFORM_POSIX */
/* end of physfs_platform_posix.c ... */

View File

@ -0,0 +1,169 @@
/*
* QNX support routines for PhysicsFS.
*
* Please see the file LICENSE.txt in the source's root directory.
*
* This file written by Ryan C. Gordon.
*/
/* This is tested against QNX 7 at the moment. */
#define __PHYSICSFS_INTERNAL__
#include "physfs_platforms.h"
#ifdef PHYSFS_PLATFORM_QNX
#include <fcntl.h>
#include <unistd.h>
#include <sys/types.h>
#include <limits.h>
#include "physfs_internal.h"
int __PHYSFS_platformInit(void)
{
return 1; /* always succeed. */
} /* __PHYSFS_platformInit */
void __PHYSFS_platformDeinit(void)
{
/* no-op */
} /* __PHYSFS_platformDeinit */
char *__PHYSFS_platformCalcBaseDir(const char *argv0)
{
char *retval = (char *) allocator.Malloc(PATH_MAX+1);
if (retval == NULL)
BAIL(PHYSFS_ERR_OUT_OF_MEMORY, NULL);
else
{
const int fd = open("/proc/self/exefile", O_RDONLY);
const ssize_t br = (fd == -1) ? -1 : read(fd, retval, PATH_MAX);
char *ptr;
if (fd != -1)
close(fd);
if ((br < 0) || (br > PATH_MAX))
{
allocator.Free(retval);
BAIL(PHYSFS_ERR_OS_ERROR, NULL);
} /* if */
retval[br] = '\0';
ptr = strrchr(retval, '/');
if (ptr == NULL) /* uhoh! */
{
allocator.Free(retval);
BAIL(PHYSFS_ERR_OS_ERROR, NULL);
} /* if */
ptr[1] = '\0'; /* chop off filename, leave dirs and '/' */
ptr = (char *) allocator.Realloc(retval, (ptr - retval) + 2);
if (ptr != NULL) /* just shrinking buffer; don't care if it failed. */
retval = ptr;
} /* else */
return retval;
} /* __PHYSFS_platformCalcBaseDir */
char *__PHYSFS_platformCalcPrefDir(const char *org, const char *app)
{
/* !!! FIXME: this might be wrong; I don't know if there's a better method
on QNX, or if it follows XDG specs, etc. */
char *retval = NULL;
const char *home = __PHYSFS_getUserDir();
if (home)
{
const size_t len = strlen(home) + strlen(app) + 3;
retval = (char *) allocator.Malloc(len);
BAIL_IF(!retval, PHYSFS_ERR_OUT_OF_MEMORY, NULL);
snprintf(retval, len, "%s.%s/", home, app);
} /* if */
return retval;
} /* __PHYSFS_platformCalcPrefDir */
#if !PHYSFS_NO_CDROM_SUPPORT
#include <devctl.h>
#include <sys/dcmd_blk.h>
#include <dirent.h>
#include <sys/stat.h>
#include <sys/statvfs.h>
static void checkPathForCD(const char *path, PHYSFS_StringCallback cb, void *d)
{
struct stat statbuf;
int fd;
/* The devctl() thing is QNX-specific. In this case, we query what is
probably the mountpoint for the device. statvfs() on that mountpoint
will tell use its filesystem type. */
if ( (stat(path, &statbuf) == 0) &&
(S_ISBLK(statbuf.st_mode)) &&
((fd = open(path, O_RDONLY | O_NONBLOCK)) != -1) )
{
char mnt[256] = { 0 };
const int rc = devctl(fd, DCMD_FSYS_MOUNTED_BY, mnt, sizeof (mnt), 0);
close(fd);
if ( (rc == EOK) && (mnt[0]) )
{
struct statvfs statvfsbuf;
if (statvfs(mnt, &statvfsbuf) == 0)
{
/* I don't know if this is a complete or accurate list. */
const char *fstype = statvfsbuf.f_basetype;
const int iscd = ( (strcmp(fstype, "cd") == 0) ||
(strcmp(fstype, "udf") == 0) );
if (iscd)
cb(d, mnt);
} /* if */
} /* if */
} /* if */
} /* checkPathForCD */
static void checkDevForCD(const char *dev, PHYSFS_StringCallback cb, void *d)
{
size_t len;
char *path;
if (dev[0] == '.') /* ignore "." and ".." */
{
if ((dev[1] == '\0') || ((dev[1] == '.') && (dev[2] == '\0')))
return;
} /* if */
len = strlen(dev) + 6;
path = (char *) __PHYSFS_smallAlloc(len);
if (!path)
return; /* oh well. */
snprintf(path, len, "/dev/%s", dev);
checkPathForCD(path, cb, d);
__PHYSFS_smallFree(path);
} /* checkDevForCD */
#endif /* !PHYSFS_NO_CDROM_SUPPORT */
void __PHYSFS_platformDetectAvailableCDs(PHYSFS_StringCallback cb, void *data)
{
#if !PHYSFS_NO_CDROM_SUPPORT
DIR *dirp = opendir("/dev");
if (dirp)
{
struct dirent *dent;
while ((dent = readdir(dirp)) != NULL)
checkDevForCD(dent->d_name, cb, data);
closedir(dirp);
} /* if */
#endif
} /* __PHYSFS_platformDetectAvailableCDs */
#endif /* PHYSFS_PLATFORM_QNX */
/* end of physfs_platform_qnx.c ... */

View File

@ -0,0 +1,367 @@
/*
* Unix support routines for PhysicsFS.
*
* Please see the file LICENSE.txt in the source's root directory.
*
* This file written by Ryan C. Gordon.
*/
#define __PHYSICSFS_INTERNAL__
#include "physfs_platforms.h"
#ifdef PHYSFS_PLATFORM_UNIX
#include <ctype.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <pwd.h>
#include <sys/stat.h>
#include <sys/param.h>
#include <dirent.h>
#include <time.h>
#include <errno.h>
#include <limits.h>
#if PHYSFS_NO_CDROM_SUPPORT
#elif PHYSFS_PLATFORM_LINUX
# define PHYSFS_HAVE_MNTENT_H 1
#elif defined __CYGWIN__
# define PHYSFS_HAVE_MNTENT_H 1
#elif PHYSFS_PLATFORM_SOLARIS
# define PHYSFS_HAVE_SYS_MNTTAB_H 1
#elif PHYSFS_PLATFORM_BSD
# define PHYSFS_HAVE_SYS_UCRED_H 1
#else
# warning No CD-ROM support included. Either define your platform here,
# warning or define PHYSFS_NO_CDROM_SUPPORT=1 to confirm this is intentional.
#endif
#ifdef PHYSFS_HAVE_SYS_UCRED_H
# ifdef PHYSFS_HAVE_MNTENT_H
# undef PHYSFS_HAVE_MNTENT_H /* don't do both... */
# endif
# include <sys/mount.h>
# include <sys/ucred.h>
#endif
#ifdef PHYSFS_HAVE_MNTENT_H
#include <mntent.h>
#endif
#ifdef PHYSFS_HAVE_SYS_MNTTAB_H
#include <sys/mnttab.h>
#endif
#ifdef PHYSFS_PLATFORM_FREEBSD
#include <sys/sysctl.h>
#endif
#include "physfs_internal.h"
int __PHYSFS_platformInit(void)
{
return 1; /* always succeed. */
} /* __PHYSFS_platformInit */
void __PHYSFS_platformDeinit(void)
{
/* no-op */
} /* __PHYSFS_platformDeinit */
void __PHYSFS_platformDetectAvailableCDs(PHYSFS_StringCallback cb, void *data)
{
#if (defined PHYSFS_NO_CDROM_SUPPORT)
/* no-op. */
#elif (defined PHYSFS_HAVE_SYS_UCRED_H)
int i;
struct statfs *mntbufp = NULL;
int mounts = getmntinfo(&mntbufp, MNT_NOWAIT);
for (i = 0; i < mounts; i++)
{
int add_it = 0;
if (strcmp(mntbufp[i].f_fstypename, "iso9660") == 0)
add_it = 1;
else if (strcmp( mntbufp[i].f_fstypename, "cd9660") == 0)
add_it = 1;
/* add other mount types here */
if (add_it)
cb(data, mntbufp[i].f_mntonname);
} /* for */
#elif (defined PHYSFS_HAVE_MNTENT_H)
FILE *mounts = NULL;
struct mntent *ent = NULL;
mounts = setmntent("/etc/mtab", "r");
BAIL_IF(mounts == NULL, PHYSFS_ERR_IO, /*return void*/);
while ( (ent = getmntent(mounts)) != NULL )
{
int add_it = 0;
if (strcmp(ent->mnt_type, "iso9660") == 0)
add_it = 1;
else if (strcmp(ent->mnt_type, "udf") == 0)
add_it = 1;
/* !!! FIXME: these might pick up floppy drives, right? */
else if (strcmp(ent->mnt_type, "auto") == 0)
add_it = 1;
else if (strcmp(ent->mnt_type, "supermount") == 0)
add_it = 1;
/* add other mount types here */
if (add_it)
cb(data, ent->mnt_dir);
} /* while */
endmntent(mounts);
#elif (defined PHYSFS_HAVE_SYS_MNTTAB_H)
FILE *mounts = fopen(MNTTAB, "r");
struct mnttab ent;
BAIL_IF(mounts == NULL, PHYSFS_ERR_IO, /*return void*/);
while (getmntent(mounts, &ent) == 0)
{
int add_it = 0;
if (strcmp(ent.mnt_fstype, "hsfs") == 0)
add_it = 1;
/* add other mount types here */
if (add_it)
cb(data, ent.mnt_mountp);
} /* while */
fclose(mounts);
#endif
} /* __PHYSFS_platformDetectAvailableCDs */
/*
* See where program (bin) resides in the $PATH specified by (envr).
* returns a copy of the first element in envr that contains it, or NULL
* if it doesn't exist or there were other problems. PHYSFS_SetError() is
* called if we have a problem.
*
* (envr) will be scribbled over, and you are expected to allocator.Free() the
* return value when you're done with it.
*/
static char *findBinaryInPath(const char *bin, char *envr)
{
size_t alloc_size = 0;
char *exe = NULL;
char *start = envr;
char *ptr;
assert(bin != NULL);
assert(envr != NULL);
do
{
size_t size;
size_t binlen;
ptr = strchr(start, ':'); /* find next $PATH separator. */
if (ptr)
*ptr = '\0';
binlen = strlen(bin);
size = strlen(start) + binlen + 2;
if (size >= alloc_size)
{
char *x = (char *) allocator.Realloc(exe, size);
if (!x)
{
if (exe != NULL)
allocator.Free(exe);
BAIL(PHYSFS_ERR_OUT_OF_MEMORY, NULL);
} /* if */
alloc_size = size;
exe = x;
} /* if */
/* build full binary path... */
strcpy(exe, start);
if ((exe[0] == '\0') || (exe[strlen(exe) - 1] != '/'))
strcat(exe, "/");
strcat(exe, bin);
if (access(exe, X_OK) == 0) /* Exists as executable? We're done. */
{
exe[(size - binlen) - 1] = '\0'; /* chop off filename, leave '/' */
return exe;
} /* if */
start = ptr + 1; /* start points to beginning of next element. */
} while (ptr != NULL);
if (exe != NULL)
allocator.Free(exe);
return NULL; /* doesn't exist in path. */
} /* findBinaryInPath */
static char *readSymLink(const char *path)
{
ssize_t len = 64;
ssize_t rc = -1;
char *retval = NULL;
while (1)
{
char *ptr = (char *) allocator.Realloc(retval, (size_t) len);
if (ptr == NULL)
break; /* out of memory. */
retval = ptr;
rc = readlink(path, retval, len);
if (rc == -1)
break; /* not a symlink, i/o error, etc. */
else if (rc < len)
{
retval[rc] = '\0'; /* readlink doesn't null-terminate. */
return retval; /* we're good to go. */
} /* else if */
len *= 2; /* grow buffer, try again. */
} /* while */
if (retval != NULL)
allocator.Free(retval);
return NULL;
} /* readSymLink */
char *__PHYSFS_platformCalcBaseDir(const char *argv0)
{
char *retval = NULL;
const char *envr = NULL;
/* Try to avoid using argv0 unless forced to. Try system-specific stuff. */
#if defined(PHYSFS_PLATFORM_FREEBSD)
{
char fullpath[PATH_MAX];
size_t buflen = sizeof (fullpath);
int mib[4] = { CTL_KERN, KERN_PROC, KERN_PROC_PATHNAME, -1 };
if (sysctl(mib, 4, fullpath, &buflen, NULL, 0) != -1)
retval = __PHYSFS_strdup(fullpath);
}
#elif defined(PHYSFS_PLATFORM_SOLARIS)
{
const char *path = getexecname();
if ((path != NULL) && (path[0] == '/')) /* must be absolute path... */
retval = __PHYSFS_strdup(path);
}
#endif
/* If there's a Linux-like /proc filesystem, you can get the full path to
* the current process from a symlink in there.
*/
if (!retval && (access("/proc", F_OK) == 0))
{
retval = readSymLink("/proc/self/exe");
if (!retval) retval = readSymLink("/proc/curproc/file");
if (!retval) retval = readSymLink("/proc/curproc/exe");
if (retval == NULL)
{
/* older kernels don't have /proc/self ... try PID version... */
const unsigned long long pid = (unsigned long long) getpid();
char path[64];
const int rc = (int) snprintf(path,sizeof(path),"/proc/%llu/exe",pid);
if ( (rc > 0) && (rc < sizeof(path)) )
retval = readSymLink(path);
} /* if */
} /* if */
if (retval != NULL) /* chop off filename. */
{
char *ptr = strrchr(retval, '/');
if (ptr != NULL)
*(ptr+1) = '\0';
else /* shouldn't happen, but just in case... */
{
allocator.Free(retval);
retval = NULL;
} /* else */
} /* if */
/* No /proc/self/exe, etc, but we have an argv[0] we can parse? */
if ((retval == NULL) && (argv0 != NULL))
{
/* fast path: default behaviour can handle this. */
if (strchr(argv0, '/') != NULL)
return NULL; /* higher level parses out real path from argv0. */
/* If there's no dirsep on argv0, then look through $PATH for it. */
envr = getenv("PATH");
if (envr != NULL)
{
char *path = (char *) __PHYSFS_smallAlloc(strlen(envr) + 1);
BAIL_IF(!path, PHYSFS_ERR_OUT_OF_MEMORY, NULL);
strcpy(path, envr);
retval = findBinaryInPath(argv0, path);
__PHYSFS_smallFree(path);
} /* if */
} /* if */
if (retval != NULL)
{
/* try to shrink buffer... */
char *ptr = (char *) allocator.Realloc(retval, strlen(retval) + 1);
if (ptr != NULL)
retval = ptr; /* oh well if it failed. */
} /* if */
return retval;
} /* __PHYSFS_platformCalcBaseDir */
char *__PHYSFS_platformCalcPrefDir(const char *org, const char *app)
{
/*
* We use XDG's base directory spec, even if you're not on Linux.
* This isn't strictly correct, but the results are relatively sane
* in any case.
*
* https://standards.freedesktop.org/basedir-spec/basedir-spec-latest.html
*/
const char *envr = getenv("XDG_DATA_HOME");
const char *append = "/";
char *retval = NULL;
size_t len = 0;
if (!envr)
{
/* You end up with "$HOME/.local/share/Game Name 2" */
envr = __PHYSFS_getUserDir();
BAIL_IF_ERRPASS(!envr, NULL); /* oh well. */
append = ".local/share/";
} /* if */
len = strlen(envr) + strlen(append) + strlen(app) + 2;
retval = (char *) allocator.Malloc(len);
BAIL_IF(!retval, PHYSFS_ERR_OUT_OF_MEMORY, NULL);
snprintf(retval, len, "%s%s%s/", envr, append, app);
return retval;
} /* __PHYSFS_platformCalcPrefDir */
#endif /* PHYSFS_PLATFORM_UNIX */
/* end of physfs_platform_unix.c ... */

File diff suppressed because it is too large Load Diff

Some files were not shown because too many files have changed in this diff Show More