From 7f2a4422b26d1c10b9f5ec89f63f307f1e64f407 Mon Sep 17 00:00:00 2001 From: xtremeqg Date: Thu, 14 Nov 2024 02:38:48 +0200 Subject: [PATCH] Play videos using ffmpeg instead of smackw32.dll (#3573) big step on the way to other platforms --- .gitignore | 1 + Makefile | 17 +- src/bflib_fmvids.c | 1512 ------------------------------------------ src/bflib_fmvids.cpp | 1437 +++++++++++++++++++++++++++++++++++++++ src/bflib_fmvids.h | 150 +---- src/config.c | 41 +- src/front_fmvids.c | 5 +- src/thread.hpp | 30 + 8 files changed, 1515 insertions(+), 1678 deletions(-) delete mode 100644 src/bflib_fmvids.c create mode 100644 src/bflib_fmvids.cpp create mode 100644 src/thread.hpp diff --git a/.gitignore b/.gitignore index b2fee1a6fe..6106b310c2 100644 --- a/.gitignore +++ b/.gitignore @@ -31,4 +31,5 @@ res/keeperfx_icon.ico /deps/spng /deps/astronomy /deps/centijson +/deps/ffmpeg /deps/*.tar.gz diff --git a/Makefile b/Makefile index 640faafc5d..23122dcb96 100644 --- a/Makefile +++ b/Makefile @@ -343,12 +343,16 @@ CU_OBJS = \ # include and library directories LINKLIB = -mwindows \ -L"sdl/lib" -lSDL2 -lSDL2_mixer -lSDL2_net -lSDL2_image \ + -L"deps/ffmpeg/libavformat" -lavformat \ + -L"deps/ffmpeg/libavcodec" -lavcodec \ + -L"deps/ffmpeg/libswresample" -lswresample \ + -L"deps/ffmpeg/libavutil" -lavutil \ -L"deps/astronomy" -lastronomy \ -L"deps/enet" -lenet \ -L"deps/spng" -lspng \ -L"deps/centijson" -ljson \ -L"deps/zlib" -lminizip -lz \ - -lwinmm -lmingw32 -limagehlp -lws2_32 -ldbghelp + -lwinmm -lmingw32 -limagehlp -lws2_32 -ldbghelp -lbcrypt INCS = \ -I"deps/zlib/include" \ -I"deps/spng/include" \ @@ -358,6 +362,7 @@ INCS = \ -I"deps/centijson/include" \ -I"deps/centitoml" \ -I"deps/astronomy/include" \ + -I"deps/ffmpeg" \ -I"obj" # To find ver_defs.h CXXINCS = $(INCS) @@ -581,7 +586,7 @@ clean-libexterns: libexterns.mk -$(RM) -rf deps/enet deps/zlib deps/spng deps/astronomy deps/centijson -$(RM) libexterns -deps/enet deps/zlib deps/spng deps/astronomy deps/centijson: +deps/enet deps/zlib deps/spng deps/astronomy deps/centijson deps/ffmpeg: $(MKDIR) $@ src/api.c: deps/centijson/include/json.h @@ -590,6 +595,8 @@ src/custom_sprites.c: deps/zlib/include/zlib.h deps/spng/include/spng.h deps/cen src/moonphase.c: deps/astronomy/include/astronomy.h deps/centitoml/toml_api.c: deps/centijson/include/json.h deps/centitoml/toml_conv.c: deps/centijson/include/json.h +src/bflib_fmvids.cpp: deps/ffmpeg/libavformat/avformat.h +obj/std/bflib_fmvids.o obj/hvlog/bflib_fmvids.o: CXXFLAGS += -Wno-error=deprecated-declarations deps/enet-mingw32.tar.gz: curl -Lso $@ "https://github.com/dkfans/kfx-deps/releases/download/initial/enet-mingw32.tar.gz" @@ -621,6 +628,12 @@ deps/centijson-mingw32.tar.gz: deps/centijson/include/json.h: deps/centijson-mingw32.tar.gz | deps/centijson tar xzmf $< -C deps/centijson +deps/ffmpeg-mingw32.tar.gz: + curl -Lso $@ "https://github.com/dkfans/kfx-deps/releases/download/initial/ffmpeg-mingw32.tar.gz" + +deps/ffmpeg/libavformat/avformat.h: deps/ffmpeg-mingw32.tar.gz | deps/ffmpeg + tar xzmf $< -C deps/ffmpeg + include tool_png2ico.mk include tool_pngpal2raw.mk include tool_png2bestpal.mk diff --git a/src/bflib_fmvids.c b/src/bflib_fmvids.c deleted file mode 100644 index c311193ba1..0000000000 --- a/src/bflib_fmvids.c +++ /dev/null @@ -1,1512 +0,0 @@ -/******************************************************************************/ -// Bullfrog Engine Emulation Library - for use to remake classic games like -// Syndicate Wars, Magic Carpet or Dungeon Keeper. -/******************************************************************************/ -/** @file bflib_fmvids.c - * Full Motion Videos (Smacker,FLIC) decode & play library. - * @par Purpose: - * Routines to create and decode videos. - * @par Comment: - * None. - * @author KeeperFX Team - * @date 27 Nov 2008 - 30 Dec 2008 - * @par Copying and copyrights: - * 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 2 of the License, or - * (at your option) any later version. - */ -/******************************************************************************/ -#include "pre_inc.h" -#include "bflib_fmvids.h" - -#include -#include -#include -#include -#define NOMINMAX -#define WIN32_LEAN_AND_MEAN -#include - -#include "bflib_basics.h" -#include "bflib_memory.h" -#include "bflib_sndlib.h" -#include "bflib_video.h" -#include "bflib_keybrd.h" -#include "bflib_inputctrl.h" -#include "bflib_fileio.h" -#include "post_inc.h" - -#ifdef __cplusplus -extern "C" { -#endif -/******************************************************************************/ -// Constants and defines -#define FLI_PREFIX 0xF100u -#define FLI_COLOR256 4 -#define FLI_SS2 7 -#define FLI_COLOR 11 -#define FLI_LC 12 -#define FLI_BLACK 13 -#define FLI_BRUN 15 -#define FLI_COPY 16 -#define FLI_PSTAMP 18 - -/******************************************************************************/ -// Global variables -static SmackDrawCallback smack_draw_callback = NULL; -static unsigned char smk_palette[768]; -static struct Animation animation; - -/******************************************************************************/ -void copy_to_screen(unsigned char *srcbuf, unsigned long width, unsigned long height, unsigned int flags); -/******************************************************************************/ -// Functions -typedef char (WINAPI *FARPROCP_C)(void *); -typedef unsigned long (WINAPI *FARPROCP_U)(void *); -typedef void (WINAPI *FARPROCP_V)(void *); -typedef void (WINAPI *FARPROCPU_V)(void *,unsigned long); -typedef void (WINAPI *FARPROCU_V)(unsigned long); -typedef struct SmackTag * (WINAPI *FARSMACKOPEN)(const char *,unsigned int,int); -typedef void (WINAPI *FARSMACKSUMMARY)(struct SmackTag *,struct SmackSumTag *); -typedef void (WINAPI *FARSMACKTOBUF)(struct SmackTag *,unsigned long,unsigned long, - unsigned long,unsigned long,const void *,unsigned long); - -char SmackSoundUseMSS(void* dig_driver) -{ - HMODULE hModule; - hModule=LoadLibrary("SMACKW32"); - FARPROC proc; - proc=GetProcAddress(hModule,"_SmackSoundUseMSS@4"); - if (proc==NULL) - { ERRORLOG("Can't get address of SmackSoundUseMSS function; skipped."); return 0; } - return ((FARPROCP_C)(void *)proc)(dig_driver); -} - -struct SmackTag *SmackOpen(const char *name,unsigned int flags,int extrabuf) -{ - HMODULE hModule; - hModule=LoadLibrary("SMACKW32"); - FARPROC proc; - proc=GetProcAddress(hModule,"_SmackOpen@12"); - if (proc==NULL) - { ERRORLOG("Can't get address of SmackOpen function; skipped."); return 0; } - return ((FARSMACKOPEN)(void *)proc)(name,flags,extrabuf); -} - -void SmackSummary(struct SmackTag *smk,struct SmackSumTag *sum) -{ - HMODULE hModule; - hModule=LoadLibrary("SMACKW32"); - FARPROC proc; - proc=GetProcAddress(hModule,"_SmackSummary@8"); - if (proc==NULL) - { ERRORLOG("Can't get address of SmackSummary function; skipped."); return; } - ((FARSMACKSUMMARY)(void *)proc)(smk,sum); -} - -unsigned long SmackWait(struct SmackTag *smk) -{ - HMODULE hModule; - hModule=LoadLibrary("SMACKW32"); - FARPROC proc; - proc=GetProcAddress(hModule,"_SmackWait@4"); - if (proc==NULL) - { ERRORLOG("Can't get address of SmackWait function; skipped."); return 0; } - return ((FARPROCP_U)proc)(smk); -} - -void SmackClose(struct SmackTag *smk) -{ - HMODULE hModule; - hModule=LoadLibrary("SMACKW32"); - FARPROC proc; - proc=GetProcAddress(hModule,"_SmackClose@4"); - if (proc==NULL) - { ERRORLOG("Can't get address of SmackClose function; skipped."); return; } - ((FARPROCP_V)(void *)proc)(smk); -} - -unsigned long SmackDoFrame(struct SmackTag *smk) -{ - HMODULE hModule; - hModule=LoadLibrary("SMACKW32"); - FARPROC proc; - proc=GetProcAddress(hModule,"_SmackDoFrame@4"); - if (proc==NULL) - { ERRORLOG("Can't get address of SmackDoFrame function; skipped."); return 0; } - return ((FARPROCP_U)proc)(smk); -} - -void SmackNextFrame(struct SmackTag *smk) -{ - HMODULE hModule; - hModule=LoadLibrary("SMACKW32"); - FARPROC proc; - proc=GetProcAddress(hModule,"_SmackNextFrame@4"); - if (proc==NULL) - { ERRORLOG("Can't get address of SmackNextFrame function; skipped."); return; } - ((FARPROCP_V)(void *)proc)(smk); -} - -void SmackToBuffer(struct SmackTag *smk,unsigned long left,unsigned long top, - unsigned long Pitch,unsigned long destheight,const void *buf,unsigned long Flags) -{ - HMODULE hModule; - hModule=LoadLibrary("SMACKW32"); - FARPROC proc; - proc=GetProcAddress(hModule,"_SmackToBuffer@28"); - if (proc==NULL) - { ERRORLOG("Can't get address of SmackToBuffer function; skipped."); return; } - ((FARSMACKTOBUF)(void *)proc)(smk,left,top,Pitch,destheight,buf,Flags); -} - -void SmackGoto(struct SmackTag *smk,unsigned long frame) -{ - HMODULE hModule; - hModule=LoadLibrary("SMACKW32"); - FARPROC proc; - proc=GetProcAddress(hModule,"_SmackGoto@8"); - if (proc==NULL) - { ERRORLOG("Can't get address of SmackGoto function; skipped."); return; } - ((FARPROCPU_V)(void *)proc)(smk,frame); -} - -void SmackSimulate(unsigned long sim) -{ - HMODULE hModule; - hModule=LoadLibrary("SMACKW32"); - FARPROC proc; - proc=GetProcAddress(hModule,"_SmackSimulate@4"); - if (proc==NULL) - { ERRORLOG("Can't get address of SmackSimulate function; skipped."); return; } - ((FARPROCU_V)(void *)proc)(sim); -} - -void copy_to_screen_px_ar_scale(unsigned char *src_buf, unsigned char *dst_buf, int src_width, int src_height, int flags) -{ - // Compute scaling ratio -> Output co-ordinates and output size - int scanline = lbDisplay.GraphicsScreenWidth; - int nlines = lbDisplay.GraphicsScreenHeight; - int spw = 0; - int sph = 0; - int dst_width = 0; - int dst_height = 0; - - if ((flags & SMK_FullscreenStretch) != 0 && !((flags & SMK_FullscreenFit) != 0)) // Use full screen resolution and fill the whole canvas by "stretching" - { - dst_width = scanline; - dst_height = nlines; - } - else // Otherwise, calculate the correct output size - { - int in_width = src_width; - int in_height = src_height; - float units_per_px = 0; - float relative_ar_difference = (in_width * 1.0 / in_height * 1.0) / (scanline * 1.0 / nlines * 1.0); // relative aspect ratio difference between the source frame and destination frame - float comparison_ratio = 1; // when keeping aspect ratio, instead of stretching, this is inverted depending on if we want to crop or fit - if (((flags & SMK_FullscreenStretch) != 0) && ((flags & SMK_FullscreenFit) != 0)) // stretch source from 320x200(16:10) to 320x240 (4:3) (i.e. vertical x 1.2) - "preserve *original* aspect ratio mode" - { - if (src_width == 320 && src_height == 200) {// make sure the source is 320x200 - in_height = (int)(in_height * 1.2); - } - } - - if ((flags & SMK_FullscreenCrop) != 0 && !((flags & SMK_FullscreenFit) != 0)) // fill screen (will crop) - { - comparison_ratio = relative_ar_difference; - } - else // fit to full screen, preserve aspect ratio (pillar/letter boxed) - { - comparison_ratio = 1.0 / relative_ar_difference; - } - if (comparison_ratio <= 1.0) // take either the destination width or height, depending on if the destination is wider or narrower than the source (same aspect ratio is treated the same as wider), and also if we want to crop or fit - { - units_per_px = (scanline>nlines?scanline:nlines)/((in_width>in_height?in_width:in_height)/16.0); - } - else - { - units_per_px = (scanline>nlines?nlines:scanline)/((in_width>in_height?in_height:in_width)/16.0); - } - if ((flags & SMK_FullscreenCrop) != 0 && ((flags & SMK_FullscreenFit) != 0)) // Find the highest integer scale possible - { - if ((flags & SMK_FullscreenStretch) != 0) { //4:3 stretch mode (crop off to the nearest 5x/6x scale - if (src_width == 320 && src_height == 200) {// make sure the source is 320x200 - units_per_px = (max(5, (int)(units_per_px / 16.0 / 5.0) * 5) * 16); // make sure the multiple is integer divisible by 5. Use 5x as a minimum, otherwise there will be no video (resolutions smaller than 1600x1200 will have a cropped image from a buffer of that size). - } - } - units_per_px = ((int)(units_per_px / 16.0) * 16); // scale to the nearest integer multiple of the source resolution. - } - // Starting point coords and width for the destination buffer (based on desired aspect ratio) - spw = (int)((scanline - in_width * units_per_px / 16.0) / 2.0); - sph = (int)((nlines - in_height * units_per_px / 16.0) / 2.0); - dst_width = (int)(in_width * units_per_px / 16.0); - dst_height = (int)(in_height * units_per_px / 16.0); - } - - unsigned char* dst; - // Source pixel coords - int sw = 0; - int sh = 0; - // Clearing top of the canvas - for (sh = 0; sh < sph; sh++) - { - dst = dst_buf + (sh)*scanline; - LbMemorySet(dst, 0, scanline); - } - // Clearing bottom of the canvas - // (Note: it must be done before drawing, to make sure we won't overwrite last line) - for (sh=sph+dst_height; sh 0) { - LbMemorySet(dst, 0, dwstart); - } - for (sw=0; sw> 2; - s = dst_shift >> 2; - do - { - i = (*src) & 0xFF; - k = (*src >> 8) & 0xFF; - n = (k << 24) + (k << 16) + (i << 8) + i; - dst[0] = n; - dst[s] = n; - i = (*src >> 16) & 0xFF; - k = (*src >> 24) & 0xFF; - n = (k << 24) + (k << 16) + (i << 8) + i; - dst[1] = n; - dst[s+1] = n; - src++; - dst += 2; - w--; - } - while (w > 0); -} - -void copy_to_screen_pxdblh(unsigned char *srcbuf, unsigned char *dstbuf, long width, long dst_shift) -{ - unsigned long n; - unsigned long w; - unsigned long s; - unsigned long *dst; - unsigned long *src; - src = (unsigned long *)srcbuf; - dst = (unsigned long *)dstbuf; - w = ((unsigned long)width) >> 2; - s = dst_shift >> 2; - do - { - n = *src; - dst[0] = n; - dst[s] = n; - src++; - dst++; - w--; - } - while (w > 0); -} - -void copy_to_screen_pxdblw(unsigned char *srcbuf, unsigned char *dstbuf, long width) -{ - unsigned long i; - unsigned long k; - unsigned long n; - unsigned long w; - unsigned long *dst; - unsigned long *src; - src = (unsigned long *)srcbuf; - dst = (unsigned long *)dstbuf; - w = ((unsigned long)width) >> 2; - do - { - i = (*src) & 0xFF; - k = (*src >> 8) & 0xFF; - n = (k << 24) + (k << 16) + (i << 8) + i; - dst[0] = n; - i = (*src >> 16) & 0xFF; - k = (*src >> 24) & 0xFF; - n = (k << 24) + (k << 16) + (i << 8) + i; - dst[1] = n; - src++; - dst += 2; - w--; - } - while (w > 0); -} - -void copy_to_screen(unsigned char *srcbuf, unsigned long width, unsigned long height, unsigned int flags) -{ - unsigned char *dstbuf; - long buf_center; - long w; - long h; - if ( ((flags & SMK_PixelDoubleLine) != 0) || ((flags & SMK_InterlaceLine) != 0) ) - { - buf_center = lbDisplay.GraphicsScreenWidth * ((LbScreenHeight() - 2 * height) >> 1); - } else - { - buf_center = lbDisplay.GraphicsScreenWidth * ((LbScreenHeight() - height) >> 1); - } - w = width; - if ((flags & SMK_PixelDoubleWidth) != 0) - w = 2 * width; - dstbuf = &lbDisplay.WScreen[buf_center + ((LbScreenWidth() - w) >> 1)]; - if ((flags & SMK_PixelDoubleLine) != 0) - { - if ((flags & SMK_PixelDoubleWidth) != 0) - { - for (h=height; h > 0; h--) - { - copy_to_screen_pxquad(srcbuf, dstbuf, width, lbDisplay.GraphicsScreenWidth); - dstbuf += 2 * lbDisplay.GraphicsScreenWidth; - srcbuf += width; - } - } else - { - for (h=height; h > 0; h--) - { - copy_to_screen_pxdblh(srcbuf, dstbuf, width, lbDisplay.GraphicsScreenWidth); - dstbuf += 2 * lbDisplay.GraphicsScreenWidth; - srcbuf += width; - } - } - } else - { - if ((flags & SMK_PixelDoubleWidth) != 0) - { - if ((flags & SMK_InterlaceLine) != 0) - { - for (h=height; h > 0; h--) - { - copy_to_screen_pxdblw(srcbuf, dstbuf, width); - dstbuf += 2 * lbDisplay.GraphicsScreenWidth; - srcbuf += width; - } - } else - { - for (h=height; h > 0; h--) - { - copy_to_screen_pxdblw(srcbuf, dstbuf, width); - dstbuf += lbDisplay.GraphicsScreenWidth; - srcbuf += width; - } - } - } - else - { - if ((flags & SMK_InterlaceLine) != 0) - { - for (h=height; h > 0; h--) - { - memcpy(dstbuf, srcbuf, width); - dstbuf += 2 * lbDisplay.GraphicsScreenWidth; - srcbuf += width; - } - } else - { - for (h=height; h > 0; h--) - { - memcpy(dstbuf, srcbuf, width); - dstbuf += lbDisplay.GraphicsScreenWidth; - srcbuf += width; - } - } - } - } - if (smack_draw_callback != NULL) { - smack_draw_callback(lbDisplay.WScreen, lbDisplay.GraphicsScreenWidth, lbDisplay.GraphicsScreenHeight); - } -} - -void copy_to_screen_scaled(unsigned char *srcbuf, unsigned long width, unsigned long height, unsigned int flags) -{ - unsigned char *dstbuf; - dstbuf = &lbDisplay.WScreen[0]; - copy_to_screen_px_ar_scale(srcbuf, dstbuf, width, height, flags); - if (smack_draw_callback != NULL) { - smack_draw_callback(lbDisplay.WScreen, lbDisplay.GraphicsScreenWidth, lbDisplay.GraphicsScreenHeight); - } -} - -short play_smk_via_buffer(char *fname, int smkflags, int plyflags) -{ - SYNCDBG(7,"Starting"); - void *snd_driver=GetSoundDriver(); - if ( snd_driver ) - SmackSoundUseMSS(snd_driver); - else - plyflags |= 0x01; - int opnflags = -((plyflags & 0x01) < 1); - struct SmackTag *smktag = SmackOpen(fname, opnflags & 0xFE000, -1); - if ( !smktag ) - return 0; - unsigned long nframe = 1; - unsigned char *buf = (unsigned char *)LbMemoryAlloc(smktag->Width*smktag->Height); - if (buf == NULL) - { - SmackClose(smktag); - return 0; - } - SmackToBuffer(smktag, 0, 0, smktag->Width, smktag->Height, buf, 0); - while ( (plyflags & 0x0400) || (smktag->Frames >= nframe) ) - { - short reset_pal = 0; - int idx; - if ( smktag->NewPalette ) - { - reset_pal = 1; - for (idx=0;idx<768;idx++) - { - unsigned char chr; - chr = smktag->Palette[idx]; - smk_palette[idx] = chr>>2; - } - } - SmackDoFrame(smktag); - if (LbScreenLock() == Lb_SUCCESS) - { - if ( (plyflags & SMK_FullscreenFit) != 0 || (plyflags & SMK_FullscreenStretch) != 0 || (plyflags & SMK_FullscreenCrop) != 0 ) // new scaling mode - { - copy_to_screen_scaled(buf, smktag->Width, smktag->Height, plyflags); - } - else - { - copy_to_screen(buf, smktag->Width, smktag->Height, plyflags); - } - LbScreenUnlock(); - //LbDoMultitasking(); - if ( reset_pal ) - { - LbScreenWaitVbi(); - LbPaletteSet(smk_palette); - } - LbScreenSwap(); - } - SmackNextFrame(smktag); - - do { - if (!LbWindowsControl()) - { - SmackClose(smktag); - return 2; - } - if (((plyflags & SMK_NoStopOnUserInput) == 0) && (lbKeyOn[KC_ESCAPE] - || lbKeyOn[KC_RETURN] || lbKeyOn[KC_SPACE] || lbDisplay.LeftButton) ) - { - SmackClose(smktag); - LbMemoryFree(buf); - return 2; - } - } while ( SmackWait(smktag) ); - ++nframe; - } - LbMemoryFree(buf); - SmackClose(smktag); - return 1; -} - -/** - * Plays Smacker file more directly. - * @return Returns 0 on error, 1 if file was played, 2 if the play was interrupted. - */ -short play_smk_direct(char *fname, int smkflags, int plyflags) -{ - SYNCDBG(7,"Starting"); - - void *snd_driver=GetSoundDriver(); - if ( snd_driver ) - SmackSoundUseMSS(snd_driver); - else - plyflags |= 0x01; - int opnflags = -((plyflags & 0x01) < 1); - struct SmackTag *smktag = SmackOpen(fname, opnflags & 0xFE000, -1); - if ( !smktag ) - return 0; - unsigned long nframe = 1; - while ( (plyflags & 0x0400) || (smktag->Frames-1 >= nframe) ) - { - short reset_pal = 0; - int idx; - if ( smktag->NewPalette ) - { - reset_pal = 1; - for (idx=0;idx<768;idx++) - { - unsigned char chr; - chr = smktag->Palette[idx]; - smk_palette[idx] = chr>>2; - } - } - if (LbScreenLock() == Lb_SUCCESS) - { - int left = 0; - if ( smktag->Width < lbDisplay.PhysicalScreenWidth ) - left = (lbDisplay.PhysicalScreenWidth-smktag->Width) >> 1; - int top = 0; - if ( smktag->Height < lbDisplay.PhysicalScreenHeight ) - top = (lbDisplay.PhysicalScreenHeight-smktag->Height) >> 1; - SmackToBuffer(smktag,left,top,lbDisplay.GraphicsScreenWidth, - lbDisplay.GraphicsScreenHeight,lbDisplay.WScreen,0); - SmackDoFrame(smktag); - LbScreenUnlock(); - //LbDoMultitasking(); - if ( reset_pal ) - { - LbScreenWaitVbi(); - LbPaletteSet(smk_palette); - } - LbScreenSwap(); - } - SmackNextFrame(smktag); - - do { - if (!LbWindowsControl()) - { - SmackClose(smktag); - return 2; - } - if (((plyflags & SMK_NoStopOnUserInput) == 0) && - (lbKeyOn[KC_ESCAPE] || lbKeyOn[KC_RETURN] || lbKeyOn[KC_SPACE] - || lbDisplay.LeftButton) ) - { - SmackClose(smktag); - return 2; - } - } while ( SmackWait(smktag) ); - ++nframe; - } - if ((plyflags & SMK_WriteStatusFile) != 0) - { - struct SmackSumTag smksum; - SmackSummary(smktag, &smksum); - FILE *ssp = fopen("smacksum.txt", "w"); - if ( ssp ) - { - fprintf(ssp, "TotalTime = %ld\n", smksum.TotalTime); - fprintf(ssp, "MS100PerFrame = %ld\n", smksum.MS100PerFrame); - fprintf(ssp, "TotalOpenTime = %ld\n", smksum.TotalOpenTime); - fprintf(ssp, "TotalFrames = %ld\n", smksum.TotalFrames); - fprintf(ssp, "SkippedFrames = %ld\n", smksum.SkippedFrames); - fprintf(ssp, "TotalBlitTime = %ld\n", smksum.TotalBlitTime); - fprintf(ssp, "TotalReadTime = %ld\n", smksum.TotalReadTime); - fprintf(ssp, "TotalDecompTime = %ld\n", smksum.TotalDecompTime); - fprintf(ssp, "TotalBackReadTime = %ld\n", smksum.TotalBackReadTime); - fprintf(ssp, "TotalReadSpeed = %ld\n", smksum.TotalReadSpeed); - fprintf(ssp, "SlowestFrameTime = %ld\n", smksum.SlowestFrameTime); - fprintf(ssp, "Slowest2FrameTime = %ld\n", smksum.Slowest2FrameTime); - fprintf(ssp, "SlowestFrameNum = %ld\n", smksum.SlowestFrameNum); - fprintf(ssp, "Slowest2FrameNum = %ld\n", smksum.Slowest2FrameNum); - fprintf(ssp, "AverageFrameSize = %ld\n", smksum.AverageFrameSize); - fprintf(ssp, "Highest1SecRate = %ld\n", smksum.Highest1SecRate); - fprintf(ssp, "Highest1SecFrame = %ld\n", smksum.Highest1SecFrame); - fprintf(ssp, "HighestMemAmount = %ld\n", smksum.HighestMemAmount); - fprintf(ssp, "TotalExtraMemory = %ld\n", smksum.TotalExtraMemory); - fprintf(ssp, "HighestExtraUsed = %ld\n", smksum.HighestExtraUsed); - fclose(ssp); - } - FILE *svp = fopen(fname, "rb"); - FILE *sgp = fopen("smkgraph.raw", "wb"); - if ( (sgp) && (svp) ) - { - int idx; - fseek(svp, 104, 0); - for (idx=0;idx0; h-- ) - { - animation.field_C++; - for (w=animation.header.width; w>0; ) - { - count = 0; - // Counting size of RLE block - for ( k=1; w>1; k++ ) - { - if (sbuf[k] != sbuf[0]) break; - if (count == 127) break; - w--; - count++; - } - // If RLE block would be valid - if ( count>0 ) - { - if ( count < 127 ) - { count++; w--; } - *animation.field_C = (char)count; - animation.field_C++; - *animation.field_C = sbuf[0]; - animation.field_C++; - sbuf += count; - } else - { - if ( w > 1 ) - { - count=0; - // Find the next block of at least 4 same pixels - for ( k = 0; w>0; k++ ) - { - if ( (sbuf[k+1]==sbuf[k]) && (sbuf[k+2]==sbuf[k]) && (sbuf[k+3]==sbuf[k]) ) - break; - if ( count == -127 ) - break; - count--; - w--; - } - } else - { count=-1; w--; } - if ( count!=0 ) - { - *animation.field_C = (char)count; - animation.field_C++; - memcpy(animation.field_C, sbuf, -count); - sbuf -= count; - animation.field_C -= count; - } - } - } - } - // Make the block size even - if ((int)animation.field_C & 1) - { - *animation.field_C='\0'; - animation.field_C++; - } - return (animation.field_C - blk_begin); -} - -/** - * Compress data into FLI's SS2 block. - * @return Returns unpacked size of the block which was compressed. - */ -long anim_make_FLI_SS2(unsigned char *curdat, unsigned char *prvdat) -{ - unsigned char *blk_begin; - blk_begin=animation.field_C; - unsigned char *cbuf; - unsigned char *pbuf; - unsigned char *cbf; - unsigned char *pbf; - short h; - short w; - short k; - short nsame; - short ndiff; - short wend; - short wendt; - cbuf = curdat; - pbuf = prvdat; - unsigned short *lines_count; - unsigned short *pckt_count; - lines_count = (unsigned short *)animation.field_C; - animation.field_C += 2; - pckt_count = (unsigned short *)animation.field_C; - - wend = 0; - for (h=animation.header.height; h>0; h--) - { - cbf = cbuf; - pbf = pbuf; - if (wend == 0) - { - pckt_count = (unsigned short *)animation.field_C; - animation.field_C += 2; - (*lines_count)++; - } - for (w=animation.header.width;w>0;) - { - for ( k=0; w>0; k++) - { - if ( *(unsigned short *)(pbf+2*(long)k) != *(unsigned short *)(cbf+2*(long)k) ) - break; - w -= 2; - } - if (2*(long)k == animation.header.width) - { - wend--; - cbf += LbGraphicsScreenWidth(); - pbf += LbGraphicsScreenWidth(); - continue; - } - if ( w > 0 ) - { - if (wend != 0) - { - (*pckt_count) = wend; - pckt_count = (unsigned short *)animation.field_C; - animation.field_C += 2; - } - wendt = 2*k; - wend = wendt; - while (wend > 255) - { - *(unsigned char *)animation.field_C = 255; - animation.field_C++; - *(unsigned char *)animation.field_C = 0; - animation.field_C++; - wend -= 255; - (*pckt_count)++; - } - cbf += wendt; - pbf += wendt; - - for (nsame=0; nsame<127; nsame++) - { - if (w <= 2) break; - if ((*(unsigned short *)(pbf+2*nsame+0) == *(unsigned short *)(cbf+2*nsame+0)) && - (*(unsigned short *)(pbf+2*nsame+2) == *(unsigned short *)(cbf+2*nsame+2))) - break; - if ( *(unsigned short *)(cbf+2*nsame+2) != *(unsigned short *)(cbf) ) - break; - w -= 2; - } - if (nsame > 0) - { - if (nsame < 127) - { - nsame++; - w -= 2; - } - *(unsigned char *)animation.field_C = wend; - animation.field_C++; - *(unsigned char *)animation.field_C = -nsame; - animation.field_C++; - *(unsigned short *)animation.field_C = *(unsigned short *)cbf; - animation.field_C+=2; - pbf += 2*nsame; - cbf += 2*nsame; - wend = 0; - (*pckt_count)++; - } else - { - if (w == 2) - { - ndiff = 1; - w -= 2; - } else - { - for (ndiff=0; ndiff<127; ndiff++) - { - if (w <= 0) break; - if ( *(unsigned short *)(pbf+2*ndiff) == *(unsigned short *)(cbf+2*ndiff) ) - break; - if ((*(unsigned short *)(cbf+2*(ndiff+1)) == *(unsigned short *)(cbf+2*ndiff)) && - (*(unsigned short *)(cbf+2*(ndiff+2)) == *(unsigned short *)(cbf+2*ndiff)) ) - break; - w -= 2; - } - } - if (ndiff>0) - { - *(unsigned char *)animation.field_C = wend; - animation.field_C++; - *(unsigned char *)animation.field_C = ndiff; - animation.field_C++; - memcpy(animation.field_C, cbf, 2*(long)ndiff); - animation.field_C += 2*(long)ndiff; - pbf += 2*(long)ndiff; - cbf += 2*(long)ndiff; - wend = 0; - (*pckt_count)++; - } - } - } - } - cbuf += LbGraphicsScreenWidth(); - pbuf += LbGraphicsScreenWidth(); - } - - if (animation.header.height+wend == 0) - { - (*lines_count) = 1; - (*pckt_count) = 1; - *(unsigned char *)animation.field_C = 0; - animation.field_C++; - *(unsigned char *)animation.field_C = 0; - animation.field_C++; - } else - if (wend != 0) - { - animation.field_C -= 2; - (*lines_count)--; - } - // Make the data size even - animation.field_C = (unsigned char *)(((unsigned int)animation.field_C + 1) & 0xFFFFFFFE); - return animation.field_C - blk_begin; -} - -/** - * Compress data into FLI's LC block. - * @return Returns unpacked size of the block which was compressed. - */ -long anim_make_FLI_LC(unsigned char *curdat, unsigned char *prvdat) -{ - unsigned char *blk_begin; - blk_begin=animation.field_C; - unsigned char *cbuf; - unsigned char *pbuf; - unsigned char *cbf; - unsigned char *pbf; - unsigned char *outptr; - short h; - short w; - short hend; - short wend; - short hdim; - short wendt; - short k; - short nsame; - short ndiff; - int blksize; - - cbuf = curdat; - pbuf = prvdat; - for (hend = animation.header.height; hend>0; hend--) - { - wend = 0; - for (w = animation.header.width; w>0; w--) - { - if (cbuf[wend] != pbuf[wend]) break; - ++wend; - } - if ( wend != animation.header.width ) - break; - cbuf += LbGraphicsScreenWidth(); - pbuf += LbGraphicsScreenWidth(); - } - - if (hend != 0) - { - hend = animation.header.height - hend; - blksize = animation.header.width * (long)(animation.header.height-1); - cbuf = curdat+blksize; - pbuf = prvdat+blksize; - for (h=animation.header.height; h>0; h--) - { - wend = 0; - for (w=animation.header.width; w>0; w--) - { - if (cbuf[wend] != pbuf[wend]) break; - wend++; - } - if ( wend != animation.header.width ) - break; - cbuf -= LbGraphicsScreenWidth(); - pbuf -= LbGraphicsScreenWidth(); - } - hdim = h - hend; - blksize = animation.header.width * (long)hend; - cbuf = curdat+blksize; - pbuf = prvdat+blksize; - *(unsigned short *)animation.field_C = hend; - animation.field_C += 2; - *(unsigned short *)animation.field_C = hdim; - animation.field_C += 2; - - for (h = hdim; h>0; h--) - { - cbf = cbuf; - pbf = pbuf; - outptr = animation.field_C++; - for (w=animation.header.width; w>0; ) - { - for ( wend=0; w>0; wend++) - { - if ( cbf[wend] != pbf[wend]) break; - w--; - } - wendt = wend; - if (animation.header.width == wend) continue; - if ( w <= 0 ) break; - while ( wend > 255 ) - { - *(unsigned char *)animation.field_C = 255; - animation.field_C++; - *(unsigned char *)animation.field_C = 0; - animation.field_C++; - wend -= 255; - (*(unsigned char *)outptr)++; - } - cbf += wendt; - pbf += wendt; - k = 0; - nsame = 0; - while ( w > 1 ) - { - if ( nsame == -127 ) break; - if ((cbf[k+0] == pbf[k+0]) && (cbf[k+1] == pbf[k+1]) && (cbf[k+2] == pbf[k+2])) - break; - if (cbf[k+1] != cbf[0]) break; - w--; - k++; - nsame--; - } - if ( nsame ) - { - if ( nsame != -127 ) - { nsame--; w--; } - *(unsigned char *)animation.field_C = wend; - animation.field_C++; - *(unsigned char *)animation.field_C = nsame; - animation.field_C++; - *(unsigned char *)animation.field_C = cbf[0]; - cbf -= nsame; - pbf -= nsame; - animation.field_C++; - (*(unsigned char *)outptr)++; - } else - { - if ( w == 1 ) - { - ndiff = nsame + 1; - w--; - } else - { - k = 0; - ndiff = 0; - while (w != 0) - { - if ( ndiff == 127 ) break; - if ((cbf[k+0] == pbf[k+0]) && (cbf[k+1] == pbf[k+1]) && (cbf[k+2] == pbf[k+2])) - break; - if ((cbf[k+1] == cbf[k+0]) && (cbf[k+2] == cbf[k+0]) && (cbf[k+3] == cbf[k+0])) - break; - w--; - k++; - ndiff++; - } - } - if (ndiff != 0) - { - *(unsigned char *)animation.field_C = wend; - animation.field_C++; - *(unsigned char *)animation.field_C = ndiff; - animation.field_C++; - memcpy(animation.field_C, cbf, ndiff); - animation.field_C += ndiff; - cbf += ndiff; - pbf += ndiff; - (*(unsigned char *)outptr)++; - } - } - } - cbuf += LbGraphicsScreenWidth(); - pbuf += LbGraphicsScreenWidth(); - } - } else - { - *(short *)animation.field_C = 0; - animation.field_C += 2; - *(short *)animation.field_C = 1; - animation.field_C += 2; - *(char *)animation.field_C = 0; - animation.field_C++; - } - // Make the data size even - animation.field_C = (unsigned char *)(((unsigned int)animation.field_C + 1) & 0xFFFFFFFE); - return animation.field_C - blk_begin; -} - -/* - * Returns size of the FLI movie frame buffer, for given width - * and height of animation. The buffer of returned size is big enough - * to store one frame of any kind (any compression). - */ -long anim_buffer_size(int width,int height,int bpp) -{ - int n; - n=(bpp>>3); - if (bpp%8) n++; - return abs(width)*abs(height)*n + 32767; -} - -/* - * Returns size of the FLI movie frame buffer, for given width - * and height of animation. The buffer of returned size is big enough - * to store one frame of any kind (any compression). - */ -short anim_format_matches(int width,int height,int bpp) -{ - if (width != animation.header.width) - return false; - if (height != animation.header.height) - return false; - if (bpp != animation.header.depth) - return false; - return true; -} - -short anim_stop(void) -{ - SYNCLOG("Finishing movie recording."); - if ( ((animation.field_0 & 0x01)==0) || (!animation.outfhndl)) - { - ERRORLOG("Can't stop recording movie"); - return false; - } - LbFileSeek(animation.outfhndl, 0, Lb_FILE_SEEK_BEGINNING); - animation.header.frames--; - LbFileWrite(animation.outfhndl, &animation.header, sizeof(struct AnimFLIHeader)); - if ( LbFileClose(animation.outfhndl) == -1 ) - { - ERRORLOG("Can't close movie file"); - return false; - } - animation.outfhndl = NULL; - LbMemoryFree(animation.chunkdata); - animation.chunkdata=NULL; - animation.field_0 = 0; - return true; -} - -short anim_open(char *fname, int arg1, short arg2, int width, int height, int bpp, unsigned int flags) -{ - if ( flags & animation.field_0 ) - { - ERRORLOG("Cannot record movie"); - return false; - } - if (flags & 0x01) - { - SYNCLOG("Starting to record new movie, \"%s\".",fname); - LbMemorySet(&animation, 0, sizeof(struct Animation)); - animation.field_0 |= flags; - animation.videobuf = LbMemoryAlloc(2 * height*width); - if (animation.videobuf==NULL) - { - ERRORLOG("Cannot allocate video buffer."); - return false; - } - long max_chunk_size = anim_buffer_size(width,height,bpp); - animation.chunkdata = LbMemoryAlloc(max_chunk_size); - if (animation.chunkdata==NULL) - { - ERRORLOG("Cannot allocate chunk buffer."); - return false; - } - animation.outfhndl = LbFileOpen(fname, Lb_FILE_MODE_NEW); - if (!animation.outfhndl) - { - ERRORLOG("Can't open movie file."); - return false; - } - animation.header.dsize = 128; - animation.header.magic = 0xAF12; - animation.header.depth = bpp; - animation.header.flags = 3; - animation.header.speed = 57; - animation.header.created = 0; - animation.header.frames = 0; - animation.header.width = width; - animation.header.updated = 0; - animation.header.aspectx = 6; - animation.header.height = height; - animation.header.reserved2 = 0; - animation.header.creator = 0x464C4942;//'BILF' - animation.header.aspecty = 5; - animation.header.updater = 0x464C4942; - LbMemorySet(animation.header.reserved3, 0, sizeof(animation.header.reserved3)); - animation.header.oframe1 = 0; - animation.header.oframe2 = 0; - LbMemorySet(animation.header.reserved4, 0, sizeof(animation.header.reserved4)); - animation.field_18 = arg2; - if ( !anim_write_data(&animation.header, sizeof(struct AnimFLIHeader)) ) - { - ERRORLOG("Movie write error."); - LbFileClose(animation.outfhndl); - return false; - } - animation.field_31C = 0; - animation.field_320 = height*width + 1024; - LbMemorySet(animation.palette, -1, sizeof(animation.palette)); - } - if (flags & 0x02) - { - SYNCLOG("Resuming movie recording, \"%s\".",fname); - animation.field_0 |= flags; - animation.inpfhndl = LbFileOpen(fname, 2); - if (!animation.inpfhndl) - return false; - // Reading header - if (!anim_read_data(&animation.header, sizeof(struct AnimFLIHeader))) - { - ERRORLOG("Movie header read error."); - LbFileClose(animation.inpfhndl); - return false; - } - // Now we can allocate chunk buffer - long max_chunk_size = anim_buffer_size(animation.header.width,animation.header.height,animation.header.depth); - animation.chunkdata = LbMemoryAlloc(max_chunk_size); - if (animation.chunkdata==NULL) - return false; - if (!anim_read_data(&animation.chunk, sizeof(struct AnimFLIChunk))) - { - ERRORLOG("Movie chunk read error."); - LbFileClose(animation.inpfhndl); - return false; - } - if (animation.chunk.ctype == FLI_PREFIX) - { - if (!anim_read_data(animation.chunkdata, animation.chunk.csize-sizeof(struct AnimFLIChunk))) - { - ERRORLOG("Movie data read error."); - LbFileClose(animation.inpfhndl); - return false; - } - } else - { - LbFileSeek(animation.inpfhndl, -sizeof(struct AnimFLIChunk), Lb_FILE_SEEK_CURRENT); - } - animation.field_31C = 0; - } - return true; -} - -TbBool anim_make_next_frame(unsigned char *screenbuf, unsigned char *palette) -{ - SYNCDBG(7,"Starting"); - unsigned long max_chunk_size; - unsigned char *dataptr; - long brun_size; - long lc_size; - long ss2_size; - int width = animation.header.width; - int height = animation.header.height; - animation.field_C = animation.chunkdata; - max_chunk_size = anim_buffer_size(width,height,animation.header.depth); - LbMemorySet(animation.chunkdata, 0, max_chunk_size); - animation.prefix.ctype = 0xF1FAu; - animation.prefix.nchunks = 0; - animation.prefix.csize = 0; - LbMemorySet(animation.prefix.reserved, 0, sizeof(animation.prefix.reserved)); - struct AnimFLIPrefix *prefx = (struct AnimFLIPrefix *)animation.field_C; - anim_store_data(&animation.prefix, sizeof(struct AnimFLIPrefix)); - animation.subchunk.ctype = 0; - animation.subchunk.csize = 0; - struct AnimFLIChunk *subchnk = (struct AnimFLIChunk *)animation.field_C; - anim_store_data(&animation.subchunk, sizeof(struct AnimFLIChunk)); - if ( animation.field_31C == 0 ) - { - animation.header.oframe1 = animation.header.dsize; - } else - if ( animation.field_31C == 1 ) - { - animation.header.oframe2 = animation.header.dsize; - } - if ( anim_make_FLI_COLOUR256(palette) ) - { - prefx->nchunks++; - subchnk->ctype = 4; - subchnk->csize = animation.field_C-(unsigned char *)subchnk; - animation.subchunk.ctype = 0; - animation.subchunk.csize = 0; - subchnk = (struct AnimFLIChunk *)animation.field_C; - anim_store_data(&animation.subchunk, sizeof(struct AnimFLIChunk)); - } - int scrpoints = animation.header.height * (long)animation.header.width; - if (animation.field_31C == 0) - { - if ( anim_make_FLI_BRUN(screenbuf) ) - { - prefx->nchunks++; - subchnk->ctype = FLI_BRUN; - } else - { - anim_make_FLI_COPY(screenbuf); - prefx->nchunks++; - subchnk->ctype = FLI_COPY; - } - } else - { - // Determining the best compression method - dataptr = animation.field_C; - brun_size = anim_make_FLI_BRUN(screenbuf); - LbMemorySet(dataptr, 0, brun_size); - animation.field_C = dataptr; - ss2_size = anim_make_FLI_SS2(screenbuf, animation.videobuf); - LbMemorySet(dataptr, 0, ss2_size); - animation.field_C = dataptr; - lc_size = anim_make_FLI_LC(screenbuf, animation.videobuf); - if ((lc_size < ss2_size) && (lc_size < brun_size)) - { - // Store the LC compressed data - prefx->nchunks++; - subchnk->ctype = FLI_LC; - } else - if (ss2_size < brun_size) - { - // Clear the LC compressed data - LbMemorySet(dataptr, 0, lc_size); - animation.field_C = dataptr; - // Compress with SS2 method - anim_make_FLI_SS2(screenbuf, animation.videobuf); - prefx->nchunks++; - subchnk->ctype = FLI_SS2; - } else - if ( brun_size < scrpoints+16 ) - { - // Clear the LC compressed data - LbMemorySet(dataptr, 0, lc_size); - animation.field_C = dataptr; - // Compress with BRUN method - anim_make_FLI_BRUN(screenbuf); - prefx->nchunks++; - subchnk->ctype = FLI_BRUN; - } else - { - // Clear the LC compressed data - LbMemorySet(dataptr, 0, lc_size); - animation.field_C = dataptr; - // Store uncompressed frame data - anim_make_FLI_COPY(screenbuf); - prefx->nchunks++; - subchnk->ctype = FLI_COPY; - } - } - subchnk->csize = animation.field_C-(unsigned char *)subchnk; - prefx->csize = animation.field_C - animation.chunkdata; - if ( !anim_write_data(animation.chunkdata, animation.field_C-animation.chunkdata) ) - { - //LbSyncLog("Finished frame w/error.\n"); - return false; - } - memcpy(animation.videobuf, screenbuf, height*width); - memcpy(animation.palette, palette, sizeof(animation.palette)); - animation.header.frames++; - animation.field_31C++; - animation.header.dsize += animation.field_C-animation.chunkdata; - //LbSyncLog("Finished frame ok.\n"); - return true; -} - -TbBool anim_record_frame(unsigned char *screenbuf, unsigned char *palette) -{ - if ((animation.field_0 & 0x01)==0) - return false; - if (!anim_format_matches(MyScreenWidth/pixel_size,MyScreenHeight/pixel_size,LbGraphicsScreenBPP())) - return false; - return anim_make_next_frame(screenbuf, palette); -} - -short anim_record(void) -{ - SYNCDBG(7,"Starting"); - static char finalname[255]; - if (LbGraphicsScreenBPP() != 8) - { - ERRORLOG("Cannot record movie in non-8bit screen mode"); - return 0; - } - int idx; - for (idx=0; idx < 10000; idx++) - { - sprintf(finalname, "%s/game%04d.flc","scrshots",idx); - if (LbFileExists(finalname)) - continue; - return anim_open(finalname, 0, 0, MyScreenWidth/pixel_size,MyScreenHeight/pixel_size,8, 1); - } - ERRORLOG("No free file name for recorded movie"); - return 0; -} - - -/******************************************************************************/ -#ifdef __cplusplus -} -#endif diff --git a/src/bflib_fmvids.cpp b/src/bflib_fmvids.cpp new file mode 100644 index 0000000000..cddc8548c1 --- /dev/null +++ b/src/bflib_fmvids.cpp @@ -0,0 +1,1437 @@ +#include "pre_inc.h" +#include "bflib_fmvids.h" +#include "bflib_video.h" +#include "bflib_inputctrl.h" +#include "bflib_keybrd.h" +#include "bflib_vidsurface.h" +#include "bflib_fileio.h" +#include "bflib_memory.h" + +// See: https://trac.ffmpeg.org/ticket/3626 +extern "C" { + #include + #include + #include + #include +} + +#include +#include +#include +#include +#include +#include "thread.hpp" +#include +#include +#include "post_inc.h" + +namespace { + +void copy_to_screen_pxquad(unsigned char *srcbuf, unsigned char *dstbuf, long width, long dst_shift) +{ + const auto s = dst_shift >> 2; + auto w = ((unsigned long)width) >> 2; + auto * src = reinterpret_cast(srcbuf); + auto * dst = reinterpret_cast(dstbuf); + do { + const auto c = *src++; + const auto i1 = c & 0xFF; + const auto k1 = (c >> 8) & 0xFF; + const auto n1 = (k1 << 24) + (k1 << 16) + (i1 << 8) + i1; + dst[0] = n1; + dst[s] = n1; + const auto i2 = (c >> 16) & 0xFF; + const auto k2 = (c >> 24) & 0xFF; + const auto n2 = (k2 << 24) + (k2 << 16) + (i2 << 8) + i2; + dst[1] = n2; + dst[s+1] = n2; + dst += 2; + w--; + } + while (w > 0); +} + +void copy_to_screen_pxdblh(unsigned char *srcbuf, unsigned char *dstbuf, long width, long dst_shift) +{ + const auto s = dst_shift >> 2; + auto w = ((unsigned long)width) >> 2; + auto src = (unsigned long *)srcbuf; + auto dst = (unsigned long *)dstbuf; + do { + const auto n = *src++; + dst[0] = n; + dst[s] = n; + dst++; + w--; + } + while (w > 0); +} + +void copy_to_screen_pxdblw(unsigned char *srcbuf, unsigned char *dstbuf, long width) +{ + auto w = ((unsigned long)width) >> 2; + auto src = (unsigned long *)srcbuf; + auto dst = (unsigned long *)dstbuf; + do { + const auto c = *src++; + const auto i1 = c & 0xFF; + const auto k1 = (c >> 8) & 0xFF; + dst[0] = (k1 << 24) + (k1 << 16) + (i1 << 8) + i1; + const auto i2 = (c >> 16) & 0xFF; + const auto k2 = (c >> 24) & 0xFF; + dst[1] = (k2 << 24) + (k2 << 16) + (i2 << 8) + i2; + dst += 2; + w--; + } + while (w > 0); +} + +void copy_to_screen(const AVFrame & frame, const int flags) +{ + const auto src_pitch = frame.linesize[0]; + auto srcbuf = frame.data[0]; + long buf_center; + if (flags & (SMK_PixelDoubleLine | SMK_InterlaceLine)) { + buf_center = lbDisplay.GraphicsScreenWidth * ((LbScreenHeight() - 2 * frame.height) >> 1); + } else { + buf_center = lbDisplay.GraphicsScreenWidth * ((LbScreenHeight() - frame.height) >> 1); + } + auto w = frame.width; + if (flags & SMK_PixelDoubleWidth) { + w = 2 * frame.width; + } + auto dstbuf = &lbDisplay.WScreen[buf_center + ((LbScreenWidth() - w) >> 1)]; + if (flags & SMK_PixelDoubleLine) { + if (flags & SMK_PixelDoubleWidth) { + for (int h = frame.height; h > 0; h--) { + copy_to_screen_pxquad(srcbuf, dstbuf, frame.width, lbDisplay.GraphicsScreenWidth); + dstbuf += 2 * lbDisplay.GraphicsScreenWidth; + srcbuf += src_pitch; + } + } else { + for (int h = frame.height; h > 0; h--) { + copy_to_screen_pxdblh(srcbuf, dstbuf, frame.width, lbDisplay.GraphicsScreenWidth); + dstbuf += 2 * lbDisplay.GraphicsScreenWidth; + srcbuf += src_pitch; + } + } + } else { + if (flags & SMK_PixelDoubleWidth) { + if (flags & SMK_InterlaceLine) { + for (int h = frame.height; h > 0; h--) { + copy_to_screen_pxdblw(srcbuf, dstbuf, frame.width); + dstbuf += 2 * lbDisplay.GraphicsScreenWidth; + srcbuf += src_pitch; + } + } else { + for (int h = frame.height; h > 0; h--) { + copy_to_screen_pxdblw(srcbuf, dstbuf, frame.width); + dstbuf += lbDisplay.GraphicsScreenWidth; + srcbuf += src_pitch; + } + } + } else if (flags & SMK_InterlaceLine) { + for (int h = frame.height; h > 0; h--) { + memcpy(dstbuf, srcbuf, frame.width); + dstbuf += 2 * lbDisplay.GraphicsScreenWidth; + srcbuf += src_pitch; + } + } else { + for (int h = frame.height; h > 0; h--) { + memcpy(dstbuf, srcbuf, frame.width); + dstbuf += lbDisplay.GraphicsScreenWidth; + srcbuf += src_pitch; + } + } + } +} + +void copy_to_screen_scaled(const AVFrame & frame, const int flags) +{ + const auto src_pitch = frame.linesize[0]; + const auto src_buf = frame.data[0]; + const auto dst_buf = &lbDisplay.WScreen[0]; + // Compute scaling ratio -> Output co-ordinates and output size + const int scanline = lbDisplay.GraphicsScreenWidth; + const int nlines = lbDisplay.GraphicsScreenHeight; + int spw = 0; + int sph = 0; + int dst_width = 0; + int dst_height = 0; + + if ((flags & SMK_FullscreenStretch) && !(flags & SMK_FullscreenFit)) { + // Use full screen resolution and fill the whole canvas by "stretching" + dst_width = scanline; + dst_height = nlines; + } else { + // Calculate the correct output size + int in_width = frame.width; + int in_height = frame.height; + float units_per_px = 0; + // relative aspect ratio difference between the source frame and destination frame + const float relative_ar_difference = (in_width * 1.0 / in_height * 1.0) / (scanline * 1.0 / nlines * 1.0); + // when keeping aspect ratio, instead of stretching, this is inverted depending on if we want to crop or fit + float comparison_ratio = 1; + if ((flags & SMK_FullscreenStretch) && (flags & SMK_FullscreenFit)) { + // stretch source from 320x200(16:10) to 320x240 (4:3) (i.e. vertical x 1.2) - "preserve *original* aspect ratio mode" + if (frame.width == 320 && frame.height == 200) { + in_height = (int)(in_height * 1.2); + } + } + if ((flags & SMK_FullscreenCrop) && !(flags & SMK_FullscreenFit)) { + // fill screen (will crop) + comparison_ratio = relative_ar_difference; + } else { + // fit to full screen, preserve aspect ratio (pillar/letter boxed) + comparison_ratio = 1.0 / relative_ar_difference; + } + // take either the destination width or height, depending on whether + // the destination is wider or narrower than the source + // (same aspect ratio is treated the same as wider), + // and also if we want to crop or fit + if (comparison_ratio <= 1.0) { + units_per_px = (scanline>nlines?scanline:nlines)/((in_width>in_height?in_width:in_height)/16.0); + } else { + units_per_px = (scanline>nlines?nlines:scanline)/((in_width>in_height?in_height:in_width)/16.0); + } + if ((flags & SMK_FullscreenCrop) && (flags & SMK_FullscreenFit)) { + // Find the highest integer scale possible + if (flags & SMK_FullscreenStretch) { + //4:3 stretch mode (crop off to the nearest 5x/6x scale + if (frame.width == 320 && frame.height == 200) { + // make sure the multiple is integer divisible by 5. Use 5x as a minimum, + // otherwise there will be no video (resolutions smaller than 1600x1200 + // will have a cropped image from a buffer of that size). + units_per_px = (max(5, (int)(units_per_px / 16.0 / 5.0) * 5) * 16); + } + } + // scale to the nearest integer multiple of the source resolution. + units_per_px = ((int)(units_per_px / 16.0) * 16); + } + // Starting point coords and width for the destination buffer (based on desired aspect ratio) + spw = (int)((scanline - in_width * units_per_px / 16.0) / 2.0); + sph = (int)((nlines - in_height * units_per_px / 16.0) / 2.0); + dst_width = (int)(in_width * units_per_px / 16.0); + dst_height = (int)(in_height * units_per_px / 16.0); + } + + // Clearing top of the canvas + for (int sh = 0; sh < sph; sh++) { + LbMemorySet(&dst_buf[sh * scanline], 0, scanline); + } + // Clearing bottom of the canvas + // (Note: it must be done before drawing, to make sure we won't overwrite last line) + for (int sh = sph + dst_height; sh < nlines; sh++) { + LbMemorySet(&dst_buf[sh * scanline], 0, scanline); + } + // Now drawing + auto dhstart = sph; + for (int sh = 0; sh < frame.height; sh++) { + const auto dhend = sph + (dst_height * (sh + 1) / frame.height); + const auto src = &src_buf[sh * src_pitch]; + // make for(k=0;k 0) { + LbMemorySet(dst, 0, dwstart); + } + for (int sw = 0; sw < frame.width; sw++) { + const auto dwend = spw + (dst_width * (sw + 1) / frame.width); + // make for(i=0;istreams[m_video_index]->time_base; + } + + ~movie_t() noexcept { + if (m_format_context) { + avformat_close_input(&m_format_context); + } + if (m_audio_context) { + avcodec_free_context(&m_audio_context); + } + if (m_video_context) { + avcodec_free_context(&m_video_context); + } + if (m_frame) { + av_frame_free(&m_frame); + } + if (m_packet) { + av_packet_free(&m_packet); + } + if (m_resampler) { + swr_free(&m_resampler); + } + if (m_audio_device > 0) { + SDL_CloseAudioDevice(m_audio_device); + m_audio_device = 0; + } + } + + void open_input(const char * filename) { + if (avformat_open_input(&m_format_context, filename, nullptr, nullptr) != 0) { + throw std::runtime_error("Cannot open source file"); + } + } + + AVSampleFormat sdl_to_ffmpeg_format(SDL_AudioFormat format) { + switch (format) { + case AUDIO_S8: return AV_SAMPLE_FMT_U8; + case AUDIO_S16SYS: return AV_SAMPLE_FMT_S16; + case AUDIO_S32SYS: return AV_SAMPLE_FMT_S32; + case AUDIO_F32SYS: return AV_SAMPLE_FMT_FLT; + default: return AV_SAMPLE_FMT_NONE; + } + } + + AVChannelLayout channels_to_ffmpeg_layout(int channels) { + switch (channels) { + case 1: return AV_CHANNEL_LAYOUT_MONO; + case 2: return AV_CHANNEL_LAYOUT_STEREO; + case 3: return AV_CHANNEL_LAYOUT_SURROUND; + case 4: return AV_CHANNEL_LAYOUT_QUAD; + case 5: return AV_CHANNEL_LAYOUT_4POINT1; + case 6: return AV_CHANNEL_LAYOUT_5POINT1; + case 7: return AV_CHANNEL_LAYOUT_6POINT1; + case 8: return AV_CHANNEL_LAYOUT_7POINT1; + default: return {}; + } + } + + void open_audio_device() { + SDL_AudioSpec desired, obtained; + desired.freq = 44100; + desired.format = AUDIO_F32SYS; + desired.channels = 2; + desired.silence = 0; + desired.samples = 0; + desired.padding = 0; + desired.size = 0; + desired.callback = nullptr; + desired.userdata = nullptr; + m_audio_device = SDL_OpenAudioDevice(nullptr, 0, &desired, &obtained, SDL_AUDIO_ALLOW_ANY_CHANGE); + if (m_audio_device <= 0) { + throw std::runtime_error("Cannot open audio device"); + } + m_output_audio_channels = obtained.channels; + m_output_audio_frequency = obtained.freq; + m_output_audio_format = sdl_to_ffmpeg_format(obtained.format); + m_output_audio_layout = channels_to_ffmpeg_layout(obtained.channels); + } + + void find_stream_info() { + if (avformat_find_stream_info(m_format_context, nullptr) < 0) { + throw std::runtime_error("Could not find stream information"); + } + } + + AVCodecContext * make_context(const AVCodec * codec) { + const auto context = avcodec_alloc_context3(codec); + if (!context) { + throw std::runtime_error("Failed to allocate codec context"); + } + return context; + } + + const AVCodec * find_codec(const AVStream * stream) { + return avcodec_find_decoder(stream->codecpar->codec_id); + } + + void copy_parameters(AVCodecContext * codec_context, const AVStream * stream) { + if (avcodec_parameters_to_context(codec_context, stream->codecpar) != 0) { + throw std::runtime_error("Failed to copy codec parameters to decoder context"); + } + } + + void open_codec(AVCodecContext * codec_context, const AVCodec * codec) { + if (avcodec_open2(codec_context, codec, nullptr) != 0) { + throw std::runtime_error("Failed to open codec"); + } + } + + int find_best_stream(AVMediaType type) { + return av_find_best_stream(m_format_context, type, -1, -1, nullptr, 0); + } + + void setup_audio() { + m_audio_index = find_best_stream(AVMEDIA_TYPE_AUDIO); + if (m_audio_index >= 0) { + m_audio_stream = m_format_context->streams[m_audio_index]; + m_audio_codec = find_codec(m_audio_stream); + if (m_audio_codec) { + m_audio_context = make_context(m_audio_codec); + copy_parameters(m_audio_context, m_audio_stream); + open_codec(m_audio_context, m_audio_codec); + } + } + } + + void setup_video() { + m_video_index = find_best_stream(AVMEDIA_TYPE_VIDEO); + if (m_video_index < 0) { + throw std::runtime_error("Could not find video stream"); + } + m_video_stream = m_format_context->streams[m_video_index]; + m_video_codec = find_codec(m_video_stream); + if (!m_video_codec) { + throw std::runtime_error("Failed to find codec"); + } + m_video_context = make_context(m_video_codec); + copy_parameters(m_video_context, m_video_stream); + open_codec(m_video_context, m_video_codec); + } + + void make_frame() { + m_frame = av_frame_alloc(); + if (!m_frame) { + throw std::runtime_error("Could not allocate frame"); + } + } + + void make_packet() { + m_packet = av_packet_alloc(); + if (!m_packet) { + throw std::runtime_error("Could not allocate packet"); + } + } + + void make_resampler() { + if (swr_alloc_set_opts2( + &m_resampler, + &m_output_audio_layout, + m_output_audio_format, + m_output_audio_frequency, + &m_audio_context->ch_layout, + m_audio_context->sample_fmt, + m_audio_context->sample_rate, + 0, + nullptr + ) != 0) { + throw std::runtime_error("Cannot create resampler"); + } + if (swr_init(m_resampler) != 0) { + throw std::runtime_error("Could not initialize resampler"); + } + } + + duration time_since_video_start() { + if (m_video_start == time_point()) { + m_video_start = clock::now(); + return duration(); + } + return clock::now() - m_video_start; + } + + void output_audio_frame() { + const auto sample_size = av_get_bytes_per_sample(m_output_audio_format); + const auto channels = m_frame->ch_layout.nb_channels; + const auto buffer_samples = swr_get_out_samples(m_resampler, m_frame->nb_samples); + // auto dst_samples = channels * av_rescale_rnd( + // swr_get_delay(m_resampler, m_frame->sample_rate) + m_frame->nb_samples, + // m_output_audio_frequency, + // m_frame->sample_rate, + // AV_ROUND_UP + // ); + uint8_t * buffer = nullptr; + av_samples_alloc( + &buffer, + nullptr, + channels, + buffer_samples, + m_output_audio_format, + 1 + ); + const auto num_samples = channels * swr_convert( + m_resampler, + &buffer, + buffer_samples, + m_frame->data, + m_frame->nb_samples + ); + SDL_QueueAudio(m_audio_device, buffer, num_samples * sample_size); + SDL_PauseAudioDevice(m_audio_device, 0); + av_freep(&buffer); + } + + void output_video_frame() { + if (m_frame->palette_has_changed) { + JUSTLOG("palette changed"); + SDL_Color palette[PALETTE_COLORS]; + for (size_t i = 0; i < PALETTE_COLORS; ++i) { + palette[i].b = m_frame->data[1][(i * 4) + 0]; // blue + palette[i].g = m_frame->data[1][(i * 4) + 1]; // green + palette[i].r = m_frame->data[1][(i * 4) + 2]; // red + } + LbScreenWaitVbi(); // this is a no-op today + // LbPaletteSet expects values in range 0-63 for reasons, nuking 75% of the color range + SDL_SetPaletteColors(lbDrawSurface->format->palette, palette, 0, PALETTE_COLORS); + } + if (LbScreenLock() != Lb_SUCCESS) { + return; + } else if (m_flags & (SMK_FullscreenFit | SMK_FullscreenStretch | SMK_FullscreenCrop)) { // new scaling mode + copy_to_screen_scaled(*m_frame, m_flags); + } else { + copy_to_screen(*m_frame, m_flags); + } + LbScreenUnlock(); + LbScreenSwap(); + } + + bool output_audio_frames() { + while (true) { + const auto result = avcodec_receive_frame(m_audio_context, m_frame); + if (result != 0) { + if (result == AVERROR(EAGAIN)) { + return true; + } else { + return false; + } + } + output_audio_frame(); + } + } + + void wait_for_pts() { + const duration pts = nanoseconds((int64_t(m_frame->pts) * (1000000000 / m_time_base.den)) * m_time_base.num); + const auto now = time_since_video_start(); + const auto delta = pts - now; + if (delta > duration()) { + std::this_thread::sleep_for(delta); + } + } + + bool output_video_frames() { + while (true) { + const auto result = avcodec_receive_frame(m_video_context, m_frame); + if (result != 0) { + if (result == AVERROR(EAGAIN)) { + return true; + } else { + return false; + } + } + wait_for_pts(); + output_video_frame(); + if (!LbWindowsControl()) { + return false; + } else if (m_flags & SMK_NoStopOnUserInput) { + return true; + } else if (lbKeyOn[KC_ESCAPE] || lbKeyOn[KC_RETURN] || lbKeyOn[KC_SPACE] || lbDisplay.LeftButton) { + return false; + } + } + } + + bool decode_audio() { + if (avcodec_send_packet(m_audio_context, m_packet) != 0) { + return false; + } + return output_audio_frames(); + } + + bool decode_video() { + if (avcodec_send_packet(m_video_context, m_packet) != 0) { + return false; + } + return output_video_frames(); + } + + void flush_audio() { + output_audio_frames(); + } + + void flush_video() { + output_video_frames(); + } + + bool read_frame() { + return av_read_frame(m_format_context, m_packet) >= 0; + } + + void play() { + while (read_frame()) { + if (m_packet->stream_index == m_audio_index) { + if (!decode_audio()) { + break; + } + } else if (m_packet->stream_index == m_video_index) { + if (!decode_video()) { + break; + } + } + } + flush_audio(); + flush_video(); + } +}; + +} // local + +extern "C" TbBool play_smk(const char * filename, const int flags) { + try { + lbDisplay.LeftButton = 0; // hack? + movie_t movie(filename, flags); + movie.play(); + return true; + } catch (const std::exception & e) { + ERRORLOG("Error playing %s: %s", filename, e.what()); + } + return false; +} + +namespace { + +enum { + FLI_PREFIX = 0xF100, + FLI_COLOR256 = 4, + FLI_SS2 = 7, + FLI_COLOR = 11, + FLI_LC = 12, + FLI_BLACK = 13, + FLI_BRUN = 15, + FLI_COPY = 16, + FLI_PSTAMP = 18, +}; + +#pragma pack(1) +struct AnimFLIHeader { // sizeof=0x80 + unsigned long dsize; + unsigned short magic; + unsigned short frames; + short width; + short height; + unsigned short depth; + unsigned short flags; + unsigned long speed; + short reserved2; + unsigned long created; + unsigned long creator; + unsigned long updated; + unsigned long updater; + short aspectx; + short aspecty; + char reserved3[38]; + unsigned long oframe1; + unsigned long oframe2; + char reserved4[40]; +}; +#pragma pack() + +#pragma pack(1) +struct AnimFLIChunk { //sizeof=0x6 + long csize; + unsigned short ctype; +}; +#pragma pack() + +#pragma pack(1) +struct AnimFLIPrefix { //sizeof=0x6 + long csize; + unsigned short ctype; + short nchunks; + char reserved[8]; +}; +#pragma pack() + +struct Animation { + long field_0; + unsigned char *videobuf; + unsigned char *chunkdata; + unsigned char *field_C; + TbFileHandle inpfhndl; + TbFileHandle outfhndl; + short field_18; + short field_1A; + unsigned char palette[768]; + long field_31C; + long field_320; + long field_324; + AnimFLIHeader header; + AnimFLIChunk chunk; + AnimFLIPrefix prefix; + AnimFLIChunk subchunk; + char field_3C4[12]; +}; + +Animation animation; + +/** + * Writes the data into FLI animation. + * @return Returns false on error, true on success. + */ +short anim_write_data(void *buf, long size) +{ + return LbFileWrite(animation.outfhndl,buf,size) == size; +} + +/** + * Stores data into FLI buffer. + * @return Returns false on error, true on success. + */ +short anim_store_data(void *buf, long size) +{ + memcpy(animation.field_C, buf, size); + animation.field_C += size; + return true; +} + +/** + * Reads the data from FLI animation. + * @return Returns false on error, true on success. + */ +short anim_read_data(void *buf, long size) +{ + if (buf == NULL) { + LbFileSeek(animation.inpfhndl,size,Lb_FILE_SEEK_CURRENT); + return true; + } else if (LbFileRead(animation.inpfhndl,buf,size) == size) { + return true; + } + return false; +} + +long anim_make_FLI_COPY(unsigned char *screenbuf) +{ + int scrpoints = animation.header.height * animation.header.width; + memcpy(animation.field_C, screenbuf, scrpoints); + animation.field_C += scrpoints; + return scrpoints; +} + +long anim_make_FLI_COLOUR256(unsigned char *palette) +{ + if (LbMemoryCompare(animation.palette, palette, 768) == 0) { + return 0; + } + unsigned short *change_count; + unsigned char *kept_count; + short colridx; + short change_chunk_len; + short kept_chunk_len; + change_chunk_len = 0; + kept_chunk_len = 0; + change_count = (unsigned short *)animation.field_C; + kept_count = NULL; + animation.field_C += 2; + for (colridx = 0; colridx < 256; colridx++) { + unsigned char *anipal; + unsigned char *srcpal; + anipal = &animation.palette[3 * colridx]; + srcpal = &palette[3 * colridx]; + if (LbMemoryCompare(anipal, srcpal, 3) == 0) { + change_chunk_len = 0; + kept_chunk_len++; + } else { + if (!change_chunk_len) { + *animation.field_C = kept_chunk_len; + kept_chunk_len = 0; + animation.field_C++; + kept_count = (unsigned char *)animation.field_C; + animation.field_C++; + } + ++change_chunk_len; + *animation.field_C = 4 * srcpal[0]; + animation.field_C++; + *animation.field_C = 4 * srcpal[1]; + animation.field_C++; + *animation.field_C = 4 * srcpal[2]; + animation.field_C++; + ++(*kept_count); + } + if (change_chunk_len == 1) { + ++(*change_count); + } + } + return 1; +} + +/** + * Compress data into FLI's BRUN block (8-bit Run-Length compression). + * @return Returns unpacked size of the block which was compressed. + */ +long anim_make_FLI_BRUN(unsigned char *screenbuf) { + unsigned char *blk_begin = animation.field_C; + short w; + short h; + short k; + short count; + unsigned char *sbuf = screenbuf; + for ( h = animation.header.height; h>0; h-- ) { + animation.field_C++; + for (w=animation.header.width; w>0; ) { + count = 0; + // Counting size of RLE block + for ( k=1; w>1; k++ ) { + if (sbuf[k] != sbuf[0]) break; + if (count == 127) break; + w--; + count++; + } + // If RLE block would be valid + if ( count>0 ) { + if ( count < 127 ) { + count++; + w--; + } + *animation.field_C = (char)count; + animation.field_C++; + *animation.field_C = sbuf[0]; + animation.field_C++; + sbuf += count; + } else { + if ( w > 1 ) { + count=0; + // Find the next block of at least 4 same pixels + for ( k = 0; w>0; k++ ) { + if ( (sbuf[k+1]==sbuf[k]) && (sbuf[k+2]==sbuf[k]) && (sbuf[k+3]==sbuf[k]) ) break; + if ( count == -127 ) break; + count--; + w--; + } + } else { + count=-1; + w--; + } + if ( count!=0 ) { + *animation.field_C = (char)count; + animation.field_C++; + memcpy(animation.field_C, sbuf, -count); + sbuf -= count; + animation.field_C -= count; + } + } + } + } + // Make the block size even + if ((int)animation.field_C & 1) { + *animation.field_C='\0'; + animation.field_C++; + } + return (animation.field_C - blk_begin); +} + +/** + * Compress data into FLI's SS2 block. + * @return Returns unpacked size of the block which was compressed. + */ +long anim_make_FLI_SS2(unsigned char *curdat, unsigned char *prvdat) +{ + unsigned char *blk_begin; + blk_begin=animation.field_C; + unsigned char *cbuf; + unsigned char *pbuf; + unsigned char *cbf; + unsigned char *pbf; + short h; + short w; + short k; + short nsame; + short ndiff; + short wend; + short wendt; + cbuf = curdat; + pbuf = prvdat; + unsigned short *lines_count; + unsigned short *pckt_count; + lines_count = (unsigned short *)animation.field_C; + animation.field_C += 2; + pckt_count = (unsigned short *)animation.field_C; + + wend = 0; + for (h=animation.header.height; h>0; h--) { + cbf = cbuf; + pbf = pbuf; + if (wend == 0) { + pckt_count = (unsigned short *)animation.field_C; + animation.field_C += 2; + (*lines_count)++; + } + for (w=animation.header.width;w>0;) { + for ( k=0; w>0; k++) { + if ( *(unsigned short *)(pbf+2*(long)k) != *(unsigned short *)(cbf+2*(long)k) ) break; + w -= 2; + } + if (2*(long)k == animation.header.width) { + wend--; + cbf += LbGraphicsScreenWidth(); + pbf += LbGraphicsScreenWidth(); + continue; + } + if ( w > 0 ) { + if (wend != 0) { + (*pckt_count) = wend; + pckt_count = (unsigned short *)animation.field_C; + animation.field_C += 2; + } + wendt = 2*k; + wend = wendt; + while (wend > 255) { + *(unsigned char *)animation.field_C = 255; + animation.field_C++; + *(unsigned char *)animation.field_C = 0; + animation.field_C++; + wend -= 255; + (*pckt_count)++; + } + cbf += wendt; + pbf += wendt; + for (nsame=0; nsame<127; nsame++) { + if (w <= 2) break; + if ((*(unsigned short *)(pbf+2*nsame+0) == *(unsigned short *)(cbf+2*nsame+0)) && + (*(unsigned short *)(pbf+2*nsame+2) == *(unsigned short *)(cbf+2*nsame+2))) { + break; + } + if ( *(unsigned short *)(cbf+2*nsame+2) != *(unsigned short *)(cbf) ) break; + w -= 2; + } + if (nsame > 0) { + if (nsame < 127) { + nsame++; + w -= 2; + } + *(unsigned char *)animation.field_C = wend; + animation.field_C++; + *(unsigned char *)animation.field_C = -nsame; + animation.field_C++; + *(unsigned short *)animation.field_C = *(unsigned short *)cbf; + animation.field_C+=2; + pbf += 2*nsame; + cbf += 2*nsame; + wend = 0; + (*pckt_count)++; + } else { + if (w == 2) { + ndiff = 1; + w -= 2; + } else { + for (ndiff=0; ndiff<127; ndiff++) { + if (w <= 0) break; + if ( *(unsigned short *)(pbf+2*ndiff) == *(unsigned short *)(cbf+2*ndiff) ) break; + if ((*(unsigned short *)(cbf+2*(ndiff+1)) == *(unsigned short *)(cbf+2*ndiff)) && + (*(unsigned short *)(cbf+2*(ndiff+2)) == *(unsigned short *)(cbf+2*ndiff)) ) { + break; + } + w -= 2; + } + } + if (ndiff>0) { + *(unsigned char *)animation.field_C = wend; + animation.field_C++; + *(unsigned char *)animation.field_C = ndiff; + animation.field_C++; + memcpy(animation.field_C, cbf, 2*(long)ndiff); + animation.field_C += 2*(long)ndiff; + pbf += 2*(long)ndiff; + cbf += 2*(long)ndiff; + wend = 0; + (*pckt_count)++; + } + } + } + } + cbuf += LbGraphicsScreenWidth(); + pbuf += LbGraphicsScreenWidth(); + } + + if (animation.header.height+wend == 0) { + (*lines_count) = 1; + (*pckt_count) = 1; + *(unsigned char *)animation.field_C = 0; + animation.field_C++; + *(unsigned char *)animation.field_C = 0; + animation.field_C++; + } else if (wend != 0) { + animation.field_C -= 2; + (*lines_count)--; + } + // Make the data size even + animation.field_C = (unsigned char *)(((unsigned int)animation.field_C + 1) & 0xFFFFFFFE); + return animation.field_C - blk_begin; +} + +/** + * Compress data into FLI's LC block. + * @return Returns unpacked size of the block which was compressed. + */ +long anim_make_FLI_LC(unsigned char *curdat, unsigned char *prvdat) +{ + unsigned char *blk_begin; + blk_begin=animation.field_C; + unsigned char *cbuf; + unsigned char *pbuf; + unsigned char *cbf; + unsigned char *pbf; + unsigned char *outptr; + short h; + short w; + short hend; + short wend; + short hdim; + short wendt; + short k; + short nsame; + short ndiff; + int blksize; + + cbuf = curdat; + pbuf = prvdat; + for (hend = animation.header.height; hend>0; hend--) { + wend = 0; + for (w = animation.header.width; w>0; w--) { + if (cbuf[wend] != pbuf[wend]) break; + ++wend; + } + if ( wend != animation.header.width ) break; + cbuf += LbGraphicsScreenWidth(); + pbuf += LbGraphicsScreenWidth(); + } + if (hend != 0) { + hend = animation.header.height - hend; + blksize = animation.header.width * (long)(animation.header.height-1); + cbuf = curdat+blksize; + pbuf = prvdat+blksize; + for (h=animation.header.height; h>0; h--) { + wend = 0; + for (w=animation.header.width; w>0; w--) { + if (cbuf[wend] != pbuf[wend]) break; + wend++; + } + if ( wend != animation.header.width ) break; + cbuf -= LbGraphicsScreenWidth(); + pbuf -= LbGraphicsScreenWidth(); + } + hdim = h - hend; + blksize = animation.header.width * (long)hend; + cbuf = curdat+blksize; + pbuf = prvdat+blksize; + *(unsigned short *)animation.field_C = hend; + animation.field_C += 2; + *(unsigned short *)animation.field_C = hdim; + animation.field_C += 2; + + for (h = hdim; h>0; h--) { + cbf = cbuf; + pbf = pbuf; + outptr = animation.field_C++; + for (w=animation.header.width; w>0; ) { + for ( wend=0; w>0; wend++) { + if ( cbf[wend] != pbf[wend]) break; + w--; + } + wendt = wend; + if (animation.header.width == wend) continue; + if ( w <= 0 ) break; + while ( wend > 255 ) { + *(unsigned char *)animation.field_C = 255; + animation.field_C++; + *(unsigned char *)animation.field_C = 0; + animation.field_C++; + wend -= 255; + (*(unsigned char *)outptr)++; + } + cbf += wendt; + pbf += wendt; + k = 0; + nsame = 0; + while ( w > 1 ) { + if ( nsame == -127 ) break; + if ((cbf[k+0] == pbf[k+0]) && (cbf[k+1] == pbf[k+1]) && (cbf[k+2] == pbf[k+2])) break; + if (cbf[k+1] != cbf[0]) break; + w--; + k++; + nsame--; + } + if ( nsame ) { + if ( nsame != -127 ) { + nsame--; + w--; + } + *(unsigned char *)animation.field_C = wend; + animation.field_C++; + *(unsigned char *)animation.field_C = nsame; + animation.field_C++; + *(unsigned char *)animation.field_C = cbf[0]; + cbf -= nsame; + pbf -= nsame; + animation.field_C++; + (*(unsigned char *)outptr)++; + } else { + if ( w == 1 ) { + ndiff = nsame + 1; + w--; + } else { + k = 0; + ndiff = 0; + while (w != 0) { + if ( ndiff == 127 ) break; + if ((cbf[k+0] == pbf[k+0]) && (cbf[k+1] == pbf[k+1]) && (cbf[k+2] == pbf[k+2])) + break; + if ((cbf[k+1] == cbf[k+0]) && (cbf[k+2] == cbf[k+0]) && (cbf[k+3] == cbf[k+0])) + break; + w--; + k++; + ndiff++; + } + } + if (ndiff != 0) { + *(unsigned char *)animation.field_C = wend; + animation.field_C++; + *(unsigned char *)animation.field_C = ndiff; + animation.field_C++; + memcpy(animation.field_C, cbf, ndiff); + animation.field_C += ndiff; + cbf += ndiff; + pbf += ndiff; + (*(unsigned char *)outptr)++; + } + } + } + cbuf += LbGraphicsScreenWidth(); + pbuf += LbGraphicsScreenWidth(); + } + } else { + *(short *)animation.field_C = 0; + animation.field_C += 2; + *(short *)animation.field_C = 1; + animation.field_C += 2; + *(char *)animation.field_C = 0; + animation.field_C++; + } + // Make the data size even + animation.field_C = (unsigned char *)(((unsigned int)animation.field_C + 1) & 0xFFFFFFFE); + return animation.field_C - blk_begin; +} + +/* + * Returns size of the FLI movie frame buffer, for given width + * and height of animation. The buffer of returned size is big enough + * to store one frame of any kind (any compression). + */ +long anim_buffer_size(int width,int height,int bpp) +{ + int n = (bpp>>3); + if (bpp%8) n++; + return abs(width)*abs(height)*n + 32767; +} + +/* + * Returns size of the FLI movie frame buffer, for given width + * and height of animation. The buffer of returned size is big enough + * to store one frame of any kind (any compression). + */ +short anim_format_matches(int width,int height,int bpp) +{ + if (width != animation.header.width) { + return false; + } else if (height != animation.header.height) { + return false; + } else if (bpp != animation.header.depth) { + return false; + } + return true; +} + +short anim_open(char *fname, int arg1, short arg2, int width, int height, int bpp, unsigned int flags) +{ + if ( flags & animation.field_0 ) { + ERRORLOG("Cannot record movie"); + return false; + } + if (flags & 0x01) { + SYNCLOG("Starting to record new movie, \"%s\".",fname); + LbMemorySet(&animation, 0, sizeof(Animation)); + animation.field_0 |= flags; + animation.videobuf = static_cast(LbMemoryAlloc(2 * height*width)); + if (animation.videobuf==NULL) { + ERRORLOG("Cannot allocate video buffer."); + return false; + } + long max_chunk_size = anim_buffer_size(width,height,bpp); + animation.chunkdata = static_cast(LbMemoryAlloc(max_chunk_size)); + if (animation.chunkdata==NULL) { + ERRORLOG("Cannot allocate chunk buffer."); + return false; + } + animation.outfhndl = LbFileOpen(fname, Lb_FILE_MODE_NEW); + if (!animation.outfhndl) { + ERRORLOG("Can't open movie file."); + return false; + } + animation.header.dsize = 128; + animation.header.magic = 0xAF12; + animation.header.depth = bpp; + animation.header.flags = 3; + animation.header.speed = 57; + animation.header.created = 0; + animation.header.frames = 0; + animation.header.width = width; + animation.header.updated = 0; + animation.header.aspectx = 6; + animation.header.height = height; + animation.header.reserved2 = 0; + animation.header.creator = 0x464C4942;//'BILF' + animation.header.aspecty = 5; + animation.header.updater = 0x464C4942; + LbMemorySet(animation.header.reserved3, 0, sizeof(animation.header.reserved3)); + animation.header.oframe1 = 0; + animation.header.oframe2 = 0; + LbMemorySet(animation.header.reserved4, 0, sizeof(animation.header.reserved4)); + animation.field_18 = arg2; + if ( !anim_write_data(&animation.header, sizeof(AnimFLIHeader)) ) { + ERRORLOG("Movie write error."); + LbFileClose(animation.outfhndl); + return false; + } + animation.field_31C = 0; + animation.field_320 = height*width + 1024; + LbMemorySet(animation.palette, -1, sizeof(animation.palette)); + } + if (flags & 0x02) { + SYNCLOG("Resuming movie recording, \"%s\".",fname); + animation.field_0 |= flags; + animation.inpfhndl = LbFileOpen(fname, 2); + if (!animation.inpfhndl) { + return false; + } + // Reading header + if (!anim_read_data(&animation.header, sizeof(AnimFLIHeader))) { + ERRORLOG("Movie header read error."); + LbFileClose(animation.inpfhndl); + return false; + } + // Now we can allocate chunk buffer + long max_chunk_size = anim_buffer_size(animation.header.width,animation.header.height,animation.header.depth); + animation.chunkdata = static_cast(LbMemoryAlloc(max_chunk_size)); + if (animation.chunkdata==NULL) { + return false; + } + if (!anim_read_data(&animation.chunk, sizeof(AnimFLIChunk))) { + ERRORLOG("Movie chunk read error."); + LbFileClose(animation.inpfhndl); + return false; + } + if (animation.chunk.ctype == FLI_PREFIX) { + if (!anim_read_data(animation.chunkdata, animation.chunk.csize-sizeof(AnimFLIChunk))) { + ERRORLOG("Movie data read error."); + LbFileClose(animation.inpfhndl); + return false; + } + } else { + LbFileSeek(animation.inpfhndl, -sizeof(AnimFLIChunk), Lb_FILE_SEEK_CURRENT); + } + animation.field_31C = 0; + } + return true; +} + +TbBool anim_make_next_frame(unsigned char *screenbuf, unsigned char *palette) +{ + SYNCDBG(7,"Starting"); + unsigned long max_chunk_size; + unsigned char *dataptr; + long brun_size; + long lc_size; + long ss2_size; + int width = animation.header.width; + int height = animation.header.height; + animation.field_C = animation.chunkdata; + max_chunk_size = anim_buffer_size(width,height,animation.header.depth); + LbMemorySet(animation.chunkdata, 0, max_chunk_size); + animation.prefix.ctype = 0xF1FAu; + animation.prefix.nchunks = 0; + animation.prefix.csize = 0; + LbMemorySet(animation.prefix.reserved, 0, sizeof(animation.prefix.reserved)); + AnimFLIPrefix *prefx = (AnimFLIPrefix *)animation.field_C; + anim_store_data(&animation.prefix, sizeof(AnimFLIPrefix)); + animation.subchunk.ctype = 0; + animation.subchunk.csize = 0; + AnimFLIChunk *subchnk = (AnimFLIChunk *)animation.field_C; + anim_store_data(&animation.subchunk, sizeof(AnimFLIChunk)); + if ( animation.field_31C == 0 ) { + animation.header.oframe1 = animation.header.dsize; + } else if ( animation.field_31C == 1 ) { + animation.header.oframe2 = animation.header.dsize; + } + if ( anim_make_FLI_COLOUR256(palette) ) { + prefx->nchunks++; + subchnk->ctype = 4; + subchnk->csize = animation.field_C-(unsigned char *)subchnk; + animation.subchunk.ctype = 0; + animation.subchunk.csize = 0; + subchnk = (AnimFLIChunk *)animation.field_C; + anim_store_data(&animation.subchunk, sizeof(AnimFLIChunk)); + } + int scrpoints = animation.header.height * (long)animation.header.width; + if (animation.field_31C == 0) { + if ( anim_make_FLI_BRUN(screenbuf) ) { + prefx->nchunks++; + subchnk->ctype = FLI_BRUN; + } else { + anim_make_FLI_COPY(screenbuf); + prefx->nchunks++; + subchnk->ctype = FLI_COPY; + } + } else { + // Determining the best compression method + dataptr = animation.field_C; + brun_size = anim_make_FLI_BRUN(screenbuf); + LbMemorySet(dataptr, 0, brun_size); + animation.field_C = dataptr; + ss2_size = anim_make_FLI_SS2(screenbuf, animation.videobuf); + LbMemorySet(dataptr, 0, ss2_size); + animation.field_C = dataptr; + lc_size = anim_make_FLI_LC(screenbuf, animation.videobuf); + if ((lc_size < ss2_size) && (lc_size < brun_size)) { + // Store the LC compressed data + prefx->nchunks++; + subchnk->ctype = FLI_LC; + } else if (ss2_size < brun_size) { + // Clear the LC compressed data + LbMemorySet(dataptr, 0, lc_size); + animation.field_C = dataptr; + // Compress with SS2 method + anim_make_FLI_SS2(screenbuf, animation.videobuf); + prefx->nchunks++; + subchnk->ctype = FLI_SS2; + } else if ( brun_size < scrpoints+16 ) { + // Clear the LC compressed data + LbMemorySet(dataptr, 0, lc_size); + animation.field_C = dataptr; + // Compress with BRUN method + anim_make_FLI_BRUN(screenbuf); + prefx->nchunks++; + subchnk->ctype = FLI_BRUN; + } else { + // Clear the LC compressed data + LbMemorySet(dataptr, 0, lc_size); + animation.field_C = dataptr; + // Store uncompressed frame data + anim_make_FLI_COPY(screenbuf); + prefx->nchunks++; + subchnk->ctype = FLI_COPY; + } + } + subchnk->csize = animation.field_C-(unsigned char *)subchnk; + prefx->csize = animation.field_C - animation.chunkdata; + if ( !anim_write_data(animation.chunkdata, animation.field_C-animation.chunkdata) ) { + //LbSyncLog("Finished frame w/error.\n"); + return false; + } + memcpy(animation.videobuf, screenbuf, height*width); + memcpy(animation.palette, palette, sizeof(animation.palette)); + animation.header.frames++; + animation.field_31C++; + animation.header.dsize += animation.field_C-animation.chunkdata; + //LbSyncLog("Finished frame ok.\n"); + return true; +} + +} // local + +extern "C" short anim_stop() +{ + SYNCLOG("Finishing movie recording."); + if ( ((animation.field_0 & 0x01)==0) || (!animation.outfhndl)) { + ERRORLOG("Can't stop recording movie"); + return false; + } + LbFileSeek(animation.outfhndl, 0, Lb_FILE_SEEK_BEGINNING); + animation.header.frames--; + LbFileWrite(animation.outfhndl, &animation.header, sizeof(AnimFLIHeader)); + if ( LbFileClose(animation.outfhndl) == -1 ) { + ERRORLOG("Can't close movie file"); + return false; + } + animation.outfhndl = nullptr; + LbMemoryFree(animation.chunkdata); + animation.chunkdata=NULL; + animation.field_0 = 0; + return true; +} + +extern "C" TbBool anim_record_frame(unsigned char *screenbuf, unsigned char *palette) +{ + if ((animation.field_0 & 0x01)==0) { + return false; + } else if (!anim_format_matches(MyScreenWidth/pixel_size,MyScreenHeight/pixel_size,LbGraphicsScreenBPP())) { + return false; + } + return anim_make_next_frame(screenbuf, palette); +} + +extern "C" short anim_record() +{ + SYNCDBG(7,"Starting"); + static char finalname[255]; + if (LbGraphicsScreenBPP() != 8) { + ERRORLOG("Cannot record movie in non-8bit screen mode"); + return 0; + } + int idx; + for (idx=0; idx < 10000; idx++) { + sprintf(finalname, "%s/game%04d.flc","scrshots",idx); + if (LbFileExists(finalname)) { + continue; + } + return anim_open(finalname, 0, 0, MyScreenWidth/pixel_size,MyScreenHeight/pixel_size,8, 1); + } + ERRORLOG("No free file name for recorded movie"); + return 0; +} diff --git a/src/bflib_fmvids.h b/src/bflib_fmvids.h index 4f5f23e3e1..cce8f3da55 100644 --- a/src/bflib_fmvids.h +++ b/src/bflib_fmvids.h @@ -21,160 +21,28 @@ #define BFLIB_FMVIDS_H #include "bflib_basics.h" -#include "globals.h" #ifdef __cplusplus extern "C" { #endif -/******************************************************************************/ + enum SmackerPlayFlags { - SMK_NoStopOnUserInput = 0x02, - SMK_PixelDoubleLine = 0x04, - SMK_InterlaceLine = 0x08, - SMK_WriteStatusFile = 0x40, - SMK_PixelDoubleWidth = 0x80, + SMK_NoSound = 0x01, + SMK_NoStopOnUserInput = 0x02, // unused? + SMK_PixelDoubleLine = 0x04, // unused? + SMK_InterlaceLine = 0x08, // unused? SMK_FullscreenFit = 0x10, SMK_FullscreenStretch = 0x20, SMK_FullscreenCrop = 0x40, + SMK_PixelDoubleWidth = 0x80, // unused? + SMK_WriteStatusFile = 0x100, // was 0x40, unused? }; -/******************************************************************************/ -#pragma pack(1) - -// Type definitions -struct SmackTag { - unsigned long Version; // SMK2 only right now - unsigned long Width; // Width (1 based, 640 for example) - unsigned long Height; // Height (1 based, 480 for example) - unsigned long Frames; // Number of frames (1 based, 100 = 100 frames) - unsigned long MSPerFrame; // Frame Rate - unsigned long SmackerType; // bit 0 set=ring frame - unsigned long LargestInTrack[7]; // Largest single size for each track - unsigned long tablesize; // Size of the init tables - unsigned long codesize; // Compression info - unsigned long absize; // ditto - unsigned long detailsize; // ditto - unsigned long typesize; // ditto - unsigned long TrackType[7]; // high byte=0x80-Comp,0x40-PCM data,0x20-16 bit,0x10-stereo - unsigned long extra; // extra value (should be zero) - unsigned long NewPalette; // set to one if the palette changed - unsigned char Palette[772]; // palette data - unsigned long PalType; // type of palette - unsigned long FrameNum; // Frame Number to be displayed - unsigned long FrameSize; // The current frame's size in bytes - unsigned long SndSize; // The current frame sound tracks' size in bytes - long LastRectx; // Rect set in from SmackToBufferRect (X coord) - long LastRecty; // Rect set in from SmackToBufferRect (Y coord) - long LastRectw; // Rect set in from SmackToBufferRect (Width) - long LastRecth; // Rect set in from SmackToBufferRect (Height) - unsigned long OpenFlags; // flags used on open - unsigned long LeftOfs; // Left Offset used in SmackTo - unsigned long TopOfs; // Top Offset used in SmackTo - unsigned long LargestFrameSize; // Largest frame size - unsigned long Highest1SecRate; // Highest 1 sec data rate - unsigned long Highest1SecFrame; // Highest 1 sec data rate starting frame - unsigned long ReadError; // Set to non-zero if a read error has ocurred - unsigned long addr32; // translated address for 16 bit interface -}; - -struct SmackSumTag { - unsigned long TotalTime; // total time - unsigned long MS100PerFrame; // MS*100 per frame (100000/MS100PerFrame=Frames/Sec) - unsigned long TotalOpenTime; // Time to open and prepare for decompression - unsigned long TotalFrames; // Total Frames displayed - unsigned long SkippedFrames; // Total number of skipped frames - unsigned long SoundSkips; // Total number of sound skips - unsigned long TotalBlitTime; // Total time spent blitting - unsigned long TotalReadTime; // Total time spent reading - unsigned long TotalDecompTime; // Total time spent decompressing - unsigned long TotalBackReadTime; // Total time spent reading in background - unsigned long TotalReadSpeed; // Total io speed (bytes/second) - unsigned long SlowestFrameTime; // Slowest single frame time - unsigned long Slowest2FrameTime; // Second slowest single frame time - unsigned long SlowestFrameNum; // Slowest single frame number - unsigned long Slowest2FrameNum; // Second slowest single frame number - unsigned long AverageFrameSize; // Average size of the frame - unsigned long Highest1SecRate; - unsigned long Highest1SecFrame; - unsigned long HighestMemAmount; // Highest amount of memory allocated - unsigned long TotalExtraMemory; // Total extra memory allocated - unsigned long HighestExtraUsed; // Highest extra memory actually used -}; - -struct AnimFLIHeader { // sizeof=0x80 - unsigned long dsize; - unsigned short magic; - unsigned short frames; - short width; - short height; - unsigned short depth; - unsigned short flags; - unsigned long speed; - short reserved2; - unsigned long created; - unsigned long creator; - unsigned long updated; - unsigned long updater; - short aspectx; - short aspecty; - char reserved3[38]; - unsigned long oframe1; - unsigned long oframe2; - char reserved4[40]; -}; - -struct AnimFLIChunk { //sizeof=0x6 - long csize; - unsigned short ctype; -}; - -struct AnimFLIPrefix { //sizeof=0x6 - long csize; - unsigned short ctype; - short nchunks; - char reserved[8]; -}; - -struct Animation { // sizeof=0x3D0 -long field_0; - unsigned char *videobuf; - unsigned char *chunkdata; - unsigned char *field_C; - TbFileHandle inpfhndl; - TbFileHandle outfhndl; -short field_18; -short field_1A; - unsigned char palette[768]; -long field_31C; -long field_320; -long field_324; - struct AnimFLIHeader header; - struct AnimFLIChunk chunk; - struct AnimFLIPrefix prefix; - struct AnimFLIChunk subchunk; -char field_3C4[12]; -}; - -typedef void (*SmackDrawCallback)(unsigned char *frame_data, long width, long height); - -/******************************************************************************/ - - - -#pragma pack() -/******************************************************************************/ -// Exported functions - SMK related -short play_smk_(char *fname, int smkflags, int plyflags); -short play_smk_direct(char *fname, int smkflags, int plyflags); -short play_smk_via_buffer(char *fname, int smkflags, int plyflags); - -// Exported functions - FLI related -short anim_open(char *fname, int arg1, short arg2, int width, int height, int arg5, unsigned int flags); +TbBool play_smk(const char * filename, int flags); short anim_stop(void); short anim_record(void); -TbBool anim_record_frame(unsigned char *screenbuf, unsigned char *palette); +TbBool anim_record_frame(unsigned char * screenbuf, unsigned char * palette); -/******************************************************************************/ #ifdef __cplusplus } #endif diff --git a/src/config.c b/src/config.c index 62d7d37892..74b4fbf235 100644 --- a/src/config.c +++ b/src/config.c @@ -34,6 +34,7 @@ #include "bflib_sound.h" #include "sounds.h" #include "engine_render.h" +#include "bflib_fmvids.h" #include "config_campaigns.h" #include "front_simple.h" @@ -167,25 +168,25 @@ const struct NamedCommand logicval_type[] = { }; const struct NamedCommand vidscale_type[] = { - {"OFF", 256}, // = 0x100 = No scaling of Smacker Video - {"DISABLED", 256}, - {"FALSE", 256}, - {"NO", 256}, - {"0", 256}, - {"FIT", 16}, // = 0x10 = SMK_FullscreenFit - fit to fullscreen, using letterbox and pillarbox as necessary - {"ON", 16}, // Duplicate of FIT, for legacy reasons - {"ENABLED", 16}, - {"TRUE", 16}, - {"YES", 16}, - {"1", 16}, - {"STRETCH", 32}, // = 0x20 = SMK_FullscreenStretch - stretch to fullscreen - ignores aspect ratio difference between source and destination - {"CROP", 64}, // = 0x40 = SMK_FullscreenCrop - fill fullscreen and crop - no letterbox or pillarbox - {"4BY3", 48}, // = 0x10 & 0x20 = [Aspect Ratio correction mode] - stretch 320x200 to 4:3 (i.e. increase height by 1.2) - {"PIXELPERFECT", 80}, // = 0x10 & 0x40 = integer multiple scale only (FIT) - {"4BY3PP", 112}, // = 0x10 & 0x20 & 0x40 = integer multiple scale only (4BY3) - {NULL, 0}, + {"OFF", 0}, // No scaling of Smacker Video + {"DISABLED", 0}, + {"FALSE", 0}, + {"NO", 0}, + {"0", 0}, + {"FIT", SMK_FullscreenFit}, // Fit to fullscreen, using letterbox and pillarbox as necessary + {"ON", SMK_FullscreenFit}, // Duplicate of FIT, for legacy reasons + {"ENABLED", SMK_FullscreenFit}, + {"TRUE", SMK_FullscreenFit}, + {"YES", SMK_FullscreenFit}, + {"1", SMK_FullscreenFit}, + {"STRETCH", SMK_FullscreenStretch}, // Stretch to fullscreen - ignores aspect ratio difference between source and destination + {"CROP", SMK_FullscreenCrop}, // Fill fullscreen and crop - no letterbox or pillarbox + {"4BY3", SMK_FullscreenFit | SMK_FullscreenStretch}, // [Aspect Ratio correction mode] - stretch 320x200 to 4:3 (i.e. increase height by 1.2) + {"PIXELPERFECT", SMK_FullscreenFit | SMK_FullscreenCrop}, // integer multiple scale only (FIT) + {"4BY3PP", SMK_FullscreenFit | SMK_FullscreenStretch | SMK_FullscreenCrop}, // integer multiple scale only (4BY3) + {NULL, 0}, }; -unsigned int vid_scale_flags = 0; +unsigned int vid_scale_flags = SMK_FullscreenFit; unsigned long features_enabled = 0; /** Line number, used when loading text files. */ @@ -1177,12 +1178,12 @@ short load_configuration(void) break; case 14: // Resize Movies i = recognize_conf_parameter(buf,&pos,len,vidscale_type); - if (i <= 0 || i > 256) + if (i < 0) { CONFWRNLOG("Couldn't recognize \"%s\" command parameter in %s file.",COMMAND_TEXT(cmd_num),config_textname); break; } - if (i < 256) { + if (i > 0) { features_enabled |= Ft_Resizemovies; vid_scale_flags = i; } diff --git a/src/front_fmvids.c b/src/front_fmvids.c index 1b38759b50..0e8a8fe41a 100644 --- a/src/front_fmvids.c +++ b/src/front_fmvids.c @@ -68,7 +68,7 @@ short play_smacker_file(char *filename, FrontendMenuState nstate) movie_flags |= vid_scale_flags; // get new scaling settings from command line } if ( SoundDisabled ) - movie_flags |= 0x01; + movie_flags |= SMK_NoSound; short result = 1; if ((result)&&(nstate>-2)) @@ -86,8 +86,7 @@ short play_smacker_file(char *filename, FrontendMenuState nstate) } if (result) { - // Fail in playing shouldn't set result=0, because result=0 means fatal error. - if (play_smk_(filename, 0, movie_flags | 0x100) == 0) + if (!play_smk(filename, movie_flags)) { ERRORLOG("Smacker play error"); result=0; diff --git a/src/thread.hpp b/src/thread.hpp new file mode 100644 index 0000000000..58c039d71d --- /dev/null +++ b/src/thread.hpp @@ -0,0 +1,30 @@ +#ifndef _DK_THREAD +#define _DK_THREAD + +#include + +#if defined(__MINGW32__) && !defined(_GLIBCXX_HAS_GTHREADS) + +// MingW32 10 and lower are broken, 13 (and higher?) work out of the box. +// Implementation below is sufficient for our needs. + +#define WIN32_LEAN_AND_MEAN +#include +#include +#include + +namespace std { +namespace this_thread { + +template +void sleep_for(const chrono::duration & rel_time) +{ + Sleep(std::chrono::duration_cast(rel_time).count()); +} + +}; +}; + +#endif + +#endif