Add support of smacker files

https://sourceforge.net/projects/libsmacker/files/libsmacker-1.2/

libsmacker is released under the Lesser GNU Public License, v2.1.
This commit is contained in:
Denis Pauk 2023-09-17 11:54:57 +03:00
parent 85ebca9979
commit c898573885
6 changed files with 2106 additions and 13 deletions

View file

@ -853,6 +853,7 @@ CLIENT_OBJS_ := \
src/client/curl/download.o \
src/client/curl/qcurl.o \
src/client/input/sdl.o \
src/client/cinema/smacker.o \
src/client/menu/menu.o \
src/client/menu/qmenu.o \
src/client/menu/videomenu.o \

1797
src/client/cinema/smacker.c Normal file

File diff suppressed because it is too large Load diff

103
src/client/cinema/smacker.h Normal file
View file

@ -0,0 +1,103 @@
/**
libsmacker - A C library for decoding .smk Smacker Video files
Copyright (C) 2012-2020 Greg Kennedy
libsmacker is a cross-platform C library which can be used for
decoding Smacker Video files produced by RAD Game Tools.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 2.1 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 Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef SMACKER_H
#define SMACKER_H
/* includes - needed for FILE* here */
#include <stdio.h>
/** forward-declaration for an struct */
typedef struct smk_t * smk;
/** a few defines as return codes from smk_next() */
#define SMK_DONE 0x00
#define SMK_MORE 0x01
#define SMK_LAST 0x02
#define SMK_ERROR -1
/** file-processing mode, pass to smk_open_file */
#define SMK_MODE_DISK 0x00
#define SMK_MODE_MEMORY 0x01
/** Y-scale meanings */
#define SMK_FLAG_Y_NONE 0x00
#define SMK_FLAG_Y_INTERLACE 0x01
#define SMK_FLAG_Y_DOUBLE 0x02
/** track mask and enable bits */
#define SMK_AUDIO_TRACK_0 0x01
#define SMK_AUDIO_TRACK_1 0x02
#define SMK_AUDIO_TRACK_2 0x04
#define SMK_AUDIO_TRACK_3 0x08
#define SMK_AUDIO_TRACK_4 0x10
#define SMK_AUDIO_TRACK_5 0x20
#define SMK_AUDIO_TRACK_6 0x40
#define SMK_VIDEO_TRACK 0x80
/* PUBLIC FUNCTIONS */
#ifdef __cplusplus
extern "C" {
#endif
/* OPEN OPERATIONS */
/** open an smk (from a file) */
smk smk_open_file(const char * filename, unsigned char mode);
/** open an smk (from a file pointer) */
smk smk_open_filepointer(FILE * file, unsigned char mode);
/** read an smk (from a memory buffer) */
smk smk_open_memory(const unsigned char * buffer, unsigned long size);
/* CLOSE OPERATIONS */
/** close out an smk file and clean up memory */
void smk_close(smk object);
/* GET FILE INFO OPERATIONS */
char smk_info_all(const smk object, unsigned long * frame, unsigned long * frame_count, double * usf);
char smk_info_video(const smk object, unsigned long * w, unsigned long * h, unsigned char * y_scale_mode);
char smk_info_audio(const smk object, unsigned char * track_mask, unsigned char channels[7], unsigned char bitdepth[7], unsigned long audio_rate[7]);
/* ENABLE/DISABLE Switches */
char smk_enable_all(smk object, unsigned char mask);
char smk_enable_video(smk object, unsigned char enable);
char smk_enable_audio(smk object, unsigned char track, unsigned char enable);
/** Retrieve palette */
const unsigned char * smk_get_palette(const smk object);
/** Retrieve video frame, as a buffer of size w*h */
const unsigned char * smk_get_video(const smk object);
/** Retrieve decoded audio chunk, track N */
const unsigned char * smk_get_audio(const smk object, unsigned char track);
/** Get size of currently pointed decoded audio chunk, track N */
unsigned long smk_get_audio_size(const smk object, unsigned char track);
/** rewind to first frame and unpack */
char smk_first(smk object);
/** advance to next frame and unpack */
char smk_next(smk object);
/** seek to first keyframe before/at N in an smk */
char smk_seek_keyframe(smk object, unsigned long frame);
#ifdef __cplusplus
}
#endif
#endif

View file

@ -0,0 +1,56 @@
/**
libsmacker - A C library for decoding .smk Smacker Video files
Copyright (C) 2012-2017 Greg Kennedy
See smacker.h for more information.
smk_malloc.h
"Safe" implementations of malloc and free.
Verbose implementation of assert.
*/
#ifndef SMK_MALLOC_H
#define SMK_MALLOC_H
/* assert */
#include <assert.h>
/* calloc */
#include <stdlib.h>
/* fprintf */
#include <stdio.h>
/* Error messages from calloc */
#include <errno.h>
#include <string.h>
/**
Safe free: attempts to prevent double-free by setting pointer to NULL.
Optionally warns on attempts to free a NULL pointer.
*/
#define smk_free(p) \
{ \
assert (p); \
free(p); \
p = NULL; \
}
/**
Safe malloc: exits if calloc() returns NULL.
Also initializes blocks to 0.
Optionally warns on attempts to malloc over an existing pointer.
Ideally, one should not exit() in a library. However, if you cannot
calloc(), you probably have bigger problems.
*/
#define smk_malloc(p, x) \
{ \
assert (p == NULL); \
p = calloc(1, x); \
if (!p) \
{ \
fprintf(stderr, "libsmacker::smk_malloc(" #p ", %lu) - ERROR: calloc() returned NULL (file: %s, line: %lu)\n\tReason: [%d] %s\n", \
(unsigned long) (x), __FILE__, (unsigned long)__LINE__, errno, strerror(errno)); \
exit(EXIT_FAILURE); \
} \
}
#endif

View file

@ -29,6 +29,7 @@
#include "header/client.h"
#include "input/header/input.h"
#include "cinema/smacker.h"
// don't need HDR stuff
#define STBI_NO_LINEAR
@ -55,6 +56,12 @@ typedef struct
int count;
} cblock_t;
typedef enum
{
video_cin,
video_smk,
} cinema_t;
typedef struct
{
qboolean restart_sound;
@ -64,10 +71,13 @@ typedef struct
int width;
int height;
float fps;
int color_bits;
cinema_t video_type;
byte *pic;
byte *pic_pending;
/* cin video */
/* order 1 huffman stuff */
int *hnodes1;
@ -76,11 +86,17 @@ typedef struct
int h_used[512];
int h_count[512];
/* shared video buffer */
void *raw_video;
/* smacker video */
smk smk_video;
} cinematics_t;
cinematics_t cin;
void
static void
SCR_LoadPCX(char *filename, byte **pic, byte **palette, int *width, int *height)
{
byte *raw;
@ -184,6 +200,18 @@ SCR_StopCinematic(void)
{
cl.cinematictime = 0; /* done */
if (cin.smk_video)
{
smk_close(cin.smk_video);
cin.smk_video = NULL;
}
if (cin.raw_video)
{
FS_FreeFile(cin.raw_video);
cin.raw_video = NULL;
}
if (cin.pic)
{
Z_Free(cin.pic);
@ -230,7 +258,7 @@ SCR_FinishCinematic(void)
SZ_Print(&cls.netchan.message, va("nextserver %i\n", cl.servercount));
}
int
static int
SmallestNode1(int numhnodes)
{
int i;
@ -270,7 +298,7 @@ SmallestNode1(int numhnodes)
/*
* Reads the 64k counts table and initializes the node trees
*/
void
static void
Huff1TableInit(void)
{
int prev;
@ -327,7 +355,7 @@ Huff1TableInit(void)
}
}
cblock_t
static cblock_t
Huff1Decompress(cblock_t in)
{
byte *input;
@ -385,7 +413,40 @@ Huff1Decompress(cblock_t in)
return out;
}
byte *
static byte *
SCR_ReadNextSMKFrame(void)
{
size_t count;
byte *buffer = Z_Malloc(cin.height * cin.width);
/* audio */
count = smk_get_audio_size(cin.smk_video, 0);
if (count && cin.s_channels)
{
count /= (cin.s_width * cin.s_channels);
S_RawSamples(count, cin.s_rate, cin.s_width, cin.s_channels,
smk_get_audio(cin.smk_video, 0), Cvar_VariableValue("s_volume"));
}
/* update palette */
memcpy(cl.cinematicpalette, smk_get_palette(cin.smk_video), sizeof(cl.cinematicpalette));
cl.cinematicpalette_active = 0;
/* get pic */
memcpy(buffer, smk_get_video(cin.smk_video), cin.height * cin.width);
cl.cinematicframe++;
if (smk_next(cin.smk_video) != SMK_MORE)
{
Z_Free(buffer);
return NULL;
}
return buffer;
}
static byte *
SCR_ReadNextFrame(void)
{
int r;
@ -442,8 +503,8 @@ SCR_ReadNextFrame(void)
FS_Read(compressed, size, cl.cinematic_file);
/* read sound */
start = cl.cinematicframe * cin.s_rate / 14;
end = (cl.cinematicframe + 1) * cin.s_rate / 14;
start = cl.cinematicframe * cin.s_rate / cin.fps;
end = (cl.cinematicframe + 1) * cin.s_rate / cin.fps;
count = end - start;
FS_Read(samples, count * cin.s_width * cin.s_channels,
@ -491,11 +552,11 @@ SCR_RunCinematic(void)
if (cls.key_dest != key_game)
{
/* pause if menu or console is up */
cl.cinematictime = cls.realtime - cl.cinematicframe * 1000 / 14;
cl.cinematictime = cls.realtime - cl.cinematicframe * 1000 / cin.fps;
return;
}
frame = (cls.realtime - cl.cinematictime) * 14.0 / 1000;
frame = (cls.realtime - cl.cinematictime) * cin.fps / 1000;
if (frame <= cl.cinematicframe)
{
@ -505,7 +566,7 @@ SCR_RunCinematic(void)
if (frame > cl.cinematicframe + 1)
{
Com_Printf("Dropped frame: %i > %i\n", frame, cl.cinematicframe + 1);
cl.cinematictime = cls.realtime - cl.cinematicframe * 1000 / 14;
cl.cinematictime = cls.realtime - cl.cinematicframe * 1000 / cin.fps;
}
if (cin.pic)
@ -515,7 +576,15 @@ SCR_RunCinematic(void)
cin.pic = cin.pic_pending;
cin.pic_pending = NULL;
cin.pic_pending = SCR_ReadNextFrame();
switch (cin.video_type)
{
case video_cin:
cin.pic_pending = SCR_ReadNextFrame();
break;
case video_smk:
cin.pic_pending = SCR_ReadNextSMKFrame();
break;
}
if (!cin.pic_pending)
{
@ -647,7 +716,7 @@ SCR_DrawCinematic(void)
return true;
}
byte *
static byte *
SCR_LoadHiColor(const char* namewe, const char *ext, int *width, int *height)
{
char filename[256];
@ -754,6 +823,69 @@ SCR_PlayCinematic(char *arg)
return;
}
if (dot && !strcmp(dot, ".smk"))
{
unsigned char trackmask, channels[7], depth[7];
unsigned long width, height;
unsigned long rate[7];
double usf; /* microseconds per frame */
size_t len;
Com_sprintf(name, sizeof(name), "video/%s", arg);
len = FS_LoadFile(name, &cin.raw_video);
if (!cin.raw_video || len <=0)
{
cl.cinematictime = 0; /* done */
return;
}
cin.smk_video = smk_open_memory(cin.raw_video, len);
if (!cin.smk_video)
{
FS_FreeFile(cin.raw_video);
cin.raw_video = NULL;
cl.cinematictime = 0; /* done */
return;
}
SCR_EndLoadingPlaque();
cin.color_bits = 8;
cls.state = ca_active;
smk_info_audio(cin.smk_video, &trackmask, channels, depth, rate);
if (trackmask != SMK_AUDIO_TRACK_0)
{
Com_Printf("%s has different track mask %d.\n", name, trackmask);
cin.s_channels = 0;
}
else
{
cin.s_rate = rate[0];
cin.s_width = depth[0] / 8;
cin.s_channels = channels[0];
smk_enable_audio(cin.smk_video, 0, true);
}
smk_info_all(cin.smk_video, NULL, NULL, &usf);
smk_info_video(cin.smk_video, &width, &height, NULL);
smk_enable_video(cin.smk_video, true);
cin.width = width;
cin.height = height;
cin.fps = 1000000.0f / usf;
/* process first frame */
smk_first(cin.smk_video);
cl.cinematicframe = 0;
cin.pic = SCR_ReadNextSMKFrame();
cl.cinematictime = Sys_Milliseconds();
cin.video_type = video_smk;
return;
}
Com_sprintf(name, sizeof(name), "video/%s", arg);
FS_FOpenFile(name, &cl.cinematic_file, false);
@ -773,6 +905,7 @@ SCR_PlayCinematic(char *arg)
FS_Read(&height, 4, cl.cinematic_file);
cin.width = LittleLong(width);
cin.height = LittleLong(height);
cin.fps = 14.0f;
FS_Read(&cin.s_rate, 4, cl.cinematic_file);
cin.s_rate = LittleLong(cin.s_rate);
@ -786,5 +919,7 @@ SCR_PlayCinematic(char *arg)
cl.cinematicframe = 0;
cin.pic = SCR_ReadNextFrame();
cl.cinematictime = Sys_Milliseconds();
cin.video_type = video_cin;
}

View file

@ -544,7 +544,8 @@ SV_Map(qboolean attractloop, char *levelstring, qboolean loadgame, qboolean isau
--l;
}
if ((l > 4) && !strcmp(level + l - 4, ".cin"))
if ((l > 4) && (!strcmp(level + l - 4, ".cin") ||
!strcmp(level + l - 4, ".smk")))
{
#ifndef DEDICATED_ONLY
SCR_BeginLoadingPlaque(); /* for local system */