mirror of
https://github.com/FWGS/xash3d-fwgs
synced 2024-11-22 09:56:22 +01:00
utils: add studio model decompiler.
This commit is contained in:
parent
3d9c482eae
commit
7a58813254
@ -71,11 +71,14 @@ Studio models are position independent, so the cache manager can move them.
|
||||
#define STUDIO_NF_CHROME 0x0002
|
||||
#define STUDIO_NF_FULLBRIGHT 0x0004
|
||||
#define STUDIO_NF_NOMIPS 0x0008 // ignore mip-maps
|
||||
|
||||
#define STUDIO_NF_NOSMOOTH 0x0010 // don't smooth tangent space
|
||||
#define STUDIO_NF_ADDITIVE 0x0020 // rendering with additive mode
|
||||
#define STUDIO_NF_MASKED 0x0040 // use texture with alpha channel
|
||||
#define STUDIO_NF_NORMALMAP 0x0080 // indexed normalmap
|
||||
|
||||
#define STUDIO_NF_SOLID 0x0800
|
||||
#define STUDIO_NF_TWOSIDE 0x1000 // render mesh as twosided
|
||||
|
||||
#define STUDIO_NF_COLORMAP (1<<30) // internal system flag
|
||||
#define STUDIO_NF_UV_COORDS (1<<31) // using half-float coords instead of ST
|
||||
|
||||
|
@ -890,6 +890,37 @@ void COM_ReplaceExtension( char *path, const char *extension )
|
||||
COM_DefaultExtension( path, extension );
|
||||
}
|
||||
|
||||
/*
|
||||
============
|
||||
COM_RemoveLineFeed
|
||||
============
|
||||
*/
|
||||
void COM_RemoveLineFeed( char *str )
|
||||
{
|
||||
while( *str != '\0' )
|
||||
{
|
||||
if( *str == '\r' || *str == '\n' )
|
||||
*str = '\0';
|
||||
|
||||
++str;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
============
|
||||
COM_PathSlashFix
|
||||
============
|
||||
*/
|
||||
void COM_PathSlashFix( char *path )
|
||||
{
|
||||
size_t len;
|
||||
|
||||
len = Q_strlen( path );
|
||||
|
||||
if( path[len - 1] != '\\' || path[len - 1] != '/' )
|
||||
Q_strcpy( &path[len], "/" );
|
||||
}
|
||||
|
||||
int matchpattern( const char *in, const char *pattern, qboolean caseinsensitive )
|
||||
{
|
||||
return matchpattern_with_separator( in, pattern, caseinsensitive, "/\\:", false );
|
||||
|
@ -82,6 +82,8 @@ void COM_ReplaceExtension( char *path, const char *extension );
|
||||
void COM_ExtractFilePath( const char *path, char *dest );
|
||||
const char *COM_FileWithoutPath( const char *in );
|
||||
void COM_StripExtension( char *path );
|
||||
void COM_RemoveLineFeed( char *str );
|
||||
void COM_PathSlashFix( char *path );
|
||||
#define COM_CheckString( string ) ( ( !string || !*string ) ? 0 : 1 )
|
||||
int matchpattern( const char *in, const char *pattern, qboolean caseinsensitive );
|
||||
int matchpattern_with_separator( const char *in, const char *pattern, qboolean caseinsensitive, const char *separators, qboolean wildcard_least_one );
|
||||
|
52
utils/mdldec/Makefile
Normal file
52
utils/mdldec/Makefile
Normal file
@ -0,0 +1,52 @@
|
||||
# Makefile for mdldec
|
||||
# Copyright (c) nekonomicon 2020
|
||||
|
||||
MODULE = mdldec
|
||||
|
||||
CC ?= gcc
|
||||
CFLAGS ?= -O3 -pipe -DHAVE_TGMATH_H
|
||||
LDFLAGS ?= -Wl,--no-undefined
|
||||
|
||||
SYS = $(shell $(CC) -dumpmachine)
|
||||
|
||||
ifneq (, $(findstring mingw, $(SYS)))
|
||||
EXT = .exe
|
||||
else
|
||||
EXT =
|
||||
endif
|
||||
|
||||
APP = $(MODULE)$(EXT)
|
||||
|
||||
SRC = mdldec.c \
|
||||
qc.c \
|
||||
smd.c \
|
||||
texture.c \
|
||||
utils.c \
|
||||
../../public/xash3d_mathlib.c \
|
||||
../../public/matrixlib.c \
|
||||
../../public/crtlib.c
|
||||
|
||||
INCLUDE = -I. \
|
||||
-I../../common \
|
||||
-I../../engine \
|
||||
-I../../engine/common \
|
||||
-I../../engine/common/imagelib \
|
||||
-I../../public
|
||||
|
||||
LIBS = -lm
|
||||
|
||||
OBJS = $(SRC:%.c=%.o)
|
||||
|
||||
all: $(APP)
|
||||
|
||||
$(APP): $(OBJS)
|
||||
$(CC) $(LDFLAGS) -o $(APP) $(OBJS) $(LIBS)
|
||||
|
||||
.c.o:
|
||||
$(CC) $(CFLAGS) $(INCLUDE) -c $< -o $@
|
||||
|
||||
.PHONY: all clean
|
||||
|
||||
clean:
|
||||
$(RM) $(OBJS)
|
||||
$(RM) $(APP)
|
302
utils/mdldec/mdldec.c
Normal file
302
utils/mdldec/mdldec.c
Normal file
@ -0,0 +1,302 @@
|
||||
/*
|
||||
mdldec.c - Half-Life Studio Model Decompiler
|
||||
Copyright (C) 2020 Andrey Akhmichin
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include "const.h"
|
||||
#include "com_model.h"
|
||||
#include "crtlib.h"
|
||||
#include "studio.h"
|
||||
#include "qc.h"
|
||||
#include "smd.h"
|
||||
#include "texture.h"
|
||||
#include "utils.h"
|
||||
#include "version.h"
|
||||
|
||||
char destdir[MAX_SYSPATH];
|
||||
char modelfile[MAX_SYSPATH];
|
||||
studiohdr_t *model_hdr;
|
||||
studiohdr_t *texture_hdr;
|
||||
studiohdr_t **anim_hdr;
|
||||
|
||||
/*
|
||||
============
|
||||
SequenceNameFix
|
||||
============
|
||||
*/
|
||||
static void SequenceNameFix( void )
|
||||
{
|
||||
int i, j, counter;
|
||||
qboolean hasduplicates = false;
|
||||
mstudioseqdesc_t *seqdesc, *seqdesc1;
|
||||
|
||||
for( i = 0; i < model_hdr->numseq; i++ )
|
||||
{
|
||||
seqdesc = (mstudioseqdesc_t *)( (byte *)model_hdr + model_hdr->seqindex ) + i;
|
||||
|
||||
counter = 1;
|
||||
|
||||
for( j = 0; j < model_hdr->numseq; j++ )
|
||||
{
|
||||
seqdesc1 = (mstudioseqdesc_t *)( (byte *)model_hdr + model_hdr->seqindex ) + j;
|
||||
|
||||
if( j != i && !Q_strncmp( seqdesc1->label, seqdesc->label, sizeof( seqdesc1->label ) ) )
|
||||
Q_snprintf( seqdesc1->label, sizeof( seqdesc1->label ), "%s_%i", seqdesc1->label, ++counter );
|
||||
}
|
||||
|
||||
if( counter > 1 )
|
||||
{
|
||||
printf( "WARNING: Sequence name \"%s\" is repeated %i times.\n", seqdesc->label, counter );
|
||||
|
||||
Q_snprintf( seqdesc->label, sizeof( seqdesc->label ), "%s_1", seqdesc->label );
|
||||
|
||||
hasduplicates = true;
|
||||
}
|
||||
}
|
||||
|
||||
if( hasduplicates )
|
||||
puts( "WARNING: Added numeric suffix to repeated sequence name(s)." );
|
||||
}
|
||||
|
||||
/*
|
||||
============
|
||||
BoneNameFix
|
||||
============
|
||||
*/
|
||||
static void BoneNameFix( void )
|
||||
{
|
||||
int i, counter = 0;
|
||||
mstudiobone_t *bone;
|
||||
|
||||
for( i = 0; i < model_hdr->numbones; i++ )
|
||||
{
|
||||
bone = (mstudiobone_t *)( (byte *)model_hdr + model_hdr->boneindex ) + i;
|
||||
|
||||
if( bone->name[0] == '\0' )
|
||||
Q_sprintf( bone->name, "MDLDEC_Bone%i", ++counter );
|
||||
}
|
||||
|
||||
if( counter )
|
||||
printf( "WARNING: Gived name to %i unnamed bone(s).\n", counter );
|
||||
}
|
||||
|
||||
/*
|
||||
============
|
||||
LoadMDL
|
||||
============
|
||||
*/
|
||||
static qboolean LoadMDL( const char *modelname )
|
||||
{
|
||||
int i;
|
||||
size_t len;
|
||||
char texturename[MAX_SYSPATH];
|
||||
char seqgroupname[MAX_SYSPATH];
|
||||
const char *ext;
|
||||
const char id_mdlhdr[] = {'I', 'D', 'S', 'T'};
|
||||
const char id_seqhdr[] = {'I', 'D', 'S', 'Q'};
|
||||
|
||||
printf( "MDL: %s\n", modelname );
|
||||
|
||||
len = Q_strlen( modelname );
|
||||
|
||||
if( len > MAX_SYSPATH - 3 )
|
||||
{
|
||||
fputs( "ERROR: Source path is too long.\n", stderr );
|
||||
return false;
|
||||
}
|
||||
|
||||
ext = COM_FileExtension( modelname );
|
||||
|
||||
if( !ext )
|
||||
{
|
||||
fprintf( stderr, "ERROR: Source file does not have extension.\n" );
|
||||
return false;
|
||||
}
|
||||
|
||||
if( Q_strcmp( ext, "mdl" ) )
|
||||
{
|
||||
fprintf( stderr, "ERROR: Only .mdl-files is supported.\n" );
|
||||
return false;
|
||||
}
|
||||
|
||||
model_hdr = (studiohdr_t *)LoadFile( modelname );
|
||||
|
||||
if( !model_hdr )
|
||||
{
|
||||
fprintf( stderr, "ERROR: Can't open %s\n", modelname );
|
||||
return false;
|
||||
}
|
||||
|
||||
if( memcmp( &model_hdr->ident, id_mdlhdr, sizeof( id_mdlhdr ) ) )
|
||||
{
|
||||
if( !memcmp( &model_hdr->ident, id_seqhdr, sizeof( id_seqhdr ) ) )
|
||||
fprintf( stderr, "ERROR: %s is not a main HL model file.\n", modelname );
|
||||
else
|
||||
fprintf( stderr, "ERROR: %s is not a valid HL model file.\n", modelname );
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
if( model_hdr->version != STUDIO_VERSION )
|
||||
{
|
||||
fprintf( stderr, "ERROR: %s has unknown Studio MDL format version.\n", modelname );
|
||||
return false;
|
||||
}
|
||||
|
||||
if( !model_hdr->numbodyparts )
|
||||
{
|
||||
fprintf( stderr, "ERROR: %s is not a main HL model file.\n", modelname );
|
||||
return false;
|
||||
}
|
||||
|
||||
if( destdir[0] != '\0' )
|
||||
{
|
||||
if( !IsFileExists( destdir ) )
|
||||
{
|
||||
fprintf( stderr, "ERROR: Couldn't find directory %s\n", destdir );
|
||||
return false;
|
||||
}
|
||||
|
||||
COM_PathSlashFix( destdir );
|
||||
}
|
||||
else
|
||||
COM_ExtractFilePath( modelname, destdir );
|
||||
|
||||
len -= 4; // path length without extension
|
||||
|
||||
if( !model_hdr->numtextures )
|
||||
{
|
||||
Q_strcpy( texturename, modelname );
|
||||
Q_strcpy( &texturename[len], "t.mdl" );
|
||||
|
||||
texture_hdr = (studiohdr_t *)LoadFile( texturename );
|
||||
|
||||
if( !texture_hdr )
|
||||
{
|
||||
#if !XASH_WIN32
|
||||
// dirty hack for casesensetive filesystems
|
||||
texturename[len] = 'T';
|
||||
|
||||
texture_hdr = (studiohdr_t *)LoadFile( texturename );
|
||||
|
||||
if( !texture_hdr )
|
||||
#endif
|
||||
{
|
||||
fprintf( stderr, "ERROR: Can't open external textures file %s\n", texturename );
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if( memcmp( &texture_hdr->ident, id_mdlhdr, sizeof( id_mdlhdr ) ) )
|
||||
{
|
||||
fprintf( stderr, "ERROR: %s is not a valid external textures file.\n", texturename );
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else
|
||||
texture_hdr = model_hdr;
|
||||
|
||||
anim_hdr = malloc( sizeof( studiohdr_t* ) * model_hdr->numseqgroups );
|
||||
|
||||
if( !anim_hdr )
|
||||
{
|
||||
fputs( "ERROR: Couldn't allocate memory for sequences.\n", stderr );
|
||||
return false;
|
||||
}
|
||||
|
||||
anim_hdr[0] = model_hdr;
|
||||
|
||||
if( model_hdr->numseqgroups > 1 )
|
||||
{
|
||||
Q_strcpy( seqgroupname, modelname );
|
||||
|
||||
for( i = 1; i < model_hdr->numseqgroups; i++ )
|
||||
{
|
||||
Q_sprintf( &seqgroupname[len], "%02d.mdl", i );
|
||||
|
||||
anim_hdr[i] = (studiohdr_t *)LoadFile( seqgroupname );
|
||||
|
||||
if( !anim_hdr[i] )
|
||||
{
|
||||
fprintf( stderr, "ERROR: Can't open sequence file %s\n", seqgroupname );
|
||||
return false;
|
||||
}
|
||||
|
||||
if( memcmp( &anim_hdr[i]->ident, id_seqhdr, sizeof( id_seqhdr ) ) )
|
||||
{
|
||||
fprintf( stderr, "ERROR: %s is not a valid sequence file.\n", seqgroupname );
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
COM_FileBase( modelname, modelfile );
|
||||
|
||||
SequenceNameFix();
|
||||
|
||||
BoneNameFix();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
============
|
||||
ShowHelp
|
||||
============
|
||||
*/
|
||||
static void ShowHelp( const char *app_name )
|
||||
{
|
||||
printf( "usage: %s source_file\n", app_name );
|
||||
printf( " %s source_file target_directory\n", app_name );
|
||||
}
|
||||
|
||||
int main( int argc, char *argv[] )
|
||||
{
|
||||
puts( "\nHalf-Life Studio Model Decompiler " APP_VERSION );
|
||||
puts( "Copyright Flying With Gauss 2020 (c) " );
|
||||
puts( "--------------------------------------------------" );
|
||||
|
||||
if( argc == 1 )
|
||||
{
|
||||
ShowHelp( argv[0] );
|
||||
goto end;
|
||||
}
|
||||
else if( argc == 3 )
|
||||
{
|
||||
if( Q_strlen( argv[2] ) > MAX_SYSPATH - 1 )
|
||||
{
|
||||
fputs( "ERROR: Destination path is too long.\n", stderr );
|
||||
goto end;
|
||||
}
|
||||
|
||||
Q_strcpy( destdir, argv[2] );
|
||||
}
|
||||
|
||||
if( !LoadActivityList( argv[0] ) || !LoadMDL( argv[1] ) )
|
||||
goto end;
|
||||
|
||||
WriteQCScript();
|
||||
WriteSMD();
|
||||
WriteTextures();
|
||||
|
||||
puts( "Done." );
|
||||
|
||||
end:
|
||||
puts( "--------------------------------------------------" );
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
26
utils/mdldec/mdldec.h
Normal file
26
utils/mdldec/mdldec.h
Normal file
@ -0,0 +1,26 @@
|
||||
/*
|
||||
mdldec.h - Half-Life Studio Model Decompiler
|
||||
Copyright (C) 2020 Andrey Akhmichin
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
*/
|
||||
#pragma once
|
||||
#ifndef MDLDEC_H
|
||||
#define MDLDEC_H
|
||||
|
||||
extern char destdir[MAX_SYSPATH];
|
||||
extern char modelfile[MAX_SYSPATH];
|
||||
extern studiohdr_t *model_hdr;
|
||||
extern studiohdr_t *texture_hdr;
|
||||
extern studiohdr_t **anim_hdr;
|
||||
|
||||
#endif // MDLDEC_H
|
||||
|
649
utils/mdldec/qc.c
Normal file
649
utils/mdldec/qc.c
Normal file
@ -0,0 +1,649 @@
|
||||
/*
|
||||
qc.c - Quake C script writer
|
||||
Copyright (C) 2020 Andrey Akhmichin
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include "eiface.h"
|
||||
#include "studio.h"
|
||||
#include "crtlib.h"
|
||||
#include "version.h"
|
||||
#include "mdldec.h"
|
||||
#include "utils.h"
|
||||
#include "qc.h"
|
||||
|
||||
static char **activity_names;
|
||||
static int activity_count;
|
||||
|
||||
/*
|
||||
============
|
||||
LoadActivityList
|
||||
============
|
||||
*/
|
||||
qboolean LoadActivityList( const char *appname )
|
||||
{
|
||||
FILE *fp;
|
||||
const char *p;
|
||||
char path[MAX_SYSPATH];
|
||||
char buf[256];
|
||||
|
||||
fp = fopen( ACTIVITIES_FILE, "r" );
|
||||
|
||||
if( !fp )
|
||||
{
|
||||
p = getenv( "MDLDEC_ACT_PATH" );
|
||||
|
||||
if( !p )
|
||||
{
|
||||
fprintf( stderr, "ERROR: Couldn't find file " ACTIVITIES_FILE ".\n" \
|
||||
"Place " ACTIVITIES_FILE " beside %s or set MDLDEC_ACT_PATH environment variable.\n", appname );
|
||||
return false;
|
||||
}
|
||||
|
||||
Q_strncpy( path, p, MAX_SYSPATH - 1 );
|
||||
|
||||
COM_PathSlashFix( path );
|
||||
|
||||
Q_strncat( path, ACTIVITIES_FILE, MAX_SYSPATH );
|
||||
|
||||
fp = fopen( path, "r" );
|
||||
|
||||
if( !fp )
|
||||
{
|
||||
fputs( "ERROR: Can't open file " ACTIVITIES_FILE ".\n", stderr );
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
while( fgets( buf, sizeof( buf ), fp ) )
|
||||
{
|
||||
activity_names = realloc( activity_names, sizeof( char* ) * ++activity_count );
|
||||
|
||||
if( !activity_names )
|
||||
{
|
||||
fputs( "ERROR: Couldn't allocate memory for activities strings.\n", stderr );
|
||||
return false;
|
||||
}
|
||||
|
||||
COM_RemoveLineFeed( buf );
|
||||
|
||||
activity_names[activity_count - 1] = strdup( buf );
|
||||
|
||||
if( !activity_names[activity_count - 1] )
|
||||
{
|
||||
fputs( "ERROR: Couldn't allocate memory for activities strings.\n", stderr );
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
fclose( fp );
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
============
|
||||
FindActivityName
|
||||
============
|
||||
*/
|
||||
static const char *FindActivityName( int type )
|
||||
{
|
||||
if( type >= 0 && type < activity_count )
|
||||
return activity_names[type];
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
============
|
||||
GetMotionTypeString
|
||||
============
|
||||
*/
|
||||
static void GetMotionTypeString( int type, char *str, qboolean is_composite )
|
||||
{
|
||||
const char *p = NULL;
|
||||
|
||||
str[0] = '\0';
|
||||
|
||||
if( is_composite )
|
||||
{
|
||||
if( type & STUDIO_X )
|
||||
Q_strcat( str, " X" );
|
||||
|
||||
if( type & STUDIO_Y )
|
||||
Q_strcat( str, " Y" );
|
||||
|
||||
if( type & STUDIO_Z )
|
||||
Q_strcat( str, " Z" );
|
||||
|
||||
if( type & STUDIO_XR )
|
||||
Q_strcat( str, " XR" );
|
||||
|
||||
if( type & STUDIO_YR )
|
||||
Q_strcat( str, " YR" );
|
||||
|
||||
if( type & STUDIO_ZR )
|
||||
Q_strcat( str, " ZR" );
|
||||
|
||||
if( type & STUDIO_LX )
|
||||
Q_strcat( str, " LX" );
|
||||
|
||||
if( type & STUDIO_LY )
|
||||
Q_strcat( str, " LY" );
|
||||
|
||||
if( type & STUDIO_LZ )
|
||||
Q_strcat( str, " LZ" );
|
||||
|
||||
if( type & STUDIO_AX )
|
||||
Q_strcat( str, " AX" );
|
||||
|
||||
if( type & STUDIO_AY )
|
||||
Q_strcat( str, " AY" );
|
||||
|
||||
if( type & STUDIO_AZ )
|
||||
Q_strcat( str, " AZ" );
|
||||
|
||||
if( type & STUDIO_AXR )
|
||||
Q_strcat( str, " AXR" );
|
||||
|
||||
if( type & STUDIO_AYR )
|
||||
Q_strcat( str, " AYR" );
|
||||
|
||||
if( type & STUDIO_AZR )
|
||||
Q_strcat( str, " AZR" );
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
type &= STUDIO_TYPES;
|
||||
|
||||
switch( type )
|
||||
{
|
||||
case STUDIO_X: p = "X"; break;
|
||||
case STUDIO_Y: p = "Y"; break;
|
||||
case STUDIO_Z: p = "Z"; break;
|
||||
case STUDIO_XR: p = "XR"; break;
|
||||
case STUDIO_YR: p = "YR"; break;
|
||||
case STUDIO_ZR: p = "ZR"; break;
|
||||
case STUDIO_LX: p = "LX"; break;
|
||||
case STUDIO_LY: p = "LY"; break;
|
||||
case STUDIO_LZ: p = "LZ"; break;
|
||||
case STUDIO_AX: p = "AX"; break;
|
||||
case STUDIO_AY: p = "AY"; break;
|
||||
case STUDIO_AZ: p = "AZ"; break;
|
||||
case STUDIO_AXR: p = "AXR"; break;
|
||||
case STUDIO_AYR: p = "AYR"; break;
|
||||
case STUDIO_AZR: p = "AZR"; break;
|
||||
default: break;
|
||||
}
|
||||
|
||||
if( p )
|
||||
Q_strcpy( str, p );
|
||||
}
|
||||
|
||||
/*
|
||||
============
|
||||
WriteTextureRenderMode
|
||||
============
|
||||
*/
|
||||
static void WriteTextureRenderMode( FILE *fp )
|
||||
{
|
||||
int i;
|
||||
mstudiotexture_t *texture;
|
||||
|
||||
for( i = 0; i < texture_hdr->numtextures; i++ )
|
||||
{
|
||||
texture = (mstudiotexture_t *)( (byte *)texture_hdr + texture_hdr->textureindex ) + i;
|
||||
|
||||
if( texture->flags & STUDIO_NF_FLATSHADE )
|
||||
fprintf( fp,"$texrendermode \"%s\" \"flatshade\" \n", texture->name ); // sven-coop extension
|
||||
|
||||
if( texture->flags & STUDIO_NF_CHROME )
|
||||
fprintf( fp, "$texrendermode \"%s\" \"chrome\" \n", texture->name ); // sven-coop extension/may be added in HLMV
|
||||
|
||||
if( texture->flags & STUDIO_NF_FULLBRIGHT )
|
||||
fprintf( fp, "$texrendermode \"%s\" \"fullbright\" \n", texture->name ); // sven-coop extension/xash3d extension
|
||||
|
||||
if( texture->flags & STUDIO_NF_NOMIPS )
|
||||
fprintf( fp, "$texrendermode \"%s\" \"nomips\" \n", texture->name ); // sven-coop extension
|
||||
|
||||
if( texture->flags & STUDIO_NF_NOSMOOTH )
|
||||
{
|
||||
fprintf( fp, "$texrendermode \"%s\" \"alpha\" \n", texture->name ); // sven-coop extension
|
||||
fprintf( fp, "$texrendermode \"%s\" \"nosmooth\" \n", texture->name ); // xash3d extension
|
||||
}
|
||||
|
||||
if( texture->flags & STUDIO_NF_ADDITIVE )
|
||||
fprintf( fp, "$texrendermode \"%s\" \"additive\" \n", texture->name );
|
||||
|
||||
if( texture->flags & STUDIO_NF_MASKED )
|
||||
fprintf( fp, "$texrendermode \"%s\" \"masked\" \n", texture->name );
|
||||
|
||||
if( texture->flags & ( STUDIO_NF_MASKED | STUDIO_NF_SOLID ) )
|
||||
fprintf( fp, "$texrendermode \"%s\" \"masked_solid\" \n", texture->name ); // xash3d extension
|
||||
|
||||
if( texture->flags & STUDIO_NF_TWOSIDE )
|
||||
fprintf( fp, "$texrendermode \"%s\" \"twoside\" \n", texture->name );
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
============
|
||||
WriteSkinFamilyInfo
|
||||
============
|
||||
*/
|
||||
static void WriteSkinFamilyInfo( FILE *fp )
|
||||
{
|
||||
int i, j, k;
|
||||
short *skinref, index;
|
||||
mstudiotexture_t *texture;
|
||||
|
||||
if( texture_hdr->numskinfamilies < 2 )
|
||||
return;
|
||||
|
||||
fprintf( fp, "\n// %i skin families\n", texture_hdr->numskinfamilies );
|
||||
|
||||
fputs( "$texturegroup skinfamilies \n{\n", fp );
|
||||
|
||||
skinref = (short *)( (byte *)texture_hdr + texture_hdr->skinindex );
|
||||
|
||||
for( i = 0; i < texture_hdr->numskinfamilies; ++i )
|
||||
{
|
||||
fputs( "{", fp );
|
||||
|
||||
for( j = 0; j < texture_hdr->numskinref; ++j )
|
||||
{
|
||||
index = *( skinref + i * texture_hdr->numskinref + j );
|
||||
|
||||
for( k = 0; k < texture_hdr->numskinfamilies; ++k )
|
||||
{
|
||||
if( index == *( skinref + k * texture_hdr->numskinref + j ) )
|
||||
continue;
|
||||
|
||||
texture = (mstudiotexture_t *)( (byte *)texture_hdr + texture_hdr->textureindex ) + index;
|
||||
|
||||
fprintf( fp, " \"%s\" ", texture->name );
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
fputs( "}\n", fp );
|
||||
}
|
||||
|
||||
fputs( "}\n", fp );
|
||||
}
|
||||
|
||||
/*
|
||||
============
|
||||
WriteAttachmentInfo
|
||||
============
|
||||
*/
|
||||
static void WriteAttachmentInfo( FILE *fp )
|
||||
{
|
||||
int i;
|
||||
mstudioattachment_t *attachment;
|
||||
mstudiobone_t *bone;
|
||||
|
||||
if( !model_hdr->numattachments )
|
||||
return;
|
||||
|
||||
fprintf( fp, "\n// %i attachment(s)\n", model_hdr->numattachments );
|
||||
|
||||
for( i = 0; i < model_hdr->numattachments; ++i )
|
||||
{
|
||||
attachment = (mstudioattachment_t *)( (byte *)model_hdr + model_hdr->attachmentindex ) + i;
|
||||
bone = (mstudiobone_t *)( (byte *)model_hdr + model_hdr->boneindex ) + attachment->bone;
|
||||
|
||||
fprintf( fp, "$attachment %i \"%s\" %f %f %f\n", i, bone->name, attachment->org[0], attachment->org[1], attachment->org[2] );
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
============
|
||||
WriteBodyGroupInfo
|
||||
============
|
||||
*/
|
||||
static void WriteBodyGroupInfo( FILE *fp )
|
||||
{
|
||||
int i, j;
|
||||
mstudiobodyparts_t *bodypart;
|
||||
mstudiomodel_t *model;
|
||||
char modelname[64];
|
||||
|
||||
fputs( "\n// reference mesh(es)\n", fp );
|
||||
|
||||
for( i = 0; i < model_hdr->numbodyparts; ++i )
|
||||
{
|
||||
bodypart = (mstudiobodyparts_t *) ( (byte *)model_hdr + model_hdr->bodypartindex ) + i;
|
||||
|
||||
if( bodypart->nummodels == 1 )
|
||||
{
|
||||
model = (mstudiomodel_t *)( (byte *)model_hdr + bodypart->modelindex );
|
||||
|
||||
COM_FileBase( model->name, modelname );
|
||||
|
||||
fprintf( fp, "$body \"%s\" \"%s\"\n\n", bodypart->name, model->name );
|
||||
continue;
|
||||
}
|
||||
|
||||
fprintf( fp, "$bodygroup \"%s\"\n", bodypart->name );
|
||||
|
||||
fputs( "{\n", fp );
|
||||
|
||||
for( j = 0; j < bodypart->nummodels; ++j )
|
||||
{
|
||||
model = (mstudiomodel_t *)( (byte *)model_hdr + bodypart->modelindex ) + j;
|
||||
|
||||
if( !Q_strncmp( model->name, "blank", 5 ) )
|
||||
{
|
||||
fputs( "blank\n", fp );
|
||||
continue;
|
||||
}
|
||||
|
||||
COM_FileBase( model->name, modelname );
|
||||
|
||||
fprintf( fp, "studio \"%s\"\n", modelname );
|
||||
}
|
||||
|
||||
fputs( "}\n\n" , fp );
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
============
|
||||
WriteControllerInfo
|
||||
============
|
||||
*/
|
||||
static void WriteControllerInfo( FILE *fp )
|
||||
{
|
||||
int i;
|
||||
mstudiobonecontroller_t *bonecontroller;
|
||||
mstudiobone_t *bone;
|
||||
char motion_types[64];
|
||||
|
||||
if( !model_hdr->numbonecontrollers )
|
||||
return;
|
||||
|
||||
fprintf( fp, "\n// %i bone controller(s)\n", model_hdr->numbonecontrollers );
|
||||
|
||||
for( i = 0; i < model_hdr->numbonecontrollers; ++i )
|
||||
{
|
||||
bonecontroller = (mstudiobonecontroller_t *)( (byte *)model_hdr + model_hdr->bonecontrollerindex ) + i;
|
||||
bone = (mstudiobone_t *)( (byte *)model_hdr + model_hdr->boneindex ) + bonecontroller->bone;
|
||||
|
||||
GetMotionTypeString( bonecontroller->type & ~STUDIO_RLOOP, motion_types, false );
|
||||
|
||||
fprintf( fp, "$controller %i \"%s\" %s %f %f\n",
|
||||
bonecontroller->index, bone->name, motion_types,
|
||||
bonecontroller->start, bonecontroller->end );
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
============
|
||||
WriteHitBoxInfo
|
||||
============
|
||||
*/
|
||||
static void WriteHitBoxInfo( FILE *fp )
|
||||
{
|
||||
int i;
|
||||
mstudiobbox_t *hitbox;
|
||||
mstudiobone_t *bone;
|
||||
|
||||
if( !model_hdr->numhitboxes )
|
||||
return;
|
||||
|
||||
fprintf( fp, "\n// %i hit box(es)\n", model_hdr->numhitboxes );
|
||||
|
||||
for( i = 0; i < model_hdr->numhitboxes; i++ )
|
||||
{
|
||||
hitbox = (mstudiobbox_t *)( (byte *)model_hdr + model_hdr->hitboxindex ) + i;
|
||||
bone = (mstudiobone_t *)( (byte *)model_hdr + model_hdr->boneindex ) + hitbox->bone;
|
||||
|
||||
fprintf( fp, "$hbox %i \"%s\" %f %f %f %f %f %f\n",
|
||||
hitbox->group, bone->name,
|
||||
hitbox->bbmin[0], hitbox->bbmin[1], hitbox->bbmin[2],
|
||||
hitbox->bbmax[0], hitbox->bbmax[1], hitbox->bbmax[2] );
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
============
|
||||
WriteSequenceInfo
|
||||
============
|
||||
*/
|
||||
static void WriteSequenceInfo( FILE *fp )
|
||||
{
|
||||
int i, j;
|
||||
const char *activity;
|
||||
char motion_types[256];
|
||||
mstudioevent_t *event;
|
||||
mstudioseqdesc_t *seqdesc;
|
||||
|
||||
if( model_hdr->numseqgroups > 1 )
|
||||
fputs( "\n$sequencegroupsize 64\n", fp );
|
||||
|
||||
if( model_hdr->numseq > 0 )
|
||||
fprintf( fp, "\n// %i animation sequence(s)\n", model_hdr->numseq );
|
||||
|
||||
for( i = 0; i < model_hdr->numseq; ++i )
|
||||
{
|
||||
seqdesc = (mstudioseqdesc_t *)( (byte *)model_hdr + model_hdr->seqindex ) + i;
|
||||
|
||||
fprintf( fp, "$sequence \"%s\" ", seqdesc->label );
|
||||
|
||||
if( seqdesc->numblends > 1 )
|
||||
{
|
||||
if( seqdesc->numblends > 2 )
|
||||
{
|
||||
fputs( "{\n", fp );
|
||||
|
||||
for( j = 0; j < seqdesc->numblends; j++ )
|
||||
{
|
||||
fputs( " ", fp );
|
||||
|
||||
fprintf( fp, "\"%s_blend%i\" ", seqdesc->label, j + 1 );
|
||||
|
||||
fputs( "\n", fp );
|
||||
}
|
||||
|
||||
fputs( " ", fp );
|
||||
}
|
||||
else
|
||||
{
|
||||
fprintf( fp, "\"%s_blend1\" ", seqdesc->label );
|
||||
fprintf( fp, "\"%s_blend2\" ", seqdesc->label );
|
||||
}
|
||||
|
||||
GetMotionTypeString( seqdesc->blendtype[0], motion_types, false );
|
||||
|
||||
fprintf( fp, "blend %s %.0f %.0f",
|
||||
motion_types, seqdesc->blendstart[0], seqdesc->blendend[0] );
|
||||
}
|
||||
else
|
||||
{
|
||||
fprintf( fp, "\"%s\"", seqdesc->label );
|
||||
}
|
||||
|
||||
if( seqdesc->motiontype )
|
||||
{
|
||||
GetMotionTypeString( seqdesc->motiontype, motion_types, true );
|
||||
|
||||
fprintf( fp, "%s", motion_types );
|
||||
}
|
||||
|
||||
fprintf( fp, " fps %.0f ", seqdesc->fps );
|
||||
|
||||
if( seqdesc->flags == 1 )
|
||||
fputs( "loop ", fp );
|
||||
|
||||
if( seqdesc->activity )
|
||||
{
|
||||
activity = FindActivityName( seqdesc->activity );
|
||||
|
||||
if( activity )
|
||||
{
|
||||
fprintf( fp, "%s %i ", activity, seqdesc->actweight );
|
||||
}
|
||||
else
|
||||
{
|
||||
printf( "WARNING: Sequence %s has a custom activity flag (ACT_%i %i).\n",
|
||||
seqdesc->label, seqdesc->activity, seqdesc->actweight );
|
||||
|
||||
fprintf( fp, "ACT_%i %i ", seqdesc->activity, seqdesc->actweight );
|
||||
}
|
||||
}
|
||||
|
||||
if( seqdesc->entrynode && seqdesc->exitnode )
|
||||
{
|
||||
if( seqdesc->entrynode == seqdesc->exitnode )
|
||||
fprintf( fp, "node %i ", seqdesc->entrynode );
|
||||
else if( seqdesc->nodeflags )
|
||||
fprintf( fp, "rtransition %i %i ", seqdesc->entrynode, seqdesc->exitnode );
|
||||
else
|
||||
fprintf( fp, "transition %i %i ", seqdesc->entrynode, seqdesc->exitnode );
|
||||
}
|
||||
|
||||
if( seqdesc->numevents > 2 )
|
||||
{
|
||||
fputs( "{\n ", fp );
|
||||
|
||||
for( j = 0; j < seqdesc->numevents; j++ )
|
||||
{
|
||||
if( seqdesc->numblends <= 2 )
|
||||
fputs( " ", fp );
|
||||
else
|
||||
fputs( " ", fp );
|
||||
|
||||
event = (mstudioevent_t *)( (byte *)model_hdr + seqdesc->eventindex ) + j;
|
||||
|
||||
fprintf( fp, "{ event %i %i", event->event, event->frame );
|
||||
|
||||
if( event->options[0] != '\0' )
|
||||
fprintf( fp, " \"%s\"", event->options );
|
||||
|
||||
fputs( " }\n ", fp );
|
||||
}
|
||||
|
||||
fputs( "}", fp );
|
||||
}
|
||||
else
|
||||
{
|
||||
for( j = 0; j < seqdesc->numevents; j++ )
|
||||
{
|
||||
event = (mstudioevent_t *)( (byte *)model_hdr + seqdesc->eventindex ) + j;
|
||||
|
||||
fprintf( fp, "{ event %i %i", event->event, event->frame );
|
||||
|
||||
if( event->options[0] != '\0')
|
||||
fprintf( fp, " \"%s\"", event->options );
|
||||
|
||||
fputs( " } ", fp );
|
||||
}
|
||||
}
|
||||
|
||||
fputs( "\n", fp );
|
||||
|
||||
if( seqdesc->numblends > 2 )
|
||||
fputs( "}\n", fp );
|
||||
|
||||
if( seqdesc->numpivots )
|
||||
printf( "WARNING: Sequence %s uses %i foot pivots, feature not supported.\n",
|
||||
seqdesc->label, seqdesc->numpivots );
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
============
|
||||
WriteQCScript
|
||||
============
|
||||
*/
|
||||
void WriteQCScript( void )
|
||||
{
|
||||
FILE *fp;
|
||||
char filename[MAX_SYSPATH];
|
||||
size_t len;
|
||||
|
||||
len = Q_snprintf( filename, MAX_SYSPATH, "%s%s.qc", destdir, modelfile );
|
||||
|
||||
if( len >= MAX_SYSPATH )
|
||||
{
|
||||
fprintf( stderr, "ERROR: Destination path is too long. Can't write %s.qc\n", modelfile );
|
||||
return;
|
||||
}
|
||||
|
||||
fp = fopen( filename, "w" );
|
||||
|
||||
if( !fp )
|
||||
{
|
||||
fprintf( stderr, "ERROR: Can't write %s\n", filename );
|
||||
return;
|
||||
}
|
||||
|
||||
fputs( "/*\n", fp );
|
||||
fputs( "==============================================================================\n\n", fp );
|
||||
fputs( "QC script generated by Half-Life Studio Model Decompiler " APP_VERSION "\n", fp );
|
||||
|
||||
fprintf( fp, "Copyright Flying With Gauss %s (c) \n\n", Q_timestamp( TIME_YEAR_ONLY ) );
|
||||
fprintf( fp, "%s.mdl\n\n", modelfile );
|
||||
|
||||
fputs( "Original internal name:\n", fp );
|
||||
|
||||
fprintf( fp, "\"%s\"\n\n", model_hdr->name );
|
||||
|
||||
fputs( "==============================================================================\n", fp );
|
||||
fputs( "*/\n\n", fp );
|
||||
|
||||
fprintf( fp, "$modelname \"%s.mdl\"\n", modelfile );
|
||||
|
||||
fputs( "$cd \".\\\"\n", fp );
|
||||
fputs( "$cdtexture \".\\\"\n", fp );
|
||||
fputs( "$scale 1.0\n", fp );
|
||||
fputs( "$cliptotextures\n", fp );
|
||||
fputs( "\n", fp );
|
||||
|
||||
if( !model_hdr->numtextures )
|
||||
fputs( "$externaltextures\n", fp );
|
||||
|
||||
if( model_hdr->flags != 0 )
|
||||
{
|
||||
fprintf( fp, "$flags %i\n", model_hdr->flags );
|
||||
|
||||
printf( "WARNING: This model uses the $flags keyword set to %i\n", model_hdr->flags );
|
||||
}
|
||||
|
||||
fputs( "\n", fp );
|
||||
|
||||
fprintf( fp, "$bbox %f %f %f", model_hdr->min[0], model_hdr->min[1], model_hdr->min[2] );
|
||||
fprintf( fp, " %f %f %f\n", model_hdr->max[0], model_hdr->max[1], model_hdr->max[2] );
|
||||
fprintf( fp, "$cbox %f %f %f", model_hdr->bbmin[0], model_hdr->bbmin[1], model_hdr->bbmin[2] );
|
||||
fprintf( fp, " %f %f %f\n", model_hdr->bbmax[0], model_hdr->bbmax[1], model_hdr->bbmax[2] );
|
||||
fprintf( fp, "$eyeposition %f %f %f\n", model_hdr->eyeposition[0], model_hdr->eyeposition[1], model_hdr->eyeposition[2] );
|
||||
|
||||
fputs( "\n", fp );
|
||||
|
||||
WriteBodyGroupInfo( fp );
|
||||
WriteTextureRenderMode( fp );
|
||||
WriteSkinFamilyInfo( fp );
|
||||
WriteAttachmentInfo( fp );
|
||||
WriteControllerInfo( fp );
|
||||
WriteHitBoxInfo( fp );
|
||||
WriteSequenceInfo( fp );
|
||||
|
||||
fputs( "\n// End of QC script.\n", fp );
|
||||
fclose( fp );
|
||||
|
||||
printf( "QC Script: %s\n", filename );
|
||||
}
|
||||
|
25
utils/mdldec/qc.h
Normal file
25
utils/mdldec/qc.h
Normal file
@ -0,0 +1,25 @@
|
||||
/*
|
||||
qc.h - Quake C script writer
|
||||
Copyright (C) 2020 Andrey Akhmichin
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
*/
|
||||
#pragma once
|
||||
#ifndef QC_H
|
||||
#define QC_H
|
||||
|
||||
#define ACTIVITIES_FILE "activities.txt"
|
||||
|
||||
qboolean LoadActivityList( const char *appname );
|
||||
void WriteQCScript( void );
|
||||
|
||||
#endif // QC_H
|
||||
|
103
utils/mdldec/res/activities.txt
Normal file
103
utils/mdldec/res/activities.txt
Normal file
@ -0,0 +1,103 @@
|
||||
ACT_RESET
|
||||
ACT_IDLE
|
||||
ACT_GUARD
|
||||
ACT_WALK
|
||||
ACT_RUN
|
||||
ACT_FLY
|
||||
ACT_SWIM
|
||||
ACT_HOP
|
||||
ACT_LEAP
|
||||
ACT_FALL
|
||||
ACT_LAND
|
||||
ACT_STRAFE_LEFT
|
||||
ACT_STRAFE_RIGHT
|
||||
ACT_ROLL_LEFT
|
||||
ACT_ROLL_RIGHT
|
||||
ACT_TURN_LEFT
|
||||
ACT_TURN_RIGHT
|
||||
ACT_CROUCH
|
||||
ACT_CROUCHIDLE
|
||||
ACT_STAND
|
||||
ACT_USE
|
||||
ACT_SIGNAL1
|
||||
ACT_SIGNAL2
|
||||
ACT_SIGNAL3
|
||||
ACT_TWITCH
|
||||
ACT_COWER
|
||||
ACT_SMALL_FLINCH
|
||||
ACT_BIG_FLINCH
|
||||
ACT_RANGE_ATTACK1
|
||||
ACT_RANGE_ATTACK2
|
||||
ACT_MELEE_ATTACK1
|
||||
ACT_MELEE_ATTACK2
|
||||
ACT_RELOAD
|
||||
ACT_ARM
|
||||
ACT_DISARM
|
||||
ACT_EAT
|
||||
ACT_DIESIMPLE
|
||||
ACT_DIEBACKWARD
|
||||
ACT_DIEFORWARD
|
||||
ACT_DIEVIOLENT
|
||||
ACT_BARNACLE_HIT
|
||||
ACT_BARNACLE_PULL
|
||||
ACT_BARNACLE_CHOMP
|
||||
ACT_BARNACLE_CHEW
|
||||
ACT_SLEEP
|
||||
ACT_INSPECT_FLOOR
|
||||
ACT_INSPECT_WALL
|
||||
ACT_IDLE_ANGRY
|
||||
ACT_WALK_HURT
|
||||
ACT_RUN_HURT
|
||||
ACT_HOVER
|
||||
ACT_GLIDE
|
||||
ACT_FLY_LEFT
|
||||
ACT_FLY_RIGHT
|
||||
ACT_DETECT_SCENT
|
||||
ACT_SNIFF
|
||||
ACT_BITE
|
||||
ACT_THREAT_DISPLAY
|
||||
ACT_FEAR_DISPLAY
|
||||
ACT_EXCITED
|
||||
ACT_SPECIAL_ATTACK1
|
||||
ACT_SPECIAL_ATTACK2
|
||||
ACT_COMBAT_IDLE
|
||||
ACT_WALK_SCARED
|
||||
ACT_RUN_SCARED
|
||||
ACT_VICTORY_DANCE
|
||||
ACT_DIE_HEADSHOT
|
||||
ACT_DIE_CHESTSHOT
|
||||
ACT_DIE_GUTSHOT
|
||||
ACT_DIE_BACKSHOT
|
||||
ACT_FLINCH_HEAD
|
||||
ACT_FLINCH_CHEST
|
||||
ACT_FLINCH_STOMACH
|
||||
ACT_FLINCH_LEFTARM
|
||||
ACT_FLINCH_RIGHTARM
|
||||
ACT_FLINCH_LEFTLEG
|
||||
ACT_FLINCH_RIGHTLEG
|
||||
ACT_VM_NONE
|
||||
ACT_VM_DEPLOY
|
||||
ACT_VM_DEPLOY_EMPTY
|
||||
ACT_VM_HOLSTER
|
||||
ACT_VM_HOLSTER_EMPTY
|
||||
ACT_VM_IDLE1
|
||||
ACT_VM_IDLE2
|
||||
ACT_VM_IDLE3
|
||||
ACT_VM_RANGE_ATTACK1
|
||||
ACT_VM_RANGE_ATTACK2
|
||||
ACT_VM_RANGE_ATTACK3
|
||||
ACT_VM_MELEE_ATTACK1
|
||||
ACT_VM_MELEE_ATTACK2
|
||||
ACT_VM_MELEE_ATTACK3
|
||||
ACT_VM_SHOOT_EMPTY
|
||||
ACT_VM_START_RELOAD
|
||||
ACT_VM_RELOAD
|
||||
ACT_VM_RELOAD_EMPTY
|
||||
ACT_VM_TURNON
|
||||
ACT_VM_TURNOFF
|
||||
ACT_VM_PUMP
|
||||
ACT_VM_PUMP_EMPTY
|
||||
ACT_VM_START_CHARGE
|
||||
ACT_VM_CHARGE
|
||||
ACT_VM_OVERLOAD
|
||||
ACT_VM_IDLE_EMPTY
|
549
utils/mdldec/smd.c
Normal file
549
utils/mdldec/smd.c
Normal file
@ -0,0 +1,549 @@
|
||||
/*
|
||||
smd.c - Studio Model Data format writer
|
||||
Copyright (C) 2020 Andrey Akhmichin
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include "const.h"
|
||||
#include "com_model.h"
|
||||
#include "xash3d_mathlib.h"
|
||||
#include "crtlib.h"
|
||||
#include "studio.h"
|
||||
#include "mdldec.h"
|
||||
#include "smd.h"
|
||||
|
||||
static matrix3x4 *bonetransform;
|
||||
|
||||
/*
|
||||
============
|
||||
CreateBoneTransformMatrices
|
||||
============
|
||||
*/
|
||||
static qboolean CreateBoneTransformMatrices( void )
|
||||
{
|
||||
bonetransform = calloc( model_hdr->numbones, sizeof( matrix3x4 ) );
|
||||
|
||||
if( !bonetransform )
|
||||
{
|
||||
fputs( "ERROR: Couldn't allocate memory for bone transformation matrices!\n", stderr );
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
============
|
||||
FillBoneTransformMatrices
|
||||
============
|
||||
*/
|
||||
static void FillBoneTransformMatrices( void )
|
||||
{
|
||||
int i;
|
||||
mstudiobone_t *bone;
|
||||
matrix3x4 bonematrix;
|
||||
vec4_t q;
|
||||
|
||||
for( i = 0; i < model_hdr->numbones; i++ )
|
||||
{
|
||||
bone = (mstudiobone_t *)( (byte *)model_hdr + model_hdr->boneindex ) + i;
|
||||
|
||||
AngleQuaternion( &bone->value[3], q, true );
|
||||
Matrix3x4_FromOriginQuat( bonematrix, q, bone->value );
|
||||
|
||||
if( bone->parent == -1 )
|
||||
{
|
||||
Matrix3x4_Copy( bonetransform[i], bonematrix );
|
||||
continue;
|
||||
}
|
||||
|
||||
Matrix3x4_ConcatTransforms( bonetransform[i], bonetransform[bone->parent], bonematrix );
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
============
|
||||
RemoveBoneTransformMatrices
|
||||
============
|
||||
*/
|
||||
static void RemoveBoneTransformMatrices( void )
|
||||
{
|
||||
free( bonetransform );
|
||||
}
|
||||
|
||||
/*
|
||||
============
|
||||
ClipRotations
|
||||
============
|
||||
*/
|
||||
static void ClipRotations( vec3_t angle )
|
||||
{
|
||||
int i;
|
||||
|
||||
for( i = 0; i < 3; i++ )
|
||||
{
|
||||
while( angle[i] >= M_PI_F )
|
||||
angle[i] -= M_PI2_F;
|
||||
|
||||
while( angle[i] < -M_PI_F )
|
||||
angle[i] += M_PI2_F;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
============
|
||||
ProperBoneRotationZ
|
||||
============
|
||||
*/
|
||||
static void ProperBoneRotationZ( mstudioseqdesc_t *seqdesc, vec_t *motion, int frame, float angle )
|
||||
{
|
||||
int i;
|
||||
float c, s, x, y;
|
||||
float rot;
|
||||
|
||||
for( i = 0; i < 3; i++ )
|
||||
motion[i] += frame * 1.0f / seqdesc->numframes * seqdesc->linearmovement[i];
|
||||
|
||||
rot = DEG2RAD( angle );
|
||||
|
||||
s = sin( rot );
|
||||
c = cos( rot );
|
||||
|
||||
x = motion[0];
|
||||
y = motion[1];
|
||||
|
||||
motion[0] = c * x - s * y;
|
||||
motion[1] = s * x + c * y;
|
||||
|
||||
motion[5] += rot;
|
||||
}
|
||||
|
||||
/*
|
||||
============
|
||||
CalcBonePosition
|
||||
============
|
||||
*/
|
||||
static void CalcBonePosition( mstudioanim_t *anim, mstudiobone_t *bone, vec_t *motion, int frame )
|
||||
{
|
||||
int i, j;
|
||||
float value;
|
||||
mstudioanimvalue_t *animvalue;
|
||||
|
||||
for( i = 0; i < 6; i++ )
|
||||
{
|
||||
motion[i] = bone->value[i];
|
||||
|
||||
if( !anim->offset[i] )
|
||||
continue;
|
||||
|
||||
animvalue = (mstudioanimvalue_t *)( (byte *)anim + anim->offset[i] );
|
||||
|
||||
j = frame;
|
||||
|
||||
while( animvalue->num.total <= j )
|
||||
{
|
||||
j -= animvalue->num.total;
|
||||
animvalue += animvalue->num.valid + 1;
|
||||
}
|
||||
|
||||
if( animvalue->num.valid > j )
|
||||
value = animvalue[j + 1].value;
|
||||
else
|
||||
value = animvalue[animvalue->num.valid].value;
|
||||
|
||||
motion[i] += value * bone->scale[i];
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
============
|
||||
WriteNodes
|
||||
============
|
||||
*/
|
||||
static void WriteNodes( FILE *fp )
|
||||
{
|
||||
int i;
|
||||
mstudiobone_t *bone;
|
||||
|
||||
fputs( "nodes\n", fp );
|
||||
|
||||
for( i = 0; i < model_hdr->numbones; i++ )
|
||||
{
|
||||
bone = (mstudiobone_t *)( (byte *)model_hdr + model_hdr->boneindex ) + i;
|
||||
|
||||
fprintf( fp, "%3i \"%s\" %i\n", i, bone->name, bone->parent );
|
||||
}
|
||||
|
||||
fputs( "end\n", fp );
|
||||
}
|
||||
|
||||
/*
|
||||
============
|
||||
WriteSkeleton
|
||||
============
|
||||
*/
|
||||
static void WriteSkeleton( FILE *fp )
|
||||
{
|
||||
int i, j;
|
||||
mstudiobone_t *bone;
|
||||
|
||||
fputs( "skeleton\n", fp );
|
||||
fputs( "time 0\n", fp );
|
||||
|
||||
for( i = 0; i < model_hdr->numbones; i++ )
|
||||
{
|
||||
bone = (mstudiobone_t *)( (byte *)model_hdr + model_hdr->boneindex ) + i;
|
||||
|
||||
fprintf( fp, "%3i", i );
|
||||
|
||||
for( j = 0; j < 6; j++ )
|
||||
fprintf( fp, " %f", bone->value[j] );
|
||||
|
||||
fputs( "\n", fp );
|
||||
}
|
||||
|
||||
fputs( "end\n", fp );
|
||||
}
|
||||
|
||||
/*
|
||||
============
|
||||
WriteTriangleInfo
|
||||
============
|
||||
*/
|
||||
static void WriteTriangleInfo( FILE *fp, mstudiomodel_t *model, mstudiotexture_t *texture, mstudiotrivert_t **triverts, qboolean isevenstrip )
|
||||
{
|
||||
int i, indices[3];
|
||||
int vert_index;
|
||||
int norm_index;
|
||||
int bone_index;
|
||||
float s, t, u, v;
|
||||
byte *vertbone;
|
||||
vec3_t *studioverts;
|
||||
vec3_t *studionorms;
|
||||
vec3_t vert, norm;
|
||||
|
||||
if( isevenstrip )
|
||||
{
|
||||
indices[0] = 1;
|
||||
indices[1] = 2;
|
||||
indices[2] = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
indices[0] = 0;
|
||||
indices[1] = 1;
|
||||
indices[2] = 2;
|
||||
}
|
||||
|
||||
vertbone = ( (byte *)model_hdr + model->vertinfoindex );
|
||||
studioverts = (vec3_t *)( (byte *)model_hdr + model->vertindex );
|
||||
studionorms = (vec3_t *)( (byte *)model_hdr + model->normindex );
|
||||
|
||||
s = 1.0f / texture->width;
|
||||
t = 1.0f / texture->height;
|
||||
|
||||
fprintf( fp, "%s\n", texture->name );
|
||||
|
||||
for( i = 0; i < 3; i++ )
|
||||
{
|
||||
vert_index = triverts[indices[i]]->vertindex;
|
||||
norm_index = triverts[indices[i]]->normindex;
|
||||
bone_index = vertbone[vert_index];
|
||||
|
||||
Matrix3x4_VectorTransform( bonetransform[bone_index], studioverts[vert_index], vert );
|
||||
Matrix3x4_VectorRotate( bonetransform[bone_index], studionorms[norm_index], norm );
|
||||
VectorNormalize( norm );
|
||||
|
||||
u = ( triverts[indices[i]]->s + 1.0f ) * s;
|
||||
v = 1.0f - triverts[indices[i]]->t * t;
|
||||
|
||||
fprintf( fp, "%3i %f %f %f %f %f %f %f %f\n",
|
||||
bone_index,
|
||||
vert[0], vert[1], vert[2],
|
||||
norm[0], norm[1], norm[2],
|
||||
u, v );
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
============
|
||||
WriteTriangles
|
||||
============
|
||||
*/
|
||||
static void WriteTriangles( FILE *fp, mstudiomodel_t *model )
|
||||
{
|
||||
int i, j, k;
|
||||
mstudiomesh_t *mesh;
|
||||
mstudiotexture_t *texture;
|
||||
mstudiotrivert_t *triverts[3];
|
||||
short *tricmds;
|
||||
|
||||
fputs( "triangles\n", fp );
|
||||
|
||||
for( i = 0; i < model->nummesh; i++ )
|
||||
{
|
||||
mesh = (mstudiomesh_t *)( (byte *)model_hdr + model->meshindex ) + i;
|
||||
tricmds = (short *)( (byte *)model_hdr + mesh->triindex );
|
||||
texture = (mstudiotexture_t *)( (byte *)texture_hdr + texture_hdr->textureindex ) + mesh->skinref;
|
||||
|
||||
while( ( j = *( tricmds++ ) ) )
|
||||
{
|
||||
if( j >= 0 )
|
||||
{
|
||||
// triangle fan
|
||||
for( k = 0; j > 0; j--, k++, tricmds += 4 )
|
||||
{
|
||||
if( k == 0 )
|
||||
{
|
||||
triverts[0] = (mstudiotrivert_t *)tricmds;
|
||||
}
|
||||
else if( k == 1 )
|
||||
{
|
||||
triverts[2] = (mstudiotrivert_t *)tricmds;
|
||||
}
|
||||
else if( k == 2 )
|
||||
{
|
||||
triverts[1] = (mstudiotrivert_t *)tricmds;
|
||||
|
||||
WriteTriangleInfo( fp, model, texture, triverts, true );
|
||||
}
|
||||
else if( k % 2 )
|
||||
{
|
||||
triverts[0] = triverts[2];
|
||||
triverts[2] = (mstudiotrivert_t *)tricmds;
|
||||
|
||||
WriteTriangleInfo( fp, model, texture, triverts, false );
|
||||
}
|
||||
else
|
||||
{
|
||||
triverts[0] = triverts[1];
|
||||
triverts[1] = (mstudiotrivert_t *)tricmds;
|
||||
|
||||
WriteTriangleInfo( fp, model, texture, triverts, true );
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// triangle strip
|
||||
j = abs( j );
|
||||
|
||||
for( k = 0; j > 0; j--, k++, tricmds += 4 )
|
||||
{
|
||||
if( k == 0 )
|
||||
{
|
||||
triverts[0] = (mstudiotrivert_t *)tricmds;
|
||||
}
|
||||
else if( k == 1 )
|
||||
{
|
||||
triverts[2] = (mstudiotrivert_t *)tricmds;
|
||||
}
|
||||
else if( k == 2 )
|
||||
{
|
||||
triverts[1] = (mstudiotrivert_t *)tricmds;
|
||||
|
||||
WriteTriangleInfo( fp, model, texture, triverts, false );
|
||||
}
|
||||
else
|
||||
{
|
||||
triverts[2] = triverts[1];
|
||||
triverts[1] = (mstudiotrivert_t *)tricmds;
|
||||
|
||||
WriteTriangleInfo( fp, model, texture, triverts, false );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fputs( "end\n", fp );
|
||||
}
|
||||
|
||||
/*
|
||||
============
|
||||
WriteFrameInfo
|
||||
============
|
||||
*/
|
||||
static void WriteFrameInfo( FILE *fp, mstudioanim_t *anim, mstudioseqdesc_t *seqdesc, int frame )
|
||||
{
|
||||
int i, j;
|
||||
vec_t motion[6]; // x, y, z, xr, yr, zr
|
||||
mstudiobone_t *bone;
|
||||
|
||||
fprintf( fp, "time %i\n", frame );
|
||||
|
||||
for( i = 0; i < model_hdr->numbones; i++, anim++ )
|
||||
{
|
||||
bone = (mstudiobone_t *)( (byte *)model_hdr + model_hdr->boneindex ) + i;
|
||||
|
||||
CalcBonePosition( anim, bone, motion, frame );
|
||||
|
||||
if( bone->parent == -1 )
|
||||
ProperBoneRotationZ( seqdesc, motion, frame, 270.0f );
|
||||
|
||||
ClipRotations( &motion[3] );
|
||||
|
||||
fprintf( fp, "%3i ", i );
|
||||
|
||||
for( j = 0; j < 6; j++ )
|
||||
fprintf( fp, " %f", motion[j] );
|
||||
|
||||
fputs( "\n", fp );
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
============
|
||||
WriteAnimations
|
||||
============
|
||||
*/
|
||||
static void WriteAnimations( FILE *fp, mstudioseqdesc_t *seqdesc, int blend )
|
||||
{
|
||||
int i;
|
||||
mstudioanim_t *anim;
|
||||
|
||||
fputs( "skeleton\n", fp );
|
||||
|
||||
anim = (mstudioanim_t *)( (byte *)anim_hdr[seqdesc->seqgroup] + seqdesc->animindex );
|
||||
anim += blend * model_hdr->numbones;
|
||||
|
||||
for( i = 0; i < seqdesc->numframes; i++ )
|
||||
WriteFrameInfo( fp, anim, seqdesc, i );
|
||||
|
||||
fputs( "end\n", fp );
|
||||
}
|
||||
|
||||
/*
|
||||
============
|
||||
WriteReferences
|
||||
============
|
||||
*/
|
||||
static void WriteReferences( void )
|
||||
{
|
||||
int i, j;
|
||||
size_t len;
|
||||
FILE *fp;
|
||||
mstudiomodel_t *model;
|
||||
mstudiobodyparts_t *bodypart;
|
||||
char name[64];
|
||||
char filename[MAX_SYSPATH];
|
||||
|
||||
if( !CreateBoneTransformMatrices() )
|
||||
return;
|
||||
|
||||
FillBoneTransformMatrices();
|
||||
|
||||
for( i = 0; i < model_hdr->numbodyparts; i++ )
|
||||
{
|
||||
bodypart = (mstudiobodyparts_t *)( (byte *)model_hdr + model_hdr->bodypartindex ) + i;
|
||||
|
||||
for( j = 0; j < bodypart->nummodels; j++ )
|
||||
{
|
||||
model = (mstudiomodel_t *)( (byte *)model_hdr + bodypart->modelindex ) + j;
|
||||
|
||||
if( !Q_strncmp( model->name, "blank", 5 ) )
|
||||
continue;
|
||||
|
||||
COM_FileBase( model->name, name );
|
||||
|
||||
len = Q_snprintf( filename, MAX_SYSPATH, "%s%s.smd", destdir, name );
|
||||
|
||||
if( len >= MAX_SYSPATH )
|
||||
{
|
||||
fprintf( stderr, "ERROR: Destination path is too long. Can't write %s.smd\n", name );
|
||||
RemoveBoneTransformMatrices();
|
||||
return;
|
||||
}
|
||||
|
||||
fp = fopen( filename, "w" );
|
||||
|
||||
if( !fp )
|
||||
{
|
||||
fprintf( stderr, "ERROR: Can't write %s\n", filename );
|
||||
RemoveBoneTransformMatrices();
|
||||
return;
|
||||
}
|
||||
|
||||
fputs( "version 1\n", fp );
|
||||
|
||||
WriteNodes( fp );
|
||||
WriteSkeleton( fp );
|
||||
WriteTriangles( fp, model );
|
||||
|
||||
fclose( fp );
|
||||
|
||||
printf( "Reference: %s\n", filename );
|
||||
}
|
||||
}
|
||||
|
||||
RemoveBoneTransformMatrices();
|
||||
}
|
||||
|
||||
/*
|
||||
============
|
||||
WriteSequences
|
||||
============
|
||||
*/
|
||||
static void WriteSequences( void )
|
||||
{
|
||||
int i, j;
|
||||
size_t len;
|
||||
FILE *fp;
|
||||
char filename[MAX_SYSPATH];
|
||||
mstudioseqdesc_t *seqdesc;
|
||||
|
||||
for( i = 0; i < model_hdr->numseq; i++ )
|
||||
{
|
||||
seqdesc = (mstudioseqdesc_t *)( (byte *)model_hdr + model_hdr->seqindex ) + i;
|
||||
|
||||
for( j = 0; j < seqdesc->numblends; j++ )
|
||||
{
|
||||
if( seqdesc->numblends == 1 )
|
||||
len = Q_snprintf( filename, MAX_SYSPATH, "%s%s.smd", destdir, seqdesc->label );
|
||||
else
|
||||
len = Q_snprintf( filename, MAX_SYSPATH, "%s%s_blend%i.smd", destdir, seqdesc->label, j + 1 );
|
||||
|
||||
if( len >= MAX_SYSPATH )
|
||||
{
|
||||
fprintf( stderr, "ERROR: Destination path is too long. Can't write %s.smd\n", seqdesc->label );
|
||||
return;
|
||||
}
|
||||
|
||||
fp = fopen( filename, "w" );
|
||||
|
||||
if( !fp )
|
||||
{
|
||||
fprintf( stderr, "ERROR: Can't write %s\n", filename );
|
||||
return;
|
||||
}
|
||||
|
||||
fputs( "version 1\n", fp );
|
||||
|
||||
WriteNodes( fp );
|
||||
WriteAnimations( fp, seqdesc, j );
|
||||
|
||||
fclose( fp );
|
||||
|
||||
printf( "Sequence: %s\n", filename );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void WriteSMD( void )
|
||||
{
|
||||
WriteReferences();
|
||||
WriteSequences();
|
||||
}
|
||||
|
22
utils/mdldec/smd.h
Normal file
22
utils/mdldec/smd.h
Normal file
@ -0,0 +1,22 @@
|
||||
/*
|
||||
smd.h - Studio Model Data format writer
|
||||
Copyright (C) 2020 Andrey Akhmichin
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
*/
|
||||
#pragma once
|
||||
#ifndef SMD_H
|
||||
#define SMD_H
|
||||
|
||||
void WriteSMD( void );
|
||||
|
||||
#endif // SMD_H
|
||||
|
127
utils/mdldec/texture.c
Normal file
127
utils/mdldec/texture.c
Normal file
@ -0,0 +1,127 @@
|
||||
/*
|
||||
texture.c - texture writer
|
||||
Copyright (C) 2020 Andrey Akhmichin
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include "const.h"
|
||||
#include "crtlib.h"
|
||||
#include "studio.h"
|
||||
#include "img_bmp.h"
|
||||
#include "mdldec.h"
|
||||
#include "texture.h"
|
||||
|
||||
/*
|
||||
============
|
||||
WriteBMP
|
||||
============
|
||||
*/
|
||||
static void WriteBMP( mstudiotexture_t *texture )
|
||||
{
|
||||
int i;
|
||||
FILE *fp;
|
||||
const byte *p;
|
||||
byte *palette, *pic, *buf;
|
||||
char filename[MAX_SYSPATH], texturename[64];
|
||||
rgba_t rgba_palette[256];
|
||||
bmp_t bmp_hdr = {0,};
|
||||
size_t texture_size, len;
|
||||
|
||||
COM_FileBase( texture->name, texturename );
|
||||
len = Q_snprintf( filename, MAX_SYSPATH, "%s%s.bmp", destdir, texturename );
|
||||
|
||||
if( len >= MAX_SYSPATH )
|
||||
{
|
||||
fprintf( stderr, "ERROR: Destination path is too long. Can't write %s.bmp\n", texturename );
|
||||
return;
|
||||
}
|
||||
|
||||
fp = fopen( filename, "wb" );
|
||||
|
||||
if( !fp )
|
||||
{
|
||||
fprintf( stderr, "ERROR: Can't write texture file %s\n", filename );
|
||||
return;
|
||||
}
|
||||
|
||||
texture_size = texture->height * texture->width;
|
||||
pic = (byte *)texture_hdr + texture->index;
|
||||
palette = pic + texture_size;
|
||||
|
||||
bmp_hdr.id[0] = 'B';
|
||||
bmp_hdr.id[1] = 'M';
|
||||
bmp_hdr.width = texture->width;
|
||||
bmp_hdr.height = texture->height;
|
||||
bmp_hdr.planes = 1;
|
||||
bmp_hdr.bitsPerPixel = 8;
|
||||
bmp_hdr.bitmapDataSize = texture_size;
|
||||
bmp_hdr.colors = 256;
|
||||
|
||||
bmp_hdr.fileSize = sizeof( bmp_hdr ) + texture_size + sizeof( rgba_palette );
|
||||
bmp_hdr.bitmapDataOffset = sizeof( bmp_hdr ) + sizeof( rgba_palette );
|
||||
bmp_hdr.bitmapHeaderSize = BI_SIZE;
|
||||
|
||||
fwrite( &bmp_hdr, sizeof( bmp_hdr ), 1, fp );
|
||||
|
||||
p = palette;
|
||||
|
||||
for( i = 0; i < (int)bmp_hdr.colors; i++ )
|
||||
{
|
||||
rgba_palette[i][2] = *p++;
|
||||
rgba_palette[i][1] = *p++;
|
||||
rgba_palette[i][0] = *p++;
|
||||
rgba_palette[i][3] = 0;
|
||||
}
|
||||
|
||||
fwrite( rgba_palette, sizeof( rgba_palette ), 1, fp );
|
||||
|
||||
buf = malloc( texture_size );
|
||||
|
||||
p = pic;
|
||||
p += ( bmp_hdr.height - 1 ) * bmp_hdr.width;
|
||||
|
||||
for( i = 0; i < bmp_hdr.height; i++ )
|
||||
{
|
||||
memcpy( buf + bmp_hdr.width * i, p, bmp_hdr.width );
|
||||
p -= bmp_hdr.width;
|
||||
}
|
||||
|
||||
fwrite( buf, texture_size, 1, fp );
|
||||
|
||||
fclose( fp );
|
||||
|
||||
free( buf );
|
||||
|
||||
printf( "Texture: %s\n", filename );
|
||||
}
|
||||
|
||||
/*
|
||||
============
|
||||
WriteTextures
|
||||
============
|
||||
*/
|
||||
void WriteTextures( void )
|
||||
{
|
||||
int i;
|
||||
mstudiotexture_t *texture;
|
||||
|
||||
for( i = 0; i < texture_hdr->numtextures; i++ )
|
||||
{
|
||||
texture = (mstudiotexture_t *)( (byte *)texture_hdr + texture_hdr->textureindex ) + i;
|
||||
|
||||
WriteBMP( texture );
|
||||
}
|
||||
}
|
||||
|
22
utils/mdldec/texture.h
Normal file
22
utils/mdldec/texture.h
Normal file
@ -0,0 +1,22 @@
|
||||
/*
|
||||
texture.h - texture writer
|
||||
Copyright (C) 2020 Andrey Akhmichin
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
*/
|
||||
#pragma once
|
||||
#ifndef TEXTURE_H
|
||||
#define TEXTURE_H
|
||||
|
||||
void WriteTextures( void );
|
||||
|
||||
#endif // TEXTURE_H
|
||||
|
86
utils/mdldec/utils.c
Normal file
86
utils/mdldec/utils.c
Normal file
@ -0,0 +1,86 @@
|
||||
/*
|
||||
utils.c - Useful helper functions
|
||||
Copyright (C) 2020 Andrey Akhmichin
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/stat.h>
|
||||
#include "xash3d_types.h"
|
||||
#include "crtlib.h"
|
||||
#include "utils.h"
|
||||
|
||||
/*
|
||||
============
|
||||
IsFileExists
|
||||
============
|
||||
*/
|
||||
qboolean IsFileExists( const char *filename )
|
||||
{
|
||||
struct stat st;
|
||||
int ret;
|
||||
|
||||
ret = stat( filename, &st );
|
||||
|
||||
if( ret == -1 )
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
============
|
||||
GetFileSize
|
||||
============
|
||||
*/
|
||||
off_t GetFileSize( FILE *fp )
|
||||
{
|
||||
struct stat st;
|
||||
int fd;
|
||||
|
||||
fd = fileno( fp );
|
||||
fstat( fd, &st );
|
||||
|
||||
return st.st_size;
|
||||
}
|
||||
|
||||
/*
|
||||
============
|
||||
LoadFile
|
||||
============
|
||||
*/
|
||||
byte *LoadFile( const char *filename )
|
||||
{
|
||||
FILE *fp;
|
||||
byte *buf;
|
||||
off_t size;
|
||||
|
||||
fp = fopen( filename, "rb" );
|
||||
|
||||
if( !fp )
|
||||
return NULL;
|
||||
|
||||
size = GetFileSize( fp );
|
||||
|
||||
buf = malloc( size );
|
||||
|
||||
if( !buf )
|
||||
return NULL;
|
||||
|
||||
fread( buf, size, 1, fp );
|
||||
fclose( fp );
|
||||
|
||||
return buf;
|
||||
}
|
||||
|
24
utils/mdldec/utils.h
Normal file
24
utils/mdldec/utils.h
Normal file
@ -0,0 +1,24 @@
|
||||
/*
|
||||
utils.h - Useful helper functions
|
||||
Copyright (C) 2020 Andrey Akhmichin
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
*/
|
||||
#pragma once
|
||||
#ifndef UTILS_H
|
||||
#define UTILS_H
|
||||
|
||||
qboolean IsFileExists( const char *filename );
|
||||
off_t GetFileSize( FILE *fp );
|
||||
byte *LoadFile( const char *filename );
|
||||
|
||||
#endif // UTILS_H
|
||||
|
8
utils/mdldec/version.h
Normal file
8
utils/mdldec/version.h
Normal file
@ -0,0 +1,8 @@
|
||||
#pragma once
|
||||
#ifndef VERSION_H
|
||||
#define VERSION_H
|
||||
|
||||
#define APP_VERSION "1.1"
|
||||
|
||||
#endif // VERSION_H
|
||||
|
Loading…
Reference in New Issue
Block a user