twn_skybox.c
This commit is contained in:
parent
0fe1023667
commit
c0dcdf8c0a
1
.gitattributes
vendored
1
.gitattributes
vendored
@ -1,3 +1,4 @@
|
|||||||
*.png filter=lfs diff=lfs merge=lfs -text
|
*.png filter=lfs diff=lfs merge=lfs -text
|
||||||
*.ogg filter=lfs diff=lfs merge=lfs -text
|
*.ogg filter=lfs diff=lfs merge=lfs -text
|
||||||
*.xm filter=lfs diff=lfs merge=lfs -text
|
*.xm filter=lfs diff=lfs merge=lfs -text
|
||||||
|
*.tga filter=lfs diff=lfs merge=lfs -text
|
||||||
|
@ -99,6 +99,7 @@ set(TWN_SOURCE_FILES
|
|||||||
src/rendering/twn_text.c
|
src/rendering/twn_text.c
|
||||||
src/rendering/twn_triangles.c
|
src/rendering/twn_triangles.c
|
||||||
src/rendering/twn_circles.c
|
src/rendering/twn_circles.c
|
||||||
|
src/rendering/twn_skybox.c
|
||||||
|
|
||||||
# for dynamic load based solution main is compiled in a separate target
|
# for dynamic load based solution main is compiled in a separate target
|
||||||
$<$<NOT:$<BOOL:${TWN_FEATURE_DYNLIB_GAME}>>:src/twn_main.c
|
$<$<NOT:$<BOOL:${TWN_FEATURE_DYNLIB_GAME}>>:src/twn_main.c
|
||||||
@ -300,6 +301,8 @@ function(use_townengine target sources output_directory data_dir)
|
|||||||
set(TWN_BOOTSTRAP_EXEC_ARGS
|
set(TWN_BOOTSTRAP_EXEC_ARGS
|
||||||
"$<IF:$<BOOL:${TWN_ARCHIVE_DATA}>,--data-dir ./data.${PACKAGE_EXTENSION},--data-dir ${data_dir}>")
|
"$<IF:$<BOOL:${TWN_ARCHIVE_DATA}>,--data-dir ./data.${PACKAGE_EXTENSION},--data-dir ${data_dir}>")
|
||||||
|
|
||||||
|
# todo: generate by python script instead
|
||||||
|
# todo: handle the case where no numerical trace exists
|
||||||
string(JOIN "\n" TWN_BOOTSTRAP_SH
|
string(JOIN "\n" TWN_BOOTSTRAP_SH
|
||||||
"#!/bin/env sh"
|
"#!/bin/env sh"
|
||||||
"cd \"$(dirname \"$0\")\""
|
"cd \"$(dirname \"$0\")\""
|
||||||
|
@ -58,28 +58,30 @@ static void ingame_tick(State *state) {
|
|||||||
|
|
||||||
for (int y = 64; y--;) {
|
for (int y = 64; y--;) {
|
||||||
for (int x = 64; x--;) {
|
for (int x = 64; x--;) {
|
||||||
float d0 = stb_perlin_noise3((float)x * TERRAIN_FREQUENCY, (float)y * TERRAIN_FREQUENCY, 0, 0, 0, 0) * 20 - 6;
|
float d0 = stb_perlin_noise3((float)x * TERRAIN_FREQUENCY, (float)y * TERRAIN_FREQUENCY, 0, 0, 0, 0) * 3 - 6;
|
||||||
float d1 = stb_perlin_noise3((float)(x + 1) * TERRAIN_FREQUENCY, (float)y * TERRAIN_FREQUENCY, 0, 0, 0, 0) * 20 - 6;
|
float d1 = stb_perlin_noise3((float)(x + 1) * TERRAIN_FREQUENCY, (float)y * TERRAIN_FREQUENCY, 0, 0, 0, 0) * 3 - 6;
|
||||||
float d2 = stb_perlin_noise3((float)(x + 1) * TERRAIN_FREQUENCY, (float)(y - 1) * TERRAIN_FREQUENCY, 0, 0, 0, 0) * 20 - 6;
|
float d2 = stb_perlin_noise3((float)(x + 1) * TERRAIN_FREQUENCY, (float)(y - 1) * TERRAIN_FREQUENCY, 0, 0, 0, 0) * 3 - 6;
|
||||||
float d3 = stb_perlin_noise3((float)x * TERRAIN_FREQUENCY, (float)(y - 1) * TERRAIN_FREQUENCY, 0, 0, 0, 0) * 20 - 6;
|
float d3 = stb_perlin_noise3((float)x * TERRAIN_FREQUENCY, (float)(y - 1) * TERRAIN_FREQUENCY, 0, 0, 0, 0) * 3 - 6;
|
||||||
|
|
||||||
unfurl_triangle("/assets/grass.gif",
|
unfurl_triangle("/assets/grass.png",
|
||||||
(Vec3){ (float)x, d0, (float)y },
|
(Vec3){ (float)x, d0, (float)y },
|
||||||
(Vec3){ (float)x + 1, d1, (float)y },
|
(Vec3){ (float)x + 1, d1, (float)y },
|
||||||
(Vec3){ (float)x, d3, (float)y - 1 },
|
(Vec3){ (float)x, d3, (float)y - 1 },
|
||||||
(Vec2sh){ 1024, 768 },
|
(Vec2sh){ 128, 128 },
|
||||||
(Vec2sh){ 1024, 0 },
|
(Vec2sh){ 128, 0 },
|
||||||
(Vec2sh){ 0, 768 });
|
(Vec2sh){ 0, 128 });
|
||||||
|
|
||||||
unfurl_triangle("/assets/grass.gif",
|
unfurl_triangle("/assets/grass.png",
|
||||||
(Vec3){ (float)x + 1, d1, (float)y },
|
(Vec3){ (float)x + 1, d1, (float)y },
|
||||||
(Vec3){ (float)x + 1, d2, (float)y - 1 },
|
(Vec3){ (float)x + 1, d2, (float)y - 1 },
|
||||||
(Vec3){ (float)x, d3, (float)y - 1 },
|
(Vec3){ (float)x, d3, (float)y - 1 },
|
||||||
(Vec2sh){ 1024, 0 },
|
(Vec2sh){ 128, 0 },
|
||||||
(Vec2sh){ 0, 0 },
|
(Vec2sh){ 0, 0 },
|
||||||
(Vec2sh){ 0, 768 });
|
(Vec2sh){ 0, 128 });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
push_skybox("/assets/miramar/miramar_*.tga");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Binary file not shown.
Before Width: | Height: | Size: 373 KiB |
BIN
data/assets/grass.png
(Stored with Git LFS)
Normal file
BIN
data/assets/grass.png
(Stored with Git LFS)
Normal file
Binary file not shown.
15
data/assets/miramar/README.TXT
Normal file
15
data/assets/miramar/README.TXT
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
THIS SKY WAS UPDATED AT THE 27TH
|
||||||
|
THE ORIG HAD SOME ERRORS
|
||||||
|
|
||||||
|
MIRAMAR
|
||||||
|
high res 1024^2 environment map
|
||||||
|
ships as TGA.
|
||||||
|
|
||||||
|
|
||||||
|
By Jockum Skoglund aka hipshot
|
||||||
|
hipshot@zfight.com
|
||||||
|
www.zfight.com
|
||||||
|
Stockholm, 2005 08 25
|
||||||
|
|
||||||
|
|
||||||
|
Modify however you like, just cred me for my work, maybe link to my page.
|
BIN
data/assets/miramar/miramar_down.tga
(Stored with Git LFS)
Normal file
BIN
data/assets/miramar/miramar_down.tga
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
data/assets/miramar/miramar_east.tga
(Stored with Git LFS)
Normal file
BIN
data/assets/miramar/miramar_east.tga
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
data/assets/miramar/miramar_north.tga
(Stored with Git LFS)
Normal file
BIN
data/assets/miramar/miramar_north.tga
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
data/assets/miramar/miramar_south.tga
(Stored with Git LFS)
Normal file
BIN
data/assets/miramar/miramar_south.tga
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
data/assets/miramar/miramar_up.tga
(Stored with Git LFS)
Normal file
BIN
data/assets/miramar/miramar_up.tga
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
data/assets/miramar/miramar_west.tga
(Stored with Git LFS)
Normal file
BIN
data/assets/miramar/miramar_west.tga
(Stored with Git LFS)
Normal file
Binary file not shown.
@ -73,4 +73,7 @@ TWN_API void unfurl_triangle(const char *path,
|
|||||||
/* pushes a camera state to be used for all future unfurl_* commands */
|
/* pushes a camera state to be used for all future unfurl_* commands */
|
||||||
TWN_API void set_camera(const Camera *camera);
|
TWN_API void set_camera(const Camera *camera);
|
||||||
|
|
||||||
|
/* expects '*' masks that will be expanded to 6 names: 'up', 'down', 'east', 'west', 'north' and 'south' */
|
||||||
|
TWN_API void push_skybox(const char *paths);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
#include "twn_rendering_c.h"
|
#include "twn_rendering_c.h"
|
||||||
#include "twn_util.h"
|
#include "twn_util.h"
|
||||||
|
#include "twn_util_c.h"
|
||||||
#include "twn_config.h"
|
#include "twn_config.h"
|
||||||
#include "twn_engine_context_c.h"
|
#include "twn_engine_context_c.h"
|
||||||
#include "twn_text_c.h"
|
#include "twn_text_c.h"
|
||||||
@ -464,3 +465,152 @@ void finally_draw_text(FontData const *font_data,
|
|||||||
size_t get_text_payload_size(void) {
|
size_t get_text_payload_size(void) {
|
||||||
return sizeof (ElementIndexedQuadWithoutColor);
|
return sizeof (ElementIndexedQuadWithoutColor);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void load_cubemap_side(const char *path, GLenum target) {
|
||||||
|
SDL_Surface *surface = textures_load_surface(path);
|
||||||
|
/* TODO: sanity check whether all of them have same dimensions? */
|
||||||
|
glTexImage2D(target,
|
||||||
|
0,
|
||||||
|
GL_RGBA8,
|
||||||
|
surface->w, surface->h,
|
||||||
|
0,
|
||||||
|
surface->format->BytesPerPixel == 4 ? GL_RGBA : GL_RGB,
|
||||||
|
GL_UNSIGNED_BYTE,
|
||||||
|
surface->pixels);
|
||||||
|
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
||||||
|
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
|
||||||
|
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
||||||
|
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
||||||
|
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
|
||||||
|
|
||||||
|
SDL_free(surface->pixels);
|
||||||
|
SDL_FreeSurface(surface);
|
||||||
|
}
|
||||||
|
|
||||||
|
void finally_render_skybox(char *paths) {
|
||||||
|
static GLuint cubemap = 0;
|
||||||
|
static char *paths_cache = NULL;
|
||||||
|
|
||||||
|
bool loading_needed = false;
|
||||||
|
|
||||||
|
/* drop it */
|
||||||
|
if (!paths_cache || (SDL_strcmp(paths_cache, paths) != 0)) {
|
||||||
|
if (cubemap)
|
||||||
|
glDeleteTextures(1, &cubemap);
|
||||||
|
glGenTextures(1, &cubemap);
|
||||||
|
if (paths_cache)
|
||||||
|
SDL_free(paths_cache);
|
||||||
|
paths_cache = paths;
|
||||||
|
loading_needed = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
Matrix4 camera_look_at_matrix_solipsist = camera_look_at_matrix;
|
||||||
|
camera_look_at_matrix_solipsist.row[3].x = 0;
|
||||||
|
camera_look_at_matrix_solipsist.row[3].y = 0;
|
||||||
|
camera_look_at_matrix_solipsist.row[3].z = 0;
|
||||||
|
|
||||||
|
glMatrixMode(GL_MODELVIEW);
|
||||||
|
glLoadMatrixf(&camera_look_at_matrix_solipsist.row[0].x);
|
||||||
|
|
||||||
|
glDisable(GL_TEXTURE_2D);
|
||||||
|
glEnable(GL_TEXTURE_CUBE_MAP);
|
||||||
|
glBindTexture(GL_TEXTURE_CUBE_MAP, cubemap);
|
||||||
|
|
||||||
|
/* note: assumes that space pipeline is applied already */
|
||||||
|
glDisable(GL_ALPHA_TEST);
|
||||||
|
glDepthMask(GL_TRUE);
|
||||||
|
|
||||||
|
if (loading_needed) {
|
||||||
|
/* load all the sides */
|
||||||
|
char *expanded = expand_asterisk(paths, "up");
|
||||||
|
load_cubemap_side(expanded, GL_TEXTURE_CUBE_MAP_POSITIVE_Y);
|
||||||
|
SDL_free(expanded);
|
||||||
|
|
||||||
|
expanded = expand_asterisk(paths, "down");
|
||||||
|
load_cubemap_side(expanded, GL_TEXTURE_CUBE_MAP_NEGATIVE_Y);
|
||||||
|
SDL_free(expanded);
|
||||||
|
|
||||||
|
expanded = expand_asterisk(paths, "east");
|
||||||
|
load_cubemap_side(expanded, GL_TEXTURE_CUBE_MAP_POSITIVE_X);
|
||||||
|
SDL_free(expanded);
|
||||||
|
|
||||||
|
expanded = expand_asterisk(paths, "north");
|
||||||
|
load_cubemap_side(expanded, GL_TEXTURE_CUBE_MAP_POSITIVE_Z);
|
||||||
|
SDL_free(expanded);
|
||||||
|
|
||||||
|
expanded = expand_asterisk(paths, "west");
|
||||||
|
load_cubemap_side(expanded, GL_TEXTURE_CUBE_MAP_NEGATIVE_X);
|
||||||
|
SDL_free(expanded);
|
||||||
|
|
||||||
|
expanded = expand_asterisk(paths, "south");
|
||||||
|
load_cubemap_side(expanded, GL_TEXTURE_CUBE_MAP_NEGATIVE_Z);
|
||||||
|
SDL_free(expanded);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* TODO: use lists at the very least */
|
||||||
|
/* TODO: figure out which coordinates to use to not have issues with far z */
|
||||||
|
|
||||||
|
glBegin(GL_QUADS); {
|
||||||
|
/* up */
|
||||||
|
glTexCoord3f(50.f, 50.f, 50.f);
|
||||||
|
glVertex3f(50.f, 50.f, 50.f);
|
||||||
|
glTexCoord3f(-50.f, 50.f, 50.f);
|
||||||
|
glVertex3f(-50.f, 50.f, 50.f);
|
||||||
|
glTexCoord3f(-50.f, 50.f, -50.f);
|
||||||
|
glVertex3f(-50.f, 50.f, -50.f);
|
||||||
|
glTexCoord3f(50.f, 50.f, -50.f);
|
||||||
|
glVertex3f(50.f, 50.f, -50.f);
|
||||||
|
|
||||||
|
/* down */
|
||||||
|
glTexCoord3f(50.f, -50.f, 50.f);
|
||||||
|
glVertex3f(50.f, -50.f, 50.f);
|
||||||
|
glTexCoord3f(50.f, -50.f, -50.f);
|
||||||
|
glVertex3f(50.f, -50.f, -50.f);
|
||||||
|
glTexCoord3f(-50.f, -50.f, -50.f);
|
||||||
|
glVertex3f(-50.f, -50.f, -50.f);
|
||||||
|
glTexCoord3f(-50.f, -50.f, 50.f);
|
||||||
|
glVertex3f(-50.f, -50.f, 50.f);
|
||||||
|
|
||||||
|
/* east */
|
||||||
|
glTexCoord3f(50.f, -50.f, 50.f);
|
||||||
|
glVertex3f(50.f, -50.f, 50.f);
|
||||||
|
glTexCoord3f(50.f, 50.f, 50.f);
|
||||||
|
glVertex3f(50.f, 50.f, 50.f);
|
||||||
|
glTexCoord3f(50.f, 50.f, -50.f);
|
||||||
|
glVertex3f(50.f, 50.f, -50.f);
|
||||||
|
glTexCoord3f(50.f, -50.f, -50.f);
|
||||||
|
glVertex3f(50.f, -50.f, -50.f);
|
||||||
|
|
||||||
|
/* west */
|
||||||
|
glTexCoord3f(-50.f, -50.f, 50.f);
|
||||||
|
glVertex3f(-50.f, -50.f, 50.f);
|
||||||
|
glTexCoord3f(-50.f, -50.f, -50.f);
|
||||||
|
glVertex3f(-50.f, -50.f, -50.f);
|
||||||
|
glTexCoord3f(-50.f, 50.f, -50.f);
|
||||||
|
glVertex3f(-50.f, 50.f, -50.f);
|
||||||
|
glTexCoord3f(-50.f, 50.f, 50.f);
|
||||||
|
glVertex3f(-50.f, 50.f, 50.f);
|
||||||
|
|
||||||
|
/* north */
|
||||||
|
glTexCoord3f(-50.f, -50.f, 50.f);
|
||||||
|
glVertex3f(-50.f, -50.f, 50.f);
|
||||||
|
glTexCoord3f(-50.f, 50.f, 50.f);
|
||||||
|
glVertex3f(-50.f, 50.f, 50.f);
|
||||||
|
glTexCoord3f(50.f, 50.f, 50.f);
|
||||||
|
glVertex3f(50.f, 50.f, 50.f);
|
||||||
|
glTexCoord3f(50.f, -50.f, 50.f);
|
||||||
|
glVertex3f(50.f, -50.f, 50.f);
|
||||||
|
|
||||||
|
/* south */
|
||||||
|
glTexCoord3f(-50.f, -50.f, -50.f);
|
||||||
|
glVertex3f(-50.f, -50.f, -50.f);
|
||||||
|
glTexCoord3f(50.f, -50.f, -50.f);
|
||||||
|
glVertex3f(50.f, -50.f, -50.f);
|
||||||
|
glTexCoord3f(50.f, 50.f, -50.f);
|
||||||
|
glVertex3f(50.f, 50.f, -50.f);
|
||||||
|
glTexCoord3f(-50.f, 50.f, -50.f);
|
||||||
|
glVertex3f(-50.f, 50.f, -50.f);
|
||||||
|
} glEnd();
|
||||||
|
|
||||||
|
glDisable(GL_TEXTURE_CUBE_MAP);
|
||||||
|
}
|
||||||
|
@ -70,6 +70,7 @@ void clear_draw_buffer(void) {
|
|||||||
(1.0f / 255) * 230,
|
(1.0f / 255) * 230,
|
||||||
(1.0f / 255) * 230, 1);
|
(1.0f / 255) * 230, 1);
|
||||||
|
|
||||||
|
/* TODO: don't clear color when skybox is applied? */
|
||||||
glClear(GL_COLOR_BUFFER_BIT |
|
glClear(GL_COLOR_BUFFER_BIT |
|
||||||
GL_DEPTH_BUFFER_BIT |
|
GL_DEPTH_BUFFER_BIT |
|
||||||
GL_STENCIL_BUFFER_BIT);
|
GL_STENCIL_BUFFER_BIT);
|
||||||
|
@ -135,6 +135,7 @@ void render(void) {
|
|||||||
|
|
||||||
clear_draw_buffer();
|
clear_draw_buffer();
|
||||||
render_space();
|
render_space();
|
||||||
|
render_skybox(); /* after space, as to use depth buffer for early rejection */
|
||||||
render_2d();
|
render_2d();
|
||||||
swap_buffers();
|
swap_buffers();
|
||||||
}
|
}
|
||||||
|
@ -219,4 +219,8 @@ void finally_draw_text(FontData const *font_data,
|
|||||||
Color color,
|
Color color,
|
||||||
VertexBuffer buffer);
|
VertexBuffer buffer);
|
||||||
|
|
||||||
|
void render_skybox(void);
|
||||||
|
|
||||||
|
void finally_render_skybox(char *paths_in_use);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
26
src/rendering/twn_skybox.c
Normal file
26
src/rendering/twn_skybox.c
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
#include "twn_rendering.h"
|
||||||
|
#include "twn_rendering_c.h"
|
||||||
|
|
||||||
|
#include <SDL2/SDL.h>
|
||||||
|
|
||||||
|
char *paths_in_use;
|
||||||
|
|
||||||
|
void push_skybox(const char *paths) {
|
||||||
|
if (paths_in_use && SDL_strcmp(paths, paths_in_use) == 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (paths_in_use)
|
||||||
|
SDL_free(paths_in_use);
|
||||||
|
|
||||||
|
paths_in_use = SDL_strdup(paths);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void render_skybox(void) {
|
||||||
|
if (!paths_in_use)
|
||||||
|
return;
|
||||||
|
|
||||||
|
/* note: ownership of 'paths_in_use' goes there */
|
||||||
|
finally_render_skybox(paths_in_use);
|
||||||
|
paths_in_use = NULL;
|
||||||
|
}
|
@ -42,7 +42,7 @@ static int load_eof_callback(void *user) {
|
|||||||
return context->position == context->size;
|
return context->position == context->size;
|
||||||
}
|
}
|
||||||
|
|
||||||
static SDL_Surface *image_to_surface(const char *path) {
|
SDL_Surface *textures_load_surface(const char *path) {
|
||||||
SDL_RWops *handle = PHYSFSRWOPS_openRead(path);
|
SDL_RWops *handle = PHYSFSRWOPS_openRead(path);
|
||||||
if (handle == NULL)
|
if (handle == NULL)
|
||||||
goto ERR_CANNOT_OPEN_FILE;
|
goto ERR_CANNOT_OPEN_FILE;
|
||||||
@ -359,7 +359,7 @@ static TextureKey textures_load(TextureCache *cache, const char *path) {
|
|||||||
if (i >= 0)
|
if (i >= 0)
|
||||||
return (TextureKey){ (uint16_t)i };
|
return (TextureKey){ (uint16_t)i };
|
||||||
|
|
||||||
SDL_Surface *surface = image_to_surface(path);
|
SDL_Surface *surface = textures_load_surface(path);
|
||||||
Texture new_texture = {
|
Texture new_texture = {
|
||||||
.data = surface,
|
.data = surface,
|
||||||
.mode = infer_texture_mode(surface),
|
.mode = infer_texture_mode(surface),
|
||||||
|
@ -89,4 +89,8 @@ size_t textures_get_num_atlases(const TextureCache *cache);
|
|||||||
/* TODO: should recieve texture_cache, get_key optimization cache should be cleared some other way */
|
/* TODO: should recieve texture_cache, get_key optimization cache should be cleared some other way */
|
||||||
void textures_reset_state(void);
|
void textures_reset_state(void);
|
||||||
|
|
||||||
|
/* uncached low-level loading */
|
||||||
|
/* warn: surface->pixels must be freed along side the surface itself */
|
||||||
|
SDL_Surface *textures_load_surface(const char *path);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
@ -253,3 +253,20 @@ bool repeat_ftimer(float *value, float at) {
|
|||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* TODO: handle utf8 */
|
||||||
|
char *expand_asterisk(const char *mask, const char *to) {
|
||||||
|
const char *offset = SDL_strchr(mask, '*');
|
||||||
|
if (!offset) {
|
||||||
|
CRY("Invalid path", "Asterisk should be given.");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
size_t const mask_len = SDL_strlen(mask);
|
||||||
|
size_t const to_len = SDL_strlen(to);
|
||||||
|
char *str = SDL_malloc(mask_len + to_len); /* NULL included, replacing the original asterisk */
|
||||||
|
SDL_memcpy(str, mask, offset - mask);
|
||||||
|
SDL_memcpy(str + (offset - mask), to, to_len);
|
||||||
|
SDL_memcpy(str + (offset - mask) + to_len, offset + 1, mask_len - (offset - mask));
|
||||||
|
str[mask_len + to_len] = '\0';
|
||||||
|
return str;
|
||||||
|
}
|
||||||
|
7
src/twn_util_c.h
Normal file
7
src/twn_util_c.h
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
#ifndef TWN_UTIL_C_H
|
||||||
|
#define TWN_UTIL_C_H
|
||||||
|
|
||||||
|
/* note: you must free the returned string */
|
||||||
|
char *expand_asterisk(const char *mask, const char *to);
|
||||||
|
|
||||||
|
#endif
|
Loading…
Reference in New Issue
Block a user