mirror of
https://github.com/libsm64/libsm64.git
synced 2025-01-22 23:52:11 -05:00
Loading and animating mario textures from rom
This commit is contained in:
parent
75b12d47f1
commit
8f893d6c0b
19 changed files with 11402 additions and 15 deletions
2
configure
vendored
2
configure
vendored
|
@ -5,7 +5,7 @@ cd "$(dirname "${BASH_SOURCE[0]}")"
|
||||||
CC='cc -g'
|
CC='cc -g'
|
||||||
CFLAGS='-Wall -fPIC'
|
CFLAGS='-Wall -fPIC'
|
||||||
BIN_FILE='dist/libsm64.so'
|
BIN_FILE='dist/libsm64.so'
|
||||||
LDFLAGS=''
|
LDFLAGS='-lm'
|
||||||
|
|
||||||
c_to_obj() {
|
c_to_obj() {
|
||||||
printf 'build/'
|
printf 'build/'
|
||||||
|
|
|
@ -69,11 +69,12 @@ def main():
|
||||||
|
|
||||||
lines = model_inc_c.splitlines()
|
lines = model_inc_c.splitlines()
|
||||||
|
|
||||||
for line in lines:
|
for i in range(len(lines)):
|
||||||
if line.startswith("const "):
|
lines[i] = lines[i].replace("#include", "//#include")
|
||||||
model_inc_h += "\nextern " + line.replace(" = {", ";")
|
lines[i] = lines[i].replace("ALIGNED8 static const u8 mario", "static const u8 xxx")
|
||||||
|
if lines[i].startswith("const "):
|
||||||
|
model_inc_h += "\nextern " + lines[i].replace(" = {", ";")
|
||||||
|
|
||||||
lines = [ x.replace("#include", "//#include") for x in lines ]
|
|
||||||
lines.insert(0, "#include \"../model_hack.h\"")
|
lines.insert(0, "#include \"../model_hack.h\"")
|
||||||
model_inc_c = "\n".join(lines)
|
model_inc_c = "\n".join(lines)
|
||||||
|
|
||||||
|
@ -89,4 +90,5 @@ def main():
|
||||||
with open("src/mario/geo.inc.h", "w") as file:
|
with open("src/mario/geo.inc.h", "w") as file:
|
||||||
file.write(geo_inc_h)
|
file.write(geo_inc_h)
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
main()
|
main()
|
|
@ -9,10 +9,18 @@
|
||||||
|
|
||||||
static Mat4 s_curMatrix;
|
static Mat4 s_curMatrix;
|
||||||
static float s_curColor[3];
|
static float s_curColor[3];
|
||||||
|
|
||||||
|
static uint16_t s_scaleS, s_scaleT, s_uls, s_ult;
|
||||||
|
static int s_textureOn, s_textureIndex;
|
||||||
|
static float s_texWidth;
|
||||||
|
static float s_texHeight;
|
||||||
|
|
||||||
static struct SM64MarioGeometryBuffers *s_outBuffers;
|
static struct SM64MarioGeometryBuffers *s_outBuffers;
|
||||||
|
|
||||||
static float *s_trianglePtr;
|
static float *s_trianglePtr;
|
||||||
static float *s_colorPtr;
|
static float *s_colorPtr;
|
||||||
static float *s_normalPtr;
|
static float *s_normalPtr;
|
||||||
|
static float *s_uvPtr;
|
||||||
|
|
||||||
static void mtxf_mul_vec3f(Mat4 mtx, Vec3f b, float w, Vec3f out)
|
static void mtxf_mul_vec3f(Mat4 mtx, Vec3f b, float w, Vec3f out)
|
||||||
{
|
{
|
||||||
|
@ -21,6 +29,16 @@ static void mtxf_mul_vec3f(Mat4 mtx, Vec3f b, float w, Vec3f out)
|
||||||
out[2] = b[0] * mtx[0][2] + b[1] * mtx[1][2] + b[2] * mtx[2][2] + w * mtx[3][2];
|
out[2] = b[0] * mtx[0][2] + b[1] * mtx[1][2] + b[2] * mtx[2][2] + w * mtx[3][2];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void convert_uv_to_atlas( float *atlas_uv_out, short tc[] )
|
||||||
|
{
|
||||||
|
float u = (float)((tc[0] * s_scaleS >> 16) - 8*s_uls) / 32.0f / s_texWidth;
|
||||||
|
float v = (float)((tc[1] * s_scaleT >> 16) - 8*s_ult) / 32.0f / s_texHeight;
|
||||||
|
|
||||||
|
// TODO define 11 (number of used textures)
|
||||||
|
atlas_uv_out[0] = u * s_texWidth / 64.0f / 11.0f + (float)s_textureIndex / 11.0f;
|
||||||
|
atlas_uv_out[1] = v * s_texHeight / 64.0f;
|
||||||
|
}
|
||||||
|
|
||||||
static void process_display_list( void *dl )
|
static void process_display_list( void *dl )
|
||||||
{
|
{
|
||||||
int64_t *ptr = (int64_t *)dl;
|
int64_t *ptr = (int64_t *)dl;
|
||||||
|
@ -88,6 +106,22 @@ static void process_display_list( void *dl )
|
||||||
*s_colorPtr++ = s_curColor[1];
|
*s_colorPtr++ = s_curColor[1];
|
||||||
*s_colorPtr++ = s_curColor[2];
|
*s_colorPtr++ = s_curColor[2];
|
||||||
|
|
||||||
|
if( s_textureOn )
|
||||||
|
{
|
||||||
|
convert_uv_to_atlas( s_uvPtr, vdata[v00].v.tc ); s_uvPtr += 2;
|
||||||
|
convert_uv_to_atlas( s_uvPtr, vdata[v01].v.tc ); s_uvPtr += 2;
|
||||||
|
convert_uv_to_atlas( s_uvPtr, vdata[v02].v.tc ); s_uvPtr += 2;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
*s_uvPtr++ = 1.0f;
|
||||||
|
*s_uvPtr++ = 1.0f;
|
||||||
|
*s_uvPtr++ = 1.0f;
|
||||||
|
*s_uvPtr++ = 1.0f;
|
||||||
|
*s_uvPtr++ = 1.0f;
|
||||||
|
*s_uvPtr++ = 1.0f;
|
||||||
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -107,6 +141,43 @@ static void process_display_list( void *dl )
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
case GFXCMD_Texture:
|
||||||
|
{
|
||||||
|
int64_t s = *ptr++;
|
||||||
|
int64_t t = *ptr++;
|
||||||
|
int64_t on = *ptr++;
|
||||||
|
|
||||||
|
s_scaleS = (uint16_t)s;
|
||||||
|
s_scaleT = (uint16_t)t;
|
||||||
|
s_textureOn = (int)on;
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case GFXCMD_SetTextureImage:
|
||||||
|
{
|
||||||
|
int64_t i = *ptr++;
|
||||||
|
|
||||||
|
s_textureIndex = (int)i;
|
||||||
|
s_texWidth = 32.0f;
|
||||||
|
s_texHeight = 32.0f;
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case GFXCMD_SetTileSize:
|
||||||
|
{
|
||||||
|
int64_t uls = *ptr++;
|
||||||
|
int64_t ult = *ptr++;
|
||||||
|
int64_t lrs = *ptr++;
|
||||||
|
int64_t lrt = *ptr++;
|
||||||
|
|
||||||
|
s_uls = (uint16_t)uls;
|
||||||
|
s_ult = (uint16_t)ult;
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
case GFXCMD_SubDisplayList:
|
case GFXCMD_SubDisplayList:
|
||||||
{
|
{
|
||||||
int64_t dl = *ptr++;
|
int64_t dl = *ptr++;
|
||||||
|
@ -141,5 +212,6 @@ void gfx_adapter_bind_output_buffers( struct SM64MarioGeometryBuffers *outBuffer
|
||||||
s_trianglePtr = s_outBuffers->position;
|
s_trianglePtr = s_outBuffers->position;
|
||||||
s_colorPtr = s_outBuffers->color;
|
s_colorPtr = s_outBuffers->color;
|
||||||
s_normalPtr = s_outBuffers->normal;
|
s_normalPtr = s_outBuffers->normal;
|
||||||
|
s_uvPtr = s_outBuffers->uv;
|
||||||
s_outBuffers->bufferUsedSize = 0;
|
s_outBuffers->bufferUsedSize = 0;
|
||||||
}
|
}
|
|
@ -6,6 +6,9 @@ enum GFXAdapterCommands
|
||||||
GFXCMD_VertexData,
|
GFXCMD_VertexData,
|
||||||
GFXCMD_Triangle,
|
GFXCMD_Triangle,
|
||||||
GFXCMD_Light,
|
GFXCMD_Light,
|
||||||
|
GFXCMD_Texture,
|
||||||
|
GFXCMD_SetTileSize,
|
||||||
|
GFXCMD_SetTextureImage,
|
||||||
GFXCMD_SubDisplayList,
|
GFXCMD_SubDisplayList,
|
||||||
GFXCMD_EndDisplayList,
|
GFXCMD_EndDisplayList,
|
||||||
};
|
};
|
|
@ -17,10 +17,10 @@
|
||||||
#include "engine/graph_node.h"
|
#include "engine/graph_node.h"
|
||||||
#include "engine/geo_layout.h"
|
#include "engine/geo_layout.h"
|
||||||
#include "game/rendering_graph_node.h"
|
#include "game/rendering_graph_node.h"
|
||||||
//#include "mario/anim_data.h"
|
|
||||||
#include "mario/geo.inc.h"
|
#include "mario/geo.inc.h"
|
||||||
#include "gfx_adapter.h"
|
#include "gfx_adapter.h"
|
||||||
#include "load_anim_data.h"
|
#include "load_anim_data.h"
|
||||||
|
#include "load_tex_data.h"
|
||||||
|
|
||||||
static struct AllocOnlyPool *s_mario_geo_pool;
|
static struct AllocOnlyPool *s_mario_geo_pool;
|
||||||
static struct GraphNode *s_mario_graph_node;
|
static struct GraphNode *s_mario_graph_node;
|
||||||
|
@ -82,6 +82,7 @@ void sm64_global_init( uint8_t *rom, SM64DebugPrintFunctionPtr debugPrintFunctio
|
||||||
{
|
{
|
||||||
gDebugPrint = debugPrintFunction;
|
gDebugPrint = debugPrintFunction;
|
||||||
|
|
||||||
|
load_mario_textures_from_rom( rom );
|
||||||
load_mario_anims_from_rom( rom );
|
load_mario_anims_from_rom( rom );
|
||||||
|
|
||||||
gMarioObject = hack_allocate_mario();
|
gMarioObject = hack_allocate_mario();
|
||||||
|
@ -98,6 +99,11 @@ void sm64_global_init( uint8_t *rom, SM64DebugPrintFunctionPtr debugPrintFunctio
|
||||||
//DEBUG_LOG( "Mario animations loaded from address %lu", (uint64_t)D_80339D10.animDmaTable );
|
//DEBUG_LOG( "Mario animations loaded from address %lu", (uint64_t)D_80339D10.animDmaTable );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
uint8_t *sm64_get_texture( void )
|
||||||
|
{
|
||||||
|
return gLibSm64TextureRgba;
|
||||||
|
}
|
||||||
|
|
||||||
void sm64_load_surfaces( uint16_t terrainType, const struct SM64Surface *surfaceArray, size_t numSurfaces )
|
void sm64_load_surfaces( uint16_t terrainType, const struct SM64Surface *surfaceArray, size_t numSurfaces )
|
||||||
{
|
{
|
||||||
surface_load_for_libsm64( surfaceArray, numSurfaces );
|
surface_load_for_libsm64( surfaceArray, numSurfaces );
|
||||||
|
|
|
@ -36,11 +36,13 @@ struct SM64MarioGeometryBuffers
|
||||||
float *position;
|
float *position;
|
||||||
float *normal;
|
float *normal;
|
||||||
float *color;
|
float *color;
|
||||||
|
float *uv;
|
||||||
};
|
};
|
||||||
|
|
||||||
typedef void (*SM64DebugPrintFunctionPtr)( const char * );
|
typedef void (*SM64DebugPrintFunctionPtr)( const char * );
|
||||||
|
|
||||||
extern void sm64_global_init( uint8_t *rom, SM64DebugPrintFunctionPtr debugPrintFunction );
|
extern void sm64_global_init( uint8_t *rom, SM64DebugPrintFunctionPtr debugPrintFunction );
|
||||||
|
extern uint8_t *sm64_get_texture( void );
|
||||||
extern void sm64_load_surfaces( uint16_t terrainType, const struct SM64Surface *surfaceArray, size_t numSurfaces );
|
extern void sm64_load_surfaces( uint16_t terrainType, const struct SM64Surface *surfaceArray, size_t numSurfaces );
|
||||||
extern void sm64_mario_reset( int16_t marioX, int16_t marioY, int16_t marioZ );
|
extern void sm64_mario_reset( int16_t marioX, int16_t marioY, int16_t marioZ );
|
||||||
extern void sm64_mario_tick( const struct SM64MarioInputs *inputs, struct SM64MarioState *outState, struct SM64MarioGeometryBuffers *outBuffers );
|
extern void sm64_mario_tick( const struct SM64MarioInputs *inputs, struct SM64MarioState *outState, struct SM64MarioGeometryBuffers *outBuffers );
|
||||||
|
|
|
@ -8,7 +8,7 @@ static struct Animation *s_marioAnimations;
|
||||||
|
|
||||||
#define ANIM_DATA_ADDRESS 0x004EC000
|
#define ANIM_DATA_ADDRESS 0x004EC000
|
||||||
|
|
||||||
uint16_t read_u16_be( uint8_t *p )
|
static uint16_t read_u16_be( uint8_t *p )
|
||||||
{
|
{
|
||||||
return
|
return
|
||||||
(uint32_t)p[0] << 8 |
|
(uint32_t)p[0] << 8 |
|
||||||
|
@ -16,12 +16,12 @@ uint16_t read_u16_be( uint8_t *p )
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
uint16_t read_s16_be( uint8_t *p )
|
static uint16_t read_s16_be( uint8_t *p )
|
||||||
{
|
{
|
||||||
return (int16_t)read_u16_be( p );
|
return (int16_t)read_u16_be( p );
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t read_u32_be( uint8_t *p )
|
static uint32_t read_u32_be( uint8_t *p )
|
||||||
{
|
{
|
||||||
return
|
return
|
||||||
(uint32_t)p[0] << 24 |
|
(uint32_t)p[0] << 24 |
|
||||||
|
|
|
@ -6,6 +6,5 @@
|
||||||
#include "include/types.h"
|
#include "include/types.h"
|
||||||
|
|
||||||
extern struct Animation *gLibSm64MarioAnimations;
|
extern struct Animation *gLibSm64MarioAnimations;
|
||||||
//extern void *gMarioAnimsPtr;
|
|
||||||
|
|
||||||
extern void load_mario_anims_from_rom( uint8_t *rom );
|
extern void load_mario_anims_from_rom( uint8_t *rom );
|
55
src/load_tex_data.c
Normal file
55
src/load_tex_data.c
Normal file
|
@ -0,0 +1,55 @@
|
||||||
|
#include "load_tex_data.h"
|
||||||
|
|
||||||
|
#include <stddef.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include "tools/libmio0.h"
|
||||||
|
#include "tools/n64graphics.h"
|
||||||
|
|
||||||
|
uint8_t *gLibSm64TextureRgba;
|
||||||
|
|
||||||
|
#define MARIO_TEX_ROM_OFFSET 1132368
|
||||||
|
#define NUM_USED_TEXTURES 11
|
||||||
|
#define ATLAS_WIDTH (NUM_USED_TEXTURES * 64)
|
||||||
|
#define ATLAS_HEIGHT 64
|
||||||
|
|
||||||
|
static int mario_tex_offsets[NUM_USED_TEXTURES] = { 144, 4240, 6288, 8336, 10384, 12432, 14480, 16528, 30864, 32912, 37008 };
|
||||||
|
static int mario_tex_widths [NUM_USED_TEXTURES] = { 64, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32 };
|
||||||
|
static int mario_tex_heights[NUM_USED_TEXTURES] = { 32, 32, 32, 32, 32, 32, 32, 32, 32, 64, 64 };
|
||||||
|
|
||||||
|
static void blt_image_to_atlas( rgba *img, int i, int w, int h )
|
||||||
|
{
|
||||||
|
for( int iy = 0; iy < h; ++iy )
|
||||||
|
for( int ix = 0; ix < w; ++ix )
|
||||||
|
{
|
||||||
|
int o = (ix + 64 * i) + iy * ATLAS_WIDTH;
|
||||||
|
int q = ix + iy * w;
|
||||||
|
gLibSm64TextureRgba[4*o + 0] = img[q].red;
|
||||||
|
gLibSm64TextureRgba[4*o + 1] = img[q].green;
|
||||||
|
gLibSm64TextureRgba[4*o + 2] = img[q].blue;
|
||||||
|
gLibSm64TextureRgba[4*o + 3] = img[q].alpha;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void load_mario_textures_from_rom( uint8_t *rom )
|
||||||
|
{
|
||||||
|
gLibSm64TextureRgba = malloc( 4 * ATLAS_WIDTH * ATLAS_HEIGHT );
|
||||||
|
memset( gLibSm64TextureRgba, 0, 4 * ATLAS_WIDTH * ATLAS_HEIGHT );
|
||||||
|
|
||||||
|
mio0_header_t head;
|
||||||
|
uint8_t *in_buf = rom + MARIO_TEX_ROM_OFFSET;
|
||||||
|
|
||||||
|
mio0_decode_header( in_buf, &head );
|
||||||
|
uint8_t *out_buf = malloc( head.dest_size );
|
||||||
|
int bytes_read = mio0_decode( in_buf, out_buf, NULL );
|
||||||
|
|
||||||
|
for( int i = 0; i < NUM_USED_TEXTURES; ++i )
|
||||||
|
{
|
||||||
|
uint8_t *raw = out_buf + mario_tex_offsets[i];
|
||||||
|
rgba *img = raw2rgba( raw, mario_tex_widths[i], mario_tex_heights[i], 16 );
|
||||||
|
blt_image_to_atlas( img, i, mario_tex_widths[i], mario_tex_heights[i] );
|
||||||
|
free( img );
|
||||||
|
}
|
||||||
|
|
||||||
|
free( out_buf );
|
||||||
|
}
|
29
src/load_tex_data.h
Normal file
29
src/load_tex_data.h
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
enum MarioTextures
|
||||||
|
{
|
||||||
|
mario_texture_metal = 0,
|
||||||
|
mario_texture_yellow_button,
|
||||||
|
mario_texture_m_logo,
|
||||||
|
mario_texture_hair_sideburn,
|
||||||
|
mario_texture_mustache,
|
||||||
|
mario_texture_eyes_front,
|
||||||
|
mario_texture_eyes_half_closed,
|
||||||
|
mario_texture_eyes_closed,
|
||||||
|
mario_texture_eyes_dead,
|
||||||
|
mario_texture_wings_half_1,
|
||||||
|
mario_texture_wings_half_2,
|
||||||
|
mario_texture_metal_wings_half_1 = -99,
|
||||||
|
mario_texture_metal_wings_half_2,
|
||||||
|
mario_texture_eyes_closed_unused1,
|
||||||
|
mario_texture_eyes_closed_unused2,
|
||||||
|
mario_texture_eyes_right,
|
||||||
|
mario_texture_eyes_left,
|
||||||
|
mario_texture_eyes_up,
|
||||||
|
mario_texture_eyes_down
|
||||||
|
};
|
||||||
|
|
||||||
|
extern uint8_t *gLibSm64TextureRgba;
|
||||||
|
extern void load_mario_textures_from_rom( uint8_t *rom );
|
|
@ -69,7 +69,12 @@ typedef struct {
|
||||||
Light l[1];
|
Light l[1];
|
||||||
} Lights1;
|
} Lights1;
|
||||||
|
|
||||||
//static const Lights1 mario_blue_lights_group = gdSPDefLights1( 0x00, 0x00, 0x7f, 0x00, 0x00, 0xff, 0x28, 0x28, 0x28);
|
|
||||||
|
#define G_TX_RENDERTILE 0
|
||||||
|
#define G_ON (1)
|
||||||
|
#define G_OFF (0)
|
||||||
|
#define G_TEXTURE_IMAGE_FRAC 2
|
||||||
|
|
||||||
#define gdSPDefLights1(ar,ag,ab,r1,g1,b1,x1,y1,z1) {{{ {ar,ag,ab},0,{ar,ag,ab},0}}, {{{ {r1,g1,b1},0,{r1,g1,b1},0,{x1,y1,z1},0}}} }
|
#define gdSPDefLights1(ar,ag,ab,r1,g1,b1,x1,y1,z1) {{{ {ar,ag,ab},0,{ar,ag,ab},0}}, {{{ {r1,g1,b1},0,{r1,g1,b1},0,{x1,y1,z1},0}}} }
|
||||||
|
|
||||||
#define gsSPVertex(v, n, v0) \
|
#define gsSPVertex(v, n, v0) \
|
||||||
|
@ -97,18 +102,27 @@ typedef struct {
|
||||||
GFXCMD_Light, \
|
GFXCMD_Light, \
|
||||||
(int64_t)l, n
|
(int64_t)l, n
|
||||||
|
|
||||||
|
#define gsSPTexture(s, t, level, tile, on) \
|
||||||
|
GFXCMD_Texture, \
|
||||||
|
s, t, on
|
||||||
|
|
||||||
|
#define gsDPSetTextureImage(f, s, w, i) \
|
||||||
|
GFXCMD_SetTextureImage, \
|
||||||
|
i
|
||||||
|
|
||||||
|
#define gsDPSetTileSize(t, uls, ult, lrs, lrt) \
|
||||||
|
GFXCMD_SetTileSize, \
|
||||||
|
uls, ult, lrs, lrt
|
||||||
|
|
||||||
#define gsDPPipeSync() (GFXCMD_None)
|
#define gsDPPipeSync() (GFXCMD_None)
|
||||||
#define gsDPSetCombineMode(a, b) (GFXCMD_None)
|
#define gsDPSetCombineMode(a, b) (GFXCMD_None)
|
||||||
#define gsSPSetGeometryMode(word) (GFXCMD_None)
|
#define gsSPSetGeometryMode(word) (GFXCMD_None)
|
||||||
#define gsDPLoadTextureBlock(timg, fmt, siz, width, height, pal, cms, cmt, masks, maskt, shifts, shiftt) (GFXCMD_None)
|
#define gsDPLoadTextureBlock(timg, fmt, siz, width, height, pal, cms, cmt, masks, maskt, shifts, shiftt) (GFXCMD_None)
|
||||||
#define gsSPTexture(s, t, level, tile, on) (GFXCMD_None)
|
|
||||||
#define gsSPClearGeometryMode(word) (GFXCMD_None)
|
#define gsSPClearGeometryMode(word) (GFXCMD_None)
|
||||||
#define gsDPSetEnvColor(r, g, b, a) (GFXCMD_None)
|
#define gsDPSetEnvColor(r, g, b, a) (GFXCMD_None)
|
||||||
#define gsDPSetAlphaCompare(type) (GFXCMD_None)
|
#define gsDPSetAlphaCompare(type) (GFXCMD_None)
|
||||||
#define gsDPTileSync() (GFXCMD_None)
|
#define gsDPTileSync() (GFXCMD_None)
|
||||||
#define gsDPSetTile(fmt, siz, line, tmem, tile, palette, cmt, maskt, shiftt, cms, masks, shifts) (GFXCMD_None)
|
#define gsDPSetTile(fmt, siz, line, tmem, tile, palette, cmt, maskt, shiftt, cms, masks, shifts) (GFXCMD_None)
|
||||||
#define gsDPSetTileSize(t, uls, ult, lrs, lrt) (GFXCMD_None)
|
|
||||||
#define gsDPSetTextureImage(f, s, w, i) (GFXCMD_None)
|
|
||||||
#define gsDPLoadBlock(tile, uls, ult, lrs, dxt) (GFXCMD_None)
|
#define gsDPLoadBlock(tile, uls, ult, lrs, dxt) (GFXCMD_None)
|
||||||
#define gsDPLoadSync() (GFXCMD_None)
|
#define gsDPLoadSync() (GFXCMD_None)
|
||||||
|
|
||||||
|
|
575
src/tools/libmio0.c
Normal file
575
src/tools/libmio0.c
Normal file
|
@ -0,0 +1,575 @@
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#if defined(_WIN32) || defined(_WIN64)
|
||||||
|
#include <io.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include "libmio0.h"
|
||||||
|
#include "utils.h"
|
||||||
|
|
||||||
|
// defines
|
||||||
|
|
||||||
|
#define MIO0_VERSION "0.1"
|
||||||
|
|
||||||
|
#define GET_BIT(buf, bit) ((buf)[(bit) / 8] & (1 << (7 - ((bit) % 8))))
|
||||||
|
|
||||||
|
// types
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
int *indexes;
|
||||||
|
int allocated;
|
||||||
|
int count;
|
||||||
|
int start;
|
||||||
|
} lookback;
|
||||||
|
|
||||||
|
// functions
|
||||||
|
#define LOOKBACK_COUNT 256
|
||||||
|
#define LOOKBACK_INIT_SIZE 128
|
||||||
|
static lookback *lookback_init(void)
|
||||||
|
{
|
||||||
|
lookback *lb = malloc(LOOKBACK_COUNT * sizeof(*lb));
|
||||||
|
for (int i = 0; i < LOOKBACK_COUNT; i++) {
|
||||||
|
lb[i].allocated = LOOKBACK_INIT_SIZE;
|
||||||
|
lb[i].indexes = malloc(lb[i].allocated * sizeof(*lb[i].indexes));
|
||||||
|
lb[i].count = 0;
|
||||||
|
lb[i].start = 0;
|
||||||
|
}
|
||||||
|
return lb;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void lookback_free(lookback *lb)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < LOOKBACK_COUNT; i++) {
|
||||||
|
free(lb[i].indexes);
|
||||||
|
}
|
||||||
|
free(lb);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void lookback_push(lookback *lkbk, unsigned char val, int index)
|
||||||
|
{
|
||||||
|
lookback *lb = &lkbk[val];
|
||||||
|
if (lb->count == lb->allocated) {
|
||||||
|
lb->allocated *= 4;
|
||||||
|
lb->indexes = realloc(lb->indexes, lb->allocated * sizeof(*lb->indexes));
|
||||||
|
}
|
||||||
|
lb->indexes[lb->count++] = index;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void PUT_BIT(unsigned char *buf, int bit, int val)
|
||||||
|
{
|
||||||
|
unsigned char mask = 1 << (7 - (bit % 8));
|
||||||
|
unsigned int offset = bit / 8;
|
||||||
|
buf[offset] = (buf[offset] & ~(mask)) | (val ? mask : 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// used to find longest matching stream in buffer
|
||||||
|
// buf: buffer
|
||||||
|
// start_offset: offset in buf to look back from
|
||||||
|
// max_search: max number of bytes to find
|
||||||
|
// found_offset: returned offset found (0 if none found)
|
||||||
|
// returns max length of matching stream (0 if none found)
|
||||||
|
static int find_longest(const unsigned char *buf, int start_offset, int max_search, int *found_offset, lookback *lkbk)
|
||||||
|
{
|
||||||
|
int best_length = 0;
|
||||||
|
int best_offset = 0;
|
||||||
|
int cur_length;
|
||||||
|
int search_len;
|
||||||
|
int farthest, off, i;
|
||||||
|
int lb_idx;
|
||||||
|
const unsigned char first = buf[start_offset];
|
||||||
|
lookback *lb = &lkbk[first];
|
||||||
|
|
||||||
|
// buf
|
||||||
|
// | off start max
|
||||||
|
// V |+i-> |+i-> |
|
||||||
|
// |--------------raw-data-----------------|
|
||||||
|
// |+i-> | |+i->
|
||||||
|
// +cur_length
|
||||||
|
|
||||||
|
// check at most the past 4096 values
|
||||||
|
farthest = MAX(start_offset - 4096, 0);
|
||||||
|
// find starting index
|
||||||
|
for (lb_idx = lb->start; lb_idx < lb->count && lb->indexes[lb_idx] < farthest; lb_idx++) {}
|
||||||
|
lb->start = lb_idx;
|
||||||
|
for ( ; lb_idx < lb->count && lb->indexes[lb_idx] < start_offset; lb_idx++) {
|
||||||
|
off = lb->indexes[lb_idx];
|
||||||
|
// check at most requested max or up until start
|
||||||
|
search_len = MIN(max_search, start_offset - off);
|
||||||
|
for (i = 0; i < search_len; i++) {
|
||||||
|
if (buf[start_offset + i] != buf[off + i]) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
cur_length = i;
|
||||||
|
// if matched up until start, continue matching in already matched parts
|
||||||
|
if (cur_length == search_len) {
|
||||||
|
// check at most requested max less current length
|
||||||
|
search_len = max_search - cur_length;
|
||||||
|
for (i = 0; i < search_len; i++) {
|
||||||
|
if (buf[start_offset + cur_length + i] != buf[off + i]) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
cur_length += i;
|
||||||
|
}
|
||||||
|
if (cur_length > best_length) {
|
||||||
|
best_offset = start_offset - off;
|
||||||
|
best_length = cur_length;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// return best reverse offset and length (may be 0)
|
||||||
|
*found_offset = best_offset;
|
||||||
|
return best_length;
|
||||||
|
}
|
||||||
|
|
||||||
|
// decode MIO0 header
|
||||||
|
// returns 1 if valid header, 0 otherwise
|
||||||
|
int mio0_decode_header(const unsigned char *buf, mio0_header_t *head)
|
||||||
|
{
|
||||||
|
if (!memcmp(buf, "MIO0", 4)) {
|
||||||
|
head->dest_size = read_u32_be(&buf[4]);
|
||||||
|
head->comp_offset = read_u32_be(&buf[8]);
|
||||||
|
head->uncomp_offset = read_u32_be(&buf[12]);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void mio0_encode_header(unsigned char *buf, const mio0_header_t *head)
|
||||||
|
{
|
||||||
|
memcpy(buf, "MIO0", 4);
|
||||||
|
write_u32_be(&buf[4], head->dest_size);
|
||||||
|
write_u32_be(&buf[8], head->comp_offset);
|
||||||
|
write_u32_be(&buf[12], head->uncomp_offset);
|
||||||
|
}
|
||||||
|
|
||||||
|
int mio0_decode(const unsigned char *in, unsigned char *out, unsigned int *end)
|
||||||
|
{
|
||||||
|
mio0_header_t head;
|
||||||
|
unsigned int bytes_written = 0;
|
||||||
|
int bit_idx = 0;
|
||||||
|
int comp_idx = 0;
|
||||||
|
int uncomp_idx = 0;
|
||||||
|
int valid;
|
||||||
|
|
||||||
|
// extract header
|
||||||
|
valid = mio0_decode_header(in, &head);
|
||||||
|
// verify MIO0 header
|
||||||
|
if (!valid) {
|
||||||
|
return -2;
|
||||||
|
}
|
||||||
|
|
||||||
|
// decode data
|
||||||
|
while (bytes_written < head.dest_size) {
|
||||||
|
if (GET_BIT(&in[MIO0_HEADER_LENGTH], bit_idx)) {
|
||||||
|
// 1 - pull uncompressed data
|
||||||
|
out[bytes_written] = in[head.uncomp_offset + uncomp_idx];
|
||||||
|
bytes_written++;
|
||||||
|
uncomp_idx++;
|
||||||
|
} else {
|
||||||
|
// 0 - read compressed data
|
||||||
|
int idx;
|
||||||
|
int length;
|
||||||
|
int i;
|
||||||
|
const unsigned char *vals = &in[head.comp_offset + comp_idx];
|
||||||
|
comp_idx += 2;
|
||||||
|
length = ((vals[0] & 0xF0) >> 4) + 3;
|
||||||
|
idx = ((vals[0] & 0x0F) << 8) + vals[1] + 1;
|
||||||
|
for (i = 0; i < length; i++) {
|
||||||
|
out[bytes_written] = out[bytes_written - idx];
|
||||||
|
bytes_written++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
bit_idx++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (end) {
|
||||||
|
*end = head.uncomp_offset + uncomp_idx;
|
||||||
|
}
|
||||||
|
|
||||||
|
return bytes_written;
|
||||||
|
}
|
||||||
|
|
||||||
|
int mio0_encode(const unsigned char *in, unsigned int length, unsigned char *out)
|
||||||
|
{
|
||||||
|
unsigned char *bit_buf;
|
||||||
|
unsigned char *comp_buf;
|
||||||
|
unsigned char *uncomp_buf;
|
||||||
|
unsigned int bit_length;
|
||||||
|
unsigned int comp_offset;
|
||||||
|
unsigned int uncomp_offset;
|
||||||
|
unsigned int bytes_proc = 0;
|
||||||
|
int bytes_written;
|
||||||
|
int bit_idx = 0;
|
||||||
|
int comp_idx = 0;
|
||||||
|
int uncomp_idx = 0;
|
||||||
|
lookback *lookbacks;
|
||||||
|
|
||||||
|
// initialize lookback buffer
|
||||||
|
lookbacks = lookback_init();
|
||||||
|
|
||||||
|
// allocate some temporary buffers worst case size
|
||||||
|
bit_buf = malloc((length + 7) / 8); // 1-bit/byte
|
||||||
|
comp_buf = malloc(length); // 16-bits/2bytes
|
||||||
|
uncomp_buf = malloc(length); // all uncompressed
|
||||||
|
memset(bit_buf, 0, (length + 7) / 8);
|
||||||
|
|
||||||
|
// encode data
|
||||||
|
// special case for first byte
|
||||||
|
lookback_push(lookbacks, in[0], 0);
|
||||||
|
uncomp_buf[uncomp_idx] = in[0];
|
||||||
|
uncomp_idx += 1;
|
||||||
|
bytes_proc += 1;
|
||||||
|
PUT_BIT(bit_buf, bit_idx++, 1);
|
||||||
|
while (bytes_proc < length) {
|
||||||
|
int offset;
|
||||||
|
int max_length = MIN(length - bytes_proc, 18);
|
||||||
|
int longest_match = find_longest(in, bytes_proc, max_length, &offset, lookbacks);
|
||||||
|
// push current byte before checking next longer match
|
||||||
|
lookback_push(lookbacks, in[bytes_proc], bytes_proc);
|
||||||
|
if (longest_match > 2) {
|
||||||
|
int lookahead_offset;
|
||||||
|
// lookahead to next byte to see if longer match
|
||||||
|
int lookahead_length = MIN(length - bytes_proc - 1, 18);
|
||||||
|
int lookahead_match = find_longest(in, bytes_proc + 1, lookahead_length, &lookahead_offset, lookbacks);
|
||||||
|
// better match found, use uncompressed + lookahead compressed
|
||||||
|
if ((longest_match + 1) < lookahead_match) {
|
||||||
|
// uncompressed byte
|
||||||
|
uncomp_buf[uncomp_idx] = in[bytes_proc];
|
||||||
|
uncomp_idx++;
|
||||||
|
PUT_BIT(bit_buf, bit_idx, 1);
|
||||||
|
bytes_proc++;
|
||||||
|
longest_match = lookahead_match;
|
||||||
|
offset = lookahead_offset;
|
||||||
|
bit_idx++;
|
||||||
|
lookback_push(lookbacks, in[bytes_proc], bytes_proc);
|
||||||
|
}
|
||||||
|
// first byte already pushed above
|
||||||
|
for (int i = 1; i < longest_match; i++) {
|
||||||
|
lookback_push(lookbacks, in[bytes_proc + i], bytes_proc + i);
|
||||||
|
}
|
||||||
|
// compressed block
|
||||||
|
comp_buf[comp_idx] = (((longest_match - 3) & 0x0F) << 4) |
|
||||||
|
(((offset - 1) >> 8) & 0x0F);
|
||||||
|
comp_buf[comp_idx + 1] = (offset - 1) & 0xFF;
|
||||||
|
comp_idx += 2;
|
||||||
|
PUT_BIT(bit_buf, bit_idx, 0);
|
||||||
|
bytes_proc += longest_match;
|
||||||
|
} else {
|
||||||
|
// uncompressed byte
|
||||||
|
uncomp_buf[uncomp_idx] = in[bytes_proc];
|
||||||
|
uncomp_idx++;
|
||||||
|
PUT_BIT(bit_buf, bit_idx, 1);
|
||||||
|
bytes_proc++;
|
||||||
|
}
|
||||||
|
bit_idx++;
|
||||||
|
}
|
||||||
|
|
||||||
|
// compute final sizes and offsets
|
||||||
|
// +7 so int division accounts for all bits
|
||||||
|
bit_length = ((bit_idx + 7) / 8);
|
||||||
|
// compressed data after control bits and aligned to 4-byte boundary
|
||||||
|
comp_offset = ALIGN(MIO0_HEADER_LENGTH + bit_length, 4);
|
||||||
|
uncomp_offset = comp_offset + comp_idx;
|
||||||
|
bytes_written = uncomp_offset + uncomp_idx;
|
||||||
|
|
||||||
|
// output header
|
||||||
|
memcpy(out, "MIO0", 4);
|
||||||
|
write_u32_be(&out[4], length);
|
||||||
|
write_u32_be(&out[8], comp_offset);
|
||||||
|
write_u32_be(&out[12], uncomp_offset);
|
||||||
|
// output data
|
||||||
|
memcpy(&out[MIO0_HEADER_LENGTH], bit_buf, bit_length);
|
||||||
|
memcpy(&out[comp_offset], comp_buf, comp_idx);
|
||||||
|
memcpy(&out[uncomp_offset], uncomp_buf, uncomp_idx);
|
||||||
|
|
||||||
|
// free allocated buffers
|
||||||
|
free(bit_buf);
|
||||||
|
free(comp_buf);
|
||||||
|
free(uncomp_buf);
|
||||||
|
lookback_free(lookbacks);
|
||||||
|
|
||||||
|
return bytes_written;
|
||||||
|
}
|
||||||
|
|
||||||
|
static FILE *mio0_open_out_file(const char *out_file) {
|
||||||
|
if (strcmp(out_file, "-") == 0) {
|
||||||
|
#if defined(_WIN32) || defined(_WIN64)
|
||||||
|
_setmode(_fileno(stdout), _O_BINARY);
|
||||||
|
#endif
|
||||||
|
return stdout;
|
||||||
|
} else {
|
||||||
|
return fopen(out_file, "wb");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int mio0_decode_file(const char *in_file, unsigned long offset, const char *out_file)
|
||||||
|
{
|
||||||
|
mio0_header_t head;
|
||||||
|
FILE *in;
|
||||||
|
FILE *out;
|
||||||
|
unsigned char *in_buf = NULL;
|
||||||
|
unsigned char *out_buf = NULL;
|
||||||
|
long file_size;
|
||||||
|
int ret_val = 0;
|
||||||
|
size_t bytes_read;
|
||||||
|
int bytes_decoded;
|
||||||
|
int bytes_written;
|
||||||
|
int valid;
|
||||||
|
|
||||||
|
in = fopen(in_file, "rb");
|
||||||
|
if (in == NULL) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// allocate buffer to read from offset to end of file
|
||||||
|
fseek(in, 0, SEEK_END);
|
||||||
|
file_size = ftell(in);
|
||||||
|
in_buf = malloc(file_size - offset);
|
||||||
|
fseek(in, offset, SEEK_SET);
|
||||||
|
|
||||||
|
// read bytes
|
||||||
|
bytes_read = fread(in_buf, 1, file_size - offset, in);
|
||||||
|
if (bytes_read != file_size - offset) {
|
||||||
|
ret_val = 2;
|
||||||
|
goto free_all;
|
||||||
|
}
|
||||||
|
|
||||||
|
// verify header
|
||||||
|
valid = mio0_decode_header(in_buf, &head);
|
||||||
|
if (!valid) {
|
||||||
|
ret_val = 3;
|
||||||
|
goto free_all;
|
||||||
|
}
|
||||||
|
out_buf = malloc(head.dest_size);
|
||||||
|
|
||||||
|
// decompress MIO0 encoded data
|
||||||
|
bytes_decoded = mio0_decode(in_buf, out_buf, NULL);
|
||||||
|
if (bytes_decoded < 0) {
|
||||||
|
ret_val = 3;
|
||||||
|
goto free_all;
|
||||||
|
}
|
||||||
|
|
||||||
|
// open output file
|
||||||
|
out = mio0_open_out_file(out_file);
|
||||||
|
if (out == NULL) {
|
||||||
|
ret_val = 4;
|
||||||
|
goto free_all;
|
||||||
|
}
|
||||||
|
|
||||||
|
// write data to file
|
||||||
|
bytes_written = fwrite(out_buf, 1, bytes_decoded, out);
|
||||||
|
if (bytes_written != bytes_decoded) {
|
||||||
|
ret_val = 5;
|
||||||
|
}
|
||||||
|
|
||||||
|
// clean up
|
||||||
|
if (out != stdout) {
|
||||||
|
fclose(out);
|
||||||
|
}
|
||||||
|
free_all:
|
||||||
|
if (out_buf) {
|
||||||
|
free(out_buf);
|
||||||
|
}
|
||||||
|
if (in_buf) {
|
||||||
|
free(in_buf);
|
||||||
|
}
|
||||||
|
fclose(in);
|
||||||
|
|
||||||
|
return ret_val;
|
||||||
|
}
|
||||||
|
|
||||||
|
int mio0_encode_file(const char *in_file, const char *out_file)
|
||||||
|
{
|
||||||
|
FILE *in;
|
||||||
|
FILE *out;
|
||||||
|
unsigned char *in_buf = NULL;
|
||||||
|
unsigned char *out_buf = NULL;
|
||||||
|
size_t file_size;
|
||||||
|
size_t bytes_read;
|
||||||
|
int bytes_encoded;
|
||||||
|
int bytes_written;
|
||||||
|
int ret_val = 0;
|
||||||
|
|
||||||
|
in = fopen(in_file, "rb");
|
||||||
|
if (in == NULL) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// allocate buffer to read entire contents of files
|
||||||
|
fseek(in, 0, SEEK_END);
|
||||||
|
file_size = ftell(in);
|
||||||
|
fseek(in, 0, SEEK_SET);
|
||||||
|
in_buf = malloc(file_size);
|
||||||
|
|
||||||
|
// read bytes
|
||||||
|
bytes_read = fread(in_buf, 1, file_size, in);
|
||||||
|
if (bytes_read != file_size) {
|
||||||
|
ret_val = 2;
|
||||||
|
goto free_all;
|
||||||
|
}
|
||||||
|
|
||||||
|
// allocate worst case length
|
||||||
|
out_buf = malloc(MIO0_HEADER_LENGTH + ((file_size+7)/8) + file_size);
|
||||||
|
|
||||||
|
// compress data in MIO0 format
|
||||||
|
bytes_encoded = mio0_encode(in_buf, file_size, out_buf);
|
||||||
|
|
||||||
|
// open output file
|
||||||
|
out = mio0_open_out_file(out_file);
|
||||||
|
if (out == NULL) {
|
||||||
|
ret_val = 4;
|
||||||
|
goto free_all;
|
||||||
|
}
|
||||||
|
|
||||||
|
// write data to file
|
||||||
|
bytes_written = fwrite(out_buf, 1, bytes_encoded, out);
|
||||||
|
if (bytes_written != bytes_encoded) {
|
||||||
|
ret_val = 5;
|
||||||
|
}
|
||||||
|
|
||||||
|
// clean up
|
||||||
|
if (out != stdout) {
|
||||||
|
fclose(out);
|
||||||
|
}
|
||||||
|
free_all:
|
||||||
|
if (out_buf) {
|
||||||
|
free(out_buf);
|
||||||
|
}
|
||||||
|
if (in_buf) {
|
||||||
|
free(in_buf);
|
||||||
|
}
|
||||||
|
fclose(in);
|
||||||
|
|
||||||
|
return ret_val;
|
||||||
|
}
|
||||||
|
|
||||||
|
// mio0 standalone executable
|
||||||
|
#ifdef MIO0_STANDALONE
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
char *in_filename;
|
||||||
|
char *out_filename;
|
||||||
|
unsigned int offset;
|
||||||
|
int compress;
|
||||||
|
} arg_config;
|
||||||
|
|
||||||
|
static arg_config default_config =
|
||||||
|
{
|
||||||
|
NULL,
|
||||||
|
NULL,
|
||||||
|
0,
|
||||||
|
1
|
||||||
|
};
|
||||||
|
|
||||||
|
static void print_usage(void)
|
||||||
|
{
|
||||||
|
ERROR("Usage: mio0 [-c / -d] [-o OFFSET] FILE [OUTPUT]\n"
|
||||||
|
"\n"
|
||||||
|
"mio0 v" MIO0_VERSION ": MIO0 compression and decompression tool\n"
|
||||||
|
"\n"
|
||||||
|
"Optional arguments:\n"
|
||||||
|
" -c compress raw data into MIO0 (default: compress)\n"
|
||||||
|
" -d decompress MIO0 into raw data\n"
|
||||||
|
" -o OFFSET starting offset in FILE (default: 0)\n"
|
||||||
|
"\n"
|
||||||
|
"File arguments:\n"
|
||||||
|
" FILE input file\n"
|
||||||
|
" [OUTPUT] output file (default: FILE.out), \"-\" for stdout\n");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// parse command line arguments
|
||||||
|
static void parse_arguments(int argc, char *argv[], arg_config *config)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
int file_count = 0;
|
||||||
|
if (argc < 2) {
|
||||||
|
print_usage();
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
for (i = 1; i < argc; i++) {
|
||||||
|
if (argv[i][0] == '-' && argv[i][1] != '\0') {
|
||||||
|
switch (argv[i][1]) {
|
||||||
|
case 'c':
|
||||||
|
config->compress = 1;
|
||||||
|
break;
|
||||||
|
case 'd':
|
||||||
|
config->compress = 0;
|
||||||
|
break;
|
||||||
|
case 'o':
|
||||||
|
if (++i >= argc) {
|
||||||
|
print_usage();
|
||||||
|
}
|
||||||
|
config->offset = strtoul(argv[i], NULL, 0);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
print_usage();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
switch (file_count) {
|
||||||
|
case 0:
|
||||||
|
config->in_filename = argv[i];
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
config->out_filename = argv[i];
|
||||||
|
break;
|
||||||
|
default: // too many
|
||||||
|
print_usage();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
file_count++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (file_count < 1) {
|
||||||
|
print_usage();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char *argv[])
|
||||||
|
{
|
||||||
|
char out_filename[FILENAME_MAX];
|
||||||
|
arg_config config;
|
||||||
|
int ret_val;
|
||||||
|
|
||||||
|
// get configuration from arguments
|
||||||
|
config = default_config;
|
||||||
|
parse_arguments(argc, argv, &config);
|
||||||
|
if (config.out_filename == NULL) {
|
||||||
|
config.out_filename = out_filename;
|
||||||
|
sprintf(config.out_filename, "%s.out", config.in_filename);
|
||||||
|
}
|
||||||
|
|
||||||
|
// operation
|
||||||
|
if (config.compress) {
|
||||||
|
ret_val = mio0_encode_file(config.in_filename, config.out_filename);
|
||||||
|
} else {
|
||||||
|
ret_val = mio0_decode_file(config.in_filename, config.offset, config.out_filename);
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (ret_val) {
|
||||||
|
case 1:
|
||||||
|
ERROR("Error opening input file \"%s\"\n", config.in_filename);
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
ERROR("Error reading from input file \"%s\"\n", config.in_filename);
|
||||||
|
break;
|
||||||
|
case 3:
|
||||||
|
ERROR("Error decoding MIO0 data. Wrong offset (0x%X)?\n", config.offset);
|
||||||
|
break;
|
||||||
|
case 4:
|
||||||
|
ERROR("Error opening output file \"%s\"\n", config.out_filename);
|
||||||
|
break;
|
||||||
|
case 5:
|
||||||
|
ERROR("Error writing bytes to output file \"%s\"\n", config.out_filename);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret_val;
|
||||||
|
}
|
||||||
|
#endif // MIO0_STANDALONE
|
||||||
|
|
50
src/tools/libmio0.h
Normal file
50
src/tools/libmio0.h
Normal file
|
@ -0,0 +1,50 @@
|
||||||
|
#ifndef LIBMIO0_H_
|
||||||
|
#define LIBMIO0_H_
|
||||||
|
|
||||||
|
// defines
|
||||||
|
|
||||||
|
#define MIO0_HEADER_LENGTH 16
|
||||||
|
|
||||||
|
// typedefs
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
unsigned int dest_size;
|
||||||
|
unsigned int comp_offset;
|
||||||
|
unsigned int uncomp_offset;
|
||||||
|
} mio0_header_t;
|
||||||
|
|
||||||
|
// function prototypes
|
||||||
|
|
||||||
|
// decode MIO0 header
|
||||||
|
// returns 1 if valid header, 0 otherwise
|
||||||
|
int mio0_decode_header(const unsigned char *buf, mio0_header_t *head);
|
||||||
|
|
||||||
|
// encode MIO0 header from struct
|
||||||
|
void mio0_encode_header(unsigned char *buf, const mio0_header_t *head);
|
||||||
|
|
||||||
|
// decode MIO0 data in memory
|
||||||
|
// in: buffer containing MIO0 data
|
||||||
|
// out: buffer for output data
|
||||||
|
// end: output offset of the last byte decoded from in (set to NULL if unwanted)
|
||||||
|
// returns bytes extracted to 'out' or negative value on failure
|
||||||
|
int mio0_decode(const unsigned char *in, unsigned char *out, unsigned int *end);
|
||||||
|
|
||||||
|
// encode MIO0 data in memory
|
||||||
|
// in: buffer containing raw data
|
||||||
|
// out: buffer for MIO0 data
|
||||||
|
// returns size of compressed data in 'out' including MIO0 header
|
||||||
|
int mio0_encode(const unsigned char *in, unsigned int length, unsigned char *out);
|
||||||
|
|
||||||
|
// decode an entire MIO0 block at an offset from file to output file
|
||||||
|
// in_file: input filename
|
||||||
|
// offset: offset to start decoding from in_file
|
||||||
|
// out_file: output filename
|
||||||
|
int mio0_decode_file(const char *in_file, unsigned long offset, const char *out_file);
|
||||||
|
|
||||||
|
// encode an entire file
|
||||||
|
// in_file: input filename containing raw data to be encoded
|
||||||
|
// out_file: output filename to write MIO0 compressed data to
|
||||||
|
int mio0_encode_file(const char *in_file, const char *out_file);
|
||||||
|
|
||||||
|
#endif // LIBMIO0_H_
|
1021
src/tools/n64graphics.c
Normal file
1021
src/tools/n64graphics.c
Normal file
File diff suppressed because it is too large
Load diff
100
src/tools/n64graphics.h
Normal file
100
src/tools/n64graphics.h
Normal file
|
@ -0,0 +1,100 @@
|
||||||
|
#ifndef N64GRAPHICS_H_
|
||||||
|
#define N64GRAPHICS_H_
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
// intermediate formats
|
||||||
|
typedef struct _rgba
|
||||||
|
{
|
||||||
|
uint8_t red;
|
||||||
|
uint8_t green;
|
||||||
|
uint8_t blue;
|
||||||
|
uint8_t alpha;
|
||||||
|
} rgba;
|
||||||
|
|
||||||
|
typedef struct _ia
|
||||||
|
{
|
||||||
|
uint8_t intensity;
|
||||||
|
uint8_t alpha;
|
||||||
|
} ia;
|
||||||
|
|
||||||
|
// CI palette
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
uint16_t data[256];
|
||||||
|
int max; // max number of entries
|
||||||
|
int used; // number of entries used
|
||||||
|
} palette_t;
|
||||||
|
|
||||||
|
//---------------------------------------------------------
|
||||||
|
// N64 RGBA/IA/I/CI -> intermediate RGBA/IA
|
||||||
|
//---------------------------------------------------------
|
||||||
|
|
||||||
|
// N64 raw RGBA16/RGBA32 -> intermediate RGBA
|
||||||
|
rgba *raw2rgba(const uint8_t *raw, int width, int height, int depth);
|
||||||
|
|
||||||
|
// N64 raw IA1/IA4/IA8/IA16 -> intermediate IA
|
||||||
|
ia *raw2ia(const uint8_t *raw, int width, int height, int depth);
|
||||||
|
|
||||||
|
// N64 raw I4/I8 -> intermediate IA
|
||||||
|
ia *raw2i(const uint8_t *raw, int width, int height, int depth);
|
||||||
|
|
||||||
|
//---------------------------------------------------------
|
||||||
|
// intermediate RGBA/IA -> N64 RGBA/IA/I/CI
|
||||||
|
// returns length written to 'raw' used or -1 on error
|
||||||
|
//---------------------------------------------------------
|
||||||
|
|
||||||
|
// intermediate RGBA -> N64 raw RGBA16/RGBA32
|
||||||
|
int rgba2raw(uint8_t *raw, const rgba *img, int width, int height, int depth);
|
||||||
|
|
||||||
|
// intermediate IA -> N64 raw IA1/IA4/IA8/IA16
|
||||||
|
int ia2raw(uint8_t *raw, const ia *img, int width, int height, int depth);
|
||||||
|
|
||||||
|
// intermediate IA -> N64 raw I4/I8
|
||||||
|
int i2raw(uint8_t *raw, const ia *img, int width, int height, int depth);
|
||||||
|
|
||||||
|
|
||||||
|
//---------------------------------------------------------
|
||||||
|
// N64 CI <-> N64 RGBA16/IA16
|
||||||
|
//---------------------------------------------------------
|
||||||
|
|
||||||
|
// N64 CI raw data and palette to raw data (either RGBA16 or IA16)
|
||||||
|
uint8_t *ci2raw(const uint8_t *rawci, const uint8_t *palette, int width, int height, int ci_depth);
|
||||||
|
|
||||||
|
// convert from raw (RGBA16 or IA16) format to CI + palette
|
||||||
|
int raw2ci(uint8_t *rawci, palette_t *pal, const uint8_t *raw, int raw_len, int ci_depth);
|
||||||
|
|
||||||
|
|
||||||
|
//---------------------------------------------------------
|
||||||
|
// intermediate RGBA/IA -> PNG
|
||||||
|
//---------------------------------------------------------
|
||||||
|
|
||||||
|
// intermediate RGBA write to PNG file
|
||||||
|
int rgba2png(const char *png_filename, const rgba *img, int width, int height);
|
||||||
|
|
||||||
|
// intermediate IA write to grayscale PNG file
|
||||||
|
int ia2png(const char *png_filename, const ia *img, int width, int height);
|
||||||
|
|
||||||
|
|
||||||
|
//---------------------------------------------------------
|
||||||
|
// PNG -> intermediate RGBA/IA
|
||||||
|
//---------------------------------------------------------
|
||||||
|
|
||||||
|
// PNG file -> intermediate RGBA
|
||||||
|
rgba *png2rgba(const char *png_filename, int *width, int *height);
|
||||||
|
|
||||||
|
// PNG file -> intermediate IA
|
||||||
|
ia *png2ia(const char *png_filename, int *width, int *height);
|
||||||
|
|
||||||
|
|
||||||
|
//---------------------------------------------------------
|
||||||
|
// version
|
||||||
|
//---------------------------------------------------------
|
||||||
|
|
||||||
|
// get version of underlying graphics reading library
|
||||||
|
const char *n64graphics_get_read_version(void);
|
||||||
|
|
||||||
|
// get version of underlying graphics writing library
|
||||||
|
const char *n64graphics_get_write_version(void);
|
||||||
|
|
||||||
|
#endif // N64GRAPHICS_H_
|
7462
src/tools/stb/stb_image.h
Normal file
7462
src/tools/stb/stb_image.h
Normal file
File diff suppressed because it is too large
Load diff
1568
src/tools/stb/stb_image_write.h
Normal file
1568
src/tools/stb/stb_image_write.h
Normal file
File diff suppressed because it is too large
Load diff
276
src/tools/utils.c
Normal file
276
src/tools/utils.c
Normal file
|
@ -0,0 +1,276 @@
|
||||||
|
#include <dirent.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#if defined(_MSC_VER) || defined(__MINGW32__)
|
||||||
|
#include <io.h>
|
||||||
|
#include <sys/utime.h>
|
||||||
|
#else
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <utime.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include "utils.h"
|
||||||
|
|
||||||
|
// global verbosity setting
|
||||||
|
int g_verbosity = 0;
|
||||||
|
|
||||||
|
int read_s16_be(unsigned char *buf)
|
||||||
|
{
|
||||||
|
unsigned tmp = read_u16_be(buf);
|
||||||
|
int ret;
|
||||||
|
if (tmp > 0x7FFF) {
|
||||||
|
ret = -((int)0x10000 - (int)tmp);
|
||||||
|
} else {
|
||||||
|
ret = (int)tmp;
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
float read_f32_be(unsigned char *buf)
|
||||||
|
{
|
||||||
|
union {uint32_t i; float f;} ret;
|
||||||
|
ret.i = read_u32_be(buf);
|
||||||
|
return ret.f;
|
||||||
|
}
|
||||||
|
|
||||||
|
int is_power2(unsigned int val)
|
||||||
|
{
|
||||||
|
while (((val & 1) == 0) && (val > 1)) {
|
||||||
|
val >>= 1;
|
||||||
|
}
|
||||||
|
return (val == 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
void fprint_hex(FILE *fp, const unsigned char *buf, int length)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
for (i = 0; i < length; i++) {
|
||||||
|
fprint_byte(fp, buf[i]);
|
||||||
|
fputc(' ', fp);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void fprint_hex_source(FILE *fp, const unsigned char *buf, int length)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
for (i = 0; i < length; i++) {
|
||||||
|
if (i > 0) fputs(", ", fp);
|
||||||
|
fputs("0x", fp);
|
||||||
|
fprint_byte(fp, buf[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void print_hex(const unsigned char *buf, int length)
|
||||||
|
{
|
||||||
|
fprint_hex(stdout, buf, length);
|
||||||
|
}
|
||||||
|
|
||||||
|
void swap_bytes(unsigned char *data, long length)
|
||||||
|
{
|
||||||
|
long i;
|
||||||
|
unsigned char tmp;
|
||||||
|
for (i = 0; i < length; i += 2) {
|
||||||
|
tmp = data[i];
|
||||||
|
data[i] = data[i+1];
|
||||||
|
data[i+1] = tmp;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void reverse_endian(unsigned char *data, long length)
|
||||||
|
{
|
||||||
|
long i;
|
||||||
|
unsigned char tmp;
|
||||||
|
for (i = 0; i < length; i += 4) {
|
||||||
|
tmp = data[i];
|
||||||
|
data[i] = data[i+3];
|
||||||
|
data[i+3] = tmp;
|
||||||
|
tmp = data[i+1];
|
||||||
|
data[i+1] = data[i+2];
|
||||||
|
data[i+2] = tmp;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
long filesize(const char *filename)
|
||||||
|
{
|
||||||
|
struct stat st;
|
||||||
|
|
||||||
|
if (stat(filename, &st) == 0) {
|
||||||
|
return st.st_size;
|
||||||
|
}
|
||||||
|
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
void touch_file(const char *filename)
|
||||||
|
{
|
||||||
|
int fd;
|
||||||
|
//fd = open(filename, O_WRONLY|O_CREAT|O_NOCTTY|O_NONBLOCK, 0666);
|
||||||
|
fd = open(filename, O_WRONLY|O_CREAT, 0666);
|
||||||
|
if (fd >= 0) {
|
||||||
|
utime(filename, NULL);
|
||||||
|
close(fd);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
long read_file(const char *file_name, unsigned char **data)
|
||||||
|
{
|
||||||
|
FILE *in;
|
||||||
|
unsigned char *in_buf = NULL;
|
||||||
|
long file_size;
|
||||||
|
long bytes_read;
|
||||||
|
in = fopen(file_name, "rb");
|
||||||
|
if (in == NULL) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// allocate buffer to read from offset to end of file
|
||||||
|
fseek(in, 0, SEEK_END);
|
||||||
|
file_size = ftell(in);
|
||||||
|
|
||||||
|
// sanity check
|
||||||
|
if (file_size > 256*MB) {
|
||||||
|
return -2;
|
||||||
|
}
|
||||||
|
|
||||||
|
in_buf = malloc(file_size);
|
||||||
|
fseek(in, 0, SEEK_SET);
|
||||||
|
|
||||||
|
// read bytes
|
||||||
|
bytes_read = fread(in_buf, 1, file_size, in);
|
||||||
|
if (bytes_read != file_size) {
|
||||||
|
return -3;
|
||||||
|
}
|
||||||
|
|
||||||
|
fclose(in);
|
||||||
|
*data = in_buf;
|
||||||
|
return bytes_read;
|
||||||
|
}
|
||||||
|
|
||||||
|
long write_file(const char *file_name, unsigned char *data, long length)
|
||||||
|
{
|
||||||
|
FILE *out;
|
||||||
|
long bytes_written;
|
||||||
|
// open output file
|
||||||
|
out = fopen(file_name, "wb");
|
||||||
|
if (out == NULL) {
|
||||||
|
perror(file_name);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
bytes_written = fwrite(data, 1, length, out);
|
||||||
|
fclose(out);
|
||||||
|
return bytes_written;
|
||||||
|
}
|
||||||
|
|
||||||
|
void generate_filename(const char *in_name, char *out_name, char *extension)
|
||||||
|
{
|
||||||
|
char tmp_name[FILENAME_MAX];
|
||||||
|
int len;
|
||||||
|
int i;
|
||||||
|
strcpy(tmp_name, in_name);
|
||||||
|
len = strlen(tmp_name);
|
||||||
|
for (i = len - 1; i > 0; i--) {
|
||||||
|
if (tmp_name[i] == '.') {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (i <= 0) {
|
||||||
|
i = len;
|
||||||
|
}
|
||||||
|
tmp_name[i] = '\0';
|
||||||
|
sprintf(out_name, "%s.%s", tmp_name, extension);
|
||||||
|
}
|
||||||
|
|
||||||
|
char *basename(const char *name)
|
||||||
|
{
|
||||||
|
const char *base = name;
|
||||||
|
while (*name) {
|
||||||
|
if (*name++ == '/') {
|
||||||
|
base = name;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return (char *)base;
|
||||||
|
}
|
||||||
|
|
||||||
|
void make_dir(const char *dir_name)
|
||||||
|
{
|
||||||
|
struct stat st = {0};
|
||||||
|
if (stat(dir_name, &st) == -1) {
|
||||||
|
mkdir(dir_name, 0755);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
long copy_file(const char *src_name, const char *dst_name)
|
||||||
|
{
|
||||||
|
unsigned char *buf;
|
||||||
|
long bytes_written;
|
||||||
|
long bytes_read;
|
||||||
|
|
||||||
|
bytes_read = read_file(src_name, &buf);
|
||||||
|
|
||||||
|
if (bytes_read > 0) {
|
||||||
|
bytes_written = write_file(dst_name, buf, bytes_read);
|
||||||
|
if (bytes_written != bytes_read) {
|
||||||
|
bytes_read = -1;
|
||||||
|
}
|
||||||
|
free(buf);
|
||||||
|
}
|
||||||
|
|
||||||
|
return bytes_read;
|
||||||
|
}
|
||||||
|
|
||||||
|
void dir_list_ext(const char *dir, const char *extension, dir_list *list)
|
||||||
|
{
|
||||||
|
char *pool;
|
||||||
|
char *pool_ptr;
|
||||||
|
struct dirent *entry;
|
||||||
|
DIR *dfd;
|
||||||
|
int idx;
|
||||||
|
|
||||||
|
dfd = opendir(dir);
|
||||||
|
if (dfd == NULL) {
|
||||||
|
ERROR("Can't open '%s'\n", dir);
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
pool = malloc(FILENAME_MAX * MAX_DIR_FILES);
|
||||||
|
pool_ptr = pool;
|
||||||
|
|
||||||
|
idx = 0;
|
||||||
|
while ((entry = readdir(dfd)) != NULL && idx < MAX_DIR_FILES) {
|
||||||
|
if (!extension || str_ends_with(entry->d_name, extension)) {
|
||||||
|
sprintf(pool_ptr, "%s/%s", dir, entry->d_name);
|
||||||
|
list->files[idx] = pool_ptr;
|
||||||
|
pool_ptr += strlen(pool_ptr) + 1;
|
||||||
|
idx++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
list->count = idx;
|
||||||
|
|
||||||
|
closedir(dfd);
|
||||||
|
}
|
||||||
|
|
||||||
|
void dir_list_free(dir_list *list)
|
||||||
|
{
|
||||||
|
// assume first entry in array is allocated
|
||||||
|
if (list->files[0]) {
|
||||||
|
free(list->files[0]);
|
||||||
|
list->files[0] = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int str_ends_with(const char *str, const char *suffix)
|
||||||
|
{
|
||||||
|
if (!str || !suffix) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
size_t len_str = strlen(str);
|
||||||
|
size_t len_suffix = strlen(suffix);
|
||||||
|
if (len_suffix > len_str) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return (0 == strncmp(str + len_str - len_suffix, suffix, len_suffix));
|
||||||
|
}
|
153
src/tools/utils.h
Normal file
153
src/tools/utils.h
Normal file
|
@ -0,0 +1,153 @@
|
||||||
|
#ifndef UTILS_H_
|
||||||
|
#define UTILS_H_
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
// defines
|
||||||
|
|
||||||
|
// printing size_t varies by compiler
|
||||||
|
#if defined(_MSC_VER) || defined(__MINGW32__)
|
||||||
|
#define SIZE_T_FORMAT "%Iu"
|
||||||
|
#else
|
||||||
|
#define SIZE_T_FORMAT "%zu"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define KB 1024
|
||||||
|
#define MB (1024 * KB)
|
||||||
|
|
||||||
|
// number of elements in statically declared array
|
||||||
|
#define DIM(S_ARR_) (sizeof(S_ARR_) / sizeof(S_ARR_[0]))
|
||||||
|
|
||||||
|
#define MIN(A_, B_) ((A_) < (B_) ? (A_) : (B_))
|
||||||
|
#define MAX(A_, B_) ((A_) > (B_) ? (A_) : (B_))
|
||||||
|
|
||||||
|
// align value to N-byte boundary
|
||||||
|
#define ALIGN(VAL_, ALIGNMENT_) (((VAL_) + ((ALIGNMENT_) - 1)) & ~((ALIGNMENT_) - 1))
|
||||||
|
|
||||||
|
// read/write u32/16 big/little endian
|
||||||
|
#define read_u32_be(buf) (unsigned int)(((buf)[0] << 24) + ((buf)[1] << 16) + ((buf)[2] << 8) + ((buf)[3]))
|
||||||
|
#define read_u32_le(buf) (unsigned int)(((buf)[1] << 24) + ((buf)[0] << 16) + ((buf)[3] << 8) + ((buf)[2]))
|
||||||
|
#define write_u32_be(buf, val) do { \
|
||||||
|
(buf)[0] = ((val) >> 24) & 0xFF; \
|
||||||
|
(buf)[1] = ((val) >> 16) & 0xFF; \
|
||||||
|
(buf)[2] = ((val) >> 8) & 0xFF; \
|
||||||
|
(buf)[3] = (val) & 0xFF; \
|
||||||
|
} while(0)
|
||||||
|
#define read_u16_be(buf) (((buf)[0] << 8) + ((buf)[1]))
|
||||||
|
#define write_u16_be(buf, val) do { \
|
||||||
|
(buf)[0] = ((val) >> 8) & 0xFF; \
|
||||||
|
(buf)[1] = ((val)) & 0xFF; \
|
||||||
|
} while(0)
|
||||||
|
|
||||||
|
// print nibbles and bytes
|
||||||
|
#define fprint_nibble(FP, NIB_) fputc((NIB_) < 10 ? ('0' + (NIB_)) : ('A' + (NIB_) - 0xA), FP)
|
||||||
|
#define fprint_byte(FP, BYTE_) do { \
|
||||||
|
fprint_nibble(FP, (BYTE_) >> 4); \
|
||||||
|
fprint_nibble(FP, (BYTE_) & 0x0F); \
|
||||||
|
} while(0)
|
||||||
|
#define print_nibble(NIB_) fprint_nibble(stdout, NIB_)
|
||||||
|
#define print_byte(BYTE_) fprint_byte(stdout, BYTE_)
|
||||||
|
|
||||||
|
// Windows compatibility
|
||||||
|
#if defined(_MSC_VER) || defined(__MINGW32__)
|
||||||
|
#include <direct.h>
|
||||||
|
#define mkdir(DIR_, PERM_) _mkdir(DIR_)
|
||||||
|
#ifndef strcasecmp
|
||||||
|
#define strcasecmp(A, B) stricmp(A, B)
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// typedefs
|
||||||
|
|
||||||
|
#define MAX_DIR_FILES 128
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
char *files[MAX_DIR_FILES];
|
||||||
|
int count;
|
||||||
|
} dir_list;
|
||||||
|
|
||||||
|
// global verbosity setting
|
||||||
|
extern int g_verbosity;
|
||||||
|
|
||||||
|
#define ERROR(...) fprintf(stderr, __VA_ARGS__)
|
||||||
|
#define INFO(...) if (g_verbosity) printf(__VA_ARGS__)
|
||||||
|
#define INFO_HEX(...) if (g_verbosity) print_hex(__VA_ARGS__)
|
||||||
|
|
||||||
|
// functions
|
||||||
|
|
||||||
|
// convert two bytes in big-endian to signed int
|
||||||
|
int read_s16_be(unsigned char *buf);
|
||||||
|
|
||||||
|
// convert four bytes in big-endian to float
|
||||||
|
float read_f32_be(unsigned char *buf);
|
||||||
|
|
||||||
|
// determine if value is power of 2
|
||||||
|
// returns 1 if val is power of 2, 0 otherwise
|
||||||
|
int is_power2(unsigned int val);
|
||||||
|
|
||||||
|
// print buffer as hex bytes
|
||||||
|
// fp: file pointer
|
||||||
|
// buf: buffer to read bytes from
|
||||||
|
// length: length of buffer to print
|
||||||
|
void fprint_hex(FILE *fp, const unsigned char *buf, int length);
|
||||||
|
void fprint_hex_source(FILE *fp, const unsigned char *buf, int length);
|
||||||
|
void print_hex(const unsigned char *buf, int length);
|
||||||
|
|
||||||
|
// perform byteswapping to convert from v64 to z64 ordering
|
||||||
|
void swap_bytes(unsigned char *data, long length);
|
||||||
|
|
||||||
|
// reverse endian to convert from n64 to z64 ordering
|
||||||
|
void reverse_endian(unsigned char *data, long length);
|
||||||
|
|
||||||
|
// get size of file without opening it;
|
||||||
|
// returns file size or negative on error
|
||||||
|
long filesize(const char *file_name);
|
||||||
|
|
||||||
|
// update file timestamp to now, creating it if it doesn't exist
|
||||||
|
void touch_file(const char *filename);
|
||||||
|
|
||||||
|
// read entire contents of file into buffer
|
||||||
|
// returns file size or negative on error
|
||||||
|
long read_file(const char *file_name, unsigned char **data);
|
||||||
|
|
||||||
|
// write buffer to file
|
||||||
|
// returns number of bytes written out or -1 on failure
|
||||||
|
long write_file(const char *file_name, unsigned char *data, long length);
|
||||||
|
|
||||||
|
// generate an output file name from input name by replacing file extension
|
||||||
|
// in_name: input file name
|
||||||
|
// out_name: buffer to write output name in
|
||||||
|
// extension: new file extension to use
|
||||||
|
void generate_filename(const char *in_name, char *out_name, char *extension);
|
||||||
|
|
||||||
|
// extract base filename from file path
|
||||||
|
// name: path to file
|
||||||
|
// returns just the file name after the last '/'
|
||||||
|
char *basename(const char *name);
|
||||||
|
|
||||||
|
// make a directory if it doesn't exist
|
||||||
|
// dir_name: name of the directory
|
||||||
|
void make_dir(const char *dir_name);
|
||||||
|
|
||||||
|
// copy a file from src_name to dst_name. will not make directories
|
||||||
|
// src_name: source file name
|
||||||
|
// dst_name: destination file name
|
||||||
|
long copy_file(const char *src_name, const char *dst_name);
|
||||||
|
|
||||||
|
// list a directory, optionally filtering files by extension
|
||||||
|
// dir: directory to list files in
|
||||||
|
// extension: extension to filter files by (NULL if no filtering)
|
||||||
|
// list: output list and count
|
||||||
|
void dir_list_ext(const char *dir, const char *extension, dir_list *list);
|
||||||
|
|
||||||
|
// free associated date from a directory list
|
||||||
|
// list: directory list filled in by dir_list_ext() call
|
||||||
|
void dir_list_free(dir_list *list);
|
||||||
|
|
||||||
|
// determine if a string ends with another string
|
||||||
|
// str: string to check if ends with 'suffix'
|
||||||
|
// suffix: string to see if 'str' ends with
|
||||||
|
// returns 1 if 'str' ends with 'suffix'
|
||||||
|
int str_ends_with(const char *str, const char *suffix);
|
||||||
|
|
||||||
|
#endif // UTILS_H_
|
Loading…
Reference in a new issue