SmackerLib API (#303)

* adds better smack support

* more docs
This commit is contained in:
Dethrace Engineering Department 2023-04-19 13:11:56 +12:00 committed by GitHub
parent ef9e4d668d
commit f3515d5f64
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 237 additions and 119 deletions

View File

@ -67,6 +67,7 @@ option(DETHRACE_FIX_BUGS "Fix Dethrace bugs" ON)
add_subdirectory(src/harness)
add_subdirectory(src/S3)
add_subdirectory(src/smackw32)
add_subdirectory(src/BRSRC13)
add_subdirectory(src/DETHRACE)

View File

@ -3,11 +3,12 @@
### DETHRACE
Game logic. According to the symbol dump, these files were originally stored in `C:\DETHRACE\src`.
- `DETHRACE/common` - all common logic
- `DETHRACE/pc-dos` - all platform-specific functions (DOS, in this case)
- `DETHRACE/common` - common game logic
- `DETHRACE/pc-dos` - DOS-specific functions
- `DETHRACE/win95sys.c` - Windows-specific functions
- `DETHRACE/pd` - platform-dependent generic headers.
_All code here is kept as similar to how we think the original code might have been. Any changes required are implemented as hooks into `harness`._
_All code here is kept as similar to how we think the original code might have looked. Any changes required are implemented as hooks into `harness`._
### BRSRC13
@ -15,15 +16,19 @@ Graphics rendering library. [BRender](https://en.wikipedia.org/wiki/Argonaut_Gam
- Stainless Software used their own build of BRender with unknown modifications.
_All code here is kept as similar to how we think the original code might have been. Any changes required are implemented as hooks into `harness`._
_All code here is kept as similar to how we think the original code might have looked. Any changes required are implemented as hooks into `harness`._
### S3
Audio library. No other information.
Audio library. Possibly short for "Stainless Sound System"?! Supports at least two audio backends - [SOS](http://web.archive.org/web/19990221132448/http://www.humanmachine.com/sos.html) (DOS) and DirectSound.
_All code here is kept as similar to how we think the original code might have been. Any changes required are implemented as hooks into `harness`._
_All code here is kept as similar to how we think the original code might have looked, with the addition of a small amount of code integrating [miniaudio](https://miniaud.io)
### smackw32
Implements the [RAD Smacker lib](https://wiki.multimedia.cx/index.php/RAD_Game_Tools_Smacker_API) interface. The implementation is backed by [libsmacker](https://libsmacker.sourceforge.net/).
### harness
- Provides functions that the original game logic calls to implement modern cross-platform support.
- SDL2 for windowing + input + networking, OpenGL for rendering, OpenAL for audio
- SDL2, OpenGL, miniaudio

View File

@ -6,21 +6,21 @@ if(NOT DEFINED CMAKE_BUILD_TYPE)
set(CMAKE_BUILD_TYPE "Debug" CACHE STRING "Choose the type of build, options are: Debug Release RelWithDebInfo" FORCE)
endif()
project(smacker C)
project(libsmacker C)
add_library(smacker STATIC)
add_library(libsmacker STATIC)
target_include_directories(smacker PUBLIC
target_include_directories(libsmacker PUBLIC
.
)
if(NOT MSVC)
target_compile_options(smacker PRIVATE -Wall)
target_compile_options(libsmacker PRIVATE -Wall)
else()
target_compile_definitions(smacker PRIVATE -D_CRT_SECURE_NO_WARNINGS)
target_compile_definitions(libsmacker PRIVATE -D_CRT_SECURE_NO_WARNINGS)
endif()
target_sources(smacker PRIVATE
target_sources(libsmacker PRIVATE
smacker.c
smk_bitstream.c
smk_hufftree.c

View File

@ -10,7 +10,7 @@ target_include_directories(dethrace_obj
pd
)
target_link_libraries(dethrace_obj PUBLIC SDL2::SDL2 smacker harness brender s3)
target_link_libraries(dethrace_obj PUBLIC SDL2::SDL2 smackw32 harness brender s3)
if (CMAKE_C_COMPILER_ID MATCHES "MSVC")

View File

@ -12,7 +12,7 @@
#include "input.h"
#include "loading.h"
#include "pd/sys.h"
#include "smacker.h"
#include "smackw32/smackw32.h"
#include "sound.h"
#include "utility.h"
#include <stdlib.h>
@ -60,32 +60,24 @@ void PlaySmackerFile(char* pSmack_name) {
tPath_name the_path;
br_colour* br_colours_ptr;
tU8* smack_colours_ptr;
// Smack* smk;
Smack* smk;
int i;
int j;
int len;
int fuck_off;
LOG_TRACE("(\"%s\")", pSmack_name);
smk s;
br_uint_8* dest_pix = (br_uint_8*)gBack_screen->pixels;
unsigned long w, h, f;
unsigned char r, g, b;
double usf;
tU32 last_frame_time;
if (!gSound_override && !gCut_scene_override) {
StopMusic();
FadePaletteDown();
ClearEntireScreen();
SmackSoundUseDirectSound(NULL);
br_colours_ptr = gCurrent_palette->pixels;
PathCat(the_path, gApplication_path, "CUTSCENE");
PathCat(the_path, the_path, pSmack_name);
dr_dprintf("Trying to open smack file '%s'", the_path);
s = smk_open_file(the_path, SMK_MODE_MEMORY);
if (s == NULL) {
smk = SmackOpen(the_path, SMACKTRACKS, SMACKAUTOEXTRA);
if (smk == NULL) {
dr_dprintf("Unable to open smack file - attempt to load smack from CD...");
if (GetCDPathFromPathsTxtFile(the_path)) {
strcat(the_path, gDir_separator);
@ -93,61 +85,50 @@ void PlaySmackerFile(char* pSmack_name) {
PathCat(the_path, the_path, "CUTSCENE");
PathCat(the_path, the_path, pSmack_name);
if (PDCheckDriveExists(the_path)) {
s = smk_open_file(the_path, SMK_MODE_MEMORY);
smk = SmackOpen(the_path, SMACKTRACKS, SMACKAUTOEXTRA);
}
} else {
dr_dprintf("Can't get CD directory name");
}
}
if (s != NULL) {
if (smk != NULL) {
dr_dprintf("Smack file opened OK");
smk_info_all(s, NULL, &f, &usf);
smk_info_video(s, &w, &h, NULL);
double fps = 1000000.0 / usf;
int delay_ms = (1 / fps) * 1000;
for (i = 1; i <= smk->Frames; i++) {
SmackToBuffer(smk, 0, 0, gBack_screen->row_bytes, gBack_screen->height, gBack_screen->pixels, 0);
smk_enable_video(s, 1);
smk_first(s);
do {
const unsigned char* pal = smk_get_palette(s);
for (i = 0; i < 256; i++) {
r = pal[(i * 3)];
g = pal[(i * 3) + 1];
b = pal[(i * 3) + 2];
br_colours_ptr[i] = b | (g << 8) | (r << 16);
if (smk->NewPalette) {
smack_colours_ptr = smk->Palette;
for (j = 0; j < 256; j++) {
br_colours_ptr[j] = (smack_colours_ptr[j * 3] << 16) | smack_colours_ptr[j * 3 + 2] | (smack_colours_ptr[j * 3 + 1] << 8);
}
// TOOD: remove the commented-out line below when smk->NewPalette is set correctly per-frame
// memset(gBack_screen->pixels, 0, gBack_screen->row_bytes * gBack_screen->height);
DRSetPalette(gCurrent_palette);
PDScreenBufferSwap(0);
EnsurePaletteUp();
const unsigned char* frame = smk_get_video(s);
for (i = 0; i < h; i++) {
for (j = 0; j < w; j++) {
dest_pix[(i * gBack_screen->row_bytes) + j] = frame[i * w + j];
}
SmackDoFrame(smk);
if (i != smk->Frames) {
SmackNextFrame(smk);
}
PDScreenBufferSwap(0);
last_frame_time = PDGetTotalTime();
do {
fuck_off = AnyKeyDown() || EitherMouseButtonDown();
// added by dethrace to avoid 100% cpu
gHarness_platform.Sleep(1);
} while (!fuck_off && PDGetTotalTime() - last_frame_time < delay_ms);
} while (!fuck_off && SmackWait(smk));
if (fuck_off) {
break;
}
} while (smk_next(s) == SMK_MORE);
smk_close(s);
}
FadePaletteDown();
ClearEntireScreen();
StartMusic();
SmackClose(smk);
} else {
dr_dprintf("Smack file '%s' failed to open", pSmack_name);
StartMusic();
}
StartMusic();
}
}

View File

@ -2942,62 +2942,6 @@ typedef struct _tag_sos_timer_system {
W32 wMIDIActiveSongHandle;
} _SOS_TIMER_SYSTEM;
typedef struct SmackTag {
unsigned long Version;
unsigned long Width;
unsigned long Height;
unsigned long Frames;
unsigned long MSPerFrame;
unsigned long SmackerType;
unsigned long LargestInTrack[7];
unsigned long tablesize;
unsigned long codesize;
unsigned long absize;
unsigned long detailsize;
unsigned long typesize;
unsigned long TrackType[7];
unsigned long extra;
unsigned long NewPalette;
unsigned char Palette[772];
unsigned long PalType;
unsigned long FrameNum;
unsigned long FrameSize;
unsigned long SndSize;
unsigned long LastRectx;
unsigned long LastRecty;
unsigned long LastRectw;
unsigned long LastRecth;
unsigned long OpenFlags;
unsigned long LeftOfs;
unsigned long TopOfs;
unsigned long ReadError;
unsigned long addr32;
} Smack;
typedef struct SmackSumTag {
unsigned long TotalTime;
unsigned long MS100PerFrame;
unsigned long TotalOpenTime;
unsigned long TotalFrames;
unsigned long SkippedFrames;
unsigned long SoundSkips;
unsigned long TotalBlitTime;
unsigned long TotalReadTime;
unsigned long TotalDecompTime;
unsigned long TotalBackReadTime;
unsigned long TotalReadSpeed;
unsigned long SlowestFrameTime;
unsigned long Slowest2FrameTime;
unsigned long SlowestFrameNum;
unsigned long Slowest2FrameNum;
unsigned long AverageFrameSize;
unsigned long Highest1SecRate;
unsigned long Highest1SecFrame;
unsigned long HighestMemAmount;
unsigned long TotalExtraMemory;
unsigned long HighestExtraUsed;
} SmackSum;
#ifndef _WIN32
typedef struct _heapinfo {
void* _pentry;

View File

@ -20,7 +20,7 @@ if(DETHRACE_FIX_BUGS)
target_compile_definitions(harness PRIVATE DETHRACE_FIX_BUGS)
endif()
target_link_libraries(harness PRIVATE brender compile_with_werror)
target_link_libraries(harness PRIVATE brender s3 compile_with_werror)
if(WIN32)
target_link_libraries(harness PRIVATE dbghelp)
@ -89,7 +89,6 @@ if (IO_PLATFORM STREQUAL "SDL_OpenGL")
resources/3d_frag.glsl.h
)
target_include_directories(harness PRIVATE "${dethrace_SOURCE_DIR}/src/DETHRACE/common")
target_include_directories(harness PRIVATE "${dethrace_SOURCE_DIR}/src/S3/include")
target_link_libraries(harness PRIVATE SDL2::SDL2 glad)
endif()

View File

@ -0,0 +1,24 @@
add_library(smackw32 STATIC)
target_include_directories(smackw32
PUBLIC
include
PRIVATE
${CMAKE_SOURCE_DIR}
)
target_link_libraries(smackw32 PRIVATE harness brender libsmacker compile_with_werror)
if(NOT MSVC)
else()
target_compile_definitions(smackw32 PRIVATE -D_CRT_SECURE_NO_WARNINGS)
target_compile_options(smackw32 PRIVATE
/wd4101
/wd4996
)
endif()
target_sources(smackw32 PRIVATE
smackw32.c
)

10
src/smackw32/README.md Normal file
View File

@ -0,0 +1,10 @@
# smackw32
Implementation of a minimal form of the Smacker API used by dethrace.
See:
- https://wiki.multimedia.cx/index.php/RAD_Game_Tools_Smacker_API
- https://github.com/OpenSourcedGames/Aliens-vs-Predator/blob/master/source/AvP_vc/3dc/win95/SMACK.H
Backed by http://libsmacker.sourceforge.net

View File

@ -0,0 +1,55 @@
#include <stddef.h>
#include <stdint.h>
#define SMACKTRACK1 0x02000 // Play audio track 1
#define SMACKTRACK2 0x04000 // Play audio track 2
#define SMACKTRACK3 0x08000 // Play audio track 3
#define SMACKTRACK4 0x10000 // Play audio track 4
#define SMACKTRACK5 0x20000 // Play audio track 5
#define SMACKTRACK6 0x40000 // Play audio track 6
#define SMACKTRACK7 0x80000 // Play audio track 7
#define SMACKTRACKS (SMACKTRACK1 | SMACKTRACK2 | SMACKTRACK3 | SMACKTRACK4 | SMACKTRACK5 | SMACKTRACK6 | SMACKTRACK7)
#define SMACKAUTOEXTRA 0xffffffff
typedef struct SmackTag {
unsigned long Version;
unsigned long Width;
unsigned long Height;
unsigned long Frames;
unsigned long MSPerFrame;
unsigned long SmackerType;
unsigned long LargestInTrack[7];
unsigned long tablesize;
unsigned long codesize;
unsigned long absize;
unsigned long detailsize;
unsigned long typesize;
unsigned long TrackType[7];
unsigned long extra;
unsigned long NewPalette;
unsigned char Palette[772];
unsigned long PalType;
unsigned long FrameNum;
unsigned long FrameSize;
unsigned long SndSize;
unsigned long LastRectx;
unsigned long LastRecty;
unsigned long LastRectw;
unsigned long LastRecth;
unsigned long OpenFlags;
unsigned long LeftOfs;
unsigned long TopOfs;
unsigned long ReadError;
unsigned long addr32;
// added by dethrace
void* smk_handle;
} Smack;
Smack* SmackOpen(const char* name, uint32_t flags, uint32_t extrabuf);
int SmackSoundUseDirectSound(void* dd); // NULL mean create instance (apparently)
void SmackToBuffer(Smack* smack, uint32_t left, uint32_t top, uint32_t pitch, uint32_t destheight, void* buf, uint32_t flags);
uint32_t SmackDoFrame(Smack* smack);
void SmackNextFrame(Smack* smack);
uint32_t SmackWait(Smack* smack);
void SmackClose(Smack* smack);

99
src/smackw32/smackw32.c Normal file
View File

@ -0,0 +1,99 @@
#include "include/smackw32/smackw32.h"
#include <assert.h>
#include <stddef.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include "harness/hooks.h"
// lib/libsmacker
#include "smacker.h"
uint32_t smack_last_frame_time;
void copy_palette(Smack* smack) {
const unsigned char* pal = smk_get_palette(smack->smk_handle);
memcpy(smack->Palette, pal, 256 * 3);
}
Smack* SmackOpen(const char* name, uint32_t flags, uint32_t extrabuf) {
double usf;
Smack* smack;
double fps;
smk smk_handle = smk_open_file(name, SMK_MODE_MEMORY);
if (smk_handle == NULL) {
return NULL;
}
smack = malloc(sizeof(Smack));
// libsmacker doesn't tell us whether the palette is new on each frame or not, so just assume it always is new
smack->NewPalette = 1;
// smk_handle is added to hold a pointer to the underlying libsmacker instance
smack->smk_handle = smk_handle;
smk_info_all(smk_handle, NULL, &smack->Frames, &usf);
fps = 1000000.0 / usf;
smack->MSPerFrame = (1 / fps) * 1000;
smk_info_video(smk_handle, &smack->Width, &smack->Height, NULL);
smk_enable_video(smk_handle, 1);
if (smk_first(smk_handle) == SMK_ERROR) {
smk_close(smk_handle);
free(smack);
return NULL;
}
copy_palette(smack);
return smack;
}
int SmackSoundUseDirectSound(void* dd) {
// TODO: do some miniaudio init
return 0;
}
void SmackToBuffer(Smack* smack, uint32_t left, uint32_t top, uint32_t pitch, uint32_t destheight, void* buf, uint32_t flags) {
int i, j;
// minimal implementation
assert(left == 0);
assert(top == 0);
assert(flags == 0);
char* char_buf = buf;
const unsigned char* frame = smk_get_video(smack->smk_handle);
for (i = 0; i < destheight; i++) {
memcpy(&char_buf[(i * pitch)], &frame[i * pitch], pitch);
}
}
uint32_t SmackDoFrame(Smack* smack) {
smack_last_frame_time = gHarness_platform.GetTicks();
// TODO: audio processing
return 0;
}
void SmackNextFrame(Smack* smack) {
smk_next(smack->smk_handle);
copy_palette(smack);
}
uint32_t SmackWait(Smack* smack) {
uint32_t now = gHarness_platform.GetTicks();
if (now < smack_last_frame_time + smack->MSPerFrame) {
gHarness_platform.Sleep(1);
return 1;
}
return 0;
}
void SmackClose(Smack* smack) {
smk_close(smack->smk_handle);
free(smack);
}