From c9ff2b334bbc8351e08171e0daec571ee192ea13 Mon Sep 17 00:00:00 2001 From: Stefanos Kornilios Mitsis Poiitidis Date: Thu, 20 Sep 2018 04:11:41 +0200 Subject: [PATCH 001/319] Docs: Add new release schedule and structure See #1381 for the initial proposal --- docs/Release schedule and structure.md | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) create mode 100644 docs/Release schedule and structure.md diff --git a/docs/Release schedule and structure.md b/docs/Release schedule and structure.md new file mode 100644 index 000000000..9e384e02c --- /dev/null +++ b/docs/Release schedule and structure.md @@ -0,0 +1,26 @@ +Release Naming +--- +Our release names are time-based and follow a `yy.mm(.respin)` notation. +Eg `Release 18.10` should happen in October of 2018. +Hotfix/respins are denoted by an increasing trailing number if needed. Eg `Release 18.10.1`, `Release 18.10.2` and so on. + +Release Schedule +--- +We aim for a release on the first Monday of each month. This is manual right now, but ideally, it will be automated. +Monday was selected because emulator use peaks on the weekends, so we have 5 days to fix things up/rollback if a bad release happens. + +Release Planning +--- +We have milestones for 3 releases ahead, then [Mid Term Goals](https://github.com/reicast/reicast-emulator/milestone/4) for 3-9 months ahead, and [Long Term Goals](https://github.com/reicast/reicast-emulator/milestone/2) for 9+ months ahead. + +Tickets are assigned to milestones based on a combination of feature planning and developer availability. + +Feature "Freeze" Windows +--- +We do a soft "feature freeze" on the week before release, to allow for the beta builds to be tested. +During this window, experimental/untested changes should not be merged. + +Release Testing / QA +--- +We depend on the public beta and people complaining right now. +If you're interested to do QA testing around releases, please let us know in #1225, and/or join our [discord](http://chat.reicast.com) From b58e866b6b44dbc490a989827e4fb4b67dcf613b Mon Sep 17 00:00:00 2001 From: David Miller Date: Mon, 20 Aug 2018 23:31:37 -0400 Subject: [PATCH 002/319] Desktop command args support fixes and auto git versioning for VS and make --- core/cfg/cfg.h | 2 +- core/cfg/cl.cpp | 40 +- core/linux-dist/main.cpp | 5 + core/windows/winmain.cpp | 48 +- shell/linux/Makefile | 8 +- shell/reicast.vcxproj | 52 +- shell/reicast.vcxproj.filters | 1921 +++++++++++++++++---------------- 7 files changed, 1087 insertions(+), 989 deletions(-) diff --git a/core/cfg/cfg.h b/core/cfg/cfg.h index b4bef336b..3bf04755b 100644 --- a/core/cfg/cfg.h +++ b/core/cfg/cfg.h @@ -1,7 +1,7 @@ #pragma once #include "types.h" - +#include "version.h" /* ** cfg* prototypes, if you pass NULL to a cfgSave* it will wipe out the section ** } if you pass it to lpKey it will wipe out that particular entry diff --git a/core/cfg/cl.cpp b/core/cfg/cl.cpp index df6b6d180..2eb90fa78 100644 --- a/core/cfg/cl.cpp +++ b/core/cfg/cl.cpp @@ -6,8 +6,12 @@ */ #include -#include #include +#include + +#ifndef linux +#include +#endif #include "cfg/cfg.h" @@ -101,27 +105,45 @@ int setconfig(wchar** arg,int cl) int showhelp(wchar** arg,int cl) { - printf("Available commands :\n"); + printf("\nAvailable commands :\n"); - printf("-config section:key=value [, ..]: add a virtual config value\n Virtual config values won't be saved to the .cfg file\n unless a different value is written to em\nNote :\n You can specify many settings in the xx:yy=zz , gg:hh=jj , ...\n format.The spaces between the values and ',' are needed."); + printf("-config section:key=value [, ..]: add a virtual config value\n Virtual config values won't be saved to the .cfg file\n unless a different value is written to em\nNote :\n You can specify many settings in the xx:yy=zz , gg:hh=jj , ...\n format.The spaces between the values and ',' are needed.\n"); + printf("\n-help: show help info\n"); + printf("\n-version: show current version #\n\n"); +#if !defined(DEF_CONSOLE) && !defined(linux) + getch(); +#endif return 0; } + +int showversion(wchar** arg,int cl) +{ + printf("\nReicast Version: # %s built on %s \n", REICAST_VERSION, __DATE__); + +#if !defined(DEF_CONSOLE) && !defined(linux) + getch(); +#endif + return 0; +} + bool ParseCommandLine(int argc,wchar* argv[]) { - int cl=argc-2; wchar** arg=argv+1; while(cl>=0) { - if (stricmp(*arg,"-help")==0) + if (stricmp(*arg,"-help")==0 || stricmp(*arg,"--help")==0) { - int as=showhelp(arg,cl); - cl-=as; - arg+=as; + showhelp(arg,cl); return true; } - else if (stricmp(*arg,"-config")==0) + if (stricmp(*arg,"-version")==0 || stricmp(*arg,"--version")==0) + { + showversion(arg,cl); + return true; + } + else if (stricmp(*arg,"-config")==0 || stricmp(*arg,"--config")==0) { int as=setconfig(arg,cl); cl-=as; diff --git a/core/linux-dist/main.cpp b/core/linux-dist/main.cpp index 698f7d2fd..15ed5bea3 100644 --- a/core/linux-dist/main.cpp +++ b/core/linux-dist/main.cpp @@ -381,6 +381,11 @@ int main(int argc, wchar* argv[]) signal(SIGKILL, clean_exit); #endif + + if(ParseCommandLine(argc,argv)) { + return 69; + } + /* Set directories */ set_user_config_dir(find_user_config_dir()); set_user_data_dir(find_user_data_dir()); diff --git a/core/windows/winmain.cpp b/core/windows/winmain.cpp index 95f47f6c2..49613d709 100644 --- a/core/windows/winmain.cpp +++ b/core/windows/winmain.cpp @@ -1,6 +1,8 @@ #include "oslib\oslib.h" #include "oslib\audiostream.h" #include "imgread\common.h" +#include "stdclass.h" +#include "cfg/cfg.h" #define _WIN32_WINNT 0x0500 #include @@ -152,7 +154,7 @@ LONG ExeptionHandler(EXCEPTION_POINTERS *ExceptionInfo) #endif else { - printf("[GPF]Unhandled access to : 0x%X\n",address); + printf("[GPF]Unhandled access to : 0x%X\n",(unat)address); } return EXCEPTION_CONTINUE_SEARCH; @@ -446,7 +448,7 @@ void os_consume(double t) if ((cycl_glob+cyc)<10*1000*1000) { - InterlockedExchangeAdd(&cycl_glob,cyc); + InterlockedExchangeAdd(&cycl_glob,(u64)cyc); } else { @@ -667,12 +669,23 @@ void setup_seh() { } #endif -int CALLBACK WinMain(HINSTANCE hInstance,HINSTANCE hPrevInstance,LPSTR lpCmdLine,int nCmdShowCmd) + + + +// DEF_CONSOLE allows you to override linker subsystem and therefore default console // +// : pragma isn't pretty but def's are configurable +#ifdef DEF_CONSOLE +#pragma comment(linker, "/subsystem:console") + +int main(int argc, char **argv) { - ReserveBottomMemory(); - tick_thd.Start(); +#else +#pragma comment(linker, "/subsystem:windows") +int CALLBACK WinMain(HINSTANCE hInstance,HINSTANCE hPrevInstance,LPSTR lpCmdLine,int nCmdShowCmd) + +{ int argc=0; wchar* cmd_line=GetCommandLineA(); wchar** argv=CommandLineToArgvA(cmd_line,&argc); @@ -687,6 +700,15 @@ int CALLBACK WinMain(HINSTANCE hInstance,HINSTANCE hPrevInstance,LPSTR lpCmdLine SetConsoleCtrlHandler( (PHANDLER_ROUTINE) CtrlHandler, TRUE ); } +#endif + + + if(ParseCommandLine(argc,argv)) { + return 69; + } + + ReserveBottomMemory(); + tick_thd.Start(); SetupPath(); //SetUnhandledExceptionFilter(&ExeptionHandler); @@ -695,14 +717,14 @@ int CALLBACK WinMain(HINSTANCE hInstance,HINSTANCE hPrevInstance,LPSTR lpCmdLine int dc_init(int argc,wchar* argv[]); void dc_run(); void dc_term(); - dc_init(argc,argv); - - #ifdef _WIN64 - setup_seh(); - #endif - - dc_run(); - dc_term(); + if (0 == dc_init(argc, argv)) + { +#ifdef _WIN64 + setup_seh(); +#endif + dc_run(); + dc_term(); + } } __except( ExeptionHandler(GetExceptionInformation()) ) { diff --git a/shell/linux/Makefile b/shell/linux/Makefile index 1854955a6..aac6efb4d 100644 --- a/shell/linux/Makefile +++ b/shell/linux/Makefile @@ -370,7 +370,11 @@ ifneq (,$(findstring gcwz,$(platform))) mksquashfs $(EXECUTABLE_STRIPPED) $(GCWZ_PKG_FILES) $(GCWZ_PKG) -all-root endif -$(EXECUTABLE): $(OBJECTS) +VERSION_HEADER=$(RZDCY_SRC_DIR)/version.h +$(VERSION_HEADER): + echo \#define REICAST_VERSION \"$(shell git describe)\" >$(RZDCY_SRC_DIR)/version.h + +$(EXECUTABLE): $(VERSION_HEADER) $(OBJECTS) $(CXX) $(MFLAGS) $(EXTRAFLAGS) $(LDFLAGS) $(OBJECTS) $(LIBS) -o $@ $(EXECUTABLE_STRIPPED): $(EXECUTABLE) @@ -422,4 +426,4 @@ uninstall: rm -f $(DESTDIR)$(ICON_DIR)/reicast.png clean: - rm -f $(OBJECTS) $(EXECUTABLE) $(EXECUTABLE_STRIPPED) .map + rm -f $(VERSION_HEADER) $(OBJECTS) $(EXECUTABLE) $(EXECUTABLE_STRIPPED) .map diff --git a/shell/reicast.vcxproj b/shell/reicast.vcxproj index f597f3930..e139e3449 100644 --- a/shell/reicast.vcxproj +++ b/shell/reicast.vcxproj @@ -1,4 +1,4 @@ - + @@ -337,6 +337,7 @@ + @@ -359,6 +360,7 @@ Win32Proj reicast reicast + 10.0.16299.0 @@ -459,6 +461,16 @@ $(SolutionDir)..\pvrframe Dsound.lib;winmm.lib;wsock32.lib;comctl32.lib;%(AdditionalDependencies) + + + + + + for /f "delims=" %%i in ('git describe') do echo #define REICAST_VERSION "%%i" >$(ProjectDir)\..\core\version.h + + + Setting Version git describe >$(ProjectDir)\version.txt && set r_ver=%%i && + @@ -466,7 +478,7 @@ Level3 Full - WIN32;NDEBUG;_CONSOLE;X86;%(PreprocessorDefinitions) + TARGET_NO_WEBUI;WIN32;NDEBUG;_CONSOLE;X86;%(PreprocessorDefinitions) $(ProjectDir)..\core\;$(ProjectDir)..\core\khronos;%(AdditionalIncludeDirectories) /MP %(AdditionalOptions) AnySuitable @@ -493,6 +505,16 @@ $(SolutionDir)..\pvrframe Dsound.lib;winmm.lib;wsock32.lib;comctl32.lib;%(AdditionalDependencies) + + + + + + for /f "delims=" %%i in ('git describe') do echo #define REICAST_VERSION "%%i" >$(ProjectDir)\..\core\version.h + + + Setting Version git describe >$(ProjectDir)\version.txt && set r_ver=%%i && + @@ -500,7 +522,7 @@ Level3 Disabled - WIN32;_DEBUG;_CONSOLE;X86;%(PreprocessorDefinitions) + DEF_CONSOLE;TARGET_NO_WEBUI;WIN32;_DEBUG;_CONSOLE;X86;%(PreprocessorDefinitions) $(ProjectDir)..\core\;$(ProjectDir)..\core\khronos;%(AdditionalIncludeDirectories) true false @@ -514,6 +536,16 @@ $(SolutionDir)..\pvrframe Dsound.lib;winmm.lib;wsock32.lib;comctl32.lib;%(AdditionalDependencies) + + + + + + for /f "delims=" %%i in ('git describe') do echo #define REICAST_VERSION "%%i" >$(ProjectDir)\..\core\version.h + + + Setting Version git describe >$(ProjectDir)\version.txt && set r_ver=%%i && + @@ -521,7 +553,7 @@ Level3 Disabled - WIN32;_DEBUG;_CONSOLE;X86;%(PreprocessorDefinitions) + TARGET_NO_WEBUI;WIN32;_DEBUG;_CONSOLE;X86;%(PreprocessorDefinitions) $(ProjectDir)..\core\;$(ProjectDir)..\core\khronos;%(AdditionalIncludeDirectories) true false @@ -537,7 +569,19 @@ true $(SolutionDir)..\pvrframe Dsound.lib;winmm.lib;wsock32.lib;comctl32.lib;%(AdditionalDependencies) + + + + + + + + for /f "delims=" %%i in ('git describe') do echo #define REICAST_VERSION "%%i" >$(ProjectDir)\..\core\version.h + + + Setting Version git describe >$(ProjectDir)\version.txt && set r_ver=%%i && + diff --git a/shell/reicast.vcxproj.filters b/shell/reicast.vcxproj.filters index 32fa5f00e..f64836100 100644 --- a/shell/reicast.vcxproj.filters +++ b/shell/reicast.vcxproj.filters @@ -1,961 +1,962 @@ - - - - - hw\aica - - - hw\aica - - - hw\aica - - - hw\aica - - - hw\aica - - - hw\arm7 - - - hw\arm7 - - - hw\arm7 - - - hw\arm7 - - - hw\gdrom - - - hw\gdrom - - - hw\maple - - - hw\maple - - - hw\maple - - - hw\maple - - - hw\mem - - - hw\sh4 - - - hw\pvr - - - hw\pvr - - - deps\zlib - - - deps\zlib - - - deps\zlib - - - deps\zlib - - - deps\zlib - - - deps\zlib - - - deps\zlib - - - deps\zlib - - - deps\zlib - - - deps\zlib - - - deps\zlib - - - deps\crypto - - - deps\crypto - - - deps\chdr - - - imgread - - - imgread - - - imgread - - - imgread - - - imgread - - - imgread - - - - - cfg - - - cfg - - - hw\pvr\emu - - - emitter - - - windows - - - profiler - - - hw\holly - - - hw\holly - - - hw\holly - - - hw\holly - - - hw\sh4 - - - oslib - - - hw\sh4\modules - - - hw\sh4\modules - - - hw\sh4\modules - - - hw\sh4\modules - - - hw\sh4\modules - - - hw\sh4\modules - - - hw\sh4\modules - - - hw\sh4\modules - - - hw\sh4\dyna - - - hw\sh4\dyna - - - hw\sh4\dyna - - - hw\sh4\dyna - - - hw\sh4\interpr - - - hw\sh4\interpr - - - hw\sh4\interpr - - - hw\sh4 - - - hw\sh4\modules - - - hw\sh4\modules - - - hw\sh4 - - - hw\sh4 - - - hw\sh4 - - - hw\sh4 - - - deps\libelf - - - deps\libelf - - - deps\libelf - - - linux - - - linux - - - deps\chdpsr - - - deps\libpng - - - deps\libpng - - - deps\libpng - - - deps\libpng - - - deps\libpng - - - deps\libpng - - - deps\libpng - - - deps\libpng - - - deps\libpng - - - deps\libpng - - - deps\libpng - - - deps\libpng - - - deps\libpng - - - deps\libpng - - - deps\libpng - - - hw\pvr\emu\regs - - - hw\pvr\emu\regs - - - hw\pvr\emu\ta - - - hw\pvr\emu\tathd - - - rend\gles - - - rend\gles - - - rend\gles - - - hw\pvr\cntx - - - rend - - - hw\pvr\emu - - - rend\d3d11 - - - deps\libwebsocket - - - deps\libwebsocket - - - deps\libwebsocket - - - deps\libwebsocket - - - deps\libwebsocket - - - deps\libwebsocket - - - deps\libwebsocket - - - deps\libwebsocket - - - deps\libwebsocket - - - deps\libwebsocket - - - deps\libwebsocket - - - deps\libwebsocket - - - deps\libwebsocket - - - deps\libwebsocket - - - deps\libwebsocket - - - deps\libwebsocket - - - deps\libwebsocket - - - deps\libwebsocket - - - deps\libwebsocket - - - webui - - - deps\coreio - - - reios - - - reios - - - reios - - - reios - - - linux - - - oslib - - - rec-ARM - - - rec-x86 - - - rec-x86 - - - rec-x86 - - - rec-x64 - - - rec-cpp - - - rend\soft - - - hw\naomi - - - hw\naomi - - - cfg - - - - - {2099cae6-67fb-489f-b082-7555e8330705} - - - {70620682-2709-4701-b0a7-7d8f8e1571de} - - - {9659b21d-ccee-4d2b-973d-09e447be743e} - - - {a07a09a2-a585-4144-a0d0-c54d4bb442ec} - - - {9da5f3a4-2e41-4f94-a499-e2524c4dd0c2} - - - {c3e9aa73-a90e-4f37-b357-b0378fb1feff} - - - {65dd7d80-739d-4b5c-a01c-3262cb8ec97a} - - - {da5bfaf9-fca7-4397-9034-e201c1f38e49} - - - {ee2a1c0f-d38c-4d2b-bb07-ba6841fc5832} - - - {09e0d071-e3c1-4549-b2a8-52f24f4691a8} - - - {d0252230-b46c-424f-8aa8-037774915f81} - - - {5cdde132-a201-4bbc-9dd4-2ba3b3637a19} - - - {bd80604a-c634-44b3-a729-811bbefd3f71} - - - {8783a652-88e4-49bf-be90-bc36abe6753b} - - - {2e4fe5a7-a86c-45cf-b456-39b107a91bc7} - - - {7e03a227-4e9d-4727-a474-6aacf1e48642} - - - {369d7f53-be71-4055-a303-675f1132b118} - - - {e14356dc-6635-49f9-94d5-dc14ff1dec70} - - - {f96b3c39-1255-4ee8-999e-5c6e8fef21e5} - - - {b81a858a-7327-4eb4-bc6b-6ae24e2c08fd} - - - {755fe7c9-b6b5-42e5-b0e6-d1d02d5a6e03} - - - {2bbf43fd-2127-412f-bd76-6260b04522f8} - - - {be756ece-25e8-4a69-b90e-2601fdbb42fe} - - - {66246039-9de4-4bc0-88a9-94582e2713e0} - - - {3ef102e4-a05f-4774-b2d7-7e1529bfd9b1} - - - {82948f1f-819b-4a2d-9e07-72dfbf3e96ca} - - - {fe073008-ffba-43c1-9192-daec4b48148e} - - - {fa363b78-585a-476a-9afc-628b0f6650cf} - - - {7a950feb-a22e-4407-805a-3eb4a22a9c3d} - - - {874a26cd-41bf-42c8-8815-16df557afea0} - - - {480ee015-fc98-45e5-a759-87ff8fc3ee85} - - - {8636de32-e2de-49b1-84d5-85e4c06de52f} - - - {3f5c03ee-36db-4818-b0d2-4eec9c084f75} - - - {cd2c89fd-7a5b-43c8-a940-6ea0d29e9b5d} - - - {cc05f61b-c484-40e6-9859-6ca0cd64735c} - - - {3d3de3ff-9e79-4920-a95a-61d190e73004} - - - {81193efc-656a-4154-9adf-146856d4c7d3} - - - {23cfa286-fe88-439d-89de-a6a5a23cacc9} - - - {f614dd66-5d30-4548-a209-f857f95fb505} - - - {df854851-d3b5-4549-8248-acdfa954be44} - - - {5a7b63eb-8c03-46ac-b6e0-dfd3ade02f11} - - - {f73263e9-dbe8-4a6f-8b73-335af8307551} - - - {63d1fcf2-64b4-4973-995f-cd471f51117c} - - - {6c4b2d69-54c0-4660-9969-a98fd0339a15} - - - {1752487d-0739-47bf-8c6b-1d38e6f389f7} - - - - - hw\aica - - - hw\aica - - - hw\aica - - - hw\aica - - - hw\aica - - - hw\arm7 - - - hw\arm7 - - - hw\arm7 - - - hw\arm7 - - - hw\arm7 - - - hw\gdrom - - - hw\gdrom - - - hw\maple - - - hw\maple - - - hw\maple - - - hw\maple - - - hw\mem - - - hw\sh4 - - - hw\sh4 - - - hw\sh4 - - - hw\pvr - - - hw\pvr - - - hw\pvr - - - hw\pvr - - - deps\zlib - - - deps\zlib - - - deps\zlib - - - deps\zlib - - - deps\zlib - - - deps\zlib - - - deps\zlib - - - deps\zlib - - - deps\zlib - - - deps\zlib - - - deps\zlib - - - deps\crypto - - - deps\crypto - - - deps\chdr - - - deps\chdr - - - imgread - - - imgread - - - imgread - - - imgread - - - imgread - - - imgread - - - oslib - - - - - cfg - - - - hw - - - hw\pvr\emu - - - emitter - - - emitter - - - emitter - - - emitter - - - emitter - - - emitter - - - emitter - - - emitter - - - emitter - - - emitter - - - profiler - - - hw\holly - - - hw\holly - - - hw\flashrom - - - hw\holly - - - hw\sh4 - - - oslib - - - hw\sh4\modules - - - hw\sh4\modules - - - hw\sh4\modules - - - hw\sh4\modules - - - hw\sh4\dyna - - - hw\sh4\dyna - - - hw\sh4\dyna - - - hw\sh4\dyna - - - hw\sh4\dyna - - - hw\sh4\dyna - - - hw\sh4\dyna - - - hw\sh4\dyna - - - hw\sh4\interpr - - - hw\sh4\modules - - - hw\sh4 - - - hw\sh4 - - - hw\sh4 - - - hw\sh4 - - - hw\sh4 - - - hw\sh4 - - - deps\libelf - - - deps\libelf - - - deps\libelf - - - deps\libelf - - - linux - - - deps\chdpsr - - - deps\libpng - - - deps\libpng - - - deps\libpng - - - deps\libpng - - - hw\pvr\emu\regs - - - hw\pvr\emu\regs - - - hw\pvr\emu\ta - - - hw\pvr\emu\tathd - - - hw\pvr\emu\tathd - - - rend\gles - - - hw\pvr\cntx - - - rend - - - rend - - - hw\pvr\emu - - - deps\libwebsocket - - - deps\libwebsocket - - - deps\libwebsocket - - - deps\libwebsocket - - - deps\libwebsocket - - - webui - - - deps\coreio - - - reios - - - reios - - - reios - - - reios - - - linux - - - oslib - - - rec-x86 - - - hw\naomi - - - hw\naomi - - - hw\naomi - - - cfg - - - hw\sh4\modules - - - - - deps\zlib - - - rec-ARM - - - rec-x86 - - + + + + + hw\aica + + + hw\aica + + + hw\aica + + + hw\aica + + + hw\aica + + + hw\arm7 + + + hw\arm7 + + + hw\arm7 + + + hw\arm7 + + + hw\gdrom + + + hw\gdrom + + + hw\maple + + + hw\maple + + + hw\maple + + + hw\maple + + + hw\mem + + + hw\sh4 + + + hw\pvr + + + hw\pvr + + + deps\zlib + + + deps\zlib + + + deps\zlib + + + deps\zlib + + + deps\zlib + + + deps\zlib + + + deps\zlib + + + deps\zlib + + + deps\zlib + + + deps\zlib + + + deps\zlib + + + deps\crypto + + + deps\crypto + + + deps\chdr + + + imgread + + + imgread + + + imgread + + + imgread + + + imgread + + + imgread + + + + + cfg + + + cfg + + + hw\pvr\emu + + + emitter + + + windows + + + profiler + + + hw\holly + + + hw\holly + + + hw\holly + + + hw\holly + + + hw\sh4 + + + oslib + + + hw\sh4\modules + + + hw\sh4\modules + + + hw\sh4\modules + + + hw\sh4\modules + + + hw\sh4\modules + + + hw\sh4\modules + + + hw\sh4\modules + + + hw\sh4\modules + + + hw\sh4\dyna + + + hw\sh4\dyna + + + hw\sh4\dyna + + + hw\sh4\dyna + + + hw\sh4\interpr + + + hw\sh4\interpr + + + hw\sh4\interpr + + + hw\sh4 + + + hw\sh4\modules + + + hw\sh4\modules + + + hw\sh4 + + + hw\sh4 + + + hw\sh4 + + + hw\sh4 + + + deps\libelf + + + deps\libelf + + + deps\libelf + + + linux + + + linux + + + deps\chdpsr + + + deps\libpng + + + deps\libpng + + + deps\libpng + + + deps\libpng + + + deps\libpng + + + deps\libpng + + + deps\libpng + + + deps\libpng + + + deps\libpng + + + deps\libpng + + + deps\libpng + + + deps\libpng + + + deps\libpng + + + deps\libpng + + + deps\libpng + + + hw\pvr\emu\regs + + + hw\pvr\emu\regs + + + hw\pvr\emu\ta + + + hw\pvr\emu\tathd + + + rend\gles + + + rend\gles + + + rend\gles + + + hw\pvr\cntx + + + rend + + + hw\pvr\emu + + + rend\d3d11 + + + deps\libwebsocket + + + deps\libwebsocket + + + deps\libwebsocket + + + deps\libwebsocket + + + deps\libwebsocket + + + deps\libwebsocket + + + deps\libwebsocket + + + deps\libwebsocket + + + deps\libwebsocket + + + deps\libwebsocket + + + deps\libwebsocket + + + deps\libwebsocket + + + deps\libwebsocket + + + deps\libwebsocket + + + deps\libwebsocket + + + deps\libwebsocket + + + deps\libwebsocket + + + deps\libwebsocket + + + deps\libwebsocket + + + webui + + + deps\coreio + + + reios + + + reios + + + reios + + + reios + + + linux + + + oslib + + + rec-ARM + + + rec-x86 + + + rec-x86 + + + rec-x86 + + + rec-x64 + + + rec-cpp + + + rend\soft + + + hw\naomi + + + hw\naomi + + + cfg + + + + + {2099cae6-67fb-489f-b082-7555e8330705} + + + {70620682-2709-4701-b0a7-7d8f8e1571de} + + + {9659b21d-ccee-4d2b-973d-09e447be743e} + + + {a07a09a2-a585-4144-a0d0-c54d4bb442ec} + + + {9da5f3a4-2e41-4f94-a499-e2524c4dd0c2} + + + {c3e9aa73-a90e-4f37-b357-b0378fb1feff} + + + {65dd7d80-739d-4b5c-a01c-3262cb8ec97a} + + + {da5bfaf9-fca7-4397-9034-e201c1f38e49} + + + {ee2a1c0f-d38c-4d2b-bb07-ba6841fc5832} + + + {09e0d071-e3c1-4549-b2a8-52f24f4691a8} + + + {d0252230-b46c-424f-8aa8-037774915f81} + + + {5cdde132-a201-4bbc-9dd4-2ba3b3637a19} + + + {bd80604a-c634-44b3-a729-811bbefd3f71} + + + {8783a652-88e4-49bf-be90-bc36abe6753b} + + + {2e4fe5a7-a86c-45cf-b456-39b107a91bc7} + + + {7e03a227-4e9d-4727-a474-6aacf1e48642} + + + {369d7f53-be71-4055-a303-675f1132b118} + + + {e14356dc-6635-49f9-94d5-dc14ff1dec70} + + + {f96b3c39-1255-4ee8-999e-5c6e8fef21e5} + + + {b81a858a-7327-4eb4-bc6b-6ae24e2c08fd} + + + {755fe7c9-b6b5-42e5-b0e6-d1d02d5a6e03} + + + {2bbf43fd-2127-412f-bd76-6260b04522f8} + + + {be756ece-25e8-4a69-b90e-2601fdbb42fe} + + + {66246039-9de4-4bc0-88a9-94582e2713e0} + + + {3ef102e4-a05f-4774-b2d7-7e1529bfd9b1} + + + {82948f1f-819b-4a2d-9e07-72dfbf3e96ca} + + + {fe073008-ffba-43c1-9192-daec4b48148e} + + + {fa363b78-585a-476a-9afc-628b0f6650cf} + + + {7a950feb-a22e-4407-805a-3eb4a22a9c3d} + + + {874a26cd-41bf-42c8-8815-16df557afea0} + + + {480ee015-fc98-45e5-a759-87ff8fc3ee85} + + + {8636de32-e2de-49b1-84d5-85e4c06de52f} + + + {3f5c03ee-36db-4818-b0d2-4eec9c084f75} + + + {cd2c89fd-7a5b-43c8-a940-6ea0d29e9b5d} + + + {cc05f61b-c484-40e6-9859-6ca0cd64735c} + + + {3d3de3ff-9e79-4920-a95a-61d190e73004} + + + {81193efc-656a-4154-9adf-146856d4c7d3} + + + {23cfa286-fe88-439d-89de-a6a5a23cacc9} + + + {f614dd66-5d30-4548-a209-f857f95fb505} + + + {df854851-d3b5-4549-8248-acdfa954be44} + + + {5a7b63eb-8c03-46ac-b6e0-dfd3ade02f11} + + + {f73263e9-dbe8-4a6f-8b73-335af8307551} + + + {63d1fcf2-64b4-4973-995f-cd471f51117c} + + + {6c4b2d69-54c0-4660-9969-a98fd0339a15} + + + {1752487d-0739-47bf-8c6b-1d38e6f389f7} + + + + + hw\aica + + + hw\aica + + + hw\aica + + + hw\aica + + + hw\aica + + + hw\arm7 + + + hw\arm7 + + + hw\arm7 + + + hw\arm7 + + + hw\arm7 + + + hw\gdrom + + + hw\gdrom + + + hw\maple + + + hw\maple + + + hw\maple + + + hw\maple + + + hw\mem + + + hw\sh4 + + + hw\sh4 + + + hw\sh4 + + + hw\pvr + + + hw\pvr + + + hw\pvr + + + hw\pvr + + + deps\zlib + + + deps\zlib + + + deps\zlib + + + deps\zlib + + + deps\zlib + + + deps\zlib + + + deps\zlib + + + deps\zlib + + + deps\zlib + + + deps\zlib + + + deps\zlib + + + deps\crypto + + + deps\crypto + + + deps\chdr + + + deps\chdr + + + imgread + + + imgread + + + imgread + + + imgread + + + imgread + + + imgread + + + oslib + + + + + cfg + + + + hw + + + hw\pvr\emu + + + emitter + + + emitter + + + emitter + + + emitter + + + emitter + + + emitter + + + emitter + + + emitter + + + emitter + + + emitter + + + profiler + + + hw\holly + + + hw\holly + + + hw\flashrom + + + hw\holly + + + hw\sh4 + + + oslib + + + hw\sh4\modules + + + hw\sh4\modules + + + hw\sh4\modules + + + hw\sh4\modules + + + hw\sh4\dyna + + + hw\sh4\dyna + + + hw\sh4\dyna + + + hw\sh4\dyna + + + hw\sh4\dyna + + + hw\sh4\dyna + + + hw\sh4\dyna + + + hw\sh4\dyna + + + hw\sh4\interpr + + + hw\sh4\modules + + + hw\sh4 + + + hw\sh4 + + + hw\sh4 + + + hw\sh4 + + + hw\sh4 + + + hw\sh4 + + + deps\libelf + + + deps\libelf + + + deps\libelf + + + deps\libelf + + + linux + + + deps\chdpsr + + + deps\libpng + + + deps\libpng + + + deps\libpng + + + deps\libpng + + + hw\pvr\emu\regs + + + hw\pvr\emu\regs + + + hw\pvr\emu\ta + + + hw\pvr\emu\tathd + + + hw\pvr\emu\tathd + + + rend\gles + + + hw\pvr\cntx + + + rend + + + rend + + + hw\pvr\emu + + + deps\libwebsocket + + + deps\libwebsocket + + + deps\libwebsocket + + + deps\libwebsocket + + + deps\libwebsocket + + + webui + + + deps\coreio + + + reios + + + reios + + + reios + + + reios + + + linux + + + oslib + + + rec-x86 + + + hw\naomi + + + hw\naomi + + + hw\naomi + + + cfg + + + hw\sh4\modules + + + + + + deps\zlib + + + rec-ARM + + + rec-x86 + + \ No newline at end of file From 3dc16faa73d7b5b090f10ea4986e6c68d5bceed2 Mon Sep 17 00:00:00 2001 From: David Miller Date: Tue, 21 Aug 2018 00:59:36 -0400 Subject: [PATCH 003/319] Android build fix --- core/cfg/cfg.h | 1 - core/cfg/cl.cpp | 6 ++++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/core/cfg/cfg.h b/core/cfg/cfg.h index 3bf04755b..08d2ee939 100644 --- a/core/cfg/cfg.h +++ b/core/cfg/cfg.h @@ -1,7 +1,6 @@ #pragma once #include "types.h" -#include "version.h" /* ** cfg* prototypes, if you pass NULL to a cfgSave* it will wipe out the section ** } if you pass it to lpKey it will wipe out that particular entry diff --git a/core/cfg/cl.cpp b/core/cfg/cl.cpp index 2eb90fa78..44b6de381 100644 --- a/core/cfg/cl.cpp +++ b/core/cfg/cl.cpp @@ -117,6 +117,12 @@ int showhelp(wchar** arg,int cl) return 0; } +#ifndef _ANDROID +#include "version.h" +#else +#define REICAST_VERSION "r7-android-tmp" +#endif + int showversion(wchar** arg,int cl) { printf("\nReicast Version: # %s built on %s \n", REICAST_VERSION, __DATE__); From 2e23ee4628aefcf1b3ccf6c366a5440481ac9378 Mon Sep 17 00:00:00 2001 From: David Miller Date: Tue, 21 Aug 2018 12:19:38 -0400 Subject: [PATCH 004/319] use enum for cli arg exit and android build ndef --- core/cfg/cl.cpp | 2 +- core/linux-dist/main.cpp | 2 +- core/types.h | 7 ++++--- core/windows/winmain.cpp | 2 +- shell/reicast.vcxproj | 8 ++++---- 5 files changed, 11 insertions(+), 10 deletions(-) diff --git a/core/cfg/cl.cpp b/core/cfg/cl.cpp index 44b6de381..49ccf17e5 100644 --- a/core/cfg/cl.cpp +++ b/core/cfg/cl.cpp @@ -9,7 +9,7 @@ #include #include -#ifndef linux +#if !defined(linux) && !defined(_ANDROID) #include #endif diff --git a/core/linux-dist/main.cpp b/core/linux-dist/main.cpp index 15ed5bea3..5394e207f 100644 --- a/core/linux-dist/main.cpp +++ b/core/linux-dist/main.cpp @@ -383,7 +383,7 @@ int main(int argc, wchar* argv[]) if(ParseCommandLine(argc,argv)) { - return 69; + return rv_cli_finish; } /* Set directories */ diff --git a/core/types.h b/core/types.h index 6aa8a3c6c..cdfde6ce4 100644 --- a/core/types.h +++ b/core/types.h @@ -302,10 +302,11 @@ struct vram_block enum ndc_error_codes { - rv_ok = 0, //no error + rv_ok = 0, //no error + rv_cli_finish=69, //clean exit after -help or -version , should we just use rv_ok? - rv_error=-2, //error - rv_serror=-1, //silent error , it has been reported to the user + rv_error=-2, //error + rv_serror=-1, //silent error , it has been reported to the user }; //Simple struct to store window rect ;) diff --git a/core/windows/winmain.cpp b/core/windows/winmain.cpp index 49613d709..9a3c557e6 100644 --- a/core/windows/winmain.cpp +++ b/core/windows/winmain.cpp @@ -704,7 +704,7 @@ int CALLBACK WinMain(HINSTANCE hInstance,HINSTANCE hPrevInstance,LPSTR lpCmdLine if(ParseCommandLine(argc,argv)) { - return 69; + return rv_cli_finish; } ReserveBottomMemory(); diff --git a/shell/reicast.vcxproj b/shell/reicast.vcxproj index e139e3449..2acc87713 100644 --- a/shell/reicast.vcxproj +++ b/shell/reicast.vcxproj @@ -469,7 +469,7 @@ for /f "delims=" %%i in ('git describe') do echo #define REICAST_VERSION "%%i" >$(ProjectDir)\..\core\version.h - Setting Version git describe >$(ProjectDir)\version.txt && set r_ver=%%i && + Setting Version # in header @@ -513,7 +513,7 @@ for /f "delims=" %%i in ('git describe') do echo #define REICAST_VERSION "%%i" >$(ProjectDir)\..\core\version.h - Setting Version git describe >$(ProjectDir)\version.txt && set r_ver=%%i && + Setting Version # in header @@ -544,7 +544,7 @@ for /f "delims=" %%i in ('git describe') do echo #define REICAST_VERSION "%%i" >$(ProjectDir)\..\core\version.h - Setting Version git describe >$(ProjectDir)\version.txt && set r_ver=%%i && + Setting Version # in header @@ -580,7 +580,7 @@ for /f "delims=" %%i in ('git describe') do echo #define REICAST_VERSION "%%i" >$(ProjectDir)\..\core\version.h - Setting Version git describe >$(ProjectDir)\version.txt && set r_ver=%%i && + Setting Version # in header From 1722a9abe4b5dc001ec16b365f8b954f0142d4e5 Mon Sep 17 00:00:00 2001 From: David Miller Date: Tue, 21 Aug 2018 12:51:52 -0400 Subject: [PATCH 005/319] getchar for non win32, so curses isn't req to be setup --- core/cfg/cl.cpp | 48 +++++++++++++++++++++++++++++-------------- shell/reicast.vcxproj | 4 ++-- 2 files changed, 35 insertions(+), 17 deletions(-) diff --git a/core/cfg/cl.cpp b/core/cfg/cl.cpp index 49ccf17e5..52fc46ce5 100644 --- a/core/cfg/cl.cpp +++ b/core/cfg/cl.cpp @@ -9,10 +9,6 @@ #include #include -#if !defined(linux) && !defined(_ANDROID) -#include -#endif - #include "cfg/cfg.h" wchar* trim_ws(wchar* str) @@ -103,6 +99,37 @@ int setconfig(wchar** arg,int cl) return rv; } +#ifndef _ANDROID +#include "version.h" +#else +#define REICAST_VERSION "r7-android-tmp" +#endif + +#if !defined(DEF_CONSOLE) +#if defined(linux) || defined(_ANDROID) +#define DEF_CONSOLE +#elif defined(_WIN32) +#include +#endif +#endif + +void cli_pause() +{ +#ifdef DEF_CONSOLE + return; +#endif + +#if defined(_WIN32) + printf("\nPress a key to exit.\n"); + getch(); +#else + printf("\nPress enter to exit.\n"); + char c = getchar(); +#endif +} + + + int showhelp(wchar** arg,int cl) { printf("\nAvailable commands :\n"); @@ -111,25 +138,16 @@ int showhelp(wchar** arg,int cl) printf("\n-help: show help info\n"); printf("\n-version: show current version #\n\n"); -#if !defined(DEF_CONSOLE) && !defined(linux) - getch(); -#endif + cli_pause(); return 0; } -#ifndef _ANDROID -#include "version.h" -#else -#define REICAST_VERSION "r7-android-tmp" -#endif int showversion(wchar** arg,int cl) { printf("\nReicast Version: # %s built on %s \n", REICAST_VERSION, __DATE__); -#if !defined(DEF_CONSOLE) && !defined(linux) - getch(); -#endif + cli_pause(); return 0; } diff --git a/shell/reicast.vcxproj b/shell/reicast.vcxproj index 2acc87713..bb8e718e0 100644 --- a/shell/reicast.vcxproj +++ b/shell/reicast.vcxproj @@ -436,7 +436,7 @@ Level3 Full - WIN32;NDEBUG;_CONSOLE;X86;%(PreprocessorDefinitions) + TARGET_NO_WEBUI;WIN32;NDEBUG;_CONSOLE;X86;%(PreprocessorDefinitions) $(ProjectDir)..\core\;$(ProjectDir)..\core\khronos;%(AdditionalIncludeDirectories) /MP %(AdditionalOptions) AnySuitable @@ -553,7 +553,7 @@ Level3 Disabled - TARGET_NO_WEBUI;WIN32;_DEBUG;_CONSOLE;X86;%(PreprocessorDefinitions) + DEF_CONSOLE;TARGET_NO_WEBUI;WIN32;_DEBUG;_CONSOLE;X86;%(PreprocessorDefinitions) $(ProjectDir)..\core\;$(ProjectDir)..\core\khronos;%(AdditionalIncludeDirectories) true false From 9c11eae1b989f0f6359d04e6e5367c7a3fd34044 Mon Sep 17 00:00:00 2001 From: David Miller Date: Tue, 21 Aug 2018 14:08:08 -0400 Subject: [PATCH 006/319] VS Solution configurations multi targetting and appveyor build tests --- appveyor.yml | 8 +- shell/reicast.sln | 56 +++- shell/reicast.vcxproj | 690 +++++++++++++++++++++++++++++++++++++----- 3 files changed, 666 insertions(+), 88 deletions(-) diff --git a/appveyor.yml b/appveyor.yml index 478d96f90..977edcb18 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -1,8 +1,10 @@ version: git-{branch}-{build} -configuration: Debug +configuration: Debug-v120 + platform: -- x86 -- x64 + - x86 + - x64 + build: project: shell/reicast.sln parallel: true diff --git a/shell/reicast.sln b/shell/reicast.sln index fd9254716..a98cda12b 100644 --- a/shell/reicast.sln +++ b/shell/reicast.sln @@ -1,4 +1,4 @@ - + Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio 14 VisualStudioVersion = 14.0.24720.0 @@ -12,22 +12,52 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|x64 = Debug|x64 - Debug|x86 = Debug|x86 - Release|x64 = Release|x64 - Release|x86 = Release|x86 + Debug-120|x64 = Debug-120|x64 + Debug-120|x86 = Debug-120|x86 + Debug-140|x64 = Debug-140|x64 + Debug-140|x86 = Debug-140|x86 + Debug-141|x64 = Debug-141|x64 + Debug-141|x86 = Debug-141|x86 + Release-120|x64 = Release-120|x64 + Release-120|x86 = Release-120|x86 + Release-140|x64 = Release-140|x64 + Release-140|x86 = Release-140|x86 + Release-141|x64 = Release-141|x64 + Release-141|x86 = Release-141|x86 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution - {58B14048-EACB-4780-8B1E-9C84C2C30A8E}.Debug|x64.ActiveCfg = Slow|x64 - {58B14048-EACB-4780-8B1E-9C84C2C30A8E}.Debug|x64.Build.0 = Slow|x64 - {58B14048-EACB-4780-8B1E-9C84C2C30A8E}.Debug|x86.ActiveCfg = Slow|Win32 - {58B14048-EACB-4780-8B1E-9C84C2C30A8E}.Debug|x86.Build.0 = Slow|Win32 - {58B14048-EACB-4780-8B1E-9C84C2C30A8E}.Release|x64.ActiveCfg = Fast|x64 - {58B14048-EACB-4780-8B1E-9C84C2C30A8E}.Release|x64.Build.0 = Fast|x64 - {58B14048-EACB-4780-8B1E-9C84C2C30A8E}.Release|x86.ActiveCfg = Fast|Win32 - {58B14048-EACB-4780-8B1E-9C84C2C30A8E}.Release|x86.Build.0 = Fast|Win32 + {58B14048-EACB-4780-8B1E-9C84C2C30A8E}.Debug-120|x64.ActiveCfg = Debug-120|x64 + {58B14048-EACB-4780-8B1E-9C84C2C30A8E}.Debug-120|x64.Build.0 = Debug-120|x64 + {58B14048-EACB-4780-8B1E-9C84C2C30A8E}.Debug-120|x86.ActiveCfg = Debug-120|Win32 + {58B14048-EACB-4780-8B1E-9C84C2C30A8E}.Debug-120|x86.Build.0 = Debug-120|Win32 + {58B14048-EACB-4780-8B1E-9C84C2C30A8E}.Debug-140|x64.ActiveCfg = Debug-140|x64 + {58B14048-EACB-4780-8B1E-9C84C2C30A8E}.Debug-140|x64.Build.0 = Debug-140|x64 + {58B14048-EACB-4780-8B1E-9C84C2C30A8E}.Debug-140|x86.ActiveCfg = Debug-140|Win32 + {58B14048-EACB-4780-8B1E-9C84C2C30A8E}.Debug-140|x86.Build.0 = Debug-140|Win32 + {58B14048-EACB-4780-8B1E-9C84C2C30A8E}.Debug-141|x64.ActiveCfg = Debug-141|x64 + {58B14048-EACB-4780-8B1E-9C84C2C30A8E}.Debug-141|x64.Build.0 = Debug-141|x64 + {58B14048-EACB-4780-8B1E-9C84C2C30A8E}.Debug-141|x86.ActiveCfg = Debug-141|Win32 + {58B14048-EACB-4780-8B1E-9C84C2C30A8E}.Debug-141|x86.Build.0 = Debug-141|Win32 + {58B14048-EACB-4780-8B1E-9C84C2C30A8E}.Release-120|x64.ActiveCfg = Release-120|x64 + {58B14048-EACB-4780-8B1E-9C84C2C30A8E}.Release-120|x64.Build.0 = Release-120|x64 + {58B14048-EACB-4780-8B1E-9C84C2C30A8E}.Release-120|x86.ActiveCfg = Release-120|Win32 + {58B14048-EACB-4780-8B1E-9C84C2C30A8E}.Release-120|x86.Build.0 = Release-120|Win32 + {58B14048-EACB-4780-8B1E-9C84C2C30A8E}.Release-140|x64.ActiveCfg = Release-140|x64 + {58B14048-EACB-4780-8B1E-9C84C2C30A8E}.Release-140|x64.Build.0 = Release-140|x64 + {58B14048-EACB-4780-8B1E-9C84C2C30A8E}.Release-140|x86.ActiveCfg = Release-140|Win32 + {58B14048-EACB-4780-8B1E-9C84C2C30A8E}.Release-140|x86.Build.0 = Release-140|Win32 + {58B14048-EACB-4780-8B1E-9C84C2C30A8E}.Release-141|x64.ActiveCfg = Release-141|x64 + {58B14048-EACB-4780-8B1E-9C84C2C30A8E}.Release-141|x64.Build.0 = Release-141|x64 + {58B14048-EACB-4780-8B1E-9C84C2C30A8E}.Release-141|x86.ActiveCfg = Release-141|Win32 + {58B14048-EACB-4780-8B1E-9C84C2C30A8E}.Release-141|x86.Build.0 = Release-141|Win32 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {1879F430-B3B8-4D8B-8619-A03A8AB6C615} + EndGlobalSection + GlobalSection(Performance) = preSolution + HasPerformanceSessions = true + EndGlobalSection EndGlobal diff --git a/shell/reicast.vcxproj b/shell/reicast.vcxproj index bb8e718e0..d57dd5e11 100644 --- a/shell/reicast.vcxproj +++ b/shell/reicast.vcxproj @@ -1,20 +1,52 @@ - - Fast + + Debug-140 Win32 - - Fast + + Debug-140 x64 - - Slow + + Debug-141 Win32 - - Slow + + Debug-141 + x64 + + + Release-120 + Win32 + + + Release-120 + x64 + + + Release-140 + Win32 + + + Release-140 + x64 + + + Release-141 + Win32 + + + Release-141 + x64 + + + Debug-120 + Win32 + + + Debug-120 x64 @@ -138,49 +170,97 @@ - true - true - true - true + true + true + true + true + true + true + true + true + true + true + true + true - true - true - true - true + true + true + true + true + true + true + true + true + true + true + true + true - true - true - true - true + true + true + true + true + true + true + true + true + true + true + true + true - true - true - true - true + true + true + true + true + true + true + true + true + true + true + true + true - /bigobj %(AdditionalOptions) - /bigobj %(AdditionalOptions) + /bigobj %(AdditionalOptions) + /bigobj %(AdditionalOptions) + /bigobj %(AdditionalOptions) + /bigobj %(AdditionalOptions) + /bigobj %(AdditionalOptions) + /bigobj %(AdditionalOptions) - true - true + true + true + true + true + true + true - true - true + true + true + true + true + true + true - true - true + true + true + true + true + true + true @@ -309,24 +389,44 @@ - true - true - true - true + true + true + true + true + true + true + true + true + true + true + true + true - true - true - true - true + true + true + true + true + true + true + true + true + true + true + true + true - true - true + true + true + true + true + true + true @@ -343,16 +443,32 @@ - true - true - true - true + true + true + true + true + true + true + true + true + true + true + true + true - true - true - true - true + true + true + true + true + true + true + true + true + true + true + true + true @@ -360,30 +476,82 @@ Win32Proj reicast reicast - 10.0.16299.0 + 8.1 - + + Application + true + MultiByte + true + v120 + + Application true MultiByte true v140 - + Application true MultiByte true v140 - + + Application + true + MultiByte + true + v120 + + + Application + true + MultiByte + true + v140 + + + Application + true + MultiByte + true + v140 + + + Application + true + MultiByte + v120 + + Application true MultiByte v140 - + + Application + true + MultiByte + v140 + + + Application + true + MultiByte + v120 + + + Application + true + MultiByte + v140 + + Application true MultiByte @@ -393,44 +561,116 @@ - + - + - + - + + + + + + + + + + + + + + + + + + + + + + + + + - + true $(SolutionDir)..\WorkDir\ $(Platform)\$(Configuration)\generated\objs\ $(ProjectName)_$(Platform)_$(Configuration) - - true - $(SolutionDir)..\WorkDir\ - $(ProjectName)_$(Platform)_$(Configuration) - $(Platform)\$(Configuration)\generated\objs\ - - + true $(SolutionDir)..\WorkDir\ $(Platform)\$(Configuration)\generated\objs\ $(ProjectName)_$(Platform)_$(Configuration) - + + true + $(SolutionDir)..\WorkDir\ + $(Platform)\$(Configuration)\generated\objs\ + $(ProjectName)_$(Platform)_$(Configuration) + + true $(SolutionDir)..\WorkDir\ $(ProjectName)_$(Platform)_$(Configuration) $(Platform)\$(Configuration)\generated\objs\ - + + true + $(SolutionDir)..\WorkDir\ + $(ProjectName)_$(Platform)_$(Configuration) + $(Platform)\$(Configuration)\generated\objs\ + + + true + $(SolutionDir)..\WorkDir\ + $(ProjectName)_$(Platform)_$(Configuration) + $(Platform)\$(Configuration)\generated\objs\ + + + true + $(SolutionDir)..\WorkDir\ + $(Platform)\$(Configuration)\generated\objs\ + $(ProjectName)_$(Platform)_$(Configuration) + + + true + $(SolutionDir)..\WorkDir\ + $(Platform)\$(Configuration)\generated\objs\ + $(ProjectName)_$(Platform)_$(Configuration) + + + true + $(SolutionDir)..\WorkDir\ + $(Platform)\$(Configuration)\generated\objs\ + $(ProjectName)_$(Platform)_$(Configuration) + + + true + $(SolutionDir)..\WorkDir\ + $(ProjectName)_$(Platform)_$(Configuration) + $(Platform)\$(Configuration)\generated\objs\ + + + true + $(SolutionDir)..\WorkDir\ + $(ProjectName)_$(Platform)_$(Configuration) + $(Platform)\$(Configuration)\generated\objs\ + + + true + $(SolutionDir)..\WorkDir\ + $(ProjectName)_$(Platform)_$(Configuration) + $(Platform)\$(Configuration)\generated\objs\ + + @@ -472,7 +712,91 @@ Setting Version # in header - + + + + + Level3 + Full + TARGET_NO_WEBUI;WIN32;NDEBUG;_CONSOLE;X86;%(PreprocessorDefinitions) + $(ProjectDir)..\core\;$(ProjectDir)..\core\khronos;%(AdditionalIncludeDirectories) + /MP %(AdditionalOptions) + AnySuitable + true + Speed + true + true + Default + MultiThreadedDLL + false + StreamingSIMDExtensions + Fast + ProgramDatabase + true + false + $(IntDir)/%(RelativeDir)/ + true + + + Windows + true + $(SolutionDir)..\pvrframe + Dsound.lib;winmm.lib;wsock32.lib;comctl32.lib;%(AdditionalDependencies) + + + + + + + for /f "delims=" %%i in ('git describe') do echo #define REICAST_VERSION "%%i" >$(ProjectDir)\..\core\version.h + + + Setting Version # in header + + + + + + + Level3 + Full + TARGET_NO_WEBUI;WIN32;NDEBUG;_CONSOLE;X86;%(PreprocessorDefinitions) + $(ProjectDir)..\core\;$(ProjectDir)..\core\khronos;%(AdditionalIncludeDirectories) + /MP %(AdditionalOptions) + AnySuitable + true + Speed + true + true + Default + MultiThreadedDLL + false + StreamingSIMDExtensions + Fast + ProgramDatabase + true + false + $(IntDir)/%(RelativeDir)/ + true + + + Windows + true + $(SolutionDir)..\pvrframe + Dsound.lib;winmm.lib;wsock32.lib;comctl32.lib;%(AdditionalDependencies) + + + + + + + for /f "delims=" %%i in ('git describe') do echo #define REICAST_VERSION "%%i" >$(ProjectDir)\..\core\version.h + + + Setting Version # in header + + + @@ -516,7 +840,95 @@ Setting Version # in header - + + + + + Level3 + Full + TARGET_NO_WEBUI;WIN32;NDEBUG;_CONSOLE;X86;%(PreprocessorDefinitions) + $(ProjectDir)..\core\;$(ProjectDir)..\core\khronos;%(AdditionalIncludeDirectories) + /MP %(AdditionalOptions) + AnySuitable + true + Speed + true + true + Default + MultiThreadedDLL + false + StreamingSIMDExtensions + Fast + ProgramDatabase + true + false + $(IntDir)/%(RelativeDir)/ + + + true + + + Windows + true + $(SolutionDir)..\pvrframe + Dsound.lib;winmm.lib;wsock32.lib;comctl32.lib;%(AdditionalDependencies) + + + + + + + for /f "delims=" %%i in ('git describe') do echo #define REICAST_VERSION "%%i" >$(ProjectDir)\..\core\version.h + + + Setting Version # in header + + + + + + + Level3 + Full + TARGET_NO_WEBUI;WIN32;NDEBUG;_CONSOLE;X86;%(PreprocessorDefinitions) + $(ProjectDir)..\core\;$(ProjectDir)..\core\khronos;%(AdditionalIncludeDirectories) + /MP %(AdditionalOptions) + AnySuitable + true + Speed + true + true + Default + MultiThreadedDLL + false + StreamingSIMDExtensions + Fast + ProgramDatabase + true + false + $(IntDir)/%(RelativeDir)/ + + + true + + + Windows + true + $(SolutionDir)..\pvrframe + Dsound.lib;winmm.lib;wsock32.lib;comctl32.lib;%(AdditionalDependencies) + + + + + + + for /f "delims=" %%i in ('git describe') do echo #define REICAST_VERSION "%%i" >$(ProjectDir)\..\core\version.h + + + Setting Version # in header + + + @@ -547,7 +959,141 @@ Setting Version # in header - + + + + + Level3 + Disabled + DEF_CONSOLE;TARGET_NO_WEBUI;WIN32;_DEBUG;_CONSOLE;X86;%(PreprocessorDefinitions) + $(ProjectDir)..\core\;$(ProjectDir)..\core\khronos;%(AdditionalIncludeDirectories) + true + false + Default + $(IntDir)/%(RelativeDir)/ + true + + + Windows + true + $(SolutionDir)..\pvrframe + Dsound.lib;winmm.lib;wsock32.lib;comctl32.lib;%(AdditionalDependencies) + + + + + + + for /f "delims=" %%i in ('git describe') do echo #define REICAST_VERSION "%%i" >$(ProjectDir)\..\core\version.h + + + Setting Version # in header + + + + + + + Level3 + Disabled + DEF_CONSOLE;TARGET_NO_WEBUI;WIN32;_DEBUG;_CONSOLE;X86;%(PreprocessorDefinitions) + $(ProjectDir)..\core\;$(ProjectDir)..\core\khronos;%(AdditionalIncludeDirectories) + true + false + Default + $(IntDir)/%(RelativeDir)/ + true + + + Windows + true + $(SolutionDir)..\pvrframe + Dsound.lib;winmm.lib;wsock32.lib;comctl32.lib;%(AdditionalDependencies) + + + + + + + for /f "delims=" %%i in ('git describe') do echo #define REICAST_VERSION "%%i" >$(ProjectDir)\..\core\version.h + + + Setting Version # in header + + + + + + + Level3 + Disabled + DEF_CONSOLE;TARGET_NO_WEBUI;WIN32;_DEBUG;_CONSOLE;X86;%(PreprocessorDefinitions) + $(ProjectDir)..\core\;$(ProjectDir)..\core\khronos;%(AdditionalIncludeDirectories) + true + false + Default + $(IntDir)/%(RelativeDir)/ + + + EditAndContinue + true + + + Windows + true + $(SolutionDir)..\pvrframe + Dsound.lib;winmm.lib;wsock32.lib;comctl32.lib;%(AdditionalDependencies) + + + + + + + + + for /f "delims=" %%i in ('git describe') do echo #define REICAST_VERSION "%%i" >$(ProjectDir)\..\core\version.h + + + Setting Version # in header + + + + + + + Level3 + Disabled + DEF_CONSOLE;TARGET_NO_WEBUI;WIN32;_DEBUG;_CONSOLE;X86;%(PreprocessorDefinitions) + $(ProjectDir)..\core\;$(ProjectDir)..\core\khronos;%(AdditionalIncludeDirectories) + true + false + Default + $(IntDir)/%(RelativeDir)/ + + + EditAndContinue + true + + + Windows + true + $(SolutionDir)..\pvrframe + Dsound.lib;winmm.lib;wsock32.lib;comctl32.lib;%(AdditionalDependencies) + + + + + + + + + for /f "delims=" %%i in ('git describe') do echo #define REICAST_VERSION "%%i" >$(ProjectDir)\..\core\version.h + + + Setting Version # in header + + + From 88f97ae431053ae4c6d6654e8e1150f33e9b61ba Mon Sep 17 00:00:00 2001 From: Ender's Games Date: Thu, 20 Sep 2018 16:57:17 -0400 Subject: [PATCH 007/319] Specify OS and Toolset, Debug-120 config --- appveyor.yml | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/appveyor.yml b/appveyor.yml index 977edcb18..5d06f0ffa 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -1,9 +1,17 @@ version: git-{branch}-{build} -configuration: Debug-v120 +os: Visual Studio 2015 + +environment: + matrix: + - Toolset: v120 platform: - x86 - x64 + +configuration: +# - Release-120 + - Debug-120 build: project: shell/reicast.sln From 0adc6b2bce3a46f8bed8a3bef14abfbbcb413012 Mon Sep 17 00:00:00 2001 From: Ender's Games Date: Thu, 20 Sep 2018 17:47:00 -0400 Subject: [PATCH 008/319] Compatibility getch() for VS 2008/2013 --- core/cfg/cl.cpp | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/core/cfg/cl.cpp b/core/cfg/cl.cpp index 52fc46ce5..3f7af8e53 100644 --- a/core/cfg/cl.cpp +++ b/core/cfg/cl.cpp @@ -108,11 +108,13 @@ int setconfig(wchar** arg,int cl) #if !defined(DEF_CONSOLE) #if defined(linux) || defined(_ANDROID) #define DEF_CONSOLE -#elif defined(_WIN32) -#include #endif #endif +#if defined(_WIN32) +#include +#endif + void cli_pause() { #ifdef DEF_CONSOLE @@ -121,7 +123,7 @@ void cli_pause() #if defined(_WIN32) printf("\nPress a key to exit.\n"); - getch(); + _getch(); #else printf("\nPress enter to exit.\n"); char c = getchar(); From e498e79bba8d750e7fdf1d04225feeec10c27d3b Mon Sep 17 00:00:00 2001 From: Ender's Games Date: Thu, 20 Sep 2018 18:02:10 -0400 Subject: [PATCH 009/319] Appveyor: Get artifact for build configuration --- appveyor.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/appveyor.yml b/appveyor.yml index 5d06f0ffa..08beb57fb 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -21,7 +21,7 @@ build: after_build: - cmd: mkdir artifacts - cmd: set PLATFORM2=%PLATFORM:x86=Win32% - - cmd: move WorkDir\reicast_%PLATFORM2%_Slow.exe artifacts/reicast-win_%PLATFORM%-slow-%APPVEYOR_REPO_COMMIT%.exe + - cmd: move WorkDir\reicast_%PLATFORM2%_%CONFIGURATION%.exe artifacts/reicast-win_%PLATFORM%-slow-%APPVEYOR_REPO_COMMIT%.exe artifacts: - path: artifacts From 2026931f2e0a505e0dd81d1ac77ed58744ce9145 Mon Sep 17 00:00:00 2001 From: "Christoph \"baka0815\" Schwerdtfeger" Date: Sat, 22 Sep 2018 14:53:56 +0200 Subject: [PATCH 010/319] CHD: Imported https://github.com/rtissera/libchdr/commit/96a2ce8a5bf59a900e4e786a9f7e42b910f7c6e0 --- core/deps/chdr/bitstream.c | 125 ++ core/deps/chdr/bitstream.h | 43 + core/deps/chdr/cdrom.c | 412 ++++++ core/deps/chdr/cdrom.h | 109 ++ core/deps/chdr/chd.c | 2440 ++++++++++++++++++++++++++++++++++++ core/deps/chdr/chd.h | 116 +- core/deps/chdr/chdr.cpp | 1856 --------------------------- core/deps/chdr/coretypes.h | 42 +- core/deps/chdr/flac.c | 331 +++++ core/deps/chdr/flac.h | 51 + core/deps/chdr/huffman.c | 514 ++++++++ core/deps/chdr/huffman.h | 89 ++ 12 files changed, 4230 insertions(+), 1898 deletions(-) create mode 100644 core/deps/chdr/bitstream.c create mode 100644 core/deps/chdr/bitstream.h create mode 100644 core/deps/chdr/cdrom.c create mode 100644 core/deps/chdr/cdrom.h create mode 100644 core/deps/chdr/chd.c delete mode 100644 core/deps/chdr/chdr.cpp create mode 100644 core/deps/chdr/flac.c create mode 100644 core/deps/chdr/flac.h create mode 100644 core/deps/chdr/huffman.c create mode 100644 core/deps/chdr/huffman.h diff --git a/core/deps/chdr/bitstream.c b/core/deps/chdr/bitstream.c new file mode 100644 index 000000000..3f61c938c --- /dev/null +++ b/core/deps/chdr/bitstream.c @@ -0,0 +1,125 @@ +/* license:BSD-3-Clause + * copyright-holders:Aaron Giles +*************************************************************************** + + bitstream.c + + Helper classes for reading/writing at the bit level. + +***************************************************************************/ + +#include "bitstream.h" +#include + +/*************************************************************************** + * INLINE FUNCTIONS + *************************************************************************** + */ + +int bitstream_overflow(struct bitstream* bitstream) { return ((bitstream->doffset - bitstream->bits / 8) > bitstream->dlength); } + +/*------------------------------------------------- + * create_bitstream - constructor + *------------------------------------------------- + */ + +struct bitstream* create_bitstream(const void *src, uint32_t srclength) +{ + struct bitstream* bitstream = (struct bitstream*)malloc(sizeof(struct bitstream)); + bitstream->buffer = 0; + bitstream->bits = 0; + bitstream->read = (const uint8_t*)src; + bitstream->doffset = 0; + bitstream->dlength = srclength; + return bitstream; +} + + +/*----------------------------------------------------- + * bitstream_peek - fetch the requested number of bits + * but don't advance the input pointer + *----------------------------------------------------- + */ + +uint32_t bitstream_peek(struct bitstream* bitstream, int numbits) +{ + if (numbits == 0) + return 0; + + /* fetch data if we need more */ + if (numbits > bitstream->bits) + { + while (bitstream->bits <= 24) + { + if (bitstream->doffset < bitstream->dlength) + bitstream->buffer |= bitstream->read[bitstream->doffset] << (24 - bitstream->bits); + bitstream->doffset++; + bitstream->bits += 8; + } + } + + /* return the data */ + return bitstream->buffer >> (32 - numbits); +} + + +/*----------------------------------------------------- + * bitstream_remove - advance the input pointer by the + * specified number of bits + *----------------------------------------------------- + */ + +void bitstream_remove(struct bitstream* bitstream, int numbits) +{ + bitstream->buffer <<= numbits; + bitstream->bits -= numbits; +} + + +/*----------------------------------------------------- + * bitstream_read - fetch the requested number of bits + *----------------------------------------------------- + */ + +uint32_t bitstream_read(struct bitstream* bitstream, int numbits) +{ + uint32_t result = bitstream_peek(bitstream, numbits); + bitstream_remove(bitstream, numbits); + return result; +} + + +/*------------------------------------------------- + * read_offset - return the current read offset + *------------------------------------------------- + */ + +uint32_t bitstream_read_offset(struct bitstream* bitstream) +{ + uint32_t result = bitstream->doffset; + int bits = bitstream->bits; + while (bits >= 8) + { + result--; + bits -= 8; + } + return result; +} + + +/*------------------------------------------------- + * flush - flush to the nearest byte + *------------------------------------------------- + */ + +uint32_t bitstream_flush(struct bitstream* bitstream) +{ + while (bitstream->bits >= 8) + { + bitstream->doffset--; + bitstream->bits -= 8; + } + bitstream->bits = bitstream->buffer = 0; + return bitstream->doffset; +} + diff --git a/core/deps/chdr/bitstream.h b/core/deps/chdr/bitstream.h new file mode 100644 index 000000000..d376373b7 --- /dev/null +++ b/core/deps/chdr/bitstream.h @@ -0,0 +1,43 @@ +/* license:BSD-3-Clause + * copyright-holders:Aaron Giles +*************************************************************************** + + bitstream.h + + Helper classes for reading/writing at the bit level. + +***************************************************************************/ + +#pragma once + +#ifndef __BITSTREAM_H__ +#define __BITSTREAM_H__ + +#include + +/*************************************************************************** + * TYPE DEFINITIONS + *************************************************************************** + */ + +/* helper class for reading from a bit buffer */ +struct bitstream +{ + uint32_t buffer; /* current bit accumulator */ + int bits; /* number of bits in the accumulator */ + const uint8_t * read; /* read pointer */ + uint32_t doffset; /* byte offset within the data */ + uint32_t dlength; /* length of the data */ +}; + +struct bitstream* create_bitstream(const void *src, uint32_t srclength); +int bitstream_overflow(struct bitstream* bitstream); +uint32_t bitstream_read_offset(struct bitstream* bitstream); + +uint32_t bitstream_read(struct bitstream* bitstream, int numbits); +uint32_t bitstream_peek(struct bitstream* bitstream, int numbits); +void bitstream_remove(struct bitstream* bitstream, int numbits); +uint32_t bitstream_flush(struct bitstream* bitstream); + + +#endif diff --git a/core/deps/chdr/cdrom.c b/core/deps/chdr/cdrom.c new file mode 100644 index 000000000..74a0786d5 --- /dev/null +++ b/core/deps/chdr/cdrom.c @@ -0,0 +1,412 @@ +/* license:BSD-3-Clause + * copyright-holders:Aaron Giles +*************************************************************************** + + cdrom.c + + Generic MAME CD-ROM utilties - build IDE and SCSI CD-ROMs on top of this + +**************************************************************************** + + IMPORTANT: + "physical" block addresses are the actual addresses on the emulated CD. + "chd" block addresses are the block addresses in the CHD file. + Because we pad each track to a 4-frame boundary, these addressing + schemes will differ after track 1! + +***************************************************************************/ + +#include +#include + +#include "cdrom.h" + +/*************************************************************************** + DEBUGGING +***************************************************************************/ + +/** @brief The verbose. */ +#define VERBOSE (0) +#if VERBOSE + +/** + * @def LOG(x) do + * + * @brief A macro that defines log. + * + * @param x The void to process. + */ + +#define LOG(x) do { if (VERBOSE) logerror x; } while (0) + +/** + * @fn void CLIB_DECL logerror(const char *text, ...) ATTR_PRINTF(1,2); + * + * @brief Logerrors the given text. + * + * @param text The text. + * + * @return A CLIB_DECL. + */ + +void CLIB_DECL logerror(const char *text, ...) ATTR_PRINTF(1,2); +#else + +/** + * @def LOG(x); + * + * @brief A macro that defines log. + * + * @param x The void to process. + */ + +#define LOG(x) +#endif + +/*************************************************************************** + CONSTANTS +***************************************************************************/ + +/** @brief offset within sector. */ +#define SYNC_OFFSET 0x000 +/** @brief 12 bytes. */ +#define SYNC_NUM_BYTES 12 + +/** @brief offset within sector. */ +#define MODE_OFFSET 0x00f + +/** @brief offset within sector. */ +#define ECC_P_OFFSET 0x81c +/** @brief 2 lots of 86. */ +#define ECC_P_NUM_BYTES 86 +/** @brief 24 bytes each. */ +#define ECC_P_COMP 24 + +/** @brief The ECC q offset. */ +#define ECC_Q_OFFSET (ECC_P_OFFSET + 2 * ECC_P_NUM_BYTES) +/** @brief 2 lots of 52. */ +#define ECC_Q_NUM_BYTES 52 +/** @brief 43 bytes each. */ +#define ECC_Q_COMP 43 + +/** + * @brief ------------------------------------------------- + * ECC lookup tables pre-calculated tables for ECC data calcs + * -------------------------------------------------. + */ + +static const uint8_t ecclow[256] = +{ + 0x00, 0x02, 0x04, 0x06, 0x08, 0x0a, 0x0c, 0x0e, 0x10, 0x12, 0x14, 0x16, 0x18, 0x1a, 0x1c, 0x1e, + 0x20, 0x22, 0x24, 0x26, 0x28, 0x2a, 0x2c, 0x2e, 0x30, 0x32, 0x34, 0x36, 0x38, 0x3a, 0x3c, 0x3e, + 0x40, 0x42, 0x44, 0x46, 0x48, 0x4a, 0x4c, 0x4e, 0x50, 0x52, 0x54, 0x56, 0x58, 0x5a, 0x5c, 0x5e, + 0x60, 0x62, 0x64, 0x66, 0x68, 0x6a, 0x6c, 0x6e, 0x70, 0x72, 0x74, 0x76, 0x78, 0x7a, 0x7c, 0x7e, + 0x80, 0x82, 0x84, 0x86, 0x88, 0x8a, 0x8c, 0x8e, 0x90, 0x92, 0x94, 0x96, 0x98, 0x9a, 0x9c, 0x9e, + 0xa0, 0xa2, 0xa4, 0xa6, 0xa8, 0xaa, 0xac, 0xae, 0xb0, 0xb2, 0xb4, 0xb6, 0xb8, 0xba, 0xbc, 0xbe, + 0xc0, 0xc2, 0xc4, 0xc6, 0xc8, 0xca, 0xcc, 0xce, 0xd0, 0xd2, 0xd4, 0xd6, 0xd8, 0xda, 0xdc, 0xde, + 0xe0, 0xe2, 0xe4, 0xe6, 0xe8, 0xea, 0xec, 0xee, 0xf0, 0xf2, 0xf4, 0xf6, 0xf8, 0xfa, 0xfc, 0xfe, + 0x1d, 0x1f, 0x19, 0x1b, 0x15, 0x17, 0x11, 0x13, 0x0d, 0x0f, 0x09, 0x0b, 0x05, 0x07, 0x01, 0x03, + 0x3d, 0x3f, 0x39, 0x3b, 0x35, 0x37, 0x31, 0x33, 0x2d, 0x2f, 0x29, 0x2b, 0x25, 0x27, 0x21, 0x23, + 0x5d, 0x5f, 0x59, 0x5b, 0x55, 0x57, 0x51, 0x53, 0x4d, 0x4f, 0x49, 0x4b, 0x45, 0x47, 0x41, 0x43, + 0x7d, 0x7f, 0x79, 0x7b, 0x75, 0x77, 0x71, 0x73, 0x6d, 0x6f, 0x69, 0x6b, 0x65, 0x67, 0x61, 0x63, + 0x9d, 0x9f, 0x99, 0x9b, 0x95, 0x97, 0x91, 0x93, 0x8d, 0x8f, 0x89, 0x8b, 0x85, 0x87, 0x81, 0x83, + 0xbd, 0xbf, 0xb9, 0xbb, 0xb5, 0xb7, 0xb1, 0xb3, 0xad, 0xaf, 0xa9, 0xab, 0xa5, 0xa7, 0xa1, 0xa3, + 0xdd, 0xdf, 0xd9, 0xdb, 0xd5, 0xd7, 0xd1, 0xd3, 0xcd, 0xcf, 0xc9, 0xcb, 0xc5, 0xc7, 0xc1, 0xc3, + 0xfd, 0xff, 0xf9, 0xfb, 0xf5, 0xf7, 0xf1, 0xf3, 0xed, 0xef, 0xe9, 0xeb, 0xe5, 0xe7, 0xe1, 0xe3 +}; + +/** @brief The ecchigh[ 256]. */ +static const uint8_t ecchigh[256] = +{ + 0x00, 0xf4, 0xf5, 0x01, 0xf7, 0x03, 0x02, 0xf6, 0xf3, 0x07, 0x06, 0xf2, 0x04, 0xf0, 0xf1, 0x05, + 0xfb, 0x0f, 0x0e, 0xfa, 0x0c, 0xf8, 0xf9, 0x0d, 0x08, 0xfc, 0xfd, 0x09, 0xff, 0x0b, 0x0a, 0xfe, + 0xeb, 0x1f, 0x1e, 0xea, 0x1c, 0xe8, 0xe9, 0x1d, 0x18, 0xec, 0xed, 0x19, 0xef, 0x1b, 0x1a, 0xee, + 0x10, 0xe4, 0xe5, 0x11, 0xe7, 0x13, 0x12, 0xe6, 0xe3, 0x17, 0x16, 0xe2, 0x14, 0xe0, 0xe1, 0x15, + 0xcb, 0x3f, 0x3e, 0xca, 0x3c, 0xc8, 0xc9, 0x3d, 0x38, 0xcc, 0xcd, 0x39, 0xcf, 0x3b, 0x3a, 0xce, + 0x30, 0xc4, 0xc5, 0x31, 0xc7, 0x33, 0x32, 0xc6, 0xc3, 0x37, 0x36, 0xc2, 0x34, 0xc0, 0xc1, 0x35, + 0x20, 0xd4, 0xd5, 0x21, 0xd7, 0x23, 0x22, 0xd6, 0xd3, 0x27, 0x26, 0xd2, 0x24, 0xd0, 0xd1, 0x25, + 0xdb, 0x2f, 0x2e, 0xda, 0x2c, 0xd8, 0xd9, 0x2d, 0x28, 0xdc, 0xdd, 0x29, 0xdf, 0x2b, 0x2a, 0xde, + 0x8b, 0x7f, 0x7e, 0x8a, 0x7c, 0x88, 0x89, 0x7d, 0x78, 0x8c, 0x8d, 0x79, 0x8f, 0x7b, 0x7a, 0x8e, + 0x70, 0x84, 0x85, 0x71, 0x87, 0x73, 0x72, 0x86, 0x83, 0x77, 0x76, 0x82, 0x74, 0x80, 0x81, 0x75, + 0x60, 0x94, 0x95, 0x61, 0x97, 0x63, 0x62, 0x96, 0x93, 0x67, 0x66, 0x92, 0x64, 0x90, 0x91, 0x65, + 0x9b, 0x6f, 0x6e, 0x9a, 0x6c, 0x98, 0x99, 0x6d, 0x68, 0x9c, 0x9d, 0x69, 0x9f, 0x6b, 0x6a, 0x9e, + 0x40, 0xb4, 0xb5, 0x41, 0xb7, 0x43, 0x42, 0xb6, 0xb3, 0x47, 0x46, 0xb2, 0x44, 0xb0, 0xb1, 0x45, + 0xbb, 0x4f, 0x4e, 0xba, 0x4c, 0xb8, 0xb9, 0x4d, 0x48, 0xbc, 0xbd, 0x49, 0xbf, 0x4b, 0x4a, 0xbe, + 0xab, 0x5f, 0x5e, 0xaa, 0x5c, 0xa8, 0xa9, 0x5d, 0x58, 0xac, 0xad, 0x59, 0xaf, 0x5b, 0x5a, 0xae, + 0x50, 0xa4, 0xa5, 0x51, 0xa7, 0x53, 0x52, 0xa6, 0xa3, 0x57, 0x56, 0xa2, 0x54, 0xa0, 0xa1, 0x55 +}; + +/** + * @brief ------------------------------------------------- + * poffsets - each row represents the addresses used to calculate a byte of the ECC P + * data 86 (*2) ECC P bytes, 24 values represented by each + * -------------------------------------------------. + */ + +static const uint16_t poffsets[ECC_P_NUM_BYTES][ECC_P_COMP] = +{ + { 0x000,0x056,0x0ac,0x102,0x158,0x1ae,0x204,0x25a,0x2b0,0x306,0x35c,0x3b2,0x408,0x45e,0x4b4,0x50a,0x560,0x5b6,0x60c,0x662,0x6b8,0x70e,0x764,0x7ba }, + { 0x001,0x057,0x0ad,0x103,0x159,0x1af,0x205,0x25b,0x2b1,0x307,0x35d,0x3b3,0x409,0x45f,0x4b5,0x50b,0x561,0x5b7,0x60d,0x663,0x6b9,0x70f,0x765,0x7bb }, + { 0x002,0x058,0x0ae,0x104,0x15a,0x1b0,0x206,0x25c,0x2b2,0x308,0x35e,0x3b4,0x40a,0x460,0x4b6,0x50c,0x562,0x5b8,0x60e,0x664,0x6ba,0x710,0x766,0x7bc }, + { 0x003,0x059,0x0af,0x105,0x15b,0x1b1,0x207,0x25d,0x2b3,0x309,0x35f,0x3b5,0x40b,0x461,0x4b7,0x50d,0x563,0x5b9,0x60f,0x665,0x6bb,0x711,0x767,0x7bd }, + { 0x004,0x05a,0x0b0,0x106,0x15c,0x1b2,0x208,0x25e,0x2b4,0x30a,0x360,0x3b6,0x40c,0x462,0x4b8,0x50e,0x564,0x5ba,0x610,0x666,0x6bc,0x712,0x768,0x7be }, + { 0x005,0x05b,0x0b1,0x107,0x15d,0x1b3,0x209,0x25f,0x2b5,0x30b,0x361,0x3b7,0x40d,0x463,0x4b9,0x50f,0x565,0x5bb,0x611,0x667,0x6bd,0x713,0x769,0x7bf }, + { 0x006,0x05c,0x0b2,0x108,0x15e,0x1b4,0x20a,0x260,0x2b6,0x30c,0x362,0x3b8,0x40e,0x464,0x4ba,0x510,0x566,0x5bc,0x612,0x668,0x6be,0x714,0x76a,0x7c0 }, + { 0x007,0x05d,0x0b3,0x109,0x15f,0x1b5,0x20b,0x261,0x2b7,0x30d,0x363,0x3b9,0x40f,0x465,0x4bb,0x511,0x567,0x5bd,0x613,0x669,0x6bf,0x715,0x76b,0x7c1 }, + { 0x008,0x05e,0x0b4,0x10a,0x160,0x1b6,0x20c,0x262,0x2b8,0x30e,0x364,0x3ba,0x410,0x466,0x4bc,0x512,0x568,0x5be,0x614,0x66a,0x6c0,0x716,0x76c,0x7c2 }, + { 0x009,0x05f,0x0b5,0x10b,0x161,0x1b7,0x20d,0x263,0x2b9,0x30f,0x365,0x3bb,0x411,0x467,0x4bd,0x513,0x569,0x5bf,0x615,0x66b,0x6c1,0x717,0x76d,0x7c3 }, + { 0x00a,0x060,0x0b6,0x10c,0x162,0x1b8,0x20e,0x264,0x2ba,0x310,0x366,0x3bc,0x412,0x468,0x4be,0x514,0x56a,0x5c0,0x616,0x66c,0x6c2,0x718,0x76e,0x7c4 }, + { 0x00b,0x061,0x0b7,0x10d,0x163,0x1b9,0x20f,0x265,0x2bb,0x311,0x367,0x3bd,0x413,0x469,0x4bf,0x515,0x56b,0x5c1,0x617,0x66d,0x6c3,0x719,0x76f,0x7c5 }, + { 0x00c,0x062,0x0b8,0x10e,0x164,0x1ba,0x210,0x266,0x2bc,0x312,0x368,0x3be,0x414,0x46a,0x4c0,0x516,0x56c,0x5c2,0x618,0x66e,0x6c4,0x71a,0x770,0x7c6 }, + { 0x00d,0x063,0x0b9,0x10f,0x165,0x1bb,0x211,0x267,0x2bd,0x313,0x369,0x3bf,0x415,0x46b,0x4c1,0x517,0x56d,0x5c3,0x619,0x66f,0x6c5,0x71b,0x771,0x7c7 }, + { 0x00e,0x064,0x0ba,0x110,0x166,0x1bc,0x212,0x268,0x2be,0x314,0x36a,0x3c0,0x416,0x46c,0x4c2,0x518,0x56e,0x5c4,0x61a,0x670,0x6c6,0x71c,0x772,0x7c8 }, + { 0x00f,0x065,0x0bb,0x111,0x167,0x1bd,0x213,0x269,0x2bf,0x315,0x36b,0x3c1,0x417,0x46d,0x4c3,0x519,0x56f,0x5c5,0x61b,0x671,0x6c7,0x71d,0x773,0x7c9 }, + { 0x010,0x066,0x0bc,0x112,0x168,0x1be,0x214,0x26a,0x2c0,0x316,0x36c,0x3c2,0x418,0x46e,0x4c4,0x51a,0x570,0x5c6,0x61c,0x672,0x6c8,0x71e,0x774,0x7ca }, + { 0x011,0x067,0x0bd,0x113,0x169,0x1bf,0x215,0x26b,0x2c1,0x317,0x36d,0x3c3,0x419,0x46f,0x4c5,0x51b,0x571,0x5c7,0x61d,0x673,0x6c9,0x71f,0x775,0x7cb }, + { 0x012,0x068,0x0be,0x114,0x16a,0x1c0,0x216,0x26c,0x2c2,0x318,0x36e,0x3c4,0x41a,0x470,0x4c6,0x51c,0x572,0x5c8,0x61e,0x674,0x6ca,0x720,0x776,0x7cc }, + { 0x013,0x069,0x0bf,0x115,0x16b,0x1c1,0x217,0x26d,0x2c3,0x319,0x36f,0x3c5,0x41b,0x471,0x4c7,0x51d,0x573,0x5c9,0x61f,0x675,0x6cb,0x721,0x777,0x7cd }, + { 0x014,0x06a,0x0c0,0x116,0x16c,0x1c2,0x218,0x26e,0x2c4,0x31a,0x370,0x3c6,0x41c,0x472,0x4c8,0x51e,0x574,0x5ca,0x620,0x676,0x6cc,0x722,0x778,0x7ce }, + { 0x015,0x06b,0x0c1,0x117,0x16d,0x1c3,0x219,0x26f,0x2c5,0x31b,0x371,0x3c7,0x41d,0x473,0x4c9,0x51f,0x575,0x5cb,0x621,0x677,0x6cd,0x723,0x779,0x7cf }, + { 0x016,0x06c,0x0c2,0x118,0x16e,0x1c4,0x21a,0x270,0x2c6,0x31c,0x372,0x3c8,0x41e,0x474,0x4ca,0x520,0x576,0x5cc,0x622,0x678,0x6ce,0x724,0x77a,0x7d0 }, + { 0x017,0x06d,0x0c3,0x119,0x16f,0x1c5,0x21b,0x271,0x2c7,0x31d,0x373,0x3c9,0x41f,0x475,0x4cb,0x521,0x577,0x5cd,0x623,0x679,0x6cf,0x725,0x77b,0x7d1 }, + { 0x018,0x06e,0x0c4,0x11a,0x170,0x1c6,0x21c,0x272,0x2c8,0x31e,0x374,0x3ca,0x420,0x476,0x4cc,0x522,0x578,0x5ce,0x624,0x67a,0x6d0,0x726,0x77c,0x7d2 }, + { 0x019,0x06f,0x0c5,0x11b,0x171,0x1c7,0x21d,0x273,0x2c9,0x31f,0x375,0x3cb,0x421,0x477,0x4cd,0x523,0x579,0x5cf,0x625,0x67b,0x6d1,0x727,0x77d,0x7d3 }, + { 0x01a,0x070,0x0c6,0x11c,0x172,0x1c8,0x21e,0x274,0x2ca,0x320,0x376,0x3cc,0x422,0x478,0x4ce,0x524,0x57a,0x5d0,0x626,0x67c,0x6d2,0x728,0x77e,0x7d4 }, + { 0x01b,0x071,0x0c7,0x11d,0x173,0x1c9,0x21f,0x275,0x2cb,0x321,0x377,0x3cd,0x423,0x479,0x4cf,0x525,0x57b,0x5d1,0x627,0x67d,0x6d3,0x729,0x77f,0x7d5 }, + { 0x01c,0x072,0x0c8,0x11e,0x174,0x1ca,0x220,0x276,0x2cc,0x322,0x378,0x3ce,0x424,0x47a,0x4d0,0x526,0x57c,0x5d2,0x628,0x67e,0x6d4,0x72a,0x780,0x7d6 }, + { 0x01d,0x073,0x0c9,0x11f,0x175,0x1cb,0x221,0x277,0x2cd,0x323,0x379,0x3cf,0x425,0x47b,0x4d1,0x527,0x57d,0x5d3,0x629,0x67f,0x6d5,0x72b,0x781,0x7d7 }, + { 0x01e,0x074,0x0ca,0x120,0x176,0x1cc,0x222,0x278,0x2ce,0x324,0x37a,0x3d0,0x426,0x47c,0x4d2,0x528,0x57e,0x5d4,0x62a,0x680,0x6d6,0x72c,0x782,0x7d8 }, + { 0x01f,0x075,0x0cb,0x121,0x177,0x1cd,0x223,0x279,0x2cf,0x325,0x37b,0x3d1,0x427,0x47d,0x4d3,0x529,0x57f,0x5d5,0x62b,0x681,0x6d7,0x72d,0x783,0x7d9 }, + { 0x020,0x076,0x0cc,0x122,0x178,0x1ce,0x224,0x27a,0x2d0,0x326,0x37c,0x3d2,0x428,0x47e,0x4d4,0x52a,0x580,0x5d6,0x62c,0x682,0x6d8,0x72e,0x784,0x7da }, + { 0x021,0x077,0x0cd,0x123,0x179,0x1cf,0x225,0x27b,0x2d1,0x327,0x37d,0x3d3,0x429,0x47f,0x4d5,0x52b,0x581,0x5d7,0x62d,0x683,0x6d9,0x72f,0x785,0x7db }, + { 0x022,0x078,0x0ce,0x124,0x17a,0x1d0,0x226,0x27c,0x2d2,0x328,0x37e,0x3d4,0x42a,0x480,0x4d6,0x52c,0x582,0x5d8,0x62e,0x684,0x6da,0x730,0x786,0x7dc }, + { 0x023,0x079,0x0cf,0x125,0x17b,0x1d1,0x227,0x27d,0x2d3,0x329,0x37f,0x3d5,0x42b,0x481,0x4d7,0x52d,0x583,0x5d9,0x62f,0x685,0x6db,0x731,0x787,0x7dd }, + { 0x024,0x07a,0x0d0,0x126,0x17c,0x1d2,0x228,0x27e,0x2d4,0x32a,0x380,0x3d6,0x42c,0x482,0x4d8,0x52e,0x584,0x5da,0x630,0x686,0x6dc,0x732,0x788,0x7de }, + { 0x025,0x07b,0x0d1,0x127,0x17d,0x1d3,0x229,0x27f,0x2d5,0x32b,0x381,0x3d7,0x42d,0x483,0x4d9,0x52f,0x585,0x5db,0x631,0x687,0x6dd,0x733,0x789,0x7df }, + { 0x026,0x07c,0x0d2,0x128,0x17e,0x1d4,0x22a,0x280,0x2d6,0x32c,0x382,0x3d8,0x42e,0x484,0x4da,0x530,0x586,0x5dc,0x632,0x688,0x6de,0x734,0x78a,0x7e0 }, + { 0x027,0x07d,0x0d3,0x129,0x17f,0x1d5,0x22b,0x281,0x2d7,0x32d,0x383,0x3d9,0x42f,0x485,0x4db,0x531,0x587,0x5dd,0x633,0x689,0x6df,0x735,0x78b,0x7e1 }, + { 0x028,0x07e,0x0d4,0x12a,0x180,0x1d6,0x22c,0x282,0x2d8,0x32e,0x384,0x3da,0x430,0x486,0x4dc,0x532,0x588,0x5de,0x634,0x68a,0x6e0,0x736,0x78c,0x7e2 }, + { 0x029,0x07f,0x0d5,0x12b,0x181,0x1d7,0x22d,0x283,0x2d9,0x32f,0x385,0x3db,0x431,0x487,0x4dd,0x533,0x589,0x5df,0x635,0x68b,0x6e1,0x737,0x78d,0x7e3 }, + { 0x02a,0x080,0x0d6,0x12c,0x182,0x1d8,0x22e,0x284,0x2da,0x330,0x386,0x3dc,0x432,0x488,0x4de,0x534,0x58a,0x5e0,0x636,0x68c,0x6e2,0x738,0x78e,0x7e4 }, + { 0x02b,0x081,0x0d7,0x12d,0x183,0x1d9,0x22f,0x285,0x2db,0x331,0x387,0x3dd,0x433,0x489,0x4df,0x535,0x58b,0x5e1,0x637,0x68d,0x6e3,0x739,0x78f,0x7e5 }, + { 0x02c,0x082,0x0d8,0x12e,0x184,0x1da,0x230,0x286,0x2dc,0x332,0x388,0x3de,0x434,0x48a,0x4e0,0x536,0x58c,0x5e2,0x638,0x68e,0x6e4,0x73a,0x790,0x7e6 }, + { 0x02d,0x083,0x0d9,0x12f,0x185,0x1db,0x231,0x287,0x2dd,0x333,0x389,0x3df,0x435,0x48b,0x4e1,0x537,0x58d,0x5e3,0x639,0x68f,0x6e5,0x73b,0x791,0x7e7 }, + { 0x02e,0x084,0x0da,0x130,0x186,0x1dc,0x232,0x288,0x2de,0x334,0x38a,0x3e0,0x436,0x48c,0x4e2,0x538,0x58e,0x5e4,0x63a,0x690,0x6e6,0x73c,0x792,0x7e8 }, + { 0x02f,0x085,0x0db,0x131,0x187,0x1dd,0x233,0x289,0x2df,0x335,0x38b,0x3e1,0x437,0x48d,0x4e3,0x539,0x58f,0x5e5,0x63b,0x691,0x6e7,0x73d,0x793,0x7e9 }, + { 0x030,0x086,0x0dc,0x132,0x188,0x1de,0x234,0x28a,0x2e0,0x336,0x38c,0x3e2,0x438,0x48e,0x4e4,0x53a,0x590,0x5e6,0x63c,0x692,0x6e8,0x73e,0x794,0x7ea }, + { 0x031,0x087,0x0dd,0x133,0x189,0x1df,0x235,0x28b,0x2e1,0x337,0x38d,0x3e3,0x439,0x48f,0x4e5,0x53b,0x591,0x5e7,0x63d,0x693,0x6e9,0x73f,0x795,0x7eb }, + { 0x032,0x088,0x0de,0x134,0x18a,0x1e0,0x236,0x28c,0x2e2,0x338,0x38e,0x3e4,0x43a,0x490,0x4e6,0x53c,0x592,0x5e8,0x63e,0x694,0x6ea,0x740,0x796,0x7ec }, + { 0x033,0x089,0x0df,0x135,0x18b,0x1e1,0x237,0x28d,0x2e3,0x339,0x38f,0x3e5,0x43b,0x491,0x4e7,0x53d,0x593,0x5e9,0x63f,0x695,0x6eb,0x741,0x797,0x7ed }, + { 0x034,0x08a,0x0e0,0x136,0x18c,0x1e2,0x238,0x28e,0x2e4,0x33a,0x390,0x3e6,0x43c,0x492,0x4e8,0x53e,0x594,0x5ea,0x640,0x696,0x6ec,0x742,0x798,0x7ee }, + { 0x035,0x08b,0x0e1,0x137,0x18d,0x1e3,0x239,0x28f,0x2e5,0x33b,0x391,0x3e7,0x43d,0x493,0x4e9,0x53f,0x595,0x5eb,0x641,0x697,0x6ed,0x743,0x799,0x7ef }, + { 0x036,0x08c,0x0e2,0x138,0x18e,0x1e4,0x23a,0x290,0x2e6,0x33c,0x392,0x3e8,0x43e,0x494,0x4ea,0x540,0x596,0x5ec,0x642,0x698,0x6ee,0x744,0x79a,0x7f0 }, + { 0x037,0x08d,0x0e3,0x139,0x18f,0x1e5,0x23b,0x291,0x2e7,0x33d,0x393,0x3e9,0x43f,0x495,0x4eb,0x541,0x597,0x5ed,0x643,0x699,0x6ef,0x745,0x79b,0x7f1 }, + { 0x038,0x08e,0x0e4,0x13a,0x190,0x1e6,0x23c,0x292,0x2e8,0x33e,0x394,0x3ea,0x440,0x496,0x4ec,0x542,0x598,0x5ee,0x644,0x69a,0x6f0,0x746,0x79c,0x7f2 }, + { 0x039,0x08f,0x0e5,0x13b,0x191,0x1e7,0x23d,0x293,0x2e9,0x33f,0x395,0x3eb,0x441,0x497,0x4ed,0x543,0x599,0x5ef,0x645,0x69b,0x6f1,0x747,0x79d,0x7f3 }, + { 0x03a,0x090,0x0e6,0x13c,0x192,0x1e8,0x23e,0x294,0x2ea,0x340,0x396,0x3ec,0x442,0x498,0x4ee,0x544,0x59a,0x5f0,0x646,0x69c,0x6f2,0x748,0x79e,0x7f4 }, + { 0x03b,0x091,0x0e7,0x13d,0x193,0x1e9,0x23f,0x295,0x2eb,0x341,0x397,0x3ed,0x443,0x499,0x4ef,0x545,0x59b,0x5f1,0x647,0x69d,0x6f3,0x749,0x79f,0x7f5 }, + { 0x03c,0x092,0x0e8,0x13e,0x194,0x1ea,0x240,0x296,0x2ec,0x342,0x398,0x3ee,0x444,0x49a,0x4f0,0x546,0x59c,0x5f2,0x648,0x69e,0x6f4,0x74a,0x7a0,0x7f6 }, + { 0x03d,0x093,0x0e9,0x13f,0x195,0x1eb,0x241,0x297,0x2ed,0x343,0x399,0x3ef,0x445,0x49b,0x4f1,0x547,0x59d,0x5f3,0x649,0x69f,0x6f5,0x74b,0x7a1,0x7f7 }, + { 0x03e,0x094,0x0ea,0x140,0x196,0x1ec,0x242,0x298,0x2ee,0x344,0x39a,0x3f0,0x446,0x49c,0x4f2,0x548,0x59e,0x5f4,0x64a,0x6a0,0x6f6,0x74c,0x7a2,0x7f8 }, + { 0x03f,0x095,0x0eb,0x141,0x197,0x1ed,0x243,0x299,0x2ef,0x345,0x39b,0x3f1,0x447,0x49d,0x4f3,0x549,0x59f,0x5f5,0x64b,0x6a1,0x6f7,0x74d,0x7a3,0x7f9 }, + { 0x040,0x096,0x0ec,0x142,0x198,0x1ee,0x244,0x29a,0x2f0,0x346,0x39c,0x3f2,0x448,0x49e,0x4f4,0x54a,0x5a0,0x5f6,0x64c,0x6a2,0x6f8,0x74e,0x7a4,0x7fa }, + { 0x041,0x097,0x0ed,0x143,0x199,0x1ef,0x245,0x29b,0x2f1,0x347,0x39d,0x3f3,0x449,0x49f,0x4f5,0x54b,0x5a1,0x5f7,0x64d,0x6a3,0x6f9,0x74f,0x7a5,0x7fb }, + { 0x042,0x098,0x0ee,0x144,0x19a,0x1f0,0x246,0x29c,0x2f2,0x348,0x39e,0x3f4,0x44a,0x4a0,0x4f6,0x54c,0x5a2,0x5f8,0x64e,0x6a4,0x6fa,0x750,0x7a6,0x7fc }, + { 0x043,0x099,0x0ef,0x145,0x19b,0x1f1,0x247,0x29d,0x2f3,0x349,0x39f,0x3f5,0x44b,0x4a1,0x4f7,0x54d,0x5a3,0x5f9,0x64f,0x6a5,0x6fb,0x751,0x7a7,0x7fd }, + { 0x044,0x09a,0x0f0,0x146,0x19c,0x1f2,0x248,0x29e,0x2f4,0x34a,0x3a0,0x3f6,0x44c,0x4a2,0x4f8,0x54e,0x5a4,0x5fa,0x650,0x6a6,0x6fc,0x752,0x7a8,0x7fe }, + { 0x045,0x09b,0x0f1,0x147,0x19d,0x1f3,0x249,0x29f,0x2f5,0x34b,0x3a1,0x3f7,0x44d,0x4a3,0x4f9,0x54f,0x5a5,0x5fb,0x651,0x6a7,0x6fd,0x753,0x7a9,0x7ff }, + { 0x046,0x09c,0x0f2,0x148,0x19e,0x1f4,0x24a,0x2a0,0x2f6,0x34c,0x3a2,0x3f8,0x44e,0x4a4,0x4fa,0x550,0x5a6,0x5fc,0x652,0x6a8,0x6fe,0x754,0x7aa,0x800 }, + { 0x047,0x09d,0x0f3,0x149,0x19f,0x1f5,0x24b,0x2a1,0x2f7,0x34d,0x3a3,0x3f9,0x44f,0x4a5,0x4fb,0x551,0x5a7,0x5fd,0x653,0x6a9,0x6ff,0x755,0x7ab,0x801 }, + { 0x048,0x09e,0x0f4,0x14a,0x1a0,0x1f6,0x24c,0x2a2,0x2f8,0x34e,0x3a4,0x3fa,0x450,0x4a6,0x4fc,0x552,0x5a8,0x5fe,0x654,0x6aa,0x700,0x756,0x7ac,0x802 }, + { 0x049,0x09f,0x0f5,0x14b,0x1a1,0x1f7,0x24d,0x2a3,0x2f9,0x34f,0x3a5,0x3fb,0x451,0x4a7,0x4fd,0x553,0x5a9,0x5ff,0x655,0x6ab,0x701,0x757,0x7ad,0x803 }, + { 0x04a,0x0a0,0x0f6,0x14c,0x1a2,0x1f8,0x24e,0x2a4,0x2fa,0x350,0x3a6,0x3fc,0x452,0x4a8,0x4fe,0x554,0x5aa,0x600,0x656,0x6ac,0x702,0x758,0x7ae,0x804 }, + { 0x04b,0x0a1,0x0f7,0x14d,0x1a3,0x1f9,0x24f,0x2a5,0x2fb,0x351,0x3a7,0x3fd,0x453,0x4a9,0x4ff,0x555,0x5ab,0x601,0x657,0x6ad,0x703,0x759,0x7af,0x805 }, + { 0x04c,0x0a2,0x0f8,0x14e,0x1a4,0x1fa,0x250,0x2a6,0x2fc,0x352,0x3a8,0x3fe,0x454,0x4aa,0x500,0x556,0x5ac,0x602,0x658,0x6ae,0x704,0x75a,0x7b0,0x806 }, + { 0x04d,0x0a3,0x0f9,0x14f,0x1a5,0x1fb,0x251,0x2a7,0x2fd,0x353,0x3a9,0x3ff,0x455,0x4ab,0x501,0x557,0x5ad,0x603,0x659,0x6af,0x705,0x75b,0x7b1,0x807 }, + { 0x04e,0x0a4,0x0fa,0x150,0x1a6,0x1fc,0x252,0x2a8,0x2fe,0x354,0x3aa,0x400,0x456,0x4ac,0x502,0x558,0x5ae,0x604,0x65a,0x6b0,0x706,0x75c,0x7b2,0x808 }, + { 0x04f,0x0a5,0x0fb,0x151,0x1a7,0x1fd,0x253,0x2a9,0x2ff,0x355,0x3ab,0x401,0x457,0x4ad,0x503,0x559,0x5af,0x605,0x65b,0x6b1,0x707,0x75d,0x7b3,0x809 }, + { 0x050,0x0a6,0x0fc,0x152,0x1a8,0x1fe,0x254,0x2aa,0x300,0x356,0x3ac,0x402,0x458,0x4ae,0x504,0x55a,0x5b0,0x606,0x65c,0x6b2,0x708,0x75e,0x7b4,0x80a }, + { 0x051,0x0a7,0x0fd,0x153,0x1a9,0x1ff,0x255,0x2ab,0x301,0x357,0x3ad,0x403,0x459,0x4af,0x505,0x55b,0x5b1,0x607,0x65d,0x6b3,0x709,0x75f,0x7b5,0x80b }, + { 0x052,0x0a8,0x0fe,0x154,0x1aa,0x200,0x256,0x2ac,0x302,0x358,0x3ae,0x404,0x45a,0x4b0,0x506,0x55c,0x5b2,0x608,0x65e,0x6b4,0x70a,0x760,0x7b6,0x80c }, + { 0x053,0x0a9,0x0ff,0x155,0x1ab,0x201,0x257,0x2ad,0x303,0x359,0x3af,0x405,0x45b,0x4b1,0x507,0x55d,0x5b3,0x609,0x65f,0x6b5,0x70b,0x761,0x7b7,0x80d }, + { 0x054,0x0aa,0x100,0x156,0x1ac,0x202,0x258,0x2ae,0x304,0x35a,0x3b0,0x406,0x45c,0x4b2,0x508,0x55e,0x5b4,0x60a,0x660,0x6b6,0x70c,0x762,0x7b8,0x80e }, + { 0x055,0x0ab,0x101,0x157,0x1ad,0x203,0x259,0x2af,0x305,0x35b,0x3b1,0x407,0x45d,0x4b3,0x509,0x55f,0x5b5,0x60b,0x661,0x6b7,0x70d,0x763,0x7b9,0x80f } +}; + +/** + * @brief ------------------------------------------------- + * qoffsets - each row represents the addresses used to calculate a byte of the ECC Q + * data 52 (*2) ECC Q bytes, 43 values represented by each + * -------------------------------------------------. + */ + +static const uint16_t qoffsets[ECC_Q_NUM_BYTES][ECC_Q_COMP] = +{ + { 0x000,0x058,0x0b0,0x108,0x160,0x1b8,0x210,0x268,0x2c0,0x318,0x370,0x3c8,0x420,0x478,0x4d0,0x528,0x580,0x5d8,0x630,0x688,0x6e0,0x738,0x790,0x7e8,0x840,0x898,0x034,0x08c,0x0e4,0x13c,0x194,0x1ec,0x244,0x29c,0x2f4,0x34c,0x3a4,0x3fc,0x454,0x4ac,0x504,0x55c,0x5b4 }, + { 0x001,0x059,0x0b1,0x109,0x161,0x1b9,0x211,0x269,0x2c1,0x319,0x371,0x3c9,0x421,0x479,0x4d1,0x529,0x581,0x5d9,0x631,0x689,0x6e1,0x739,0x791,0x7e9,0x841,0x899,0x035,0x08d,0x0e5,0x13d,0x195,0x1ed,0x245,0x29d,0x2f5,0x34d,0x3a5,0x3fd,0x455,0x4ad,0x505,0x55d,0x5b5 }, + { 0x056,0x0ae,0x106,0x15e,0x1b6,0x20e,0x266,0x2be,0x316,0x36e,0x3c6,0x41e,0x476,0x4ce,0x526,0x57e,0x5d6,0x62e,0x686,0x6de,0x736,0x78e,0x7e6,0x83e,0x896,0x032,0x08a,0x0e2,0x13a,0x192,0x1ea,0x242,0x29a,0x2f2,0x34a,0x3a2,0x3fa,0x452,0x4aa,0x502,0x55a,0x5b2,0x60a }, + { 0x057,0x0af,0x107,0x15f,0x1b7,0x20f,0x267,0x2bf,0x317,0x36f,0x3c7,0x41f,0x477,0x4cf,0x527,0x57f,0x5d7,0x62f,0x687,0x6df,0x737,0x78f,0x7e7,0x83f,0x897,0x033,0x08b,0x0e3,0x13b,0x193,0x1eb,0x243,0x29b,0x2f3,0x34b,0x3a3,0x3fb,0x453,0x4ab,0x503,0x55b,0x5b3,0x60b }, + { 0x0ac,0x104,0x15c,0x1b4,0x20c,0x264,0x2bc,0x314,0x36c,0x3c4,0x41c,0x474,0x4cc,0x524,0x57c,0x5d4,0x62c,0x684,0x6dc,0x734,0x78c,0x7e4,0x83c,0x894,0x030,0x088,0x0e0,0x138,0x190,0x1e8,0x240,0x298,0x2f0,0x348,0x3a0,0x3f8,0x450,0x4a8,0x500,0x558,0x5b0,0x608,0x660 }, + { 0x0ad,0x105,0x15d,0x1b5,0x20d,0x265,0x2bd,0x315,0x36d,0x3c5,0x41d,0x475,0x4cd,0x525,0x57d,0x5d5,0x62d,0x685,0x6dd,0x735,0x78d,0x7e5,0x83d,0x895,0x031,0x089,0x0e1,0x139,0x191,0x1e9,0x241,0x299,0x2f1,0x349,0x3a1,0x3f9,0x451,0x4a9,0x501,0x559,0x5b1,0x609,0x661 }, + { 0x102,0x15a,0x1b2,0x20a,0x262,0x2ba,0x312,0x36a,0x3c2,0x41a,0x472,0x4ca,0x522,0x57a,0x5d2,0x62a,0x682,0x6da,0x732,0x78a,0x7e2,0x83a,0x892,0x02e,0x086,0x0de,0x136,0x18e,0x1e6,0x23e,0x296,0x2ee,0x346,0x39e,0x3f6,0x44e,0x4a6,0x4fe,0x556,0x5ae,0x606,0x65e,0x6b6 }, + { 0x103,0x15b,0x1b3,0x20b,0x263,0x2bb,0x313,0x36b,0x3c3,0x41b,0x473,0x4cb,0x523,0x57b,0x5d3,0x62b,0x683,0x6db,0x733,0x78b,0x7e3,0x83b,0x893,0x02f,0x087,0x0df,0x137,0x18f,0x1e7,0x23f,0x297,0x2ef,0x347,0x39f,0x3f7,0x44f,0x4a7,0x4ff,0x557,0x5af,0x607,0x65f,0x6b7 }, + { 0x158,0x1b0,0x208,0x260,0x2b8,0x310,0x368,0x3c0,0x418,0x470,0x4c8,0x520,0x578,0x5d0,0x628,0x680,0x6d8,0x730,0x788,0x7e0,0x838,0x890,0x02c,0x084,0x0dc,0x134,0x18c,0x1e4,0x23c,0x294,0x2ec,0x344,0x39c,0x3f4,0x44c,0x4a4,0x4fc,0x554,0x5ac,0x604,0x65c,0x6b4,0x70c }, + { 0x159,0x1b1,0x209,0x261,0x2b9,0x311,0x369,0x3c1,0x419,0x471,0x4c9,0x521,0x579,0x5d1,0x629,0x681,0x6d9,0x731,0x789,0x7e1,0x839,0x891,0x02d,0x085,0x0dd,0x135,0x18d,0x1e5,0x23d,0x295,0x2ed,0x345,0x39d,0x3f5,0x44d,0x4a5,0x4fd,0x555,0x5ad,0x605,0x65d,0x6b5,0x70d }, + { 0x1ae,0x206,0x25e,0x2b6,0x30e,0x366,0x3be,0x416,0x46e,0x4c6,0x51e,0x576,0x5ce,0x626,0x67e,0x6d6,0x72e,0x786,0x7de,0x836,0x88e,0x02a,0x082,0x0da,0x132,0x18a,0x1e2,0x23a,0x292,0x2ea,0x342,0x39a,0x3f2,0x44a,0x4a2,0x4fa,0x552,0x5aa,0x602,0x65a,0x6b2,0x70a,0x762 }, + { 0x1af,0x207,0x25f,0x2b7,0x30f,0x367,0x3bf,0x417,0x46f,0x4c7,0x51f,0x577,0x5cf,0x627,0x67f,0x6d7,0x72f,0x787,0x7df,0x837,0x88f,0x02b,0x083,0x0db,0x133,0x18b,0x1e3,0x23b,0x293,0x2eb,0x343,0x39b,0x3f3,0x44b,0x4a3,0x4fb,0x553,0x5ab,0x603,0x65b,0x6b3,0x70b,0x763 }, + { 0x204,0x25c,0x2b4,0x30c,0x364,0x3bc,0x414,0x46c,0x4c4,0x51c,0x574,0x5cc,0x624,0x67c,0x6d4,0x72c,0x784,0x7dc,0x834,0x88c,0x028,0x080,0x0d8,0x130,0x188,0x1e0,0x238,0x290,0x2e8,0x340,0x398,0x3f0,0x448,0x4a0,0x4f8,0x550,0x5a8,0x600,0x658,0x6b0,0x708,0x760,0x7b8 }, + { 0x205,0x25d,0x2b5,0x30d,0x365,0x3bd,0x415,0x46d,0x4c5,0x51d,0x575,0x5cd,0x625,0x67d,0x6d5,0x72d,0x785,0x7dd,0x835,0x88d,0x029,0x081,0x0d9,0x131,0x189,0x1e1,0x239,0x291,0x2e9,0x341,0x399,0x3f1,0x449,0x4a1,0x4f9,0x551,0x5a9,0x601,0x659,0x6b1,0x709,0x761,0x7b9 }, + { 0x25a,0x2b2,0x30a,0x362,0x3ba,0x412,0x46a,0x4c2,0x51a,0x572,0x5ca,0x622,0x67a,0x6d2,0x72a,0x782,0x7da,0x832,0x88a,0x026,0x07e,0x0d6,0x12e,0x186,0x1de,0x236,0x28e,0x2e6,0x33e,0x396,0x3ee,0x446,0x49e,0x4f6,0x54e,0x5a6,0x5fe,0x656,0x6ae,0x706,0x75e,0x7b6,0x80e }, + { 0x25b,0x2b3,0x30b,0x363,0x3bb,0x413,0x46b,0x4c3,0x51b,0x573,0x5cb,0x623,0x67b,0x6d3,0x72b,0x783,0x7db,0x833,0x88b,0x027,0x07f,0x0d7,0x12f,0x187,0x1df,0x237,0x28f,0x2e7,0x33f,0x397,0x3ef,0x447,0x49f,0x4f7,0x54f,0x5a7,0x5ff,0x657,0x6af,0x707,0x75f,0x7b7,0x80f }, + { 0x2b0,0x308,0x360,0x3b8,0x410,0x468,0x4c0,0x518,0x570,0x5c8,0x620,0x678,0x6d0,0x728,0x780,0x7d8,0x830,0x888,0x024,0x07c,0x0d4,0x12c,0x184,0x1dc,0x234,0x28c,0x2e4,0x33c,0x394,0x3ec,0x444,0x49c,0x4f4,0x54c,0x5a4,0x5fc,0x654,0x6ac,0x704,0x75c,0x7b4,0x80c,0x864 }, + { 0x2b1,0x309,0x361,0x3b9,0x411,0x469,0x4c1,0x519,0x571,0x5c9,0x621,0x679,0x6d1,0x729,0x781,0x7d9,0x831,0x889,0x025,0x07d,0x0d5,0x12d,0x185,0x1dd,0x235,0x28d,0x2e5,0x33d,0x395,0x3ed,0x445,0x49d,0x4f5,0x54d,0x5a5,0x5fd,0x655,0x6ad,0x705,0x75d,0x7b5,0x80d,0x865 }, + { 0x306,0x35e,0x3b6,0x40e,0x466,0x4be,0x516,0x56e,0x5c6,0x61e,0x676,0x6ce,0x726,0x77e,0x7d6,0x82e,0x886,0x022,0x07a,0x0d2,0x12a,0x182,0x1da,0x232,0x28a,0x2e2,0x33a,0x392,0x3ea,0x442,0x49a,0x4f2,0x54a,0x5a2,0x5fa,0x652,0x6aa,0x702,0x75a,0x7b2,0x80a,0x862,0x8ba }, + { 0x307,0x35f,0x3b7,0x40f,0x467,0x4bf,0x517,0x56f,0x5c7,0x61f,0x677,0x6cf,0x727,0x77f,0x7d7,0x82f,0x887,0x023,0x07b,0x0d3,0x12b,0x183,0x1db,0x233,0x28b,0x2e3,0x33b,0x393,0x3eb,0x443,0x49b,0x4f3,0x54b,0x5a3,0x5fb,0x653,0x6ab,0x703,0x75b,0x7b3,0x80b,0x863,0x8bb }, + { 0x35c,0x3b4,0x40c,0x464,0x4bc,0x514,0x56c,0x5c4,0x61c,0x674,0x6cc,0x724,0x77c,0x7d4,0x82c,0x884,0x020,0x078,0x0d0,0x128,0x180,0x1d8,0x230,0x288,0x2e0,0x338,0x390,0x3e8,0x440,0x498,0x4f0,0x548,0x5a0,0x5f8,0x650,0x6a8,0x700,0x758,0x7b0,0x808,0x860,0x8b8,0x054 }, + { 0x35d,0x3b5,0x40d,0x465,0x4bd,0x515,0x56d,0x5c5,0x61d,0x675,0x6cd,0x725,0x77d,0x7d5,0x82d,0x885,0x021,0x079,0x0d1,0x129,0x181,0x1d9,0x231,0x289,0x2e1,0x339,0x391,0x3e9,0x441,0x499,0x4f1,0x549,0x5a1,0x5f9,0x651,0x6a9,0x701,0x759,0x7b1,0x809,0x861,0x8b9,0x055 }, + { 0x3b2,0x40a,0x462,0x4ba,0x512,0x56a,0x5c2,0x61a,0x672,0x6ca,0x722,0x77a,0x7d2,0x82a,0x882,0x01e,0x076,0x0ce,0x126,0x17e,0x1d6,0x22e,0x286,0x2de,0x336,0x38e,0x3e6,0x43e,0x496,0x4ee,0x546,0x59e,0x5f6,0x64e,0x6a6,0x6fe,0x756,0x7ae,0x806,0x85e,0x8b6,0x052,0x0aa }, + { 0x3b3,0x40b,0x463,0x4bb,0x513,0x56b,0x5c3,0x61b,0x673,0x6cb,0x723,0x77b,0x7d3,0x82b,0x883,0x01f,0x077,0x0cf,0x127,0x17f,0x1d7,0x22f,0x287,0x2df,0x337,0x38f,0x3e7,0x43f,0x497,0x4ef,0x547,0x59f,0x5f7,0x64f,0x6a7,0x6ff,0x757,0x7af,0x807,0x85f,0x8b7,0x053,0x0ab }, + { 0x408,0x460,0x4b8,0x510,0x568,0x5c0,0x618,0x670,0x6c8,0x720,0x778,0x7d0,0x828,0x880,0x01c,0x074,0x0cc,0x124,0x17c,0x1d4,0x22c,0x284,0x2dc,0x334,0x38c,0x3e4,0x43c,0x494,0x4ec,0x544,0x59c,0x5f4,0x64c,0x6a4,0x6fc,0x754,0x7ac,0x804,0x85c,0x8b4,0x050,0x0a8,0x100 }, + { 0x409,0x461,0x4b9,0x511,0x569,0x5c1,0x619,0x671,0x6c9,0x721,0x779,0x7d1,0x829,0x881,0x01d,0x075,0x0cd,0x125,0x17d,0x1d5,0x22d,0x285,0x2dd,0x335,0x38d,0x3e5,0x43d,0x495,0x4ed,0x545,0x59d,0x5f5,0x64d,0x6a5,0x6fd,0x755,0x7ad,0x805,0x85d,0x8b5,0x051,0x0a9,0x101 }, + { 0x45e,0x4b6,0x50e,0x566,0x5be,0x616,0x66e,0x6c6,0x71e,0x776,0x7ce,0x826,0x87e,0x01a,0x072,0x0ca,0x122,0x17a,0x1d2,0x22a,0x282,0x2da,0x332,0x38a,0x3e2,0x43a,0x492,0x4ea,0x542,0x59a,0x5f2,0x64a,0x6a2,0x6fa,0x752,0x7aa,0x802,0x85a,0x8b2,0x04e,0x0a6,0x0fe,0x156 }, + { 0x45f,0x4b7,0x50f,0x567,0x5bf,0x617,0x66f,0x6c7,0x71f,0x777,0x7cf,0x827,0x87f,0x01b,0x073,0x0cb,0x123,0x17b,0x1d3,0x22b,0x283,0x2db,0x333,0x38b,0x3e3,0x43b,0x493,0x4eb,0x543,0x59b,0x5f3,0x64b,0x6a3,0x6fb,0x753,0x7ab,0x803,0x85b,0x8b3,0x04f,0x0a7,0x0ff,0x157 }, + { 0x4b4,0x50c,0x564,0x5bc,0x614,0x66c,0x6c4,0x71c,0x774,0x7cc,0x824,0x87c,0x018,0x070,0x0c8,0x120,0x178,0x1d0,0x228,0x280,0x2d8,0x330,0x388,0x3e0,0x438,0x490,0x4e8,0x540,0x598,0x5f0,0x648,0x6a0,0x6f8,0x750,0x7a8,0x800,0x858,0x8b0,0x04c,0x0a4,0x0fc,0x154,0x1ac }, + { 0x4b5,0x50d,0x565,0x5bd,0x615,0x66d,0x6c5,0x71d,0x775,0x7cd,0x825,0x87d,0x019,0x071,0x0c9,0x121,0x179,0x1d1,0x229,0x281,0x2d9,0x331,0x389,0x3e1,0x439,0x491,0x4e9,0x541,0x599,0x5f1,0x649,0x6a1,0x6f9,0x751,0x7a9,0x801,0x859,0x8b1,0x04d,0x0a5,0x0fd,0x155,0x1ad }, + { 0x50a,0x562,0x5ba,0x612,0x66a,0x6c2,0x71a,0x772,0x7ca,0x822,0x87a,0x016,0x06e,0x0c6,0x11e,0x176,0x1ce,0x226,0x27e,0x2d6,0x32e,0x386,0x3de,0x436,0x48e,0x4e6,0x53e,0x596,0x5ee,0x646,0x69e,0x6f6,0x74e,0x7a6,0x7fe,0x856,0x8ae,0x04a,0x0a2,0x0fa,0x152,0x1aa,0x202 }, + { 0x50b,0x563,0x5bb,0x613,0x66b,0x6c3,0x71b,0x773,0x7cb,0x823,0x87b,0x017,0x06f,0x0c7,0x11f,0x177,0x1cf,0x227,0x27f,0x2d7,0x32f,0x387,0x3df,0x437,0x48f,0x4e7,0x53f,0x597,0x5ef,0x647,0x69f,0x6f7,0x74f,0x7a7,0x7ff,0x857,0x8af,0x04b,0x0a3,0x0fb,0x153,0x1ab,0x203 }, + { 0x560,0x5b8,0x610,0x668,0x6c0,0x718,0x770,0x7c8,0x820,0x878,0x014,0x06c,0x0c4,0x11c,0x174,0x1cc,0x224,0x27c,0x2d4,0x32c,0x384,0x3dc,0x434,0x48c,0x4e4,0x53c,0x594,0x5ec,0x644,0x69c,0x6f4,0x74c,0x7a4,0x7fc,0x854,0x8ac,0x048,0x0a0,0x0f8,0x150,0x1a8,0x200,0x258 }, + { 0x561,0x5b9,0x611,0x669,0x6c1,0x719,0x771,0x7c9,0x821,0x879,0x015,0x06d,0x0c5,0x11d,0x175,0x1cd,0x225,0x27d,0x2d5,0x32d,0x385,0x3dd,0x435,0x48d,0x4e5,0x53d,0x595,0x5ed,0x645,0x69d,0x6f5,0x74d,0x7a5,0x7fd,0x855,0x8ad,0x049,0x0a1,0x0f9,0x151,0x1a9,0x201,0x259 }, + { 0x5b6,0x60e,0x666,0x6be,0x716,0x76e,0x7c6,0x81e,0x876,0x012,0x06a,0x0c2,0x11a,0x172,0x1ca,0x222,0x27a,0x2d2,0x32a,0x382,0x3da,0x432,0x48a,0x4e2,0x53a,0x592,0x5ea,0x642,0x69a,0x6f2,0x74a,0x7a2,0x7fa,0x852,0x8aa,0x046,0x09e,0x0f6,0x14e,0x1a6,0x1fe,0x256,0x2ae }, + { 0x5b7,0x60f,0x667,0x6bf,0x717,0x76f,0x7c7,0x81f,0x877,0x013,0x06b,0x0c3,0x11b,0x173,0x1cb,0x223,0x27b,0x2d3,0x32b,0x383,0x3db,0x433,0x48b,0x4e3,0x53b,0x593,0x5eb,0x643,0x69b,0x6f3,0x74b,0x7a3,0x7fb,0x853,0x8ab,0x047,0x09f,0x0f7,0x14f,0x1a7,0x1ff,0x257,0x2af }, + { 0x60c,0x664,0x6bc,0x714,0x76c,0x7c4,0x81c,0x874,0x010,0x068,0x0c0,0x118,0x170,0x1c8,0x220,0x278,0x2d0,0x328,0x380,0x3d8,0x430,0x488,0x4e0,0x538,0x590,0x5e8,0x640,0x698,0x6f0,0x748,0x7a0,0x7f8,0x850,0x8a8,0x044,0x09c,0x0f4,0x14c,0x1a4,0x1fc,0x254,0x2ac,0x304 }, + { 0x60d,0x665,0x6bd,0x715,0x76d,0x7c5,0x81d,0x875,0x011,0x069,0x0c1,0x119,0x171,0x1c9,0x221,0x279,0x2d1,0x329,0x381,0x3d9,0x431,0x489,0x4e1,0x539,0x591,0x5e9,0x641,0x699,0x6f1,0x749,0x7a1,0x7f9,0x851,0x8a9,0x045,0x09d,0x0f5,0x14d,0x1a5,0x1fd,0x255,0x2ad,0x305 }, + { 0x662,0x6ba,0x712,0x76a,0x7c2,0x81a,0x872,0x00e,0x066,0x0be,0x116,0x16e,0x1c6,0x21e,0x276,0x2ce,0x326,0x37e,0x3d6,0x42e,0x486,0x4de,0x536,0x58e,0x5e6,0x63e,0x696,0x6ee,0x746,0x79e,0x7f6,0x84e,0x8a6,0x042,0x09a,0x0f2,0x14a,0x1a2,0x1fa,0x252,0x2aa,0x302,0x35a }, + { 0x663,0x6bb,0x713,0x76b,0x7c3,0x81b,0x873,0x00f,0x067,0x0bf,0x117,0x16f,0x1c7,0x21f,0x277,0x2cf,0x327,0x37f,0x3d7,0x42f,0x487,0x4df,0x537,0x58f,0x5e7,0x63f,0x697,0x6ef,0x747,0x79f,0x7f7,0x84f,0x8a7,0x043,0x09b,0x0f3,0x14b,0x1a3,0x1fb,0x253,0x2ab,0x303,0x35b }, + { 0x6b8,0x710,0x768,0x7c0,0x818,0x870,0x00c,0x064,0x0bc,0x114,0x16c,0x1c4,0x21c,0x274,0x2cc,0x324,0x37c,0x3d4,0x42c,0x484,0x4dc,0x534,0x58c,0x5e4,0x63c,0x694,0x6ec,0x744,0x79c,0x7f4,0x84c,0x8a4,0x040,0x098,0x0f0,0x148,0x1a0,0x1f8,0x250,0x2a8,0x300,0x358,0x3b0 }, + { 0x6b9,0x711,0x769,0x7c1,0x819,0x871,0x00d,0x065,0x0bd,0x115,0x16d,0x1c5,0x21d,0x275,0x2cd,0x325,0x37d,0x3d5,0x42d,0x485,0x4dd,0x535,0x58d,0x5e5,0x63d,0x695,0x6ed,0x745,0x79d,0x7f5,0x84d,0x8a5,0x041,0x099,0x0f1,0x149,0x1a1,0x1f9,0x251,0x2a9,0x301,0x359,0x3b1 }, + { 0x70e,0x766,0x7be,0x816,0x86e,0x00a,0x062,0x0ba,0x112,0x16a,0x1c2,0x21a,0x272,0x2ca,0x322,0x37a,0x3d2,0x42a,0x482,0x4da,0x532,0x58a,0x5e2,0x63a,0x692,0x6ea,0x742,0x79a,0x7f2,0x84a,0x8a2,0x03e,0x096,0x0ee,0x146,0x19e,0x1f6,0x24e,0x2a6,0x2fe,0x356,0x3ae,0x406 }, + { 0x70f,0x767,0x7bf,0x817,0x86f,0x00b,0x063,0x0bb,0x113,0x16b,0x1c3,0x21b,0x273,0x2cb,0x323,0x37b,0x3d3,0x42b,0x483,0x4db,0x533,0x58b,0x5e3,0x63b,0x693,0x6eb,0x743,0x79b,0x7f3,0x84b,0x8a3,0x03f,0x097,0x0ef,0x147,0x19f,0x1f7,0x24f,0x2a7,0x2ff,0x357,0x3af,0x407 }, + { 0x764,0x7bc,0x814,0x86c,0x008,0x060,0x0b8,0x110,0x168,0x1c0,0x218,0x270,0x2c8,0x320,0x378,0x3d0,0x428,0x480,0x4d8,0x530,0x588,0x5e0,0x638,0x690,0x6e8,0x740,0x798,0x7f0,0x848,0x8a0,0x03c,0x094,0x0ec,0x144,0x19c,0x1f4,0x24c,0x2a4,0x2fc,0x354,0x3ac,0x404,0x45c }, + { 0x765,0x7bd,0x815,0x86d,0x009,0x061,0x0b9,0x111,0x169,0x1c1,0x219,0x271,0x2c9,0x321,0x379,0x3d1,0x429,0x481,0x4d9,0x531,0x589,0x5e1,0x639,0x691,0x6e9,0x741,0x799,0x7f1,0x849,0x8a1,0x03d,0x095,0x0ed,0x145,0x19d,0x1f5,0x24d,0x2a5,0x2fd,0x355,0x3ad,0x405,0x45d }, + { 0x7ba,0x812,0x86a,0x006,0x05e,0x0b6,0x10e,0x166,0x1be,0x216,0x26e,0x2c6,0x31e,0x376,0x3ce,0x426,0x47e,0x4d6,0x52e,0x586,0x5de,0x636,0x68e,0x6e6,0x73e,0x796,0x7ee,0x846,0x89e,0x03a,0x092,0x0ea,0x142,0x19a,0x1f2,0x24a,0x2a2,0x2fa,0x352,0x3aa,0x402,0x45a,0x4b2 }, + { 0x7bb,0x813,0x86b,0x007,0x05f,0x0b7,0x10f,0x167,0x1bf,0x217,0x26f,0x2c7,0x31f,0x377,0x3cf,0x427,0x47f,0x4d7,0x52f,0x587,0x5df,0x637,0x68f,0x6e7,0x73f,0x797,0x7ef,0x847,0x89f,0x03b,0x093,0x0eb,0x143,0x19b,0x1f3,0x24b,0x2a3,0x2fb,0x353,0x3ab,0x403,0x45b,0x4b3 }, + { 0x810,0x868,0x004,0x05c,0x0b4,0x10c,0x164,0x1bc,0x214,0x26c,0x2c4,0x31c,0x374,0x3cc,0x424,0x47c,0x4d4,0x52c,0x584,0x5dc,0x634,0x68c,0x6e4,0x73c,0x794,0x7ec,0x844,0x89c,0x038,0x090,0x0e8,0x140,0x198,0x1f0,0x248,0x2a0,0x2f8,0x350,0x3a8,0x400,0x458,0x4b0,0x508 }, + { 0x811,0x869,0x005,0x05d,0x0b5,0x10d,0x165,0x1bd,0x215,0x26d,0x2c5,0x31d,0x375,0x3cd,0x425,0x47d,0x4d5,0x52d,0x585,0x5dd,0x635,0x68d,0x6e5,0x73d,0x795,0x7ed,0x845,0x89d,0x039,0x091,0x0e9,0x141,0x199,0x1f1,0x249,0x2a1,0x2f9,0x351,0x3a9,0x401,0x459,0x4b1,0x509 }, + { 0x866,0x002,0x05a,0x0b2,0x10a,0x162,0x1ba,0x212,0x26a,0x2c2,0x31a,0x372,0x3ca,0x422,0x47a,0x4d2,0x52a,0x582,0x5da,0x632,0x68a,0x6e2,0x73a,0x792,0x7ea,0x842,0x89a,0x036,0x08e,0x0e6,0x13e,0x196,0x1ee,0x246,0x29e,0x2f6,0x34e,0x3a6,0x3fe,0x456,0x4ae,0x506,0x55e }, + { 0x867,0x003,0x05b,0x0b3,0x10b,0x163,0x1bb,0x213,0x26b,0x2c3,0x31b,0x373,0x3cb,0x423,0x47b,0x4d3,0x52b,0x583,0x5db,0x633,0x68b,0x6e3,0x73b,0x793,0x7eb,0x843,0x89b,0x037,0x08f,0x0e7,0x13f,0x197,0x1ef,0x247,0x29f,0x2f7,0x34f,0x3a7,0x3ff,0x457,0x4af,0x507,0x55f } +}; + +/*------------------------------------------------- + * ecc_source_byte - return data from the sector + * at the given offset, masking anything + * particular to a mode + *------------------------------------------------- + */ + +static inline uint8_t ecc_source_byte(const uint8_t *sector, uint32_t offset) +{ + /* in mode 2 always treat these as 0 bytes */ + return (sector[MODE_OFFSET] == 2 && offset < 4) ? 0x00 : sector[SYNC_OFFSET + SYNC_NUM_BYTES + offset]; +} + +/** + * @fn void ecc_compute_bytes(const uint8_t *sector, const uint16_t *row, int rowlen, uint8_t &val1, uint8_t &val2) + * + * @brief ------------------------------------------------- + * ecc_compute_bytes - calculate an ECC value (P or Q) + * -------------------------------------------------. + * + * @param sector The sector. + * @param row The row. + * @param rowlen The rowlen. + * @param [in,out] val1 The first value. + * @param [in,out] val2 The second value. + */ + +void ecc_compute_bytes(const uint8_t *sector, const uint16_t *row, int rowlen, uint8_t *val1, uint8_t *val2) +{ + int component; + *val1 = *val2 = 0; + for (component = 0; component < rowlen; component++) + { + *val1 ^= ecc_source_byte(sector, row[component]); + *val2 ^= ecc_source_byte(sector, row[component]); + *val1 = ecclow[*val1]; + } + *val1 = ecchigh[ecclow[*val1] ^ *val2]; + *val2 ^= *val1; +} + +/** + * @fn int ecc_verify(const uint8_t *sector) + * + * @brief ------------------------------------------------- + * ecc_verify - verify the P and Q ECC codes in a sector + * -------------------------------------------------. + * + * @param sector The sector. + * + * @return true if it succeeds, false if it fails. + */ + +int ecc_verify(const uint8_t *sector) +{ + int byte; + /* first verify P bytes */ + for (byte = 0; byte < ECC_P_NUM_BYTES; byte++) + { + uint8_t val1, val2; + ecc_compute_bytes(sector, poffsets[byte], ECC_P_COMP, &val1, &val2); + if (sector[ECC_P_OFFSET + byte] != val1 || sector[ECC_P_OFFSET + ECC_P_NUM_BYTES + byte] != val2) + return 0; + } + + /* then verify Q bytes */ + for (byte = 0; byte < ECC_Q_NUM_BYTES; byte++) + { + uint8_t val1, val2; + ecc_compute_bytes(sector, qoffsets[byte], ECC_Q_COMP, &val1, &val2); + if (sector[ECC_Q_OFFSET + byte] != val1 || sector[ECC_Q_OFFSET + ECC_Q_NUM_BYTES + byte] != val2) + return 0; + } + return 1; +} + +/** + * @fn void ecc_generate(uint8_t *sector) + * + * @brief ------------------------------------------------- + * ecc_generate - generate the P and Q ECC codes for a sector, overwriting any + * existing codes + * -------------------------------------------------. + * + * @param [in,out] sector If non-null, the sector. + */ + +void ecc_generate(uint8_t *sector) +{ + int byte; + /* first verify P bytes */ + for (byte = 0; byte < ECC_P_NUM_BYTES; byte++) + ecc_compute_bytes(sector, poffsets[byte], ECC_P_COMP, §or[ECC_P_OFFSET + byte], §or[ECC_P_OFFSET + ECC_P_NUM_BYTES + byte]); + + /* then verify Q bytes */ + for (byte = 0; byte < ECC_Q_NUM_BYTES; byte++) + ecc_compute_bytes(sector, qoffsets[byte], ECC_Q_COMP, §or[ECC_Q_OFFSET + byte], §or[ECC_Q_OFFSET + ECC_Q_NUM_BYTES + byte]); +} + +/** + * @fn void ecc_clear(uint8_t *sector) + * + * @brief ------------------------------------------------- + * ecc_clear - erase the ECC P and Q cods to 0 within a sector + * -------------------------------------------------. + * + * @param [in,out] sector If non-null, the sector. + */ + +void ecc_clear(uint8_t *sector) +{ + memset(§or[ECC_P_OFFSET], 0, 2 * ECC_P_NUM_BYTES); + memset(§or[ECC_Q_OFFSET], 0, 2 * ECC_Q_NUM_BYTES); +} diff --git a/core/deps/chdr/cdrom.h b/core/deps/chdr/cdrom.h new file mode 100644 index 000000000..65aa18218 --- /dev/null +++ b/core/deps/chdr/cdrom.h @@ -0,0 +1,109 @@ +/* license:BSD-3-Clause + * copyright-holders:Aaron Giles +*************************************************************************** + + cdrom.h + + Generic MAME cd-rom implementation + +***************************************************************************/ + +#pragma once + +#ifndef __CDROM_H__ +#define __CDROM_H__ + +#include + + +/*************************************************************************** + CONSTANTS +***************************************************************************/ + +/* tracks are padded to a multiple of this many frames */ +extern const uint32_t CD_TRACK_PADDING; + +#define CD_MAX_TRACKS (99) /* AFAIK the theoretical limit */ +#define CD_MAX_SECTOR_DATA (2352) +#define CD_MAX_SUBCODE_DATA (96) + +#define CD_FRAME_SIZE (CD_MAX_SECTOR_DATA + CD_MAX_SUBCODE_DATA) +#define CD_FRAMES_PER_HUNK (8) + +#define CD_METADATA_WORDS (1+(CD_MAX_TRACKS * 6)) + +enum +{ + CD_TRACK_MODE1 = 0, /* mode 1 2048 bytes/sector */ + CD_TRACK_MODE1_RAW, /* mode 1 2352 bytes/sector */ + CD_TRACK_MODE2, /* mode 2 2336 bytes/sector */ + CD_TRACK_MODE2_FORM1, /* mode 2 2048 bytes/sector */ + CD_TRACK_MODE2_FORM2, /* mode 2 2324 bytes/sector */ + CD_TRACK_MODE2_FORM_MIX, /* mode 2 2336 bytes/sector */ + CD_TRACK_MODE2_RAW, /* mode 2 2352 bytes / sector */ + CD_TRACK_AUDIO, /* redbook audio track 2352 bytes/sector (588 samples) */ + + CD_TRACK_RAW_DONTCARE /* special flag for cdrom_read_data: just return me whatever is there */ +}; + +enum +{ + CD_SUB_NORMAL = 0, /* "cooked" 96 bytes per sector */ + CD_SUB_RAW, /* raw uninterleaved 96 bytes per sector */ + CD_SUB_NONE /* no subcode data stored */ +}; + +#define CD_FLAG_GDROM 0x00000001 /* disc is a GD-ROM, all tracks should be stored with GD-ROM metadata */ +#define CD_FLAG_GDROMLE 0x00000002 /* legacy GD-ROM, with little-endian CDDA data */ + +/*************************************************************************** + FUNCTION PROTOTYPES +***************************************************************************/ + +/* ECC utilities */ +int ecc_verify(const uint8_t *sector); +void ecc_generate(uint8_t *sector); +void ecc_clear(uint8_t *sector); + + + +/*************************************************************************** + INLINE FUNCTIONS +***************************************************************************/ + +static inline uint32_t msf_to_lba(uint32_t msf) +{ + return ( ((msf&0x00ff0000)>>16) * 60 * 75) + (((msf&0x0000ff00)>>8) * 75) + ((msf&0x000000ff)>>0); +} + +static inline uint32_t lba_to_msf(uint32_t lba) +{ + uint8_t m, s, f; + + m = lba / (60 * 75); + lba -= m * (60 * 75); + s = lba / 75; + f = lba % 75; + + return ((m / 10) << 20) | ((m % 10) << 16) | + ((s / 10) << 12) | ((s % 10) << 8) | + ((f / 10) << 4) | ((f % 10) << 0); +} + +/** + * segacd needs it like this.. investigate + * Angelo also says PCE tracks often start playing at the + * wrong address.. related? + **/ +static inline uint32_t lba_to_msf_alt(int lba) +{ + uint32_t ret = 0; + + ret |= ((lba / (60 * 75))&0xff)<<16; + ret |= (((lba / 75) % 60)&0xff)<<8; + ret |= ((lba % 75)&0xff)<<0; + + return ret; +} + +#endif /* __CDROM_H__ */ diff --git a/core/deps/chdr/chd.c b/core/deps/chdr/chd.c new file mode 100644 index 000000000..d853bb519 --- /dev/null +++ b/core/deps/chdr/chd.c @@ -0,0 +1,2440 @@ +/*************************************************************************** + + chd.c + + MAME Compressed Hunks of Data file format + +**************************************************************************** + + Copyright Aaron Giles + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are + met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + * Neither the name 'MAME' nor the names of its contributors may be + used to endorse or promote products derived from this software + without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY AARON GILES ''AS IS'' AND ANY EXPRESS OR + IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL AARON GILES BE LIABLE FOR ANY DIRECT, + INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. + +***************************************************************************/ + +#include +#include +#include +#include +#include +#include "chd.h" +#include "cdrom.h" +#include "flac.h" +#include "huffman.h" +#include "LzmaEnc.h" +#include "LzmaDec.h" +#include "md5.h" +#include "sha1.h" +#include "zlib.h" + +#define TRUE 1 +#define FALSE 0 + +#define MAX(x, y) (((x) > (y)) ? (x) : (y)) +#define MIN(x, y) (((x) < (y)) ? (x) : (y)) + +#define SHA1_DIGEST_SIZE 20 + +/*************************************************************************** + DEBUGGING +***************************************************************************/ + +#define PRINTF_MAX_HUNK (0) + +/*************************************************************************** + CONSTANTS +***************************************************************************/ + +#define MAP_STACK_ENTRIES 512 /* max number of entries to use on the stack */ +#define MAP_ENTRY_SIZE 16 /* V3 and later */ +#define OLD_MAP_ENTRY_SIZE 8 /* V1-V2 */ +#define METADATA_HEADER_SIZE 16 /* metadata header size */ +#define CRCMAP_HASH_SIZE 4095 /* number of CRC hashtable entries */ + +#define MAP_ENTRY_FLAG_TYPE_MASK 0x0f /* what type of hunk */ +#define MAP_ENTRY_FLAG_NO_CRC 0x10 /* no CRC is present */ + +#define CHD_V1_SECTOR_SIZE 512 /* size of a "sector" in the V1 header */ + +#define COOKIE_VALUE 0xbaadf00d +#define MAX_ZLIB_ALLOCS 64 + +#define END_OF_LIST_COOKIE "EndOfListCookie" + +#define NO_MATCH (~0) + +static const uint8_t s_cd_sync_header[12] = { 0x00,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x00 }; + +/* V3-V4 entry types */ +enum +{ + V34_MAP_ENTRY_TYPE_INVALID = 0, /* invalid type */ + V34_MAP_ENTRY_TYPE_COMPRESSED = 1, /* standard compression */ + V34_MAP_ENTRY_TYPE_UNCOMPRESSED = 2, /* uncompressed data */ + V34_MAP_ENTRY_TYPE_MINI = 3, /* mini: use offset as raw data */ + V34_MAP_ENTRY_TYPE_SELF_HUNK = 4, /* same as another hunk in this file */ + V34_MAP_ENTRY_TYPE_PARENT_HUNK = 5, /* same as a hunk in the parent file */ + V34_MAP_ENTRY_TYPE_2ND_COMPRESSED = 6 /* compressed with secondary algorithm (usually FLAC CDDA) */ +}; + +/* V5 compression types */ +enum +{ + /* codec #0 + * these types are live when running */ + COMPRESSION_TYPE_0 = 0, + /* codec #1 */ + COMPRESSION_TYPE_1 = 1, + /* codec #2 */ + COMPRESSION_TYPE_2 = 2, + /* codec #3 */ + COMPRESSION_TYPE_3 = 3, + /* no compression; implicit length = hunkbytes */ + COMPRESSION_NONE = 4, + /* same as another block in this chd */ + COMPRESSION_SELF = 5, + /* same as a hunk's worth of units in the parent chd */ + COMPRESSION_PARENT = 6, + + /* start of small RLE run (4-bit length) + * these additional pseudo-types are used for compressed encodings: */ + COMPRESSION_RLE_SMALL, + /* start of large RLE run (8-bit length) */ + COMPRESSION_RLE_LARGE, + /* same as the last COMPRESSION_SELF block */ + COMPRESSION_SELF_0, + /* same as the last COMPRESSION_SELF block + 1 */ + COMPRESSION_SELF_1, + /* same block in the parent */ + COMPRESSION_PARENT_SELF, + /* same as the last COMPRESSION_PARENT block */ + COMPRESSION_PARENT_0, + /* same as the last COMPRESSION_PARENT block + 1 */ + COMPRESSION_PARENT_1 +}; + +/*************************************************************************** + MACROS +***************************************************************************/ + +#define EARLY_EXIT(x) do { (void)(x); goto cleanup; } while (0) + +/*************************************************************************** + TYPE DEFINITIONS +***************************************************************************/ + +/* interface to a codec */ +typedef struct _codec_interface codec_interface; +struct _codec_interface +{ + UINT32 compression; /* type of compression */ + const char *compname; /* name of the algorithm */ + UINT8 lossy; /* is this a lossy algorithm? */ + chd_error (*init)(void *codec, UINT32 hunkbytes); /* codec initialize */ + void (*free)(void *codec); /* codec free */ + chd_error (*decompress)(void *codec, const uint8_t *src, uint32_t complen, uint8_t *dest, uint32_t destlen); /* decompress data */ + chd_error (*config)(void *codec, int param, void *config); /* configure */ +}; + +/* a single map entry */ +typedef struct _map_entry map_entry; +struct _map_entry +{ + UINT64 offset; /* offset within the file of the data */ + UINT32 crc; /* 32-bit CRC of the data */ + UINT32 length; /* length of the data */ + UINT8 flags; /* misc flags */ +}; + +/* simple linked-list of hunks used for our CRC map */ +typedef struct _crcmap_entry crcmap_entry; +struct _crcmap_entry +{ + UINT32 hunknum; /* hunk number */ + crcmap_entry * next; /* next entry in list */ +}; + +/* a single metadata entry */ +typedef struct _metadata_entry metadata_entry; +struct _metadata_entry +{ + UINT64 offset; /* offset within the file of the header */ + UINT64 next; /* offset within the file of the next header */ + UINT64 prev; /* offset within the file of the previous header */ + UINT32 length; /* length of the metadata */ + UINT32 metatag; /* metadata tag */ + UINT8 flags; /* flag bits */ +}; + +/* codec-private data for the ZLIB codec */ + +typedef struct _zlib_allocator zlib_allocator; +struct _zlib_allocator +{ + UINT32 * allocptr[MAX_ZLIB_ALLOCS]; +}; + +typedef struct _zlib_codec_data zlib_codec_data; +struct _zlib_codec_data +{ + z_stream inflater; + zlib_allocator allocator; +}; + +/* codec-private data for the LZMA codec */ +#define MAX_LZMA_ALLOCS 64 + +typedef struct _lzma_allocator lzma_allocator; +struct _lzma_allocator +{ + void *(*Alloc)(void *p, size_t size); + void (*Free)(void *p, void *address); /* address can be 0 */ + void (*FreeSz)(void *p, void *address, size_t size); /* address can be 0 */ + uint32_t* allocptr[MAX_LZMA_ALLOCS]; +}; + +typedef struct _lzma_codec_data lzma_codec_data; +struct _lzma_codec_data +{ + CLzmaDec decoder; + lzma_allocator allocator; +}; + +/* codec-private data for the CDZL codec */ +typedef struct _cdzl_codec_data cdzl_codec_data; +struct _cdzl_codec_data { + /* internal state */ + zlib_codec_data base_decompressor; + zlib_codec_data subcode_decompressor; + uint8_t* buffer; +}; + +/* codec-private data for the CDLZ codec */ +typedef struct _cdlz_codec_data cdlz_codec_data; +struct _cdlz_codec_data { + /* internal state */ + lzma_codec_data base_decompressor; + zlib_codec_data subcode_decompressor; + uint8_t* buffer; +}; + +/* codec-private data for the CDFL codec */ +typedef struct _cdfl_codec_data cdfl_codec_data; +struct _cdfl_codec_data { + /* internal state */ + int swap_endian; + flac_decoder decoder; + z_stream inflater; + zlib_allocator allocator; + uint8_t* buffer; +}; + +/* internal representation of an open CHD file */ +struct _chd_file +{ + UINT32 cookie; /* cookie, should equal COOKIE_VALUE */ + + core_file * file; /* handle to the open core file */ + UINT8 owns_file; /* flag indicating if this file should be closed on chd_close() */ + chd_header header; /* header, extracted from file */ + + chd_file * parent; /* pointer to parent file, or NULL */ + + map_entry * map; /* array of map entries */ + + UINT8 * cache; /* hunk cache pointer */ + UINT32 cachehunk; /* index of currently cached hunk */ + + UINT8 * compare; /* hunk compare pointer */ + UINT32 comparehunk; /* index of current compare data */ + + UINT8 * compressed; /* pointer to buffer for compressed data */ + const codec_interface * codecintf[4]; /* interface to the codec */ + + zlib_codec_data zlib_codec_data; /* zlib codec data */ + cdzl_codec_data cdzl_codec_data; /* cdzl codec data */ + cdlz_codec_data cdlz_codec_data; /* cdlz codec data */ + cdfl_codec_data cdfl_codec_data; /* cdfl codec data */ + + crcmap_entry * crcmap; /* CRC map entries */ + crcmap_entry * crcfree; /* free list CRC entries */ + crcmap_entry ** crctable; /* table of CRC entries */ + + UINT32 maxhunk; /* maximum hunk accessed */ + + UINT8 compressing; /* are we compressing? */ + MD5_CTX compmd5; /* running MD5 during compression */ + SHA1_CTX compsha1; /* running SHA1 during compression */ + UINT32 comphunk; /* next hunk we will compress */ + + UINT8 verifying; /* are we verifying? */ + MD5_CTX vermd5; /* running MD5 during verification */ + SHA1_CTX versha1; /* running SHA1 during verification */ + UINT32 verhunk; /* next hunk we will verify */ + + UINT32 async_hunknum; /* hunk index for asynchronous operations */ + void * async_buffer; /* buffer pointer for asynchronous operations */ +}; + +/* a single metadata hash entry */ +typedef struct _metadata_hash metadata_hash; +struct _metadata_hash +{ + UINT8 tag[4]; /* tag of the metadata in big-endian */ + UINT8 sha1[CHD_SHA1_BYTES]; /* hash */ +}; + +/*************************************************************************** + GLOBAL VARIABLES +***************************************************************************/ + +static const UINT8 nullmd5[CHD_MD5_BYTES] = { 0 }; +static const UINT8 nullsha1[CHD_SHA1_BYTES] = { 0 }; + +/*************************************************************************** + PROTOTYPES +***************************************************************************/ + +/* internal header operations */ +static chd_error header_validate(const chd_header *header); +static chd_error header_read(chd_file *chd, chd_header *header); + +/* internal hunk read/write */ +static chd_error hunk_read_into_cache(chd_file *chd, UINT32 hunknum); +static chd_error hunk_read_into_memory(chd_file *chd, UINT32 hunknum, UINT8 *dest); + +/* internal map access */ +static chd_error map_read(chd_file *chd); + +/* metadata management */ +static chd_error metadata_find_entry(chd_file *chd, UINT32 metatag, UINT32 metaindex, metadata_entry *metaentry); + +/* zlib compression codec */ +static chd_error zlib_codec_init(void *codec, uint32_t hunkbytes); +static void zlib_codec_free(void *codec); +static chd_error zlib_codec_decompress(void *codec, const uint8_t *src, uint32_t complen, uint8_t *dest, uint32_t destlen); +static voidpf zlib_fast_alloc(voidpf opaque, uInt items, uInt size); +static void zlib_fast_free(voidpf opaque, voidpf address); + +/* lzma compression codec */ +static chd_error lzma_codec_init(void *codec, uint32_t hunkbytes); +static void lzma_codec_free(void *codec); +static chd_error lzma_codec_decompress(void *codec, const uint8_t *src, uint32_t complen, uint8_t *dest, uint32_t destlen); + +/* cdzl compression codec */ +static chd_error cdzl_codec_init(void* codec, uint32_t hunkbytes); +static void cdzl_codec_free(void* codec); +static chd_error cdzl_codec_decompress(void *codec, const uint8_t *src, uint32_t complen, uint8_t *dest, uint32_t destlen); + +/* cdlz compression codec */ +static chd_error cdlz_codec_init(void* codec, uint32_t hunkbytes); +static void cdlz_codec_free(void* codec); +static chd_error cdlz_codec_decompress(void *codec, const uint8_t *src, uint32_t complen, uint8_t *dest, uint32_t destlen); + +/* cdfl compression codec */ +static chd_error cdfl_codec_init(void* codec, uint32_t hunkbytes); +static void cdfl_codec_free(void* codec); +static chd_error cdfl_codec_decompress(void *codec, const uint8_t *src, uint32_t complen, uint8_t *dest, uint32_t destlen); + +/*************************************************************************** + * LZMA ALLOCATOR HELPER + *************************************************************************** + */ + +void *lzma_fast_alloc(void *p, size_t size); +void lzma_fast_free(void *p, void *address); + +/*------------------------------------------------- + * lzma_allocator_init + *------------------------------------------------- + */ + +void lzma_allocator_init(void* p) +{ + lzma_allocator *codec = (lzma_allocator *)(p); + + /* reset pointer list */ + memset(codec->allocptr, 0, sizeof(codec->allocptr)); + codec->Alloc = lzma_fast_alloc; + codec->Free = lzma_fast_free; +} + +/*------------------------------------------------- + * lzma_allocator_free + *------------------------------------------------- + */ + +void lzma_allocator_free(void* p ) +{ + lzma_allocator *codec = (lzma_allocator *)(p); + + /* free our memory */ + for (int i = 0 ; i < MAX_LZMA_ALLOCS ; i++) + { + if (codec->allocptr[i] != NULL) + free(codec->allocptr[i]); + } +} + +/*------------------------------------------------- + * lzma_fast_alloc - fast malloc for lzma, which + * allocates and frees memory frequently + *------------------------------------------------- + */ + +void *lzma_fast_alloc(void *p, size_t size) +{ + lzma_allocator *codec = (lzma_allocator *)(p); + + /* compute the size, rounding to the nearest 1k */ + size = (size + 0x3ff) & ~0x3ff; + + /* reuse a hunk if we can */ + for (int scan = 0; scan < MAX_LZMA_ALLOCS; scan++) + { + uint32_t *ptr = codec->allocptr[scan]; + if (ptr != NULL && size == *ptr) + { + /* set the low bit of the size so we don't match next time */ + *ptr |= 1; + return ptr + 1; + } + } + + /* alloc a new one and put it into the list */ + uint32_t *addr = (uint32_t *)malloc(sizeof(uint8_t) * (size + sizeof(uint32_t))); + if (addr==NULL) + return NULL; + for (int scan = 0; scan < MAX_LZMA_ALLOCS; scan++) + { + if (codec->allocptr[scan] == NULL) + { + codec->allocptr[scan] = addr; + break; + } + } + + /* set the low bit of the size so we don't match next time */ + *addr = size | 1; + return addr + 1; +} + +/*------------------------------------------------- + * lzma_fast_free - fast free for lzma, which + * allocates and frees memory frequently + *------------------------------------------------- + */ + +void lzma_fast_free(void *p, void *address) +{ + if (address == NULL) + return; + + lzma_allocator *codec = (lzma_allocator *)(p); + + /* find the hunk */ + uint32_t *ptr = (uint32_t *)(address) - 1; + for (int scan = 0; scan < MAX_LZMA_ALLOCS; scan++) + { + if (ptr == codec->allocptr[scan]) + { + /* clear the low bit of the size to allow matches */ + *ptr &= ~1; + return; + } + } +} + +/*************************************************************************** + * LZMA DECOMPRESSOR + *************************************************************************** + */ + +/*------------------------------------------------- + * lzma_codec_init - constructor + *------------------------------------------------- + */ + +chd_error lzma_codec_init(void* codec, uint32_t hunkbytes) +{ + lzma_codec_data* lzma_codec = (lzma_codec_data*) codec; + + /* construct the decoder */ + LzmaDec_Construct(&lzma_codec->decoder); + + /* FIXME: this code is written in a way that makes it impossible to safely upgrade the LZMA SDK + * This code assumes that the current version of the encoder imposes the same requirements on the + * decoder as the encoder used to produce the file. This is not necessarily true. The format + * needs to be changed so the encoder properties are written to the file. + + * configure the properties like the compressor did */ + CLzmaEncProps encoder_props; + LzmaEncProps_Init(&encoder_props); + encoder_props.level = 9; + encoder_props.reduceSize = hunkbytes; + LzmaEncProps_Normalize(&encoder_props); + + /* convert to decoder properties */ + lzma_allocator* alloc = &lzma_codec->allocator; + lzma_allocator_init(alloc); + CLzmaEncHandle enc = LzmaEnc_Create((ISzAlloc*)alloc); + if (!enc) + return CHDERR_DECOMPRESSION_ERROR; + if (LzmaEnc_SetProps(enc, &encoder_props) != SZ_OK) + { + LzmaEnc_Destroy(enc, (ISzAlloc*)&alloc, (ISzAlloc*)&alloc); + return CHDERR_DECOMPRESSION_ERROR; + } + Byte decoder_props[LZMA_PROPS_SIZE]; + SizeT props_size = sizeof(decoder_props); + if (LzmaEnc_WriteProperties(enc, decoder_props, &props_size) != SZ_OK) + { + LzmaEnc_Destroy(enc, (ISzAlloc*)alloc, (ISzAlloc*)alloc); + return CHDERR_DECOMPRESSION_ERROR; + } + LzmaEnc_Destroy(enc, (ISzAlloc*)alloc, (ISzAlloc*)alloc); + + /* do memory allocations */ + if (LzmaDec_Allocate(&lzma_codec->decoder, decoder_props, LZMA_PROPS_SIZE, (ISzAlloc*)alloc) != SZ_OK) + return CHDERR_DECOMPRESSION_ERROR; + + /* Okay */ + return CHDERR_NONE; +} + +/*------------------------------------------------- + * lzma_codec_free + *------------------------------------------------- + */ + +void lzma_codec_free(void* codec) +{ + lzma_codec_data* lzma_codec = (lzma_codec_data*) codec; + + /* free memory */ + LzmaDec_Free(&lzma_codec->decoder, (ISzAlloc*)&lzma_codec->allocator); +} + +/*------------------------------------------------- + * decompress - decompress data using the LZMA + * codec + *------------------------------------------------- + */ + +chd_error lzma_codec_decompress(void* codec, const uint8_t *src, uint32_t complen, uint8_t *dest, uint32_t destlen) +{ + /* initialize */ + lzma_codec_data* lzma_codec = (lzma_codec_data*) codec; + LzmaDec_Init(&lzma_codec->decoder); + + /* decode */ + SizeT consumedlen = complen; + SizeT decodedlen = destlen; + ELzmaStatus status; + SRes res = LzmaDec_DecodeToBuf(&lzma_codec->decoder, dest, &decodedlen, src, &consumedlen, LZMA_FINISH_END, &status); + if ((res != SZ_OK && res != LZMA_STATUS_MAYBE_FINISHED_WITHOUT_MARK) || consumedlen != complen || decodedlen != destlen) + return CHDERR_DECOMPRESSION_ERROR; + return CHDERR_NONE; +} + +/* cdlz */ +chd_error cdlz_codec_init(void* codec, uint32_t hunkbytes) +{ + cdlz_codec_data* cdlz = (cdlz_codec_data*) codec; + + /* allocate buffer */ + cdlz->buffer = (uint8_t*)malloc(sizeof(uint8_t) * hunkbytes); + + /* make sure the CHD's hunk size is an even multiple of the frame size */ + lzma_codec_init(&cdlz->base_decompressor, (hunkbytes / CD_FRAME_SIZE) * CD_MAX_SECTOR_DATA); + zlib_codec_init(&cdlz->subcode_decompressor, (hunkbytes / CD_FRAME_SIZE) * CD_MAX_SUBCODE_DATA); + + if (hunkbytes % CD_FRAME_SIZE != 0) + return CHDERR_CODEC_ERROR; + + return CHDERR_NONE; +} + +void cdlz_codec_free(void* codec) +{ + /* TODO */ +} + +chd_error cdlz_codec_decompress(void *codec, const uint8_t *src, uint32_t complen, uint8_t *dest, uint32_t destlen) +{ + uint8_t *sector; + cdlz_codec_data* cdlz = (cdlz_codec_data*)codec; + + /* determine header bytes */ + uint32_t frames = destlen / CD_FRAME_SIZE; + uint32_t complen_bytes = (destlen < 65536) ? 2 : 3; + uint32_t ecc_bytes = (frames + 7) / 8; + uint32_t header_bytes = ecc_bytes + complen_bytes; + + /* extract compressed length of base */ + uint32_t complen_base = (src[ecc_bytes + 0] << 8) | src[ecc_bytes + 1]; + if (complen_bytes > 2) + complen_base = (complen_base << 8) | src[ecc_bytes + 2]; + + /* reset and decode */ + lzma_codec_decompress(&cdlz->base_decompressor, &src[header_bytes], complen_base, &cdlz->buffer[0], frames * CD_MAX_SECTOR_DATA); + zlib_codec_decompress(&cdlz->subcode_decompressor, &src[header_bytes + complen_base], complen - complen_base - header_bytes, &cdlz->buffer[frames * CD_MAX_SECTOR_DATA], frames * CD_MAX_SUBCODE_DATA); + + /* reassemble the data */ + for (uint32_t framenum = 0; framenum < frames; framenum++) + { + memcpy(&dest[framenum * CD_FRAME_SIZE], &cdlz->buffer[framenum * CD_MAX_SECTOR_DATA], CD_MAX_SECTOR_DATA); + memcpy(&dest[framenum * CD_FRAME_SIZE + CD_MAX_SECTOR_DATA], &cdlz->buffer[frames * CD_MAX_SECTOR_DATA + framenum * CD_MAX_SUBCODE_DATA], CD_MAX_SUBCODE_DATA); + + /* reconstitute the ECC data and sync header */ + sector = (uint8_t *)&dest[framenum * CD_FRAME_SIZE]; + if ((src[framenum / 8] & (1 << (framenum % 8))) != 0) + { + memcpy(sector, s_cd_sync_header, sizeof(s_cd_sync_header)); + ecc_generate(sector); + } + } + return CHDERR_NONE; +} + +/* cdzl */ + +chd_error cdzl_codec_init(void *codec, uint32_t hunkbytes) +{ + cdzl_codec_data* cdzl = (cdzl_codec_data*)codec; + + /* make sure the CHD's hunk size is an even multiple of the frame size */ + zlib_codec_init(&cdzl->base_decompressor, (hunkbytes / CD_FRAME_SIZE) * CD_MAX_SECTOR_DATA); + zlib_codec_init(&cdzl->subcode_decompressor, (hunkbytes / CD_FRAME_SIZE) * CD_MAX_SUBCODE_DATA); + + cdzl->buffer = (uint8_t*)malloc(sizeof(uint8_t) * hunkbytes); + if (hunkbytes % CD_FRAME_SIZE != 0) + return CHDERR_CODEC_ERROR; + + return CHDERR_NONE; +} + +void cdzl_codec_free(void *codec) +{ + /* TODO */ +} + +chd_error cdzl_codec_decompress(void *codec, const uint8_t *src, uint32_t complen, uint8_t *dest, uint32_t destlen) +{ + uint8_t *sector; + cdzl_codec_data* cdzl = (cdzl_codec_data*)codec; + + /* determine header bytes */ + uint32_t frames = destlen / CD_FRAME_SIZE; + uint32_t complen_bytes = (destlen < 65536) ? 2 : 3; + uint32_t ecc_bytes = (frames + 7) / 8; + uint32_t header_bytes = ecc_bytes + complen_bytes; + + /* extract compressed length of base */ + uint32_t complen_base = (src[ecc_bytes + 0] << 8) | src[ecc_bytes + 1]; + if (complen_bytes > 2) + complen_base = (complen_base << 8) | src[ecc_bytes + 2]; + + /* reset and decode */ + zlib_codec_decompress(&cdzl->base_decompressor, &src[header_bytes], complen_base, &cdzl->buffer[0], frames * CD_MAX_SECTOR_DATA); + zlib_codec_decompress(&cdzl->subcode_decompressor, &src[header_bytes + complen_base], complen - complen_base - header_bytes, &cdzl->buffer[frames * CD_MAX_SECTOR_DATA], frames * CD_MAX_SUBCODE_DATA); + + /* reassemble the data */ + for (uint32_t framenum = 0; framenum < frames; framenum++) + { + memcpy(&dest[framenum * CD_FRAME_SIZE], &cdzl->buffer[framenum * CD_MAX_SECTOR_DATA], CD_MAX_SECTOR_DATA); + memcpy(&dest[framenum * CD_FRAME_SIZE + CD_MAX_SECTOR_DATA], &cdzl->buffer[frames * CD_MAX_SECTOR_DATA + framenum * CD_MAX_SUBCODE_DATA], CD_MAX_SUBCODE_DATA); + + /* reconstitute the ECC data and sync header */ + sector = (uint8_t *)&dest[framenum * CD_FRAME_SIZE]; + if ((src[framenum / 8] & (1 << (framenum % 8))) != 0) + { + memcpy(sector, s_cd_sync_header, sizeof(s_cd_sync_header)); + ecc_generate(sector); + } + } + return CHDERR_NONE; +} + +/*************************************************************************** + * CD FLAC DECOMPRESSOR + *************************************************************************** + */ + +/*------------------------------------------------------ + * cdfl_codec_blocksize - return the optimal block size + *------------------------------------------------------ + */ + +static uint32_t cdfl_codec_blocksize(uint32_t bytes) +{ + /* determine FLAC block size, which must be 16-65535 + * clamp to 2k since that's supposed to be the sweet spot */ + uint32_t hunkbytes = bytes / 4; + while (hunkbytes > 2048) + hunkbytes /= 2; + return hunkbytes; +} + +chd_error cdfl_codec_init(void *codec, uint32_t hunkbytes) +{ + cdfl_codec_data *cdfl = (cdfl_codec_data*)codec; + + cdfl->buffer = (uint8_t*)malloc(sizeof(uint8_t) * hunkbytes); + + /* make sure the CHD's hunk size is an even multiple of the frame size */ + if (hunkbytes % CD_FRAME_SIZE != 0) + return CHDERR_CODEC_ERROR; + + /* determine whether we want native or swapped samples */ + uint16_t native_endian = 0; + *(uint8_t *)(&native_endian) = 1; + cdfl->swap_endian = (native_endian & 1); + + /* init the inflater */ + cdfl->inflater.next_in = (Bytef *)cdfl; /* bogus, but that's ok */ + cdfl->inflater.avail_in = 0; +#if 0 + cdfl->allocator.install(cdfl->inflater); +#endif + cdfl->inflater.zalloc = zlib_fast_alloc; + cdfl->inflater.zfree = zlib_fast_free; + cdfl->inflater.opaque = &cdfl->allocator; + int zerr = inflateInit2(&cdfl->inflater, -MAX_WBITS); + + /* convert errors */ + if (zerr == Z_MEM_ERROR) + return CHDERR_OUT_OF_MEMORY; + else if (zerr != Z_OK) + return CHDERR_CODEC_ERROR; + + /* flac decoder init */ + flac_decoder_init(&cdfl->decoder); + return CHDERR_NONE; +} + +void cdfl_codec_free(void *codec) +{ + cdfl_codec_data *cdfl = (cdfl_codec_data*)codec; + inflateEnd(&cdfl->inflater); +} + +chd_error cdfl_codec_decompress(void *codec, const uint8_t *src, uint32_t complen, uint8_t *dest, uint32_t destlen) +{ + cdfl_codec_data *cdfl = (cdfl_codec_data*)codec; + + /* reset and decode */ + uint32_t frames = destlen / CD_FRAME_SIZE; + + if (!flac_decoder_reset(&cdfl->decoder, 44100, 2, cdfl_codec_blocksize(frames * CD_MAX_SECTOR_DATA), src, complen)) + return CHDERR_DECOMPRESSION_ERROR; + uint8_t *buffer = &cdfl->buffer[0]; + if (!flac_decoder_decode_interleaved(&cdfl->decoder, (int16_t *)(buffer), frames * CD_MAX_SECTOR_DATA/4, cdfl->swap_endian)) + return CHDERR_DECOMPRESSION_ERROR; + + /* inflate the subcode data */ + uint32_t offset = flac_decoder_finish(&cdfl->decoder); + cdfl->inflater.next_in = (Bytef *)(src + offset); + cdfl->inflater.avail_in = complen - offset; + cdfl->inflater.total_in = 0; + cdfl->inflater.next_out = &cdfl->buffer[frames * CD_MAX_SECTOR_DATA]; + cdfl->inflater.avail_out = frames * CD_MAX_SUBCODE_DATA; + cdfl->inflater.total_out = 0; + int zerr = inflateReset(&cdfl->inflater); + if (zerr != Z_OK) + return CHDERR_DECOMPRESSION_ERROR; + + /* do it */ + zerr = inflate(&cdfl->inflater, Z_FINISH); + if (zerr != Z_STREAM_END) + return CHDERR_DECOMPRESSION_ERROR; + if (cdfl->inflater.total_out != frames * CD_MAX_SUBCODE_DATA) + return CHDERR_DECOMPRESSION_ERROR; + + /* reassemble the data */ + for (uint32_t framenum = 0; framenum < frames; framenum++) + { + memcpy(&dest[framenum * CD_FRAME_SIZE], &cdfl->buffer[framenum * CD_MAX_SECTOR_DATA], CD_MAX_SECTOR_DATA); + memcpy(&dest[framenum * CD_FRAME_SIZE + CD_MAX_SECTOR_DATA], &cdfl->buffer[frames * CD_MAX_SECTOR_DATA + framenum * CD_MAX_SUBCODE_DATA], CD_MAX_SUBCODE_DATA); + } + + return CHDERR_NONE; +} +/*************************************************************************** + CODEC INTERFACES +***************************************************************************/ + +static const codec_interface codec_interfaces[] = +{ + /* "none" or no compression */ + { + CHDCOMPRESSION_NONE, + "none", + FALSE, + NULL, + NULL, + NULL, + NULL + }, + + /* standard zlib compression */ + { + CHDCOMPRESSION_ZLIB, + "zlib", + FALSE, + zlib_codec_init, + zlib_codec_free, + zlib_codec_decompress, + NULL + }, + + /* zlib+ compression */ + { + CHDCOMPRESSION_ZLIB_PLUS, + "zlib+", + FALSE, + zlib_codec_init, + zlib_codec_free, + zlib_codec_decompress, + NULL + }, + + /* V5 zlib compression */ + { + CHD_CODEC_ZLIB, + "zlib (Deflate)", + FALSE, + zlib_codec_init, + zlib_codec_free, + zlib_codec_decompress, + NULL + }, + + /* V5 CD zlib compression */ + { + CHD_CODEC_CD_ZLIB, + "cdzl (CD Deflate)", + FALSE, + cdzl_codec_init, + cdzl_codec_free, + cdzl_codec_decompress, + NULL + }, + + /* V5 CD lzma compression */ + { + CHD_CODEC_CD_LZMA, + "cdlz (CD LZMA)", + FALSE, + cdlz_codec_init, + cdlz_codec_free, + cdlz_codec_decompress, + NULL + }, + + /* V5 CD flac compression */ + { + CHD_CODEC_CD_FLAC, + "cdfl (CD FLAC)", + FALSE, + cdfl_codec_init, + cdfl_codec_free, + cdfl_codec_decompress, + NULL + }, +}; + +/*************************************************************************** + INLINE FUNCTIONS +***************************************************************************/ + +/*------------------------------------------------- + get_bigendian_uint64 - fetch a UINT64 from + the data stream in bigendian order +-------------------------------------------------*/ + +static inline UINT64 get_bigendian_uint64(const UINT8 *base) +{ + return ((UINT64)base[0] << 56) | ((UINT64)base[1] << 48) | ((UINT64)base[2] << 40) | ((UINT64)base[3] << 32) | + ((UINT64)base[4] << 24) | ((UINT64)base[5] << 16) | ((UINT64)base[6] << 8) | (UINT64)base[7]; +} + +/*------------------------------------------------- + put_bigendian_uint64 - write a UINT64 to + the data stream in bigendian order +-------------------------------------------------*/ + +static inline void put_bigendian_uint64(UINT8 *base, UINT64 value) +{ + base[0] = value >> 56; + base[1] = value >> 48; + base[2] = value >> 40; + base[3] = value >> 32; + base[4] = value >> 24; + base[5] = value >> 16; + base[6] = value >> 8; + base[7] = value; +} + +/*------------------------------------------------- + get_bigendian_uint48 - fetch a UINT48 from + the data stream in bigendian order +-------------------------------------------------*/ + +static inline UINT64 get_bigendian_uint48(const UINT8 *base) +{ + return ((UINT64)base[0] << 40) | ((UINT64)base[1] << 32) | + ((UINT64)base[2] << 24) | ((UINT64)base[3] << 16) | ((UINT64)base[4] << 8) | (UINT64)base[5]; +} + +/*------------------------------------------------- + put_bigendian_uint48 - write a UINT48 to + the data stream in bigendian order +-------------------------------------------------*/ + +static inline void put_bigendian_uint48(UINT8 *base, UINT64 value) +{ + value &= 0xffffffffffff; + base[0] = value >> 40; + base[1] = value >> 32; + base[2] = value >> 24; + base[3] = value >> 16; + base[4] = value >> 8; + base[5] = value; +} +/*------------------------------------------------- + get_bigendian_uint32 - fetch a UINT32 from + the data stream in bigendian order +-------------------------------------------------*/ + +static inline UINT32 get_bigendian_uint32(const UINT8 *base) +{ + return (base[0] << 24) | (base[1] << 16) | (base[2] << 8) | base[3]; +} + +/*------------------------------------------------- + put_bigendian_uint32 - write a UINT32 to + the data stream in bigendian order +-------------------------------------------------*/ + +static inline void put_bigendian_uint32(UINT8 *base, UINT32 value) +{ + base[0] = value >> 24; + base[1] = value >> 16; + base[2] = value >> 8; + base[3] = value; +} + +/*------------------------------------------------- + put_bigendian_uint24 - write a UINT24 to + the data stream in bigendian order +-------------------------------------------------*/ + +static inline void put_bigendian_uint24(UINT8 *base, UINT32 value) +{ + value &= 0xffffff; + base[0] = value >> 16; + base[1] = value >> 8; + base[2] = value; +} + +/*------------------------------------------------- + get_bigendian_uint24 - fetch a UINT24 from + the data stream in bigendian order +-------------------------------------------------*/ + +static inline UINT32 get_bigendian_uint24(const UINT8 *base) +{ + return (base[0] << 16) | (base[1] << 8) | base[2]; +} + +/*------------------------------------------------- + get_bigendian_uint16 - fetch a UINT16 from + the data stream in bigendian order +-------------------------------------------------*/ + +static inline UINT16 get_bigendian_uint16(const UINT8 *base) +{ + return (base[0] << 8) | base[1]; +} + +/*------------------------------------------------- + put_bigendian_uint16 - write a UINT16 to + the data stream in bigendian order +-------------------------------------------------*/ + +static inline void put_bigendian_uint16(UINT8 *base, UINT16 value) +{ + base[0] = value >> 8; + base[1] = value; +} + +/*------------------------------------------------- + map_extract - extract a single map + entry from the datastream +-------------------------------------------------*/ + +static inline void map_extract(const UINT8 *base, map_entry *entry) +{ + entry->offset = get_bigendian_uint64(&base[0]); + entry->crc = get_bigendian_uint32(&base[8]); + entry->length = get_bigendian_uint16(&base[12]) | (base[14] << 16); + entry->flags = base[15]; +} + +/*------------------------------------------------- + map_assemble - write a single map + entry to the datastream +-------------------------------------------------*/ + +static inline void map_assemble(UINT8 *base, map_entry *entry) +{ + put_bigendian_uint64(&base[0], entry->offset); + put_bigendian_uint32(&base[8], entry->crc); + put_bigendian_uint16(&base[12], entry->length); + base[14] = entry->length >> 16; + base[15] = entry->flags; +} + +/*------------------------------------------------- + map_size_v5 - calculate CHDv5 map size +-------------------------------------------------*/ +static inline int map_size_v5(chd_header* header) +{ + return header->hunkcount * header->mapentrybytes; +} + +/*------------------------------------------------- + crc16 - calculate CRC16 (from hashing.cpp) +-------------------------------------------------*/ +uint16_t crc16(const void *data, uint32_t length) +{ + uint16_t crc = 0xffff; + + static const uint16_t s_table[256] = + { + 0x0000, 0x1021, 0x2042, 0x3063, 0x4084, 0x50a5, 0x60c6, 0x70e7, + 0x8108, 0x9129, 0xa14a, 0xb16b, 0xc18c, 0xd1ad, 0xe1ce, 0xf1ef, + 0x1231, 0x0210, 0x3273, 0x2252, 0x52b5, 0x4294, 0x72f7, 0x62d6, + 0x9339, 0x8318, 0xb37b, 0xa35a, 0xd3bd, 0xc39c, 0xf3ff, 0xe3de, + 0x2462, 0x3443, 0x0420, 0x1401, 0x64e6, 0x74c7, 0x44a4, 0x5485, + 0xa56a, 0xb54b, 0x8528, 0x9509, 0xe5ee, 0xf5cf, 0xc5ac, 0xd58d, + 0x3653, 0x2672, 0x1611, 0x0630, 0x76d7, 0x66f6, 0x5695, 0x46b4, + 0xb75b, 0xa77a, 0x9719, 0x8738, 0xf7df, 0xe7fe, 0xd79d, 0xc7bc, + 0x48c4, 0x58e5, 0x6886, 0x78a7, 0x0840, 0x1861, 0x2802, 0x3823, + 0xc9cc, 0xd9ed, 0xe98e, 0xf9af, 0x8948, 0x9969, 0xa90a, 0xb92b, + 0x5af5, 0x4ad4, 0x7ab7, 0x6a96, 0x1a71, 0x0a50, 0x3a33, 0x2a12, + 0xdbfd, 0xcbdc, 0xfbbf, 0xeb9e, 0x9b79, 0x8b58, 0xbb3b, 0xab1a, + 0x6ca6, 0x7c87, 0x4ce4, 0x5cc5, 0x2c22, 0x3c03, 0x0c60, 0x1c41, + 0xedae, 0xfd8f, 0xcdec, 0xddcd, 0xad2a, 0xbd0b, 0x8d68, 0x9d49, + 0x7e97, 0x6eb6, 0x5ed5, 0x4ef4, 0x3e13, 0x2e32, 0x1e51, 0x0e70, + 0xff9f, 0xefbe, 0xdfdd, 0xcffc, 0xbf1b, 0xaf3a, 0x9f59, 0x8f78, + 0x9188, 0x81a9, 0xb1ca, 0xa1eb, 0xd10c, 0xc12d, 0xf14e, 0xe16f, + 0x1080, 0x00a1, 0x30c2, 0x20e3, 0x5004, 0x4025, 0x7046, 0x6067, + 0x83b9, 0x9398, 0xa3fb, 0xb3da, 0xc33d, 0xd31c, 0xe37f, 0xf35e, + 0x02b1, 0x1290, 0x22f3, 0x32d2, 0x4235, 0x5214, 0x6277, 0x7256, + 0xb5ea, 0xa5cb, 0x95a8, 0x8589, 0xf56e, 0xe54f, 0xd52c, 0xc50d, + 0x34e2, 0x24c3, 0x14a0, 0x0481, 0x7466, 0x6447, 0x5424, 0x4405, + 0xa7db, 0xb7fa, 0x8799, 0x97b8, 0xe75f, 0xf77e, 0xc71d, 0xd73c, + 0x26d3, 0x36f2, 0x0691, 0x16b0, 0x6657, 0x7676, 0x4615, 0x5634, + 0xd94c, 0xc96d, 0xf90e, 0xe92f, 0x99c8, 0x89e9, 0xb98a, 0xa9ab, + 0x5844, 0x4865, 0x7806, 0x6827, 0x18c0, 0x08e1, 0x3882, 0x28a3, + 0xcb7d, 0xdb5c, 0xeb3f, 0xfb1e, 0x8bf9, 0x9bd8, 0xabbb, 0xbb9a, + 0x4a75, 0x5a54, 0x6a37, 0x7a16, 0x0af1, 0x1ad0, 0x2ab3, 0x3a92, + 0xfd2e, 0xed0f, 0xdd6c, 0xcd4d, 0xbdaa, 0xad8b, 0x9de8, 0x8dc9, + 0x7c26, 0x6c07, 0x5c64, 0x4c45, 0x3ca2, 0x2c83, 0x1ce0, 0x0cc1, + 0xef1f, 0xff3e, 0xcf5d, 0xdf7c, 0xaf9b, 0xbfba, 0x8fd9, 0x9ff8, + 0x6e17, 0x7e36, 0x4e55, 0x5e74, 0x2e93, 0x3eb2, 0x0ed1, 0x1ef0 + }; + + const uint8_t *src = (uint8_t*)data; + + /* fetch the current value into a local and rip through the source data */ + while (length-- != 0) + crc = (crc << 8) ^ s_table[(crc >> 8) ^ *src++]; + return crc; +} + +/*------------------------------------------------- + compressed - test if CHD file is compressed ++-------------------------------------------------*/ + +static inline int compressed(chd_header* header) { + return header->compression[0] != CHD_CODEC_NONE; +} + +/*------------------------------------------------- + decompress_v5_map - decompress the v5 map +-------------------------------------------------*/ + +static chd_error decompress_v5_map(chd_file* chd, chd_header* header) +{ + int rawmapsize = map_size_v5(header); + + if (!compressed(header)) + { + header->rawmap = (uint8_t*)malloc(rawmapsize); + core_fseek(chd->file, header->mapoffset, SEEK_SET); + core_fread(chd->file, header->rawmap, rawmapsize); + return CHDERR_NONE; + } + + /* read the reader */ + uint8_t rawbuf[16]; + core_fseek(chd->file, header->mapoffset, SEEK_SET); + core_fread(chd->file, rawbuf, sizeof(rawbuf)); + uint32_t const mapbytes = get_bigendian_uint32(&rawbuf[0]); + uint64_t const firstoffs = get_bigendian_uint48(&rawbuf[4]); + uint16_t const mapcrc = get_bigendian_uint16(&rawbuf[10]); + uint8_t const lengthbits = rawbuf[12]; + uint8_t const selfbits = rawbuf[13]; + uint8_t const parentbits = rawbuf[14]; + + /* now read the map */ + uint8_t* compressed = (uint8_t*)malloc(sizeof(uint8_t) * mapbytes); + core_fseek(chd->file, header->mapoffset + 16, SEEK_SET); + core_fread(chd->file, compressed, mapbytes); + struct bitstream* bitbuf = create_bitstream(compressed, sizeof(uint8_t) * mapbytes); + header->rawmap = (uint8_t*)malloc(rawmapsize); + + /* first decode the compression types */ + struct huffman_decoder* decoder = create_huffman_decoder(16, 8); + enum huffman_error err = huffman_import_tree_rle(decoder, bitbuf); + if (err != HUFFERR_NONE) + return CHDERR_DECOMPRESSION_ERROR; + uint8_t lastcomp = 0; + int repcount = 0; + for (int hunknum = 0; hunknum < header->hunkcount; hunknum++) + { + uint8_t *rawmap = header->rawmap + (hunknum * 12); + if (repcount > 0) + rawmap[0] = lastcomp, repcount--; + else + { + uint8_t val = huffman_decode_one(decoder, bitbuf); + if (val == COMPRESSION_RLE_SMALL) + rawmap[0] = lastcomp, repcount = 2 + huffman_decode_one(decoder, bitbuf); + else if (val == COMPRESSION_RLE_LARGE) + rawmap[0] = lastcomp, repcount = 2 + 16 + (huffman_decode_one(decoder, bitbuf) << 4), repcount += huffman_decode_one(decoder, bitbuf); + else + rawmap[0] = lastcomp = val; + } + } + + /* then iterate through the hunks and extract the needed data */ + uint64_t curoffset = firstoffs; + uint32_t last_self = 0; + uint64_t last_parent = 0; + for (int hunknum = 0; hunknum < header->hunkcount; hunknum++) + { + uint8_t *rawmap = header->rawmap + (hunknum * 12); + uint64_t offset = curoffset; + uint32_t length = 0; + uint16_t crc = 0; + switch (rawmap[0]) + { + /* base types */ + case COMPRESSION_TYPE_0: + case COMPRESSION_TYPE_1: + case COMPRESSION_TYPE_2: + case COMPRESSION_TYPE_3: + curoffset += length = bitstream_read(bitbuf, lengthbits); + crc = bitstream_read(bitbuf, 16); + break; + + case COMPRESSION_NONE: + curoffset += length = header->hunkbytes; + crc = bitstream_read(bitbuf, 16); + break; + + case COMPRESSION_SELF: + last_self = offset = bitstream_read(bitbuf, selfbits); + break; + + case COMPRESSION_PARENT: + offset = bitstream_read(bitbuf, parentbits); + last_parent = offset; + break; + + /* pseudo-types; convert into base types */ + case COMPRESSION_SELF_1: + last_self++; + case COMPRESSION_SELF_0: + rawmap[0] = COMPRESSION_SELF; + offset = last_self; + break; + + case COMPRESSION_PARENT_SELF: + rawmap[0] = COMPRESSION_PARENT; + last_parent = offset = ( ((uint64_t)hunknum) * ((uint64_t)header->hunkbytes) ) / header->unitbytes; + break; + + case COMPRESSION_PARENT_1: + last_parent += header->hunkbytes / header->unitbytes; + case COMPRESSION_PARENT_0: + rawmap[0] = COMPRESSION_PARENT; + offset = last_parent; + break; + } + /* UINT24 length */ + put_bigendian_uint24(&rawmap[1], length); + + /* UINT48 offset */ + put_bigendian_uint48(&rawmap[4], offset); + + /* crc16 */ + put_bigendian_uint16(&rawmap[10], crc); + } + + /* verify the final CRC */ + if (crc16(&header->rawmap[0], header->hunkcount * 12) != mapcrc) + return CHDERR_DECOMPRESSION_ERROR; + + return CHDERR_NONE; +} + +/*------------------------------------------------- + map_extract_old - extract a single map + entry in old format from the datastream +-------------------------------------------------*/ + +static inline void map_extract_old(const UINT8 *base, map_entry *entry, UINT32 hunkbytes) +{ + entry->offset = get_bigendian_uint64(&base[0]); + entry->crc = 0; + entry->length = entry->offset >> 44; + entry->flags = MAP_ENTRY_FLAG_NO_CRC | ((entry->length == hunkbytes) ? V34_MAP_ENTRY_TYPE_UNCOMPRESSED : V34_MAP_ENTRY_TYPE_COMPRESSED); +#ifdef __MWERKS__ + entry->offset = entry->offset & 0x00000FFFFFFFFFFFLL; +#else + entry->offset = (entry->offset << 20) >> 20; +#endif +} + +/*************************************************************************** + CHD FILE MANAGEMENT +***************************************************************************/ + +/*------------------------------------------------- + chd_open_file - open a CHD file for access +-------------------------------------------------*/ + +chd_error chd_open_file(core_file *file, int mode, chd_file *parent, chd_file **chd) +{ + chd_file *newchd = NULL; + chd_error err; + int intfnum; + + /* verify parameters */ + if (file == NULL) + EARLY_EXIT(err = CHDERR_INVALID_PARAMETER); + + /* punt if invalid parent */ + if (parent != NULL && parent->cookie != COOKIE_VALUE) + EARLY_EXIT(err = CHDERR_INVALID_PARAMETER); + + /* allocate memory for the final result */ + newchd = (chd_file *)malloc(sizeof(**chd)); + if (newchd == NULL) + EARLY_EXIT(err = CHDERR_OUT_OF_MEMORY); + memset(newchd, 0, sizeof(*newchd)); + newchd->cookie = COOKIE_VALUE; + newchd->parent = parent; + newchd->file = file; + + /* now attempt to read the header */ + err = header_read(newchd, &newchd->header); + if (err != CHDERR_NONE) + EARLY_EXIT(err); + + /* validate the header */ + err = header_validate(&newchd->header); + if (err != CHDERR_NONE) + EARLY_EXIT(err); + + /* make sure we don't open a read-only file writeable */ + if (mode == CHD_OPEN_READWRITE && !(newchd->header.flags & CHDFLAGS_IS_WRITEABLE)) + EARLY_EXIT(err = CHDERR_FILE_NOT_WRITEABLE); + + /* also, never open an older version writeable */ + if (mode == CHD_OPEN_READWRITE && newchd->header.version < CHD_HEADER_VERSION) + EARLY_EXIT(err = CHDERR_UNSUPPORTED_VERSION); + + /* if we need a parent, make sure we have one */ + if (parent == NULL && (newchd->header.flags & CHDFLAGS_HAS_PARENT)) + EARLY_EXIT(err = CHDERR_REQUIRES_PARENT); + + /* make sure we have a valid parent */ + if (parent != NULL) + { + /* check MD5 if it isn't empty */ + if (memcmp(nullmd5, newchd->header.parentmd5, sizeof(newchd->header.parentmd5)) != 0 && + memcmp(nullmd5, newchd->parent->header.md5, sizeof(newchd->parent->header.md5)) != 0 && + memcmp(newchd->parent->header.md5, newchd->header.parentmd5, sizeof(newchd->header.parentmd5)) != 0) + EARLY_EXIT(err = CHDERR_INVALID_PARENT); + + /* check SHA1 if it isn't empty */ + if (memcmp(nullsha1, newchd->header.parentsha1, sizeof(newchd->header.parentsha1)) != 0 && + memcmp(nullsha1, newchd->parent->header.sha1, sizeof(newchd->parent->header.sha1)) != 0 && + memcmp(newchd->parent->header.sha1, newchd->header.parentsha1, sizeof(newchd->header.parentsha1)) != 0) + EARLY_EXIT(err = CHDERR_INVALID_PARENT); + } + + /* now read the hunk map */ + if (newchd->header.version < 5) + { + err = map_read(newchd); + } + else + { + err = decompress_v5_map(newchd, &(newchd->header)); + } + if (err != CHDERR_NONE) + EARLY_EXIT(err); + + + /* allocate and init the hunk cache */ + newchd->cache = (UINT8 *)malloc(newchd->header.hunkbytes); + newchd->compare = (UINT8 *)malloc(newchd->header.hunkbytes); + if (newchd->cache == NULL || newchd->compare == NULL) + EARLY_EXIT(err = CHDERR_OUT_OF_MEMORY); + newchd->cachehunk = ~0; + newchd->comparehunk = ~0; + + /* allocate the temporary compressed buffer */ + newchd->compressed = (UINT8 *)malloc(newchd->header.hunkbytes); + if (newchd->compressed == NULL) + EARLY_EXIT(err = CHDERR_OUT_OF_MEMORY); + + /* find the codec interface */ + if (newchd->header.version < 5) + { + for (intfnum = 0; intfnum < ARRAY_LENGTH(codec_interfaces); intfnum++) + { + if (codec_interfaces[intfnum].compression == newchd->header.compression[0]) + { + newchd->codecintf[0] = &codec_interfaces[intfnum]; + break; + } + } + + if (intfnum == ARRAY_LENGTH(codec_interfaces)) + EARLY_EXIT(err = CHDERR_UNSUPPORTED_FORMAT); + + /* initialize the codec */ + if (newchd->codecintf[0]->init != NULL) + { + err = (*newchd->codecintf[0]->init)(&newchd->zlib_codec_data, newchd->header.hunkbytes); + if (err != CHDERR_NONE) + EARLY_EXIT(err); + } + } + else + { + /* verify the compression types and initialize the codecs */ + for (int decompnum = 0; decompnum < ARRAY_LENGTH(newchd->header.compression); decompnum++) + { + for (int i = 0 ; i < ARRAY_LENGTH(codec_interfaces) ; i++) + { + if (codec_interfaces[i].compression == newchd->header.compression[decompnum]) + { + newchd->codecintf[decompnum] = &codec_interfaces[i]; + break; + } + } + + if (newchd->codecintf[decompnum] == NULL && newchd->header.compression[decompnum] != 0) + EARLY_EXIT(err = CHDERR_UNSUPPORTED_FORMAT); + + /* initialize the codec */ + if (newchd->codecintf[decompnum]->init != NULL) + { + void* codec = NULL; + switch (newchd->header.compression[decompnum]) + { + case CHD_CODEC_ZLIB: + codec = &newchd->zlib_codec_data; + break; + + case CHD_CODEC_CD_ZLIB: + codec = &newchd->cdzl_codec_data; + break; + + case CHD_CODEC_CD_LZMA: + codec = &newchd->cdlz_codec_data; + break; + + case CHD_CODEC_CD_FLAC: + codec = &newchd->cdfl_codec_data; + break; + } + + if (codec == NULL) + EARLY_EXIT(err = CHDERR_UNSUPPORTED_FORMAT); + + err = (*newchd->codecintf[decompnum]->init)(codec, newchd->header.hunkbytes); + if (err != CHDERR_NONE) + EARLY_EXIT(err); + } + } + } + + /* all done */ + *chd = newchd; + return CHDERR_NONE; + +cleanup: + if (newchd != NULL) + chd_close(newchd); + return err; +} + +/*------------------------------------------------- + chd_open - open a CHD file by + filename +-------------------------------------------------*/ + +chd_error chd_open(const char *filename, int mode, chd_file *parent, chd_file **chd) +{ + chd_error err; + core_file *file = NULL; + UINT32 openflags; + + /* choose the proper mode */ + switch(mode) + { + case CHD_OPEN_READ: + break; + + default: + err = CHDERR_INVALID_PARAMETER; + goto cleanup; + } + + /* open the file */ + file = core_fopen(filename); + if (file == 0) + { + err = CHDERR_FILE_NOT_FOUND; + goto cleanup; + } + + /* now open the CHD */ + err = chd_open_file(file, mode, parent, chd); + if (err != CHDERR_NONE) + goto cleanup; + + /* we now own this file */ + (*chd)->owns_file = TRUE; + +cleanup: + if ((err != CHDERR_NONE) && (file != NULL)) + core_fclose(file); + return err; +} + +/*------------------------------------------------- + chd_close - close a CHD file for access +-------------------------------------------------*/ + +void chd_close(chd_file *chd) +{ + /* punt if NULL or invalid */ + if (chd == NULL || chd->cookie != COOKIE_VALUE) + return; + + /* deinit the codec */ + if (chd->header.version < 5) + { + if (chd->codecintf[0] != NULL && chd->codecintf[0]->free != NULL) + (*chd->codecintf[0]->free)(&chd->zlib_codec_data); + } + else + { + /* Free the codecs */ + for (int i = 0 ; i < ARRAY_LENGTH(chd->codecintf); i++) + { + void* codec = NULL; + + if (chd->codecintf[i] == NULL) + continue; + + switch (chd->codecintf[i]->compression) + { + case CHD_CODEC_CD_LZMA: + codec = &chd->cdlz_codec_data; + break; + + case CHD_CODEC_ZLIB: + codec = &chd->zlib_codec_data; + break; + + case CHD_CODEC_CD_ZLIB: + codec = &chd->cdzl_codec_data; + break; + + case CHD_CODEC_CD_FLAC: + codec = &chd->cdfl_codec_data; + break; + } + + if (codec) + { + (*chd->codecintf[i]->free)(codec); + } + } + + /* Free the raw map */ + if (chd->header.rawmap != NULL) + free(chd->header.rawmap); + } + + /* free the compressed data buffer */ + if (chd->compressed != NULL) + free(chd->compressed); + + /* free the hunk cache and compare data */ + if (chd->compare != NULL) + free(chd->compare); + if (chd->cache != NULL) + free(chd->cache); + + /* free the hunk map */ + if (chd->map != NULL) + free(chd->map); + + /* free the CRC table */ + if (chd->crctable != NULL) + free(chd->crctable); + + /* free the CRC map */ + if (chd->crcmap != NULL) + free(chd->crcmap); + + /* close the file */ + if (chd->owns_file && chd->file != NULL) + core_fclose(chd->file); + + if (PRINTF_MAX_HUNK) printf("Max hunk = %d/%d\n", chd->maxhunk, chd->header.totalhunks); + + /* free our memory */ + free(chd); +} + +/*------------------------------------------------- + chd_core_file - return the associated + core_file +-------------------------------------------------*/ + +core_file *chd_core_file(chd_file *chd) +{ + return chd->file; +} + +/*------------------------------------------------- + chd_error_string - return an error string for + the given CHD error +-------------------------------------------------*/ + +const char *chd_error_string(chd_error err) +{ + switch (err) + { + case CHDERR_NONE: return "no error"; + case CHDERR_NO_INTERFACE: return "no drive interface"; + case CHDERR_OUT_OF_MEMORY: return "out of memory"; + case CHDERR_INVALID_FILE: return "invalid file"; + case CHDERR_INVALID_PARAMETER: return "invalid parameter"; + case CHDERR_INVALID_DATA: return "invalid data"; + case CHDERR_FILE_NOT_FOUND: return "file not found"; + case CHDERR_REQUIRES_PARENT: return "requires parent"; + case CHDERR_FILE_NOT_WRITEABLE: return "file not writeable"; + case CHDERR_READ_ERROR: return "read error"; + case CHDERR_WRITE_ERROR: return "write error"; + case CHDERR_CODEC_ERROR: return "codec error"; + case CHDERR_INVALID_PARENT: return "invalid parent"; + case CHDERR_HUNK_OUT_OF_RANGE: return "hunk out of range"; + case CHDERR_DECOMPRESSION_ERROR: return "decompression error"; + case CHDERR_COMPRESSION_ERROR: return "compression error"; + case CHDERR_CANT_CREATE_FILE: return "can't create file"; + case CHDERR_CANT_VERIFY: return "can't verify file"; + case CHDERR_NOT_SUPPORTED: return "operation not supported"; + case CHDERR_METADATA_NOT_FOUND: return "can't find metadata"; + case CHDERR_INVALID_METADATA_SIZE: return "invalid metadata size"; + case CHDERR_UNSUPPORTED_VERSION: return "unsupported CHD version"; + case CHDERR_VERIFY_INCOMPLETE: return "incomplete verify"; + case CHDERR_INVALID_METADATA: return "invalid metadata"; + case CHDERR_INVALID_STATE: return "invalid state"; + case CHDERR_OPERATION_PENDING: return "operation pending"; + case CHDERR_NO_ASYNC_OPERATION: return "no async operation in progress"; + case CHDERR_UNSUPPORTED_FORMAT: return "unsupported format"; + default: return "undocumented error"; + } +} + +/*************************************************************************** + CHD HEADER MANAGEMENT +***************************************************************************/ + +/*------------------------------------------------- + chd_get_header - return a pointer to the + extracted header data +-------------------------------------------------*/ + +const chd_header *chd_get_header(chd_file *chd) +{ + /* punt if NULL or invalid */ + if (chd == NULL || chd->cookie != COOKIE_VALUE) + return NULL; + + return &chd->header; +} + +/*************************************************************************** + CORE DATA READ/WRITE +***************************************************************************/ + +/*------------------------------------------------- + chd_read - read a single hunk from the CHD + file +-------------------------------------------------*/ + +chd_error chd_read(chd_file *chd, UINT32 hunknum, void *buffer) +{ + /* punt if NULL or invalid */ + if (chd == NULL || chd->cookie != COOKIE_VALUE) + return CHDERR_INVALID_PARAMETER; + + /* if we're past the end, fail */ + if (hunknum >= chd->header.totalhunks) + return CHDERR_HUNK_OUT_OF_RANGE; + + /* perform the read */ + return hunk_read_into_memory(chd, hunknum, (UINT8 *)buffer); +} + +/*************************************************************************** + METADATA MANAGEMENT +***************************************************************************/ + +/*------------------------------------------------- + chd_get_metadata - get the indexed metadata + of the given type +-------------------------------------------------*/ + +chd_error chd_get_metadata(chd_file *chd, UINT32 searchtag, UINT32 searchindex, void *output, UINT32 outputlen, UINT32 *resultlen, UINT32 *resulttag, UINT8 *resultflags) +{ + metadata_entry metaentry; + chd_error err; + UINT32 count; + + /* if we didn't find it, just return */ + err = metadata_find_entry(chd, searchtag, searchindex, &metaentry); + if (err != CHDERR_NONE) + { + /* unless we're an old version and they are requesting hard disk metadata */ + if (chd->header.version < 3 && (searchtag == HARD_DISK_METADATA_TAG || searchtag == CHDMETATAG_WILDCARD) && searchindex == 0) + { + char faux_metadata[256]; + UINT32 faux_length; + + /* fill in the faux metadata */ + sprintf(faux_metadata, HARD_DISK_METADATA_FORMAT, chd->header.obsolete_cylinders, chd->header.obsolete_heads, chd->header.obsolete_sectors, chd->header.hunkbytes / chd->header.obsolete_hunksize); + faux_length = (UINT32)strlen(faux_metadata) + 1; + + /* copy the metadata itself */ + memcpy(output, faux_metadata, MIN(outputlen, faux_length)); + + /* return the length of the data and the tag */ + if (resultlen != NULL) + *resultlen = faux_length; + if (resulttag != NULL) + *resulttag = HARD_DISK_METADATA_TAG; + return CHDERR_NONE; + } + return err; + } + + /* read the metadata */ + outputlen = MIN(outputlen, metaentry.length); + core_fseek(chd->file, metaentry.offset + METADATA_HEADER_SIZE, SEEK_SET); + count = core_fread(chd->file, output, outputlen); + if (count != outputlen) + return CHDERR_READ_ERROR; + + /* return the length of the data and the tag */ + if (resultlen != NULL) + *resultlen = metaentry.length; + if (resulttag != NULL) + *resulttag = metaentry.metatag; + if (resultflags != NULL) + *resultflags = metaentry.flags; + return CHDERR_NONE; +} + +/*************************************************************************** + CODEC INTERFACES +***************************************************************************/ + +/*------------------------------------------------- + chd_codec_config - set internal codec + parameters +-------------------------------------------------*/ + +chd_error chd_codec_config(chd_file *chd, int param, void *config) +{ + return CHDERR_INVALID_PARAMETER; +} + +/*------------------------------------------------- + chd_get_codec_name - get the name of a + particular codec +-------------------------------------------------*/ + +const char *chd_get_codec_name(UINT32 codec) +{ + return "Unknown"; +} + +/*************************************************************************** + INTERNAL HEADER OPERATIONS +***************************************************************************/ + +/*------------------------------------------------- + header_validate - check the validity of a + CHD header +-------------------------------------------------*/ + +static chd_error header_validate(const chd_header *header) +{ + int intfnum; + + /* require a valid version */ + if (header->version == 0 || header->version > CHD_HEADER_VERSION) + return CHDERR_UNSUPPORTED_VERSION; + + /* require a valid length */ + if ((header->version == 1 && header->length != CHD_V1_HEADER_SIZE) || + (header->version == 2 && header->length != CHD_V2_HEADER_SIZE) || + (header->version == 3 && header->length != CHD_V3_HEADER_SIZE) || + (header->version == 4 && header->length != CHD_V4_HEADER_SIZE) || + (header->version == 5 && header->length != CHD_V5_HEADER_SIZE)) + return CHDERR_INVALID_PARAMETER; + + /* Do not validate v5 header */ + if (header->version <= 4) + { + /* require valid flags */ + if (header->flags & CHDFLAGS_UNDEFINED) + return CHDERR_INVALID_PARAMETER; + + /* require a supported compression mechanism */ + for (intfnum = 0; intfnum < ARRAY_LENGTH(codec_interfaces); intfnum++) + if (codec_interfaces[intfnum].compression == header->compression[0]) + break; + + if (intfnum == ARRAY_LENGTH(codec_interfaces)) + return CHDERR_INVALID_PARAMETER; + + /* require a valid hunksize */ + if (header->hunkbytes == 0 || header->hunkbytes >= 65536 * 256) + return CHDERR_INVALID_PARAMETER; + + /* require a valid hunk count */ + if (header->totalhunks == 0) + return CHDERR_INVALID_PARAMETER; + + /* require a valid MD5 and/or SHA1 if we're using a parent */ + if ((header->flags & CHDFLAGS_HAS_PARENT) && memcmp(header->parentmd5, nullmd5, sizeof(nullmd5)) == 0 && memcmp(header->parentsha1, nullsha1, sizeof(nullsha1)) == 0) + return CHDERR_INVALID_PARAMETER; + + /* if we're V3 or later, the obsolete fields must be 0 */ + if (header->version >= 3 && + (header->obsolete_cylinders != 0 || header->obsolete_sectors != 0 || + header->obsolete_heads != 0 || header->obsolete_hunksize != 0)) + return CHDERR_INVALID_PARAMETER; + + /* if we're pre-V3, the obsolete fields must NOT be 0 */ + if (header->version < 3 && + (header->obsolete_cylinders == 0 || header->obsolete_sectors == 0 || + header->obsolete_heads == 0 || header->obsolete_hunksize == 0)) + return CHDERR_INVALID_PARAMETER; + } + + return CHDERR_NONE; +} + +/*------------------------------------------------- + header_guess_unitbytes - for older CHD formats, + guess at the bytes/unit based on metadata +-------------------------------------------------*/ + +static UINT32 header_guess_unitbytes(chd_file *chd) +{ + /* look for hard disk metadata; if found, then the unit size == sector size */ + char metadata[512]; + int i0, i1, i2, i3; + if (chd_get_metadata(chd, HARD_DISK_METADATA_TAG, 0, metadata, sizeof(metadata), NULL, NULL, NULL) == CHDERR_NONE && + sscanf(metadata, HARD_DISK_METADATA_FORMAT, &i0, &i1, &i2, &i3) == 4) + return i3; + + /* look for CD-ROM metadata; if found, then the unit size == CD frame size */ + if (chd_get_metadata(chd, CDROM_OLD_METADATA_TAG, 0, metadata, sizeof(metadata), NULL, NULL, NULL) == CHDERR_NONE || + chd_get_metadata(chd, CDROM_TRACK_METADATA_TAG, 0, metadata, sizeof(metadata), NULL, NULL, NULL) == CHDERR_NONE || + chd_get_metadata(chd, CDROM_TRACK_METADATA2_TAG, 0, metadata, sizeof(metadata), NULL, NULL, NULL) == CHDERR_NONE || + chd_get_metadata(chd, GDROM_OLD_METADATA_TAG, 0, metadata, sizeof(metadata), NULL, NULL, NULL) == CHDERR_NONE || + chd_get_metadata(chd, GDROM_TRACK_METADATA_TAG, 0, metadata, sizeof(metadata), NULL, NULL, NULL) == CHDERR_NONE) + return CD_FRAME_SIZE; + + /* otherwise, just map 1:1 with the hunk size */ + return chd->header.hunkbytes; +} + +/*------------------------------------------------- + header_read - read a CHD header into the + internal data structure +-------------------------------------------------*/ + +static chd_error header_read(chd_file *chd, chd_header *header) +{ + UINT8 rawheader[CHD_MAX_HEADER_SIZE]; + UINT32 count; + + /* punt if NULL */ + if (header == NULL) + return CHDERR_INVALID_PARAMETER; + + /* punt if invalid file */ + if (chd->file == NULL) + return CHDERR_INVALID_FILE; + + /* seek and read */ + core_fseek(chd->file, 0, SEEK_SET); + count = core_fread(chd->file, rawheader, sizeof(rawheader)); + if (count != sizeof(rawheader)) + return CHDERR_READ_ERROR; + + /* verify the tag */ + if (strncmp((char *)rawheader, "MComprHD", 8) != 0) + return CHDERR_INVALID_DATA; + + /* extract the direct data */ + memset(header, 0, sizeof(*header)); + header->length = get_bigendian_uint32(&rawheader[8]); + header->version = get_bigendian_uint32(&rawheader[12]); + + /* make sure it's a version we understand */ + if (header->version == 0 || header->version > CHD_HEADER_VERSION) + return CHDERR_UNSUPPORTED_VERSION; + + /* make sure the length is expected */ + if ((header->version == 1 && header->length != CHD_V1_HEADER_SIZE) || + (header->version == 2 && header->length != CHD_V2_HEADER_SIZE) || + (header->version == 3 && header->length != CHD_V3_HEADER_SIZE) || + (header->version == 4 && header->length != CHD_V4_HEADER_SIZE) || + (header->version == 5 && header->length != CHD_V5_HEADER_SIZE)) + + return CHDERR_INVALID_DATA; + + /* extract the common data */ + header->flags = get_bigendian_uint32(&rawheader[16]); + header->compression[0] = get_bigendian_uint32(&rawheader[20]); + header->compression[1] = CHD_CODEC_NONE; + header->compression[2] = CHD_CODEC_NONE; + header->compression[3] = CHD_CODEC_NONE; + + /* extract the V1/V2-specific data */ + if (header->version < 3) + { + int seclen = (header->version == 1) ? CHD_V1_SECTOR_SIZE : get_bigendian_uint32(&rawheader[76]); + header->obsolete_hunksize = get_bigendian_uint32(&rawheader[24]); + header->totalhunks = get_bigendian_uint32(&rawheader[28]); + header->obsolete_cylinders = get_bigendian_uint32(&rawheader[32]); + header->obsolete_heads = get_bigendian_uint32(&rawheader[36]); + header->obsolete_sectors = get_bigendian_uint32(&rawheader[40]); + memcpy(header->md5, &rawheader[44], CHD_MD5_BYTES); + memcpy(header->parentmd5, &rawheader[60], CHD_MD5_BYTES); + header->logicalbytes = (UINT64)header->obsolete_cylinders * (UINT64)header->obsolete_heads * (UINT64)header->obsolete_sectors * (UINT64)seclen; + header->hunkbytes = seclen * header->obsolete_hunksize; + header->unitbytes = header_guess_unitbytes(chd); + header->unitcount = (header->logicalbytes + header->unitbytes - 1) / header->unitbytes; + header->metaoffset = 0; + } + + /* extract the V3-specific data */ + else if (header->version == 3) + { + header->totalhunks = get_bigendian_uint32(&rawheader[24]); + header->logicalbytes = get_bigendian_uint64(&rawheader[28]); + header->metaoffset = get_bigendian_uint64(&rawheader[36]); + memcpy(header->md5, &rawheader[44], CHD_MD5_BYTES); + memcpy(header->parentmd5, &rawheader[60], CHD_MD5_BYTES); + header->hunkbytes = get_bigendian_uint32(&rawheader[76]); + header->unitbytes = header_guess_unitbytes(chd); + header->unitcount = (header->logicalbytes + header->unitbytes - 1) / header->unitbytes; + memcpy(header->sha1, &rawheader[80], CHD_SHA1_BYTES); + memcpy(header->parentsha1, &rawheader[100], CHD_SHA1_BYTES); + } + + /* extract the V4-specific data */ + else if (header->version == 4) + { + header->totalhunks = get_bigendian_uint32(&rawheader[24]); + header->logicalbytes = get_bigendian_uint64(&rawheader[28]); + header->metaoffset = get_bigendian_uint64(&rawheader[36]); + header->hunkbytes = get_bigendian_uint32(&rawheader[44]); + header->unitbytes = header_guess_unitbytes(chd); + header->unitcount = (header->logicalbytes + header->unitbytes - 1) / header->unitbytes; + memcpy(header->sha1, &rawheader[48], CHD_SHA1_BYTES); + memcpy(header->parentsha1, &rawheader[68], CHD_SHA1_BYTES); + memcpy(header->rawsha1, &rawheader[88], CHD_SHA1_BYTES); + } + + /* extract the V5-specific data */ + else if (header->version == 5) + { + /* TODO */ + header->compression[0] = get_bigendian_uint32(&rawheader[16]); + header->compression[1] = get_bigendian_uint32(&rawheader[20]); + header->compression[2] = get_bigendian_uint32(&rawheader[24]); + header->compression[3] = get_bigendian_uint32(&rawheader[28]); + header->logicalbytes = get_bigendian_uint64(&rawheader[32]); + header->mapoffset = get_bigendian_uint64(&rawheader[40]); + header->metaoffset = get_bigendian_uint64(&rawheader[48]); + header->hunkbytes = get_bigendian_uint32(&rawheader[56]); + header->hunkcount = (header->logicalbytes + header->hunkbytes - 1) / header->hunkbytes; + header->unitbytes = get_bigendian_uint32(&rawheader[60]); + header->unitcount = (header->logicalbytes + header->unitbytes - 1) / header->unitbytes; + memcpy(header->sha1, &rawheader[84], CHD_SHA1_BYTES); + memcpy(header->parentsha1, &rawheader[104], CHD_SHA1_BYTES); + memcpy(header->rawsha1, &rawheader[64], CHD_SHA1_BYTES); + + /* determine properties of map entries */ + header->mapentrybytes = compressed(header) ? 12 : 4; + + /* hack */ + header->totalhunks = header->hunkcount; + } + + /* Unknown version */ + else + { + /* TODO */ + } + + /* guess it worked */ + return CHDERR_NONE; +} + +/*************************************************************************** + INTERNAL HUNK READ/WRITE +***************************************************************************/ + +/*------------------------------------------------- + hunk_read_into_cache - read a hunk into + the CHD's hunk cache +-------------------------------------------------*/ + +static chd_error hunk_read_into_cache(chd_file *chd, UINT32 hunknum) +{ + chd_error err; + + /* track the max */ + if (hunknum > chd->maxhunk) + chd->maxhunk = hunknum; + + /* if we're already in the cache, we're done */ + if (chd->cachehunk == hunknum) + return CHDERR_NONE; + chd->cachehunk = ~0; + + /* otherwise, read the data */ + err = hunk_read_into_memory(chd, hunknum, chd->cache); + if (err != CHDERR_NONE) + return err; + + /* mark the hunk successfully cached in */ + chd->cachehunk = hunknum; + return CHDERR_NONE; +} + +/*------------------------------------------------- + hunk_read_into_memory - read a hunk into + memory at the given location +-------------------------------------------------*/ + +static chd_error hunk_read_into_memory(chd_file *chd, UINT32 hunknum, UINT8 *dest) +{ + chd_error err; + + /* punt if no file */ + if (chd->file == NULL) + return CHDERR_INVALID_FILE; + + /* return an error if out of range */ + if (hunknum >= chd->header.totalhunks) + return CHDERR_HUNK_OUT_OF_RANGE; + + if (chd->header.version < 5) + { + map_entry *entry = &chd->map[hunknum]; + UINT32 bytes; + + /* switch off the entry type */ + switch (entry->flags & MAP_ENTRY_FLAG_TYPE_MASK) + { + /* compressed data */ + case V34_MAP_ENTRY_TYPE_COMPRESSED: + + /* read it into the decompression buffer */ + core_fseek(chd->file, entry->offset, SEEK_SET); + bytes = core_fread(chd->file, chd->compressed, entry->length); + if (bytes != entry->length) + return CHDERR_READ_ERROR; + + /* now decompress using the codec */ + err = CHDERR_NONE; + void* codec = &chd->zlib_codec_data; + if (chd->codecintf[0]->decompress != NULL) + err = (*chd->codecintf[0]->decompress)(codec, chd->compressed, entry->length, dest, chd->header.hunkbytes); + if (err != CHDERR_NONE) + return err; + break; + + /* uncompressed data */ + case V34_MAP_ENTRY_TYPE_UNCOMPRESSED: + core_fseek(chd->file, entry->offset, SEEK_SET); + bytes = core_fread(chd->file, dest, chd->header.hunkbytes); + if (bytes != chd->header.hunkbytes) + return CHDERR_READ_ERROR; + break; + + /* mini-compressed data */ + case V34_MAP_ENTRY_TYPE_MINI: + put_bigendian_uint64(&dest[0], entry->offset); + for (bytes = 8; bytes < chd->header.hunkbytes; bytes++) + dest[bytes] = dest[bytes - 8]; + break; + + /* self-referenced data */ + case V34_MAP_ENTRY_TYPE_SELF_HUNK: + if (chd->cachehunk == entry->offset && dest == chd->cache) + break; + return hunk_read_into_memory(chd, entry->offset, dest); + + /* parent-referenced data */ + case V34_MAP_ENTRY_TYPE_PARENT_HUNK: + err = hunk_read_into_memory(chd->parent, entry->offset, dest); + if (err != CHDERR_NONE) + return err; + break; + } + return CHDERR_NONE; + } + else + { + /* get a pointer to the map entry */ + uint64_t blockoffs; + uint32_t blocklen; + uint16_t blockcrc; + uint8_t *rawmap = &chd->header.rawmap[chd->header.mapentrybytes * hunknum]; + + /* uncompressed case */ + if (!compressed(&chd->header)) + { + blockoffs = (uint64_t)get_bigendian_uint32(rawmap) * (uint64_t)chd->header.hunkbytes; + if (blockoffs != 0) { + core_fseek(chd->file, blockoffs, SEEK_SET); + core_fread(chd->file, dest, chd->header.hunkbytes); + /* TODO + else if (m_parent_missing) + throw CHDERR_REQUIRES_PARENT; */ + } else if (chd->parent) { + err = hunk_read_into_memory(chd->parent, hunknum, dest); + if (err != CHDERR_NONE) + return err; + } else { + memset(dest, 0, chd->header.hunkbytes); + } + } + + /* compressed case */ + blocklen = get_bigendian_uint24(&rawmap[1]); + blockoffs = get_bigendian_uint48(&rawmap[4]); + blockcrc = get_bigendian_uint16(&rawmap[10]); + void* codec = NULL; + switch (rawmap[0]) + { + case COMPRESSION_TYPE_0: + case COMPRESSION_TYPE_1: + case COMPRESSION_TYPE_2: + case COMPRESSION_TYPE_3: + core_fseek(chd->file, blockoffs, SEEK_SET); + core_fread(chd->file, chd->compressed, blocklen); + switch (chd->codecintf[rawmap[0]]->compression) + { + case CHD_CODEC_CD_LZMA: + codec = &chd->cdlz_codec_data; + break; + + case CHD_CODEC_ZLIB: + codec = &chd->zlib_codec_data; + break; + + case CHD_CODEC_CD_ZLIB: + codec = &chd->cdzl_codec_data; + break; + + case CHD_CODEC_CD_FLAC: + codec = &chd->cdfl_codec_data; + break; + } + if (codec==NULL) + return CHDERR_DECOMPRESSION_ERROR; + chd->codecintf[rawmap[0]]->decompress(codec, chd->compressed, blocklen, dest, chd->header.hunkbytes); + if (dest != NULL && crc16(dest, chd->header.hunkbytes) != blockcrc) + return CHDERR_DECOMPRESSION_ERROR; + return CHDERR_NONE; + + case COMPRESSION_NONE: + core_fseek(chd->file, blockoffs, SEEK_SET); + core_fread(chd->file, dest, chd->header.hunkbytes); + if (crc16(dest, chd->header.hunkbytes) != blockcrc) + return CHDERR_DECOMPRESSION_ERROR; + return CHDERR_NONE; + + case COMPRESSION_SELF: + return hunk_read_into_memory(chd, blockoffs, dest); + + case COMPRESSION_PARENT: +#if 0 + /* TODO */ + if (m_parent_missing) + return CHDERR_REQUIRES_PARENT; + return m_parent->read_bytes(uint64_t(blockoffs) * uint64_t(m_parent->unit_bytes()), dest, m_hunkbytes); +#endif + return CHDERR_DECOMPRESSION_ERROR; + } + return CHDERR_NONE; + } + + /* We should not reach this code */ + return CHDERR_DECOMPRESSION_ERROR; +} + +/*************************************************************************** + INTERNAL MAP ACCESS +***************************************************************************/ + +/*------------------------------------------------- + map_read - read the initial sector map +-------------------------------------------------*/ + +static chd_error map_read(chd_file *chd) +{ + UINT32 entrysize = (chd->header.version < 3) ? OLD_MAP_ENTRY_SIZE : MAP_ENTRY_SIZE; + UINT8 raw_map_entries[MAP_STACK_ENTRIES * MAP_ENTRY_SIZE]; + UINT64 fileoffset, maxoffset = 0; + UINT8 cookie[MAP_ENTRY_SIZE]; + UINT32 count; + chd_error err; + int i; + + /* first allocate memory */ + chd->map = (map_entry *)malloc(sizeof(chd->map[0]) * chd->header.totalhunks); + if (!chd->map) + return CHDERR_OUT_OF_MEMORY; + + /* read the map entries in in chunks and extract to the map list */ + fileoffset = chd->header.length; + for (i = 0; i < chd->header.totalhunks; i += MAP_STACK_ENTRIES) + { + /* compute how many entries this time */ + int entries = chd->header.totalhunks - i, j; + if (entries > MAP_STACK_ENTRIES) + entries = MAP_STACK_ENTRIES; + + /* read that many */ + core_fseek(chd->file, fileoffset, SEEK_SET); + count = core_fread(chd->file, raw_map_entries, entries * entrysize); + if (count != entries * entrysize) + { + err = CHDERR_READ_ERROR; + goto cleanup; + } + fileoffset += entries * entrysize; + + /* process that many */ + if (entrysize == MAP_ENTRY_SIZE) + { + for (j = 0; j < entries; j++) + map_extract(&raw_map_entries[j * MAP_ENTRY_SIZE], &chd->map[i + j]); + } + else + { + for (j = 0; j < entries; j++) + map_extract_old(&raw_map_entries[j * OLD_MAP_ENTRY_SIZE], &chd->map[i + j], chd->header.hunkbytes); + } + + /* track the maximum offset */ + for (j = 0; j < entries; j++) + if ((chd->map[i + j].flags & MAP_ENTRY_FLAG_TYPE_MASK) == V34_MAP_ENTRY_TYPE_COMPRESSED || + (chd->map[i + j].flags & MAP_ENTRY_FLAG_TYPE_MASK) == V34_MAP_ENTRY_TYPE_UNCOMPRESSED) + maxoffset = MAX(maxoffset, chd->map[i + j].offset + chd->map[i + j].length); + } + + /* verify the cookie */ + core_fseek(chd->file, fileoffset, SEEK_SET); + count = core_fread(chd->file, &cookie, entrysize); + if (count != entrysize || memcmp(&cookie, END_OF_LIST_COOKIE, entrysize)) + { + err = CHDERR_INVALID_FILE; + goto cleanup; + } + + /* verify the length */ + if (maxoffset > core_fsize(chd->file)) + { + err = CHDERR_INVALID_FILE; + goto cleanup; + } + return CHDERR_NONE; + +cleanup: + if (chd->map) + free(chd->map); + chd->map = NULL; + return err; +} + +/*************************************************************************** + INTERNAL METADATA ACCESS +***************************************************************************/ + +/*------------------------------------------------- + metadata_find_entry - find a metadata entry +-------------------------------------------------*/ + +static chd_error metadata_find_entry(chd_file *chd, UINT32 metatag, UINT32 metaindex, metadata_entry *metaentry) +{ + /* start at the beginning */ + metaentry->offset = chd->header.metaoffset; + metaentry->prev = 0; + + /* loop until we run out of options */ + while (metaentry->offset != 0) + { + UINT8 raw_meta_header[METADATA_HEADER_SIZE]; + UINT32 count; + + /* read the raw header */ + core_fseek(chd->file, metaentry->offset, SEEK_SET); + count = core_fread(chd->file, raw_meta_header, sizeof(raw_meta_header)); + if (count != sizeof(raw_meta_header)) + break; + + /* extract the data */ + metaentry->metatag = get_bigendian_uint32(&raw_meta_header[0]); + metaentry->length = get_bigendian_uint32(&raw_meta_header[4]); + metaentry->next = get_bigendian_uint64(&raw_meta_header[8]); + + /* flags are encoded in the high byte of length */ + metaentry->flags = metaentry->length >> 24; + metaentry->length &= 0x00ffffff; + + /* if we got a match, proceed */ + if (metatag == CHDMETATAG_WILDCARD || metaentry->metatag == metatag) + if (metaindex-- == 0) + return CHDERR_NONE; + + /* no match, fetch the next link */ + metaentry->prev = metaentry->offset; + metaentry->offset = metaentry->next; + } + + /* if we get here, we didn't find it */ + return CHDERR_METADATA_NOT_FOUND; +} + +/*************************************************************************** + ZLIB COMPRESSION CODEC +***************************************************************************/ + +/*------------------------------------------------- + zlib_codec_init - initialize the ZLIB codec +-------------------------------------------------*/ + +static chd_error zlib_codec_init(void *codec, uint32_t hunkbytes) +{ + zlib_codec_data *data = (zlib_codec_data*)codec; + chd_error err; + int zerr; + + /* clear the buffers */ + memset(data, 0, sizeof(zlib_codec_data)); + + /* init the inflater first */ + data->inflater.next_in = (Bytef *)data; /* bogus, but that's ok */ + data->inflater.avail_in = 0; + data->inflater.zalloc = zlib_fast_alloc; + data->inflater.zfree = zlib_fast_free; + data->inflater.opaque = &data->allocator; + zerr = inflateInit2(&data->inflater, -MAX_WBITS); + + /* convert errors */ + if (zerr == Z_MEM_ERROR) + err = CHDERR_OUT_OF_MEMORY; + else if (zerr != Z_OK) + err = CHDERR_CODEC_ERROR; + else + err = CHDERR_NONE; + + /* handle an error */ + if (err != CHDERR_NONE) + free(data); + + return err; +} + +/*------------------------------------------------- + zlib_codec_free - free data for the ZLIB + codec +-------------------------------------------------*/ + +static void zlib_codec_free(void *codec) +{ + zlib_codec_data *data = (zlib_codec_data *)codec; + + /* deinit the streams */ + if (data != NULL) + { + int i; + + inflateEnd(&data->inflater); + + /* free our fast memory */ + zlib_allocator alloc = data->allocator; + for (i = 0; i < MAX_ZLIB_ALLOCS; i++) + if (alloc.allocptr[i]) + free(alloc.allocptr[i]); + } +} + +/*------------------------------------------------- + zlib_codec_decompress - decomrpess data using + the ZLIB codec +-------------------------------------------------*/ + +static chd_error zlib_codec_decompress(void *codec, const uint8_t *src, uint32_t complen, uint8_t *dest, uint32_t destlen) +{ + zlib_codec_data *data = (zlib_codec_data *)codec; + int zerr; + + /* reset the decompressor */ + data->inflater.next_in = (Bytef *)src; + data->inflater.avail_in = complen; + data->inflater.total_in = 0; + data->inflater.next_out = (Bytef *)dest; + data->inflater.avail_out = destlen; + data->inflater.total_out = 0; + zerr = inflateReset(&data->inflater); + if (zerr != Z_OK) + return CHDERR_DECOMPRESSION_ERROR; + + /* do it */ + zerr = inflate(&data->inflater, Z_FINISH); + if (data->inflater.total_out != destlen) + return CHDERR_DECOMPRESSION_ERROR; + + return CHDERR_NONE; +} + +/*------------------------------------------------- + zlib_fast_alloc - fast malloc for ZLIB, which + allocates and frees memory frequently +-------------------------------------------------*/ + +static voidpf zlib_fast_alloc(voidpf opaque, uInt items, uInt size) +{ + zlib_allocator *alloc = (zlib_allocator *)opaque; + UINT32 *ptr; + int i; + + /* compute the size, rounding to the nearest 1k */ + size = (size * items + 0x3ff) & ~0x3ff; + + /* reuse a hunk if we can */ + for (i = 0; i < MAX_ZLIB_ALLOCS; i++) + { + ptr = alloc->allocptr[i]; + if (ptr && size == *ptr) + { + /* set the low bit of the size so we don't match next time */ + *ptr |= 1; + return ptr + 1; + } + } + + /* alloc a new one */ + ptr = (UINT32 *)malloc(size + sizeof(UINT32)); + if (!ptr) + return NULL; + + /* put it into the list */ + for (i = 0; i < MAX_ZLIB_ALLOCS; i++) + if (!alloc->allocptr[i]) + { + alloc->allocptr[i] = ptr; + break; + } + + /* set the low bit of the size so we don't match next time */ + *ptr = size | 1; + return ptr + 1; +} + +/*------------------------------------------------- + zlib_fast_free - fast free for ZLIB, which + allocates and frees memory frequently +-------------------------------------------------*/ + +static void zlib_fast_free(voidpf opaque, voidpf address) +{ + zlib_allocator *alloc = (zlib_allocator *)opaque; + UINT32 *ptr = (UINT32 *)address - 1; + int i; + + /* find the hunk */ + for (i = 0; i < MAX_ZLIB_ALLOCS; i++) + if (ptr == alloc->allocptr[i]) + { + /* clear the low bit of the size to allow matches */ + *ptr &= ~1; + return; + } +} diff --git a/core/deps/chdr/chd.h b/core/deps/chdr/chd.h index a5e5c00b8..dc950715f 100644 --- a/core/deps/chdr/chd.h +++ b/core/deps/chdr/chd.h @@ -42,8 +42,11 @@ #ifndef __CHD_H__ #define __CHD_H__ +#ifdef __cplusplus +extern "C" { +#endif + #include "coretypes.h" -#include "deps/coreio/coreio.h" /*************************************************************************** @@ -122,6 +125,49 @@ 0x00000001 - set if this drive has a parent 0x00000002 - set if this drive allows writes + ========================================================================= + + V5 header: + + [ 0] char tag[8]; // 'MComprHD' + [ 8] uint32_t length; // length of header (including tag and length fields) + [ 12] uint32_t version; // drive format version + [ 16] uint32_t compressors[4];// which custom compressors are used? + [ 32] uint64_t logicalbytes; // logical size of the data (in bytes) + [ 40] uint64_t mapoffset; // offset to the map + [ 48] uint64_t metaoffset; // offset to the first blob of metadata + [ 56] uint32_t hunkbytes; // number of bytes per hunk (512k maximum) + [ 60] uint32_t unitbytes; // number of bytes per unit within each hunk + [ 64] uint8_t rawsha1[20]; // raw data SHA1 + [ 84] uint8_t sha1[20]; // combined raw+meta SHA1 + [104] uint8_t parentsha1[20];// combined raw+meta SHA1 of parent + [124] (V5 header length) + + If parentsha1 != 0, we have a parent (no need for flags) + If compressors[0] == 0, we are uncompressed (including maps) + + V5 uncompressed map format: + + [ 0] uint32_t offset; // starting offset / hunk size + + V5 compressed map format header: + + [ 0] uint32_t length; // length of compressed map + [ 4] UINT48 datastart; // offset of first block + [ 10] uint16_t crc; // crc-16 of the map + [ 12] uint8_t lengthbits; // bits used to encode complength + [ 13] uint8_t hunkbits; // bits used to encode self-refs + [ 14] uint8_t parentunitbits; // bits used to encode parent unit refs + [ 15] uint8_t reserved; // future use + [ 16] (compressed header length) + + Each compressed map entry, once expanded, looks like: + + [ 0] uint8_t compression; // compression type + [ 1] UINT24 complength; // compressed length + [ 4] UINT48 offset; // offset + [ 10] uint16_t crc; // crc-16 of the data + ***************************************************************************/ @@ -130,12 +176,14 @@ ***************************************************************************/ /* header information */ -#define CHD_HEADER_VERSION 4 +#define CHD_HEADER_VERSION 5 #define CHD_V1_HEADER_SIZE 76 #define CHD_V2_HEADER_SIZE 80 #define CHD_V3_HEADER_SIZE 120 #define CHD_V4_HEADER_SIZE 108 -#define CHD_MAX_HEADER_SIZE CHD_V4_HEADER_SIZE +#define CHD_V5_HEADER_SIZE 124 + +#define CHD_MAX_HEADER_SIZE CHD_V5_HEADER_SIZE /* checksumming information */ #define CHD_MD5_BYTES 16 @@ -146,12 +194,21 @@ #define CHDFLAGS_IS_WRITEABLE 0x00000002 #define CHDFLAGS_UNDEFINED 0xfffffffc +#define CHD_MAKE_TAG(a,b,c,d) (((a) << 24) | ((b) << 16) | ((c) << 8) | (d)) + /* compression types */ #define CHDCOMPRESSION_NONE 0 #define CHDCOMPRESSION_ZLIB 1 #define CHDCOMPRESSION_ZLIB_PLUS 2 #define CHDCOMPRESSION_AV 3 +#define CHD_CODEC_NONE 0 +#define CHD_CODEC_ZLIB CHD_MAKE_TAG('z','l','i','b') +/* general codecs with CD frontend */ +#define CHD_CODEC_CD_ZLIB CHD_MAKE_TAG('c','d','z','l') +#define CHD_CODEC_CD_LZMA CHD_MAKE_TAG('c','d','l','z') +#define CHD_CODEC_CD_FLAC CHD_MAKE_TAG('c','d','f','l') + /* A/V codec configuration parameters */ #define AV_CODEC_COMPRESS_CONFIG 1 #define AV_CODEC_DECOMPRESS_CONFIG 2 @@ -164,31 +221,34 @@ #define CHD_MDFLAGS_CHECKSUM 0x01 /* indicates data is checksummed */ /* standard hard disk metadata */ -#define HARD_DISK_METADATA_TAG 0x47444444 /* 'GDDD' */ +#define HARD_DISK_METADATA_TAG CHD_MAKE_TAG('G','D','D','D') #define HARD_DISK_METADATA_FORMAT "CYLS:%d,HEADS:%d,SECS:%d,BPS:%d" /* hard disk identify information */ -#define HARD_DISK_IDENT_METADATA_TAG 0x49444e54 /* 'IDNT' */ +#define HARD_DISK_IDENT_METADATA_TAG CHD_MAKE_TAG('I','D','N','T') /* hard disk key information */ -#define HARD_DISK_KEY_METADATA_TAG 0x4b455920 /* 'KEY ' */ +#define HARD_DISK_KEY_METADATA_TAG CHD_MAKE_TAG('K','E','Y',' ') /* pcmcia CIS information */ -#define PCMCIA_CIS_METADATA_TAG 0x43495320 /* 'CIS ' */ +#define PCMCIA_CIS_METADATA_TAG CHD_MAKE_TAG('C','I','S',' ') /* standard CD-ROM metadata */ -#define CDROM_OLD_METADATA_TAG 0x43484344 /* 'CHCD' */ -#define CDROM_TRACK_METADATA_TAG 0x43485452 /* 'CHTR' */ +#define CDROM_OLD_METADATA_TAG CHD_MAKE_TAG('C','H','C','D') +#define CDROM_TRACK_METADATA_TAG CHD_MAKE_TAG('C','H','T','R') #define CDROM_TRACK_METADATA_FORMAT "TRACK:%d TYPE:%s SUBTYPE:%s FRAMES:%d" -#define CDROM_TRACK_METADATA2_TAG 0x43485432 /* 'CHT2' */ +#define CDROM_TRACK_METADATA2_TAG CHD_MAKE_TAG('C','H','T','2') #define CDROM_TRACK_METADATA2_FORMAT "TRACK:%d TYPE:%s SUBTYPE:%s FRAMES:%d PREGAP:%d PGTYPE:%s PGSUB:%s POSTGAP:%d" +#define GDROM_OLD_METADATA_TAG CHD_MAKE_TAG('C','H','G','T') +#define GDROM_TRACK_METADATA_TAG CHD_MAKE_TAG('C', 'H', 'G', 'D') +#define GDROM_TRACK_METADATA_FORMAT "TRACK:%d TYPE:%s SUBTYPE:%s FRAMES:%d PAD:%d PREGAP:%d PGTYPE:%s PGSUB:%s POSTGAP:%d" /* standard A/V metadata */ -#define AV_METADATA_TAG 0x41564156 /* 'AVAV' */ +#define AV_METADATA_TAG CHD_MAKE_TAG('A','V','A','V') #define AV_METADATA_FORMAT "FPS:%d.%06d WIDTH:%d HEIGHT:%d INTERLACED:%d CHANNELS:%d SAMPLERATE:%d" /* A/V laserdisc frame metadata */ -#define AV_LD_METADATA_TAG 0x41564C44 /* 'AVLD' */ +#define AV_LD_METADATA_TAG CHD_MAKE_TAG('A','V','L','D') /* CHD open values */ #define CHD_OPEN_READ 1 @@ -245,16 +305,24 @@ struct _chd_header UINT32 length; /* length of header data */ UINT32 version; /* drive format version */ UINT32 flags; /* flags field */ - UINT32 compression; /* compression type */ + UINT32 compression[4]; /* compression type */ UINT32 hunkbytes; /* number of bytes per hunk */ UINT32 totalhunks; /* total # of hunks represented */ UINT64 logicalbytes; /* logical size of the data */ UINT64 metaoffset; /* offset in file of first metadata */ + UINT64 mapoffset; /* TOOD V5 */ UINT8 md5[CHD_MD5_BYTES]; /* overall MD5 checksum */ UINT8 parentmd5[CHD_MD5_BYTES]; /* overall MD5 checksum of parent */ UINT8 sha1[CHD_SHA1_BYTES]; /* overall SHA1 checksum */ UINT8 rawsha1[CHD_SHA1_BYTES]; /* SHA1 checksum of raw data */ UINT8 parentsha1[CHD_SHA1_BYTES]; /* overall SHA1 checksum of parent */ + UINT32 unitbytes; /* TODO V5 */ + UINT64 unitcount; /* TODO V5 */ + UINT32 hunkcount; /* TODO V5 */ + + /* map information */ + UINT32 mapentrybytes; /* length of each entry in a map (V5) */ + UINT8* rawmap; /* raw map data */ UINT32 obsolete_cylinders; /* obsolete field -- do not use! */ UINT32 obsolete_sectors; /* obsolete field -- do not use! */ @@ -283,13 +351,13 @@ struct _chd_verify_result /* ----- CHD file management ----- */ /* create a new CHD file fitting the given description */ -chd_error chd_create(const char *filename, UINT64 logicalbytes, UINT32 hunkbytes, UINT32 compression, chd_file *parent); +/* chd_error chd_create(const char *filename, UINT64 logicalbytes, UINT32 hunkbytes, UINT32 compression, chd_file *parent); */ /* same as chd_create(), but accepts an already-opened core_file object */ -chd_error chd_create_file(core_file *file, UINT64 logicalbytes, UINT32 hunkbytes, UINT32 compression, chd_file *parent); +/* chd_error chd_create_file(core_file *file, UINT64 logicalbytes, UINT32 hunkbytes, UINT32 compression, chd_file *parent); */ /* open an existing CHD file */ -chd_error chd_open(const wchar *filename, int mode, chd_file *parent, chd_file **chd); +chd_error chd_open(const char *filename, int mode, chd_file *parent, chd_file **chd); /* close a CHD file */ @@ -326,19 +394,6 @@ chd_error chd_get_metadata(chd_file *chd, UINT32 searchtag, UINT32 searchindex, -/* ----- verification management ----- */ - -/* begin verifying a CHD */ -chd_error chd_verify_begin(chd_file *chd); - -/* verify a single hunk of data */ -chd_error chd_verify_hunk(chd_file *chd); - -/* finish verifying a CHD, returning the computed MD5 and SHA1 */ -chd_error chd_verify_finish(chd_file *chd, chd_verify_result *result); - - - /* ----- codec interfaces ----- */ /* set internal codec parameters */ @@ -347,5 +402,8 @@ chd_error chd_codec_config(chd_file *chd, int param, void *config); /* return a string description of a codec */ const char *chd_get_codec_name(UINT32 codec); +#ifdef __cplusplus +} +#endif #endif /* __CHD_H__ */ diff --git a/core/deps/chdr/chdr.cpp b/core/deps/chdr/chdr.cpp deleted file mode 100644 index d375193c1..000000000 --- a/core/deps/chdr/chdr.cpp +++ /dev/null @@ -1,1856 +0,0 @@ -/*************************************************************************** - - chd.c - - MAME Compressed Hunks of Data file format - -**************************************************************************** - - Copyright Aaron Giles - All rights reserved. - - Redistribution and use in source and binary forms, with or without - modification, are permitted provided that the following conditions are - met: - - * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in - the documentation and/or other materials provided with the - distribution. - * Neither the name 'MAME' nor the names of its contributors may be - used to endorse or promote products derived from this software - without specific prior written permission. - - THIS SOFTWARE IS PROVIDED BY AARON GILES ''AS IS'' AND ANY EXPRESS OR - IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - DISCLAIMED. IN NO EVENT SHALL AARON GILES BE LIABLE FOR ANY DIRECT, - INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, - STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING - IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - POSSIBILITY OF SUCH DAMAGE. - -***************************************************************************/ - -#include "chd.h" - -#include "deps/crypto/md5.h" -#include "deps/crypto/sha1.h" -#include "deps/zlib/zlib.h" - -#include -#include -#include -#include -#define TRUE 1 -#define FALSE 0 - - -/*************************************************************************** - DEBUGGING -***************************************************************************/ - -#define PRINTF_MAX_HUNK (0) - - - -/*************************************************************************** - CONSTANTS -***************************************************************************/ - -#define MAP_STACK_ENTRIES 512 /* max number of entries to use on the stack */ -#define MAP_ENTRY_SIZE 16 /* V3 and later */ -#define OLD_MAP_ENTRY_SIZE 8 /* V1-V2 */ -#define METADATA_HEADER_SIZE 16 /* metadata header size */ -#define CRCMAP_HASH_SIZE 4095 /* number of CRC hashtable entries */ - -#define MAP_ENTRY_FLAG_TYPE_MASK 0x0f /* what type of hunk */ -#define MAP_ENTRY_FLAG_NO_CRC 0x10 /* no CRC is present */ - -#define MAP_ENTRY_TYPE_INVALID 0x00 /* invalid type */ -#define MAP_ENTRY_TYPE_COMPRESSED 0x01 /* standard compression */ -#define MAP_ENTRY_TYPE_UNCOMPRESSED 0x02 /* uncompressed data */ -#define MAP_ENTRY_TYPE_MINI 0x03 /* mini: use offset as raw data */ -#define MAP_ENTRY_TYPE_SELF_HUNK 0x04 /* same as another hunk in this file */ -#define MAP_ENTRY_TYPE_PARENT_HUNK 0x05 /* same as a hunk in the parent file */ - -#define CHD_V1_SECTOR_SIZE 512 /* size of a "sector" in the V1 header */ - -#define COOKIE_VALUE 0xbaadf00d -#define MAX_ZLIB_ALLOCS 64 - -#define END_OF_LIST_COOKIE "EndOfListCookie" - -#define NO_MATCH (~0) - - - -/*************************************************************************** - MACROS -***************************************************************************/ - -#define EARLY_EXIT(x) do { (void)(x); goto cleanup; } while (0) - - - -/*************************************************************************** - TYPE DEFINITIONS -***************************************************************************/ - -/* interface to a codec */ -typedef struct _codec_interface codec_interface; -struct _codec_interface -{ - UINT32 compression; /* type of compression */ - const char *compname; /* name of the algorithm */ - UINT8 lossy; /* is this a lossy algorithm? */ - chd_error (*init)(chd_file *chd); /* codec initialize */ - void (*free)(chd_file *chd); /* codec free */ - chd_error (*compress)(chd_file *chd, const void *src, UINT32 *complen); /* compress data */ - chd_error (*decompress)(chd_file *chd, UINT32 complen, void *dst); /* decompress data */ - chd_error (*config)(chd_file *chd, int param, void *config); /* configure */ -}; - - -/* a single map entry */ -typedef struct _map_entry map_entry; -struct _map_entry -{ - UINT64 offset; /* offset within the file of the data */ - UINT32 crc; /* 32-bit CRC of the data */ - UINT32 length; /* length of the data */ - UINT8 flags; /* misc flags */ -}; - - -/* simple linked-list of hunks used for our CRC map */ -typedef struct _crcmap_entry crcmap_entry; -struct _crcmap_entry -{ - UINT32 hunknum; /* hunk number */ - crcmap_entry * next; /* next entry in list */ -}; - - -/* a single metadata entry */ -typedef struct _metadata_entry metadata_entry; -struct _metadata_entry -{ - UINT64 offset; /* offset within the file of the header */ - UINT64 next; /* offset within the file of the next header */ - UINT64 prev; /* offset within the file of the previous header */ - UINT32 length; /* length of the metadata */ - UINT32 metatag; /* metadata tag */ - UINT8 flags; /* flag bits */ -}; - - -/* internal representation of an open CHD file */ -struct _chd_file -{ - UINT32 cookie; /* cookie, should equal COOKIE_VALUE */ - - core_file * file; /* handle to the open core file */ - UINT8 owns_file; /* flag indicating if this file should be closed on chd_close() */ - chd_header header; /* header, extracted from file */ - - chd_file * parent; /* pointer to parent file, or NULL */ - - map_entry * map; /* array of map entries */ - - UINT8 * cache; /* hunk cache pointer */ - UINT32 cachehunk; /* index of currently cached hunk */ - - UINT8 * compare; /* hunk compare pointer */ - UINT32 comparehunk; /* index of current compare data */ - - UINT8 * compressed; /* pointer to buffer for compressed data */ - const codec_interface * codecintf; /* interface to the codec */ - void * codecdata; /* opaque pointer to codec data */ - - crcmap_entry * crcmap; /* CRC map entries */ - crcmap_entry * crcfree; /* free list CRC entries */ - crcmap_entry ** crctable; /* table of CRC entries */ - - UINT32 maxhunk; /* maximum hunk accessed */ - - UINT8 compressing; /* are we compressing? */ - struct MD5Context compmd5; /* running MD5 during compression */ - struct sha1_ctx compsha1; /* running SHA1 during compression */ - UINT32 comphunk; /* next hunk we will compress */ - - UINT8 verifying; /* are we verifying? */ - struct MD5Context vermd5; /* running MD5 during verification */ - struct sha1_ctx versha1; /* running SHA1 during verification */ - UINT32 verhunk; /* next hunk we will verify */ - - UINT32 async_hunknum; /* hunk index for asynchronous operations */ - void * async_buffer; /* buffer pointer for asynchronous operations */ -}; - - -/* codec-private data for the ZLIB codec */ -typedef struct _zlib_codec_data zlib_codec_data; -struct _zlib_codec_data -{ - z_stream inflater; - z_stream deflater; - UINT32 * allocptr[MAX_ZLIB_ALLOCS]; -}; - - -/* a single metadata hash entry */ -typedef struct _metadata_hash metadata_hash; -struct _metadata_hash -{ - UINT8 tag[4]; /* tag of the metadata in big-endian */ - UINT8 sha1[CHD_SHA1_BYTES]; /* hash */ -}; - - - -/*************************************************************************** - GLOBAL VARIABLES -***************************************************************************/ - -static const UINT8 nullmd5[CHD_MD5_BYTES] = { 0 }; -static const UINT8 nullsha1[CHD_SHA1_BYTES] = { 0 }; - - - -/*************************************************************************** - PROTOTYPES -***************************************************************************/ - -/* internal header operations */ -static chd_error header_validate(const chd_header *header); -static chd_error header_read(core_file *file, chd_header *header); - - -/* internal hunk read/write */ -static chd_error hunk_read_into_cache(chd_file *chd, UINT32 hunknum); -static chd_error hunk_read_into_memory(chd_file *chd, UINT32 hunknum, UINT8 *dest); - -/* internal map access */ -static chd_error map_read(chd_file *chd); - -/* internal CRC map access */ -static void crcmap_init(chd_file *chd, int prepopulate); -static void crcmap_add_entry(chd_file *chd, UINT32 hunknum); -static UINT32 crcmap_find_hunk(chd_file *chd, UINT32 hunknum, UINT32 crc, const UINT8 *rawdata); - -/* metadata management */ -static chd_error metadata_find_entry(chd_file *chd, UINT32 metatag, UINT32 metaindex, metadata_entry *metaentry); - -static chd_error metadata_compute_hash(chd_file *chd, const UINT8 *rawsha1, UINT8 *finalsha1); -static int metadata_hash_compare(const void *elem1, const void *elem2); - -/* zlib compression codec */ -static chd_error zlib_codec_init(chd_file *chd); -static void zlib_codec_free(chd_file *chd); -static chd_error zlib_codec_compress(chd_file *chd, const void *src, UINT32 *length); -static chd_error zlib_codec_decompress(chd_file *chd, UINT32 srclength, void *dest); -static voidpf zlib_fast_alloc(voidpf opaque, uInt items, uInt size); -static void zlib_fast_free(voidpf opaque, voidpf address); - - -/*************************************************************************** - CODEC INTERFACES -***************************************************************************/ - -static const codec_interface codec_interfaces[] = -{ - /* "none" or no compression */ - { - CHDCOMPRESSION_NONE, - "none", - FALSE, - NULL, - NULL, - NULL, - NULL, - NULL - }, - - /* standard zlib compression */ - { - CHDCOMPRESSION_ZLIB, - "zlib", - FALSE, - zlib_codec_init, - zlib_codec_free, - zlib_codec_compress, - zlib_codec_decompress, - NULL - }, - - /* zlib+ compression */ - { - CHDCOMPRESSION_ZLIB_PLUS, - "zlib+", - FALSE, - zlib_codec_init, - zlib_codec_free, - zlib_codec_compress, - zlib_codec_decompress, - NULL - }, -}; - -#define MIN min -#define MAX max -/*************************************************************************** - INLINE FUNCTIONS -***************************************************************************/ - -/*------------------------------------------------- - get_bigendian_uint64 - fetch a UINT64 from - the data stream in bigendian order --------------------------------------------------*/ - -INLINE UINT64 get_bigendian_uint64(const UINT8 *base) -{ - return ((UINT64)base[0] << 56) | ((UINT64)base[1] << 48) | ((UINT64)base[2] << 40) | ((UINT64)base[3] << 32) | - ((UINT64)base[4] << 24) | ((UINT64)base[5] << 16) | ((UINT64)base[6] << 8) | (UINT64)base[7]; -} - - -/*------------------------------------------------- - put_bigendian_uint64 - write a UINT64 to - the data stream in bigendian order --------------------------------------------------*/ - -INLINE void put_bigendian_uint64(UINT8 *base, UINT64 value) -{ - base[0] = value >> 56; - base[1] = value >> 48; - base[2] = value >> 40; - base[3] = value >> 32; - base[4] = value >> 24; - base[5] = value >> 16; - base[6] = value >> 8; - base[7] = value; -} - - -/*------------------------------------------------- - get_bigendian_uint32 - fetch a UINT32 from - the data stream in bigendian order --------------------------------------------------*/ - -INLINE UINT32 get_bigendian_uint32(const UINT8 *base) -{ - return (base[0] << 24) | (base[1] << 16) | (base[2] << 8) | base[3]; -} - - -/*------------------------------------------------- - put_bigendian_uint32 - write a UINT32 to - the data stream in bigendian order --------------------------------------------------*/ - -INLINE void put_bigendian_uint32(UINT8 *base, UINT32 value) -{ - base[0] = value >> 24; - base[1] = value >> 16; - base[2] = value >> 8; - base[3] = value; -} - - -/*------------------------------------------------- - get_bigendian_uint16 - fetch a UINT16 from - the data stream in bigendian order --------------------------------------------------*/ - -INLINE UINT16 get_bigendian_uint16(const UINT8 *base) -{ - return (base[0] << 8) | base[1]; -} - - -/*------------------------------------------------- - put_bigendian_uint16 - write a UINT16 to - the data stream in bigendian order --------------------------------------------------*/ - -INLINE void put_bigendian_uint16(UINT8 *base, UINT16 value) -{ - base[0] = value >> 8; - base[1] = value; -} - - -/*------------------------------------------------- - map_extract - extract a single map - entry from the datastream --------------------------------------------------*/ - -INLINE void map_extract(const UINT8 *base, map_entry *entry) -{ - entry->offset = get_bigendian_uint64(&base[0]); - entry->crc = get_bigendian_uint32(&base[8]); - entry->length = get_bigendian_uint16(&base[12]) | (base[14] << 16); - entry->flags = base[15]; -} - - -/*------------------------------------------------- - map_assemble - write a single map - entry to the datastream --------------------------------------------------*/ - -INLINE void map_assemble(UINT8 *base, map_entry *entry) -{ - put_bigendian_uint64(&base[0], entry->offset); - put_bigendian_uint32(&base[8], entry->crc); - put_bigendian_uint16(&base[12], entry->length); - base[14] = entry->length >> 16; - base[15] = entry->flags; -} - - -/*------------------------------------------------- - map_extract_old - extract a single map - entry in old format from the datastream --------------------------------------------------*/ - -INLINE void map_extract_old(const UINT8 *base, map_entry *entry, UINT32 hunkbytes) -{ - entry->offset = get_bigendian_uint64(&base[0]); - entry->crc = 0; - entry->length = entry->offset >> 44; - entry->flags = MAP_ENTRY_FLAG_NO_CRC | ((entry->length == hunkbytes) ? MAP_ENTRY_TYPE_UNCOMPRESSED : MAP_ENTRY_TYPE_COMPRESSED); -#ifdef __MWERKS__ - entry->offset = entry->offset & 0x00000FFFFFFFFFFFLL; -#else - entry->offset = (entry->offset << 20) >> 20; -#endif -} - - -/*------------------------------------------------- - queue_async_operation - queue a new work - item --------------------------------------------------*/ - - -/*************************************************************************** - CHD FILE MANAGEMENT -***************************************************************************/ - - -/*------------------------------------------------- - chd_open_file - open a CHD file for access --------------------------------------------------*/ - -chd_error chd_open_file(core_file *file, int mode, chd_file *parent, chd_file **chd) -{ - chd_file *newchd = NULL; - chd_error err; - int intfnum; - - /* verify parameters */ - if (file == NULL) - EARLY_EXIT(err = CHDERR_INVALID_PARAMETER); - - /* punt if invalid parent */ - if (parent != NULL && parent->cookie != COOKIE_VALUE) - EARLY_EXIT(err = CHDERR_INVALID_PARAMETER); - - /* allocate memory for the final result */ - newchd = (chd_file *)malloc(sizeof(**chd)); - if (newchd == NULL) - EARLY_EXIT(err = CHDERR_OUT_OF_MEMORY); - memset(newchd, 0, sizeof(*newchd)); - newchd->cookie = COOKIE_VALUE; - newchd->parent = parent; - newchd->file = file; - - /* now attempt to read the header */ - err = header_read(newchd->file, &newchd->header); - if (err != CHDERR_NONE) - EARLY_EXIT(err); - - /* validate the header */ - err = header_validate(&newchd->header); - if (err != CHDERR_NONE) - EARLY_EXIT(err); - - /* make sure we don't open a read-only file writeable */ - if (mode == CHD_OPEN_READWRITE && !(newchd->header.flags & CHDFLAGS_IS_WRITEABLE)) - EARLY_EXIT(err = CHDERR_FILE_NOT_WRITEABLE); - - /* also, never open an older version writeable */ - if (mode == CHD_OPEN_READWRITE && newchd->header.version < CHD_HEADER_VERSION) - EARLY_EXIT(err = CHDERR_UNSUPPORTED_VERSION); - - /* if we need a parent, make sure we have one */ - if (parent == NULL && (newchd->header.flags & CHDFLAGS_HAS_PARENT)) - EARLY_EXIT(err = CHDERR_REQUIRES_PARENT); - - /* make sure we have a valid parent */ - if (parent != NULL) - { - /* check MD5 if it isn't empty */ - if (memcmp(nullmd5, newchd->header.parentmd5, sizeof(newchd->header.parentmd5)) != 0 && - memcmp(nullmd5, newchd->parent->header.md5, sizeof(newchd->parent->header.md5)) != 0 && - memcmp(newchd->parent->header.md5, newchd->header.parentmd5, sizeof(newchd->header.parentmd5)) != 0) - EARLY_EXIT(err = CHDERR_INVALID_PARENT); - - /* check SHA1 if it isn't empty */ - if (memcmp(nullsha1, newchd->header.parentsha1, sizeof(newchd->header.parentsha1)) != 0 && - memcmp(nullsha1, newchd->parent->header.sha1, sizeof(newchd->parent->header.sha1)) != 0 && - memcmp(newchd->parent->header.sha1, newchd->header.parentsha1, sizeof(newchd->header.parentsha1)) != 0) - EARLY_EXIT(err = CHDERR_INVALID_PARENT); - } - - /* now read the hunk map */ - err = map_read(newchd); - if (err != CHDERR_NONE) - EARLY_EXIT(err); - - /* allocate and init the hunk cache */ - newchd->cache = (UINT8 *)malloc(newchd->header.hunkbytes); - newchd->compare = (UINT8 *)malloc(newchd->header.hunkbytes); - if (newchd->cache == NULL || newchd->compare == NULL) - EARLY_EXIT(err = CHDERR_OUT_OF_MEMORY); - newchd->cachehunk = ~0; - newchd->comparehunk = ~0; - - /* allocate the temporary compressed buffer */ - newchd->compressed = (UINT8 *)malloc(newchd->header.hunkbytes); - if (newchd->compressed == NULL) - EARLY_EXIT(err = CHDERR_OUT_OF_MEMORY); - - /* find the codec interface */ - for (intfnum = 0; intfnum < ARRAY_LENGTH(codec_interfaces); intfnum++) - if (codec_interfaces[intfnum].compression == newchd->header.compression) - { - newchd->codecintf = &codec_interfaces[intfnum]; - break; - } - if (intfnum == ARRAY_LENGTH(codec_interfaces)) - EARLY_EXIT(err = CHDERR_UNSUPPORTED_FORMAT); - - /* initialize the codec */ - if (newchd->codecintf->init != NULL) - err = (*newchd->codecintf->init)(newchd); - if (err != CHDERR_NONE) - EARLY_EXIT(err); - - /* all done */ - *chd = newchd; - return CHDERR_NONE; - -cleanup: - if (newchd != NULL) - chd_close(newchd); - return err; -} - -/*------------------------------------------------- - chd_open - open a CHD file by - filename --------------------------------------------------*/ - -chd_error chd_open(const wchar *filename, int mode, chd_file *parent, chd_file **chd) -{ - chd_error err; - core_file *file = NULL; - UINT32 openflags; - - /* choose the proper mode */ - switch(mode) - { - case CHD_OPEN_READ: - break; - - default: - err = CHDERR_INVALID_PARAMETER; - goto cleanup; - } - - /* open the file */ - file=core_fopen(filename); - if (file == 0) - { - err = CHDERR_FILE_NOT_FOUND; - goto cleanup; - } - - /* now open the CHD */ - err = chd_open_file(file, mode, parent, chd); - if (err != CHDERR_NONE) - goto cleanup; - - /* we now own this file */ - (*chd)->owns_file = TRUE; - -cleanup: - if ((err != CHDERR_NONE) && (file != NULL)) - core_fclose(file); - return err; -} - - -/*------------------------------------------------- - chd_close - close a CHD file for access --------------------------------------------------*/ - -void chd_close(chd_file *chd) -{ - /* punt if NULL or invalid */ - if (chd == NULL || chd->cookie != COOKIE_VALUE) - return; - - /* deinit the codec */ - if (chd->codecintf != NULL && chd->codecintf->free != NULL) - (*chd->codecintf->free)(chd); - - /* free the compressed data buffer */ - if (chd->compressed != NULL) - free(chd->compressed); - - /* free the hunk cache and compare data */ - if (chd->compare != NULL) - free(chd->compare); - if (chd->cache != NULL) - free(chd->cache); - - /* free the hunk map */ - if (chd->map != NULL) - free(chd->map); - - /* free the CRC table */ - if (chd->crctable != NULL) - free(chd->crctable); - - /* free the CRC map */ - if (chd->crcmap != NULL) - free(chd->crcmap); - - /* close the file */ - if (chd->owns_file && chd->file != NULL) - core_fclose(chd->file); - - if (PRINTF_MAX_HUNK) printf("Max hunk = %d/%d\n", chd->maxhunk, chd->header.totalhunks); - - /* free our memory */ - free(chd); -} - - -/*------------------------------------------------- - chd_core_file - return the associated - core_file --------------------------------------------------*/ - -core_file *chd_core_file(chd_file *chd) -{ - return chd->file; -} - - -/*------------------------------------------------- - chd_error_string - return an error string for - the given CHD error --------------------------------------------------*/ - -const char *chd_error_string(chd_error err) -{ - switch (err) - { - case CHDERR_NONE: return "no error"; - case CHDERR_NO_INTERFACE: return "no drive interface"; - case CHDERR_OUT_OF_MEMORY: return "out of memory"; - case CHDERR_INVALID_FILE: return "invalid file"; - case CHDERR_INVALID_PARAMETER: return "invalid parameter"; - case CHDERR_INVALID_DATA: return "invalid data"; - case CHDERR_FILE_NOT_FOUND: return "file not found"; - case CHDERR_REQUIRES_PARENT: return "requires parent"; - case CHDERR_FILE_NOT_WRITEABLE: return "file not writeable"; - case CHDERR_READ_ERROR: return "read error"; - case CHDERR_WRITE_ERROR: return "write error"; - case CHDERR_CODEC_ERROR: return "codec error"; - case CHDERR_INVALID_PARENT: return "invalid parent"; - case CHDERR_HUNK_OUT_OF_RANGE: return "hunk out of range"; - case CHDERR_DECOMPRESSION_ERROR: return "decompression error"; - case CHDERR_COMPRESSION_ERROR: return "compression error"; - case CHDERR_CANT_CREATE_FILE: return "can't create file"; - case CHDERR_CANT_VERIFY: return "can't verify file"; - case CHDERR_NOT_SUPPORTED: return "operation not supported"; - case CHDERR_METADATA_NOT_FOUND: return "can't find metadata"; - case CHDERR_INVALID_METADATA_SIZE: return "invalid metadata size"; - case CHDERR_UNSUPPORTED_VERSION: return "unsupported CHD version"; - case CHDERR_VERIFY_INCOMPLETE: return "incomplete verify"; - case CHDERR_INVALID_METADATA: return "invalid metadata"; - case CHDERR_INVALID_STATE: return "invalid state"; - case CHDERR_OPERATION_PENDING: return "operation pending"; - case CHDERR_NO_ASYNC_OPERATION: return "no async operation in progress"; - case CHDERR_UNSUPPORTED_FORMAT: return "unsupported format"; - default: return "undocumented error"; - } -} - - - -/*************************************************************************** - CHD HEADER MANAGEMENT -***************************************************************************/ - -/*------------------------------------------------- - chd_get_header - return a pointer to the - extracted header data --------------------------------------------------*/ - -const chd_header *chd_get_header(chd_file *chd) -{ - /* punt if NULL or invalid */ - if (chd == NULL || chd->cookie != COOKIE_VALUE) - return NULL; - - return &chd->header; -} - - - -/*************************************************************************** - CORE DATA READ/WRITE -***************************************************************************/ - -/*------------------------------------------------- - chd_read - read a single hunk from the CHD - file --------------------------------------------------*/ - -chd_error chd_read(chd_file *chd, UINT32 hunknum, void *buffer) -{ - /* punt if NULL or invalid */ - if (chd == NULL || chd->cookie != COOKIE_VALUE) - return CHDERR_INVALID_PARAMETER; - - /* if we're past the end, fail */ - if (hunknum >= chd->header.totalhunks) - return CHDERR_HUNK_OUT_OF_RANGE; - - /* perform the read */ - return hunk_read_into_memory(chd, hunknum, (UINT8 *)buffer); -} - - - - - -/*************************************************************************** - METADATA MANAGEMENT -***************************************************************************/ - -/*------------------------------------------------- - chd_get_metadata - get the indexed metadata - of the given type --------------------------------------------------*/ - -chd_error chd_get_metadata(chd_file *chd, UINT32 searchtag, UINT32 searchindex, void *output, UINT32 outputlen, UINT32 *resultlen, UINT32 *resulttag, UINT8 *resultflags) -{ - metadata_entry metaentry; - chd_error err; - UINT32 count; - - /* if we didn't find it, just return */ - err = metadata_find_entry(chd, searchtag, searchindex, &metaentry); - if (err != CHDERR_NONE) - { - /* unless we're an old version and they are requesting hard disk metadata */ - if (chd->header.version < 3 && (searchtag == HARD_DISK_METADATA_TAG || searchtag == CHDMETATAG_WILDCARD) && searchindex == 0) - { - char faux_metadata[256]; - UINT32 faux_length; - - /* fill in the faux metadata */ - sprintf(faux_metadata, HARD_DISK_METADATA_FORMAT, chd->header.obsolete_cylinders, chd->header.obsolete_heads, chd->header.obsolete_sectors, chd->header.hunkbytes / chd->header.obsolete_hunksize); - faux_length = (UINT32)strlen(faux_metadata) + 1; - - /* copy the metadata itself */ - memcpy(output, faux_metadata, MIN(outputlen, faux_length)); - - /* return the length of the data and the tag */ - if (resultlen != NULL) - *resultlen = faux_length; - if (resulttag != NULL) - *resulttag = HARD_DISK_METADATA_TAG; - return CHDERR_NONE; - } - return err; - } - - /* read the metadata */ - outputlen = min(outputlen, metaentry.length); - core_fseek(chd->file, metaentry.offset + METADATA_HEADER_SIZE, SEEK_SET); - count = core_fread(chd->file, output, outputlen); - if (count != outputlen) - return CHDERR_READ_ERROR; - - /* return the length of the data and the tag */ - if (resultlen != NULL) - *resultlen = metaentry.length; - if (resulttag != NULL) - *resulttag = metaentry.metatag; - if (resultflags != NULL) - *resultflags = metaentry.flags; - return CHDERR_NONE; -} - - - -/*************************************************************************** - COMPRESSION MANAGEMENT -***************************************************************************/ - - -/*************************************************************************** - VERIFICATION -***************************************************************************/ - -/*------------------------------------------------- - chd_verify_begin - begin compressing data - into a CHD --------------------------------------------------*/ - -chd_error chd_verify_begin(chd_file *chd) -{ - /* verify parameters */ - if (chd == NULL) - return CHDERR_INVALID_PARAMETER; - - /* if this is a writeable file image, we can't verify */ - if (chd->header.flags & CHDFLAGS_IS_WRITEABLE) - return CHDERR_CANT_VERIFY; - - - /* init the MD5/SHA1 computations */ - MD5Init(&chd->vermd5); - sha1_init(&chd->versha1); - chd->verifying = TRUE; - chd->verhunk = 0; - - return CHDERR_NONE; -} - - -/*------------------------------------------------- - chd_verify_hunk - verify the next hunk in - the CHD --------------------------------------------------*/ - -chd_error chd_verify_hunk(chd_file *chd) -{ - UINT32 thishunk = chd->verhunk++; - UINT64 hunkoffset = (UINT64)thishunk * (UINT64)chd->header.hunkbytes; - map_entry *entry; - chd_error err; - - /* error if in the wrong state */ - if (!chd->verifying) - return CHDERR_INVALID_STATE; - - /* read the hunk into the cache */ - err = hunk_read_into_cache(chd, thishunk); - if (err != CHDERR_NONE) - return err; - - /* update the MD5/SHA1 */ - if (hunkoffset < chd->header.logicalbytes) - { - UINT64 bytestochecksum = MIN((u64)chd->header.hunkbytes, (u64)(chd->header.logicalbytes - hunkoffset)); - if (bytestochecksum > 0) - { - MD5Update(&chd->vermd5, chd->cache, bytestochecksum); - sha1_update(&chd->versha1, bytestochecksum, chd->cache); - } - } - - /* validate the CRC if we have one */ - entry = &chd->map[thishunk]; - if (!(entry->flags & MAP_ENTRY_FLAG_NO_CRC) && entry->crc != crc32(0, chd->cache, chd->header.hunkbytes)) - return CHDERR_DECOMPRESSION_ERROR; - - return CHDERR_NONE; -} - - -/*------------------------------------------------- - chd_verify_finish - finish verification of - the CHD --------------------------------------------------*/ - -chd_error chd_verify_finish(chd_file *chd, chd_verify_result *result) -{ - /* error if in the wrong state */ - if (!chd->verifying) - return CHDERR_INVALID_STATE; - - /* compute the final MD5 */ - MD5Final(result->md5, &chd->vermd5); - - /* compute the final SHA1 */ - sha1_final(&chd->versha1); - sha1_digest(&chd->versha1, SHA1_DIGEST_SIZE, result->rawsha1); - - /* compute the overall hash including metadata */ - metadata_compute_hash(chd, result->rawsha1, result->sha1); - - /* return an error */ - chd->verifying = FALSE; - return (chd->verhunk < chd->header.totalhunks) ? CHDERR_VERIFY_INCOMPLETE : CHDERR_NONE; -} - - - -/*************************************************************************** - CODEC INTERFACES -***************************************************************************/ - -/*------------------------------------------------- - chd_codec_config - set internal codec - parameters --------------------------------------------------*/ - -chd_error chd_codec_config(chd_file *chd, int param, void *config) -{ - - return CHDERR_INVALID_PARAMETER; -} - - -/*------------------------------------------------- - chd_get_codec_name - get the name of a - particular codec --------------------------------------------------*/ - -const char *chd_get_codec_name(UINT32 codec) -{ - return "Unknown"; -} - - -/*************************************************************************** - INTERNAL HEADER OPERATIONS -***************************************************************************/ - -/*------------------------------------------------- - header_validate - check the validity of a - CHD header --------------------------------------------------*/ - -static chd_error header_validate(const chd_header *header) -{ - int intfnum; - - /* require a valid version */ - if (header->version == 0 || header->version > CHD_HEADER_VERSION) - return CHDERR_UNSUPPORTED_VERSION; - - /* require a valid length */ - if ((header->version == 1 && header->length != CHD_V1_HEADER_SIZE) || - (header->version == 2 && header->length != CHD_V2_HEADER_SIZE) || - (header->version == 3 && header->length != CHD_V3_HEADER_SIZE) || - (header->version == 4 && header->length != CHD_V4_HEADER_SIZE)) - return CHDERR_INVALID_PARAMETER; - - /* require valid flags */ - if (header->flags & CHDFLAGS_UNDEFINED) - return CHDERR_INVALID_PARAMETER; - - /* require a supported compression mechanism */ - for (intfnum = 0; intfnum < ARRAY_LENGTH(codec_interfaces); intfnum++) - if (codec_interfaces[intfnum].compression == header->compression) - break; - if (intfnum == ARRAY_LENGTH(codec_interfaces)) - return CHDERR_INVALID_PARAMETER; - - /* require a valid hunksize */ - if (header->hunkbytes == 0 || header->hunkbytes >= 65536 * 256) - return CHDERR_INVALID_PARAMETER; - - /* require a valid hunk count */ - if (header->totalhunks == 0) - return CHDERR_INVALID_PARAMETER; - - /* require a valid MD5 and/or SHA1 if we're using a parent */ - if ((header->flags & CHDFLAGS_HAS_PARENT) && memcmp(header->parentmd5, nullmd5, sizeof(nullmd5)) == 0 && memcmp(header->parentsha1, nullsha1, sizeof(nullsha1)) == 0) - return CHDERR_INVALID_PARAMETER; - - /* if we're V3 or later, the obsolete fields must be 0 */ - if (header->version >= 3 && - (header->obsolete_cylinders != 0 || header->obsolete_sectors != 0 || - header->obsolete_heads != 0 || header->obsolete_hunksize != 0)) - return CHDERR_INVALID_PARAMETER; - - /* if we're pre-V3, the obsolete fields must NOT be 0 */ - if (header->version < 3 && - (header->obsolete_cylinders == 0 || header->obsolete_sectors == 0 || - header->obsolete_heads == 0 || header->obsolete_hunksize == 0)) - return CHDERR_INVALID_PARAMETER; - - return CHDERR_NONE; -} - - -/*------------------------------------------------- - header_read - read a CHD header into the - internal data structure --------------------------------------------------*/ - -static chd_error header_read(core_file *file, chd_header *header) -{ - UINT8 rawheader[CHD_MAX_HEADER_SIZE]; - UINT32 count; - - /* punt if NULL */ - if (header == NULL) - return CHDERR_INVALID_PARAMETER; - - /* punt if invalid file */ - if (file == NULL) - return CHDERR_INVALID_FILE; - - /* seek and read */ - core_fseek(file, 0, SEEK_SET); - count = core_fread(file, rawheader, sizeof(rawheader)); - if (count != sizeof(rawheader)) - return CHDERR_READ_ERROR; - - /* verify the tag */ - if (strncmp((char *)rawheader, "MComprHD", 8) != 0) - return CHDERR_INVALID_DATA; - - /* extract the direct data */ - memset(header, 0, sizeof(*header)); - header->length = get_bigendian_uint32(&rawheader[8]); - header->version = get_bigendian_uint32(&rawheader[12]); - - /* make sure it's a version we understand */ - if (header->version == 0 || header->version > CHD_HEADER_VERSION) - return CHDERR_UNSUPPORTED_VERSION; - - /* make sure the length is expected */ - if ((header->version == 1 && header->length != CHD_V1_HEADER_SIZE) || - (header->version == 2 && header->length != CHD_V2_HEADER_SIZE) || - (header->version == 3 && header->length != CHD_V3_HEADER_SIZE) || - (header->version == 4 && header->length != CHD_V4_HEADER_SIZE)) - return CHDERR_INVALID_DATA; - - /* extract the common data */ - header->flags = get_bigendian_uint32(&rawheader[16]); - header->compression = get_bigendian_uint32(&rawheader[20]); - - /* extract the V1/V2-specific data */ - if (header->version < 3) - { - int seclen = (header->version == 1) ? CHD_V1_SECTOR_SIZE : get_bigendian_uint32(&rawheader[76]); - header->obsolete_hunksize = get_bigendian_uint32(&rawheader[24]); - header->totalhunks = get_bigendian_uint32(&rawheader[28]); - header->obsolete_cylinders = get_bigendian_uint32(&rawheader[32]); - header->obsolete_heads = get_bigendian_uint32(&rawheader[36]); - header->obsolete_sectors = get_bigendian_uint32(&rawheader[40]); - memcpy(header->md5, &rawheader[44], CHD_MD5_BYTES); - memcpy(header->parentmd5, &rawheader[60], CHD_MD5_BYTES); - header->logicalbytes = (UINT64)header->obsolete_cylinders * (UINT64)header->obsolete_heads * (UINT64)header->obsolete_sectors * (UINT64)seclen; - header->hunkbytes = seclen * header->obsolete_hunksize; - header->metaoffset = 0; - } - - /* extract the V3-specific data */ - else if (header->version == 3) - { - header->totalhunks = get_bigendian_uint32(&rawheader[24]); - header->logicalbytes = get_bigendian_uint64(&rawheader[28]); - header->metaoffset = get_bigendian_uint64(&rawheader[36]); - memcpy(header->md5, &rawheader[44], CHD_MD5_BYTES); - memcpy(header->parentmd5, &rawheader[60], CHD_MD5_BYTES); - header->hunkbytes = get_bigendian_uint32(&rawheader[76]); - memcpy(header->sha1, &rawheader[80], CHD_SHA1_BYTES); - memcpy(header->parentsha1, &rawheader[100], CHD_SHA1_BYTES); - } - - /* extract the V4-specific data */ - else - { - header->totalhunks = get_bigendian_uint32(&rawheader[24]); - header->logicalbytes = get_bigendian_uint64(&rawheader[28]); - header->metaoffset = get_bigendian_uint64(&rawheader[36]); - header->hunkbytes = get_bigendian_uint32(&rawheader[44]); - memcpy(header->sha1, &rawheader[48], CHD_SHA1_BYTES); - memcpy(header->parentsha1, &rawheader[68], CHD_SHA1_BYTES); - memcpy(header->rawsha1, &rawheader[88], CHD_SHA1_BYTES); - } - - /* guess it worked */ - return CHDERR_NONE; -} - - -/*************************************************************************** - INTERNAL HUNK READ/WRITE -***************************************************************************/ - -/*------------------------------------------------- - hunk_read_into_cache - read a hunk into - the CHD's hunk cache --------------------------------------------------*/ - -static chd_error hunk_read_into_cache(chd_file *chd, UINT32 hunknum) -{ - chd_error err; - - /* track the max */ - if (hunknum > chd->maxhunk) - chd->maxhunk = hunknum; - - /* if we're already in the cache, we're done */ - if (chd->cachehunk == hunknum) - return CHDERR_NONE; - chd->cachehunk = ~0; - - /* otherwise, read the data */ - err = hunk_read_into_memory(chd, hunknum, chd->cache); - if (err != CHDERR_NONE) - return err; - - /* mark the hunk successfully cached in */ - chd->cachehunk = hunknum; - return CHDERR_NONE; -} - - -/*------------------------------------------------- - hunk_read_into_memory - read a hunk into - memory at the given location --------------------------------------------------*/ - -static chd_error hunk_read_into_memory(chd_file *chd, UINT32 hunknum, UINT8 *dest) -{ - map_entry *entry = &chd->map[hunknum]; - chd_error err; - UINT32 bytes; - - /* return an error if out of range */ - if (hunknum >= chd->header.totalhunks) - return CHDERR_HUNK_OUT_OF_RANGE; - - /* switch off the entry type */ - switch (entry->flags & MAP_ENTRY_FLAG_TYPE_MASK) - { - /* compressed data */ - case MAP_ENTRY_TYPE_COMPRESSED: - - /* read it into the decompression buffer */ - core_fseek(chd->file, entry->offset, SEEK_SET); - bytes = core_fread(chd->file, chd->compressed, entry->length); - if (bytes != entry->length) - return CHDERR_READ_ERROR; - - /* now decompress using the codec */ - err = CHDERR_NONE; - if (chd->codecintf->decompress != NULL) - err = (*chd->codecintf->decompress)(chd, entry->length, dest); - if (err != CHDERR_NONE) - return err; - break; - - /* uncompressed data */ - case MAP_ENTRY_TYPE_UNCOMPRESSED: - core_fseek(chd->file, entry->offset, SEEK_SET); - bytes = core_fread(chd->file, dest, chd->header.hunkbytes); - if (bytes != chd->header.hunkbytes) - return CHDERR_READ_ERROR; - break; - - /* mini-compressed data */ - case MAP_ENTRY_TYPE_MINI: - put_bigendian_uint64(&dest[0], entry->offset); - for (bytes = 8; bytes < chd->header.hunkbytes; bytes++) - dest[bytes] = dest[bytes - 8]; - break; - - /* self-referenced data */ - case MAP_ENTRY_TYPE_SELF_HUNK: - if (chd->cachehunk == entry->offset && dest == chd->cache) - break; - return hunk_read_into_memory(chd, entry->offset, dest); - - /* parent-referenced data */ - case MAP_ENTRY_TYPE_PARENT_HUNK: - err = hunk_read_into_memory(chd->parent, entry->offset, dest); - if (err != CHDERR_NONE) - return err; - break; - } - return CHDERR_NONE; -} - - -/*************************************************************************** - INTERNAL MAP ACCESS -***************************************************************************/ - -/*------------------------------------------------- - map_write_initial - write an initial map to - a new CHD file --------------------------------------------------*/ - -/*------------------------------------------------- - map_read - read the initial sector map --------------------------------------------------*/ - -static chd_error map_read(chd_file *chd) -{ - UINT32 entrysize = (chd->header.version < 3) ? OLD_MAP_ENTRY_SIZE : MAP_ENTRY_SIZE; - UINT8 raw_map_entries[MAP_STACK_ENTRIES * MAP_ENTRY_SIZE]; - UINT64 fileoffset, maxoffset = 0; - UINT8 cookie[MAP_ENTRY_SIZE]; - UINT32 count; - chd_error err; - int i; - - /* first allocate memory */ - chd->map = (map_entry *)malloc(sizeof(chd->map[0]) * chd->header.totalhunks); - if (!chd->map) - return CHDERR_OUT_OF_MEMORY; - - /* read the map entries in in chunks and extract to the map list */ - fileoffset = chd->header.length; - for (i = 0; i < chd->header.totalhunks; i += MAP_STACK_ENTRIES) - { - /* compute how many entries this time */ - int entries = chd->header.totalhunks - i, j; - if (entries > MAP_STACK_ENTRIES) - entries = MAP_STACK_ENTRIES; - - /* read that many */ - core_fseek(chd->file, fileoffset, SEEK_SET); - count = core_fread(chd->file, raw_map_entries, entries * entrysize); - if (count != entries * entrysize) - { - err = CHDERR_READ_ERROR; - goto cleanup; - } - fileoffset += entries * entrysize; - - /* process that many */ - if (entrysize == MAP_ENTRY_SIZE) - { - for (j = 0; j < entries; j++) - map_extract(&raw_map_entries[j * MAP_ENTRY_SIZE], &chd->map[i + j]); - } - else - { - for (j = 0; j < entries; j++) - map_extract_old(&raw_map_entries[j * OLD_MAP_ENTRY_SIZE], &chd->map[i + j], chd->header.hunkbytes); - } - - /* track the maximum offset */ - for (j = 0; j < entries; j++) - if ((chd->map[i + j].flags & MAP_ENTRY_FLAG_TYPE_MASK) == MAP_ENTRY_TYPE_COMPRESSED || - (chd->map[i + j].flags & MAP_ENTRY_FLAG_TYPE_MASK) == MAP_ENTRY_TYPE_UNCOMPRESSED) - maxoffset = MAX(maxoffset, chd->map[i + j].offset + chd->map[i + j].length); - } - - /* verify the cookie */ - core_fseek(chd->file, fileoffset, SEEK_SET); - count = core_fread(chd->file, &cookie, entrysize); - if (count != entrysize || memcmp(&cookie, END_OF_LIST_COOKIE, entrysize)) - { - err = CHDERR_INVALID_FILE; - goto cleanup; - } - - /* verify the length */ - if (maxoffset > core_fsize(chd->file)) - { - err = CHDERR_INVALID_FILE; - goto cleanup; - } - return CHDERR_NONE; - -cleanup: - if (chd->map) - free(chd->map); - chd->map = NULL; - return err; -} - - - -/*************************************************************************** - INTERNAL CRC MAP ACCESS -***************************************************************************/ - -/*------------------------------------------------- - crcmap_init - initialize the CRC map --------------------------------------------------*/ - -static void crcmap_init(chd_file *chd, int prepopulate) -{ - int i; - - /* if we already have one, bail */ - if (chd->crcmap != NULL) - return; - - /* reset all pointers */ - chd->crcmap = NULL; - chd->crcfree = NULL; - chd->crctable = NULL; - - /* allocate a list; one for each hunk */ - chd->crcmap = (crcmap_entry *)malloc(chd->header.totalhunks * sizeof(chd->crcmap[0])); - if (chd->crcmap == NULL) - return; - - /* allocate a CRC map table */ - chd->crctable = (crcmap_entry **)malloc(CRCMAP_HASH_SIZE * sizeof(chd->crctable[0])); - if (chd->crctable == NULL) - { - free(chd->crcmap); - chd->crcmap = NULL; - return; - } - - /* initialize the free list */ - for (i = 0; i < chd->header.totalhunks; i++) - { - chd->crcmap[i].next = chd->crcfree; - chd->crcfree = &chd->crcmap[i]; - } - - /* initialize the table */ - memset(chd->crctable, 0, CRCMAP_HASH_SIZE * sizeof(chd->crctable[0])); - - /* if we're to prepopulate, go for it */ - if (prepopulate) - for (i = 0; i < chd->header.totalhunks; i++) - crcmap_add_entry(chd, i); -} - - -/*------------------------------------------------- - crcmap_add_entry - log a CRC entry --------------------------------------------------*/ - -static void crcmap_add_entry(chd_file *chd, UINT32 hunknum) -{ - UINT32 hash = chd->map[hunknum].crc % CRCMAP_HASH_SIZE; - crcmap_entry *crcmap; - - /* pull a free entry off the list */ - crcmap = chd->crcfree; - chd->crcfree = crcmap->next; - - /* set up the entry and link it into the hash table */ - crcmap->hunknum = hunknum; - crcmap->next = chd->crctable[hash]; - chd->crctable[hash] = crcmap; -} - - -/*------------------------------------------------- - crcmap_verify_hunk_match - verify that a - hunk really matches by doing a byte-for-byte - compare --------------------------------------------------*/ - -static int crcmap_verify_hunk_match(chd_file *chd, UINT32 hunknum, const UINT8 *rawdata) -{ - /* we have a potential match -- better be sure */ - /* read the hunk from disk and compare byte-for-byte */ - if (hunknum != chd->comparehunk) - { - chd->comparehunk = ~0; - if (hunk_read_into_memory(chd, hunknum, chd->compare) == CHDERR_NONE) - chd->comparehunk = hunknum; - } - return (hunknum == chd->comparehunk && memcmp(rawdata, chd->compare, chd->header.hunkbytes) == 0); -} - - -/*------------------------------------------------- - crcmap_find_hunk - find a hunk with a matching - CRC in the map --------------------------------------------------*/ - -static UINT32 crcmap_find_hunk(chd_file *chd, UINT32 hunknum, UINT32 crc, const UINT8 *rawdata) -{ - UINT32 lasthunk = (hunknum < chd->header.totalhunks) ? hunknum : chd->header.totalhunks; - int curhunk; - - /* if we have a CRC map, use that */ - if (chd->crctable) - { - crcmap_entry *curentry; - for (curentry = chd->crctable[crc % CRCMAP_HASH_SIZE]; curentry; curentry = curentry->next) - { - curhunk = curentry->hunknum; - if (chd->map[curhunk].crc == crc && !(chd->map[curhunk].flags & MAP_ENTRY_FLAG_NO_CRC) && crcmap_verify_hunk_match(chd, curhunk, rawdata)) - return curhunk; - } - return NO_MATCH; - } - - /* first see if the last match is a valid one */ - if (chd->comparehunk < chd->header.totalhunks && chd->map[chd->comparehunk].crc == crc && !(chd->map[chd->comparehunk].flags & MAP_ENTRY_FLAG_NO_CRC) && - memcmp(rawdata, chd->compare, chd->header.hunkbytes) == 0) - return chd->comparehunk; - - /* scan through the CHD's hunk map looking for a match */ - for (curhunk = 0; curhunk < lasthunk; curhunk++) - if (chd->map[curhunk].crc == crc && !(chd->map[curhunk].flags & MAP_ENTRY_FLAG_NO_CRC) && crcmap_verify_hunk_match(chd, curhunk, rawdata)) - return curhunk; - - return NO_MATCH; -} - - - -/*************************************************************************** - INTERNAL METADATA ACCESS -***************************************************************************/ - -/*------------------------------------------------- - metadata_find_entry - find a metadata entry --------------------------------------------------*/ - -static chd_error metadata_find_entry(chd_file *chd, UINT32 metatag, UINT32 metaindex, metadata_entry *metaentry) -{ - /* start at the beginning */ - metaentry->offset = chd->header.metaoffset; - metaentry->prev = 0; - - /* loop until we run out of options */ - while (metaentry->offset != 0) - { - UINT8 raw_meta_header[METADATA_HEADER_SIZE]; - UINT32 count; - - /* read the raw header */ - core_fseek(chd->file, metaentry->offset, SEEK_SET); - count = core_fread(chd->file, raw_meta_header, sizeof(raw_meta_header)); - if (count != sizeof(raw_meta_header)) - break; - - /* extract the data */ - metaentry->metatag = get_bigendian_uint32(&raw_meta_header[0]); - metaentry->length = get_bigendian_uint32(&raw_meta_header[4]); - metaentry->next = get_bigendian_uint64(&raw_meta_header[8]); - - /* flags are encoded in the high byte of length */ - metaentry->flags = metaentry->length >> 24; - metaentry->length &= 0x00ffffff; - - /* if we got a match, proceed */ - if (metatag == CHDMETATAG_WILDCARD || metaentry->metatag == metatag) - if (metaindex-- == 0) - return CHDERR_NONE; - - /* no match, fetch the next link */ - metaentry->prev = metaentry->offset; - metaentry->offset = metaentry->next; - } - - /* if we get here, we didn't find it */ - return CHDERR_METADATA_NOT_FOUND; -} - - -/*------------------------------------------------- - metadata_set_previous_next - set the 'next' - offset of a piece of metadata --------------------------------------------------*/ - - -/*------------------------------------------------- - metadata_set_length - set the length field of - a piece of metadata --------------------------------------------------*/ - - -/*------------------------------------------------- - metadata_compute_hash - compute the SHA1 - hash of all metadata that requests it --------------------------------------------------*/ - -static chd_error metadata_compute_hash(chd_file *chd, const UINT8 *rawsha1, UINT8 *finalsha1) -{ - metadata_hash *hasharray = NULL; - chd_error err = CHDERR_NONE; - struct sha1_ctx sha1; - UINT32 hashindex = 0; - UINT32 hashalloc = 0; - UINT64 offset, next; - - /* only works for V4 and above */ - if (chd->header.version < 4) - { - memcpy(finalsha1, rawsha1, SHA1_DIGEST_SIZE); - return CHDERR_NONE; - } - - /* loop until we run out of data */ - for (offset = chd->header.metaoffset; offset != 0; offset = next) - { - UINT8 raw_meta_header[METADATA_HEADER_SIZE]; - UINT32 count, metalength, metatag; - UINT8 *tempbuffer; - UINT8 metaflags; - - /* read the raw header */ - core_fseek(chd->file, offset, SEEK_SET); - count = core_fread(chd->file, raw_meta_header, sizeof(raw_meta_header)); - if (count != sizeof(raw_meta_header)) - break; - - /* extract the data */ - metatag = get_bigendian_uint32(&raw_meta_header[0]); - metalength = get_bigendian_uint32(&raw_meta_header[4]); - next = get_bigendian_uint64(&raw_meta_header[8]); - - /* flags are encoded in the high byte of length */ - metaflags = metalength >> 24; - metalength &= 0x00ffffff; - - /* if not checksumming, continue */ - if (!(metaflags & CHD_MDFLAGS_CHECKSUM)) - continue; - - /* allocate memory */ - tempbuffer = (UINT8 *)malloc(metalength); - if (tempbuffer == NULL) - { - err = CHDERR_OUT_OF_MEMORY; - goto cleanup; - } - - /* seek and read the metadata */ - core_fseek(chd->file, offset + METADATA_HEADER_SIZE, SEEK_SET); - count = core_fread(chd->file, tempbuffer, metalength); - if (count != metalength) - { - free(tempbuffer); - err = CHDERR_READ_ERROR; - goto cleanup; - } - - /* compute this entry's hash */ - sha1_init(&sha1); - sha1_update(&sha1, metalength, tempbuffer); - sha1_final(&sha1); - free(tempbuffer); - - /* expand the hasharray if necessary */ - if (hashindex >= hashalloc) - { - hashalloc += 256; - hasharray = (metadata_hash *)realloc(hasharray, hashalloc * sizeof(hasharray[0])); - if (hasharray == NULL) - { - err = CHDERR_OUT_OF_MEMORY; - goto cleanup; - } - } - - /* fill in the entry */ - put_bigendian_uint32(hasharray[hashindex].tag, metatag); - sha1_digest(&sha1, SHA1_DIGEST_SIZE, hasharray[hashindex].sha1); - hashindex++; - } - - /* sort the array */ - qsort(hasharray, hashindex, sizeof(hasharray[0]), metadata_hash_compare); - - /* compute the SHA1 of the raw plus the various metadata */ - sha1_init(&sha1); - sha1_update(&sha1, CHD_SHA1_BYTES, rawsha1); - sha1_update(&sha1, hashindex * sizeof(hasharray[0]), (const UINT8 *)hasharray); - sha1_final(&sha1); - sha1_digest(&sha1, SHA1_DIGEST_SIZE, finalsha1); - -cleanup: - if (hasharray != NULL) - free(hasharray); - return err; -} - - -/*------------------------------------------------- - metadata_hash_compare - compare two hash - entries --------------------------------------------------*/ - -static int metadata_hash_compare(const void *elem1, const void *elem2) -{ - return memcmp(elem1, elem2, sizeof(metadata_hash)); -} - - - -/*************************************************************************** - ZLIB COMPRESSION CODEC -***************************************************************************/ - -/*------------------------------------------------- - zlib_codec_init - initialize the ZLIB codec --------------------------------------------------*/ - -static chd_error zlib_codec_init(chd_file *chd) -{ - zlib_codec_data *data; - chd_error err; - int zerr; - - /* allocate memory for the 2 stream buffers */ - data = (zlib_codec_data *)malloc(sizeof(*data)); - if (data == NULL) - return CHDERR_OUT_OF_MEMORY; - - /* clear the buffers */ - memset(data, 0, sizeof(*data)); - - /* init the inflater first */ - data->inflater.next_in = (Bytef *)data; /* bogus, but that's ok */ - data->inflater.avail_in = 0; - data->inflater.zalloc = zlib_fast_alloc; - data->inflater.zfree = zlib_fast_free; - data->inflater.opaque = data; - zerr = inflateInit2(&data->inflater, -MAX_WBITS); - - /* if that worked, initialize the deflater */ - if (zerr == Z_OK) - { - data->deflater.next_in = (Bytef *)data; /* bogus, but that's ok */ - data->deflater.avail_in = 0; - data->deflater.zalloc = zlib_fast_alloc; - data->deflater.zfree = zlib_fast_free; - data->deflater.opaque = data; - zerr = deflateInit2(&data->deflater, Z_BEST_COMPRESSION, Z_DEFLATED, -MAX_WBITS, 8, Z_DEFAULT_STRATEGY); - } - - /* convert errors */ - if (zerr == Z_MEM_ERROR) - err = CHDERR_OUT_OF_MEMORY; - else if (zerr != Z_OK) - err = CHDERR_CODEC_ERROR; - else - err = CHDERR_NONE; - - /* handle an error */ - if (err == CHDERR_NONE) - chd->codecdata = data; - else - free(data); - - return err; -} - - -/*------------------------------------------------- - zlib_codec_free - free data for the ZLIB - codec --------------------------------------------------*/ - -static void zlib_codec_free(chd_file *chd) -{ - zlib_codec_data *data = (zlib_codec_data *)chd->codecdata; - - /* deinit the streams */ - if (data != NULL) - { - int i; - - inflateEnd(&data->inflater); - deflateEnd(&data->deflater); - - /* free our fast memory */ - for (i = 0; i < MAX_ZLIB_ALLOCS; i++) - if (data->allocptr[i]) - free(data->allocptr[i]); - free(data); - } -} - - -/*------------------------------------------------- - zlib_codec_compress - compress data using the - ZLIB codec --------------------------------------------------*/ - -static chd_error zlib_codec_compress(chd_file *chd, const void *src, UINT32 *length) -{ - zlib_codec_data *data = (zlib_codec_data *)chd->codecdata; - int zerr; - - /* reset the decompressor */ - data->deflater.next_in = (Bytef *)src; - data->deflater.avail_in = chd->header.hunkbytes; - data->deflater.total_in = 0; - data->deflater.next_out = chd->compressed; - data->deflater.avail_out = chd->header.hunkbytes; - data->deflater.total_out = 0; - zerr = deflateReset(&data->deflater); - if (zerr != Z_OK) - return CHDERR_COMPRESSION_ERROR; - - /* do it */ - zerr = deflate(&data->deflater, Z_FINISH); - - /* if we ended up with more data than we started with, return an error */ - if (zerr != Z_STREAM_END || data->deflater.total_out >= chd->header.hunkbytes) - return CHDERR_COMPRESSION_ERROR; - - /* otherwise, fill in the length and return success */ - *length = data->deflater.total_out; - return CHDERR_NONE; -} - - -/*------------------------------------------------- - zlib_codec_decompress - decomrpess data using - the ZLIB codec --------------------------------------------------*/ - -static chd_error zlib_codec_decompress(chd_file *chd, UINT32 srclength, void *dest) -{ - zlib_codec_data *data = (zlib_codec_data *)chd->codecdata; - int zerr; - - /* reset the decompressor */ - data->inflater.next_in = chd->compressed; - data->inflater.avail_in = srclength; - data->inflater.total_in = 0; - data->inflater.next_out = (Bytef *)dest; - data->inflater.avail_out = chd->header.hunkbytes; - data->inflater.total_out = 0; - zerr = inflateReset(&data->inflater); - if (zerr != Z_OK) - return CHDERR_DECOMPRESSION_ERROR; - - /* do it */ - zerr = inflate(&data->inflater, Z_FINISH); - if (data->inflater.total_out != chd->header.hunkbytes) - return CHDERR_DECOMPRESSION_ERROR; - - return CHDERR_NONE; -} - - -/*------------------------------------------------- - zlib_fast_alloc - fast malloc for ZLIB, which - allocates and frees memory frequently --------------------------------------------------*/ - -static voidpf zlib_fast_alloc(voidpf opaque, uInt items, uInt size) -{ - zlib_codec_data *data = (zlib_codec_data *)opaque; - UINT32 *ptr; - int i; - - /* compute the size, rounding to the nearest 1k */ - size = (size * items + 0x3ff) & ~0x3ff; - - /* reuse a hunk if we can */ - for (i = 0; i < MAX_ZLIB_ALLOCS; i++) - { - ptr = data->allocptr[i]; - if (ptr && size == *ptr) - { - /* set the low bit of the size so we don't match next time */ - *ptr |= 1; - return ptr + 1; - } - } - - /* alloc a new one */ - ptr = (UINT32 *)malloc(size + sizeof(UINT32)); - if (!ptr) - return NULL; - - /* put it into the list */ - for (i = 0; i < MAX_ZLIB_ALLOCS; i++) - if (!data->allocptr[i]) - { - data->allocptr[i] = ptr; - break; - } - - /* set the low bit of the size so we don't match next time */ - *ptr = size | 1; - return ptr + 1; -} - - -/*------------------------------------------------- - zlib_fast_free - fast free for ZLIB, which - allocates and frees memory frequently --------------------------------------------------*/ - -static void zlib_fast_free(voidpf opaque, voidpf address) -{ - zlib_codec_data *data = (zlib_codec_data *)opaque; - UINT32 *ptr = (UINT32 *)address - 1; - int i; - - /* find the hunk */ - for (i = 0; i < MAX_ZLIB_ALLOCS; i++) - if (ptr == data->allocptr[i]) - { - /* clear the low bit of the size to allow matches */ - *ptr &= ~1; - return; - } -} - - - -/*************************************************************************** - AV COMPRESSION CODEC -***************************************************************************/ - -/*------------------------------------------------- - av_raw_data_size - compute the raw data size --------------------------------------------------*/ - -INLINE UINT32 av_raw_data_size(const UINT8 *data) -{ - int size = 0; - - /* make sure we have a correct header */ - if (data[0] == 'c' && data[1] == 'h' && data[2] == 'a' && data[3] == 'v') - { - /* add in header size plus metadata length */ - size = 12 + data[4]; - - /* add in channels * samples */ - size += 2 * data[5] * ((data[6] << 8) + data[7]); - - /* add in 2 * width * height */ - size += 2 * ((data[8] << 8) + data[9]) * (((data[10] << 8) + data[11]) & 0x7fff); - } - return size; -} - - -/*------------------------------------------------- - av_codec_init - initialize the A/V codec --------------------------------------------------*/ - - - -/*------------------------------------------------- - av_codec_free - free data for the A/V - codec --------------------------------------------------*/ diff --git a/core/deps/chdr/coretypes.h b/core/deps/chdr/coretypes.h index ce2bb2a39..5aecc14de 100644 --- a/core/deps/chdr/coretypes.h +++ b/core/deps/chdr/coretypes.h @@ -1,18 +1,34 @@ +#ifndef __CORETYPES_H__ +#define __CORETYPES_H__ -#include "types.h" +#include +#include -typedef u64 UINT64; -typedef u32 UINT32; -typedef u16 UINT16; -typedef u8 UINT8; +#define ARRAY_LENGTH(x) (sizeof(x)/sizeof(x[0])) -typedef s64 INT64; -typedef s32 INT32; -typedef s16 INT16; -typedef s8 INT8; +typedef uint64_t UINT64; +typedef uint32_t UINT32; +typedef uint16_t UINT16; +typedef uint8_t UINT8; + +typedef int64_t INT64; +typedef int32_t INT32; +typedef int16_t INT16; +typedef int8_t INT8; + +#define core_file FILE +#define core_fopen(file) fopen(file, "rb") +#define core_fseek fseek +#define core_fread(fc, buff, len) fread(buff, 1, len, fc) +#define core_fclose fclose +#define core_ftell ftell +static size_t core_fsize(core_file *f) +{ + long p = ftell(f); + fseek(f, 0, SEEK_END); + long rv = ftell(f); + fseek(f, p, SEEK_SET); + return rv; +} -#ifndef INLINE -#define INLINE inline #endif - -#define ARRAY_LENGTH(x) (sizeof(x)/sizeof(x[0])) \ No newline at end of file diff --git a/core/deps/chdr/flac.c b/core/deps/chdr/flac.c new file mode 100644 index 000000000..8e31ed6b7 --- /dev/null +++ b/core/deps/chdr/flac.c @@ -0,0 +1,331 @@ +/* license:BSD-3-Clause + * copyright-holders:Aaron Giles +*************************************************************************** + + flac.c + + FLAC compression wrappers + +***************************************************************************/ + +#include +#include +#include "flac.h" + +/*************************************************************************** + * FLAC DECODER + *************************************************************************** + */ + +static FLAC__StreamDecoderReadStatus flac_decoder_read_callback_static(const FLAC__StreamDecoder *decoder, FLAC__byte buffer[], size_t *bytes, void *client_data); +FLAC__StreamDecoderReadStatus flac_decoder_read_callback(void* client_data, FLAC__byte buffer[], size_t *bytes); +static void flac_decoder_metadata_callback_static(const FLAC__StreamDecoder *decoder, const FLAC__StreamMetadata *metadata, void *client_data); +static FLAC__StreamDecoderTellStatus flac_decoder_tell_callback_static(const FLAC__StreamDecoder *decoder, FLAC__uint64 *absolute_byte_offset, void *client_data); +static FLAC__StreamDecoderWriteStatus flac_decoder_write_callback_static(const FLAC__StreamDecoder *decoder, const FLAC__Frame *frame, const FLAC__int32 * const buffer[], void *client_data); +FLAC__StreamDecoderWriteStatus flac_decoder_write_callback(void* client_data, const FLAC__Frame *frame, const FLAC__int32 * const buffer[]); +static void flac_decoder_error_callback_static(const FLAC__StreamDecoder *decoder, FLAC__StreamDecoderErrorStatus status, void *client_data); + +/* getters (valid after reset) */ +static uint32_t sample_rate(flac_decoder *decoder) { return decoder->sample_rate; } +static uint8_t channels(flac_decoder *decoder) { return decoder->channels; } +static uint8_t bits_per_sample(flac_decoder *decoder) { return decoder->bits_per_sample; } +static uint32_t total_samples(flac_decoder *decoder) { return FLAC__stream_decoder_get_total_samples(decoder->decoder); } +static FLAC__StreamDecoderState state(flac_decoder *decoder) { return FLAC__stream_decoder_get_state(decoder->decoder); } +static const char *state_string(flac_decoder *decoder) { return FLAC__stream_decoder_get_resolved_state_string(decoder->decoder); } + +/*------------------------------------------------- + * flac_decoder - constructor + *------------------------------------------------- + */ + +void flac_decoder_init(flac_decoder *decoder) +{ + decoder->decoder = FLAC__stream_decoder_new(); + decoder->sample_rate = 0; + decoder->channels = 0; + decoder->bits_per_sample = 0; + decoder->compressed_offset = 0; + decoder->compressed_start = NULL; + decoder->compressed_length = 0; + decoder->compressed2_start = NULL; + decoder->compressed2_length = 0; + decoder->uncompressed_offset = 0; + decoder->uncompressed_length = 0; + decoder->uncompressed_swap = 0; +} + +/*------------------------------------------------- + * flac_decoder - destructor + *------------------------------------------------- + */ + +void flac_decoder_free(flac_decoder* decoder) +{ + if ((decoder != NULL) && (decoder->decoder != NULL)) + FLAC__stream_decoder_delete(decoder->decoder); +} + +/*------------------------------------------------- + * reset - reset state with the original + * parameters + *------------------------------------------------- + */ + +static int flac_decoder_internal_reset(flac_decoder* decoder) +{ + decoder->compressed_offset = 0; + if (FLAC__stream_decoder_init_stream(decoder->decoder, + &flac_decoder_read_callback_static, + NULL, + &flac_decoder_tell_callback_static, + NULL, + NULL, + &flac_decoder_write_callback_static, + &flac_decoder_metadata_callback_static, + &flac_decoder_error_callback_static, decoder) != FLAC__STREAM_DECODER_INIT_STATUS_OK) + return 0; + return FLAC__stream_decoder_process_until_end_of_metadata(decoder->decoder); +} + +/*------------------------------------------------- + * reset - reset state with new memory parameters + * and a custom-generated header + *------------------------------------------------- + */ + +int flac_decoder_reset(flac_decoder* decoder, uint32_t sample_rate, uint8_t num_channels, uint32_t block_size, const void *buffer, uint32_t length) +{ + /* modify the template header with our parameters */ + static const uint8_t s_header_template[0x2a] = + { + 0x66, 0x4C, 0x61, 0x43, /* +00: 'fLaC' stream header */ + 0x80, /* +04: metadata block type 0 (STREAMINFO), */ + /* flagged as last block */ + 0x00, 0x00, 0x22, /* +05: metadata block length = 0x22 */ + 0x00, 0x00, /* +08: minimum block size */ + 0x00, 0x00, /* +0A: maximum block size */ + 0x00, 0x00, 0x00, /* +0C: minimum frame size (0 == unknown) */ + 0x00, 0x00, 0x00, /* +0F: maximum frame size (0 == unknown) */ + 0x0A, 0xC4, 0x42, 0xF0, 0x00, 0x00, 0x00, 0x00, /* +12: sample rate (0x0ac44 == 44100), */ + /* numchannels (2), sample bits (16), */ + /* samples in stream (0 == unknown) */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* +1A: MD5 signature (0 == none) */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 /* +2A: start of stream data */ + }; + memcpy(decoder->custom_header, s_header_template, sizeof(s_header_template)); + decoder->custom_header[0x08] = decoder->custom_header[0x0a] = block_size >> 8; + decoder->custom_header[0x09] = decoder->custom_header[0x0b] = block_size & 0xff; + decoder->custom_header[0x12] = sample_rate >> 12; + decoder->custom_header[0x13] = sample_rate >> 4; + decoder->custom_header[0x14] = (sample_rate << 4) | ((num_channels - 1) << 1); + + /* configure the header ahead of the provided buffer */ + decoder->compressed_start = (const FLAC__byte *)(decoder->custom_header); + decoder->compressed_length = sizeof(decoder->custom_header); + decoder->compressed2_start = (const FLAC__byte *)(buffer); + decoder->compressed2_length = length; + return flac_decoder_internal_reset(decoder); +} + +/*------------------------------------------------- + * decode_interleaved - decode to an interleaved + * sound stream + *------------------------------------------------- + */ + +int flac_decoder_decode_interleaved(flac_decoder* decoder, int16_t *samples, uint32_t num_samples, int swap_endian) +{ + /* configure the uncompressed buffer */ + memset(decoder->uncompressed_start, 0, sizeof(decoder->uncompressed_start)); + decoder->uncompressed_start[0] = samples; + decoder->uncompressed_offset = 0; + decoder->uncompressed_length = num_samples; + decoder->uncompressed_swap = swap_endian; + + /* loop until we get everything we want */ + while (decoder->uncompressed_offset < decoder->uncompressed_length) + if (!FLAC__stream_decoder_process_single(decoder->decoder)) + return 0; + return 1; +} + +#if 0 +/*------------------------------------------------- + * decode - decode to an multiple independent + * data streams + *------------------------------------------------- + */ + +bool flac_decoder::decode(int16_t **samples, uint32_t num_samples, bool swap_endian) +{ + /* make sure we don't have too many channels */ + int chans = channels(); + if (chans > ARRAY_LENGTH(m_uncompressed_start)) + return false; + + /* configure the uncompressed buffer */ + memset(m_uncompressed_start, 0, sizeof(m_uncompressed_start)); + for (int curchan = 0; curchan < chans; curchan++) + m_uncompressed_start[curchan] = samples[curchan]; + m_uncompressed_offset = 0; + m_uncompressed_length = num_samples; + m_uncompressed_swap = swap_endian; + + /* loop until we get everything we want */ + while (m_uncompressed_offset < m_uncompressed_length) + if (!FLAC__stream_decoder_process_single(m_decoder)) + return false; + return true; +} +#endif + +/*------------------------------------------------- + * finish - finish up the decode + *------------------------------------------------- + */ + +uint32_t flac_decoder_finish(flac_decoder* decoder) +{ + /* get the final decoding position and move forward */ + FLAC__uint64 position = 0; + FLAC__stream_decoder_get_decode_position(decoder->decoder, &position); + FLAC__stream_decoder_finish(decoder->decoder); + + /* adjust position if we provided the header */ + if (position == 0) + return 0; + if (decoder->compressed_start == (const FLAC__byte *)(decoder->custom_header)) + position -= decoder->compressed_length; + return position; +} + +/*------------------------------------------------- + * read_callback - handle reads from the input + * stream + *------------------------------------------------- + */ + +#define MIN(x, y) ((x) < (y) ? (x) : (y)) + +FLAC__StreamDecoderReadStatus flac_decoder_read_callback_static(const FLAC__StreamDecoder *decoder, FLAC__byte buffer[], size_t *bytes, void *client_data) +{ + return flac_decoder_read_callback(client_data, buffer, bytes); +} + +FLAC__StreamDecoderReadStatus flac_decoder_read_callback(void* client_data, FLAC__byte buffer[], size_t *bytes) +{ + flac_decoder* decoder = (flac_decoder*)client_data; + + uint32_t expected = *bytes; + + /* copy from primary buffer first */ + uint32_t outputpos = 0; + if (outputpos < *bytes && decoder->compressed_offset < decoder->compressed_length) + { + uint32_t bytes_to_copy = MIN(*bytes - outputpos, decoder->compressed_length - decoder->compressed_offset); + memcpy(&buffer[outputpos], decoder->compressed_start + decoder->compressed_offset, bytes_to_copy); + outputpos += bytes_to_copy; + decoder->compressed_offset += bytes_to_copy; + } + + /* once we're out of that, copy from the secondary buffer */ + if (outputpos < *bytes && decoder->compressed_offset < decoder->compressed_length + decoder->compressed2_length) + { + uint32_t bytes_to_copy = MIN(*bytes - outputpos, decoder->compressed2_length - (decoder->compressed_offset - decoder->compressed_length)); + memcpy(&buffer[outputpos], decoder->compressed2_start + decoder->compressed_offset - decoder->compressed_length, bytes_to_copy); + outputpos += bytes_to_copy; + decoder->compressed_offset += bytes_to_copy; + } + *bytes = outputpos; + + /* return based on whether we ran out of data */ + return (*bytes < expected) ? FLAC__STREAM_DECODER_READ_STATUS_END_OF_STREAM : FLAC__STREAM_DECODER_READ_STATUS_CONTINUE; +} + +/*------------------------------------------------- + * metadata_callback - handle STREAMINFO metadata + *------------------------------------------------- + */ + +void flac_decoder_metadata_callback_static(const FLAC__StreamDecoder *decoder, const FLAC__StreamMetadata *metadata, void *client_data) +{ + flac_decoder *fldecoder; + /* ignore all but STREAMINFO metadata */ + if (metadata->type != FLAC__METADATA_TYPE_STREAMINFO) + return; + + /* parse out the data we care about */ + fldecoder = (flac_decoder *)(client_data); + fldecoder->sample_rate = metadata->data.stream_info.sample_rate; + fldecoder->bits_per_sample = metadata->data.stream_info.bits_per_sample; + fldecoder->channels = metadata->data.stream_info.channels; +} + +/*------------------------------------------------- + * tell_callback - handle requests to find out + * where in the input stream we are + *------------------------------------------------- + */ + +FLAC__StreamDecoderTellStatus flac_decoder_tell_callback_static(const FLAC__StreamDecoder *decoder, FLAC__uint64 *absolute_byte_offset, void *client_data) +{ + *absolute_byte_offset = ((flac_decoder *)client_data)->compressed_offset; + return FLAC__STREAM_DECODER_TELL_STATUS_OK; +} + +/*------------------------------------------------- + * write_callback - handle writes to the output + * stream + *------------------------------------------------- + */ + +FLAC__StreamDecoderWriteStatus flac_decoder_write_callback_static(const FLAC__StreamDecoder *decoder, const FLAC__Frame *frame, const FLAC__int32 * const buffer[], void *client_data) +{ + return flac_decoder_write_callback(client_data, frame, buffer); +} + +FLAC__StreamDecoderWriteStatus flac_decoder_write_callback(void *client_data, const FLAC__Frame *frame, const FLAC__int32 * const buffer[]) +{ + int sampnum, chan; + int shift, blocksize; + flac_decoder * decoder = (flac_decoder *)client_data; + + assert(frame->header.channels == channels(decoder)); + + /* interleaved case */ + shift = decoder->uncompressed_swap ? 8 : 0; + blocksize = frame->header.blocksize; + if (decoder->uncompressed_start[1] == NULL) + { + int16_t *dest = decoder->uncompressed_start[0] + decoder->uncompressed_offset * frame->header.channels; + for (sampnum = 0; sampnum < blocksize && decoder->uncompressed_offset < decoder->uncompressed_length; sampnum++, decoder->uncompressed_offset++) + for (chan = 0; chan < frame->header.channels; chan++) + *dest++ = (int16_t)((((uint16_t)buffer[chan][sampnum]) << shift) | (((uint16_t)buffer[chan][sampnum]) >> shift)); + } + + /* non-interleaved case */ + else + { + for (sampnum = 0; sampnum < blocksize && decoder->uncompressed_offset < decoder->uncompressed_length; sampnum++, decoder->uncompressed_offset++) + for (chan = 0; chan < frame->header.channels; chan++) + if (decoder->uncompressed_start[chan] != NULL) + decoder->uncompressed_start[chan][decoder->uncompressed_offset] = (int16_t) ( (((uint16_t)(buffer[chan][sampnum])) << shift) | ( ((uint16_t)(buffer[chan][sampnum])) >> shift) ); + } + return FLAC__STREAM_DECODER_WRITE_STATUS_CONTINUE; +} + +/** + * @fn void flac_decoder::error_callback_static(const FLAC__StreamDecoder *decoder, FLAC__StreamDecoderErrorStatus status, void *client_data) + * + * @brief ------------------------------------------------- + * error_callback - handle errors (ignore them) + * -------------------------------------------------. + * + * @param decoder The decoder. + * @param status The status. + * @param [in,out] client_data If non-null, information describing the client. + */ + +void flac_decoder_error_callback_static(const FLAC__StreamDecoder *decoder, FLAC__StreamDecoderErrorStatus status, void *client_data) +{ +} diff --git a/core/deps/chdr/flac.h b/core/deps/chdr/flac.h new file mode 100644 index 000000000..5a3bf2d9c --- /dev/null +++ b/core/deps/chdr/flac.h @@ -0,0 +1,51 @@ +/* license:BSD-3-Clause + * copyright-holders:Aaron Giles + *************************************************************************** + + flac.h + + FLAC compression wrappers + +***************************************************************************/ + +#pragma once + +#ifndef __FLAC_H__ +#define __FLAC_H__ + +#include +#include "FLAC/all.h" + +/*************************************************************************** + * TYPE DEFINITIONS + *************************************************************************** + */ + +typedef struct _flac_decoder flac_decoder; +struct _flac_decoder { + /* output state */ + FLAC__StreamDecoder* decoder; /* actual encoder */ + uint32_t sample_rate; /* decoded sample rate */ + uint8_t channels; /* decoded number of channels */ + uint8_t bits_per_sample; /* decoded bits per sample */ + uint32_t compressed_offset; /* current offset in compressed data */ + const FLAC__byte * compressed_start; /* start of compressed data */ + uint32_t compressed_length; /* length of compressed data */ + const FLAC__byte * compressed2_start; /* start of compressed data */ + uint32_t compressed2_length; /* length of compressed data */ + int16_t * uncompressed_start[8]; /* pointer to start of uncompressed data (up to 8 streams) */ + uint32_t uncompressed_offset; /* current position in uncompressed data */ + uint32_t uncompressed_length; /* length of uncompressed data */ + int uncompressed_swap; /* swap uncompressed sample data */ + uint8_t custom_header[0x2a]; /* custom header */ +}; + +/* ======================> flac_decoder */ + +void flac_decoder_init(flac_decoder* decoder); +void flac_decoder_free(flac_decoder* decoder); +int flac_decoder_reset(flac_decoder* decoder, uint32_t sample_rate, uint8_t num_channels, uint32_t block_size, const void *buffer, uint32_t length); +int flac_decoder_decode_interleaved(flac_decoder* decoder, int16_t *samples, uint32_t num_samples, int swap_endian); +uint32_t flac_decoder_finish(flac_decoder* decoder); + +#endif /* __FLAC_H__ */ diff --git a/core/deps/chdr/huffman.c b/core/deps/chdr/huffman.c new file mode 100644 index 000000000..f48210ea5 --- /dev/null +++ b/core/deps/chdr/huffman.c @@ -0,0 +1,514 @@ +/* license:BSD-3-Clause + * copyright-holders:Aaron Giles +**************************************************************************** + + huffman.c + + Static Huffman compression and decompression helpers. + +**************************************************************************** + + Maximum codelength is officially (alphabetsize - 1). This would be 255 bits + (since we use 1 byte values). However, it is also dependent upon the number + of samples used, as follows: + + 2 bits -> 3..4 samples + 3 bits -> 5..7 samples + 4 bits -> 8..12 samples + 5 bits -> 13..20 samples + 6 bits -> 21..33 samples + 7 bits -> 34..54 samples + 8 bits -> 55..88 samples + 9 bits -> 89..143 samples + 10 bits -> 144..232 samples + 11 bits -> 233..376 samples + 12 bits -> 377..609 samples + 13 bits -> 610..986 samples + 14 bits -> 987..1596 samples + 15 bits -> 1597..2583 samples + 16 bits -> 2584..4180 samples -> note that a 4k data size guarantees codelength <= 16 bits + 17 bits -> 4181..6764 samples + 18 bits -> 6765..10945 samples + 19 bits -> 10946..17710 samples + 20 bits -> 17711..28656 samples + 21 bits -> 28657..46367 samples + 22 bits -> 46368..75024 samples + 23 bits -> 75025..121392 samples + 24 bits -> 121393..196417 samples + 25 bits -> 196418..317810 samples + 26 bits -> 317811..514228 samples + 27 bits -> 514229..832039 samples + 28 bits -> 832040..1346268 samples + 29 bits -> 1346269..2178308 samples + 30 bits -> 2178309..3524577 samples + 31 bits -> 3524578..5702886 samples + 32 bits -> 5702887..9227464 samples + + Looking at it differently, here is where powers of 2 fall into these buckets: + + 256 samples -> 11 bits max + 512 samples -> 12 bits max + 1k samples -> 14 bits max + 2k samples -> 15 bits max + 4k samples -> 16 bits max + 8k samples -> 18 bits max + 16k samples -> 19 bits max + 32k samples -> 21 bits max + 64k samples -> 22 bits max + 128k samples -> 24 bits max + 256k samples -> 25 bits max + 512k samples -> 27 bits max + 1M samples -> 28 bits max + 2M samples -> 29 bits max + 4M samples -> 31 bits max + 8M samples -> 32 bits max + +**************************************************************************** + + Delta-RLE encoding works as follows: + + Starting value is assumed to be 0. All data is encoded as a delta + from the previous value, such that final[i] = final[i - 1] + delta. + Long runs of 0s are RLE-encoded as follows: + + 0x100 = repeat count of 8 + 0x101 = repeat count of 9 + 0x102 = repeat count of 10 + 0x103 = repeat count of 11 + 0x104 = repeat count of 12 + 0x105 = repeat count of 13 + 0x106 = repeat count of 14 + 0x107 = repeat count of 15 + 0x108 = repeat count of 16 + 0x109 = repeat count of 32 + 0x10a = repeat count of 64 + 0x10b = repeat count of 128 + 0x10c = repeat count of 256 + 0x10d = repeat count of 512 + 0x10e = repeat count of 1024 + 0x10f = repeat count of 2048 + + Note that repeat counts are reset at the end of a row, so if a 0 run + extends to the end of a row, a large repeat count may be used. + + The reason for starting the run counts at 8 is that 0 is expected to + be the most common symbol, and is typically encoded in 1 or 2 bits. + +***************************************************************************/ + +#include +#include +#include +#include + +#include "huffman.h" + +#define MAX(x,y) ((x) > (y) ? (x) : (y)) + +/*************************************************************************** + * MACROS + *************************************************************************** + */ + +#define MAKE_LOOKUP(code,bits) (((code) << 5) | ((bits) & 0x1f)) + +/*************************************************************************** + * IMPLEMENTATION + *************************************************************************** + */ + +/*------------------------------------------------- + * huffman_context_base - create an encoding/ + * decoding context + *------------------------------------------------- + */ + +struct huffman_decoder* create_huffman_decoder(int numcodes, int maxbits) +{ + /* limit to 24 bits */ + if (maxbits > 24) + return NULL; + + struct huffman_decoder* decoder = (struct huffman_decoder*)malloc(sizeof(struct huffman_decoder)); + decoder->numcodes = numcodes; + decoder->maxbits = maxbits; + decoder->lookup = (lookup_value*)malloc(sizeof(lookup_value) * (1 << maxbits)); + decoder->huffnode = (struct node_t*)malloc(sizeof(struct node_t) * numcodes); + decoder->datahisto = NULL; + decoder->prevdata = 0; + decoder->rleremaining = 0; + return decoder; +} + +/*------------------------------------------------- + * decode_one - decode a single code from the + * huffman stream + *------------------------------------------------- + */ + +uint32_t huffman_decode_one(struct huffman_decoder* decoder, struct bitstream* bitbuf) +{ + /* peek ahead to get maxbits worth of data */ + uint32_t bits = bitstream_peek(bitbuf, decoder->maxbits); + + /* look it up, then remove the actual number of bits for this code */ + lookup_value lookup = decoder->lookup[bits]; + bitstream_remove(bitbuf, lookup & 0x1f); + + /* return the value */ + return lookup >> 5; +} + +/*------------------------------------------------- + * import_tree_rle - import an RLE-encoded + * huffman tree from a source data stream + *------------------------------------------------- + */ + +enum huffman_error huffman_import_tree_rle(struct huffman_decoder* decoder, struct bitstream* bitbuf) +{ + /* bits per entry depends on the maxbits */ + int numbits; + if (decoder->maxbits >= 16) + numbits = 5; + else if (decoder->maxbits >= 8) + numbits = 4; + else + numbits = 3; + + /* loop until we read all the nodes */ + int curnode; + for (curnode = 0; curnode < decoder->numcodes; ) + { + /* a non-one value is just raw */ + int nodebits = bitstream_read(bitbuf, numbits); + if (nodebits != 1) + decoder->huffnode[curnode++].numbits = nodebits; + + /* a one value is an escape code */ + else + { + /* a double 1 is just a single 1 */ + nodebits = bitstream_read(bitbuf, numbits); + if (nodebits == 1) + decoder->huffnode[curnode++].numbits = nodebits; + + /* otherwise, we need one for value for the repeat count */ + else + { + int repcount = bitstream_read(bitbuf, numbits) + 3; + while (repcount--) + decoder->huffnode[curnode++].numbits = nodebits; + } + } + } + + /* make sure we ended up with the right number */ + if (curnode != decoder->numcodes) + return HUFFERR_INVALID_DATA; + + /* assign canonical codes for all nodes based on their code lengths */ + enum huffman_error error = huffman_assign_canonical_codes(decoder); + if (error != HUFFERR_NONE) + return error; + + /* build the lookup table */ + huffman_build_lookup_table(decoder); + + /* determine final input length and report errors */ + return bitstream_overflow(bitbuf) ? HUFFERR_INPUT_BUFFER_TOO_SMALL : HUFFERR_NONE; +} + + +/*------------------------------------------------- + * import_tree_huffman - import a huffman-encoded + * huffman tree from a source data stream + *------------------------------------------------- + */ + +enum huffman_error huffman_import_tree_huffman(struct huffman_decoder* decoder, struct bitstream* bitbuf) +{ + /* start by parsing the lengths for the small tree */ + struct huffman_decoder* smallhuff = create_huffman_decoder(24, 6); + smallhuff->huffnode[0].numbits = bitstream_read(bitbuf, 3); + int start = bitstream_read(bitbuf, 3) + 1; + int count = 0; + for (int index = 1; index < 24; index++) + { + if (index < start || count == 7) + smallhuff->huffnode[index].numbits = 0; + else + { + count = bitstream_read(bitbuf, 3); + smallhuff->huffnode[index].numbits = (count == 7) ? 0 : count; + } + } + + /* then regenerate the tree */ + enum huffman_error error = huffman_assign_canonical_codes(smallhuff); + if (error != HUFFERR_NONE) + return error; + huffman_build_lookup_table(smallhuff); + + /* determine the maximum length of an RLE count */ + uint32_t temp = decoder->numcodes - 9; + uint8_t rlefullbits = 0; + while (temp != 0) + temp >>= 1, rlefullbits++; + + /* now process the rest of the data */ + int last = 0; + int curcode; + for (curcode = 0; curcode < decoder->numcodes; ) + { + int value = huffman_decode_one(smallhuff, bitbuf); + if (value != 0) + decoder->huffnode[curcode++].numbits = last = value - 1; + else + { + int count = bitstream_read(bitbuf, 3) + 2; + if (count == 7+2) + count += bitstream_read(bitbuf, rlefullbits); + for ( ; count != 0 && curcode < decoder->numcodes; count--) + decoder->huffnode[curcode++].numbits = last; + } + } + + /* make sure we ended up with the right number */ + if (curcode != decoder->numcodes) + return HUFFERR_INVALID_DATA; + + /* assign canonical codes for all nodes based on their code lengths */ + error = huffman_assign_canonical_codes(decoder); + if (error != HUFFERR_NONE) + return error; + + /* build the lookup table */ + huffman_build_lookup_table(decoder); + + /* determine final input length and report errors */ + return bitstream_overflow(bitbuf) ? HUFFERR_INPUT_BUFFER_TOO_SMALL : HUFFERR_NONE; +} + +/*------------------------------------------------- + * compute_tree_from_histo - common backend for + * computing a tree based on the data histogram + *------------------------------------------------- + */ + +enum huffman_error huffman_compute_tree_from_histo(struct huffman_decoder* decoder) +{ + /* compute the number of data items in the histogram */ + uint32_t sdatacount = 0; + for (int i = 0; i < decoder->numcodes; i++) + sdatacount += decoder->datahisto[i]; + + /* binary search to achieve the optimum encoding */ + uint32_t lowerweight = 0; + uint32_t upperweight = sdatacount * 2; + while (1) + { + /* build a tree using the current weight */ + uint32_t curweight = (upperweight + lowerweight) / 2; + int curmaxbits = huffman_build_tree(decoder, sdatacount, curweight); + + /* apply binary search here */ + if (curmaxbits <= decoder->maxbits) + { + lowerweight = curweight; + + /* early out if it worked with the raw weights, or if we're done searching */ + if (curweight == sdatacount || (upperweight - lowerweight) <= 1) + break; + } + else + upperweight = curweight; + } + + /* assign canonical codes for all nodes based on their code lengths */ + return huffman_assign_canonical_codes(decoder); +} + +/*************************************************************************** + * INTERNAL FUNCTIONS + *************************************************************************** + */ + +/*------------------------------------------------- + * tree_node_compare - compare two tree nodes + * by weight + *------------------------------------------------- + */ + +static int huffman_tree_node_compare(const void *item1, const void *item2) +{ + const struct node_t *node1 = *(const struct node_t **)item1; + const struct node_t *node2 = *(const struct node_t **)item2; + if (node2->weight != node1->weight) + return node2->weight - node1->weight; + if (node2->bits - node1->bits == 0) + fprintf(stderr, "identical node sort keys, should not happen!\n"); + return (int)node1->bits - (int)node2->bits; +} + +/*------------------------------------------------- + * build_tree - build a huffman tree based on the + * data distribution + *------------------------------------------------- + */ + +int huffman_build_tree(struct huffman_decoder* decoder, uint32_t totaldata, uint32_t totalweight) +{ + /* make a list of all non-zero nodes */ + struct node_t** list = (struct node_t**)malloc(sizeof(struct node_t*) * decoder->numcodes * 2); + int listitems = 0; + memset(decoder->huffnode, 0, decoder->numcodes * sizeof(decoder->huffnode[0])); + for (int curcode = 0; curcode < decoder->numcodes; curcode++) + if (decoder->datahisto[curcode] != 0) + { + list[listitems++] = &decoder->huffnode[curcode]; + decoder->huffnode[curcode].count = decoder->datahisto[curcode]; + decoder->huffnode[curcode].bits = curcode; + + /* scale the weight by the current effective length, ensuring we don't go to 0 */ + decoder->huffnode[curcode].weight = ((uint64_t)decoder->datahisto[curcode]) * ((uint64_t)totalweight) / ((uint64_t)totaldata); + if (decoder->huffnode[curcode].weight == 0) + decoder->huffnode[curcode].weight = 1; + } + +#if 0 + fprintf(stderr, "Pre-sort:\n"); + for (int i = 0; i < listitems; i++) { + fprintf(stderr, "weight: %d code: %d\n", list[i]->m_weight, list[i]->m_bits); + } +#endif + + /* sort the list by weight, largest weight first */ + qsort(&list[0], listitems, sizeof(list[0]), huffman_tree_node_compare); + +#if 0 + fprintf(stderr, "Post-sort:\n"); + for (int i = 0; i < listitems; i++) { + fprintf(stderr, "weight: %d code: %d\n", list[i]->m_weight, list[i]->m_bits); + } + fprintf(stderr, "===================\n"); +#endif + + /* now build the tree */ + int nextalloc = decoder->numcodes; + while (listitems > 1) + { + /* remove lowest two items */ + struct node_t* node1 = &(*list[--listitems]); + struct node_t* node0 = &(*list[--listitems]); + + /* create new node */ + struct node_t* newnode = &decoder->huffnode[nextalloc++]; + newnode->parent = NULL; + node0->parent = node1->parent = newnode; + newnode->weight = node0->weight + node1->weight; + + /* insert into list at appropriate location */ + int curitem; + for (curitem = 0; curitem < listitems; curitem++) + if (newnode->weight > list[curitem]->weight) + { + memmove(&list[curitem+1], &list[curitem], (listitems - curitem) * sizeof(list[0])); + break; + } + list[curitem] = newnode; + listitems++; + } + + /* compute the number of bits in each code, and fill in another histogram */ + int maxbits = 0; + for (int curcode = 0; curcode < decoder->numcodes; curcode++) + { + struct node_t* node = &decoder->huffnode[curcode]; + node->numbits = 0; + node->bits = 0; + + /* if we have a non-zero weight, compute the number of bits */ + if (node->weight > 0) + { + /* determine the number of bits for this node */ + for (struct node_t *curnode = node; curnode->parent != NULL; curnode = curnode->parent) + node->numbits++; + if (node->numbits == 0) + node->numbits = 1; + + /* keep track of the max */ + maxbits = MAX(maxbits, ((int)node->numbits)); + } + } + return maxbits; +} + +/*------------------------------------------------- + * assign_canonical_codes - assign canonical codes + * to all the nodes based on the number of bits + * in each + *------------------------------------------------- + */ + +enum huffman_error huffman_assign_canonical_codes(struct huffman_decoder* decoder) +{ + /* build up a histogram of bit lengths */ + uint32_t bithisto[33] = { 0 }; + for (int curcode = 0; curcode < decoder->numcodes; curcode++) + { + struct node_t* node = &decoder->huffnode[curcode]; + if (node->numbits > decoder->maxbits) + return HUFFERR_INTERNAL_INCONSISTENCY; + if (node->numbits <= 32) + bithisto[node->numbits]++; + } + + /* for each code length, determine the starting code number */ + uint32_t curstart = 0; + for (int codelen = 32; codelen > 0; codelen--) + { + uint32_t nextstart = (curstart + bithisto[codelen]) >> 1; + if (codelen != 1 && nextstart * 2 != (curstart + bithisto[codelen])) + return HUFFERR_INTERNAL_INCONSISTENCY; + bithisto[codelen] = curstart; + curstart = nextstart; + } + + /* now assign canonical codes */ + for (int curcode = 0; curcode < decoder->numcodes; curcode++) + { + struct node_t* node = &decoder->huffnode[curcode]; + if (node->numbits > 0) + node->bits = bithisto[node->numbits]++; + } + return HUFFERR_NONE; +} + +/*------------------------------------------------- + * build_lookup_table - build a lookup table for + * fast decoding + *------------------------------------------------- + */ + +void huffman_build_lookup_table(struct huffman_decoder* decoder) +{ + /* iterate over all codes */ + for (int curcode = 0; curcode < decoder->numcodes; curcode++) + { + /* process all nodes which have non-zero bits */ + struct node_t* node = &decoder->huffnode[curcode]; + if (node->numbits > 0) + { + /* set up the entry */ + lookup_value value = MAKE_LOOKUP(curcode, node->numbits); + + /* fill all matching entries */ + int shift = decoder->maxbits - node->numbits; + lookup_value *dest = &decoder->lookup[node->bits << shift]; + lookup_value *destend = &decoder->lookup[((node->bits + 1) << shift) - 1]; + while (dest <= destend) + *dest++ = value; + } + } +} diff --git a/core/deps/chdr/huffman.h b/core/deps/chdr/huffman.h new file mode 100644 index 000000000..8bcc45acd --- /dev/null +++ b/core/deps/chdr/huffman.h @@ -0,0 +1,89 @@ +/* license:BSD-3-Clause + * copyright-holders:Aaron Giles + *************************************************************************** + + huffman.h + + Static Huffman compression and decompression helpers. + +***************************************************************************/ + +#pragma once + +#ifndef __HUFFMAN_H__ +#define __HUFFMAN_H__ + +#include "bitstream.h" + + +/*************************************************************************** + * CONSTANTS + *************************************************************************** + */ + +enum huffman_error +{ + HUFFERR_NONE = 0, + HUFFERR_TOO_MANY_BITS, + HUFFERR_INVALID_DATA, + HUFFERR_INPUT_BUFFER_TOO_SMALL, + HUFFERR_OUTPUT_BUFFER_TOO_SMALL, + HUFFERR_INTERNAL_INCONSISTENCY, + HUFFERR_TOO_MANY_CONTEXTS +}; + +/*************************************************************************** + * TYPE DEFINITIONS + *************************************************************************** + */ + +typedef uint16_t lookup_value; + +/* a node in the huffman tree */ +struct node_t +{ + struct node_t* parent; /* pointer to parent node */ + uint32_t count; /* number of hits on this node */ + uint32_t weight; /* assigned weight of this node */ + uint32_t bits; /* bits used to encode the node */ + uint8_t numbits; /* number of bits needed for this node */ +}; + +/* ======================> huffman_context_base */ + +/* context class for decoding */ +struct huffman_decoder +{ + /* internal state */ + uint32_t numcodes; /* number of total codes being processed */ + uint8_t maxbits; /* maximum bits per code */ + uint8_t prevdata; /* value of the previous data (for delta-RLE encoding) */ + int rleremaining; /* number of RLE bytes remaining (for delta-RLE encoding) */ + lookup_value * lookup; /* pointer to the lookup table */ + struct node_t * huffnode; /* array of nodes */ + uint32_t * datahisto; /* histogram of data values */ + + /* array versions of the info we need */ +#if 0 + node_t* huffnode_array; /* [_NumCodes]; */ + lookup_value* lookup_array; /* [1 << _MaxBits]; */ +#endif +}; + +/* ======================> huffman_decoder */ + +struct huffman_decoder* create_huffman_decoder(int numcodes, int maxbits); + +/* single item operations */ +uint32_t huffman_decode_one(struct huffman_decoder* decoder, struct bitstream* bitbuf); + +enum huffman_error huffman_import_tree_rle(struct huffman_decoder* decoder, struct bitstream* bitbuf); +enum huffman_error huffman_import_tree_huffman(struct huffman_decoder* decoder, struct bitstream* bitbuf); + +int huffman_build_tree(struct huffman_decoder* decoder, uint32_t totaldata, uint32_t totalweight); +enum huffman_error huffman_assign_canonical_codes(struct huffman_decoder* decoder); +enum huffman_error huffman_compute_tree_from_histo(struct huffman_decoder* decoder); + +void huffman_build_lookup_table(struct huffman_decoder* decoder); + +#endif From 7cb77a4c21930ec08c5d414d7c35318700e4321e Mon Sep 17 00:00:00 2001 From: "Christoph \"baka0815\" Schwerdtfeger" Date: Sat, 22 Sep 2018 15:18:10 +0200 Subject: [PATCH 011/319] CHD: Wrap FLAC and LZMA with defines Make CHDv5 generally work without FLAC and LZMA decoding possibilities so there is currently no need for extra deps. --- core/deps/chdr/chd.c | 59 ++++++++++++++++++++++++++++++++++++-------- 1 file changed, 49 insertions(+), 10 deletions(-) diff --git a/core/deps/chdr/chd.c b/core/deps/chdr/chd.c index d853bb519..cbb4616fc 100644 --- a/core/deps/chdr/chd.c +++ b/core/deps/chdr/chd.c @@ -44,12 +44,16 @@ #include #include "chd.h" #include "cdrom.h" -#include "flac.h" +#if defined(CHD5_FLAC) + #include "flac.h" +#endif // CHD5_FLAC #include "huffman.h" -#include "LzmaEnc.h" -#include "LzmaDec.h" -#include "md5.h" -#include "sha1.h" +#if defined(CHD5_LZMA) + #include "LzmaEnc.h" + #include "LzmaDec.h" +#endif // CHD5_LZMA +#include "crypto/md5.h" +#include "crypto/sha1.h" #include "zlib.h" #define TRUE 1 @@ -207,6 +211,7 @@ struct _zlib_codec_data }; /* codec-private data for the LZMA codec */ +#if defined(CHD5_LZMA) #define MAX_LZMA_ALLOCS 64 typedef struct _lzma_allocator lzma_allocator; @@ -224,6 +229,7 @@ struct _lzma_codec_data CLzmaDec decoder; lzma_allocator allocator; }; +#endif // CHD5_LZMA /* codec-private data for the CDZL codec */ typedef struct _cdzl_codec_data cdzl_codec_data; @@ -234,6 +240,7 @@ struct _cdzl_codec_data { uint8_t* buffer; }; +#if defined(CHD5_LZMA) /* codec-private data for the CDLZ codec */ typedef struct _cdlz_codec_data cdlz_codec_data; struct _cdlz_codec_data { @@ -242,7 +249,9 @@ struct _cdlz_codec_data { zlib_codec_data subcode_decompressor; uint8_t* buffer; }; +#endif // CHD5_LZMA +#if defined(CHD5_FLAC) /* codec-private data for the CDFL codec */ typedef struct _cdfl_codec_data cdfl_codec_data; struct _cdfl_codec_data { @@ -253,6 +262,7 @@ struct _cdfl_codec_data { zlib_allocator allocator; uint8_t* buffer; }; +#endif // CHD5_FLAC /* internal representation of an open CHD file */ struct _chd_file @@ -278,8 +288,12 @@ struct _chd_file zlib_codec_data zlib_codec_data; /* zlib codec data */ cdzl_codec_data cdzl_codec_data; /* cdzl codec data */ +#if defined CHD5_LZMA cdlz_codec_data cdlz_codec_data; /* cdlz codec data */ +#endif // CHD5_LZMA +#if defined(CHD5_FLAC) cdfl_codec_data cdfl_codec_data; /* cdfl codec data */ +#endif // CHD5_FLAC crcmap_entry * crcmap; /* CRC map entries */ crcmap_entry * crcfree; /* free list CRC entries */ @@ -288,13 +302,13 @@ struct _chd_file UINT32 maxhunk; /* maximum hunk accessed */ UINT8 compressing; /* are we compressing? */ - MD5_CTX compmd5; /* running MD5 during compression */ - SHA1_CTX compsha1; /* running SHA1 during compression */ + struct MD5Context compmd5; /* running MD5 during compression */ + struct sha1_ctx compsha1; /* running SHA1 during compression */ UINT32 comphunk; /* next hunk we will compress */ UINT8 verifying; /* are we verifying? */ - MD5_CTX vermd5; /* running MD5 during verification */ - SHA1_CTX versha1; /* running SHA1 during verification */ + struct MD5Context vermd5; /* running MD5 during verification */ + struct sha1_ctx versha1; /* running SHA1 during verification */ UINT32 verhunk; /* next hunk we will verify */ UINT32 async_hunknum; /* hunk index for asynchronous operations */ @@ -341,26 +355,33 @@ static chd_error zlib_codec_decompress(void *codec, const uint8_t *src, uint32_t static voidpf zlib_fast_alloc(voidpf opaque, uInt items, uInt size); static void zlib_fast_free(voidpf opaque, voidpf address); +#if defined(CHD5_LZMA) /* lzma compression codec */ static chd_error lzma_codec_init(void *codec, uint32_t hunkbytes); static void lzma_codec_free(void *codec); static chd_error lzma_codec_decompress(void *codec, const uint8_t *src, uint32_t complen, uint8_t *dest, uint32_t destlen); +#endif // CHD5_LZMA /* cdzl compression codec */ static chd_error cdzl_codec_init(void* codec, uint32_t hunkbytes); static void cdzl_codec_free(void* codec); static chd_error cdzl_codec_decompress(void *codec, const uint8_t *src, uint32_t complen, uint8_t *dest, uint32_t destlen); +#if defined(CHD5_LZMA) /* cdlz compression codec */ static chd_error cdlz_codec_init(void* codec, uint32_t hunkbytes); static void cdlz_codec_free(void* codec); static chd_error cdlz_codec_decompress(void *codec, const uint8_t *src, uint32_t complen, uint8_t *dest, uint32_t destlen); +#endif // CHD5_LZMA +#if defined(CHD5_FLAC) /* cdfl compression codec */ static chd_error cdfl_codec_init(void* codec, uint32_t hunkbytes); static void cdfl_codec_free(void* codec); static chd_error cdfl_codec_decompress(void *codec, const uint8_t *src, uint32_t complen, uint8_t *dest, uint32_t destlen); +#endif // CHD5_FLAC +#if defined(CHD5_LZMA) /*************************************************************************** * LZMA ALLOCATOR HELPER *************************************************************************** @@ -621,6 +642,7 @@ chd_error cdlz_codec_decompress(void *codec, const uint8_t *src, uint32_t comple } return CHDERR_NONE; } +#endif // CHD5_LZMA /* cdzl */ @@ -681,6 +703,7 @@ chd_error cdzl_codec_decompress(void *codec, const uint8_t *src, uint32_t comple return CHDERR_NONE; } +#if defined(CHD5_FLAC) /*************************************************************************** * CD FLAC DECOMPRESSOR *************************************************************************** @@ -785,6 +808,7 @@ chd_error cdfl_codec_decompress(void *codec, const uint8_t *src, uint32_t comple return CHDERR_NONE; } +#endif // CHD5_FLAC /*************************************************************************** CODEC INTERFACES ***************************************************************************/ @@ -846,6 +870,7 @@ static const codec_interface codec_interfaces[] = NULL }, +#if defined(CHD5_LZMA) /* V5 CD lzma compression */ { CHD_CODEC_CD_LZMA, @@ -856,7 +881,9 @@ static const codec_interface codec_interfaces[] = cdlz_codec_decompress, NULL }, +#endif // CHD5_LZMA +#if defined(CHD5_FLAC) /* V5 CD flac compression */ { CHD_CODEC_CD_FLAC, @@ -867,6 +894,7 @@ static const codec_interface codec_interfaces[] = cdfl_codec_decompress, NULL }, +#endif // CHD5_FLAC }; /*************************************************************************** @@ -1385,13 +1413,17 @@ chd_error chd_open_file(core_file *file, int mode, chd_file *parent, chd_file ** codec = &newchd->cdzl_codec_data; break; +#if defined(CHD5_LZMA) case CHD_CODEC_CD_LZMA: codec = &newchd->cdlz_codec_data; +#endif // CHD5_LZMA break; +#if defined(CHD5_FLAC) case CHD_CODEC_CD_FLAC: codec = &newchd->cdfl_codec_data; break; +#endif // CHD5_FLAC } if (codec == NULL) @@ -1486,10 +1518,11 @@ void chd_close(chd_file *chd) switch (chd->codecintf[i]->compression) { +#if defined(CHD5_LZMA) case CHD_CODEC_CD_LZMA: codec = &chd->cdlz_codec_data; break; - +#endif // CHD5_LZMA case CHD_CODEC_ZLIB: codec = &chd->zlib_codec_data; break; @@ -1498,9 +1531,11 @@ void chd_close(chd_file *chd) codec = &chd->cdzl_codec_data; break; +#if defined(CHD5_FLAC) case CHD_CODEC_CD_FLAC: codec = &chd->cdfl_codec_data; break; +#endif // CHD5_FLAC } if (codec) @@ -2098,9 +2133,11 @@ static chd_error hunk_read_into_memory(chd_file *chd, UINT32 hunknum, UINT8 *des core_fread(chd->file, chd->compressed, blocklen); switch (chd->codecintf[rawmap[0]]->compression) { +#if defined(CHD5_LZMA) case CHD_CODEC_CD_LZMA: codec = &chd->cdlz_codec_data; break; +#endif // CHD5_LZMA case CHD_CODEC_ZLIB: codec = &chd->zlib_codec_data; @@ -2110,9 +2147,11 @@ static chd_error hunk_read_into_memory(chd_file *chd, UINT32 hunknum, UINT8 *des codec = &chd->cdzl_codec_data; break; +#if defined(CHD5_FLAC) case CHD_CODEC_CD_FLAC: codec = &chd->cdfl_codec_data; break; +#endif // CHD5_FLAC } if (codec==NULL) return CHDERR_DECOMPRESSION_ERROR; From 14260594ea8fbbb2f3dd5d31d2f818a31d4e6d97 Mon Sep 17 00:00:00 2001 From: "Christoph \"baka0815\" Schwerdtfeger" Date: Sat, 22 Sep 2018 15:19:57 +0200 Subject: [PATCH 012/319] CHD: read additional track types Also don't print "Unable to find metadata" as it's mostly not true (we reach this part after the last track) --- core/imgread/chd.cpp | 55 +++++++++++++++++++++++++++----------------- 1 file changed, 34 insertions(+), 21 deletions(-) diff --git a/core/imgread/chd.cpp b/core/imgread/chd.cpp index df9bd45c9..9b4f65dc2 100644 --- a/core/imgread/chd.cpp +++ b/core/imgread/chd.cpp @@ -10,7 +10,7 @@ struct CHDDisc : Disc u32 hunkbytes; u32 sph; - + CHDDisc() { chd=0; @@ -19,8 +19,8 @@ struct CHDDisc : Disc bool TryOpen(const wchar* file); - ~CHDDisc() - { + ~CHDDisc() + { if (hunk_mem) delete [] hunk_mem; if (chd) @@ -35,9 +35,9 @@ struct CHDTrack : TrackFile u32 StartHunk; u32 fmt; - CHDTrack(CHDDisc* disc, u32 StartFAD,u32 StartHunk, u32 fmt) - { - this->disc=disc; + CHDTrack(CHDDisc* disc, u32 StartFAD,u32 StartHunk, u32 fmt) + { + this->disc=disc; this->StartFAD=StartFAD; this->StartHunk=StartHunk; this->fmt=fmt; @@ -55,9 +55,9 @@ struct CHDTrack : TrackFile u32 hunk_ofs=fad_offs%disc->sph; memcpy(dst,disc->hunk_mem+hunk_ofs*(2352+96),fmt); - + *sector_type=fmt==2352?SECFMT_2352:SECFMT_2048_MODE1; - + //While space is reserved for it, the images contain no actual subcodes //memcpy(subcode,disc->hunk_mem+hunk_ofs*(2352+96)+2352,96); *subcode_type=SUBFMT_NONE; @@ -81,12 +81,12 @@ bool CHDDisc::TryOpen(const wchar* file) sph = hunkbytes/(2352+96); - if (hunkbytes%(2352+96)!=0) + if (hunkbytes%(2352+96)!=0) { printf("chd: hunkbytes is invalid, %d\n",hunkbytes); return false; } - + u32 tag; u8 flags; char temp[512]; @@ -98,23 +98,36 @@ bool CHDDisc::TryOpen(const wchar* file) for(;;) { - char type[64],subtype[32]="NONE",pgtype[32],pgsub[32]; - int tkid,frames,pregap=0,postgap=0; - err=chd_get_metadata(chd,CDROM_TRACK_METADATA2_TAG,tracks.size(),temp,sizeof(temp),&temp_len,&tag,&flags); - if (err==CHDERR_NONE) + char type[16], subtype[16], pgtype[16], pgsub[16]; + int tkid=-1, frames=0, pregap=0, postgap=0, padframes=0; + + err = chd_get_metadata(chd, CDROM_TRACK_METADATA2_TAG, tracks.size(), temp, sizeof(temp), &temp_len, &tag, &flags); + if (err == CHDERR_NONE) { //"TRACK:%d TYPE:%s SUBTYPE:%s FRAMES:%d PREGAP:%d PGTYPE:%s PGSUB:%s POSTGAP:%d" - sscanf(temp,CDROM_TRACK_METADATA2_FORMAT,&tkid,type,subtype,&frames,&pregap,pgtype,pgsub,&postgap); + sscanf(temp, CDROM_TRACK_METADATA2_FORMAT, &tkid, type, subtype, &frames, &pregap, pgtype, pgsub, &postgap); } - else if (CHDERR_NONE== (err=chd_get_metadata(chd,CDROM_TRACK_METADATA_TAG,tracks.size(),temp,sizeof(temp),&temp_len,&tag,&flags)) ) + else if (CHDERR_NONE== (err = chd_get_metadata(chd, CDROM_TRACK_METADATA_TAG, tracks.size(), temp, sizeof(temp), &temp_len, &tag, &flags)) ) { //CDROM_TRACK_METADATA_FORMAT "TRACK:%d TYPE:%s SUBTYPE:%s FRAMES:%d" - sscanf(temp,CDROM_TRACK_METADATA_FORMAT,&tkid,type,subtype,&frames); + sscanf(temp, CDROM_TRACK_METADATA_FORMAT, &tkid, type, subtype, &frames); } else { - printf("chd: Unable to find metadata, %d\n",err); - break; + err = chd_get_metadata(chd, GDROM_OLD_METADATA_TAG, tracks.size(), temp, sizeof(temp), &temp_len, &tag, &flags); + if (err != CHDERR_NONE) + { + err = chd_get_metadata(chd, GDROM_TRACK_METADATA_TAG, tracks.size(), temp, sizeof(temp), &temp_len, &tag, &flags); + } + if (err == CHDERR_NONE) + { + //GDROM_TRACK_METADATA_FORMAT "TRACK:%d TYPE:%s SUBTYPE:%s FRAMES:%d PAD:%d PREGAP:%d PGTYPE:%s PGSUB:%s POSTGAP:%d" + sscanf(temp, GDROM_TRACK_METADATA_FORMAT, &tkid, type, subtype, &frames, &padframes, &pregap, pgtype, pgsub, &postgap); + } + else + { + break; + } } if (tkid!=(tracks.size()+1) || (strcmp(type,"MODE1_RAW")!=0 && strcmp(type,"AUDIO")!=0 && strcmp(type,"MODE1")!=0) || strcmp(subtype,"NONE")!=0 || pregap!=0 || postgap!=0) @@ -156,7 +169,7 @@ bool CHDDisc::TryOpen(const wchar* file) Disc* chd_parse(const wchar* file) { CHDDisc* rv = new CHDDisc(); - + if (rv->TryOpen(file)) return rv; else @@ -164,4 +177,4 @@ Disc* chd_parse(const wchar* file) delete rv; return 0; } -} \ No newline at end of file +} From 1d7ecda2af97dcd4a1d2140bdf4b78947fc51a49 Mon Sep 17 00:00:00 2001 From: "Christoph \"baka0815\" Schwerdtfeger" Date: Sat, 22 Sep 2018 15:27:08 +0200 Subject: [PATCH 013/319] CHD: Check for extra frames If the frame count is not dividable by 4, there are extra frames added to keep a padding. This is true for F355 (450 frames -> 2 extra frames) and others. Also move CD_TRACK_PADDING to chd.cpp to work around problems with including from C and C++. --- core/deps/chdr/cdrom.h | 3 --- core/imgread/chd.cpp | 28 +++++++++++++++++----------- 2 files changed, 17 insertions(+), 14 deletions(-) diff --git a/core/deps/chdr/cdrom.h b/core/deps/chdr/cdrom.h index 65aa18218..60c819262 100644 --- a/core/deps/chdr/cdrom.h +++ b/core/deps/chdr/cdrom.h @@ -20,9 +20,6 @@ CONSTANTS ***************************************************************************/ -/* tracks are padded to a multiple of this many frames */ -extern const uint32_t CD_TRACK_PADDING; - #define CD_MAX_TRACKS (99) /* AFAIK the theoretical limit */ #define CD_MAX_SECTOR_DATA (2352) #define CD_MAX_SUBCODE_DATA (96) diff --git a/core/imgread/chd.cpp b/core/imgread/chd.cpp index 9b4f65dc2..8ac1b1302 100644 --- a/core/imgread/chd.cpp +++ b/core/imgread/chd.cpp @@ -2,6 +2,9 @@ #include "deps/chdr/chd.h" +/* tracks are padded to a multiple of this many frames */ +const uint32_t CD_TRACK_PADDING = 4; + struct CHDDisc : Disc { chd_file* chd; @@ -91,10 +94,11 @@ bool CHDDisc::TryOpen(const wchar* file) u8 flags; char temp[512]; u32 temp_len; - u32 total_frames=150; + u32 total_frames = 150; - u32 total_secs=0; - u32 total_hunks=0; + u32 total_secs = 0; + u32 total_hunks = 0; + int extraframes = 0; for(;;) { @@ -137,15 +141,17 @@ bool CHDDisc::TryOpen(const wchar* file) } printf("%s\n",temp); Track t; - t.StartFAD=total_frames; - total_frames+=frames; - t.EndFAD=total_frames-1; - t.ADDR=0; - t.CTRL=strcmp(type,"AUDIO")==0?0:4; - t.file = new CHDTrack(this,t.StartFAD,total_hunks,strcmp(type,"MODE1")?2352:2048); + t.StartFAD = total_frames + extraframes; + int padded = (frames + CD_TRACK_PADDING - 1) / CD_TRACK_PADDING; + extraframes += (padded * CD_TRACK_PADDING) - frames; + total_frames += frames; + t.EndFAD = total_frames - 1 + extraframes; + t.ADDR = 0; + t.CTRL = strcmp(type,"AUDIO") == 0 ? 0 : 4; + t.file = new CHDTrack(this, t.StartFAD, total_hunks, strcmp(type,"MODE1") ? 2352 : 2048); - total_hunks+=frames/sph; - if (frames%sph) + total_hunks += frames / sph; + if (frames % sph) total_hunks++; tracks.push_back(t); From b4155b0c23c836580d8181daade08f21357e03f1 Mon Sep 17 00:00:00 2001 From: "Christoph \"baka0815\" Schwerdtfeger" Date: Sat, 22 Sep 2018 16:36:43 +0200 Subject: [PATCH 014/319] CHD: Enable support for LZMA (CDLZ) compression --- core/core.mk | 14 +- core/deps/chdr/chd.c | 4 +- core/deps/lzma/7zTypes.h | 256 ++ core/deps/lzma/Alloc.c | 136 + core/deps/lzma/Alloc.h | 37 + core/deps/lzma/Bra.c | 135 + core/deps/lzma/Bra.h | 64 + core/deps/lzma/Bra86.c | 82 + core/deps/lzma/BraIA64.c | 69 + core/deps/lzma/Compiler.h | 32 + core/deps/lzma/CpuArch.c | 200 ++ core/deps/lzma/CpuArch.h | 223 ++ core/deps/lzma/Delta.c | 64 + core/deps/lzma/Delta.h | 19 + core/deps/lzma/LzFind.c | 1044 ++++++++ core/deps/lzma/LzFind.h | 117 + core/deps/lzma/LzHash.h | 57 + core/deps/lzma/Lzma86.h | 111 + core/deps/lzma/Lzma86Dec.c | 54 + core/deps/lzma/Lzma86Enc.c | 106 + core/deps/lzma/LzmaDec.c | 1100 ++++++++ core/deps/lzma/LzmaDec.h | 227 ++ core/deps/lzma/LzmaEnc.c | 2351 +++++++++++++++++ core/deps/lzma/LzmaEnc.h | 78 + core/deps/lzma/LzmaLib.c | 40 + core/deps/lzma/LzmaLib.h | 131 + core/deps/lzma/Precomp.h | 10 + core/deps/lzma/Sort.c | 141 + core/deps/lzma/Sort.h | 18 + .../reicast/src/main/jni/Android.mk | 6 + shell/linux/Makefile | 7 + shell/reicast.vcxproj | 47 +- shell/reicast.vcxproj.filters | 72 +- 33 files changed, 7029 insertions(+), 23 deletions(-) create mode 100644 core/deps/lzma/7zTypes.h create mode 100644 core/deps/lzma/Alloc.c create mode 100644 core/deps/lzma/Alloc.h create mode 100644 core/deps/lzma/Bra.c create mode 100644 core/deps/lzma/Bra.h create mode 100644 core/deps/lzma/Bra86.c create mode 100644 core/deps/lzma/BraIA64.c create mode 100644 core/deps/lzma/Compiler.h create mode 100644 core/deps/lzma/CpuArch.c create mode 100644 core/deps/lzma/CpuArch.h create mode 100644 core/deps/lzma/Delta.c create mode 100644 core/deps/lzma/Delta.h create mode 100644 core/deps/lzma/LzFind.c create mode 100644 core/deps/lzma/LzFind.h create mode 100644 core/deps/lzma/LzHash.h create mode 100644 core/deps/lzma/Lzma86.h create mode 100644 core/deps/lzma/Lzma86Dec.c create mode 100644 core/deps/lzma/Lzma86Enc.c create mode 100644 core/deps/lzma/LzmaDec.c create mode 100644 core/deps/lzma/LzmaDec.h create mode 100644 core/deps/lzma/LzmaEnc.c create mode 100644 core/deps/lzma/LzmaEnc.h create mode 100644 core/deps/lzma/LzmaLib.c create mode 100644 core/deps/lzma/LzmaLib.h create mode 100644 core/deps/lzma/Precomp.h create mode 100644 core/deps/lzma/Sort.c create mode 100644 core/deps/lzma/Sort.h diff --git a/core/core.mk b/core/core.mk index b7e3835f5..7927a5654 100755 --- a/core/core.mk +++ b/core/core.mk @@ -1,15 +1,19 @@ -#LOCAL_PATH:= +#LOCAL_PATH:= #MFLAGS := -marm -march=armv7-a -mtune=cortex-a8 -mfpu=vfpv3-d16 -mfloat-abi=softfp #ASFLAGS := -march=armv7-a -mfpu=vfp-d16 -mfloat-abi=softfp -#LDFLAGS := -Wl,-Map,$(notdir $@).map,--gc-sections -Wl,-O3 -Wl,--sort-common +#LDFLAGS := -Wl,-Map,$(notdir $@).map,--gc-sections -Wl,-O3 -Wl,--sort-common RZDCY_SRC_DIR ?= $(call my-dir) RZDCY_MODULES := cfg/ hw/arm7/ hw/aica/ hw/holly/ hw/ hw/gdrom/ hw/maple/ \ hw/mem/ hw/pvr/ hw/sh4/ hw/sh4/interpr/ hw/sh4/modules/ plugins/ profiler/ oslib/ \ hw/extdev/ hw/arm/ hw/naomi/ imgread/ linux/ ./ deps/coreio/ deps/zlib/ deps/chdr/ deps/crypto/ \ - deps/libelf/ deps/chdpsr/ arm_emitter/ rend/ reios/ deps/libpng/ + deps/libelf/ deps/chdpsr/ arm_emitter/ rend/ reios/ deps/libpng/ + + ifdef CHD5_LZMA + RZDCY_MODULES += deps/lzma/ + endif ifdef WEBUI @@ -70,7 +74,7 @@ endif RZDCY_FILES := $(foreach dir,$(addprefix $(RZDCY_SRC_DIR)/,$(RZDCY_MODULES)),$(wildcard $(dir)*.cpp)) RZDCY_FILES += $(foreach dir,$(addprefix $(RZDCY_SRC_DIR)/,$(RZDCY_MODULES)),$(wildcard $(dir)*.c)) RZDCY_FILES += $(foreach dir,$(addprefix $(RZDCY_SRC_DIR)/,$(RZDCY_MODULES)),$(wildcard $(dir)*.S)) - + ifdef FOR_PANDORA RZDCY_CFLAGS := \ $(CFLAGS) -c -O3 -I$(RZDCY_SRC_DIR) -I$(RZDCY_SRC_DIR)/deps \ @@ -86,7 +90,7 @@ RZDCY_CFLAGS := \ -D_ANDROID -DRELEASE\ -frename-registers -fsingle-precision-constant -ffast-math \ -ftree-vectorize -fomit-frame-pointer - + ifndef NOT_ARM RZDCY_CFLAGS += -march=armv7-a -mtune=cortex-a9 -mfpu=vfpv3-d16 RZDCY_CFLAGS += -DTARGET_LINUX_ARMELv7 diff --git a/core/deps/chdr/chd.c b/core/deps/chdr/chd.c index cbb4616fc..d15bf6aee 100644 --- a/core/deps/chdr/chd.c +++ b/core/deps/chdr/chd.c @@ -49,8 +49,8 @@ #endif // CHD5_FLAC #include "huffman.h" #if defined(CHD5_LZMA) - #include "LzmaEnc.h" - #include "LzmaDec.h" + #include "deps/lzma/LzmaEnc.h" + #include "deps/lzma/LzmaDec.h" #endif // CHD5_LZMA #include "crypto/md5.h" #include "crypto/sha1.h" diff --git a/core/deps/lzma/7zTypes.h b/core/deps/lzma/7zTypes.h new file mode 100644 index 000000000..903047b10 --- /dev/null +++ b/core/deps/lzma/7zTypes.h @@ -0,0 +1,256 @@ +/* 7zTypes.h -- Basic types +2013-11-12 : Igor Pavlov : Public domain */ + +#ifndef __7Z_TYPES_H +#define __7Z_TYPES_H + +#ifdef _WIN32 +/* #include */ +#endif + +#include + +#ifndef EXTERN_C_BEGIN +#ifdef __cplusplus +#define EXTERN_C_BEGIN extern "C" { +#define EXTERN_C_END } +#else +#define EXTERN_C_BEGIN +#define EXTERN_C_END +#endif +#endif + +EXTERN_C_BEGIN + +#define SZ_OK 0 + +#define SZ_ERROR_DATA 1 +#define SZ_ERROR_MEM 2 +#define SZ_ERROR_CRC 3 +#define SZ_ERROR_UNSUPPORTED 4 +#define SZ_ERROR_PARAM 5 +#define SZ_ERROR_INPUT_EOF 6 +#define SZ_ERROR_OUTPUT_EOF 7 +#define SZ_ERROR_READ 8 +#define SZ_ERROR_WRITE 9 +#define SZ_ERROR_PROGRESS 10 +#define SZ_ERROR_FAIL 11 +#define SZ_ERROR_THREAD 12 + +#define SZ_ERROR_ARCHIVE 16 +#define SZ_ERROR_NO_ARCHIVE 17 + +typedef int SRes; + +#ifdef _WIN32 +/* typedef DWORD WRes; */ +typedef unsigned WRes; +#else +typedef int WRes; +#endif + +#ifndef RINOK +#define RINOK(x) { int __result__ = (x); if (__result__ != 0) return __result__; } +#endif + +typedef unsigned char Byte; +typedef short Int16; +typedef unsigned short UInt16; + +#ifdef _LZMA_UINT32_IS_ULONG +typedef long Int32; +typedef unsigned long UInt32; +#else +typedef int Int32; +typedef unsigned int UInt32; +#endif + +#ifdef _SZ_NO_INT_64 + +/* define _SZ_NO_INT_64, if your compiler doesn't support 64-bit integers. + NOTES: Some code will work incorrectly in that case! */ + +typedef long Int64; +typedef unsigned long UInt64; + +#else + +#if defined(_MSC_VER) || defined(__BORLANDC__) +typedef __int64 Int64; +typedef unsigned __int64 UInt64; +#define UINT64_CONST(n) n +#else +typedef long long int Int64; +typedef unsigned long long int UInt64; +#define UINT64_CONST(n) n ## ULL +#endif + +#endif + +#ifdef _LZMA_NO_SYSTEM_SIZE_T +typedef UInt32 SizeT; +#else +typedef size_t SizeT; +#endif + +typedef int Bool; +#define True 1 +#define False 0 + + +#ifdef _WIN32 +#define MY_STD_CALL __stdcall +#else +#define MY_STD_CALL +#endif + +#ifdef _MSC_VER + +#if _MSC_VER >= 1300 +#define MY_NO_INLINE __declspec(noinline) +#else +#define MY_NO_INLINE +#endif + +#define MY_CDECL __cdecl +#define MY_FAST_CALL __fastcall + +#else + +#define MY_NO_INLINE +#define MY_CDECL +#define MY_FAST_CALL + +#endif + + +/* The following interfaces use first parameter as pointer to structure */ + +typedef struct +{ + Byte (*Read)(void *p); /* reads one byte, returns 0 in case of EOF or error */ +} IByteIn; + +typedef struct +{ + void (*Write)(void *p, Byte b); +} IByteOut; + +typedef struct +{ + SRes (*Read)(void *p, void *buf, size_t *size); + /* if (input(*size) != 0 && output(*size) == 0) means end_of_stream. + (output(*size) < input(*size)) is allowed */ +} ISeqInStream; + +/* it can return SZ_ERROR_INPUT_EOF */ +SRes SeqInStream_Read(ISeqInStream *stream, void *buf, size_t size); +SRes SeqInStream_Read2(ISeqInStream *stream, void *buf, size_t size, SRes errorType); +SRes SeqInStream_ReadByte(ISeqInStream *stream, Byte *buf); + +typedef struct +{ + size_t (*Write)(void *p, const void *buf, size_t size); + /* Returns: result - the number of actually written bytes. + (result < size) means error */ +} ISeqOutStream; + +typedef enum +{ + SZ_SEEK_SET = 0, + SZ_SEEK_CUR = 1, + SZ_SEEK_END = 2 +} ESzSeek; + +typedef struct +{ + SRes (*Read)(void *p, void *buf, size_t *size); /* same as ISeqInStream::Read */ + SRes (*Seek)(void *p, Int64 *pos, ESzSeek origin); +} ISeekInStream; + +typedef struct +{ + SRes (*Look)(void *p, const void **buf, size_t *size); + /* if (input(*size) != 0 && output(*size) == 0) means end_of_stream. + (output(*size) > input(*size)) is not allowed + (output(*size) < input(*size)) is allowed */ + SRes (*Skip)(void *p, size_t offset); + /* offset must be <= output(*size) of Look */ + + SRes (*Read)(void *p, void *buf, size_t *size); + /* reads directly (without buffer). It's same as ISeqInStream::Read */ + SRes (*Seek)(void *p, Int64 *pos, ESzSeek origin); +} ILookInStream; + +SRes LookInStream_LookRead(ILookInStream *stream, void *buf, size_t *size); +SRes LookInStream_SeekTo(ILookInStream *stream, UInt64 offset); + +/* reads via ILookInStream::Read */ +SRes LookInStream_Read2(ILookInStream *stream, void *buf, size_t size, SRes errorType); +SRes LookInStream_Read(ILookInStream *stream, void *buf, size_t size); + +#define LookToRead_BUF_SIZE (1 << 14) + +typedef struct +{ + ILookInStream s; + ISeekInStream *realStream; + size_t pos; + size_t size; + Byte buf[LookToRead_BUF_SIZE]; +} CLookToRead; + +void LookToRead_CreateVTable(CLookToRead *p, int lookahead); +void LookToRead_Init(CLookToRead *p); + +typedef struct +{ + ISeqInStream s; + ILookInStream *realStream; +} CSecToLook; + +void SecToLook_CreateVTable(CSecToLook *p); + +typedef struct +{ + ISeqInStream s; + ILookInStream *realStream; +} CSecToRead; + +void SecToRead_CreateVTable(CSecToRead *p); + +typedef struct +{ + SRes (*Progress)(void *p, UInt64 inSize, UInt64 outSize); + /* Returns: result. (result != SZ_OK) means break. + Value (UInt64)(Int64)-1 for size means unknown value. */ +} ICompressProgress; + +typedef struct +{ + void *(*Alloc)(void *p, size_t size); + void (*Free)(void *p, void *address); /* address can be 0 */ +} ISzAlloc; + +#define IAlloc_Alloc(p, size) (p)->Alloc((p), size) +#define IAlloc_Free(p, a) (p)->Free((p), a) + +#ifdef _WIN32 + +#define CHAR_PATH_SEPARATOR '\\' +#define WCHAR_PATH_SEPARATOR L'\\' +#define STRING_PATH_SEPARATOR "\\" +#define WSTRING_PATH_SEPARATOR L"\\" + +#else + +#define CHAR_PATH_SEPARATOR '/' +#define WCHAR_PATH_SEPARATOR L'/' +#define STRING_PATH_SEPARATOR "/" +#define WSTRING_PATH_SEPARATOR L"/" + +#endif + +EXTERN_C_END + +#endif diff --git a/core/deps/lzma/Alloc.c b/core/deps/lzma/Alloc.c new file mode 100644 index 000000000..9f1d036af --- /dev/null +++ b/core/deps/lzma/Alloc.c @@ -0,0 +1,136 @@ +/* Alloc.c -- Memory allocation functions +2015-02-21 : Igor Pavlov : Public domain */ + +#include "Precomp.h" + +#ifdef _WIN32 +#include +#endif +#include + +#include "Alloc.h" + +/* #define _SZ_ALLOC_DEBUG */ + +/* use _SZ_ALLOC_DEBUG to debug alloc/free operations */ +#ifdef _SZ_ALLOC_DEBUG +#include +int g_allocCount = 0; +int g_allocCountMid = 0; +int g_allocCountBig = 0; +#endif + +void *MyAlloc(size_t size) +{ + if (size == 0) + return 0; + #ifdef _SZ_ALLOC_DEBUG + { + void *p = malloc(size); + fprintf(stderr, "\nAlloc %10d bytes, count = %10d, addr = %8X", size, g_allocCount++, (unsigned)p); + return p; + } + #else + return malloc(size); + #endif +} + +void MyFree(void *address) +{ + #ifdef _SZ_ALLOC_DEBUG + if (address != 0) + fprintf(stderr, "\nFree; count = %10d, addr = %8X", --g_allocCount, (unsigned)address); + #endif + free(address); +} + +#ifdef _WIN32 + +void *MidAlloc(size_t size) +{ + if (size == 0) + return 0; + #ifdef _SZ_ALLOC_DEBUG + fprintf(stderr, "\nAlloc_Mid %10d bytes; count = %10d", size, g_allocCountMid++); + #endif + return VirtualAlloc(0, size, MEM_COMMIT, PAGE_READWRITE); +} + +void MidFree(void *address) +{ + #ifdef _SZ_ALLOC_DEBUG + if (address != 0) + fprintf(stderr, "\nFree_Mid; count = %10d", --g_allocCountMid); + #endif + if (address == 0) + return; + VirtualFree(address, 0, MEM_RELEASE); +} + +#ifndef MEM_LARGE_PAGES +#undef _7ZIP_LARGE_PAGES +#endif + +#ifdef _7ZIP_LARGE_PAGES +SIZE_T g_LargePageSize = 0; +typedef SIZE_T (WINAPI *GetLargePageMinimumP)(); +#endif + +void SetLargePageSize() +{ + #ifdef _7ZIP_LARGE_PAGES + SIZE_T size = 0; + GetLargePageMinimumP largePageMinimum = (GetLargePageMinimumP) + GetProcAddress(GetModuleHandle(TEXT("kernel32.dll")), "GetLargePageMinimum"); + if (largePageMinimum == 0) + return; + size = largePageMinimum(); + if (size == 0 || (size & (size - 1)) != 0) + return; + g_LargePageSize = size; + #endif +} + + +void *BigAlloc(size_t size) +{ + if (size == 0) + return 0; + #ifdef _SZ_ALLOC_DEBUG + fprintf(stderr, "\nAlloc_Big %10d bytes; count = %10d", size, g_allocCountBig++); + #endif + + #ifdef _7ZIP_LARGE_PAGES + if (g_LargePageSize != 0 && g_LargePageSize <= (1 << 30) && size >= (1 << 18)) + { + void *res = VirtualAlloc(0, (size + g_LargePageSize - 1) & (~(g_LargePageSize - 1)), + MEM_COMMIT | MEM_LARGE_PAGES, PAGE_READWRITE); + if (res != 0) + return res; + } + #endif + return VirtualAlloc(0, size, MEM_COMMIT, PAGE_READWRITE); +} + +void BigFree(void *address) +{ + #ifdef _SZ_ALLOC_DEBUG + if (address != 0) + fprintf(stderr, "\nFree_Big; count = %10d", --g_allocCountBig); + #endif + + if (address == 0) + return; + VirtualFree(address, 0, MEM_RELEASE); +} + +#endif + + +static void *SzAlloc(void *p, size_t size) { UNUSED_VAR(p); return MyAlloc(size); } +static void SzFree(void *p, void *address) { UNUSED_VAR(p); MyFree(address); } +ISzAlloc g_Alloc = { SzAlloc, SzFree }; + +static void *SzBigAlloc(void *p, size_t size) { UNUSED_VAR(p); return BigAlloc(size); } +static void SzBigFree(void *p, void *address) { UNUSED_VAR(p); BigFree(address); } +ISzAlloc g_BigAlloc = { SzBigAlloc, SzBigFree }; diff --git a/core/deps/lzma/Alloc.h b/core/deps/lzma/Alloc.h new file mode 100644 index 000000000..73b282a07 --- /dev/null +++ b/core/deps/lzma/Alloc.h @@ -0,0 +1,37 @@ +/* Alloc.h -- Memory allocation functions +2015-02-21 : Igor Pavlov : Public domain */ + +#ifndef __COMMON_ALLOC_H +#define __COMMON_ALLOC_H + +#include "7zTypes.h" + +EXTERN_C_BEGIN + +void *MyAlloc(size_t size); +void MyFree(void *address); + +#ifdef _WIN32 + +void SetLargePageSize(); + +void *MidAlloc(size_t size); +void MidFree(void *address); +void *BigAlloc(size_t size); +void BigFree(void *address); + +#else + +#define MidAlloc(size) MyAlloc(size) +#define MidFree(address) MyFree(address) +#define BigAlloc(size) MyAlloc(size) +#define BigFree(address) MyFree(address) + +#endif + +extern ISzAlloc g_Alloc; +extern ISzAlloc g_BigAlloc; + +EXTERN_C_END + +#endif diff --git a/core/deps/lzma/Bra.c b/core/deps/lzma/Bra.c new file mode 100644 index 000000000..976810c96 --- /dev/null +++ b/core/deps/lzma/Bra.c @@ -0,0 +1,135 @@ +/* Bra.c -- Converters for RISC code +2010-04-16 : Igor Pavlov : Public domain */ + +#include "Precomp.h" + +#include "Bra.h" + +SizeT ARM_Convert(Byte *data, SizeT size, UInt32 ip, int encoding) +{ + SizeT i; + if (size < 4) + return 0; + size -= 4; + ip += 8; + for (i = 0; i <= size; i += 4) + { + if (data[i + 3] == 0xEB) + { + UInt32 dest; + UInt32 src = ((UInt32)data[i + 2] << 16) | ((UInt32)data[i + 1] << 8) | (data[i + 0]); + src <<= 2; + if (encoding) + dest = ip + (UInt32)i + src; + else + dest = src - (ip + (UInt32)i); + dest >>= 2; + data[i + 2] = (Byte)(dest >> 16); + data[i + 1] = (Byte)(dest >> 8); + data[i + 0] = (Byte)dest; + } + } + return i; +} + +SizeT ARMT_Convert(Byte *data, SizeT size, UInt32 ip, int encoding) +{ + SizeT i; + if (size < 4) + return 0; + size -= 4; + ip += 4; + for (i = 0; i <= size; i += 2) + { + if ((data[i + 1] & 0xF8) == 0xF0 && + (data[i + 3] & 0xF8) == 0xF8) + { + UInt32 dest; + UInt32 src = + (((UInt32)data[i + 1] & 0x7) << 19) | + ((UInt32)data[i + 0] << 11) | + (((UInt32)data[i + 3] & 0x7) << 8) | + (data[i + 2]); + + src <<= 1; + if (encoding) + dest = ip + (UInt32)i + src; + else + dest = src - (ip + (UInt32)i); + dest >>= 1; + + data[i + 1] = (Byte)(0xF0 | ((dest >> 19) & 0x7)); + data[i + 0] = (Byte)(dest >> 11); + data[i + 3] = (Byte)(0xF8 | ((dest >> 8) & 0x7)); + data[i + 2] = (Byte)dest; + i += 2; + } + } + return i; +} + +SizeT PPC_Convert(Byte *data, SizeT size, UInt32 ip, int encoding) +{ + SizeT i; + if (size < 4) + return 0; + size -= 4; + for (i = 0; i <= size; i += 4) + { + if ((data[i] >> 2) == 0x12 && (data[i + 3] & 3) == 1) + { + UInt32 src = ((UInt32)(data[i + 0] & 3) << 24) | + ((UInt32)data[i + 1] << 16) | + ((UInt32)data[i + 2] << 8) | + ((UInt32)data[i + 3] & (~3)); + + UInt32 dest; + if (encoding) + dest = ip + (UInt32)i + src; + else + dest = src - (ip + (UInt32)i); + data[i + 0] = (Byte)(0x48 | ((dest >> 24) & 0x3)); + data[i + 1] = (Byte)(dest >> 16); + data[i + 2] = (Byte)(dest >> 8); + data[i + 3] &= 0x3; + data[i + 3] |= dest; + } + } + return i; +} + +SizeT SPARC_Convert(Byte *data, SizeT size, UInt32 ip, int encoding) +{ + UInt32 i; + if (size < 4) + return 0; + size -= 4; + for (i = 0; i <= size; i += 4) + { + if ((data[i] == 0x40 && (data[i + 1] & 0xC0) == 0x00) || + (data[i] == 0x7F && (data[i + 1] & 0xC0) == 0xC0)) + { + UInt32 src = + ((UInt32)data[i + 0] << 24) | + ((UInt32)data[i + 1] << 16) | + ((UInt32)data[i + 2] << 8) | + ((UInt32)data[i + 3]); + UInt32 dest; + + src <<= 2; + if (encoding) + dest = ip + i + src; + else + dest = src - (ip + i); + dest >>= 2; + + dest = (((0 - ((dest >> 22) & 1)) << 22) & 0x3FFFFFFF) | (dest & 0x3FFFFF) | 0x40000000; + + data[i + 0] = (Byte)(dest >> 24); + data[i + 1] = (Byte)(dest >> 16); + data[i + 2] = (Byte)(dest >> 8); + data[i + 3] = (Byte)dest; + } + } + return i; +} diff --git a/core/deps/lzma/Bra.h b/core/deps/lzma/Bra.h new file mode 100644 index 000000000..aba8dce14 --- /dev/null +++ b/core/deps/lzma/Bra.h @@ -0,0 +1,64 @@ +/* Bra.h -- Branch converters for executables +2013-01-18 : Igor Pavlov : Public domain */ + +#ifndef __BRA_H +#define __BRA_H + +#include "7zTypes.h" + +EXTERN_C_BEGIN + +/* +These functions convert relative addresses to absolute addresses +in CALL instructions to increase the compression ratio. + + In: + data - data buffer + size - size of data + ip - current virtual Instruction Pinter (IP) value + state - state variable for x86 converter + encoding - 0 (for decoding), 1 (for encoding) + + Out: + state - state variable for x86 converter + + Returns: + The number of processed bytes. If you call these functions with multiple calls, + you must start next call with first byte after block of processed bytes. + + Type Endian Alignment LookAhead + + x86 little 1 4 + ARMT little 2 2 + ARM little 4 0 + PPC big 4 0 + SPARC big 4 0 + IA64 little 16 0 + + size must be >= Alignment + LookAhead, if it's not last block. + If (size < Alignment + LookAhead), converter returns 0. + + Example: + + UInt32 ip = 0; + for () + { + ; size must be >= Alignment + LookAhead, if it's not last block + SizeT processed = Convert(data, size, ip, 1); + data += processed; + size -= processed; + ip += processed; + } +*/ + +#define x86_Convert_Init(state) { state = 0; } +SizeT x86_Convert(Byte *data, SizeT size, UInt32 ip, UInt32 *state, int encoding); +SizeT ARM_Convert(Byte *data, SizeT size, UInt32 ip, int encoding); +SizeT ARMT_Convert(Byte *data, SizeT size, UInt32 ip, int encoding); +SizeT PPC_Convert(Byte *data, SizeT size, UInt32 ip, int encoding); +SizeT SPARC_Convert(Byte *data, SizeT size, UInt32 ip, int encoding); +SizeT IA64_Convert(Byte *data, SizeT size, UInt32 ip, int encoding); + +EXTERN_C_END + +#endif diff --git a/core/deps/lzma/Bra86.c b/core/deps/lzma/Bra86.c new file mode 100644 index 000000000..8dd3ed48d --- /dev/null +++ b/core/deps/lzma/Bra86.c @@ -0,0 +1,82 @@ +/* Bra86.c -- Converter for x86 code (BCJ) +2013-11-12 : Igor Pavlov : Public domain */ + +#include "Precomp.h" + +#include "Bra.h" + +#define Test86MSByte(b) ((((b) + 1) & 0xFE) == 0) + +SizeT x86_Convert(Byte *data, SizeT size, UInt32 ip, UInt32 *state, int encoding) +{ + SizeT pos = 0; + UInt32 mask = *state & 7; + if (size < 5) + return 0; + size -= 4; + ip += 5; + + for (;;) + { + Byte *p = data + pos; + const Byte *limit = data + size; + for (; p < limit; p++) + if ((*p & 0xFE) == 0xE8) + break; + + { + SizeT d = (SizeT)(p - data - pos); + pos = (SizeT)(p - data); + if (p >= limit) + { + *state = (d > 2 ? 0 : mask >> (unsigned)d); + return pos; + } + if (d > 2) + mask = 0; + else + { + mask >>= (unsigned)d; + if (mask != 0 && (mask > 4 || mask == 3 || Test86MSByte(p[(mask >> 1) + 1]))) + { + mask = (mask >> 1) | 4; + pos++; + continue; + } + } + } + + if (Test86MSByte(p[4])) + { + UInt32 v = ((UInt32)p[4] << 24) | ((UInt32)p[3] << 16) | ((UInt32)p[2] << 8) | ((UInt32)p[1]); + UInt32 cur = ip + (UInt32)pos; + pos += 5; + if (encoding) + v += cur; + else + v -= cur; + if (mask != 0) + { + unsigned sh = (mask & 6) << 2; + if (Test86MSByte((Byte)(v >> sh))) + { + v ^= (((UInt32)0x100 << sh) - 1); + if (encoding) + v += cur; + else + v -= cur; + } + mask = 0; + } + p[1] = (Byte)v; + p[2] = (Byte)(v >> 8); + p[3] = (Byte)(v >> 16); + p[4] = (Byte)(0 - ((v >> 24) & 1)); + } + else + { + mask = (mask >> 1) | 4; + pos++; + } + } +} diff --git a/core/deps/lzma/BraIA64.c b/core/deps/lzma/BraIA64.c new file mode 100644 index 000000000..813830c79 --- /dev/null +++ b/core/deps/lzma/BraIA64.c @@ -0,0 +1,69 @@ +/* BraIA64.c -- Converter for IA-64 code +2013-11-12 : Igor Pavlov : Public domain */ + +#include "Precomp.h" + +#include "Bra.h" + +static const Byte kBranchTable[32] = +{ + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 4, 4, 6, 6, 0, 0, 7, 7, + 4, 4, 0, 0, 4, 4, 0, 0 +}; + +SizeT IA64_Convert(Byte *data, SizeT size, UInt32 ip, int encoding) +{ + SizeT i; + if (size < 16) + return 0; + size -= 16; + for (i = 0; i <= size; i += 16) + { + UInt32 instrTemplate = data[i] & 0x1F; + UInt32 mask = kBranchTable[instrTemplate]; + UInt32 bitPos = 5; + int slot; + for (slot = 0; slot < 3; slot++, bitPos += 41) + { + UInt32 bytePos, bitRes; + UInt64 instruction, instNorm; + int j; + if (((mask >> slot) & 1) == 0) + continue; + bytePos = (bitPos >> 3); + bitRes = bitPos & 0x7; + instruction = 0; + for (j = 0; j < 6; j++) + instruction += (UInt64)data[i + j + bytePos] << (8 * j); + + instNorm = instruction >> bitRes; + if (((instNorm >> 37) & 0xF) == 0x5 && ((instNorm >> 9) & 0x7) == 0) + { + UInt32 src = (UInt32)((instNorm >> 13) & 0xFFFFF); + UInt32 dest; + src |= ((UInt32)(instNorm >> 36) & 1) << 20; + + src <<= 4; + + if (encoding) + dest = ip + (UInt32)i + src; + else + dest = src - (ip + (UInt32)i); + + dest >>= 4; + + instNorm &= ~((UInt64)(0x8FFFFF) << 13); + instNorm |= ((UInt64)(dest & 0xFFFFF) << 13); + instNorm |= ((UInt64)(dest & 0x100000) << (36 - 20)); + + instruction &= (1 << bitRes) - 1; + instruction |= (instNorm << bitRes); + for (j = 0; j < 6; j++) + data[i + j + bytePos] = (Byte)(instruction >> (8 * j)); + } + } + } + return i; +} diff --git a/core/deps/lzma/Compiler.h b/core/deps/lzma/Compiler.h new file mode 100644 index 000000000..de8fab374 --- /dev/null +++ b/core/deps/lzma/Compiler.h @@ -0,0 +1,32 @@ +/* Compiler.h +2015-08-02 : Igor Pavlov : Public domain */ + +#ifndef __7Z_COMPILER_H +#define __7Z_COMPILER_H + +#ifdef _MSC_VER + + #ifdef UNDER_CE + #define RPC_NO_WINDOWS_H + /* #pragma warning(disable : 4115) // '_RPC_ASYNC_STATE' : named type definition in parentheses */ + #pragma warning(disable : 4201) // nonstandard extension used : nameless struct/union + #pragma warning(disable : 4214) // nonstandard extension used : bit field types other than int + #endif + + #if _MSC_VER >= 1300 + #pragma warning(disable : 4996) // This function or variable may be unsafe + #else + #pragma warning(disable : 4511) // copy constructor could not be generated + #pragma warning(disable : 4512) // assignment operator could not be generated + #pragma warning(disable : 4514) // unreferenced inline function has been removed + #pragma warning(disable : 4702) // unreachable code + #pragma warning(disable : 4710) // not inlined + #pragma warning(disable : 4786) // identifier was truncated to '255' characters in the debug information + #endif + +#endif + +#define UNUSED_VAR(x) (void)x; +/* #define UNUSED_VAR(x) x=x; */ + +#endif diff --git a/core/deps/lzma/CpuArch.c b/core/deps/lzma/CpuArch.c new file mode 100644 index 000000000..f835c2b7b --- /dev/null +++ b/core/deps/lzma/CpuArch.c @@ -0,0 +1,200 @@ +/* CpuArch.c -- CPU specific code +2016-02-25: Igor Pavlov : Public domain */ + +#include "Precomp.h" + +#include "CpuArch.h" + +#ifdef MY_CPU_X86_OR_AMD64 + +#if (defined(_MSC_VER) && !defined(MY_CPU_AMD64)) || defined(__GNUC__) +#define USE_ASM +#endif + +#if !defined(USE_ASM) && _MSC_VER >= 1500 +#include +#endif + +#if defined(USE_ASM) && !defined(MY_CPU_AMD64) +static UInt32 CheckFlag(UInt32 flag) +{ + #ifdef _MSC_VER + __asm pushfd; + __asm pop EAX; + __asm mov EDX, EAX; + __asm xor EAX, flag; + __asm push EAX; + __asm popfd; + __asm pushfd; + __asm pop EAX; + __asm xor EAX, EDX; + __asm push EDX; + __asm popfd; + __asm and flag, EAX; + #else + __asm__ __volatile__ ( + "pushf\n\t" + "pop %%EAX\n\t" + "movl %%EAX,%%EDX\n\t" + "xorl %0,%%EAX\n\t" + "push %%EAX\n\t" + "popf\n\t" + "pushf\n\t" + "pop %%EAX\n\t" + "xorl %%EDX,%%EAX\n\t" + "push %%EDX\n\t" + "popf\n\t" + "andl %%EAX, %0\n\t": + "=c" (flag) : "c" (flag) : + "%eax", "%edx"); + #endif + return flag; +} +#define CHECK_CPUID_IS_SUPPORTED if (CheckFlag(1 << 18) == 0 || CheckFlag(1 << 21) == 0) return False; +#else +#define CHECK_CPUID_IS_SUPPORTED +#endif + +void MyCPUID(UInt32 function, UInt32 *a, UInt32 *b, UInt32 *c, UInt32 *d) +{ + #ifdef USE_ASM + + #ifdef _MSC_VER + + UInt32 a2, b2, c2, d2; + __asm xor EBX, EBX; + __asm xor ECX, ECX; + __asm xor EDX, EDX; + __asm mov EAX, function; + __asm cpuid; + __asm mov a2, EAX; + __asm mov b2, EBX; + __asm mov c2, ECX; + __asm mov d2, EDX; + + *a = a2; + *b = b2; + *c = c2; + *d = d2; + + #else + + __asm__ __volatile__ ( + #if defined(MY_CPU_AMD64) && defined(__PIC__) + "mov %%rbx, %%rdi;" + "cpuid;" + "xchg %%rbx, %%rdi;" + : "=a" (*a) , + "=D" (*b) , + #elif defined(MY_CPU_X86) && defined(__PIC__) + "mov %%ebx, %%edi;" + "cpuid;" + "xchgl %%ebx, %%edi;" + : "=a" (*a) , + "=D" (*b) , + #else + "cpuid" + : "=a" (*a) , + "=b" (*b) , + #endif + "=c" (*c) , + "=d" (*d) + : "0" (function)) ; + + #endif + + #else + + int CPUInfo[4]; + __cpuid(CPUInfo, function); + *a = CPUInfo[0]; + *b = CPUInfo[1]; + *c = CPUInfo[2]; + *d = CPUInfo[3]; + + #endif +} + +Bool x86cpuid_CheckAndRead(Cx86cpuid *p) +{ + CHECK_CPUID_IS_SUPPORTED + MyCPUID(0, &p->maxFunc, &p->vendor[0], &p->vendor[2], &p->vendor[1]); + MyCPUID(1, &p->ver, &p->b, &p->c, &p->d); + return True; +} + +static const UInt32 kVendors[][3] = +{ + { 0x756E6547, 0x49656E69, 0x6C65746E}, + { 0x68747541, 0x69746E65, 0x444D4163}, + { 0x746E6543, 0x48727561, 0x736C7561} +}; + +int x86cpuid_GetFirm(const Cx86cpuid *p) +{ + unsigned i; + for (i = 0; i < sizeof(kVendors) / sizeof(kVendors[i]); i++) + { + const UInt32 *v = kVendors[i]; + if (v[0] == p->vendor[0] && + v[1] == p->vendor[1] && + v[2] == p->vendor[2]) + return (int)i; + } + return -1; +} + +Bool CPU_Is_InOrder() +{ + Cx86cpuid p; + int firm; + UInt32 family, model; + if (!x86cpuid_CheckAndRead(&p)) + return True; + + family = x86cpuid_GetFamily(p.ver); + model = x86cpuid_GetModel(p.ver); + + firm = x86cpuid_GetFirm(&p); + + switch (firm) + { + case CPU_FIRM_INTEL: return (family < 6 || (family == 6 && ( + /* In-Order Atom CPU */ + model == 0x1C /* 45 nm, N4xx, D4xx, N5xx, D5xx, 230, 330 */ + || model == 0x26 /* 45 nm, Z6xx */ + || model == 0x27 /* 32 nm, Z2460 */ + || model == 0x35 /* 32 nm, Z2760 */ + || model == 0x36 /* 32 nm, N2xxx, D2xxx */ + ))); + case CPU_FIRM_AMD: return (family < 5 || (family == 5 && (model < 6 || model == 0xA))); + case CPU_FIRM_VIA: return (family < 6 || (family == 6 && model < 0xF)); + } + return True; +} + +#if !defined(MY_CPU_AMD64) && defined(_WIN32) +#include +static Bool CPU_Sys_Is_SSE_Supported() +{ + OSVERSIONINFO vi; + vi.dwOSVersionInfoSize = sizeof(vi); + if (!GetVersionEx(&vi)) + return False; + return (vi.dwMajorVersion >= 5); +} +#define CHECK_SYS_SSE_SUPPORT if (!CPU_Sys_Is_SSE_Supported()) return False; +#else +#define CHECK_SYS_SSE_SUPPORT +#endif + +Bool CPU_Is_Aes_Supported() +{ + Cx86cpuid p; + CHECK_SYS_SSE_SUPPORT + if (!x86cpuid_CheckAndRead(&p)) + return False; + return (p.c >> 25) & 1; +} + +#endif diff --git a/core/deps/lzma/CpuArch.h b/core/deps/lzma/CpuArch.h new file mode 100644 index 000000000..ef6083c3b --- /dev/null +++ b/core/deps/lzma/CpuArch.h @@ -0,0 +1,223 @@ +/* CpuArch.h -- CPU specific code +2016-06-09: Igor Pavlov : Public domain */ + +#ifndef __CPU_ARCH_H +#define __CPU_ARCH_H + +#include "7zTypes.h" + +EXTERN_C_BEGIN + +/* +MY_CPU_LE means that CPU is LITTLE ENDIAN. +MY_CPU_BE means that CPU is BIG ENDIAN. +If MY_CPU_LE and MY_CPU_BE are not defined, we don't know about ENDIANNESS of platform. + +MY_CPU_LE_UNALIGN means that CPU is LITTLE ENDIAN and CPU supports unaligned memory accesses. +*/ + +#if defined(_M_X64) \ + || defined(_M_AMD64) \ + || defined(__x86_64__) \ + || defined(__AMD64__) \ + || defined(__amd64__) + #define MY_CPU_AMD64 +#endif + +#if defined(MY_CPU_AMD64) \ + || defined(_M_IA64) \ + || defined(__AARCH64EL__) \ + || defined(__AARCH64EB__) + #define MY_CPU_64BIT +#endif + +#if defined(_M_IX86) || defined(__i386__) +#define MY_CPU_X86 +#endif + +#if defined(MY_CPU_X86) || defined(MY_CPU_AMD64) +#define MY_CPU_X86_OR_AMD64 +#endif + +#if defined(MY_CPU_X86) \ + || defined(_M_ARM) \ + || defined(__ARMEL__) \ + || defined(__THUMBEL__) \ + || defined(__ARMEB__) \ + || defined(__THUMBEB__) + #define MY_CPU_32BIT +#endif + +#if defined(_WIN32) && defined(_M_ARM) +#define MY_CPU_ARM_LE +#endif + +#if defined(_WIN32) && defined(_M_IA64) +#define MY_CPU_IA64_LE +#endif + +#if defined(MY_CPU_X86_OR_AMD64) \ + || defined(MY_CPU_ARM_LE) \ + || defined(MY_CPU_IA64_LE) \ + || defined(__LITTLE_ENDIAN__) \ + || defined(__ARMEL__) \ + || defined(__THUMBEL__) \ + || defined(__AARCH64EL__) \ + || defined(__MIPSEL__) \ + || defined(__MIPSEL) \ + || defined(_MIPSEL) \ + || defined(__BFIN__) \ + || (defined(__BYTE_ORDER__) && (__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__)) + #define MY_CPU_LE +#endif + +#if defined(__BIG_ENDIAN__) \ + || defined(__ARMEB__) \ + || defined(__THUMBEB__) \ + || defined(__AARCH64EB__) \ + || defined(__MIPSEB__) \ + || defined(__MIPSEB) \ + || defined(_MIPSEB) \ + || defined(__m68k__) \ + || defined(__s390__) \ + || defined(__s390x__) \ + || defined(__zarch__) \ + || (defined(__BYTE_ORDER__) && (__BYTE_ORDER__ == __ORDER_BIG_ENDIAN__)) + #define MY_CPU_BE +#endif + +#if defined(MY_CPU_LE) && defined(MY_CPU_BE) +Stop_Compiling_Bad_Endian +#endif + + +#ifdef MY_CPU_LE + #if defined(MY_CPU_X86_OR_AMD64) \ + /* || defined(__AARCH64EL__) */ + #define MY_CPU_LE_UNALIGN + #endif +#endif + + +#ifdef MY_CPU_LE_UNALIGN + +#define GetUi16(p) (*(const UInt16 *)(const void *)(p)) +#define GetUi32(p) (*(const UInt32 *)(const void *)(p)) +#define GetUi64(p) (*(const UInt64 *)(const void *)(p)) + +#define SetUi16(p, v) { *(UInt16 *)(p) = (v); } +#define SetUi32(p, v) { *(UInt32 *)(p) = (v); } +#define SetUi64(p, v) { *(UInt64 *)(p) = (v); } + +#else + +#define GetUi16(p) ( (UInt16) ( \ + ((const Byte *)(p))[0] | \ + ((UInt16)((const Byte *)(p))[1] << 8) )) + +#define GetUi32(p) ( \ + ((const Byte *)(p))[0] | \ + ((UInt32)((const Byte *)(p))[1] << 8) | \ + ((UInt32)((const Byte *)(p))[2] << 16) | \ + ((UInt32)((const Byte *)(p))[3] << 24)) + +#define GetUi64(p) (GetUi32(p) | ((UInt64)GetUi32(((const Byte *)(p)) + 4) << 32)) + +#define SetUi16(p, v) { Byte *_ppp_ = (Byte *)(p); UInt32 _vvv_ = (v); \ + _ppp_[0] = (Byte)_vvv_; \ + _ppp_[1] = (Byte)(_vvv_ >> 8); } + +#define SetUi32(p, v) { Byte *_ppp_ = (Byte *)(p); UInt32 _vvv_ = (v); \ + _ppp_[0] = (Byte)_vvv_; \ + _ppp_[1] = (Byte)(_vvv_ >> 8); \ + _ppp_[2] = (Byte)(_vvv_ >> 16); \ + _ppp_[3] = (Byte)(_vvv_ >> 24); } + +#define SetUi64(p, v) { Byte *_ppp2_ = (Byte *)(p); UInt64 _vvv2_ = (v); \ + SetUi32(_ppp2_ , (UInt32)_vvv2_); \ + SetUi32(_ppp2_ + 4, (UInt32)(_vvv2_ >> 32)); } + +#endif + + +#if defined(MY_CPU_LE_UNALIGN) && /* defined(_WIN64) && */ (_MSC_VER >= 1300) + +/* Note: we use bswap instruction, that is unsupported in 386 cpu */ + +#include + +#pragma intrinsic(_byteswap_ulong) +#pragma intrinsic(_byteswap_uint64) +#define GetBe32(p) _byteswap_ulong(*(const UInt32 *)(const Byte *)(p)) +#define GetBe64(p) _byteswap_uint64(*(const UInt64 *)(const Byte *)(p)) + +#define SetBe32(p, v) (*(UInt32 *)(void *)(p)) = _byteswap_ulong(v) + +#elif defined(MY_CPU_LE_UNALIGN) && defined (__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 3)) + +#define GetBe32(p) __builtin_bswap32(*(const UInt32 *)(const Byte *)(p)) +#define GetBe64(p) __builtin_bswap64(*(const UInt64 *)(const Byte *)(p)) + +#define SetBe32(p, v) (*(UInt32 *)(void *)(p)) = __builtin_bswap32(v) + +#else + +#define GetBe32(p) ( \ + ((UInt32)((const Byte *)(p))[0] << 24) | \ + ((UInt32)((const Byte *)(p))[1] << 16) | \ + ((UInt32)((const Byte *)(p))[2] << 8) | \ + ((const Byte *)(p))[3] ) + +#define GetBe64(p) (((UInt64)GetBe32(p) << 32) | GetBe32(((const Byte *)(p)) + 4)) + +#define SetBe32(p, v) { Byte *_ppp_ = (Byte *)(p); UInt32 _vvv_ = (v); \ + _ppp_[0] = (Byte)(_vvv_ >> 24); \ + _ppp_[1] = (Byte)(_vvv_ >> 16); \ + _ppp_[2] = (Byte)(_vvv_ >> 8); \ + _ppp_[3] = (Byte)_vvv_; } + +#endif + + +#define GetBe16(p) ( (UInt16) ( \ + ((UInt16)((const Byte *)(p))[0] << 8) | \ + ((const Byte *)(p))[1] )) + + + +#ifdef MY_CPU_X86_OR_AMD64 + +typedef struct +{ + UInt32 maxFunc; + UInt32 vendor[3]; + UInt32 ver; + UInt32 b; + UInt32 c; + UInt32 d; +} Cx86cpuid; + +enum +{ + CPU_FIRM_INTEL, + CPU_FIRM_AMD, + CPU_FIRM_VIA +}; + +void MyCPUID(UInt32 function, UInt32 *a, UInt32 *b, UInt32 *c, UInt32 *d); + +Bool x86cpuid_CheckAndRead(Cx86cpuid *p); +int x86cpuid_GetFirm(const Cx86cpuid *p); + +#define x86cpuid_GetFamily(ver) (((ver >> 16) & 0xFF0) | ((ver >> 8) & 0xF)) +#define x86cpuid_GetModel(ver) (((ver >> 12) & 0xF0) | ((ver >> 4) & 0xF)) +#define x86cpuid_GetStepping(ver) (ver & 0xF) + +Bool CPU_Is_InOrder(); +Bool CPU_Is_Aes_Supported(); + +#endif + +EXTERN_C_END + +#endif diff --git a/core/deps/lzma/Delta.c b/core/deps/lzma/Delta.c new file mode 100644 index 000000000..6cbbe4601 --- /dev/null +++ b/core/deps/lzma/Delta.c @@ -0,0 +1,64 @@ +/* Delta.c -- Delta converter +2009-05-26 : Igor Pavlov : Public domain */ + +#include "Precomp.h" + +#include "Delta.h" + +void Delta_Init(Byte *state) +{ + unsigned i; + for (i = 0; i < DELTA_STATE_SIZE; i++) + state[i] = 0; +} + +static void MyMemCpy(Byte *dest, const Byte *src, unsigned size) +{ + unsigned i; + for (i = 0; i < size; i++) + dest[i] = src[i]; +} + +void Delta_Encode(Byte *state, unsigned delta, Byte *data, SizeT size) +{ + Byte buf[DELTA_STATE_SIZE]; + unsigned j = 0; + MyMemCpy(buf, state, delta); + { + SizeT i; + for (i = 0; i < size;) + { + for (j = 0; j < delta && i < size; i++, j++) + { + Byte b = data[i]; + data[i] = (Byte)(b - buf[j]); + buf[j] = b; + } + } + } + if (j == delta) + j = 0; + MyMemCpy(state, buf + j, delta - j); + MyMemCpy(state + delta - j, buf, j); +} + +void Delta_Decode(Byte *state, unsigned delta, Byte *data, SizeT size) +{ + Byte buf[DELTA_STATE_SIZE]; + unsigned j = 0; + MyMemCpy(buf, state, delta); + { + SizeT i; + for (i = 0; i < size;) + { + for (j = 0; j < delta && i < size; i++, j++) + { + buf[j] = data[i] = (Byte)(buf[j] + data[i]); + } + } + } + if (j == delta) + j = 0; + MyMemCpy(state, buf + j, delta - j); + MyMemCpy(state + delta - j, buf, j); +} diff --git a/core/deps/lzma/Delta.h b/core/deps/lzma/Delta.h new file mode 100644 index 000000000..e59d5a252 --- /dev/null +++ b/core/deps/lzma/Delta.h @@ -0,0 +1,19 @@ +/* Delta.h -- Delta converter +2013-01-18 : Igor Pavlov : Public domain */ + +#ifndef __DELTA_H +#define __DELTA_H + +#include "7zTypes.h" + +EXTERN_C_BEGIN + +#define DELTA_STATE_SIZE 256 + +void Delta_Init(Byte *state); +void Delta_Encode(Byte *state, unsigned delta, Byte *data, SizeT size); +void Delta_Decode(Byte *state, unsigned delta, Byte *data, SizeT size); + +EXTERN_C_END + +#endif diff --git a/core/deps/lzma/LzFind.c b/core/deps/lzma/LzFind.c new file mode 100644 index 000000000..c335d363c --- /dev/null +++ b/core/deps/lzma/LzFind.c @@ -0,0 +1,1044 @@ +/* LzFind.c -- Match finder for LZ algorithms +2015-10-15 : Igor Pavlov : Public domain */ + +#include "Precomp.h" + +#include + +#include "LzFind.h" +#include "LzHash.h" + +#define kEmptyHashValue 0 +#define kMaxValForNormalize ((UInt32)0xFFFFFFFF) +#define kNormalizeStepMin (1 << 10) /* it must be power of 2 */ +#define kNormalizeMask (~(UInt32)(kNormalizeStepMin - 1)) +#define kMaxHistorySize ((UInt32)7 << 29) + +#define kStartMaxLen 3 + +static void LzInWindow_Free(CMatchFinder *p, ISzAlloc *alloc) +{ + if (!p->directInput) + { + alloc->Free(alloc, p->bufferBase); + p->bufferBase = NULL; + } +} + +/* keepSizeBefore + keepSizeAfter + keepSizeReserv must be < 4G) */ + +static int LzInWindow_Create(CMatchFinder *p, UInt32 keepSizeReserv, ISzAlloc *alloc) +{ + UInt32 blockSize = p->keepSizeBefore + p->keepSizeAfter + keepSizeReserv; + if (p->directInput) + { + p->blockSize = blockSize; + return 1; + } + if (!p->bufferBase || p->blockSize != blockSize) + { + LzInWindow_Free(p, alloc); + p->blockSize = blockSize; + p->bufferBase = (Byte *)alloc->Alloc(alloc, (size_t)blockSize); + } + return (p->bufferBase != NULL); +} + +Byte *MatchFinder_GetPointerToCurrentPos(CMatchFinder *p) { return p->buffer; } + +UInt32 MatchFinder_GetNumAvailableBytes(CMatchFinder *p) { return p->streamPos - p->pos; } + +void MatchFinder_ReduceOffsets(CMatchFinder *p, UInt32 subValue) +{ + p->posLimit -= subValue; + p->pos -= subValue; + p->streamPos -= subValue; +} + +static void MatchFinder_ReadBlock(CMatchFinder *p) +{ + if (p->streamEndWasReached || p->result != SZ_OK) + return; + + /* We use (p->streamPos - p->pos) value. (p->streamPos < p->pos) is allowed. */ + + if (p->directInput) + { + UInt32 curSize = 0xFFFFFFFF - (p->streamPos - p->pos); + if (curSize > p->directInputRem) + curSize = (UInt32)p->directInputRem; + p->directInputRem -= curSize; + p->streamPos += curSize; + if (p->directInputRem == 0) + p->streamEndWasReached = 1; + return; + } + + for (;;) + { + Byte *dest = p->buffer + (p->streamPos - p->pos); + size_t size = (p->bufferBase + p->blockSize - dest); + if (size == 0) + return; + + p->result = p->stream->Read(p->stream, dest, &size); + if (p->result != SZ_OK) + return; + if (size == 0) + { + p->streamEndWasReached = 1; + return; + } + p->streamPos += (UInt32)size; + if (p->streamPos - p->pos > p->keepSizeAfter) + return; + } +} + +void MatchFinder_MoveBlock(CMatchFinder *p) +{ + memmove(p->bufferBase, + p->buffer - p->keepSizeBefore, + (size_t)(p->streamPos - p->pos) + p->keepSizeBefore); + p->buffer = p->bufferBase + p->keepSizeBefore; +} + +int MatchFinder_NeedMove(CMatchFinder *p) +{ + if (p->directInput) + return 0; + /* if (p->streamEndWasReached) return 0; */ + return ((size_t)(p->bufferBase + p->blockSize - p->buffer) <= p->keepSizeAfter); +} + +void MatchFinder_ReadIfRequired(CMatchFinder *p) +{ + if (p->streamEndWasReached) + return; + if (p->keepSizeAfter >= p->streamPos - p->pos) + MatchFinder_ReadBlock(p); +} + +static void MatchFinder_CheckAndMoveAndRead(CMatchFinder *p) +{ + if (MatchFinder_NeedMove(p)) + MatchFinder_MoveBlock(p); + MatchFinder_ReadBlock(p); +} + +static void MatchFinder_SetDefaultSettings(CMatchFinder *p) +{ + p->cutValue = 32; + p->btMode = 1; + p->numHashBytes = 4; + p->bigHash = 0; +} + +#define kCrcPoly 0xEDB88320 + +void MatchFinder_Construct(CMatchFinder *p) +{ + UInt32 i; + p->bufferBase = NULL; + p->directInput = 0; + p->hash = NULL; + MatchFinder_SetDefaultSettings(p); + + for (i = 0; i < 256; i++) + { + UInt32 r = i; + unsigned j; + for (j = 0; j < 8; j++) + r = (r >> 1) ^ (kCrcPoly & ~((r & 1) - 1)); + p->crc[i] = r; + } +} + +static void MatchFinder_FreeThisClassMemory(CMatchFinder *p, ISzAlloc *alloc) +{ + alloc->Free(alloc, p->hash); + p->hash = NULL; +} + +void MatchFinder_Free(CMatchFinder *p, ISzAlloc *alloc) +{ + MatchFinder_FreeThisClassMemory(p, alloc); + LzInWindow_Free(p, alloc); +} + +static CLzRef* AllocRefs(size_t num, ISzAlloc *alloc) +{ + size_t sizeInBytes = (size_t)num * sizeof(CLzRef); + if (sizeInBytes / sizeof(CLzRef) != num) + return NULL; + return (CLzRef *)alloc->Alloc(alloc, sizeInBytes); +} + +int MatchFinder_Create(CMatchFinder *p, UInt32 historySize, + UInt32 keepAddBufferBefore, UInt32 matchMaxLen, UInt32 keepAddBufferAfter, + ISzAlloc *alloc) +{ + UInt32 sizeReserv; + + if (historySize > kMaxHistorySize) + { + MatchFinder_Free(p, alloc); + return 0; + } + + sizeReserv = historySize >> 1; + if (historySize >= ((UInt32)3 << 30)) sizeReserv = historySize >> 3; + else if (historySize >= ((UInt32)2 << 30)) sizeReserv = historySize >> 2; + + sizeReserv += (keepAddBufferBefore + matchMaxLen + keepAddBufferAfter) / 2 + (1 << 19); + + p->keepSizeBefore = historySize + keepAddBufferBefore + 1; + p->keepSizeAfter = matchMaxLen + keepAddBufferAfter; + + /* we need one additional byte, since we use MoveBlock after pos++ and before dictionary using */ + + if (LzInWindow_Create(p, sizeReserv, alloc)) + { + UInt32 newCyclicBufferSize = historySize + 1; + UInt32 hs; + p->matchMaxLen = matchMaxLen; + { + p->fixedHashSize = 0; + if (p->numHashBytes == 2) + hs = (1 << 16) - 1; + else + { + hs = historySize - 1; + hs |= (hs >> 1); + hs |= (hs >> 2); + hs |= (hs >> 4); + hs |= (hs >> 8); + hs >>= 1; + hs |= 0xFFFF; /* don't change it! It's required for Deflate */ + if (hs > (1 << 24)) + { + if (p->numHashBytes == 3) + hs = (1 << 24) - 1; + else + hs >>= 1; + /* if (bigHash) mode, GetHeads4b() in LzFindMt.c needs (hs >= ((1 << 24) - 1))) */ + } + } + p->hashMask = hs; + hs++; + if (p->numHashBytes > 2) p->fixedHashSize += kHash2Size; + if (p->numHashBytes > 3) p->fixedHashSize += kHash3Size; + if (p->numHashBytes > 4) p->fixedHashSize += kHash4Size; + hs += p->fixedHashSize; + } + + { + size_t newSize; + size_t numSons; + p->historySize = historySize; + p->hashSizeSum = hs; + p->cyclicBufferSize = newCyclicBufferSize; + + numSons = newCyclicBufferSize; + if (p->btMode) + numSons <<= 1; + newSize = hs + numSons; + + if (p->hash && p->numRefs == newSize) + return 1; + + MatchFinder_FreeThisClassMemory(p, alloc); + p->numRefs = newSize; + p->hash = AllocRefs(newSize, alloc); + + if (p->hash) + { + p->son = p->hash + p->hashSizeSum; + return 1; + } + } + } + + MatchFinder_Free(p, alloc); + return 0; +} + +static void MatchFinder_SetLimits(CMatchFinder *p) +{ + UInt32 limit = kMaxValForNormalize - p->pos; + UInt32 limit2 = p->cyclicBufferSize - p->cyclicBufferPos; + + if (limit2 < limit) + limit = limit2; + limit2 = p->streamPos - p->pos; + + if (limit2 <= p->keepSizeAfter) + { + if (limit2 > 0) + limit2 = 1; + } + else + limit2 -= p->keepSizeAfter; + + if (limit2 < limit) + limit = limit2; + + { + UInt32 lenLimit = p->streamPos - p->pos; + if (lenLimit > p->matchMaxLen) + lenLimit = p->matchMaxLen; + p->lenLimit = lenLimit; + } + p->posLimit = p->pos + limit; +} + +void MatchFinder_Init_2(CMatchFinder *p, int readData) +{ + UInt32 i; + UInt32 *hash = p->hash; + UInt32 num = p->hashSizeSum; + for (i = 0; i < num; i++) + hash[i] = kEmptyHashValue; + + p->cyclicBufferPos = 0; + p->buffer = p->bufferBase; + p->pos = p->streamPos = p->cyclicBufferSize; + p->result = SZ_OK; + p->streamEndWasReached = 0; + + if (readData) + MatchFinder_ReadBlock(p); + + MatchFinder_SetLimits(p); +} + +void MatchFinder_Init(CMatchFinder *p) +{ + MatchFinder_Init_2(p, True); +} + +static UInt32 MatchFinder_GetSubValue(CMatchFinder *p) +{ + return (p->pos - p->historySize - 1) & kNormalizeMask; +} + +void MatchFinder_Normalize3(UInt32 subValue, CLzRef *items, size_t numItems) +{ + size_t i; + for (i = 0; i < numItems; i++) + { + UInt32 value = items[i]; + if (value <= subValue) + value = kEmptyHashValue; + else + value -= subValue; + items[i] = value; + } +} + +static void MatchFinder_Normalize(CMatchFinder *p) +{ + UInt32 subValue = MatchFinder_GetSubValue(p); + MatchFinder_Normalize3(subValue, p->hash, p->numRefs); + MatchFinder_ReduceOffsets(p, subValue); +} + +static void MatchFinder_CheckLimits(CMatchFinder *p) +{ + if (p->pos == kMaxValForNormalize) + MatchFinder_Normalize(p); + if (!p->streamEndWasReached && p->keepSizeAfter == p->streamPos - p->pos) + MatchFinder_CheckAndMoveAndRead(p); + if (p->cyclicBufferPos == p->cyclicBufferSize) + p->cyclicBufferPos = 0; + MatchFinder_SetLimits(p); +} + +static UInt32 * Hc_GetMatchesSpec(UInt32 lenLimit, UInt32 curMatch, UInt32 pos, const Byte *cur, CLzRef *son, + UInt32 _cyclicBufferPos, UInt32 _cyclicBufferSize, UInt32 cutValue, + UInt32 *distances, UInt32 maxLen) +{ + son[_cyclicBufferPos] = curMatch; + for (;;) + { + UInt32 delta = pos - curMatch; + if (cutValue-- == 0 || delta >= _cyclicBufferSize) + return distances; + { + const Byte *pb = cur - delta; + curMatch = son[_cyclicBufferPos - delta + ((delta > _cyclicBufferPos) ? _cyclicBufferSize : 0)]; + if (pb[maxLen] == cur[maxLen] && *pb == *cur) + { + UInt32 len = 0; + while (++len != lenLimit) + if (pb[len] != cur[len]) + break; + if (maxLen < len) + { + *distances++ = maxLen = len; + *distances++ = delta - 1; + if (len == lenLimit) + return distances; + } + } + } + } +} + +UInt32 * GetMatchesSpec1(UInt32 lenLimit, UInt32 curMatch, UInt32 pos, const Byte *cur, CLzRef *son, + UInt32 _cyclicBufferPos, UInt32 _cyclicBufferSize, UInt32 cutValue, + UInt32 *distances, UInt32 maxLen) +{ + CLzRef *ptr0 = son + (_cyclicBufferPos << 1) + 1; + CLzRef *ptr1 = son + (_cyclicBufferPos << 1); + UInt32 len0 = 0, len1 = 0; + for (;;) + { + UInt32 delta = pos - curMatch; + if (cutValue-- == 0 || delta >= _cyclicBufferSize) + { + *ptr0 = *ptr1 = kEmptyHashValue; + return distances; + } + { + CLzRef *pair = son + ((_cyclicBufferPos - delta + ((delta > _cyclicBufferPos) ? _cyclicBufferSize : 0)) << 1); + const Byte *pb = cur - delta; + UInt32 len = (len0 < len1 ? len0 : len1); + if (pb[len] == cur[len]) + { + if (++len != lenLimit && pb[len] == cur[len]) + while (++len != lenLimit) + if (pb[len] != cur[len]) + break; + if (maxLen < len) + { + *distances++ = maxLen = len; + *distances++ = delta - 1; + if (len == lenLimit) + { + *ptr1 = pair[0]; + *ptr0 = pair[1]; + return distances; + } + } + } + if (pb[len] < cur[len]) + { + *ptr1 = curMatch; + ptr1 = pair + 1; + curMatch = *ptr1; + len1 = len; + } + else + { + *ptr0 = curMatch; + ptr0 = pair; + curMatch = *ptr0; + len0 = len; + } + } + } +} + +static void SkipMatchesSpec(UInt32 lenLimit, UInt32 curMatch, UInt32 pos, const Byte *cur, CLzRef *son, + UInt32 _cyclicBufferPos, UInt32 _cyclicBufferSize, UInt32 cutValue) +{ + CLzRef *ptr0 = son + (_cyclicBufferPos << 1) + 1; + CLzRef *ptr1 = son + (_cyclicBufferPos << 1); + UInt32 len0 = 0, len1 = 0; + for (;;) + { + UInt32 delta = pos - curMatch; + if (cutValue-- == 0 || delta >= _cyclicBufferSize) + { + *ptr0 = *ptr1 = kEmptyHashValue; + return; + } + { + CLzRef *pair = son + ((_cyclicBufferPos - delta + ((delta > _cyclicBufferPos) ? _cyclicBufferSize : 0)) << 1); + const Byte *pb = cur - delta; + UInt32 len = (len0 < len1 ? len0 : len1); + if (pb[len] == cur[len]) + { + while (++len != lenLimit) + if (pb[len] != cur[len]) + break; + { + if (len == lenLimit) + { + *ptr1 = pair[0]; + *ptr0 = pair[1]; + return; + } + } + } + if (pb[len] < cur[len]) + { + *ptr1 = curMatch; + ptr1 = pair + 1; + curMatch = *ptr1; + len1 = len; + } + else + { + *ptr0 = curMatch; + ptr0 = pair; + curMatch = *ptr0; + len0 = len; + } + } + } +} + +#define MOVE_POS \ + ++p->cyclicBufferPos; \ + p->buffer++; \ + if (++p->pos == p->posLimit) MatchFinder_CheckLimits(p); + +#define MOVE_POS_RET MOVE_POS return offset; + +static void MatchFinder_MovePos(CMatchFinder *p) { MOVE_POS; } + +#define GET_MATCHES_HEADER2(minLen, ret_op) \ + UInt32 lenLimit; UInt32 hv; const Byte *cur; UInt32 curMatch; \ + lenLimit = p->lenLimit; { if (lenLimit < minLen) { MatchFinder_MovePos(p); ret_op; }} \ + cur = p->buffer; + +#define GET_MATCHES_HEADER(minLen) GET_MATCHES_HEADER2(minLen, return 0) +#define SKIP_HEADER(minLen) GET_MATCHES_HEADER2(minLen, continue) + +#define MF_PARAMS(p) p->pos, p->buffer, p->son, p->cyclicBufferPos, p->cyclicBufferSize, p->cutValue + +#define GET_MATCHES_FOOTER(offset, maxLen) \ + offset = (UInt32)(GetMatchesSpec1(lenLimit, curMatch, MF_PARAMS(p), \ + distances + offset, maxLen) - distances); MOVE_POS_RET; + +#define SKIP_FOOTER \ + SkipMatchesSpec(lenLimit, curMatch, MF_PARAMS(p)); MOVE_POS; + +#define UPDATE_maxLen { \ + ptrdiff_t diff = (ptrdiff_t)0 - d2; \ + const Byte *c = cur + maxLen; \ + const Byte *lim = cur + lenLimit; \ + for (; c != lim; c++) if (*(c + diff) != *c) break; \ + maxLen = (UInt32)(c - cur); } + +static UInt32 Bt2_MatchFinder_GetMatches(CMatchFinder *p, UInt32 *distances) +{ + UInt32 offset; + GET_MATCHES_HEADER(2) + HASH2_CALC; + curMatch = p->hash[hv]; + p->hash[hv] = p->pos; + offset = 0; + GET_MATCHES_FOOTER(offset, 1) +} + +UInt32 Bt3Zip_MatchFinder_GetMatches(CMatchFinder *p, UInt32 *distances) +{ + UInt32 offset; + GET_MATCHES_HEADER(3) + HASH_ZIP_CALC; + curMatch = p->hash[hv]; + p->hash[hv] = p->pos; + offset = 0; + GET_MATCHES_FOOTER(offset, 2) +} + +static UInt32 Bt3_MatchFinder_GetMatches(CMatchFinder *p, UInt32 *distances) +{ + UInt32 h2, d2, maxLen, offset, pos; + UInt32 *hash; + GET_MATCHES_HEADER(3) + + HASH3_CALC; + + hash = p->hash; + pos = p->pos; + + d2 = pos - hash[h2]; + + curMatch = hash[kFix3HashSize + hv]; + + hash[h2] = pos; + hash[kFix3HashSize + hv] = pos; + + maxLen = 2; + offset = 0; + + if (d2 < p->cyclicBufferSize && *(cur - d2) == *cur) + { + UPDATE_maxLen + distances[0] = maxLen; + distances[1] = d2 - 1; + offset = 2; + if (maxLen == lenLimit) + { + SkipMatchesSpec(lenLimit, curMatch, MF_PARAMS(p)); + MOVE_POS_RET; + } + } + + GET_MATCHES_FOOTER(offset, maxLen) +} + +static UInt32 Bt4_MatchFinder_GetMatches(CMatchFinder *p, UInt32 *distances) +{ + UInt32 h2, h3, d2, d3, maxLen, offset, pos; + UInt32 *hash; + GET_MATCHES_HEADER(4) + + HASH4_CALC; + + hash = p->hash; + pos = p->pos; + + d2 = pos - hash[ h2]; + d3 = pos - hash[kFix3HashSize + h3]; + + curMatch = hash[kFix4HashSize + hv]; + + hash[ h2] = pos; + hash[kFix3HashSize + h3] = pos; + hash[kFix4HashSize + hv] = pos; + + maxLen = 0; + offset = 0; + + if (d2 < p->cyclicBufferSize && *(cur - d2) == *cur) + { + distances[0] = maxLen = 2; + distances[1] = d2 - 1; + offset = 2; + } + + if (d2 != d3 && d3 < p->cyclicBufferSize && *(cur - d3) == *cur) + { + maxLen = 3; + distances[offset + 1] = d3 - 1; + offset += 2; + d2 = d3; + } + + if (offset != 0) + { + UPDATE_maxLen + distances[offset - 2] = maxLen; + if (maxLen == lenLimit) + { + SkipMatchesSpec(lenLimit, curMatch, MF_PARAMS(p)); + MOVE_POS_RET; + } + } + + if (maxLen < 3) + maxLen = 3; + + GET_MATCHES_FOOTER(offset, maxLen) +} + +/* +static UInt32 Bt5_MatchFinder_GetMatches(CMatchFinder *p, UInt32 *distances) +{ + UInt32 h2, h3, h4, d2, d3, d4, maxLen, offset, pos; + UInt32 *hash; + GET_MATCHES_HEADER(5) + + HASH5_CALC; + + hash = p->hash; + pos = p->pos; + + d2 = pos - hash[ h2]; + d3 = pos - hash[kFix3HashSize + h3]; + d4 = pos - hash[kFix4HashSize + h4]; + + curMatch = hash[kFix5HashSize + hv]; + + hash[ h2] = pos; + hash[kFix3HashSize + h3] = pos; + hash[kFix4HashSize + h4] = pos; + hash[kFix5HashSize + hv] = pos; + + maxLen = 0; + offset = 0; + + if (d2 < p->cyclicBufferSize && *(cur - d2) == *cur) + { + distances[0] = maxLen = 2; + distances[1] = d2 - 1; + offset = 2; + if (*(cur - d2 + 2) == cur[2]) + distances[0] = maxLen = 3; + else if (d3 < p->cyclicBufferSize && *(cur - d3) == *cur) + { + distances[2] = maxLen = 3; + distances[3] = d3 - 1; + offset = 4; + d2 = d3; + } + } + else if (d3 < p->cyclicBufferSize && *(cur - d3) == *cur) + { + distances[0] = maxLen = 3; + distances[1] = d3 - 1; + offset = 2; + d2 = d3; + } + + if (d2 != d4 && d4 < p->cyclicBufferSize + && *(cur - d4) == *cur + && *(cur - d4 + 3) == *(cur + 3)) + { + maxLen = 4; + distances[offset + 1] = d4 - 1; + offset += 2; + d2 = d4; + } + + if (offset != 0) + { + UPDATE_maxLen + distances[offset - 2] = maxLen; + if (maxLen == lenLimit) + { + SkipMatchesSpec(lenLimit, curMatch, MF_PARAMS(p)); + MOVE_POS_RET; + } + } + + if (maxLen < 4) + maxLen = 4; + + GET_MATCHES_FOOTER(offset, maxLen) +} +*/ + +static UInt32 Hc4_MatchFinder_GetMatches(CMatchFinder *p, UInt32 *distances) +{ + UInt32 h2, h3, d2, d3, maxLen, offset, pos; + UInt32 *hash; + GET_MATCHES_HEADER(4) + + HASH4_CALC; + + hash = p->hash; + pos = p->pos; + + d2 = pos - hash[ h2]; + d3 = pos - hash[kFix3HashSize + h3]; + + curMatch = hash[kFix4HashSize + hv]; + + hash[ h2] = pos; + hash[kFix3HashSize + h3] = pos; + hash[kFix4HashSize + hv] = pos; + + maxLen = 0; + offset = 0; + + if (d2 < p->cyclicBufferSize && *(cur - d2) == *cur) + { + distances[0] = maxLen = 2; + distances[1] = d2 - 1; + offset = 2; + } + + if (d2 != d3 && d3 < p->cyclicBufferSize && *(cur - d3) == *cur) + { + maxLen = 3; + distances[offset + 1] = d3 - 1; + offset += 2; + d2 = d3; + } + + if (offset != 0) + { + UPDATE_maxLen + distances[offset - 2] = maxLen; + if (maxLen == lenLimit) + { + p->son[p->cyclicBufferPos] = curMatch; + MOVE_POS_RET; + } + } + + if (maxLen < 3) + maxLen = 3; + + offset = (UInt32)(Hc_GetMatchesSpec(lenLimit, curMatch, MF_PARAMS(p), + distances + offset, maxLen) - (distances)); + MOVE_POS_RET +} + +/* +static UInt32 Hc5_MatchFinder_GetMatches(CMatchFinder *p, UInt32 *distances) +{ + UInt32 h2, h3, h4, d2, d3, d4, maxLen, offset, pos + UInt32 *hash; + GET_MATCHES_HEADER(5) + + HASH5_CALC; + + hash = p->hash; + pos = p->pos; + + d2 = pos - hash[ h2]; + d3 = pos - hash[kFix3HashSize + h3]; + d4 = pos - hash[kFix4HashSize + h4]; + + curMatch = hash[kFix5HashSize + hv]; + + hash[ h2] = pos; + hash[kFix3HashSize + h3] = pos; + hash[kFix4HashSize + h4] = pos; + hash[kFix5HashSize + hv] = pos; + + maxLen = 0; + offset = 0; + + if (d2 < p->cyclicBufferSize && *(cur - d2) == *cur) + { + distances[0] = maxLen = 2; + distances[1] = d2 - 1; + offset = 2; + if (*(cur - d2 + 2) == cur[2]) + distances[0] = maxLen = 3; + else if (d3 < p->cyclicBufferSize && *(cur - d3) == *cur) + { + distances[2] = maxLen = 3; + distances[3] = d3 - 1; + offset = 4; + d2 = d3; + } + } + else if (d3 < p->cyclicBufferSize && *(cur - d3) == *cur) + { + distances[0] = maxLen = 3; + distances[1] = d3 - 1; + offset = 2; + d2 = d3; + } + + if (d2 != d4 && d4 < p->cyclicBufferSize + && *(cur - d4) == *cur + && *(cur - d4 + 3) == *(cur + 3)) + { + maxLen = 4; + distances[offset + 1] = d4 - 1; + offset += 2; + d2 = d4; + } + + if (offset != 0) + { + UPDATE_maxLen + distances[offset - 2] = maxLen; + if (maxLen == lenLimit) + { + p->son[p->cyclicBufferPos] = curMatch; + MOVE_POS_RET; + } + } + + if (maxLen < 4) + maxLen = 4; + + offset = (UInt32)(Hc_GetMatchesSpec(lenLimit, curMatch, MF_PARAMS(p), + distances + offset, maxLen) - (distances)); + MOVE_POS_RET +} +*/ + +UInt32 Hc3Zip_MatchFinder_GetMatches(CMatchFinder *p, UInt32 *distances) +{ + UInt32 offset; + GET_MATCHES_HEADER(3) + HASH_ZIP_CALC; + curMatch = p->hash[hv]; + p->hash[hv] = p->pos; + offset = (UInt32)(Hc_GetMatchesSpec(lenLimit, curMatch, MF_PARAMS(p), + distances, 2) - (distances)); + MOVE_POS_RET +} + +static void Bt2_MatchFinder_Skip(CMatchFinder *p, UInt32 num) +{ + do + { + SKIP_HEADER(2) + HASH2_CALC; + curMatch = p->hash[hv]; + p->hash[hv] = p->pos; + SKIP_FOOTER + } + while (--num != 0); +} + +void Bt3Zip_MatchFinder_Skip(CMatchFinder *p, UInt32 num) +{ + do + { + SKIP_HEADER(3) + HASH_ZIP_CALC; + curMatch = p->hash[hv]; + p->hash[hv] = p->pos; + SKIP_FOOTER + } + while (--num != 0); +} + +static void Bt3_MatchFinder_Skip(CMatchFinder *p, UInt32 num) +{ + do + { + UInt32 h2; + UInt32 *hash; + SKIP_HEADER(3) + HASH3_CALC; + hash = p->hash; + curMatch = hash[kFix3HashSize + hv]; + hash[h2] = + hash[kFix3HashSize + hv] = p->pos; + SKIP_FOOTER + } + while (--num != 0); +} + +static void Bt4_MatchFinder_Skip(CMatchFinder *p, UInt32 num) +{ + do + { + UInt32 h2, h3; + UInt32 *hash; + SKIP_HEADER(4) + HASH4_CALC; + hash = p->hash; + curMatch = hash[kFix4HashSize + hv]; + hash[ h2] = + hash[kFix3HashSize + h3] = + hash[kFix4HashSize + hv] = p->pos; + SKIP_FOOTER + } + while (--num != 0); +} + +/* +static void Bt5_MatchFinder_Skip(CMatchFinder *p, UInt32 num) +{ + do + { + UInt32 h2, h3, h4; + UInt32 *hash; + SKIP_HEADER(5) + HASH5_CALC; + hash = p->hash; + curMatch = hash[kFix5HashSize + hv]; + hash[ h2] = + hash[kFix3HashSize + h3] = + hash[kFix4HashSize + h4] = + hash[kFix5HashSize + hv] = p->pos; + SKIP_FOOTER + } + while (--num != 0); +} +*/ + +static void Hc4_MatchFinder_Skip(CMatchFinder *p, UInt32 num) +{ + do + { + UInt32 h2, h3; + UInt32 *hash; + SKIP_HEADER(4) + HASH4_CALC; + hash = p->hash; + curMatch = hash[kFix4HashSize + hv]; + hash[ h2] = + hash[kFix3HashSize + h3] = + hash[kFix4HashSize + hv] = p->pos; + p->son[p->cyclicBufferPos] = curMatch; + MOVE_POS + } + while (--num != 0); +} + +/* +static void Hc5_MatchFinder_Skip(CMatchFinder *p, UInt32 num) +{ + do + { + UInt32 h2, h3, h4; + UInt32 *hash; + SKIP_HEADER(5) + HASH5_CALC; + hash = p->hash; + curMatch = p->hash[kFix5HashSize + hv]; + hash[ h2] = + hash[kFix3HashSize + h3] = + hash[kFix4HashSize + h4] = + hash[kFix5HashSize + hv] = p->pos; + p->son[p->cyclicBufferPos] = curMatch; + MOVE_POS + } + while (--num != 0); +} +*/ + +void Hc3Zip_MatchFinder_Skip(CMatchFinder *p, UInt32 num) +{ + do + { + SKIP_HEADER(3) + HASH_ZIP_CALC; + curMatch = p->hash[hv]; + p->hash[hv] = p->pos; + p->son[p->cyclicBufferPos] = curMatch; + MOVE_POS + } + while (--num != 0); +} + +void MatchFinder_CreateVTable(CMatchFinder *p, IMatchFinder *vTable) +{ + vTable->Init = (Mf_Init_Func)MatchFinder_Init; + vTable->GetNumAvailableBytes = (Mf_GetNumAvailableBytes_Func)MatchFinder_GetNumAvailableBytes; + vTable->GetPointerToCurrentPos = (Mf_GetPointerToCurrentPos_Func)MatchFinder_GetPointerToCurrentPos; + if (!p->btMode) + { + /* if (p->numHashBytes <= 4) */ + { + vTable->GetMatches = (Mf_GetMatches_Func)Hc4_MatchFinder_GetMatches; + vTable->Skip = (Mf_Skip_Func)Hc4_MatchFinder_Skip; + } + /* + else + { + vTable->GetMatches = (Mf_GetMatches_Func)Hc5_MatchFinder_GetMatches; + vTable->Skip = (Mf_Skip_Func)Hc5_MatchFinder_Skip; + } + */ + } + else if (p->numHashBytes == 2) + { + vTable->GetMatches = (Mf_GetMatches_Func)Bt2_MatchFinder_GetMatches; + vTable->Skip = (Mf_Skip_Func)Bt2_MatchFinder_Skip; + } + else if (p->numHashBytes == 3) + { + vTable->GetMatches = (Mf_GetMatches_Func)Bt3_MatchFinder_GetMatches; + vTable->Skip = (Mf_Skip_Func)Bt3_MatchFinder_Skip; + } + else /* if (p->numHashBytes == 4) */ + { + vTable->GetMatches = (Mf_GetMatches_Func)Bt4_MatchFinder_GetMatches; + vTable->Skip = (Mf_Skip_Func)Bt4_MatchFinder_Skip; + } + /* + else + { + vTable->GetMatches = (Mf_GetMatches_Func)Bt5_MatchFinder_GetMatches; + vTable->Skip = (Mf_Skip_Func)Bt5_MatchFinder_Skip; + } + */ +} diff --git a/core/deps/lzma/LzFind.h b/core/deps/lzma/LzFind.h new file mode 100644 index 000000000..2ff667377 --- /dev/null +++ b/core/deps/lzma/LzFind.h @@ -0,0 +1,117 @@ +/* LzFind.h -- Match finder for LZ algorithms +2015-10-15 : Igor Pavlov : Public domain */ + +#ifndef __LZ_FIND_H +#define __LZ_FIND_H + +#include "7zTypes.h" + +EXTERN_C_BEGIN + +typedef UInt32 CLzRef; + +typedef struct _CMatchFinder +{ + Byte *buffer; + UInt32 pos; + UInt32 posLimit; + UInt32 streamPos; + UInt32 lenLimit; + + UInt32 cyclicBufferPos; + UInt32 cyclicBufferSize; /* it must be = (historySize + 1) */ + + Byte streamEndWasReached; + Byte btMode; + Byte bigHash; + Byte directInput; + + UInt32 matchMaxLen; + CLzRef *hash; + CLzRef *son; + UInt32 hashMask; + UInt32 cutValue; + + Byte *bufferBase; + ISeqInStream *stream; + + UInt32 blockSize; + UInt32 keepSizeBefore; + UInt32 keepSizeAfter; + + UInt32 numHashBytes; + size_t directInputRem; + UInt32 historySize; + UInt32 fixedHashSize; + UInt32 hashSizeSum; + SRes result; + UInt32 crc[256]; + size_t numRefs; +} CMatchFinder; + +#define Inline_MatchFinder_GetPointerToCurrentPos(p) ((p)->buffer) + +#define Inline_MatchFinder_GetNumAvailableBytes(p) ((p)->streamPos - (p)->pos) + +#define Inline_MatchFinder_IsFinishedOK(p) \ + ((p)->streamEndWasReached \ + && (p)->streamPos == (p)->pos \ + && (!(p)->directInput || (p)->directInputRem == 0)) + +int MatchFinder_NeedMove(CMatchFinder *p); +Byte *MatchFinder_GetPointerToCurrentPos(CMatchFinder *p); +void MatchFinder_MoveBlock(CMatchFinder *p); +void MatchFinder_ReadIfRequired(CMatchFinder *p); + +void MatchFinder_Construct(CMatchFinder *p); + +/* Conditions: + historySize <= 3 GB + keepAddBufferBefore + matchMaxLen + keepAddBufferAfter < 511MB +*/ +int MatchFinder_Create(CMatchFinder *p, UInt32 historySize, + UInt32 keepAddBufferBefore, UInt32 matchMaxLen, UInt32 keepAddBufferAfter, + ISzAlloc *alloc); +void MatchFinder_Free(CMatchFinder *p, ISzAlloc *alloc); +void MatchFinder_Normalize3(UInt32 subValue, CLzRef *items, size_t numItems); +void MatchFinder_ReduceOffsets(CMatchFinder *p, UInt32 subValue); + +UInt32 * GetMatchesSpec1(UInt32 lenLimit, UInt32 curMatch, UInt32 pos, const Byte *buffer, CLzRef *son, + UInt32 _cyclicBufferPos, UInt32 _cyclicBufferSize, UInt32 _cutValue, + UInt32 *distances, UInt32 maxLen); + +/* +Conditions: + Mf_GetNumAvailableBytes_Func must be called before each Mf_GetMatchLen_Func. + Mf_GetPointerToCurrentPos_Func's result must be used only before any other function +*/ + +typedef void (*Mf_Init_Func)(void *object); +typedef UInt32 (*Mf_GetNumAvailableBytes_Func)(void *object); +typedef const Byte * (*Mf_GetPointerToCurrentPos_Func)(void *object); +typedef UInt32 (*Mf_GetMatches_Func)(void *object, UInt32 *distances); +typedef void (*Mf_Skip_Func)(void *object, UInt32); + +typedef struct _IMatchFinder +{ + Mf_Init_Func Init; + Mf_GetNumAvailableBytes_Func GetNumAvailableBytes; + Mf_GetPointerToCurrentPos_Func GetPointerToCurrentPos; + Mf_GetMatches_Func GetMatches; + Mf_Skip_Func Skip; +} IMatchFinder; + +void MatchFinder_CreateVTable(CMatchFinder *p, IMatchFinder *vTable); + +void MatchFinder_Init_2(CMatchFinder *p, int readData); +void MatchFinder_Init(CMatchFinder *p); + +UInt32 Bt3Zip_MatchFinder_GetMatches(CMatchFinder *p, UInt32 *distances); +UInt32 Hc3Zip_MatchFinder_GetMatches(CMatchFinder *p, UInt32 *distances); + +void Bt3Zip_MatchFinder_Skip(CMatchFinder *p, UInt32 num); +void Hc3Zip_MatchFinder_Skip(CMatchFinder *p, UInt32 num); + +EXTERN_C_END + +#endif diff --git a/core/deps/lzma/LzHash.h b/core/deps/lzma/LzHash.h new file mode 100644 index 000000000..219144407 --- /dev/null +++ b/core/deps/lzma/LzHash.h @@ -0,0 +1,57 @@ +/* LzHash.h -- HASH functions for LZ algorithms +2015-04-12 : Igor Pavlov : Public domain */ + +#ifndef __LZ_HASH_H +#define __LZ_HASH_H + +#define kHash2Size (1 << 10) +#define kHash3Size (1 << 16) +#define kHash4Size (1 << 20) + +#define kFix3HashSize (kHash2Size) +#define kFix4HashSize (kHash2Size + kHash3Size) +#define kFix5HashSize (kHash2Size + kHash3Size + kHash4Size) + +#define HASH2_CALC hv = cur[0] | ((UInt32)cur[1] << 8); + +#define HASH3_CALC { \ + UInt32 temp = p->crc[cur[0]] ^ cur[1]; \ + h2 = temp & (kHash2Size - 1); \ + hv = (temp ^ ((UInt32)cur[2] << 8)) & p->hashMask; } + +#define HASH4_CALC { \ + UInt32 temp = p->crc[cur[0]] ^ cur[1]; \ + h2 = temp & (kHash2Size - 1); \ + temp ^= ((UInt32)cur[2] << 8); \ + h3 = temp & (kHash3Size - 1); \ + hv = (temp ^ (p->crc[cur[3]] << 5)) & p->hashMask; } + +#define HASH5_CALC { \ + UInt32 temp = p->crc[cur[0]] ^ cur[1]; \ + h2 = temp & (kHash2Size - 1); \ + temp ^= ((UInt32)cur[2] << 8); \ + h3 = temp & (kHash3Size - 1); \ + temp ^= (p->crc[cur[3]] << 5); \ + h4 = temp & (kHash4Size - 1); \ + hv = (temp ^ (p->crc[cur[4]] << 3)) & p->hashMask; } + +/* #define HASH_ZIP_CALC hv = ((cur[0] | ((UInt32)cur[1] << 8)) ^ p->crc[cur[2]]) & 0xFFFF; */ +#define HASH_ZIP_CALC hv = ((cur[2] | ((UInt32)cur[0] << 8)) ^ p->crc[cur[1]]) & 0xFFFF; + + +#define MT_HASH2_CALC \ + h2 = (p->crc[cur[0]] ^ cur[1]) & (kHash2Size - 1); + +#define MT_HASH3_CALC { \ + UInt32 temp = p->crc[cur[0]] ^ cur[1]; \ + h2 = temp & (kHash2Size - 1); \ + h3 = (temp ^ ((UInt32)cur[2] << 8)) & (kHash3Size - 1); } + +#define MT_HASH4_CALC { \ + UInt32 temp = p->crc[cur[0]] ^ cur[1]; \ + h2 = temp & (kHash2Size - 1); \ + temp ^= ((UInt32)cur[2] << 8); \ + h3 = temp & (kHash3Size - 1); \ + h4 = (temp ^ (p->crc[cur[3]] << 5)) & (kHash4Size - 1); } + +#endif diff --git a/core/deps/lzma/Lzma86.h b/core/deps/lzma/Lzma86.h new file mode 100644 index 000000000..83057e598 --- /dev/null +++ b/core/deps/lzma/Lzma86.h @@ -0,0 +1,111 @@ +/* Lzma86.h -- LZMA + x86 (BCJ) Filter +2013-01-18 : Igor Pavlov : Public domain */ + +#ifndef __LZMA86_H +#define __LZMA86_H + +#include "7zTypes.h" + +EXTERN_C_BEGIN + +#define LZMA86_SIZE_OFFSET (1 + 5) +#define LZMA86_HEADER_SIZE (LZMA86_SIZE_OFFSET + 8) + +/* +It's an example for LZMA + x86 Filter use. +You can use .lzma86 extension, if you write that stream to file. +.lzma86 header adds one additional byte to standard .lzma header. +.lzma86 header (14 bytes): + Offset Size Description + 0 1 = 0 - no filter, pure LZMA + = 1 - x86 filter + LZMA + 1 1 lc, lp and pb in encoded form + 2 4 dictSize (little endian) + 6 8 uncompressed size (little endian) + + +Lzma86_Encode +------------- +level - compression level: 0 <= level <= 9, the default value for "level" is 5. + +dictSize - The dictionary size in bytes. The maximum value is + 128 MB = (1 << 27) bytes for 32-bit version + 1 GB = (1 << 30) bytes for 64-bit version + The default value is 16 MB = (1 << 24) bytes, for level = 5. + It's recommended to use the dictionary that is larger than 4 KB and + that can be calculated as (1 << N) or (3 << N) sizes. + For better compression ratio dictSize must be >= inSize. + +filterMode: + SZ_FILTER_NO - no Filter + SZ_FILTER_YES - x86 Filter + SZ_FILTER_AUTO - it tries both alternatives to select best. + Encoder will use 2 or 3 passes: + 2 passes when FILTER_NO provides better compression. + 3 passes when FILTER_YES provides better compression. + +Lzma86Encode allocates Data with MyAlloc functions. +RAM Requirements for compressing: + RamSize = dictionarySize * 11.5 + 6MB + FilterBlockSize + filterMode FilterBlockSize + SZ_FILTER_NO 0 + SZ_FILTER_YES inSize + SZ_FILTER_AUTO inSize + + +Return code: + SZ_OK - OK + SZ_ERROR_MEM - Memory allocation error + SZ_ERROR_PARAM - Incorrect paramater + SZ_ERROR_OUTPUT_EOF - output buffer overflow + SZ_ERROR_THREAD - errors in multithreading functions (only for Mt version) +*/ + +enum ESzFilterMode +{ + SZ_FILTER_NO, + SZ_FILTER_YES, + SZ_FILTER_AUTO +}; + +SRes Lzma86_Encode(Byte *dest, size_t *destLen, const Byte *src, size_t srcLen, + int level, UInt32 dictSize, int filterMode); + + +/* +Lzma86_GetUnpackSize: + In: + src - input data + srcLen - input data size + Out: + unpackSize - size of uncompressed stream + Return code: + SZ_OK - OK + SZ_ERROR_INPUT_EOF - Error in headers +*/ + +SRes Lzma86_GetUnpackSize(const Byte *src, SizeT srcLen, UInt64 *unpackSize); + +/* +Lzma86_Decode: + In: + dest - output data + destLen - output data size + src - input data + srcLen - input data size + Out: + destLen - processed output size + srcLen - processed input size + Return code: + SZ_OK - OK + SZ_ERROR_DATA - Data error + SZ_ERROR_MEM - Memory allocation error + SZ_ERROR_UNSUPPORTED - unsupported file + SZ_ERROR_INPUT_EOF - it needs more bytes in input buffer +*/ + +SRes Lzma86_Decode(Byte *dest, SizeT *destLen, const Byte *src, SizeT *srcLen); + +EXTERN_C_END + +#endif diff --git a/core/deps/lzma/Lzma86Dec.c b/core/deps/lzma/Lzma86Dec.c new file mode 100644 index 000000000..20ac5e7a9 --- /dev/null +++ b/core/deps/lzma/Lzma86Dec.c @@ -0,0 +1,54 @@ +/* Lzma86Dec.c -- LZMA + x86 (BCJ) Filter Decoder +2016-05-16 : Igor Pavlov : Public domain */ + +#include "Precomp.h" + +#include "Lzma86.h" + +#include "Alloc.h" +#include "Bra.h" +#include "LzmaDec.h" + +SRes Lzma86_GetUnpackSize(const Byte *src, SizeT srcLen, UInt64 *unpackSize) +{ + unsigned i; + if (srcLen < LZMA86_HEADER_SIZE) + return SZ_ERROR_INPUT_EOF; + *unpackSize = 0; + for (i = 0; i < sizeof(UInt64); i++) + *unpackSize += ((UInt64)src[LZMA86_SIZE_OFFSET + i]) << (8 * i); + return SZ_OK; +} + +SRes Lzma86_Decode(Byte *dest, SizeT *destLen, const Byte *src, SizeT *srcLen) +{ + SRes res; + int useFilter; + SizeT inSizePure; + ELzmaStatus status; + + if (*srcLen < LZMA86_HEADER_SIZE) + return SZ_ERROR_INPUT_EOF; + + useFilter = src[0]; + + if (useFilter > 1) + { + *destLen = 0; + return SZ_ERROR_UNSUPPORTED; + } + + inSizePure = *srcLen - LZMA86_HEADER_SIZE; + res = LzmaDecode(dest, destLen, src + LZMA86_HEADER_SIZE, &inSizePure, + src + 1, LZMA_PROPS_SIZE, LZMA_FINISH_ANY, &status, &g_Alloc); + *srcLen = inSizePure + LZMA86_HEADER_SIZE; + if (res != SZ_OK) + return res; + if (useFilter == 1) + { + UInt32 x86State; + x86_Convert_Init(x86State); + x86_Convert(dest, *destLen, 0, &x86State, 0); + } + return SZ_OK; +} diff --git a/core/deps/lzma/Lzma86Enc.c b/core/deps/lzma/Lzma86Enc.c new file mode 100644 index 000000000..ee59fb7d7 --- /dev/null +++ b/core/deps/lzma/Lzma86Enc.c @@ -0,0 +1,106 @@ +/* Lzma86Enc.c -- LZMA + x86 (BCJ) Filter Encoder +2016-05-16 : Igor Pavlov : Public domain */ + +#include "Precomp.h" + +#include + +#include "Lzma86.h" + +#include "Alloc.h" +#include "Bra.h" +#include "LzmaEnc.h" + +#define SZE_OUT_OVERFLOW SZE_DATA_ERROR + +int Lzma86_Encode(Byte *dest, size_t *destLen, const Byte *src, size_t srcLen, + int level, UInt32 dictSize, int filterMode) +{ + size_t outSize2 = *destLen; + Byte *filteredStream; + Bool useFilter; + int mainResult = SZ_ERROR_OUTPUT_EOF; + CLzmaEncProps props; + LzmaEncProps_Init(&props); + props.level = level; + props.dictSize = dictSize; + + *destLen = 0; + if (outSize2 < LZMA86_HEADER_SIZE) + return SZ_ERROR_OUTPUT_EOF; + + { + int i; + UInt64 t = srcLen; + for (i = 0; i < 8; i++, t >>= 8) + dest[LZMA86_SIZE_OFFSET + i] = (Byte)t; + } + + filteredStream = 0; + useFilter = (filterMode != SZ_FILTER_NO); + if (useFilter) + { + if (srcLen != 0) + { + filteredStream = (Byte *)MyAlloc(srcLen); + if (filteredStream == 0) + return SZ_ERROR_MEM; + memcpy(filteredStream, src, srcLen); + } + { + UInt32 x86State; + x86_Convert_Init(x86State); + x86_Convert(filteredStream, srcLen, 0, &x86State, 1); + } + } + + { + size_t minSize = 0; + Bool bestIsFiltered = False; + + /* passes for SZ_FILTER_AUTO: + 0 - BCJ + LZMA + 1 - LZMA + 2 - BCJ + LZMA agaian, if pass 0 (BCJ + LZMA) is better. + */ + int numPasses = (filterMode == SZ_FILTER_AUTO) ? 3 : 1; + + int i; + for (i = 0; i < numPasses; i++) + { + size_t outSizeProcessed = outSize2 - LZMA86_HEADER_SIZE; + size_t outPropsSize = 5; + SRes curRes; + Bool curModeIsFiltered = (numPasses > 1 && i == numPasses - 1); + if (curModeIsFiltered && !bestIsFiltered) + break; + if (useFilter && i == 0) + curModeIsFiltered = True; + + curRes = LzmaEncode(dest + LZMA86_HEADER_SIZE, &outSizeProcessed, + curModeIsFiltered ? filteredStream : src, srcLen, + &props, dest + 1, &outPropsSize, 0, + NULL, &g_Alloc, &g_Alloc); + + if (curRes != SZ_ERROR_OUTPUT_EOF) + { + if (curRes != SZ_OK) + { + mainResult = curRes; + break; + } + if (outSizeProcessed <= minSize || mainResult != SZ_OK) + { + minSize = outSizeProcessed; + bestIsFiltered = curModeIsFiltered; + mainResult = SZ_OK; + } + } + } + dest[0] = (Byte)(bestIsFiltered ? 1 : 0); + *destLen = LZMA86_HEADER_SIZE + minSize; + } + if (useFilter) + MyFree(filteredStream); + return mainResult; +} diff --git a/core/deps/lzma/LzmaDec.c b/core/deps/lzma/LzmaDec.c new file mode 100644 index 000000000..64f1164f3 --- /dev/null +++ b/core/deps/lzma/LzmaDec.c @@ -0,0 +1,1100 @@ +/* LzmaDec.c -- LZMA Decoder +2016-05-16 : Igor Pavlov : Public domain */ + +#include "Precomp.h" + +#include "LzmaDec.h" + +#include + +#define kNumTopBits 24 +#define kTopValue ((UInt32)1 << kNumTopBits) + +#define kNumBitModelTotalBits 11 +#define kBitModelTotal (1 << kNumBitModelTotalBits) +#define kNumMoveBits 5 + +#define RC_INIT_SIZE 5 + +#define NORMALIZE if (range < kTopValue) { range <<= 8; code = (code << 8) | (*buf++); } + +#define IF_BIT_0(p) ttt = *(p); NORMALIZE; bound = (range >> kNumBitModelTotalBits) * ttt; if (code < bound) +#define UPDATE_0(p) range = bound; *(p) = (CLzmaProb)(ttt + ((kBitModelTotal - ttt) >> kNumMoveBits)); +#define UPDATE_1(p) range -= bound; code -= bound; *(p) = (CLzmaProb)(ttt - (ttt >> kNumMoveBits)); +#define GET_BIT2(p, i, A0, A1) IF_BIT_0(p) \ + { UPDATE_0(p); i = (i + i); A0; } else \ + { UPDATE_1(p); i = (i + i) + 1; A1; } +#define GET_BIT(p, i) GET_BIT2(p, i, ; , ;) + +#define TREE_GET_BIT(probs, i) { GET_BIT((probs + i), i); } +#define TREE_DECODE(probs, limit, i) \ + { i = 1; do { TREE_GET_BIT(probs, i); } while (i < limit); i -= limit; } + +/* #define _LZMA_SIZE_OPT */ + +#ifdef _LZMA_SIZE_OPT +#define TREE_6_DECODE(probs, i) TREE_DECODE(probs, (1 << 6), i) +#else +#define TREE_6_DECODE(probs, i) \ + { i = 1; \ + TREE_GET_BIT(probs, i); \ + TREE_GET_BIT(probs, i); \ + TREE_GET_BIT(probs, i); \ + TREE_GET_BIT(probs, i); \ + TREE_GET_BIT(probs, i); \ + TREE_GET_BIT(probs, i); \ + i -= 0x40; } +#endif + +#define NORMAL_LITER_DEC GET_BIT(prob + symbol, symbol) +#define MATCHED_LITER_DEC \ + matchByte <<= 1; \ + bit = (matchByte & offs); \ + probLit = prob + offs + bit + symbol; \ + GET_BIT2(probLit, symbol, offs &= ~bit, offs &= bit) + +#define NORMALIZE_CHECK if (range < kTopValue) { if (buf >= bufLimit) return DUMMY_ERROR; range <<= 8; code = (code << 8) | (*buf++); } + +#define IF_BIT_0_CHECK(p) ttt = *(p); NORMALIZE_CHECK; bound = (range >> kNumBitModelTotalBits) * ttt; if (code < bound) +#define UPDATE_0_CHECK range = bound; +#define UPDATE_1_CHECK range -= bound; code -= bound; +#define GET_BIT2_CHECK(p, i, A0, A1) IF_BIT_0_CHECK(p) \ + { UPDATE_0_CHECK; i = (i + i); A0; } else \ + { UPDATE_1_CHECK; i = (i + i) + 1; A1; } +#define GET_BIT_CHECK(p, i) GET_BIT2_CHECK(p, i, ; , ;) +#define TREE_DECODE_CHECK(probs, limit, i) \ + { i = 1; do { GET_BIT_CHECK(probs + i, i) } while (i < limit); i -= limit; } + + +#define kNumPosBitsMax 4 +#define kNumPosStatesMax (1 << kNumPosBitsMax) + +#define kLenNumLowBits 3 +#define kLenNumLowSymbols (1 << kLenNumLowBits) +#define kLenNumMidBits 3 +#define kLenNumMidSymbols (1 << kLenNumMidBits) +#define kLenNumHighBits 8 +#define kLenNumHighSymbols (1 << kLenNumHighBits) + +#define LenChoice 0 +#define LenChoice2 (LenChoice + 1) +#define LenLow (LenChoice2 + 1) +#define LenMid (LenLow + (kNumPosStatesMax << kLenNumLowBits)) +#define LenHigh (LenMid + (kNumPosStatesMax << kLenNumMidBits)) +#define kNumLenProbs (LenHigh + kLenNumHighSymbols) + + +#define kNumStates 12 +#define kNumLitStates 7 + +#define kStartPosModelIndex 4 +#define kEndPosModelIndex 14 +#define kNumFullDistances (1 << (kEndPosModelIndex >> 1)) + +#define kNumPosSlotBits 6 +#define kNumLenToPosStates 4 + +#define kNumAlignBits 4 +#define kAlignTableSize (1 << kNumAlignBits) + +#define kMatchMinLen 2 +#define kMatchSpecLenStart (kMatchMinLen + kLenNumLowSymbols + kLenNumMidSymbols + kLenNumHighSymbols) + +#define IsMatch 0 +#define IsRep (IsMatch + (kNumStates << kNumPosBitsMax)) +#define IsRepG0 (IsRep + kNumStates) +#define IsRepG1 (IsRepG0 + kNumStates) +#define IsRepG2 (IsRepG1 + kNumStates) +#define IsRep0Long (IsRepG2 + kNumStates) +#define PosSlot (IsRep0Long + (kNumStates << kNumPosBitsMax)) +#define SpecPos (PosSlot + (kNumLenToPosStates << kNumPosSlotBits)) +#define Align (SpecPos + kNumFullDistances - kEndPosModelIndex) +#define LenCoder (Align + kAlignTableSize) +#define RepLenCoder (LenCoder + kNumLenProbs) +#define Literal (RepLenCoder + kNumLenProbs) + +#define LZMA_BASE_SIZE 1846 +#define LZMA_LIT_SIZE 0x300 + +#if Literal != LZMA_BASE_SIZE +StopCompilingDueBUG +#endif + +#define LzmaProps_GetNumProbs(p) (Literal + ((UInt32)LZMA_LIT_SIZE << ((p)->lc + (p)->lp))) + +#define LZMA_DIC_MIN (1 << 12) + +/* First LZMA-symbol is always decoded. +And it decodes new LZMA-symbols while (buf < bufLimit), but "buf" is without last normalization +Out: + Result: + SZ_OK - OK + SZ_ERROR_DATA - Error + p->remainLen: + < kMatchSpecLenStart : normal remain + = kMatchSpecLenStart : finished + = kMatchSpecLenStart + 1 : Flush marker (unused now) + = kMatchSpecLenStart + 2 : State Init Marker (unused now) +*/ + +static int MY_FAST_CALL LzmaDec_DecodeReal(CLzmaDec *p, SizeT limit, const Byte *bufLimit) +{ + CLzmaProb *probs = p->probs; + + unsigned state = p->state; + UInt32 rep0 = p->reps[0], rep1 = p->reps[1], rep2 = p->reps[2], rep3 = p->reps[3]; + unsigned pbMask = ((unsigned)1 << (p->prop.pb)) - 1; + unsigned lpMask = ((unsigned)1 << (p->prop.lp)) - 1; + unsigned lc = p->prop.lc; + + Byte *dic = p->dic; + SizeT dicBufSize = p->dicBufSize; + SizeT dicPos = p->dicPos; + + UInt32 processedPos = p->processedPos; + UInt32 checkDicSize = p->checkDicSize; + unsigned len = 0; + + const Byte *buf = p->buf; + UInt32 range = p->range; + UInt32 code = p->code; + + do + { + CLzmaProb *prob; + UInt32 bound; + unsigned ttt; + unsigned posState = processedPos & pbMask; + + prob = probs + IsMatch + (state << kNumPosBitsMax) + posState; + IF_BIT_0(prob) + { + unsigned symbol; + UPDATE_0(prob); + prob = probs + Literal; + if (processedPos != 0 || checkDicSize != 0) + prob += ((UInt32)LZMA_LIT_SIZE * (((processedPos & lpMask) << lc) + + (dic[(dicPos == 0 ? dicBufSize : dicPos) - 1] >> (8 - lc)))); + processedPos++; + + if (state < kNumLitStates) + { + state -= (state < 4) ? state : 3; + symbol = 1; + #ifdef _LZMA_SIZE_OPT + do { NORMAL_LITER_DEC } while (symbol < 0x100); + #else + NORMAL_LITER_DEC + NORMAL_LITER_DEC + NORMAL_LITER_DEC + NORMAL_LITER_DEC + NORMAL_LITER_DEC + NORMAL_LITER_DEC + NORMAL_LITER_DEC + NORMAL_LITER_DEC + #endif + } + else + { + unsigned matchByte = dic[dicPos - rep0 + (dicPos < rep0 ? dicBufSize : 0)]; + unsigned offs = 0x100; + state -= (state < 10) ? 3 : 6; + symbol = 1; + #ifdef _LZMA_SIZE_OPT + do + { + unsigned bit; + CLzmaProb *probLit; + MATCHED_LITER_DEC + } + while (symbol < 0x100); + #else + { + unsigned bit; + CLzmaProb *probLit; + MATCHED_LITER_DEC + MATCHED_LITER_DEC + MATCHED_LITER_DEC + MATCHED_LITER_DEC + MATCHED_LITER_DEC + MATCHED_LITER_DEC + MATCHED_LITER_DEC + MATCHED_LITER_DEC + } + #endif + } + + dic[dicPos++] = (Byte)symbol; + continue; + } + + { + UPDATE_1(prob); + prob = probs + IsRep + state; + IF_BIT_0(prob) + { + UPDATE_0(prob); + state += kNumStates; + prob = probs + LenCoder; + } + else + { + UPDATE_1(prob); + if (checkDicSize == 0 && processedPos == 0) + return SZ_ERROR_DATA; + prob = probs + IsRepG0 + state; + IF_BIT_0(prob) + { + UPDATE_0(prob); + prob = probs + IsRep0Long + (state << kNumPosBitsMax) + posState; + IF_BIT_0(prob) + { + UPDATE_0(prob); + dic[dicPos] = dic[dicPos - rep0 + (dicPos < rep0 ? dicBufSize : 0)]; + dicPos++; + processedPos++; + state = state < kNumLitStates ? 9 : 11; + continue; + } + UPDATE_1(prob); + } + else + { + UInt32 distance; + UPDATE_1(prob); + prob = probs + IsRepG1 + state; + IF_BIT_0(prob) + { + UPDATE_0(prob); + distance = rep1; + } + else + { + UPDATE_1(prob); + prob = probs + IsRepG2 + state; + IF_BIT_0(prob) + { + UPDATE_0(prob); + distance = rep2; + } + else + { + UPDATE_1(prob); + distance = rep3; + rep3 = rep2; + } + rep2 = rep1; + } + rep1 = rep0; + rep0 = distance; + } + state = state < kNumLitStates ? 8 : 11; + prob = probs + RepLenCoder; + } + + #ifdef _LZMA_SIZE_OPT + { + unsigned lim, offset; + CLzmaProb *probLen = prob + LenChoice; + IF_BIT_0(probLen) + { + UPDATE_0(probLen); + probLen = prob + LenLow + (posState << kLenNumLowBits); + offset = 0; + lim = (1 << kLenNumLowBits); + } + else + { + UPDATE_1(probLen); + probLen = prob + LenChoice2; + IF_BIT_0(probLen) + { + UPDATE_0(probLen); + probLen = prob + LenMid + (posState << kLenNumMidBits); + offset = kLenNumLowSymbols; + lim = (1 << kLenNumMidBits); + } + else + { + UPDATE_1(probLen); + probLen = prob + LenHigh; + offset = kLenNumLowSymbols + kLenNumMidSymbols; + lim = (1 << kLenNumHighBits); + } + } + TREE_DECODE(probLen, lim, len); + len += offset; + } + #else + { + CLzmaProb *probLen = prob + LenChoice; + IF_BIT_0(probLen) + { + UPDATE_0(probLen); + probLen = prob + LenLow + (posState << kLenNumLowBits); + len = 1; + TREE_GET_BIT(probLen, len); + TREE_GET_BIT(probLen, len); + TREE_GET_BIT(probLen, len); + len -= 8; + } + else + { + UPDATE_1(probLen); + probLen = prob + LenChoice2; + IF_BIT_0(probLen) + { + UPDATE_0(probLen); + probLen = prob + LenMid + (posState << kLenNumMidBits); + len = 1; + TREE_GET_BIT(probLen, len); + TREE_GET_BIT(probLen, len); + TREE_GET_BIT(probLen, len); + } + else + { + UPDATE_1(probLen); + probLen = prob + LenHigh; + TREE_DECODE(probLen, (1 << kLenNumHighBits), len); + len += kLenNumLowSymbols + kLenNumMidSymbols; + } + } + } + #endif + + if (state >= kNumStates) + { + UInt32 distance; + prob = probs + PosSlot + + ((len < kNumLenToPosStates ? len : kNumLenToPosStates - 1) << kNumPosSlotBits); + TREE_6_DECODE(prob, distance); + if (distance >= kStartPosModelIndex) + { + unsigned posSlot = (unsigned)distance; + unsigned numDirectBits = (unsigned)(((distance >> 1) - 1)); + distance = (2 | (distance & 1)); + if (posSlot < kEndPosModelIndex) + { + distance <<= numDirectBits; + prob = probs + SpecPos + distance - posSlot - 1; + { + UInt32 mask = 1; + unsigned i = 1; + do + { + GET_BIT2(prob + i, i, ; , distance |= mask); + mask <<= 1; + } + while (--numDirectBits != 0); + } + } + else + { + numDirectBits -= kNumAlignBits; + do + { + NORMALIZE + range >>= 1; + + { + UInt32 t; + code -= range; + t = (0 - ((UInt32)code >> 31)); /* (UInt32)((Int32)code >> 31) */ + distance = (distance << 1) + (t + 1); + code += range & t; + } + /* + distance <<= 1; + if (code >= range) + { + code -= range; + distance |= 1; + } + */ + } + while (--numDirectBits != 0); + prob = probs + Align; + distance <<= kNumAlignBits; + { + unsigned i = 1; + GET_BIT2(prob + i, i, ; , distance |= 1); + GET_BIT2(prob + i, i, ; , distance |= 2); + GET_BIT2(prob + i, i, ; , distance |= 4); + GET_BIT2(prob + i, i, ; , distance |= 8); + } + if (distance == (UInt32)0xFFFFFFFF) + { + len += kMatchSpecLenStart; + state -= kNumStates; + break; + } + } + } + + rep3 = rep2; + rep2 = rep1; + rep1 = rep0; + rep0 = distance + 1; + if (checkDicSize == 0) + { + if (distance >= processedPos) + { + p->dicPos = dicPos; + return SZ_ERROR_DATA; + } + } + else if (distance >= checkDicSize) + { + p->dicPos = dicPos; + return SZ_ERROR_DATA; + } + state = (state < kNumStates + kNumLitStates) ? kNumLitStates : kNumLitStates + 3; + } + + len += kMatchMinLen; + + { + SizeT rem; + unsigned curLen; + SizeT pos; + + if ((rem = limit - dicPos) == 0) + { + p->dicPos = dicPos; + return SZ_ERROR_DATA; + } + + curLen = ((rem < len) ? (unsigned)rem : len); + pos = dicPos - rep0 + (dicPos < rep0 ? dicBufSize : 0); + + processedPos += curLen; + + len -= curLen; + if (curLen <= dicBufSize - pos) + { + Byte *dest = dic + dicPos; + ptrdiff_t src = (ptrdiff_t)pos - (ptrdiff_t)dicPos; + const Byte *lim = dest + curLen; + dicPos += curLen; + do + *(dest) = (Byte)*(dest + src); + while (++dest != lim); + } + else + { + do + { + dic[dicPos++] = dic[pos]; + if (++pos == dicBufSize) + pos = 0; + } + while (--curLen != 0); + } + } + } + } + while (dicPos < limit && buf < bufLimit); + + NORMALIZE; + + p->buf = buf; + p->range = range; + p->code = code; + p->remainLen = len; + p->dicPos = dicPos; + p->processedPos = processedPos; + p->reps[0] = rep0; + p->reps[1] = rep1; + p->reps[2] = rep2; + p->reps[3] = rep3; + p->state = state; + + return SZ_OK; +} + +static void MY_FAST_CALL LzmaDec_WriteRem(CLzmaDec *p, SizeT limit) +{ + if (p->remainLen != 0 && p->remainLen < kMatchSpecLenStart) + { + Byte *dic = p->dic; + SizeT dicPos = p->dicPos; + SizeT dicBufSize = p->dicBufSize; + unsigned len = p->remainLen; + SizeT rep0 = p->reps[0]; /* we use SizeT to avoid the BUG of VC14 for AMD64 */ + SizeT rem = limit - dicPos; + if (rem < len) + len = (unsigned)(rem); + + if (p->checkDicSize == 0 && p->prop.dicSize - p->processedPos <= len) + p->checkDicSize = p->prop.dicSize; + + p->processedPos += len; + p->remainLen -= len; + while (len != 0) + { + len--; + dic[dicPos] = dic[dicPos - rep0 + (dicPos < rep0 ? dicBufSize : 0)]; + dicPos++; + } + p->dicPos = dicPos; + } +} + +static int MY_FAST_CALL LzmaDec_DecodeReal2(CLzmaDec *p, SizeT limit, const Byte *bufLimit) +{ + do + { + SizeT limit2 = limit; + if (p->checkDicSize == 0) + { + UInt32 rem = p->prop.dicSize - p->processedPos; + if (limit - p->dicPos > rem) + limit2 = p->dicPos + rem; + } + + RINOK(LzmaDec_DecodeReal(p, limit2, bufLimit)); + + if (p->checkDicSize == 0 && p->processedPos >= p->prop.dicSize) + p->checkDicSize = p->prop.dicSize; + + LzmaDec_WriteRem(p, limit); + } + while (p->dicPos < limit && p->buf < bufLimit && p->remainLen < kMatchSpecLenStart); + + if (p->remainLen > kMatchSpecLenStart) + p->remainLen = kMatchSpecLenStart; + + return 0; +} + +typedef enum +{ + DUMMY_ERROR, /* unexpected end of input stream */ + DUMMY_LIT, + DUMMY_MATCH, + DUMMY_REP +} ELzmaDummy; + +static ELzmaDummy LzmaDec_TryDummy(const CLzmaDec *p, const Byte *buf, SizeT inSize) +{ + UInt32 range = p->range; + UInt32 code = p->code; + const Byte *bufLimit = buf + inSize; + const CLzmaProb *probs = p->probs; + unsigned state = p->state; + ELzmaDummy res; + + { + const CLzmaProb *prob; + UInt32 bound; + unsigned ttt; + unsigned posState = (p->processedPos) & ((1 << p->prop.pb) - 1); + + prob = probs + IsMatch + (state << kNumPosBitsMax) + posState; + IF_BIT_0_CHECK(prob) + { + UPDATE_0_CHECK + + /* if (bufLimit - buf >= 7) return DUMMY_LIT; */ + + prob = probs + Literal; + if (p->checkDicSize != 0 || p->processedPos != 0) + prob += ((UInt32)LZMA_LIT_SIZE * + ((((p->processedPos) & ((1 << (p->prop.lp)) - 1)) << p->prop.lc) + + (p->dic[(p->dicPos == 0 ? p->dicBufSize : p->dicPos) - 1] >> (8 - p->prop.lc)))); + + if (state < kNumLitStates) + { + unsigned symbol = 1; + do { GET_BIT_CHECK(prob + symbol, symbol) } while (symbol < 0x100); + } + else + { + unsigned matchByte = p->dic[p->dicPos - p->reps[0] + + (p->dicPos < p->reps[0] ? p->dicBufSize : 0)]; + unsigned offs = 0x100; + unsigned symbol = 1; + do + { + unsigned bit; + const CLzmaProb *probLit; + matchByte <<= 1; + bit = (matchByte & offs); + probLit = prob + offs + bit + symbol; + GET_BIT2_CHECK(probLit, symbol, offs &= ~bit, offs &= bit) + } + while (symbol < 0x100); + } + res = DUMMY_LIT; + } + else + { + unsigned len; + UPDATE_1_CHECK; + + prob = probs + IsRep + state; + IF_BIT_0_CHECK(prob) + { + UPDATE_0_CHECK; + state = 0; + prob = probs + LenCoder; + res = DUMMY_MATCH; + } + else + { + UPDATE_1_CHECK; + res = DUMMY_REP; + prob = probs + IsRepG0 + state; + IF_BIT_0_CHECK(prob) + { + UPDATE_0_CHECK; + prob = probs + IsRep0Long + (state << kNumPosBitsMax) + posState; + IF_BIT_0_CHECK(prob) + { + UPDATE_0_CHECK; + NORMALIZE_CHECK; + return DUMMY_REP; + } + else + { + UPDATE_1_CHECK; + } + } + else + { + UPDATE_1_CHECK; + prob = probs + IsRepG1 + state; + IF_BIT_0_CHECK(prob) + { + UPDATE_0_CHECK; + } + else + { + UPDATE_1_CHECK; + prob = probs + IsRepG2 + state; + IF_BIT_0_CHECK(prob) + { + UPDATE_0_CHECK; + } + else + { + UPDATE_1_CHECK; + } + } + } + state = kNumStates; + prob = probs + RepLenCoder; + } + { + unsigned limit, offset; + const CLzmaProb *probLen = prob + LenChoice; + IF_BIT_0_CHECK(probLen) + { + UPDATE_0_CHECK; + probLen = prob + LenLow + (posState << kLenNumLowBits); + offset = 0; + limit = 1 << kLenNumLowBits; + } + else + { + UPDATE_1_CHECK; + probLen = prob + LenChoice2; + IF_BIT_0_CHECK(probLen) + { + UPDATE_0_CHECK; + probLen = prob + LenMid + (posState << kLenNumMidBits); + offset = kLenNumLowSymbols; + limit = 1 << kLenNumMidBits; + } + else + { + UPDATE_1_CHECK; + probLen = prob + LenHigh; + offset = kLenNumLowSymbols + kLenNumMidSymbols; + limit = 1 << kLenNumHighBits; + } + } + TREE_DECODE_CHECK(probLen, limit, len); + len += offset; + } + + if (state < 4) + { + unsigned posSlot; + prob = probs + PosSlot + + ((len < kNumLenToPosStates ? len : kNumLenToPosStates - 1) << + kNumPosSlotBits); + TREE_DECODE_CHECK(prob, 1 << kNumPosSlotBits, posSlot); + if (posSlot >= kStartPosModelIndex) + { + unsigned numDirectBits = ((posSlot >> 1) - 1); + + /* if (bufLimit - buf >= 8) return DUMMY_MATCH; */ + + if (posSlot < kEndPosModelIndex) + { + prob = probs + SpecPos + ((2 | (posSlot & 1)) << numDirectBits) - posSlot - 1; + } + else + { + numDirectBits -= kNumAlignBits; + do + { + NORMALIZE_CHECK + range >>= 1; + code -= range & (((code - range) >> 31) - 1); + /* if (code >= range) code -= range; */ + } + while (--numDirectBits != 0); + prob = probs + Align; + numDirectBits = kNumAlignBits; + } + { + unsigned i = 1; + do + { + GET_BIT_CHECK(prob + i, i); + } + while (--numDirectBits != 0); + } + } + } + } + } + NORMALIZE_CHECK; + return res; +} + + +void LzmaDec_InitDicAndState(CLzmaDec *p, Bool initDic, Bool initState) +{ + p->needFlush = 1; + p->remainLen = 0; + p->tempBufSize = 0; + + if (initDic) + { + p->processedPos = 0; + p->checkDicSize = 0; + p->needInitState = 1; + } + if (initState) + p->needInitState = 1; +} + +void LzmaDec_Init(CLzmaDec *p) +{ + p->dicPos = 0; + LzmaDec_InitDicAndState(p, True, True); +} + +static void LzmaDec_InitStateReal(CLzmaDec *p) +{ + SizeT numProbs = LzmaProps_GetNumProbs(&p->prop); + SizeT i; + CLzmaProb *probs = p->probs; + for (i = 0; i < numProbs; i++) + probs[i] = kBitModelTotal >> 1; + p->reps[0] = p->reps[1] = p->reps[2] = p->reps[3] = 1; + p->state = 0; + p->needInitState = 0; +} + +SRes LzmaDec_DecodeToDic(CLzmaDec *p, SizeT dicLimit, const Byte *src, SizeT *srcLen, + ELzmaFinishMode finishMode, ELzmaStatus *status) +{ + SizeT inSize = *srcLen; + (*srcLen) = 0; + LzmaDec_WriteRem(p, dicLimit); + + *status = LZMA_STATUS_NOT_SPECIFIED; + + while (p->remainLen != kMatchSpecLenStart) + { + int checkEndMarkNow; + + if (p->needFlush) + { + for (; inSize > 0 && p->tempBufSize < RC_INIT_SIZE; (*srcLen)++, inSize--) + p->tempBuf[p->tempBufSize++] = *src++; + if (p->tempBufSize < RC_INIT_SIZE) + { + *status = LZMA_STATUS_NEEDS_MORE_INPUT; + return SZ_OK; + } + if (p->tempBuf[0] != 0) + return SZ_ERROR_DATA; + p->code = + ((UInt32)p->tempBuf[1] << 24) + | ((UInt32)p->tempBuf[2] << 16) + | ((UInt32)p->tempBuf[3] << 8) + | ((UInt32)p->tempBuf[4]); + p->range = 0xFFFFFFFF; + p->needFlush = 0; + p->tempBufSize = 0; + } + + checkEndMarkNow = 0; + if (p->dicPos >= dicLimit) + { + if (p->remainLen == 0 && p->code == 0) + { + *status = LZMA_STATUS_MAYBE_FINISHED_WITHOUT_MARK; + return SZ_OK; + } + if (finishMode == LZMA_FINISH_ANY) + { + *status = LZMA_STATUS_NOT_FINISHED; + return SZ_OK; + } + if (p->remainLen != 0) + { + *status = LZMA_STATUS_NOT_FINISHED; + return SZ_ERROR_DATA; + } + checkEndMarkNow = 1; + } + + if (p->needInitState) + LzmaDec_InitStateReal(p); + + if (p->tempBufSize == 0) + { + SizeT processed; + const Byte *bufLimit; + if (inSize < LZMA_REQUIRED_INPUT_MAX || checkEndMarkNow) + { + int dummyRes = LzmaDec_TryDummy(p, src, inSize); + if (dummyRes == DUMMY_ERROR) + { + memcpy(p->tempBuf, src, inSize); + p->tempBufSize = (unsigned)inSize; + (*srcLen) += inSize; + *status = LZMA_STATUS_NEEDS_MORE_INPUT; + return SZ_OK; + } + if (checkEndMarkNow && dummyRes != DUMMY_MATCH) + { + *status = LZMA_STATUS_NOT_FINISHED; + return SZ_ERROR_DATA; + } + bufLimit = src; + } + else + bufLimit = src + inSize - LZMA_REQUIRED_INPUT_MAX; + p->buf = src; + if (LzmaDec_DecodeReal2(p, dicLimit, bufLimit) != 0) + return SZ_ERROR_DATA; + processed = (SizeT)(p->buf - src); + (*srcLen) += processed; + src += processed; + inSize -= processed; + } + else + { + unsigned rem = p->tempBufSize, lookAhead = 0; + while (rem < LZMA_REQUIRED_INPUT_MAX && lookAhead < inSize) + p->tempBuf[rem++] = src[lookAhead++]; + p->tempBufSize = rem; + if (rem < LZMA_REQUIRED_INPUT_MAX || checkEndMarkNow) + { + int dummyRes = LzmaDec_TryDummy(p, p->tempBuf, rem); + if (dummyRes == DUMMY_ERROR) + { + (*srcLen) += lookAhead; + *status = LZMA_STATUS_NEEDS_MORE_INPUT; + return SZ_OK; + } + if (checkEndMarkNow && dummyRes != DUMMY_MATCH) + { + *status = LZMA_STATUS_NOT_FINISHED; + return SZ_ERROR_DATA; + } + } + p->buf = p->tempBuf; + if (LzmaDec_DecodeReal2(p, dicLimit, p->buf) != 0) + return SZ_ERROR_DATA; + + { + unsigned kkk = (unsigned)(p->buf - p->tempBuf); + if (rem < kkk) + return SZ_ERROR_FAIL; /* some internal error */ + rem -= kkk; + if (lookAhead < rem) + return SZ_ERROR_FAIL; /* some internal error */ + lookAhead -= rem; + } + (*srcLen) += lookAhead; + src += lookAhead; + inSize -= lookAhead; + p->tempBufSize = 0; + } + } + if (p->code == 0) + *status = LZMA_STATUS_FINISHED_WITH_MARK; + return (p->code == 0) ? SZ_OK : SZ_ERROR_DATA; +} + +SRes LzmaDec_DecodeToBuf(CLzmaDec *p, Byte *dest, SizeT *destLen, const Byte *src, SizeT *srcLen, ELzmaFinishMode finishMode, ELzmaStatus *status) +{ + SizeT outSize = *destLen; + SizeT inSize = *srcLen; + *srcLen = *destLen = 0; + for (;;) + { + SizeT inSizeCur = inSize, outSizeCur, dicPos; + ELzmaFinishMode curFinishMode; + SRes res; + if (p->dicPos == p->dicBufSize) + p->dicPos = 0; + dicPos = p->dicPos; + if (outSize > p->dicBufSize - dicPos) + { + outSizeCur = p->dicBufSize; + curFinishMode = LZMA_FINISH_ANY; + } + else + { + outSizeCur = dicPos + outSize; + curFinishMode = finishMode; + } + + res = LzmaDec_DecodeToDic(p, outSizeCur, src, &inSizeCur, curFinishMode, status); + src += inSizeCur; + inSize -= inSizeCur; + *srcLen += inSizeCur; + outSizeCur = p->dicPos - dicPos; + memcpy(dest, p->dic + dicPos, outSizeCur); + dest += outSizeCur; + outSize -= outSizeCur; + *destLen += outSizeCur; + if (res != 0) + return res; + if (outSizeCur == 0 || outSize == 0) + return SZ_OK; + } +} + +void LzmaDec_FreeProbs(CLzmaDec *p, ISzAlloc *alloc) +{ + alloc->Free(alloc, p->probs); + p->probs = NULL; +} + +static void LzmaDec_FreeDict(CLzmaDec *p, ISzAlloc *alloc) +{ + alloc->Free(alloc, p->dic); + p->dic = NULL; +} + +void LzmaDec_Free(CLzmaDec *p, ISzAlloc *alloc) +{ + LzmaDec_FreeProbs(p, alloc); + LzmaDec_FreeDict(p, alloc); +} + +SRes LzmaProps_Decode(CLzmaProps *p, const Byte *data, unsigned size) +{ + UInt32 dicSize; + Byte d; + + if (size < LZMA_PROPS_SIZE) + return SZ_ERROR_UNSUPPORTED; + else + dicSize = data[1] | ((UInt32)data[2] << 8) | ((UInt32)data[3] << 16) | ((UInt32)data[4] << 24); + + if (dicSize < LZMA_DIC_MIN) + dicSize = LZMA_DIC_MIN; + p->dicSize = dicSize; + + d = data[0]; + if (d >= (9 * 5 * 5)) + return SZ_ERROR_UNSUPPORTED; + + p->lc = d % 9; + d /= 9; + p->pb = d / 5; + p->lp = d % 5; + + return SZ_OK; +} + +static SRes LzmaDec_AllocateProbs2(CLzmaDec *p, const CLzmaProps *propNew, ISzAlloc *alloc) +{ + UInt32 numProbs = LzmaProps_GetNumProbs(propNew); + if (!p->probs || numProbs != p->numProbs) + { + LzmaDec_FreeProbs(p, alloc); + p->probs = (CLzmaProb *)alloc->Alloc(alloc, numProbs * sizeof(CLzmaProb)); + p->numProbs = numProbs; + if (!p->probs) + return SZ_ERROR_MEM; + } + return SZ_OK; +} + +SRes LzmaDec_AllocateProbs(CLzmaDec *p, const Byte *props, unsigned propsSize, ISzAlloc *alloc) +{ + CLzmaProps propNew; + RINOK(LzmaProps_Decode(&propNew, props, propsSize)); + RINOK(LzmaDec_AllocateProbs2(p, &propNew, alloc)); + p->prop = propNew; + return SZ_OK; +} + +SRes LzmaDec_Allocate(CLzmaDec *p, const Byte *props, unsigned propsSize, ISzAlloc *alloc) +{ + CLzmaProps propNew; + SizeT dicBufSize; + RINOK(LzmaProps_Decode(&propNew, props, propsSize)); + RINOK(LzmaDec_AllocateProbs2(p, &propNew, alloc)); + + { + UInt32 dictSize = propNew.dicSize; + SizeT mask = ((UInt32)1 << 12) - 1; + if (dictSize >= ((UInt32)1 << 30)) mask = ((UInt32)1 << 22) - 1; + else if (dictSize >= ((UInt32)1 << 22)) mask = ((UInt32)1 << 20) - 1;; + dicBufSize = ((SizeT)dictSize + mask) & ~mask; + if (dicBufSize < dictSize) + dicBufSize = dictSize; + } + + if (!p->dic || dicBufSize != p->dicBufSize) + { + LzmaDec_FreeDict(p, alloc); + p->dic = (Byte *)alloc->Alloc(alloc, dicBufSize); + if (!p->dic) + { + LzmaDec_FreeProbs(p, alloc); + return SZ_ERROR_MEM; + } + } + p->dicBufSize = dicBufSize; + p->prop = propNew; + return SZ_OK; +} + +SRes LzmaDecode(Byte *dest, SizeT *destLen, const Byte *src, SizeT *srcLen, + const Byte *propData, unsigned propSize, ELzmaFinishMode finishMode, + ELzmaStatus *status, ISzAlloc *alloc) +{ + CLzmaDec p; + SRes res; + SizeT outSize = *destLen, inSize = *srcLen; + *destLen = *srcLen = 0; + *status = LZMA_STATUS_NOT_SPECIFIED; + if (inSize < RC_INIT_SIZE) + return SZ_ERROR_INPUT_EOF; + LzmaDec_Construct(&p); + RINOK(LzmaDec_AllocateProbs(&p, propData, propSize, alloc)); + p.dic = dest; + p.dicBufSize = outSize; + LzmaDec_Init(&p); + *srcLen = inSize; + res = LzmaDec_DecodeToDic(&p, outSize, src, srcLen, finishMode, status); + *destLen = p.dicPos; + if (res == SZ_OK && *status == LZMA_STATUS_NEEDS_MORE_INPUT) + res = SZ_ERROR_INPUT_EOF; + LzmaDec_FreeProbs(&p, alloc); + return res; +} diff --git a/core/deps/lzma/LzmaDec.h b/core/deps/lzma/LzmaDec.h new file mode 100644 index 000000000..2633abeac --- /dev/null +++ b/core/deps/lzma/LzmaDec.h @@ -0,0 +1,227 @@ +/* LzmaDec.h -- LZMA Decoder +2013-01-18 : Igor Pavlov : Public domain */ + +#ifndef __LZMA_DEC_H +#define __LZMA_DEC_H + +#include "7zTypes.h" + +EXTERN_C_BEGIN + +/* #define _LZMA_PROB32 */ +/* _LZMA_PROB32 can increase the speed on some CPUs, + but memory usage for CLzmaDec::probs will be doubled in that case */ + +#ifdef _LZMA_PROB32 +#define CLzmaProb UInt32 +#else +#define CLzmaProb UInt16 +#endif + + +/* ---------- LZMA Properties ---------- */ + +#define LZMA_PROPS_SIZE 5 + +typedef struct _CLzmaProps +{ + unsigned lc, lp, pb; + UInt32 dicSize; +} CLzmaProps; + +/* LzmaProps_Decode - decodes properties +Returns: + SZ_OK + SZ_ERROR_UNSUPPORTED - Unsupported properties +*/ + +SRes LzmaProps_Decode(CLzmaProps *p, const Byte *data, unsigned size); + + +/* ---------- LZMA Decoder state ---------- */ + +/* LZMA_REQUIRED_INPUT_MAX = number of required input bytes for worst case. + Num bits = log2((2^11 / 31) ^ 22) + 26 < 134 + 26 = 160; */ + +#define LZMA_REQUIRED_INPUT_MAX 20 + +typedef struct +{ + CLzmaProps prop; + CLzmaProb *probs; + Byte *dic; + const Byte *buf; + UInt32 range, code; + SizeT dicPos; + SizeT dicBufSize; + UInt32 processedPos; + UInt32 checkDicSize; + unsigned state; + UInt32 reps[4]; + unsigned remainLen; + int needFlush; + int needInitState; + UInt32 numProbs; + unsigned tempBufSize; + Byte tempBuf[LZMA_REQUIRED_INPUT_MAX]; +} CLzmaDec; + +#define LzmaDec_Construct(p) { (p)->dic = 0; (p)->probs = 0; } + +void LzmaDec_Init(CLzmaDec *p); + +/* There are two types of LZMA streams: + 0) Stream with end mark. That end mark adds about 6 bytes to compressed size. + 1) Stream without end mark. You must know exact uncompressed size to decompress such stream. */ + +typedef enum +{ + LZMA_FINISH_ANY, /* finish at any point */ + LZMA_FINISH_END /* block must be finished at the end */ +} ELzmaFinishMode; + +/* ELzmaFinishMode has meaning only if the decoding reaches output limit !!! + + You must use LZMA_FINISH_END, when you know that current output buffer + covers last bytes of block. In other cases you must use LZMA_FINISH_ANY. + + If LZMA decoder sees end marker before reaching output limit, it returns SZ_OK, + and output value of destLen will be less than output buffer size limit. + You can check status result also. + + You can use multiple checks to test data integrity after full decompression: + 1) Check Result and "status" variable. + 2) Check that output(destLen) = uncompressedSize, if you know real uncompressedSize. + 3) Check that output(srcLen) = compressedSize, if you know real compressedSize. + You must use correct finish mode in that case. */ + +typedef enum +{ + LZMA_STATUS_NOT_SPECIFIED, /* use main error code instead */ + LZMA_STATUS_FINISHED_WITH_MARK, /* stream was finished with end mark. */ + LZMA_STATUS_NOT_FINISHED, /* stream was not finished */ + LZMA_STATUS_NEEDS_MORE_INPUT, /* you must provide more input bytes */ + LZMA_STATUS_MAYBE_FINISHED_WITHOUT_MARK /* there is probability that stream was finished without end mark */ +} ELzmaStatus; + +/* ELzmaStatus is used only as output value for function call */ + + +/* ---------- Interfaces ---------- */ + +/* There are 3 levels of interfaces: + 1) Dictionary Interface + 2) Buffer Interface + 3) One Call Interface + You can select any of these interfaces, but don't mix functions from different + groups for same object. */ + + +/* There are two variants to allocate state for Dictionary Interface: + 1) LzmaDec_Allocate / LzmaDec_Free + 2) LzmaDec_AllocateProbs / LzmaDec_FreeProbs + You can use variant 2, if you set dictionary buffer manually. + For Buffer Interface you must always use variant 1. + +LzmaDec_Allocate* can return: + SZ_OK + SZ_ERROR_MEM - Memory allocation error + SZ_ERROR_UNSUPPORTED - Unsupported properties +*/ + +SRes LzmaDec_AllocateProbs(CLzmaDec *p, const Byte *props, unsigned propsSize, ISzAlloc *alloc); +void LzmaDec_FreeProbs(CLzmaDec *p, ISzAlloc *alloc); + +SRes LzmaDec_Allocate(CLzmaDec *state, const Byte *prop, unsigned propsSize, ISzAlloc *alloc); +void LzmaDec_Free(CLzmaDec *state, ISzAlloc *alloc); + +/* ---------- Dictionary Interface ---------- */ + +/* You can use it, if you want to eliminate the overhead for data copying from + dictionary to some other external buffer. + You must work with CLzmaDec variables directly in this interface. + + STEPS: + LzmaDec_Constr() + LzmaDec_Allocate() + for (each new stream) + { + LzmaDec_Init() + while (it needs more decompression) + { + LzmaDec_DecodeToDic() + use data from CLzmaDec::dic and update CLzmaDec::dicPos + } + } + LzmaDec_Free() +*/ + +/* LzmaDec_DecodeToDic + + The decoding to internal dictionary buffer (CLzmaDec::dic). + You must manually update CLzmaDec::dicPos, if it reaches CLzmaDec::dicBufSize !!! + +finishMode: + It has meaning only if the decoding reaches output limit (dicLimit). + LZMA_FINISH_ANY - Decode just dicLimit bytes. + LZMA_FINISH_END - Stream must be finished after dicLimit. + +Returns: + SZ_OK + status: + LZMA_STATUS_FINISHED_WITH_MARK + LZMA_STATUS_NOT_FINISHED + LZMA_STATUS_NEEDS_MORE_INPUT + LZMA_STATUS_MAYBE_FINISHED_WITHOUT_MARK + SZ_ERROR_DATA - Data error +*/ + +SRes LzmaDec_DecodeToDic(CLzmaDec *p, SizeT dicLimit, + const Byte *src, SizeT *srcLen, ELzmaFinishMode finishMode, ELzmaStatus *status); + + +/* ---------- Buffer Interface ---------- */ + +/* It's zlib-like interface. + See LzmaDec_DecodeToDic description for information about STEPS and return results, + but you must use LzmaDec_DecodeToBuf instead of LzmaDec_DecodeToDic and you don't need + to work with CLzmaDec variables manually. + +finishMode: + It has meaning only if the decoding reaches output limit (*destLen). + LZMA_FINISH_ANY - Decode just destLen bytes. + LZMA_FINISH_END - Stream must be finished after (*destLen). +*/ + +SRes LzmaDec_DecodeToBuf(CLzmaDec *p, Byte *dest, SizeT *destLen, + const Byte *src, SizeT *srcLen, ELzmaFinishMode finishMode, ELzmaStatus *status); + + +/* ---------- One Call Interface ---------- */ + +/* LzmaDecode + +finishMode: + It has meaning only if the decoding reaches output limit (*destLen). + LZMA_FINISH_ANY - Decode just destLen bytes. + LZMA_FINISH_END - Stream must be finished after (*destLen). + +Returns: + SZ_OK + status: + LZMA_STATUS_FINISHED_WITH_MARK + LZMA_STATUS_NOT_FINISHED + LZMA_STATUS_MAYBE_FINISHED_WITHOUT_MARK + SZ_ERROR_DATA - Data error + SZ_ERROR_MEM - Memory allocation error + SZ_ERROR_UNSUPPORTED - Unsupported properties + SZ_ERROR_INPUT_EOF - It needs more bytes in input buffer (src). +*/ + +SRes LzmaDecode(Byte *dest, SizeT *destLen, const Byte *src, SizeT *srcLen, + const Byte *propData, unsigned propSize, ELzmaFinishMode finishMode, + ELzmaStatus *status, ISzAlloc *alloc); + +EXTERN_C_END + +#endif diff --git a/core/deps/lzma/LzmaEnc.c b/core/deps/lzma/LzmaEnc.c new file mode 100644 index 000000000..462ca6756 --- /dev/null +++ b/core/deps/lzma/LzmaEnc.c @@ -0,0 +1,2351 @@ +/* LzmaEnc.c -- LZMA Encoder +2016-05-16 : Igor Pavlov : Public domain */ + +#include "Precomp.h" + +#include + +/* #define SHOW_STAT */ +/* #define SHOW_STAT2 */ + +#if defined(SHOW_STAT) || defined(SHOW_STAT2) +#include +#endif + +#include "LzmaEnc.h" + +#include "LzFind.h" +#ifndef _7ZIP_ST +#include "LzFindMt.h" +#endif + +#ifdef SHOW_STAT +static unsigned g_STAT_OFFSET = 0; +#endif + +#define kMaxHistorySize ((UInt32)3 << 29) +/* #define kMaxHistorySize ((UInt32)7 << 29) */ + +#define kBlockSizeMax ((1 << LZMA_NUM_BLOCK_SIZE_BITS) - 1) + +#define kBlockSize (9 << 10) +#define kUnpackBlockSize (1 << 18) +#define kMatchArraySize (1 << 21) +#define kMatchRecordMaxSize ((LZMA_MATCH_LEN_MAX * 2 + 3) * LZMA_MATCH_LEN_MAX) + +#define kNumMaxDirectBits (31) + +#define kNumTopBits 24 +#define kTopValue ((UInt32)1 << kNumTopBits) + +#define kNumBitModelTotalBits 11 +#define kBitModelTotal (1 << kNumBitModelTotalBits) +#define kNumMoveBits 5 +#define kProbInitValue (kBitModelTotal >> 1) + +#define kNumMoveReducingBits 4 +#define kNumBitPriceShiftBits 4 +#define kBitPrice (1 << kNumBitPriceShiftBits) + +void LzmaEncProps_Init(CLzmaEncProps *p) +{ + p->level = 5; + p->dictSize = p->mc = 0; + p->reduceSize = (UInt64)(Int64)-1; + p->lc = p->lp = p->pb = p->algo = p->fb = p->btMode = p->numHashBytes = p->numThreads = -1; + p->writeEndMark = 0; +} + +void LzmaEncProps_Normalize(CLzmaEncProps *p) +{ + int level = p->level; + if (level < 0) level = 5; + p->level = level; + + if (p->dictSize == 0) p->dictSize = (level <= 5 ? (1 << (level * 2 + 14)) : (level == 6 ? (1 << 25) : (1 << 26))); + if (p->dictSize > p->reduceSize) + { + unsigned i; + for (i = 11; i <= 30; i++) + { + if ((UInt32)p->reduceSize <= ((UInt32)2 << i)) { p->dictSize = ((UInt32)2 << i); break; } + if ((UInt32)p->reduceSize <= ((UInt32)3 << i)) { p->dictSize = ((UInt32)3 << i); break; } + } + } + + if (p->lc < 0) p->lc = 3; + if (p->lp < 0) p->lp = 0; + if (p->pb < 0) p->pb = 2; + + if (p->algo < 0) p->algo = (level < 5 ? 0 : 1); + if (p->fb < 0) p->fb = (level < 7 ? 32 : 64); + if (p->btMode < 0) p->btMode = (p->algo == 0 ? 0 : 1); + if (p->numHashBytes < 0) p->numHashBytes = 4; + if (p->mc == 0) p->mc = (16 + (p->fb >> 1)) >> (p->btMode ? 0 : 1); + + if (p->numThreads < 0) + p->numThreads = + #ifndef _7ZIP_ST + ((p->btMode && p->algo) ? 2 : 1); + #else + 1; + #endif +} + +UInt32 LzmaEncProps_GetDictSize(const CLzmaEncProps *props2) +{ + CLzmaEncProps props = *props2; + LzmaEncProps_Normalize(&props); + return props.dictSize; +} + +#if (_MSC_VER >= 1400) +/* BSR code is fast for some new CPUs */ +/* #define LZMA_LOG_BSR */ +#endif + +#ifdef LZMA_LOG_BSR + +#define kDicLogSizeMaxCompress 32 + +#define BSR2_RET(pos, res) { unsigned long zz; _BitScanReverse(&zz, (pos)); res = (zz + zz) + ((pos >> (zz - 1)) & 1); } + +static UInt32 GetPosSlot1(UInt32 pos) +{ + UInt32 res; + BSR2_RET(pos, res); + return res; +} +#define GetPosSlot2(pos, res) { BSR2_RET(pos, res); } +#define GetPosSlot(pos, res) { if (pos < 2) res = pos; else BSR2_RET(pos, res); } + +#else + +#define kNumLogBits (9 + sizeof(size_t) / 2) +/* #define kNumLogBits (11 + sizeof(size_t) / 8 * 3) */ + +#define kDicLogSizeMaxCompress ((kNumLogBits - 1) * 2 + 7) + +static void LzmaEnc_FastPosInit(Byte *g_FastPos) +{ + unsigned slot; + g_FastPos[0] = 0; + g_FastPos[1] = 1; + g_FastPos += 2; + + for (slot = 2; slot < kNumLogBits * 2; slot++) + { + size_t k = ((size_t)1 << ((slot >> 1) - 1)); + size_t j; + for (j = 0; j < k; j++) + g_FastPos[j] = (Byte)slot; + g_FastPos += k; + } +} + +/* we can use ((limit - pos) >> 31) only if (pos < ((UInt32)1 << 31)) */ +/* +#define BSR2_RET(pos, res) { UInt32 zz = 6 + ((kNumLogBits - 1) & \ + (0 - (((((UInt32)1 << (kNumLogBits + 6)) - 1) - pos) >> 31))); \ + res = p->g_FastPos[pos >> zz] + (zz * 2); } +*/ + +/* +#define BSR2_RET(pos, res) { UInt32 zz = 6 + ((kNumLogBits - 1) & \ + (0 - (((((UInt32)1 << (kNumLogBits)) - 1) - (pos >> 6)) >> 31))); \ + res = p->g_FastPos[pos >> zz] + (zz * 2); } +*/ + +#define BSR2_RET(pos, res) { UInt32 zz = (pos < (1 << (kNumLogBits + 6))) ? 6 : 6 + kNumLogBits - 1; \ + res = p->g_FastPos[pos >> zz] + (zz * 2); } + +/* +#define BSR2_RET(pos, res) { res = (pos < (1 << (kNumLogBits + 6))) ? \ + p->g_FastPos[pos >> 6] + 12 : \ + p->g_FastPos[pos >> (6 + kNumLogBits - 1)] + (6 + (kNumLogBits - 1)) * 2; } +*/ + +#define GetPosSlot1(pos) p->g_FastPos[pos] +#define GetPosSlot2(pos, res) { BSR2_RET(pos, res); } +#define GetPosSlot(pos, res) { if (pos < kNumFullDistances) res = p->g_FastPos[pos]; else BSR2_RET(pos, res); } + +#endif + + +#define LZMA_NUM_REPS 4 + +typedef unsigned CState; + +typedef struct +{ + UInt32 price; + + CState state; + int prev1IsChar; + int prev2; + + UInt32 posPrev2; + UInt32 backPrev2; + + UInt32 posPrev; + UInt32 backPrev; + UInt32 backs[LZMA_NUM_REPS]; +} COptimal; + +#define kNumOpts (1 << 12) + +#define kNumLenToPosStates 4 +#define kNumPosSlotBits 6 +#define kDicLogSizeMin 0 +#define kDicLogSizeMax 32 +#define kDistTableSizeMax (kDicLogSizeMax * 2) + + +#define kNumAlignBits 4 +#define kAlignTableSize (1 << kNumAlignBits) +#define kAlignMask (kAlignTableSize - 1) + +#define kStartPosModelIndex 4 +#define kEndPosModelIndex 14 +#define kNumPosModels (kEndPosModelIndex - kStartPosModelIndex) + +#define kNumFullDistances (1 << (kEndPosModelIndex >> 1)) + +#ifdef _LZMA_PROB32 +#define CLzmaProb UInt32 +#else +#define CLzmaProb UInt16 +#endif + +#define LZMA_PB_MAX 4 +#define LZMA_LC_MAX 8 +#define LZMA_LP_MAX 4 + +#define LZMA_NUM_PB_STATES_MAX (1 << LZMA_PB_MAX) + + +#define kLenNumLowBits 3 +#define kLenNumLowSymbols (1 << kLenNumLowBits) +#define kLenNumMidBits 3 +#define kLenNumMidSymbols (1 << kLenNumMidBits) +#define kLenNumHighBits 8 +#define kLenNumHighSymbols (1 << kLenNumHighBits) + +#define kLenNumSymbolsTotal (kLenNumLowSymbols + kLenNumMidSymbols + kLenNumHighSymbols) + +#define LZMA_MATCH_LEN_MIN 2 +#define LZMA_MATCH_LEN_MAX (LZMA_MATCH_LEN_MIN + kLenNumSymbolsTotal - 1) + +#define kNumStates 12 + + +typedef struct +{ + CLzmaProb choice; + CLzmaProb choice2; + CLzmaProb low[LZMA_NUM_PB_STATES_MAX << kLenNumLowBits]; + CLzmaProb mid[LZMA_NUM_PB_STATES_MAX << kLenNumMidBits]; + CLzmaProb high[kLenNumHighSymbols]; +} CLenEnc; + + +typedef struct +{ + CLenEnc p; + UInt32 tableSize; + UInt32 prices[LZMA_NUM_PB_STATES_MAX][kLenNumSymbolsTotal]; + UInt32 counters[LZMA_NUM_PB_STATES_MAX]; +} CLenPriceEnc; + + +typedef struct +{ + UInt32 range; + Byte cache; + UInt64 low; + UInt64 cacheSize; + Byte *buf; + Byte *bufLim; + Byte *bufBase; + ISeqOutStream *outStream; + UInt64 processed; + SRes res; +} CRangeEnc; + + +typedef struct +{ + CLzmaProb *litProbs; + + UInt32 state; + UInt32 reps[LZMA_NUM_REPS]; + + CLzmaProb isMatch[kNumStates][LZMA_NUM_PB_STATES_MAX]; + CLzmaProb isRep[kNumStates]; + CLzmaProb isRepG0[kNumStates]; + CLzmaProb isRepG1[kNumStates]; + CLzmaProb isRepG2[kNumStates]; + CLzmaProb isRep0Long[kNumStates][LZMA_NUM_PB_STATES_MAX]; + + CLzmaProb posSlotEncoder[kNumLenToPosStates][1 << kNumPosSlotBits]; + CLzmaProb posEncoders[kNumFullDistances - kEndPosModelIndex]; + CLzmaProb posAlignEncoder[1 << kNumAlignBits]; + + CLenPriceEnc lenEnc; + CLenPriceEnc repLenEnc; +} CSaveState; + + +typedef struct +{ + void *matchFinderObj; + IMatchFinder matchFinder; + + UInt32 optimumEndIndex; + UInt32 optimumCurrentIndex; + + UInt32 longestMatchLength; + UInt32 numPairs; + UInt32 numAvail; + + UInt32 numFastBytes; + UInt32 additionalOffset; + UInt32 reps[LZMA_NUM_REPS]; + UInt32 state; + + unsigned lc, lp, pb; + unsigned lpMask, pbMask; + unsigned lclp; + + CLzmaProb *litProbs; + + Bool fastMode; + Bool writeEndMark; + Bool finished; + Bool multiThread; + Bool needInit; + + UInt64 nowPos64; + + UInt32 matchPriceCount; + UInt32 alignPriceCount; + + UInt32 distTableSize; + + UInt32 dictSize; + SRes result; + + CRangeEnc rc; + + #ifndef _7ZIP_ST + Bool mtMode; + CMatchFinderMt matchFinderMt; + #endif + + CMatchFinder matchFinderBase; + + #ifndef _7ZIP_ST + Byte pad[128]; + #endif + + COptimal opt[kNumOpts]; + + #ifndef LZMA_LOG_BSR + Byte g_FastPos[1 << kNumLogBits]; + #endif + + UInt32 ProbPrices[kBitModelTotal >> kNumMoveReducingBits]; + UInt32 matches[LZMA_MATCH_LEN_MAX * 2 + 2 + 1]; + + UInt32 posSlotPrices[kNumLenToPosStates][kDistTableSizeMax]; + UInt32 distancesPrices[kNumLenToPosStates][kNumFullDistances]; + UInt32 alignPrices[kAlignTableSize]; + + CLzmaProb isMatch[kNumStates][LZMA_NUM_PB_STATES_MAX]; + CLzmaProb isRep[kNumStates]; + CLzmaProb isRepG0[kNumStates]; + CLzmaProb isRepG1[kNumStates]; + CLzmaProb isRepG2[kNumStates]; + CLzmaProb isRep0Long[kNumStates][LZMA_NUM_PB_STATES_MAX]; + + CLzmaProb posSlotEncoder[kNumLenToPosStates][1 << kNumPosSlotBits]; + CLzmaProb posEncoders[kNumFullDistances - kEndPosModelIndex]; + CLzmaProb posAlignEncoder[1 << kNumAlignBits]; + + CLenPriceEnc lenEnc; + CLenPriceEnc repLenEnc; + + CSaveState saveState; + + #ifndef _7ZIP_ST + Byte pad2[128]; + #endif +} CLzmaEnc; + + +void LzmaEnc_SaveState(CLzmaEncHandle pp) +{ + CLzmaEnc *p = (CLzmaEnc *)pp; + CSaveState *dest = &p->saveState; + int i; + dest->lenEnc = p->lenEnc; + dest->repLenEnc = p->repLenEnc; + dest->state = p->state; + + for (i = 0; i < kNumStates; i++) + { + memcpy(dest->isMatch[i], p->isMatch[i], sizeof(p->isMatch[i])); + memcpy(dest->isRep0Long[i], p->isRep0Long[i], sizeof(p->isRep0Long[i])); + } + for (i = 0; i < kNumLenToPosStates; i++) + memcpy(dest->posSlotEncoder[i], p->posSlotEncoder[i], sizeof(p->posSlotEncoder[i])); + memcpy(dest->isRep, p->isRep, sizeof(p->isRep)); + memcpy(dest->isRepG0, p->isRepG0, sizeof(p->isRepG0)); + memcpy(dest->isRepG1, p->isRepG1, sizeof(p->isRepG1)); + memcpy(dest->isRepG2, p->isRepG2, sizeof(p->isRepG2)); + memcpy(dest->posEncoders, p->posEncoders, sizeof(p->posEncoders)); + memcpy(dest->posAlignEncoder, p->posAlignEncoder, sizeof(p->posAlignEncoder)); + memcpy(dest->reps, p->reps, sizeof(p->reps)); + memcpy(dest->litProbs, p->litProbs, ((UInt32)0x300 << p->lclp) * sizeof(CLzmaProb)); +} + +void LzmaEnc_RestoreState(CLzmaEncHandle pp) +{ + CLzmaEnc *dest = (CLzmaEnc *)pp; + const CSaveState *p = &dest->saveState; + int i; + dest->lenEnc = p->lenEnc; + dest->repLenEnc = p->repLenEnc; + dest->state = p->state; + + for (i = 0; i < kNumStates; i++) + { + memcpy(dest->isMatch[i], p->isMatch[i], sizeof(p->isMatch[i])); + memcpy(dest->isRep0Long[i], p->isRep0Long[i], sizeof(p->isRep0Long[i])); + } + for (i = 0; i < kNumLenToPosStates; i++) + memcpy(dest->posSlotEncoder[i], p->posSlotEncoder[i], sizeof(p->posSlotEncoder[i])); + memcpy(dest->isRep, p->isRep, sizeof(p->isRep)); + memcpy(dest->isRepG0, p->isRepG0, sizeof(p->isRepG0)); + memcpy(dest->isRepG1, p->isRepG1, sizeof(p->isRepG1)); + memcpy(dest->isRepG2, p->isRepG2, sizeof(p->isRepG2)); + memcpy(dest->posEncoders, p->posEncoders, sizeof(p->posEncoders)); + memcpy(dest->posAlignEncoder, p->posAlignEncoder, sizeof(p->posAlignEncoder)); + memcpy(dest->reps, p->reps, sizeof(p->reps)); + memcpy(dest->litProbs, p->litProbs, ((UInt32)0x300 << dest->lclp) * sizeof(CLzmaProb)); +} + +SRes LzmaEnc_SetProps(CLzmaEncHandle pp, const CLzmaEncProps *props2) +{ + CLzmaEnc *p = (CLzmaEnc *)pp; + CLzmaEncProps props = *props2; + LzmaEncProps_Normalize(&props); + + if (props.lc > LZMA_LC_MAX + || props.lp > LZMA_LP_MAX + || props.pb > LZMA_PB_MAX + || props.dictSize > ((UInt64)1 << kDicLogSizeMaxCompress) + || props.dictSize > kMaxHistorySize) + return SZ_ERROR_PARAM; + + p->dictSize = props.dictSize; + { + unsigned fb = props.fb; + if (fb < 5) + fb = 5; + if (fb > LZMA_MATCH_LEN_MAX) + fb = LZMA_MATCH_LEN_MAX; + p->numFastBytes = fb; + } + p->lc = props.lc; + p->lp = props.lp; + p->pb = props.pb; + p->fastMode = (props.algo == 0); + p->matchFinderBase.btMode = (Byte)(props.btMode ? 1 : 0); + { + UInt32 numHashBytes = 4; + if (props.btMode) + { + if (props.numHashBytes < 2) + numHashBytes = 2; + else if (props.numHashBytes < 4) + numHashBytes = props.numHashBytes; + } + p->matchFinderBase.numHashBytes = numHashBytes; + } + + p->matchFinderBase.cutValue = props.mc; + + p->writeEndMark = props.writeEndMark; + + #ifndef _7ZIP_ST + /* + if (newMultiThread != _multiThread) + { + ReleaseMatchFinder(); + _multiThread = newMultiThread; + } + */ + p->multiThread = (props.numThreads > 1); + #endif + + return SZ_OK; +} + +static const int kLiteralNextStates[kNumStates] = {0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 4, 5}; +static const int kMatchNextStates[kNumStates] = {7, 7, 7, 7, 7, 7, 7, 10, 10, 10, 10, 10}; +static const int kRepNextStates[kNumStates] = {8, 8, 8, 8, 8, 8, 8, 11, 11, 11, 11, 11}; +static const int kShortRepNextStates[kNumStates]= {9, 9, 9, 9, 9, 9, 9, 11, 11, 11, 11, 11}; + +#define IsCharState(s) ((s) < 7) + +#define GetLenToPosState(len) (((len) < kNumLenToPosStates + 1) ? (len) - 2 : kNumLenToPosStates - 1) + +#define kInfinityPrice (1 << 30) + +static void RangeEnc_Construct(CRangeEnc *p) +{ + p->outStream = NULL; + p->bufBase = NULL; +} + +#define RangeEnc_GetProcessed(p) ((p)->processed + ((p)->buf - (p)->bufBase) + (p)->cacheSize) + +#define RC_BUF_SIZE (1 << 16) +static int RangeEnc_Alloc(CRangeEnc *p, ISzAlloc *alloc) +{ + if (!p->bufBase) + { + p->bufBase = (Byte *)alloc->Alloc(alloc, RC_BUF_SIZE); + if (!p->bufBase) + return 0; + p->bufLim = p->bufBase + RC_BUF_SIZE; + } + return 1; +} + +static void RangeEnc_Free(CRangeEnc *p, ISzAlloc *alloc) +{ + alloc->Free(alloc, p->bufBase); + p->bufBase = 0; +} + +static void RangeEnc_Init(CRangeEnc *p) +{ + /* Stream.Init(); */ + p->low = 0; + p->range = 0xFFFFFFFF; + p->cacheSize = 1; + p->cache = 0; + + p->buf = p->bufBase; + + p->processed = 0; + p->res = SZ_OK; +} + +static void RangeEnc_FlushStream(CRangeEnc *p) +{ + size_t num; + if (p->res != SZ_OK) + return; + num = p->buf - p->bufBase; + if (num != p->outStream->Write(p->outStream, p->bufBase, num)) + p->res = SZ_ERROR_WRITE; + p->processed += num; + p->buf = p->bufBase; +} + +static void MY_FAST_CALL RangeEnc_ShiftLow(CRangeEnc *p) +{ + if ((UInt32)p->low < (UInt32)0xFF000000 || (unsigned)(p->low >> 32) != 0) + { + Byte temp = p->cache; + do + { + Byte *buf = p->buf; + *buf++ = (Byte)(temp + (Byte)(p->low >> 32)); + p->buf = buf; + if (buf == p->bufLim) + RangeEnc_FlushStream(p); + temp = 0xFF; + } + while (--p->cacheSize != 0); + p->cache = (Byte)((UInt32)p->low >> 24); + } + p->cacheSize++; + p->low = (UInt32)p->low << 8; +} + +static void RangeEnc_FlushData(CRangeEnc *p) +{ + int i; + for (i = 0; i < 5; i++) + RangeEnc_ShiftLow(p); +} + +static void RangeEnc_EncodeDirectBits(CRangeEnc *p, UInt32 value, unsigned numBits) +{ + do + { + p->range >>= 1; + p->low += p->range & (0 - ((value >> --numBits) & 1)); + if (p->range < kTopValue) + { + p->range <<= 8; + RangeEnc_ShiftLow(p); + } + } + while (numBits != 0); +} + +static void RangeEnc_EncodeBit(CRangeEnc *p, CLzmaProb *prob, UInt32 symbol) +{ + UInt32 ttt = *prob; + UInt32 newBound = (p->range >> kNumBitModelTotalBits) * ttt; + if (symbol == 0) + { + p->range = newBound; + ttt += (kBitModelTotal - ttt) >> kNumMoveBits; + } + else + { + p->low += newBound; + p->range -= newBound; + ttt -= ttt >> kNumMoveBits; + } + *prob = (CLzmaProb)ttt; + if (p->range < kTopValue) + { + p->range <<= 8; + RangeEnc_ShiftLow(p); + } +} + +static void LitEnc_Encode(CRangeEnc *p, CLzmaProb *probs, UInt32 symbol) +{ + symbol |= 0x100; + do + { + RangeEnc_EncodeBit(p, probs + (symbol >> 8), (symbol >> 7) & 1); + symbol <<= 1; + } + while (symbol < 0x10000); +} + +static void LitEnc_EncodeMatched(CRangeEnc *p, CLzmaProb *probs, UInt32 symbol, UInt32 matchByte) +{ + UInt32 offs = 0x100; + symbol |= 0x100; + do + { + matchByte <<= 1; + RangeEnc_EncodeBit(p, probs + (offs + (matchByte & offs) + (symbol >> 8)), (symbol >> 7) & 1); + symbol <<= 1; + offs &= ~(matchByte ^ symbol); + } + while (symbol < 0x10000); +} + +static void LzmaEnc_InitPriceTables(UInt32 *ProbPrices) +{ + UInt32 i; + for (i = (1 << kNumMoveReducingBits) / 2; i < kBitModelTotal; i += (1 << kNumMoveReducingBits)) + { + const int kCyclesBits = kNumBitPriceShiftBits; + UInt32 w = i; + UInt32 bitCount = 0; + int j; + for (j = 0; j < kCyclesBits; j++) + { + w = w * w; + bitCount <<= 1; + while (w >= ((UInt32)1 << 16)) + { + w >>= 1; + bitCount++; + } + } + ProbPrices[i >> kNumMoveReducingBits] = ((kNumBitModelTotalBits << kCyclesBits) - 15 - bitCount); + } +} + + +#define GET_PRICE(prob, symbol) \ + p->ProbPrices[((prob) ^ (((-(int)(symbol))) & (kBitModelTotal - 1))) >> kNumMoveReducingBits]; + +#define GET_PRICEa(prob, symbol) \ + ProbPrices[((prob) ^ ((-((int)(symbol))) & (kBitModelTotal - 1))) >> kNumMoveReducingBits]; + +#define GET_PRICE_0(prob) p->ProbPrices[(prob) >> kNumMoveReducingBits] +#define GET_PRICE_1(prob) p->ProbPrices[((prob) ^ (kBitModelTotal - 1)) >> kNumMoveReducingBits] + +#define GET_PRICE_0a(prob) ProbPrices[(prob) >> kNumMoveReducingBits] +#define GET_PRICE_1a(prob) ProbPrices[((prob) ^ (kBitModelTotal - 1)) >> kNumMoveReducingBits] + +static UInt32 LitEnc_GetPrice(const CLzmaProb *probs, UInt32 symbol, const UInt32 *ProbPrices) +{ + UInt32 price = 0; + symbol |= 0x100; + do + { + price += GET_PRICEa(probs[symbol >> 8], (symbol >> 7) & 1); + symbol <<= 1; + } + while (symbol < 0x10000); + return price; +} + +static UInt32 LitEnc_GetPriceMatched(const CLzmaProb *probs, UInt32 symbol, UInt32 matchByte, const UInt32 *ProbPrices) +{ + UInt32 price = 0; + UInt32 offs = 0x100; + symbol |= 0x100; + do + { + matchByte <<= 1; + price += GET_PRICEa(probs[offs + (matchByte & offs) + (symbol >> 8)], (symbol >> 7) & 1); + symbol <<= 1; + offs &= ~(matchByte ^ symbol); + } + while (symbol < 0x10000); + return price; +} + + +static void RcTree_Encode(CRangeEnc *rc, CLzmaProb *probs, int numBitLevels, UInt32 symbol) +{ + UInt32 m = 1; + int i; + for (i = numBitLevels; i != 0;) + { + UInt32 bit; + i--; + bit = (symbol >> i) & 1; + RangeEnc_EncodeBit(rc, probs + m, bit); + m = (m << 1) | bit; + } +} + +static void RcTree_ReverseEncode(CRangeEnc *rc, CLzmaProb *probs, int numBitLevels, UInt32 symbol) +{ + UInt32 m = 1; + int i; + for (i = 0; i < numBitLevels; i++) + { + UInt32 bit = symbol & 1; + RangeEnc_EncodeBit(rc, probs + m, bit); + m = (m << 1) | bit; + symbol >>= 1; + } +} + +static UInt32 RcTree_GetPrice(const CLzmaProb *probs, int numBitLevels, UInt32 symbol, const UInt32 *ProbPrices) +{ + UInt32 price = 0; + symbol |= (1 << numBitLevels); + while (symbol != 1) + { + price += GET_PRICEa(probs[symbol >> 1], symbol & 1); + symbol >>= 1; + } + return price; +} + +static UInt32 RcTree_ReverseGetPrice(const CLzmaProb *probs, int numBitLevels, UInt32 symbol, const UInt32 *ProbPrices) +{ + UInt32 price = 0; + UInt32 m = 1; + int i; + for (i = numBitLevels; i != 0; i--) + { + UInt32 bit = symbol & 1; + symbol >>= 1; + price += GET_PRICEa(probs[m], bit); + m = (m << 1) | bit; + } + return price; +} + + +static void LenEnc_Init(CLenEnc *p) +{ + unsigned i; + p->choice = p->choice2 = kProbInitValue; + for (i = 0; i < (LZMA_NUM_PB_STATES_MAX << kLenNumLowBits); i++) + p->low[i] = kProbInitValue; + for (i = 0; i < (LZMA_NUM_PB_STATES_MAX << kLenNumMidBits); i++) + p->mid[i] = kProbInitValue; + for (i = 0; i < kLenNumHighSymbols; i++) + p->high[i] = kProbInitValue; +} + +static void LenEnc_Encode(CLenEnc *p, CRangeEnc *rc, UInt32 symbol, UInt32 posState) +{ + if (symbol < kLenNumLowSymbols) + { + RangeEnc_EncodeBit(rc, &p->choice, 0); + RcTree_Encode(rc, p->low + (posState << kLenNumLowBits), kLenNumLowBits, symbol); + } + else + { + RangeEnc_EncodeBit(rc, &p->choice, 1); + if (symbol < kLenNumLowSymbols + kLenNumMidSymbols) + { + RangeEnc_EncodeBit(rc, &p->choice2, 0); + RcTree_Encode(rc, p->mid + (posState << kLenNumMidBits), kLenNumMidBits, symbol - kLenNumLowSymbols); + } + else + { + RangeEnc_EncodeBit(rc, &p->choice2, 1); + RcTree_Encode(rc, p->high, kLenNumHighBits, symbol - kLenNumLowSymbols - kLenNumMidSymbols); + } + } +} + +static void LenEnc_SetPrices(CLenEnc *p, UInt32 posState, UInt32 numSymbols, UInt32 *prices, const UInt32 *ProbPrices) +{ + UInt32 a0 = GET_PRICE_0a(p->choice); + UInt32 a1 = GET_PRICE_1a(p->choice); + UInt32 b0 = a1 + GET_PRICE_0a(p->choice2); + UInt32 b1 = a1 + GET_PRICE_1a(p->choice2); + UInt32 i = 0; + for (i = 0; i < kLenNumLowSymbols; i++) + { + if (i >= numSymbols) + return; + prices[i] = a0 + RcTree_GetPrice(p->low + (posState << kLenNumLowBits), kLenNumLowBits, i, ProbPrices); + } + for (; i < kLenNumLowSymbols + kLenNumMidSymbols; i++) + { + if (i >= numSymbols) + return; + prices[i] = b0 + RcTree_GetPrice(p->mid + (posState << kLenNumMidBits), kLenNumMidBits, i - kLenNumLowSymbols, ProbPrices); + } + for (; i < numSymbols; i++) + prices[i] = b1 + RcTree_GetPrice(p->high, kLenNumHighBits, i - kLenNumLowSymbols - kLenNumMidSymbols, ProbPrices); +} + +static void MY_FAST_CALL LenPriceEnc_UpdateTable(CLenPriceEnc *p, UInt32 posState, const UInt32 *ProbPrices) +{ + LenEnc_SetPrices(&p->p, posState, p->tableSize, p->prices[posState], ProbPrices); + p->counters[posState] = p->tableSize; +} + +static void LenPriceEnc_UpdateTables(CLenPriceEnc *p, UInt32 numPosStates, const UInt32 *ProbPrices) +{ + UInt32 posState; + for (posState = 0; posState < numPosStates; posState++) + LenPriceEnc_UpdateTable(p, posState, ProbPrices); +} + +static void LenEnc_Encode2(CLenPriceEnc *p, CRangeEnc *rc, UInt32 symbol, UInt32 posState, Bool updatePrice, const UInt32 *ProbPrices) +{ + LenEnc_Encode(&p->p, rc, symbol, posState); + if (updatePrice) + if (--p->counters[posState] == 0) + LenPriceEnc_UpdateTable(p, posState, ProbPrices); +} + + + + +static void MovePos(CLzmaEnc *p, UInt32 num) +{ + #ifdef SHOW_STAT + g_STAT_OFFSET += num; + printf("\n MovePos %u", num); + #endif + + if (num != 0) + { + p->additionalOffset += num; + p->matchFinder.Skip(p->matchFinderObj, num); + } +} + +static UInt32 ReadMatchDistances(CLzmaEnc *p, UInt32 *numDistancePairsRes) +{ + UInt32 lenRes = 0, numPairs; + p->numAvail = p->matchFinder.GetNumAvailableBytes(p->matchFinderObj); + numPairs = p->matchFinder.GetMatches(p->matchFinderObj, p->matches); + + #ifdef SHOW_STAT + printf("\n i = %u numPairs = %u ", g_STAT_OFFSET, numPairs / 2); + g_STAT_OFFSET++; + { + UInt32 i; + for (i = 0; i < numPairs; i += 2) + printf("%2u %6u | ", p->matches[i], p->matches[i + 1]); + } + #endif + + if (numPairs > 0) + { + lenRes = p->matches[numPairs - 2]; + if (lenRes == p->numFastBytes) + { + UInt32 numAvail = p->numAvail; + if (numAvail > LZMA_MATCH_LEN_MAX) + numAvail = LZMA_MATCH_LEN_MAX; + { + const Byte *pbyCur = p->matchFinder.GetPointerToCurrentPos(p->matchFinderObj) - 1; + const Byte *pby = pbyCur + lenRes; + ptrdiff_t dif = (ptrdiff_t)-1 - p->matches[numPairs - 1]; + const Byte *pbyLim = pbyCur + numAvail; + for (; pby != pbyLim && *pby == pby[dif]; pby++); + lenRes = (UInt32)(pby - pbyCur); + } + } + } + p->additionalOffset++; + *numDistancePairsRes = numPairs; + return lenRes; +} + + +#define MakeAsChar(p) (p)->backPrev = (UInt32)(-1); (p)->prev1IsChar = False; +#define MakeAsShortRep(p) (p)->backPrev = 0; (p)->prev1IsChar = False; +#define IsShortRep(p) ((p)->backPrev == 0) + +static UInt32 GetRepLen1Price(CLzmaEnc *p, UInt32 state, UInt32 posState) +{ + return + GET_PRICE_0(p->isRepG0[state]) + + GET_PRICE_0(p->isRep0Long[state][posState]); +} + +static UInt32 GetPureRepPrice(CLzmaEnc *p, UInt32 repIndex, UInt32 state, UInt32 posState) +{ + UInt32 price; + if (repIndex == 0) + { + price = GET_PRICE_0(p->isRepG0[state]); + price += GET_PRICE_1(p->isRep0Long[state][posState]); + } + else + { + price = GET_PRICE_1(p->isRepG0[state]); + if (repIndex == 1) + price += GET_PRICE_0(p->isRepG1[state]); + else + { + price += GET_PRICE_1(p->isRepG1[state]); + price += GET_PRICE(p->isRepG2[state], repIndex - 2); + } + } + return price; +} + +static UInt32 GetRepPrice(CLzmaEnc *p, UInt32 repIndex, UInt32 len, UInt32 state, UInt32 posState) +{ + return p->repLenEnc.prices[posState][len - LZMA_MATCH_LEN_MIN] + + GetPureRepPrice(p, repIndex, state, posState); +} + +static UInt32 Backward(CLzmaEnc *p, UInt32 *backRes, UInt32 cur) +{ + UInt32 posMem = p->opt[cur].posPrev; + UInt32 backMem = p->opt[cur].backPrev; + p->optimumEndIndex = cur; + do + { + if (p->opt[cur].prev1IsChar) + { + MakeAsChar(&p->opt[posMem]) + p->opt[posMem].posPrev = posMem - 1; + if (p->opt[cur].prev2) + { + p->opt[posMem - 1].prev1IsChar = False; + p->opt[posMem - 1].posPrev = p->opt[cur].posPrev2; + p->opt[posMem - 1].backPrev = p->opt[cur].backPrev2; + } + } + { + UInt32 posPrev = posMem; + UInt32 backCur = backMem; + + backMem = p->opt[posPrev].backPrev; + posMem = p->opt[posPrev].posPrev; + + p->opt[posPrev].backPrev = backCur; + p->opt[posPrev].posPrev = cur; + cur = posPrev; + } + } + while (cur != 0); + *backRes = p->opt[0].backPrev; + p->optimumCurrentIndex = p->opt[0].posPrev; + return p->optimumCurrentIndex; +} + +#define LIT_PROBS(pos, prevByte) (p->litProbs + ((((pos) & p->lpMask) << p->lc) + ((prevByte) >> (8 - p->lc))) * (UInt32)0x300) + +static UInt32 GetOptimum(CLzmaEnc *p, UInt32 position, UInt32 *backRes) +{ + UInt32 lenEnd, cur; + UInt32 reps[LZMA_NUM_REPS], repLens[LZMA_NUM_REPS]; + UInt32 *matches; + + { + + UInt32 numAvail, mainLen, numPairs, repMaxIndex, i, posState, len; + UInt32 matchPrice, repMatchPrice, normalMatchPrice; + const Byte *data; + Byte curByte, matchByte; + + if (p->optimumEndIndex != p->optimumCurrentIndex) + { + const COptimal *opt = &p->opt[p->optimumCurrentIndex]; + UInt32 lenRes = opt->posPrev - p->optimumCurrentIndex; + *backRes = opt->backPrev; + p->optimumCurrentIndex = opt->posPrev; + return lenRes; + } + p->optimumCurrentIndex = p->optimumEndIndex = 0; + + if (p->additionalOffset == 0) + mainLen = ReadMatchDistances(p, &numPairs); + else + { + mainLen = p->longestMatchLength; + numPairs = p->numPairs; + } + + numAvail = p->numAvail; + if (numAvail < 2) + { + *backRes = (UInt32)(-1); + return 1; + } + if (numAvail > LZMA_MATCH_LEN_MAX) + numAvail = LZMA_MATCH_LEN_MAX; + + data = p->matchFinder.GetPointerToCurrentPos(p->matchFinderObj) - 1; + repMaxIndex = 0; + for (i = 0; i < LZMA_NUM_REPS; i++) + { + UInt32 lenTest; + const Byte *data2; + reps[i] = p->reps[i]; + data2 = data - reps[i] - 1; + if (data[0] != data2[0] || data[1] != data2[1]) + { + repLens[i] = 0; + continue; + } + for (lenTest = 2; lenTest < numAvail && data[lenTest] == data2[lenTest]; lenTest++); + repLens[i] = lenTest; + if (lenTest > repLens[repMaxIndex]) + repMaxIndex = i; + } + if (repLens[repMaxIndex] >= p->numFastBytes) + { + UInt32 lenRes; + *backRes = repMaxIndex; + lenRes = repLens[repMaxIndex]; + MovePos(p, lenRes - 1); + return lenRes; + } + + matches = p->matches; + if (mainLen >= p->numFastBytes) + { + *backRes = matches[numPairs - 1] + LZMA_NUM_REPS; + MovePos(p, mainLen - 1); + return mainLen; + } + curByte = *data; + matchByte = *(data - (reps[0] + 1)); + + if (mainLen < 2 && curByte != matchByte && repLens[repMaxIndex] < 2) + { + *backRes = (UInt32)-1; + return 1; + } + + p->opt[0].state = (CState)p->state; + + posState = (position & p->pbMask); + + { + const CLzmaProb *probs = LIT_PROBS(position, *(data - 1)); + p->opt[1].price = GET_PRICE_0(p->isMatch[p->state][posState]) + + (!IsCharState(p->state) ? + LitEnc_GetPriceMatched(probs, curByte, matchByte, p->ProbPrices) : + LitEnc_GetPrice(probs, curByte, p->ProbPrices)); + } + + MakeAsChar(&p->opt[1]); + + matchPrice = GET_PRICE_1(p->isMatch[p->state][posState]); + repMatchPrice = matchPrice + GET_PRICE_1(p->isRep[p->state]); + + if (matchByte == curByte) + { + UInt32 shortRepPrice = repMatchPrice + GetRepLen1Price(p, p->state, posState); + if (shortRepPrice < p->opt[1].price) + { + p->opt[1].price = shortRepPrice; + MakeAsShortRep(&p->opt[1]); + } + } + lenEnd = ((mainLen >= repLens[repMaxIndex]) ? mainLen : repLens[repMaxIndex]); + + if (lenEnd < 2) + { + *backRes = p->opt[1].backPrev; + return 1; + } + + p->opt[1].posPrev = 0; + for (i = 0; i < LZMA_NUM_REPS; i++) + p->opt[0].backs[i] = reps[i]; + + len = lenEnd; + do + p->opt[len--].price = kInfinityPrice; + while (len >= 2); + + for (i = 0; i < LZMA_NUM_REPS; i++) + { + UInt32 repLen = repLens[i]; + UInt32 price; + if (repLen < 2) + continue; + price = repMatchPrice + GetPureRepPrice(p, i, p->state, posState); + do + { + UInt32 curAndLenPrice = price + p->repLenEnc.prices[posState][repLen - 2]; + COptimal *opt = &p->opt[repLen]; + if (curAndLenPrice < opt->price) + { + opt->price = curAndLenPrice; + opt->posPrev = 0; + opt->backPrev = i; + opt->prev1IsChar = False; + } + } + while (--repLen >= 2); + } + + normalMatchPrice = matchPrice + GET_PRICE_0(p->isRep[p->state]); + + len = ((repLens[0] >= 2) ? repLens[0] + 1 : 2); + if (len <= mainLen) + { + UInt32 offs = 0; + while (len > matches[offs]) + offs += 2; + for (; ; len++) + { + COptimal *opt; + UInt32 distance = matches[offs + 1]; + + UInt32 curAndLenPrice = normalMatchPrice + p->lenEnc.prices[posState][len - LZMA_MATCH_LEN_MIN]; + UInt32 lenToPosState = GetLenToPosState(len); + if (distance < kNumFullDistances) + curAndLenPrice += p->distancesPrices[lenToPosState][distance]; + else + { + UInt32 slot; + GetPosSlot2(distance, slot); + curAndLenPrice += p->alignPrices[distance & kAlignMask] + p->posSlotPrices[lenToPosState][slot]; + } + opt = &p->opt[len]; + if (curAndLenPrice < opt->price) + { + opt->price = curAndLenPrice; + opt->posPrev = 0; + opt->backPrev = distance + LZMA_NUM_REPS; + opt->prev1IsChar = False; + } + if (len == matches[offs]) + { + offs += 2; + if (offs == numPairs) + break; + } + } + } + + cur = 0; + + #ifdef SHOW_STAT2 + /* if (position >= 0) */ + { + unsigned i; + printf("\n pos = %4X", position); + for (i = cur; i <= lenEnd; i++) + printf("\nprice[%4X] = %u", position - cur + i, p->opt[i].price); + } + #endif + + } + + for (;;) + { + UInt32 numAvail; + UInt32 numAvailFull, newLen, numPairs, posPrev, state, posState, startLen; + UInt32 curPrice, curAnd1Price, matchPrice, repMatchPrice; + Bool nextIsChar; + Byte curByte, matchByte; + const Byte *data; + COptimal *curOpt; + COptimal *nextOpt; + + cur++; + if (cur == lenEnd) + return Backward(p, backRes, cur); + + newLen = ReadMatchDistances(p, &numPairs); + if (newLen >= p->numFastBytes) + { + p->numPairs = numPairs; + p->longestMatchLength = newLen; + return Backward(p, backRes, cur); + } + position++; + curOpt = &p->opt[cur]; + posPrev = curOpt->posPrev; + if (curOpt->prev1IsChar) + { + posPrev--; + if (curOpt->prev2) + { + state = p->opt[curOpt->posPrev2].state; + if (curOpt->backPrev2 < LZMA_NUM_REPS) + state = kRepNextStates[state]; + else + state = kMatchNextStates[state]; + } + else + state = p->opt[posPrev].state; + state = kLiteralNextStates[state]; + } + else + state = p->opt[posPrev].state; + if (posPrev == cur - 1) + { + if (IsShortRep(curOpt)) + state = kShortRepNextStates[state]; + else + state = kLiteralNextStates[state]; + } + else + { + UInt32 pos; + const COptimal *prevOpt; + if (curOpt->prev1IsChar && curOpt->prev2) + { + posPrev = curOpt->posPrev2; + pos = curOpt->backPrev2; + state = kRepNextStates[state]; + } + else + { + pos = curOpt->backPrev; + if (pos < LZMA_NUM_REPS) + state = kRepNextStates[state]; + else + state = kMatchNextStates[state]; + } + prevOpt = &p->opt[posPrev]; + if (pos < LZMA_NUM_REPS) + { + UInt32 i; + reps[0] = prevOpt->backs[pos]; + for (i = 1; i <= pos; i++) + reps[i] = prevOpt->backs[i - 1]; + for (; i < LZMA_NUM_REPS; i++) + reps[i] = prevOpt->backs[i]; + } + else + { + UInt32 i; + reps[0] = (pos - LZMA_NUM_REPS); + for (i = 1; i < LZMA_NUM_REPS; i++) + reps[i] = prevOpt->backs[i - 1]; + } + } + curOpt->state = (CState)state; + + curOpt->backs[0] = reps[0]; + curOpt->backs[1] = reps[1]; + curOpt->backs[2] = reps[2]; + curOpt->backs[3] = reps[3]; + + curPrice = curOpt->price; + nextIsChar = False; + data = p->matchFinder.GetPointerToCurrentPos(p->matchFinderObj) - 1; + curByte = *data; + matchByte = *(data - (reps[0] + 1)); + + posState = (position & p->pbMask); + + curAnd1Price = curPrice + GET_PRICE_0(p->isMatch[state][posState]); + { + const CLzmaProb *probs = LIT_PROBS(position, *(data - 1)); + curAnd1Price += + (!IsCharState(state) ? + LitEnc_GetPriceMatched(probs, curByte, matchByte, p->ProbPrices) : + LitEnc_GetPrice(probs, curByte, p->ProbPrices)); + } + + nextOpt = &p->opt[cur + 1]; + + if (curAnd1Price < nextOpt->price) + { + nextOpt->price = curAnd1Price; + nextOpt->posPrev = cur; + MakeAsChar(nextOpt); + nextIsChar = True; + } + + matchPrice = curPrice + GET_PRICE_1(p->isMatch[state][posState]); + repMatchPrice = matchPrice + GET_PRICE_1(p->isRep[state]); + + if (matchByte == curByte && !(nextOpt->posPrev < cur && nextOpt->backPrev == 0)) + { + UInt32 shortRepPrice = repMatchPrice + GetRepLen1Price(p, state, posState); + if (shortRepPrice <= nextOpt->price) + { + nextOpt->price = shortRepPrice; + nextOpt->posPrev = cur; + MakeAsShortRep(nextOpt); + nextIsChar = True; + } + } + numAvailFull = p->numAvail; + { + UInt32 temp = kNumOpts - 1 - cur; + if (temp < numAvailFull) + numAvailFull = temp; + } + + if (numAvailFull < 2) + continue; + numAvail = (numAvailFull <= p->numFastBytes ? numAvailFull : p->numFastBytes); + + if (!nextIsChar && matchByte != curByte) /* speed optimization */ + { + /* try Literal + rep0 */ + UInt32 temp; + UInt32 lenTest2; + const Byte *data2 = data - reps[0] - 1; + UInt32 limit = p->numFastBytes + 1; + if (limit > numAvailFull) + limit = numAvailFull; + + for (temp = 1; temp < limit && data[temp] == data2[temp]; temp++); + lenTest2 = temp - 1; + if (lenTest2 >= 2) + { + UInt32 state2 = kLiteralNextStates[state]; + UInt32 posStateNext = (position + 1) & p->pbMask; + UInt32 nextRepMatchPrice = curAnd1Price + + GET_PRICE_1(p->isMatch[state2][posStateNext]) + + GET_PRICE_1(p->isRep[state2]); + /* for (; lenTest2 >= 2; lenTest2--) */ + { + UInt32 curAndLenPrice; + COptimal *opt; + UInt32 offset = cur + 1 + lenTest2; + while (lenEnd < offset) + p->opt[++lenEnd].price = kInfinityPrice; + curAndLenPrice = nextRepMatchPrice + GetRepPrice(p, 0, lenTest2, state2, posStateNext); + opt = &p->opt[offset]; + if (curAndLenPrice < opt->price) + { + opt->price = curAndLenPrice; + opt->posPrev = cur + 1; + opt->backPrev = 0; + opt->prev1IsChar = True; + opt->prev2 = False; + } + } + } + } + + startLen = 2; /* speed optimization */ + { + UInt32 repIndex; + for (repIndex = 0; repIndex < LZMA_NUM_REPS; repIndex++) + { + UInt32 lenTest; + UInt32 lenTestTemp; + UInt32 price; + const Byte *data2 = data - reps[repIndex] - 1; + if (data[0] != data2[0] || data[1] != data2[1]) + continue; + for (lenTest = 2; lenTest < numAvail && data[lenTest] == data2[lenTest]; lenTest++); + while (lenEnd < cur + lenTest) + p->opt[++lenEnd].price = kInfinityPrice; + lenTestTemp = lenTest; + price = repMatchPrice + GetPureRepPrice(p, repIndex, state, posState); + do + { + UInt32 curAndLenPrice = price + p->repLenEnc.prices[posState][lenTest - 2]; + COptimal *opt = &p->opt[cur + lenTest]; + if (curAndLenPrice < opt->price) + { + opt->price = curAndLenPrice; + opt->posPrev = cur; + opt->backPrev = repIndex; + opt->prev1IsChar = False; + } + } + while (--lenTest >= 2); + lenTest = lenTestTemp; + + if (repIndex == 0) + startLen = lenTest + 1; + + /* if (_maxMode) */ + { + UInt32 lenTest2 = lenTest + 1; + UInt32 limit = lenTest2 + p->numFastBytes; + if (limit > numAvailFull) + limit = numAvailFull; + for (; lenTest2 < limit && data[lenTest2] == data2[lenTest2]; lenTest2++); + lenTest2 -= lenTest + 1; + if (lenTest2 >= 2) + { + UInt32 nextRepMatchPrice; + UInt32 state2 = kRepNextStates[state]; + UInt32 posStateNext = (position + lenTest) & p->pbMask; + UInt32 curAndLenCharPrice = + price + p->repLenEnc.prices[posState][lenTest - 2] + + GET_PRICE_0(p->isMatch[state2][posStateNext]) + + LitEnc_GetPriceMatched(LIT_PROBS(position + lenTest, data[lenTest - 1]), + data[lenTest], data2[lenTest], p->ProbPrices); + state2 = kLiteralNextStates[state2]; + posStateNext = (position + lenTest + 1) & p->pbMask; + nextRepMatchPrice = curAndLenCharPrice + + GET_PRICE_1(p->isMatch[state2][posStateNext]) + + GET_PRICE_1(p->isRep[state2]); + + /* for (; lenTest2 >= 2; lenTest2--) */ + { + UInt32 curAndLenPrice; + COptimal *opt; + UInt32 offset = cur + lenTest + 1 + lenTest2; + while (lenEnd < offset) + p->opt[++lenEnd].price = kInfinityPrice; + curAndLenPrice = nextRepMatchPrice + GetRepPrice(p, 0, lenTest2, state2, posStateNext); + opt = &p->opt[offset]; + if (curAndLenPrice < opt->price) + { + opt->price = curAndLenPrice; + opt->posPrev = cur + lenTest + 1; + opt->backPrev = 0; + opt->prev1IsChar = True; + opt->prev2 = True; + opt->posPrev2 = cur; + opt->backPrev2 = repIndex; + } + } + } + } + } + } + /* for (UInt32 lenTest = 2; lenTest <= newLen; lenTest++) */ + if (newLen > numAvail) + { + newLen = numAvail; + for (numPairs = 0; newLen > matches[numPairs]; numPairs += 2); + matches[numPairs] = newLen; + numPairs += 2; + } + if (newLen >= startLen) + { + UInt32 normalMatchPrice = matchPrice + GET_PRICE_0(p->isRep[state]); + UInt32 offs, curBack, posSlot; + UInt32 lenTest; + while (lenEnd < cur + newLen) + p->opt[++lenEnd].price = kInfinityPrice; + + offs = 0; + while (startLen > matches[offs]) + offs += 2; + curBack = matches[offs + 1]; + GetPosSlot2(curBack, posSlot); + for (lenTest = /*2*/ startLen; ; lenTest++) + { + UInt32 curAndLenPrice = normalMatchPrice + p->lenEnc.prices[posState][lenTest - LZMA_MATCH_LEN_MIN]; + { + UInt32 lenToPosState = GetLenToPosState(lenTest); + COptimal *opt; + if (curBack < kNumFullDistances) + curAndLenPrice += p->distancesPrices[lenToPosState][curBack]; + else + curAndLenPrice += p->posSlotPrices[lenToPosState][posSlot] + p->alignPrices[curBack & kAlignMask]; + + opt = &p->opt[cur + lenTest]; + if (curAndLenPrice < opt->price) + { + opt->price = curAndLenPrice; + opt->posPrev = cur; + opt->backPrev = curBack + LZMA_NUM_REPS; + opt->prev1IsChar = False; + } + } + + if (/*_maxMode && */lenTest == matches[offs]) + { + /* Try Match + Literal + Rep0 */ + const Byte *data2 = data - curBack - 1; + UInt32 lenTest2 = lenTest + 1; + UInt32 limit = lenTest2 + p->numFastBytes; + if (limit > numAvailFull) + limit = numAvailFull; + for (; lenTest2 < limit && data[lenTest2] == data2[lenTest2]; lenTest2++); + lenTest2 -= lenTest + 1; + if (lenTest2 >= 2) + { + UInt32 nextRepMatchPrice; + UInt32 state2 = kMatchNextStates[state]; + UInt32 posStateNext = (position + lenTest) & p->pbMask; + UInt32 curAndLenCharPrice = curAndLenPrice + + GET_PRICE_0(p->isMatch[state2][posStateNext]) + + LitEnc_GetPriceMatched(LIT_PROBS(position + lenTest, data[lenTest - 1]), + data[lenTest], data2[lenTest], p->ProbPrices); + state2 = kLiteralNextStates[state2]; + posStateNext = (posStateNext + 1) & p->pbMask; + nextRepMatchPrice = curAndLenCharPrice + + GET_PRICE_1(p->isMatch[state2][posStateNext]) + + GET_PRICE_1(p->isRep[state2]); + + /* for (; lenTest2 >= 2; lenTest2--) */ + { + UInt32 offset = cur + lenTest + 1 + lenTest2; + UInt32 curAndLenPrice2; + COptimal *opt; + while (lenEnd < offset) + p->opt[++lenEnd].price = kInfinityPrice; + curAndLenPrice2 = nextRepMatchPrice + GetRepPrice(p, 0, lenTest2, state2, posStateNext); + opt = &p->opt[offset]; + if (curAndLenPrice2 < opt->price) + { + opt->price = curAndLenPrice2; + opt->posPrev = cur + lenTest + 1; + opt->backPrev = 0; + opt->prev1IsChar = True; + opt->prev2 = True; + opt->posPrev2 = cur; + opt->backPrev2 = curBack + LZMA_NUM_REPS; + } + } + } + offs += 2; + if (offs == numPairs) + break; + curBack = matches[offs + 1]; + if (curBack >= kNumFullDistances) + GetPosSlot2(curBack, posSlot); + } + } + } + } +} + +#define ChangePair(smallDist, bigDist) (((bigDist) >> 7) > (smallDist)) + +static UInt32 GetOptimumFast(CLzmaEnc *p, UInt32 *backRes) +{ + UInt32 numAvail, mainLen, mainDist, numPairs, repIndex, repLen, i; + const Byte *data; + const UInt32 *matches; + + if (p->additionalOffset == 0) + mainLen = ReadMatchDistances(p, &numPairs); + else + { + mainLen = p->longestMatchLength; + numPairs = p->numPairs; + } + + numAvail = p->numAvail; + *backRes = (UInt32)-1; + if (numAvail < 2) + return 1; + if (numAvail > LZMA_MATCH_LEN_MAX) + numAvail = LZMA_MATCH_LEN_MAX; + data = p->matchFinder.GetPointerToCurrentPos(p->matchFinderObj) - 1; + + repLen = repIndex = 0; + for (i = 0; i < LZMA_NUM_REPS; i++) + { + UInt32 len; + const Byte *data2 = data - p->reps[i] - 1; + if (data[0] != data2[0] || data[1] != data2[1]) + continue; + for (len = 2; len < numAvail && data[len] == data2[len]; len++); + if (len >= p->numFastBytes) + { + *backRes = i; + MovePos(p, len - 1); + return len; + } + if (len > repLen) + { + repIndex = i; + repLen = len; + } + } + + matches = p->matches; + if (mainLen >= p->numFastBytes) + { + *backRes = matches[numPairs - 1] + LZMA_NUM_REPS; + MovePos(p, mainLen - 1); + return mainLen; + } + + mainDist = 0; /* for GCC */ + if (mainLen >= 2) + { + mainDist = matches[numPairs - 1]; + while (numPairs > 2 && mainLen == matches[numPairs - 4] + 1) + { + if (!ChangePair(matches[numPairs - 3], mainDist)) + break; + numPairs -= 2; + mainLen = matches[numPairs - 2]; + mainDist = matches[numPairs - 1]; + } + if (mainLen == 2 && mainDist >= 0x80) + mainLen = 1; + } + + if (repLen >= 2 && ( + (repLen + 1 >= mainLen) || + (repLen + 2 >= mainLen && mainDist >= (1 << 9)) || + (repLen + 3 >= mainLen && mainDist >= (1 << 15)))) + { + *backRes = repIndex; + MovePos(p, repLen - 1); + return repLen; + } + + if (mainLen < 2 || numAvail <= 2) + return 1; + + p->longestMatchLength = ReadMatchDistances(p, &p->numPairs); + if (p->longestMatchLength >= 2) + { + UInt32 newDistance = matches[p->numPairs - 1]; + if ((p->longestMatchLength >= mainLen && newDistance < mainDist) || + (p->longestMatchLength == mainLen + 1 && !ChangePair(mainDist, newDistance)) || + (p->longestMatchLength > mainLen + 1) || + (p->longestMatchLength + 1 >= mainLen && mainLen >= 3 && ChangePair(newDistance, mainDist))) + return 1; + } + + data = p->matchFinder.GetPointerToCurrentPos(p->matchFinderObj) - 1; + for (i = 0; i < LZMA_NUM_REPS; i++) + { + UInt32 len, limit; + const Byte *data2 = data - p->reps[i] - 1; + if (data[0] != data2[0] || data[1] != data2[1]) + continue; + limit = mainLen - 1; + for (len = 2; len < limit && data[len] == data2[len]; len++); + if (len >= limit) + return 1; + } + *backRes = mainDist + LZMA_NUM_REPS; + MovePos(p, mainLen - 2); + return mainLen; +} + +static void WriteEndMarker(CLzmaEnc *p, UInt32 posState) +{ + UInt32 len; + RangeEnc_EncodeBit(&p->rc, &p->isMatch[p->state][posState], 1); + RangeEnc_EncodeBit(&p->rc, &p->isRep[p->state], 0); + p->state = kMatchNextStates[p->state]; + len = LZMA_MATCH_LEN_MIN; + LenEnc_Encode2(&p->lenEnc, &p->rc, len - LZMA_MATCH_LEN_MIN, posState, !p->fastMode, p->ProbPrices); + RcTree_Encode(&p->rc, p->posSlotEncoder[GetLenToPosState(len)], kNumPosSlotBits, (1 << kNumPosSlotBits) - 1); + RangeEnc_EncodeDirectBits(&p->rc, (((UInt32)1 << 30) - 1) >> kNumAlignBits, 30 - kNumAlignBits); + RcTree_ReverseEncode(&p->rc, p->posAlignEncoder, kNumAlignBits, kAlignMask); +} + +static SRes CheckErrors(CLzmaEnc *p) +{ + if (p->result != SZ_OK) + return p->result; + if (p->rc.res != SZ_OK) + p->result = SZ_ERROR_WRITE; + if (p->matchFinderBase.result != SZ_OK) + p->result = SZ_ERROR_READ; + if (p->result != SZ_OK) + p->finished = True; + return p->result; +} + +static SRes Flush(CLzmaEnc *p, UInt32 nowPos) +{ + /* ReleaseMFStream(); */ + p->finished = True; + if (p->writeEndMark) + WriteEndMarker(p, nowPos & p->pbMask); + RangeEnc_FlushData(&p->rc); + RangeEnc_FlushStream(&p->rc); + return CheckErrors(p); +} + +static void FillAlignPrices(CLzmaEnc *p) +{ + UInt32 i; + for (i = 0; i < kAlignTableSize; i++) + p->alignPrices[i] = RcTree_ReverseGetPrice(p->posAlignEncoder, kNumAlignBits, i, p->ProbPrices); + p->alignPriceCount = 0; +} + +static void FillDistancesPrices(CLzmaEnc *p) +{ + UInt32 tempPrices[kNumFullDistances]; + UInt32 i, lenToPosState; + for (i = kStartPosModelIndex; i < kNumFullDistances; i++) + { + UInt32 posSlot = GetPosSlot1(i); + UInt32 footerBits = ((posSlot >> 1) - 1); + UInt32 base = ((2 | (posSlot & 1)) << footerBits); + tempPrices[i] = RcTree_ReverseGetPrice(p->posEncoders + base - posSlot - 1, footerBits, i - base, p->ProbPrices); + } + + for (lenToPosState = 0; lenToPosState < kNumLenToPosStates; lenToPosState++) + { + UInt32 posSlot; + const CLzmaProb *encoder = p->posSlotEncoder[lenToPosState]; + UInt32 *posSlotPrices = p->posSlotPrices[lenToPosState]; + for (posSlot = 0; posSlot < p->distTableSize; posSlot++) + posSlotPrices[posSlot] = RcTree_GetPrice(encoder, kNumPosSlotBits, posSlot, p->ProbPrices); + for (posSlot = kEndPosModelIndex; posSlot < p->distTableSize; posSlot++) + posSlotPrices[posSlot] += ((((posSlot >> 1) - 1) - kNumAlignBits) << kNumBitPriceShiftBits); + + { + UInt32 *distancesPrices = p->distancesPrices[lenToPosState]; + for (i = 0; i < kStartPosModelIndex; i++) + distancesPrices[i] = posSlotPrices[i]; + for (; i < kNumFullDistances; i++) + distancesPrices[i] = posSlotPrices[GetPosSlot1(i)] + tempPrices[i]; + } + } + p->matchPriceCount = 0; +} + +void LzmaEnc_Construct(CLzmaEnc *p) +{ + RangeEnc_Construct(&p->rc); + MatchFinder_Construct(&p->matchFinderBase); + + #ifndef _7ZIP_ST + MatchFinderMt_Construct(&p->matchFinderMt); + p->matchFinderMt.MatchFinder = &p->matchFinderBase; + #endif + + { + CLzmaEncProps props; + LzmaEncProps_Init(&props); + LzmaEnc_SetProps(p, &props); + } + + #ifndef LZMA_LOG_BSR + LzmaEnc_FastPosInit(p->g_FastPos); + #endif + + LzmaEnc_InitPriceTables(p->ProbPrices); + p->litProbs = NULL; + p->saveState.litProbs = NULL; +} + +CLzmaEncHandle LzmaEnc_Create(ISzAlloc *alloc) +{ + void *p; + p = alloc->Alloc(alloc, sizeof(CLzmaEnc)); + if (p) + LzmaEnc_Construct((CLzmaEnc *)p); + return p; +} + +void LzmaEnc_FreeLits(CLzmaEnc *p, ISzAlloc *alloc) +{ + alloc->Free(alloc, p->litProbs); + alloc->Free(alloc, p->saveState.litProbs); + p->litProbs = NULL; + p->saveState.litProbs = NULL; +} + +void LzmaEnc_Destruct(CLzmaEnc *p, ISzAlloc *alloc, ISzAlloc *allocBig) +{ + #ifndef _7ZIP_ST + MatchFinderMt_Destruct(&p->matchFinderMt, allocBig); + #endif + + MatchFinder_Free(&p->matchFinderBase, allocBig); + LzmaEnc_FreeLits(p, alloc); + RangeEnc_Free(&p->rc, alloc); +} + +void LzmaEnc_Destroy(CLzmaEncHandle p, ISzAlloc *alloc, ISzAlloc *allocBig) +{ + LzmaEnc_Destruct((CLzmaEnc *)p, alloc, allocBig); + alloc->Free(alloc, p); +} + +static SRes LzmaEnc_CodeOneBlock(CLzmaEnc *p, Bool useLimits, UInt32 maxPackSize, UInt32 maxUnpackSize) +{ + UInt32 nowPos32, startPos32; + if (p->needInit) + { + p->matchFinder.Init(p->matchFinderObj); + p->needInit = 0; + } + + if (p->finished) + return p->result; + RINOK(CheckErrors(p)); + + nowPos32 = (UInt32)p->nowPos64; + startPos32 = nowPos32; + + if (p->nowPos64 == 0) + { + UInt32 numPairs; + Byte curByte; + if (p->matchFinder.GetNumAvailableBytes(p->matchFinderObj) == 0) + return Flush(p, nowPos32); + ReadMatchDistances(p, &numPairs); + RangeEnc_EncodeBit(&p->rc, &p->isMatch[p->state][0], 0); + p->state = kLiteralNextStates[p->state]; + curByte = *(p->matchFinder.GetPointerToCurrentPos(p->matchFinderObj) - p->additionalOffset); + LitEnc_Encode(&p->rc, p->litProbs, curByte); + p->additionalOffset--; + nowPos32++; + } + + if (p->matchFinder.GetNumAvailableBytes(p->matchFinderObj) != 0) + for (;;) + { + UInt32 pos, len, posState; + + if (p->fastMode) + len = GetOptimumFast(p, &pos); + else + len = GetOptimum(p, nowPos32, &pos); + + #ifdef SHOW_STAT2 + printf("\n pos = %4X, len = %u pos = %u", nowPos32, len, pos); + #endif + + posState = nowPos32 & p->pbMask; + if (len == 1 && pos == (UInt32)-1) + { + Byte curByte; + CLzmaProb *probs; + const Byte *data; + + RangeEnc_EncodeBit(&p->rc, &p->isMatch[p->state][posState], 0); + data = p->matchFinder.GetPointerToCurrentPos(p->matchFinderObj) - p->additionalOffset; + curByte = *data; + probs = LIT_PROBS(nowPos32, *(data - 1)); + if (IsCharState(p->state)) + LitEnc_Encode(&p->rc, probs, curByte); + else + LitEnc_EncodeMatched(&p->rc, probs, curByte, *(data - p->reps[0] - 1)); + p->state = kLiteralNextStates[p->state]; + } + else + { + RangeEnc_EncodeBit(&p->rc, &p->isMatch[p->state][posState], 1); + if (pos < LZMA_NUM_REPS) + { + RangeEnc_EncodeBit(&p->rc, &p->isRep[p->state], 1); + if (pos == 0) + { + RangeEnc_EncodeBit(&p->rc, &p->isRepG0[p->state], 0); + RangeEnc_EncodeBit(&p->rc, &p->isRep0Long[p->state][posState], ((len == 1) ? 0 : 1)); + } + else + { + UInt32 distance = p->reps[pos]; + RangeEnc_EncodeBit(&p->rc, &p->isRepG0[p->state], 1); + if (pos == 1) + RangeEnc_EncodeBit(&p->rc, &p->isRepG1[p->state], 0); + else + { + RangeEnc_EncodeBit(&p->rc, &p->isRepG1[p->state], 1); + RangeEnc_EncodeBit(&p->rc, &p->isRepG2[p->state], pos - 2); + if (pos == 3) + p->reps[3] = p->reps[2]; + p->reps[2] = p->reps[1]; + } + p->reps[1] = p->reps[0]; + p->reps[0] = distance; + } + if (len == 1) + p->state = kShortRepNextStates[p->state]; + else + { + LenEnc_Encode2(&p->repLenEnc, &p->rc, len - LZMA_MATCH_LEN_MIN, posState, !p->fastMode, p->ProbPrices); + p->state = kRepNextStates[p->state]; + } + } + else + { + UInt32 posSlot; + RangeEnc_EncodeBit(&p->rc, &p->isRep[p->state], 0); + p->state = kMatchNextStates[p->state]; + LenEnc_Encode2(&p->lenEnc, &p->rc, len - LZMA_MATCH_LEN_MIN, posState, !p->fastMode, p->ProbPrices); + pos -= LZMA_NUM_REPS; + GetPosSlot(pos, posSlot); + RcTree_Encode(&p->rc, p->posSlotEncoder[GetLenToPosState(len)], kNumPosSlotBits, posSlot); + + if (posSlot >= kStartPosModelIndex) + { + UInt32 footerBits = ((posSlot >> 1) - 1); + UInt32 base = ((2 | (posSlot & 1)) << footerBits); + UInt32 posReduced = pos - base; + + if (posSlot < kEndPosModelIndex) + RcTree_ReverseEncode(&p->rc, p->posEncoders + base - posSlot - 1, footerBits, posReduced); + else + { + RangeEnc_EncodeDirectBits(&p->rc, posReduced >> kNumAlignBits, footerBits - kNumAlignBits); + RcTree_ReverseEncode(&p->rc, p->posAlignEncoder, kNumAlignBits, posReduced & kAlignMask); + p->alignPriceCount++; + } + } + p->reps[3] = p->reps[2]; + p->reps[2] = p->reps[1]; + p->reps[1] = p->reps[0]; + p->reps[0] = pos; + p->matchPriceCount++; + } + } + p->additionalOffset -= len; + nowPos32 += len; + if (p->additionalOffset == 0) + { + UInt32 processed; + if (!p->fastMode) + { + if (p->matchPriceCount >= (1 << 7)) + FillDistancesPrices(p); + if (p->alignPriceCount >= kAlignTableSize) + FillAlignPrices(p); + } + if (p->matchFinder.GetNumAvailableBytes(p->matchFinderObj) == 0) + break; + processed = nowPos32 - startPos32; + if (useLimits) + { + if (processed + kNumOpts + 300 >= maxUnpackSize || + RangeEnc_GetProcessed(&p->rc) + kNumOpts * 2 >= maxPackSize) + break; + } + else if (processed >= (1 << 17)) + { + p->nowPos64 += nowPos32 - startPos32; + return CheckErrors(p); + } + } + } + p->nowPos64 += nowPos32 - startPos32; + return Flush(p, nowPos32); +} + +#define kBigHashDicLimit ((UInt32)1 << 24) + +static SRes LzmaEnc_Alloc(CLzmaEnc *p, UInt32 keepWindowSize, ISzAlloc *alloc, ISzAlloc *allocBig) +{ + UInt32 beforeSize = kNumOpts; + if (!RangeEnc_Alloc(&p->rc, alloc)) + return SZ_ERROR_MEM; + + #ifndef _7ZIP_ST + p->mtMode = (p->multiThread && !p->fastMode && (p->matchFinderBase.btMode != 0)); + #endif + + { + unsigned lclp = p->lc + p->lp; + if (!p->litProbs || !p->saveState.litProbs || p->lclp != lclp) + { + LzmaEnc_FreeLits(p, alloc); + p->litProbs = (CLzmaProb *)alloc->Alloc(alloc, ((UInt32)0x300 << lclp) * sizeof(CLzmaProb)); + p->saveState.litProbs = (CLzmaProb *)alloc->Alloc(alloc, ((UInt32)0x300 << lclp) * sizeof(CLzmaProb)); + if (!p->litProbs || !p->saveState.litProbs) + { + LzmaEnc_FreeLits(p, alloc); + return SZ_ERROR_MEM; + } + p->lclp = lclp; + } + } + + p->matchFinderBase.bigHash = (Byte)(p->dictSize > kBigHashDicLimit ? 1 : 0); + + if (beforeSize + p->dictSize < keepWindowSize) + beforeSize = keepWindowSize - p->dictSize; + + #ifndef _7ZIP_ST + if (p->mtMode) + { + RINOK(MatchFinderMt_Create(&p->matchFinderMt, p->dictSize, beforeSize, p->numFastBytes, LZMA_MATCH_LEN_MAX, allocBig)); + p->matchFinderObj = &p->matchFinderMt; + MatchFinderMt_CreateVTable(&p->matchFinderMt, &p->matchFinder); + } + else + #endif + { + if (!MatchFinder_Create(&p->matchFinderBase, p->dictSize, beforeSize, p->numFastBytes, LZMA_MATCH_LEN_MAX, allocBig)) + return SZ_ERROR_MEM; + p->matchFinderObj = &p->matchFinderBase; + MatchFinder_CreateVTable(&p->matchFinderBase, &p->matchFinder); + } + + return SZ_OK; +} + +void LzmaEnc_Init(CLzmaEnc *p) +{ + UInt32 i; + p->state = 0; + for (i = 0 ; i < LZMA_NUM_REPS; i++) + p->reps[i] = 0; + + RangeEnc_Init(&p->rc); + + + for (i = 0; i < kNumStates; i++) + { + UInt32 j; + for (j = 0; j < LZMA_NUM_PB_STATES_MAX; j++) + { + p->isMatch[i][j] = kProbInitValue; + p->isRep0Long[i][j] = kProbInitValue; + } + p->isRep[i] = kProbInitValue; + p->isRepG0[i] = kProbInitValue; + p->isRepG1[i] = kProbInitValue; + p->isRepG2[i] = kProbInitValue; + } + + { + UInt32 num = (UInt32)0x300 << (p->lp + p->lc); + CLzmaProb *probs = p->litProbs; + for (i = 0; i < num; i++) + probs[i] = kProbInitValue; + } + + { + for (i = 0; i < kNumLenToPosStates; i++) + { + CLzmaProb *probs = p->posSlotEncoder[i]; + UInt32 j; + for (j = 0; j < (1 << kNumPosSlotBits); j++) + probs[j] = kProbInitValue; + } + } + { + for (i = 0; i < kNumFullDistances - kEndPosModelIndex; i++) + p->posEncoders[i] = kProbInitValue; + } + + LenEnc_Init(&p->lenEnc.p); + LenEnc_Init(&p->repLenEnc.p); + + for (i = 0; i < (1 << kNumAlignBits); i++) + p->posAlignEncoder[i] = kProbInitValue; + + p->optimumEndIndex = 0; + p->optimumCurrentIndex = 0; + p->additionalOffset = 0; + + p->pbMask = (1 << p->pb) - 1; + p->lpMask = (1 << p->lp) - 1; +} + +void LzmaEnc_InitPrices(CLzmaEnc *p) +{ + if (!p->fastMode) + { + FillDistancesPrices(p); + FillAlignPrices(p); + } + + p->lenEnc.tableSize = + p->repLenEnc.tableSize = + p->numFastBytes + 1 - LZMA_MATCH_LEN_MIN; + LenPriceEnc_UpdateTables(&p->lenEnc, 1 << p->pb, p->ProbPrices); + LenPriceEnc_UpdateTables(&p->repLenEnc, 1 << p->pb, p->ProbPrices); +} + +static SRes LzmaEnc_AllocAndInit(CLzmaEnc *p, UInt32 keepWindowSize, ISzAlloc *alloc, ISzAlloc *allocBig) +{ + UInt32 i; + for (i = 0; i < (UInt32)kDicLogSizeMaxCompress; i++) + if (p->dictSize <= ((UInt32)1 << i)) + break; + p->distTableSize = i * 2; + + p->finished = False; + p->result = SZ_OK; + RINOK(LzmaEnc_Alloc(p, keepWindowSize, alloc, allocBig)); + LzmaEnc_Init(p); + LzmaEnc_InitPrices(p); + p->nowPos64 = 0; + return SZ_OK; +} + +static SRes LzmaEnc_Prepare(CLzmaEncHandle pp, ISeqOutStream *outStream, ISeqInStream *inStream, + ISzAlloc *alloc, ISzAlloc *allocBig) +{ + CLzmaEnc *p = (CLzmaEnc *)pp; + p->matchFinderBase.stream = inStream; + p->needInit = 1; + p->rc.outStream = outStream; + return LzmaEnc_AllocAndInit(p, 0, alloc, allocBig); +} + +SRes LzmaEnc_PrepareForLzma2(CLzmaEncHandle pp, + ISeqInStream *inStream, UInt32 keepWindowSize, + ISzAlloc *alloc, ISzAlloc *allocBig) +{ + CLzmaEnc *p = (CLzmaEnc *)pp; + p->matchFinderBase.stream = inStream; + p->needInit = 1; + return LzmaEnc_AllocAndInit(p, keepWindowSize, alloc, allocBig); +} + +static void LzmaEnc_SetInputBuf(CLzmaEnc *p, const Byte *src, SizeT srcLen) +{ + p->matchFinderBase.directInput = 1; + p->matchFinderBase.bufferBase = (Byte *)src; + p->matchFinderBase.directInputRem = srcLen; +} + +SRes LzmaEnc_MemPrepare(CLzmaEncHandle pp, const Byte *src, SizeT srcLen, + UInt32 keepWindowSize, ISzAlloc *alloc, ISzAlloc *allocBig) +{ + CLzmaEnc *p = (CLzmaEnc *)pp; + LzmaEnc_SetInputBuf(p, src, srcLen); + p->needInit = 1; + + return LzmaEnc_AllocAndInit(p, keepWindowSize, alloc, allocBig); +} + +void LzmaEnc_Finish(CLzmaEncHandle pp) +{ + #ifndef _7ZIP_ST + CLzmaEnc *p = (CLzmaEnc *)pp; + if (p->mtMode) + MatchFinderMt_ReleaseStream(&p->matchFinderMt); + #else + UNUSED_VAR(pp); + #endif +} + + +typedef struct +{ + ISeqOutStream funcTable; + Byte *data; + SizeT rem; + Bool overflow; +} CSeqOutStreamBuf; + +static size_t MyWrite(void *pp, const void *data, size_t size) +{ + CSeqOutStreamBuf *p = (CSeqOutStreamBuf *)pp; + if (p->rem < size) + { + size = p->rem; + p->overflow = True; + } + memcpy(p->data, data, size); + p->rem -= size; + p->data += size; + return size; +} + + +UInt32 LzmaEnc_GetNumAvailableBytes(CLzmaEncHandle pp) +{ + const CLzmaEnc *p = (CLzmaEnc *)pp; + return p->matchFinder.GetNumAvailableBytes(p->matchFinderObj); +} + + +const Byte *LzmaEnc_GetCurBuf(CLzmaEncHandle pp) +{ + const CLzmaEnc *p = (CLzmaEnc *)pp; + return p->matchFinder.GetPointerToCurrentPos(p->matchFinderObj) - p->additionalOffset; +} + + +SRes LzmaEnc_CodeOneMemBlock(CLzmaEncHandle pp, Bool reInit, + Byte *dest, size_t *destLen, UInt32 desiredPackSize, UInt32 *unpackSize) +{ + CLzmaEnc *p = (CLzmaEnc *)pp; + UInt64 nowPos64; + SRes res; + CSeqOutStreamBuf outStream; + + outStream.funcTable.Write = MyWrite; + outStream.data = dest; + outStream.rem = *destLen; + outStream.overflow = False; + + p->writeEndMark = False; + p->finished = False; + p->result = SZ_OK; + + if (reInit) + LzmaEnc_Init(p); + LzmaEnc_InitPrices(p); + nowPos64 = p->nowPos64; + RangeEnc_Init(&p->rc); + p->rc.outStream = &outStream.funcTable; + + res = LzmaEnc_CodeOneBlock(p, True, desiredPackSize, *unpackSize); + + *unpackSize = (UInt32)(p->nowPos64 - nowPos64); + *destLen -= outStream.rem; + if (outStream.overflow) + return SZ_ERROR_OUTPUT_EOF; + + return res; +} + + +static SRes LzmaEnc_Encode2(CLzmaEnc *p, ICompressProgress *progress) +{ + SRes res = SZ_OK; + + #ifndef _7ZIP_ST + Byte allocaDummy[0x300]; + allocaDummy[0] = 0; + allocaDummy[1] = allocaDummy[0]; + #endif + + for (;;) + { + res = LzmaEnc_CodeOneBlock(p, False, 0, 0); + if (res != SZ_OK || p->finished) + break; + if (progress) + { + res = progress->Progress(progress, p->nowPos64, RangeEnc_GetProcessed(&p->rc)); + if (res != SZ_OK) + { + res = SZ_ERROR_PROGRESS; + break; + } + } + } + + LzmaEnc_Finish(p); + + /* + if (res == S_OK && !Inline_MatchFinder_IsFinishedOK(&p->matchFinderBase)) + res = SZ_ERROR_FAIL; + } + */ + + return res; +} + + +SRes LzmaEnc_Encode(CLzmaEncHandle pp, ISeqOutStream *outStream, ISeqInStream *inStream, ICompressProgress *progress, + ISzAlloc *alloc, ISzAlloc *allocBig) +{ + RINOK(LzmaEnc_Prepare(pp, outStream, inStream, alloc, allocBig)); + return LzmaEnc_Encode2((CLzmaEnc *)pp, progress); +} + + +SRes LzmaEnc_WriteProperties(CLzmaEncHandle pp, Byte *props, SizeT *size) +{ + CLzmaEnc *p = (CLzmaEnc *)pp; + unsigned i; + UInt32 dictSize = p->dictSize; + if (*size < LZMA_PROPS_SIZE) + return SZ_ERROR_PARAM; + *size = LZMA_PROPS_SIZE; + props[0] = (Byte)((p->pb * 5 + p->lp) * 9 + p->lc); + + if (dictSize >= ((UInt32)1 << 22)) + { + UInt32 kDictMask = ((UInt32)1 << 20) - 1; + if (dictSize < (UInt32)0xFFFFFFFF - kDictMask) + dictSize = (dictSize + kDictMask) & ~kDictMask; + } + else for (i = 11; i <= 30; i++) + { + if (dictSize <= ((UInt32)2 << i)) { dictSize = (2 << i); break; } + if (dictSize <= ((UInt32)3 << i)) { dictSize = (3 << i); break; } + } + + for (i = 0; i < 4; i++) + props[1 + i] = (Byte)(dictSize >> (8 * i)); + return SZ_OK; +} + + +SRes LzmaEnc_MemEncode(CLzmaEncHandle pp, Byte *dest, SizeT *destLen, const Byte *src, SizeT srcLen, + int writeEndMark, ICompressProgress *progress, ISzAlloc *alloc, ISzAlloc *allocBig) +{ + SRes res; + CLzmaEnc *p = (CLzmaEnc *)pp; + + CSeqOutStreamBuf outStream; + + outStream.funcTable.Write = MyWrite; + outStream.data = dest; + outStream.rem = *destLen; + outStream.overflow = False; + + p->writeEndMark = writeEndMark; + p->rc.outStream = &outStream.funcTable; + + res = LzmaEnc_MemPrepare(pp, src, srcLen, 0, alloc, allocBig); + + if (res == SZ_OK) + { + res = LzmaEnc_Encode2(p, progress); + if (res == SZ_OK && p->nowPos64 != srcLen) + res = SZ_ERROR_FAIL; + } + + *destLen -= outStream.rem; + if (outStream.overflow) + return SZ_ERROR_OUTPUT_EOF; + return res; +} + + +SRes LzmaEncode(Byte *dest, SizeT *destLen, const Byte *src, SizeT srcLen, + const CLzmaEncProps *props, Byte *propsEncoded, SizeT *propsSize, int writeEndMark, + ICompressProgress *progress, ISzAlloc *alloc, ISzAlloc *allocBig) +{ + CLzmaEnc *p = (CLzmaEnc *)LzmaEnc_Create(alloc); + SRes res; + if (!p) + return SZ_ERROR_MEM; + + res = LzmaEnc_SetProps(p, props); + if (res == SZ_OK) + { + res = LzmaEnc_WriteProperties(p, propsEncoded, propsSize); + if (res == SZ_OK) + res = LzmaEnc_MemEncode(p, dest, destLen, src, srcLen, + writeEndMark, progress, alloc, allocBig); + } + + LzmaEnc_Destroy(p, alloc, allocBig); + return res; +} diff --git a/core/deps/lzma/LzmaEnc.h b/core/deps/lzma/LzmaEnc.h new file mode 100644 index 000000000..c2806b45f --- /dev/null +++ b/core/deps/lzma/LzmaEnc.h @@ -0,0 +1,78 @@ +/* LzmaEnc.h -- LZMA Encoder +2013-01-18 : Igor Pavlov : Public domain */ + +#ifndef __LZMA_ENC_H +#define __LZMA_ENC_H + +#include "7zTypes.h" + +EXTERN_C_BEGIN + +#define LZMA_PROPS_SIZE 5 + +typedef struct _CLzmaEncProps +{ + int level; /* 0 <= level <= 9 */ + UInt32 dictSize; /* (1 << 12) <= dictSize <= (1 << 27) for 32-bit version + (1 << 12) <= dictSize <= (1 << 30) for 64-bit version + default = (1 << 24) */ + UInt64 reduceSize; /* estimated size of data that will be compressed. default = 0xFFFFFFFF. + Encoder uses this value to reduce dictionary size */ + int lc; /* 0 <= lc <= 8, default = 3 */ + int lp; /* 0 <= lp <= 4, default = 0 */ + int pb; /* 0 <= pb <= 4, default = 2 */ + int algo; /* 0 - fast, 1 - normal, default = 1 */ + int fb; /* 5 <= fb <= 273, default = 32 */ + int btMode; /* 0 - hashChain Mode, 1 - binTree mode - normal, default = 1 */ + int numHashBytes; /* 2, 3 or 4, default = 4 */ + UInt32 mc; /* 1 <= mc <= (1 << 30), default = 32 */ + unsigned writeEndMark; /* 0 - do not write EOPM, 1 - write EOPM, default = 0 */ + int numThreads; /* 1 or 2, default = 2 */ +} CLzmaEncProps; + +void LzmaEncProps_Init(CLzmaEncProps *p); +void LzmaEncProps_Normalize(CLzmaEncProps *p); +UInt32 LzmaEncProps_GetDictSize(const CLzmaEncProps *props2); + + +/* ---------- CLzmaEncHandle Interface ---------- */ + +/* LzmaEnc_* functions can return the following exit codes: +Returns: + SZ_OK - OK + SZ_ERROR_MEM - Memory allocation error + SZ_ERROR_PARAM - Incorrect paramater in props + SZ_ERROR_WRITE - Write callback error. + SZ_ERROR_PROGRESS - some break from progress callback + SZ_ERROR_THREAD - errors in multithreading functions (only for Mt version) +*/ + +typedef void * CLzmaEncHandle; + +CLzmaEncHandle LzmaEnc_Create(ISzAlloc *alloc); +void LzmaEnc_Destroy(CLzmaEncHandle p, ISzAlloc *alloc, ISzAlloc *allocBig); +SRes LzmaEnc_SetProps(CLzmaEncHandle p, const CLzmaEncProps *props); +SRes LzmaEnc_WriteProperties(CLzmaEncHandle p, Byte *properties, SizeT *size); +SRes LzmaEnc_Encode(CLzmaEncHandle p, ISeqOutStream *outStream, ISeqInStream *inStream, + ICompressProgress *progress, ISzAlloc *alloc, ISzAlloc *allocBig); +SRes LzmaEnc_MemEncode(CLzmaEncHandle p, Byte *dest, SizeT *destLen, const Byte *src, SizeT srcLen, + int writeEndMark, ICompressProgress *progress, ISzAlloc *alloc, ISzAlloc *allocBig); + +/* ---------- One Call Interface ---------- */ + +/* LzmaEncode +Return code: + SZ_OK - OK + SZ_ERROR_MEM - Memory allocation error + SZ_ERROR_PARAM - Incorrect paramater + SZ_ERROR_OUTPUT_EOF - output buffer overflow + SZ_ERROR_THREAD - errors in multithreading functions (only for Mt version) +*/ + +SRes LzmaEncode(Byte *dest, SizeT *destLen, const Byte *src, SizeT srcLen, + const CLzmaEncProps *props, Byte *propsEncoded, SizeT *propsSize, int writeEndMark, + ICompressProgress *progress, ISzAlloc *alloc, ISzAlloc *allocBig); + +EXTERN_C_END + +#endif diff --git a/core/deps/lzma/LzmaLib.c b/core/deps/lzma/LzmaLib.c new file mode 100644 index 000000000..c10cf1a0f --- /dev/null +++ b/core/deps/lzma/LzmaLib.c @@ -0,0 +1,40 @@ +/* LzmaLib.c -- LZMA library wrapper +2015-06-13 : Igor Pavlov : Public domain */ + +#include "Alloc.h" +#include "LzmaDec.h" +#include "LzmaEnc.h" +#include "LzmaLib.h" + +MY_STDAPI LzmaCompress(unsigned char *dest, size_t *destLen, const unsigned char *src, size_t srcLen, + unsigned char *outProps, size_t *outPropsSize, + int level, /* 0 <= level <= 9, default = 5 */ + unsigned dictSize, /* use (1 << N) or (3 << N). 4 KB < dictSize <= 128 MB */ + int lc, /* 0 <= lc <= 8, default = 3 */ + int lp, /* 0 <= lp <= 4, default = 0 */ + int pb, /* 0 <= pb <= 4, default = 2 */ + int fb, /* 5 <= fb <= 273, default = 32 */ + int numThreads /* 1 or 2, default = 2 */ +) +{ + CLzmaEncProps props; + LzmaEncProps_Init(&props); + props.level = level; + props.dictSize = dictSize; + props.lc = lc; + props.lp = lp; + props.pb = pb; + props.fb = fb; + props.numThreads = numThreads; + + return LzmaEncode(dest, destLen, src, srcLen, &props, outProps, outPropsSize, 0, + NULL, &g_Alloc, &g_Alloc); +} + + +MY_STDAPI LzmaUncompress(unsigned char *dest, size_t *destLen, const unsigned char *src, size_t *srcLen, + const unsigned char *props, size_t propsSize) +{ + ELzmaStatus status; + return LzmaDecode(dest, destLen, src, srcLen, props, (unsigned)propsSize, LZMA_FINISH_ANY, &status, &g_Alloc); +} diff --git a/core/deps/lzma/LzmaLib.h b/core/deps/lzma/LzmaLib.h new file mode 100644 index 000000000..5c35e5365 --- /dev/null +++ b/core/deps/lzma/LzmaLib.h @@ -0,0 +1,131 @@ +/* LzmaLib.h -- LZMA library interface +2013-01-18 : Igor Pavlov : Public domain */ + +#ifndef __LZMA_LIB_H +#define __LZMA_LIB_H + +#include "7zTypes.h" + +EXTERN_C_BEGIN + +#define MY_STDAPI int MY_STD_CALL + +#define LZMA_PROPS_SIZE 5 + +/* +RAM requirements for LZMA: + for compression: (dictSize * 11.5 + 6 MB) + state_size + for decompression: dictSize + state_size + state_size = (4 + (1.5 << (lc + lp))) KB + by default (lc=3, lp=0), state_size = 16 KB. + +LZMA properties (5 bytes) format + Offset Size Description + 0 1 lc, lp and pb in encoded form. + 1 4 dictSize (little endian). +*/ + +/* +LzmaCompress +------------ + +outPropsSize - + In: the pointer to the size of outProps buffer; *outPropsSize = LZMA_PROPS_SIZE = 5. + Out: the pointer to the size of written properties in outProps buffer; *outPropsSize = LZMA_PROPS_SIZE = 5. + + LZMA Encoder will use defult values for any parameter, if it is + -1 for any from: level, loc, lp, pb, fb, numThreads + 0 for dictSize + +level - compression level: 0 <= level <= 9; + + level dictSize algo fb + 0: 16 KB 0 32 + 1: 64 KB 0 32 + 2: 256 KB 0 32 + 3: 1 MB 0 32 + 4: 4 MB 0 32 + 5: 16 MB 1 32 + 6: 32 MB 1 32 + 7+: 64 MB 1 64 + + The default value for "level" is 5. + + algo = 0 means fast method + algo = 1 means normal method + +dictSize - The dictionary size in bytes. The maximum value is + 128 MB = (1 << 27) bytes for 32-bit version + 1 GB = (1 << 30) bytes for 64-bit version + The default value is 16 MB = (1 << 24) bytes. + It's recommended to use the dictionary that is larger than 4 KB and + that can be calculated as (1 << N) or (3 << N) sizes. + +lc - The number of literal context bits (high bits of previous literal). + It can be in the range from 0 to 8. The default value is 3. + Sometimes lc=4 gives the gain for big files. + +lp - The number of literal pos bits (low bits of current position for literals). + It can be in the range from 0 to 4. The default value is 0. + The lp switch is intended for periodical data when the period is equal to 2^lp. + For example, for 32-bit (4 bytes) periodical data you can use lp=2. Often it's + better to set lc=0, if you change lp switch. + +pb - The number of pos bits (low bits of current position). + It can be in the range from 0 to 4. The default value is 2. + The pb switch is intended for periodical data when the period is equal 2^pb. + +fb - Word size (the number of fast bytes). + It can be in the range from 5 to 273. The default value is 32. + Usually, a big number gives a little bit better compression ratio and + slower compression process. + +numThreads - The number of thereads. 1 or 2. The default value is 2. + Fast mode (algo = 0) can use only 1 thread. + +Out: + destLen - processed output size +Returns: + SZ_OK - OK + SZ_ERROR_MEM - Memory allocation error + SZ_ERROR_PARAM - Incorrect paramater + SZ_ERROR_OUTPUT_EOF - output buffer overflow + SZ_ERROR_THREAD - errors in multithreading functions (only for Mt version) +*/ + +MY_STDAPI LzmaCompress(unsigned char *dest, size_t *destLen, const unsigned char *src, size_t srcLen, + unsigned char *outProps, size_t *outPropsSize, /* *outPropsSize must be = 5 */ + int level, /* 0 <= level <= 9, default = 5 */ + unsigned dictSize, /* default = (1 << 24) */ + int lc, /* 0 <= lc <= 8, default = 3 */ + int lp, /* 0 <= lp <= 4, default = 0 */ + int pb, /* 0 <= pb <= 4, default = 2 */ + int fb, /* 5 <= fb <= 273, default = 32 */ + int numThreads /* 1 or 2, default = 2 */ + ); + +/* +LzmaUncompress +-------------- +In: + dest - output data + destLen - output data size + src - input data + srcLen - input data size +Out: + destLen - processed output size + srcLen - processed input size +Returns: + SZ_OK - OK + SZ_ERROR_DATA - Data error + SZ_ERROR_MEM - Memory allocation arror + SZ_ERROR_UNSUPPORTED - Unsupported properties + SZ_ERROR_INPUT_EOF - it needs more bytes in input buffer (src) +*/ + +MY_STDAPI LzmaUncompress(unsigned char *dest, size_t *destLen, const unsigned char *src, SizeT *srcLen, + const unsigned char *props, size_t propsSize); + +EXTERN_C_END + +#endif diff --git a/core/deps/lzma/Precomp.h b/core/deps/lzma/Precomp.h new file mode 100644 index 000000000..edb581443 --- /dev/null +++ b/core/deps/lzma/Precomp.h @@ -0,0 +1,10 @@ +/* Precomp.h -- StdAfx +2013-11-12 : Igor Pavlov : Public domain */ + +#ifndef __7Z_PRECOMP_H +#define __7Z_PRECOMP_H + +#include "Compiler.h" +/* #include "7zTypes.h" */ + +#endif diff --git a/core/deps/lzma/Sort.c b/core/deps/lzma/Sort.c new file mode 100644 index 000000000..73dcbf059 --- /dev/null +++ b/core/deps/lzma/Sort.c @@ -0,0 +1,141 @@ +/* Sort.c -- Sort functions +2014-04-05 : Igor Pavlov : Public domain */ + +#include "Precomp.h" + +#include "Sort.h" + +#define HeapSortDown(p, k, size, temp) \ + { for (;;) { \ + size_t s = (k << 1); \ + if (s > size) break; \ + if (s < size && p[s + 1] > p[s]) s++; \ + if (temp >= p[s]) break; \ + p[k] = p[s]; k = s; \ + } p[k] = temp; } + +void HeapSort(UInt32 *p, size_t size) +{ + if (size <= 1) + return; + p--; + { + size_t i = size / 2; + do + { + UInt32 temp = p[i]; + size_t k = i; + HeapSortDown(p, k, size, temp) + } + while (--i != 0); + } + /* + do + { + size_t k = 1; + UInt32 temp = p[size]; + p[size--] = p[1]; + HeapSortDown(p, k, size, temp) + } + while (size > 1); + */ + while (size > 3) + { + UInt32 temp = p[size]; + size_t k = (p[3] > p[2]) ? 3 : 2; + p[size--] = p[1]; + p[1] = p[k]; + HeapSortDown(p, k, size, temp) + } + { + UInt32 temp = p[size]; + p[size] = p[1]; + if (size > 2 && p[2] < temp) + { + p[1] = p[2]; + p[2] = temp; + } + else + p[1] = temp; + } +} + +void HeapSort64(UInt64 *p, size_t size) +{ + if (size <= 1) + return; + p--; + { + size_t i = size / 2; + do + { + UInt64 temp = p[i]; + size_t k = i; + HeapSortDown(p, k, size, temp) + } + while (--i != 0); + } + /* + do + { + size_t k = 1; + UInt64 temp = p[size]; + p[size--] = p[1]; + HeapSortDown(p, k, size, temp) + } + while (size > 1); + */ + while (size > 3) + { + UInt64 temp = p[size]; + size_t k = (p[3] > p[2]) ? 3 : 2; + p[size--] = p[1]; + p[1] = p[k]; + HeapSortDown(p, k, size, temp) + } + { + UInt64 temp = p[size]; + p[size] = p[1]; + if (size > 2 && p[2] < temp) + { + p[1] = p[2]; + p[2] = temp; + } + else + p[1] = temp; + } +} + +/* +#define HeapSortRefDown(p, vals, n, size, temp) \ + { size_t k = n; UInt32 val = vals[temp]; for (;;) { \ + size_t s = (k << 1); \ + if (s > size) break; \ + if (s < size && vals[p[s + 1]] > vals[p[s]]) s++; \ + if (val >= vals[p[s]]) break; \ + p[k] = p[s]; k = s; \ + } p[k] = temp; } + +void HeapSortRef(UInt32 *p, UInt32 *vals, size_t size) +{ + if (size <= 1) + return; + p--; + { + size_t i = size / 2; + do + { + UInt32 temp = p[i]; + HeapSortRefDown(p, vals, i, size, temp); + } + while (--i != 0); + } + do + { + UInt32 temp = p[size]; + p[size--] = p[1]; + HeapSortRefDown(p, vals, 1, size, temp); + } + while (size > 1); +} +*/ diff --git a/core/deps/lzma/Sort.h b/core/deps/lzma/Sort.h new file mode 100644 index 000000000..7209d7824 --- /dev/null +++ b/core/deps/lzma/Sort.h @@ -0,0 +1,18 @@ +/* Sort.h -- Sort functions +2014-04-05 : Igor Pavlov : Public domain */ + +#ifndef __7Z_SORT_H +#define __7Z_SORT_H + +#include "7zTypes.h" + +EXTERN_C_BEGIN + +void HeapSort(UInt32 *p, size_t size); +void HeapSort64(UInt64 *p, size_t size); + +/* void HeapSortRef(UInt32 *p, UInt32 *vals, size_t size); */ + +EXTERN_C_END + +#endif diff --git a/shell/android-studio/reicast/src/main/jni/Android.mk b/shell/android-studio/reicast/src/main/jni/Android.mk index fff94d333..396825b36 100644 --- a/shell/android-studio/reicast/src/main/jni/Android.mk +++ b/shell/android-studio/reicast/src/main/jni/Android.mk @@ -22,6 +22,7 @@ include $(CLEAR_VARS) FOR_ANDROID := 1 WEBUI := 1 USE_GLES := 1 +CHD5_LZMA := 1 ifneq ($(TARGET_ARCH_ABI),armeabi-v7a) NOT_ARM := 1 @@ -43,6 +44,11 @@ else NO_REC := endif +# 7-Zip/LZMA settings (CHDv5) +ifdef USE_LZMA + LOCAL_CFLAGS += -D_7ZIP_ST -DCHD5_LZMA +endif + $(info $$TARGET_ARCH_ABI is [${TARGET_ARCH_ABI}]) include $(LOCAL_PATH)/../../../../../core/core.mk diff --git a/shell/linux/Makefile b/shell/linux/Makefile index aac6efb4d..78304e169 100644 --- a/shell/linux/Makefile +++ b/shell/linux/Makefile @@ -14,6 +14,8 @@ AS=${CC_PREFIX}as STRIP=${CC_PREFIX}strip LD=${CC} +CHD5_LZMA := 1 + MFLAGS := ASFLAGS := LDFLAGS := @@ -234,6 +236,11 @@ else $(error Unknown platform) endif +# 7-Zip/LZMA settings (CHDv5) +ifdef CHD5_LZMA + CFLAGS += -D_7ZIP_ST -DCHD5_LZMA +endif + RZDCY_SRC_DIR = $(LOCAL_PATH)/../../core include $(RZDCY_SRC_DIR)/core.mk diff --git a/shell/reicast.vcxproj b/shell/reicast.vcxproj index d57dd5e11..25e6c831b 100644 --- a/shell/reicast.vcxproj +++ b/shell/reicast.vcxproj @@ -55,7 +55,11 @@ - + + + + + @@ -96,6 +100,19 @@ + + + + + + + + + + + + + @@ -281,8 +298,12 @@ + + + + @@ -676,7 +697,7 @@ Level3 Full - TARGET_NO_WEBUI;WIN32;NDEBUG;_CONSOLE;X86;%(PreprocessorDefinitions) + TARGET_NO_WEBUI;WIN32;_7ZIP_ST;CHD5_LZMA;NDEBUG;_CONSOLE;X86;%(PreprocessorDefinitions) $(ProjectDir)..\core\;$(ProjectDir)..\core\khronos;%(AdditionalIncludeDirectories) /MP %(AdditionalOptions) AnySuitable @@ -718,7 +739,7 @@ Level3 Full - TARGET_NO_WEBUI;WIN32;NDEBUG;_CONSOLE;X86;%(PreprocessorDefinitions) + TARGET_NO_WEBUI;WIN32;_7ZIP_ST;CHD5_LZMA;NDEBUG;_CONSOLE;X86;%(PreprocessorDefinitions) $(ProjectDir)..\core\;$(ProjectDir)..\core\khronos;%(AdditionalIncludeDirectories) /MP %(AdditionalOptions) AnySuitable @@ -760,7 +781,7 @@ Level3 Full - TARGET_NO_WEBUI;WIN32;NDEBUG;_CONSOLE;X86;%(PreprocessorDefinitions) + TARGET_NO_WEBUI;WIN32;_7ZIP_ST;CHD5_LZMA;NDEBUG;_CONSOLE;X86;%(PreprocessorDefinitions) $(ProjectDir)..\core\;$(ProjectDir)..\core\khronos;%(AdditionalIncludeDirectories) /MP %(AdditionalOptions) AnySuitable @@ -802,7 +823,7 @@ Level3 Full - TARGET_NO_WEBUI;WIN32;NDEBUG;_CONSOLE;X86;%(PreprocessorDefinitions) + TARGET_NO_WEBUI;WIN32;_7ZIP_ST;CHD5_LZMA;NDEBUG;_CONSOLE;X86;%(PreprocessorDefinitions) $(ProjectDir)..\core\;$(ProjectDir)..\core\khronos;%(AdditionalIncludeDirectories) /MP %(AdditionalOptions) AnySuitable @@ -846,7 +867,7 @@ Level3 Full - TARGET_NO_WEBUI;WIN32;NDEBUG;_CONSOLE;X86;%(PreprocessorDefinitions) + TARGET_NO_WEBUI;WIN32;_7ZIP_ST;CHD5_LZMA;NDEBUG;_CONSOLE;X86;%(PreprocessorDefinitions) $(ProjectDir)..\core\;$(ProjectDir)..\core\khronos;%(AdditionalIncludeDirectories) /MP %(AdditionalOptions) AnySuitable @@ -890,7 +911,7 @@ Level3 Full - TARGET_NO_WEBUI;WIN32;NDEBUG;_CONSOLE;X86;%(PreprocessorDefinitions) + TARGET_NO_WEBUI;WIN32;_7ZIP_ST;CHD5_LZMA;NDEBUG;_CONSOLE;X86;%(PreprocessorDefinitions) $(ProjectDir)..\core\;$(ProjectDir)..\core\khronos;%(AdditionalIncludeDirectories) /MP %(AdditionalOptions) AnySuitable @@ -934,7 +955,7 @@ Level3 Disabled - DEF_CONSOLE;TARGET_NO_WEBUI;WIN32;_DEBUG;_CONSOLE;X86;%(PreprocessorDefinitions) + DEF_CONSOLE;TARGET_NO_WEBUI;WIN32;_7ZIP_ST;CHD5_LZMA;_DEBUG;_CONSOLE;X86;%(PreprocessorDefinitions) $(ProjectDir)..\core\;$(ProjectDir)..\core\khronos;%(AdditionalIncludeDirectories) true false @@ -965,7 +986,7 @@ Level3 Disabled - DEF_CONSOLE;TARGET_NO_WEBUI;WIN32;_DEBUG;_CONSOLE;X86;%(PreprocessorDefinitions) + DEF_CONSOLE;TARGET_NO_WEBUI;WIN32;_7ZIP_ST;CHD5_LZMA;_DEBUG;_CONSOLE;X86;%(PreprocessorDefinitions) $(ProjectDir)..\core\;$(ProjectDir)..\core\khronos;%(AdditionalIncludeDirectories) true false @@ -996,7 +1017,7 @@ Level3 Disabled - DEF_CONSOLE;TARGET_NO_WEBUI;WIN32;_DEBUG;_CONSOLE;X86;%(PreprocessorDefinitions) + DEF_CONSOLE;TARGET_NO_WEBUI;WIN32;_7ZIP_ST;CHD5_LZMA;_DEBUG;_CONSOLE;X86;%(PreprocessorDefinitions) $(ProjectDir)..\core\;$(ProjectDir)..\core\khronos;%(AdditionalIncludeDirectories) true false @@ -1027,7 +1048,7 @@ Level3 Disabled - DEF_CONSOLE;TARGET_NO_WEBUI;WIN32;_DEBUG;_CONSOLE;X86;%(PreprocessorDefinitions) + DEF_CONSOLE;TARGET_NO_WEBUI;WIN32;_7ZIP_ST;CHD5_LZMA;_DEBUG;_CONSOLE;X86;%(PreprocessorDefinitions) $(ProjectDir)..\core\;$(ProjectDir)..\core\khronos;%(AdditionalIncludeDirectories) true false @@ -1063,7 +1084,7 @@ Level3 Disabled - DEF_CONSOLE;TARGET_NO_WEBUI;WIN32;_DEBUG;_CONSOLE;X86;%(PreprocessorDefinitions) + DEF_CONSOLE;TARGET_NO_WEBUI;WIN32;_7ZIP_ST;CHD5_LZMA;_DEBUG;_CONSOLE;X86;%(PreprocessorDefinitions) $(ProjectDir)..\core\;$(ProjectDir)..\core\khronos;%(AdditionalIncludeDirectories) true false @@ -1099,7 +1120,7 @@ Level3 Disabled - DEF_CONSOLE;TARGET_NO_WEBUI;WIN32;_DEBUG;_CONSOLE;X86;%(PreprocessorDefinitions) + DEF_CONSOLE;TARGET_NO_WEBUI;WIN32;_7ZIP_ST;CHD5_LZMA;_DEBUG;_CONSOLE;X86;%(PreprocessorDefinitions) $(ProjectDir)..\core\;$(ProjectDir)..\core\khronos;%(AdditionalIncludeDirectories) true false diff --git a/shell/reicast.vcxproj.filters b/shell/reicast.vcxproj.filters index f64836100..3a73b9785 100644 --- a/shell/reicast.vcxproj.filters +++ b/shell/reicast.vcxproj.filters @@ -97,9 +97,6 @@ deps\crypto - - deps\chdr - imgread @@ -429,6 +426,60 @@ cfg + + deps\chdr + + + deps\chdr + + + deps\chdr + + + deps\chdr + + + deps\chdr + + + deps\lzma + + + deps\lzma + + + deps\lzma + + + deps\lzma + + + deps\lzma + + + deps\lzma + + + deps\lzma + + + deps\lzma + + + deps\lzma + + + deps\lzma + + + deps\lzma + + + deps\lzma + + + deps\lzma + @@ -566,6 +617,9 @@ {1752487d-0739-47bf-8c6b-1d38e6f389f7} + + {9198fe3c-8215-431d-9adf-d36e81eda98d} + @@ -947,6 +1001,18 @@ hw\sh4\modules + + deps\chdr + + + deps\chdr + + + deps\chdr + + + deps\chdr + From b1be3af1a1a2c4901f2f5a484f1b26e295bfacf1 Mon Sep 17 00:00:00 2001 From: "Christoph \"baka0815\" Schwerdtfeger" Date: Sat, 22 Sep 2018 17:19:26 +0200 Subject: [PATCH 015/319] CHD: Enable FLAC via library (on Linux builds) --- core/deps/chdr/flac.h | 2 +- core/deps/flac/include/FLAC/all.h | 371 ++ core/deps/flac/include/FLAC/callback.h | 185 + core/deps/flac/include/FLAC/export.h | 97 + core/deps/flac/include/FLAC/format.h | 1025 +++++ core/deps/flac/include/FLAC/metadata.h | 2182 +++++++++++ core/deps/flac/include/FLAC/ordinals.h | 86 + core/deps/flac/include/FLAC/stream_decoder.h | 1560 ++++++++ core/deps/flac/include/FLAC/stream_encoder.h | 1790 +++++++++ core/deps/flac/include/share/alloc.h | 219 ++ core/deps/flac/include/share/compat.h | 209 + core/deps/flac/src/libFLAC/bitmath.c | 73 + core/deps/flac/src/libFLAC/bitreader.c | 1087 +++++ core/deps/flac/src/libFLAC/cpu.c | 293 ++ core/deps/flac/src/libFLAC/crc.c | 143 + core/deps/flac/src/libFLAC/fixed.c | 395 ++ .../deps/flac/src/libFLAC/fixed_intrin_sse2.c | 255 ++ .../flac/src/libFLAC/fixed_intrin_ssse3.c | 243 ++ core/deps/flac/src/libFLAC/float.c | 302 ++ core/deps/flac/src/libFLAC/format.c | 589 +++ .../src/libFLAC/include/private/bitmath.h | 210 + .../flac/src/libFLAC/include/private/cpu.h | 186 + .../flac/src/libFLAC/include/private/crc.h | 62 + .../flac/src/libFLAC/include/private/fixed.h | 107 + .../flac/src/libFLAC/include/private/float.h | 95 + .../flac/src/libFLAC/include/private/format.h | 45 + .../flac/src/libFLAC/include/private/macros.h | 72 + core/deps/flac/src/libFLAC/lpc.c | 1357 +++++++ core/deps/flac/src/libFLAC/lpc_intrin_avx2.c | 1122 ++++++ core/deps/flac/src/libFLAC/lpc_intrin_sse.c | 454 +++ core/deps/flac/src/libFLAC/lpc_intrin_sse2.c | 1090 ++++++ core/deps/flac/src/libFLAC/lpc_intrin_sse41.c | 1314 +++++++ core/deps/flac/src/libFLAC/md5.c | 516 +++ core/deps/flac/src/libFLAC/memory.c | 218 ++ .../flac/src/libFLAC/metadata_iterators.c | 3481 +++++++++++++++++ core/deps/flac/src/libFLAC/metadata_object.c | 1821 +++++++++ core/deps/flac/src/libFLAC/stream_decoder.c | 3400 ++++++++++++++++ core/deps/flac/src/libFLAC/window.c | 282 ++ .../src/libFLAC/windows_unicode_filenames.c | 201 + shell/linux/Makefile | 6 + wercker.yml | 2 +- 41 files changed, 27145 insertions(+), 2 deletions(-) create mode 100644 core/deps/flac/include/FLAC/all.h create mode 100644 core/deps/flac/include/FLAC/callback.h create mode 100644 core/deps/flac/include/FLAC/export.h create mode 100644 core/deps/flac/include/FLAC/format.h create mode 100644 core/deps/flac/include/FLAC/metadata.h create mode 100644 core/deps/flac/include/FLAC/ordinals.h create mode 100644 core/deps/flac/include/FLAC/stream_decoder.h create mode 100644 core/deps/flac/include/FLAC/stream_encoder.h create mode 100644 core/deps/flac/include/share/alloc.h create mode 100644 core/deps/flac/include/share/compat.h create mode 100644 core/deps/flac/src/libFLAC/bitmath.c create mode 100644 core/deps/flac/src/libFLAC/bitreader.c create mode 100644 core/deps/flac/src/libFLAC/cpu.c create mode 100644 core/deps/flac/src/libFLAC/crc.c create mode 100644 core/deps/flac/src/libFLAC/fixed.c create mode 100644 core/deps/flac/src/libFLAC/fixed_intrin_sse2.c create mode 100644 core/deps/flac/src/libFLAC/fixed_intrin_ssse3.c create mode 100644 core/deps/flac/src/libFLAC/float.c create mode 100644 core/deps/flac/src/libFLAC/format.c create mode 100644 core/deps/flac/src/libFLAC/include/private/bitmath.h create mode 100644 core/deps/flac/src/libFLAC/include/private/cpu.h create mode 100644 core/deps/flac/src/libFLAC/include/private/crc.h create mode 100644 core/deps/flac/src/libFLAC/include/private/fixed.h create mode 100644 core/deps/flac/src/libFLAC/include/private/float.h create mode 100644 core/deps/flac/src/libFLAC/include/private/format.h create mode 100644 core/deps/flac/src/libFLAC/include/private/macros.h create mode 100644 core/deps/flac/src/libFLAC/lpc.c create mode 100644 core/deps/flac/src/libFLAC/lpc_intrin_avx2.c create mode 100644 core/deps/flac/src/libFLAC/lpc_intrin_sse.c create mode 100644 core/deps/flac/src/libFLAC/lpc_intrin_sse2.c create mode 100644 core/deps/flac/src/libFLAC/lpc_intrin_sse41.c create mode 100644 core/deps/flac/src/libFLAC/md5.c create mode 100644 core/deps/flac/src/libFLAC/memory.c create mode 100644 core/deps/flac/src/libFLAC/metadata_iterators.c create mode 100644 core/deps/flac/src/libFLAC/metadata_object.c create mode 100644 core/deps/flac/src/libFLAC/stream_decoder.c create mode 100644 core/deps/flac/src/libFLAC/window.c create mode 100644 core/deps/flac/src/libFLAC/windows_unicode_filenames.c diff --git a/core/deps/chdr/flac.h b/core/deps/chdr/flac.h index 5a3bf2d9c..3de00eb61 100644 --- a/core/deps/chdr/flac.h +++ b/core/deps/chdr/flac.h @@ -14,7 +14,7 @@ #define __FLAC_H__ #include -#include "FLAC/all.h" +#include "deps/flac/include/FLAC/all.h" /*************************************************************************** * TYPE DEFINITIONS diff --git a/core/deps/flac/include/FLAC/all.h b/core/deps/flac/include/FLAC/all.h new file mode 100644 index 000000000..11d47d797 --- /dev/null +++ b/core/deps/flac/include/FLAC/all.h @@ -0,0 +1,371 @@ +/* libFLAC - Free Lossless Audio Codec library + * Copyright (C) 2000-2009 Josh Coalson + * Copyright (C) 2011-2016 Xiph.Org Foundation + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * - Neither the name of the Xiph.org Foundation nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef FLAC__ALL_H +#define FLAC__ALL_H + +#include "export.h" + +#include "assert.h" +#include "callback.h" +#include "format.h" +#include "metadata.h" +#include "ordinals.h" +#include "stream_decoder.h" +#include "stream_encoder.h" + +/** \mainpage + * + * \section intro Introduction + * + * This is the documentation for the FLAC C and C++ APIs. It is + * highly interconnected; this introduction should give you a top + * level idea of the structure and how to find the information you + * need. As a prerequisite you should have at least a basic + * knowledge of the FLAC format, documented + * here. + * + * \section c_api FLAC C API + * + * The FLAC C API is the interface to libFLAC, a set of structures + * describing the components of FLAC streams, and functions for + * encoding and decoding streams, as well as manipulating FLAC + * metadata in files. The public include files will be installed + * in your include area (for example /usr/include/FLAC/...). + * + * By writing a little code and linking against libFLAC, it is + * relatively easy to add FLAC support to another program. The + * library is licensed under Xiph's BSD license. + * Complete source code of libFLAC as well as the command-line + * encoder and plugins is available and is a useful source of + * examples. + * + * Aside from encoders and decoders, libFLAC provides a powerful + * metadata interface for manipulating metadata in FLAC files. It + * allows the user to add, delete, and modify FLAC metadata blocks + * and it can automatically take advantage of PADDING blocks to avoid + * rewriting the entire FLAC file when changing the size of the + * metadata. + * + * libFLAC usually only requires the standard C library and C math + * library. In particular, threading is not used so there is no + * dependency on a thread library. However, libFLAC does not use + * global variables and should be thread-safe. + * + * libFLAC also supports encoding to and decoding from Ogg FLAC. + * However the metadata editing interfaces currently have limited + * read-only support for Ogg FLAC files. + * + * \section cpp_api FLAC C++ API + * + * The FLAC C++ API is a set of classes that encapsulate the + * structures and functions in libFLAC. They provide slightly more + * functionality with respect to metadata but are otherwise + * equivalent. For the most part, they share the same usage as + * their counterparts in libFLAC, and the FLAC C API documentation + * can be used as a supplement. The public include files + * for the C++ API will be installed in your include area (for + * example /usr/include/FLAC++/...). + * + * libFLAC++ is also licensed under + * Xiph's BSD license. + * + * \section getting_started Getting Started + * + * A good starting point for learning the API is to browse through + * the modules. Modules are logical + * groupings of related functions or classes, which correspond roughly + * to header files or sections of header files. Each module includes a + * detailed description of the general usage of its functions or + * classes. + * + * From there you can go on to look at the documentation of + * individual functions. You can see different views of the individual + * functions through the links in top bar across this page. + * + * If you prefer a more hands-on approach, you can jump right to some + * example code. + * + * \section porting_guide Porting Guide + * + * Starting with FLAC 1.1.3 a \link porting Porting Guide \endlink + * has been introduced which gives detailed instructions on how to + * port your code to newer versions of FLAC. + * + * \section embedded_developers Embedded Developers + * + * libFLAC has grown larger over time as more functionality has been + * included, but much of it may be unnecessary for a particular embedded + * implementation. Unused parts may be pruned by some simple editing of + * src/libFLAC/Makefile.am. In general, the decoders, encoders, and + * metadata interface are all independent from each other. + * + * It is easiest to just describe the dependencies: + * + * - All modules depend on the \link flac_format Format \endlink module. + * - The decoders and encoders depend on the bitbuffer. + * - The decoder is independent of the encoder. The encoder uses the + * decoder because of the verify feature, but this can be removed if + * not needed. + * - Parts of the metadata interface require the stream decoder (but not + * the encoder). + * - Ogg support is selectable through the compile time macro + * \c FLAC__HAS_OGG. + * + * For example, if your application only requires the stream decoder, no + * encoder, and no metadata interface, you can remove the stream encoder + * and the metadata interface, which will greatly reduce the size of the + * library. + * + * Also, there are several places in the libFLAC code with comments marked + * with "OPT:" where a #define can be changed to enable code that might be + * faster on a specific platform. Experimenting with these can yield faster + * binaries. + */ + +/** \defgroup porting Porting Guide for New Versions + * + * This module describes differences in the library interfaces from + * version to version. It assists in the porting of code that uses + * the libraries to newer versions of FLAC. + * + * One simple facility for making porting easier that has been added + * in FLAC 1.1.3 is a set of \c #defines in \c export.h of each + * library's includes (e.g. \c include/FLAC/export.h). The + * \c #defines mirror the libraries' + * libtool version numbers, + * e.g. in libFLAC there are \c FLAC_API_VERSION_CURRENT, + * \c FLAC_API_VERSION_REVISION, and \c FLAC_API_VERSION_AGE. + * These can be used to support multiple versions of an API during the + * transition phase, e.g. + * + * \code + * #if !defined(FLAC_API_VERSION_CURRENT) || FLAC_API_VERSION_CURRENT <= 7 + * legacy code + * #else + * new code + * #endif + * \endcode + * + * The source will work for multiple versions and the legacy code can + * easily be removed when the transition is complete. + * + * Another available symbol is FLAC_API_SUPPORTS_OGG_FLAC (defined in + * include/FLAC/export.h), which can be used to determine whether or not + * the library has been compiled with support for Ogg FLAC. This is + * simpler than trying to call an Ogg init function and catching the + * error. + */ + +/** \defgroup porting_1_1_2_to_1_1_3 Porting from FLAC 1.1.2 to 1.1.3 + * \ingroup porting + * + * \brief + * This module describes porting from FLAC 1.1.2 to FLAC 1.1.3. + * + * The main change between the APIs in 1.1.2 and 1.1.3 is that they have + * been simplified. First, libOggFLAC has been merged into libFLAC and + * libOggFLAC++ has been merged into libFLAC++. Second, both the three + * decoding layers and three encoding layers have been merged into a + * single stream decoder and stream encoder. That is, the functionality + * of FLAC__SeekableStreamDecoder and FLAC__FileDecoder has been merged + * into FLAC__StreamDecoder, and FLAC__SeekableStreamEncoder and + * FLAC__FileEncoder into FLAC__StreamEncoder. Only the + * FLAC__StreamDecoder and FLAC__StreamEncoder remain. What this means + * is there is now a single API that can be used to encode or decode + * streams to/from native FLAC or Ogg FLAC and the single API can work + * on both seekable and non-seekable streams. + * + * Instead of creating an encoder or decoder of a certain layer, now the + * client will always create a FLAC__StreamEncoder or + * FLAC__StreamDecoder. The old layers are now differentiated by the + * initialization function. For example, for the decoder, + * FLAC__stream_decoder_init() has been replaced by + * FLAC__stream_decoder_init_stream(). This init function takes + * callbacks for the I/O, and the seeking callbacks are optional. This + * allows the client to use the same object for seekable and + * non-seekable streams. For decoding a FLAC file directly, the client + * can use FLAC__stream_decoder_init_file() and pass just a filename + * and fewer callbacks; most of the other callbacks are supplied + * internally. For situations where fopen()ing by filename is not + * possible (e.g. Unicode filenames on Windows) the client can instead + * open the file itself and supply the FILE* to + * FLAC__stream_decoder_init_FILE(). The init functions now returns a + * FLAC__StreamDecoderInitStatus instead of FLAC__StreamDecoderState. + * Since the callbacks and client data are now passed to the init + * function, the FLAC__stream_decoder_set_*_callback() functions and + * FLAC__stream_decoder_set_client_data() are no longer needed. The + * rest of the calls to the decoder are the same as before. + * + * There are counterpart init functions for Ogg FLAC, e.g. + * FLAC__stream_decoder_init_ogg_stream(). All the rest of the calls + * and callbacks are the same as for native FLAC. + * + * As an example, in FLAC 1.1.2 a seekable stream decoder would have + * been set up like so: + * + * \code + * FLAC__SeekableStreamDecoder *decoder = FLAC__seekable_stream_decoder_new(); + * if(decoder == NULL) do_something; + * FLAC__seekable_stream_decoder_set_md5_checking(decoder, true); + * [... other settings ...] + * FLAC__seekable_stream_decoder_set_read_callback(decoder, my_read_callback); + * FLAC__seekable_stream_decoder_set_seek_callback(decoder, my_seek_callback); + * FLAC__seekable_stream_decoder_set_tell_callback(decoder, my_tell_callback); + * FLAC__seekable_stream_decoder_set_length_callback(decoder, my_length_callback); + * FLAC__seekable_stream_decoder_set_eof_callback(decoder, my_eof_callback); + * FLAC__seekable_stream_decoder_set_write_callback(decoder, my_write_callback); + * FLAC__seekable_stream_decoder_set_metadata_callback(decoder, my_metadata_callback); + * FLAC__seekable_stream_decoder_set_error_callback(decoder, my_error_callback); + * FLAC__seekable_stream_decoder_set_client_data(decoder, my_client_data); + * if(FLAC__seekable_stream_decoder_init(decoder) != FLAC__SEEKABLE_STREAM_DECODER_OK) do_something; + * \endcode + * + * In FLAC 1.1.3 it is like this: + * + * \code + * FLAC__StreamDecoder *decoder = FLAC__stream_decoder_new(); + * if(decoder == NULL) do_something; + * FLAC__stream_decoder_set_md5_checking(decoder, true); + * [... other settings ...] + * if(FLAC__stream_decoder_init_stream( + * decoder, + * my_read_callback, + * my_seek_callback, // or NULL + * my_tell_callback, // or NULL + * my_length_callback, // or NULL + * my_eof_callback, // or NULL + * my_write_callback, + * my_metadata_callback, // or NULL + * my_error_callback, + * my_client_data + * ) != FLAC__STREAM_DECODER_INIT_STATUS_OK) do_something; + * \endcode + * + * or you could do; + * + * \code + * [...] + * FILE *file = fopen("somefile.flac","rb"); + * if(file == NULL) do_somthing; + * if(FLAC__stream_decoder_init_FILE( + * decoder, + * file, + * my_write_callback, + * my_metadata_callback, // or NULL + * my_error_callback, + * my_client_data + * ) != FLAC__STREAM_DECODER_INIT_STATUS_OK) do_something; + * \endcode + * + * or just: + * + * \code + * [...] + * if(FLAC__stream_decoder_init_file( + * decoder, + * "somefile.flac", + * my_write_callback, + * my_metadata_callback, // or NULL + * my_error_callback, + * my_client_data + * ) != FLAC__STREAM_DECODER_INIT_STATUS_OK) do_something; + * \endcode + * + * Another small change to the decoder is in how it handles unparseable + * streams. Before, when the decoder found an unparseable stream + * (reserved for when the decoder encounters a stream from a future + * encoder that it can't parse), it changed the state to + * \c FLAC__STREAM_DECODER_UNPARSEABLE_STREAM. Now the decoder instead + * drops sync and calls the error callback with a new error code + * \c FLAC__STREAM_DECODER_ERROR_STATUS_UNPARSEABLE_STREAM. This is + * more robust. If your error callback does not discriminate on the the + * error state, your code does not need to be changed. + * + * The encoder now has a new setting: + * FLAC__stream_encoder_set_apodization(). This is for setting the + * method used to window the data before LPC analysis. You only need to + * add a call to this function if the default is not suitable. There + * are also two new convenience functions that may be useful: + * FLAC__metadata_object_cuesheet_calculate_cddb_id() and + * FLAC__metadata_get_cuesheet(). + * + * The \a bytes parameter to FLAC__StreamDecoderReadCallback, + * FLAC__StreamEncoderReadCallback, and FLAC__StreamEncoderWriteCallback + * is now \c size_t instead of \c unsigned. + */ + +/** \defgroup porting_1_1_3_to_1_1_4 Porting from FLAC 1.1.3 to 1.1.4 + * \ingroup porting + * + * \brief + * This module describes porting from FLAC 1.1.3 to FLAC 1.1.4. + * + * There were no changes to any of the interfaces from 1.1.3 to 1.1.4. + * There was a slight change in the implementation of + * FLAC__stream_encoder_set_metadata(); the function now makes a copy + * of the \a metadata array of pointers so the client no longer needs + * to maintain it after the call. The objects themselves that are + * pointed to by the array are still not copied though and must be + * maintained until the call to FLAC__stream_encoder_finish(). + */ + +/** \defgroup porting_1_1_4_to_1_2_0 Porting from FLAC 1.1.4 to 1.2.0 + * \ingroup porting + * + * \brief + * This module describes porting from FLAC 1.1.4 to FLAC 1.2.0. + * + * There were only very minor changes to the interfaces from 1.1.4 to 1.2.0. + * In libFLAC, \c FLAC__format_sample_rate_is_subset() was added. + * In libFLAC++, \c FLAC::Decoder::Stream::get_decode_position() was added. + * + * Finally, value of the constant \c FLAC__FRAME_HEADER_RESERVED_LEN + * has changed to reflect the conversion of one of the reserved bits + * into active use. It used to be \c 2 and now is \c 1. However the + * FLAC frame header length has not changed, so to skip the proper + * number of bits, use \c FLAC__FRAME_HEADER_RESERVED_LEN + + * \c FLAC__FRAME_HEADER_BLOCKING_STRATEGY_LEN + */ + +/** \defgroup flac FLAC C API + * + * The FLAC C API is the interface to libFLAC, a set of structures + * describing the components of FLAC streams, and functions for + * encoding and decoding streams, as well as manipulating FLAC + * metadata in files. + * + * You should start with the format components as all other modules + * are dependent on it. + */ + +#endif diff --git a/core/deps/flac/include/FLAC/callback.h b/core/deps/flac/include/FLAC/callback.h new file mode 100644 index 000000000..f942dd259 --- /dev/null +++ b/core/deps/flac/include/FLAC/callback.h @@ -0,0 +1,185 @@ +/* libFLAC - Free Lossless Audio Codec library + * Copyright (C) 2004-2009 Josh Coalson + * Copyright (C) 2011-2016 Xiph.Org Foundation + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * - Neither the name of the Xiph.org Foundation nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef FLAC__CALLBACK_H +#define FLAC__CALLBACK_H + +#include "ordinals.h" +#include /* for size_t */ + +/** \file include/FLAC/callback.h + * + * \brief + * This module defines the structures for describing I/O callbacks + * to the other FLAC interfaces. + * + * See the detailed documentation for callbacks in the + * \link flac_callbacks callbacks \endlink module. + */ + +/** \defgroup flac_callbacks FLAC/callback.h: I/O callback structures + * \ingroup flac + * + * \brief + * This module defines the structures for describing I/O callbacks + * to the other FLAC interfaces. + * + * The purpose of the I/O callback functions is to create a common way + * for the metadata interfaces to handle I/O. + * + * Originally the metadata interfaces required filenames as the way of + * specifying FLAC files to operate on. This is problematic in some + * environments so there is an additional option to specify a set of + * callbacks for doing I/O on the FLAC file, instead of the filename. + * + * In addition to the callbacks, a FLAC__IOHandle type is defined as an + * opaque structure for a data source. + * + * The callback function prototypes are similar (but not identical) to the + * stdio functions fread, fwrite, fseek, ftell, feof, and fclose. If you use + * stdio streams to implement the callbacks, you can pass fread, fwrite, and + * fclose anywhere a FLAC__IOCallback_Read, FLAC__IOCallback_Write, or + * FLAC__IOCallback_Close is required, and a FILE* anywhere a FLAC__IOHandle + * is required. \warning You generally CANNOT directly use fseek or ftell + * for FLAC__IOCallback_Seek or FLAC__IOCallback_Tell since on most systems + * these use 32-bit offsets and FLAC requires 64-bit offsets to deal with + * large files. You will have to find an equivalent function (e.g. ftello), + * or write a wrapper. The same is true for feof() since this is usually + * implemented as a macro, not as a function whose address can be taken. + * + * \{ + */ + +#ifdef __cplusplus +extern "C" { +#endif + +/** This is the opaque handle type used by the callbacks. Typically + * this is a \c FILE* or address of a file descriptor. + */ +typedef void* FLAC__IOHandle; + +/** Signature for the read callback. + * The signature and semantics match POSIX fread() implementations + * and can generally be used interchangeably. + * + * \param ptr The address of the read buffer. + * \param size The size of the records to be read. + * \param nmemb The number of records to be read. + * \param handle The handle to the data source. + * \retval size_t + * The number of records read. + */ +typedef size_t (*FLAC__IOCallback_Read) (void *ptr, size_t size, size_t nmemb, FLAC__IOHandle handle); + +/** Signature for the write callback. + * The signature and semantics match POSIX fwrite() implementations + * and can generally be used interchangeably. + * + * \param ptr The address of the write buffer. + * \param size The size of the records to be written. + * \param nmemb The number of records to be written. + * \param handle The handle to the data source. + * \retval size_t + * The number of records written. + */ +typedef size_t (*FLAC__IOCallback_Write) (const void *ptr, size_t size, size_t nmemb, FLAC__IOHandle handle); + +/** Signature for the seek callback. + * The signature and semantics mostly match POSIX fseek() WITH ONE IMPORTANT + * EXCEPTION: the offset is a 64-bit type whereas fseek() is generally 'long' + * and 32-bits wide. + * + * \param handle The handle to the data source. + * \param offset The new position, relative to \a whence + * \param whence \c SEEK_SET, \c SEEK_CUR, or \c SEEK_END + * \retval int + * \c 0 on success, \c -1 on error. + */ +typedef int (*FLAC__IOCallback_Seek) (FLAC__IOHandle handle, FLAC__int64 offset, int whence); + +/** Signature for the tell callback. + * The signature and semantics mostly match POSIX ftell() WITH ONE IMPORTANT + * EXCEPTION: the offset is a 64-bit type whereas ftell() is generally 'long' + * and 32-bits wide. + * + * \param handle The handle to the data source. + * \retval FLAC__int64 + * The current position on success, \c -1 on error. + */ +typedef FLAC__int64 (*FLAC__IOCallback_Tell) (FLAC__IOHandle handle); + +/** Signature for the EOF callback. + * The signature and semantics mostly match POSIX feof() but WATCHOUT: + * on many systems, feof() is a macro, so in this case a wrapper function + * must be provided instead. + * + * \param handle The handle to the data source. + * \retval int + * \c 0 if not at end of file, nonzero if at end of file. + */ +typedef int (*FLAC__IOCallback_Eof) (FLAC__IOHandle handle); + +/** Signature for the close callback. + * The signature and semantics match POSIX fclose() implementations + * and can generally be used interchangeably. + * + * \param handle The handle to the data source. + * \retval int + * \c 0 on success, \c EOF on error. + */ +typedef int (*FLAC__IOCallback_Close) (FLAC__IOHandle handle); + +/** A structure for holding a set of callbacks. + * Each FLAC interface that requires a FLAC__IOCallbacks structure will + * describe which of the callbacks are required. The ones that are not + * required may be set to NULL. + * + * If the seek requirement for an interface is optional, you can signify that + * a data sorce is not seekable by setting the \a seek field to \c NULL. + */ +typedef struct { + FLAC__IOCallback_Read read; + FLAC__IOCallback_Write write; + FLAC__IOCallback_Seek seek; + FLAC__IOCallback_Tell tell; + FLAC__IOCallback_Eof eof; + FLAC__IOCallback_Close close; +} FLAC__IOCallbacks; + +/* \} */ + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/core/deps/flac/include/FLAC/export.h b/core/deps/flac/include/FLAC/export.h new file mode 100644 index 000000000..d52f0bbb5 --- /dev/null +++ b/core/deps/flac/include/FLAC/export.h @@ -0,0 +1,97 @@ +/* libFLAC - Free Lossless Audio Codec library + * Copyright (C) 2000-2009 Josh Coalson + * Copyright (C) 2011-2016 Xiph.Org Foundation + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * - Neither the name of the Xiph.org Foundation nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef FLAC__EXPORT_H +#define FLAC__EXPORT_H + +/** \file include/FLAC/export.h + * + * \brief + * This module contains #defines and symbols for exporting function + * calls, and providing version information and compiled-in features. + * + * See the \link flac_export export \endlink module. + */ + +/** \defgroup flac_export FLAC/export.h: export symbols + * \ingroup flac + * + * \brief + * This module contains #defines and symbols for exporting function + * calls, and providing version information and compiled-in features. + * + * If you are compiling with MSVC and will link to the static library + * (libFLAC.lib) you should define FLAC__NO_DLL in your project to + * make sure the symbols are exported properly. + * + * \{ + */ + +#if defined(FLAC__NO_DLL) +#define FLAC_API + +#elif defined(_MSC_VER) +#ifdef FLAC_API_EXPORTS +#define FLAC_API __declspec(dllexport) +#else +#define FLAC_API __declspec(dllimport) +#endif + +#elif defined(FLAC__USE_VISIBILITY_ATTR) +#define FLAC_API __attribute__ ((visibility ("default"))) + +#else +#define FLAC_API + +#endif + +/** These #defines will mirror the libtool-based library version number, see + * http://www.gnu.org/software/libtool/manual/libtool.html#Libtool-versioning + */ +#define FLAC_API_VERSION_CURRENT 11 +#define FLAC_API_VERSION_REVISION 0 /**< see above */ +#define FLAC_API_VERSION_AGE 3 /**< see above */ + +#ifdef __cplusplus +extern "C" { +#endif + +/** \c 1 if the library has been compiled with support for Ogg FLAC, else \c 0. */ +extern FLAC_API int FLAC_API_SUPPORTS_OGG_FLAC; + +#ifdef __cplusplus +} +#endif + +/* \} */ + +#endif diff --git a/core/deps/flac/include/FLAC/format.h b/core/deps/flac/include/FLAC/format.h new file mode 100644 index 000000000..c087d4a70 --- /dev/null +++ b/core/deps/flac/include/FLAC/format.h @@ -0,0 +1,1025 @@ +/* libFLAC - Free Lossless Audio Codec library + * Copyright (C) 2000-2009 Josh Coalson + * Copyright (C) 2011-2016 Xiph.Org Foundation + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * - Neither the name of the Xiph.org Foundation nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef FLAC__FORMAT_H +#define FLAC__FORMAT_H + +#include "export.h" +#include "ordinals.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** \file include/FLAC/format.h + * + * \brief + * This module contains structure definitions for the representation + * of FLAC format components in memory. These are the basic + * structures used by the rest of the interfaces. + * + * See the detailed documentation in the + * \link flac_format format \endlink module. + */ + +/** \defgroup flac_format FLAC/format.h: format components + * \ingroup flac + * + * \brief + * This module contains structure definitions for the representation + * of FLAC format components in memory. These are the basic + * structures used by the rest of the interfaces. + * + * First, you should be familiar with the + * FLAC format. Many of the values here + * follow directly from the specification. As a user of libFLAC, the + * interesting parts really are the structures that describe the frame + * header and metadata blocks. + * + * The format structures here are very primitive, designed to store + * information in an efficient way. Reading information from the + * structures is easy but creating or modifying them directly is + * more complex. For the most part, as a user of a library, editing + * is not necessary; however, for metadata blocks it is, so there are + * convenience functions provided in the \link flac_metadata metadata + * module \endlink to simplify the manipulation of metadata blocks. + * + * \note + * It's not the best convention, but symbols ending in _LEN are in bits + * and _LENGTH are in bytes. _LENGTH symbols are \#defines instead of + * global variables because they are usually used when declaring byte + * arrays and some compilers require compile-time knowledge of array + * sizes when declared on the stack. + * + * \{ + */ + + +/* + Most of the values described in this file are defined by the FLAC + format specification. There is nothing to tune here. +*/ + +/** The largest legal metadata type code. */ +#define FLAC__MAX_METADATA_TYPE_CODE (126u) + +/** The minimum block size, in samples, permitted by the format. */ +#define FLAC__MIN_BLOCK_SIZE (16u) + +/** The maximum block size, in samples, permitted by the format. */ +#define FLAC__MAX_BLOCK_SIZE (65535u) + +/** The maximum block size, in samples, permitted by the FLAC subset for + * sample rates up to 48kHz. */ +#define FLAC__SUBSET_MAX_BLOCK_SIZE_48000HZ (4608u) + +/** The maximum number of channels permitted by the format. */ +#define FLAC__MAX_CHANNELS (8u) + +/** The minimum sample resolution permitted by the format. */ +#define FLAC__MIN_BITS_PER_SAMPLE (4u) + +/** The maximum sample resolution permitted by the format. */ +#define FLAC__MAX_BITS_PER_SAMPLE (32u) + +/** The maximum sample resolution permitted by libFLAC. + * + * \warning + * FLAC__MAX_BITS_PER_SAMPLE is the limit of the FLAC format. However, + * the reference encoder/decoder is currently limited to 24 bits because + * of prevalent 32-bit math, so make sure and use this value when + * appropriate. + */ +#define FLAC__REFERENCE_CODEC_MAX_BITS_PER_SAMPLE (24u) + +/** The maximum sample rate permitted by the format. The value is + * ((2 ^ 16) - 1) * 10; see FLAC format + * as to why. + */ +#define FLAC__MAX_SAMPLE_RATE (655350u) + +/** The maximum LPC order permitted by the format. */ +#define FLAC__MAX_LPC_ORDER (32u) + +/** The maximum LPC order permitted by the FLAC subset for sample rates + * up to 48kHz. */ +#define FLAC__SUBSET_MAX_LPC_ORDER_48000HZ (12u) + +/** The minimum quantized linear predictor coefficient precision + * permitted by the format. + */ +#define FLAC__MIN_QLP_COEFF_PRECISION (5u) + +/** The maximum quantized linear predictor coefficient precision + * permitted by the format. + */ +#define FLAC__MAX_QLP_COEFF_PRECISION (15u) + +/** The maximum order of the fixed predictors permitted by the format. */ +#define FLAC__MAX_FIXED_ORDER (4u) + +/** The maximum Rice partition order permitted by the format. */ +#define FLAC__MAX_RICE_PARTITION_ORDER (15u) + +/** The maximum Rice partition order permitted by the FLAC Subset. */ +#define FLAC__SUBSET_MAX_RICE_PARTITION_ORDER (8u) + +/** The version string of the release, stamped onto the libraries and binaries. + * + * \note + * This does not correspond to the shared library version number, which + * is used to determine binary compatibility. + */ +extern FLAC_API const char *FLAC__VERSION_STRING; + +/** The vendor string inserted by the encoder into the VORBIS_COMMENT block. + * This is a NUL-terminated ASCII string; when inserted into the + * VORBIS_COMMENT the trailing null is stripped. + */ +extern FLAC_API const char *FLAC__VENDOR_STRING; + +/** The byte string representation of the beginning of a FLAC stream. */ +extern FLAC_API const FLAC__byte FLAC__STREAM_SYNC_STRING[4]; /* = "fLaC" */ + +/** The 32-bit integer big-endian representation of the beginning of + * a FLAC stream. + */ +extern FLAC_API const unsigned FLAC__STREAM_SYNC; /* = 0x664C6143 */ + +/** The length of the FLAC signature in bits. */ +extern FLAC_API const unsigned FLAC__STREAM_SYNC_LEN; /* = 32 bits */ + +/** The length of the FLAC signature in bytes. */ +#define FLAC__STREAM_SYNC_LENGTH (4u) + + +/***************************************************************************** + * + * Subframe structures + * + *****************************************************************************/ + +/*****************************************************************************/ + +/** An enumeration of the available entropy coding methods. */ +typedef enum { + FLAC__ENTROPY_CODING_METHOD_PARTITIONED_RICE = 0, + /**< Residual is coded by partitioning into contexts, each with it's own + * 4-bit Rice parameter. */ + + FLAC__ENTROPY_CODING_METHOD_PARTITIONED_RICE2 = 1 + /**< Residual is coded by partitioning into contexts, each with it's own + * 5-bit Rice parameter. */ +} FLAC__EntropyCodingMethodType; + +/** Maps a FLAC__EntropyCodingMethodType to a C string. + * + * Using a FLAC__EntropyCodingMethodType as the index to this array will + * give the string equivalent. The contents should not be modified. + */ +extern FLAC_API const char * const FLAC__EntropyCodingMethodTypeString[]; + + +/** Contents of a Rice partitioned residual + */ +typedef struct { + + unsigned *parameters; + /**< The Rice parameters for each context. */ + + unsigned *raw_bits; + /**< Widths for escape-coded partitions. Will be non-zero for escaped + * partitions and zero for unescaped partitions. + */ + + unsigned capacity_by_order; + /**< The capacity of the \a parameters and \a raw_bits arrays + * specified as an order, i.e. the number of array elements + * allocated is 2 ^ \a capacity_by_order. + */ +} FLAC__EntropyCodingMethod_PartitionedRiceContents; + +/** Header for a Rice partitioned residual. (c.f. format specification) + */ +typedef struct { + + unsigned order; + /**< The partition order, i.e. # of contexts = 2 ^ \a order. */ + + const FLAC__EntropyCodingMethod_PartitionedRiceContents *contents; + /**< The context's Rice parameters and/or raw bits. */ + +} FLAC__EntropyCodingMethod_PartitionedRice; + +extern FLAC_API const unsigned FLAC__ENTROPY_CODING_METHOD_PARTITIONED_RICE_ORDER_LEN; /**< == 4 (bits) */ +extern FLAC_API const unsigned FLAC__ENTROPY_CODING_METHOD_PARTITIONED_RICE_PARAMETER_LEN; /**< == 4 (bits) */ +extern FLAC_API const unsigned FLAC__ENTROPY_CODING_METHOD_PARTITIONED_RICE2_PARAMETER_LEN; /**< == 5 (bits) */ +extern FLAC_API const unsigned FLAC__ENTROPY_CODING_METHOD_PARTITIONED_RICE_RAW_LEN; /**< == 5 (bits) */ + +extern FLAC_API const unsigned FLAC__ENTROPY_CODING_METHOD_PARTITIONED_RICE_ESCAPE_PARAMETER; +/**< == (1<format specification) + */ +typedef struct { + FLAC__EntropyCodingMethodType type; + union { + FLAC__EntropyCodingMethod_PartitionedRice partitioned_rice; + } data; +} FLAC__EntropyCodingMethod; + +extern FLAC_API const unsigned FLAC__ENTROPY_CODING_METHOD_TYPE_LEN; /**< == 2 (bits) */ + +/*****************************************************************************/ + +/** An enumeration of the available subframe types. */ +typedef enum { + FLAC__SUBFRAME_TYPE_CONSTANT = 0, /**< constant signal */ + FLAC__SUBFRAME_TYPE_VERBATIM = 1, /**< uncompressed signal */ + FLAC__SUBFRAME_TYPE_FIXED = 2, /**< fixed polynomial prediction */ + FLAC__SUBFRAME_TYPE_LPC = 3 /**< linear prediction */ +} FLAC__SubframeType; + +/** Maps a FLAC__SubframeType to a C string. + * + * Using a FLAC__SubframeType as the index to this array will + * give the string equivalent. The contents should not be modified. + */ +extern FLAC_API const char * const FLAC__SubframeTypeString[]; + + +/** CONSTANT subframe. (c.f. format specification) + */ +typedef struct { + FLAC__int32 value; /**< The constant signal value. */ +} FLAC__Subframe_Constant; + + +/** VERBATIM subframe. (c.f. format specification) + */ +typedef struct { + const FLAC__int32 *data; /**< A pointer to verbatim signal. */ +} FLAC__Subframe_Verbatim; + + +/** FIXED subframe. (c.f. format specification) + */ +typedef struct { + FLAC__EntropyCodingMethod entropy_coding_method; + /**< The residual coding method. */ + + unsigned order; + /**< The polynomial order. */ + + FLAC__int32 warmup[FLAC__MAX_FIXED_ORDER]; + /**< Warmup samples to prime the predictor, length == order. */ + + const FLAC__int32 *residual; + /**< The residual signal, length == (blocksize minus order) samples. */ +} FLAC__Subframe_Fixed; + + +/** LPC subframe. (c.f. format specification) + */ +typedef struct { + FLAC__EntropyCodingMethod entropy_coding_method; + /**< The residual coding method. */ + + unsigned order; + /**< The FIR order. */ + + unsigned qlp_coeff_precision; + /**< Quantized FIR filter coefficient precision in bits. */ + + int quantization_level; + /**< The qlp coeff shift needed. */ + + FLAC__int32 qlp_coeff[FLAC__MAX_LPC_ORDER]; + /**< FIR filter coefficients. */ + + FLAC__int32 warmup[FLAC__MAX_LPC_ORDER]; + /**< Warmup samples to prime the predictor, length == order. */ + + const FLAC__int32 *residual; + /**< The residual signal, length == (blocksize minus order) samples. */ +} FLAC__Subframe_LPC; + +extern FLAC_API const unsigned FLAC__SUBFRAME_LPC_QLP_COEFF_PRECISION_LEN; /**< == 4 (bits) */ +extern FLAC_API const unsigned FLAC__SUBFRAME_LPC_QLP_SHIFT_LEN; /**< == 5 (bits) */ + + +/** FLAC subframe structure. (c.f. format specification) + */ +typedef struct { + FLAC__SubframeType type; + union { + FLAC__Subframe_Constant constant; + FLAC__Subframe_Fixed fixed; + FLAC__Subframe_LPC lpc; + FLAC__Subframe_Verbatim verbatim; + } data; + unsigned wasted_bits; +} FLAC__Subframe; + +/** == 1 (bit) + * + * This used to be a zero-padding bit (hence the name + * FLAC__SUBFRAME_ZERO_PAD_LEN) but is now a reserved bit. It still has a + * mandatory value of \c 0 but in the future may take on the value \c 0 or \c 1 + * to mean something else. + */ +extern FLAC_API const unsigned FLAC__SUBFRAME_ZERO_PAD_LEN; +extern FLAC_API const unsigned FLAC__SUBFRAME_TYPE_LEN; /**< == 6 (bits) */ +extern FLAC_API const unsigned FLAC__SUBFRAME_WASTED_BITS_FLAG_LEN; /**< == 1 (bit) */ + +extern FLAC_API const unsigned FLAC__SUBFRAME_TYPE_CONSTANT_BYTE_ALIGNED_MASK; /**< = 0x00 */ +extern FLAC_API const unsigned FLAC__SUBFRAME_TYPE_VERBATIM_BYTE_ALIGNED_MASK; /**< = 0x02 */ +extern FLAC_API const unsigned FLAC__SUBFRAME_TYPE_FIXED_BYTE_ALIGNED_MASK; /**< = 0x10 */ +extern FLAC_API const unsigned FLAC__SUBFRAME_TYPE_LPC_BYTE_ALIGNED_MASK; /**< = 0x40 */ + +/*****************************************************************************/ + + +/***************************************************************************** + * + * Frame structures + * + *****************************************************************************/ + +/** An enumeration of the available channel assignments. */ +typedef enum { + FLAC__CHANNEL_ASSIGNMENT_INDEPENDENT = 0, /**< independent channels */ + FLAC__CHANNEL_ASSIGNMENT_LEFT_SIDE = 1, /**< left+side stereo */ + FLAC__CHANNEL_ASSIGNMENT_RIGHT_SIDE = 2, /**< right+side stereo */ + FLAC__CHANNEL_ASSIGNMENT_MID_SIDE = 3 /**< mid+side stereo */ +} FLAC__ChannelAssignment; + +/** Maps a FLAC__ChannelAssignment to a C string. + * + * Using a FLAC__ChannelAssignment as the index to this array will + * give the string equivalent. The contents should not be modified. + */ +extern FLAC_API const char * const FLAC__ChannelAssignmentString[]; + +/** An enumeration of the possible frame numbering methods. */ +typedef enum { + FLAC__FRAME_NUMBER_TYPE_FRAME_NUMBER, /**< number contains the frame number */ + FLAC__FRAME_NUMBER_TYPE_SAMPLE_NUMBER /**< number contains the sample number of first sample in frame */ +} FLAC__FrameNumberType; + +/** Maps a FLAC__FrameNumberType to a C string. + * + * Using a FLAC__FrameNumberType as the index to this array will + * give the string equivalent. The contents should not be modified. + */ +extern FLAC_API const char * const FLAC__FrameNumberTypeString[]; + + +/** FLAC frame header structure. (c.f. format specification) + */ +typedef struct { + unsigned blocksize; + /**< The number of samples per subframe. */ + + unsigned sample_rate; + /**< The sample rate in Hz. */ + + unsigned channels; + /**< The number of channels (== number of subframes). */ + + FLAC__ChannelAssignment channel_assignment; + /**< The channel assignment for the frame. */ + + unsigned bits_per_sample; + /**< The sample resolution. */ + + FLAC__FrameNumberType number_type; + /**< The numbering scheme used for the frame. As a convenience, the + * decoder will always convert a frame number to a sample number because + * the rules are complex. */ + + union { + FLAC__uint32 frame_number; + FLAC__uint64 sample_number; + } number; + /**< The frame number or sample number of first sample in frame; + * use the \a number_type value to determine which to use. */ + + FLAC__uint8 crc; + /**< CRC-8 (polynomial = x^8 + x^2 + x^1 + x^0, initialized with 0) + * of the raw frame header bytes, meaning everything before the CRC byte + * including the sync code. + */ +} FLAC__FrameHeader; + +extern FLAC_API const unsigned FLAC__FRAME_HEADER_SYNC; /**< == 0x3ffe; the frame header sync code */ +extern FLAC_API const unsigned FLAC__FRAME_HEADER_SYNC_LEN; /**< == 14 (bits) */ +extern FLAC_API const unsigned FLAC__FRAME_HEADER_RESERVED_LEN; /**< == 1 (bits) */ +extern FLAC_API const unsigned FLAC__FRAME_HEADER_BLOCKING_STRATEGY_LEN; /**< == 1 (bits) */ +extern FLAC_API const unsigned FLAC__FRAME_HEADER_BLOCK_SIZE_LEN; /**< == 4 (bits) */ +extern FLAC_API const unsigned FLAC__FRAME_HEADER_SAMPLE_RATE_LEN; /**< == 4 (bits) */ +extern FLAC_API const unsigned FLAC__FRAME_HEADER_CHANNEL_ASSIGNMENT_LEN; /**< == 4 (bits) */ +extern FLAC_API const unsigned FLAC__FRAME_HEADER_BITS_PER_SAMPLE_LEN; /**< == 3 (bits) */ +extern FLAC_API const unsigned FLAC__FRAME_HEADER_ZERO_PAD_LEN; /**< == 1 (bit) */ +extern FLAC_API const unsigned FLAC__FRAME_HEADER_CRC_LEN; /**< == 8 (bits) */ + + +/** FLAC frame footer structure. (c.f. format specification) + */ +typedef struct { + FLAC__uint16 crc; + /**< CRC-16 (polynomial = x^16 + x^15 + x^2 + x^0, initialized with + * 0) of the bytes before the crc, back to and including the frame header + * sync code. + */ +} FLAC__FrameFooter; + +extern FLAC_API const unsigned FLAC__FRAME_FOOTER_CRC_LEN; /**< == 16 (bits) */ + + +/** FLAC frame structure. (c.f. format specification) + */ +typedef struct { + FLAC__FrameHeader header; + FLAC__Subframe subframes[FLAC__MAX_CHANNELS]; + FLAC__FrameFooter footer; +} FLAC__Frame; + +/*****************************************************************************/ + + +/***************************************************************************** + * + * Meta-data structures + * + *****************************************************************************/ + +/** An enumeration of the available metadata block types. */ +typedef enum { + + FLAC__METADATA_TYPE_STREAMINFO = 0, + /**< STREAMINFO block */ + + FLAC__METADATA_TYPE_PADDING = 1, + /**< PADDING block */ + + FLAC__METADATA_TYPE_APPLICATION = 2, + /**< APPLICATION block */ + + FLAC__METADATA_TYPE_SEEKTABLE = 3, + /**< SEEKTABLE block */ + + FLAC__METADATA_TYPE_VORBIS_COMMENT = 4, + /**< VORBISCOMMENT block (a.k.a. FLAC tags) */ + + FLAC__METADATA_TYPE_CUESHEET = 5, + /**< CUESHEET block */ + + FLAC__METADATA_TYPE_PICTURE = 6, + /**< PICTURE block */ + + FLAC__METADATA_TYPE_UNDEFINED = 7, + /**< marker to denote beginning of undefined type range; this number will increase as new metadata types are added */ + + FLAC__MAX_METADATA_TYPE = FLAC__MAX_METADATA_TYPE_CODE, + /**< No type will ever be greater than this. There is not enough room in the protocol block. */ +} FLAC__MetadataType; + +/** Maps a FLAC__MetadataType to a C string. + * + * Using a FLAC__MetadataType as the index to this array will + * give the string equivalent. The contents should not be modified. + */ +extern FLAC_API const char * const FLAC__MetadataTypeString[]; + + +/** FLAC STREAMINFO structure. (c.f. format specification) + */ +typedef struct { + unsigned min_blocksize, max_blocksize; + unsigned min_framesize, max_framesize; + unsigned sample_rate; + unsigned channels; + unsigned bits_per_sample; + FLAC__uint64 total_samples; + FLAC__byte md5sum[16]; +} FLAC__StreamMetadata_StreamInfo; + +extern FLAC_API const unsigned FLAC__STREAM_METADATA_STREAMINFO_MIN_BLOCK_SIZE_LEN; /**< == 16 (bits) */ +extern FLAC_API const unsigned FLAC__STREAM_METADATA_STREAMINFO_MAX_BLOCK_SIZE_LEN; /**< == 16 (bits) */ +extern FLAC_API const unsigned FLAC__STREAM_METADATA_STREAMINFO_MIN_FRAME_SIZE_LEN; /**< == 24 (bits) */ +extern FLAC_API const unsigned FLAC__STREAM_METADATA_STREAMINFO_MAX_FRAME_SIZE_LEN; /**< == 24 (bits) */ +extern FLAC_API const unsigned FLAC__STREAM_METADATA_STREAMINFO_SAMPLE_RATE_LEN; /**< == 20 (bits) */ +extern FLAC_API const unsigned FLAC__STREAM_METADATA_STREAMINFO_CHANNELS_LEN; /**< == 3 (bits) */ +extern FLAC_API const unsigned FLAC__STREAM_METADATA_STREAMINFO_BITS_PER_SAMPLE_LEN; /**< == 5 (bits) */ +extern FLAC_API const unsigned FLAC__STREAM_METADATA_STREAMINFO_TOTAL_SAMPLES_LEN; /**< == 36 (bits) */ +extern FLAC_API const unsigned FLAC__STREAM_METADATA_STREAMINFO_MD5SUM_LEN; /**< == 128 (bits) */ + +/** The total stream length of the STREAMINFO block in bytes. */ +#define FLAC__STREAM_METADATA_STREAMINFO_LENGTH (34u) + +/** FLAC PADDING structure. (c.f. format specification) + */ +typedef struct { + int dummy; + /**< Conceptually this is an empty struct since we don't store the + * padding bytes. Empty structs are not allowed by some C compilers, + * hence the dummy. + */ +} FLAC__StreamMetadata_Padding; + + +/** FLAC APPLICATION structure. (c.f. format specification) + */ +typedef struct { + FLAC__byte id[4]; + FLAC__byte *data; +} FLAC__StreamMetadata_Application; + +extern FLAC_API const unsigned FLAC__STREAM_METADATA_APPLICATION_ID_LEN; /**< == 32 (bits) */ + +/** SeekPoint structure used in SEEKTABLE blocks. (c.f. format specification) + */ +typedef struct { + FLAC__uint64 sample_number; + /**< The sample number of the target frame. */ + + FLAC__uint64 stream_offset; + /**< The offset, in bytes, of the target frame with respect to + * beginning of the first frame. */ + + unsigned frame_samples; + /**< The number of samples in the target frame. */ +} FLAC__StreamMetadata_SeekPoint; + +extern FLAC_API const unsigned FLAC__STREAM_METADATA_SEEKPOINT_SAMPLE_NUMBER_LEN; /**< == 64 (bits) */ +extern FLAC_API const unsigned FLAC__STREAM_METADATA_SEEKPOINT_STREAM_OFFSET_LEN; /**< == 64 (bits) */ +extern FLAC_API const unsigned FLAC__STREAM_METADATA_SEEKPOINT_FRAME_SAMPLES_LEN; /**< == 16 (bits) */ + +/** The total stream length of a seek point in bytes. */ +#define FLAC__STREAM_METADATA_SEEKPOINT_LENGTH (18u) + +/** The value used in the \a sample_number field of + * FLAC__StreamMetadataSeekPoint used to indicate a placeholder + * point (== 0xffffffffffffffff). + */ +extern FLAC_API const FLAC__uint64 FLAC__STREAM_METADATA_SEEKPOINT_PLACEHOLDER; + + +/** FLAC SEEKTABLE structure. (c.f. format specification) + * + * \note From the format specification: + * - The seek points must be sorted by ascending sample number. + * - Each seek point's sample number must be the first sample of the + * target frame. + * - Each seek point's sample number must be unique within the table. + * - Existence of a SEEKTABLE block implies a correct setting of + * total_samples in the stream_info block. + * - Behavior is undefined when more than one SEEKTABLE block is + * present in a stream. + */ +typedef struct { + unsigned num_points; + FLAC__StreamMetadata_SeekPoint *points; +} FLAC__StreamMetadata_SeekTable; + + +/** Vorbis comment entry structure used in VORBIS_COMMENT blocks. (c.f. format specification) + * + * For convenience, the APIs maintain a trailing NUL character at the end of + * \a entry which is not counted toward \a length, i.e. + * \code strlen(entry) == length \endcode + */ +typedef struct { + FLAC__uint32 length; + FLAC__byte *entry; +} FLAC__StreamMetadata_VorbisComment_Entry; + +extern FLAC_API const unsigned FLAC__STREAM_METADATA_VORBIS_COMMENT_ENTRY_LENGTH_LEN; /**< == 32 (bits) */ + + +/** FLAC VORBIS_COMMENT structure. (c.f. format specification) + */ +typedef struct { + FLAC__StreamMetadata_VorbisComment_Entry vendor_string; + FLAC__uint32 num_comments; + FLAC__StreamMetadata_VorbisComment_Entry *comments; +} FLAC__StreamMetadata_VorbisComment; + +extern FLAC_API const unsigned FLAC__STREAM_METADATA_VORBIS_COMMENT_NUM_COMMENTS_LEN; /**< == 32 (bits) */ + + +/** FLAC CUESHEET track index structure. (See the + * format specification for + * the full description of each field.) + */ +typedef struct { + FLAC__uint64 offset; + /**< Offset in samples, relative to the track offset, of the index + * point. + */ + + FLAC__byte number; + /**< The index point number. */ +} FLAC__StreamMetadata_CueSheet_Index; + +extern FLAC_API const unsigned FLAC__STREAM_METADATA_CUESHEET_INDEX_OFFSET_LEN; /**< == 64 (bits) */ +extern FLAC_API const unsigned FLAC__STREAM_METADATA_CUESHEET_INDEX_NUMBER_LEN; /**< == 8 (bits) */ +extern FLAC_API const unsigned FLAC__STREAM_METADATA_CUESHEET_INDEX_RESERVED_LEN; /**< == 3*8 (bits) */ + + +/** FLAC CUESHEET track structure. (See the + * format specification for + * the full description of each field.) + */ +typedef struct { + FLAC__uint64 offset; + /**< Track offset in samples, relative to the beginning of the FLAC audio stream. */ + + FLAC__byte number; + /**< The track number. */ + + char isrc[13]; + /**< Track ISRC. This is a 12-digit alphanumeric code plus a trailing \c NUL byte */ + + unsigned type:1; + /**< The track type: 0 for audio, 1 for non-audio. */ + + unsigned pre_emphasis:1; + /**< The pre-emphasis flag: 0 for no pre-emphasis, 1 for pre-emphasis. */ + + FLAC__byte num_indices; + /**< The number of track index points. */ + + FLAC__StreamMetadata_CueSheet_Index *indices; + /**< NULL if num_indices == 0, else pointer to array of index points. */ + +} FLAC__StreamMetadata_CueSheet_Track; + +extern FLAC_API const unsigned FLAC__STREAM_METADATA_CUESHEET_TRACK_OFFSET_LEN; /**< == 64 (bits) */ +extern FLAC_API const unsigned FLAC__STREAM_METADATA_CUESHEET_TRACK_NUMBER_LEN; /**< == 8 (bits) */ +extern FLAC_API const unsigned FLAC__STREAM_METADATA_CUESHEET_TRACK_ISRC_LEN; /**< == 12*8 (bits) */ +extern FLAC_API const unsigned FLAC__STREAM_METADATA_CUESHEET_TRACK_TYPE_LEN; /**< == 1 (bit) */ +extern FLAC_API const unsigned FLAC__STREAM_METADATA_CUESHEET_TRACK_PRE_EMPHASIS_LEN; /**< == 1 (bit) */ +extern FLAC_API const unsigned FLAC__STREAM_METADATA_CUESHEET_TRACK_RESERVED_LEN; /**< == 6+13*8 (bits) */ +extern FLAC_API const unsigned FLAC__STREAM_METADATA_CUESHEET_TRACK_NUM_INDICES_LEN; /**< == 8 (bits) */ + + +/** FLAC CUESHEET structure. (See the + * format specification + * for the full description of each field.) + */ +typedef struct { + char media_catalog_number[129]; + /**< Media catalog number, in ASCII printable characters 0x20-0x7e. In + * general, the media catalog number may be 0 to 128 bytes long; any + * unused characters should be right-padded with NUL characters. + */ + + FLAC__uint64 lead_in; + /**< The number of lead-in samples. */ + + FLAC__bool is_cd; + /**< \c true if CUESHEET corresponds to a Compact Disc, else \c false. */ + + unsigned num_tracks; + /**< The number of tracks. */ + + FLAC__StreamMetadata_CueSheet_Track *tracks; + /**< NULL if num_tracks == 0, else pointer to array of tracks. */ + +} FLAC__StreamMetadata_CueSheet; + +extern FLAC_API const unsigned FLAC__STREAM_METADATA_CUESHEET_MEDIA_CATALOG_NUMBER_LEN; /**< == 128*8 (bits) */ +extern FLAC_API const unsigned FLAC__STREAM_METADATA_CUESHEET_LEAD_IN_LEN; /**< == 64 (bits) */ +extern FLAC_API const unsigned FLAC__STREAM_METADATA_CUESHEET_IS_CD_LEN; /**< == 1 (bit) */ +extern FLAC_API const unsigned FLAC__STREAM_METADATA_CUESHEET_RESERVED_LEN; /**< == 7+258*8 (bits) */ +extern FLAC_API const unsigned FLAC__STREAM_METADATA_CUESHEET_NUM_TRACKS_LEN; /**< == 8 (bits) */ + + +/** An enumeration of the PICTURE types (see FLAC__StreamMetadataPicture and id3 v2.4 APIC tag). */ +typedef enum { + FLAC__STREAM_METADATA_PICTURE_TYPE_OTHER = 0, /**< Other */ + FLAC__STREAM_METADATA_PICTURE_TYPE_FILE_ICON_STANDARD = 1, /**< 32x32 pixels 'file icon' (PNG only) */ + FLAC__STREAM_METADATA_PICTURE_TYPE_FILE_ICON = 2, /**< Other file icon */ + FLAC__STREAM_METADATA_PICTURE_TYPE_FRONT_COVER = 3, /**< Cover (front) */ + FLAC__STREAM_METADATA_PICTURE_TYPE_BACK_COVER = 4, /**< Cover (back) */ + FLAC__STREAM_METADATA_PICTURE_TYPE_LEAFLET_PAGE = 5, /**< Leaflet page */ + FLAC__STREAM_METADATA_PICTURE_TYPE_MEDIA = 6, /**< Media (e.g. label side of CD) */ + FLAC__STREAM_METADATA_PICTURE_TYPE_LEAD_ARTIST = 7, /**< Lead artist/lead performer/soloist */ + FLAC__STREAM_METADATA_PICTURE_TYPE_ARTIST = 8, /**< Artist/performer */ + FLAC__STREAM_METADATA_PICTURE_TYPE_CONDUCTOR = 9, /**< Conductor */ + FLAC__STREAM_METADATA_PICTURE_TYPE_BAND = 10, /**< Band/Orchestra */ + FLAC__STREAM_METADATA_PICTURE_TYPE_COMPOSER = 11, /**< Composer */ + FLAC__STREAM_METADATA_PICTURE_TYPE_LYRICIST = 12, /**< Lyricist/text writer */ + FLAC__STREAM_METADATA_PICTURE_TYPE_RECORDING_LOCATION = 13, /**< Recording Location */ + FLAC__STREAM_METADATA_PICTURE_TYPE_DURING_RECORDING = 14, /**< During recording */ + FLAC__STREAM_METADATA_PICTURE_TYPE_DURING_PERFORMANCE = 15, /**< During performance */ + FLAC__STREAM_METADATA_PICTURE_TYPE_VIDEO_SCREEN_CAPTURE = 16, /**< Movie/video screen capture */ + FLAC__STREAM_METADATA_PICTURE_TYPE_FISH = 17, /**< A bright coloured fish */ + FLAC__STREAM_METADATA_PICTURE_TYPE_ILLUSTRATION = 18, /**< Illustration */ + FLAC__STREAM_METADATA_PICTURE_TYPE_BAND_LOGOTYPE = 19, /**< Band/artist logotype */ + FLAC__STREAM_METADATA_PICTURE_TYPE_PUBLISHER_LOGOTYPE = 20, /**< Publisher/Studio logotype */ + FLAC__STREAM_METADATA_PICTURE_TYPE_UNDEFINED +} FLAC__StreamMetadata_Picture_Type; + +/** Maps a FLAC__StreamMetadata_Picture_Type to a C string. + * + * Using a FLAC__StreamMetadata_Picture_Type as the index to this array + * will give the string equivalent. The contents should not be + * modified. + */ +extern FLAC_API const char * const FLAC__StreamMetadata_Picture_TypeString[]; + +/** FLAC PICTURE structure. (See the + * format specification + * for the full description of each field.) + */ +typedef struct { + FLAC__StreamMetadata_Picture_Type type; + /**< The kind of picture stored. */ + + char *mime_type; + /**< Picture data's MIME type, in ASCII printable characters + * 0x20-0x7e, NUL terminated. For best compatibility with players, + * use picture data of MIME type \c image/jpeg or \c image/png. A + * MIME type of '-->' is also allowed, in which case the picture + * data should be a complete URL. In file storage, the MIME type is + * stored as a 32-bit length followed by the ASCII string with no NUL + * terminator, but is converted to a plain C string in this structure + * for convenience. + */ + + FLAC__byte *description; + /**< Picture's description in UTF-8, NUL terminated. In file storage, + * the description is stored as a 32-bit length followed by the UTF-8 + * string with no NUL terminator, but is converted to a plain C string + * in this structure for convenience. + */ + + FLAC__uint32 width; + /**< Picture's width in pixels. */ + + FLAC__uint32 height; + /**< Picture's height in pixels. */ + + FLAC__uint32 depth; + /**< Picture's color depth in bits-per-pixel. */ + + FLAC__uint32 colors; + /**< For indexed palettes (like GIF), picture's number of colors (the + * number of palette entries), or \c 0 for non-indexed (i.e. 2^depth). + */ + + FLAC__uint32 data_length; + /**< Length of binary picture data in bytes. */ + + FLAC__byte *data; + /**< Binary picture data. */ + +} FLAC__StreamMetadata_Picture; + +extern FLAC_API const unsigned FLAC__STREAM_METADATA_PICTURE_TYPE_LEN; /**< == 32 (bits) */ +extern FLAC_API const unsigned FLAC__STREAM_METADATA_PICTURE_MIME_TYPE_LENGTH_LEN; /**< == 32 (bits) */ +extern FLAC_API const unsigned FLAC__STREAM_METADATA_PICTURE_DESCRIPTION_LENGTH_LEN; /**< == 32 (bits) */ +extern FLAC_API const unsigned FLAC__STREAM_METADATA_PICTURE_WIDTH_LEN; /**< == 32 (bits) */ +extern FLAC_API const unsigned FLAC__STREAM_METADATA_PICTURE_HEIGHT_LEN; /**< == 32 (bits) */ +extern FLAC_API const unsigned FLAC__STREAM_METADATA_PICTURE_DEPTH_LEN; /**< == 32 (bits) */ +extern FLAC_API const unsigned FLAC__STREAM_METADATA_PICTURE_COLORS_LEN; /**< == 32 (bits) */ +extern FLAC_API const unsigned FLAC__STREAM_METADATA_PICTURE_DATA_LENGTH_LEN; /**< == 32 (bits) */ + + +/** Structure that is used when a metadata block of unknown type is loaded. + * The contents are opaque. The structure is used only internally to + * correctly handle unknown metadata. + */ +typedef struct { + FLAC__byte *data; +} FLAC__StreamMetadata_Unknown; + + +/** FLAC metadata block structure. (c.f. format specification) + */ +typedef struct { + FLAC__MetadataType type; + /**< The type of the metadata block; used determine which member of the + * \a data union to dereference. If type >= FLAC__METADATA_TYPE_UNDEFINED + * then \a data.unknown must be used. */ + + FLAC__bool is_last; + /**< \c true if this metadata block is the last, else \a false */ + + unsigned length; + /**< Length, in bytes, of the block data as it appears in the stream. */ + + union { + FLAC__StreamMetadata_StreamInfo stream_info; + FLAC__StreamMetadata_Padding padding; + FLAC__StreamMetadata_Application application; + FLAC__StreamMetadata_SeekTable seek_table; + FLAC__StreamMetadata_VorbisComment vorbis_comment; + FLAC__StreamMetadata_CueSheet cue_sheet; + FLAC__StreamMetadata_Picture picture; + FLAC__StreamMetadata_Unknown unknown; + } data; + /**< Polymorphic block data; use the \a type value to determine which + * to use. */ +} FLAC__StreamMetadata; + +extern FLAC_API const unsigned FLAC__STREAM_METADATA_IS_LAST_LEN; /**< == 1 (bit) */ +extern FLAC_API const unsigned FLAC__STREAM_METADATA_TYPE_LEN; /**< == 7 (bits) */ +extern FLAC_API const unsigned FLAC__STREAM_METADATA_LENGTH_LEN; /**< == 24 (bits) */ + +/** The total stream length of a metadata block header in bytes. */ +#define FLAC__STREAM_METADATA_HEADER_LENGTH (4u) + +/*****************************************************************************/ + + +/***************************************************************************** + * + * Utility functions + * + *****************************************************************************/ + +/** Tests that a sample rate is valid for FLAC. + * + * \param sample_rate The sample rate to test for compliance. + * \retval FLAC__bool + * \c true if the given sample rate conforms to the specification, else + * \c false. + */ +FLAC_API FLAC__bool FLAC__format_sample_rate_is_valid(unsigned sample_rate); + +/** Tests that a blocksize at the given sample rate is valid for the FLAC + * subset. + * + * \param blocksize The blocksize to test for compliance. + * \param sample_rate The sample rate is needed, since the valid subset + * blocksize depends on the sample rate. + * \retval FLAC__bool + * \c true if the given blocksize conforms to the specification for the + * subset at the given sample rate, else \c false. + */ +FLAC_API FLAC__bool FLAC__format_blocksize_is_subset(unsigned blocksize, unsigned sample_rate); + +/** Tests that a sample rate is valid for the FLAC subset. The subset rules + * for valid sample rates are slightly more complex since the rate has to + * be expressible completely in the frame header. + * + * \param sample_rate The sample rate to test for compliance. + * \retval FLAC__bool + * \c true if the given sample rate conforms to the specification for the + * subset, else \c false. + */ +FLAC_API FLAC__bool FLAC__format_sample_rate_is_subset(unsigned sample_rate); + +/** Check a Vorbis comment entry name to see if it conforms to the Vorbis + * comment specification. + * + * Vorbis comment names must be composed only of characters from + * [0x20-0x3C,0x3E-0x7D]. + * + * \param name A NUL-terminated string to be checked. + * \assert + * \code name != NULL \endcode + * \retval FLAC__bool + * \c false if entry name is illegal, else \c true. + */ +FLAC_API FLAC__bool FLAC__format_vorbiscomment_entry_name_is_legal(const char *name); + +/** Check a Vorbis comment entry value to see if it conforms to the Vorbis + * comment specification. + * + * Vorbis comment values must be valid UTF-8 sequences. + * + * \param value A string to be checked. + * \param length A the length of \a value in bytes. May be + * \c (unsigned)(-1) to indicate that \a value is a plain + * UTF-8 NUL-terminated string. + * \assert + * \code value != NULL \endcode + * \retval FLAC__bool + * \c false if entry name is illegal, else \c true. + */ +FLAC_API FLAC__bool FLAC__format_vorbiscomment_entry_value_is_legal(const FLAC__byte *value, unsigned length); + +/** Check a Vorbis comment entry to see if it conforms to the Vorbis + * comment specification. + * + * Vorbis comment entries must be of the form 'name=value', and 'name' and + * 'value' must be legal according to + * FLAC__format_vorbiscomment_entry_name_is_legal() and + * FLAC__format_vorbiscomment_entry_value_is_legal() respectively. + * + * \param entry An entry to be checked. + * \param length The length of \a entry in bytes. + * \assert + * \code value != NULL \endcode + * \retval FLAC__bool + * \c false if entry name is illegal, else \c true. + */ +FLAC_API FLAC__bool FLAC__format_vorbiscomment_entry_is_legal(const FLAC__byte *entry, unsigned length); + +/** Check a seek table to see if it conforms to the FLAC specification. + * See the format specification for limits on the contents of the + * seek table. + * + * \param seek_table A pointer to a seek table to be checked. + * \assert + * \code seek_table != NULL \endcode + * \retval FLAC__bool + * \c false if seek table is illegal, else \c true. + */ +FLAC_API FLAC__bool FLAC__format_seektable_is_legal(const FLAC__StreamMetadata_SeekTable *seek_table); + +/** Sort a seek table's seek points according to the format specification. + * This includes a "unique-ification" step to remove duplicates, i.e. + * seek points with identical \a sample_number values. Duplicate seek + * points are converted into placeholder points and sorted to the end of + * the table. + * + * \param seek_table A pointer to a seek table to be sorted. + * \assert + * \code seek_table != NULL \endcode + * \retval unsigned + * The number of duplicate seek points converted into placeholders. + */ +FLAC_API unsigned FLAC__format_seektable_sort(FLAC__StreamMetadata_SeekTable *seek_table); + +/** Check a cue sheet to see if it conforms to the FLAC specification. + * See the format specification for limits on the contents of the + * cue sheet. + * + * \param cue_sheet A pointer to an existing cue sheet to be checked. + * \param check_cd_da_subset If \c true, check CUESHEET against more + * stringent requirements for a CD-DA (audio) disc. + * \param violation Address of a pointer to a string. If there is a + * violation, a pointer to a string explanation of the + * violation will be returned here. \a violation may be + * \c NULL if you don't need the returned string. Do not + * free the returned string; it will always point to static + * data. + * \assert + * \code cue_sheet != NULL \endcode + * \retval FLAC__bool + * \c false if cue sheet is illegal, else \c true. + */ +FLAC_API FLAC__bool FLAC__format_cuesheet_is_legal(const FLAC__StreamMetadata_CueSheet *cue_sheet, FLAC__bool check_cd_da_subset, const char **violation); + +/** Check picture data to see if it conforms to the FLAC specification. + * See the format specification for limits on the contents of the + * PICTURE block. + * + * \param picture A pointer to existing picture data to be checked. + * \param violation Address of a pointer to a string. If there is a + * violation, a pointer to a string explanation of the + * violation will be returned here. \a violation may be + * \c NULL if you don't need the returned string. Do not + * free the returned string; it will always point to static + * data. + * \assert + * \code picture != NULL \endcode + * \retval FLAC__bool + * \c false if picture data is illegal, else \c true. + */ +FLAC_API FLAC__bool FLAC__format_picture_is_legal(const FLAC__StreamMetadata_Picture *picture, const char **violation); + +/* \} */ + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/core/deps/flac/include/FLAC/metadata.h b/core/deps/flac/include/FLAC/metadata.h new file mode 100644 index 000000000..4e18cd684 --- /dev/null +++ b/core/deps/flac/include/FLAC/metadata.h @@ -0,0 +1,2182 @@ +/* libFLAC - Free Lossless Audio Codec library + * Copyright (C) 2001-2009 Josh Coalson + * Copyright (C) 2011-2016 Xiph.Org Foundation + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * - Neither the name of the Xiph.org Foundation nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef FLAC__METADATA_H +#define FLAC__METADATA_H + +#include /* for off_t */ +#include "export.h" +#include "callback.h" +#include "format.h" + +/* -------------------------------------------------------------------- + (For an example of how all these routines are used, see the source + code for the unit tests in src/test_libFLAC/metadata_*.c, or + metaflac in src/metaflac/) + ------------------------------------------------------------------*/ + +/** \file include/FLAC/metadata.h + * + * \brief + * This module provides functions for creating and manipulating FLAC + * metadata blocks in memory, and three progressively more powerful + * interfaces for traversing and editing metadata in FLAC files. + * + * See the detailed documentation for each interface in the + * \link flac_metadata metadata \endlink module. + */ + +/** \defgroup flac_metadata FLAC/metadata.h: metadata interfaces + * \ingroup flac + * + * \brief + * This module provides functions for creating and manipulating FLAC + * metadata blocks in memory, and three progressively more powerful + * interfaces for traversing and editing metadata in native FLAC files. + * Note that currently only the Chain interface (level 2) supports Ogg + * FLAC files, and it is read-only i.e. no writing back changed + * metadata to file. + * + * There are three metadata interfaces of increasing complexity: + * + * Level 0: + * Read-only access to the STREAMINFO, VORBIS_COMMENT, CUESHEET, and + * PICTURE blocks. + * + * Level 1: + * Read-write access to all metadata blocks. This level is write- + * efficient in most cases (more on this below), and uses less memory + * than level 2. + * + * Level 2: + * Read-write access to all metadata blocks. This level is write- + * efficient in all cases, but uses more memory since all metadata for + * the whole file is read into memory and manipulated before writing + * out again. + * + * What do we mean by efficient? Since FLAC metadata appears at the + * beginning of the file, when writing metadata back to a FLAC file + * it is possible to grow or shrink the metadata such that the entire + * file must be rewritten. However, if the size remains the same during + * changes or PADDING blocks are utilized, only the metadata needs to be + * overwritten, which is much faster. + * + * Efficient means the whole file is rewritten at most one time, and only + * when necessary. Level 1 is not efficient only in the case that you + * cause more than one metadata block to grow or shrink beyond what can + * be accomodated by padding. In this case you should probably use level + * 2, which allows you to edit all the metadata for a file in memory and + * write it out all at once. + * + * All levels know how to skip over and not disturb an ID3v2 tag at the + * front of the file. + * + * All levels access files via their filenames. In addition, level 2 + * has additional alternative read and write functions that take an I/O + * handle and callbacks, for situations where access by filename is not + * possible. + * + * In addition to the three interfaces, this module defines functions for + * creating and manipulating various metadata objects in memory. As we see + * from the Format module, FLAC metadata blocks in memory are very primitive + * structures for storing information in an efficient way. Reading + * information from the structures is easy but creating or modifying them + * directly is more complex. The metadata object routines here facilitate + * this by taking care of the consistency and memory management drudgery. + * + * Unless you will be using the level 1 or 2 interfaces to modify existing + * metadata however, you will not probably not need these. + * + * From a dependency standpoint, none of the encoders or decoders require + * the metadata module. This is so that embedded users can strip out the + * metadata module from libFLAC to reduce the size and complexity. + */ + +#ifdef __cplusplus +extern "C" { +#endif + + +/** \defgroup flac_metadata_level0 FLAC/metadata.h: metadata level 0 interface + * \ingroup flac_metadata + * + * \brief + * The level 0 interface consists of individual routines to read the + * STREAMINFO, VORBIS_COMMENT, CUESHEET, and PICTURE blocks, requiring + * only a filename. + * + * They try to skip any ID3v2 tag at the head of the file. + * + * \{ + */ + +/** Read the STREAMINFO metadata block of the given FLAC file. This function + * will try to skip any ID3v2 tag at the head of the file. + * + * \param filename The path to the FLAC file to read. + * \param streaminfo A pointer to space for the STREAMINFO block. Since + * FLAC__StreamMetadata is a simple structure with no + * memory allocation involved, you pass the address of + * an existing structure. It need not be initialized. + * \assert + * \code filename != NULL \endcode + * \code streaminfo != NULL \endcode + * \retval FLAC__bool + * \c true if a valid STREAMINFO block was read from \a filename. Returns + * \c false if there was a memory allocation error, a file decoder error, + * or the file contained no STREAMINFO block. (A memory allocation error + * is possible because this function must set up a file decoder.) + */ +FLAC_API FLAC__bool FLAC__metadata_get_streaminfo(const char *filename, FLAC__StreamMetadata *streaminfo); + +/** Read the VORBIS_COMMENT metadata block of the given FLAC file. This + * function will try to skip any ID3v2 tag at the head of the file. + * + * \param filename The path to the FLAC file to read. + * \param tags The address where the returned pointer will be + * stored. The \a tags object must be deleted by + * the caller using FLAC__metadata_object_delete(). + * \assert + * \code filename != NULL \endcode + * \code tags != NULL \endcode + * \retval FLAC__bool + * \c true if a valid VORBIS_COMMENT block was read from \a filename, + * and \a *tags will be set to the address of the metadata structure. + * Returns \c false if there was a memory allocation error, a file + * decoder error, or the file contained no VORBIS_COMMENT block, and + * \a *tags will be set to \c NULL. + */ +FLAC_API FLAC__bool FLAC__metadata_get_tags(const char *filename, FLAC__StreamMetadata **tags); + +/** Read the CUESHEET metadata block of the given FLAC file. This + * function will try to skip any ID3v2 tag at the head of the file. + * + * \param filename The path to the FLAC file to read. + * \param cuesheet The address where the returned pointer will be + * stored. The \a cuesheet object must be deleted by + * the caller using FLAC__metadata_object_delete(). + * \assert + * \code filename != NULL \endcode + * \code cuesheet != NULL \endcode + * \retval FLAC__bool + * \c true if a valid CUESHEET block was read from \a filename, + * and \a *cuesheet will be set to the address of the metadata + * structure. Returns \c false if there was a memory allocation + * error, a file decoder error, or the file contained no CUESHEET + * block, and \a *cuesheet will be set to \c NULL. + */ +FLAC_API FLAC__bool FLAC__metadata_get_cuesheet(const char *filename, FLAC__StreamMetadata **cuesheet); + +/** Read a PICTURE metadata block of the given FLAC file. This + * function will try to skip any ID3v2 tag at the head of the file. + * Since there can be more than one PICTURE block in a file, this + * function takes a number of parameters that act as constraints to + * the search. The PICTURE block with the largest area matching all + * the constraints will be returned, or \a *picture will be set to + * \c NULL if there was no such block. + * + * \param filename The path to the FLAC file to read. + * \param picture The address where the returned pointer will be + * stored. The \a picture object must be deleted by + * the caller using FLAC__metadata_object_delete(). + * \param type The desired picture type. Use \c -1 to mean + * "any type". + * \param mime_type The desired MIME type, e.g. "image/jpeg". The + * string will be matched exactly. Use \c NULL to + * mean "any MIME type". + * \param description The desired description. The string will be + * matched exactly. Use \c NULL to mean "any + * description". + * \param max_width The maximum width in pixels desired. Use + * \c (unsigned)(-1) to mean "any width". + * \param max_height The maximum height in pixels desired. Use + * \c (unsigned)(-1) to mean "any height". + * \param max_depth The maximum color depth in bits-per-pixel desired. + * Use \c (unsigned)(-1) to mean "any depth". + * \param max_colors The maximum number of colors desired. Use + * \c (unsigned)(-1) to mean "any number of colors". + * \assert + * \code filename != NULL \endcode + * \code picture != NULL \endcode + * \retval FLAC__bool + * \c true if a valid PICTURE block was read from \a filename, + * and \a *picture will be set to the address of the metadata + * structure. Returns \c false if there was a memory allocation + * error, a file decoder error, or the file contained no PICTURE + * block, and \a *picture will be set to \c NULL. + */ +FLAC_API FLAC__bool FLAC__metadata_get_picture(const char *filename, FLAC__StreamMetadata **picture, FLAC__StreamMetadata_Picture_Type type, const char *mime_type, const FLAC__byte *description, unsigned max_width, unsigned max_height, unsigned max_depth, unsigned max_colors); + +/* \} */ + + +/** \defgroup flac_metadata_level1 FLAC/metadata.h: metadata level 1 interface + * \ingroup flac_metadata + * + * \brief + * The level 1 interface provides read-write access to FLAC file metadata and + * operates directly on the FLAC file. + * + * The general usage of this interface is: + * + * - Create an iterator using FLAC__metadata_simple_iterator_new() + * - Attach it to a file using FLAC__metadata_simple_iterator_init() and check + * the exit code. Call FLAC__metadata_simple_iterator_is_writable() to + * see if the file is writable, or only read access is allowed. + * - Use FLAC__metadata_simple_iterator_next() and + * FLAC__metadata_simple_iterator_prev() to traverse the blocks. + * This is does not read the actual blocks themselves. + * FLAC__metadata_simple_iterator_next() is relatively fast. + * FLAC__metadata_simple_iterator_prev() is slower since it needs to search + * forward from the front of the file. + * - Use FLAC__metadata_simple_iterator_get_block_type() or + * FLAC__metadata_simple_iterator_get_block() to access the actual data at + * the current iterator position. The returned object is yours to modify + * and free. + * - Use FLAC__metadata_simple_iterator_set_block() to write a modified block + * back. You must have write permission to the original file. Make sure to + * read the whole comment to FLAC__metadata_simple_iterator_set_block() + * below. + * - Use FLAC__metadata_simple_iterator_insert_block_after() to add new blocks. + * Use the object creation functions from + * \link flac_metadata_object here \endlink to generate new objects. + * - Use FLAC__metadata_simple_iterator_delete_block() to remove the block + * currently referred to by the iterator, or replace it with padding. + * - Destroy the iterator with FLAC__metadata_simple_iterator_delete() when + * finished. + * + * \note + * The FLAC file remains open the whole time between + * FLAC__metadata_simple_iterator_init() and + * FLAC__metadata_simple_iterator_delete(), so make sure you are not altering + * the file during this time. + * + * \note + * Do not modify the \a is_last, \a length, or \a type fields of returned + * FLAC__StreamMetadata objects. These are managed automatically. + * + * \note + * If any of the modification functions + * (FLAC__metadata_simple_iterator_set_block(), + * FLAC__metadata_simple_iterator_delete_block(), + * FLAC__metadata_simple_iterator_insert_block_after(), etc.) return \c false, + * you should delete the iterator as it may no longer be valid. + * + * \{ + */ + +struct FLAC__Metadata_SimpleIterator; +/** The opaque structure definition for the level 1 iterator type. + * See the + * \link flac_metadata_level1 metadata level 1 module \endlink + * for a detailed description. + */ +typedef struct FLAC__Metadata_SimpleIterator FLAC__Metadata_SimpleIterator; + +/** Status type for FLAC__Metadata_SimpleIterator. + * + * The iterator's current status can be obtained by calling FLAC__metadata_simple_iterator_status(). + */ +typedef enum { + + FLAC__METADATA_SIMPLE_ITERATOR_STATUS_OK = 0, + /**< The iterator is in the normal OK state */ + + FLAC__METADATA_SIMPLE_ITERATOR_STATUS_ILLEGAL_INPUT, + /**< The data passed into a function violated the function's usage criteria */ + + FLAC__METADATA_SIMPLE_ITERATOR_STATUS_ERROR_OPENING_FILE, + /**< The iterator could not open the target file */ + + FLAC__METADATA_SIMPLE_ITERATOR_STATUS_NOT_A_FLAC_FILE, + /**< The iterator could not find the FLAC signature at the start of the file */ + + FLAC__METADATA_SIMPLE_ITERATOR_STATUS_NOT_WRITABLE, + /**< The iterator tried to write to a file that was not writable */ + + FLAC__METADATA_SIMPLE_ITERATOR_STATUS_BAD_METADATA, + /**< The iterator encountered input that does not conform to the FLAC metadata specification */ + + FLAC__METADATA_SIMPLE_ITERATOR_STATUS_READ_ERROR, + /**< The iterator encountered an error while reading the FLAC file */ + + FLAC__METADATA_SIMPLE_ITERATOR_STATUS_SEEK_ERROR, + /**< The iterator encountered an error while seeking in the FLAC file */ + + FLAC__METADATA_SIMPLE_ITERATOR_STATUS_WRITE_ERROR, + /**< The iterator encountered an error while writing the FLAC file */ + + FLAC__METADATA_SIMPLE_ITERATOR_STATUS_RENAME_ERROR, + /**< The iterator encountered an error renaming the FLAC file */ + + FLAC__METADATA_SIMPLE_ITERATOR_STATUS_UNLINK_ERROR, + /**< The iterator encountered an error removing the temporary file */ + + FLAC__METADATA_SIMPLE_ITERATOR_STATUS_MEMORY_ALLOCATION_ERROR, + /**< Memory allocation failed */ + + FLAC__METADATA_SIMPLE_ITERATOR_STATUS_INTERNAL_ERROR + /**< The caller violated an assertion or an unexpected error occurred */ + +} FLAC__Metadata_SimpleIteratorStatus; + +/** Maps a FLAC__Metadata_SimpleIteratorStatus to a C string. + * + * Using a FLAC__Metadata_SimpleIteratorStatus as the index to this array + * will give the string equivalent. The contents should not be modified. + */ +extern FLAC_API const char * const FLAC__Metadata_SimpleIteratorStatusString[]; + + +/** Create a new iterator instance. + * + * \retval FLAC__Metadata_SimpleIterator* + * \c NULL if there was an error allocating memory, else the new instance. + */ +FLAC_API FLAC__Metadata_SimpleIterator *FLAC__metadata_simple_iterator_new(void); + +/** Free an iterator instance. Deletes the object pointed to by \a iterator. + * + * \param iterator A pointer to an existing iterator. + * \assert + * \code iterator != NULL \endcode + */ +FLAC_API void FLAC__metadata_simple_iterator_delete(FLAC__Metadata_SimpleIterator *iterator); + +/** Get the current status of the iterator. Call this after a function + * returns \c false to get the reason for the error. Also resets the status + * to FLAC__METADATA_SIMPLE_ITERATOR_STATUS_OK. + * + * \param iterator A pointer to an existing iterator. + * \assert + * \code iterator != NULL \endcode + * \retval FLAC__Metadata_SimpleIteratorStatus + * The current status of the iterator. + */ +FLAC_API FLAC__Metadata_SimpleIteratorStatus FLAC__metadata_simple_iterator_status(FLAC__Metadata_SimpleIterator *iterator); + +/** Initialize the iterator to point to the first metadata block in the + * given FLAC file. + * + * \param iterator A pointer to an existing iterator. + * \param filename The path to the FLAC file. + * \param read_only If \c true, the FLAC file will be opened + * in read-only mode; if \c false, the FLAC + * file will be opened for edit even if no + * edits are performed. + * \param preserve_file_stats If \c true, the owner and modification + * time will be preserved even if the FLAC + * file is written to. + * \assert + * \code iterator != NULL \endcode + * \code filename != NULL \endcode + * \retval FLAC__bool + * \c false if a memory allocation error occurs, the file can't be + * opened, or another error occurs, else \c true. + */ +FLAC_API FLAC__bool FLAC__metadata_simple_iterator_init(FLAC__Metadata_SimpleIterator *iterator, const char *filename, FLAC__bool read_only, FLAC__bool preserve_file_stats); + +/** Returns \c true if the FLAC file is writable. If \c false, calls to + * FLAC__metadata_simple_iterator_set_block() and + * FLAC__metadata_simple_iterator_insert_block_after() will fail. + * + * \param iterator A pointer to an existing iterator. + * \assert + * \code iterator != NULL \endcode + * \retval FLAC__bool + * See above. + */ +FLAC_API FLAC__bool FLAC__metadata_simple_iterator_is_writable(const FLAC__Metadata_SimpleIterator *iterator); + +/** Moves the iterator forward one metadata block, returning \c false if + * already at the end. + * + * \param iterator A pointer to an existing initialized iterator. + * \assert + * \code iterator != NULL \endcode + * \a iterator has been successfully initialized with + * FLAC__metadata_simple_iterator_init() + * \retval FLAC__bool + * \c false if already at the last metadata block of the chain, else + * \c true. + */ +FLAC_API FLAC__bool FLAC__metadata_simple_iterator_next(FLAC__Metadata_SimpleIterator *iterator); + +/** Moves the iterator backward one metadata block, returning \c false if + * already at the beginning. + * + * \param iterator A pointer to an existing initialized iterator. + * \assert + * \code iterator != NULL \endcode + * \a iterator has been successfully initialized with + * FLAC__metadata_simple_iterator_init() + * \retval FLAC__bool + * \c false if already at the first metadata block of the chain, else + * \c true. + */ +FLAC_API FLAC__bool FLAC__metadata_simple_iterator_prev(FLAC__Metadata_SimpleIterator *iterator); + +/** Returns a flag telling if the current metadata block is the last. + * + * \param iterator A pointer to an existing initialized iterator. + * \assert + * \code iterator != NULL \endcode + * \a iterator has been successfully initialized with + * FLAC__metadata_simple_iterator_init() + * \retval FLAC__bool + * \c true if the current metadata block is the last in the file, + * else \c false. + */ +FLAC_API FLAC__bool FLAC__metadata_simple_iterator_is_last(const FLAC__Metadata_SimpleIterator *iterator); + +/** Get the offset of the metadata block at the current position. This + * avoids reading the actual block data which can save time for large + * blocks. + * + * \param iterator A pointer to an existing initialized iterator. + * \assert + * \code iterator != NULL \endcode + * \a iterator has been successfully initialized with + * FLAC__metadata_simple_iterator_init() + * \retval off_t + * The offset of the metadata block at the current iterator position. + * This is the byte offset relative to the beginning of the file of + * the current metadata block's header. + */ +FLAC_API off_t FLAC__metadata_simple_iterator_get_block_offset(const FLAC__Metadata_SimpleIterator *iterator); + +/** Get the type of the metadata block at the current position. This + * avoids reading the actual block data which can save time for large + * blocks. + * + * \param iterator A pointer to an existing initialized iterator. + * \assert + * \code iterator != NULL \endcode + * \a iterator has been successfully initialized with + * FLAC__metadata_simple_iterator_init() + * \retval FLAC__MetadataType + * The type of the metadata block at the current iterator position. + */ +FLAC_API FLAC__MetadataType FLAC__metadata_simple_iterator_get_block_type(const FLAC__Metadata_SimpleIterator *iterator); + +/** Get the length of the metadata block at the current position. This + * avoids reading the actual block data which can save time for large + * blocks. + * + * \param iterator A pointer to an existing initialized iterator. + * \assert + * \code iterator != NULL \endcode + * \a iterator has been successfully initialized with + * FLAC__metadata_simple_iterator_init() + * \retval unsigned + * The length of the metadata block at the current iterator position. + * The is same length as that in the + * metadata block header, + * i.e. the length of the metadata body that follows the header. + */ +FLAC_API unsigned FLAC__metadata_simple_iterator_get_block_length(const FLAC__Metadata_SimpleIterator *iterator); + +/** Get the application ID of the \c APPLICATION block at the current + * position. This avoids reading the actual block data which can save + * time for large blocks. + * + * \param iterator A pointer to an existing initialized iterator. + * \param id A pointer to a buffer of at least \c 4 bytes where + * the ID will be stored. + * \assert + * \code iterator != NULL \endcode + * \code id != NULL \endcode + * \a iterator has been successfully initialized with + * FLAC__metadata_simple_iterator_init() + * \retval FLAC__bool + * \c true if the ID was successfully read, else \c false, in which + * case you should check FLAC__metadata_simple_iterator_status() to + * find out why. If the status is + * \c FLAC__METADATA_SIMPLE_ITERATOR_STATUS_ILLEGAL_INPUT, then the + * current metadata block is not an \c APPLICATION block. Otherwise + * if the status is + * \c FLAC__METADATA_SIMPLE_ITERATOR_STATUS_READ_ERROR or + * \c FLAC__METADATA_SIMPLE_ITERATOR_STATUS_SEEK_ERROR, an I/O error + * occurred and the iterator can no longer be used. + */ +FLAC_API FLAC__bool FLAC__metadata_simple_iterator_get_application_id(FLAC__Metadata_SimpleIterator *iterator, FLAC__byte *id); + +/** Get the metadata block at the current position. You can modify the + * block but must use FLAC__metadata_simple_iterator_set_block() to + * write it back to the FLAC file. + * + * You must call FLAC__metadata_object_delete() on the returned object + * when you are finished with it. + * + * \param iterator A pointer to an existing initialized iterator. + * \assert + * \code iterator != NULL \endcode + * \a iterator has been successfully initialized with + * FLAC__metadata_simple_iterator_init() + * \retval FLAC__StreamMetadata* + * The current metadata block, or \c NULL if there was a memory + * allocation error. + */ +FLAC_API FLAC__StreamMetadata *FLAC__metadata_simple_iterator_get_block(FLAC__Metadata_SimpleIterator *iterator); + +/** Write a block back to the FLAC file. This function tries to be + * as efficient as possible; how the block is actually written is + * shown by the following: + * + * Existing block is a STREAMINFO block and the new block is a + * STREAMINFO block: the new block is written in place. Make sure + * you know what you're doing when changing the values of a + * STREAMINFO block. + * + * Existing block is a STREAMINFO block and the new block is a + * not a STREAMINFO block: this is an error since the first block + * must be a STREAMINFO block. Returns \c false without altering the + * file. + * + * Existing block is not a STREAMINFO block and the new block is a + * STREAMINFO block: this is an error since there may be only one + * STREAMINFO block. Returns \c false without altering the file. + * + * Existing block and new block are the same length: the existing + * block will be replaced by the new block, written in place. + * + * Existing block is longer than new block: if use_padding is \c true, + * the existing block will be overwritten in place with the new + * block followed by a PADDING block, if possible, to make the total + * size the same as the existing block. Remember that a padding + * block requires at least four bytes so if the difference in size + * between the new block and existing block is less than that, the + * entire file will have to be rewritten, using the new block's + * exact size. If use_padding is \c false, the entire file will be + * rewritten, replacing the existing block by the new block. + * + * Existing block is shorter than new block: if use_padding is \c true, + * the function will try and expand the new block into the following + * PADDING block, if it exists and doing so won't shrink the PADDING + * block to less than 4 bytes. If there is no following PADDING + * block, or it will shrink to less than 4 bytes, or use_padding is + * \c false, the entire file is rewritten, replacing the existing block + * with the new block. Note that in this case any following PADDING + * block is preserved as is. + * + * After writing the block, the iterator will remain in the same + * place, i.e. pointing to the new block. + * + * \param iterator A pointer to an existing initialized iterator. + * \param block The block to set. + * \param use_padding See above. + * \assert + * \code iterator != NULL \endcode + * \a iterator has been successfully initialized with + * FLAC__metadata_simple_iterator_init() + * \code block != NULL \endcode + * \retval FLAC__bool + * \c true if successful, else \c false. + */ +FLAC_API FLAC__bool FLAC__metadata_simple_iterator_set_block(FLAC__Metadata_SimpleIterator *iterator, FLAC__StreamMetadata *block, FLAC__bool use_padding); + +/** This is similar to FLAC__metadata_simple_iterator_set_block() + * except that instead of writing over an existing block, it appends + * a block after the existing block. \a use_padding is again used to + * tell the function to try an expand into following padding in an + * attempt to avoid rewriting the entire file. + * + * This function will fail and return \c false if given a STREAMINFO + * block. + * + * After writing the block, the iterator will be pointing to the + * new block. + * + * \param iterator A pointer to an existing initialized iterator. + * \param block The block to set. + * \param use_padding See above. + * \assert + * \code iterator != NULL \endcode + * \a iterator has been successfully initialized with + * FLAC__metadata_simple_iterator_init() + * \code block != NULL \endcode + * \retval FLAC__bool + * \c true if successful, else \c false. + */ +FLAC_API FLAC__bool FLAC__metadata_simple_iterator_insert_block_after(FLAC__Metadata_SimpleIterator *iterator, FLAC__StreamMetadata *block, FLAC__bool use_padding); + +/** Deletes the block at the current position. This will cause the + * entire FLAC file to be rewritten, unless \a use_padding is \c true, + * in which case the block will be replaced by an equal-sized PADDING + * block. The iterator will be left pointing to the block before the + * one just deleted. + * + * You may not delete the STREAMINFO block. + * + * \param iterator A pointer to an existing initialized iterator. + * \param use_padding See above. + * \assert + * \code iterator != NULL \endcode + * \a iterator has been successfully initialized with + * FLAC__metadata_simple_iterator_init() + * \retval FLAC__bool + * \c true if successful, else \c false. + */ +FLAC_API FLAC__bool FLAC__metadata_simple_iterator_delete_block(FLAC__Metadata_SimpleIterator *iterator, FLAC__bool use_padding); + +/* \} */ + + +/** \defgroup flac_metadata_level2 FLAC/metadata.h: metadata level 2 interface + * \ingroup flac_metadata + * + * \brief + * The level 2 interface provides read-write access to FLAC file metadata; + * all metadata is read into memory, operated on in memory, and then written + * to file, which is more efficient than level 1 when editing multiple blocks. + * + * Currently Ogg FLAC is supported for read only, via + * FLAC__metadata_chain_read_ogg() but a subsequent + * FLAC__metadata_chain_write() will fail. + * + * The general usage of this interface is: + * + * - Create a new chain using FLAC__metadata_chain_new(). A chain is a + * linked list of FLAC metadata blocks. + * - Read all metadata into the chain from a FLAC file using + * FLAC__metadata_chain_read() or FLAC__metadata_chain_read_ogg() and + * check the status. + * - Optionally, consolidate the padding using + * FLAC__metadata_chain_merge_padding() or + * FLAC__metadata_chain_sort_padding(). + * - Create a new iterator using FLAC__metadata_iterator_new() + * - Initialize the iterator to point to the first element in the chain + * using FLAC__metadata_iterator_init() + * - Traverse the chain using FLAC__metadata_iterator_next and + * FLAC__metadata_iterator_prev(). + * - Get a block for reading or modification using + * FLAC__metadata_iterator_get_block(). The pointer to the object + * inside the chain is returned, so the block is yours to modify. + * Changes will be reflected in the FLAC file when you write the + * chain. You can also add and delete blocks (see functions below). + * - When done, write out the chain using FLAC__metadata_chain_write(). + * Make sure to read the whole comment to the function below. + * - Delete the chain using FLAC__metadata_chain_delete(). + * + * \note + * Even though the FLAC file is not open while the chain is being + * manipulated, you must not alter the file externally during + * this time. The chain assumes the FLAC file will not change + * between the time of FLAC__metadata_chain_read()/FLAC__metadata_chain_read_ogg() + * and FLAC__metadata_chain_write(). + * + * \note + * Do not modify the is_last, length, or type fields of returned + * FLAC__StreamMetadata objects. These are managed automatically. + * + * \note + * The metadata objects returned by FLAC__metadata_iterator_get_block() + * are owned by the chain; do not FLAC__metadata_object_delete() them. + * In the same way, blocks passed to FLAC__metadata_iterator_set_block() + * become owned by the chain and they will be deleted when the chain is + * deleted. + * + * \{ + */ + +struct FLAC__Metadata_Chain; +/** The opaque structure definition for the level 2 chain type. + */ +typedef struct FLAC__Metadata_Chain FLAC__Metadata_Chain; + +struct FLAC__Metadata_Iterator; +/** The opaque structure definition for the level 2 iterator type. + */ +typedef struct FLAC__Metadata_Iterator FLAC__Metadata_Iterator; + +typedef enum { + FLAC__METADATA_CHAIN_STATUS_OK = 0, + /**< The chain is in the normal OK state */ + + FLAC__METADATA_CHAIN_STATUS_ILLEGAL_INPUT, + /**< The data passed into a function violated the function's usage criteria */ + + FLAC__METADATA_CHAIN_STATUS_ERROR_OPENING_FILE, + /**< The chain could not open the target file */ + + FLAC__METADATA_CHAIN_STATUS_NOT_A_FLAC_FILE, + /**< The chain could not find the FLAC signature at the start of the file */ + + FLAC__METADATA_CHAIN_STATUS_NOT_WRITABLE, + /**< The chain tried to write to a file that was not writable */ + + FLAC__METADATA_CHAIN_STATUS_BAD_METADATA, + /**< The chain encountered input that does not conform to the FLAC metadata specification */ + + FLAC__METADATA_CHAIN_STATUS_READ_ERROR, + /**< The chain encountered an error while reading the FLAC file */ + + FLAC__METADATA_CHAIN_STATUS_SEEK_ERROR, + /**< The chain encountered an error while seeking in the FLAC file */ + + FLAC__METADATA_CHAIN_STATUS_WRITE_ERROR, + /**< The chain encountered an error while writing the FLAC file */ + + FLAC__METADATA_CHAIN_STATUS_RENAME_ERROR, + /**< The chain encountered an error renaming the FLAC file */ + + FLAC__METADATA_CHAIN_STATUS_UNLINK_ERROR, + /**< The chain encountered an error removing the temporary file */ + + FLAC__METADATA_CHAIN_STATUS_MEMORY_ALLOCATION_ERROR, + /**< Memory allocation failed */ + + FLAC__METADATA_CHAIN_STATUS_INTERNAL_ERROR, + /**< The caller violated an assertion or an unexpected error occurred */ + + FLAC__METADATA_CHAIN_STATUS_INVALID_CALLBACKS, + /**< One or more of the required callbacks was NULL */ + + FLAC__METADATA_CHAIN_STATUS_READ_WRITE_MISMATCH, + /**< FLAC__metadata_chain_write() was called on a chain read by + * FLAC__metadata_chain_read_with_callbacks()/FLAC__metadata_chain_read_ogg_with_callbacks(), + * or + * FLAC__metadata_chain_write_with_callbacks()/FLAC__metadata_chain_write_with_callbacks_and_tempfile() + * was called on a chain read by + * FLAC__metadata_chain_read()/FLAC__metadata_chain_read_ogg(). + * Matching read/write methods must always be used. */ + + FLAC__METADATA_CHAIN_STATUS_WRONG_WRITE_CALL + /**< FLAC__metadata_chain_write_with_callbacks() was called when the + * chain write requires a tempfile; use + * FLAC__metadata_chain_write_with_callbacks_and_tempfile() instead. + * Or, FLAC__metadata_chain_write_with_callbacks_and_tempfile() was + * called when the chain write does not require a tempfile; use + * FLAC__metadata_chain_write_with_callbacks() instead. + * Always check FLAC__metadata_chain_check_if_tempfile_needed() + * before writing via callbacks. */ + +} FLAC__Metadata_ChainStatus; + +/** Maps a FLAC__Metadata_ChainStatus to a C string. + * + * Using a FLAC__Metadata_ChainStatus as the index to this array + * will give the string equivalent. The contents should not be modified. + */ +extern FLAC_API const char * const FLAC__Metadata_ChainStatusString[]; + +/*********** FLAC__Metadata_Chain ***********/ + +/** Create a new chain instance. + * + * \retval FLAC__Metadata_Chain* + * \c NULL if there was an error allocating memory, else the new instance. + */ +FLAC_API FLAC__Metadata_Chain *FLAC__metadata_chain_new(void); + +/** Free a chain instance. Deletes the object pointed to by \a chain. + * + * \param chain A pointer to an existing chain. + * \assert + * \code chain != NULL \endcode + */ +FLAC_API void FLAC__metadata_chain_delete(FLAC__Metadata_Chain *chain); + +/** Get the current status of the chain. Call this after a function + * returns \c false to get the reason for the error. Also resets the + * status to FLAC__METADATA_CHAIN_STATUS_OK. + * + * \param chain A pointer to an existing chain. + * \assert + * \code chain != NULL \endcode + * \retval FLAC__Metadata_ChainStatus + * The current status of the chain. + */ +FLAC_API FLAC__Metadata_ChainStatus FLAC__metadata_chain_status(FLAC__Metadata_Chain *chain); + +/** Read all metadata from a FLAC file into the chain. + * + * \param chain A pointer to an existing chain. + * \param filename The path to the FLAC file to read. + * \assert + * \code chain != NULL \endcode + * \code filename != NULL \endcode + * \retval FLAC__bool + * \c true if a valid list of metadata blocks was read from + * \a filename, else \c false. On failure, check the status with + * FLAC__metadata_chain_status(). + */ +FLAC_API FLAC__bool FLAC__metadata_chain_read(FLAC__Metadata_Chain *chain, const char *filename); + +/** Read all metadata from an Ogg FLAC file into the chain. + * + * \note Ogg FLAC metadata data writing is not supported yet and + * FLAC__metadata_chain_write() will fail. + * + * \param chain A pointer to an existing chain. + * \param filename The path to the Ogg FLAC file to read. + * \assert + * \code chain != NULL \endcode + * \code filename != NULL \endcode + * \retval FLAC__bool + * \c true if a valid list of metadata blocks was read from + * \a filename, else \c false. On failure, check the status with + * FLAC__metadata_chain_status(). + */ +FLAC_API FLAC__bool FLAC__metadata_chain_read_ogg(FLAC__Metadata_Chain *chain, const char *filename); + +/** Read all metadata from a FLAC stream into the chain via I/O callbacks. + * + * The \a handle need only be open for reading, but must be seekable. + * The equivalent minimum stdio fopen() file mode is \c "r" (or \c "rb" + * for Windows). + * + * \param chain A pointer to an existing chain. + * \param handle The I/O handle of the FLAC stream to read. The + * handle will NOT be closed after the metadata is read; + * that is the duty of the caller. + * \param callbacks + * A set of callbacks to use for I/O. The mandatory + * callbacks are \a read, \a seek, and \a tell. + * \assert + * \code chain != NULL \endcode + * \retval FLAC__bool + * \c true if a valid list of metadata blocks was read from + * \a handle, else \c false. On failure, check the status with + * FLAC__metadata_chain_status(). + */ +FLAC_API FLAC__bool FLAC__metadata_chain_read_with_callbacks(FLAC__Metadata_Chain *chain, FLAC__IOHandle handle, FLAC__IOCallbacks callbacks); + +/** Read all metadata from an Ogg FLAC stream into the chain via I/O callbacks. + * + * The \a handle need only be open for reading, but must be seekable. + * The equivalent minimum stdio fopen() file mode is \c "r" (or \c "rb" + * for Windows). + * + * \note Ogg FLAC metadata data writing is not supported yet and + * FLAC__metadata_chain_write() will fail. + * + * \param chain A pointer to an existing chain. + * \param handle The I/O handle of the Ogg FLAC stream to read. The + * handle will NOT be closed after the metadata is read; + * that is the duty of the caller. + * \param callbacks + * A set of callbacks to use for I/O. The mandatory + * callbacks are \a read, \a seek, and \a tell. + * \assert + * \code chain != NULL \endcode + * \retval FLAC__bool + * \c true if a valid list of metadata blocks was read from + * \a handle, else \c false. On failure, check the status with + * FLAC__metadata_chain_status(). + */ +FLAC_API FLAC__bool FLAC__metadata_chain_read_ogg_with_callbacks(FLAC__Metadata_Chain *chain, FLAC__IOHandle handle, FLAC__IOCallbacks callbacks); + +/** Checks if writing the given chain would require the use of a + * temporary file, or if it could be written in place. + * + * Under certain conditions, padding can be utilized so that writing + * edited metadata back to the FLAC file does not require rewriting the + * entire file. If rewriting is required, then a temporary workfile is + * required. When writing metadata using callbacks, you must check + * this function to know whether to call + * FLAC__metadata_chain_write_with_callbacks() or + * FLAC__metadata_chain_write_with_callbacks_and_tempfile(). When + * writing with FLAC__metadata_chain_write(), the temporary file is + * handled internally. + * + * \param chain A pointer to an existing chain. + * \param use_padding + * Whether or not padding will be allowed to be used + * during the write. The value of \a use_padding given + * here must match the value later passed to + * FLAC__metadata_chain_write_with_callbacks() or + * FLAC__metadata_chain_write_with_callbacks_with_tempfile(). + * \assert + * \code chain != NULL \endcode + * \retval FLAC__bool + * \c true if writing the current chain would require a tempfile, or + * \c false if metadata can be written in place. + */ +FLAC_API FLAC__bool FLAC__metadata_chain_check_if_tempfile_needed(FLAC__Metadata_Chain *chain, FLAC__bool use_padding); + +/** Write all metadata out to the FLAC file. This function tries to be as + * efficient as possible; how the metadata is actually written is shown by + * the following: + * + * If the current chain is the same size as the existing metadata, the new + * data is written in place. + * + * If the current chain is longer than the existing metadata, and + * \a use_padding is \c true, and the last block is a PADDING block of + * sufficient length, the function will truncate the final padding block + * so that the overall size of the metadata is the same as the existing + * metadata, and then just rewrite the metadata. Otherwise, if not all of + * the above conditions are met, the entire FLAC file must be rewritten. + * If you want to use padding this way it is a good idea to call + * FLAC__metadata_chain_sort_padding() first so that you have the maximum + * amount of padding to work with, unless you need to preserve ordering + * of the PADDING blocks for some reason. + * + * If the current chain is shorter than the existing metadata, and + * \a use_padding is \c true, and the final block is a PADDING block, the padding + * is extended to make the overall size the same as the existing data. If + * \a use_padding is \c true and the last block is not a PADDING block, a new + * PADDING block is added to the end of the new data to make it the same + * size as the existing data (if possible, see the note to + * FLAC__metadata_simple_iterator_set_block() about the four byte limit) + * and the new data is written in place. If none of the above apply or + * \a use_padding is \c false, the entire FLAC file is rewritten. + * + * If \a preserve_file_stats is \c true, the owner and modification time will + * be preserved even if the FLAC file is written. + * + * For this write function to be used, the chain must have been read with + * FLAC__metadata_chain_read()/FLAC__metadata_chain_read_ogg(), not + * FLAC__metadata_chain_read_with_callbacks()/FLAC__metadata_chain_read_ogg_with_callbacks(). + * + * \param chain A pointer to an existing chain. + * \param use_padding See above. + * \param preserve_file_stats See above. + * \assert + * \code chain != NULL \endcode + * \retval FLAC__bool + * \c true if the write succeeded, else \c false. On failure, + * check the status with FLAC__metadata_chain_status(). + */ +FLAC_API FLAC__bool FLAC__metadata_chain_write(FLAC__Metadata_Chain *chain, FLAC__bool use_padding, FLAC__bool preserve_file_stats); + +/** Write all metadata out to a FLAC stream via callbacks. + * + * (See FLAC__metadata_chain_write() for the details on how padding is + * used to write metadata in place if possible.) + * + * The \a handle must be open for updating and be seekable. The + * equivalent minimum stdio fopen() file mode is \c "r+" (or \c "r+b" + * for Windows). + * + * For this write function to be used, the chain must have been read with + * FLAC__metadata_chain_read_with_callbacks()/FLAC__metadata_chain_read_ogg_with_callbacks(), + * not FLAC__metadata_chain_read()/FLAC__metadata_chain_read_ogg(). + * Also, FLAC__metadata_chain_check_if_tempfile_needed() must have returned + * \c false. + * + * \param chain A pointer to an existing chain. + * \param use_padding See FLAC__metadata_chain_write() + * \param handle The I/O handle of the FLAC stream to write. The + * handle will NOT be closed after the metadata is + * written; that is the duty of the caller. + * \param callbacks A set of callbacks to use for I/O. The mandatory + * callbacks are \a write and \a seek. + * \assert + * \code chain != NULL \endcode + * \retval FLAC__bool + * \c true if the write succeeded, else \c false. On failure, + * check the status with FLAC__metadata_chain_status(). + */ +FLAC_API FLAC__bool FLAC__metadata_chain_write_with_callbacks(FLAC__Metadata_Chain *chain, FLAC__bool use_padding, FLAC__IOHandle handle, FLAC__IOCallbacks callbacks); + +/** Write all metadata out to a FLAC stream via callbacks. + * + * (See FLAC__metadata_chain_write() for the details on how padding is + * used to write metadata in place if possible.) + * + * This version of the write-with-callbacks function must be used when + * FLAC__metadata_chain_check_if_tempfile_needed() returns true. In + * this function, you must supply an I/O handle corresponding to the + * FLAC file to edit, and a temporary handle to which the new FLAC + * file will be written. It is the caller's job to move this temporary + * FLAC file on top of the original FLAC file to complete the metadata + * edit. + * + * The \a handle must be open for reading and be seekable. The + * equivalent minimum stdio fopen() file mode is \c "r" (or \c "rb" + * for Windows). + * + * The \a temp_handle must be open for writing. The + * equivalent minimum stdio fopen() file mode is \c "w" (or \c "wb" + * for Windows). It should be an empty stream, or at least positioned + * at the start-of-file (in which case it is the caller's duty to + * truncate it on return). + * + * For this write function to be used, the chain must have been read with + * FLAC__metadata_chain_read_with_callbacks()/FLAC__metadata_chain_read_ogg_with_callbacks(), + * not FLAC__metadata_chain_read()/FLAC__metadata_chain_read_ogg(). + * Also, FLAC__metadata_chain_check_if_tempfile_needed() must have returned + * \c true. + * + * \param chain A pointer to an existing chain. + * \param use_padding See FLAC__metadata_chain_write() + * \param handle The I/O handle of the original FLAC stream to read. + * The handle will NOT be closed after the metadata is + * written; that is the duty of the caller. + * \param callbacks A set of callbacks to use for I/O on \a handle. + * The mandatory callbacks are \a read, \a seek, and + * \a eof. + * \param temp_handle The I/O handle of the FLAC stream to write. The + * handle will NOT be closed after the metadata is + * written; that is the duty of the caller. + * \param temp_callbacks + * A set of callbacks to use for I/O on temp_handle. + * The only mandatory callback is \a write. + * \assert + * \code chain != NULL \endcode + * \retval FLAC__bool + * \c true if the write succeeded, else \c false. On failure, + * check the status with FLAC__metadata_chain_status(). + */ +FLAC_API FLAC__bool FLAC__metadata_chain_write_with_callbacks_and_tempfile(FLAC__Metadata_Chain *chain, FLAC__bool use_padding, FLAC__IOHandle handle, FLAC__IOCallbacks callbacks, FLAC__IOHandle temp_handle, FLAC__IOCallbacks temp_callbacks); + +/** Merge adjacent PADDING blocks into a single block. + * + * \note This function does not write to the FLAC file, it only + * modifies the chain. + * + * \warning Any iterator on the current chain will become invalid after this + * call. You should delete the iterator and get a new one. + * + * \param chain A pointer to an existing chain. + * \assert + * \code chain != NULL \endcode + */ +FLAC_API void FLAC__metadata_chain_merge_padding(FLAC__Metadata_Chain *chain); + +/** This function will move all PADDING blocks to the end on the metadata, + * then merge them into a single block. + * + * \note This function does not write to the FLAC file, it only + * modifies the chain. + * + * \warning Any iterator on the current chain will become invalid after this + * call. You should delete the iterator and get a new one. + * + * \param chain A pointer to an existing chain. + * \assert + * \code chain != NULL \endcode + */ +FLAC_API void FLAC__metadata_chain_sort_padding(FLAC__Metadata_Chain *chain); + + +/*********** FLAC__Metadata_Iterator ***********/ + +/** Create a new iterator instance. + * + * \retval FLAC__Metadata_Iterator* + * \c NULL if there was an error allocating memory, else the new instance. + */ +FLAC_API FLAC__Metadata_Iterator *FLAC__metadata_iterator_new(void); + +/** Free an iterator instance. Deletes the object pointed to by \a iterator. + * + * \param iterator A pointer to an existing iterator. + * \assert + * \code iterator != NULL \endcode + */ +FLAC_API void FLAC__metadata_iterator_delete(FLAC__Metadata_Iterator *iterator); + +/** Initialize the iterator to point to the first metadata block in the + * given chain. + * + * \param iterator A pointer to an existing iterator. + * \param chain A pointer to an existing and initialized (read) chain. + * \assert + * \code iterator != NULL \endcode + * \code chain != NULL \endcode + */ +FLAC_API void FLAC__metadata_iterator_init(FLAC__Metadata_Iterator *iterator, FLAC__Metadata_Chain *chain); + +/** Moves the iterator forward one metadata block, returning \c false if + * already at the end. + * + * \param iterator A pointer to an existing initialized iterator. + * \assert + * \code iterator != NULL \endcode + * \a iterator has been successfully initialized with + * FLAC__metadata_iterator_init() + * \retval FLAC__bool + * \c false if already at the last metadata block of the chain, else + * \c true. + */ +FLAC_API FLAC__bool FLAC__metadata_iterator_next(FLAC__Metadata_Iterator *iterator); + +/** Moves the iterator backward one metadata block, returning \c false if + * already at the beginning. + * + * \param iterator A pointer to an existing initialized iterator. + * \assert + * \code iterator != NULL \endcode + * \a iterator has been successfully initialized with + * FLAC__metadata_iterator_init() + * \retval FLAC__bool + * \c false if already at the first metadata block of the chain, else + * \c true. + */ +FLAC_API FLAC__bool FLAC__metadata_iterator_prev(FLAC__Metadata_Iterator *iterator); + +/** Get the type of the metadata block at the current position. + * + * \param iterator A pointer to an existing initialized iterator. + * \assert + * \code iterator != NULL \endcode + * \a iterator has been successfully initialized with + * FLAC__metadata_iterator_init() + * \retval FLAC__MetadataType + * The type of the metadata block at the current iterator position. + */ +FLAC_API FLAC__MetadataType FLAC__metadata_iterator_get_block_type(const FLAC__Metadata_Iterator *iterator); + +/** Get the metadata block at the current position. You can modify + * the block in place but must write the chain before the changes + * are reflected to the FLAC file. You do not need to call + * FLAC__metadata_iterator_set_block() to reflect the changes; + * the pointer returned by FLAC__metadata_iterator_get_block() + * points directly into the chain. + * + * \warning + * Do not call FLAC__metadata_object_delete() on the returned object; + * to delete a block use FLAC__metadata_iterator_delete_block(). + * + * \param iterator A pointer to an existing initialized iterator. + * \assert + * \code iterator != NULL \endcode + * \a iterator has been successfully initialized with + * FLAC__metadata_iterator_init() + * \retval FLAC__StreamMetadata* + * The current metadata block. + */ +FLAC_API FLAC__StreamMetadata *FLAC__metadata_iterator_get_block(FLAC__Metadata_Iterator *iterator); + +/** Set the metadata block at the current position, replacing the existing + * block. The new block passed in becomes owned by the chain and it will be + * deleted when the chain is deleted. + * + * \param iterator A pointer to an existing initialized iterator. + * \param block A pointer to a metadata block. + * \assert + * \code iterator != NULL \endcode + * \a iterator has been successfully initialized with + * FLAC__metadata_iterator_init() + * \code block != NULL \endcode + * \retval FLAC__bool + * \c false if the conditions in the above description are not met, or + * a memory allocation error occurs, otherwise \c true. + */ +FLAC_API FLAC__bool FLAC__metadata_iterator_set_block(FLAC__Metadata_Iterator *iterator, FLAC__StreamMetadata *block); + +/** Removes the current block from the chain. If \a replace_with_padding is + * \c true, the block will instead be replaced with a padding block of equal + * size. You can not delete the STREAMINFO block. The iterator will be + * left pointing to the block before the one just "deleted", even if + * \a replace_with_padding is \c true. + * + * \param iterator A pointer to an existing initialized iterator. + * \param replace_with_padding See above. + * \assert + * \code iterator != NULL \endcode + * \a iterator has been successfully initialized with + * FLAC__metadata_iterator_init() + * \retval FLAC__bool + * \c false if the conditions in the above description are not met, + * otherwise \c true. + */ +FLAC_API FLAC__bool FLAC__metadata_iterator_delete_block(FLAC__Metadata_Iterator *iterator, FLAC__bool replace_with_padding); + +/** Insert a new block before the current block. You cannot insert a block + * before the first STREAMINFO block. You cannot insert a STREAMINFO block + * as there can be only one, the one that already exists at the head when you + * read in a chain. The chain takes ownership of the new block and it will be + * deleted when the chain is deleted. The iterator will be left pointing to + * the new block. + * + * \param iterator A pointer to an existing initialized iterator. + * \param block A pointer to a metadata block to insert. + * \assert + * \code iterator != NULL \endcode + * \a iterator has been successfully initialized with + * FLAC__metadata_iterator_init() + * \retval FLAC__bool + * \c false if the conditions in the above description are not met, or + * a memory allocation error occurs, otherwise \c true. + */ +FLAC_API FLAC__bool FLAC__metadata_iterator_insert_block_before(FLAC__Metadata_Iterator *iterator, FLAC__StreamMetadata *block); + +/** Insert a new block after the current block. You cannot insert a STREAMINFO + * block as there can be only one, the one that already exists at the head when + * you read in a chain. The chain takes ownership of the new block and it will + * be deleted when the chain is deleted. The iterator will be left pointing to + * the new block. + * + * \param iterator A pointer to an existing initialized iterator. + * \param block A pointer to a metadata block to insert. + * \assert + * \code iterator != NULL \endcode + * \a iterator has been successfully initialized with + * FLAC__metadata_iterator_init() + * \retval FLAC__bool + * \c false if the conditions in the above description are not met, or + * a memory allocation error occurs, otherwise \c true. + */ +FLAC_API FLAC__bool FLAC__metadata_iterator_insert_block_after(FLAC__Metadata_Iterator *iterator, FLAC__StreamMetadata *block); + +/* \} */ + + +/** \defgroup flac_metadata_object FLAC/metadata.h: metadata object methods + * \ingroup flac_metadata + * + * \brief + * This module contains methods for manipulating FLAC metadata objects. + * + * Since many are variable length we have to be careful about the memory + * management. We decree that all pointers to data in the object are + * owned by the object and memory-managed by the object. + * + * Use the FLAC__metadata_object_new() and FLAC__metadata_object_delete() + * functions to create all instances. When using the + * FLAC__metadata_object_set_*() functions to set pointers to data, set + * \a copy to \c true to have the function make it's own copy of the data, or + * to \c false to give the object ownership of your data. In the latter case + * your pointer must be freeable by free() and will be free()d when the object + * is FLAC__metadata_object_delete()d. It is legal to pass a null pointer as + * the data pointer to a FLAC__metadata_object_set_*() function as long as + * the length argument is 0 and the \a copy argument is \c false. + * + * The FLAC__metadata_object_new() and FLAC__metadata_object_clone() function + * will return \c NULL in the case of a memory allocation error, otherwise a new + * object. The FLAC__metadata_object_set_*() functions return \c false in the + * case of a memory allocation error. + * + * We don't have the convenience of C++ here, so note that the library relies + * on you to keep the types straight. In other words, if you pass, for + * example, a FLAC__StreamMetadata* that represents a STREAMINFO block to + * FLAC__metadata_object_application_set_data(), you will get an assertion + * failure. + * + * For convenience the FLAC__metadata_object_vorbiscomment_*() functions + * maintain a trailing NUL on each Vorbis comment entry. This is not counted + * toward the length or stored in the stream, but it can make working with plain + * comments (those that don't contain embedded-NULs in the value) easier. + * Entries passed into these functions have trailing NULs added if missing, and + * returned entries are guaranteed to have a trailing NUL. + * + * The FLAC__metadata_object_vorbiscomment_*() functions that take a Vorbis + * comment entry/name/value will first validate that it complies with the Vorbis + * comment specification and return false if it does not. + * + * There is no need to recalculate the length field on metadata blocks you + * have modified. They will be calculated automatically before they are + * written back to a file. + * + * \{ + */ + + +/** Create a new metadata object instance of the given type. + * + * The object will be "empty"; i.e. values and data pointers will be \c 0, + * with the exception of FLAC__METADATA_TYPE_VORBIS_COMMENT, which will have + * the vendor string set (but zero comments). + * + * Do not pass in a value greater than or equal to + * \a FLAC__METADATA_TYPE_UNDEFINED unless you really know what you're + * doing. + * + * \param type Type of object to create + * \retval FLAC__StreamMetadata* + * \c NULL if there was an error allocating memory or the type code is + * greater than FLAC__MAX_METADATA_TYPE_CODE, else the new instance. + */ +FLAC_API FLAC__StreamMetadata *FLAC__metadata_object_new(FLAC__MetadataType type); + +/** Create a copy of an existing metadata object. + * + * The copy is a "deep" copy, i.e. dynamically allocated data within the + * object is also copied. The caller takes ownership of the new block and + * is responsible for freeing it with FLAC__metadata_object_delete(). + * + * \param object Pointer to object to copy. + * \assert + * \code object != NULL \endcode + * \retval FLAC__StreamMetadata* + * \c NULL if there was an error allocating memory, else the new instance. + */ +FLAC_API FLAC__StreamMetadata *FLAC__metadata_object_clone(const FLAC__StreamMetadata *object); + +/** Free a metadata object. Deletes the object pointed to by \a object. + * + * The delete is a "deep" delete, i.e. dynamically allocated data within the + * object is also deleted. + * + * \param object A pointer to an existing object. + * \assert + * \code object != NULL \endcode + */ +FLAC_API void FLAC__metadata_object_delete(FLAC__StreamMetadata *object); + +/** Compares two metadata objects. + * + * The compare is "deep", i.e. dynamically allocated data within the + * object is also compared. + * + * \param block1 A pointer to an existing object. + * \param block2 A pointer to an existing object. + * \assert + * \code block1 != NULL \endcode + * \code block2 != NULL \endcode + * \retval FLAC__bool + * \c true if objects are identical, else \c false. + */ +FLAC_API FLAC__bool FLAC__metadata_object_is_equal(const FLAC__StreamMetadata *block1, const FLAC__StreamMetadata *block2); + +/** Sets the application data of an APPLICATION block. + * + * If \a copy is \c true, a copy of the data is stored; otherwise, the object + * takes ownership of the pointer. The existing data will be freed if this + * function is successful, otherwise the original data will remain if \a copy + * is \c true and malloc() fails. + * + * \note It is safe to pass a const pointer to \a data if \a copy is \c true. + * + * \param object A pointer to an existing APPLICATION object. + * \param data A pointer to the data to set. + * \param length The length of \a data in bytes. + * \param copy See above. + * \assert + * \code object != NULL \endcode + * \code object->type == FLAC__METADATA_TYPE_APPLICATION \endcode + * \code (data != NULL && length > 0) || + * (data == NULL && length == 0 && copy == false) \endcode + * \retval FLAC__bool + * \c false if \a copy is \c true and malloc() fails, else \c true. + */ +FLAC_API FLAC__bool FLAC__metadata_object_application_set_data(FLAC__StreamMetadata *object, FLAC__byte *data, unsigned length, FLAC__bool copy); + +/** Resize the seekpoint array. + * + * If the size shrinks, elements will truncated; if it grows, new placeholder + * points will be added to the end. + * + * \param object A pointer to an existing SEEKTABLE object. + * \param new_num_points The desired length of the array; may be \c 0. + * \assert + * \code object != NULL \endcode + * \code object->type == FLAC__METADATA_TYPE_SEEKTABLE \endcode + * \code (object->data.seek_table.points == NULL && object->data.seek_table.num_points == 0) || + * (object->data.seek_table.points != NULL && object->data.seek_table.num_points > 0) \endcode + * \retval FLAC__bool + * \c false if memory allocation error, else \c true. + */ +FLAC_API FLAC__bool FLAC__metadata_object_seektable_resize_points(FLAC__StreamMetadata *object, unsigned new_num_points); + +/** Set a seekpoint in a seektable. + * + * \param object A pointer to an existing SEEKTABLE object. + * \param point_num Index into seekpoint array to set. + * \param point The point to set. + * \assert + * \code object != NULL \endcode + * \code object->type == FLAC__METADATA_TYPE_SEEKTABLE \endcode + * \code object->data.seek_table.num_points > point_num \endcode + */ +FLAC_API void FLAC__metadata_object_seektable_set_point(FLAC__StreamMetadata *object, unsigned point_num, FLAC__StreamMetadata_SeekPoint point); + +/** Insert a seekpoint into a seektable. + * + * \param object A pointer to an existing SEEKTABLE object. + * \param point_num Index into seekpoint array to set. + * \param point The point to set. + * \assert + * \code object != NULL \endcode + * \code object->type == FLAC__METADATA_TYPE_SEEKTABLE \endcode + * \code object->data.seek_table.num_points >= point_num \endcode + * \retval FLAC__bool + * \c false if memory allocation error, else \c true. + */ +FLAC_API FLAC__bool FLAC__metadata_object_seektable_insert_point(FLAC__StreamMetadata *object, unsigned point_num, FLAC__StreamMetadata_SeekPoint point); + +/** Delete a seekpoint from a seektable. + * + * \param object A pointer to an existing SEEKTABLE object. + * \param point_num Index into seekpoint array to set. + * \assert + * \code object != NULL \endcode + * \code object->type == FLAC__METADATA_TYPE_SEEKTABLE \endcode + * \code object->data.seek_table.num_points > point_num \endcode + * \retval FLAC__bool + * \c false if memory allocation error, else \c true. + */ +FLAC_API FLAC__bool FLAC__metadata_object_seektable_delete_point(FLAC__StreamMetadata *object, unsigned point_num); + +/** Check a seektable to see if it conforms to the FLAC specification. + * See the format specification for limits on the contents of the + * seektable. + * + * \param object A pointer to an existing SEEKTABLE object. + * \assert + * \code object != NULL \endcode + * \code object->type == FLAC__METADATA_TYPE_SEEKTABLE \endcode + * \retval FLAC__bool + * \c false if seek table is illegal, else \c true. + */ +FLAC_API FLAC__bool FLAC__metadata_object_seektable_is_legal(const FLAC__StreamMetadata *object); + +/** Append a number of placeholder points to the end of a seek table. + * + * \note + * As with the other ..._seektable_template_... functions, you should + * call FLAC__metadata_object_seektable_template_sort() when finished + * to make the seek table legal. + * + * \param object A pointer to an existing SEEKTABLE object. + * \param num The number of placeholder points to append. + * \assert + * \code object != NULL \endcode + * \code object->type == FLAC__METADATA_TYPE_SEEKTABLE \endcode + * \retval FLAC__bool + * \c false if memory allocation fails, else \c true. + */ +FLAC_API FLAC__bool FLAC__metadata_object_seektable_template_append_placeholders(FLAC__StreamMetadata *object, unsigned num); + +/** Append a specific seek point template to the end of a seek table. + * + * \note + * As with the other ..._seektable_template_... functions, you should + * call FLAC__metadata_object_seektable_template_sort() when finished + * to make the seek table legal. + * + * \param object A pointer to an existing SEEKTABLE object. + * \param sample_number The sample number of the seek point template. + * \assert + * \code object != NULL \endcode + * \code object->type == FLAC__METADATA_TYPE_SEEKTABLE \endcode + * \retval FLAC__bool + * \c false if memory allocation fails, else \c true. + */ +FLAC_API FLAC__bool FLAC__metadata_object_seektable_template_append_point(FLAC__StreamMetadata *object, FLAC__uint64 sample_number); + +/** Append specific seek point templates to the end of a seek table. + * + * \note + * As with the other ..._seektable_template_... functions, you should + * call FLAC__metadata_object_seektable_template_sort() when finished + * to make the seek table legal. + * + * \param object A pointer to an existing SEEKTABLE object. + * \param sample_numbers An array of sample numbers for the seek points. + * \param num The number of seek point templates to append. + * \assert + * \code object != NULL \endcode + * \code object->type == FLAC__METADATA_TYPE_SEEKTABLE \endcode + * \retval FLAC__bool + * \c false if memory allocation fails, else \c true. + */ +FLAC_API FLAC__bool FLAC__metadata_object_seektable_template_append_points(FLAC__StreamMetadata *object, FLAC__uint64 sample_numbers[], unsigned num); + +/** Append a set of evenly-spaced seek point templates to the end of a + * seek table. + * + * \note + * As with the other ..._seektable_template_... functions, you should + * call FLAC__metadata_object_seektable_template_sort() when finished + * to make the seek table legal. + * + * \param object A pointer to an existing SEEKTABLE object. + * \param num The number of placeholder points to append. + * \param total_samples The total number of samples to be encoded; + * the seekpoints will be spaced approximately + * \a total_samples / \a num samples apart. + * \assert + * \code object != NULL \endcode + * \code object->type == FLAC__METADATA_TYPE_SEEKTABLE \endcode + * \code total_samples > 0 \endcode + * \retval FLAC__bool + * \c false if memory allocation fails, else \c true. + */ +FLAC_API FLAC__bool FLAC__metadata_object_seektable_template_append_spaced_points(FLAC__StreamMetadata *object, unsigned num, FLAC__uint64 total_samples); + +/** Append a set of evenly-spaced seek point templates to the end of a + * seek table. + * + * \note + * As with the other ..._seektable_template_... functions, you should + * call FLAC__metadata_object_seektable_template_sort() when finished + * to make the seek table legal. + * + * \param object A pointer to an existing SEEKTABLE object. + * \param samples The number of samples apart to space the placeholder + * points. The first point will be at sample \c 0, the + * second at sample \a samples, then 2*\a samples, and + * so on. As long as \a samples and \a total_samples + * are greater than \c 0, there will always be at least + * one seekpoint at sample \c 0. + * \param total_samples The total number of samples to be encoded; + * the seekpoints will be spaced + * \a samples samples apart. + * \assert + * \code object != NULL \endcode + * \code object->type == FLAC__METADATA_TYPE_SEEKTABLE \endcode + * \code samples > 0 \endcode + * \code total_samples > 0 \endcode + * \retval FLAC__bool + * \c false if memory allocation fails, else \c true. + */ +FLAC_API FLAC__bool FLAC__metadata_object_seektable_template_append_spaced_points_by_samples(FLAC__StreamMetadata *object, unsigned samples, FLAC__uint64 total_samples); + +/** Sort a seek table's seek points according to the format specification, + * removing duplicates. + * + * \param object A pointer to a seek table to be sorted. + * \param compact If \c false, behaves like FLAC__format_seektable_sort(). + * If \c true, duplicates are deleted and the seek table is + * shrunk appropriately; the number of placeholder points + * present in the seek table will be the same after the call + * as before. + * \assert + * \code object != NULL \endcode + * \code object->type == FLAC__METADATA_TYPE_SEEKTABLE \endcode + * \retval FLAC__bool + * \c false if realloc() fails, else \c true. + */ +FLAC_API FLAC__bool FLAC__metadata_object_seektable_template_sort(FLAC__StreamMetadata *object, FLAC__bool compact); + +/** Sets the vendor string in a VORBIS_COMMENT block. + * + * For convenience, a trailing NUL is added to the entry if it doesn't have + * one already. + * + * If \a copy is \c true, a copy of the entry is stored; otherwise, the object + * takes ownership of the \c entry.entry pointer. + * + * \note If this function returns \c false, the caller still owns the + * pointer. + * + * \param object A pointer to an existing VORBIS_COMMENT object. + * \param entry The entry to set the vendor string to. + * \param copy See above. + * \assert + * \code object != NULL \endcode + * \code object->type == FLAC__METADATA_TYPE_VORBIS_COMMENT \endcode + * \code (entry.entry != NULL && entry.length > 0) || + * (entry.entry == NULL && entry.length == 0) \endcode + * \retval FLAC__bool + * \c false if memory allocation fails or \a entry does not comply with the + * Vorbis comment specification, else \c true. + */ +FLAC_API FLAC__bool FLAC__metadata_object_vorbiscomment_set_vendor_string(FLAC__StreamMetadata *object, FLAC__StreamMetadata_VorbisComment_Entry entry, FLAC__bool copy); + +/** Resize the comment array. + * + * If the size shrinks, elements will truncated; if it grows, new empty + * fields will be added to the end. + * + * \param object A pointer to an existing VORBIS_COMMENT object. + * \param new_num_comments The desired length of the array; may be \c 0. + * \assert + * \code object != NULL \endcode + * \code object->type == FLAC__METADATA_TYPE_VORBIS_COMMENT \endcode + * \code (object->data.vorbis_comment.comments == NULL && object->data.vorbis_comment.num_comments == 0) || + * (object->data.vorbis_comment.comments != NULL && object->data.vorbis_comment.num_comments > 0) \endcode + * \retval FLAC__bool + * \c false if memory allocation fails, else \c true. + */ +FLAC_API FLAC__bool FLAC__metadata_object_vorbiscomment_resize_comments(FLAC__StreamMetadata *object, unsigned new_num_comments); + +/** Sets a comment in a VORBIS_COMMENT block. + * + * For convenience, a trailing NUL is added to the entry if it doesn't have + * one already. + * + * If \a copy is \c true, a copy of the entry is stored; otherwise, the object + * takes ownership of the \c entry.entry pointer. + * + * \note If this function returns \c false, the caller still owns the + * pointer. + * + * \param object A pointer to an existing VORBIS_COMMENT object. + * \param comment_num Index into comment array to set. + * \param entry The entry to set the comment to. + * \param copy See above. + * \assert + * \code object != NULL \endcode + * \code object->type == FLAC__METADATA_TYPE_VORBIS_COMMENT \endcode + * \code comment_num < object->data.vorbis_comment.num_comments \endcode + * \code (entry.entry != NULL && entry.length > 0) || + * (entry.entry == NULL && entry.length == 0) \endcode + * \retval FLAC__bool + * \c false if memory allocation fails or \a entry does not comply with the + * Vorbis comment specification, else \c true. + */ +FLAC_API FLAC__bool FLAC__metadata_object_vorbiscomment_set_comment(FLAC__StreamMetadata *object, unsigned comment_num, FLAC__StreamMetadata_VorbisComment_Entry entry, FLAC__bool copy); + +/** Insert a comment in a VORBIS_COMMENT block at the given index. + * + * For convenience, a trailing NUL is added to the entry if it doesn't have + * one already. + * + * If \a copy is \c true, a copy of the entry is stored; otherwise, the object + * takes ownership of the \c entry.entry pointer. + * + * \note If this function returns \c false, the caller still owns the + * pointer. + * + * \param object A pointer to an existing VORBIS_COMMENT object. + * \param comment_num The index at which to insert the comment. The comments + * at and after \a comment_num move right one position. + * To append a comment to the end, set \a comment_num to + * \c object->data.vorbis_comment.num_comments . + * \param entry The comment to insert. + * \param copy See above. + * \assert + * \code object != NULL \endcode + * \code object->type == FLAC__METADATA_TYPE_VORBIS_COMMENT \endcode + * \code object->data.vorbis_comment.num_comments >= comment_num \endcode + * \code (entry.entry != NULL && entry.length > 0) || + * (entry.entry == NULL && entry.length == 0 && copy == false) \endcode + * \retval FLAC__bool + * \c false if memory allocation fails or \a entry does not comply with the + * Vorbis comment specification, else \c true. + */ +FLAC_API FLAC__bool FLAC__metadata_object_vorbiscomment_insert_comment(FLAC__StreamMetadata *object, unsigned comment_num, FLAC__StreamMetadata_VorbisComment_Entry entry, FLAC__bool copy); + +/** Appends a comment to a VORBIS_COMMENT block. + * + * For convenience, a trailing NUL is added to the entry if it doesn't have + * one already. + * + * If \a copy is \c true, a copy of the entry is stored; otherwise, the object + * takes ownership of the \c entry.entry pointer. + * + * \note If this function returns \c false, the caller still owns the + * pointer. + * + * \param object A pointer to an existing VORBIS_COMMENT object. + * \param entry The comment to insert. + * \param copy See above. + * \assert + * \code object != NULL \endcode + * \code object->type == FLAC__METADATA_TYPE_VORBIS_COMMENT \endcode + * \code (entry.entry != NULL && entry.length > 0) || + * (entry.entry == NULL && entry.length == 0 && copy == false) \endcode + * \retval FLAC__bool + * \c false if memory allocation fails or \a entry does not comply with the + * Vorbis comment specification, else \c true. + */ +FLAC_API FLAC__bool FLAC__metadata_object_vorbiscomment_append_comment(FLAC__StreamMetadata *object, FLAC__StreamMetadata_VorbisComment_Entry entry, FLAC__bool copy); + +/** Replaces comments in a VORBIS_COMMENT block with a new one. + * + * For convenience, a trailing NUL is added to the entry if it doesn't have + * one already. + * + * Depending on the value of \a all, either all or just the first comment + * whose field name(s) match the given entry's name will be replaced by the + * given entry. If no comments match, \a entry will simply be appended. + * + * If \a copy is \c true, a copy of the entry is stored; otherwise, the object + * takes ownership of the \c entry.entry pointer. + * + * \note If this function returns \c false, the caller still owns the + * pointer. + * + * \param object A pointer to an existing VORBIS_COMMENT object. + * \param entry The comment to insert. + * \param all If \c true, all comments whose field name matches + * \a entry's field name will be removed, and \a entry will + * be inserted at the position of the first matching + * comment. If \c false, only the first comment whose + * field name matches \a entry's field name will be + * replaced with \a entry. + * \param copy See above. + * \assert + * \code object != NULL \endcode + * \code object->type == FLAC__METADATA_TYPE_VORBIS_COMMENT \endcode + * \code (entry.entry != NULL && entry.length > 0) || + * (entry.entry == NULL && entry.length == 0 && copy == false) \endcode + * \retval FLAC__bool + * \c false if memory allocation fails or \a entry does not comply with the + * Vorbis comment specification, else \c true. + */ +FLAC_API FLAC__bool FLAC__metadata_object_vorbiscomment_replace_comment(FLAC__StreamMetadata *object, FLAC__StreamMetadata_VorbisComment_Entry entry, FLAC__bool all, FLAC__bool copy); + +/** Delete a comment in a VORBIS_COMMENT block at the given index. + * + * \param object A pointer to an existing VORBIS_COMMENT object. + * \param comment_num The index of the comment to delete. + * \assert + * \code object != NULL \endcode + * \code object->type == FLAC__METADATA_TYPE_VORBIS_COMMENT \endcode + * \code object->data.vorbis_comment.num_comments > comment_num \endcode + * \retval FLAC__bool + * \c false if realloc() fails, else \c true. + */ +FLAC_API FLAC__bool FLAC__metadata_object_vorbiscomment_delete_comment(FLAC__StreamMetadata *object, unsigned comment_num); + +/** Creates a Vorbis comment entry from NUL-terminated name and value strings. + * + * On return, the filled-in \a entry->entry pointer will point to malloc()ed + * memory and shall be owned by the caller. For convenience the entry will + * have a terminating NUL. + * + * \param entry A pointer to a Vorbis comment entry. The entry's + * \c entry pointer should not point to allocated + * memory as it will be overwritten. + * \param field_name The field name in ASCII, \c NUL terminated. + * \param field_value The field value in UTF-8, \c NUL terminated. + * \assert + * \code entry != NULL \endcode + * \code field_name != NULL \endcode + * \code field_value != NULL \endcode + * \retval FLAC__bool + * \c false if malloc() fails, or if \a field_name or \a field_value does + * not comply with the Vorbis comment specification, else \c true. + */ +FLAC_API FLAC__bool FLAC__metadata_object_vorbiscomment_entry_from_name_value_pair(FLAC__StreamMetadata_VorbisComment_Entry *entry, const char *field_name, const char *field_value); + +/** Splits a Vorbis comment entry into NUL-terminated name and value strings. + * + * The returned pointers to name and value will be allocated by malloc() + * and shall be owned by the caller. + * + * \param entry An existing Vorbis comment entry. + * \param field_name The address of where the returned pointer to the + * field name will be stored. + * \param field_value The address of where the returned pointer to the + * field value will be stored. + * \assert + * \code (entry.entry != NULL && entry.length > 0) \endcode + * \code memchr(entry.entry, '=', entry.length) != NULL \endcode + * \code field_name != NULL \endcode + * \code field_value != NULL \endcode + * \retval FLAC__bool + * \c false if memory allocation fails or \a entry does not comply with the + * Vorbis comment specification, else \c true. + */ +FLAC_API FLAC__bool FLAC__metadata_object_vorbiscomment_entry_to_name_value_pair(const FLAC__StreamMetadata_VorbisComment_Entry entry, char **field_name, char **field_value); + +/** Check if the given Vorbis comment entry's field name matches the given + * field name. + * + * \param entry An existing Vorbis comment entry. + * \param field_name The field name to check. + * \param field_name_length The length of \a field_name, not including the + * terminating \c NUL. + * \assert + * \code (entry.entry != NULL && entry.length > 0) \endcode + * \retval FLAC__bool + * \c true if the field names match, else \c false + */ +FLAC_API FLAC__bool FLAC__metadata_object_vorbiscomment_entry_matches(const FLAC__StreamMetadata_VorbisComment_Entry entry, const char *field_name, unsigned field_name_length); + +/** Find a Vorbis comment with the given field name. + * + * The search begins at entry number \a offset; use an offset of 0 to + * search from the beginning of the comment array. + * + * \param object A pointer to an existing VORBIS_COMMENT object. + * \param offset The offset into the comment array from where to start + * the search. + * \param field_name The field name of the comment to find. + * \assert + * \code object != NULL \endcode + * \code object->type == FLAC__METADATA_TYPE_VORBIS_COMMENT \endcode + * \code field_name != NULL \endcode + * \retval int + * The offset in the comment array of the first comment whose field + * name matches \a field_name, or \c -1 if no match was found. + */ +FLAC_API int FLAC__metadata_object_vorbiscomment_find_entry_from(const FLAC__StreamMetadata *object, unsigned offset, const char *field_name); + +/** Remove first Vorbis comment matching the given field name. + * + * \param object A pointer to an existing VORBIS_COMMENT object. + * \param field_name The field name of comment to delete. + * \assert + * \code object != NULL \endcode + * \code object->type == FLAC__METADATA_TYPE_VORBIS_COMMENT \endcode + * \retval int + * \c -1 for memory allocation error, \c 0 for no matching entries, + * \c 1 for one matching entry deleted. + */ +FLAC_API int FLAC__metadata_object_vorbiscomment_remove_entry_matching(FLAC__StreamMetadata *object, const char *field_name); + +/** Remove all Vorbis comments matching the given field name. + * + * \param object A pointer to an existing VORBIS_COMMENT object. + * \param field_name The field name of comments to delete. + * \assert + * \code object != NULL \endcode + * \code object->type == FLAC__METADATA_TYPE_VORBIS_COMMENT \endcode + * \retval int + * \c -1 for memory allocation error, \c 0 for no matching entries, + * else the number of matching entries deleted. + */ +FLAC_API int FLAC__metadata_object_vorbiscomment_remove_entries_matching(FLAC__StreamMetadata *object, const char *field_name); + +/** Create a new CUESHEET track instance. + * + * The object will be "empty"; i.e. values and data pointers will be \c 0. + * + * \retval FLAC__StreamMetadata_CueSheet_Track* + * \c NULL if there was an error allocating memory, else the new instance. + */ +FLAC_API FLAC__StreamMetadata_CueSheet_Track *FLAC__metadata_object_cuesheet_track_new(void); + +/** Create a copy of an existing CUESHEET track object. + * + * The copy is a "deep" copy, i.e. dynamically allocated data within the + * object is also copied. The caller takes ownership of the new object and + * is responsible for freeing it with + * FLAC__metadata_object_cuesheet_track_delete(). + * + * \param object Pointer to object to copy. + * \assert + * \code object != NULL \endcode + * \retval FLAC__StreamMetadata_CueSheet_Track* + * \c NULL if there was an error allocating memory, else the new instance. + */ +FLAC_API FLAC__StreamMetadata_CueSheet_Track *FLAC__metadata_object_cuesheet_track_clone(const FLAC__StreamMetadata_CueSheet_Track *object); + +/** Delete a CUESHEET track object + * + * \param object A pointer to an existing CUESHEET track object. + * \assert + * \code object != NULL \endcode + */ +FLAC_API void FLAC__metadata_object_cuesheet_track_delete(FLAC__StreamMetadata_CueSheet_Track *object); + +/** Resize a track's index point array. + * + * If the size shrinks, elements will truncated; if it grows, new blank + * indices will be added to the end. + * + * \param object A pointer to an existing CUESHEET object. + * \param track_num The index of the track to modify. NOTE: this is not + * necessarily the same as the track's \a number field. + * \param new_num_indices The desired length of the array; may be \c 0. + * \assert + * \code object != NULL \endcode + * \code object->type == FLAC__METADATA_TYPE_CUESHEET \endcode + * \code object->data.cue_sheet.num_tracks > track_num \endcode + * \code (object->data.cue_sheet.tracks[track_num].indices == NULL && object->data.cue_sheet.tracks[track_num].num_indices == 0) || + * (object->data.cue_sheet.tracks[track_num].indices != NULL && object->data.cue_sheet.tracks[track_num].num_indices > 0) \endcode + * \retval FLAC__bool + * \c false if memory allocation error, else \c true. + */ +FLAC_API FLAC__bool FLAC__metadata_object_cuesheet_track_resize_indices(FLAC__StreamMetadata *object, unsigned track_num, unsigned new_num_indices); + +/** Insert an index point in a CUESHEET track at the given index. + * + * \param object A pointer to an existing CUESHEET object. + * \param track_num The index of the track to modify. NOTE: this is not + * necessarily the same as the track's \a number field. + * \param index_num The index into the track's index array at which to + * insert the index point. NOTE: this is not necessarily + * the same as the index point's \a number field. The + * indices at and after \a index_num move right one + * position. To append an index point to the end, set + * \a index_num to + * \c object->data.cue_sheet.tracks[track_num].num_indices . + * \param index The index point to insert. + * \assert + * \code object != NULL \endcode + * \code object->type == FLAC__METADATA_TYPE_CUESHEET \endcode + * \code object->data.cue_sheet.num_tracks > track_num \endcode + * \code object->data.cue_sheet.tracks[track_num].num_indices >= index_num \endcode + * \retval FLAC__bool + * \c false if realloc() fails, else \c true. + */ +FLAC_API FLAC__bool FLAC__metadata_object_cuesheet_track_insert_index(FLAC__StreamMetadata *object, unsigned track_num, unsigned index_num, FLAC__StreamMetadata_CueSheet_Index index); + +/** Insert a blank index point in a CUESHEET track at the given index. + * + * A blank index point is one in which all field values are zero. + * + * \param object A pointer to an existing CUESHEET object. + * \param track_num The index of the track to modify. NOTE: this is not + * necessarily the same as the track's \a number field. + * \param index_num The index into the track's index array at which to + * insert the index point. NOTE: this is not necessarily + * the same as the index point's \a number field. The + * indices at and after \a index_num move right one + * position. To append an index point to the end, set + * \a index_num to + * \c object->data.cue_sheet.tracks[track_num].num_indices . + * \assert + * \code object != NULL \endcode + * \code object->type == FLAC__METADATA_TYPE_CUESHEET \endcode + * \code object->data.cue_sheet.num_tracks > track_num \endcode + * \code object->data.cue_sheet.tracks[track_num].num_indices >= index_num \endcode + * \retval FLAC__bool + * \c false if realloc() fails, else \c true. + */ +FLAC_API FLAC__bool FLAC__metadata_object_cuesheet_track_insert_blank_index(FLAC__StreamMetadata *object, unsigned track_num, unsigned index_num); + +/** Delete an index point in a CUESHEET track at the given index. + * + * \param object A pointer to an existing CUESHEET object. + * \param track_num The index into the track array of the track to + * modify. NOTE: this is not necessarily the same + * as the track's \a number field. + * \param index_num The index into the track's index array of the index + * to delete. NOTE: this is not necessarily the same + * as the index's \a number field. + * \assert + * \code object != NULL \endcode + * \code object->type == FLAC__METADATA_TYPE_CUESHEET \endcode + * \code object->data.cue_sheet.num_tracks > track_num \endcode + * \code object->data.cue_sheet.tracks[track_num].num_indices > index_num \endcode + * \retval FLAC__bool + * \c false if realloc() fails, else \c true. + */ +FLAC_API FLAC__bool FLAC__metadata_object_cuesheet_track_delete_index(FLAC__StreamMetadata *object, unsigned track_num, unsigned index_num); + +/** Resize the track array. + * + * If the size shrinks, elements will truncated; if it grows, new blank + * tracks will be added to the end. + * + * \param object A pointer to an existing CUESHEET object. + * \param new_num_tracks The desired length of the array; may be \c 0. + * \assert + * \code object != NULL \endcode + * \code object->type == FLAC__METADATA_TYPE_CUESHEET \endcode + * \code (object->data.cue_sheet.tracks == NULL && object->data.cue_sheet.num_tracks == 0) || + * (object->data.cue_sheet.tracks != NULL && object->data.cue_sheet.num_tracks > 0) \endcode + * \retval FLAC__bool + * \c false if memory allocation error, else \c true. + */ +FLAC_API FLAC__bool FLAC__metadata_object_cuesheet_resize_tracks(FLAC__StreamMetadata *object, unsigned new_num_tracks); + +/** Sets a track in a CUESHEET block. + * + * If \a copy is \c true, a copy of the track is stored; otherwise, the object + * takes ownership of the \a track pointer. + * + * \param object A pointer to an existing CUESHEET object. + * \param track_num Index into track array to set. NOTE: this is not + * necessarily the same as the track's \a number field. + * \param track The track to set the track to. You may safely pass in + * a const pointer if \a copy is \c true. + * \param copy See above. + * \assert + * \code object != NULL \endcode + * \code object->type == FLAC__METADATA_TYPE_CUESHEET \endcode + * \code track_num < object->data.cue_sheet.num_tracks \endcode + * \code (track->indices != NULL && track->num_indices > 0) || + * (track->indices == NULL && track->num_indices == 0) \endcode + * \retval FLAC__bool + * \c false if \a copy is \c true and malloc() fails, else \c true. + */ +FLAC_API FLAC__bool FLAC__metadata_object_cuesheet_set_track(FLAC__StreamMetadata *object, unsigned track_num, FLAC__StreamMetadata_CueSheet_Track *track, FLAC__bool copy); + +/** Insert a track in a CUESHEET block at the given index. + * + * If \a copy is \c true, a copy of the track is stored; otherwise, the object + * takes ownership of the \a track pointer. + * + * \param object A pointer to an existing CUESHEET object. + * \param track_num The index at which to insert the track. NOTE: this + * is not necessarily the same as the track's \a number + * field. The tracks at and after \a track_num move right + * one position. To append a track to the end, set + * \a track_num to \c object->data.cue_sheet.num_tracks . + * \param track The track to insert. You may safely pass in a const + * pointer if \a copy is \c true. + * \param copy See above. + * \assert + * \code object != NULL \endcode + * \code object->type == FLAC__METADATA_TYPE_CUESHEET \endcode + * \code object->data.cue_sheet.num_tracks >= track_num \endcode + * \retval FLAC__bool + * \c false if \a copy is \c true and malloc() fails, else \c true. + */ +FLAC_API FLAC__bool FLAC__metadata_object_cuesheet_insert_track(FLAC__StreamMetadata *object, unsigned track_num, FLAC__StreamMetadata_CueSheet_Track *track, FLAC__bool copy); + +/** Insert a blank track in a CUESHEET block at the given index. + * + * A blank track is one in which all field values are zero. + * + * \param object A pointer to an existing CUESHEET object. + * \param track_num The index at which to insert the track. NOTE: this + * is not necessarily the same as the track's \a number + * field. The tracks at and after \a track_num move right + * one position. To append a track to the end, set + * \a track_num to \c object->data.cue_sheet.num_tracks . + * \assert + * \code object != NULL \endcode + * \code object->type == FLAC__METADATA_TYPE_CUESHEET \endcode + * \code object->data.cue_sheet.num_tracks >= track_num \endcode + * \retval FLAC__bool + * \c false if \a copy is \c true and malloc() fails, else \c true. + */ +FLAC_API FLAC__bool FLAC__metadata_object_cuesheet_insert_blank_track(FLAC__StreamMetadata *object, unsigned track_num); + +/** Delete a track in a CUESHEET block at the given index. + * + * \param object A pointer to an existing CUESHEET object. + * \param track_num The index into the track array of the track to + * delete. NOTE: this is not necessarily the same + * as the track's \a number field. + * \assert + * \code object != NULL \endcode + * \code object->type == FLAC__METADATA_TYPE_CUESHEET \endcode + * \code object->data.cue_sheet.num_tracks > track_num \endcode + * \retval FLAC__bool + * \c false if realloc() fails, else \c true. + */ +FLAC_API FLAC__bool FLAC__metadata_object_cuesheet_delete_track(FLAC__StreamMetadata *object, unsigned track_num); + +/** Check a cue sheet to see if it conforms to the FLAC specification. + * See the format specification for limits on the contents of the + * cue sheet. + * + * \param object A pointer to an existing CUESHEET object. + * \param check_cd_da_subset If \c true, check CUESHEET against more + * stringent requirements for a CD-DA (audio) disc. + * \param violation Address of a pointer to a string. If there is a + * violation, a pointer to a string explanation of the + * violation will be returned here. \a violation may be + * \c NULL if you don't need the returned string. Do not + * free the returned string; it will always point to static + * data. + * \assert + * \code object != NULL \endcode + * \code object->type == FLAC__METADATA_TYPE_CUESHEET \endcode + * \retval FLAC__bool + * \c false if cue sheet is illegal, else \c true. + */ +FLAC_API FLAC__bool FLAC__metadata_object_cuesheet_is_legal(const FLAC__StreamMetadata *object, FLAC__bool check_cd_da_subset, const char **violation); + +/** Calculate and return the CDDB/freedb ID for a cue sheet. The function + * assumes the cue sheet corresponds to a CD; the result is undefined + * if the cuesheet's is_cd bit is not set. + * + * \param object A pointer to an existing CUESHEET object. + * \assert + * \code object != NULL \endcode + * \code object->type == FLAC__METADATA_TYPE_CUESHEET \endcode + * \retval FLAC__uint32 + * The unsigned integer representation of the CDDB/freedb ID + */ +FLAC_API FLAC__uint32 FLAC__metadata_object_cuesheet_calculate_cddb_id(const FLAC__StreamMetadata *object); + +/** Sets the MIME type of a PICTURE block. + * + * If \a copy is \c true, a copy of the string is stored; otherwise, the object + * takes ownership of the pointer. The existing string will be freed if this + * function is successful, otherwise the original string will remain if \a copy + * is \c true and malloc() fails. + * + * \note It is safe to pass a const pointer to \a mime_type if \a copy is \c true. + * + * \param object A pointer to an existing PICTURE object. + * \param mime_type A pointer to the MIME type string. The string must be + * ASCII characters 0x20-0x7e, NUL-terminated. No validation + * is done. + * \param copy See above. + * \assert + * \code object != NULL \endcode + * \code object->type == FLAC__METADATA_TYPE_PICTURE \endcode + * \code (mime_type != NULL) \endcode + * \retval FLAC__bool + * \c false if \a copy is \c true and malloc() fails, else \c true. + */ +FLAC_API FLAC__bool FLAC__metadata_object_picture_set_mime_type(FLAC__StreamMetadata *object, char *mime_type, FLAC__bool copy); + +/** Sets the description of a PICTURE block. + * + * If \a copy is \c true, a copy of the string is stored; otherwise, the object + * takes ownership of the pointer. The existing string will be freed if this + * function is successful, otherwise the original string will remain if \a copy + * is \c true and malloc() fails. + * + * \note It is safe to pass a const pointer to \a description if \a copy is \c true. + * + * \param object A pointer to an existing PICTURE object. + * \param description A pointer to the description string. The string must be + * valid UTF-8, NUL-terminated. No validation is done. + * \param copy See above. + * \assert + * \code object != NULL \endcode + * \code object->type == FLAC__METADATA_TYPE_PICTURE \endcode + * \code (description != NULL) \endcode + * \retval FLAC__bool + * \c false if \a copy is \c true and malloc() fails, else \c true. + */ +FLAC_API FLAC__bool FLAC__metadata_object_picture_set_description(FLAC__StreamMetadata *object, FLAC__byte *description, FLAC__bool copy); + +/** Sets the picture data of a PICTURE block. + * + * If \a copy is \c true, a copy of the data is stored; otherwise, the object + * takes ownership of the pointer. Also sets the \a data_length field of the + * metadata object to what is passed in as the \a length parameter. The + * existing data will be freed if this function is successful, otherwise the + * original data and data_length will remain if \a copy is \c true and + * malloc() fails. + * + * \note It is safe to pass a const pointer to \a data if \a copy is \c true. + * + * \param object A pointer to an existing PICTURE object. + * \param data A pointer to the data to set. + * \param length The length of \a data in bytes. + * \param copy See above. + * \assert + * \code object != NULL \endcode + * \code object->type == FLAC__METADATA_TYPE_PICTURE \endcode + * \code (data != NULL && length > 0) || + * (data == NULL && length == 0 && copy == false) \endcode + * \retval FLAC__bool + * \c false if \a copy is \c true and malloc() fails, else \c true. + */ +FLAC_API FLAC__bool FLAC__metadata_object_picture_set_data(FLAC__StreamMetadata *object, FLAC__byte *data, FLAC__uint32 length, FLAC__bool copy); + +/** Check a PICTURE block to see if it conforms to the FLAC specification. + * See the format specification for limits on the contents of the + * PICTURE block. + * + * \param object A pointer to existing PICTURE block to be checked. + * \param violation Address of a pointer to a string. If there is a + * violation, a pointer to a string explanation of the + * violation will be returned here. \a violation may be + * \c NULL if you don't need the returned string. Do not + * free the returned string; it will always point to static + * data. + * \assert + * \code object != NULL \endcode + * \code object->type == FLAC__METADATA_TYPE_PICTURE \endcode + * \retval FLAC__bool + * \c false if PICTURE block is illegal, else \c true. + */ +FLAC_API FLAC__bool FLAC__metadata_object_picture_is_legal(const FLAC__StreamMetadata *object, const char **violation); + +/* \} */ + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/core/deps/flac/include/FLAC/ordinals.h b/core/deps/flac/include/FLAC/ordinals.h new file mode 100644 index 000000000..ea52ea637 --- /dev/null +++ b/core/deps/flac/include/FLAC/ordinals.h @@ -0,0 +1,86 @@ +/* libFLAC - Free Lossless Audio Codec library + * Copyright (C) 2000-2009 Josh Coalson + * Copyright (C) 2011-2016 Xiph.Org Foundation + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * - Neither the name of the Xiph.org Foundation nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef FLAC__ORDINALS_H +#define FLAC__ORDINALS_H + +#if defined(_MSC_VER) && _MSC_VER < 1600 + +/* Microsoft Visual Studio earlier than the 2010 version did not provide + * the 1999 ISO C Standard header file . + */ + +typedef __int8 FLAC__int8; +typedef unsigned __int8 FLAC__uint8; + +typedef __int16 FLAC__int16; +typedef __int32 FLAC__int32; +typedef __int64 FLAC__int64; +typedef unsigned __int16 FLAC__uint16; +typedef unsigned __int32 FLAC__uint32; +typedef unsigned __int64 FLAC__uint64; + +#else + +/* For MSVC 2010 and everything else which provides . */ + +#include + +typedef int8_t FLAC__int8; +typedef uint8_t FLAC__uint8; + +typedef int16_t FLAC__int16; +typedef int32_t FLAC__int32; +typedef int64_t FLAC__int64; +typedef uint16_t FLAC__uint16; +typedef uint32_t FLAC__uint32; +typedef uint64_t FLAC__uint64; + +#endif + +typedef int FLAC__bool; + +typedef FLAC__uint8 FLAC__byte; + + +#ifdef true +#undef true +#endif +#ifdef false +#undef false +#endif +#ifndef __cplusplus +#define true 1 +#define false 0 +#endif + +#endif diff --git a/core/deps/flac/include/FLAC/stream_decoder.h b/core/deps/flac/include/FLAC/stream_decoder.h new file mode 100644 index 000000000..39c958dbd --- /dev/null +++ b/core/deps/flac/include/FLAC/stream_decoder.h @@ -0,0 +1,1560 @@ +/* libFLAC - Free Lossless Audio Codec library + * Copyright (C) 2000-2009 Josh Coalson + * Copyright (C) 2011-2016 Xiph.Org Foundation + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * - Neither the name of the Xiph.org Foundation nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef FLAC__STREAM_DECODER_H +#define FLAC__STREAM_DECODER_H + +#include /* for FILE */ +#include "export.h" +#include "format.h" + +#ifdef __cplusplus +extern "C" { +#endif + + +/** \file include/FLAC/stream_decoder.h + * + * \brief + * This module contains the functions which implement the stream + * decoder. + * + * See the detailed documentation in the + * \link flac_stream_decoder stream decoder \endlink module. + */ + +/** \defgroup flac_decoder FLAC/ \*_decoder.h: decoder interfaces + * \ingroup flac + * + * \brief + * This module describes the decoder layers provided by libFLAC. + * + * The stream decoder can be used to decode complete streams either from + * the client via callbacks, or directly from a file, depending on how + * it is initialized. When decoding via callbacks, the client provides + * callbacks for reading FLAC data and writing decoded samples, and + * handling metadata and errors. If the client also supplies seek-related + * callback, the decoder function for sample-accurate seeking within the + * FLAC input is also available. When decoding from a file, the client + * needs only supply a filename or open \c FILE* and write/metadata/error + * callbacks; the rest of the callbacks are supplied internally. For more + * info see the \link flac_stream_decoder stream decoder \endlink module. + */ + +/** \defgroup flac_stream_decoder FLAC/stream_decoder.h: stream decoder interface + * \ingroup flac_decoder + * + * \brief + * This module contains the functions which implement the stream + * decoder. + * + * The stream decoder can decode native FLAC, and optionally Ogg FLAC + * (check FLAC_API_SUPPORTS_OGG_FLAC) streams and files. + * + * The basic usage of this decoder is as follows: + * - The program creates an instance of a decoder using + * FLAC__stream_decoder_new(). + * - The program overrides the default settings using + * FLAC__stream_decoder_set_*() functions. + * - The program initializes the instance to validate the settings and + * prepare for decoding using + * - FLAC__stream_decoder_init_stream() or FLAC__stream_decoder_init_FILE() + * or FLAC__stream_decoder_init_file() for native FLAC, + * - FLAC__stream_decoder_init_ogg_stream() or FLAC__stream_decoder_init_ogg_FILE() + * or FLAC__stream_decoder_init_ogg_file() for Ogg FLAC + * - The program calls the FLAC__stream_decoder_process_*() functions + * to decode data, which subsequently calls the callbacks. + * - The program finishes the decoding with FLAC__stream_decoder_finish(), + * which flushes the input and output and resets the decoder to the + * uninitialized state. + * - The instance may be used again or deleted with + * FLAC__stream_decoder_delete(). + * + * In more detail, the program will create a new instance by calling + * FLAC__stream_decoder_new(), then call FLAC__stream_decoder_set_*() + * functions to override the default decoder options, and call + * one of the FLAC__stream_decoder_init_*() functions. + * + * There are three initialization functions for native FLAC, one for + * setting up the decoder to decode FLAC data from the client via + * callbacks, and two for decoding directly from a FLAC file. + * + * For decoding via callbacks, use FLAC__stream_decoder_init_stream(). + * You must also supply several callbacks for handling I/O. Some (like + * seeking) are optional, depending on the capabilities of the input. + * + * For decoding directly from a file, use FLAC__stream_decoder_init_FILE() + * or FLAC__stream_decoder_init_file(). Then you must only supply an open + * \c FILE* or filename and fewer callbacks; the decoder will handle + * the other callbacks internally. + * + * There are three similarly-named init functions for decoding from Ogg + * FLAC streams. Check \c FLAC_API_SUPPORTS_OGG_FLAC to find out if the + * library has been built with Ogg support. + * + * Once the decoder is initialized, your program will call one of several + * functions to start the decoding process: + * + * - FLAC__stream_decoder_process_single() - Tells the decoder to process at + * most one metadata block or audio frame and return, calling either the + * metadata callback or write callback, respectively, once. If the decoder + * loses sync it will return with only the error callback being called. + * - FLAC__stream_decoder_process_until_end_of_metadata() - Tells the decoder + * to process the stream from the current location and stop upon reaching + * the first audio frame. The client will get one metadata, write, or error + * callback per metadata block, audio frame, or sync error, respectively. + * - FLAC__stream_decoder_process_until_end_of_stream() - Tells the decoder + * to process the stream from the current location until the read callback + * returns FLAC__STREAM_DECODER_READ_STATUS_END_OF_STREAM or + * FLAC__STREAM_DECODER_READ_STATUS_ABORT. The client will get one metadata, + * write, or error callback per metadata block, audio frame, or sync error, + * respectively. + * + * When the decoder has finished decoding (normally or through an abort), + * the instance is finished by calling FLAC__stream_decoder_finish(), which + * ensures the decoder is in the correct state and frees memory. Then the + * instance may be deleted with FLAC__stream_decoder_delete() or initialized + * again to decode another stream. + * + * Seeking is exposed through the FLAC__stream_decoder_seek_absolute() method. + * At any point after the stream decoder has been initialized, the client can + * call this function to seek to an exact sample within the stream. + * Subsequently, the first time the write callback is called it will be + * passed a (possibly partial) block starting at that sample. + * + * If the client cannot seek via the callback interface provided, but still + * has another way of seeking, it can flush the decoder using + * FLAC__stream_decoder_flush() and start feeding data from the new position + * through the read callback. + * + * The stream decoder also provides MD5 signature checking. If this is + * turned on before initialization, FLAC__stream_decoder_finish() will + * report when the decoded MD5 signature does not match the one stored + * in the STREAMINFO block. MD5 checking is automatically turned off + * (until the next FLAC__stream_decoder_reset()) if there is no signature + * in the STREAMINFO block or when a seek is attempted. + * + * The FLAC__stream_decoder_set_metadata_*() functions deserve special + * attention. By default, the decoder only calls the metadata_callback for + * the STREAMINFO block. These functions allow you to tell the decoder + * explicitly which blocks to parse and return via the metadata_callback + * and/or which to skip. Use a FLAC__stream_decoder_set_metadata_respond_all(), + * FLAC__stream_decoder_set_metadata_ignore() ... or FLAC__stream_decoder_set_metadata_ignore_all(), + * FLAC__stream_decoder_set_metadata_respond() ... sequence to exactly specify + * which blocks to return. Remember that metadata blocks can potentially + * be big (for example, cover art) so filtering out the ones you don't + * use can reduce the memory requirements of the decoder. Also note the + * special forms FLAC__stream_decoder_set_metadata_respond_application(id) + * and FLAC__stream_decoder_set_metadata_ignore_application(id) for + * filtering APPLICATION blocks based on the application ID. + * + * STREAMINFO and SEEKTABLE blocks are always parsed and used internally, but + * they still can legally be filtered from the metadata_callback. + * + * \note + * The "set" functions may only be called when the decoder is in the + * state FLAC__STREAM_DECODER_UNINITIALIZED, i.e. after + * FLAC__stream_decoder_new() or FLAC__stream_decoder_finish(), but + * before FLAC__stream_decoder_init_*(). If this is the case they will + * return \c true, otherwise \c false. + * + * \note + * FLAC__stream_decoder_finish() resets all settings to the constructor + * defaults, including the callbacks. + * + * \{ + */ + + +/** State values for a FLAC__StreamDecoder + * + * The decoder's state can be obtained by calling FLAC__stream_decoder_get_state(). + */ +typedef enum { + + FLAC__STREAM_DECODER_SEARCH_FOR_METADATA = 0, + /**< The decoder is ready to search for metadata. */ + + FLAC__STREAM_DECODER_READ_METADATA, + /**< The decoder is ready to or is in the process of reading metadata. */ + + FLAC__STREAM_DECODER_SEARCH_FOR_FRAME_SYNC, + /**< The decoder is ready to or is in the process of searching for the + * frame sync code. + */ + + FLAC__STREAM_DECODER_READ_FRAME, + /**< The decoder is ready to or is in the process of reading a frame. */ + + FLAC__STREAM_DECODER_END_OF_STREAM, + /**< The decoder has reached the end of the stream. */ + + FLAC__STREAM_DECODER_OGG_ERROR, + /**< An error occurred in the underlying Ogg layer. */ + + FLAC__STREAM_DECODER_SEEK_ERROR, + /**< An error occurred while seeking. The decoder must be flushed + * with FLAC__stream_decoder_flush() or reset with + * FLAC__stream_decoder_reset() before decoding can continue. + */ + + FLAC__STREAM_DECODER_ABORTED, + /**< The decoder was aborted by the read or write callback. */ + + FLAC__STREAM_DECODER_MEMORY_ALLOCATION_ERROR, + /**< An error occurred allocating memory. The decoder is in an invalid + * state and can no longer be used. + */ + + FLAC__STREAM_DECODER_UNINITIALIZED + /**< The decoder is in the uninitialized state; one of the + * FLAC__stream_decoder_init_*() functions must be called before samples + * can be processed. + */ + +} FLAC__StreamDecoderState; + +/** Maps a FLAC__StreamDecoderState to a C string. + * + * Using a FLAC__StreamDecoderState as the index to this array + * will give the string equivalent. The contents should not be modified. + */ +extern FLAC_API const char * const FLAC__StreamDecoderStateString[]; + + +/** Possible return values for the FLAC__stream_decoder_init_*() functions. + */ +typedef enum { + + FLAC__STREAM_DECODER_INIT_STATUS_OK = 0, + /**< Initialization was successful. */ + + FLAC__STREAM_DECODER_INIT_STATUS_UNSUPPORTED_CONTAINER, + /**< The library was not compiled with support for the given container + * format. + */ + + FLAC__STREAM_DECODER_INIT_STATUS_INVALID_CALLBACKS, + /**< A required callback was not supplied. */ + + FLAC__STREAM_DECODER_INIT_STATUS_MEMORY_ALLOCATION_ERROR, + /**< An error occurred allocating memory. */ + + FLAC__STREAM_DECODER_INIT_STATUS_ERROR_OPENING_FILE, + /**< fopen() failed in FLAC__stream_decoder_init_file() or + * FLAC__stream_decoder_init_ogg_file(). */ + + FLAC__STREAM_DECODER_INIT_STATUS_ALREADY_INITIALIZED + /**< FLAC__stream_decoder_init_*() was called when the decoder was + * already initialized, usually because + * FLAC__stream_decoder_finish() was not called. + */ + +} FLAC__StreamDecoderInitStatus; + +/** Maps a FLAC__StreamDecoderInitStatus to a C string. + * + * Using a FLAC__StreamDecoderInitStatus as the index to this array + * will give the string equivalent. The contents should not be modified. + */ +extern FLAC_API const char * const FLAC__StreamDecoderInitStatusString[]; + + +/** Return values for the FLAC__StreamDecoder read callback. + */ +typedef enum { + + FLAC__STREAM_DECODER_READ_STATUS_CONTINUE, + /**< The read was OK and decoding can continue. */ + + FLAC__STREAM_DECODER_READ_STATUS_END_OF_STREAM, + /**< The read was attempted while at the end of the stream. Note that + * the client must only return this value when the read callback was + * called when already at the end of the stream. Otherwise, if the read + * itself moves to the end of the stream, the client should still return + * the data and \c FLAC__STREAM_DECODER_READ_STATUS_CONTINUE, and then on + * the next read callback it should return + * \c FLAC__STREAM_DECODER_READ_STATUS_END_OF_STREAM with a byte count + * of \c 0. + */ + + FLAC__STREAM_DECODER_READ_STATUS_ABORT + /**< An unrecoverable error occurred. The decoder will return from the process call. */ + +} FLAC__StreamDecoderReadStatus; + +/** Maps a FLAC__StreamDecoderReadStatus to a C string. + * + * Using a FLAC__StreamDecoderReadStatus as the index to this array + * will give the string equivalent. The contents should not be modified. + */ +extern FLAC_API const char * const FLAC__StreamDecoderReadStatusString[]; + + +/** Return values for the FLAC__StreamDecoder seek callback. + */ +typedef enum { + + FLAC__STREAM_DECODER_SEEK_STATUS_OK, + /**< The seek was OK and decoding can continue. */ + + FLAC__STREAM_DECODER_SEEK_STATUS_ERROR, + /**< An unrecoverable error occurred. The decoder will return from the process call. */ + + FLAC__STREAM_DECODER_SEEK_STATUS_UNSUPPORTED + /**< Client does not support seeking. */ + +} FLAC__StreamDecoderSeekStatus; + +/** Maps a FLAC__StreamDecoderSeekStatus to a C string. + * + * Using a FLAC__StreamDecoderSeekStatus as the index to this array + * will give the string equivalent. The contents should not be modified. + */ +extern FLAC_API const char * const FLAC__StreamDecoderSeekStatusString[]; + + +/** Return values for the FLAC__StreamDecoder tell callback. + */ +typedef enum { + + FLAC__STREAM_DECODER_TELL_STATUS_OK, + /**< The tell was OK and decoding can continue. */ + + FLAC__STREAM_DECODER_TELL_STATUS_ERROR, + /**< An unrecoverable error occurred. The decoder will return from the process call. */ + + FLAC__STREAM_DECODER_TELL_STATUS_UNSUPPORTED + /**< Client does not support telling the position. */ + +} FLAC__StreamDecoderTellStatus; + +/** Maps a FLAC__StreamDecoderTellStatus to a C string. + * + * Using a FLAC__StreamDecoderTellStatus as the index to this array + * will give the string equivalent. The contents should not be modified. + */ +extern FLAC_API const char * const FLAC__StreamDecoderTellStatusString[]; + + +/** Return values for the FLAC__StreamDecoder length callback. + */ +typedef enum { + + FLAC__STREAM_DECODER_LENGTH_STATUS_OK, + /**< The length call was OK and decoding can continue. */ + + FLAC__STREAM_DECODER_LENGTH_STATUS_ERROR, + /**< An unrecoverable error occurred. The decoder will return from the process call. */ + + FLAC__STREAM_DECODER_LENGTH_STATUS_UNSUPPORTED + /**< Client does not support reporting the length. */ + +} FLAC__StreamDecoderLengthStatus; + +/** Maps a FLAC__StreamDecoderLengthStatus to a C string. + * + * Using a FLAC__StreamDecoderLengthStatus as the index to this array + * will give the string equivalent. The contents should not be modified. + */ +extern FLAC_API const char * const FLAC__StreamDecoderLengthStatusString[]; + + +/** Return values for the FLAC__StreamDecoder write callback. + */ +typedef enum { + + FLAC__STREAM_DECODER_WRITE_STATUS_CONTINUE, + /**< The write was OK and decoding can continue. */ + + FLAC__STREAM_DECODER_WRITE_STATUS_ABORT + /**< An unrecoverable error occurred. The decoder will return from the process call. */ + +} FLAC__StreamDecoderWriteStatus; + +/** Maps a FLAC__StreamDecoderWriteStatus to a C string. + * + * Using a FLAC__StreamDecoderWriteStatus as the index to this array + * will give the string equivalent. The contents should not be modified. + */ +extern FLAC_API const char * const FLAC__StreamDecoderWriteStatusString[]; + + +/** Possible values passed back to the FLAC__StreamDecoder error callback. + * \c FLAC__STREAM_DECODER_ERROR_STATUS_LOST_SYNC is the generic catch- + * all. The rest could be caused by bad sync (false synchronization on + * data that is not the start of a frame) or corrupted data. The error + * itself is the decoder's best guess at what happened assuming a correct + * sync. For example \c FLAC__STREAM_DECODER_ERROR_STATUS_BAD_HEADER + * could be caused by a correct sync on the start of a frame, but some + * data in the frame header was corrupted. Or it could be the result of + * syncing on a point the stream that looked like the starting of a frame + * but was not. \c FLAC__STREAM_DECODER_ERROR_STATUS_UNPARSEABLE_STREAM + * could be because the decoder encountered a valid frame made by a future + * version of the encoder which it cannot parse, or because of a false + * sync making it appear as though an encountered frame was generated by + * a future encoder. + */ +typedef enum { + + FLAC__STREAM_DECODER_ERROR_STATUS_LOST_SYNC, + /**< An error in the stream caused the decoder to lose synchronization. */ + + FLAC__STREAM_DECODER_ERROR_STATUS_BAD_HEADER, + /**< The decoder encountered a corrupted frame header. */ + + FLAC__STREAM_DECODER_ERROR_STATUS_FRAME_CRC_MISMATCH, + /**< The frame's data did not match the CRC in the footer. */ + + FLAC__STREAM_DECODER_ERROR_STATUS_UNPARSEABLE_STREAM + /**< The decoder encountered reserved fields in use in the stream. */ + +} FLAC__StreamDecoderErrorStatus; + +/** Maps a FLAC__StreamDecoderErrorStatus to a C string. + * + * Using a FLAC__StreamDecoderErrorStatus as the index to this array + * will give the string equivalent. The contents should not be modified. + */ +extern FLAC_API const char * const FLAC__StreamDecoderErrorStatusString[]; + + +/*********************************************************************** + * + * class FLAC__StreamDecoder + * + ***********************************************************************/ + +struct FLAC__StreamDecoderProtected; +struct FLAC__StreamDecoderPrivate; +/** The opaque structure definition for the stream decoder type. + * See the \link flac_stream_decoder stream decoder module \endlink + * for a detailed description. + */ +typedef struct { + struct FLAC__StreamDecoderProtected *protected_; /* avoid the C++ keyword 'protected' */ + struct FLAC__StreamDecoderPrivate *private_; /* avoid the C++ keyword 'private' */ +} FLAC__StreamDecoder; + +/** Signature for the read callback. + * + * A function pointer matching this signature must be passed to + * FLAC__stream_decoder_init*_stream(). The supplied function will be + * called when the decoder needs more input data. The address of the + * buffer to be filled is supplied, along with the number of bytes the + * buffer can hold. The callback may choose to supply less data and + * modify the byte count but must be careful not to overflow the buffer. + * The callback then returns a status code chosen from + * FLAC__StreamDecoderReadStatus. + * + * Here is an example of a read callback for stdio streams: + * \code + * FLAC__StreamDecoderReadStatus read_cb(const FLAC__StreamDecoder *decoder, FLAC__byte buffer[], size_t *bytes, void *client_data) + * { + * FILE *file = ((MyClientData*)client_data)->file; + * if(*bytes > 0) { + * *bytes = fread(buffer, sizeof(FLAC__byte), *bytes, file); + * if(ferror(file)) + * return FLAC__STREAM_DECODER_READ_STATUS_ABORT; + * else if(*bytes == 0) + * return FLAC__STREAM_DECODER_READ_STATUS_END_OF_STREAM; + * else + * return FLAC__STREAM_DECODER_READ_STATUS_CONTINUE; + * } + * else + * return FLAC__STREAM_DECODER_READ_STATUS_ABORT; + * } + * \endcode + * + * \note In general, FLAC__StreamDecoder functions which change the + * state should not be called on the \a decoder while in the callback. + * + * \param decoder The decoder instance calling the callback. + * \param buffer A pointer to a location for the callee to store + * data to be decoded. + * \param bytes A pointer to the size of the buffer. On entry + * to the callback, it contains the maximum number + * of bytes that may be stored in \a buffer. The + * callee must set it to the actual number of bytes + * stored (0 in case of error or end-of-stream) before + * returning. + * \param client_data The callee's client data set through + * FLAC__stream_decoder_init_*(). + * \retval FLAC__StreamDecoderReadStatus + * The callee's return status. Note that the callback should return + * \c FLAC__STREAM_DECODER_READ_STATUS_END_OF_STREAM if and only if + * zero bytes were read and there is no more data to be read. + */ +typedef FLAC__StreamDecoderReadStatus (*FLAC__StreamDecoderReadCallback)(const FLAC__StreamDecoder *decoder, FLAC__byte buffer[], size_t *bytes, void *client_data); + +/** Signature for the seek callback. + * + * A function pointer matching this signature may be passed to + * FLAC__stream_decoder_init*_stream(). The supplied function will be + * called when the decoder needs to seek the input stream. The decoder + * will pass the absolute byte offset to seek to, 0 meaning the + * beginning of the stream. + * + * Here is an example of a seek callback for stdio streams: + * \code + * FLAC__StreamDecoderSeekStatus seek_cb(const FLAC__StreamDecoder *decoder, FLAC__uint64 absolute_byte_offset, void *client_data) + * { + * FILE *file = ((MyClientData*)client_data)->file; + * if(file == stdin) + * return FLAC__STREAM_DECODER_SEEK_STATUS_UNSUPPORTED; + * else if(fseeko(file, (off_t)absolute_byte_offset, SEEK_SET) < 0) + * return FLAC__STREAM_DECODER_SEEK_STATUS_ERROR; + * else + * return FLAC__STREAM_DECODER_SEEK_STATUS_OK; + * } + * \endcode + * + * \note In general, FLAC__StreamDecoder functions which change the + * state should not be called on the \a decoder while in the callback. + * + * \param decoder The decoder instance calling the callback. + * \param absolute_byte_offset The offset from the beginning of the stream + * to seek to. + * \param client_data The callee's client data set through + * FLAC__stream_decoder_init_*(). + * \retval FLAC__StreamDecoderSeekStatus + * The callee's return status. + */ +typedef FLAC__StreamDecoderSeekStatus (*FLAC__StreamDecoderSeekCallback)(const FLAC__StreamDecoder *decoder, FLAC__uint64 absolute_byte_offset, void *client_data); + +/** Signature for the tell callback. + * + * A function pointer matching this signature may be passed to + * FLAC__stream_decoder_init*_stream(). The supplied function will be + * called when the decoder wants to know the current position of the + * stream. The callback should return the byte offset from the + * beginning of the stream. + * + * Here is an example of a tell callback for stdio streams: + * \code + * FLAC__StreamDecoderTellStatus tell_cb(const FLAC__StreamDecoder *decoder, FLAC__uint64 *absolute_byte_offset, void *client_data) + * { + * FILE *file = ((MyClientData*)client_data)->file; + * off_t pos; + * if(file == stdin) + * return FLAC__STREAM_DECODER_TELL_STATUS_UNSUPPORTED; + * else if((pos = ftello(file)) < 0) + * return FLAC__STREAM_DECODER_TELL_STATUS_ERROR; + * else { + * *absolute_byte_offset = (FLAC__uint64)pos; + * return FLAC__STREAM_DECODER_TELL_STATUS_OK; + * } + * } + * \endcode + * + * \note In general, FLAC__StreamDecoder functions which change the + * state should not be called on the \a decoder while in the callback. + * + * \param decoder The decoder instance calling the callback. + * \param absolute_byte_offset A pointer to storage for the current offset + * from the beginning of the stream. + * \param client_data The callee's client data set through + * FLAC__stream_decoder_init_*(). + * \retval FLAC__StreamDecoderTellStatus + * The callee's return status. + */ +typedef FLAC__StreamDecoderTellStatus (*FLAC__StreamDecoderTellCallback)(const FLAC__StreamDecoder *decoder, FLAC__uint64 *absolute_byte_offset, void *client_data); + +/** Signature for the length callback. + * + * A function pointer matching this signature may be passed to + * FLAC__stream_decoder_init*_stream(). The supplied function will be + * called when the decoder wants to know the total length of the stream + * in bytes. + * + * Here is an example of a length callback for stdio streams: + * \code + * FLAC__StreamDecoderLengthStatus length_cb(const FLAC__StreamDecoder *decoder, FLAC__uint64 *stream_length, void *client_data) + * { + * FILE *file = ((MyClientData*)client_data)->file; + * struct stat filestats; + * + * if(file == stdin) + * return FLAC__STREAM_DECODER_LENGTH_STATUS_UNSUPPORTED; + * else if(fstat(fileno(file), &filestats) != 0) + * return FLAC__STREAM_DECODER_LENGTH_STATUS_ERROR; + * else { + * *stream_length = (FLAC__uint64)filestats.st_size; + * return FLAC__STREAM_DECODER_LENGTH_STATUS_OK; + * } + * } + * \endcode + * + * \note In general, FLAC__StreamDecoder functions which change the + * state should not be called on the \a decoder while in the callback. + * + * \param decoder The decoder instance calling the callback. + * \param stream_length A pointer to storage for the length of the stream + * in bytes. + * \param client_data The callee's client data set through + * FLAC__stream_decoder_init_*(). + * \retval FLAC__StreamDecoderLengthStatus + * The callee's return status. + */ +typedef FLAC__StreamDecoderLengthStatus (*FLAC__StreamDecoderLengthCallback)(const FLAC__StreamDecoder *decoder, FLAC__uint64 *stream_length, void *client_data); + +/** Signature for the EOF callback. + * + * A function pointer matching this signature may be passed to + * FLAC__stream_decoder_init*_stream(). The supplied function will be + * called when the decoder needs to know if the end of the stream has + * been reached. + * + * Here is an example of a EOF callback for stdio streams: + * FLAC__bool eof_cb(const FLAC__StreamDecoder *decoder, void *client_data) + * \code + * { + * FILE *file = ((MyClientData*)client_data)->file; + * return feof(file)? true : false; + * } + * \endcode + * + * \note In general, FLAC__StreamDecoder functions which change the + * state should not be called on the \a decoder while in the callback. + * + * \param decoder The decoder instance calling the callback. + * \param client_data The callee's client data set through + * FLAC__stream_decoder_init_*(). + * \retval FLAC__bool + * \c true if the currently at the end of the stream, else \c false. + */ +typedef FLAC__bool (*FLAC__StreamDecoderEofCallback)(const FLAC__StreamDecoder *decoder, void *client_data); + +/** Signature for the write callback. + * + * A function pointer matching this signature must be passed to one of + * the FLAC__stream_decoder_init_*() functions. + * The supplied function will be called when the decoder has decoded a + * single audio frame. The decoder will pass the frame metadata as well + * as an array of pointers (one for each channel) pointing to the + * decoded audio. + * + * \note In general, FLAC__StreamDecoder functions which change the + * state should not be called on the \a decoder while in the callback. + * + * \param decoder The decoder instance calling the callback. + * \param frame The description of the decoded frame. See + * FLAC__Frame. + * \param buffer An array of pointers to decoded channels of data. + * Each pointer will point to an array of signed + * samples of length \a frame->header.blocksize. + * Channels will be ordered according to the FLAC + * specification; see the documentation for the + * frame header. + * \param client_data The callee's client data set through + * FLAC__stream_decoder_init_*(). + * \retval FLAC__StreamDecoderWriteStatus + * The callee's return status. + */ +typedef FLAC__StreamDecoderWriteStatus (*FLAC__StreamDecoderWriteCallback)(const FLAC__StreamDecoder *decoder, const FLAC__Frame *frame, const FLAC__int32 * const buffer[], void *client_data); + +/** Signature for the metadata callback. + * + * A function pointer matching this signature must be passed to one of + * the FLAC__stream_decoder_init_*() functions. + * The supplied function will be called when the decoder has decoded a + * metadata block. In a valid FLAC file there will always be one + * \c STREAMINFO block, followed by zero or more other metadata blocks. + * These will be supplied by the decoder in the same order as they + * appear in the stream and always before the first audio frame (i.e. + * write callback). The metadata block that is passed in must not be + * modified, and it doesn't live beyond the callback, so you should make + * a copy of it with FLAC__metadata_object_clone() if you will need it + * elsewhere. Since metadata blocks can potentially be large, by + * default the decoder only calls the metadata callback for the + * \c STREAMINFO block; you can instruct the decoder to pass or filter + * other blocks with FLAC__stream_decoder_set_metadata_*() calls. + * + * \note In general, FLAC__StreamDecoder functions which change the + * state should not be called on the \a decoder while in the callback. + * + * \param decoder The decoder instance calling the callback. + * \param metadata The decoded metadata block. + * \param client_data The callee's client data set through + * FLAC__stream_decoder_init_*(). + */ +typedef void (*FLAC__StreamDecoderMetadataCallback)(const FLAC__StreamDecoder *decoder, const FLAC__StreamMetadata *metadata, void *client_data); + +/** Signature for the error callback. + * + * A function pointer matching this signature must be passed to one of + * the FLAC__stream_decoder_init_*() functions. + * The supplied function will be called whenever an error occurs during + * decoding. + * + * \note In general, FLAC__StreamDecoder functions which change the + * state should not be called on the \a decoder while in the callback. + * + * \param decoder The decoder instance calling the callback. + * \param status The error encountered by the decoder. + * \param client_data The callee's client data set through + * FLAC__stream_decoder_init_*(). + */ +typedef void (*FLAC__StreamDecoderErrorCallback)(const FLAC__StreamDecoder *decoder, FLAC__StreamDecoderErrorStatus status, void *client_data); + + +/*********************************************************************** + * + * Class constructor/destructor + * + ***********************************************************************/ + +/** Create a new stream decoder instance. The instance is created with + * default settings; see the individual FLAC__stream_decoder_set_*() + * functions for each setting's default. + * + * \retval FLAC__StreamDecoder* + * \c NULL if there was an error allocating memory, else the new instance. + */ +FLAC_API FLAC__StreamDecoder *FLAC__stream_decoder_new(void); + +/** Free a decoder instance. Deletes the object pointed to by \a decoder. + * + * \param decoder A pointer to an existing decoder. + * \assert + * \code decoder != NULL \endcode + */ +FLAC_API void FLAC__stream_decoder_delete(FLAC__StreamDecoder *decoder); + + +/*********************************************************************** + * + * Public class method prototypes + * + ***********************************************************************/ + +/** Set the serial number for the FLAC stream within the Ogg container. + * The default behavior is to use the serial number of the first Ogg + * page. Setting a serial number here will explicitly specify which + * stream is to be decoded. + * + * \note + * This does not need to be set for native FLAC decoding. + * + * \default \c use serial number of first page + * \param decoder A decoder instance to set. + * \param serial_number See above. + * \assert + * \code decoder != NULL \endcode + * \retval FLAC__bool + * \c false if the decoder is already initialized, else \c true. + */ +FLAC_API FLAC__bool FLAC__stream_decoder_set_ogg_serial_number(FLAC__StreamDecoder *decoder, long serial_number); + +/** Set the "MD5 signature checking" flag. If \c true, the decoder will + * compute the MD5 signature of the unencoded audio data while decoding + * and compare it to the signature from the STREAMINFO block, if it + * exists, during FLAC__stream_decoder_finish(). + * + * MD5 signature checking will be turned off (until the next + * FLAC__stream_decoder_reset()) if there is no signature in the + * STREAMINFO block or when a seek is attempted. + * + * Clients that do not use the MD5 check should leave this off to speed + * up decoding. + * + * \default \c false + * \param decoder A decoder instance to set. + * \param value Flag value (see above). + * \assert + * \code decoder != NULL \endcode + * \retval FLAC__bool + * \c false if the decoder is already initialized, else \c true. + */ +FLAC_API FLAC__bool FLAC__stream_decoder_set_md5_checking(FLAC__StreamDecoder *decoder, FLAC__bool value); + +/** Direct the decoder to pass on all metadata blocks of type \a type. + * + * \default By default, only the \c STREAMINFO block is returned via the + * metadata callback. + * \param decoder A decoder instance to set. + * \param type See above. + * \assert + * \code decoder != NULL \endcode + * \a type is valid + * \retval FLAC__bool + * \c false if the decoder is already initialized, else \c true. + */ +FLAC_API FLAC__bool FLAC__stream_decoder_set_metadata_respond(FLAC__StreamDecoder *decoder, FLAC__MetadataType type); + +/** Direct the decoder to pass on all APPLICATION metadata blocks of the + * given \a id. + * + * \default By default, only the \c STREAMINFO block is returned via the + * metadata callback. + * \param decoder A decoder instance to set. + * \param id See above. + * \assert + * \code decoder != NULL \endcode + * \code id != NULL \endcode + * \retval FLAC__bool + * \c false if the decoder is already initialized, else \c true. + */ +FLAC_API FLAC__bool FLAC__stream_decoder_set_metadata_respond_application(FLAC__StreamDecoder *decoder, const FLAC__byte id[4]); + +/** Direct the decoder to pass on all metadata blocks of any type. + * + * \default By default, only the \c STREAMINFO block is returned via the + * metadata callback. + * \param decoder A decoder instance to set. + * \assert + * \code decoder != NULL \endcode + * \retval FLAC__bool + * \c false if the decoder is already initialized, else \c true. + */ +FLAC_API FLAC__bool FLAC__stream_decoder_set_metadata_respond_all(FLAC__StreamDecoder *decoder); + +/** Direct the decoder to filter out all metadata blocks of type \a type. + * + * \default By default, only the \c STREAMINFO block is returned via the + * metadata callback. + * \param decoder A decoder instance to set. + * \param type See above. + * \assert + * \code decoder != NULL \endcode + * \a type is valid + * \retval FLAC__bool + * \c false if the decoder is already initialized, else \c true. + */ +FLAC_API FLAC__bool FLAC__stream_decoder_set_metadata_ignore(FLAC__StreamDecoder *decoder, FLAC__MetadataType type); + +/** Direct the decoder to filter out all APPLICATION metadata blocks of + * the given \a id. + * + * \default By default, only the \c STREAMINFO block is returned via the + * metadata callback. + * \param decoder A decoder instance to set. + * \param id See above. + * \assert + * \code decoder != NULL \endcode + * \code id != NULL \endcode + * \retval FLAC__bool + * \c false if the decoder is already initialized, else \c true. + */ +FLAC_API FLAC__bool FLAC__stream_decoder_set_metadata_ignore_application(FLAC__StreamDecoder *decoder, const FLAC__byte id[4]); + +/** Direct the decoder to filter out all metadata blocks of any type. + * + * \default By default, only the \c STREAMINFO block is returned via the + * metadata callback. + * \param decoder A decoder instance to set. + * \assert + * \code decoder != NULL \endcode + * \retval FLAC__bool + * \c false if the decoder is already initialized, else \c true. + */ +FLAC_API FLAC__bool FLAC__stream_decoder_set_metadata_ignore_all(FLAC__StreamDecoder *decoder); + +/** Get the current decoder state. + * + * \param decoder A decoder instance to query. + * \assert + * \code decoder != NULL \endcode + * \retval FLAC__StreamDecoderState + * The current decoder state. + */ +FLAC_API FLAC__StreamDecoderState FLAC__stream_decoder_get_state(const FLAC__StreamDecoder *decoder); + +/** Get the current decoder state as a C string. + * + * \param decoder A decoder instance to query. + * \assert + * \code decoder != NULL \endcode + * \retval const char * + * The decoder state as a C string. Do not modify the contents. + */ +FLAC_API const char *FLAC__stream_decoder_get_resolved_state_string(const FLAC__StreamDecoder *decoder); + +/** Get the "MD5 signature checking" flag. + * This is the value of the setting, not whether or not the decoder is + * currently checking the MD5 (remember, it can be turned off automatically + * by a seek). When the decoder is reset the flag will be restored to the + * value returned by this function. + * + * \param decoder A decoder instance to query. + * \assert + * \code decoder != NULL \endcode + * \retval FLAC__bool + * See above. + */ +FLAC_API FLAC__bool FLAC__stream_decoder_get_md5_checking(const FLAC__StreamDecoder *decoder); + +/** Get the total number of samples in the stream being decoded. + * Will only be valid after decoding has started and will contain the + * value from the \c STREAMINFO block. A value of \c 0 means "unknown". + * + * \param decoder A decoder instance to query. + * \assert + * \code decoder != NULL \endcode + * \retval unsigned + * See above. + */ +FLAC_API FLAC__uint64 FLAC__stream_decoder_get_total_samples(const FLAC__StreamDecoder *decoder); + +/** Get the current number of channels in the stream being decoded. + * Will only be valid after decoding has started and will contain the + * value from the most recently decoded frame header. + * + * \param decoder A decoder instance to query. + * \assert + * \code decoder != NULL \endcode + * \retval unsigned + * See above. + */ +FLAC_API unsigned FLAC__stream_decoder_get_channels(const FLAC__StreamDecoder *decoder); + +/** Get the current channel assignment in the stream being decoded. + * Will only be valid after decoding has started and will contain the + * value from the most recently decoded frame header. + * + * \param decoder A decoder instance to query. + * \assert + * \code decoder != NULL \endcode + * \retval FLAC__ChannelAssignment + * See above. + */ +FLAC_API FLAC__ChannelAssignment FLAC__stream_decoder_get_channel_assignment(const FLAC__StreamDecoder *decoder); + +/** Get the current sample resolution in the stream being decoded. + * Will only be valid after decoding has started and will contain the + * value from the most recently decoded frame header. + * + * \param decoder A decoder instance to query. + * \assert + * \code decoder != NULL \endcode + * \retval unsigned + * See above. + */ +FLAC_API unsigned FLAC__stream_decoder_get_bits_per_sample(const FLAC__StreamDecoder *decoder); + +/** Get the current sample rate in Hz of the stream being decoded. + * Will only be valid after decoding has started and will contain the + * value from the most recently decoded frame header. + * + * \param decoder A decoder instance to query. + * \assert + * \code decoder != NULL \endcode + * \retval unsigned + * See above. + */ +FLAC_API unsigned FLAC__stream_decoder_get_sample_rate(const FLAC__StreamDecoder *decoder); + +/** Get the current blocksize of the stream being decoded. + * Will only be valid after decoding has started and will contain the + * value from the most recently decoded frame header. + * + * \param decoder A decoder instance to query. + * \assert + * \code decoder != NULL \endcode + * \retval unsigned + * See above. + */ +FLAC_API unsigned FLAC__stream_decoder_get_blocksize(const FLAC__StreamDecoder *decoder); + +/** Returns the decoder's current read position within the stream. + * The position is the byte offset from the start of the stream. + * Bytes before this position have been fully decoded. Note that + * there may still be undecoded bytes in the decoder's read FIFO. + * The returned position is correct even after a seek. + * + * \warning This function currently only works for native FLAC, + * not Ogg FLAC streams. + * + * \param decoder A decoder instance to query. + * \param position Address at which to return the desired position. + * \assert + * \code decoder != NULL \endcode + * \code position != NULL \endcode + * \retval FLAC__bool + * \c true if successful, \c false if the stream is not native FLAC, + * or there was an error from the 'tell' callback or it returned + * \c FLAC__STREAM_DECODER_TELL_STATUS_UNSUPPORTED. + */ +FLAC_API FLAC__bool FLAC__stream_decoder_get_decode_position(const FLAC__StreamDecoder *decoder, FLAC__uint64 *position); + +/** Initialize the decoder instance to decode native FLAC streams. + * + * This flavor of initialization sets up the decoder to decode from a + * native FLAC stream. I/O is performed via callbacks to the client. + * For decoding from a plain file via filename or open FILE*, + * FLAC__stream_decoder_init_file() and FLAC__stream_decoder_init_FILE() + * provide a simpler interface. + * + * This function should be called after FLAC__stream_decoder_new() and + * FLAC__stream_decoder_set_*() but before any of the + * FLAC__stream_decoder_process_*() functions. Will set and return the + * decoder state, which will be FLAC__STREAM_DECODER_SEARCH_FOR_METADATA + * if initialization succeeded. + * + * \param decoder An uninitialized decoder instance. + * \param read_callback See FLAC__StreamDecoderReadCallback. This + * pointer must not be \c NULL. + * \param seek_callback See FLAC__StreamDecoderSeekCallback. This + * pointer may be \c NULL if seeking is not + * supported. If \a seek_callback is not \c NULL then a + * \a tell_callback, \a length_callback, and \a eof_callback must also be supplied. + * Alternatively, a dummy seek callback that just + * returns \c FLAC__STREAM_DECODER_SEEK_STATUS_UNSUPPORTED + * may also be supplied, all though this is slightly + * less efficient for the decoder. + * \param tell_callback See FLAC__StreamDecoderTellCallback. This + * pointer may be \c NULL if not supported by the client. If + * \a seek_callback is not \c NULL then a + * \a tell_callback must also be supplied. + * Alternatively, a dummy tell callback that just + * returns \c FLAC__STREAM_DECODER_TELL_STATUS_UNSUPPORTED + * may also be supplied, all though this is slightly + * less efficient for the decoder. + * \param length_callback See FLAC__StreamDecoderLengthCallback. This + * pointer may be \c NULL if not supported by the client. If + * \a seek_callback is not \c NULL then a + * \a length_callback must also be supplied. + * Alternatively, a dummy length callback that just + * returns \c FLAC__STREAM_DECODER_LENGTH_STATUS_UNSUPPORTED + * may also be supplied, all though this is slightly + * less efficient for the decoder. + * \param eof_callback See FLAC__StreamDecoderEofCallback. This + * pointer may be \c NULL if not supported by the client. If + * \a seek_callback is not \c NULL then a + * \a eof_callback must also be supplied. + * Alternatively, a dummy length callback that just + * returns \c false + * may also be supplied, all though this is slightly + * less efficient for the decoder. + * \param write_callback See FLAC__StreamDecoderWriteCallback. This + * pointer must not be \c NULL. + * \param metadata_callback See FLAC__StreamDecoderMetadataCallback. This + * pointer may be \c NULL if the callback is not + * desired. + * \param error_callback See FLAC__StreamDecoderErrorCallback. This + * pointer must not be \c NULL. + * \param client_data This value will be supplied to callbacks in their + * \a client_data argument. + * \assert + * \code decoder != NULL \endcode + * \retval FLAC__StreamDecoderInitStatus + * \c FLAC__STREAM_DECODER_INIT_STATUS_OK if initialization was successful; + * see FLAC__StreamDecoderInitStatus for the meanings of other return values. + */ +FLAC_API FLAC__StreamDecoderInitStatus FLAC__stream_decoder_init_stream( + FLAC__StreamDecoder *decoder, + FLAC__StreamDecoderReadCallback read_callback, + FLAC__StreamDecoderSeekCallback seek_callback, + FLAC__StreamDecoderTellCallback tell_callback, + FLAC__StreamDecoderLengthCallback length_callback, + FLAC__StreamDecoderEofCallback eof_callback, + FLAC__StreamDecoderWriteCallback write_callback, + FLAC__StreamDecoderMetadataCallback metadata_callback, + FLAC__StreamDecoderErrorCallback error_callback, + void *client_data +); + +/** Initialize the decoder instance to decode Ogg FLAC streams. + * + * This flavor of initialization sets up the decoder to decode from a + * FLAC stream in an Ogg container. I/O is performed via callbacks to the + * client. For decoding from a plain file via filename or open FILE*, + * FLAC__stream_decoder_init_ogg_file() and FLAC__stream_decoder_init_ogg_FILE() + * provide a simpler interface. + * + * This function should be called after FLAC__stream_decoder_new() and + * FLAC__stream_decoder_set_*() but before any of the + * FLAC__stream_decoder_process_*() functions. Will set and return the + * decoder state, which will be FLAC__STREAM_DECODER_SEARCH_FOR_METADATA + * if initialization succeeded. + * + * \note Support for Ogg FLAC in the library is optional. If this + * library has been built without support for Ogg FLAC, this function + * will return \c FLAC__STREAM_DECODER_INIT_STATUS_UNSUPPORTED_CONTAINER. + * + * \param decoder An uninitialized decoder instance. + * \param read_callback See FLAC__StreamDecoderReadCallback. This + * pointer must not be \c NULL. + * \param seek_callback See FLAC__StreamDecoderSeekCallback. This + * pointer may be \c NULL if seeking is not + * supported. If \a seek_callback is not \c NULL then a + * \a tell_callback, \a length_callback, and \a eof_callback must also be supplied. + * Alternatively, a dummy seek callback that just + * returns \c FLAC__STREAM_DECODER_SEEK_STATUS_UNSUPPORTED + * may also be supplied, all though this is slightly + * less efficient for the decoder. + * \param tell_callback See FLAC__StreamDecoderTellCallback. This + * pointer may be \c NULL if not supported by the client. If + * \a seek_callback is not \c NULL then a + * \a tell_callback must also be supplied. + * Alternatively, a dummy tell callback that just + * returns \c FLAC__STREAM_DECODER_TELL_STATUS_UNSUPPORTED + * may also be supplied, all though this is slightly + * less efficient for the decoder. + * \param length_callback See FLAC__StreamDecoderLengthCallback. This + * pointer may be \c NULL if not supported by the client. If + * \a seek_callback is not \c NULL then a + * \a length_callback must also be supplied. + * Alternatively, a dummy length callback that just + * returns \c FLAC__STREAM_DECODER_LENGTH_STATUS_UNSUPPORTED + * may also be supplied, all though this is slightly + * less efficient for the decoder. + * \param eof_callback See FLAC__StreamDecoderEofCallback. This + * pointer may be \c NULL if not supported by the client. If + * \a seek_callback is not \c NULL then a + * \a eof_callback must also be supplied. + * Alternatively, a dummy length callback that just + * returns \c false + * may also be supplied, all though this is slightly + * less efficient for the decoder. + * \param write_callback See FLAC__StreamDecoderWriteCallback. This + * pointer must not be \c NULL. + * \param metadata_callback See FLAC__StreamDecoderMetadataCallback. This + * pointer may be \c NULL if the callback is not + * desired. + * \param error_callback See FLAC__StreamDecoderErrorCallback. This + * pointer must not be \c NULL. + * \param client_data This value will be supplied to callbacks in their + * \a client_data argument. + * \assert + * \code decoder != NULL \endcode + * \retval FLAC__StreamDecoderInitStatus + * \c FLAC__STREAM_DECODER_INIT_STATUS_OK if initialization was successful; + * see FLAC__StreamDecoderInitStatus for the meanings of other return values. + */ +FLAC_API FLAC__StreamDecoderInitStatus FLAC__stream_decoder_init_ogg_stream( + FLAC__StreamDecoder *decoder, + FLAC__StreamDecoderReadCallback read_callback, + FLAC__StreamDecoderSeekCallback seek_callback, + FLAC__StreamDecoderTellCallback tell_callback, + FLAC__StreamDecoderLengthCallback length_callback, + FLAC__StreamDecoderEofCallback eof_callback, + FLAC__StreamDecoderWriteCallback write_callback, + FLAC__StreamDecoderMetadataCallback metadata_callback, + FLAC__StreamDecoderErrorCallback error_callback, + void *client_data +); + +/** Initialize the decoder instance to decode native FLAC files. + * + * This flavor of initialization sets up the decoder to decode from a + * plain native FLAC file. For non-stdio streams, you must use + * FLAC__stream_decoder_init_stream() and provide callbacks for the I/O. + * + * This function should be called after FLAC__stream_decoder_new() and + * FLAC__stream_decoder_set_*() but before any of the + * FLAC__stream_decoder_process_*() functions. Will set and return the + * decoder state, which will be FLAC__STREAM_DECODER_SEARCH_FOR_METADATA + * if initialization succeeded. + * + * \param decoder An uninitialized decoder instance. + * \param file An open FLAC file. The file should have been + * opened with mode \c "rb" and rewound. The file + * becomes owned by the decoder and should not be + * manipulated by the client while decoding. + * Unless \a file is \c stdin, it will be closed + * when FLAC__stream_decoder_finish() is called. + * Note however that seeking will not work when + * decoding from \c stdout since it is not seekable. + * \param write_callback See FLAC__StreamDecoderWriteCallback. This + * pointer must not be \c NULL. + * \param metadata_callback See FLAC__StreamDecoderMetadataCallback. This + * pointer may be \c NULL if the callback is not + * desired. + * \param error_callback See FLAC__StreamDecoderErrorCallback. This + * pointer must not be \c NULL. + * \param client_data This value will be supplied to callbacks in their + * \a client_data argument. + * \assert + * \code decoder != NULL \endcode + * \code file != NULL \endcode + * \retval FLAC__StreamDecoderInitStatus + * \c FLAC__STREAM_DECODER_INIT_STATUS_OK if initialization was successful; + * see FLAC__StreamDecoderInitStatus for the meanings of other return values. + */ +FLAC_API FLAC__StreamDecoderInitStatus FLAC__stream_decoder_init_FILE( + FLAC__StreamDecoder *decoder, + FILE *file, + FLAC__StreamDecoderWriteCallback write_callback, + FLAC__StreamDecoderMetadataCallback metadata_callback, + FLAC__StreamDecoderErrorCallback error_callback, + void *client_data +); + +/** Initialize the decoder instance to decode Ogg FLAC files. + * + * This flavor of initialization sets up the decoder to decode from a + * plain Ogg FLAC file. For non-stdio streams, you must use + * FLAC__stream_decoder_init_ogg_stream() and provide callbacks for the I/O. + * + * This function should be called after FLAC__stream_decoder_new() and + * FLAC__stream_decoder_set_*() but before any of the + * FLAC__stream_decoder_process_*() functions. Will set and return the + * decoder state, which will be FLAC__STREAM_DECODER_SEARCH_FOR_METADATA + * if initialization succeeded. + * + * \note Support for Ogg FLAC in the library is optional. If this + * library has been built without support for Ogg FLAC, this function + * will return \c FLAC__STREAM_DECODER_INIT_STATUS_UNSUPPORTED_CONTAINER. + * + * \param decoder An uninitialized decoder instance. + * \param file An open FLAC file. The file should have been + * opened with mode \c "rb" and rewound. The file + * becomes owned by the decoder and should not be + * manipulated by the client while decoding. + * Unless \a file is \c stdin, it will be closed + * when FLAC__stream_decoder_finish() is called. + * Note however that seeking will not work when + * decoding from \c stdout since it is not seekable. + * \param write_callback See FLAC__StreamDecoderWriteCallback. This + * pointer must not be \c NULL. + * \param metadata_callback See FLAC__StreamDecoderMetadataCallback. This + * pointer may be \c NULL if the callback is not + * desired. + * \param error_callback See FLAC__StreamDecoderErrorCallback. This + * pointer must not be \c NULL. + * \param client_data This value will be supplied to callbacks in their + * \a client_data argument. + * \assert + * \code decoder != NULL \endcode + * \code file != NULL \endcode + * \retval FLAC__StreamDecoderInitStatus + * \c FLAC__STREAM_DECODER_INIT_STATUS_OK if initialization was successful; + * see FLAC__StreamDecoderInitStatus for the meanings of other return values. + */ +FLAC_API FLAC__StreamDecoderInitStatus FLAC__stream_decoder_init_ogg_FILE( + FLAC__StreamDecoder *decoder, + FILE *file, + FLAC__StreamDecoderWriteCallback write_callback, + FLAC__StreamDecoderMetadataCallback metadata_callback, + FLAC__StreamDecoderErrorCallback error_callback, + void *client_data +); + +/** Initialize the decoder instance to decode native FLAC files. + * + * This flavor of initialization sets up the decoder to decode from a plain + * native FLAC file. If POSIX fopen() semantics are not sufficient, (for + * example, with Unicode filenames on Windows), you must use + * FLAC__stream_decoder_init_FILE(), or FLAC__stream_decoder_init_stream() + * and provide callbacks for the I/O. + * + * This function should be called after FLAC__stream_decoder_new() and + * FLAC__stream_decoder_set_*() but before any of the + * FLAC__stream_decoder_process_*() functions. Will set and return the + * decoder state, which will be FLAC__STREAM_DECODER_SEARCH_FOR_METADATA + * if initialization succeeded. + * + * \param decoder An uninitialized decoder instance. + * \param filename The name of the file to decode from. The file will + * be opened with fopen(). Use \c NULL to decode from + * \c stdin. Note that \c stdin is not seekable. + * \param write_callback See FLAC__StreamDecoderWriteCallback. This + * pointer must not be \c NULL. + * \param metadata_callback See FLAC__StreamDecoderMetadataCallback. This + * pointer may be \c NULL if the callback is not + * desired. + * \param error_callback See FLAC__StreamDecoderErrorCallback. This + * pointer must not be \c NULL. + * \param client_data This value will be supplied to callbacks in their + * \a client_data argument. + * \assert + * \code decoder != NULL \endcode + * \retval FLAC__StreamDecoderInitStatus + * \c FLAC__STREAM_DECODER_INIT_STATUS_OK if initialization was successful; + * see FLAC__StreamDecoderInitStatus for the meanings of other return values. + */ +FLAC_API FLAC__StreamDecoderInitStatus FLAC__stream_decoder_init_file( + FLAC__StreamDecoder *decoder, + const char *filename, + FLAC__StreamDecoderWriteCallback write_callback, + FLAC__StreamDecoderMetadataCallback metadata_callback, + FLAC__StreamDecoderErrorCallback error_callback, + void *client_data +); + +/** Initialize the decoder instance to decode Ogg FLAC files. + * + * This flavor of initialization sets up the decoder to decode from a plain + * Ogg FLAC file. If POSIX fopen() semantics are not sufficient, (for + * example, with Unicode filenames on Windows), you must use + * FLAC__stream_decoder_init_ogg_FILE(), or FLAC__stream_decoder_init_ogg_stream() + * and provide callbacks for the I/O. + * + * This function should be called after FLAC__stream_decoder_new() and + * FLAC__stream_decoder_set_*() but before any of the + * FLAC__stream_decoder_process_*() functions. Will set and return the + * decoder state, which will be FLAC__STREAM_DECODER_SEARCH_FOR_METADATA + * if initialization succeeded. + * + * \note Support for Ogg FLAC in the library is optional. If this + * library has been built without support for Ogg FLAC, this function + * will return \c FLAC__STREAM_DECODER_INIT_STATUS_UNSUPPORTED_CONTAINER. + * + * \param decoder An uninitialized decoder instance. + * \param filename The name of the file to decode from. The file will + * be opened with fopen(). Use \c NULL to decode from + * \c stdin. Note that \c stdin is not seekable. + * \param write_callback See FLAC__StreamDecoderWriteCallback. This + * pointer must not be \c NULL. + * \param metadata_callback See FLAC__StreamDecoderMetadataCallback. This + * pointer may be \c NULL if the callback is not + * desired. + * \param error_callback See FLAC__StreamDecoderErrorCallback. This + * pointer must not be \c NULL. + * \param client_data This value will be supplied to callbacks in their + * \a client_data argument. + * \assert + * \code decoder != NULL \endcode + * \retval FLAC__StreamDecoderInitStatus + * \c FLAC__STREAM_DECODER_INIT_STATUS_OK if initialization was successful; + * see FLAC__StreamDecoderInitStatus for the meanings of other return values. + */ +FLAC_API FLAC__StreamDecoderInitStatus FLAC__stream_decoder_init_ogg_file( + FLAC__StreamDecoder *decoder, + const char *filename, + FLAC__StreamDecoderWriteCallback write_callback, + FLAC__StreamDecoderMetadataCallback metadata_callback, + FLAC__StreamDecoderErrorCallback error_callback, + void *client_data +); + +/** Finish the decoding process. + * Flushes the decoding buffer, releases resources, resets the decoder + * settings to their defaults, and returns the decoder state to + * FLAC__STREAM_DECODER_UNINITIALIZED. + * + * In the event of a prematurely-terminated decode, it is not strictly + * necessary to call this immediately before FLAC__stream_decoder_delete() + * but it is good practice to match every FLAC__stream_decoder_init_*() + * with a FLAC__stream_decoder_finish(). + * + * \param decoder An uninitialized decoder instance. + * \assert + * \code decoder != NULL \endcode + * \retval FLAC__bool + * \c false if MD5 checking is on AND a STREAMINFO block was available + * AND the MD5 signature in the STREAMINFO block was non-zero AND the + * signature does not match the one computed by the decoder; else + * \c true. + */ +FLAC_API FLAC__bool FLAC__stream_decoder_finish(FLAC__StreamDecoder *decoder); + +/** Flush the stream input. + * The decoder's input buffer will be cleared and the state set to + * \c FLAC__STREAM_DECODER_SEARCH_FOR_FRAME_SYNC. This will also turn + * off MD5 checking. + * + * \param decoder A decoder instance. + * \assert + * \code decoder != NULL \endcode + * \retval FLAC__bool + * \c true if successful, else \c false if a memory allocation + * error occurs (in which case the state will be set to + * \c FLAC__STREAM_DECODER_MEMORY_ALLOCATION_ERROR). + */ +FLAC_API FLAC__bool FLAC__stream_decoder_flush(FLAC__StreamDecoder *decoder); + +/** Reset the decoding process. + * The decoder's input buffer will be cleared and the state set to + * \c FLAC__STREAM_DECODER_SEARCH_FOR_METADATA. This is similar to + * FLAC__stream_decoder_finish() except that the settings are + * preserved; there is no need to call FLAC__stream_decoder_init_*() + * before decoding again. MD5 checking will be restored to its original + * setting. + * + * If the decoder is seekable, or was initialized with + * FLAC__stream_decoder_init*_FILE() or FLAC__stream_decoder_init*_file(), + * the decoder will also attempt to seek to the beginning of the file. + * If this rewind fails, this function will return \c false. It follows + * that FLAC__stream_decoder_reset() cannot be used when decoding from + * \c stdin. + * + * If the decoder was initialized with FLAC__stream_encoder_init*_stream() + * and is not seekable (i.e. no seek callback was provided or the seek + * callback returns \c FLAC__STREAM_DECODER_SEEK_STATUS_UNSUPPORTED), it + * is the duty of the client to start feeding data from the beginning of + * the stream on the next FLAC__stream_decoder_process() or + * FLAC__stream_decoder_process_interleaved() call. + * + * \param decoder A decoder instance. + * \assert + * \code decoder != NULL \endcode + * \retval FLAC__bool + * \c true if successful, else \c false if a memory allocation occurs + * (in which case the state will be set to + * \c FLAC__STREAM_DECODER_MEMORY_ALLOCATION_ERROR) or a seek error + * occurs (the state will be unchanged). + */ +FLAC_API FLAC__bool FLAC__stream_decoder_reset(FLAC__StreamDecoder *decoder); + +/** Decode one metadata block or audio frame. + * This version instructs the decoder to decode a either a single metadata + * block or a single frame and stop, unless the callbacks return a fatal + * error or the read callback returns + * \c FLAC__STREAM_DECODER_READ_STATUS_END_OF_STREAM. + * + * As the decoder needs more input it will call the read callback. + * Depending on what was decoded, the metadata or write callback will be + * called with the decoded metadata block or audio frame. + * + * Unless there is a fatal read error or end of stream, this function + * will return once one whole frame is decoded. In other words, if the + * stream is not synchronized or points to a corrupt frame header, the + * decoder will continue to try and resync until it gets to a valid + * frame, then decode one frame, then return. If the decoder points to + * a frame whose frame CRC in the frame footer does not match the + * computed frame CRC, this function will issue a + * FLAC__STREAM_DECODER_ERROR_STATUS_FRAME_CRC_MISMATCH error to the + * error callback, and return, having decoded one complete, although + * corrupt, frame. (Such corrupted frames are sent as silence of the + * correct length to the write callback.) + * + * \param decoder An initialized decoder instance. + * \assert + * \code decoder != NULL \endcode + * \retval FLAC__bool + * \c false if any fatal read, write, or memory allocation error + * occurred (meaning decoding must stop), else \c true; for more + * information about the decoder, check the decoder state with + * FLAC__stream_decoder_get_state(). + */ +FLAC_API FLAC__bool FLAC__stream_decoder_process_single(FLAC__StreamDecoder *decoder); + +/** Decode until the end of the metadata. + * This version instructs the decoder to decode from the current position + * and continue until all the metadata has been read, or until the + * callbacks return a fatal error or the read callback returns + * \c FLAC__STREAM_DECODER_READ_STATUS_END_OF_STREAM. + * + * As the decoder needs more input it will call the read callback. + * As each metadata block is decoded, the metadata callback will be called + * with the decoded metadata. + * + * \param decoder An initialized decoder instance. + * \assert + * \code decoder != NULL \endcode + * \retval FLAC__bool + * \c false if any fatal read, write, or memory allocation error + * occurred (meaning decoding must stop), else \c true; for more + * information about the decoder, check the decoder state with + * FLAC__stream_decoder_get_state(). + */ +FLAC_API FLAC__bool FLAC__stream_decoder_process_until_end_of_metadata(FLAC__StreamDecoder *decoder); + +/** Decode until the end of the stream. + * This version instructs the decoder to decode from the current position + * and continue until the end of stream (the read callback returns + * \c FLAC__STREAM_DECODER_READ_STATUS_END_OF_STREAM), or until the + * callbacks return a fatal error. + * + * As the decoder needs more input it will call the read callback. + * As each metadata block and frame is decoded, the metadata or write + * callback will be called with the decoded metadata or frame. + * + * \param decoder An initialized decoder instance. + * \assert + * \code decoder != NULL \endcode + * \retval FLAC__bool + * \c false if any fatal read, write, or memory allocation error + * occurred (meaning decoding must stop), else \c true; for more + * information about the decoder, check the decoder state with + * FLAC__stream_decoder_get_state(). + */ +FLAC_API FLAC__bool FLAC__stream_decoder_process_until_end_of_stream(FLAC__StreamDecoder *decoder); + +/** Skip one audio frame. + * This version instructs the decoder to 'skip' a single frame and stop, + * unless the callbacks return a fatal error or the read callback returns + * \c FLAC__STREAM_DECODER_READ_STATUS_END_OF_STREAM. + * + * The decoding flow is the same as what occurs when + * FLAC__stream_decoder_process_single() is called to process an audio + * frame, except that this function does not decode the parsed data into + * PCM or call the write callback. The integrity of the frame is still + * checked the same way as in the other process functions. + * + * This function will return once one whole frame is skipped, in the + * same way that FLAC__stream_decoder_process_single() will return once + * one whole frame is decoded. + * + * This function can be used in more quickly determining FLAC frame + * boundaries when decoding of the actual data is not needed, for + * example when an application is separating a FLAC stream into frames + * for editing or storing in a container. To do this, the application + * can use FLAC__stream_decoder_skip_single_frame() to quickly advance + * to the next frame, then use + * FLAC__stream_decoder_get_decode_position() to find the new frame + * boundary. + * + * This function should only be called when the stream has advanced + * past all the metadata, otherwise it will return \c false. + * + * \param decoder An initialized decoder instance not in a metadata + * state. + * \assert + * \code decoder != NULL \endcode + * \retval FLAC__bool + * \c false if any fatal read, write, or memory allocation error + * occurred (meaning decoding must stop), or if the decoder + * is in the FLAC__STREAM_DECODER_SEARCH_FOR_METADATA or + * FLAC__STREAM_DECODER_READ_METADATA state, else \c true; for more + * information about the decoder, check the decoder state with + * FLAC__stream_decoder_get_state(). + */ +FLAC_API FLAC__bool FLAC__stream_decoder_skip_single_frame(FLAC__StreamDecoder *decoder); + +/** Flush the input and seek to an absolute sample. + * Decoding will resume at the given sample. Note that because of + * this, the next write callback may contain a partial block. The + * client must support seeking the input or this function will fail + * and return \c false. Furthermore, if the decoder state is + * \c FLAC__STREAM_DECODER_SEEK_ERROR, then the decoder must be flushed + * with FLAC__stream_decoder_flush() or reset with + * FLAC__stream_decoder_reset() before decoding can continue. + * + * \param decoder A decoder instance. + * \param sample The target sample number to seek to. + * \assert + * \code decoder != NULL \endcode + * \retval FLAC__bool + * \c true if successful, else \c false. + */ +FLAC_API FLAC__bool FLAC__stream_decoder_seek_absolute(FLAC__StreamDecoder *decoder, FLAC__uint64 sample); + +/* \} */ + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/core/deps/flac/include/FLAC/stream_encoder.h b/core/deps/flac/include/FLAC/stream_encoder.h new file mode 100644 index 000000000..40a2fd361 --- /dev/null +++ b/core/deps/flac/include/FLAC/stream_encoder.h @@ -0,0 +1,1790 @@ +/* libFLAC - Free Lossless Audio Codec library + * Copyright (C) 2000-2009 Josh Coalson + * Copyright (C) 2011-2016 Xiph.Org Foundation + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * - Neither the name of the Xiph.org Foundation nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef FLAC__STREAM_ENCODER_H +#define FLAC__STREAM_ENCODER_H + +#include /* for FILE */ +#include "export.h" +#include "format.h" +#include "stream_decoder.h" + +#ifdef __cplusplus +extern "C" { +#endif + + +/** \file include/FLAC/stream_encoder.h + * + * \brief + * This module contains the functions which implement the stream + * encoder. + * + * See the detailed documentation in the + * \link flac_stream_encoder stream encoder \endlink module. + */ + +/** \defgroup flac_encoder FLAC/ \*_encoder.h: encoder interfaces + * \ingroup flac + * + * \brief + * This module describes the encoder layers provided by libFLAC. + * + * The stream encoder can be used to encode complete streams either to the + * client via callbacks, or directly to a file, depending on how it is + * initialized. When encoding via callbacks, the client provides a write + * callback which will be called whenever FLAC data is ready to be written. + * If the client also supplies a seek callback, the encoder will also + * automatically handle the writing back of metadata discovered while + * encoding, like stream info, seek points offsets, etc. When encoding to + * a file, the client needs only supply a filename or open \c FILE* and an + * optional progress callback for periodic notification of progress; the + * write and seek callbacks are supplied internally. For more info see the + * \link flac_stream_encoder stream encoder \endlink module. + */ + +/** \defgroup flac_stream_encoder FLAC/stream_encoder.h: stream encoder interface + * \ingroup flac_encoder + * + * \brief + * This module contains the functions which implement the stream + * encoder. + * + * The stream encoder can encode to native FLAC, and optionally Ogg FLAC + * (check FLAC_API_SUPPORTS_OGG_FLAC) streams and files. + * + * The basic usage of this encoder is as follows: + * - The program creates an instance of an encoder using + * FLAC__stream_encoder_new(). + * - The program overrides the default settings using + * FLAC__stream_encoder_set_*() functions. At a minimum, the following + * functions should be called: + * - FLAC__stream_encoder_set_channels() + * - FLAC__stream_encoder_set_bits_per_sample() + * - FLAC__stream_encoder_set_sample_rate() + * - FLAC__stream_encoder_set_ogg_serial_number() (if encoding to Ogg FLAC) + * - FLAC__stream_encoder_set_total_samples_estimate() (if known) + * - If the application wants to control the compression level or set its own + * metadata, then the following should also be called: + * - FLAC__stream_encoder_set_compression_level() + * - FLAC__stream_encoder_set_verify() + * - FLAC__stream_encoder_set_metadata() + * - The rest of the set functions should only be called if the client needs + * exact control over how the audio is compressed; thorough understanding + * of the FLAC format is necessary to achieve good results. + * - The program initializes the instance to validate the settings and + * prepare for encoding using + * - FLAC__stream_encoder_init_stream() or FLAC__stream_encoder_init_FILE() + * or FLAC__stream_encoder_init_file() for native FLAC + * - FLAC__stream_encoder_init_ogg_stream() or FLAC__stream_encoder_init_ogg_FILE() + * or FLAC__stream_encoder_init_ogg_file() for Ogg FLAC + * - The program calls FLAC__stream_encoder_process() or + * FLAC__stream_encoder_process_interleaved() to encode data, which + * subsequently calls the callbacks when there is encoder data ready + * to be written. + * - The program finishes the encoding with FLAC__stream_encoder_finish(), + * which causes the encoder to encode any data still in its input pipe, + * update the metadata with the final encoding statistics if output + * seeking is possible, and finally reset the encoder to the + * uninitialized state. + * - The instance may be used again or deleted with + * FLAC__stream_encoder_delete(). + * + * In more detail, the stream encoder functions similarly to the + * \link flac_stream_decoder stream decoder \endlink, but has fewer + * callbacks and more options. Typically the client will create a new + * instance by calling FLAC__stream_encoder_new(), then set the necessary + * parameters with FLAC__stream_encoder_set_*(), and initialize it by + * calling one of the FLAC__stream_encoder_init_*() functions. + * + * Unlike the decoders, the stream encoder has many options that can + * affect the speed and compression ratio. When setting these parameters + * you should have some basic knowledge of the format (see the + * user-level documentation + * or the formal description). The + * FLAC__stream_encoder_set_*() functions themselves do not validate the + * values as many are interdependent. The FLAC__stream_encoder_init_*() + * functions will do this, so make sure to pay attention to the state + * returned by FLAC__stream_encoder_init_*() to make sure that it is + * FLAC__STREAM_ENCODER_INIT_STATUS_OK. Any parameters that are not set + * before FLAC__stream_encoder_init_*() will take on the defaults from + * the constructor. + * + * There are three initialization functions for native FLAC, one for + * setting up the encoder to encode FLAC data to the client via + * callbacks, and two for encoding directly to a file. + * + * For encoding via callbacks, use FLAC__stream_encoder_init_stream(). + * You must also supply a write callback which will be called anytime + * there is raw encoded data to write. If the client can seek the output + * it is best to also supply seek and tell callbacks, as this allows the + * encoder to go back after encoding is finished to write back + * information that was collected while encoding, like seek point offsets, + * frame sizes, etc. + * + * For encoding directly to a file, use FLAC__stream_encoder_init_FILE() + * or FLAC__stream_encoder_init_file(). Then you must only supply a + * filename or open \c FILE*; the encoder will handle all the callbacks + * internally. You may also supply a progress callback for periodic + * notification of the encoding progress. + * + * There are three similarly-named init functions for encoding to Ogg + * FLAC streams. Check \c FLAC_API_SUPPORTS_OGG_FLAC to find out if the + * library has been built with Ogg support. + * + * The call to FLAC__stream_encoder_init_*() currently will also immediately + * call the write callback several times, once with the \c fLaC signature, + * and once for each encoded metadata block. Note that for Ogg FLAC + * encoding you will usually get at least twice the number of callbacks than + * with native FLAC, one for the Ogg page header and one for the page body. + * + * After initializing the instance, the client may feed audio data to the + * encoder in one of two ways: + * + * - Channel separate, through FLAC__stream_encoder_process() - The client + * will pass an array of pointers to buffers, one for each channel, to + * the encoder, each of the same length. The samples need not be + * block-aligned, but each channel should have the same number of samples. + * - Channel interleaved, through + * FLAC__stream_encoder_process_interleaved() - The client will pass a single + * pointer to data that is channel-interleaved (i.e. channel0_sample0, + * channel1_sample0, ... , channelN_sample0, channel0_sample1, ...). + * Again, the samples need not be block-aligned but they must be + * sample-aligned, i.e. the first value should be channel0_sample0 and + * the last value channelN_sampleM. + * + * Note that for either process call, each sample in the buffers should be a + * signed integer, right-justified to the resolution set by + * FLAC__stream_encoder_set_bits_per_sample(). For example, if the resolution + * is 16 bits per sample, the samples should all be in the range [-32768,32767]. + * + * When the client is finished encoding data, it calls + * FLAC__stream_encoder_finish(), which causes the encoder to encode any + * data still in its input pipe, and call the metadata callback with the + * final encoding statistics. Then the instance may be deleted with + * FLAC__stream_encoder_delete() or initialized again to encode another + * stream. + * + * For programs that write their own metadata, but that do not know the + * actual metadata until after encoding, it is advantageous to instruct + * the encoder to write a PADDING block of the correct size, so that + * instead of rewriting the whole stream after encoding, the program can + * just overwrite the PADDING block. If only the maximum size of the + * metadata is known, the program can write a slightly larger padding + * block, then split it after encoding. + * + * Make sure you understand how lengths are calculated. All FLAC metadata + * blocks have a 4 byte header which contains the type and length. This + * length does not include the 4 bytes of the header. See the format page + * for the specification of metadata blocks and their lengths. + * + * \note + * If you are writing the FLAC data to a file via callbacks, make sure it + * is open for update (e.g. mode "w+" for stdio streams). This is because + * after the first encoding pass, the encoder will try to seek back to the + * beginning of the stream, to the STREAMINFO block, to write some data + * there. (If using FLAC__stream_encoder_init*_file() or + * FLAC__stream_encoder_init*_FILE(), the file is managed internally.) + * + * \note + * The "set" functions may only be called when the encoder is in the + * state FLAC__STREAM_ENCODER_UNINITIALIZED, i.e. after + * FLAC__stream_encoder_new() or FLAC__stream_encoder_finish(), but + * before FLAC__stream_encoder_init_*(). If this is the case they will + * return \c true, otherwise \c false. + * + * \note + * FLAC__stream_encoder_finish() resets all settings to the constructor + * defaults. + * + * \{ + */ + + +/** State values for a FLAC__StreamEncoder. + * + * The encoder's state can be obtained by calling FLAC__stream_encoder_get_state(). + * + * If the encoder gets into any other state besides \c FLAC__STREAM_ENCODER_OK + * or \c FLAC__STREAM_ENCODER_UNINITIALIZED, it becomes invalid for encoding and + * must be deleted with FLAC__stream_encoder_delete(). + */ +typedef enum { + + FLAC__STREAM_ENCODER_OK = 0, + /**< The encoder is in the normal OK state and samples can be processed. */ + + FLAC__STREAM_ENCODER_UNINITIALIZED, + /**< The encoder is in the uninitialized state; one of the + * FLAC__stream_encoder_init_*() functions must be called before samples + * can be processed. + */ + + FLAC__STREAM_ENCODER_OGG_ERROR, + /**< An error occurred in the underlying Ogg layer. */ + + FLAC__STREAM_ENCODER_VERIFY_DECODER_ERROR, + /**< An error occurred in the underlying verify stream decoder; + * check FLAC__stream_encoder_get_verify_decoder_state(). + */ + + FLAC__STREAM_ENCODER_VERIFY_MISMATCH_IN_AUDIO_DATA, + /**< The verify decoder detected a mismatch between the original + * audio signal and the decoded audio signal. + */ + + FLAC__STREAM_ENCODER_CLIENT_ERROR, + /**< One of the callbacks returned a fatal error. */ + + FLAC__STREAM_ENCODER_IO_ERROR, + /**< An I/O error occurred while opening/reading/writing a file. + * Check \c errno. + */ + + FLAC__STREAM_ENCODER_FRAMING_ERROR, + /**< An error occurred while writing the stream; usually, the + * write_callback returned an error. + */ + + FLAC__STREAM_ENCODER_MEMORY_ALLOCATION_ERROR + /**< Memory allocation failed. */ + +} FLAC__StreamEncoderState; + +/** Maps a FLAC__StreamEncoderState to a C string. + * + * Using a FLAC__StreamEncoderState as the index to this array + * will give the string equivalent. The contents should not be modified. + */ +extern FLAC_API const char * const FLAC__StreamEncoderStateString[]; + + +/** Possible return values for the FLAC__stream_encoder_init_*() functions. + */ +typedef enum { + + FLAC__STREAM_ENCODER_INIT_STATUS_OK = 0, + /**< Initialization was successful. */ + + FLAC__STREAM_ENCODER_INIT_STATUS_ENCODER_ERROR, + /**< General failure to set up encoder; call FLAC__stream_encoder_get_state() for cause. */ + + FLAC__STREAM_ENCODER_INIT_STATUS_UNSUPPORTED_CONTAINER, + /**< The library was not compiled with support for the given container + * format. + */ + + FLAC__STREAM_ENCODER_INIT_STATUS_INVALID_CALLBACKS, + /**< A required callback was not supplied. */ + + FLAC__STREAM_ENCODER_INIT_STATUS_INVALID_NUMBER_OF_CHANNELS, + /**< The encoder has an invalid setting for number of channels. */ + + FLAC__STREAM_ENCODER_INIT_STATUS_INVALID_BITS_PER_SAMPLE, + /**< The encoder has an invalid setting for bits-per-sample. + * FLAC supports 4-32 bps but the reference encoder currently supports + * only up to 24 bps. + */ + + FLAC__STREAM_ENCODER_INIT_STATUS_INVALID_SAMPLE_RATE, + /**< The encoder has an invalid setting for the input sample rate. */ + + FLAC__STREAM_ENCODER_INIT_STATUS_INVALID_BLOCK_SIZE, + /**< The encoder has an invalid setting for the block size. */ + + FLAC__STREAM_ENCODER_INIT_STATUS_INVALID_MAX_LPC_ORDER, + /**< The encoder has an invalid setting for the maximum LPC order. */ + + FLAC__STREAM_ENCODER_INIT_STATUS_INVALID_QLP_COEFF_PRECISION, + /**< The encoder has an invalid setting for the precision of the quantized linear predictor coefficients. */ + + FLAC__STREAM_ENCODER_INIT_STATUS_BLOCK_SIZE_TOO_SMALL_FOR_LPC_ORDER, + /**< The specified block size is less than the maximum LPC order. */ + + FLAC__STREAM_ENCODER_INIT_STATUS_NOT_STREAMABLE, + /**< The encoder is bound to the Subset but other settings violate it. */ + + FLAC__STREAM_ENCODER_INIT_STATUS_INVALID_METADATA, + /**< The metadata input to the encoder is invalid, in one of the following ways: + * - FLAC__stream_encoder_set_metadata() was called with a null pointer but a block count > 0 + * - One of the metadata blocks contains an undefined type + * - It contains an illegal CUESHEET as checked by FLAC__format_cuesheet_is_legal() + * - It contains an illegal SEEKTABLE as checked by FLAC__format_seektable_is_legal() + * - It contains more than one SEEKTABLE block or more than one VORBIS_COMMENT block + */ + + FLAC__STREAM_ENCODER_INIT_STATUS_ALREADY_INITIALIZED + /**< FLAC__stream_encoder_init_*() was called when the encoder was + * already initialized, usually because + * FLAC__stream_encoder_finish() was not called. + */ + +} FLAC__StreamEncoderInitStatus; + +/** Maps a FLAC__StreamEncoderInitStatus to a C string. + * + * Using a FLAC__StreamEncoderInitStatus as the index to this array + * will give the string equivalent. The contents should not be modified. + */ +extern FLAC_API const char * const FLAC__StreamEncoderInitStatusString[]; + + +/** Return values for the FLAC__StreamEncoder read callback. + */ +typedef enum { + + FLAC__STREAM_ENCODER_READ_STATUS_CONTINUE, + /**< The read was OK and decoding can continue. */ + + FLAC__STREAM_ENCODER_READ_STATUS_END_OF_STREAM, + /**< The read was attempted at the end of the stream. */ + + FLAC__STREAM_ENCODER_READ_STATUS_ABORT, + /**< An unrecoverable error occurred. */ + + FLAC__STREAM_ENCODER_READ_STATUS_UNSUPPORTED + /**< Client does not support reading back from the output. */ + +} FLAC__StreamEncoderReadStatus; + +/** Maps a FLAC__StreamEncoderReadStatus to a C string. + * + * Using a FLAC__StreamEncoderReadStatus as the index to this array + * will give the string equivalent. The contents should not be modified. + */ +extern FLAC_API const char * const FLAC__StreamEncoderReadStatusString[]; + + +/** Return values for the FLAC__StreamEncoder write callback. + */ +typedef enum { + + FLAC__STREAM_ENCODER_WRITE_STATUS_OK = 0, + /**< The write was OK and encoding can continue. */ + + FLAC__STREAM_ENCODER_WRITE_STATUS_FATAL_ERROR + /**< An unrecoverable error occurred. The encoder will return from the process call. */ + +} FLAC__StreamEncoderWriteStatus; + +/** Maps a FLAC__StreamEncoderWriteStatus to a C string. + * + * Using a FLAC__StreamEncoderWriteStatus as the index to this array + * will give the string equivalent. The contents should not be modified. + */ +extern FLAC_API const char * const FLAC__StreamEncoderWriteStatusString[]; + + +/** Return values for the FLAC__StreamEncoder seek callback. + */ +typedef enum { + + FLAC__STREAM_ENCODER_SEEK_STATUS_OK, + /**< The seek was OK and encoding can continue. */ + + FLAC__STREAM_ENCODER_SEEK_STATUS_ERROR, + /**< An unrecoverable error occurred. */ + + FLAC__STREAM_ENCODER_SEEK_STATUS_UNSUPPORTED + /**< Client does not support seeking. */ + +} FLAC__StreamEncoderSeekStatus; + +/** Maps a FLAC__StreamEncoderSeekStatus to a C string. + * + * Using a FLAC__StreamEncoderSeekStatus as the index to this array + * will give the string equivalent. The contents should not be modified. + */ +extern FLAC_API const char * const FLAC__StreamEncoderSeekStatusString[]; + + +/** Return values for the FLAC__StreamEncoder tell callback. + */ +typedef enum { + + FLAC__STREAM_ENCODER_TELL_STATUS_OK, + /**< The tell was OK and encoding can continue. */ + + FLAC__STREAM_ENCODER_TELL_STATUS_ERROR, + /**< An unrecoverable error occurred. */ + + FLAC__STREAM_ENCODER_TELL_STATUS_UNSUPPORTED + /**< Client does not support seeking. */ + +} FLAC__StreamEncoderTellStatus; + +/** Maps a FLAC__StreamEncoderTellStatus to a C string. + * + * Using a FLAC__StreamEncoderTellStatus as the index to this array + * will give the string equivalent. The contents should not be modified. + */ +extern FLAC_API const char * const FLAC__StreamEncoderTellStatusString[]; + + +/*********************************************************************** + * + * class FLAC__StreamEncoder + * + ***********************************************************************/ + +struct FLAC__StreamEncoderProtected; +struct FLAC__StreamEncoderPrivate; +/** The opaque structure definition for the stream encoder type. + * See the \link flac_stream_encoder stream encoder module \endlink + * for a detailed description. + */ +typedef struct { + struct FLAC__StreamEncoderProtected *protected_; /* avoid the C++ keyword 'protected' */ + struct FLAC__StreamEncoderPrivate *private_; /* avoid the C++ keyword 'private' */ +} FLAC__StreamEncoder; + +/** Signature for the read callback. + * + * A function pointer matching this signature must be passed to + * FLAC__stream_encoder_init_ogg_stream() if seeking is supported. + * The supplied function will be called when the encoder needs to read back + * encoded data. This happens during the metadata callback, when the encoder + * has to read, modify, and rewrite the metadata (e.g. seekpoints) gathered + * while encoding. The address of the buffer to be filled is supplied, along + * with the number of bytes the buffer can hold. The callback may choose to + * supply less data and modify the byte count but must be careful not to + * overflow the buffer. The callback then returns a status code chosen from + * FLAC__StreamEncoderReadStatus. + * + * Here is an example of a read callback for stdio streams: + * \code + * FLAC__StreamEncoderReadStatus read_cb(const FLAC__StreamEncoder *encoder, FLAC__byte buffer[], size_t *bytes, void *client_data) + * { + * FILE *file = ((MyClientData*)client_data)->file; + * if(*bytes > 0) { + * *bytes = fread(buffer, sizeof(FLAC__byte), *bytes, file); + * if(ferror(file)) + * return FLAC__STREAM_ENCODER_READ_STATUS_ABORT; + * else if(*bytes == 0) + * return FLAC__STREAM_ENCODER_READ_STATUS_END_OF_STREAM; + * else + * return FLAC__STREAM_ENCODER_READ_STATUS_CONTINUE; + * } + * else + * return FLAC__STREAM_ENCODER_READ_STATUS_ABORT; + * } + * \endcode + * + * \note In general, FLAC__StreamEncoder functions which change the + * state should not be called on the \a encoder while in the callback. + * + * \param encoder The encoder instance calling the callback. + * \param buffer A pointer to a location for the callee to store + * data to be encoded. + * \param bytes A pointer to the size of the buffer. On entry + * to the callback, it contains the maximum number + * of bytes that may be stored in \a buffer. The + * callee must set it to the actual number of bytes + * stored (0 in case of error or end-of-stream) before + * returning. + * \param client_data The callee's client data set through + * FLAC__stream_encoder_set_client_data(). + * \retval FLAC__StreamEncoderReadStatus + * The callee's return status. + */ +typedef FLAC__StreamEncoderReadStatus (*FLAC__StreamEncoderReadCallback)(const FLAC__StreamEncoder *encoder, FLAC__byte buffer[], size_t *bytes, void *client_data); + +/** Signature for the write callback. + * + * A function pointer matching this signature must be passed to + * FLAC__stream_encoder_init*_stream(). The supplied function will be called + * by the encoder anytime there is raw encoded data ready to write. It may + * include metadata mixed with encoded audio frames and the data is not + * guaranteed to be aligned on frame or metadata block boundaries. + * + * The only duty of the callback is to write out the \a bytes worth of data + * in \a buffer to the current position in the output stream. The arguments + * \a samples and \a current_frame are purely informational. If \a samples + * is greater than \c 0, then \a current_frame will hold the current frame + * number that is being written; otherwise it indicates that the write + * callback is being called to write metadata. + * + * \note + * Unlike when writing to native FLAC, when writing to Ogg FLAC the + * write callback will be called twice when writing each audio + * frame; once for the page header, and once for the page body. + * When writing the page header, the \a samples argument to the + * write callback will be \c 0. + * + * \note In general, FLAC__StreamEncoder functions which change the + * state should not be called on the \a encoder while in the callback. + * + * \param encoder The encoder instance calling the callback. + * \param buffer An array of encoded data of length \a bytes. + * \param bytes The byte length of \a buffer. + * \param samples The number of samples encoded by \a buffer. + * \c 0 has a special meaning; see above. + * \param current_frame The number of the current frame being encoded. + * \param client_data The callee's client data set through + * FLAC__stream_encoder_init_*(). + * \retval FLAC__StreamEncoderWriteStatus + * The callee's return status. + */ +typedef FLAC__StreamEncoderWriteStatus (*FLAC__StreamEncoderWriteCallback)(const FLAC__StreamEncoder *encoder, const FLAC__byte buffer[], size_t bytes, unsigned samples, unsigned current_frame, void *client_data); + +/** Signature for the seek callback. + * + * A function pointer matching this signature may be passed to + * FLAC__stream_encoder_init*_stream(). The supplied function will be called + * when the encoder needs to seek the output stream. The encoder will pass + * the absolute byte offset to seek to, 0 meaning the beginning of the stream. + * + * Here is an example of a seek callback for stdio streams: + * \code + * FLAC__StreamEncoderSeekStatus seek_cb(const FLAC__StreamEncoder *encoder, FLAC__uint64 absolute_byte_offset, void *client_data) + * { + * FILE *file = ((MyClientData*)client_data)->file; + * if(file == stdin) + * return FLAC__STREAM_ENCODER_SEEK_STATUS_UNSUPPORTED; + * else if(fseeko(file, (off_t)absolute_byte_offset, SEEK_SET) < 0) + * return FLAC__STREAM_ENCODER_SEEK_STATUS_ERROR; + * else + * return FLAC__STREAM_ENCODER_SEEK_STATUS_OK; + * } + * \endcode + * + * \note In general, FLAC__StreamEncoder functions which change the + * state should not be called on the \a encoder while in the callback. + * + * \param encoder The encoder instance calling the callback. + * \param absolute_byte_offset The offset from the beginning of the stream + * to seek to. + * \param client_data The callee's client data set through + * FLAC__stream_encoder_init_*(). + * \retval FLAC__StreamEncoderSeekStatus + * The callee's return status. + */ +typedef FLAC__StreamEncoderSeekStatus (*FLAC__StreamEncoderSeekCallback)(const FLAC__StreamEncoder *encoder, FLAC__uint64 absolute_byte_offset, void *client_data); + +/** Signature for the tell callback. + * + * A function pointer matching this signature may be passed to + * FLAC__stream_encoder_init*_stream(). The supplied function will be called + * when the encoder needs to know the current position of the output stream. + * + * \warning + * The callback must return the true current byte offset of the output to + * which the encoder is writing. If you are buffering the output, make + * sure and take this into account. If you are writing directly to a + * FILE* from your write callback, ftell() is sufficient. If you are + * writing directly to a file descriptor from your write callback, you + * can use lseek(fd, SEEK_CUR, 0). The encoder may later seek back to + * these points to rewrite metadata after encoding. + * + * Here is an example of a tell callback for stdio streams: + * \code + * FLAC__StreamEncoderTellStatus tell_cb(const FLAC__StreamEncoder *encoder, FLAC__uint64 *absolute_byte_offset, void *client_data) + * { + * FILE *file = ((MyClientData*)client_data)->file; + * off_t pos; + * if(file == stdin) + * return FLAC__STREAM_ENCODER_TELL_STATUS_UNSUPPORTED; + * else if((pos = ftello(file)) < 0) + * return FLAC__STREAM_ENCODER_TELL_STATUS_ERROR; + * else { + * *absolute_byte_offset = (FLAC__uint64)pos; + * return FLAC__STREAM_ENCODER_TELL_STATUS_OK; + * } + * } + * \endcode + * + * \note In general, FLAC__StreamEncoder functions which change the + * state should not be called on the \a encoder while in the callback. + * + * \param encoder The encoder instance calling the callback. + * \param absolute_byte_offset The address at which to store the current + * position of the output. + * \param client_data The callee's client data set through + * FLAC__stream_encoder_init_*(). + * \retval FLAC__StreamEncoderTellStatus + * The callee's return status. + */ +typedef FLAC__StreamEncoderTellStatus (*FLAC__StreamEncoderTellCallback)(const FLAC__StreamEncoder *encoder, FLAC__uint64 *absolute_byte_offset, void *client_data); + +/** Signature for the metadata callback. + * + * A function pointer matching this signature may be passed to + * FLAC__stream_encoder_init*_stream(). The supplied function will be called + * once at the end of encoding with the populated STREAMINFO structure. This + * is so the client can seek back to the beginning of the file and write the + * STREAMINFO block with the correct statistics after encoding (like + * minimum/maximum frame size and total samples). + * + * \note In general, FLAC__StreamEncoder functions which change the + * state should not be called on the \a encoder while in the callback. + * + * \param encoder The encoder instance calling the callback. + * \param metadata The final populated STREAMINFO block. + * \param client_data The callee's client data set through + * FLAC__stream_encoder_init_*(). + */ +typedef void (*FLAC__StreamEncoderMetadataCallback)(const FLAC__StreamEncoder *encoder, const FLAC__StreamMetadata *metadata, void *client_data); + +/** Signature for the progress callback. + * + * A function pointer matching this signature may be passed to + * FLAC__stream_encoder_init*_file() or FLAC__stream_encoder_init*_FILE(). + * The supplied function will be called when the encoder has finished + * writing a frame. The \c total_frames_estimate argument to the + * callback will be based on the value from + * FLAC__stream_encoder_set_total_samples_estimate(). + * + * \note In general, FLAC__StreamEncoder functions which change the + * state should not be called on the \a encoder while in the callback. + * + * \param encoder The encoder instance calling the callback. + * \param bytes_written Bytes written so far. + * \param samples_written Samples written so far. + * \param frames_written Frames written so far. + * \param total_frames_estimate The estimate of the total number of + * frames to be written. + * \param client_data The callee's client data set through + * FLAC__stream_encoder_init_*(). + */ +typedef void (*FLAC__StreamEncoderProgressCallback)(const FLAC__StreamEncoder *encoder, FLAC__uint64 bytes_written, FLAC__uint64 samples_written, unsigned frames_written, unsigned total_frames_estimate, void *client_data); + + +/*********************************************************************** + * + * Class constructor/destructor + * + ***********************************************************************/ + +/** Create a new stream encoder instance. The instance is created with + * default settings; see the individual FLAC__stream_encoder_set_*() + * functions for each setting's default. + * + * \retval FLAC__StreamEncoder* + * \c NULL if there was an error allocating memory, else the new instance. + */ +FLAC_API FLAC__StreamEncoder *FLAC__stream_encoder_new(void); + +/** Free an encoder instance. Deletes the object pointed to by \a encoder. + * + * \param encoder A pointer to an existing encoder. + * \assert + * \code encoder != NULL \endcode + */ +FLAC_API void FLAC__stream_encoder_delete(FLAC__StreamEncoder *encoder); + + +/*********************************************************************** + * + * Public class method prototypes + * + ***********************************************************************/ + +/** Set the serial number for the FLAC stream to use in the Ogg container. + * + * \note + * This does not need to be set for native FLAC encoding. + * + * \note + * It is recommended to set a serial number explicitly as the default of '0' + * may collide with other streams. + * + * \default \c 0 + * \param encoder An encoder instance to set. + * \param serial_number See above. + * \assert + * \code encoder != NULL \endcode + * \retval FLAC__bool + * \c false if the encoder is already initialized, else \c true. + */ +FLAC_API FLAC__bool FLAC__stream_encoder_set_ogg_serial_number(FLAC__StreamEncoder *encoder, long serial_number); + +/** Set the "verify" flag. If \c true, the encoder will verify it's own + * encoded output by feeding it through an internal decoder and comparing + * the original signal against the decoded signal. If a mismatch occurs, + * the process call will return \c false. Note that this will slow the + * encoding process by the extra time required for decoding and comparison. + * + * \default \c false + * \param encoder An encoder instance to set. + * \param value Flag value (see above). + * \assert + * \code encoder != NULL \endcode + * \retval FLAC__bool + * \c false if the encoder is already initialized, else \c true. + */ +FLAC_API FLAC__bool FLAC__stream_encoder_set_verify(FLAC__StreamEncoder *encoder, FLAC__bool value); + +/** Set the Subset flag. If \c true, + * the encoder will comply with the Subset and will check the + * settings during FLAC__stream_encoder_init_*() to see if all settings + * comply. If \c false, the settings may take advantage of the full + * range that the format allows. + * + * Make sure you know what it entails before setting this to \c false. + * + * \default \c true + * \param encoder An encoder instance to set. + * \param value Flag value (see above). + * \assert + * \code encoder != NULL \endcode + * \retval FLAC__bool + * \c false if the encoder is already initialized, else \c true. + */ +FLAC_API FLAC__bool FLAC__stream_encoder_set_streamable_subset(FLAC__StreamEncoder *encoder, FLAC__bool value); + +/** Set the number of channels to be encoded. + * + * \default \c 2 + * \param encoder An encoder instance to set. + * \param value See above. + * \assert + * \code encoder != NULL \endcode + * \retval FLAC__bool + * \c false if the encoder is already initialized, else \c true. + */ +FLAC_API FLAC__bool FLAC__stream_encoder_set_channels(FLAC__StreamEncoder *encoder, unsigned value); + +/** Set the sample resolution of the input to be encoded. + * + * \warning + * Do not feed the encoder data that is wider than the value you + * set here or you will generate an invalid stream. + * + * \default \c 16 + * \param encoder An encoder instance to set. + * \param value See above. + * \assert + * \code encoder != NULL \endcode + * \retval FLAC__bool + * \c false if the encoder is already initialized, else \c true. + */ +FLAC_API FLAC__bool FLAC__stream_encoder_set_bits_per_sample(FLAC__StreamEncoder *encoder, unsigned value); + +/** Set the sample rate (in Hz) of the input to be encoded. + * + * \default \c 44100 + * \param encoder An encoder instance to set. + * \param value See above. + * \assert + * \code encoder != NULL \endcode + * \retval FLAC__bool + * \c false if the encoder is already initialized, else \c true. + */ +FLAC_API FLAC__bool FLAC__stream_encoder_set_sample_rate(FLAC__StreamEncoder *encoder, unsigned value); + +/** Set the compression level + * + * The compression level is roughly proportional to the amount of effort + * the encoder expends to compress the file. A higher level usually + * means more computation but higher compression. The default level is + * suitable for most applications. + * + * Currently the levels range from \c 0 (fastest, least compression) to + * \c 8 (slowest, most compression). A value larger than \c 8 will be + * treated as \c 8. + * + * This function automatically calls the following other \c _set_ + * functions with appropriate values, so the client does not need to + * unless it specifically wants to override them: + * - FLAC__stream_encoder_set_do_mid_side_stereo() + * - FLAC__stream_encoder_set_loose_mid_side_stereo() + * - FLAC__stream_encoder_set_apodization() + * - FLAC__stream_encoder_set_max_lpc_order() + * - FLAC__stream_encoder_set_qlp_coeff_precision() + * - FLAC__stream_encoder_set_do_qlp_coeff_prec_search() + * - FLAC__stream_encoder_set_do_escape_coding() + * - FLAC__stream_encoder_set_do_exhaustive_model_search() + * - FLAC__stream_encoder_set_min_residual_partition_order() + * - FLAC__stream_encoder_set_max_residual_partition_order() + * - FLAC__stream_encoder_set_rice_parameter_search_dist() + * + * The actual values set for each level are: + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + *
leveldo mid-side stereoloose mid-side stereoapodizationmax lpc orderqlp coeff precisionqlp coeff prec searchescape codingexhaustive model searchmin residual partition ordermax residual partition orderrice parameter search dist
0 false false tukey(0.5) 0 0 false false false 0 3 0
1 true true tukey(0.5) 0 0 false false false 0 3 0
2 true false tukey(0.5) 0 0 false false false 0 3 0
3 false false tukey(0.5) 6 0 false false false 0 4 0
4 true true tukey(0.5) 8 0 false false false 0 4 0
5 true false tukey(0.5) 8 0 false false false 0 5 0
6 true false tukey(0.5);partial_tukey(2) 8 0 false false false 0 6 0
7 true false tukey(0.5);partial_tukey(2) 12 0 false false false 0 6 0
8 true false tukey(0.5);partial_tukey(2);punchout_tukey(3) 12 0 false false false 0 6 0
+ * + * \default \c 5 + * \param encoder An encoder instance to set. + * \param value See above. + * \assert + * \code encoder != NULL \endcode + * \retval FLAC__bool + * \c false if the encoder is already initialized, else \c true. + */ +FLAC_API FLAC__bool FLAC__stream_encoder_set_compression_level(FLAC__StreamEncoder *encoder, unsigned value); + +/** Set the blocksize to use while encoding. + * + * The number of samples to use per frame. Use \c 0 to let the encoder + * estimate a blocksize; this is usually best. + * + * \default \c 0 + * \param encoder An encoder instance to set. + * \param value See above. + * \assert + * \code encoder != NULL \endcode + * \retval FLAC__bool + * \c false if the encoder is already initialized, else \c true. + */ +FLAC_API FLAC__bool FLAC__stream_encoder_set_blocksize(FLAC__StreamEncoder *encoder, unsigned value); + +/** Set to \c true to enable mid-side encoding on stereo input. The + * number of channels must be 2 for this to have any effect. Set to + * \c false to use only independent channel coding. + * + * \default \c false + * \param encoder An encoder instance to set. + * \param value Flag value (see above). + * \assert + * \code encoder != NULL \endcode + * \retval FLAC__bool + * \c false if the encoder is already initialized, else \c true. + */ +FLAC_API FLAC__bool FLAC__stream_encoder_set_do_mid_side_stereo(FLAC__StreamEncoder *encoder, FLAC__bool value); + +/** Set to \c true to enable adaptive switching between mid-side and + * left-right encoding on stereo input. Set to \c false to use + * exhaustive searching. Setting this to \c true requires + * FLAC__stream_encoder_set_do_mid_side_stereo() to also be set to + * \c true in order to have any effect. + * + * \default \c false + * \param encoder An encoder instance to set. + * \param value Flag value (see above). + * \assert + * \code encoder != NULL \endcode + * \retval FLAC__bool + * \c false if the encoder is already initialized, else \c true. + */ +FLAC_API FLAC__bool FLAC__stream_encoder_set_loose_mid_side_stereo(FLAC__StreamEncoder *encoder, FLAC__bool value); + +/** Sets the apodization function(s) the encoder will use when windowing + * audio data for LPC analysis. + * + * The \a specification is a plain ASCII string which specifies exactly + * which functions to use. There may be more than one (up to 32), + * separated by \c ';' characters. Some functions take one or more + * comma-separated arguments in parentheses. + * + * The available functions are \c bartlett, \c bartlett_hann, + * \c blackman, \c blackman_harris_4term_92db, \c connes, \c flattop, + * \c gauss(STDDEV), \c hamming, \c hann, \c kaiser_bessel, \c nuttall, + * \c rectangle, \c triangle, \c tukey(P), \c partial_tukey(n[/ov[/P]]), + * \c punchout_tukey(n[/ov[/P]]), \c welch. + * + * For \c gauss(STDDEV), STDDEV specifies the standard deviation + * (0blocksize / (2 ^ order). + * + * Set both min and max values to \c 0 to force a single context, + * whose Rice parameter is based on the residual signal variance. + * Otherwise, set a min and max order, and the encoder will search + * all orders, using the mean of each context for its Rice parameter, + * and use the best. + * + * \default \c 0 + * \param encoder An encoder instance to set. + * \param value See above. + * \assert + * \code encoder != NULL \endcode + * \retval FLAC__bool + * \c false if the encoder is already initialized, else \c true. + */ +FLAC_API FLAC__bool FLAC__stream_encoder_set_min_residual_partition_order(FLAC__StreamEncoder *encoder, unsigned value); + +/** Set the maximum partition order to search when coding the residual. + * This is used in tandem with + * FLAC__stream_encoder_set_min_residual_partition_order(). + * + * The partition order determines the context size in the residual. + * The context size will be approximately blocksize / (2 ^ order). + * + * Set both min and max values to \c 0 to force a single context, + * whose Rice parameter is based on the residual signal variance. + * Otherwise, set a min and max order, and the encoder will search + * all orders, using the mean of each context for its Rice parameter, + * and use the best. + * + * \default \c 0 + * \param encoder An encoder instance to set. + * \param value See above. + * \assert + * \code encoder != NULL \endcode + * \retval FLAC__bool + * \c false if the encoder is already initialized, else \c true. + */ +FLAC_API FLAC__bool FLAC__stream_encoder_set_max_residual_partition_order(FLAC__StreamEncoder *encoder, unsigned value); + +/** Deprecated. Setting this value has no effect. + * + * \default \c 0 + * \param encoder An encoder instance to set. + * \param value See above. + * \assert + * \code encoder != NULL \endcode + * \retval FLAC__bool + * \c false if the encoder is already initialized, else \c true. + */ +FLAC_API FLAC__bool FLAC__stream_encoder_set_rice_parameter_search_dist(FLAC__StreamEncoder *encoder, unsigned value); + +/** Set an estimate of the total samples that will be encoded. + * This is merely an estimate and may be set to \c 0 if unknown. + * This value will be written to the STREAMINFO block before encoding, + * and can remove the need for the caller to rewrite the value later + * if the value is known before encoding. + * + * \default \c 0 + * \param encoder An encoder instance to set. + * \param value See above. + * \assert + * \code encoder != NULL \endcode + * \retval FLAC__bool + * \c false if the encoder is already initialized, else \c true. + */ +FLAC_API FLAC__bool FLAC__stream_encoder_set_total_samples_estimate(FLAC__StreamEncoder *encoder, FLAC__uint64 value); + +/** Set the metadata blocks to be emitted to the stream before encoding. + * A value of \c NULL, \c 0 implies no metadata; otherwise, supply an + * array of pointers to metadata blocks. The array is non-const since + * the encoder may need to change the \a is_last flag inside them, and + * in some cases update seek point offsets. Otherwise, the encoder will + * not modify or free the blocks. It is up to the caller to free the + * metadata blocks after encoding finishes. + * + * \note + * The encoder stores only copies of the pointers in the \a metadata array; + * the metadata blocks themselves must survive at least until after + * FLAC__stream_encoder_finish() returns. Do not free the blocks until then. + * + * \note + * The STREAMINFO block is always written and no STREAMINFO block may + * occur in the supplied array. + * + * \note + * By default the encoder does not create a SEEKTABLE. If one is supplied + * in the \a metadata array, but the client has specified that it does not + * support seeking, then the SEEKTABLE will be written verbatim. However + * by itself this is not very useful as the client will not know the stream + * offsets for the seekpoints ahead of time. In order to get a proper + * seektable the client must support seeking. See next note. + * + * \note + * SEEKTABLE blocks are handled specially. Since you will not know + * the values for the seek point stream offsets, you should pass in + * a SEEKTABLE 'template', that is, a SEEKTABLE object with the + * required sample numbers (or placeholder points), with \c 0 for the + * \a frame_samples and \a stream_offset fields for each point. If the + * client has specified that it supports seeking by providing a seek + * callback to FLAC__stream_encoder_init_stream() or both seek AND read + * callback to FLAC__stream_encoder_init_ogg_stream() (or by using + * FLAC__stream_encoder_init*_file() or FLAC__stream_encoder_init*_FILE()), + * then while it is encoding the encoder will fill the stream offsets in + * for you and when encoding is finished, it will seek back and write the + * real values into the SEEKTABLE block in the stream. There are helper + * routines for manipulating seektable template blocks; see metadata.h: + * FLAC__metadata_object_seektable_template_*(). If the client does + * not support seeking, the SEEKTABLE will have inaccurate offsets which + * will slow down or remove the ability to seek in the FLAC stream. + * + * \note + * The encoder instance \b will modify the first \c SEEKTABLE block + * as it transforms the template to a valid seektable while encoding, + * but it is still up to the caller to free all metadata blocks after + * encoding. + * + * \note + * A VORBIS_COMMENT block may be supplied. The vendor string in it + * will be ignored. libFLAC will use it's own vendor string. libFLAC + * will not modify the passed-in VORBIS_COMMENT's vendor string, it + * will simply write it's own into the stream. If no VORBIS_COMMENT + * block is present in the \a metadata array, libFLAC will write an + * empty one, containing only the vendor string. + * + * \note The Ogg FLAC mapping requires that the VORBIS_COMMENT block be + * the second metadata block of the stream. The encoder already supplies + * the STREAMINFO block automatically. If \a metadata does not contain a + * VORBIS_COMMENT block, the encoder will supply that too. Otherwise, if + * \a metadata does contain a VORBIS_COMMENT block and it is not the + * first, the init function will reorder \a metadata by moving the + * VORBIS_COMMENT block to the front; the relative ordering of the other + * blocks will remain as they were. + * + * \note The Ogg FLAC mapping limits the number of metadata blocks per + * stream to \c 65535. If \a num_blocks exceeds this the function will + * return \c false. + * + * \default \c NULL, 0 + * \param encoder An encoder instance to set. + * \param metadata See above. + * \param num_blocks See above. + * \assert + * \code encoder != NULL \endcode + * \retval FLAC__bool + * \c false if the encoder is already initialized, else \c true. + * \c false if the encoder is already initialized, or if + * \a num_blocks > 65535 if encoding to Ogg FLAC, else \c true. + */ +FLAC_API FLAC__bool FLAC__stream_encoder_set_metadata(FLAC__StreamEncoder *encoder, FLAC__StreamMetadata **metadata, unsigned num_blocks); + +/** Get the current encoder state. + * + * \param encoder An encoder instance to query. + * \assert + * \code encoder != NULL \endcode + * \retval FLAC__StreamEncoderState + * The current encoder state. + */ +FLAC_API FLAC__StreamEncoderState FLAC__stream_encoder_get_state(const FLAC__StreamEncoder *encoder); + +/** Get the state of the verify stream decoder. + * Useful when the stream encoder state is + * \c FLAC__STREAM_ENCODER_VERIFY_DECODER_ERROR. + * + * \param encoder An encoder instance to query. + * \assert + * \code encoder != NULL \endcode + * \retval FLAC__StreamDecoderState + * The verify stream decoder state. + */ +FLAC_API FLAC__StreamDecoderState FLAC__stream_encoder_get_verify_decoder_state(const FLAC__StreamEncoder *encoder); + +/** Get the current encoder state as a C string. + * This version automatically resolves + * \c FLAC__STREAM_ENCODER_VERIFY_DECODER_ERROR by getting the + * verify decoder's state. + * + * \param encoder A encoder instance to query. + * \assert + * \code encoder != NULL \endcode + * \retval const char * + * The encoder state as a C string. Do not modify the contents. + */ +FLAC_API const char *FLAC__stream_encoder_get_resolved_state_string(const FLAC__StreamEncoder *encoder); + +/** Get relevant values about the nature of a verify decoder error. + * Useful when the stream encoder state is + * \c FLAC__STREAM_ENCODER_VERIFY_DECODER_ERROR. The arguments should + * be addresses in which the stats will be returned, or NULL if value + * is not desired. + * + * \param encoder An encoder instance to query. + * \param absolute_sample The absolute sample number of the mismatch. + * \param frame_number The number of the frame in which the mismatch occurred. + * \param channel The channel in which the mismatch occurred. + * \param sample The number of the sample (relative to the frame) in + * which the mismatch occurred. + * \param expected The expected value for the sample in question. + * \param got The actual value returned by the decoder. + * \assert + * \code encoder != NULL \endcode + */ +FLAC_API void FLAC__stream_encoder_get_verify_decoder_error_stats(const FLAC__StreamEncoder *encoder, FLAC__uint64 *absolute_sample, unsigned *frame_number, unsigned *channel, unsigned *sample, FLAC__int32 *expected, FLAC__int32 *got); + +/** Get the "verify" flag. + * + * \param encoder An encoder instance to query. + * \assert + * \code encoder != NULL \endcode + * \retval FLAC__bool + * See FLAC__stream_encoder_set_verify(). + */ +FLAC_API FLAC__bool FLAC__stream_encoder_get_verify(const FLAC__StreamEncoder *encoder); + +/** Get the frame header. + * + * \param encoder An initialized encoder instance in the OK state. + * \param buffer An array of pointers to each channel's signal. + * \param samples The number of samples in one channel. + * \assert + * \code encoder != NULL \endcode + * \code FLAC__stream_encoder_get_state(encoder) == FLAC__STREAM_ENCODER_OK \endcode + * \retval FLAC__bool + * \c true if successful, else \c false; in this case, check the + * encoder state with FLAC__stream_encoder_get_state() to see what + * went wrong. + */ +FLAC_API FLAC__bool FLAC__stream_encoder_process(FLAC__StreamEncoder *encoder, const FLAC__int32 * const buffer[], unsigned samples); + +/** Submit data for encoding. + * This version allows you to supply the input data where the channels + * are interleaved into a single array (i.e. channel0_sample0, + * channel1_sample0, ... , channelN_sample0, channel0_sample1, ...). + * The samples need not be block-aligned but they must be + * sample-aligned, i.e. the first value should be channel0_sample0 + * and the last value channelN_sampleM. Each sample should be a signed + * integer, right-justified to the resolution set by + * FLAC__stream_encoder_set_bits_per_sample(). For example, if the + * resolution is 16 bits per sample, the samples should all be in the + * range [-32768,32767]. + * + * For applications where channel order is important, channels must + * follow the order as described in the + * frame header. + * + * \param encoder An initialized encoder instance in the OK state. + * \param buffer An array of channel-interleaved data (see above). + * \param samples The number of samples in one channel, the same as for + * FLAC__stream_encoder_process(). For example, if + * encoding two channels, \c 1000 \a samples corresponds + * to a \a buffer of 2000 values. + * \assert + * \code encoder != NULL \endcode + * \code FLAC__stream_encoder_get_state(encoder) == FLAC__STREAM_ENCODER_OK \endcode + * \retval FLAC__bool + * \c true if successful, else \c false; in this case, check the + * encoder state with FLAC__stream_encoder_get_state() to see what + * went wrong. + */ +FLAC_API FLAC__bool FLAC__stream_encoder_process_interleaved(FLAC__StreamEncoder *encoder, const FLAC__int32 buffer[], unsigned samples); + +/* \} */ + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/core/deps/flac/include/share/alloc.h b/core/deps/flac/include/share/alloc.h new file mode 100644 index 000000000..914de9ba6 --- /dev/null +++ b/core/deps/flac/include/share/alloc.h @@ -0,0 +1,219 @@ +/* alloc - Convenience routines for safely allocating memory + * Copyright (C) 2007-2009 Josh Coalson + * Copyright (C) 2011-2016 Xiph.Org Foundation + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * - Neither the name of the Xiph.org Foundation nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef FLAC__SHARE__ALLOC_H +#define FLAC__SHARE__ALLOC_H + +#ifdef HAVE_CONFIG_H +# include +#endif + +/* WATCHOUT: for c++ you may have to #define __STDC_LIMIT_MACROS 1 real early + * before #including this file, otherwise SIZE_MAX might not be defined + */ + +#include /* for SIZE_MAX */ +#if HAVE_STDINT_H +#include /* for SIZE_MAX in case limits.h didn't get it */ +#endif +#include /* for size_t, malloc(), etc */ +#include "share/compat.h" + +#ifndef SIZE_MAX +# ifndef SIZE_T_MAX +# ifdef _MSC_VER +# ifdef _WIN64 +# define SIZE_T_MAX FLAC__U64L(0xffffffffffffffff) +# else +# define SIZE_T_MAX 0xffffffff +# endif +# else +# error +# endif +# endif +# define SIZE_MAX SIZE_T_MAX +#endif + +/* avoid malloc()ing 0 bytes, see: + * https://www.securecoding.cert.org/confluence/display/seccode/MEM04-A.+Do+not+make+assumptions+about+the+result+of+allocating+0+bytes?focusedCommentId=5407003 +*/ +static inline void *safe_malloc_(size_t size) +{ + /* malloc(0) is undefined; FLAC src convention is to always allocate */ + if(!size) + size++; + return malloc(size); +} + +static inline void *safe_calloc_(size_t nmemb, size_t size) +{ + if(!nmemb || !size) + return malloc(1); /* malloc(0) is undefined; FLAC src convention is to always allocate */ + return calloc(nmemb, size); +} + +/*@@@@ there's probably a better way to prevent overflows when allocating untrusted sums but this works for now */ + +static inline void *safe_malloc_add_2op_(size_t size1, size_t size2) +{ + size2 += size1; + if(size2 < size1) + return 0; + return safe_malloc_(size2); +} + +static inline void *safe_malloc_add_3op_(size_t size1, size_t size2, size_t size3) +{ + size2 += size1; + if(size2 < size1) + return 0; + size3 += size2; + if(size3 < size2) + return 0; + return safe_malloc_(size3); +} + +static inline void *safe_malloc_add_4op_(size_t size1, size_t size2, size_t size3, size_t size4) +{ + size2 += size1; + if(size2 < size1) + return 0; + size3 += size2; + if(size3 < size2) + return 0; + size4 += size3; + if(size4 < size3) + return 0; + return safe_malloc_(size4); +} + +void *safe_malloc_mul_2op_(size_t size1, size_t size2) ; + +static inline void *safe_malloc_mul_3op_(size_t size1, size_t size2, size_t size3) +{ + if(!size1 || !size2 || !size3) + return malloc(1); /* malloc(0) is undefined; FLAC src convention is to always allocate */ + if(size1 > SIZE_MAX / size2) + return 0; + size1 *= size2; + if(size1 > SIZE_MAX / size3) + return 0; + return malloc(size1*size3); +} + +/* size1*size2 + size3 */ +static inline void *safe_malloc_mul2add_(size_t size1, size_t size2, size_t size3) +{ + if(!size1 || !size2) + return safe_malloc_(size3); + if(size1 > SIZE_MAX / size2) + return 0; + return safe_malloc_add_2op_(size1*size2, size3); +} + +/* size1 * (size2 + size3) */ +static inline void *safe_malloc_muladd2_(size_t size1, size_t size2, size_t size3) +{ + if(!size1 || (!size2 && !size3)) + return malloc(1); /* malloc(0) is undefined; FLAC src convention is to always allocate */ + size2 += size3; + if(size2 < size3) + return 0; + if(size1 > SIZE_MAX / size2) + return 0; + return malloc(size1*size2); +} + +static inline void *safe_realloc_(void *ptr, size_t size) +{ + void *oldptr = ptr; + void *newptr = realloc(ptr, size); + if(size > 0 && newptr == 0) + free(oldptr); + return newptr; +} +static inline void *safe_realloc_add_2op_(void *ptr, size_t size1, size_t size2) +{ + size2 += size1; + if(size2 < size1) { + free(ptr); + return 0; + } + return realloc(ptr, size2); +} + +static inline void *safe_realloc_add_3op_(void *ptr, size_t size1, size_t size2, size_t size3) +{ + size2 += size1; + if(size2 < size1) + return 0; + size3 += size2; + if(size3 < size2) + return 0; + return realloc(ptr, size3); +} + +static inline void *safe_realloc_add_4op_(void *ptr, size_t size1, size_t size2, size_t size3, size_t size4) +{ + size2 += size1; + if(size2 < size1) + return 0; + size3 += size2; + if(size3 < size2) + return 0; + size4 += size3; + if(size4 < size3) + return 0; + return realloc(ptr, size4); +} + +static inline void *safe_realloc_mul_2op_(void *ptr, size_t size1, size_t size2) +{ + if(!size1 || !size2) + return realloc(ptr, 0); /* preserve POSIX realloc(ptr, 0) semantics */ + if(size1 > SIZE_MAX / size2) + return 0; + return safe_realloc_(ptr, size1*size2); +} + +/* size1 * (size2 + size3) */ +static inline void *safe_realloc_muladd2_(void *ptr, size_t size1, size_t size2, size_t size3) +{ + if(!size1 || (!size2 && !size3)) + return realloc(ptr, 0); /* preserve POSIX realloc(ptr, 0) semantics */ + size2 += size3; + if(size2 < size3) + return 0; + return safe_realloc_mul_2op_(ptr, size1, size2); +} + +#endif diff --git a/core/deps/flac/include/share/compat.h b/core/deps/flac/include/share/compat.h new file mode 100644 index 000000000..2083f3a23 --- /dev/null +++ b/core/deps/flac/include/share/compat.h @@ -0,0 +1,209 @@ +/* libFLAC - Free Lossless Audio Codec library + * Copyright (C) 2012-2016 Xiph.org Foundation + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * - Neither the name of the Xiph.org Foundation nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* This is the prefered location of all CPP hackery to make $random_compiler + * work like something approaching a C99 (or maybe more accurately GNU99) + * compiler. + * + * It is assumed that this header will be included after "config.h". + */ + +#ifndef FLAC__SHARE__COMPAT_H +#define FLAC__SHARE__COMPAT_H + +#if defined _WIN32 && !defined __CYGWIN__ +/* where MSVC puts unlink() */ +# include +#else +# include +#endif + +#if defined _MSC_VER || defined __BORLANDC__ || defined __MINGW32__ +#include /* for off_t */ +#define FLAC__off_t __int64 /* use this instead of off_t to fix the 2 GB limit */ +#if !defined __MINGW32__ +#define fseeko _fseeki64 +#define ftello _ftelli64 +#else /* MinGW */ +#if !defined(HAVE_FSEEKO) +#define fseeko fseeko64 +#define ftello ftello64 +#endif +#endif +#else +#define FLAC__off_t off_t +#endif + +#if HAVE_INTTYPES_H +#define __STDC_FORMAT_MACROS +#include +#endif + +#if defined(_MSC_VER) +#define strtoll _strtoi64 +#define strtoull _strtoui64 +#endif + +#if defined(_MSC_VER) +#define inline __inline +#endif + +#if defined __INTEL_COMPILER || (defined _MSC_VER && defined _WIN64) +/* MSVS generates VERY slow 32-bit code with __restrict */ +#define flac_restrict __restrict +#elif defined __GNUC__ +#define flac_restrict __restrict__ +#else +#define flac_restrict +#endif + +#define FLAC__U64L(x) x##ULL + +#if defined _MSC_VER || defined __MINGW32__ +#define FLAC__STRCASECMP _stricmp +#define FLAC__STRNCASECMP _strnicmp +#elif defined __BORLANDC__ +#define FLAC__STRCASECMP stricmp +#define FLAC__STRNCASECMP strnicmp +#else +#define FLAC__STRCASECMP strcasecmp +#define FLAC__STRNCASECMP strncasecmp +#endif + +#if defined _MSC_VER || defined __MINGW32__ || defined __CYGWIN__ || defined __EMX__ +#include /* for _setmode(), chmod() */ +#include /* for _O_BINARY */ +#else +#include /* for chown(), unlink() */ +#endif + +#if defined _MSC_VER || defined __BORLANDC__ || defined __MINGW32__ +#if defined __BORLANDC__ +#include /* for utime() */ +#else +#include /* for utime() */ +#endif +#else +#include /* some flavors of BSD (like OS X) require this to get time_t */ +#include /* for utime() */ +#endif + +#if defined _MSC_VER +# if _MSC_VER >= 1800 +# include +# elif _MSC_VER >= 1600 +/* Visual Studio 2010 has decent C99 support */ +# include +# define PRIu64 "llu" +# define PRId64 "lld" +# define PRIx64 "llx" +# else +# include +# ifndef UINT32_MAX +# define UINT32_MAX _UI32_MAX +# endif + typedef unsigned __int64 uint64_t; + typedef unsigned __int32 uint32_t; + typedef unsigned __int16 uint16_t; + typedef unsigned __int8 uint8_t; + typedef __int64 int64_t; + typedef __int32 int32_t; + typedef __int16 int16_t; + typedef __int8 int8_t; +# define PRIu64 "I64u" +# define PRId64 "I64d" +# define PRIx64 "I64x" +# endif +#endif /* defined _MSC_VER */ + +#ifdef _WIN32 +/* All char* strings are in UTF-8 format. Added to support Unicode files on Windows */ + +#include "share/win_utf8_io.h" +#define flac_printf printf_utf8 +#define flac_fprintf fprintf_utf8 +#define flac_vfprintf vfprintf_utf8 + +#include "share/windows_unicode_filenames.h" +#define flac_fopen flac_internal_fopen_utf8 +#define flac_chmod flac_internal_chmod_utf8 +#define flac_utime flac_internal_utime_utf8 +#define flac_unlink flac_internal_unlink_utf8 +#define flac_rename flac_internal_rename_utf8 +#define flac_stat flac_internal_stat64_utf8 + +#else + +#define flac_printf printf +#define flac_fprintf fprintf +#define flac_vfprintf vfprintf + +#define flac_fopen fopen +#define flac_chmod chmod +#define flac_utime utime +#define flac_unlink unlink +#define flac_rename rename +#define flac_stat stat + +#endif + +#ifdef _WIN32 +#define flac_stat_s __stat64 /* stat struct */ +#define flac_fstat _fstat64 +#else +#define flac_stat_s stat /* stat struct */ +#define flac_fstat fstat +#endif + +#ifndef M_LN2 +#define M_LN2 0.69314718055994530942 +#endif +#ifndef M_PI +#define M_PI 3.14159265358979323846 +#endif + +/* FLAC needs to compile and work correctly on systems with a normal ISO C99 + * snprintf as well as Microsoft Visual Studio which has an non-standards + * conformant snprint_s function. + * + * This function wraps the MS version to behave more like the ISO version. + */ +#include +#ifdef __cplusplus +extern "C" { +#endif +int flac_snprintf(char *str, size_t size, const char *fmt, ...); +int flac_vsnprintf(char *str, size_t size, const char *fmt, va_list va); +#ifdef __cplusplus +}; +#endif + +#endif /* FLAC__SHARE__COMPAT_H */ diff --git a/core/deps/flac/src/libFLAC/bitmath.c b/core/deps/flac/src/libFLAC/bitmath.c new file mode 100644 index 000000000..b3d797d39 --- /dev/null +++ b/core/deps/flac/src/libFLAC/bitmath.c @@ -0,0 +1,73 @@ +/* libFLAC - Free Lossless Audio Codec library + * Copyright (C) 2001-2009 Josh Coalson + * Copyright (C) 2011-2016 Xiph.Org Foundation + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * - Neither the name of the Xiph.org Foundation nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifdef HAVE_CONFIG_H +# include +#endif + +#include "private/bitmath.h" + +/* An example of what FLAC__bitmath_silog2() computes: + * + * silog2(-10) = 5 + * silog2(- 9) = 5 + * silog2(- 8) = 4 + * silog2(- 7) = 4 + * silog2(- 6) = 4 + * silog2(- 5) = 4 + * silog2(- 4) = 3 + * silog2(- 3) = 3 + * silog2(- 2) = 2 + * silog2(- 1) = 2 + * silog2( 0) = 0 + * silog2( 1) = 2 + * silog2( 2) = 3 + * silog2( 3) = 3 + * silog2( 4) = 4 + * silog2( 5) = 4 + * silog2( 6) = 4 + * silog2( 7) = 4 + * silog2( 8) = 5 + * silog2( 9) = 5 + * silog2( 10) = 5 + */ +unsigned FLAC__bitmath_silog2(FLAC__int64 v) +{ + if(v == 0) + return 0; + + if(v == -1) + return 2; + + v = (v < 0) ? (-(v+1)) : v; + return FLAC__bitmath_ilog2_wide(v)+2; +} diff --git a/core/deps/flac/src/libFLAC/bitreader.c b/core/deps/flac/src/libFLAC/bitreader.c new file mode 100644 index 000000000..ab62d414f --- /dev/null +++ b/core/deps/flac/src/libFLAC/bitreader.c @@ -0,0 +1,1087 @@ +/* libFLAC - Free Lossless Audio Codec library + * Copyright (C) 2000-2009 Josh Coalson + * Copyright (C) 2011-2016 Xiph.Org Foundation + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * - Neither the name of the Xiph.org Foundation nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifdef HAVE_CONFIG_H +# include +#endif + +#include +#include +#include "private/bitmath.h" +#include "private/bitreader.h" +#include "private/crc.h" +#include "private/macros.h" +#include "FLAC/assert.h" +#include "share/compat.h" +#include "share/endswap.h" + +/* Things should be fastest when this matches the machine word size */ +/* WATCHOUT: if you change this you must also change the following #defines down to COUNT_ZERO_MSBS2 below to match */ +/* WATCHOUT: there are a few places where the code will not work unless brword is >= 32 bits wide */ +/* also, some sections currently only have fast versions for 4 or 8 bytes per word */ + +#if (ENABLE_64_BIT_WORDS == 0) + +typedef FLAC__uint32 brword; +#define FLAC__BYTES_PER_WORD 4 /* sizeof brword */ +#define FLAC__BITS_PER_WORD 32 +#define FLAC__WORD_ALL_ONES ((FLAC__uint32)0xffffffff) +/* SWAP_BE_WORD_TO_HOST swaps bytes in a brword (which is always big-endian) if necessary to match host byte order */ +#if WORDS_BIGENDIAN +#define SWAP_BE_WORD_TO_HOST(x) (x) +#else +#define SWAP_BE_WORD_TO_HOST(x) ENDSWAP_32(x) +#endif +/* counts the # of zero MSBs in a word */ +#define COUNT_ZERO_MSBS(word) FLAC__clz_uint32(word) +#define COUNT_ZERO_MSBS2(word) FLAC__clz2_uint32(word) + +#else + +typedef FLAC__uint64 brword; +#define FLAC__BYTES_PER_WORD 8 /* sizeof brword */ +#define FLAC__BITS_PER_WORD 64 +#define FLAC__WORD_ALL_ONES ((FLAC__uint64)FLAC__U64L(0xffffffffffffffff)) +/* SWAP_BE_WORD_TO_HOST swaps bytes in a brword (which is always big-endian) if necessary to match host byte order */ +#if WORDS_BIGENDIAN +#define SWAP_BE_WORD_TO_HOST(x) (x) +#else +#define SWAP_BE_WORD_TO_HOST(x) ENDSWAP_64(x) +#endif +/* counts the # of zero MSBs in a word */ +#define COUNT_ZERO_MSBS(word) FLAC__clz_uint64(word) +#define COUNT_ZERO_MSBS2(word) FLAC__clz2_uint64(word) + +#endif + +/* + * This should be at least twice as large as the largest number of words + * required to represent any 'number' (in any encoding) you are going to + * read. With FLAC this is on the order of maybe a few hundred bits. + * If the buffer is smaller than that, the decoder won't be able to read + * in a whole number that is in a variable length encoding (e.g. Rice). + * But to be practical it should be at least 1K bytes. + * + * Increase this number to decrease the number of read callbacks, at the + * expense of using more memory. Or decrease for the reverse effect, + * keeping in mind the limit from the first paragraph. The optimal size + * also depends on the CPU cache size and other factors; some twiddling + * may be necessary to squeeze out the best performance. + */ +static const unsigned FLAC__BITREADER_DEFAULT_CAPACITY = 65536u / FLAC__BITS_PER_WORD; /* in words */ + +struct FLAC__BitReader { + /* any partially-consumed word at the head will stay right-justified as bits are consumed from the left */ + /* any incomplete word at the tail will be left-justified, and bytes from the read callback are added on the right */ + brword *buffer; + unsigned capacity; /* in words */ + unsigned words; /* # of completed words in buffer */ + unsigned bytes; /* # of bytes in incomplete word at buffer[words] */ + unsigned consumed_words; /* #words ... */ + unsigned consumed_bits; /* ... + (#bits of head word) already consumed from the front of buffer */ + unsigned read_crc16; /* the running frame CRC */ + unsigned crc16_align; /* the number of bits in the current consumed word that should not be CRC'd */ + FLAC__BitReaderReadCallback read_callback; + void *client_data; +}; + +static inline void crc16_update_word_(FLAC__BitReader *br, brword word) +{ + register unsigned crc = br->read_crc16; +#if FLAC__BYTES_PER_WORD == 4 + switch(br->crc16_align) { + case 0: crc = FLAC__CRC16_UPDATE((unsigned)(word >> 24), crc); + case 8: crc = FLAC__CRC16_UPDATE((unsigned)((word >> 16) & 0xff), crc); + case 16: crc = FLAC__CRC16_UPDATE((unsigned)((word >> 8) & 0xff), crc); + case 24: br->read_crc16 = FLAC__CRC16_UPDATE((unsigned)(word & 0xff), crc); + } +#elif FLAC__BYTES_PER_WORD == 8 + switch(br->crc16_align) { + case 0: crc = FLAC__CRC16_UPDATE((unsigned)(word >> 56), crc); + case 8: crc = FLAC__CRC16_UPDATE((unsigned)((word >> 48) & 0xff), crc); + case 16: crc = FLAC__CRC16_UPDATE((unsigned)((word >> 40) & 0xff), crc); + case 24: crc = FLAC__CRC16_UPDATE((unsigned)((word >> 32) & 0xff), crc); + case 32: crc = FLAC__CRC16_UPDATE((unsigned)((word >> 24) & 0xff), crc); + case 40: crc = FLAC__CRC16_UPDATE((unsigned)((word >> 16) & 0xff), crc); + case 48: crc = FLAC__CRC16_UPDATE((unsigned)((word >> 8) & 0xff), crc); + case 56: br->read_crc16 = FLAC__CRC16_UPDATE((unsigned)(word & 0xff), crc); + } +#else + for( ; br->crc16_align < FLAC__BITS_PER_WORD; br->crc16_align += 8) + crc = FLAC__CRC16_UPDATE((unsigned)((word >> (FLAC__BITS_PER_WORD-8-br->crc16_align)) & 0xff), crc); + br->read_crc16 = crc; +#endif + br->crc16_align = 0; +} + +static FLAC__bool bitreader_read_from_client_(FLAC__BitReader *br) +{ + unsigned start, end; + size_t bytes; + FLAC__byte *target; + + /* first shift the unconsumed buffer data toward the front as much as possible */ + if(br->consumed_words > 0) { + start = br->consumed_words; + end = br->words + (br->bytes? 1:0); + memmove(br->buffer, br->buffer+start, FLAC__BYTES_PER_WORD * (end - start)); + + br->words -= start; + br->consumed_words = 0; + } + + /* + * set the target for reading, taking into account word alignment and endianness + */ + bytes = (br->capacity - br->words) * FLAC__BYTES_PER_WORD - br->bytes; + if(bytes == 0) + return false; /* no space left, buffer is too small; see note for FLAC__BITREADER_DEFAULT_CAPACITY */ + target = ((FLAC__byte*)(br->buffer+br->words)) + br->bytes; + + /* before reading, if the existing reader looks like this (say brword is 32 bits wide) + * bitstream : 11 22 33 44 55 br->words=1 br->bytes=1 (partial tail word is left-justified) + * buffer[BE]: 11 22 33 44 55 ?? ?? ?? (shown layed out as bytes sequentially in memory) + * buffer[LE]: 44 33 22 11 ?? ?? ?? 55 (?? being don't-care) + * ^^-------target, bytes=3 + * on LE machines, have to byteswap the odd tail word so nothing is + * overwritten: + */ +#if WORDS_BIGENDIAN +#else + if(br->bytes) + br->buffer[br->words] = SWAP_BE_WORD_TO_HOST(br->buffer[br->words]); +#endif + + /* now it looks like: + * bitstream : 11 22 33 44 55 br->words=1 br->bytes=1 + * buffer[BE]: 11 22 33 44 55 ?? ?? ?? + * buffer[LE]: 44 33 22 11 55 ?? ?? ?? + * ^^-------target, bytes=3 + */ + + /* read in the data; note that the callback may return a smaller number of bytes */ + if(!br->read_callback(target, &bytes, br->client_data)) + return false; + + /* after reading bytes 66 77 88 99 AA BB CC DD EE FF from the client: + * bitstream : 11 22 33 44 55 66 77 88 99 AA BB CC DD EE FF + * buffer[BE]: 11 22 33 44 55 66 77 88 99 AA BB CC DD EE FF ?? + * buffer[LE]: 44 33 22 11 55 66 77 88 99 AA BB CC DD EE FF ?? + * now have to byteswap on LE machines: + */ +#if WORDS_BIGENDIAN +#else + end = (br->words*FLAC__BYTES_PER_WORD + br->bytes + (unsigned)bytes + (FLAC__BYTES_PER_WORD-1)) / FLAC__BYTES_PER_WORD; + for(start = br->words; start < end; start++) + br->buffer[start] = SWAP_BE_WORD_TO_HOST(br->buffer[start]); +#endif + + /* now it looks like: + * bitstream : 11 22 33 44 55 66 77 88 99 AA BB CC DD EE FF + * buffer[BE]: 11 22 33 44 55 66 77 88 99 AA BB CC DD EE FF ?? + * buffer[LE]: 44 33 22 11 88 77 66 55 CC BB AA 99 ?? FF EE DD + * finally we'll update the reader values: + */ + end = br->words*FLAC__BYTES_PER_WORD + br->bytes + (unsigned)bytes; + br->words = end / FLAC__BYTES_PER_WORD; + br->bytes = end % FLAC__BYTES_PER_WORD; + + return true; +} + +/*********************************************************************** + * + * Class constructor/destructor + * + ***********************************************************************/ + +FLAC__BitReader *FLAC__bitreader_new(void) +{ + FLAC__BitReader *br = calloc(1, sizeof(FLAC__BitReader)); + + /* calloc() implies: + memset(br, 0, sizeof(FLAC__BitReader)); + br->buffer = 0; + br->capacity = 0; + br->words = br->bytes = 0; + br->consumed_words = br->consumed_bits = 0; + br->read_callback = 0; + br->client_data = 0; + */ + return br; +} + +void FLAC__bitreader_delete(FLAC__BitReader *br) +{ + FLAC__ASSERT(0 != br); + + FLAC__bitreader_free(br); + free(br); +} + +/*********************************************************************** + * + * Public class methods + * + ***********************************************************************/ + +FLAC__bool FLAC__bitreader_init(FLAC__BitReader *br, FLAC__BitReaderReadCallback rcb, void *cd) +{ + FLAC__ASSERT(0 != br); + + br->words = br->bytes = 0; + br->consumed_words = br->consumed_bits = 0; + br->capacity = FLAC__BITREADER_DEFAULT_CAPACITY; + br->buffer = malloc(sizeof(brword) * br->capacity); + if(br->buffer == 0) + return false; + br->read_callback = rcb; + br->client_data = cd; + + return true; +} + +void FLAC__bitreader_free(FLAC__BitReader *br) +{ + FLAC__ASSERT(0 != br); + + if(0 != br->buffer) + free(br->buffer); + br->buffer = 0; + br->capacity = 0; + br->words = br->bytes = 0; + br->consumed_words = br->consumed_bits = 0; + br->read_callback = 0; + br->client_data = 0; +} + +FLAC__bool FLAC__bitreader_clear(FLAC__BitReader *br) +{ + br->words = br->bytes = 0; + br->consumed_words = br->consumed_bits = 0; + return true; +} + +void FLAC__bitreader_dump(const FLAC__BitReader *br, FILE *out) +{ + unsigned i, j; + if(br == 0) { + fprintf(out, "bitreader is NULL\n"); + } + else { + fprintf(out, "bitreader: capacity=%u words=%u bytes=%u consumed: words=%u, bits=%u\n", br->capacity, br->words, br->bytes, br->consumed_words, br->consumed_bits); + + for(i = 0; i < br->words; i++) { + fprintf(out, "%08X: ", i); + for(j = 0; j < FLAC__BITS_PER_WORD; j++) + if(i < br->consumed_words || (i == br->consumed_words && j < br->consumed_bits)) + fprintf(out, "."); + else + fprintf(out, "%01u", br->buffer[i] & ((brword)1 << (FLAC__BITS_PER_WORD-j-1)) ? 1:0); + fprintf(out, "\n"); + } + if(br->bytes > 0) { + fprintf(out, "%08X: ", i); + for(j = 0; j < br->bytes*8; j++) + if(i < br->consumed_words || (i == br->consumed_words && j < br->consumed_bits)) + fprintf(out, "."); + else + fprintf(out, "%01u", br->buffer[i] & ((brword)1 << (br->bytes*8-j-1)) ? 1:0); + fprintf(out, "\n"); + } + } +} + +void FLAC__bitreader_reset_read_crc16(FLAC__BitReader *br, FLAC__uint16 seed) +{ + FLAC__ASSERT(0 != br); + FLAC__ASSERT(0 != br->buffer); + FLAC__ASSERT((br->consumed_bits & 7) == 0); + + br->read_crc16 = (unsigned)seed; + br->crc16_align = br->consumed_bits; +} + +FLAC__uint16 FLAC__bitreader_get_read_crc16(FLAC__BitReader *br) +{ + FLAC__ASSERT(0 != br); + FLAC__ASSERT(0 != br->buffer); + FLAC__ASSERT((br->consumed_bits & 7) == 0); + FLAC__ASSERT(br->crc16_align <= br->consumed_bits); + + /* CRC any tail bytes in a partially-consumed word */ + if(br->consumed_bits) { + const brword tail = br->buffer[br->consumed_words]; + for( ; br->crc16_align < br->consumed_bits; br->crc16_align += 8) + br->read_crc16 = FLAC__CRC16_UPDATE((unsigned)((tail >> (FLAC__BITS_PER_WORD-8-br->crc16_align)) & 0xff), br->read_crc16); + } + return br->read_crc16; +} + +inline FLAC__bool FLAC__bitreader_is_consumed_byte_aligned(const FLAC__BitReader *br) +{ + return ((br->consumed_bits & 7) == 0); +} + +inline unsigned FLAC__bitreader_bits_left_for_byte_alignment(const FLAC__BitReader *br) +{ + return 8 - (br->consumed_bits & 7); +} + +inline unsigned FLAC__bitreader_get_input_bits_unconsumed(const FLAC__BitReader *br) +{ + return (br->words-br->consumed_words)*FLAC__BITS_PER_WORD + br->bytes*8 - br->consumed_bits; +} + +FLAC__bool FLAC__bitreader_read_raw_uint32(FLAC__BitReader *br, FLAC__uint32 *val, unsigned bits) +{ + FLAC__ASSERT(0 != br); + FLAC__ASSERT(0 != br->buffer); + + FLAC__ASSERT(bits <= 32); + FLAC__ASSERT((br->capacity*FLAC__BITS_PER_WORD) * 2 >= bits); + FLAC__ASSERT(br->consumed_words <= br->words); + + /* WATCHOUT: code does not work with <32bit words; we can make things much faster with this assertion */ + FLAC__ASSERT(FLAC__BITS_PER_WORD >= 32); + + if(bits == 0) { /* OPT: investigate if this can ever happen, maybe change to assertion */ + *val = 0; + return true; + } + + while((br->words-br->consumed_words)*FLAC__BITS_PER_WORD + br->bytes*8 - br->consumed_bits < bits) { + if(!bitreader_read_from_client_(br)) + return false; + } + if(br->consumed_words < br->words) { /* if we've not consumed up to a partial tail word... */ + /* OPT: taking out the consumed_bits==0 "else" case below might make things faster if less code allows the compiler to inline this function */ + if(br->consumed_bits) { + /* this also works when consumed_bits==0, it's just a little slower than necessary for that case */ + const unsigned n = FLAC__BITS_PER_WORD - br->consumed_bits; + const brword word = br->buffer[br->consumed_words]; + if(bits < n) { + *val = (FLAC__uint32)((word & (FLAC__WORD_ALL_ONES >> br->consumed_bits)) >> (n-bits)); /* The result has <= 32 non-zero bits */ + br->consumed_bits += bits; + return true; + } + /* (FLAC__BITS_PER_WORD - br->consumed_bits <= bits) ==> (FLAC__WORD_ALL_ONES >> br->consumed_bits) has no more than 'bits' non-zero bits */ + *val = (FLAC__uint32)(word & (FLAC__WORD_ALL_ONES >> br->consumed_bits)); + bits -= n; + crc16_update_word_(br, word); + br->consumed_words++; + br->consumed_bits = 0; + if(bits) { /* if there are still bits left to read, there have to be less than 32 so they will all be in the next word */ + *val <<= bits; + *val |= (FLAC__uint32)(br->buffer[br->consumed_words] >> (FLAC__BITS_PER_WORD-bits)); + br->consumed_bits = bits; + } + return true; + } + else { /* br->consumed_bits == 0 */ + const brword word = br->buffer[br->consumed_words]; + if(bits < FLAC__BITS_PER_WORD) { + *val = (FLAC__uint32)(word >> (FLAC__BITS_PER_WORD-bits)); + br->consumed_bits = bits; + return true; + } + /* at this point bits == FLAC__BITS_PER_WORD == 32; because of previous assertions, it can't be larger */ + *val = (FLAC__uint32)word; + crc16_update_word_(br, word); + br->consumed_words++; + return true; + } + } + else { + /* in this case we're starting our read at a partial tail word; + * the reader has guaranteed that we have at least 'bits' bits + * available to read, which makes this case simpler. + */ + /* OPT: taking out the consumed_bits==0 "else" case below might make things faster if less code allows the compiler to inline this function */ + if(br->consumed_bits) { + /* this also works when consumed_bits==0, it's just a little slower than necessary for that case */ + FLAC__ASSERT(br->consumed_bits + bits <= br->bytes*8); + *val = (FLAC__uint32)((br->buffer[br->consumed_words] & (FLAC__WORD_ALL_ONES >> br->consumed_bits)) >> (FLAC__BITS_PER_WORD-br->consumed_bits-bits)); + br->consumed_bits += bits; + return true; + } + else { + *val = (FLAC__uint32)(br->buffer[br->consumed_words] >> (FLAC__BITS_PER_WORD-bits)); + br->consumed_bits += bits; + return true; + } + } +} + +FLAC__bool FLAC__bitreader_read_raw_int32(FLAC__BitReader *br, FLAC__int32 *val, unsigned bits) +{ + FLAC__uint32 uval, mask; + /* OPT: inline raw uint32 code here, or make into a macro if possible in the .h file */ + if(!FLAC__bitreader_read_raw_uint32(br, &uval, bits)) + return false; + /* sign-extend *val assuming it is currently bits wide. */ + /* From: https://graphics.stanford.edu/~seander/bithacks.html#FixedSignExtend */ + mask = 1u << (bits - 1); + *val = (uval ^ mask) - mask; + return true; +} + +FLAC__bool FLAC__bitreader_read_raw_uint64(FLAC__BitReader *br, FLAC__uint64 *val, unsigned bits) +{ + FLAC__uint32 hi, lo; + + if(bits > 32) { + if(!FLAC__bitreader_read_raw_uint32(br, &hi, bits-32)) + return false; + if(!FLAC__bitreader_read_raw_uint32(br, &lo, 32)) + return false; + *val = hi; + *val <<= 32; + *val |= lo; + } + else { + if(!FLAC__bitreader_read_raw_uint32(br, &lo, bits)) + return false; + *val = lo; + } + return true; +} + +inline FLAC__bool FLAC__bitreader_read_uint32_little_endian(FLAC__BitReader *br, FLAC__uint32 *val) +{ + FLAC__uint32 x8, x32 = 0; + + /* this doesn't need to be that fast as currently it is only used for vorbis comments */ + + if(!FLAC__bitreader_read_raw_uint32(br, &x32, 8)) + return false; + + if(!FLAC__bitreader_read_raw_uint32(br, &x8, 8)) + return false; + x32 |= (x8 << 8); + + if(!FLAC__bitreader_read_raw_uint32(br, &x8, 8)) + return false; + x32 |= (x8 << 16); + + if(!FLAC__bitreader_read_raw_uint32(br, &x8, 8)) + return false; + x32 |= (x8 << 24); + + *val = x32; + return true; +} + +FLAC__bool FLAC__bitreader_skip_bits_no_crc(FLAC__BitReader *br, unsigned bits) +{ + /* + * OPT: a faster implementation is possible but probably not that useful + * since this is only called a couple of times in the metadata readers. + */ + FLAC__ASSERT(0 != br); + FLAC__ASSERT(0 != br->buffer); + + if(bits > 0) { + const unsigned n = br->consumed_bits & 7; + unsigned m; + FLAC__uint32 x; + + if(n != 0) { + m = flac_min(8-n, bits); + if(!FLAC__bitreader_read_raw_uint32(br, &x, m)) + return false; + bits -= m; + } + m = bits / 8; + if(m > 0) { + if(!FLAC__bitreader_skip_byte_block_aligned_no_crc(br, m)) + return false; + bits %= 8; + } + if(bits > 0) { + if(!FLAC__bitreader_read_raw_uint32(br, &x, bits)) + return false; + } + } + + return true; +} + +FLAC__bool FLAC__bitreader_skip_byte_block_aligned_no_crc(FLAC__BitReader *br, unsigned nvals) +{ + FLAC__uint32 x; + + FLAC__ASSERT(0 != br); + FLAC__ASSERT(0 != br->buffer); + FLAC__ASSERT(FLAC__bitreader_is_consumed_byte_aligned(br)); + + /* step 1: skip over partial head word to get word aligned */ + while(nvals && br->consumed_bits) { /* i.e. run until we read 'nvals' bytes or we hit the end of the head word */ + if(!FLAC__bitreader_read_raw_uint32(br, &x, 8)) + return false; + nvals--; + } + if(0 == nvals) + return true; + /* step 2: skip whole words in chunks */ + while(nvals >= FLAC__BYTES_PER_WORD) { + if(br->consumed_words < br->words) { + br->consumed_words++; + nvals -= FLAC__BYTES_PER_WORD; + } + else if(!bitreader_read_from_client_(br)) + return false; + } + /* step 3: skip any remainder from partial tail bytes */ + while(nvals) { + if(!FLAC__bitreader_read_raw_uint32(br, &x, 8)) + return false; + nvals--; + } + + return true; +} + +FLAC__bool FLAC__bitreader_read_byte_block_aligned_no_crc(FLAC__BitReader *br, FLAC__byte *val, unsigned nvals) +{ + FLAC__uint32 x; + + FLAC__ASSERT(0 != br); + FLAC__ASSERT(0 != br->buffer); + FLAC__ASSERT(FLAC__bitreader_is_consumed_byte_aligned(br)); + + /* step 1: read from partial head word to get word aligned */ + while(nvals && br->consumed_bits) { /* i.e. run until we read 'nvals' bytes or we hit the end of the head word */ + if(!FLAC__bitreader_read_raw_uint32(br, &x, 8)) + return false; + *val++ = (FLAC__byte)x; + nvals--; + } + if(0 == nvals) + return true; + /* step 2: read whole words in chunks */ + while(nvals >= FLAC__BYTES_PER_WORD) { + if(br->consumed_words < br->words) { + const brword word = br->buffer[br->consumed_words++]; +#if FLAC__BYTES_PER_WORD == 4 + val[0] = (FLAC__byte)(word >> 24); + val[1] = (FLAC__byte)(word >> 16); + val[2] = (FLAC__byte)(word >> 8); + val[3] = (FLAC__byte)word; +#elif FLAC__BYTES_PER_WORD == 8 + val[0] = (FLAC__byte)(word >> 56); + val[1] = (FLAC__byte)(word >> 48); + val[2] = (FLAC__byte)(word >> 40); + val[3] = (FLAC__byte)(word >> 32); + val[4] = (FLAC__byte)(word >> 24); + val[5] = (FLAC__byte)(word >> 16); + val[6] = (FLAC__byte)(word >> 8); + val[7] = (FLAC__byte)word; +#else + for(x = 0; x < FLAC__BYTES_PER_WORD; x++) + val[x] = (FLAC__byte)(word >> (8*(FLAC__BYTES_PER_WORD-x-1))); +#endif + val += FLAC__BYTES_PER_WORD; + nvals -= FLAC__BYTES_PER_WORD; + } + else if(!bitreader_read_from_client_(br)) + return false; + } + /* step 3: read any remainder from partial tail bytes */ + while(nvals) { + if(!FLAC__bitreader_read_raw_uint32(br, &x, 8)) + return false; + *val++ = (FLAC__byte)x; + nvals--; + } + + return true; +} + +FLAC__bool FLAC__bitreader_read_unary_unsigned(FLAC__BitReader *br, unsigned *val) +#if 0 /* slow but readable version */ +{ + unsigned bit; + + FLAC__ASSERT(0 != br); + FLAC__ASSERT(0 != br->buffer); + + *val = 0; + while(1) { + if(!FLAC__bitreader_read_bit(br, &bit)) + return false; + if(bit) + break; + else + *val++; + } + return true; +} +#else +{ + unsigned i; + + FLAC__ASSERT(0 != br); + FLAC__ASSERT(0 != br->buffer); + + *val = 0; + while(1) { + while(br->consumed_words < br->words) { /* if we've not consumed up to a partial tail word... */ + brword b = br->buffer[br->consumed_words] << br->consumed_bits; + if(b) { + i = COUNT_ZERO_MSBS(b); + *val += i; + i++; + br->consumed_bits += i; + if(br->consumed_bits >= FLAC__BITS_PER_WORD) { /* faster way of testing if(br->consumed_bits == FLAC__BITS_PER_WORD) */ + crc16_update_word_(br, br->buffer[br->consumed_words]); + br->consumed_words++; + br->consumed_bits = 0; + } + return true; + } + else { + *val += FLAC__BITS_PER_WORD - br->consumed_bits; + crc16_update_word_(br, br->buffer[br->consumed_words]); + br->consumed_words++; + br->consumed_bits = 0; + /* didn't find stop bit yet, have to keep going... */ + } + } + /* at this point we've eaten up all the whole words; have to try + * reading through any tail bytes before calling the read callback. + * this is a repeat of the above logic adjusted for the fact we + * don't have a whole word. note though if the client is feeding + * us data a byte at a time (unlikely), br->consumed_bits may not + * be zero. + */ + if(br->bytes*8 > br->consumed_bits) { + const unsigned end = br->bytes * 8; + brword b = (br->buffer[br->consumed_words] & (FLAC__WORD_ALL_ONES << (FLAC__BITS_PER_WORD-end))) << br->consumed_bits; + if(b) { + i = COUNT_ZERO_MSBS(b); + *val += i; + i++; + br->consumed_bits += i; + FLAC__ASSERT(br->consumed_bits < FLAC__BITS_PER_WORD); + return true; + } + else { + *val += end - br->consumed_bits; + br->consumed_bits = end; + FLAC__ASSERT(br->consumed_bits < FLAC__BITS_PER_WORD); + /* didn't find stop bit yet, have to keep going... */ + } + } + if(!bitreader_read_from_client_(br)) + return false; + } +} +#endif + +FLAC__bool FLAC__bitreader_read_rice_signed(FLAC__BitReader *br, int *val, unsigned parameter) +{ + FLAC__uint32 lsbs = 0, msbs = 0; + unsigned uval; + + FLAC__ASSERT(0 != br); + FLAC__ASSERT(0 != br->buffer); + FLAC__ASSERT(parameter <= 31); + + /* read the unary MSBs and end bit */ + if(!FLAC__bitreader_read_unary_unsigned(br, &msbs)) + return false; + + /* read the binary LSBs */ + if(!FLAC__bitreader_read_raw_uint32(br, &lsbs, parameter)) + return false; + + /* compose the value */ + uval = (msbs << parameter) | lsbs; + if(uval & 1) + *val = -((int)(uval >> 1)) - 1; + else + *val = (int)(uval >> 1); + + return true; +} + +/* this is by far the most heavily used reader call. it ain't pretty but it's fast */ +FLAC__bool FLAC__bitreader_read_rice_signed_block(FLAC__BitReader *br, int vals[], unsigned nvals, unsigned parameter) +{ + /* try and get br->consumed_words and br->consumed_bits into register; + * must remember to flush them back to *br before calling other + * bitreader functions that use them, and before returning */ + unsigned cwords, words, lsbs, msbs, x, y; + unsigned ucbits; /* keep track of the number of unconsumed bits in word */ + brword b; + int *val, *end; + + FLAC__ASSERT(0 != br); + FLAC__ASSERT(0 != br->buffer); + /* WATCHOUT: code does not work with <32bit words; we can make things much faster with this assertion */ + FLAC__ASSERT(FLAC__BITS_PER_WORD >= 32); + FLAC__ASSERT(parameter < 32); + /* the above two asserts also guarantee that the binary part never straddles more than 2 words, so we don't have to loop to read it */ + + val = vals; + end = vals + nvals; + + if(parameter == 0) { + while(val < end) { + /* read the unary MSBs and end bit */ + if(!FLAC__bitreader_read_unary_unsigned(br, &msbs)) + return false; + + *val++ = (int)(msbs >> 1) ^ -(int)(msbs & 1); + } + + return true; + } + + FLAC__ASSERT(parameter > 0); + + cwords = br->consumed_words; + words = br->words; + + /* if we've not consumed up to a partial tail word... */ + if(cwords >= words) { + x = 0; + goto process_tail; + } + + ucbits = FLAC__BITS_PER_WORD - br->consumed_bits; + b = br->buffer[cwords] << br->consumed_bits; /* keep unconsumed bits aligned to left */ + + while(val < end) { + /* read the unary MSBs and end bit */ + x = y = COUNT_ZERO_MSBS2(b); + if(x == FLAC__BITS_PER_WORD) { + x = ucbits; + do { + /* didn't find stop bit yet, have to keep going... */ + crc16_update_word_(br, br->buffer[cwords++]); + if (cwords >= words) + goto incomplete_msbs; + b = br->buffer[cwords]; + y = COUNT_ZERO_MSBS2(b); + x += y; + } while(y == FLAC__BITS_PER_WORD); + } + b <<= y; + b <<= 1; /* account for stop bit */ + ucbits = (ucbits - x - 1) % FLAC__BITS_PER_WORD; + msbs = x; + + /* read the binary LSBs */ + x = (FLAC__uint32)(b >> (FLAC__BITS_PER_WORD - parameter)); /* parameter < 32, so we can cast to 32-bit unsigned */ + if(parameter <= ucbits) { + ucbits -= parameter; + b <<= parameter; + } else { + /* there are still bits left to read, they will all be in the next word */ + crc16_update_word_(br, br->buffer[cwords++]); + if (cwords >= words) + goto incomplete_lsbs; + b = br->buffer[cwords]; + ucbits += FLAC__BITS_PER_WORD - parameter; + x |= (FLAC__uint32)(b >> ucbits); + b <<= FLAC__BITS_PER_WORD - ucbits; + } + lsbs = x; + + /* compose the value */ + x = (msbs << parameter) | lsbs; + *val++ = (int)(x >> 1) ^ -(int)(x & 1); + + continue; + + /* at this point we've eaten up all the whole words */ +process_tail: + do { + if(0) { +incomplete_msbs: + br->consumed_bits = 0; + br->consumed_words = cwords; + } + + /* read the unary MSBs and end bit */ + if(!FLAC__bitreader_read_unary_unsigned(br, &msbs)) + return false; + msbs += x; + x = ucbits = 0; + + if(0) { +incomplete_lsbs: + br->consumed_bits = 0; + br->consumed_words = cwords; + } + + /* read the binary LSBs */ + if(!FLAC__bitreader_read_raw_uint32(br, &lsbs, parameter - ucbits)) + return false; + lsbs = x | lsbs; + + /* compose the value */ + x = (msbs << parameter) | lsbs; + *val++ = (int)(x >> 1) ^ -(int)(x & 1); + x = 0; + + cwords = br->consumed_words; + words = br->words; + ucbits = FLAC__BITS_PER_WORD - br->consumed_bits; + b = br->buffer[cwords] << br->consumed_bits; + } while(cwords >= words && val < end); + } + + if(ucbits == 0 && cwords < words) { + /* don't leave the head word with no unconsumed bits */ + crc16_update_word_(br, br->buffer[cwords++]); + ucbits = FLAC__BITS_PER_WORD; + } + + br->consumed_bits = FLAC__BITS_PER_WORD - ucbits; + br->consumed_words = cwords; + + return true; +} + +#if 0 /* UNUSED */ +FLAC__bool FLAC__bitreader_read_golomb_signed(FLAC__BitReader *br, int *val, unsigned parameter) +{ + FLAC__uint32 lsbs = 0, msbs = 0; + unsigned bit, uval, k; + + FLAC__ASSERT(0 != br); + FLAC__ASSERT(0 != br->buffer); + + k = FLAC__bitmath_ilog2(parameter); + + /* read the unary MSBs and end bit */ + if(!FLAC__bitreader_read_unary_unsigned(br, &msbs)) + return false; + + /* read the binary LSBs */ + if(!FLAC__bitreader_read_raw_uint32(br, &lsbs, k)) + return false; + + if(parameter == 1u<= d) { + if(!FLAC__bitreader_read_bit(br, &bit)) + return false; + lsbs <<= 1; + lsbs |= bit; + lsbs -= d; + } + /* compose the value */ + uval = msbs * parameter + lsbs; + } + + /* unfold unsigned to signed */ + if(uval & 1) + *val = -((int)(uval >> 1)) - 1; + else + *val = (int)(uval >> 1); + + return true; +} + +FLAC__bool FLAC__bitreader_read_golomb_unsigned(FLAC__BitReader *br, unsigned *val, unsigned parameter) +{ + FLAC__uint32 lsbs, msbs = 0; + unsigned bit, k; + + FLAC__ASSERT(0 != br); + FLAC__ASSERT(0 != br->buffer); + + k = FLAC__bitmath_ilog2(parameter); + + /* read the unary MSBs and end bit */ + if(!FLAC__bitreader_read_unary_unsigned(br, &msbs)) + return false; + + /* read the binary LSBs */ + if(!FLAC__bitreader_read_raw_uint32(br, &lsbs, k)) + return false; + + if(parameter == 1u<= d) { + if(!FLAC__bitreader_read_bit(br, &bit)) + return false; + lsbs <<= 1; + lsbs |= bit; + lsbs -= d; + } + /* compose the value */ + *val = msbs * parameter + lsbs; + } + + return true; +} +#endif /* UNUSED */ + +/* on return, if *val == 0xffffffff then the utf-8 sequence was invalid, but the return value will be true */ +FLAC__bool FLAC__bitreader_read_utf8_uint32(FLAC__BitReader *br, FLAC__uint32 *val, FLAC__byte *raw, unsigned *rawlen) +{ + FLAC__uint32 v = 0; + FLAC__uint32 x; + unsigned i; + + if(!FLAC__bitreader_read_raw_uint32(br, &x, 8)) + return false; + if(raw) + raw[(*rawlen)++] = (FLAC__byte)x; + if(!(x & 0x80)) { /* 0xxxxxxx */ + v = x; + i = 0; + } + else if(x & 0xC0 && !(x & 0x20)) { /* 110xxxxx */ + v = x & 0x1F; + i = 1; + } + else if(x & 0xE0 && !(x & 0x10)) { /* 1110xxxx */ + v = x & 0x0F; + i = 2; + } + else if(x & 0xF0 && !(x & 0x08)) { /* 11110xxx */ + v = x & 0x07; + i = 3; + } + else if(x & 0xF8 && !(x & 0x04)) { /* 111110xx */ + v = x & 0x03; + i = 4; + } + else if(x & 0xFC && !(x & 0x02)) { /* 1111110x */ + v = x & 0x01; + i = 5; + } + else { + *val = 0xffffffff; + return true; + } + for( ; i; i--) { + if(!FLAC__bitreader_read_raw_uint32(br, &x, 8)) + return false; + if(raw) + raw[(*rawlen)++] = (FLAC__byte)x; + if(!(x & 0x80) || (x & 0x40)) { /* 10xxxxxx */ + *val = 0xffffffff; + return true; + } + v <<= 6; + v |= (x & 0x3F); + } + *val = v; + return true; +} + +/* on return, if *val == 0xffffffffffffffff then the utf-8 sequence was invalid, but the return value will be true */ +FLAC__bool FLAC__bitreader_read_utf8_uint64(FLAC__BitReader *br, FLAC__uint64 *val, FLAC__byte *raw, unsigned *rawlen) +{ + FLAC__uint64 v = 0; + FLAC__uint32 x; + unsigned i; + + if(!FLAC__bitreader_read_raw_uint32(br, &x, 8)) + return false; + if(raw) + raw[(*rawlen)++] = (FLAC__byte)x; + if(!(x & 0x80)) { /* 0xxxxxxx */ + v = x; + i = 0; + } + else if(x & 0xC0 && !(x & 0x20)) { /* 110xxxxx */ + v = x & 0x1F; + i = 1; + } + else if(x & 0xE0 && !(x & 0x10)) { /* 1110xxxx */ + v = x & 0x0F; + i = 2; + } + else if(x & 0xF0 && !(x & 0x08)) { /* 11110xxx */ + v = x & 0x07; + i = 3; + } + else if(x & 0xF8 && !(x & 0x04)) { /* 111110xx */ + v = x & 0x03; + i = 4; + } + else if(x & 0xFC && !(x & 0x02)) { /* 1111110x */ + v = x & 0x01; + i = 5; + } + else if(x & 0xFE && !(x & 0x01)) { /* 11111110 */ + v = 0; + i = 6; + } + else { + *val = FLAC__U64L(0xffffffffffffffff); + return true; + } + for( ; i; i--) { + if(!FLAC__bitreader_read_raw_uint32(br, &x, 8)) + return false; + if(raw) + raw[(*rawlen)++] = (FLAC__byte)x; + if(!(x & 0x80) || (x & 0x40)) { /* 10xxxxxx */ + *val = FLAC__U64L(0xffffffffffffffff); + return true; + } + v <<= 6; + v |= (x & 0x3F); + } + *val = v; + return true; +} + +/* These functions are declared inline in this file but are also callable as + * externs from elsewhere. + * According to the C99 spec, section 6.7.4, simply providing a function + * prototype in a header file without 'inline' and making the function inline + * in this file should be sufficient. + * Unfortunately, the Microsoft VS compiler doesn't pick them up externally. To + * fix that we add extern declarations here. + */ +extern FLAC__bool FLAC__bitreader_is_consumed_byte_aligned(const FLAC__BitReader *br); +extern unsigned FLAC__bitreader_bits_left_for_byte_alignment(const FLAC__BitReader *br); +extern unsigned FLAC__bitreader_get_input_bits_unconsumed(const FLAC__BitReader *br); +extern FLAC__bool FLAC__bitreader_read_uint32_little_endian(FLAC__BitReader *br, FLAC__uint32 *val); diff --git a/core/deps/flac/src/libFLAC/cpu.c b/core/deps/flac/src/libFLAC/cpu.c new file mode 100644 index 000000000..b9df19a95 --- /dev/null +++ b/core/deps/flac/src/libFLAC/cpu.c @@ -0,0 +1,293 @@ +/* libFLAC - Free Lossless Audio Codec library + * Copyright (C) 2001-2009 Josh Coalson + * Copyright (C) 2011-2016 Xiph.Org Foundation + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * - Neither the name of the Xiph.org Foundation nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifdef HAVE_CONFIG_H +# include +#endif + +#include "private/cpu.h" +#include "share/compat.h" +#include +#include + +#if defined(_MSC_VER) +# include /* for __cpuid() and _xgetbv() */ +#endif + +#if defined __GNUC__ && defined HAVE_CPUID_H +# include /* for __get_cpuid() and __get_cpuid_max() */ +#endif + +#ifdef DEBUG +#include + +#define dfprintf fprintf +#else +/* This is bad practice, it should be a static void empty function */ +#define dfprintf(file, format, ...) +#endif + + +#if defined FLAC__CPU_IA32 +/* these are flags in EDX of CPUID AX=00000001 */ +static const unsigned FLAC__CPUINFO_IA32_CPUID_CMOV = 0x00008000; +static const unsigned FLAC__CPUINFO_IA32_CPUID_MMX = 0x00800000; +static const unsigned FLAC__CPUINFO_IA32_CPUID_SSE = 0x02000000; +static const unsigned FLAC__CPUINFO_IA32_CPUID_SSE2 = 0x04000000; +#endif + +#if FLAC__HAS_X86INTRIN || FLAC__AVX_SUPPORTED +/* these are flags in ECX of CPUID AX=00000001 */ +static const unsigned FLAC__CPUINFO_IA32_CPUID_SSE3 = 0x00000001; +static const unsigned FLAC__CPUINFO_IA32_CPUID_SSSE3 = 0x00000200; +static const unsigned FLAC__CPUINFO_IA32_CPUID_SSE41 = 0x00080000; +static const unsigned FLAC__CPUINFO_IA32_CPUID_SSE42 = 0x00100000; + +/* these are flags in ECX of CPUID AX=00000001 */ +static const unsigned FLAC__CPUINFO_IA32_CPUID_OSXSAVE = 0x08000000; +static const unsigned FLAC__CPUINFO_IA32_CPUID_AVX = 0x10000000; +static const unsigned FLAC__CPUINFO_IA32_CPUID_FMA = 0x00001000; +/* these are flags in EBX of CPUID AX=00000007 */ +static const unsigned FLAC__CPUINFO_IA32_CPUID_AVX2 = 0x00000020; +#endif + +#if defined FLAC__CPU_IA32 || defined FLAC__CPU_X86_64 +static uint32_t +cpu_xgetbv_x86(void) +{ +#if (defined _MSC_VER || defined __INTEL_COMPILER) && FLAC__HAS_X86INTRIN && FLAC__AVX_SUPPORTED + return (uint32_t)_xgetbv(0); +#elif defined __GNUC__ + uint32_t lo, hi; + asm volatile (".byte 0x0f, 0x01, 0xd0" : "=a"(lo), "=d"(hi) : "c" (0)); + return lo; +#else + return 0; +#endif +} +#endif + +static void +ia32_cpu_info (FLAC__CPUInfo *info) +{ +#if !defined FLAC__CPU_IA32 + (void) info; +#else + FLAC__bool ia32_osxsave = false; + FLAC__uint32 flags_eax, flags_ebx, flags_ecx, flags_edx; + +#if !defined FLAC__NO_ASM && (defined FLAC__HAS_NASM || FLAC__HAS_X86INTRIN) + info->use_asm = true; /* we assume a minimum of 80386 with FLAC__CPU_IA32 */ +#if defined FLAC__HAS_NASM + if(!FLAC__cpu_have_cpuid_asm_ia32()) + return; +#endif + /* http://www.sandpile.org/x86/cpuid.htm */ + if (FLAC__HAS_X86INTRIN) { + FLAC__cpu_info_x86(0, &flags_eax, &flags_ebx, &flags_ecx, &flags_edx); + info->ia32.intel = (flags_ebx == 0x756E6547 && flags_edx == 0x49656E69 && flags_ecx == 0x6C65746E) ? true : false; /* GenuineIntel */ + FLAC__cpu_info_x86(1, &flags_eax, &flags_ebx, &flags_ecx, &flags_edx); + } + else { + FLAC__cpu_info_asm_ia32(&flags_edx, &flags_ecx); + } + + info->ia32.cmov = (flags_edx & FLAC__CPUINFO_IA32_CPUID_CMOV ) ? true : false; + info->ia32.mmx = (flags_edx & FLAC__CPUINFO_IA32_CPUID_MMX ) ? true : false; + info->ia32.sse = (flags_edx & FLAC__CPUINFO_IA32_CPUID_SSE ) ? true : false; + info->ia32.sse2 = (flags_edx & FLAC__CPUINFO_IA32_CPUID_SSE2 ) ? true : false; + info->ia32.sse3 = (flags_ecx & FLAC__CPUINFO_IA32_CPUID_SSE3 ) ? true : false; + info->ia32.ssse3 = (flags_ecx & FLAC__CPUINFO_IA32_CPUID_SSSE3) ? true : false; + info->ia32.sse41 = (flags_ecx & FLAC__CPUINFO_IA32_CPUID_SSE41) ? true : false; + info->ia32.sse42 = (flags_ecx & FLAC__CPUINFO_IA32_CPUID_SSE42) ? true : false; + + if (FLAC__HAS_X86INTRIN && FLAC__AVX_SUPPORTED) { + ia32_osxsave = (flags_ecx & FLAC__CPUINFO_IA32_CPUID_OSXSAVE) ? true : false; + info->ia32.avx = (flags_ecx & FLAC__CPUINFO_IA32_CPUID_AVX ) ? true : false; + info->ia32.fma = (flags_ecx & FLAC__CPUINFO_IA32_CPUID_FMA ) ? true : false; + FLAC__cpu_info_x86(7, &flags_eax, &flags_ebx, &flags_ecx, &flags_edx); + info->ia32.avx2 = (flags_ebx & FLAC__CPUINFO_IA32_CPUID_AVX2 ) ? true : false; + } + + dfprintf(stderr, "CPU info (IA-32):\n"); + dfprintf(stderr, " CMOV ....... %c\n", info->ia32.cmov ? 'Y' : 'n'); + dfprintf(stderr, " MMX ........ %c\n", info->ia32.mmx ? 'Y' : 'n'); + dfprintf(stderr, " SSE ........ %c\n", info->ia32.sse ? 'Y' : 'n'); + dfprintf(stderr, " SSE2 ....... %c\n", info->ia32.sse2 ? 'Y' : 'n'); + dfprintf(stderr, " SSE3 ....... %c\n", info->ia32.sse3 ? 'Y' : 'n'); + dfprintf(stderr, " SSSE3 ...... %c\n", info->ia32.ssse3 ? 'Y' : 'n'); + dfprintf(stderr, " SSE41 ...... %c\n", info->ia32.sse41 ? 'Y' : 'n'); + dfprintf(stderr, " SSE42 ...... %c\n", info->ia32.sse42 ? 'Y' : 'n'); + + if (FLAC__HAS_X86INTRIN && FLAC__AVX_SUPPORTED) { + dfprintf(stderr, " AVX ........ %c\n", info->ia32.avx ? 'Y' : 'n'); + dfprintf(stderr, " FMA ........ %c\n", info->ia32.fma ? 'Y' : 'n'); + dfprintf(stderr, " AVX2 ....... %c\n", info->ia32.avx2 ? 'Y' : 'n'); + } + + /* + * now have to check for OS support of AVX instructions + */ + if (!FLAC__HAS_X86INTRIN || !info->ia32.avx || !ia32_osxsave || (cpu_xgetbv_x86() & 0x6) != 0x6) { + /* no OS AVX support */ + info->ia32.avx = false; + info->ia32.avx2 = false; + info->ia32.fma = false; + } + + if (FLAC__HAS_X86INTRIN && FLAC__AVX_SUPPORTED) { + dfprintf(stderr, " AVX OS sup . %c\n", info->ia32.avx ? 'Y' : 'n'); + } +#else + info->use_asm = false; +#endif +#endif +} + +static void +x86_64_cpu_info (FLAC__CPUInfo *info) +{ +#if !defined FLAC__NO_ASM && FLAC__HAS_X86INTRIN + FLAC__bool x86_osxsave = false; + FLAC__uint32 flags_eax, flags_ebx, flags_ecx, flags_edx; + + info->use_asm = true; + + /* http://www.sandpile.org/x86/cpuid.htm */ + FLAC__cpu_info_x86(0, &flags_eax, &flags_ebx, &flags_ecx, &flags_edx); + info->x86.intel = (flags_ebx == 0x756E6547 && flags_edx == 0x49656E69 && flags_ecx == 0x6C65746E) ? true : false; /* GenuineIntel */ + FLAC__cpu_info_x86(1, &flags_eax, &flags_ebx, &flags_ecx, &flags_edx); + info->x86.sse3 = (flags_ecx & FLAC__CPUINFO_IA32_CPUID_SSE3 ) ? true : false; + info->x86.ssse3 = (flags_ecx & FLAC__CPUINFO_IA32_CPUID_SSSE3) ? true : false; + info->x86.sse41 = (flags_ecx & FLAC__CPUINFO_IA32_CPUID_SSE41) ? true : false; + info->x86.sse42 = (flags_ecx & FLAC__CPUINFO_IA32_CPUID_SSE42) ? true : false; + + if (FLAC__AVX_SUPPORTED) { + x86_osxsave = (flags_ecx & FLAC__CPUINFO_IA32_CPUID_OSXSAVE) ? true : false; + info->x86.avx = (flags_ecx & FLAC__CPUINFO_IA32_CPUID_AVX ) ? true : false; + info->x86.fma = (flags_ecx & FLAC__CPUINFO_IA32_CPUID_FMA ) ? true : false; + FLAC__cpu_info_x86(7, &flags_eax, &flags_ebx, &flags_ecx, &flags_edx); + info->x86.avx2 = (flags_ebx & FLAC__CPUINFO_IA32_CPUID_AVX2 ) ? true : false; + } + + dfprintf(stderr, "CPU info (x86-64):\n"); + dfprintf(stderr, " SSE3 ....... %c\n", info->x86.sse3 ? 'Y' : 'n'); + dfprintf(stderr, " SSSE3 ...... %c\n", info->x86.ssse3 ? 'Y' : 'n'); + dfprintf(stderr, " SSE41 ...... %c\n", info->x86.sse41 ? 'Y' : 'n'); + dfprintf(stderr, " SSE42 ...... %c\n", info->x86.sse42 ? 'Y' : 'n'); + + if (FLAC__AVX_SUPPORTED) { + dfprintf(stderr, " AVX ........ %c\n", info->x86.avx ? 'Y' : 'n'); + dfprintf(stderr, " FMA ........ %c\n", info->x86.fma ? 'Y' : 'n'); + dfprintf(stderr, " AVX2 ....... %c\n", info->x86.avx2 ? 'Y' : 'n'); + } + + /* + * now have to check for OS support of AVX instructions + */ + if (!info->x86.avx || !x86_osxsave || (cpu_xgetbv_x86() & 0x6) != 0x6) { + /* no OS AVX support */ + info->x86.avx = false; + info->x86.avx2 = false; + info->x86.fma = false; + } + + if (FLAC__AVX_SUPPORTED) { + dfprintf(stderr, " AVX OS sup . %c\n", info->x86.avx ? 'Y' : 'n'); + } +#else + /* Silence compiler warnings. */ + (void) info; +#if defined FLAC__CPU_IA32 || defined FLAC__CPU_X86_64 + if (0) cpu_xgetbv_x86 (); +#endif +#endif +} + +void FLAC__cpu_info (FLAC__CPUInfo *info) +{ + memset(info, 0, sizeof(*info)); + +#ifdef FLAC__CPU_IA32 + info->type = FLAC__CPUINFO_TYPE_IA32; +#elif defined FLAC__CPU_X86_64 + info->type = FLAC__CPUINFO_TYPE_X86_64; +#else + info->type = FLAC__CPUINFO_TYPE_UNKNOWN; + info->use_asm = false; +#endif + + switch (info->type) { + case FLAC__CPUINFO_TYPE_IA32: + ia32_cpu_info (info); + break; + case FLAC__CPUINFO_TYPE_X86_64: + x86_64_cpu_info (info); + break; + default: + info->use_asm = false; + break; + } +} + +#if (defined FLAC__CPU_IA32 || defined FLAC__CPU_X86_64) && FLAC__HAS_X86INTRIN + +void FLAC__cpu_info_x86(FLAC__uint32 level, FLAC__uint32 *eax, FLAC__uint32 *ebx, FLAC__uint32 *ecx, FLAC__uint32 *edx) +{ +#if defined _MSC_VER || defined __INTEL_COMPILER + int cpuinfo[4]; + int ext = level & 0x80000000; + __cpuid(cpuinfo, ext); + if((unsigned)cpuinfo[0] >= level) { +#if FLAC__AVX_SUPPORTED + __cpuidex(cpuinfo, ext, 0); /* for AVX2 detection */ +#else + __cpuid(cpuinfo, ext); /* some old compilers don't support __cpuidex */ +#endif + + *eax = cpuinfo[0]; *ebx = cpuinfo[1]; *ecx = cpuinfo[2]; *edx = cpuinfo[3]; + + return; + } +#elif defined __GNUC__ && defined HAVE_CPUID_H + FLAC__uint32 ext = level & 0x80000000; + __cpuid(ext, *eax, *ebx, *ecx, *edx); + if (*eax >= level) { + __cpuid_count(level, 0, *eax, *ebx, *ecx, *edx); + + return; + } +#endif + *eax = *ebx = *ecx = *edx = 0; +} + +#endif /* (FLAC__CPU_IA32 || FLAC__CPU_X86_64) && FLAC__HAS_X86INTRIN */ diff --git a/core/deps/flac/src/libFLAC/crc.c b/core/deps/flac/src/libFLAC/crc.c new file mode 100644 index 000000000..8123c3b69 --- /dev/null +++ b/core/deps/flac/src/libFLAC/crc.c @@ -0,0 +1,143 @@ +/* libFLAC - Free Lossless Audio Codec library + * Copyright (C) 2000-2009 Josh Coalson + * Copyright (C) 2011-2016 Xiph.Org Foundation + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * - Neither the name of the Xiph.org Foundation nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifdef HAVE_CONFIG_H +# include +#endif + +#include "private/crc.h" + +/* CRC-8, poly = x^8 + x^2 + x^1 + x^0, init = 0 */ + +FLAC__byte const FLAC__crc8_table[256] = { + 0x00, 0x07, 0x0E, 0x09, 0x1C, 0x1B, 0x12, 0x15, + 0x38, 0x3F, 0x36, 0x31, 0x24, 0x23, 0x2A, 0x2D, + 0x70, 0x77, 0x7E, 0x79, 0x6C, 0x6B, 0x62, 0x65, + 0x48, 0x4F, 0x46, 0x41, 0x54, 0x53, 0x5A, 0x5D, + 0xE0, 0xE7, 0xEE, 0xE9, 0xFC, 0xFB, 0xF2, 0xF5, + 0xD8, 0xDF, 0xD6, 0xD1, 0xC4, 0xC3, 0xCA, 0xCD, + 0x90, 0x97, 0x9E, 0x99, 0x8C, 0x8B, 0x82, 0x85, + 0xA8, 0xAF, 0xA6, 0xA1, 0xB4, 0xB3, 0xBA, 0xBD, + 0xC7, 0xC0, 0xC9, 0xCE, 0xDB, 0xDC, 0xD5, 0xD2, + 0xFF, 0xF8, 0xF1, 0xF6, 0xE3, 0xE4, 0xED, 0xEA, + 0xB7, 0xB0, 0xB9, 0xBE, 0xAB, 0xAC, 0xA5, 0xA2, + 0x8F, 0x88, 0x81, 0x86, 0x93, 0x94, 0x9D, 0x9A, + 0x27, 0x20, 0x29, 0x2E, 0x3B, 0x3C, 0x35, 0x32, + 0x1F, 0x18, 0x11, 0x16, 0x03, 0x04, 0x0D, 0x0A, + 0x57, 0x50, 0x59, 0x5E, 0x4B, 0x4C, 0x45, 0x42, + 0x6F, 0x68, 0x61, 0x66, 0x73, 0x74, 0x7D, 0x7A, + 0x89, 0x8E, 0x87, 0x80, 0x95, 0x92, 0x9B, 0x9C, + 0xB1, 0xB6, 0xBF, 0xB8, 0xAD, 0xAA, 0xA3, 0xA4, + 0xF9, 0xFE, 0xF7, 0xF0, 0xE5, 0xE2, 0xEB, 0xEC, + 0xC1, 0xC6, 0xCF, 0xC8, 0xDD, 0xDA, 0xD3, 0xD4, + 0x69, 0x6E, 0x67, 0x60, 0x75, 0x72, 0x7B, 0x7C, + 0x51, 0x56, 0x5F, 0x58, 0x4D, 0x4A, 0x43, 0x44, + 0x19, 0x1E, 0x17, 0x10, 0x05, 0x02, 0x0B, 0x0C, + 0x21, 0x26, 0x2F, 0x28, 0x3D, 0x3A, 0x33, 0x34, + 0x4E, 0x49, 0x40, 0x47, 0x52, 0x55, 0x5C, 0x5B, + 0x76, 0x71, 0x78, 0x7F, 0x6A, 0x6D, 0x64, 0x63, + 0x3E, 0x39, 0x30, 0x37, 0x22, 0x25, 0x2C, 0x2B, + 0x06, 0x01, 0x08, 0x0F, 0x1A, 0x1D, 0x14, 0x13, + 0xAE, 0xA9, 0xA0, 0xA7, 0xB2, 0xB5, 0xBC, 0xBB, + 0x96, 0x91, 0x98, 0x9F, 0x8A, 0x8D, 0x84, 0x83, + 0xDE, 0xD9, 0xD0, 0xD7, 0xC2, 0xC5, 0xCC, 0xCB, + 0xE6, 0xE1, 0xE8, 0xEF, 0xFA, 0xFD, 0xF4, 0xF3 +}; + +/* CRC-16, poly = x^16 + x^15 + x^2 + x^0, init = 0 */ + +unsigned const FLAC__crc16_table[256] = { + 0x0000, 0x8005, 0x800f, 0x000a, 0x801b, 0x001e, 0x0014, 0x8011, + 0x8033, 0x0036, 0x003c, 0x8039, 0x0028, 0x802d, 0x8027, 0x0022, + 0x8063, 0x0066, 0x006c, 0x8069, 0x0078, 0x807d, 0x8077, 0x0072, + 0x0050, 0x8055, 0x805f, 0x005a, 0x804b, 0x004e, 0x0044, 0x8041, + 0x80c3, 0x00c6, 0x00cc, 0x80c9, 0x00d8, 0x80dd, 0x80d7, 0x00d2, + 0x00f0, 0x80f5, 0x80ff, 0x00fa, 0x80eb, 0x00ee, 0x00e4, 0x80e1, + 0x00a0, 0x80a5, 0x80af, 0x00aa, 0x80bb, 0x00be, 0x00b4, 0x80b1, + 0x8093, 0x0096, 0x009c, 0x8099, 0x0088, 0x808d, 0x8087, 0x0082, + 0x8183, 0x0186, 0x018c, 0x8189, 0x0198, 0x819d, 0x8197, 0x0192, + 0x01b0, 0x81b5, 0x81bf, 0x01ba, 0x81ab, 0x01ae, 0x01a4, 0x81a1, + 0x01e0, 0x81e5, 0x81ef, 0x01ea, 0x81fb, 0x01fe, 0x01f4, 0x81f1, + 0x81d3, 0x01d6, 0x01dc, 0x81d9, 0x01c8, 0x81cd, 0x81c7, 0x01c2, + 0x0140, 0x8145, 0x814f, 0x014a, 0x815b, 0x015e, 0x0154, 0x8151, + 0x8173, 0x0176, 0x017c, 0x8179, 0x0168, 0x816d, 0x8167, 0x0162, + 0x8123, 0x0126, 0x012c, 0x8129, 0x0138, 0x813d, 0x8137, 0x0132, + 0x0110, 0x8115, 0x811f, 0x011a, 0x810b, 0x010e, 0x0104, 0x8101, + 0x8303, 0x0306, 0x030c, 0x8309, 0x0318, 0x831d, 0x8317, 0x0312, + 0x0330, 0x8335, 0x833f, 0x033a, 0x832b, 0x032e, 0x0324, 0x8321, + 0x0360, 0x8365, 0x836f, 0x036a, 0x837b, 0x037e, 0x0374, 0x8371, + 0x8353, 0x0356, 0x035c, 0x8359, 0x0348, 0x834d, 0x8347, 0x0342, + 0x03c0, 0x83c5, 0x83cf, 0x03ca, 0x83db, 0x03de, 0x03d4, 0x83d1, + 0x83f3, 0x03f6, 0x03fc, 0x83f9, 0x03e8, 0x83ed, 0x83e7, 0x03e2, + 0x83a3, 0x03a6, 0x03ac, 0x83a9, 0x03b8, 0x83bd, 0x83b7, 0x03b2, + 0x0390, 0x8395, 0x839f, 0x039a, 0x838b, 0x038e, 0x0384, 0x8381, + 0x0280, 0x8285, 0x828f, 0x028a, 0x829b, 0x029e, 0x0294, 0x8291, + 0x82b3, 0x02b6, 0x02bc, 0x82b9, 0x02a8, 0x82ad, 0x82a7, 0x02a2, + 0x82e3, 0x02e6, 0x02ec, 0x82e9, 0x02f8, 0x82fd, 0x82f7, 0x02f2, + 0x02d0, 0x82d5, 0x82df, 0x02da, 0x82cb, 0x02ce, 0x02c4, 0x82c1, + 0x8243, 0x0246, 0x024c, 0x8249, 0x0258, 0x825d, 0x8257, 0x0252, + 0x0270, 0x8275, 0x827f, 0x027a, 0x826b, 0x026e, 0x0264, 0x8261, + 0x0220, 0x8225, 0x822f, 0x022a, 0x823b, 0x023e, 0x0234, 0x8231, + 0x8213, 0x0216, 0x021c, 0x8219, 0x0208, 0x820d, 0x8207, 0x0202 +}; + + +void FLAC__crc8_update(const FLAC__byte data, FLAC__uint8 *crc) +{ + *crc = FLAC__crc8_table[*crc ^ data]; +} + +void FLAC__crc8_update_block(const FLAC__byte *data, unsigned len, FLAC__uint8 *crc) +{ + while(len--) + *crc = FLAC__crc8_table[*crc ^ *data++]; +} + +FLAC__uint8 FLAC__crc8(const FLAC__byte *data, unsigned len) +{ + FLAC__uint8 crc = 0; + + while(len--) + crc = FLAC__crc8_table[crc ^ *data++]; + + return crc; +} + +unsigned FLAC__crc16(const FLAC__byte *data, unsigned len) +{ + unsigned crc = 0; + + while(len--) + crc = ((crc<<8) ^ FLAC__crc16_table[(crc>>8) ^ *data++]) & 0xffff; + + return crc; +} diff --git a/core/deps/flac/src/libFLAC/fixed.c b/core/deps/flac/src/libFLAC/fixed.c new file mode 100644 index 000000000..1e2d5b284 --- /dev/null +++ b/core/deps/flac/src/libFLAC/fixed.c @@ -0,0 +1,395 @@ +/* libFLAC - Free Lossless Audio Codec library + * Copyright (C) 2000-2009 Josh Coalson + * Copyright (C) 2011-2016 Xiph.Org Foundation + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * - Neither the name of the Xiph.org Foundation nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifdef HAVE_CONFIG_H +# include +#endif + +#include +#include +#include "share/compat.h" +#include "private/bitmath.h" +#include "private/fixed.h" +#include "private/macros.h" +#include "FLAC/assert.h" + +#ifdef local_abs +#undef local_abs +#endif +#define local_abs(x) ((unsigned)((x)<0? -(x) : (x))) + +#ifdef FLAC__INTEGER_ONLY_LIBRARY +/* rbps stands for residual bits per sample + * + * (ln(2) * err) + * rbps = log (-----------) + * 2 ( n ) + */ +static FLAC__fixedpoint local__compute_rbps_integerized(FLAC__uint32 err, FLAC__uint32 n) +{ + FLAC__uint32 rbps; + unsigned bits; /* the number of bits required to represent a number */ + int fracbits; /* the number of bits of rbps that comprise the fractional part */ + + FLAC__ASSERT(sizeof(rbps) == sizeof(FLAC__fixedpoint)); + FLAC__ASSERT(err > 0); + FLAC__ASSERT(n > 0); + + FLAC__ASSERT(n <= FLAC__MAX_BLOCK_SIZE); + if(err <= n) + return 0; + /* + * The above two things tell us 1) n fits in 16 bits; 2) err/n > 1. + * These allow us later to know we won't lose too much precision in the + * fixed-point division (err< 0); + bits = FLAC__bitmath_ilog2(err)+1; + if(bits > 16) { + err >>= (bits-16); + fracbits -= (bits-16); + } + rbps = (FLAC__uint32)err; + + /* Multiply by fixed-point version of ln(2), with 16 fractional bits */ + rbps *= FLAC__FP_LN2; + fracbits += 16; + FLAC__ASSERT(fracbits >= 0); + + /* FLAC__fixedpoint_log2 requires fracbits%4 to be 0 */ + { + const int f = fracbits & 3; + if(f) { + rbps >>= f; + fracbits -= f; + } + } + + rbps = FLAC__fixedpoint_log2(rbps, fracbits, (unsigned)(-1)); + + if(rbps == 0) + return 0; + + /* + * The return value must have 16 fractional bits. Since the whole part + * of the base-2 log of a 32 bit number must fit in 5 bits, and fracbits + * must be >= -3, these assertion allows us to be able to shift rbps + * left if necessary to get 16 fracbits without losing any bits of the + * whole part of rbps. + * + * There is a slight chance due to accumulated error that the whole part + * will require 6 bits, so we use 6 in the assertion. Really though as + * long as it fits in 13 bits (32 - (16 - (-3))) we are fine. + */ + FLAC__ASSERT((int)FLAC__bitmath_ilog2(rbps)+1 <= fracbits + 6); + FLAC__ASSERT(fracbits >= -3); + + /* now shift the decimal point into place */ + if(fracbits < 16) + return rbps << (16-fracbits); + else if(fracbits > 16) + return rbps >> (fracbits-16); + else + return rbps; +} + +static FLAC__fixedpoint local__compute_rbps_wide_integerized(FLAC__uint64 err, FLAC__uint32 n) +{ + FLAC__uint32 rbps; + unsigned bits; /* the number of bits required to represent a number */ + int fracbits; /* the number of bits of rbps that comprise the fractional part */ + + FLAC__ASSERT(sizeof(rbps) == sizeof(FLAC__fixedpoint)); + FLAC__ASSERT(err > 0); + FLAC__ASSERT(n > 0); + + FLAC__ASSERT(n <= FLAC__MAX_BLOCK_SIZE); + if(err <= n) + return 0; + /* + * The above two things tell us 1) n fits in 16 bits; 2) err/n > 1. + * These allow us later to know we won't lose too much precision in the + * fixed-point division (err< 0); + bits = FLAC__bitmath_ilog2_wide(err)+1; + if(bits > 16) { + err >>= (bits-16); + fracbits -= (bits-16); + } + rbps = (FLAC__uint32)err; + + /* Multiply by fixed-point version of ln(2), with 16 fractional bits */ + rbps *= FLAC__FP_LN2; + fracbits += 16; + FLAC__ASSERT(fracbits >= 0); + + /* FLAC__fixedpoint_log2 requires fracbits%4 to be 0 */ + { + const int f = fracbits & 3; + if(f) { + rbps >>= f; + fracbits -= f; + } + } + + rbps = FLAC__fixedpoint_log2(rbps, fracbits, (unsigned)(-1)); + + if(rbps == 0) + return 0; + + /* + * The return value must have 16 fractional bits. Since the whole part + * of the base-2 log of a 32 bit number must fit in 5 bits, and fracbits + * must be >= -3, these assertion allows us to be able to shift rbps + * left if necessary to get 16 fracbits without losing any bits of the + * whole part of rbps. + * + * There is a slight chance due to accumulated error that the whole part + * will require 6 bits, so we use 6 in the assertion. Really though as + * long as it fits in 13 bits (32 - (16 - (-3))) we are fine. + */ + FLAC__ASSERT((int)FLAC__bitmath_ilog2(rbps)+1 <= fracbits + 6); + FLAC__ASSERT(fracbits >= -3); + + /* now shift the decimal point into place */ + if(fracbits < 16) + return rbps << (16-fracbits); + else if(fracbits > 16) + return rbps >> (fracbits-16); + else + return rbps; +} +#endif + +#ifndef FLAC__INTEGER_ONLY_LIBRARY +unsigned FLAC__fixed_compute_best_predictor(const FLAC__int32 data[], unsigned data_len, float residual_bits_per_sample[FLAC__MAX_FIXED_ORDER+1]) +#else +unsigned FLAC__fixed_compute_best_predictor(const FLAC__int32 data[], unsigned data_len, FLAC__fixedpoint residual_bits_per_sample[FLAC__MAX_FIXED_ORDER+1]) +#endif +{ + FLAC__int32 last_error_0 = data[-1]; + FLAC__int32 last_error_1 = data[-1] - data[-2]; + FLAC__int32 last_error_2 = last_error_1 - (data[-2] - data[-3]); + FLAC__int32 last_error_3 = last_error_2 - (data[-2] - 2*data[-3] + data[-4]); + FLAC__int32 error, save; + FLAC__uint32 total_error_0 = 0, total_error_1 = 0, total_error_2 = 0, total_error_3 = 0, total_error_4 = 0; + unsigned i, order; + + for(i = 0; i < data_len; i++) { + error = data[i] ; total_error_0 += local_abs(error); save = error; + error -= last_error_0; total_error_1 += local_abs(error); last_error_0 = save; save = error; + error -= last_error_1; total_error_2 += local_abs(error); last_error_1 = save; save = error; + error -= last_error_2; total_error_3 += local_abs(error); last_error_2 = save; save = error; + error -= last_error_3; total_error_4 += local_abs(error); last_error_3 = save; + } + + if(total_error_0 < flac_min(flac_min(flac_min(total_error_1, total_error_2), total_error_3), total_error_4)) + order = 0; + else if(total_error_1 < flac_min(flac_min(total_error_2, total_error_3), total_error_4)) + order = 1; + else if(total_error_2 < flac_min(total_error_3, total_error_4)) + order = 2; + else if(total_error_3 < total_error_4) + order = 3; + else + order = 4; + + /* Estimate the expected number of bits per residual signal sample. */ + /* 'total_error*' is linearly related to the variance of the residual */ + /* signal, so we use it directly to compute E(|x|) */ + FLAC__ASSERT(data_len > 0 || total_error_0 == 0); + FLAC__ASSERT(data_len > 0 || total_error_1 == 0); + FLAC__ASSERT(data_len > 0 || total_error_2 == 0); + FLAC__ASSERT(data_len > 0 || total_error_3 == 0); + FLAC__ASSERT(data_len > 0 || total_error_4 == 0); +#ifndef FLAC__INTEGER_ONLY_LIBRARY + residual_bits_per_sample[0] = (float)((total_error_0 > 0) ? log(M_LN2 * (double)total_error_0 / (double)data_len) / M_LN2 : 0.0); + residual_bits_per_sample[1] = (float)((total_error_1 > 0) ? log(M_LN2 * (double)total_error_1 / (double)data_len) / M_LN2 : 0.0); + residual_bits_per_sample[2] = (float)((total_error_2 > 0) ? log(M_LN2 * (double)total_error_2 / (double)data_len) / M_LN2 : 0.0); + residual_bits_per_sample[3] = (float)((total_error_3 > 0) ? log(M_LN2 * (double)total_error_3 / (double)data_len) / M_LN2 : 0.0); + residual_bits_per_sample[4] = (float)((total_error_4 > 0) ? log(M_LN2 * (double)total_error_4 / (double)data_len) / M_LN2 : 0.0); +#else + residual_bits_per_sample[0] = (total_error_0 > 0) ? local__compute_rbps_integerized(total_error_0, data_len) : 0; + residual_bits_per_sample[1] = (total_error_1 > 0) ? local__compute_rbps_integerized(total_error_1, data_len) : 0; + residual_bits_per_sample[2] = (total_error_2 > 0) ? local__compute_rbps_integerized(total_error_2, data_len) : 0; + residual_bits_per_sample[3] = (total_error_3 > 0) ? local__compute_rbps_integerized(total_error_3, data_len) : 0; + residual_bits_per_sample[4] = (total_error_4 > 0) ? local__compute_rbps_integerized(total_error_4, data_len) : 0; +#endif + + return order; +} + +#ifndef FLAC__INTEGER_ONLY_LIBRARY +unsigned FLAC__fixed_compute_best_predictor_wide(const FLAC__int32 data[], unsigned data_len, float residual_bits_per_sample[FLAC__MAX_FIXED_ORDER+1]) +#else +unsigned FLAC__fixed_compute_best_predictor_wide(const FLAC__int32 data[], unsigned data_len, FLAC__fixedpoint residual_bits_per_sample[FLAC__MAX_FIXED_ORDER+1]) +#endif +{ + FLAC__int32 last_error_0 = data[-1]; + FLAC__int32 last_error_1 = data[-1] - data[-2]; + FLAC__int32 last_error_2 = last_error_1 - (data[-2] - data[-3]); + FLAC__int32 last_error_3 = last_error_2 - (data[-2] - 2*data[-3] + data[-4]); + FLAC__int32 error, save; + /* total_error_* are 64-bits to avoid overflow when encoding + * erratic signals when the bits-per-sample and blocksize are + * large. + */ + FLAC__uint64 total_error_0 = 0, total_error_1 = 0, total_error_2 = 0, total_error_3 = 0, total_error_4 = 0; + unsigned i, order; + + for(i = 0; i < data_len; i++) { + error = data[i] ; total_error_0 += local_abs(error); save = error; + error -= last_error_0; total_error_1 += local_abs(error); last_error_0 = save; save = error; + error -= last_error_1; total_error_2 += local_abs(error); last_error_1 = save; save = error; + error -= last_error_2; total_error_3 += local_abs(error); last_error_2 = save; save = error; + error -= last_error_3; total_error_4 += local_abs(error); last_error_3 = save; + } + + if(total_error_0 < flac_min(flac_min(flac_min(total_error_1, total_error_2), total_error_3), total_error_4)) + order = 0; + else if(total_error_1 < flac_min(flac_min(total_error_2, total_error_3), total_error_4)) + order = 1; + else if(total_error_2 < flac_min(total_error_3, total_error_4)) + order = 2; + else if(total_error_3 < total_error_4) + order = 3; + else + order = 4; + + /* Estimate the expected number of bits per residual signal sample. */ + /* 'total_error*' is linearly related to the variance of the residual */ + /* signal, so we use it directly to compute E(|x|) */ + FLAC__ASSERT(data_len > 0 || total_error_0 == 0); + FLAC__ASSERT(data_len > 0 || total_error_1 == 0); + FLAC__ASSERT(data_len > 0 || total_error_2 == 0); + FLAC__ASSERT(data_len > 0 || total_error_3 == 0); + FLAC__ASSERT(data_len > 0 || total_error_4 == 0); +#ifndef FLAC__INTEGER_ONLY_LIBRARY + residual_bits_per_sample[0] = (float)((total_error_0 > 0) ? log(M_LN2 * (double)total_error_0 / (double)data_len) / M_LN2 : 0.0); + residual_bits_per_sample[1] = (float)((total_error_1 > 0) ? log(M_LN2 * (double)total_error_1 / (double)data_len) / M_LN2 : 0.0); + residual_bits_per_sample[2] = (float)((total_error_2 > 0) ? log(M_LN2 * (double)total_error_2 / (double)data_len) / M_LN2 : 0.0); + residual_bits_per_sample[3] = (float)((total_error_3 > 0) ? log(M_LN2 * (double)total_error_3 / (double)data_len) / M_LN2 : 0.0); + residual_bits_per_sample[4] = (float)((total_error_4 > 0) ? log(M_LN2 * (double)total_error_4 / (double)data_len) / M_LN2 : 0.0); +#else + residual_bits_per_sample[0] = (total_error_0 > 0) ? local__compute_rbps_wide_integerized(total_error_0, data_len) : 0; + residual_bits_per_sample[1] = (total_error_1 > 0) ? local__compute_rbps_wide_integerized(total_error_1, data_len) : 0; + residual_bits_per_sample[2] = (total_error_2 > 0) ? local__compute_rbps_wide_integerized(total_error_2, data_len) : 0; + residual_bits_per_sample[3] = (total_error_3 > 0) ? local__compute_rbps_wide_integerized(total_error_3, data_len) : 0; + residual_bits_per_sample[4] = (total_error_4 > 0) ? local__compute_rbps_wide_integerized(total_error_4, data_len) : 0; +#endif + + return order; +} + +void FLAC__fixed_compute_residual(const FLAC__int32 data[], unsigned data_len, unsigned order, FLAC__int32 residual[]) +{ + const int idata_len = (int)data_len; + int i; + + switch(order) { + case 0: + FLAC__ASSERT(sizeof(residual[0]) == sizeof(data[0])); + memcpy(residual, data, sizeof(residual[0])*data_len); + break; + case 1: + for(i = 0; i < idata_len; i++) + residual[i] = data[i] - data[i-1]; + break; + case 2: + for(i = 0; i < idata_len; i++) + residual[i] = data[i] - 2*data[i-1] + data[i-2]; + break; + case 3: + for(i = 0; i < idata_len; i++) + residual[i] = data[i] - 3*data[i-1] + 3*data[i-2] - data[i-3]; + break; + case 4: + for(i = 0; i < idata_len; i++) + residual[i] = data[i] - 4*data[i-1] + 6*data[i-2] - 4*data[i-3] + data[i-4]; + break; + default: + FLAC__ASSERT(0); + } +} + +void FLAC__fixed_restore_signal(const FLAC__int32 residual[], unsigned data_len, unsigned order, FLAC__int32 data[]) +{ + int i, idata_len = (int)data_len; + + switch(order) { + case 0: + FLAC__ASSERT(sizeof(residual[0]) == sizeof(data[0])); + memcpy(data, residual, sizeof(residual[0])*data_len); + break; + case 1: + for(i = 0; i < idata_len; i++) + data[i] = residual[i] + data[i-1]; + break; + case 2: + for(i = 0; i < idata_len; i++) + data[i] = residual[i] + 2*data[i-1] - data[i-2]; + break; + case 3: + for(i = 0; i < idata_len; i++) + data[i] = residual[i] + 3*data[i-1] - 3*data[i-2] + data[i-3]; + break; + case 4: + for(i = 0; i < idata_len; i++) + data[i] = residual[i] + 4*data[i-1] - 6*data[i-2] + 4*data[i-3] - data[i-4]; + break; + default: + FLAC__ASSERT(0); + } +} diff --git a/core/deps/flac/src/libFLAC/fixed_intrin_sse2.c b/core/deps/flac/src/libFLAC/fixed_intrin_sse2.c new file mode 100644 index 000000000..6a9b4dd0a --- /dev/null +++ b/core/deps/flac/src/libFLAC/fixed_intrin_sse2.c @@ -0,0 +1,255 @@ +/* libFLAC - Free Lossless Audio Codec library + * Copyright (C) 2000-2009 Josh Coalson + * Copyright (C) 2011-2016 Xiph.Org Foundation + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * - Neither the name of the Xiph.org Foundation nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifdef HAVE_CONFIG_H +# include +#endif + +#include "private/cpu.h" + +#ifndef FLAC__INTEGER_ONLY_LIBRARY +#ifndef FLAC__NO_ASM +#if (defined FLAC__CPU_IA32 || defined FLAC__CPU_X86_64) && defined FLAC__HAS_X86INTRIN +#include "private/fixed.h" +#ifdef FLAC__SSE2_SUPPORTED + +#include /* SSE2 */ +#include +#include "private/macros.h" +#include "share/compat.h" +#include "FLAC/assert.h" + +#ifdef FLAC__CPU_IA32 +#define m128i_to_i64(dest, src) _mm_storel_epi64((__m128i*)&dest, src) +#else +#define m128i_to_i64(dest, src) dest = _mm_cvtsi128_si64(src) +#endif + +FLAC__SSE_TARGET("sse2") +unsigned FLAC__fixed_compute_best_predictor_intrin_sse2(const FLAC__int32 data[], unsigned data_len, float residual_bits_per_sample[FLAC__MAX_FIXED_ORDER + 1]) +{ + FLAC__uint32 total_error_0, total_error_1, total_error_2, total_error_3, total_error_4; + unsigned i, order; + + __m128i total_err0, total_err1, total_err2; + + { + FLAC__int32 itmp; + __m128i last_error; + + last_error = _mm_cvtsi32_si128(data[-1]); // 0 0 0 le0 + itmp = data[-2]; + last_error = _mm_shuffle_epi32(last_error, _MM_SHUFFLE(2,1,0,0)); + last_error = _mm_sub_epi32(last_error, _mm_cvtsi32_si128(itmp)); // 0 0 le0 le1 + itmp -= data[-3]; + last_error = _mm_shuffle_epi32(last_error, _MM_SHUFFLE(2,1,0,0)); + last_error = _mm_sub_epi32(last_error, _mm_cvtsi32_si128(itmp)); // 0 le0 le1 le2 + itmp -= data[-3] - data[-4]; + last_error = _mm_shuffle_epi32(last_error, _MM_SHUFFLE(2,1,0,0)); + last_error = _mm_sub_epi32(last_error, _mm_cvtsi32_si128(itmp)); // le0 le1 le2 le3 + + total_err0 = total_err1 = _mm_setzero_si128(); + for(i = 0; i < data_len; i++) { + __m128i err0, err1, tmp; + err0 = _mm_cvtsi32_si128(data[i]); // 0 0 0 e0 + err1 = _mm_shuffle_epi32(err0, _MM_SHUFFLE(0,0,0,0)); // e0 e0 e0 e0 +#if 1 /* OPT_SSE */ + err1 = _mm_sub_epi32(err1, last_error); + last_error = _mm_srli_si128(last_error, 4); // 0 le0 le1 le2 + err1 = _mm_sub_epi32(err1, last_error); + last_error = _mm_srli_si128(last_error, 4); // 0 0 le0 le1 + err1 = _mm_sub_epi32(err1, last_error); + last_error = _mm_srli_si128(last_error, 4); // 0 0 0 le0 + err1 = _mm_sub_epi32(err1, last_error); // e1 e2 e3 e4 +#else + last_error = _mm_add_epi32(last_error, _mm_srli_si128(last_error, 8)); // le0 le1 le2+le0 le3+le1 + last_error = _mm_add_epi32(last_error, _mm_srli_si128(last_error, 4)); // le0 le1+le0 le2+le0+le1 le3+le1+le2+le0 + err1 = _mm_sub_epi32(err1, last_error); // e1 e2 e3 e4 +#endif + tmp = _mm_slli_si128(err0, 12); // e0 0 0 0 + last_error = _mm_srli_si128(err1, 4); // 0 e1 e2 e3 + last_error = _mm_or_si128(last_error, tmp); // e0 e1 e2 e3 + + tmp = _mm_srai_epi32(err0, 31); + err0 = _mm_xor_si128(err0, tmp); + err0 = _mm_sub_epi32(err0, tmp); + tmp = _mm_srai_epi32(err1, 31); + err1 = _mm_xor_si128(err1, tmp); + err1 = _mm_sub_epi32(err1, tmp); + + total_err0 = _mm_add_epi32(total_err0, err0); // 0 0 0 te0 + total_err1 = _mm_add_epi32(total_err1, err1); // te1 te2 te3 te4 + } + } + + total_error_0 = _mm_cvtsi128_si32(total_err0); + total_err2 = total_err1; // te1 te2 te3 te4 + total_err1 = _mm_srli_si128(total_err1, 8); // 0 0 te1 te2 + total_error_4 = _mm_cvtsi128_si32(total_err2); + total_error_2 = _mm_cvtsi128_si32(total_err1); + total_err2 = _mm_srli_si128(total_err2, 4); // 0 te1 te2 te3 + total_err1 = _mm_srli_si128(total_err1, 4); // 0 0 0 te1 + total_error_3 = _mm_cvtsi128_si32(total_err2); + total_error_1 = _mm_cvtsi128_si32(total_err1); + + /* prefer higher order */ + if(total_error_0 < flac_min(flac_min(flac_min(total_error_1, total_error_2), total_error_3), total_error_4)) + order = 0; + else if(total_error_1 < flac_min(flac_min(total_error_2, total_error_3), total_error_4)) + order = 1; + else if(total_error_2 < flac_min(total_error_3, total_error_4)) + order = 2; + else if(total_error_3 < total_error_4) + order = 3; + else + order = 4; + + /* Estimate the expected number of bits per residual signal sample. */ + /* 'total_error*' is linearly related to the variance of the residual */ + /* signal, so we use it directly to compute E(|x|) */ + FLAC__ASSERT(data_len > 0 || total_error_0 == 0); + FLAC__ASSERT(data_len > 0 || total_error_1 == 0); + FLAC__ASSERT(data_len > 0 || total_error_2 == 0); + FLAC__ASSERT(data_len > 0 || total_error_3 == 0); + FLAC__ASSERT(data_len > 0 || total_error_4 == 0); + + residual_bits_per_sample[0] = (float)((total_error_0 > 0) ? log(M_LN2 * (double)total_error_0 / (double)data_len) / M_LN2 : 0.0); + residual_bits_per_sample[1] = (float)((total_error_1 > 0) ? log(M_LN2 * (double)total_error_1 / (double)data_len) / M_LN2 : 0.0); + residual_bits_per_sample[2] = (float)((total_error_2 > 0) ? log(M_LN2 * (double)total_error_2 / (double)data_len) / M_LN2 : 0.0); + residual_bits_per_sample[3] = (float)((total_error_3 > 0) ? log(M_LN2 * (double)total_error_3 / (double)data_len) / M_LN2 : 0.0); + residual_bits_per_sample[4] = (float)((total_error_4 > 0) ? log(M_LN2 * (double)total_error_4 / (double)data_len) / M_LN2 : 0.0); + + return order; +} + +FLAC__SSE_TARGET("sse2") +unsigned FLAC__fixed_compute_best_predictor_wide_intrin_sse2(const FLAC__int32 data[], unsigned data_len, float residual_bits_per_sample[FLAC__MAX_FIXED_ORDER + 1]) +{ + FLAC__uint64 total_error_0, total_error_1, total_error_2, total_error_3, total_error_4; + unsigned i, order; + + __m128i total_err0, total_err1, total_err3; + + { + FLAC__int32 itmp; + __m128i last_error, zero = _mm_setzero_si128(); + + last_error = _mm_cvtsi32_si128(data[-1]); // 0 0 0 le0 + itmp = data[-2]; + last_error = _mm_shuffle_epi32(last_error, _MM_SHUFFLE(2,1,0,0)); + last_error = _mm_sub_epi32(last_error, _mm_cvtsi32_si128(itmp)); // 0 0 le0 le1 + itmp -= data[-3]; + last_error = _mm_shuffle_epi32(last_error, _MM_SHUFFLE(2,1,0,0)); + last_error = _mm_sub_epi32(last_error, _mm_cvtsi32_si128(itmp)); // 0 le0 le1 le2 + itmp -= data[-3] - data[-4]; + last_error = _mm_shuffle_epi32(last_error, _MM_SHUFFLE(2,1,0,0)); + last_error = _mm_sub_epi32(last_error, _mm_cvtsi32_si128(itmp)); // le0 le1 le2 le3 + + total_err0 = total_err1 = total_err3 = _mm_setzero_si128(); + for(i = 0; i < data_len; i++) { + __m128i err0, err1, tmp; + err0 = _mm_cvtsi32_si128(data[i]); // 0 0 0 e0 + err1 = _mm_shuffle_epi32(err0, _MM_SHUFFLE(0,0,0,0)); // e0 e0 e0 e0 +#if 1 /* OPT_SSE */ + err1 = _mm_sub_epi32(err1, last_error); + last_error = _mm_srli_si128(last_error, 4); // 0 le0 le1 le2 + err1 = _mm_sub_epi32(err1, last_error); + last_error = _mm_srli_si128(last_error, 4); // 0 0 le0 le1 + err1 = _mm_sub_epi32(err1, last_error); + last_error = _mm_srli_si128(last_error, 4); // 0 0 0 le0 + err1 = _mm_sub_epi32(err1, last_error); // e1 e2 e3 e4 +#else + last_error = _mm_add_epi32(last_error, _mm_srli_si128(last_error, 8)); // le0 le1 le2+le0 le3+le1 + last_error = _mm_add_epi32(last_error, _mm_srli_si128(last_error, 4)); // le0 le1+le0 le2+le0+le1 le3+le1+le2+le0 + err1 = _mm_sub_epi32(err1, last_error); // e1 e2 e3 e4 +#endif + tmp = _mm_slli_si128(err0, 12); // e0 0 0 0 + last_error = _mm_srli_si128(err1, 4); // 0 e1 e2 e3 + last_error = _mm_or_si128(last_error, tmp); // e0 e1 e2 e3 + + tmp = _mm_srai_epi32(err0, 31); + err0 = _mm_xor_si128(err0, tmp); + err0 = _mm_sub_epi32(err0, tmp); + tmp = _mm_srai_epi32(err1, 31); + err1 = _mm_xor_si128(err1, tmp); + err1 = _mm_sub_epi32(err1, tmp); + + total_err0 = _mm_add_epi64(total_err0, err0); // 0 te0 + err0 = _mm_unpacklo_epi32(err1, zero); // 0 |e3| 0 |e4| + err1 = _mm_unpackhi_epi32(err1, zero); // 0 |e1| 0 |e2| + total_err3 = _mm_add_epi64(total_err3, err0); // te3 te4 + total_err1 = _mm_add_epi64(total_err1, err1); // te1 te2 + } + } + + m128i_to_i64(total_error_0, total_err0); + m128i_to_i64(total_error_4, total_err3); + m128i_to_i64(total_error_2, total_err1); + total_err3 = _mm_srli_si128(total_err3, 8); // 0 te3 + total_err1 = _mm_srli_si128(total_err1, 8); // 0 te1 + m128i_to_i64(total_error_3, total_err3); + m128i_to_i64(total_error_1, total_err1); + + /* prefer higher order */ + if(total_error_0 < flac_min(flac_min(flac_min(total_error_1, total_error_2), total_error_3), total_error_4)) + order = 0; + else if(total_error_1 < flac_min(flac_min(total_error_2, total_error_3), total_error_4)) + order = 1; + else if(total_error_2 < flac_min(total_error_3, total_error_4)) + order = 2; + else if(total_error_3 < total_error_4) + order = 3; + else + order = 4; + + /* Estimate the expected number of bits per residual signal sample. */ + /* 'total_error*' is linearly related to the variance of the residual */ + /* signal, so we use it directly to compute E(|x|) */ + FLAC__ASSERT(data_len > 0 || total_error_0 == 0); + FLAC__ASSERT(data_len > 0 || total_error_1 == 0); + FLAC__ASSERT(data_len > 0 || total_error_2 == 0); + FLAC__ASSERT(data_len > 0 || total_error_3 == 0); + FLAC__ASSERT(data_len > 0 || total_error_4 == 0); + + residual_bits_per_sample[0] = (float)((total_error_0 > 0) ? log(M_LN2 * (double)total_error_0 / (double)data_len) / M_LN2 : 0.0); + residual_bits_per_sample[1] = (float)((total_error_1 > 0) ? log(M_LN2 * (double)total_error_1 / (double)data_len) / M_LN2 : 0.0); + residual_bits_per_sample[2] = (float)((total_error_2 > 0) ? log(M_LN2 * (double)total_error_2 / (double)data_len) / M_LN2 : 0.0); + residual_bits_per_sample[3] = (float)((total_error_3 > 0) ? log(M_LN2 * (double)total_error_3 / (double)data_len) / M_LN2 : 0.0); + residual_bits_per_sample[4] = (float)((total_error_4 > 0) ? log(M_LN2 * (double)total_error_4 / (double)data_len) / M_LN2 : 0.0); + + return order; +} + +#endif /* FLAC__SSE2_SUPPORTED */ +#endif /* (FLAC__CPU_IA32 || FLAC__CPU_X86_64) && FLAC__HAS_X86INTRIN */ +#endif /* FLAC__NO_ASM */ +#endif /* FLAC__INTEGER_ONLY_LIBRARY */ diff --git a/core/deps/flac/src/libFLAC/fixed_intrin_ssse3.c b/core/deps/flac/src/libFLAC/fixed_intrin_ssse3.c new file mode 100644 index 000000000..f4d93e8fd --- /dev/null +++ b/core/deps/flac/src/libFLAC/fixed_intrin_ssse3.c @@ -0,0 +1,243 @@ +/* libFLAC - Free Lossless Audio Codec library + * Copyright (C) 2000-2009 Josh Coalson + * Copyright (C) 2011-2016 Xiph.Org Foundation + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * - Neither the name of the Xiph.org Foundation nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifdef HAVE_CONFIG_H +# include +#endif + +#include "private/cpu.h" + +#ifndef FLAC__INTEGER_ONLY_LIBRARY +#ifndef FLAC__NO_ASM +#if (defined FLAC__CPU_IA32 || defined FLAC__CPU_X86_64) && FLAC__HAS_X86INTRIN +#include "private/fixed.h" +#ifdef FLAC__SSSE3_SUPPORTED + +#include /* SSSE3 */ +#include +#include "private/macros.h" +#include "share/compat.h" +#include "FLAC/assert.h" + +#ifdef FLAC__CPU_IA32 +#define m128i_to_i64(dest, src) _mm_storel_epi64((__m128i*)&dest, src) +#else +#define m128i_to_i64(dest, src) dest = _mm_cvtsi128_si64(src) +#endif + +FLAC__SSE_TARGET("ssse3") +unsigned FLAC__fixed_compute_best_predictor_intrin_ssse3(const FLAC__int32 data[], unsigned data_len, float residual_bits_per_sample[FLAC__MAX_FIXED_ORDER + 1]) +{ + FLAC__uint32 total_error_0, total_error_1, total_error_2, total_error_3, total_error_4; + unsigned i, order; + + __m128i total_err0, total_err1, total_err2; + + { + FLAC__int32 itmp; + __m128i last_error; + + last_error = _mm_cvtsi32_si128(data[-1]); // 0 0 0 le0 + itmp = data[-2]; + last_error = _mm_shuffle_epi32(last_error, _MM_SHUFFLE(2,1,0,0)); + last_error = _mm_sub_epi32(last_error, _mm_cvtsi32_si128(itmp)); // 0 0 le0 le1 + itmp -= data[-3]; + last_error = _mm_shuffle_epi32(last_error, _MM_SHUFFLE(2,1,0,0)); + last_error = _mm_sub_epi32(last_error, _mm_cvtsi32_si128(itmp)); // 0 le0 le1 le2 + itmp -= data[-3] - data[-4]; + last_error = _mm_shuffle_epi32(last_error, _MM_SHUFFLE(2,1,0,0)); + last_error = _mm_sub_epi32(last_error, _mm_cvtsi32_si128(itmp)); // le0 le1 le2 le3 + + total_err0 = total_err1 = _mm_setzero_si128(); + for(i = 0; i < data_len; i++) { + __m128i err0, err1; + err0 = _mm_cvtsi32_si128(data[i]); // 0 0 0 e0 + err1 = _mm_shuffle_epi32(err0, _MM_SHUFFLE(0,0,0,0)); // e0 e0 e0 e0 +#if 1 /* OPT_SSE */ + err1 = _mm_sub_epi32(err1, last_error); + last_error = _mm_srli_si128(last_error, 4); // 0 le0 le1 le2 + err1 = _mm_sub_epi32(err1, last_error); + last_error = _mm_srli_si128(last_error, 4); // 0 0 le0 le1 + err1 = _mm_sub_epi32(err1, last_error); + last_error = _mm_srli_si128(last_error, 4); // 0 0 0 le0 + err1 = _mm_sub_epi32(err1, last_error); // e1 e2 e3 e4 +#else + last_error = _mm_add_epi32(last_error, _mm_srli_si128(last_error, 8)); // le0 le1 le2+le0 le3+le1 + last_error = _mm_add_epi32(last_error, _mm_srli_si128(last_error, 4)); // le0 le1+le0 le2+le0+le1 le3+le1+le2+le0 + err1 = _mm_sub_epi32(err1, last_error); // e1 e2 e3 e4 +#endif + last_error = _mm_alignr_epi8(err0, err1, 4); // e0 e1 e2 e3 + + err0 = _mm_abs_epi32(err0); + err1 = _mm_abs_epi32(err1); + + total_err0 = _mm_add_epi32(total_err0, err0); // 0 0 0 te0 + total_err1 = _mm_add_epi32(total_err1, err1); // te1 te2 te3 te4 + } + } + + total_error_0 = _mm_cvtsi128_si32(total_err0); + total_err2 = total_err1; // te1 te2 te3 te4 + total_err1 = _mm_srli_si128(total_err1, 8); // 0 0 te1 te2 + total_error_4 = _mm_cvtsi128_si32(total_err2); + total_error_2 = _mm_cvtsi128_si32(total_err1); + total_err2 = _mm_srli_si128(total_err2, 4); // 0 te1 te2 te3 + total_err1 = _mm_srli_si128(total_err1, 4); // 0 0 0 te1 + total_error_3 = _mm_cvtsi128_si32(total_err2); + total_error_1 = _mm_cvtsi128_si32(total_err1); + + /* prefer higher order */ + if(total_error_0 < flac_min(flac_min(flac_min(total_error_1, total_error_2), total_error_3), total_error_4)) + order = 0; + else if(total_error_1 < flac_min(flac_min(total_error_2, total_error_3), total_error_4)) + order = 1; + else if(total_error_2 < flac_min(total_error_3, total_error_4)) + order = 2; + else if(total_error_3 < total_error_4) + order = 3; + else + order = 4; + + /* Estimate the expected number of bits per residual signal sample. */ + /* 'total_error*' is linearly related to the variance of the residual */ + /* signal, so we use it directly to compute E(|x|) */ + FLAC__ASSERT(data_len > 0 || total_error_0 == 0); + FLAC__ASSERT(data_len > 0 || total_error_1 == 0); + FLAC__ASSERT(data_len > 0 || total_error_2 == 0); + FLAC__ASSERT(data_len > 0 || total_error_3 == 0); + FLAC__ASSERT(data_len > 0 || total_error_4 == 0); + + residual_bits_per_sample[0] = (float)((total_error_0 > 0) ? log(M_LN2 * (double)total_error_0 / (double)data_len) / M_LN2 : 0.0); + residual_bits_per_sample[1] = (float)((total_error_1 > 0) ? log(M_LN2 * (double)total_error_1 / (double)data_len) / M_LN2 : 0.0); + residual_bits_per_sample[2] = (float)((total_error_2 > 0) ? log(M_LN2 * (double)total_error_2 / (double)data_len) / M_LN2 : 0.0); + residual_bits_per_sample[3] = (float)((total_error_3 > 0) ? log(M_LN2 * (double)total_error_3 / (double)data_len) / M_LN2 : 0.0); + residual_bits_per_sample[4] = (float)((total_error_4 > 0) ? log(M_LN2 * (double)total_error_4 / (double)data_len) / M_LN2 : 0.0); + + return order; +} + +FLAC__SSE_TARGET("ssse3") +unsigned FLAC__fixed_compute_best_predictor_wide_intrin_ssse3(const FLAC__int32 data[], unsigned data_len, float residual_bits_per_sample[FLAC__MAX_FIXED_ORDER + 1]) +{ + FLAC__uint64 total_error_0, total_error_1, total_error_2, total_error_3, total_error_4; + unsigned i, order; + + __m128i total_err0, total_err1, total_err3; + + { + FLAC__int32 itmp; + __m128i last_error, zero = _mm_setzero_si128(); + + last_error = _mm_cvtsi32_si128(data[-1]); // 0 0 0 le0 + itmp = data[-2]; + last_error = _mm_shuffle_epi32(last_error, _MM_SHUFFLE(2,1,0,0)); + last_error = _mm_sub_epi32(last_error, _mm_cvtsi32_si128(itmp)); // 0 0 le0 le1 + itmp -= data[-3]; + last_error = _mm_shuffle_epi32(last_error, _MM_SHUFFLE(2,1,0,0)); + last_error = _mm_sub_epi32(last_error, _mm_cvtsi32_si128(itmp)); // 0 le0 le1 le2 + itmp -= data[-3] - data[-4]; + last_error = _mm_shuffle_epi32(last_error, _MM_SHUFFLE(2,1,0,0)); + last_error = _mm_sub_epi32(last_error, _mm_cvtsi32_si128(itmp)); // le0 le1 le2 le3 + + total_err0 = total_err1 = total_err3 = _mm_setzero_si128(); + for(i = 0; i < data_len; i++) { + __m128i err0, err1; + err0 = _mm_cvtsi32_si128(data[i]); // 0 0 0 e0 + err1 = _mm_shuffle_epi32(err0, _MM_SHUFFLE(0,0,0,0)); // e0 e0 e0 e0 +#if 1 /* OPT_SSE */ + err1 = _mm_sub_epi32(err1, last_error); + last_error = _mm_srli_si128(last_error, 4); // 0 le0 le1 le2 + err1 = _mm_sub_epi32(err1, last_error); + last_error = _mm_srli_si128(last_error, 4); // 0 0 le0 le1 + err1 = _mm_sub_epi32(err1, last_error); + last_error = _mm_srli_si128(last_error, 4); // 0 0 0 le0 + err1 = _mm_sub_epi32(err1, last_error); // e1 e2 e3 e4 +#else + last_error = _mm_add_epi32(last_error, _mm_srli_si128(last_error, 8)); // le0 le1 le2+le0 le3+le1 + last_error = _mm_add_epi32(last_error, _mm_srli_si128(last_error, 4)); // le0 le1+le0 le2+le0+le1 le3+le1+le2+le0 + err1 = _mm_sub_epi32(err1, last_error); // e1 e2 e3 e4 +#endif + last_error = _mm_alignr_epi8(err0, err1, 4); // e0 e1 e2 e3 + + err0 = _mm_abs_epi32(err0); + err1 = _mm_abs_epi32(err1); // |e1| |e2| |e3| |e4| + + total_err0 = _mm_add_epi64(total_err0, err0); // 0 te0 + err0 = _mm_unpacklo_epi32(err1, zero); // 0 |e3| 0 |e4| + err1 = _mm_unpackhi_epi32(err1, zero); // 0 |e1| 0 |e2| + total_err3 = _mm_add_epi64(total_err3, err0); // te3 te4 + total_err1 = _mm_add_epi64(total_err1, err1); // te1 te2 + } + } + + m128i_to_i64(total_error_0, total_err0); + m128i_to_i64(total_error_4, total_err3); + m128i_to_i64(total_error_2, total_err1); + total_err3 = _mm_srli_si128(total_err3, 8); // 0 te3 + total_err1 = _mm_srli_si128(total_err1, 8); // 0 te1 + m128i_to_i64(total_error_3, total_err3); + m128i_to_i64(total_error_1, total_err1); + + /* prefer higher order */ + if(total_error_0 < flac_min(flac_min(flac_min(total_error_1, total_error_2), total_error_3), total_error_4)) + order = 0; + else if(total_error_1 < flac_min(flac_min(total_error_2, total_error_3), total_error_4)) + order = 1; + else if(total_error_2 < flac_min(total_error_3, total_error_4)) + order = 2; + else if(total_error_3 < total_error_4) + order = 3; + else + order = 4; + + /* Estimate the expected number of bits per residual signal sample. */ + /* 'total_error*' is linearly related to the variance of the residual */ + /* signal, so we use it directly to compute E(|x|) */ + FLAC__ASSERT(data_len > 0 || total_error_0 == 0); + FLAC__ASSERT(data_len > 0 || total_error_1 == 0); + FLAC__ASSERT(data_len > 0 || total_error_2 == 0); + FLAC__ASSERT(data_len > 0 || total_error_3 == 0); + FLAC__ASSERT(data_len > 0 || total_error_4 == 0); + + residual_bits_per_sample[0] = (float)((total_error_0 > 0) ? log(M_LN2 * (double)total_error_0 / (double)data_len) / M_LN2 : 0.0); + residual_bits_per_sample[1] = (float)((total_error_1 > 0) ? log(M_LN2 * (double)total_error_1 / (double)data_len) / M_LN2 : 0.0); + residual_bits_per_sample[2] = (float)((total_error_2 > 0) ? log(M_LN2 * (double)total_error_2 / (double)data_len) / M_LN2 : 0.0); + residual_bits_per_sample[3] = (float)((total_error_3 > 0) ? log(M_LN2 * (double)total_error_3 / (double)data_len) / M_LN2 : 0.0); + residual_bits_per_sample[4] = (float)((total_error_4 > 0) ? log(M_LN2 * (double)total_error_4 / (double)data_len) / M_LN2 : 0.0); + + return order; +} + +#endif /* FLAC__SSSE3_SUPPORTED */ +#endif /* (FLAC__CPU_IA32 || FLAC__CPU_X86_64) && FLAC__HAS_X86INTRIN */ +#endif /* FLAC__NO_ASM */ +#endif /* FLAC__INTEGER_ONLY_LIBRARY */ diff --git a/core/deps/flac/src/libFLAC/float.c b/core/deps/flac/src/libFLAC/float.c new file mode 100644 index 000000000..25d1a7861 --- /dev/null +++ b/core/deps/flac/src/libFLAC/float.c @@ -0,0 +1,302 @@ +/* libFLAC - Free Lossless Audio Codec library + * Copyright (C) 2004-2009 Josh Coalson + * Copyright (C) 2011-2016 Xiph.Org Foundation + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * - Neither the name of the Xiph.org Foundation nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifdef HAVE_CONFIG_H +# include +#endif + +#include "FLAC/assert.h" +#include "share/compat.h" +#include "private/float.h" + +#ifdef FLAC__INTEGER_ONLY_LIBRARY + +const FLAC__fixedpoint FLAC__FP_ZERO = 0; +const FLAC__fixedpoint FLAC__FP_ONE_HALF = 0x00008000; +const FLAC__fixedpoint FLAC__FP_ONE = 0x00010000; +const FLAC__fixedpoint FLAC__FP_LN2 = 45426; +const FLAC__fixedpoint FLAC__FP_E = 178145; + +/* Lookup tables for Knuth's logarithm algorithm */ +#define LOG2_LOOKUP_PRECISION 16 +static const FLAC__uint32 log2_lookup[][LOG2_LOOKUP_PRECISION] = { + { + /* + * 0 fraction bits + */ + /* undefined */ 0x00000000, + /* lg(2/1) = */ 0x00000001, + /* lg(4/3) = */ 0x00000000, + /* lg(8/7) = */ 0x00000000, + /* lg(16/15) = */ 0x00000000, + /* lg(32/31) = */ 0x00000000, + /* lg(64/63) = */ 0x00000000, + /* lg(128/127) = */ 0x00000000, + /* lg(256/255) = */ 0x00000000, + /* lg(512/511) = */ 0x00000000, + /* lg(1024/1023) = */ 0x00000000, + /* lg(2048/2047) = */ 0x00000000, + /* lg(4096/4095) = */ 0x00000000, + /* lg(8192/8191) = */ 0x00000000, + /* lg(16384/16383) = */ 0x00000000, + /* lg(32768/32767) = */ 0x00000000 + }, + { + /* + * 4 fraction bits + */ + /* undefined */ 0x00000000, + /* lg(2/1) = */ 0x00000010, + /* lg(4/3) = */ 0x00000007, + /* lg(8/7) = */ 0x00000003, + /* lg(16/15) = */ 0x00000001, + /* lg(32/31) = */ 0x00000001, + /* lg(64/63) = */ 0x00000000, + /* lg(128/127) = */ 0x00000000, + /* lg(256/255) = */ 0x00000000, + /* lg(512/511) = */ 0x00000000, + /* lg(1024/1023) = */ 0x00000000, + /* lg(2048/2047) = */ 0x00000000, + /* lg(4096/4095) = */ 0x00000000, + /* lg(8192/8191) = */ 0x00000000, + /* lg(16384/16383) = */ 0x00000000, + /* lg(32768/32767) = */ 0x00000000 + }, + { + /* + * 8 fraction bits + */ + /* undefined */ 0x00000000, + /* lg(2/1) = */ 0x00000100, + /* lg(4/3) = */ 0x0000006a, + /* lg(8/7) = */ 0x00000031, + /* lg(16/15) = */ 0x00000018, + /* lg(32/31) = */ 0x0000000c, + /* lg(64/63) = */ 0x00000006, + /* lg(128/127) = */ 0x00000003, + /* lg(256/255) = */ 0x00000001, + /* lg(512/511) = */ 0x00000001, + /* lg(1024/1023) = */ 0x00000000, + /* lg(2048/2047) = */ 0x00000000, + /* lg(4096/4095) = */ 0x00000000, + /* lg(8192/8191) = */ 0x00000000, + /* lg(16384/16383) = */ 0x00000000, + /* lg(32768/32767) = */ 0x00000000 + }, + { + /* + * 12 fraction bits + */ + /* undefined */ 0x00000000, + /* lg(2/1) = */ 0x00001000, + /* lg(4/3) = */ 0x000006a4, + /* lg(8/7) = */ 0x00000315, + /* lg(16/15) = */ 0x0000017d, + /* lg(32/31) = */ 0x000000bc, + /* lg(64/63) = */ 0x0000005d, + /* lg(128/127) = */ 0x0000002e, + /* lg(256/255) = */ 0x00000017, + /* lg(512/511) = */ 0x0000000c, + /* lg(1024/1023) = */ 0x00000006, + /* lg(2048/2047) = */ 0x00000003, + /* lg(4096/4095) = */ 0x00000001, + /* lg(8192/8191) = */ 0x00000001, + /* lg(16384/16383) = */ 0x00000000, + /* lg(32768/32767) = */ 0x00000000 + }, + { + /* + * 16 fraction bits + */ + /* undefined */ 0x00000000, + /* lg(2/1) = */ 0x00010000, + /* lg(4/3) = */ 0x00006a40, + /* lg(8/7) = */ 0x00003151, + /* lg(16/15) = */ 0x000017d6, + /* lg(32/31) = */ 0x00000bba, + /* lg(64/63) = */ 0x000005d1, + /* lg(128/127) = */ 0x000002e6, + /* lg(256/255) = */ 0x00000172, + /* lg(512/511) = */ 0x000000b9, + /* lg(1024/1023) = */ 0x0000005c, + /* lg(2048/2047) = */ 0x0000002e, + /* lg(4096/4095) = */ 0x00000017, + /* lg(8192/8191) = */ 0x0000000c, + /* lg(16384/16383) = */ 0x00000006, + /* lg(32768/32767) = */ 0x00000003 + }, + { + /* + * 20 fraction bits + */ + /* undefined */ 0x00000000, + /* lg(2/1) = */ 0x00100000, + /* lg(4/3) = */ 0x0006a3fe, + /* lg(8/7) = */ 0x00031513, + /* lg(16/15) = */ 0x00017d60, + /* lg(32/31) = */ 0x0000bb9d, + /* lg(64/63) = */ 0x00005d10, + /* lg(128/127) = */ 0x00002e59, + /* lg(256/255) = */ 0x00001721, + /* lg(512/511) = */ 0x00000b8e, + /* lg(1024/1023) = */ 0x000005c6, + /* lg(2048/2047) = */ 0x000002e3, + /* lg(4096/4095) = */ 0x00000171, + /* lg(8192/8191) = */ 0x000000b9, + /* lg(16384/16383) = */ 0x0000005c, + /* lg(32768/32767) = */ 0x0000002e + }, + { + /* + * 24 fraction bits + */ + /* undefined */ 0x00000000, + /* lg(2/1) = */ 0x01000000, + /* lg(4/3) = */ 0x006a3fe6, + /* lg(8/7) = */ 0x00315130, + /* lg(16/15) = */ 0x0017d605, + /* lg(32/31) = */ 0x000bb9ca, + /* lg(64/63) = */ 0x0005d0fc, + /* lg(128/127) = */ 0x0002e58f, + /* lg(256/255) = */ 0x0001720e, + /* lg(512/511) = */ 0x0000b8d8, + /* lg(1024/1023) = */ 0x00005c61, + /* lg(2048/2047) = */ 0x00002e2d, + /* lg(4096/4095) = */ 0x00001716, + /* lg(8192/8191) = */ 0x00000b8b, + /* lg(16384/16383) = */ 0x000005c5, + /* lg(32768/32767) = */ 0x000002e3 + }, + { + /* + * 28 fraction bits + */ + /* undefined */ 0x00000000, + /* lg(2/1) = */ 0x10000000, + /* lg(4/3) = */ 0x06a3fe5c, + /* lg(8/7) = */ 0x03151301, + /* lg(16/15) = */ 0x017d6049, + /* lg(32/31) = */ 0x00bb9ca6, + /* lg(64/63) = */ 0x005d0fba, + /* lg(128/127) = */ 0x002e58f7, + /* lg(256/255) = */ 0x001720da, + /* lg(512/511) = */ 0x000b8d87, + /* lg(1024/1023) = */ 0x0005c60b, + /* lg(2048/2047) = */ 0x0002e2d7, + /* lg(4096/4095) = */ 0x00017160, + /* lg(8192/8191) = */ 0x0000b8ad, + /* lg(16384/16383) = */ 0x00005c56, + /* lg(32768/32767) = */ 0x00002e2b + } +}; + +#if 0 +static const FLAC__uint64 log2_lookup_wide[] = { + { + /* + * 32 fraction bits + */ + /* undefined */ 0x00000000, + /* lg(2/1) = */ FLAC__U64L(0x100000000), + /* lg(4/3) = */ FLAC__U64L(0x6a3fe5c6), + /* lg(8/7) = */ FLAC__U64L(0x31513015), + /* lg(16/15) = */ FLAC__U64L(0x17d60497), + /* lg(32/31) = */ FLAC__U64L(0x0bb9ca65), + /* lg(64/63) = */ FLAC__U64L(0x05d0fba2), + /* lg(128/127) = */ FLAC__U64L(0x02e58f74), + /* lg(256/255) = */ FLAC__U64L(0x01720d9c), + /* lg(512/511) = */ FLAC__U64L(0x00b8d875), + /* lg(1024/1023) = */ FLAC__U64L(0x005c60aa), + /* lg(2048/2047) = */ FLAC__U64L(0x002e2d72), + /* lg(4096/4095) = */ FLAC__U64L(0x00171600), + /* lg(8192/8191) = */ FLAC__U64L(0x000b8ad2), + /* lg(16384/16383) = */ FLAC__U64L(0x0005c55d), + /* lg(32768/32767) = */ FLAC__U64L(0x0002e2ac) + }, + { + /* + * 48 fraction bits + */ + /* undefined */ 0x00000000, + /* lg(2/1) = */ FLAC__U64L(0x1000000000000), + /* lg(4/3) = */ FLAC__U64L(0x6a3fe5c60429), + /* lg(8/7) = */ FLAC__U64L(0x315130157f7a), + /* lg(16/15) = */ FLAC__U64L(0x17d60496cfbb), + /* lg(32/31) = */ FLAC__U64L(0xbb9ca64ecac), + /* lg(64/63) = */ FLAC__U64L(0x5d0fba187cd), + /* lg(128/127) = */ FLAC__U64L(0x2e58f7441ee), + /* lg(256/255) = */ FLAC__U64L(0x1720d9c06a8), + /* lg(512/511) = */ FLAC__U64L(0xb8d8752173), + /* lg(1024/1023) = */ FLAC__U64L(0x5c60aa252e), + /* lg(2048/2047) = */ FLAC__U64L(0x2e2d71b0d8), + /* lg(4096/4095) = */ FLAC__U64L(0x1716001719), + /* lg(8192/8191) = */ FLAC__U64L(0xb8ad1de1b), + /* lg(16384/16383) = */ FLAC__U64L(0x5c55d640d), + /* lg(32768/32767) = */ FLAC__U64L(0x2e2abcf52) + } +}; +#endif + +FLAC__uint32 FLAC__fixedpoint_log2(FLAC__uint32 x, unsigned fracbits, unsigned precision) +{ + const FLAC__uint32 ONE = (1u << fracbits); + const FLAC__uint32 *table = log2_lookup[fracbits >> 2]; + + FLAC__ASSERT(fracbits < 32); + FLAC__ASSERT((fracbits & 0x3) == 0); + + if(x < ONE) + return 0; + + if(precision > LOG2_LOOKUP_PRECISION) + precision = LOG2_LOOKUP_PRECISION; + + /* Knuth's algorithm for computing logarithms, optimized for base-2 with lookup tables */ + { + FLAC__uint32 y = 0; + FLAC__uint32 z = x >> 1, k = 1; + while (x > ONE && k < precision) { + if (x - z >= ONE) { + x -= z; + z = x >> k; + y += table[k]; + } + else { + z >>= 1; + k++; + } + } + return y; + } +} + +#endif /* defined FLAC__INTEGER_ONLY_LIBRARY */ diff --git a/core/deps/flac/src/libFLAC/format.c b/core/deps/flac/src/libFLAC/format.c new file mode 100644 index 000000000..214bd09a9 --- /dev/null +++ b/core/deps/flac/src/libFLAC/format.c @@ -0,0 +1,589 @@ +/* libFLAC - Free Lossless Audio Codec library + * Copyright (C) 2000-2009 Josh Coalson + * Copyright (C) 2011-2016 Xiph.Org Foundation + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * - Neither the name of the Xiph.org Foundation nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifdef HAVE_CONFIG_H +# include +#endif + +#include +#include /* for qsort() */ +#include /* for memset() */ +#include "FLAC/assert.h" +#include "FLAC/format.h" +#include "share/alloc.h" +#include "share/compat.h" +#include "private/format.h" +#include "private/macros.h" + +/* PACKAGE_VERSION should come from configure */ +FLAC_API const char *FLAC__VERSION_STRING = PACKAGE_VERSION; + +FLAC_API const char *FLAC__VENDOR_STRING = "reference libFLAC " PACKAGE_VERSION " 20170101"; + +FLAC_API const FLAC__byte FLAC__STREAM_SYNC_STRING[4] = { 'f','L','a','C' }; +FLAC_API const unsigned FLAC__STREAM_SYNC = 0x664C6143; +FLAC_API const unsigned FLAC__STREAM_SYNC_LEN = 32; /* bits */ + +FLAC_API const unsigned FLAC__STREAM_METADATA_STREAMINFO_MIN_BLOCK_SIZE_LEN = 16; /* bits */ +FLAC_API const unsigned FLAC__STREAM_METADATA_STREAMINFO_MAX_BLOCK_SIZE_LEN = 16; /* bits */ +FLAC_API const unsigned FLAC__STREAM_METADATA_STREAMINFO_MIN_FRAME_SIZE_LEN = 24; /* bits */ +FLAC_API const unsigned FLAC__STREAM_METADATA_STREAMINFO_MAX_FRAME_SIZE_LEN = 24; /* bits */ +FLAC_API const unsigned FLAC__STREAM_METADATA_STREAMINFO_SAMPLE_RATE_LEN = 20; /* bits */ +FLAC_API const unsigned FLAC__STREAM_METADATA_STREAMINFO_CHANNELS_LEN = 3; /* bits */ +FLAC_API const unsigned FLAC__STREAM_METADATA_STREAMINFO_BITS_PER_SAMPLE_LEN = 5; /* bits */ +FLAC_API const unsigned FLAC__STREAM_METADATA_STREAMINFO_TOTAL_SAMPLES_LEN = 36; /* bits */ +FLAC_API const unsigned FLAC__STREAM_METADATA_STREAMINFO_MD5SUM_LEN = 128; /* bits */ + +FLAC_API const unsigned FLAC__STREAM_METADATA_APPLICATION_ID_LEN = 32; /* bits */ + +FLAC_API const unsigned FLAC__STREAM_METADATA_SEEKPOINT_SAMPLE_NUMBER_LEN = 64; /* bits */ +FLAC_API const unsigned FLAC__STREAM_METADATA_SEEKPOINT_STREAM_OFFSET_LEN = 64; /* bits */ +FLAC_API const unsigned FLAC__STREAM_METADATA_SEEKPOINT_FRAME_SAMPLES_LEN = 16; /* bits */ + +FLAC_API const FLAC__uint64 FLAC__STREAM_METADATA_SEEKPOINT_PLACEHOLDER = FLAC__U64L(0xffffffffffffffff); + +FLAC_API const unsigned FLAC__STREAM_METADATA_VORBIS_COMMENT_ENTRY_LENGTH_LEN = 32; /* bits */ +FLAC_API const unsigned FLAC__STREAM_METADATA_VORBIS_COMMENT_NUM_COMMENTS_LEN = 32; /* bits */ + +FLAC_API const unsigned FLAC__STREAM_METADATA_CUESHEET_INDEX_OFFSET_LEN = 64; /* bits */ +FLAC_API const unsigned FLAC__STREAM_METADATA_CUESHEET_INDEX_NUMBER_LEN = 8; /* bits */ +FLAC_API const unsigned FLAC__STREAM_METADATA_CUESHEET_INDEX_RESERVED_LEN = 3*8; /* bits */ + +FLAC_API const unsigned FLAC__STREAM_METADATA_CUESHEET_TRACK_OFFSET_LEN = 64; /* bits */ +FLAC_API const unsigned FLAC__STREAM_METADATA_CUESHEET_TRACK_NUMBER_LEN = 8; /* bits */ +FLAC_API const unsigned FLAC__STREAM_METADATA_CUESHEET_TRACK_ISRC_LEN = 12*8; /* bits */ +FLAC_API const unsigned FLAC__STREAM_METADATA_CUESHEET_TRACK_TYPE_LEN = 1; /* bit */ +FLAC_API const unsigned FLAC__STREAM_METADATA_CUESHEET_TRACK_PRE_EMPHASIS_LEN = 1; /* bit */ +FLAC_API const unsigned FLAC__STREAM_METADATA_CUESHEET_TRACK_RESERVED_LEN = 6+13*8; /* bits */ +FLAC_API const unsigned FLAC__STREAM_METADATA_CUESHEET_TRACK_NUM_INDICES_LEN = 8; /* bits */ + +FLAC_API const unsigned FLAC__STREAM_METADATA_CUESHEET_MEDIA_CATALOG_NUMBER_LEN = 128*8; /* bits */ +FLAC_API const unsigned FLAC__STREAM_METADATA_CUESHEET_LEAD_IN_LEN = 64; /* bits */ +FLAC_API const unsigned FLAC__STREAM_METADATA_CUESHEET_IS_CD_LEN = 1; /* bit */ +FLAC_API const unsigned FLAC__STREAM_METADATA_CUESHEET_RESERVED_LEN = 7+258*8; /* bits */ +FLAC_API const unsigned FLAC__STREAM_METADATA_CUESHEET_NUM_TRACKS_LEN = 8; /* bits */ + +FLAC_API const unsigned FLAC__STREAM_METADATA_PICTURE_TYPE_LEN = 32; /* bits */ +FLAC_API const unsigned FLAC__STREAM_METADATA_PICTURE_MIME_TYPE_LENGTH_LEN = 32; /* bits */ +FLAC_API const unsigned FLAC__STREAM_METADATA_PICTURE_DESCRIPTION_LENGTH_LEN = 32; /* bits */ +FLAC_API const unsigned FLAC__STREAM_METADATA_PICTURE_WIDTH_LEN = 32; /* bits */ +FLAC_API const unsigned FLAC__STREAM_METADATA_PICTURE_HEIGHT_LEN = 32; /* bits */ +FLAC_API const unsigned FLAC__STREAM_METADATA_PICTURE_DEPTH_LEN = 32; /* bits */ +FLAC_API const unsigned FLAC__STREAM_METADATA_PICTURE_COLORS_LEN = 32; /* bits */ +FLAC_API const unsigned FLAC__STREAM_METADATA_PICTURE_DATA_LENGTH_LEN = 32; /* bits */ + +FLAC_API const unsigned FLAC__STREAM_METADATA_IS_LAST_LEN = 1; /* bits */ +FLAC_API const unsigned FLAC__STREAM_METADATA_TYPE_LEN = 7; /* bits */ +FLAC_API const unsigned FLAC__STREAM_METADATA_LENGTH_LEN = 24; /* bits */ + +FLAC_API const unsigned FLAC__FRAME_HEADER_SYNC = 0x3ffe; +FLAC_API const unsigned FLAC__FRAME_HEADER_SYNC_LEN = 14; /* bits */ +FLAC_API const unsigned FLAC__FRAME_HEADER_RESERVED_LEN = 1; /* bits */ +FLAC_API const unsigned FLAC__FRAME_HEADER_BLOCKING_STRATEGY_LEN = 1; /* bits */ +FLAC_API const unsigned FLAC__FRAME_HEADER_BLOCK_SIZE_LEN = 4; /* bits */ +FLAC_API const unsigned FLAC__FRAME_HEADER_SAMPLE_RATE_LEN = 4; /* bits */ +FLAC_API const unsigned FLAC__FRAME_HEADER_CHANNEL_ASSIGNMENT_LEN = 4; /* bits */ +FLAC_API const unsigned FLAC__FRAME_HEADER_BITS_PER_SAMPLE_LEN = 3; /* bits */ +FLAC_API const unsigned FLAC__FRAME_HEADER_ZERO_PAD_LEN = 1; /* bits */ +FLAC_API const unsigned FLAC__FRAME_HEADER_CRC_LEN = 8; /* bits */ + +FLAC_API const unsigned FLAC__FRAME_FOOTER_CRC_LEN = 16; /* bits */ + +FLAC_API const unsigned FLAC__ENTROPY_CODING_METHOD_TYPE_LEN = 2; /* bits */ +FLAC_API const unsigned FLAC__ENTROPY_CODING_METHOD_PARTITIONED_RICE_ORDER_LEN = 4; /* bits */ +FLAC_API const unsigned FLAC__ENTROPY_CODING_METHOD_PARTITIONED_RICE_PARAMETER_LEN = 4; /* bits */ +FLAC_API const unsigned FLAC__ENTROPY_CODING_METHOD_PARTITIONED_RICE2_PARAMETER_LEN = 5; /* bits */ +FLAC_API const unsigned FLAC__ENTROPY_CODING_METHOD_PARTITIONED_RICE_RAW_LEN = 5; /* bits */ + +FLAC_API const unsigned FLAC__ENTROPY_CODING_METHOD_PARTITIONED_RICE_ESCAPE_PARAMETER = 15; /* == (1< FLAC__MAX_SAMPLE_RATE) { + return false; + } + else + return true; +} + +FLAC_API FLAC__bool FLAC__format_blocksize_is_subset(unsigned blocksize, unsigned sample_rate) +{ + if(blocksize > 16384) + return false; + else if(sample_rate <= 48000 && blocksize > 4608) + return false; + else + return true; +} + +FLAC_API FLAC__bool FLAC__format_sample_rate_is_subset(unsigned sample_rate) +{ + if( + !FLAC__format_sample_rate_is_valid(sample_rate) || + ( + sample_rate >= (1u << 16) && + !(sample_rate % 1000 == 0 || sample_rate % 10 == 0) + ) + ) { + return false; + } + else + return true; +} + +/* @@@@ add to unit tests; it is already indirectly tested by the metadata_object tests */ +FLAC_API FLAC__bool FLAC__format_seektable_is_legal(const FLAC__StreamMetadata_SeekTable *seek_table) +{ + unsigned i; + FLAC__uint64 prev_sample_number = 0; + FLAC__bool got_prev = false; + + FLAC__ASSERT(0 != seek_table); + + for(i = 0; i < seek_table->num_points; i++) { + if(got_prev) { + if( + seek_table->points[i].sample_number != FLAC__STREAM_METADATA_SEEKPOINT_PLACEHOLDER && + seek_table->points[i].sample_number <= prev_sample_number + ) + return false; + } + prev_sample_number = seek_table->points[i].sample_number; + got_prev = true; + } + + return true; +} + +/* used as the sort predicate for qsort() */ +static int seekpoint_compare_(const FLAC__StreamMetadata_SeekPoint *l, const FLAC__StreamMetadata_SeekPoint *r) +{ + /* we don't just 'return l->sample_number - r->sample_number' since the result (FLAC__int64) might overflow an 'int' */ + if(l->sample_number == r->sample_number) + return 0; + else if(l->sample_number < r->sample_number) + return -1; + else + return 1; +} + +/* @@@@ add to unit tests; it is already indirectly tested by the metadata_object tests */ +FLAC_API unsigned FLAC__format_seektable_sort(FLAC__StreamMetadata_SeekTable *seek_table) +{ + unsigned i, j; + FLAC__bool first; + + FLAC__ASSERT(0 != seek_table); + + if (seek_table->num_points == 0) + return 0; + + /* sort the seekpoints */ + qsort(seek_table->points, seek_table->num_points, sizeof(FLAC__StreamMetadata_SeekPoint), (int (*)(const void *, const void *))seekpoint_compare_); + + /* uniquify the seekpoints */ + first = true; + for(i = j = 0; i < seek_table->num_points; i++) { + if(seek_table->points[i].sample_number != FLAC__STREAM_METADATA_SEEKPOINT_PLACEHOLDER) { + if(!first) { + if(seek_table->points[i].sample_number == seek_table->points[j-1].sample_number) + continue; + } + } + first = false; + seek_table->points[j++] = seek_table->points[i]; + } + + for(i = j; i < seek_table->num_points; i++) { + seek_table->points[i].sample_number = FLAC__STREAM_METADATA_SEEKPOINT_PLACEHOLDER; + seek_table->points[i].stream_offset = 0; + seek_table->points[i].frame_samples = 0; + } + + return j; +} + +/* + * also disallows non-shortest-form encodings, c.f. + * http://www.unicode.org/versions/corrigendum1.html + * and a more clear explanation at the end of this section: + * http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 + */ +static unsigned utf8len_(const FLAC__byte *utf8) +{ + FLAC__ASSERT(0 != utf8); + if ((utf8[0] & 0x80) == 0) { + return 1; + } + else if ((utf8[0] & 0xE0) == 0xC0 && (utf8[1] & 0xC0) == 0x80) { + if ((utf8[0] & 0xFE) == 0xC0) /* overlong sequence check */ + return 0; + return 2; + } + else if ((utf8[0] & 0xF0) == 0xE0 && (utf8[1] & 0xC0) == 0x80 && (utf8[2] & 0xC0) == 0x80) { + if (utf8[0] == 0xE0 && (utf8[1] & 0xE0) == 0x80) /* overlong sequence check */ + return 0; + /* illegal surrogates check (U+D800...U+DFFF and U+FFFE...U+FFFF) */ + if (utf8[0] == 0xED && (utf8[1] & 0xE0) == 0xA0) /* D800-DFFF */ + return 0; + if (utf8[0] == 0xEF && utf8[1] == 0xBF && (utf8[2] & 0xFE) == 0xBE) /* FFFE-FFFF */ + return 0; + return 3; + } + else if ((utf8[0] & 0xF8) == 0xF0 && (utf8[1] & 0xC0) == 0x80 && (utf8[2] & 0xC0) == 0x80 && (utf8[3] & 0xC0) == 0x80) { + if (utf8[0] == 0xF0 && (utf8[1] & 0xF0) == 0x80) /* overlong sequence check */ + return 0; + return 4; + } + else if ((utf8[0] & 0xFC) == 0xF8 && (utf8[1] & 0xC0) == 0x80 && (utf8[2] & 0xC0) == 0x80 && (utf8[3] & 0xC0) == 0x80 && (utf8[4] & 0xC0) == 0x80) { + if (utf8[0] == 0xF8 && (utf8[1] & 0xF8) == 0x80) /* overlong sequence check */ + return 0; + return 5; + } + else if ((utf8[0] & 0xFE) == 0xFC && (utf8[1] & 0xC0) == 0x80 && (utf8[2] & 0xC0) == 0x80 && (utf8[3] & 0xC0) == 0x80 && (utf8[4] & 0xC0) == 0x80 && (utf8[5] & 0xC0) == 0x80) { + if (utf8[0] == 0xFC && (utf8[1] & 0xFC) == 0x80) /* overlong sequence check */ + return 0; + return 6; + } + else { + return 0; + } +} + +FLAC_API FLAC__bool FLAC__format_vorbiscomment_entry_name_is_legal(const char *name) +{ + char c; + for(c = *name; c; c = *(++name)) + if(c < 0x20 || c == 0x3d || c > 0x7d) + return false; + return true; +} + +FLAC_API FLAC__bool FLAC__format_vorbiscomment_entry_value_is_legal(const FLAC__byte *value, unsigned length) +{ + if(length == (unsigned)(-1)) { + while(*value) { + unsigned n = utf8len_(value); + if(n == 0) + return false; + value += n; + } + } + else { + const FLAC__byte *end = value + length; + while(value < end) { + unsigned n = utf8len_(value); + if(n == 0) + return false; + value += n; + } + if(value != end) + return false; + } + return true; +} + +FLAC_API FLAC__bool FLAC__format_vorbiscomment_entry_is_legal(const FLAC__byte *entry, unsigned length) +{ + const FLAC__byte *s, *end; + + for(s = entry, end = s + length; s < end && *s != '='; s++) { + if(*s < 0x20 || *s > 0x7D) + return false; + } + if(s == end) + return false; + + s++; /* skip '=' */ + + while(s < end) { + unsigned n = utf8len_(s); + if(n == 0) + return false; + s += n; + } + if(s != end) + return false; + + return true; +} + +/* @@@@ add to unit tests; it is already indirectly tested by the metadata_object tests */ +FLAC_API FLAC__bool FLAC__format_cuesheet_is_legal(const FLAC__StreamMetadata_CueSheet *cue_sheet, FLAC__bool check_cd_da_subset, const char **violation) +{ + unsigned i, j; + + if(check_cd_da_subset) { + if(cue_sheet->lead_in < 2 * 44100) { + if(violation) *violation = "CD-DA cue sheet must have a lead-in length of at least 2 seconds"; + return false; + } + if(cue_sheet->lead_in % 588 != 0) { + if(violation) *violation = "CD-DA cue sheet lead-in length must be evenly divisible by 588 samples"; + return false; + } + } + + if(cue_sheet->num_tracks == 0) { + if(violation) *violation = "cue sheet must have at least one track (the lead-out)"; + return false; + } + + if(check_cd_da_subset && cue_sheet->tracks[cue_sheet->num_tracks-1].number != 170) { + if(violation) *violation = "CD-DA cue sheet must have a lead-out track number 170 (0xAA)"; + return false; + } + + for(i = 0; i < cue_sheet->num_tracks; i++) { + if(cue_sheet->tracks[i].number == 0) { + if(violation) *violation = "cue sheet may not have a track number 0"; + return false; + } + + if(check_cd_da_subset) { + if(!((cue_sheet->tracks[i].number >= 1 && cue_sheet->tracks[i].number <= 99) || cue_sheet->tracks[i].number == 170)) { + if(violation) *violation = "CD-DA cue sheet track number must be 1-99 or 170"; + return false; + } + } + + if(check_cd_da_subset && cue_sheet->tracks[i].offset % 588 != 0) { + if(violation) { + if(i == cue_sheet->num_tracks-1) /* the lead-out track... */ + *violation = "CD-DA cue sheet lead-out offset must be evenly divisible by 588 samples"; + else + *violation = "CD-DA cue sheet track offset must be evenly divisible by 588 samples"; + } + return false; + } + + if(i < cue_sheet->num_tracks - 1) { + if(cue_sheet->tracks[i].num_indices == 0) { + if(violation) *violation = "cue sheet track must have at least one index point"; + return false; + } + + if(cue_sheet->tracks[i].indices[0].number > 1) { + if(violation) *violation = "cue sheet track's first index number must be 0 or 1"; + return false; + } + } + + for(j = 0; j < cue_sheet->tracks[i].num_indices; j++) { + if(check_cd_da_subset && cue_sheet->tracks[i].indices[j].offset % 588 != 0) { + if(violation) *violation = "CD-DA cue sheet track index offset must be evenly divisible by 588 samples"; + return false; + } + + if(j > 0) { + if(cue_sheet->tracks[i].indices[j].number != cue_sheet->tracks[i].indices[j-1].number + 1) { + if(violation) *violation = "cue sheet track index numbers must increase by 1"; + return false; + } + } + } + } + + return true; +} + +/* @@@@ add to unit tests; it is already indirectly tested by the metadata_object tests */ +FLAC_API FLAC__bool FLAC__format_picture_is_legal(const FLAC__StreamMetadata_Picture *picture, const char **violation) +{ + char *p; + FLAC__byte *b; + + for(p = picture->mime_type; *p; p++) { + if(*p < 0x20 || *p > 0x7e) { + if(violation) *violation = "MIME type string must contain only printable ASCII characters (0x20-0x7e)"; + return false; + } + } + + for(b = picture->description; *b; ) { + unsigned n = utf8len_(b); + if(n == 0) { + if(violation) *violation = "description string must be valid UTF-8"; + return false; + } + b += n; + } + + return true; +} + +/* + * These routines are private to libFLAC + */ +unsigned FLAC__format_get_max_rice_partition_order(unsigned blocksize, unsigned predictor_order) +{ + return + FLAC__format_get_max_rice_partition_order_from_blocksize_limited_max_and_predictor_order( + FLAC__format_get_max_rice_partition_order_from_blocksize(blocksize), + blocksize, + predictor_order + ); +} + +unsigned FLAC__format_get_max_rice_partition_order_from_blocksize(unsigned blocksize) +{ + unsigned max_rice_partition_order = 0; + while(!(blocksize & 1)) { + max_rice_partition_order++; + blocksize >>= 1; + } + return flac_min(FLAC__MAX_RICE_PARTITION_ORDER, max_rice_partition_order); +} + +unsigned FLAC__format_get_max_rice_partition_order_from_blocksize_limited_max_and_predictor_order(unsigned limit, unsigned blocksize, unsigned predictor_order) +{ + unsigned max_rice_partition_order = limit; + + while(max_rice_partition_order > 0 && (blocksize >> max_rice_partition_order) <= predictor_order) + max_rice_partition_order--; + + FLAC__ASSERT( + (max_rice_partition_order == 0 && blocksize >= predictor_order) || + (max_rice_partition_order > 0 && blocksize >> max_rice_partition_order > predictor_order) + ); + + return max_rice_partition_order; +} + +void FLAC__format_entropy_coding_method_partitioned_rice_contents_init(FLAC__EntropyCodingMethod_PartitionedRiceContents *object) +{ + FLAC__ASSERT(0 != object); + + object->parameters = 0; + object->raw_bits = 0; + object->capacity_by_order = 0; +} + +void FLAC__format_entropy_coding_method_partitioned_rice_contents_clear(FLAC__EntropyCodingMethod_PartitionedRiceContents *object) +{ + FLAC__ASSERT(0 != object); + + if(0 != object->parameters) + free(object->parameters); + if(0 != object->raw_bits) + free(object->raw_bits); + FLAC__format_entropy_coding_method_partitioned_rice_contents_init(object); +} + +FLAC__bool FLAC__format_entropy_coding_method_partitioned_rice_contents_ensure_size(FLAC__EntropyCodingMethod_PartitionedRiceContents *object, unsigned max_partition_order) +{ + FLAC__ASSERT(0 != object); + + FLAC__ASSERT(object->capacity_by_order > 0 || (0 == object->parameters && 0 == object->raw_bits)); + + if(object->capacity_by_order < max_partition_order) { + if(0 == (object->parameters = safe_realloc_(object->parameters, sizeof(unsigned)*(1 << max_partition_order)))) + return false; + if(0 == (object->raw_bits = safe_realloc_(object->raw_bits, sizeof(unsigned)*(1 << max_partition_order)))) + return false; + memset(object->raw_bits, 0, sizeof(unsigned)*(1 << max_partition_order)); + object->capacity_by_order = max_partition_order; + } + + return true; +} diff --git a/core/deps/flac/src/libFLAC/include/private/bitmath.h b/core/deps/flac/src/libFLAC/include/private/bitmath.h new file mode 100644 index 000000000..9c75f85bf --- /dev/null +++ b/core/deps/flac/src/libFLAC/include/private/bitmath.h @@ -0,0 +1,210 @@ +/* libFLAC - Free Lossless Audio Codec library + * Copyright (C) 2001-2009 Josh Coalson + * Copyright (C) 2011-2016 Xiph.Org Foundation + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * - Neither the name of the Xiph.org Foundation nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef FLAC__PRIVATE__BITMATH_H +#define FLAC__PRIVATE__BITMATH_H + +#include "FLAC/ordinals.h" +#include "FLAC/assert.h" + +#include "share/compat.h" + +#if defined(_MSC_VER) +#include /* for _BitScanReverse* */ +#endif + +/* Will never be emitted for MSVC, GCC, Intel compilers */ +static inline unsigned int FLAC__clz_soft_uint32(FLAC__uint32 word) +{ + static const unsigned char byte_to_unary_table[] = { + 8, 7, 6, 6, 5, 5, 5, 5, 4, 4, 4, 4, 4, 4, 4, 4, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + }; + + return word > 0xffffff ? byte_to_unary_table[word >> 24] : + word > 0xffff ? byte_to_unary_table[word >> 16] + 8 : + word > 0xff ? byte_to_unary_table[word >> 8] + 16 : + byte_to_unary_table[word] + 24; +} + +static inline unsigned int FLAC__clz_uint32(FLAC__uint32 v) +{ +/* Never used with input 0 */ + FLAC__ASSERT(v > 0); +#if defined(__INTEL_COMPILER) + return _bit_scan_reverse(v) ^ 31U; +#elif defined(__GNUC__) && (__GNUC__ >= 4 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 4)) +/* This will translate either to (bsr ^ 31U), clz , ctlz, cntlz, lzcnt depending on + * -march= setting or to a software routine in exotic machines. */ + return __builtin_clz(v); +#elif defined(_MSC_VER) + { + unsigned long idx; + _BitScanReverse(&idx, v); + return idx ^ 31U; + } +#else + return FLAC__clz_soft_uint32(v); +#endif +} + +/* Used when 64-bit bsr/clz is unavailable; can use 32-bit bsr/clz when possible */ +static inline unsigned int FLAC__clz_soft_uint64(FLAC__uint64 word) +{ + return (FLAC__uint32)(word>>32) ? FLAC__clz_uint32((FLAC__uint32)(word>>32)) : + FLAC__clz_uint32((FLAC__uint32)word) + 32; +} + +static inline unsigned int FLAC__clz_uint64(FLAC__uint64 v) +{ + /* Never used with input 0 */ + FLAC__ASSERT(v > 0); +#if defined(__GNUC__) && (__GNUC__ >= 4 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 4)) + return __builtin_clzll(v); +#elif (defined(__INTEL_COMPILER) || defined(_MSC_VER)) && (defined(_M_IA64) || defined(_M_X64)) + { + unsigned long idx; + _BitScanReverse64(&idx, v); + return idx ^ 63U; + } +#else + return FLAC__clz_soft_uint64(v); +#endif +} + +/* These two functions work with input 0 */ +static inline unsigned int FLAC__clz2_uint32(FLAC__uint32 v) +{ + if (!v) + return 32; + return FLAC__clz_uint32(v); +} + +static inline unsigned int FLAC__clz2_uint64(FLAC__uint64 v) +{ + if (!v) + return 64; + return FLAC__clz_uint64(v); +} + +/* An example of what FLAC__bitmath_ilog2() computes: + * + * ilog2( 0) = assertion failure + * ilog2( 1) = 0 + * ilog2( 2) = 1 + * ilog2( 3) = 1 + * ilog2( 4) = 2 + * ilog2( 5) = 2 + * ilog2( 6) = 2 + * ilog2( 7) = 2 + * ilog2( 8) = 3 + * ilog2( 9) = 3 + * ilog2(10) = 3 + * ilog2(11) = 3 + * ilog2(12) = 3 + * ilog2(13) = 3 + * ilog2(14) = 3 + * ilog2(15) = 3 + * ilog2(16) = 4 + * ilog2(17) = 4 + * ilog2(18) = 4 + */ + +static inline unsigned FLAC__bitmath_ilog2(FLAC__uint32 v) +{ + FLAC__ASSERT(v > 0); +#if defined(__INTEL_COMPILER) + return _bit_scan_reverse(v); +#elif defined(_MSC_VER) + { + unsigned long idx; + _BitScanReverse(&idx, v); + return idx; + } +#else + return FLAC__clz_uint32(v) ^ 31U; +#endif +} + +static inline unsigned FLAC__bitmath_ilog2_wide(FLAC__uint64 v) +{ + FLAC__ASSERT(v > 0); +#if defined(__GNUC__) && (__GNUC__ >= 4 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 4)) + return __builtin_clzll(v) ^ 63U; +/* Sorry, only supported in x64/Itanium.. and both have fast FPU which makes integer-only encoder pointless */ +#elif (defined(__INTEL_COMPILER) || defined(_MSC_VER)) && (defined(_M_IA64) || defined(_M_X64)) + { + unsigned long idx; + _BitScanReverse64(&idx, v); + return idx; + } +#else +/* Brain-damaged compilers will use the fastest possible way that is, + de Bruijn sequences (http://supertech.csail.mit.edu/papers/debruijn.pdf) + (C) Timothy B. Terriberry (tterribe@xiph.org) 2001-2009 CC0 (Public domain). +*/ + { + static const unsigned char DEBRUIJN_IDX64[64]={ + 0, 1, 2, 7, 3,13, 8,19, 4,25,14,28, 9,34,20,40, + 5,17,26,38,15,46,29,48,10,31,35,54,21,50,41,57, + 63, 6,12,18,24,27,33,39,16,37,45,47,30,53,49,56, + 62,11,23,32,36,44,52,55,61,22,43,51,60,42,59,58 + }; + v|= v>>1; + v|= v>>2; + v|= v>>4; + v|= v>>8; + v|= v>>16; + v|= v>>32; + v= (v>>1)+1; + return DEBRUIJN_IDX64[v*FLAC__U64L(0x218A392CD3D5DBF)>>58&0x3F]; + } +#endif +} + +unsigned FLAC__bitmath_silog2(FLAC__int64 v); + +#endif diff --git a/core/deps/flac/src/libFLAC/include/private/cpu.h b/core/deps/flac/src/libFLAC/include/private/cpu.h new file mode 100644 index 000000000..7c6518076 --- /dev/null +++ b/core/deps/flac/src/libFLAC/include/private/cpu.h @@ -0,0 +1,186 @@ +/* libFLAC - Free Lossless Audio Codec library + * Copyright (C) 2001-2009 Josh Coalson + * Copyright (C) 2011-2016 Xiph.Org Foundation + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * - Neither the name of the Xiph.org Foundation nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef FLAC__PRIVATE__CPU_H +#define FLAC__PRIVATE__CPU_H + +#include "FLAC/ordinals.h" + +#ifdef HAVE_CONFIG_H +#include +#endif + +#ifndef FLAC__CPU_X86_64 + +#if defined(__amd64__) || defined(__amd64) || defined(__x86_64__) || defined(__x86_64) || defined(_M_X64) || defined(_M_AMD64) +#define FLAC__CPU_X86_64 +#endif + +#endif + +#ifndef FLAC__CPU_IA32 + +#if defined(__i386__) || defined(__i486__) || defined(__i586__) || defined(__i686__) ||defined( __i386) || defined(_M_IX86) +#define FLAC__CPU_IA32 +#endif + +#endif + + +#if FLAC__HAS_X86INTRIN +/* SSE intrinsics support by ICC/MSVC/GCC */ +#if defined __INTEL_COMPILER + #define FLAC__SSE_TARGET(x) + #define FLAC__SSE_SUPPORTED 1 + #define FLAC__SSE2_SUPPORTED 1 + #if (__INTEL_COMPILER >= 1000) /* Intel C++ Compiler 10.0 */ + #define FLAC__SSSE3_SUPPORTED 1 + #define FLAC__SSE4_1_SUPPORTED 1 + #endif + #if (__INTEL_COMPILER >= 1110) /* Intel C++ Compiler 11.1 */ + #define FLAC__AVX_SUPPORTED 1 + #endif + #if (__INTEL_COMPILER >= 1300) /* Intel C++ Compiler 13.0 */ + #define FLAC__AVX2_SUPPORTED 1 + #define FLAC__FMA_SUPPORTED 1 + #endif +#elif defined _MSC_VER + #define FLAC__SSE_TARGET(x) + #define FLAC__SSE_SUPPORTED 1 + #define FLAC__SSE2_SUPPORTED 1 + #if (_MSC_VER >= 1500) /* MS Visual Studio 2008 */ + #define FLAC__SSSE3_SUPPORTED 1 + #define FLAC__SSE4_1_SUPPORTED 1 + #endif + #if (_MSC_FULL_VER >= 160040219) /* MS Visual Studio 2010 SP1 */ + #define FLAC__AVX_SUPPORTED 1 + #endif + #if (_MSC_VER >= 1700) /* MS Visual Studio 2012 */ + #define FLAC__AVX2_SUPPORTED 1 + #define FLAC__FMA_SUPPORTED 1 + #endif +#elif defined __GNUC__ + #if (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 9)) /* since GCC 4.9 -msse.. compiler options aren't necessary */ + #define FLAC__SSE_TARGET(x) __attribute__ ((__target__ (x))) + #define FLAC__SSE_SUPPORTED 1 + #define FLAC__SSE2_SUPPORTED 1 + #define FLAC__SSSE3_SUPPORTED 1 + #define FLAC__SSE4_1_SUPPORTED 1 +#ifdef FLAC__USE_AVX + #define FLAC__AVX_SUPPORTED 1 + #define FLAC__AVX2_SUPPORTED 1 + #define FLAC__FMA_SUPPORTED 1 +#endif + #else /* for GCC older than 4.9 */ + #define FLAC__SSE_TARGET(x) + #ifdef __SSE__ + #define FLAC__SSE_SUPPORTED 1 + #endif + #ifdef __SSE2__ + #define FLAC__SSE2_SUPPORTED 1 + #endif + #ifdef __SSSE3__ + #define FLAC__SSSE3_SUPPORTED 1 + #endif + #ifdef __SSE4_1__ + #define FLAC__SSE4_1_SUPPORTED 1 + #endif + #ifdef __AVX__ + #define FLAC__AVX_SUPPORTED 1 + #endif + #ifdef __AVX2__ + #define FLAC__AVX2_SUPPORTED 1 + #endif + #ifdef __FMA__ + #define FLAC__FMA_SUPPORTED 1 + #endif + #endif /* GCC version */ +#endif /* compiler version */ +#endif /* intrinsics support */ + + +#ifndef FLAC__AVX_SUPPORTED +#define FLAC__AVX_SUPPORTED 0 +#endif + +typedef enum { + FLAC__CPUINFO_TYPE_IA32, + FLAC__CPUINFO_TYPE_X86_64, + FLAC__CPUINFO_TYPE_UNKNOWN +} FLAC__CPUInfo_Type; + +typedef struct { + FLAC__bool intel; + + FLAC__bool cmov; + FLAC__bool mmx; + FLAC__bool sse; + FLAC__bool sse2; + + FLAC__bool sse3; + FLAC__bool ssse3; + FLAC__bool sse41; + FLAC__bool sse42; + FLAC__bool avx; + FLAC__bool avx2; + FLAC__bool fma; +} FLAC__CPUInfo_IA32; + +typedef struct { + FLAC__bool intel; + + FLAC__bool sse3; + FLAC__bool ssse3; + FLAC__bool sse41; + FLAC__bool sse42; + FLAC__bool avx; + FLAC__bool avx2; + FLAC__bool fma; +} FLAC__CPUInfo_x86; + + +typedef struct { + FLAC__bool use_asm; + FLAC__CPUInfo_Type type; + FLAC__CPUInfo_IA32 ia32; + FLAC__CPUInfo_x86 x86; +} FLAC__CPUInfo; + +void FLAC__cpu_info(FLAC__CPUInfo *info); + +FLAC__uint32 FLAC__cpu_have_cpuid_asm_ia32(void); + +void FLAC__cpu_info_asm_ia32(FLAC__uint32 *flags_edx, FLAC__uint32 *flags_ecx); + +void FLAC__cpu_info_x86(FLAC__uint32 level, FLAC__uint32 *eax, FLAC__uint32 *ebx, FLAC__uint32 *ecx, FLAC__uint32 *edx); + +#endif diff --git a/core/deps/flac/src/libFLAC/include/private/crc.h b/core/deps/flac/src/libFLAC/include/private/crc.h new file mode 100644 index 000000000..294f60eaa --- /dev/null +++ b/core/deps/flac/src/libFLAC/include/private/crc.h @@ -0,0 +1,62 @@ +/* libFLAC - Free Lossless Audio Codec library + * Copyright (C) 2000-2009 Josh Coalson + * Copyright (C) 2011-2016 Xiph.Org Foundation + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * - Neither the name of the Xiph.org Foundation nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef FLAC__PRIVATE__CRC_H +#define FLAC__PRIVATE__CRC_H + +#include "FLAC/ordinals.h" + +/* 8 bit CRC generator, MSB shifted first +** polynomial = x^8 + x^2 + x^1 + x^0 +** init = 0 +*/ +extern FLAC__byte const FLAC__crc8_table[256]; +#define FLAC__CRC8_UPDATE(data, crc) (crc) = FLAC__crc8_table[(crc) ^ (data)]; +void FLAC__crc8_update(const FLAC__byte data, FLAC__uint8 *crc); +void FLAC__crc8_update_block(const FLAC__byte *data, unsigned len, FLAC__uint8 *crc); +FLAC__uint8 FLAC__crc8(const FLAC__byte *data, unsigned len); + +/* 16 bit CRC generator, MSB shifted first +** polynomial = x^16 + x^15 + x^2 + x^0 +** init = 0 +*/ +extern unsigned const FLAC__crc16_table[256]; + +#define FLAC__CRC16_UPDATE(data, crc) ((((crc)<<8) & 0xffff) ^ FLAC__crc16_table[((crc)>>8) ^ (data)]) +/* this alternate may be faster on some systems/compilers */ +#if 0 +#define FLAC__CRC16_UPDATE(data, crc) ((((crc)<<8) ^ FLAC__crc16_table[((crc)>>8) ^ (data)]) & 0xffff) +#endif + +unsigned FLAC__crc16(const FLAC__byte *data, unsigned len); + +#endif diff --git a/core/deps/flac/src/libFLAC/include/private/fixed.h b/core/deps/flac/src/libFLAC/include/private/fixed.h new file mode 100644 index 000000000..68cdfceb3 --- /dev/null +++ b/core/deps/flac/src/libFLAC/include/private/fixed.h @@ -0,0 +1,107 @@ +/* libFLAC - Free Lossless Audio Codec library + * Copyright (C) 2000-2009 Josh Coalson + * Copyright (C) 2011-2016 Xiph.Org Foundation + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * - Neither the name of the Xiph.org Foundation nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef FLAC__PRIVATE__FIXED_H +#define FLAC__PRIVATE__FIXED_H + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include "private/cpu.h" +#include "private/float.h" +#include "FLAC/format.h" + +/* + * FLAC__fixed_compute_best_predictor() + * -------------------------------------------------------------------- + * Compute the best fixed predictor and the expected bits-per-sample + * of the residual signal for each order. The _wide() version uses + * 64-bit integers which is statistically necessary when bits-per- + * sample + log2(blocksize) > 30 + * + * IN data[0,data_len-1] + * IN data_len + * OUT residual_bits_per_sample[0,FLAC__MAX_FIXED_ORDER] + */ +#ifndef FLAC__INTEGER_ONLY_LIBRARY +unsigned FLAC__fixed_compute_best_predictor(const FLAC__int32 data[], unsigned data_len, float residual_bits_per_sample[FLAC__MAX_FIXED_ORDER+1]); +unsigned FLAC__fixed_compute_best_predictor_wide(const FLAC__int32 data[], unsigned data_len, float residual_bits_per_sample[FLAC__MAX_FIXED_ORDER+1]); +# ifndef FLAC__NO_ASM +# if (defined FLAC__CPU_IA32 || defined FLAC__CPU_X86_64) && FLAC__HAS_X86INTRIN +# ifdef FLAC__SSE2_SUPPORTED +unsigned FLAC__fixed_compute_best_predictor_intrin_sse2(const FLAC__int32 data[], unsigned data_len, float residual_bits_per_sample[FLAC__MAX_FIXED_ORDER + 1]); +unsigned FLAC__fixed_compute_best_predictor_wide_intrin_sse2(const FLAC__int32 data[], unsigned data_len, float residual_bits_per_sample[FLAC__MAX_FIXED_ORDER + 1]); +# endif +# ifdef FLAC__SSSE3_SUPPORTED +unsigned FLAC__fixed_compute_best_predictor_intrin_ssse3(const FLAC__int32 data[], unsigned data_len, float residual_bits_per_sample[FLAC__MAX_FIXED_ORDER+1]); +unsigned FLAC__fixed_compute_best_predictor_wide_intrin_ssse3(const FLAC__int32 data[], unsigned data_len, float residual_bits_per_sample[FLAC__MAX_FIXED_ORDER + 1]); +# endif +# endif +# if defined FLAC__CPU_IA32 && defined FLAC__HAS_NASM +unsigned FLAC__fixed_compute_best_predictor_asm_ia32_mmx_cmov(const FLAC__int32 data[], unsigned data_len, float residual_bits_per_sample[FLAC__MAX_FIXED_ORDER+1]); +# endif +# endif +#else +unsigned FLAC__fixed_compute_best_predictor(const FLAC__int32 data[], unsigned data_len, FLAC__fixedpoint residual_bits_per_sample[FLAC__MAX_FIXED_ORDER+1]); +unsigned FLAC__fixed_compute_best_predictor_wide(const FLAC__int32 data[], unsigned data_len, FLAC__fixedpoint residual_bits_per_sample[FLAC__MAX_FIXED_ORDER+1]); +#endif + +/* + * FLAC__fixed_compute_residual() + * -------------------------------------------------------------------- + * Compute the residual signal obtained from sutracting the predicted + * signal from the original. + * + * IN data[-order,data_len-1] original signal (NOTE THE INDICES!) + * IN data_len length of original signal + * IN order <= FLAC__MAX_FIXED_ORDER fixed-predictor order + * OUT residual[0,data_len-1] residual signal + */ +void FLAC__fixed_compute_residual(const FLAC__int32 data[], unsigned data_len, unsigned order, FLAC__int32 residual[]); + +/* + * FLAC__fixed_restore_signal() + * -------------------------------------------------------------------- + * Restore the original signal by summing the residual and the + * predictor. + * + * IN residual[0,data_len-1] residual signal + * IN data_len length of original signal + * IN order <= FLAC__MAX_FIXED_ORDER fixed-predictor order + * *** IMPORTANT: the caller must pass in the historical samples: + * IN data[-order,-1] previously-reconstructed historical samples + * OUT data[0,data_len-1] original signal + */ +void FLAC__fixed_restore_signal(const FLAC__int32 residual[], unsigned data_len, unsigned order, FLAC__int32 data[]); + +#endif diff --git a/core/deps/flac/src/libFLAC/include/private/float.h b/core/deps/flac/src/libFLAC/include/private/float.h new file mode 100644 index 000000000..12ece6056 --- /dev/null +++ b/core/deps/flac/src/libFLAC/include/private/float.h @@ -0,0 +1,95 @@ +/* libFLAC - Free Lossless Audio Codec library + * Copyright (C) 2004-2009 Josh Coalson + * Copyright (C) 2011-2016 Xiph.Org Foundation + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * - Neither the name of the Xiph.org Foundation nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef FLAC__PRIVATE__FLOAT_H +#define FLAC__PRIVATE__FLOAT_H + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include "FLAC/ordinals.h" + +/* + * All the code in libFLAC that uses float and double + * should be protected by checks of the macro + * FLAC__INTEGER_ONLY_LIBRARY. + * + */ +#ifndef FLAC__INTEGER_ONLY_LIBRARY +/* + * FLAC__real is the basic floating point type used in LPC analysis. + * + * WATCHOUT: changing FLAC__real will change the signatures of many + * functions that have assembly language equivalents and break them. + */ +typedef float FLAC__real; +#else +/* + * The convention for FLAC__fixedpoint is to use the upper 16 bits + * for the integer part and lower 16 bits for the fractional part. + */ +typedef FLAC__int32 FLAC__fixedpoint; +extern const FLAC__fixedpoint FLAC__FP_ZERO; +extern const FLAC__fixedpoint FLAC__FP_ONE_HALF; +extern const FLAC__fixedpoint FLAC__FP_ONE; +extern const FLAC__fixedpoint FLAC__FP_LN2; +extern const FLAC__fixedpoint FLAC__FP_E; + +#define FLAC__fixedpoint_trunc(x) ((x)>>16) + +#define FLAC__fixedpoint_mul(x, y) ( (FLAC__fixedpoint) ( ((FLAC__int64)(x)*(FLAC__int64)(y)) >> 16 ) ) + +#define FLAC__fixedpoint_div(x, y) ( (FLAC__fixedpoint) ( ( ((FLAC__int64)(x)<<32) / (FLAC__int64)(y) ) >> 16 ) ) + +/* + * FLAC__fixedpoint_log2() + * -------------------------------------------------------------------- + * Returns the base-2 logarithm of the fixed-point number 'x' using an + * algorithm by Knuth for x >= 1.0 + * + * 'fracbits' is the number of fractional bits of 'x'. 'fracbits' must + * be < 32 and evenly divisible by 4 (0 is OK but not very precise). + * + * 'precision' roughly limits the number of iterations that are done; + * use (unsigned)(-1) for maximum precision. + * + * If 'x' is less than one -- that is, x < (1< 4 || ( __GNUC__ == 4 && __GNUC_MINOR__ >= 3)) + +#define flac_max(a,b) \ + ({ __typeof__ (a) _a = (a); \ + __typeof__ (b) _b = (b); \ + _a > _b ? _a : _b; }) + +#define MIN_PASTE(A,B) A##B +#define MIN_IMPL(A,B,L) ({ \ + __typeof__(A) MIN_PASTE(__a,L) = (A); \ + __typeof__(B) MIN_PASTE(__b,L) = (B); \ + MIN_PASTE(__a,L) < MIN_PASTE(__b,L) ? MIN_PASTE(__a,L) : MIN_PASTE(__b,L); \ + }) + +#define flac_min(A,B) MIN_IMPL(A,B,__COUNTER__) + +/* Whatever other unix that has sys/param.h */ +#elif defined(HAVE_SYS_PARAM_H) +#include +#define flac_max(a,b) MAX(a,b) +#define flac_min(a,b) MIN(a,b) + +/* Windows VS has them in stdlib.h.. XXX:Untested */ +#elif defined(_MSC_VER) +#include +#define flac_max(a,b) __max(a,b) +#define flac_min(a,b) __min(a,b) +#endif + +#ifndef MIN +#define MIN(x,y) ((x) <= (y) ? (x) : (y)) +#endif + +#ifndef MAX +#define MAX(x,y) ((x) >= (y) ? (x) : (y)) +#endif + +#endif diff --git a/core/deps/flac/src/libFLAC/lpc.c b/core/deps/flac/src/libFLAC/lpc.c new file mode 100644 index 000000000..531247b59 --- /dev/null +++ b/core/deps/flac/src/libFLAC/lpc.c @@ -0,0 +1,1357 @@ +/* libFLAC - Free Lossless Audio Codec library + * Copyright (C) 2000-2009 Josh Coalson + * Copyright (C) 2011-2016 Xiph.Org Foundation + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * - Neither the name of the Xiph.org Foundation nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifdef HAVE_CONFIG_H +# include +#endif + +#include + +#include "FLAC/assert.h" +#include "FLAC/format.h" +#include "share/compat.h" +#include "private/bitmath.h" +#include "private/lpc.h" +#include "private/macros.h" +#if defined DEBUG || defined FLAC__OVERFLOW_DETECT || defined FLAC__OVERFLOW_DETECT_VERBOSE +#include +#endif + +/* OPT: #undef'ing this may improve the speed on some architectures */ +#define FLAC__LPC_UNROLLED_FILTER_LOOPS + +#ifndef FLAC__INTEGER_ONLY_LIBRARY + +#if defined(_MSC_VER) && (_MSC_VER < 1800) +#include +static inline long int lround(double x) { + return (long)(x + _copysign(0.5, x)); +} +#elif !defined(HAVE_LROUND) && defined(__GNUC__) +static inline long int lround(double x) { + return (long)(x + __builtin_copysign(0.5, x)); +} +/* If this fails, we are in the presence of a mid 90's compiler, move along... */ +#endif + +void FLAC__lpc_window_data(const FLAC__int32 in[], const FLAC__real window[], FLAC__real out[], unsigned data_len) +{ + unsigned i; + for(i = 0; i < data_len; i++) + out[i] = in[i] * window[i]; +} + +void FLAC__lpc_compute_autocorrelation(const FLAC__real data[], unsigned data_len, unsigned lag, FLAC__real autoc[]) +{ + /* a readable, but slower, version */ +#if 0 + FLAC__real d; + unsigned i; + + FLAC__ASSERT(lag > 0); + FLAC__ASSERT(lag <= data_len); + + /* + * Technically we should subtract the mean first like so: + * for(i = 0; i < data_len; i++) + * data[i] -= mean; + * but it appears not to make enough of a difference to matter, and + * most signals are already closely centered around zero + */ + while(lag--) { + for(i = lag, d = 0.0; i < data_len; i++) + d += data[i] * data[i - lag]; + autoc[lag] = d; + } +#endif + + /* + * this version tends to run faster because of better data locality + * ('data_len' is usually much larger than 'lag') + */ + FLAC__real d; + unsigned sample, coeff; + const unsigned limit = data_len - lag; + + FLAC__ASSERT(lag > 0); + FLAC__ASSERT(lag <= data_len); + + for(coeff = 0; coeff < lag; coeff++) + autoc[coeff] = 0.0; + for(sample = 0; sample <= limit; sample++) { + d = data[sample]; + for(coeff = 0; coeff < lag; coeff++) + autoc[coeff] += d * data[sample+coeff]; + } + for(; sample < data_len; sample++) { + d = data[sample]; + for(coeff = 0; coeff < data_len - sample; coeff++) + autoc[coeff] += d * data[sample+coeff]; + } +} + +void FLAC__lpc_compute_lp_coefficients(const FLAC__real autoc[], unsigned *max_order, FLAC__real lp_coeff[][FLAC__MAX_LPC_ORDER], double error[]) +{ + unsigned i, j; + double r, err, lpc[FLAC__MAX_LPC_ORDER]; + + FLAC__ASSERT(0 != max_order); + FLAC__ASSERT(0 < *max_order); + FLAC__ASSERT(*max_order <= FLAC__MAX_LPC_ORDER); + FLAC__ASSERT(autoc[0] != 0.0); + + err = autoc[0]; + + for(i = 0; i < *max_order; i++) { + /* Sum up this iteration's reflection coefficient. */ + r = -autoc[i+1]; + for(j = 0; j < i; j++) + r -= lpc[j] * autoc[i-j]; + r /= err; + + /* Update LPC coefficients and total error. */ + lpc[i]=r; + for(j = 0; j < (i>>1); j++) { + double tmp = lpc[j]; + lpc[j] += r * lpc[i-1-j]; + lpc[i-1-j] += r * tmp; + } + if(i & 1) + lpc[j] += lpc[j] * r; + + err *= (1.0 - r * r); + + /* save this order */ + for(j = 0; j <= i; j++) + lp_coeff[i][j] = (FLAC__real)(-lpc[j]); /* negate FIR filter coeff to get predictor coeff */ + error[i] = err; + + /* see SF bug https://sourceforge.net/p/flac/bugs/234/ */ + if(err == 0.0) { + *max_order = i+1; + return; + } + } +} + +int FLAC__lpc_quantize_coefficients(const FLAC__real lp_coeff[], unsigned order, unsigned precision, FLAC__int32 qlp_coeff[], int *shift) +{ + unsigned i; + double cmax; + FLAC__int32 qmax, qmin; + + FLAC__ASSERT(precision > 0); + FLAC__ASSERT(precision >= FLAC__MIN_QLP_COEFF_PRECISION); + + /* drop one bit for the sign; from here on out we consider only |lp_coeff[i]| */ + precision--; + qmax = 1 << precision; + qmin = -qmax; + qmax--; + + /* calc cmax = max( |lp_coeff[i]| ) */ + cmax = 0.0; + for(i = 0; i < order; i++) { + const double d = fabs(lp_coeff[i]); + if(d > cmax) + cmax = d; + } + + if(cmax <= 0.0) { + /* => coefficients are all 0, which means our constant-detect didn't work */ + return 2; + } + else { + const int max_shiftlimit = (1 << (FLAC__SUBFRAME_LPC_QLP_SHIFT_LEN-1)) - 1; + const int min_shiftlimit = -max_shiftlimit - 1; + int log2cmax; + + (void)frexp(cmax, &log2cmax); + log2cmax--; + *shift = (int)precision - log2cmax - 1; + + if(*shift > max_shiftlimit) + *shift = max_shiftlimit; + else if(*shift < min_shiftlimit) + return 1; + } + + if(*shift >= 0) { + double error = 0.0; + FLAC__int32 q; + for(i = 0; i < order; i++) { + error += lp_coeff[i] * (1 << *shift); + q = lround(error); + +#ifdef FLAC__OVERFLOW_DETECT + if(q > qmax+1) /* we expect q==qmax+1 occasionally due to rounding */ + fprintf(stderr,"FLAC__lpc_quantize_coefficients: quantizer overflow: q>qmax %d>%d shift=%d cmax=%f precision=%u lpc[%u]=%f\n",q,qmax,*shift,cmax,precision+1,i,lp_coeff[i]); + else if(q < qmin) + fprintf(stderr,"FLAC__lpc_quantize_coefficients: quantizer overflow: q qmax) + q = qmax; + else if(q < qmin) + q = qmin; + error -= q; + qlp_coeff[i] = q; + } + } + /* negative shift is very rare but due to design flaw, negative shift is + * not allowed in the decoder, so it must be handled specially by scaling + * down coeffs + */ + else { + const int nshift = -(*shift); + double error = 0.0; + FLAC__int32 q; +#ifdef DEBUG + fprintf(stderr,"FLAC__lpc_quantize_coefficients: negative shift=%d order=%u cmax=%f\n", *shift, order, cmax); +#endif + for(i = 0; i < order; i++) { + error += lp_coeff[i] / (1 << nshift); + q = lround(error); +#ifdef FLAC__OVERFLOW_DETECT + if(q > qmax+1) /* we expect q==qmax+1 occasionally due to rounding */ + fprintf(stderr,"FLAC__lpc_quantize_coefficients: quantizer overflow: q>qmax %d>%d shift=%d cmax=%f precision=%u lpc[%u]=%f\n",q,qmax,*shift,cmax,precision+1,i,lp_coeff[i]); + else if(q < qmin) + fprintf(stderr,"FLAC__lpc_quantize_coefficients: quantizer overflow: q qmax) + q = qmax; + else if(q < qmin) + q = qmin; + error -= q; + qlp_coeff[i] = q; + } + *shift = 0; + } + + return 0; +} + +#if defined(_MSC_VER) +// silence MSVC warnings about __restrict modifier +#pragma warning ( disable : 4028 ) +#endif + +void FLAC__lpc_compute_residual_from_qlp_coefficients(const FLAC__int32 * flac_restrict data, unsigned data_len, const FLAC__int32 * flac_restrict qlp_coeff, unsigned order, int lp_quantization, FLAC__int32 * flac_restrict residual) +#if defined(FLAC__OVERFLOW_DETECT) || !defined(FLAC__LPC_UNROLLED_FILTER_LOOPS) +{ + FLAC__int64 sumo; + unsigned i, j; + FLAC__int32 sum; + const FLAC__int32 *history; + +#ifdef FLAC__OVERFLOW_DETECT_VERBOSE + fprintf(stderr,"FLAC__lpc_compute_residual_from_qlp_coefficients: data_len=%d, order=%u, lpq=%d",data_len,order,lp_quantization); + for(i=0;i 0); + + for(i = 0; i < data_len; i++) { + sumo = 0; + sum = 0; + history = data; + for(j = 0; j < order; j++) { + sum += qlp_coeff[j] * (*(--history)); + sumo += (FLAC__int64)qlp_coeff[j] * (FLAC__int64)(*history); + if(sumo > 2147483647ll || sumo < -2147483648ll) + fprintf(stderr,"FLAC__lpc_compute_residual_from_qlp_coefficients: OVERFLOW, i=%u, j=%u, c=%d, d=%d, sumo=%" PRId64 "\n",i,j,qlp_coeff[j],*history,sumo); + } + *(residual++) = *(data++) - (sum >> lp_quantization); + } + + /* Here's a slower but clearer version: + for(i = 0; i < data_len; i++) { + sum = 0; + for(j = 0; j < order; j++) + sum += qlp_coeff[j] * data[i-j-1]; + residual[i] = data[i] - (sum >> lp_quantization); + } + */ +} +#else /* fully unrolled version for normal use */ +{ + int i; + FLAC__int32 sum; + + FLAC__ASSERT(order > 0); + FLAC__ASSERT(order <= 32); + + /* + * We do unique versions up to 12th order since that's the subset limit. + * Also they are roughly ordered to match frequency of occurrence to + * minimize branching. + */ + if(order <= 12) { + if(order > 8) { + if(order > 10) { + if(order == 12) { + for(i = 0; i < (int)data_len; i++) { + sum = 0; + sum += qlp_coeff[11] * data[i-12]; + sum += qlp_coeff[10] * data[i-11]; + sum += qlp_coeff[9] * data[i-10]; + sum += qlp_coeff[8] * data[i-9]; + sum += qlp_coeff[7] * data[i-8]; + sum += qlp_coeff[6] * data[i-7]; + sum += qlp_coeff[5] * data[i-6]; + sum += qlp_coeff[4] * data[i-5]; + sum += qlp_coeff[3] * data[i-4]; + sum += qlp_coeff[2] * data[i-3]; + sum += qlp_coeff[1] * data[i-2]; + sum += qlp_coeff[0] * data[i-1]; + residual[i] = data[i] - (sum >> lp_quantization); + } + } + else { /* order == 11 */ + for(i = 0; i < (int)data_len; i++) { + sum = 0; + sum += qlp_coeff[10] * data[i-11]; + sum += qlp_coeff[9] * data[i-10]; + sum += qlp_coeff[8] * data[i-9]; + sum += qlp_coeff[7] * data[i-8]; + sum += qlp_coeff[6] * data[i-7]; + sum += qlp_coeff[5] * data[i-6]; + sum += qlp_coeff[4] * data[i-5]; + sum += qlp_coeff[3] * data[i-4]; + sum += qlp_coeff[2] * data[i-3]; + sum += qlp_coeff[1] * data[i-2]; + sum += qlp_coeff[0] * data[i-1]; + residual[i] = data[i] - (sum >> lp_quantization); + } + } + } + else { + if(order == 10) { + for(i = 0; i < (int)data_len; i++) { + sum = 0; + sum += qlp_coeff[9] * data[i-10]; + sum += qlp_coeff[8] * data[i-9]; + sum += qlp_coeff[7] * data[i-8]; + sum += qlp_coeff[6] * data[i-7]; + sum += qlp_coeff[5] * data[i-6]; + sum += qlp_coeff[4] * data[i-5]; + sum += qlp_coeff[3] * data[i-4]; + sum += qlp_coeff[2] * data[i-3]; + sum += qlp_coeff[1] * data[i-2]; + sum += qlp_coeff[0] * data[i-1]; + residual[i] = data[i] - (sum >> lp_quantization); + } + } + else { /* order == 9 */ + for(i = 0; i < (int)data_len; i++) { + sum = 0; + sum += qlp_coeff[8] * data[i-9]; + sum += qlp_coeff[7] * data[i-8]; + sum += qlp_coeff[6] * data[i-7]; + sum += qlp_coeff[5] * data[i-6]; + sum += qlp_coeff[4] * data[i-5]; + sum += qlp_coeff[3] * data[i-4]; + sum += qlp_coeff[2] * data[i-3]; + sum += qlp_coeff[1] * data[i-2]; + sum += qlp_coeff[0] * data[i-1]; + residual[i] = data[i] - (sum >> lp_quantization); + } + } + } + } + else if(order > 4) { + if(order > 6) { + if(order == 8) { + for(i = 0; i < (int)data_len; i++) { + sum = 0; + sum += qlp_coeff[7] * data[i-8]; + sum += qlp_coeff[6] * data[i-7]; + sum += qlp_coeff[5] * data[i-6]; + sum += qlp_coeff[4] * data[i-5]; + sum += qlp_coeff[3] * data[i-4]; + sum += qlp_coeff[2] * data[i-3]; + sum += qlp_coeff[1] * data[i-2]; + sum += qlp_coeff[0] * data[i-1]; + residual[i] = data[i] - (sum >> lp_quantization); + } + } + else { /* order == 7 */ + for(i = 0; i < (int)data_len; i++) { + sum = 0; + sum += qlp_coeff[6] * data[i-7]; + sum += qlp_coeff[5] * data[i-6]; + sum += qlp_coeff[4] * data[i-5]; + sum += qlp_coeff[3] * data[i-4]; + sum += qlp_coeff[2] * data[i-3]; + sum += qlp_coeff[1] * data[i-2]; + sum += qlp_coeff[0] * data[i-1]; + residual[i] = data[i] - (sum >> lp_quantization); + } + } + } + else { + if(order == 6) { + for(i = 0; i < (int)data_len; i++) { + sum = 0; + sum += qlp_coeff[5] * data[i-6]; + sum += qlp_coeff[4] * data[i-5]; + sum += qlp_coeff[3] * data[i-4]; + sum += qlp_coeff[2] * data[i-3]; + sum += qlp_coeff[1] * data[i-2]; + sum += qlp_coeff[0] * data[i-1]; + residual[i] = data[i] - (sum >> lp_quantization); + } + } + else { /* order == 5 */ + for(i = 0; i < (int)data_len; i++) { + sum = 0; + sum += qlp_coeff[4] * data[i-5]; + sum += qlp_coeff[3] * data[i-4]; + sum += qlp_coeff[2] * data[i-3]; + sum += qlp_coeff[1] * data[i-2]; + sum += qlp_coeff[0] * data[i-1]; + residual[i] = data[i] - (sum >> lp_quantization); + } + } + } + } + else { + if(order > 2) { + if(order == 4) { + for(i = 0; i < (int)data_len; i++) { + sum = 0; + sum += qlp_coeff[3] * data[i-4]; + sum += qlp_coeff[2] * data[i-3]; + sum += qlp_coeff[1] * data[i-2]; + sum += qlp_coeff[0] * data[i-1]; + residual[i] = data[i] - (sum >> lp_quantization); + } + } + else { /* order == 3 */ + for(i = 0; i < (int)data_len; i++) { + sum = 0; + sum += qlp_coeff[2] * data[i-3]; + sum += qlp_coeff[1] * data[i-2]; + sum += qlp_coeff[0] * data[i-1]; + residual[i] = data[i] - (sum >> lp_quantization); + } + } + } + else { + if(order == 2) { + for(i = 0; i < (int)data_len; i++) { + sum = 0; + sum += qlp_coeff[1] * data[i-2]; + sum += qlp_coeff[0] * data[i-1]; + residual[i] = data[i] - (sum >> lp_quantization); + } + } + else { /* order == 1 */ + for(i = 0; i < (int)data_len; i++) + residual[i] = data[i] - ((qlp_coeff[0] * data[i-1]) >> lp_quantization); + } + } + } + } + else { /* order > 12 */ + for(i = 0; i < (int)data_len; i++) { + sum = 0; + switch(order) { + case 32: sum += qlp_coeff[31] * data[i-32]; + case 31: sum += qlp_coeff[30] * data[i-31]; + case 30: sum += qlp_coeff[29] * data[i-30]; + case 29: sum += qlp_coeff[28] * data[i-29]; + case 28: sum += qlp_coeff[27] * data[i-28]; + case 27: sum += qlp_coeff[26] * data[i-27]; + case 26: sum += qlp_coeff[25] * data[i-26]; + case 25: sum += qlp_coeff[24] * data[i-25]; + case 24: sum += qlp_coeff[23] * data[i-24]; + case 23: sum += qlp_coeff[22] * data[i-23]; + case 22: sum += qlp_coeff[21] * data[i-22]; + case 21: sum += qlp_coeff[20] * data[i-21]; + case 20: sum += qlp_coeff[19] * data[i-20]; + case 19: sum += qlp_coeff[18] * data[i-19]; + case 18: sum += qlp_coeff[17] * data[i-18]; + case 17: sum += qlp_coeff[16] * data[i-17]; + case 16: sum += qlp_coeff[15] * data[i-16]; + case 15: sum += qlp_coeff[14] * data[i-15]; + case 14: sum += qlp_coeff[13] * data[i-14]; + case 13: sum += qlp_coeff[12] * data[i-13]; + sum += qlp_coeff[11] * data[i-12]; + sum += qlp_coeff[10] * data[i-11]; + sum += qlp_coeff[ 9] * data[i-10]; + sum += qlp_coeff[ 8] * data[i- 9]; + sum += qlp_coeff[ 7] * data[i- 8]; + sum += qlp_coeff[ 6] * data[i- 7]; + sum += qlp_coeff[ 5] * data[i- 6]; + sum += qlp_coeff[ 4] * data[i- 5]; + sum += qlp_coeff[ 3] * data[i- 4]; + sum += qlp_coeff[ 2] * data[i- 3]; + sum += qlp_coeff[ 1] * data[i- 2]; + sum += qlp_coeff[ 0] * data[i- 1]; + } + residual[i] = data[i] - (sum >> lp_quantization); + } + } +} +#endif + +void FLAC__lpc_compute_residual_from_qlp_coefficients_wide(const FLAC__int32 * flac_restrict data, unsigned data_len, const FLAC__int32 * flac_restrict qlp_coeff, unsigned order, int lp_quantization, FLAC__int32 * flac_restrict residual) +#if defined(FLAC__OVERFLOW_DETECT) || !defined(FLAC__LPC_UNROLLED_FILTER_LOOPS) +{ + unsigned i, j; + FLAC__int64 sum; + const FLAC__int32 *history; + +#ifdef FLAC__OVERFLOW_DETECT_VERBOSE + fprintf(stderr,"FLAC__lpc_compute_residual_from_qlp_coefficients_wide: data_len=%d, order=%u, lpq=%d",data_len,order,lp_quantization); + for(i=0;i 0); + + for(i = 0; i < data_len; i++) { + sum = 0; + history = data; + for(j = 0; j < order; j++) + sum += (FLAC__int64)qlp_coeff[j] * (FLAC__int64)(*(--history)); + if(FLAC__bitmath_silog2(sum >> lp_quantization) > 32) { + fprintf(stderr,"FLAC__lpc_compute_residual_from_qlp_coefficients_wide: OVERFLOW, i=%u, sum=%" PRId64 "\n", i, (sum >> lp_quantization)); + break; + } + if(FLAC__bitmath_silog2((FLAC__int64)(*data) - (sum >> lp_quantization)) > 32) { + fprintf(stderr,"FLAC__lpc_compute_residual_from_qlp_coefficients_wide: OVERFLOW, i=%u, data=%d, sum=%" PRId64 ", residual=%" PRId64 "\n", i, *data, (int64_t)(sum >> lp_quantization), ((FLAC__int64)(*data) - (sum >> lp_quantization))); + break; + } + *(residual++) = *(data++) - (FLAC__int32)(sum >> lp_quantization); + } +} +#else /* fully unrolled version for normal use */ +{ + int i; + FLAC__int64 sum; + + FLAC__ASSERT(order > 0); + FLAC__ASSERT(order <= 32); + + /* + * We do unique versions up to 12th order since that's the subset limit. + * Also they are roughly ordered to match frequency of occurrence to + * minimize branching. + */ + if(order <= 12) { + if(order > 8) { + if(order > 10) { + if(order == 12) { + for(i = 0; i < (int)data_len; i++) { + sum = 0; + sum += qlp_coeff[11] * (FLAC__int64)data[i-12]; + sum += qlp_coeff[10] * (FLAC__int64)data[i-11]; + sum += qlp_coeff[9] * (FLAC__int64)data[i-10]; + sum += qlp_coeff[8] * (FLAC__int64)data[i-9]; + sum += qlp_coeff[7] * (FLAC__int64)data[i-8]; + sum += qlp_coeff[6] * (FLAC__int64)data[i-7]; + sum += qlp_coeff[5] * (FLAC__int64)data[i-6]; + sum += qlp_coeff[4] * (FLAC__int64)data[i-5]; + sum += qlp_coeff[3] * (FLAC__int64)data[i-4]; + sum += qlp_coeff[2] * (FLAC__int64)data[i-3]; + sum += qlp_coeff[1] * (FLAC__int64)data[i-2]; + sum += qlp_coeff[0] * (FLAC__int64)data[i-1]; + residual[i] = data[i] - (FLAC__int32)(sum >> lp_quantization); + } + } + else { /* order == 11 */ + for(i = 0; i < (int)data_len; i++) { + sum = 0; + sum += qlp_coeff[10] * (FLAC__int64)data[i-11]; + sum += qlp_coeff[9] * (FLAC__int64)data[i-10]; + sum += qlp_coeff[8] * (FLAC__int64)data[i-9]; + sum += qlp_coeff[7] * (FLAC__int64)data[i-8]; + sum += qlp_coeff[6] * (FLAC__int64)data[i-7]; + sum += qlp_coeff[5] * (FLAC__int64)data[i-6]; + sum += qlp_coeff[4] * (FLAC__int64)data[i-5]; + sum += qlp_coeff[3] * (FLAC__int64)data[i-4]; + sum += qlp_coeff[2] * (FLAC__int64)data[i-3]; + sum += qlp_coeff[1] * (FLAC__int64)data[i-2]; + sum += qlp_coeff[0] * (FLAC__int64)data[i-1]; + residual[i] = data[i] - (FLAC__int32)(sum >> lp_quantization); + } + } + } + else { + if(order == 10) { + for(i = 0; i < (int)data_len; i++) { + sum = 0; + sum += qlp_coeff[9] * (FLAC__int64)data[i-10]; + sum += qlp_coeff[8] * (FLAC__int64)data[i-9]; + sum += qlp_coeff[7] * (FLAC__int64)data[i-8]; + sum += qlp_coeff[6] * (FLAC__int64)data[i-7]; + sum += qlp_coeff[5] * (FLAC__int64)data[i-6]; + sum += qlp_coeff[4] * (FLAC__int64)data[i-5]; + sum += qlp_coeff[3] * (FLAC__int64)data[i-4]; + sum += qlp_coeff[2] * (FLAC__int64)data[i-3]; + sum += qlp_coeff[1] * (FLAC__int64)data[i-2]; + sum += qlp_coeff[0] * (FLAC__int64)data[i-1]; + residual[i] = data[i] - (FLAC__int32)(sum >> lp_quantization); + } + } + else { /* order == 9 */ + for(i = 0; i < (int)data_len; i++) { + sum = 0; + sum += qlp_coeff[8] * (FLAC__int64)data[i-9]; + sum += qlp_coeff[7] * (FLAC__int64)data[i-8]; + sum += qlp_coeff[6] * (FLAC__int64)data[i-7]; + sum += qlp_coeff[5] * (FLAC__int64)data[i-6]; + sum += qlp_coeff[4] * (FLAC__int64)data[i-5]; + sum += qlp_coeff[3] * (FLAC__int64)data[i-4]; + sum += qlp_coeff[2] * (FLAC__int64)data[i-3]; + sum += qlp_coeff[1] * (FLAC__int64)data[i-2]; + sum += qlp_coeff[0] * (FLAC__int64)data[i-1]; + residual[i] = data[i] - (FLAC__int32)(sum >> lp_quantization); + } + } + } + } + else if(order > 4) { + if(order > 6) { + if(order == 8) { + for(i = 0; i < (int)data_len; i++) { + sum = 0; + sum += qlp_coeff[7] * (FLAC__int64)data[i-8]; + sum += qlp_coeff[6] * (FLAC__int64)data[i-7]; + sum += qlp_coeff[5] * (FLAC__int64)data[i-6]; + sum += qlp_coeff[4] * (FLAC__int64)data[i-5]; + sum += qlp_coeff[3] * (FLAC__int64)data[i-4]; + sum += qlp_coeff[2] * (FLAC__int64)data[i-3]; + sum += qlp_coeff[1] * (FLAC__int64)data[i-2]; + sum += qlp_coeff[0] * (FLAC__int64)data[i-1]; + residual[i] = data[i] - (FLAC__int32)(sum >> lp_quantization); + } + } + else { /* order == 7 */ + for(i = 0; i < (int)data_len; i++) { + sum = 0; + sum += qlp_coeff[6] * (FLAC__int64)data[i-7]; + sum += qlp_coeff[5] * (FLAC__int64)data[i-6]; + sum += qlp_coeff[4] * (FLAC__int64)data[i-5]; + sum += qlp_coeff[3] * (FLAC__int64)data[i-4]; + sum += qlp_coeff[2] * (FLAC__int64)data[i-3]; + sum += qlp_coeff[1] * (FLAC__int64)data[i-2]; + sum += qlp_coeff[0] * (FLAC__int64)data[i-1]; + residual[i] = data[i] - (FLAC__int32)(sum >> lp_quantization); + } + } + } + else { + if(order == 6) { + for(i = 0; i < (int)data_len; i++) { + sum = 0; + sum += qlp_coeff[5] * (FLAC__int64)data[i-6]; + sum += qlp_coeff[4] * (FLAC__int64)data[i-5]; + sum += qlp_coeff[3] * (FLAC__int64)data[i-4]; + sum += qlp_coeff[2] * (FLAC__int64)data[i-3]; + sum += qlp_coeff[1] * (FLAC__int64)data[i-2]; + sum += qlp_coeff[0] * (FLAC__int64)data[i-1]; + residual[i] = data[i] - (FLAC__int32)(sum >> lp_quantization); + } + } + else { /* order == 5 */ + for(i = 0; i < (int)data_len; i++) { + sum = 0; + sum += qlp_coeff[4] * (FLAC__int64)data[i-5]; + sum += qlp_coeff[3] * (FLAC__int64)data[i-4]; + sum += qlp_coeff[2] * (FLAC__int64)data[i-3]; + sum += qlp_coeff[1] * (FLAC__int64)data[i-2]; + sum += qlp_coeff[0] * (FLAC__int64)data[i-1]; + residual[i] = data[i] - (FLAC__int32)(sum >> lp_quantization); + } + } + } + } + else { + if(order > 2) { + if(order == 4) { + for(i = 0; i < (int)data_len; i++) { + sum = 0; + sum += qlp_coeff[3] * (FLAC__int64)data[i-4]; + sum += qlp_coeff[2] * (FLAC__int64)data[i-3]; + sum += qlp_coeff[1] * (FLAC__int64)data[i-2]; + sum += qlp_coeff[0] * (FLAC__int64)data[i-1]; + residual[i] = data[i] - (FLAC__int32)(sum >> lp_quantization); + } + } + else { /* order == 3 */ + for(i = 0; i < (int)data_len; i++) { + sum = 0; + sum += qlp_coeff[2] * (FLAC__int64)data[i-3]; + sum += qlp_coeff[1] * (FLAC__int64)data[i-2]; + sum += qlp_coeff[0] * (FLAC__int64)data[i-1]; + residual[i] = data[i] - (FLAC__int32)(sum >> lp_quantization); + } + } + } + else { + if(order == 2) { + for(i = 0; i < (int)data_len; i++) { + sum = 0; + sum += qlp_coeff[1] * (FLAC__int64)data[i-2]; + sum += qlp_coeff[0] * (FLAC__int64)data[i-1]; + residual[i] = data[i] - (FLAC__int32)(sum >> lp_quantization); + } + } + else { /* order == 1 */ + for(i = 0; i < (int)data_len; i++) + residual[i] = data[i] - (FLAC__int32)((qlp_coeff[0] * (FLAC__int64)data[i-1]) >> lp_quantization); + } + } + } + } + else { /* order > 12 */ + for(i = 0; i < (int)data_len; i++) { + sum = 0; + switch(order) { + case 32: sum += qlp_coeff[31] * (FLAC__int64)data[i-32]; + case 31: sum += qlp_coeff[30] * (FLAC__int64)data[i-31]; + case 30: sum += qlp_coeff[29] * (FLAC__int64)data[i-30]; + case 29: sum += qlp_coeff[28] * (FLAC__int64)data[i-29]; + case 28: sum += qlp_coeff[27] * (FLAC__int64)data[i-28]; + case 27: sum += qlp_coeff[26] * (FLAC__int64)data[i-27]; + case 26: sum += qlp_coeff[25] * (FLAC__int64)data[i-26]; + case 25: sum += qlp_coeff[24] * (FLAC__int64)data[i-25]; + case 24: sum += qlp_coeff[23] * (FLAC__int64)data[i-24]; + case 23: sum += qlp_coeff[22] * (FLAC__int64)data[i-23]; + case 22: sum += qlp_coeff[21] * (FLAC__int64)data[i-22]; + case 21: sum += qlp_coeff[20] * (FLAC__int64)data[i-21]; + case 20: sum += qlp_coeff[19] * (FLAC__int64)data[i-20]; + case 19: sum += qlp_coeff[18] * (FLAC__int64)data[i-19]; + case 18: sum += qlp_coeff[17] * (FLAC__int64)data[i-18]; + case 17: sum += qlp_coeff[16] * (FLAC__int64)data[i-17]; + case 16: sum += qlp_coeff[15] * (FLAC__int64)data[i-16]; + case 15: sum += qlp_coeff[14] * (FLAC__int64)data[i-15]; + case 14: sum += qlp_coeff[13] * (FLAC__int64)data[i-14]; + case 13: sum += qlp_coeff[12] * (FLAC__int64)data[i-13]; + sum += qlp_coeff[11] * (FLAC__int64)data[i-12]; + sum += qlp_coeff[10] * (FLAC__int64)data[i-11]; + sum += qlp_coeff[ 9] * (FLAC__int64)data[i-10]; + sum += qlp_coeff[ 8] * (FLAC__int64)data[i- 9]; + sum += qlp_coeff[ 7] * (FLAC__int64)data[i- 8]; + sum += qlp_coeff[ 6] * (FLAC__int64)data[i- 7]; + sum += qlp_coeff[ 5] * (FLAC__int64)data[i- 6]; + sum += qlp_coeff[ 4] * (FLAC__int64)data[i- 5]; + sum += qlp_coeff[ 3] * (FLAC__int64)data[i- 4]; + sum += qlp_coeff[ 2] * (FLAC__int64)data[i- 3]; + sum += qlp_coeff[ 1] * (FLAC__int64)data[i- 2]; + sum += qlp_coeff[ 0] * (FLAC__int64)data[i- 1]; + } + residual[i] = data[i] - (FLAC__int32)(sum >> lp_quantization); + } + } +} +#endif + +#endif /* !defined FLAC__INTEGER_ONLY_LIBRARY */ + +void FLAC__lpc_restore_signal(const FLAC__int32 * flac_restrict residual, unsigned data_len, const FLAC__int32 * flac_restrict qlp_coeff, unsigned order, int lp_quantization, FLAC__int32 * flac_restrict data) +#if defined(FLAC__OVERFLOW_DETECT) || !defined(FLAC__LPC_UNROLLED_FILTER_LOOPS) +{ + FLAC__int64 sumo; + unsigned i, j; + FLAC__int32 sum; + const FLAC__int32 *r = residual, *history; + +#ifdef FLAC__OVERFLOW_DETECT_VERBOSE + fprintf(stderr,"FLAC__lpc_restore_signal: data_len=%d, order=%u, lpq=%d",data_len,order,lp_quantization); + for(i=0;i 0); + + for(i = 0; i < data_len; i++) { + sumo = 0; + sum = 0; + history = data; + for(j = 0; j < order; j++) { + sum += qlp_coeff[j] * (*(--history)); + sumo += (FLAC__int64)qlp_coeff[j] * (FLAC__int64)(*history); + if(sumo > 2147483647ll || sumo < -2147483648ll) + fprintf(stderr,"FLAC__lpc_restore_signal: OVERFLOW, i=%u, j=%u, c=%d, d=%d, sumo=%" PRId64 "\n",i,j,qlp_coeff[j],*history,sumo); + } + *(data++) = *(r++) + (sum >> lp_quantization); + } + + /* Here's a slower but clearer version: + for(i = 0; i < data_len; i++) { + sum = 0; + for(j = 0; j < order; j++) + sum += qlp_coeff[j] * data[i-j-1]; + data[i] = residual[i] + (sum >> lp_quantization); + } + */ +} +#else /* fully unrolled version for normal use */ +{ + int i; + FLAC__int32 sum; + + FLAC__ASSERT(order > 0); + FLAC__ASSERT(order <= 32); + + /* + * We do unique versions up to 12th order since that's the subset limit. + * Also they are roughly ordered to match frequency of occurrence to + * minimize branching. + */ + if(order <= 12) { + if(order > 8) { + if(order > 10) { + if(order == 12) { + for(i = 0; i < (int)data_len; i++) { + sum = 0; + sum += qlp_coeff[11] * data[i-12]; + sum += qlp_coeff[10] * data[i-11]; + sum += qlp_coeff[9] * data[i-10]; + sum += qlp_coeff[8] * data[i-9]; + sum += qlp_coeff[7] * data[i-8]; + sum += qlp_coeff[6] * data[i-7]; + sum += qlp_coeff[5] * data[i-6]; + sum += qlp_coeff[4] * data[i-5]; + sum += qlp_coeff[3] * data[i-4]; + sum += qlp_coeff[2] * data[i-3]; + sum += qlp_coeff[1] * data[i-2]; + sum += qlp_coeff[0] * data[i-1]; + data[i] = residual[i] + (sum >> lp_quantization); + } + } + else { /* order == 11 */ + for(i = 0; i < (int)data_len; i++) { + sum = 0; + sum += qlp_coeff[10] * data[i-11]; + sum += qlp_coeff[9] * data[i-10]; + sum += qlp_coeff[8] * data[i-9]; + sum += qlp_coeff[7] * data[i-8]; + sum += qlp_coeff[6] * data[i-7]; + sum += qlp_coeff[5] * data[i-6]; + sum += qlp_coeff[4] * data[i-5]; + sum += qlp_coeff[3] * data[i-4]; + sum += qlp_coeff[2] * data[i-3]; + sum += qlp_coeff[1] * data[i-2]; + sum += qlp_coeff[0] * data[i-1]; + data[i] = residual[i] + (sum >> lp_quantization); + } + } + } + else { + if(order == 10) { + for(i = 0; i < (int)data_len; i++) { + sum = 0; + sum += qlp_coeff[9] * data[i-10]; + sum += qlp_coeff[8] * data[i-9]; + sum += qlp_coeff[7] * data[i-8]; + sum += qlp_coeff[6] * data[i-7]; + sum += qlp_coeff[5] * data[i-6]; + sum += qlp_coeff[4] * data[i-5]; + sum += qlp_coeff[3] * data[i-4]; + sum += qlp_coeff[2] * data[i-3]; + sum += qlp_coeff[1] * data[i-2]; + sum += qlp_coeff[0] * data[i-1]; + data[i] = residual[i] + (sum >> lp_quantization); + } + } + else { /* order == 9 */ + for(i = 0; i < (int)data_len; i++) { + sum = 0; + sum += qlp_coeff[8] * data[i-9]; + sum += qlp_coeff[7] * data[i-8]; + sum += qlp_coeff[6] * data[i-7]; + sum += qlp_coeff[5] * data[i-6]; + sum += qlp_coeff[4] * data[i-5]; + sum += qlp_coeff[3] * data[i-4]; + sum += qlp_coeff[2] * data[i-3]; + sum += qlp_coeff[1] * data[i-2]; + sum += qlp_coeff[0] * data[i-1]; + data[i] = residual[i] + (sum >> lp_quantization); + } + } + } + } + else if(order > 4) { + if(order > 6) { + if(order == 8) { + for(i = 0; i < (int)data_len; i++) { + sum = 0; + sum += qlp_coeff[7] * data[i-8]; + sum += qlp_coeff[6] * data[i-7]; + sum += qlp_coeff[5] * data[i-6]; + sum += qlp_coeff[4] * data[i-5]; + sum += qlp_coeff[3] * data[i-4]; + sum += qlp_coeff[2] * data[i-3]; + sum += qlp_coeff[1] * data[i-2]; + sum += qlp_coeff[0] * data[i-1]; + data[i] = residual[i] + (sum >> lp_quantization); + } + } + else { /* order == 7 */ + for(i = 0; i < (int)data_len; i++) { + sum = 0; + sum += qlp_coeff[6] * data[i-7]; + sum += qlp_coeff[5] * data[i-6]; + sum += qlp_coeff[4] * data[i-5]; + sum += qlp_coeff[3] * data[i-4]; + sum += qlp_coeff[2] * data[i-3]; + sum += qlp_coeff[1] * data[i-2]; + sum += qlp_coeff[0] * data[i-1]; + data[i] = residual[i] + (sum >> lp_quantization); + } + } + } + else { + if(order == 6) { + for(i = 0; i < (int)data_len; i++) { + sum = 0; + sum += qlp_coeff[5] * data[i-6]; + sum += qlp_coeff[4] * data[i-5]; + sum += qlp_coeff[3] * data[i-4]; + sum += qlp_coeff[2] * data[i-3]; + sum += qlp_coeff[1] * data[i-2]; + sum += qlp_coeff[0] * data[i-1]; + data[i] = residual[i] + (sum >> lp_quantization); + } + } + else { /* order == 5 */ + for(i = 0; i < (int)data_len; i++) { + sum = 0; + sum += qlp_coeff[4] * data[i-5]; + sum += qlp_coeff[3] * data[i-4]; + sum += qlp_coeff[2] * data[i-3]; + sum += qlp_coeff[1] * data[i-2]; + sum += qlp_coeff[0] * data[i-1]; + data[i] = residual[i] + (sum >> lp_quantization); + } + } + } + } + else { + if(order > 2) { + if(order == 4) { + for(i = 0; i < (int)data_len; i++) { + sum = 0; + sum += qlp_coeff[3] * data[i-4]; + sum += qlp_coeff[2] * data[i-3]; + sum += qlp_coeff[1] * data[i-2]; + sum += qlp_coeff[0] * data[i-1]; + data[i] = residual[i] + (sum >> lp_quantization); + } + } + else { /* order == 3 */ + for(i = 0; i < (int)data_len; i++) { + sum = 0; + sum += qlp_coeff[2] * data[i-3]; + sum += qlp_coeff[1] * data[i-2]; + sum += qlp_coeff[0] * data[i-1]; + data[i] = residual[i] + (sum >> lp_quantization); + } + } + } + else { + if(order == 2) { + for(i = 0; i < (int)data_len; i++) { + sum = 0; + sum += qlp_coeff[1] * data[i-2]; + sum += qlp_coeff[0] * data[i-1]; + data[i] = residual[i] + (sum >> lp_quantization); + } + } + else { /* order == 1 */ + for(i = 0; i < (int)data_len; i++) + data[i] = residual[i] + ((qlp_coeff[0] * data[i-1]) >> lp_quantization); + } + } + } + } + else { /* order > 12 */ + for(i = 0; i < (int)data_len; i++) { + sum = 0; + switch(order) { + case 32: sum += qlp_coeff[31] * data[i-32]; + case 31: sum += qlp_coeff[30] * data[i-31]; + case 30: sum += qlp_coeff[29] * data[i-30]; + case 29: sum += qlp_coeff[28] * data[i-29]; + case 28: sum += qlp_coeff[27] * data[i-28]; + case 27: sum += qlp_coeff[26] * data[i-27]; + case 26: sum += qlp_coeff[25] * data[i-26]; + case 25: sum += qlp_coeff[24] * data[i-25]; + case 24: sum += qlp_coeff[23] * data[i-24]; + case 23: sum += qlp_coeff[22] * data[i-23]; + case 22: sum += qlp_coeff[21] * data[i-22]; + case 21: sum += qlp_coeff[20] * data[i-21]; + case 20: sum += qlp_coeff[19] * data[i-20]; + case 19: sum += qlp_coeff[18] * data[i-19]; + case 18: sum += qlp_coeff[17] * data[i-18]; + case 17: sum += qlp_coeff[16] * data[i-17]; + case 16: sum += qlp_coeff[15] * data[i-16]; + case 15: sum += qlp_coeff[14] * data[i-15]; + case 14: sum += qlp_coeff[13] * data[i-14]; + case 13: sum += qlp_coeff[12] * data[i-13]; + sum += qlp_coeff[11] * data[i-12]; + sum += qlp_coeff[10] * data[i-11]; + sum += qlp_coeff[ 9] * data[i-10]; + sum += qlp_coeff[ 8] * data[i- 9]; + sum += qlp_coeff[ 7] * data[i- 8]; + sum += qlp_coeff[ 6] * data[i- 7]; + sum += qlp_coeff[ 5] * data[i- 6]; + sum += qlp_coeff[ 4] * data[i- 5]; + sum += qlp_coeff[ 3] * data[i- 4]; + sum += qlp_coeff[ 2] * data[i- 3]; + sum += qlp_coeff[ 1] * data[i- 2]; + sum += qlp_coeff[ 0] * data[i- 1]; + } + data[i] = residual[i] + (sum >> lp_quantization); + } + } +} +#endif + +void FLAC__lpc_restore_signal_wide(const FLAC__int32 * flac_restrict residual, unsigned data_len, const FLAC__int32 * flac_restrict qlp_coeff, unsigned order, int lp_quantization, FLAC__int32 * flac_restrict data) +#if defined(FLAC__OVERFLOW_DETECT) || !defined(FLAC__LPC_UNROLLED_FILTER_LOOPS) +{ + unsigned i, j; + FLAC__int64 sum; + const FLAC__int32 *r = residual, *history; + +#ifdef FLAC__OVERFLOW_DETECT_VERBOSE + fprintf(stderr,"FLAC__lpc_restore_signal_wide: data_len=%d, order=%u, lpq=%d",data_len,order,lp_quantization); + for(i=0;i 0); + + for(i = 0; i < data_len; i++) { + sum = 0; + history = data; + for(j = 0; j < order; j++) + sum += (FLAC__int64)qlp_coeff[j] * (FLAC__int64)(*(--history)); + if(FLAC__bitmath_silog2(sum >> lp_quantization) > 32) { + fprintf(stderr,"FLAC__lpc_restore_signal_wide: OVERFLOW, i=%u, sum=%" PRId64 "\n", i, (sum >> lp_quantization)); + break; + } + if(FLAC__bitmath_silog2((FLAC__int64)(*r) + (sum >> lp_quantization)) > 32) { + fprintf(stderr,"FLAC__lpc_restore_signal_wide: OVERFLOW, i=%u, residual=%d, sum=%" PRId64 ", data=%" PRId64 "\n", i, *r, (sum >> lp_quantization), ((FLAC__int64)(*r) + (sum >> lp_quantization))); + break; + } + *(data++) = *(r++) + (FLAC__int32)(sum >> lp_quantization); + } +} +#else /* fully unrolled version for normal use */ +{ + int i; + FLAC__int64 sum; + + FLAC__ASSERT(order > 0); + FLAC__ASSERT(order <= 32); + + /* + * We do unique versions up to 12th order since that's the subset limit. + * Also they are roughly ordered to match frequency of occurrence to + * minimize branching. + */ + if(order <= 12) { + if(order > 8) { + if(order > 10) { + if(order == 12) { + for(i = 0; i < (int)data_len; i++) { + sum = 0; + sum += qlp_coeff[11] * (FLAC__int64)data[i-12]; + sum += qlp_coeff[10] * (FLAC__int64)data[i-11]; + sum += qlp_coeff[9] * (FLAC__int64)data[i-10]; + sum += qlp_coeff[8] * (FLAC__int64)data[i-9]; + sum += qlp_coeff[7] * (FLAC__int64)data[i-8]; + sum += qlp_coeff[6] * (FLAC__int64)data[i-7]; + sum += qlp_coeff[5] * (FLAC__int64)data[i-6]; + sum += qlp_coeff[4] * (FLAC__int64)data[i-5]; + sum += qlp_coeff[3] * (FLAC__int64)data[i-4]; + sum += qlp_coeff[2] * (FLAC__int64)data[i-3]; + sum += qlp_coeff[1] * (FLAC__int64)data[i-2]; + sum += qlp_coeff[0] * (FLAC__int64)data[i-1]; + data[i] = residual[i] + (FLAC__int32)(sum >> lp_quantization); + } + } + else { /* order == 11 */ + for(i = 0; i < (int)data_len; i++) { + sum = 0; + sum += qlp_coeff[10] * (FLAC__int64)data[i-11]; + sum += qlp_coeff[9] * (FLAC__int64)data[i-10]; + sum += qlp_coeff[8] * (FLAC__int64)data[i-9]; + sum += qlp_coeff[7] * (FLAC__int64)data[i-8]; + sum += qlp_coeff[6] * (FLAC__int64)data[i-7]; + sum += qlp_coeff[5] * (FLAC__int64)data[i-6]; + sum += qlp_coeff[4] * (FLAC__int64)data[i-5]; + sum += qlp_coeff[3] * (FLAC__int64)data[i-4]; + sum += qlp_coeff[2] * (FLAC__int64)data[i-3]; + sum += qlp_coeff[1] * (FLAC__int64)data[i-2]; + sum += qlp_coeff[0] * (FLAC__int64)data[i-1]; + data[i] = residual[i] + (FLAC__int32)(sum >> lp_quantization); + } + } + } + else { + if(order == 10) { + for(i = 0; i < (int)data_len; i++) { + sum = 0; + sum += qlp_coeff[9] * (FLAC__int64)data[i-10]; + sum += qlp_coeff[8] * (FLAC__int64)data[i-9]; + sum += qlp_coeff[7] * (FLAC__int64)data[i-8]; + sum += qlp_coeff[6] * (FLAC__int64)data[i-7]; + sum += qlp_coeff[5] * (FLAC__int64)data[i-6]; + sum += qlp_coeff[4] * (FLAC__int64)data[i-5]; + sum += qlp_coeff[3] * (FLAC__int64)data[i-4]; + sum += qlp_coeff[2] * (FLAC__int64)data[i-3]; + sum += qlp_coeff[1] * (FLAC__int64)data[i-2]; + sum += qlp_coeff[0] * (FLAC__int64)data[i-1]; + data[i] = residual[i] + (FLAC__int32)(sum >> lp_quantization); + } + } + else { /* order == 9 */ + for(i = 0; i < (int)data_len; i++) { + sum = 0; + sum += qlp_coeff[8] * (FLAC__int64)data[i-9]; + sum += qlp_coeff[7] * (FLAC__int64)data[i-8]; + sum += qlp_coeff[6] * (FLAC__int64)data[i-7]; + sum += qlp_coeff[5] * (FLAC__int64)data[i-6]; + sum += qlp_coeff[4] * (FLAC__int64)data[i-5]; + sum += qlp_coeff[3] * (FLAC__int64)data[i-4]; + sum += qlp_coeff[2] * (FLAC__int64)data[i-3]; + sum += qlp_coeff[1] * (FLAC__int64)data[i-2]; + sum += qlp_coeff[0] * (FLAC__int64)data[i-1]; + data[i] = residual[i] + (FLAC__int32)(sum >> lp_quantization); + } + } + } + } + else if(order > 4) { + if(order > 6) { + if(order == 8) { + for(i = 0; i < (int)data_len; i++) { + sum = 0; + sum += qlp_coeff[7] * (FLAC__int64)data[i-8]; + sum += qlp_coeff[6] * (FLAC__int64)data[i-7]; + sum += qlp_coeff[5] * (FLAC__int64)data[i-6]; + sum += qlp_coeff[4] * (FLAC__int64)data[i-5]; + sum += qlp_coeff[3] * (FLAC__int64)data[i-4]; + sum += qlp_coeff[2] * (FLAC__int64)data[i-3]; + sum += qlp_coeff[1] * (FLAC__int64)data[i-2]; + sum += qlp_coeff[0] * (FLAC__int64)data[i-1]; + data[i] = residual[i] + (FLAC__int32)(sum >> lp_quantization); + } + } + else { /* order == 7 */ + for(i = 0; i < (int)data_len; i++) { + sum = 0; + sum += qlp_coeff[6] * (FLAC__int64)data[i-7]; + sum += qlp_coeff[5] * (FLAC__int64)data[i-6]; + sum += qlp_coeff[4] * (FLAC__int64)data[i-5]; + sum += qlp_coeff[3] * (FLAC__int64)data[i-4]; + sum += qlp_coeff[2] * (FLAC__int64)data[i-3]; + sum += qlp_coeff[1] * (FLAC__int64)data[i-2]; + sum += qlp_coeff[0] * (FLAC__int64)data[i-1]; + data[i] = residual[i] + (FLAC__int32)(sum >> lp_quantization); + } + } + } + else { + if(order == 6) { + for(i = 0; i < (int)data_len; i++) { + sum = 0; + sum += qlp_coeff[5] * (FLAC__int64)data[i-6]; + sum += qlp_coeff[4] * (FLAC__int64)data[i-5]; + sum += qlp_coeff[3] * (FLAC__int64)data[i-4]; + sum += qlp_coeff[2] * (FLAC__int64)data[i-3]; + sum += qlp_coeff[1] * (FLAC__int64)data[i-2]; + sum += qlp_coeff[0] * (FLAC__int64)data[i-1]; + data[i] = residual[i] + (FLAC__int32)(sum >> lp_quantization); + } + } + else { /* order == 5 */ + for(i = 0; i < (int)data_len; i++) { + sum = 0; + sum += qlp_coeff[4] * (FLAC__int64)data[i-5]; + sum += qlp_coeff[3] * (FLAC__int64)data[i-4]; + sum += qlp_coeff[2] * (FLAC__int64)data[i-3]; + sum += qlp_coeff[1] * (FLAC__int64)data[i-2]; + sum += qlp_coeff[0] * (FLAC__int64)data[i-1]; + data[i] = residual[i] + (FLAC__int32)(sum >> lp_quantization); + } + } + } + } + else { + if(order > 2) { + if(order == 4) { + for(i = 0; i < (int)data_len; i++) { + sum = 0; + sum += qlp_coeff[3] * (FLAC__int64)data[i-4]; + sum += qlp_coeff[2] * (FLAC__int64)data[i-3]; + sum += qlp_coeff[1] * (FLAC__int64)data[i-2]; + sum += qlp_coeff[0] * (FLAC__int64)data[i-1]; + data[i] = residual[i] + (FLAC__int32)(sum >> lp_quantization); + } + } + else { /* order == 3 */ + for(i = 0; i < (int)data_len; i++) { + sum = 0; + sum += qlp_coeff[2] * (FLAC__int64)data[i-3]; + sum += qlp_coeff[1] * (FLAC__int64)data[i-2]; + sum += qlp_coeff[0] * (FLAC__int64)data[i-1]; + data[i] = residual[i] + (FLAC__int32)(sum >> lp_quantization); + } + } + } + else { + if(order == 2) { + for(i = 0; i < (int)data_len; i++) { + sum = 0; + sum += qlp_coeff[1] * (FLAC__int64)data[i-2]; + sum += qlp_coeff[0] * (FLAC__int64)data[i-1]; + data[i] = residual[i] + (FLAC__int32)(sum >> lp_quantization); + } + } + else { /* order == 1 */ + for(i = 0; i < (int)data_len; i++) + data[i] = residual[i] + (FLAC__int32)((qlp_coeff[0] * (FLAC__int64)data[i-1]) >> lp_quantization); + } + } + } + } + else { /* order > 12 */ + for(i = 0; i < (int)data_len; i++) { + sum = 0; + switch(order) { + case 32: sum += qlp_coeff[31] * (FLAC__int64)data[i-32]; + case 31: sum += qlp_coeff[30] * (FLAC__int64)data[i-31]; + case 30: sum += qlp_coeff[29] * (FLAC__int64)data[i-30]; + case 29: sum += qlp_coeff[28] * (FLAC__int64)data[i-29]; + case 28: sum += qlp_coeff[27] * (FLAC__int64)data[i-28]; + case 27: sum += qlp_coeff[26] * (FLAC__int64)data[i-27]; + case 26: sum += qlp_coeff[25] * (FLAC__int64)data[i-26]; + case 25: sum += qlp_coeff[24] * (FLAC__int64)data[i-25]; + case 24: sum += qlp_coeff[23] * (FLAC__int64)data[i-24]; + case 23: sum += qlp_coeff[22] * (FLAC__int64)data[i-23]; + case 22: sum += qlp_coeff[21] * (FLAC__int64)data[i-22]; + case 21: sum += qlp_coeff[20] * (FLAC__int64)data[i-21]; + case 20: sum += qlp_coeff[19] * (FLAC__int64)data[i-20]; + case 19: sum += qlp_coeff[18] * (FLAC__int64)data[i-19]; + case 18: sum += qlp_coeff[17] * (FLAC__int64)data[i-18]; + case 17: sum += qlp_coeff[16] * (FLAC__int64)data[i-17]; + case 16: sum += qlp_coeff[15] * (FLAC__int64)data[i-16]; + case 15: sum += qlp_coeff[14] * (FLAC__int64)data[i-15]; + case 14: sum += qlp_coeff[13] * (FLAC__int64)data[i-14]; + case 13: sum += qlp_coeff[12] * (FLAC__int64)data[i-13]; + sum += qlp_coeff[11] * (FLAC__int64)data[i-12]; + sum += qlp_coeff[10] * (FLAC__int64)data[i-11]; + sum += qlp_coeff[ 9] * (FLAC__int64)data[i-10]; + sum += qlp_coeff[ 8] * (FLAC__int64)data[i- 9]; + sum += qlp_coeff[ 7] * (FLAC__int64)data[i- 8]; + sum += qlp_coeff[ 6] * (FLAC__int64)data[i- 7]; + sum += qlp_coeff[ 5] * (FLAC__int64)data[i- 6]; + sum += qlp_coeff[ 4] * (FLAC__int64)data[i- 5]; + sum += qlp_coeff[ 3] * (FLAC__int64)data[i- 4]; + sum += qlp_coeff[ 2] * (FLAC__int64)data[i- 3]; + sum += qlp_coeff[ 1] * (FLAC__int64)data[i- 2]; + sum += qlp_coeff[ 0] * (FLAC__int64)data[i- 1]; + } + data[i] = residual[i] + (FLAC__int32)(sum >> lp_quantization); + } + } +} +#endif + +#if defined(_MSC_VER) +#pragma warning ( default : 4028 ) +#endif + +#ifndef FLAC__INTEGER_ONLY_LIBRARY + +double FLAC__lpc_compute_expected_bits_per_residual_sample(double lpc_error, unsigned total_samples) +{ + double error_scale; + + FLAC__ASSERT(total_samples > 0); + + error_scale = 0.5 / (double)total_samples; + + return FLAC__lpc_compute_expected_bits_per_residual_sample_with_error_scale(lpc_error, error_scale); +} + +double FLAC__lpc_compute_expected_bits_per_residual_sample_with_error_scale(double lpc_error, double error_scale) +{ + if(lpc_error > 0.0) { + double bps = (double)0.5 * log(error_scale * lpc_error) / M_LN2; + if(bps >= 0.0) + return bps; + else + return 0.0; + } + else if(lpc_error < 0.0) { /* error should not be negative but can happen due to inadequate floating-point resolution */ + return 1e32; + } + else { + return 0.0; + } +} + +unsigned FLAC__lpc_compute_best_order(const double lpc_error[], unsigned max_order, unsigned total_samples, unsigned overhead_bits_per_order) +{ + unsigned order, indx, best_index; /* 'index' the index into lpc_error; index==order-1 since lpc_error[0] is for order==1, lpc_error[1] is for order==2, etc */ + double bits, best_bits, error_scale; + + FLAC__ASSERT(max_order > 0); + FLAC__ASSERT(total_samples > 0); + + error_scale = 0.5 / (double)total_samples; + + best_index = 0; + best_bits = (unsigned)(-1); + + for(indx = 0, order = 1; indx < max_order; indx++, order++) { + bits = FLAC__lpc_compute_expected_bits_per_residual_sample_with_error_scale(lpc_error[indx], error_scale) * (double)(total_samples - order) + (double)(order * overhead_bits_per_order); + if(bits < best_bits) { + best_index = indx; + best_bits = bits; + } + } + + return best_index+1; /* +1 since indx of lpc_error[] is order-1 */ +} + +#endif /* !defined FLAC__INTEGER_ONLY_LIBRARY */ diff --git a/core/deps/flac/src/libFLAC/lpc_intrin_avx2.c b/core/deps/flac/src/libFLAC/lpc_intrin_avx2.c new file mode 100644 index 000000000..f9f5ccdb0 --- /dev/null +++ b/core/deps/flac/src/libFLAC/lpc_intrin_avx2.c @@ -0,0 +1,1122 @@ +/* libFLAC - Free Lossless Audio Codec library + * Copyright (C) 2000-2009 Josh Coalson + * Copyright (C) 2011-2016 Xiph.Org Foundation + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * - Neither the name of the Xiph.org Foundation nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifdef HAVE_CONFIG_H +# include +#endif + +#include "private/cpu.h" + +#ifndef FLAC__INTEGER_ONLY_LIBRARY +#ifndef FLAC__NO_ASM +#if (defined FLAC__CPU_IA32 || defined FLAC__CPU_X86_64) && FLAC__HAS_X86INTRIN +#include "private/lpc.h" +#ifdef FLAC__AVX2_SUPPORTED + +#include "FLAC/assert.h" +#include "FLAC/format.h" + +#include /* AVX2 */ + +FLAC__SSE_TARGET("avx2") +void FLAC__lpc_compute_residual_from_qlp_coefficients_16_intrin_avx2(const FLAC__int32 *data, unsigned data_len, const FLAC__int32 qlp_coeff[], unsigned order, int lp_quantization, FLAC__int32 residual[]) +{ + int i; + FLAC__int32 sum; + __m128i cnt = _mm_cvtsi32_si128(lp_quantization); + + FLAC__ASSERT(order > 0); + FLAC__ASSERT(order <= 32); + + if(order <= 12) { + if(order > 8) { + if(order > 10) { + if(order == 12) { + __m256i q0, q1, q2, q3, q4, q5, q6, q7, q8, q9, q10, q11; + q0 = _mm256_set1_epi32(0xffff & qlp_coeff[0 ]); + q1 = _mm256_set1_epi32(0xffff & qlp_coeff[1 ]); + q2 = _mm256_set1_epi32(0xffff & qlp_coeff[2 ]); + q3 = _mm256_set1_epi32(0xffff & qlp_coeff[3 ]); + q4 = _mm256_set1_epi32(0xffff & qlp_coeff[4 ]); + q5 = _mm256_set1_epi32(0xffff & qlp_coeff[5 ]); + q6 = _mm256_set1_epi32(0xffff & qlp_coeff[6 ]); + q7 = _mm256_set1_epi32(0xffff & qlp_coeff[7 ]); + q8 = _mm256_set1_epi32(0xffff & qlp_coeff[8 ]); + q9 = _mm256_set1_epi32(0xffff & qlp_coeff[9 ]); + q10 = _mm256_set1_epi32(0xffff & qlp_coeff[10]); + q11 = _mm256_set1_epi32(0xffff & qlp_coeff[11]); + + for(i = 0; i < (int)data_len-7; i+=8) { + __m256i summ, mull; + summ = _mm256_madd_epi16(q11, _mm256_loadu_si256((const __m256i*)(data+i-12))); + mull = _mm256_madd_epi16(q10, _mm256_loadu_si256((const __m256i*)(data+i-11))); summ = _mm256_add_epi32(summ, mull); + mull = _mm256_madd_epi16(q9, _mm256_loadu_si256((const __m256i*)(data+i-10))); summ = _mm256_add_epi32(summ, mull); + mull = _mm256_madd_epi16(q8, _mm256_loadu_si256((const __m256i*)(data+i-9 ))); summ = _mm256_add_epi32(summ, mull); + mull = _mm256_madd_epi16(q7, _mm256_loadu_si256((const __m256i*)(data+i-8 ))); summ = _mm256_add_epi32(summ, mull); + mull = _mm256_madd_epi16(q6, _mm256_loadu_si256((const __m256i*)(data+i-7 ))); summ = _mm256_add_epi32(summ, mull); + mull = _mm256_madd_epi16(q5, _mm256_loadu_si256((const __m256i*)(data+i-6 ))); summ = _mm256_add_epi32(summ, mull); + mull = _mm256_madd_epi16(q4, _mm256_loadu_si256((const __m256i*)(data+i-5 ))); summ = _mm256_add_epi32(summ, mull); + mull = _mm256_madd_epi16(q3, _mm256_loadu_si256((const __m256i*)(data+i-4 ))); summ = _mm256_add_epi32(summ, mull); + mull = _mm256_madd_epi16(q2, _mm256_loadu_si256((const __m256i*)(data+i-3 ))); summ = _mm256_add_epi32(summ, mull); + mull = _mm256_madd_epi16(q1, _mm256_loadu_si256((const __m256i*)(data+i-2 ))); summ = _mm256_add_epi32(summ, mull); + mull = _mm256_madd_epi16(q0, _mm256_loadu_si256((const __m256i*)(data+i-1 ))); summ = _mm256_add_epi32(summ, mull); + summ = _mm256_sra_epi32(summ, cnt); + _mm256_storeu_si256((__m256i*)(residual+i), _mm256_sub_epi32(_mm256_loadu_si256((const __m256i*)(data+i)), summ)); + } + } + else { /* order == 11 */ + __m256i q0, q1, q2, q3, q4, q5, q6, q7, q8, q9, q10; + q0 = _mm256_set1_epi32(0xffff & qlp_coeff[0 ]); + q1 = _mm256_set1_epi32(0xffff & qlp_coeff[1 ]); + q2 = _mm256_set1_epi32(0xffff & qlp_coeff[2 ]); + q3 = _mm256_set1_epi32(0xffff & qlp_coeff[3 ]); + q4 = _mm256_set1_epi32(0xffff & qlp_coeff[4 ]); + q5 = _mm256_set1_epi32(0xffff & qlp_coeff[5 ]); + q6 = _mm256_set1_epi32(0xffff & qlp_coeff[6 ]); + q7 = _mm256_set1_epi32(0xffff & qlp_coeff[7 ]); + q8 = _mm256_set1_epi32(0xffff & qlp_coeff[8 ]); + q9 = _mm256_set1_epi32(0xffff & qlp_coeff[9 ]); + q10 = _mm256_set1_epi32(0xffff & qlp_coeff[10]); + + for(i = 0; i < (int)data_len-7; i+=8) { + __m256i summ, mull; + summ = _mm256_madd_epi16(q10, _mm256_loadu_si256((const __m256i*)(data+i-11))); + mull = _mm256_madd_epi16(q9, _mm256_loadu_si256((const __m256i*)(data+i-10))); summ = _mm256_add_epi32(summ, mull); + mull = _mm256_madd_epi16(q8, _mm256_loadu_si256((const __m256i*)(data+i-9 ))); summ = _mm256_add_epi32(summ, mull); + mull = _mm256_madd_epi16(q7, _mm256_loadu_si256((const __m256i*)(data+i-8 ))); summ = _mm256_add_epi32(summ, mull); + mull = _mm256_madd_epi16(q6, _mm256_loadu_si256((const __m256i*)(data+i-7 ))); summ = _mm256_add_epi32(summ, mull); + mull = _mm256_madd_epi16(q5, _mm256_loadu_si256((const __m256i*)(data+i-6 ))); summ = _mm256_add_epi32(summ, mull); + mull = _mm256_madd_epi16(q4, _mm256_loadu_si256((const __m256i*)(data+i-5 ))); summ = _mm256_add_epi32(summ, mull); + mull = _mm256_madd_epi16(q3, _mm256_loadu_si256((const __m256i*)(data+i-4 ))); summ = _mm256_add_epi32(summ, mull); + mull = _mm256_madd_epi16(q2, _mm256_loadu_si256((const __m256i*)(data+i-3 ))); summ = _mm256_add_epi32(summ, mull); + mull = _mm256_madd_epi16(q1, _mm256_loadu_si256((const __m256i*)(data+i-2 ))); summ = _mm256_add_epi32(summ, mull); + mull = _mm256_madd_epi16(q0, _mm256_loadu_si256((const __m256i*)(data+i-1 ))); summ = _mm256_add_epi32(summ, mull); + summ = _mm256_sra_epi32(summ, cnt); + _mm256_storeu_si256((__m256i*)(residual+i), _mm256_sub_epi32(_mm256_loadu_si256((const __m256i*)(data+i)), summ)); + } + } + } + else { + if(order == 10) { + __m256i q0, q1, q2, q3, q4, q5, q6, q7, q8, q9; + q0 = _mm256_set1_epi32(0xffff & qlp_coeff[0 ]); + q1 = _mm256_set1_epi32(0xffff & qlp_coeff[1 ]); + q2 = _mm256_set1_epi32(0xffff & qlp_coeff[2 ]); + q3 = _mm256_set1_epi32(0xffff & qlp_coeff[3 ]); + q4 = _mm256_set1_epi32(0xffff & qlp_coeff[4 ]); + q5 = _mm256_set1_epi32(0xffff & qlp_coeff[5 ]); + q6 = _mm256_set1_epi32(0xffff & qlp_coeff[6 ]); + q7 = _mm256_set1_epi32(0xffff & qlp_coeff[7 ]); + q8 = _mm256_set1_epi32(0xffff & qlp_coeff[8 ]); + q9 = _mm256_set1_epi32(0xffff & qlp_coeff[9 ]); + + for(i = 0; i < (int)data_len-7; i+=8) { + __m256i summ, mull; + summ = _mm256_madd_epi16(q9, _mm256_loadu_si256((const __m256i*)(data+i-10))); + mull = _mm256_madd_epi16(q8, _mm256_loadu_si256((const __m256i*)(data+i-9 ))); summ = _mm256_add_epi32(summ, mull); + mull = _mm256_madd_epi16(q7, _mm256_loadu_si256((const __m256i*)(data+i-8 ))); summ = _mm256_add_epi32(summ, mull); + mull = _mm256_madd_epi16(q6, _mm256_loadu_si256((const __m256i*)(data+i-7 ))); summ = _mm256_add_epi32(summ, mull); + mull = _mm256_madd_epi16(q5, _mm256_loadu_si256((const __m256i*)(data+i-6 ))); summ = _mm256_add_epi32(summ, mull); + mull = _mm256_madd_epi16(q4, _mm256_loadu_si256((const __m256i*)(data+i-5 ))); summ = _mm256_add_epi32(summ, mull); + mull = _mm256_madd_epi16(q3, _mm256_loadu_si256((const __m256i*)(data+i-4 ))); summ = _mm256_add_epi32(summ, mull); + mull = _mm256_madd_epi16(q2, _mm256_loadu_si256((const __m256i*)(data+i-3 ))); summ = _mm256_add_epi32(summ, mull); + mull = _mm256_madd_epi16(q1, _mm256_loadu_si256((const __m256i*)(data+i-2 ))); summ = _mm256_add_epi32(summ, mull); + mull = _mm256_madd_epi16(q0, _mm256_loadu_si256((const __m256i*)(data+i-1 ))); summ = _mm256_add_epi32(summ, mull); + summ = _mm256_sra_epi32(summ, cnt); + _mm256_storeu_si256((__m256i*)(residual+i), _mm256_sub_epi32(_mm256_loadu_si256((const __m256i*)(data+i)), summ)); + } + } + else { /* order == 9 */ + __m256i q0, q1, q2, q3, q4, q5, q6, q7, q8; + q0 = _mm256_set1_epi32(0xffff & qlp_coeff[0 ]); + q1 = _mm256_set1_epi32(0xffff & qlp_coeff[1 ]); + q2 = _mm256_set1_epi32(0xffff & qlp_coeff[2 ]); + q3 = _mm256_set1_epi32(0xffff & qlp_coeff[3 ]); + q4 = _mm256_set1_epi32(0xffff & qlp_coeff[4 ]); + q5 = _mm256_set1_epi32(0xffff & qlp_coeff[5 ]); + q6 = _mm256_set1_epi32(0xffff & qlp_coeff[6 ]); + q7 = _mm256_set1_epi32(0xffff & qlp_coeff[7 ]); + q8 = _mm256_set1_epi32(0xffff & qlp_coeff[8 ]); + + for(i = 0; i < (int)data_len-7; i+=8) { + __m256i summ, mull; + summ = _mm256_madd_epi16(q8, _mm256_loadu_si256((const __m256i*)(data+i-9 ))); + mull = _mm256_madd_epi16(q7, _mm256_loadu_si256((const __m256i*)(data+i-8 ))); summ = _mm256_add_epi32(summ, mull); + mull = _mm256_madd_epi16(q6, _mm256_loadu_si256((const __m256i*)(data+i-7 ))); summ = _mm256_add_epi32(summ, mull); + mull = _mm256_madd_epi16(q5, _mm256_loadu_si256((const __m256i*)(data+i-6 ))); summ = _mm256_add_epi32(summ, mull); + mull = _mm256_madd_epi16(q4, _mm256_loadu_si256((const __m256i*)(data+i-5 ))); summ = _mm256_add_epi32(summ, mull); + mull = _mm256_madd_epi16(q3, _mm256_loadu_si256((const __m256i*)(data+i-4 ))); summ = _mm256_add_epi32(summ, mull); + mull = _mm256_madd_epi16(q2, _mm256_loadu_si256((const __m256i*)(data+i-3 ))); summ = _mm256_add_epi32(summ, mull); + mull = _mm256_madd_epi16(q1, _mm256_loadu_si256((const __m256i*)(data+i-2 ))); summ = _mm256_add_epi32(summ, mull); + mull = _mm256_madd_epi16(q0, _mm256_loadu_si256((const __m256i*)(data+i-1 ))); summ = _mm256_add_epi32(summ, mull); + summ = _mm256_sra_epi32(summ, cnt); + _mm256_storeu_si256((__m256i*)(residual+i), _mm256_sub_epi32(_mm256_loadu_si256((const __m256i*)(data+i)), summ)); + } + } + } + } + else if(order > 4) { + if(order > 6) { + if(order == 8) { + __m256i q0, q1, q2, q3, q4, q5, q6, q7; + q0 = _mm256_set1_epi32(0xffff & qlp_coeff[0 ]); + q1 = _mm256_set1_epi32(0xffff & qlp_coeff[1 ]); + q2 = _mm256_set1_epi32(0xffff & qlp_coeff[2 ]); + q3 = _mm256_set1_epi32(0xffff & qlp_coeff[3 ]); + q4 = _mm256_set1_epi32(0xffff & qlp_coeff[4 ]); + q5 = _mm256_set1_epi32(0xffff & qlp_coeff[5 ]); + q6 = _mm256_set1_epi32(0xffff & qlp_coeff[6 ]); + q7 = _mm256_set1_epi32(0xffff & qlp_coeff[7 ]); + + for(i = 0; i < (int)data_len-7; i+=8) { + __m256i summ, mull; + summ = _mm256_madd_epi16(q7, _mm256_loadu_si256((const __m256i*)(data+i-8 ))); + mull = _mm256_madd_epi16(q6, _mm256_loadu_si256((const __m256i*)(data+i-7 ))); summ = _mm256_add_epi32(summ, mull); + mull = _mm256_madd_epi16(q5, _mm256_loadu_si256((const __m256i*)(data+i-6 ))); summ = _mm256_add_epi32(summ, mull); + mull = _mm256_madd_epi16(q4, _mm256_loadu_si256((const __m256i*)(data+i-5 ))); summ = _mm256_add_epi32(summ, mull); + mull = _mm256_madd_epi16(q3, _mm256_loadu_si256((const __m256i*)(data+i-4 ))); summ = _mm256_add_epi32(summ, mull); + mull = _mm256_madd_epi16(q2, _mm256_loadu_si256((const __m256i*)(data+i-3 ))); summ = _mm256_add_epi32(summ, mull); + mull = _mm256_madd_epi16(q1, _mm256_loadu_si256((const __m256i*)(data+i-2 ))); summ = _mm256_add_epi32(summ, mull); + mull = _mm256_madd_epi16(q0, _mm256_loadu_si256((const __m256i*)(data+i-1 ))); summ = _mm256_add_epi32(summ, mull); + summ = _mm256_sra_epi32(summ, cnt); + _mm256_storeu_si256((__m256i*)(residual+i), _mm256_sub_epi32(_mm256_loadu_si256((const __m256i*)(data+i)), summ)); + } + } + else { /* order == 7 */ + __m256i q0, q1, q2, q3, q4, q5, q6; + q0 = _mm256_set1_epi32(0xffff & qlp_coeff[0 ]); + q1 = _mm256_set1_epi32(0xffff & qlp_coeff[1 ]); + q2 = _mm256_set1_epi32(0xffff & qlp_coeff[2 ]); + q3 = _mm256_set1_epi32(0xffff & qlp_coeff[3 ]); + q4 = _mm256_set1_epi32(0xffff & qlp_coeff[4 ]); + q5 = _mm256_set1_epi32(0xffff & qlp_coeff[5 ]); + q6 = _mm256_set1_epi32(0xffff & qlp_coeff[6 ]); + + for(i = 0; i < (int)data_len-7; i+=8) { + __m256i summ, mull; + summ = _mm256_madd_epi16(q6, _mm256_loadu_si256((const __m256i*)(data+i-7 ))); + mull = _mm256_madd_epi16(q5, _mm256_loadu_si256((const __m256i*)(data+i-6 ))); summ = _mm256_add_epi32(summ, mull); + mull = _mm256_madd_epi16(q4, _mm256_loadu_si256((const __m256i*)(data+i-5 ))); summ = _mm256_add_epi32(summ, mull); + mull = _mm256_madd_epi16(q3, _mm256_loadu_si256((const __m256i*)(data+i-4 ))); summ = _mm256_add_epi32(summ, mull); + mull = _mm256_madd_epi16(q2, _mm256_loadu_si256((const __m256i*)(data+i-3 ))); summ = _mm256_add_epi32(summ, mull); + mull = _mm256_madd_epi16(q1, _mm256_loadu_si256((const __m256i*)(data+i-2 ))); summ = _mm256_add_epi32(summ, mull); + mull = _mm256_madd_epi16(q0, _mm256_loadu_si256((const __m256i*)(data+i-1 ))); summ = _mm256_add_epi32(summ, mull); + summ = _mm256_sra_epi32(summ, cnt); + _mm256_storeu_si256((__m256i*)(residual+i), _mm256_sub_epi32(_mm256_loadu_si256((const __m256i*)(data+i)), summ)); + } + } + } + else { + if(order == 6) { + __m256i q0, q1, q2, q3, q4, q5; + q0 = _mm256_set1_epi32(0xffff & qlp_coeff[0 ]); + q1 = _mm256_set1_epi32(0xffff & qlp_coeff[1 ]); + q2 = _mm256_set1_epi32(0xffff & qlp_coeff[2 ]); + q3 = _mm256_set1_epi32(0xffff & qlp_coeff[3 ]); + q4 = _mm256_set1_epi32(0xffff & qlp_coeff[4 ]); + q5 = _mm256_set1_epi32(0xffff & qlp_coeff[5 ]); + + for(i = 0; i < (int)data_len-7; i+=8) { + __m256i summ, mull; + summ = _mm256_madd_epi16(q5, _mm256_loadu_si256((const __m256i*)(data+i-6 ))); + mull = _mm256_madd_epi16(q4, _mm256_loadu_si256((const __m256i*)(data+i-5 ))); summ = _mm256_add_epi32(summ, mull); + mull = _mm256_madd_epi16(q3, _mm256_loadu_si256((const __m256i*)(data+i-4 ))); summ = _mm256_add_epi32(summ, mull); + mull = _mm256_madd_epi16(q2, _mm256_loadu_si256((const __m256i*)(data+i-3 ))); summ = _mm256_add_epi32(summ, mull); + mull = _mm256_madd_epi16(q1, _mm256_loadu_si256((const __m256i*)(data+i-2 ))); summ = _mm256_add_epi32(summ, mull); + mull = _mm256_madd_epi16(q0, _mm256_loadu_si256((const __m256i*)(data+i-1 ))); summ = _mm256_add_epi32(summ, mull); + summ = _mm256_sra_epi32(summ, cnt); + _mm256_storeu_si256((__m256i*)(residual+i), _mm256_sub_epi32(_mm256_loadu_si256((const __m256i*)(data+i)), summ)); + } + } + else { /* order == 5 */ + __m256i q0, q1, q2, q3, q4; + q0 = _mm256_set1_epi32(0xffff & qlp_coeff[0 ]); + q1 = _mm256_set1_epi32(0xffff & qlp_coeff[1 ]); + q2 = _mm256_set1_epi32(0xffff & qlp_coeff[2 ]); + q3 = _mm256_set1_epi32(0xffff & qlp_coeff[3 ]); + q4 = _mm256_set1_epi32(0xffff & qlp_coeff[4 ]); + + for(i = 0; i < (int)data_len-7; i+=8) { + __m256i summ, mull; + summ = _mm256_madd_epi16(q4, _mm256_loadu_si256((const __m256i*)(data+i-5 ))); + mull = _mm256_madd_epi16(q3, _mm256_loadu_si256((const __m256i*)(data+i-4 ))); summ = _mm256_add_epi32(summ, mull); + mull = _mm256_madd_epi16(q2, _mm256_loadu_si256((const __m256i*)(data+i-3 ))); summ = _mm256_add_epi32(summ, mull); + mull = _mm256_madd_epi16(q1, _mm256_loadu_si256((const __m256i*)(data+i-2 ))); summ = _mm256_add_epi32(summ, mull); + mull = _mm256_madd_epi16(q0, _mm256_loadu_si256((const __m256i*)(data+i-1 ))); summ = _mm256_add_epi32(summ, mull); + summ = _mm256_sra_epi32(summ, cnt); + _mm256_storeu_si256((__m256i*)(residual+i), _mm256_sub_epi32(_mm256_loadu_si256((const __m256i*)(data+i)), summ)); + } + } + } + } + else { + if(order > 2) { + if(order == 4) { + __m256i q0, q1, q2, q3; + q0 = _mm256_set1_epi32(0xffff & qlp_coeff[0 ]); + q1 = _mm256_set1_epi32(0xffff & qlp_coeff[1 ]); + q2 = _mm256_set1_epi32(0xffff & qlp_coeff[2 ]); + q3 = _mm256_set1_epi32(0xffff & qlp_coeff[3 ]); + + for(i = 0; i < (int)data_len-7; i+=8) { + __m256i summ, mull; + summ = _mm256_madd_epi16(q3, _mm256_loadu_si256((const __m256i*)(data+i-4 ))); + mull = _mm256_madd_epi16(q2, _mm256_loadu_si256((const __m256i*)(data+i-3 ))); summ = _mm256_add_epi32(summ, mull); + mull = _mm256_madd_epi16(q1, _mm256_loadu_si256((const __m256i*)(data+i-2 ))); summ = _mm256_add_epi32(summ, mull); + mull = _mm256_madd_epi16(q0, _mm256_loadu_si256((const __m256i*)(data+i-1 ))); summ = _mm256_add_epi32(summ, mull); + summ = _mm256_sra_epi32(summ, cnt); + _mm256_storeu_si256((__m256i*)(residual+i), _mm256_sub_epi32(_mm256_loadu_si256((const __m256i*)(data+i)), summ)); + } + } + else { /* order == 3 */ + __m256i q0, q1, q2; + q0 = _mm256_set1_epi32(0xffff & qlp_coeff[0 ]); + q1 = _mm256_set1_epi32(0xffff & qlp_coeff[1 ]); + q2 = _mm256_set1_epi32(0xffff & qlp_coeff[2 ]); + + for(i = 0; i < (int)data_len-7; i+=8) { + __m256i summ, mull; + summ = _mm256_madd_epi16(q2, _mm256_loadu_si256((const __m256i*)(data+i-3 ))); + mull = _mm256_madd_epi16(q1, _mm256_loadu_si256((const __m256i*)(data+i-2 ))); summ = _mm256_add_epi32(summ, mull); + mull = _mm256_madd_epi16(q0, _mm256_loadu_si256((const __m256i*)(data+i-1 ))); summ = _mm256_add_epi32(summ, mull); + summ = _mm256_sra_epi32(summ, cnt); + _mm256_storeu_si256((__m256i*)(residual+i), _mm256_sub_epi32(_mm256_loadu_si256((const __m256i*)(data+i)), summ)); + } + } + } + else { + if(order == 2) { + __m256i q0, q1; + q0 = _mm256_set1_epi32(0xffff & qlp_coeff[0 ]); + q1 = _mm256_set1_epi32(0xffff & qlp_coeff[1 ]); + + for(i = 0; i < (int)data_len-7; i+=8) { + __m256i summ, mull; + summ = _mm256_madd_epi16(q1, _mm256_loadu_si256((const __m256i*)(data+i-2 ))); + mull = _mm256_madd_epi16(q0, _mm256_loadu_si256((const __m256i*)(data+i-1 ))); summ = _mm256_add_epi32(summ, mull); + summ = _mm256_sra_epi32(summ, cnt); + _mm256_storeu_si256((__m256i*)(residual+i), _mm256_sub_epi32(_mm256_loadu_si256((const __m256i*)(data+i)), summ)); + } + } + else { /* order == 1 */ + __m256i q0; + q0 = _mm256_set1_epi32(0xffff & qlp_coeff[0 ]); + + for(i = 0; i < (int)data_len-7; i+=8) { + __m256i summ; + summ = _mm256_madd_epi16(q0, _mm256_loadu_si256((const __m256i*)(data+i-1 ))); + summ = _mm256_sra_epi32(summ, cnt); + _mm256_storeu_si256((__m256i*)(residual+i), _mm256_sub_epi32(_mm256_loadu_si256((const __m256i*)(data+i)), summ)); + } + } + } + } + for(; i < (int)data_len; i++) { + sum = 0; + switch(order) { + case 12: sum += qlp_coeff[11] * data[i-12]; + case 11: sum += qlp_coeff[10] * data[i-11]; + case 10: sum += qlp_coeff[ 9] * data[i-10]; + case 9: sum += qlp_coeff[ 8] * data[i- 9]; + case 8: sum += qlp_coeff[ 7] * data[i- 8]; + case 7: sum += qlp_coeff[ 6] * data[i- 7]; + case 6: sum += qlp_coeff[ 5] * data[i- 6]; + case 5: sum += qlp_coeff[ 4] * data[i- 5]; + case 4: sum += qlp_coeff[ 3] * data[i- 4]; + case 3: sum += qlp_coeff[ 2] * data[i- 3]; + case 2: sum += qlp_coeff[ 1] * data[i- 2]; + case 1: sum += qlp_coeff[ 0] * data[i- 1]; + } + residual[i] = data[i] - (sum >> lp_quantization); + } + } + else { /* order > 12 */ + for(i = 0; i < (int)data_len; i++) { + sum = 0; + switch(order) { + case 32: sum += qlp_coeff[31] * data[i-32]; + case 31: sum += qlp_coeff[30] * data[i-31]; + case 30: sum += qlp_coeff[29] * data[i-30]; + case 29: sum += qlp_coeff[28] * data[i-29]; + case 28: sum += qlp_coeff[27] * data[i-28]; + case 27: sum += qlp_coeff[26] * data[i-27]; + case 26: sum += qlp_coeff[25] * data[i-26]; + case 25: sum += qlp_coeff[24] * data[i-25]; + case 24: sum += qlp_coeff[23] * data[i-24]; + case 23: sum += qlp_coeff[22] * data[i-23]; + case 22: sum += qlp_coeff[21] * data[i-22]; + case 21: sum += qlp_coeff[20] * data[i-21]; + case 20: sum += qlp_coeff[19] * data[i-20]; + case 19: sum += qlp_coeff[18] * data[i-19]; + case 18: sum += qlp_coeff[17] * data[i-18]; + case 17: sum += qlp_coeff[16] * data[i-17]; + case 16: sum += qlp_coeff[15] * data[i-16]; + case 15: sum += qlp_coeff[14] * data[i-15]; + case 14: sum += qlp_coeff[13] * data[i-14]; + case 13: sum += qlp_coeff[12] * data[i-13]; + sum += qlp_coeff[11] * data[i-12]; + sum += qlp_coeff[10] * data[i-11]; + sum += qlp_coeff[ 9] * data[i-10]; + sum += qlp_coeff[ 8] * data[i- 9]; + sum += qlp_coeff[ 7] * data[i- 8]; + sum += qlp_coeff[ 6] * data[i- 7]; + sum += qlp_coeff[ 5] * data[i- 6]; + sum += qlp_coeff[ 4] * data[i- 5]; + sum += qlp_coeff[ 3] * data[i- 4]; + sum += qlp_coeff[ 2] * data[i- 3]; + sum += qlp_coeff[ 1] * data[i- 2]; + sum += qlp_coeff[ 0] * data[i- 1]; + } + residual[i] = data[i] - (sum >> lp_quantization); + } + } + _mm256_zeroupper(); +} + +FLAC__SSE_TARGET("avx2") +void FLAC__lpc_compute_residual_from_qlp_coefficients_intrin_avx2(const FLAC__int32 *data, unsigned data_len, const FLAC__int32 qlp_coeff[], unsigned order, int lp_quantization, FLAC__int32 residual[]) +{ + int i; + FLAC__int32 sum; + __m128i cnt = _mm_cvtsi32_si128(lp_quantization); + + FLAC__ASSERT(order > 0); + FLAC__ASSERT(order <= 32); + + if(order <= 12) { + if(order > 8) { + if(order > 10) { + if(order == 12) { + __m256i q0, q1, q2, q3, q4, q5, q6, q7, q8, q9, q10, q11; + q0 = _mm256_set1_epi32(qlp_coeff[0 ]); + q1 = _mm256_set1_epi32(qlp_coeff[1 ]); + q2 = _mm256_set1_epi32(qlp_coeff[2 ]); + q3 = _mm256_set1_epi32(qlp_coeff[3 ]); + q4 = _mm256_set1_epi32(qlp_coeff[4 ]); + q5 = _mm256_set1_epi32(qlp_coeff[5 ]); + q6 = _mm256_set1_epi32(qlp_coeff[6 ]); + q7 = _mm256_set1_epi32(qlp_coeff[7 ]); + q8 = _mm256_set1_epi32(qlp_coeff[8 ]); + q9 = _mm256_set1_epi32(qlp_coeff[9 ]); + q10 = _mm256_set1_epi32(qlp_coeff[10]); + q11 = _mm256_set1_epi32(qlp_coeff[11]); + + for(i = 0; i < (int)data_len-7; i+=8) { + __m256i summ, mull; + summ = _mm256_mullo_epi32(q11, _mm256_loadu_si256((const __m256i*)(data+i-12))); + mull = _mm256_mullo_epi32(q10, _mm256_loadu_si256((const __m256i*)(data+i-11))); summ = _mm256_add_epi32(summ, mull); + mull = _mm256_mullo_epi32(q9, _mm256_loadu_si256((const __m256i*)(data+i-10))); summ = _mm256_add_epi32(summ, mull); + mull = _mm256_mullo_epi32(q8, _mm256_loadu_si256((const __m256i*)(data+i-9))); summ = _mm256_add_epi32(summ, mull); + mull = _mm256_mullo_epi32(q7, _mm256_loadu_si256((const __m256i*)(data+i-8))); summ = _mm256_add_epi32(summ, mull); + mull = _mm256_mullo_epi32(q6, _mm256_loadu_si256((const __m256i*)(data+i-7))); summ = _mm256_add_epi32(summ, mull); + mull = _mm256_mullo_epi32(q5, _mm256_loadu_si256((const __m256i*)(data+i-6))); summ = _mm256_add_epi32(summ, mull); + mull = _mm256_mullo_epi32(q4, _mm256_loadu_si256((const __m256i*)(data+i-5))); summ = _mm256_add_epi32(summ, mull); + mull = _mm256_mullo_epi32(q3, _mm256_loadu_si256((const __m256i*)(data+i-4))); summ = _mm256_add_epi32(summ, mull); + mull = _mm256_mullo_epi32(q2, _mm256_loadu_si256((const __m256i*)(data+i-3))); summ = _mm256_add_epi32(summ, mull); + mull = _mm256_mullo_epi32(q1, _mm256_loadu_si256((const __m256i*)(data+i-2))); summ = _mm256_add_epi32(summ, mull); + mull = _mm256_mullo_epi32(q0, _mm256_loadu_si256((const __m256i*)(data+i-1))); summ = _mm256_add_epi32(summ, mull); + summ = _mm256_sra_epi32(summ, cnt); + _mm256_storeu_si256((__m256i*)(residual+i), _mm256_sub_epi32(_mm256_loadu_si256((const __m256i*)(data+i)), summ)); + } + } + else { /* order == 11 */ + __m256i q0, q1, q2, q3, q4, q5, q6, q7, q8, q9, q10; + q0 = _mm256_set1_epi32(qlp_coeff[0 ]); + q1 = _mm256_set1_epi32(qlp_coeff[1 ]); + q2 = _mm256_set1_epi32(qlp_coeff[2 ]); + q3 = _mm256_set1_epi32(qlp_coeff[3 ]); + q4 = _mm256_set1_epi32(qlp_coeff[4 ]); + q5 = _mm256_set1_epi32(qlp_coeff[5 ]); + q6 = _mm256_set1_epi32(qlp_coeff[6 ]); + q7 = _mm256_set1_epi32(qlp_coeff[7 ]); + q8 = _mm256_set1_epi32(qlp_coeff[8 ]); + q9 = _mm256_set1_epi32(qlp_coeff[9 ]); + q10 = _mm256_set1_epi32(qlp_coeff[10]); + + for(i = 0; i < (int)data_len-7; i+=8) { + __m256i summ, mull; + summ = _mm256_mullo_epi32(q10, _mm256_loadu_si256((const __m256i*)(data+i-11))); + mull = _mm256_mullo_epi32(q9, _mm256_loadu_si256((const __m256i*)(data+i-10))); summ = _mm256_add_epi32(summ, mull); + mull = _mm256_mullo_epi32(q8, _mm256_loadu_si256((const __m256i*)(data+i-9))); summ = _mm256_add_epi32(summ, mull); + mull = _mm256_mullo_epi32(q7, _mm256_loadu_si256((const __m256i*)(data+i-8))); summ = _mm256_add_epi32(summ, mull); + mull = _mm256_mullo_epi32(q6, _mm256_loadu_si256((const __m256i*)(data+i-7))); summ = _mm256_add_epi32(summ, mull); + mull = _mm256_mullo_epi32(q5, _mm256_loadu_si256((const __m256i*)(data+i-6))); summ = _mm256_add_epi32(summ, mull); + mull = _mm256_mullo_epi32(q4, _mm256_loadu_si256((const __m256i*)(data+i-5))); summ = _mm256_add_epi32(summ, mull); + mull = _mm256_mullo_epi32(q3, _mm256_loadu_si256((const __m256i*)(data+i-4))); summ = _mm256_add_epi32(summ, mull); + mull = _mm256_mullo_epi32(q2, _mm256_loadu_si256((const __m256i*)(data+i-3))); summ = _mm256_add_epi32(summ, mull); + mull = _mm256_mullo_epi32(q1, _mm256_loadu_si256((const __m256i*)(data+i-2))); summ = _mm256_add_epi32(summ, mull); + mull = _mm256_mullo_epi32(q0, _mm256_loadu_si256((const __m256i*)(data+i-1))); summ = _mm256_add_epi32(summ, mull); + summ = _mm256_sra_epi32(summ, cnt); + _mm256_storeu_si256((__m256i*)(residual+i), _mm256_sub_epi32(_mm256_loadu_si256((const __m256i*)(data+i)), summ)); + } + } + } + else { + if(order == 10) { + __m256i q0, q1, q2, q3, q4, q5, q6, q7, q8, q9; + q0 = _mm256_set1_epi32(qlp_coeff[0 ]); + q1 = _mm256_set1_epi32(qlp_coeff[1 ]); + q2 = _mm256_set1_epi32(qlp_coeff[2 ]); + q3 = _mm256_set1_epi32(qlp_coeff[3 ]); + q4 = _mm256_set1_epi32(qlp_coeff[4 ]); + q5 = _mm256_set1_epi32(qlp_coeff[5 ]); + q6 = _mm256_set1_epi32(qlp_coeff[6 ]); + q7 = _mm256_set1_epi32(qlp_coeff[7 ]); + q8 = _mm256_set1_epi32(qlp_coeff[8 ]); + q9 = _mm256_set1_epi32(qlp_coeff[9 ]); + + for(i = 0; i < (int)data_len-7; i+=8) { + __m256i summ, mull; + summ = _mm256_mullo_epi32(q9, _mm256_loadu_si256((const __m256i*)(data+i-10))); + mull = _mm256_mullo_epi32(q8, _mm256_loadu_si256((const __m256i*)(data+i-9))); summ = _mm256_add_epi32(summ, mull); + mull = _mm256_mullo_epi32(q7, _mm256_loadu_si256((const __m256i*)(data+i-8))); summ = _mm256_add_epi32(summ, mull); + mull = _mm256_mullo_epi32(q6, _mm256_loadu_si256((const __m256i*)(data+i-7))); summ = _mm256_add_epi32(summ, mull); + mull = _mm256_mullo_epi32(q5, _mm256_loadu_si256((const __m256i*)(data+i-6))); summ = _mm256_add_epi32(summ, mull); + mull = _mm256_mullo_epi32(q4, _mm256_loadu_si256((const __m256i*)(data+i-5))); summ = _mm256_add_epi32(summ, mull); + mull = _mm256_mullo_epi32(q3, _mm256_loadu_si256((const __m256i*)(data+i-4))); summ = _mm256_add_epi32(summ, mull); + mull = _mm256_mullo_epi32(q2, _mm256_loadu_si256((const __m256i*)(data+i-3))); summ = _mm256_add_epi32(summ, mull); + mull = _mm256_mullo_epi32(q1, _mm256_loadu_si256((const __m256i*)(data+i-2))); summ = _mm256_add_epi32(summ, mull); + mull = _mm256_mullo_epi32(q0, _mm256_loadu_si256((const __m256i*)(data+i-1))); summ = _mm256_add_epi32(summ, mull); + summ = _mm256_sra_epi32(summ, cnt); + _mm256_storeu_si256((__m256i*)(residual+i), _mm256_sub_epi32(_mm256_loadu_si256((const __m256i*)(data+i)), summ)); + } + } + else { /* order == 9 */ + __m256i q0, q1, q2, q3, q4, q5, q6, q7, q8; + q0 = _mm256_set1_epi32(qlp_coeff[0 ]); + q1 = _mm256_set1_epi32(qlp_coeff[1 ]); + q2 = _mm256_set1_epi32(qlp_coeff[2 ]); + q3 = _mm256_set1_epi32(qlp_coeff[3 ]); + q4 = _mm256_set1_epi32(qlp_coeff[4 ]); + q5 = _mm256_set1_epi32(qlp_coeff[5 ]); + q6 = _mm256_set1_epi32(qlp_coeff[6 ]); + q7 = _mm256_set1_epi32(qlp_coeff[7 ]); + q8 = _mm256_set1_epi32(qlp_coeff[8 ]); + + for(i = 0; i < (int)data_len-7; i+=8) { + __m256i summ, mull; + summ = _mm256_mullo_epi32(q8, _mm256_loadu_si256((const __m256i*)(data+i-9))); + mull = _mm256_mullo_epi32(q7, _mm256_loadu_si256((const __m256i*)(data+i-8))); summ = _mm256_add_epi32(summ, mull); + mull = _mm256_mullo_epi32(q6, _mm256_loadu_si256((const __m256i*)(data+i-7))); summ = _mm256_add_epi32(summ, mull); + mull = _mm256_mullo_epi32(q5, _mm256_loadu_si256((const __m256i*)(data+i-6))); summ = _mm256_add_epi32(summ, mull); + mull = _mm256_mullo_epi32(q4, _mm256_loadu_si256((const __m256i*)(data+i-5))); summ = _mm256_add_epi32(summ, mull); + mull = _mm256_mullo_epi32(q3, _mm256_loadu_si256((const __m256i*)(data+i-4))); summ = _mm256_add_epi32(summ, mull); + mull = _mm256_mullo_epi32(q2, _mm256_loadu_si256((const __m256i*)(data+i-3))); summ = _mm256_add_epi32(summ, mull); + mull = _mm256_mullo_epi32(q1, _mm256_loadu_si256((const __m256i*)(data+i-2))); summ = _mm256_add_epi32(summ, mull); + mull = _mm256_mullo_epi32(q0, _mm256_loadu_si256((const __m256i*)(data+i-1))); summ = _mm256_add_epi32(summ, mull); + summ = _mm256_sra_epi32(summ, cnt); + _mm256_storeu_si256((__m256i*)(residual+i), _mm256_sub_epi32(_mm256_loadu_si256((const __m256i*)(data+i)), summ)); + } + } + } + } + else if(order > 4) { + if(order > 6) { + if(order == 8) { + __m256i q0, q1, q2, q3, q4, q5, q6, q7; + q0 = _mm256_set1_epi32(qlp_coeff[0 ]); + q1 = _mm256_set1_epi32(qlp_coeff[1 ]); + q2 = _mm256_set1_epi32(qlp_coeff[2 ]); + q3 = _mm256_set1_epi32(qlp_coeff[3 ]); + q4 = _mm256_set1_epi32(qlp_coeff[4 ]); + q5 = _mm256_set1_epi32(qlp_coeff[5 ]); + q6 = _mm256_set1_epi32(qlp_coeff[6 ]); + q7 = _mm256_set1_epi32(qlp_coeff[7 ]); + + for(i = 0; i < (int)data_len-7; i+=8) { + __m256i summ, mull; + summ = _mm256_mullo_epi32(q7, _mm256_loadu_si256((const __m256i*)(data+i-8))); + mull = _mm256_mullo_epi32(q6, _mm256_loadu_si256((const __m256i*)(data+i-7))); summ = _mm256_add_epi32(summ, mull); + mull = _mm256_mullo_epi32(q5, _mm256_loadu_si256((const __m256i*)(data+i-6))); summ = _mm256_add_epi32(summ, mull); + mull = _mm256_mullo_epi32(q4, _mm256_loadu_si256((const __m256i*)(data+i-5))); summ = _mm256_add_epi32(summ, mull); + mull = _mm256_mullo_epi32(q3, _mm256_loadu_si256((const __m256i*)(data+i-4))); summ = _mm256_add_epi32(summ, mull); + mull = _mm256_mullo_epi32(q2, _mm256_loadu_si256((const __m256i*)(data+i-3))); summ = _mm256_add_epi32(summ, mull); + mull = _mm256_mullo_epi32(q1, _mm256_loadu_si256((const __m256i*)(data+i-2))); summ = _mm256_add_epi32(summ, mull); + mull = _mm256_mullo_epi32(q0, _mm256_loadu_si256((const __m256i*)(data+i-1))); summ = _mm256_add_epi32(summ, mull); + summ = _mm256_sra_epi32(summ, cnt); + _mm256_storeu_si256((__m256i*)(residual+i), _mm256_sub_epi32(_mm256_loadu_si256((const __m256i*)(data+i)), summ)); + } + } + else { /* order == 7 */ + __m256i q0, q1, q2, q3, q4, q5, q6; + q0 = _mm256_set1_epi32(qlp_coeff[0 ]); + q1 = _mm256_set1_epi32(qlp_coeff[1 ]); + q2 = _mm256_set1_epi32(qlp_coeff[2 ]); + q3 = _mm256_set1_epi32(qlp_coeff[3 ]); + q4 = _mm256_set1_epi32(qlp_coeff[4 ]); + q5 = _mm256_set1_epi32(qlp_coeff[5 ]); + q6 = _mm256_set1_epi32(qlp_coeff[6 ]); + + for(i = 0; i < (int)data_len-7; i+=8) { + __m256i summ, mull; + summ = _mm256_mullo_epi32(q6, _mm256_loadu_si256((const __m256i*)(data+i-7))); + mull = _mm256_mullo_epi32(q5, _mm256_loadu_si256((const __m256i*)(data+i-6))); summ = _mm256_add_epi32(summ, mull); + mull = _mm256_mullo_epi32(q4, _mm256_loadu_si256((const __m256i*)(data+i-5))); summ = _mm256_add_epi32(summ, mull); + mull = _mm256_mullo_epi32(q3, _mm256_loadu_si256((const __m256i*)(data+i-4))); summ = _mm256_add_epi32(summ, mull); + mull = _mm256_mullo_epi32(q2, _mm256_loadu_si256((const __m256i*)(data+i-3))); summ = _mm256_add_epi32(summ, mull); + mull = _mm256_mullo_epi32(q1, _mm256_loadu_si256((const __m256i*)(data+i-2))); summ = _mm256_add_epi32(summ, mull); + mull = _mm256_mullo_epi32(q0, _mm256_loadu_si256((const __m256i*)(data+i-1))); summ = _mm256_add_epi32(summ, mull); + summ = _mm256_sra_epi32(summ, cnt); + _mm256_storeu_si256((__m256i*)(residual+i), _mm256_sub_epi32(_mm256_loadu_si256((const __m256i*)(data+i)), summ)); + } + } + } + else { + if(order == 6) { + __m256i q0, q1, q2, q3, q4, q5; + q0 = _mm256_set1_epi32(qlp_coeff[0 ]); + q1 = _mm256_set1_epi32(qlp_coeff[1 ]); + q2 = _mm256_set1_epi32(qlp_coeff[2 ]); + q3 = _mm256_set1_epi32(qlp_coeff[3 ]); + q4 = _mm256_set1_epi32(qlp_coeff[4 ]); + q5 = _mm256_set1_epi32(qlp_coeff[5 ]); + + for(i = 0; i < (int)data_len-7; i+=8) { + __m256i summ, mull; + summ = _mm256_mullo_epi32(q5, _mm256_loadu_si256((const __m256i*)(data+i-6))); + mull = _mm256_mullo_epi32(q4, _mm256_loadu_si256((const __m256i*)(data+i-5))); summ = _mm256_add_epi32(summ, mull); + mull = _mm256_mullo_epi32(q3, _mm256_loadu_si256((const __m256i*)(data+i-4))); summ = _mm256_add_epi32(summ, mull); + mull = _mm256_mullo_epi32(q2, _mm256_loadu_si256((const __m256i*)(data+i-3))); summ = _mm256_add_epi32(summ, mull); + mull = _mm256_mullo_epi32(q1, _mm256_loadu_si256((const __m256i*)(data+i-2))); summ = _mm256_add_epi32(summ, mull); + mull = _mm256_mullo_epi32(q0, _mm256_loadu_si256((const __m256i*)(data+i-1))); summ = _mm256_add_epi32(summ, mull); + summ = _mm256_sra_epi32(summ, cnt); + _mm256_storeu_si256((__m256i*)(residual+i), _mm256_sub_epi32(_mm256_loadu_si256((const __m256i*)(data+i)), summ)); + } + } + else { /* order == 5 */ + __m256i q0, q1, q2, q3, q4; + q0 = _mm256_set1_epi32(qlp_coeff[0 ]); + q1 = _mm256_set1_epi32(qlp_coeff[1 ]); + q2 = _mm256_set1_epi32(qlp_coeff[2 ]); + q3 = _mm256_set1_epi32(qlp_coeff[3 ]); + q4 = _mm256_set1_epi32(qlp_coeff[4 ]); + + for(i = 0; i < (int)data_len-7; i+=8) { + __m256i summ, mull; + summ = _mm256_mullo_epi32(q4, _mm256_loadu_si256((const __m256i*)(data+i-5))); + mull = _mm256_mullo_epi32(q3, _mm256_loadu_si256((const __m256i*)(data+i-4))); summ = _mm256_add_epi32(summ, mull); + mull = _mm256_mullo_epi32(q2, _mm256_loadu_si256((const __m256i*)(data+i-3))); summ = _mm256_add_epi32(summ, mull); + mull = _mm256_mullo_epi32(q1, _mm256_loadu_si256((const __m256i*)(data+i-2))); summ = _mm256_add_epi32(summ, mull); + mull = _mm256_mullo_epi32(q0, _mm256_loadu_si256((const __m256i*)(data+i-1))); summ = _mm256_add_epi32(summ, mull); + summ = _mm256_sra_epi32(summ, cnt); + _mm256_storeu_si256((__m256i*)(residual+i), _mm256_sub_epi32(_mm256_loadu_si256((const __m256i*)(data+i)), summ)); + } + } + } + } + else { + if(order > 2) { + if(order == 4) { + __m256i q0, q1, q2, q3; + q0 = _mm256_set1_epi32(qlp_coeff[0 ]); + q1 = _mm256_set1_epi32(qlp_coeff[1 ]); + q2 = _mm256_set1_epi32(qlp_coeff[2 ]); + q3 = _mm256_set1_epi32(qlp_coeff[3 ]); + + for(i = 0; i < (int)data_len-7; i+=8) { + __m256i summ, mull; + summ = _mm256_mullo_epi32(q3, _mm256_loadu_si256((const __m256i*)(data+i-4))); + mull = _mm256_mullo_epi32(q2, _mm256_loadu_si256((const __m256i*)(data+i-3))); summ = _mm256_add_epi32(summ, mull); + mull = _mm256_mullo_epi32(q1, _mm256_loadu_si256((const __m256i*)(data+i-2))); summ = _mm256_add_epi32(summ, mull); + mull = _mm256_mullo_epi32(q0, _mm256_loadu_si256((const __m256i*)(data+i-1))); summ = _mm256_add_epi32(summ, mull); + summ = _mm256_sra_epi32(summ, cnt); + _mm256_storeu_si256((__m256i*)(residual+i), _mm256_sub_epi32(_mm256_loadu_si256((const __m256i*)(data+i)), summ)); + } + } + else { /* order == 3 */ + __m256i q0, q1, q2; + q0 = _mm256_set1_epi32(qlp_coeff[0 ]); + q1 = _mm256_set1_epi32(qlp_coeff[1 ]); + q2 = _mm256_set1_epi32(qlp_coeff[2 ]); + + for(i = 0; i < (int)data_len-7; i+=8) { + __m256i summ, mull; + summ = _mm256_mullo_epi32(q2, _mm256_loadu_si256((const __m256i*)(data+i-3))); + mull = _mm256_mullo_epi32(q1, _mm256_loadu_si256((const __m256i*)(data+i-2))); summ = _mm256_add_epi32(summ, mull); + mull = _mm256_mullo_epi32(q0, _mm256_loadu_si256((const __m256i*)(data+i-1))); summ = _mm256_add_epi32(summ, mull); + summ = _mm256_sra_epi32(summ, cnt); + _mm256_storeu_si256((__m256i*)(residual+i), _mm256_sub_epi32(_mm256_loadu_si256((const __m256i*)(data+i)), summ)); + } + } + } + else { + if(order == 2) { + __m256i q0, q1; + q0 = _mm256_set1_epi32(qlp_coeff[0 ]); + q1 = _mm256_set1_epi32(qlp_coeff[1 ]); + + for(i = 0; i < (int)data_len-7; i+=8) { + __m256i summ, mull; + summ = _mm256_mullo_epi32(q1, _mm256_loadu_si256((const __m256i*)(data+i-2))); + mull = _mm256_mullo_epi32(q0, _mm256_loadu_si256((const __m256i*)(data+i-1))); summ = _mm256_add_epi32(summ, mull); + summ = _mm256_sra_epi32(summ, cnt); + _mm256_storeu_si256((__m256i*)(residual+i), _mm256_sub_epi32(_mm256_loadu_si256((const __m256i*)(data+i)), summ)); + } + } + else { /* order == 1 */ + __m256i q0; + q0 = _mm256_set1_epi32(qlp_coeff[0 ]); + + for(i = 0; i < (int)data_len-7; i+=8) { + __m256i summ; + summ = _mm256_mullo_epi32(q0, _mm256_loadu_si256((const __m256i*)(data+i-1))); + summ = _mm256_sra_epi32(summ, cnt); + _mm256_storeu_si256((__m256i*)(residual+i), _mm256_sub_epi32(_mm256_loadu_si256((const __m256i*)(data+i)), summ)); + } + } + } + } + for(; i < (int)data_len; i++) { + sum = 0; + switch(order) { + case 12: sum += qlp_coeff[11] * data[i-12]; + case 11: sum += qlp_coeff[10] * data[i-11]; + case 10: sum += qlp_coeff[ 9] * data[i-10]; + case 9: sum += qlp_coeff[ 8] * data[i- 9]; + case 8: sum += qlp_coeff[ 7] * data[i- 8]; + case 7: sum += qlp_coeff[ 6] * data[i- 7]; + case 6: sum += qlp_coeff[ 5] * data[i- 6]; + case 5: sum += qlp_coeff[ 4] * data[i- 5]; + case 4: sum += qlp_coeff[ 3] * data[i- 4]; + case 3: sum += qlp_coeff[ 2] * data[i- 3]; + case 2: sum += qlp_coeff[ 1] * data[i- 2]; + case 1: sum += qlp_coeff[ 0] * data[i- 1]; + } + residual[i] = data[i] - (sum >> lp_quantization); + } + } + else { /* order > 12 */ + for(i = 0; i < (int)data_len; i++) { + sum = 0; + switch(order) { + case 32: sum += qlp_coeff[31] * data[i-32]; + case 31: sum += qlp_coeff[30] * data[i-31]; + case 30: sum += qlp_coeff[29] * data[i-30]; + case 29: sum += qlp_coeff[28] * data[i-29]; + case 28: sum += qlp_coeff[27] * data[i-28]; + case 27: sum += qlp_coeff[26] * data[i-27]; + case 26: sum += qlp_coeff[25] * data[i-26]; + case 25: sum += qlp_coeff[24] * data[i-25]; + case 24: sum += qlp_coeff[23] * data[i-24]; + case 23: sum += qlp_coeff[22] * data[i-23]; + case 22: sum += qlp_coeff[21] * data[i-22]; + case 21: sum += qlp_coeff[20] * data[i-21]; + case 20: sum += qlp_coeff[19] * data[i-20]; + case 19: sum += qlp_coeff[18] * data[i-19]; + case 18: sum += qlp_coeff[17] * data[i-18]; + case 17: sum += qlp_coeff[16] * data[i-17]; + case 16: sum += qlp_coeff[15] * data[i-16]; + case 15: sum += qlp_coeff[14] * data[i-15]; + case 14: sum += qlp_coeff[13] * data[i-14]; + case 13: sum += qlp_coeff[12] * data[i-13]; + sum += qlp_coeff[11] * data[i-12]; + sum += qlp_coeff[10] * data[i-11]; + sum += qlp_coeff[ 9] * data[i-10]; + sum += qlp_coeff[ 8] * data[i- 9]; + sum += qlp_coeff[ 7] * data[i- 8]; + sum += qlp_coeff[ 6] * data[i- 7]; + sum += qlp_coeff[ 5] * data[i- 6]; + sum += qlp_coeff[ 4] * data[i- 5]; + sum += qlp_coeff[ 3] * data[i- 4]; + sum += qlp_coeff[ 2] * data[i- 3]; + sum += qlp_coeff[ 1] * data[i- 2]; + sum += qlp_coeff[ 0] * data[i- 1]; + } + residual[i] = data[i] - (sum >> lp_quantization); + } + } + _mm256_zeroupper(); +} + +static FLAC__int32 pack_arr[8] = { 0, 2, 4, 6, 1, 3, 5, 7 }; + +FLAC__SSE_TARGET("avx2") +void FLAC__lpc_compute_residual_from_qlp_coefficients_wide_intrin_avx2(const FLAC__int32 *data, unsigned data_len, const FLAC__int32 qlp_coeff[], unsigned order, int lp_quantization, FLAC__int32 residual[]) +{ + int i; + FLAC__int64 sum; + __m128i cnt = _mm_cvtsi32_si128(lp_quantization); + __m256i pack = _mm256_loadu_si256((const __m256i *)pack_arr); + + FLAC__ASSERT(order > 0); + FLAC__ASSERT(order <= 32); + FLAC__ASSERT(lp_quantization <= 32); /* there's no _mm256_sra_epi64() so we have to use _mm256_srl_epi64() */ + + if(order <= 12) { + if(order > 8) { + if(order > 10) { + if(order == 12) { + __m256i q0, q1, q2, q3, q4, q5, q6, q7, q8, q9, q10, q11; + q0 = _mm256_cvtepu32_epi64(_mm_set1_epi32(qlp_coeff[0 ])); + q1 = _mm256_cvtepu32_epi64(_mm_set1_epi32(qlp_coeff[1 ])); + q2 = _mm256_cvtepu32_epi64(_mm_set1_epi32(qlp_coeff[2 ])); + q3 = _mm256_cvtepu32_epi64(_mm_set1_epi32(qlp_coeff[3 ])); + q4 = _mm256_cvtepu32_epi64(_mm_set1_epi32(qlp_coeff[4 ])); + q5 = _mm256_cvtepu32_epi64(_mm_set1_epi32(qlp_coeff[5 ])); + q6 = _mm256_cvtepu32_epi64(_mm_set1_epi32(qlp_coeff[6 ])); + q7 = _mm256_cvtepu32_epi64(_mm_set1_epi32(qlp_coeff[7 ])); + q8 = _mm256_cvtepu32_epi64(_mm_set1_epi32(qlp_coeff[8 ])); + q9 = _mm256_cvtepu32_epi64(_mm_set1_epi32(qlp_coeff[9 ])); + q10 = _mm256_cvtepu32_epi64(_mm_set1_epi32(qlp_coeff[10])); + q11 = _mm256_cvtepu32_epi64(_mm_set1_epi32(qlp_coeff[11])); + + for(i = 0; i < (int)data_len-3; i+=4) { + __m256i summ, mull; + summ = _mm256_mul_epi32(q11, _mm256_cvtepu32_epi64(_mm_loadu_si128((const __m128i*)(data+i-12)))); + mull = _mm256_mul_epi32(q10, _mm256_cvtepu32_epi64(_mm_loadu_si128((const __m128i*)(data+i-11)))); summ = _mm256_add_epi64(summ, mull); + mull = _mm256_mul_epi32(q9, _mm256_cvtepu32_epi64(_mm_loadu_si128((const __m128i*)(data+i-10)))); summ = _mm256_add_epi64(summ, mull); + mull = _mm256_mul_epi32(q8, _mm256_cvtepu32_epi64(_mm_loadu_si128((const __m128i*)(data+i-9 )))); summ = _mm256_add_epi64(summ, mull); + mull = _mm256_mul_epi32(q7, _mm256_cvtepu32_epi64(_mm_loadu_si128((const __m128i*)(data+i-8 )))); summ = _mm256_add_epi64(summ, mull); + mull = _mm256_mul_epi32(q6, _mm256_cvtepu32_epi64(_mm_loadu_si128((const __m128i*)(data+i-7 )))); summ = _mm256_add_epi64(summ, mull); + mull = _mm256_mul_epi32(q5, _mm256_cvtepu32_epi64(_mm_loadu_si128((const __m128i*)(data+i-6 )))); summ = _mm256_add_epi64(summ, mull); + mull = _mm256_mul_epi32(q4, _mm256_cvtepu32_epi64(_mm_loadu_si128((const __m128i*)(data+i-5 )))); summ = _mm256_add_epi64(summ, mull); + mull = _mm256_mul_epi32(q3, _mm256_cvtepu32_epi64(_mm_loadu_si128((const __m128i*)(data+i-4 )))); summ = _mm256_add_epi64(summ, mull); + mull = _mm256_mul_epi32(q2, _mm256_cvtepu32_epi64(_mm_loadu_si128((const __m128i*)(data+i-3 )))); summ = _mm256_add_epi64(summ, mull); + mull = _mm256_mul_epi32(q1, _mm256_cvtepu32_epi64(_mm_loadu_si128((const __m128i*)(data+i-2 )))); summ = _mm256_add_epi64(summ, mull); + mull = _mm256_mul_epi32(q0, _mm256_cvtepu32_epi64(_mm_loadu_si128((const __m128i*)(data+i-1 )))); summ = _mm256_add_epi64(summ, mull); + summ = _mm256_permutevar8x32_epi32(_mm256_srl_epi64(summ, cnt), pack); + _mm_storeu_si128((__m128i*)(residual+i), _mm_sub_epi32(_mm_loadu_si128((const __m128i*)(data+i)), _mm256_castsi256_si128(summ))); + } + } + else { /* order == 11 */ + __m256i q0, q1, q2, q3, q4, q5, q6, q7, q8, q9, q10; + q0 = _mm256_cvtepu32_epi64(_mm_set1_epi32(qlp_coeff[0 ])); + q1 = _mm256_cvtepu32_epi64(_mm_set1_epi32(qlp_coeff[1 ])); + q2 = _mm256_cvtepu32_epi64(_mm_set1_epi32(qlp_coeff[2 ])); + q3 = _mm256_cvtepu32_epi64(_mm_set1_epi32(qlp_coeff[3 ])); + q4 = _mm256_cvtepu32_epi64(_mm_set1_epi32(qlp_coeff[4 ])); + q5 = _mm256_cvtepu32_epi64(_mm_set1_epi32(qlp_coeff[5 ])); + q6 = _mm256_cvtepu32_epi64(_mm_set1_epi32(qlp_coeff[6 ])); + q7 = _mm256_cvtepu32_epi64(_mm_set1_epi32(qlp_coeff[7 ])); + q8 = _mm256_cvtepu32_epi64(_mm_set1_epi32(qlp_coeff[8 ])); + q9 = _mm256_cvtepu32_epi64(_mm_set1_epi32(qlp_coeff[9 ])); + q10 = _mm256_cvtepu32_epi64(_mm_set1_epi32(qlp_coeff[10])); + + for(i = 0; i < (int)data_len-3; i+=4) { + __m256i summ, mull; + summ = _mm256_mul_epi32(q10, _mm256_cvtepu32_epi64(_mm_loadu_si128((const __m128i*)(data+i-11)))); + mull = _mm256_mul_epi32(q9, _mm256_cvtepu32_epi64(_mm_loadu_si128((const __m128i*)(data+i-10)))); summ = _mm256_add_epi64(summ, mull); + mull = _mm256_mul_epi32(q8, _mm256_cvtepu32_epi64(_mm_loadu_si128((const __m128i*)(data+i-9 )))); summ = _mm256_add_epi64(summ, mull); + mull = _mm256_mul_epi32(q7, _mm256_cvtepu32_epi64(_mm_loadu_si128((const __m128i*)(data+i-8 )))); summ = _mm256_add_epi64(summ, mull); + mull = _mm256_mul_epi32(q6, _mm256_cvtepu32_epi64(_mm_loadu_si128((const __m128i*)(data+i-7 )))); summ = _mm256_add_epi64(summ, mull); + mull = _mm256_mul_epi32(q5, _mm256_cvtepu32_epi64(_mm_loadu_si128((const __m128i*)(data+i-6 )))); summ = _mm256_add_epi64(summ, mull); + mull = _mm256_mul_epi32(q4, _mm256_cvtepu32_epi64(_mm_loadu_si128((const __m128i*)(data+i-5 )))); summ = _mm256_add_epi64(summ, mull); + mull = _mm256_mul_epi32(q3, _mm256_cvtepu32_epi64(_mm_loadu_si128((const __m128i*)(data+i-4 )))); summ = _mm256_add_epi64(summ, mull); + mull = _mm256_mul_epi32(q2, _mm256_cvtepu32_epi64(_mm_loadu_si128((const __m128i*)(data+i-3 )))); summ = _mm256_add_epi64(summ, mull); + mull = _mm256_mul_epi32(q1, _mm256_cvtepu32_epi64(_mm_loadu_si128((const __m128i*)(data+i-2 )))); summ = _mm256_add_epi64(summ, mull); + mull = _mm256_mul_epi32(q0, _mm256_cvtepu32_epi64(_mm_loadu_si128((const __m128i*)(data+i-1 )))); summ = _mm256_add_epi64(summ, mull); + summ = _mm256_permutevar8x32_epi32(_mm256_srl_epi64(summ, cnt), pack); + _mm_storeu_si128((__m128i*)(residual+i), _mm_sub_epi32(_mm_loadu_si128((const __m128i*)(data+i)), _mm256_castsi256_si128(summ))); + } + } + } + else { + if(order == 10) { + __m256i q0, q1, q2, q3, q4, q5, q6, q7, q8, q9; + q0 = _mm256_cvtepu32_epi64(_mm_set1_epi32(qlp_coeff[0 ])); + q1 = _mm256_cvtepu32_epi64(_mm_set1_epi32(qlp_coeff[1 ])); + q2 = _mm256_cvtepu32_epi64(_mm_set1_epi32(qlp_coeff[2 ])); + q3 = _mm256_cvtepu32_epi64(_mm_set1_epi32(qlp_coeff[3 ])); + q4 = _mm256_cvtepu32_epi64(_mm_set1_epi32(qlp_coeff[4 ])); + q5 = _mm256_cvtepu32_epi64(_mm_set1_epi32(qlp_coeff[5 ])); + q6 = _mm256_cvtepu32_epi64(_mm_set1_epi32(qlp_coeff[6 ])); + q7 = _mm256_cvtepu32_epi64(_mm_set1_epi32(qlp_coeff[7 ])); + q8 = _mm256_cvtepu32_epi64(_mm_set1_epi32(qlp_coeff[8 ])); + q9 = _mm256_cvtepu32_epi64(_mm_set1_epi32(qlp_coeff[9 ])); + + for(i = 0; i < (int)data_len-3; i+=4) { + __m256i summ, mull; + summ = _mm256_mul_epi32(q9, _mm256_cvtepu32_epi64(_mm_loadu_si128((const __m128i*)(data+i-10)))); + mull = _mm256_mul_epi32(q8, _mm256_cvtepu32_epi64(_mm_loadu_si128((const __m128i*)(data+i-9 )))); summ = _mm256_add_epi64(summ, mull); + mull = _mm256_mul_epi32(q7, _mm256_cvtepu32_epi64(_mm_loadu_si128((const __m128i*)(data+i-8 )))); summ = _mm256_add_epi64(summ, mull); + mull = _mm256_mul_epi32(q6, _mm256_cvtepu32_epi64(_mm_loadu_si128((const __m128i*)(data+i-7 )))); summ = _mm256_add_epi64(summ, mull); + mull = _mm256_mul_epi32(q5, _mm256_cvtepu32_epi64(_mm_loadu_si128((const __m128i*)(data+i-6 )))); summ = _mm256_add_epi64(summ, mull); + mull = _mm256_mul_epi32(q4, _mm256_cvtepu32_epi64(_mm_loadu_si128((const __m128i*)(data+i-5 )))); summ = _mm256_add_epi64(summ, mull); + mull = _mm256_mul_epi32(q3, _mm256_cvtepu32_epi64(_mm_loadu_si128((const __m128i*)(data+i-4 )))); summ = _mm256_add_epi64(summ, mull); + mull = _mm256_mul_epi32(q2, _mm256_cvtepu32_epi64(_mm_loadu_si128((const __m128i*)(data+i-3 )))); summ = _mm256_add_epi64(summ, mull); + mull = _mm256_mul_epi32(q1, _mm256_cvtepu32_epi64(_mm_loadu_si128((const __m128i*)(data+i-2 )))); summ = _mm256_add_epi64(summ, mull); + mull = _mm256_mul_epi32(q0, _mm256_cvtepu32_epi64(_mm_loadu_si128((const __m128i*)(data+i-1 )))); summ = _mm256_add_epi64(summ, mull); + summ = _mm256_permutevar8x32_epi32(_mm256_srl_epi64(summ, cnt), pack); + _mm_storeu_si128((__m128i*)(residual+i), _mm_sub_epi32(_mm_loadu_si128((const __m128i*)(data+i)), _mm256_castsi256_si128(summ))); + } + } + else { /* order == 9 */ + __m256i q0, q1, q2, q3, q4, q5, q6, q7, q8; + q0 = _mm256_cvtepu32_epi64(_mm_set1_epi32(qlp_coeff[0 ])); + q1 = _mm256_cvtepu32_epi64(_mm_set1_epi32(qlp_coeff[1 ])); + q2 = _mm256_cvtepu32_epi64(_mm_set1_epi32(qlp_coeff[2 ])); + q3 = _mm256_cvtepu32_epi64(_mm_set1_epi32(qlp_coeff[3 ])); + q4 = _mm256_cvtepu32_epi64(_mm_set1_epi32(qlp_coeff[4 ])); + q5 = _mm256_cvtepu32_epi64(_mm_set1_epi32(qlp_coeff[5 ])); + q6 = _mm256_cvtepu32_epi64(_mm_set1_epi32(qlp_coeff[6 ])); + q7 = _mm256_cvtepu32_epi64(_mm_set1_epi32(qlp_coeff[7 ])); + q8 = _mm256_cvtepu32_epi64(_mm_set1_epi32(qlp_coeff[8 ])); + + for(i = 0; i < (int)data_len-3; i+=4) { + __m256i summ, mull; + summ = _mm256_mul_epi32(q8, _mm256_cvtepu32_epi64(_mm_loadu_si128((const __m128i*)(data+i-9 )))); + mull = _mm256_mul_epi32(q7, _mm256_cvtepu32_epi64(_mm_loadu_si128((const __m128i*)(data+i-8 )))); summ = _mm256_add_epi64(summ, mull); + mull = _mm256_mul_epi32(q6, _mm256_cvtepu32_epi64(_mm_loadu_si128((const __m128i*)(data+i-7 )))); summ = _mm256_add_epi64(summ, mull); + mull = _mm256_mul_epi32(q5, _mm256_cvtepu32_epi64(_mm_loadu_si128((const __m128i*)(data+i-6 )))); summ = _mm256_add_epi64(summ, mull); + mull = _mm256_mul_epi32(q4, _mm256_cvtepu32_epi64(_mm_loadu_si128((const __m128i*)(data+i-5 )))); summ = _mm256_add_epi64(summ, mull); + mull = _mm256_mul_epi32(q3, _mm256_cvtepu32_epi64(_mm_loadu_si128((const __m128i*)(data+i-4 )))); summ = _mm256_add_epi64(summ, mull); + mull = _mm256_mul_epi32(q2, _mm256_cvtepu32_epi64(_mm_loadu_si128((const __m128i*)(data+i-3 )))); summ = _mm256_add_epi64(summ, mull); + mull = _mm256_mul_epi32(q1, _mm256_cvtepu32_epi64(_mm_loadu_si128((const __m128i*)(data+i-2 )))); summ = _mm256_add_epi64(summ, mull); + mull = _mm256_mul_epi32(q0, _mm256_cvtepu32_epi64(_mm_loadu_si128((const __m128i*)(data+i-1 )))); summ = _mm256_add_epi64(summ, mull); + summ = _mm256_permutevar8x32_epi32(_mm256_srl_epi64(summ, cnt), pack); + _mm_storeu_si128((__m128i*)(residual+i), _mm_sub_epi32(_mm_loadu_si128((const __m128i*)(data+i)), _mm256_castsi256_si128(summ))); + } + } + } + } + else if(order > 4) { + if(order > 6) { + if(order == 8) { + __m256i q0, q1, q2, q3, q4, q5, q6, q7; + q0 = _mm256_cvtepu32_epi64(_mm_set1_epi32(qlp_coeff[0 ])); + q1 = _mm256_cvtepu32_epi64(_mm_set1_epi32(qlp_coeff[1 ])); + q2 = _mm256_cvtepu32_epi64(_mm_set1_epi32(qlp_coeff[2 ])); + q3 = _mm256_cvtepu32_epi64(_mm_set1_epi32(qlp_coeff[3 ])); + q4 = _mm256_cvtepu32_epi64(_mm_set1_epi32(qlp_coeff[4 ])); + q5 = _mm256_cvtepu32_epi64(_mm_set1_epi32(qlp_coeff[5 ])); + q6 = _mm256_cvtepu32_epi64(_mm_set1_epi32(qlp_coeff[6 ])); + q7 = _mm256_cvtepu32_epi64(_mm_set1_epi32(qlp_coeff[7 ])); + + for(i = 0; i < (int)data_len-3; i+=4) { + __m256i summ, mull; + summ = _mm256_mul_epi32(q7, _mm256_cvtepu32_epi64(_mm_loadu_si128((const __m128i*)(data+i-8 )))); + mull = _mm256_mul_epi32(q6, _mm256_cvtepu32_epi64(_mm_loadu_si128((const __m128i*)(data+i-7 )))); summ = _mm256_add_epi64(summ, mull); + mull = _mm256_mul_epi32(q5, _mm256_cvtepu32_epi64(_mm_loadu_si128((const __m128i*)(data+i-6 )))); summ = _mm256_add_epi64(summ, mull); + mull = _mm256_mul_epi32(q4, _mm256_cvtepu32_epi64(_mm_loadu_si128((const __m128i*)(data+i-5 )))); summ = _mm256_add_epi64(summ, mull); + mull = _mm256_mul_epi32(q3, _mm256_cvtepu32_epi64(_mm_loadu_si128((const __m128i*)(data+i-4 )))); summ = _mm256_add_epi64(summ, mull); + mull = _mm256_mul_epi32(q2, _mm256_cvtepu32_epi64(_mm_loadu_si128((const __m128i*)(data+i-3 )))); summ = _mm256_add_epi64(summ, mull); + mull = _mm256_mul_epi32(q1, _mm256_cvtepu32_epi64(_mm_loadu_si128((const __m128i*)(data+i-2 )))); summ = _mm256_add_epi64(summ, mull); + mull = _mm256_mul_epi32(q0, _mm256_cvtepu32_epi64(_mm_loadu_si128((const __m128i*)(data+i-1 )))); summ = _mm256_add_epi64(summ, mull); + summ = _mm256_permutevar8x32_epi32(_mm256_srl_epi64(summ, cnt), pack); + _mm_storeu_si128((__m128i*)(residual+i), _mm_sub_epi32(_mm_loadu_si128((const __m128i*)(data+i)), _mm256_castsi256_si128(summ))); + } + } + else { /* order == 7 */ + __m256i q0, q1, q2, q3, q4, q5, q6; + q0 = _mm256_cvtepu32_epi64(_mm_set1_epi32(qlp_coeff[0 ])); + q1 = _mm256_cvtepu32_epi64(_mm_set1_epi32(qlp_coeff[1 ])); + q2 = _mm256_cvtepu32_epi64(_mm_set1_epi32(qlp_coeff[2 ])); + q3 = _mm256_cvtepu32_epi64(_mm_set1_epi32(qlp_coeff[3 ])); + q4 = _mm256_cvtepu32_epi64(_mm_set1_epi32(qlp_coeff[4 ])); + q5 = _mm256_cvtepu32_epi64(_mm_set1_epi32(qlp_coeff[5 ])); + q6 = _mm256_cvtepu32_epi64(_mm_set1_epi32(qlp_coeff[6 ])); + + for(i = 0; i < (int)data_len-3; i+=4) { + __m256i summ, mull; + summ = _mm256_mul_epi32(q6, _mm256_cvtepu32_epi64(_mm_loadu_si128((const __m128i*)(data+i-7 )))); + mull = _mm256_mul_epi32(q5, _mm256_cvtepu32_epi64(_mm_loadu_si128((const __m128i*)(data+i-6 )))); summ = _mm256_add_epi64(summ, mull); + mull = _mm256_mul_epi32(q4, _mm256_cvtepu32_epi64(_mm_loadu_si128((const __m128i*)(data+i-5 )))); summ = _mm256_add_epi64(summ, mull); + mull = _mm256_mul_epi32(q3, _mm256_cvtepu32_epi64(_mm_loadu_si128((const __m128i*)(data+i-4 )))); summ = _mm256_add_epi64(summ, mull); + mull = _mm256_mul_epi32(q2, _mm256_cvtepu32_epi64(_mm_loadu_si128((const __m128i*)(data+i-3 )))); summ = _mm256_add_epi64(summ, mull); + mull = _mm256_mul_epi32(q1, _mm256_cvtepu32_epi64(_mm_loadu_si128((const __m128i*)(data+i-2 )))); summ = _mm256_add_epi64(summ, mull); + mull = _mm256_mul_epi32(q0, _mm256_cvtepu32_epi64(_mm_loadu_si128((const __m128i*)(data+i-1 )))); summ = _mm256_add_epi64(summ, mull); + summ = _mm256_permutevar8x32_epi32(_mm256_srl_epi64(summ, cnt), pack); + _mm_storeu_si128((__m128i*)(residual+i), _mm_sub_epi32(_mm_loadu_si128((const __m128i*)(data+i)), _mm256_castsi256_si128(summ))); + } + } + } + else { + if(order == 6) { + __m256i q0, q1, q2, q3, q4, q5; + q0 = _mm256_cvtepu32_epi64(_mm_set1_epi32(qlp_coeff[0 ])); + q1 = _mm256_cvtepu32_epi64(_mm_set1_epi32(qlp_coeff[1 ])); + q2 = _mm256_cvtepu32_epi64(_mm_set1_epi32(qlp_coeff[2 ])); + q3 = _mm256_cvtepu32_epi64(_mm_set1_epi32(qlp_coeff[3 ])); + q4 = _mm256_cvtepu32_epi64(_mm_set1_epi32(qlp_coeff[4 ])); + q5 = _mm256_cvtepu32_epi64(_mm_set1_epi32(qlp_coeff[5 ])); + + for(i = 0; i < (int)data_len-3; i+=4) { + __m256i summ, mull; + summ = _mm256_mul_epi32(q5, _mm256_cvtepu32_epi64(_mm_loadu_si128((const __m128i*)(data+i-6 )))); + mull = _mm256_mul_epi32(q4, _mm256_cvtepu32_epi64(_mm_loadu_si128((const __m128i*)(data+i-5 )))); summ = _mm256_add_epi64(summ, mull); + mull = _mm256_mul_epi32(q3, _mm256_cvtepu32_epi64(_mm_loadu_si128((const __m128i*)(data+i-4 )))); summ = _mm256_add_epi64(summ, mull); + mull = _mm256_mul_epi32(q2, _mm256_cvtepu32_epi64(_mm_loadu_si128((const __m128i*)(data+i-3 )))); summ = _mm256_add_epi64(summ, mull); + mull = _mm256_mul_epi32(q1, _mm256_cvtepu32_epi64(_mm_loadu_si128((const __m128i*)(data+i-2 )))); summ = _mm256_add_epi64(summ, mull); + mull = _mm256_mul_epi32(q0, _mm256_cvtepu32_epi64(_mm_loadu_si128((const __m128i*)(data+i-1 )))); summ = _mm256_add_epi64(summ, mull); + summ = _mm256_permutevar8x32_epi32(_mm256_srl_epi64(summ, cnt), pack); + _mm_storeu_si128((__m128i*)(residual+i), _mm_sub_epi32(_mm_loadu_si128((const __m128i*)(data+i)), _mm256_castsi256_si128(summ))); + } + } + else { /* order == 5 */ + __m256i q0, q1, q2, q3, q4; + q0 = _mm256_cvtepu32_epi64(_mm_set1_epi32(qlp_coeff[0 ])); + q1 = _mm256_cvtepu32_epi64(_mm_set1_epi32(qlp_coeff[1 ])); + q2 = _mm256_cvtepu32_epi64(_mm_set1_epi32(qlp_coeff[2 ])); + q3 = _mm256_cvtepu32_epi64(_mm_set1_epi32(qlp_coeff[3 ])); + q4 = _mm256_cvtepu32_epi64(_mm_set1_epi32(qlp_coeff[4 ])); + + for(i = 0; i < (int)data_len-3; i+=4) { + __m256i summ, mull; + summ = _mm256_mul_epi32(q4, _mm256_cvtepu32_epi64(_mm_loadu_si128((const __m128i*)(data+i-5 )))); + mull = _mm256_mul_epi32(q3, _mm256_cvtepu32_epi64(_mm_loadu_si128((const __m128i*)(data+i-4 )))); summ = _mm256_add_epi64(summ, mull); + mull = _mm256_mul_epi32(q2, _mm256_cvtepu32_epi64(_mm_loadu_si128((const __m128i*)(data+i-3 )))); summ = _mm256_add_epi64(summ, mull); + mull = _mm256_mul_epi32(q1, _mm256_cvtepu32_epi64(_mm_loadu_si128((const __m128i*)(data+i-2 )))); summ = _mm256_add_epi64(summ, mull); + mull = _mm256_mul_epi32(q0, _mm256_cvtepu32_epi64(_mm_loadu_si128((const __m128i*)(data+i-1 )))); summ = _mm256_add_epi64(summ, mull); + summ = _mm256_permutevar8x32_epi32(_mm256_srl_epi64(summ, cnt), pack); + _mm_storeu_si128((__m128i*)(residual+i), _mm_sub_epi32(_mm_loadu_si128((const __m128i*)(data+i)), _mm256_castsi256_si128(summ))); + } + } + } + } + else { + if(order > 2) { + if(order == 4) { + __m256i q0, q1, q2, q3; + q0 = _mm256_cvtepu32_epi64(_mm_set1_epi32(qlp_coeff[0 ])); + q1 = _mm256_cvtepu32_epi64(_mm_set1_epi32(qlp_coeff[1 ])); + q2 = _mm256_cvtepu32_epi64(_mm_set1_epi32(qlp_coeff[2 ])); + q3 = _mm256_cvtepu32_epi64(_mm_set1_epi32(qlp_coeff[3 ])); + + for(i = 0; i < (int)data_len-3; i+=4) { + __m256i summ, mull; + summ = _mm256_mul_epi32(q3, _mm256_cvtepu32_epi64(_mm_loadu_si128((const __m128i*)(data+i-4 )))); + mull = _mm256_mul_epi32(q2, _mm256_cvtepu32_epi64(_mm_loadu_si128((const __m128i*)(data+i-3 )))); summ = _mm256_add_epi64(summ, mull); + mull = _mm256_mul_epi32(q1, _mm256_cvtepu32_epi64(_mm_loadu_si128((const __m128i*)(data+i-2 )))); summ = _mm256_add_epi64(summ, mull); + mull = _mm256_mul_epi32(q0, _mm256_cvtepu32_epi64(_mm_loadu_si128((const __m128i*)(data+i-1 )))); summ = _mm256_add_epi64(summ, mull); + summ = _mm256_permutevar8x32_epi32(_mm256_srl_epi64(summ, cnt), pack); + _mm_storeu_si128((__m128i*)(residual+i), _mm_sub_epi32(_mm_loadu_si128((const __m128i*)(data+i)), _mm256_castsi256_si128(summ))); + } + } + else { /* order == 3 */ + __m256i q0, q1, q2; + q0 = _mm256_cvtepu32_epi64(_mm_set1_epi32(qlp_coeff[0 ])); + q1 = _mm256_cvtepu32_epi64(_mm_set1_epi32(qlp_coeff[1 ])); + q2 = _mm256_cvtepu32_epi64(_mm_set1_epi32(qlp_coeff[2 ])); + + for(i = 0; i < (int)data_len-3; i+=4) { + __m256i summ, mull; + summ = _mm256_mul_epi32(q2, _mm256_cvtepu32_epi64(_mm_loadu_si128((const __m128i*)(data+i-3 )))); + mull = _mm256_mul_epi32(q1, _mm256_cvtepu32_epi64(_mm_loadu_si128((const __m128i*)(data+i-2 )))); summ = _mm256_add_epi64(summ, mull); + mull = _mm256_mul_epi32(q0, _mm256_cvtepu32_epi64(_mm_loadu_si128((const __m128i*)(data+i-1 )))); summ = _mm256_add_epi64(summ, mull); + summ = _mm256_permutevar8x32_epi32(_mm256_srl_epi64(summ, cnt), pack); + _mm_storeu_si128((__m128i*)(residual+i), _mm_sub_epi32(_mm_loadu_si128((const __m128i*)(data+i)), _mm256_castsi256_si128(summ))); + } + } + } + else { + if(order == 2) { + __m256i q0, q1; + q0 = _mm256_cvtepu32_epi64(_mm_set1_epi32(qlp_coeff[0 ])); + q1 = _mm256_cvtepu32_epi64(_mm_set1_epi32(qlp_coeff[1 ])); + + for(i = 0; i < (int)data_len-3; i+=4) { + __m256i summ, mull; + summ = _mm256_mul_epi32(q1, _mm256_cvtepu32_epi64(_mm_loadu_si128((const __m128i*)(data+i-2 )))); + mull = _mm256_mul_epi32(q0, _mm256_cvtepu32_epi64(_mm_loadu_si128((const __m128i*)(data+i-1 )))); summ = _mm256_add_epi64(summ, mull); + summ = _mm256_permutevar8x32_epi32(_mm256_srl_epi64(summ, cnt), pack); + _mm_storeu_si128((__m128i*)(residual+i), _mm_sub_epi32(_mm_loadu_si128((const __m128i*)(data+i)), _mm256_castsi256_si128(summ))); + } + } + else { /* order == 1 */ + __m256i q0; + q0 = _mm256_cvtepu32_epi64(_mm_set1_epi32(qlp_coeff[0 ])); + + for(i = 0; i < (int)data_len-3; i+=4) { + __m256i summ; + summ = _mm256_mul_epi32(q0, _mm256_cvtepu32_epi64(_mm_loadu_si128((const __m128i*)(data+i-1 )))); + summ = _mm256_permutevar8x32_epi32(_mm256_srl_epi64(summ, cnt), pack); + _mm_storeu_si128((__m128i*)(residual+i), _mm_sub_epi32(_mm_loadu_si128((const __m128i*)(data+i)), _mm256_castsi256_si128(summ))); + } + } + } + } + for(; i < (int)data_len; i++) { + sum = 0; + switch(order) { + case 12: sum += qlp_coeff[11] * (FLAC__int64)data[i-12]; + case 11: sum += qlp_coeff[10] * (FLAC__int64)data[i-11]; + case 10: sum += qlp_coeff[ 9] * (FLAC__int64)data[i-10]; + case 9: sum += qlp_coeff[ 8] * (FLAC__int64)data[i- 9]; + case 8: sum += qlp_coeff[ 7] * (FLAC__int64)data[i- 8]; + case 7: sum += qlp_coeff[ 6] * (FLAC__int64)data[i- 7]; + case 6: sum += qlp_coeff[ 5] * (FLAC__int64)data[i- 6]; + case 5: sum += qlp_coeff[ 4] * (FLAC__int64)data[i- 5]; + case 4: sum += qlp_coeff[ 3] * (FLAC__int64)data[i- 4]; + case 3: sum += qlp_coeff[ 2] * (FLAC__int64)data[i- 3]; + case 2: sum += qlp_coeff[ 1] * (FLAC__int64)data[i- 2]; + case 1: sum += qlp_coeff[ 0] * (FLAC__int64)data[i- 1]; + } + residual[i] = data[i] - (FLAC__int32)(sum >> lp_quantization); + } + } + else { /* order > 12 */ + for(i = 0; i < (int)data_len; i++) { + sum = 0; + switch(order) { + case 32: sum += qlp_coeff[31] * (FLAC__int64)data[i-32]; + case 31: sum += qlp_coeff[30] * (FLAC__int64)data[i-31]; + case 30: sum += qlp_coeff[29] * (FLAC__int64)data[i-30]; + case 29: sum += qlp_coeff[28] * (FLAC__int64)data[i-29]; + case 28: sum += qlp_coeff[27] * (FLAC__int64)data[i-28]; + case 27: sum += qlp_coeff[26] * (FLAC__int64)data[i-27]; + case 26: sum += qlp_coeff[25] * (FLAC__int64)data[i-26]; + case 25: sum += qlp_coeff[24] * (FLAC__int64)data[i-25]; + case 24: sum += qlp_coeff[23] * (FLAC__int64)data[i-24]; + case 23: sum += qlp_coeff[22] * (FLAC__int64)data[i-23]; + case 22: sum += qlp_coeff[21] * (FLAC__int64)data[i-22]; + case 21: sum += qlp_coeff[20] * (FLAC__int64)data[i-21]; + case 20: sum += qlp_coeff[19] * (FLAC__int64)data[i-20]; + case 19: sum += qlp_coeff[18] * (FLAC__int64)data[i-19]; + case 18: sum += qlp_coeff[17] * (FLAC__int64)data[i-18]; + case 17: sum += qlp_coeff[16] * (FLAC__int64)data[i-17]; + case 16: sum += qlp_coeff[15] * (FLAC__int64)data[i-16]; + case 15: sum += qlp_coeff[14] * (FLAC__int64)data[i-15]; + case 14: sum += qlp_coeff[13] * (FLAC__int64)data[i-14]; + case 13: sum += qlp_coeff[12] * (FLAC__int64)data[i-13]; + sum += qlp_coeff[11] * (FLAC__int64)data[i-12]; + sum += qlp_coeff[10] * (FLAC__int64)data[i-11]; + sum += qlp_coeff[ 9] * (FLAC__int64)data[i-10]; + sum += qlp_coeff[ 8] * (FLAC__int64)data[i- 9]; + sum += qlp_coeff[ 7] * (FLAC__int64)data[i- 8]; + sum += qlp_coeff[ 6] * (FLAC__int64)data[i- 7]; + sum += qlp_coeff[ 5] * (FLAC__int64)data[i- 6]; + sum += qlp_coeff[ 4] * (FLAC__int64)data[i- 5]; + sum += qlp_coeff[ 3] * (FLAC__int64)data[i- 4]; + sum += qlp_coeff[ 2] * (FLAC__int64)data[i- 3]; + sum += qlp_coeff[ 1] * (FLAC__int64)data[i- 2]; + sum += qlp_coeff[ 0] * (FLAC__int64)data[i- 1]; + } + residual[i] = data[i] - (FLAC__int32)(sum >> lp_quantization); + } + } + _mm256_zeroupper(); +} + +#endif /* FLAC__AVX2_SUPPORTED */ +#endif /* (FLAC__CPU_IA32 || FLAC__CPU_X86_64) && FLAC__HAS_X86INTRIN */ +#endif /* FLAC__NO_ASM */ +#endif /* FLAC__INTEGER_ONLY_LIBRARY */ diff --git a/core/deps/flac/src/libFLAC/lpc_intrin_sse.c b/core/deps/flac/src/libFLAC/lpc_intrin_sse.c new file mode 100644 index 000000000..430e73f08 --- /dev/null +++ b/core/deps/flac/src/libFLAC/lpc_intrin_sse.c @@ -0,0 +1,454 @@ +/* libFLAC - Free Lossless Audio Codec library + * Copyright (C) 2000-2009 Josh Coalson + * Copyright (C) 2011-2016 Xiph.Org Foundation + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * - Neither the name of the Xiph.org Foundation nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifdef HAVE_CONFIG_H +# include +#endif + +#include "private/cpu.h" + +#ifndef FLAC__INTEGER_ONLY_LIBRARY +#ifndef FLAC__NO_ASM +#if (defined FLAC__CPU_IA32 || defined FLAC__CPU_X86_64) && FLAC__HAS_X86INTRIN +#include "private/lpc.h" +#ifdef FLAC__SSE_SUPPORTED +#include "FLAC/assert.h" +#include "FLAC/format.h" + +#include /* SSE */ + +/* new routines: more unaligned loads, less shuffle + * old routines: less unaligned loads, more shuffle + * these *_old routines are equivalent to the ASM routines in ia32/lpc_asm.nasm + */ + +/* new routines: faster on current Intel (starting from Core i aka Nehalem) and all AMD CPUs */ + +FLAC__SSE_TARGET("sse") +void FLAC__lpc_compute_autocorrelation_intrin_sse_lag_4_new(const FLAC__real data[], unsigned data_len, unsigned lag, FLAC__real autoc[]) +{ + int i; + int limit = data_len - 4; + __m128 sum0; + + (void) lag; + FLAC__ASSERT(lag <= 4); + FLAC__ASSERT(lag <= data_len); + + sum0 = _mm_setzero_ps(); + + for(i = 0; i <= limit; i++) { + __m128 d, d0; + d0 = _mm_loadu_ps(data+i); + d = d0; d = _mm_shuffle_ps(d, d, 0); + sum0 = _mm_add_ps(sum0, _mm_mul_ps(d0, d)); + } + + { + __m128 d0 = _mm_setzero_ps(); + limit++; if(limit < 0) limit = 0; + + for(i = data_len-1; i >= limit; i--) { + __m128 d; + d = _mm_load_ss(data+i); d = _mm_shuffle_ps(d, d, 0); + d0 = _mm_shuffle_ps(d0, d0, _MM_SHUFFLE(2,1,0,3)); + d0 = _mm_move_ss(d0, d); + sum0 = _mm_add_ps(sum0, _mm_mul_ps(d, d0)); + } + } + + _mm_storeu_ps(autoc, sum0); +} + +FLAC__SSE_TARGET("sse") +void FLAC__lpc_compute_autocorrelation_intrin_sse_lag_8_new(const FLAC__real data[], unsigned data_len, unsigned lag, FLAC__real autoc[]) +{ + int i; + int limit = data_len - 8; + __m128 sum0, sum1; + + (void) lag; + FLAC__ASSERT(lag <= 8); + FLAC__ASSERT(lag <= data_len); + + sum0 = _mm_setzero_ps(); + sum1 = _mm_setzero_ps(); + + for(i = 0; i <= limit; i++) { + __m128 d, d0, d1; + d0 = _mm_loadu_ps(data+i); + d1 = _mm_loadu_ps(data+i+4); + d = d0; d = _mm_shuffle_ps(d, d, 0); + sum0 = _mm_add_ps(sum0, _mm_mul_ps(d0, d)); + sum1 = _mm_add_ps(sum1, _mm_mul_ps(d1, d)); + } + + { + __m128 d0 = _mm_setzero_ps(); + __m128 d1 = _mm_setzero_ps(); + limit++; if(limit < 0) limit = 0; + + for(i = data_len-1; i >= limit; i--) { + __m128 d; + d = _mm_load_ss(data+i); d = _mm_shuffle_ps(d, d, 0); + d1 = _mm_shuffle_ps(d1, d1, _MM_SHUFFLE(2,1,0,3)); + d0 = _mm_shuffle_ps(d0, d0, _MM_SHUFFLE(2,1,0,3)); + d1 = _mm_move_ss(d1, d0); + d0 = _mm_move_ss(d0, d); + sum1 = _mm_add_ps(sum1, _mm_mul_ps(d, d1)); + sum0 = _mm_add_ps(sum0, _mm_mul_ps(d, d0)); + } + } + + _mm_storeu_ps(autoc, sum0); + _mm_storeu_ps(autoc+4, sum1); +} + +FLAC__SSE_TARGET("sse") +void FLAC__lpc_compute_autocorrelation_intrin_sse_lag_12_new(const FLAC__real data[], unsigned data_len, unsigned lag, FLAC__real autoc[]) +{ + int i; + int limit = data_len - 12; + __m128 sum0, sum1, sum2; + + (void) lag; + FLAC__ASSERT(lag <= 12); + FLAC__ASSERT(lag <= data_len); + + sum0 = _mm_setzero_ps(); + sum1 = _mm_setzero_ps(); + sum2 = _mm_setzero_ps(); + + for(i = 0; i <= limit; i++) { + __m128 d, d0, d1, d2; + d0 = _mm_loadu_ps(data+i); + d1 = _mm_loadu_ps(data+i+4); + d2 = _mm_loadu_ps(data+i+8); + d = d0; d = _mm_shuffle_ps(d, d, 0); + sum0 = _mm_add_ps(sum0, _mm_mul_ps(d0, d)); + sum1 = _mm_add_ps(sum1, _mm_mul_ps(d1, d)); + sum2 = _mm_add_ps(sum2, _mm_mul_ps(d2, d)); + } + + { + __m128 d0 = _mm_setzero_ps(); + __m128 d1 = _mm_setzero_ps(); + __m128 d2 = _mm_setzero_ps(); + limit++; if(limit < 0) limit = 0; + + for(i = data_len-1; i >= limit; i--) { + __m128 d; + d = _mm_load_ss(data+i); d = _mm_shuffle_ps(d, d, 0); + d2 = _mm_shuffle_ps(d2, d2, _MM_SHUFFLE(2,1,0,3)); + d1 = _mm_shuffle_ps(d1, d1, _MM_SHUFFLE(2,1,0,3)); + d0 = _mm_shuffle_ps(d0, d0, _MM_SHUFFLE(2,1,0,3)); + d2 = _mm_move_ss(d2, d1); + d1 = _mm_move_ss(d1, d0); + d0 = _mm_move_ss(d0, d); + sum2 = _mm_add_ps(sum2, _mm_mul_ps(d, d2)); + sum1 = _mm_add_ps(sum1, _mm_mul_ps(d, d1)); + sum0 = _mm_add_ps(sum0, _mm_mul_ps(d, d0)); + } + } + + _mm_storeu_ps(autoc, sum0); + _mm_storeu_ps(autoc+4, sum1); + _mm_storeu_ps(autoc+8, sum2); +} + +FLAC__SSE_TARGET("sse") +void FLAC__lpc_compute_autocorrelation_intrin_sse_lag_16_new(const FLAC__real data[], unsigned data_len, unsigned lag, FLAC__real autoc[]) +{ + int i; + int limit = data_len - 16; + __m128 sum0, sum1, sum2, sum3; + + (void) lag; + FLAC__ASSERT(lag <= 16); + FLAC__ASSERT(lag <= data_len); + + sum0 = _mm_setzero_ps(); + sum1 = _mm_setzero_ps(); + sum2 = _mm_setzero_ps(); + sum3 = _mm_setzero_ps(); + + for(i = 0; i <= limit; i++) { + __m128 d, d0, d1, d2, d3; + d0 = _mm_loadu_ps(data+i); + d1 = _mm_loadu_ps(data+i+4); + d2 = _mm_loadu_ps(data+i+8); + d3 = _mm_loadu_ps(data+i+12); + d = d0; d = _mm_shuffle_ps(d, d, 0); + sum0 = _mm_add_ps(sum0, _mm_mul_ps(d0, d)); + sum1 = _mm_add_ps(sum1, _mm_mul_ps(d1, d)); + sum2 = _mm_add_ps(sum2, _mm_mul_ps(d2, d)); + sum3 = _mm_add_ps(sum3, _mm_mul_ps(d3, d)); + } + + { + __m128 d0 = _mm_setzero_ps(); + __m128 d1 = _mm_setzero_ps(); + __m128 d2 = _mm_setzero_ps(); + __m128 d3 = _mm_setzero_ps(); + limit++; if(limit < 0) limit = 0; + + for(i = data_len-1; i >= limit; i--) { + __m128 d; + d = _mm_load_ss(data+i); d = _mm_shuffle_ps(d, d, 0); + d3 = _mm_shuffle_ps(d3, d3, _MM_SHUFFLE(2,1,0,3)); + d2 = _mm_shuffle_ps(d2, d2, _MM_SHUFFLE(2,1,0,3)); + d1 = _mm_shuffle_ps(d1, d1, _MM_SHUFFLE(2,1,0,3)); + d0 = _mm_shuffle_ps(d0, d0, _MM_SHUFFLE(2,1,0,3)); + d3 = _mm_move_ss(d3, d2); + d2 = _mm_move_ss(d2, d1); + d1 = _mm_move_ss(d1, d0); + d0 = _mm_move_ss(d0, d); + sum3 = _mm_add_ps(sum3, _mm_mul_ps(d, d3)); + sum2 = _mm_add_ps(sum2, _mm_mul_ps(d, d2)); + sum1 = _mm_add_ps(sum1, _mm_mul_ps(d, d1)); + sum0 = _mm_add_ps(sum0, _mm_mul_ps(d, d0)); + } + } + + _mm_storeu_ps(autoc, sum0); + _mm_storeu_ps(autoc+4, sum1); + _mm_storeu_ps(autoc+8, sum2); + _mm_storeu_ps(autoc+12,sum3); +} + +/* old routines: faster on older Intel CPUs (up to Core 2) */ + +FLAC__SSE_TARGET("sse") +void FLAC__lpc_compute_autocorrelation_intrin_sse_lag_4_old(const FLAC__real data[], unsigned data_len, unsigned lag, FLAC__real autoc[]) +{ + __m128 xmm0, xmm2, xmm5; + + (void) lag; + FLAC__ASSERT(lag > 0); + FLAC__ASSERT(lag <= 4); + FLAC__ASSERT(lag <= data_len); + FLAC__ASSERT(data_len > 0); + + xmm5 = _mm_setzero_ps(); + + xmm0 = _mm_load_ss(data++); + xmm2 = xmm0; + xmm0 = _mm_shuffle_ps(xmm0, xmm0, 0); + + xmm0 = _mm_mul_ps(xmm0, xmm2); + xmm5 = _mm_add_ps(xmm5, xmm0); + + data_len--; + + while(data_len) + { + xmm0 = _mm_load1_ps(data++); + + xmm2 = _mm_shuffle_ps(xmm2, xmm2, _MM_SHUFFLE(2,1,0,3)); + xmm2 = _mm_move_ss(xmm2, xmm0); + xmm0 = _mm_mul_ps(xmm0, xmm2); + xmm5 = _mm_add_ps(xmm5, xmm0); + + data_len--; + } + + _mm_storeu_ps(autoc, xmm5); +} + +FLAC__SSE_TARGET("sse") +void FLAC__lpc_compute_autocorrelation_intrin_sse_lag_8_old(const FLAC__real data[], unsigned data_len, unsigned lag, FLAC__real autoc[]) +{ + __m128 xmm0, xmm1, xmm2, xmm3, xmm5, xmm6; + + (void) lag; + FLAC__ASSERT(lag > 0); + FLAC__ASSERT(lag <= 8); + FLAC__ASSERT(lag <= data_len); + FLAC__ASSERT(data_len > 0); + + xmm5 = _mm_setzero_ps(); + xmm6 = _mm_setzero_ps(); + + xmm0 = _mm_load_ss(data++); + xmm2 = xmm0; + xmm0 = _mm_shuffle_ps(xmm0, xmm0, 0); + xmm3 = _mm_setzero_ps(); + + xmm0 = _mm_mul_ps(xmm0, xmm2); + xmm5 = _mm_add_ps(xmm5, xmm0); + + data_len--; + + while(data_len) + { + xmm0 = _mm_load1_ps(data++); + + xmm2 = _mm_shuffle_ps(xmm2, xmm2, _MM_SHUFFLE(2,1,0,3)); + xmm3 = _mm_shuffle_ps(xmm3, xmm3, _MM_SHUFFLE(2,1,0,3)); + xmm3 = _mm_move_ss(xmm3, xmm2); + xmm2 = _mm_move_ss(xmm2, xmm0); + + xmm1 = xmm0; + xmm1 = _mm_mul_ps(xmm1, xmm3); + xmm0 = _mm_mul_ps(xmm0, xmm2); + xmm6 = _mm_add_ps(xmm6, xmm1); + xmm5 = _mm_add_ps(xmm5, xmm0); + + data_len--; + } + + _mm_storeu_ps(autoc, xmm5); + _mm_storeu_ps(autoc+4, xmm6); +} + +FLAC__SSE_TARGET("sse") +void FLAC__lpc_compute_autocorrelation_intrin_sse_lag_12_old(const FLAC__real data[], unsigned data_len, unsigned lag, FLAC__real autoc[]) +{ + __m128 xmm0, xmm1, xmm2, xmm3, xmm4, xmm5, xmm6, xmm7; + + (void) lag; + FLAC__ASSERT(lag > 0); + FLAC__ASSERT(lag <= 12); + FLAC__ASSERT(lag <= data_len); + FLAC__ASSERT(data_len > 0); + + xmm5 = _mm_setzero_ps(); + xmm6 = _mm_setzero_ps(); + xmm7 = _mm_setzero_ps(); + + xmm0 = _mm_load_ss(data++); + xmm2 = xmm0; + xmm0 = _mm_shuffle_ps(xmm0, xmm0, 0); + xmm3 = _mm_setzero_ps(); + xmm4 = _mm_setzero_ps(); + + xmm0 = _mm_mul_ps(xmm0, xmm2); + xmm5 = _mm_add_ps(xmm5, xmm0); + + data_len--; + + while(data_len) + { + xmm0 = _mm_load1_ps(data++); + + xmm2 = _mm_shuffle_ps(xmm2, xmm2, _MM_SHUFFLE(2,1,0,3)); + xmm3 = _mm_shuffle_ps(xmm3, xmm3, _MM_SHUFFLE(2,1,0,3)); + xmm4 = _mm_shuffle_ps(xmm4, xmm4, _MM_SHUFFLE(2,1,0,3)); + xmm4 = _mm_move_ss(xmm4, xmm3); + xmm3 = _mm_move_ss(xmm3, xmm2); + xmm2 = _mm_move_ss(xmm2, xmm0); + + xmm1 = xmm0; + xmm1 = _mm_mul_ps(xmm1, xmm2); + xmm5 = _mm_add_ps(xmm5, xmm1); + xmm1 = xmm0; + xmm1 = _mm_mul_ps(xmm1, xmm3); + xmm6 = _mm_add_ps(xmm6, xmm1); + xmm0 = _mm_mul_ps(xmm0, xmm4); + xmm7 = _mm_add_ps(xmm7, xmm0); + + data_len--; + } + + _mm_storeu_ps(autoc, xmm5); + _mm_storeu_ps(autoc+4, xmm6); + _mm_storeu_ps(autoc+8, xmm7); +} + +FLAC__SSE_TARGET("sse") +void FLAC__lpc_compute_autocorrelation_intrin_sse_lag_16_old(const FLAC__real data[], unsigned data_len, unsigned lag, FLAC__real autoc[]) +{ + __m128 xmm0, xmm1, xmm2, xmm3, xmm4, xmm5, xmm6, xmm7, xmm8, xmm9; + + (void) lag; + FLAC__ASSERT(lag > 0); + FLAC__ASSERT(lag <= 16); + FLAC__ASSERT(lag <= data_len); + FLAC__ASSERT(data_len > 0); + + xmm6 = _mm_setzero_ps(); + xmm7 = _mm_setzero_ps(); + xmm8 = _mm_setzero_ps(); + xmm9 = _mm_setzero_ps(); + + xmm0 = _mm_load_ss(data++); + xmm2 = xmm0; + xmm0 = _mm_shuffle_ps(xmm0, xmm0, 0); + xmm3 = _mm_setzero_ps(); + xmm4 = _mm_setzero_ps(); + xmm5 = _mm_setzero_ps(); + + xmm0 = _mm_mul_ps(xmm0, xmm2); + xmm6 = _mm_add_ps(xmm6, xmm0); + + data_len--; + + while(data_len) + { + xmm0 = _mm_load1_ps(data++); + + /* shift xmm5:xmm4:xmm3:xmm2 left by one float */ + xmm5 = _mm_shuffle_ps(xmm5, xmm5, _MM_SHUFFLE(2,1,0,3)); + xmm4 = _mm_shuffle_ps(xmm4, xmm4, _MM_SHUFFLE(2,1,0,3)); + xmm3 = _mm_shuffle_ps(xmm3, xmm3, _MM_SHUFFLE(2,1,0,3)); + xmm2 = _mm_shuffle_ps(xmm2, xmm2, _MM_SHUFFLE(2,1,0,3)); + xmm5 = _mm_move_ss(xmm5, xmm4); + xmm4 = _mm_move_ss(xmm4, xmm3); + xmm3 = _mm_move_ss(xmm3, xmm2); + xmm2 = _mm_move_ss(xmm2, xmm0); + + /* xmm9|xmm8|xmm7|xmm6 += xmm0|xmm0|xmm0|xmm0 * xmm5|xmm4|xmm3|xmm2 */ + xmm1 = xmm0; + xmm1 = _mm_mul_ps(xmm1, xmm5); + xmm9 = _mm_add_ps(xmm9, xmm1); + xmm1 = xmm0; + xmm1 = _mm_mul_ps(xmm1, xmm4); + xmm8 = _mm_add_ps(xmm8, xmm1); + xmm1 = xmm0; + xmm1 = _mm_mul_ps(xmm1, xmm3); + xmm7 = _mm_add_ps(xmm7, xmm1); + xmm0 = _mm_mul_ps(xmm0, xmm2); + xmm6 = _mm_add_ps(xmm6, xmm0); + + data_len--; + } + + _mm_storeu_ps(autoc, xmm6); + _mm_storeu_ps(autoc+4, xmm7); + _mm_storeu_ps(autoc+8, xmm8); + _mm_storeu_ps(autoc+12,xmm9); +} + +#endif /* FLAC__SSE_SUPPORTED */ +#endif /* (FLAC__CPU_IA32 || FLAC__CPU_X86_64) && FLAC__HAS_X86INTRIN */ +#endif /* FLAC__NO_ASM */ +#endif /* FLAC__INTEGER_ONLY_LIBRARY */ diff --git a/core/deps/flac/src/libFLAC/lpc_intrin_sse2.c b/core/deps/flac/src/libFLAC/lpc_intrin_sse2.c new file mode 100644 index 000000000..138339483 --- /dev/null +++ b/core/deps/flac/src/libFLAC/lpc_intrin_sse2.c @@ -0,0 +1,1090 @@ +/* libFLAC - Free Lossless Audio Codec library + * Copyright (C) 2000-2009 Josh Coalson + * Copyright (C) 2011-2016 Xiph.Org Foundation + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * - Neither the name of the Xiph.org Foundation nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifdef HAVE_CONFIG_H +# include +#endif + +#include "private/cpu.h" + +#ifndef FLAC__INTEGER_ONLY_LIBRARY +#ifndef FLAC__NO_ASM +#if (defined FLAC__CPU_IA32 || defined FLAC__CPU_X86_64) && FLAC__HAS_X86INTRIN +#include "private/lpc.h" +#ifdef FLAC__SSE2_SUPPORTED + +#include "FLAC/assert.h" +#include "FLAC/format.h" + +#include /* SSE2 */ + +#define RESIDUAL16_RESULT(xmmN) curr = *data++; *residual++ = curr - (_mm_cvtsi128_si32(xmmN) >> lp_quantization); +#define DATA16_RESULT(xmmN) curr = *residual++ + (_mm_cvtsi128_si32(xmmN) >> lp_quantization); *data++ = curr; + +#define RESIDUAL32_RESULT(xmmN) residual[i] = data[i] - (_mm_cvtsi128_si32(xmmN) >> lp_quantization); +#define DATA32_RESULT(xmmN) data[i] = residual[i] + (_mm_cvtsi128_si32(xmmN) >> lp_quantization); + +FLAC__SSE_TARGET("sse2") +void FLAC__lpc_compute_residual_from_qlp_coefficients_16_intrin_sse2(const FLAC__int32 *data, unsigned data_len, const FLAC__int32 qlp_coeff[], unsigned order, int lp_quantization, FLAC__int32 residual[]) +{ + int i; + FLAC__int32 sum; + __m128i cnt = _mm_cvtsi32_si128(lp_quantization); + + FLAC__ASSERT(order > 0); + FLAC__ASSERT(order <= 32); + + if(order <= 12) { + if(order > 8) { + if(order > 10) { + if(order == 12) { + __m128i q0, q1, q2, q3, q4, q5, q6, q7, q8, q9, q10, q11; + q0 = _mm_cvtsi32_si128(0xffff & qlp_coeff[0]); q0 = _mm_shuffle_epi32(q0, _MM_SHUFFLE(0,0,0,0)); + q1 = _mm_cvtsi32_si128(0xffff & qlp_coeff[1]); q1 = _mm_shuffle_epi32(q1, _MM_SHUFFLE(0,0,0,0)); + q2 = _mm_cvtsi32_si128(0xffff & qlp_coeff[2]); q2 = _mm_shuffle_epi32(q2, _MM_SHUFFLE(0,0,0,0)); + q3 = _mm_cvtsi32_si128(0xffff & qlp_coeff[3]); q3 = _mm_shuffle_epi32(q3, _MM_SHUFFLE(0,0,0,0)); + q4 = _mm_cvtsi32_si128(0xffff & qlp_coeff[4]); q4 = _mm_shuffle_epi32(q4, _MM_SHUFFLE(0,0,0,0)); + q5 = _mm_cvtsi32_si128(0xffff & qlp_coeff[5]); q5 = _mm_shuffle_epi32(q5, _MM_SHUFFLE(0,0,0,0)); + q6 = _mm_cvtsi32_si128(0xffff & qlp_coeff[6]); q6 = _mm_shuffle_epi32(q6, _MM_SHUFFLE(0,0,0,0)); + q7 = _mm_cvtsi32_si128(0xffff & qlp_coeff[7]); q7 = _mm_shuffle_epi32(q7, _MM_SHUFFLE(0,0,0,0)); + q8 = _mm_cvtsi32_si128(0xffff & qlp_coeff[8]); q8 = _mm_shuffle_epi32(q8, _MM_SHUFFLE(0,0,0,0)); + q9 = _mm_cvtsi32_si128(0xffff & qlp_coeff[9]); q9 = _mm_shuffle_epi32(q9, _MM_SHUFFLE(0,0,0,0)); + q10 = _mm_cvtsi32_si128(0xffff & qlp_coeff[10]); q10 = _mm_shuffle_epi32(q10, _MM_SHUFFLE(0,0,0,0)); + q11 = _mm_cvtsi32_si128(0xffff & qlp_coeff[11]); q11 = _mm_shuffle_epi32(q11, _MM_SHUFFLE(0,0,0,0)); + + for(i = 0; i < (int)data_len-3; i+=4) { + __m128i summ, mull; + summ = _mm_madd_epi16(q11, _mm_loadu_si128((const __m128i*)(data+i-12))); + mull = _mm_madd_epi16(q10, _mm_loadu_si128((const __m128i*)(data+i-11))); summ = _mm_add_epi32(summ, mull); + mull = _mm_madd_epi16(q9, _mm_loadu_si128((const __m128i*)(data+i-10))); summ = _mm_add_epi32(summ, mull); + mull = _mm_madd_epi16(q8, _mm_loadu_si128((const __m128i*)(data+i-9))); summ = _mm_add_epi32(summ, mull); + mull = _mm_madd_epi16(q7, _mm_loadu_si128((const __m128i*)(data+i-8))); summ = _mm_add_epi32(summ, mull); + mull = _mm_madd_epi16(q6, _mm_loadu_si128((const __m128i*)(data+i-7))); summ = _mm_add_epi32(summ, mull); + mull = _mm_madd_epi16(q5, _mm_loadu_si128((const __m128i*)(data+i-6))); summ = _mm_add_epi32(summ, mull); + mull = _mm_madd_epi16(q4, _mm_loadu_si128((const __m128i*)(data+i-5))); summ = _mm_add_epi32(summ, mull); + mull = _mm_madd_epi16(q3, _mm_loadu_si128((const __m128i*)(data+i-4))); summ = _mm_add_epi32(summ, mull); + mull = _mm_madd_epi16(q2, _mm_loadu_si128((const __m128i*)(data+i-3))); summ = _mm_add_epi32(summ, mull); + mull = _mm_madd_epi16(q1, _mm_loadu_si128((const __m128i*)(data+i-2))); summ = _mm_add_epi32(summ, mull); + mull = _mm_madd_epi16(q0, _mm_loadu_si128((const __m128i*)(data+i-1))); summ = _mm_add_epi32(summ, mull); + summ = _mm_sra_epi32(summ, cnt); + _mm_storeu_si128((__m128i*)(residual+i), _mm_sub_epi32(_mm_loadu_si128((const __m128i*)(data+i)), summ)); + } + } + else { /* order == 11 */ + __m128i q0, q1, q2, q3, q4, q5, q6, q7, q8, q9, q10; + q0 = _mm_cvtsi32_si128(0xffff & qlp_coeff[0]); q0 = _mm_shuffle_epi32(q0, _MM_SHUFFLE(0,0,0,0)); + q1 = _mm_cvtsi32_si128(0xffff & qlp_coeff[1]); q1 = _mm_shuffle_epi32(q1, _MM_SHUFFLE(0,0,0,0)); + q2 = _mm_cvtsi32_si128(0xffff & qlp_coeff[2]); q2 = _mm_shuffle_epi32(q2, _MM_SHUFFLE(0,0,0,0)); + q3 = _mm_cvtsi32_si128(0xffff & qlp_coeff[3]); q3 = _mm_shuffle_epi32(q3, _MM_SHUFFLE(0,0,0,0)); + q4 = _mm_cvtsi32_si128(0xffff & qlp_coeff[4]); q4 = _mm_shuffle_epi32(q4, _MM_SHUFFLE(0,0,0,0)); + q5 = _mm_cvtsi32_si128(0xffff & qlp_coeff[5]); q5 = _mm_shuffle_epi32(q5, _MM_SHUFFLE(0,0,0,0)); + q6 = _mm_cvtsi32_si128(0xffff & qlp_coeff[6]); q6 = _mm_shuffle_epi32(q6, _MM_SHUFFLE(0,0,0,0)); + q7 = _mm_cvtsi32_si128(0xffff & qlp_coeff[7]); q7 = _mm_shuffle_epi32(q7, _MM_SHUFFLE(0,0,0,0)); + q8 = _mm_cvtsi32_si128(0xffff & qlp_coeff[8]); q8 = _mm_shuffle_epi32(q8, _MM_SHUFFLE(0,0,0,0)); + q9 = _mm_cvtsi32_si128(0xffff & qlp_coeff[9]); q9 = _mm_shuffle_epi32(q9, _MM_SHUFFLE(0,0,0,0)); + q10 = _mm_cvtsi32_si128(0xffff & qlp_coeff[10]); q10 = _mm_shuffle_epi32(q10, _MM_SHUFFLE(0,0,0,0)); + + for(i = 0; i < (int)data_len-3; i+=4) { + __m128i summ, mull; + summ = _mm_madd_epi16(q10, _mm_loadu_si128((const __m128i*)(data+i-11))); + mull = _mm_madd_epi16(q9, _mm_loadu_si128((const __m128i*)(data+i-10))); summ = _mm_add_epi32(summ, mull); + mull = _mm_madd_epi16(q8, _mm_loadu_si128((const __m128i*)(data+i-9))); summ = _mm_add_epi32(summ, mull); + mull = _mm_madd_epi16(q7, _mm_loadu_si128((const __m128i*)(data+i-8))); summ = _mm_add_epi32(summ, mull); + mull = _mm_madd_epi16(q6, _mm_loadu_si128((const __m128i*)(data+i-7))); summ = _mm_add_epi32(summ, mull); + mull = _mm_madd_epi16(q5, _mm_loadu_si128((const __m128i*)(data+i-6))); summ = _mm_add_epi32(summ, mull); + mull = _mm_madd_epi16(q4, _mm_loadu_si128((const __m128i*)(data+i-5))); summ = _mm_add_epi32(summ, mull); + mull = _mm_madd_epi16(q3, _mm_loadu_si128((const __m128i*)(data+i-4))); summ = _mm_add_epi32(summ, mull); + mull = _mm_madd_epi16(q2, _mm_loadu_si128((const __m128i*)(data+i-3))); summ = _mm_add_epi32(summ, mull); + mull = _mm_madd_epi16(q1, _mm_loadu_si128((const __m128i*)(data+i-2))); summ = _mm_add_epi32(summ, mull); + mull = _mm_madd_epi16(q0, _mm_loadu_si128((const __m128i*)(data+i-1))); summ = _mm_add_epi32(summ, mull); + summ = _mm_sra_epi32(summ, cnt); + _mm_storeu_si128((__m128i*)(residual+i), _mm_sub_epi32(_mm_loadu_si128((const __m128i*)(data+i)), summ)); + } + } + } + else { + if(order == 10) { + __m128i q0, q1, q2, q3, q4, q5, q6, q7, q8, q9; + q0 = _mm_cvtsi32_si128(0xffff & qlp_coeff[0]); q0 = _mm_shuffle_epi32(q0, _MM_SHUFFLE(0,0,0,0)); + q1 = _mm_cvtsi32_si128(0xffff & qlp_coeff[1]); q1 = _mm_shuffle_epi32(q1, _MM_SHUFFLE(0,0,0,0)); + q2 = _mm_cvtsi32_si128(0xffff & qlp_coeff[2]); q2 = _mm_shuffle_epi32(q2, _MM_SHUFFLE(0,0,0,0)); + q3 = _mm_cvtsi32_si128(0xffff & qlp_coeff[3]); q3 = _mm_shuffle_epi32(q3, _MM_SHUFFLE(0,0,0,0)); + q4 = _mm_cvtsi32_si128(0xffff & qlp_coeff[4]); q4 = _mm_shuffle_epi32(q4, _MM_SHUFFLE(0,0,0,0)); + q5 = _mm_cvtsi32_si128(0xffff & qlp_coeff[5]); q5 = _mm_shuffle_epi32(q5, _MM_SHUFFLE(0,0,0,0)); + q6 = _mm_cvtsi32_si128(0xffff & qlp_coeff[6]); q6 = _mm_shuffle_epi32(q6, _MM_SHUFFLE(0,0,0,0)); + q7 = _mm_cvtsi32_si128(0xffff & qlp_coeff[7]); q7 = _mm_shuffle_epi32(q7, _MM_SHUFFLE(0,0,0,0)); + q8 = _mm_cvtsi32_si128(0xffff & qlp_coeff[8]); q8 = _mm_shuffle_epi32(q8, _MM_SHUFFLE(0,0,0,0)); + q9 = _mm_cvtsi32_si128(0xffff & qlp_coeff[9]); q9 = _mm_shuffle_epi32(q9, _MM_SHUFFLE(0,0,0,0)); + + for(i = 0; i < (int)data_len-3; i+=4) { + __m128i summ, mull; + summ = _mm_madd_epi16(q9, _mm_loadu_si128((const __m128i*)(data+i-10))); + mull = _mm_madd_epi16(q8, _mm_loadu_si128((const __m128i*)(data+i-9))); summ = _mm_add_epi32(summ, mull); + mull = _mm_madd_epi16(q7, _mm_loadu_si128((const __m128i*)(data+i-8))); summ = _mm_add_epi32(summ, mull); + mull = _mm_madd_epi16(q6, _mm_loadu_si128((const __m128i*)(data+i-7))); summ = _mm_add_epi32(summ, mull); + mull = _mm_madd_epi16(q5, _mm_loadu_si128((const __m128i*)(data+i-6))); summ = _mm_add_epi32(summ, mull); + mull = _mm_madd_epi16(q4, _mm_loadu_si128((const __m128i*)(data+i-5))); summ = _mm_add_epi32(summ, mull); + mull = _mm_madd_epi16(q3, _mm_loadu_si128((const __m128i*)(data+i-4))); summ = _mm_add_epi32(summ, mull); + mull = _mm_madd_epi16(q2, _mm_loadu_si128((const __m128i*)(data+i-3))); summ = _mm_add_epi32(summ, mull); + mull = _mm_madd_epi16(q1, _mm_loadu_si128((const __m128i*)(data+i-2))); summ = _mm_add_epi32(summ, mull); + mull = _mm_madd_epi16(q0, _mm_loadu_si128((const __m128i*)(data+i-1))); summ = _mm_add_epi32(summ, mull); + summ = _mm_sra_epi32(summ, cnt); + _mm_storeu_si128((__m128i*)(residual+i), _mm_sub_epi32(_mm_loadu_si128((const __m128i*)(data+i)), summ)); + } + } + else { /* order == 9 */ + __m128i q0, q1, q2, q3, q4, q5, q6, q7, q8; + q0 = _mm_cvtsi32_si128(0xffff & qlp_coeff[0]); q0 = _mm_shuffle_epi32(q0, _MM_SHUFFLE(0,0,0,0)); + q1 = _mm_cvtsi32_si128(0xffff & qlp_coeff[1]); q1 = _mm_shuffle_epi32(q1, _MM_SHUFFLE(0,0,0,0)); + q2 = _mm_cvtsi32_si128(0xffff & qlp_coeff[2]); q2 = _mm_shuffle_epi32(q2, _MM_SHUFFLE(0,0,0,0)); + q3 = _mm_cvtsi32_si128(0xffff & qlp_coeff[3]); q3 = _mm_shuffle_epi32(q3, _MM_SHUFFLE(0,0,0,0)); + q4 = _mm_cvtsi32_si128(0xffff & qlp_coeff[4]); q4 = _mm_shuffle_epi32(q4, _MM_SHUFFLE(0,0,0,0)); + q5 = _mm_cvtsi32_si128(0xffff & qlp_coeff[5]); q5 = _mm_shuffle_epi32(q5, _MM_SHUFFLE(0,0,0,0)); + q6 = _mm_cvtsi32_si128(0xffff & qlp_coeff[6]); q6 = _mm_shuffle_epi32(q6, _MM_SHUFFLE(0,0,0,0)); + q7 = _mm_cvtsi32_si128(0xffff & qlp_coeff[7]); q7 = _mm_shuffle_epi32(q7, _MM_SHUFFLE(0,0,0,0)); + q8 = _mm_cvtsi32_si128(0xffff & qlp_coeff[8]); q8 = _mm_shuffle_epi32(q8, _MM_SHUFFLE(0,0,0,0)); + + for(i = 0; i < (int)data_len-3; i+=4) { + __m128i summ, mull; + summ = _mm_madd_epi16(q8, _mm_loadu_si128((const __m128i*)(data+i-9))); + mull = _mm_madd_epi16(q7, _mm_loadu_si128((const __m128i*)(data+i-8))); summ = _mm_add_epi32(summ, mull); + mull = _mm_madd_epi16(q6, _mm_loadu_si128((const __m128i*)(data+i-7))); summ = _mm_add_epi32(summ, mull); + mull = _mm_madd_epi16(q5, _mm_loadu_si128((const __m128i*)(data+i-6))); summ = _mm_add_epi32(summ, mull); + mull = _mm_madd_epi16(q4, _mm_loadu_si128((const __m128i*)(data+i-5))); summ = _mm_add_epi32(summ, mull); + mull = _mm_madd_epi16(q3, _mm_loadu_si128((const __m128i*)(data+i-4))); summ = _mm_add_epi32(summ, mull); + mull = _mm_madd_epi16(q2, _mm_loadu_si128((const __m128i*)(data+i-3))); summ = _mm_add_epi32(summ, mull); + mull = _mm_madd_epi16(q1, _mm_loadu_si128((const __m128i*)(data+i-2))); summ = _mm_add_epi32(summ, mull); + mull = _mm_madd_epi16(q0, _mm_loadu_si128((const __m128i*)(data+i-1))); summ = _mm_add_epi32(summ, mull); + summ = _mm_sra_epi32(summ, cnt); + _mm_storeu_si128((__m128i*)(residual+i), _mm_sub_epi32(_mm_loadu_si128((const __m128i*)(data+i)), summ)); + } + } + } + } + else if(order > 4) { + if(order > 6) { + if(order == 8) { + __m128i q0, q1, q2, q3, q4, q5, q6, q7; + q0 = _mm_cvtsi32_si128(0xffff & qlp_coeff[0]); q0 = _mm_shuffle_epi32(q0, _MM_SHUFFLE(0,0,0,0)); + q1 = _mm_cvtsi32_si128(0xffff & qlp_coeff[1]); q1 = _mm_shuffle_epi32(q1, _MM_SHUFFLE(0,0,0,0)); + q2 = _mm_cvtsi32_si128(0xffff & qlp_coeff[2]); q2 = _mm_shuffle_epi32(q2, _MM_SHUFFLE(0,0,0,0)); + q3 = _mm_cvtsi32_si128(0xffff & qlp_coeff[3]); q3 = _mm_shuffle_epi32(q3, _MM_SHUFFLE(0,0,0,0)); + q4 = _mm_cvtsi32_si128(0xffff & qlp_coeff[4]); q4 = _mm_shuffle_epi32(q4, _MM_SHUFFLE(0,0,0,0)); + q5 = _mm_cvtsi32_si128(0xffff & qlp_coeff[5]); q5 = _mm_shuffle_epi32(q5, _MM_SHUFFLE(0,0,0,0)); + q6 = _mm_cvtsi32_si128(0xffff & qlp_coeff[6]); q6 = _mm_shuffle_epi32(q6, _MM_SHUFFLE(0,0,0,0)); + q7 = _mm_cvtsi32_si128(0xffff & qlp_coeff[7]); q7 = _mm_shuffle_epi32(q7, _MM_SHUFFLE(0,0,0,0)); + + for(i = 0; i < (int)data_len-3; i+=4) { + __m128i summ, mull; + summ = _mm_madd_epi16(q7, _mm_loadu_si128((const __m128i*)(data+i-8))); + mull = _mm_madd_epi16(q6, _mm_loadu_si128((const __m128i*)(data+i-7))); summ = _mm_add_epi32(summ, mull); + mull = _mm_madd_epi16(q5, _mm_loadu_si128((const __m128i*)(data+i-6))); summ = _mm_add_epi32(summ, mull); + mull = _mm_madd_epi16(q4, _mm_loadu_si128((const __m128i*)(data+i-5))); summ = _mm_add_epi32(summ, mull); + mull = _mm_madd_epi16(q3, _mm_loadu_si128((const __m128i*)(data+i-4))); summ = _mm_add_epi32(summ, mull); + mull = _mm_madd_epi16(q2, _mm_loadu_si128((const __m128i*)(data+i-3))); summ = _mm_add_epi32(summ, mull); + mull = _mm_madd_epi16(q1, _mm_loadu_si128((const __m128i*)(data+i-2))); summ = _mm_add_epi32(summ, mull); + mull = _mm_madd_epi16(q0, _mm_loadu_si128((const __m128i*)(data+i-1))); summ = _mm_add_epi32(summ, mull); + summ = _mm_sra_epi32(summ, cnt); + _mm_storeu_si128((__m128i*)(residual+i), _mm_sub_epi32(_mm_loadu_si128((const __m128i*)(data+i)), summ)); + } + } + else { /* order == 7 */ + __m128i q0, q1, q2, q3, q4, q5, q6; + q0 = _mm_cvtsi32_si128(0xffff & qlp_coeff[0]); q0 = _mm_shuffle_epi32(q0, _MM_SHUFFLE(0,0,0,0)); + q1 = _mm_cvtsi32_si128(0xffff & qlp_coeff[1]); q1 = _mm_shuffle_epi32(q1, _MM_SHUFFLE(0,0,0,0)); + q2 = _mm_cvtsi32_si128(0xffff & qlp_coeff[2]); q2 = _mm_shuffle_epi32(q2, _MM_SHUFFLE(0,0,0,0)); + q3 = _mm_cvtsi32_si128(0xffff & qlp_coeff[3]); q3 = _mm_shuffle_epi32(q3, _MM_SHUFFLE(0,0,0,0)); + q4 = _mm_cvtsi32_si128(0xffff & qlp_coeff[4]); q4 = _mm_shuffle_epi32(q4, _MM_SHUFFLE(0,0,0,0)); + q5 = _mm_cvtsi32_si128(0xffff & qlp_coeff[5]); q5 = _mm_shuffle_epi32(q5, _MM_SHUFFLE(0,0,0,0)); + q6 = _mm_cvtsi32_si128(0xffff & qlp_coeff[6]); q6 = _mm_shuffle_epi32(q6, _MM_SHUFFLE(0,0,0,0)); + + for(i = 0; i < (int)data_len-3; i+=4) { + __m128i summ, mull; + summ = _mm_madd_epi16(q6, _mm_loadu_si128((const __m128i*)(data+i-7))); + mull = _mm_madd_epi16(q5, _mm_loadu_si128((const __m128i*)(data+i-6))); summ = _mm_add_epi32(summ, mull); + mull = _mm_madd_epi16(q4, _mm_loadu_si128((const __m128i*)(data+i-5))); summ = _mm_add_epi32(summ, mull); + mull = _mm_madd_epi16(q3, _mm_loadu_si128((const __m128i*)(data+i-4))); summ = _mm_add_epi32(summ, mull); + mull = _mm_madd_epi16(q2, _mm_loadu_si128((const __m128i*)(data+i-3))); summ = _mm_add_epi32(summ, mull); + mull = _mm_madd_epi16(q1, _mm_loadu_si128((const __m128i*)(data+i-2))); summ = _mm_add_epi32(summ, mull); + mull = _mm_madd_epi16(q0, _mm_loadu_si128((const __m128i*)(data+i-1))); summ = _mm_add_epi32(summ, mull); + summ = _mm_sra_epi32(summ, cnt); + _mm_storeu_si128((__m128i*)(residual+i), _mm_sub_epi32(_mm_loadu_si128((const __m128i*)(data+i)), summ)); + } + } + } + else { + if(order == 6) { + __m128i q0, q1, q2, q3, q4, q5; + q0 = _mm_cvtsi32_si128(0xffff & qlp_coeff[0]); q0 = _mm_shuffle_epi32(q0, _MM_SHUFFLE(0,0,0,0)); + q1 = _mm_cvtsi32_si128(0xffff & qlp_coeff[1]); q1 = _mm_shuffle_epi32(q1, _MM_SHUFFLE(0,0,0,0)); + q2 = _mm_cvtsi32_si128(0xffff & qlp_coeff[2]); q2 = _mm_shuffle_epi32(q2, _MM_SHUFFLE(0,0,0,0)); + q3 = _mm_cvtsi32_si128(0xffff & qlp_coeff[3]); q3 = _mm_shuffle_epi32(q3, _MM_SHUFFLE(0,0,0,0)); + q4 = _mm_cvtsi32_si128(0xffff & qlp_coeff[4]); q4 = _mm_shuffle_epi32(q4, _MM_SHUFFLE(0,0,0,0)); + q5 = _mm_cvtsi32_si128(0xffff & qlp_coeff[5]); q5 = _mm_shuffle_epi32(q5, _MM_SHUFFLE(0,0,0,0)); + + for(i = 0; i < (int)data_len-3; i+=4) { + __m128i summ, mull; + summ = _mm_madd_epi16(q5, _mm_loadu_si128((const __m128i*)(data+i-6))); + mull = _mm_madd_epi16(q4, _mm_loadu_si128((const __m128i*)(data+i-5))); summ = _mm_add_epi32(summ, mull); + mull = _mm_madd_epi16(q3, _mm_loadu_si128((const __m128i*)(data+i-4))); summ = _mm_add_epi32(summ, mull); + mull = _mm_madd_epi16(q2, _mm_loadu_si128((const __m128i*)(data+i-3))); summ = _mm_add_epi32(summ, mull); + mull = _mm_madd_epi16(q1, _mm_loadu_si128((const __m128i*)(data+i-2))); summ = _mm_add_epi32(summ, mull); + mull = _mm_madd_epi16(q0, _mm_loadu_si128((const __m128i*)(data+i-1))); summ = _mm_add_epi32(summ, mull); + summ = _mm_sra_epi32(summ, cnt); + _mm_storeu_si128((__m128i*)(residual+i), _mm_sub_epi32(_mm_loadu_si128((const __m128i*)(data+i)), summ)); + } + } + else { /* order == 5 */ + __m128i q0, q1, q2, q3, q4; + q0 = _mm_cvtsi32_si128(0xffff & qlp_coeff[0]); q0 = _mm_shuffle_epi32(q0, _MM_SHUFFLE(0,0,0,0)); + q1 = _mm_cvtsi32_si128(0xffff & qlp_coeff[1]); q1 = _mm_shuffle_epi32(q1, _MM_SHUFFLE(0,0,0,0)); + q2 = _mm_cvtsi32_si128(0xffff & qlp_coeff[2]); q2 = _mm_shuffle_epi32(q2, _MM_SHUFFLE(0,0,0,0)); + q3 = _mm_cvtsi32_si128(0xffff & qlp_coeff[3]); q3 = _mm_shuffle_epi32(q3, _MM_SHUFFLE(0,0,0,0)); + q4 = _mm_cvtsi32_si128(0xffff & qlp_coeff[4]); q4 = _mm_shuffle_epi32(q4, _MM_SHUFFLE(0,0,0,0)); + + for(i = 0; i < (int)data_len-3; i+=4) { + __m128i summ, mull; + summ = _mm_madd_epi16(q4, _mm_loadu_si128((const __m128i*)(data+i-5))); + mull = _mm_madd_epi16(q3, _mm_loadu_si128((const __m128i*)(data+i-4))); summ = _mm_add_epi32(summ, mull); + mull = _mm_madd_epi16(q2, _mm_loadu_si128((const __m128i*)(data+i-3))); summ = _mm_add_epi32(summ, mull); + mull = _mm_madd_epi16(q1, _mm_loadu_si128((const __m128i*)(data+i-2))); summ = _mm_add_epi32(summ, mull); + mull = _mm_madd_epi16(q0, _mm_loadu_si128((const __m128i*)(data+i-1))); summ = _mm_add_epi32(summ, mull); + summ = _mm_sra_epi32(summ, cnt); + _mm_storeu_si128((__m128i*)(residual+i), _mm_sub_epi32(_mm_loadu_si128((const __m128i*)(data+i)), summ)); + } + } + } + } + else { + if(order > 2) { + if(order == 4) { + __m128i q0, q1, q2, q3; + q0 = _mm_cvtsi32_si128(0xffff & qlp_coeff[0]); q0 = _mm_shuffle_epi32(q0, _MM_SHUFFLE(0,0,0,0)); + q1 = _mm_cvtsi32_si128(0xffff & qlp_coeff[1]); q1 = _mm_shuffle_epi32(q1, _MM_SHUFFLE(0,0,0,0)); + q2 = _mm_cvtsi32_si128(0xffff & qlp_coeff[2]); q2 = _mm_shuffle_epi32(q2, _MM_SHUFFLE(0,0,0,0)); + q3 = _mm_cvtsi32_si128(0xffff & qlp_coeff[3]); q3 = _mm_shuffle_epi32(q3, _MM_SHUFFLE(0,0,0,0)); + + for(i = 0; i < (int)data_len-3; i+=4) { + __m128i summ, mull; + summ = _mm_madd_epi16(q3, _mm_loadu_si128((const __m128i*)(data+i-4))); + mull = _mm_madd_epi16(q2, _mm_loadu_si128((const __m128i*)(data+i-3))); summ = _mm_add_epi32(summ, mull); + mull = _mm_madd_epi16(q1, _mm_loadu_si128((const __m128i*)(data+i-2))); summ = _mm_add_epi32(summ, mull); + mull = _mm_madd_epi16(q0, _mm_loadu_si128((const __m128i*)(data+i-1))); summ = _mm_add_epi32(summ, mull); + summ = _mm_sra_epi32(summ, cnt); + _mm_storeu_si128((__m128i*)(residual+i), _mm_sub_epi32(_mm_loadu_si128((const __m128i*)(data+i)), summ)); + } + } + else { /* order == 3 */ + __m128i q0, q1, q2; + q0 = _mm_cvtsi32_si128(0xffff & qlp_coeff[0]); q0 = _mm_shuffle_epi32(q0, _MM_SHUFFLE(0,0,0,0)); + q1 = _mm_cvtsi32_si128(0xffff & qlp_coeff[1]); q1 = _mm_shuffle_epi32(q1, _MM_SHUFFLE(0,0,0,0)); + q2 = _mm_cvtsi32_si128(0xffff & qlp_coeff[2]); q2 = _mm_shuffle_epi32(q2, _MM_SHUFFLE(0,0,0,0)); + + for(i = 0; i < (int)data_len-3; i+=4) { + __m128i summ, mull; + summ = _mm_madd_epi16(q2, _mm_loadu_si128((const __m128i*)(data+i-3))); + mull = _mm_madd_epi16(q1, _mm_loadu_si128((const __m128i*)(data+i-2))); summ = _mm_add_epi32(summ, mull); + mull = _mm_madd_epi16(q0, _mm_loadu_si128((const __m128i*)(data+i-1))); summ = _mm_add_epi32(summ, mull); + summ = _mm_sra_epi32(summ, cnt); + _mm_storeu_si128((__m128i*)(residual+i), _mm_sub_epi32(_mm_loadu_si128((const __m128i*)(data+i)), summ)); + } + } + } + else { + if(order == 2) { + __m128i q0, q1; + q0 = _mm_cvtsi32_si128(0xffff & qlp_coeff[0]); q0 = _mm_shuffle_epi32(q0, _MM_SHUFFLE(0,0,0,0)); + q1 = _mm_cvtsi32_si128(0xffff & qlp_coeff[1]); q1 = _mm_shuffle_epi32(q1, _MM_SHUFFLE(0,0,0,0)); + + for(i = 0; i < (int)data_len-3; i+=4) { + __m128i summ, mull; + summ = _mm_madd_epi16(q1, _mm_loadu_si128((const __m128i*)(data+i-2))); + mull = _mm_madd_epi16(q0, _mm_loadu_si128((const __m128i*)(data+i-1))); summ = _mm_add_epi32(summ, mull); + summ = _mm_sra_epi32(summ, cnt); + _mm_storeu_si128((__m128i*)(residual+i), _mm_sub_epi32(_mm_loadu_si128((const __m128i*)(data+i)), summ)); + } + } + else { /* order == 1 */ + __m128i q0; + q0 = _mm_cvtsi32_si128(0xffff & qlp_coeff[0]); q0 = _mm_shuffle_epi32(q0, _MM_SHUFFLE(0,0,0,0)); + + for(i = 0; i < (int)data_len-3; i+=4) { + __m128i summ; + summ = _mm_madd_epi16(q0, _mm_loadu_si128((const __m128i*)(data+i-1))); + summ = _mm_sra_epi32(summ, cnt); + _mm_storeu_si128((__m128i*)(residual+i), _mm_sub_epi32(_mm_loadu_si128((const __m128i*)(data+i)), summ)); + } + } + } + } + for(; i < (int)data_len; i++) { + sum = 0; + switch(order) { + case 12: sum += qlp_coeff[11] * data[i-12]; + case 11: sum += qlp_coeff[10] * data[i-11]; + case 10: sum += qlp_coeff[ 9] * data[i-10]; + case 9: sum += qlp_coeff[ 8] * data[i- 9]; + case 8: sum += qlp_coeff[ 7] * data[i- 8]; + case 7: sum += qlp_coeff[ 6] * data[i- 7]; + case 6: sum += qlp_coeff[ 5] * data[i- 6]; + case 5: sum += qlp_coeff[ 4] * data[i- 5]; + case 4: sum += qlp_coeff[ 3] * data[i- 4]; + case 3: sum += qlp_coeff[ 2] * data[i- 3]; + case 2: sum += qlp_coeff[ 1] * data[i- 2]; + case 1: sum += qlp_coeff[ 0] * data[i- 1]; + } + residual[i] = data[i] - (sum >> lp_quantization); + } + } + else { /* order > 12 */ + for(i = 0; i < (int)data_len; i++) { + sum = 0; + switch(order) { + case 32: sum += qlp_coeff[31] * data[i-32]; + case 31: sum += qlp_coeff[30] * data[i-31]; + case 30: sum += qlp_coeff[29] * data[i-30]; + case 29: sum += qlp_coeff[28] * data[i-29]; + case 28: sum += qlp_coeff[27] * data[i-28]; + case 27: sum += qlp_coeff[26] * data[i-27]; + case 26: sum += qlp_coeff[25] * data[i-26]; + case 25: sum += qlp_coeff[24] * data[i-25]; + case 24: sum += qlp_coeff[23] * data[i-24]; + case 23: sum += qlp_coeff[22] * data[i-23]; + case 22: sum += qlp_coeff[21] * data[i-22]; + case 21: sum += qlp_coeff[20] * data[i-21]; + case 20: sum += qlp_coeff[19] * data[i-20]; + case 19: sum += qlp_coeff[18] * data[i-19]; + case 18: sum += qlp_coeff[17] * data[i-18]; + case 17: sum += qlp_coeff[16] * data[i-17]; + case 16: sum += qlp_coeff[15] * data[i-16]; + case 15: sum += qlp_coeff[14] * data[i-15]; + case 14: sum += qlp_coeff[13] * data[i-14]; + case 13: sum += qlp_coeff[12] * data[i-13]; + sum += qlp_coeff[11] * data[i-12]; + sum += qlp_coeff[10] * data[i-11]; + sum += qlp_coeff[ 9] * data[i-10]; + sum += qlp_coeff[ 8] * data[i- 9]; + sum += qlp_coeff[ 7] * data[i- 8]; + sum += qlp_coeff[ 6] * data[i- 7]; + sum += qlp_coeff[ 5] * data[i- 6]; + sum += qlp_coeff[ 4] * data[i- 5]; + sum += qlp_coeff[ 3] * data[i- 4]; + sum += qlp_coeff[ 2] * data[i- 3]; + sum += qlp_coeff[ 1] * data[i- 2]; + sum += qlp_coeff[ 0] * data[i- 1]; + } + residual[i] = data[i] - (sum >> lp_quantization); + } + } +} + +FLAC__SSE_TARGET("sse2") +void FLAC__lpc_compute_residual_from_qlp_coefficients_intrin_sse2(const FLAC__int32 *data, unsigned data_len, const FLAC__int32 qlp_coeff[], unsigned order, int lp_quantization, FLAC__int32 residual[]) +{ + int i; + + FLAC__ASSERT(order > 0); + FLAC__ASSERT(order <= 32); + + if(order <= 12) { + if(order > 8) { /* order == 9, 10, 11, 12 */ + if(order > 10) { /* order == 11, 12 */ + if(order == 12) { + __m128i xmm0, xmm1, xmm2, xmm3, xmm4, xmm5, xmm6, xmm7; + xmm0 = _mm_loadl_epi64((const __m128i*)(qlp_coeff+0)); // 0 0 q[1] q[0] + xmm1 = _mm_loadl_epi64((const __m128i*)(qlp_coeff+2)); // 0 0 q[3] q[2] + xmm2 = _mm_loadl_epi64((const __m128i*)(qlp_coeff+4)); // 0 0 q[5] q[4] + xmm3 = _mm_loadl_epi64((const __m128i*)(qlp_coeff+6)); // 0 0 q[7] q[6] + xmm4 = _mm_loadl_epi64((const __m128i*)(qlp_coeff+8)); // 0 0 q[9] q[8] + xmm5 = _mm_loadl_epi64((const __m128i*)(qlp_coeff+10)); // 0 0 q[11] q[10] + + xmm0 = _mm_shuffle_epi32(xmm0, _MM_SHUFFLE(3,1,2,0)); // 0 q[1] 0 q[0] + xmm1 = _mm_shuffle_epi32(xmm1, _MM_SHUFFLE(3,1,2,0)); // 0 q[3] 0 q[2] + xmm2 = _mm_shuffle_epi32(xmm2, _MM_SHUFFLE(3,1,2,0)); // 0 q[5] 0 q[4] + xmm3 = _mm_shuffle_epi32(xmm3, _MM_SHUFFLE(3,1,2,0)); // 0 q[7] 0 q[6] + xmm4 = _mm_shuffle_epi32(xmm4, _MM_SHUFFLE(3,1,2,0)); // 0 q[9] 0 q[8] + xmm5 = _mm_shuffle_epi32(xmm5, _MM_SHUFFLE(3,1,2,0)); // 0 q[11] 0 q[10] + + for(i = 0; i < (int)data_len; i++) { + //sum = 0; + //sum += qlp_coeff[11] * data[i-12]; + //sum += qlp_coeff[10] * data[i-11]; + xmm7 = _mm_loadl_epi64((const __m128i*)(data+i-12)); // 0 0 d[i-11] d[i-12] + xmm7 = _mm_shuffle_epi32(xmm7, _MM_SHUFFLE(2,0,3,1)); // 0 d[i-12] 0 d[i-11] + xmm7 = _mm_mul_epu32(xmm7, xmm5); /* we use _unsigned_ multiplication and discard high dword of the result values */ + + //sum += qlp_coeff[9] * data[i-10]; + //sum += qlp_coeff[8] * data[i-9]; + xmm6 = _mm_loadl_epi64((const __m128i*)(data+i-10)); + xmm6 = _mm_shuffle_epi32(xmm6, _MM_SHUFFLE(2,0,3,1)); + xmm6 = _mm_mul_epu32(xmm6, xmm4); + xmm7 = _mm_add_epi32(xmm7, xmm6); + + //sum += qlp_coeff[7] * data[i-8]; + //sum += qlp_coeff[6] * data[i-7]; + xmm6 = _mm_loadl_epi64((const __m128i*)(data+i-8)); + xmm6 = _mm_shuffle_epi32(xmm6, _MM_SHUFFLE(2,0,3,1)); + xmm6 = _mm_mul_epu32(xmm6, xmm3); + xmm7 = _mm_add_epi32(xmm7, xmm6); + + //sum += qlp_coeff[5] * data[i-6]; + //sum += qlp_coeff[4] * data[i-5]; + xmm6 = _mm_loadl_epi64((const __m128i*)(data+i-6)); + xmm6 = _mm_shuffle_epi32(xmm6, _MM_SHUFFLE(2,0,3,1)); + xmm6 = _mm_mul_epu32(xmm6, xmm2); + xmm7 = _mm_add_epi32(xmm7, xmm6); + + //sum += qlp_coeff[3] * data[i-4]; + //sum += qlp_coeff[2] * data[i-3]; + xmm6 = _mm_loadl_epi64((const __m128i*)(data+i-4)); + xmm6 = _mm_shuffle_epi32(xmm6, _MM_SHUFFLE(2,0,3,1)); + xmm6 = _mm_mul_epu32(xmm6, xmm1); + xmm7 = _mm_add_epi32(xmm7, xmm6); + + //sum += qlp_coeff[1] * data[i-2]; + //sum += qlp_coeff[0] * data[i-1]; + xmm6 = _mm_loadl_epi64((const __m128i*)(data+i-2)); + xmm6 = _mm_shuffle_epi32(xmm6, _MM_SHUFFLE(2,0,3,1)); + xmm6 = _mm_mul_epu32(xmm6, xmm0); + xmm7 = _mm_add_epi32(xmm7, xmm6); + + xmm7 = _mm_add_epi32(xmm7, _mm_srli_si128(xmm7, 8)); + RESIDUAL32_RESULT(xmm7); + } + } + else { /* order == 11 */ + __m128i xmm0, xmm1, xmm2, xmm3, xmm4, xmm5, xmm6, xmm7; + xmm0 = _mm_loadl_epi64((const __m128i*)(qlp_coeff+0)); + xmm1 = _mm_loadl_epi64((const __m128i*)(qlp_coeff+2)); + xmm2 = _mm_loadl_epi64((const __m128i*)(qlp_coeff+4)); + xmm3 = _mm_loadl_epi64((const __m128i*)(qlp_coeff+6)); + xmm4 = _mm_loadl_epi64((const __m128i*)(qlp_coeff+8)); + xmm5 = _mm_cvtsi32_si128(qlp_coeff[10]); + + xmm0 = _mm_shuffle_epi32(xmm0, _MM_SHUFFLE(3,1,2,0)); + xmm1 = _mm_shuffle_epi32(xmm1, _MM_SHUFFLE(3,1,2,0)); + xmm2 = _mm_shuffle_epi32(xmm2, _MM_SHUFFLE(3,1,2,0)); + xmm3 = _mm_shuffle_epi32(xmm3, _MM_SHUFFLE(3,1,2,0)); + xmm4 = _mm_shuffle_epi32(xmm4, _MM_SHUFFLE(3,1,2,0)); + + for(i = 0; i < (int)data_len; i++) { + //sum = 0; + //sum = qlp_coeff[10] * data[i-11]; + xmm7 = _mm_cvtsi32_si128(data[i-11]); + xmm7 = _mm_mul_epu32(xmm7, xmm5); + + //sum += qlp_coeff[9] * data[i-10]; + //sum += qlp_coeff[8] * data[i-9]; + xmm6 = _mm_loadl_epi64((const __m128i*)(data+i-10)); + xmm6 = _mm_shuffle_epi32(xmm6, _MM_SHUFFLE(2,0,3,1)); + xmm6 = _mm_mul_epu32(xmm6, xmm4); + xmm7 = _mm_add_epi32(xmm7, xmm6); + + //sum += qlp_coeff[7] * data[i-8]; + //sum += qlp_coeff[6] * data[i-7]; + xmm6 = _mm_loadl_epi64((const __m128i*)(data+i-8)); + xmm6 = _mm_shuffle_epi32(xmm6, _MM_SHUFFLE(2,0,3,1)); + xmm6 = _mm_mul_epu32(xmm6, xmm3); + xmm7 = _mm_add_epi32(xmm7, xmm6); + + //sum += qlp_coeff[5] * data[i-6]; + //sum += qlp_coeff[4] * data[i-5]; + xmm6 = _mm_loadl_epi64((const __m128i*)(data+i-6)); + xmm6 = _mm_shuffle_epi32(xmm6, _MM_SHUFFLE(2,0,3,1)); + xmm6 = _mm_mul_epu32(xmm6, xmm2); + xmm7 = _mm_add_epi32(xmm7, xmm6); + + //sum += qlp_coeff[3] * data[i-4]; + //sum += qlp_coeff[2] * data[i-3]; + xmm6 = _mm_loadl_epi64((const __m128i*)(data+i-4)); + xmm6 = _mm_shuffle_epi32(xmm6, _MM_SHUFFLE(2,0,3,1)); + xmm6 = _mm_mul_epu32(xmm6, xmm1); + xmm7 = _mm_add_epi32(xmm7, xmm6); + + //sum += qlp_coeff[1] * data[i-2]; + //sum += qlp_coeff[0] * data[i-1]; + xmm6 = _mm_loadl_epi64((const __m128i*)(data+i-2)); + xmm6 = _mm_shuffle_epi32(xmm6, _MM_SHUFFLE(2,0,3,1)); + xmm6 = _mm_mul_epu32(xmm6, xmm0); + xmm7 = _mm_add_epi32(xmm7, xmm6); + + xmm7 = _mm_add_epi32(xmm7, _mm_srli_si128(xmm7, 8)); + RESIDUAL32_RESULT(xmm7); + } + } + } + else { /* order == 9, 10 */ + if(order == 10) { + __m128i xmm0, xmm1, xmm2, xmm3, xmm4, xmm6, xmm7; + xmm0 = _mm_loadl_epi64((const __m128i*)(qlp_coeff+0)); + xmm1 = _mm_loadl_epi64((const __m128i*)(qlp_coeff+2)); + xmm2 = _mm_loadl_epi64((const __m128i*)(qlp_coeff+4)); + xmm3 = _mm_loadl_epi64((const __m128i*)(qlp_coeff+6)); + xmm4 = _mm_loadl_epi64((const __m128i*)(qlp_coeff+8)); + + xmm0 = _mm_shuffle_epi32(xmm0, _MM_SHUFFLE(3,1,2,0)); + xmm1 = _mm_shuffle_epi32(xmm1, _MM_SHUFFLE(3,1,2,0)); + xmm2 = _mm_shuffle_epi32(xmm2, _MM_SHUFFLE(3,1,2,0)); + xmm3 = _mm_shuffle_epi32(xmm3, _MM_SHUFFLE(3,1,2,0)); + xmm4 = _mm_shuffle_epi32(xmm4, _MM_SHUFFLE(3,1,2,0)); + + for(i = 0; i < (int)data_len; i++) { + //sum = 0; + //sum += qlp_coeff[9] * data[i-10]; + //sum += qlp_coeff[8] * data[i-9]; + xmm7 = _mm_loadl_epi64((const __m128i*)(data+i-10)); + xmm7 = _mm_shuffle_epi32(xmm7, _MM_SHUFFLE(2,0,3,1)); + xmm7 = _mm_mul_epu32(xmm7, xmm4); + + //sum += qlp_coeff[7] * data[i-8]; + //sum += qlp_coeff[6] * data[i-7]; + xmm6 = _mm_loadl_epi64((const __m128i*)(data+i-8)); + xmm6 = _mm_shuffle_epi32(xmm6, _MM_SHUFFLE(2,0,3,1)); + xmm6 = _mm_mul_epu32(xmm6, xmm3); + xmm7 = _mm_add_epi32(xmm7, xmm6); + + //sum += qlp_coeff[5] * data[i-6]; + //sum += qlp_coeff[4] * data[i-5]; + xmm6 = _mm_loadl_epi64((const __m128i*)(data+i-6)); + xmm6 = _mm_shuffle_epi32(xmm6, _MM_SHUFFLE(2,0,3,1)); + xmm6 = _mm_mul_epu32(xmm6, xmm2); + xmm7 = _mm_add_epi32(xmm7, xmm6); + + //sum += qlp_coeff[3] * data[i-4]; + //sum += qlp_coeff[2] * data[i-3]; + xmm6 = _mm_loadl_epi64((const __m128i*)(data+i-4)); + xmm6 = _mm_shuffle_epi32(xmm6, _MM_SHUFFLE(2,0,3,1)); + xmm6 = _mm_mul_epu32(xmm6, xmm1); + xmm7 = _mm_add_epi32(xmm7, xmm6); + + //sum += qlp_coeff[1] * data[i-2]; + //sum += qlp_coeff[0] * data[i-1]; + xmm6 = _mm_loadl_epi64((const __m128i*)(data+i-2)); + xmm6 = _mm_shuffle_epi32(xmm6, _MM_SHUFFLE(2,0,3,1)); + xmm6 = _mm_mul_epu32(xmm6, xmm0); + xmm7 = _mm_add_epi32(xmm7, xmm6); + + xmm7 = _mm_add_epi32(xmm7, _mm_srli_si128(xmm7, 8)); + RESIDUAL32_RESULT(xmm7); + } + } + else { /* order == 9 */ + __m128i xmm0, xmm1, xmm2, xmm3, xmm4, xmm6, xmm7; + xmm0 = _mm_loadl_epi64((const __m128i*)(qlp_coeff+0)); + xmm1 = _mm_loadl_epi64((const __m128i*)(qlp_coeff+2)); + xmm2 = _mm_loadl_epi64((const __m128i*)(qlp_coeff+4)); + xmm3 = _mm_loadl_epi64((const __m128i*)(qlp_coeff+6)); + xmm4 = _mm_cvtsi32_si128(qlp_coeff[8]); + + xmm0 = _mm_shuffle_epi32(xmm0, _MM_SHUFFLE(3,1,2,0)); + xmm1 = _mm_shuffle_epi32(xmm1, _MM_SHUFFLE(3,1,2,0)); + xmm2 = _mm_shuffle_epi32(xmm2, _MM_SHUFFLE(3,1,2,0)); + xmm3 = _mm_shuffle_epi32(xmm3, _MM_SHUFFLE(3,1,2,0)); + + for(i = 0; i < (int)data_len; i++) { + //sum = 0; + //sum = qlp_coeff[8] * data[i-9]; + xmm7 = _mm_cvtsi32_si128(data[i-9]); + xmm7 = _mm_mul_epu32(xmm7, xmm4); + + //sum += qlp_coeff[7] * data[i-8]; + //sum += qlp_coeff[6] * data[i-7]; + xmm6 = _mm_loadl_epi64((const __m128i*)(data+i-8)); + xmm6 = _mm_shuffle_epi32(xmm6, _MM_SHUFFLE(2,0,3,1)); + xmm6 = _mm_mul_epu32(xmm6, xmm3); + xmm7 = _mm_add_epi32(xmm7, xmm6); + + //sum += qlp_coeff[5] * data[i-6]; + //sum += qlp_coeff[4] * data[i-5]; + xmm6 = _mm_loadl_epi64((const __m128i*)(data+i-6)); + xmm6 = _mm_shuffle_epi32(xmm6, _MM_SHUFFLE(2,0,3,1)); + xmm6 = _mm_mul_epu32(xmm6, xmm2); + xmm7 = _mm_add_epi32(xmm7, xmm6); + + //sum += qlp_coeff[3] * data[i-4]; + //sum += qlp_coeff[2] * data[i-3]; + xmm6 = _mm_loadl_epi64((const __m128i*)(data+i-4)); + xmm6 = _mm_shuffle_epi32(xmm6, _MM_SHUFFLE(2,0,3,1)); + xmm6 = _mm_mul_epu32(xmm6, xmm1); + xmm7 = _mm_add_epi32(xmm7, xmm6); + + //sum += qlp_coeff[1] * data[i-2]; + //sum += qlp_coeff[0] * data[i-1]; + xmm6 = _mm_loadl_epi64((const __m128i*)(data+i-2)); + xmm6 = _mm_shuffle_epi32(xmm6, _MM_SHUFFLE(2,0,3,1)); + xmm6 = _mm_mul_epu32(xmm6, xmm0); + xmm7 = _mm_add_epi32(xmm7, xmm6); + + xmm7 = _mm_add_epi32(xmm7, _mm_srli_si128(xmm7, 8)); + RESIDUAL32_RESULT(xmm7); + } + } + } + } + else if(order > 4) { /* order == 5, 6, 7, 8 */ + if(order > 6) { /* order == 7, 8 */ + if(order == 8) { + __m128i xmm0, xmm1, xmm2, xmm3, xmm6, xmm7; + xmm0 = _mm_loadl_epi64((const __m128i*)(qlp_coeff+0)); + xmm1 = _mm_loadl_epi64((const __m128i*)(qlp_coeff+2)); + xmm2 = _mm_loadl_epi64((const __m128i*)(qlp_coeff+4)); + xmm3 = _mm_loadl_epi64((const __m128i*)(qlp_coeff+6)); + + xmm0 = _mm_shuffle_epi32(xmm0, _MM_SHUFFLE(3,1,2,0)); + xmm1 = _mm_shuffle_epi32(xmm1, _MM_SHUFFLE(3,1,2,0)); + xmm2 = _mm_shuffle_epi32(xmm2, _MM_SHUFFLE(3,1,2,0)); + xmm3 = _mm_shuffle_epi32(xmm3, _MM_SHUFFLE(3,1,2,0)); + + for(i = 0; i < (int)data_len; i++) { + //sum = 0; + //sum += qlp_coeff[7] * data[i-8]; + //sum += qlp_coeff[6] * data[i-7]; + xmm7 = _mm_loadl_epi64((const __m128i*)(data+i-8)); + xmm7 = _mm_shuffle_epi32(xmm7, _MM_SHUFFLE(2,0,3,1)); + xmm7 = _mm_mul_epu32(xmm7, xmm3); + + //sum += qlp_coeff[5] * data[i-6]; + //sum += qlp_coeff[4] * data[i-5]; + xmm6 = _mm_loadl_epi64((const __m128i*)(data+i-6)); + xmm6 = _mm_shuffle_epi32(xmm6, _MM_SHUFFLE(2,0,3,1)); + xmm6 = _mm_mul_epu32(xmm6, xmm2); + xmm7 = _mm_add_epi32(xmm7, xmm6); + + //sum += qlp_coeff[3] * data[i-4]; + //sum += qlp_coeff[2] * data[i-3]; + xmm6 = _mm_loadl_epi64((const __m128i*)(data+i-4)); + xmm6 = _mm_shuffle_epi32(xmm6, _MM_SHUFFLE(2,0,3,1)); + xmm6 = _mm_mul_epu32(xmm6, xmm1); + xmm7 = _mm_add_epi32(xmm7, xmm6); + + //sum += qlp_coeff[1] * data[i-2]; + //sum += qlp_coeff[0] * data[i-1]; + xmm6 = _mm_loadl_epi64((const __m128i*)(data+i-2)); + xmm6 = _mm_shuffle_epi32(xmm6, _MM_SHUFFLE(2,0,3,1)); + xmm6 = _mm_mul_epu32(xmm6, xmm0); + xmm7 = _mm_add_epi32(xmm7, xmm6); + + xmm7 = _mm_add_epi32(xmm7, _mm_srli_si128(xmm7, 8)); + RESIDUAL32_RESULT(xmm7); + } + } + else { /* order == 7 */ + __m128i xmm0, xmm1, xmm2, xmm3, xmm6, xmm7; + xmm0 = _mm_loadl_epi64((const __m128i*)(qlp_coeff+0)); + xmm1 = _mm_loadl_epi64((const __m128i*)(qlp_coeff+2)); + xmm2 = _mm_loadl_epi64((const __m128i*)(qlp_coeff+4)); + xmm3 = _mm_cvtsi32_si128(qlp_coeff[6]); + + xmm0 = _mm_shuffle_epi32(xmm0, _MM_SHUFFLE(3,1,2,0)); + xmm1 = _mm_shuffle_epi32(xmm1, _MM_SHUFFLE(3,1,2,0)); + xmm2 = _mm_shuffle_epi32(xmm2, _MM_SHUFFLE(3,1,2,0)); + + for(i = 0; i < (int)data_len; i++) { + //sum = 0; + //sum = qlp_coeff[6] * data[i-7]; + xmm7 = _mm_cvtsi32_si128(data[i-7]); + xmm7 = _mm_mul_epu32(xmm7, xmm3); + + //sum += qlp_coeff[5] * data[i-6]; + //sum += qlp_coeff[4] * data[i-5]; + xmm6 = _mm_loadl_epi64((const __m128i*)(data+i-6)); + xmm6 = _mm_shuffle_epi32(xmm6, _MM_SHUFFLE(2,0,3,1)); + xmm6 = _mm_mul_epu32(xmm6, xmm2); + xmm7 = _mm_add_epi32(xmm7, xmm6); + + //sum += qlp_coeff[3] * data[i-4]; + //sum += qlp_coeff[2] * data[i-3]; + xmm6 = _mm_loadl_epi64((const __m128i*)(data+i-4)); + xmm6 = _mm_shuffle_epi32(xmm6, _MM_SHUFFLE(2,0,3,1)); + xmm6 = _mm_mul_epu32(xmm6, xmm1); + xmm7 = _mm_add_epi32(xmm7, xmm6); + + //sum += qlp_coeff[1] * data[i-2]; + //sum += qlp_coeff[0] * data[i-1]; + xmm6 = _mm_loadl_epi64((const __m128i*)(data+i-2)); + xmm6 = _mm_shuffle_epi32(xmm6, _MM_SHUFFLE(2,0,3,1)); + xmm6 = _mm_mul_epu32(xmm6, xmm0); + xmm7 = _mm_add_epi32(xmm7, xmm6); + + xmm7 = _mm_add_epi32(xmm7, _mm_srli_si128(xmm7, 8)); + RESIDUAL32_RESULT(xmm7); + } + } + } + else { /* order == 5, 6 */ + if(order == 6) { + __m128i xmm0, xmm1, xmm2, xmm6, xmm7; + xmm0 = _mm_loadl_epi64((const __m128i*)(qlp_coeff+0)); + xmm1 = _mm_loadl_epi64((const __m128i*)(qlp_coeff+2)); + xmm2 = _mm_loadl_epi64((const __m128i*)(qlp_coeff+4)); + + xmm0 = _mm_shuffle_epi32(xmm0, _MM_SHUFFLE(3,1,2,0)); + xmm1 = _mm_shuffle_epi32(xmm1, _MM_SHUFFLE(3,1,2,0)); + xmm2 = _mm_shuffle_epi32(xmm2, _MM_SHUFFLE(3,1,2,0)); + + for(i = 0; i < (int)data_len; i++) { + //sum = 0; + //sum += qlp_coeff[5] * data[i-6]; + //sum += qlp_coeff[4] * data[i-5]; + xmm7 = _mm_loadl_epi64((const __m128i*)(data+i-6)); + xmm7 = _mm_shuffle_epi32(xmm7, _MM_SHUFFLE(2,0,3,1)); + xmm7 = _mm_mul_epu32(xmm7, xmm2); + + //sum += qlp_coeff[3] * data[i-4]; + //sum += qlp_coeff[2] * data[i-3]; + xmm6 = _mm_loadl_epi64((const __m128i*)(data+i-4)); + xmm6 = _mm_shuffle_epi32(xmm6, _MM_SHUFFLE(2,0,3,1)); + xmm6 = _mm_mul_epu32(xmm6, xmm1); + xmm7 = _mm_add_epi32(xmm7, xmm6); + + //sum += qlp_coeff[1] * data[i-2]; + //sum += qlp_coeff[0] * data[i-1]; + xmm6 = _mm_loadl_epi64((const __m128i*)(data+i-2)); + xmm6 = _mm_shuffle_epi32(xmm6, _MM_SHUFFLE(2,0,3,1)); + xmm6 = _mm_mul_epu32(xmm6, xmm0); + xmm7 = _mm_add_epi32(xmm7, xmm6); + + xmm7 = _mm_add_epi32(xmm7, _mm_srli_si128(xmm7, 8)); + RESIDUAL32_RESULT(xmm7); + } + } + else { /* order == 5 */ + __m128i xmm0, xmm1, xmm2, xmm6, xmm7; + xmm0 = _mm_loadl_epi64((const __m128i*)(qlp_coeff+0)); + xmm1 = _mm_loadl_epi64((const __m128i*)(qlp_coeff+2)); + xmm2 = _mm_cvtsi32_si128(qlp_coeff[4]); + + xmm0 = _mm_shuffle_epi32(xmm0, _MM_SHUFFLE(3,1,2,0)); + xmm1 = _mm_shuffle_epi32(xmm1, _MM_SHUFFLE(3,1,2,0)); + + for(i = 0; i < (int)data_len; i++) { + //sum = 0; + //sum = qlp_coeff[4] * data[i-5]; + xmm7 = _mm_cvtsi32_si128(data[i-5]); + xmm7 = _mm_mul_epu32(xmm7, xmm2); + + //sum += qlp_coeff[3] * data[i-4]; + //sum += qlp_coeff[2] * data[i-3]; + xmm6 = _mm_loadl_epi64((const __m128i*)(data+i-4)); + xmm6 = _mm_shuffle_epi32(xmm6, _MM_SHUFFLE(2,0,3,1)); + xmm6 = _mm_mul_epu32(xmm6, xmm1); + xmm7 = _mm_add_epi32(xmm7, xmm6); + + //sum += qlp_coeff[1] * data[i-2]; + //sum += qlp_coeff[0] * data[i-1]; + xmm6 = _mm_loadl_epi64((const __m128i*)(data+i-2)); + xmm6 = _mm_shuffle_epi32(xmm6, _MM_SHUFFLE(2,0,3,1)); + xmm6 = _mm_mul_epu32(xmm6, xmm0); + xmm7 = _mm_add_epi32(xmm7, xmm6); + + xmm7 = _mm_add_epi32(xmm7, _mm_srli_si128(xmm7, 8)); + RESIDUAL32_RESULT(xmm7); + } + } + } + } + else { /* order == 1, 2, 3, 4 */ + if(order > 2) { /* order == 3, 4 */ + if(order == 4) { + __m128i xmm0, xmm1, xmm6, xmm7; + xmm0 = _mm_loadl_epi64((const __m128i*)(qlp_coeff+0)); + xmm1 = _mm_loadl_epi64((const __m128i*)(qlp_coeff+2)); + + xmm0 = _mm_shuffle_epi32(xmm0, _MM_SHUFFLE(3,1,2,0)); + xmm1 = _mm_shuffle_epi32(xmm1, _MM_SHUFFLE(3,1,2,0)); + + for(i = 0; i < (int)data_len; i++) { + //sum = 0; + //sum += qlp_coeff[3] * data[i-4]; + //sum += qlp_coeff[2] * data[i-3]; + xmm7 = _mm_loadl_epi64((const __m128i*)(data+i-4)); + xmm7 = _mm_shuffle_epi32(xmm7, _MM_SHUFFLE(2,0,3,1)); + xmm7 = _mm_mul_epu32(xmm7, xmm1); + + //sum += qlp_coeff[1] * data[i-2]; + //sum += qlp_coeff[0] * data[i-1]; + xmm6 = _mm_loadl_epi64((const __m128i*)(data+i-2)); + xmm6 = _mm_shuffle_epi32(xmm6, _MM_SHUFFLE(2,0,3,1)); + xmm6 = _mm_mul_epu32(xmm6, xmm0); + xmm7 = _mm_add_epi32(xmm7, xmm6); + + xmm7 = _mm_add_epi32(xmm7, _mm_srli_si128(xmm7, 8)); + RESIDUAL32_RESULT(xmm7); + } + } + else { /* order == 3 */ + __m128i xmm0, xmm1, xmm6, xmm7; + xmm0 = _mm_loadl_epi64((const __m128i*)(qlp_coeff+0)); + xmm1 = _mm_cvtsi32_si128(qlp_coeff[2]); + + xmm0 = _mm_shuffle_epi32(xmm0, _MM_SHUFFLE(3,1,2,0)); + + for(i = 0; i < (int)data_len; i++) { + //sum = 0; + //sum = qlp_coeff[2] * data[i-3]; + xmm7 = _mm_cvtsi32_si128(data[i-3]); + xmm7 = _mm_mul_epu32(xmm7, xmm1); + + //sum += qlp_coeff[1] * data[i-2]; + //sum += qlp_coeff[0] * data[i-1]; + xmm6 = _mm_loadl_epi64((const __m128i*)(data+i-2)); + xmm6 = _mm_shuffle_epi32(xmm6, _MM_SHUFFLE(2,0,3,1)); + xmm6 = _mm_mul_epu32(xmm6, xmm0); + xmm7 = _mm_add_epi32(xmm7, xmm6); + + xmm7 = _mm_add_epi32(xmm7, _mm_srli_si128(xmm7, 8)); + RESIDUAL32_RESULT(xmm7); + } + } + } + else { /* order == 1, 2 */ + if(order == 2) { + __m128i xmm0, xmm7; + xmm0 = _mm_loadl_epi64((const __m128i*)(qlp_coeff+0)); + xmm0 = _mm_shuffle_epi32(xmm0, _MM_SHUFFLE(3,1,2,0)); + + for(i = 0; i < (int)data_len; i++) { + //sum = 0; + //sum += qlp_coeff[1] * data[i-2]; + //sum += qlp_coeff[0] * data[i-1]; + xmm7 = _mm_loadl_epi64((const __m128i*)(data+i-2)); + xmm7 = _mm_shuffle_epi32(xmm7, _MM_SHUFFLE(2,0,3,1)); + xmm7 = _mm_mul_epu32(xmm7, xmm0); + + xmm7 = _mm_add_epi32(xmm7, _mm_srli_si128(xmm7, 8)); + RESIDUAL32_RESULT(xmm7); + } + } + else { /* order == 1 */ + for(i = 0; i < (int)data_len; i++) + residual[i] = data[i] - ((qlp_coeff[0] * data[i-1]) >> lp_quantization); + } + } + } + } + else { /* order > 12 */ + FLAC__int32 sum; + for(i = 0; i < (int)data_len; i++) { + sum = 0; + switch(order) { + case 32: sum += qlp_coeff[31] * data[i-32]; + case 31: sum += qlp_coeff[30] * data[i-31]; + case 30: sum += qlp_coeff[29] * data[i-30]; + case 29: sum += qlp_coeff[28] * data[i-29]; + case 28: sum += qlp_coeff[27] * data[i-28]; + case 27: sum += qlp_coeff[26] * data[i-27]; + case 26: sum += qlp_coeff[25] * data[i-26]; + case 25: sum += qlp_coeff[24] * data[i-25]; + case 24: sum += qlp_coeff[23] * data[i-24]; + case 23: sum += qlp_coeff[22] * data[i-23]; + case 22: sum += qlp_coeff[21] * data[i-22]; + case 21: sum += qlp_coeff[20] * data[i-21]; + case 20: sum += qlp_coeff[19] * data[i-20]; + case 19: sum += qlp_coeff[18] * data[i-19]; + case 18: sum += qlp_coeff[17] * data[i-18]; + case 17: sum += qlp_coeff[16] * data[i-17]; + case 16: sum += qlp_coeff[15] * data[i-16]; + case 15: sum += qlp_coeff[14] * data[i-15]; + case 14: sum += qlp_coeff[13] * data[i-14]; + case 13: sum += qlp_coeff[12] * data[i-13]; + sum += qlp_coeff[11] * data[i-12]; + sum += qlp_coeff[10] * data[i-11]; + sum += qlp_coeff[ 9] * data[i-10]; + sum += qlp_coeff[ 8] * data[i- 9]; + sum += qlp_coeff[ 7] * data[i- 8]; + sum += qlp_coeff[ 6] * data[i- 7]; + sum += qlp_coeff[ 5] * data[i- 6]; + sum += qlp_coeff[ 4] * data[i- 5]; + sum += qlp_coeff[ 3] * data[i- 4]; + sum += qlp_coeff[ 2] * data[i- 3]; + sum += qlp_coeff[ 1] * data[i- 2]; + sum += qlp_coeff[ 0] * data[i- 1]; + } + residual[i] = data[i] - (sum >> lp_quantization); + } + } +} + +#if defined FLAC__CPU_IA32 && !defined FLAC__HAS_NASM /* unused for x64; not better than MMX asm */ + +FLAC__SSE_TARGET("sse2") +void FLAC__lpc_restore_signal_16_intrin_sse2(const FLAC__int32 residual[], unsigned data_len, const FLAC__int32 qlp_coeff[], unsigned order, int lp_quantization, FLAC__int32 data[]) +{ + if (order < 8 || order > 12) { + FLAC__lpc_restore_signal(residual, data_len, qlp_coeff, order, lp_quantization, data); + return; + } + if (data_len == 0) + return; + + FLAC__ASSERT(order >= 8); + FLAC__ASSERT(order <= 12); + + if(order > 8) { /* order == 9, 10, 11, 12 */ + FLAC__int32 curr; + __m128i xmm0, xmm1, xmm2, xmm3, xmm4, xmm5, xmm6, xmm7; + xmm0 = _mm_loadu_si128((const __m128i*)(qlp_coeff+0)); + xmm6 = _mm_loadu_si128((const __m128i*)(qlp_coeff+4)); + xmm1 = _mm_loadu_si128((const __m128i*)(qlp_coeff+8)); /* read 0 to 3 uninitialized coeffs... */ + switch(order) /* ...and zero them out */ + { + case 9: + xmm1 = _mm_slli_si128(xmm1, 12); xmm1 = _mm_srli_si128(xmm1, 12); break; + case 10: + xmm1 = _mm_slli_si128(xmm1, 8); xmm1 = _mm_srli_si128(xmm1, 8); break; + case 11: + xmm1 = _mm_slli_si128(xmm1, 4); xmm1 = _mm_srli_si128(xmm1, 4); break; + } + xmm2 = _mm_setzero_si128(); + xmm0 = _mm_packs_epi32(xmm0, xmm6); + xmm1 = _mm_packs_epi32(xmm1, xmm2); + + xmm4 = _mm_loadu_si128((const __m128i*)(data-12)); + xmm5 = _mm_loadu_si128((const __m128i*)(data-8)); + xmm3 = _mm_loadu_si128((const __m128i*)(data-4)); + xmm4 = _mm_shuffle_epi32(xmm4, _MM_SHUFFLE(0,1,2,3)); + xmm5 = _mm_shuffle_epi32(xmm5, _MM_SHUFFLE(0,1,2,3)); + xmm3 = _mm_shuffle_epi32(xmm3, _MM_SHUFFLE(0,1,2,3)); + xmm4 = _mm_packs_epi32(xmm4, xmm2); + xmm3 = _mm_packs_epi32(xmm3, xmm5); + + xmm7 = _mm_slli_si128(xmm1, 2); + xmm7 = _mm_or_si128(xmm7, _mm_srli_si128(xmm0, 14)); + xmm2 = _mm_slli_si128(xmm0, 2); + + /* xmm0, xmm1: qlp_coeff + xmm2, xmm7: qlp_coeff << 16 bit + xmm3, xmm4: data */ + + xmm5 = _mm_madd_epi16(xmm4, xmm1); + xmm6 = _mm_madd_epi16(xmm3, xmm0); + xmm6 = _mm_add_epi32(xmm6, xmm5); + xmm6 = _mm_add_epi32(xmm6, _mm_srli_si128(xmm6, 8)); + xmm6 = _mm_add_epi32(xmm6, _mm_srli_si128(xmm6, 4)); + + DATA16_RESULT(xmm6); + + data_len--; + + if(data_len % 2) { + xmm6 = _mm_srli_si128(xmm3, 14); + xmm4 = _mm_slli_si128(xmm4, 2); + xmm3 = _mm_slli_si128(xmm3, 2); + xmm4 = _mm_or_si128(xmm4, xmm6); + xmm3 = _mm_insert_epi16(xmm3, curr, 0); + + xmm5 = _mm_madd_epi16(xmm4, xmm1); + xmm6 = _mm_madd_epi16(xmm3, xmm0); + xmm6 = _mm_add_epi32(xmm6, xmm5); + xmm6 = _mm_add_epi32(xmm6, _mm_srli_si128(xmm6, 8)); + xmm6 = _mm_add_epi32(xmm6, _mm_srli_si128(xmm6, 4)); + + DATA16_RESULT(xmm6); + + data_len--; + } + + while(data_len) { /* data_len is a multiple of 2 */ + /* 1 _mm_slli_si128 per data element less but we need shifted qlp_coeff in xmm2:xmm7 */ + xmm6 = _mm_srli_si128(xmm3, 12); + xmm4 = _mm_slli_si128(xmm4, 4); + xmm3 = _mm_slli_si128(xmm3, 4); + xmm4 = _mm_or_si128(xmm4, xmm6); + xmm3 = _mm_insert_epi16(xmm3, curr, 1); + + xmm5 = _mm_madd_epi16(xmm4, xmm7); + xmm6 = _mm_madd_epi16(xmm3, xmm2); + xmm6 = _mm_add_epi32(xmm6, xmm5); + xmm6 = _mm_add_epi32(xmm6, _mm_srli_si128(xmm6, 8)); + xmm6 = _mm_add_epi32(xmm6, _mm_srli_si128(xmm6, 4)); + + DATA16_RESULT(xmm6); + + xmm3 = _mm_insert_epi16(xmm3, curr, 0); + + xmm5 = _mm_madd_epi16(xmm4, xmm1); + xmm6 = _mm_madd_epi16(xmm3, xmm0); + xmm6 = _mm_add_epi32(xmm6, xmm5); + xmm6 = _mm_add_epi32(xmm6, _mm_srli_si128(xmm6, 8)); + xmm6 = _mm_add_epi32(xmm6, _mm_srli_si128(xmm6, 4)); + + DATA16_RESULT(xmm6); + + data_len-=2; + } + } /* endif(order > 8) */ + else + { + FLAC__int32 curr; + __m128i xmm0, xmm1, xmm3, xmm6; + xmm0 = _mm_loadu_si128((const __m128i*)(qlp_coeff+0)); + xmm1 = _mm_loadu_si128((const __m128i*)(qlp_coeff+4)); + xmm0 = _mm_packs_epi32(xmm0, xmm1); + + xmm1 = _mm_loadu_si128((const __m128i*)(data-8)); + xmm3 = _mm_loadu_si128((const __m128i*)(data-4)); + xmm1 = _mm_shuffle_epi32(xmm1, _MM_SHUFFLE(0,1,2,3)); + xmm3 = _mm_shuffle_epi32(xmm3, _MM_SHUFFLE(0,1,2,3)); + xmm3 = _mm_packs_epi32(xmm3, xmm1); + + /* xmm0: qlp_coeff + xmm3: data */ + + xmm6 = _mm_madd_epi16(xmm3, xmm0); + xmm6 = _mm_add_epi32(xmm6, _mm_srli_si128(xmm6, 8)); + xmm6 = _mm_add_epi32(xmm6, _mm_srli_si128(xmm6, 4)); + + DATA16_RESULT(xmm6); + + data_len--; + + while(data_len) { + xmm3 = _mm_slli_si128(xmm3, 2); + xmm3 = _mm_insert_epi16(xmm3, curr, 0); + + xmm6 = _mm_madd_epi16(xmm3, xmm0); + xmm6 = _mm_add_epi32(xmm6, _mm_srli_si128(xmm6, 8)); + xmm6 = _mm_add_epi32(xmm6, _mm_srli_si128(xmm6, 4)); + + DATA16_RESULT(xmm6); + + data_len--; + } + } +} + +#endif /* defined FLAC__CPU_IA32 && !defined FLAC__HAS_NASM */ + +#endif /* FLAC__SSE2_SUPPORTED */ +#endif /* (FLAC__CPU_IA32 || FLAC__CPU_X86_64) && FLAC__HAS_X86INTRIN */ +#endif /* FLAC__NO_ASM */ +#endif /* FLAC__INTEGER_ONLY_LIBRARY */ diff --git a/core/deps/flac/src/libFLAC/lpc_intrin_sse41.c b/core/deps/flac/src/libFLAC/lpc_intrin_sse41.c new file mode 100644 index 000000000..bef73f41f --- /dev/null +++ b/core/deps/flac/src/libFLAC/lpc_intrin_sse41.c @@ -0,0 +1,1314 @@ +/* libFLAC - Free Lossless Audio Codec library + * Copyright (C) 2000-2009 Josh Coalson + * Copyright (C) 2011-2016 Xiph.Org Foundation + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * - Neither the name of the Xiph.org Foundation nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifdef HAVE_CONFIG_H +# include +#endif + +#include "private/cpu.h" + +#ifndef FLAC__INTEGER_ONLY_LIBRARY +#ifndef FLAC__NO_ASM +#if (defined FLAC__CPU_IA32 || defined FLAC__CPU_X86_64) && FLAC__HAS_X86INTRIN +#include "private/lpc.h" +#ifdef FLAC__SSE4_1_SUPPORTED + +#include "FLAC/assert.h" +#include "FLAC/format.h" + +#include /* SSE4.1 */ + +#if defined FLAC__CPU_IA32 /* unused for x64 */ + +#define RESIDUAL64_RESULT(xmmN) residual[i] = data[i] - _mm_cvtsi128_si32(_mm_srl_epi64(xmmN, cnt)) +#define RESIDUAL64_RESULT1(xmmN) residual[i] = data[i] - _mm_cvtsi128_si32(_mm_srli_epi64(xmmN, lp_quantization)) + +FLAC__SSE_TARGET("sse4.1") +void FLAC__lpc_compute_residual_from_qlp_coefficients_wide_intrin_sse41(const FLAC__int32 *data, unsigned data_len, const FLAC__int32 qlp_coeff[], unsigned order, int lp_quantization, FLAC__int32 residual[]) +{ + int i; + __m128i cnt = _mm_cvtsi32_si128(lp_quantization); + + FLAC__ASSERT(order > 0); + FLAC__ASSERT(order <= 32); + FLAC__ASSERT(lp_quantization <= 32); /* there's no _mm_sra_epi64() so we have to use _mm_srl_epi64() */ + + if(order <= 12) { + if(order > 8) { /* order == 9, 10, 11, 12 */ + if(order > 10) { /* order == 11, 12 */ + if(order == 12) { + __m128i xmm0, xmm1, xmm2, xmm3, xmm4, xmm5, xmm6, xmm7; + xmm0 = _mm_loadl_epi64((const __m128i*)(qlp_coeff+0)); // 0 0 q[1] q[0] + xmm1 = _mm_loadl_epi64((const __m128i*)(qlp_coeff+2)); // 0 0 q[3] q[2] + xmm2 = _mm_loadl_epi64((const __m128i*)(qlp_coeff+4)); // 0 0 q[5] q[4] + xmm3 = _mm_loadl_epi64((const __m128i*)(qlp_coeff+6)); // 0 0 q[7] q[6] + xmm4 = _mm_loadl_epi64((const __m128i*)(qlp_coeff+8)); // 0 0 q[9] q[8] + xmm5 = _mm_loadl_epi64((const __m128i*)(qlp_coeff+10)); // 0 0 q[11] q[10] + + xmm0 = _mm_shuffle_epi32(xmm0, _MM_SHUFFLE(3,1,2,0)); // 0 q[1] 0 q[0] + xmm1 = _mm_shuffle_epi32(xmm1, _MM_SHUFFLE(3,1,2,0)); // 0 q[3] 0 q[2] + xmm2 = _mm_shuffle_epi32(xmm2, _MM_SHUFFLE(3,1,2,0)); // 0 q[5] 0 q[4] + xmm3 = _mm_shuffle_epi32(xmm3, _MM_SHUFFLE(3,1,2,0)); // 0 q[7] 0 q[6] + xmm4 = _mm_shuffle_epi32(xmm4, _MM_SHUFFLE(3,1,2,0)); // 0 q[9] 0 q[8] + xmm5 = _mm_shuffle_epi32(xmm5, _MM_SHUFFLE(3,1,2,0)); // 0 q[11] 0 q[10] + + for(i = 0; i < (int)data_len; i++) { + //sum = 0; + //sum += qlp_coeff[11] * (FLAC__int64)data[i-12]; + //sum += qlp_coeff[10] * (FLAC__int64)data[i-11]; + xmm7 = _mm_loadl_epi64((const __m128i*)(data+i-12)); // 0 0 d[i-11] d[i-12] + xmm7 = _mm_shuffle_epi32(xmm7, _MM_SHUFFLE(2,0,3,1)); // 0 d[i-12] 0 d[i-11] + xmm7 = _mm_mul_epi32(xmm7, xmm5); + + //sum += qlp_coeff[9] * (FLAC__int64)data[i-10]; + //sum += qlp_coeff[8] * (FLAC__int64)data[i-9]; + xmm6 = _mm_loadl_epi64((const __m128i*)(data+i-10)); + xmm6 = _mm_shuffle_epi32(xmm6, _MM_SHUFFLE(2,0,3,1)); + xmm6 = _mm_mul_epi32(xmm6, xmm4); + xmm7 = _mm_add_epi64(xmm7, xmm6); + + //sum += qlp_coeff[7] * (FLAC__int64)data[i-8]; + //sum += qlp_coeff[6] * (FLAC__int64)data[i-7]; + xmm6 = _mm_loadl_epi64((const __m128i*)(data+i-8)); + xmm6 = _mm_shuffle_epi32(xmm6, _MM_SHUFFLE(2,0,3,1)); + xmm6 = _mm_mul_epi32(xmm6, xmm3); + xmm7 = _mm_add_epi64(xmm7, xmm6); + + //sum += qlp_coeff[5] * (FLAC__int64)data[i-6]; + //sum += qlp_coeff[4] * (FLAC__int64)data[i-5]; + xmm6 = _mm_loadl_epi64((const __m128i*)(data+i-6)); + xmm6 = _mm_shuffle_epi32(xmm6, _MM_SHUFFLE(2,0,3,1)); + xmm6 = _mm_mul_epi32(xmm6, xmm2); + xmm7 = _mm_add_epi64(xmm7, xmm6); + + //sum += qlp_coeff[3] * (FLAC__int64)data[i-4]; + //sum += qlp_coeff[2] * (FLAC__int64)data[i-3]; + xmm6 = _mm_loadl_epi64((const __m128i*)(data+i-4)); + xmm6 = _mm_shuffle_epi32(xmm6, _MM_SHUFFLE(2,0,3,1)); + xmm6 = _mm_mul_epi32(xmm6, xmm1); + xmm7 = _mm_add_epi64(xmm7, xmm6); + + //sum += qlp_coeff[1] * (FLAC__int64)data[i-2]; + //sum += qlp_coeff[0] * (FLAC__int64)data[i-1]; + xmm6 = _mm_loadl_epi64((const __m128i*)(data+i-2)); + xmm6 = _mm_shuffle_epi32(xmm6, _MM_SHUFFLE(2,0,3,1)); + xmm6 = _mm_mul_epi32(xmm6, xmm0); + xmm7 = _mm_add_epi64(xmm7, xmm6); + + xmm7 = _mm_add_epi64(xmm7, _mm_srli_si128(xmm7, 8)); + RESIDUAL64_RESULT1(xmm7); + } + } + else { /* order == 11 */ + __m128i xmm0, xmm1, xmm2, xmm3, xmm4, xmm5, xmm6, xmm7; + xmm0 = _mm_loadl_epi64((const __m128i*)(qlp_coeff+0)); + xmm1 = _mm_loadl_epi64((const __m128i*)(qlp_coeff+2)); + xmm2 = _mm_loadl_epi64((const __m128i*)(qlp_coeff+4)); + xmm3 = _mm_loadl_epi64((const __m128i*)(qlp_coeff+6)); + xmm4 = _mm_loadl_epi64((const __m128i*)(qlp_coeff+8)); + xmm5 = _mm_cvtsi32_si128(qlp_coeff[10]); + + xmm0 = _mm_shuffle_epi32(xmm0, _MM_SHUFFLE(3,1,2,0)); + xmm1 = _mm_shuffle_epi32(xmm1, _MM_SHUFFLE(3,1,2,0)); + xmm2 = _mm_shuffle_epi32(xmm2, _MM_SHUFFLE(3,1,2,0)); + xmm3 = _mm_shuffle_epi32(xmm3, _MM_SHUFFLE(3,1,2,0)); + xmm4 = _mm_shuffle_epi32(xmm4, _MM_SHUFFLE(3,1,2,0)); + + for(i = 0; i < (int)data_len; i++) { + //sum = 0; + //sum = qlp_coeff[10] * (FLAC__int64)data[i-11]; + xmm7 = _mm_cvtsi32_si128(data[i-11]); + xmm7 = _mm_mul_epi32(xmm7, xmm5); + + //sum += qlp_coeff[9] * (FLAC__int64)data[i-10]; + //sum += qlp_coeff[8] * (FLAC__int64)data[i-9]; + xmm6 = _mm_loadl_epi64((const __m128i*)(data+i-10)); + xmm6 = _mm_shuffle_epi32(xmm6, _MM_SHUFFLE(2,0,3,1)); + xmm6 = _mm_mul_epi32(xmm6, xmm4); + xmm7 = _mm_add_epi64(xmm7, xmm6); + + //sum += qlp_coeff[7] * (FLAC__int64)data[i-8]; + //sum += qlp_coeff[6] * (FLAC__int64)data[i-7]; + xmm6 = _mm_loadl_epi64((const __m128i*)(data+i-8)); + xmm6 = _mm_shuffle_epi32(xmm6, _MM_SHUFFLE(2,0,3,1)); + xmm6 = _mm_mul_epi32(xmm6, xmm3); + xmm7 = _mm_add_epi64(xmm7, xmm6); + + //sum += qlp_coeff[5] * (FLAC__int64)data[i-6]; + //sum += qlp_coeff[4] * (FLAC__int64)data[i-5]; + xmm6 = _mm_loadl_epi64((const __m128i*)(data+i-6)); + xmm6 = _mm_shuffle_epi32(xmm6, _MM_SHUFFLE(2,0,3,1)); + xmm6 = _mm_mul_epi32(xmm6, xmm2); + xmm7 = _mm_add_epi64(xmm7, xmm6); + + //sum += qlp_coeff[3] * (FLAC__int64)data[i-4]; + //sum += qlp_coeff[2] * (FLAC__int64)data[i-3]; + xmm6 = _mm_loadl_epi64((const __m128i*)(data+i-4)); + xmm6 = _mm_shuffle_epi32(xmm6, _MM_SHUFFLE(2,0,3,1)); + xmm6 = _mm_mul_epi32(xmm6, xmm1); + xmm7 = _mm_add_epi64(xmm7, xmm6); + + //sum += qlp_coeff[1] * (FLAC__int64)data[i-2]; + //sum += qlp_coeff[0] * (FLAC__int64)data[i-1]; + xmm6 = _mm_loadl_epi64((const __m128i*)(data+i-2)); + xmm6 = _mm_shuffle_epi32(xmm6, _MM_SHUFFLE(2,0,3,1)); + xmm6 = _mm_mul_epi32(xmm6, xmm0); + xmm7 = _mm_add_epi64(xmm7, xmm6); + + xmm7 = _mm_add_epi64(xmm7, _mm_srli_si128(xmm7, 8)); + RESIDUAL64_RESULT1(xmm7); + } + } + } + else { /* order == 9, 10 */ + if(order == 10) { + __m128i xmm0, xmm1, xmm2, xmm3, xmm4, xmm6, xmm7; + xmm0 = _mm_loadl_epi64((const __m128i*)(qlp_coeff+0)); + xmm1 = _mm_loadl_epi64((const __m128i*)(qlp_coeff+2)); + xmm2 = _mm_loadl_epi64((const __m128i*)(qlp_coeff+4)); + xmm3 = _mm_loadl_epi64((const __m128i*)(qlp_coeff+6)); + xmm4 = _mm_loadl_epi64((const __m128i*)(qlp_coeff+8)); + + xmm0 = _mm_shuffle_epi32(xmm0, _MM_SHUFFLE(3,1,2,0)); + xmm1 = _mm_shuffle_epi32(xmm1, _MM_SHUFFLE(3,1,2,0)); + xmm2 = _mm_shuffle_epi32(xmm2, _MM_SHUFFLE(3,1,2,0)); + xmm3 = _mm_shuffle_epi32(xmm3, _MM_SHUFFLE(3,1,2,0)); + xmm4 = _mm_shuffle_epi32(xmm4, _MM_SHUFFLE(3,1,2,0)); + + for(i = 0; i < (int)data_len; i++) { + //sum = 0; + //sum += qlp_coeff[9] * (FLAC__int64)data[i-10]; + //sum += qlp_coeff[8] * (FLAC__int64)data[i-9]; + xmm7 = _mm_loadl_epi64((const __m128i*)(data+i-10)); + xmm7 = _mm_shuffle_epi32(xmm7, _MM_SHUFFLE(2,0,3,1)); + xmm7 = _mm_mul_epi32(xmm7, xmm4); + + //sum += qlp_coeff[7] * (FLAC__int64)data[i-8]; + //sum += qlp_coeff[6] * (FLAC__int64)data[i-7]; + xmm6 = _mm_loadl_epi64((const __m128i*)(data+i-8)); + xmm6 = _mm_shuffle_epi32(xmm6, _MM_SHUFFLE(2,0,3,1)); + xmm6 = _mm_mul_epi32(xmm6, xmm3); + xmm7 = _mm_add_epi64(xmm7, xmm6); + + //sum += qlp_coeff[5] * (FLAC__int64)data[i-6]; + //sum += qlp_coeff[4] * (FLAC__int64)data[i-5]; + xmm6 = _mm_loadl_epi64((const __m128i*)(data+i-6)); + xmm6 = _mm_shuffle_epi32(xmm6, _MM_SHUFFLE(2,0,3,1)); + xmm6 = _mm_mul_epi32(xmm6, xmm2); + xmm7 = _mm_add_epi64(xmm7, xmm6); + + //sum += qlp_coeff[3] * (FLAC__int64)data[i-4]; + //sum += qlp_coeff[2] * (FLAC__int64)data[i-3]; + xmm6 = _mm_loadl_epi64((const __m128i*)(data+i-4)); + xmm6 = _mm_shuffle_epi32(xmm6, _MM_SHUFFLE(2,0,3,1)); + xmm6 = _mm_mul_epi32(xmm6, xmm1); + xmm7 = _mm_add_epi64(xmm7, xmm6); + + //sum += qlp_coeff[1] * (FLAC__int64)data[i-2]; + //sum += qlp_coeff[0] * (FLAC__int64)data[i-1]; + xmm6 = _mm_loadl_epi64((const __m128i*)(data+i-2)); + xmm6 = _mm_shuffle_epi32(xmm6, _MM_SHUFFLE(2,0,3,1)); + xmm6 = _mm_mul_epi32(xmm6, xmm0); + xmm7 = _mm_add_epi64(xmm7, xmm6); + + xmm7 = _mm_add_epi64(xmm7, _mm_srli_si128(xmm7, 8)); + RESIDUAL64_RESULT(xmm7); + } + } + else { /* order == 9 */ + __m128i xmm0, xmm1, xmm2, xmm3, xmm4, xmm6, xmm7; + xmm0 = _mm_loadl_epi64((const __m128i*)(qlp_coeff+0)); + xmm1 = _mm_loadl_epi64((const __m128i*)(qlp_coeff+2)); + xmm2 = _mm_loadl_epi64((const __m128i*)(qlp_coeff+4)); + xmm3 = _mm_loadl_epi64((const __m128i*)(qlp_coeff+6)); + xmm4 = _mm_cvtsi32_si128(qlp_coeff[8]); + + xmm0 = _mm_shuffle_epi32(xmm0, _MM_SHUFFLE(3,1,2,0)); + xmm1 = _mm_shuffle_epi32(xmm1, _MM_SHUFFLE(3,1,2,0)); + xmm2 = _mm_shuffle_epi32(xmm2, _MM_SHUFFLE(3,1,2,0)); + xmm3 = _mm_shuffle_epi32(xmm3, _MM_SHUFFLE(3,1,2,0)); + + for(i = 0; i < (int)data_len; i++) { + //sum = 0; + //sum = qlp_coeff[8] * (FLAC__int64)data[i-9]; + xmm7 = _mm_cvtsi32_si128(data[i-9]); + xmm7 = _mm_mul_epi32(xmm7, xmm4); + + //sum += qlp_coeff[7] * (FLAC__int64)data[i-8]; + //sum += qlp_coeff[6] * (FLAC__int64)data[i-7]; + xmm6 = _mm_loadl_epi64((const __m128i*)(data+i-8)); + xmm6 = _mm_shuffle_epi32(xmm6, _MM_SHUFFLE(2,0,3,1)); + xmm6 = _mm_mul_epi32(xmm6, xmm3); + xmm7 = _mm_add_epi64(xmm7, xmm6); + + //sum += qlp_coeff[5] * (FLAC__int64)data[i-6]; + //sum += qlp_coeff[4] * (FLAC__int64)data[i-5]; + xmm6 = _mm_loadl_epi64((const __m128i*)(data+i-6)); + xmm6 = _mm_shuffle_epi32(xmm6, _MM_SHUFFLE(2,0,3,1)); + xmm6 = _mm_mul_epi32(xmm6, xmm2); + xmm7 = _mm_add_epi64(xmm7, xmm6); + + //sum += qlp_coeff[3] * (FLAC__int64)data[i-4]; + //sum += qlp_coeff[2] * (FLAC__int64)data[i-3]; + xmm6 = _mm_loadl_epi64((const __m128i*)(data+i-4)); + xmm6 = _mm_shuffle_epi32(xmm6, _MM_SHUFFLE(2,0,3,1)); + xmm6 = _mm_mul_epi32(xmm6, xmm1); + xmm7 = _mm_add_epi64(xmm7, xmm6); + + //sum += qlp_coeff[1] * (FLAC__int64)data[i-2]; + //sum += qlp_coeff[0] * (FLAC__int64)data[i-1]; + xmm6 = _mm_loadl_epi64((const __m128i*)(data+i-2)); + xmm6 = _mm_shuffle_epi32(xmm6, _MM_SHUFFLE(2,0,3,1)); + xmm6 = _mm_mul_epi32(xmm6, xmm0); + xmm7 = _mm_add_epi64(xmm7, xmm6); + + xmm7 = _mm_add_epi64(xmm7, _mm_srli_si128(xmm7, 8)); + RESIDUAL64_RESULT(xmm7); + } + } + } + } + else if(order > 4) { /* order == 5, 6, 7, 8 */ + if(order > 6) { /* order == 7, 8 */ + if(order == 8) { + __m128i xmm0, xmm1, xmm2, xmm3, xmm6, xmm7; + xmm0 = _mm_loadl_epi64((const __m128i*)(qlp_coeff+0)); + xmm1 = _mm_loadl_epi64((const __m128i*)(qlp_coeff+2)); + xmm2 = _mm_loadl_epi64((const __m128i*)(qlp_coeff+4)); + xmm3 = _mm_loadl_epi64((const __m128i*)(qlp_coeff+6)); + + xmm0 = _mm_shuffle_epi32(xmm0, _MM_SHUFFLE(3,1,2,0)); + xmm1 = _mm_shuffle_epi32(xmm1, _MM_SHUFFLE(3,1,2,0)); + xmm2 = _mm_shuffle_epi32(xmm2, _MM_SHUFFLE(3,1,2,0)); + xmm3 = _mm_shuffle_epi32(xmm3, _MM_SHUFFLE(3,1,2,0)); + + for(i = 0; i < (int)data_len; i++) { + //sum = 0; + //sum += qlp_coeff[7] * (FLAC__int64)data[i-8]; + //sum += qlp_coeff[6] * (FLAC__int64)data[i-7]; + xmm7 = _mm_loadl_epi64((const __m128i*)(data+i-8)); + xmm7 = _mm_shuffle_epi32(xmm7, _MM_SHUFFLE(2,0,3,1)); + xmm7 = _mm_mul_epi32(xmm7, xmm3); + + //sum += qlp_coeff[5] * (FLAC__int64)data[i-6]; + //sum += qlp_coeff[4] * (FLAC__int64)data[i-5]; + xmm6 = _mm_loadl_epi64((const __m128i*)(data+i-6)); + xmm6 = _mm_shuffle_epi32(xmm6, _MM_SHUFFLE(2,0,3,1)); + xmm6 = _mm_mul_epi32(xmm6, xmm2); + xmm7 = _mm_add_epi64(xmm7, xmm6); + + //sum += qlp_coeff[3] * (FLAC__int64)data[i-4]; + //sum += qlp_coeff[2] * (FLAC__int64)data[i-3]; + xmm6 = _mm_loadl_epi64((const __m128i*)(data+i-4)); + xmm6 = _mm_shuffle_epi32(xmm6, _MM_SHUFFLE(2,0,3,1)); + xmm6 = _mm_mul_epi32(xmm6, xmm1); + xmm7 = _mm_add_epi64(xmm7, xmm6); + + //sum += qlp_coeff[1] * (FLAC__int64)data[i-2]; + //sum += qlp_coeff[0] * (FLAC__int64)data[i-1]; + xmm6 = _mm_loadl_epi64((const __m128i*)(data+i-2)); + xmm6 = _mm_shuffle_epi32(xmm6, _MM_SHUFFLE(2,0,3,1)); + xmm6 = _mm_mul_epi32(xmm6, xmm0); + xmm7 = _mm_add_epi64(xmm7, xmm6); + + xmm7 = _mm_add_epi64(xmm7, _mm_srli_si128(xmm7, 8)); + RESIDUAL64_RESULT(xmm7); + } + } + else { /* order == 7 */ + __m128i xmm0, xmm1, xmm2, xmm3, xmm6, xmm7; + xmm0 = _mm_loadl_epi64((const __m128i*)(qlp_coeff+0)); + xmm1 = _mm_loadl_epi64((const __m128i*)(qlp_coeff+2)); + xmm2 = _mm_loadl_epi64((const __m128i*)(qlp_coeff+4)); + xmm3 = _mm_cvtsi32_si128(qlp_coeff[6]); + + xmm0 = _mm_shuffle_epi32(xmm0, _MM_SHUFFLE(3,1,2,0)); + xmm1 = _mm_shuffle_epi32(xmm1, _MM_SHUFFLE(3,1,2,0)); + xmm2 = _mm_shuffle_epi32(xmm2, _MM_SHUFFLE(3,1,2,0)); + + for(i = 0; i < (int)data_len; i++) { + //sum = 0; + //sum = qlp_coeff[6] * (FLAC__int64)data[i-7]; + xmm7 = _mm_cvtsi32_si128(data[i-7]); + xmm7 = _mm_mul_epi32(xmm7, xmm3); + + //sum += qlp_coeff[5] * (FLAC__int64)data[i-6]; + //sum += qlp_coeff[4] * (FLAC__int64)data[i-5]; + xmm6 = _mm_loadl_epi64((const __m128i*)(data+i-6)); + xmm6 = _mm_shuffle_epi32(xmm6, _MM_SHUFFLE(2,0,3,1)); + xmm6 = _mm_mul_epi32(xmm6, xmm2); + xmm7 = _mm_add_epi64(xmm7, xmm6); + + //sum += qlp_coeff[3] * (FLAC__int64)data[i-4]; + //sum += qlp_coeff[2] * (FLAC__int64)data[i-3]; + xmm6 = _mm_loadl_epi64((const __m128i*)(data+i-4)); + xmm6 = _mm_shuffle_epi32(xmm6, _MM_SHUFFLE(2,0,3,1)); + xmm6 = _mm_mul_epi32(xmm6, xmm1); + xmm7 = _mm_add_epi64(xmm7, xmm6); + + //sum += qlp_coeff[1] * (FLAC__int64)data[i-2]; + //sum += qlp_coeff[0] * (FLAC__int64)data[i-1]; + xmm6 = _mm_loadl_epi64((const __m128i*)(data+i-2)); + xmm6 = _mm_shuffle_epi32(xmm6, _MM_SHUFFLE(2,0,3,1)); + xmm6 = _mm_mul_epi32(xmm6, xmm0); + xmm7 = _mm_add_epi64(xmm7, xmm6); + + xmm7 = _mm_add_epi64(xmm7, _mm_srli_si128(xmm7, 8)); + RESIDUAL64_RESULT(xmm7); + } + } + } + else { /* order == 5, 6 */ + if(order == 6) { + __m128i xmm0, xmm1, xmm2, xmm6, xmm7; + xmm0 = _mm_loadl_epi64((const __m128i*)(qlp_coeff+0)); + xmm1 = _mm_loadl_epi64((const __m128i*)(qlp_coeff+2)); + xmm2 = _mm_loadl_epi64((const __m128i*)(qlp_coeff+4)); + + xmm0 = _mm_shuffle_epi32(xmm0, _MM_SHUFFLE(3,1,2,0)); + xmm1 = _mm_shuffle_epi32(xmm1, _MM_SHUFFLE(3,1,2,0)); + xmm2 = _mm_shuffle_epi32(xmm2, _MM_SHUFFLE(3,1,2,0)); + + for(i = 0; i < (int)data_len; i++) { + //sum = 0; + //sum += qlp_coeff[5] * (FLAC__int64)data[i-6]; + //sum += qlp_coeff[4] * (FLAC__int64)data[i-5]; + xmm7 = _mm_loadl_epi64((const __m128i*)(data+i-6)); + xmm7 = _mm_shuffle_epi32(xmm7, _MM_SHUFFLE(2,0,3,1)); + xmm7 = _mm_mul_epi32(xmm7, xmm2); + + //sum += qlp_coeff[3] * (FLAC__int64)data[i-4]; + //sum += qlp_coeff[2] * (FLAC__int64)data[i-3]; + xmm6 = _mm_loadl_epi64((const __m128i*)(data+i-4)); + xmm6 = _mm_shuffle_epi32(xmm6, _MM_SHUFFLE(2,0,3,1)); + xmm6 = _mm_mul_epi32(xmm6, xmm1); + xmm7 = _mm_add_epi64(xmm7, xmm6); + + //sum += qlp_coeff[1] * (FLAC__int64)data[i-2]; + //sum += qlp_coeff[0] * (FLAC__int64)data[i-1]; + xmm6 = _mm_loadl_epi64((const __m128i*)(data+i-2)); + xmm6 = _mm_shuffle_epi32(xmm6, _MM_SHUFFLE(2,0,3,1)); + xmm6 = _mm_mul_epi32(xmm6, xmm0); + xmm7 = _mm_add_epi64(xmm7, xmm6); + + xmm7 = _mm_add_epi64(xmm7, _mm_srli_si128(xmm7, 8)); + RESIDUAL64_RESULT(xmm7); + } + } + else { /* order == 5 */ + __m128i xmm0, xmm1, xmm2, xmm6, xmm7; + xmm0 = _mm_loadl_epi64((const __m128i*)(qlp_coeff+0)); + xmm1 = _mm_loadl_epi64((const __m128i*)(qlp_coeff+2)); + xmm2 = _mm_cvtsi32_si128(qlp_coeff[4]); + + xmm0 = _mm_shuffle_epi32(xmm0, _MM_SHUFFLE(3,1,2,0)); + xmm1 = _mm_shuffle_epi32(xmm1, _MM_SHUFFLE(3,1,2,0)); + + for(i = 0; i < (int)data_len; i++) { + //sum = 0; + //sum = qlp_coeff[4] * (FLAC__int64)data[i-5]; + xmm7 = _mm_cvtsi32_si128(data[i-5]); + xmm7 = _mm_mul_epi32(xmm7, xmm2); + + //sum += qlp_coeff[3] * (FLAC__int64)data[i-4]; + //sum += qlp_coeff[2] * (FLAC__int64)data[i-3]; + xmm6 = _mm_loadl_epi64((const __m128i*)(data+i-4)); + xmm6 = _mm_shuffle_epi32(xmm6, _MM_SHUFFLE(2,0,3,1)); + xmm6 = _mm_mul_epi32(xmm6, xmm1); + xmm7 = _mm_add_epi64(xmm7, xmm6); + + //sum += qlp_coeff[1] * (FLAC__int64)data[i-2]; + //sum += qlp_coeff[0] * (FLAC__int64)data[i-1]; + xmm6 = _mm_loadl_epi64((const __m128i*)(data+i-2)); + xmm6 = _mm_shuffle_epi32(xmm6, _MM_SHUFFLE(2,0,3,1)); + xmm6 = _mm_mul_epi32(xmm6, xmm0); + xmm7 = _mm_add_epi64(xmm7, xmm6); + + xmm7 = _mm_add_epi64(xmm7, _mm_srli_si128(xmm7, 8)); + RESIDUAL64_RESULT(xmm7); + } + } + } + } + else { /* order == 1, 2, 3, 4 */ + if(order > 2) { /* order == 3, 4 */ + if(order == 4) { + __m128i xmm0, xmm1, xmm6, xmm7; + xmm0 = _mm_loadl_epi64((const __m128i*)(qlp_coeff+0)); + xmm1 = _mm_loadl_epi64((const __m128i*)(qlp_coeff+2)); + + xmm0 = _mm_shuffle_epi32(xmm0, _MM_SHUFFLE(3,1,2,0)); + xmm1 = _mm_shuffle_epi32(xmm1, _MM_SHUFFLE(3,1,2,0)); + + for(i = 0; i < (int)data_len; i++) { + //sum = 0; + //sum += qlp_coeff[3] * (FLAC__int64)data[i-4]; + //sum += qlp_coeff[2] * (FLAC__int64)data[i-3]; + xmm7 = _mm_loadl_epi64((const __m128i*)(data+i-4)); + xmm7 = _mm_shuffle_epi32(xmm7, _MM_SHUFFLE(2,0,3,1)); + xmm7 = _mm_mul_epi32(xmm7, xmm1); + + //sum += qlp_coeff[1] * (FLAC__int64)data[i-2]; + //sum += qlp_coeff[0] * (FLAC__int64)data[i-1]; + xmm6 = _mm_loadl_epi64((const __m128i*)(data+i-2)); + xmm6 = _mm_shuffle_epi32(xmm6, _MM_SHUFFLE(2,0,3,1)); + xmm6 = _mm_mul_epi32(xmm6, xmm0); + xmm7 = _mm_add_epi64(xmm7, xmm6); + + xmm7 = _mm_add_epi64(xmm7, _mm_srli_si128(xmm7, 8)); + RESIDUAL64_RESULT(xmm7); + } + } + else { /* order == 3 */ + __m128i xmm0, xmm1, xmm6, xmm7; + xmm0 = _mm_loadl_epi64((const __m128i*)(qlp_coeff+0)); + xmm1 = _mm_cvtsi32_si128(qlp_coeff[2]); + + xmm0 = _mm_shuffle_epi32(xmm0, _MM_SHUFFLE(3,1,2,0)); + + for(i = 0; i < (int)data_len; i++) { + //sum = 0; + //sum = qlp_coeff[2] * (FLAC__int64)data[i-3]; + xmm7 = _mm_cvtsi32_si128(data[i-3]); + xmm7 = _mm_mul_epi32(xmm7, xmm1); + + //sum += qlp_coeff[1] * (FLAC__int64)data[i-2]; + //sum += qlp_coeff[0] * (FLAC__int64)data[i-1]; + xmm6 = _mm_loadl_epi64((const __m128i*)(data+i-2)); + xmm6 = _mm_shuffle_epi32(xmm6, _MM_SHUFFLE(2,0,3,1)); + xmm6 = _mm_mul_epi32(xmm6, xmm0); + xmm7 = _mm_add_epi64(xmm7, xmm6); + + xmm7 = _mm_add_epi64(xmm7, _mm_srli_si128(xmm7, 8)); + RESIDUAL64_RESULT(xmm7); + } + } + } + else { /* order == 1, 2 */ + if(order == 2) { + __m128i xmm0, xmm7; + xmm0 = _mm_loadl_epi64((const __m128i*)(qlp_coeff+0)); + xmm0 = _mm_shuffle_epi32(xmm0, _MM_SHUFFLE(3,1,2,0)); + + for(i = 0; i < (int)data_len; i++) { + //sum = 0; + //sum += qlp_coeff[1] * (FLAC__int64)data[i-2]; + //sum += qlp_coeff[0] * (FLAC__int64)data[i-1]; + xmm7 = _mm_loadl_epi64((const __m128i*)(data+i-2)); + xmm7 = _mm_shuffle_epi32(xmm7, _MM_SHUFFLE(2,0,3,1)); + xmm7 = _mm_mul_epi32(xmm7, xmm0); + + xmm7 = _mm_add_epi64(xmm7, _mm_srli_si128(xmm7, 8)); + RESIDUAL64_RESULT(xmm7); + } + } + else { /* order == 1 */ + __m128i xmm0, xmm7; + xmm0 = _mm_cvtsi32_si128(qlp_coeff[0]); + + for(i = 0; i < (int)data_len; i++) { + //sum = qlp_coeff[0] * (FLAC__int64)data[i-1]; + xmm7 = _mm_cvtsi32_si128(data[i-1]); + xmm7 = _mm_mul_epi32(xmm7, xmm0); + RESIDUAL64_RESULT(xmm7); + } + } + } + } + } + else { /* order > 12 */ + FLAC__int64 sum; + for(i = 0; i < (int)data_len; i++) { + sum = 0; + switch(order) { + case 32: sum += qlp_coeff[31] * (FLAC__int64)data[i-32]; + case 31: sum += qlp_coeff[30] * (FLAC__int64)data[i-31]; + case 30: sum += qlp_coeff[29] * (FLAC__int64)data[i-30]; + case 29: sum += qlp_coeff[28] * (FLAC__int64)data[i-29]; + case 28: sum += qlp_coeff[27] * (FLAC__int64)data[i-28]; + case 27: sum += qlp_coeff[26] * (FLAC__int64)data[i-27]; + case 26: sum += qlp_coeff[25] * (FLAC__int64)data[i-26]; + case 25: sum += qlp_coeff[24] * (FLAC__int64)data[i-25]; + case 24: sum += qlp_coeff[23] * (FLAC__int64)data[i-24]; + case 23: sum += qlp_coeff[22] * (FLAC__int64)data[i-23]; + case 22: sum += qlp_coeff[21] * (FLAC__int64)data[i-22]; + case 21: sum += qlp_coeff[20] * (FLAC__int64)data[i-21]; + case 20: sum += qlp_coeff[19] * (FLAC__int64)data[i-20]; + case 19: sum += qlp_coeff[18] * (FLAC__int64)data[i-19]; + case 18: sum += qlp_coeff[17] * (FLAC__int64)data[i-18]; + case 17: sum += qlp_coeff[16] * (FLAC__int64)data[i-17]; + case 16: sum += qlp_coeff[15] * (FLAC__int64)data[i-16]; + case 15: sum += qlp_coeff[14] * (FLAC__int64)data[i-15]; + case 14: sum += qlp_coeff[13] * (FLAC__int64)data[i-14]; + case 13: sum += qlp_coeff[12] * (FLAC__int64)data[i-13]; + sum += qlp_coeff[11] * (FLAC__int64)data[i-12]; + sum += qlp_coeff[10] * (FLAC__int64)data[i-11]; + sum += qlp_coeff[ 9] * (FLAC__int64)data[i-10]; + sum += qlp_coeff[ 8] * (FLAC__int64)data[i- 9]; + sum += qlp_coeff[ 7] * (FLAC__int64)data[i- 8]; + sum += qlp_coeff[ 6] * (FLAC__int64)data[i- 7]; + sum += qlp_coeff[ 5] * (FLAC__int64)data[i- 6]; + sum += qlp_coeff[ 4] * (FLAC__int64)data[i- 5]; + sum += qlp_coeff[ 3] * (FLAC__int64)data[i- 4]; + sum += qlp_coeff[ 2] * (FLAC__int64)data[i- 3]; + sum += qlp_coeff[ 1] * (FLAC__int64)data[i- 2]; + sum += qlp_coeff[ 0] * (FLAC__int64)data[i- 1]; + } + residual[i] = data[i] - (FLAC__int32)(sum >> lp_quantization); + } + } +} + +FLAC__SSE_TARGET("sse4.1") +void FLAC__lpc_restore_signal_wide_intrin_sse41(const FLAC__int32 residual[], unsigned data_len, const FLAC__int32 qlp_coeff[], unsigned order, int lp_quantization, FLAC__int32 data[]) +{ + int i; + __m128i cnt = _mm_cvtsi32_si128(lp_quantization); + + if (!data_len) + return; + + FLAC__ASSERT(order > 0); + FLAC__ASSERT(order <= 32); + FLAC__ASSERT(lp_quantization <= 32); /* there's no _mm_sra_epi64() so we have to use _mm_srl_epi64() */ + + if(order <= 12) { + if(order > 8) { /* order == 9, 10, 11, 12 */ + if(order > 10) { /* order == 11, 12 */ + __m128i qlp[6], dat[6]; + __m128i summ, temp; + qlp[0] = _mm_loadl_epi64((const __m128i*)(qlp_coeff+0)); // 0 0 q[1] q[0] + qlp[1] = _mm_loadl_epi64((const __m128i*)(qlp_coeff+2)); // 0 0 q[3] q[2] + qlp[2] = _mm_loadl_epi64((const __m128i*)(qlp_coeff+4)); // 0 0 q[5] q[4] + qlp[3] = _mm_loadl_epi64((const __m128i*)(qlp_coeff+6)); // 0 0 q[7] q[6] + qlp[4] = _mm_loadl_epi64((const __m128i*)(qlp_coeff+8)); // 0 0 q[9] q[8] + if (order == 12) + qlp[5] = _mm_loadl_epi64((const __m128i*)(qlp_coeff+10)); // 0 0 q[11] q[10] + else + qlp[5] = _mm_cvtsi32_si128(qlp_coeff[10]); // 0 0 0 q[10] + + qlp[0] = _mm_shuffle_epi32(qlp[0], _MM_SHUFFLE(2,0,3,1)); // 0 q[0] 0 q[1] + qlp[1] = _mm_shuffle_epi32(qlp[1], _MM_SHUFFLE(2,0,3,1)); // 0 q[2] 0 q[3] + qlp[2] = _mm_shuffle_epi32(qlp[2], _MM_SHUFFLE(2,0,3,1)); // 0 q[4] 0 q[5] + qlp[3] = _mm_shuffle_epi32(qlp[3], _MM_SHUFFLE(2,0,3,1)); // 0 q[5] 0 q[7] + qlp[4] = _mm_shuffle_epi32(qlp[4], _MM_SHUFFLE(2,0,3,1)); // 0 q[8] 0 q[9] + qlp[5] = _mm_shuffle_epi32(qlp[5], _MM_SHUFFLE(2,0,3,1)); // 0 q[10] 0 q[11] + + dat[5] = _mm_cvtepu32_epi64(_mm_loadl_epi64((const __m128i*)(data-12))); // ? d[i-11] ? d[i-12] + dat[4] = _mm_cvtepu32_epi64(_mm_loadl_epi64((const __m128i*)(data-10))); // ? d[i-9] ? d[i-10] + dat[3] = _mm_cvtepu32_epi64(_mm_loadl_epi64((const __m128i*)(data-8 ))); // ? d[i-7] ? d[i-8] + dat[2] = _mm_cvtepu32_epi64(_mm_loadl_epi64((const __m128i*)(data-6 ))); // ? d[i-5] ? d[i-6] + dat[1] = _mm_cvtepu32_epi64(_mm_loadl_epi64((const __m128i*)(data-4 ))); // ? d[i-3] ? d[i-4] + dat[0] = _mm_cvtepu32_epi64(_mm_loadl_epi64((const __m128i*)(data-2 ))); // ? d[i-1] ? d[i-2] + + summ = _mm_mul_epi32(dat[5], qlp[5]) ; + summ = _mm_add_epi64(summ, _mm_mul_epi32(dat[4], qlp[4])); + summ = _mm_add_epi64(summ, _mm_mul_epi32(dat[3], qlp[3])); + summ = _mm_add_epi64(summ, _mm_mul_epi32(dat[2], qlp[2])); + summ = _mm_add_epi64(summ, _mm_mul_epi32(dat[1], qlp[1])); + summ = _mm_add_epi64(summ, _mm_mul_epi32(dat[0], qlp[0])); + + summ = _mm_add_epi64(summ, _mm_srli_si128(summ, 8)); // ?_64 sum_64 + summ = _mm_srl_epi64(summ, cnt); // ?_64 (sum >> lp_quantization)_64 == ?_32 ?_32 ?_32 (sum >> lp_quantization)_32 + temp = _mm_cvtsi32_si128(residual[0]); // 0 0 0 r[i] + temp = _mm_add_epi32(temp, summ); // ? ? ? d[i] + data[0] = _mm_cvtsi128_si32(temp); + + for(i = 1; i < (int)data_len; i++) { + dat[5] = _mm_alignr_epi8(dat[4], dat[5], 8); // ? d[i-10] ? d[i-11] + dat[4] = _mm_alignr_epi8(dat[3], dat[4], 8); // ? d[i-8] ? d[i-9] + dat[3] = _mm_alignr_epi8(dat[2], dat[3], 8); // ? d[i-6] ? d[i-7] + dat[2] = _mm_alignr_epi8(dat[1], dat[2], 8); // ? d[i-4] ? d[i-5] + dat[1] = _mm_alignr_epi8(dat[0], dat[1], 8); // ? d[i-2] ? d[i-3] + dat[0] = _mm_alignr_epi8(temp, dat[0], 8); // ? d[i ] ? d[i-1] + + summ = _mm_mul_epi32(dat[5], qlp[5]) ; + summ = _mm_add_epi64(summ, _mm_mul_epi32(dat[4], qlp[4])); + summ = _mm_add_epi64(summ, _mm_mul_epi32(dat[3], qlp[3])); + summ = _mm_add_epi64(summ, _mm_mul_epi32(dat[2], qlp[2])); + summ = _mm_add_epi64(summ, _mm_mul_epi32(dat[1], qlp[1])); + summ = _mm_add_epi64(summ, _mm_mul_epi32(dat[0], qlp[0])); + + summ = _mm_add_epi64(summ, _mm_srli_si128(summ, 8)); // ?_64 sum_64 + summ = _mm_srl_epi64(summ, cnt); // ?_64 (sum >> lp_quantization)_64 == ?_32 ?_32 ?_32 (sum >> lp_quantization)_32 + temp = _mm_cvtsi32_si128(residual[i]); // 0 0 0 r[i] + temp = _mm_add_epi32(temp, summ); // ? ? ? d[i] + data[i] = _mm_cvtsi128_si32(temp); + } + } + else { /* order == 9, 10 */ + __m128i qlp[5], dat[5]; + __m128i summ, temp; + qlp[0] = _mm_loadl_epi64((const __m128i*)(qlp_coeff+0)); + qlp[1] = _mm_loadl_epi64((const __m128i*)(qlp_coeff+2)); + qlp[2] = _mm_loadl_epi64((const __m128i*)(qlp_coeff+4)); + qlp[3] = _mm_loadl_epi64((const __m128i*)(qlp_coeff+6)); + if (order == 10) + qlp[4] = _mm_loadl_epi64((const __m128i*)(qlp_coeff+8)); + else + qlp[4] = _mm_cvtsi32_si128(qlp_coeff[8]); + + qlp[0] = _mm_shuffle_epi32(qlp[0], _MM_SHUFFLE(2,0,3,1)); + qlp[1] = _mm_shuffle_epi32(qlp[1], _MM_SHUFFLE(2,0,3,1)); + qlp[2] = _mm_shuffle_epi32(qlp[2], _MM_SHUFFLE(2,0,3,1)); + qlp[3] = _mm_shuffle_epi32(qlp[3], _MM_SHUFFLE(2,0,3,1)); + qlp[4] = _mm_shuffle_epi32(qlp[4], _MM_SHUFFLE(2,0,3,1)); + + dat[4] = _mm_cvtepu32_epi64(_mm_loadl_epi64((const __m128i*)(data-10))); + dat[3] = _mm_cvtepu32_epi64(_mm_loadl_epi64((const __m128i*)(data-8 ))); + dat[2] = _mm_cvtepu32_epi64(_mm_loadl_epi64((const __m128i*)(data-6 ))); + dat[1] = _mm_cvtepu32_epi64(_mm_loadl_epi64((const __m128i*)(data-4 ))); + dat[0] = _mm_cvtepu32_epi64(_mm_loadl_epi64((const __m128i*)(data-2 ))); + + summ = _mm_mul_epi32(dat[4], qlp[4]) ; + summ = _mm_add_epi64(summ, _mm_mul_epi32(dat[3], qlp[3])); + summ = _mm_add_epi64(summ, _mm_mul_epi32(dat[2], qlp[2])); + summ = _mm_add_epi64(summ, _mm_mul_epi32(dat[1], qlp[1])); + summ = _mm_add_epi64(summ, _mm_mul_epi32(dat[0], qlp[0])); + + summ = _mm_add_epi64(summ, _mm_srli_si128(summ, 8)); + summ = _mm_srl_epi64(summ, cnt); + temp = _mm_cvtsi32_si128(residual[0]); + temp = _mm_add_epi32(temp, summ); + data[0] = _mm_cvtsi128_si32(temp); + + for(i = 1; i < (int)data_len; i++) { + dat[4] = _mm_alignr_epi8(dat[3], dat[4], 8); + dat[3] = _mm_alignr_epi8(dat[2], dat[3], 8); + dat[2] = _mm_alignr_epi8(dat[1], dat[2], 8); + dat[1] = _mm_alignr_epi8(dat[0], dat[1], 8); + dat[0] = _mm_alignr_epi8(temp, dat[0], 8); + + summ = _mm_mul_epi32(dat[4], qlp[4]) ; + summ = _mm_add_epi64(summ, _mm_mul_epi32(dat[3], qlp[3])); + summ = _mm_add_epi64(summ, _mm_mul_epi32(dat[2], qlp[2])); + summ = _mm_add_epi64(summ, _mm_mul_epi32(dat[1], qlp[1])); + summ = _mm_add_epi64(summ, _mm_mul_epi32(dat[0], qlp[0])); + + summ = _mm_add_epi64(summ, _mm_srli_si128(summ, 8)); + summ = _mm_srl_epi64(summ, cnt); + temp = _mm_cvtsi32_si128(residual[i]); + temp = _mm_add_epi32(temp, summ); + data[i] = _mm_cvtsi128_si32(temp); + } + } + } + else if(order > 4) { /* order == 5, 6, 7, 8 */ + if(order > 6) { /* order == 7, 8 */ + __m128i qlp[4], dat[4]; + __m128i summ, temp; + qlp[0] = _mm_loadl_epi64((const __m128i*)(qlp_coeff+0)); + qlp[1] = _mm_loadl_epi64((const __m128i*)(qlp_coeff+2)); + qlp[2] = _mm_loadl_epi64((const __m128i*)(qlp_coeff+4)); + if (order == 8) + qlp[3] = _mm_loadl_epi64((const __m128i*)(qlp_coeff+6)); + else + qlp[3] = _mm_cvtsi32_si128(qlp_coeff[6]); + + qlp[0] = _mm_shuffle_epi32(qlp[0], _MM_SHUFFLE(2,0,3,1)); + qlp[1] = _mm_shuffle_epi32(qlp[1], _MM_SHUFFLE(2,0,3,1)); + qlp[2] = _mm_shuffle_epi32(qlp[2], _MM_SHUFFLE(2,0,3,1)); + qlp[3] = _mm_shuffle_epi32(qlp[3], _MM_SHUFFLE(2,0,3,1)); + + dat[3] = _mm_cvtepu32_epi64(_mm_loadl_epi64((const __m128i*)(data-8 ))); + dat[2] = _mm_cvtepu32_epi64(_mm_loadl_epi64((const __m128i*)(data-6 ))); + dat[1] = _mm_cvtepu32_epi64(_mm_loadl_epi64((const __m128i*)(data-4 ))); + dat[0] = _mm_cvtepu32_epi64(_mm_loadl_epi64((const __m128i*)(data-2 ))); + + summ = _mm_mul_epi32(dat[3], qlp[3]) ; + summ = _mm_add_epi64(summ, _mm_mul_epi32(dat[2], qlp[2])); + summ = _mm_add_epi64(summ, _mm_mul_epi32(dat[1], qlp[1])); + summ = _mm_add_epi64(summ, _mm_mul_epi32(dat[0], qlp[0])); + + summ = _mm_add_epi64(summ, _mm_srli_si128(summ, 8)); + summ = _mm_srl_epi64(summ, cnt); + temp = _mm_cvtsi32_si128(residual[0]); + temp = _mm_add_epi32(temp, summ); + data[0] = _mm_cvtsi128_si32(temp); + + for(i = 1; i < (int)data_len; i++) { + dat[3] = _mm_alignr_epi8(dat[2], dat[3], 8); + dat[2] = _mm_alignr_epi8(dat[1], dat[2], 8); + dat[1] = _mm_alignr_epi8(dat[0], dat[1], 8); + dat[0] = _mm_alignr_epi8(temp, dat[0], 8); + + summ = _mm_mul_epi32(dat[3], qlp[3]) ; + summ = _mm_add_epi64(summ, _mm_mul_epi32(dat[2], qlp[2])); + summ = _mm_add_epi64(summ, _mm_mul_epi32(dat[1], qlp[1])); + summ = _mm_add_epi64(summ, _mm_mul_epi32(dat[0], qlp[0])); + + summ = _mm_add_epi64(summ, _mm_srli_si128(summ, 8)); + summ = _mm_srl_epi64(summ, cnt); + temp = _mm_cvtsi32_si128(residual[i]); + temp = _mm_add_epi32(temp, summ); + data[i] = _mm_cvtsi128_si32(temp); + } + } + else { /* order == 5, 6 */ + __m128i qlp[3], dat[3]; + __m128i summ, temp; + qlp[0] = _mm_loadl_epi64((const __m128i*)(qlp_coeff+0)); + qlp[1] = _mm_loadl_epi64((const __m128i*)(qlp_coeff+2)); + if (order == 6) + qlp[2] = _mm_loadl_epi64((const __m128i*)(qlp_coeff+4)); + else + qlp[2] = _mm_cvtsi32_si128(qlp_coeff[4]); + + qlp[0] = _mm_shuffle_epi32(qlp[0], _MM_SHUFFLE(2,0,3,1)); + qlp[1] = _mm_shuffle_epi32(qlp[1], _MM_SHUFFLE(2,0,3,1)); + qlp[2] = _mm_shuffle_epi32(qlp[2], _MM_SHUFFLE(2,0,3,1)); + + dat[2] = _mm_cvtepu32_epi64(_mm_loadl_epi64((const __m128i*)(data-6 ))); + dat[1] = _mm_cvtepu32_epi64(_mm_loadl_epi64((const __m128i*)(data-4 ))); + dat[0] = _mm_cvtepu32_epi64(_mm_loadl_epi64((const __m128i*)(data-2 ))); + + summ = _mm_mul_epi32(dat[2], qlp[2]) ; + summ = _mm_add_epi64(summ, _mm_mul_epi32(dat[1], qlp[1])); + summ = _mm_add_epi64(summ, _mm_mul_epi32(dat[0], qlp[0])); + + summ = _mm_add_epi64(summ, _mm_srli_si128(summ, 8)); + summ = _mm_srl_epi64(summ, cnt); + temp = _mm_cvtsi32_si128(residual[0]); + temp = _mm_add_epi32(temp, summ); + data[0] = _mm_cvtsi128_si32(temp); + + for(i = 1; i < (int)data_len; i++) { + dat[2] = _mm_alignr_epi8(dat[1], dat[2], 8); + dat[1] = _mm_alignr_epi8(dat[0], dat[1], 8); + dat[0] = _mm_alignr_epi8(temp, dat[0], 8); + + summ = _mm_mul_epi32(dat[2], qlp[2]) ; + summ = _mm_add_epi64(summ, _mm_mul_epi32(dat[1], qlp[1])); + summ = _mm_add_epi64(summ, _mm_mul_epi32(dat[0], qlp[0])); + + summ = _mm_add_epi64(summ, _mm_srli_si128(summ, 8)); + summ = _mm_srl_epi64(summ, cnt); + temp = _mm_cvtsi32_si128(residual[i]); + temp = _mm_add_epi32(temp, summ); + data[i] = _mm_cvtsi128_si32(temp); + } + } + } + else { /* order == 1, 2, 3, 4 */ + if(order > 2) { /* order == 3, 4 */ + __m128i qlp[2], dat[2]; + __m128i summ, temp; + qlp[0] = _mm_loadl_epi64((const __m128i*)(qlp_coeff+0)); + if (order == 4) + qlp[1] = _mm_loadl_epi64((const __m128i*)(qlp_coeff+2)); + else + qlp[1] = _mm_cvtsi32_si128(qlp_coeff[2]); + + qlp[0] = _mm_shuffle_epi32(qlp[0], _MM_SHUFFLE(2,0,3,1)); + qlp[1] = _mm_shuffle_epi32(qlp[1], _MM_SHUFFLE(2,0,3,1)); + + dat[1] = _mm_cvtepu32_epi64(_mm_loadl_epi64((const __m128i*)(data-4 ))); + dat[0] = _mm_cvtepu32_epi64(_mm_loadl_epi64((const __m128i*)(data-2 ))); + + summ = _mm_mul_epi32(dat[1], qlp[1]) ; + summ = _mm_add_epi64(summ, _mm_mul_epi32(dat[0], qlp[0])); + + summ = _mm_add_epi64(summ, _mm_srli_si128(summ, 8)); + summ = _mm_srl_epi64(summ, cnt); + temp = _mm_cvtsi32_si128(residual[0]); + temp = _mm_add_epi32(temp, summ); + data[0] = _mm_cvtsi128_si32(temp); + + for(i = 1; i < (int)data_len; i++) { + dat[1] = _mm_alignr_epi8(dat[0], dat[1], 8); + dat[0] = _mm_alignr_epi8(temp, dat[0], 8); + + summ = _mm_mul_epi32(dat[1], qlp[1]) ; + summ = _mm_add_epi64(summ, _mm_mul_epi32(dat[0], qlp[0])); + + summ = _mm_add_epi64(summ, _mm_srli_si128(summ, 8)); + summ = _mm_srl_epi64(summ, cnt); + temp = _mm_cvtsi32_si128(residual[i]); + temp = _mm_add_epi32(temp, summ); + data[i] = _mm_cvtsi128_si32(temp); + } + } + else { /* order == 1, 2 */ + if(order == 2) { + __m128i qlp0, dat0; + __m128i summ, temp; + qlp0 = _mm_loadl_epi64((const __m128i*)(qlp_coeff)); + qlp0 = _mm_shuffle_epi32(qlp0, _MM_SHUFFLE(2,0,3,1)); + + dat0 = _mm_cvtepu32_epi64(_mm_loadl_epi64((const __m128i*)(data-2 ))); + + summ = _mm_mul_epi32(dat0, qlp0) ; + + summ = _mm_add_epi64(summ, _mm_srli_si128(summ, 8)); + summ = _mm_srl_epi64(summ, cnt); + temp = _mm_cvtsi32_si128(residual[0]); + temp = _mm_add_epi32(temp, summ); + data[0] = _mm_cvtsi128_si32(temp); + + for(i = 1; i < (int)data_len; i++) { + dat0 = _mm_alignr_epi8(temp, dat0, 8); + + summ = _mm_mul_epi32(dat0, qlp0) ; + + summ = _mm_add_epi64(summ, _mm_srli_si128(summ, 8)); + summ = _mm_srl_epi64(summ, cnt); + temp = _mm_cvtsi32_si128(residual[i]); + temp = _mm_add_epi32(temp, summ); + data[i] = _mm_cvtsi128_si32(temp); + } + } + else { /* order == 1 */ + __m128i qlp0; + __m128i summ, temp; + qlp0 = _mm_cvtsi32_si128(qlp_coeff[0]); + temp = _mm_cvtsi32_si128(data[-1]); + + summ = _mm_mul_epi32(temp, qlp0); + summ = _mm_srl_epi64(summ, cnt); + temp = _mm_cvtsi32_si128(residual[0]); + temp = _mm_add_epi32(temp, summ); + data[0] = _mm_cvtsi128_si32(temp); + + for(i = 1; i < (int)data_len; i++) { + summ = _mm_mul_epi32(temp, qlp0) ; + summ = _mm_srl_epi64(summ, cnt); + temp = _mm_cvtsi32_si128(residual[i]); + temp = _mm_add_epi32(temp, summ); + data[i] = _mm_cvtsi128_si32(temp); + } + } + } + } + } + else { /* order > 12 */ + FLAC__int64 sum; + for(i = 0; i < (int)data_len; i++) { + sum = 0; + switch(order) { + case 32: sum += qlp_coeff[31] * (FLAC__int64)data[i-32]; + case 31: sum += qlp_coeff[30] * (FLAC__int64)data[i-31]; + case 30: sum += qlp_coeff[29] * (FLAC__int64)data[i-30]; + case 29: sum += qlp_coeff[28] * (FLAC__int64)data[i-29]; + case 28: sum += qlp_coeff[27] * (FLAC__int64)data[i-28]; + case 27: sum += qlp_coeff[26] * (FLAC__int64)data[i-27]; + case 26: sum += qlp_coeff[25] * (FLAC__int64)data[i-26]; + case 25: sum += qlp_coeff[24] * (FLAC__int64)data[i-25]; + case 24: sum += qlp_coeff[23] * (FLAC__int64)data[i-24]; + case 23: sum += qlp_coeff[22] * (FLAC__int64)data[i-23]; + case 22: sum += qlp_coeff[21] * (FLAC__int64)data[i-22]; + case 21: sum += qlp_coeff[20] * (FLAC__int64)data[i-21]; + case 20: sum += qlp_coeff[19] * (FLAC__int64)data[i-20]; + case 19: sum += qlp_coeff[18] * (FLAC__int64)data[i-19]; + case 18: sum += qlp_coeff[17] * (FLAC__int64)data[i-18]; + case 17: sum += qlp_coeff[16] * (FLAC__int64)data[i-17]; + case 16: sum += qlp_coeff[15] * (FLAC__int64)data[i-16]; + case 15: sum += qlp_coeff[14] * (FLAC__int64)data[i-15]; + case 14: sum += qlp_coeff[13] * (FLAC__int64)data[i-14]; + case 13: sum += qlp_coeff[12] * (FLAC__int64)data[i-13]; + sum += qlp_coeff[11] * (FLAC__int64)data[i-12]; + sum += qlp_coeff[10] * (FLAC__int64)data[i-11]; + sum += qlp_coeff[ 9] * (FLAC__int64)data[i-10]; + sum += qlp_coeff[ 8] * (FLAC__int64)data[i- 9]; + sum += qlp_coeff[ 7] * (FLAC__int64)data[i- 8]; + sum += qlp_coeff[ 6] * (FLAC__int64)data[i- 7]; + sum += qlp_coeff[ 5] * (FLAC__int64)data[i- 6]; + sum += qlp_coeff[ 4] * (FLAC__int64)data[i- 5]; + sum += qlp_coeff[ 3] * (FLAC__int64)data[i- 4]; + sum += qlp_coeff[ 2] * (FLAC__int64)data[i- 3]; + sum += qlp_coeff[ 1] * (FLAC__int64)data[i- 2]; + sum += qlp_coeff[ 0] * (FLAC__int64)data[i- 1]; + } + data[i] = residual[i] + (FLAC__int32)(sum >> lp_quantization); + } + } +} + +#endif /* defined FLAC__CPU_IA32 */ + +FLAC__SSE_TARGET("sse4.1") +void FLAC__lpc_compute_residual_from_qlp_coefficients_intrin_sse41(const FLAC__int32 *data, unsigned data_len, const FLAC__int32 qlp_coeff[], unsigned order, int lp_quantization, FLAC__int32 residual[]) +{ + int i; + FLAC__int32 sum; + __m128i cnt = _mm_cvtsi32_si128(lp_quantization); + + FLAC__ASSERT(order > 0); + FLAC__ASSERT(order <= 32); + + if(order <= 12) { + if(order > 8) { + if(order > 10) { + if(order == 12) { + __m128i q0, q1, q2, q3, q4, q5, q6, q7, q8, q9, q10, q11; + q0 = _mm_cvtsi32_si128(qlp_coeff[0]); q0 = _mm_shuffle_epi32(q0, _MM_SHUFFLE(0,0,0,0)); + q1 = _mm_cvtsi32_si128(qlp_coeff[1]); q1 = _mm_shuffle_epi32(q1, _MM_SHUFFLE(0,0,0,0)); + q2 = _mm_cvtsi32_si128(qlp_coeff[2]); q2 = _mm_shuffle_epi32(q2, _MM_SHUFFLE(0,0,0,0)); + q3 = _mm_cvtsi32_si128(qlp_coeff[3]); q3 = _mm_shuffle_epi32(q3, _MM_SHUFFLE(0,0,0,0)); + q4 = _mm_cvtsi32_si128(qlp_coeff[4]); q4 = _mm_shuffle_epi32(q4, _MM_SHUFFLE(0,0,0,0)); + q5 = _mm_cvtsi32_si128(qlp_coeff[5]); q5 = _mm_shuffle_epi32(q5, _MM_SHUFFLE(0,0,0,0)); + q6 = _mm_cvtsi32_si128(qlp_coeff[6]); q6 = _mm_shuffle_epi32(q6, _MM_SHUFFLE(0,0,0,0)); + q7 = _mm_cvtsi32_si128(qlp_coeff[7]); q7 = _mm_shuffle_epi32(q7, _MM_SHUFFLE(0,0,0,0)); + q8 = _mm_cvtsi32_si128(qlp_coeff[8]); q8 = _mm_shuffle_epi32(q8, _MM_SHUFFLE(0,0,0,0)); + q9 = _mm_cvtsi32_si128(qlp_coeff[9]); q9 = _mm_shuffle_epi32(q9, _MM_SHUFFLE(0,0,0,0)); + q10 = _mm_cvtsi32_si128(qlp_coeff[10]); q10 = _mm_shuffle_epi32(q10, _MM_SHUFFLE(0,0,0,0)); + q11 = _mm_cvtsi32_si128(qlp_coeff[11]); q11 = _mm_shuffle_epi32(q11, _MM_SHUFFLE(0,0,0,0)); + + for(i = 0; i < (int)data_len-3; i+=4) { + __m128i summ, mull; + summ = _mm_mullo_epi32(q11, _mm_loadu_si128((const __m128i*)(data+i-12))); + mull = _mm_mullo_epi32(q10, _mm_loadu_si128((const __m128i*)(data+i-11))); summ = _mm_add_epi32(summ, mull); + mull = _mm_mullo_epi32(q9, _mm_loadu_si128((const __m128i*)(data+i-10))); summ = _mm_add_epi32(summ, mull); + mull = _mm_mullo_epi32(q8, _mm_loadu_si128((const __m128i*)(data+i-9))); summ = _mm_add_epi32(summ, mull); + mull = _mm_mullo_epi32(q7, _mm_loadu_si128((const __m128i*)(data+i-8))); summ = _mm_add_epi32(summ, mull); + mull = _mm_mullo_epi32(q6, _mm_loadu_si128((const __m128i*)(data+i-7))); summ = _mm_add_epi32(summ, mull); + mull = _mm_mullo_epi32(q5, _mm_loadu_si128((const __m128i*)(data+i-6))); summ = _mm_add_epi32(summ, mull); + mull = _mm_mullo_epi32(q4, _mm_loadu_si128((const __m128i*)(data+i-5))); summ = _mm_add_epi32(summ, mull); + mull = _mm_mullo_epi32(q3, _mm_loadu_si128((const __m128i*)(data+i-4))); summ = _mm_add_epi32(summ, mull); + mull = _mm_mullo_epi32(q2, _mm_loadu_si128((const __m128i*)(data+i-3))); summ = _mm_add_epi32(summ, mull); + mull = _mm_mullo_epi32(q1, _mm_loadu_si128((const __m128i*)(data+i-2))); summ = _mm_add_epi32(summ, mull); + mull = _mm_mullo_epi32(q0, _mm_loadu_si128((const __m128i*)(data+i-1))); summ = _mm_add_epi32(summ, mull); + summ = _mm_sra_epi32(summ, cnt); + _mm_storeu_si128((__m128i*)(residual+i), _mm_sub_epi32(_mm_loadu_si128((const __m128i*)(data+i)), summ)); + } + } + else { /* order == 11 */ + __m128i q0, q1, q2, q3, q4, q5, q6, q7, q8, q9, q10; + q0 = _mm_cvtsi32_si128(qlp_coeff[0]); q0 = _mm_shuffle_epi32(q0, _MM_SHUFFLE(0,0,0,0)); + q1 = _mm_cvtsi32_si128(qlp_coeff[1]); q1 = _mm_shuffle_epi32(q1, _MM_SHUFFLE(0,0,0,0)); + q2 = _mm_cvtsi32_si128(qlp_coeff[2]); q2 = _mm_shuffle_epi32(q2, _MM_SHUFFLE(0,0,0,0)); + q3 = _mm_cvtsi32_si128(qlp_coeff[3]); q3 = _mm_shuffle_epi32(q3, _MM_SHUFFLE(0,0,0,0)); + q4 = _mm_cvtsi32_si128(qlp_coeff[4]); q4 = _mm_shuffle_epi32(q4, _MM_SHUFFLE(0,0,0,0)); + q5 = _mm_cvtsi32_si128(qlp_coeff[5]); q5 = _mm_shuffle_epi32(q5, _MM_SHUFFLE(0,0,0,0)); + q6 = _mm_cvtsi32_si128(qlp_coeff[6]); q6 = _mm_shuffle_epi32(q6, _MM_SHUFFLE(0,0,0,0)); + q7 = _mm_cvtsi32_si128(qlp_coeff[7]); q7 = _mm_shuffle_epi32(q7, _MM_SHUFFLE(0,0,0,0)); + q8 = _mm_cvtsi32_si128(qlp_coeff[8]); q8 = _mm_shuffle_epi32(q8, _MM_SHUFFLE(0,0,0,0)); + q9 = _mm_cvtsi32_si128(qlp_coeff[9]); q9 = _mm_shuffle_epi32(q9, _MM_SHUFFLE(0,0,0,0)); + q10 = _mm_cvtsi32_si128(qlp_coeff[10]); q10 = _mm_shuffle_epi32(q10, _MM_SHUFFLE(0,0,0,0)); + + for(i = 0; i < (int)data_len-3; i+=4) { + __m128i summ, mull; + summ = _mm_mullo_epi32(q10, _mm_loadu_si128((const __m128i*)(data+i-11))); + mull = _mm_mullo_epi32(q9, _mm_loadu_si128((const __m128i*)(data+i-10))); summ = _mm_add_epi32(summ, mull); + mull = _mm_mullo_epi32(q8, _mm_loadu_si128((const __m128i*)(data+i-9))); summ = _mm_add_epi32(summ, mull); + mull = _mm_mullo_epi32(q7, _mm_loadu_si128((const __m128i*)(data+i-8))); summ = _mm_add_epi32(summ, mull); + mull = _mm_mullo_epi32(q6, _mm_loadu_si128((const __m128i*)(data+i-7))); summ = _mm_add_epi32(summ, mull); + mull = _mm_mullo_epi32(q5, _mm_loadu_si128((const __m128i*)(data+i-6))); summ = _mm_add_epi32(summ, mull); + mull = _mm_mullo_epi32(q4, _mm_loadu_si128((const __m128i*)(data+i-5))); summ = _mm_add_epi32(summ, mull); + mull = _mm_mullo_epi32(q3, _mm_loadu_si128((const __m128i*)(data+i-4))); summ = _mm_add_epi32(summ, mull); + mull = _mm_mullo_epi32(q2, _mm_loadu_si128((const __m128i*)(data+i-3))); summ = _mm_add_epi32(summ, mull); + mull = _mm_mullo_epi32(q1, _mm_loadu_si128((const __m128i*)(data+i-2))); summ = _mm_add_epi32(summ, mull); + mull = _mm_mullo_epi32(q0, _mm_loadu_si128((const __m128i*)(data+i-1))); summ = _mm_add_epi32(summ, mull); + summ = _mm_sra_epi32(summ, cnt); + _mm_storeu_si128((__m128i*)(residual+i), _mm_sub_epi32(_mm_loadu_si128((const __m128i*)(data+i)), summ)); + } + } + } + else { + if(order == 10) { + __m128i q0, q1, q2, q3, q4, q5, q6, q7, q8, q9; + q0 = _mm_cvtsi32_si128(qlp_coeff[0]); q0 = _mm_shuffle_epi32(q0, _MM_SHUFFLE(0,0,0,0)); + q1 = _mm_cvtsi32_si128(qlp_coeff[1]); q1 = _mm_shuffle_epi32(q1, _MM_SHUFFLE(0,0,0,0)); + q2 = _mm_cvtsi32_si128(qlp_coeff[2]); q2 = _mm_shuffle_epi32(q2, _MM_SHUFFLE(0,0,0,0)); + q3 = _mm_cvtsi32_si128(qlp_coeff[3]); q3 = _mm_shuffle_epi32(q3, _MM_SHUFFLE(0,0,0,0)); + q4 = _mm_cvtsi32_si128(qlp_coeff[4]); q4 = _mm_shuffle_epi32(q4, _MM_SHUFFLE(0,0,0,0)); + q5 = _mm_cvtsi32_si128(qlp_coeff[5]); q5 = _mm_shuffle_epi32(q5, _MM_SHUFFLE(0,0,0,0)); + q6 = _mm_cvtsi32_si128(qlp_coeff[6]); q6 = _mm_shuffle_epi32(q6, _MM_SHUFFLE(0,0,0,0)); + q7 = _mm_cvtsi32_si128(qlp_coeff[7]); q7 = _mm_shuffle_epi32(q7, _MM_SHUFFLE(0,0,0,0)); + q8 = _mm_cvtsi32_si128(qlp_coeff[8]); q8 = _mm_shuffle_epi32(q8, _MM_SHUFFLE(0,0,0,0)); + q9 = _mm_cvtsi32_si128(qlp_coeff[9]); q9 = _mm_shuffle_epi32(q9, _MM_SHUFFLE(0,0,0,0)); + + for(i = 0; i < (int)data_len-3; i+=4) { + __m128i summ, mull; + summ = _mm_mullo_epi32(q9, _mm_loadu_si128((const __m128i*)(data+i-10))); + mull = _mm_mullo_epi32(q8, _mm_loadu_si128((const __m128i*)(data+i-9))); summ = _mm_add_epi32(summ, mull); + mull = _mm_mullo_epi32(q7, _mm_loadu_si128((const __m128i*)(data+i-8))); summ = _mm_add_epi32(summ, mull); + mull = _mm_mullo_epi32(q6, _mm_loadu_si128((const __m128i*)(data+i-7))); summ = _mm_add_epi32(summ, mull); + mull = _mm_mullo_epi32(q5, _mm_loadu_si128((const __m128i*)(data+i-6))); summ = _mm_add_epi32(summ, mull); + mull = _mm_mullo_epi32(q4, _mm_loadu_si128((const __m128i*)(data+i-5))); summ = _mm_add_epi32(summ, mull); + mull = _mm_mullo_epi32(q3, _mm_loadu_si128((const __m128i*)(data+i-4))); summ = _mm_add_epi32(summ, mull); + mull = _mm_mullo_epi32(q2, _mm_loadu_si128((const __m128i*)(data+i-3))); summ = _mm_add_epi32(summ, mull); + mull = _mm_mullo_epi32(q1, _mm_loadu_si128((const __m128i*)(data+i-2))); summ = _mm_add_epi32(summ, mull); + mull = _mm_mullo_epi32(q0, _mm_loadu_si128((const __m128i*)(data+i-1))); summ = _mm_add_epi32(summ, mull); + summ = _mm_sra_epi32(summ, cnt); + _mm_storeu_si128((__m128i*)(residual+i), _mm_sub_epi32(_mm_loadu_si128((const __m128i*)(data+i)), summ)); + } + } + else { /* order == 9 */ + __m128i q0, q1, q2, q3, q4, q5, q6, q7, q8; + q0 = _mm_cvtsi32_si128(qlp_coeff[0]); q0 = _mm_shuffle_epi32(q0, _MM_SHUFFLE(0,0,0,0)); + q1 = _mm_cvtsi32_si128(qlp_coeff[1]); q1 = _mm_shuffle_epi32(q1, _MM_SHUFFLE(0,0,0,0)); + q2 = _mm_cvtsi32_si128(qlp_coeff[2]); q2 = _mm_shuffle_epi32(q2, _MM_SHUFFLE(0,0,0,0)); + q3 = _mm_cvtsi32_si128(qlp_coeff[3]); q3 = _mm_shuffle_epi32(q3, _MM_SHUFFLE(0,0,0,0)); + q4 = _mm_cvtsi32_si128(qlp_coeff[4]); q4 = _mm_shuffle_epi32(q4, _MM_SHUFFLE(0,0,0,0)); + q5 = _mm_cvtsi32_si128(qlp_coeff[5]); q5 = _mm_shuffle_epi32(q5, _MM_SHUFFLE(0,0,0,0)); + q6 = _mm_cvtsi32_si128(qlp_coeff[6]); q6 = _mm_shuffle_epi32(q6, _MM_SHUFFLE(0,0,0,0)); + q7 = _mm_cvtsi32_si128(qlp_coeff[7]); q7 = _mm_shuffle_epi32(q7, _MM_SHUFFLE(0,0,0,0)); + q8 = _mm_cvtsi32_si128(qlp_coeff[8]); q8 = _mm_shuffle_epi32(q8, _MM_SHUFFLE(0,0,0,0)); + + for(i = 0; i < (int)data_len-3; i+=4) { + __m128i summ, mull; + summ = _mm_mullo_epi32(q8, _mm_loadu_si128((const __m128i*)(data+i-9))); + mull = _mm_mullo_epi32(q7, _mm_loadu_si128((const __m128i*)(data+i-8))); summ = _mm_add_epi32(summ, mull); + mull = _mm_mullo_epi32(q6, _mm_loadu_si128((const __m128i*)(data+i-7))); summ = _mm_add_epi32(summ, mull); + mull = _mm_mullo_epi32(q5, _mm_loadu_si128((const __m128i*)(data+i-6))); summ = _mm_add_epi32(summ, mull); + mull = _mm_mullo_epi32(q4, _mm_loadu_si128((const __m128i*)(data+i-5))); summ = _mm_add_epi32(summ, mull); + mull = _mm_mullo_epi32(q3, _mm_loadu_si128((const __m128i*)(data+i-4))); summ = _mm_add_epi32(summ, mull); + mull = _mm_mullo_epi32(q2, _mm_loadu_si128((const __m128i*)(data+i-3))); summ = _mm_add_epi32(summ, mull); + mull = _mm_mullo_epi32(q1, _mm_loadu_si128((const __m128i*)(data+i-2))); summ = _mm_add_epi32(summ, mull); + mull = _mm_mullo_epi32(q0, _mm_loadu_si128((const __m128i*)(data+i-1))); summ = _mm_add_epi32(summ, mull); + summ = _mm_sra_epi32(summ, cnt); + _mm_storeu_si128((__m128i*)(residual+i), _mm_sub_epi32(_mm_loadu_si128((const __m128i*)(data+i)), summ)); + } + } + } + } + else if(order > 4) { + if(order > 6) { + if(order == 8) { + __m128i q0, q1, q2, q3, q4, q5, q6, q7; + q0 = _mm_cvtsi32_si128(qlp_coeff[0]); q0 = _mm_shuffle_epi32(q0, _MM_SHUFFLE(0,0,0,0)); + q1 = _mm_cvtsi32_si128(qlp_coeff[1]); q1 = _mm_shuffle_epi32(q1, _MM_SHUFFLE(0,0,0,0)); + q2 = _mm_cvtsi32_si128(qlp_coeff[2]); q2 = _mm_shuffle_epi32(q2, _MM_SHUFFLE(0,0,0,0)); + q3 = _mm_cvtsi32_si128(qlp_coeff[3]); q3 = _mm_shuffle_epi32(q3, _MM_SHUFFLE(0,0,0,0)); + q4 = _mm_cvtsi32_si128(qlp_coeff[4]); q4 = _mm_shuffle_epi32(q4, _MM_SHUFFLE(0,0,0,0)); + q5 = _mm_cvtsi32_si128(qlp_coeff[5]); q5 = _mm_shuffle_epi32(q5, _MM_SHUFFLE(0,0,0,0)); + q6 = _mm_cvtsi32_si128(qlp_coeff[6]); q6 = _mm_shuffle_epi32(q6, _MM_SHUFFLE(0,0,0,0)); + q7 = _mm_cvtsi32_si128(qlp_coeff[7]); q7 = _mm_shuffle_epi32(q7, _MM_SHUFFLE(0,0,0,0)); + + for(i = 0; i < (int)data_len-3; i+=4) { + __m128i summ, mull; + summ = _mm_mullo_epi32(q7, _mm_loadu_si128((const __m128i*)(data+i-8))); + mull = _mm_mullo_epi32(q6, _mm_loadu_si128((const __m128i*)(data+i-7))); summ = _mm_add_epi32(summ, mull); + mull = _mm_mullo_epi32(q5, _mm_loadu_si128((const __m128i*)(data+i-6))); summ = _mm_add_epi32(summ, mull); + mull = _mm_mullo_epi32(q4, _mm_loadu_si128((const __m128i*)(data+i-5))); summ = _mm_add_epi32(summ, mull); + mull = _mm_mullo_epi32(q3, _mm_loadu_si128((const __m128i*)(data+i-4))); summ = _mm_add_epi32(summ, mull); + mull = _mm_mullo_epi32(q2, _mm_loadu_si128((const __m128i*)(data+i-3))); summ = _mm_add_epi32(summ, mull); + mull = _mm_mullo_epi32(q1, _mm_loadu_si128((const __m128i*)(data+i-2))); summ = _mm_add_epi32(summ, mull); + mull = _mm_mullo_epi32(q0, _mm_loadu_si128((const __m128i*)(data+i-1))); summ = _mm_add_epi32(summ, mull); + summ = _mm_sra_epi32(summ, cnt); + _mm_storeu_si128((__m128i*)(residual+i), _mm_sub_epi32(_mm_loadu_si128((const __m128i*)(data+i)), summ)); + } + } + else { /* order == 7 */ + __m128i q0, q1, q2, q3, q4, q5, q6; + q0 = _mm_cvtsi32_si128(qlp_coeff[0]); q0 = _mm_shuffle_epi32(q0, _MM_SHUFFLE(0,0,0,0)); + q1 = _mm_cvtsi32_si128(qlp_coeff[1]); q1 = _mm_shuffle_epi32(q1, _MM_SHUFFLE(0,0,0,0)); + q2 = _mm_cvtsi32_si128(qlp_coeff[2]); q2 = _mm_shuffle_epi32(q2, _MM_SHUFFLE(0,0,0,0)); + q3 = _mm_cvtsi32_si128(qlp_coeff[3]); q3 = _mm_shuffle_epi32(q3, _MM_SHUFFLE(0,0,0,0)); + q4 = _mm_cvtsi32_si128(qlp_coeff[4]); q4 = _mm_shuffle_epi32(q4, _MM_SHUFFLE(0,0,0,0)); + q5 = _mm_cvtsi32_si128(qlp_coeff[5]); q5 = _mm_shuffle_epi32(q5, _MM_SHUFFLE(0,0,0,0)); + q6 = _mm_cvtsi32_si128(qlp_coeff[6]); q6 = _mm_shuffle_epi32(q6, _MM_SHUFFLE(0,0,0,0)); + + for(i = 0; i < (int)data_len-3; i+=4) { + __m128i summ, mull; + summ = _mm_mullo_epi32(q6, _mm_loadu_si128((const __m128i*)(data+i-7))); + mull = _mm_mullo_epi32(q5, _mm_loadu_si128((const __m128i*)(data+i-6))); summ = _mm_add_epi32(summ, mull); + mull = _mm_mullo_epi32(q4, _mm_loadu_si128((const __m128i*)(data+i-5))); summ = _mm_add_epi32(summ, mull); + mull = _mm_mullo_epi32(q3, _mm_loadu_si128((const __m128i*)(data+i-4))); summ = _mm_add_epi32(summ, mull); + mull = _mm_mullo_epi32(q2, _mm_loadu_si128((const __m128i*)(data+i-3))); summ = _mm_add_epi32(summ, mull); + mull = _mm_mullo_epi32(q1, _mm_loadu_si128((const __m128i*)(data+i-2))); summ = _mm_add_epi32(summ, mull); + mull = _mm_mullo_epi32(q0, _mm_loadu_si128((const __m128i*)(data+i-1))); summ = _mm_add_epi32(summ, mull); + summ = _mm_sra_epi32(summ, cnt); + _mm_storeu_si128((__m128i*)(residual+i), _mm_sub_epi32(_mm_loadu_si128((const __m128i*)(data+i)), summ)); + } + } + } + else { + if(order == 6) { + __m128i q0, q1, q2, q3, q4, q5; + q0 = _mm_cvtsi32_si128(qlp_coeff[0]); q0 = _mm_shuffle_epi32(q0, _MM_SHUFFLE(0,0,0,0)); + q1 = _mm_cvtsi32_si128(qlp_coeff[1]); q1 = _mm_shuffle_epi32(q1, _MM_SHUFFLE(0,0,0,0)); + q2 = _mm_cvtsi32_si128(qlp_coeff[2]); q2 = _mm_shuffle_epi32(q2, _MM_SHUFFLE(0,0,0,0)); + q3 = _mm_cvtsi32_si128(qlp_coeff[3]); q3 = _mm_shuffle_epi32(q3, _MM_SHUFFLE(0,0,0,0)); + q4 = _mm_cvtsi32_si128(qlp_coeff[4]); q4 = _mm_shuffle_epi32(q4, _MM_SHUFFLE(0,0,0,0)); + q5 = _mm_cvtsi32_si128(qlp_coeff[5]); q5 = _mm_shuffle_epi32(q5, _MM_SHUFFLE(0,0,0,0)); + + for(i = 0; i < (int)data_len-3; i+=4) { + __m128i summ, mull; + summ = _mm_mullo_epi32(q5, _mm_loadu_si128((const __m128i*)(data+i-6))); + mull = _mm_mullo_epi32(q4, _mm_loadu_si128((const __m128i*)(data+i-5))); summ = _mm_add_epi32(summ, mull); + mull = _mm_mullo_epi32(q3, _mm_loadu_si128((const __m128i*)(data+i-4))); summ = _mm_add_epi32(summ, mull); + mull = _mm_mullo_epi32(q2, _mm_loadu_si128((const __m128i*)(data+i-3))); summ = _mm_add_epi32(summ, mull); + mull = _mm_mullo_epi32(q1, _mm_loadu_si128((const __m128i*)(data+i-2))); summ = _mm_add_epi32(summ, mull); + mull = _mm_mullo_epi32(q0, _mm_loadu_si128((const __m128i*)(data+i-1))); summ = _mm_add_epi32(summ, mull); + summ = _mm_sra_epi32(summ, cnt); + _mm_storeu_si128((__m128i*)(residual+i), _mm_sub_epi32(_mm_loadu_si128((const __m128i*)(data+i)), summ)); + } + } + else { /* order == 5 */ + __m128i q0, q1, q2, q3, q4; + q0 = _mm_cvtsi32_si128(qlp_coeff[0]); q0 = _mm_shuffle_epi32(q0, _MM_SHUFFLE(0,0,0,0)); + q1 = _mm_cvtsi32_si128(qlp_coeff[1]); q1 = _mm_shuffle_epi32(q1, _MM_SHUFFLE(0,0,0,0)); + q2 = _mm_cvtsi32_si128(qlp_coeff[2]); q2 = _mm_shuffle_epi32(q2, _MM_SHUFFLE(0,0,0,0)); + q3 = _mm_cvtsi32_si128(qlp_coeff[3]); q3 = _mm_shuffle_epi32(q3, _MM_SHUFFLE(0,0,0,0)); + q4 = _mm_cvtsi32_si128(qlp_coeff[4]); q4 = _mm_shuffle_epi32(q4, _MM_SHUFFLE(0,0,0,0)); + + for(i = 0; i < (int)data_len-3; i+=4) { + __m128i summ, mull; + summ = _mm_mullo_epi32(q4, _mm_loadu_si128((const __m128i*)(data+i-5))); + mull = _mm_mullo_epi32(q3, _mm_loadu_si128((const __m128i*)(data+i-4))); summ = _mm_add_epi32(summ, mull); + mull = _mm_mullo_epi32(q2, _mm_loadu_si128((const __m128i*)(data+i-3))); summ = _mm_add_epi32(summ, mull); + mull = _mm_mullo_epi32(q1, _mm_loadu_si128((const __m128i*)(data+i-2))); summ = _mm_add_epi32(summ, mull); + mull = _mm_mullo_epi32(q0, _mm_loadu_si128((const __m128i*)(data+i-1))); summ = _mm_add_epi32(summ, mull); + summ = _mm_sra_epi32(summ, cnt); + _mm_storeu_si128((__m128i*)(residual+i), _mm_sub_epi32(_mm_loadu_si128((const __m128i*)(data+i)), summ)); + } + } + } + } + else { + if(order > 2) { + if(order == 4) { + __m128i q0, q1, q2, q3; + q0 = _mm_cvtsi32_si128(qlp_coeff[0]); q0 = _mm_shuffle_epi32(q0, _MM_SHUFFLE(0,0,0,0)); + q1 = _mm_cvtsi32_si128(qlp_coeff[1]); q1 = _mm_shuffle_epi32(q1, _MM_SHUFFLE(0,0,0,0)); + q2 = _mm_cvtsi32_si128(qlp_coeff[2]); q2 = _mm_shuffle_epi32(q2, _MM_SHUFFLE(0,0,0,0)); + q3 = _mm_cvtsi32_si128(qlp_coeff[3]); q3 = _mm_shuffle_epi32(q3, _MM_SHUFFLE(0,0,0,0)); + + for(i = 0; i < (int)data_len-3; i+=4) { + __m128i summ, mull; + summ = _mm_mullo_epi32(q3, _mm_loadu_si128((const __m128i*)(data+i-4))); + mull = _mm_mullo_epi32(q2, _mm_loadu_si128((const __m128i*)(data+i-3))); summ = _mm_add_epi32(summ, mull); + mull = _mm_mullo_epi32(q1, _mm_loadu_si128((const __m128i*)(data+i-2))); summ = _mm_add_epi32(summ, mull); + mull = _mm_mullo_epi32(q0, _mm_loadu_si128((const __m128i*)(data+i-1))); summ = _mm_add_epi32(summ, mull); + summ = _mm_sra_epi32(summ, cnt); + _mm_storeu_si128((__m128i*)(residual+i), _mm_sub_epi32(_mm_loadu_si128((const __m128i*)(data+i)), summ)); + } + } + else { /* order == 3 */ + __m128i q0, q1, q2; + q0 = _mm_cvtsi32_si128(qlp_coeff[0]); q0 = _mm_shuffle_epi32(q0, _MM_SHUFFLE(0,0,0,0)); + q1 = _mm_cvtsi32_si128(qlp_coeff[1]); q1 = _mm_shuffle_epi32(q1, _MM_SHUFFLE(0,0,0,0)); + q2 = _mm_cvtsi32_si128(qlp_coeff[2]); q2 = _mm_shuffle_epi32(q2, _MM_SHUFFLE(0,0,0,0)); + + for(i = 0; i < (int)data_len-3; i+=4) { + __m128i summ, mull; + summ = _mm_mullo_epi32(q2, _mm_loadu_si128((const __m128i*)(data+i-3))); + mull = _mm_mullo_epi32(q1, _mm_loadu_si128((const __m128i*)(data+i-2))); summ = _mm_add_epi32(summ, mull); + mull = _mm_mullo_epi32(q0, _mm_loadu_si128((const __m128i*)(data+i-1))); summ = _mm_add_epi32(summ, mull); + summ = _mm_sra_epi32(summ, cnt); + _mm_storeu_si128((__m128i*)(residual+i), _mm_sub_epi32(_mm_loadu_si128((const __m128i*)(data+i)), summ)); + } + } + } + else { + if(order == 2) { + __m128i q0, q1; + q0 = _mm_cvtsi32_si128(qlp_coeff[0]); q0 = _mm_shuffle_epi32(q0, _MM_SHUFFLE(0,0,0,0)); + q1 = _mm_cvtsi32_si128(qlp_coeff[1]); q1 = _mm_shuffle_epi32(q1, _MM_SHUFFLE(0,0,0,0)); + + for(i = 0; i < (int)data_len-3; i+=4) { + __m128i summ, mull; + summ = _mm_mullo_epi32(q1, _mm_loadu_si128((const __m128i*)(data+i-2))); + mull = _mm_mullo_epi32(q0, _mm_loadu_si128((const __m128i*)(data+i-1))); summ = _mm_add_epi32(summ, mull); + summ = _mm_sra_epi32(summ, cnt); + _mm_storeu_si128((__m128i*)(residual+i), _mm_sub_epi32(_mm_loadu_si128((const __m128i*)(data+i)), summ)); + } + } + else { /* order == 1 */ + __m128i q0; + q0 = _mm_cvtsi32_si128(qlp_coeff[0]); q0 = _mm_shuffle_epi32(q0, _MM_SHUFFLE(0,0,0,0)); + + for(i = 0; i < (int)data_len-3; i+=4) { + __m128i summ; + summ = _mm_mullo_epi32(q0, _mm_loadu_si128((const __m128i*)(data+i-1))); + summ = _mm_sra_epi32(summ, cnt); + _mm_storeu_si128((__m128i*)(residual+i), _mm_sub_epi32(_mm_loadu_si128((const __m128i*)(data+i)), summ)); + } + } + } + } + for(; i < (int)data_len; i++) { + sum = 0; + switch(order) { + case 12: sum += qlp_coeff[11] * data[i-12]; + case 11: sum += qlp_coeff[10] * data[i-11]; + case 10: sum += qlp_coeff[ 9] * data[i-10]; + case 9: sum += qlp_coeff[ 8] * data[i- 9]; + case 8: sum += qlp_coeff[ 7] * data[i- 8]; + case 7: sum += qlp_coeff[ 6] * data[i- 7]; + case 6: sum += qlp_coeff[ 5] * data[i- 6]; + case 5: sum += qlp_coeff[ 4] * data[i- 5]; + case 4: sum += qlp_coeff[ 3] * data[i- 4]; + case 3: sum += qlp_coeff[ 2] * data[i- 3]; + case 2: sum += qlp_coeff[ 1] * data[i- 2]; + case 1: sum += qlp_coeff[ 0] * data[i- 1]; + } + residual[i] = data[i] - (sum >> lp_quantization); + } + } + else { /* order > 12 */ + for(i = 0; i < (int)data_len; i++) { + sum = 0; + switch(order) { + case 32: sum += qlp_coeff[31] * data[i-32]; + case 31: sum += qlp_coeff[30] * data[i-31]; + case 30: sum += qlp_coeff[29] * data[i-30]; + case 29: sum += qlp_coeff[28] * data[i-29]; + case 28: sum += qlp_coeff[27] * data[i-28]; + case 27: sum += qlp_coeff[26] * data[i-27]; + case 26: sum += qlp_coeff[25] * data[i-26]; + case 25: sum += qlp_coeff[24] * data[i-25]; + case 24: sum += qlp_coeff[23] * data[i-24]; + case 23: sum += qlp_coeff[22] * data[i-23]; + case 22: sum += qlp_coeff[21] * data[i-22]; + case 21: sum += qlp_coeff[20] * data[i-21]; + case 20: sum += qlp_coeff[19] * data[i-20]; + case 19: sum += qlp_coeff[18] * data[i-19]; + case 18: sum += qlp_coeff[17] * data[i-18]; + case 17: sum += qlp_coeff[16] * data[i-17]; + case 16: sum += qlp_coeff[15] * data[i-16]; + case 15: sum += qlp_coeff[14] * data[i-15]; + case 14: sum += qlp_coeff[13] * data[i-14]; + case 13: sum += qlp_coeff[12] * data[i-13]; + sum += qlp_coeff[11] * data[i-12]; + sum += qlp_coeff[10] * data[i-11]; + sum += qlp_coeff[ 9] * data[i-10]; + sum += qlp_coeff[ 8] * data[i- 9]; + sum += qlp_coeff[ 7] * data[i- 8]; + sum += qlp_coeff[ 6] * data[i- 7]; + sum += qlp_coeff[ 5] * data[i- 6]; + sum += qlp_coeff[ 4] * data[i- 5]; + sum += qlp_coeff[ 3] * data[i- 4]; + sum += qlp_coeff[ 2] * data[i- 3]; + sum += qlp_coeff[ 1] * data[i- 2]; + sum += qlp_coeff[ 0] * data[i- 1]; + } + residual[i] = data[i] - (sum >> lp_quantization); + } + } +} + +#endif /* FLAC__SSE4_1_SUPPORTED */ +#endif /* (FLAC__CPU_IA32 || FLAC__CPU_X86_64) && FLAC__HAS_X86INTRIN */ +#endif /* FLAC__NO_ASM */ +#endif /* FLAC__INTEGER_ONLY_LIBRARY */ diff --git a/core/deps/flac/src/libFLAC/md5.c b/core/deps/flac/src/libFLAC/md5.c new file mode 100644 index 000000000..e9013a9a3 --- /dev/null +++ b/core/deps/flac/src/libFLAC/md5.c @@ -0,0 +1,516 @@ +#ifdef HAVE_CONFIG_H +# include +#endif + +#include /* for malloc() */ +#include /* for memcpy() */ + +#include "private/md5.h" +#include "share/alloc.h" +#include "share/endswap.h" + +/* + * This code implements the MD5 message-digest algorithm. + * The algorithm is due to Ron Rivest. This code was + * written by Colin Plumb in 1993, no copyright is claimed. + * This code is in the public domain; do with it what you wish. + * + * Equivalent code is available from RSA Data Security, Inc. + * This code has been tested against that, and is equivalent, + * except that you don't need to include two pages of legalese + * with every copy. + * + * To compute the message digest of a chunk of bytes, declare an + * MD5Context structure, pass it to MD5Init, call MD5Update as + * needed on buffers full of bytes, and then call MD5Final, which + * will fill a supplied 16-byte array with the digest. + * + * Changed so as no longer to depend on Colin Plumb's `usual.h' header + * definitions; now uses stuff from dpkg's config.h. + * - Ian Jackson . + * Still in the public domain. + * + * Josh Coalson: made some changes to integrate with libFLAC. + * Still in the public domain. + */ + +/* The four core functions - F1 is optimized somewhat */ + +/* #define F1(x, y, z) (x & y | ~x & z) */ +#define F1(x, y, z) (z ^ (x & (y ^ z))) +#define F2(x, y, z) F1(z, x, y) +#define F3(x, y, z) (x ^ y ^ z) +#define F4(x, y, z) (y ^ (x | ~z)) + +/* This is the central step in the MD5 algorithm. */ +#define MD5STEP(f,w,x,y,z,in,s) \ + (w += f(x,y,z) + in, w = (w<>(32-s)) + x) + +/* + * The core of the MD5 algorithm, this alters an existing MD5 hash to + * reflect the addition of 16 longwords of new data. MD5Update blocks + * the data and converts bytes into longwords for this routine. + */ +static void FLAC__MD5Transform(FLAC__uint32 buf[4], FLAC__uint32 const in[16]) +{ + register FLAC__uint32 a, b, c, d; + + a = buf[0]; + b = buf[1]; + c = buf[2]; + d = buf[3]; + + MD5STEP(F1, a, b, c, d, in[0] + 0xd76aa478, 7); + MD5STEP(F1, d, a, b, c, in[1] + 0xe8c7b756, 12); + MD5STEP(F1, c, d, a, b, in[2] + 0x242070db, 17); + MD5STEP(F1, b, c, d, a, in[3] + 0xc1bdceee, 22); + MD5STEP(F1, a, b, c, d, in[4] + 0xf57c0faf, 7); + MD5STEP(F1, d, a, b, c, in[5] + 0x4787c62a, 12); + MD5STEP(F1, c, d, a, b, in[6] + 0xa8304613, 17); + MD5STEP(F1, b, c, d, a, in[7] + 0xfd469501, 22); + MD5STEP(F1, a, b, c, d, in[8] + 0x698098d8, 7); + MD5STEP(F1, d, a, b, c, in[9] + 0x8b44f7af, 12); + MD5STEP(F1, c, d, a, b, in[10] + 0xffff5bb1, 17); + MD5STEP(F1, b, c, d, a, in[11] + 0x895cd7be, 22); + MD5STEP(F1, a, b, c, d, in[12] + 0x6b901122, 7); + MD5STEP(F1, d, a, b, c, in[13] + 0xfd987193, 12); + MD5STEP(F1, c, d, a, b, in[14] + 0xa679438e, 17); + MD5STEP(F1, b, c, d, a, in[15] + 0x49b40821, 22); + + MD5STEP(F2, a, b, c, d, in[1] + 0xf61e2562, 5); + MD5STEP(F2, d, a, b, c, in[6] + 0xc040b340, 9); + MD5STEP(F2, c, d, a, b, in[11] + 0x265e5a51, 14); + MD5STEP(F2, b, c, d, a, in[0] + 0xe9b6c7aa, 20); + MD5STEP(F2, a, b, c, d, in[5] + 0xd62f105d, 5); + MD5STEP(F2, d, a, b, c, in[10] + 0x02441453, 9); + MD5STEP(F2, c, d, a, b, in[15] + 0xd8a1e681, 14); + MD5STEP(F2, b, c, d, a, in[4] + 0xe7d3fbc8, 20); + MD5STEP(F2, a, b, c, d, in[9] + 0x21e1cde6, 5); + MD5STEP(F2, d, a, b, c, in[14] + 0xc33707d6, 9); + MD5STEP(F2, c, d, a, b, in[3] + 0xf4d50d87, 14); + MD5STEP(F2, b, c, d, a, in[8] + 0x455a14ed, 20); + MD5STEP(F2, a, b, c, d, in[13] + 0xa9e3e905, 5); + MD5STEP(F2, d, a, b, c, in[2] + 0xfcefa3f8, 9); + MD5STEP(F2, c, d, a, b, in[7] + 0x676f02d9, 14); + MD5STEP(F2, b, c, d, a, in[12] + 0x8d2a4c8a, 20); + + MD5STEP(F3, a, b, c, d, in[5] + 0xfffa3942, 4); + MD5STEP(F3, d, a, b, c, in[8] + 0x8771f681, 11); + MD5STEP(F3, c, d, a, b, in[11] + 0x6d9d6122, 16); + MD5STEP(F3, b, c, d, a, in[14] + 0xfde5380c, 23); + MD5STEP(F3, a, b, c, d, in[1] + 0xa4beea44, 4); + MD5STEP(F3, d, a, b, c, in[4] + 0x4bdecfa9, 11); + MD5STEP(F3, c, d, a, b, in[7] + 0xf6bb4b60, 16); + MD5STEP(F3, b, c, d, a, in[10] + 0xbebfbc70, 23); + MD5STEP(F3, a, b, c, d, in[13] + 0x289b7ec6, 4); + MD5STEP(F3, d, a, b, c, in[0] + 0xeaa127fa, 11); + MD5STEP(F3, c, d, a, b, in[3] + 0xd4ef3085, 16); + MD5STEP(F3, b, c, d, a, in[6] + 0x04881d05, 23); + MD5STEP(F3, a, b, c, d, in[9] + 0xd9d4d039, 4); + MD5STEP(F3, d, a, b, c, in[12] + 0xe6db99e5, 11); + MD5STEP(F3, c, d, a, b, in[15] + 0x1fa27cf8, 16); + MD5STEP(F3, b, c, d, a, in[2] + 0xc4ac5665, 23); + + MD5STEP(F4, a, b, c, d, in[0] + 0xf4292244, 6); + MD5STEP(F4, d, a, b, c, in[7] + 0x432aff97, 10); + MD5STEP(F4, c, d, a, b, in[14] + 0xab9423a7, 15); + MD5STEP(F4, b, c, d, a, in[5] + 0xfc93a039, 21); + MD5STEP(F4, a, b, c, d, in[12] + 0x655b59c3, 6); + MD5STEP(F4, d, a, b, c, in[3] + 0x8f0ccc92, 10); + MD5STEP(F4, c, d, a, b, in[10] + 0xffeff47d, 15); + MD5STEP(F4, b, c, d, a, in[1] + 0x85845dd1, 21); + MD5STEP(F4, a, b, c, d, in[8] + 0x6fa87e4f, 6); + MD5STEP(F4, d, a, b, c, in[15] + 0xfe2ce6e0, 10); + MD5STEP(F4, c, d, a, b, in[6] + 0xa3014314, 15); + MD5STEP(F4, b, c, d, a, in[13] + 0x4e0811a1, 21); + MD5STEP(F4, a, b, c, d, in[4] + 0xf7537e82, 6); + MD5STEP(F4, d, a, b, c, in[11] + 0xbd3af235, 10); + MD5STEP(F4, c, d, a, b, in[2] + 0x2ad7d2bb, 15); + MD5STEP(F4, b, c, d, a, in[9] + 0xeb86d391, 21); + + buf[0] += a; + buf[1] += b; + buf[2] += c; + buf[3] += d; +} + +#if WORDS_BIGENDIAN +//@@@@@@ OPT: use bswap/intrinsics +static void byteSwap(FLAC__uint32 *buf, unsigned words) +{ + register FLAC__uint32 x; + do { + x = *buf; + x = ((x << 8) & 0xff00ff00) | ((x >> 8) & 0x00ff00ff); + *buf++ = (x >> 16) | (x << 16); + } while (--words); +} +static void byteSwapX16(FLAC__uint32 *buf) +{ + register FLAC__uint32 x; + + x = *buf; x = ((x << 8) & 0xff00ff00) | ((x >> 8) & 0x00ff00ff); *buf++ = (x >> 16) | (x << 16); + x = *buf; x = ((x << 8) & 0xff00ff00) | ((x >> 8) & 0x00ff00ff); *buf++ = (x >> 16) | (x << 16); + x = *buf; x = ((x << 8) & 0xff00ff00) | ((x >> 8) & 0x00ff00ff); *buf++ = (x >> 16) | (x << 16); + x = *buf; x = ((x << 8) & 0xff00ff00) | ((x >> 8) & 0x00ff00ff); *buf++ = (x >> 16) | (x << 16); + x = *buf; x = ((x << 8) & 0xff00ff00) | ((x >> 8) & 0x00ff00ff); *buf++ = (x >> 16) | (x << 16); + x = *buf; x = ((x << 8) & 0xff00ff00) | ((x >> 8) & 0x00ff00ff); *buf++ = (x >> 16) | (x << 16); + x = *buf; x = ((x << 8) & 0xff00ff00) | ((x >> 8) & 0x00ff00ff); *buf++ = (x >> 16) | (x << 16); + x = *buf; x = ((x << 8) & 0xff00ff00) | ((x >> 8) & 0x00ff00ff); *buf++ = (x >> 16) | (x << 16); + x = *buf; x = ((x << 8) & 0xff00ff00) | ((x >> 8) & 0x00ff00ff); *buf++ = (x >> 16) | (x << 16); + x = *buf; x = ((x << 8) & 0xff00ff00) | ((x >> 8) & 0x00ff00ff); *buf++ = (x >> 16) | (x << 16); + x = *buf; x = ((x << 8) & 0xff00ff00) | ((x >> 8) & 0x00ff00ff); *buf++ = (x >> 16) | (x << 16); + x = *buf; x = ((x << 8) & 0xff00ff00) | ((x >> 8) & 0x00ff00ff); *buf++ = (x >> 16) | (x << 16); + x = *buf; x = ((x << 8) & 0xff00ff00) | ((x >> 8) & 0x00ff00ff); *buf++ = (x >> 16) | (x << 16); + x = *buf; x = ((x << 8) & 0xff00ff00) | ((x >> 8) & 0x00ff00ff); *buf++ = (x >> 16) | (x << 16); + x = *buf; x = ((x << 8) & 0xff00ff00) | ((x >> 8) & 0x00ff00ff); *buf++ = (x >> 16) | (x << 16); + x = *buf; x = ((x << 8) & 0xff00ff00) | ((x >> 8) & 0x00ff00ff); *buf = (x >> 16) | (x << 16); +} +#else +#define byteSwap(buf, words) +#define byteSwapX16(buf) +#endif + +/* + * Update context to reflect the concatenation of another buffer full + * of bytes. + */ +static void FLAC__MD5Update(FLAC__MD5Context *ctx, FLAC__byte const *buf, unsigned len) +{ + FLAC__uint32 t; + + /* Update byte count */ + + t = ctx->bytes[0]; + if ((ctx->bytes[0] = t + len) < t) + ctx->bytes[1]++; /* Carry from low to high */ + + t = 64 - (t & 0x3f); /* Space available in ctx->in (at least 1) */ + if (t > len) { + memcpy((FLAC__byte *)ctx->in + 64 - t, buf, len); + return; + } + /* First chunk is an odd size */ + memcpy((FLAC__byte *)ctx->in + 64 - t, buf, t); + byteSwapX16(ctx->in); + FLAC__MD5Transform(ctx->buf, ctx->in); + buf += t; + len -= t; + + /* Process data in 64-byte chunks */ + while (len >= 64) { + memcpy(ctx->in, buf, 64); + byteSwapX16(ctx->in); + FLAC__MD5Transform(ctx->buf, ctx->in); + buf += 64; + len -= 64; + } + + /* Handle any remaining bytes of data. */ + memcpy(ctx->in, buf, len); +} + +/* + * Start MD5 accumulation. Set bit count to 0 and buffer to mysterious + * initialization constants. + */ +void FLAC__MD5Init(FLAC__MD5Context *ctx) +{ + ctx->buf[0] = 0x67452301; + ctx->buf[1] = 0xefcdab89; + ctx->buf[2] = 0x98badcfe; + ctx->buf[3] = 0x10325476; + + ctx->bytes[0] = 0; + ctx->bytes[1] = 0; + + ctx->internal_buf.p8 = 0; + ctx->capacity = 0; +} + +/* + * Final wrapup - pad to 64-byte boundary with the bit pattern + * 1 0* (64-bit count of bits processed, MSB-first) + */ +void FLAC__MD5Final(FLAC__byte digest[16], FLAC__MD5Context *ctx) +{ + int count = ctx->bytes[0] & 0x3f; /* Number of bytes in ctx->in */ + FLAC__byte *p = (FLAC__byte *)ctx->in + count; + + /* Set the first char of padding to 0x80. There is always room. */ + *p++ = 0x80; + + /* Bytes of padding needed to make 56 bytes (-8..55) */ + count = 56 - 1 - count; + + if (count < 0) { /* Padding forces an extra block */ + memset(p, 0, count + 8); + byteSwapX16(ctx->in); + FLAC__MD5Transform(ctx->buf, ctx->in); + p = (FLAC__byte *)ctx->in; + count = 56; + } + memset(p, 0, count); + byteSwap(ctx->in, 14); + + /* Append length in bits and transform */ + ctx->in[14] = ctx->bytes[0] << 3; + ctx->in[15] = ctx->bytes[1] << 3 | ctx->bytes[0] >> 29; + FLAC__MD5Transform(ctx->buf, ctx->in); + + byteSwap(ctx->buf, 4); + memcpy(digest, ctx->buf, 16); + if (0 != ctx->internal_buf.p8) { + free(ctx->internal_buf.p8); + ctx->internal_buf.p8 = 0; + ctx->capacity = 0; + } + memset(ctx, 0, sizeof(*ctx)); /* In case it's sensitive */ +} + +/* + * Convert the incoming audio signal to a byte stream + */ +static void format_input_(FLAC__multibyte *mbuf, const FLAC__int32 * const signal[], unsigned channels, unsigned samples, unsigned bytes_per_sample) +{ + FLAC__byte *buf_ = mbuf->p8; + FLAC__int16 *buf16 = mbuf->p16; + FLAC__int32 *buf32 = mbuf->p32; + FLAC__int32 a_word; + unsigned channel, sample; + + /* Storage in the output buffer, buf, is little endian. */ + +#define BYTES_CHANNEL_SELECTOR(bytes, channels) (bytes * 100 + channels) + + /* First do the most commonly used combinations. */ + switch (BYTES_CHANNEL_SELECTOR (bytes_per_sample, channels)) { + /* One byte per sample. */ + case (BYTES_CHANNEL_SELECTOR (1, 1)): + for (sample = 0; sample < samples; sample++) + *buf_++ = signal[0][sample]; + return; + + case (BYTES_CHANNEL_SELECTOR (1, 2)): + for (sample = 0; sample < samples; sample++) { + *buf_++ = signal[0][sample]; + *buf_++ = signal[1][sample]; + } + return; + + case (BYTES_CHANNEL_SELECTOR (1, 4)): + for (sample = 0; sample < samples; sample++) { + *buf_++ = signal[0][sample]; + *buf_++ = signal[1][sample]; + *buf_++ = signal[2][sample]; + *buf_++ = signal[3][sample]; + } + return; + + case (BYTES_CHANNEL_SELECTOR (1, 6)): + for (sample = 0; sample < samples; sample++) { + *buf_++ = signal[0][sample]; + *buf_++ = signal[1][sample]; + *buf_++ = signal[2][sample]; + *buf_++ = signal[3][sample]; + *buf_++ = signal[4][sample]; + *buf_++ = signal[5][sample]; + } + return; + + case (BYTES_CHANNEL_SELECTOR (1, 8)): + for (sample = 0; sample < samples; sample++) { + *buf_++ = signal[0][sample]; + *buf_++ = signal[1][sample]; + *buf_++ = signal[2][sample]; + *buf_++ = signal[3][sample]; + *buf_++ = signal[4][sample]; + *buf_++ = signal[5][sample]; + *buf_++ = signal[6][sample]; + *buf_++ = signal[7][sample]; + } + return; + + /* Two bytes per sample. */ + case (BYTES_CHANNEL_SELECTOR (2, 1)): + for (sample = 0; sample < samples; sample++) + *buf16++ = H2LE_16(signal[0][sample]); + return; + + case (BYTES_CHANNEL_SELECTOR (2, 2)): + for (sample = 0; sample < samples; sample++) { + *buf16++ = H2LE_16(signal[0][sample]); + *buf16++ = H2LE_16(signal[1][sample]); + } + return; + + case (BYTES_CHANNEL_SELECTOR (2, 4)): + for (sample = 0; sample < samples; sample++) { + *buf16++ = H2LE_16(signal[0][sample]); + *buf16++ = H2LE_16(signal[1][sample]); + *buf16++ = H2LE_16(signal[2][sample]); + *buf16++ = H2LE_16(signal[3][sample]); + } + return; + + case (BYTES_CHANNEL_SELECTOR (2, 6)): + for (sample = 0; sample < samples; sample++) { + *buf16++ = H2LE_16(signal[0][sample]); + *buf16++ = H2LE_16(signal[1][sample]); + *buf16++ = H2LE_16(signal[2][sample]); + *buf16++ = H2LE_16(signal[3][sample]); + *buf16++ = H2LE_16(signal[4][sample]); + *buf16++ = H2LE_16(signal[5][sample]); + } + return; + + case (BYTES_CHANNEL_SELECTOR (2, 8)): + for (sample = 0; sample < samples; sample++) { + *buf16++ = H2LE_16(signal[0][sample]); + *buf16++ = H2LE_16(signal[1][sample]); + *buf16++ = H2LE_16(signal[2][sample]); + *buf16++ = H2LE_16(signal[3][sample]); + *buf16++ = H2LE_16(signal[4][sample]); + *buf16++ = H2LE_16(signal[5][sample]); + *buf16++ = H2LE_16(signal[6][sample]); + *buf16++ = H2LE_16(signal[7][sample]); + } + return; + + /* Three bytes per sample. */ + case (BYTES_CHANNEL_SELECTOR (3, 1)): + for (sample = 0; sample < samples; sample++) { + a_word = signal[0][sample]; + *buf_++ = (FLAC__byte)a_word; a_word >>= 8; + *buf_++ = (FLAC__byte)a_word; a_word >>= 8; + *buf_++ = (FLAC__byte)a_word; + } + return; + + case (BYTES_CHANNEL_SELECTOR (3, 2)): + for (sample = 0; sample < samples; sample++) { + a_word = signal[0][sample]; + *buf_++ = (FLAC__byte)a_word; a_word >>= 8; + *buf_++ = (FLAC__byte)a_word; a_word >>= 8; + *buf_++ = (FLAC__byte)a_word; + a_word = signal[1][sample]; + *buf_++ = (FLAC__byte)a_word; a_word >>= 8; + *buf_++ = (FLAC__byte)a_word; a_word >>= 8; + *buf_++ = (FLAC__byte)a_word; + } + return; + + /* Four bytes per sample. */ + case (BYTES_CHANNEL_SELECTOR (4, 1)): + for (sample = 0; sample < samples; sample++) + *buf32++ = H2LE_32(signal[0][sample]); + return; + + case (BYTES_CHANNEL_SELECTOR (4, 2)): + for (sample = 0; sample < samples; sample++) { + *buf32++ = H2LE_32(signal[0][sample]); + *buf32++ = H2LE_32(signal[1][sample]); + } + return; + + case (BYTES_CHANNEL_SELECTOR (4, 4)): + for (sample = 0; sample < samples; sample++) { + *buf32++ = H2LE_32(signal[0][sample]); + *buf32++ = H2LE_32(signal[1][sample]); + *buf32++ = H2LE_32(signal[2][sample]); + *buf32++ = H2LE_32(signal[3][sample]); + } + return; + + case (BYTES_CHANNEL_SELECTOR (4, 6)): + for (sample = 0; sample < samples; sample++) { + *buf32++ = H2LE_32(signal[0][sample]); + *buf32++ = H2LE_32(signal[1][sample]); + *buf32++ = H2LE_32(signal[2][sample]); + *buf32++ = H2LE_32(signal[3][sample]); + *buf32++ = H2LE_32(signal[4][sample]); + *buf32++ = H2LE_32(signal[5][sample]); + } + return; + + case (BYTES_CHANNEL_SELECTOR (4, 8)): + for (sample = 0; sample < samples; sample++) { + *buf32++ = H2LE_32(signal[0][sample]); + *buf32++ = H2LE_32(signal[1][sample]); + *buf32++ = H2LE_32(signal[2][sample]); + *buf32++ = H2LE_32(signal[3][sample]); + *buf32++ = H2LE_32(signal[4][sample]); + *buf32++ = H2LE_32(signal[5][sample]); + *buf32++ = H2LE_32(signal[6][sample]); + *buf32++ = H2LE_32(signal[7][sample]); + } + return; + + default: + break; + } + + /* General version. */ + switch (bytes_per_sample) { + case 1: + for (sample = 0; sample < samples; sample++) + for (channel = 0; channel < channels; channel++) + *buf_++ = signal[channel][sample]; + return; + + case 2: + for (sample = 0; sample < samples; sample++) + for (channel = 0; channel < channels; channel++) + *buf16++ = H2LE_16(signal[channel][sample]); + return; + + case 3: + for (sample = 0; sample < samples; sample++) + for (channel = 0; channel < channels; channel++) { + a_word = signal[channel][sample]; + *buf_++ = (FLAC__byte)a_word; a_word >>= 8; + *buf_++ = (FLAC__byte)a_word; a_word >>= 8; + *buf_++ = (FLAC__byte)a_word; + } + return; + + case 4: + for (sample = 0; sample < samples; sample++) + for (channel = 0; channel < channels; channel++) + *buf32++ = H2LE_32(signal[channel][sample]); + return; + + default: + break; + } +} + +/* + * Convert the incoming audio signal to a byte stream and FLAC__MD5Update it. + */ +FLAC__bool FLAC__MD5Accumulate(FLAC__MD5Context *ctx, const FLAC__int32 * const signal[], unsigned channels, unsigned samples, unsigned bytes_per_sample) +{ + const size_t bytes_needed = (size_t)channels * (size_t)samples * (size_t)bytes_per_sample; + + /* overflow check */ + if ((size_t)channels > SIZE_MAX / (size_t)bytes_per_sample) + return false; + if ((size_t)channels * (size_t)bytes_per_sample > SIZE_MAX / (size_t)samples) + return false; + + if (ctx->capacity < bytes_needed) { + if (0 == (ctx->internal_buf.p8 = safe_realloc_(ctx->internal_buf.p8, bytes_needed))) { + if (0 == (ctx->internal_buf.p8 = safe_malloc_(bytes_needed))) { + ctx->capacity = 0; + return false; + } + } + ctx->capacity = bytes_needed; + } + + format_input_(&ctx->internal_buf, signal, channels, samples, bytes_per_sample); + + FLAC__MD5Update(ctx, ctx->internal_buf.p8, bytes_needed); + + return true; +} diff --git a/core/deps/flac/src/libFLAC/memory.c b/core/deps/flac/src/libFLAC/memory.c new file mode 100644 index 000000000..a8ebd10fb --- /dev/null +++ b/core/deps/flac/src/libFLAC/memory.c @@ -0,0 +1,218 @@ +/* libFLAC - Free Lossless Audio Codec library + * Copyright (C) 2001-2009 Josh Coalson + * Copyright (C) 2011-2016 Xiph.Org Foundation + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * - Neither the name of the Xiph.org Foundation nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifdef HAVE_CONFIG_H +# include +#endif + +#ifdef HAVE_STDINT_H +#include +#endif + +#include "private/memory.h" +#include "FLAC/assert.h" +#include "share/alloc.h" + +void *FLAC__memory_alloc_aligned(size_t bytes, void **aligned_address) +{ + void *x; + + FLAC__ASSERT(0 != aligned_address); + +#ifdef FLAC__ALIGN_MALLOC_DATA + /* align on 32-byte (256-bit) boundary */ + x = safe_malloc_add_2op_(bytes, /*+*/31L); + *aligned_address = (void*)(((uintptr_t)x + 31L) & -32L); +#else + x = safe_malloc_(bytes); + *aligned_address = x; +#endif + return x; +} + +FLAC__bool FLAC__memory_alloc_aligned_int32_array(size_t elements, FLAC__int32 **unaligned_pointer, FLAC__int32 **aligned_pointer) +{ + FLAC__int32 *pu; /* unaligned pointer */ + union { /* union needed to comply with C99 pointer aliasing rules */ + FLAC__int32 *pa; /* aligned pointer */ + void *pv; /* aligned pointer alias */ + } u; + + FLAC__ASSERT(elements > 0); + FLAC__ASSERT(0 != unaligned_pointer); + FLAC__ASSERT(0 != aligned_pointer); + FLAC__ASSERT(unaligned_pointer != aligned_pointer); + + if(elements > SIZE_MAX / sizeof(*pu)) /* overflow check */ + return false; + + pu = FLAC__memory_alloc_aligned(sizeof(*pu) * elements, &u.pv); + if(0 == pu) { + return false; + } + else { + if(*unaligned_pointer != 0) + free(*unaligned_pointer); + *unaligned_pointer = pu; + *aligned_pointer = u.pa; + return true; + } +} + +FLAC__bool FLAC__memory_alloc_aligned_uint32_array(size_t elements, FLAC__uint32 **unaligned_pointer, FLAC__uint32 **aligned_pointer) +{ + FLAC__uint32 *pu; /* unaligned pointer */ + union { /* union needed to comply with C99 pointer aliasing rules */ + FLAC__uint32 *pa; /* aligned pointer */ + void *pv; /* aligned pointer alias */ + } u; + + FLAC__ASSERT(elements > 0); + FLAC__ASSERT(0 != unaligned_pointer); + FLAC__ASSERT(0 != aligned_pointer); + FLAC__ASSERT(unaligned_pointer != aligned_pointer); + + if(elements > SIZE_MAX / sizeof(*pu)) /* overflow check */ + return false; + + pu = FLAC__memory_alloc_aligned(sizeof(*pu) * elements, &u.pv); + if(0 == pu) { + return false; + } + else { + if(*unaligned_pointer != 0) + free(*unaligned_pointer); + *unaligned_pointer = pu; + *aligned_pointer = u.pa; + return true; + } +} + +FLAC__bool FLAC__memory_alloc_aligned_uint64_array(size_t elements, FLAC__uint64 **unaligned_pointer, FLAC__uint64 **aligned_pointer) +{ + FLAC__uint64 *pu; /* unaligned pointer */ + union { /* union needed to comply with C99 pointer aliasing rules */ + FLAC__uint64 *pa; /* aligned pointer */ + void *pv; /* aligned pointer alias */ + } u; + + FLAC__ASSERT(elements > 0); + FLAC__ASSERT(0 != unaligned_pointer); + FLAC__ASSERT(0 != aligned_pointer); + FLAC__ASSERT(unaligned_pointer != aligned_pointer); + + if(elements > SIZE_MAX / sizeof(*pu)) /* overflow check */ + return false; + + pu = FLAC__memory_alloc_aligned(sizeof(*pu) * elements, &u.pv); + if(0 == pu) { + return false; + } + else { + if(*unaligned_pointer != 0) + free(*unaligned_pointer); + *unaligned_pointer = pu; + *aligned_pointer = u.pa; + return true; + } +} + +FLAC__bool FLAC__memory_alloc_aligned_unsigned_array(size_t elements, unsigned **unaligned_pointer, unsigned **aligned_pointer) +{ + unsigned *pu; /* unaligned pointer */ + union { /* union needed to comply with C99 pointer aliasing rules */ + unsigned *pa; /* aligned pointer */ + void *pv; /* aligned pointer alias */ + } u; + + FLAC__ASSERT(elements > 0); + FLAC__ASSERT(0 != unaligned_pointer); + FLAC__ASSERT(0 != aligned_pointer); + FLAC__ASSERT(unaligned_pointer != aligned_pointer); + + if(elements > SIZE_MAX / sizeof(*pu)) /* overflow check */ + return false; + + pu = FLAC__memory_alloc_aligned(sizeof(*pu) * elements, &u.pv); + if(0 == pu) { + return false; + } + else { + if(*unaligned_pointer != 0) + free(*unaligned_pointer); + *unaligned_pointer = pu; + *aligned_pointer = u.pa; + return true; + } +} + +#ifndef FLAC__INTEGER_ONLY_LIBRARY + +FLAC__bool FLAC__memory_alloc_aligned_real_array(size_t elements, FLAC__real **unaligned_pointer, FLAC__real **aligned_pointer) +{ + FLAC__real *pu; /* unaligned pointer */ + union { /* union needed to comply with C99 pointer aliasing rules */ + FLAC__real *pa; /* aligned pointer */ + void *pv; /* aligned pointer alias */ + } u; + + FLAC__ASSERT(elements > 0); + FLAC__ASSERT(0 != unaligned_pointer); + FLAC__ASSERT(0 != aligned_pointer); + FLAC__ASSERT(unaligned_pointer != aligned_pointer); + + if(elements > SIZE_MAX / sizeof(*pu)) /* overflow check */ + return false; + + pu = FLAC__memory_alloc_aligned(sizeof(*pu) * elements, &u.pv); + if(0 == pu) { + return false; + } + else { + if(*unaligned_pointer != 0) + free(*unaligned_pointer); + *unaligned_pointer = pu; + *aligned_pointer = u.pa; + return true; + } +} + +#endif + +void *safe_malloc_mul_2op_p(size_t size1, size_t size2) +{ + if(!size1 || !size2) + return malloc(1); /* malloc(0) is undefined; FLAC src convention is to always allocate */ + if(size1 > SIZE_MAX / size2) + return 0; + return malloc(size1*size2); +} diff --git a/core/deps/flac/src/libFLAC/metadata_iterators.c b/core/deps/flac/src/libFLAC/metadata_iterators.c new file mode 100644 index 000000000..0a84d03bc --- /dev/null +++ b/core/deps/flac/src/libFLAC/metadata_iterators.c @@ -0,0 +1,3481 @@ +/* libFLAC - Free Lossless Audio Codec library + * Copyright (C) 2001-2009 Josh Coalson + * Copyright (C) 2011-2016 Xiph.Org Foundation + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * - Neither the name of the Xiph.org Foundation nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifdef HAVE_CONFIG_H +# include +#endif + +#include +#include +#include +#include +#include + +#include /* for stat(), maybe chmod() */ + +#include "private/metadata.h" + +#include "FLAC/assert.h" +#include "FLAC/stream_decoder.h" +#include "share/alloc.h" +#include "share/compat.h" +#include "share/macros.h" +#include "share/safe_str.h" +#include "private/macros.h" +#include "private/memory.h" + +/* Alias the first (in share/alloc.h) to the second (in src/libFLAC/memory.c). */ +#define safe_malloc_mul_2op_ safe_malloc_mul_2op_p + +/**************************************************************************** + * + * Local function declarations + * + ***************************************************************************/ + +static void pack_uint32_(FLAC__uint32 val, FLAC__byte *b, unsigned bytes); +static void pack_uint32_little_endian_(FLAC__uint32 val, FLAC__byte *b, unsigned bytes); +static void pack_uint64_(FLAC__uint64 val, FLAC__byte *b, unsigned bytes); +static FLAC__uint32 unpack_uint32_(FLAC__byte *b, unsigned bytes); +static FLAC__uint32 unpack_uint32_little_endian_(FLAC__byte *b, unsigned bytes); +static FLAC__uint64 unpack_uint64_(FLAC__byte *b, unsigned bytes); + +static FLAC__bool read_metadata_block_header_(FLAC__Metadata_SimpleIterator *iterator); +static FLAC__bool read_metadata_block_data_(FLAC__Metadata_SimpleIterator *iterator, FLAC__StreamMetadata *block); +static FLAC__bool read_metadata_block_header_cb_(FLAC__IOHandle handle, FLAC__IOCallback_Read read_cb, FLAC__bool *is_last, FLAC__MetadataType *type, unsigned *length); +static FLAC__Metadata_SimpleIteratorStatus read_metadata_block_data_cb_(FLAC__IOHandle handle, FLAC__IOCallback_Read read_cb, FLAC__IOCallback_Seek seek_cb, FLAC__StreamMetadata *block); +static FLAC__Metadata_SimpleIteratorStatus read_metadata_block_data_streaminfo_cb_(FLAC__IOHandle handle, FLAC__IOCallback_Read read_cb, FLAC__StreamMetadata_StreamInfo *block); +static FLAC__Metadata_SimpleIteratorStatus read_metadata_block_data_padding_cb_(FLAC__IOHandle handle, FLAC__IOCallback_Seek seek_cb, FLAC__StreamMetadata_Padding *block, unsigned block_length); +static FLAC__Metadata_SimpleIteratorStatus read_metadata_block_data_application_cb_(FLAC__IOHandle handle, FLAC__IOCallback_Read read_cb, FLAC__StreamMetadata_Application *block, unsigned block_length); +static FLAC__Metadata_SimpleIteratorStatus read_metadata_block_data_seektable_cb_(FLAC__IOHandle handle, FLAC__IOCallback_Read read_cb, FLAC__StreamMetadata_SeekTable *block, unsigned block_length); +static FLAC__Metadata_SimpleIteratorStatus read_metadata_block_data_vorbis_comment_entry_cb_(FLAC__IOHandle handle, FLAC__IOCallback_Read read_cb, FLAC__StreamMetadata_VorbisComment_Entry *entry, unsigned max_length); +static FLAC__Metadata_SimpleIteratorStatus read_metadata_block_data_vorbis_comment_cb_(FLAC__IOHandle handle, FLAC__IOCallback_Read read_cb, FLAC__IOCallback_Seek seek_cb, FLAC__StreamMetadata_VorbisComment *block, unsigned block_length); +static FLAC__Metadata_SimpleIteratorStatus read_metadata_block_data_cuesheet_track_cb_(FLAC__IOHandle handle, FLAC__IOCallback_Read read_cb, FLAC__StreamMetadata_CueSheet_Track *track); +static FLAC__Metadata_SimpleIteratorStatus read_metadata_block_data_cuesheet_cb_(FLAC__IOHandle handle, FLAC__IOCallback_Read read_cb, FLAC__StreamMetadata_CueSheet *block); +static FLAC__Metadata_SimpleIteratorStatus read_metadata_block_data_picture_cb_(FLAC__IOHandle handle, FLAC__IOCallback_Read read_cb, FLAC__StreamMetadata_Picture *block); +static FLAC__Metadata_SimpleIteratorStatus read_metadata_block_data_unknown_cb_(FLAC__IOHandle handle, FLAC__IOCallback_Read read_cb, FLAC__StreamMetadata_Unknown *block, unsigned block_length); + +static FLAC__bool write_metadata_block_header_(FILE *file, FLAC__Metadata_SimpleIteratorStatus *status, const FLAC__StreamMetadata *block); +static FLAC__bool write_metadata_block_data_(FILE *file, FLAC__Metadata_SimpleIteratorStatus *status, const FLAC__StreamMetadata *block); +static FLAC__bool write_metadata_block_header_cb_(FLAC__IOHandle handle, FLAC__IOCallback_Write write_cb, const FLAC__StreamMetadata *block); +static FLAC__bool write_metadata_block_data_cb_(FLAC__IOHandle handle, FLAC__IOCallback_Write write_cb, const FLAC__StreamMetadata *block); +static FLAC__bool write_metadata_block_data_streaminfo_cb_(FLAC__IOHandle handle, FLAC__IOCallback_Write write_cb, const FLAC__StreamMetadata_StreamInfo *block); +static FLAC__bool write_metadata_block_data_padding_cb_(FLAC__IOHandle handle, FLAC__IOCallback_Write write_cb, const FLAC__StreamMetadata_Padding *block, unsigned block_length); +static FLAC__bool write_metadata_block_data_application_cb_(FLAC__IOHandle handle, FLAC__IOCallback_Write write_cb, const FLAC__StreamMetadata_Application *block, unsigned block_length); +static FLAC__bool write_metadata_block_data_seektable_cb_(FLAC__IOHandle handle, FLAC__IOCallback_Write write_cb, const FLAC__StreamMetadata_SeekTable *block); +static FLAC__bool write_metadata_block_data_vorbis_comment_cb_(FLAC__IOHandle handle, FLAC__IOCallback_Write write_cb, const FLAC__StreamMetadata_VorbisComment *block); +static FLAC__bool write_metadata_block_data_cuesheet_cb_(FLAC__IOHandle handle, FLAC__IOCallback_Write write_cb, const FLAC__StreamMetadata_CueSheet *block); +static FLAC__bool write_metadata_block_data_picture_cb_(FLAC__IOHandle handle, FLAC__IOCallback_Write write_cb, const FLAC__StreamMetadata_Picture *block); +static FLAC__bool write_metadata_block_data_unknown_cb_(FLAC__IOHandle handle, FLAC__IOCallback_Write write_cb, const FLAC__StreamMetadata_Unknown *block, unsigned block_length); + +static FLAC__bool write_metadata_block_stationary_(FLAC__Metadata_SimpleIterator *iterator, const FLAC__StreamMetadata *block); +static FLAC__bool write_metadata_block_stationary_with_padding_(FLAC__Metadata_SimpleIterator *iterator, FLAC__StreamMetadata *block, unsigned padding_length, FLAC__bool padding_is_last); +static FLAC__bool rewrite_whole_file_(FLAC__Metadata_SimpleIterator *iterator, FLAC__StreamMetadata *block, FLAC__bool append); + +static void simple_iterator_push_(FLAC__Metadata_SimpleIterator *iterator); +static FLAC__bool simple_iterator_pop_(FLAC__Metadata_SimpleIterator *iterator); + +static unsigned seek_to_first_metadata_block_cb_(FLAC__IOHandle handle, FLAC__IOCallback_Read read_cb, FLAC__IOCallback_Seek seek_cb); +static unsigned seek_to_first_metadata_block_(FILE *f); + +static FLAC__bool simple_iterator_copy_file_prefix_(FLAC__Metadata_SimpleIterator *iterator, FILE **tempfile, char **tempfilename, FLAC__bool append); +static FLAC__bool simple_iterator_copy_file_postfix_(FLAC__Metadata_SimpleIterator *iterator, FILE **tempfile, char **tempfilename, int fixup_is_last_code, FLAC__off_t fixup_is_last_flag_offset, FLAC__bool backup); + +static FLAC__bool copy_n_bytes_from_file_(FILE *file, FILE *tempfile, FLAC__off_t bytes, FLAC__Metadata_SimpleIteratorStatus *status); +static FLAC__bool copy_n_bytes_from_file_cb_(FLAC__IOHandle handle, FLAC__IOCallback_Read read_cb, FLAC__IOHandle temp_handle, FLAC__IOCallback_Write temp_write_cb, FLAC__off_t bytes, FLAC__Metadata_SimpleIteratorStatus *status); +static FLAC__bool copy_remaining_bytes_from_file_(FILE *file, FILE *tempfile, FLAC__Metadata_SimpleIteratorStatus *status); +static FLAC__bool copy_remaining_bytes_from_file_cb_(FLAC__IOHandle handle, FLAC__IOCallback_Read read_cb, FLAC__IOCallback_Eof eof_cb, FLAC__IOHandle temp_handle, FLAC__IOCallback_Write temp_write_cb, FLAC__Metadata_SimpleIteratorStatus *status); + +static FLAC__bool open_tempfile_(const char *filename, const char *tempfile_path_prefix, FILE **tempfile, char **tempfilename, FLAC__Metadata_SimpleIteratorStatus *status); +static FLAC__bool transport_tempfile_(const char *filename, FILE **tempfile, char **tempfilename, FLAC__Metadata_SimpleIteratorStatus *status); +static void cleanup_tempfile_(FILE **tempfile, char **tempfilename); + +static FLAC__bool get_file_stats_(const char *filename, struct flac_stat_s *stats); +static void set_file_stats_(const char *filename, struct flac_stat_s *stats); + +static int fseek_wrapper_(FLAC__IOHandle handle, FLAC__int64 offset, int whence); +static FLAC__int64 ftell_wrapper_(FLAC__IOHandle handle); + +static FLAC__Metadata_ChainStatus get_equivalent_status_(FLAC__Metadata_SimpleIteratorStatus status); + + +#ifdef FLAC__VALGRIND_TESTING +static size_t local__fwrite(const void *ptr, size_t size, size_t nmemb, FILE *stream) +{ + size_t ret = fwrite(ptr, size, nmemb, stream); + if(!ferror(stream)) + fflush(stream); + return ret; +} +#else +#define local__fwrite fwrite +#endif + +/**************************************************************************** + * + * Level 0 implementation + * + ***************************************************************************/ + +static FLAC__StreamDecoderWriteStatus write_callback_(const FLAC__StreamDecoder *decoder, const FLAC__Frame *frame, const FLAC__int32 * const buffer[], void *client_data); +static void metadata_callback_(const FLAC__StreamDecoder *decoder, const FLAC__StreamMetadata *metadata, void *client_data); +static void error_callback_(const FLAC__StreamDecoder *decoder, FLAC__StreamDecoderErrorStatus status, void *client_data); + +typedef struct { + FLAC__bool got_error; + FLAC__StreamMetadata *object; +} level0_client_data; + +static FLAC__StreamMetadata *get_one_metadata_block_(const char *filename, FLAC__MetadataType type) +{ + level0_client_data cd; + FLAC__StreamDecoder *decoder; + + FLAC__ASSERT(0 != filename); + + cd.got_error = false; + cd.object = 0; + + decoder = FLAC__stream_decoder_new(); + + if(0 == decoder) + return 0; + + FLAC__stream_decoder_set_md5_checking(decoder, false); + FLAC__stream_decoder_set_metadata_ignore_all(decoder); + FLAC__stream_decoder_set_metadata_respond(decoder, type); + + if(FLAC__stream_decoder_init_file(decoder, filename, write_callback_, metadata_callback_, error_callback_, &cd) != FLAC__STREAM_DECODER_INIT_STATUS_OK || cd.got_error) { + (void)FLAC__stream_decoder_finish(decoder); + FLAC__stream_decoder_delete(decoder); + return 0; + } + + if(!FLAC__stream_decoder_process_until_end_of_metadata(decoder) || cd.got_error) { + (void)FLAC__stream_decoder_finish(decoder); + FLAC__stream_decoder_delete(decoder); + if(0 != cd.object) + FLAC__metadata_object_delete(cd.object); + return 0; + } + + (void)FLAC__stream_decoder_finish(decoder); + FLAC__stream_decoder_delete(decoder); + + return cd.object; +} + +FLAC_API FLAC__bool FLAC__metadata_get_streaminfo(const char *filename, FLAC__StreamMetadata *streaminfo) +{ + FLAC__StreamMetadata *object; + + FLAC__ASSERT(0 != filename); + FLAC__ASSERT(0 != streaminfo); + + object = get_one_metadata_block_(filename, FLAC__METADATA_TYPE_STREAMINFO); + + if (object) { + /* can just copy the contents since STREAMINFO has no internal structure */ + *streaminfo = *object; + FLAC__metadata_object_delete(object); + return true; + } + else { + return false; + } +} + +FLAC_API FLAC__bool FLAC__metadata_get_tags(const char *filename, FLAC__StreamMetadata **tags) +{ + FLAC__ASSERT(0 != filename); + FLAC__ASSERT(0 != tags); + + *tags = get_one_metadata_block_(filename, FLAC__METADATA_TYPE_VORBIS_COMMENT); + + return 0 != *tags; +} + +FLAC_API FLAC__bool FLAC__metadata_get_cuesheet(const char *filename, FLAC__StreamMetadata **cuesheet) +{ + FLAC__ASSERT(0 != filename); + FLAC__ASSERT(0 != cuesheet); + + *cuesheet = get_one_metadata_block_(filename, FLAC__METADATA_TYPE_CUESHEET); + + return 0 != *cuesheet; +} + +FLAC__StreamDecoderWriteStatus write_callback_(const FLAC__StreamDecoder *decoder, const FLAC__Frame *frame, const FLAC__int32 * const buffer[], void *client_data) +{ + (void)decoder, (void)frame, (void)buffer, (void)client_data; + + return FLAC__STREAM_DECODER_WRITE_STATUS_CONTINUE; +} + +void metadata_callback_(const FLAC__StreamDecoder *decoder, const FLAC__StreamMetadata *metadata, void *client_data) +{ + level0_client_data *cd = (level0_client_data *)client_data; + (void)decoder; + + /* + * we assume we only get here when the one metadata block we were + * looking for was passed to us + */ + if(!cd->got_error && 0 == cd->object) { + if(0 == (cd->object = FLAC__metadata_object_clone(metadata))) + cd->got_error = true; + } +} + +void error_callback_(const FLAC__StreamDecoder *decoder, FLAC__StreamDecoderErrorStatus status, void *client_data) +{ + level0_client_data *cd = (level0_client_data *)client_data; + (void)decoder; + + if(status != FLAC__STREAM_DECODER_ERROR_STATUS_LOST_SYNC) + cd->got_error = true; +} + +FLAC_API FLAC__bool FLAC__metadata_get_picture(const char *filename, FLAC__StreamMetadata **picture, FLAC__StreamMetadata_Picture_Type type, const char *mime_type, const FLAC__byte *description, unsigned max_width, unsigned max_height, unsigned max_depth, unsigned max_colors) +{ + FLAC__Metadata_SimpleIterator *it; + FLAC__uint64 max_area_seen = 0; + FLAC__uint64 max_depth_seen = 0; + + FLAC__ASSERT(0 != filename); + FLAC__ASSERT(0 != picture); + + *picture = 0; + + it = FLAC__metadata_simple_iterator_new(); + if(0 == it) + return false; + if(!FLAC__metadata_simple_iterator_init(it, filename, /*read_only=*/true, /*preserve_file_stats=*/true)) { + FLAC__metadata_simple_iterator_delete(it); + return false; + } + do { + if(FLAC__metadata_simple_iterator_get_block_type(it) == FLAC__METADATA_TYPE_PICTURE) { + FLAC__StreamMetadata *obj = FLAC__metadata_simple_iterator_get_block(it); + FLAC__uint64 area = (FLAC__uint64)obj->data.picture.width * (FLAC__uint64)obj->data.picture.height; + /* check constraints */ + if( + (type == (FLAC__StreamMetadata_Picture_Type)(-1) || type == obj->data.picture.type) && + (mime_type == 0 || !strcmp(mime_type, obj->data.picture.mime_type)) && + (description == 0 || !strcmp((const char *)description, (const char *)obj->data.picture.description)) && + obj->data.picture.width <= max_width && + obj->data.picture.height <= max_height && + obj->data.picture.depth <= max_depth && + obj->data.picture.colors <= max_colors && + (area > max_area_seen || (area == max_area_seen && obj->data.picture.depth > max_depth_seen)) + ) { + if(*picture) + FLAC__metadata_object_delete(*picture); + *picture = obj; + max_area_seen = area; + max_depth_seen = obj->data.picture.depth; + } + else { + FLAC__metadata_object_delete(obj); + } + } + } while(FLAC__metadata_simple_iterator_next(it)); + + FLAC__metadata_simple_iterator_delete(it); + + return (0 != *picture); +} + + +/**************************************************************************** + * + * Level 1 implementation + * + ***************************************************************************/ + +#define SIMPLE_ITERATOR_MAX_PUSH_DEPTH (1+4) +/* 1 for initial offset, +4 for our own personal use */ + +struct FLAC__Metadata_SimpleIterator { + FILE *file; + char *filename, *tempfile_path_prefix; + struct flac_stat_s stats; + FLAC__bool has_stats; + FLAC__bool is_writable; + FLAC__Metadata_SimpleIteratorStatus status; + FLAC__off_t offset[SIMPLE_ITERATOR_MAX_PUSH_DEPTH]; + FLAC__off_t first_offset; /* this is the offset to the STREAMINFO block */ + unsigned depth; + /* this is the metadata block header of the current block we are pointing to: */ + FLAC__bool is_last; + FLAC__MetadataType type; + unsigned length; +}; + +FLAC_API const char * const FLAC__Metadata_SimpleIteratorStatusString[] = { + "FLAC__METADATA_SIMPLE_ITERATOR_STATUS_OK", + "FLAC__METADATA_SIMPLE_ITERATOR_STATUS_ILLEGAL_INPUT", + "FLAC__METADATA_SIMPLE_ITERATOR_STATUS_ERROR_OPENING_FILE", + "FLAC__METADATA_SIMPLE_ITERATOR_STATUS_NOT_A_FLAC_FILE", + "FLAC__METADATA_SIMPLE_ITERATOR_STATUS_NOT_WRITABLE", + "FLAC__METADATA_SIMPLE_ITERATOR_STATUS_BAD_METADATA", + "FLAC__METADATA_SIMPLE_ITERATOR_STATUS_READ_ERROR", + "FLAC__METADATA_SIMPLE_ITERATOR_STATUS_SEEK_ERROR", + "FLAC__METADATA_SIMPLE_ITERATOR_STATUS_WRITE_ERROR", + "FLAC__METADATA_SIMPLE_ITERATOR_STATUS_RENAME_ERROR", + "FLAC__METADATA_SIMPLE_ITERATOR_STATUS_UNLINK_ERROR", + "FLAC__METADATA_SIMPLE_ITERATOR_STATUS_MEMORY_ALLOCATION_ERROR", + "FLAC__METADATA_SIMPLE_ITERATOR_STATUS_INTERNAL_ERROR" +}; + + +FLAC_API FLAC__Metadata_SimpleIterator *FLAC__metadata_simple_iterator_new(void) +{ + FLAC__Metadata_SimpleIterator *iterator = calloc(1, sizeof(FLAC__Metadata_SimpleIterator)); + + if(0 != iterator) { + iterator->file = 0; + iterator->filename = 0; + iterator->tempfile_path_prefix = 0; + iterator->has_stats = false; + iterator->is_writable = false; + iterator->status = FLAC__METADATA_SIMPLE_ITERATOR_STATUS_OK; + iterator->first_offset = iterator->offset[0] = -1; + iterator->depth = 0; + } + + return iterator; +} + +static void simple_iterator_free_guts_(FLAC__Metadata_SimpleIterator *iterator) +{ + FLAC__ASSERT(0 != iterator); + + if(0 != iterator->file) { + fclose(iterator->file); + iterator->file = 0; + if(iterator->has_stats) + set_file_stats_(iterator->filename, &iterator->stats); + } + if(0 != iterator->filename) { + free(iterator->filename); + iterator->filename = 0; + } + if(0 != iterator->tempfile_path_prefix) { + free(iterator->tempfile_path_prefix); + iterator->tempfile_path_prefix = 0; + } +} + +FLAC_API void FLAC__metadata_simple_iterator_delete(FLAC__Metadata_SimpleIterator *iterator) +{ + FLAC__ASSERT(0 != iterator); + + simple_iterator_free_guts_(iterator); + free(iterator); +} + +FLAC_API FLAC__Metadata_SimpleIteratorStatus FLAC__metadata_simple_iterator_status(FLAC__Metadata_SimpleIterator *iterator) +{ + FLAC__Metadata_SimpleIteratorStatus status; + + FLAC__ASSERT(0 != iterator); + + status = iterator->status; + iterator->status = FLAC__METADATA_SIMPLE_ITERATOR_STATUS_OK; + return status; +} + +static FLAC__bool simple_iterator_prime_input_(FLAC__Metadata_SimpleIterator *iterator, FLAC__bool read_only) +{ + unsigned ret; + + FLAC__ASSERT(0 != iterator); + + if(read_only || 0 == (iterator->file = flac_fopen(iterator->filename, "r+b"))) { + iterator->is_writable = false; + if(read_only || errno == EACCES) { + if(0 == (iterator->file = flac_fopen(iterator->filename, "rb"))) { + iterator->status = FLAC__METADATA_SIMPLE_ITERATOR_STATUS_ERROR_OPENING_FILE; + return false; + } + } + else { + iterator->status = FLAC__METADATA_SIMPLE_ITERATOR_STATUS_ERROR_OPENING_FILE; + return false; + } + } + else { + iterator->is_writable = true; + } + + ret = seek_to_first_metadata_block_(iterator->file); + switch(ret) { + case 0: + iterator->depth = 0; + iterator->first_offset = iterator->offset[iterator->depth] = ftello(iterator->file); + return read_metadata_block_header_(iterator); + case 1: + iterator->status = FLAC__METADATA_SIMPLE_ITERATOR_STATUS_READ_ERROR; + return false; + case 2: + iterator->status = FLAC__METADATA_SIMPLE_ITERATOR_STATUS_SEEK_ERROR; + return false; + case 3: + iterator->status = FLAC__METADATA_SIMPLE_ITERATOR_STATUS_NOT_A_FLAC_FILE; + return false; + default: + FLAC__ASSERT(0); + return false; + } +} + +#if 0 +@@@ If we decide to finish implementing this, put this comment back in metadata.h +/* + * The 'tempfile_path_prefix' allows you to specify a directory where + * tempfiles should go. Remember that if your metadata edits cause the + * FLAC file to grow, the entire file will have to be rewritten. If + * 'tempfile_path_prefix' is NULL, the temp file will be written in the + * same directory as the original FLAC file. This makes replacing the + * original with the tempfile fast but requires extra space in the same + * partition for the tempfile. If space is a problem, you can pass a + * directory name belonging to a different partition in + * 'tempfile_path_prefix'. Note that you should use the forward slash + * '/' as the directory separator. A trailing slash is not needed; it + * will be added automatically. + */ +FLAC__bool FLAC__metadata_simple_iterator_init(FLAC__Metadata_SimpleIterator *iterator, const char *filename, FLAC__bool preserve_file_stats, const char *tempfile_path_prefix); +#endif + +FLAC_API FLAC__bool FLAC__metadata_simple_iterator_init(FLAC__Metadata_SimpleIterator *iterator, const char *filename, FLAC__bool read_only, FLAC__bool preserve_file_stats) +{ + const char *tempfile_path_prefix = 0; /*@@@ search for comments near 'flac_rename(...)' for what it will take to finish implementing this */ + + FLAC__ASSERT(0 != iterator); + FLAC__ASSERT(0 != filename); + + simple_iterator_free_guts_(iterator); + + if(!read_only && preserve_file_stats) + iterator->has_stats = get_file_stats_(filename, &iterator->stats); + + if(0 == (iterator->filename = strdup(filename))) { + iterator->status = FLAC__METADATA_SIMPLE_ITERATOR_STATUS_MEMORY_ALLOCATION_ERROR; + return false; + } + if(0 != tempfile_path_prefix && 0 == (iterator->tempfile_path_prefix = strdup(tempfile_path_prefix))) { + iterator->status = FLAC__METADATA_SIMPLE_ITERATOR_STATUS_MEMORY_ALLOCATION_ERROR; + return false; + } + + return simple_iterator_prime_input_(iterator, read_only); +} + +FLAC_API FLAC__bool FLAC__metadata_simple_iterator_is_writable(const FLAC__Metadata_SimpleIterator *iterator) +{ + FLAC__ASSERT(0 != iterator); + FLAC__ASSERT(0 != iterator->file); + + return iterator->is_writable; +} + +FLAC_API FLAC__bool FLAC__metadata_simple_iterator_next(FLAC__Metadata_SimpleIterator *iterator) +{ + FLAC__ASSERT(0 != iterator); + FLAC__ASSERT(0 != iterator->file); + + if(iterator->is_last) + return false; + + if(0 != fseeko(iterator->file, iterator->length, SEEK_CUR)) { + iterator->status = FLAC__METADATA_SIMPLE_ITERATOR_STATUS_SEEK_ERROR; + return false; + } + + iterator->offset[iterator->depth] = ftello(iterator->file); + + return read_metadata_block_header_(iterator); +} + +FLAC_API FLAC__bool FLAC__metadata_simple_iterator_prev(FLAC__Metadata_SimpleIterator *iterator) +{ + FLAC__off_t this_offset; + + FLAC__ASSERT(0 != iterator); + FLAC__ASSERT(0 != iterator->file); + + if(iterator->offset[iterator->depth] == iterator->first_offset) + return false; + + if(0 != fseeko(iterator->file, iterator->first_offset, SEEK_SET)) { + iterator->status = FLAC__METADATA_SIMPLE_ITERATOR_STATUS_SEEK_ERROR; + return false; + } + this_offset = iterator->first_offset; + if(!read_metadata_block_header_(iterator)) + return false; + + /* we ignore any error from ftello() and catch it in fseeko() */ + while(ftello(iterator->file) + (FLAC__off_t)iterator->length < iterator->offset[iterator->depth]) { + if(0 != fseeko(iterator->file, iterator->length, SEEK_CUR)) { + iterator->status = FLAC__METADATA_SIMPLE_ITERATOR_STATUS_SEEK_ERROR; + return false; + } + this_offset = ftello(iterator->file); + if(!read_metadata_block_header_(iterator)) + return false; + } + + iterator->offset[iterator->depth] = this_offset; + + return true; +} + +/*@@@@add to tests*/ +FLAC_API FLAC__bool FLAC__metadata_simple_iterator_is_last(const FLAC__Metadata_SimpleIterator *iterator) +{ + FLAC__ASSERT(0 != iterator); + FLAC__ASSERT(0 != iterator->file); + + return iterator->is_last; +} + +/*@@@@add to tests*/ +FLAC_API off_t FLAC__metadata_simple_iterator_get_block_offset(const FLAC__Metadata_SimpleIterator *iterator) +{ + FLAC__ASSERT(0 != iterator); + FLAC__ASSERT(0 != iterator->file); + + return iterator->offset[iterator->depth]; +} + +FLAC_API FLAC__MetadataType FLAC__metadata_simple_iterator_get_block_type(const FLAC__Metadata_SimpleIterator *iterator) +{ + FLAC__ASSERT(0 != iterator); + FLAC__ASSERT(0 != iterator->file); + + return iterator->type; +} + +/*@@@@add to tests*/ +FLAC_API unsigned FLAC__metadata_simple_iterator_get_block_length(const FLAC__Metadata_SimpleIterator *iterator) +{ + FLAC__ASSERT(0 != iterator); + FLAC__ASSERT(0 != iterator->file); + + return iterator->length; +} + +/*@@@@add to tests*/ +FLAC_API FLAC__bool FLAC__metadata_simple_iterator_get_application_id(FLAC__Metadata_SimpleIterator *iterator, FLAC__byte *id) +{ + const unsigned id_bytes = FLAC__STREAM_METADATA_APPLICATION_ID_LEN / 8; + + FLAC__ASSERT(0 != iterator); + FLAC__ASSERT(0 != iterator->file); + FLAC__ASSERT(0 != id); + + if(iterator->type != FLAC__METADATA_TYPE_APPLICATION) { + iterator->status = FLAC__METADATA_SIMPLE_ITERATOR_STATUS_ILLEGAL_INPUT; + return false; + } + + if(fread(id, 1, id_bytes, iterator->file) != id_bytes) { + iterator->status = FLAC__METADATA_SIMPLE_ITERATOR_STATUS_READ_ERROR; + return false; + } + + /* back up */ + if(0 != fseeko(iterator->file, -((int)id_bytes), SEEK_CUR)) { + iterator->status = FLAC__METADATA_SIMPLE_ITERATOR_STATUS_SEEK_ERROR; + return false; + } + + return true; +} + +FLAC_API FLAC__StreamMetadata *FLAC__metadata_simple_iterator_get_block(FLAC__Metadata_SimpleIterator *iterator) +{ + FLAC__StreamMetadata *block = FLAC__metadata_object_new(iterator->type); + + FLAC__ASSERT(0 != iterator); + FLAC__ASSERT(0 != iterator->file); + + if(0 != block) { + block->is_last = iterator->is_last; + block->length = iterator->length; + + if(!read_metadata_block_data_(iterator, block)) { + FLAC__metadata_object_delete(block); + return 0; + } + + /* back up to the beginning of the block data to stay consistent */ + if(0 != fseeko(iterator->file, iterator->offset[iterator->depth] + FLAC__STREAM_METADATA_HEADER_LENGTH, SEEK_SET)) { + iterator->status = FLAC__METADATA_SIMPLE_ITERATOR_STATUS_SEEK_ERROR; + FLAC__metadata_object_delete(block); + return 0; + } + } + else + iterator->status = FLAC__METADATA_SIMPLE_ITERATOR_STATUS_MEMORY_ALLOCATION_ERROR; + + return block; +} + +FLAC_API FLAC__bool FLAC__metadata_simple_iterator_set_block(FLAC__Metadata_SimpleIterator *iterator, FLAC__StreamMetadata *block, FLAC__bool use_padding) +{ + FLAC__ASSERT_DECLARATION(FLAC__off_t debug_target_offset = iterator->offset[iterator->depth];) + FLAC__bool ret; + + FLAC__ASSERT(0 != iterator); + FLAC__ASSERT(0 != iterator->file); + FLAC__ASSERT(0 != block); + + if(!iterator->is_writable) { + iterator->status = FLAC__METADATA_SIMPLE_ITERATOR_STATUS_NOT_WRITABLE; + return false; + } + + if(iterator->type == FLAC__METADATA_TYPE_STREAMINFO || block->type == FLAC__METADATA_TYPE_STREAMINFO) { + if(iterator->type != block->type) { + iterator->status = FLAC__METADATA_SIMPLE_ITERATOR_STATUS_ILLEGAL_INPUT; + return false; + } + } + + block->is_last = iterator->is_last; + + if(iterator->length == block->length) + return write_metadata_block_stationary_(iterator, block); + else if(iterator->length > block->length) { + if(use_padding && iterator->length >= FLAC__STREAM_METADATA_HEADER_LENGTH + block->length) { + ret = write_metadata_block_stationary_with_padding_(iterator, block, iterator->length - FLAC__STREAM_METADATA_HEADER_LENGTH - block->length, block->is_last); + FLAC__ASSERT(!ret || iterator->offset[iterator->depth] == debug_target_offset); + FLAC__ASSERT(!ret || ftello(iterator->file) == debug_target_offset + (FLAC__off_t)FLAC__STREAM_METADATA_HEADER_LENGTH); + return ret; + } + else { + ret = rewrite_whole_file_(iterator, block, /*append=*/false); + FLAC__ASSERT(!ret || iterator->offset[iterator->depth] == debug_target_offset); + FLAC__ASSERT(!ret || ftello(iterator->file) == debug_target_offset + (FLAC__off_t)FLAC__STREAM_METADATA_HEADER_LENGTH); + return ret; + } + } + else /* iterator->length < block->length */ { + unsigned padding_leftover = 0; + FLAC__bool padding_is_last = false; + if(use_padding) { + /* first see if we can even use padding */ + if(iterator->is_last) { + use_padding = false; + } + else { + const unsigned extra_padding_bytes_required = block->length - iterator->length; + simple_iterator_push_(iterator); + if(!FLAC__metadata_simple_iterator_next(iterator)) { + (void)simple_iterator_pop_(iterator); + return false; + } + if(iterator->type != FLAC__METADATA_TYPE_PADDING) { + use_padding = false; + } + else { + if(FLAC__STREAM_METADATA_HEADER_LENGTH + iterator->length == extra_padding_bytes_required) { + padding_leftover = 0; + block->is_last = iterator->is_last; + } + else if(iterator->length < extra_padding_bytes_required) + use_padding = false; + else { + padding_leftover = FLAC__STREAM_METADATA_HEADER_LENGTH + iterator->length - extra_padding_bytes_required; + padding_is_last = iterator->is_last; + block->is_last = false; + } + } + if(!simple_iterator_pop_(iterator)) + return false; + } + } + if(use_padding) { + if(padding_leftover == 0) { + ret = write_metadata_block_stationary_(iterator, block); + FLAC__ASSERT(!ret || iterator->offset[iterator->depth] == debug_target_offset); + FLAC__ASSERT(!ret || ftello(iterator->file) == debug_target_offset + (FLAC__off_t)FLAC__STREAM_METADATA_HEADER_LENGTH); + return ret; + } + else { + FLAC__ASSERT(padding_leftover >= FLAC__STREAM_METADATA_HEADER_LENGTH); + ret = write_metadata_block_stationary_with_padding_(iterator, block, padding_leftover - FLAC__STREAM_METADATA_HEADER_LENGTH, padding_is_last); + FLAC__ASSERT(!ret || iterator->offset[iterator->depth] == debug_target_offset); + FLAC__ASSERT(!ret || ftello(iterator->file) == debug_target_offset + (FLAC__off_t)FLAC__STREAM_METADATA_HEADER_LENGTH); + return ret; + } + } + else { + ret = rewrite_whole_file_(iterator, block, /*append=*/false); + FLAC__ASSERT(!ret || iterator->offset[iterator->depth] == debug_target_offset); + FLAC__ASSERT(!ret || ftello(iterator->file) == debug_target_offset + (FLAC__off_t)FLAC__STREAM_METADATA_HEADER_LENGTH); + return ret; + } + } +} + +FLAC_API FLAC__bool FLAC__metadata_simple_iterator_insert_block_after(FLAC__Metadata_SimpleIterator *iterator, FLAC__StreamMetadata *block, FLAC__bool use_padding) +{ + unsigned padding_leftover = 0; + FLAC__bool padding_is_last = false; + + FLAC__ASSERT_DECLARATION(FLAC__off_t debug_target_offset = iterator->offset[iterator->depth] + FLAC__STREAM_METADATA_HEADER_LENGTH + iterator->length;) + FLAC__bool ret; + + FLAC__ASSERT(0 != iterator); + FLAC__ASSERT(0 != iterator->file); + FLAC__ASSERT(0 != block); + + if(!iterator->is_writable) { + iterator->status = FLAC__METADATA_SIMPLE_ITERATOR_STATUS_NOT_WRITABLE; + return false; + } + + if(block->type == FLAC__METADATA_TYPE_STREAMINFO) { + iterator->status = FLAC__METADATA_SIMPLE_ITERATOR_STATUS_ILLEGAL_INPUT; + return false; + } + + block->is_last = iterator->is_last; + + if(use_padding) { + /* first see if we can even use padding */ + if(iterator->is_last) { + use_padding = false; + } + else { + simple_iterator_push_(iterator); + if(!FLAC__metadata_simple_iterator_next(iterator)) { + (void)simple_iterator_pop_(iterator); + return false; + } + if(iterator->type != FLAC__METADATA_TYPE_PADDING) { + use_padding = false; + } + else { + if(iterator->length == block->length) { + padding_leftover = 0; + block->is_last = iterator->is_last; + } + else if(iterator->length < FLAC__STREAM_METADATA_HEADER_LENGTH + block->length) + use_padding = false; + else { + padding_leftover = iterator->length - block->length; + padding_is_last = iterator->is_last; + block->is_last = false; + } + } + if(!simple_iterator_pop_(iterator)) + return false; + } + } + if(use_padding) { + /* move to the next block, which is suitable padding */ + if(!FLAC__metadata_simple_iterator_next(iterator)) + return false; + if(padding_leftover == 0) { + ret = write_metadata_block_stationary_(iterator, block); + FLAC__ASSERT(iterator->offset[iterator->depth] == debug_target_offset); + FLAC__ASSERT(ftello(iterator->file) == debug_target_offset + (FLAC__off_t)FLAC__STREAM_METADATA_HEADER_LENGTH); + return ret; + } + else { + FLAC__ASSERT(padding_leftover >= FLAC__STREAM_METADATA_HEADER_LENGTH); + ret = write_metadata_block_stationary_with_padding_(iterator, block, padding_leftover - FLAC__STREAM_METADATA_HEADER_LENGTH, padding_is_last); + FLAC__ASSERT(iterator->offset[iterator->depth] == debug_target_offset); + FLAC__ASSERT(ftello(iterator->file) == debug_target_offset + (FLAC__off_t)FLAC__STREAM_METADATA_HEADER_LENGTH); + return ret; + } + } + else { + ret = rewrite_whole_file_(iterator, block, /*append=*/true); + FLAC__ASSERT(iterator->offset[iterator->depth] == debug_target_offset); + FLAC__ASSERT(ftello(iterator->file) == debug_target_offset + (FLAC__off_t)FLAC__STREAM_METADATA_HEADER_LENGTH); + return ret; + } +} + +FLAC_API FLAC__bool FLAC__metadata_simple_iterator_delete_block(FLAC__Metadata_SimpleIterator *iterator, FLAC__bool use_padding) +{ + FLAC__ASSERT_DECLARATION(FLAC__off_t debug_target_offset = iterator->offset[iterator->depth];) + FLAC__bool ret; + + if(!iterator->is_writable) { + iterator->status = FLAC__METADATA_SIMPLE_ITERATOR_STATUS_NOT_WRITABLE; + return false; + } + + if(iterator->type == FLAC__METADATA_TYPE_STREAMINFO) { + iterator->status = FLAC__METADATA_SIMPLE_ITERATOR_STATUS_ILLEGAL_INPUT; + return false; + } + + if(use_padding) { + FLAC__StreamMetadata *padding = FLAC__metadata_object_new(FLAC__METADATA_TYPE_PADDING); + if(0 == padding) { + iterator->status = FLAC__METADATA_SIMPLE_ITERATOR_STATUS_MEMORY_ALLOCATION_ERROR; + return false; + } + padding->length = iterator->length; + if(!FLAC__metadata_simple_iterator_set_block(iterator, padding, false)) { + FLAC__metadata_object_delete(padding); + return false; + } + FLAC__metadata_object_delete(padding); + if(!FLAC__metadata_simple_iterator_prev(iterator)) + return false; + FLAC__ASSERT(iterator->offset[iterator->depth] + (FLAC__off_t)FLAC__STREAM_METADATA_HEADER_LENGTH + (FLAC__off_t)iterator->length == debug_target_offset); + FLAC__ASSERT(ftello(iterator->file) + (FLAC__off_t)iterator->length == debug_target_offset); + return true; + } + else { + ret = rewrite_whole_file_(iterator, 0, /*append=*/false); + FLAC__ASSERT(iterator->offset[iterator->depth] + (FLAC__off_t)FLAC__STREAM_METADATA_HEADER_LENGTH + (FLAC__off_t)iterator->length == debug_target_offset); + FLAC__ASSERT(ftello(iterator->file) + (FLAC__off_t)iterator->length == debug_target_offset); + return ret; + } +} + + + +/**************************************************************************** + * + * Level 2 implementation + * + ***************************************************************************/ + + +typedef struct FLAC__Metadata_Node { + FLAC__StreamMetadata *data; + struct FLAC__Metadata_Node *prev, *next; +} FLAC__Metadata_Node; + +struct FLAC__Metadata_Chain { + char *filename; /* will be NULL if using callbacks */ + FLAC__bool is_ogg; + FLAC__Metadata_Node *head; + FLAC__Metadata_Node *tail; + unsigned nodes; + FLAC__Metadata_ChainStatus status; + FLAC__off_t first_offset, last_offset; + /* + * This is the length of the chain initially read from the FLAC file. + * it is used to compare against the current length to decide whether + * or not the whole file has to be rewritten. + */ + FLAC__off_t initial_length; + /* @@@ hacky, these are currently only needed by ogg reader */ + FLAC__IOHandle handle; + FLAC__IOCallback_Read read_cb; +}; + +struct FLAC__Metadata_Iterator { + FLAC__Metadata_Chain *chain; + FLAC__Metadata_Node *current; +}; + +FLAC_API const char * const FLAC__Metadata_ChainStatusString[] = { + "FLAC__METADATA_CHAIN_STATUS_OK", + "FLAC__METADATA_CHAIN_STATUS_ILLEGAL_INPUT", + "FLAC__METADATA_CHAIN_STATUS_ERROR_OPENING_FILE", + "FLAC__METADATA_CHAIN_STATUS_NOT_A_FLAC_FILE", + "FLAC__METADATA_CHAIN_STATUS_NOT_WRITABLE", + "FLAC__METADATA_CHAIN_STATUS_BAD_METADATA", + "FLAC__METADATA_CHAIN_STATUS_READ_ERROR", + "FLAC__METADATA_CHAIN_STATUS_SEEK_ERROR", + "FLAC__METADATA_CHAIN_STATUS_WRITE_ERROR", + "FLAC__METADATA_CHAIN_STATUS_RENAME_ERROR", + "FLAC__METADATA_CHAIN_STATUS_UNLINK_ERROR", + "FLAC__METADATA_CHAIN_STATUS_MEMORY_ALLOCATION_ERROR", + "FLAC__METADATA_CHAIN_STATUS_INTERNAL_ERROR", + "FLAC__METADATA_CHAIN_STATUS_INVALID_CALLBACKS", + "FLAC__METADATA_CHAIN_STATUS_READ_WRITE_MISMATCH", + "FLAC__METADATA_CHAIN_STATUS_WRONG_WRITE_CALL" +}; + + +static FLAC__Metadata_Node *node_new_(void) +{ + return calloc(1, sizeof(FLAC__Metadata_Node)); +} + +static void node_delete_(FLAC__Metadata_Node *node) +{ + FLAC__ASSERT(0 != node); + if(0 != node->data) + FLAC__metadata_object_delete(node->data); + free(node); +} + +static void chain_init_(FLAC__Metadata_Chain *chain) +{ + FLAC__ASSERT(0 != chain); + + chain->filename = 0; + chain->is_ogg = false; + chain->head = chain->tail = 0; + chain->nodes = 0; + chain->status = FLAC__METADATA_CHAIN_STATUS_OK; + chain->initial_length = 0; + chain->read_cb = 0; +} + +static void chain_clear_(FLAC__Metadata_Chain *chain) +{ + FLAC__Metadata_Node *node, *next; + + FLAC__ASSERT(0 != chain); + + for(node = chain->head; node; ) { + next = node->next; + node_delete_(node); + node = next; + } + + if(0 != chain->filename) + free(chain->filename); + + chain_init_(chain); +} + +static void chain_append_node_(FLAC__Metadata_Chain *chain, FLAC__Metadata_Node *node) +{ + FLAC__ASSERT(0 != chain); + FLAC__ASSERT(0 != node); + FLAC__ASSERT(0 != node->data); + + node->next = node->prev = 0; + node->data->is_last = true; + if(0 != chain->tail) + chain->tail->data->is_last = false; + + if(0 == chain->head) + chain->head = node; + else { + FLAC__ASSERT(0 != chain->tail); + chain->tail->next = node; + node->prev = chain->tail; + } + chain->tail = node; + chain->nodes++; +} + +static void chain_remove_node_(FLAC__Metadata_Chain *chain, FLAC__Metadata_Node *node) +{ + FLAC__ASSERT(0 != chain); + FLAC__ASSERT(0 != node); + + if(node == chain->head) + chain->head = node->next; + else + node->prev->next = node->next; + + if(node == chain->tail) + chain->tail = node->prev; + else + node->next->prev = node->prev; + + if(0 != chain->tail) + chain->tail->data->is_last = true; + + chain->nodes--; +} + +static void chain_delete_node_(FLAC__Metadata_Chain *chain, FLAC__Metadata_Node *node) +{ + chain_remove_node_(chain, node); + node_delete_(node); +} + +static FLAC__off_t chain_calculate_length_(FLAC__Metadata_Chain *chain) +{ + const FLAC__Metadata_Node *node; + FLAC__off_t length = 0; + for(node = chain->head; node; node = node->next) + length += (FLAC__STREAM_METADATA_HEADER_LENGTH + node->data->length); + return length; +} + +static void iterator_insert_node_(FLAC__Metadata_Iterator *iterator, FLAC__Metadata_Node *node) +{ + FLAC__ASSERT(0 != node); + FLAC__ASSERT(0 != node->data); + FLAC__ASSERT(0 != iterator); + FLAC__ASSERT(0 != iterator->current); + FLAC__ASSERT(0 != iterator->chain); + FLAC__ASSERT(0 != iterator->chain->head); + FLAC__ASSERT(0 != iterator->chain->tail); + + node->data->is_last = false; + + node->prev = iterator->current->prev; + node->next = iterator->current; + + if(0 == node->prev) + iterator->chain->head = node; + else + node->prev->next = node; + + iterator->current->prev = node; + + iterator->chain->nodes++; +} + +static void iterator_insert_node_after_(FLAC__Metadata_Iterator *iterator, FLAC__Metadata_Node *node) +{ + FLAC__ASSERT(0 != node); + FLAC__ASSERT(0 != node->data); + FLAC__ASSERT(0 != iterator); + FLAC__ASSERT(0 != iterator->current); + FLAC__ASSERT(0 != iterator->chain); + FLAC__ASSERT(0 != iterator->chain->head); + FLAC__ASSERT(0 != iterator->chain->tail); + + iterator->current->data->is_last = false; + + node->prev = iterator->current; + node->next = iterator->current->next; + + if(0 == node->next) + iterator->chain->tail = node; + else + node->next->prev = node; + + node->prev->next = node; + + iterator->chain->tail->data->is_last = true; + + iterator->chain->nodes++; +} + +/* return true iff node and node->next are both padding */ +static FLAC__bool chain_merge_adjacent_padding_(FLAC__Metadata_Chain *chain, FLAC__Metadata_Node *node) +{ + if(node->data->type == FLAC__METADATA_TYPE_PADDING && 0 != node->next && node->next->data->type == FLAC__METADATA_TYPE_PADDING) { + const unsigned growth = FLAC__STREAM_METADATA_HEADER_LENGTH + node->next->data->length; + node->data->length += growth; /* new block size can be greater than max metadata block size, but it'll be fixed later in chain_prepare_for_write_() */ + + chain_delete_node_(chain, node->next); + return true; + } + else + return false; +} + +/* Returns the new length of the chain, or 0 if there was an error. */ +/* WATCHOUT: This can get called multiple times before a write, so + * it should still work when this happens. + */ +/* WATCHOUT: Make sure to also update the logic in + * FLAC__metadata_chain_check_if_tempfile_needed() if the logic here changes. + */ +static FLAC__off_t chain_prepare_for_write_(FLAC__Metadata_Chain *chain, FLAC__bool use_padding) +{ + FLAC__off_t current_length = chain_calculate_length_(chain); + + if(use_padding) { + /* if the metadata shrank and the last block is padding, we just extend the last padding block */ + if(current_length < chain->initial_length && chain->tail->data->type == FLAC__METADATA_TYPE_PADDING) { + const FLAC__off_t delta = chain->initial_length - current_length; + chain->tail->data->length += delta; + current_length += delta; + FLAC__ASSERT(current_length == chain->initial_length); + } + /* if the metadata shrank more than 4 bytes then there's room to add another padding block */ + else if(current_length + (FLAC__off_t)FLAC__STREAM_METADATA_HEADER_LENGTH <= chain->initial_length) { + FLAC__StreamMetadata *padding; + FLAC__Metadata_Node *node; + if(0 == (padding = FLAC__metadata_object_new(FLAC__METADATA_TYPE_PADDING))) { + chain->status = FLAC__METADATA_CHAIN_STATUS_MEMORY_ALLOCATION_ERROR; + return 0; + } + padding->length = chain->initial_length - (FLAC__STREAM_METADATA_HEADER_LENGTH + current_length); + if(0 == (node = node_new_())) { + FLAC__metadata_object_delete(padding); + chain->status = FLAC__METADATA_CHAIN_STATUS_MEMORY_ALLOCATION_ERROR; + return 0; + } + node->data = padding; + chain_append_node_(chain, node); + current_length = chain_calculate_length_(chain); + FLAC__ASSERT(current_length == chain->initial_length); + } + /* if the metadata grew but the last block is padding, try cutting the padding to restore the original length so we don't have to rewrite the whole file */ + else if(current_length > chain->initial_length) { + const FLAC__off_t delta = current_length - chain->initial_length; + if(chain->tail->data->type == FLAC__METADATA_TYPE_PADDING) { + /* if the delta is exactly the size of the last padding block, remove the padding block */ + if((FLAC__off_t)chain->tail->data->length + (FLAC__off_t)FLAC__STREAM_METADATA_HEADER_LENGTH == delta) { + chain_delete_node_(chain, chain->tail); + current_length = chain_calculate_length_(chain); + FLAC__ASSERT(current_length == chain->initial_length); + } + /* if there is at least 'delta' bytes of padding, trim the padding down */ + else if((FLAC__off_t)chain->tail->data->length >= delta) { + chain->tail->data->length -= delta; + current_length -= delta; + FLAC__ASSERT(current_length == chain->initial_length); + } + } + } + } + + /* check sizes of all metadata blocks; reduce padding size if necessary */ + { + FLAC__Metadata_Node *node; + for (node = chain->head; node; node = node->next) { + if(node->data->length >= (1u << FLAC__STREAM_METADATA_LENGTH_LEN)) { + if(node->data->type == FLAC__METADATA_TYPE_PADDING) { + node->data->length = (1u << FLAC__STREAM_METADATA_LENGTH_LEN) - 1; + current_length = chain_calculate_length_(chain); + } else { + chain->status = FLAC__METADATA_CHAIN_STATUS_BAD_METADATA; + return 0; + } + } + } + } + + return current_length; +} + +static FLAC__bool chain_read_cb_(FLAC__Metadata_Chain *chain, FLAC__IOHandle handle, FLAC__IOCallback_Read read_cb, FLAC__IOCallback_Seek seek_cb, FLAC__IOCallback_Tell tell_cb) +{ + FLAC__Metadata_Node *node; + + FLAC__ASSERT(0 != chain); + + /* we assume we're already at the beginning of the file */ + + switch(seek_to_first_metadata_block_cb_(handle, read_cb, seek_cb)) { + case 0: + break; + case 1: + chain->status = FLAC__METADATA_CHAIN_STATUS_READ_ERROR; + return false; + case 2: + chain->status = FLAC__METADATA_CHAIN_STATUS_SEEK_ERROR; + return false; + case 3: + chain->status = FLAC__METADATA_CHAIN_STATUS_NOT_A_FLAC_FILE; + return false; + default: + FLAC__ASSERT(0); + return false; + } + + { + FLAC__int64 pos = tell_cb(handle); + if(pos < 0) { + chain->status = FLAC__METADATA_CHAIN_STATUS_READ_ERROR; + return false; + } + chain->first_offset = (FLAC__off_t)pos; + } + + { + FLAC__bool is_last; + FLAC__MetadataType type; + unsigned length; + + do { + node = node_new_(); + if(0 == node) { + chain->status = FLAC__METADATA_CHAIN_STATUS_MEMORY_ALLOCATION_ERROR; + return false; + } + + if(!read_metadata_block_header_cb_(handle, read_cb, &is_last, &type, &length)) { + node_delete_(node); + chain->status = FLAC__METADATA_CHAIN_STATUS_READ_ERROR; + return false; + } + + node->data = FLAC__metadata_object_new(type); + if(0 == node->data) { + node_delete_(node); + chain->status = FLAC__METADATA_CHAIN_STATUS_MEMORY_ALLOCATION_ERROR; + return false; + } + + node->data->is_last = is_last; + node->data->length = length; + + chain->status = get_equivalent_status_(read_metadata_block_data_cb_(handle, read_cb, seek_cb, node->data)); + if(chain->status != FLAC__METADATA_CHAIN_STATUS_OK) { + node_delete_(node); + return false; + } + chain_append_node_(chain, node); + } while(!is_last); + } + + { + FLAC__int64 pos = tell_cb(handle); + if(pos < 0) { + chain->status = FLAC__METADATA_CHAIN_STATUS_READ_ERROR; + return false; + } + chain->last_offset = (FLAC__off_t)pos; + } + + chain->initial_length = chain_calculate_length_(chain); + + return true; +} + +static FLAC__StreamDecoderReadStatus chain_read_ogg_read_cb_(const FLAC__StreamDecoder *decoder, FLAC__byte buffer[], size_t *bytes, void *client_data) +{ + FLAC__Metadata_Chain *chain = (FLAC__Metadata_Chain*)client_data; + (void)decoder; + if(*bytes > 0 && chain->status == FLAC__METADATA_CHAIN_STATUS_OK) { + *bytes = chain->read_cb(buffer, sizeof(FLAC__byte), *bytes, chain->handle); + if(*bytes == 0) + return FLAC__STREAM_DECODER_READ_STATUS_END_OF_STREAM; + else + return FLAC__STREAM_DECODER_READ_STATUS_CONTINUE; + } + else + return FLAC__STREAM_DECODER_READ_STATUS_ABORT; +} + +static FLAC__StreamDecoderWriteStatus chain_read_ogg_write_cb_(const FLAC__StreamDecoder *decoder, const FLAC__Frame *frame, const FLAC__int32 * const buffer[], void *client_data) +{ + (void)decoder, (void)frame, (void)buffer, (void)client_data; + return FLAC__STREAM_DECODER_WRITE_STATUS_ABORT; +} + +static void chain_read_ogg_metadata_cb_(const FLAC__StreamDecoder *decoder, const FLAC__StreamMetadata *metadata, void *client_data) +{ + FLAC__Metadata_Chain *chain = (FLAC__Metadata_Chain*)client_data; + FLAC__Metadata_Node *node; + + (void)decoder; + + node = node_new_(); + if(0 == node) { + chain->status = FLAC__METADATA_CHAIN_STATUS_MEMORY_ALLOCATION_ERROR; + return; + } + + node->data = FLAC__metadata_object_clone(metadata); + if(0 == node->data) { + node_delete_(node); + chain->status = FLAC__METADATA_CHAIN_STATUS_MEMORY_ALLOCATION_ERROR; + return; + } + + chain_append_node_(chain, node); +} + +static void chain_read_ogg_error_cb_(const FLAC__StreamDecoder *decoder, FLAC__StreamDecoderErrorStatus status, void *client_data) +{ + FLAC__Metadata_Chain *chain = (FLAC__Metadata_Chain*)client_data; + (void)decoder, (void)status; + chain->status = FLAC__METADATA_CHAIN_STATUS_INTERNAL_ERROR; /*@@@ maybe needs better error code */ +} + +static FLAC__bool chain_read_ogg_cb_(FLAC__Metadata_Chain *chain, FLAC__IOHandle handle, FLAC__IOCallback_Read read_cb) +{ + FLAC__StreamDecoder *decoder; + + FLAC__ASSERT(0 != chain); + + /* we assume we're already at the beginning of the file */ + + chain->handle = handle; + chain->read_cb = read_cb; + if(0 == (decoder = FLAC__stream_decoder_new())) { + chain->status = FLAC__METADATA_CHAIN_STATUS_MEMORY_ALLOCATION_ERROR; + return false; + } + FLAC__stream_decoder_set_metadata_respond_all(decoder); + if(FLAC__stream_decoder_init_ogg_stream(decoder, chain_read_ogg_read_cb_, /*seek_callback=*/0, /*tell_callback=*/0, /*length_callback=*/0, /*eof_callback=*/0, chain_read_ogg_write_cb_, chain_read_ogg_metadata_cb_, chain_read_ogg_error_cb_, chain) != FLAC__STREAM_DECODER_INIT_STATUS_OK) { + FLAC__stream_decoder_delete(decoder); + chain->status = FLAC__METADATA_CHAIN_STATUS_INTERNAL_ERROR; /*@@@ maybe needs better error code */ + return false; + } + + chain->first_offset = 0; /*@@@ wrong; will need to be set correctly to implement metadata writing for Ogg FLAC */ + + if(!FLAC__stream_decoder_process_until_end_of_metadata(decoder)) + chain->status = FLAC__METADATA_CHAIN_STATUS_INTERNAL_ERROR; /*@@@ maybe needs better error code */ + if(chain->status != FLAC__METADATA_CHAIN_STATUS_OK) { + FLAC__stream_decoder_delete(decoder); + return false; + } + + FLAC__stream_decoder_delete(decoder); + + chain->last_offset = 0; /*@@@ wrong; will need to be set correctly to implement metadata writing for Ogg FLAC */ + + chain->initial_length = chain_calculate_length_(chain); + + return true; +} + +static FLAC__bool chain_rewrite_metadata_in_place_cb_(FLAC__Metadata_Chain *chain, FLAC__IOHandle handle, FLAC__IOCallback_Write write_cb, FLAC__IOCallback_Seek seek_cb) +{ + FLAC__Metadata_Node *node; + + FLAC__ASSERT(0 != chain); + FLAC__ASSERT(0 != chain->head); + + if(0 != seek_cb(handle, chain->first_offset, SEEK_SET)) { + chain->status = FLAC__METADATA_CHAIN_STATUS_SEEK_ERROR; + return false; + } + + for(node = chain->head; node; node = node->next) { + if(!write_metadata_block_header_cb_(handle, write_cb, node->data)) { + chain->status = FLAC__METADATA_CHAIN_STATUS_WRITE_ERROR; + return false; + } + if(!write_metadata_block_data_cb_(handle, write_cb, node->data)) { + chain->status = FLAC__METADATA_CHAIN_STATUS_WRITE_ERROR; + return false; + } + } + + /*FLAC__ASSERT(fflush(), ftello() == chain->last_offset);*/ + + chain->status = FLAC__METADATA_CHAIN_STATUS_OK; + return true; +} + +static FLAC__bool chain_rewrite_metadata_in_place_(FLAC__Metadata_Chain *chain) +{ + FILE *file; + FLAC__bool ret; + + FLAC__ASSERT(0 != chain->filename); + + if(0 == (file = flac_fopen(chain->filename, "r+b"))) { + chain->status = FLAC__METADATA_CHAIN_STATUS_ERROR_OPENING_FILE; + return false; + } + + /* chain_rewrite_metadata_in_place_cb_() sets chain->status for us */ + ret = chain_rewrite_metadata_in_place_cb_(chain, (FLAC__IOHandle)file, (FLAC__IOCallback_Write)fwrite, fseek_wrapper_); + + fclose(file); + + return ret; +} + +static FLAC__bool chain_rewrite_file_(FLAC__Metadata_Chain *chain, const char *tempfile_path_prefix) +{ + FILE *f, *tempfile = NULL; + char *tempfilename; + FLAC__Metadata_SimpleIteratorStatus status; + const FLAC__Metadata_Node *node; + + FLAC__ASSERT(0 != chain); + FLAC__ASSERT(0 != chain->filename); + FLAC__ASSERT(0 != chain->head); + + /* copy the file prefix (data up to first metadata block */ + if(0 == (f = flac_fopen(chain->filename, "rb"))) { + chain->status = FLAC__METADATA_CHAIN_STATUS_ERROR_OPENING_FILE; + return false; + } + if(!open_tempfile_(chain->filename, tempfile_path_prefix, &tempfile, &tempfilename, &status)) { + chain->status = get_equivalent_status_(status); + goto err; + } + if(!copy_n_bytes_from_file_(f, tempfile, chain->first_offset, &status)) { + chain->status = get_equivalent_status_(status); + goto err; + } + + /* write the metadata */ + for(node = chain->head; node; node = node->next) { + if(!write_metadata_block_header_(tempfile, &status, node->data)) { + chain->status = get_equivalent_status_(status); + goto err; + } + if(!write_metadata_block_data_(tempfile, &status, node->data)) { + chain->status = get_equivalent_status_(status); + goto err; + } + } + /*FLAC__ASSERT(fflush(), ftello() == chain->last_offset);*/ + + /* copy the file postfix (everything after the metadata) */ + if(0 != fseeko(f, chain->last_offset, SEEK_SET)) { + chain->status = FLAC__METADATA_CHAIN_STATUS_SEEK_ERROR; + goto err; + } + if(!copy_remaining_bytes_from_file_(f, tempfile, &status)) { + chain->status = get_equivalent_status_(status); + goto err; + } + + /* move the tempfile on top of the original */ + (void)fclose(f); + if(!transport_tempfile_(chain->filename, &tempfile, &tempfilename, &status)) + return false; + + return true; + +err: + (void)fclose(f); + cleanup_tempfile_(&tempfile, &tempfilename); + return false; +} + +/* assumes 'handle' is already at beginning of file */ +static FLAC__bool chain_rewrite_file_cb_(FLAC__Metadata_Chain *chain, FLAC__IOHandle handle, FLAC__IOCallback_Read read_cb, FLAC__IOCallback_Seek seek_cb, FLAC__IOCallback_Eof eof_cb, FLAC__IOHandle temp_handle, FLAC__IOCallback_Write temp_write_cb) +{ + FLAC__Metadata_SimpleIteratorStatus status; + const FLAC__Metadata_Node *node; + + FLAC__ASSERT(0 != chain); + FLAC__ASSERT(0 == chain->filename); + FLAC__ASSERT(0 != chain->head); + + /* copy the file prefix (data up to first metadata block */ + if(!copy_n_bytes_from_file_cb_(handle, read_cb, temp_handle, temp_write_cb, chain->first_offset, &status)) { + chain->status = get_equivalent_status_(status); + return false; + } + + /* write the metadata */ + for(node = chain->head; node; node = node->next) { + if(!write_metadata_block_header_cb_(temp_handle, temp_write_cb, node->data)) { + chain->status = FLAC__METADATA_CHAIN_STATUS_WRITE_ERROR; + return false; + } + if(!write_metadata_block_data_cb_(temp_handle, temp_write_cb, node->data)) { + chain->status = FLAC__METADATA_CHAIN_STATUS_WRITE_ERROR; + return false; + } + } + /*FLAC__ASSERT(fflush(), ftello() == chain->last_offset);*/ + + /* copy the file postfix (everything after the metadata) */ + if(0 != seek_cb(handle, chain->last_offset, SEEK_SET)) { + chain->status = FLAC__METADATA_CHAIN_STATUS_SEEK_ERROR; + return false; + } + if(!copy_remaining_bytes_from_file_cb_(handle, read_cb, eof_cb, temp_handle, temp_write_cb, &status)) { + chain->status = get_equivalent_status_(status); + return false; + } + + return true; +} + +FLAC_API FLAC__Metadata_Chain *FLAC__metadata_chain_new(void) +{ + FLAC__Metadata_Chain *chain = calloc(1, sizeof(FLAC__Metadata_Chain)); + + if(0 != chain) + chain_init_(chain); + + return chain; +} + +FLAC_API void FLAC__metadata_chain_delete(FLAC__Metadata_Chain *chain) +{ + FLAC__ASSERT(0 != chain); + + chain_clear_(chain); + + free(chain); +} + +FLAC_API FLAC__Metadata_ChainStatus FLAC__metadata_chain_status(FLAC__Metadata_Chain *chain) +{ + FLAC__Metadata_ChainStatus status; + + FLAC__ASSERT(0 != chain); + + status = chain->status; + chain->status = FLAC__METADATA_CHAIN_STATUS_OK; + return status; +} + +static FLAC__bool chain_read_(FLAC__Metadata_Chain *chain, const char *filename, FLAC__bool is_ogg) +{ + FILE *file; + FLAC__bool ret; + + FLAC__ASSERT(0 != chain); + FLAC__ASSERT(0 != filename); + + chain_clear_(chain); + + if(0 == (chain->filename = strdup(filename))) { + chain->status = FLAC__METADATA_CHAIN_STATUS_MEMORY_ALLOCATION_ERROR; + return false; + } + + chain->is_ogg = is_ogg; + + if(0 == (file = flac_fopen(filename, "rb"))) { + chain->status = FLAC__METADATA_CHAIN_STATUS_ERROR_OPENING_FILE; + return false; + } + + /* the function also sets chain->status for us */ + ret = is_ogg? + chain_read_ogg_cb_(chain, file, (FLAC__IOCallback_Read)fread) : + chain_read_cb_(chain, file, (FLAC__IOCallback_Read)fread, fseek_wrapper_, ftell_wrapper_) + ; + + fclose(file); + + return ret; +} + +FLAC_API FLAC__bool FLAC__metadata_chain_read(FLAC__Metadata_Chain *chain, const char *filename) +{ + return chain_read_(chain, filename, /*is_ogg=*/false); +} + +/*@@@@add to tests*/ +FLAC_API FLAC__bool FLAC__metadata_chain_read_ogg(FLAC__Metadata_Chain *chain, const char *filename) +{ + return chain_read_(chain, filename, /*is_ogg=*/true); +} + +static FLAC__bool chain_read_with_callbacks_(FLAC__Metadata_Chain *chain, FLAC__IOHandle handle, FLAC__IOCallbacks callbacks, FLAC__bool is_ogg) +{ + FLAC__bool ret; + + FLAC__ASSERT(0 != chain); + + chain_clear_(chain); + + if (0 == callbacks.read || 0 == callbacks.seek || 0 == callbacks.tell) { + chain->status = FLAC__METADATA_CHAIN_STATUS_INVALID_CALLBACKS; + return false; + } + + chain->is_ogg = is_ogg; + + /* rewind */ + if(0 != callbacks.seek(handle, 0, SEEK_SET)) { + chain->status = FLAC__METADATA_CHAIN_STATUS_SEEK_ERROR; + return false; + } + + /* the function also sets chain->status for us */ + ret = is_ogg? + chain_read_ogg_cb_(chain, handle, callbacks.read) : + chain_read_cb_(chain, handle, callbacks.read, callbacks.seek, callbacks.tell) + ; + + return ret; +} + +FLAC_API FLAC__bool FLAC__metadata_chain_read_with_callbacks(FLAC__Metadata_Chain *chain, FLAC__IOHandle handle, FLAC__IOCallbacks callbacks) +{ + return chain_read_with_callbacks_(chain, handle, callbacks, /*is_ogg=*/false); +} + +/*@@@@add to tests*/ +FLAC_API FLAC__bool FLAC__metadata_chain_read_ogg_with_callbacks(FLAC__Metadata_Chain *chain, FLAC__IOHandle handle, FLAC__IOCallbacks callbacks) +{ + return chain_read_with_callbacks_(chain, handle, callbacks, /*is_ogg=*/true); +} + +typedef enum { + LBS_NONE = 0, + LBS_SIZE_CHANGED, + LBS_BLOCK_ADDED, + LBS_BLOCK_REMOVED +} LastBlockState; + +FLAC_API FLAC__bool FLAC__metadata_chain_check_if_tempfile_needed(FLAC__Metadata_Chain *chain, FLAC__bool use_padding) +{ + /* This does all the same checks that are in chain_prepare_for_write_() + * but doesn't actually alter the chain. Make sure to update the logic + * here if chain_prepare_for_write_() changes. + */ + FLAC__off_t current_length; + LastBlockState lbs_state = LBS_NONE; + unsigned lbs_size = 0; + + FLAC__ASSERT(0 != chain); + + current_length = chain_calculate_length_(chain); + + if(use_padding) { + const FLAC__Metadata_Node * const node = chain->tail; + /* if the metadata shrank and the last block is padding, we just extend the last padding block */ + if(current_length < chain->initial_length && node->data->type == FLAC__METADATA_TYPE_PADDING) { + lbs_state = LBS_SIZE_CHANGED; + lbs_size = node->data->length + (chain->initial_length - current_length); + } + /* if the metadata shrank more than 4 bytes then there's room to add another padding block */ + else if(current_length + (FLAC__off_t)FLAC__STREAM_METADATA_HEADER_LENGTH <= chain->initial_length) { + lbs_state = LBS_BLOCK_ADDED; + lbs_size = chain->initial_length - (current_length + (FLAC__off_t)FLAC__STREAM_METADATA_HEADER_LENGTH); + } + /* if the metadata grew but the last block is padding, try cutting the padding to restore the original length so we don't have to rewrite the whole file */ + else if(current_length > chain->initial_length) { + const FLAC__off_t delta = current_length - chain->initial_length; + if(node->data->type == FLAC__METADATA_TYPE_PADDING) { + /* if the delta is exactly the size of the last padding block, remove the padding block */ + if((FLAC__off_t)node->data->length + (FLAC__off_t)FLAC__STREAM_METADATA_HEADER_LENGTH == delta) { + lbs_state = LBS_BLOCK_REMOVED; + lbs_size = 0; + } + /* if there is at least 'delta' bytes of padding, trim the padding down */ + else if((FLAC__off_t)node->data->length >= delta) { + lbs_state = LBS_SIZE_CHANGED; + lbs_size = node->data->length - delta; + } + } + } + } + + current_length = 0; + /* check sizes of all metadata blocks; reduce padding size if necessary */ + { + const FLAC__Metadata_Node *node; + for(node = chain->head; node; node = node->next) { + unsigned block_len = node->data->length; + if(node == chain->tail) { + if(lbs_state == LBS_BLOCK_REMOVED) + continue; + else if(lbs_state == LBS_SIZE_CHANGED) + block_len = lbs_size; + } + if(block_len >= (1u << FLAC__STREAM_METADATA_LENGTH_LEN)) { + if(node->data->type == FLAC__METADATA_TYPE_PADDING) + block_len = (1u << FLAC__STREAM_METADATA_LENGTH_LEN) - 1; + else + return false /* the return value doesn't matter */; + } + current_length += (FLAC__STREAM_METADATA_HEADER_LENGTH + block_len); + } + + if(lbs_state == LBS_BLOCK_ADDED) { + /* test added padding block */ + unsigned block_len = lbs_size; + if(block_len >= (1u << FLAC__STREAM_METADATA_LENGTH_LEN)) + block_len = (1u << FLAC__STREAM_METADATA_LENGTH_LEN) - 1; + current_length += (FLAC__STREAM_METADATA_HEADER_LENGTH + block_len); + } + } + + return (current_length != chain->initial_length); +} + +FLAC_API FLAC__bool FLAC__metadata_chain_write(FLAC__Metadata_Chain *chain, FLAC__bool use_padding, FLAC__bool preserve_file_stats) +{ + struct flac_stat_s stats; + const char *tempfile_path_prefix = 0; + FLAC__off_t current_length; + + FLAC__ASSERT(0 != chain); + + if (chain->is_ogg) { /* cannot write back to Ogg FLAC yet */ + chain->status = FLAC__METADATA_CHAIN_STATUS_INTERNAL_ERROR; + return false; + } + + if (0 == chain->filename) { + chain->status = FLAC__METADATA_CHAIN_STATUS_READ_WRITE_MISMATCH; + return false; + } + + current_length = chain_prepare_for_write_(chain, use_padding); + + /* a return value of 0 means there was an error; chain->status is already set */ + if (0 == current_length) + return false; + + if(preserve_file_stats) + get_file_stats_(chain->filename, &stats); + + if(current_length == chain->initial_length) { + if(!chain_rewrite_metadata_in_place_(chain)) + return false; + } + else { + if(!chain_rewrite_file_(chain, tempfile_path_prefix)) + return false; + + /* recompute lengths and offsets */ + { + const FLAC__Metadata_Node *node; + chain->initial_length = current_length; + chain->last_offset = chain->first_offset; + for(node = chain->head; node; node = node->next) + chain->last_offset += (FLAC__STREAM_METADATA_HEADER_LENGTH + node->data->length); + } + } + + if(preserve_file_stats) + set_file_stats_(chain->filename, &stats); + + return true; +} + +FLAC_API FLAC__bool FLAC__metadata_chain_write_with_callbacks(FLAC__Metadata_Chain *chain, FLAC__bool use_padding, FLAC__IOHandle handle, FLAC__IOCallbacks callbacks) +{ + FLAC__off_t current_length; + + FLAC__ASSERT(0 != chain); + + if (chain->is_ogg) { /* cannot write back to Ogg FLAC yet */ + chain->status = FLAC__METADATA_CHAIN_STATUS_INTERNAL_ERROR; + return false; + } + + if (0 != chain->filename) { + chain->status = FLAC__METADATA_CHAIN_STATUS_READ_WRITE_MISMATCH; + return false; + } + + if (0 == callbacks.write || 0 == callbacks.seek) { + chain->status = FLAC__METADATA_CHAIN_STATUS_INVALID_CALLBACKS; + return false; + } + + if (FLAC__metadata_chain_check_if_tempfile_needed(chain, use_padding)) { + chain->status = FLAC__METADATA_CHAIN_STATUS_WRONG_WRITE_CALL; + return false; + } + + current_length = chain_prepare_for_write_(chain, use_padding); + + /* a return value of 0 means there was an error; chain->status is already set */ + if (0 == current_length) + return false; + + FLAC__ASSERT(current_length == chain->initial_length); + + return chain_rewrite_metadata_in_place_cb_(chain, handle, callbacks.write, callbacks.seek); +} + +FLAC_API FLAC__bool FLAC__metadata_chain_write_with_callbacks_and_tempfile(FLAC__Metadata_Chain *chain, FLAC__bool use_padding, FLAC__IOHandle handle, FLAC__IOCallbacks callbacks, FLAC__IOHandle temp_handle, FLAC__IOCallbacks temp_callbacks) +{ + FLAC__off_t current_length; + + FLAC__ASSERT(0 != chain); + + if (chain->is_ogg) { /* cannot write back to Ogg FLAC yet */ + chain->status = FLAC__METADATA_CHAIN_STATUS_INTERNAL_ERROR; + return false; + } + + if (0 != chain->filename) { + chain->status = FLAC__METADATA_CHAIN_STATUS_READ_WRITE_MISMATCH; + return false; + } + + if (0 == callbacks.read || 0 == callbacks.seek || 0 == callbacks.eof) { + chain->status = FLAC__METADATA_CHAIN_STATUS_INVALID_CALLBACKS; + return false; + } + if (0 == temp_callbacks.write) { + chain->status = FLAC__METADATA_CHAIN_STATUS_INVALID_CALLBACKS; + return false; + } + + if (!FLAC__metadata_chain_check_if_tempfile_needed(chain, use_padding)) { + chain->status = FLAC__METADATA_CHAIN_STATUS_WRONG_WRITE_CALL; + return false; + } + + current_length = chain_prepare_for_write_(chain, use_padding); + + /* a return value of 0 means there was an error; chain->status is already set */ + if (0 == current_length) + return false; + + FLAC__ASSERT(current_length != chain->initial_length); + + /* rewind */ + if(0 != callbacks.seek(handle, 0, SEEK_SET)) { + chain->status = FLAC__METADATA_CHAIN_STATUS_SEEK_ERROR; + return false; + } + + if(!chain_rewrite_file_cb_(chain, handle, callbacks.read, callbacks.seek, callbacks.eof, temp_handle, temp_callbacks.write)) + return false; + + /* recompute lengths and offsets */ + { + const FLAC__Metadata_Node *node; + chain->initial_length = current_length; + chain->last_offset = chain->first_offset; + for(node = chain->head; node; node = node->next) + chain->last_offset += (FLAC__STREAM_METADATA_HEADER_LENGTH + node->data->length); + } + + return true; +} + +FLAC_API void FLAC__metadata_chain_merge_padding(FLAC__Metadata_Chain *chain) +{ + FLAC__Metadata_Node *node; + + FLAC__ASSERT(0 != chain); + + for(node = chain->head; node; ) { + if(!chain_merge_adjacent_padding_(chain, node)) + node = node->next; + } +} + +FLAC_API void FLAC__metadata_chain_sort_padding(FLAC__Metadata_Chain *chain) +{ + FLAC__Metadata_Node *node, *save; + unsigned i; + + FLAC__ASSERT(0 != chain); + + /* + * Don't try and be too smart... this simple algo is good enough for + * the small number of nodes that we deal with. + */ + for(i = 0, node = chain->head; i < chain->nodes; i++) { + if(node->data->type == FLAC__METADATA_TYPE_PADDING) { + save = node->next; + chain_remove_node_(chain, node); + chain_append_node_(chain, node); + node = save; + } + else { + node = node->next; + } + } + + FLAC__metadata_chain_merge_padding(chain); +} + + +FLAC_API FLAC__Metadata_Iterator *FLAC__metadata_iterator_new(void) +{ + FLAC__Metadata_Iterator *iterator = calloc(1, sizeof(FLAC__Metadata_Iterator)); + + /* calloc() implies: + iterator->current = 0; + iterator->chain = 0; + */ + + return iterator; +} + +FLAC_API void FLAC__metadata_iterator_delete(FLAC__Metadata_Iterator *iterator) +{ + FLAC__ASSERT(0 != iterator); + + free(iterator); +} + +FLAC_API void FLAC__metadata_iterator_init(FLAC__Metadata_Iterator *iterator, FLAC__Metadata_Chain *chain) +{ + FLAC__ASSERT(0 != iterator); + FLAC__ASSERT(0 != chain); + FLAC__ASSERT(0 != chain->head); + + iterator->chain = chain; + iterator->current = chain->head; +} + +FLAC_API FLAC__bool FLAC__metadata_iterator_next(FLAC__Metadata_Iterator *iterator) +{ + FLAC__ASSERT(0 != iterator); + + if(0 == iterator->current || 0 == iterator->current->next) + return false; + + iterator->current = iterator->current->next; + return true; +} + +FLAC_API FLAC__bool FLAC__metadata_iterator_prev(FLAC__Metadata_Iterator *iterator) +{ + FLAC__ASSERT(0 != iterator); + + if(0 == iterator->current || 0 == iterator->current->prev) + return false; + + iterator->current = iterator->current->prev; + return true; +} + +FLAC_API FLAC__MetadataType FLAC__metadata_iterator_get_block_type(const FLAC__Metadata_Iterator *iterator) +{ + FLAC__ASSERT(0 != iterator); + FLAC__ASSERT(0 != iterator->current); + FLAC__ASSERT(0 != iterator->current->data); + + return iterator->current->data->type; +} + +FLAC_API FLAC__StreamMetadata *FLAC__metadata_iterator_get_block(FLAC__Metadata_Iterator *iterator) +{ + FLAC__ASSERT(0 != iterator); + FLAC__ASSERT(0 != iterator->current); + + return iterator->current->data; +} + +FLAC_API FLAC__bool FLAC__metadata_iterator_set_block(FLAC__Metadata_Iterator *iterator, FLAC__StreamMetadata *block) +{ + FLAC__ASSERT(0 != iterator); + FLAC__ASSERT(0 != block); + return FLAC__metadata_iterator_delete_block(iterator, false) && FLAC__metadata_iterator_insert_block_after(iterator, block); +} + +FLAC_API FLAC__bool FLAC__metadata_iterator_delete_block(FLAC__Metadata_Iterator *iterator, FLAC__bool replace_with_padding) +{ + FLAC__Metadata_Node *save; + + FLAC__ASSERT(0 != iterator); + FLAC__ASSERT(0 != iterator->current); + + if(0 == iterator->current->prev) { + FLAC__ASSERT(iterator->current->data->type == FLAC__METADATA_TYPE_STREAMINFO); + return false; + } + + save = iterator->current->prev; + + if(replace_with_padding) { + FLAC__metadata_object_delete_data(iterator->current->data); + iterator->current->data->type = FLAC__METADATA_TYPE_PADDING; + } + else { + chain_delete_node_(iterator->chain, iterator->current); + } + + iterator->current = save; + return true; +} + +FLAC_API FLAC__bool FLAC__metadata_iterator_insert_block_before(FLAC__Metadata_Iterator *iterator, FLAC__StreamMetadata *block) +{ + FLAC__Metadata_Node *node; + + FLAC__ASSERT(0 != iterator); + FLAC__ASSERT(0 != iterator->current); + FLAC__ASSERT(0 != block); + + if(block->type == FLAC__METADATA_TYPE_STREAMINFO) + return false; + + if(0 == iterator->current->prev) { + FLAC__ASSERT(iterator->current->data->type == FLAC__METADATA_TYPE_STREAMINFO); + return false; + } + + if(0 == (node = node_new_())) + return false; + + node->data = block; + iterator_insert_node_(iterator, node); + iterator->current = node; + return true; +} + +FLAC_API FLAC__bool FLAC__metadata_iterator_insert_block_after(FLAC__Metadata_Iterator *iterator, FLAC__StreamMetadata *block) +{ + FLAC__Metadata_Node *node; + + FLAC__ASSERT(0 != iterator); + FLAC__ASSERT(0 != iterator->current); + FLAC__ASSERT(0 != block); + + if(block->type == FLAC__METADATA_TYPE_STREAMINFO) + return false; + + if(0 == (node = node_new_())) + return false; + + node->data = block; + iterator_insert_node_after_(iterator, node); + iterator->current = node; + return true; +} + + +/**************************************************************************** + * + * Local function definitions + * + ***************************************************************************/ + +void pack_uint32_(FLAC__uint32 val, FLAC__byte *b, unsigned bytes) +{ + unsigned i; + + b += bytes; + + for(i = 0; i < bytes; i++) { + *(--b) = (FLAC__byte)(val & 0xff); + val >>= 8; + } +} + +void pack_uint32_little_endian_(FLAC__uint32 val, FLAC__byte *b, unsigned bytes) +{ + unsigned i; + + for(i = 0; i < bytes; i++) { + *(b++) = (FLAC__byte)(val & 0xff); + val >>= 8; + } +} + +void pack_uint64_(FLAC__uint64 val, FLAC__byte *b, unsigned bytes) +{ + unsigned i; + + b += bytes; + + for(i = 0; i < bytes; i++) { + *(--b) = (FLAC__byte)(val & 0xff); + val >>= 8; + } +} + +FLAC__uint32 unpack_uint32_(FLAC__byte *b, unsigned bytes) +{ + FLAC__uint32 ret = 0; + unsigned i; + + for(i = 0; i < bytes; i++) + ret = (ret << 8) | (FLAC__uint32)(*b++); + + return ret; +} + +FLAC__uint32 unpack_uint32_little_endian_(FLAC__byte *b, unsigned bytes) +{ + FLAC__uint32 ret = 0; + unsigned i; + + b += bytes; + + for(i = 0; i < bytes; i++) + ret = (ret << 8) | (FLAC__uint32)(*--b); + + return ret; +} + +FLAC__uint64 unpack_uint64_(FLAC__byte *b, unsigned bytes) +{ + FLAC__uint64 ret = 0; + unsigned i; + + for(i = 0; i < bytes; i++) + ret = (ret << 8) | (FLAC__uint64)(*b++); + + return ret; +} + +FLAC__bool read_metadata_block_header_(FLAC__Metadata_SimpleIterator *iterator) +{ + FLAC__ASSERT(0 != iterator); + FLAC__ASSERT(0 != iterator->file); + + if(!read_metadata_block_header_cb_((FLAC__IOHandle)iterator->file, (FLAC__IOCallback_Read)fread, &iterator->is_last, &iterator->type, &iterator->length)) { + iterator->status = FLAC__METADATA_SIMPLE_ITERATOR_STATUS_READ_ERROR; + return false; + } + + return true; +} + +FLAC__bool read_metadata_block_data_(FLAC__Metadata_SimpleIterator *iterator, FLAC__StreamMetadata *block) +{ + FLAC__ASSERT(0 != iterator); + FLAC__ASSERT(0 != iterator->file); + + iterator->status = read_metadata_block_data_cb_((FLAC__IOHandle)iterator->file, (FLAC__IOCallback_Read)fread, fseek_wrapper_, block); + + return (iterator->status == FLAC__METADATA_SIMPLE_ITERATOR_STATUS_OK); +} + +FLAC__bool read_metadata_block_header_cb_(FLAC__IOHandle handle, FLAC__IOCallback_Read read_cb, FLAC__bool *is_last, FLAC__MetadataType *type, unsigned *length) +{ + FLAC__byte raw_header[FLAC__STREAM_METADATA_HEADER_LENGTH]; + + if(read_cb(raw_header, 1, FLAC__STREAM_METADATA_HEADER_LENGTH, handle) != FLAC__STREAM_METADATA_HEADER_LENGTH) + return false; + + *is_last = raw_header[0] & 0x80? true : false; + *type = (FLAC__MetadataType)(raw_header[0] & 0x7f); + *length = unpack_uint32_(raw_header + 1, 3); + + /* Note that we don't check: + * if(iterator->type >= FLAC__METADATA_TYPE_UNDEFINED) + * we just will read in an opaque block + */ + + return true; +} + +FLAC__Metadata_SimpleIteratorStatus read_metadata_block_data_cb_(FLAC__IOHandle handle, FLAC__IOCallback_Read read_cb, FLAC__IOCallback_Seek seek_cb, FLAC__StreamMetadata *block) +{ + switch(block->type) { + case FLAC__METADATA_TYPE_STREAMINFO: + return read_metadata_block_data_streaminfo_cb_(handle, read_cb, &block->data.stream_info); + case FLAC__METADATA_TYPE_PADDING: + return read_metadata_block_data_padding_cb_(handle, seek_cb, &block->data.padding, block->length); + case FLAC__METADATA_TYPE_APPLICATION: + return read_metadata_block_data_application_cb_(handle, read_cb, &block->data.application, block->length); + case FLAC__METADATA_TYPE_SEEKTABLE: + return read_metadata_block_data_seektable_cb_(handle, read_cb, &block->data.seek_table, block->length); + case FLAC__METADATA_TYPE_VORBIS_COMMENT: + return read_metadata_block_data_vorbis_comment_cb_(handle, read_cb, seek_cb, &block->data.vorbis_comment, block->length); + case FLAC__METADATA_TYPE_CUESHEET: + return read_metadata_block_data_cuesheet_cb_(handle, read_cb, &block->data.cue_sheet); + case FLAC__METADATA_TYPE_PICTURE: + return read_metadata_block_data_picture_cb_(handle, read_cb, &block->data.picture); + default: + return read_metadata_block_data_unknown_cb_(handle, read_cb, &block->data.unknown, block->length); + } +} + +FLAC__Metadata_SimpleIteratorStatus read_metadata_block_data_streaminfo_cb_(FLAC__IOHandle handle, FLAC__IOCallback_Read read_cb, FLAC__StreamMetadata_StreamInfo *block) +{ + FLAC__byte buffer[FLAC__STREAM_METADATA_STREAMINFO_LENGTH], *b; + + if(read_cb(buffer, 1, FLAC__STREAM_METADATA_STREAMINFO_LENGTH, handle) != FLAC__STREAM_METADATA_STREAMINFO_LENGTH) + return FLAC__METADATA_SIMPLE_ITERATOR_STATUS_READ_ERROR; + + b = buffer; + + /* we are using hardcoded numbers for simplicity but we should + * probably eventually write a bit-level unpacker and use the + * _STREAMINFO_ constants. + */ + block->min_blocksize = unpack_uint32_(b, 2); b += 2; + block->max_blocksize = unpack_uint32_(b, 2); b += 2; + block->min_framesize = unpack_uint32_(b, 3); b += 3; + block->max_framesize = unpack_uint32_(b, 3); b += 3; + block->sample_rate = (unpack_uint32_(b, 2) << 4) | ((unsigned)(b[2] & 0xf0) >> 4); + block->channels = (unsigned)((b[2] & 0x0e) >> 1) + 1; + block->bits_per_sample = ((((unsigned)(b[2] & 0x01)) << 4) | (((unsigned)(b[3] & 0xf0)) >> 4)) + 1; + block->total_samples = (((FLAC__uint64)(b[3] & 0x0f)) << 32) | unpack_uint64_(b+4, 4); + memcpy(block->md5sum, b+8, 16); + + return FLAC__METADATA_SIMPLE_ITERATOR_STATUS_OK; +} + +FLAC__Metadata_SimpleIteratorStatus read_metadata_block_data_padding_cb_(FLAC__IOHandle handle, FLAC__IOCallback_Seek seek_cb, FLAC__StreamMetadata_Padding *block, unsigned block_length) +{ + (void)block; /* nothing to do; we don't care about reading the padding bytes */ + + if(0 != seek_cb(handle, block_length, SEEK_CUR)) + return FLAC__METADATA_SIMPLE_ITERATOR_STATUS_SEEK_ERROR; + + return FLAC__METADATA_SIMPLE_ITERATOR_STATUS_OK; +} + +FLAC__Metadata_SimpleIteratorStatus read_metadata_block_data_application_cb_(FLAC__IOHandle handle, FLAC__IOCallback_Read read_cb, FLAC__StreamMetadata_Application *block, unsigned block_length) +{ + const unsigned id_bytes = FLAC__STREAM_METADATA_APPLICATION_ID_LEN / 8; + + if(read_cb(block->id, 1, id_bytes, handle) != id_bytes) + return FLAC__METADATA_SIMPLE_ITERATOR_STATUS_READ_ERROR; + + if(block_length < id_bytes) + return FLAC__METADATA_SIMPLE_ITERATOR_STATUS_READ_ERROR; + + block_length -= id_bytes; + + if(block_length == 0) { + block->data = 0; + } + else { + if(0 == (block->data = malloc(block_length))) + return FLAC__METADATA_SIMPLE_ITERATOR_STATUS_MEMORY_ALLOCATION_ERROR; + + if(read_cb(block->data, 1, block_length, handle) != block_length) + return FLAC__METADATA_SIMPLE_ITERATOR_STATUS_READ_ERROR; + } + + return FLAC__METADATA_SIMPLE_ITERATOR_STATUS_OK; +} + +FLAC__Metadata_SimpleIteratorStatus read_metadata_block_data_seektable_cb_(FLAC__IOHandle handle, FLAC__IOCallback_Read read_cb, FLAC__StreamMetadata_SeekTable *block, unsigned block_length) +{ + unsigned i; + FLAC__byte buffer[FLAC__STREAM_METADATA_SEEKPOINT_LENGTH]; + + FLAC__ASSERT(block_length % FLAC__STREAM_METADATA_SEEKPOINT_LENGTH == 0); + + block->num_points = block_length / FLAC__STREAM_METADATA_SEEKPOINT_LENGTH; + + if(block->num_points == 0) + block->points = 0; + else if(0 == (block->points = safe_malloc_mul_2op_p(block->num_points, /*times*/sizeof(FLAC__StreamMetadata_SeekPoint)))) + return FLAC__METADATA_SIMPLE_ITERATOR_STATUS_MEMORY_ALLOCATION_ERROR; + + for(i = 0; i < block->num_points; i++) { + if(read_cb(buffer, 1, FLAC__STREAM_METADATA_SEEKPOINT_LENGTH, handle) != FLAC__STREAM_METADATA_SEEKPOINT_LENGTH) + return FLAC__METADATA_SIMPLE_ITERATOR_STATUS_READ_ERROR; + /* some MAGIC NUMBERs here */ + block->points[i].sample_number = unpack_uint64_(buffer, 8); + block->points[i].stream_offset = unpack_uint64_(buffer+8, 8); + block->points[i].frame_samples = unpack_uint32_(buffer+16, 2); + } + + return FLAC__METADATA_SIMPLE_ITERATOR_STATUS_OK; +} + +FLAC__Metadata_SimpleIteratorStatus read_metadata_block_data_vorbis_comment_entry_cb_(FLAC__IOHandle handle, FLAC__IOCallback_Read read_cb, FLAC__StreamMetadata_VorbisComment_Entry *entry, unsigned max_length) +{ + const unsigned entry_length_len = FLAC__STREAM_METADATA_VORBIS_COMMENT_ENTRY_LENGTH_LEN / 8; + FLAC__byte buffer[4]; /* magic number is asserted below */ + + FLAC__ASSERT(FLAC__STREAM_METADATA_VORBIS_COMMENT_ENTRY_LENGTH_LEN / 8 == sizeof(buffer)); + + if(max_length < entry_length_len) + return FLAC__METADATA_SIMPLE_ITERATOR_STATUS_BAD_METADATA; + + max_length -= entry_length_len; + if(read_cb(buffer, 1, entry_length_len, handle) != entry_length_len) + return FLAC__METADATA_SIMPLE_ITERATOR_STATUS_READ_ERROR; + entry->length = unpack_uint32_little_endian_(buffer, entry_length_len); + if(max_length < entry->length) { + entry->length = 0; + return FLAC__METADATA_SIMPLE_ITERATOR_STATUS_BAD_METADATA; + } else max_length -= entry->length; + + if(0 != entry->entry) + free(entry->entry); + + if(entry->length == 0) { + entry->entry = 0; + } + else { + if(0 == (entry->entry = safe_malloc_add_2op_(entry->length, /*+*/1))) + return FLAC__METADATA_SIMPLE_ITERATOR_STATUS_MEMORY_ALLOCATION_ERROR; + + if(read_cb(entry->entry, 1, entry->length, handle) != entry->length) + return FLAC__METADATA_SIMPLE_ITERATOR_STATUS_READ_ERROR; + + entry->entry[entry->length] = '\0'; + } + + return FLAC__METADATA_SIMPLE_ITERATOR_STATUS_OK; +} + +FLAC__Metadata_SimpleIteratorStatus read_metadata_block_data_vorbis_comment_cb_(FLAC__IOHandle handle, FLAC__IOCallback_Read read_cb, FLAC__IOCallback_Seek seek_cb, FLAC__StreamMetadata_VorbisComment *block, unsigned block_length) +{ + unsigned i; + FLAC__Metadata_SimpleIteratorStatus status; + const unsigned num_comments_len = FLAC__STREAM_METADATA_VORBIS_COMMENT_NUM_COMMENTS_LEN / 8; + FLAC__byte buffer[4]; /* magic number is asserted below */ + + FLAC__ASSERT(FLAC__STREAM_METADATA_VORBIS_COMMENT_NUM_COMMENTS_LEN / 8 == sizeof(buffer)); + + status = read_metadata_block_data_vorbis_comment_entry_cb_(handle, read_cb, &(block->vendor_string), block_length); + if(block_length >= 4) + block_length -= 4; + if(status == FLAC__METADATA_SIMPLE_ITERATOR_STATUS_BAD_METADATA) + goto skip; + else if(status != FLAC__METADATA_SIMPLE_ITERATOR_STATUS_OK) + return status; + block_length -= block->vendor_string.length; + + if(block_length < num_comments_len) goto skip; else block_length -= num_comments_len; + if(read_cb(buffer, 1, num_comments_len, handle) != num_comments_len) + return FLAC__METADATA_SIMPLE_ITERATOR_STATUS_READ_ERROR; + block->num_comments = unpack_uint32_little_endian_(buffer, num_comments_len); + + if(block->num_comments == 0) { + block->comments = 0; + } + else if(0 == (block->comments = calloc(block->num_comments, sizeof(FLAC__StreamMetadata_VorbisComment_Entry)))) { + block->num_comments = 0; + return FLAC__METADATA_SIMPLE_ITERATOR_STATUS_MEMORY_ALLOCATION_ERROR; + } + + for(i = 0; i < block->num_comments; i++) { + status = read_metadata_block_data_vorbis_comment_entry_cb_(handle, read_cb, block->comments + i, block_length); + if(block_length >= 4) block_length -= 4; + if(status == FLAC__METADATA_SIMPLE_ITERATOR_STATUS_BAD_METADATA) { + block->num_comments = i; + goto skip; + } + else if(status != FLAC__METADATA_SIMPLE_ITERATOR_STATUS_OK) return status; + block_length -= block->comments[i].length; + } + + skip: + if(block_length > 0) { + /* bad metadata */ + if(0 != seek_cb(handle, block_length, SEEK_CUR)) + return FLAC__METADATA_SIMPLE_ITERATOR_STATUS_SEEK_ERROR; + } + + return FLAC__METADATA_SIMPLE_ITERATOR_STATUS_OK; +} + +FLAC__Metadata_SimpleIteratorStatus read_metadata_block_data_cuesheet_track_cb_(FLAC__IOHandle handle, FLAC__IOCallback_Read read_cb, FLAC__StreamMetadata_CueSheet_Track *track) +{ + unsigned i, len; + FLAC__byte buffer[32]; /* asserted below that this is big enough */ + + FLAC__ASSERT(sizeof(buffer) >= sizeof(FLAC__uint64)); + FLAC__ASSERT(sizeof(buffer) >= FLAC__STREAM_METADATA_CUESHEET_INDEX_RESERVED_LEN/8); + FLAC__ASSERT(sizeof(buffer) >= (FLAC__STREAM_METADATA_CUESHEET_TRACK_TYPE_LEN + FLAC__STREAM_METADATA_CUESHEET_TRACK_PRE_EMPHASIS_LEN + FLAC__STREAM_METADATA_CUESHEET_TRACK_RESERVED_LEN) / 8); + + FLAC__ASSERT(FLAC__STREAM_METADATA_CUESHEET_TRACK_OFFSET_LEN % 8 == 0); + len = FLAC__STREAM_METADATA_CUESHEET_TRACK_OFFSET_LEN / 8; + if(read_cb(buffer, 1, len, handle) != len) + return FLAC__METADATA_SIMPLE_ITERATOR_STATUS_READ_ERROR; + track->offset = unpack_uint64_(buffer, len); + + FLAC__ASSERT(FLAC__STREAM_METADATA_CUESHEET_TRACK_NUMBER_LEN % 8 == 0); + len = FLAC__STREAM_METADATA_CUESHEET_TRACK_NUMBER_LEN / 8; + if(read_cb(buffer, 1, len, handle) != len) + return FLAC__METADATA_SIMPLE_ITERATOR_STATUS_READ_ERROR; + track->number = (FLAC__byte)unpack_uint32_(buffer, len); + + FLAC__ASSERT(FLAC__STREAM_METADATA_CUESHEET_TRACK_ISRC_LEN % 8 == 0); + len = FLAC__STREAM_METADATA_CUESHEET_TRACK_ISRC_LEN / 8; + if(read_cb(track->isrc, 1, len, handle) != len) + return FLAC__METADATA_SIMPLE_ITERATOR_STATUS_READ_ERROR; + + FLAC__ASSERT((FLAC__STREAM_METADATA_CUESHEET_TRACK_TYPE_LEN + FLAC__STREAM_METADATA_CUESHEET_TRACK_PRE_EMPHASIS_LEN + FLAC__STREAM_METADATA_CUESHEET_TRACK_RESERVED_LEN) % 8 == 0); + len = (FLAC__STREAM_METADATA_CUESHEET_TRACK_TYPE_LEN + FLAC__STREAM_METADATA_CUESHEET_TRACK_PRE_EMPHASIS_LEN + FLAC__STREAM_METADATA_CUESHEET_TRACK_RESERVED_LEN) / 8; + if(read_cb(buffer, 1, len, handle) != len) + return FLAC__METADATA_SIMPLE_ITERATOR_STATUS_READ_ERROR; + FLAC__ASSERT(FLAC__STREAM_METADATA_CUESHEET_TRACK_TYPE_LEN == 1); + FLAC__ASSERT(FLAC__STREAM_METADATA_CUESHEET_TRACK_PRE_EMPHASIS_LEN == 1); + track->type = buffer[0] >> 7; + track->pre_emphasis = (buffer[0] >> 6) & 1; + + FLAC__ASSERT(FLAC__STREAM_METADATA_CUESHEET_TRACK_NUM_INDICES_LEN % 8 == 0); + len = FLAC__STREAM_METADATA_CUESHEET_TRACK_NUM_INDICES_LEN / 8; + if(read_cb(buffer, 1, len, handle) != len) + return FLAC__METADATA_SIMPLE_ITERATOR_STATUS_READ_ERROR; + track->num_indices = (FLAC__byte)unpack_uint32_(buffer, len); + + if(track->num_indices == 0) { + track->indices = 0; + } + else if(0 == (track->indices = calloc(track->num_indices, sizeof(FLAC__StreamMetadata_CueSheet_Index)))) + return FLAC__METADATA_SIMPLE_ITERATOR_STATUS_MEMORY_ALLOCATION_ERROR; + + for(i = 0; i < track->num_indices; i++) { + FLAC__ASSERT(FLAC__STREAM_METADATA_CUESHEET_INDEX_OFFSET_LEN % 8 == 0); + len = FLAC__STREAM_METADATA_CUESHEET_INDEX_OFFSET_LEN / 8; + if(read_cb(buffer, 1, len, handle) != len) + return FLAC__METADATA_SIMPLE_ITERATOR_STATUS_READ_ERROR; + track->indices[i].offset = unpack_uint64_(buffer, len); + + FLAC__ASSERT(FLAC__STREAM_METADATA_CUESHEET_INDEX_NUMBER_LEN % 8 == 0); + len = FLAC__STREAM_METADATA_CUESHEET_INDEX_NUMBER_LEN / 8; + if(read_cb(buffer, 1, len, handle) != len) + return FLAC__METADATA_SIMPLE_ITERATOR_STATUS_READ_ERROR; + track->indices[i].number = (FLAC__byte)unpack_uint32_(buffer, len); + + FLAC__ASSERT(FLAC__STREAM_METADATA_CUESHEET_INDEX_RESERVED_LEN % 8 == 0); + len = FLAC__STREAM_METADATA_CUESHEET_INDEX_RESERVED_LEN / 8; + if(read_cb(buffer, 1, len, handle) != len) + return FLAC__METADATA_SIMPLE_ITERATOR_STATUS_READ_ERROR; + } + + return FLAC__METADATA_SIMPLE_ITERATOR_STATUS_OK; +} + +FLAC__Metadata_SimpleIteratorStatus read_metadata_block_data_cuesheet_cb_(FLAC__IOHandle handle, FLAC__IOCallback_Read read_cb, FLAC__StreamMetadata_CueSheet *block) +{ + unsigned i, len; + FLAC__Metadata_SimpleIteratorStatus status; + FLAC__byte buffer[1024]; /* MSVC needs a constant expression so we put a magic number and assert */ + + FLAC__ASSERT((FLAC__STREAM_METADATA_CUESHEET_IS_CD_LEN + FLAC__STREAM_METADATA_CUESHEET_RESERVED_LEN)/8 <= sizeof(buffer)); + FLAC__ASSERT(sizeof(FLAC__uint64) <= sizeof(buffer)); + + FLAC__ASSERT(FLAC__STREAM_METADATA_CUESHEET_MEDIA_CATALOG_NUMBER_LEN % 8 == 0); + len = FLAC__STREAM_METADATA_CUESHEET_MEDIA_CATALOG_NUMBER_LEN / 8; + if(read_cb(block->media_catalog_number, 1, len, handle) != len) + return FLAC__METADATA_SIMPLE_ITERATOR_STATUS_READ_ERROR; + + FLAC__ASSERT(FLAC__STREAM_METADATA_CUESHEET_LEAD_IN_LEN % 8 == 0); + len = FLAC__STREAM_METADATA_CUESHEET_LEAD_IN_LEN / 8; + if(read_cb(buffer, 1, len, handle) != len) + return FLAC__METADATA_SIMPLE_ITERATOR_STATUS_READ_ERROR; + block->lead_in = unpack_uint64_(buffer, len); + + FLAC__ASSERT((FLAC__STREAM_METADATA_CUESHEET_IS_CD_LEN + FLAC__STREAM_METADATA_CUESHEET_RESERVED_LEN) % 8 == 0); + len = (FLAC__STREAM_METADATA_CUESHEET_IS_CD_LEN + FLAC__STREAM_METADATA_CUESHEET_RESERVED_LEN) / 8; + if(read_cb(buffer, 1, len, handle) != len) + return FLAC__METADATA_SIMPLE_ITERATOR_STATUS_READ_ERROR; + block->is_cd = buffer[0]&0x80? true : false; + + FLAC__ASSERT(FLAC__STREAM_METADATA_CUESHEET_NUM_TRACKS_LEN % 8 == 0); + len = FLAC__STREAM_METADATA_CUESHEET_NUM_TRACKS_LEN / 8; + if(read_cb(buffer, 1, len, handle) != len) + return FLAC__METADATA_SIMPLE_ITERATOR_STATUS_READ_ERROR; + block->num_tracks = unpack_uint32_(buffer, len); + + if(block->num_tracks == 0) { + block->tracks = 0; + } + else if(0 == (block->tracks = calloc(block->num_tracks, sizeof(FLAC__StreamMetadata_CueSheet_Track)))) + return FLAC__METADATA_SIMPLE_ITERATOR_STATUS_MEMORY_ALLOCATION_ERROR; + + for(i = 0; i < block->num_tracks; i++) { + if(FLAC__METADATA_SIMPLE_ITERATOR_STATUS_OK != (status = read_metadata_block_data_cuesheet_track_cb_(handle, read_cb, block->tracks + i))) + return status; + } + + return FLAC__METADATA_SIMPLE_ITERATOR_STATUS_OK; +} + +static FLAC__Metadata_SimpleIteratorStatus read_metadata_block_data_picture_cstring_cb_(FLAC__IOHandle handle, FLAC__IOCallback_Read read_cb, FLAC__byte **data, FLAC__uint32 *length, FLAC__uint32 length_len) +{ + FLAC__byte buffer[sizeof(FLAC__uint32)]; + + FLAC__ASSERT(0 != data); + FLAC__ASSERT(length_len%8 == 0); + + length_len /= 8; /* convert to bytes */ + + FLAC__ASSERT(sizeof(buffer) >= length_len); + + if(read_cb(buffer, 1, length_len, handle) != length_len) + return FLAC__METADATA_SIMPLE_ITERATOR_STATUS_READ_ERROR; + *length = unpack_uint32_(buffer, length_len); + + if(0 != *data) + free(*data); + + if(0 == (*data = safe_malloc_add_2op_(*length, /*+*/1))) + return FLAC__METADATA_SIMPLE_ITERATOR_STATUS_MEMORY_ALLOCATION_ERROR; + + if(*length > 0) { + if(read_cb(*data, 1, *length, handle) != *length) + return FLAC__METADATA_SIMPLE_ITERATOR_STATUS_READ_ERROR; + } + + (*data)[*length] = '\0'; + + return FLAC__METADATA_SIMPLE_ITERATOR_STATUS_OK; +} + +FLAC__Metadata_SimpleIteratorStatus read_metadata_block_data_picture_cb_(FLAC__IOHandle handle, FLAC__IOCallback_Read read_cb, FLAC__StreamMetadata_Picture *block) +{ + FLAC__Metadata_SimpleIteratorStatus status; + FLAC__byte buffer[4]; /* asserted below that this is big enough */ + FLAC__uint32 len; + + FLAC__ASSERT(sizeof(buffer) >= FLAC__STREAM_METADATA_PICTURE_TYPE_LEN/8); + FLAC__ASSERT(sizeof(buffer) >= FLAC__STREAM_METADATA_PICTURE_WIDTH_LEN/8); + FLAC__ASSERT(sizeof(buffer) >= FLAC__STREAM_METADATA_PICTURE_HEIGHT_LEN/8); + FLAC__ASSERT(sizeof(buffer) >= FLAC__STREAM_METADATA_PICTURE_DEPTH_LEN/8); + FLAC__ASSERT(sizeof(buffer) >= FLAC__STREAM_METADATA_PICTURE_COLORS_LEN/8); + + FLAC__ASSERT(FLAC__STREAM_METADATA_PICTURE_TYPE_LEN % 8 == 0); + len = FLAC__STREAM_METADATA_PICTURE_TYPE_LEN / 8; + if(read_cb(buffer, 1, len, handle) != len) + return FLAC__METADATA_SIMPLE_ITERATOR_STATUS_READ_ERROR; + block->type = (FLAC__StreamMetadata_Picture_Type)unpack_uint32_(buffer, len); + + if((status = read_metadata_block_data_picture_cstring_cb_(handle, read_cb, (FLAC__byte**)(&(block->mime_type)), &len, FLAC__STREAM_METADATA_PICTURE_MIME_TYPE_LENGTH_LEN)) != FLAC__METADATA_SIMPLE_ITERATOR_STATUS_OK) + return status; + + if((status = read_metadata_block_data_picture_cstring_cb_(handle, read_cb, &(block->description), &len, FLAC__STREAM_METADATA_PICTURE_DESCRIPTION_LENGTH_LEN)) != FLAC__METADATA_SIMPLE_ITERATOR_STATUS_OK) + return status; + + FLAC__ASSERT(FLAC__STREAM_METADATA_PICTURE_WIDTH_LEN % 8 == 0); + len = FLAC__STREAM_METADATA_PICTURE_WIDTH_LEN / 8; + if(read_cb(buffer, 1, len, handle) != len) + return FLAC__METADATA_SIMPLE_ITERATOR_STATUS_READ_ERROR; + block->width = unpack_uint32_(buffer, len); + + FLAC__ASSERT(FLAC__STREAM_METADATA_PICTURE_HEIGHT_LEN % 8 == 0); + len = FLAC__STREAM_METADATA_PICTURE_HEIGHT_LEN / 8; + if(read_cb(buffer, 1, len, handle) != len) + return FLAC__METADATA_SIMPLE_ITERATOR_STATUS_READ_ERROR; + block->height = unpack_uint32_(buffer, len); + + FLAC__ASSERT(FLAC__STREAM_METADATA_PICTURE_DEPTH_LEN % 8 == 0); + len = FLAC__STREAM_METADATA_PICTURE_DEPTH_LEN / 8; + if(read_cb(buffer, 1, len, handle) != len) + return FLAC__METADATA_SIMPLE_ITERATOR_STATUS_READ_ERROR; + block->depth = unpack_uint32_(buffer, len); + + FLAC__ASSERT(FLAC__STREAM_METADATA_PICTURE_COLORS_LEN % 8 == 0); + len = FLAC__STREAM_METADATA_PICTURE_COLORS_LEN / 8; + if(read_cb(buffer, 1, len, handle) != len) + return FLAC__METADATA_SIMPLE_ITERATOR_STATUS_READ_ERROR; + block->colors = unpack_uint32_(buffer, len); + + /* for convenience we use read_metadata_block_data_picture_cstring_cb_() even though it adds an extra terminating NUL we don't use */ + if((status = read_metadata_block_data_picture_cstring_cb_(handle, read_cb, &(block->data), &(block->data_length), FLAC__STREAM_METADATA_PICTURE_DATA_LENGTH_LEN)) != FLAC__METADATA_SIMPLE_ITERATOR_STATUS_OK) + return status; + + return FLAC__METADATA_SIMPLE_ITERATOR_STATUS_OK; +} + +FLAC__Metadata_SimpleIteratorStatus read_metadata_block_data_unknown_cb_(FLAC__IOHandle handle, FLAC__IOCallback_Read read_cb, FLAC__StreamMetadata_Unknown *block, unsigned block_length) +{ + if(block_length == 0) { + block->data = 0; + } + else { + if(0 == (block->data = malloc(block_length))) + return FLAC__METADATA_SIMPLE_ITERATOR_STATUS_MEMORY_ALLOCATION_ERROR; + + if(read_cb(block->data, 1, block_length, handle) != block_length) + return FLAC__METADATA_SIMPLE_ITERATOR_STATUS_READ_ERROR; + } + + return FLAC__METADATA_SIMPLE_ITERATOR_STATUS_OK; +} + +FLAC__bool write_metadata_block_header_(FILE *file, FLAC__Metadata_SimpleIteratorStatus *status, const FLAC__StreamMetadata *block) +{ + FLAC__ASSERT(0 != file); + FLAC__ASSERT(0 != status); + + if(!write_metadata_block_header_cb_((FLAC__IOHandle)file, (FLAC__IOCallback_Write)fwrite, block)) { + *status = FLAC__METADATA_SIMPLE_ITERATOR_STATUS_WRITE_ERROR; + return false; + } + + return true; +} + +FLAC__bool write_metadata_block_data_(FILE *file, FLAC__Metadata_SimpleIteratorStatus *status, const FLAC__StreamMetadata *block) +{ + FLAC__ASSERT(0 != file); + FLAC__ASSERT(0 != status); + + if (write_metadata_block_data_cb_((FLAC__IOHandle)file, (FLAC__IOCallback_Write)fwrite, block)) { + *status = FLAC__METADATA_SIMPLE_ITERATOR_STATUS_OK; + return true; + } + else { + *status = FLAC__METADATA_SIMPLE_ITERATOR_STATUS_WRITE_ERROR; + return false; + } +} + +FLAC__bool write_metadata_block_header_cb_(FLAC__IOHandle handle, FLAC__IOCallback_Write write_cb, const FLAC__StreamMetadata *block) +{ + FLAC__byte buffer[FLAC__STREAM_METADATA_HEADER_LENGTH]; + + FLAC__ASSERT(block->length < (1u << FLAC__STREAM_METADATA_LENGTH_LEN)); + /* double protection */ + if(block->length >= (1u << FLAC__STREAM_METADATA_LENGTH_LEN)) + return false; + + buffer[0] = (block->is_last? 0x80 : 0) | (FLAC__byte)block->type; + pack_uint32_(block->length, buffer + 1, 3); + + if(write_cb(buffer, 1, FLAC__STREAM_METADATA_HEADER_LENGTH, handle) != FLAC__STREAM_METADATA_HEADER_LENGTH) + return false; + + return true; +} + +FLAC__bool write_metadata_block_data_cb_(FLAC__IOHandle handle, FLAC__IOCallback_Write write_cb, const FLAC__StreamMetadata *block) +{ + FLAC__ASSERT(0 != block); + + switch(block->type) { + case FLAC__METADATA_TYPE_STREAMINFO: + return write_metadata_block_data_streaminfo_cb_(handle, write_cb, &block->data.stream_info); + case FLAC__METADATA_TYPE_PADDING: + return write_metadata_block_data_padding_cb_(handle, write_cb, &block->data.padding, block->length); + case FLAC__METADATA_TYPE_APPLICATION: + return write_metadata_block_data_application_cb_(handle, write_cb, &block->data.application, block->length); + case FLAC__METADATA_TYPE_SEEKTABLE: + return write_metadata_block_data_seektable_cb_(handle, write_cb, &block->data.seek_table); + case FLAC__METADATA_TYPE_VORBIS_COMMENT: + return write_metadata_block_data_vorbis_comment_cb_(handle, write_cb, &block->data.vorbis_comment); + case FLAC__METADATA_TYPE_CUESHEET: + return write_metadata_block_data_cuesheet_cb_(handle, write_cb, &block->data.cue_sheet); + case FLAC__METADATA_TYPE_PICTURE: + return write_metadata_block_data_picture_cb_(handle, write_cb, &block->data.picture); + default: + return write_metadata_block_data_unknown_cb_(handle, write_cb, &block->data.unknown, block->length); + } +} + +FLAC__bool write_metadata_block_data_streaminfo_cb_(FLAC__IOHandle handle, FLAC__IOCallback_Write write_cb, const FLAC__StreamMetadata_StreamInfo *block) +{ + FLAC__byte buffer[FLAC__STREAM_METADATA_STREAMINFO_LENGTH]; + const unsigned channels1 = block->channels - 1; + const unsigned bps1 = block->bits_per_sample - 1; + + /* we are using hardcoded numbers for simplicity but we should + * probably eventually write a bit-level packer and use the + * _STREAMINFO_ constants. + */ + pack_uint32_(block->min_blocksize, buffer, 2); + pack_uint32_(block->max_blocksize, buffer+2, 2); + pack_uint32_(block->min_framesize, buffer+4, 3); + pack_uint32_(block->max_framesize, buffer+7, 3); + buffer[10] = (block->sample_rate >> 12) & 0xff; + buffer[11] = (block->sample_rate >> 4) & 0xff; + buffer[12] = ((block->sample_rate & 0x0f) << 4) | (channels1 << 1) | (bps1 >> 4); + buffer[13] = (FLAC__byte)(((bps1 & 0x0f) << 4) | ((block->total_samples >> 32) & 0x0f)); + pack_uint32_((FLAC__uint32)block->total_samples, buffer+14, 4); + memcpy(buffer+18, block->md5sum, 16); + + if(write_cb(buffer, 1, FLAC__STREAM_METADATA_STREAMINFO_LENGTH, handle) != FLAC__STREAM_METADATA_STREAMINFO_LENGTH) + return false; + + return true; +} + +FLAC__bool write_metadata_block_data_padding_cb_(FLAC__IOHandle handle, FLAC__IOCallback_Write write_cb, const FLAC__StreamMetadata_Padding *block, unsigned block_length) +{ + unsigned i, n = block_length; + FLAC__byte buffer[1024]; + + (void)block; + + memset(buffer, 0, 1024); + + for(i = 0; i < n/1024; i++) + if(write_cb(buffer, 1, 1024, handle) != 1024) + return false; + + n %= 1024; + + if(write_cb(buffer, 1, n, handle) != n) + return false; + + return true; +} + +FLAC__bool write_metadata_block_data_application_cb_(FLAC__IOHandle handle, FLAC__IOCallback_Write write_cb, const FLAC__StreamMetadata_Application *block, unsigned block_length) +{ + const unsigned id_bytes = FLAC__STREAM_METADATA_APPLICATION_ID_LEN / 8; + + if(write_cb(block->id, 1, id_bytes, handle) != id_bytes) + return false; + + block_length -= id_bytes; + + if(write_cb(block->data, 1, block_length, handle) != block_length) + return false; + + return true; +} + +FLAC__bool write_metadata_block_data_seektable_cb_(FLAC__IOHandle handle, FLAC__IOCallback_Write write_cb, const FLAC__StreamMetadata_SeekTable *block) +{ + unsigned i; + FLAC__byte buffer[FLAC__STREAM_METADATA_SEEKPOINT_LENGTH]; + + for(i = 0; i < block->num_points; i++) { + /* some MAGIC NUMBERs here */ + pack_uint64_(block->points[i].sample_number, buffer, 8); + pack_uint64_(block->points[i].stream_offset, buffer+8, 8); + pack_uint32_(block->points[i].frame_samples, buffer+16, 2); + if(write_cb(buffer, 1, FLAC__STREAM_METADATA_SEEKPOINT_LENGTH, handle) != FLAC__STREAM_METADATA_SEEKPOINT_LENGTH) + return false; + } + + return true; +} + +FLAC__bool write_metadata_block_data_vorbis_comment_cb_(FLAC__IOHandle handle, FLAC__IOCallback_Write write_cb, const FLAC__StreamMetadata_VorbisComment *block) +{ + unsigned i; + const unsigned entry_length_len = FLAC__STREAM_METADATA_VORBIS_COMMENT_ENTRY_LENGTH_LEN / 8; + const unsigned num_comments_len = FLAC__STREAM_METADATA_VORBIS_COMMENT_NUM_COMMENTS_LEN / 8; + FLAC__byte buffer[4]; /* magic number is asserted below */ + + FLAC__ASSERT(flac_max(FLAC__STREAM_METADATA_VORBIS_COMMENT_ENTRY_LENGTH_LEN, FLAC__STREAM_METADATA_VORBIS_COMMENT_NUM_COMMENTS_LEN) / 8 == sizeof(buffer)); + + pack_uint32_little_endian_(block->vendor_string.length, buffer, entry_length_len); + if(write_cb(buffer, 1, entry_length_len, handle) != entry_length_len) + return false; + if(write_cb(block->vendor_string.entry, 1, block->vendor_string.length, handle) != block->vendor_string.length) + return false; + + pack_uint32_little_endian_(block->num_comments, buffer, num_comments_len); + if(write_cb(buffer, 1, num_comments_len, handle) != num_comments_len) + return false; + + for(i = 0; i < block->num_comments; i++) { + pack_uint32_little_endian_(block->comments[i].length, buffer, entry_length_len); + if(write_cb(buffer, 1, entry_length_len, handle) != entry_length_len) + return false; + if(write_cb(block->comments[i].entry, 1, block->comments[i].length, handle) != block->comments[i].length) + return false; + } + + return true; +} + +FLAC__bool write_metadata_block_data_cuesheet_cb_(FLAC__IOHandle handle, FLAC__IOCallback_Write write_cb, const FLAC__StreamMetadata_CueSheet *block) +{ + unsigned i, j, len; + FLAC__byte buffer[1024]; /* asserted below that this is big enough */ + + FLAC__ASSERT(sizeof(buffer) >= sizeof(FLAC__uint64)); + FLAC__ASSERT(sizeof(buffer) >= FLAC__STREAM_METADATA_CUESHEET_RESERVED_LEN/8); + FLAC__ASSERT(sizeof(buffer) >= (FLAC__STREAM_METADATA_CUESHEET_TRACK_TYPE_LEN + FLAC__STREAM_METADATA_CUESHEET_TRACK_PRE_EMPHASIS_LEN + FLAC__STREAM_METADATA_CUESHEET_IS_CD_LEN + FLAC__STREAM_METADATA_CUESHEET_TRACK_RESERVED_LEN)/8); + FLAC__ASSERT(sizeof(buffer) >= FLAC__STREAM_METADATA_CUESHEET_INDEX_RESERVED_LEN/8); + + FLAC__ASSERT(FLAC__STREAM_METADATA_CUESHEET_MEDIA_CATALOG_NUMBER_LEN % 8 == 0); + len = FLAC__STREAM_METADATA_CUESHEET_MEDIA_CATALOG_NUMBER_LEN / 8; + if(write_cb(block->media_catalog_number, 1, len, handle) != len) + return false; + + FLAC__ASSERT(FLAC__STREAM_METADATA_CUESHEET_LEAD_IN_LEN % 8 == 0); + len = FLAC__STREAM_METADATA_CUESHEET_LEAD_IN_LEN / 8; + pack_uint64_(block->lead_in, buffer, len); + if(write_cb(buffer, 1, len, handle) != len) + return false; + + FLAC__ASSERT((FLAC__STREAM_METADATA_CUESHEET_IS_CD_LEN + FLAC__STREAM_METADATA_CUESHEET_RESERVED_LEN) % 8 == 0); + len = (FLAC__STREAM_METADATA_CUESHEET_IS_CD_LEN + FLAC__STREAM_METADATA_CUESHEET_RESERVED_LEN) / 8; + memset(buffer, 0, len); + if(block->is_cd) + buffer[0] |= 0x80; + if(write_cb(buffer, 1, len, handle) != len) + return false; + + FLAC__ASSERT(FLAC__STREAM_METADATA_CUESHEET_NUM_TRACKS_LEN % 8 == 0); + len = FLAC__STREAM_METADATA_CUESHEET_NUM_TRACKS_LEN / 8; + pack_uint32_(block->num_tracks, buffer, len); + if(write_cb(buffer, 1, len, handle) != len) + return false; + + for(i = 0; i < block->num_tracks; i++) { + FLAC__StreamMetadata_CueSheet_Track *track = block->tracks + i; + + FLAC__ASSERT(FLAC__STREAM_METADATA_CUESHEET_TRACK_OFFSET_LEN % 8 == 0); + len = FLAC__STREAM_METADATA_CUESHEET_TRACK_OFFSET_LEN / 8; + pack_uint64_(track->offset, buffer, len); + if(write_cb(buffer, 1, len, handle) != len) + return false; + + FLAC__ASSERT(FLAC__STREAM_METADATA_CUESHEET_TRACK_NUMBER_LEN % 8 == 0); + len = FLAC__STREAM_METADATA_CUESHEET_TRACK_NUMBER_LEN / 8; + pack_uint32_(track->number, buffer, len); + if(write_cb(buffer, 1, len, handle) != len) + return false; + + FLAC__ASSERT(FLAC__STREAM_METADATA_CUESHEET_TRACK_ISRC_LEN % 8 == 0); + len = FLAC__STREAM_METADATA_CUESHEET_TRACK_ISRC_LEN / 8; + if(write_cb(track->isrc, 1, len, handle) != len) + return false; + + FLAC__ASSERT((FLAC__STREAM_METADATA_CUESHEET_TRACK_TYPE_LEN + FLAC__STREAM_METADATA_CUESHEET_TRACK_PRE_EMPHASIS_LEN + FLAC__STREAM_METADATA_CUESHEET_TRACK_RESERVED_LEN) % 8 == 0); + len = (FLAC__STREAM_METADATA_CUESHEET_TRACK_TYPE_LEN + FLAC__STREAM_METADATA_CUESHEET_TRACK_PRE_EMPHASIS_LEN + FLAC__STREAM_METADATA_CUESHEET_TRACK_RESERVED_LEN) / 8; + memset(buffer, 0, len); + buffer[0] = (track->type << 7) | (track->pre_emphasis << 6); + if(write_cb(buffer, 1, len, handle) != len) + return false; + + FLAC__ASSERT(FLAC__STREAM_METADATA_CUESHEET_TRACK_NUM_INDICES_LEN % 8 == 0); + len = FLAC__STREAM_METADATA_CUESHEET_TRACK_NUM_INDICES_LEN / 8; + pack_uint32_(track->num_indices, buffer, len); + if(write_cb(buffer, 1, len, handle) != len) + return false; + + for(j = 0; j < track->num_indices; j++) { + FLAC__StreamMetadata_CueSheet_Index *indx = track->indices + j; + + FLAC__ASSERT(FLAC__STREAM_METADATA_CUESHEET_INDEX_OFFSET_LEN % 8 == 0); + len = FLAC__STREAM_METADATA_CUESHEET_INDEX_OFFSET_LEN / 8; + pack_uint64_(indx->offset, buffer, len); + if(write_cb(buffer, 1, len, handle) != len) + return false; + + FLAC__ASSERT(FLAC__STREAM_METADATA_CUESHEET_INDEX_NUMBER_LEN % 8 == 0); + len = FLAC__STREAM_METADATA_CUESHEET_INDEX_NUMBER_LEN / 8; + pack_uint32_(indx->number, buffer, len); + if(write_cb(buffer, 1, len, handle) != len) + return false; + + FLAC__ASSERT(FLAC__STREAM_METADATA_CUESHEET_INDEX_RESERVED_LEN % 8 == 0); + len = FLAC__STREAM_METADATA_CUESHEET_INDEX_RESERVED_LEN / 8; + memset(buffer, 0, len); + if(write_cb(buffer, 1, len, handle) != len) + return false; + } + } + + return true; +} + +FLAC__bool write_metadata_block_data_picture_cb_(FLAC__IOHandle handle, FLAC__IOCallback_Write write_cb, const FLAC__StreamMetadata_Picture *block) +{ + unsigned len; + size_t slen; + FLAC__byte buffer[4]; /* magic number is asserted below */ + + FLAC__ASSERT(0 == FLAC__STREAM_METADATA_PICTURE_TYPE_LEN%8); + FLAC__ASSERT(0 == FLAC__STREAM_METADATA_PICTURE_MIME_TYPE_LENGTH_LEN%8); + FLAC__ASSERT(0 == FLAC__STREAM_METADATA_PICTURE_DESCRIPTION_LENGTH_LEN%8); + FLAC__ASSERT(0 == FLAC__STREAM_METADATA_PICTURE_WIDTH_LEN%8); + FLAC__ASSERT(0 == FLAC__STREAM_METADATA_PICTURE_HEIGHT_LEN%8); + FLAC__ASSERT(0 == FLAC__STREAM_METADATA_PICTURE_DEPTH_LEN%8); + FLAC__ASSERT(0 == FLAC__STREAM_METADATA_PICTURE_COLORS_LEN%8); + FLAC__ASSERT(0 == FLAC__STREAM_METADATA_PICTURE_DATA_LENGTH_LEN%8); + FLAC__ASSERT(sizeof(buffer) >= FLAC__STREAM_METADATA_PICTURE_TYPE_LEN/8); + FLAC__ASSERT(sizeof(buffer) >= FLAC__STREAM_METADATA_PICTURE_MIME_TYPE_LENGTH_LEN/8); + FLAC__ASSERT(sizeof(buffer) >= FLAC__STREAM_METADATA_PICTURE_DESCRIPTION_LENGTH_LEN/8); + FLAC__ASSERT(sizeof(buffer) >= FLAC__STREAM_METADATA_PICTURE_WIDTH_LEN/8); + FLAC__ASSERT(sizeof(buffer) >= FLAC__STREAM_METADATA_PICTURE_HEIGHT_LEN/8); + FLAC__ASSERT(sizeof(buffer) >= FLAC__STREAM_METADATA_PICTURE_DEPTH_LEN/8); + FLAC__ASSERT(sizeof(buffer) >= FLAC__STREAM_METADATA_PICTURE_COLORS_LEN/8); + FLAC__ASSERT(sizeof(buffer) >= FLAC__STREAM_METADATA_PICTURE_DATA_LENGTH_LEN/8); + + len = FLAC__STREAM_METADATA_PICTURE_TYPE_LEN/8; + pack_uint32_(block->type, buffer, len); + if(write_cb(buffer, 1, len, handle) != len) + return false; + + len = FLAC__STREAM_METADATA_PICTURE_MIME_TYPE_LENGTH_LEN/8; + slen = strlen(block->mime_type); + pack_uint32_(slen, buffer, len); + if(write_cb(buffer, 1, len, handle) != len) + return false; + if(write_cb(block->mime_type, 1, slen, handle) != slen) + return false; + + len = FLAC__STREAM_METADATA_PICTURE_DESCRIPTION_LENGTH_LEN/8; + slen = strlen((const char *)block->description); + pack_uint32_(slen, buffer, len); + if(write_cb(buffer, 1, len, handle) != len) + return false; + if(write_cb(block->description, 1, slen, handle) != slen) + return false; + + len = FLAC__STREAM_METADATA_PICTURE_WIDTH_LEN/8; + pack_uint32_(block->width, buffer, len); + if(write_cb(buffer, 1, len, handle) != len) + return false; + + len = FLAC__STREAM_METADATA_PICTURE_HEIGHT_LEN/8; + pack_uint32_(block->height, buffer, len); + if(write_cb(buffer, 1, len, handle) != len) + return false; + + len = FLAC__STREAM_METADATA_PICTURE_DEPTH_LEN/8; + pack_uint32_(block->depth, buffer, len); + if(write_cb(buffer, 1, len, handle) != len) + return false; + + len = FLAC__STREAM_METADATA_PICTURE_COLORS_LEN/8; + pack_uint32_(block->colors, buffer, len); + if(write_cb(buffer, 1, len, handle) != len) + return false; + + len = FLAC__STREAM_METADATA_PICTURE_DATA_LENGTH_LEN/8; + pack_uint32_(block->data_length, buffer, len); + if(write_cb(buffer, 1, len, handle) != len) + return false; + if(write_cb(block->data, 1, block->data_length, handle) != block->data_length) + return false; + + return true; +} + +FLAC__bool write_metadata_block_data_unknown_cb_(FLAC__IOHandle handle, FLAC__IOCallback_Write write_cb, const FLAC__StreamMetadata_Unknown *block, unsigned block_length) +{ + if(write_cb(block->data, 1, block_length, handle) != block_length) + return false; + + return true; +} + +FLAC__bool write_metadata_block_stationary_(FLAC__Metadata_SimpleIterator *iterator, const FLAC__StreamMetadata *block) +{ + if(0 != fseeko(iterator->file, iterator->offset[iterator->depth], SEEK_SET)) { + iterator->status = FLAC__METADATA_SIMPLE_ITERATOR_STATUS_SEEK_ERROR; + return false; + } + + if(!write_metadata_block_header_(iterator->file, &iterator->status, block)) + return false; + + if(!write_metadata_block_data_(iterator->file, &iterator->status, block)) + return false; + + if(0 != fseeko(iterator->file, iterator->offset[iterator->depth], SEEK_SET)) { + iterator->status = FLAC__METADATA_SIMPLE_ITERATOR_STATUS_SEEK_ERROR; + return false; + } + + return read_metadata_block_header_(iterator); +} + +FLAC__bool write_metadata_block_stationary_with_padding_(FLAC__Metadata_SimpleIterator *iterator, FLAC__StreamMetadata *block, unsigned padding_length, FLAC__bool padding_is_last) +{ + FLAC__StreamMetadata *padding; + + if(0 != fseeko(iterator->file, iterator->offset[iterator->depth], SEEK_SET)) { + iterator->status = FLAC__METADATA_SIMPLE_ITERATOR_STATUS_SEEK_ERROR; + return false; + } + + block->is_last = false; + + if(!write_metadata_block_header_(iterator->file, &iterator->status, block)) + return false; + + if(!write_metadata_block_data_(iterator->file, &iterator->status, block)) + return false; + + if(0 == (padding = FLAC__metadata_object_new(FLAC__METADATA_TYPE_PADDING))) + return FLAC__METADATA_SIMPLE_ITERATOR_STATUS_MEMORY_ALLOCATION_ERROR; + + padding->is_last = padding_is_last; + padding->length = padding_length; + + if(!write_metadata_block_header_(iterator->file, &iterator->status, padding)) { + FLAC__metadata_object_delete(padding); + return false; + } + + if(!write_metadata_block_data_(iterator->file, &iterator->status, padding)) { + FLAC__metadata_object_delete(padding); + return false; + } + + FLAC__metadata_object_delete(padding); + + if(0 != fseeko(iterator->file, iterator->offset[iterator->depth], SEEK_SET)) { + iterator->status = FLAC__METADATA_SIMPLE_ITERATOR_STATUS_SEEK_ERROR; + return false; + } + + return read_metadata_block_header_(iterator); +} + +FLAC__bool rewrite_whole_file_(FLAC__Metadata_SimpleIterator *iterator, FLAC__StreamMetadata *block, FLAC__bool append) +{ + FILE *tempfile = NULL; + char *tempfilename = NULL; + int fixup_is_last_code = 0; /* 0 => no need to change any is_last flags */ + FLAC__off_t fixup_is_last_flag_offset = -1; + + FLAC__ASSERT(0 != block || append == false); + + if(iterator->is_last) { + if(append) { + fixup_is_last_code = 1; /* 1 => clear the is_last flag at the following offset */ + fixup_is_last_flag_offset = iterator->offset[iterator->depth]; + } + else if(0 == block) { + simple_iterator_push_(iterator); + if(!FLAC__metadata_simple_iterator_prev(iterator)) { + (void)simple_iterator_pop_(iterator); + return false; + } + fixup_is_last_code = -1; /* -1 => set the is_last the flag at the following offset */ + fixup_is_last_flag_offset = iterator->offset[iterator->depth]; + if(!simple_iterator_pop_(iterator)) + return false; + } + } + + if(!simple_iterator_copy_file_prefix_(iterator, &tempfile, &tempfilename, append)) + return false; + + if(0 != block) { + if(!write_metadata_block_header_(tempfile, &iterator->status, block)) { + cleanup_tempfile_(&tempfile, &tempfilename); + return false; + } + + if(!write_metadata_block_data_(tempfile, &iterator->status, block)) { + cleanup_tempfile_(&tempfile, &tempfilename); + return false; + } + } + + if(!simple_iterator_copy_file_postfix_(iterator, &tempfile, &tempfilename, fixup_is_last_code, fixup_is_last_flag_offset, block==0)) + return false; + + if(append) + return FLAC__metadata_simple_iterator_next(iterator); + + return true; +} + +void simple_iterator_push_(FLAC__Metadata_SimpleIterator *iterator) +{ + FLAC__ASSERT(iterator->depth+1 < SIMPLE_ITERATOR_MAX_PUSH_DEPTH); + iterator->offset[iterator->depth+1] = iterator->offset[iterator->depth]; + iterator->depth++; +} + +FLAC__bool simple_iterator_pop_(FLAC__Metadata_SimpleIterator *iterator) +{ + FLAC__ASSERT(iterator->depth > 0); + iterator->depth--; + if(0 != fseeko(iterator->file, iterator->offset[iterator->depth], SEEK_SET)) { + iterator->status = FLAC__METADATA_SIMPLE_ITERATOR_STATUS_SEEK_ERROR; + return false; + } + + return read_metadata_block_header_(iterator); +} + +/* return meanings: + * 0: ok + * 1: read error + * 2: seek error + * 3: not a FLAC file + */ +unsigned seek_to_first_metadata_block_cb_(FLAC__IOHandle handle, FLAC__IOCallback_Read read_cb, FLAC__IOCallback_Seek seek_cb) +{ + FLAC__byte buffer[4]; + size_t n; + unsigned i; + + FLAC__ASSERT(FLAC__STREAM_SYNC_LENGTH == sizeof(buffer)); + + /* skip any id3v2 tag */ + errno = 0; + n = read_cb(buffer, 1, 4, handle); + if(errno) + return 1; + else if(n != 4) + return 3; + else if(0 == memcmp(buffer, "ID3", 3)) { + unsigned tag_length = 0; + + /* skip to the tag length */ + if(seek_cb(handle, 2, SEEK_CUR) < 0) + return 2; + + /* read the length */ + for(i = 0; i < 4; i++) { + if(read_cb(buffer, 1, 1, handle) < 1 || buffer[0] & 0x80) + return 1; + tag_length <<= 7; + tag_length |= (buffer[0] & 0x7f); + } + + /* skip the rest of the tag */ + if(seek_cb(handle, tag_length, SEEK_CUR) < 0) + return 2; + + /* read the stream sync code */ + errno = 0; + n = read_cb(buffer, 1, 4, handle); + if(errno) + return 1; + else if(n != 4) + return 3; + } + + /* check for the fLaC signature */ + if(0 == memcmp(FLAC__STREAM_SYNC_STRING, buffer, FLAC__STREAM_SYNC_LENGTH)) + return 0; + else + return 3; +} + +unsigned seek_to_first_metadata_block_(FILE *f) +{ + return seek_to_first_metadata_block_cb_((FLAC__IOHandle)f, (FLAC__IOCallback_Read)fread, fseek_wrapper_); +} + +FLAC__bool simple_iterator_copy_file_prefix_(FLAC__Metadata_SimpleIterator *iterator, FILE **tempfile, char **tempfilename, FLAC__bool append) +{ + const FLAC__off_t offset_end = append? iterator->offset[iterator->depth] + (FLAC__off_t)FLAC__STREAM_METADATA_HEADER_LENGTH + (FLAC__off_t)iterator->length : iterator->offset[iterator->depth]; + + if(0 != fseeko(iterator->file, 0, SEEK_SET)) { + iterator->status = FLAC__METADATA_SIMPLE_ITERATOR_STATUS_SEEK_ERROR; + return false; + } + if(!open_tempfile_(iterator->filename, iterator->tempfile_path_prefix, tempfile, tempfilename, &iterator->status)) { + cleanup_tempfile_(tempfile, tempfilename); + return false; + } + if(!copy_n_bytes_from_file_(iterator->file, *tempfile, offset_end, &iterator->status)) { + cleanup_tempfile_(tempfile, tempfilename); + return false; + } + + return true; +} + +FLAC__bool simple_iterator_copy_file_postfix_(FLAC__Metadata_SimpleIterator *iterator, FILE **tempfile, char **tempfilename, int fixup_is_last_code, FLAC__off_t fixup_is_last_flag_offset, FLAC__bool backup) +{ + FLAC__off_t save_offset = iterator->offset[iterator->depth]; + FLAC__ASSERT(0 != *tempfile); + + if(0 != fseeko(iterator->file, save_offset + (FLAC__off_t)FLAC__STREAM_METADATA_HEADER_LENGTH + (FLAC__off_t)iterator->length, SEEK_SET)) { + cleanup_tempfile_(tempfile, tempfilename); + iterator->status = FLAC__METADATA_SIMPLE_ITERATOR_STATUS_SEEK_ERROR; + return false; + } + if(!copy_remaining_bytes_from_file_(iterator->file, *tempfile, &iterator->status)) { + cleanup_tempfile_(tempfile, tempfilename); + return false; + } + + if(fixup_is_last_code != 0) { + /* + * if code == 1, it means a block was appended to the end so + * we have to clear the is_last flag of the previous block + * if code == -1, it means the last block was deleted so + * we have to set the is_last flag of the previous block + */ + /* MAGIC NUMBERs here; we know the is_last flag is the high bit of the byte at this location */ + FLAC__byte x; + if(0 != fseeko(*tempfile, fixup_is_last_flag_offset, SEEK_SET)) { + cleanup_tempfile_(tempfile, tempfilename); + iterator->status = FLAC__METADATA_SIMPLE_ITERATOR_STATUS_SEEK_ERROR; + return false; + } + if(fread(&x, 1, 1, *tempfile) != 1) { + cleanup_tempfile_(tempfile, tempfilename); + iterator->status = FLAC__METADATA_SIMPLE_ITERATOR_STATUS_READ_ERROR; + return false; + } + if(fixup_is_last_code > 0) { + FLAC__ASSERT(x & 0x80); + x &= 0x7f; + } + else { + FLAC__ASSERT(!(x & 0x80)); + x |= 0x80; + } + if(0 != fseeko(*tempfile, fixup_is_last_flag_offset, SEEK_SET)) { + cleanup_tempfile_(tempfile, tempfilename); + iterator->status = FLAC__METADATA_SIMPLE_ITERATOR_STATUS_SEEK_ERROR; + return false; + } + if(local__fwrite(&x, 1, 1, *tempfile) != 1) { + cleanup_tempfile_(tempfile, tempfilename); + iterator->status = FLAC__METADATA_SIMPLE_ITERATOR_STATUS_WRITE_ERROR; + return false; + } + } + + (void)fclose(iterator->file); + + if(!transport_tempfile_(iterator->filename, tempfile, tempfilename, &iterator->status)) + return false; + + if(iterator->has_stats) + set_file_stats_(iterator->filename, &iterator->stats); + + if(!simple_iterator_prime_input_(iterator, !iterator->is_writable)) + return false; + if(backup) { + while(iterator->offset[iterator->depth] + (FLAC__off_t)FLAC__STREAM_METADATA_HEADER_LENGTH + (FLAC__off_t)iterator->length < save_offset) + if(!FLAC__metadata_simple_iterator_next(iterator)) + return false; + return true; + } + else { + /* move the iterator to it's original block faster by faking a push, then doing a pop_ */ + FLAC__ASSERT(iterator->depth == 0); + iterator->offset[0] = save_offset; + iterator->depth++; + return simple_iterator_pop_(iterator); + } +} + +FLAC__bool copy_n_bytes_from_file_(FILE *file, FILE *tempfile, FLAC__off_t bytes, FLAC__Metadata_SimpleIteratorStatus *status) +{ + FLAC__byte buffer[8192]; + size_t n; + + FLAC__ASSERT(bytes >= 0); + while(bytes > 0) { + n = flac_min(sizeof(buffer), (size_t)bytes); + if(fread(buffer, 1, n, file) != n) { + *status = FLAC__METADATA_SIMPLE_ITERATOR_STATUS_READ_ERROR; + return false; + } + if(local__fwrite(buffer, 1, n, tempfile) != n) { + *status = FLAC__METADATA_SIMPLE_ITERATOR_STATUS_WRITE_ERROR; + return false; + } + bytes -= n; + } + + return true; +} + +FLAC__bool copy_n_bytes_from_file_cb_(FLAC__IOHandle handle, FLAC__IOCallback_Read read_cb, FLAC__IOHandle temp_handle, FLAC__IOCallback_Write temp_write_cb, FLAC__off_t bytes, FLAC__Metadata_SimpleIteratorStatus *status) +{ + FLAC__byte buffer[8192]; + size_t n; + + FLAC__ASSERT(bytes >= 0); + while(bytes > 0) { + n = flac_min(sizeof(buffer), (size_t)bytes); + if(read_cb(buffer, 1, n, handle) != n) { + *status = FLAC__METADATA_SIMPLE_ITERATOR_STATUS_READ_ERROR; + return false; + } + if(temp_write_cb(buffer, 1, n, temp_handle) != n) { + *status = FLAC__METADATA_SIMPLE_ITERATOR_STATUS_WRITE_ERROR; + return false; + } + bytes -= n; + } + + return true; +} + +FLAC__bool copy_remaining_bytes_from_file_(FILE *file, FILE *tempfile, FLAC__Metadata_SimpleIteratorStatus *status) +{ + FLAC__byte buffer[8192]; + size_t n; + + while(!feof(file)) { + n = fread(buffer, 1, sizeof(buffer), file); + if(n == 0 && !feof(file)) { + *status = FLAC__METADATA_SIMPLE_ITERATOR_STATUS_READ_ERROR; + return false; + } + if(n > 0 && local__fwrite(buffer, 1, n, tempfile) != n) { + *status = FLAC__METADATA_SIMPLE_ITERATOR_STATUS_WRITE_ERROR; + return false; + } + } + + return true; +} + +FLAC__bool copy_remaining_bytes_from_file_cb_(FLAC__IOHandle handle, FLAC__IOCallback_Read read_cb, FLAC__IOCallback_Eof eof_cb, FLAC__IOHandle temp_handle, FLAC__IOCallback_Write temp_write_cb, FLAC__Metadata_SimpleIteratorStatus *status) +{ + FLAC__byte buffer[8192]; + size_t n; + + while(!eof_cb(handle)) { + n = read_cb(buffer, 1, sizeof(buffer), handle); + if(n == 0 && !eof_cb(handle)) { + *status = FLAC__METADATA_SIMPLE_ITERATOR_STATUS_READ_ERROR; + return false; + } + if(n > 0 && temp_write_cb(buffer, 1, n, temp_handle) != n) { + *status = FLAC__METADATA_SIMPLE_ITERATOR_STATUS_WRITE_ERROR; + return false; + } + } + + return true; +} + +static int +local_snprintf(char *str, size_t size, const char *fmt, ...) +{ + va_list va; + int rc; + +#if defined _MSC_VER + if (size == 0) + return 1024; +#endif + + va_start (va, fmt); + +#if defined _MSC_VER + rc = vsnprintf_s (str, size, _TRUNCATE, fmt, va); + if (rc < 0) + rc = size - 1; +#elif defined __MINGW32__ + rc = __mingw_vsnprintf (str, size, fmt, va); +#else + rc = vsnprintf (str, size, fmt, va); +#endif + va_end (va); + + return rc; +} + +FLAC__bool open_tempfile_(const char *filename, const char *tempfile_path_prefix, FILE **tempfile, char **tempfilename, FLAC__Metadata_SimpleIteratorStatus *status) +{ + static const char *tempfile_suffix = ".metadata_edit"; + if(0 == tempfile_path_prefix) { + size_t dest_len = strlen(filename) + strlen(tempfile_suffix) + 1; + if(0 == (*tempfilename = safe_malloc_(dest_len))) { + *status = FLAC__METADATA_SIMPLE_ITERATOR_STATUS_MEMORY_ALLOCATION_ERROR; + return false; + } + local_snprintf(*tempfilename, dest_len, "%s%s", filename, tempfile_suffix); + } + else { + const char *p = strrchr(filename, '/'); + size_t dest_len; + if(0 == p) + p = filename; + else + p++; + + dest_len = strlen(tempfile_path_prefix) + strlen(p) + strlen(tempfile_suffix) + 2; + + if(0 == (*tempfilename = safe_malloc_(dest_len))) { + *status = FLAC__METADATA_SIMPLE_ITERATOR_STATUS_MEMORY_ALLOCATION_ERROR; + return false; + } + local_snprintf(*tempfilename, dest_len, "%s/%s%s", tempfile_path_prefix, p, tempfile_suffix); + } + + if(0 == (*tempfile = flac_fopen(*tempfilename, "w+b"))) { + *status = FLAC__METADATA_SIMPLE_ITERATOR_STATUS_ERROR_OPENING_FILE; + return false; + } + + return true; +} + +FLAC__bool transport_tempfile_(const char *filename, FILE **tempfile, char **tempfilename, FLAC__Metadata_SimpleIteratorStatus *status) +{ + FLAC__ASSERT(0 != filename); + FLAC__ASSERT(0 != tempfile); + FLAC__ASSERT(0 != *tempfile); + FLAC__ASSERT(0 != tempfilename); + FLAC__ASSERT(0 != *tempfilename); + FLAC__ASSERT(0 != status); + + (void)fclose(*tempfile); + *tempfile = 0; + +#if defined _MSC_VER || defined __BORLANDC__ || defined __MINGW32__ || defined __EMX__ + /* on some flavors of windows, flac_rename() will fail if the destination already exists */ + if(flac_unlink(filename) < 0) { + cleanup_tempfile_(tempfile, tempfilename); + *status = FLAC__METADATA_SIMPLE_ITERATOR_STATUS_UNLINK_ERROR; + return false; + } +#endif + + /*@@@ to fully support the tempfile_path_prefix we need to update this piece to actually copy across filesystems instead of just flac_rename(): */ + if(0 != flac_rename(*tempfilename, filename)) { + cleanup_tempfile_(tempfile, tempfilename); + *status = FLAC__METADATA_SIMPLE_ITERATOR_STATUS_RENAME_ERROR; + return false; + } + + cleanup_tempfile_(tempfile, tempfilename); + + return true; +} + +void cleanup_tempfile_(FILE **tempfile, char **tempfilename) +{ + if(0 != *tempfile) { + (void)fclose(*tempfile); + *tempfile = 0; + } + + if(0 != *tempfilename) { + (void)flac_unlink(*tempfilename); + free(*tempfilename); + *tempfilename = 0; + } +} + +FLAC__bool get_file_stats_(const char *filename, struct flac_stat_s *stats) +{ + FLAC__ASSERT(0 != filename); + FLAC__ASSERT(0 != stats); + return (0 == flac_stat(filename, stats)); +} + +void set_file_stats_(const char *filename, struct flac_stat_s *stats) +{ + struct utimbuf srctime; + + FLAC__ASSERT(0 != filename); + FLAC__ASSERT(0 != stats); + + srctime.actime = stats->st_atime; + srctime.modtime = stats->st_mtime; + (void)flac_chmod(filename, stats->st_mode); + (void)flac_utime(filename, &srctime); +#if !defined _MSC_VER && !defined __BORLANDC__ && !defined __MINGW32__ + FLAC_CHECK_RETURN(chown(filename, stats->st_uid, -1)); + FLAC_CHECK_RETURN(chown(filename, -1, stats->st_gid)); +#endif +} + +int fseek_wrapper_(FLAC__IOHandle handle, FLAC__int64 offset, int whence) +{ + return fseeko((FILE*)handle, (FLAC__off_t)offset, whence); +} + +FLAC__int64 ftell_wrapper_(FLAC__IOHandle handle) +{ + return ftello((FILE*)handle); +} + +FLAC__Metadata_ChainStatus get_equivalent_status_(FLAC__Metadata_SimpleIteratorStatus status) +{ + switch(status) { + case FLAC__METADATA_SIMPLE_ITERATOR_STATUS_OK: + return FLAC__METADATA_CHAIN_STATUS_OK; + case FLAC__METADATA_SIMPLE_ITERATOR_STATUS_ILLEGAL_INPUT: + return FLAC__METADATA_CHAIN_STATUS_ILLEGAL_INPUT; + case FLAC__METADATA_SIMPLE_ITERATOR_STATUS_ERROR_OPENING_FILE: + return FLAC__METADATA_CHAIN_STATUS_ERROR_OPENING_FILE; + case FLAC__METADATA_SIMPLE_ITERATOR_STATUS_NOT_A_FLAC_FILE: + return FLAC__METADATA_CHAIN_STATUS_NOT_A_FLAC_FILE; + case FLAC__METADATA_SIMPLE_ITERATOR_STATUS_NOT_WRITABLE: + return FLAC__METADATA_CHAIN_STATUS_NOT_WRITABLE; + case FLAC__METADATA_SIMPLE_ITERATOR_STATUS_BAD_METADATA: + return FLAC__METADATA_CHAIN_STATUS_BAD_METADATA; + case FLAC__METADATA_SIMPLE_ITERATOR_STATUS_READ_ERROR: + return FLAC__METADATA_CHAIN_STATUS_READ_ERROR; + case FLAC__METADATA_SIMPLE_ITERATOR_STATUS_SEEK_ERROR: + return FLAC__METADATA_CHAIN_STATUS_SEEK_ERROR; + case FLAC__METADATA_SIMPLE_ITERATOR_STATUS_WRITE_ERROR: + return FLAC__METADATA_CHAIN_STATUS_WRITE_ERROR; + case FLAC__METADATA_SIMPLE_ITERATOR_STATUS_RENAME_ERROR: + return FLAC__METADATA_CHAIN_STATUS_RENAME_ERROR; + case FLAC__METADATA_SIMPLE_ITERATOR_STATUS_UNLINK_ERROR: + return FLAC__METADATA_CHAIN_STATUS_UNLINK_ERROR; + case FLAC__METADATA_SIMPLE_ITERATOR_STATUS_MEMORY_ALLOCATION_ERROR: + return FLAC__METADATA_CHAIN_STATUS_MEMORY_ALLOCATION_ERROR; + case FLAC__METADATA_SIMPLE_ITERATOR_STATUS_INTERNAL_ERROR: + default: + return FLAC__METADATA_CHAIN_STATUS_INTERNAL_ERROR; + } +} diff --git a/core/deps/flac/src/libFLAC/metadata_object.c b/core/deps/flac/src/libFLAC/metadata_object.c new file mode 100644 index 000000000..9cb95019d --- /dev/null +++ b/core/deps/flac/src/libFLAC/metadata_object.c @@ -0,0 +1,1821 @@ +/* libFLAC - Free Lossless Audio Codec library + * Copyright (C) 2001-2009 Josh Coalson + * Copyright (C) 2011-2016 Xiph.Org Foundation + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * - Neither the name of the Xiph.org Foundation nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifdef HAVE_CONFIG_H +# include +#endif + +#include +#include + +#include "private/metadata.h" +#include "private/memory.h" + +#include "FLAC/assert.h" +#include "share/alloc.h" +#include "share/compat.h" + +/* Alias the first (in share/alloc.h) to the second (in src/libFLAC/memory.c). */ +#define safe_malloc_mul_2op_ safe_malloc_mul_2op_p + + +/**************************************************************************** + * + * Local routines + * + ***************************************************************************/ + +/* copy bytes: + * from = NULL && bytes = 0 + * to <- NULL + * from != NULL && bytes > 0 + * to <- copy of from + * else ASSERT + * malloc error leaves 'to' unchanged + */ +static FLAC__bool copy_bytes_(FLAC__byte **to, const FLAC__byte *from, unsigned bytes) +{ + FLAC__ASSERT(to != NULL); + if (bytes > 0 && from != NULL) { + FLAC__byte *x; + if ((x = safe_malloc_(bytes)) == NULL) + return false; + memcpy(x, from, bytes); + *to = x; + } + else { + *to = 0; + } + return true; +} + +#if 0 /* UNUSED */ +/* like copy_bytes_(), but free()s the original '*to' if the copy succeeds and the original '*to' is non-NULL */ +static FLAC__bool free_copy_bytes_(FLAC__byte **to, const FLAC__byte *from, unsigned bytes) +{ + FLAC__byte *copy; + FLAC__ASSERT(to != NULL); + if (copy_bytes_(©, from, bytes)) { + free(*to); + *to = copy; + return true; + } + else + return false; +} +#endif + +/* reallocate entry to 1 byte larger and add a terminating NUL */ +/* realloc() failure leaves entry unchanged */ +static FLAC__bool ensure_null_terminated_(FLAC__byte **entry, unsigned length) +{ + FLAC__byte *x = safe_realloc_add_2op_(*entry, length, /*+*/1); + if (x != NULL) { + x[length] = '\0'; + *entry = x; + return true; + } + else + return false; +} + +/* copies the NUL-terminated C-string 'from' to '*to', leaving '*to' + * unchanged if malloc fails, free()ing the original '*to' if it + * succeeds and the original '*to' was not NULL + */ +static FLAC__bool copy_cstring_(char **to, const char *from) +{ + char *copy = strdup(from); + FLAC__ASSERT(to != NULL); + if (copy) { + free(*to); + *to = copy; + return true; + } + else + return false; +} + +static FLAC__bool copy_vcentry_(FLAC__StreamMetadata_VorbisComment_Entry *to, const FLAC__StreamMetadata_VorbisComment_Entry *from) +{ + to->length = from->length; + if (from->entry == 0) { + FLAC__ASSERT(from->length == 0); + to->entry = 0; + } + else { + FLAC__byte *x; + FLAC__ASSERT(from->length > 0); + if ((x = safe_malloc_add_2op_(from->length, /*+*/1)) == NULL) + return false; + memcpy(x, from->entry, from->length); + x[from->length] = '\0'; + to->entry = x; + } + return true; +} + +static FLAC__bool copy_track_(FLAC__StreamMetadata_CueSheet_Track *to, const FLAC__StreamMetadata_CueSheet_Track *from) +{ + memcpy(to, from, sizeof(FLAC__StreamMetadata_CueSheet_Track)); + if (from->indices == 0) { + FLAC__ASSERT(from->num_indices == 0); + } + else { + FLAC__StreamMetadata_CueSheet_Index *x; + FLAC__ASSERT(from->num_indices > 0); + if ((x = safe_malloc_mul_2op_p(from->num_indices, /*times*/sizeof(FLAC__StreamMetadata_CueSheet_Index))) == NULL) + return false; + memcpy(x, from->indices, from->num_indices * sizeof(FLAC__StreamMetadata_CueSheet_Index)); + to->indices = x; + } + return true; +} + +static void seektable_calculate_length_(FLAC__StreamMetadata *object) +{ + FLAC__ASSERT(object != NULL); + FLAC__ASSERT(object->type == FLAC__METADATA_TYPE_SEEKTABLE); + + object->length = object->data.seek_table.num_points * FLAC__STREAM_METADATA_SEEKPOINT_LENGTH; +} + +static FLAC__StreamMetadata_SeekPoint *seekpoint_array_new_(unsigned num_points) +{ + FLAC__StreamMetadata_SeekPoint *object_array; + + FLAC__ASSERT(num_points > 0); + + object_array = safe_malloc_mul_2op_p(num_points, /*times*/sizeof(FLAC__StreamMetadata_SeekPoint)); + + if (object_array != NULL) { + unsigned i; + for (i = 0; i < num_points; i++) { + object_array[i].sample_number = FLAC__STREAM_METADATA_SEEKPOINT_PLACEHOLDER; + object_array[i].stream_offset = 0; + object_array[i].frame_samples = 0; + } + } + + return object_array; +} + +static void vorbiscomment_calculate_length_(FLAC__StreamMetadata *object) +{ + unsigned i; + + FLAC__ASSERT(object->type == FLAC__METADATA_TYPE_VORBIS_COMMENT); + + object->length = (FLAC__STREAM_METADATA_VORBIS_COMMENT_ENTRY_LENGTH_LEN) / 8; + object->length += object->data.vorbis_comment.vendor_string.length; + object->length += (FLAC__STREAM_METADATA_VORBIS_COMMENT_NUM_COMMENTS_LEN) / 8; + for (i = 0; i < object->data.vorbis_comment.num_comments; i++) { + object->length += (FLAC__STREAM_METADATA_VORBIS_COMMENT_ENTRY_LENGTH_LEN / 8); + object->length += object->data.vorbis_comment.comments[i].length; + } +} + +static FLAC__StreamMetadata_VorbisComment_Entry *vorbiscomment_entry_array_new_(unsigned num_comments) +{ + FLAC__ASSERT(num_comments > 0); + + return safe_calloc_(num_comments, sizeof(FLAC__StreamMetadata_VorbisComment_Entry)); +} + +static void vorbiscomment_entry_array_delete_(FLAC__StreamMetadata_VorbisComment_Entry *object_array, unsigned num_comments) +{ + unsigned i; + + FLAC__ASSERT(object_array != NULL && num_comments > 0); + + for (i = 0; i < num_comments; i++) + free(object_array[i].entry); + + free(object_array); +} + +static FLAC__StreamMetadata_VorbisComment_Entry *vorbiscomment_entry_array_copy_(const FLAC__StreamMetadata_VorbisComment_Entry *object_array, unsigned num_comments) +{ + FLAC__StreamMetadata_VorbisComment_Entry *return_array; + + FLAC__ASSERT(object_array != NULL); + FLAC__ASSERT(num_comments > 0); + + return_array = vorbiscomment_entry_array_new_(num_comments); + + if (return_array != NULL) { + unsigned i; + + for (i = 0; i < num_comments; i++) { + if (!copy_vcentry_(return_array+i, object_array+i)) { + vorbiscomment_entry_array_delete_(return_array, num_comments); + return 0; + } + } + } + + return return_array; +} + +static FLAC__bool vorbiscomment_set_entry_(FLAC__StreamMetadata *object, FLAC__StreamMetadata_VorbisComment_Entry *dest, const FLAC__StreamMetadata_VorbisComment_Entry *src, FLAC__bool copy) +{ + FLAC__byte *save; + + FLAC__ASSERT(object != NULL); + FLAC__ASSERT(dest != NULL); + FLAC__ASSERT(src != NULL); + FLAC__ASSERT(object->type == FLAC__METADATA_TYPE_VORBIS_COMMENT); + FLAC__ASSERT((src->entry != NULL && src->length > 0) || (src->entry == NULL && src->length == 0)); + + save = dest->entry; + + if (src->entry != NULL) { + if (copy) { + /* do the copy first so that if we fail we leave the dest object untouched */ + if (!copy_vcentry_(dest, src)) + return false; + } + else { + /* we have to make sure that the string we're taking over is null-terminated */ + + /* + * Stripping the const from src->entry is OK since we're taking + * ownership of the pointer. This is a hack around a deficiency + * in the API where the same function is used for 'copy' and + * 'own', but the source entry is a const pointer. If we were + * precise, the 'own' flavor would be a separate function with a + * non-const source pointer. But it's not, so we hack away. + */ + if (!ensure_null_terminated_((FLAC__byte**)(&src->entry), src->length)) + return false; + *dest = *src; + } + } + else { + /* the src is null */ + *dest = *src; + } + + free(save); + + vorbiscomment_calculate_length_(object); + return true; +} + +static int vorbiscomment_find_entry_from_(const FLAC__StreamMetadata *object, unsigned offset, const char *field_name, unsigned field_name_length) +{ + unsigned i; + + FLAC__ASSERT(object != NULL); + FLAC__ASSERT(object->type == FLAC__METADATA_TYPE_VORBIS_COMMENT); + FLAC__ASSERT(field_name != NULL); + + for (i = offset; i < object->data.vorbis_comment.num_comments; i++) { + if (FLAC__metadata_object_vorbiscomment_entry_matches(object->data.vorbis_comment.comments[i], field_name, field_name_length)) + return (int)i; + } + + return -1; +} + +static void cuesheet_calculate_length_(FLAC__StreamMetadata *object) +{ + unsigned i; + + FLAC__ASSERT(object->type == FLAC__METADATA_TYPE_CUESHEET); + + object->length = ( + FLAC__STREAM_METADATA_CUESHEET_MEDIA_CATALOG_NUMBER_LEN + + FLAC__STREAM_METADATA_CUESHEET_LEAD_IN_LEN + + FLAC__STREAM_METADATA_CUESHEET_IS_CD_LEN + + FLAC__STREAM_METADATA_CUESHEET_RESERVED_LEN + + FLAC__STREAM_METADATA_CUESHEET_NUM_TRACKS_LEN + ) / 8; + + object->length += object->data.cue_sheet.num_tracks * ( + FLAC__STREAM_METADATA_CUESHEET_TRACK_OFFSET_LEN + + FLAC__STREAM_METADATA_CUESHEET_TRACK_NUMBER_LEN + + FLAC__STREAM_METADATA_CUESHEET_TRACK_ISRC_LEN + + FLAC__STREAM_METADATA_CUESHEET_TRACK_TYPE_LEN + + FLAC__STREAM_METADATA_CUESHEET_TRACK_PRE_EMPHASIS_LEN + + FLAC__STREAM_METADATA_CUESHEET_TRACK_RESERVED_LEN + + FLAC__STREAM_METADATA_CUESHEET_TRACK_NUM_INDICES_LEN + ) / 8; + + for (i = 0; i < object->data.cue_sheet.num_tracks; i++) { + object->length += object->data.cue_sheet.tracks[i].num_indices * ( + FLAC__STREAM_METADATA_CUESHEET_INDEX_OFFSET_LEN + + FLAC__STREAM_METADATA_CUESHEET_INDEX_NUMBER_LEN + + FLAC__STREAM_METADATA_CUESHEET_INDEX_RESERVED_LEN + ) / 8; + } +} + +static FLAC__StreamMetadata_CueSheet_Index *cuesheet_track_index_array_new_(unsigned num_indices) +{ + FLAC__ASSERT(num_indices > 0); + + return safe_calloc_(num_indices, sizeof(FLAC__StreamMetadata_CueSheet_Index)); +} + +static FLAC__StreamMetadata_CueSheet_Track *cuesheet_track_array_new_(unsigned num_tracks) +{ + FLAC__ASSERT(num_tracks > 0); + + return safe_calloc_(num_tracks, sizeof(FLAC__StreamMetadata_CueSheet_Track)); +} + +static void cuesheet_track_array_delete_(FLAC__StreamMetadata_CueSheet_Track *object_array, unsigned num_tracks) +{ + unsigned i; + + FLAC__ASSERT(object_array != NULL && num_tracks > 0); + + for (i = 0; i < num_tracks; i++) { + if (object_array[i].indices != 0) { + FLAC__ASSERT(object_array[i].num_indices > 0); + free(object_array[i].indices); + } + } + + free(object_array); +} + +static FLAC__StreamMetadata_CueSheet_Track *cuesheet_track_array_copy_(const FLAC__StreamMetadata_CueSheet_Track *object_array, unsigned num_tracks) +{ + FLAC__StreamMetadata_CueSheet_Track *return_array; + + FLAC__ASSERT(object_array != NULL); + FLAC__ASSERT(num_tracks > 0); + + return_array = cuesheet_track_array_new_(num_tracks); + + if (return_array != NULL) { + unsigned i; + + for (i = 0; i < num_tracks; i++) { + if (!copy_track_(return_array+i, object_array+i)) { + cuesheet_track_array_delete_(return_array, num_tracks); + return 0; + } + } + } + + return return_array; +} + +static FLAC__bool cuesheet_set_track_(FLAC__StreamMetadata *object, FLAC__StreamMetadata_CueSheet_Track *dest, const FLAC__StreamMetadata_CueSheet_Track *src, FLAC__bool copy) +{ + FLAC__StreamMetadata_CueSheet_Index *save; + + FLAC__ASSERT(object != NULL); + FLAC__ASSERT(dest != NULL); + FLAC__ASSERT(src != NULL); + FLAC__ASSERT(object->type == FLAC__METADATA_TYPE_CUESHEET); + FLAC__ASSERT((src->indices != NULL && src->num_indices > 0) || (src->indices == NULL && src->num_indices == 0)); + + save = dest->indices; + + /* do the copy first so that if we fail we leave the object untouched */ + if (copy) { + if (!copy_track_(dest, src)) + return false; + } + else { + *dest = *src; + } + + free(save); + + cuesheet_calculate_length_(object); + return true; +} + + +/**************************************************************************** + * + * Metadata object routines + * + ***************************************************************************/ + +FLAC_API FLAC__StreamMetadata *FLAC__metadata_object_new(FLAC__MetadataType type) +{ + FLAC__StreamMetadata *object; + + if (type > FLAC__MAX_METADATA_TYPE) + return 0; + + object = calloc(1, sizeof(FLAC__StreamMetadata)); + if (object != NULL) { + object->is_last = false; + object->type = type; + switch(type) { + case FLAC__METADATA_TYPE_STREAMINFO: + object->length = FLAC__STREAM_METADATA_STREAMINFO_LENGTH; + break; + case FLAC__METADATA_TYPE_PADDING: + /* calloc() took care of this for us: + object->length = 0; + */ + break; + case FLAC__METADATA_TYPE_APPLICATION: + object->length = FLAC__STREAM_METADATA_APPLICATION_ID_LEN / 8; + /* calloc() took care of this for us: + object->data.application.data = 0; + */ + break; + case FLAC__METADATA_TYPE_SEEKTABLE: + /* calloc() took care of this for us: + object->length = 0; + object->data.seek_table.num_points = 0; + object->data.seek_table.points = 0; + */ + break; + case FLAC__METADATA_TYPE_VORBIS_COMMENT: + object->data.vorbis_comment.vendor_string.length = (unsigned)strlen(FLAC__VENDOR_STRING); + if (!copy_bytes_(&object->data.vorbis_comment.vendor_string.entry, (const FLAC__byte*)FLAC__VENDOR_STRING, object->data.vorbis_comment.vendor_string.length+1)) { + free(object); + return 0; + } + vorbiscomment_calculate_length_(object); + break; + case FLAC__METADATA_TYPE_CUESHEET: + cuesheet_calculate_length_(object); + break; + case FLAC__METADATA_TYPE_PICTURE: + object->length = ( + FLAC__STREAM_METADATA_PICTURE_TYPE_LEN + + FLAC__STREAM_METADATA_PICTURE_MIME_TYPE_LENGTH_LEN + /* empty mime_type string */ + FLAC__STREAM_METADATA_PICTURE_DESCRIPTION_LENGTH_LEN + /* empty description string */ + FLAC__STREAM_METADATA_PICTURE_WIDTH_LEN + + FLAC__STREAM_METADATA_PICTURE_HEIGHT_LEN + + FLAC__STREAM_METADATA_PICTURE_DEPTH_LEN + + FLAC__STREAM_METADATA_PICTURE_COLORS_LEN + + FLAC__STREAM_METADATA_PICTURE_DATA_LENGTH_LEN + + 0 /* no data */ + ) / 8; + object->data.picture.type = FLAC__STREAM_METADATA_PICTURE_TYPE_OTHER; + object->data.picture.mime_type = 0; + object->data.picture.description = 0; + /* calloc() took care of this for us: + object->data.picture.width = 0; + object->data.picture.height = 0; + object->data.picture.depth = 0; + object->data.picture.colors = 0; + object->data.picture.data_length = 0; + object->data.picture.data = 0; + */ + /* now initialize mime_type and description with empty strings to make things easier on the client */ + if (!copy_cstring_(&object->data.picture.mime_type, "")) { + free(object); + return 0; + } + if (!copy_cstring_((char**)(&object->data.picture.description), "")) { + free(object->data.picture.mime_type); + free(object); + return 0; + } + break; + default: + /* calloc() took care of this for us: + object->length = 0; + object->data.unknown.data = 0; + */ + break; + } + } + + return object; +} + +FLAC_API FLAC__StreamMetadata *FLAC__metadata_object_clone(const FLAC__StreamMetadata *object) +{ + FLAC__StreamMetadata *to; + + FLAC__ASSERT(object != NULL); + + if ((to = FLAC__metadata_object_new(object->type)) != NULL) { + to->is_last = object->is_last; + to->type = object->type; + to->length = object->length; + switch(to->type) { + case FLAC__METADATA_TYPE_STREAMINFO: + memcpy(&to->data.stream_info, &object->data.stream_info, sizeof(FLAC__StreamMetadata_StreamInfo)); + break; + case FLAC__METADATA_TYPE_PADDING: + break; + case FLAC__METADATA_TYPE_APPLICATION: + if (to->length < FLAC__STREAM_METADATA_APPLICATION_ID_LEN / 8) { /* underflow check */ + FLAC__metadata_object_delete(to); + return 0; + } + memcpy(&to->data.application.id, &object->data.application.id, FLAC__STREAM_METADATA_APPLICATION_ID_LEN / 8); + if (!copy_bytes_(&to->data.application.data, object->data.application.data, object->length - FLAC__STREAM_METADATA_APPLICATION_ID_LEN / 8)) { + FLAC__metadata_object_delete(to); + return 0; + } + break; + case FLAC__METADATA_TYPE_SEEKTABLE: + to->data.seek_table.num_points = object->data.seek_table.num_points; + if (to->data.seek_table.num_points > UINT32_MAX / sizeof(FLAC__StreamMetadata_SeekPoint)) { /* overflow check */ + FLAC__metadata_object_delete(to); + return 0; + } + if (!copy_bytes_((FLAC__byte**)&to->data.seek_table.points, (FLAC__byte*)object->data.seek_table.points, object->data.seek_table.num_points * sizeof(FLAC__StreamMetadata_SeekPoint))) { + FLAC__metadata_object_delete(to); + return 0; + } + break; + case FLAC__METADATA_TYPE_VORBIS_COMMENT: + if (to->data.vorbis_comment.vendor_string.entry != NULL) { + free(to->data.vorbis_comment.vendor_string.entry); + to->data.vorbis_comment.vendor_string.entry = 0; + } + if (!copy_vcentry_(&to->data.vorbis_comment.vendor_string, &object->data.vorbis_comment.vendor_string)) { + FLAC__metadata_object_delete(to); + return 0; + } + if (object->data.vorbis_comment.num_comments == 0) { + to->data.vorbis_comment.comments = 0; + } + else { + to->data.vorbis_comment.comments = vorbiscomment_entry_array_copy_(object->data.vorbis_comment.comments, object->data.vorbis_comment.num_comments); + if (to->data.vorbis_comment.comments == NULL) { + to->data.vorbis_comment.num_comments = 0; + FLAC__metadata_object_delete(to); + return 0; + } + } + to->data.vorbis_comment.num_comments = object->data.vorbis_comment.num_comments; + break; + case FLAC__METADATA_TYPE_CUESHEET: + memcpy(&to->data.cue_sheet, &object->data.cue_sheet, sizeof(FLAC__StreamMetadata_CueSheet)); + if (object->data.cue_sheet.num_tracks == 0) { + FLAC__ASSERT(object->data.cue_sheet.tracks == NULL); + } + else { + FLAC__ASSERT(object->data.cue_sheet.tracks != 0); + to->data.cue_sheet.tracks = cuesheet_track_array_copy_(object->data.cue_sheet.tracks, object->data.cue_sheet.num_tracks); + if (to->data.cue_sheet.tracks == NULL) { + FLAC__metadata_object_delete(to); + return 0; + } + } + break; + case FLAC__METADATA_TYPE_PICTURE: + to->data.picture.type = object->data.picture.type; + if (!copy_cstring_(&to->data.picture.mime_type, object->data.picture.mime_type)) { + FLAC__metadata_object_delete(to); + return 0; + } + if (!copy_cstring_((char**)(&to->data.picture.description), (const char*)object->data.picture.description)) { + FLAC__metadata_object_delete(to); + return 0; + } + to->data.picture.width = object->data.picture.width; + to->data.picture.height = object->data.picture.height; + to->data.picture.depth = object->data.picture.depth; + to->data.picture.colors = object->data.picture.colors; + to->data.picture.data_length = object->data.picture.data_length; + if (!copy_bytes_((&to->data.picture.data), object->data.picture.data, object->data.picture.data_length)) { + FLAC__metadata_object_delete(to); + return 0; + } + break; + default: + if (!copy_bytes_(&to->data.unknown.data, object->data.unknown.data, object->length)) { + FLAC__metadata_object_delete(to); + return 0; + } + break; + } + } + + return to; +} + +void FLAC__metadata_object_delete_data(FLAC__StreamMetadata *object) +{ + FLAC__ASSERT(object != NULL); + + switch(object->type) { + case FLAC__METADATA_TYPE_STREAMINFO: + case FLAC__METADATA_TYPE_PADDING: + break; + case FLAC__METADATA_TYPE_APPLICATION: + if (object->data.application.data != NULL) { + free(object->data.application.data); + object->data.application.data = NULL; + } + break; + case FLAC__METADATA_TYPE_SEEKTABLE: + if (object->data.seek_table.points != NULL) { + free(object->data.seek_table.points); + object->data.seek_table.points = NULL; + } + break; + case FLAC__METADATA_TYPE_VORBIS_COMMENT: + if (object->data.vorbis_comment.vendor_string.entry != NULL) { + free(object->data.vorbis_comment.vendor_string.entry); + object->data.vorbis_comment.vendor_string.entry = 0; + } + if (object->data.vorbis_comment.comments != NULL) { + FLAC__ASSERT(object->data.vorbis_comment.num_comments > 0); + vorbiscomment_entry_array_delete_(object->data.vorbis_comment.comments, object->data.vorbis_comment.num_comments); + object->data.vorbis_comment.comments = NULL; + object->data.vorbis_comment.num_comments = 0; + } + break; + case FLAC__METADATA_TYPE_CUESHEET: + if (object->data.cue_sheet.tracks != NULL) { + FLAC__ASSERT(object->data.cue_sheet.num_tracks > 0); + cuesheet_track_array_delete_(object->data.cue_sheet.tracks, object->data.cue_sheet.num_tracks); + object->data.cue_sheet.tracks = NULL; + object->data.cue_sheet.num_tracks = 0; + } + break; + case FLAC__METADATA_TYPE_PICTURE: + if (object->data.picture.mime_type != NULL) { + free(object->data.picture.mime_type); + object->data.picture.mime_type = NULL; + } + if (object->data.picture.description != NULL) { + free(object->data.picture.description); + object->data.picture.description = NULL; + } + if (object->data.picture.data != NULL) { + free(object->data.picture.data); + object->data.picture.data = NULL; + } + break; + default: + if (object->data.unknown.data != NULL) { + free(object->data.unknown.data); + object->data.unknown.data = NULL; + } + break; + } +} + +FLAC_API void FLAC__metadata_object_delete(FLAC__StreamMetadata *object) +{ + FLAC__metadata_object_delete_data(object); + free(object); +} + +static FLAC__bool compare_block_data_streaminfo_(const FLAC__StreamMetadata_StreamInfo *block1, const FLAC__StreamMetadata_StreamInfo *block2) +{ + if (block1->min_blocksize != block2->min_blocksize) + return false; + if (block1->max_blocksize != block2->max_blocksize) + return false; + if (block1->min_framesize != block2->min_framesize) + return false; + if (block1->max_framesize != block2->max_framesize) + return false; + if (block1->sample_rate != block2->sample_rate) + return false; + if (block1->channels != block2->channels) + return false; + if (block1->bits_per_sample != block2->bits_per_sample) + return false; + if (block1->total_samples != block2->total_samples) + return false; + if (memcmp(block1->md5sum, block2->md5sum, 16) != 0) + return false; + return true; +} + +static FLAC__bool compare_block_data_application_(const FLAC__StreamMetadata_Application *block1, const FLAC__StreamMetadata_Application *block2, unsigned block_length) +{ + FLAC__ASSERT(block1 != NULL); + FLAC__ASSERT(block2 != NULL); + FLAC__ASSERT(block_length >= sizeof(block1->id)); + + if (memcmp(block1->id, block2->id, sizeof(block1->id)) != 0) + return false; + if (block1->data != NULL && block2->data != NULL) + return memcmp(block1->data, block2->data, block_length - sizeof(block1->id)) == 0; + else + return block1->data == block2->data; +} + +static FLAC__bool compare_block_data_seektable_(const FLAC__StreamMetadata_SeekTable *block1, const FLAC__StreamMetadata_SeekTable *block2) +{ + unsigned i; + + FLAC__ASSERT(block1 != NULL); + FLAC__ASSERT(block2 != NULL); + + if (block1->num_points != block2->num_points) + return false; + + if (block1->points != NULL && block2->points != NULL) { + for (i = 0; i < block1->num_points; i++) { + if (block1->points[i].sample_number != block2->points[i].sample_number) + return false; + if (block1->points[i].stream_offset != block2->points[i].stream_offset) + return false; + if (block1->points[i].frame_samples != block2->points[i].frame_samples) + return false; + } + return true; + } + else + return block1->points == block2->points; +} + +static FLAC__bool compare_block_data_vorbiscomment_(const FLAC__StreamMetadata_VorbisComment *block1, const FLAC__StreamMetadata_VorbisComment *block2) +{ + unsigned i; + + if (block1->vendor_string.length != block2->vendor_string.length) + return false; + + if (block1->vendor_string.entry != NULL && block2->vendor_string.entry != NULL) { + if (memcmp(block1->vendor_string.entry, block2->vendor_string.entry, block1->vendor_string.length) != 0) + return false; + } + else if (block1->vendor_string.entry != block2->vendor_string.entry) + return false; + + if (block1->num_comments != block2->num_comments) + return false; + + for (i = 0; i < block1->num_comments; i++) { + if (block1->comments[i].entry != NULL && block2->comments[i].entry != NULL) { + if (memcmp(block1->comments[i].entry, block2->comments[i].entry, block1->comments[i].length) != 0) + return false; + } + else if (block1->comments[i].entry != block2->comments[i].entry) + return false; + } + return true; +} + +static FLAC__bool compare_block_data_cuesheet_(const FLAC__StreamMetadata_CueSheet *block1, const FLAC__StreamMetadata_CueSheet *block2) +{ + unsigned i, j; + + if (strcmp(block1->media_catalog_number, block2->media_catalog_number) != 0) + return false; + + if (block1->lead_in != block2->lead_in) + return false; + + if (block1->is_cd != block2->is_cd) + return false; + + if (block1->num_tracks != block2->num_tracks) + return false; + + if (block1->tracks != NULL && block2->tracks != NULL) { + FLAC__ASSERT(block1->num_tracks > 0); + for (i = 0; i < block1->num_tracks; i++) { + if (block1->tracks[i].offset != block2->tracks[i].offset) + return false; + if (block1->tracks[i].number != block2->tracks[i].number) + return false; + if (memcmp(block1->tracks[i].isrc, block2->tracks[i].isrc, sizeof(block1->tracks[i].isrc)) != 0) + return false; + if (block1->tracks[i].type != block2->tracks[i].type) + return false; + if (block1->tracks[i].pre_emphasis != block2->tracks[i].pre_emphasis) + return false; + if (block1->tracks[i].num_indices != block2->tracks[i].num_indices) + return false; + if (block1->tracks[i].indices != NULL && block2->tracks[i].indices != NULL) { + FLAC__ASSERT(block1->tracks[i].num_indices > 0); + for (j = 0; j < block1->tracks[i].num_indices; j++) { + if (block1->tracks[i].indices[j].offset != block2->tracks[i].indices[j].offset) + return false; + if (block1->tracks[i].indices[j].number != block2->tracks[i].indices[j].number) + return false; + } + } + else if (block1->tracks[i].indices != block2->tracks[i].indices) + return false; + } + } + else if (block1->tracks != block2->tracks) + return false; + return true; +} + +static FLAC__bool compare_block_data_picture_(const FLAC__StreamMetadata_Picture *block1, const FLAC__StreamMetadata_Picture *block2) +{ + if (block1->type != block2->type) + return false; + if (block1->mime_type != block2->mime_type && (block1->mime_type == 0 || block2->mime_type == 0 || strcmp(block1->mime_type, block2->mime_type))) + return false; + if (block1->description != block2->description && (block1->description == 0 || block2->description == 0 || strcmp((const char *)block1->description, (const char *)block2->description))) + return false; + if (block1->width != block2->width) + return false; + if (block1->height != block2->height) + return false; + if (block1->depth != block2->depth) + return false; + if (block1->colors != block2->colors) + return false; + if (block1->data_length != block2->data_length) + return false; + if (block1->data != block2->data && (block1->data == NULL || block2->data == NULL || memcmp(block1->data, block2->data, block1->data_length))) + return false; + return true; +} + +static FLAC__bool compare_block_data_unknown_(const FLAC__StreamMetadata_Unknown *block1, const FLAC__StreamMetadata_Unknown *block2, unsigned block_length) +{ + FLAC__ASSERT(block1 != NULL); + FLAC__ASSERT(block2 != NULL); + + if (block1->data != NULL && block2->data != NULL) + return memcmp(block1->data, block2->data, block_length) == 0; + else + return block1->data == block2->data; +} + +FLAC_API FLAC__bool FLAC__metadata_object_is_equal(const FLAC__StreamMetadata *block1, const FLAC__StreamMetadata *block2) +{ + FLAC__ASSERT(block1 != NULL); + FLAC__ASSERT(block2 != NULL); + + if (block1->type != block2->type) { + return false; + } + if (block1->is_last != block2->is_last) { + return false; + } + if (block1->length != block2->length) { + return false; + } + switch(block1->type) { + case FLAC__METADATA_TYPE_STREAMINFO: + return compare_block_data_streaminfo_(&block1->data.stream_info, &block2->data.stream_info); + case FLAC__METADATA_TYPE_PADDING: + return true; /* we don't compare the padding guts */ + case FLAC__METADATA_TYPE_APPLICATION: + return compare_block_data_application_(&block1->data.application, &block2->data.application, block1->length); + case FLAC__METADATA_TYPE_SEEKTABLE: + return compare_block_data_seektable_(&block1->data.seek_table, &block2->data.seek_table); + case FLAC__METADATA_TYPE_VORBIS_COMMENT: + return compare_block_data_vorbiscomment_(&block1->data.vorbis_comment, &block2->data.vorbis_comment); + case FLAC__METADATA_TYPE_CUESHEET: + return compare_block_data_cuesheet_(&block1->data.cue_sheet, &block2->data.cue_sheet); + case FLAC__METADATA_TYPE_PICTURE: + return compare_block_data_picture_(&block1->data.picture, &block2->data.picture); + default: + return compare_block_data_unknown_(&block1->data.unknown, &block2->data.unknown, block1->length); + } +} + +FLAC_API FLAC__bool FLAC__metadata_object_application_set_data(FLAC__StreamMetadata *object, FLAC__byte *data, unsigned length, FLAC__bool copy) +{ + FLAC__byte *save; + + FLAC__ASSERT(object != NULL); + FLAC__ASSERT(object->type == FLAC__METADATA_TYPE_APPLICATION); + FLAC__ASSERT((data != NULL && length > 0) || (data == NULL && length == 0 && copy == false)); + + save = object->data.application.data; + + /* do the copy first so that if we fail we leave the object untouched */ + if (copy) { + if (!copy_bytes_(&object->data.application.data, data, length)) + return false; + } + else { + object->data.application.data = data; + } + + free(save); + + object->length = FLAC__STREAM_METADATA_APPLICATION_ID_LEN / 8 + length; + return true; +} + +FLAC_API FLAC__bool FLAC__metadata_object_seektable_resize_points(FLAC__StreamMetadata *object, unsigned new_num_points) +{ + FLAC__ASSERT(object != NULL); + FLAC__ASSERT(object->type == FLAC__METADATA_TYPE_SEEKTABLE); + + if (object->data.seek_table.points == 0) { + FLAC__ASSERT(object->data.seek_table.num_points == 0); + if (new_num_points == 0) + return true; + else if ((object->data.seek_table.points = seekpoint_array_new_(new_num_points)) == 0) + return false; + } + else { + const size_t old_size = object->data.seek_table.num_points * sizeof(FLAC__StreamMetadata_SeekPoint); + const size_t new_size = new_num_points * sizeof(FLAC__StreamMetadata_SeekPoint); + + /* overflow check */ + if (new_num_points > UINT32_MAX / sizeof(FLAC__StreamMetadata_SeekPoint)) + return false; + + FLAC__ASSERT(object->data.seek_table.num_points > 0); + + if (new_size == 0) { + free(object->data.seek_table.points); + object->data.seek_table.points = 0; + } + else if ((object->data.seek_table.points = safe_realloc_(object->data.seek_table.points, new_size)) == NULL) + return false; + + /* if growing, set new elements to placeholders */ + if (new_size > old_size) { + unsigned i; + for (i = object->data.seek_table.num_points; i < new_num_points; i++) { + object->data.seek_table.points[i].sample_number = FLAC__STREAM_METADATA_SEEKPOINT_PLACEHOLDER; + object->data.seek_table.points[i].stream_offset = 0; + object->data.seek_table.points[i].frame_samples = 0; + } + } + } + + object->data.seek_table.num_points = new_num_points; + + seektable_calculate_length_(object); + return true; +} + +FLAC_API void FLAC__metadata_object_seektable_set_point(FLAC__StreamMetadata *object, unsigned point_num, FLAC__StreamMetadata_SeekPoint point) +{ + FLAC__ASSERT(object != NULL); + FLAC__ASSERT(object->type == FLAC__METADATA_TYPE_SEEKTABLE); + FLAC__ASSERT(point_num < object->data.seek_table.num_points); + + object->data.seek_table.points[point_num] = point; +} + +FLAC_API FLAC__bool FLAC__metadata_object_seektable_insert_point(FLAC__StreamMetadata *object, unsigned point_num, FLAC__StreamMetadata_SeekPoint point) +{ + int i; + + FLAC__ASSERT(object != NULL); + FLAC__ASSERT(object->type == FLAC__METADATA_TYPE_SEEKTABLE); + FLAC__ASSERT(point_num <= object->data.seek_table.num_points); + + if (!FLAC__metadata_object_seektable_resize_points(object, object->data.seek_table.num_points+1)) + return false; + + /* move all points >= point_num forward one space */ + for (i = (int)object->data.seek_table.num_points-1; i > (int)point_num; i--) + object->data.seek_table.points[i] = object->data.seek_table.points[i-1]; + + FLAC__metadata_object_seektable_set_point(object, point_num, point); + seektable_calculate_length_(object); + return true; +} + +FLAC_API FLAC__bool FLAC__metadata_object_seektable_delete_point(FLAC__StreamMetadata *object, unsigned point_num) +{ + unsigned i; + + FLAC__ASSERT(object != NULL); + FLAC__ASSERT(object->type == FLAC__METADATA_TYPE_SEEKTABLE); + FLAC__ASSERT(point_num < object->data.seek_table.num_points); + + /* move all points > point_num backward one space */ + for (i = point_num; i < object->data.seek_table.num_points-1; i++) + object->data.seek_table.points[i] = object->data.seek_table.points[i+1]; + + return FLAC__metadata_object_seektable_resize_points(object, object->data.seek_table.num_points-1); +} + +FLAC_API FLAC__bool FLAC__metadata_object_seektable_is_legal(const FLAC__StreamMetadata *object) +{ + FLAC__ASSERT(object != NULL); + FLAC__ASSERT(object->type == FLAC__METADATA_TYPE_SEEKTABLE); + + return FLAC__format_seektable_is_legal(&object->data.seek_table); +} + +FLAC_API FLAC__bool FLAC__metadata_object_seektable_template_append_placeholders(FLAC__StreamMetadata *object, unsigned num) +{ + FLAC__ASSERT(object != NULL); + FLAC__ASSERT(object->type == FLAC__METADATA_TYPE_SEEKTABLE); + + if (num > 0) + /* WATCHOUT: we rely on the fact that growing the array adds PLACEHOLDERS at the end */ + return FLAC__metadata_object_seektable_resize_points(object, object->data.seek_table.num_points + num); + else + return true; +} + +FLAC_API FLAC__bool FLAC__metadata_object_seektable_template_append_point(FLAC__StreamMetadata *object, FLAC__uint64 sample_number) +{ + FLAC__StreamMetadata_SeekTable *seek_table; + + FLAC__ASSERT(object != NULL); + FLAC__ASSERT(object->type == FLAC__METADATA_TYPE_SEEKTABLE); + + seek_table = &object->data.seek_table; + + if (!FLAC__metadata_object_seektable_resize_points(object, seek_table->num_points + 1)) + return false; + + seek_table->points[seek_table->num_points - 1].sample_number = sample_number; + seek_table->points[seek_table->num_points - 1].stream_offset = 0; + seek_table->points[seek_table->num_points - 1].frame_samples = 0; + + return true; +} + +FLAC_API FLAC__bool FLAC__metadata_object_seektable_template_append_points(FLAC__StreamMetadata *object, FLAC__uint64 sample_numbers[], unsigned num) +{ + FLAC__ASSERT(object != NULL); + FLAC__ASSERT(object->type == FLAC__METADATA_TYPE_SEEKTABLE); + FLAC__ASSERT(sample_numbers != 0 || num == 0); + + if (num > 0) { + FLAC__StreamMetadata_SeekTable *seek_table = &object->data.seek_table; + unsigned i, j; + + i = seek_table->num_points; + + if (!FLAC__metadata_object_seektable_resize_points(object, seek_table->num_points + num)) + return false; + + for (j = 0; j < num; i++, j++) { + seek_table->points[i].sample_number = sample_numbers[j]; + seek_table->points[i].stream_offset = 0; + seek_table->points[i].frame_samples = 0; + } + } + + return true; +} + +FLAC_API FLAC__bool FLAC__metadata_object_seektable_template_append_spaced_points(FLAC__StreamMetadata *object, unsigned num, FLAC__uint64 total_samples) +{ + FLAC__ASSERT(object != NULL); + FLAC__ASSERT(object->type == FLAC__METADATA_TYPE_SEEKTABLE); + FLAC__ASSERT(total_samples > 0); + + if (num > 0 && total_samples > 0) { + FLAC__StreamMetadata_SeekTable *seek_table = &object->data.seek_table; + unsigned i, j; + + i = seek_table->num_points; + + if (!FLAC__metadata_object_seektable_resize_points(object, seek_table->num_points + num)) + return false; + + for (j = 0; j < num; i++, j++) { + seek_table->points[i].sample_number = total_samples * (FLAC__uint64)j / (FLAC__uint64)num; + seek_table->points[i].stream_offset = 0; + seek_table->points[i].frame_samples = 0; + } + } + + return true; +} + +FLAC_API FLAC__bool FLAC__metadata_object_seektable_template_append_spaced_points_by_samples(FLAC__StreamMetadata *object, unsigned samples, FLAC__uint64 total_samples) +{ + FLAC__ASSERT(object != NULL); + FLAC__ASSERT(object->type == FLAC__METADATA_TYPE_SEEKTABLE); + FLAC__ASSERT(samples > 0); + FLAC__ASSERT(total_samples > 0); + + if (samples > 0 && total_samples > 0) { + FLAC__StreamMetadata_SeekTable *seek_table = &object->data.seek_table; + unsigned i, j; + FLAC__uint64 num, sample; + + num = 1 + total_samples / samples; /* 1+ for the first sample at 0 */ + /* now account for the fact that we don't place a seekpoint at "total_samples" since samples are number from 0: */ + if (total_samples % samples == 0) + num--; + + /* Put a strict upper bound on the number of allowed seek points. */ + if (num > 32768) { + /* Set the bound and recalculate samples accordingly. */ + num = 32768; + samples = total_samples / num; + } + + i = seek_table->num_points; + + if (!FLAC__metadata_object_seektable_resize_points(object, seek_table->num_points + (unsigned)num)) + return false; + + sample = 0; + for (j = 0; j < num; i++, j++, sample += samples) { + seek_table->points[i].sample_number = sample; + seek_table->points[i].stream_offset = 0; + seek_table->points[i].frame_samples = 0; + } + } + + return true; +} + +FLAC_API FLAC__bool FLAC__metadata_object_seektable_template_sort(FLAC__StreamMetadata *object, FLAC__bool compact) +{ + unsigned unique; + + FLAC__ASSERT(object != NULL); + FLAC__ASSERT(object->type == FLAC__METADATA_TYPE_SEEKTABLE); + + unique = FLAC__format_seektable_sort(&object->data.seek_table); + + return !compact || FLAC__metadata_object_seektable_resize_points(object, unique); +} + +FLAC_API FLAC__bool FLAC__metadata_object_vorbiscomment_set_vendor_string(FLAC__StreamMetadata *object, FLAC__StreamMetadata_VorbisComment_Entry entry, FLAC__bool copy) +{ + if (!FLAC__format_vorbiscomment_entry_value_is_legal(entry.entry, entry.length)) + return false; + return vorbiscomment_set_entry_(object, &object->data.vorbis_comment.vendor_string, &entry, copy); +} + +FLAC_API FLAC__bool FLAC__metadata_object_vorbiscomment_resize_comments(FLAC__StreamMetadata *object, unsigned new_num_comments) +{ + FLAC__ASSERT(object != NULL); + FLAC__ASSERT(object->type == FLAC__METADATA_TYPE_VORBIS_COMMENT); + + if (object->data.vorbis_comment.comments == NULL) { + FLAC__ASSERT(object->data.vorbis_comment.num_comments == 0); + if (new_num_comments == 0) + return true; + else if ((object->data.vorbis_comment.comments = vorbiscomment_entry_array_new_(new_num_comments)) == NULL) + return false; + } + else { + const size_t old_size = object->data.vorbis_comment.num_comments * sizeof(FLAC__StreamMetadata_VorbisComment_Entry); + const size_t new_size = new_num_comments * sizeof(FLAC__StreamMetadata_VorbisComment_Entry); + + /* overflow check */ + if (new_num_comments > UINT32_MAX / sizeof(FLAC__StreamMetadata_VorbisComment_Entry)) + return false; + + FLAC__ASSERT(object->data.vorbis_comment.num_comments > 0); + + /* if shrinking, free the truncated entries */ + if (new_num_comments < object->data.vorbis_comment.num_comments) { + unsigned i; + for (i = new_num_comments; i < object->data.vorbis_comment.num_comments; i++) + if (object->data.vorbis_comment.comments[i].entry != NULL) + free(object->data.vorbis_comment.comments[i].entry); + } + + if (new_size == 0) { + free(object->data.vorbis_comment.comments); + object->data.vorbis_comment.comments = 0; + } + else { + FLAC__StreamMetadata_VorbisComment_Entry *oldptr = object->data.vorbis_comment.comments; + if ((object->data.vorbis_comment.comments = realloc(object->data.vorbis_comment.comments, new_size)) == NULL) { + vorbiscomment_entry_array_delete_(oldptr, object->data.vorbis_comment.num_comments); + object->data.vorbis_comment.num_comments = 0; + return false; + } + } + + /* if growing, zero all the length/pointers of new elements */ + if (new_size > old_size) + memset(object->data.vorbis_comment.comments + object->data.vorbis_comment.num_comments, 0, new_size - old_size); + } + + object->data.vorbis_comment.num_comments = new_num_comments; + + vorbiscomment_calculate_length_(object); + return true; +} + +FLAC_API FLAC__bool FLAC__metadata_object_vorbiscomment_set_comment(FLAC__StreamMetadata *object, unsigned comment_num, FLAC__StreamMetadata_VorbisComment_Entry entry, FLAC__bool copy) +{ + FLAC__ASSERT(object != NULL); + FLAC__ASSERT(comment_num < object->data.vorbis_comment.num_comments); + + if (!FLAC__format_vorbiscomment_entry_is_legal(entry.entry, entry.length)) + return false; + return vorbiscomment_set_entry_(object, &object->data.vorbis_comment.comments[comment_num], &entry, copy); +} + +FLAC_API FLAC__bool FLAC__metadata_object_vorbiscomment_insert_comment(FLAC__StreamMetadata *object, unsigned comment_num, FLAC__StreamMetadata_VorbisComment_Entry entry, FLAC__bool copy) +{ + FLAC__StreamMetadata_VorbisComment *vc; + + FLAC__ASSERT(object != NULL); + FLAC__ASSERT(object->type == FLAC__METADATA_TYPE_VORBIS_COMMENT); + FLAC__ASSERT(comment_num <= object->data.vorbis_comment.num_comments); + + if (!FLAC__format_vorbiscomment_entry_is_legal(entry.entry, entry.length)) + return false; + + vc = &object->data.vorbis_comment; + + if (!FLAC__metadata_object_vorbiscomment_resize_comments(object, vc->num_comments+1)) + return false; + + /* move all comments >= comment_num forward one space */ + memmove(&vc->comments[comment_num+1], &vc->comments[comment_num], sizeof(FLAC__StreamMetadata_VorbisComment_Entry)*(vc->num_comments-1-comment_num)); + vc->comments[comment_num].length = 0; + vc->comments[comment_num].entry = 0; + + return FLAC__metadata_object_vorbiscomment_set_comment(object, comment_num, entry, copy); +} + +FLAC_API FLAC__bool FLAC__metadata_object_vorbiscomment_append_comment(FLAC__StreamMetadata *object, FLAC__StreamMetadata_VorbisComment_Entry entry, FLAC__bool copy) +{ + FLAC__ASSERT(object != NULL); + FLAC__ASSERT(object->type == FLAC__METADATA_TYPE_VORBIS_COMMENT); + return FLAC__metadata_object_vorbiscomment_insert_comment(object, object->data.vorbis_comment.num_comments, entry, copy); +} + +FLAC_API FLAC__bool FLAC__metadata_object_vorbiscomment_replace_comment(FLAC__StreamMetadata *object, FLAC__StreamMetadata_VorbisComment_Entry entry, FLAC__bool all, FLAC__bool copy) +{ + FLAC__ASSERT(entry.entry != NULL && entry.length > 0); + + if (!FLAC__format_vorbiscomment_entry_is_legal(entry.entry, entry.length)) + return false; + + { + int i; + size_t field_name_length; + const FLAC__byte *eq = (FLAC__byte*)memchr(entry.entry, '=', entry.length); + + if (eq == NULL) + return false; /* double protection */ + + field_name_length = eq-entry.entry; + + i = vorbiscomment_find_entry_from_(object, 0, (const char *)entry.entry, field_name_length); + if (i >= 0) { + unsigned indx = (unsigned)i; + if (!FLAC__metadata_object_vorbiscomment_set_comment(object, indx, entry, copy)) + return false; + entry = object->data.vorbis_comment.comments[indx]; + indx++; /* skip over replaced comment */ + if (all && indx < object->data.vorbis_comment.num_comments) { + i = vorbiscomment_find_entry_from_(object, indx, (const char *)entry.entry, field_name_length); + while (i >= 0) { + indx = (unsigned)i; + if (!FLAC__metadata_object_vorbiscomment_delete_comment(object, indx)) + return false; + if (indx < object->data.vorbis_comment.num_comments) + i = vorbiscomment_find_entry_from_(object, indx, (const char *)entry.entry, field_name_length); + else + i = -1; + } + } + return true; + } + else + return FLAC__metadata_object_vorbiscomment_append_comment(object, entry, copy); + } +} + +FLAC_API FLAC__bool FLAC__metadata_object_vorbiscomment_delete_comment(FLAC__StreamMetadata *object, unsigned comment_num) +{ + FLAC__StreamMetadata_VorbisComment *vc; + + FLAC__ASSERT(object != NULL); + FLAC__ASSERT(object->type == FLAC__METADATA_TYPE_VORBIS_COMMENT); + FLAC__ASSERT(comment_num < object->data.vorbis_comment.num_comments); + + vc = &object->data.vorbis_comment; + + /* free the comment at comment_num */ + free(vc->comments[comment_num].entry); + + /* move all comments > comment_num backward one space */ + memmove(&vc->comments[comment_num], &vc->comments[comment_num+1], sizeof(FLAC__StreamMetadata_VorbisComment_Entry)*(vc->num_comments-comment_num-1)); + vc->comments[vc->num_comments-1].length = 0; + vc->comments[vc->num_comments-1].entry = 0; + + return FLAC__metadata_object_vorbiscomment_resize_comments(object, vc->num_comments-1); +} + +FLAC_API FLAC__bool FLAC__metadata_object_vorbiscomment_entry_from_name_value_pair(FLAC__StreamMetadata_VorbisComment_Entry *entry, const char *field_name, const char *field_value) +{ + FLAC__ASSERT(entry != NULL); + FLAC__ASSERT(field_name != NULL); + FLAC__ASSERT(field_value != NULL); + + if (!FLAC__format_vorbiscomment_entry_name_is_legal(field_name)) + return false; + if (!FLAC__format_vorbiscomment_entry_value_is_legal((const FLAC__byte *)field_value, (unsigned)(-1))) + return false; + + { + const size_t nn = strlen(field_name); + const size_t nv = strlen(field_value); + entry->length = nn + 1 /*=*/ + nv; + if ((entry->entry = safe_malloc_add_4op_(nn, /*+*/1, /*+*/nv, /*+*/1)) == NULL) + return false; + memcpy(entry->entry, field_name, nn); + entry->entry[nn] = '='; + memcpy(entry->entry+nn+1, field_value, nv); + entry->entry[entry->length] = '\0'; + } + + return true; +} + +FLAC_API FLAC__bool FLAC__metadata_object_vorbiscomment_entry_to_name_value_pair(const FLAC__StreamMetadata_VorbisComment_Entry entry, char **field_name, char **field_value) +{ + FLAC__ASSERT(entry.entry != NULL && entry.length > 0); + FLAC__ASSERT(field_name != NULL); + FLAC__ASSERT(field_value != NULL); + + if (!FLAC__format_vorbiscomment_entry_is_legal(entry.entry, entry.length)) + return false; + + { + const FLAC__byte *eq = (FLAC__byte*)memchr(entry.entry, '=', entry.length); + const size_t nn = eq-entry.entry; + const size_t nv = entry.length-nn-1; /* -1 for the '=' */ + + if (eq == NULL) + return false; /* double protection */ + if ((*field_name = safe_malloc_add_2op_(nn, /*+*/1)) == NULL) + return false; + if ((*field_value = safe_malloc_add_2op_(nv, /*+*/1)) == NULL) { + free(*field_name); + return false; + } + memcpy(*field_name, entry.entry, nn); + memcpy(*field_value, entry.entry+nn+1, nv); + (*field_name)[nn] = '\0'; + (*field_value)[nv] = '\0'; + } + + return true; +} + +FLAC_API FLAC__bool FLAC__metadata_object_vorbiscomment_entry_matches(const FLAC__StreamMetadata_VorbisComment_Entry entry, const char *field_name, unsigned field_name_length) +{ + FLAC__ASSERT(entry.entry != NULL && entry.length > 0); + { + const FLAC__byte *eq = (FLAC__byte*)memchr(entry.entry, '=', entry.length); + return (eq != NULL && (unsigned)(eq-entry.entry) == field_name_length && FLAC__STRNCASECMP(field_name, (const char *)entry.entry, field_name_length) == 0); + } +} + +FLAC_API int FLAC__metadata_object_vorbiscomment_find_entry_from(const FLAC__StreamMetadata *object, unsigned offset, const char *field_name) +{ + FLAC__ASSERT(field_name != NULL); + + return vorbiscomment_find_entry_from_(object, offset, field_name, strlen(field_name)); +} + +FLAC_API int FLAC__metadata_object_vorbiscomment_remove_entry_matching(FLAC__StreamMetadata *object, const char *field_name) +{ + const unsigned field_name_length = strlen(field_name); + unsigned i; + + FLAC__ASSERT(object != NULL); + FLAC__ASSERT(object->type == FLAC__METADATA_TYPE_VORBIS_COMMENT); + + for (i = 0; i < object->data.vorbis_comment.num_comments; i++) { + if (FLAC__metadata_object_vorbiscomment_entry_matches(object->data.vorbis_comment.comments[i], field_name, field_name_length)) { + if (!FLAC__metadata_object_vorbiscomment_delete_comment(object, i)) + return -1; + else + return 1; + } + } + + return 0; +} + +FLAC_API int FLAC__metadata_object_vorbiscomment_remove_entries_matching(FLAC__StreamMetadata *object, const char *field_name) +{ + FLAC__bool ok = true; + unsigned matching = 0; + const unsigned field_name_length = strlen(field_name); + int i; + + FLAC__ASSERT(object != NULL); + FLAC__ASSERT(object->type == FLAC__METADATA_TYPE_VORBIS_COMMENT); + + /* must delete from end to start otherwise it will interfere with our iteration */ + for (i = (int)object->data.vorbis_comment.num_comments - 1; ok && i >= 0; i--) { + if (FLAC__metadata_object_vorbiscomment_entry_matches(object->data.vorbis_comment.comments[i], field_name, field_name_length)) { + matching++; + ok &= FLAC__metadata_object_vorbiscomment_delete_comment(object, (unsigned)i); + } + } + + return ok? (int)matching : -1; +} + +FLAC_API FLAC__StreamMetadata_CueSheet_Track *FLAC__metadata_object_cuesheet_track_new(void) +{ + return calloc(1, sizeof(FLAC__StreamMetadata_CueSheet_Track)); +} + +FLAC_API FLAC__StreamMetadata_CueSheet_Track *FLAC__metadata_object_cuesheet_track_clone(const FLAC__StreamMetadata_CueSheet_Track *object) +{ + FLAC__StreamMetadata_CueSheet_Track *to; + + FLAC__ASSERT(object != NULL); + + if ((to = FLAC__metadata_object_cuesheet_track_new()) != NULL) { + if (!copy_track_(to, object)) { + FLAC__metadata_object_cuesheet_track_delete(to); + return 0; + } + } + + return to; +} + +void FLAC__metadata_object_cuesheet_track_delete_data(FLAC__StreamMetadata_CueSheet_Track *object) +{ + FLAC__ASSERT(object != NULL); + + if (object->indices != NULL) { + FLAC__ASSERT(object->num_indices > 0); + free(object->indices); + } +} + +FLAC_API void FLAC__metadata_object_cuesheet_track_delete(FLAC__StreamMetadata_CueSheet_Track *object) +{ + FLAC__metadata_object_cuesheet_track_delete_data(object); + free(object); +} + +FLAC_API FLAC__bool FLAC__metadata_object_cuesheet_track_resize_indices(FLAC__StreamMetadata *object, unsigned track_num, unsigned new_num_indices) +{ + FLAC__StreamMetadata_CueSheet_Track *track; + FLAC__ASSERT(object != NULL); + FLAC__ASSERT(object->type == FLAC__METADATA_TYPE_CUESHEET); + FLAC__ASSERT(track_num < object->data.cue_sheet.num_tracks); + + track = &object->data.cue_sheet.tracks[track_num]; + + if (track->indices == NULL) { + FLAC__ASSERT(track->num_indices == 0); + if (new_num_indices == 0) + return true; + else if ((track->indices = cuesheet_track_index_array_new_(new_num_indices)) == NULL) + return false; + } + else { + const size_t old_size = track->num_indices * sizeof(FLAC__StreamMetadata_CueSheet_Index); + const size_t new_size = new_num_indices * sizeof(FLAC__StreamMetadata_CueSheet_Index); + + /* overflow check */ + if (new_num_indices > UINT32_MAX / sizeof(FLAC__StreamMetadata_CueSheet_Index)) + return false; + + FLAC__ASSERT(track->num_indices > 0); + + if (new_size == 0) { + free(track->indices); + track->indices = 0; + } + else if ((track->indices = safe_realloc_(track->indices, new_size)) == NULL) + return false; + + /* if growing, zero all the lengths/pointers of new elements */ + if (new_size > old_size) + memset(track->indices + track->num_indices, 0, new_size - old_size); + } + + track->num_indices = new_num_indices; + + cuesheet_calculate_length_(object); + return true; +} + +FLAC_API FLAC__bool FLAC__metadata_object_cuesheet_track_insert_index(FLAC__StreamMetadata *object, unsigned track_num, unsigned index_num, FLAC__StreamMetadata_CueSheet_Index indx) +{ + FLAC__StreamMetadata_CueSheet_Track *track; + + FLAC__ASSERT(object != NULL); + FLAC__ASSERT(object->type == FLAC__METADATA_TYPE_CUESHEET); + FLAC__ASSERT(track_num < object->data.cue_sheet.num_tracks); + FLAC__ASSERT(index_num <= object->data.cue_sheet.tracks[track_num].num_indices); + + track = &object->data.cue_sheet.tracks[track_num]; + + if (!FLAC__metadata_object_cuesheet_track_resize_indices(object, track_num, track->num_indices+1)) + return false; + + /* move all indices >= index_num forward one space */ + memmove(&track->indices[index_num+1], &track->indices[index_num], sizeof(FLAC__StreamMetadata_CueSheet_Index)*(track->num_indices-1-index_num)); + + track->indices[index_num] = indx; + cuesheet_calculate_length_(object); + return true; +} + +FLAC_API FLAC__bool FLAC__metadata_object_cuesheet_track_insert_blank_index(FLAC__StreamMetadata *object, unsigned track_num, unsigned index_num) +{ + FLAC__StreamMetadata_CueSheet_Index indx; + memset(&indx, 0, sizeof(indx)); + return FLAC__metadata_object_cuesheet_track_insert_index(object, track_num, index_num, indx); +} + +FLAC_API FLAC__bool FLAC__metadata_object_cuesheet_track_delete_index(FLAC__StreamMetadata *object, unsigned track_num, unsigned index_num) +{ + FLAC__StreamMetadata_CueSheet_Track *track; + + FLAC__ASSERT(object != NULL); + FLAC__ASSERT(object->type == FLAC__METADATA_TYPE_CUESHEET); + FLAC__ASSERT(track_num < object->data.cue_sheet.num_tracks); + FLAC__ASSERT(index_num < object->data.cue_sheet.tracks[track_num].num_indices); + + track = &object->data.cue_sheet.tracks[track_num]; + + /* move all indices > index_num backward one space */ + memmove(&track->indices[index_num], &track->indices[index_num+1], sizeof(FLAC__StreamMetadata_CueSheet_Index)*(track->num_indices-index_num-1)); + + FLAC__metadata_object_cuesheet_track_resize_indices(object, track_num, track->num_indices-1); + cuesheet_calculate_length_(object); + return true; +} + +FLAC_API FLAC__bool FLAC__metadata_object_cuesheet_resize_tracks(FLAC__StreamMetadata *object, unsigned new_num_tracks) +{ + FLAC__ASSERT(object != NULL); + FLAC__ASSERT(object->type == FLAC__METADATA_TYPE_CUESHEET); + + if (object->data.cue_sheet.tracks == NULL) { + FLAC__ASSERT(object->data.cue_sheet.num_tracks == 0); + if (new_num_tracks == 0) + return true; + else if ((object->data.cue_sheet.tracks = cuesheet_track_array_new_(new_num_tracks)) == NULL) + return false; + } + else { + const size_t old_size = object->data.cue_sheet.num_tracks * sizeof(FLAC__StreamMetadata_CueSheet_Track); + const size_t new_size = new_num_tracks * sizeof(FLAC__StreamMetadata_CueSheet_Track); + + /* overflow check */ + if (new_num_tracks > UINT32_MAX / sizeof(FLAC__StreamMetadata_CueSheet_Track)) + return false; + + FLAC__ASSERT(object->data.cue_sheet.num_tracks > 0); + + /* if shrinking, free the truncated entries */ + if (new_num_tracks < object->data.cue_sheet.num_tracks) { + unsigned i; + for (i = new_num_tracks; i < object->data.cue_sheet.num_tracks; i++) + free(object->data.cue_sheet.tracks[i].indices); + } + + if (new_size == 0) { + free(object->data.cue_sheet.tracks); + object->data.cue_sheet.tracks = 0; + } + else if ((object->data.cue_sheet.tracks = safe_realloc_(object->data.cue_sheet.tracks, new_size)) == NULL) + return false; + + /* if growing, zero all the lengths/pointers of new elements */ + if (new_size > old_size) + memset(object->data.cue_sheet.tracks + object->data.cue_sheet.num_tracks, 0, new_size - old_size); + } + + object->data.cue_sheet.num_tracks = new_num_tracks; + + cuesheet_calculate_length_(object); + return true; +} + +FLAC_API FLAC__bool FLAC__metadata_object_cuesheet_set_track(FLAC__StreamMetadata *object, unsigned track_num, FLAC__StreamMetadata_CueSheet_Track *track, FLAC__bool copy) +{ + FLAC__ASSERT(object != NULL); + FLAC__ASSERT(track_num < object->data.cue_sheet.num_tracks); + + return cuesheet_set_track_(object, object->data.cue_sheet.tracks + track_num, track, copy); +} + +FLAC_API FLAC__bool FLAC__metadata_object_cuesheet_insert_track(FLAC__StreamMetadata *object, unsigned track_num, FLAC__StreamMetadata_CueSheet_Track *track, FLAC__bool copy) +{ + FLAC__StreamMetadata_CueSheet *cs; + + FLAC__ASSERT(object != NULL); + FLAC__ASSERT(object->type == FLAC__METADATA_TYPE_CUESHEET); + FLAC__ASSERT(track_num <= object->data.cue_sheet.num_tracks); + + cs = &object->data.cue_sheet; + + if (!FLAC__metadata_object_cuesheet_resize_tracks(object, cs->num_tracks+1)) + return false; + + /* move all tracks >= track_num forward one space */ + memmove(&cs->tracks[track_num+1], &cs->tracks[track_num], sizeof(FLAC__StreamMetadata_CueSheet_Track)*(cs->num_tracks-1-track_num)); + cs->tracks[track_num].num_indices = 0; + cs->tracks[track_num].indices = 0; + + return FLAC__metadata_object_cuesheet_set_track(object, track_num, track, copy); +} + +FLAC_API FLAC__bool FLAC__metadata_object_cuesheet_insert_blank_track(FLAC__StreamMetadata *object, unsigned track_num) +{ + FLAC__StreamMetadata_CueSheet_Track track; + memset(&track, 0, sizeof(track)); + return FLAC__metadata_object_cuesheet_insert_track(object, track_num, &track, /*copy=*/false); +} + +FLAC_API FLAC__bool FLAC__metadata_object_cuesheet_delete_track(FLAC__StreamMetadata *object, unsigned track_num) +{ + FLAC__StreamMetadata_CueSheet *cs; + + FLAC__ASSERT(object != NULL); + FLAC__ASSERT(object->type == FLAC__METADATA_TYPE_CUESHEET); + FLAC__ASSERT(track_num < object->data.cue_sheet.num_tracks); + + cs = &object->data.cue_sheet; + + /* free the track at track_num */ + free(cs->tracks[track_num].indices); + + /* move all tracks > track_num backward one space */ + memmove(&cs->tracks[track_num], &cs->tracks[track_num+1], sizeof(FLAC__StreamMetadata_CueSheet_Track)*(cs->num_tracks-track_num-1)); + cs->tracks[cs->num_tracks-1].num_indices = 0; + cs->tracks[cs->num_tracks-1].indices = 0; + + return FLAC__metadata_object_cuesheet_resize_tracks(object, cs->num_tracks-1); +} + +FLAC_API FLAC__bool FLAC__metadata_object_cuesheet_is_legal(const FLAC__StreamMetadata *object, FLAC__bool check_cd_da_subset, const char **violation) +{ + FLAC__ASSERT(object != NULL); + FLAC__ASSERT(object->type == FLAC__METADATA_TYPE_CUESHEET); + + return FLAC__format_cuesheet_is_legal(&object->data.cue_sheet, check_cd_da_subset, violation); +} + +static FLAC__uint64 get_index_01_offset_(const FLAC__StreamMetadata_CueSheet *cs, unsigned track) +{ + if (track >= (cs->num_tracks-1) || cs->tracks[track].num_indices < 1) + return 0; + else if (cs->tracks[track].indices[0].number == 1) + return cs->tracks[track].indices[0].offset + cs->tracks[track].offset + cs->lead_in; + else if (cs->tracks[track].num_indices < 2) + return 0; + else if (cs->tracks[track].indices[1].number == 1) + return cs->tracks[track].indices[1].offset + cs->tracks[track].offset + cs->lead_in; + else + return 0; +} + +static FLAC__uint32 cddb_add_digits_(FLAC__uint32 x) +{ + FLAC__uint32 n = 0; + while (x) { + n += (x%10); + x /= 10; + } + return n; +} + +/*@@@@add to tests*/ +FLAC_API FLAC__uint32 FLAC__metadata_object_cuesheet_calculate_cddb_id(const FLAC__StreamMetadata *object) +{ + const FLAC__StreamMetadata_CueSheet *cs; + + FLAC__ASSERT(object != NULL); + FLAC__ASSERT(object->type == FLAC__METADATA_TYPE_CUESHEET); + + cs = &object->data.cue_sheet; + + if (cs->num_tracks < 2) /* need at least one real track and the lead-out track */ + return 0; + + { + FLAC__uint32 i, length, sum = 0; + for (i = 0; i < (cs->num_tracks-1); i++) /* -1 to avoid counting the lead-out */ + sum += cddb_add_digits_((FLAC__uint32)(get_index_01_offset_(cs, i) / 44100)); + length = (FLAC__uint32)((cs->tracks[cs->num_tracks-1].offset+cs->lead_in) / 44100) - (FLAC__uint32)(get_index_01_offset_(cs, 0) / 44100); + + return (sum % 0xFF) << 24 | length << 8 | (FLAC__uint32)(cs->num_tracks-1); + } +} + +FLAC_API FLAC__bool FLAC__metadata_object_picture_set_mime_type(FLAC__StreamMetadata *object, char *mime_type, FLAC__bool copy) +{ + char *old; + size_t old_length, new_length; + + FLAC__ASSERT(object != NULL); + FLAC__ASSERT(object->type == FLAC__METADATA_TYPE_PICTURE); + FLAC__ASSERT(mime_type != NULL); + + old = object->data.picture.mime_type; + old_length = old? strlen(old) : 0; + new_length = strlen(mime_type); + + /* do the copy first so that if we fail we leave the object untouched */ + if (copy) { + if (new_length >= SIZE_MAX) /* overflow check */ + return false; + if (!copy_bytes_((FLAC__byte**)(&object->data.picture.mime_type), (FLAC__byte*)mime_type, new_length+1)) + return false; + } + else { + object->data.picture.mime_type = mime_type; + } + + free(old); + + object->length -= old_length; + object->length += new_length; + return true; +} + +FLAC_API FLAC__bool FLAC__metadata_object_picture_set_description(FLAC__StreamMetadata *object, FLAC__byte *description, FLAC__bool copy) +{ + FLAC__byte *old; + size_t old_length, new_length; + + FLAC__ASSERT(object != NULL); + FLAC__ASSERT(object->type == FLAC__METADATA_TYPE_PICTURE); + FLAC__ASSERT(description != NULL); + + old = object->data.picture.description; + old_length = old? strlen((const char *)old) : 0; + new_length = strlen((const char *)description); + + /* do the copy first so that if we fail we leave the object untouched */ + if (copy) { + if (new_length >= SIZE_MAX) /* overflow check */ + return false; + if (!copy_bytes_(&object->data.picture.description, description, new_length+1)) + return false; + } + else { + object->data.picture.description = description; + } + + free(old); + + object->length -= old_length; + object->length += new_length; + return true; +} + +FLAC_API FLAC__bool FLAC__metadata_object_picture_set_data(FLAC__StreamMetadata *object, FLAC__byte *data, FLAC__uint32 length, FLAC__bool copy) +{ + FLAC__byte *old; + + FLAC__ASSERT(object != NULL); + FLAC__ASSERT(object->type == FLAC__METADATA_TYPE_PICTURE); + FLAC__ASSERT((data != NULL && length > 0) || (data == NULL && length == 0 && copy == false)); + + old = object->data.picture.data; + + /* do the copy first so that if we fail we leave the object untouched */ + if (copy) { + if (!copy_bytes_(&object->data.picture.data, data, length)) + return false; + } + else { + object->data.picture.data = data; + } + + free(old); + + object->length -= object->data.picture.data_length; + object->data.picture.data_length = length; + object->length += length; + return true; +} + +FLAC_API FLAC__bool FLAC__metadata_object_picture_is_legal(const FLAC__StreamMetadata *object, const char **violation) +{ + FLAC__ASSERT(object != NULL); + FLAC__ASSERT(object->type == FLAC__METADATA_TYPE_PICTURE); + + return FLAC__format_picture_is_legal(&object->data.picture, violation); +} diff --git a/core/deps/flac/src/libFLAC/stream_decoder.c b/core/deps/flac/src/libFLAC/stream_decoder.c new file mode 100644 index 000000000..d364b0ce9 --- /dev/null +++ b/core/deps/flac/src/libFLAC/stream_decoder.c @@ -0,0 +1,3400 @@ +/* libFLAC - Free Lossless Audio Codec library + * Copyright (C) 2000-2009 Josh Coalson + * Copyright (C) 2011-2016 Xiph.Org Foundation + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * - Neither the name of the Xiph.org Foundation nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifdef HAVE_CONFIG_H +# include +#endif + +#include +#include /* for malloc() */ +#include /* for memset/memcpy() */ +#include /* for stat() */ +#include /* for off_t */ +#include "share/compat.h" +#include "FLAC/assert.h" +#include "share/alloc.h" +#include "protected/stream_decoder.h" +#include "private/bitreader.h" +#include "private/bitmath.h" +#include "private/cpu.h" +#include "private/crc.h" +#include "private/fixed.h" +#include "private/format.h" +#include "private/lpc.h" +#include "private/md5.h" +#include "private/memory.h" +#include "private/macros.h" + + +/* technically this should be in an "export.c" but this is convenient enough */ +FLAC_API int FLAC_API_SUPPORTS_OGG_FLAC = FLAC__HAS_OGG; + + +/*********************************************************************** + * + * Private static data + * + ***********************************************************************/ + +static const FLAC__byte ID3V2_TAG_[3] = { 'I', 'D', '3' }; + +/*********************************************************************** + * + * Private class method prototypes + * + ***********************************************************************/ + +static void set_defaults_(FLAC__StreamDecoder *decoder); +static FILE *get_binary_stdin_(void); +static FLAC__bool allocate_output_(FLAC__StreamDecoder *decoder, unsigned size, unsigned channels); +static FLAC__bool has_id_filtered_(FLAC__StreamDecoder *decoder, FLAC__byte *id); +static FLAC__bool find_metadata_(FLAC__StreamDecoder *decoder); +static FLAC__bool read_metadata_(FLAC__StreamDecoder *decoder); +static FLAC__bool read_metadata_streaminfo_(FLAC__StreamDecoder *decoder, FLAC__bool is_last, unsigned length); +static FLAC__bool read_metadata_seektable_(FLAC__StreamDecoder *decoder, FLAC__bool is_last, unsigned length); +static FLAC__bool read_metadata_vorbiscomment_(FLAC__StreamDecoder *decoder, FLAC__StreamMetadata_VorbisComment *obj, unsigned length); +static FLAC__bool read_metadata_cuesheet_(FLAC__StreamDecoder *decoder, FLAC__StreamMetadata_CueSheet *obj); +static FLAC__bool read_metadata_picture_(FLAC__StreamDecoder *decoder, FLAC__StreamMetadata_Picture *obj); +static FLAC__bool skip_id3v2_tag_(FLAC__StreamDecoder *decoder); +static FLAC__bool frame_sync_(FLAC__StreamDecoder *decoder); +static FLAC__bool read_frame_(FLAC__StreamDecoder *decoder, FLAC__bool *got_a_frame, FLAC__bool do_full_decode); +static FLAC__bool read_frame_header_(FLAC__StreamDecoder *decoder); +static FLAC__bool read_subframe_(FLAC__StreamDecoder *decoder, unsigned channel, unsigned bps, FLAC__bool do_full_decode); +static FLAC__bool read_subframe_constant_(FLAC__StreamDecoder *decoder, unsigned channel, unsigned bps, FLAC__bool do_full_decode); +static FLAC__bool read_subframe_fixed_(FLAC__StreamDecoder *decoder, unsigned channel, unsigned bps, const unsigned order, FLAC__bool do_full_decode); +static FLAC__bool read_subframe_lpc_(FLAC__StreamDecoder *decoder, unsigned channel, unsigned bps, const unsigned order, FLAC__bool do_full_decode); +static FLAC__bool read_subframe_verbatim_(FLAC__StreamDecoder *decoder, unsigned channel, unsigned bps, FLAC__bool do_full_decode); +static FLAC__bool read_residual_partitioned_rice_(FLAC__StreamDecoder *decoder, unsigned predictor_order, unsigned partition_order, FLAC__EntropyCodingMethod_PartitionedRiceContents *partitioned_rice_contents, FLAC__int32 *residual, FLAC__bool is_extended); +static FLAC__bool read_zero_padding_(FLAC__StreamDecoder *decoder); +static FLAC__bool read_callback_(FLAC__byte buffer[], size_t *bytes, void *client_data); +#if FLAC__HAS_OGG +static FLAC__StreamDecoderReadStatus read_callback_ogg_aspect_(const FLAC__StreamDecoder *decoder, FLAC__byte buffer[], size_t *bytes); +static FLAC__OggDecoderAspectReadStatus read_callback_proxy_(const void *void_decoder, FLAC__byte buffer[], size_t *bytes, void *client_data); +#endif +static FLAC__StreamDecoderWriteStatus write_audio_frame_to_client_(FLAC__StreamDecoder *decoder, const FLAC__Frame *frame, const FLAC__int32 * const buffer[]); +static void send_error_to_client_(const FLAC__StreamDecoder *decoder, FLAC__StreamDecoderErrorStatus status); +static FLAC__bool seek_to_absolute_sample_(FLAC__StreamDecoder *decoder, FLAC__uint64 stream_length, FLAC__uint64 target_sample); +#if FLAC__HAS_OGG +static FLAC__bool seek_to_absolute_sample_ogg_(FLAC__StreamDecoder *decoder, FLAC__uint64 stream_length, FLAC__uint64 target_sample); +#endif +static FLAC__StreamDecoderReadStatus file_read_callback_(const FLAC__StreamDecoder *decoder, FLAC__byte buffer[], size_t *bytes, void *client_data); +static FLAC__StreamDecoderSeekStatus file_seek_callback_(const FLAC__StreamDecoder *decoder, FLAC__uint64 absolute_byte_offset, void *client_data); +static FLAC__StreamDecoderTellStatus file_tell_callback_(const FLAC__StreamDecoder *decoder, FLAC__uint64 *absolute_byte_offset, void *client_data); +static FLAC__StreamDecoderLengthStatus file_length_callback_(const FLAC__StreamDecoder *decoder, FLAC__uint64 *stream_length, void *client_data); +static FLAC__bool file_eof_callback_(const FLAC__StreamDecoder *decoder, void *client_data); + +/*********************************************************************** + * + * Private class data + * + ***********************************************************************/ + +typedef struct FLAC__StreamDecoderPrivate { + FLAC__bool is_ogg; + FLAC__StreamDecoderReadCallback read_callback; + FLAC__StreamDecoderSeekCallback seek_callback; + FLAC__StreamDecoderTellCallback tell_callback; + FLAC__StreamDecoderLengthCallback length_callback; + FLAC__StreamDecoderEofCallback eof_callback; + FLAC__StreamDecoderWriteCallback write_callback; + FLAC__StreamDecoderMetadataCallback metadata_callback; + FLAC__StreamDecoderErrorCallback error_callback; + /* generic 32-bit datapath: */ + void (*local_lpc_restore_signal)(const FLAC__int32 residual[], unsigned data_len, const FLAC__int32 qlp_coeff[], unsigned order, int lp_quantization, FLAC__int32 data[]); + /* generic 64-bit datapath: */ + void (*local_lpc_restore_signal_64bit)(const FLAC__int32 residual[], unsigned data_len, const FLAC__int32 qlp_coeff[], unsigned order, int lp_quantization, FLAC__int32 data[]); + /* for use when the signal is <= 16 bits-per-sample, or <= 15 bits-per-sample on a side channel (which requires 1 extra bit): */ + void (*local_lpc_restore_signal_16bit)(const FLAC__int32 residual[], unsigned data_len, const FLAC__int32 qlp_coeff[], unsigned order, int lp_quantization, FLAC__int32 data[]); + void *client_data; + FILE *file; /* only used if FLAC__stream_decoder_init_file()/FLAC__stream_decoder_init_file() called, else NULL */ + FLAC__BitReader *input; + FLAC__int32 *output[FLAC__MAX_CHANNELS]; + FLAC__int32 *residual[FLAC__MAX_CHANNELS]; /* WATCHOUT: these are the aligned pointers; the real pointers that should be free()'d are residual_unaligned[] below */ + FLAC__EntropyCodingMethod_PartitionedRiceContents partitioned_rice_contents[FLAC__MAX_CHANNELS]; + unsigned output_capacity, output_channels; + FLAC__uint32 fixed_block_size, next_fixed_block_size; + FLAC__uint64 samples_decoded; + FLAC__bool has_stream_info, has_seek_table; + FLAC__StreamMetadata stream_info; + FLAC__StreamMetadata seek_table; + FLAC__bool metadata_filter[128]; /* MAGIC number 128 == total number of metadata block types == 1 << 7 */ + FLAC__byte *metadata_filter_ids; + size_t metadata_filter_ids_count, metadata_filter_ids_capacity; /* units for both are IDs, not bytes */ + FLAC__Frame frame; + FLAC__bool cached; /* true if there is a byte in lookahead */ + FLAC__CPUInfo cpuinfo; + FLAC__byte header_warmup[2]; /* contains the sync code and reserved bits */ + FLAC__byte lookahead; /* temp storage when we need to look ahead one byte in the stream */ + /* unaligned (original) pointers to allocated data */ + FLAC__int32 *residual_unaligned[FLAC__MAX_CHANNELS]; + FLAC__bool do_md5_checking; /* initially gets protected_->md5_checking but is turned off after a seek or if the metadata has a zero MD5 */ + FLAC__bool internal_reset_hack; /* used only during init() so we can call reset to set up the decoder without rewinding the input */ + FLAC__bool is_seeking; + FLAC__MD5Context md5context; + FLAC__byte computed_md5sum[16]; /* this is the sum we computed from the decoded data */ + /* (the rest of these are only used for seeking) */ + FLAC__Frame last_frame; /* holds the info of the last frame we seeked to */ + FLAC__uint64 first_frame_offset; /* hint to the seek routine of where in the stream the first audio frame starts */ + FLAC__uint64 target_sample; + unsigned unparseable_frame_count; /* used to tell whether we're decoding a future version of FLAC or just got a bad sync */ + FLAC__bool got_a_frame; /* hack needed in Ogg FLAC seek routine to check when process_single() actually writes a frame */ +} FLAC__StreamDecoderPrivate; + +/*********************************************************************** + * + * Public static class data + * + ***********************************************************************/ + +FLAC_API const char * const FLAC__StreamDecoderStateString[] = { + "FLAC__STREAM_DECODER_SEARCH_FOR_METADATA", + "FLAC__STREAM_DECODER_READ_METADATA", + "FLAC__STREAM_DECODER_SEARCH_FOR_FRAME_SYNC", + "FLAC__STREAM_DECODER_READ_FRAME", + "FLAC__STREAM_DECODER_END_OF_STREAM", + "FLAC__STREAM_DECODER_OGG_ERROR", + "FLAC__STREAM_DECODER_SEEK_ERROR", + "FLAC__STREAM_DECODER_ABORTED", + "FLAC__STREAM_DECODER_MEMORY_ALLOCATION_ERROR", + "FLAC__STREAM_DECODER_UNINITIALIZED" +}; + +FLAC_API const char * const FLAC__StreamDecoderInitStatusString[] = { + "FLAC__STREAM_DECODER_INIT_STATUS_OK", + "FLAC__STREAM_DECODER_INIT_STATUS_UNSUPPORTED_CONTAINER", + "FLAC__STREAM_DECODER_INIT_STATUS_INVALID_CALLBACKS", + "FLAC__STREAM_DECODER_INIT_STATUS_MEMORY_ALLOCATION_ERROR", + "FLAC__STREAM_DECODER_INIT_STATUS_ERROR_OPENING_FILE", + "FLAC__STREAM_DECODER_INIT_STATUS_ALREADY_INITIALIZED" +}; + +FLAC_API const char * const FLAC__StreamDecoderReadStatusString[] = { + "FLAC__STREAM_DECODER_READ_STATUS_CONTINUE", + "FLAC__STREAM_DECODER_READ_STATUS_END_OF_STREAM", + "FLAC__STREAM_DECODER_READ_STATUS_ABORT" +}; + +FLAC_API const char * const FLAC__StreamDecoderSeekStatusString[] = { + "FLAC__STREAM_DECODER_SEEK_STATUS_OK", + "FLAC__STREAM_DECODER_SEEK_STATUS_ERROR", + "FLAC__STREAM_DECODER_SEEK_STATUS_UNSUPPORTED" +}; + +FLAC_API const char * const FLAC__StreamDecoderTellStatusString[] = { + "FLAC__STREAM_DECODER_TELL_STATUS_OK", + "FLAC__STREAM_DECODER_TELL_STATUS_ERROR", + "FLAC__STREAM_DECODER_TELL_STATUS_UNSUPPORTED" +}; + +FLAC_API const char * const FLAC__StreamDecoderLengthStatusString[] = { + "FLAC__STREAM_DECODER_LENGTH_STATUS_OK", + "FLAC__STREAM_DECODER_LENGTH_STATUS_ERROR", + "FLAC__STREAM_DECODER_LENGTH_STATUS_UNSUPPORTED" +}; + +FLAC_API const char * const FLAC__StreamDecoderWriteStatusString[] = { + "FLAC__STREAM_DECODER_WRITE_STATUS_CONTINUE", + "FLAC__STREAM_DECODER_WRITE_STATUS_ABORT" +}; + +FLAC_API const char * const FLAC__StreamDecoderErrorStatusString[] = { + "FLAC__STREAM_DECODER_ERROR_STATUS_LOST_SYNC", + "FLAC__STREAM_DECODER_ERROR_STATUS_BAD_HEADER", + "FLAC__STREAM_DECODER_ERROR_STATUS_FRAME_CRC_MISMATCH", + "FLAC__STREAM_DECODER_ERROR_STATUS_UNPARSEABLE_STREAM" +}; + +/*********************************************************************** + * + * Class constructor/destructor + * + ***********************************************************************/ +FLAC_API FLAC__StreamDecoder *FLAC__stream_decoder_new(void) +{ + FLAC__StreamDecoder *decoder; + unsigned i; + + FLAC__ASSERT(sizeof(int) >= 4); /* we want to die right away if this is not true */ + + decoder = calloc(1, sizeof(FLAC__StreamDecoder)); + if(decoder == 0) { + return 0; + } + + decoder->protected_ = calloc(1, sizeof(FLAC__StreamDecoderProtected)); + if(decoder->protected_ == 0) { + free(decoder); + return 0; + } + + decoder->private_ = calloc(1, sizeof(FLAC__StreamDecoderPrivate)); + if(decoder->private_ == 0) { + free(decoder->protected_); + free(decoder); + return 0; + } + + decoder->private_->input = FLAC__bitreader_new(); + if(decoder->private_->input == 0) { + free(decoder->private_); + free(decoder->protected_); + free(decoder); + return 0; + } + + decoder->private_->metadata_filter_ids_capacity = 16; + if(0 == (decoder->private_->metadata_filter_ids = malloc((FLAC__STREAM_METADATA_APPLICATION_ID_LEN/8) * decoder->private_->metadata_filter_ids_capacity))) { + FLAC__bitreader_delete(decoder->private_->input); + free(decoder->private_); + free(decoder->protected_); + free(decoder); + return 0; + } + + for(i = 0; i < FLAC__MAX_CHANNELS; i++) { + decoder->private_->output[i] = 0; + decoder->private_->residual_unaligned[i] = decoder->private_->residual[i] = 0; + } + + decoder->private_->output_capacity = 0; + decoder->private_->output_channels = 0; + decoder->private_->has_seek_table = false; + + for(i = 0; i < FLAC__MAX_CHANNELS; i++) + FLAC__format_entropy_coding_method_partitioned_rice_contents_init(&decoder->private_->partitioned_rice_contents[i]); + + decoder->private_->file = 0; + + set_defaults_(decoder); + + decoder->protected_->state = FLAC__STREAM_DECODER_UNINITIALIZED; + + return decoder; +} + +FLAC_API void FLAC__stream_decoder_delete(FLAC__StreamDecoder *decoder) +{ + unsigned i; + + if (decoder == NULL) + return ; + + FLAC__ASSERT(0 != decoder->protected_); + FLAC__ASSERT(0 != decoder->private_); + FLAC__ASSERT(0 != decoder->private_->input); + + (void)FLAC__stream_decoder_finish(decoder); + + if(0 != decoder->private_->metadata_filter_ids) + free(decoder->private_->metadata_filter_ids); + + FLAC__bitreader_delete(decoder->private_->input); + + for(i = 0; i < FLAC__MAX_CHANNELS; i++) + FLAC__format_entropy_coding_method_partitioned_rice_contents_clear(&decoder->private_->partitioned_rice_contents[i]); + + free(decoder->private_); + free(decoder->protected_); + free(decoder); +} + +/*********************************************************************** + * + * Public class methods + * + ***********************************************************************/ + +static FLAC__StreamDecoderInitStatus init_stream_internal_( + FLAC__StreamDecoder *decoder, + FLAC__StreamDecoderReadCallback read_callback, + FLAC__StreamDecoderSeekCallback seek_callback, + FLAC__StreamDecoderTellCallback tell_callback, + FLAC__StreamDecoderLengthCallback length_callback, + FLAC__StreamDecoderEofCallback eof_callback, + FLAC__StreamDecoderWriteCallback write_callback, + FLAC__StreamDecoderMetadataCallback metadata_callback, + FLAC__StreamDecoderErrorCallback error_callback, + void *client_data, + FLAC__bool is_ogg +) +{ + FLAC__ASSERT(0 != decoder); + + if(decoder->protected_->state != FLAC__STREAM_DECODER_UNINITIALIZED) + return FLAC__STREAM_DECODER_INIT_STATUS_ALREADY_INITIALIZED; + + if(FLAC__HAS_OGG == 0 && is_ogg) + return FLAC__STREAM_DECODER_INIT_STATUS_UNSUPPORTED_CONTAINER; + + if( + 0 == read_callback || + 0 == write_callback || + 0 == error_callback || + (seek_callback && (0 == tell_callback || 0 == length_callback || 0 == eof_callback)) + ) + return FLAC__STREAM_DECODER_INIT_STATUS_INVALID_CALLBACKS; + +#if FLAC__HAS_OGG + decoder->private_->is_ogg = is_ogg; + if(is_ogg && !FLAC__ogg_decoder_aspect_init(&decoder->protected_->ogg_decoder_aspect)) + return decoder->protected_->initstate = FLAC__STREAM_DECODER_INIT_STATUS_ERROR_OPENING_FILE; +#endif + + /* + * get the CPU info and set the function pointers + */ + FLAC__cpu_info(&decoder->private_->cpuinfo); + /* first default to the non-asm routines */ + decoder->private_->local_lpc_restore_signal = FLAC__lpc_restore_signal; + decoder->private_->local_lpc_restore_signal_64bit = FLAC__lpc_restore_signal_wide; + decoder->private_->local_lpc_restore_signal_16bit = FLAC__lpc_restore_signal; + /* now override with asm where appropriate */ +#ifndef FLAC__NO_ASM + if(decoder->private_->cpuinfo.use_asm) { +#ifdef FLAC__CPU_IA32 + FLAC__ASSERT(decoder->private_->cpuinfo.type == FLAC__CPUINFO_TYPE_IA32); +#ifdef FLAC__HAS_NASM + decoder->private_->local_lpc_restore_signal_64bit = FLAC__lpc_restore_signal_wide_asm_ia32; /* OPT_IA32: was really necessary for GCC < 4.9 */ + if(decoder->private_->cpuinfo.ia32.mmx) { + decoder->private_->local_lpc_restore_signal = FLAC__lpc_restore_signal_asm_ia32; + decoder->private_->local_lpc_restore_signal_16bit = FLAC__lpc_restore_signal_asm_ia32_mmx; + } + else { + decoder->private_->local_lpc_restore_signal = FLAC__lpc_restore_signal_asm_ia32; + decoder->private_->local_lpc_restore_signal_16bit = FLAC__lpc_restore_signal_asm_ia32; + } +#endif +#if FLAC__HAS_X86INTRIN && ! defined FLAC__INTEGER_ONLY_LIBRARY +# if defined FLAC__SSE2_SUPPORTED && !defined FLAC__HAS_NASM /* OPT_SSE: not better than MMX asm */ + if(decoder->private_->cpuinfo.ia32.sse2) { + decoder->private_->local_lpc_restore_signal_16bit = FLAC__lpc_restore_signal_16_intrin_sse2; + } +# endif +# if defined FLAC__SSE4_1_SUPPORTED + if(decoder->private_->cpuinfo.ia32.sse41) { + decoder->private_->local_lpc_restore_signal_64bit = FLAC__lpc_restore_signal_wide_intrin_sse41; + } +# endif +#endif +#elif defined FLAC__CPU_X86_64 + FLAC__ASSERT(decoder->private_->cpuinfo.type == FLAC__CPUINFO_TYPE_X86_64); + /* No useful SSE optimizations yet */ +#endif + } +#endif + + /* from here on, errors are fatal */ + + if(!FLAC__bitreader_init(decoder->private_->input, read_callback_, decoder)) { + decoder->protected_->state = FLAC__STREAM_DECODER_MEMORY_ALLOCATION_ERROR; + return FLAC__STREAM_DECODER_INIT_STATUS_MEMORY_ALLOCATION_ERROR; + } + + decoder->private_->read_callback = read_callback; + decoder->private_->seek_callback = seek_callback; + decoder->private_->tell_callback = tell_callback; + decoder->private_->length_callback = length_callback; + decoder->private_->eof_callback = eof_callback; + decoder->private_->write_callback = write_callback; + decoder->private_->metadata_callback = metadata_callback; + decoder->private_->error_callback = error_callback; + decoder->private_->client_data = client_data; + decoder->private_->fixed_block_size = decoder->private_->next_fixed_block_size = 0; + decoder->private_->samples_decoded = 0; + decoder->private_->has_stream_info = false; + decoder->private_->cached = false; + + decoder->private_->do_md5_checking = decoder->protected_->md5_checking; + decoder->private_->is_seeking = false; + + decoder->private_->internal_reset_hack = true; /* so the following reset does not try to rewind the input */ + if(!FLAC__stream_decoder_reset(decoder)) { + /* above call sets the state for us */ + return FLAC__STREAM_DECODER_INIT_STATUS_MEMORY_ALLOCATION_ERROR; + } + + return FLAC__STREAM_DECODER_INIT_STATUS_OK; +} + +FLAC_API FLAC__StreamDecoderInitStatus FLAC__stream_decoder_init_stream( + FLAC__StreamDecoder *decoder, + FLAC__StreamDecoderReadCallback read_callback, + FLAC__StreamDecoderSeekCallback seek_callback, + FLAC__StreamDecoderTellCallback tell_callback, + FLAC__StreamDecoderLengthCallback length_callback, + FLAC__StreamDecoderEofCallback eof_callback, + FLAC__StreamDecoderWriteCallback write_callback, + FLAC__StreamDecoderMetadataCallback metadata_callback, + FLAC__StreamDecoderErrorCallback error_callback, + void *client_data +) +{ + return init_stream_internal_( + decoder, + read_callback, + seek_callback, + tell_callback, + length_callback, + eof_callback, + write_callback, + metadata_callback, + error_callback, + client_data, + /*is_ogg=*/false + ); +} + +FLAC_API FLAC__StreamDecoderInitStatus FLAC__stream_decoder_init_ogg_stream( + FLAC__StreamDecoder *decoder, + FLAC__StreamDecoderReadCallback read_callback, + FLAC__StreamDecoderSeekCallback seek_callback, + FLAC__StreamDecoderTellCallback tell_callback, + FLAC__StreamDecoderLengthCallback length_callback, + FLAC__StreamDecoderEofCallback eof_callback, + FLAC__StreamDecoderWriteCallback write_callback, + FLAC__StreamDecoderMetadataCallback metadata_callback, + FLAC__StreamDecoderErrorCallback error_callback, + void *client_data +) +{ + return init_stream_internal_( + decoder, + read_callback, + seek_callback, + tell_callback, + length_callback, + eof_callback, + write_callback, + metadata_callback, + error_callback, + client_data, + /*is_ogg=*/true + ); +} + +static FLAC__StreamDecoderInitStatus init_FILE_internal_( + FLAC__StreamDecoder *decoder, + FILE *file, + FLAC__StreamDecoderWriteCallback write_callback, + FLAC__StreamDecoderMetadataCallback metadata_callback, + FLAC__StreamDecoderErrorCallback error_callback, + void *client_data, + FLAC__bool is_ogg +) +{ + FLAC__ASSERT(0 != decoder); + FLAC__ASSERT(0 != file); + + if(decoder->protected_->state != FLAC__STREAM_DECODER_UNINITIALIZED) + return decoder->protected_->initstate = FLAC__STREAM_DECODER_INIT_STATUS_ALREADY_INITIALIZED; + + if(0 == write_callback || 0 == error_callback) + return decoder->protected_->initstate = FLAC__STREAM_DECODER_INIT_STATUS_INVALID_CALLBACKS; + + /* + * To make sure that our file does not go unclosed after an error, we + * must assign the FILE pointer before any further error can occur in + * this routine. + */ + if(file == stdin) + file = get_binary_stdin_(); /* just to be safe */ + + decoder->private_->file = file; + + return init_stream_internal_( + decoder, + file_read_callback_, + decoder->private_->file == stdin? 0: file_seek_callback_, + decoder->private_->file == stdin? 0: file_tell_callback_, + decoder->private_->file == stdin? 0: file_length_callback_, + file_eof_callback_, + write_callback, + metadata_callback, + error_callback, + client_data, + is_ogg + ); +} + +FLAC_API FLAC__StreamDecoderInitStatus FLAC__stream_decoder_init_FILE( + FLAC__StreamDecoder *decoder, + FILE *file, + FLAC__StreamDecoderWriteCallback write_callback, + FLAC__StreamDecoderMetadataCallback metadata_callback, + FLAC__StreamDecoderErrorCallback error_callback, + void *client_data +) +{ + return init_FILE_internal_(decoder, file, write_callback, metadata_callback, error_callback, client_data, /*is_ogg=*/false); +} + +FLAC_API FLAC__StreamDecoderInitStatus FLAC__stream_decoder_init_ogg_FILE( + FLAC__StreamDecoder *decoder, + FILE *file, + FLAC__StreamDecoderWriteCallback write_callback, + FLAC__StreamDecoderMetadataCallback metadata_callback, + FLAC__StreamDecoderErrorCallback error_callback, + void *client_data +) +{ + return init_FILE_internal_(decoder, file, write_callback, metadata_callback, error_callback, client_data, /*is_ogg=*/true); +} + +static FLAC__StreamDecoderInitStatus init_file_internal_( + FLAC__StreamDecoder *decoder, + const char *filename, + FLAC__StreamDecoderWriteCallback write_callback, + FLAC__StreamDecoderMetadataCallback metadata_callback, + FLAC__StreamDecoderErrorCallback error_callback, + void *client_data, + FLAC__bool is_ogg +) +{ + FILE *file; + + FLAC__ASSERT(0 != decoder); + + /* + * To make sure that our file does not go unclosed after an error, we + * have to do the same entrance checks here that are later performed + * in FLAC__stream_decoder_init_FILE() before the FILE* is assigned. + */ + if(decoder->protected_->state != FLAC__STREAM_DECODER_UNINITIALIZED) + return decoder->protected_->initstate = FLAC__STREAM_DECODER_INIT_STATUS_ALREADY_INITIALIZED; + + if(0 == write_callback || 0 == error_callback) + return decoder->protected_->initstate = FLAC__STREAM_DECODER_INIT_STATUS_INVALID_CALLBACKS; + + file = filename? flac_fopen(filename, "rb") : stdin; + + if(0 == file) + return FLAC__STREAM_DECODER_INIT_STATUS_ERROR_OPENING_FILE; + + return init_FILE_internal_(decoder, file, write_callback, metadata_callback, error_callback, client_data, is_ogg); +} + +FLAC_API FLAC__StreamDecoderInitStatus FLAC__stream_decoder_init_file( + FLAC__StreamDecoder *decoder, + const char *filename, + FLAC__StreamDecoderWriteCallback write_callback, + FLAC__StreamDecoderMetadataCallback metadata_callback, + FLAC__StreamDecoderErrorCallback error_callback, + void *client_data +) +{ + return init_file_internal_(decoder, filename, write_callback, metadata_callback, error_callback, client_data, /*is_ogg=*/false); +} + +FLAC_API FLAC__StreamDecoderInitStatus FLAC__stream_decoder_init_ogg_file( + FLAC__StreamDecoder *decoder, + const char *filename, + FLAC__StreamDecoderWriteCallback write_callback, + FLAC__StreamDecoderMetadataCallback metadata_callback, + FLAC__StreamDecoderErrorCallback error_callback, + void *client_data +) +{ + return init_file_internal_(decoder, filename, write_callback, metadata_callback, error_callback, client_data, /*is_ogg=*/true); +} + +FLAC_API FLAC__bool FLAC__stream_decoder_finish(FLAC__StreamDecoder *decoder) +{ + FLAC__bool md5_failed = false; + unsigned i; + + FLAC__ASSERT(0 != decoder); + FLAC__ASSERT(0 != decoder->private_); + FLAC__ASSERT(0 != decoder->protected_); + + if(decoder->protected_->state == FLAC__STREAM_DECODER_UNINITIALIZED) + return true; + + /* see the comment in FLAC__stream_decoder_reset() as to why we + * always call FLAC__MD5Final() + */ + FLAC__MD5Final(decoder->private_->computed_md5sum, &decoder->private_->md5context); + + free(decoder->private_->seek_table.data.seek_table.points); + decoder->private_->seek_table.data.seek_table.points = 0; + decoder->private_->has_seek_table = false; + + FLAC__bitreader_free(decoder->private_->input); + for(i = 0; i < FLAC__MAX_CHANNELS; i++) { + /* WATCHOUT: + * FLAC__lpc_restore_signal_asm_ia32_mmx() requires that the + * output arrays have a buffer of up to 3 zeroes in front + * (at negative indices) for alignment purposes; we use 4 + * to keep the data well-aligned. + */ + if(0 != decoder->private_->output[i]) { + free(decoder->private_->output[i]-4); + decoder->private_->output[i] = 0; + } + if(0 != decoder->private_->residual_unaligned[i]) { + free(decoder->private_->residual_unaligned[i]); + decoder->private_->residual_unaligned[i] = decoder->private_->residual[i] = 0; + } + } + decoder->private_->output_capacity = 0; + decoder->private_->output_channels = 0; + +#if FLAC__HAS_OGG + if(decoder->private_->is_ogg) + FLAC__ogg_decoder_aspect_finish(&decoder->protected_->ogg_decoder_aspect); +#endif + + if(0 != decoder->private_->file) { + if(decoder->private_->file != stdin) + fclose(decoder->private_->file); + decoder->private_->file = 0; + } + + if(decoder->private_->do_md5_checking) { + if(memcmp(decoder->private_->stream_info.data.stream_info.md5sum, decoder->private_->computed_md5sum, 16)) + md5_failed = true; + } + decoder->private_->is_seeking = false; + + set_defaults_(decoder); + + decoder->protected_->state = FLAC__STREAM_DECODER_UNINITIALIZED; + + return !md5_failed; +} + +FLAC_API FLAC__bool FLAC__stream_decoder_set_ogg_serial_number(FLAC__StreamDecoder *decoder, long value) +{ + FLAC__ASSERT(0 != decoder); + FLAC__ASSERT(0 != decoder->private_); + FLAC__ASSERT(0 != decoder->protected_); + if(decoder->protected_->state != FLAC__STREAM_DECODER_UNINITIALIZED) + return false; +#if FLAC__HAS_OGG + /* can't check decoder->private_->is_ogg since that's not set until init time */ + FLAC__ogg_decoder_aspect_set_serial_number(&decoder->protected_->ogg_decoder_aspect, value); + return true; +#else + (void)value; + return false; +#endif +} + +FLAC_API FLAC__bool FLAC__stream_decoder_set_md5_checking(FLAC__StreamDecoder *decoder, FLAC__bool value) +{ + FLAC__ASSERT(0 != decoder); + FLAC__ASSERT(0 != decoder->protected_); + if(decoder->protected_->state != FLAC__STREAM_DECODER_UNINITIALIZED) + return false; + decoder->protected_->md5_checking = value; + return true; +} + +FLAC_API FLAC__bool FLAC__stream_decoder_set_metadata_respond(FLAC__StreamDecoder *decoder, FLAC__MetadataType type) +{ + FLAC__ASSERT(0 != decoder); + FLAC__ASSERT(0 != decoder->private_); + FLAC__ASSERT(0 != decoder->protected_); + FLAC__ASSERT((unsigned)type <= FLAC__MAX_METADATA_TYPE_CODE); + /* double protection */ + if((unsigned)type > FLAC__MAX_METADATA_TYPE_CODE) + return false; + if(decoder->protected_->state != FLAC__STREAM_DECODER_UNINITIALIZED) + return false; + decoder->private_->metadata_filter[type] = true; + if(type == FLAC__METADATA_TYPE_APPLICATION) + decoder->private_->metadata_filter_ids_count = 0; + return true; +} + +FLAC_API FLAC__bool FLAC__stream_decoder_set_metadata_respond_application(FLAC__StreamDecoder *decoder, const FLAC__byte id[4]) +{ + FLAC__ASSERT(0 != decoder); + FLAC__ASSERT(0 != decoder->private_); + FLAC__ASSERT(0 != decoder->protected_); + FLAC__ASSERT(0 != id); + if(decoder->protected_->state != FLAC__STREAM_DECODER_UNINITIALIZED) + return false; + + if(decoder->private_->metadata_filter[FLAC__METADATA_TYPE_APPLICATION]) + return true; + + FLAC__ASSERT(0 != decoder->private_->metadata_filter_ids); + + if(decoder->private_->metadata_filter_ids_count == decoder->private_->metadata_filter_ids_capacity) { + if(0 == (decoder->private_->metadata_filter_ids = safe_realloc_mul_2op_(decoder->private_->metadata_filter_ids, decoder->private_->metadata_filter_ids_capacity, /*times*/2))) { + decoder->protected_->state = FLAC__STREAM_DECODER_MEMORY_ALLOCATION_ERROR; + return false; + } + decoder->private_->metadata_filter_ids_capacity *= 2; + } + + memcpy(decoder->private_->metadata_filter_ids + decoder->private_->metadata_filter_ids_count * (FLAC__STREAM_METADATA_APPLICATION_ID_LEN/8), id, (FLAC__STREAM_METADATA_APPLICATION_ID_LEN/8)); + decoder->private_->metadata_filter_ids_count++; + + return true; +} + +FLAC_API FLAC__bool FLAC__stream_decoder_set_metadata_respond_all(FLAC__StreamDecoder *decoder) +{ + unsigned i; + FLAC__ASSERT(0 != decoder); + FLAC__ASSERT(0 != decoder->private_); + FLAC__ASSERT(0 != decoder->protected_); + if(decoder->protected_->state != FLAC__STREAM_DECODER_UNINITIALIZED) + return false; + for(i = 0; i < sizeof(decoder->private_->metadata_filter) / sizeof(decoder->private_->metadata_filter[0]); i++) + decoder->private_->metadata_filter[i] = true; + decoder->private_->metadata_filter_ids_count = 0; + return true; +} + +FLAC_API FLAC__bool FLAC__stream_decoder_set_metadata_ignore(FLAC__StreamDecoder *decoder, FLAC__MetadataType type) +{ + FLAC__ASSERT(0 != decoder); + FLAC__ASSERT(0 != decoder->private_); + FLAC__ASSERT(0 != decoder->protected_); + FLAC__ASSERT((unsigned)type <= FLAC__MAX_METADATA_TYPE_CODE); + /* double protection */ + if((unsigned)type > FLAC__MAX_METADATA_TYPE_CODE) + return false; + if(decoder->protected_->state != FLAC__STREAM_DECODER_UNINITIALIZED) + return false; + decoder->private_->metadata_filter[type] = false; + if(type == FLAC__METADATA_TYPE_APPLICATION) + decoder->private_->metadata_filter_ids_count = 0; + return true; +} + +FLAC_API FLAC__bool FLAC__stream_decoder_set_metadata_ignore_application(FLAC__StreamDecoder *decoder, const FLAC__byte id[4]) +{ + FLAC__ASSERT(0 != decoder); + FLAC__ASSERT(0 != decoder->private_); + FLAC__ASSERT(0 != decoder->protected_); + FLAC__ASSERT(0 != id); + if(decoder->protected_->state != FLAC__STREAM_DECODER_UNINITIALIZED) + return false; + + if(!decoder->private_->metadata_filter[FLAC__METADATA_TYPE_APPLICATION]) + return true; + + FLAC__ASSERT(0 != decoder->private_->metadata_filter_ids); + + if(decoder->private_->metadata_filter_ids_count == decoder->private_->metadata_filter_ids_capacity) { + if(0 == (decoder->private_->metadata_filter_ids = safe_realloc_mul_2op_(decoder->private_->metadata_filter_ids, decoder->private_->metadata_filter_ids_capacity, /*times*/2))) { + decoder->protected_->state = FLAC__STREAM_DECODER_MEMORY_ALLOCATION_ERROR; + return false; + } + decoder->private_->metadata_filter_ids_capacity *= 2; + } + + memcpy(decoder->private_->metadata_filter_ids + decoder->private_->metadata_filter_ids_count * (FLAC__STREAM_METADATA_APPLICATION_ID_LEN/8), id, (FLAC__STREAM_METADATA_APPLICATION_ID_LEN/8)); + decoder->private_->metadata_filter_ids_count++; + + return true; +} + +FLAC_API FLAC__bool FLAC__stream_decoder_set_metadata_ignore_all(FLAC__StreamDecoder *decoder) +{ + FLAC__ASSERT(0 != decoder); + FLAC__ASSERT(0 != decoder->private_); + FLAC__ASSERT(0 != decoder->protected_); + if(decoder->protected_->state != FLAC__STREAM_DECODER_UNINITIALIZED) + return false; + memset(decoder->private_->metadata_filter, 0, sizeof(decoder->private_->metadata_filter)); + decoder->private_->metadata_filter_ids_count = 0; + return true; +} + +FLAC_API FLAC__StreamDecoderState FLAC__stream_decoder_get_state(const FLAC__StreamDecoder *decoder) +{ + FLAC__ASSERT(0 != decoder); + FLAC__ASSERT(0 != decoder->protected_); + return decoder->protected_->state; +} + +FLAC_API const char *FLAC__stream_decoder_get_resolved_state_string(const FLAC__StreamDecoder *decoder) +{ + return FLAC__StreamDecoderStateString[decoder->protected_->state]; +} + +FLAC_API FLAC__bool FLAC__stream_decoder_get_md5_checking(const FLAC__StreamDecoder *decoder) +{ + FLAC__ASSERT(0 != decoder); + FLAC__ASSERT(0 != decoder->protected_); + return decoder->protected_->md5_checking; +} + +FLAC_API FLAC__uint64 FLAC__stream_decoder_get_total_samples(const FLAC__StreamDecoder *decoder) +{ + FLAC__ASSERT(0 != decoder); + FLAC__ASSERT(0 != decoder->protected_); + return decoder->private_->has_stream_info? decoder->private_->stream_info.data.stream_info.total_samples : 0; +} + +FLAC_API unsigned FLAC__stream_decoder_get_channels(const FLAC__StreamDecoder *decoder) +{ + FLAC__ASSERT(0 != decoder); + FLAC__ASSERT(0 != decoder->protected_); + return decoder->protected_->channels; +} + +FLAC_API FLAC__ChannelAssignment FLAC__stream_decoder_get_channel_assignment(const FLAC__StreamDecoder *decoder) +{ + FLAC__ASSERT(0 != decoder); + FLAC__ASSERT(0 != decoder->protected_); + return decoder->protected_->channel_assignment; +} + +FLAC_API unsigned FLAC__stream_decoder_get_bits_per_sample(const FLAC__StreamDecoder *decoder) +{ + FLAC__ASSERT(0 != decoder); + FLAC__ASSERT(0 != decoder->protected_); + return decoder->protected_->bits_per_sample; +} + +FLAC_API unsigned FLAC__stream_decoder_get_sample_rate(const FLAC__StreamDecoder *decoder) +{ + FLAC__ASSERT(0 != decoder); + FLAC__ASSERT(0 != decoder->protected_); + return decoder->protected_->sample_rate; +} + +FLAC_API unsigned FLAC__stream_decoder_get_blocksize(const FLAC__StreamDecoder *decoder) +{ + FLAC__ASSERT(0 != decoder); + FLAC__ASSERT(0 != decoder->protected_); + return decoder->protected_->blocksize; +} + +FLAC_API FLAC__bool FLAC__stream_decoder_get_decode_position(const FLAC__StreamDecoder *decoder, FLAC__uint64 *position) +{ + FLAC__ASSERT(0 != decoder); + FLAC__ASSERT(0 != decoder->private_); + FLAC__ASSERT(0 != position); + + if(FLAC__HAS_OGG && decoder->private_->is_ogg) + return false; + + if(0 == decoder->private_->tell_callback) + return false; + if(decoder->private_->tell_callback(decoder, position, decoder->private_->client_data) != FLAC__STREAM_DECODER_TELL_STATUS_OK) + return false; + /* should never happen since all FLAC frames and metadata blocks are byte aligned, but check just in case */ + if(!FLAC__bitreader_is_consumed_byte_aligned(decoder->private_->input)) + return false; + FLAC__ASSERT(*position >= FLAC__stream_decoder_get_input_bytes_unconsumed(decoder)); + *position -= FLAC__stream_decoder_get_input_bytes_unconsumed(decoder); + return true; +} + +FLAC_API FLAC__bool FLAC__stream_decoder_flush(FLAC__StreamDecoder *decoder) +{ + FLAC__ASSERT(0 != decoder); + FLAC__ASSERT(0 != decoder->private_); + FLAC__ASSERT(0 != decoder->protected_); + + if(!decoder->private_->internal_reset_hack && decoder->protected_->state == FLAC__STREAM_DECODER_UNINITIALIZED) + return false; + + decoder->private_->samples_decoded = 0; + decoder->private_->do_md5_checking = false; + +#if FLAC__HAS_OGG + if(decoder->private_->is_ogg) + FLAC__ogg_decoder_aspect_flush(&decoder->protected_->ogg_decoder_aspect); +#endif + + if(!FLAC__bitreader_clear(decoder->private_->input)) { + decoder->protected_->state = FLAC__STREAM_DECODER_MEMORY_ALLOCATION_ERROR; + return false; + } + decoder->protected_->state = FLAC__STREAM_DECODER_SEARCH_FOR_FRAME_SYNC; + + return true; +} + +FLAC_API FLAC__bool FLAC__stream_decoder_reset(FLAC__StreamDecoder *decoder) +{ + FLAC__ASSERT(0 != decoder); + FLAC__ASSERT(0 != decoder->private_); + FLAC__ASSERT(0 != decoder->protected_); + + if(!FLAC__stream_decoder_flush(decoder)) { + /* above call sets the state for us */ + return false; + } + +#if FLAC__HAS_OGG + /*@@@ could go in !internal_reset_hack block below */ + if(decoder->private_->is_ogg) + FLAC__ogg_decoder_aspect_reset(&decoder->protected_->ogg_decoder_aspect); +#endif + + /* Rewind if necessary. If FLAC__stream_decoder_init() is calling us, + * (internal_reset_hack) don't try to rewind since we are already at + * the beginning of the stream and don't want to fail if the input is + * not seekable. + */ + if(!decoder->private_->internal_reset_hack) { + if(decoder->private_->file == stdin) + return false; /* can't rewind stdin, reset fails */ + if(decoder->private_->seek_callback && decoder->private_->seek_callback(decoder, 0, decoder->private_->client_data) == FLAC__STREAM_DECODER_SEEK_STATUS_ERROR) + return false; /* seekable and seek fails, reset fails */ + } + else + decoder->private_->internal_reset_hack = false; + + decoder->protected_->state = FLAC__STREAM_DECODER_SEARCH_FOR_METADATA; + + decoder->private_->has_stream_info = false; + + free(decoder->private_->seek_table.data.seek_table.points); + decoder->private_->seek_table.data.seek_table.points = 0; + decoder->private_->has_seek_table = false; + + decoder->private_->do_md5_checking = decoder->protected_->md5_checking; + /* + * This goes in reset() and not flush() because according to the spec, a + * fixed-blocksize stream must stay that way through the whole stream. + */ + decoder->private_->fixed_block_size = decoder->private_->next_fixed_block_size = 0; + + /* We initialize the FLAC__MD5Context even though we may never use it. This + * is because md5 checking may be turned on to start and then turned off if + * a seek occurs. So we init the context here and finalize it in + * FLAC__stream_decoder_finish() to make sure things are always cleaned up + * properly. + */ + FLAC__MD5Init(&decoder->private_->md5context); + + decoder->private_->first_frame_offset = 0; + decoder->private_->unparseable_frame_count = 0; + + return true; +} + +FLAC_API FLAC__bool FLAC__stream_decoder_process_single(FLAC__StreamDecoder *decoder) +{ + FLAC__bool got_a_frame; + FLAC__ASSERT(0 != decoder); + FLAC__ASSERT(0 != decoder->protected_); + + while(1) { + switch(decoder->protected_->state) { + case FLAC__STREAM_DECODER_SEARCH_FOR_METADATA: + if(!find_metadata_(decoder)) + return false; /* above function sets the status for us */ + break; + case FLAC__STREAM_DECODER_READ_METADATA: + if(!read_metadata_(decoder)) + return false; /* above function sets the status for us */ + else + return true; + case FLAC__STREAM_DECODER_SEARCH_FOR_FRAME_SYNC: + if(!frame_sync_(decoder)) + return true; /* above function sets the status for us */ + break; + case FLAC__STREAM_DECODER_READ_FRAME: + if(!read_frame_(decoder, &got_a_frame, /*do_full_decode=*/true)) + return false; /* above function sets the status for us */ + if(got_a_frame) + return true; /* above function sets the status for us */ + break; + case FLAC__STREAM_DECODER_END_OF_STREAM: + case FLAC__STREAM_DECODER_ABORTED: + return true; + default: + FLAC__ASSERT(0); + return false; + } + } +} + +FLAC_API FLAC__bool FLAC__stream_decoder_process_until_end_of_metadata(FLAC__StreamDecoder *decoder) +{ + FLAC__ASSERT(0 != decoder); + FLAC__ASSERT(0 != decoder->protected_); + + while(1) { + switch(decoder->protected_->state) { + case FLAC__STREAM_DECODER_SEARCH_FOR_METADATA: + if(!find_metadata_(decoder)) + return false; /* above function sets the status for us */ + break; + case FLAC__STREAM_DECODER_READ_METADATA: + if(!read_metadata_(decoder)) + return false; /* above function sets the status for us */ + break; + case FLAC__STREAM_DECODER_SEARCH_FOR_FRAME_SYNC: + case FLAC__STREAM_DECODER_READ_FRAME: + case FLAC__STREAM_DECODER_END_OF_STREAM: + case FLAC__STREAM_DECODER_ABORTED: + return true; + default: + FLAC__ASSERT(0); + return false; + } + } +} + +FLAC_API FLAC__bool FLAC__stream_decoder_process_until_end_of_stream(FLAC__StreamDecoder *decoder) +{ + FLAC__bool dummy; + FLAC__ASSERT(0 != decoder); + FLAC__ASSERT(0 != decoder->protected_); + + while(1) { + switch(decoder->protected_->state) { + case FLAC__STREAM_DECODER_SEARCH_FOR_METADATA: + if(!find_metadata_(decoder)) + return false; /* above function sets the status for us */ + break; + case FLAC__STREAM_DECODER_READ_METADATA: + if(!read_metadata_(decoder)) + return false; /* above function sets the status for us */ + break; + case FLAC__STREAM_DECODER_SEARCH_FOR_FRAME_SYNC: + if(!frame_sync_(decoder)) + return true; /* above function sets the status for us */ + break; + case FLAC__STREAM_DECODER_READ_FRAME: + if(!read_frame_(decoder, &dummy, /*do_full_decode=*/true)) + return false; /* above function sets the status for us */ + break; + case FLAC__STREAM_DECODER_END_OF_STREAM: + case FLAC__STREAM_DECODER_ABORTED: + return true; + default: + FLAC__ASSERT(0); + return false; + } + } +} + +FLAC_API FLAC__bool FLAC__stream_decoder_skip_single_frame(FLAC__StreamDecoder *decoder) +{ + FLAC__bool got_a_frame; + FLAC__ASSERT(0 != decoder); + FLAC__ASSERT(0 != decoder->protected_); + + while(1) { + switch(decoder->protected_->state) { + case FLAC__STREAM_DECODER_SEARCH_FOR_METADATA: + case FLAC__STREAM_DECODER_READ_METADATA: + return false; /* above function sets the status for us */ + case FLAC__STREAM_DECODER_SEARCH_FOR_FRAME_SYNC: + if(!frame_sync_(decoder)) + return true; /* above function sets the status for us */ + break; + case FLAC__STREAM_DECODER_READ_FRAME: + if(!read_frame_(decoder, &got_a_frame, /*do_full_decode=*/false)) + return false; /* above function sets the status for us */ + if(got_a_frame) + return true; /* above function sets the status for us */ + break; + case FLAC__STREAM_DECODER_END_OF_STREAM: + case FLAC__STREAM_DECODER_ABORTED: + return true; + default: + FLAC__ASSERT(0); + return false; + } + } +} + +FLAC_API FLAC__bool FLAC__stream_decoder_seek_absolute(FLAC__StreamDecoder *decoder, FLAC__uint64 sample) +{ + FLAC__uint64 length; + + FLAC__ASSERT(0 != decoder); + + if( + decoder->protected_->state != FLAC__STREAM_DECODER_SEARCH_FOR_METADATA && + decoder->protected_->state != FLAC__STREAM_DECODER_READ_METADATA && + decoder->protected_->state != FLAC__STREAM_DECODER_SEARCH_FOR_FRAME_SYNC && + decoder->protected_->state != FLAC__STREAM_DECODER_READ_FRAME && + decoder->protected_->state != FLAC__STREAM_DECODER_END_OF_STREAM + ) + return false; + + if(0 == decoder->private_->seek_callback) + return false; + + FLAC__ASSERT(decoder->private_->seek_callback); + FLAC__ASSERT(decoder->private_->tell_callback); + FLAC__ASSERT(decoder->private_->length_callback); + FLAC__ASSERT(decoder->private_->eof_callback); + + if(FLAC__stream_decoder_get_total_samples(decoder) > 0 && sample >= FLAC__stream_decoder_get_total_samples(decoder)) + return false; + + decoder->private_->is_seeking = true; + + /* turn off md5 checking if a seek is attempted */ + decoder->private_->do_md5_checking = false; + + /* get the file length (currently our algorithm needs to know the length so it's also an error to get FLAC__STREAM_DECODER_LENGTH_STATUS_UNSUPPORTED) */ + if(decoder->private_->length_callback(decoder, &length, decoder->private_->client_data) != FLAC__STREAM_DECODER_LENGTH_STATUS_OK) { + decoder->private_->is_seeking = false; + return false; + } + + /* if we haven't finished processing the metadata yet, do that so we have the STREAMINFO, SEEK_TABLE, and first_frame_offset */ + if( + decoder->protected_->state == FLAC__STREAM_DECODER_SEARCH_FOR_METADATA || + decoder->protected_->state == FLAC__STREAM_DECODER_READ_METADATA + ) { + if(!FLAC__stream_decoder_process_until_end_of_metadata(decoder)) { + /* above call sets the state for us */ + decoder->private_->is_seeking = false; + return false; + } + /* check this again in case we didn't know total_samples the first time */ + if(FLAC__stream_decoder_get_total_samples(decoder) > 0 && sample >= FLAC__stream_decoder_get_total_samples(decoder)) { + decoder->private_->is_seeking = false; + return false; + } + } + + { + const FLAC__bool ok = +#if FLAC__HAS_OGG + decoder->private_->is_ogg? + seek_to_absolute_sample_ogg_(decoder, length, sample) : +#endif + seek_to_absolute_sample_(decoder, length, sample) + ; + decoder->private_->is_seeking = false; + return ok; + } +} + +/*********************************************************************** + * + * Protected class methods + * + ***********************************************************************/ + +unsigned FLAC__stream_decoder_get_input_bytes_unconsumed(const FLAC__StreamDecoder *decoder) +{ + FLAC__ASSERT(0 != decoder); + FLAC__ASSERT(FLAC__bitreader_is_consumed_byte_aligned(decoder->private_->input)); + FLAC__ASSERT(!(FLAC__bitreader_get_input_bits_unconsumed(decoder->private_->input) & 7)); + return FLAC__bitreader_get_input_bits_unconsumed(decoder->private_->input) / 8; +} + +/*********************************************************************** + * + * Private class methods + * + ***********************************************************************/ + +void set_defaults_(FLAC__StreamDecoder *decoder) +{ + decoder->private_->is_ogg = false; + decoder->private_->read_callback = 0; + decoder->private_->seek_callback = 0; + decoder->private_->tell_callback = 0; + decoder->private_->length_callback = 0; + decoder->private_->eof_callback = 0; + decoder->private_->write_callback = 0; + decoder->private_->metadata_callback = 0; + decoder->private_->error_callback = 0; + decoder->private_->client_data = 0; + + memset(decoder->private_->metadata_filter, 0, sizeof(decoder->private_->metadata_filter)); + decoder->private_->metadata_filter[FLAC__METADATA_TYPE_STREAMINFO] = true; + decoder->private_->metadata_filter_ids_count = 0; + + decoder->protected_->md5_checking = false; + +#if FLAC__HAS_OGG + FLAC__ogg_decoder_aspect_set_defaults(&decoder->protected_->ogg_decoder_aspect); +#endif +} + +/* + * This will forcibly set stdin to binary mode (for OSes that require it) + */ +FILE *get_binary_stdin_(void) +{ + /* if something breaks here it is probably due to the presence or + * absence of an underscore before the identifiers 'setmode', + * 'fileno', and/or 'O_BINARY'; check your system header files. + */ +#if defined _MSC_VER || defined __MINGW32__ + _setmode(_fileno(stdin), _O_BINARY); +#elif defined __CYGWIN__ + /* almost certainly not needed for any modern Cygwin, but let's be safe... */ + setmode(_fileno(stdin), _O_BINARY); +#elif defined __EMX__ + setmode(fileno(stdin), O_BINARY); +#endif + + return stdin; +} + +FLAC__bool allocate_output_(FLAC__StreamDecoder *decoder, unsigned size, unsigned channels) +{ + unsigned i; + FLAC__int32 *tmp; + + if(size <= decoder->private_->output_capacity && channels <= decoder->private_->output_channels) + return true; + + /* simply using realloc() is not practical because the number of channels may change mid-stream */ + + for(i = 0; i < FLAC__MAX_CHANNELS; i++) { + if(0 != decoder->private_->output[i]) { + free(decoder->private_->output[i]-4); + decoder->private_->output[i] = 0; + } + if(0 != decoder->private_->residual_unaligned[i]) { + free(decoder->private_->residual_unaligned[i]); + decoder->private_->residual_unaligned[i] = decoder->private_->residual[i] = 0; + } + } + + for(i = 0; i < channels; i++) { + /* WATCHOUT: + * FLAC__lpc_restore_signal_asm_ia32_mmx() requires that the + * output arrays have a buffer of up to 3 zeroes in front + * (at negative indices) for alignment purposes; we use 4 + * to keep the data well-aligned. + */ + tmp = safe_malloc_muladd2_(sizeof(FLAC__int32), /*times (*/size, /*+*/4/*)*/); + if(tmp == 0) { + decoder->protected_->state = FLAC__STREAM_DECODER_MEMORY_ALLOCATION_ERROR; + return false; + } + memset(tmp, 0, sizeof(FLAC__int32)*4); + decoder->private_->output[i] = tmp + 4; + + if(!FLAC__memory_alloc_aligned_int32_array(size, &decoder->private_->residual_unaligned[i], &decoder->private_->residual[i])) { + decoder->protected_->state = FLAC__STREAM_DECODER_MEMORY_ALLOCATION_ERROR; + return false; + } + } + + decoder->private_->output_capacity = size; + decoder->private_->output_channels = channels; + + return true; +} + +FLAC__bool has_id_filtered_(FLAC__StreamDecoder *decoder, FLAC__byte *id) +{ + size_t i; + + FLAC__ASSERT(0 != decoder); + FLAC__ASSERT(0 != decoder->private_); + + for(i = 0; i < decoder->private_->metadata_filter_ids_count; i++) + if(0 == memcmp(decoder->private_->metadata_filter_ids + i * (FLAC__STREAM_METADATA_APPLICATION_ID_LEN/8), id, (FLAC__STREAM_METADATA_APPLICATION_ID_LEN/8))) + return true; + + return false; +} + +FLAC__bool find_metadata_(FLAC__StreamDecoder *decoder) +{ + FLAC__uint32 x; + unsigned i, id; + FLAC__bool first = true; + + FLAC__ASSERT(FLAC__bitreader_is_consumed_byte_aligned(decoder->private_->input)); + + for(i = id = 0; i < 4; ) { + if(decoder->private_->cached) { + x = (FLAC__uint32)decoder->private_->lookahead; + decoder->private_->cached = false; + } + else { + if(!FLAC__bitreader_read_raw_uint32(decoder->private_->input, &x, 8)) + return false; /* read_callback_ sets the state for us */ + } + if(x == FLAC__STREAM_SYNC_STRING[i]) { + first = true; + i++; + id = 0; + continue; + } + + if(id >= 3) + return false; + + if(x == ID3V2_TAG_[id]) { + id++; + i = 0; + if(id == 3) { + if(!skip_id3v2_tag_(decoder)) + return false; /* skip_id3v2_tag_ sets the state for us */ + } + continue; + } + id = 0; + if(x == 0xff) { /* MAGIC NUMBER for the first 8 frame sync bits */ + decoder->private_->header_warmup[0] = (FLAC__byte)x; + if(!FLAC__bitreader_read_raw_uint32(decoder->private_->input, &x, 8)) + return false; /* read_callback_ sets the state for us */ + + /* we have to check if we just read two 0xff's in a row; the second may actually be the beginning of the sync code */ + /* else we have to check if the second byte is the end of a sync code */ + if(x == 0xff) { /* MAGIC NUMBER for the first 8 frame sync bits */ + decoder->private_->lookahead = (FLAC__byte)x; + decoder->private_->cached = true; + } + else if(x >> 1 == 0x7c) { /* MAGIC NUMBER for the last 6 sync bits and reserved 7th bit */ + decoder->private_->header_warmup[1] = (FLAC__byte)x; + decoder->protected_->state = FLAC__STREAM_DECODER_READ_FRAME; + return true; + } + } + i = 0; + if(first) { + send_error_to_client_(decoder, FLAC__STREAM_DECODER_ERROR_STATUS_LOST_SYNC); + first = false; + } + } + + decoder->protected_->state = FLAC__STREAM_DECODER_READ_METADATA; + return true; +} + +FLAC__bool read_metadata_(FLAC__StreamDecoder *decoder) +{ + FLAC__bool is_last; + FLAC__uint32 i, x, type, length; + + FLAC__ASSERT(FLAC__bitreader_is_consumed_byte_aligned(decoder->private_->input)); + + if(!FLAC__bitreader_read_raw_uint32(decoder->private_->input, &x, FLAC__STREAM_METADATA_IS_LAST_LEN)) + return false; /* read_callback_ sets the state for us */ + is_last = x? true : false; + + if(!FLAC__bitreader_read_raw_uint32(decoder->private_->input, &type, FLAC__STREAM_METADATA_TYPE_LEN)) + return false; /* read_callback_ sets the state for us */ + + if(!FLAC__bitreader_read_raw_uint32(decoder->private_->input, &length, FLAC__STREAM_METADATA_LENGTH_LEN)) + return false; /* read_callback_ sets the state for us */ + + if(type == FLAC__METADATA_TYPE_STREAMINFO) { + if(!read_metadata_streaminfo_(decoder, is_last, length)) + return false; + + decoder->private_->has_stream_info = true; + if(0 == memcmp(decoder->private_->stream_info.data.stream_info.md5sum, "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0", 16)) + decoder->private_->do_md5_checking = false; + if(!decoder->private_->is_seeking && decoder->private_->metadata_filter[FLAC__METADATA_TYPE_STREAMINFO] && decoder->private_->metadata_callback) + decoder->private_->metadata_callback(decoder, &decoder->private_->stream_info, decoder->private_->client_data); + } + else if(type == FLAC__METADATA_TYPE_SEEKTABLE) { + /* just in case we already have a seek table, and reading the next one fails: */ + decoder->private_->has_seek_table = false; + + if(!read_metadata_seektable_(decoder, is_last, length)) + return false; + + decoder->private_->has_seek_table = true; + if(!decoder->private_->is_seeking && decoder->private_->metadata_filter[FLAC__METADATA_TYPE_SEEKTABLE] && decoder->private_->metadata_callback) + decoder->private_->metadata_callback(decoder, &decoder->private_->seek_table, decoder->private_->client_data); + } + else { + FLAC__bool skip_it = !decoder->private_->metadata_filter[type]; + unsigned real_length = length; + FLAC__StreamMetadata block; + + memset(&block, 0, sizeof(block)); + block.is_last = is_last; + block.type = (FLAC__MetadataType)type; + block.length = length; + + if(type == FLAC__METADATA_TYPE_APPLICATION) { + if(!FLAC__bitreader_read_byte_block_aligned_no_crc(decoder->private_->input, block.data.application.id, FLAC__STREAM_METADATA_APPLICATION_ID_LEN/8)) + return false; /* read_callback_ sets the state for us */ + + if(real_length < FLAC__STREAM_METADATA_APPLICATION_ID_LEN/8) { /* underflow check */ + decoder->protected_->state = FLAC__STREAM_DECODER_MEMORY_ALLOCATION_ERROR;/*@@@@@@ maybe wrong error? need to resync?*/ + return false; + } + + real_length -= FLAC__STREAM_METADATA_APPLICATION_ID_LEN/8; + + if(decoder->private_->metadata_filter_ids_count > 0 && has_id_filtered_(decoder, block.data.application.id)) + skip_it = !skip_it; + } + + if(skip_it) { + if(!FLAC__bitreader_skip_byte_block_aligned_no_crc(decoder->private_->input, real_length)) + return false; /* read_callback_ sets the state for us */ + } + else { + FLAC__bool ok = true; + switch(type) { + case FLAC__METADATA_TYPE_PADDING: + /* skip the padding bytes */ + if(!FLAC__bitreader_skip_byte_block_aligned_no_crc(decoder->private_->input, real_length)) + ok = false; /* read_callback_ sets the state for us */ + break; + case FLAC__METADATA_TYPE_APPLICATION: + /* remember, we read the ID already */ + if(real_length > 0) { + if(0 == (block.data.application.data = malloc(real_length))) { + decoder->protected_->state = FLAC__STREAM_DECODER_MEMORY_ALLOCATION_ERROR; + ok = false; + } + else if(!FLAC__bitreader_read_byte_block_aligned_no_crc(decoder->private_->input, block.data.application.data, real_length)) + ok = false; /* read_callback_ sets the state for us */ + } + else + block.data.application.data = 0; + break; + case FLAC__METADATA_TYPE_VORBIS_COMMENT: + if(!read_metadata_vorbiscomment_(decoder, &block.data.vorbis_comment, real_length)) + ok = false; + break; + case FLAC__METADATA_TYPE_CUESHEET: + if(!read_metadata_cuesheet_(decoder, &block.data.cue_sheet)) + ok = false; + break; + case FLAC__METADATA_TYPE_PICTURE: + if(!read_metadata_picture_(decoder, &block.data.picture)) + ok = false; + break; + case FLAC__METADATA_TYPE_STREAMINFO: + case FLAC__METADATA_TYPE_SEEKTABLE: + FLAC__ASSERT(0); + break; + default: + if(real_length > 0) { + if(0 == (block.data.unknown.data = malloc(real_length))) { + decoder->protected_->state = FLAC__STREAM_DECODER_MEMORY_ALLOCATION_ERROR; + ok = false; + } + else if(!FLAC__bitreader_read_byte_block_aligned_no_crc(decoder->private_->input, block.data.unknown.data, real_length)) + ok = false; /* read_callback_ sets the state for us */ + } + else + block.data.unknown.data = 0; + break; + } + if(ok && !decoder->private_->is_seeking && decoder->private_->metadata_callback) + decoder->private_->metadata_callback(decoder, &block, decoder->private_->client_data); + + /* now we have to free any malloc()ed data in the block */ + switch(type) { + case FLAC__METADATA_TYPE_PADDING: + break; + case FLAC__METADATA_TYPE_APPLICATION: + if(0 != block.data.application.data) + free(block.data.application.data); + break; + case FLAC__METADATA_TYPE_VORBIS_COMMENT: + if(0 != block.data.vorbis_comment.vendor_string.entry) + free(block.data.vorbis_comment.vendor_string.entry); + if(block.data.vorbis_comment.num_comments > 0) + for(i = 0; i < block.data.vorbis_comment.num_comments; i++) + if(0 != block.data.vorbis_comment.comments[i].entry) + free(block.data.vorbis_comment.comments[i].entry); + if(0 != block.data.vorbis_comment.comments) + free(block.data.vorbis_comment.comments); + break; + case FLAC__METADATA_TYPE_CUESHEET: + if(block.data.cue_sheet.num_tracks > 0) + for(i = 0; i < block.data.cue_sheet.num_tracks; i++) + if(0 != block.data.cue_sheet.tracks[i].indices) + free(block.data.cue_sheet.tracks[i].indices); + if(0 != block.data.cue_sheet.tracks) + free(block.data.cue_sheet.tracks); + break; + case FLAC__METADATA_TYPE_PICTURE: + if(0 != block.data.picture.mime_type) + free(block.data.picture.mime_type); + if(0 != block.data.picture.description) + free(block.data.picture.description); + if(0 != block.data.picture.data) + free(block.data.picture.data); + break; + case FLAC__METADATA_TYPE_STREAMINFO: + case FLAC__METADATA_TYPE_SEEKTABLE: + FLAC__ASSERT(0); + default: + if(0 != block.data.unknown.data) + free(block.data.unknown.data); + break; + } + + if(!ok) /* anything that unsets "ok" should also make sure decoder->protected_->state is updated */ + return false; + } + } + + if(is_last) { + /* if this fails, it's OK, it's just a hint for the seek routine */ + if(!FLAC__stream_decoder_get_decode_position(decoder, &decoder->private_->first_frame_offset)) + decoder->private_->first_frame_offset = 0; + decoder->protected_->state = FLAC__STREAM_DECODER_SEARCH_FOR_FRAME_SYNC; + } + + return true; +} + +FLAC__bool read_metadata_streaminfo_(FLAC__StreamDecoder *decoder, FLAC__bool is_last, unsigned length) +{ + FLAC__uint32 x; + unsigned bits, used_bits = 0; + + FLAC__ASSERT(FLAC__bitreader_is_consumed_byte_aligned(decoder->private_->input)); + + decoder->private_->stream_info.type = FLAC__METADATA_TYPE_STREAMINFO; + decoder->private_->stream_info.is_last = is_last; + decoder->private_->stream_info.length = length; + + bits = FLAC__STREAM_METADATA_STREAMINFO_MIN_BLOCK_SIZE_LEN; + if(!FLAC__bitreader_read_raw_uint32(decoder->private_->input, &x, bits)) + return false; /* read_callback_ sets the state for us */ + decoder->private_->stream_info.data.stream_info.min_blocksize = x; + used_bits += bits; + + bits = FLAC__STREAM_METADATA_STREAMINFO_MAX_BLOCK_SIZE_LEN; + if(!FLAC__bitreader_read_raw_uint32(decoder->private_->input, &x, FLAC__STREAM_METADATA_STREAMINFO_MAX_BLOCK_SIZE_LEN)) + return false; /* read_callback_ sets the state for us */ + decoder->private_->stream_info.data.stream_info.max_blocksize = x; + used_bits += bits; + + bits = FLAC__STREAM_METADATA_STREAMINFO_MIN_FRAME_SIZE_LEN; + if(!FLAC__bitreader_read_raw_uint32(decoder->private_->input, &x, FLAC__STREAM_METADATA_STREAMINFO_MIN_FRAME_SIZE_LEN)) + return false; /* read_callback_ sets the state for us */ + decoder->private_->stream_info.data.stream_info.min_framesize = x; + used_bits += bits; + + bits = FLAC__STREAM_METADATA_STREAMINFO_MAX_FRAME_SIZE_LEN; + if(!FLAC__bitreader_read_raw_uint32(decoder->private_->input, &x, FLAC__STREAM_METADATA_STREAMINFO_MAX_FRAME_SIZE_LEN)) + return false; /* read_callback_ sets the state for us */ + decoder->private_->stream_info.data.stream_info.max_framesize = x; + used_bits += bits; + + bits = FLAC__STREAM_METADATA_STREAMINFO_SAMPLE_RATE_LEN; + if(!FLAC__bitreader_read_raw_uint32(decoder->private_->input, &x, FLAC__STREAM_METADATA_STREAMINFO_SAMPLE_RATE_LEN)) + return false; /* read_callback_ sets the state for us */ + decoder->private_->stream_info.data.stream_info.sample_rate = x; + used_bits += bits; + + bits = FLAC__STREAM_METADATA_STREAMINFO_CHANNELS_LEN; + if(!FLAC__bitreader_read_raw_uint32(decoder->private_->input, &x, FLAC__STREAM_METADATA_STREAMINFO_CHANNELS_LEN)) + return false; /* read_callback_ sets the state for us */ + decoder->private_->stream_info.data.stream_info.channels = x+1; + used_bits += bits; + + bits = FLAC__STREAM_METADATA_STREAMINFO_BITS_PER_SAMPLE_LEN; + if(!FLAC__bitreader_read_raw_uint32(decoder->private_->input, &x, FLAC__STREAM_METADATA_STREAMINFO_BITS_PER_SAMPLE_LEN)) + return false; /* read_callback_ sets the state for us */ + decoder->private_->stream_info.data.stream_info.bits_per_sample = x+1; + used_bits += bits; + + bits = FLAC__STREAM_METADATA_STREAMINFO_TOTAL_SAMPLES_LEN; + if(!FLAC__bitreader_read_raw_uint64(decoder->private_->input, &decoder->private_->stream_info.data.stream_info.total_samples, FLAC__STREAM_METADATA_STREAMINFO_TOTAL_SAMPLES_LEN)) + return false; /* read_callback_ sets the state for us */ + used_bits += bits; + + if(!FLAC__bitreader_read_byte_block_aligned_no_crc(decoder->private_->input, decoder->private_->stream_info.data.stream_info.md5sum, 16)) + return false; /* read_callback_ sets the state for us */ + used_bits += 16*8; + + /* skip the rest of the block */ + FLAC__ASSERT(used_bits % 8 == 0); + length -= (used_bits / 8); + if(!FLAC__bitreader_skip_byte_block_aligned_no_crc(decoder->private_->input, length)) + return false; /* read_callback_ sets the state for us */ + + return true; +} + +FLAC__bool read_metadata_seektable_(FLAC__StreamDecoder *decoder, FLAC__bool is_last, unsigned length) +{ + FLAC__uint32 i, x; + FLAC__uint64 xx; + + FLAC__ASSERT(FLAC__bitreader_is_consumed_byte_aligned(decoder->private_->input)); + + decoder->private_->seek_table.type = FLAC__METADATA_TYPE_SEEKTABLE; + decoder->private_->seek_table.is_last = is_last; + decoder->private_->seek_table.length = length; + + decoder->private_->seek_table.data.seek_table.num_points = length / FLAC__STREAM_METADATA_SEEKPOINT_LENGTH; + + /* use realloc since we may pass through here several times (e.g. after seeking) */ + if(0 == (decoder->private_->seek_table.data.seek_table.points = safe_realloc_mul_2op_(decoder->private_->seek_table.data.seek_table.points, decoder->private_->seek_table.data.seek_table.num_points, /*times*/sizeof(FLAC__StreamMetadata_SeekPoint)))) { + decoder->protected_->state = FLAC__STREAM_DECODER_MEMORY_ALLOCATION_ERROR; + return false; + } + for(i = 0; i < decoder->private_->seek_table.data.seek_table.num_points; i++) { + if(!FLAC__bitreader_read_raw_uint64(decoder->private_->input, &xx, FLAC__STREAM_METADATA_SEEKPOINT_SAMPLE_NUMBER_LEN)) + return false; /* read_callback_ sets the state for us */ + decoder->private_->seek_table.data.seek_table.points[i].sample_number = xx; + + if(!FLAC__bitreader_read_raw_uint64(decoder->private_->input, &xx, FLAC__STREAM_METADATA_SEEKPOINT_STREAM_OFFSET_LEN)) + return false; /* read_callback_ sets the state for us */ + decoder->private_->seek_table.data.seek_table.points[i].stream_offset = xx; + + if(!FLAC__bitreader_read_raw_uint32(decoder->private_->input, &x, FLAC__STREAM_METADATA_SEEKPOINT_FRAME_SAMPLES_LEN)) + return false; /* read_callback_ sets the state for us */ + decoder->private_->seek_table.data.seek_table.points[i].frame_samples = x; + } + length -= (decoder->private_->seek_table.data.seek_table.num_points * FLAC__STREAM_METADATA_SEEKPOINT_LENGTH); + /* if there is a partial point left, skip over it */ + if(length > 0) { + /*@@@ do a send_error_to_client_() here? there's an argument for either way */ + if(!FLAC__bitreader_skip_byte_block_aligned_no_crc(decoder->private_->input, length)) + return false; /* read_callback_ sets the state for us */ + } + + return true; +} + +FLAC__bool read_metadata_vorbiscomment_(FLAC__StreamDecoder *decoder, FLAC__StreamMetadata_VorbisComment *obj, unsigned length) +{ + FLAC__uint32 i; + + FLAC__ASSERT(FLAC__bitreader_is_consumed_byte_aligned(decoder->private_->input)); + + /* read vendor string */ + if (length >= 8) { + length -= 8; /* vendor string length + num comments entries alone take 8 bytes */ + FLAC__ASSERT(FLAC__STREAM_METADATA_VORBIS_COMMENT_ENTRY_LENGTH_LEN == 32); + if (!FLAC__bitreader_read_uint32_little_endian(decoder->private_->input, &obj->vendor_string.length)) + return false; /* read_callback_ sets the state for us */ + if (obj->vendor_string.length > 0) { + if (length < obj->vendor_string.length) { + obj->vendor_string.length = 0; + obj->vendor_string.entry = 0; + goto skip; + } + else + length -= obj->vendor_string.length; + if (0 == (obj->vendor_string.entry = safe_malloc_add_2op_(obj->vendor_string.length, /*+*/1))) { + decoder->protected_->state = FLAC__STREAM_DECODER_MEMORY_ALLOCATION_ERROR; + return false; + } + if (!FLAC__bitreader_read_byte_block_aligned_no_crc(decoder->private_->input, obj->vendor_string.entry, obj->vendor_string.length)) + return false; /* read_callback_ sets the state for us */ + obj->vendor_string.entry[obj->vendor_string.length] = '\0'; + } + else + obj->vendor_string.entry = 0; + + /* read num comments */ + FLAC__ASSERT(FLAC__STREAM_METADATA_VORBIS_COMMENT_NUM_COMMENTS_LEN == 32); + if (!FLAC__bitreader_read_uint32_little_endian(decoder->private_->input, &obj->num_comments)) + return false; /* read_callback_ sets the state for us */ + + /* read comments */ + if (obj->num_comments > 100000) { + /* Possibly malicious file. */ + obj->num_comments = 0; + return false; + } + if (obj->num_comments > 0) { + if (0 == (obj->comments = safe_malloc_mul_2op_p(obj->num_comments, /*times*/sizeof(FLAC__StreamMetadata_VorbisComment_Entry)))) { + obj->num_comments = 0; + decoder->protected_->state = FLAC__STREAM_DECODER_MEMORY_ALLOCATION_ERROR; + return false; + } + for (i = 0; i < obj->num_comments; i++) { + /* Initialize here just to make sure. */ + obj->comments[i].length = 0; + obj->comments[i].entry = 0; + + FLAC__ASSERT(FLAC__STREAM_METADATA_VORBIS_COMMENT_ENTRY_LENGTH_LEN == 32); + if (length < 4) { + obj->num_comments = i; + goto skip; + } + else + length -= 4; + if (!FLAC__bitreader_read_uint32_little_endian(decoder->private_->input, &obj->comments[i].length)) { + obj->num_comments = i; + return false; /* read_callback_ sets the state for us */ + } + if (obj->comments[i].length > 0) { + if (length < obj->comments[i].length) { + obj->num_comments = i; + goto skip; + } + else + length -= obj->comments[i].length; + if (0 == (obj->comments[i].entry = safe_malloc_add_2op_(obj->comments[i].length, /*+*/1))) { + decoder->protected_->state = FLAC__STREAM_DECODER_MEMORY_ALLOCATION_ERROR; + obj->num_comments = i; + return false; + } + memset (obj->comments[i].entry, 0, obj->comments[i].length) ; + if (!FLAC__bitreader_read_byte_block_aligned_no_crc(decoder->private_->input, obj->comments[i].entry, obj->comments[i].length)) { + obj->num_comments = i; + goto skip; + } + obj->comments[i].entry[obj->comments[i].length] = '\0'; + } + else + obj->comments[i].entry = 0; + } + } + } + + skip: + if (length > 0) { + /* length > 0 can only happen on files with invalid data in comments */ + if(obj->num_comments < 1) { + free(obj->comments); + obj->comments = NULL; + } + if(!FLAC__bitreader_skip_byte_block_aligned_no_crc(decoder->private_->input, length)) + return false; /* read_callback_ sets the state for us */ + } + + return true; +} + +FLAC__bool read_metadata_cuesheet_(FLAC__StreamDecoder *decoder, FLAC__StreamMetadata_CueSheet *obj) +{ + FLAC__uint32 i, j, x; + + FLAC__ASSERT(FLAC__bitreader_is_consumed_byte_aligned(decoder->private_->input)); + + memset(obj, 0, sizeof(FLAC__StreamMetadata_CueSheet)); + + FLAC__ASSERT(FLAC__STREAM_METADATA_CUESHEET_MEDIA_CATALOG_NUMBER_LEN % 8 == 0); + if(!FLAC__bitreader_read_byte_block_aligned_no_crc(decoder->private_->input, (FLAC__byte*)obj->media_catalog_number, FLAC__STREAM_METADATA_CUESHEET_MEDIA_CATALOG_NUMBER_LEN/8)) + return false; /* read_callback_ sets the state for us */ + + if(!FLAC__bitreader_read_raw_uint64(decoder->private_->input, &obj->lead_in, FLAC__STREAM_METADATA_CUESHEET_LEAD_IN_LEN)) + return false; /* read_callback_ sets the state for us */ + + if(!FLAC__bitreader_read_raw_uint32(decoder->private_->input, &x, FLAC__STREAM_METADATA_CUESHEET_IS_CD_LEN)) + return false; /* read_callback_ sets the state for us */ + obj->is_cd = x? true : false; + + if(!FLAC__bitreader_skip_bits_no_crc(decoder->private_->input, FLAC__STREAM_METADATA_CUESHEET_RESERVED_LEN)) + return false; /* read_callback_ sets the state for us */ + + if(!FLAC__bitreader_read_raw_uint32(decoder->private_->input, &x, FLAC__STREAM_METADATA_CUESHEET_NUM_TRACKS_LEN)) + return false; /* read_callback_ sets the state for us */ + obj->num_tracks = x; + + if(obj->num_tracks > 0) { + if(0 == (obj->tracks = safe_calloc_(obj->num_tracks, sizeof(FLAC__StreamMetadata_CueSheet_Track)))) { + decoder->protected_->state = FLAC__STREAM_DECODER_MEMORY_ALLOCATION_ERROR; + return false; + } + for(i = 0; i < obj->num_tracks; i++) { + FLAC__StreamMetadata_CueSheet_Track *track = &obj->tracks[i]; + if(!FLAC__bitreader_read_raw_uint64(decoder->private_->input, &track->offset, FLAC__STREAM_METADATA_CUESHEET_TRACK_OFFSET_LEN)) + return false; /* read_callback_ sets the state for us */ + + if(!FLAC__bitreader_read_raw_uint32(decoder->private_->input, &x, FLAC__STREAM_METADATA_CUESHEET_TRACK_NUMBER_LEN)) + return false; /* read_callback_ sets the state for us */ + track->number = (FLAC__byte)x; + + FLAC__ASSERT(FLAC__STREAM_METADATA_CUESHEET_TRACK_ISRC_LEN % 8 == 0); + if(!FLAC__bitreader_read_byte_block_aligned_no_crc(decoder->private_->input, (FLAC__byte*)track->isrc, FLAC__STREAM_METADATA_CUESHEET_TRACK_ISRC_LEN/8)) + return false; /* read_callback_ sets the state for us */ + + if(!FLAC__bitreader_read_raw_uint32(decoder->private_->input, &x, FLAC__STREAM_METADATA_CUESHEET_TRACK_TYPE_LEN)) + return false; /* read_callback_ sets the state for us */ + track->type = x; + + if(!FLAC__bitreader_read_raw_uint32(decoder->private_->input, &x, FLAC__STREAM_METADATA_CUESHEET_TRACK_PRE_EMPHASIS_LEN)) + return false; /* read_callback_ sets the state for us */ + track->pre_emphasis = x; + + if(!FLAC__bitreader_skip_bits_no_crc(decoder->private_->input, FLAC__STREAM_METADATA_CUESHEET_TRACK_RESERVED_LEN)) + return false; /* read_callback_ sets the state for us */ + + if(!FLAC__bitreader_read_raw_uint32(decoder->private_->input, &x, FLAC__STREAM_METADATA_CUESHEET_TRACK_NUM_INDICES_LEN)) + return false; /* read_callback_ sets the state for us */ + track->num_indices = (FLAC__byte)x; + + if(track->num_indices > 0) { + if(0 == (track->indices = safe_calloc_(track->num_indices, sizeof(FLAC__StreamMetadata_CueSheet_Index)))) { + decoder->protected_->state = FLAC__STREAM_DECODER_MEMORY_ALLOCATION_ERROR; + return false; + } + for(j = 0; j < track->num_indices; j++) { + FLAC__StreamMetadata_CueSheet_Index *indx = &track->indices[j]; + if(!FLAC__bitreader_read_raw_uint64(decoder->private_->input, &indx->offset, FLAC__STREAM_METADATA_CUESHEET_INDEX_OFFSET_LEN)) + return false; /* read_callback_ sets the state for us */ + + if(!FLAC__bitreader_read_raw_uint32(decoder->private_->input, &x, FLAC__STREAM_METADATA_CUESHEET_INDEX_NUMBER_LEN)) + return false; /* read_callback_ sets the state for us */ + indx->number = (FLAC__byte)x; + + if(!FLAC__bitreader_skip_bits_no_crc(decoder->private_->input, FLAC__STREAM_METADATA_CUESHEET_INDEX_RESERVED_LEN)) + return false; /* read_callback_ sets the state for us */ + } + } + } + } + + return true; +} + +FLAC__bool read_metadata_picture_(FLAC__StreamDecoder *decoder, FLAC__StreamMetadata_Picture *obj) +{ + FLAC__uint32 x; + + FLAC__ASSERT(FLAC__bitreader_is_consumed_byte_aligned(decoder->private_->input)); + + /* read type */ + if(!FLAC__bitreader_read_raw_uint32(decoder->private_->input, &x, FLAC__STREAM_METADATA_PICTURE_TYPE_LEN)) + return false; /* read_callback_ sets the state for us */ + obj->type = x; + + /* read MIME type */ + if(!FLAC__bitreader_read_raw_uint32(decoder->private_->input, &x, FLAC__STREAM_METADATA_PICTURE_MIME_TYPE_LENGTH_LEN)) + return false; /* read_callback_ sets the state for us */ + if(0 == (obj->mime_type = safe_malloc_add_2op_(x, /*+*/1))) { + decoder->protected_->state = FLAC__STREAM_DECODER_MEMORY_ALLOCATION_ERROR; + return false; + } + if(x > 0) { + if(!FLAC__bitreader_read_byte_block_aligned_no_crc(decoder->private_->input, (FLAC__byte*)obj->mime_type, x)) + return false; /* read_callback_ sets the state for us */ + } + obj->mime_type[x] = '\0'; + + /* read description */ + if(!FLAC__bitreader_read_raw_uint32(decoder->private_->input, &x, FLAC__STREAM_METADATA_PICTURE_DESCRIPTION_LENGTH_LEN)) + return false; /* read_callback_ sets the state for us */ + if(0 == (obj->description = safe_malloc_add_2op_(x, /*+*/1))) { + decoder->protected_->state = FLAC__STREAM_DECODER_MEMORY_ALLOCATION_ERROR; + return false; + } + if(x > 0) { + if(!FLAC__bitreader_read_byte_block_aligned_no_crc(decoder->private_->input, obj->description, x)) + return false; /* read_callback_ sets the state for us */ + } + obj->description[x] = '\0'; + + /* read width */ + if(!FLAC__bitreader_read_raw_uint32(decoder->private_->input, &obj->width, FLAC__STREAM_METADATA_PICTURE_WIDTH_LEN)) + return false; /* read_callback_ sets the state for us */ + + /* read height */ + if(!FLAC__bitreader_read_raw_uint32(decoder->private_->input, &obj->height, FLAC__STREAM_METADATA_PICTURE_HEIGHT_LEN)) + return false; /* read_callback_ sets the state for us */ + + /* read depth */ + if(!FLAC__bitreader_read_raw_uint32(decoder->private_->input, &obj->depth, FLAC__STREAM_METADATA_PICTURE_DEPTH_LEN)) + return false; /* read_callback_ sets the state for us */ + + /* read colors */ + if(!FLAC__bitreader_read_raw_uint32(decoder->private_->input, &obj->colors, FLAC__STREAM_METADATA_PICTURE_COLORS_LEN)) + return false; /* read_callback_ sets the state for us */ + + /* read data */ + if(!FLAC__bitreader_read_raw_uint32(decoder->private_->input, &(obj->data_length), FLAC__STREAM_METADATA_PICTURE_DATA_LENGTH_LEN)) + return false; /* read_callback_ sets the state for us */ + if(0 == (obj->data = safe_malloc_(obj->data_length))) { + decoder->protected_->state = FLAC__STREAM_DECODER_MEMORY_ALLOCATION_ERROR; + return false; + } + if(obj->data_length > 0) { + if(!FLAC__bitreader_read_byte_block_aligned_no_crc(decoder->private_->input, obj->data, obj->data_length)) + return false; /* read_callback_ sets the state for us */ + } + + return true; +} + +FLAC__bool skip_id3v2_tag_(FLAC__StreamDecoder *decoder) +{ + FLAC__uint32 x; + unsigned i, skip; + + /* skip the version and flags bytes */ + if(!FLAC__bitreader_read_raw_uint32(decoder->private_->input, &x, 24)) + return false; /* read_callback_ sets the state for us */ + /* get the size (in bytes) to skip */ + skip = 0; + for(i = 0; i < 4; i++) { + if(!FLAC__bitreader_read_raw_uint32(decoder->private_->input, &x, 8)) + return false; /* read_callback_ sets the state for us */ + skip <<= 7; + skip |= (x & 0x7f); + } + /* skip the rest of the tag */ + if(!FLAC__bitreader_skip_byte_block_aligned_no_crc(decoder->private_->input, skip)) + return false; /* read_callback_ sets the state for us */ + return true; +} + +FLAC__bool frame_sync_(FLAC__StreamDecoder *decoder) +{ + FLAC__uint32 x; + FLAC__bool first = true; + + /* If we know the total number of samples in the stream, stop if we've read that many. */ + /* This will stop us, for example, from wasting time trying to sync on an ID3V1 tag. */ + if(FLAC__stream_decoder_get_total_samples(decoder) > 0) { + if(decoder->private_->samples_decoded >= FLAC__stream_decoder_get_total_samples(decoder)) { + decoder->protected_->state = FLAC__STREAM_DECODER_END_OF_STREAM; + return true; + } + } + + /* make sure we're byte aligned */ + if(!FLAC__bitreader_is_consumed_byte_aligned(decoder->private_->input)) { + if(!FLAC__bitreader_read_raw_uint32(decoder->private_->input, &x, FLAC__bitreader_bits_left_for_byte_alignment(decoder->private_->input))) + return false; /* read_callback_ sets the state for us */ + } + + while(1) { + if(decoder->private_->cached) { + x = (FLAC__uint32)decoder->private_->lookahead; + decoder->private_->cached = false; + } + else { + if(!FLAC__bitreader_read_raw_uint32(decoder->private_->input, &x, 8)) + return false; /* read_callback_ sets the state for us */ + } + if(x == 0xff) { /* MAGIC NUMBER for the first 8 frame sync bits */ + decoder->private_->header_warmup[0] = (FLAC__byte)x; + if(!FLAC__bitreader_read_raw_uint32(decoder->private_->input, &x, 8)) + return false; /* read_callback_ sets the state for us */ + + /* we have to check if we just read two 0xff's in a row; the second may actually be the beginning of the sync code */ + /* else we have to check if the second byte is the end of a sync code */ + if(x == 0xff) { /* MAGIC NUMBER for the first 8 frame sync bits */ + decoder->private_->lookahead = (FLAC__byte)x; + decoder->private_->cached = true; + } + else if(x >> 1 == 0x7c) { /* MAGIC NUMBER for the last 6 sync bits and reserved 7th bit */ + decoder->private_->header_warmup[1] = (FLAC__byte)x; + decoder->protected_->state = FLAC__STREAM_DECODER_READ_FRAME; + return true; + } + } + if(first) { + send_error_to_client_(decoder, FLAC__STREAM_DECODER_ERROR_STATUS_LOST_SYNC); + first = false; + } + } + + return true; +} + +FLAC__bool read_frame_(FLAC__StreamDecoder *decoder, FLAC__bool *got_a_frame, FLAC__bool do_full_decode) +{ + unsigned channel; + unsigned i; + FLAC__int32 mid, side; + unsigned frame_crc; /* the one we calculate from the input stream */ + FLAC__uint32 x; + + *got_a_frame = false; + + /* init the CRC */ + frame_crc = 0; + frame_crc = FLAC__CRC16_UPDATE(decoder->private_->header_warmup[0], frame_crc); + frame_crc = FLAC__CRC16_UPDATE(decoder->private_->header_warmup[1], frame_crc); + FLAC__bitreader_reset_read_crc16(decoder->private_->input, (FLAC__uint16)frame_crc); + + if(!read_frame_header_(decoder)) + return false; + if(decoder->protected_->state == FLAC__STREAM_DECODER_SEARCH_FOR_FRAME_SYNC) /* means we didn't sync on a valid header */ + return true; + if(!allocate_output_(decoder, decoder->private_->frame.header.blocksize, decoder->private_->frame.header.channels)) + return false; + for(channel = 0; channel < decoder->private_->frame.header.channels; channel++) { + /* + * first figure the correct bits-per-sample of the subframe + */ + unsigned bps = decoder->private_->frame.header.bits_per_sample; + switch(decoder->private_->frame.header.channel_assignment) { + case FLAC__CHANNEL_ASSIGNMENT_INDEPENDENT: + /* no adjustment needed */ + break; + case FLAC__CHANNEL_ASSIGNMENT_LEFT_SIDE: + FLAC__ASSERT(decoder->private_->frame.header.channels == 2); + if(channel == 1) + bps++; + break; + case FLAC__CHANNEL_ASSIGNMENT_RIGHT_SIDE: + FLAC__ASSERT(decoder->private_->frame.header.channels == 2); + if(channel == 0) + bps++; + break; + case FLAC__CHANNEL_ASSIGNMENT_MID_SIDE: + FLAC__ASSERT(decoder->private_->frame.header.channels == 2); + if(channel == 1) + bps++; + break; + default: + FLAC__ASSERT(0); + } + /* + * now read it + */ + if(!read_subframe_(decoder, channel, bps, do_full_decode)) + return false; + if(decoder->protected_->state == FLAC__STREAM_DECODER_SEARCH_FOR_FRAME_SYNC) /* means bad sync or got corruption */ + return true; + } + if(!read_zero_padding_(decoder)) + return false; + if(decoder->protected_->state == FLAC__STREAM_DECODER_SEARCH_FOR_FRAME_SYNC) /* means bad sync or got corruption (i.e. "zero bits" were not all zeroes) */ + return true; + + /* + * Read the frame CRC-16 from the footer and check + */ + frame_crc = FLAC__bitreader_get_read_crc16(decoder->private_->input); + if(!FLAC__bitreader_read_raw_uint32(decoder->private_->input, &x, FLAC__FRAME_FOOTER_CRC_LEN)) + return false; /* read_callback_ sets the state for us */ + if(frame_crc == x) { + if(do_full_decode) { + /* Undo any special channel coding */ + switch(decoder->private_->frame.header.channel_assignment) { + case FLAC__CHANNEL_ASSIGNMENT_INDEPENDENT: + /* do nothing */ + break; + case FLAC__CHANNEL_ASSIGNMENT_LEFT_SIDE: + FLAC__ASSERT(decoder->private_->frame.header.channels == 2); + for(i = 0; i < decoder->private_->frame.header.blocksize; i++) + decoder->private_->output[1][i] = decoder->private_->output[0][i] - decoder->private_->output[1][i]; + break; + case FLAC__CHANNEL_ASSIGNMENT_RIGHT_SIDE: + FLAC__ASSERT(decoder->private_->frame.header.channels == 2); + for(i = 0; i < decoder->private_->frame.header.blocksize; i++) + decoder->private_->output[0][i] += decoder->private_->output[1][i]; + break; + case FLAC__CHANNEL_ASSIGNMENT_MID_SIDE: + FLAC__ASSERT(decoder->private_->frame.header.channels == 2); + for(i = 0; i < decoder->private_->frame.header.blocksize; i++) { +#if 1 + mid = decoder->private_->output[0][i]; + side = decoder->private_->output[1][i]; + mid = ((uint32_t) mid) << 1; + mid |= (side & 1); /* i.e. if 'side' is odd... */ + decoder->private_->output[0][i] = (mid + side) >> 1; + decoder->private_->output[1][i] = (mid - side) >> 1; +#else + /* OPT: without 'side' temp variable */ + mid = (decoder->private_->output[0][i] << 1) | (decoder->private_->output[1][i] & 1); /* i.e. if 'side' is odd... */ + decoder->private_->output[0][i] = (mid + decoder->private_->output[1][i]) >> 1; + decoder->private_->output[1][i] = (mid - decoder->private_->output[1][i]) >> 1; +#endif + } + break; + default: + FLAC__ASSERT(0); + break; + } + } + } + else { + /* Bad frame, emit error and zero the output signal */ + send_error_to_client_(decoder, FLAC__STREAM_DECODER_ERROR_STATUS_FRAME_CRC_MISMATCH); + if(do_full_decode) { + for(channel = 0; channel < decoder->private_->frame.header.channels; channel++) { + memset(decoder->private_->output[channel], 0, sizeof(FLAC__int32) * decoder->private_->frame.header.blocksize); + } + } + } + + *got_a_frame = true; + + /* we wait to update fixed_block_size until here, when we're sure we've got a proper frame and hence a correct blocksize */ + if(decoder->private_->next_fixed_block_size) + decoder->private_->fixed_block_size = decoder->private_->next_fixed_block_size; + + /* put the latest values into the public section of the decoder instance */ + decoder->protected_->channels = decoder->private_->frame.header.channels; + decoder->protected_->channel_assignment = decoder->private_->frame.header.channel_assignment; + decoder->protected_->bits_per_sample = decoder->private_->frame.header.bits_per_sample; + decoder->protected_->sample_rate = decoder->private_->frame.header.sample_rate; + decoder->protected_->blocksize = decoder->private_->frame.header.blocksize; + + FLAC__ASSERT(decoder->private_->frame.header.number_type == FLAC__FRAME_NUMBER_TYPE_SAMPLE_NUMBER); + decoder->private_->samples_decoded = decoder->private_->frame.header.number.sample_number + decoder->private_->frame.header.blocksize; + + /* write it */ + if(do_full_decode) { + if(write_audio_frame_to_client_(decoder, &decoder->private_->frame, (const FLAC__int32 * const *)decoder->private_->output) != FLAC__STREAM_DECODER_WRITE_STATUS_CONTINUE) { + decoder->protected_->state = FLAC__STREAM_DECODER_ABORTED; + return false; + } + } + + decoder->protected_->state = FLAC__STREAM_DECODER_SEARCH_FOR_FRAME_SYNC; + return true; +} + +FLAC__bool read_frame_header_(FLAC__StreamDecoder *decoder) +{ + FLAC__uint32 x; + FLAC__uint64 xx; + unsigned i, blocksize_hint = 0, sample_rate_hint = 0; + FLAC__byte crc8, raw_header[16]; /* MAGIC NUMBER based on the maximum frame header size, including CRC */ + unsigned raw_header_len; + FLAC__bool is_unparseable = false; + + FLAC__ASSERT(FLAC__bitreader_is_consumed_byte_aligned(decoder->private_->input)); + + /* init the raw header with the saved bits from synchronization */ + raw_header[0] = decoder->private_->header_warmup[0]; + raw_header[1] = decoder->private_->header_warmup[1]; + raw_header_len = 2; + + /* check to make sure that reserved bit is 0 */ + if(raw_header[1] & 0x02) /* MAGIC NUMBER */ + is_unparseable = true; + + /* + * Note that along the way as we read the header, we look for a sync + * code inside. If we find one it would indicate that our original + * sync was bad since there cannot be a sync code in a valid header. + * + * Three kinds of things can go wrong when reading the frame header: + * 1) We may have sync'ed incorrectly and not landed on a frame header. + * If we don't find a sync code, it can end up looking like we read + * a valid but unparseable header, until getting to the frame header + * CRC. Even then we could get a false positive on the CRC. + * 2) We may have sync'ed correctly but on an unparseable frame (from a + * future encoder). + * 3) We may be on a damaged frame which appears valid but unparseable. + * + * For all these reasons, we try and read a complete frame header as + * long as it seems valid, even if unparseable, up until the frame + * header CRC. + */ + + /* + * read in the raw header as bytes so we can CRC it, and parse it on the way + */ + for(i = 0; i < 2; i++) { + if(!FLAC__bitreader_read_raw_uint32(decoder->private_->input, &x, 8)) + return false; /* read_callback_ sets the state for us */ + if(x == 0xff) { /* MAGIC NUMBER for the first 8 frame sync bits */ + /* if we get here it means our original sync was erroneous since the sync code cannot appear in the header */ + decoder->private_->lookahead = (FLAC__byte)x; + decoder->private_->cached = true; + send_error_to_client_(decoder, FLAC__STREAM_DECODER_ERROR_STATUS_BAD_HEADER); + decoder->protected_->state = FLAC__STREAM_DECODER_SEARCH_FOR_FRAME_SYNC; + return true; + } + raw_header[raw_header_len++] = (FLAC__byte)x; + } + + switch(x = raw_header[2] >> 4) { + case 0: + is_unparseable = true; + break; + case 1: + decoder->private_->frame.header.blocksize = 192; + break; + case 2: + case 3: + case 4: + case 5: + decoder->private_->frame.header.blocksize = 576 << (x-2); + break; + case 6: + case 7: + blocksize_hint = x; + break; + case 8: + case 9: + case 10: + case 11: + case 12: + case 13: + case 14: + case 15: + decoder->private_->frame.header.blocksize = 256 << (x-8); + break; + default: + FLAC__ASSERT(0); + break; + } + + switch(x = raw_header[2] & 0x0f) { + case 0: + if(decoder->private_->has_stream_info) + decoder->private_->frame.header.sample_rate = decoder->private_->stream_info.data.stream_info.sample_rate; + else + is_unparseable = true; + break; + case 1: + decoder->private_->frame.header.sample_rate = 88200; + break; + case 2: + decoder->private_->frame.header.sample_rate = 176400; + break; + case 3: + decoder->private_->frame.header.sample_rate = 192000; + break; + case 4: + decoder->private_->frame.header.sample_rate = 8000; + break; + case 5: + decoder->private_->frame.header.sample_rate = 16000; + break; + case 6: + decoder->private_->frame.header.sample_rate = 22050; + break; + case 7: + decoder->private_->frame.header.sample_rate = 24000; + break; + case 8: + decoder->private_->frame.header.sample_rate = 32000; + break; + case 9: + decoder->private_->frame.header.sample_rate = 44100; + break; + case 10: + decoder->private_->frame.header.sample_rate = 48000; + break; + case 11: + decoder->private_->frame.header.sample_rate = 96000; + break; + case 12: + case 13: + case 14: + sample_rate_hint = x; + break; + case 15: + send_error_to_client_(decoder, FLAC__STREAM_DECODER_ERROR_STATUS_BAD_HEADER); + decoder->protected_->state = FLAC__STREAM_DECODER_SEARCH_FOR_FRAME_SYNC; + return true; + default: + FLAC__ASSERT(0); + } + + x = (unsigned)(raw_header[3] >> 4); + if(x & 8) { + decoder->private_->frame.header.channels = 2; + switch(x & 7) { + case 0: + decoder->private_->frame.header.channel_assignment = FLAC__CHANNEL_ASSIGNMENT_LEFT_SIDE; + break; + case 1: + decoder->private_->frame.header.channel_assignment = FLAC__CHANNEL_ASSIGNMENT_RIGHT_SIDE; + break; + case 2: + decoder->private_->frame.header.channel_assignment = FLAC__CHANNEL_ASSIGNMENT_MID_SIDE; + break; + default: + is_unparseable = true; + break; + } + } + else { + decoder->private_->frame.header.channels = (unsigned)x + 1; + decoder->private_->frame.header.channel_assignment = FLAC__CHANNEL_ASSIGNMENT_INDEPENDENT; + } + + switch(x = (unsigned)(raw_header[3] & 0x0e) >> 1) { + case 0: + if(decoder->private_->has_stream_info) + decoder->private_->frame.header.bits_per_sample = decoder->private_->stream_info.data.stream_info.bits_per_sample; + else + is_unparseable = true; + break; + case 1: + decoder->private_->frame.header.bits_per_sample = 8; + break; + case 2: + decoder->private_->frame.header.bits_per_sample = 12; + break; + case 4: + decoder->private_->frame.header.bits_per_sample = 16; + break; + case 5: + decoder->private_->frame.header.bits_per_sample = 20; + break; + case 6: + decoder->private_->frame.header.bits_per_sample = 24; + break; + case 3: + case 7: + is_unparseable = true; + break; + default: + FLAC__ASSERT(0); + break; + } + + /* check to make sure that reserved bit is 0 */ + if(raw_header[3] & 0x01) /* MAGIC NUMBER */ + is_unparseable = true; + + /* read the frame's starting sample number (or frame number as the case may be) */ + if( + raw_header[1] & 0x01 || + /*@@@ this clause is a concession to the old way of doing variable blocksize; the only known implementation is flake and can probably be removed without inconveniencing anyone */ + (decoder->private_->has_stream_info && decoder->private_->stream_info.data.stream_info.min_blocksize != decoder->private_->stream_info.data.stream_info.max_blocksize) + ) { /* variable blocksize */ + if(!FLAC__bitreader_read_utf8_uint64(decoder->private_->input, &xx, raw_header, &raw_header_len)) + return false; /* read_callback_ sets the state for us */ + if(xx == FLAC__U64L(0xffffffffffffffff)) { /* i.e. non-UTF8 code... */ + decoder->private_->lookahead = raw_header[raw_header_len-1]; /* back up as much as we can */ + decoder->private_->cached = true; + send_error_to_client_(decoder, FLAC__STREAM_DECODER_ERROR_STATUS_BAD_HEADER); + decoder->protected_->state = FLAC__STREAM_DECODER_SEARCH_FOR_FRAME_SYNC; + return true; + } + decoder->private_->frame.header.number_type = FLAC__FRAME_NUMBER_TYPE_SAMPLE_NUMBER; + decoder->private_->frame.header.number.sample_number = xx; + } + else { /* fixed blocksize */ + if(!FLAC__bitreader_read_utf8_uint32(decoder->private_->input, &x, raw_header, &raw_header_len)) + return false; /* read_callback_ sets the state for us */ + if(x == 0xffffffff) { /* i.e. non-UTF8 code... */ + decoder->private_->lookahead = raw_header[raw_header_len-1]; /* back up as much as we can */ + decoder->private_->cached = true; + send_error_to_client_(decoder, FLAC__STREAM_DECODER_ERROR_STATUS_BAD_HEADER); + decoder->protected_->state = FLAC__STREAM_DECODER_SEARCH_FOR_FRAME_SYNC; + return true; + } + decoder->private_->frame.header.number_type = FLAC__FRAME_NUMBER_TYPE_FRAME_NUMBER; + decoder->private_->frame.header.number.frame_number = x; + } + + if(blocksize_hint) { + if(!FLAC__bitreader_read_raw_uint32(decoder->private_->input, &x, 8)) + return false; /* read_callback_ sets the state for us */ + raw_header[raw_header_len++] = (FLAC__byte)x; + if(blocksize_hint == 7) { + FLAC__uint32 _x; + if(!FLAC__bitreader_read_raw_uint32(decoder->private_->input, &_x, 8)) + return false; /* read_callback_ sets the state for us */ + raw_header[raw_header_len++] = (FLAC__byte)_x; + x = (x << 8) | _x; + } + decoder->private_->frame.header.blocksize = x+1; + } + + if(sample_rate_hint) { + if(!FLAC__bitreader_read_raw_uint32(decoder->private_->input, &x, 8)) + return false; /* read_callback_ sets the state for us */ + raw_header[raw_header_len++] = (FLAC__byte)x; + if(sample_rate_hint != 12) { + FLAC__uint32 _x; + if(!FLAC__bitreader_read_raw_uint32(decoder->private_->input, &_x, 8)) + return false; /* read_callback_ sets the state for us */ + raw_header[raw_header_len++] = (FLAC__byte)_x; + x = (x << 8) | _x; + } + if(sample_rate_hint == 12) + decoder->private_->frame.header.sample_rate = x*1000; + else if(sample_rate_hint == 13) + decoder->private_->frame.header.sample_rate = x; + else + decoder->private_->frame.header.sample_rate = x*10; + } + + /* read the CRC-8 byte */ + if(!FLAC__bitreader_read_raw_uint32(decoder->private_->input, &x, 8)) + return false; /* read_callback_ sets the state for us */ + crc8 = (FLAC__byte)x; + + if(FLAC__crc8(raw_header, raw_header_len) != crc8) { + send_error_to_client_(decoder, FLAC__STREAM_DECODER_ERROR_STATUS_BAD_HEADER); + decoder->protected_->state = FLAC__STREAM_DECODER_SEARCH_FOR_FRAME_SYNC; + return true; + } + + /* calculate the sample number from the frame number if needed */ + decoder->private_->next_fixed_block_size = 0; + if(decoder->private_->frame.header.number_type == FLAC__FRAME_NUMBER_TYPE_FRAME_NUMBER) { + x = decoder->private_->frame.header.number.frame_number; + decoder->private_->frame.header.number_type = FLAC__FRAME_NUMBER_TYPE_SAMPLE_NUMBER; + if(decoder->private_->fixed_block_size) + decoder->private_->frame.header.number.sample_number = (FLAC__uint64)decoder->private_->fixed_block_size * (FLAC__uint64)x; + else if(decoder->private_->has_stream_info) { + if(decoder->private_->stream_info.data.stream_info.min_blocksize == decoder->private_->stream_info.data.stream_info.max_blocksize) { + decoder->private_->frame.header.number.sample_number = (FLAC__uint64)decoder->private_->stream_info.data.stream_info.min_blocksize * (FLAC__uint64)x; + decoder->private_->next_fixed_block_size = decoder->private_->stream_info.data.stream_info.max_blocksize; + } + else + is_unparseable = true; + } + else if(x == 0) { + decoder->private_->frame.header.number.sample_number = 0; + decoder->private_->next_fixed_block_size = decoder->private_->frame.header.blocksize; + } + else { + /* can only get here if the stream has invalid frame numbering and no STREAMINFO, so assume it's not the last (possibly short) frame */ + decoder->private_->frame.header.number.sample_number = (FLAC__uint64)decoder->private_->frame.header.blocksize * (FLAC__uint64)x; + } + } + + if(is_unparseable) { + send_error_to_client_(decoder, FLAC__STREAM_DECODER_ERROR_STATUS_UNPARSEABLE_STREAM); + decoder->protected_->state = FLAC__STREAM_DECODER_SEARCH_FOR_FRAME_SYNC; + return true; + } + + return true; +} + +FLAC__bool read_subframe_(FLAC__StreamDecoder *decoder, unsigned channel, unsigned bps, FLAC__bool do_full_decode) +{ + FLAC__uint32 x; + FLAC__bool wasted_bits; + unsigned i; + + if(!FLAC__bitreader_read_raw_uint32(decoder->private_->input, &x, 8)) /* MAGIC NUMBER */ + return false; /* read_callback_ sets the state for us */ + + wasted_bits = (x & 1); + x &= 0xfe; + + if(wasted_bits) { + unsigned u; + if(!FLAC__bitreader_read_unary_unsigned(decoder->private_->input, &u)) + return false; /* read_callback_ sets the state for us */ + decoder->private_->frame.subframes[channel].wasted_bits = u+1; + if (decoder->private_->frame.subframes[channel].wasted_bits >= bps) + return false; + bps -= decoder->private_->frame.subframes[channel].wasted_bits; + } + else + decoder->private_->frame.subframes[channel].wasted_bits = 0; + + /* + * Lots of magic numbers here + */ + if(x & 0x80) { + send_error_to_client_(decoder, FLAC__STREAM_DECODER_ERROR_STATUS_LOST_SYNC); + decoder->protected_->state = FLAC__STREAM_DECODER_SEARCH_FOR_FRAME_SYNC; + return true; + } + else if(x == 0) { + if(!read_subframe_constant_(decoder, channel, bps, do_full_decode)) + return false; + } + else if(x == 2) { + if(!read_subframe_verbatim_(decoder, channel, bps, do_full_decode)) + return false; + } + else if(x < 16) { + send_error_to_client_(decoder, FLAC__STREAM_DECODER_ERROR_STATUS_UNPARSEABLE_STREAM); + decoder->protected_->state = FLAC__STREAM_DECODER_SEARCH_FOR_FRAME_SYNC; + return true; + } + else if(x <= 24) { + if(!read_subframe_fixed_(decoder, channel, bps, (x>>1)&7, do_full_decode)) + return false; + if(decoder->protected_->state == FLAC__STREAM_DECODER_SEARCH_FOR_FRAME_SYNC) /* means bad sync or got corruption */ + return true; + } + else if(x < 64) { + send_error_to_client_(decoder, FLAC__STREAM_DECODER_ERROR_STATUS_UNPARSEABLE_STREAM); + decoder->protected_->state = FLAC__STREAM_DECODER_SEARCH_FOR_FRAME_SYNC; + return true; + } + else { + if(!read_subframe_lpc_(decoder, channel, bps, ((x>>1)&31)+1, do_full_decode)) + return false; + if(decoder->protected_->state == FLAC__STREAM_DECODER_SEARCH_FOR_FRAME_SYNC) /* means bad sync or got corruption */ + return true; + } + + if(wasted_bits && do_full_decode) { + x = decoder->private_->frame.subframes[channel].wasted_bits; + for(i = 0; i < decoder->private_->frame.header.blocksize; i++) { + uint32_t val = decoder->private_->output[channel][i]; + decoder->private_->output[channel][i] = (val << x); + } + } + + return true; +} + +FLAC__bool read_subframe_constant_(FLAC__StreamDecoder *decoder, unsigned channel, unsigned bps, FLAC__bool do_full_decode) +{ + FLAC__Subframe_Constant *subframe = &decoder->private_->frame.subframes[channel].data.constant; + FLAC__int32 x; + unsigned i; + FLAC__int32 *output = decoder->private_->output[channel]; + + decoder->private_->frame.subframes[channel].type = FLAC__SUBFRAME_TYPE_CONSTANT; + + if(!FLAC__bitreader_read_raw_int32(decoder->private_->input, &x, bps)) + return false; /* read_callback_ sets the state for us */ + + subframe->value = x; + + /* decode the subframe */ + if(do_full_decode) { + for(i = 0; i < decoder->private_->frame.header.blocksize; i++) + output[i] = x; + } + + return true; +} + +FLAC__bool read_subframe_fixed_(FLAC__StreamDecoder *decoder, unsigned channel, unsigned bps, const unsigned order, FLAC__bool do_full_decode) +{ + FLAC__Subframe_Fixed *subframe = &decoder->private_->frame.subframes[channel].data.fixed; + FLAC__int32 i32; + FLAC__uint32 u32; + unsigned u; + + decoder->private_->frame.subframes[channel].type = FLAC__SUBFRAME_TYPE_FIXED; + + subframe->residual = decoder->private_->residual[channel]; + subframe->order = order; + + /* read warm-up samples */ + for(u = 0; u < order; u++) { + if(!FLAC__bitreader_read_raw_int32(decoder->private_->input, &i32, bps)) + return false; /* read_callback_ sets the state for us */ + subframe->warmup[u] = i32; + } + + /* read entropy coding method info */ + if(!FLAC__bitreader_read_raw_uint32(decoder->private_->input, &u32, FLAC__ENTROPY_CODING_METHOD_TYPE_LEN)) + return false; /* read_callback_ sets the state for us */ + subframe->entropy_coding_method.type = (FLAC__EntropyCodingMethodType)u32; + switch(subframe->entropy_coding_method.type) { + case FLAC__ENTROPY_CODING_METHOD_PARTITIONED_RICE: + case FLAC__ENTROPY_CODING_METHOD_PARTITIONED_RICE2: + if(!FLAC__bitreader_read_raw_uint32(decoder->private_->input, &u32, FLAC__ENTROPY_CODING_METHOD_PARTITIONED_RICE_ORDER_LEN)) + return false; /* read_callback_ sets the state for us */ + if(decoder->private_->frame.header.blocksize >> u32 < order) { + send_error_to_client_(decoder, FLAC__STREAM_DECODER_ERROR_STATUS_LOST_SYNC); + decoder->protected_->state = FLAC__STREAM_DECODER_SEARCH_FOR_FRAME_SYNC; + return true; + } + subframe->entropy_coding_method.data.partitioned_rice.order = u32; + subframe->entropy_coding_method.data.partitioned_rice.contents = &decoder->private_->partitioned_rice_contents[channel]; + break; + default: + send_error_to_client_(decoder, FLAC__STREAM_DECODER_ERROR_STATUS_UNPARSEABLE_STREAM); + decoder->protected_->state = FLAC__STREAM_DECODER_SEARCH_FOR_FRAME_SYNC; + return true; + } + + /* read residual */ + switch(subframe->entropy_coding_method.type) { + case FLAC__ENTROPY_CODING_METHOD_PARTITIONED_RICE: + case FLAC__ENTROPY_CODING_METHOD_PARTITIONED_RICE2: + if(!read_residual_partitioned_rice_(decoder, order, subframe->entropy_coding_method.data.partitioned_rice.order, &decoder->private_->partitioned_rice_contents[channel], decoder->private_->residual[channel], /*is_extended=*/subframe->entropy_coding_method.type == FLAC__ENTROPY_CODING_METHOD_PARTITIONED_RICE2)) + return false; + break; + default: + FLAC__ASSERT(0); + } + + /* decode the subframe */ + if(do_full_decode) { + memcpy(decoder->private_->output[channel], subframe->warmup, sizeof(FLAC__int32) * order); + FLAC__fixed_restore_signal(decoder->private_->residual[channel], decoder->private_->frame.header.blocksize-order, order, decoder->private_->output[channel]+order); + } + + return true; +} + +FLAC__bool read_subframe_lpc_(FLAC__StreamDecoder *decoder, unsigned channel, unsigned bps, const unsigned order, FLAC__bool do_full_decode) +{ + FLAC__Subframe_LPC *subframe = &decoder->private_->frame.subframes[channel].data.lpc; + FLAC__int32 i32; + FLAC__uint32 u32; + unsigned u; + + decoder->private_->frame.subframes[channel].type = FLAC__SUBFRAME_TYPE_LPC; + + subframe->residual = decoder->private_->residual[channel]; + subframe->order = order; + + /* read warm-up samples */ + for(u = 0; u < order; u++) { + if(!FLAC__bitreader_read_raw_int32(decoder->private_->input, &i32, bps)) + return false; /* read_callback_ sets the state for us */ + subframe->warmup[u] = i32; + } + + /* read qlp coeff precision */ + if(!FLAC__bitreader_read_raw_uint32(decoder->private_->input, &u32, FLAC__SUBFRAME_LPC_QLP_COEFF_PRECISION_LEN)) + return false; /* read_callback_ sets the state for us */ + if(u32 == (1u << FLAC__SUBFRAME_LPC_QLP_COEFF_PRECISION_LEN) - 1) { + send_error_to_client_(decoder, FLAC__STREAM_DECODER_ERROR_STATUS_LOST_SYNC); + decoder->protected_->state = FLAC__STREAM_DECODER_SEARCH_FOR_FRAME_SYNC; + return true; + } + subframe->qlp_coeff_precision = u32+1; + + /* read qlp shift */ + if(!FLAC__bitreader_read_raw_int32(decoder->private_->input, &i32, FLAC__SUBFRAME_LPC_QLP_SHIFT_LEN)) + return false; /* read_callback_ sets the state for us */ + if(i32 < 0) { + send_error_to_client_(decoder, FLAC__STREAM_DECODER_ERROR_STATUS_LOST_SYNC); + decoder->protected_->state = FLAC__STREAM_DECODER_SEARCH_FOR_FRAME_SYNC; + return true; + } + subframe->quantization_level = i32; + + /* read quantized lp coefficiencts */ + for(u = 0; u < order; u++) { + if(!FLAC__bitreader_read_raw_int32(decoder->private_->input, &i32, subframe->qlp_coeff_precision)) + return false; /* read_callback_ sets the state for us */ + subframe->qlp_coeff[u] = i32; + } + + /* read entropy coding method info */ + if(!FLAC__bitreader_read_raw_uint32(decoder->private_->input, &u32, FLAC__ENTROPY_CODING_METHOD_TYPE_LEN)) + return false; /* read_callback_ sets the state for us */ + subframe->entropy_coding_method.type = (FLAC__EntropyCodingMethodType)u32; + switch(subframe->entropy_coding_method.type) { + case FLAC__ENTROPY_CODING_METHOD_PARTITIONED_RICE: + case FLAC__ENTROPY_CODING_METHOD_PARTITIONED_RICE2: + if(!FLAC__bitreader_read_raw_uint32(decoder->private_->input, &u32, FLAC__ENTROPY_CODING_METHOD_PARTITIONED_RICE_ORDER_LEN)) + return false; /* read_callback_ sets the state for us */ + if(decoder->private_->frame.header.blocksize >> u32 < order) { + send_error_to_client_(decoder, FLAC__STREAM_DECODER_ERROR_STATUS_LOST_SYNC); + decoder->protected_->state = FLAC__STREAM_DECODER_SEARCH_FOR_FRAME_SYNC; + return true; + } + subframe->entropy_coding_method.data.partitioned_rice.order = u32; + subframe->entropy_coding_method.data.partitioned_rice.contents = &decoder->private_->partitioned_rice_contents[channel]; + break; + default: + send_error_to_client_(decoder, FLAC__STREAM_DECODER_ERROR_STATUS_UNPARSEABLE_STREAM); + decoder->protected_->state = FLAC__STREAM_DECODER_SEARCH_FOR_FRAME_SYNC; + return true; + } + + /* read residual */ + switch(subframe->entropy_coding_method.type) { + case FLAC__ENTROPY_CODING_METHOD_PARTITIONED_RICE: + case FLAC__ENTROPY_CODING_METHOD_PARTITIONED_RICE2: + if(!read_residual_partitioned_rice_(decoder, order, subframe->entropy_coding_method.data.partitioned_rice.order, &decoder->private_->partitioned_rice_contents[channel], decoder->private_->residual[channel], /*is_extended=*/subframe->entropy_coding_method.type == FLAC__ENTROPY_CODING_METHOD_PARTITIONED_RICE2)) + return false; + break; + default: + FLAC__ASSERT(0); + } + + /* decode the subframe */ + if(do_full_decode) { + memcpy(decoder->private_->output[channel], subframe->warmup, sizeof(FLAC__int32) * order); + if(bps + subframe->qlp_coeff_precision + FLAC__bitmath_ilog2(order) <= 32) + if(bps <= 16 && subframe->qlp_coeff_precision <= 16) + decoder->private_->local_lpc_restore_signal_16bit(decoder->private_->residual[channel], decoder->private_->frame.header.blocksize-order, subframe->qlp_coeff, order, subframe->quantization_level, decoder->private_->output[channel]+order); + else + decoder->private_->local_lpc_restore_signal(decoder->private_->residual[channel], decoder->private_->frame.header.blocksize-order, subframe->qlp_coeff, order, subframe->quantization_level, decoder->private_->output[channel]+order); + else + decoder->private_->local_lpc_restore_signal_64bit(decoder->private_->residual[channel], decoder->private_->frame.header.blocksize-order, subframe->qlp_coeff, order, subframe->quantization_level, decoder->private_->output[channel]+order); + } + + return true; +} + +FLAC__bool read_subframe_verbatim_(FLAC__StreamDecoder *decoder, unsigned channel, unsigned bps, FLAC__bool do_full_decode) +{ + FLAC__Subframe_Verbatim *subframe = &decoder->private_->frame.subframes[channel].data.verbatim; + FLAC__int32 x, *residual = decoder->private_->residual[channel]; + unsigned i; + + decoder->private_->frame.subframes[channel].type = FLAC__SUBFRAME_TYPE_VERBATIM; + + subframe->data = residual; + + for(i = 0; i < decoder->private_->frame.header.blocksize; i++) { + if(!FLAC__bitreader_read_raw_int32(decoder->private_->input, &x, bps)) + return false; /* read_callback_ sets the state for us */ + residual[i] = x; + } + + /* decode the subframe */ + if(do_full_decode) + memcpy(decoder->private_->output[channel], subframe->data, sizeof(FLAC__int32) * decoder->private_->frame.header.blocksize); + + return true; +} + +FLAC__bool read_residual_partitioned_rice_(FLAC__StreamDecoder *decoder, unsigned predictor_order, unsigned partition_order, FLAC__EntropyCodingMethod_PartitionedRiceContents *partitioned_rice_contents, FLAC__int32 *residual, FLAC__bool is_extended) +{ + FLAC__uint32 rice_parameter; + int i; + unsigned partition, sample, u; + const unsigned partitions = 1u << partition_order; + const unsigned partition_samples = partition_order > 0? decoder->private_->frame.header.blocksize >> partition_order : decoder->private_->frame.header.blocksize - predictor_order; + const unsigned plen = is_extended? FLAC__ENTROPY_CODING_METHOD_PARTITIONED_RICE2_PARAMETER_LEN : FLAC__ENTROPY_CODING_METHOD_PARTITIONED_RICE_PARAMETER_LEN; + const unsigned pesc = is_extended? FLAC__ENTROPY_CODING_METHOD_PARTITIONED_RICE2_ESCAPE_PARAMETER : FLAC__ENTROPY_CODING_METHOD_PARTITIONED_RICE_ESCAPE_PARAMETER; + + /* invalid predictor and partition orders mush be handled in the callers */ + FLAC__ASSERT(partition_order > 0? partition_samples >= predictor_order : decoder->private_->frame.header.blocksize >= predictor_order); + + if(!FLAC__format_entropy_coding_method_partitioned_rice_contents_ensure_size(partitioned_rice_contents, flac_max(6u, partition_order))) { + decoder->protected_->state = FLAC__STREAM_DECODER_MEMORY_ALLOCATION_ERROR; + return false; + } + + sample = 0; + for(partition = 0; partition < partitions; partition++) { + if(!FLAC__bitreader_read_raw_uint32(decoder->private_->input, &rice_parameter, plen)) + return false; /* read_callback_ sets the state for us */ + partitioned_rice_contents->parameters[partition] = rice_parameter; + if(rice_parameter < pesc) { + partitioned_rice_contents->raw_bits[partition] = 0; + u = (partition_order == 0 || partition > 0)? partition_samples : partition_samples - predictor_order; + if(!FLAC__bitreader_read_rice_signed_block(decoder->private_->input, residual + sample, u, rice_parameter)) + return false; /* read_callback_ sets the state for us */ + sample += u; + } + else { + if(!FLAC__bitreader_read_raw_uint32(decoder->private_->input, &rice_parameter, FLAC__ENTROPY_CODING_METHOD_PARTITIONED_RICE_RAW_LEN)) + return false; /* read_callback_ sets the state for us */ + partitioned_rice_contents->raw_bits[partition] = rice_parameter; + for(u = (partition_order == 0 || partition > 0)? 0 : predictor_order; u < partition_samples; u++, sample++) { + if(!FLAC__bitreader_read_raw_int32(decoder->private_->input, &i, rice_parameter)) + return false; /* read_callback_ sets the state for us */ + residual[sample] = i; + } + } + } + + return true; +} + +FLAC__bool read_zero_padding_(FLAC__StreamDecoder *decoder) +{ + if(!FLAC__bitreader_is_consumed_byte_aligned(decoder->private_->input)) { + FLAC__uint32 zero = 0; + if(!FLAC__bitreader_read_raw_uint32(decoder->private_->input, &zero, FLAC__bitreader_bits_left_for_byte_alignment(decoder->private_->input))) + return false; /* read_callback_ sets the state for us */ + if(zero != 0) { + send_error_to_client_(decoder, FLAC__STREAM_DECODER_ERROR_STATUS_LOST_SYNC); + decoder->protected_->state = FLAC__STREAM_DECODER_SEARCH_FOR_FRAME_SYNC; + } + } + return true; +} + +FLAC__bool read_callback_(FLAC__byte buffer[], size_t *bytes, void *client_data) +{ + FLAC__StreamDecoder *decoder = (FLAC__StreamDecoder *)client_data; + + if( +#if FLAC__HAS_OGG + /* see [1] HACK NOTE below for why we don't call the eof_callback when decoding Ogg FLAC */ + !decoder->private_->is_ogg && +#endif + decoder->private_->eof_callback && decoder->private_->eof_callback(decoder, decoder->private_->client_data) + ) { + *bytes = 0; + decoder->protected_->state = FLAC__STREAM_DECODER_END_OF_STREAM; + return false; + } + else if(*bytes > 0) { + /* While seeking, it is possible for our seek to land in the + * middle of audio data that looks exactly like a frame header + * from a future version of an encoder. When that happens, our + * error callback will get an + * FLAC__STREAM_DECODER_UNPARSEABLE_STREAM and increment its + * unparseable_frame_count. But there is a remote possibility + * that it is properly synced at such a "future-codec frame", + * so to make sure, we wait to see many "unparseable" errors in + * a row before bailing out. + */ + if(decoder->private_->is_seeking && decoder->private_->unparseable_frame_count > 20) { + decoder->protected_->state = FLAC__STREAM_DECODER_ABORTED; + return false; + } + else { + const FLAC__StreamDecoderReadStatus status = +#if FLAC__HAS_OGG + decoder->private_->is_ogg? + read_callback_ogg_aspect_(decoder, buffer, bytes) : +#endif + decoder->private_->read_callback(decoder, buffer, bytes, decoder->private_->client_data) + ; + if(status == FLAC__STREAM_DECODER_READ_STATUS_ABORT) { + decoder->protected_->state = FLAC__STREAM_DECODER_ABORTED; + return false; + } + else if(*bytes == 0) { + if( + status == FLAC__STREAM_DECODER_READ_STATUS_END_OF_STREAM || + ( +#if FLAC__HAS_OGG + /* see [1] HACK NOTE below for why we don't call the eof_callback when decoding Ogg FLAC */ + !decoder->private_->is_ogg && +#endif + decoder->private_->eof_callback && decoder->private_->eof_callback(decoder, decoder->private_->client_data) + ) + ) { + decoder->protected_->state = FLAC__STREAM_DECODER_END_OF_STREAM; + return false; + } + else + return true; + } + else + return true; + } + } + else { + /* abort to avoid a deadlock */ + decoder->protected_->state = FLAC__STREAM_DECODER_ABORTED; + return false; + } + /* [1] @@@ HACK NOTE: The end-of-stream checking has to be hacked around + * for Ogg FLAC. This is because the ogg decoder aspect can lose sync + * and at the same time hit the end of the stream (for example, seeking + * to a point that is after the beginning of the last Ogg page). There + * is no way to report an Ogg sync loss through the callbacks (see note + * in read_callback_ogg_aspect_()) so it returns CONTINUE with *bytes==0. + * So to keep the decoder from stopping at this point we gate the call + * to the eof_callback and let the Ogg decoder aspect set the + * end-of-stream state when it is needed. + */ +} + +#if FLAC__HAS_OGG +FLAC__StreamDecoderReadStatus read_callback_ogg_aspect_(const FLAC__StreamDecoder *decoder, FLAC__byte buffer[], size_t *bytes) +{ + switch(FLAC__ogg_decoder_aspect_read_callback_wrapper(&decoder->protected_->ogg_decoder_aspect, buffer, bytes, read_callback_proxy_, decoder, decoder->private_->client_data)) { + case FLAC__OGG_DECODER_ASPECT_READ_STATUS_OK: + return FLAC__STREAM_DECODER_READ_STATUS_CONTINUE; + /* we don't really have a way to handle lost sync via read + * callback so we'll let it pass and let the underlying + * FLAC decoder catch the error + */ + case FLAC__OGG_DECODER_ASPECT_READ_STATUS_LOST_SYNC: + return FLAC__STREAM_DECODER_READ_STATUS_CONTINUE; + case FLAC__OGG_DECODER_ASPECT_READ_STATUS_END_OF_STREAM: + return FLAC__STREAM_DECODER_READ_STATUS_END_OF_STREAM; + case FLAC__OGG_DECODER_ASPECT_READ_STATUS_NOT_FLAC: + case FLAC__OGG_DECODER_ASPECT_READ_STATUS_UNSUPPORTED_MAPPING_VERSION: + case FLAC__OGG_DECODER_ASPECT_READ_STATUS_ABORT: + case FLAC__OGG_DECODER_ASPECT_READ_STATUS_ERROR: + case FLAC__OGG_DECODER_ASPECT_READ_STATUS_MEMORY_ALLOCATION_ERROR: + return FLAC__STREAM_DECODER_READ_STATUS_ABORT; + default: + FLAC__ASSERT(0); + /* double protection */ + return FLAC__STREAM_DECODER_READ_STATUS_ABORT; + } +} + +FLAC__OggDecoderAspectReadStatus read_callback_proxy_(const void *void_decoder, FLAC__byte buffer[], size_t *bytes, void *client_data) +{ + FLAC__StreamDecoder *decoder = (FLAC__StreamDecoder*)void_decoder; + + switch(decoder->private_->read_callback(decoder, buffer, bytes, client_data)) { + case FLAC__STREAM_DECODER_READ_STATUS_CONTINUE: + return FLAC__OGG_DECODER_ASPECT_READ_STATUS_OK; + case FLAC__STREAM_DECODER_READ_STATUS_END_OF_STREAM: + return FLAC__OGG_DECODER_ASPECT_READ_STATUS_END_OF_STREAM; + case FLAC__STREAM_DECODER_READ_STATUS_ABORT: + return FLAC__OGG_DECODER_ASPECT_READ_STATUS_ABORT; + default: + /* double protection: */ + FLAC__ASSERT(0); + return FLAC__OGG_DECODER_ASPECT_READ_STATUS_ABORT; + } +} +#endif + +FLAC__StreamDecoderWriteStatus write_audio_frame_to_client_(FLAC__StreamDecoder *decoder, const FLAC__Frame *frame, const FLAC__int32 * const buffer[]) +{ + if(decoder->private_->is_seeking) { + FLAC__uint64 this_frame_sample = frame->header.number.sample_number; + FLAC__uint64 next_frame_sample = this_frame_sample + (FLAC__uint64)frame->header.blocksize; + FLAC__uint64 target_sample = decoder->private_->target_sample; + + FLAC__ASSERT(frame->header.number_type == FLAC__FRAME_NUMBER_TYPE_SAMPLE_NUMBER); + +#if FLAC__HAS_OGG + decoder->private_->got_a_frame = true; +#endif + decoder->private_->last_frame = *frame; /* save the frame */ + if(this_frame_sample <= target_sample && target_sample < next_frame_sample) { /* we hit our target frame */ + unsigned delta = (unsigned)(target_sample - this_frame_sample); + /* kick out of seek mode */ + decoder->private_->is_seeking = false; + /* shift out the samples before target_sample */ + if(delta > 0) { + unsigned channel; + const FLAC__int32 *newbuffer[FLAC__MAX_CHANNELS]; + for(channel = 0; channel < frame->header.channels; channel++) + newbuffer[channel] = buffer[channel] + delta; + decoder->private_->last_frame.header.blocksize -= delta; + decoder->private_->last_frame.header.number.sample_number += (FLAC__uint64)delta; + /* write the relevant samples */ + return decoder->private_->write_callback(decoder, &decoder->private_->last_frame, newbuffer, decoder->private_->client_data); + } + else { + /* write the relevant samples */ + return decoder->private_->write_callback(decoder, frame, buffer, decoder->private_->client_data); + } + } + else { + return FLAC__STREAM_DECODER_WRITE_STATUS_CONTINUE; + } + } + else { + /* + * If we never got STREAMINFO, turn off MD5 checking to save + * cycles since we don't have a sum to compare to anyway + */ + if(!decoder->private_->has_stream_info) + decoder->private_->do_md5_checking = false; + if(decoder->private_->do_md5_checking) { + if(!FLAC__MD5Accumulate(&decoder->private_->md5context, buffer, frame->header.channels, frame->header.blocksize, (frame->header.bits_per_sample+7) / 8)) + return FLAC__STREAM_DECODER_WRITE_STATUS_ABORT; + } + return decoder->private_->write_callback(decoder, frame, buffer, decoder->private_->client_data); + } +} + +void send_error_to_client_(const FLAC__StreamDecoder *decoder, FLAC__StreamDecoderErrorStatus status) +{ + if(!decoder->private_->is_seeking) + decoder->private_->error_callback(decoder, status, decoder->private_->client_data); + else if(status == FLAC__STREAM_DECODER_ERROR_STATUS_UNPARSEABLE_STREAM) + decoder->private_->unparseable_frame_count++; +} + +FLAC__bool seek_to_absolute_sample_(FLAC__StreamDecoder *decoder, FLAC__uint64 stream_length, FLAC__uint64 target_sample) +{ + FLAC__uint64 first_frame_offset = decoder->private_->first_frame_offset, lower_bound, upper_bound, lower_bound_sample, upper_bound_sample, this_frame_sample; + FLAC__int64 pos = -1; + int i; + unsigned approx_bytes_per_frame; + FLAC__bool first_seek = true; + const FLAC__uint64 total_samples = FLAC__stream_decoder_get_total_samples(decoder); + const unsigned min_blocksize = decoder->private_->stream_info.data.stream_info.min_blocksize; + const unsigned max_blocksize = decoder->private_->stream_info.data.stream_info.max_blocksize; + const unsigned max_framesize = decoder->private_->stream_info.data.stream_info.max_framesize; + const unsigned min_framesize = decoder->private_->stream_info.data.stream_info.min_framesize; + /* take these from the current frame in case they've changed mid-stream */ + unsigned channels = FLAC__stream_decoder_get_channels(decoder); + unsigned bps = FLAC__stream_decoder_get_bits_per_sample(decoder); + const FLAC__StreamMetadata_SeekTable *seek_table = decoder->private_->has_seek_table? &decoder->private_->seek_table.data.seek_table : 0; + + /* use values from stream info if we didn't decode a frame */ + if(channels == 0) + channels = decoder->private_->stream_info.data.stream_info.channels; + if(bps == 0) + bps = decoder->private_->stream_info.data.stream_info.bits_per_sample; + + /* we are just guessing here */ + if(max_framesize > 0) + approx_bytes_per_frame = (max_framesize + min_framesize) / 2 + 1; + /* + * Check if it's a known fixed-blocksize stream. Note that though + * the spec doesn't allow zeroes in the STREAMINFO block, we may + * never get a STREAMINFO block when decoding so the value of + * min_blocksize might be zero. + */ + else if(min_blocksize == max_blocksize && min_blocksize > 0) { + /* note there are no () around 'bps/8' to keep precision up since it's an integer calulation */ + approx_bytes_per_frame = min_blocksize * channels * bps/8 + 64; + } + else + approx_bytes_per_frame = 4096 * channels * bps/8 + 64; + + /* + * First, we set an upper and lower bound on where in the + * stream we will search. For now we assume the worst case + * scenario, which is our best guess at the beginning of + * the first frame and end of the stream. + */ + lower_bound = first_frame_offset; + lower_bound_sample = 0; + upper_bound = stream_length; + upper_bound_sample = total_samples > 0 ? total_samples : target_sample /*estimate it*/; + + /* + * Now we refine the bounds if we have a seektable with + * suitable points. Note that according to the spec they + * must be ordered by ascending sample number. + * + * Note: to protect against invalid seek tables we will ignore points + * that have frame_samples==0 or sample_number>=total_samples + */ + if(seek_table) { + FLAC__uint64 new_lower_bound = lower_bound; + FLAC__uint64 new_upper_bound = upper_bound; + FLAC__uint64 new_lower_bound_sample = lower_bound_sample; + FLAC__uint64 new_upper_bound_sample = upper_bound_sample; + + /* find the closest seek point <= target_sample, if it exists */ + for(i = (int)seek_table->num_points - 1; i >= 0; i--) { + if( + seek_table->points[i].sample_number != FLAC__STREAM_METADATA_SEEKPOINT_PLACEHOLDER && + seek_table->points[i].frame_samples > 0 && /* defense against bad seekpoints */ + (total_samples <= 0 || seek_table->points[i].sample_number < total_samples) && /* defense against bad seekpoints */ + seek_table->points[i].sample_number <= target_sample + ) + break; + } + if(i >= 0) { /* i.e. we found a suitable seek point... */ + new_lower_bound = first_frame_offset + seek_table->points[i].stream_offset; + new_lower_bound_sample = seek_table->points[i].sample_number; + } + + /* find the closest seek point > target_sample, if it exists */ + for(i = 0; i < (int)seek_table->num_points; i++) { + if( + seek_table->points[i].sample_number != FLAC__STREAM_METADATA_SEEKPOINT_PLACEHOLDER && + seek_table->points[i].frame_samples > 0 && /* defense against bad seekpoints */ + (total_samples <= 0 || seek_table->points[i].sample_number < total_samples) && /* defense against bad seekpoints */ + seek_table->points[i].sample_number > target_sample + ) + break; + } + if(i < (int)seek_table->num_points) { /* i.e. we found a suitable seek point... */ + new_upper_bound = first_frame_offset + seek_table->points[i].stream_offset; + new_upper_bound_sample = seek_table->points[i].sample_number; + } + /* final protection against unsorted seek tables; keep original values if bogus */ + if(new_upper_bound >= new_lower_bound) { + lower_bound = new_lower_bound; + upper_bound = new_upper_bound; + lower_bound_sample = new_lower_bound_sample; + upper_bound_sample = new_upper_bound_sample; + } + } + + FLAC__ASSERT(upper_bound_sample >= lower_bound_sample); + /* there are 2 insidious ways that the following equality occurs, which + * we need to fix: + * 1) total_samples is 0 (unknown) and target_sample is 0 + * 2) total_samples is 0 (unknown) and target_sample happens to be + * exactly equal to the last seek point in the seek table; this + * means there is no seek point above it, and upper_bound_samples + * remains equal to the estimate (of target_samples) we made above + * in either case it does not hurt to move upper_bound_sample up by 1 + */ + if(upper_bound_sample == lower_bound_sample) + upper_bound_sample++; + + decoder->private_->target_sample = target_sample; + while(1) { + /* check if the bounds are still ok */ + if (lower_bound_sample >= upper_bound_sample || lower_bound > upper_bound) { + decoder->protected_->state = FLAC__STREAM_DECODER_SEEK_ERROR; + return false; + } +#ifndef FLAC__INTEGER_ONLY_LIBRARY + pos = (FLAC__int64)lower_bound + (FLAC__int64)((double)(target_sample - lower_bound_sample) / (double)(upper_bound_sample - lower_bound_sample) * (double)(upper_bound - lower_bound)) - approx_bytes_per_frame; +#else + /* a little less accurate: */ + if(upper_bound - lower_bound < 0xffffffff) + pos = (FLAC__int64)lower_bound + (FLAC__int64)(((target_sample - lower_bound_sample) * (upper_bound - lower_bound)) / (upper_bound_sample - lower_bound_sample)) - approx_bytes_per_frame; + else /* @@@ WATCHOUT, ~2TB limit */ + pos = (FLAC__int64)lower_bound + (FLAC__int64)((((target_sample - lower_bound_sample)>>8) * ((upper_bound - lower_bound)>>8)) / ((upper_bound_sample - lower_bound_sample)>>16)) - approx_bytes_per_frame; +#endif + if(pos >= (FLAC__int64)upper_bound) + pos = (FLAC__int64)upper_bound - 1; + if(pos < (FLAC__int64)lower_bound) + pos = (FLAC__int64)lower_bound; + if(decoder->private_->seek_callback(decoder, (FLAC__uint64)pos, decoder->private_->client_data) != FLAC__STREAM_DECODER_SEEK_STATUS_OK) { + decoder->protected_->state = FLAC__STREAM_DECODER_SEEK_ERROR; + return false; + } + if(!FLAC__stream_decoder_flush(decoder)) { + /* above call sets the state for us */ + return false; + } + /* Now we need to get a frame. First we need to reset our + * unparseable_frame_count; if we get too many unparseable + * frames in a row, the read callback will return + * FLAC__STREAM_DECODER_READ_STATUS_ABORT, causing + * FLAC__stream_decoder_process_single() to return false. + */ + decoder->private_->unparseable_frame_count = 0; + if(!FLAC__stream_decoder_process_single(decoder) || + decoder->protected_->state == FLAC__STREAM_DECODER_ABORTED) { + decoder->protected_->state = FLAC__STREAM_DECODER_SEEK_ERROR; + return false; + } + /* our write callback will change the state when it gets to the target frame */ + /* actually, we could have got_a_frame if our decoder is at FLAC__STREAM_DECODER_END_OF_STREAM so we need to check for that also */ +#if 0 + /*@@@@@@ used to be the following; not clear if the check for end of stream is needed anymore */ + if(decoder->protected_->state != FLAC__SEEKABLE_STREAM_DECODER_SEEKING && decoder->protected_->state != FLAC__STREAM_DECODER_END_OF_STREAM) + break; +#endif + if(!decoder->private_->is_seeking) + break; + + FLAC__ASSERT(decoder->private_->last_frame.header.number_type == FLAC__FRAME_NUMBER_TYPE_SAMPLE_NUMBER); + this_frame_sample = decoder->private_->last_frame.header.number.sample_number; + + if (0 == decoder->private_->samples_decoded || (this_frame_sample + decoder->private_->last_frame.header.blocksize >= upper_bound_sample && !first_seek)) { + if (pos == (FLAC__int64)lower_bound) { + /* can't move back any more than the first frame, something is fatally wrong */ + decoder->protected_->state = FLAC__STREAM_DECODER_SEEK_ERROR; + return false; + } + /* our last move backwards wasn't big enough, try again */ + approx_bytes_per_frame = approx_bytes_per_frame? approx_bytes_per_frame * 2 : 16; + continue; + } + /* allow one seek over upper bound, so we can get a correct upper_bound_sample for streams with unknown total_samples */ + first_seek = false; + + /* make sure we are not seeking in corrupted stream */ + if (this_frame_sample < lower_bound_sample) { + decoder->protected_->state = FLAC__STREAM_DECODER_SEEK_ERROR; + return false; + } + + /* we need to narrow the search */ + if(target_sample < this_frame_sample) { + upper_bound_sample = this_frame_sample + decoder->private_->last_frame.header.blocksize; +/*@@@@@@ what will decode position be if at end of stream? */ + if(!FLAC__stream_decoder_get_decode_position(decoder, &upper_bound)) { + decoder->protected_->state = FLAC__STREAM_DECODER_SEEK_ERROR; + return false; + } + approx_bytes_per_frame = (unsigned)(2 * (upper_bound - pos) / 3 + 16); + } + else { /* target_sample >= this_frame_sample + this frame's blocksize */ + lower_bound_sample = this_frame_sample + decoder->private_->last_frame.header.blocksize; + if(!FLAC__stream_decoder_get_decode_position(decoder, &lower_bound)) { + decoder->protected_->state = FLAC__STREAM_DECODER_SEEK_ERROR; + return false; + } + approx_bytes_per_frame = (unsigned)(2 * (lower_bound - pos) / 3 + 16); + } + } + + return true; +} + +#if FLAC__HAS_OGG +FLAC__bool seek_to_absolute_sample_ogg_(FLAC__StreamDecoder *decoder, FLAC__uint64 stream_length, FLAC__uint64 target_sample) +{ + FLAC__uint64 left_pos = 0, right_pos = stream_length; + FLAC__uint64 left_sample = 0, right_sample = FLAC__stream_decoder_get_total_samples(decoder); + FLAC__uint64 this_frame_sample = (FLAC__uint64)0 - 1; + FLAC__uint64 pos = 0; /* only initialized to avoid compiler warning */ + FLAC__bool did_a_seek; + unsigned iteration = 0; + + /* In the first iterations, we will calculate the target byte position + * by the distance from the target sample to left_sample and + * right_sample (let's call it "proportional search"). After that, we + * will switch to binary search. + */ + unsigned BINARY_SEARCH_AFTER_ITERATION = 2; + + /* We will switch to a linear search once our current sample is less + * than this number of samples ahead of the target sample + */ + static const FLAC__uint64 LINEAR_SEARCH_WITHIN_SAMPLES = FLAC__MAX_BLOCK_SIZE * 2; + + /* If the total number of samples is unknown, use a large value, and + * force binary search immediately. + */ + if(right_sample == 0) { + right_sample = (FLAC__uint64)(-1); + BINARY_SEARCH_AFTER_ITERATION = 0; + } + + decoder->private_->target_sample = target_sample; + for( ; ; iteration++) { + if (iteration == 0 || this_frame_sample > target_sample || target_sample - this_frame_sample > LINEAR_SEARCH_WITHIN_SAMPLES) { + if (iteration >= BINARY_SEARCH_AFTER_ITERATION) { + pos = (right_pos + left_pos) / 2; + } + else { +#ifndef FLAC__INTEGER_ONLY_LIBRARY + pos = (FLAC__uint64)((double)(target_sample - left_sample) / (double)(right_sample - left_sample) * (double)(right_pos - left_pos)); +#else + /* a little less accurate: */ + if ((target_sample-left_sample <= 0xffffffff) && (right_pos-left_pos <= 0xffffffff)) + pos = (FLAC__int64)(((target_sample-left_sample) * (right_pos-left_pos)) / (right_sample-left_sample)); + else /* @@@ WATCHOUT, ~2TB limit */ + pos = (FLAC__int64)((((target_sample-left_sample)>>8) * ((right_pos-left_pos)>>8)) / ((right_sample-left_sample)>>16)); +#endif + /* @@@ TODO: might want to limit pos to some distance + * before EOF, to make sure we land before the last frame, + * thereby getting a this_frame_sample and so having a better + * estimate. + */ + } + + /* physical seek */ + if(decoder->private_->seek_callback((FLAC__StreamDecoder*)decoder, (FLAC__uint64)pos, decoder->private_->client_data) != FLAC__STREAM_DECODER_SEEK_STATUS_OK) { + decoder->protected_->state = FLAC__STREAM_DECODER_SEEK_ERROR; + return false; + } + if(!FLAC__stream_decoder_flush(decoder)) { + /* above call sets the state for us */ + return false; + } + did_a_seek = true; + } + else + did_a_seek = false; + + decoder->private_->got_a_frame = false; + if(!FLAC__stream_decoder_process_single(decoder) || + decoder->protected_->state == FLAC__STREAM_DECODER_ABORTED) { + decoder->protected_->state = FLAC__STREAM_DECODER_SEEK_ERROR; + return false; + } + if(!decoder->private_->got_a_frame) { + if(did_a_seek) { + /* this can happen if we seek to a point after the last frame; we drop + * to binary search right away in this case to avoid any wasted + * iterations of proportional search. + */ + right_pos = pos; + BINARY_SEARCH_AFTER_ITERATION = 0; + } + else { + /* this can probably only happen if total_samples is unknown and the + * target_sample is past the end of the stream + */ + decoder->protected_->state = FLAC__STREAM_DECODER_SEEK_ERROR; + return false; + } + } + /* our write callback will change the state when it gets to the target frame */ + else if(!decoder->private_->is_seeking) { + break; + } + else { + this_frame_sample = decoder->private_->last_frame.header.number.sample_number; + FLAC__ASSERT(decoder->private_->last_frame.header.number_type == FLAC__FRAME_NUMBER_TYPE_SAMPLE_NUMBER); + + if (did_a_seek) { + if (this_frame_sample <= target_sample) { + /* The 'equal' case should not happen, since + * FLAC__stream_decoder_process_single() + * should recognize that it has hit the + * target sample and we would exit through + * the 'break' above. + */ + FLAC__ASSERT(this_frame_sample != target_sample); + + left_sample = this_frame_sample; + /* sanity check to avoid infinite loop */ + if (left_pos == pos) { + decoder->protected_->state = FLAC__STREAM_DECODER_SEEK_ERROR; + return false; + } + left_pos = pos; + } + else if(this_frame_sample > target_sample) { + right_sample = this_frame_sample; + /* sanity check to avoid infinite loop */ + if (right_pos == pos) { + decoder->protected_->state = FLAC__STREAM_DECODER_SEEK_ERROR; + return false; + } + right_pos = pos; + } + } + } + } + + return true; +} +#endif + +FLAC__StreamDecoderReadStatus file_read_callback_(const FLAC__StreamDecoder *decoder, FLAC__byte buffer[], size_t *bytes, void *client_data) +{ + (void)client_data; + + if(*bytes > 0) { + *bytes = fread(buffer, sizeof(FLAC__byte), *bytes, decoder->private_->file); + if(ferror(decoder->private_->file)) + return FLAC__STREAM_DECODER_READ_STATUS_ABORT; + else if(*bytes == 0) + return FLAC__STREAM_DECODER_READ_STATUS_END_OF_STREAM; + else + return FLAC__STREAM_DECODER_READ_STATUS_CONTINUE; + } + else + return FLAC__STREAM_DECODER_READ_STATUS_ABORT; /* abort to avoid a deadlock */ +} + +FLAC__StreamDecoderSeekStatus file_seek_callback_(const FLAC__StreamDecoder *decoder, FLAC__uint64 absolute_byte_offset, void *client_data) +{ + (void)client_data; + + if(decoder->private_->file == stdin) + return FLAC__STREAM_DECODER_SEEK_STATUS_UNSUPPORTED; + else if(fseeko(decoder->private_->file, (FLAC__off_t)absolute_byte_offset, SEEK_SET) < 0) + return FLAC__STREAM_DECODER_SEEK_STATUS_ERROR; + else + return FLAC__STREAM_DECODER_SEEK_STATUS_OK; +} + +FLAC__StreamDecoderTellStatus file_tell_callback_(const FLAC__StreamDecoder *decoder, FLAC__uint64 *absolute_byte_offset, void *client_data) +{ + FLAC__off_t pos; + (void)client_data; + + if(decoder->private_->file == stdin) + return FLAC__STREAM_DECODER_TELL_STATUS_UNSUPPORTED; + else if((pos = ftello(decoder->private_->file)) < 0) + return FLAC__STREAM_DECODER_TELL_STATUS_ERROR; + else { + *absolute_byte_offset = (FLAC__uint64)pos; + return FLAC__STREAM_DECODER_TELL_STATUS_OK; + } +} + +FLAC__StreamDecoderLengthStatus file_length_callback_(const FLAC__StreamDecoder *decoder, FLAC__uint64 *stream_length, void *client_data) +{ + struct flac_stat_s filestats; + (void)client_data; + + if(decoder->private_->file == stdin) + return FLAC__STREAM_DECODER_LENGTH_STATUS_UNSUPPORTED; + else if(flac_fstat(fileno(decoder->private_->file), &filestats) != 0) + return FLAC__STREAM_DECODER_LENGTH_STATUS_ERROR; + else { + *stream_length = (FLAC__uint64)filestats.st_size; + return FLAC__STREAM_DECODER_LENGTH_STATUS_OK; + } +} + +FLAC__bool file_eof_callback_(const FLAC__StreamDecoder *decoder, void *client_data) +{ + (void)client_data; + + return feof(decoder->private_->file)? true : false; +} diff --git a/core/deps/flac/src/libFLAC/window.c b/core/deps/flac/src/libFLAC/window.c new file mode 100644 index 000000000..e977fd862 --- /dev/null +++ b/core/deps/flac/src/libFLAC/window.c @@ -0,0 +1,282 @@ +/* libFLAC - Free Lossless Audio Codec library + * Copyright (C) 2006-2009 Josh Coalson + * Copyright (C) 2011-2016 Xiph.Org Foundation + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * - Neither the name of the Xiph.org Foundation nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifdef HAVE_CONFIG_H +# include +#endif + +#include +#include "share/compat.h" +#include "FLAC/assert.h" +#include "FLAC/format.h" +#include "private/window.h" + +#ifndef FLAC__INTEGER_ONLY_LIBRARY + + +void FLAC__window_bartlett(FLAC__real *window, const FLAC__int32 L) +{ + const FLAC__int32 N = L - 1; + FLAC__int32 n; + + if (L & 1) { + for (n = 0; n <= N/2; n++) + window[n] = 2.0f * n / (float)N; + for (; n <= N; n++) + window[n] = 2.0f - 2.0f * n / (float)N; + } + else { + for (n = 0; n <= L/2-1; n++) + window[n] = 2.0f * n / (float)N; + for (; n <= N; n++) + window[n] = 2.0f - 2.0f * n / (float)N; + } +} + +void FLAC__window_bartlett_hann(FLAC__real *window, const FLAC__int32 L) +{ + const FLAC__int32 N = L - 1; + FLAC__int32 n; + + for (n = 0; n < L; n++) + window[n] = (FLAC__real)(0.62f - 0.48f * fabs((float)n/(float)N-0.5f) - 0.38f * cos(2.0f * M_PI * ((float)n/(float)N))); +} + +void FLAC__window_blackman(FLAC__real *window, const FLAC__int32 L) +{ + const FLAC__int32 N = L - 1; + FLAC__int32 n; + + for (n = 0; n < L; n++) + window[n] = (FLAC__real)(0.42f - 0.5f * cos(2.0f * M_PI * n / N) + 0.08f * cos(4.0f * M_PI * n / N)); +} + +/* 4-term -92dB side-lobe */ +void FLAC__window_blackman_harris_4term_92db_sidelobe(FLAC__real *window, const FLAC__int32 L) +{ + const FLAC__int32 N = L - 1; + FLAC__int32 n; + + for (n = 0; n <= N; n++) + window[n] = (FLAC__real)(0.35875f - 0.48829f * cos(2.0f * M_PI * n / N) + 0.14128f * cos(4.0f * M_PI * n / N) - 0.01168f * cos(6.0f * M_PI * n / N)); +} + +void FLAC__window_connes(FLAC__real *window, const FLAC__int32 L) +{ + const FLAC__int32 N = L - 1; + const double N2 = (double)N / 2.; + FLAC__int32 n; + + for (n = 0; n <= N; n++) { + double k = ((double)n - N2) / N2; + k = 1.0f - k * k; + window[n] = (FLAC__real)(k * k); + } +} + +void FLAC__window_flattop(FLAC__real *window, const FLAC__int32 L) +{ + const FLAC__int32 N = L - 1; + FLAC__int32 n; + + for (n = 0; n < L; n++) + window[n] = (FLAC__real)(1.0f - 1.93f * cos(2.0f * M_PI * n / N) + 1.29f * cos(4.0f * M_PI * n / N) - 0.388f * cos(6.0f * M_PI * n / N) + 0.0322f * cos(8.0f * M_PI * n / N)); +} + +void FLAC__window_gauss(FLAC__real *window, const FLAC__int32 L, const FLAC__real stddev) +{ + const FLAC__int32 N = L - 1; + const double N2 = (double)N / 2.; + FLAC__int32 n; + + for (n = 0; n <= N; n++) { + const double k = ((double)n - N2) / (stddev * N2); + window[n] = (FLAC__real)exp(-0.5f * k * k); + } +} + +void FLAC__window_hamming(FLAC__real *window, const FLAC__int32 L) +{ + const FLAC__int32 N = L - 1; + FLAC__int32 n; + + for (n = 0; n < L; n++) + window[n] = (FLAC__real)(0.54f - 0.46f * cos(2.0f * M_PI * n / N)); +} + +void FLAC__window_hann(FLAC__real *window, const FLAC__int32 L) +{ + const FLAC__int32 N = L - 1; + FLAC__int32 n; + + for (n = 0; n < L; n++) + window[n] = (FLAC__real)(0.5f - 0.5f * cos(2.0f * M_PI * n / N)); +} + +void FLAC__window_kaiser_bessel(FLAC__real *window, const FLAC__int32 L) +{ + const FLAC__int32 N = L - 1; + FLAC__int32 n; + + for (n = 0; n < L; n++) + window[n] = (FLAC__real)(0.402f - 0.498f * cos(2.0f * M_PI * n / N) + 0.098f * cos(4.0f * M_PI * n / N) - 0.001f * cos(6.0f * M_PI * n / N)); +} + +void FLAC__window_nuttall(FLAC__real *window, const FLAC__int32 L) +{ + const FLAC__int32 N = L - 1; + FLAC__int32 n; + + for (n = 0; n < L; n++) + window[n] = (FLAC__real)(0.3635819f - 0.4891775f*cos(2.0f*M_PI*n/N) + 0.1365995f*cos(4.0f*M_PI*n/N) - 0.0106411f*cos(6.0f*M_PI*n/N)); +} + +void FLAC__window_rectangle(FLAC__real *window, const FLAC__int32 L) +{ + FLAC__int32 n; + + for (n = 0; n < L; n++) + window[n] = 1.0f; +} + +void FLAC__window_triangle(FLAC__real *window, const FLAC__int32 L) +{ + FLAC__int32 n; + + if (L & 1) { + for (n = 1; n <= (L+1)/2; n++) + window[n-1] = 2.0f * n / ((float)L + 1.0f); + for (; n <= L; n++) + window[n-1] = (float)(2 * (L - n + 1)) / ((float)L + 1.0f); + } + else { + for (n = 1; n <= L/2; n++) + window[n-1] = 2.0f * n / ((float)L + 1.0f); + for (; n <= L; n++) + window[n-1] = (float)(2 * (L - n + 1)) / ((float)L + 1.0f); + } +} + +void FLAC__window_tukey(FLAC__real *window, const FLAC__int32 L, const FLAC__real p) +{ + if (p <= 0.0) + FLAC__window_rectangle(window, L); + else if (p >= 1.0) + FLAC__window_hann(window, L); + else { + const FLAC__int32 Np = (FLAC__int32)(p / 2.0f * L) - 1; + FLAC__int32 n; + /* start with rectangle... */ + FLAC__window_rectangle(window, L); + /* ...replace ends with hann */ + if (Np > 0) { + for (n = 0; n <= Np; n++) { + window[n] = (FLAC__real)(0.5f - 0.5f * cos(M_PI * n / Np)); + window[L-Np-1+n] = (FLAC__real)(0.5f - 0.5f * cos(M_PI * (n+Np) / Np)); + } + } + } +} + +void FLAC__window_partial_tukey(FLAC__real *window, const FLAC__int32 L, const FLAC__real p, const FLAC__real start, const FLAC__real end) +{ + const FLAC__int32 start_n = (FLAC__int32)(start * L); + const FLAC__int32 end_n = (FLAC__int32)(end * L); + const FLAC__int32 N = end_n - start_n; + FLAC__int32 Np, n, i; + + if (p <= 0.0f) + FLAC__window_partial_tukey(window, L, 0.05f, start, end); + else if (p >= 1.0f) + FLAC__window_partial_tukey(window, L, 0.95f, start, end); + else { + + Np = (FLAC__int32)(p / 2.0f * N); + + for (n = 0; n < start_n && n < L; n++) + window[n] = 0.0f; + for (i = 1; n < (start_n+Np) && n < L; n++, i++) + window[n] = (FLAC__real)(0.5f - 0.5f * cos(M_PI * i / Np)); + for (; n < (end_n-Np) && n < L; n++) + window[n] = 1.0f; + for (i = Np; n < end_n && n < L; n++, i--) + window[n] = (FLAC__real)(0.5f - 0.5f * cos(M_PI * i / Np)); + for (; n < L; n++) + window[n] = 0.0f; + } +} + +void FLAC__window_punchout_tukey(FLAC__real *window, const FLAC__int32 L, const FLAC__real p, const FLAC__real start, const FLAC__real end) +{ + const FLAC__int32 start_n = (FLAC__int32)(start * L); + const FLAC__int32 end_n = (FLAC__int32)(end * L); + FLAC__int32 Ns, Ne, n, i; + + if (p <= 0.0f) + FLAC__window_punchout_tukey(window, L, 0.05f, start, end); + else if (p >= 1.0f) + FLAC__window_punchout_tukey(window, L, 0.95f, start, end); + else { + + Ns = (FLAC__int32)(p / 2.0f * start_n); + Ne = (FLAC__int32)(p / 2.0f * (L - end_n)); + + for (n = 0, i = 1; n < Ns && n < L; n++, i++) + window[n] = (FLAC__real)(0.5f - 0.5f * cos(M_PI * i / Ns)); + for (; n < start_n-Ns && n < L; n++) + window[n] = 1.0f; + for (i = Ns; n < start_n && n < L; n++, i--) + window[n] = (FLAC__real)(0.5f - 0.5f * cos(M_PI * i / Ns)); + for (; n < end_n && n < L; n++) + window[n] = 0.0f; + for (i = 1; n < end_n+Ne && n < L; n++, i++) + window[n] = (FLAC__real)(0.5f - 0.5f * cos(M_PI * i / Ne)); + for (; n < L - (Ne) && n < L; n++) + window[n] = 1.0f; + for (i = Ne; n < L; n++, i--) + window[n] = (FLAC__real)(0.5f - 0.5f * cos(M_PI * i / Ne)); + } +} + +void FLAC__window_welch(FLAC__real *window, const FLAC__int32 L) +{ + const FLAC__int32 N = L - 1; + const double N2 = (double)N / 2.; + FLAC__int32 n; + + for (n = 0; n <= N; n++) { + const double k = ((double)n - N2) / N2; + window[n] = (FLAC__real)(1.0f - k * k); + } +} + +#endif /* !defined FLAC__INTEGER_ONLY_LIBRARY */ diff --git a/core/deps/flac/src/libFLAC/windows_unicode_filenames.c b/core/deps/flac/src/libFLAC/windows_unicode_filenames.c new file mode 100644 index 000000000..2404e3193 --- /dev/null +++ b/core/deps/flac/src/libFLAC/windows_unicode_filenames.c @@ -0,0 +1,201 @@ +/* libFLAC - Free Lossless Audio Codec library + * Copyright (C) 2013-2016 Xiph.Org Foundation + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * - Neither the name of the Xiph.org Foundation nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifdef HAVE_CONFIG_H +# include +#endif + +#include +#include "share/windows_unicode_filenames.h" + +/* convert UTF-8 back to WCHAR. Caller is responsible for freeing memory */ +static wchar_t *wchar_from_utf8(const char *str) +{ + wchar_t *widestr; + int len; + + if (!str) + return NULL; + if ((len = MultiByteToWideChar(CP_UTF8, 0, str, -1, NULL, 0)) == 0) + return NULL; + if ((widestr = (wchar_t *)malloc(len*sizeof(wchar_t))) == NULL) + return NULL; + if (MultiByteToWideChar(CP_UTF8, 0, str, -1, widestr, len) == 0) { + free(widestr); + widestr = NULL; + } + + return widestr; +} + + +static FLAC__bool utf8_filenames = false; + + +void flac_internal_set_utf8_filenames(FLAC__bool flag) +{ + utf8_filenames = flag ? true : false; +} + +FLAC__bool flac_internal_get_utf8_filenames(void) +{ + return utf8_filenames; +} + +/* file functions */ + +FILE* flac_internal_fopen_utf8(const char *filename, const char *mode) +{ + if (!utf8_filenames) { + return fopen(filename, mode); + } else { + wchar_t *wname = NULL; + wchar_t *wmode = NULL; + FILE *f = NULL; + + do { + if (!(wname = wchar_from_utf8(filename))) break; + if (!(wmode = wchar_from_utf8(mode))) break; + f = _wfopen(wname, wmode); + } while(0); + + free(wname); + free(wmode); + + return f; + } +} + +int flac_internal_stat64_utf8(const char *path, struct __stat64 *buffer) +{ + if (!utf8_filenames) { + return _stat64(path, buffer); + } else { + wchar_t *wpath; + int ret; + + if (!(wpath = wchar_from_utf8(path))) return -1; + ret = _wstat64(wpath, buffer); + free(wpath); + + return ret; + } +} + +int flac_internal_chmod_utf8(const char *filename, int pmode) +{ + if (!utf8_filenames) { + return _chmod(filename, pmode); + } else { + wchar_t *wname; + int ret; + + if (!(wname = wchar_from_utf8(filename))) return -1; + ret = _wchmod(wname, pmode); + free(wname); + + return ret; + } +} + +int flac_internal_utime_utf8(const char *filename, struct utimbuf *times) +{ + if (!utf8_filenames) { + return utime(filename, times); + } else { + wchar_t *wname; + struct __utimbuf64 ut; + int ret; + + if (!(wname = wchar_from_utf8(filename))) return -1; + ut.actime = times->actime; + ut.modtime = times->modtime; + ret = _wutime64(wname, &ut); + free(wname); + + return ret; + } +} + +int flac_internal_unlink_utf8(const char *filename) +{ + if (!utf8_filenames) { + return _unlink(filename); + } else { + wchar_t *wname; + int ret; + + if (!(wname = wchar_from_utf8(filename))) return -1; + ret = _wunlink(wname); + free(wname); + + return ret; + } +} + +int flac_internal_rename_utf8(const char *oldname, const char *newname) +{ + if (!utf8_filenames) { + return rename(oldname, newname); + } else { + wchar_t *wold = NULL; + wchar_t *wnew = NULL; + int ret = -1; + + do { + if (!(wold = wchar_from_utf8(oldname))) break; + if (!(wnew = wchar_from_utf8(newname))) break; + ret = _wrename(wold, wnew); + } while(0); + + free(wold); + free(wnew); + + return ret; + } +} + +HANDLE WINAPI flac_internal_CreateFile_utf8(const char *lpFileName, DWORD dwDesiredAccess, DWORD dwShareMode, LPSECURITY_ATTRIBUTES lpSecurityAttributes, DWORD dwCreationDisposition, DWORD dwFlagsAndAttributes, HANDLE hTemplateFile) +{ + if (!utf8_filenames) { + return CreateFileA(lpFileName, dwDesiredAccess, dwShareMode, lpSecurityAttributes, dwCreationDisposition, dwFlagsAndAttributes, hTemplateFile); + } else { + wchar_t *wname; + HANDLE handle = INVALID_HANDLE_VALUE; + + if ((wname = wchar_from_utf8(lpFileName)) != NULL) { + handle = CreateFileW(wname, dwDesiredAccess, dwShareMode, lpSecurityAttributes, dwCreationDisposition, dwFlagsAndAttributes, hTemplateFile); + free(wname); + } + + return handle; + } +} diff --git a/shell/linux/Makefile b/shell/linux/Makefile index 78304e169..801c743b1 100644 --- a/shell/linux/Makefile +++ b/shell/linux/Makefile @@ -15,6 +15,7 @@ STRIP=${CC_PREFIX}strip LD=${CC} CHD5_LZMA := 1 +CHD5_FLAC := 1 MFLAGS := ASFLAGS := @@ -241,6 +242,11 @@ ifdef CHD5_LZMA CFLAGS += -D_7ZIP_ST -DCHD5_LZMA endif +ifdef CHD5_FLAC + CFLAGS += -DCHD5_FLAC + LIBS += `pkg-config --libs flac` +endif + RZDCY_SRC_DIR = $(LOCAL_PATH)/../../core include $(RZDCY_SRC_DIR)/core.mk diff --git a/wercker.yml b/wercker.yml index 2c55a3a61..bc13be149 100644 --- a/wercker.yml +++ b/wercker.yml @@ -4,7 +4,7 @@ build: steps: - script: name: install-dependencies - code: sudo apt-get clean && sudo apt-get update && sudo apt-get install -y build-essential pkgconf libasound2-dev libgl1-mesa-dev libx11-dev + code: sudo apt-get clean && sudo apt-get update && sudo apt-get install -y build-essential pkgconf libasound2-dev libgl1-mesa-dev libx11-dev libflac-dev - script: name: gcc-version code: gcc --version From f78dc64bfa4927708bca8a5755ce29aee67a7c17 Mon Sep 17 00:00:00 2001 From: "Christoph \"baka0815\" Schwerdtfeger" Date: Sat, 22 Sep 2018 17:34:14 +0200 Subject: [PATCH 016/319] Fix includes --- core/deps/chdr/chd.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/core/deps/chdr/chd.c b/core/deps/chdr/chd.c index d15bf6aee..01bb4bd2b 100644 --- a/core/deps/chdr/chd.c +++ b/core/deps/chdr/chd.c @@ -52,9 +52,9 @@ #include "deps/lzma/LzmaEnc.h" #include "deps/lzma/LzmaDec.h" #endif // CHD5_LZMA -#include "crypto/md5.h" -#include "crypto/sha1.h" -#include "zlib.h" +#include "deps/crypto/md5.h" +#include "deps/crypto/sha1.h" +#include "deps/zlib/zlib.h" #define TRUE 1 #define FALSE 0 From 12e38001e4aed13a4bd00e6b4a0519296ba4d81b Mon Sep 17 00:00:00 2001 From: Stefanos Kornilios Mitsis Poiitidis Date: Sun, 23 Sep 2018 12:38:52 +0200 Subject: [PATCH 017/319] Docs: Import Dynarec Architecture from skmp's blog Source: https://web.archive.org/web/20110812025705/http://drk.emudev.org:80/blog/?page_id=18 --- docs/Dynarec Architecture.md | 79 ++++++++++++++++++++++++++++++++++++ 1 file changed, 79 insertions(+) create mode 100644 docs/Dynarec Architecture.md diff --git a/docs/Dynarec Architecture.md b/docs/Dynarec Architecture.md new file mode 100644 index 000000000..9b2acd359 --- /dev/null +++ b/docs/Dynarec Architecture.md @@ -0,0 +1,79 @@ +Import from skmp's old dead blog, https://web.archive.org/web/20110812025705/http://drk.emudev.org:80/blog/?page_id=18 + +Dynarec Architecture +=== + +Design document for the rec_v2 dynarec. This is WIP but i’m bored so i’l make it public :) + +The rec_v2 dynarec is designed to be modular and require minimum work for porting to different host cpus. Its also designed to be simple (simpler than v1x) to allow for simpler optimization passes and for relatively easy mitigation to ssa-based model. For reference both v1 and v2 are based on an IL that is kinda independent from both the source and destination arch (i’l call it shil from now on). + +Some history first +--- + +The original rec_v1 for mainline was designed for translation to x86 (and only that). The shil opcodes are more-or-less x86 opcodes but use sh4 registers. Flags can be saved/set with two opcodes (SaveT and LoadT) to the T register using any of the SetCC conditions. Other special opcodes are memory opcodes. These were expanded to memory moves/calls depending on a 16bit lookup table after calculating the memory address.Loads from known memory addresses were converted to direct calls/moves depending on their location. Register allocation was dynamic for 4 registers (ebp,esi,edi,ebx) and fixed for the entire block. This was the dynarec used on the nullDC 1.0.0 beta 1 release. + +For nullDC 1.0.0 b1.5/1.6 rec_v1x (x for extended :p) was used. It had a much more complex memory mapping scheme allowing for direct (no-lookup) memory access for any buffer mapped (ram/vram/aram). It also included code to more accurately detect memory constants and convert em to direct moves, as well as memory opcode ‘patching’ using exceptions to ensure correct logical flow in the case on an unpredicted memory access. X86 page protection and windows file mapping were the base for all that stuff. There were also many other fixes and optimizations such as the addition of a multilevel call stack cache, indirect jump/calls multilevel prediction and other fun stuff, mainly targeting the ‘glue’ code of the dynarec.Also a lot of fat was cut down from the block prologue/epilogue (like not loading registers when their initial value isn’t writen, smaller code for interrupt checks, etc) + +nullDC 1.0.3 used r1x with some more work to implement more sh4 opcodes (like pref for faster SQ writebacks) and some heuristics to detect blocks that didn’t work with constant-memory-read-to-constant forwarding.As far as i remember there weren’t any remarkable changes apart from that :p + +nullDC 1.0.4 added support for block relocation to better utilize/organize dynarec memory + +Some good things about v1/v1x +--- +- It is fast. On a core2-level cpu it gets 1:7 to 1:1.2 sh4:x86 cycle ratio.It generates about 30 bytes per sh4 opcode (including all linking/block overhead). The x86 code executes at around 1.2 opcodes per cycle. +- It is fairly compact and modular (~ 3k lines for the il compiler, around 10k lines for most of the important code). Its logically organised into the block manager, the dynarec driver, the sh4 analyzer/decoder, the optimizer and the il-compiler. It also supports super-blocks and hot-spot detection but these features were never used. +- A full block graph is used so blocks can be easily and safely removed undoing any static linking between em :) +- Full support for loaded/smc code with page protection and block verification (similar to the system i implemented for pcsx2) + +Some bad things +--- +- It's modeled around x86 (ie, x86 flags are supported and can be accessed any time with the SaveT/LoadT opcodes). While this allows a cheap way to generate fast code it makes interpretation of the IL almost impossible.It also makes porting to flag-less cpu architectures much much much much harder. +- It’s not as compact as it could be +- It doesn’t provide a fallback for its opcodes +- It doesn’t use 3-way opcodes (these can simplify optimization a lot on the IL level) +- The full block graph is a bit memory intesive + +When the psp port started there was the need for some dynarec that could easyly run on both x86 and mips and share as much code as possible to catch bugs on the easy & nice development platofrm (pc). This after some rewrites resulted to the rec_v2 as it is presented on this document :) + +rec_v2 features +--- +- A very modular design (frontend/decoder are data driven, the block manager is simple, the compiler is tiny) +- ALL platform dependant code is isolated with the ngen interface (native generator) +- There is no block graph or block descriptors, each address is mapped to a single code pointer. +- Heuristic based support for code modifications, as well as icache flush support. Single block invalidation isn’t possible leading to a much simpler model for the dynarec +- A default implementation for most of the dynarec opcodes (only 7 opcodes _must_ be provided natively) +- All opcodes have fixed input and output fields, with full description on which physical registers they use so data flow computation is simple. +- Support for x86, mips, Cortex-A8 and powerpc ! + +Well, there are a few drawbacks to the design as wel +--- +- Block modification needs special detection for some games (like Doa2le) +- The dynarec cache can be flushed only ‘at once’ and not at block level. This isn’t usualy a problem +- It’s nowhere as complete as rec_v1 (but its slowly getting there !) +- It’s noticeably slower than rec_v1, up to 3x on some points. Build in idle loop detection makes that problem less visible and more work on the opcodes will make it faster + +The shil format +--- +Shil (for sh il) is the fully ‘expanded’ form of the opcodes that the dynarec uses internally. Its a simple struct with 2 possible outputs (rd and rd1) and 3 possible inputs (rs1,rs2,rs3). Each input can be an register or a 32 bit imm. Currently there are C implementations for almost all the shil opcodes (only 7 opcodes are required to be implemented by the ngen backend).There were in total 36 shil opcodes last time i counted em ;) + +Sh4 decoder (dec_*) +--- +The sh4 decoder gets a stream of sh4 opcodes and converts em to shil. The decoder supports both ‘native’ opcodes (they have a function to handle the translation) and a data driven mapping (this part is really hacky). The decoder currently stops at a branch or when a maximum amount of cycles is reached. While the code is a bit overcomplicated the uglyness is self-contained as the shil format is fairly clean and nice :) + +Shil optimiser +--- +The shil optimiser gets the block as output from the decoder and performs some basic optimisations.Currently it only does dead code elimination. + +Native generator/Shil compiler (ngen_*) +--- +The compiler is invoked to compile a block after it has been processed by the optimiser. The compiler is isolated from the rest of the code with the ngen interface (ngen_* are supplied by each dynarec implementation). It communicates with the dynarec driver and the block manager to complete the task. The compiler usualy consists of a case() to implement the opcodes, some code to handle block header/footer, glue code for the dynarec driver and the dynarec mainloop. This is the only cpu isa depedant part of the dynarec. + +Currently the x86 implementation is around 900 lines, the psp one 1300 (but includes a lot of emitter helpers) the arm one around 600 (but misses many opcodes) and ppc around 400 (but its not working reliably yet) + +Block Manager (bm_*) +--- +The block manager is repsosible for keeping the sh4 addr <-> code mappings and replying to queries about em to the dynarec code. It has a full implementation in C but some parts can be also implemented as part of the dynarec loop to improve performance. Right now it uses cached hashed lists to do the mappings. While the bm is fairly fast (the cache takes care of that) the big idea is that it shound’t be used a lot as extensibe prediction can remove most of the queries :) + +Dynarec Driver (rdv_*) +--- +Its the small glue code that ‘drives’ the emulation and provides functions needed by the ngen interface. Generally any function that doesn’t belong clearly to one of the other parts and it is portable across all shil compilers ends up in here :) From c5cb6fec8628180277534037498195a6070ebf4a Mon Sep 17 00:00:00 2001 From: Stefanos Kornilios Mitsis Poiitidis Date: Sun, 23 Sep 2018 12:42:29 +0200 Subject: [PATCH 018/319] Docs: Import notable game bugs from skmp's blog Source: https://web.archive.org/web/20110809053548/http://drk.emudev.org/blog/?page_id=4 --- docs/Notable game bugs | 39 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) create mode 100644 docs/Notable game bugs diff --git a/docs/Notable game bugs b/docs/Notable game bugs new file mode 100644 index 000000000..6207bd17e --- /dev/null +++ b/docs/Notable game bugs @@ -0,0 +1,39 @@ +Somehwat outdated. Imported from skmp's old dead blog, https://web.archive.org/web/20110809053548/http://drk.emudev.org/blog/?page_id=4 + +List of bugs and fixes +==== + +Its been a while since i wanted to make a list of known bugs with known fixes/hacks/debug states to keep as a reference for future bug checking/help other people ;) After many delays here it is ! Most of these bugs are nullDC/Dreamcast emulation related, but they may apply to other emulators as well.If you know a bug that is not on the list contact me to add it (sources will be credited, unless otherwise mentioned i found/debugged em :) ) + +Also check PsyMan’s list, i don’t have his fixes duplicated here :) + +Last update : 25/03/2009 + +- Thps2 locks with a black screen : Pending interrupt never gets a chance to raise.Thps2 enables interrupts on the status register, and shortly after disables em.Make sure to check for interrupts on writes to the status register.Also it uses Yuv decoder on TA, make sure its emulated. +- Echelon intros +-- The logo on the DOA2LE intro rotates wrongly : fpu double opcodes(used to build the sin/cos table on the intro) are wrong (remember, they are stored on hi:low order on register pairs).Check fabs.d and fneg.d. +-- Some intros abuse Cache, and expect it to maintain the mem data when mem is updated externaly (using the non cached mirror).Emulating the cache side effects or skiping the routines that do that fixes the problem.The values to patch are 0x8c01313a:0x490b=0×0009 and 0x8c0130f8:0x490b=0×0009 (thanks CaH4e3 for the patches !).Also, invalidating the code cache only on icache sync opcodes propably will fix the problem. +-- Framebuffer emulation doesn’t detect size properly: When interlacing is disabled, the screen height varies based on the video clock divider (iirc its on FB_R_*). +- Sonic Adventure 2, +-- Characters collide ‘oddly’ with the walls : fpu code doesnt properly handle nans/infinitives on compares.Check fcmp/eq/gt. +-- Random polygons appear all around : SA2 sends polygons with x/y/z +/- infintive.Different cards handle em differently.Checking manualy for em and removing em fixes the problem :) +- Ikaruga +-- Ika and Many newer jap games wont work : Newer versions of the katana sdk use mmu to remap the SQ write-back space.Thankfully, 1mb pages are used so the implementation is quite fast (i use an 64 entry remap table).Only SQ writes are mapped, mem accesses use the non-mmu mapped mirror. +-- (Some) Bullets are invisible on level 2 : The FSCA opcode is not accurate, it is expected to return 0 (exactly) for cos(90)/sin(180).Its a good idea to dump the fsca table from a dreamcast and use that, its just 128 kb :) . +- Some games have clicking on the streaming music (SA2, Crazy Taxi, thps1/2, more …) : The sound decompression code on the sh4 uses ftrc (float -> int) to convert the samples.Make sure you saturate the value when converting (x86 defaults to using 0×8000 0000 for values that dont fit). +- Test Drive Le Mans 24h, OVERLOAD shows up and game won’t progress : The game checks the power usage of maple devices.Make sure you return valid values for the current (mA) usage on the get device info command ;) +- Linux/dc locks after doing a lot work (including user space, i cant exactly remember where atm) .Also netbsd/dc locks after initing aica : Check the gdrom code, on nullDC a bug made read aborts to raise interrupts for ever. +- Netbsd crashes as soon as it boots: Make sure you have connected a keyboard, it needs it =P. +- Soul Reaver has no 3d ingame yet you can hear the sound and menus work : Check mac.w, clrmac(also check mac.l, C has a nasty habit of doing 32 bit muls unless you cast to 64 bits before the mul). +- Backgrounds on Soul Calibur seem to use wrong size textures/textures on SC seem to get only some colors right : Soul Calibur does varius tricks to optimise vram usage.It uses textures with different data fortmats ‘overlapping’ on memory (to take advantage of unused regions on a teuxtre and store other textures there).It also uses varius sizes of the same data.The fix is to identify a texture by the tcw:tsp pair , not just tcw/start address. +- Resident Evil 3 loops after starting a new game but doesnt hang : It uses rtc to time the intro text, emulate it =P. +- Sonic Shuffle locks after stating a new game : Emulate/Check PVR-DMA transfers & interrupt. +- Crazi Taxi locks after some time (~30 secs) of gameplay, music gets corrupted right before the lockup : Its AEG emulation on aica, fix/hack it to return proper values. +- Gauntlet Legends locks while loading first level, Mat Hoffman BMX locks on logo, more locks on places that loading happens : These games use multy trasnfer pio on gdrom to trasnfer > 64kb on one PIO request.Emulate that :) +- Many games run faster than normal, including RE:CV, Time Stalkers : Make sure to emulate the field register on SPG/Interlaced mode. +- Time Stalkers music is wrong , especialy when hi-pitched : This game does synth using adpcm sound bank(s).Make sure adpcm is decoded correctly on hi sample rates (all adpcm samples must be decoded no matter the pitch). +- Some capcom games(mvsc2,more) run slow using normal TMU timings : Capcom game engine expects pvr rendering to take a while(well, some versions of it at least).Make sure you have a delay betwen the StartRender write and the pvr end of render interrupts, that wll fix it.A common way to hack this is to edit tmu timings (like capcom hack on chankast). +- Sword of the Berserk, the ingame sfx stop working after a few seconds : When looping is disabled on an aica channel the loop end flag must still be set properly.SOTB ‘free’ channel detection code requires that to work. +- Resident Evil Code Veronica, the statue wont turn; Soul Calibur , Yoshimitsu exhibition mode crashing at fade out : The speed of gdrom transfers needs to be limited to about 1MB/s for these games to work +- Prince Of Persia 3D : the textures are totaly wrong: Check the dynamic shift opcodes, the game depends on only using the lower 5 bits. +- Skies Of Arcadia crashes “randomly” after exiting the ingame menu: For some reason, everytime that you exit it writes stuff to the flash.When the flash is full, it clears a sector.Make sure the flash clear command is handled properly. From 5a6c944228de2914eb3d4abcba4f596d9131c6fc Mon Sep 17 00:00:00 2001 From: Stefanos Kornilios Mitsis Poiitidis Date: Sun, 23 Sep 2018 12:44:17 +0200 Subject: [PATCH 019/319] Rename Notable game bugs to Notable game bugs.md --- docs/{Notable game bugs => Notable game bugs.md} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename docs/{Notable game bugs => Notable game bugs.md} (100%) diff --git a/docs/Notable game bugs b/docs/Notable game bugs.md similarity index 100% rename from docs/Notable game bugs rename to docs/Notable game bugs.md From 9bb7d969624862f9f33cc1b5708c48a0f9b8aecf Mon Sep 17 00:00:00 2001 From: Christoph 'baka0815' Schwerdtfeger Date: Sun, 23 Sep 2018 15:27:57 +0200 Subject: [PATCH 020/319] CHD/FLAC: surround flac.c with #ifdef --- core/deps/chdr/flac.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/core/deps/chdr/flac.c b/core/deps/chdr/flac.c index 8e31ed6b7..359ef9e41 100644 --- a/core/deps/chdr/flac.c +++ b/core/deps/chdr/flac.c @@ -1,3 +1,4 @@ +#if defined(CHD5_FLAC) /* license:BSD-3-Clause * copyright-holders:Aaron Giles *************************************************************************** @@ -329,3 +330,4 @@ FLAC__StreamDecoderWriteStatus flac_decoder_write_callback(void *client_data, co void flac_decoder_error_callback_static(const FLAC__StreamDecoder *decoder, FLAC__StreamDecoderErrorStatus status, void *client_data) { } +#endif // CHD5_FLAC \ No newline at end of file From 659430e9cc51fc0b5e8bd41cc2525814e7736050 Mon Sep 17 00:00:00 2001 From: "Christoph \"baka0815\" Schwerdtfeger" Date: Sun, 23 Sep 2018 15:43:40 +0200 Subject: [PATCH 021/319] CHD: Fixed the build for Android MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit CFLAGS were set in the wrong place and the name of the DEF was wrong too -_-° --- shell/android-studio/reicast/src/main/jni/Android.mk | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/shell/android-studio/reicast/src/main/jni/Android.mk b/shell/android-studio/reicast/src/main/jni/Android.mk index 396825b36..603cb2bf9 100644 --- a/shell/android-studio/reicast/src/main/jni/Android.mk +++ b/shell/android-studio/reicast/src/main/jni/Android.mk @@ -44,11 +44,6 @@ else NO_REC := endif -# 7-Zip/LZMA settings (CHDv5) -ifdef USE_LZMA - LOCAL_CFLAGS += -D_7ZIP_ST -DCHD5_LZMA -endif - $(info $$TARGET_ARCH_ABI is [${TARGET_ARCH_ABI}]) include $(LOCAL_PATH)/../../../../../core/core.mk @@ -60,6 +55,11 @@ LOCAL_CFLAGS := $(RZDCY_CFLAGS) -fPIC -fvisibility=hidden -ffunction-sections - LOCAL_CXXFLAGS := $(RZDCY_CXXFLAGS) -fPIC -fvisibility=hidden -fvisibility-inlines-hidden -ffunction-sections -fdata-sections LOCAL_CPPFLAGS := $(RZDCY_CXXFLAGS) -fPIC -fvisibility=hidden -fvisibility-inlines-hidden -ffunction-sections -fdata-sections +# 7-Zip/LZMA settings (CHDv5) +ifdef CHD5_LZMA + LOCAL_CFLAGS += -D_7ZIP_ST -DCHD5_LZMA +endif + # LOCAL_CFLAGS += -std=c++11 LOCAL_CXXFLAGS += -std=c++11 From 30caadc42f6080352bccd230c4a102c1f9f1115a Mon Sep 17 00:00:00 2001 From: "Christoph \"baka0815\" Schwerdtfeger" Date: Sun, 23 Sep 2018 16:09:56 +0200 Subject: [PATCH 022/319] CHD/FLAC: Use local deps for FLAC and enable on Android Using locally installed FLAC libs, so no more need for libflac-dev. --- core/core.mk | 34 ++- core/deps/flac/include/FLAC/assert.h | 46 ++++ core/deps/flac/include/share/endswap.h | 84 ++++++ core/deps/flac/include/share/macros.h | 45 ++++ core/deps/flac/include/share/safe_str.h | 69 +++++ .../src/libFLAC/include/private/bitreader.h | 91 +++++++ .../flac/src/libFLAC/include/private/lpc.h | 250 ++++++++++++++++++ .../flac/src/libFLAC/include/private/md5.h | 50 ++++ .../flac/src/libFLAC/include/private/memory.h | 58 ++++ .../src/libFLAC/include/private/metadata.h | 46 ++++ .../flac/src/libFLAC/include/private/window.h | 74 ++++++ .../include/protected/stream_decoder.h | 60 +++++ .../reicast/src/main/jni/Android.mk | 6 + shell/linux/Makefile | 1 - wercker.yml | 2 +- 15 files changed, 911 insertions(+), 5 deletions(-) create mode 100644 core/deps/flac/include/FLAC/assert.h create mode 100644 core/deps/flac/include/share/endswap.h create mode 100644 core/deps/flac/include/share/macros.h create mode 100644 core/deps/flac/include/share/safe_str.h create mode 100644 core/deps/flac/src/libFLAC/include/private/bitreader.h create mode 100644 core/deps/flac/src/libFLAC/include/private/lpc.h create mode 100644 core/deps/flac/src/libFLAC/include/private/md5.h create mode 100644 core/deps/flac/src/libFLAC/include/private/memory.h create mode 100644 core/deps/flac/src/libFLAC/include/private/metadata.h create mode 100644 core/deps/flac/src/libFLAC/include/private/window.h create mode 100644 core/deps/flac/src/libFLAC/include/protected/stream_decoder.h diff --git a/core/core.mk b/core/core.mk index 7927a5654..0df5d6a42 100755 --- a/core/core.mk +++ b/core/core.mk @@ -11,10 +11,9 @@ RZDCY_MODULES := cfg/ hw/arm7/ hw/aica/ hw/holly/ hw/ hw/gdrom/ hw/maple/ \ hw/extdev/ hw/arm/ hw/naomi/ imgread/ linux/ ./ deps/coreio/ deps/zlib/ deps/chdr/ deps/crypto/ \ deps/libelf/ deps/chdpsr/ arm_emitter/ rend/ reios/ deps/libpng/ - ifdef CHD5_LZMA +ifdef CHD5_LZMA RZDCY_MODULES += deps/lzma/ - endif - +endif ifdef WEBUI RZDCY_MODULES += webui/ @@ -115,4 +114,33 @@ ifdef HAS_SOFTREND RZDCY_CFLAGS += -DTARGET_SOFTREND endif +ifdef CHD5_FLAC + RZDCY_MODULES += deps/flac/src/libFLAC/ + RZDCY_FILES += $(RZDCY_SRC_DIR)/deps/flac/src/libFLAC/bitmath.c + RZDCY_FILES += $(RZDCY_SRC_DIR)/deps/flac/src/libFLAC/bitreader.c + RZDCY_FILES += $(RZDCY_SRC_DIR)/deps/flac/src/libFLAC/cpu.c + RZDCY_FILES += $(RZDCY_SRC_DIR)/deps/flac/src/libFLAC/crc.c + RZDCY_FILES += $(RZDCY_SRC_DIR)/deps/flac/src/libFLAC/fixed.c + RZDCY_FILES += $(RZDCY_SRC_DIR)/deps/flac/src/libFLAC/fixed_intrin_sse2.c + RZDCY_FILES += $(RZDCY_SRC_DIR)/deps/flac/src/libFLAC/fixed_intrin_ssse3.c + RZDCY_FILES += $(RZDCY_SRC_DIR)/deps/flac/src/libFLAC/float.c + RZDCY_FILES += $(RZDCY_SRC_DIR)/deps/flac/src/libFLAC/format.c + RZDCY_FILES += $(RZDCY_SRC_DIR)/deps/flac/src/libFLAC/lpc.c + RZDCY_FILES += $(RZDCY_SRC_DIR)/deps/flac/src/libFLAC/lpc_intrin_avx2.c + RZDCY_FILES += $(RZDCY_SRC_DIR)/deps/flac/src/libFLAC/lpc_intrin_sse2.c + RZDCY_FILES += $(RZDCY_SRC_DIR)/deps/flac/src/libFLAC/lpc_intrin_sse41.c + RZDCY_FILES += $(RZDCY_SRC_DIR)/deps/flac/src/libFLAC/lpc_intrin_sse.c + RZDCY_FILES += $(RZDCY_SRC_DIR)/deps/flac/src/libFLAC/md5.c + RZDCY_FILES += $(RZDCY_SRC_DIR)/deps/flac/src/libFLAC/memory.c + RZDCY_FILES += $(RZDCY_SRC_DIR)/deps/flac/src/libFLAC/metadata_iterators.c + RZDCY_FILES += $(RZDCY_SRC_DIR)/deps/flac/src/libFLAC/metadata_object.c + RZDCY_FILES += $(RZDCY_SRC_DIR)/deps/flac/src/libFLAC/stream_decoder.c + RZDCY_FILES += $(RZDCY_SRC_DIR)/deps/flac/src/libFLAC/window.c + + RZDCY_CFLAGS += -I$(RZDCY_SRC_DIR)/deps/flac/src/libFLAC/include/ -I$(RZDCY_SRC_DIR)/deps/flac/include + RZDCY_CFLAGS += -DPACKAGE_VERSION=\"1.3.2\" -DFLAC__HAS_OGG=0 -DFLAC__NO_DLL -DHAVE_LROUND -DHAVE_STDINT_H -DHAVE_STDLIB_H -DHAVE_SYS_PARAM_H + CFLAGS += -I$(RZDCY_SRC_DIR)/deps/flac/include -I$(RZDCY_SRC_DIR)/deps/flac/src/libFLAC/include/ + CFLAGS += -DPACKAGE_VERSION=\"1.3.2\" -DFLAC__HAS_OGG=0 -DFLAC__NO_DLL -DHAVE_LROUND -DHAVE_STDINT_H -DHAVE_STDLIB_H -DHAVE_SYS_PARAM_H +endif + RZDCY_CXXFLAGS := $(RZDCY_CFLAGS) -fno-exceptions -fno-rtti -std=gnu++11 diff --git a/core/deps/flac/include/FLAC/assert.h b/core/deps/flac/include/FLAC/assert.h new file mode 100644 index 000000000..b546fd070 --- /dev/null +++ b/core/deps/flac/include/FLAC/assert.h @@ -0,0 +1,46 @@ +/* libFLAC - Free Lossless Audio Codec library + * Copyright (C) 2001-2009 Josh Coalson + * Copyright (C) 2011-2016 Xiph.Org Foundation + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * - Neither the name of the Xiph.org Foundation nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef FLAC__ASSERT_H +#define FLAC__ASSERT_H + +/* we need this since some compilers (like MSVC) leave assert()s on release code (and we don't want to use their ASSERT) */ +#ifdef DEBUG +#include +#define FLAC__ASSERT(x) assert(x) +#define FLAC__ASSERT_DECLARATION(x) x +#else +#define FLAC__ASSERT(x) +#define FLAC__ASSERT_DECLARATION(x) +#endif + +#endif diff --git a/core/deps/flac/include/share/endswap.h b/core/deps/flac/include/share/endswap.h new file mode 100644 index 000000000..9088a7474 --- /dev/null +++ b/core/deps/flac/include/share/endswap.h @@ -0,0 +1,84 @@ +/* libFLAC - Free Lossless Audio Codec library + * Copyright (C) 2012-2016 Xiph.org Foundation + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * - Neither the name of the Xiph.org Foundation nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* It is assumed that this header will be included after "config.h". */ + +#if HAVE_BSWAP32 /* GCC and Clang */ + +/* GCC prior to 4.8 didn't provide bswap16 on x86_64 */ +#if ! HAVE_BSWAP16 +static inline unsigned short __builtin_bswap16(unsigned short a) +{ + return (a<<8)|(a>>8); +} +#endif + +#define ENDSWAP_16(x) (__builtin_bswap16 (x)) +#define ENDSWAP_32(x) (__builtin_bswap32 (x)) +#define ENDSWAP_64(x) (__builtin_bswap64 (x)) + +#elif defined _MSC_VER /* Windows */ + +#include + +#define ENDSWAP_16(x) (_byteswap_ushort (x)) +#define ENDSWAP_32(x) (_byteswap_ulong (x)) +#define ENDSWAP_64(x) (_byteswap_uint64 (x)) + +#elif defined HAVE_BYTESWAP_H /* Linux */ + +#include + +#define ENDSWAP_16(x) (bswap_16 (x)) +#define ENDSWAP_32(x) (bswap_32 (x)) +#define ENDSWAP_64(x) (bswap_64 (x)) + +#else + +#define ENDSWAP_16(x) ((((x) >> 8) & 0xFF) | (((x) & 0xFF) << 8)) +#define ENDSWAP_32(x) ((((x) >> 24) & 0xFF) | (((x) >> 8) & 0xFF00) | (((x) & 0xFF00) << 8) | (((x) & 0xFF) << 24)) +#define ENDSWAP_64(x) ((ENDSWAP_32(((x) >> 32) & 0xFFFFFFFF)) | (ENDSWAP_32((x) & 0xFFFFFFFF) << 32)) + +#endif + + +/* Host to little-endian byte swapping (for MD5 calculation) */ +#if CPU_IS_BIG_ENDIAN + +#define H2LE_16(x) ENDSWAP_16 (x) +#define H2LE_32(x) ENDSWAP_32 (x) + +#else + +#define H2LE_16(x) (x) +#define H2LE_32(x) (x) + +#endif diff --git a/core/deps/flac/include/share/macros.h b/core/deps/flac/include/share/macros.h new file mode 100644 index 000000000..20b3ea56b --- /dev/null +++ b/core/deps/flac/include/share/macros.h @@ -0,0 +1,45 @@ +/* libFLAC - Free Lossless Audio Codec library + * Copyright (C) 2013-2016 Xiph.org Foundation + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * - Neither the name of the Xiph.org Foundation nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include + +/* FLAC_CHECK_RETURN : Check the return value of the provided function and + * print an error message if it fails (ie returns a value < 0). + * + * Ideally, a library should not print anything, but this macro is only used + * for things that extremely unlikely to fail, like `chown` to a previoulsy + * saved `uid`. + */ + +#define FLAC_CHECK_RETURN(x) \ + { if ((x) < 0) \ + fprintf (stderr, "%s : %s\n", #x, strerror (errno)) ; \ + } diff --git a/core/deps/flac/include/share/safe_str.h b/core/deps/flac/include/share/safe_str.h new file mode 100644 index 000000000..eb974c517 --- /dev/null +++ b/core/deps/flac/include/share/safe_str.h @@ -0,0 +1,69 @@ +/* libFLAC - Free Lossless Audio Codec library + * Copyright (C) 2013-2016 Xiph.org Foundation + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * - Neither the name of the Xiph.org Foundation nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* Safe string handling functions to replace things like strcpy, strncpy, + * strcat, strncat etc. + * All of these functions guarantee a correctly NUL terminated string but + * the string may be truncated if the destination buffer was too short. + */ + +#ifndef FLAC__SHARE_SAFE_STR_H +#define FLAC__SHARE_SAFE_STR_H + +static inline char * +safe_strncat(char *dest, const char *src, size_t dest_size) +{ + char * ret; + + if (dest_size < 1) + return dest; + + ret = strncat(dest, src, dest_size - strlen (dest)); + dest [dest_size - 1] = 0; + + return ret; +} + +static inline char * +safe_strncpy(char *dest, const char *src, size_t dest_size) +{ + char * ret; + + if (dest_size < 1) + return dest; + + ret = strncpy(dest, src, dest_size); + dest [dest_size - 1] = 0; + + return ret; +} + +#endif /* FLAC__SHARE_SAFE_STR_H */ diff --git a/core/deps/flac/src/libFLAC/include/private/bitreader.h b/core/deps/flac/src/libFLAC/include/private/bitreader.h new file mode 100644 index 000000000..7c7316556 --- /dev/null +++ b/core/deps/flac/src/libFLAC/include/private/bitreader.h @@ -0,0 +1,91 @@ +/* libFLAC - Free Lossless Audio Codec library + * Copyright (C) 2000-2009 Josh Coalson + * Copyright (C) 2011-2016 Xiph.Org Foundation + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * - Neither the name of the Xiph.org Foundation nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef FLAC__PRIVATE__BITREADER_H +#define FLAC__PRIVATE__BITREADER_H + +#include /* for FILE */ +#include "FLAC/ordinals.h" +#include "cpu.h" + +/* + * opaque structure definition + */ +struct FLAC__BitReader; +typedef struct FLAC__BitReader FLAC__BitReader; + +typedef FLAC__bool (*FLAC__BitReaderReadCallback)(FLAC__byte buffer[], size_t *bytes, void *client_data); + +/* + * construction, deletion, initialization, etc functions + */ +FLAC__BitReader *FLAC__bitreader_new(void); +void FLAC__bitreader_delete(FLAC__BitReader *br); +FLAC__bool FLAC__bitreader_init(FLAC__BitReader *br, FLAC__BitReaderReadCallback rcb, void *cd); +void FLAC__bitreader_free(FLAC__BitReader *br); /* does not 'free(br)' */ +FLAC__bool FLAC__bitreader_clear(FLAC__BitReader *br); +void FLAC__bitreader_dump(const FLAC__BitReader *br, FILE *out); + +/* + * CRC functions + */ +void FLAC__bitreader_reset_read_crc16(FLAC__BitReader *br, FLAC__uint16 seed); +FLAC__uint16 FLAC__bitreader_get_read_crc16(FLAC__BitReader *br); + +/* + * info functions + */ +FLAC__bool FLAC__bitreader_is_consumed_byte_aligned(const FLAC__BitReader *br); +unsigned FLAC__bitreader_bits_left_for_byte_alignment(const FLAC__BitReader *br); +unsigned FLAC__bitreader_get_input_bits_unconsumed(const FLAC__BitReader *br); + +/* + * read functions + */ + +FLAC__bool FLAC__bitreader_read_raw_uint32(FLAC__BitReader *br, FLAC__uint32 *val, unsigned bits); +FLAC__bool FLAC__bitreader_read_raw_int32(FLAC__BitReader *br, FLAC__int32 *val, unsigned bits); +FLAC__bool FLAC__bitreader_read_raw_uint64(FLAC__BitReader *br, FLAC__uint64 *val, unsigned bits); +FLAC__bool FLAC__bitreader_read_uint32_little_endian(FLAC__BitReader *br, FLAC__uint32 *val); /*only for bits=32*/ +FLAC__bool FLAC__bitreader_skip_bits_no_crc(FLAC__BitReader *br, unsigned bits); /* WATCHOUT: does not CRC the skipped data! */ /*@@@@ add to unit tests */ +FLAC__bool FLAC__bitreader_skip_byte_block_aligned_no_crc(FLAC__BitReader *br, unsigned nvals); /* WATCHOUT: does not CRC the read data! */ +FLAC__bool FLAC__bitreader_read_byte_block_aligned_no_crc(FLAC__BitReader *br, FLAC__byte *val, unsigned nvals); /* WATCHOUT: does not CRC the read data! */ +FLAC__bool FLAC__bitreader_read_unary_unsigned(FLAC__BitReader *br, unsigned *val); +FLAC__bool FLAC__bitreader_read_rice_signed(FLAC__BitReader *br, int *val, unsigned parameter); +FLAC__bool FLAC__bitreader_read_rice_signed_block(FLAC__BitReader *br, int vals[], unsigned nvals, unsigned parameter); +#if 0 /* UNUSED */ +FLAC__bool FLAC__bitreader_read_golomb_signed(FLAC__BitReader *br, int *val, unsigned parameter); +FLAC__bool FLAC__bitreader_read_golomb_unsigned(FLAC__BitReader *br, unsigned *val, unsigned parameter); +#endif +FLAC__bool FLAC__bitreader_read_utf8_uint32(FLAC__BitReader *br, FLAC__uint32 *val, FLAC__byte *raw, unsigned *rawlen); +FLAC__bool FLAC__bitreader_read_utf8_uint64(FLAC__BitReader *br, FLAC__uint64 *val, FLAC__byte *raw, unsigned *rawlen); +#endif diff --git a/core/deps/flac/src/libFLAC/include/private/lpc.h b/core/deps/flac/src/libFLAC/include/private/lpc.h new file mode 100644 index 000000000..6eb02be63 --- /dev/null +++ b/core/deps/flac/src/libFLAC/include/private/lpc.h @@ -0,0 +1,250 @@ +/* libFLAC - Free Lossless Audio Codec library + * Copyright (C) 2000-2009 Josh Coalson + * Copyright (C) 2011-2016 Xiph.Org Foundation + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * - Neither the name of the Xiph.org Foundation nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef FLAC__PRIVATE__LPC_H +#define FLAC__PRIVATE__LPC_H + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include "private/cpu.h" +#include "private/float.h" +#include "FLAC/format.h" + +#ifndef FLAC__INTEGER_ONLY_LIBRARY + +/* + * FLAC__lpc_window_data() + * -------------------------------------------------------------------- + * Applies the given window to the data. + * OPT: asm implementation + * + * IN in[0,data_len-1] + * IN window[0,data_len-1] + * OUT out[0,lag-1] + * IN data_len + */ +void FLAC__lpc_window_data(const FLAC__int32 in[], const FLAC__real window[], FLAC__real out[], unsigned data_len); + +/* + * FLAC__lpc_compute_autocorrelation() + * -------------------------------------------------------------------- + * Compute the autocorrelation for lags between 0 and lag-1. + * Assumes data[] outside of [0,data_len-1] == 0. + * Asserts that lag > 0. + * + * IN data[0,data_len-1] + * IN data_len + * IN 0 < lag <= data_len + * OUT autoc[0,lag-1] + */ +void FLAC__lpc_compute_autocorrelation(const FLAC__real data[], unsigned data_len, unsigned lag, FLAC__real autoc[]); +#ifndef FLAC__NO_ASM +# ifdef FLAC__CPU_IA32 +# ifdef FLAC__HAS_NASM +void FLAC__lpc_compute_autocorrelation_asm_ia32(const FLAC__real data[], unsigned data_len, unsigned lag, FLAC__real autoc[]); +void FLAC__lpc_compute_autocorrelation_asm_ia32_sse_lag_4_old(const FLAC__real data[], unsigned data_len, unsigned lag, FLAC__real autoc[]); +void FLAC__lpc_compute_autocorrelation_asm_ia32_sse_lag_8_old(const FLAC__real data[], unsigned data_len, unsigned lag, FLAC__real autoc[]); +void FLAC__lpc_compute_autocorrelation_asm_ia32_sse_lag_12_old(const FLAC__real data[], unsigned data_len, unsigned lag, FLAC__real autoc[]); +void FLAC__lpc_compute_autocorrelation_asm_ia32_sse_lag_16_old(const FLAC__real data[], unsigned data_len, unsigned lag, FLAC__real autoc[]); +# endif +# endif +# if (defined FLAC__CPU_IA32 || defined FLAC__CPU_X86_64) && FLAC__HAS_X86INTRIN +# ifdef FLAC__SSE_SUPPORTED +void FLAC__lpc_compute_autocorrelation_intrin_sse_lag_4_old(const FLAC__real data[], unsigned data_len, unsigned lag, FLAC__real autoc[]); +void FLAC__lpc_compute_autocorrelation_intrin_sse_lag_8_old(const FLAC__real data[], unsigned data_len, unsigned lag, FLAC__real autoc[]); +void FLAC__lpc_compute_autocorrelation_intrin_sse_lag_12_old(const FLAC__real data[], unsigned data_len, unsigned lag, FLAC__real autoc[]); +void FLAC__lpc_compute_autocorrelation_intrin_sse_lag_16_old(const FLAC__real data[], unsigned data_len, unsigned lag, FLAC__real autoc[]); +void FLAC__lpc_compute_autocorrelation_intrin_sse_lag_4_new(const FLAC__real data[], unsigned data_len, unsigned lag, FLAC__real autoc[]); +void FLAC__lpc_compute_autocorrelation_intrin_sse_lag_8_new(const FLAC__real data[], unsigned data_len, unsigned lag, FLAC__real autoc[]); +void FLAC__lpc_compute_autocorrelation_intrin_sse_lag_12_new(const FLAC__real data[], unsigned data_len, unsigned lag, FLAC__real autoc[]); +void FLAC__lpc_compute_autocorrelation_intrin_sse_lag_16_new(const FLAC__real data[], unsigned data_len, unsigned lag, FLAC__real autoc[]); +# endif +# endif +#endif + +/* + * FLAC__lpc_compute_lp_coefficients() + * -------------------------------------------------------------------- + * Computes LP coefficients for orders 1..max_order. + * Do not call if autoc[0] == 0.0. This means the signal is zero + * and there is no point in calculating a predictor. + * + * IN autoc[0,max_order] autocorrelation values + * IN 0 < max_order <= FLAC__MAX_LPC_ORDER max LP order to compute + * OUT lp_coeff[0,max_order-1][0,max_order-1] LP coefficients for each order + * *** IMPORTANT: + * *** lp_coeff[0,max_order-1][max_order,FLAC__MAX_LPC_ORDER-1] are untouched + * OUT error[0,max_order-1] error for each order (more + * specifically, the variance of + * the error signal times # of + * samples in the signal) + * + * Example: if max_order is 9, the LP coefficients for order 9 will be + * in lp_coeff[8][0,8], the LP coefficients for order 8 will be + * in lp_coeff[7][0,7], etc. + */ +void FLAC__lpc_compute_lp_coefficients(const FLAC__real autoc[], unsigned *max_order, FLAC__real lp_coeff[][FLAC__MAX_LPC_ORDER], double error[]); + +/* + * FLAC__lpc_quantize_coefficients() + * -------------------------------------------------------------------- + * Quantizes the LP coefficients. NOTE: precision + bits_per_sample + * must be less than 32 (sizeof(FLAC__int32)*8). + * + * IN lp_coeff[0,order-1] LP coefficients + * IN order LP order + * IN FLAC__MIN_QLP_COEFF_PRECISION < precision + * desired precision (in bits, including sign + * bit) of largest coefficient + * OUT qlp_coeff[0,order-1] quantized coefficients + * OUT shift # of bits to shift right to get approximated + * LP coefficients. NOTE: could be negative. + * RETURN 0 => quantization OK + * 1 => coefficients require too much shifting for *shift to + * fit in the LPC subframe header. 'shift' is unset. + * 2 => coefficients are all zero, which is bad. 'shift' is + * unset. + */ +int FLAC__lpc_quantize_coefficients(const FLAC__real lp_coeff[], unsigned order, unsigned precision, FLAC__int32 qlp_coeff[], int *shift); + +/* + * FLAC__lpc_compute_residual_from_qlp_coefficients() + * -------------------------------------------------------------------- + * Compute the residual signal obtained from sutracting the predicted + * signal from the original. + * + * IN data[-order,data_len-1] original signal (NOTE THE INDICES!) + * IN data_len length of original signal + * IN qlp_coeff[0,order-1] quantized LP coefficients + * IN order > 0 LP order + * IN lp_quantization quantization of LP coefficients in bits + * OUT residual[0,data_len-1] residual signal + */ +void FLAC__lpc_compute_residual_from_qlp_coefficients(const FLAC__int32 *data, unsigned data_len, const FLAC__int32 qlp_coeff[], unsigned order, int lp_quantization, FLAC__int32 residual[]); +void FLAC__lpc_compute_residual_from_qlp_coefficients_wide(const FLAC__int32 *data, unsigned data_len, const FLAC__int32 qlp_coeff[], unsigned order, int lp_quantization, FLAC__int32 residual[]); +#ifndef FLAC__NO_ASM +# ifdef FLAC__CPU_IA32 +# ifdef FLAC__HAS_NASM +void FLAC__lpc_compute_residual_from_qlp_coefficients_asm_ia32(const FLAC__int32 *data, unsigned data_len, const FLAC__int32 qlp_coeff[], unsigned order, int lp_quantization, FLAC__int32 residual[]); +void FLAC__lpc_compute_residual_from_qlp_coefficients_asm_ia32_mmx(const FLAC__int32 *data, unsigned data_len, const FLAC__int32 qlp_coeff[], unsigned order, int lp_quantization, FLAC__int32 residual[]); +void FLAC__lpc_compute_residual_from_qlp_coefficients_wide_asm_ia32(const FLAC__int32 *data, unsigned data_len, const FLAC__int32 qlp_coeff[], unsigned order, int lp_quantization, FLAC__int32 residual[]); +# endif +# endif +# if (defined FLAC__CPU_IA32 || defined FLAC__CPU_X86_64) && FLAC__HAS_X86INTRIN +# ifdef FLAC__SSE2_SUPPORTED +void FLAC__lpc_compute_residual_from_qlp_coefficients_16_intrin_sse2(const FLAC__int32 *data, unsigned data_len, const FLAC__int32 qlp_coeff[], unsigned order, int lp_quantization, FLAC__int32 residual[]); +void FLAC__lpc_compute_residual_from_qlp_coefficients_intrin_sse2(const FLAC__int32 *data, unsigned data_len, const FLAC__int32 qlp_coeff[], unsigned order, int lp_quantization, FLAC__int32 residual[]); +# endif +# ifdef FLAC__SSE4_1_SUPPORTED +void FLAC__lpc_compute_residual_from_qlp_coefficients_intrin_sse41(const FLAC__int32 *data, unsigned data_len, const FLAC__int32 qlp_coeff[], unsigned order, int lp_quantization, FLAC__int32 residual[]); +void FLAC__lpc_compute_residual_from_qlp_coefficients_wide_intrin_sse41(const FLAC__int32 *data, unsigned data_len, const FLAC__int32 qlp_coeff[], unsigned order, int lp_quantization, FLAC__int32 residual[]); +# endif +# ifdef FLAC__AVX2_SUPPORTED +void FLAC__lpc_compute_residual_from_qlp_coefficients_16_intrin_avx2(const FLAC__int32 *data, unsigned data_len, const FLAC__int32 qlp_coeff[], unsigned order, int lp_quantization, FLAC__int32 residual[]); +void FLAC__lpc_compute_residual_from_qlp_coefficients_intrin_avx2(const FLAC__int32 *data, unsigned data_len, const FLAC__int32 qlp_coeff[], unsigned order, int lp_quantization, FLAC__int32 residual[]); +void FLAC__lpc_compute_residual_from_qlp_coefficients_wide_intrin_avx2(const FLAC__int32 *data, unsigned data_len, const FLAC__int32 qlp_coeff[], unsigned order, int lp_quantization, FLAC__int32 residual[]); +# endif +# endif +#endif + +#endif /* !defined FLAC__INTEGER_ONLY_LIBRARY */ + +/* + * FLAC__lpc_restore_signal() + * -------------------------------------------------------------------- + * Restore the original signal by summing the residual and the + * predictor. + * + * IN residual[0,data_len-1] residual signal + * IN data_len length of original signal + * IN qlp_coeff[0,order-1] quantized LP coefficients + * IN order > 0 LP order + * IN lp_quantization quantization of LP coefficients in bits + * *** IMPORTANT: the caller must pass in the historical samples: + * IN data[-order,-1] previously-reconstructed historical samples + * OUT data[0,data_len-1] original signal + */ +void FLAC__lpc_restore_signal(const FLAC__int32 residual[], unsigned data_len, const FLAC__int32 qlp_coeff[], unsigned order, int lp_quantization, FLAC__int32 data[]); +void FLAC__lpc_restore_signal_wide(const FLAC__int32 residual[], unsigned data_len, const FLAC__int32 qlp_coeff[], unsigned order, int lp_quantization, FLAC__int32 data[]); +#ifndef FLAC__NO_ASM +# ifdef FLAC__CPU_IA32 +# ifdef FLAC__HAS_NASM +void FLAC__lpc_restore_signal_asm_ia32(const FLAC__int32 residual[], unsigned data_len, const FLAC__int32 qlp_coeff[], unsigned order, int lp_quantization, FLAC__int32 data[]); +void FLAC__lpc_restore_signal_asm_ia32_mmx(const FLAC__int32 residual[], unsigned data_len, const FLAC__int32 qlp_coeff[], unsigned order, int lp_quantization, FLAC__int32 data[]); +void FLAC__lpc_restore_signal_wide_asm_ia32(const FLAC__int32 residual[], unsigned data_len, const FLAC__int32 qlp_coeff[], unsigned order, int lp_quantization, FLAC__int32 data[]); +# endif /* FLAC__HAS_NASM */ +# endif /* FLAC__CPU_IA32 */ +# if (defined FLAC__CPU_IA32 || defined FLAC__CPU_X86_64) && FLAC__HAS_X86INTRIN +# ifdef FLAC__SSE2_SUPPORTED +void FLAC__lpc_restore_signal_16_intrin_sse2(const FLAC__int32 residual[], unsigned data_len, const FLAC__int32 qlp_coeff[], unsigned order, int lp_quantization, FLAC__int32 data[]); +# endif +# ifdef FLAC__SSE4_1_SUPPORTED +void FLAC__lpc_restore_signal_wide_intrin_sse41(const FLAC__int32 residual[], unsigned data_len, const FLAC__int32 qlp_coeff[], unsigned order, int lp_quantization, FLAC__int32 data[]); +# endif +# endif +#endif /* FLAC__NO_ASM */ + +#ifndef FLAC__INTEGER_ONLY_LIBRARY + +/* + * FLAC__lpc_compute_expected_bits_per_residual_sample() + * -------------------------------------------------------------------- + * Compute the expected number of bits per residual signal sample + * based on the LP error (which is related to the residual variance). + * + * IN lpc_error >= 0.0 error returned from calculating LP coefficients + * IN total_samples > 0 # of samples in residual signal + * RETURN expected bits per sample + */ +double FLAC__lpc_compute_expected_bits_per_residual_sample(double lpc_error, unsigned total_samples); +double FLAC__lpc_compute_expected_bits_per_residual_sample_with_error_scale(double lpc_error, double error_scale); + +/* + * FLAC__lpc_compute_best_order() + * -------------------------------------------------------------------- + * Compute the best order from the array of signal errors returned + * during coefficient computation. + * + * IN lpc_error[0,max_order-1] >= 0.0 error returned from calculating LP coefficients + * IN max_order > 0 max LP order + * IN total_samples > 0 # of samples in residual signal + * IN overhead_bits_per_order # of bits overhead for each increased LP order + * (includes warmup sample size and quantized LP coefficient) + * RETURN [1,max_order] best order + */ +unsigned FLAC__lpc_compute_best_order(const double lpc_error[], unsigned max_order, unsigned total_samples, unsigned overhead_bits_per_order); + +#endif /* !defined FLAC__INTEGER_ONLY_LIBRARY */ + +#endif diff --git a/core/deps/flac/src/libFLAC/include/private/md5.h b/core/deps/flac/src/libFLAC/include/private/md5.h new file mode 100644 index 000000000..c665ab313 --- /dev/null +++ b/core/deps/flac/src/libFLAC/include/private/md5.h @@ -0,0 +1,50 @@ +#ifndef FLAC__PRIVATE__MD5_H +#define FLAC__PRIVATE__MD5_H + +/* + * This is the header file for the MD5 message-digest algorithm. + * The algorithm is due to Ron Rivest. This code was + * written by Colin Plumb in 1993, no copyright is claimed. + * This code is in the public domain; do with it what you wish. + * + * Equivalent code is available from RSA Data Security, Inc. + * This code has been tested against that, and is equivalent, + * except that you don't need to include two pages of legalese + * with every copy. + * + * To compute the message digest of a chunk of bytes, declare an + * MD5Context structure, pass it to MD5Init, call MD5Update as + * needed on buffers full of bytes, and then call MD5Final, which + * will fill a supplied 16-byte array with the digest. + * + * Changed so as no longer to depend on Colin Plumb's `usual.h' + * header definitions; now uses stuff from dpkg's config.h + * - Ian Jackson . + * Still in the public domain. + * + * Josh Coalson: made some changes to integrate with libFLAC. + * Still in the public domain, with no warranty. + */ + +#include "FLAC/ordinals.h" + +typedef union { + FLAC__byte *p8; + FLAC__int16 *p16; + FLAC__int32 *p32; +} FLAC__multibyte; + +typedef struct { + FLAC__uint32 in[16]; + FLAC__uint32 buf[4]; + FLAC__uint32 bytes[2]; + FLAC__multibyte internal_buf; + size_t capacity; +} FLAC__MD5Context; + +void FLAC__MD5Init(FLAC__MD5Context *context); +void FLAC__MD5Final(FLAC__byte digest[16], FLAC__MD5Context *context); + +FLAC__bool FLAC__MD5Accumulate(FLAC__MD5Context *ctx, const FLAC__int32 * const signal[], unsigned channels, unsigned samples, unsigned bytes_per_sample); + +#endif diff --git a/core/deps/flac/src/libFLAC/include/private/memory.h b/core/deps/flac/src/libFLAC/include/private/memory.h new file mode 100644 index 000000000..f103c531f --- /dev/null +++ b/core/deps/flac/src/libFLAC/include/private/memory.h @@ -0,0 +1,58 @@ +/* libFLAC - Free Lossless Audio Codec library + * Copyright (C) 2001-2009 Josh Coalson + * Copyright (C) 2011-2016 Xiph.Org Foundation + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * - Neither the name of the Xiph.org Foundation nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef FLAC__PRIVATE__MEMORY_H +#define FLAC__PRIVATE__MEMORY_H + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include /* for size_t */ + +#include "private/float.h" +#include "FLAC/ordinals.h" /* for FLAC__bool */ + +/* Returns the unaligned address returned by malloc. + * Use free() on this address to deallocate. + */ +void *FLAC__memory_alloc_aligned(size_t bytes, void **aligned_address); +FLAC__bool FLAC__memory_alloc_aligned_int32_array(size_t elements, FLAC__int32 **unaligned_pointer, FLAC__int32 **aligned_pointer); +FLAC__bool FLAC__memory_alloc_aligned_uint32_array(size_t elements, FLAC__uint32 **unaligned_pointer, FLAC__uint32 **aligned_pointer); +FLAC__bool FLAC__memory_alloc_aligned_uint64_array(size_t elements, FLAC__uint64 **unaligned_pointer, FLAC__uint64 **aligned_pointer); +FLAC__bool FLAC__memory_alloc_aligned_unsigned_array(size_t elements, unsigned **unaligned_pointer, unsigned **aligned_pointer); +#ifndef FLAC__INTEGER_ONLY_LIBRARY +FLAC__bool FLAC__memory_alloc_aligned_real_array(size_t elements, FLAC__real **unaligned_pointer, FLAC__real **aligned_pointer); +#endif +void *safe_malloc_mul_2op_p(size_t size1, size_t size2); + +#endif diff --git a/core/deps/flac/src/libFLAC/include/private/metadata.h b/core/deps/flac/src/libFLAC/include/private/metadata.h new file mode 100644 index 000000000..161947fd7 --- /dev/null +++ b/core/deps/flac/src/libFLAC/include/private/metadata.h @@ -0,0 +1,46 @@ +/* libFLAC - Free Lossless Audio Codec library + * Copyright (C) 2002-2009 Josh Coalson + * Copyright (C) 2011-2016 Xiph.Org Foundation + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * - Neither the name of the Xiph.org Foundation nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef FLAC__PRIVATE__METADATA_H +#define FLAC__PRIVATE__METADATA_H + +#include "FLAC/metadata.h" + +/* WATCHOUT: all malloc()ed data in the block is free()ed; this may not + * be a consistent state (e.g. PICTURE) or equivalent to the initial + * state after FLAC__metadata_object_new() + */ +void FLAC__metadata_object_delete_data(FLAC__StreamMetadata *object); + +void FLAC__metadata_object_cuesheet_track_delete_data(FLAC__StreamMetadata_CueSheet_Track *object); + +#endif diff --git a/core/deps/flac/src/libFLAC/include/private/window.h b/core/deps/flac/src/libFLAC/include/private/window.h new file mode 100644 index 000000000..bfed7740b --- /dev/null +++ b/core/deps/flac/src/libFLAC/include/private/window.h @@ -0,0 +1,74 @@ +/* libFLAC - Free Lossless Audio Codec library + * Copyright (C) 2006-2009 Josh Coalson + * Copyright (C) 2011-2016 Xiph.Org Foundation + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * - Neither the name of the Xiph.org Foundation nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef FLAC__PRIVATE__WINDOW_H +#define FLAC__PRIVATE__WINDOW_H + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include "private/float.h" +#include "FLAC/format.h" + +#ifndef FLAC__INTEGER_ONLY_LIBRARY + +/* + * FLAC__window_*() + * -------------------------------------------------------------------- + * Calculates window coefficients according to different apodization + * functions. + * + * OUT window[0,L-1] + * IN L (number of points in window) + */ +void FLAC__window_bartlett(FLAC__real *window, const FLAC__int32 L); +void FLAC__window_bartlett_hann(FLAC__real *window, const FLAC__int32 L); +void FLAC__window_blackman(FLAC__real *window, const FLAC__int32 L); +void FLAC__window_blackman_harris_4term_92db_sidelobe(FLAC__real *window, const FLAC__int32 L); +void FLAC__window_connes(FLAC__real *window, const FLAC__int32 L); +void FLAC__window_flattop(FLAC__real *window, const FLAC__int32 L); +void FLAC__window_gauss(FLAC__real *window, const FLAC__int32 L, const FLAC__real stddev); /* 0.0 < stddev <= 0.5 */ +void FLAC__window_hamming(FLAC__real *window, const FLAC__int32 L); +void FLAC__window_hann(FLAC__real *window, const FLAC__int32 L); +void FLAC__window_kaiser_bessel(FLAC__real *window, const FLAC__int32 L); +void FLAC__window_nuttall(FLAC__real *window, const FLAC__int32 L); +void FLAC__window_rectangle(FLAC__real *window, const FLAC__int32 L); +void FLAC__window_triangle(FLAC__real *window, const FLAC__int32 L); +void FLAC__window_tukey(FLAC__real *window, const FLAC__int32 L, const FLAC__real p); +void FLAC__window_partial_tukey(FLAC__real *window, const FLAC__int32 L, const FLAC__real p, const FLAC__real start, const FLAC__real end); +void FLAC__window_punchout_tukey(FLAC__real *window, const FLAC__int32 L, const FLAC__real p, const FLAC__real start, const FLAC__real end); +void FLAC__window_welch(FLAC__real *window, const FLAC__int32 L); + +#endif /* !defined FLAC__INTEGER_ONLY_LIBRARY */ + +#endif diff --git a/core/deps/flac/src/libFLAC/include/protected/stream_decoder.h b/core/deps/flac/src/libFLAC/include/protected/stream_decoder.h new file mode 100644 index 000000000..5c31c1618 --- /dev/null +++ b/core/deps/flac/src/libFLAC/include/protected/stream_decoder.h @@ -0,0 +1,60 @@ +/* libFLAC - Free Lossless Audio Codec library + * Copyright (C) 2000-2009 Josh Coalson + * Copyright (C) 2011-2016 Xiph.Org Foundation + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * - Neither the name of the Xiph.org Foundation nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef FLAC__PROTECTED__STREAM_DECODER_H +#define FLAC__PROTECTED__STREAM_DECODER_H + +#include "FLAC/stream_decoder.h" +#if FLAC__HAS_OGG +#include "private/ogg_decoder_aspect.h" +#endif + +typedef struct FLAC__StreamDecoderProtected { + FLAC__StreamDecoderState state; + FLAC__StreamDecoderInitStatus initstate; + unsigned channels; + FLAC__ChannelAssignment channel_assignment; + unsigned bits_per_sample; + unsigned sample_rate; /* in Hz */ + unsigned blocksize; /* in samples (per channel) */ + FLAC__bool md5_checking; /* if true, generate MD5 signature of decoded data and compare against signature in the STREAMINFO metadata block */ +#if FLAC__HAS_OGG + FLAC__OggDecoderAspect ogg_decoder_aspect; +#endif +} FLAC__StreamDecoderProtected; + +/* + * return the number of input bytes consumed + */ +unsigned FLAC__stream_decoder_get_input_bytes_unconsumed(const FLAC__StreamDecoder *decoder); + +#endif diff --git a/shell/android-studio/reicast/src/main/jni/Android.mk b/shell/android-studio/reicast/src/main/jni/Android.mk index 603cb2bf9..d45636ba9 100644 --- a/shell/android-studio/reicast/src/main/jni/Android.mk +++ b/shell/android-studio/reicast/src/main/jni/Android.mk @@ -23,6 +23,7 @@ FOR_ANDROID := 1 WEBUI := 1 USE_GLES := 1 CHD5_LZMA := 1 +CHD5_FLAC := 1 ifneq ($(TARGET_ARCH_ABI),armeabi-v7a) NOT_ARM := 1 @@ -60,6 +61,11 @@ ifdef CHD5_LZMA LOCAL_CFLAGS += -D_7ZIP_ST -DCHD5_LZMA endif +# FLAC settings (CHDv5) +ifdef CHD5_FLAC + LOCAL_CFLAGS += -DCHD5_FLAC +endif + # LOCAL_CFLAGS += -std=c++11 LOCAL_CXXFLAGS += -std=c++11 diff --git a/shell/linux/Makefile b/shell/linux/Makefile index 801c743b1..535119068 100644 --- a/shell/linux/Makefile +++ b/shell/linux/Makefile @@ -244,7 +244,6 @@ endif ifdef CHD5_FLAC CFLAGS += -DCHD5_FLAC - LIBS += `pkg-config --libs flac` endif RZDCY_SRC_DIR = $(LOCAL_PATH)/../../core diff --git a/wercker.yml b/wercker.yml index bc13be149..2c55a3a61 100644 --- a/wercker.yml +++ b/wercker.yml @@ -4,7 +4,7 @@ build: steps: - script: name: install-dependencies - code: sudo apt-get clean && sudo apt-get update && sudo apt-get install -y build-essential pkgconf libasound2-dev libgl1-mesa-dev libx11-dev libflac-dev + code: sudo apt-get clean && sudo apt-get update && sudo apt-get install -y build-essential pkgconf libasound2-dev libgl1-mesa-dev libx11-dev - script: name: gcc-version code: gcc --version From 6da63b249a09c02bd876ac1198d2fdfa220ffef1 Mon Sep 17 00:00:00 2001 From: "Christoph \"baka0815\" Schwerdtfeger" Date: Sun, 23 Sep 2018 16:44:23 +0200 Subject: [PATCH 023/319] Trying to break-fix Windows using __inline instead of inline --- core/deps/chdr/cdrom.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/core/deps/chdr/cdrom.h b/core/deps/chdr/cdrom.h index 60c819262..76b34d1eb 100644 --- a/core/deps/chdr/cdrom.h +++ b/core/deps/chdr/cdrom.h @@ -15,6 +15,9 @@ #include +#if defined(_MSC_VER) +#define inline __inline +#endif /*************************************************************************** CONSTANTS From a5e01e4d792f3aefb22f5b3558f51c6ad1386b5b Mon Sep 17 00:00:00 2001 From: Christoph 'baka0815' Schwerdtfeger Date: Sun, 23 Sep 2018 17:00:27 +0200 Subject: [PATCH 024/319] CHD: Enable FLAC on Windows --- core/deps/flac/include/share/win_utf8_io.h | 58 ++++++++++++++++ .../include/share/windows_unicode_filenames.h | 67 ++++++++++++++++++ shell/reicast.vcxproj | 69 ++++++++++++------- 3 files changed, 170 insertions(+), 24 deletions(-) create mode 100644 core/deps/flac/include/share/win_utf8_io.h create mode 100644 core/deps/flac/include/share/windows_unicode_filenames.h diff --git a/core/deps/flac/include/share/win_utf8_io.h b/core/deps/flac/include/share/win_utf8_io.h new file mode 100644 index 000000000..13fd11866 --- /dev/null +++ b/core/deps/flac/include/share/win_utf8_io.h @@ -0,0 +1,58 @@ +/* libFLAC - Free Lossless Audio Codec library + * Copyright (C) 2013-2016 Xiph.Org Foundation + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * - Neither the name of the Xiph.org Foundation nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifdef _WIN32 + +#ifndef flac__win_utf8_io_h +#define flac__win_utf8_io_h + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +size_t strlen_utf8(const char *str); +int win_get_console_width(void); + +int get_utf8_argv(int *argc, char ***argv); + +int printf_utf8(const char *format, ...); +int fprintf_utf8(FILE *stream, const char *format, ...); +int vfprintf_utf8(FILE *stream, const char *format, va_list argptr); + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif +#endif diff --git a/core/deps/flac/include/share/windows_unicode_filenames.h b/core/deps/flac/include/share/windows_unicode_filenames.h new file mode 100644 index 000000000..86820ca15 --- /dev/null +++ b/core/deps/flac/include/share/windows_unicode_filenames.h @@ -0,0 +1,67 @@ +/* libFLAC - Free Lossless Audio Codec library + * Copyright (C) 2013-2016 Xiph.Org Foundation + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * - Neither the name of the Xiph.org Foundation nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifdef _WIN32 + +#ifndef flac__windows_unicode_filenames_h +#define flac__windows_unicode_filenames_h + +#include +#include +#include +#include "FLAC/ordinals.h" + +#ifdef __cplusplus +extern "C" { +#endif + +void flac_internal_set_utf8_filenames(FLAC__bool flag); +FLAC__bool flac_internal_get_utf8_filenames(void); +#define flac_set_utf8_filenames flac_internal_set_utf8_filenames +#define flac_get_utf8_filenames flac_internal_get_utf8_filenames + +FILE* flac_internal_fopen_utf8(const char *filename, const char *mode); +int flac_internal_stat64_utf8(const char *path, struct __stat64 *buffer); +int flac_internal_chmod_utf8(const char *filename, int pmode); +int flac_internal_utime_utf8(const char *filename, struct utimbuf *times); +int flac_internal_unlink_utf8(const char *filename); +int flac_internal_rename_utf8(const char *oldname, const char *newname); + +#include +HANDLE WINAPI flac_internal_CreateFile_utf8(const char *lpFileName, DWORD dwDesiredAccess, DWORD dwShareMode, LPSECURITY_ATTRIBUTES lpSecurityAttributes, DWORD dwCreationDisposition, DWORD dwFlagsAndAttributes, HANDLE hTemplateFile); +#define CreateFile_utf8 flac_internal_CreateFile_utf8 + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif +#endif diff --git a/shell/reicast.vcxproj b/shell/reicast.vcxproj index 25e6c831b..f42dba17d 100644 --- a/shell/reicast.vcxproj +++ b/shell/reicast.vcxproj @@ -63,6 +63,27 @@ + + + + + + + + + + + + + + + + + + + + + @@ -697,8 +718,8 @@ Level3 Full - TARGET_NO_WEBUI;WIN32;_7ZIP_ST;CHD5_LZMA;NDEBUG;_CONSOLE;X86;%(PreprocessorDefinitions) - $(ProjectDir)..\core\;$(ProjectDir)..\core\khronos;%(AdditionalIncludeDirectories) + TARGET_NO_WEBUI;WIN32;_7ZIP_ST;CHD5_LZMA;CHD5_FLAC;PACKAGE_VERSION="1.3.2";FLAC__HAS_OGG=0;FLAC__NO_DLL;HAVE_LROUND;HAVE_STDINT_H;HAVE_STDLIB_H;NDEBUG;_CONSOLE;X86;%(PreprocessorDefinitions) + $(ProjectDir)..\core\;$(ProjectDir)..\core\khronos;$(ProjectDir)..\core\deps\flac\include;$(ProjectDir)..\core\deps\flac\src\libFLAC\include;%(AdditionalIncludeDirectories) /MP %(AdditionalOptions) AnySuitable true @@ -739,8 +760,8 @@ Level3 Full - TARGET_NO_WEBUI;WIN32;_7ZIP_ST;CHD5_LZMA;NDEBUG;_CONSOLE;X86;%(PreprocessorDefinitions) - $(ProjectDir)..\core\;$(ProjectDir)..\core\khronos;%(AdditionalIncludeDirectories) + TARGET_NO_WEBUI;WIN32;_7ZIP_ST;CHD5_LZMA;CHD5_FLAC;PACKAGE_VERSION="1.3.2";FLAC__HAS_OGG=0;FLAC__NO_DLL;HAVE_LROUND;HAVE_STDINT_H;HAVE_STDLIB_H;NDEBUG;_CONSOLE;X86;%(PreprocessorDefinitions) + $(ProjectDir)..\core\;$(ProjectDir)..\core\khronos;$(ProjectDir)..\core\deps\flac\include;$(ProjectDir)..\core\deps\flac\src\libFLAC\include;%(AdditionalIncludeDirectories) /MP %(AdditionalOptions) AnySuitable true @@ -781,8 +802,8 @@ Level3 Full - TARGET_NO_WEBUI;WIN32;_7ZIP_ST;CHD5_LZMA;NDEBUG;_CONSOLE;X86;%(PreprocessorDefinitions) - $(ProjectDir)..\core\;$(ProjectDir)..\core\khronos;%(AdditionalIncludeDirectories) + TARGET_NO_WEBUI;WIN32;_7ZIP_ST;CHD5_LZMA;CHD5_FLAC;PACKAGE_VERSION="1.3.2";FLAC__HAS_OGG=0;FLAC__NO_DLL;HAVE_LROUND;HAVE_STDINT_H;HAVE_STDLIB_H;NDEBUG;_CONSOLE;X86;%(PreprocessorDefinitions) + $(ProjectDir)..\core\;$(ProjectDir)..\core\khronos;$(ProjectDir)..\core\deps\flac\include;$(ProjectDir)..\core\deps\flac\src\libFLAC\include;%(AdditionalIncludeDirectories) /MP %(AdditionalOptions) AnySuitable true @@ -823,8 +844,8 @@ Level3 Full - TARGET_NO_WEBUI;WIN32;_7ZIP_ST;CHD5_LZMA;NDEBUG;_CONSOLE;X86;%(PreprocessorDefinitions) - $(ProjectDir)..\core\;$(ProjectDir)..\core\khronos;%(AdditionalIncludeDirectories) + TARGET_NO_WEBUI;WIN32;_7ZIP_ST;CHD5_LZMA;CHD5_FLAC;PACKAGE_VERSION="1.3.2";FLAC__HAS_OGG=0;FLAC__NO_DLL;HAVE_LROUND;HAVE_STDINT_H;HAVE_STDLIB_H;NDEBUG;_CONSOLE;X86;%(PreprocessorDefinitions) + $(ProjectDir)..\core\;$(ProjectDir)..\core\khronos;$(ProjectDir)..\core\deps\flac\include;$(ProjectDir)..\core\deps\flac\src\libFLAC\include;%(AdditionalIncludeDirectories) /MP %(AdditionalOptions) AnySuitable true @@ -867,8 +888,8 @@ Level3 Full - TARGET_NO_WEBUI;WIN32;_7ZIP_ST;CHD5_LZMA;NDEBUG;_CONSOLE;X86;%(PreprocessorDefinitions) - $(ProjectDir)..\core\;$(ProjectDir)..\core\khronos;%(AdditionalIncludeDirectories) + TARGET_NO_WEBUI;WIN32;_7ZIP_ST;CHD5_LZMA;CHD5_FLAC;PACKAGE_VERSION="1.3.2";FLAC__HAS_OGG=0;FLAC__NO_DLL;HAVE_LROUND;HAVE_STDINT_H;HAVE_STDLIB_H;NDEBUG;_CONSOLE;X86;%(PreprocessorDefinitions) + $(ProjectDir)..\core\;$(ProjectDir)..\core\khronos;$(ProjectDir)..\core\deps\flac\include;$(ProjectDir)..\core\deps\flac\src\libFLAC\include;%(AdditionalIncludeDirectories) /MP %(AdditionalOptions) AnySuitable true @@ -911,8 +932,8 @@ Level3 Full - TARGET_NO_WEBUI;WIN32;_7ZIP_ST;CHD5_LZMA;NDEBUG;_CONSOLE;X86;%(PreprocessorDefinitions) - $(ProjectDir)..\core\;$(ProjectDir)..\core\khronos;%(AdditionalIncludeDirectories) + TARGET_NO_WEBUI;WIN32;_7ZIP_ST;CHD5_LZMA;CHD5_FLAC;PACKAGE_VERSION="1.3.2";FLAC__HAS_OGG=0;FLAC__NO_DLL;HAVE_LROUND;HAVE_STDINT_H;HAVE_STDLIB_H;NDEBUG;_CONSOLE;X86;%(PreprocessorDefinitions) + $(ProjectDir)..\core\;$(ProjectDir)..\core\khronos;$(ProjectDir)..\core\deps\flac\include;$(ProjectDir)..\core\deps\flac\src\libFLAC\include;%(AdditionalIncludeDirectories) /MP %(AdditionalOptions) AnySuitable true @@ -955,8 +976,8 @@ Level3 Disabled - DEF_CONSOLE;TARGET_NO_WEBUI;WIN32;_7ZIP_ST;CHD5_LZMA;_DEBUG;_CONSOLE;X86;%(PreprocessorDefinitions) - $(ProjectDir)..\core\;$(ProjectDir)..\core\khronos;%(AdditionalIncludeDirectories) + DEF_CONSOLE;TARGET_NO_WEBUI;WIN32;_7ZIP_ST;CHD5_LZMA;CHD5_FLAC;PACKAGE_VERSION="1.3.2";FLAC__HAS_OGG=0;FLAC__NO_DLL;HAVE_LROUND;HAVE_STDINT_H;HAVE_STDLIB_H;_DEBUG;_CONSOLE;X86;%(PreprocessorDefinitions) + $(ProjectDir)..\core\;$(ProjectDir)..\core\khronos;$(ProjectDir)..\core\deps\flac\include;$(ProjectDir)..\core\deps\flac\src\libFLAC\include;%(AdditionalIncludeDirectories) true false Default @@ -986,8 +1007,8 @@ Level3 Disabled - DEF_CONSOLE;TARGET_NO_WEBUI;WIN32;_7ZIP_ST;CHD5_LZMA;_DEBUG;_CONSOLE;X86;%(PreprocessorDefinitions) - $(ProjectDir)..\core\;$(ProjectDir)..\core\khronos;%(AdditionalIncludeDirectories) + DEF_CONSOLE;TARGET_NO_WEBUI;WIN32;_7ZIP_ST;CHD5_LZMA;CHD5_FLAC;PACKAGE_VERSION="1.3.2";FLAC__HAS_OGG=0;FLAC__NO_DLL;HAVE_LROUND;HAVE_STDINT_H;HAVE_STDLIB_H;_DEBUG;_CONSOLE;X86;%(PreprocessorDefinitions) + $(ProjectDir)..\core\;$(ProjectDir)..\core\khronos;$(ProjectDir)..\core\deps\flac\include;$(ProjectDir)..\core\deps\flac\src\libFLAC\include;%(AdditionalIncludeDirectories) true false Default @@ -1017,8 +1038,8 @@ Level3 Disabled - DEF_CONSOLE;TARGET_NO_WEBUI;WIN32;_7ZIP_ST;CHD5_LZMA;_DEBUG;_CONSOLE;X86;%(PreprocessorDefinitions) - $(ProjectDir)..\core\;$(ProjectDir)..\core\khronos;%(AdditionalIncludeDirectories) + DEF_CONSOLE;TARGET_NO_WEBUI;WIN32;_7ZIP_ST;CHD5_LZMA;CHD5_FLAC;PACKAGE_VERSION="1.3.2";FLAC__HAS_OGG=0;FLAC__NO_DLL;HAVE_LROUND;HAVE_STDINT_H;HAVE_STDLIB_H;_DEBUG;_CONSOLE;X86;%(PreprocessorDefinitions) + $(ProjectDir)..\core\;$(ProjectDir)..\core\khronos;$(ProjectDir)..\core\deps\flac\include;$(ProjectDir)..\core\deps\flac\src\libFLAC\include;%(AdditionalIncludeDirectories) true false Default @@ -1048,8 +1069,8 @@ Level3 Disabled - DEF_CONSOLE;TARGET_NO_WEBUI;WIN32;_7ZIP_ST;CHD5_LZMA;_DEBUG;_CONSOLE;X86;%(PreprocessorDefinitions) - $(ProjectDir)..\core\;$(ProjectDir)..\core\khronos;%(AdditionalIncludeDirectories) + DEF_CONSOLE;TARGET_NO_WEBUI;WIN32;_7ZIP_ST;CHD5_LZMA;CHD5_FLAC;PACKAGE_VERSION="1.3.2";FLAC__HAS_OGG=0;FLAC__NO_DLL;HAVE_LROUND;HAVE_STDINT_H;HAVE_STDLIB_H;_DEBUG;_CONSOLE;X86;%(PreprocessorDefinitions) + $(ProjectDir)..\core\;$(ProjectDir)..\core\khronos;$(ProjectDir)..\core\deps\flac\include;$(ProjectDir)..\core\deps\flac\src\libFLAC\include;%(AdditionalIncludeDirectories) true false Default @@ -1084,8 +1105,8 @@ Level3 Disabled - DEF_CONSOLE;TARGET_NO_WEBUI;WIN32;_7ZIP_ST;CHD5_LZMA;_DEBUG;_CONSOLE;X86;%(PreprocessorDefinitions) - $(ProjectDir)..\core\;$(ProjectDir)..\core\khronos;%(AdditionalIncludeDirectories) + DEF_CONSOLE;TARGET_NO_WEBUI;WIN32;_7ZIP_ST;CHD5_LZMA;CHD5_FLAC;PACKAGE_VERSION="1.3.2";FLAC__HAS_OGG=0;FLAC__NO_DLL;HAVE_LROUND;HAVE_STDINT_H;HAVE_STDLIB_H;_DEBUG;_CONSOLE;X86;%(PreprocessorDefinitions) + $(ProjectDir)..\core\;$(ProjectDir)..\core\khronos;$(ProjectDir)..\core\deps\flac\include;$(ProjectDir)..\core\deps\flac\src\libFLAC\include;%(AdditionalIncludeDirectories) true false Default @@ -1120,8 +1141,8 @@ Level3 Disabled - DEF_CONSOLE;TARGET_NO_WEBUI;WIN32;_7ZIP_ST;CHD5_LZMA;_DEBUG;_CONSOLE;X86;%(PreprocessorDefinitions) - $(ProjectDir)..\core\;$(ProjectDir)..\core\khronos;%(AdditionalIncludeDirectories) + DEF_CONSOLE;TARGET_NO_WEBUI;WIN32;_7ZIP_ST;CHD5_LZMA;CHD5_FLAC;PACKAGE_VERSION="1.3.2";FLAC__HAS_OGG=0;FLAC__NO_DLL;HAVE_LROUND;HAVE_STDINT_H;HAVE_STDLIB_H;_DEBUG;_CONSOLE;X86;%(PreprocessorDefinitions) + $(ProjectDir)..\core\;$(ProjectDir)..\core\khronos;$(ProjectDir)..\core\deps\flac\include;$(ProjectDir)..\core\deps\flac\src\libFLAC\include;%(AdditionalIncludeDirectories) true false Default From b6843d6726fbb38a8ac0f5b5c201387bf149fdd1 Mon Sep 17 00:00:00 2001 From: Ender's Games Date: Sun, 23 Sep 2018 12:09:09 -0400 Subject: [PATCH 025/319] Android: Add style resources for theme support --- .../reicast/src/main/AndroidManifest.xml | 2 +- .../reicast/src/main/res/values/colors.xml | 7 +++++ .../reicast/src/main/res/values/styles.xml | 26 ++++++++++++++++++- 3 files changed, 33 insertions(+), 2 deletions(-) diff --git a/shell/android-studio/reicast/src/main/AndroidManifest.xml b/shell/android-studio/reicast/src/main/AndroidManifest.xml index 68228266c..bceb7656b 100644 --- a/shell/android-studio/reicast/src/main/AndroidManifest.xml +++ b/shell/android-studio/reicast/src/main/AndroidManifest.xml @@ -50,7 +50,7 @@ android:name=".MainActivity" android:configChanges="orientation|navigation|screenSize|screenLayout|uiMode|keyboard|keyboardHidden" android:label="@string/app_name" - android:theme="@style/AppTheme.NoActionBar"> + android:theme="@style/AppTheme.Dark.NoActionBar"> diff --git a/shell/android-studio/reicast/src/main/res/values/colors.xml b/shell/android-studio/reicast/src/main/res/values/colors.xml index a86e8bb50..324258f38 100644 --- a/shell/android-studio/reicast/src/main/res/values/colors.xml +++ b/shell/android-studio/reicast/src/main/res/values/colors.xml @@ -6,6 +6,13 @@ #AA1074bc #F5A623 + #3F51B5 + #303F9F + #FF4081 + + #ff8a80 + #c85a54 + #8effae #ffd34c #0f0 diff --git a/shell/android-studio/reicast/src/main/res/values/styles.xml b/shell/android-studio/reicast/src/main/res/values/styles.xml index c58ab9898..e66be7536 100644 --- a/shell/android-studio/reicast/src/main/res/values/styles.xml +++ b/shell/android-studio/reicast/src/main/res/values/styles.xml @@ -11,17 +11,41 @@ @color/white @color/body_text_secondary @color/white - @color/windowBackground + @color/colorPrimary true true false + + + + + + + + - - - - + + - - - - - + + - - - - - - - - - - - - - From ca750ae454334457dd5315af8c9e71c22af26658 Mon Sep 17 00:00:00 2001 From: Flyinghead Date: Thu, 28 Mar 2019 11:41:48 +0100 Subject: [PATCH 190/319] arm: aica RAM overflow crash with arm32 and arm64 recs Move target platform #def's to build.h Fix Sturmwind and Volgarr crashes --- core/build.h | 109 ++++++++++++++++++++++++++++++++++++++-- core/hw/arm7/arm64.cpp | 9 +++- core/rec-ARM/ngen_arm.S | 8 +++ core/types.h | 100 ------------------------------------ 4 files changed, 120 insertions(+), 106 deletions(-) diff --git a/core/build.h b/core/build.h index c3dc80b9a..a9a9777dd 100755 --- a/core/build.h +++ b/core/build.h @@ -286,10 +286,6 @@ #define FEAT_HAS_SOFTREND BUILD_COMPILER == COMPILER_VC //GCC wants us to enable sse4 globaly to enable intrins #endif -#define RAM_SIZE_MAX (32*1024*1024) -#define VRAM_SIZE_MAX (16*1024*1024) -#define ARAM_SIZE_MAX (8*1024*1024) - //Depricated build configs #ifdef HOST_NO_REC #error Dont use HOST_NO_REC @@ -298,3 +294,108 @@ #ifdef HOST_NO_AREC #error Dont use HOST_NO_AREC #endif + +// TARGET PLATFORM + +#define RAM_SIZE_MAX (32*1024*1024) +#define VRAM_SIZE_MAX (16*1024*1024) +#define ARAM_SIZE_MAX (8*1024*1024) + +#if (DC_PLATFORM==DC_PLATFORM_DREAMCAST) + + #define BUILD_DREAMCAST 1 + + //DC : 16 mb ram, 8 mb vram, 2 mb aram, 2 mb bios, 128k flash + #define RAM_SIZE (16*1024*1024) + #define VRAM_SIZE (8*1024*1024) + #define ARAM_SIZE (2*1024*1024) + #define BIOS_SIZE (2*1024*1024) + #define FLASH_SIZE (128*1024) + + #define ROM_PREFIX "dc_" + #define ROM_NAMES + #define NVR_OPTIONAL 0 + +#elif (DC_PLATFORM==DC_PLATFORM_DEV_UNIT) + + #define BUILD_DEV_UNIT 1 + + //Devkit : 32 mb ram, 8? mb vram, 2? mb aram, 2? mb bios, ? flash + #define RAM_SIZE (32*1024*1024) + #define VRAM_SIZE (8*1024*1024) + #define ARAM_SIZE (2*1024*1024) + #define BIOS_SIZE (2*1024*1024) + #define FLASH_SIZE (128*1024) + + #define ROM_PREFIX "hkt_" + #define ROM_NAMES + #define NVR_OPTIONAL 0 + +#elif (DC_PLATFORM==DC_PLATFORM_NAOMI) + + //Naomi : 32 mb ram, 16 mb vram, 8 mb aram, 2 mb bios, ? flash + #define RAM_SIZE (32*1024*1024) + #define VRAM_SIZE (16*1024*1024) + #define ARAM_SIZE (8*1024*1024) + #define BIOS_SIZE (2*1024*1024) + #define BBSRAM_SIZE (32*1024) + + #define ROM_PREFIX "naomi_" + #define ROM_NAMES ";epr-21576d.bin" + #define NVR_OPTIONAL 1 + +#elif (DC_PLATFORM==DC_PLATFORM_NAOMI2) + + //Naomi2 : 32 mb ram, 16 mb vram, 8 mb aram, 2 mb bios, ? flash + #define RAM_SIZE (32*1024*1024) + #define VRAM_SIZE (16*1024*1024) + #define ARAM_SIZE (8*1024*1024) + #define BIOS_SIZE (2*1024*1024) + #define BBSRAM_SIZE (32*1024) + + #define ROM_PREFIX "n2_" + #define ROM_NAMES + #define NVR_OPTIONAL 1 + +#elif (DC_PLATFORM==DC_PLATFORM_ATOMISWAVE) + + #define BUILD_ATOMISWAVE 1 + + //Atomiswave : 16 mb ram, 8 mb vram, 8 mb aram, 128kb bios on flash, 128kb battery-backed ram + #define RAM_SIZE (16*1024*1024) + #define VRAM_SIZE (8*1024*1024) + #define ARAM_SIZE (8*1024*1024) + #define BIOS_SIZE (128*1024) + #define BBSRAM_SIZE (128*1024) + + #define ROM_PREFIX "aw_" + #define ROM_NAMES ";bios.ic23_l" + #define NVR_OPTIONAL 1 + +#else + #error invalid build config +#endif + +#define RAM_MASK (RAM_SIZE-1) +#define VRAM_MASK (VRAM_SIZE-1) +#define ARAM_MASK (ARAM_SIZE-1) +#define BIOS_MASK (BIOS_SIZE-1) + +#ifdef FLASH_SIZE +#define FLASH_MASK (FLASH_SIZE-1) +#endif + +#ifdef BBSRAM_SIZE +#define BBSRAM_MASK (BBSRAM_SIZE-1) +#endif + +#define GD_CLOCK 33868800 //GDROM XTAL -- 768fs + +#define AICA_CORE_CLOCK (GD_CLOCK*4/3) //[45158400] GD->PLL 3:4 -> AICA CORE -- 1024fs +#define ADAC_CLOCK (AICA_CORE_CLOCK/2) //[11289600] 44100*256, AICA CORE -> PLL 4:1 -> ADAC -- 256fs +#define AICA_ARM_CLOCK (AICA_CORE_CLOCK/2) //[22579200] AICA CORE -> PLL 2:1 -> ARM +#define AICA_SDRAM_CLOCK (GD_CLOCK*2) //[67737600] GD-> PLL 2 -> SDRAM +#define SH4_MAIN_CLOCK (200*1000*1000) //[200000000] XTal(13.5) -> PLL (33.3) -> PLL 1:6 (200) +#define SH4_RAM_CLOCK (100*1000*1000) //[100000000] XTal(13.5) -> PLL (33.3) -> PLL 1:3 (100) , also suplied to HOLLY chip +#define G2_BUS_CLOCK (25*1000*1000) //[25000000] from Holly, from SH4_RAM_CLOCK w/ 2 2:1 plls + diff --git a/core/hw/arm7/arm64.cpp b/core/hw/arm7/arm64.cpp index bcaf7d70c..89e57ab66 100644 --- a/core/hw/arm7/arm64.cpp +++ b/core/hw/arm7/arm64.cpp @@ -499,8 +499,13 @@ __asm__ ( ".hidden arm_dispatch \n" "arm_dispatch: \n\t" "ldp w0, w1, [x28, #184] \n\t" // load Next PC, interrupt - - "ubfx w2, w0, #2, #21 \n\t" // w2 = pc >> 2. Note: assuming address space <= 8 MB (23 bits) +#if ARAM_SIZE == 2*1024*1024 + "ubfx w2, w0, #2, #19 \n\t" // w2 = pc >> 2. Note: assuming address space == 2 MB (21 bits) +#elif ARAM_SIZE == 8*1024*1024 + "ubfx w2, w0, #2, #21 \n\t" // w2 = pc >> 2. Note: assuming address space == 8 MB (23 bits) +#else +#error Unsupported AICA RAM size +#endif "cbnz w1, arm_dofiq \n\t" // if interrupt pending, handle it "add x2, x26, x2, lsl #3 \n\t" // x2 = EntryPoints + pc << 1 diff --git a/core/rec-ARM/ngen_arm.S b/core/rec-ARM/ngen_arm.S index 61ab59e82..55fbc8fad 100644 --- a/core/rec-ARM/ngen_arm.S +++ b/core/rec-ARM/ngen_arm.S @@ -58,6 +58,7 @@ bkpt #0 bkpt #endif ubfx r0,r3,#5,#19 @ get vram offset + @ should be only 18 bits for 8MB VRAM but it wraps around on dc add r3,r1,#0x04000000 @ get vram ptr from r1, part 1 add r3,#512 @ get ram ptr from r1, part 2 add r3,r0,lsl #5 @ ram + offset @@ -180,6 +181,7 @@ CSYM(no_update): @ next_pc _MUST_ be on r4 *R4 NOT R0 anymore* #if RAM_SIZE_MAX == 33554432 sub r2,r8,#0x4100000 ubfx r1,r4,#1,#24 @ 24+1 bits: 32 MB + @ RAM wraps around so if actual RAM size is 16MB, we won't overflow #elif RAM_SIZE_MAX == 16777216 sub r2,r8,#0x2100000 ubfx r1,r4,#1,#23 @ 23+1 bits: 16 MB @@ -241,7 +243,13 @@ HIDDEN(arm_dispatch) CSYM(arm_dispatch): ldrd r0,r1,[r8,#184] @load: Next PC, interrupt +#if ARAM_SIZE == 2*1024*1024 + ubfx r2,r0,#2,#19 @ assuming 2 MB address space max (21 bits) +#elif ARAM_SIZE == 8*1024*1024 ubfx r2,r0,#2,#21 @ assuming 8 MB address space max (23 bits) +#else +#error Unsupported AICA RAM size +#endif cmp r1,#0 bne arm_dofiq diff --git a/core/types.h b/core/types.h index a893d27e2..b3f809dc3 100644 --- a/core/types.h +++ b/core/types.h @@ -204,106 +204,6 @@ struct vram_block void* userdata; }; - -#if (DC_PLATFORM==DC_PLATFORM_DREAMCAST) - - #define BUILD_DREAMCAST 1 - - //DC : 16 mb ram, 8 mb vram, 2 mb aram, 2 mb bios, 128k flash - #define RAM_SIZE (16*1024*1024) - #define VRAM_SIZE (8*1024*1024) - #define ARAM_SIZE (2*1024*1024) - #define BIOS_SIZE (2*1024*1024) - #define FLASH_SIZE (128*1024) - - #define ROM_PREFIX "dc_" - #define ROM_NAMES - #define NVR_OPTIONAL 0 - -#elif (DC_PLATFORM==DC_PLATFORM_DEV_UNIT) - - #define BUILD_DEV_UNIT 1 - - //Devkit : 32 mb ram, 8? mb vram, 2? mb aram, 2? mb bios, ? flash - #define RAM_SIZE (32*1024*1024) - #define VRAM_SIZE (8*1024*1024) - #define ARAM_SIZE (2*1024*1024) - #define BIOS_SIZE (2*1024*1024) - #define FLASH_SIZE (128*1024) - - #define ROM_PREFIX "hkt_" - #define ROM_NAMES - #define NVR_OPTIONAL 0 - -#elif (DC_PLATFORM==DC_PLATFORM_NAOMI) - - //Naomi : 32 mb ram, 16 mb vram, 8 mb aram, 2 mb bios, ? flash - #define RAM_SIZE (32*1024*1024) - #define VRAM_SIZE (16*1024*1024) - #define ARAM_SIZE (8*1024*1024) - #define BIOS_SIZE (2*1024*1024) - #define BBSRAM_SIZE (32*1024) - - #define ROM_PREFIX "naomi_" - #define ROM_NAMES ";epr-21576d.bin" - #define NVR_OPTIONAL 1 - -#elif (DC_PLATFORM==DC_PLATFORM_NAOMI2) - - //Naomi2 : 32 mb ram, 16 mb vram, 8 mb aram, 2 mb bios, ? flash - #define RAM_SIZE (32*1024*1024) - #define VRAM_SIZE (16*1024*1024) - #define ARAM_SIZE (8*1024*1024) - #define BIOS_SIZE (2*1024*1024) - #define BBSRAM_SIZE (32*1024) - - #define ROM_PREFIX "n2_" - #define ROM_NAMES - #define NVR_OPTIONAL 1 - -#elif (DC_PLATFORM==DC_PLATFORM_ATOMISWAVE) - - #define BUILD_ATOMISWAVE 1 - - //Atomiswave : 16 mb ram, 8 mb vram, 8 mb aram, 128kb bios on flash, 128kb battery-backed ram - #define RAM_SIZE (16*1024*1024) - #define VRAM_SIZE (8*1024*1024) - #define ARAM_SIZE (8*1024*1024) - #define BIOS_SIZE (128*1024) - #define BBSRAM_SIZE (128*1024) - - #define ROM_PREFIX "aw_" - #define ROM_NAMES ";bios.ic23_l" - #define NVR_OPTIONAL 1 - -#else - #error invalid build config -#endif - -#define RAM_MASK (RAM_SIZE-1) -#define VRAM_MASK (VRAM_SIZE-1) -#define ARAM_MASK (ARAM_SIZE-1) -#define BIOS_MASK (BIOS_SIZE-1) - -#ifdef FLASH_SIZE -#define FLASH_MASK (FLASH_SIZE-1) -#endif - -#ifdef BBSRAM_SIZE -#define BBSRAM_MASK (BBSRAM_SIZE-1) -#endif - -#define GD_CLOCK 33868800 //GDROM XTAL -- 768fs - -#define AICA_CORE_CLOCK (GD_CLOCK*4/3) //[45158400] GD->PLL 3:4 -> AICA CORE -- 1024fs -#define ADAC_CLOCK (AICA_CORE_CLOCK/2) //[11289600] 44100*256, AICA CORE -> PLL 4:1 -> ADAC -- 256fs -#define AICA_ARM_CLOCK (AICA_CORE_CLOCK/2) //[22579200] AICA CORE -> PLL 2:1 -> ARM -#define AICA_SDRAM_CLOCK (GD_CLOCK*2) //[67737600] GD-> PLL 2 -> SDRAM -#define SH4_MAIN_CLOCK (200*1000*1000) //[200000000] XTal(13.5) -> PLL (33.3) -> PLL 1:6 (200) -#define SH4_RAM_CLOCK (100*1000*1000) //[100000000] XTal(13.5) -> PLL (33.3) -> PLL 1:3 (100) , also suplied to HOLLY chip -#define G2_BUS_CLOCK (25*1000*1000) //[25000000] from Holly, from SH4_RAM_CLOCK w/ 2 2:1 plls - - enum ndc_error_codes { rv_ok = 0, //no error From 20b09cf1b71a141b54ec00c324b5063e85a31241 Mon Sep 17 00:00:00 2001 From: Flyinghead Date: Thu, 28 Mar 2019 13:02:08 +0100 Subject: [PATCH 191/319] arm64: disable partially implemented 0x1337 pref optimization hack --- core/rec-ARM64/rec_arm64.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/core/rec-ARM64/rec_arm64.cpp b/core/rec-ARM64/rec_arm64.cpp index ca5d96b4c..d8660c976 100644 --- a/core/rec-ARM64/rec_arm64.cpp +++ b/core/rec-ARM64/rec_arm64.cpp @@ -618,7 +618,7 @@ public: case shop_pref: Mov(w0, regalloc.MapRegister(op.rs1)); - if (op.flags != 0x1337) + //if (op.flags != 0x1337) { Lsr(w1, regalloc.MapRegister(op.rs1), 26); Cmp(w1, 0x38); @@ -634,9 +634,9 @@ public: Ldr(x9, MemOperand(x9)); Sub(x1, x28, offsetof(Sh4RCB, cntx) - offsetof(Sh4RCB, sq_buffer)); } - if (op.flags == 0x1337) - Blr(x9); - else + //if (op.flags == 0x1337) + // Blr(x9); + //else { Label no_branch; B(&no_branch, ne); From 5242af58790ba1815c40c0b1937e0ddbbce31647 Mon Sep 17 00:00:00 2001 From: Flyinghead Date: Thu, 28 Mar 2019 14:26:04 +0100 Subject: [PATCH 192/319] rec-arm64: better pref fix --- core/rec-ARM64/rec_arm64.cpp | 34 ++++++++++++++-------------------- 1 file changed, 14 insertions(+), 20 deletions(-) diff --git a/core/rec-ARM64/rec_arm64.cpp b/core/rec-ARM64/rec_arm64.cpp index d8660c976..cc35f57b6 100644 --- a/core/rec-ARM64/rec_arm64.cpp +++ b/core/rec-ARM64/rec_arm64.cpp @@ -617,31 +617,25 @@ public: break; case shop_pref: - Mov(w0, regalloc.MapRegister(op.rs1)); - //if (op.flags != 0x1337) { Lsr(w1, regalloc.MapRegister(op.rs1), 26); Cmp(w1, 0x38); - } + Label not_sqw; + B(¬_sqw, ne); + Mov(w0, regalloc.MapRegister(op.rs1)); - if (CCN_MMUCR.AT) - { - Ldr(x9, reinterpret_cast(&do_sqw_mmu)); - } - else - { - Sub(x9, x28, offsetof(Sh4RCB, cntx) - offsetof(Sh4RCB, do_sqw_nommu)); - Ldr(x9, MemOperand(x9)); - Sub(x1, x28, offsetof(Sh4RCB, cntx) - offsetof(Sh4RCB, sq_buffer)); - } - //if (op.flags == 0x1337) - // Blr(x9); - //else - { - Label no_branch; - B(&no_branch, ne); + if (CCN_MMUCR.AT) + { + Ldr(x9, reinterpret_cast(&do_sqw_mmu)); + } + else + { + Sub(x9, x28, offsetof(Sh4RCB, cntx) - offsetof(Sh4RCB, do_sqw_nommu)); + Ldr(x9, MemOperand(x9)); + Sub(x1, x28, offsetof(Sh4RCB, cntx) - offsetof(Sh4RCB, sq_buffer)); + } Blr(x9); - Bind(&no_branch); + Bind(¬_sqw); } break; From eb7be51f674989c98a354e5fae087a8b2f3f1c0c Mon Sep 17 00:00:00 2001 From: Flyinghead Date: Thu, 28 Mar 2019 14:53:41 +0100 Subject: [PATCH 193/319] gdrom: stop playing cdda when changing/ejecting disk --- core/hw/gdrom/gdromv3.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/core/hw/gdrom/gdromv3.cpp b/core/hw/gdrom/gdromv3.cpp index fac898596..0d8f82e38 100644 --- a/core/hw/gdrom/gdromv3.cpp +++ b/core/hw/gdrom/gdromv3.cpp @@ -256,6 +256,7 @@ void gd_set_state(gd_states state) void gd_setdisc() { + cdda.playing = false; DiscType newd = (DiscType)libGDR_GetDiscType(); switch(newd) From 47201b9e48b952607defb3a3ad032b09403ed5b7 Mon Sep 17 00:00:00 2001 From: Flyinghead Date: Thu, 28 Mar 2019 17:56:59 +0100 Subject: [PATCH 194/319] input: analog axis as DPad should now work --- core/input/gamepad.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/core/input/gamepad.h b/core/input/gamepad.h index 46996f6c8..5c37c0038 100644 --- a/core/input/gamepad.h +++ b/core/input/gamepad.h @@ -53,8 +53,8 @@ enum DreamcastKey // System axes EMU_AXIS_NONE = 0x00000, - EMU_AXIS_DPAD1_X = 0x00001, - EMU_AXIS_DPAD1_Y = 0x00002, - EMU_AXIS_DPAD2_X = 0x00003, - EMU_AXIS_DPAD2_Y = 0x00004, + EMU_AXIS_DPAD1_X = DC_DPAD_LEFT, + EMU_AXIS_DPAD1_Y = DC_DPAD_UP, + EMU_AXIS_DPAD2_X = DC_DPAD2_LEFT, + EMU_AXIS_DPAD2_Y = DC_DPAD2_RIGHT, }; From 8751e55213d55a41222e236b24013000bed74dfe Mon Sep 17 00:00:00 2001 From: Flyinghead Date: Thu, 28 Mar 2019 18:28:29 +0100 Subject: [PATCH 195/319] input: wait for 200ms before detecting button/axis input when remapping --- core/input/gamepad_device.cpp | 22 ++++++++++++++++++++-- core/input/gamepad_device.h | 13 +++---------- 2 files changed, 23 insertions(+), 12 deletions(-) diff --git a/core/input/gamepad_device.cpp b/core/input/gamepad_device.cpp index 29cb1436a..f8bee9bb7 100644 --- a/core/input/gamepad_device.cpp +++ b/core/input/gamepad_device.cpp @@ -20,6 +20,7 @@ #include #include "gamepad_device.h" #include "rend/gui.h" +#include "oslib/oslib.h" extern void dc_exit(); @@ -32,7 +33,8 @@ std::mutex GamepadDevice::_gamepads_mutex; bool GamepadDevice::gamepad_btn_input(u32 code, bool pressed) { - if (_input_detected != NULL && _detecting_button && pressed) + if (_input_detected != NULL && _detecting_button + && os_GetSeconds() >= _detection_start_time && pressed) { _input_detected(code); _input_detected = NULL; @@ -84,7 +86,8 @@ bool GamepadDevice::gamepad_axis_input(u32 code, int value) v = (get_axis_min_value(code) + get_axis_range(code) - value) * 255 / get_axis_range(code) - 128; else v = (value - get_axis_min_value(code)) * 255 / get_axis_range(code) - 128; //-128 ... + 127 range - if (_input_detected != NULL && !_detecting_button && (v >= 64 || v <= -64)) + if (_input_detected != NULL && !_detecting_button + && os_GetSeconds() >= _detection_start_time && (v >= 64 || v <= -64)) { _input_detected(code); _input_detected = NULL; @@ -235,3 +238,18 @@ void UpdateVibration(u32 port, float power, float inclination, u32 duration_ms) gamepad->rumble(power, inclination, duration_ms); } } + +void GamepadDevice::detect_btn_input(input_detected_cb button_pressed) +{ + _input_detected = button_pressed; + _detecting_button = true; + _detection_start_time = os_GetSeconds() + 0.2; +} + +void GamepadDevice::detect_axis_input(input_detected_cb axis_moved) +{ + _input_detected = axis_moved; + _detecting_button = false; + _detection_start_time = os_GetSeconds() + 0.2; +} + diff --git a/core/input/gamepad_device.h b/core/input/gamepad_device.h index e11c5e90d..d7cb5d4a5 100644 --- a/core/input/gamepad_device.h +++ b/core/input/gamepad_device.h @@ -35,16 +35,8 @@ public: bool gamepad_axis_input(u32 code, int value); virtual ~GamepadDevice() {} - void detect_btn_input(input_detected_cb button_pressed) - { - _input_detected = button_pressed; - _detecting_button = true; - } - void detect_axis_input(input_detected_cb axis_moved) - { - _input_detected = axis_moved; - _detecting_button = false; - } + void detect_btn_input(input_detected_cb button_pressed); + void detect_axis_input(input_detected_cb axis_moved); void cancel_detect_input() { _input_detected = NULL; @@ -104,6 +96,7 @@ private: std::string _api_name; int _maple_port; bool _detecting_button = false; + double _detection_start_time; input_detected_cb _input_detected; bool _remappable; float _dead_zone = 0.1f; From eb017f6cb0977fad5b452a0231b02115e506e53b Mon Sep 17 00:00:00 2001 From: Flyinghead Date: Fri, 29 Mar 2019 11:28:54 +0100 Subject: [PATCH 196/319] evdev: resurrect udev - fix hot swapping --- shell/linux/Makefile | 3 +++ 1 file changed, 3 insertions(+) diff --git a/shell/linux/Makefile b/shell/linux/Makefile index 5c59eefc1..eea87ef76 100644 --- a/shell/linux/Makefile +++ b/shell/linux/Makefile @@ -331,8 +331,11 @@ endif ifdef USE_EVDEV CXXFLAGS += -D USE_EVDEV +ifdef USE_UDEV + CXXFLAGS += -D USE_UDEV LIBS += -ludev endif +endif ifdef USE_JOYSTICK CXXFLAGS += -D USE_JOYSTICK From 36acae9a00100d2f58505c09eeeb27875c754062 Mon Sep 17 00:00:00 2001 From: Flyinghead Date: Fri, 29 Mar 2019 14:19:41 +0100 Subject: [PATCH 197/319] gdi: treat slash as path separator on windows --- core/imgread/gdi.cpp | 25 ++++++++++++------------- 1 file changed, 12 insertions(+), 13 deletions(-) diff --git a/core/imgread/gdi.cpp b/core/imgread/gdi.cpp index 3f59ea5bf..92c896519 100644 --- a/core/imgread/gdi.cpp +++ b/core/imgread/gdi.cpp @@ -3,10 +3,22 @@ #include #include +// On windows, transform / to \\ + +string normalize_path_separator(string path) +{ +#if HOST_OS == OS_WINDOWS + std::replace(path.begin(), path.end(), '/', '\\'); +#endif + + return path; +} + // given file/name.ext or file\name.ext returns file/ or file\, depending on the platform // given name.ext returns ./ or .\, depending on the platform string OS_dirname(string file) { + file = normalize_path_separator(file); #if HOST_OS == OS_WINDOWS const char sep = '\\'; #else @@ -25,19 +37,6 @@ string OS_dirname(string file) return file.substr(0, last_slash + 1); } -// On windows, transform / to \\ -// On linux, transform \\ to / -string normalize_path_separator(string path) -{ - #if HOST_OS == OS_WINDOWS - std::replace( path.begin(), path.end(), '/', '\\'); - #else - std::replace( path.begin(), path.end(), '\\', '/'); - #endif - - return path; -} - #if 0 // TODO: Move this to some tests, make it platform agnostic namespace { struct OS_dirname_Test { From daf4980fc811876e74528d5e960f9e727572ab63 Mon Sep 17 00:00:00 2001 From: Flyinghead Date: Fri, 29 Mar 2019 17:19:18 +0100 Subject: [PATCH 198/319] input: save assigned maple port of each gamepad --- core/input/gamepad_device.cpp | 36 +++++++++++++++++++ core/input/gamepad_device.h | 23 +++--------- core/linux-dist/evdev_gamepad.h | 26 ++++++++------ core/linux-dist/x11.cpp | 1 + core/linux-dist/x11_keyboard.h | 1 + core/nullDC.cpp | 4 +++ core/sdl/sdl_gamepad.h | 9 +++++ core/windows/xinput_gamepad.h | 5 +++ .../emulator/periph/InputDeviceManager.java | 6 ++-- .../reicast/src/main/jni/src/Android.cpp | 9 ++--- .../src/main/jni/src/android_gamepad.h | 7 ++-- .../emulator-osx/emulator-osx/osx_gamepad.h | 2 ++ 12 files changed, 92 insertions(+), 37 deletions(-) diff --git a/core/input/gamepad_device.cpp b/core/input/gamepad_device.cpp index f8bee9bb7..843f18d62 100644 --- a/core/input/gamepad_device.cpp +++ b/core/input/gamepad_device.cpp @@ -21,6 +21,9 @@ #include "gamepad_device.h" #include "rend/gui.h" #include "oslib/oslib.h" +#include "cfg/cfg.h" + +#define MAPLE_PORT_CFG_PREFIX "maple_" extern void dc_exit(); @@ -253,3 +256,36 @@ void GamepadDevice::detect_axis_input(input_detected_cb axis_moved) _detection_start_time = os_GetSeconds() + 0.2; } +void GamepadDevice::Register(std::shared_ptr gamepad) +{ + int maple_port = cfgLoadInt("input", + (MAPLE_PORT_CFG_PREFIX + gamepad->unique_id()).c_str(), 12345); + if (maple_port != 12345) + gamepad->set_maple_port(maple_port); + + _gamepads_mutex.lock(); + _gamepads.push_back(gamepad); + _gamepads_mutex.unlock(); +} + +void GamepadDevice::Unregister(std::shared_ptr gamepad) +{ + gamepad->save_mapping(); + _gamepads_mutex.lock(); + for (auto it = _gamepads.begin(); it != _gamepads.end(); it++) + if (*it == gamepad) { + _gamepads.erase(it); + break; + } + _gamepads_mutex.unlock(); +} + +void GamepadDevice::SaveMaplePorts() +{ + for (int i = 0; i < GamepadDevice::GetGamepadCount(); i++) + { + std::shared_ptr gamepad = GamepadDevice::GetGamepad(i); + if (gamepad != NULL && !gamepad->unique_id().empty()) + cfgSaveInt("input", (MAPLE_PORT_CFG_PREFIX + gamepad->unique_id()).c_str(), gamepad->maple_port()); + } +} diff --git a/core/input/gamepad_device.h b/core/input/gamepad_device.h index d7cb5d4a5..92a75692d 100644 --- a/core/input/gamepad_device.h +++ b/core/input/gamepad_device.h @@ -31,6 +31,7 @@ public: const std::string& name() { return _name; } int maple_port() { return _maple_port; } void set_maple_port(int port) { _maple_port = port; } + const std::string& unique_id() { return _unique_id; } virtual bool gamepad_btn_input(u32 code, bool pressed); bool gamepad_axis_input(u32 code, int value); virtual ~GamepadDevice() {} @@ -50,28 +51,13 @@ public: virtual void update_rumble() {} bool is_rumble_enabled() { return _rumble_enabled; } - static void Register(std::shared_ptr gamepad) - { - _gamepads_mutex.lock(); - _gamepads.push_back(gamepad); - _gamepads_mutex.unlock(); - } + static void Register(std::shared_ptr gamepad); - static void Unregister(std::shared_ptr gamepad) - { - gamepad->save_mapping(); - _gamepads_mutex.lock(); - for (auto it = _gamepads.begin(); it != _gamepads.end(); it++) - if (*it == gamepad) - { - _gamepads.erase(it); - break; - } - _gamepads_mutex.unlock(); - } + static void Unregister(std::shared_ptr gamepad); static int GetGamepadCount(); static std::shared_ptr GetGamepad(int index); + static void SaveMaplePorts(); protected: GamepadDevice(int maple_port, const char *api_name, bool remappable = true) @@ -83,6 +69,7 @@ protected: virtual void load_axis_min_max(u32 axis) {} std::string _name; + std::string _unique_id = ""; InputMapping *input_mapper; std::map axis_min_values; std::map axis_ranges; diff --git a/core/linux-dist/evdev_gamepad.h b/core/linux-dist/evdev_gamepad.h index f3c5c4bd4..2d06ce4fe 100644 --- a/core/linux-dist/evdev_gamepad.h +++ b/core/linux-dist/evdev_gamepad.h @@ -9,12 +9,18 @@ public: : GamepadDevice(maple_port, "evdev"), _fd(fd), _rumble_effect_id(-1), _devnode(devnode) { fcntl(fd, F_SETFL, O_NONBLOCK); - char name[256] = "Unknown"; - if (ioctl(fd, EVIOCGNAME(sizeof(name)), name) < 0) + char buf[256] = "Unknown"; + if (ioctl(fd, EVIOCGNAME(sizeof(buf) - 1), buf) < 0) perror("evdev: ioctl(EVIOCGNAME)"); else - printf("evdev: Opened device '%s' ", name); - _name = name; + printf("evdev: Opened device '%s' ", buf); + _name = buf; + buf[0] = 0; + if (ioctl(fd, EVIOCGUNIQ(sizeof(buf) - 1), buf) == 0) + _unique_id = buf; + if (_unique_id.empty()) + _unique_id = devnode; + if (!find_mapping(mapping_file)) { #if defined(TARGET_PANDORA) @@ -22,18 +28,18 @@ public: #elif defined(TARGET_GCW0) mapping_file = "controller_gcwz.cfg"; #else - if (!strcmp(name, "Microsoft X-Box 360 pad") - || !strcmp(name, "Xbox 360 Wireless Receiver") - || !strcmp(name, "Xbox 360 Wireless Receiver (XBOX)")) + if (_name == "Microsoft X-Box 360 pad" + || _name == "Xbox 360 Wireless Receiver" + || _name == "Xbox 360 Wireless Receiver (XBOX)") { mapping_file = "controller_xpad.cfg"; } - else if (strstr(name, "Xbox Gamepad (userspace driver)") != NULL) + else if (_name.find("Xbox Gamepad (userspace driver)") != std::string::npos) { mapping_file = "controller_xboxdrv.cfg"; } - else if (strstr(name, "keyboard") != NULL || - strstr(name, "Keyboard") != NULL) + else if (_name.find("keyboard") != std::string::npos + || _name.find("Keyboard") != std::string::npos) { mapping_file = "keyboard.cfg"; } diff --git a/core/linux-dist/x11.cpp b/core/linux-dist/x11.cpp index da977e6b3..85c6fd5f1 100644 --- a/core/linux-dist/x11.cpp +++ b/core/linux-dist/x11.cpp @@ -52,6 +52,7 @@ public: X11MouseGamepadDevice(int maple_port) : GamepadDevice(maple_port, "X11") { _name = "Mouse"; + _unique_id = "x11_mouse"; if (!find_mapping()) input_mapper = new MouseInputMapping(); } diff --git a/core/linux-dist/x11_keyboard.h b/core/linux-dist/x11_keyboard.h index e78717e32..70e3edd69 100644 --- a/core/linux-dist/x11_keyboard.h +++ b/core/linux-dist/x11_keyboard.h @@ -181,6 +181,7 @@ public: X11KbGamepadDevice(int maple_port) : GamepadDevice(maple_port, "X11") { _name = "Keyboard"; + _unique_id = "x11_keyboard"; if (!find_mapping()) input_mapper = new KbInputMapping(); } diff --git a/core/nullDC.cpp b/core/nullDC.cpp index 868e6f1b9..00309f0dc 100755 --- a/core/nullDC.cpp +++ b/core/nullDC.cpp @@ -22,6 +22,7 @@ #include "imgread/common.h" #include "rend/gui.h" #include "profiler/profiler.h" +#include "input/gamepad_device.h" void FlushCache(); void LoadCustom(); @@ -741,6 +742,9 @@ void SaveSettings() paths += path; } cfgSaveStr("config", "Dreamcast.ContentPath", paths.c_str()); + + GamepadDevice::SaveMaplePorts(); + #ifdef _ANDROID void SaveAndroidSettings(); SaveAndroidSettings(); diff --git a/core/sdl/sdl_gamepad.h b/core/sdl/sdl_gamepad.h index 4d970a700..9f74f578c 100644 --- a/core/sdl/sdl_gamepad.h +++ b/core/sdl/sdl_gamepad.h @@ -50,6 +50,13 @@ public: _name = SDL_JoystickName(sdl_joystick); sdl_joystick_instance = SDL_JoystickInstanceID(sdl_joystick); printf("SDL: Opened joystick on port %d: '%s' ", maple_port, _name.c_str()); + SDL_JoystickGUID guid = SDL_JoystickGetGUID(sdl_joystick); + char buf[33]; + SDL_JoystickGetGUIDString(guid, buf, sizeof(buf)); + _unique_id = buf; + if (_unique_id.empty()) + _unique_id = _name; + if (!find_mapping()) { if (_name == "Microsoft X-Box 360 pad") @@ -175,6 +182,7 @@ public: SDLKbGamepadDevice(int maple_port) : GamepadDevice(maple_port, "SDL") { _name = "Keyboard"; + _unique_id = "sdl_keyboard; if (!find_mapping()) input_mapper = new KbInputMapping(); } @@ -201,6 +209,7 @@ public: SDLMouseGamepadDevice(int maple_port) : GamepadDevice(maple_port, "SDL") { _name = "Mouse"; + _unique_id = "sdl_mouse"; if (!find_mapping()) input_mapper = new MouseInputMapping(); } diff --git a/core/windows/xinput_gamepad.h b/core/windows/xinput_gamepad.h index 5aa9f70e7..26b3e39e7 100644 --- a/core/windows/xinput_gamepad.h +++ b/core/windows/xinput_gamepad.h @@ -36,6 +36,9 @@ public: XInputGamepadDevice(int maple_port, int xinput_port) : GamepadDevice(maple_port, "xinput"), _xinput_port(xinput_port) { + char buf[32]; + sprintf(buf, "xinput-%d", xinput_port + 1); + _unique_id = buf; } void ReadInput() @@ -231,6 +234,7 @@ public: WinKbGamepadDevice(int maple_port) : GamepadDevice(maple_port, "win32") { _name = "Keyboard"; + _unique_id = "win_keyboard; if (!find_mapping()) input_mapper = new KbInputMapping(); } @@ -257,6 +261,7 @@ public: WinMouseGamepadDevice(int maple_port) : GamepadDevice(maple_port, "win32") { _name = "Mouse"; + _unique_id = "win_mouse; if (!find_mapping()) input_mapper = new MouseInputMapping(); } diff --git a/shell/android-studio/reicast/src/main/java/com/reicast/emulator/periph/InputDeviceManager.java b/shell/android-studio/reicast/src/main/java/com/reicast/emulator/periph/InputDeviceManager.java index e8ff499d0..cfe754612 100644 --- a/shell/android-studio/reicast/src/main/java/com/reicast/emulator/periph/InputDeviceManager.java +++ b/shell/android-studio/reicast/src/main/java/com/reicast/emulator/periph/InputDeviceManager.java @@ -24,7 +24,7 @@ public final class InputDeviceManager implements InputManager.InputDeviceListene { maple_port = 0; if (applicationContext.getPackageManager().hasSystemFeature("android.hardware.touchscreen")) - joystickAdded(VIRTUAL_GAMEPAD_ID, "Virtual Gamepad", maple_port == 3 ? 3 : maple_port++); + joystickAdded(VIRTUAL_GAMEPAD_ID, "Virtual Gamepad", maple_port == 3 ? 3 : maple_port++, "virtual_gamepad_uid"); int[] ids = InputDevice.getDeviceIds(); for (int id : ids) onInputDeviceAdded(id); @@ -49,7 +49,7 @@ public final class InputDeviceManager implements InputManager.InputDeviceListene if ((device.getSources() & InputDevice.SOURCE_CLASS_JOYSTICK) == InputDevice.SOURCE_CLASS_JOYSTICK) { port = this.maple_port == 3 ? 3 : this.maple_port++; } - joystickAdded(i, device.getName(), port); + joystickAdded(i, device.getName(), port, device.getDescriptor()); } } @@ -96,6 +96,6 @@ public final class InputDeviceManager implements InputManager.InputDeviceListene public native boolean joystickButtonEvent(int id, int button, boolean pressed); public native boolean joystickAxisEvent(int id, int button, int value); public native void mouseEvent(int xpos, int ypos, int buttons); - private native void joystickAdded(int id, String name, int maple_port); + private native void joystickAdded(int id, String name, int maple_port, String uniqueId); private native void joystickRemoved(int id); } diff --git a/shell/android-studio/reicast/src/main/jni/src/Android.cpp b/shell/android-studio/reicast/src/main/jni/src/Android.cpp index 3acc179e9..917586e8c 100644 --- a/shell/android-studio/reicast/src/main/jni/src/Android.cpp +++ b/shell/android-studio/reicast/src/main/jni/src/Android.cpp @@ -114,7 +114,7 @@ JNIEXPORT jboolean JNICALL Java_com_reicast_emulator_emu_JNIdc_guiIsOpen(JNIEnv JNIEXPORT jboolean JNICALL Java_com_reicast_emulator_emu_JNIdc_guiIsContentBrowser(JNIEnv *env,jobject obj) __attribute__((visibility("default"))); JNIEXPORT void JNICALL Java_com_reicast_emulator_periph_InputDeviceManager_init(JNIEnv *env, jobject obj) __attribute__((visibility("default"))); -JNIEXPORT void JNICALL Java_com_reicast_emulator_periph_InputDeviceManager_joystickAdded(JNIEnv *env, jobject obj, jint id, jstring name, jint maple_port) __attribute__((visibility("default"))); +JNIEXPORT void JNICALL Java_com_reicast_emulator_periph_InputDeviceManager_joystickAdded(JNIEnv *env, jobject obj, jint id, jstring name, jint maple_port, jstring junique_id) __attribute__((visibility("default"))); JNIEXPORT void JNICALL Java_com_reicast_emulator_periph_InputDeviceManager_joystickRemoved(JNIEnv *env, jobject obj, jint id) __attribute__((visibility("default"))); JNIEXPORT void JNICALL Java_com_reicast_emulator_periph_InputDeviceManager_virtualGamepadEvent(JNIEnv *env, jobject obj, jint kcode, jint joyx, jint joyy, jint lt, jint rt) __attribute__((visibility("default"))); JNIEXPORT jboolean JNICALL Java_com_reicast_emulator_periph_InputDeviceManager_joystickButtonEvent(JNIEnv *env, jobject obj, jint id, jint key, jboolean pressed) __attribute__((visibility("default"))); @@ -622,13 +622,14 @@ JNIEXPORT void JNICALL Java_com_reicast_emulator_periph_InputDeviceManager_init( input_device_manager_rumble = env->GetMethodID(env->GetObjectClass(obj), "rumble", "(IFFI)Z"); } -JNIEXPORT void JNICALL Java_com_reicast_emulator_periph_InputDeviceManager_joystickAdded(JNIEnv *env, jobject obj, jint id, jstring name, jint maple_port) +JNIEXPORT void JNICALL Java_com_reicast_emulator_periph_InputDeviceManager_joystickAdded(JNIEnv *env, jobject obj, jint id, jstring name, jint maple_port, jstring junique_id) { const char* joyname = env->GetStringUTFChars(name,0); - std::shared_ptr gamepad = std::make_shared(maple_port, id, joyname); + const char* unique_id = env->GetStringUTFChars(junique_id, 0); + std::shared_ptr gamepad = std::make_shared(maple_port, id, joyname, unique_id); AndroidGamepadDevice::AddAndroidGamepad(gamepad); env->ReleaseStringUTFChars(name, joyname); - + env->ReleaseStringUTFChars(name, unique_id); } JNIEXPORT void JNICALL Java_com_reicast_emulator_periph_InputDeviceManager_joystickRemoved(JNIEnv *env, jobject obj, jint id) { diff --git a/shell/android-studio/reicast/src/main/jni/src/android_gamepad.h b/shell/android-studio/reicast/src/main/jni/src/android_gamepad.h index 28cb874b4..c1fe2b465 100644 --- a/shell/android-studio/reicast/src/main/jni/src/android_gamepad.h +++ b/shell/android-studio/reicast/src/main/jni/src/android_gamepad.h @@ -87,10 +87,12 @@ public: class AndroidGamepadDevice : public GamepadDevice { public: - AndroidGamepadDevice(int maple_port, int id, const char *name) : GamepadDevice(maple_port, "Android", id != VIRTUAL_GAMEPAD_ID), android_id(id) + AndroidGamepadDevice(int maple_port, int id, const char *name, const char *unique_id) + : GamepadDevice(maple_port, "Android", id != VIRTUAL_GAMEPAD_ID), android_id(id) { _name = name; - printf("Android: Opened joystick %d on port %d: '%s' ", id, maple_port, _name.c_str()); + _unique_id = unique_id; + printf("Android: Opened joystick %d on port %d: '%s' descriptor '%s'", id, maple_port, _name.c_str(), _unique_id.c_str()); if (id == VIRTUAL_GAMEPAD_ID) { input_mapper = new IdentityInputMapping(); @@ -213,6 +215,7 @@ public: AndroidMouseGamepadDevice(int maple_port) : GamepadDevice(maple_port, "Android") { _name = "Mouse"; + _unique_id = "android_mouse"; if (!find_mapping()) input_mapper = new MouseInputMapping(); } diff --git a/shell/apple/emulator-osx/emulator-osx/osx_gamepad.h b/shell/apple/emulator-osx/emulator-osx/osx_gamepad.h index da3c92773..6b7e0d461 100644 --- a/shell/apple/emulator-osx/emulator-osx/osx_gamepad.h +++ b/shell/apple/emulator-osx/emulator-osx/osx_gamepad.h @@ -36,6 +36,7 @@ public: OSXKbGamepadDevice(int maple_port) : GamepadDevice(maple_port, "OSX") { _name = "Keyboard"; + _unique_id = "osx_keyboard"; if (!find_mapping()) input_mapper = new KbInputMapping(); } @@ -61,6 +62,7 @@ public: OSXMouseGamepadDevice(int maple_port) : GamepadDevice(maple_port, "OSX") { _name = "Mouse"; + _unique_id = "osx_mouse"; if (!find_mapping()) input_mapper = new MouseInputMapping(); } From 80626b1f4ecd7437e8ae7a12ac735808bf4b2b8f Mon Sep 17 00:00:00 2001 From: Flyinghead Date: Fri, 29 Mar 2019 17:34:16 +0100 Subject: [PATCH 199/319] win: compile fix --- core/windows/xinput_gamepad.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/core/windows/xinput_gamepad.h b/core/windows/xinput_gamepad.h index 26b3e39e7..894d0aa79 100644 --- a/core/windows/xinput_gamepad.h +++ b/core/windows/xinput_gamepad.h @@ -234,7 +234,7 @@ public: WinKbGamepadDevice(int maple_port) : GamepadDevice(maple_port, "win32") { _name = "Keyboard"; - _unique_id = "win_keyboard; + _unique_id = "win_keyboard"; if (!find_mapping()) input_mapper = new KbInputMapping(); } @@ -261,7 +261,7 @@ public: WinMouseGamepadDevice(int maple_port) : GamepadDevice(maple_port, "win32") { _name = "Mouse"; - _unique_id = "win_mouse; + _unique_id = "win_mouse"; if (!find_mapping()) input_mapper = new MouseInputMapping(); } From 8fb1d807515ef4c46c96fa4d8ce833aa322b2e55 Mon Sep 17 00:00:00 2001 From: Flyinghead Date: Fri, 29 Mar 2019 17:35:00 +0100 Subject: [PATCH 200/319] gui: display VMUs on pause --- core/linux-dist/main.cpp | 1 - core/rend/gles/imgui_impl_opengl3.cpp | 17 ++++ core/rend/gles/imgui_impl_opengl3.h | 2 + core/rend/gui.cpp | 98 +++++++++++++++++++ core/rend/gui_util.cpp | 6 +- .../reicast/src/main/jni/src/Android.cpp | 35 ------- 6 files changed, 120 insertions(+), 39 deletions(-) diff --git a/core/linux-dist/main.cpp b/core/linux-dist/main.cpp index cee37671d..044fa9aa6 100644 --- a/core/linux-dist/main.cpp +++ b/core/linux-dist/main.cpp @@ -408,7 +408,6 @@ int main(int argc, wchar* argv[]) #endif int get_mic_data(u8* buffer) { return 0; } -int push_vmu_screen(u8* buffer) { return 0; } void os_DebugBreak() { diff --git a/core/rend/gles/imgui_impl_opengl3.cpp b/core/rend/gles/imgui_impl_opengl3.cpp index 3fe069942..fce30f9fa 100644 --- a/core/rend/gles/imgui_impl_opengl3.cpp +++ b/core/rend/gles/imgui_impl_opengl3.cpp @@ -585,3 +585,20 @@ void ImGui_ImplOpenGL3_DrawBackground() glClear(GL_COLOR_BUFFER_BIT); } } + +ImTextureID ImGui_ImplOpenGL3_CreateVmuTexture(const unsigned int *data) +{ + GLuint tex_id; + glGenTextures(1, &tex_id); + glBindTexture(GL_TEXTURE_2D, tex_id); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 48, 32, 0, GL_RGBA, GL_UNSIGNED_BYTE, data); + + return reinterpret_cast(tex_id); +} + +void ImGui_ImplOpenGL3_DeleteVmuTexture(ImTextureID tex_id) +{ + glDeleteTextures(1, &(GLuint &)tex_id); +} diff --git a/core/rend/gles/imgui_impl_opengl3.h b/core/rend/gles/imgui_impl_opengl3.h index f736563bb..a5146a6e2 100644 --- a/core/rend/gles/imgui_impl_opengl3.h +++ b/core/rend/gles/imgui_impl_opengl3.h @@ -34,6 +34,8 @@ IMGUI_IMPL_API void ImGui_ImplOpenGL3_Shutdown(); IMGUI_IMPL_API void ImGui_ImplOpenGL3_NewFrame(); IMGUI_IMPL_API void ImGui_ImplOpenGL3_RenderDrawData(ImDrawData* draw_data, bool save_background = false); IMGUI_IMPL_API void ImGui_ImplOpenGL3_DrawBackground(); +IMGUI_IMPL_API ImTextureID ImGui_ImplOpenGL3_CreateVmuTexture(const unsigned int *); +IMGUI_IMPL_API void ImGui_ImplOpenGL3_DeleteVmuTexture(ImTextureID); // Called by Init/NewFrame/Shutdown IMGUI_IMPL_API bool ImGui_ImplOpenGL3_CreateFontsTexture(); diff --git a/core/rend/gui.cpp b/core/rend/gui.cpp index f16414182..e2e9ef357 100644 --- a/core/rend/gui.cpp +++ b/core/rend/gui.cpp @@ -64,6 +64,10 @@ GuiState gui_state = Main; static bool settings_opening; static bool touch_up; +static void display_vmus(); +static void reset_vmus(); +static void term_vmus(); + void gui_init() { if (inited) @@ -290,6 +294,8 @@ static void gui_display_commands() if (!settings_opening) ImGui_ImplOpenGL3_DrawBackground(); + display_vmus(); + ImGui::SetNextWindowPos(ImVec2(screen_width / 2.f, screen_height / 2.f), ImGuiCond_Always, ImVec2(0.5f, 0.5f)); ImGui::SetNextWindowSize(ImVec2(330 * scaling, 0)); @@ -635,6 +641,7 @@ static void gui_display_settings() gui_state = Main; #if DC_PLATFORM == DC_PLATFORM_DREAMCAST maple_ReconnectDevices(); + reset_vmus(); #endif SaveSettings(); } @@ -1470,6 +1477,7 @@ void gui_open_onboarding() void gui_term() { inited = false; + term_vmus(); ImGui_ImplOpenGL3_Shutdown(); ImGui::DestroyContext(); } @@ -1495,3 +1503,93 @@ void gui_refresh_files() game_list_done = false; subfolders_read = false; } + +#define VMU_WIDTH (70 * 48 * scaling / 32) +#define VMU_HEIGHT (70 * scaling) +#define VMU_PADDING (8 * scaling) +static u32 vmu_lcd_data[8][48 * 32]; +static bool vmu_lcd_status[8]; +static ImTextureID vmu_lcd_tex_ids[8]; + +int push_vmu_screen(int vmu_id, u8* buffer) +{ + u32 *p = &vmu_lcd_data[vmu_id][0]; + for (int i = 0; i < ARRAY_SIZE(vmu_lcd_data[vmu_id]); i++, buffer++) + *p++ = *buffer != 0 ? 0xFFFFFFFFu : 0xFF000000u; + vmu_lcd_status[vmu_id] = true; + + return 0; +} + +static const int vmu_coords[8][2] = { + { 0 , 0 }, + { 0 , 0 }, + { 1 , 0 }, + { 1 , 0 }, + { 0 , 1 }, + { 0 , 1 }, + { 1 , 1 }, + { 1 , 1 }, +}; + +static void display_vmus() +{ + if (!game_started) + return; + ImGui::SetNextWindowBgAlpha(0); + ImGui::SetNextWindowPos(ImVec2(0, 0)); + ImGui::SetNextWindowSize(ImVec2(screen_width, screen_height)); + + ImGui::Begin("vmu-window", NULL, ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_NoNav | ImGuiWindowFlags_NoInputs + | ImGuiWindowFlags_NoBackground | ImGuiWindowFlags_NoFocusOnAppearing); + for (int i = 0; i < 8; i++) + { + if (!vmu_lcd_status[i]) + continue; + + if (vmu_lcd_tex_ids[i] != (ImTextureID)0) + ImGui_ImplOpenGL3_DeleteVmuTexture(vmu_lcd_tex_ids[i]); + vmu_lcd_tex_ids[i] = ImGui_ImplOpenGL3_CreateVmuTexture(vmu_lcd_data[i]); + + int x = vmu_coords[i][0]; + int y = vmu_coords[i][1]; + ImVec2 pos; + if (x == 0) + pos.x = VMU_PADDING; + else + pos.x = screen_width - VMU_WIDTH - VMU_PADDING; + if (y == 0) + { + pos.y = VMU_PADDING; + if (i & 1) + pos.y += VMU_HEIGHT + VMU_PADDING; + } + else + { + pos.y = screen_height - VMU_HEIGHT - VMU_PADDING; + if (i & 1) + pos.y -= VMU_HEIGHT + VMU_PADDING; + } + ImVec2 pos_b(pos.x + VMU_WIDTH, pos.y + VMU_HEIGHT); + ImGui::GetWindowDrawList()->AddImage(vmu_lcd_tex_ids[i], pos, pos_b, ImVec2(0, 1), ImVec2(1, 0), 0xC0ffffff); + } + ImGui::End(); +} + +static void reset_vmus() +{ + for (int i = 0; i < ARRAY_SIZE(vmu_lcd_status); i++) + vmu_lcd_status[i] = false; +} + +static void term_vmus() +{ + for (int i = 0; i < ARRAY_SIZE(vmu_lcd_status); i++) + { + if (vmu_lcd_tex_ids[i] != (ImTextureID)0) + { + ImGui_ImplOpenGL3_DeleteVmuTexture(vmu_lcd_tex_ids[i]); + vmu_lcd_tex_ids[i] = (ImTextureID)0; + } + } +} diff --git a/core/rend/gui_util.cpp b/core/rend/gui_util.cpp index d1c6ede53..f9e9ce91b 100644 --- a/core/rend/gui_util.cpp +++ b/core/rend/gui_util.cpp @@ -183,7 +183,7 @@ void select_directory_popup(const char *prompt, float scaling, StringCallback ca } ImGui::Text("%s", error_message.empty() ? select_current_directory.c_str() : error_message.c_str()); - ImGui::BeginChild(ImGui::GetID("dir_list"), ImVec2(0, -ImGui::CalcTextSize("Cancel").y - ImGui::GetStyle().FramePadding. y * 2 - ImGui::GetStyle().ItemSpacing.y), true); + ImGui::BeginChild(ImGui::GetID("dir_list"), ImVec2(0, - 30 * scaling - ImGui::GetStyle().ItemSpacing.y), true); ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(8 * scaling, 20 * scaling)); // from 8, 4 @@ -245,14 +245,14 @@ void select_directory_popup(const char *prompt, float scaling, StringCallback ca } ImGui::PopStyleVar(); ImGui::EndChild(); - if (ImGui::Button("Select Current Directory")) + if (ImGui::Button("Select Current Directory", ImVec2(0, 30 * scaling))) { subfolders_read = false; callback(false, select_current_directory); ImGui::CloseCurrentPopup(); } ImGui::SameLine(); - if (ImGui::Button("Cancel")) + if (ImGui::Button("Cancel", ImVec2(0, 30 * scaling))) { subfolders_read = false; callback(true, ""); diff --git a/shell/android-studio/reicast/src/main/jni/src/Android.cpp b/shell/android-studio/reicast/src/main/jni/src/Android.cpp index 917586e8c..3ab96e5dd 100644 --- a/shell/android-studio/reicast/src/main/jni/src/Android.cpp +++ b/shell/android-studio/reicast/src/main/jni/src/Android.cpp @@ -315,10 +315,6 @@ JNIEXPORT void JNICALL Java_com_reicast_emulator_emu_JNIdc_diskSwap(JNIEnv *env, //stuff for microphone jobject sipemu; jmethodID getmicdata; -//stuff for vmu lcd -jobject vmulcd = NULL; -jbyteArray jpix = NULL; -jmethodID updatevmuscreen; extern bool game_started; //stuff for audio @@ -333,12 +329,6 @@ JNIEXPORT void JNICALL Java_com_reicast_emulator_emu_JNIdc_setupMic(JNIEnv *env, getmicdata = env->GetMethodID(env->GetObjectClass(sipemu),"getData","()[B"); } -JNIEXPORT void JNICALL Java_com_reicast_emulator_emu_JNIdc_setupVmu(JNIEnv *env,jobject obj,jobject vmu) -{ - vmulcd = env->NewGlobalRef(vmu); - updatevmuscreen = env->GetMethodID(env->GetObjectClass(vmu),"updateBytes","([B)V"); -} - JNIEXPORT void JNICALL Java_com_reicast_emulator_emu_JNIdc_pause(JNIEnv *env,jobject obj) { if (game_started) @@ -362,18 +352,6 @@ JNIEXPORT void JNICALL Java_com_reicast_emulator_emu_JNIdc_destroy(JNIEnv *env,j dc_term(); } -JNIEXPORT void JNICALL Java_com_reicast_emulator_emu_JNIdc_vmuSwap(JNIEnv *env,jobject obj) -{ - maple_device* olda = MapleDevices[0][0]; - maple_device* oldb = MapleDevices[0][1]; - MapleDevices[0][0] = NULL; - MapleDevices[0][1] = NULL; - usleep(50000);//50 ms, wait for host to detect disconnect - - MapleDevices[0][0] = oldb; - MapleDevices[0][1] = olda; -} - JNIEXPORT jint JNICALL Java_com_reicast_emulator_emu_JNIdc_send(JNIEnv *env,jobject obj,jint cmd, jint param) { if (cmd==0) @@ -584,19 +562,6 @@ int get_mic_data(u8* buffer) return 1; } -int push_vmu_screen(u8* buffer) -{ - if(vmulcd==NULL){ - return 0; - } - if(jpix==NULL){ - jpix = jvm_attacher.getEnv()->NewByteArray(1536); - } - jvm_attacher.getEnv()->SetByteArrayRegion(jpix, 0, 1536, (jbyte*)buffer); - jvm_attacher.getEnv()->CallVoidMethod(vmulcd, updatevmuscreen, jpix); - return 1; -} - void os_DebugBreak() { // TODO: notify the parent thread about it ... From 09ea5eba78ee9ecba6a6eb978b2a19f6b2d10625 Mon Sep 17 00:00:00 2001 From: Flyinghead Date: Fri, 29 Mar 2019 17:47:17 +0100 Subject: [PATCH 201/319] missing changes for previous commit --- core/hw/maple/maple_devs.cpp | 13 ++++++------- core/hw/maple/maple_devs.h | 2 +- 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/core/hw/maple/maple_devs.cpp b/core/hw/maple/maple_devs.cpp index 3efc18fe8..90cc5a089 100755 --- a/core/hw/maple/maple_devs.cpp +++ b/core/hw/maple/maple_devs.cpp @@ -644,9 +644,8 @@ struct maple_sega_vmu: maple_base } } config->SetImage(lcd_data_decoded); -#if !defined(TARGET_PANDORA) && HOST_OS != OS_DARWIN - push_vmu_screen(lcd_data_decoded); -#endif + if (bus_id <= 3 && bus_port <= 1) + push_vmu_screen(bus_id * 2 + bus_port, lcd_data_decoded); #if 0 // Update LCD window if (!dev->lcd.visible) @@ -791,7 +790,7 @@ struct maple_microphone: maple_base switch (cmd) { case MDC_DeviceRequest: - LOGI("maple_microphone::dma MDC_DeviceRequest"); + LOGI("maple_microphone::dma MDC_DeviceRequest\n"); //this was copied from the controller case with just the id and name replaced! //caps @@ -826,7 +825,7 @@ struct maple_microphone: maple_base case MDCF_GetCondition: { - LOGI("maple_microphone::dma MDCF_GetCondition"); + LOGI("maple_microphone::dma MDCF_GetCondition\n"); //this was copied from the controller case with just the id replaced! //PlainJoystickState pjs; @@ -863,7 +862,7 @@ struct maple_microphone: maple_base case MDC_DeviceReset: //uhhh do nothing? - LOGI("maple_microphone::dma MDC_DeviceReset"); + LOGI("maple_microphone::dma MDC_DeviceReset\n"); return MDRS_DeviceReply; case MDCF_MICControl: @@ -932,7 +931,7 @@ struct maple_microphone: maple_base LOGI("maple_microphone::dma MDCF_MICControl set gain %#010x\n",secondword); return MDRS_DeviceReply; case MDRE_TransmitAgain: - LOGW("maple_microphone::dma MDCF_MICControl MDRE_TransminAgain"); + LOGW("maple_microphone::dma MDCF_MICControl MDRE_TransmitAgain\n"); //apparently this doesnt matter //wptr(micdata, SIZE_OF_MIC_DATA); return MDRS_DeviceReply;//MDRS_DataTransfer; diff --git a/core/hw/maple/maple_devs.h b/core/hw/maple/maple_devs.h index 8ad5db972..34cce8e56 100755 --- a/core/hw/maple/maple_devs.h +++ b/core/hw/maple/maple_devs.h @@ -101,6 +101,6 @@ maple_device* maple_Create(MapleDeviceType type); #define SIZE_OF_MIC_DATA 480 //ALSO DEFINED IN SipEmulator.java #ifndef TARGET_PANDORA int get_mic_data(u8* buffer); //implemented in Android.cpp -int push_vmu_screen(u8* buffer); //implemented in Android.cpp #endif +int push_vmu_screen(int vmu_id, u8* buffer); // vmu_id: 0-7 #define MAPLE_PORTS 4 From 6438a402dfb3ef1759a532fd05de0e86b1e55e7e Mon Sep 17 00:00:00 2001 From: Flyinghead Date: Fri, 29 Mar 2019 19:23:37 +0100 Subject: [PATCH 202/319] dynarec: add option to control smc code checks: none, fast, full --- core/hw/sh4/dyna/driver.cpp | 6 +++ core/nullDC.cpp | 4 ++ core/rec-ARM/rec_arm.cpp | 65 +++++++++++++++---------- core/rec-ARM64/rec_arm64.cpp | 85 +++++++++++++++++++-------------- core/rec-x64/rec_x64.cpp | 63 ++++++++++++++---------- core/rec-x86/rec_x86_driver.cpp | 37 +++++++++----- core/rend/gui.cpp | 25 +++++++++- core/types.h | 6 +++ 8 files changed, 193 insertions(+), 98 deletions(-) diff --git a/core/hw/sh4/dyna/driver.cpp b/core/hw/sh4/dyna/driver.cpp index d398a6f70..dcb74ec4c 100644 --- a/core/hw/sh4/dyna/driver.cpp +++ b/core/hw/sh4/dyna/driver.cpp @@ -86,6 +86,12 @@ void recSh4_Run() sh4_dyna_rcb=(u8*)&Sh4cntx + sizeof(Sh4cntx); printf("cntx // fpcb offset: %td // pc offset: %td // pc %08X\n",(u8*)&sh4rcb.fpcb - sh4_dyna_rcb, (u8*)&sh4rcb.cntx.pc - sh4_dyna_rcb,sh4rcb.cntx.pc); + if (!settings.dynarec.safemode) + printf("Warning: Dynarec safe mode is off\n"); + if (settings.dynarec.unstable_opt) + printf("Warning: Unstable optimizations is on\n"); + if (settings.dynarec.SmcCheckLevel != FullCheck) + printf("Warning: SMC check mode is %d\n", settings.dynarec.SmcCheckLevel); verify(rcb_noffs(&next_pc)==-184); ngen_mainloop(sh4_dyna_rcb); diff --git a/core/nullDC.cpp b/core/nullDC.cpp index 868e6f1b9..1be9a7b76 100755 --- a/core/nullDC.cpp +++ b/core/nullDC.cpp @@ -494,6 +494,7 @@ void InitSettings() settings.dreamcast.broadcast = 4; // default settings.dreamcast.language = 6; // default settings.dreamcast.FullMMU = false; + settings.dynarec.SmcCheckLevel = NoCheck; settings.aica.LimitFPS = true; settings.aica.NoBatch = false; // This also controls the DSP. Disabled by default settings.aica.NoSound = false; @@ -562,6 +563,7 @@ void LoadSettings(bool game_specific) settings.dynarec.idleskip = cfgLoadBool(config_section, "Dynarec.idleskip", settings.dynarec.idleskip); settings.dynarec.unstable_opt = cfgLoadBool(config_section, "Dynarec.unstable-opt", settings.dynarec.unstable_opt); settings.dynarec.safemode = cfgLoadBool(config_section, "Dynarec.safe-mode", settings.dynarec.safemode); + settings.dynarec.SmcCheckLevel = (SmcCheckEnum)cfgLoadInt(config_section, "Dynarec.SmcCheckLevel", settings.dynarec.SmcCheckLevel); //disable_nvmem can't be loaded, because nvmem init is before cfg load settings.dreamcast.cable = cfgLoadInt(config_section, "Dreamcast.Cable", settings.dreamcast.cable); settings.dreamcast.region = cfgLoadInt(config_section, "Dreamcast.Region", settings.dreamcast.region); @@ -697,6 +699,8 @@ void SaveSettings() cfgSaveBool("config", "Dynarec.unstable-opt", settings.dynarec.unstable_opt); if (!safemode_game || !settings.dynarec.safemode) cfgSaveBool("config", "Dynarec.safe-mode", settings.dynarec.safemode); + cfgSaveInt("config", "Dynarec.SmcCheckLevel", (int)settings.dynarec.SmcCheckLevel); + cfgSaveInt("config", "Dreamcast.Language", settings.dreamcast.language); cfgSaveBool("config", "aica.LimitFPS", settings.aica.LimitFPS); cfgSaveBool("config", "aica.NoBatch", settings.aica.NoBatch); diff --git a/core/rec-ARM/rec_arm.cpp b/core/rec-ARM/rec_arm.cpp index 9e61af8ac..9fdf8ea58 100644 --- a/core/rec-ARM/rec_arm.cpp +++ b/core/rec-ARM/rec_arm.cpp @@ -2114,37 +2114,50 @@ void ngen_Compile(RuntimeBlockInfo* block,bool force_checks, bool reset, bool st reg.OpBegin(&block->oplist[0],0); //scheduler - if (force_checks) + if (force_checks && settings.dynarec.SmcCheckLevel != NoCheck) { - s32 sz = block->sh4_code_size; - u32 addr = block->addr; - MOV32(r0,addr); - - while (sz > 0) + if (settings.dynarec.SmcCheckLevel == FastCheck) { - if (sz > 2) - { - u32* ptr=(u32*)GetMemPtr(addr,4); - MOV32(r2,(u32)ptr); - LDR(r2,r2,0); - MOV32(r1,*ptr); - CMP(r1,r2); + MOV32(r0,block->addr); + u32* ptr=(u32*)GetMemPtr(block->addr,4); + MOV32(r2,(u32)ptr); + LDR(r2,r2,0); + MOV32(r1,*ptr); + CMP(r1,r2); + JUMP((u32)ngen_blockcheckfail, CC_NE); + } + else // FullCheck + { + s32 sz = block->sh4_code_size; + u32 addr = block->addr; + MOV32(r0,addr); - JUMP((u32)ngen_blockcheckfail, CC_NE); - addr += 4; - sz -= 4; - } - else + while (sz > 0) { - u16* ptr = (u16 *)GetMemPtr(addr, 2); - MOV32(r2, (u32)ptr); - LDRH(r2, r2, 0, AL); - MOVW(r1, *ptr, AL); - CMP(r1, r2); + if (sz > 2) + { + u32* ptr=(u32*)GetMemPtr(addr,4); + MOV32(r2,(u32)ptr); + LDR(r2,r2,0); + MOV32(r1,*ptr); + CMP(r1,r2); - JUMP((u32)ngen_blockcheckfail, CC_NE); - addr += 2; - sz -= 2; + JUMP((u32)ngen_blockcheckfail, CC_NE); + addr += 4; + sz -= 4; + } + else + { + u16* ptr = (u16 *)GetMemPtr(addr, 2); + MOV32(r2, (u32)ptr); + LDRH(r2, r2, 0, AL); + MOVW(r1, *ptr, AL); + CMP(r1, r2); + + JUMP((u32)ngen_blockcheckfail, CC_NE); + addr += 2; + sz -= 2; + } } } } diff --git a/core/rec-ARM64/rec_arm64.cpp b/core/rec-ARM64/rec_arm64.cpp index cc35f57b6..a4b7803fb 100644 --- a/core/rec-ARM64/rec_arm64.cpp +++ b/core/rec-ARM64/rec_arm64.cpp @@ -1294,48 +1294,63 @@ private: void CheckBlock(RuntimeBlockInfo* block) { - s32 sz = block->sh4_code_size; + if (settings.dynarec.SmcCheckLevel == NoCheck) + return; Label blockcheck_fail; Label blockcheck_success; - u8* ptr = GetMemPtr(block->addr, sz); - if (ptr == NULL) - // FIXME Can a block cross a RAM / non-RAM boundary?? - return; - - Ldr(x9, reinterpret_cast(ptr)); - - while (sz > 0) + if (settings.dynarec.SmcCheckLevel == FastCheck) { - if (sz >= 8) - { - Ldr(x10, MemOperand(x9, 8, PostIndex)); - Ldr(x11, *(u64*)ptr); - Cmp(x10, x11); - sz -= 8; - ptr += 8; - } - else if (sz >= 4) - { - Ldr(w10, MemOperand(x9, 4, PostIndex)); - Ldr(w11, *(u32*)ptr); - Cmp(w10, w11); - sz -= 4; - ptr += 4; - } - else - { - Ldrh(w10, MemOperand(x9, 2, PostIndex)); - Mov(w11, *(u16*)ptr); - Cmp(w10, w11); - sz -= 2; - ptr += 2; - } - B(ne, &blockcheck_fail); + u8* ptr = GetMemPtr(block->addr, 4); + if (ptr == NULL) + return; + Ldr(x9, reinterpret_cast(ptr)); + Ldr(w10, MemOperand(x9)); + Ldr(w11, *(u32*)ptr); + Cmp(w10, w11); + B(eq, &blockcheck_success); } - B(&blockcheck_success); + else // FullCheck + { + s32 sz = block->sh4_code_size; + u8* ptr = GetMemPtr(block->addr, sz); + if (ptr == NULL) + return; + + Ldr(x9, reinterpret_cast(ptr)); + + while (sz > 0) + { + if (sz >= 8) + { + Ldr(x10, MemOperand(x9, 8, PostIndex)); + Ldr(x11, *(u64*)ptr); + Cmp(x10, x11); + sz -= 8; + ptr += 8; + } + else if (sz >= 4) + { + Ldr(w10, MemOperand(x9, 4, PostIndex)); + Ldr(w11, *(u32*)ptr); + Cmp(w10, w11); + sz -= 4; + ptr += 4; + } + else + { + Ldrh(w10, MemOperand(x9, 2, PostIndex)); + Mov(w11, *(u16*)ptr); + Cmp(w10, w11); + sz -= 2; + ptr += 2; + } + B(ne, &blockcheck_fail); + } + B(&blockcheck_success); + } Bind(&blockcheck_fail); Ldr(w0, block->addr); TailCallRuntime(ngen_blockcheckfail); diff --git a/core/rec-x64/rec_x64.cpp b/core/rec-x64/rec_x64.cpp index 19e9ba835..286fefc82 100644 --- a/core/rec-x64/rec_x64.cpp +++ b/core/rec-x64/rec_x64.cpp @@ -1087,40 +1087,55 @@ private: typedef void (BlockCompiler::*X64BinaryFOp)(const Xbyak::Xmm&, const Xbyak::Operand&); void CheckBlock(RuntimeBlockInfo* block) { + if (settings.dynarec.SmcCheckLevel == NoCheck) + return; mov(call_regs[0], block->addr); - s32 sz=block->sh4_code_size; - u32 sa=block->addr; - - while (sz > 0) + if (settings.dynarec.SmcCheckLevel == FastCheck) { - void* ptr = (void*)GetMemPtr(sa, sz > 8 ? 8 : sz); + void* ptr = (void*)GetMemPtr(block->addr, 4); if (ptr) { mov(rax, reinterpret_cast(ptr)); - - if (sz >= 8) { - mov(rdx, *(u64*)ptr); - cmp(qword[rax], rdx); - sz -= 8; - sa += 8; - } - else if (sz >= 4) { - mov(edx, *(u32*)ptr); - cmp(dword[rax], edx); - sz -= 4; - sa += 4; - } - else { - mov(edx, *(u16*)ptr); - cmp(word[rax],dx); - sz -= 2; - sa += 2; - } + mov(edx, *(u32*)ptr); + cmp(dword[rax], edx); jne(reinterpret_cast(&ngen_blockcheckfail)); } } + else + { + s32 sz=block->sh4_code_size; + u32 sa=block->addr; + while (sz > 0) + { + void* ptr = (void*)GetMemPtr(sa, sz > 8 ? 8 : sz); + if (ptr) + { + mov(rax, reinterpret_cast(ptr)); + + if (sz >= 8) { + mov(rdx, *(u64*)ptr); + cmp(qword[rax], rdx); + sz -= 8; + sa += 8; + } + else if (sz >= 4) { + mov(edx, *(u32*)ptr); + cmp(dword[rax], edx); + sz -= 4; + sa += 4; + } + else { + mov(edx, *(u16*)ptr); + cmp(word[rax],dx); + sz -= 2; + sa += 2; + } + jne(reinterpret_cast(&ngen_blockcheckfail)); + } + } + } } void GenBinaryOp(const shil_opcode &op, X64BinaryOp natop) diff --git a/core/rec-x86/rec_x86_driver.cpp b/core/rec-x86/rec_x86_driver.cpp index 1c14a6924..e95cd9494 100644 --- a/core/rec-x86/rec_x86_driver.cpp +++ b/core/rec-x86/rec_x86_driver.cpp @@ -231,23 +231,36 @@ extern int mips_counter; void CheckBlock(RuntimeBlockInfo* block,x86_ptr_imm place) { - s32 sz=block->sh4_code_size; - u32 sa=block->addr; - while(sz>0) + if (settings.dynarec.SmcCheckLevel == NoCheck) + return; + if (settings.dynarec.SmcCheckLevel == FastCheck) { - void* ptr=(void*)GetMemPtr(sa,4); + void* ptr = (void*)GetMemPtr(block->addr, 4); if (ptr) { - if (sz==2) - x86e->Emit(op_cmp16,ptr,*(u16*)ptr); - else - x86e->Emit(op_cmp32,ptr,*(u32*)ptr); - x86e->Emit(op_jne,place); + x86e->Emit(op_cmp32, ptr, *(u32*)ptr); + x86e->Emit(op_jne, place); + } + } + else // FullCheck + { + s32 sz=block->sh4_code_size; + u32 sa=block->addr; + while(sz>0) + { + void* ptr=(void*)GetMemPtr(sa,4); + if (ptr) + { + if (sz==2) + x86e->Emit(op_cmp16,ptr,*(u16*)ptr); + else + x86e->Emit(op_cmp32,ptr,*(u32*)ptr); + x86e->Emit(op_jne,place); + } + sz-=4; + sa+=4; } - sz-=4; - sa+=4; } - } diff --git a/core/rend/gui.cpp b/core/rend/gui.cpp index f16414182..8514ddd31 100644 --- a/core/rend/gui.cpp +++ b/core/rend/gui.cpp @@ -1010,10 +1010,33 @@ static void gui_display_settings() ShowHelpMarker("Do not optimize integer division. Recommended"); ImGui::Checkbox("Unstable Optimizations", &settings.dynarec.unstable_opt); ImGui::SameLine(); - ShowHelpMarker("Enable unsafe optimizations. May cause crash or environmental disaster"); + ShowHelpMarker("Enable unsafe optimizations. Will cause crash or environmental disaster"); ImGui::Checkbox("Idle Skip", &settings.dynarec.idleskip); ImGui::SameLine(); ShowHelpMarker("Skip wait loops. Recommended"); + ImGui::PushItemWidth(ImGui::CalcTextSize("Largeenough").x); + const char *preview = settings.dynarec.SmcCheckLevel == NoCheck ? "None" : settings.dynarec.SmcCheckLevel == FastCheck ? "Fast" : "Full"; + if (ImGui::BeginCombo("SMC Checks", preview , ImGuiComboFlags_None)) + { + bool is_selected = settings.dynarec.SmcCheckLevel == NoCheck; + if (ImGui::Selectable("None", &is_selected)) + settings.dynarec.SmcCheckLevel = NoCheck; + if (is_selected) + ImGui::SetItemDefaultFocus(); + is_selected = settings.dynarec.SmcCheckLevel == FastCheck; + if (ImGui::Selectable("Fast", &is_selected)) + settings.dynarec.SmcCheckLevel = FastCheck; + if (is_selected) + ImGui::SetItemDefaultFocus(); + is_selected = settings.dynarec.SmcCheckLevel == FullCheck; + if (ImGui::Selectable("Full", &is_selected)) + settings.dynarec.SmcCheckLevel = FullCheck; + if (is_selected) + ImGui::SetItemDefaultFocus(); + ImGui::EndCombo(); + } + ImGui::SameLine(); + ShowHelpMarker("How to detect self-modifying code. Full check recommended"); } if (ImGui::CollapsingHeader("Other", ImGuiTreeNodeFlags_DefaultOpen)) { diff --git a/core/types.h b/core/types.h index b3f809dc3..9d2e54d4d 100644 --- a/core/types.h +++ b/core/types.h @@ -601,6 +601,11 @@ struct RegisterStruct u32 flags; //Access flags ! }; +enum SmcCheckEnum { + FullCheck = 0, + FastCheck = 1, + NoCheck = 2 +}; struct settings_t { @@ -637,6 +642,7 @@ struct settings_t bool unstable_opt; bool safemode; bool disable_nvmem; + SmcCheckEnum SmcCheckLevel; } dynarec; struct From 7f44bb99560c5e308cca0325b47efcfd711f0653 Mon Sep 17 00:00:00 2001 From: Flyinghead Date: Fri, 29 Mar 2019 19:54:59 +0100 Subject: [PATCH 203/319] vmu: pass bus id and port to callback --- core/hw/maple/maple_devs.cpp | 3 +-- core/hw/maple/maple_devs.h | 2 +- core/rend/gui.cpp | 7 ++++--- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/core/hw/maple/maple_devs.cpp b/core/hw/maple/maple_devs.cpp index 90cc5a089..ef9d5ac35 100755 --- a/core/hw/maple/maple_devs.cpp +++ b/core/hw/maple/maple_devs.cpp @@ -644,8 +644,7 @@ struct maple_sega_vmu: maple_base } } config->SetImage(lcd_data_decoded); - if (bus_id <= 3 && bus_port <= 1) - push_vmu_screen(bus_id * 2 + bus_port, lcd_data_decoded); + push_vmu_screen(bus_id, bus_port, lcd_data_decoded); #if 0 // Update LCD window if (!dev->lcd.visible) diff --git a/core/hw/maple/maple_devs.h b/core/hw/maple/maple_devs.h index 34cce8e56..ec45b4986 100755 --- a/core/hw/maple/maple_devs.h +++ b/core/hw/maple/maple_devs.h @@ -102,5 +102,5 @@ maple_device* maple_Create(MapleDeviceType type); #ifndef TARGET_PANDORA int get_mic_data(u8* buffer); //implemented in Android.cpp #endif -int push_vmu_screen(int vmu_id, u8* buffer); // vmu_id: 0-7 +void push_vmu_screen(int bus_id, int bus_port, u8* buffer); #define MAPLE_PORTS 4 diff --git a/core/rend/gui.cpp b/core/rend/gui.cpp index e2e9ef357..4b53795fb 100644 --- a/core/rend/gui.cpp +++ b/core/rend/gui.cpp @@ -1511,14 +1511,15 @@ static u32 vmu_lcd_data[8][48 * 32]; static bool vmu_lcd_status[8]; static ImTextureID vmu_lcd_tex_ids[8]; -int push_vmu_screen(int vmu_id, u8* buffer) +void push_vmu_screen(int bus_id, int bus_port, u8* buffer) { + int vmu_id = bus_id * 2 + bus_port; + if (vmu_id < 0 || vmu_id >= ARRAY_SIZE(vmu_lcd_data)) + return; u32 *p = &vmu_lcd_data[vmu_id][0]; for (int i = 0; i < ARRAY_SIZE(vmu_lcd_data[vmu_id]); i++, buffer++) *p++ = *buffer != 0 ? 0xFFFFFFFFu : 0xFF000000u; vmu_lcd_status[vmu_id] = true; - - return 0; } static const int vmu_coords[8][2] = { From 8e9b1306e1e205f055949adb94f4781914890069 Mon Sep 17 00:00:00 2001 From: Flyinghead Date: Fri, 29 Mar 2019 20:28:49 +0100 Subject: [PATCH 204/319] dynarec: make FullCheck the default SMC option --- core/nullDC.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/nullDC.cpp b/core/nullDC.cpp index 1be9a7b76..2b9df65ce 100755 --- a/core/nullDC.cpp +++ b/core/nullDC.cpp @@ -494,7 +494,7 @@ void InitSettings() settings.dreamcast.broadcast = 4; // default settings.dreamcast.language = 6; // default settings.dreamcast.FullMMU = false; - settings.dynarec.SmcCheckLevel = NoCheck; + settings.dynarec.SmcCheckLevel = FullCheck; settings.aica.LimitFPS = true; settings.aica.NoBatch = false; // This also controls the DSP. Disabled by default settings.aica.NoSound = false; From af6993a81912d048aca53d9179843e19a42ffbee Mon Sep 17 00:00:00 2001 From: Stefanos Kornilios Mitsis Poiitidis Date: Sat, 30 Mar 2019 06:33:52 +0100 Subject: [PATCH 205/319] dynarec: Refactor smc-option a bit --- core/hw/sh4/dyna/blockmanager.cpp | 4 +- core/hw/sh4/dyna/driver.cpp | 62 ++++++++++------ core/hw/sh4/dyna/ngen.h | 2 +- core/hw/sh4/modules/ccn.cpp | 21 ++++-- core/rec-ARM/rec_arm.cpp | 21 ++++-- core/rec-ARM64/rec_arm64.cpp | 118 ++++++++++++++++-------------- core/rec-cpp/rec_cpp.cpp | 18 +++-- core/rec-x64/rec_x64.cpp | 108 ++++++++++++++------------- core/rec-x86/rec_x86_driver.cpp | 64 +++++++++------- core/rend/gui.cpp | 4 +- 10 files changed, 245 insertions(+), 177 deletions(-) diff --git a/core/hw/sh4/dyna/blockmanager.cpp b/core/hw/sh4/dyna/blockmanager.cpp index dd0a3b818..e64cd0d60 100644 --- a/core/hw/sh4/dyna/blockmanager.cpp +++ b/core/hw/sh4/dyna/blockmanager.cpp @@ -304,6 +304,8 @@ void bm_Rebuild() { return; + die("this is broken in multiple levels, including compile options"); + void RASDASD(); RASDASD(); @@ -321,7 +323,7 @@ void bm_Rebuild() //constprop(all_blocks[i]); //#endif } - ngen_Compile(all_blocks[i],false,false,all_blocks[i]->staging_runs>0,do_opts); + ngen_Compile(all_blocks[i],NoCheck,false,all_blocks[i]->staging_runs>0,do_opts); blkmap.insert(all_blocks[i]); verify(bm_GetBlock((RuntimeBlockInfo*)all_blocks[i]->code)==all_blocks[i]); diff --git a/core/hw/sh4/dyna/driver.cpp b/core/hw/sh4/dyna/driver.cpp index dcb74ec4c..8f66a4258 100644 --- a/core/hw/sh4/dyna/driver.cpp +++ b/core/hw/sh4/dyna/driver.cpp @@ -86,10 +86,13 @@ void recSh4_Run() sh4_dyna_rcb=(u8*)&Sh4cntx + sizeof(Sh4cntx); printf("cntx // fpcb offset: %td // pc offset: %td // pc %08X\n",(u8*)&sh4rcb.fpcb - sh4_dyna_rcb, (u8*)&sh4rcb.cntx.pc - sh4_dyna_rcb,sh4rcb.cntx.pc); + if (!settings.dynarec.safemode) printf("Warning: Dynarec safe mode is off\n"); + if (settings.dynarec.unstable_opt) printf("Warning: Unstable optimizations is on\n"); + if (settings.dynarec.SmcCheckLevel != FullCheck) printf("Warning: SMC check mode is %d\n", settings.dynarec.SmcCheckLevel); @@ -125,34 +128,51 @@ u32 emit_FreeSpace() } -bool DoCheck(u32 pc) +SmcCheckEnum DoCheck(u32 pc) { - if (IsOnRam(pc)) - { - if (!settings.dynarec.unstable_opt) - return true; - pc&=0xFFFFFF; - switch(pc) - { - //DOA2LE - case 0x3DAFC6: - case 0x3C83F8: + switch (settings.dynarec.SmcCheckLevel) { - //Shenmue 2 - case 0x348000: - - //Shenmue - case 0x41860e: - + // Heuristic-elimintaed FastChecks + case NoCheck: { + if (IsOnRam(pc)) + { + pc&=0xFFFFFF; + switch(pc) + { + //DOA2LE + case 0x3DAFC6: + case 0x3C83F8: - return true; + //Shenmue 2 + case 0x348000: + + //Shenmue + case 0x41860e: + - default: - return false; + return FastCheck; + + default: + return NoCheck; + } + } + return NoCheck; } + break; + + // Fast Check everything + case FastCheck: + return FastCheck; + + // Full Check everything + case FullCheck: + return FastCheck; + + default: + die("Unhandled settings.dynarec.SmcCheckLevel"); } - return false; + } void AnalyseBlock(RuntimeBlockInfo* blk); diff --git a/core/hw/sh4/dyna/ngen.h b/core/hw/sh4/dyna/ngen.h index 0e7d9cd3b..e7d2aff35 100644 --- a/core/hw/sh4/dyna/ngen.h +++ b/core/hw/sh4/dyna/ngen.h @@ -85,7 +85,7 @@ u32 DYNACALL rdv_DoInterrupts_pc(u32 pc); void ngen_init(); //Called to compile a block -void ngen_Compile(RuntimeBlockInfo* block,bool force_checks, bool reset, bool staging,bool optimise); +void ngen_Compile(RuntimeBlockInfo* block, SmcCheckEnum smc_checks, bool reset, bool staging,bool optimise); //Called when blocks are reseted void ngen_ResetBlocks(); diff --git a/core/hw/sh4/modules/ccn.cpp b/core/hw/sh4/modules/ccn.cpp index 49b7c574f..99d694c8d 100644 --- a/core/hw/sh4/modules/ccn.cpp +++ b/core/hw/sh4/modules/ccn.cpp @@ -12,6 +12,8 @@ //Types +#define printf_smc(...) // printf + u32 CCN_QACR_TR[2]; @@ -72,13 +74,18 @@ void CCN_CCR_write(u32 addr, u32 value) temp.reg_data=value; - //what is 0xAC13DBF8 from ? - if (temp.ICI && curr_pc!=0xAC13DBF8) - { - //printf("Sh4: i-cache invalidation %08X\n",curr_pc); - // Shikigami No Shiro II sets ICI frequently - // Any reason to flush the dynarec cache for this? - //sh4_cpu.ResetCache(); + if (temp.ICI) { + printf_smc("Sh4: i-cache invalidation %08X\n",curr_pc); + + if (settings.dynarec.SmcCheckLevel != FullCheck) { + //TODO: Add skip/check vectors for Shikigami No Shiro II (uses ICI frequently) + //which game is 0xAC13DBF8 from ? + if (curr_pc != 0xAC13DBF8) + { + printf("Sh4: code cache clear (ICI) pc: %08X\n",curr_pc); + sh4_cpu.ResetCache(); + } + } } temp.ICI=0; diff --git a/core/rec-ARM/rec_arm.cpp b/core/rec-ARM/rec_arm.cpp index 9fdf8ea58..78f8f9249 100644 --- a/core/rec-ARM/rec_arm.cpp +++ b/core/rec-ARM/rec_arm.cpp @@ -2082,7 +2082,7 @@ __default: } -void ngen_Compile(RuntimeBlockInfo* block,bool force_checks, bool reset, bool staging,bool optimise) +void ngen_Compile(RuntimeBlockInfo* block, SmcCheckEnum smc_checks, bool reset, bool staging,bool optimise) { //printf("Compile: %08X, %d, %d\n",block->addr,staging,optimise); block->code=(DynarecCodeEntryPtr)EMIT_GET_PTR(); @@ -2114,10 +2114,11 @@ void ngen_Compile(RuntimeBlockInfo* block,bool force_checks, bool reset, bool st reg.OpBegin(&block->oplist[0],0); //scheduler - if (force_checks && settings.dynarec.SmcCheckLevel != NoCheck) - { - if (settings.dynarec.SmcCheckLevel == FastCheck) - { + switch (smc_checks) { + case NoCheck: + break; + + case FastCheck: { MOV32(r0,block->addr); u32* ptr=(u32*)GetMemPtr(block->addr,4); MOV32(r2,(u32)ptr); @@ -2126,8 +2127,9 @@ void ngen_Compile(RuntimeBlockInfo* block,bool force_checks, bool reset, bool st CMP(r1,r2); JUMP((u32)ngen_blockcheckfail, CC_NE); } - else // FullCheck - { + break; + + case FullCheck: { s32 sz = block->sh4_code_size; u32 addr = block->addr; MOV32(r0,addr); @@ -2160,6 +2162,11 @@ void ngen_Compile(RuntimeBlockInfo* block,bool force_checks, bool reset, bool st } } } + break; + + default: { + die("unhandled smc_checks"); + } } u32 cyc=block->guest_cycles; diff --git a/core/rec-ARM64/rec_arm64.cpp b/core/rec-ARM64/rec_arm64.cpp index a4b7803fb..b30720796 100644 --- a/core/rec-ARM64/rec_arm64.cpp +++ b/core/rec-ARM64/rec_arm64.cpp @@ -348,15 +348,15 @@ public: return *ret_reg; } - void ngen_Compile(RuntimeBlockInfo* block, bool force_checks, bool reset, bool staging, bool optimise) + void ngen_Compile(RuntimeBlockInfo* block, SmcCheckEnum smc_checks, bool reset, bool staging, bool optimise) { //printf("REC-ARM64 compiling %08x\n", block->addr); #ifdef PROFILING SaveFramePointer(); #endif this->block = block; - if (force_checks) - CheckBlock(block); + + CheckBlock(smc_checks, block); // run register allocator regalloc.DoAlloc(block); @@ -1292,65 +1292,73 @@ private: verify (GetCursorAddress() - start_instruction == code_size * kInstructionSize); } - void CheckBlock(RuntimeBlockInfo* block) + void CheckBlock(SmcCheckEnum smc_checks, RuntimeBlockInfo* block) { - if (settings.dynarec.SmcCheckLevel == NoCheck) - return; Label blockcheck_fail; Label blockcheck_success; - if (settings.dynarec.SmcCheckLevel == FastCheck) - { - u8* ptr = GetMemPtr(block->addr, 4); - if (ptr == NULL) - return; - Ldr(x9, reinterpret_cast(ptr)); - Ldr(w10, MemOperand(x9)); - Ldr(w11, *(u32*)ptr); - Cmp(w10, w11); - B(eq, &blockcheck_success); - } - else // FullCheck - { - s32 sz = block->sh4_code_size; - - u8* ptr = GetMemPtr(block->addr, sz); - if (ptr == NULL) + switch (smc_checks) { + case NoCheck: return; - Ldr(x9, reinterpret_cast(ptr)); - - while (sz > 0) - { - if (sz >= 8) - { - Ldr(x10, MemOperand(x9, 8, PostIndex)); - Ldr(x11, *(u64*)ptr); - Cmp(x10, x11); - sz -= 8; - ptr += 8; - } - else if (sz >= 4) - { - Ldr(w10, MemOperand(x9, 4, PostIndex)); - Ldr(w11, *(u32*)ptr); - Cmp(w10, w11); - sz -= 4; - ptr += 4; - } - else - { - Ldrh(w10, MemOperand(x9, 2, PostIndex)); - Mov(w11, *(u16*)ptr); - Cmp(w10, w11); - sz -= 2; - ptr += 2; - } - B(ne, &blockcheck_fail); + case FastCheck: { + u8* ptr = GetMemPtr(block->addr, 4); + if (ptr == NULL) + return; + Ldr(x9, reinterpret_cast(ptr)); + Ldr(w10, MemOperand(x9)); + Ldr(w11, *(u32*)ptr); + Cmp(w10, w11); + B(eq, &blockcheck_success); } - B(&blockcheck_success); + break; + + case FullCheck: { + s32 sz = block->sh4_code_size; + + u8* ptr = GetMemPtr(block->addr, sz); + if (ptr == NULL) + return; + + Ldr(x9, reinterpret_cast(ptr)); + + while (sz > 0) + { + if (sz >= 8) + { + Ldr(x10, MemOperand(x9, 8, PostIndex)); + Ldr(x11, *(u64*)ptr); + Cmp(x10, x11); + sz -= 8; + ptr += 8; + } + else if (sz >= 4) + { + Ldr(w10, MemOperand(x9, 4, PostIndex)); + Ldr(w11, *(u32*)ptr); + Cmp(w10, w11); + sz -= 4; + ptr += 4; + } + else + { + Ldrh(w10, MemOperand(x9, 2, PostIndex)); + Mov(w11, *(u16*)ptr); + Cmp(w10, w11); + sz -= 2; + ptr += 2; + } + B(ne, &blockcheck_fail); + } + B(&blockcheck_success); + } + break; + + default: + die("unhandled smc_checks"); } + Bind(&blockcheck_fail); Ldr(w0, block->addr); TailCallRuntime(ngen_blockcheckfail); @@ -1419,13 +1427,13 @@ private: static Arm64Assembler* compiler; -void ngen_Compile(RuntimeBlockInfo* block, bool force_checks, bool reset, bool staging, bool optimise) +void ngen_Compile(RuntimeBlockInfo* block, SmcCheckEnum smc_checks, bool reset, bool staging, bool optimise) { verify(emit_FreeSpace() >= 16 * 1024); compiler = new Arm64Assembler(); - compiler->ngen_Compile(block, force_checks, reset, staging, optimise); + compiler->ngen_Compile(block, smc_checks, reset, staging, optimise); delete compiler; compiler = NULL; diff --git a/core/rec-cpp/rec_cpp.cpp b/core/rec-cpp/rec_cpp.cpp index a3d155679..caa403bdd 100644 --- a/core/rec-cpp/rec_cpp.cpp +++ b/core/rec-cpp/rec_cpp.cpp @@ -1190,10 +1190,10 @@ public: size_t opcode_index; opcodeExec** ptrsg; - void compile(RuntimeBlockInfo* block, bool force_checks, bool reset, bool staging, bool optimise) { + void compile(RuntimeBlockInfo* block, SmcCheckEnum smc_checks, bool reset, bool staging, bool optimise) { //we need an extra one for the end opcode and optionally one more for block check - auto ptrs = fnnCtor_forreal(block->oplist.size() + 1 + (force_checks ? 1 : 0))(block->guest_cycles); + auto ptrs = fnnCtor_forreal(block->oplist.size() + 1 + (smc_checks != NoCheck ? 1 : 0))(block->guest_cycles); ptrsg = ptrs.ptrs; @@ -1207,9 +1207,16 @@ public: } size_t i = 0; - if (force_checks) + if (smc_checks != NoCheck) { + verify (smc_checks == FastCheck || smc_checks == FullCheck) opcodeExec* op; + int check_size = block->sh4_code_size; + + if (smc_checks == FastCheck) { + check_size = 4; + } + switch (block->sh4_code_size) { case 4: @@ -1227,6 +1234,7 @@ public: } ptrs.ptrs[i++] = op; } + for (size_t opnum = 0; opnum < block->oplist.size(); opnum++, i++) { opcode_index = i; shil_opcode& op = block->oplist[opnum]; @@ -1551,14 +1559,14 @@ public: BlockCompiler* compiler; -void ngen_Compile(RuntimeBlockInfo* block, bool force_checks, bool reset, bool staging, bool optimise) +void ngen_Compile(RuntimeBlockInfo* block, SmcCheckEnum smc_checks, bool reset, bool staging, bool optimise) { verify(emit_FreeSpace() >= 16 * 1024); compiler = new BlockCompiler(); - compiler->compile(block, force_checks, reset, staging, optimise); + compiler->compile(block, smc_checks, reset, staging, optimise); delete compiler; } diff --git a/core/rec-x64/rec_x64.cpp b/core/rec-x64/rec_x64.cpp index 286fefc82..f3a23bdc3 100644 --- a/core/rec-x64/rec_x64.cpp +++ b/core/rec-x64/rec_x64.cpp @@ -239,12 +239,11 @@ public: call_regsxmm.push_back(xmm3); } - void compile(RuntimeBlockInfo* block, bool force_checks, bool reset, bool staging, bool optimise) + void compile(RuntimeBlockInfo* block, SmcCheckEnum smc_checks, bool reset, bool staging, bool optimise) { //printf("X86_64 compiling %08x to %p\n", block->addr, emit_GetCCPtr()); - if (force_checks) { - CheckBlock(block); - } + CheckBlock(smc_checks, block); + regalloc.DoAlloc(block); sub(dword[rip + &cycle_counter], block->guest_cycles); @@ -1086,55 +1085,64 @@ private: typedef void (BlockCompiler::*X64BinaryOp)(const Xbyak::Operand&, const Xbyak::Operand&); typedef void (BlockCompiler::*X64BinaryFOp)(const Xbyak::Xmm&, const Xbyak::Operand&); - void CheckBlock(RuntimeBlockInfo* block) { - if (settings.dynarec.SmcCheckLevel == NoCheck) - return; - mov(call_regs[0], block->addr); + void CheckBlock(SmcCheckEnum smc_checks, RuntimeBlockInfo* block) { - if (settings.dynarec.SmcCheckLevel == FastCheck) - { - void* ptr = (void*)GetMemPtr(block->addr, 4); - if (ptr) - { - mov(rax, reinterpret_cast(ptr)); - mov(edx, *(u32*)ptr); - cmp(dword[rax], edx); - jne(reinterpret_cast(&ngen_blockcheckfail)); - } - } - else - { - s32 sz=block->sh4_code_size; - u32 sa=block->addr; + switch (smc_checks) { + case NoCheck: + return; - while (sz > 0) - { - void* ptr = (void*)GetMemPtr(sa, sz > 8 ? 8 : sz); - if (ptr) + case FastCheck: { + void* ptr = (void*)GetMemPtr(block->addr, 4); + if (ptr) { + mov(call_regs[0], block->addr); mov(rax, reinterpret_cast(ptr)); - - if (sz >= 8) { - mov(rdx, *(u64*)ptr); - cmp(qword[rax], rdx); - sz -= 8; - sa += 8; - } - else if (sz >= 4) { - mov(edx, *(u32*)ptr); - cmp(dword[rax], edx); - sz -= 4; - sa += 4; - } - else { - mov(edx, *(u16*)ptr); - cmp(word[rax],dx); - sz -= 2; - sa += 2; - } - jne(reinterpret_cast(&ngen_blockcheckfail)); + mov(edx, *(u32*)ptr); + cmp(dword[rax], edx); + jne(reinterpret_cast(&ngen_blockcheckfail)); } - } + } + break; + + case FullCheck: { + s32 sz=block->sh4_code_size; + u32 sa=block->addr; + + mov(call_regs[0], block->addr); + + while (sz > 0) + { + void* ptr = (void*)GetMemPtr(sa, sz > 8 ? 8 : sz); + if (ptr) + { + mov(rax, reinterpret_cast(ptr)); + + if (sz >= 8) { + mov(rdx, *(u64*)ptr); + cmp(qword[rax], rdx); + sz -= 8; + sa += 8; + } + else if (sz >= 4) { + mov(edx, *(u32*)ptr); + cmp(dword[rax], edx); + sz -= 4; + sa += 4; + } + else { + mov(edx, *(u16*)ptr); + cmp(word[rax],dx); + sz -= 2; + sa += 2; + } + jne(reinterpret_cast(&ngen_blockcheckfail)); + } + } + } + break; + + default: + die("unhandled smc_checks"); } } @@ -1277,13 +1285,13 @@ void X64RegAlloc::Writeback_FPU(u32 reg, s8 nreg) static BlockCompiler* compiler; -void ngen_Compile(RuntimeBlockInfo* block, bool force_checks, bool reset, bool staging, bool optimise) +void ngen_Compile(RuntimeBlockInfo* block, SmcCheckEnum smc_checks, bool reset, bool staging, bool optimise) { verify(emit_FreeSpace() >= 16 * 1024); compiler = new BlockCompiler(); - compiler->compile(block, force_checks, reset, staging, optimise); + compiler->compile(block, smc_checks, reset, staging, optimise); delete compiler; } diff --git a/core/rec-x86/rec_x86_driver.cpp b/core/rec-x86/rec_x86_driver.cpp index e95cd9494..1d19a46fa 100644 --- a/core/rec-x86/rec_x86_driver.cpp +++ b/core/rec-x86/rec_x86_driver.cpp @@ -229,42 +229,50 @@ u32 rdmt[6]; extern u32 memops_t,memops_l; extern int mips_counter; -void CheckBlock(RuntimeBlockInfo* block,x86_ptr_imm place) +//TODO: Get back validating mode for this +void CheckBlock(SmcCheckEnum smc_checks, RuntimeBlockInfo* block) { - if (settings.dynarec.SmcCheckLevel == NoCheck) - return; - if (settings.dynarec.SmcCheckLevel == FastCheck) - { - void* ptr = (void*)GetMemPtr(block->addr, 4); - if (ptr) - { - x86e->Emit(op_cmp32, ptr, *(u32*)ptr); - x86e->Emit(op_jne, place); - } - } - else // FullCheck - { - s32 sz=block->sh4_code_size; - u32 sa=block->addr; - while(sz>0) - { - void* ptr=(void*)GetMemPtr(sa,4); + switch (smc_checks) { + case NoCheck: + break; + + case FastCheck: { + void* ptr = (void*)GetMemPtr(block->addr, 4); if (ptr) { - if (sz==2) - x86e->Emit(op_cmp16,ptr,*(u16*)ptr); - else - x86e->Emit(op_cmp32,ptr,*(u32*)ptr); - x86e->Emit(op_jne,place); + x86e->Emit(op_cmp32, ptr, *(u32*)ptr); + x86e->Emit(op_jne, x86_ptr_imm(ngen_blockcheckfail)); } - sz-=4; - sa+=4; } + break; + + case FullCheck: { + s32 sz=block->sh4_code_size; + u32 sa=block->addr; + while(sz>0) + { + void* ptr=(void*)GetMemPtr(sa,4); + if (ptr) + { + if (sz==2) + x86e->Emit(op_cmp16,ptr,*(u16*)ptr); + else + x86e->Emit(op_cmp32,ptr,*(u32*)ptr); + x86e->Emit(op_jne,x86_ptr_imm(ngen_blockcheckfail)); + } + sz-=4; + sa+=4; + } + } + break; + + default: + die("unhandled smc_checks"); } } -void ngen_Compile(RuntimeBlockInfo* block,bool force_checks, bool reset, bool staging,bool optimise) +void ngen_Compile(RuntimeBlockInfo* block, SmcCheckEnum smc_checks, bool reset, bool staging,bool optimise) { //initialise stuff DetectCpuFeatures(); @@ -295,7 +303,7 @@ void ngen_Compile(RuntimeBlockInfo* block,bool force_checks, bool reset, bool st //block invl. checks x86e->Emit(op_mov32,ECX,block->addr); - CheckBlock(block,force_checks?x86_ptr_imm(ngen_blockcheckfail):x86_ptr_imm(ngen_blockcheckfail2)); + CheckBlock(smc_checks, block); //Scheduler x86_Label* no_up=x86e->CreateLabel(false,8); diff --git a/core/rend/gui.cpp b/core/rend/gui.cpp index 8514ddd31..27e676307 100644 --- a/core/rend/gui.cpp +++ b/core/rend/gui.cpp @@ -1015,11 +1015,11 @@ static void gui_display_settings() ImGui::SameLine(); ShowHelpMarker("Skip wait loops. Recommended"); ImGui::PushItemWidth(ImGui::CalcTextSize("Largeenough").x); - const char *preview = settings.dynarec.SmcCheckLevel == NoCheck ? "None" : settings.dynarec.SmcCheckLevel == FastCheck ? "Fast" : "Full"; + const char *preview = settings.dynarec.SmcCheckLevel == NoCheck ? "Faster" : settings.dynarec.SmcCheckLevel == FastCheck ? "Fast" : "Full"; if (ImGui::BeginCombo("SMC Checks", preview , ImGuiComboFlags_None)) { bool is_selected = settings.dynarec.SmcCheckLevel == NoCheck; - if (ImGui::Selectable("None", &is_selected)) + if (ImGui::Selectable("Faster", &is_selected)) settings.dynarec.SmcCheckLevel = NoCheck; if (is_selected) ImGui::SetItemDefaultFocus(); From 505c5b6c4d6d195ca7525678ad3dba55932fef30 Mon Sep 17 00:00:00 2001 From: Flyinghead Date: Sat, 30 Mar 2019 10:06:19 +0100 Subject: [PATCH 206/319] dynarec: FullCheck was being ignored and FastCheck used instead Fix crash on arm32 Fix infinite loop on x64 --- core/hw/sh4/dyna/driver.cpp | 4 ++-- core/rec-ARM/rec_arm.cpp | 39 +++++++++++++++++++++++-------------- core/rec-x64/rec_x64.cpp | 13 +++++++------ 3 files changed, 33 insertions(+), 23 deletions(-) diff --git a/core/hw/sh4/dyna/driver.cpp b/core/hw/sh4/dyna/driver.cpp index 8f66a4258..56e66724e 100644 --- a/core/hw/sh4/dyna/driver.cpp +++ b/core/hw/sh4/dyna/driver.cpp @@ -167,12 +167,12 @@ SmcCheckEnum DoCheck(u32 pc) // Full Check everything case FullCheck: - return FastCheck; + return FullCheck; default: die("Unhandled settings.dynarec.SmcCheckLevel"); + return FullCheck; } - } void AnalyseBlock(RuntimeBlockInfo* blk); diff --git a/core/rec-ARM/rec_arm.cpp b/core/rec-ARM/rec_arm.cpp index 78f8f9249..933ff19d2 100644 --- a/core/rec-ARM/rec_arm.cpp +++ b/core/rec-ARM/rec_arm.cpp @@ -2121,11 +2121,14 @@ void ngen_Compile(RuntimeBlockInfo* block, SmcCheckEnum smc_checks, bool reset, case FastCheck: { MOV32(r0,block->addr); u32* ptr=(u32*)GetMemPtr(block->addr,4); - MOV32(r2,(u32)ptr); - LDR(r2,r2,0); - MOV32(r1,*ptr); - CMP(r1,r2); - JUMP((u32)ngen_blockcheckfail, CC_NE); + if (ptr != NULL) + { + MOV32(r2,(u32)ptr); + LDR(r2,r2,0); + MOV32(r1,*ptr); + CMP(r1,r2); + JUMP((u32)ngen_blockcheckfail, CC_NE); + } } break; @@ -2139,24 +2142,30 @@ void ngen_Compile(RuntimeBlockInfo* block, SmcCheckEnum smc_checks, bool reset, if (sz > 2) { u32* ptr=(u32*)GetMemPtr(addr,4); - MOV32(r2,(u32)ptr); - LDR(r2,r2,0); - MOV32(r1,*ptr); - CMP(r1,r2); + if (ptr != NULL) + { + MOV32(r2,(u32)ptr); + LDR(r2,r2,0); + MOV32(r1,*ptr); + CMP(r1,r2); - JUMP((u32)ngen_blockcheckfail, CC_NE); + JUMP((u32)ngen_blockcheckfail, CC_NE); + } addr += 4; sz -= 4; } else { u16* ptr = (u16 *)GetMemPtr(addr, 2); - MOV32(r2, (u32)ptr); - LDRH(r2, r2, 0, AL); - MOVW(r1, *ptr, AL); - CMP(r1, r2); + if (ptr != NULL) + { + MOV32(r2, (u32)ptr); + LDRH(r2, r2, 0, AL); + MOVW(r1, *ptr, AL); + CMP(r1, r2); - JUMP((u32)ngen_blockcheckfail, CC_NE); + JUMP((u32)ngen_blockcheckfail, CC_NE); + } addr += 2; sz -= 2; } diff --git a/core/rec-x64/rec_x64.cpp b/core/rec-x64/rec_x64.cpp index f3a23bdc3..b80df57ee 100644 --- a/core/rec-x64/rec_x64.cpp +++ b/core/rec-x64/rec_x64.cpp @@ -1108,12 +1108,12 @@ private: s32 sz=block->sh4_code_size; u32 sa=block->addr; - mov(call_regs[0], block->addr); - - while (sz > 0) + void* ptr = (void*)GetMemPtr(sa, sz > 8 ? 8 : sz); + if (ptr) { - void* ptr = (void*)GetMemPtr(sa, sz > 8 ? 8 : sz); - if (ptr) + mov(call_regs[0], block->addr); + + while (sz > 0) { mov(rax, reinterpret_cast(ptr)); @@ -1136,8 +1136,9 @@ private: sa += 2; } jne(reinterpret_cast(&ngen_blockcheckfail)); + ptr = (void*)GetMemPtr(sa, sz > 8 ? 8 : sz); } - } + } } break; From 0e198fac5802f1f40e050cddf2af49af84c3ced3 Mon Sep 17 00:00:00 2001 From: flyinghead Date: Sat, 30 Mar 2019 11:22:51 +0100 Subject: [PATCH 207/319] win32: allow window to be resized/maximized. Save size and max state. --- core/windows/winmain.cpp | 37 ++++++++++++++++++++++++++----------- 1 file changed, 26 insertions(+), 11 deletions(-) diff --git a/core/windows/winmain.cpp b/core/windows/winmain.cpp index d8d0e19df..480367f56 100644 --- a/core/windows/winmain.cpp +++ b/core/windows/winmain.cpp @@ -232,9 +232,10 @@ void UpdateInputState(u32 port) #define WINDOW_CLASS "nilDC" // Width and height of the window -#define WINDOW_WIDTH 1280 -#define WINDOW_HEIGHT 720 - +#define DEFAULT_WINDOW_WIDTH 1280 +#define DEFAULT_WINDOW_HEIGHT 720 +extern int screen_width, screen_height; +static bool window_maximized = false; LRESULT CALLBACK WndProc2(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) { @@ -259,6 +260,12 @@ LRESULT CALLBACK WndProc2(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) PostQuitMessage(0); return 1; + case WM_SIZE: + screen_width = LOWORD(lParam); + screen_height = HIWORD(lParam); + window_maximized = (wParam & SIZE_MAXIMIZED) != 0; + return 0; + case WM_LBUTTONDOWN: case WM_LBUTTONUP: case WM_MBUTTONDOWN: @@ -293,8 +300,8 @@ LRESULT CALLBACK WndProc2(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) static int prev_y = -1; int xPos = GET_X_LPARAM(lParam); int yPos = GET_Y_LPARAM(lParam); - mo_x_abs = (xPos - (WINDOW_WIDTH - 640 * WINDOW_HEIGHT / 480) / 2) * 480 / WINDOW_HEIGHT; - mo_y_abs = yPos * 480 / WINDOW_HEIGHT; + mo_x_abs = (xPos - (screen_width - 640 * screen_height / 480) / 2) * 480 / screen_height; + mo_y_abs = yPos * 480 / screen_height; mo_buttons = 0xffffffff; if (wParam & MK_LBUTTON) mo_buttons &= ~(1 << 2); @@ -354,12 +361,13 @@ void os_CreateWindow() sWC.cbWndExtra = 0; sWC.hInstance = (HINSTANCE)GetModuleHandle(0); sWC.hIcon = 0; - sWC.hCursor = 0; + sWC.hCursor = LoadCursor(NULL, IDC_ARROW); sWC.lpszMenuName = 0; sWC.hbrBackground = (HBRUSH) GetStockObject(WHITE_BRUSH); sWC.lpszClassName = WINDOW_CLASS; - unsigned int nWidth = WINDOW_WIDTH; - unsigned int nHeight = WINDOW_HEIGHT; + screen_width = cfgLoadInt("windows", "width", DEFAULT_WINDOW_WIDTH); + screen_height = cfgLoadInt("windows", "height", DEFAULT_WINDOW_HEIGHT); + window_maximized = cfgLoadBool("windows", "maximized", false); ATOM registerClass = RegisterClass(&sWC); if (!registerClass) @@ -369,9 +377,10 @@ void os_CreateWindow() // Create the eglWindow RECT sRect; - SetRect(&sRect, 0, 0, nWidth, nHeight); - AdjustWindowRectEx(&sRect, WS_CAPTION | WS_SYSMENU, false, 0); - HWND hWnd = CreateWindow( WINDOW_CLASS, VER_FULLNAME, WS_VISIBLE | WS_SYSMENU, + SetRect(&sRect, 0, 0, screen_width, screen_height); + AdjustWindowRectEx(&sRect, WS_OVERLAPPEDWINDOW, false, 0); + + HWND hWnd = CreateWindow( WINDOW_CLASS, VER_FULLNAME, WS_VISIBLE | WS_OVERLAPPEDWINDOW | (window_maximized ? WS_MAXIMIZE : 0), 0, 0, sRect.right-sRect.left, sRect.bottom-sRect.top, NULL, NULL, sWC.hInstance, NULL); window_win=hWnd; @@ -685,6 +694,12 @@ int CALLBACK WinMain(HINSTANCE hInstance,HINSTANCE hPrevInstance,LPSTR lpCmdLine } #endif SetUnhandledExceptionFilter(0); + cfgSaveBool("windows", "maximized", window_maximized); + if (!window_maximized) + { + cfgSaveInt("windows", "width", screen_width); + cfgSaveInt("windows", "height", screen_height); + } return 0; } From 4b0e77a2b3805c5921f618ab8f053f9538bb0d77 Mon Sep 17 00:00:00 2001 From: flyinghead Date: Sat, 30 Mar 2019 11:27:14 +0100 Subject: [PATCH 208/319] win32: don't save window size if minimized --- core/windows/winmain.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/windows/winmain.cpp b/core/windows/winmain.cpp index 480367f56..1731193e9 100644 --- a/core/windows/winmain.cpp +++ b/core/windows/winmain.cpp @@ -695,7 +695,7 @@ int CALLBACK WinMain(HINSTANCE hInstance,HINSTANCE hPrevInstance,LPSTR lpCmdLine #endif SetUnhandledExceptionFilter(0); cfgSaveBool("windows", "maximized", window_maximized); - if (!window_maximized) + if (!window_maximized && screen_width != 0 && screen_width != 0) { cfgSaveInt("windows", "width", screen_width); cfgSaveInt("windows", "height", screen_height); From c02a6969df4427f9295c7c3f38d27c196bd78ee1 Mon Sep 17 00:00:00 2001 From: flyinghead Date: Sat, 30 Mar 2019 12:32:37 +0100 Subject: [PATCH 209/319] win32: add app icon and version resource --- core/.gitignore | 1 + core/core.mk | 11 +++-- core/rend/gui.cpp | 8 ++-- core/version/.gitignore | 1 - core/version/version.h | 3 -- .../reicast-osx.xcodeproj/project.pbxproj | 2 +- shell/linux/Makefile | 8 +++- shell/windows/reicast.ico | Bin 0 -> 103422 bytes shell/windows/reicast.rc | 39 ++++++++++++++++++ shell/windows/resource.h | 16 +++++++ 10 files changed, 73 insertions(+), 16 deletions(-) create mode 100644 core/.gitignore delete mode 100644 core/version/.gitignore delete mode 100644 core/version/version.h create mode 100644 shell/windows/reicast.ico create mode 100644 shell/windows/reicast.rc create mode 100644 shell/windows/resource.h diff --git a/core/.gitignore b/core/.gitignore new file mode 100644 index 000000000..a948ed453 --- /dev/null +++ b/core/.gitignore @@ -0,0 +1 @@ +/version.h diff --git a/core/core.mk b/core/core.mk index cad01b997..1337c531a 100755 --- a/core/core.mk +++ b/core/core.mk @@ -5,7 +5,7 @@ #LDFLAGS := -Wl,-Map,$(notdir $@).map,--gc-sections -Wl,-O3 -Wl,--sort-common RZDCY_SRC_DIR ?= $(call my-dir) -VERSION_SRC := $(RZDCY_SRC_DIR)/version/version.cpp +VERSION_HEADER := $(RZDCY_SRC_DIR)/version.h RZDCY_MODULES := cfg/ hw/arm7/ hw/aica/ hw/holly/ hw/ hw/gdrom/ hw/maple/ hw/modem/ \ hw/mem/ hw/pvr/ hw/sh4/ hw/sh4/interpr/ hw/sh4/modules/ plugins/ profiler/ oslib/ \ @@ -94,7 +94,6 @@ RZDCY_FILES := $(foreach dir,$(addprefix $(RZDCY_SRC_DIR)/,$(RZDCY_MODULES)),$(w RZDCY_FILES += $(foreach dir,$(addprefix $(RZDCY_SRC_DIR)/,$(RZDCY_MODULES)),$(wildcard $(dir)*.cc)) RZDCY_FILES += $(foreach dir,$(addprefix $(RZDCY_SRC_DIR)/,$(RZDCY_MODULES)),$(wildcard $(dir)*.c)) RZDCY_FILES += $(foreach dir,$(addprefix $(RZDCY_SRC_DIR)/,$(RZDCY_MODULES)),$(wildcard $(dir)*.S)) -RZDCY_FILES += $(VERSION_SRC) ifdef FOR_PANDORA RZDCY_CFLAGS := \ @@ -156,8 +155,8 @@ endif RZDCY_CXXFLAGS := $(RZDCY_CFLAGS) -fno-exceptions -fno-rtti -std=gnu++11 -$(VERSION_SRC): - echo "const char *version = \"`git describe --tags --always`\";" > $(VERSION_SRC) - echo "const char *git_hash = \"`git rev-parse --short HEAD`\";" >> $(VERSION_SRC) - echo "const char *build_date = \"`date '+%Y-%m-%d %H:%M:%S %Z'`\";" >> $(VERSION_SRC) +$(VERSION_HEADER): + echo "#define REICAST_VERSION \"`git describe --tags --always`\"" > $(VERSION_HEADER) + echo "#define GIT_HASH \"`git rev-parse --short HEAD`\"" >> $(VERSION_HEADER) + echo "#define BUILD_DATE \"`date '+%Y-%m-%d %H:%M:%S %Z'`\"" >> $(VERSION_HEADER) diff --git a/core/rend/gui.cpp b/core/rend/gui.cpp index 4b53795fb..54786facf 100644 --- a/core/rend/gui.cpp +++ b/core/rend/gui.cpp @@ -34,7 +34,7 @@ #include "linux-dist/main.h" // FIXME for kcode[] #include "gui_util.h" #include "gui_android.h" -#include "version/version.h" +#include "version.h" extern void dc_loadstate(); extern void dc_savestate(); @@ -1041,9 +1041,9 @@ static void gui_display_settings() ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, normal_padding); if (ImGui::CollapsingHeader("Reicast", ImGuiTreeNodeFlags_DefaultOpen)) { - ImGui::Text("Version: %s", version); - ImGui::Text("Git Hash: %s", git_hash); - ImGui::Text("Build Date: %s", build_date); + ImGui::Text("Version: %s", REICAST_VERSION); + ImGui::Text("Git Hash: %s", GIT_HASH); + ImGui::Text("Build Date: %s", BUILD_DATE); ImGui::Text("Target: %s", #if DC_PLATFORM == DC_PLATFORM_DREAMCAST "Dreamcast" diff --git a/core/version/.gitignore b/core/version/.gitignore deleted file mode 100644 index 1946cee8c..000000000 --- a/core/version/.gitignore +++ /dev/null @@ -1 +0,0 @@ -/version.cpp diff --git a/core/version/version.h b/core/version/version.h deleted file mode 100644 index 3c9912aa7..000000000 --- a/core/version/version.h +++ /dev/null @@ -1,3 +0,0 @@ -extern const char *version; -extern const char *git_hash; -extern const char *build_date; diff --git a/shell/apple/emulator-osx/reicast-osx.xcodeproj/project.pbxproj b/shell/apple/emulator-osx/reicast-osx.xcodeproj/project.pbxproj index b50789eb9..a12ad753f 100644 --- a/shell/apple/emulator-osx/reicast-osx.xcodeproj/project.pbxproj +++ b/shell/apple/emulator-osx/reicast-osx.xcodeproj/project.pbxproj @@ -2199,7 +2199,7 @@ ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "echo \"const char *version = \\\"`git describe --tags --always`\\\";\" > $SRCROOT/../../../core/version/version.cpp\necho \"const char *git_hash = \\\"`git rev-parse --short HEAD`\\\";\" >> $SRCROOT/../../../core/version/version.cpp\necho \"const char *build_date = \\\"`date '+%Y-%m-%d %H:%M:%S %Z'`\\\";\" >> $SRCROOT/../../../core/version/version.cpp\n"; + shellScript = "echo \"#define REICAST_VERSION \\\"`git describe --tags --always`\\\"\" > $SRCROOT/../../../core/version.h\necho \"#define GIT_HASH \\\"`git rev-parse --short HEAD`\\\"\" >> $SRCROOT/../../../core/version.h\necho \"#define BUILD_DATE \\\"`date '+%Y-%m-%d %H:%M:%S %Z'`\\\"\" >> $SRCROOT/../../../core/version.h\n"; }; /* End PBXShellScriptBuildPhase section */ diff --git a/shell/linux/Makefile b/shell/linux/Makefile index eea87ef76..aa2acba74 100644 --- a/shell/linux/Makefile +++ b/shell/linux/Makefile @@ -421,6 +421,12 @@ OBJECTS:=$(OBJECTS:.c=.build_obj) OBJECTS:=$(OBJECTS:.S=.build_obj) OBJECTS:=$(patsubst $(RZDCY_SRC_DIR)/%,$(BUILDDIR)/%,$(OBJECTS)) +ifdef FOR_WINDOWS +OBJECTS+=$(BUILDDIR)/reicastres.build_obj +$(BUILDDIR)/reicastres.build_obj: $(LOCAL_PATH)/../windows/reicast.rc $(LOCAL_PATH)/../windows/reicast.ico $(RZDCY_SRC_DIR)/version.h + windres $< $@ +endif + DEPDIR := .dep-$(BUILDDIR) DEPFLAGS = -MT $@ -MD -MP -MF $(DEPDIR)/$*.Td DEPS=$(RZDCY_FILES:.cpp=.d) @@ -500,7 +506,7 @@ uninstall: rm -f $(DESTDIR)$(ICON_DIR)/reicast.png clean: - rm -f $(OBJECTS) $(EXECUTABLE) $(EXECUTABLE_STRIPPED) .map + rm -f $(OBJECTS) $(EXECUTABLE) $(EXECUTABLE_STRIPPED) .map $(RZDCY_SRC_DIR)/version.h .PRECIOUS = $(DEPDIR)/%.d $(DEPDIR)/%.d: ; diff --git a/shell/windows/reicast.ico b/shell/windows/reicast.ico new file mode 100644 index 0000000000000000000000000000000000000000..99528ed33a1e76c635b124bf67983ba16b478049 GIT binary patch literal 103422 zcmeEP2Uu1|vmT0qBA_55DguhAfLKrjX&=4!-V_x@1$*xrd)L^FEox$-#u5{w>AhFe z{=^hx5|d~&YK&dZecuCzd=d~aB)|7wc^uFAPT8H^eRpPdc6N`5fz*@6jRk*>GRRmY z9cRs(m;RpLK;(H`b95~I{Ro~5w-D*lrR;Y*k+ZEumM<^;Jt)CI)`W_5z)vRW2r^Lr z#|hH0HY+9k`X~R)jYM1xM4BNJoL2v5Qu>H>lX^y3-EFOoWV^RHo$2cINl> zC0H667&w_X{7XVCBmN9*P3oujYGL!{AkPk0mq+?s+maY~YI9=Xxz#bg%G}T%KlbnH z60$W}C+5btvV;7@RHS{ntJ7DjVtOff=j`C_9bX7{wA&ru&VIwRz^)H&N(?-<8RL6-ad^*x z^FzHp+M1+0=w@ZE>)f>Ql1^3@0ZgKkwWZbkFt2w|-qS<6cgP1HXV=B~EeUh9du*bA zX9fA*oZ#oNe4MYxPg|1$cSW{oF@08WcLmSwIgk@xA4wSB7wowUW&E|cN5?)3@&5hM zz3v0eKz`TO$N7I6=iF-Zyil)_4e|avbKKkR!cjqfYr5Ll%axe}WRicExybVx_#cD% zes)QO_fKgZ9Oq=YI&BKHw|yzUbGzS=&e=X5&YMuyZ#N|du%3sli}RPwNr5suq00D!KPc2 zgVa{ony8a)i9yDAzdz`gd7(WNv>WrkjC9x*H*lTpTLRj*B}r$q8Sm)-fd|>08DfC@ z@hIDe#2@H9=n8&c#Pyeucj4ybz-Alc{pDamg8u93`Hqwz*`BO3craPl{J|t$N0iNL zOHyDM($L}Da#eIM*`0;XU59^)B64NA$Z-de86-<2&d#W!wDdRw=Ni3;?sFXX&*53V z%icgy)kS^O5&z>4GV5QGGo|_c7ys{BmNz7mImByER8@OiLbooj!8_uvRoPppYHM`T zZ0mZ)hkG=)I;U%G`=gtc#Y=Xk^;b4BFz8K@&Q4s@zovK)ZDxRXS|RPC=BD-EKp(w2 z+NYBOxudXeFOBq8CI@s;3c9-7Ozz;YrbUDL4cj+ik#7ITf(zs;o=mnkGyN>GP0Jfo z0=lYcLY7^8bXHOHUkSowNmZetS#S}uItABNI8N& z9e8F;FOQwUt!-x~wzDrD?&Vs1f0WNdxPJn&LV;X43z?9rEFCT;hDfB6KO5*&oBHNr zX>2$Gvf?IrWBTZ`-$D*=fbRJ`uATkWaHke$yqa5o>uK9$>w?f8UbugR&p}QdOKtDq zme$d+lGIB9fg6s{6`w%1Qid`84`SQ4oZr^M>?_C{%2bx+2HOB~?h52;ZDPY&oR2eZTfl1B>*GqI^>JRI^$trNWWR8k3kQW)>A^j+f)3zOfTxu z@eS%l$ivqcg?Twbzr4UQpv>#6(R>z$27}OrQ&Cec_0TSCmC^oPB)Uz@;Va5`-9W_b3++`q(uc-J+q%2N7*rTw)o)19eh(YDdQp$Uo%TQd7VWxcE7@Q`?eQ z=T@&y^!2bG<>e;%U0npvMI-&ItQ)4^v2i2a%plLpNaxJTC?D^x&6+%n@~iEl@|zg= zLsZ)q1p~UZ{|(P5+f#z(HmzqI2cE70Z+z|y^6Y_g32bdE=sWHU!+V}$`nbNKL*qt4 zGj-jF#osq3`1eG64#Yb*nKlVB_dpk$#@zHy@ob3=Nto!Ex|*_j$-w=*qB zoKgR6EE+buFUa!~U_yByK45F-rk35HOMYQm#a^y!hkChoM83Pg&zGp5!)Tj1ooyO_ zt>Ix!jPGcl7EK>siMr!ENdL(GtT599*;XG_p$ zuc1C4YF@wIc(*3zvs;;&z8GNN>@xUKF@>}b0%vohom>6Nv`HxUs$fS4d95H;T0n3q z&#V;G?{u~m_@@s1JjumA8f9IBJg=i4DFxlyE3nN7&`(5VGJjVERdN0(Wx$ox0fVoKP;V}*g znPl*2YS1ldYd`)oX<(^cldQAC^D9ADnFi&9M*K}84soAE8|BMQNdaRX&I~o(kshKY zSLG3QjG&$y0%ue3?s3p*&;`&Hkk$__B!f!G2+x$#aTsRjKv%?4Zis`FAPp^D+F_0$9jJV~hr!YR7ds5a{y+0qU|m`i zTLUr2a6Je*)6ZPQ&Qahfz~7wG+jaiGeUs88n;M8U?AdJOdl>fN@2*xB7g{wm{mjzP z@G;m0Q(-s8;^>IMy+PS4e~TBmFQhxt!kE||=@vCKkT;-DuEsjsE0mvQV|Zm)Pd5ej zsM6D}={09dv!nJ6O}t&3pl0syk2ZEY*f$vRbU<0BnHd;-3LSX^V|eOURkl*bLjI}e zn(WnVWcV6hht7BE(X95hkcGVIi9p_A7Dk5Oz@{zf*TqGF@1k6;LoQ!K{^xLneG+g9 zq>V#WerT`v>D=xS*k_IH8=?fY__GC)tPRDYv9ZyUu*H-$vA$*YXQ}Q+o@ZgZtSRW+ zu3%EHPHD@+d&X=@3>W~N`~>(oS6U|lv{P?D*Ud-P^^sPMIEaOBY1v`O?~6>uvsHfFB@1V)PL}eTnu+?QOL~U zCaRW)rl>GWL$S85Z~WpYjC@kFez~AP%jT@VkAA$R?(0lZ6G5Wwl@+3r?O#}4f4mBwKaS)U$Y;Tz56@t zTpzxJ{(X(tS*Nh!POp#g8*O7!Zy?}$q>Y8yFC3fl9(Z~t)~UI5h@;(8tS6=edt$8e z{5olFQtv_7)z>j*`_8(a(GyJ#r2~8D*3~`5t`r|;!@b?;kNGqI?C_q}#zxVT{JYS1 z$m>5KpA@vk=;jSfF2Mh!j)}GQLO*!U)v}?7Z%dow7?-K(z>XQp>ctrEE&8bzN2ufI z%gZ8tMk~9=8tfk!s`^Usj6uJyE?aIXe|ESx^M@VDKF{(!y*aXHLyQsDjP6y6QC=^r zFOHA)+n5@sX1F=gzocG&C?%)>X}iGA{fW=9eC!{<&Gh@iedS1szwm+Wu(MKK9VfBR zm!)?R^?ytLc=zq6@*)~d_UjxEY|;kQo>j4mvCv%f#aS9}y#GjANG_T=rgvxOtEeCC z`Rnj$rEZ8<-Id5)B*o21;GaxKz1&dS8NSh7=^;KSe-z4pmGuvt{ha3Ndt>DP z3dba6JY3lnAF$fJiFrK61s`%uQr3<)A5INQZER#Xeyp#DexE+`P-;*&)R&q!|HC*^ zx;1Ok6Mo@ytUI*B74(JV)(uTsA-#{89@`ZD%6EW0k4M29$}kmaY=f_IRa29COChsQ z!XJP!OgV;+&kObpaxibO591-V-pMb@_u20Bkmh?b{)XS}@)h%E*)T@= zsC6ULZumVMzb~?0*gnAB#|;e)RzhUWhJKn2JigPRiTO?HLJdE7_G^rz2ScW8>}G3q z75-+eu85)!SmM{_eL@Q{mpKeG;5NBYEPNx1)?ASb(x&=cPR^YA5=>drEp z@ct!?H#1g8`^z`;Bg^yW2{0j#W`r~Z|5GszrG8cU=GYs4h{u~48Ma;>-75|Eze4{x z3x0kAeTVVOm!Qw!mw&s9t<|&5OzWTT3%#JqtUu+6+DA4f>6Sm19cKCn%39gqj+9_| zBrUie@-74qr%HLCPt50LQ0wL&!N2!p`zGdZcW&0?b&sZvpMbA_8+?CDTpKrwGl zCEU9{=}#G6-sWZHe;i{#n=Prqm1SLiZGT1t+9ug>b3$M;c=(p@vaTs-IWF$orJVv9 zqa?O(txzWg*xQzP*;(I&k4J&uNPV^}KfU||KirlQ?7AT#P~I%2rE~kgeOZwb=h{-x z?*q_gAC=aF>W@kzod<*pA()gZoI5+wB8SCABb9lN;?&TrK z#{+4+P%+(VesipR402=9gDJYMhla%%?Ctv}-u1G%?dguVAmop^h~yv#q_-3FBZ&SQ z;`1s<8ON(=1Fok*1ag zk`0k|7-QPs%?u3`(C;|^68^2n;jx+prJ0Q9I=YBhAQHgf&(XoZUntZmV~z}Ljl>>& z7lZFNQOQ>=jExk}=2n>3=%~OSqa?L=P=B{FYoJ&d8C^wrPJ&*=^Q%yvjyV3Sf9}Z5 z3;M_wc>^QU;BWX8eBZFHUr+J3w^hdZc2@0j`b}WBsQwe!OA5xF3hm}R4`(HywXNdZ zxRC-o{)mmCK})MD=7;ODV9nv8>R=%Cfo(5fVFfVp9qO?Jeg-A7ZA)ba=C*0)!oJe% zyjzYO)AJt7MVpx8M9xw6YH70(d^T!Qy$kG)ibKotQQ!nI1b*-#Z3oY%8W9Gr zr25J@b}#dv+`=>Ky((f^Sr(N0YM17XCnFnU_>JoPkG`iCBF@AK`y|Fs3Vap7gSUzg z)}=m=7zf+{aeR6e$LnRkOK`m9Iqe;mdxW?9Z;s{-vo36(q4|31q#QnQfIVs<2Efal zrY7|j%z3MRan>90pz)v`F_t@r@#@@1(t@J*WroJWzY+)kO!CgO;9T^D^?2?(l;baD zVOtb*arxG^e!YM$Ru;lKtBXH#@DHD}2QYIy*2SLl@8x4Tjb|Rq>h{FaNS~pQKlhXI(BVr)@pHhcg*Vtj>kK1p{$p+HX;A?C&Q2UChDqPYZGMrH~(4# zG~OM=sF8uJXaU*Bxq*sUQF+IFHhh*J%?ay~VS+GBQv`e`O;^) zCEv}-2Yl}V|0RBq|MXq4Pm&iNOZ5PJ=r|`%5NmQB{*B?v+iT_dVbKln+%90Lyl(i+ zhPZ$u`?4df_hv@iQ5lFn$oUY+$+OfqM!X_|0bq+F5r2#-F4-4TYbLes=P$ zJxgqmHw`1a&iix2ypAD$%Wa%5+aM?y>7CKu;Suj#+L06-`Cvj&t@*dd8;&Ao;A@Wy zWF>uLWxiIGFXZ(T{J;a?kLrj%`n;K;p%M$9D1E2;ZG`6-zpw}KIcve=b12(6tsEP` zY@ZzxU;*6{yLkgNh%dTeT^wtS{24*prObd7%K4{GE#+T|26ykE&;L1;Ei|=%j6esiY6NW2e@?wby{FZ!UVZ%b zRPRVHv`tVO%SIQqc(Ahjr}t)tc8zv*67<`dX%S<)GlKnrL4DTMb0^I2L+-~; z59(Gv&s81&Nbf5Aq45htx=Uu87Hv?U@Xh`yODpmZ{C~AE#=AZG$L`z;G8$z*c_=TU z9WZW*wE}OdahetMC(8fzf$UI+eU-#!H#NpYv!Ov}*oCJmwEMM(Q*;UJzZL7BeFeVO z0ShC%SqDalOGNx#<@|4m^FO&gIVih*ePhSJV867_9S64TQMQg4x1C%Z!TGX3(_&ue zD=)q|AkO@q!uY?`KiZ6cwtc(0e5~QFtj!Q(8JLjd-iG`S(Z@fZ8{_NoINzHZ)J+g0 z*$Z`ihIwgqkKb(fFL$N{$6(Cm4tco4$vLT$_LT` z9~+e)CmDP`D^_Jz|G$orx}sgb)XK->w7_m9m}h$kuiG3Q zmSPIsH3IFo3-{h4Cg7)kW0uY{9x`PFc>f0cDdCtO(b6cZBl7y{m&M7JcxG}0#j zpz0g6_1Gyv9EJb&FP#ZjA%ieC_!QoNnl={XvzB$rd`Zk_yl>|cm&T2@1G`R$v1yXg zvZ>XI;BIyg#P;eE*t+=u$dhN0{B=9X@oe|Ds=cJIKTy}7Jd_gRZ*3qUc=mJ5S^u@~ z#s^%*m?8lf`YZEgSHgjjZZ?fa=*uA16Xyy$H*35HkJ;=@4R%LMmi24o z|1Plj66)~@oF9aL=rnaU=A&R|b|(h@)P1anc>j0SM*9Tdoxyniq_s&sWyW7)F(!>%dH z+C)LTseAV&H)fXM~%)J0py& zR{1x{yQ^$Z)eQt+oCmul-?BQs+TPr3HWDy>JU841&piOTR`FT>R&`m0G;saqmSo-G z?k$^6g&sRboz3}9mbKiLsPGPA8ZKeJuV8wNhbSI}Rr1ao`*Oksb90T5)_r)7>w#`9 z1L_y?!wT$-b(V z73o&Yi@#xa-`tcOwDhHZv8GRCMctNn?dB8t5%OYTyve47z@ezy@;GuWec~AL;lE?N zI|XA--_DKA{SYg^2DZ~%$nQy%1B34o3w8{ccn>n?6=3=>uI~kH#`#jnm$|t2A!0NXtT9l>r`7Tf+jeJq zaK{Hy|BQ*z=drr;&+@WF`TR@4xGn_x{}a}SR!77(+D73z5XLMqMkN^bI>+)Hv%5F7 zRQ;Xy7z5BRL0>8Hk_GwC_w;3vmZtvkf%w25cBF=cjYgc8vU^o^tX6sdo8h4n)v>c| zOVV{k49&~>a-aE8UcjbR^%!~5Y!>p!cuQN?HUf3tlt9=g0KHE{0hMad>d z`u)|;`8!3~0NuAIE7TVGR4Fs{`Kjr7nT>ZAeQNKv)S#5z`5|VzZlkCEP8qf(2g|O^ zaC5B7oDGcpQBxi(dk%b;z>jwv{dgr}%6yOHL^RkOSG9ffclg$S_fT%MJewb90!$1C zUta2xje^8ic^fJbv|K{H_)Diunj4@_y zxi2fsb#GRv7si<(7|SQY_tPK8(Xe-Cqi?SQpAUl$p}bsQ@;Kt_a`)zh*eVarmQP1y z{F`joI_jj)b?$yBGt^w$1s%egWt2xdr zy{N+)=0g);}^*fxk#$`tw)ZZ13MsSx}KBc6z9#C2H6kb>Vsy=o=@*EI4 z!U0kqrxl8YL2xFJv_Dq3O%?Q*$eA5H3%tgNOc&9P1CxKEy zu@B0e3nPviv;n(R*ly$_BNU%iKXWLO^A!JlC^jAyDsAv%76lJ}p+cZ6JPk&WyyWI(P_|89ppBOYU?H|g7 zOpp%tOe^d~2hno@B1pwTMch!v5G%ZoSIUWa;dOm%@m*f$vn0wW?0uuyiG1OV?bp3T z2Ki$9?Qn#s(@rBF#16}P&;F?#Lw5syu!V@)MyY7fcu*LY1wcM*1~y6z(Z1AG`Z(Y@ zaYHl8ZpRFJRJ??N#X;y zl=>jzL?1i+jV3I|jU8~)*P|`>0Z|V3j#l<(M_|ot0QMT}t`v52QNpmcxud0-ng-L< zC#J8THnC$0(qt5I#k`>NZouaHz}ZM1$TE^vBap~w&?L}={{qcGo`XQpzyB}CUqL8$ zdk}RL$NlY_Nu-mZyh~YtOKO`{h8yiZpXmTh6k$&$>;t4clOLn(Obb!J)xfpa+f#xS z?D?TQ1w0+fi^6v+f|aG%7iYA$o05+23w6gn9Zl;Q{YB>yFT{{OrYN&C8O3~UMcy9{ zVa1Opvi}GF7l`$z+5Jv9|Nk<64@Ft2FFA&G#j-VTk$$i}zF~jICAIHYh8yY?-bb@3 zU7K1c<9l^h_Gg7FNBTsoSmB-nTw`64xaOMIxL3iW8KKG(xsmESd+{#!NtvnZqU1w% zQRlU7WUA)Rd^BQylt=n_;yrAn^@sz6*MIX5^`~N}4UYdG@WcL+0)k!I$Q^q%0VikK z-`PfNAAQ_t=e&nkRDHsI&*owuB4Xf4$Q14YQWb70;z<8D_e{Y4Dv(>j3ih2;WfaqV zC{>5=ZFoVZbyO0ZTPYq^=87fu9AWj^-nf#OB|IQc# ztJ8Yf3#0oB{^{a9>@;I-j>*pHpEjI z)}w>si2Y)zcSG!?YrS14*>2E|e}N9esWHBEJ|-O5{a=wk(MFJ`00G9lsxJ<6SHUgu z*&W&sxKyLDlvBe7N|7gRVx97IpBUv4v>(4;rr9wyZU4G{j``Cr$M*~s+D@+Ff$ zN3k(BRj~QM_n!XJ*i8qU4$jVQ9iUqvX#WZ7>MGLN9dm~;-2I`${`o6;afAKa?#Q;OYur?o9`~Q74aU9ufFA`a z<%MDtgmbpkDXf!!f@l*^t_L8)p6HzaoADEka>Rl-28W(9?QS4rtPJE7WjgKR1|WSs zrJeI0UQx%yes}NR1?*{y&qfu)^#7!>9Lu7-73#hJ2$Ka-VxWT^kN~Ws=zMJ4%Y7@hbU|Z;YpH=;> zwa@>CBkgSN7ymTmke{QSilKa{uvw^|VIcac>yltL%K%Up=r8ZPM1SRgpPEuOo-GDp zSJmM-s5(f~OB_d$_`5!c_elk4BjqN(bE`at zvA;tT?g%CqP@j%{a6&oFI;#uP+K6LXPvFO=<=r8FYE!m+@ZJ~xKbZbM(YL3M9iG$n z!6PbRZ+K-?FE`l4A*xMGf8D>Sqy9~N(w?SoY76>=NquAXtB+7u23kBwUD?txs9cU! zy5B|kVIQdp!B?iv+4WR+=lfIEQ07p7;kJq&+Qd`+JHxjduKITIt=~Gsjk-^G9KP8d z;ioLc_oJ49mPg`9!ue7hS4R6O>tg(rRnfli|KX?-&wUkq(;EA_|M~6k>SRl8?h`{C z11!XT&8!QEhuy>t>}u%_>hU)TE<^DK5kp>g5q@&PPfdt=7q(Ax*fhUW&X|KV`v>0* zQ$nz}_`X_t-T+A-r$V$O`!n_Pjs#a9YT5_-MWd{q%5Z@M(O%;WvEq=J%~Bx*JR2S0?UM{xDZx8=Tzk zS=vLK%PsEis$vM(nMHe%?H&zc+w)jk;(NrRuI$|&cM*QFAv~%h#4Jb=sB`I-ovPl_ zuoLFgoZrF#Zeo;ui&PqCZ^6>80Mp_gX}p!u}{LuvFS3z z(^4kbw~#HtU1f7n7g^=!COad#%iH-8@^o&Xd^9mb#m~vHDRO3fs$9Ph*qSp)ZqDf| zSLXJS%j0t8kEQ+O*r=44{k_9pT$|{tek)*Ql<(h`MI2A=K>Wxd%*l3b(Nx7yC{!56 z6o2avGN0oXbw2TK!cPkr(6=KS7|b5@Y1EUgU|+=!+{|jdOoE+5onM!FN?$*0Mt@!u z))Vm;{=X^@jOloyPbBR}i>7ejyNI-M14Fb6*r&PHCkPaY?{W3V7d68{k(LdmpS8Kn zM1bS$F70G|2YXr5qrH5R9xA`g&l6?+82MpahWs!o!|B6AJx>Z=JWLR=s?tccN2bGeK2oR1$D&rZfKY` zUeevbsMOw}-Gd@1p0JajL?5GWsZHJ1Akz(#=g} ziF*gRe@F-U&*XGb7LJv3^ZFRSofX=9UryAAYvX*Ch4}99YS`b5_oxZq`p=ZkaYQPs z)j19Wr3JoCGY4{f6sQk)=wAyez_SBE5g_8|Zo*G>QXs z7z4f47T*Dde3}m$2wa5)V@(*kgh?;FL@c#+fL-BiEW;XD$#a43a${<~oSQaCz8aj= z<%K>m+qWkK-&h*Xv7I;MEbO{k*h!Q_oa2~|?`4ul)Z@nXVDe7>&>z%z2Fk3v2|q)? zPi0h$>%ZjzdcWz)+~G2}V_V7&Gq~$N zV%}!AA!3Kvf~}w)i7CEgSayx?0#`~ik>7x=4_rm|J6OolmKHJ$IExL%_uZB|JID{C zvgO8{A#!6(^9H zs$Aqb<>T$h4fiQWiJeH$-GrYGV5l;3N`b6|(*kYx3|Lm}9<~SDf^m8W5W82Ga}UJM z(g+{;cKnqu=VfJ_-%=oZ?Au8@5tuvj74y}wLtkYB5cwhz-;hmziCxy7*N(&%pCi7Q z9^Yk|c%A1MBi#@GmSi?=Am0qj7G=sfd4EBqe7z_|E>9UO$3~_GKA98oEPY8e^-pSZ z9M3=w1>n2a%qI&_7z+k68^b5k1Oqf2Z%YuU`Cu{}FGqmxCj7KXgnd(iobbB?oI)*p zC4qI#e$6&x*`x4{P}W6F>rdY%sO+3^HJi|%=SAS#-7!8FuFVzh9kajhkX8sx0JRG)*+*9 zp!LD$VcoKhSHSN^KTloA9@S1p-7X0CQTD|5 z`b1fr-&9#NN>;aZqTd!nKDogBi4V3pkHj;t#UVzS_B+4Jx;(O{G8tn*eA84(@93aJ z;2YLmnl?sEd;`Uzk{D*buMyMJu*GYTw=bjn2*iT4lKQfAXji%TV1GF~roWt?oGHrM zBKdhrM&Js_;B~Zzfd85h?P`8F3hrb81rxx{ARozlwt;HB?Zg3Cfg~7@DmBoef10k_?*nmtgZb-YN7Ff(9UaxRUhpxiF@cYyhpOwoh$pVsIfQs3d^=QmC`E^P4j*Nv z8gtrF>D{S~imjf_trTZ`V;A4vR{I3=(I=>jLfG5<9yvd~B-?}WEz9*Y<>r>Ta&6O0 zQ663-&kV}hvk2b{u8kc;3^BHPA;y)=uZzfn!8pme4RL<5H>e*d1a!B_p8zO*ee%Nq zIJ21;I3R`)?3U{-OT@B25bvkPnEzefMV++@*jbFWeZMGWzOrw=e3cU>DQK`}HWEP_ zmwD@BhrdY!@!ea_4U%&kUIS%;h3$YdrePj{*547=A|3U{G^$8+}IzaUq5kFy|yA40h0B((JaBepo z)LH#zdbzy-wv}A0lVZD3mscfIYI2=&=YHtG#b~$BhNbsW9-k*~=BR!_)>Rty3FS;> z*ctBas=U}QPF>G+CwvfFs=Wms7|YC<0Q&h3LT)i0mFrhuM*72px+(B2ZKhn#1I2=J zAc*$9WG0V%VIrGe6WMdDk?b8Gv~^W4ow_EZCS_Og10JZuwk1a>OTygNKU>gRb`Q3d zhl_3Gk)md|&|@QQ<)I?@bH50neD3p#F$@mJ^e zD@(7cYvg|od|cGE%ECc0kDs!Y`ygFcR6^@+8p@CshN|s+H_9K%MG|H82oT1v({NDR zj%~>E{4OrAd+uEJXm;{^e2;opQt+|ww&&R?JI2X4dmGgcqJvwO`B3&$#EucJO@sgK z+5Fft?9|L&t*Y&+yUwlRB?6_u)G3KHE(E5IB*qK~EPFb&&y*5W;XC3CZ7bD*JKx)w8>r2KNTzH~Dw)JYG zwu`OEeXs+{=VkTf4!%*ZEQYPgwLHx8@7i58<(s;ZF{$}I-PnJ|(FUOn#J{5Mt{8*O z1%Afh8>JKFj|n4NZ-~)dT@~A_*7ZXS&4oOEF+S~iW%zheibsf2R7OLFi!yz-e3+E3 z&X?V-_(A(gGmrxaVI_;}m0^ctT3k{_KyMs@U2rG%26fF!#M;b9|9rP7@qT6TNcnxn zP?_p#sm=qsz+cBa=%1-5cN*8LudK)16aD{pl6Ot9Qq^-0LjLT|iBPlc{>mBBP&P3zt~%p1%u?3;)n%v5&Lu}^JMoU&h62(;15oWOp~vNr^r{sO6i;7 zsd9csZ<*n2r`qte!|q1;LtGbvP&aL1f7~!ZTheyLC8fQUCB`mc>R6?!damj^v9kqR5?I>!t*gc z9#~^j`~JTf^VLhjz0vl)uKzN05R~5(`D|#I_&6C$7?fitk$p^`GLCYGL>%#2E3*cO zBMww;%R9l2YFA)>VHxIMSB3d0D{#IV?MmN^e!JD=oMUaq#l>JPHFQ`q+mSNJ6|^$| z*qJ>;lp#f;OdTsnM`TW202x%1boHNEg?QWp>AGh>%?pzEb95>l?Hw$~2Sv%)mQB?0 z7G>Yvik}hSEe%VGXs7DSp02h{l*i#G*#bGPU*4LWFMwZUTVl|O^OO5JDytXCcZC_M zjpc5Kxe`-#gGu)#esl-nT%K>W#?p3wvH8L%Q)CX z!yE3-3_7_S^Ds53Bkh^xi0^p0Z_JawjLea*iqhp6=)&XzdAwIob<9WqViAZ(=1uBp z4+krZjc}Z|0sS}kZ^0ZB#}#{^yHq(qTLhPs```oNz9u!bJvjbgo6iAu4&;Tu^!2hd zV`bwcIhvOO>=>%?UCj;U4W><5qsbl3&hlv9MBSr+b0f9I4)flC{e0%b*4~+1k`FuGe@hE3DSop`FDTuLYRTe0o6Qu1m!UuxSU%p#opoI>JL< ziVK&$ae?xDuAh9A9U|Z4hRWD>wqU0;4&_0}CerQNnV5JmFG4v!W>DP8!TGRpbLE#2 zePvImpY(2FD}x`y5Bz>@^1inqD)}>A{%8E#!HXGcC3w`ZYlJ_Q~jdeRz_#a<*}La`_6n>AKF7& zAi*e*k4P_KheVvB%cyw;wrU1B7oqII`n%f79b#t%>VFyXJ~||2nzH3SjES;jPvC&ixDw%-szQ+O>qwfaPq4`PD5LLvt?VcKD%uTZ*`!?HPWb-<&i; zN`_9v*m|zKg0Z!8Y1z~^!n{jyuyae*k6o2cte6kifxOf&dgIwinUFuJ^4pXg`DAE< zMAkD_Wj1vb1a7}OEOk|BuoX$fj&AfICHV-x0`3j+~O1nd~mFGK$fbsmr2 zPQVW)_q4yd*grf1tfAcHF)+cHyFLQHXvK0?_nT|5p6e6&(ytS<De|w^gZs zFcJ&{KRzrgajIb_9&_lNGk6qh3~P&>jgWQALVT6A;r`0!MN!GhLsRAYs;RQLrIp&2 z*w5dhr-6jIAzljsL|cL5_fXH5nYWg|eFGD59Pytd9#(tGvND~C1;BV zVZy~g?c=#9V{61Z;-D-;oEi7mtBDL+4twpzzL6`>j8Bu}Ba`GT@c+?(C`mE|^GJ*s z$N~YU`%-_lYao2GA>KARE*@WFf{Pk&cyrTM^@?Feb|W>Hz93( z@|8N1e2@nl_z5_Vi}hiD=`WNY`Z9$1GLH4JT-_I^X2Lf!KU>}!7^eCnkRSI|F~B(g zJEj|o{UM%!{I1yN^wZPsEyo=43;D5&PL0izZ-!^erTYqHe~h2HR)=yY6_f!2{*MtO z;3&#NMQg4O8QM zQD0gaYjP~bPsD3y#%IWnW76c@3t;!oNfR z9LMyWFL*8?{>@{nlNww4<)_B0(q;iJ>1%bSjvyw?4GooW=m;M>E7(DXyPo*?d@lc&*hPR%Kn_-y(g!{%gtpuve>`9I<{mRw-b3tOWy-As+@z>{7c&V z6_4ycE7OCO&yxbjT=(rJzv+5lbEE)S*2-3mr>D(JyQ-VWY%T2`@N+ztiL~LDdkA&7 zA9|8w^zg$hyrQBHpn`~8duaXm! zdWS1mTcgZHjB7h5X=??>n~F(Wkylwq)E}DM;gQ#9^SCx{q=Y)yD%pr(8t>}j(1@hg3e)9dYfl}DoM%7<&NH-JYf;Dd}3n>LQ2GiE(Ps@jURZy-o zmWf21t%lEZK(`LcwC)`aof(?qsmvT~_4C{TvT|YTI>8V7PFU%35@i(Xmg9DP-fDWz z@z>LRBHuhSF;jjVmoDd~)(w6ZKwmwa5%S&H;aQH#3ix@a7RcH*j_RDEu8Fj6*ho(3 z>njaEtm{Uw52j;(IhKh&0?wx$fNz`euJrk6wzz&7wYJc|rk&pp`{%8~*i{#1=E{jl z8Isr3LY)so{dK0i)9kyEm~*D@ReMi6R!p1tVGQrlqJ;jxK=1#6di!N^wiF`@A`}H6 zc0h*B@eL}Lhs618#_2LGEj{hL;*ouiF}~c-Vju9d2D*A%j94W9^%wn14 z+)^Ezz^-fq{dApms@XTh4rLIpwSq0M26O-14}<&9->wWbE$<50UvCbGnWNm7FCP}f zsOwk*k(mc>5TwJU$?=SBQ*Pmzy`5^{*2v%t>h!;{3lp^3m= zR_!`ROqbAhX4|H*d%` zFO@(3%Ev`Y#L_TLuC<20lQs_Zls*>q@uPpeVma1O zU!qPZG#pht*Z5e9am#_UpwF(2?;~esVBecMkUueC zEFFYCG@>c?ir~KM)%o)^{`8M!_Rl+YY5y!o{Ls5Y(uZD|RV;tZE|%lt@+G%{i5idB z4?Ks$J)*T6>o>#INjVICrTVk$7(2xC2EpnjolArHfP zx~lsERED35*Gln2`4by*WUs9vAt zCCKUGTv_AOnZ6`RM56Jg(f}gv7Z|51ez?av*NRl+jlZkPLwlC@mxTH&Z;cF|d3jx^ z{Jc1%&hW!=BKmS0wHLTnH)milw(l5(-p$^!sdonEJE9DQ{;H!~o0q3UXNtB(nYU5&T zv{kcD+^qk!ee~N?|6EIt$2IWR(wF*SpTzD*bCcynVXn+@a}vzkNlIh*w9TX|Z9BG0 zLtt|eu(`VeHuclH-8ttJRzw6SUyhH+Dp?sJ=NE?88Gfk4U`NCJl#|(A+Wp19a67)V zdu8}}zpm_Orpx^>pU4e6cyTJmwc}H;aYH{jl;o}YMkq6>cj0qRSqQs!uezT@^)`pb zZ*7m%HI!>#56%igYV^S!?TQq(@&$O=WBug%}M^s$)U+zFAUF;(?hZ6Q61Pn zk*EU_+XMaRg#gU0+{qYB!%tn=&#V`9z92XJ{hy(?7+X%6_IiGVYX8tapzj>>vKHAL z9M0~iT~G(>s;g>`b+G~JSniiux;DSf^OIDI`kdQy*B|!{^`h-lbJ@+fC4TGUyHTkw zKSOSxLJV0kZvgseNZkK6tCc*;wM|Roulta z_ONVR%Yc}GH_jub@8{_S^3$}s!4K=7L|x#5Eu*X(ny7mM)MN~%U+2r6KqOZ`A-ILaW{ zoAKjx-PF0Dy2OoEz6DtKxGgd8{b25a~_A+aQM{uGcm#$j=e zc0cVzyu_XOuCy}fA2lD#LhLQ`NJ7AeXJ+&uVFRmr8>7NAD(D$p^{S z@>xbR>1$n2ZRgwJr?RxPYwKXI@_Q?o_w!Sx^g$=gQT@>8A?JHr)RSJQYuavzZ;;&P zRw+B-GkgFsLfZRxdStxn+62T@2SBFwj()0SHunGBFiPGkP8N5>!_rm+eq0-(ZQ0-O zQyGN49V*UiR+qnO|D@_pe>*0r;f?uyMs40$^@THU|n zR8gk9kRSIf*GX&d-cGh@&s02ey*0Z_hRycFS0giwuFe@K`xAZD?;Q}I9ZGj4Q}Gjo*n!IC zV=Jat)$jFD>NluM4sP#ezyEP}*}uy}rMkdR6y%OZ$vCek@=_pTM>vkqWO!A4>0hTT zemFhk<4+M=^*Q9kw^Q?_e@h#6EPAK%hv`3oxqwKQ)(V!g^kThYysqkx@y)D7*^ISU z+p%tZ4dQ_w7%88POe*HOMGZIFQJ>G-om2O%#2o1y=F@iomU3@K=$FSfhRX-r0_DR^K`PY+eyF=NN&$YXMILC6z2CSm zPHk)n`iilR`{Q5WJN$KArd*hkC%xe-ZjTD)Tn=Kq{8-mkwT%DaJ_a%P{xRMyNrO!= z7$=OW!nj@T_jSQuV8wEIQI1?+mWwsN+4fr#1Fv&jqhGf*!4b>KF(lV=eAPduzp`Pn zyg4CMLYt`eImf6@j%ZV4TOQxo2&jBWNvTF@uokBF1+N5;wTQ_^LkOG~xiW}%WHI~>rhuG9y9R@EYYjP*7GeigP8 zd}%jTPeSt|iqq6dk&t-KJ|H%EG1z+M_2SLY3pCAL=TzH#=(5`i(+ zsj_^_V;hMbP4;kn&2Mqm#_z*6X1NxG_T+cw-#9-lPp-`BFJqmWs%w(5bdaAMQOnyA z@gR)#y;IwnKGijtV|#C4T<;%>Qsmg6#B!=@{J?&ekoslwVAvCo>(DF1b7j{yKwdqP z82HsDUoR={?kOX@d&mf1Zy924BNHslAk@rdqD2E4)zZ4#YP8$d8pa%R-he*ZXRwb! zLOXl4PBG@W3Ji57hKNmmi{SlY%;)Tbe12?(T$?pmF3ssJzZ9p)H~r&U9UYY#{?3qu z;V%`$Zg?^`;+0()A-~c;N;`RGP*>$}|AcRql|^=6^!Aq=V|Bf(jf2Qx;zqTPanxiE zaRXO}$_MYKwr{P*iq%Hf^Vt;`k1vI-_QUv`Oy!AL^25Bo<*~5v4{T-Hq=+_{52%Gt zyJFjLE%gJ48~AQip4aa)3b2Q4-*T!8{6qmW8pS~V#MM__5I<&d9;_BJnQO*oVIC7@ zUH~fpbLnm=7pG>+xe4j=)3_8lKOs%_qz8Y;eQzq3wW{C55cjX5Effpe9*J|@dVeR-{mP3b&XP31D>!vm0&%`pFgs!+lZ6FdZs!ObU~mssL^Z_q8XhROB0 z{bXNu*oSjMdemxsM?4Wj+%tnQRACOy)qK#-7oy=Vi9}XWpm2~5M499Z<DTv7vm1--DBekm$~PXCkY0zdllhv)PGwMDBp-E4CqRAKtS=pO}caDDI`ly`*b{8}dx-EP%e+o*3{uVl>=;95zrk_X-m5Oib4Y zwx4D`lyS_L`SW*|W{t70L};!1nLd{8$GGO5At|GOgY9!-LhoCsF7Tt-KH8D`%j*v3 z!C0@fQRbBCG1fu%4iXXF6=!JNCCS}ji_F2=Kdc>{U0}K^Dd^q#A!XmQK|Sl2`*!EV z5a)uZKe&Gf^P%2hd-F)ru2HUNr0FGVT*K0_n|Qv2?`UntcB8H$miT@>5ba~`HN`z3 z)qRGL_w9JAc#qiOw=y1#^*g0(p5&xFFhSlb7LvNvf0zZhElwc#(?RQ7>U@R-gN)z!t3f2MJWH9>t zEc|eQP?);f7wi9KVIFk2e7!nDb`1$Vyd>C1-5;zb-vZF;l33*$8T$2mwX{+5qRpU> z1?J6m(I@tC;)0l}*k*j5Jd?U&4e1KlD$n8DOZ<*tWihc8+pFU5&D_TiWBaX%zTYXE zCfokHs7UggHdb{O*b-=1?0iy0r(zDEEJ0@hcj-j zpKuS9&(~p{^O}D0PJYrubHckT>+ya~^qH0iV@m0Fc>;b0Z@bcOjZ&Uaez0vw#E8~5 zRUO%8+IXiazTZ9!-%mb-eIGf8%(zc2znazq%fNRQhICi9MTLH@?44&>f<3K*jEvR2 zZo8DN8zGJ<@5mQhrh)-!k8w9s13s0bz16CqT~U>29Gq z!4E21i{a3Y$tVcQ_Ci3}JeWRDH92S9u@0ZZT8P2j+N*T}opHw76(J8>QA78O%v<0q zm$w$k`Q?~bUpQJ`?Vq-s^G;mzN10!feE3W$hQidg_Gxad=EXL+6N<&YE3{RqgE^OV zCvB%;X942P9xv9t_si-6ljDO2O0KyP=d>Jw=dY+=i4{#&5<|oidFFS6_-zHQmF2ju zw%FCLw?#47TRJW78D-R1xi)H~TpKy!7OD&U#G*hIDH8?5m}Gq$jKTIHCZaa&sqs(0 zCw-=jYtO>E8rDtyhVpHcn^@@}hfI;dkn9ekJczl&wb;)+zpuO<N8 z6Vs-rs35bE*hbc{JNkq7@~g?ovzf1xO88FDoT{-OH@7cP$IuG}As zIfWX;@(@2HwmD)I5l0`ct{>qV60UX8zK!xsezZDf;~0^)H`j<^egpE$Pd%rPMNJFs zuV03mo|j`MxV!RZQR)-QL-%2f<<5RcztjraIn=HC`ieNhZN(JvVdG)zsD6Pvw6!Yo z$^N@e7o@zlz`x%|FL#x<4|x8C>HIdsyP5%DU0Avuj7jJ~e@D z^9~Ami+_L^qW#Y6?Xa$b*k>Q(_~CEyp;z9u@XxNtJkf8n(y(bU_Q{`%*t?2U2l4Y? zOihxC^zp=a`Egzb<|7Bmivy$gE)MlBZ--mzO^@H(T_=X9Tlmdg-?Fh%bL>fidMy2e zL2zFS2TWvR4t*o`!ksuqe#-9?v>^@Ot3~Yb?J!goUi7h}@mAS2?cSXp$=h1Y^UsX* zRNfnwxlzIXbN4lGLf?)K<-0_gL;F}GeSH;ttjBDmtyDs@#i#e}t$^IAK%?fGq zPFk3}g>T+hq`Htl*H;ddiga^zpayFM7-+N7U@aTy!~R80Ht{3c%u z;5XQv67lBAzP;s(>@4{r=dV;3__?sGw^T*ShW_%!}8oYuef)|h~k3$Wk!o#b6xOw=?F z4LeJ)F6P?8p)=25E{$=^7|tFhMyQ`O%kHLLq?sSZIJ08i`a_+a4 z=%t()KGIw1yB1@UrIk}1=&#o!gQY5ZH8e;*O^uLe@*`g2K295epSr*h{VH6CYj19* z)@dO2XXAQg?Qf1J=38fCuc$THcVqTI`DR0CC+ztHy0=mU7Nn3|HjMsfIf5viiKPT$+y5^B>vj)0!RcNU41}${W}Z zZ#VW-e5?wp1N=Pg6ChR56J34fl`tLrGXv$7fr*cB&XemQi1FL?f$Hv24pHZCMR~c0 zCF|BfWDI;D<$u}j9r4XtVhDcstpRdjVWHvS?5I~3g!-sm+{v^C|e1sKe`8U+MF?0J7(Ba>CLJqoc4NUK=?+ z&jr-JQmPC6^@2_(RngPE0_5fJKq*-^1m8=`mv_=b7cIefcNO|wU0{g5j@7Uq8kFtv zh&j#y{ZZLE7!Lf)ZP`q&kI9l>55~&Xjfj(&}cXFS+ z&BflC&ku+{@?&0_JnrTrPkDsPVeCX-75yvtiO|V)d|SJuI33@+%9djjdKWJMez*r_ zU0{gw=!_{fGBQ%@I~#ts$q+=oAjITIDArqz4YZb{h4`+;&KdIklvvpt<-c(`WEj`K zREMFOV1vFAjnr{Ie?!jD=I0tl=EL#uJjmaJLsFD$#VO7cI|4cIWf3Fv;}A2HIn-D1 zOQ>b>u(nhlL<|FCmY7~mc)26&Anir4FCHA6@a@e>MdnKXfpQK0*z5iJ-Io3}{46Sz z8^F&WLsGF8Y8dw735#Ej?*?$q^54Y}`y+kS3(#F{u z{2_j@-^ay4z|XS&a%FrZ_UP^FxiLB5BEP5gcWr9=%s7^v1RpHhg!ZPhfp|sW2jlKz zK#?FfkUwaqlO6WA&&Ib#iqoIUi&qZykHNQFVy_2^bXVQ9!zgeSEVBs(0E6tbO0P%?v z5j)O4!SyTul)j>zqQ8;rOXk96{vtmjTfu&?<(-gBR!9F0{46byGedN8a|zey=+O##1$CT^DAnUvsevz_bpTT_NPK;|FfIoZ**3eAphVR?JM#_cnG10|9 zJZHJMHQE*0E9s#q&x{?BUS1D+yE-1wc{F?`$zPs|^N~X_zH%hSQ@-jQDKAX$m4m6C z@^p;1eB38oei)f5-wsQaPqGs59i2e=pZ2Z91=_fVsa{|$78i@RfT?^m)3l`Sp3QGlTR_0Jpp@b|CyK2^Og9Je-w@!qE%oI^M&Z}oU1%I{@|X7 zZSbk`;J`af4ym???GXdz*67xhzaq2FfE`4AzK8MZn>fD;{CUWnehYbjCCvART6N^^-2eTa<|UxugtRJxS?p&ecINvz^z(Ij+_b_CdXi#?Remz3l3d6 z{xSP6wm$}c>QiBJ7C!h+muAVv)KMEt{f8-i-S%?`GEj^+U&CA_q5o~FZpat&TW7to zpWydz2L6uenmdqVW}SMU$v>{?acs+bct1Iaa0(ndjIsL12ya98dmDJR33A?beWC7# z^J>KXl4)>k%siO+;q!@Sz`i@%9Lkxyf;;@q3SkF&LzLg1FN(8!)>bZ({m&N4flVcH zU<3SxUSPQB{Fxjh%`=jv9%G_f_>25^^28F_tXLCr;rbWm_hg>g=F`40eZGg>ev{$? z>)rh3+jA!8%)tA}9dd}zH91Azc?a^1+#tsICTDo&HM#RM=tWcCdmH-Q&R9z}%f$Q4 zc8B{FD?$f-esWF0FSL!%!4ANB_+Wj!?cpN#U-jfqR*KY>B+0=d_~lrYDccqXWk2eN zH8$A#Z?DXXV@}G&Ik&ePI`ZCa$=ChQx@X(MH5fCHKkeb3_dVWg$wQ#4wvjs6m}4=& zoi#W2<_^b*l+~8R`Dw1W(NWr1=q-dqz~@S6`@*HdpH<0naA}-0txS=9Pr$ZSW=h1W z;K9n~ReR_6tP{!>pGF-`#ds)f!0p-;?4^sn(-(eoZqSB9j&JYtW9@$IQRw@_A80pX z9s7o!-_fpx&kJ(qeEry0agV$+`#18>Xp*34CQAG*o&;>0o30i-{^~p`mbE)n~$}Ch0x)jhq?6Bd;5ZS zx2U}oQT=Yx{L!Wj{7G-E|203 zwsN4o@wraA9Q$4Wk{Y>*>w{V__!Eu2wNcP{X>Yg$`LmP!$xo9{p+j_f(d3(+iuOOa z0(+lWZr^mB$Siw~&sm=AGuDUe{vg=jJq0~Cj$!G;i28)44kGtxP!G}MAZ>ckHUxcS zY@R^d1a@r=cvk|sNGaALxG$N!OTYgPEn{FmE$OaXwQ$&IDTnNjYst(9=Zj{3I{UmH zX+7*eRNFKy@x#YY4!Ck2MsN9O5f@@a|4qUrF@EOwhy9n){^e!NZ+{>{6J_9#nkD%VZ>73&_GWFTJ zYJTONf99_g>*3`=Lz|m|hWC4`sFysoUSxA6Ld7==mo$H71<4mn(&fKS$YB;I!R}<( z7RvW%hp)Z;nRmH%)&E^FEn5Uv|E-|2E+C*HRREul9a=Oum*0pYuTzMDvFX zDFCwUjtJd>KlzFBnKMyN&yJT*^Acpw+)25G7-!I)Q+H~Ix}1-YYb^^Nq`fvh?rCj4 z_I$^$fb+pJ==QFLvzSLZCUhhIoXnjh_0Z+fmQNE`(%6`_@H@P!1LXHzUauHCcUGEg zlW7yJ6n6BM!#3HAss8V3nR#$b1@0s)LV#{~M~3dkpIo%Rd5QAnj2LNHk~XwFbmV6% z(GFeieHq6&l%sR6$!e6x##sA-Hy2IFJpRDlSLzG!-@~H!_d7mxNB+!Dl9Q9MURpUv z8uCJKd2H<9+L8{?0qLwZp;MoWd3iZ_Qw*KcO2~X(N*wpMe`QVxtj-=MHI=DyYykG> zwq-or*0|l3Kd=F&t;vBNQGguJiN0!U%D8uzQ$PPQ7+15crHqgBh*fB79t$0I=GBSg z*4NI7>wD_g(2H9%P4-R?lKrKL(r|Y_i$BKrS&e_#f3f{B_;Vt2q%Kb4?j{^^r-@a(wAjIkG4P ze$LY5lj*@0f6V#&SMevy4|X*ngPoHmCv%eI-5C?s7TJeDpY+=^HzRLoo4*)xoQiR@ zr|-Yx!`Wex$DzAMf4MH`s~w6SFGume#h+wxuRmWPe@@JYUTikn>P*bd%IC_TvqI;t zhR>VT7!!2Y`Mo(GD1%I-)DJ$WLScs_C1}IGnXv;-E`@LE;z+5R9wSHR#>+n1@Qewu z_|sZ`(H8#9N|J_Ykl(}Z_yO1hs+*bYcx2Qt$fCLzht&KV<6-VASOxAp8|l~ha(2Md zn#D=ioq#>zhWQSuTM#96OT(lFdl4F7TiD{ydE{ShWW?BZ zRe&q-+gd$4T559Rq~79>M}O7SA3K-)v|ap}E*}+wKZ|F`?k5~5>-amLclhtz6zAXg zY|N-8))o6S%09X8kUDDgyTQF^)B`M~tO#?OGPGN3$vON+zb?uKJLbydoXNWm8Gj=$ z+#|HgAA3;7+F_$6;Kh$~9Yc>7PLUH+BjnhWNZGzRSZWFrrN-iqzJJGC|G6{x!}bUK zdHc}_`Cnlg#+dO^Ju~9Unuk)a**iD(woj&q4&0j^GiGPbgxI$p9GkvlYRJOv>ERE* zk~;3mKTaI|!nVY~H((QGH~fm$JQMBL1m3CnGWP&hLY`C}0*=AvK`FSWhauqMuT&dX z22QSpjz$Ic3NpUpozcFTZJg?(z(ews{>6V6F}mrcS%IY=X*zPl@BVZgvE`j&|;!FH9U$i#=OSY~#4scpdc7XrD#3V`$IH!5jL_piKO^Ndfz} zCyz-#oaOlLfeP&NSvFEmXE@|wev;HKNs%Kd7JtsUf3QpWgK;%{L+r^2l|%3sT)hDG zgS}Y?a^UaCiMj9W2yxAbkVB}m>VhQMp9TAO_`L?Q{#y7mu7+=b>fDJ^58r076&X2SRAl8U~%AE%K=oa_W|QtXB`%Xt(ad`F1 zTNSWgDo6GfSZ_^p!k4@o!-d>q(_F2wD(Qi&>o3CRA({U^gqw?sUhi5_#YpSf0XstK9LNB6=;gz-%s+w69C-f7kPZWUEjr%`6yu z6>_6;)e9IRDGYV&#*nlR#fcX$}3hzbI#Bkxpdbmu?*Nri0J)Q?cr5Tv# zc`OAVr`S=J?t57ZI!13QS{2h>cn<6#H>RGa;wen{ytA+o-dF+eRTjZL@mgUnrb`S& zGVfIwUf#e|8MrZcRz}uaVmG(Os+3#8mhoI7XFTy5nJ%ixhN8x6tMXGA369%QD;~@T zjUBS%g&|U-AS>QjBTM)C7MS`9n(qOIWYqJTc_Wxyg}QK|2sM;FKA&d&UzN0h2{3h# z*CvLns)I?JnBBnocwxRy9UOvF0n@;%lUO{G5lqa-g$v^oGcjfmhx)`6_9ZU#%Ehtm?#7g-68M8ArZHUvRlR6m~uUYYTAV=glqU&DyUlG2X? zH|EI6uYEamUk~OvJZEJiWs$XoaDA^_+O_)nl9$&kY-Sc3< Date: Sat, 30 Mar 2019 13:40:36 +0100 Subject: [PATCH 210/319] linux and android build fix --- shell/android-studio/reicast/src/main/jni/Android.mk | 2 ++ shell/linux/Makefile | 10 +++++----- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/shell/android-studio/reicast/src/main/jni/Android.mk b/shell/android-studio/reicast/src/main/jni/Android.mk index 357131a41..9bb894c11 100644 --- a/shell/android-studio/reicast/src/main/jni/Android.mk +++ b/shell/android-studio/reicast/src/main/jni/Android.mk @@ -112,6 +112,8 @@ else endif endif +$(LOCAL_SRC_FILES): $(VERSION_HEADER) + # # android has poor support for hardfp calling. # r9b+ is required, and it only works for internal calls diff --git a/shell/linux/Makefile b/shell/linux/Makefile index aa2acba74..306667515 100644 --- a/shell/linux/Makefile +++ b/shell/linux/Makefile @@ -448,27 +448,27 @@ $(EXECUTABLE_STRIPPED): $(EXECUTABLE) cp $< $@ && $(STRIP) $@ $(BUILDDIR)/%.build_obj : $(RZDCY_SRC_DIR)/%.cpp -$(BUILDDIR)/%.build_obj: $(RZDCY_SRC_DIR)/%.cpp $(DEPDIR)/%.d +$(BUILDDIR)/%.build_obj: $(RZDCY_SRC_DIR)/%.cpp $(DEPDIR)/%.d $(VERSION_HEADER) mkdir -p $(dir $@) mkdir -p .dep-$(dir $@) $(CXX) $(EXTRAFLAGS) $(INCS) $(DEPFLAGS) $(CFLAGS) $(MFLAGS) $(CXXFLAGS) $< -o $@ $(POSTCOMPILE) $(BUILDDIR)/%.build_obj : $(RZDCY_SRC_DIR)/%.cc -$(BUILDDIR)/%.build_obj: $(RZDCY_SRC_DIR)/%.cc $(DEPDIR)/%.d +$(BUILDDIR)/%.build_obj: $(RZDCY_SRC_DIR)/%.cc $(DEPDIR)/%.d $(VERSION_HEADER) mkdir -p $(dir $@) mkdir -p .dep-$(dir $@) $(CXX) $(EXTRAFLAGS) $(INCS) $(DEPFLAGS) $(CFLAGS) $(MFLAGS) $(CXXFLAGS) $< -o $@ $(POSTCOMPILE) $(BUILDDIR)/%.build_obj : $(RZDCY_SRC_DIR)/%.c -$(BUILDDIR)/%.build_obj: $(RZDCY_SRC_DIR)/%.c $(DEPDIR)/%.d +$(BUILDDIR)/%.build_obj: $(RZDCY_SRC_DIR)/%.c $(DEPDIR)/%.d $(VERSION_HEADER) mkdir -p $(dir $@) mkdir -p .dep-$(dir $@) $(CC) $(EXTRAFLAGS) $(INCS) $(DEPFLAGS) $(CFLAGS) $< -o $@ $(POSTCOMPILE) -$(BUILDDIR)/%.build_obj : $(RZDCY_SRC_DIR)/%.S +$(BUILDDIR)/%.build_obj : $(RZDCY_SRC_DIR)/%.S $(VERSION_HEADER) mkdir -p $(dir $@) $(AS) $(ASFLAGS) $(INCS) $< -o $@ @@ -506,7 +506,7 @@ uninstall: rm -f $(DESTDIR)$(ICON_DIR)/reicast.png clean: - rm -f $(OBJECTS) $(EXECUTABLE) $(EXECUTABLE_STRIPPED) .map $(RZDCY_SRC_DIR)/version.h + rm -f $(OBJECTS) $(EXECUTABLE) $(EXECUTABLE_STRIPPED) .map $(VERSION_HEADER) .PRECIOUS = $(DEPDIR)/%.d $(DEPDIR)/%.d: ; From 7f0489ff286fe3a176c1d6db76560f44f5f9250c Mon Sep 17 00:00:00 2001 From: flyinghead Date: Sat, 30 Mar 2019 19:26:05 +0100 Subject: [PATCH 211/319] visual studio compatibility no modem support no zip or 7z support so no naomi for now hacked a .asm file as vs doesn't support inline assembly -> code dup --- core/archive/archive.cpp | 4 + core/deps/dirent/dirent.c | 148 ++ core/deps/dirent/dirent.h | 50 + core/hw/naomi/awcartridge.cpp | 5 + core/hw/sh4/dyna/blockmanager.h | 5 +- core/hw/sh4/sh4_if.h | 2 +- core/hw/sh4/sh4_interpreter.h | 5 +- core/rec-x64/msvc.asm | 61 + core/rec-x64/rec_x64.cpp | 7 +- core/rend/gles/CustomTexture.cpp | 4 + core/rend/gui.cpp | 5 + core/rend/gui_util.cpp | 8 + core/stdclass.cpp | 12 +- core/windows/winmain.cpp | 33 +- core/windows/xinput_gamepad.h | 1 - shell/linux/Makefile | 4 +- shell/reicast.vcxproj | 145 +- shell/reicast.vcxproj.filters | 2627 ++++++++++++++++-------------- 18 files changed, 1870 insertions(+), 1256 deletions(-) create mode 100644 core/deps/dirent/dirent.c create mode 100644 core/deps/dirent/dirent.h create mode 100644 core/rec-x64/msvc.asm diff --git a/core/archive/archive.cpp b/core/archive/archive.cpp index 904839a5f..1bef0f2a4 100644 --- a/core/archive/archive.cpp +++ b/core/archive/archive.cpp @@ -21,12 +21,15 @@ #include "archive.h" #include "7zArchive.h" +#ifndef _MSC_VER #include "ZipArchive.h" +#endif Archive *OpenArchive(const char *path) { std::string base_path(path); +#ifndef _MSC_VER Archive *sz_archive = new SzArchive(); if (sz_archive->Open(base_path.c_str()) || sz_archive->Open((base_path + ".7z").c_str()) || sz_archive->Open((base_path + ".7Z").c_str())) return sz_archive; @@ -36,6 +39,7 @@ Archive *OpenArchive(const char *path) if (zip_archive->Open(base_path.c_str()) || zip_archive->Open((base_path + ".zip").c_str()) || zip_archive->Open((base_path + ".ZIP").c_str())) return zip_archive; delete zip_archive; +#endif return NULL; } diff --git a/core/deps/dirent/dirent.c b/core/deps/dirent/dirent.c new file mode 100644 index 000000000..8d496474c --- /dev/null +++ b/core/deps/dirent/dirent.c @@ -0,0 +1,148 @@ +/* + + Implementation of POSIX directory browsing functions and types for Win32. + + Author: Kevlin Henney (kevlin@acm.org, kevlin@curbralan.com) + History: Created March 1997. Updated June 2003 and July 2012. + Rights: See end of file. + +*/ + +#include "dirent.h" +#include +#include /* _findfirst and _findnext set errno iff they return -1 */ +#include +#include + +#ifdef __cplusplus +extern "C" +{ +#endif + + typedef ptrdiff_t handle_type; /* C99's intptr_t not sufficiently portable */ + + struct DIR + { + handle_type handle; /* -1 for failed rewind */ + struct _finddata_t info; + struct dirent result; /* d_name null iff first time */ + char *name; /* null-terminated char string */ + }; + + DIR *opendir(const char *name) + { + DIR *dir = 0; + + if (name && name[0]) + { + size_t base_length = strlen(name); + const char *all = /* search pattern must end with suitable wildcard */ + strchr("/\\", name[base_length - 1]) ? "*" : "/*"; + + if ((dir = (DIR *)malloc(sizeof *dir)) != 0 && + (dir->name = (char *)malloc(base_length + strlen(all) + 1)) != 0) + { + strcat(strcpy(dir->name, name), all); + + if ((dir->handle = + (handle_type)_findfirst(dir->name, &dir->info)) != -1) + { + dir->result.d_name = 0; + } + else /* rollback */ + { + free(dir->name); + free(dir); + dir = 0; + } + } + else /* rollback */ + { + free(dir); + dir = 0; + errno = ENOMEM; + } + } + else + { + errno = EINVAL; + } + + return dir; + } + + int closedir(DIR *dir) + { + int result = -1; + + if (dir) + { + if (dir->handle != -1) + { + result = _findclose(dir->handle); + } + + free(dir->name); + free(dir); + } + + if (result == -1) /* map all errors to EBADF */ + { + errno = EBADF; + } + + return result; + } + + struct dirent *readdir(DIR *dir) + { + struct dirent *result = 0; + + if (dir && dir->handle != -1) + { + if (!dir->result.d_name || _findnext(dir->handle, &dir->info) != -1) + { + result = &dir->result; + result->d_name = dir->info.name; + } + } + else + { + errno = EBADF; + } + + return result; + } + + void rewinddir(DIR *dir) + { + if (dir && dir->handle != -1) + { + _findclose(dir->handle); + dir->handle = (handle_type)_findfirst(dir->name, &dir->info); + dir->result.d_name = 0; + } + else + { + errno = EBADF; + } + } + +#ifdef __cplusplus +} +#endif + +/* + + Copyright Kevlin Henney, 1997, 2003, 2012. All rights reserved. + + Permission to use, copy, modify, and distribute this software and its + documentation for any purpose is hereby granted without fee, provided + that this copyright and permissions notice appear in all copies and + derivatives. + + This software is supplied "as is" without express or implied warranty. + + But that said, if there are any problems please get in touch. + +*/ diff --git a/core/deps/dirent/dirent.h b/core/deps/dirent/dirent.h new file mode 100644 index 000000000..192be112f --- /dev/null +++ b/core/deps/dirent/dirent.h @@ -0,0 +1,50 @@ +#ifndef DIRENT_INCLUDED +#define DIRENT_INCLUDED + +/* + + Declaration of POSIX directory browsing functions and types for Win32. + + Author: Kevlin Henney (kevlin@acm.org, kevlin@curbralan.com) + History: Created March 1997. Updated June 2003. + Rights: See end of file. + +*/ + +#ifdef __cplusplus +extern "C" +{ +#endif + + typedef struct DIR DIR; + + struct dirent + { + char *d_name; + }; + + DIR *opendir(const char *); + int closedir(DIR *); + struct dirent *readdir(DIR *); + void rewinddir(DIR *); + + /* + + Copyright Kevlin Henney, 1997, 2003. All rights reserved. + + Permission to use, copy, modify, and distribute this software and its + documentation for any purpose is hereby granted without fee, provided + that this copyright and permissions notice appear in all copies and + derivatives. + + This software is supplied "as is" without express or implied warranty. + + But that said, if there are any problems please get in touch. + + */ + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/core/hw/naomi/awcartridge.cpp b/core/hw/naomi/awcartridge.cpp index 02b773b1a..f013ce8f7 100644 --- a/core/hw/naomi/awcartridge.cpp +++ b/core/hw/naomi/awcartridge.cpp @@ -162,6 +162,11 @@ ROM board internal layouts: */ #include "awcartridge.h" #include "awave_regs.h" +#ifdef _MSC_VER +#undef min +#undef max +#include +#endif u32 AWCartridge::ReadMem(u32 address, u32 size) { verify(size != 1); diff --git a/core/hw/sh4/dyna/blockmanager.h b/core/hw/sh4/dyna/blockmanager.h index f676500c3..5c11af77f 100644 --- a/core/hw/sh4/dyna/blockmanager.h +++ b/core/hw/sh4/dyna/blockmanager.h @@ -86,7 +86,10 @@ void bm_WriteBlockMap(const string& file); DynarecCodeEntryPtr DYNACALL bm_GetCode(u32 addr); extern "C" { -__attribute__((used)) DynarecCodeEntryPtr DYNACALL bm_GetCode2(u32 addr); +#ifndef _MSC_VER +__attribute__((used)) +#endif + DynarecCodeEntryPtr DYNACALL bm_GetCode2(u32 addr); } RuntimeBlockInfo* bm_GetBlock(void* dynarec_code); diff --git a/core/hw/sh4/sh4_if.h b/core/hw/sh4/sh4_if.h index c9505700b..ddd295a1b 100644 --- a/core/hw/sh4/sh4_if.h +++ b/core/hw/sh4/sh4_if.h @@ -314,7 +314,7 @@ struct Sh4RCB Sh4Context cntx; }; -extern Sh4RCB* p_sh4rcb; +extern "C" Sh4RCB* p_sh4rcb; extern u8* sh4_dyna_rcb; INLINE u32 sh4_sr_GetFull() diff --git a/core/hw/sh4/sh4_interpreter.h b/core/hw/sh4/sh4_interpreter.h index 2bf603e76..8d827af84 100644 --- a/core/hw/sh4/sh4_interpreter.h +++ b/core/hw/sh4/sh4_interpreter.h @@ -61,6 +61,9 @@ void ExecuteDelayslot_RTE(); extern "C" { int UpdateSystem(); -__attribute__((used)) int UpdateSystem_INTC(); +#ifndef _MSC_VER +__attribute__((used)) +#endif + int UpdateSystem_INTC(); } diff --git a/core/rec-x64/msvc.asm b/core/rec-x64/msvc.asm new file mode 100644 index 000000000..4562cbdd2 --- /dev/null +++ b/core/rec-x64/msvc.asm @@ -0,0 +1,61 @@ +_TEXT SEGMENT + +SH4_TIMESLICE = 448 +CPU_RUNNING = 135266148 +PC = 135266120 + +EXTERN bm_GetCode2: PROC +EXTERN UpdateSystem_INTC: PROC +EXTERN cycle_counter: dword +EXTERN p_sh4rcb: qword + +PUBLIC ngen_mainloop +ngen_mainloop PROC + + push rbx + push rbp + push rdi + push rsi + push r12 + push r13 + push r14 + push r15 + sub rsp, 40 ; 32-byte shadow space + 8 for stack 16-byte alignment + + mov dword ptr [cycle_counter], SH4_TIMESLICE + +run_loop: + mov rax, qword ptr [p_sh4rcb] + mov edx, dword ptr[CPU_RUNNING + rax] + test edx, edx + je end_run_loop + +slice_loop: + mov rax, qword ptr [p_sh4rcb] + mov ecx, dword ptr[PC + rax] + call bm_GetCode2 + call rax + mov ecx, dword ptr [cycle_counter] + test ecx, ecx + jg slice_loop + + add ecx, SH4_TIMESLICE + mov dword ptr [cycle_counter], ecx + call UpdateSystem_INTC + jmp run_loop + +end_run_loop: + + add rsp, 40 + pop r15 + pop r14 + pop r13 + pop r12 + pop rsi + pop rdi + pop rbp + pop rbx + ret +ngen_mainloop ENDP +_TEXT ENDS +END diff --git a/core/rec-x64/rec_x64.cpp b/core/rec-x64/rec_x64.cpp index 19e9ba835..1f7a916fa 100644 --- a/core/rec-x64/rec_x64.cpp +++ b/core/rec-x64/rec_x64.cpp @@ -32,7 +32,9 @@ struct DynaRBI : RuntimeBlockInfo } }; -int cycle_counter; +extern "C" { + int cycle_counter; +} double host_cpu_time; u64 guest_cpu_cycles; @@ -76,6 +78,8 @@ static __attribute((used)) void end_slice() #error RAM_SIZE_MAX unknown #endif +#ifndef _MSC_VER + #ifdef _WIN32 // Fully naked function in win32 for proper SEH prologue __asm__ ( @@ -177,6 +181,7 @@ WIN32_ONLY( ".seh_pushreg %r14 \n\t") } #endif +#endif // !_MSC_VER #undef _U #undef _S diff --git a/core/rend/gles/CustomTexture.cpp b/core/rend/gles/CustomTexture.cpp index 3d8db4561..b384497f4 100644 --- a/core/rend/gles/CustomTexture.cpp +++ b/core/rend/gles/CustomTexture.cpp @@ -22,7 +22,11 @@ #include #include #include +#ifdef _MSC_VER +#include "dirent/dirent.h" +#else #include +#endif #include "deps/libpng/png.h" #include "reios/reios.h" diff --git a/core/rend/gui.cpp b/core/rend/gui.cpp index 54786facf..7f59fd343 100644 --- a/core/rend/gui.cpp +++ b/core/rend/gui.cpp @@ -18,7 +18,12 @@ */ #include #include +#ifdef _MSC_VER +#include "dirent/dirent.h" +#define S_ISDIR(mode) (((mode) & _S_IFMT) == _S_IFDIR) +#else #include +#endif #include #include "gui.h" diff --git a/core/rend/gui_util.cpp b/core/rend/gui_util.cpp index f9e9ce91b..5fb9e9d02 100644 --- a/core/rend/gui_util.cpp +++ b/core/rend/gui_util.cpp @@ -21,8 +21,16 @@ #include #include #include +#ifdef _MSC_VER +#include +#include "dirent/dirent.h" +#define S_ISDIR(mode) (((mode) & _S_IFMT) == _S_IFDIR) +#define access _access +#define R_OK 4 +#else #include #include +#endif #include #include "types.h" diff --git a/core/stdclass.cpp b/core/stdclass.cpp index 5f689b72e..59bf1e94a 100644 --- a/core/stdclass.cpp +++ b/core/stdclass.cpp @@ -5,6 +5,10 @@ #include "types.h" #include "cfg/cfg.h" +#ifdef WIN32 +#include +#endif + #if BUILD_COMPILER==COMPILER_VC #include #define access _access @@ -140,11 +144,11 @@ string get_game_dir() bool make_directory(const string& path) { - return mkdir(path.c_str() -#ifndef _WIN32 - , 0755 +#ifdef WIN32 + return _mkdir(path.c_str()) == 0; +#else + return mkdir(path.c_str(), 0755) == 0; #endif - ) == 0; } #if 0 diff --git a/core/windows/winmain.cpp b/core/windows/winmain.cpp index 1731193e9..0b1e38072 100644 --- a/core/windows/winmain.cpp +++ b/core/windows/winmain.cpp @@ -646,15 +646,31 @@ int CALLBACK WinMain(HINSTANCE hInstance,HINSTANCE hPrevInstance,LPSTR lpCmdLine int argc=0; wchar* cmd_line=GetCommandLineA(); wchar** argv=CommandLineToArgvA(cmd_line,&argc); - if(strstr(cmd_line,"NoConsole")==0) + for (int i = 0; i < argc; i++) { - if (AllocConsole()) + if (!stricmp(argv[i], "-console")) { - freopen("CON","w",stdout); - freopen("CON","w",stderr); - freopen("CON","r",stdin); + if (AllocConsole()) + { + freopen("CON", "w", stdout); + freopen("CON", "w", stderr); + freopen("CON", "r", stdin); + } + SetConsoleCtrlHandler((PHANDLER_ROUTINE)CtrlHandler, TRUE); + } + else if (!stricmp(argv[i], "-log")) + { + const char *logfile; + if (i < argc - 1) + { + logfile = argv[i + 1]; + i++; + } + else + logfile = "reicast-log.txt"; + freopen(logfile, "w", stdout); + freopen(logfile, "w", stderr); } - SetConsoleCtrlHandler( (PHANDLER_ROUTINE) CtrlHandler, TRUE ); } #endif @@ -662,14 +678,13 @@ int CALLBACK WinMain(HINSTANCE hInstance,HINSTANCE hPrevInstance,LPSTR lpCmdLine ReserveBottomMemory(); SetupPath(); -#ifndef __GNUC__ - __try -#else #ifdef _WIN64 AddVectoredExceptionHandler(1, ExeptionHandler); #else SetUnhandledExceptionFilter(&ExeptionHandler); #endif +#ifndef __GNUC__ + __try #endif { int reicast_init(int argc, char* argv[]); diff --git a/core/windows/xinput_gamepad.h b/core/windows/xinput_gamepad.h index 894d0aa79..336df20e4 100644 --- a/core/windows/xinput_gamepad.h +++ b/core/windows/xinput_gamepad.h @@ -181,7 +181,6 @@ protected: private: void do_rumble(float power) { - printf("do_rumble %f\n", power); XINPUT_VIBRATION vib; vib.wLeftMotorSpeed = (u16)(65535 * power); diff --git a/shell/linux/Makefile b/shell/linux/Makefile index 306667515..6af3aed05 100644 --- a/shell/linux/Makefile +++ b/shell/linux/Makefile @@ -240,7 +240,7 @@ else ifneq (,$(findstring vero4k,$(platform))) else ifneq (,$(findstring win32,$(platform))) NOT_ARM := 1 CFLAGS += -DTARGET_NO_WEBUI -fno-builtin-sqrtf -funroll-loops -DHAVE_FSEEKO -D TARGET_NO_AREC - LDFLAGS += -static-libgcc -static-libstdc++ + LDFLAGS += -static-libgcc -static-libstdc++ -Wl,-subsystem,windows LIBS := -lopengl32 -lwinmm -lgdi32 -lwsock32 -ldsound -lcomctl32 -lcomdlg32 -lxinput -liphlpapi PLATFORM_EXT := exe CC = gcc @@ -289,7 +289,7 @@ INCS += -I$(RZDCY_SRC_DIR) -I$(RZDCY_SRC_DIR)/deps -I$(RZDCY_SRC_DIR)/khronos LIBS += -lm -lpthread ifdef FOR_LINUX -LIBS += -lrt -ldl +LIBS += -lrt endif PREFIX ?= /usr/local diff --git a/shell/reicast.vcxproj b/shell/reicast.vcxproj index 6a8ec8b4c..2f10bff9e 100644 --- a/shell/reicast.vcxproj +++ b/shell/reicast.vcxproj @@ -35,6 +35,27 @@
+ + true + true + true + true + true + true + true + true + + + + true + true + true + true + true + true + true + true + @@ -47,6 +68,7 @@ + @@ -68,6 +90,10 @@ + + + + @@ -154,6 +180,11 @@ + + + + + @@ -192,9 +223,13 @@ + + + + true true @@ -269,6 +304,8 @@ + + @@ -276,6 +313,9 @@ + + + @@ -284,6 +324,9 @@ + + + @@ -297,6 +340,7 @@ + @@ -313,6 +357,13 @@ + + + + + + + @@ -384,9 +435,17 @@ + + + + + + + + @@ -430,6 +489,10 @@ + + + + true true @@ -454,6 +517,11 @@ + + + + Document + true true @@ -464,9 +532,14 @@ + + + + + @@ -640,7 +713,7 @@ Level3 Full WIN32;CHD5_FLAC;PACKAGE_VERSION="1.3.2";FLAC__HAS_OGG=0;FLAC__NO_DLL;HAVE_LROUND;HAVE_STDINT_H;HAVE_STDLIB_H;CHD5_LZMA;_7ZIP_ST;NDEBUG;_CONSOLE;X86;RELEASE;%(PreprocessorDefinitions) - $(ProjectDir)..\core\;$(ProjectDir)..\core\khronos;$(ProjectDir)..\core\rend\gles;$(ProjectDir)..\core\deps\flac\src\libflac\include;$(ProjectDir)..\core\deps\flac\include + $(ProjectDir)..\core\;$(ProjectDir)..\core\khronos;$(ProjectDir)..\core\rend\gles;$(ProjectDir)..\core\deps;$(ProjectDir)..\core\deps\flac\src\libflac\include;$(ProjectDir)..\core\deps\flac\include /MP %(AdditionalOptions) AnySuitable true @@ -664,6 +737,13 @@ $(SolutionDir)..\pvrframe Dsound.lib;winmm.lib;wsock32.lib;comctl32.lib;%(AdditionalDependencies) + + for /f "delims=" %%i in ('git describe') do echo #define REICAST_VERSION "%%i" >$(ProjectDir)\..\core\version.h +for /f "delims=" %%i in ('git rev-parse --short HEAD') do echo #define GIT_HASH "%%i" >>$(ProjectDir)\..\core\version.h +for /f "delims=" %%i in ('date /T') do echo #define BUILD_DATE "%%i" >>$(ProjectDir)\..\core\version.h + + Create version.h + @@ -672,7 +752,7 @@ Level3 Full WIN32;CHD5_FLAC;PACKAGE_VERSION="1.3.2";FLAC__HAS_OGG=0;FLAC__NO_DLL;HAVE_LROUND;HAVE_STDINT_H;HAVE_STDLIB_H;CHD5_LZMA;_7ZIP_ST;NDEBUG;_CONSOLE;X86;RELEASE;TARGET_NAOMI;%(PreprocessorDefinitions) - $(ProjectDir)..\core\;$(ProjectDir)..\core\khronos;$(ProjectDir)..\core\rend\gles;$(ProjectDir)..\core\deps\flac\src\libflac\include;$(ProjectDir)..\core\deps\flac\include + $(ProjectDir)..\core\;$(ProjectDir)..\core\khronos;$(ProjectDir)..\core\rend\gles;$(ProjectDir)..\core\deps;$(ProjectDir)..\core\deps\flac\src\libflac\include;$(ProjectDir)..\core\deps\flac\include /MP %(AdditionalOptions) AnySuitable true @@ -696,6 +776,13 @@ $(SolutionDir)..\pvrframe Dsound.lib;winmm.lib;wsock32.lib;comctl32.lib;%(AdditionalDependencies) + + for /f "delims=" %%i in ('git describe') do echo #define REICAST_VERSION "%%i" >$(ProjectDir)\..\core\version.h +for /f "delims=" %%i in ('git rev-parse --short HEAD') do echo #define GIT_HASH "%%i" >>$(ProjectDir)\..\core\version.h +for /f "delims=" %%i in ('date /T') do echo #define BUILD_DATE "%%i" >>$(ProjectDir)\..\core\version.h + + Create version.h + @@ -704,7 +791,7 @@ Level3 Full WIN32;CHD5_FLAC;PACKAGE_VERSION="1.3.2";FLAC__HAS_OGG=0;FLAC__NO_DLL;HAVE_LROUND;HAVE_STDINT_H;HAVE_STDLIB_H;CHD5_LZMA;_7ZIP_ST;NDEBUG;_CONSOLE;X86;RELEASE;%(PreprocessorDefinitions) - $(ProjectDir)..\core\;$(ProjectDir)..\core\khronos;$(ProjectDir)..\core\rend\gles;$(ProjectDir)..\core\deps\flac\src\libflac\include;$(ProjectDir)..\core\deps\flac\include + $(ProjectDir)..\core\;$(ProjectDir)..\core\khronos;$(ProjectDir)..\core\rend\gles;$(ProjectDir)..\core\deps;$(ProjectDir)..\core\deps\flac\src\libflac\include;$(ProjectDir)..\core\deps\flac\include /MP %(AdditionalOptions) AnySuitable true @@ -730,6 +817,13 @@ $(SolutionDir)..\pvrframe Dsound.lib;winmm.lib;wsock32.lib;comctl32.lib;%(AdditionalDependencies) + + for /f "delims=" %%i in ('git describe') do echo #define REICAST_VERSION "%%i" >$(ProjectDir)\..\core\version.h +for /f "delims=" %%i in ('git rev-parse --short HEAD') do echo #define GIT_HASH "%%i" >>$(ProjectDir)\..\core\version.h +for /f "delims=" %%i in ('date /T') do echo #define BUILD_DATE "%%i" >>$(ProjectDir)\..\core\version.h + + Create version.h + @@ -738,7 +832,7 @@ Level3 Full WIN32;CHD5_FLAC;PACKAGE_VERSION="1.3.2";FLAC__HAS_OGG=0;FLAC__NO_DLL;HAVE_LROUND;HAVE_STDINT_H;HAVE_STDLIB_H;CHD5_LZMA;_7ZIP_ST;NDEBUG;_CONSOLE;X86;RELEASE;TARGET_NAOMI;%(PreprocessorDefinitions) - $(ProjectDir)..\core\;$(ProjectDir)..\core\khronos;$(ProjectDir)..\core\rend\gles;$(ProjectDir)..\core\deps\flac\src\libflac\include;$(ProjectDir)..\core\deps\flac\include + $(ProjectDir)..\core\;$(ProjectDir)..\core\khronos;$(ProjectDir)..\core\rend\gles;$(ProjectDir)..\core\deps;$(ProjectDir)..\core\deps\flac\src\libflac\include;$(ProjectDir)..\core\deps\flac\include /MP %(AdditionalOptions) AnySuitable true @@ -764,6 +858,13 @@ $(SolutionDir)..\pvrframe Dsound.lib;winmm.lib;wsock32.lib;comctl32.lib;%(AdditionalDependencies) + + for /f "delims=" %%i in ('git describe') do echo #define REICAST_VERSION "%%i" >$(ProjectDir)\..\core\version.h +for /f "delims=" %%i in ('git rev-parse --short HEAD') do echo #define GIT_HASH "%%i" >>$(ProjectDir)\..\core\version.h +for /f "delims=" %%i in ('date /T') do echo #define BUILD_DATE "%%i" >>$(ProjectDir)\..\core\version.h + + Create version.h + @@ -772,7 +873,7 @@ Level3 Disabled WIN32;CHD5_FLAC;PACKAGE_VERSION="1.3.2";FLAC__HAS_OGG=0;FLAC__NO_DLL;HAVE_LROUND;HAVE_STDINT_H;HAVE_STDLIB_H;CHD5_LZMA;_7ZIP_ST;_DEBUG;_CONSOLE;X86;%(PreprocessorDefinitions) - $(ProjectDir)..\core\;$(ProjectDir)..\core\khronos;$(ProjectDir)..\core\rend\gles;$(ProjectDir)..\core\deps\flac\src\libflac\include;$(ProjectDir)..\core\deps\flac\include + $(ProjectDir)..\core\;$(ProjectDir)..\core\khronos;$(ProjectDir)..\core\rend\gles;$(ProjectDir)..\core\deps;$(ProjectDir)..\core\deps\flac\src\libflac\include;$(ProjectDir)..\core\deps\flac\include true false Default @@ -785,6 +886,13 @@ $(SolutionDir)..\pvrframe Dsound.lib;winmm.lib;wsock32.lib;comctl32.lib;%(AdditionalDependencies) + + for /f "delims=" %%i in ('git describe') do echo #define REICAST_VERSION "%%i" >$(ProjectDir)\..\core\version.h +for /f "delims=" %%i in ('git rev-parse --short HEAD') do echo #define GIT_HASH "%%i" >>$(ProjectDir)\..\core\version.h +for /f "delims=" %%i in ('date /T') do echo #define BUILD_DATE "%%i" >>$(ProjectDir)\..\core\version.h + + Create version.h + @@ -793,7 +901,7 @@ Level3 Disabled WIN32;CHD5_FLAC;PACKAGE_VERSION="1.3.2";FLAC__HAS_OGG=0;FLAC__NO_DLL;HAVE_LROUND;HAVE_STDINT_H;HAVE_STDLIB_H;CHD5_LZMA;_7ZIP_ST;_DEBUG;_CONSOLE;X86;TARGET_NAOMI;%(PreprocessorDefinitions) - $(ProjectDir)..\core\;$(ProjectDir)..\core\khronos;$(ProjectDir)..\core\rend\gles;$(ProjectDir)..\core\deps\flac\src\libflac\include;$(ProjectDir)..\core\deps\flac\include + $(ProjectDir)..\core\;$(ProjectDir)..\core\khronos;$(ProjectDir)..\core\rend\gles;$(ProjectDir)..\core\deps;$(ProjectDir)..\core\deps\flac\src\libflac\include;$(ProjectDir)..\core\deps\flac\include true false Default @@ -806,6 +914,13 @@ $(SolutionDir)..\pvrframe Dsound.lib;winmm.lib;wsock32.lib;comctl32.lib;%(AdditionalDependencies) + + for /f "delims=" %%i in ('git describe') do echo #define REICAST_VERSION "%%i" >$(ProjectDir)\..\core\version.h +for /f "delims=" %%i in ('git rev-parse --short HEAD') do echo #define GIT_HASH "%%i" >>$(ProjectDir)\..\core\version.h +for /f "delims=" %%i in ('date /T') do echo #define BUILD_DATE "%%i" >>$(ProjectDir)\..\core\version.h + + Create version.h + @@ -814,7 +929,7 @@ Level3 Disabled WIN32;CHD5_FLAC;PACKAGE_VERSION="1.3.2";FLAC__HAS_OGG=0;FLAC__NO_DLL;HAVE_LROUND;HAVE_STDINT_H;HAVE_STDLIB_H;CHD5_LZMA;_7ZIP_ST;_DEBUG;_CONSOLE;X86;%(PreprocessorDefinitions) - $(ProjectDir)..\core\;$(ProjectDir)..\core\khronos;$(ProjectDir)..\core\rend\gles;$(ProjectDir)..\core\deps\flac\src\libflac\include;$(ProjectDir)..\core\deps\flac\include + $(ProjectDir)..\core\;$(ProjectDir)..\core\khronos;$(ProjectDir)..\core\rend\gles;$(ProjectDir)..\core\deps;$(ProjectDir)..\core\deps\flac\src\libflac\include;$(ProjectDir)..\core\deps\flac\include true false Default @@ -830,6 +945,13 @@ $(SolutionDir)..\pvrframe Dsound.lib;winmm.lib;wsock32.lib;comctl32.lib;%(AdditionalDependencies) + + for /f "delims=" %%i in ('git describe') do echo #define REICAST_VERSION "%%i" >$(ProjectDir)\..\core\version.h +for /f "delims=" %%i in ('git rev-parse --short HEAD') do echo #define GIT_HASH "%%i" >>$(ProjectDir)\..\core\version.h +for /f "delims=" %%i in ('date /T') do echo #define BUILD_DATE "%%i" >>$(ProjectDir)\..\core\version.h + + Create version.h + @@ -838,7 +960,7 @@ Level3 Disabled WIN32;CHD5_FLAC;PACKAGE_VERSION="1.3.2";FLAC__HAS_OGG=0;FLAC__NO_DLL;HAVE_LROUND;HAVE_STDINT_H;HAVE_STDLIB_H;CHD5_LZMA;_7ZIP_ST;_DEBUG;_CONSOLE;X86;TARGET_NAOMI;%(PreprocessorDefinitions) - $(ProjectDir)..\core\;$(ProjectDir)..\core\khronos;$(ProjectDir)..\core\rend\gles;$(ProjectDir)..\core\deps\flac\src\libflac\include;$(ProjectDir)..\core\deps\flac\include + $(ProjectDir)..\core\;$(ProjectDir)..\core\khronos;$(ProjectDir)..\core\rend\gles;$(ProjectDir)..\core\deps;$(ProjectDir)..\core\deps\flac\src\libflac\include;$(ProjectDir)..\core\deps\flac\include true false Default @@ -854,6 +976,13 @@ $(SolutionDir)..\pvrframe Dsound.lib;winmm.lib;wsock32.lib;comctl32.lib;%(AdditionalDependencies) + + for /f "delims=" %%i in ('git describe') do echo #define REICAST_VERSION "%%i" >$(ProjectDir)\..\core\version.h +for /f "delims=" %%i in ('git rev-parse --short HEAD') do echo #define GIT_HASH "%%i" >>$(ProjectDir)\..\core\version.h +for /f "delims=" %%i in ('date /T') do echo #define BUILD_DATE "%%i" >>$(ProjectDir)\..\core\version.h + + Create version.h + diff --git a/shell/reicast.vcxproj.filters b/shell/reicast.vcxproj.filters index 6c854badf..1f347b584 100644 --- a/shell/reicast.vcxproj.filters +++ b/shell/reicast.vcxproj.filters @@ -1,1229 +1,1400 @@ - - - - - hw\aica - - - hw\aica - - - hw\aica - - - hw\aica - - - hw\aica - - - hw\arm7 - - - hw\arm7 - - - hw\arm7 - - - hw\arm7 - - - hw\gdrom - - - hw\gdrom - - - hw\maple - - - hw\maple - - - hw\maple - - - hw\maple - - - hw\mem - - - hw\sh4 - - - hw\pvr - - - hw\pvr - - - deps\zlib - - - deps\zlib - - - deps\zlib - - - deps\zlib - - - deps\zlib - - - deps\zlib - - - deps\zlib - - - deps\zlib - - - deps\zlib - - - deps\zlib - - - deps\zlib - - - deps\crypto - - - deps\crypto - - - imgread - - - imgread - - - imgread - - - imgread - - - imgread - - - imgread - - - - - cfg - - - cfg - - - emitter - - - windows - - - profiler - - - hw\holly - - - hw\holly - - - hw\holly - - - hw\sh4 - - - oslib - - - hw\sh4\modules - - - hw\sh4\modules - - - hw\sh4\modules - - - hw\sh4\modules - - - hw\sh4\modules - - - hw\sh4\modules - - - hw\sh4\modules - - - hw\sh4\modules - - - hw\sh4\dyna - - - hw\sh4\dyna - - - hw\sh4\dyna - - - hw\sh4\dyna - - - hw\sh4\interpr - - - hw\sh4\interpr - - - hw\sh4\interpr - - - hw\sh4 - - - hw\sh4\modules - - - hw\sh4\modules - - - hw\sh4 - - - hw\sh4 - - - hw\sh4 - - - hw\sh4 - - - deps\libelf - - - deps\libelf - - - deps\libelf - - - linux - - - linux - - - deps\chdpsr - - - deps\libpng - - - deps\libpng - - - deps\libpng - - - deps\libpng - - - deps\libpng - - - deps\libpng - - - deps\libpng - - - deps\libpng - - - deps\libpng - - - deps\libpng - - - deps\libpng - - - deps\libpng - - - deps\libpng - - - deps\libpng - - - deps\libpng - - - rend\gles - - - rend\gles - - - rend\gles - - - rend - - - rend\d3d11 - - - deps\libwebsocket - - - deps\libwebsocket - - - deps\libwebsocket - - - deps\libwebsocket - - - deps\libwebsocket - - - deps\libwebsocket - - - deps\libwebsocket - - - deps\libwebsocket - - - deps\libwebsocket - - - deps\libwebsocket - - - deps\libwebsocket - - - deps\libwebsocket - - - deps\libwebsocket - - - deps\libwebsocket - - - deps\libwebsocket - - - deps\libwebsocket - - - deps\libwebsocket - - - deps\libwebsocket - - - deps\libwebsocket - - - webui - - - deps\coreio - - - reios - - - reios - - - reios - - - reios - - - linux - - - oslib - - - rec-ARM - - - rec-x86 - - - rec-x86 - - - rec-x86 - - - rec-x64 - - - rec-cpp - - - rend\soft - - - hw\naomi - - - hw\naomi - - - cfg - - - - deps\xxhash - - - rend\gl4 - - - rend\gl4 - - - rend\gl4 - - - rend\gl4 - - - hw\aica - - - hw\modem - - - hw\modem - - - deps\xbrz - - - hw\pvr - - - hw\pvr - - - hw\pvr - - - hw\pvr - - - hw\pvr - - - hw\pvr - - - hw\pvr - - - deps\lzma - - - deps\lzma - - - deps\lzma - - - deps\lzma - - - deps\lzma - - - deps\lzma - - - deps\lzma - - - deps\lzma - - - deps\lzma - - - deps\lzma - - - deps\lzma - - - deps\lzma - - - deps\lzma - - - deps\flac\src\libFLAC - - - deps\flac\src\libFLAC - - - deps\flac\src\libFLAC - - - deps\flac\src\libFLAC - - - deps\flac\src\libFLAC - - - deps\flac\src\libFLAC - - - deps\flac\src\libFLAC - - - deps\flac\src\libFLAC - - - deps\flac\src\libFLAC - - - deps\flac\src\libFLAC - - - deps\flac\src\libFLAC - - - deps\flac\src\libFLAC - - - deps\flac\src\libFLAC - - - deps\flac\src\libFLAC - - - deps\flac\src\libFLAC - - - deps\flac\src\libFLAC - - - deps\flac\src\libFLAC - - - deps\flac\src\libFLAC - - - deps\flac\src\libFLAC - - - deps\flac\src\libFLAC - - - deps\flac\src\libFLAC - - - deps\chdr - - - deps\chdr - - - deps\chdr - - - deps\chdr - - - deps\chdr - - - - - {2099cae6-67fb-489f-b082-7555e8330705} - - - {70620682-2709-4701-b0a7-7d8f8e1571de} - - - {9659b21d-ccee-4d2b-973d-09e447be743e} - - - {a07a09a2-a585-4144-a0d0-c54d4bb442ec} - - - {9da5f3a4-2e41-4f94-a499-e2524c4dd0c2} - - - {c3e9aa73-a90e-4f37-b357-b0378fb1feff} - - - {65dd7d80-739d-4b5c-a01c-3262cb8ec97a} - - - {da5bfaf9-fca7-4397-9034-e201c1f38e49} - - - {ee2a1c0f-d38c-4d2b-bb07-ba6841fc5832} - - - {09e0d071-e3c1-4549-b2a8-52f24f4691a8} - - - {d0252230-b46c-424f-8aa8-037774915f81} - - - {5cdde132-a201-4bbc-9dd4-2ba3b3637a19} - - - {bd80604a-c634-44b3-a729-811bbefd3f71} - - - {8783a652-88e4-49bf-be90-bc36abe6753b} - - - {2e4fe5a7-a86c-45cf-b456-39b107a91bc7} - - - {369d7f53-be71-4055-a303-675f1132b118} - - - {e14356dc-6635-49f9-94d5-dc14ff1dec70} - - - {f96b3c39-1255-4ee8-999e-5c6e8fef21e5} - - - {b81a858a-7327-4eb4-bc6b-6ae24e2c08fd} - - - {755fe7c9-b6b5-42e5-b0e6-d1d02d5a6e03} - - - {2bbf43fd-2127-412f-bd76-6260b04522f8} - - - {be756ece-25e8-4a69-b90e-2601fdbb42fe} - - - {66246039-9de4-4bc0-88a9-94582e2713e0} - - - {3ef102e4-a05f-4774-b2d7-7e1529bfd9b1} - - - {82948f1f-819b-4a2d-9e07-72dfbf3e96ca} - - - {fe073008-ffba-43c1-9192-daec4b48148e} - - - {fa363b78-585a-476a-9afc-628b0f6650cf} - - - {3f5c03ee-36db-4818-b0d2-4eec9c084f75} - - - {cd2c89fd-7a5b-43c8-a940-6ea0d29e9b5d} - - - {cc05f61b-c484-40e6-9859-6ca0cd64735c} - - - {3d3de3ff-9e79-4920-a95a-61d190e73004} - - - {81193efc-656a-4154-9adf-146856d4c7d3} - - - {23cfa286-fe88-439d-89de-a6a5a23cacc9} - - - {f614dd66-5d30-4548-a209-f857f95fb505} - - - {df854851-d3b5-4549-8248-acdfa954be44} - - - {5a7b63eb-8c03-46ac-b6e0-dfd3ade02f11} - - - {f73263e9-dbe8-4a6f-8b73-335af8307551} - - - {63d1fcf2-64b4-4973-995f-cd471f51117c} - - - {6c4b2d69-54c0-4660-9969-a98fd0339a15} - - - {1752487d-0739-47bf-8c6b-1d38e6f389f7} - - - {83d7e5cc-cbd2-40e3-b2c0-79cc53b1dd9a} - - - {e9e574e5-cce8-467b-8457-acf3999240c1} - - - {c1ffb17b-f29e-466a-a6a6-e93aa1bb2ca7} - - - {a67aece4-e47f-48a2-8944-6afbe679a5fb} - - - {4c0328b2-a9b3-4b0a-ab54-558ba960b03a} - - - {8bb0b4a0-7661-4533-9245-fc43582f6182} - - - {98226656-1e2d-4d69-bcd4-c1c1b6d4cb0a} - - - {f06382df-ae0e-459c-92a2-dc0eaab25b05} - - - {39e82b10-530a-4978-b8cc-8a11b67981d7} - - - {51e03f58-a90d-4ebc-95f7-6af6cdc39165} - - - {4c52c0a3-9e35-4285-a062-290d3667e1d1} - - - - - hw\aica - - - hw\aica - - - hw\aica - - - hw\aica - - - hw\aica - - - hw\arm7 - - - hw\arm7 - - - hw\arm7 - - - hw\arm7 - - - hw\gdrom - - - hw\gdrom - - - hw\maple - - - hw\maple - - - hw\maple - - - hw\maple - - - hw\mem - - - hw\sh4 - - - hw\sh4 - - - hw\sh4 - - - hw\pvr - - - hw\pvr - - - hw\pvr - - - hw\pvr - - - deps\zlib - - - deps\zlib - - - deps\zlib - - - deps\zlib - - - deps\zlib - - - deps\zlib - - - deps\zlib - - - deps\zlib - - - deps\zlib - - - deps\zlib - - - deps\zlib - - - deps\crypto - - - deps\crypto - - - deps\chdr - - - deps\chdr - - - imgread - - - imgread - - - imgread - - - imgread - - - imgread - - - imgread - - - oslib - - - - - cfg - - - - emitter - - - emitter - - - emitter - - - emitter - - - emitter - - - emitter - - - emitter - - - emitter - - - emitter - - - emitter - - - profiler - - - hw\holly - - - hw\holly - - - hw\flashrom - - - hw\holly - - - hw\sh4 - - - oslib - - - hw\sh4\modules - - - hw\sh4\modules - - - hw\sh4\modules - - - hw\sh4\modules - - - hw\sh4\dyna - - - hw\sh4\dyna - - - hw\sh4\dyna - - - hw\sh4\dyna - - - hw\sh4\dyna - - - hw\sh4\dyna - - - hw\sh4\dyna - - - hw\sh4\dyna - - - hw\sh4\interpr - - - hw\sh4\modules - - - hw\sh4 - - - hw\sh4 - - - hw\sh4 - - - hw\sh4 - - - hw\sh4 - - - hw\sh4 - - - deps\libelf - - - deps\libelf - - - deps\libelf - - - deps\libelf - - - linux - - - deps\chdpsr - - - deps\libpng - - - deps\libpng - - - deps\libpng - - - deps\libpng - - - rend\gles - - - rend - - - rend - - - deps\libwebsocket - - - deps\libwebsocket - - - deps\libwebsocket - - - deps\libwebsocket - - - deps\libwebsocket - - - webui - - - deps\coreio - - - reios - - - reios - - - reios - - - reios - - - linux - - - oslib - - - rec-x86 - - - hw\naomi - - - hw\naomi - - - hw\naomi - - - cfg - - - hw\sh4\modules - - - rend\gl4 - - - rend\gl4 - - - hw\modem - - - hw\modem - - - hw\modem - - - hw\pvr - - - hw\pvr - - - hw\pvr - - - hw\pvr - - - hw\pvr - - - hw\pvr - - - hw\pvr - - - hw\pvr - - - deps\lzma - - - deps\lzma - - - deps\lzma - - - deps\lzma - - - deps\lzma - - - deps\lzma - - - deps\lzma - - - deps\lzma - - - deps\lzma - - - deps\lzma - - - deps\lzma - - - deps\lzma - - - deps\lzma - - - deps\lzma - - - deps\flac\include\FLAC - - - deps\flac\include\FLAC - - - deps\flac\include\FLAC - - - deps\flac\include\FLAC - - - deps\flac\include\FLAC - - - deps\flac\include\FLAC - - - deps\flac\include\FLAC - - - deps\flac\include\FLAC - - - deps\flac\include\FLAC - - - deps\flac\include\share - - - deps\flac\include\share - - - deps\flac\include\share - - - deps\flac\include\share - - - deps\flac\include\share - - - deps\flac\include\share - - - deps\flac\include\share - - - deps\chdr - - - deps\chdr - - - deps\chdr - - - deps\chdr - - - - - deps\zlib - - - rec-ARM - - - rec-x86 - - + + + + + hw\aica + + + hw\aica + + + hw\aica + + + hw\aica + + + hw\aica + + + hw\arm7 + + + hw\arm7 + + + hw\arm7 + + + hw\arm7 + + + hw\gdrom + + + hw\gdrom + + + hw\maple + + + hw\maple + + + hw\maple + + + hw\maple + + + hw\mem + + + hw\sh4 + + + hw\pvr + + + hw\pvr + + + deps\zlib + + + deps\zlib + + + deps\zlib + + + deps\zlib + + + deps\zlib + + + deps\zlib + + + deps\zlib + + + deps\zlib + + + deps\zlib + + + deps\zlib + + + deps\zlib + + + deps\crypto + + + deps\crypto + + + imgread + + + imgread + + + imgread + + + imgread + + + imgread + + + imgread + + + + + cfg + + + cfg + + + emitter + + + windows + + + profiler + + + hw\holly + + + hw\holly + + + hw\holly + + + hw\sh4 + + + oslib + + + hw\sh4\modules + + + hw\sh4\modules + + + hw\sh4\modules + + + hw\sh4\modules + + + hw\sh4\modules + + + hw\sh4\modules + + + hw\sh4\modules + + + hw\sh4\modules + + + hw\sh4\dyna + + + hw\sh4\dyna + + + hw\sh4\dyna + + + hw\sh4\dyna + + + hw\sh4\interpr + + + hw\sh4\interpr + + + hw\sh4\interpr + + + hw\sh4 + + + hw\sh4\modules + + + hw\sh4\modules + + + hw\sh4 + + + hw\sh4 + + + hw\sh4 + + + hw\sh4 + + + deps\libelf + + + deps\libelf + + + deps\libelf + + + linux + + + linux + + + deps\chdpsr + + + deps\libpng + + + deps\libpng + + + deps\libpng + + + deps\libpng + + + deps\libpng + + + deps\libpng + + + deps\libpng + + + deps\libpng + + + deps\libpng + + + deps\libpng + + + deps\libpng + + + deps\libpng + + + deps\libpng + + + deps\libpng + + + deps\libpng + + + rend\gles + + + rend\gles + + + rend\gles + + + rend + + + rend\d3d11 + + + deps\libwebsocket + + + deps\libwebsocket + + + deps\libwebsocket + + + deps\libwebsocket + + + deps\libwebsocket + + + deps\libwebsocket + + + deps\libwebsocket + + + deps\libwebsocket + + + deps\libwebsocket + + + deps\libwebsocket + + + deps\libwebsocket + + + deps\libwebsocket + + + deps\libwebsocket + + + deps\libwebsocket + + + deps\libwebsocket + + + deps\libwebsocket + + + deps\libwebsocket + + + deps\libwebsocket + + + deps\libwebsocket + + + webui + + + deps\coreio + + + reios + + + reios + + + reios + + + reios + + + linux + + + oslib + + + rec-ARM + + + rec-x86 + + + rec-x86 + + + rec-x86 + + + rec-x64 + + + rec-cpp + + + rend\soft + + + hw\naomi + + + hw\naomi + + + cfg + + + + deps\xxhash + + + rend\gl4 + + + rend\gl4 + + + rend\gl4 + + + rend\gl4 + + + hw\aica + + + hw\modem + + + hw\modem + + + deps\xbrz + + + hw\pvr + + + hw\pvr + + + hw\pvr + + + hw\pvr + + + hw\pvr + + + hw\pvr + + + hw\pvr + + + deps\lzma + + + deps\lzma + + + deps\lzma + + + deps\lzma + + + deps\lzma + + + deps\lzma + + + deps\lzma + + + deps\lzma + + + deps\lzma + + + deps\lzma + + + deps\lzma + + + deps\lzma + + + deps\lzma + + + deps\flac\src\libFLAC + + + deps\flac\src\libFLAC + + + deps\flac\src\libFLAC + + + deps\flac\src\libFLAC + + + deps\flac\src\libFLAC + + + deps\flac\src\libFLAC + + + deps\flac\src\libFLAC + + + deps\flac\src\libFLAC + + + deps\flac\src\libFLAC + + + deps\flac\src\libFLAC + + + deps\flac\src\libFLAC + + + deps\flac\src\libFLAC + + + deps\flac\src\libFLAC + + + deps\flac\src\libFLAC + + + deps\flac\src\libFLAC + + + deps\flac\src\libFLAC + + + deps\flac\src\libFLAC + + + deps\flac\src\libFLAC + + + deps\flac\src\libFLAC + + + deps\flac\src\libFLAC + + + deps\flac\src\libFLAC + + + deps\chdr + + + deps\chdr + + + deps\chdr + + + deps\chdr + + + deps\chdr + + + rend + + + rend + + + deps\imgui + + + deps\imgui + + + deps\imgui + + + deps\imgui + + + deps\dirent + + + input + + + input + + + input + + + rend\gles + + + rend\gles + + + rend\gles + + + hw\naomi + + + hw\naomi + + + hw\naomi + + + hw\naomi + + + hw\naomi + + + archive + + + archive + + + archive + + + imgread + + + + + {2099cae6-67fb-489f-b082-7555e8330705} + + + {70620682-2709-4701-b0a7-7d8f8e1571de} + + + {9659b21d-ccee-4d2b-973d-09e447be743e} + + + {a07a09a2-a585-4144-a0d0-c54d4bb442ec} + + + {9da5f3a4-2e41-4f94-a499-e2524c4dd0c2} + + + {c3e9aa73-a90e-4f37-b357-b0378fb1feff} + + + {65dd7d80-739d-4b5c-a01c-3262cb8ec97a} + + + {da5bfaf9-fca7-4397-9034-e201c1f38e49} + + + {ee2a1c0f-d38c-4d2b-bb07-ba6841fc5832} + + + {09e0d071-e3c1-4549-b2a8-52f24f4691a8} + + + {d0252230-b46c-424f-8aa8-037774915f81} + + + {5cdde132-a201-4bbc-9dd4-2ba3b3637a19} + + + {bd80604a-c634-44b3-a729-811bbefd3f71} + + + {8783a652-88e4-49bf-be90-bc36abe6753b} + + + {2e4fe5a7-a86c-45cf-b456-39b107a91bc7} + + + {369d7f53-be71-4055-a303-675f1132b118} + + + {e14356dc-6635-49f9-94d5-dc14ff1dec70} + + + {f96b3c39-1255-4ee8-999e-5c6e8fef21e5} + + + {b81a858a-7327-4eb4-bc6b-6ae24e2c08fd} + + + {755fe7c9-b6b5-42e5-b0e6-d1d02d5a6e03} + + + {2bbf43fd-2127-412f-bd76-6260b04522f8} + + + {be756ece-25e8-4a69-b90e-2601fdbb42fe} + + + {66246039-9de4-4bc0-88a9-94582e2713e0} + + + {3ef102e4-a05f-4774-b2d7-7e1529bfd9b1} + + + {82948f1f-819b-4a2d-9e07-72dfbf3e96ca} + + + {fe073008-ffba-43c1-9192-daec4b48148e} + + + {fa363b78-585a-476a-9afc-628b0f6650cf} + + + {3f5c03ee-36db-4818-b0d2-4eec9c084f75} + + + {cd2c89fd-7a5b-43c8-a940-6ea0d29e9b5d} + + + {cc05f61b-c484-40e6-9859-6ca0cd64735c} + + + {3d3de3ff-9e79-4920-a95a-61d190e73004} + + + {81193efc-656a-4154-9adf-146856d4c7d3} + + + {23cfa286-fe88-439d-89de-a6a5a23cacc9} + + + {f614dd66-5d30-4548-a209-f857f95fb505} + + + {df854851-d3b5-4549-8248-acdfa954be44} + + + {5a7b63eb-8c03-46ac-b6e0-dfd3ade02f11} + + + {f73263e9-dbe8-4a6f-8b73-335af8307551} + + + {63d1fcf2-64b4-4973-995f-cd471f51117c} + + + {6c4b2d69-54c0-4660-9969-a98fd0339a15} + + + {1752487d-0739-47bf-8c6b-1d38e6f389f7} + + + {83d7e5cc-cbd2-40e3-b2c0-79cc53b1dd9a} + + + {e9e574e5-cce8-467b-8457-acf3999240c1} + + + {c1ffb17b-f29e-466a-a6a6-e93aa1bb2ca7} + + + {a67aece4-e47f-48a2-8944-6afbe679a5fb} + + + {4c0328b2-a9b3-4b0a-ab54-558ba960b03a} + + + {8bb0b4a0-7661-4533-9245-fc43582f6182} + + + {98226656-1e2d-4d69-bcd4-c1c1b6d4cb0a} + + + {f06382df-ae0e-459c-92a2-dc0eaab25b05} + + + {39e82b10-530a-4978-b8cc-8a11b67981d7} + + + {51e03f58-a90d-4ebc-95f7-6af6cdc39165} + + + {4c52c0a3-9e35-4285-a062-290d3667e1d1} + + + {0746d3c7-838f-43ed-817b-8215a24b87d4} + + + {29043329-ba53-45d5-8513-a5ee92c73853} + + + {ff6259dd-375b-46bb-a2ad-bf2825eb81ee} + + + {8fea504f-a313-4ab2-b25b-74e80fe449c8} + + + + + hw\aica + + + hw\aica + + + hw\aica + + + hw\aica + + + hw\aica + + + hw\arm7 + + + hw\arm7 + + + hw\arm7 + + + hw\arm7 + + + hw\gdrom + + + hw\gdrom + + + hw\maple + + + hw\maple + + + hw\maple + + + hw\maple + + + hw\mem + + + hw\sh4 + + + hw\sh4 + + + hw\sh4 + + + hw\pvr + + + hw\pvr + + + hw\pvr + + + hw\pvr + + + deps\zlib + + + deps\zlib + + + deps\zlib + + + deps\zlib + + + deps\zlib + + + deps\zlib + + + deps\zlib + + + deps\zlib + + + deps\zlib + + + deps\zlib + + + deps\zlib + + + deps\crypto + + + deps\crypto + + + deps\chdr + + + deps\chdr + + + imgread + + + imgread + + + imgread + + + imgread + + + imgread + + + imgread + + + oslib + + + + + cfg + + + + emitter + + + emitter + + + emitter + + + emitter + + + emitter + + + emitter + + + emitter + + + emitter + + + emitter + + + emitter + + + profiler + + + hw\holly + + + hw\holly + + + hw\flashrom + + + hw\holly + + + hw\sh4 + + + oslib + + + hw\sh4\modules + + + hw\sh4\modules + + + hw\sh4\modules + + + hw\sh4\modules + + + hw\sh4\dyna + + + hw\sh4\dyna + + + hw\sh4\dyna + + + hw\sh4\dyna + + + hw\sh4\dyna + + + hw\sh4\dyna + + + hw\sh4\dyna + + + hw\sh4\dyna + + + hw\sh4\interpr + + + hw\sh4\modules + + + hw\sh4 + + + hw\sh4 + + + hw\sh4 + + + hw\sh4 + + + hw\sh4 + + + hw\sh4 + + + deps\libelf + + + deps\libelf + + + deps\libelf + + + deps\libelf + + + linux + + + deps\chdpsr + + + deps\libpng + + + deps\libpng + + + deps\libpng + + + deps\libpng + + + rend\gles + + + rend + + + rend + + + deps\libwebsocket + + + deps\libwebsocket + + + deps\libwebsocket + + + deps\libwebsocket + + + deps\libwebsocket + + + webui + + + deps\coreio + + + reios + + + reios + + + reios + + + reios + + + linux + + + oslib + + + rec-x86 + + + hw\naomi + + + hw\naomi + + + hw\naomi + + + cfg + + + hw\sh4\modules + + + rend\gl4 + + + rend\gl4 + + + hw\modem + + + hw\modem + + + hw\modem + + + hw\pvr + + + hw\pvr + + + hw\pvr + + + hw\pvr + + + hw\pvr + + + hw\pvr + + + hw\pvr + + + hw\pvr + + + deps\lzma + + + deps\lzma + + + deps\lzma + + + deps\lzma + + + deps\lzma + + + deps\lzma + + + deps\lzma + + + deps\lzma + + + deps\lzma + + + deps\lzma + + + deps\lzma + + + deps\lzma + + + deps\lzma + + + deps\lzma + + + deps\flac\include\FLAC + + + deps\flac\include\FLAC + + + deps\flac\include\FLAC + + + deps\flac\include\FLAC + + + deps\flac\include\FLAC + + + deps\flac\include\FLAC + + + deps\flac\include\FLAC + + + deps\flac\include\FLAC + + + deps\flac\include\FLAC + + + deps\flac\include\share + + + deps\flac\include\share + + + deps\flac\include\share + + + deps\flac\include\share + + + deps\flac\include\share + + + deps\flac\include\share + + + deps\flac\include\share + + + deps\chdr + + + deps\chdr + + + deps\chdr + + + deps\chdr + + + rend + + + rend + + + deps\imgui + + + deps\imgui + + + deps\imgui + + + deps\imgui + + + deps\imgui + + + deps\imgui + + + deps\imgui + + + deps\dirent + + + input + + + input + + + input + + + input + + + rend\gles + + + rend\gles + + + rend\gles + + + hw\naomi + + + hw\naomi + + + hw\naomi + + + hw\naomi + + + hw\naomi + + + hw\naomi + + + hw\naomi + + + hw\naomi + + + archive + + + archive + + + archive + + + windows + + + windows + + + + + deps\zlib + + + rec-ARM + + + rec-x86 + + + + + \ No newline at end of file From 8992549e47b4c10a8360c047a494cc22242319ad Mon Sep 17 00:00:00 2001 From: flyinghead Date: Sat, 30 Mar 2019 19:44:38 +0100 Subject: [PATCH 212/319] appveyor: archive stripped exe to save space --- appveyor.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/appveyor.yml b/appveyor.yml index dab32d51c..51b133577 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -18,7 +18,7 @@ build_script: after_build: - cmd: cd ..\.. - cmd: mkdir artifacts - - cmd: move shell\linux\reicast.exe artifacts/reicast-win_x64-fast-%APPVEYOR_REPO_COMMIT%.exe + - cmd: move shell\linux\nosym-reicast.exe artifacts/reicast-win_x64-fast-%APPVEYOR_REPO_COMMIT%.exe - cmd: copy %EXTRA_PATH%\libgcc_s_seh-1.dll artifacts - cmd: copy %EXTRA_PATH%\libwinpthread-1.dll artifacts - cmd: copy %EXTRA_PATH%\libgomp-1.dll artifacts From d945b2b8de98f14af0079c2154a7cec0a52db6c8 Mon Sep 17 00:00:00 2001 From: flyinghead Date: Sat, 30 Mar 2019 11:22:51 +0100 Subject: [PATCH 213/319] win32: allow window to be resized/maximized. Save size and max state. --- core/windows/winmain.cpp | 37 ++++++++++++++++++++++++++----------- 1 file changed, 26 insertions(+), 11 deletions(-) diff --git a/core/windows/winmain.cpp b/core/windows/winmain.cpp index d8d0e19df..480367f56 100644 --- a/core/windows/winmain.cpp +++ b/core/windows/winmain.cpp @@ -232,9 +232,10 @@ void UpdateInputState(u32 port) #define WINDOW_CLASS "nilDC" // Width and height of the window -#define WINDOW_WIDTH 1280 -#define WINDOW_HEIGHT 720 - +#define DEFAULT_WINDOW_WIDTH 1280 +#define DEFAULT_WINDOW_HEIGHT 720 +extern int screen_width, screen_height; +static bool window_maximized = false; LRESULT CALLBACK WndProc2(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) { @@ -259,6 +260,12 @@ LRESULT CALLBACK WndProc2(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) PostQuitMessage(0); return 1; + case WM_SIZE: + screen_width = LOWORD(lParam); + screen_height = HIWORD(lParam); + window_maximized = (wParam & SIZE_MAXIMIZED) != 0; + return 0; + case WM_LBUTTONDOWN: case WM_LBUTTONUP: case WM_MBUTTONDOWN: @@ -293,8 +300,8 @@ LRESULT CALLBACK WndProc2(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) static int prev_y = -1; int xPos = GET_X_LPARAM(lParam); int yPos = GET_Y_LPARAM(lParam); - mo_x_abs = (xPos - (WINDOW_WIDTH - 640 * WINDOW_HEIGHT / 480) / 2) * 480 / WINDOW_HEIGHT; - mo_y_abs = yPos * 480 / WINDOW_HEIGHT; + mo_x_abs = (xPos - (screen_width - 640 * screen_height / 480) / 2) * 480 / screen_height; + mo_y_abs = yPos * 480 / screen_height; mo_buttons = 0xffffffff; if (wParam & MK_LBUTTON) mo_buttons &= ~(1 << 2); @@ -354,12 +361,13 @@ void os_CreateWindow() sWC.cbWndExtra = 0; sWC.hInstance = (HINSTANCE)GetModuleHandle(0); sWC.hIcon = 0; - sWC.hCursor = 0; + sWC.hCursor = LoadCursor(NULL, IDC_ARROW); sWC.lpszMenuName = 0; sWC.hbrBackground = (HBRUSH) GetStockObject(WHITE_BRUSH); sWC.lpszClassName = WINDOW_CLASS; - unsigned int nWidth = WINDOW_WIDTH; - unsigned int nHeight = WINDOW_HEIGHT; + screen_width = cfgLoadInt("windows", "width", DEFAULT_WINDOW_WIDTH); + screen_height = cfgLoadInt("windows", "height", DEFAULT_WINDOW_HEIGHT); + window_maximized = cfgLoadBool("windows", "maximized", false); ATOM registerClass = RegisterClass(&sWC); if (!registerClass) @@ -369,9 +377,10 @@ void os_CreateWindow() // Create the eglWindow RECT sRect; - SetRect(&sRect, 0, 0, nWidth, nHeight); - AdjustWindowRectEx(&sRect, WS_CAPTION | WS_SYSMENU, false, 0); - HWND hWnd = CreateWindow( WINDOW_CLASS, VER_FULLNAME, WS_VISIBLE | WS_SYSMENU, + SetRect(&sRect, 0, 0, screen_width, screen_height); + AdjustWindowRectEx(&sRect, WS_OVERLAPPEDWINDOW, false, 0); + + HWND hWnd = CreateWindow( WINDOW_CLASS, VER_FULLNAME, WS_VISIBLE | WS_OVERLAPPEDWINDOW | (window_maximized ? WS_MAXIMIZE : 0), 0, 0, sRect.right-sRect.left, sRect.bottom-sRect.top, NULL, NULL, sWC.hInstance, NULL); window_win=hWnd; @@ -685,6 +694,12 @@ int CALLBACK WinMain(HINSTANCE hInstance,HINSTANCE hPrevInstance,LPSTR lpCmdLine } #endif SetUnhandledExceptionFilter(0); + cfgSaveBool("windows", "maximized", window_maximized); + if (!window_maximized) + { + cfgSaveInt("windows", "width", screen_width); + cfgSaveInt("windows", "height", screen_height); + } return 0; } From d7fcc80be4693b2aedc59097586e993837ea7403 Mon Sep 17 00:00:00 2001 From: flyinghead Date: Sat, 30 Mar 2019 11:27:14 +0100 Subject: [PATCH 214/319] win32: don't save window size if minimized --- core/windows/winmain.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/windows/winmain.cpp b/core/windows/winmain.cpp index 480367f56..1731193e9 100644 --- a/core/windows/winmain.cpp +++ b/core/windows/winmain.cpp @@ -695,7 +695,7 @@ int CALLBACK WinMain(HINSTANCE hInstance,HINSTANCE hPrevInstance,LPSTR lpCmdLine #endif SetUnhandledExceptionFilter(0); cfgSaveBool("windows", "maximized", window_maximized); - if (!window_maximized) + if (!window_maximized && screen_width != 0 && screen_width != 0) { cfgSaveInt("windows", "width", screen_width); cfgSaveInt("windows", "height", screen_height); From 9b48874e87abfd5b8ae736b04d7ebcc6872bb35a Mon Sep 17 00:00:00 2001 From: flyinghead Date: Sat, 30 Mar 2019 19:44:38 +0100 Subject: [PATCH 215/319] appveyor: archive stripped exe to save space --- appveyor.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/appveyor.yml b/appveyor.yml index dab32d51c..51b133577 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -18,7 +18,7 @@ build_script: after_build: - cmd: cd ..\.. - cmd: mkdir artifacts - - cmd: move shell\linux\reicast.exe artifacts/reicast-win_x64-fast-%APPVEYOR_REPO_COMMIT%.exe + - cmd: move shell\linux\nosym-reicast.exe artifacts/reicast-win_x64-fast-%APPVEYOR_REPO_COMMIT%.exe - cmd: copy %EXTRA_PATH%\libgcc_s_seh-1.dll artifacts - cmd: copy %EXTRA_PATH%\libwinpthread-1.dll artifacts - cmd: copy %EXTRA_PATH%\libgomp-1.dll artifacts From 2d03662a9ac3137c5b28a16294b0f1001da57abd Mon Sep 17 00:00:00 2001 From: Flyinghead Date: Wed, 3 Apr 2019 18:39:57 +0200 Subject: [PATCH 216/319] android: run the vibrator in a separate thread avoid lag when spamming the touchscreen controls --- .../emulator/emu/VirtualJoystickDelegate.java | 55 +++++++++++++++++-- 1 file changed, 51 insertions(+), 4 deletions(-) diff --git a/shell/android-studio/reicast/src/main/java/com/reicast/emulator/emu/VirtualJoystickDelegate.java b/shell/android-studio/reicast/src/main/java/com/reicast/emulator/emu/VirtualJoystickDelegate.java index 68f35545b..aeddce39b 100644 --- a/shell/android-studio/reicast/src/main/java/com/reicast/emulator/emu/VirtualJoystickDelegate.java +++ b/shell/android-studio/reicast/src/main/java/com/reicast/emulator/emu/VirtualJoystickDelegate.java @@ -15,7 +15,7 @@ import com.reicast.emulator.periph.InputDeviceManager; import com.reicast.emulator.periph.VJoy; public class VirtualJoystickDelegate { - private Vibrator vib; + private VibratorThread vibratorThread; private boolean editVjoyMode = false; private int selectedVjoyElement = -1; @@ -39,7 +39,10 @@ public class VirtualJoystickDelegate { public VirtualJoystickDelegate(View view) { this.view = view; this.context = view.getContext(); - vib = (Vibrator)context.getSystemService(Context.VIBRATOR_SERVICE); + + vibratorThread = new VibratorThread(context); + vibratorThread.start(); + readCustomVjoyValues(); scaleGestureDetector = new ScaleGestureDetector(context, new OscOnScaleGestureListener()); } @@ -224,8 +227,9 @@ public class VirtualJoystickDelegate { if (y > vjoy[j][1] && y <= (vjoy[j][1] + vjoy[j][3])) { if (vjoy[j][4] >= -2) { if (vjoy[j][5] == 0) - if (!editVjoyMode && Emulator.vibrationDuration > 0) - vib.vibrate(Emulator.vibrationDuration); + if (!editVjoyMode) { + vibratorThread.vibrate(); + } vjoy[j][5] = 2; } @@ -397,4 +401,47 @@ public class VirtualJoystickDelegate { selectedVjoyElement = -1; } } + + private class VibratorThread extends Thread + { + private Vibrator vibrator; + private boolean vibrate = false; + private boolean stopping = false; + + VibratorThread(Context context) { + vibrator = (Vibrator)context.getSystemService(Context.VIBRATOR_SERVICE); + } + + @Override + public void run() { + while (!stopping) { + synchronized (this) { + try { + this.wait(); + } catch (InterruptedException e) { + } + if (vibrate) { + vibrator.vibrate(Emulator.vibrationDuration); + vibrate = false; + } + } + } + } + + public void stopVibrator() { + synchronized (this) { + stopping = true; + notify(); + } + } + + public void vibrate() { + if (Emulator.vibrationDuration > 0) { + synchronized (this) { + vibrate = true; + notify(); + } + } + } + } } From cc26e255e04db5f871b80d9eaeb4c27bb5485211 Mon Sep 17 00:00:00 2001 From: Flyinghead Date: Wed, 3 Apr 2019 18:49:40 +0200 Subject: [PATCH 217/319] android: vibrate out of the synchronized section --- .../com/reicast/emulator/emu/VirtualJoystickDelegate.java | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/shell/android-studio/reicast/src/main/java/com/reicast/emulator/emu/VirtualJoystickDelegate.java b/shell/android-studio/reicast/src/main/java/com/reicast/emulator/emu/VirtualJoystickDelegate.java index aeddce39b..e3e142adf 100644 --- a/shell/android-studio/reicast/src/main/java/com/reicast/emulator/emu/VirtualJoystickDelegate.java +++ b/shell/android-studio/reicast/src/main/java/com/reicast/emulator/emu/VirtualJoystickDelegate.java @@ -415,16 +415,20 @@ public class VirtualJoystickDelegate { @Override public void run() { while (!stopping) { + boolean doVibrate; synchronized (this) { + doVibrate = false; try { this.wait(); } catch (InterruptedException e) { } if (vibrate) { - vibrator.vibrate(Emulator.vibrationDuration); + doVibrate = true; vibrate = false; } } + if (doVibrate) + vibrator.vibrate(Emulator.vibrationDuration); } } From eadf3047c0560045af9fb3e1bdb8d1b57fcdcf0b Mon Sep 17 00:00:00 2001 From: Flyinghead Date: Wed, 3 Apr 2019 20:24:28 +0000 Subject: [PATCH 218/319] gles: no runtime load of libGLESv2 on non-android platforms --- core/rend/gles/gles.cpp | 3 +++ core/rend/gles/gles.h | 2 ++ 2 files changed, 5 insertions(+) diff --git a/core/rend/gles/gles.cpp b/core/rend/gles/gles.cpp index 6965ec97d..8aab28fe0 100644 --- a/core/rend/gles/gles.cpp +++ b/core/rend/gles/gles.cpp @@ -560,7 +560,10 @@ GLuint fogTextureId; return false; } #ifdef GLES +// EGL only supports runtime loading with android? TDB +#ifdef _ANDROID load_gles_symbols(); +#endif #else egl_makecurrent(); if (gl3wInit()) diff --git a/core/rend/gles/gles.h b/core/rend/gles/gles.h index 2b907c156..4fb98e656 100755 --- a/core/rend/gles/gles.h +++ b/core/rend/gles/gles.h @@ -15,7 +15,9 @@ #endif #include #include +#ifdef _ANDROID #include "gl32funcs.h" +#endif #ifndef GL_NV_draw_path //IMGTEC GLES emulation From 125cccaa5a1e82155c868739efa471f7abde1254 Mon Sep 17 00:00:00 2001 From: Flyinghead Date: Thu, 4 Apr 2019 08:10:32 +0000 Subject: [PATCH 219/319] gles: only use GLES3 functions on android --- core/rend/gles/gldraw.cpp | 6 ++++-- core/rend/gles/gles.cpp | 4 ++++ core/rend/gles/imgui_impl_opengl3.cpp | 16 ++++++++++++++-- 3 files changed, 22 insertions(+), 4 deletions(-) diff --git a/core/rend/gles/gldraw.cpp b/core/rend/gles/gldraw.cpp index 5ed09a66e..4d58204b8 100644 --- a/core/rend/gles/gldraw.cpp +++ b/core/rend/gles/gldraw.cpp @@ -934,9 +934,10 @@ void SetMVS_Mode(ModifierVolumeMode mv_mode, ISP_Modvol ispc) static void SetupMainVBO() { +#if !defined(GLES) || defined(_ANDROID) if (gl.gl_major >= 3) glBindVertexArray(gl.vbo.vao); - +#endif glBindBuffer(GL_ARRAY_BUFFER, gl.vbo.geometry); glCheck(); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, gl.vbo.idxs); glCheck(); @@ -956,9 +957,10 @@ static void SetupMainVBO() void SetupModvolVBO() { +#if !defined(GLES) || defined(_ANDROID) if (gl.gl_major >= 3) glBindVertexArray(gl.vbo.vao); - +#endif glBindBuffer(GL_ARRAY_BUFFER, gl.vbo.modvols); glCheck(); //setup vertex buffers attrib pointers diff --git a/core/rend/gles/gles.cpp b/core/rend/gles/gles.cpp index 8aab28fe0..3bf1ecfb7 100644 --- a/core/rend/gles/gles.cpp +++ b/core/rend/gles/gles.cpp @@ -1150,7 +1150,9 @@ bool gl_create_resources() //create vao //This is really not "proper", vaos are supposed to be defined once //i keep updating the same one to make the es2 code work in 3.1 context +#if !defined(GLES) || defined(_ANDROID) glGenVertexArrays(1, &gl.vbo.vao); +#endif } //create vbos @@ -1991,8 +1993,10 @@ struct glesrend : Renderer void DrawOSD(bool clear_screen) { +#if !defined(GLES) || defined(_ANDROID) if (gl.gl_major >= 3) glBindVertexArray(gl.vbo.vao); +#endif glBindBuffer(GL_ARRAY_BUFFER, gl.vbo.geometry); glCheck(); glEnableVertexAttribArray(VERTEX_POS_ARRAY); glVertexAttribPointer(VERTEX_POS_ARRAY, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), (void*)offsetof(Vertex,x)); diff --git a/core/rend/gles/imgui_impl_opengl3.cpp b/core/rend/gles/imgui_impl_opengl3.cpp index fce30f9fa..3107feaf2 100644 --- a/core/rend/gles/imgui_impl_opengl3.cpp +++ b/core/rend/gles/imgui_impl_opengl3.cpp @@ -161,6 +161,7 @@ void ImGui_ImplOpenGL3_RenderDrawData(ImDrawData* draw_data, bool save_backgr if (save_background) { +#if !defined(GLES) || defined(_ANDROID) if (!gl.is_gles && glReadBuffer != NULL) glReadBuffer(GL_FRONT); @@ -177,6 +178,7 @@ void ImGui_ImplOpenGL3_RenderDrawData(ImDrawData* draw_data, bool save_backgr // Copy the current framebuffer into it glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 0, 0, fb_width, fb_height); +#endif } // Setup render state: alpha-blending enabled, no face culling, no depth testing, scissor enabled, polygon fill @@ -208,10 +210,12 @@ void ImGui_ImplOpenGL3_RenderDrawData(ImDrawData* draw_data, bool save_backgr glUseProgram(g_ShaderHandle); glUniform1i(g_AttribLocationTex, 0); glUniformMatrix4fv(g_AttribLocationProjMtx, 1, GL_FALSE, &ortho_projection[0][0]); +#if !defined(GLES) || defined(_ANDROID) if (gl.gl_major >= 3 && glBindSampler != NULL) glBindSampler(0, 0); // We use combined texture/sampler state. Applications using GL 3.3 may set that otherwise. - +#endif GLuint vao_handle = 0; +#if !defined(GLES) || defined(_ANDROID) if (gl.gl_major >= 3) { // Recreate the VAO every time @@ -219,6 +223,7 @@ void ImGui_ImplOpenGL3_RenderDrawData(ImDrawData* draw_data, bool save_backgr glGenVertexArrays(1, &vao_handle); glBindVertexArray(vao_handle); } +#endif glBindBuffer(GL_ARRAY_BUFFER, g_VboHandle); glEnableVertexAttribArray(g_AttribLocationPosition); glEnableVertexAttribArray(g_AttribLocationUV); @@ -267,18 +272,23 @@ void ImGui_ImplOpenGL3_RenderDrawData(ImDrawData* draw_data, bool save_backgr idx_buffer_offset += pcmd->ElemCount; } } +#if !defined(GLES) || defined(_ANDROID) if (vao_handle != 0) glDeleteVertexArrays(1, &vao_handle); - +#endif // Restore modified GL state glUseProgram(last_program); glBindTexture(GL_TEXTURE_2D, last_texture); +#if !defined(GLES) || defined(_ANDROID) if (gl.gl_major >= 3 && glBindSampler != NULL) glBindSampler(0, last_sampler); +#endif glActiveTexture(last_active_texture); glBindBuffer(GL_ARRAY_BUFFER, last_array_buffer); +#if !defined(GLES) || defined(_ANDROID) if (gl.gl_major >= 3) glBindVertexArray(last_vertex_array); +#endif glBlendEquationSeparate(last_blend_equation_rgb, last_blend_equation_alpha); glBlendFuncSeparate(last_blend_src_rgb, last_blend_dst_rgb, last_blend_src_alpha, last_blend_dst_alpha); if (last_enable_blend) glEnable(GL_BLEND); else glDisable(GL_BLEND); @@ -537,8 +547,10 @@ bool ImGui_ImplOpenGL3_CreateDeviceObjects() // Restore modified GL state glBindTexture(GL_TEXTURE_2D, last_texture); glBindBuffer(GL_ARRAY_BUFFER, last_array_buffer); +#if !defined(GLES) || defined(_ANDROID) if (gl.gl_major >= 3) glBindVertexArray(last_vertex_array); +#endif return true; } From f83a06d9c0e8847b1111c6f6f7ded5d7317bf18a Mon Sep 17 00:00:00 2001 From: Flyinghead Date: Thu, 4 Apr 2019 19:06:46 +0200 Subject: [PATCH 220/319] glcache was always disabled --- core/rend/gles/glcache.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/rend/gles/glcache.h b/core/rend/gles/glcache.h index d7f292af9..e245a368d 100644 --- a/core/rend/gles/glcache.h +++ b/core/rend/gles/glcache.h @@ -179,7 +179,7 @@ public: void DisableCache() { _disable_cache = true; } void EnableCache() { - _disable_cache = true; + _disable_cache = false; Reset(); } From 382279b12eebe7941003497f21b3b3aaf472bbb1 Mon Sep 17 00:00:00 2001 From: Flyinghead Date: Thu, 4 Apr 2019 19:07:09 +0200 Subject: [PATCH 221/319] imgui: use glcache --- core/rend/gles/imgui_impl_opengl3.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/core/rend/gles/imgui_impl_opengl3.cpp b/core/rend/gles/imgui_impl_opengl3.cpp index fce30f9fa..2857a19d5 100644 --- a/core/rend/gles/imgui_impl_opengl3.cpp +++ b/core/rend/gles/imgui_impl_opengl3.cpp @@ -67,6 +67,7 @@ #endif #include "gles.h" +#include "glcache.h" // OpenGL Data static char g_GlslVersionString[32] = ""; @@ -588,11 +589,10 @@ void ImGui_ImplOpenGL3_DrawBackground() ImTextureID ImGui_ImplOpenGL3_CreateVmuTexture(const unsigned int *data) { - GLuint tex_id; - glGenTextures(1, &tex_id); - glBindTexture(GL_TEXTURE_2D, tex_id); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + GLuint tex_id = glcache.GenTexture(); + glcache.BindTexture(GL_TEXTURE_2D, tex_id); + glcache.TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glcache.TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 48, 32, 0, GL_RGBA, GL_UNSIGNED_BYTE, data); return reinterpret_cast(tex_id); @@ -600,5 +600,5 @@ ImTextureID ImGui_ImplOpenGL3_CreateVmuTexture(const unsigned int *data) void ImGui_ImplOpenGL3_DeleteVmuTexture(ImTextureID tex_id) { - glDeleteTextures(1, &(GLuint &)tex_id); + glcache.DeleteTextures(1, &(GLuint &)tex_id); } From 994d52e012e28d79d44d9e392f5f26f5319afa5e Mon Sep 17 00:00:00 2001 From: Flyinghead Date: Thu, 4 Apr 2019 19:08:21 +0200 Subject: [PATCH 222/319] gl: put shaders in a map and set uniforms once for all before each frame --- core/rend/gles/gldraw.cpp | 46 +++++++----------- core/rend/gles/gles.cpp | 99 +++++++++++---------------------------- core/rend/gles/gles.h | 9 ++-- 3 files changed, 49 insertions(+), 105 deletions(-) diff --git a/core/rend/gles/gldraw.cpp b/core/rend/gles/gldraw.cpp index 5ed09a66e..e735ea99f 100644 --- a/core/rend/gles/gldraw.cpp +++ b/core/rend/gles/gldraw.cpp @@ -174,27 +174,23 @@ __forceinline bool color_clamp = gp->tsp.ColorClamp && (pvrrc.fog_clamp_min != 0 || pvrrc.fog_clamp_max != 0xffffffff); - CurrentShader = &gl.pogram_table[ - GetProgramID(Type == ListType_Punch_Through ? 1 : 0, - SetTileClip(gp->tileclip, -1) + 1, - gp->pcw.Texture, - gp->tsp.UseAlpha, - gp->tsp.IgnoreTexA, - gp->tsp.ShadInstr, - gp->pcw.Offset, - gp->tsp.FogCtrl, - gp->pcw.Gouraud, - gp->tcw.PixelFmt == PixelBumpMap, - color_clamp, - ShaderUniforms.trilinear_alpha != 1.f)]; + CurrentShader = GetProgram(Type == ListType_Punch_Through ? 1 : 0, + SetTileClip(gp->tileclip, -1) + 1, + gp->pcw.Texture, + gp->tsp.UseAlpha, + gp->tsp.IgnoreTexA, + gp->tsp.ShadInstr, + gp->pcw.Offset, + gp->tsp.FogCtrl, + gp->pcw.Gouraud, + gp->tcw.PixelFmt == PixelBumpMap, + color_clamp, + ShaderUniforms.trilinear_alpha != 1.f); - if (CurrentShader->program == -1) - CompilePipelineShader(CurrentShader); - else - { - glcache.UseProgram(CurrentShader->program); - ShaderUniforms.Set(CurrentShader); - } + glcache.UseProgram(CurrentShader->program); + if (CurrentShader->trilinear_alpha != -1) + glUniform1f(CurrentShader->trilinear_alpha, ShaderUniforms.trilinear_alpha); + SetTileClip(gp->tileclip, CurrentShader->pp_ClipTest); //This bit control which pixels are affected @@ -1122,14 +1118,8 @@ static void DrawQuad(GLuint texId, float x, float y, float w, float h, float u0, ShaderUniforms.trilinear_alpha = 1.0; - PipelineShader *shader = &gl.pogram_table[GetProgramID(0, 1, 1, 0, 1, 0, 0, 2, false, false, false, false)]; - if (shader->program == -1) - CompilePipelineShader(shader); - else - { - glcache.UseProgram(shader->program); - ShaderUniforms.Set(shader); - } + PipelineShader *shader = GetProgram(0, 1, 1, 0, 1, 0, 0, 2, false, false, false, false); + glcache.UseProgram(shader->program); glActiveTexture(GL_TEXTURE0); glcache.BindTexture(GL_TEXTURE_2D, texId); diff --git a/core/rend/gles/gles.cpp b/core/rend/gles/gles.cpp index 6965ec97d..e88b70a7a 100644 --- a/core/rend/gles/gles.cpp +++ b/core/rend/gles/gles.cpp @@ -865,7 +865,7 @@ static void gles_term() gl_free_osd_resources(); free_output_framebuffer(); - memset(gl.pogram_table, 0, sizeof(gl.pogram_table)); + gl.shaders.clear(); gl_term(); } @@ -1014,7 +1014,7 @@ GLuint gl_CompileAndLink(const char* VertexShader, const char* FragmentShader) return program; } -int GetProgramID(u32 cp_AlphaTest, u32 pp_ClipTestMode, +PipelineShader *GetProgram(u32 cp_AlphaTest, u32 pp_ClipTestMode, u32 pp_Texture, u32 pp_UseAlpha, u32 pp_IgnoreTexA, u32 pp_ShadInstr, u32 pp_Offset, u32 pp_FogCtrl, bool pp_Gouraud, bool pp_BumpMap, bool fog_clamping, bool trilinear) { @@ -1033,7 +1033,25 @@ int GetProgramID(u32 cp_AlphaTest, u32 pp_ClipTestMode, rv<<=1; rv|=fog_clamping; rv<<=1; rv|=trilinear; - return rv; + PipelineShader *shader = &gl.shaders[rv]; + if (shader->program == 0) + { + shader->cp_AlphaTest = cp_AlphaTest; + shader->pp_ClipTestMode = pp_ClipTestMode-1; + shader->pp_Texture = pp_Texture; + shader->pp_UseAlpha = pp_UseAlpha; + shader->pp_IgnoreTexA = pp_IgnoreTexA; + shader->pp_ShadInstr = pp_ShadInstr; + shader->pp_Offset = pp_Offset; + shader->pp_FogCtrl = pp_FogCtrl; + shader->pp_Gouraud = pp_Gouraud; + shader->pp_BumpMap = pp_BumpMap; + shader->fog_clamping = fog_clamping; + shader->trilinear = trilinear; + CompilePipelineShader(shader); + } + + return shader; } bool CompilePipelineShader( PipelineShader* s) @@ -1156,65 +1174,6 @@ bool gl_create_resources() glGenBuffers(1, &gl.vbo.idxs); glGenBuffers(1, &gl.vbo.idxs2); - memset(gl.pogram_table,0,sizeof(gl.pogram_table)); - - PipelineShader* dshader=0; - u32 compile=0; -#define forl(name,max) for(u32 name=0;name<=max;name++) - forl(cp_AlphaTest,1) - { - forl(pp_ClipTestMode,2) - { - forl(pp_UseAlpha,1) - { - forl(pp_Texture,1) - { - forl(pp_FogCtrl,3) - { - forl(pp_IgnoreTexA,1) - { - forl(pp_ShadInstr,3) - { - forl(pp_Offset,1) - { - forl(pp_Gouraud,1) - { - forl(pp_BumpMap,1) - { - forl(fog_clamping,1) - { - forl(trilinear,1) - { - dshader=&gl.pogram_table[GetProgramID(cp_AlphaTest,pp_ClipTestMode,pp_Texture,pp_UseAlpha,pp_IgnoreTexA, - pp_ShadInstr,pp_Offset,pp_FogCtrl, (bool)pp_Gouraud, (bool)pp_BumpMap, (bool)fog_clamping, - (bool)trilinear)]; - - dshader->cp_AlphaTest = cp_AlphaTest; - dshader->pp_ClipTestMode = pp_ClipTestMode-1; - dshader->pp_Texture = pp_Texture; - dshader->pp_UseAlpha = pp_UseAlpha; - dshader->pp_IgnoreTexA = pp_IgnoreTexA; - dshader->pp_ShadInstr = pp_ShadInstr; - dshader->pp_Offset = pp_Offset; - dshader->pp_FogCtrl = pp_FogCtrl; - dshader->pp_Gouraud = pp_Gouraud; - dshader->pp_BumpMap = pp_BumpMap; - dshader->fog_clamping = fog_clamping; - dshader->trilinear = trilinear; - dshader->program = -1; - } - } - } - } - } - } - } - } - } - } - } - } - char vshader[8192]; sprintf(vshader, VertexShaderSource, gl.glsl_version_header, gl.gl_version, 1); char fshader[8192]; @@ -1782,16 +1741,12 @@ bool RenderFrame() ShaderUniforms.PT_ALPHA=(PT_ALPHA_REF&0xFF)/255.0f; -// for (u32 i=0;iprogram == -1) -// continue; -// -// glcache.UseProgram(s->program); -// -// ShaderUniforms.Set(s); -// } + for (auto it : gl.shaders) + { + glcache.UseProgram(it.second.program); + ShaderUniforms.Set(&it.second); + } + //setup render target first if (is_rtt) { diff --git a/core/rend/gles/gles.h b/core/rend/gles/gles.h index 2b907c156..282cb27fb 100755 --- a/core/rend/gles/gles.h +++ b/core/rend/gles/gles.h @@ -1,4 +1,5 @@ #pragma once +#include #include "rend/rend.h" #if (defined(GLES) && !defined(TARGET_NACL32) && HOST_OS != OS_DARWIN && !defined(USE_SDL)) || defined(_ANDROID) @@ -93,7 +94,8 @@ struct gl_ctx } modvol_shader; - PipelineShader pogram_table[24576]; + std::map shaders; + struct { GLuint program; @@ -176,7 +178,7 @@ void free_output_framebuffer(); void HideOSD(); void OSD_DRAW(bool clear_screen); -int GetProgramID(u32 cp_AlphaTest, u32 pp_ClipTestMode, +PipelineShader *GetProgram(u32 cp_AlphaTest, u32 pp_ClipTestMode, u32 pp_Texture, u32 pp_UseAlpha, u32 pp_IgnoreTexA, u32 pp_ShadInstr, u32 pp_Offset, u32 pp_FogCtrl, bool pp_Gouraud, bool pp_BumpMap, bool fog_clamping, bool trilinear); @@ -223,9 +225,6 @@ extern struct ShaderUniforms_t if (s->sp_FOG_COL_VERT!=-1) glUniform3fv( s->sp_FOG_COL_VERT, 1, ps_FOG_COL_VERT); - if (s->trilinear_alpha != -1) - glUniform1f(s->trilinear_alpha, trilinear_alpha); - if (s->fog_clamp_min != -1) glUniform4fv(s->fog_clamp_min, 1, fog_clamp_min); if (s->fog_clamp_max != -1) From ec95c95b89178c9090c14b2f78c26a563593bef6 Mon Sep 17 00:00:00 2001 From: Flyinghead Date: Thu, 4 Apr 2019 19:26:15 +0200 Subject: [PATCH 223/319] add option to disable fog effects --- core/nullDC.cpp | 3 +++ core/rend/gl4/gldraw.cpp | 4 +++- core/rend/gles/gldraw.cpp | 3 ++- core/types.h | 1 + 4 files changed, 9 insertions(+), 2 deletions(-) diff --git a/core/nullDC.cpp b/core/nullDC.cpp index e770b3c08..89c637803 100755 --- a/core/nullDC.cpp +++ b/core/nullDC.cpp @@ -513,6 +513,7 @@ void InitSettings() settings.rend.CustomTextures = false; settings.rend.DumpTextures = false; settings.rend.ScreenScaling = 100; + settings.rend.Fog = true; settings.pvr.ta_skip = 0; settings.pvr.rend = 0; @@ -595,6 +596,7 @@ void LoadSettings(bool game_specific) settings.rend.DumpTextures = cfgLoadBool(config_section, "rend.DumpTextures", settings.rend.DumpTextures); settings.rend.ScreenScaling = cfgLoadInt(config_section, "rend.ScreenScaling", settings.rend.ScreenScaling); settings.rend.ScreenScaling = min(max(1, settings.rend.ScreenScaling), 100); + settings.rend.Fog = cfgLoadBool(config_section, "rend.Fog", settings.rend.Fog); settings.pvr.ta_skip = cfgLoadInt(config_section, "ta.skip", settings.pvr.ta_skip); settings.pvr.rend = cfgLoadInt(config_section, "pvr.rend", settings.pvr.rend); @@ -718,6 +720,7 @@ void SaveSettings() cfgSaveBool("config", "rend.CustomTextures", settings.rend.CustomTextures); cfgSaveBool("config", "rend.DumpTextures", settings.rend.DumpTextures); cfgSaveInt("config", "rend.ScreenScaling", settings.rend.ScreenScaling); + cfgSaveBool("config", "rend.Fog", settings.rend.Fog); cfgSaveInt("config", "ta.skip", settings.pvr.ta_skip); cfgSaveInt("config", "pvr.rend", settings.pvr.rend); diff --git a/core/rend/gl4/gldraw.cpp b/core/rend/gl4/gldraw.cpp index 3342325d7..21ec3e923 100644 --- a/core/rend/gl4/gldraw.cpp +++ b/core/rend/gl4/gldraw.cpp @@ -153,6 +153,8 @@ template bool two_volumes_mode = (gp->tsp1.full != -1) && Type != ListType_Translucent; bool color_clamp = gp->tsp.ColorClamp && (pvrrc.fog_clamp_min != 0 || pvrrc.fog_clamp_max != 0xffffffff); + int fog_ctrl = settings.rend.Fog ? gp->tsp.FogCtrl : 2; + int depth_func = 0; if (Type == ListType_Translucent) { @@ -169,7 +171,7 @@ template gp->tsp.IgnoreTexA, gp->tsp.ShadInstr, gp->pcw.Offset, - gp->tsp.FogCtrl, + fog_ctrl, two_volumes_mode, depth_func, gp->pcw.Gouraud, diff --git a/core/rend/gles/gldraw.cpp b/core/rend/gles/gldraw.cpp index e735ea99f..d337848cb 100644 --- a/core/rend/gles/gldraw.cpp +++ b/core/rend/gles/gldraw.cpp @@ -173,6 +173,7 @@ __forceinline ShaderUniforms.trilinear_alpha = 1.f; bool color_clamp = gp->tsp.ColorClamp && (pvrrc.fog_clamp_min != 0 || pvrrc.fog_clamp_max != 0xffffffff); + int fog_ctrl = settings.rend.Fog ? gp->tsp.FogCtrl : 2; CurrentShader = GetProgram(Type == ListType_Punch_Through ? 1 : 0, SetTileClip(gp->tileclip, -1) + 1, @@ -181,7 +182,7 @@ __forceinline gp->tsp.IgnoreTexA, gp->tsp.ShadInstr, gp->pcw.Offset, - gp->tsp.FogCtrl, + fog_ctrl, gp->pcw.Gouraud, gp->tcw.PixelFmt == PixelBumpMap, color_clamp, diff --git a/core/types.h b/core/types.h index 9d2e54d4d..a9a835ac1 100644 --- a/core/types.h +++ b/core/types.h @@ -633,6 +633,7 @@ struct settings_t bool CustomTextures; bool DumpTextures; int ScreenScaling; // in percent. 50 means half the native resolution + bool Fog; } rend; struct From 3a7393824063d9157f034bfb36c2ee069d8f4b37 Mon Sep 17 00:00:00 2001 From: Flyinghead Date: Thu, 4 Apr 2019 20:05:13 +0200 Subject: [PATCH 224/319] UI for fog disable option --- core/rend/gui.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/core/rend/gui.cpp b/core/rend/gui.cpp index 03e976d85..a8a35edf2 100644 --- a/core/rend/gui.cpp +++ b/core/rend/gui.cpp @@ -940,6 +940,9 @@ static void gui_display_settings() ImGui::Checkbox("Shadows", &settings.rend.ModifierVolumes); ImGui::SameLine(); ShowHelpMarker("Enable modifier volumes, usually used for shadows"); + ImGui::Checkbox("Fog", &settings.rend.Fog); + ImGui::SameLine(); + ShowHelpMarker("Enable fog effects"); ImGui::Checkbox("Widescreen", &settings.rend.WideScreen); ImGui::SameLine(); ShowHelpMarker("Draw geometry outside of the normal 4:3 aspect ratio. May produce graphical glitches in the revealed areas"); From 05df2f06e20c91954dab7fe861f56a3d383664d9 Mon Sep 17 00:00:00 2001 From: Flyinghead Date: Thu, 4 Apr 2019 22:26:21 +0200 Subject: [PATCH 225/319] imgui: use glcache, don't save/restore state gl4: backport shader map changes from gl renderer --- core/rend/gl4/gl4.h | 14 +--- core/rend/gl4/gldraw.cpp | 66 ++++++----------- core/rend/gl4/gles.cpp | 5 +- core/rend/gles/gles.h | 4 +- core/rend/gles/imgui_impl_opengl3.cpp | 102 ++++++-------------------- core/rend/gui.cpp | 18 ----- 6 files changed, 51 insertions(+), 158 deletions(-) diff --git a/core/rend/gl4/gl4.h b/core/rend/gl4/gl4.h index 7abc1db20..5ca4dd141 100755 --- a/core/rend/gl4/gl4.h +++ b/core/rend/gl4/gl4.h @@ -1,6 +1,6 @@ #pragma once #include "rend/gles/gles.h" -#include +#include void gl4DrawStrips(GLuint output_fbo); @@ -44,7 +44,7 @@ struct gl4_ctx GLuint extra_depth_scale; } modvol_shader; - std::map shaders; + std::unordered_map shaders; struct { @@ -53,16 +53,6 @@ struct gl4_ctx GLuint modvol_vao; GLuint tr_poly_params; } vbo; - - gl4PipelineShader *getShader(int programId) { - gl4PipelineShader *shader = shaders[programId]; - if (shader == NULL) { - shader = new gl4PipelineShader(); - shaders[programId] = shader; - shader->program = -1; - } - return shader; - } }; extern gl4_ctx gl4; diff --git a/core/rend/gl4/gldraw.cpp b/core/rend/gl4/gldraw.cpp index 21ec3e923..447a97461 100644 --- a/core/rend/gl4/gldraw.cpp +++ b/core/rend/gl4/gldraw.cpp @@ -45,7 +45,7 @@ static GLuint texSamplers[2]; static GLuint depth_fbo; GLuint depthSaveTexId; -static int gl4GetProgramID(u32 cp_AlphaTest, u32 pp_ClipTestMode, +static gl4PipelineShader *gl4GetProgram(u32 cp_AlphaTest, u32 pp_ClipTestMode, u32 pp_Texture, u32 pp_UseAlpha, u32 pp_IgnoreTexA, u32 pp_ShadInstr, u32 pp_Offset, u32 pp_FogCtrl, bool pp_TwoVolumes, u32 pp_DepthFunc, bool pp_Gouraud, bool pp_BumpMap, bool fog_clamping, int pass) { @@ -66,45 +66,27 @@ static int gl4GetProgramID(u32 cp_AlphaTest, u32 pp_ClipTestMode, rv <<= 1; rv |= fog_clamping; rv <<= 2; rv |= pass; - return rv; -} - -static void setCurrentShader(u32 cp_AlphaTest, u32 pp_ClipTestMode, - u32 pp_Texture, u32 pp_UseAlpha, u32 pp_IgnoreTexA, u32 pp_ShadInstr, u32 pp_Offset, - u32 pp_FogCtrl, bool pp_TwoVolumes, u32 pp_DepthFunc, bool pp_Gouraud, bool pp_BumpMap, bool fog_clamping, int pass) -{ - int shaderId = gl4GetProgramID(cp_AlphaTest, - pp_ClipTestMode + 1, - pp_Texture, - pp_UseAlpha, - pp_IgnoreTexA, - pp_ShadInstr, - pp_Offset, - pp_FogCtrl, - pp_TwoVolumes, - pp_DepthFunc, - pp_Gouraud, - pp_BumpMap, - fog_clamping, - pass); - CurrentShader = gl4.getShader(shaderId); - if (CurrentShader->program == -1) { - CurrentShader->cp_AlphaTest = cp_AlphaTest; - CurrentShader->pp_ClipTestMode = pp_ClipTestMode; - CurrentShader->pp_Texture = pp_Texture; - CurrentShader->pp_UseAlpha = pp_UseAlpha; - CurrentShader->pp_IgnoreTexA = pp_IgnoreTexA; - CurrentShader->pp_ShadInstr = pp_ShadInstr; - CurrentShader->pp_Offset = pp_Offset; - CurrentShader->pp_FogCtrl = pp_FogCtrl; - CurrentShader->pp_TwoVolumes = pp_TwoVolumes; - CurrentShader->pp_DepthFunc = pp_DepthFunc; - CurrentShader->pp_Gouraud = pp_Gouraud; - CurrentShader->pp_BumpMap = pp_BumpMap; - CurrentShader->fog_clamping = fog_clamping; - CurrentShader->pass = pass; - gl4CompilePipelineShader(CurrentShader); + gl4PipelineShader *shader = &gl4.shaders[rv]; + if (shader->program == 0) + { + shader->cp_AlphaTest = cp_AlphaTest; + shader->pp_ClipTestMode = pp_ClipTestMode; + shader->pp_Texture = pp_Texture; + shader->pp_UseAlpha = pp_UseAlpha; + shader->pp_IgnoreTexA = pp_IgnoreTexA; + shader->pp_ShadInstr = pp_ShadInstr; + shader->pp_Offset = pp_Offset; + shader->pp_FogCtrl = pp_FogCtrl; + shader->pp_TwoVolumes = pp_TwoVolumes; + shader->pp_DepthFunc = pp_DepthFunc; + shader->pp_Gouraud = pp_Gouraud; + shader->pp_BumpMap = pp_BumpMap; + shader->fog_clamping = fog_clamping; + shader->pass = pass; + gl4CompilePipelineShader(shader); } + + return shader; } static void SetTextureRepeatMode(int index, GLuint dir, u32 clamp, u32 mirror) @@ -132,7 +114,7 @@ template if (pass == 0) { - setCurrentShader(Type == ListType_Punch_Through ? 1 : 0, + CurrentShader = gl4GetProgram(Type == ListType_Punch_Through ? 1 : 0, clipping, Type == ListType_Punch_Through ? gp->pcw.Texture : 0, 1, @@ -164,7 +146,7 @@ template depth_func = gp->isp.DepthMode; } - setCurrentShader(Type == ListType_Punch_Through ? 1 : 0, + CurrentShader = gl4GetProgram(Type == ListType_Punch_Through ? 1 : 0, clipping, gp->pcw.Texture, gp->tsp.UseAlpha, @@ -683,7 +665,7 @@ static void gl4_draw_quad_texture(GLuint texture, bool upsideDown, float x = 0.f ShaderUniforms.trilinear_alpha = 1.0; - setCurrentShader(0, + CurrentShader = gl4GetProgram(0, 0, 1, 0, diff --git a/core/rend/gl4/gles.cpp b/core/rend/gl4/gles.cpp index 505b1d23f..5db1d336d 100644 --- a/core/rend/gl4/gles.cpp +++ b/core/rend/gl4/gles.cpp @@ -489,9 +489,8 @@ static void gles_term(void) glDeleteBuffers(1, &gl4.vbo.tr_poly_params); for (auto it = gl4.shaders.begin(); it != gl4.shaders.end(); it++) { - if (it->second->program != -1) - glDeleteProgram(it->second->program); - delete it->second; + if (it->second.program != 0) + glDeleteProgram(it->second.program); } gl4.shaders.clear(); glDeleteVertexArrays(1, &gl4.vbo.main_vao); diff --git a/core/rend/gles/gles.h b/core/rend/gles/gles.h index 282cb27fb..832ecf31a 100755 --- a/core/rend/gles/gles.h +++ b/core/rend/gles/gles.h @@ -1,5 +1,5 @@ #pragma once -#include +#include #include "rend/rend.h" #if (defined(GLES) && !defined(TARGET_NACL32) && HOST_OS != OS_DARWIN && !defined(USE_SDL)) || defined(_ANDROID) @@ -94,7 +94,7 @@ struct gl_ctx } modvol_shader; - std::map shaders; + std::unordered_map shaders; struct { diff --git a/core/rend/gles/imgui_impl_opengl3.cpp b/core/rend/gles/imgui_impl_opengl3.cpp index 2857a19d5..853e41563 100644 --- a/core/rend/gles/imgui_impl_opengl3.cpp +++ b/core/rend/gles/imgui_impl_opengl3.cpp @@ -128,28 +128,7 @@ void ImGui_ImplOpenGL3_RenderDrawData(ImDrawData* draw_data, bool save_backgr draw_data->ScaleClipRects(io.DisplayFramebufferScale); // Backup GL state - GLenum last_active_texture; glGetIntegerv(GL_ACTIVE_TEXTURE, (GLint*)&last_active_texture); glActiveTexture(GL_TEXTURE0); - GLint last_program; glGetIntegerv(GL_CURRENT_PROGRAM, &last_program); - GLint last_texture; glGetIntegerv(GL_TEXTURE_BINDING_2D, &last_texture); - GLint last_sampler; glGetIntegerv(GL_SAMPLER_BINDING, &last_sampler); - GLint last_array_buffer; glGetIntegerv(GL_ARRAY_BUFFER_BINDING, &last_array_buffer); - GLint last_vertex_array; glGetIntegerv(GL_VERTEX_ARRAY_BINDING, &last_vertex_array); -#ifdef GL_POLYGON_MODE - GLint last_polygon_mode[2]; glGetIntegerv(GL_POLYGON_MODE, last_polygon_mode); -#endif - GLint last_viewport[4]; glGetIntegerv(GL_VIEWPORT, last_viewport); - GLint last_scissor_box[4]; glGetIntegerv(GL_SCISSOR_BOX, last_scissor_box); - GLenum last_blend_src_rgb; glGetIntegerv(GL_BLEND_SRC_RGB, (GLint*)&last_blend_src_rgb); - GLenum last_blend_dst_rgb; glGetIntegerv(GL_BLEND_DST_RGB, (GLint*)&last_blend_dst_rgb); - GLenum last_blend_src_alpha; glGetIntegerv(GL_BLEND_SRC_ALPHA, (GLint*)&last_blend_src_alpha); - GLenum last_blend_dst_alpha; glGetIntegerv(GL_BLEND_DST_ALPHA, (GLint*)&last_blend_dst_alpha); - GLenum last_blend_equation_rgb; glGetIntegerv(GL_BLEND_EQUATION_RGB, (GLint*)&last_blend_equation_rgb); - GLenum last_blend_equation_alpha; glGetIntegerv(GL_BLEND_EQUATION_ALPHA, (GLint*)&last_blend_equation_alpha); - GLboolean last_enable_blend = glIsEnabled(GL_BLEND); - GLboolean last_enable_cull_face = glIsEnabled(GL_CULL_FACE); - GLboolean last_enable_depth_test = glIsEnabled(GL_DEPTH_TEST); - GLboolean last_enable_scissor_test = glIsEnabled(GL_SCISSOR_TEST); bool clip_origin_lower_left = true; #ifdef GL_CLIP_ORIGIN if (gl.gl_major >= 4 && glClipControl != NULL) @@ -167,13 +146,13 @@ void ImGui_ImplOpenGL3_RenderDrawData(ImDrawData* draw_data, bool save_backgr // (Re-)create the background texture and reserve space for it if (g_BackgroundTexture != 0) - glDeleteTextures(1, &g_BackgroundTexture); - glGenTextures(1, &g_BackgroundTexture); - glBindTexture(GL_TEXTURE_2D, g_BackgroundTexture); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + glcache.DeleteTextures(1, &g_BackgroundTexture); + g_BackgroundTexture = glcache.GenTexture(); + glcache.BindTexture(GL_TEXTURE_2D, g_BackgroundTexture); + glcache.TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glcache.TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glcache.TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glcache.TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, fb_width, fb_height, 0, GL_RGB, GL_UNSIGNED_BYTE, (GLvoid*)NULL); // Copy the current framebuffer into it @@ -181,12 +160,12 @@ void ImGui_ImplOpenGL3_RenderDrawData(ImDrawData* draw_data, bool save_backgr } // Setup render state: alpha-blending enabled, no face culling, no depth testing, scissor enabled, polygon fill - glEnable(GL_BLEND); + glcache.Enable(GL_BLEND); glBlendEquation(GL_FUNC_ADD); - glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - glDisable(GL_CULL_FACE); - glDisable(GL_DEPTH_TEST); - glEnable(GL_SCISSOR_TEST); + glcache.BlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + glcache.Disable(GL_CULL_FACE); + glcache.Disable(GL_DEPTH_TEST); + glcache.Enable(GL_SCISSOR_TEST); #ifdef GL_POLYGON_MODE if (glPolygonMode != NULL) glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); @@ -206,7 +185,7 @@ void ImGui_ImplOpenGL3_RenderDrawData(ImDrawData* draw_data, bool save_backgr { 0.0f, 0.0f, -1.0f, 0.0f }, { (R+L)/(L-R), (T+B)/(B-T), 0.0f, 1.0f }, }; - glUseProgram(g_ShaderHandle); + glcache.UseProgram(g_ShaderHandle); glUniform1i(g_AttribLocationTex, 0); glUniformMatrix4fv(g_AttribLocationProjMtx, 1, GL_FALSE, &ortho_projection[0][0]); if (gl.gl_major >= 3 && glBindSampler != NULL) @@ -261,7 +240,7 @@ void ImGui_ImplOpenGL3_RenderDrawData(ImDrawData* draw_data, bool save_backgr glScissor((int)clip_rect.x, (int)clip_rect.y, (int)clip_rect.z, (int)clip_rect.w); // Support for GL 4.5's glClipControl(GL_UPPER_LEFT) // Bind texture, Draw - glBindTexture(GL_TEXTURE_2D, (GLuint)(intptr_t)pcmd->TextureId); + glcache.BindTexture(GL_TEXTURE_2D, (GLuint)(intptr_t)pcmd->TextureId); glDrawElements(GL_TRIANGLES, (GLsizei)pcmd->ElemCount, sizeof(ImDrawIdx) == 2 ? GL_UNSIGNED_SHORT : GL_UNSIGNED_INT, idx_buffer_offset); } } @@ -270,28 +249,6 @@ void ImGui_ImplOpenGL3_RenderDrawData(ImDrawData* draw_data, bool save_backgr } if (vao_handle != 0) glDeleteVertexArrays(1, &vao_handle); - - // Restore modified GL state - glUseProgram(last_program); - glBindTexture(GL_TEXTURE_2D, last_texture); - if (gl.gl_major >= 3 && glBindSampler != NULL) - glBindSampler(0, last_sampler); - glActiveTexture(last_active_texture); - glBindBuffer(GL_ARRAY_BUFFER, last_array_buffer); - if (gl.gl_major >= 3) - glBindVertexArray(last_vertex_array); - glBlendEquationSeparate(last_blend_equation_rgb, last_blend_equation_alpha); - glBlendFuncSeparate(last_blend_src_rgb, last_blend_dst_rgb, last_blend_src_alpha, last_blend_dst_alpha); - if (last_enable_blend) glEnable(GL_BLEND); else glDisable(GL_BLEND); - if (last_enable_cull_face) glEnable(GL_CULL_FACE); else glDisable(GL_CULL_FACE); - if (last_enable_depth_test) glEnable(GL_DEPTH_TEST); else glDisable(GL_DEPTH_TEST); - if (last_enable_scissor_test) glEnable(GL_SCISSOR_TEST); else glDisable(GL_SCISSOR_TEST); -#ifdef GL_POLYGON_MODE - if (glPolygonMode != NULL) - glPolygonMode(GL_FRONT_AND_BACK, (GLenum)last_polygon_mode[0]); -#endif - glViewport(last_viewport[0], last_viewport[1], (GLsizei)last_viewport[2], (GLsizei)last_viewport[3]); - glScissor(last_scissor_box[0], last_scissor_box[1], (GLsizei)last_scissor_box[2], (GLsizei)last_scissor_box[3]); } bool ImGui_ImplOpenGL3_CreateFontsTexture() @@ -303,21 +260,16 @@ bool ImGui_ImplOpenGL3_CreateFontsTexture() io.Fonts->GetTexDataAsRGBA32(&pixels, &width, &height); // Load as RGBA 32-bits (75% of the memory is wasted, but default font is so small) because it is more likely to be compatible with user's existing shaders. If your ImTextureId represent a higher-level concept than just a GL texture id, consider calling GetTexDataAsAlpha8() instead to save on GPU memory. // Upload texture to graphics system - GLint last_texture; - glGetIntegerv(GL_TEXTURE_BINDING_2D, &last_texture); - glGenTextures(1, &g_FontTexture); - glBindTexture(GL_TEXTURE_2D, g_FontTexture); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + g_FontTexture = glcache.GenTexture(); + glcache.BindTexture(GL_TEXTURE_2D, g_FontTexture); + glcache.TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glcache.TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glPixelStorei(GL_UNPACK_ROW_LENGTH, 0); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, pixels); // Store our identifier io.Fonts->TexID = (ImTextureID)(intptr_t)g_FontTexture; - // Restore state - glBindTexture(GL_TEXTURE_2D, last_texture); - return true; } @@ -326,7 +278,7 @@ void ImGui_ImplOpenGL3_DestroyFontsTexture() if (g_FontTexture) { ImGuiIO& io = ImGui::GetIO(); - glDeleteTextures(1, &g_FontTexture); + glcache.DeleteTextures(1, &g_FontTexture); io.Fonts->TexID = 0; g_FontTexture = 0; } @@ -370,12 +322,6 @@ static bool CheckProgram(GLuint handle, const char* desc) bool ImGui_ImplOpenGL3_CreateDeviceObjects() { - // Backup GL state - GLint last_texture, last_array_buffer, last_vertex_array; - glGetIntegerv(GL_TEXTURE_BINDING_2D, &last_texture); - glGetIntegerv(GL_ARRAY_BUFFER_BINDING, &last_array_buffer); - glGetIntegerv(GL_VERTEX_ARRAY_BINDING, &last_vertex_array); - // Parse GLSL version string int glsl_version = 130; sscanf(g_GlslVersionString, "#version %d", &glsl_version); @@ -535,12 +481,6 @@ bool ImGui_ImplOpenGL3_CreateDeviceObjects() ImGui_ImplOpenGL3_CreateFontsTexture(); - // Restore modified GL state - glBindTexture(GL_TEXTURE_2D, last_texture); - glBindBuffer(GL_ARRAY_BUFFER, last_array_buffer); - if (gl.gl_major >= 3) - glBindVertexArray(last_vertex_array); - return true; } @@ -564,7 +504,7 @@ void ImGui_ImplOpenGL3_DestroyDeviceObjects() ImGui_ImplOpenGL3_DestroyFontsTexture(); if (g_BackgroundTexture != 0) - glDeleteTextures(1, &g_BackgroundTexture); + glcache.DeleteTextures(1, &g_BackgroundTexture); g_BackgroundTexture = 0; } @@ -582,7 +522,7 @@ void ImGui_ImplOpenGL3_DrawBackground() } else { - glClearColor(0, 0, 0, 0); + glcache.ClearColor(0, 0, 0, 0); glClear(GL_COLOR_BUFFER_BIT); } } diff --git a/core/rend/gui.cpp b/core/rend/gui.cpp index a8a35edf2..cfc134cbc 100644 --- a/core/rend/gui.cpp +++ b/core/rend/gui.cpp @@ -1408,24 +1408,6 @@ void gui_display_ui() gui_state = Closed; } -void gui_display_fps(const char *string) -{ - ImGui_Impl_NewFrame(); - ImGui::NewFrame(); - - ImGui::SetNextWindowBgAlpha(0); - ImGui::SetNextWindowPos(ImVec2(0, screen_height), ImGuiCond_Always, ImVec2(0.f, 1.f)); // Lower left corner - - ImGui::Begin("##fps", NULL, ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_NoNav - | ImGuiWindowFlags_NoInputs | ImGuiWindowFlags_NoBackground); - ImGui::SetWindowFontScale(2); - ImGui::TextColored(ImVec4(1, 1, 0, 0.7), "%s", string); - ImGui::End(); - - ImGui::Render(); - ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData()); -} - static float LastFPSTime; static int lastFrameCount = 0; static float fps = -1; From d524e3381d72b5b8d3c2c36863718e38b8b0ff51 Mon Sep 17 00:00:00 2001 From: Flyinghead Date: Thu, 4 Apr 2019 22:54:36 +0200 Subject: [PATCH 226/319] only update the fog texture if fog is enabled --- core/rend/gl4/gles.cpp | 2 +- core/rend/gles/gles.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/core/rend/gl4/gles.cpp b/core/rend/gl4/gles.cpp index 5db1d336d..ecda63dec 100644 --- a/core/rend/gl4/gles.cpp +++ b/core/rend/gl4/gles.cpp @@ -703,7 +703,7 @@ static bool RenderFrame() gl4ShaderUniforms.fog_clamp_max[2] = ((pvrrc.fog_clamp_max >> 0) & 0xFF) / 255.0f; gl4ShaderUniforms.fog_clamp_max[3] = ((pvrrc.fog_clamp_max >> 24) & 0xFF) / 255.0f; - if (fog_needs_update) + if (fog_needs_update && settings.rend.Fog) { fog_needs_update = false; UpdateFogTexture((u8 *)FOG_TABLE, GL_TEXTURE5, GL_RED); diff --git a/core/rend/gles/gles.cpp b/core/rend/gles/gles.cpp index e88b70a7a..b97d9e4e2 100644 --- a/core/rend/gles/gles.cpp +++ b/core/rend/gles/gles.cpp @@ -1727,7 +1727,7 @@ bool RenderFrame() ShaderUniforms.fog_clamp_max[2] = ((pvrrc.fog_clamp_max >> 0) & 0xFF) / 255.0f; ShaderUniforms.fog_clamp_max[3] = ((pvrrc.fog_clamp_max >> 24) & 0xFF) / 255.0f; - if (fog_needs_update) + if (fog_needs_update && settings.rend.Fog) { fog_needs_update = false; UpdateFogTexture((u8 *)FOG_TABLE, GL_TEXTURE1, gl.fog_image_format); From 0b7024f6a084abd1ec60b57f536eb77ef9b1feb5 Mon Sep 17 00:00:00 2001 From: Flyinghead Date: Fri, 5 Apr 2019 14:48:59 +0200 Subject: [PATCH 227/319] Avoid opposite dpad buttons being both down. Ignore android key repeats --- core/input/gamepad_device.cpp | 32 +++++++++++++++++++ .../com/reicast/emulator/BaseGLActivity.java | 28 ++++++++-------- 2 files changed, 47 insertions(+), 13 deletions(-) diff --git a/core/input/gamepad_device.cpp b/core/input/gamepad_device.cpp index 843f18d62..82ccefdf8 100644 --- a/core/input/gamepad_device.cpp +++ b/core/input/gamepad_device.cpp @@ -51,7 +51,39 @@ bool GamepadDevice::gamepad_btn_input(u32 code, bool pressed) if (key < 0x10000) { if (pressed) + { kcode[_maple_port] &= ~(u16)key; + // Avoid two opposite dpad keys being pressed simultaneously + switch (key) + { + case DC_DPAD_UP: + kcode[_maple_port] |= (u16)DC_DPAD_DOWN; + break; + case DC_DPAD_DOWN: + kcode[_maple_port] |= (u16)DC_DPAD_UP; + break; + case DC_DPAD_LEFT: + kcode[_maple_port] |= (u16)DC_DPAD_RIGHT; + break; + case DC_DPAD_RIGHT: + kcode[_maple_port] |= (u16)DC_DPAD_LEFT; + break; + case DC_DPAD2_UP: + kcode[_maple_port] |= (u16)DC_DPAD2_DOWN; + break; + case DC_DPAD2_DOWN: + kcode[_maple_port] |= (u16)DC_DPAD2_UP; + break; + case DC_DPAD2_LEFT: + kcode[_maple_port] |= (u16)DC_DPAD2_RIGHT; + break; + case DC_DPAD2_RIGHT: + kcode[_maple_port] |= (u16)DC_DPAD2_LEFT; + break; + default: + break; + } + } else kcode[_maple_port] |= (u16)key; } diff --git a/shell/android-studio/reicast/src/main/java/com/reicast/emulator/BaseGLActivity.java b/shell/android-studio/reicast/src/main/java/com/reicast/emulator/BaseGLActivity.java index f06e02c9a..a0b28e302 100644 --- a/shell/android-studio/reicast/src/main/java/com/reicast/emulator/BaseGLActivity.java +++ b/shell/android-studio/reicast/src/main/java/com/reicast/emulator/BaseGLActivity.java @@ -228,22 +228,24 @@ public abstract class BaseGLActivity extends Activity implements ActivityCompat. @Override public boolean onKeyDown(int keyCode, KeyEvent event) { - if (keyCode == KeyEvent.KEYCODE_BACK) { - if (!JNIdc.guiIsOpen()) { - showMenu(); - return true; + if (event.getRepeatCount() == 0) { + if (keyCode == KeyEvent.KEYCODE_BACK) { + if (!JNIdc.guiIsOpen()) { + showMenu(); + return true; + } + else if (JNIdc.guiIsContentBrowser()) { + finish(); + return true; + } } - else if (JNIdc.guiIsContentBrowser()) { - finish(); + if (InputDeviceManager.getInstance().joystickButtonEvent(event.getDeviceId(), keyCode, true)) return true; - } - } - if (InputDeviceManager.getInstance().joystickButtonEvent(event.getDeviceId(), keyCode, true)) - return true; - if (ViewConfiguration.get(this).hasPermanentMenuKey()) { - if (keyCode == KeyEvent.KEYCODE_MENU) { - return showMenu(); + if (ViewConfiguration.get(this).hasPermanentMenuKey()) { + if (keyCode == KeyEvent.KEYCODE_MENU) { + return showMenu(); + } } } return super.onKeyDown(keyCode, event); From b443fe9b255b07d0e0f590983c864e7baea9b1e4 Mon Sep 17 00:00:00 2001 From: "Christoph \"baka0815\" Schwerdtfeger" Date: Fri, 5 Apr 2019 21:05:18 +0200 Subject: [PATCH 228/319] AUDIO: make the plugins self-registering No need anymore for the RegisterAllBackends() function and all plugins in one place. Use a static boolean to register every plugin by itself. --- core/oslib/audiobackend_alsa.cpp | 6 +- core/oslib/audiobackend_alsa.h | 4 -- core/oslib/audiobackend_android.h | 4 -- core/oslib/audiobackend_coreaudio.cpp | 47 ++++++------- core/oslib/audiobackend_coreaudio.h | 5 -- core/oslib/audiobackend_directsound.cpp | 46 ++++++------- core/oslib/audiobackend_directsound.h | 4 -- core/oslib/audiobackend_libao.cpp | 13 ++-- core/oslib/audiobackend_libao.h | 4 -- core/oslib/audiobackend_omx.cpp | 3 +- core/oslib/audiobackend_omx.h | 4 -- core/oslib/audiobackend_oss.cpp | 3 +- core/oslib/audiobackend_oss.h | 4 -- core/oslib/audiobackend_pulseaudio.cpp | 4 +- core/oslib/audiobackend_pulseaudio.h | 4 -- core/oslib/audiostream.cpp | 66 ++++++------------- .../reicast/src/main/jni/src/Android.cpp | 7 +- 17 files changed, 91 insertions(+), 137 deletions(-) delete mode 100644 core/oslib/audiobackend_alsa.h delete mode 100644 core/oslib/audiobackend_android.h delete mode 100644 core/oslib/audiobackend_coreaudio.h delete mode 100644 core/oslib/audiobackend_directsound.h delete mode 100644 core/oslib/audiobackend_libao.h delete mode 100644 core/oslib/audiobackend_omx.h delete mode 100644 core/oslib/audiobackend_oss.h delete mode 100644 core/oslib/audiobackend_pulseaudio.h diff --git a/core/oslib/audiobackend_alsa.cpp b/core/oslib/audiobackend_alsa.cpp index 949af077e..2ee230976 100644 --- a/core/oslib/audiobackend_alsa.cpp +++ b/core/oslib/audiobackend_alsa.cpp @@ -1,4 +1,4 @@ -#include "oslib/audiobackend_alsa.h" +#include "oslib/audiostream.h" #if USE_ALSA #include #include "cfg/cfg.h" @@ -175,11 +175,13 @@ static void alsa_term() snd_pcm_close(handle); } -audiobackend_t audiobackend_alsa = { +static audiobackend_t audiobackend_alsa = { "alsa", // Slug "Advanced Linux Sound Architecture", // Name &alsa_init, &alsa_push, &alsa_term }; + +static bool alsa = RegisterAudioBackend(&audiobackend_alsa); #endif diff --git a/core/oslib/audiobackend_alsa.h b/core/oslib/audiobackend_alsa.h deleted file mode 100644 index 3995cb1ac..000000000 --- a/core/oslib/audiobackend_alsa.h +++ /dev/null @@ -1,4 +0,0 @@ -#pragma once -#include "oslib/audiostream.h" - -extern audiobackend_t audiobackend_alsa; diff --git a/core/oslib/audiobackend_android.h b/core/oslib/audiobackend_android.h deleted file mode 100644 index c2765c14b..000000000 --- a/core/oslib/audiobackend_android.h +++ /dev/null @@ -1,4 +0,0 @@ -#pragma once -#include "oslib/audiostream.h" - -extern audiobackend_t audiobackend_android; \ No newline at end of file diff --git a/core/oslib/audiobackend_coreaudio.cpp b/core/oslib/audiobackend_coreaudio.cpp index 9ce685cd9..dd04926ce 100644 --- a/core/oslib/audiobackend_coreaudio.cpp +++ b/core/oslib/audiobackend_coreaudio.cpp @@ -1,18 +1,18 @@ /* Simple Core Audio backend for osx (and maybe ios?) Based off various audio core samples and dolphin's code - + This is part of the Reicast project, please consult the LICENSE file for licensing & related information - + This could do with some locking logic to avoid race conditions, and some variable length buffer logic to support chunk sizes other than 512 bytes - + It does work on my macmini though */ -#include "oslib/audiobackend_coreaudio.h" +#include "oslib/audiostream.h" #if HOST_OS == OS_DARWIN #include @@ -49,9 +49,9 @@ static OSStatus coreaudio_callback(void* ctx, AudioUnitRenderActionFlags* flags, samples_rptr = (samples_rptr + buf_size) % BUFSIZE; } } - + bufferEmpty.Set(); - + return noErr; } @@ -63,7 +63,7 @@ static void coreaudio_init() AudioStreamBasicDescription format; AudioComponentDescription desc; AudioComponent component; - + desc.componentType = kAudioUnitType_Output; #if !defined(TARGET_IPHONE) desc.componentSubType = kAudioUnitSubType_DefaultOutput; @@ -75,12 +75,12 @@ static void coreaudio_init() desc.componentFlagsMask = 0; desc.componentManufacturer = kAudioUnitManufacturer_Apple; component = AudioComponentFindNext(nullptr, &desc); - + verify(component != nullptr); - + err = AudioComponentInstanceNew(component, &audioUnit); verify(err == noErr); - + FillOutASBDForLPCM(format, 44100, 2, 16, 16, false, false, false); err = AudioUnitSetProperty(audioUnit, @@ -88,7 +88,7 @@ static void coreaudio_init() kAudioUnitScope_Input, 0, &format, sizeof(AudioStreamBasicDescription)); verify(err == noErr); - + callback_struct.inputProc = coreaudio_callback; callback_struct.inputProcRefCon = 0; err = AudioUnitSetProperty(audioUnit, @@ -96,24 +96,24 @@ static void coreaudio_init() kAudioUnitScope_Input, 0, &callback_struct, sizeof callback_struct); verify(err == noErr); - + /* err = AudioUnitSetParameter(audioUnit, kHALOutputParam_Volume, kAudioUnitParameterFlag_Output, 0, 1, 0); verify(err == noErr); - + */ - + err = AudioUnitInitialize(audioUnit); - + verify(err == noErr); - + err = AudioOutputUnitStart(audioUnit); verify(err == noErr); - + bufferEmpty.Set(); } @@ -134,23 +134,23 @@ static u32 coreaudio_push(void* frame, u32 samples, bool wait) samples_wptr = (samples_wptr + byte_size) % BUFSIZE; break; } - + return 1; } static void coreaudio_term() { OSStatus err; - + err = AudioOutputUnitStop(audioUnit); verify(err == noErr); - + err = AudioUnitUninitialize(audioUnit); verify(err == noErr); - + err = AudioComponentInstanceDispose(audioUnit); verify(err == noErr); - + bufferEmpty.Set(); } @@ -161,4 +161,7 @@ audiobackend_t audiobackend_coreaudio = { &coreaudio_push, &coreaudio_term }; + +static bool core = RegisterAudioBackend(&audiobackend_coreaudio); + #endif diff --git a/core/oslib/audiobackend_coreaudio.h b/core/oslib/audiobackend_coreaudio.h deleted file mode 100644 index 8f3172317..000000000 --- a/core/oslib/audiobackend_coreaudio.h +++ /dev/null @@ -1,5 +0,0 @@ -#pragma once -#include "oslib/audiostream.h" - -extern audiobackend_t audiobackend_coreaudio; - diff --git a/core/oslib/audiobackend_directsound.cpp b/core/oslib/audiobackend_directsound.cpp index 36a4d692c..d173e434b 100644 --- a/core/oslib/audiobackend_directsound.cpp +++ b/core/oslib/audiobackend_directsound.cpp @@ -1,4 +1,4 @@ -#include "oslib/audiobackend_directsound.h" +#include "oslib/audiostream.h" #if HOST_OS==OS_WINDOWS #include "oslib.h" #include @@ -19,31 +19,31 @@ static void directsound_init() verifyc(dsound->SetCooperativeLevel((HWND)libPvr_GetRenderTarget(),DSSCL_PRIORITY)); IDirectSoundBuffer* buffer_; - WAVEFORMATEX wfx; - DSBUFFERDESC desc; + WAVEFORMATEX wfx; + DSBUFFERDESC desc; - // Set up WAV format structure. + // Set up WAV format structure. - memset(&wfx, 0, sizeof(WAVEFORMATEX)); - wfx.wFormatTag = WAVE_FORMAT_PCM; - wfx.nChannels = 2; - wfx.nSamplesPerSec = 44100; - wfx.nBlockAlign = 4; - wfx.nAvgBytesPerSec = wfx.nSamplesPerSec * wfx.nBlockAlign; - wfx.wBitsPerSample = 16; + memset(&wfx, 0, sizeof(WAVEFORMATEX)); + wfx.wFormatTag = WAVE_FORMAT_PCM; + wfx.nChannels = 2; + wfx.nSamplesPerSec = 44100; + wfx.nBlockAlign = 4; + wfx.nAvgBytesPerSec = wfx.nSamplesPerSec * wfx.nBlockAlign; + wfx.wBitsPerSample = 16; - // Set up DSBUFFERDESC structure. + // Set up DSBUFFERDESC structure. ds_ring_size=8192*wfx.nBlockAlign; - memset(&desc, 0, sizeof(DSBUFFERDESC)); - desc.dwSize = sizeof(DSBUFFERDESC); - desc.dwFlags = DSBCAPS_GETCURRENTPOSITION2 | DSBCAPS_CTRLPOSITIONNOTIFY;// _CTRLPAN | DSBCAPS_CTRLVOLUME | DSBCAPS_CTRLFREQUENCY; - - desc.dwBufferBytes = ds_ring_size; - desc.lpwfxFormat = &wfx; + memset(&desc, 0, sizeof(DSBUFFERDESC)); + desc.dwSize = sizeof(DSBUFFERDESC); + desc.dwFlags = DSBCAPS_GETCURRENTPOSITION2 | DSBCAPS_CTRLPOSITIONNOTIFY;// _CTRLPAN | DSBCAPS_CTRLVOLUME | DSBCAPS_CTRLFREQUENCY; + + desc.dwBufferBytes = ds_ring_size; + desc.lpwfxFormat = &wfx; + - if (settings.aica.HW_mixing==0) { @@ -71,7 +71,7 @@ static void directsound_init() //Play the buffer ! verifyc(buffer->Play(0,0,DSBPLAY_LOOPING)); - + } @@ -159,7 +159,7 @@ static u32 directsound_push(void* frame, u32 samples, bool wait) wait &= w; */ int ffs=1; - + /* while (directsound_IsAudioBufferedLots() && wait) if (ffs == 0) @@ -175,7 +175,7 @@ static u32 directsound_push(void* frame, u32 samples, bool wait) static void directsound_term() { buffer->Stop(); - + buffer->Release(); dsound->Release(); } @@ -187,4 +187,6 @@ audiobackend_t audiobackend_directsound = { &directsound_push, &directsound_term }; + +static bool ds = RegisterAudioBackend(&audiobackend_directsound); #endif diff --git a/core/oslib/audiobackend_directsound.h b/core/oslib/audiobackend_directsound.h deleted file mode 100644 index 544ba629d..000000000 --- a/core/oslib/audiobackend_directsound.h +++ /dev/null @@ -1,4 +0,0 @@ -#pragma once -#include "oslib/audiostream.h" - -extern audiobackend_t audiobackend_directsound; diff --git a/core/oslib/audiobackend_libao.cpp b/core/oslib/audiobackend_libao.cpp index 1445c1492..c235dca00 100644 --- a/core/oslib/audiobackend_libao.cpp +++ b/core/oslib/audiobackend_libao.cpp @@ -1,4 +1,4 @@ -#include "oslib/audiobackend_libao.h" +#include "oslib/audiostream.h" #ifdef USE_LIBAO #include @@ -10,12 +10,12 @@ static void libao_init() { ao_initialize(); memset(&aoformat, 0, sizeof(aoformat)); - + aoformat.bits = 16; aoformat.channels = 2; aoformat.rate = 44100; aoformat.byte_format = AO_FMT_LITTLE; - + aodevice = ao_open_live(ao_default_driver_id(), &aoformat, NULL); // Live output if (!aodevice) aodevice = ao_open_live(ao_driver_id("null"), &aoformat, NULL); @@ -23,13 +23,13 @@ static void libao_init() static u32 libao_push(void* frame, u32 samples, bool wait) { - if (aodevice) + if (aodevice) ao_play(aodevice, (char*)frame, samples * 4); - + return 1; } -static void libao_term() +static void libao_term() { if (aodevice) { @@ -46,4 +46,5 @@ audiobackend_t audiobackend_libao = { &libao_term }; +static bool ao = RegisterAudioBackend(&audiobackend_libao); #endif diff --git a/core/oslib/audiobackend_libao.h b/core/oslib/audiobackend_libao.h deleted file mode 100644 index 5bf957286..000000000 --- a/core/oslib/audiobackend_libao.h +++ /dev/null @@ -1,4 +0,0 @@ -#pragma once -#include "oslib/audiostream.h" - -extern audiobackend_t audiobackend_libao; diff --git a/core/oslib/audiobackend_omx.cpp b/core/oslib/audiobackend_omx.cpp index ffb0779a9..71c426b68 100644 --- a/core/oslib/audiobackend_omx.cpp +++ b/core/oslib/audiobackend_omx.cpp @@ -1,4 +1,4 @@ -#include "oslib/audiobackend_omx.h" +#include "oslib/audiostream.h" #if USE_OMX #include @@ -316,4 +316,5 @@ audiobackend_t audiobackend_omx = { &omx_term }; +static bool omx = RegisterAudioBackend(&audiobackend_omx); #endif diff --git a/core/oslib/audiobackend_omx.h b/core/oslib/audiobackend_omx.h deleted file mode 100644 index 63fdcb5fd..000000000 --- a/core/oslib/audiobackend_omx.h +++ /dev/null @@ -1,4 +0,0 @@ -#pragma once -#include "oslib/audiostream.h" - -extern audiobackend_t audiobackend_omx; diff --git a/core/oslib/audiobackend_oss.cpp b/core/oslib/audiobackend_oss.cpp index 67015db6c..cc653466f 100644 --- a/core/oslib/audiobackend_oss.cpp +++ b/core/oslib/audiobackend_oss.cpp @@ -1,4 +1,4 @@ -#include "oslib/audiobackend_oss.h" +#include "oslib/audiostream.h" #ifdef USE_OSS #include #include @@ -51,4 +51,5 @@ audiobackend_t audiobackend_oss = { &oss_term }; +static bool oss = RegisterAudioBackend(&audiobackend_oss); #endif diff --git a/core/oslib/audiobackend_oss.h b/core/oslib/audiobackend_oss.h deleted file mode 100644 index cfeb53955..000000000 --- a/core/oslib/audiobackend_oss.h +++ /dev/null @@ -1,4 +0,0 @@ -#pragma once -#include "oslib/audiostream.h" - -extern audiobackend_t audiobackend_oss; diff --git a/core/oslib/audiobackend_pulseaudio.cpp b/core/oslib/audiobackend_pulseaudio.cpp index dea4256cc..28963b00a 100644 --- a/core/oslib/audiobackend_pulseaudio.cpp +++ b/core/oslib/audiobackend_pulseaudio.cpp @@ -1,4 +1,4 @@ -#include "oslib/audiobackend_pulseaudio.h" +#include "oslib/audiostream.h" #ifdef USE_PULSEAUDIO #include #include @@ -45,4 +45,6 @@ audiobackend_t audiobackend_pulseaudio = { &pulseaudio_push, &pulseaudio_term }; + +static bool pulse = RegisterAudioBackend(&audiobackend_pulseaudio); #endif diff --git a/core/oslib/audiobackend_pulseaudio.h b/core/oslib/audiobackend_pulseaudio.h deleted file mode 100644 index 7948d5b16..000000000 --- a/core/oslib/audiobackend_pulseaudio.h +++ /dev/null @@ -1,4 +0,0 @@ -#pragma once -#include "oslib/audiostream.h" - -extern audiobackend_t audiobackend_pulseaudio; diff --git a/core/oslib/audiostream.cpp b/core/oslib/audiostream.cpp index f89e783fb..8cc617e3e 100644 --- a/core/oslib/audiostream.cpp +++ b/core/oslib/audiostream.cpp @@ -2,14 +2,6 @@ #include "cfg/cfg.h" #include "oslib/oslib.h" #include "audiostream.h" -#include "oslib/audiobackend_directsound.h" -#include "oslib/audiobackend_android.h" -#include "oslib/audiobackend_alsa.h" -#include "oslib/audiobackend_oss.h" -#include "oslib/audiobackend_pulseaudio.h" -#include "oslib/audiobackend_coreaudio.h" -#include "oslib/audiobackend_omx.h" -#include "oslib/audiobackend_libao.h" struct SoundFrame { s16 l;s16 r; }; #define SAMPLE_COUNT 512 @@ -25,18 +17,20 @@ u32 gen_samples=0; double time_diff = 128/44100.0; double time_last; + #ifdef LOG_SOUND +// TODO Only works on Windows! WaveWriter rawout("d:\\aica_out.wav"); #endif -static bool audiobackends_registered = false; static unsigned int audiobackends_num_max = 1; static unsigned int audiobackends_num_registered = 0; -static audiobackend_t **audiobackends = static_cast(calloc(audiobackends_num_max, sizeof(audiobackend_t*))); +static audiobackend_t **audiobackends = NULL; static audiobackend_t *audiobackend_current = NULL; bool RegisterAudioBackend(audiobackend_t *backend) { + printf("RegisterAUdio!\n"); /* This function announces the availability of an audio backend to reicast. */ // Check if backend is valid if (backend == NULL) @@ -44,10 +38,16 @@ bool RegisterAudioBackend(audiobackend_t *backend) printf("ERROR: Tried to register invalid audio backend (NULL pointer).\n"); return false; } + if (backend->slug == "auto" || backend->slug == "none") { printf("ERROR: Tried to register invalid audio backend (slug \"%s\" is a reserved keyword).\n", backend->slug.c_str()); return false; } + + // First call to RegisterAudioBackend(), create the backend structure; + if (audiobackends == NULL) + audiobackends = static_cast(calloc(audiobackends_num_max, sizeof(audiobackend_t*))); + // Check if we need to allocate addition memory for storing the pointers and allocate if neccessary if (audiobackends_num_registered == audiobackends_num_max) { @@ -67,46 +67,20 @@ bool RegisterAudioBackend(audiobackend_t *backend) } audiobackends = new_ptr; } + audiobackends[audiobackends_num_registered] = backend; audiobackends_num_registered++; return true; } -void RegisterAllAudioBackends() { - #if HOST_OS==OS_WINDOWS - RegisterAudioBackend(&audiobackend_directsound); - #endif - #if ANDROID - RegisterAudioBackend(&audiobackend_android); - #endif - #if USE_OMX - RegisterAudioBackend(&audiobackend_omx); - #endif - #if USE_ALSA - RegisterAudioBackend(&audiobackend_alsa); - #endif - #if USE_OSS - RegisterAudioBackend(&audiobackend_oss); - #endif - #if USE_PULSEAUDIO - RegisterAudioBackend(&audiobackend_pulseaudio); - #endif - #if USE_LIBAO - RegisterAudioBackend(&audiobackend_libao); - #endif - #if HOST_OS == OS_DARWIN - RegisterAudioBackend(&audiobackend_coreaudio); - #endif - audiobackends_registered = true; -} - static audiobackend_t* GetAudioBackend(std::string slug) { + printf("AudioBackend: %s\n", slug); if (slug == "none") { printf("WARNING: Audio backend set to \"none\"!\n"); } - else if(audiobackends_num_registered > 0) + else if (audiobackends_num_registered > 0) { if (slug == "auto") { @@ -135,7 +109,8 @@ static audiobackend_t* GetAudioBackend(std::string slug) return NULL; } -u32 PushAudio(void* frame, u32 amt, bool wait) { +u32 PushAudio(void* frame, u32 amt, bool wait) +{ if (audiobackend_current != NULL) { return audiobackend_current->push(frame, amt, wait); } @@ -151,6 +126,7 @@ u32 asRingUsedCount() //s32 sz=(WritePtr+1)%RingBufferSampleCount-ReadPtr; //return sz<0?sz+RingBufferSampleCount:sz; } + u32 asRingFreeCount() { return RingBufferSampleCount-asRingUsedCount(); @@ -178,11 +154,6 @@ void InitAudio() cfgSaveInt("audio","disable",0); - if (!audiobackends_registered) { - //FIXME: There might some nicer way to do this. - RegisterAllAudioBackends(); - } - if (audiobackend_current != NULL) { printf("ERROR: The audio backend \"%s\" (%s) has already been initialized, you need to terminate it before you can call audio_init() again!\n", audiobackend_current->slug.c_str(), audiobackend_current->name.c_str()); return; @@ -194,8 +165,9 @@ void InitAudio() printf("WARNING: Running without audio!\n"); return; } + printf("Initializing audio backend \"%s\" (%s)...\n", audiobackend_current->slug.c_str(), audiobackend_current->name.c_str()); - audiobackend_current->init(); + audiobackend_current->init(); } void TermAudio() @@ -204,5 +176,5 @@ void TermAudio() audiobackend_current->term(); printf("Terminating audio backend \"%s\" (%s)...\n", audiobackend_current->slug.c_str(), audiobackend_current->name.c_str()); audiobackend_current = NULL; - } + } } diff --git a/shell/android-studio/reicast/src/main/jni/src/Android.cpp b/shell/android-studio/reicast/src/main/jni/src/Android.cpp index 3ab96e5dd..fad20b5e7 100644 --- a/shell/android-studio/reicast/src/main/jni/src/Android.cpp +++ b/shell/android-studio/reicast/src/main/jni/src/Android.cpp @@ -20,7 +20,7 @@ #include "hw/maple/maple_devs.h" #include "hw/maple/maple_if.h" #include "hw/naomi/naomi_cart.h" -#include "oslib/audiobackend_android.h" +#include "oslib/audiostream.h" #include "imgread/common.h" #include "rend/gui.h" #include "cfg/cfg.h" @@ -529,6 +529,9 @@ audiobackend_t audiobackend_android = { &androidaudio_term }; +static bool android = RegisterAudioBackend(&audiobackend_android); + + JNIEXPORT void JNICALL Java_com_reicast_emulator_emu_AudioBackend_setInstance(JNIEnv *env, jobject obj, jobject instance) { if (g_audioBackend != NULL) @@ -568,7 +571,7 @@ void os_DebugBreak() raise(SIGABRT); //pthread_exit(NULL); - + // Attach debugger here to figure out what went wrong for(;;) ; } From b8ae61bc5c8fbcc0c16beac37122b970473ebdd4 Mon Sep 17 00:00:00 2001 From: "Christoph \"baka0815\" Schwerdtfeger" Date: Fri, 5 Apr 2019 21:14:42 +0200 Subject: [PATCH 229/319] AUDIO: Sort audio plugins by name --- core/oslib/audiostream.cpp | 25 ++++++++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/core/oslib/audiostream.cpp b/core/oslib/audiostream.cpp index 8cc617e3e..6149ea902 100644 --- a/core/oslib/audiostream.cpp +++ b/core/oslib/audiostream.cpp @@ -145,6 +145,27 @@ void WriteSample(s16 r, s16 l) } } +static bool backends_sorted = false; +void SortBackends() +{ + if (backends_sorted) + return; + + // Sort backends by slug + for (int n = audiobackends_num_registered; n > 0; n--) + { + for (int i = 0; i < n-1; i++) + { + if (audiobackends[i]->slug > audiobackends[i+1]->slug) + { + audiobackend_t* swap = audiobackends[i]; + audiobackends[i] = audiobackends[i+1]; + audiobackends[i+1] = swap; + } + } + } +} + void InitAudio() { if (cfgLoadInt("audio", "disable", 0)) { @@ -152,13 +173,15 @@ void InitAudio() return; } - cfgSaveInt("audio","disable",0); + cfgSaveInt("audio", "disable", 0); if (audiobackend_current != NULL) { printf("ERROR: The audio backend \"%s\" (%s) has already been initialized, you need to terminate it before you can call audio_init() again!\n", audiobackend_current->slug.c_str(), audiobackend_current->name.c_str()); return; } + SortBackends(); + string audiobackend_slug = cfgLoadStr("audio", "backend", "auto"); // FIXME: This could be made a parameter audiobackend_current = GetAudioBackend(audiobackend_slug); if (audiobackend_current == NULL) { From 16f9200bfa36917f6b749c79973acf0d592c2144 Mon Sep 17 00:00:00 2001 From: "Christoph \"baka0815\" Schwerdtfeger" Date: Fri, 5 Apr 2019 22:22:46 +0200 Subject: [PATCH 230/319] AUDIO: Allow selection of backend --- core/nullDC.cpp | 8 +++++-- core/oslib/audiostream.cpp | 20 +++++++++++------ core/oslib/audiostream.h | 6 ++++++ core/rend/gui.cpp | 44 ++++++++++++++++++++++++++++++++++++++ core/types.h | 41 +++++++++++++++++++---------------- 5 files changed, 92 insertions(+), 27 deletions(-) diff --git a/core/nullDC.cpp b/core/nullDC.cpp index 89c637803..fc44f3a3e 100755 --- a/core/nullDC.cpp +++ b/core/nullDC.cpp @@ -134,7 +134,7 @@ void LoadSpecialSettings() safemode_game = false; tr_poly_depth_mask_game = false; extra_depth_game = false; - + if (reios_windows_ce) { printf("Enabling Extra depth scaling for Windows CE games\n"); @@ -190,7 +190,7 @@ void LoadSpecialSettings() } #elif DC_PLATFORM == DC_PLATFORM_NAOMI || DC_PLATFORM == DC_PLATFORM_ATOMISWAVE printf("Game ID is [%s]\n", naomi_game_id); - + if (!strcmp("METAL SLUG 6", naomi_game_id) || !strcmp("WAVE RUNNER GP", naomi_game_id)) { printf("Enabling Dynarec safe mode for game %s\n", naomi_game_id); @@ -499,6 +499,7 @@ void InitSettings() settings.aica.LimitFPS = true; settings.aica.NoBatch = false; // This also controls the DSP. Disabled by default settings.aica.NoSound = false; + settings.audio.backend = "auto"; settings.rend.UseMipmaps = true; settings.rend.WideScreen = false; settings.rend.ShowFPS = false; @@ -560,6 +561,7 @@ void LoadSettings(bool game_specific) { const char *config_section = game_specific ? cfgGetGameId() : "config"; const char *input_section = game_specific ? cfgGetGameId() : "input"; + const char *audio_section = game_specific ? cfgGetGameId() : "audio"; settings.dynarec.Enable = cfgLoadBool(config_section, "Dynarec.Enabled", settings.dynarec.Enable); settings.dynarec.idleskip = cfgLoadBool(config_section, "Dynarec.idleskip", settings.dynarec.idleskip); @@ -575,6 +577,7 @@ void LoadSettings(bool game_specific) settings.aica.LimitFPS = cfgLoadBool(config_section, "aica.LimitFPS", settings.aica.LimitFPS); settings.aica.NoBatch = cfgLoadBool(config_section, "aica.NoBatch", settings.aica.NoBatch); settings.aica.NoSound = cfgLoadBool(config_section, "aica.NoSound", settings.aica.NoSound); + settings.audio.backend = cfgLoadStr(audio_section, "backend", settings.audio.backend.c_str()); settings.rend.UseMipmaps = cfgLoadBool(config_section, "rend.UseMipmaps", settings.rend.UseMipmaps); settings.rend.WideScreen = cfgLoadBool(config_section, "rend.WideScreen", settings.rend.WideScreen); settings.rend.ShowFPS = cfgLoadBool(config_section, "rend.ShowFPS", settings.rend.ShowFPS); @@ -708,6 +711,7 @@ void SaveSettings() cfgSaveBool("config", "aica.LimitFPS", settings.aica.LimitFPS); cfgSaveBool("config", "aica.NoBatch", settings.aica.NoBatch); cfgSaveBool("config", "aica.NoSound", settings.aica.NoSound); + cfgSaveStr("audio", "backend", settings.audio.backend.c_str()); cfgSaveBool("config", "rend.WideScreen", settings.rend.WideScreen); cfgSaveBool("config", "rend.ShowFPS", settings.rend.ShowFPS); if (!rtt_to_buffer_game || !settings.rend.RenderToTextureBuffer) diff --git a/core/oslib/audiostream.cpp b/core/oslib/audiostream.cpp index 6149ea902..ea6845bed 100644 --- a/core/oslib/audiostream.cpp +++ b/core/oslib/audiostream.cpp @@ -28,9 +28,18 @@ static unsigned int audiobackends_num_registered = 0; static audiobackend_t **audiobackends = NULL; static audiobackend_t *audiobackend_current = NULL; +u32 GetAudioBackendCount() +{ + return audiobackends_num_registered; +} + +audiobackend_t* GetAudioBackend(int num) +{ + return audiobackends[num]; +} + bool RegisterAudioBackend(audiobackend_t *backend) { - printf("RegisterAUdio!\n"); /* This function announces the availability of an audio backend to reicast. */ // Check if backend is valid if (backend == NULL) @@ -73,9 +82,8 @@ bool RegisterAudioBackend(audiobackend_t *backend) return true; } -static audiobackend_t* GetAudioBackend(std::string slug) +audiobackend_t* GetAudioBackend(std::string slug) { - printf("AudioBackend: %s\n", slug); if (slug == "none") { printf("WARNING: Audio backend set to \"none\"!\n"); @@ -146,7 +154,7 @@ void WriteSample(s16 r, s16 l) } static bool backends_sorted = false; -void SortBackends() +void SortAudioBackends() { if (backends_sorted) return; @@ -180,9 +188,9 @@ void InitAudio() return; } - SortBackends(); + SortAudioBackends(); - string audiobackend_slug = cfgLoadStr("audio", "backend", "auto"); // FIXME: This could be made a parameter + string audiobackend_slug = settings.audio.backend; audiobackend_current = GetAudioBackend(audiobackend_slug); if (audiobackend_current == NULL) { printf("WARNING: Running without audio!\n"); diff --git a/core/oslib/audiostream.h b/core/oslib/audiostream.h index b700eb066..2726db0e1 100644 --- a/core/oslib/audiostream.h +++ b/core/oslib/audiostream.h @@ -1,5 +1,6 @@ #pragma once #include "types.h" +#include //Get used size in the ring buffer u32 asRingUsedCount(); @@ -24,3 +25,8 @@ extern bool RegisterAudioBackend(audiobackend_t* backend); extern void InitAudio(); extern u32 PushAudio(void* frame, u32 amt, bool wait); extern void TermAudio(); + +u32 GetAudioBackendCount(); +void SortAudioBackends(); +audiobackend_t* GetAudioBackend(int num); +audiobackend_t* GetAudioBackend(std::string slug); diff --git a/core/rend/gui.cpp b/core/rend/gui.cpp index cfc134cbc..2db5ea336 100644 --- a/core/rend/gui.cpp +++ b/core/rend/gui.cpp @@ -35,6 +35,8 @@ #include "gui_util.h" #include "gui_android.h" #include "version/version.h" +#include "oslib/audiostream.h" + extern void dc_loadstate(); extern void dc_savestate(); @@ -989,6 +991,48 @@ static void gui_display_settings() ImGui::Checkbox("Disable Sound", &settings.aica.NoSound); ImGui::SameLine(); ShowHelpMarker("Disable the emulator sound output"); + + audiobackend_t* backend = NULL;; + std::string backend_name = settings.audio.backend; + if (backend_name != "auto" && backend_name != "none") + { + backend = GetAudioBackend(settings.audio.backend); + if (backend != NULL) + backend_name = backend->slug; + } + + SortAudioBackends(); + if (ImGui::BeginCombo("Audio Backend", backend_name.c_str(), ImGuiComboFlags_None)) + { + bool is_selected = (settings.audio.backend == "auto"); + if (ImGui::Selectable("auto", &is_selected)) + settings.audio.backend = "auto"; + ImGui::SameLine(); ImGui::Text("-"); + ImGui::SameLine(); ImGui::Text("Autoselect audio backend"); + + is_selected = (settings.audio.backend == "none"); + if (ImGui::Selectable("none", &is_selected)) + settings.audio.backend = "none"; + ImGui::SameLine(); ImGui::Text("-"); + ImGui::SameLine(); ImGui::Text("No audio backend"); + + for (int i = 0; i < GetAudioBackendCount(); i++) + { + audiobackend_t* backend = GetAudioBackend(i); + is_selected = (settings.audio.backend == backend->slug); + + if (ImGui::Selectable(backend->slug.c_str(), &is_selected)) + settings.audio.backend = backend->slug; + ImGui::SameLine(); ImGui::Text("-"); + ImGui::SameLine(); ImGui::Text(backend->name.c_str()); + if (is_selected) + ImGui::SetItemDefaultFocus(); + } + ImGui::EndCombo(); + } + ImGui::SameLine(); + ShowHelpMarker("The audio backend to use"); + ImGui::Checkbox("Enable DSP", &settings.aica.NoBatch); ImGui::SameLine(); ShowHelpMarker("Enable the Dreamcast Digital Sound Processor. Only recommended on fast and arm64 platforms"); diff --git a/core/types.h b/core/types.h index a9a835ac1..4769c6e48 100644 --- a/core/types.h +++ b/core/types.h @@ -39,7 +39,7 @@ #undef _CRT_SECURE_NO_DEPRECATE #endif -#define _CRT_SECURE_NO_DEPRECATE +#define _CRT_SECURE_NO_DEPRECATE //unnamed struncts/unions #pragma warning( disable : 4201) @@ -123,7 +123,7 @@ enum HollyInterruptType }; enum HollyInterruptID -{ +{ // asic9a /sh4 external holly normal [internal] holly_RENDER_DONE_vd = holly_nrm | 0, //bit 0 = End of Render interrupt : Video holly_RENDER_DONE_isp = holly_nrm | 1, //bit 1 = End of Render interrupt : ISP @@ -132,11 +132,11 @@ enum HollyInterruptID holly_SCANINT1 = holly_nrm | 3, //bit 3 = V Blank-in interrupt holly_SCANINT2 = holly_nrm | 4, //bit 4 = V Blank-out interrupt holly_HBLank = holly_nrm | 5, //bit 5 = H Blank-in interrupt - + holly_YUV_DMA = holly_nrm | 6, //bit 6 = End of Transferring interrupt : YUV holly_OPAQUE = holly_nrm | 7, //bit 7 = End of Transferring interrupt : Opaque List holly_OPAQUEMOD = holly_nrm | 8, //bit 8 = End of Transferring interrupt : Opaque Modifier Volume List - + holly_TRANS = holly_nrm | 9, //bit 9 = End of Transferring interrupt : Translucent List holly_TRANSMOD = holly_nrm | 10, //bit 10 = End of Transferring interrupt : Translucent Modifier Volume List holly_PVR_DMA = holly_nrm | 11, //bit 11 = End of DMA interrupt : PVR-DMA @@ -145,12 +145,12 @@ enum HollyInterruptID holly_MAPLE_VBOI = holly_nrm | 13, //bit 13 = Maple V blank over interrupt holly_GDROM_DMA = holly_nrm | 14, //bit 14 = End of DMA interrupt : GD-DMA holly_SPU_DMA = holly_nrm | 15, //bit 15 = End of DMA interrupt : AICA-DMA - + holly_EXT_DMA1 = holly_nrm | 16, //bit 16 = End of DMA interrupt : Ext-DMA1(External 1) holly_EXT_DMA2 = holly_nrm | 17, //bit 17 = End of DMA interrupt : Ext-DMA2(External 2) holly_DEV_DMA = holly_nrm | 18, //bit 18 = End of DMA interrupt : Dev-DMA(Development tool DMA) - - holly_CH2_DMA = holly_nrm | 19, //bit 19 = End of DMA interrupt : ch2-DMA + + holly_CH2_DMA = holly_nrm | 19, //bit 19 = End of DMA interrupt : ch2-DMA holly_PVR_SortDMA = holly_nrm | 20, //bit 20 = End of DMA interrupt : Sort-DMA (Transferring for alpha sorting) holly_PUNCHTHRU = holly_nrm | 21, //bit 21 = End of Transferring interrupt : Punch Through List @@ -188,8 +188,8 @@ enum HollyInterruptID //bit 23 = G2 : AICA-DMA Time out //bit 24 = G2 : Ext-DMA1 Time out //bit 25 = G2 : Ext-DMA2 Time out - //bit 26 = G2 : Dev-DMA Time out - //bit 27 = G2 : Time out in CPU accessing + //bit 26 = G2 : Dev-DMA Time out + //bit 27 = G2 : Time out in CPU accessing }; @@ -200,7 +200,7 @@ struct vram_block u32 end; u32 len; u32 type; - + void* userdata; }; @@ -229,7 +229,7 @@ struct NDC_WINDOW_RECT //****************************************************** //*********************** PowerVR ********************** //****************************************************** - + void libCore_vramlock_Unlock_block (vram_block* block); void libCore_vramlock_Unlock_block_wb (vram_block* block); vram_block* libCore_vramlock_Lock(u32 start_offset,u32 end_offset,void* userdata); @@ -246,7 +246,7 @@ enum DiscType CdRom_XA=0x20, CdRom_Extra=0x30, CdRom_CDI=0x40, - GdRom=0x80, + GdRom=0x80, NoDisk=0x1, //These are a bit hacky .. but work for now ... Open=0x2, //tray is open :) @@ -576,7 +576,7 @@ enum RegIO RIO_RO = REG_RO | REG_WF, RIO_RO_FUNC = REG_RO | REG_RF | REG_WF, RIO_CONST = REG_RO | REG_WF, - RIO_WO_FUNC = REG_WF | REG_RF | REG_WO, + RIO_WO_FUNC = REG_WF | REG_RF | REG_WO, RIO_NO_ACCESS = REG_WF | REG_RF | REG_NO_ACCESS }; @@ -645,7 +645,7 @@ struct settings_t bool disable_nvmem; SmcCheckEnum SmcCheckLevel; } dynarec; - + struct { u32 run_counts; @@ -677,6 +677,9 @@ struct settings_t bool NoSound; } aica; + struct{ + std::string backend; + } audio; #if USE_OMX struct { @@ -706,7 +709,7 @@ struct settings_t { u32 ta_skip; u32 rend; - + u32 MaxThreads; bool SynchronousRender; } pvr; @@ -749,7 +752,7 @@ static inline void do_nada(...) { } #ifdef _ANDROID #include -#ifdef printf +#ifdef printf #undef printf #endif @@ -856,7 +859,7 @@ void libARM_Update(u32 cycles); else if (sz==2) \ return *(u16*)&arr[addr]; \ else if (sz==4) \ - return *(u32*)&arr[addr];} + return *(u32*)&arr[addr];} #define WriteMemArr(arr,addr,data,sz) \ {if(sz==1) \ @@ -864,7 +867,7 @@ void libARM_Update(u32 cycles); else if (sz==2) \ {*(u16*)&arr[addr]=(u16)data;} \ else if (sz==4) \ - {*(u32*)&arr[addr]=data;}} + {*(u32*)&arr[addr]=data;}} #define WriteMemArrRet(arr,addr,data,sz) \ {if(sz==1) \ @@ -872,7 +875,7 @@ void libARM_Update(u32 cycles); else if (sz==2) \ {*(u16*)&arr[addr]=(u16)data;return;} \ else if (sz==4) \ - {*(u32*)&arr[addr]=data;return;}} + {*(u32*)&arr[addr]=data;return;}} struct OnLoad { From e8205e568b2c9057fd321e0f18bf69b1e83c4670 Mon Sep 17 00:00:00 2001 From: Flyinghead Date: Sat, 6 Apr 2019 19:36:57 +0200 Subject: [PATCH 231/319] naomi/aw: fix atomiswave and naomi inputs atomiswave analog axes support map atomiswave and naomi driving games inputs --- core/hw/maple/maple_cfg.cpp | 68 +++++++++++-- core/hw/maple/maple_cfg.h | 2 + core/hw/maple/maple_devs.cpp | 186 ++++++++++++++++++++++------------- core/hw/naomi/naomi.cpp | 11 ++- core/hw/naomi/naomi_cart.cpp | 4 +- core/hw/naomi/naomi_cart.h | 2 +- core/linux-dist/x11.cpp | 12 --- core/windows/winmain.cpp | 8 -- 8 files changed, 187 insertions(+), 106 deletions(-) diff --git a/core/hw/maple/maple_cfg.cpp b/core/hw/maple/maple_cfg.cpp index 5a02ef11b..f14562411 100644 --- a/core/hw/maple/maple_cfg.cpp +++ b/core/hw/maple/maple_cfg.cpp @@ -4,6 +4,7 @@ #include "maple_devs.h" #include "maple_cfg.h" #include "cfg/cfg.h" +#include "hw/naomi/naomi_cart.h" #define HAS_VMU /* @@ -28,13 +29,31 @@ extern u16 kcode[4]; extern u32 vks[4]; extern s8 joyx[4],joyy[4]; extern u8 rt[4],lt[4]; -extern bool naomi_test_button; u8 GetBtFromSgn(s8 val) { return val+128; } +u32 awave_button_mapping[] = { + AWAVE_SERVICE_KEY, // DC_BTN_C + AWAVE_BTN1_KEY, // DC_BTN_B + AWAVE_BTN0_KEY, // DC_BTN_A + AWAVE_START_KEY, // DC_BTN_START + AWAVE_UP_KEY, // DC_DPAD_UP + AWAVE_DOWN_KEY, // DC_DPAD_DOWN + AWAVE_LEFT_KEY, // DC_DPAD_LEFT + AWAVE_RIGHT_KEY, // DC_DPAD_RIGHT + AWAVE_TEST_KEY, // DC_BTN_Z + AWAVE_BTN3_KEY, // DC_BTN_Y + AWAVE_BTN2_KEY, // DC_BTN_X + AWAVE_COIN_KEY, // DC_BTN_D + // DC_DPAD2_UP + // DC_DPAD2_DOWN + // DC_DPAD2_LEFT + // DC_DPAD2_RIGHT +}; + struct MapleConfigMap : IMapleConfigMap { maple_device* dev; @@ -59,17 +78,32 @@ struct MapleConfigMap : IMapleConfigMap pjs->kcode=kcode[player_num]; #if DC_PLATFORM == DC_PLATFORM_DREAMCAST - pjs->kcode |= 0xF901; -#elif DC_PLATFORM == DC_PLATFORM_ATOMISWAVE - if (naomi_test_button) - pjs->kcode &= ~(1 << 14); -// if (!(pjs->kcode & (1 << 9))) // Hack (Y -> service btn) -// pjs->kcode &= ~(1 << 13); -#endif + pjs->kcode |= 0xF901; // mask off DPad2, C, D and Z pjs->joy[PJAI_X1]=GetBtFromSgn(joyx[player_num]); pjs->joy[PJAI_Y1]=GetBtFromSgn(joyy[player_num]); pjs->trigger[PJTI_R]=rt[player_num]; pjs->trigger[PJTI_L]=lt[player_num]; +#elif DC_PLATFORM == DC_PLATFORM_ATOMISWAVE + pjs->kcode = 0xFFFF; + for (int i = 0; i < 16; i++) + { + if ((kcode[player_num] & (1 << i)) == 0) + pjs->kcode &= ~awave_button_mapping[i]; + } + pjs->joy[PJAI_X1] = GetBtFromSgn(joyx[player_num]); + if (NaomiGameInputs != NULL && NaomiGameInputs->axes[1].name != NULL && NaomiGameInputs->axes[1].type == Half) + { + // Driving games: put axis 2 on RT (accel) and axis 3 on LT (brake) + pjs->joy[PJAI_Y1] = rt[player_num]; + pjs->joy[PJAI_X2] = lt[player_num]; + } + else + { + pjs->joy[PJAI_Y1] = GetBtFromSgn(joyy[player_num]); + pjs->joy[PJAI_X2] = rt[player_num]; + pjs->joy[PJAI_Y2] = lt[player_num]; + } +#endif } void SetImage(void* img) { @@ -77,6 +111,16 @@ struct MapleConfigMap : IMapleConfigMap } }; +bool maple_atomiswave_coin_chute(int slot) +{ + for (int i = 0; i < 16; i++) + { + if (awave_button_mapping[i] == AWAVE_COIN_KEY && (kcode[slot] & (1 << i)) == 0) + return true; + } + return false; +} + void mcfg_Create(MapleDeviceType type, u32 bus, u32 port, s32 player_num = -1) { if (MapleDevices[bus][port] != NULL) @@ -100,8 +144,12 @@ void mcfg_CreateAtomisWaveControllers() // Then other devices on port 2 and 3 for analog axes, light guns, ... mcfg_Create(MDT_SegaController, 0, 5); mcfg_Create(MDT_SegaController, 1, 5); -// mcfg_Create(MDT_SegaController, 2, 5, 0); -// mcfg_Create(MDT_SegaController, 3, 5, 1); + if (NaomiGameInputs != NULL && NaomiGameInputs->axes[0].name != NULL) + { + // Game needs analog axes + mcfg_Create(MDT_SegaController, 2, 5, 0); + mcfg_Create(MDT_SegaController, 3, 5, 1); + } // mcfg_Create(MDT_LightGun, 2, 5, 0); // mcfg_Create(MDT_LightGun, 3, 5, 1); // mcfg_Create(MDT_Mouse, 2, 5, 0); diff --git a/core/hw/maple/maple_cfg.h b/core/hw/maple/maple_cfg.h index 4afcc99ce..75d4d116e 100644 --- a/core/hw/maple/maple_cfg.h +++ b/core/hw/maple/maple_cfg.h @@ -68,3 +68,5 @@ void mcfg_CreateAtomisWaveControllers(); void mcfg_DestroyDevices(); void mcfg_SerializeDevices(void **data, unsigned int *total_size); void mcfg_UnserializeDevices(void **data, unsigned int *total_size); + +bool maple_atomiswave_coin_chute(int slot); diff --git a/core/hw/maple/maple_devs.cpp b/core/hw/maple/maple_devs.cpp index ef9d5ac35..a26a3f01a 100755 --- a/core/hw/maple/maple_devs.cpp +++ b/core/hw/maple/maple_devs.cpp @@ -7,6 +7,7 @@ #include "hw/naomi/naomi.h" #include "hw/naomi/naomi_cart.h" #include "hw/pvr/spg.h" +#include "input/gamepad.h" #include #include "deps/zlib/zlib.h" @@ -215,7 +216,12 @@ struct maple_sega_controller: maple_base //struct data //3*4 - w32( 0xfe060f00); +#if DC_PLATFORM != DC_PLATFORM_ATOMISWAVE + w32(0xfe060f00); +#else + // More buttons, more digital axes + w32(0xff663f00); +#endif w32( 0); w32( 0); @@ -248,6 +254,7 @@ struct maple_sega_controller: maple_base //4 w32(MFID_0_Input); +#if DC_PLATFORM != DC_PLATFORM_ATOMISWAVE //state data //2 key code w16(pjs.kcode); @@ -270,6 +277,31 @@ struct maple_sega_controller: maple_base w8(0x80); //1 w8(0x80); +#else + //state data + //2 key code + w16(pjs.kcode | AWAVE_TRIGGER_KEY); + + //not used + //1 + w8(0); + //1 + w8(0); + + //joyx + //1 + w8(pjs.joy[PJAI_X1]); + //joyy + //1 + w8(pjs.joy[PJAI_Y1]); + + //joyrx + //1 + w8(pjs.joy[PJAI_X2]); + //joyry + //1 + w8(pjs.joy[PJAI_Y2]); +#endif } return MDRS_DataTransfer; @@ -1381,20 +1413,29 @@ static u16 getRightTriggerAxis() return rt[0] << 8; } -NaomiInputMapping Naomi_Mapping = { - { getJoystickXAxis, getJoystickYAxis, getRightTriggerAxis, getLeftTriggerAxis }, - { 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0 }, - { 0x40, 0x01, 0x02, 0x80, 0x20, 0x10, 0x08, 0x04, 0, 0x80, 0x40, 0, 0 }, -// SERVICE BTN1 BTN0 START UP DOWN LEFT RIGHT BTN2 BTN3 +u32 naomi_button_mapping[] = { + NAOMI_SERVICE_KEY, // DC_BTN_C + NAOMI_BTN1_KEY, // DC_BTN_B + NAOMI_BTN0_KEY, // DC_BTN_A + NAOMI_START_KEY, // DC_BTN_START + NAOMI_UP_KEY, // DC_DPAD_UP + NAOMI_DOWN_KEY, // DC_DPAD_DOWN + NAOMI_LEFT_KEY, // DC_DPAD_LEFT + NAOMI_RIGHT_KEY, // DC_DPAD_RIGHT + NAOMI_TEST_KEY, // DC_BTN_Z + NAOMI_BTN3_KEY, // DC_BTN_Y + NAOMI_BTN2_KEY, // DC_BTN_X + NAOMI_COIN_KEY, // DC_BTN_D + // DC_DPAD2_UP + // DC_DPAD2_DOWN + // DC_DPAD2_LEFT + // DC_DPAD2_RIGHT }; - /* * Sega JVS I/O board */ -bool coin_chute; -static bool old_coin_chute; -static int coin_count; -bool naomi_test_button = false; +static bool old_coin_chute[4]; +static int coin_count[4]; struct maple_naomi_jamma; @@ -2313,63 +2354,56 @@ u32 jvs_io_board::handle_jvs_message(u8 *buffer_in, u32 length_in, u8 *buffer_ou { JVS_STATUS1(); // report byte - LOGJVS("btns "); - JVS_OUT(naomi_test_button ? 0x80 : 0x00); // test, tilt1, tilt2, tilt3, unused, unused, unused, unused - // FIXME in-lst mapping - u8 buttons[8] = { 0 }; - u32 keycode = ~kcode[0]; - for (int i = 0; i < 16; i++) - if ((keycode & (1 << i)) != 0) - { - buttons[Naomi_Mapping.button_mapping_byte[i]] |= Naomi_Mapping.button_mapping_mask[i]; - } - for (int player = 0; player < buffer_in[cmdi + 1]; player++) + u16 buttons[4] = { 0 }; + for (int player = 0; player < buffer_in[cmdi + 1] && first_player + player < ARRAY_SIZE(kcode); player++) { - u8 *cur_btns = &buttons[(first_player + player) * 2]; - LOGJVS("P%d %02x ", player + 1 + first_player, cur_btns[0]); - JVS_OUT(cur_btns[0]); - if (buffer_in[cmdi + 2] == 2) + u32 keycode = ~kcode[first_player + player]; + for (int i = 0; i < 16; i++) { - LOGJVS("%02x ", cur_btns[1]); - JVS_OUT(cur_btns[1]); + if ((keycode & (1 << i)) != 0) + buttons[player] |= naomi_button_mapping[i]; + } + } + + LOGJVS("btns "); + JVS_OUT((buttons[0] & NAOMI_TEST_KEY) ? 0x80 : 0x00); // test, tilt1, tilt2, tilt3, unused, unused, unused, unused + for (int player = 0; player < buffer_in[cmdi + 1]; player++) + { + u16 cur_btns = first_player + player < ARRAY_SIZE(buttons) ? buttons[first_player + player] : 0; + LOGJVS("P%d %02x ", player + 1 + first_player, cur_btns >> 8); + JVS_OUT(cur_btns >> 8); + if (buffer_in[cmdi + 2] == 2) + { + LOGJVS("%02x ", cur_btns & 0xFF); + JVS_OUT(cur_btns); } } -// for (int player = 0; player < jvs_request[channel][cmdi + 1]; player++) -// { -// u32 keycode = ~kcode[player]; -// if (keycode & DC_BTN_C) -// keycode |= 0xFFff; -// -// if (jvs_request[channel][cmdi + 2] == 1) -// JVS_OUT(keycode); -// else -// w16(keycode); -// } cmdi += 3; } break; case 0x21: // Read coins { - if (coin_chute && !old_coin_chute) - coin_count++; - old_coin_chute = coin_chute; JVS_STATUS1(); // report byte LOGJVS("coins "); for (int slot = 0; slot < buffer_in[cmdi + 1]; slot++) { - if (slot == 0) + bool coin_chute = false; + u32 keycode = ~kcode[first_player + slot]; + for (int i = 0; i < 16 && !coin_chute; i++) { - LOGJVS("0:%d ", coin_count); - JVS_OUT((coin_count >> 8) & 0x3F); // status (2 highest bits, 0: normal), coin count MSB - JVS_OUT(coin_count); // coin count LSB - } - else - { - LOGJVS("%d:0 ", slot); - JVS_OUT(0); - JVS_OUT(0); + if (naomi_button_mapping[i] == NAOMI_COIN_KEY && (keycode & (1 << i)) != 0) + coin_chute = true; } + if (coin_chute && !old_coin_chute[first_player + slot]) + coin_count[first_player + slot] += 1; + old_coin_chute[first_player + slot] = coin_chute; + + LOGJVS("%d:%d ", slot + 1 + first_player, coin_count[first_player + slot]); + // status (2 highest bits, 0: normal), coin count MSB + JVS_OUT((coin_count[first_player + slot] >> 8) & 0x3F); + // coin count LSB + JVS_OUT(coin_count[first_player + slot]); } cmdi += 2; } @@ -2393,34 +2427,50 @@ u32 jvs_io_board::handle_jvs_message(u8 *buffer_in, u32 length_in, u8 *buffer_ou } LOGJVS("x,y:%4x,%4x ", x, y); JVS_OUT(x >> 8); // X, MSB - JVS_OUT(x); // X, LSB + JVS_OUT(x); // X, LSB JVS_OUT(y >> 8); // Y, MSB - JVS_OUT(y); // Y, LSB + JVS_OUT(y); // Y, LSB axis = 2; } + int full_axis_count = 0; + int half_axis_count = 0; for (; axis < buffer_in[cmdi + 1]; axis++) { - // FIXME Need to know how many axes per player for proper mapping u16 axis_value; - if (axis + first_player * 4 < 8 && Naomi_Mapping.axis[axis + first_player * 4] != NULL) - axis_value = Naomi_Mapping.axis[axis + first_player * 4](); + if (NaomiGameInputs != NULL + && axis < ARRAY_SIZE(NaomiGameInputs->axes) + && NaomiGameInputs->axes[axis].name != NULL + && NaomiGameInputs->axes[axis].type == Half) + { + if (half_axis_count == 0) + axis_value = rt[first_player] << 8; + else if (half_axis_count == 1) + axis_value = lt[first_player] << 8; + else + axis_value = 0; + half_axis_count++; + } else { - switch (axis) { + switch (full_axis_count) { case 0: - axis_value = (joyx[first_player + axis / 4] + 128) << 8; + axis_value = (joyx[first_player] + 128) << 8; break; case 1: - axis_value = (joyy[first_player + axis / 4] + 128) << 8; - break; - case 2: - axis_value = rt[first_player + axis / 4] << 8; - break; - case 3: - axis_value = lt[first_player + axis / 4] << 8; + axis_value = (joyy[first_player] + 128) << 8; break; + // TODO right analog stick +// case 2: +// axis_value = (joyrx[first_player] + 128) << 8; +// break; +// case 3: +// axis_value = (joyry[first_player] + 128) << 8; +// break; + default: + axis_value = 128; } + full_axis_count++; } LOGJVS("%d:%4x ", axis, axis_value); JVS_OUT(axis_value >> 8); @@ -2493,8 +2543,8 @@ u32 jvs_io_board::handle_jvs_message(u8 *buffer_in, u32 length_in, u8 *buffer_ou break; case 0x30: // substract coin - if (buffer_in[cmdi + 1] == 1) - coin_count -= (buffer_in[cmdi + 2] << 8) + buffer_in[cmdi + 3]; + if (buffer_in[cmdi + 1] > 0 && first_player + buffer_in[cmdi + 1] - 1 < ARRAY_SIZE(coin_count)) + coin_count[first_player + buffer_in[cmdi + 1] - 1] -= (buffer_in[cmdi + 2] << 8) + buffer_in[cmdi + 3]; JVS_STATUS1(); // report byte cmdi += 4; break; diff --git a/core/hw/naomi/naomi.cpp b/core/hw/naomi/naomi.cpp index 3f2a40af8..1b75b1a03 100644 --- a/core/hw/naomi/naomi.cpp +++ b/core/hw/naomi/naomi.cpp @@ -6,6 +6,7 @@ #include "hw/holly/sb.h" #include "hw/sh4/sh4_mem.h" #include "hw/holly/holly_intc.h" +#include "hw/maple/maple_cfg.h" #include "naomi.h" #include "naomi_cart.h" @@ -634,7 +635,6 @@ void Update_naomi() } static u8 aw_maple_devs; -extern bool coin_chute; u32 libExtDevice_ReadMem_A0_006(u32 addr,u32 size) { addr &= 0x7ff; @@ -653,12 +653,13 @@ u32 libExtDevice_ReadMem_A0_006(u32 addr,u32 size) { aw_ram_test_skipped = true; return 0; } - if (coin_chute) { - // FIXME Coin Error if coin_chute is set for too long - return 0xE; + u8 coin_input = 0xF; + for (int slot = 0; slot < 4; slot++) + if (maple_atomiswave_coin_chute(slot)) + coin_input &= ~(1 << slot); + return coin_input; } - return 0xF; case 0x284: // Atomiswave maple devices // ddcc0000 where cc/dd are the types of devices on maple bus 2 and 3: diff --git a/core/hw/naomi/naomi_cart.cpp b/core/hw/naomi/naomi_cart.cpp index 1dee0c64f..e9b18e2c0 100644 --- a/core/hw/naomi/naomi_cart.cpp +++ b/core/hw/naomi/naomi_cart.cpp @@ -32,7 +32,7 @@ fd_t* RomCacheMap = NULL; u32 RomCacheMapCount; char naomi_game_id[33]; -InputDescriptors *naomi_game_inputs; +InputDescriptors *NaomiGameInputs; u8 *naomi_default_eeprom; extern RomChip sys_rom; @@ -246,7 +246,7 @@ static bool naomi_cart_LoadZip(char *filename) break; } CurrentCartridge->SetKey(game->key); - naomi_game_inputs = game->inputs; + NaomiGameInputs = game->inputs; for (int romid = 0; game->blobs[romid].filename != NULL; romid++) { diff --git a/core/hw/naomi/naomi_cart.h b/core/hw/naomi/naomi_cart.h index b5c9745dc..918de1b72 100644 --- a/core/hw/naomi/naomi_cart.h +++ b/core/hw/naomi/naomi_cart.h @@ -110,6 +110,6 @@ struct InputDescriptors AxisDescriptor axes[8]; }; -extern InputDescriptors *naomi_game_inputs; +extern InputDescriptors *NaomiGameInputs; #endif //NAOMI_CART_H diff --git a/core/linux-dist/x11.cpp b/core/linux-dist/x11.cpp index 85c6fd5f1..aa71bdbdf 100644 --- a/core/linux-dist/x11.cpp +++ b/core/linux-dist/x11.cpp @@ -84,8 +84,6 @@ Atom wmDeleteMessage; void* x11_vis; extern bool dump_frame_switch; -extern bool naomi_test_button; -extern bool coin_chute; void dc_exit(void); @@ -275,16 +273,6 @@ void input_x11_handle() x11_fullscreen = !x11_fullscreen; x11_window_set_fullscreen(x11_fullscreen); } -#if DC_PLATFORM == DC_PLATFORM_NAOMI || DC_PLATFORM == DC_PLATFORM_ATOMISWAVE - else if (e.xkey.keycode == KEY_F8) - { - coin_chute = e.type == KeyPress; - } - else if (e.xkey.keycode == KEY_F7) - { - naomi_test_button = e.type == KeyPress; - } -#endif } } break; diff --git a/core/windows/winmain.cpp b/core/windows/winmain.cpp index 1731193e9..68db4682c 100644 --- a/core/windows/winmain.cpp +++ b/core/windows/winmain.cpp @@ -188,8 +188,6 @@ u16 kcode[4] = { 0xffff, 0xffff, 0xffff, 0xffff }; u32 vks[4]; s8 joyx[4],joyy[4]; u8 rt[4],lt[4]; -extern bool coin_chute; -extern bool naomi_test_button; // Mouse extern s32 mo_x_abs; extern s32 mo_y_abs; @@ -220,12 +218,6 @@ void UpdateInputState(u32 port) std::shared_ptr gamepad = XInputGamepadDevice::GetXInputDevice(port); if (gamepad != NULL) gamepad->ReadInput(); - -#if DC_PLATFORM == DC_PLATFORM_NAOMI || DC_PLATFORM == DC_PLATFORM_ATOMISWAVE - // FIXME - coin_chute = GetAsyncKeyState(VK_F8); - naomi_test_button = GetAsyncKeyState(VK_F7); -#endif } // Windows class name to register From 4b7e4f467795ea67e38e2538c86112e92df18b34 Mon Sep 17 00:00:00 2001 From: Flyinghead Date: Sat, 6 Apr 2019 19:38:00 +0200 Subject: [PATCH 232/319] custom textures: fix race conditions --- core/rend/gles/CustomTexture.cpp | 34 ++++++++++++++++++-------------- core/rend/gles/CustomTexture.h | 2 +- core/rend/gles/gles.cpp | 3 --- core/rend/gles/gles.h | 9 +++++---- core/rend/gles/gltex.cpp | 19 ++++-------------- 5 files changed, 29 insertions(+), 38 deletions(-) diff --git a/core/rend/gles/CustomTexture.cpp b/core/rend/gles/CustomTexture.cpp index 3d8db4561..0a9a20176 100644 --- a/core/rend/gles/CustomTexture.cpp +++ b/core/rend/gles/CustomTexture.cpp @@ -46,23 +46,28 @@ void CustomTexture::LoaderThread() if (texture != NULL) { - // FIXME texture may have been deleted. Need to detect this. texture->ComputeHash(); - int width, height; - u8 *image_data = LoadCustomTexture(texture->texture_hash, width, height); - if (image_data == NULL) + if (texture->custom_image_data != NULL) { - image_data = LoadCustomTexture(texture->old_texture_hash, width, height); + delete [] texture->custom_image_data; + texture->custom_image_data = NULL; } - if (image_data != NULL) + if (!texture->dirty) { - if (texture->custom_image_data != NULL) - delete [] texture->custom_image_data; - texture->custom_width = width; - texture->custom_height = height; - texture->custom_image_data = image_data; + int width, height; + u8 *image_data = LoadCustomTexture(texture->texture_hash, width, height); + if (image_data == NULL) + { + image_data = LoadCustomTexture(texture->old_texture_hash, width, height); + } + if (image_data != NULL) + { + texture->custom_width = width; + texture->custom_height = height; + texture->custom_image_data = image_data; + } } - texture->custom_load_in_progress = false; + texture->custom_load_in_progress--; } } while (texture != NULL); @@ -140,10 +145,9 @@ u8* CustomTexture::LoadCustomTexture(u32 hash, int& width, int& height) void CustomTexture::LoadCustomTextureAsync(TextureCacheData *texture_data) { if (!Init()) - { - texture_data->custom_load_in_progress = false; return; - } + + texture_data->custom_load_in_progress++; work_queue_mutex.Lock(); work_queue.insert(work_queue.begin(), texture_data); work_queue_mutex.Unlock(); diff --git a/core/rend/gles/CustomTexture.h b/core/rend/gles/CustomTexture.h index d0847717b..830855542 100644 --- a/core/rend/gles/CustomTexture.h +++ b/core/rend/gles/CustomTexture.h @@ -53,7 +53,7 @@ private: cThread loader_thread; #endif cResetEvent wakeup_thread; - std::vector work_queue; + std::vector work_queue; cMutex work_queue_mutex; }; diff --git a/core/rend/gles/gles.cpp b/core/rend/gles/gles.cpp index b97d9e4e2..b243a6ffc 100644 --- a/core/rend/gles/gles.cpp +++ b/core/rend/gles/gles.cpp @@ -1838,9 +1838,6 @@ bool RenderFrame() glBufferData(GL_ARRAY_BUFFER,pvrrc.modtrig.bytes(),pvrrc.modtrig.head(),GL_STREAM_DRAW); glCheck(); } - int offs_x=ds2s_offs_x+0.5f; - //this needs to be scaled - //not all scaling affects pixel operations, scale to adjust for that scale_x *= scissoring_scale_x; diff --git a/core/rend/gles/gles.h b/core/rend/gles/gles.h index 832ecf31a..c6339b802 100755 --- a/core/rend/gles/gles.h +++ b/core/rend/gles/gles.h @@ -1,5 +1,6 @@ #pragma once #include +#include #include "rend/rend.h" #if (defined(GLES) && !defined(TARGET_NACL32) && HOST_OS != OS_DARWIN && !defined(USE_SDL)) || defined(_ANDROID) @@ -271,10 +272,10 @@ struct TextureCacheData //a texture can't be both VQ and PAL at the same time u32 texture_hash; // xxhash of texture data, used for custom textures u32 old_texture_hash; // legacy hash - u8* custom_image_data; // loaded custom image data - u32 custom_width; - u32 custom_height; - bool custom_load_in_progress; + u8* volatile custom_image_data; // loaded custom image data + volatile u32 custom_width; + volatile u32 custom_height; + std::atomic_int custom_load_in_progress; void PrintTextureName(); diff --git a/core/rend/gles/gltex.cpp b/core/rend/gles/gltex.cpp index ef6263ddc..5bf3b45eb 100644 --- a/core/rend/gles/gltex.cpp +++ b/core/rend/gles/gltex.cpp @@ -296,10 +296,7 @@ void TextureCacheData::Update() } } if (settings.rend.CustomTextures) - { - custom_load_in_progress = true; custom_texture.LoadCustomTextureAsync(this); - } void *temp_tex_buffer = NULL; u32 upscaled_w = w; @@ -428,7 +425,7 @@ void TextureCacheData::UploadToGPU(GLuint textype, int width, int height, u8 *te void TextureCacheData::CheckCustomTexture() { - if (custom_image_data != NULL) + if (custom_load_in_progress == 0 && custom_image_data != NULL) { UploadToGPU(GL_UNSIGNED_BYTE, custom_width, custom_height, custom_image_data); delete [] custom_image_data; @@ -446,7 +443,7 @@ bool TextureCacheData::NeedsUpdate() { bool TextureCacheData::Delete() { - if (custom_load_in_progress) + if (custom_load_in_progress > 0) return false; if (pData) { @@ -736,11 +733,7 @@ TextureCacheData *getTextureCacheData(TSP tsp, TCW tcw) { } else //create if not existing { - TextureCacheData tfc={0}; - TexCache[key] = tfc; - - tx=TexCache.find(key); - tf=&tx->second; + tf=&TexCache[key]; tf->tsp = tsp; tf->tcw = tcw; @@ -800,11 +793,7 @@ text_info raw_GetTexture(TSP tsp, TCW tcw) } else //create if not existing { - TextureCacheData tfc = { 0 }; - TexCache[key] = tfc; - - tx = TexCache.find(key); - tf = &tx->second; + tf = &TexCache[key]; tf->tsp = tsp; tf->tcw = tcw; From 408d16b2993302b1c5384b4876cf7e14df328d01 Mon Sep 17 00:00:00 2001 From: Flyinghead Date: Sun, 7 Apr 2019 23:33:24 +0200 Subject: [PATCH 233/319] imgui: clear color buffer before drawing background tex fixes blinking edges on nvidia shield and other glitches --- core/rend/gles/imgui_impl_opengl3.cpp | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/core/rend/gles/imgui_impl_opengl3.cpp b/core/rend/gles/imgui_impl_opengl3.cpp index 853e41563..ddb363c1c 100644 --- a/core/rend/gles/imgui_impl_opengl3.cpp +++ b/core/rend/gles/imgui_impl_opengl3.cpp @@ -510,6 +510,9 @@ void ImGui_ImplOpenGL3_DestroyDeviceObjects() void ImGui_ImplOpenGL3_DrawBackground() { + glcache.Disable(GL_SCISSOR_TEST); + glcache.ClearColor(0, 0, 0, 0); + glClear(GL_COLOR_BUFFER_BIT); if (g_BackgroundTexture != 0) { ImGuiIO& io = ImGui::GetIO(); @@ -520,11 +523,6 @@ void ImGui_ImplOpenGL3_DrawBackground() ImGui::GetWindowDrawList()->AddImage((ImTextureID)(uintptr_t)g_BackgroundTexture, ImVec2(0, 0), io.DisplaySize, ImVec2(0, 1), ImVec2(1, 0), 0xffffffff); ImGui::End(); } - else - { - glcache.ClearColor(0, 0, 0, 0); - glClear(GL_COLOR_BUFFER_BIT); - } } ImTextureID ImGui_ImplOpenGL3_CreateVmuTexture(const unsigned int *data) From 47bb509f02802cf6c8a233d249573ba6c118b1a2 Mon Sep 17 00:00:00 2001 From: Flyinghead Date: Mon, 8 Apr 2019 00:21:06 +0200 Subject: [PATCH 234/319] Add horizontal screen stretching option fix scissor test when scaling/stretching fix infiniloop when starting a game fails --- core/nullDC.cpp | 7 ++++- core/rend/gl4/gldraw.cpp | 12 ++++++--- core/rend/gl4/gles.cpp | 25 ++++++++--------- core/rend/gles/gldraw.cpp | 34 ++++++++++++++--------- core/rend/gles/gles.cpp | 27 ++++++++++--------- core/rend/gles/gles.h | 1 + core/rend/gles/gltex.cpp | 57 ++++++++++++++++++++++++++------------- core/rend/gui.cpp | 4 +++ core/types.h | 3 ++- 9 files changed, 108 insertions(+), 62 deletions(-) diff --git a/core/nullDC.cpp b/core/nullDC.cpp index 89c637803..844e38ba9 100755 --- a/core/nullDC.cpp +++ b/core/nullDC.cpp @@ -313,7 +313,9 @@ int dc_start_game(const char *path) #if DC_PLATFORM == DC_PLATFORM_DREAMCAST if (!settings.bios.UseReios) #endif - LoadRomFiles(get_readonly_data_path(DATA_PATH)); + if (!LoadRomFiles(get_readonly_data_path(DATA_PATH))) + return -5; + #if DC_PLATFORM == DC_PLATFORM_DREAMCAST if (path == NULL) { @@ -513,6 +515,7 @@ void InitSettings() settings.rend.CustomTextures = false; settings.rend.DumpTextures = false; settings.rend.ScreenScaling = 100; + settings.rend.ScreenStretching = 100; settings.rend.Fog = true; settings.pvr.ta_skip = 0; @@ -596,6 +599,7 @@ void LoadSettings(bool game_specific) settings.rend.DumpTextures = cfgLoadBool(config_section, "rend.DumpTextures", settings.rend.DumpTextures); settings.rend.ScreenScaling = cfgLoadInt(config_section, "rend.ScreenScaling", settings.rend.ScreenScaling); settings.rend.ScreenScaling = min(max(1, settings.rend.ScreenScaling), 100); + settings.rend.ScreenStretching = cfgLoadInt(config_section, "rend.ScreenStretching", settings.rend.ScreenStretching); settings.rend.Fog = cfgLoadBool(config_section, "rend.Fog", settings.rend.Fog); settings.pvr.ta_skip = cfgLoadInt(config_section, "ta.skip", settings.pvr.ta_skip); @@ -720,6 +724,7 @@ void SaveSettings() cfgSaveBool("config", "rend.CustomTextures", settings.rend.CustomTextures); cfgSaveBool("config", "rend.DumpTextures", settings.rend.DumpTextures); cfgSaveInt("config", "rend.ScreenScaling", settings.rend.ScreenScaling); + cfgSaveInt("config", "rend.ScreenStretching", settings.rend.ScreenStretching); cfgSaveBool("config", "rend.Fog", settings.rend.Fog); cfgSaveInt("config", "ta.skip", settings.pvr.ta_skip); cfgSaveInt("config", "pvr.rend", settings.pvr.rend); diff --git a/core/rend/gl4/gldraw.cpp b/core/rend/gl4/gldraw.cpp index 447a97461..75ad7eda9 100644 --- a/core/rend/gl4/gldraw.cpp +++ b/core/rend/gl4/gldraw.cpp @@ -697,11 +697,15 @@ void gl4DrawFramebuffer(float w, float h) bool gl4_render_output_framebuffer() { - glBindFramebuffer(GL_FRAMEBUFFER, 0); glViewport(0, 0, screen_width, screen_height); - if (gl.ofbo.tex == 0) + glcache.Disable(GL_SCISSOR_TEST); + if (gl.ofbo.fbo == 0) return false; - - gl4_draw_quad_texture(gl.ofbo.tex, true); + glBindFramebuffer(GL_READ_FRAMEBUFFER, gl.ofbo.fbo); + glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0); + glBlitFramebuffer(0, 0, gl.ofbo.width, gl.ofbo.height, + 0, 0, screen_width, screen_height, + GL_COLOR_BUFFER_BIT, GL_LINEAR); + glBindFramebuffer(GL_FRAMEBUFFER, 0); return true; } diff --git a/core/rend/gl4/gles.cpp b/core/rend/gl4/gles.cpp index ecda63dec..93700f20b 100644 --- a/core/rend/gl4/gles.cpp +++ b/core/rend/gl4/gles.cpp @@ -662,14 +662,15 @@ static bool RenderFrame() Handle Dc to screen scaling */ float screen_scaling = is_rtt ? 1.f : settings.rend.ScreenScaling / 100.f; + float screen_stretching = settings.rend.ScreenStretching / 100.f; + float dc2s_scale_h = is_rtt ? (screen_width / dc_width) : (screen_height / 480.0); - dc2s_scale_h *= screen_scaling; - float ds2s_offs_x = is_rtt ? 0 : (((screen_width * screen_scaling) - dc2s_scale_h * 640.0) / 2); + float ds2s_offs_x = is_rtt ? 0 : ((screen_width - dc2s_scale_h * 640.0 * screen_stretching) / 2); //-1 -> too much to left - gl4ShaderUniforms.scale_coefs[0] = 2.0f / (screen_width * screen_scaling / dc2s_scale_h * scale_x); + gl4ShaderUniforms.scale_coefs[0] = 2.0f / (screen_width / dc2s_scale_h * scale_x) * screen_stretching; gl4ShaderUniforms.scale_coefs[1] = (is_rtt ? 2 : -2) / dc_height; // FIXME CT2 needs 480 here instead of dc_height=512 - gl4ShaderUniforms.scale_coefs[2] = 1 - 2 * ds2s_offs_x / (screen_width * screen_scaling); + gl4ShaderUniforms.scale_coefs[2] = 1 - 2 * ds2s_offs_x / screen_width; gl4ShaderUniforms.scale_coefs[3] = (is_rtt ? 1 : -1); gl4ShaderUniforms.extra_depth_scale = settings.rend.ExtraDepthScale; @@ -763,7 +764,7 @@ static bool RenderFrame() { if (settings.rend.ScreenScaling != 100 || gl.swap_buffer_not_preserved) { - output_fbo = init_output_framebuffer(screen_width * screen_scaling, screen_height * screen_scaling); + output_fbo = init_output_framebuffer(screen_width * screen_scaling + 0.5f, screen_height * screen_scaling + 0.5f); } else { @@ -826,21 +827,21 @@ static bool RenderFrame() if (!is_rtt) { // Add x offset for aspect ratio > 4/3 - min_x = min_x * dc2s_scale_h + ds2s_offs_x; + min_x = min_x * dc2s_scale_h * screen_stretching + ds2s_offs_x * screen_scaling; // Invert y coordinates when rendering to screen - min_y = screen_height * screen_scaling - (min_y + height) * dc2s_scale_h; - width *= dc2s_scale_h; - height *= dc2s_scale_h; + min_y = (screen_height - (min_y + height) * dc2s_scale_h) * screen_scaling; + width *= dc2s_scale_h * screen_scaling * screen_stretching; + height *= dc2s_scale_h * screen_scaling; if (ds2s_offs_x > 0) { - float rounded_offs_x = ds2s_offs_x + 0.5f; + float scaled_offs_x = ds2s_offs_x * screen_scaling; glcache.ClearColor(0.f, 0.f, 0.f, 0.f); glcache.Enable(GL_SCISSOR_TEST); - glScissor(0, 0, rounded_offs_x, screen_height); + glScissor(0, 0, scaled_offs_x + 0.5f, screen_height * screen_scaling + 0.5f); glClear(GL_COLOR_BUFFER_BIT); - glScissor(screen_width - rounded_offs_x, 0, rounded_offs_x, screen_height); + glScissor(screen_width * screen_scaling - scaled_offs_x + 0.5f, 0, scaled_offs_x + 1.f, screen_height * screen_scaling + 0.5f); glClear(GL_COLOR_BUFFER_BIT); } } diff --git a/core/rend/gles/gldraw.cpp b/core/rend/gles/gldraw.cpp index d337848cb..618a3e33f 100644 --- a/core/rend/gles/gldraw.cpp +++ b/core/rend/gles/gldraw.cpp @@ -1141,17 +1141,27 @@ void DrawFramebuffer(float w, float h) bool render_output_framebuffer() { -#if HOST_OS != OS_DARWIN - //Fix this in a proper way - glBindFramebuffer(GL_FRAMEBUFFER, 0); -#endif - glViewport(0, 0, screen_width, screen_height); - if (gl.ofbo.tex == 0) - return false; - - float scl = 480.f / screen_height; - float tx = (screen_width * scl - 640.f) / 2; - DrawQuad(gl.ofbo.tex, -tx, 0, 640.f + tx * 2, 480.f, 0, 1, 1, 0); - + glcache.Disable(GL_SCISSOR_TEST); + if (gl.gl_major < 3) + { + glViewport(0, 0, screen_width, screen_height); + if (gl.ofbo.tex == 0) + return false; + glBindFramebuffer(GL_FRAMEBUFFER, 0); + float scl = 480.f / screen_height; + float tx = (screen_width * scl - 640.f) / 2; + DrawQuad(gl.ofbo.tex, -tx, 0, 640.f + tx * 2, 480.f, 0, 1, 1, 0); + } + else + { + if (gl.ofbo.fbo == 0) + return false; + glBindFramebuffer(GL_READ_FRAMEBUFFER, gl.ofbo.fbo); + glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0); + glBlitFramebuffer(0, 0, gl.ofbo.width, gl.ofbo.height, + 0, 0, screen_width, screen_height, + GL_COLOR_BUFFER_BIT, GL_LINEAR); + glBindFramebuffer(GL_FRAMEBUFFER, 0); + } return true; } diff --git a/core/rend/gles/gles.cpp b/core/rend/gles/gles.cpp index b243a6ffc..fad89106d 100644 --- a/core/rend/gles/gles.cpp +++ b/core/rend/gles/gles.cpp @@ -1678,15 +1678,16 @@ bool RenderFrame() /* Handle Dc to screen scaling */ - float screen_scaling = is_rtt ? 1.f : settings.rend.ScreenScaling / 100.f; + float screen_stretching = settings.rend.ScreenStretching / 100.f; + float screen_scaling = settings.rend.ScreenScaling / 100.f; + float dc2s_scale_h = is_rtt ? (screen_width / dc_width) : (screen_height / 480.0); - dc2s_scale_h *= screen_scaling; - float ds2s_offs_x = is_rtt ? 0 : (((screen_width * screen_scaling) - dc2s_scale_h * 640.0) / 2); + float ds2s_offs_x = is_rtt ? 0 : ((screen_width - dc2s_scale_h * 640.0 * screen_stretching) / 2); //-1 -> too much to left - ShaderUniforms.scale_coefs[0] = 2.0f / (screen_width * screen_scaling / dc2s_scale_h * scale_x); + ShaderUniforms.scale_coefs[0] = 2.0f / (screen_width / dc2s_scale_h * scale_x) * screen_stretching; ShaderUniforms.scale_coefs[1]= (is_rtt ? 2 : -2) / dc_height; // FIXME CT2 needs 480 here instead of dc_height=512 - ShaderUniforms.scale_coefs[2]= 1 - 2 * ds2s_offs_x / (screen_width * screen_scaling); + ShaderUniforms.scale_coefs[2]= 1 - 2 * ds2s_offs_x / screen_width; ShaderUniforms.scale_coefs[3]= (is_rtt ? 1 : -1); @@ -1791,7 +1792,7 @@ bool RenderFrame() { if (settings.rend.ScreenScaling != 100 || gl.swap_buffer_not_preserved) { - init_output_framebuffer(screen_width * screen_scaling, screen_height * screen_scaling); + init_output_framebuffer(screen_width * screen_scaling + 0.5f, screen_height * screen_scaling + 0.5f); } else { @@ -1857,21 +1858,21 @@ bool RenderFrame() if (!is_rtt) { // Add x offset for aspect ratio > 4/3 - min_x = min_x * dc2s_scale_h + ds2s_offs_x; + min_x = min_x * dc2s_scale_h * screen_stretching + ds2s_offs_x * screen_scaling; // Invert y coordinates when rendering to screen - min_y = screen_height * screen_scaling - (min_y + height) * dc2s_scale_h; - width *= dc2s_scale_h; - height *= dc2s_scale_h; + min_y = (screen_height - (min_y + height) * dc2s_scale_h) * screen_scaling; + width *= dc2s_scale_h * screen_scaling * screen_stretching; + height *= dc2s_scale_h * screen_scaling; if (ds2s_offs_x > 0) { - float rounded_offs_x = ds2s_offs_x + 0.5f; + float scaled_offs_x = ds2s_offs_x * screen_scaling; glcache.ClearColor(0.f, 0.f, 0.f, 0.f); glcache.Enable(GL_SCISSOR_TEST); - glScissor(0, 0, rounded_offs_x, screen_height); + glScissor(0, 0, scaled_offs_x + 0.5f, screen_height * screen_scaling + 0.5f); glClear(GL_COLOR_BUFFER_BIT); - glScissor(screen_width - rounded_offs_x, 0, rounded_offs_x, screen_height); + glScissor(screen_width * screen_scaling - scaled_offs_x + 0.5f, 0, scaled_offs_x + 1.f, screen_height * screen_scaling + 0.5f); glClear(GL_COLOR_BUFFER_BIT); } } diff --git a/core/rend/gles/gles.h b/core/rend/gles/gles.h index c6339b802..7e0cec3cb 100755 --- a/core/rend/gles/gles.h +++ b/core/rend/gles/gles.h @@ -120,6 +120,7 @@ struct gl_ctx struct { GLuint depthb; + GLuint colorb; GLuint tex; GLuint fbo; int width; diff --git a/core/rend/gles/gltex.cpp b/core/rend/gles/gltex.cpp index 5bf3b45eb..1a3802fb7 100644 --- a/core/rend/gles/gltex.cpp +++ b/core/rend/gles/gltex.cpp @@ -996,13 +996,7 @@ GLuint init_output_framebuffer(int width, int height) { if (width != gl.ofbo.width || height != gl.ofbo.height) { - if (gl.ofbo.fbo != 0) - { - glDeleteFramebuffers(1, &gl.ofbo.fbo); - gl.ofbo.fbo = 0; - glDeleteRenderbuffers(1, &gl.ofbo.depthb); - glcache.DeleteTextures(1, &gl.ofbo.tex); - } + free_output_framebuffer(); gl.ofbo.width = width; gl.ofbo.height = height; } @@ -1026,15 +1020,25 @@ GLuint init_output_framebuffer(int width, int height) else glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, width, height); - // Create a texture for rendering to - gl.ofbo.tex = glcache.GenTexture(); - glcache.BindTexture(GL_TEXTURE_2D, gl.ofbo.tex); + if (gl.gl_major < 3) + { + // Create a texture for rendering to + gl.ofbo.tex = glcache.GenTexture(); + glcache.BindTexture(GL_TEXTURE_2D, gl.ofbo.tex); - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0); - glcache.TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - glcache.TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - glcache.TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); - glcache.TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0); + glcache.TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glcache.TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glcache.TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glcache.TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + } + else + { + // Use a renderbuffer and glBlitFramebuffer + glGenRenderbuffers(1, &gl.ofbo.colorb); + glBindRenderbuffer(GL_RENDERBUFFER, gl.ofbo.colorb); + glRenderbufferStorage(GL_RENDERBUFFER, GL_RGBA8, width, height); + } // Create the framebuffer glGenFramebuffers(1, &gl.ofbo.fbo); @@ -1046,13 +1050,20 @@ GLuint init_output_framebuffer(int width, int height) if (!gl.is_gles || gl.GL_OES_packed_depth_stencil_supported) glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, gl.ofbo.depthb); - // Attach the texture to the FBO - glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, gl.ofbo.tex, 0); + // Attach the texture/renderbuffer to the FBO + if (gl.gl_major < 3) + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, gl.ofbo.tex, 0); + else + glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, gl.ofbo.colorb); // Check that our FBO creation was successful GLuint uStatus = glCheckFramebufferStatus(GL_FRAMEBUFFER); verify(uStatus == GL_FRAMEBUFFER_COMPLETE); + + glcache.Disable(GL_SCISSOR_TEST); + glcache.ClearColor(0.f, 0.f, 0.f, 0.f); + glClear(GL_COLOR_BUFFER_BIT); } else glBindFramebuffer(GL_FRAMEBUFFER, gl.ofbo.fbo); @@ -1071,7 +1082,15 @@ void free_output_framebuffer() gl.ofbo.fbo = 0; glDeleteRenderbuffers(1, &gl.ofbo.depthb); gl.ofbo.depthb = 0; - glcache.DeleteTextures(1, &gl.ofbo.tex); - gl.ofbo.tex = 0; + if (gl.ofbo.tex != 0) + { + glcache.DeleteTextures(1, &gl.ofbo.tex); + gl.ofbo.tex = 0; + } + if (gl.ofbo.colorb != 0) + { + glDeleteRenderbuffers(1, &gl.ofbo.colorb); + gl.ofbo.colorb = 0; + } } } diff --git a/core/rend/gui.cpp b/core/rend/gui.cpp index cfc134cbc..6c096d755 100644 --- a/core/rend/gui.cpp +++ b/core/rend/gui.cpp @@ -952,6 +952,9 @@ static void gui_display_settings() ImGui::SliderInt("Scaling", (int *)&settings.rend.ScreenScaling, 1, 100); ImGui::SameLine(); ShowHelpMarker("Downscaling factor relative to native screen resolution. Higher is better"); + ImGui::SliderInt("Horizontal Stretching", (int *)&settings.rend.ScreenStretching, 100, 150); + ImGui::SameLine(); + ShowHelpMarker("Stretch the screen horizontally"); ImGui::SliderInt("Frame Skipping", (int *)&settings.pvr.ta_skip, 0, 6); ImGui::SameLine(); ShowHelpMarker("Number of frames to skip between two actually rendered frames"); @@ -1251,6 +1254,7 @@ static void gui_start_game(const std::string& path) { gui_state = Main; game_started = false; + cfgSetVirtual("config", "image", ""); switch (rc) { case -3: error_msg = "Audio/video initialization failed"; diff --git a/core/types.h b/core/types.h index a9a835ac1..bfd779ef8 100644 --- a/core/types.h +++ b/core/types.h @@ -632,7 +632,8 @@ struct settings_t f32 ExtraDepthScale; bool CustomTextures; bool DumpTextures; - int ScreenScaling; // in percent. 50 means half the native resolution + int ScreenScaling; // in percent. 50 means half the native resolution + int ScreenStretching; // in percent. 150 means stretch from 4/3 to 6/3 bool Fog; } rend; From d78fc620551dbaa17897e641d993e20bccd1c83b Mon Sep 17 00:00:00 2001 From: Flyinghead Date: Mon, 8 Apr 2019 10:11:28 +0200 Subject: [PATCH 235/319] android: fix Send Logs crash --- .../src/main/java/com/reicast/emulator/BaseGLActivity.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/shell/android-studio/reicast/src/main/java/com/reicast/emulator/BaseGLActivity.java b/shell/android-studio/reicast/src/main/java/com/reicast/emulator/BaseGLActivity.java index a0b28e302..94fc19410 100644 --- a/shell/android-studio/reicast/src/main/java/com/reicast/emulator/BaseGLActivity.java +++ b/shell/android-studio/reicast/src/main/java/com/reicast/emulator/BaseGLActivity.java @@ -298,7 +298,7 @@ public abstract class BaseGLActivity extends Activity implements ActivityCompat. } // Called from native code - private void generateErrorLog() { + protected void generateErrorLog() { try { new GenerateLogs(this).execute(getFilesDir().getAbsolutePath()); } catch (RuntimeException e) { From f5a60ee5f18c18508db3e29e99bbcd650d3978e0 Mon Sep 17 00:00:00 2001 From: Flyinghead Date: Mon, 8 Apr 2019 15:54:37 +0200 Subject: [PATCH 236/319] New option to display VMUs in game --- core/nullDC.cpp | 3 +++ core/rend/gui.cpp | 55 +++++++++++++++++++++++++++++++++-------------- core/types.h | 1 + 3 files changed, 43 insertions(+), 16 deletions(-) diff --git a/core/nullDC.cpp b/core/nullDC.cpp index 844e38ba9..87155fefc 100755 --- a/core/nullDC.cpp +++ b/core/nullDC.cpp @@ -517,6 +517,7 @@ void InitSettings() settings.rend.ScreenScaling = 100; settings.rend.ScreenStretching = 100; settings.rend.Fog = true; + settings.rend.FloatVMUs = false; settings.pvr.ta_skip = 0; settings.pvr.rend = 0; @@ -601,6 +602,7 @@ void LoadSettings(bool game_specific) settings.rend.ScreenScaling = min(max(1, settings.rend.ScreenScaling), 100); settings.rend.ScreenStretching = cfgLoadInt(config_section, "rend.ScreenStretching", settings.rend.ScreenStretching); settings.rend.Fog = cfgLoadBool(config_section, "rend.Fog", settings.rend.Fog); + settings.rend.FloatVMUs = cfgLoadBool(config_section, "rend.FloatVMUs", settings.rend.FloatVMUs); settings.pvr.ta_skip = cfgLoadInt(config_section, "ta.skip", settings.pvr.ta_skip); settings.pvr.rend = cfgLoadInt(config_section, "pvr.rend", settings.pvr.rend); @@ -726,6 +728,7 @@ void SaveSettings() cfgSaveInt("config", "rend.ScreenScaling", settings.rend.ScreenScaling); cfgSaveInt("config", "rend.ScreenStretching", settings.rend.ScreenStretching); cfgSaveBool("config", "rend.Fog", settings.rend.Fog); + cfgSaveBool("config", "rend.FloatVMUs", settings.rend.FloatVMUs); cfgSaveInt("config", "ta.skip", settings.pvr.ta_skip); cfgSaveInt("config", "pvr.rend", settings.pvr.rend); diff --git a/core/rend/gui.cpp b/core/rend/gui.cpp index 6c096d755..7a0fb107d 100644 --- a/core/rend/gui.cpp +++ b/core/rend/gui.cpp @@ -294,7 +294,9 @@ static void gui_display_commands() if (!settings_opening) ImGui_ImplOpenGL3_DrawBackground(); - display_vmus(); + if (!settings.rend.FloatVMUs) + // If floating VMUs, they are already visible on the background + display_vmus(); ImGui::SetNextWindowPos(ImVec2(screen_width / 2.f, screen_height / 2.f), ImGuiCond_Always, ImVec2(0.5f, 0.5f)); ImGui::SetNextWindowSize(ImVec2(330 * scaling, 0)); @@ -617,6 +619,8 @@ void directory_selected_callback(bool cancelled, std::string selection) static void gui_display_settings() { + static bool maple_devices_changed; + ImGui_Impl_NewFrame(); ImGui::NewFrame(); @@ -639,10 +643,14 @@ static void gui_display_settings() gui_state = Commands; else gui_state = Main; + if (maple_devices_changed) + { + maple_devices_changed = false; #if DC_PLATFORM == DC_PLATFORM_DREAMCAST - maple_ReconnectDevices(); - reset_vmus(); + maple_ReconnectDevices(); + reset_vmus(); #endif + } SaveSettings(); } if (game_started) @@ -813,7 +821,10 @@ static void gui_display_settings() { bool is_selected = settings.input.maple_devices[bus] == maple_device_type_from_index(i); if (ImGui::Selectable(maple_device_types[i], &is_selected)) + { settings.input.maple_devices[bus] = maple_device_type_from_index(i); + maple_devices_changed = true; + } if (is_selected) ImGui::SetItemDefaultFocus(); } @@ -831,7 +842,10 @@ static void gui_display_settings() { bool is_selected = settings.input.maple_expansion_devices[bus][port] == maple_expansion_device_type_from_index(i); if (ImGui::Selectable(maple_expansion_device_types[i], &is_selected)) + { settings.input.maple_expansion_devices[bus][port] = maple_expansion_device_type_from_index(i); + maple_devices_changed = true; + } if (is_selected) ImGui::SetItemDefaultFocus(); } @@ -949,6 +963,9 @@ static void gui_display_settings() ImGui::Checkbox("Show FPS Counter", &settings.rend.ShowFPS); ImGui::SameLine(); ShowHelpMarker("Show on-screen frame/sec counter"); + ImGui::Checkbox("Show VMU in game", &settings.rend.FloatVMUs); + ImGui::SameLine(); + ShowHelpMarker("Show the VMU LCD screens while in game"); ImGui::SliderInt("Scaling", (int *)&settings.rend.ScreenScaling, 1, 100); ImGui::SameLine(); ShowHelpMarker("Downscaling factor relative to native screen resolution. Higher is better"); @@ -1459,26 +1476,32 @@ void gui_display_osd() if (osd_message.empty()) { message = getFPSNotification(); - if (message.empty()) - return; } else message = osd_message; - ImGui_Impl_NewFrame(); - ImGui::NewFrame(); + if (!message.empty() || settings.rend.FloatVMUs) + { + ImGui_Impl_NewFrame(); + ImGui::NewFrame(); - ImGui::SetNextWindowBgAlpha(0); - ImGui::SetNextWindowPos(ImVec2(0, screen_height), ImGuiCond_Always, ImVec2(0.f, 1.f)); // Lower left corner + if (!message.empty()) + { + ImGui::SetNextWindowBgAlpha(0); + ImGui::SetNextWindowPos(ImVec2(0, screen_height), ImGuiCond_Always, ImVec2(0.f, 1.f)); // Lower left corner - ImGui::Begin("##osd", NULL, ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_NoNav - | ImGuiWindowFlags_NoInputs | ImGuiWindowFlags_NoBackground); - ImGui::SetWindowFontScale(1.5); - ImGui::TextColored(ImVec4(1, 1, 0, 0.7), "%s", message.c_str()); - ImGui::End(); + ImGui::Begin("##osd", NULL, ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_NoNav + | ImGuiWindowFlags_NoInputs | ImGuiWindowFlags_NoBackground); + ImGui::SetWindowFontScale(1.5); + ImGui::TextColored(ImVec4(1, 1, 0, 0.7), "%s", message.c_str()); + ImGui::End(); + } + if (settings.rend.FloatVMUs) + display_vmus(); - ImGui::Render(); - ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData()); + ImGui::Render(); + ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData()); + } } void gui_open_onboarding() diff --git a/core/types.h b/core/types.h index bfd779ef8..2479fef25 100644 --- a/core/types.h +++ b/core/types.h @@ -635,6 +635,7 @@ struct settings_t int ScreenScaling; // in percent. 50 means half the native resolution int ScreenStretching; // in percent. 150 means stretch from 4/3 to 6/3 bool Fog; + bool FloatVMUs; } rend; struct From cf7434a9e6609c15534335baa3461eaa7e87a0fd Mon Sep 17 00:00:00 2001 From: Flyinghead Date: Mon, 8 Apr 2019 17:09:10 +0200 Subject: [PATCH 237/319] atomiswave: fix coin input. Subclass sega controller instead of #def --- core/hw/maple/maple_cfg.cpp | 2 +- core/hw/maple/maple_devs.cpp | 118 ++++++++++++++++++++++------------- 2 files changed, 77 insertions(+), 43 deletions(-) diff --git a/core/hw/maple/maple_cfg.cpp b/core/hw/maple/maple_cfg.cpp index f14562411..fd6c18d18 100644 --- a/core/hw/maple/maple_cfg.cpp +++ b/core/hw/maple/maple_cfg.cpp @@ -115,7 +115,7 @@ bool maple_atomiswave_coin_chute(int slot) { for (int i = 0; i < 16; i++) { - if (awave_button_mapping[i] == AWAVE_COIN_KEY && (kcode[slot] & (1 << i)) == 0) + if ((kcode[slot] & (1 << i)) == 0 && awave_button_mapping[i] == AWAVE_COIN_KEY) return true; } return false; diff --git a/core/hw/maple/maple_devs.cpp b/core/hw/maple/maple_devs.cpp index a26a3f01a..8ed632777 100755 --- a/core/hw/maple/maple_devs.cpp +++ b/core/hw/maple/maple_devs.cpp @@ -199,6 +199,35 @@ struct maple_base: maple_device */ struct maple_sega_controller: maple_base { + virtual u32 get_capabilities() { + // byte 0: 0 0 0 0 0 0 0 0 + // byte 1: 0 0 a5 a4 a3 a2 a1 a0 + // byte 2: R2 L2 D2 U2 D X Y Z + // byte 3: R L D U St A B C + + return 0xfe060f00; // 4 analog axes (0-3) X Y A B Start U D L R + } + + virtual u32 process_kcode(u32 kcode) { + return kcode; + } + + virtual u32 get_analog_axis(int index, const PlainJoystickState &pjs) { + switch (index) + { + case 0: + return pjs.trigger[PJTI_R]; // Right trigger + case 1: + return pjs.trigger[PJTI_L]; // Left trigger + case 2: + return pjs.joy[PJAI_X1]; // Stick X + case 3: + return pjs.joy[PJAI_Y1]; // Stick Y + default: + return 0x80; // unused + } + } + virtual MapleDeviceType get_device_type() { return MDT_SegaController; @@ -216,14 +245,9 @@ struct maple_sega_controller: maple_base //struct data //3*4 -#if DC_PLATFORM != DC_PLATFORM_ATOMISWAVE - w32(0xfe060f00); -#else - // More buttons, more digital axes - w32(0xff663f00); -#endif - w32( 0); - w32( 0); + w32(get_capabilities()); + w32(0); + w32(0); //1 area code w8(0xFF); @@ -254,54 +278,28 @@ struct maple_sega_controller: maple_base //4 w32(MFID_0_Input); -#if DC_PLATFORM != DC_PLATFORM_ATOMISWAVE //state data //2 key code - w16(pjs.kcode); + w16(process_kcode(pjs.kcode)); //triggers //1 R - w8(pjs.trigger[PJTI_R]); + w8(get_analog_axis(0, pjs)); //1 L - w8(pjs.trigger[PJTI_L]); + w8(get_analog_axis(1, pjs)); //joyx //1 - w8(pjs.joy[PJAI_X1]); + w8(get_analog_axis(2, pjs)); //joyy //1 - w8(pjs.joy[PJAI_Y1]); + w8(get_analog_axis(3, pjs)); //not used //1 - w8(0x80); + w8(get_analog_axis(4, pjs)); //1 - w8(0x80); -#else - //state data - //2 key code - w16(pjs.kcode | AWAVE_TRIGGER_KEY); - - //not used - //1 - w8(0); - //1 - w8(0); - - //joyx - //1 - w8(pjs.joy[PJAI_X1]); - //joyy - //1 - w8(pjs.joy[PJAI_Y1]); - - //joyrx - //1 - w8(pjs.joy[PJAI_X2]); - //joyry - //1 - w8(pjs.joy[PJAI_Y2]); -#endif + w8(get_analog_axis(5, pjs)); } return MDRS_DataTransfer; @@ -313,6 +311,38 @@ struct maple_sega_controller: maple_base } }; +struct maple_atomiswave_controller: maple_sega_controller +{ + virtual u32 get_capabilities() { + // byte 0: 0 0 0 0 0 0 0 0 + // byte 1: 0 0 a5 a4 a3 a2 a1 a0 + // byte 2: R2 L2 D2 U2 D X Y Z + // byte 3: R L D U St A B C + + return 0xff663f00; // 6 analog axes, X Y L2/D2(?) A B C Start U D L R + } + + virtual u32 process_kcode(u32 kcode) { + return kcode | AWAVE_TRIGGER_KEY; + } + + virtual u32 get_analog_axis(int index, const PlainJoystickState &pjs) { + switch (index) + { + case 2: + return pjs.joy[PJAI_X1]; + case 3: + return pjs.joy[PJAI_Y1]; + case 4: + return pjs.joy[PJAI_X2]; + case 5: + return pjs.joy[PJAI_Y2]; + default: + return 0x80; + } + } +}; + /* Sega Dreamcast Visual Memory Unit This is pretty much done (?) @@ -2592,7 +2622,11 @@ maple_device* maple_Create(MapleDeviceType type) switch(type) { case MDT_SegaController: - rv=new maple_sega_controller(); +#if DC_PLATFORM != DC_PLATFORM_ATOMISWAVE + rv = new maple_sega_controller(); +#else + rv = new maple_atomiswave_controller(); +#endif break; case MDT_Microphone: From b87a1cc31a3f7918e204ab3ac8666f54dc1f6dac Mon Sep 17 00:00:00 2001 From: Flyinghead Date: Mon, 8 Apr 2019 19:14:55 +0200 Subject: [PATCH 238/319] atomiswave: setup maple controls depending on game more lightgun fixes but still not working --- core/hw/maple/maple_cfg.cpp | 29 ++++++++++++++++++++--------- core/hw/maple/maple_devs.cpp | 31 +++++++++++++++++++------------ core/nullDC.cpp | 12 ++++++++---- 3 files changed, 47 insertions(+), 25 deletions(-) diff --git a/core/hw/maple/maple_cfg.cpp b/core/hw/maple/maple_cfg.cpp index fd6c18d18..4f8c9d2d6 100644 --- a/core/hw/maple/maple_cfg.cpp +++ b/core/hw/maple/maple_cfg.cpp @@ -149,16 +149,27 @@ void mcfg_CreateAtomisWaveControllers() // Game needs analog axes mcfg_Create(MDT_SegaController, 2, 5, 0); mcfg_Create(MDT_SegaController, 3, 5, 1); + // Faster Than Speed needs 1 std controller on port 0 (digital inputs) and one on port 2 (analog axes) + // Maximum Speed same + } + else if (settings.input.JammaSetup == 1) + { + // 4 players + mcfg_Create(MDT_SegaController, 2, 5); + mcfg_Create(MDT_SegaController, 3, 5); + } + else if (settings.input.JammaSetup == 5) + { + // Clay Challenge needs 2 std controllers on port 0 & 1 (digital in) and light guns on port 2 & 3 + // Sports Shooting same + mcfg_Create(MDT_LightGun, 2, 5, 0); + mcfg_Create(MDT_LightGun, 3, 5, 1); + } + else if (settings.input.JammaSetup == 3) + { + // Sega Bass Fishing Challenge needs a mouse (track-ball) on port 2 + mcfg_Create(MDT_Mouse, 2, 5, 0); } -// mcfg_Create(MDT_LightGun, 2, 5, 0); -// mcfg_Create(MDT_LightGun, 3, 5, 1); -// mcfg_Create(MDT_Mouse, 2, 5, 0); - // Guilty Gear Isuka (4P) needs 4 std controllers - // Faster Than Speed needs 1 std controller on port 0 (digital inputs) and one on port 2 (analog axes) - // Maximum Speed same - // Clay Challenge needs 2 std controllers on port 0 & 1 (digital in) and light guns on port 2 & 3 - // Sports Shooting same - // Sega Bass Fishing Challenge needs a mouse (track-ball) on port 3 } void mcfg_CreateDevices() diff --git a/core/hw/maple/maple_devs.cpp b/core/hw/maple/maple_devs.cpp index 8ed632777..bb65dde69 100755 --- a/core/hw/maple/maple_devs.cpp +++ b/core/hw/maple/maple_devs.cpp @@ -313,7 +313,7 @@ struct maple_sega_controller: maple_base struct maple_atomiswave_controller: maple_sega_controller { - virtual u32 get_capabilities() { + virtual u32 get_capabilities() override { // byte 0: 0 0 0 0 0 0 0 0 // byte 1: 0 0 a5 a4 a3 a2 a1 a0 // byte 2: R2 L2 D2 U2 D X Y Z @@ -322,11 +322,11 @@ struct maple_atomiswave_controller: maple_sega_controller return 0xff663f00; // 6 analog axes, X Y L2/D2(?) A B C Start U D L R } - virtual u32 process_kcode(u32 kcode) { + virtual u32 process_kcode(u32 kcode) override { return kcode | AWAVE_TRIGGER_KEY; } - virtual u32 get_analog_axis(int index, const PlainJoystickState &pjs) { + virtual u32 get_analog_axis(int index, const PlainJoystickState &pjs) override { switch (index) { case 2: @@ -1329,6 +1329,10 @@ struct maple_mouse : maple_base struct maple_lightgun : maple_base { + virtual u32 process_kcode(u32 kcode) { + return kcode | 0xFF01; + } + virtual MapleDeviceType get_device_type() { return MDT_LightGun; @@ -1377,21 +1381,13 @@ struct maple_lightgun : maple_base PlainJoystickState pjs; config->GetInput(&pjs); - // Also use the mouse buttons - if (!(mo_buttons & 4)) // Left button - pjs.kcode &= ~4; // A - if (!(mo_buttons & 2)) // Right button - pjs.kcode &= ~2; // B - if (!(mo_buttons & 8)) // Wheel button - pjs.kcode &= ~8; // Start - //caps //4 w32(MFID_0_Input); //state data //2 key code - w16(pjs.kcode | 0xFF01); + w16(process_kcode(pjs.kcode)); //not used //2 @@ -1417,6 +1413,13 @@ struct maple_lightgun : maple_base } }; +struct atomiswave_lightgun : maple_lightgun +{ + virtual u32 process_kcode(u32 kcode) override { + return (kcode & AWAVE_TRIGGER_KEY) == 0 ? ~AWAVE_BTN0_KEY : ~0; + } +}; + extern u16 kcode[4]; extern s8 joyx[4],joyy[4]; extern u8 rt[4], lt[4]; @@ -2650,7 +2653,11 @@ maple_device* maple_Create(MapleDeviceType type) break; case MDT_LightGun: +#if DC_PLATFORM != DC_PLATFORM_ATOMISWAVE rv = new maple_lightgun(); +#else + rv = new atomiswave_lightgun(); +#endif break; case MDT_NaomiJamma: diff --git a/core/nullDC.cpp b/core/nullDC.cpp index 87155fefc..dbbfe5325 100755 --- a/core/nullDC.cpp +++ b/core/nullDC.cpp @@ -213,12 +213,14 @@ void LoadSpecialSettings() printf("Enabling JVS rotary encoders for game %s\n", naomi_game_id); settings.input.JammaSetup = 2; } - else if (!strcmp("POWER STONE 2 JAPAN", naomi_game_id)) + else if (!strcmp("POWER STONE 2 JAPAN", naomi_game_id) // Naomi + || !strcmp("GUILTY GEAR isuka", naomi_game_id)) // AW { printf("Enabling 4-player setup for game %s\n", naomi_game_id); settings.input.JammaSetup = 1; } - else if (!strcmp("SEGA MARINE FISHING JAPAN", naomi_game_id)) + else if (!strcmp("SEGA MARINE FISHING JAPAN", naomi_game_id) + || !strcmp(naomi_game_id, "BASS FISHING SIMULATOR VER.A")) // AW { printf("Enabling specific JVS setup for game %s\n", naomi_game_id); settings.input.JammaSetup = 3; @@ -228,9 +230,11 @@ void LoadSpecialSettings() printf("Enabling specific JVS setup for game %s\n", naomi_game_id); settings.input.JammaSetup = 4; } - else if (!strcmp("NINJA ASSAULT", naomi_game_id)) + else if (!strcmp("NINJA ASSAULT", naomi_game_id) + || !strcmp(naomi_game_id, "Sports Shooting USA") // AW + || !strcmp(naomi_game_id, "SEGA CLAY CHALLENGE")) // AW { - printf("Enabling specific JVS setup for game %s\n", naomi_game_id); + printf("Enabling lightgun setup for game %s\n", naomi_game_id); settings.input.JammaSetup = 5; } else if (!strcmp(" BIOHAZARD GUN SURVIVOR2", naomi_game_id)) From 0a6f503efcb712d9d53b7f6530f1d8c67802d8be Mon Sep 17 00:00:00 2001 From: Flyinghead Date: Mon, 8 Apr 2019 21:51:59 +0200 Subject: [PATCH 239/319] Don't use screen stretching parameter for RTT --- core/rend/gl4/gles.cpp | 28 ++++++++++++++++++++-------- core/rend/gles/gles.cpp | 27 +++++++++++++++++++-------- 2 files changed, 39 insertions(+), 16 deletions(-) diff --git a/core/rend/gl4/gles.cpp b/core/rend/gl4/gles.cpp index 93700f20b..73330cc66 100644 --- a/core/rend/gl4/gles.cpp +++ b/core/rend/gl4/gles.cpp @@ -661,17 +661,29 @@ static bool RenderFrame() /* Handle Dc to screen scaling */ - float screen_scaling = is_rtt ? 1.f : settings.rend.ScreenScaling / 100.f; + float screen_scaling = settings.rend.ScreenScaling / 100.f; float screen_stretching = settings.rend.ScreenStretching / 100.f; - float dc2s_scale_h = is_rtt ? (screen_width / dc_width) : (screen_height / 480.0); - float ds2s_offs_x = is_rtt ? 0 : ((screen_width - dc2s_scale_h * 640.0 * screen_stretching) / 2); + float dc2s_scale_h; + float ds2s_offs_x; - //-1 -> too much to left - gl4ShaderUniforms.scale_coefs[0] = 2.0f / (screen_width / dc2s_scale_h * scale_x) * screen_stretching; - gl4ShaderUniforms.scale_coefs[1] = (is_rtt ? 2 : -2) / dc_height; // FIXME CT2 needs 480 here instead of dc_height=512 - gl4ShaderUniforms.scale_coefs[2] = 1 - 2 * ds2s_offs_x / screen_width; - gl4ShaderUniforms.scale_coefs[3] = (is_rtt ? 1 : -1); + if (is_rtt) + { + gl4ShaderUniforms.scale_coefs[0] = 2.0f / dc_width; + gl4ShaderUniforms.scale_coefs[1] = 2.0f / dc_height; // FIXME CT2 needs 480 here instead of dc_height=512 + gl4ShaderUniforms.scale_coefs[2] = 1; + gl4ShaderUniforms.scale_coefs[3] = 1; + } + else + { + dc2s_scale_h = screen_height / 480.0; + ds2s_offs_x = (screen_width - dc2s_scale_h * 640.0 * screen_stretching) / 2; + //-1 -> too much to left + gl4ShaderUniforms.scale_coefs[0] = 2.0f / (screen_width / dc2s_scale_h * scale_x) * screen_stretching; + gl4ShaderUniforms.scale_coefs[1] = -2.0f / dc_height; + gl4ShaderUniforms.scale_coefs[2] = 1 - 2 * ds2s_offs_x / screen_width; + gl4ShaderUniforms.scale_coefs[3] = -1; + } gl4ShaderUniforms.extra_depth_scale = settings.rend.ExtraDepthScale; diff --git a/core/rend/gles/gles.cpp b/core/rend/gles/gles.cpp index fad89106d..b16274d99 100644 --- a/core/rend/gles/gles.cpp +++ b/core/rend/gles/gles.cpp @@ -1681,15 +1681,26 @@ bool RenderFrame() float screen_stretching = settings.rend.ScreenStretching / 100.f; float screen_scaling = settings.rend.ScreenScaling / 100.f; - float dc2s_scale_h = is_rtt ? (screen_width / dc_width) : (screen_height / 480.0); - float ds2s_offs_x = is_rtt ? 0 : ((screen_width - dc2s_scale_h * 640.0 * screen_stretching) / 2); - - //-1 -> too much to left - ShaderUniforms.scale_coefs[0] = 2.0f / (screen_width / dc2s_scale_h * scale_x) * screen_stretching; - ShaderUniforms.scale_coefs[1]= (is_rtt ? 2 : -2) / dc_height; // FIXME CT2 needs 480 here instead of dc_height=512 - ShaderUniforms.scale_coefs[2]= 1 - 2 * ds2s_offs_x / screen_width; - ShaderUniforms.scale_coefs[3]= (is_rtt ? 1 : -1); + float dc2s_scale_h; + float ds2s_offs_x; + if (is_rtt) + { + ShaderUniforms.scale_coefs[0] = 2.0f / dc_width; + ShaderUniforms.scale_coefs[1] = 2.0f / dc_height; // FIXME CT2 needs 480 here instead of dc_height=512 + ShaderUniforms.scale_coefs[2] = 1; + ShaderUniforms.scale_coefs[3] = 1; + } + else + { + dc2s_scale_h = screen_height / 480.0; + ds2s_offs_x = (screen_width - dc2s_scale_h * 640.0 * screen_stretching) / 2; + //-1 -> too much to left + ShaderUniforms.scale_coefs[0] = 2.0f / (screen_width / dc2s_scale_h * scale_x) * screen_stretching; + ShaderUniforms.scale_coefs[1] = -2.0f / dc_height; + ShaderUniforms.scale_coefs[2] = 1 - 2 * ds2s_offs_x / screen_width; + ShaderUniforms.scale_coefs[3] = -1; + } ShaderUniforms.depth_coefs[0]=2/(vtx_max_fZ-vtx_min_fZ); ShaderUniforms.depth_coefs[1]=-vtx_min_fZ-1; From 1b04ef4cb11b286e7487683a8283512976579cb5 Mon Sep 17 00:00:00 2001 From: Flyinghead Date: Mon, 8 Apr 2019 22:09:22 +0200 Subject: [PATCH 240/319] Get rid of RTC in settings --- core/hw/aica/aica.cpp | 7 ++++++- core/hw/aica/aica_if.cpp | 20 +++++++++----------- core/hw/aica/aica_if.h | 3 ++- core/hw/sh4/interpr/sh4_interpreter.cpp | 2 +- core/nullDC.cpp | 3 --- core/types.h | 1 - 6 files changed, 18 insertions(+), 18 deletions(-) diff --git a/core/hw/aica/aica.cpp b/core/hw/aica/aica.cpp index 1eea5a03d..3cbf02bd9 100644 --- a/core/hw/aica/aica.cpp +++ b/core/hw/aica/aica.cpp @@ -1,4 +1,5 @@ #include "aica.h" +#include "aica_if.h" #include "sgc_if.h" #include "aica_mem.h" #include @@ -180,6 +181,7 @@ template void WriteAicaReg<2>(u32 reg,u32 data); s32 libAICA_Init() { init_mem(); + aica_Init(); verify(sizeof(*CommonData)==0x508); verify(sizeof(*DSPData)==0x15C8); @@ -203,9 +205,12 @@ s32 libAICA_Init() return rv_ok; } -void libAICA_Reset(bool m) +void libAICA_Reset(bool manual) { + if (!manual) + init_mem(); sgc_Init(); + aica_Reset(manual); } void libAICA_Term() diff --git a/core/hw/aica/aica_if.cpp b/core/hw/aica/aica_if.cpp index c717f8507..9ba65c0a7 100644 --- a/core/hw/aica/aica_if.cpp +++ b/core/hw/aica/aica_if.cpp @@ -18,6 +18,7 @@ u32 VREG;//video reg =P u32 ARMRST;//arm reset reg u32 rtc_EN=0; int dma_sched_id; +u32 RealTimeClock; u32 GetRTC_now() { @@ -39,9 +40,9 @@ u32 ReadMem_aica_rtc(u32 addr,u32 sz) switch( addr & 0xFF ) { case 0: - return settings.dreamcast.RTC>>16; + return RealTimeClock>>16; case 4: - return settings.dreamcast.RTC &0xFFFF; + return RealTimeClock &0xFFFF; case 8: return 0; } @@ -57,16 +58,16 @@ void WriteMem_aica_rtc(u32 addr,u32 data,u32 sz) case 0: if (rtc_EN) { - settings.dreamcast.RTC&=0xFFFF; - settings.dreamcast.RTC|=(data&0xFFFF)<<16; + RealTimeClock&=0xFFFF; + RealTimeClock|=(data&0xFFFF)<<16; rtc_EN=0; } return; case 4: if (rtc_EN) { - settings.dreamcast.RTC&=0xFFFF0000; - settings.dreamcast.RTC|= data&0xFFFF; + RealTimeClock&=0xFFFF0000; + RealTimeClock|= data&0xFFFF; //TODO: Clean the internal timer ? } return; @@ -153,15 +154,12 @@ void WriteMem_aica_reg(u32 addr,u32 data,u32 sz) //Init/res/term void aica_Init() { - //mmnnn ? gotta fill it w/ something + RealTimeClock = GetRTC_now(); } void aica_Reset(bool Manual) { - if (!Manual) - { - aica_ram.Zero(); - } + aica_Init(); } void aica_Term() diff --git a/core/hw/aica/aica_if.h b/core/hw/aica/aica_if.h index 8c6009a66..8c58b5326 100644 --- a/core/hw/aica/aica_if.h +++ b/core/hw/aica/aica_if.h @@ -3,6 +3,7 @@ extern u32 VREG; extern VArray2 aica_ram; +extern u32 RealTimeClock; u32 ReadMem_aica_rtc(u32 addr,u32 sz); void WriteMem_aica_rtc(u32 addr,u32 data,u32 sz); u32 ReadMem_aica_reg(u32 addr,u32 sz); @@ -17,4 +18,4 @@ void aica_Term(); void aica_sb_Init(); void aica_sb_Reset(bool Manual); -void aica_sb_Term(); \ No newline at end of file +void aica_sb_Term(); diff --git a/core/hw/sh4/interpr/sh4_interpreter.cpp b/core/hw/sh4/interpr/sh4_interpreter.cpp index ddc7d509c..5f48886a2 100644 --- a/core/hw/sh4/interpr/sh4_interpreter.cpp +++ b/core/hw/sh4/interpr/sh4_interpreter.cpp @@ -232,7 +232,7 @@ int AicaUpdate(int tag, int c, int j) int DreamcastSecond(int tag, int c, int j) { - settings.dreamcast.RTC++; + RealTimeClock++; #if 1 //HOST_OS==OS_WINDOWS prof_periodical(); diff --git a/core/nullDC.cpp b/core/nullDC.cpp index dbbfe5325..ffc4d524f 100755 --- a/core/nullDC.cpp +++ b/core/nullDC.cpp @@ -313,7 +313,6 @@ int dc_start_game(const char *path) { InitSettings(); LoadSettings(false); - settings.dreamcast.RTC = GetRTC_now(); // FIXME This shouldn't be in settings anymore #if DC_PLATFORM == DC_PLATFORM_DREAMCAST if (!settings.bios.UseReios) #endif @@ -351,7 +350,6 @@ int dc_start_game(const char *path) return 0; } - settings.dreamcast.RTC = GetRTC_now(); // FIXME This shouldn't be in settings anymore if (settings.bios.UseReios || !LoadRomFiles(get_readonly_data_path(DATA_PATH))) { #ifdef USE_REIOS @@ -491,7 +489,6 @@ void dc_exit() void InitSettings() { - settings.dreamcast.RTC = GetRTC_now(); settings.dynarec.Enable = true; settings.dynarec.idleskip = true; settings.dynarec.unstable_opt = false; diff --git a/core/types.h b/core/types.h index 2479fef25..c04f79442 100644 --- a/core/types.h +++ b/core/types.h @@ -656,7 +656,6 @@ struct settings_t struct { u32 cable; // 0 -> VGA, 1 -> VGA, 2 -> RGB, 3 -> TV - u32 RTC; u32 region; // 0 -> JP, 1 -> USA, 2 -> EU, 3 -> default u32 broadcast; // 0 -> NTSC, 1 -> PAL, 2 -> PAL/M, 3 -> PAL/N, 4 -> default u32 language; // 0 -> JP, 1 -> EN, 2 -> DE, 3 -> FR, 4 -> SP, 5 -> IT, 6 -> default From 37a533740bfbc056e03486bbf59f7ce01fffaf52 Mon Sep 17 00:00:00 2001 From: Flyinghead Date: Tue, 9 Apr 2019 10:37:49 +0200 Subject: [PATCH 241/319] win32: close thread handle when finished --- core/windows/winmain.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/core/windows/winmain.cpp b/core/windows/winmain.cpp index 68db4682c..b2a90b8ed 100644 --- a/core/windows/winmain.cpp +++ b/core/windows/winmain.cpp @@ -749,6 +749,7 @@ cThread::cThread(ThreadEntryFP* function,void* prm) void cThread::Start() { + verify(hThread == NULL); hThread=CreateThread(NULL,0,(LPTHREAD_START_ROUTINE)Entry,param,0,NULL); ResumeThread(hThread); } @@ -756,6 +757,8 @@ void cThread::Start() void cThread::WaitToEnd() { WaitForSingleObject(hThread,INFINITE); + CloseHandle(hThread); + hThread = NULL; } //End thread class From 1fa052987bebbfe58ea3b7ec28f6b8581fa340fb Mon Sep 17 00:00:00 2001 From: Flyinghead Date: Tue, 9 Apr 2019 10:39:29 +0200 Subject: [PATCH 242/319] android: run renderer in a separate native thread fixes simultaneous gamepad button presses not being registered on some platforms --- .../java/com/reicast/emulator/emu/JNIdc.java | 3 +- .../reicast/emulator/emu/NativeGLView.java | 24 +------ .../reicast/src/main/jni/src/Android.cpp | 71 ++++++++++++------- 3 files changed, 49 insertions(+), 49 deletions(-) diff --git a/shell/android-studio/reicast/src/main/java/com/reicast/emulator/emu/JNIdc.java b/shell/android-studio/reicast/src/main/java/com/reicast/emulator/emu/JNIdc.java index 6262fb27a..fef8b9dfa 100644 --- a/shell/android-studio/reicast/src/main/java/com/reicast/emulator/emu/JNIdc.java +++ b/shell/android-studio/reicast/src/main/java/com/reicast/emulator/emu/JNIdc.java @@ -21,8 +21,7 @@ public final class JNIdc public static native int send(int cmd, int opt); public static native int data(int cmd, byte[] data); - public static native void rendinitNative(Surface surface, int w, int h); - public static native boolean rendframeNative(); + public static native void rendinitNative(Surface surface); public static native void rendinitJava(int w, int h); public static native boolean rendframeJava(); public static native void rendtermJava(); diff --git a/shell/android-studio/reicast/src/main/java/com/reicast/emulator/emu/NativeGLView.java b/shell/android-studio/reicast/src/main/java/com/reicast/emulator/emu/NativeGLView.java index 3a7268401..383a8684a 100644 --- a/shell/android-studio/reicast/src/main/java/com/reicast/emulator/emu/NativeGLView.java +++ b/shell/android-studio/reicast/src/main/java/com/reicast/emulator/emu/NativeGLView.java @@ -19,8 +19,6 @@ import com.reicast.emulator.NativeGLActivity; import com.reicast.emulator.config.Config; public class NativeGLView extends SurfaceView implements SurfaceHolder.Callback { - private Handler handler = new Handler(); - private boolean surfaceReady = false; private boolean paused = false; VirtualJoystickDelegate vjoyDelegate; @@ -66,23 +64,6 @@ public class NativeGLView extends SurfaceView implements SurfaceHolder.Callback if (NativeGLActivity.syms != null) JNIdc.data(1, NativeGLActivity.syms); - - startRendering(); - } - - private void startRendering() { - // Continuously render frames - handler.removeCallbacksAndMessages(null); - handler.postAtTime(new Runnable() { - @Override - public void run() { - if (!paused) - { - JNIdc.rendframeNative(); - handler.post(this); - } - } - }, SystemClock.uptimeMillis() + 500); } @Override @@ -111,7 +92,7 @@ public class NativeGLView extends SurfaceView implements SurfaceHolder.Callback public void surfaceChanged(SurfaceHolder surfaceHolder, int format, int w, int h) { //Log.i("reicast", "NativeGLView.surfaceChanged: " + w + "x" + h); surfaceReady = true; - JNIdc.rendinitNative(surfaceHolder.getSurface(), w, h); + JNIdc.rendinitNative(surfaceHolder.getSurface()); Emulator.getCurrentActivity().handleStateChange(false); } @@ -119,7 +100,7 @@ public class NativeGLView extends SurfaceView implements SurfaceHolder.Callback public void surfaceDestroyed(SurfaceHolder surfaceHolder) { //Log.i("reicast", "NativeGLView.surfaceDestroyed"); surfaceReady = false; - JNIdc.rendinitNative(null, 0, 0); + JNIdc.rendinitNative(null); Emulator.getCurrentActivity().handleStateChange(true); } @@ -142,7 +123,6 @@ public class NativeGLView extends SurfaceView implements SurfaceHolder.Callback requestFocus(); JNIdc.resume(); } - startRendering(); } @TargetApi(19) diff --git a/shell/android-studio/reicast/src/main/jni/src/Android.cpp b/shell/android-studio/reicast/src/main/jni/src/Android.cpp index 3ab96e5dd..ced2f366d 100644 --- a/shell/android-studio/reicast/src/main/jni/src/Android.cpp +++ b/shell/android-studio/reicast/src/main/jni/src/Android.cpp @@ -91,8 +91,7 @@ JNIEXPORT void JNICALL Java_com_reicast_emulator_emu_JNIdc_destroy(JNIEnv *env,j JNIEXPORT jint JNICALL Java_com_reicast_emulator_emu_JNIdc_send(JNIEnv *env,jobject obj,jint id, jint v) __attribute__((visibility("default"))); JNIEXPORT jint JNICALL Java_com_reicast_emulator_emu_JNIdc_data(JNIEnv *env,jobject obj,jint id, jbyteArray d) __attribute__((visibility("default"))); -JNIEXPORT void JNICALL Java_com_reicast_emulator_emu_JNIdc_rendinitNative(JNIEnv *env, jobject obj, jobject surface, jint w, jint h) __attribute__((visibility("default"))); -JNIEXPORT jboolean JNICALL Java_com_reicast_emulator_emu_JNIdc_rendframeNative(JNIEnv *env,jobject obj) __attribute__((visibility("default"))); +JNIEXPORT void JNICALL Java_com_reicast_emulator_emu_JNIdc_rendinitNative(JNIEnv *env, jobject obj, jobject surface) __attribute__((visibility("default"))); JNIEXPORT void JNICALL Java_com_reicast_emulator_emu_JNIdc_rendinitJava(JNIEnv *env, jobject obj, jint w, jint h) __attribute__((visibility("default"))); JNIEXPORT jboolean JNICALL Java_com_reicast_emulator_emu_JNIdc_rendframeJava(JNIEnv *env, jobject obj) __attribute__((visibility("default"))); JNIEXPORT void JNICALL Java_com_reicast_emulator_emu_JNIdc_rendtermJava(JNIEnv *env, jobject obj) __attribute__((visibility("default"))); @@ -404,35 +403,57 @@ JNIEXPORT jint JNICALL Java_com_reicast_emulator_emu_JNIdc_data(JNIEnv *env, job extern void gl_swap(); extern void egl_stealcntx(); +volatile static bool render_running; +volatile static bool render_reinit; -JNIEXPORT jboolean JNICALL Java_com_reicast_emulator_emu_JNIdc_rendframeNative(JNIEnv *env,jobject obj) +void *render_thread_func(void *) { - if (g_window == NULL) - return false; - if (!egl_makecurrent()) - return false; - jboolean ret = (jboolean)rend_single_frame(); - if (ret) - gl_swap(); - return ret; + render_running = true; + + rend_init_renderer(); + + while (render_running) { + if (render_reinit) + { + render_reinit = false; + rend_init_renderer(); + } + else + if (!egl_makecurrent()) + break;; + + bool ret = rend_single_frame(); + if (ret) + gl_swap(); + } + egl_makecurrent(); + rend_term_renderer(); + ANativeWindow_release(g_window); + g_window = NULL; + render_running = false; + + return NULL; } -JNIEXPORT void JNICALL Java_com_reicast_emulator_emu_JNIdc_rendinitNative(JNIEnv * env, jobject obj, jobject surface, jint width, jint height) +static cThread render_thread(render_thread_func, NULL); + +JNIEXPORT void JNICALL Java_com_reicast_emulator_emu_JNIdc_rendinitNative(JNIEnv * env, jobject obj, jobject surface) { - if (g_window != NULL) - { - egl_makecurrent(); - rend_term_renderer(); - ANativeWindow_release(g_window); - g_window = NULL; - } - if (surface != NULL) - { + if (render_thread.hThread != NULL) + { + if (surface == NULL) + { + render_running = false; + render_thread.WaitToEnd(); + } + else + render_reinit = true; + } + else if (surface != NULL) + { g_window = ANativeWindow_fromSurface(env, surface); - rend_init_renderer(); - screen_width = width; - screen_height = height; - } + render_thread.Start(); + } } JNIEXPORT void JNICALL Java_com_reicast_emulator_emu_JNIdc_rendinitJava(JNIEnv * env, jobject obj, jint width, jint height) From 251b4d381d8da7a57b559f4b198ef4a653715974 Mon Sep 17 00:00:00 2001 From: Flyinghead Date: Tue, 9 Apr 2019 13:09:45 +0200 Subject: [PATCH 243/319] android: add activity alias for intents backward-compatibility --- .../reicast/src/dreamcast/AndroidManifest.xml | 51 +++++++++++++++ .../reicast/src/main/AndroidManifest.xml | 10 +++ .../reicast/src/naomi/AndroidManifest.xml | 63 ++++++++++++++++++- 3 files changed, 123 insertions(+), 1 deletion(-) diff --git a/shell/android-studio/reicast/src/dreamcast/AndroidManifest.xml b/shell/android-studio/reicast/src/dreamcast/AndroidManifest.xml index 165b7b014..112f56962 100644 --- a/shell/android-studio/reicast/src/dreamcast/AndroidManifest.xml +++ b/shell/android-studio/reicast/src/dreamcast/AndroidManifest.xml @@ -52,5 +52,56 @@ android:scheme="file" /> + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/shell/android-studio/reicast/src/main/AndroidManifest.xml b/shell/android-studio/reicast/src/main/AndroidManifest.xml index de600f7bc..ac89e2bb2 100644 --- a/shell/android-studio/reicast/src/main/AndroidManifest.xml +++ b/shell/android-studio/reicast/src/main/AndroidManifest.xml @@ -61,6 +61,16 @@ + + + + + + + + + android:name="com.reicast.emulator.NativeGLActivity"> @@ -62,5 +62,66 @@ android:scheme="file" /> + + + + + + + + + + + + + + + + + + + From 0445542ec240e806c753eb20a3710d2f41d9c284 Mon Sep 17 00:00:00 2001 From: Flyinghead Date: Tue, 9 Apr 2019 15:18:48 +0200 Subject: [PATCH 244/319] gl: 90deg screen rotation option. Fix scissor/clip wrt scale/stretch 90deg CC screen rotation option for some arcade games Fix scissoring and clipping when screen scaling/stretching/rotating Clear shader cache when screen rotation changes Properly delete all gl programs and shaders when needed --- core/nullDC.cpp | 3 + core/rend/gl4/abuffer.cpp | 19 ++++- core/rend/gl4/gl4.h | 4 +- core/rend/gl4/gldraw.cpp | 7 +- core/rend/gl4/gles.cpp | 87 ++++++++++++++++------ core/rend/gles/glcache.h | 13 ++++ core/rend/gles/gldraw.cpp | 31 ++++++-- core/rend/gles/gles.cpp | 103 ++++++++++++++++++-------- core/rend/gles/gles.h | 1 + core/rend/gles/imgui_impl_opengl3.cpp | 8 +- core/rend/gui.cpp | 10 ++- core/types.h | 1 + 12 files changed, 211 insertions(+), 76 deletions(-) diff --git a/core/nullDC.cpp b/core/nullDC.cpp index ffc4d524f..3baf42fa8 100755 --- a/core/nullDC.cpp +++ b/core/nullDC.cpp @@ -519,6 +519,7 @@ void InitSettings() settings.rend.ScreenStretching = 100; settings.rend.Fog = true; settings.rend.FloatVMUs = false; + settings.rend.Rotate90 = false; settings.pvr.ta_skip = 0; settings.pvr.rend = 0; @@ -604,6 +605,7 @@ void LoadSettings(bool game_specific) settings.rend.ScreenStretching = cfgLoadInt(config_section, "rend.ScreenStretching", settings.rend.ScreenStretching); settings.rend.Fog = cfgLoadBool(config_section, "rend.Fog", settings.rend.Fog); settings.rend.FloatVMUs = cfgLoadBool(config_section, "rend.FloatVMUs", settings.rend.FloatVMUs); + settings.rend.Rotate90 = cfgLoadBool(config_section, "rend.Rotate90", settings.rend.Rotate90); settings.pvr.ta_skip = cfgLoadInt(config_section, "ta.skip", settings.pvr.ta_skip); settings.pvr.rend = cfgLoadInt(config_section, "pvr.rend", settings.pvr.rend); @@ -730,6 +732,7 @@ void SaveSettings() cfgSaveInt("config", "rend.ScreenStretching", settings.rend.ScreenStretching); cfgSaveBool("config", "rend.Fog", settings.rend.Fog); cfgSaveBool("config", "rend.FloatVMUs", settings.rend.FloatVMUs); + cfgSaveBool("config", "rend.Rotate90", settings.rend.Rotate90); cfgSaveInt("config", "ta.skip", settings.pvr.ta_skip); cfgSaveInt("config", "pvr.rend", settings.pvr.rend); diff --git a/core/rend/gl4/abuffer.cpp b/core/rend/gl4/abuffer.cpp index b09cc962d..7e9b67c64 100644 --- a/core/rend/gl4/abuffer.cpp +++ b/core/rend/gl4/abuffer.cpp @@ -331,23 +331,23 @@ void initABuffer() { char source[16384]; sprintf(source, final_shader_source, 1); - gl4CompilePipelineShader(&g_abuffer_final_shader, source); + gl4CompilePipelineShader(&g_abuffer_final_shader, false, source); } if (g_abuffer_final_nosort_shader.program == 0) { char source[16384]; sprintf(source, final_shader_source, 0); - gl4CompilePipelineShader(&g_abuffer_final_nosort_shader, source); + gl4CompilePipelineShader(&g_abuffer_final_nosort_shader, false, source); } if (g_abuffer_clear_shader.program == 0) - gl4CompilePipelineShader(&g_abuffer_clear_shader, clear_shader_source); + gl4CompilePipelineShader(&g_abuffer_clear_shader, false, clear_shader_source); if (g_abuffer_tr_modvol_shaders[0].program == 0) { char source[16384]; for (int mode = 0; mode < ModeCount; mode++) { sprintf(source, tr_modvol_shader_source, mode); - gl4CompilePipelineShader(&g_abuffer_tr_modvol_shaders[mode], source); + gl4CompilePipelineShader(&g_abuffer_tr_modvol_shaders[mode], false, source); } } @@ -417,6 +417,17 @@ void termABuffer() glDeleteBuffers(1, &g_quadBuffer); g_quadBuffer = 0; } + glcache.DeleteProgram(g_abuffer_final_shader.program); + g_abuffer_final_shader.program = 0; + glcache.DeleteProgram(g_abuffer_final_nosort_shader.program); + g_abuffer_final_nosort_shader.program = 0; + glcache.DeleteProgram(g_abuffer_clear_shader.program); + g_abuffer_clear_shader.program = 0; + for (int mode = 0; mode < ModeCount; mode++) + { + glcache.DeleteProgram(g_abuffer_tr_modvol_shaders[mode].program); + g_abuffer_tr_modvol_shaders[mode].program = 0; + } } void reshapeABuffer(int w, int h) diff --git a/core/rend/gl4/gl4.h b/core/rend/gl4/gl4.h index 5ca4dd141..4c3ca82ac 100755 --- a/core/rend/gl4/gl4.h +++ b/core/rend/gl4/gl4.h @@ -45,6 +45,7 @@ struct gl4_ctx } modvol_shader; std::unordered_map shaders; + bool rotate90; struct { @@ -66,7 +67,8 @@ bool gl4_render_output_framebuffer(); void abufferDrawQuad(bool upsideDown = false, float x = 0.f, float y = 0.f, float w = 0.f, float h = 0.f); extern const char *gl4PixelPipelineShader; -bool gl4CompilePipelineShader(gl4PipelineShader* s, const char *source = gl4PixelPipelineShader); +bool gl4CompilePipelineShader(gl4PipelineShader* s, bool rotate_90, const char *source = gl4PixelPipelineShader); +void gl4_delete_shaders(); extern GLuint stencilTexId; extern GLuint depthTexId; diff --git a/core/rend/gl4/gldraw.cpp b/core/rend/gl4/gldraw.cpp index 75ad7eda9..dfa403a19 100644 --- a/core/rend/gl4/gldraw.cpp +++ b/core/rend/gl4/gldraw.cpp @@ -49,6 +49,11 @@ static gl4PipelineShader *gl4GetProgram(u32 cp_AlphaTest, u32 pp_ClipTestMode, u32 pp_Texture, u32 pp_UseAlpha, u32 pp_IgnoreTexA, u32 pp_ShadInstr, u32 pp_Offset, u32 pp_FogCtrl, bool pp_TwoVolumes, u32 pp_DepthFunc, bool pp_Gouraud, bool pp_BumpMap, bool fog_clamping, int pass) { + if (settings.rend.Rotate90 != gl4.rotate90) + { + gl4_delete_shaders(); + gl4.rotate90 = settings.rend.Rotate90; + } u32 rv=0; rv|=pp_ClipTestMode; @@ -83,7 +88,7 @@ static gl4PipelineShader *gl4GetProgram(u32 cp_AlphaTest, u32 pp_ClipTestMode, shader->pp_BumpMap = pp_BumpMap; shader->fog_clamping = fog_clamping; shader->pass = pass; - gl4CompilePipelineShader(shader); + gl4CompilePipelineShader(shader, settings.rend.Rotate90); } return shader; diff --git a/core/rend/gl4/gles.cpp b/core/rend/gl4/gles.cpp index 73330cc66..aa2da9c99 100644 --- a/core/rend/gl4/gles.cpp +++ b/core/rend/gl4/gles.cpp @@ -14,6 +14,7 @@ static const char* VertexShaderSource = "\ #version 140 \n\ #define pp_Gouraud %d \n\ +#define ROTATE_90 %d \n\ \n\ #if pp_Gouraud == 0 \n\ #define INTERPOLATION flat \n\ @@ -56,6 +57,9 @@ void main() \n\ \n\ vpos.w = extra_depth_scale / vpos.z; \n\ vpos.z = vpos.w; \n\ +#if ROTATE_90 == 1 \n\ + vpos.xy = vec2(vpos.y, -vpos.x); \n\ +#endif \n\ vpos.xy=vpos.xy*scale.xy-scale.zw; \n\ vpos.xy*=vpos.w; \n\ gl_Position = vpos; \n\ @@ -393,11 +397,11 @@ gl4_ctx gl4; struct gl4ShaderUniforms_t gl4ShaderUniforms; -bool gl4CompilePipelineShader( gl4PipelineShader* s, const char *source /* = PixelPipelineShader */) +bool gl4CompilePipelineShader( gl4PipelineShader* s, bool rotate_90, const char *source /* = PixelPipelineShader */) { char vshader[16384]; - sprintf(vshader, VertexShaderSource, s->pp_Gouraud); + sprintf(vshader, VertexShaderSource, s->pp_Gouraud, rotate_90); char pshader[16384]; @@ -478,27 +482,45 @@ bool gl4CompilePipelineShader( gl4PipelineShader* s, const char *source /* = Pix void gl_term(); +void gl4_delete_shaders() +{ + for (auto it : gl4.shaders) + { + if (it.second.program != 0) + glcache.DeleteProgram(it.second.program); + } + gl4.shaders.clear(); + glcache.DeleteProgram(gl4.modvol_shader.program); + gl4.modvol_shader.program = 0; +} + static void gles_term(void) { - glDeleteProgram(gl4.modvol_shader.program); glDeleteBuffers(1, &gl4.vbo.geometry); gl4.vbo.geometry = 0; glDeleteBuffers(1, &gl4.vbo.modvols); glDeleteBuffers(1, &gl4.vbo.idxs); glDeleteBuffers(1, &gl4.vbo.idxs2); glDeleteBuffers(1, &gl4.vbo.tr_poly_params); - for (auto it = gl4.shaders.begin(); it != gl4.shaders.end(); it++) - { - if (it->second.program != 0) - glDeleteProgram(it->second.program); - } - gl4.shaders.clear(); + gl4_delete_shaders(); glDeleteVertexArrays(1, &gl4.vbo.main_vao); glDeleteVertexArrays(1, &gl4.vbo.modvol_vao); gl_term(); } +static void create_modvol_shader() +{ + if (gl4.modvol_shader.program != 0) + return; + char vshader[16384]; + sprintf(vshader, VertexShaderSource, 1, settings.rend.Rotate90); + + gl4.modvol_shader.program=gl_CompileAndLink(vshader, ModifierVolumeShader); + gl4.modvol_shader.scale = glGetUniformLocation(gl4.modvol_shader.program, "scale"); + gl4.modvol_shader.extra_depth_scale = glGetUniformLocation(gl4.modvol_shader.program, "extra_depth_scale"); +} + static bool gl_create_resources() { if (gl4.vbo.geometry != 0) @@ -520,12 +542,7 @@ static bool gl_create_resources() gl4SetupMainVBO(); gl4SetupModvolVBO(); - char vshader[16384]; - sprintf(vshader, VertexShaderSource, 1); - - gl4.modvol_shader.program=gl_CompileAndLink(vshader, ModifierVolumeShader); - gl4.modvol_shader.scale = glGetUniformLocation(gl4.modvol_shader.program, "scale"); - gl4.modvol_shader.extra_depth_scale = glGetUniformLocation(gl4.modvol_shader.program, "extra_depth_scale"); + create_modvol_shader(); gl_load_osd_resources(); @@ -603,6 +620,7 @@ static bool RenderFrame() old_screen_scaling = settings.rend.ScreenScaling; } DoCleanup(); + create_modvol_shader(); bool is_rtt=pvrrc.isRTT; @@ -676,13 +694,25 @@ static bool RenderFrame() } else { - dc2s_scale_h = screen_height / 480.0; - ds2s_offs_x = (screen_width - dc2s_scale_h * 640.0 * screen_stretching) / 2; - //-1 -> too much to left - gl4ShaderUniforms.scale_coefs[0] = 2.0f / (screen_width / dc2s_scale_h * scale_x) * screen_stretching; - gl4ShaderUniforms.scale_coefs[1] = -2.0f / dc_height; - gl4ShaderUniforms.scale_coefs[2] = 1 - 2 * ds2s_offs_x / screen_width; - gl4ShaderUniforms.scale_coefs[3] = -1; + if (settings.rend.Rotate90) + { + dc2s_scale_h = screen_height / 640.0; + ds2s_offs_x = (screen_width - dc2s_scale_h * 480.0 * screen_stretching) / 2; + gl4ShaderUniforms.scale_coefs[0] = 2.0f / (screen_width / dc2s_scale_h * scale_x) * screen_stretching; + gl4ShaderUniforms.scale_coefs[1] = -2.0f / dc_width; + gl4ShaderUniforms.scale_coefs[2] = 1 - 2 * ds2s_offs_x / screen_width; + gl4ShaderUniforms.scale_coefs[3] = 1; + } + else + { + dc2s_scale_h = screen_height / 480.0; + ds2s_offs_x = (screen_width - dc2s_scale_h * 640.0 * screen_stretching) / 2; + //-1 -> too much to left + gl4ShaderUniforms.scale_coefs[0] = 2.0f / (screen_width / dc2s_scale_h * scale_x) * screen_stretching; + gl4ShaderUniforms.scale_coefs[1] = -2.0f / dc_height; + gl4ShaderUniforms.scale_coefs[2] = 1 - 2 * ds2s_offs_x / screen_width; + gl4ShaderUniforms.scale_coefs[3] = -1; + } } gl4ShaderUniforms.extra_depth_scale = settings.rend.ExtraDepthScale; @@ -838,11 +868,20 @@ static bool RenderFrame() float min_y = pvrrc.fb_Y_CLIP.min / scale_y; if (!is_rtt) { + if (settings.rend.Rotate90) + { + float t = width; + width = height; + height = t; + t = min_x; + min_x = min_y; + min_y = 640 - t - height; + } // Add x offset for aspect ratio > 4/3 - min_x = min_x * dc2s_scale_h * screen_stretching + ds2s_offs_x * screen_scaling; + min_x = (min_x * dc2s_scale_h * screen_stretching + ds2s_offs_x) * screen_scaling; // Invert y coordinates when rendering to screen min_y = (screen_height - (min_y + height) * dc2s_scale_h) * screen_scaling; - width *= dc2s_scale_h * screen_scaling * screen_stretching; + width *= dc2s_scale_h * screen_stretching * screen_scaling; height *= dc2s_scale_h * screen_scaling; if (ds2s_offs_x > 0) diff --git a/core/rend/gles/glcache.h b/core/rend/gles/glcache.h index e245a368d..32c73c644 100644 --- a/core/rend/gles/glcache.h +++ b/core/rend/gles/glcache.h @@ -149,6 +149,19 @@ public: return _texture_ids[--_texture_cache_size]; } + void DeleteProgram(GLuint program) + { + GLsizei shader_count; + GLuint shaders[2]; + glGetAttachedShaders(program, ARRAY_SIZE(shaders), &shader_count, shaders); + for (int i = 0; i < shader_count; i++) + glDeleteShader(shaders[i]); + + glDeleteProgram(program); + if (_program == program) + _program = 0; + } + void Reset() { _texture = 0xFFFFFFFFu; _src_blend_factor = 0xFFFFFFFFu; diff --git a/core/rend/gles/gldraw.cpp b/core/rend/gles/gldraw.cpp index 618a3e33f..b4381170d 100644 --- a/core/rend/gles/gldraw.cpp +++ b/core/rend/gles/gldraw.cpp @@ -114,13 +114,30 @@ s32 SetTileClip(u32 val, GLint uniform) csy /= scale_y; cex /= scale_x; cey /= scale_y; - float t = cey; - cey = 480 - csy; - csy = 480 - t; - float dc2s_scale_h = screen_height / 480.0f; - float ds2s_offs_x = (screen_width - dc2s_scale_h * 640) / 2; - csx = csx * dc2s_scale_h + ds2s_offs_x; - cex = cex * dc2s_scale_h + ds2s_offs_x; + float dc2s_scale_h; + float ds2s_offs_x; + float screen_stretching = settings.rend.ScreenStretching / 100.f; + + if (settings.rend.Rotate90) + { + float t = cex; + cex = cey; + cey = 640 - csx; + csx = csy; + csy = 640 - t; + dc2s_scale_h = screen_height / 640.0f; + ds2s_offs_x = (screen_width - dc2s_scale_h * 480.0 * screen_stretching) / 2; + } + else + { + float t = cey; + cey = 480 - csy; + csy = 480 - t; + dc2s_scale_h = screen_height / 480.0f; + ds2s_offs_x = (screen_width - dc2s_scale_h * 640.0 * screen_stretching) / 2; + } + csx = csx * dc2s_scale_h * screen_stretching + ds2s_offs_x; + cex = cex * dc2s_scale_h * screen_stretching + ds2s_offs_x; csy = csy * dc2s_scale_h; cey = cey * dc2s_scale_h; } diff --git a/core/rend/gles/gles.cpp b/core/rend/gles/gles.cpp index b16274d99..a2155f460 100644 --- a/core/rend/gles/gles.cpp +++ b/core/rend/gles/gles.cpp @@ -79,6 +79,7 @@ const char* VertexShaderSource = %s \n\ #define TARGET_GL %s \n\ #define pp_Gouraud %d \n\ +#define ROTATE_90 %d \n\ \n\ #define GLES2 0 \n\ #define GLES3 1 \n\ @@ -136,6 +137,9 @@ void main() \n\ vpos.z = vpos.w; \n\ #else \n\ vpos.z=depth_scale.x+depth_scale.y*vpos.w; \n\ +#endif \n\ +#if ROTATE_90 == 1 \n\ + vpos.xy = vec2(vpos.y, -vpos.x); \n\ #endif \n\ vpos.xy=vpos.xy*scale.xy-scale.zw; \n\ vpos.xy*=vpos.w; \n\ @@ -850,9 +854,20 @@ GLuint fogTextureId; extern void gl_term(); #endif +static void gl_delete_shaders() +{ + for (auto it : gl.shaders) + { + if (it.second.program != 0) + glcache.DeleteProgram(it.second.program); + } + gl.shaders.clear(); + glcache.DeleteProgram(gl.modvol_shader.program); + gl.modvol_shader.program = 0; +} + static void gles_term() { - glDeleteProgram(gl.modvol_shader.program); glDeleteBuffers(1, &gl.vbo.geometry); gl.vbo.geometry = 0; glDeleteBuffers(1, &gl.vbo.modvols); @@ -865,7 +880,7 @@ static void gles_term() gl_free_osd_resources(); free_output_framebuffer(); - gl.shaders.clear(); + gl_delete_shaders(); gl_term(); } @@ -1018,6 +1033,11 @@ PipelineShader *GetProgram(u32 cp_AlphaTest, u32 pp_ClipTestMode, u32 pp_Texture, u32 pp_UseAlpha, u32 pp_IgnoreTexA, u32 pp_ShadInstr, u32 pp_Offset, u32 pp_FogCtrl, bool pp_Gouraud, bool pp_BumpMap, bool fog_clamping, bool trilinear) { + if (settings.rend.Rotate90 != gl.rotate90) + { + gl_delete_shaders(); + gl.rotate90 = settings.rend.Rotate90; + } u32 rv=0; rv|=pp_ClipTestMode; @@ -1058,7 +1078,7 @@ bool CompilePipelineShader( PipelineShader* s) { char vshader[8192]; - sprintf(vshader, VertexShaderSource, gl.glsl_version_header, gl.gl_version, s->pp_Gouraud); + sprintf(vshader, VertexShaderSource, gl.glsl_version_header, gl.gl_version, s->pp_Gouraud, settings.rend.Rotate90); char pshader[8192]; @@ -1144,13 +1164,30 @@ void gl_load_osd_resources() void gl_free_osd_resources() { - glDeleteProgram(gl.OSD_SHADER.program); + glcache.DeleteProgram(gl.OSD_SHADER.program); if (osd_tex != 0) { glcache.DeleteTextures(1, &osd_tex); osd_tex = 0; } } + +static void create_modvol_shader() +{ + if (gl.modvol_shader.program != 0) + return; + char vshader[8192]; + sprintf(vshader, VertexShaderSource, gl.glsl_version_header, gl.gl_version, 1, settings.rend.Rotate90); + char fshader[8192]; + sprintf(fshader, ModifierVolumeShader, gl.glsl_version_header, gl.gl_version); + + gl.modvol_shader.program=gl_CompileAndLink(vshader, fshader); + gl.modvol_shader.scale = glGetUniformLocation(gl.modvol_shader.program, "scale"); + gl.modvol_shader.sp_ShaderColor = glGetUniformLocation(gl.modvol_shader.program, "sp_ShaderColor"); + gl.modvol_shader.depth_scale = glGetUniformLocation(gl.modvol_shader.program, "depth_scale"); + gl.modvol_shader.extra_depth_scale = glGetUniformLocation(gl.modvol_shader.program, "extra_depth_scale"); +} + bool gl_create_resources() { if (gl.vbo.geometry != 0) @@ -1174,25 +1211,7 @@ bool gl_create_resources() glGenBuffers(1, &gl.vbo.idxs); glGenBuffers(1, &gl.vbo.idxs2); - char vshader[8192]; - sprintf(vshader, VertexShaderSource, gl.glsl_version_header, gl.gl_version, 1); - char fshader[8192]; - sprintf(fshader, ModifierVolumeShader, gl.glsl_version_header, gl.gl_version); - - gl.modvol_shader.program=gl_CompileAndLink(vshader, fshader); - gl.modvol_shader.scale = glGetUniformLocation(gl.modvol_shader.program, "scale"); - gl.modvol_shader.sp_ShaderColor = glGetUniformLocation(gl.modvol_shader.program, "sp_ShaderColor"); - gl.modvol_shader.depth_scale = glGetUniformLocation(gl.modvol_shader.program, "depth_scale"); - gl.modvol_shader.extra_depth_scale = glGetUniformLocation(gl.modvol_shader.program, "extra_depth_scale"); - - //#define PRECOMPILE_SHADERS - #ifdef PRECOMPILE_SHADERS - for (u32 i=0;i too much to left - ShaderUniforms.scale_coefs[0] = 2.0f / (screen_width / dc2s_scale_h * scale_x) * screen_stretching; - ShaderUniforms.scale_coefs[1] = -2.0f / dc_height; - ShaderUniforms.scale_coefs[2] = 1 - 2 * ds2s_offs_x / screen_width; - ShaderUniforms.scale_coefs[3] = -1; } ShaderUniforms.depth_coefs[0]=2/(vtx_max_fZ-vtx_min_fZ); @@ -1868,11 +1900,20 @@ bool RenderFrame() float min_y = pvrrc.fb_Y_CLIP.min / scale_y; if (!is_rtt) { + if (settings.rend.Rotate90) + { + float t = width; + width = height; + height = t; + t = min_x; + min_x = min_y; + min_y = 640 - t - height; + } // Add x offset for aspect ratio > 4/3 - min_x = min_x * dc2s_scale_h * screen_stretching + ds2s_offs_x * screen_scaling; + min_x = (min_x * dc2s_scale_h * screen_stretching + ds2s_offs_x) * screen_scaling; // Invert y coordinates when rendering to screen min_y = (screen_height - (min_y + height) * dc2s_scale_h) * screen_scaling; - width *= dc2s_scale_h * screen_scaling * screen_stretching; + width *= dc2s_scale_h * screen_stretching * screen_scaling; height *= dc2s_scale_h * screen_scaling; if (ds2s_offs_x > 0) diff --git a/core/rend/gles/gles.h b/core/rend/gles/gles.h index 7e0cec3cb..85f1b90e8 100755 --- a/core/rend/gles/gles.h +++ b/core/rend/gles/gles.h @@ -96,6 +96,7 @@ struct gl_ctx } modvol_shader; std::unordered_map shaders; + bool rotate90; struct { diff --git a/core/rend/gles/imgui_impl_opengl3.cpp b/core/rend/gles/imgui_impl_opengl3.cpp index ddb363c1c..0380a8901 100644 --- a/core/rend/gles/imgui_impl_opengl3.cpp +++ b/core/rend/gles/imgui_impl_opengl3.cpp @@ -490,15 +490,9 @@ void ImGui_ImplOpenGL3_DestroyDeviceObjects() if (g_ElementsHandle) glDeleteBuffers(1, &g_ElementsHandle); g_VboHandle = g_ElementsHandle = 0; - if (g_ShaderHandle && g_VertHandle) glDetachShader(g_ShaderHandle, g_VertHandle); - if (g_VertHandle) glDeleteShader(g_VertHandle); + glcache.DeleteProgram(g_ShaderHandle); g_VertHandle = 0; - - if (g_ShaderHandle && g_FragHandle) glDetachShader(g_ShaderHandle, g_FragHandle); - if (g_FragHandle) glDeleteShader(g_FragHandle); g_FragHandle = 0; - - if (g_ShaderHandle) glDeleteProgram(g_ShaderHandle); g_ShaderHandle = 0; ImGui_ImplOpenGL3_DestroyFontsTexture(); diff --git a/core/rend/gui.cpp b/core/rend/gui.cpp index 7a0fb107d..c02e81005 100644 --- a/core/rend/gui.cpp +++ b/core/rend/gui.cpp @@ -804,13 +804,14 @@ static void gui_display_settings() if (ImGui::BeginTabItem("Controls")) { ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, normal_padding); -#if DC_PLATFORM == DC_PLATFORM_DREAMCAST +#if DC_PLATFORM == DC_PLATFORM_DREAMCAST || DC_PLATFORM == DC_PLATFORM_ATOMISWAVE if (ImGui::CollapsingHeader("Dreamcast Devices", ImGuiTreeNodeFlags_DefaultOpen)) { for (int bus = 0; bus < MAPLE_PORTS; bus++) { ImGui::Text("Device %c", bus + 'A'); ImGui::SameLine(); +#if DC_PLATFORM == DC_PLATFORM_DREAMCAST char device_name[32]; sprintf(device_name, "##device%d", bus); float w = ImGui::CalcItemWidth() / 3; @@ -854,6 +855,10 @@ static void gui_display_settings() ImGui::PopID(); } ImGui::PopItemWidth(); +#elif DC_PLATFORM == DC_PLATFORM_ATOMISWAVE + if (MapleDevices[bus][5] != NULL) + ImGui::Text("%s", maple_device_name(MapleDevices[bus][5]->get_device_type())); +#endif } ImGui::Spacing(); } @@ -966,6 +971,9 @@ static void gui_display_settings() ImGui::Checkbox("Show VMU in game", &settings.rend.FloatVMUs); ImGui::SameLine(); ShowHelpMarker("Show the VMU LCD screens while in game"); + ImGui::Checkbox("Rotate screen 90°", &settings.rend.Rotate90); + ImGui::SameLine(); + ShowHelpMarker("Rotate the screen 90° counterclockwise"); ImGui::SliderInt("Scaling", (int *)&settings.rend.ScreenScaling, 1, 100); ImGui::SameLine(); ShowHelpMarker("Downscaling factor relative to native screen resolution. Higher is better"); diff --git a/core/types.h b/core/types.h index c04f79442..3d48ae574 100644 --- a/core/types.h +++ b/core/types.h @@ -636,6 +636,7 @@ struct settings_t int ScreenStretching; // in percent. 150 means stretch from 4/3 to 6/3 bool Fog; bool FloatVMUs; + bool Rotate90; // Rotate the screen 90 deg CC } rend; struct From 13341ecd597ddb83cc31508d56c068b03f88a7cf Mon Sep 17 00:00:00 2001 From: Flyinghead Date: Wed, 10 Apr 2019 11:31:08 +0200 Subject: [PATCH 245/319] maple: process_kcode -> transform_kcode --- core/hw/maple/maple_devs.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/core/hw/maple/maple_devs.cpp b/core/hw/maple/maple_devs.cpp index bb65dde69..90b4c972a 100755 --- a/core/hw/maple/maple_devs.cpp +++ b/core/hw/maple/maple_devs.cpp @@ -208,7 +208,7 @@ struct maple_sega_controller: maple_base return 0xfe060f00; // 4 analog axes (0-3) X Y A B Start U D L R } - virtual u32 process_kcode(u32 kcode) { + virtual u32 transform_kcode(u32 kcode) { return kcode; } @@ -280,7 +280,7 @@ struct maple_sega_controller: maple_base //state data //2 key code - w16(process_kcode(pjs.kcode)); + w16(transform_kcode(pjs.kcode)); //triggers //1 R @@ -322,7 +322,7 @@ struct maple_atomiswave_controller: maple_sega_controller return 0xff663f00; // 6 analog axes, X Y L2/D2(?) A B C Start U D L R } - virtual u32 process_kcode(u32 kcode) override { + virtual u32 transform_kcode(u32 kcode) override { return kcode | AWAVE_TRIGGER_KEY; } @@ -1329,7 +1329,7 @@ struct maple_mouse : maple_base struct maple_lightgun : maple_base { - virtual u32 process_kcode(u32 kcode) { + virtual u32 transform_kcode(u32 kcode) { return kcode | 0xFF01; } @@ -1387,7 +1387,7 @@ struct maple_lightgun : maple_base //state data //2 key code - w16(process_kcode(pjs.kcode)); + w16(transform_kcode(pjs.kcode)); //not used //2 @@ -1415,7 +1415,7 @@ struct maple_lightgun : maple_base struct atomiswave_lightgun : maple_lightgun { - virtual u32 process_kcode(u32 kcode) override { + virtual u32 transform_kcode(u32 kcode) override { return (kcode & AWAVE_TRIGGER_KEY) == 0 ? ~AWAVE_BTN0_KEY : ~0; } }; From cc9d5ec55b1d8c54856a6715362478cf5a2cbd6d Mon Sep 17 00:00:00 2001 From: david miller Date: Fri, 12 Apr 2019 16:59:39 -0400 Subject: [PATCH 246/319] CMake/Master working, tested on windows with Clang && MSC (x86,x64) --- CMakeLists.txt | 313 ++++++++++++ CMakeSettings.json | 216 ++++++++ core/build.h | 36 +- core/hw/modem/dns.cpp | 5 + core/hw/sh4/dyna/blockmanager.h | 5 +- core/hw/sh4/sh4_interpreter.h | 6 +- core/hw/sh4/sh4_sched.cpp | 36 +- core/rend/gl4/gltex.cpp | 2 +- core/rend/gui.cpp | 3 +- core/serialize.cpp | 187 ++++--- core/stdclass.cpp | 6 +- core/types.h | 8 +- core/version.h.in | 8 + core/windows/winmain.cpp | 2 +- shell/cmake/GetGitRevisionDescription.cmake | 168 ++++++ .../cmake/GetGitRevisionDescription.cmake.in | 41 ++ shell/cmake/android.cmake | 101 ++++ shell/cmake/config.cmake | 481 ++++++++++++++++++ shell/cmake/devkitA64.cmake | 41 ++ shell/cmake/ps4sdk.cmake | 130 +++++ shell/reicast.vcxproj | 2 +- 21 files changed, 1662 insertions(+), 135 deletions(-) create mode 100644 CMakeLists.txt create mode 100644 CMakeSettings.json create mode 100644 core/version.h.in create mode 100644 shell/cmake/GetGitRevisionDescription.cmake create mode 100644 shell/cmake/GetGitRevisionDescription.cmake.in create mode 100644 shell/cmake/android.cmake create mode 100644 shell/cmake/config.cmake create mode 100644 shell/cmake/devkitA64.cmake create mode 100644 shell/cmake/ps4sdk.cmake diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 000000000..49a9a1138 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,313 @@ +cmake_minimum_required(VERSION 3.5.0 FATAL_ERROR) + +set(TNAME reicast) + +project(${TNAME}) + +enable_language(ASM) + +set(DEBUG_CMAKE ON) + +set(CMAKE_VERBOSE_MAKEFILE ON) +set(CMAKE_INCLUDE_CURRENT_DIR ON) + + +## built-in cmake modules # +# +#include(CheckIncludeFiles) +#include(CheckFunctionExists) +#include(CheckCSourceCompiles) + +#set(CMAKE_BINARY_DIR ${CMAKE_CURRENT_BINARY_DIR}/bin) +#set(LIBRARY_OUTPUT_PATH ${CMAKE_CURRENT_BINARY_DIR}) +#set(EXECUTABLE_OUTPUT_PATH ${CMAKE_CURRENT_BINARY_DIR}) + + + +set(reicast_root_path "${CMAKE_CURRENT_SOURCE_DIR}") +set(reicast_core_path "${reicast_root_path}/core") +set(reicast_shell_path "${reicast_root_path}/shell") + +include_directories ("${reicast_core_path}") + +list(APPEND CMAKE_MODULE_PATH "${reicast_shell_path}/cmake") + + +include(GetGitRevisionDescription) +git_describe(GIT_VERSION --tags) +configure_file(${reicast_core_path}/version.h.in ${reicast_core_path}/version.h @ONLY) + + + + +## reicast build modules # +# + +set(reicast_SRCS "") + +include(config) # configure build settings, must be first + + + +### libdreamcast.cmake ######################################################################### + +set(d_core ${reicast_core_path}) + +file(GLOB_RECURSE hw_SRCS ${d_core}/hw/*.cpp ${d_core}/hw/*.h) + +file(GLOB cfg_SRCS ${d_core}/cfg/*.cpp ${d_core}/cfg/*.h) +file(GLOB rend_SRCS ${d_core}/rend/*.cpp ${d_core}/rend/*.h) +file(GLOB input_SRCS ${d_core}/input/*.cpp ${d_core}/input/*.h) +file(GLOB reios_SRCS ${d_core}/reios/*.cpp ${d_core}/reios/*.h) +file(GLOB imgread_SRCS ${d_core}/imgread/*.cpp ${d_core}/imgread/*.h) +file(GLOB profiler_SRCS ${d_core}/profiler/*.cpp ${d_core}/profiler/*.h) +file(GLOB archive_SRCS ${d_core}/archive/*.cpp ${d_core}/archive/*.h) + +#### option(rend) +file(GLOB gl4_SRCS ${d_core}/rend/gl4/*.cpp ${d_core}/rend/gl4/*.h) +file(GLOB gles_SRCS ${d_core}/rend/gles/*.cpp ${d_core}/rend/gles/*.h) + +set(core_SRCS + ${hw_SRCS} + ${cfg_SRCS} + ${rend_SRCS} + ${gl4_SRCS} + ${gles_SRCS} + ${input_SRCS} + ${reios_SRCS} + ${imgread_SRCS} + ${profiler_SRCS} +# ${archive_SRCS} + ${d_core}/archive/archive.cpp ${d_core}/archive/archive.h + ${d_core}/nullDC.cpp + ${d_core}/stdclass.cpp + ${d_core}/dispframe.cpp + ${d_core}/serialize.cpp +) + + +if(${FEAT_SHREC} EQUAL ${DYNAREC_JIT}) +# + if(${HOST_CPU} EQUAL ${CPU_X86}) + list(APPEND core_SRCS + ${d_core}/rec-x86/rec_x86_driver.cpp + ${d_core}/rec-x86/rec_x86_il.cpp + ${d_core}/rec-x86/rec_x86_asm.cpp # change for linux , rec_lin86_asm.S + ${d_core}/rec-x86/rec_x86_ngen.h + ) + elseif(${HOST_CPU} EQUAL ${CPU_ARM}) + list(APPEND core_SRCS + ${d_core}/rec-ARM/ngen_arm.S + ${d_core}/rec-ARM/rec_arm.cpp + ) + elseif(${HOST_CPU} EQUAL ${CPU_X64}) + + ### FIXME: asm with cmake ninja+VC + if(${BUILD_COMPILER} EQUAL ${COMPILER_VC}) + list(APPEND core_SRCS ${d_core}/rec-x64/msvc.asm) + endif() + + list(APPEND core_SRCS ${d_core}/rec-x64/rec_x64.cpp ${d_core}/rec-x64/x64_regalloc.h) + + elseif(${HOST_CPU} EQUAL ${CPU_A64}) + list(APPEND core_SRCS ${d_core}/rec-ARM64/rec_arm64.cpp ${d_core}/rec-ARM64/arm64_regalloc.h) + + else() + message(" FEAT_SHREC==DYNAREC_JIT && HOST_CPU Unknown Default add arch or disable rec if not avail.") + error() + endif() +# +elseif(${FEAT_SHREC} EQUAL ${DYNAREC_CPP}) + list(APPEND core_SRCS ${d_core}/rec-cpp/rec_cpp.cpp) +endif() + +add_definitions(/DFEAT_HAS_SOFTREND=0) + + +### deps.cmake ################################################################################# + +set(d_deps ${reicast_core_path}/deps) +include_directories ("${d_deps}") +include_directories ("${d_deps}/picotcp/include") +include_directories ("${d_deps}/picotcp/modules") + +file(GLOB xbyak_H ${d_deps}/xbyak/*.h) # include headers into cmake target/project view + +file(GLOB chdr_SRCS ${d_deps}/chdr/*.c) +file(GLOB lzma_SRCS ${d_deps}/lzma/*.c) +file(GLOB lz_SRCS ${d_deps}/zlib/*.c) +file(GLOB lzip_SRCS ${d_deps}/libzip/*.c) +file(GLOB lpng_SRCS ${d_deps}/libpng/*.c) +file(GLOB lelf_SRCS ${d_deps}/libelf/el*.cpp) +file(GLOB crypt_SRCS ${d_deps}/crypto/*.cpp) +file(GLOB imgui_SRCS ${d_deps}/imgui/*.cpp) +file(GLOB lws_SRCS ${d_deps}/libwebsocket/*.c) + +file(GLOB picoModS ${d_deps}/picotcp/modules/*.c) +file(GLOB picoStkS ${d_deps}/picotcp/stack/*.c) +set(pico_SRCS ${picoModS} ${picoStkS}) + +set(deps_SRCS + ${lz_SRCS} +# ${lzip_SRCS} +# ${lzma_SRCS} +# ${pico_SRCS} + ${lpng_SRCS} + ${lelf_SRCS} + ${chdr_SRCS} + ${crypt_SRCS} + ${imgui_SRCS} + ${d_deps}/xbrz/xbrz.cpp + ${d_deps}/dirent/dirent.c + ${d_deps}/xxhash/xxhash.c + ${d_deps}/chdpsr/cdipsr.cpp # sigh, this dir is named chdpsr for some reason ... + ${d_deps}/coreio/coreio.cpp +# ${d_deps}/ifaddrs/ifaddrs.c + ${xbyak_H} +) + + + +### libosd.cmake ################################################################################ + + +set(d_aout ${reicast_core_path}/oslib) + +include_directories ("${d_core}/khronos") + +## I really should just glob all of the dirs and ;shrug; if guards don't do it all ## + +set(osd_SRCS "") + +list(APPEND osd_SRCS ${d_aout}/audiostream.cpp) + +if (${HOST_OS} EQUAL ${OS_WINDOWS}) + + list(APPEND osd_SRCS ${d_core}/windows/winmain.cpp) + list(APPEND osd_SRCS ${d_aout}/audiobackend_directsound.cpp) + + + link_libraries(Dsound.lib winmm.lib) + + +elseif (${HOST_OS} EQUAL ${OS_LINUX} OR ${HOST_OS} EQUAL ${OS_ANDROID}) + + list(APPEND osd_SRCS + ${d_core}/linux/common.cpp + ${d_core}/linux/context.cpp + ${d_core}/linux/nixprof/nixprof.cpp + + ${d_aout}/audiobackend_oss.cpp # add option + ) # todo: configure linux audio lib options + + if(NOT ANDROID) + list(APPEND osd_SRCS + ${d_core}/linux-dist/x11.cpp + ${d_core}/linux-dist/main.cpp + ${d_core}/linux-dist/evdev.cpp) + + add_definitions(-DSUPPORT_X11) ## don't use GLES ? + link_libraries(X11) + else() + list(APPEND osd_SRCS + .//android-studio/reicast/src/main/jni/src/Android.cpp + .//android-studio/reicast/src/main/jni/src/utils.cpp + # .//android-studio/reicast/src/main/jni/src/XperiaPlay.c + ) + endif() # ANDROID + + add_definitions(-DGLES -DUSE_EVDEV) + + link_libraries(pthread dl rt asound Xext GLESv2 EGL) + +elseif(${HOST_OS} EQUAL ${OS_DARWIN}) +# + list(APPEND objc_SRCS + ./shell/apple/emulator-osx/emulator-osx/osx-main.mm + ./shell/apple/emulator-osx/emulator-osx/AppDelegate.swift + ./shell/apple/emulator-osx/emulator-osx/EmuGLView.swift + ) + + set_source_files_properties(${objc_SRCS} PROPERTIES COMPILE_FLAGS "-x objective-c++") + + list(APPEND osd_SRCS ${objc_SRCS} + ${d_osd}/linux/common.cpp + ${d_osd}/linux/context.cpp + ${d_osd}/audiobackend/audiobackend_coreaudio.cpp + # if NOT USE_SWIFT / ObjC + #${d_osd}/apple/osx_osd.cpp + ) + +else() +# + message("OS Unhandled") + error() +# +endif() + + + + + +set(reicast_SRCS ${core_SRCS} ${deps_SRCS} ${osd_SRCS}) + +add_executable(${TNAME}${binSuffix} ${reicast_SRCS} ${deps_SRCS}) + + + + + +if(APPLE) + enable_language(Swift) + set_property(TARGET ${TNAME} PROPERTY XCODE_ATTRIBUTE_SWIFT_OBJC_BRIDGING_HEADER "./shell/apple/emulator-osx/emulator-osx/emulator-osx-Bridging-Header.h") + + target_link_libraries(${TNAME} +# "-framework Cocoa" +# "-framework AppKit" + "-framework CoreData" + "-framework CoreAudio" + "-framework AudioUnit" + "-framework AudioToolbox" + "-framework Foundation" +) + +#### OSX Notes, when not using xcode you have to make app bundle, edit plist and copy, convert MainMenu.xib to nib and copy, +#null@devpc:~$ /Users/null/Documents/projects/reicast-emulator/bin/RelWithDebInfo/Reicast.app/Contents/MacOS/reicast ; exit; +#2019-03-18 14:28:44.842 reicast[11468:131797] Unknown class _TtC12emulator_osx9EmuGLView in Interface Builder file at path /Users/null/Documents/projects/reicast-emulator/bin/RelWithDebInfo/Reicast.app/Contents/Resources/MainMenu.nib. +#2019-03-18 14:28:44.842 reicast[11468:131797] Unknown class _TtC12emulator_osx11AppDelegate in Interface Builder file at path /Users/null/Documents/projects/reicast-emulator/bin/RelWithDebInfo/Reicast.app/Contents/Resources/MainMenu.nib. +#2019-03-18 14:28:44.860 reicast[11468:131797] Failed to connect (window) outlet from (NSObject) to (NSWindow): missing setter or instance variable +# + +endif() #APPLE + + +if(DEBUG_CMAKE) + message(" ------------------------------------------------") + message(" - HOST_OS: ${HOST_OS} - HOST_CPU: ${HOST_CPU} ") + message(" - host_os: ${host_os} - host_arch: ${host_arch} ") + message(" ------------------------------------------------") + message(" C Flags: ${CMAKE_C_FLAGS} ") + message(" CXX Flags: ${CMAKE_CXX_FLAGS} ") + message(" LINK_DIRS: ${LINK_DIRECTORIES}") + message("LINK_FLAGS: ${CMAKE_EXE_LINKER_FLAGS}") + message(" ------------------------------------------------\n") +endif() + + + + + + + + + + + + + + + + + + + diff --git a/CMakeSettings.json b/CMakeSettings.json new file mode 100644 index 000000000..8ddfb307d --- /dev/null +++ b/CMakeSettings.json @@ -0,0 +1,216 @@ +{ + "environments": [ + { + "environment": "toolchain.generic", + "TOOLCHAIN_FILE": "ps4sdk.cmake" + } + ], + "configurations": [ + { + "name": "win-x86-Debug", + "generator": "Ninja", + "description": "TemplateDescription_Localize_x86Debug", + "configurationType": "Debug", + "inheritEnvironments": [ + "msvc_x86" + ], + "buildRoot": "${projectDir}\\build\\${name}", + "installRoot": "${projectDir}\\build\\${name}\\install", + "cmakeCommandArgs": "", + "buildCommandArgs": "-v", + "ctestCommandArgs": "" + }, + { + "name": "win-x86-Release", + "generator": "Ninja", + "description": "TemplateDescription_Localize_x86Release", + "configurationType": "RelWithDebInfo", + "inheritEnvironments": [ + "msvc_x86" + ], + "buildRoot": "${projectDir}\\build\\${name}", + "installRoot": "${projectDir}\\build\\${name}\\install", + "cmakeCommandArgs": "", + "buildCommandArgs": "-v", + "ctestCommandArgs": "" + }, + { + "name": "win-x64-Debug", + "generator": "Ninja", + "description": "TemplateDescription_Localize_x64Debug", + "configurationType": "Debug", + "inheritEnvironments": [ + "msvc_x64_x64" + ], + "buildRoot": "${projectDir}\\build\\${name}", + "installRoot": "${projectDir}\\build\\${name}\\install", + "cmakeCommandArgs": "-DNINJA=1", + "buildCommandArgs": "-v", + "ctestCommandArgs": "" + }, + { + "name": "win-x64-Release", + "generator": "Ninja", + "description": "TemplateDescription_Localize_x64Release", + "configurationType": "RelWithDebInfo", + "inheritEnvironments": [ + "msvc_x64_x64" + ], + "buildRoot": "${projectDir}\\build\\${name}", + "installRoot": "${projectDir}\\build\\${name}\\install", + "cmakeCommandArgs": "-DNINJA=1", + "buildCommandArgs": "-v", + "ctestCommandArgs": "" + }, + { + "name": "win-x64-Clang-RelWithDebInfo", + "generator": "Ninja", + "description": "TemplateDescription_Localize_x64Release", + "configurationType": "RelWithDebInfo", + "inheritEnvironments": [ + "msvc_x64_x64" + ], + "buildRoot": "${projectDir}\\build\\${name}", + "installRoot": "${projectDir}\\build\\${name}\\install", + "cmakeCommandArgs": "", + "buildCommandArgs": "-v", + "ctestCommandArgs": "", + "variables": [ + { + "name": "CMAKE_C_COMPILER", + "value": "clang-cl.exe" + }, + { + "name": "CMAKE_CXX_COMPILER", + "value": "clang-cl.exe" + } + ] + }, + { + "name": "PS4 SDK", + "generator": "Ninja", + "description": "TemplateDescription_Localize_PS4SDK", + "configurationType": "RelWithDebInfo", + "inheritEnvironments": [ + "toolchain.generic" + ], + "buildRoot": "${projectDir}\\build\\${name}", + "installRoot": "${projectDir}\\build\\${name}\\install", + "cmakeCommandArgs": "-DCMAKE_TOOLCHAIN_FILE=${projectDir}\\cmake\\ps4sdk.cmake", + "buildCommandArgs": "-v", + "ctestCommandArgs": "" + }, + { + "name": "NSW SDK", + "generator": "Ninja", + "description": "TemplateDescription_Localize_PS4SDK", + "configurationType": "RelWithDebInfo", + "inheritEnvironments": [ + "toolchain.generic" + ], + "buildRoot": "${projectDir}\\build\\${name}", + "installRoot": "${projectDir}\\build\\${name}\\install", + "cmakeCommandArgs": "-DCMAKE_TOOLCHAIN_FILE=${projectDir}\\cmake\\devkitA64.cmake", + "buildCommandArgs": "-v", + "ctestCommandArgs": "" + }, + { + "name": "uwp-x64-Release", + "generator": "Visual Studio 15 2017 Win64", + "description": "TemplateDescription_Localize_x64Release", + "configurationType": "RelWithDebInfo", + "inheritEnvironments": [ + "msvc_x64_x64" + ], + "buildRoot": "${projectDir}\\build\\${name}", + "installRoot": "${projectDir}\\build\\${name}\\install", + "cmakeCommandArgs": "-DCMAKE_SYSTEM_NAME=WindowsStore -DCMAKE_SYSTEM_VERSION=10", + "buildCommandArgs": "", + "ctestCommandArgs": "" + }, + { + "name": "win-x64-MSBuild-Release", + "generator": "Visual Studio 15 2017 Win64", + "description": "TemplateDescription_Localize_x64Release", + "configurationType": "RelWithDebInfo", + "inheritEnvironments": [ + "msvc_x64_x64" + ], + "buildRoot": "${projectDir}\\build\\${name}", + "installRoot": "${projectDir}\\build\\${name}\\install", + "cmakeCommandArgs": "", + "buildCommandArgs": "", + "ctestCommandArgs": "" + }, + { + "environments": [ + { + "MINGW64_ROOT": "C:\\msys64\\mingw64", + "BIN_ROOT": "${env.MINGW64_ROOT}\\bin", + "FLAVOR": "x86_64-w64-mingw32", + "TOOLSET_VERSION": "7.3.0", + "PATH": "${env.MINGW64_ROOT}\\bin;${env.MINGW64_ROOT}\\..\\usr\\local\\bin;${env.MINGW64_ROOT}\\..\\usr\\bin;${env.MINGW64_ROOT}\\..\\bin;${env.PATH}", + "INCLUDE": "${env.INCLUDE};${env.MINGW64_ROOT}\\include\\c++\\${env.TOOLSET_VERSION};${env.MINGW64_ROOT}\\include\\c++\\${env.TOOLSET_VERSION}\\tr1;${env.MINGW64_ROOT}\\include\\c++\\${env.TOOLSET_VERSION}\\${env.FLAVOR}", + "environment": "mingw_64" + } + ], + "name": "Mingw64-Release", + "generator": "Ninja", + "configurationType": "RelWithDebInfo", + "inheritEnvironments": [ + "mingw_64" + ], + "buildRoot": "${projectDir}\\build\\${name}", + "installRoot": "${projectDir}\\build\\${name}\\install", + "cmakeCommandArgs": "", + "buildCommandArgs": "-v", + "ctestCommandArgs": "", + "intelliSenseMode": "linux-gcc-x64", + "variables": [ + { + "name": "CMAKE_C_COMPILER", + "value": "${env.BIN_ROOT}\\gcc.exe" + }, + { + "name": "CMAKE_CXX_COMPILER", + "value": "${env.BIN_ROOT}\\g++.exe" + } + ] + }, + { + "environments": [ + { + "MINGW64_ROOT": "C:\\msys64\\mingw64", + "BIN_ROOT": "${env.MINGW64_ROOT}\\bin", + "FLAVOR": "x86_64-w64-mingw32", + "TOOLSET_VERSION": "7.3.0", + "PATH": "${env.MINGW64_ROOT}\\bin;${env.MINGW64_ROOT}\\..\\usr\\local\\bin;${env.MINGW64_ROOT}\\..\\usr\\bin;${env.MINGW64_ROOT}\\..\\bin;${env.PATH}", + "INCLUDE": "${env.INCLUDE};${env.MINGW64_ROOT}\\include\\c++\\${env.TOOLSET_VERSION};${env.MINGW64_ROOT}\\include\\c++\\${env.TOOLSET_VERSION}\\tr1;${env.MINGW64_ROOT}\\include\\c++\\${env.TOOLSET_VERSION}\\${env.FLAVOR}", + "environment": "mingw_64" + } + ], + "name": "Mingw64-Debug", + "generator": "Ninja", + "configurationType": "Debug", + "inheritEnvironments": [ + "mingw_64" + ], + "buildRoot": "${projectDir}\\build\\${name}", + "installRoot": "${projectDir}\\build\\${name}\\install", + "cmakeCommandArgs": "", + "buildCommandArgs": "-v", + "ctestCommandArgs": "", + "intelliSenseMode": "linux-gcc-x64", + "variables": [ + { + "name": "CMAKE_C_COMPILER", + "value": "${env.BIN_ROOT}\\gcc.exe" + }, + { + "name": "CMAKE_CXX_COMPILER", + "value": "${env.BIN_ROOT}\\g++.exe" + } + ] + } + ] +} \ No newline at end of file diff --git a/core/build.h b/core/build.h index a9a9777dd..4dc84a222 100755 --- a/core/build.h +++ b/core/build.h @@ -127,10 +127,18 @@ #define DC_PLATFORM_AURORA 6 /* Needs to be done, Uses newer 300 mhz sh4 + 150 mhz pvr mbx SoC */ + //HOST_OS #define OS_WINDOWS 0x10000001 #define OS_LINUX 0x10000002 #define OS_DARWIN 0x10000003 +#define OS_IOS 0x10000004 +#define OS_ANDROID 0x10000005 + +#define OS_UWP 0x10000011 +#define OS_NSW_HOS 0x80000001 +#define OS_PS4_BSD 0x80000002 + //HOST_CPU #define CPU_X86 0x20000001 @@ -138,11 +146,16 @@ #define CPU_MIPS 0x20000003 #define CPU_X64 0x20000004 #define CPU_GENERIC 0x20000005 //used for pnacl, emscripten, etc -#define CPU_ARM64 0x20000006 +#define CPU_PPC 0x20000006 +#define CPU_PPC64 0x20000007 +#define CPU_A64 0x20000008 +#define CPU_MIPS64 0x20000009 //BUILD_COMPILER -#define COMPILER_VC 0x30000001 -#define COMPILER_GCC 0x30000002 +#define COMPILER_VC 0x30000001 +#define COMPILER_GCC 0x30000002 +#define COMPILER_CLANG 0x30000002 +#define COMPILER_INTEL 0x30000002 //FEAT_SHREC, FEAT_AREC, FEAT_DSPREC #define DYNAREC_NONE 0x40000001 @@ -152,6 +165,8 @@ //automatic +#ifndef CMAKE_BUILD + #if defined(_WIN32) && !defined(TARGET_WIN86) && !defined(TARGET_WIN64) #if !defined(_M_AMD64) && !defined(__x86_64__) #define TARGET_WIN86 @@ -233,6 +248,8 @@ #define FEAT_DSPREC DYNAREC_NONE #endif +#endif // !CMAKE_BUILD + #if defined(TARGET_NO_NIXPROF) #define FEAT_HAS_NIXPROF 0 @@ -295,6 +312,19 @@ #error Dont use HOST_NO_AREC #endif + +// Compiler Related + +#if BUILD_COMPILER!=COMPILER_VC +#define ATTR_USED __attribute__((used)) +#define ATTR_UNUSED __attribute__((used)) +#else +#define ATTR_USED +#define ATTR_UNUSED +#endif + + + // TARGET PLATFORM #define RAM_SIZE_MAX (32*1024*1024) diff --git a/core/hw/modem/dns.cpp b/core/hw/modem/dns.cpp index 1dc7f46c8..1f1b3283e 100644 --- a/core/hw/modem/dns.cpp +++ b/core/hw/modem/dns.cpp @@ -18,6 +18,9 @@ You should have received a copy of the GNU General Public License along with reicast. If not, see . */ +#include "types.h" + +#if BUILD_COMPILER!=COMPILER_VC && (BUILD_COMPILER!=COMPILER_CLANG || !WIN32) #include #include @@ -145,3 +148,5 @@ char *read_name(char *reader, char *buffer, int *count) return name; } + +#endif // BUILD_COMPILER!=COMPILER_VC \ No newline at end of file diff --git a/core/hw/sh4/dyna/blockmanager.h b/core/hw/sh4/dyna/blockmanager.h index 5c11af77f..e21fb3dee 100644 --- a/core/hw/sh4/dyna/blockmanager.h +++ b/core/hw/sh4/dyna/blockmanager.h @@ -86,10 +86,7 @@ void bm_WriteBlockMap(const string& file); DynarecCodeEntryPtr DYNACALL bm_GetCode(u32 addr); extern "C" { -#ifndef _MSC_VER -__attribute__((used)) -#endif - DynarecCodeEntryPtr DYNACALL bm_GetCode2(u32 addr); +ATTR_USED DynarecCodeEntryPtr DYNACALL bm_GetCode2(u32 addr); } RuntimeBlockInfo* bm_GetBlock(void* dynarec_code); diff --git a/core/hw/sh4/sh4_interpreter.h b/core/hw/sh4/sh4_interpreter.h index 8d827af84..8a22caa83 100644 --- a/core/hw/sh4/sh4_interpreter.h +++ b/core/hw/sh4/sh4_interpreter.h @@ -61,9 +61,7 @@ void ExecuteDelayslot_RTE(); extern "C" { int UpdateSystem(); -#ifndef _MSC_VER -__attribute__((used)) -#endif - int UpdateSystem_INTC(); + +ATTR_USED int UpdateSystem_INTC(); } diff --git a/core/hw/sh4/sh4_sched.cpp b/core/hw/sh4/sh4_sched.cpp index baeef15a8..65b8af0c7 100755 --- a/core/hw/sh4/sh4_sched.cpp +++ b/core/hw/sh4/sh4_sched.cpp @@ -25,15 +25,15 @@ u64 sh4_sched_ffb; u32 sh4_sched_intr; -vector list; +vector sch_list; // using list as external inside a macro confuses clang and msc int sh4_sched_next_id=-1; u32 sh4_sched_remaining(int id, u32 reference) { - if (list[id].end != -1) + if (sch_list[id].end != -1) { - return list[id].end - reference; + return sch_list[id].end - reference; } else { @@ -51,7 +51,7 @@ void sh4_sched_ffts() u32 diff=-1; int slot=-1; - for (size_t i=0;i= 0 && cycles <= SH4_MAIN_CLOCK)); - list[id].start=sh4_sched_now(); + sch_list[id].start=sh4_sched_now(); if (cycles == -1) { - list[id].end = -1; + sch_list[id].end = -1; } else { - list[id].end = list[id].start + cycles; - if (list[id].end == -1) - list[id].end++; + sch_list[id].end = sch_list[id].start + cycles; + if (sch_list[id].end == -1) + sch_list[id].end++; } sh4_sched_ffts(); @@ -120,10 +120,10 @@ void sh4_sched_request(int id, int cycles) int sh4_sched_elapsed(int id) { - if (list[id].end!=-1) + if (sch_list[id].end!=-1) { - int rv=sh4_sched_now()-list[id].start; - list[id].start=sh4_sched_now(); + int rv=sh4_sched_now()-sch_list[id].start; + sch_list[id].start=sh4_sched_now(); return rv; } else @@ -132,12 +132,12 @@ int sh4_sched_elapsed(int id) void handle_cb(int id) { - int remain=list[id].end-list[id].start; + int remain=sch_list[id].end-sch_list[id].start; int elapsd=sh4_sched_elapsed(id); int jitter=elapsd-remain; - list[id].end=-1; - int re_sch=list[id].cb(list[id].tag,remain,jitter); + sch_list[id].end=-1; + int re_sch=sch_list[id].cb(sch_list[id].tag,remain,jitter); if (re_sch > 0) sh4_sched_request(id, max(0, re_sch - jitter)); @@ -156,7 +156,7 @@ void sh4_sched_tick(int cycles) sh4_sched_intr++; if (sh4_sched_next_id!=-1) { - for (int i=0;i= 0 || remaining == -1); diff --git a/core/rend/gl4/gltex.cpp b/core/rend/gl4/gltex.cpp index 85a0b9430..52aba888e 100644 --- a/core/rend/gl4/gltex.cpp +++ b/core/rend/gl4/gltex.cpp @@ -1,5 +1,5 @@ #include "gl4.h" -#include "glcache.h" +#include "../gles/glcache.h" GLuint gl4BindRTT(u32 addy, u32 fbw, u32 fbh, u32 channels, u32 fmt) { diff --git a/core/rend/gui.cpp b/core/rend/gui.cpp index e3fde42f4..a530f5b5c 100644 --- a/core/rend/gui.cpp +++ b/core/rend/gui.cpp @@ -39,7 +39,8 @@ #include "linux-dist/main.h" // FIXME for kcode[] #include "gui_util.h" #include "gui_android.h" -#include "version/version.h" + +#include "version.h" #include "oslib/audiostream.h" diff --git a/core/serialize.cpp b/core/serialize.cpp index f040744a4..9d28fd068 100644 --- a/core/serialize.cpp +++ b/core/serialize.cpp @@ -441,7 +441,7 @@ extern u32 old_dn; //./core/hw/sh4/sh4_sched.o extern u64 sh4_sched_ffb; extern u32 sh4_sched_intr; -extern vector list; +extern vector sch_list; //extern int sh4_sched_next_id; @@ -975,8 +975,6 @@ bool dc_serialize(void **data, unsigned int *total_size) REICAST_S(decoded_srimask); - - //default to nommu_full i = 3 ; if ( do_sqw_nommu == &do_sqw_nommu_area_3) @@ -1008,48 +1006,49 @@ bool dc_serialize(void **data, unsigned int *total_size) //extern vector list; - REICAST_S(list[aica_schid].tag) ; - REICAST_S(list[aica_schid].start) ; - REICAST_S(list[aica_schid].end) ; - REICAST_S(list[rtc_schid].tag) ; - REICAST_S(list[rtc_schid].start) ; - REICAST_S(list[rtc_schid].end) ; + REICAST_S(sch_list[aica_schid].tag) ; + REICAST_S(sch_list[aica_schid].start) ; + REICAST_S(sch_list[aica_schid].end) ; - REICAST_S(list[gdrom_schid].tag) ; - REICAST_S(list[gdrom_schid].start) ; - REICAST_S(list[gdrom_schid].end) ; + REICAST_S(sch_list[rtc_schid].tag) ; + REICAST_S(sch_list[rtc_schid].start) ; + REICAST_S(sch_list[rtc_schid].end) ; - REICAST_S(list[maple_schid].tag) ; - REICAST_S(list[maple_schid].start) ; - REICAST_S(list[maple_schid].end) ; + REICAST_S(sch_list[gdrom_schid].tag) ; + REICAST_S(sch_list[gdrom_schid].start) ; + REICAST_S(sch_list[gdrom_schid].end) ; - REICAST_S(list[dma_sched_id].tag) ; - REICAST_S(list[dma_sched_id].start) ; - REICAST_S(list[dma_sched_id].end) ; + REICAST_S(sch_list[maple_schid].tag) ; + REICAST_S(sch_list[maple_schid].start) ; + REICAST_S(sch_list[maple_schid].end) ; + + REICAST_S(sch_list[dma_sched_id].tag) ; + REICAST_S(sch_list[dma_sched_id].start) ; + REICAST_S(sch_list[dma_sched_id].end) ; for (int i = 0; i < 3; i++) { - REICAST_S(list[tmu_sched[i]].tag) ; - REICAST_S(list[tmu_sched[i]].start) ; - REICAST_S(list[tmu_sched[i]].end) ; + REICAST_S(sch_list[tmu_sched[i]].tag) ; + REICAST_S(sch_list[tmu_sched[i]].start) ; + REICAST_S(sch_list[tmu_sched[i]].end) ; } - REICAST_S(list[render_end_schid].tag) ; - REICAST_S(list[render_end_schid].start) ; - REICAST_S(list[render_end_schid].end) ; + REICAST_S(sch_list[render_end_schid].tag) ; + REICAST_S(sch_list[render_end_schid].start) ; + REICAST_S(sch_list[render_end_schid].end) ; - REICAST_S(list[vblank_schid].tag) ; - REICAST_S(list[vblank_schid].start) ; - REICAST_S(list[vblank_schid].end) ; + REICAST_S(sch_list[vblank_schid].tag) ; + REICAST_S(sch_list[vblank_schid].start) ; + REICAST_S(sch_list[vblank_schid].end) ; - REICAST_S(list[time_sync].tag) ; - REICAST_S(list[time_sync].start) ; - REICAST_S(list[time_sync].end) ; + REICAST_S(sch_list[time_sync].tag) ; + REICAST_S(sch_list[time_sync].start) ; + REICAST_S(sch_list[time_sync].end) ; - REICAST_S(list[modem_sched].tag) ; - REICAST_S(list[modem_sched].start) ; - REICAST_S(list[modem_sched].end) ; + REICAST_S(sch_list[modem_sched].tag) ; + REICAST_S(sch_list[modem_sched].start) ; + REICAST_S(sch_list[modem_sched].end) ; @@ -1408,50 +1407,50 @@ static bool dc_unserialize_libretro(void **data, unsigned int *total_size) REICAST_US(sh4_sched_ffb); REICAST_US(sh4_sched_intr); - //extern vector list; + //extern vector sch_list; - REICAST_US(list[aica_schid].tag) ; - REICAST_US(list[aica_schid].start) ; - REICAST_US(list[aica_schid].end) ; + REICAST_US(sch_list[aica_schid].tag) ; + REICAST_US(sch_list[aica_schid].start) ; + REICAST_US(sch_list[aica_schid].end) ; - REICAST_US(list[rtc_schid].tag) ; - REICAST_US(list[rtc_schid].start) ; - REICAST_US(list[rtc_schid].end) ; + REICAST_US(sch_list[rtc_schid].tag) ; + REICAST_US(sch_list[rtc_schid].start) ; + REICAST_US(sch_list[rtc_schid].end) ; - REICAST_US(list[gdrom_schid].tag) ; - REICAST_US(list[gdrom_schid].start) ; - REICAST_US(list[gdrom_schid].end) ; + REICAST_US(sch_list[gdrom_schid].tag) ; + REICAST_US(sch_list[gdrom_schid].start) ; + REICAST_US(sch_list[gdrom_schid].end) ; - REICAST_US(list[maple_schid].tag) ; - REICAST_US(list[maple_schid].start) ; - REICAST_US(list[maple_schid].end) ; + REICAST_US(sch_list[maple_schid].tag) ; + REICAST_US(sch_list[maple_schid].start) ; + REICAST_US(sch_list[maple_schid].end) ; - REICAST_US(list[dma_sched_id].tag) ; - REICAST_US(list[dma_sched_id].start) ; - REICAST_US(list[dma_sched_id].end) ; + REICAST_US(sch_list[dma_sched_id].tag) ; + REICAST_US(sch_list[dma_sched_id].start) ; + REICAST_US(sch_list[dma_sched_id].end) ; for (int i = 0; i < 3; i++) { - REICAST_US(list[tmu_sched[i]].tag) ; - REICAST_US(list[tmu_sched[i]].start) ; - REICAST_US(list[tmu_sched[i]].end) ; + REICAST_US(sch_list[tmu_sched[i]].tag) ; + REICAST_US(sch_list[tmu_sched[i]].start) ; + REICAST_US(sch_list[tmu_sched[i]].end) ; } - REICAST_US(list[render_end_schid].tag) ; - REICAST_US(list[render_end_schid].start) ; - REICAST_US(list[render_end_schid].end) ; + REICAST_US(sch_list[render_end_schid].tag) ; + REICAST_US(sch_list[render_end_schid].start) ; + REICAST_US(sch_list[render_end_schid].end) ; - REICAST_US(list[vblank_schid].tag) ; - REICAST_US(list[vblank_schid].start) ; - REICAST_US(list[vblank_schid].end) ; + REICAST_US(sch_list[vblank_schid].tag) ; + REICAST_US(sch_list[vblank_schid].start) ; + REICAST_US(sch_list[vblank_schid].end) ; - REICAST_US(list[time_sync].tag) ; - REICAST_US(list[time_sync].start) ; - REICAST_US(list[time_sync].end) ; + REICAST_US(sch_list[time_sync].tag) ; + REICAST_US(sch_list[time_sync].start) ; + REICAST_US(sch_list[time_sync].end) ; - REICAST_US(list[modem_sched].tag) ; - REICAST_US(list[modem_sched].start) ; - REICAST_US(list[modem_sched].end) ; + REICAST_US(sch_list[modem_sched].tag) ; + REICAST_US(sch_list[modem_sched].start) ; + REICAST_US(sch_list[modem_sched].end) ; @@ -1800,48 +1799,48 @@ bool dc_unserialize(void **data, unsigned int *total_size) //extern vector list; - REICAST_US(list[aica_schid].tag) ; - REICAST_US(list[aica_schid].start) ; - REICAST_US(list[aica_schid].end) ; + REICAST_US(sch_list[aica_schid].tag) ; + REICAST_US(sch_list[aica_schid].start) ; + REICAST_US(sch_list[aica_schid].end) ; - REICAST_US(list[rtc_schid].tag) ; - REICAST_US(list[rtc_schid].start) ; - REICAST_US(list[rtc_schid].end) ; + REICAST_US(sch_list[rtc_schid].tag) ; + REICAST_US(sch_list[rtc_schid].start) ; + REICAST_US(sch_list[rtc_schid].end) ; - REICAST_US(list[gdrom_schid].tag) ; - REICAST_US(list[gdrom_schid].start) ; - REICAST_US(list[gdrom_schid].end) ; + REICAST_US(sch_list[gdrom_schid].tag) ; + REICAST_US(sch_list[gdrom_schid].start) ; + REICAST_US(sch_list[gdrom_schid].end) ; - REICAST_US(list[maple_schid].tag) ; - REICAST_US(list[maple_schid].start) ; - REICAST_US(list[maple_schid].end) ; + REICAST_US(sch_list[maple_schid].tag) ; + REICAST_US(sch_list[maple_schid].start) ; + REICAST_US(sch_list[maple_schid].end) ; - REICAST_US(list[dma_sched_id].tag) ; - REICAST_US(list[dma_sched_id].start) ; - REICAST_US(list[dma_sched_id].end) ; + REICAST_US(sch_list[dma_sched_id].tag) ; + REICAST_US(sch_list[dma_sched_id].start) ; + REICAST_US(sch_list[dma_sched_id].end) ; for (int i = 0; i < 3; i++) { - REICAST_US(list[tmu_sched[i]].tag) ; - REICAST_US(list[tmu_sched[i]].start) ; - REICAST_US(list[tmu_sched[i]].end) ; + REICAST_US(sch_list[tmu_sched[i]].tag) ; + REICAST_US(sch_list[tmu_sched[i]].start) ; + REICAST_US(sch_list[tmu_sched[i]].end) ; } - REICAST_US(list[render_end_schid].tag) ; - REICAST_US(list[render_end_schid].start) ; - REICAST_US(list[render_end_schid].end) ; + REICAST_US(sch_list[render_end_schid].tag) ; + REICAST_US(sch_list[render_end_schid].start) ; + REICAST_US(sch_list[render_end_schid].end) ; - REICAST_US(list[vblank_schid].tag) ; - REICAST_US(list[vblank_schid].start) ; - REICAST_US(list[vblank_schid].end) ; + REICAST_US(sch_list[vblank_schid].tag) ; + REICAST_US(sch_list[vblank_schid].start) ; + REICAST_US(sch_list[vblank_schid].end) ; - REICAST_US(list[time_sync].tag) ; - REICAST_US(list[time_sync].start) ; - REICAST_US(list[time_sync].end) ; + REICAST_US(sch_list[time_sync].tag) ; + REICAST_US(sch_list[time_sync].start) ; + REICAST_US(sch_list[time_sync].end) ; - REICAST_US(list[modem_sched].tag) ; - REICAST_US(list[modem_sched].start) ; - REICAST_US(list[modem_sched].end) ; + REICAST_US(sch_list[modem_sched].tag) ; + REICAST_US(sch_list[modem_sched].start) ; + REICAST_US(sch_list[modem_sched].end) ; diff --git a/core/stdclass.cpp b/core/stdclass.cpp index 59bf1e94a..f9acec72f 100644 --- a/core/stdclass.cpp +++ b/core/stdclass.cpp @@ -5,12 +5,10 @@ #include "types.h" #include "cfg/cfg.h" -#ifdef WIN32 -#include -#endif -#if BUILD_COMPILER==COMPILER_VC +#if BUILD_COMPILER==COMPILER_VC || (WIN32 && BUILD_COMPILER==COMPILER_CLANG) #include + #include #define access _access #define R_OK 4 #else diff --git a/core/types.h b/core/types.h index 4d9369127..4f3fb23b8 100644 --- a/core/types.h +++ b/core/types.h @@ -385,9 +385,9 @@ using namespace std; #include "stdclass.h" #ifndef RELEASE -#define EMUERROR(format, ...) printf("Error in %s:%s:%d: " format "\n", \ - strlen(__FILE__) <= 20 ? __FILE__ : __FILE__ + strlen(__FILE__) - 20, \ - __FUNCTION__, __LINE__, ##__VA_ARGS__) +#define EMUERROR(format, ...) printf("Error in %20s:%s:%d: " format "\n", \ + __FILE__, __FUNCTION__, __LINE__, ##__VA_ARGS__) +//strlen(__FILE__) <= 20 ? __FILE__ : __FILE__ + strlen(__FILE__) - 20, #else #define EMUERROR(format, ...) #endif @@ -521,7 +521,7 @@ typedef union #if COMPILER_VC==BUILD_COMPILER #pragma warning( disable : 4127 4996 /*4244*/) #else -#define stricmp strcasecmp +#define stricmp _stricmp // ISO , was strcasecmp #endif #ifndef STRIP_TEXT diff --git a/core/version.h.in b/core/version.h.in new file mode 100644 index 000000000..81a831a05 --- /dev/null +++ b/core/version.h.in @@ -0,0 +1,8 @@ +/* + * reicast: version.h + */ +#pragma once + +#define REICAST_VERSION "@GIT_VERSION@" +#define GIT_HASH "@GIT_HASH@" +#define BUILD_DATE __DATE__ \ No newline at end of file diff --git a/core/windows/winmain.cpp b/core/windows/winmain.cpp index 3aa5a4a51..7e269a90d 100644 --- a/core/windows/winmain.cpp +++ b/core/windows/winmain.cpp @@ -673,7 +673,7 @@ int CALLBACK WinMain(HINSTANCE hInstance,HINSTANCE hPrevInstance,LPSTR lpCmdLine #ifdef _WIN64 AddVectoredExceptionHandler(1, ExeptionHandler); #else - SetUnhandledExceptionFilter(&ExeptionHandler); + SetUnhandledExceptionFilter((LPTOP_LEVEL_EXCEPTION_FILTER)&ExeptionHandler); #endif #ifndef __GNUC__ __try diff --git a/shell/cmake/GetGitRevisionDescription.cmake b/shell/cmake/GetGitRevisionDescription.cmake new file mode 100644 index 000000000..8ab03bc5f --- /dev/null +++ b/shell/cmake/GetGitRevisionDescription.cmake @@ -0,0 +1,168 @@ +# - Returns a version string from Git +# +# These functions force a re-configure on each git commit so that you can +# trust the values of the variables in your build system. +# +# get_git_head_revision( [ ...]) +# +# Returns the refspec and sha hash of the current head revision +# +# git_describe( [ ...]) +# +# Returns the results of git describe on the source tree, and adjusting +# the output so that it tests false if an error occurs. +# +# git_get_exact_tag( [ ...]) +# +# Returns the results of git describe --exact-match on the source tree, +# and adjusting the output so that it tests false if there was no exact +# matching tag. +# +# git_local_changes() +# +# Returns either "CLEAN" or "DIRTY" with respect to uncommitted changes. +# Uses the return code of "git diff-index --quiet HEAD --". +# Does not regard untracked files. +# +# Requires CMake 2.6 or newer (uses the 'function' command) +# +# Original Author: +# 2009-2010 Ryan Pavlik +# http://academic.cleardefinition.com +# Iowa State University HCI Graduate Program/VRAC +# +# Copyright Iowa State University 2009-2010. +# Distributed under the Boost Software License, Version 1.0. +# (See accompanying file LICENSE_1_0.txt or copy at +# http://www.boost.org/LICENSE_1_0.txt) + +if(__get_git_revision_description) + return() +endif() +set(__get_git_revision_description YES) + +# We must run the following at "include" time, not at function call time, +# to find the path to this module rather than the path to a calling list file +get_filename_component(_gitdescmoddir ${CMAKE_CURRENT_LIST_FILE} PATH) + +function(get_git_head_revision _refspecvar _hashvar) + set(GIT_PARENT_DIR "${CMAKE_CURRENT_SOURCE_DIR}") + set(GIT_DIR "${GIT_PARENT_DIR}/.git") + while(NOT EXISTS "${GIT_DIR}") # .git dir not found, search parent directories + set(GIT_PREVIOUS_PARENT "${GIT_PARENT_DIR}") + get_filename_component(GIT_PARENT_DIR ${GIT_PARENT_DIR} PATH) + if(GIT_PARENT_DIR STREQUAL GIT_PREVIOUS_PARENT) + # We have reached the root directory, we are not in git + set(${_refspecvar} "GITDIR-NOTFOUND" PARENT_SCOPE) + set(${_hashvar} "GITDIR-NOTFOUND" PARENT_SCOPE) + return() + endif() + set(GIT_DIR "${GIT_PARENT_DIR}/.git") + endwhile() + # check if this is a submodule + if(NOT IS_DIRECTORY ${GIT_DIR}) + file(READ ${GIT_DIR} submodule) + string(REGEX REPLACE "gitdir: (.*)\n$" "\\1" GIT_DIR_RELATIVE ${submodule}) + get_filename_component(SUBMODULE_DIR ${GIT_DIR} PATH) + get_filename_component(GIT_DIR ${SUBMODULE_DIR}/${GIT_DIR_RELATIVE} ABSOLUTE) + endif() + set(GIT_DATA "${CMAKE_CURRENT_BINARY_DIR}/CMakeFiles/git-data") + if(NOT EXISTS "${GIT_DATA}") + file(MAKE_DIRECTORY "${GIT_DATA}") + endif() + + if(NOT EXISTS "${GIT_DIR}/HEAD") + return() + endif() + set(HEAD_FILE "${GIT_DATA}/HEAD") + configure_file("${GIT_DIR}/HEAD" "${HEAD_FILE}" COPYONLY) + + configure_file("${_gitdescmoddir}/GetGitRevisionDescription.cmake.in" + "${GIT_DATA}/grabRef.cmake" + @ONLY) + include("${GIT_DATA}/grabRef.cmake") + + set(${_refspecvar} "${HEAD_REF}" PARENT_SCOPE) + set(${_hashvar} "${HEAD_HASH}" PARENT_SCOPE) +endfunction() + +function(git_describe _var) + if(NOT GIT_FOUND) + find_package(Git QUIET) + endif() + get_git_head_revision(refspec hash) + if(NOT GIT_FOUND) + set(${_var} "GIT-NOTFOUND" PARENT_SCOPE) + return() + endif() + if(NOT hash) + set(${_var} "HEAD-HASH-NOTFOUND" PARENT_SCOPE) + return() + endif() + + # TODO sanitize + #if((${ARGN}" MATCHES "&&") OR + # (ARGN MATCHES "||") OR + # (ARGN MATCHES "\\;")) + # message("Please report the following error to the project!") + # message(FATAL_ERROR "Looks like someone's doing something nefarious with git_describe! Passed arguments ${ARGN}") + #endif() + + #message(STATUS "Arguments to execute_process: ${ARGN}") + + execute_process(COMMAND + "${GIT_EXECUTABLE}" + describe + ${hash} + ${ARGN} + WORKING_DIRECTORY + "${CMAKE_CURRENT_SOURCE_DIR}" + RESULT_VARIABLE + res + OUTPUT_VARIABLE + out + ERROR_QUIET + OUTPUT_STRIP_TRAILING_WHITESPACE) + if(NOT res EQUAL 0) + set(out "${out}-${res}-NOTFOUND") + endif() + + set(${_var} "${out}" PARENT_SCOPE) +endfunction() + +function(git_get_exact_tag _var) + git_describe(out --exact-match ${ARGN}) + set(${_var} "${out}" PARENT_SCOPE) +endfunction() + +function(git_local_changes _var) + if(NOT GIT_FOUND) + find_package(Git QUIET) + endif() + get_git_head_revision(refspec hash) + if(NOT GIT_FOUND) + set(${_var} "GIT-NOTFOUND" PARENT_SCOPE) + return() + endif() + if(NOT hash) + set(${_var} "HEAD-HASH-NOTFOUND" PARENT_SCOPE) + return() + endif() + + execute_process(COMMAND + "${GIT_EXECUTABLE}" + diff-index --quiet HEAD -- + WORKING_DIRECTORY + "${CMAKE_CURRENT_SOURCE_DIR}" + RESULT_VARIABLE + res + OUTPUT_VARIABLE + out + ERROR_QUIET + OUTPUT_STRIP_TRAILING_WHITESPACE) + if(res EQUAL 0) + set(${_var} "CLEAN" PARENT_SCOPE) + else() + set(${_var} "DIRTY" PARENT_SCOPE) + endif() +endfunction() diff --git a/shell/cmake/GetGitRevisionDescription.cmake.in b/shell/cmake/GetGitRevisionDescription.cmake.in new file mode 100644 index 000000000..6d8b708ef --- /dev/null +++ b/shell/cmake/GetGitRevisionDescription.cmake.in @@ -0,0 +1,41 @@ +# +# Internal file for GetGitRevisionDescription.cmake +# +# Requires CMake 2.6 or newer (uses the 'function' command) +# +# Original Author: +# 2009-2010 Ryan Pavlik +# http://academic.cleardefinition.com +# Iowa State University HCI Graduate Program/VRAC +# +# Copyright Iowa State University 2009-2010. +# Distributed under the Boost Software License, Version 1.0. +# (See accompanying file LICENSE_1_0.txt or copy at +# http://www.boost.org/LICENSE_1_0.txt) + +set(HEAD_HASH) + +file(READ "@HEAD_FILE@" HEAD_CONTENTS LIMIT 1024) + +string(STRIP "${HEAD_CONTENTS}" HEAD_CONTENTS) +if(HEAD_CONTENTS MATCHES "ref") + # named branch + string(REPLACE "ref: " "" HEAD_REF "${HEAD_CONTENTS}") + if(EXISTS "@GIT_DIR@/${HEAD_REF}") + configure_file("@GIT_DIR@/${HEAD_REF}" "@GIT_DATA@/head-ref" COPYONLY) + else() + configure_file("@GIT_DIR@/packed-refs" "@GIT_DATA@/packed-refs" COPYONLY) + file(READ "@GIT_DATA@/packed-refs" PACKED_REFS) + if(${PACKED_REFS} MATCHES "([0-9a-z]*) ${HEAD_REF}") + set(HEAD_HASH "${CMAKE_MATCH_1}") + endif() + endif() +else() + # detached HEAD + configure_file("@GIT_DIR@/HEAD" "@GIT_DATA@/head-ref" COPYONLY) +endif() + +if(NOT HEAD_HASH) + file(READ "@GIT_DATA@/head-ref" HEAD_HASH LIMIT 1024) + string(STRIP "${HEAD_HASH}" HEAD_HASH) +endif() diff --git a/shell/cmake/android.cmake b/shell/cmake/android.cmake new file mode 100644 index 000000000..366fcb043 --- /dev/null +++ b/shell/cmake/android.cmake @@ -0,0 +1,101 @@ +## android.cmake +# + + + +function(TestPathNDK ndkPath) +# + file(TO_CMAKE_PATH "${ndkPath}" testPath) + + if(NOT NDK AND EXISTS "${testPath}") + if(EXISTS "${testPath}/ndk-bundle") + set(NDK ${testPath}/ndk-bundle PARENT_SCOPE) + elseif(EXISTS "${testPath}/sysroot") + set(NDK ${testPath} PARENT_SCOPE) + endif() + endif() +# +endfunction(TestPathNDK) + + +TestPathNDK("$ENV{ANDROID_HOME}") +TestPathNDK("$ENV{NDK}") +TestPathNDK("$ENV{NDK_ROOT}") + +if(NOT NDK) + message("Failed to find NDK !") +endif() + + + + + + +### option for ARM || ARM64 ? HOST isn't useful it's a cross ... + +#set(CMAKE_SYSTEM_PROCESSOR aarch64) + + + +set(CMAKE_SYSTEM_NAME Android) +set(CMAKE_SYSTEM_VERSION 22) # API level + +set(CMAKE_ANDROID_NDK ${NDK}) +set(CMAKE_ANDROID_ARCH_ABI armeabi-v7a) #arm64-v8a , armeabi-v7a , armeabi +set(CMAKE_ANDROID_STL_TYPE c++_static) #gnustl_static libc++ will allow C++17, if you use _shared you must include in apk ! +set(CMAKE_ANDROID_NDK_TOOLCHAIN_VERSION clang) + + +#arm $TOOLCHAIN/ arm-linux-androideabi /lib/ +#arm64 $TOOLCHAIN/ aarch64-linux-android /lib/ +#x86 $TOOLCHAIN/ i686-linux-android /lib/ +#x86_64 $TOOLCHAIN/ x86_64-linux-android /lib/ + + + +#include(${NDK}/build/cmake/android.toolchain.cmake) + + +set(ANDROID ON) + +add_definitions(-D_ANDROID -DANDROID) +add_definitions(-DANDROID_STL=c++_static) + + +add_definitions(-DTARGET_ANDROID) + +add_definitions(-DGLES) + + + + + +## FML + +#[[ +CMAKE_ANDROID_ANT_ADDITIONAL_OPTIONS +CMAKE_ANDROID_API +CMAKE_ANDROID_API_MIN +CMAKE_ANDROID_ARCH +CMAKE_ANDROID_ARCH_ABI +CMAKE_ANDROID_ARM_MODE +CMAKE_ANDROID_ARM_NEON +CMAKE_ANDROID_ASSETS_DIRECTORIES +CMAKE_ANDROID_GUI +CMAKE_ANDROID_JAR_DEPENDENCIES +CMAKE_ANDROID_JAR_DIRECTORIES +CMAKE_ANDROID_JAVA_SOURCE_DIR +CMAKE_ANDROID_NATIVE_LIB_DEPENDENCIES +CMAKE_ANDROID_NATIVE_LIB_DIRECTORIES +CMAKE_ANDROID_NDK +CMAKE_ANDROID_NDK_DEPRECATED_HEADERS +CMAKE_ANDROID_NDK_TOOLCHAIN_HOST_TAG +CMAKE_ANDROID_NDK_TOOLCHAIN_VERSION +CMAKE_ANDROID_PROCESS_MAX +CMAKE_ANDROID_PROGUARD +CMAKE_ANDROID_PROGUARD_CONFIG_PATH +CMAKE_ANDROID_SECURE_PROPS_PATH +CMAKE_ANDROID_SKIP_ANT_STEP +CMAKE_ANDROID_STANDALONE_TOOLCHAIN +CMAKE_ANDROID_STL_TYPE +#]] diff --git a/shell/cmake/config.cmake b/shell/cmake/config.cmake new file mode 100644 index 000000000..aa0c67d40 --- /dev/null +++ b/shell/cmake/config.cmake @@ -0,0 +1,481 @@ +## config module +# +# get luserx0 to doc this shit or something, vars in all caps are to be exported as defs if they aren't in build.h already +# handle options for FEAT per platform, so rec isn't built for targets w.o one... +# *TODO* fix Android for build system in emu too, OS_LINUX is hardly fitting: prob works better as PLATFORM_ANDROID_{S,N}DK or something +# *TODO* setup git version like it's done in VS/make and configure header so --version works still +# *TODO* lots of other shit to improve build, add enabling/disabling libs/features, setting 3rd party libs as either built in static, dynamic or shared/system +# +# + + +set(ZBUILD Off) + + + +set(BUILD_LIBS OFF) ## Scope:Local If set will build libs { dreamcast, osd, ... } +set(BUILD_LIB_TYPE STATIC) ## Scope:Local If BUILD_LIBS is set, will use this as type of lib to use { STATIC, SHARED, MODULE (plugin) } *TODO* +set(BUILD_SHARED_LIBS OFF) ## Scope:CMAKE If type is not specified in add_library, use SHARED + + + + +## Build flags ## +# + +set(DC_PLATFORM_MASK 7) # Z: Uh, not a bitset +set(DC_PLATFORM_DREAMCAST 0) # /* Works, for the most part */ +set(DC_PLATFORM_DEV_UNIT 1) # /* This is missing hardware */ +set(DC_PLATFORM_NAOMI 2) # /* Works, for the most part */ +set(DC_PLATFORM_NAOMI2 3) # /* Needs to be done, 2xsh4 + 2xpvr + custom TNL */ +set(DC_PLATFORM_ATOMISWAVE 4) # /* Needs to be done, DC-like hardware with possibly more ram */ +set(DC_PLATFORM_HIKARU 5) # /* Needs to be done, 2xsh4, 2x aica , custom vpu */ +set(DC_PLATFORM_AURORA 6) # /* Needs to be done, Uses newer 300 mhz sh4 + 150 mhz pvr mbx SoC */ + + + +set(OS_WINDOWS 0x10000001) # HOST_OS +set(OS_LINUX 0x10000002) +set(OS_DARWIN 0x10000003) +set(OS_IOS 0x10000004) # todo: iOS != OS_DARWIN +set(OS_ANDROID 0x10000005) # todo: should be SYSTEM_ANDROID but ! OS_LINUX + +set(OS_UWP 0x10000011) +set(OS_NSW_HOS 0x80000001) +set(OS_PS4_BSD 0x80000002) + + + +set(CPU_X86 0x20000001) # HOST_CPU +set(CPU_X64 0x20000004) +set(CPU_ARM 0x20000002) +set(CPU_A64 0x20000008) +set(CPU_MIPS 0x20000003) +set(CPU_MIPS64 0x20000009) +set(CPU_PPC 0x20000006) +set(CPU_PPC64 0x20000007) +set(CPU_GENERIC 0x20000005) # used for pnacl, emscripten, etc + +set(DYNAREC_NONE 0x40000001) # FEAT_SHREC, FEAT_AREC, FEAT_DSPREC +set(DYNAREC_JIT 0x40000002) +set(DYNAREC_CPP 0x40000003) + +set(COMPILER_VC 0x30000001) # BUILD_COMPILER +set(COMPILER_GCC 0x30000002) +set(COMPILER_CLANG 0x30000002) +set(COMPILER_INTEL 0x30000002) + + + + + + + +## These default to host, but are used for cross so make sure not to contaminate +# +# CMAKE_SYSTEM ${CMAKE_SYSTEM_NAME}-${CMAKE_SYSTEM_VERSION}. +# CMAKE_SYSTEM_NAME CMAKE_HOST_SYSTEM_NAME uname -s Linux, Windows, and Darwin +# CMAKE_SYSTEM_VERSION CMAKE_HOST_SYSTEM_VERSION uname -r +# CMAKE_SYSTEM_PROCESSOR CMAKE_HOST_SYSTEM_PROCESSOR uname -p +# +# +# +# BOOL: CMAKE_HOST_UNIX CMAKE_HOST_WIN32 CMAKE_HOST_APPLE +# +# +# CMAKE_LIBRARY_ARCHITECTURE CMAKE__LIBRARY_ARCHITECTURE /lib/ +# +# + + + + +## strings are used to append to path/file names, and to filter multiple possibilities down to one +# AMD64/x86_64:x64, i*86:x86, ppc/powerpc[64][b|l]e:ppc[64] etc +# +if("${CMAKE_SYSTEM_PROCESSOR}" STREQUAL "i686") # todo: check MATCHES "i.86" ? + set(host_arch "x86") + set(HOST_CPU ${CPU_X86}) +# +elseif(("${CMAKE_SYSTEM_PROCESSOR}" STREQUAL "AMD64") OR + ("${CMAKE_SYSTEM_PROCESSOR}" STREQUAL "x86_64")) + set(host_arch "x64") + set(HOST_CPU ${CPU_X64}) +# +elseif("${CMAKE_SYSTEM_PROCESSOR}" MATCHES "aarch64") + set(host_arch "arm64") + set(HOST_CPU ${CPU_A64}) +# +elseif("${CMAKE_SYSTEM_PROCESSOR}" MATCHES "arm") + set(host_arch "arm") + set(HOST_CPU ${CPU_ARM}) +# +elseif("${CMAKE_SYSTEM_PROCESSOR}" MATCHES "ppc64") + set(host_arch "ppc64") + set(HOST_CPU ${CPU_PPC64}) +# +elseif("${CMAKE_SYSTEM_PROCESSOR}" MATCHES "ppc") # has to be done after ppc64 obv + set(host_arch "ppc") + set(HOST_CPU ${CPU_PPC}) +# +elseif("${CMAKE_SYSTEM_PROCESSOR}" MATCHES "mips64") # todo: check , r* names? + set(host_arch "mips64") + set(HOST_CPU ${CPU_MIPS64}) +# +elseif("${CMAKE_SYSTEM_PROCESSOR}" MATCHES "mips") # todo: check , r* names? + set(host_arch "mips") + set(HOST_CPU ${CPU_MIPS}) +# +else() + message("Warning: Unknown Host System Processor: \"${CMAKE_SYSTEM_PROCESSOR}\"") + set(host_arch "${CMAKE_SYSTEM_PROCESSOR}") + set(HOST_CPU ${CPU_GENERIC}) +endif() + + + + +string(TOLOWER ${CMAKE_SYSTEM_NAME} host_os) + +#message(" - testing cmake host_os: \"${host_os}\"") + +## HOST_* is not TARGET_* ;; change ndc-e internal naming it's wrong , then change this ;; + +if("android" STREQUAL "${host_os}" OR ANDROID) + set(HOST_OS ${OS_LINUX}) # *FIXME* we might have to keep as OS_LINUX or add to full cleanup list :| + +elseif("windowsstore" STREQUAL "${host_os}") + set(HOST_OS ${OS_UWP}) + set(HOST_CPU ${CPU_X64}) + +elseif(CMAKE_HOST_WIN32) + set(HOST_OS ${OS_WINDOWS}) + +elseif(CMAKE_HOST_APPLE) + + if("${host_arch}" MATCHES "arm") + set(HOST_OS ${OS_IOS}) + set(TARGET_IOS On) + add_definitions(-DTARGET_IPHONE -DTARGET_IOS) + else() + set(HOST_OS ${OS_DARWIN}) # todo ios check, check compiler/arch? + set(TARGET_OSX On) + add_definitions(-DTARGET_OSX) + endif() + +elseif(CMAKE_HOST_UNIX) # GP UNIX MUST BE AFTER OTHER UNIX'ish options such as APPLE , it matches both + + set(HOST_OS ${OS_LINUX}) # todo android check, just check android vars? +endif() + + + +#option(TARGET_NO_REC BOOL "") +#option(TARGET_NO_AREC BOOL "") +#option(TARGET_NO_JIT BOOL "") + + + +## Dynarec avail on x86,x64,arm and aarch64 in arm.32 compat +# +if((${HOST_CPU} EQUAL ${CPU_X86}) OR (${HOST_CPU} EQUAL ${CPU_X64}) OR + (${HOST_CPU} EQUAL ${CPU_ARM}) OR (${HOST_CPU} EQUAL ${CPU_A64})) +# + message("Dynarec Features Available") + + set(FEAT_SHREC ${DYNAREC_JIT}) + set(FEAT_AREC ${DYNAREC_NONE}) + set(FEAT_DSPREC ${DYNAREC_NONE}) +# +else() + set(FEAT_SHREC ${DYNAREC_CPP}) + set(FEAT_AREC ${DYNAREC_NONE}) + set(FEAT_DSPREC ${DYNAREC_NONE}) +endif() + +## Handle TARGET_* to FEAT_ *FIXME* stupid use one or the other and propogate : part of build cleanup , TARGET_ will only be for platform specifics and FEAT_ as OPTIONS +# +if(TARGET_NO_REC) + set(FEAT_SHREC ${DYNAREC_NONE}) + set(FEAT_AREC ${DYNAREC_NONE}) + set(FEAT_DSPREC ${DYNAREC_NONE}) +endif() + +if(TARGET_NO_AREC) + set(FEAT_SHREC ${DYNAREC_JIT}) + set(FEAT_AREC ${DYNAREC_NONE}) + set(FEAT_DSPREC ${DYNAREC_NONE}) +endif() + +if(TARGET_NO_JIT) + set(FEAT_SHREC ${DYNAREC_CPP}) + set(FEAT_AREC ${DYNAREC_NONE}) + set(FEAT_DSPREC ${DYNAREC_NONE}) +endif() + + + + + +######## Looks like something to delete, but if we're going to handle options here and NOT in libosd/lib* ######### + +# FindNativeCompilers() +## options BUILD_COMPILER { GCC, Clang, Intel, RealView? } + + +#set(CMAKE_C_COMPILER clang) +#set(CMAKE_C_COMPILER_TARGET ${triple}) +#set(CMAKE_CXX_COMPILER clang++) +#set(CMAKE_CXX_COMPILER_TARGET ${triple}) + + + +if(${HOST_OS} EQUAL ${OS_LINUX}) +# option SUPPORT_X11 +# option FEAT_HAS_NIXPROF +# option EMSCripten +endif() + +if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC") +# + set(BUILD_COMPILER ${COMPILER_VC}) + message("MSVC Platform: ${CMAKE_VS_PLATFORM_NAME}") + message("MSVC Toolset: ${CMAKE_VS_PLATFORM_TOOLSET}") + + + add_definitions(/D_CRT_SECURE_NO_WARNINGS /D_CRT_SECURE_CPP_OVERLOAD_STANDARD_NAMES=1) + + if("${HOST_OS}" STREQUAL "${OS_UWP}") + set(_CXX_FLAGS "/ZW ") + set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} /AppContainer") + endif() +# +elseif("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU") + set(BUILD_COMPILER ${COMPILER_GCC}) +# +elseif("${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang") # AppleClang ffs + set(BUILD_COMPILER ${COMPILER_CLANG}) +# +elseif("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Intel") + set(BUILD_COMPILER ${COMPILER_INTEL}) +# +else() + message("Unknown Compiler: ${CMAKE_CXX_COMPILER_ID}") +endif() + + + + +## Setup some common flags +# +if ((${BUILD_COMPILER} EQUAL ${COMPILER_VC}) OR + (${BUILD_COMPILER} EQUAL ${COMPILER_CLANG} AND ${HOST_OS} EQUAL ${OS_WINDOWS})) + + if((${HOST_CPU} EQUAL ${CPU_X64}) AND (${FEAT_SHREC} EQUAL ${DYNAREC_JIT})) # AND NOT "${NINJA}" STREQUAL "") + set(FEAT_SHREC ${DYNAREC_CPP}) + message("---x64 rec disabled for VC x64 via NINJA") + endif() + + add_definitions(/D_CRT_SECURE_NO_WARNINGS /D_CRT_SECURE_CPP_OVERLOAD_STANDARD_NAMES=1) + +if(${BUILD_COMPILER} EQUAL ${COMPILER_CLANG}) + add_definitions(/DXBYAK_NO_OP_NAMES /DTARGET_NO_OPENMP) #*FIXME* check openmp on clang-cl + remove_definitions(/U_HAS_STD_BYTE) + set(_CXX_FLAGS "/std:c++14") # /U_HAS_STD_BYTE not working, have to use c++14 not 17 :| + set(_C_FLAGS "-Wno-unused-function -Wno-unused-variable") +endif() + + +elseif ((${BUILD_COMPILER} EQUAL ${COMPILER_GCC}) OR + (${BUILD_COMPILER} EQUAL ${COMPILER_CLANG})) # AND NOT ${HOST_OS} EQUAL ${OS_WINDOWS})) + + + set(_C_FLAGS "-fno-operator-names") # or add_definitions(/DXBYAK_NO_OP_NAMES) + + + if(USE_32B OR TARGET_LINUX_X86) + set(_C_FLAGS "${_C_FLAGS} -m32") + endif() + + if((${HOST_CPU} EQUAL ${CPU_X86}) OR (${HOST_CPU} EQUAL ${CPU_X64})) + set(_C_FLAGS "${_C_FLAGS} -msse4") + + if(NOT CMAKE_HOST_APPLE) + set(_C_FLAGS "${_C_FLAGS} -fopenmp") + endif() + endif() # X86 family + + + set(_CXX_FLAGS "${_CXX_FLAGS} -std=c++17 -fcxx-exceptions") ## xbyak needs exceptions + + +endif() + + +set(_CXX_FLAGS "${_CXX_FLAGS} ${_C_FLAGS}") + + + +set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${_C_FLAGS}") +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${_CXX_FLAGS}") + + + +#if defined(TARGET_NAOMI) + #define DC_PLATFORM DC_PLATFORM_NAOMI + #undef TARGET_NAOMI +#endif + + + +#if defined(TARGET_NO_NIXPROF) +#define FEAT_HAS_NIXPROF 0 +#endif + +#if defined(TARGET_NO_COREIO_HTTP) +#define FEAT_HAS_COREIO_HTTP 0 +#endif + +#if defined(TARGET_SOFTREND) # need -fopenmp + #define FEAT_HAS_SOFTREND 1 +#endif + + +if (TARGET_NSW) # -DCMAKE_TOOLCHAIN_FILE=./cmake/devkitA64.cmake -DTARGET_NSW=ON + set(HOST_OS ${OS_NSW_HOS}) + + message(" DEVKITA64: ${DEVKITA64} ") + message("HOST_OS ${HOST_OS}") + + add_definitions(-D__SWITCH__ -DGLES -DMESA_EGL_NO_X11_HEADERS) + add_definitions(-DTARGET_NO_THREADS -DTARGET_NO_EXCEPTIONS -DTARGET_NO_NIXPROF) + add_definitions(-DTARGET_NO_COREIO_HTTP -DTARGET_NO_WEBUI -UTARGET_SOFTREND) + add_definitions(-D_GLIBCXX_USE_C99_MATH_TR1 -D_LDBL_EQ_DBL) + +endif() + +if (TARGET_PS4) # -DCMAKE_TOOLCHAIN_FILE=./cmake/{ps4sdk,clang_scei}.cmake -DTARGET_PS4=ON + set(HOST_OS ${OS_PS4_BSD}) + message("HOST_OS ${HOST_OS}") + + + add_definitions(-DPS4 -DTARGET_PS4 -DTARGET_BSD -D__ORBIS__ -DGLES -DMESA_EGL_NO_X11_HEADERS) ## last needed for __unix__ on eglplatform.h + add_definitions(-DTARGET_NO_THREADS -DTARGET_NO_EXCEPTIONS -DTARGET_NO_NIXPROF) + add_definitions(-DTARGET_NO_COREIO_HTTP -DTARGET_NO_WEBUI -UTARGET_SOFTREND) + + + message("*******FIXME******** LARGE PAGES !!") +endif() + + + +if(ZBUILD) + set(DEBUG_CMAKE ON) + add_definitions(-D_Z_) # Get rid of some warnings and internal dev testing + + if(NOT TARGET_PS4 AND NOT TARGET_NSW AND + NOT TARGET_OSX AND NOT TARGET_IOS ) + set(USE_QT ON) + endif() +endif() + + + +# configure options for osd/ui +# osd_default, osd_qt +# ui_default, ui_sdl, ui_qt +# USE_NATIVE , USE_SDL , USE_QT -- these (can) define multiple + +option(USE_QT False "Use Qt5 for UI and support OS Deps.") + + + + +#option TARGET_NO_WEBUI + + + + + +#option(BUILD_TESTS "Build tests" OFF) # todo: luserx0 this is your arena, you want tests add em + + +add_definitions(-DCMAKE_BUILD) + + + + +add_definitions(-DHOST_OS=${HOST_OS}) +add_definitions(-DHOST_CPU=${HOST_CPU}) + +add_definitions(-DFEAT_AREC=${FEAT_AREC}) +add_definitions(-DFEAT_SHREC=${FEAT_SHREC}) +add_definitions(-DFEAT_DSPREC=${FEAT_DSPREC}) + +add_definitions(-DBUILD_COMPILER=${BUILD_COMPILER}) + +add_definitions(-DTARGET_NO_WEBUI) +add_definitions(-DDEF_CONSOLE) + + +set(RE_CMAKE_CONFIGURED 1) +#add_definitions(-D=${}) + + + + + + + + + + + + + + +### These were for internal testing, don't use ### +# +function(CpuIs CpuType Res) + set(${Res} OFF PARENT_SCOPE) + if (${HOST_CPU} EQUAL ${CpuType}) + set(${Res} ON PARENT_SCOPE) + endif() +endfunction() + +macro(CpuIsX86 res) + CpuIs(CPU_X86 ${res}) +endmacro() + +macro(CpuIsX64 res) + CpuIs(CPU_X64 ${res}) +endmacro() + +macro(CpuIsARM res) + CpuIs(CPU_ARM ${res}) +endmacro() + +macro(CpuIsA64 res) + CpuIs(CPU_A64 ${res}) +endmacro() + +macro(CpuIsPPC res) + CpuIs(CPU_PPC ${res}) +endmacro() + +macro(CpuIsPPC64 res) + CpuIs(CPU_PPC64 ${res}) +endmacro() + +macro(CpuIsMIPS res) + CpuIs(CPU_MIPS ${res}) +endmacro() + +macro(CpuIsMIPS64 res) + CpuIs(CPU_MIPS64 ${res}) +endmacro() + + + + + diff --git a/shell/cmake/devkitA64.cmake b/shell/cmake/devkitA64.cmake new file mode 100644 index 000000000..3eb80ff24 --- /dev/null +++ b/shell/cmake/devkitA64.cmake @@ -0,0 +1,41 @@ +## devkitA64.cmake - devkitpro A64 cross-compile +# +set(CMAKE_SYSTEM_NAME Linux) # this one is important // Add Platform/switch to use this name ... +set(CMAKE_SYSTEM_PROCESSOR aarch64) + +set(CMAKE_SYSTEM_VERSION 1) # this one not so much + + +set(DEVKITPRO $ENV{DEVKITPRO}) +set(DEVKITA64 $ENV{DEVKITA64}) + + +if ("" STREQUAL "${DEVKITPRO}") + set(DEVKITA64 "/opt/devkitpro") +endif() + +if ("" STREQUAL "${DEVKITA64}") + set(DEVKITA64 ${DEVKITPRO}/devkitA64) +endif() + + +## specify the cross compiler +# +set(CMAKE_C_COMPILER ${DEVKITA64}/bin/aarch64-none-elf-gcc) +set(CMAKE_CXX_COMPILER ${DEVKITA64}/bin/aarch64-none-elf-g++) + + +set(CMAKE_FIND_ROOT_PATH ${DEVKITA64}) # where is the target environment + +set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) # search for programs in the build host directories +set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) # for libraries and headers in the target directories +set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) + + + + +include_directories(${DEVKITPRO}/libnx/include) + + + +set(TARGET_NSW ON) diff --git a/shell/cmake/ps4sdk.cmake b/shell/cmake/ps4sdk.cmake new file mode 100644 index 000000000..d8cac168f --- /dev/null +++ b/shell/cmake/ps4sdk.cmake @@ -0,0 +1,130 @@ +## ps4sdk.cmake - devkitpro A64 cross-compile +# +set(CMAKE_SYSTEM_NAME FreeBSD) # this one is important +set(CMAKE_SYSTEM_PROCESSOR x86_64) +set(CMAKE_SYSTEM_VERSION 9) # this one not so much + + + + +set(TARGET_PS4 ON) +set(TARGET_BSD ON) + + + +### This shit is very WIP ### +# +## TODO: Check for + + +set(PS4SDK $ENV{PS4SDK}) +set(SCESDK $ENV{SCESDK}) + +set(USE_SCE ON) +set(PS4_PKG ON) + +if(PS4_PKG) + add_definitions(-DPS4_PKG) +endif() + + + +if ("" STREQUAL "${PS4SDK}") + if ("Windows" STREQUAL "${CMAKE_HOST_SYSTEM_NAME}") + set(PS4SDK "C:/Dev/SDK/PS4") + else() + set(PS4SDK "/opt/ps4") + endif() +endif() + + + +set(TAUON_SDK ${PS4SDK}/tauon) + + + +if(USE_SCE) +# + set(PS4SDK ${PS4SDK}/SCE/PS4SDK) + + set(PS4HOST ${PS4SDK}/host_tools) + set(PS4TARGET ${PS4SDK}/target) + + set(toolPrefix "orbis-") + set(toolSuffix ".exe") + + set(CMAKE_C_COMPILER ${PS4HOST}/bin/${toolPrefix}clang${toolSuffix}) + set(CMAKE_CXX_COMPILER ${PS4HOST}/bin/${toolPrefix}clang++${toolSuffix}) + + set(CMAKE_FIND_ROOT_PATH ${PS4TARGET}) # where is the target environment + + + + set (PS4_inc_dirs + ${TAUON_SDK}/include + ${PS4TARGET}/include + ${PS4TARGET}/include_common + ) + +# set (PS4_link_dirs +# "${PS4TARGET}/lib" +# "${PS4TARGET}/tauon/lib" +# ) + + +#LDFLAGS += -L $(TAUON_SDK_DIR)/lib -L $(SCE_ORBIS_SDK_DIR)/target/lib + set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -s -Wl,--addressing=non-aslr,--strip-unused-data ") + set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -L ${TAUON_SDK}/lib -L ${PS4TARGET}/lib") + + message("CMAKE_EXE_LINKER_FLAGS ${CMAKE_EXE_LINKER_FLAGS}") +# +else() +# + set(triple "x86_64-scei-ps4") + + set(CMAKE_C_COMPILER_TARGET ${triple}) + set(CMAKE_CXX_COMPILER_TARGET ${triple}) + + set(CMAKE_C_COMPILER clang) + set(CMAKE_CXX_COMPILER clang++) + + + set (PS4_inc_dirs + ${TAUON_SDK}/include + + ${PS4SDK}/include + ${PS4SDK}/tauon/include + ) + +# set (PS4_link_dirs +# "${PS4SDK}/lib" +# "${PS4SDK}/tauon/lib" +# ) + + + set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -s -Wl,--addressing=non-aslr,--strip-unused-data -L${TAUON_SDK}/lib") +# +endif() + + + + +set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) # search for programs in the build host directories +set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY BOTH) # for libraries and headers in the target directories +set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE BOTH) + + +include_directories(${PS4_inc_dirs}) + + + + + ### Add a helper to add libSce PREFIX and [_tau]*_stub[_weak]*.a SUFFIX + # +link_libraries( + kernel_tau_stub_weak SceSysmodule_tau_stub_weak SceSystemService_stub_weak SceSystemService_tau_stub_weak SceShellCoreUtil_tau_stub_weak ScePigletv2VSH_tau_stub_weak kernel_util + ScePad_stub_weak SceNet_stub_weak SceCommonDialog_stub_weak ScePosix_stub_weak +) + + + diff --git a/shell/reicast.vcxproj b/shell/reicast.vcxproj index 2f10bff9e..3d10f789a 100644 --- a/shell/reicast.vcxproj +++ b/shell/reicast.vcxproj @@ -574,7 +574,7 @@ Win32Proj reicast reicast - 10.0.17134.0 + 10.0.17763.0 From b6c234b71b1586e66f9c14f26e94d16f80f2a999 Mon Sep 17 00:00:00 2001 From: david miller Date: Fri, 12 Apr 2019 17:19:40 -0400 Subject: [PATCH 247/319] revert to using strcasecmp for other platforms --- CMakeSettings.json | 4 ++++ core/types.h | 4 ++-- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/CMakeSettings.json b/CMakeSettings.json index 8ddfb307d..4e5b0d73c 100644 --- a/CMakeSettings.json +++ b/CMakeSettings.json @@ -86,6 +86,8 @@ } ] }, + + // Console SDK's { "name": "PS4 SDK", "generator": "Ninja", @@ -114,6 +116,8 @@ "buildCommandArgs": "-v", "ctestCommandArgs": "" }, + + // UWP and VS Gen (temp?) { "name": "uwp-x64-Release", "generator": "Visual Studio 15 2017 Win64", diff --git a/core/types.h b/core/types.h index 4f3fb23b8..fbcb74c7c 100644 --- a/core/types.h +++ b/core/types.h @@ -518,10 +518,10 @@ typedef union -#if COMPILER_VC==BUILD_COMPILER +#if BUILD_COMPILER==COMPILER_VC || BUILD_COMPILER==COMPILER_CLANG && WIN32 #pragma warning( disable : 4127 4996 /*4244*/) #else -#define stricmp _stricmp // ISO , was strcasecmp +#define stricmp strcasecmp #endif #ifndef STRIP_TEXT From 68ee192ad4bb3c178e6200490cfc3235fa7b8e1c Mon Sep 17 00:00:00 2001 From: david miller Date: Fri, 12 Apr 2019 18:08:36 -0400 Subject: [PATCH 248/319] compiler def. fixes --- core/build.h | 4 ++-- shell/cmake/config.cmake | 23 +++++++++++------------ 2 files changed, 13 insertions(+), 14 deletions(-) diff --git a/core/build.h b/core/build.h index 4dc84a222..800e5a1e4 100755 --- a/core/build.h +++ b/core/build.h @@ -154,8 +154,8 @@ //BUILD_COMPILER #define COMPILER_VC 0x30000001 #define COMPILER_GCC 0x30000002 -#define COMPILER_CLANG 0x30000002 -#define COMPILER_INTEL 0x30000002 +#define COMPILER_CLANG 0x30000003 +#define COMPILER_INTEL 0x30000004 //FEAT_SHREC, FEAT_AREC, FEAT_DSPREC #define DYNAREC_NONE 0x40000001 diff --git a/shell/cmake/config.cmake b/shell/cmake/config.cmake index aa0c67d40..ad4b53b9d 100644 --- a/shell/cmake/config.cmake +++ b/shell/cmake/config.cmake @@ -62,8 +62,8 @@ set(DYNAREC_CPP 0x40000003) set(COMPILER_VC 0x30000001) # BUILD_COMPILER set(COMPILER_GCC 0x30000002) -set(COMPILER_CLANG 0x30000002) -set(COMPILER_INTEL 0x30000002) +set(COMPILER_CLANG 0x30000003) +set(COMPILER_INTEL 0x30000004) @@ -265,11 +265,10 @@ endif() - ## Setup some common flags # if ((${BUILD_COMPILER} EQUAL ${COMPILER_VC}) OR - (${BUILD_COMPILER} EQUAL ${COMPILER_CLANG} AND ${HOST_OS} EQUAL ${OS_WINDOWS})) + (${BUILD_COMPILER} EQUAL ${COMPILER_CLANG}) AND (${HOST_OS} STREQUAL ${OS_WINDOWS})) if((${HOST_CPU} EQUAL ${CPU_X64}) AND (${FEAT_SHREC} EQUAL ${DYNAREC_JIT})) # AND NOT "${NINJA}" STREQUAL "") set(FEAT_SHREC ${DYNAREC_CPP}) @@ -278,12 +277,12 @@ if ((${BUILD_COMPILER} EQUAL ${COMPILER_VC}) OR add_definitions(/D_CRT_SECURE_NO_WARNINGS /D_CRT_SECURE_CPP_OVERLOAD_STANDARD_NAMES=1) -if(${BUILD_COMPILER} EQUAL ${COMPILER_CLANG}) - add_definitions(/DXBYAK_NO_OP_NAMES /DTARGET_NO_OPENMP) #*FIXME* check openmp on clang-cl - remove_definitions(/U_HAS_STD_BYTE) - set(_CXX_FLAGS "/std:c++14") # /U_HAS_STD_BYTE not working, have to use c++14 not 17 :| - set(_C_FLAGS "-Wno-unused-function -Wno-unused-variable") -endif() + if(${BUILD_COMPILER} EQUAL ${COMPILER_CLANG}) + add_definitions(/DXBYAK_NO_OP_NAMES /DTARGET_NO_OPENMP) #*FIXME* check openmp on clang-cl + remove_definitions(/U_HAS_STD_BYTE) + set(_CXX_FLAGS "/std:c++14") # /U_HAS_STD_BYTE not working, have to use c++14 not 17 :| + set(_C_FLAGS "-Wno-unused-function -Wno-unused-variable") + endif() elseif ((${BUILD_COMPILER} EQUAL ${COMPILER_GCC}) OR @@ -316,8 +315,8 @@ set(_CXX_FLAGS "${_CXX_FLAGS} ${_C_FLAGS}") -set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${_C_FLAGS}") -set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${_CXX_FLAGS}") +set(CMAKE_C_FLAGS " ${_C_FLAGS}") # ${CMAKE_C_FLAGS} -- these hold default VC flags for non VC Build ? +set(CMAKE_CXX_FLAGS " ${_CXX_FLAGS}") # ${CMAKE_CXX_FLAGS} From 803cad95f97bccad675db8d32e2a8951f7dae69a Mon Sep 17 00:00:00 2001 From: david miller Date: Fri, 12 Apr 2019 18:13:09 -0400 Subject: [PATCH 249/319] gcc flags & misc --- core/hw/modem/dns.cpp | 2 +- core/stdclass.cpp | 2 +- core/types.h | 2 +- shell/cmake/config.cmake | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/core/hw/modem/dns.cpp b/core/hw/modem/dns.cpp index 1f1b3283e..5eaaf9c8e 100644 --- a/core/hw/modem/dns.cpp +++ b/core/hw/modem/dns.cpp @@ -20,7 +20,7 @@ */ #include "types.h" -#if BUILD_COMPILER!=COMPILER_VC && (BUILD_COMPILER!=COMPILER_CLANG || !WIN32) +#if BUILD_COMPILER!=COMPILER_VC && (BUILD_COMPILER!=COMPILER_CLANG || !defined(WIN32)) #include #include diff --git a/core/stdclass.cpp b/core/stdclass.cpp index f9acec72f..0ac32fb1d 100644 --- a/core/stdclass.cpp +++ b/core/stdclass.cpp @@ -6,7 +6,7 @@ #include "cfg/cfg.h" -#if BUILD_COMPILER==COMPILER_VC || (WIN32 && BUILD_COMPILER==COMPILER_CLANG) +#if BUILD_COMPILER==COMPILER_VC || BUILD_COMPILER==COMPILER_CLANG && defined(WIN32) #include #include #define access _access diff --git a/core/types.h b/core/types.h index fbcb74c7c..ac087590e 100644 --- a/core/types.h +++ b/core/types.h @@ -518,7 +518,7 @@ typedef union -#if BUILD_COMPILER==COMPILER_VC || BUILD_COMPILER==COMPILER_CLANG && WIN32 +#if BUILD_COMPILER==COMPILER_VC || BUILD_COMPILER==COMPILER_CLANG && defined(WIN32) #pragma warning( disable : 4127 4996 /*4244*/) #else #define stricmp strcasecmp diff --git a/shell/cmake/config.cmake b/shell/cmake/config.cmake index ad4b53b9d..ac4eb3238 100644 --- a/shell/cmake/config.cmake +++ b/shell/cmake/config.cmake @@ -305,7 +305,7 @@ elseif ((${BUILD_COMPILER} EQUAL ${COMPILER_GCC}) OR endif() # X86 family - set(_CXX_FLAGS "${_CXX_FLAGS} -std=c++17 -fcxx-exceptions") ## xbyak needs exceptions + set(_CXX_FLAGS "${_CXX_FLAGS} -std=c++17") # -fcxx-exceptions") ## xbyak needs exceptions endif() From 6539b8c247d867bb0c4ba4480705d21a13c3729f Mon Sep 17 00:00:00 2001 From: david miller Date: Fri, 12 Apr 2019 18:24:11 -0400 Subject: [PATCH 250/319] _mkdir guard --- core/stdclass.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/stdclass.cpp b/core/stdclass.cpp index 0ac32fb1d..006255100 100644 --- a/core/stdclass.cpp +++ b/core/stdclass.cpp @@ -142,7 +142,7 @@ string get_game_dir() bool make_directory(const string& path) { -#ifdef WIN32 +#ifdef BUILD_COMPILER==COMPILER_VC || (BUILD_COMPILER==COMPILER_CLANG && defined(WIN32)) return _mkdir(path.c_str()) == 0; #else return mkdir(path.c_str(), 0755) == 0; From 3157520deeb6d47cdafa2e454a4f45ac3e265472 Mon Sep 17 00:00:00 2001 From: david miller Date: Fri, 12 Apr 2019 19:15:31 -0400 Subject: [PATCH 251/319] mingw64 fixes --- CMakeSettings.json | 4 ++-- core/build.h | 2 ++ core/hw/modem/dns.cpp | 2 +- core/stdclass.cpp | 10 +++++++--- core/types.h | 2 +- shell/cmake/config.cmake | 4 ++-- 6 files changed, 15 insertions(+), 9 deletions(-) diff --git a/CMakeSettings.json b/CMakeSettings.json index 4e5b0d73c..7fbe9b101 100644 --- a/CMakeSettings.json +++ b/CMakeSettings.json @@ -149,7 +149,7 @@ { "environments": [ { - "MINGW64_ROOT": "C:\\msys64\\mingw64", + //"MINGW64_ROOT": "C:\\msys64\\mingw64", "BIN_ROOT": "${env.MINGW64_ROOT}\\bin", "FLAVOR": "x86_64-w64-mingw32", "TOOLSET_VERSION": "7.3.0", @@ -184,7 +184,7 @@ { "environments": [ { - "MINGW64_ROOT": "C:\\msys64\\mingw64", + //"MINGW64_ROOT": "C:\\msys64\\mingw64", "BIN_ROOT": "${env.MINGW64_ROOT}\\bin", "FLAVOR": "x86_64-w64-mingw32", "TOOLSET_VERSION": "7.3.0", diff --git a/core/build.h b/core/build.h index 800e5a1e4..57a00a46c 100755 --- a/core/build.h +++ b/core/build.h @@ -315,6 +315,8 @@ // Compiler Related +#define COMPILER_VC_OR_CLANG_WIN32 ((BUILD_COMPILER == COMPILER_VC) || (BUILD_COMPILER == COMPILER_CLANG) && defined(WIN32)) + #if BUILD_COMPILER!=COMPILER_VC #define ATTR_USED __attribute__((used)) #define ATTR_UNUSED __attribute__((used)) diff --git a/core/hw/modem/dns.cpp b/core/hw/modem/dns.cpp index 5eaaf9c8e..95c8657c4 100644 --- a/core/hw/modem/dns.cpp +++ b/core/hw/modem/dns.cpp @@ -149,4 +149,4 @@ char *read_name(char *reader, char *buffer, int *count) return name; } -#endif // BUILD_COMPILER!=COMPILER_VC \ No newline at end of file +#endif // !COMPILER_VC_OR_CLANG_WIN32 \ No newline at end of file diff --git a/core/stdclass.cpp b/core/stdclass.cpp index 006255100..ca773ee24 100644 --- a/core/stdclass.cpp +++ b/core/stdclass.cpp @@ -6,7 +6,7 @@ #include "cfg/cfg.h" -#if BUILD_COMPILER==COMPILER_VC || BUILD_COMPILER==COMPILER_CLANG && defined(WIN32) +#if COMPILER_VC_OR_CLANG_WIN32 #include #include #define access _access @@ -142,8 +142,12 @@ string get_game_dir() bool make_directory(const string& path) { -#ifdef BUILD_COMPILER==COMPILER_VC || (BUILD_COMPILER==COMPILER_CLANG && defined(WIN32)) - return _mkdir(path.c_str()) == 0; +#if COMPILER_VC_OR_CLANG_WIN32 +#define mkdir _mkdir +#endif + +#ifdef _WIN32 + return mkdir(path.c_str()) == 0; #else return mkdir(path.c_str(), 0755) == 0; #endif diff --git a/core/types.h b/core/types.h index ac087590e..771d758e6 100644 --- a/core/types.h +++ b/core/types.h @@ -518,7 +518,7 @@ typedef union -#if BUILD_COMPILER==COMPILER_VC || BUILD_COMPILER==COMPILER_CLANG && defined(WIN32) +#if COMPILER_VC_OR_CLANG_WIN32 #pragma warning( disable : 4127 4996 /*4244*/) #else #define stricmp strcasecmp diff --git a/shell/cmake/config.cmake b/shell/cmake/config.cmake index ac4eb3238..1abf2258a 100644 --- a/shell/cmake/config.cmake +++ b/shell/cmake/config.cmake @@ -289,7 +289,7 @@ elseif ((${BUILD_COMPILER} EQUAL ${COMPILER_GCC}) OR (${BUILD_COMPILER} EQUAL ${COMPILER_CLANG})) # AND NOT ${HOST_OS} EQUAL ${OS_WINDOWS})) - set(_C_FLAGS "-fno-operator-names") # or add_definitions(/DXBYAK_NO_OP_NAMES) + set(_C_FLAGS "-fno-operator-names -fpermissive") # or add_definitions(/DXBYAK_NO_OP_NAMES) if(USE_32B OR TARGET_LINUX_X86) @@ -305,7 +305,7 @@ elseif ((${BUILD_COMPILER} EQUAL ${COMPILER_GCC}) OR endif() # X86 family - set(_CXX_FLAGS "${_CXX_FLAGS} -std=c++17") # -fcxx-exceptions") ## xbyak needs exceptions + set(_CXX_FLAGS "${_CXX_FLAGS} -std=c++11") # -fcxx-exceptions") ## xbyak needs exceptions endif() From a1df35d13d0f2c288bb7b23960eb4ae7166dead2 Mon Sep 17 00:00:00 2001 From: david miller Date: Fri, 12 Apr 2019 19:42:14 -0400 Subject: [PATCH 252/319] cmake cleanup --- CMakeLists.txt | 38 ++++++++++++------------- shell/cmake/config.cmake | 60 +--------------------------------------- 2 files changed, 19 insertions(+), 79 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 49a9a1138..9a757f3d0 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -12,24 +12,10 @@ set(CMAKE_VERBOSE_MAKEFILE ON) set(CMAKE_INCLUDE_CURRENT_DIR ON) -## built-in cmake modules # -# -#include(CheckIncludeFiles) -#include(CheckFunctionExists) -#include(CheckCSourceCompiles) - -#set(CMAKE_BINARY_DIR ${CMAKE_CURRENT_BINARY_DIR}/bin) -#set(LIBRARY_OUTPUT_PATH ${CMAKE_CURRENT_BINARY_DIR}) -#set(EXECUTABLE_OUTPUT_PATH ${CMAKE_CURRENT_BINARY_DIR}) - - - set(reicast_root_path "${CMAKE_CURRENT_SOURCE_DIR}") set(reicast_core_path "${reicast_root_path}/core") set(reicast_shell_path "${reicast_root_path}/shell") -include_directories ("${reicast_core_path}") - list(APPEND CMAKE_MODULE_PATH "${reicast_shell_path}/cmake") @@ -77,7 +63,6 @@ set(core_SRCS ${reios_SRCS} ${imgread_SRCS} ${profiler_SRCS} -# ${archive_SRCS} ${d_core}/archive/archive.cpp ${d_core}/archive/archive.h ${d_core}/nullDC.cpp ${d_core}/stdclass.cpp @@ -85,6 +70,10 @@ set(core_SRCS ${d_core}/serialize.cpp ) + +if(${BUILD_COMPILER} EQUAL ${COMPILER_GCC}) # Add Clang if NOT WIN32 *FIXME* + list(APPEND core_SRCS ${archive_SRCS}) +endif() if(${FEAT_SHREC} EQUAL ${DYNAREC_JIT}) # @@ -149,9 +138,6 @@ set(pico_SRCS ${picoModS} ${picoStkS}) set(deps_SRCS ${lz_SRCS} -# ${lzip_SRCS} -# ${lzma_SRCS} -# ${pico_SRCS} ${lpng_SRCS} ${lelf_SRCS} ${chdr_SRCS} @@ -166,7 +152,13 @@ set(deps_SRCS ${xbyak_H} ) - +if(${BUILD_COMPILER} EQUAL ${COMPILER_GCC}) # Add Clang if NOT WIN32 *FIXME* + list(APPEND deps_SRCS + ${lzip_SRCS} + ${lzma_SRCS} + ${pico_SRCS} + ) +endif() ### libosd.cmake ################################################################################ @@ -187,7 +179,7 @@ if (${HOST_OS} EQUAL ${OS_WINDOWS}) list(APPEND osd_SRCS ${d_aout}/audiobackend_directsound.cpp) - link_libraries(Dsound.lib winmm.lib) + link_libraries(dsound.lib winmm.lib xinput.lib wsock32.lib opengl32.lib) elseif (${HOST_OS} EQUAL ${OS_LINUX} OR ${HOST_OS} EQUAL ${OS_ANDROID}) @@ -247,6 +239,12 @@ endif() +## + +include_directories ("${reicast_core_path}") + +add_definitions(-D_7ZIP_ST -DCHD5_LZMA) + set(reicast_SRCS ${core_SRCS} ${deps_SRCS} ${osd_SRCS}) diff --git a/shell/cmake/config.cmake b/shell/cmake/config.cmake index 1abf2258a..d1dfd59e4 100644 --- a/shell/cmake/config.cmake +++ b/shell/cmake/config.cmake @@ -287,10 +287,6 @@ if ((${BUILD_COMPILER} EQUAL ${COMPILER_VC}) OR elseif ((${BUILD_COMPILER} EQUAL ${COMPILER_GCC}) OR (${BUILD_COMPILER} EQUAL ${COMPILER_CLANG})) # AND NOT ${HOST_OS} EQUAL ${OS_WINDOWS})) - - - set(_C_FLAGS "-fno-operator-names -fpermissive") # or add_definitions(/DXBYAK_NO_OP_NAMES) - if(USE_32B OR TARGET_LINUX_X86) set(_C_FLAGS "${_C_FLAGS} -m32") @@ -305,7 +301,7 @@ elseif ((${BUILD_COMPILER} EQUAL ${COMPILER_GCC}) OR endif() # X86 family - set(_CXX_FLAGS "${_CXX_FLAGS} -std=c++11") # -fcxx-exceptions") ## xbyak needs exceptions + set(_CXX_FLAGS "${_CXX_FLAGS} -fno-operator-names -fpermissive -std=c++11") # -fcxx-exceptions") ## xbyak needs exceptions endif() @@ -424,57 +420,3 @@ set(RE_CMAKE_CONFIGURED 1) - - - - - - - - - -### These were for internal testing, don't use ### -# -function(CpuIs CpuType Res) - set(${Res} OFF PARENT_SCOPE) - if (${HOST_CPU} EQUAL ${CpuType}) - set(${Res} ON PARENT_SCOPE) - endif() -endfunction() - -macro(CpuIsX86 res) - CpuIs(CPU_X86 ${res}) -endmacro() - -macro(CpuIsX64 res) - CpuIs(CPU_X64 ${res}) -endmacro() - -macro(CpuIsARM res) - CpuIs(CPU_ARM ${res}) -endmacro() - -macro(CpuIsA64 res) - CpuIs(CPU_A64 ${res}) -endmacro() - -macro(CpuIsPPC res) - CpuIs(CPU_PPC ${res}) -endmacro() - -macro(CpuIsPPC64 res) - CpuIs(CPU_PPC64 ${res}) -endmacro() - -macro(CpuIsMIPS res) - CpuIs(CPU_MIPS ${res}) -endmacro() - -macro(CpuIsMIPS64 res) - CpuIs(CPU_MIPS64 ${res}) -endmacro() - - - - - From aa8a922b6241e5b431a97f1317b5d42862feb5dc Mon Sep 17 00:00:00 2001 From: Braden Farmer Date: Tue, 16 Apr 2019 23:38:33 -0600 Subject: [PATCH 253/319] Android: remove duplicate intent filters The latest reicast builds displayed a redundant second launcher icon in the app drawer, due to a change in #1548. This fixes the issue by removing the duplicate intent filters causing the redundant icon to display. --- .../reicast/src/dreamcast/AndroidManifest.xml | 52 +--------------- .../reicast/src/main/AndroidManifest.xml | 10 +--- .../reicast/src/naomi/AndroidManifest.xml | 60 +------------------ 3 files changed, 5 insertions(+), 117 deletions(-) diff --git a/shell/android-studio/reicast/src/dreamcast/AndroidManifest.xml b/shell/android-studio/reicast/src/dreamcast/AndroidManifest.xml index 112f56962..d0a0cfb3b 100644 --- a/shell/android-studio/reicast/src/dreamcast/AndroidManifest.xml +++ b/shell/android-studio/reicast/src/dreamcast/AndroidManifest.xml @@ -3,55 +3,7 @@ xmlns:tools="http://schemas.android.com/tools"> - - - - - - - - - - - - - - - - + android:name="com.reicast.emulator.NativeGLActivity"/> @@ -104,4 +56,4 @@ - \ No newline at end of file + diff --git a/shell/android-studio/reicast/src/main/AndroidManifest.xml b/shell/android-studio/reicast/src/main/AndroidManifest.xml index ac89e2bb2..bc93a562f 100644 --- a/shell/android-studio/reicast/src/main/AndroidManifest.xml +++ b/shell/android-studio/reicast/src/main/AndroidManifest.xml @@ -53,14 +53,8 @@ - - - - - - - + android:screenOrientation="sensorLandscape" + android:exported="true"/> diff --git a/shell/android-studio/reicast/src/naomi/AndroidManifest.xml b/shell/android-studio/reicast/src/naomi/AndroidManifest.xml index 32fceb16c..a1a4c24b2 100644 --- a/shell/android-studio/reicast/src/naomi/AndroidManifest.xml +++ b/shell/android-studio/reicast/src/naomi/AndroidManifest.xml @@ -3,65 +3,7 @@ android:name="com.reicast.emulator.Emulator"> - - - - - - - - - - - - - - - - - - + android:name="com.reicast.emulator.NativeGLActivity"/> From 0a3c361da2adce112f43964bd86ae6126d81dd34 Mon Sep 17 00:00:00 2001 From: "Christoph \"baka0815\" Schwerdtfeger" Date: Wed, 24 Apr 2019 21:41:38 +0200 Subject: [PATCH 254/319] AUDIO: Allow backend specific settings --- core/nullDC.cpp | 20 ++++++ core/oslib/audiobackend_alsa.cpp | 71 ++++++++++++++++++- core/oslib/audiobackend_omx.cpp | 3 +- core/oslib/audiobackend_oss.cpp | 3 +- core/oslib/audiobackend_pulseaudio.cpp | 3 +- core/oslib/audiostream.h | 24 +++++++ core/rend/gui.cpp | 53 +++++++++++++- core/types.h | 8 ++- .../reicast/src/main/jni/src/Android.cpp | 3 +- 9 files changed, 181 insertions(+), 7 deletions(-) diff --git a/core/nullDC.cpp b/core/nullDC.cpp index 2f673fc5f..1deeebad7 100755 --- a/core/nullDC.cpp +++ b/core/nullDC.cpp @@ -721,6 +721,26 @@ void SaveSettings() cfgSaveBool("config", "aica.NoBatch", settings.aica.NoBatch); cfgSaveBool("config", "aica.NoSound", settings.aica.NoSound); cfgSaveStr("audio", "backend", settings.audio.backend.c_str()); + + // Write backend specific settings + // std::map> + for (std::map>::iterator it = settings.audio.options.begin(); it != settings.audio.options.end(); ++it) + { + + std::pair> p = (std::pair>)*it; + std::string section = p.first; + std::map options = p.second; + + for (std::map::iterator it2 = options.begin(); it2 != options.end(); ++it2) + { + std::pair p2 = (std::pair)*it2; + std::string key = p2.first; + std::string val = p2.second; + + cfgSaveStr(section.c_str(), key.c_str(), val.c_str()); + } + } + cfgSaveBool("config", "rend.WideScreen", settings.rend.WideScreen); cfgSaveBool("config", "rend.ShowFPS", settings.rend.ShowFPS); if (!rtt_to_buffer_game || !settings.rend.RenderToTextureBuffer) diff --git a/core/oslib/audiobackend_alsa.cpp b/core/oslib/audiobackend_alsa.cpp index 2ee230976..9effbcb6d 100644 --- a/core/oslib/audiobackend_alsa.cpp +++ b/core/oslib/audiobackend_alsa.cpp @@ -175,12 +175,81 @@ static void alsa_term() snd_pcm_close(handle); } +std::vector alsa_get_devicelist() +{ + std::vector result; + + char **hints; + int err = snd_device_name_hint(-1, "pcm", (void***)&hints); + + // Error initializing ALSA + if (err != 0) + return result; + + + char** n = hints; + while (*n != NULL) + { + // Get the type (NULL/Input/Output) + char *type = snd_device_name_get_hint(*n, "IOID"); + char *name = snd_device_name_get_hint(*n, "NAME"); + + if (name != NULL) + { + // We only want output or special devices (like "default" or "pulse") + // TODO Only those with type == NULL? + if (type == NULL || strcmp(type, "Output") == 0) + { + // TODO Check if device works (however we need to hash the resulting list then) + /*snd_pcm_t *handle; + int rc = snd_pcm_open(&handle, name, SND_PCM_STREAM_PLAYBACK, 0); + + if (rc == 0) + { + result.push_back(name); + snd_pcm_close(handle); + } + */ + + result.push_back(name); + } + + } + + if (type != NULL) + free(type); + + if (name != NULL) + free(name); + + n++; + } + + snd_device_name_free_hint((void**)hints); + + return result; +} + +static audio_option_t* alsa_audio_options(int* option_count) +{ + *option_count = 1; + static audio_option_t result[1]; + + result[0].cfg_name = "device"; + result[0].caption = "Device"; + result[0].type = list; + result[0].list_callback = alsa_get_devicelist; + + return result; +} + static audiobackend_t audiobackend_alsa = { "alsa", // Slug "Advanced Linux Sound Architecture", // Name &alsa_init, &alsa_push, - &alsa_term + &alsa_term, + &alsa_audio_options }; static bool alsa = RegisterAudioBackend(&audiobackend_alsa); diff --git a/core/oslib/audiobackend_omx.cpp b/core/oslib/audiobackend_omx.cpp index 71c426b68..c8c8a6536 100644 --- a/core/oslib/audiobackend_omx.cpp +++ b/core/oslib/audiobackend_omx.cpp @@ -313,7 +313,8 @@ audiobackend_t audiobackend_omx = { "OpenMAX IL", // Name &omx_init, &omx_push, - &omx_term + &omx_term, + NULL }; static bool omx = RegisterAudioBackend(&audiobackend_omx); diff --git a/core/oslib/audiobackend_oss.cpp b/core/oslib/audiobackend_oss.cpp index cc653466f..b3a48cbd6 100644 --- a/core/oslib/audiobackend_oss.cpp +++ b/core/oslib/audiobackend_oss.cpp @@ -48,7 +48,8 @@ audiobackend_t audiobackend_oss = { "Open Sound System", // Name &oss_init, &oss_push, - &oss_term + &oss_term, + NULL }; static bool oss = RegisterAudioBackend(&audiobackend_oss); diff --git a/core/oslib/audiobackend_pulseaudio.cpp b/core/oslib/audiobackend_pulseaudio.cpp index 28963b00a..b651b4003 100644 --- a/core/oslib/audiobackend_pulseaudio.cpp +++ b/core/oslib/audiobackend_pulseaudio.cpp @@ -43,7 +43,8 @@ audiobackend_t audiobackend_pulseaudio = { "PulseAudio", // Name &pulseaudio_init, &pulseaudio_push, - &pulseaudio_term + &pulseaudio_term, + NULL }; static bool pulse = RegisterAudioBackend(&audiobackend_pulseaudio); diff --git a/core/oslib/audiostream.h b/core/oslib/audiostream.h index 2726db0e1..37c1cbc43 100644 --- a/core/oslib/audiostream.h +++ b/core/oslib/audiostream.h @@ -11,6 +11,29 @@ u32 asRingFreeCount(); bool asRingRead(u8* dst,u32 count=0); void UpdateBuff(u8* pos); +typedef std::vector (*audio_option_callback_t)(); +enum audio_option_type +{ + text = 0 +, integer = 1 +, list = 2 +}; + +typedef struct { + std::string cfg_name; + std::string caption; + audio_option_type type; + + // type int_value (spin edit) + int min_value; + int max_value; + + // type list edit (string/char*) + audio_option_callback_t list_callback; +} audio_option_t; + +typedef audio_option_t* (*audio_options_func_t)(int* option_count); + typedef void (*audio_backend_init_func_t)(); typedef u32 (*audio_backend_push_func_t)(void*, u32, bool); typedef void (*audio_backend_term_func_t)(); @@ -20,6 +43,7 @@ typedef struct { audio_backend_init_func_t init; audio_backend_push_func_t push; audio_backend_term_func_t term; + audio_options_func_t get_options; } audiobackend_t; extern bool RegisterAudioBackend(audiobackend_t* backend); extern void InitAudio(); diff --git a/core/rend/gui.cpp b/core/rend/gui.cpp index a530f5b5c..008ed1041 100644 --- a/core/rend/gui.cpp +++ b/core/rend/gui.cpp @@ -1036,6 +1036,8 @@ static void gui_display_settings() } SortAudioBackends(); + + audiobackend_t* current_backend = backend; if (ImGui::BeginCombo("Audio Backend", backend_name.c_str(), ImGuiComboFlags_None)) { bool is_selected = (settings.audio.backend == "auto"); @@ -1052,9 +1054,12 @@ static void gui_display_settings() for (int i = 0; i < GetAudioBackendCount(); i++) { - audiobackend_t* backend = GetAudioBackend(i); + backend = GetAudioBackend(i); is_selected = (settings.audio.backend == backend->slug); + if (is_selected) + current_backend = backend; + if (ImGui::Selectable(backend->slug.c_str(), &is_selected)) settings.audio.backend = backend->slug; ImGui::SameLine(); ImGui::Text("-"); @@ -1067,6 +1072,52 @@ static void gui_display_settings() ImGui::SameLine(); ShowHelpMarker("The audio backend to use"); + if (current_backend != NULL && current_backend->get_options != NULL) + { + // get backend specific options + int option_count; + audio_option_t* options = current_backend->get_options(&option_count); + + // initialize options if not already done + std::map* cfg_entries = &settings.audio.options[current_backend->slug]; + bool populate_entries = (cfg_entries->size() == 0); + + for (int o = 0; o < option_count; o++) + { + std::string value; + if (populate_entries) + { + value = cfgLoadStr(current_backend->slug.c_str(), options->cfg_name.c_str(), ""); + (*cfg_entries)[options->cfg_name] = value; + } + value = (*cfg_entries)[options->cfg_name]; + + if (options->type == list) + { + if (ImGui::BeginCombo(options->caption.c_str(), value.c_str(), ImGuiComboFlags_None)) + { + bool is_selected = false; + std::vector list_items = options->list_callback(); + for (std::vector::iterator it = list_items.begin() ; it != list_items.end(); ++it) + { + std::string cur = (std::string)*it; + is_selected = (value == cur); + if (ImGui::Selectable(cur.c_str(), &is_selected)) + { + (*cfg_entries)[options->cfg_name] = cur; + } + + if (is_selected) + ImGui::SetItemDefaultFocus(); + } + ImGui::EndCombo(); + } + } + + options++; + } + } + ImGui::Checkbox("Enable DSP", &settings.aica.NoBatch); ImGui::SameLine(); ShowHelpMarker("Enable the Dreamcast Digital Sound Processor. Only recommended on fast and arm64 platforms"); diff --git a/core/types.h b/core/types.h index 771d758e6..8b1a039a8 100644 --- a/core/types.h +++ b/core/types.h @@ -344,6 +344,7 @@ int darw_printf(const wchar* Text,...); //includes from c++rt #include #include +#include using namespace std; //used for asm-olny functions @@ -387,7 +388,7 @@ using namespace std; #ifndef RELEASE #define EMUERROR(format, ...) printf("Error in %20s:%s:%d: " format "\n", \ __FILE__, __FUNCTION__, __LINE__, ##__VA_ARGS__) -//strlen(__FILE__) <= 20 ? __FILE__ : __FILE__ + strlen(__FILE__) - 20, +//strlen(__FILE__) <= 20 ? __FILE__ : __FILE__ + strlen(__FILE__) - 20, #else #define EMUERROR(format, ...) #endif @@ -681,7 +682,12 @@ struct settings_t struct{ std::string backend; + + // slug<> + std::map> options; } audio; + + #if USE_OMX struct { diff --git a/shell/android-studio/reicast/src/main/jni/src/Android.cpp b/shell/android-studio/reicast/src/main/jni/src/Android.cpp index 7b6ad5533..07521e6ee 100644 --- a/shell/android-studio/reicast/src/main/jni/src/Android.cpp +++ b/shell/android-studio/reicast/src/main/jni/src/Android.cpp @@ -547,7 +547,8 @@ audiobackend_t audiobackend_android = { "Android Audio", // Name &androidaudio_init, &androidaudio_push, - &androidaudio_term + &androidaudio_term, + NULL }; static bool android = RegisterAudioBackend(&audiobackend_android); From 7b50d5df8c48d74e7091ad05b351e04c2c468ab6 Mon Sep 17 00:00:00 2001 From: David Guillen Fandos Date: Sat, 27 Apr 2019 12:05:13 +0200 Subject: [PATCH 255/319] Add ENABLE_MODEM and rework makefiles to support it. --- core/core.mk | 51 +++++++++++++++++++--------------------- core/hw/holly/sb.cpp | 2 +- core/hw/holly/sb_mem.cpp | 6 +++-- core/serialize.cpp | 12 +++++----- shell/linux/Makefile | 10 +------- 5 files changed, 36 insertions(+), 45 deletions(-) diff --git a/core/core.mk b/core/core.mk index 1337c531a..4573ba5e7 100755 --- a/core/core.mk +++ b/core/core.mk @@ -7,20 +7,11 @@ RZDCY_SRC_DIR ?= $(call my-dir) VERSION_HEADER := $(RZDCY_SRC_DIR)/version.h -RZDCY_MODULES := cfg/ hw/arm7/ hw/aica/ hw/holly/ hw/ hw/gdrom/ hw/maple/ hw/modem/ \ +RZDCY_MODULES := cfg/ hw/arm7/ hw/aica/ hw/holly/ hw/ hw/gdrom/ hw/maple/ \ hw/mem/ hw/pvr/ hw/sh4/ hw/sh4/interpr/ hw/sh4/modules/ plugins/ profiler/ oslib/ \ hw/extdev/ hw/arm/ hw/naomi/ imgread/ ./ deps/coreio/ deps/zlib/ deps/chdr/ deps/crypto/ \ deps/libelf/ deps/chdpsr/ arm_emitter/ rend/ reios/ deps/libpng/ deps/xbrz/ \ - deps/picotcp/modules/ deps/picotcp/stack/ deps/xxhash/ deps/libzip/ deps/imgui/ \ - archive/ input/ - -ifdef CHD5_LZMA - RZDCY_MODULES += deps/lzma/ -endif - -ifdef CHD5_FLAC - RZDCY_MODULES += deps/flac/src/libFLAC/ -endif + deps/xxhash/ deps/libzip/ deps/imgui/ archive/ input/ ifdef WEBUI RZDCY_MODULES += webui/ @@ -31,10 +22,6 @@ ifdef WEBUI endif endif -ifndef NO_REC - RZDCY_MODULES += hw/sh4/dyna/ -endif - ifndef NOT_ARM RZDCY_MODULES += rec-ARM/ endif @@ -66,10 +53,6 @@ else RZDCY_MODULES += rend/norend/ endif -ifdef HAS_SOFTREND - RZDCY_MODULES += rend/soft/ -endif - ifndef NO_NIXPROF RZDCY_MODULES += linux/nixprof/ endif @@ -90,11 +73,6 @@ ifdef FOR_WINDOWS RZDCY_MODULES += windows/ endif -RZDCY_FILES := $(foreach dir,$(addprefix $(RZDCY_SRC_DIR)/,$(RZDCY_MODULES)),$(wildcard $(dir)*.cpp)) -RZDCY_FILES += $(foreach dir,$(addprefix $(RZDCY_SRC_DIR)/,$(RZDCY_MODULES)),$(wildcard $(dir)*.cc)) -RZDCY_FILES += $(foreach dir,$(addprefix $(RZDCY_SRC_DIR)/,$(RZDCY_MODULES)),$(wildcard $(dir)*.c)) -RZDCY_FILES += $(foreach dir,$(addprefix $(RZDCY_SRC_DIR)/,$(RZDCY_MODULES)),$(wildcard $(dir)*.S)) - ifdef FOR_PANDORA RZDCY_CFLAGS := \ $(CFLAGS) -c -O3 \ @@ -133,11 +111,17 @@ RZDCY_CFLAGS := endif RZDCY_CFLAGS += -I$(RZDCY_SRC_DIR) -I$(RZDCY_SRC_DIR)/rend/gles -I$(RZDCY_SRC_DIR)/deps \ - -I$(RZDCY_SRC_DIR)/deps/picotcp/include -I$(RZDCY_SRC_DIR)/deps/picotcp/modules \ -I$(RZDCY_SRC_DIR)/deps/vixl -I$(RZDCY_SRC_DIR)/khronos +ifdef USE_MODEM + RZDCY_CFLAGS += -DENABLE_MODEM -I$(RZDCY_SRC_DIR)/deps/picotcp/include -I$(RZDCY_SRC_DIR)/deps/picotcp/modules + RZDCY_MODULES += hw/modem/ deps/picotcp/modules/ deps/picotcp/stack/ +endif + ifdef NO_REC - RZDCY_CFLAGS += -DTARGET_NO_REC + RZDCY_CFLAGS += -DTARGET_NO_REC +else + RZDCY_MODULES += hw/sh4/dyna/ endif ifdef USE_GLES @@ -146,15 +130,28 @@ endif ifdef HAS_SOFTREND RZDCY_CFLAGS += -DTARGET_SOFTREND + RZDCY_MODULES += rend/soft/ endif ifdef CHD5_FLAC - RZDCY_CFLAGS += -I$(RZDCY_SRC_DIR)/deps/flac/src/libFLAC/include/ -I$(RZDCY_SRC_DIR)/deps/flac/include + RZDCY_CFLAGS += -DCHD5_FLAC -I$(RZDCY_SRC_DIR)/deps/flac/src/libFLAC/include/ -I$(RZDCY_SRC_DIR)/deps/flac/include RZDCY_CFLAGS += -DPACKAGE_VERSION=\"1.3.2\" -DFLAC__HAS_OGG=0 -DFLAC__NO_DLL -DHAVE_LROUND -DHAVE_STDINT_H -DHAVE_STDLIB_H -DHAVE_SYS_PARAM_H + RZDCY_MODULES += deps/flac/src/libFLAC/ +endif + +# 7-Zip/LZMA settings (CHDv5) +ifdef CHD5_LZMA + RZDCY_MODULES += deps/lzma/ + RZDCY_CFLAGS += -D_7ZIP_ST -DCHD5_LZMA endif RZDCY_CXXFLAGS := $(RZDCY_CFLAGS) -fno-exceptions -fno-rtti -std=gnu++11 +RZDCY_FILES := $(foreach dir,$(addprefix $(RZDCY_SRC_DIR)/,$(RZDCY_MODULES)),$(wildcard $(dir)*.cpp)) +RZDCY_FILES += $(foreach dir,$(addprefix $(RZDCY_SRC_DIR)/,$(RZDCY_MODULES)),$(wildcard $(dir)*.cc)) +RZDCY_FILES += $(foreach dir,$(addprefix $(RZDCY_SRC_DIR)/,$(RZDCY_MODULES)),$(wildcard $(dir)*.c)) +RZDCY_FILES += $(foreach dir,$(addprefix $(RZDCY_SRC_DIR)/,$(RZDCY_MODULES)),$(wildcard $(dir)*.S)) + $(VERSION_HEADER): echo "#define REICAST_VERSION \"`git describe --tags --always`\"" > $(VERSION_HEADER) echo "#define GIT_HASH \"`git rev-parse --short HEAD`\"" >> $(VERSION_HEADER) diff --git a/core/hw/holly/sb.cpp b/core/hw/holly/sb.cpp index 69c809a82..4c962b6f6 100644 --- a/core/hw/holly/sb.cpp +++ b/core/hw/holly/sb.cpp @@ -783,7 +783,7 @@ void sb_Init() maple_Init(); aica_sb_Init(); -#if DC_PLATFORM == DC_PLATFORM_DREAMCAST +#if DC_PLATFORM == DC_PLATFORM_DREAMCAST && defined(ENABLE_MODEM) ModemInit(); #endif } diff --git a/core/hw/holly/sb_mem.cpp b/core/hw/holly/sb_mem.cpp index d201550c3..c60e4a7ba 100644 --- a/core/hw/holly/sb_mem.cpp +++ b/core/hw/holly/sb_mem.cpp @@ -217,8 +217,10 @@ T DYNACALL ReadMem_area0(u32 addr) { #if DC_PLATFORM == DC_PLATFORM_NAOMI || DC_PLATFORM == DC_PLATFORM_ATOMISWAVE return (T)libExtDevice_ReadMem_A0_006(addr, sz); -#else +#elif defined(ENABLE_MODEM) return (T)ModemReadMem_A0_006(addr, sz); +#else + return (T)0; #endif } //map 0x0060 to 0x006F @@ -302,7 +304,7 @@ void DYNACALL WriteMem_area0(u32 addr,T data) { #if DC_PLATFORM == DC_PLATFORM_NAOMI || DC_PLATFORM == DC_PLATFORM_ATOMISWAVE libExtDevice_WriteMem_A0_006(addr, data, sz); -#else +#elif defined(ENABLE_MODEM) ModemWriteMem_A0_006(addr, data, sz); #endif } diff --git a/core/serialize.cpp b/core/serialize.cpp index 9d28fd068..1d7fec35b 100644 --- a/core/serialize.cpp +++ b/core/serialize.cpp @@ -1046,11 +1046,11 @@ bool dc_serialize(void **data, unsigned int *total_size) REICAST_S(sch_list[time_sync].start) ; REICAST_S(sch_list[time_sync].end) ; + #ifdef ENABLE_MODEM REICAST_S(sch_list[modem_sched].tag) ; REICAST_S(sch_list[modem_sched].start) ; REICAST_S(sch_list[modem_sched].end) ; - - + #endif REICAST_S(SCIF_SCFSR2); REICAST_S(SCIF_SCFRDR2); @@ -1448,11 +1448,11 @@ static bool dc_unserialize_libretro(void **data, unsigned int *total_size) REICAST_US(sch_list[time_sync].start) ; REICAST_US(sch_list[time_sync].end) ; + #ifdef ENABLE_MODEM REICAST_US(sch_list[modem_sched].tag) ; REICAST_US(sch_list[modem_sched].start) ; REICAST_US(sch_list[modem_sched].end) ; - - + #endif REICAST_US(SCIF_SCFSR2); REICAST_US(SCIF_SCFRDR2); @@ -1838,11 +1838,11 @@ bool dc_unserialize(void **data, unsigned int *total_size) REICAST_US(sch_list[time_sync].start) ; REICAST_US(sch_list[time_sync].end) ; + #ifdef ENABLE_MODEM REICAST_US(sch_list[modem_sched].tag) ; REICAST_US(sch_list[modem_sched].start) ; REICAST_US(sch_list[modem_sched].end) ; - - + #endif REICAST_US(SCIF_SCFSR2); REICAST_US(SCIF_SCFRDR2); diff --git a/shell/linux/Makefile b/shell/linux/Makefile index 6af3aed05..d182e0601 100644 --- a/shell/linux/Makefile +++ b/shell/linux/Makefile @@ -9,6 +9,7 @@ USE_OSS := 1 #USE_LIBAO := 1 USE_EVDEV := 1 USE_UDEV := 1 +USE_MODEM := 1 PLATFORM_EXT := elf CXX=${CC_PREFIX}g++ @@ -265,15 +266,6 @@ else $(error Unknown platform) endif -# 7-Zip/LZMA settings (CHDv5) -ifdef CHD5_LZMA - CFLAGS += -D_7ZIP_ST -DCHD5_LZMA -endif - -ifdef CHD5_FLAC - CFLAGS += -DCHD5_FLAC -endif - RZDCY_SRC_DIR = $(LOCAL_PATH)/../../core include $(RZDCY_SRC_DIR)/core.mk From 3692ea7ae609dbfd5cfccc58c7b1d2cb540f8961 Mon Sep 17 00:00:00 2001 From: David Guillen Fandos Date: Sat, 27 Apr 2019 14:08:43 +0200 Subject: [PATCH 256/319] Improve stdclass.h/cpp support for non-Linux non-Windows platforms Simplifies having implementation on platform separated files, which is a pain for platforms which are not Windows but not Linux either (and yet support pthreads). Some minor cleanup here and there while we are at it. --- core/hw/pvr/Renderer_if.cpp | 3 +- core/hw/pvr/ta_ctx.cpp | 2 +- core/linux/common.cpp | 103 ------------------- core/rend/gles/CustomTexture.h | 4 +- core/stdclass.cpp | 181 +++++++++++++++++++++++++-------- core/stdclass.h | 27 ++--- core/windows/winmain.cpp | 72 ------------- 7 files changed, 155 insertions(+), 237 deletions(-) diff --git a/core/hw/pvr/Renderer_if.cpp b/core/hw/pvr/Renderer_if.cpp index fc4db314d..0a9c5b16b 100644 --- a/core/hw/pvr/Renderer_if.cpp +++ b/core/hw/pvr/Renderer_if.cpp @@ -82,8 +82,7 @@ bool renderer_enabled = true; // Signals the renderer thread to exit bool renderer_changed = false; // Signals the renderer thread to switch renderer #if !defined(TARGET_NO_THREADS) -cResetEvent rs(false,true); -cResetEvent re(false,true); +cResetEvent rs, re; #endif int max_idx,max_mvo,max_op,max_pt,max_tr,max_vtx,max_modt, ovrn; diff --git a/core/hw/pvr/ta_ctx.cpp b/core/hw/pvr/ta_ctx.cpp index 781d82aa4..31e80b07e 100644 --- a/core/hw/pvr/ta_ctx.cpp +++ b/core/hw/pvr/ta_ctx.cpp @@ -124,7 +124,7 @@ void VDecEnd() cMutex mtx_rqueue; TA_context* rqueue; -cResetEvent frame_finished(false, true); +cResetEvent frame_finished; double last_frame = 0; u64 last_cyces = 0; diff --git a/core/linux/common.cpp b/core/linux/common.cpp index 7b6db0f7f..98ffca196 100644 --- a/core/linux/common.cpp +++ b/core/linux/common.cpp @@ -130,109 +130,6 @@ void install_fault_handler (void) #endif } -#if !defined(TARGET_NO_THREADS) - -//Thread class -cThread::cThread(ThreadEntryFP* function,void* prm) -{ - Entry=function; - param=prm; -} - -void cThread::Start() -{ - verify(hThread == NULL); - if (pthread_create( (pthread_t*)&hThread, NULL, Entry, param)) - { - die("Thread creation failed"); - } -} - -void cThread::WaitToEnd() -{ - if (hThread != NULL) - { - pthread_join((pthread_t)hThread,0); - hThread = NULL; - } -} - -//End thread class -#endif - -//cResetEvent Calss -cResetEvent::cResetEvent(bool State,bool Auto) -{ - //sem_init((sem_t*)hEvent, 0, State?1:0); - verify(State==false&&Auto==true); - pthread_mutex_init(&mutx, NULL); - pthread_cond_init(&cond, NULL); -} -cResetEvent::~cResetEvent() -{ - //Destroy the event object ? - -} -void cResetEvent::Set()//Signal -{ - pthread_mutex_lock( &mutx ); - state=true; - pthread_cond_signal( &cond); - pthread_mutex_unlock( &mutx ); -} -void cResetEvent::Reset()//reset -{ - pthread_mutex_lock( &mutx ); - state=false; - pthread_mutex_unlock( &mutx ); -} -bool cResetEvent::Wait(u32 msec)//Wait for signal , then reset -{ - pthread_mutex_lock( &mutx ); - if (!state) - { - struct timespec ts; -#if HOST_OS == OS_DARWIN - // OSX doesn't have clock_gettime. - clock_serv_t cclock; - mach_timespec_t mts; - - host_get_clock_service(mach_host_self(), CALENDAR_CLOCK, &cclock); - clock_get_time(cclock, &mts); - mach_port_deallocate(mach_task_self(), cclock); - ts.tv_sec = mts.tv_sec; - ts.tv_nsec = mts.tv_nsec; -#else - clock_gettime(CLOCK_REALTIME, &ts); -#endif - ts.tv_sec += msec / 1000; - ts.tv_nsec += (msec % 1000) * 1000000; - while (ts.tv_nsec > 1000000000) - { - ts.tv_nsec -= 1000000000; - ts.tv_sec++; - } - pthread_cond_timedwait( &cond, &mutx, &ts ); - } - bool rc = state; - state=false; - pthread_mutex_unlock( &mutx ); - - return rc; -} -void cResetEvent::Wait()//Wait for signal , then reset -{ - pthread_mutex_lock( &mutx ); - if (!state) - { - pthread_cond_wait( &cond, &mutx ); - } - state=false; - pthread_mutex_unlock( &mutx ); -} - -//End AutoResetEvent - #include void VArray2::LockRegion(u32 offset,u32 size) diff --git a/core/rend/gles/CustomTexture.h b/core/rend/gles/CustomTexture.h index 830855542..fd8a00db1 100644 --- a/core/rend/gles/CustomTexture.h +++ b/core/rend/gles/CustomTexture.h @@ -29,9 +29,9 @@ public: CustomTexture() : #ifndef TARGET_NO_THREADS - loader_thread(loader_thread_func, this), + loader_thread(loader_thread_func, this) #endif - wakeup_thread(false, true) {} + {} ~CustomTexture() { Terminate(); } u8* LoadCustomTexture(u32 hash, int& width, int& height); void LoadCustomTextureAsync(TextureCacheData *texture_data); diff --git a/core/stdclass.cpp b/core/stdclass.cpp index ca773ee24..05cc49013 100644 --- a/core/stdclass.cpp +++ b/core/stdclass.cpp @@ -4,6 +4,7 @@ #include #include "types.h" #include "cfg/cfg.h" +#include "stdclass.h" #if COMPILER_VC_OR_CLANG_WIN32 @@ -153,55 +154,145 @@ bool make_directory(const string& path) #endif } -#if 0 -//File Enumeration -void FindAllFiles(FileFoundCB* callback,wchar* dir,void* param) -{ - WIN32_FIND_DATA FindFileData; - HANDLE hFind = INVALID_HANDLE_VALUE; - wchar DirSpec[MAX_PATH + 1]; // directory specification - DWORD dwError; +// Thread & related platform dependant code +#if !defined(HOST_NO_THREADS) - strncpy (DirSpec, dir, strlen(dir)+1); - //strncat (DirSpec, "\\*", 3); - - hFind = FindFirstFile( DirSpec, &FindFileData); - - if (hFind == INVALID_HANDLE_VALUE) - { - return; - } - else - { - - if ((FindFileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)==0) - { - callback(FindFileData.cFileName,param); - } -u32 rv; - while ( (rv=FindNextFile(hFind, &FindFileData)) != 0) - { - if ((FindFileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)==0) - { - callback(FindFileData.cFileName,param); - } - } - dwError = GetLastError(); - FindClose(hFind); - if (dwError != ERROR_NO_MORE_FILES) - { - return ; - } +#if HOST_OS==OS_WINDOWS +void cThread::Start() { + verify(hThread == NULL); + hThread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)entry, param, 0, NULL); + ResumeThread(hThread); +} +void cThread::WaitToEnd() { + WaitForSingleObject(hThread,INFINITE); + CloseHandle(hThread); + hThread = NULL; +} +#else +void cThread::Start() { + verify(hThread == NULL); + hThread = new pthread_t; + if (pthread_create( hThread, NULL, entry, param)) + die("Thread creation failed"); +} +void cThread::WaitToEnd() { + if (hThread) { + pthread_join(*hThread,0); + delete hThread; + hThread = NULL; } - return ; } #endif -/* -#include "dc\sh4\rec_v1\compiledblock.h" -#include "dc\sh4\rec_v1\blockmanager.h" +#endif + + +#if HOST_OS==OS_WINDOWS +cResetEvent::cResetEvent() { + hEvent = CreateEvent( + NULL, // default security attributes + FALSE, // auto-reset event? + FALSE, // initial state is State + NULL // unnamed object + ); +} +cResetEvent::~cResetEvent() +{ + //Destroy the event object ? + CloseHandle(hEvent); +} +void cResetEvent::Set()//Signal +{ + #if defined(DEBUG_THREADS) + Sleep(rand() % 10); + #endif + SetEvent(hEvent); +} +void cResetEvent::Reset()//reset +{ + #if defined(DEBUG_THREADS) + Sleep(rand() % 10); + #endif + ResetEvent(hEvent); +} +bool cResetEvent::Wait(u32 msec)//Wait for signal , then reset +{ + #if defined(DEBUG_THREADS) + Sleep(rand() % 10); + #endif + return WaitForSingleObject(hEvent,msec) == WAIT_OBJECT_0; +} +void cResetEvent::Wait()//Wait for signal , then reset +{ + #if defined(DEBUG_THREADS) + Sleep(rand() % 10); + #endif + WaitForSingleObject(hEvent,(u32)-1); +} +#else +cResetEvent::cResetEvent() { + pthread_mutex_init(&mutx, NULL); + pthread_cond_init(&cond, NULL); +} +cResetEvent::~cResetEvent() { +} +void cResetEvent::Set()//Signal +{ + pthread_mutex_lock( &mutx ); + state=true; + pthread_cond_signal( &cond); + pthread_mutex_unlock( &mutx ); +} +void cResetEvent::Reset()//reset +{ + pthread_mutex_lock( &mutx ); + state=false; + pthread_mutex_unlock( &mutx ); +} +bool cResetEvent::Wait(u32 msec)//Wait for signal , then reset +{ + pthread_mutex_lock( &mutx ); + if (!state) + { + struct timespec ts; +#if HOST_OS == OS_DARWIN + // OSX doesn't have clock_gettime. + clock_serv_t cclock; + mach_timespec_t mts; + + host_get_clock_service(mach_host_self(), CALENDAR_CLOCK, &cclock); + clock_get_time(cclock, &mts); + mach_port_deallocate(mach_task_self(), cclock); + ts.tv_sec = mts.tv_sec; + ts.tv_nsec = mts.tv_nsec; +#else + clock_gettime(CLOCK_REALTIME, &ts); +#endif + ts.tv_sec += msec / 1000; + ts.tv_nsec += (msec % 1000) * 1000000; + while (ts.tv_nsec > 1000000000) + { + ts.tv_nsec -= 1000000000; + ts.tv_sec++; + } + pthread_cond_timedwait( &cond, &mutx, &ts ); + } + bool rc = state; + state=false; + pthread_mutex_unlock( &mutx ); + + return rc; +} +void cResetEvent::Wait()//Wait for signal , then reset +{ + pthread_mutex_lock( &mutx ); + if (!state) + { + pthread_cond_wait( &cond, &mutx ); + } + state=false; + pthread_mutex_unlock( &mutx ); +} +#endif -bool VramLockedWrite(u8* address); -bool RamLockedWrite(u8* address,u32* sp); -*/ diff --git a/core/stdclass.h b/core/stdclass.h index 0c30dd534..f5e87e6cb 100644 --- a/core/stdclass.h +++ b/core/stdclass.h @@ -166,38 +166,41 @@ public: #if !defined(HOST_NO_THREADS) typedef void* ThreadEntryFP(void* param); -typedef void* THREADHANDLE; - -class cThread -{ +class cThread { private: - ThreadEntryFP* Entry; + ThreadEntryFP* entry; void* param; public : - THREADHANDLE hThread; - cThread(ThreadEntryFP* function,void* param); - + #if HOST_OS==OS_WINDOWS + HANDLE hThread; + #else + pthread_t *hThread; + #endif + + cThread(ThreadEntryFP* function, void* param) + :entry(function), param(param), hThread(NULL) {} + ~cThread() { WaitToEnd(); } void Start(); void WaitToEnd(); }; #endif + + //Wait Events typedef void* EVENTHANDLE; class cResetEvent { - private: #if HOST_OS==OS_WINDOWS EVENTHANDLE hEvent; #else pthread_mutex_t mutx; pthread_cond_t cond; - + bool state; #endif public : - bool state; - cResetEvent(bool State,bool Auto); + cResetEvent(); ~cResetEvent(); void Set(); //Set state to signaled void Reset(); //Set state to non signaled diff --git a/core/windows/winmain.cpp b/core/windows/winmain.cpp index 7e269a90d..27d651dd4 100644 --- a/core/windows/winmain.cpp +++ b/core/windows/winmain.cpp @@ -750,78 +750,6 @@ void os_DoEvents() } - - -//Windoze Code implementation of commong classes from here and after .. - -//Thread class -cThread::cThread(ThreadEntryFP* function,void* prm) -{ - Entry=function; - param=prm; -} - - -void cThread::Start() -{ - verify(hThread == NULL); - hThread=CreateThread(NULL,0,(LPTHREAD_START_ROUTINE)Entry,param,0,NULL); - ResumeThread(hThread); -} - -void cThread::WaitToEnd() -{ - WaitForSingleObject(hThread,INFINITE); - CloseHandle(hThread); - hThread = NULL; -} -//End thread class - -//cResetEvent Calss -cResetEvent::cResetEvent(bool State,bool Auto) -{ - hEvent = CreateEvent( - NULL, // default security attributes - Auto?FALSE:TRUE, // auto-reset event? - State?TRUE:FALSE, // initial state is State - NULL // unnamed object - ); -} -cResetEvent::~cResetEvent() -{ - //Destroy the event object ? - CloseHandle(hEvent); -} -void cResetEvent::Set()//Signal -{ - #if defined(DEBUG_THREADS) - Sleep(rand() % 10); - #endif - SetEvent(hEvent); -} -void cResetEvent::Reset()//reset -{ - #if defined(DEBUG_THREADS) - Sleep(rand() % 10); - #endif - ResetEvent(hEvent); -} -bool cResetEvent::Wait(u32 msec)//Wait for signal , then reset -{ - #if defined(DEBUG_THREADS) - Sleep(rand() % 10); - #endif - return WaitForSingleObject(hEvent,msec) == WAIT_OBJECT_0; -} -void cResetEvent::Wait()//Wait for signal , then reset -{ - #if defined(DEBUG_THREADS) - Sleep(rand() % 10); - #endif - WaitForSingleObject(hEvent,(u32)-1); -} -//End AutoResetEvent - void VArray2::LockRegion(u32 offset,u32 size) { //verify(offset+sizesize); From d6f682a32937fc9e1b91dfe120c7951018324d7a Mon Sep 17 00:00:00 2001 From: David Miller Date: Wed, 1 May 2019 12:01:45 -0400 Subject: [PATCH 257/319] audiostream only wait if at speed --- core/oslib/audiostream.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/core/oslib/audiostream.cpp b/core/oslib/audiostream.cpp index ea6845bed..b07666b7a 100644 --- a/core/oslib/audiostream.cpp +++ b/core/oslib/audiostream.cpp @@ -117,10 +117,14 @@ audiobackend_t* GetAudioBackend(std::string slug) return NULL; } +extern double full_rps; + u32 PushAudio(void* frame, u32 amt, bool wait) { + bool do_wait = (full_rps<50.f)?false:wait; + if (audiobackend_current != NULL) { - return audiobackend_current->push(frame, amt, wait); + return audiobackend_current->push(frame, amt, do_wait); } return 0; } From c681dc77dc8a2d7b9b8d12dc6291c2fe9eed692f Mon Sep 17 00:00:00 2001 From: david miller Date: Wed, 1 May 2019 19:11:35 -0400 Subject: [PATCH 258/319] Win32 fullscreen via alt-enter --- CMakeLists.txt | 2 +- core/windows/winmain.cpp | 51 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 52 insertions(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 9a757f3d0..80342e0d2 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -158,6 +158,7 @@ if(${BUILD_COMPILER} EQUAL ${COMPILER_GCC}) # Add Clang if NOT WIN32 *FIXME* ${lzma_SRCS} ${pico_SRCS} ) + add_definitions(-D_7ZIP_ST -DCHD5_LZMA) endif() ### libosd.cmake ################################################################################ @@ -243,7 +244,6 @@ endif() include_directories ("${reicast_core_path}") -add_definitions(-D_7ZIP_ST -DCHD5_LZMA) diff --git a/core/windows/winmain.cpp b/core/windows/winmain.cpp index 27d651dd4..029ba1246 100644 --- a/core/windows/winmain.cpp +++ b/core/windows/winmain.cpp @@ -198,6 +198,10 @@ extern f32 mo_wheel_delta; // Keyboard static Win32KeyboardDevice keyboard(0); + +void ToggleFullscreen(); + + void UpdateInputState(u32 port) { /* @@ -331,6 +335,14 @@ LRESULT CALLBACK WndProc2(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) keyboard.keyboard_input(keycode, message == WM_KEYDOWN); } break; + + case WM_SYSKEYDOWN: + if (wParam == VK_RETURN) + if ((HIWORD(lParam) & KF_ALTDOWN)) + ToggleFullscreen(); + + break; + case WM_CHAR: keyboard.keyboard_character((char)wParam); return 0; @@ -388,6 +400,45 @@ void* libPvr_GetRenderSurface() return GetDC((HWND)window_win); } + +void ToggleFullscreen() +{ + static RECT rSaved; + static bool fullscreen=false; + HWND hWnd = (HWND)window_win; + + fullscreen = !fullscreen; + + + if (fullscreen) + { + GetWindowRect(hWnd, &rSaved); + + MONITORINFO mi = { sizeof(mi) }; + HMONITOR hmon = MonitorFromWindow(hWnd, MONITOR_DEFAULTTONEAREST); + if (GetMonitorInfo(hmon, &mi)) { + + SetWindowLongPtr(hWnd, GWL_EXSTYLE, WS_EX_APPWINDOW | WS_EX_TOPMOST); + SetWindowLongPtr(hWnd, GWL_STYLE, WS_POPUP | WS_VISIBLE); + + SetWindowPos(hWnd, HWND_TOPMOST, mi.rcMonitor.left, mi.rcMonitor.top, + mi.rcMonitor.right - mi.rcMonitor.left, mi.rcMonitor.bottom - mi.rcMonitor.top, + SWP_SHOWWINDOW|SWP_FRAMECHANGED|SWP_ASYNCWINDOWPOS); + } + } + else { + + SetWindowLongPtr(hWnd, GWL_EXSTYLE, WS_EX_APPWINDOW | WS_EX_TOPMOST); + SetWindowLongPtr(hWnd, GWL_STYLE, WS_VISIBLE | WS_OVERLAPPEDWINDOW | (window_maximized ? WS_MAXIMIZE : 0)); + + SetWindowPos(hWnd, NULL, rSaved.left, rSaved.top, + rSaved.right - rSaved.left, rSaved.bottom - rSaved.top, + SWP_SHOWWINDOW|SWP_FRAMECHANGED|SWP_ASYNCWINDOWPOS|SWP_NOZORDER); + } + +} + + BOOL CtrlHandler( DWORD fdwCtrlType ) { switch( fdwCtrlType ) From ac9e6d147d67b5da8713e3658f51ed6ad010ede9 Mon Sep 17 00:00:00 2001 From: David Quintana Date: Thu, 2 May 2019 03:21:22 +0200 Subject: [PATCH 259/319] Change speed-dependant limiting to rely on time dilation instead of frame rate. --- core/hw/pvr/spg.cpp | 4 +++- core/oslib/audiostream.cpp | 13 +++++++------ 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/core/hw/pvr/spg.cpp b/core/hw/pvr/spg.cpp index 0cf4c4d0b..b0970c3f1 100755 --- a/core/hw/pvr/spg.cpp +++ b/core/hw/pvr/spg.cpp @@ -88,6 +88,8 @@ double full_rps; static u32 lightgun_line = 0xffff; static u32 lightgun_hpos; +double mspdf; + u32 fskip=0; //called from sh4 context , should update pvr/ta state and everything else int spg_line_sched(int tag, int cycl, int jit) @@ -174,7 +176,7 @@ int spg_line_sched(int tag, int cycl, int jit) } double frames_done=spd_cpu/2; - double mspdf=1/frames_done*1000; + mspdf=1/frames_done*1000; full_rps=(spd_fps+fskip/ts); diff --git a/core/oslib/audiostream.cpp b/core/oslib/audiostream.cpp index b07666b7a..5e6a981ed 100644 --- a/core/oslib/audiostream.cpp +++ b/core/oslib/audiostream.cpp @@ -117,14 +117,10 @@ audiobackend_t* GetAudioBackend(std::string slug) return NULL; } -extern double full_rps; - u32 PushAudio(void* frame, u32 amt, bool wait) { - bool do_wait = (full_rps<50.f)?false:wait; - if (audiobackend_current != NULL) { - return audiobackend_current->push(frame, amt, do_wait); + return audiobackend_current->push(frame, amt, wait); } return 0; } @@ -144,6 +140,8 @@ u32 asRingFreeCount() return RingBufferSampleCount-asRingUsedCount(); } +extern double mspdf; +double mspdf_smooth; void WriteSample(s16 r, s16 l) { const u32 ptr=(WritePtr+1)%RingBufferSampleCount; @@ -153,7 +151,10 @@ void WriteSample(s16 r, s16 l) if (WritePtr==(SAMPLE_COUNT-1)) { - PushAudio(RingBuffer,SAMPLE_COUNT,settings.aica.LimitFPS); + mspdf_smooth = mspdf_smooth * 0.9 + mspdf * 0.1; + bool do_wait = settings.aica.LimitFPS && (mspdf_smooth <= 11); + + PushAudio(RingBuffer,SAMPLE_COUNT, do_wait); } } From f307f9d5ad47a038fd5967b69bee2563ceb61a7f Mon Sep 17 00:00:00 2001 From: David Quintana Date: Thu, 2 May 2019 03:36:10 +0200 Subject: [PATCH 260/319] Simplify. Smoothing adds no value there. --- core/oslib/audiostream.cpp | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/core/oslib/audiostream.cpp b/core/oslib/audiostream.cpp index 5e6a981ed..1d6f4f079 100644 --- a/core/oslib/audiostream.cpp +++ b/core/oslib/audiostream.cpp @@ -141,7 +141,6 @@ u32 asRingFreeCount() } extern double mspdf; -double mspdf_smooth; void WriteSample(s16 r, s16 l) { const u32 ptr=(WritePtr+1)%RingBufferSampleCount; @@ -151,8 +150,7 @@ void WriteSample(s16 r, s16 l) if (WritePtr==(SAMPLE_COUNT-1)) { - mspdf_smooth = mspdf_smooth * 0.9 + mspdf * 0.1; - bool do_wait = settings.aica.LimitFPS && (mspdf_smooth <= 11); + bool do_wait = settings.aica.LimitFPS && (mspdf <= 11); PushAudio(RingBuffer,SAMPLE_COUNT, do_wait); } From 684ba26ec0bd78fc98eea5b1d7bbbc3962048177 Mon Sep 17 00:00:00 2001 From: "Christoph \"baka0815\" Schwerdtfeger" Date: Thu, 2 May 2019 18:41:45 +0200 Subject: [PATCH 261/319] AUDIO: Reorganize settings Move DSP and FPS above the slug selection so that all slug specific settings are after the slug selection. --- core/rend/gui.cpp | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/core/rend/gui.cpp b/core/rend/gui.cpp index 008ed1041..0404b503a 100644 --- a/core/rend/gui.cpp +++ b/core/rend/gui.cpp @@ -1026,6 +1026,13 @@ static void gui_display_settings() ImGui::SameLine(); ShowHelpMarker("Disable the emulator sound output"); + ImGui::Checkbox("Enable DSP", &settings.aica.NoBatch); + ImGui::SameLine(); + ShowHelpMarker("Enable the Dreamcast Digital Sound Processor. Only recommended on fast and arm64 platforms"); + ImGui::Checkbox("Limit FPS", &settings.aica.LimitFPS); + ImGui::SameLine(); + ShowHelpMarker("Use the sound output to limit the speed of the emulator. Recommended in most cases"); + audiobackend_t* backend = NULL;; std::string backend_name = settings.audio.backend; if (backend_name != "auto" && backend_name != "none") @@ -1118,12 +1125,6 @@ static void gui_display_settings() } } - ImGui::Checkbox("Enable DSP", &settings.aica.NoBatch); - ImGui::SameLine(); - ShowHelpMarker("Enable the Dreamcast Digital Sound Processor. Only recommended on fast and arm64 platforms"); - ImGui::Checkbox("Limit FPS", &settings.aica.LimitFPS); - ImGui::SameLine(); - ShowHelpMarker("Use the sound output to limit the speed of the emulator. Recommended in most cases"); ImGui::PopStyleVar(); ImGui::EndTabItem(); } From 5678556cb6ee2378a03f2ac8d318d116f10dc8a0 Mon Sep 17 00:00:00 2001 From: "Christoph \"baka0815\" Schwerdtfeger" Date: Thu, 2 May 2019 18:48:09 +0200 Subject: [PATCH 262/319] ALSA: Rework initialization of pcm device As we can now configure the alsa device in the GUI, it's no longer necessary to write the first working device back to the configuration. Also there is now the "auto" device to automatically try to initialize the alsa device. --- core/oslib/audiobackend_alsa.cpp | 27 +++++++++++++++++++++------ 1 file changed, 21 insertions(+), 6 deletions(-) diff --git a/core/oslib/audiobackend_alsa.cpp b/core/oslib/audiobackend_alsa.cpp index 9effbcb6d..52223d895 100644 --- a/core/oslib/audiobackend_alsa.cpp +++ b/core/oslib/audiobackend_alsa.cpp @@ -18,30 +18,43 @@ static void alsa_init() string device = cfgLoadStr("alsa", "device", ""); int rc = -1; - if (device == "") + if (device == "" || device == "auto") { printf("ALSA: trying to determine audio device\n"); - /* Open PCM device for playback. */ + + // trying default device device = "default"; rc = snd_pcm_open(&handle, device.c_str(), SND_PCM_STREAM_PLAYBACK, 0); + // "default" didn't work, try first device if (rc < 0) { device = "plughw:0,0,0"; rc = snd_pcm_open(&handle, device.c_str(), SND_PCM_STREAM_PLAYBACK, 0); + + if (rc < 0) + { + device = "plughw:0,0"; + rc = snd_pcm_open(&handle, device.c_str(), SND_PCM_STREAM_PLAYBACK, 0); + } } + // first didn't work, try second if (rc < 0) { - device = "plughw:0,0"; + device = "plughw:1,0"; rc = snd_pcm_open(&handle, device.c_str(), SND_PCM_STREAM_PLAYBACK, 0); } - if (rc >= 0) + // try pulse audio backend + if (rc < 0) { - // init successfull, write value back to config - cfgSaveStr("alsa", "device", device.c_str()); + device = "pulse"; + rc = snd_pcm_open(&handle, device.c_str(), SND_PCM_STREAM_PLAYBACK, 0); } + + if (rc < 0) + printf("ALSA: unable to automatically determine audio device.\n"); } else { rc = snd_pcm_open(&handle, device.c_str(), SND_PCM_STREAM_PLAYBACK, 0); @@ -186,6 +199,8 @@ std::vector alsa_get_devicelist() if (err != 0) return result; + // special value to automatically detect on initialization + result.push_back("auto"); char** n = hints; while (*n != NULL) From dc709c604f06bb18bc8f092353ec0b7d53d1a6ce Mon Sep 17 00:00:00 2001 From: "Christoph \"baka0815\" Schwerdtfeger" Date: Thu, 2 May 2019 18:49:44 +0200 Subject: [PATCH 263/319] ALSA: prefix output with "ALSA:" --- core/oslib/audiobackend_alsa.cpp | 23 ++++++++++++++--------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/core/oslib/audiobackend_alsa.cpp b/core/oslib/audiobackend_alsa.cpp index 52223d895..47c4f348c 100644 --- a/core/oslib/audiobackend_alsa.cpp +++ b/core/oslib/audiobackend_alsa.cpp @@ -62,7 +62,7 @@ static void alsa_init() if (rc < 0) { - fprintf(stderr, "unable to open PCM device %s: %s\n", device.c_str(), snd_strerror(rc)); + fprintf(stderr, "ALSA: unable to open PCM device %s: %s\n", device.c_str(), snd_strerror(rc)); return; } @@ -75,7 +75,7 @@ static void alsa_init() rc=snd_pcm_hw_params_any(handle, params); if (rc < 0) { - fprintf(stderr, "Error:snd_pcm_hw_params_any %s\n", snd_strerror(rc)); + fprintf(stderr, "ALSA: Error:snd_pcm_hw_params_any %s\n", snd_strerror(rc)); return; } @@ -85,7 +85,7 @@ static void alsa_init() rc=snd_pcm_hw_params_set_access(handle, params, SND_PCM_ACCESS_RW_INTERLEAVED); if (rc < 0) { - fprintf(stderr, "Error:snd_pcm_hw_params_set_access %s\n", snd_strerror(rc)); + fprintf(stderr, "ALSA: Error:snd_pcm_hw_params_set_access %s\n", snd_strerror(rc)); return; } @@ -93,7 +93,7 @@ static void alsa_init() rc=snd_pcm_hw_params_set_format(handle, params, SND_PCM_FORMAT_S16_LE); if (rc < 0) { - fprintf(stderr, "Error:snd_pcm_hw_params_set_format %s\n", snd_strerror(rc)); + fprintf(stderr, "ALSA: Error:snd_pcm_hw_params_set_format %s\n", snd_strerror(rc)); return; } @@ -101,7 +101,7 @@ static void alsa_init() rc=snd_pcm_hw_params_set_channels(handle, params, 2); if (rc < 0) { - fprintf(stderr, "Error:snd_pcm_hw_params_set_channels %s\n", snd_strerror(rc)); + fprintf(stderr, "ALSA: Error:snd_pcm_hw_params_set_channels %s\n", snd_strerror(rc)); return; } @@ -110,7 +110,7 @@ static void alsa_init() rc=snd_pcm_hw_params_set_rate_near(handle, params, &val, &dir); if (rc < 0) { - fprintf(stderr, "Error:snd_pcm_hw_params_set_rate_near %s\n", snd_strerror(rc)); + fprintf(stderr, "ALSA: Error:snd_pcm_hw_params_set_rate_near %s\n", snd_strerror(rc)); return; } @@ -119,26 +119,31 @@ static void alsa_init() rc=snd_pcm_hw_params_set_period_size_near(handle, params, &period_size, &dir); if (rc < 0) { - fprintf(stderr, "Error:snd_pcm_hw_params_set_buffer_size_near %s\n", snd_strerror(rc)); + fprintf(stderr, "ALSA: Error:snd_pcm_hw_params_set_buffer_size_near %s\n", snd_strerror(rc)); return; } else + { printf("ALSA: period size set to %ld\n", period_size); + } + buffer_size = (44100 * 100 /* settings.omx.Audio_Latency */ / 1000 / period_size + 1) * period_size; rc=snd_pcm_hw_params_set_buffer_size_near(handle, params, &buffer_size); if (rc < 0) { - fprintf(stderr, "Error:snd_pcm_hw_params_set_buffer_size_near %s\n", snd_strerror(rc)); + fprintf(stderr, "ALSA: Error:snd_pcm_hw_params_set_buffer_size_near %s\n", snd_strerror(rc)); return; } else + { printf("ALSA: buffer size set to %ld\n", buffer_size); + } /* Write the parameters to the driver */ rc = snd_pcm_hw_params(handle, params); if (rc < 0) { - fprintf(stderr, "Unable to set hw parameters: %s\n", snd_strerror(rc)); + fprintf(stderr, "ALSA: Unable to set hw parameters: %s\n", snd_strerror(rc)); return; } } From 139ef22408679f255fdc107c313364c540936b3d Mon Sep 17 00:00:00 2001 From: "Christoph \"baka0815\" Schwerdtfeger" Date: Thu, 2 May 2019 19:02:34 +0200 Subject: [PATCH 264/319] AUDIO: compile fixes for coreaudio, dsound and libao --- core/oslib/audiobackend_coreaudio.cpp | 3 ++- core/oslib/audiobackend_directsound.cpp | 3 ++- core/oslib/audiobackend_libao.cpp | 3 ++- 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/core/oslib/audiobackend_coreaudio.cpp b/core/oslib/audiobackend_coreaudio.cpp index dd04926ce..8e0e9b344 100644 --- a/core/oslib/audiobackend_coreaudio.cpp +++ b/core/oslib/audiobackend_coreaudio.cpp @@ -159,7 +159,8 @@ audiobackend_t audiobackend_coreaudio = { "Core Audio", // Name &coreaudio_init, &coreaudio_push, - &coreaudio_term + &coreaudio_term, + NULL }; static bool core = RegisterAudioBackend(&audiobackend_coreaudio); diff --git a/core/oslib/audiobackend_directsound.cpp b/core/oslib/audiobackend_directsound.cpp index d173e434b..f004bdfd2 100644 --- a/core/oslib/audiobackend_directsound.cpp +++ b/core/oslib/audiobackend_directsound.cpp @@ -185,7 +185,8 @@ audiobackend_t audiobackend_directsound = { "Microsoft DirectSound", // Name &directsound_init, &directsound_push, - &directsound_term + &directsound_term, + NULL }; static bool ds = RegisterAudioBackend(&audiobackend_directsound); diff --git a/core/oslib/audiobackend_libao.cpp b/core/oslib/audiobackend_libao.cpp index c235dca00..0a6a72e7d 100644 --- a/core/oslib/audiobackend_libao.cpp +++ b/core/oslib/audiobackend_libao.cpp @@ -43,7 +43,8 @@ audiobackend_t audiobackend_libao = { "libao", // Name &libao_init, &libao_push, - &libao_term + &libao_term, + NULL }; static bool ao = RegisterAudioBackend(&audiobackend_libao); From 99033e297c604934dece7265d0e952740d7453a8 Mon Sep 17 00:00:00 2001 From: "Christoph \"baka0815\" Schwerdtfeger" Date: Thu, 2 May 2019 20:24:49 +0200 Subject: [PATCH 265/319] AUDIO: Implement integer and checkbox options I remove "text" as a possibility for the moment as we're currently not having **any** text option. --- core/oslib/audiostream.h | 4 ++-- core/rend/gui.cpp | 18 +++++++++++++++++- 2 files changed, 19 insertions(+), 3 deletions(-) diff --git a/core/oslib/audiostream.h b/core/oslib/audiostream.h index 37c1cbc43..408592b55 100644 --- a/core/oslib/audiostream.h +++ b/core/oslib/audiostream.h @@ -14,8 +14,8 @@ void UpdateBuff(u8* pos); typedef std::vector (*audio_option_callback_t)(); enum audio_option_type { - text = 0 -, integer = 1 + integer = 0 +, checkbox = 1 , list = 2 }; diff --git a/core/rend/gui.cpp b/core/rend/gui.cpp index 0404b503a..b6f0b30aa 100644 --- a/core/rend/gui.cpp +++ b/core/rend/gui.cpp @@ -1099,7 +1099,20 @@ static void gui_display_settings() } value = (*cfg_entries)[options->cfg_name]; - if (options->type == list) + if (options->type == integer) + { + int val = stoi(value); + ImGui::SliderInt(options->caption.c_str(), &val, options->min_value, options->max_value); + (*cfg_entries)[options->cfg_name] = to_string(val); + } + else if (options->type == checkbox) + { + bool check = (value == "1"); + ImGui::Checkbox(options->caption.c_str(), &check); + std::string cur = check ? "1" : "0"; + (*cfg_entries)[options->cfg_name] = cur; + } + else if (options->type == list) { if (ImGui::BeginCombo(options->caption.c_str(), value.c_str(), ImGuiComboFlags_None)) { @@ -1120,6 +1133,9 @@ static void gui_display_settings() ImGui::EndCombo(); } } + else { + printf("Unknown option\n"); + } options++; } From c330cdd88e4d302adc99a887984fecc861df812b Mon Sep 17 00:00:00 2001 From: Flyinghead Date: Mon, 6 May 2019 15:23:54 +0200 Subject: [PATCH 266/319] fix scissor test and vertical scaling when using the Y scaler Fixes missing bottom half-screen in Mr Driller (PAL, 60 Hz), Kaen Seibo, Mahjong Taikai II Special, Pon'n'Music 1 and 2 when in TV Composite and RGB. Fixes stretched screen in Cho - Hatsumei Boy Kanipan. --- core/rend/gl4/gles.cpp | 8 ++++++++ core/rend/gles/gles.cpp | 8 ++++++++ 2 files changed, 16 insertions(+) diff --git a/core/rend/gl4/gles.cpp b/core/rend/gl4/gles.cpp index aa2da9c99..202d48b31 100644 --- a/core/rend/gl4/gles.cpp +++ b/core/rend/gl4/gles.cpp @@ -656,6 +656,8 @@ static bool RenderFrame() { scale_x=fb_scale_x; scale_y=fb_scale_y; + if (SCALER_CTL.interlace == 0) + scale_y *= SCALER_CTL.vscalefactor / 0x400; //work out scaling parameters ! //Pixel doubling is on VO, so it does not affect any pixel operations @@ -868,6 +870,12 @@ static bool RenderFrame() float min_y = pvrrc.fb_Y_CLIP.min / scale_y; if (!is_rtt) { + if (SCALER_CTL.interlace) + { + // Clipping is done after scaling/filtering so account for that if enabled + height *= SCALER_CTL.vscalefactor / 0x400; + min_y *= SCALER_CTL.vscalefactor / 0x400; + } if (settings.rend.Rotate90) { float t = width; diff --git a/core/rend/gles/gles.cpp b/core/rend/gles/gles.cpp index a2155f460..684f96e26 100644 --- a/core/rend/gles/gles.cpp +++ b/core/rend/gles/gles.cpp @@ -1651,6 +1651,8 @@ bool RenderFrame() { scale_x=fb_scale_x; scale_y=fb_scale_y; + if (SCALER_CTL.interlace == 0) + scale_y *= SCALER_CTL.vscalefactor / 0x400; //work out scaling parameters ! //Pixel doubling is on VO, so it does not affect any pixel operations @@ -1900,6 +1902,12 @@ bool RenderFrame() float min_y = pvrrc.fb_Y_CLIP.min / scale_y; if (!is_rtt) { + if (SCALER_CTL.interlace) + { + // Clipping is done after scaling/filtering so account for that if enabled + height *= SCALER_CTL.vscalefactor / 0x400; + min_y *= SCALER_CTL.vscalefactor / 0x400; + } if (settings.rend.Rotate90) { float t = width; From ed8bcd7329c46747585c0570d6a5609226a61476 Mon Sep 17 00:00:00 2001 From: David Guillen Fandos Date: Wed, 8 May 2019 20:47:58 +0200 Subject: [PATCH 267/319] Add SDL2 audio backend. The backend supports 44.1KHz and 48KHz (with resamping). The resampler is not great, has some noise but no idea where it comes from. This enables the switch port, since using SDL2 is the quickest way to get audio working. TODO: Add support in the cmake, once cmake is fixed at master/HEAD. --- core/oslib/audiobackend_sdl2.cpp | 136 +++++++++++++++++++++++++++++++ shell/linux/Makefile | 6 ++ 2 files changed, 142 insertions(+) create mode 100644 core/oslib/audiobackend_sdl2.cpp diff --git a/core/oslib/audiobackend_sdl2.cpp b/core/oslib/audiobackend_sdl2.cpp new file mode 100644 index 000000000..39d2e8464 --- /dev/null +++ b/core/oslib/audiobackend_sdl2.cpp @@ -0,0 +1,136 @@ + +#if defined(USE_SDL_AUDIO) + +#include +#include "oslib/audiostream.h" +#include "stdclass.h" + +static SDL_AudioDeviceID audiodev; +static bool needs_resampling; +static cResetEvent read_wait; +static cMutex stream_mutex; +static struct { + uint32_t prevs; + uint32_t sample_buffer[2048]; +} audiobuf; +static unsigned sample_count = 0; + +// To easily access samples. +union Sample { int16_t s[2]; uint32_t l; }; + +static float InterpolateCatmull4pt3oX(float x0, float x1, float x2, float x3, float t) { + return 0.45 * ((2 * x1) + t * ((-x0 + x2) + t * ((2 * x0 - 5 * x1 + 4 * x2 - x3) + t * (-x0 + 3 * x1 - 3 * x2 + x3)))); +} + +static void sdl2_audiocb(void* userdata, Uint8* stream, int len) { + stream_mutex.Lock(); + // Wait until there's enough samples to feed the kraken + unsigned oslen = len / sizeof(uint32_t); + unsigned islen = needs_resampling ? oslen * 16 / 17 : oslen; + unsigned minlen = needs_resampling ? islen + 2 : islen; // Resampler looks ahead by 2 samples. + + if (sample_count < minlen) { + // No data, just output a bit of silence for the underrun + memset(stream, 0, len); + stream_mutex.Unlock(); + read_wait.Set(); + return; + } + + if (!needs_resampling) { + // Just copy bytes for this case. + memcpy(stream, &audiobuf.sample_buffer[0], len); + } + else { + // 44.1KHz to 48KHz (actually 46.86KHz) resampling + uint32_t *outbuf = (uint32_t*)stream; + const float ra = 1.0f / 17; + Sample *sbuf = (Sample*)&audiobuf.sample_buffer[0]; // [-1] stores the previous iteration last sample output + for (int i = 0; i < islen/16; i++) { + *outbuf++ = sbuf[i*16+ 0].l; // First sample stays at the same location. + for (int k = 1; k < 17; k++) { + Sample r; + // Note we access offset -1 on first iteration, as to access prevs + r.s[0] = InterpolateCatmull4pt3oX(sbuf[i*16+k-2].s[0], sbuf[i*16+k-1].s[0], sbuf[i*16+k].s[0], sbuf[i*16+k+1].s[0], 1 - ra*k); + r.s[1] = InterpolateCatmull4pt3oX(sbuf[i*16+k-2].s[1], sbuf[i*16+k-1].s[1], sbuf[i*16+k].s[1], sbuf[i*16+k+1].s[1], 1 - ra*k); + *outbuf++ = r.l; + } + } + audiobuf.prevs = audiobuf.sample_buffer[islen-1]; + } + + // Move samples in the buffer and consume them + memmove(&audiobuf.sample_buffer[0], &audiobuf.sample_buffer[islen], (sample_count-islen)*sizeof(uint32_t)); + sample_count -= islen; + + stream_mutex.Unlock(); + read_wait.Set(); +} + +static void sdl2_audio_init() { + if (!SDL_WasInit(SDL_INIT_AUDIO)) + SDL_InitSubSystem(SDL_INIT_AUDIO); + + // Support 44.1KHz (native) but also upsampling to 48KHz + SDL_AudioSpec wav_spec, out_spec; + memset(&wav_spec, 0, sizeof(wav_spec)); + wav_spec.freq = 44100; + wav_spec.format = AUDIO_S16; + wav_spec.channels = 2; + wav_spec.samples = 1024; // Must be power of two + wav_spec.callback = sdl2_audiocb; + + // Try 44.1KHz which should be faster since it's native. + audiodev = SDL_OpenAudioDevice(NULL, 0, &wav_spec, &out_spec, 0); + if (!audiodev) { + needs_resampling = true; + wav_spec.freq = 48000; + audiodev = SDL_OpenAudioDevice(NULL, 0, &wav_spec, &out_spec, 0); + verify(audiodev); + } +} + +static u32 sdl2_audio_push(void* frame, u32 samples, bool wait) { + // Unpause the device shall it be paused. + if (SDL_GetAudioDeviceStatus(audiodev) != SDL_AUDIO_PLAYING) + SDL_PauseAudioDevice(audiodev, 0); + + // If wait, then wait for the buffer to be smaller than a certain size. + stream_mutex.Lock(); + if (wait) { + while (sample_count + samples > sizeof(audiobuf.sample_buffer)/sizeof(audiobuf.sample_buffer[0])) { + stream_mutex.Unlock(); + read_wait.Wait(); + read_wait.Reset(); + stream_mutex.Lock(); + } + } + + // Copy as many samples as possible, drop any remaining (this should not happen usually) + unsigned free_samples = sizeof(audiobuf.sample_buffer) / sizeof(audiobuf.sample_buffer[0]) - sample_count; + unsigned tocopy = samples < free_samples ? samples : free_samples; + memcpy(&audiobuf.sample_buffer[sample_count], frame, tocopy * sizeof(uint32_t)); + sample_count += tocopy; + stream_mutex.Unlock(); + + return 1; +} + +static void sdl2_audio_term() { + // Stop audio playback. + SDL_PauseAudioDevice(audiodev, 1); + read_wait.Set(); +} + +audiobackend_t audiobackend_sdl2audio = { + "sdl2", // Slug + "Simple DirectMedia Layer 2 Audio", // Name + &sdl2_audio_init, + &sdl2_audio_push, + &sdl2_audio_term +}; + +static bool sdl2audiobe = RegisterAudioBackend(&audiobackend_sdl2audio); + +#endif + diff --git a/shell/linux/Makefile b/shell/linux/Makefile index 6af3aed05..b96a05891 100644 --- a/shell/linux/Makefile +++ b/shell/linux/Makefile @@ -6,6 +6,7 @@ FOR_LINUX :=1 WEBUI :=1 USE_OSS := 1 #USE_PULSEAUDIO := 1 +#USE_SDLAUDIO := 1 #USE_LIBAO := 1 USE_EVDEV := 1 USE_UDEV := 1 @@ -360,6 +361,11 @@ ifdef USE_PULSEAUDIO LIBS += `pkg-config --libs libpulse-simple` endif +ifdef USE_SDLAUDIO + CXXFLAGS += `sdl2-config --cflags` -D USE_SDL_AUDIO + LIBS += `sdl2-config --libs` +endif + ifdef USE_LIBAO CXXFLAGS += `pkg-config --cflags ao` -D USE_LIBAO LIBS += `pkg-config --libs ao` From 3cd9736fcaf32c0d81cd44f02cb30203275eed0e Mon Sep 17 00:00:00 2001 From: Flyinghead Date: Wed, 8 May 2019 21:33:49 +0200 Subject: [PATCH 268/319] Ignore vscalefactor if < 1 fix black screen on some intel platforms --- core/rend/gl4/gles.cpp | 4 ++-- core/rend/gles/gles.cpp | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/core/rend/gl4/gles.cpp b/core/rend/gl4/gles.cpp index 202d48b31..4d3003569 100644 --- a/core/rend/gl4/gles.cpp +++ b/core/rend/gl4/gles.cpp @@ -656,7 +656,7 @@ static bool RenderFrame() { scale_x=fb_scale_x; scale_y=fb_scale_y; - if (SCALER_CTL.interlace == 0) + if (SCALER_CTL.interlace == 0 && SCALER_CTL.vscalefactor >= 0x400) scale_y *= SCALER_CTL.vscalefactor / 0x400; //work out scaling parameters ! @@ -870,7 +870,7 @@ static bool RenderFrame() float min_y = pvrrc.fb_Y_CLIP.min / scale_y; if (!is_rtt) { - if (SCALER_CTL.interlace) + if (SCALER_CTL.interlace && SCALER_CTL.vscalefactor >= 0x400) { // Clipping is done after scaling/filtering so account for that if enabled height *= SCALER_CTL.vscalefactor / 0x400; diff --git a/core/rend/gles/gles.cpp b/core/rend/gles/gles.cpp index 684f96e26..9e7c812a2 100644 --- a/core/rend/gles/gles.cpp +++ b/core/rend/gles/gles.cpp @@ -1651,7 +1651,7 @@ bool RenderFrame() { scale_x=fb_scale_x; scale_y=fb_scale_y; - if (SCALER_CTL.interlace == 0) + if (SCALER_CTL.interlace == 0 && SCALER_CTL.vscalefactor >= 0x400) scale_y *= SCALER_CTL.vscalefactor / 0x400; //work out scaling parameters ! @@ -1902,7 +1902,7 @@ bool RenderFrame() float min_y = pvrrc.fb_Y_CLIP.min / scale_y; if (!is_rtt) { - if (SCALER_CTL.interlace) + if (SCALER_CTL.interlace && SCALER_CTL.vscalefactor >= 0x400) { // Clipping is done after scaling/filtering so account for that if enabled height *= SCALER_CTL.vscalefactor / 0x400; From 0eb874ba1c973994eeab5da9f486e4c15def3e71 Mon Sep 17 00:00:00 2001 From: David GF Date: Wed, 8 May 2019 22:33:14 +0200 Subject: [PATCH 269/319] Adding ARM64 Linux target in Makefile --- shell/linux/Makefile | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/shell/linux/Makefile b/shell/linux/Makefile index 6af3aed05..58ba615fc 100644 --- a/shell/linux/Makefile +++ b/shell/linux/Makefile @@ -45,6 +45,7 @@ ifeq (,$(platform)) else ifeq ($(ARCH), $(filter $(ARCH), x86_64 AMD64 amd64)) platform = x64 else ifneq (,$(findstring aarch64,$(ARCH))) + platform = arm64 HARDWARE = $(shell grep Hardware /proc/cpuinfo) ifneq (,$(findstring Vero4K,$(HARDWARE))) platform = vero4k @@ -122,6 +123,15 @@ else ifneq (,$(findstring x64,$(platform))) HAS_SOFTREND := 1 endif +# Generic 64 bit ARM (armv8) Linux +else ifneq (,$(findstring arm64,$(platform))) + NOT_ARM := 1 + USE_X11 := 1 + ARM64_REC := 1 + ISARM64 := 1 + CFLAGS += -D TARGET_LINUX_ARMv8 -D TARGET_NO_AREC -fno-builtin-sqrtf + CXXFLAGS += -fexceptions + # Generic 32 bit ARMhf (a.k.a. ARMv7h) else ifneq (,$(findstring armv7h,$(platform))) MFLAGS += -marm -mfloat-abi=hard -march=armv7-a -funroll-loops From 5ba56627a9c37db320b3743f2cc0143e9ed0e0f7 Mon Sep 17 00:00:00 2001 From: David GF Date: Wed, 8 May 2019 23:53:56 +0200 Subject: [PATCH 270/319] Optimize rec-ARM64 by emitting less instructions on loads. Fast load uses 3 or 4 insts, whereas slow load takes 2+. On A57 measured ~3% perf, which is expected for an OOO CPU, hopefully perf will be much better on A53 and other in-order CPUs. --- core/rec-ARM64/rec_arm64.cpp | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/core/rec-ARM64/rec_arm64.cpp b/core/rec-ARM64/rec_arm64.cpp index b30720796..2a9108349 100644 --- a/core/rec-ARM64/rec_arm64.cpp +++ b/core/rec-ARM64/rec_arm64.cpp @@ -1420,9 +1420,12 @@ private: std::vector call_fregs; Arm64RegAlloc regalloc; RuntimeBlockInfo* block; + const int write_memory_rewrite_size = 3; // same size (fast write) for any size: add, bfc, str + #ifdef EXPLODE_SPANS const int read_memory_rewrite_size = 6; // worst case for u64: add, bfc, ldr, fmov, lsr, fmov - // FIXME rewrite size per read/write size? - const int write_memory_rewrite_size = 3; + #else + const int read_memory_rewrite_size = 4; // worst case for u64: add, bfc, ldr, str + #endif }; static Arm64Assembler* compiler; From eb725f9e66da28568d96e9649aa9ba30fa4d5d4c Mon Sep 17 00:00:00 2001 From: Flyinghead Date: Fri, 10 May 2019 11:15:44 +0200 Subject: [PATCH 271/319] x11: fix fallback to gl 3.0. fix crash when switching renderer. Fallback to GL 3.1 when GL 4.3 isn't available was broken. Fallback to GL 3.0 instead of 3.1. Fix crash when switching renderer (per-pixel, per-triangle) --- core/hw/pvr/Renderer_if.cpp | 18 ++++++++++-------- core/linux-dist/x11.cpp | 13 ++++++++++--- 2 files changed, 20 insertions(+), 11 deletions(-) diff --git a/core/hw/pvr/Renderer_if.cpp b/core/hw/pvr/Renderer_if.cpp index 0a9c5b16b..71c954e34 100644 --- a/core/hw/pvr/Renderer_if.cpp +++ b/core/hw/pvr/Renderer_if.cpp @@ -98,6 +98,7 @@ TA_context* _pvrrc; void SetREP(TA_context* cntx); void killtex(); bool render_output_framebuffer(); +static void rend_create_renderer(); void dump_frame(const char* file, TA_context* ctx, u8* vram, u8* vram_ref = NULL) { FILE* fw = fopen(file, "wb"); @@ -266,6 +267,13 @@ bool rend_frame(TA_context* ctx, bool draw_osd) { bool rend_single_frame() { + if (renderer_changed) + { + renderer_changed = false; + rend_term_renderer(); + rend_create_renderer(); + rend_init_renderer(); + } //wait render start only if no frame pending do { @@ -362,6 +370,7 @@ void rend_init_renderer() } printf("Selected renderer initialization failed. Falling back to default renderer.\n"); renderer = fallback_renderer; + fallback_renderer = NULL; // avoid double-free } } @@ -377,7 +386,6 @@ void rend_term_renderer() delete fallback_renderer; fallback_renderer = NULL; } - tactx_Term(); } void* rend_thread(void* p) @@ -391,13 +399,6 @@ void* rend_thread(void* p) { if (rend_single_frame()) renderer->Present(); - if (renderer_changed) - { - renderer_changed = false; - rend_term_renderer(); - rend_create_renderer(); - rend_init_renderer(); - } } rend_term_renderer(); @@ -538,6 +539,7 @@ void rend_end_render() void rend_stop_renderer() { renderer_enabled = false; + tactx_Term(); } void rend_vblank() diff --git a/core/linux-dist/x11.cpp b/core/linux-dist/x11.cpp index aa71bdbdf..e053097be 100644 --- a/core/linux-dist/x11.cpp +++ b/core/linux-dist/x11.cpp @@ -361,6 +361,11 @@ void input_x11_init() printf("X11 Keyboard input disabled by config.\n"); } +static int x11_error_handler(Display *, XErrorEvent *) +{ + return 0; +} + void x11_window_create() { if (cfgLoadInt("pvr", "nox11", 0) == 0) @@ -519,20 +524,22 @@ void x11_window_create() GLX_CONTEXT_PROFILE_MASK_ARB, GLX_CONTEXT_CORE_PROFILE_BIT_ARB, None }; + int (*old_handler)(Display *, XErrorEvent *) = XSetErrorHandler(&x11_error_handler); x11_glc = glXCreateContextAttribsARB(x11Display, bestFbc, 0, True, context_attribs); if (!x11_glc) { printf("Open GL 4.3 not supported\n"); - // Try GL 3.1 + // Try GL 3.0 context_attribs[1] = 3; - context_attribs[3] = 1; + context_attribs[3] = 0; x11_glc = glXCreateContextAttribsARB(x11Display, bestFbc, 0, True, context_attribs); if (!x11_glc) { - die("Open GL 3.1 not supported\n"); + die("Open GL 3.0 not supported\n"); } } + XSetErrorHandler(old_handler); XSync(x11Display, False); #endif From be1ecbaa8ba9124807dda1ed21b1a9cfa38c4a1e Mon Sep 17 00:00:00 2001 From: David Guillen Fandos Date: Fri, 10 May 2019 18:57:28 +0200 Subject: [PATCH 272/319] Fix TARGET_NO_NVMEM and deprecate TARGET_NO_EXCEPTIONS Linked them both toghether since you can't really define one and not the other (plus Linux honors one windows the other in some cases). More refactoring on this area to follow. --- core/hw/mem/_vmem.cpp | 2 +- core/hw/sh4/dyna/blockmanager.cpp | 4 ++-- core/linux/common.cpp | 17 ++++++++--------- core/linux/context.cpp | 4 ++-- shell/cmake/config.cmake | 4 ++-- shell/emscripten/Makefile | 2 +- shell/nacl/Makefile | 4 ++-- 7 files changed, 18 insertions(+), 19 deletions(-) diff --git a/core/hw/mem/_vmem.cpp b/core/hw/mem/_vmem.cpp index 2c02197c5..e621fa948 100644 --- a/core/hw/mem/_vmem.cpp +++ b/core/hw/mem/_vmem.cpp @@ -440,7 +440,7 @@ void _vmem_bm_reset() { if (!virt_ram_base) #endif { - bm_vmem_pagefill((void**)p_sh4rcb->fpcb, FPCB_SIZE); + bm_vmem_pagefill((void**)p_sh4rcb->fpcb, sizeof(p_sh4rcb->fpcb)); } } diff --git a/core/hw/sh4/dyna/blockmanager.cpp b/core/hw/sh4/dyna/blockmanager.cpp index e64cd0d60..0994c3469 100644 --- a/core/hw/sh4/dyna/blockmanager.cpp +++ b/core/hw/sh4/dyna/blockmanager.cpp @@ -339,9 +339,9 @@ void bm_Rebuild() rebuild_counter=30; } -void bm_vmem_pagefill(void** ptr,u32 PAGE_SZ) +void bm_vmem_pagefill(void** ptr, u32 size_bytes) { - for (size_t i=0; i void VArray2::LockRegion(u32 offset,u32 size) { - #if !defined(TARGET_NO_EXCEPTIONS) + #if !defined(TARGET_NO_NVMEM) u32 inpage=offset & PAGE_MASK; u32 rv=mprotect (data+offset-inpage, size+inpage, PROT_READ ); if (rv!=0) @@ -185,7 +184,7 @@ void print_mem_addr() void VArray2::UnLockRegion(u32 offset,u32 size) { - #if !defined(TARGET_NO_EXCEPTIONS) + #if !defined(TARGET_NO_NVMEM) u32 inpage=offset & PAGE_MASK; u32 rv=mprotect (data+offset-inpage, size+inpage, PROT_READ | PROT_WRITE); if (rv!=0) diff --git a/core/linux/context.cpp b/core/linux/context.cpp index 7c317c401..9884ad426 100644 --- a/core/linux/context.cpp +++ b/core/linux/context.cpp @@ -8,7 +8,7 @@ #define __USE_GNU 1 #endif - #if !defined(TARGET_NO_EXCEPTIONS) + #if !defined(TARGET_NO_NVMEM) #include #endif #endif @@ -29,7 +29,7 @@ void bicopy(Ta& rei, Tb& seg, bool to_segfault) { void context_segfault(rei_host_context_t* reictx, void* segfault_ctx, bool to_segfault) { -#if !defined(TARGET_NO_EXCEPTIONS) +#if !defined(TARGET_NO_NVMEM) #if HOST_CPU == CPU_ARM #if defined(__FreeBSD__) bicopy(reictx->pc, MCTX(.__gregs[_REG_PC]), to_segfault); diff --git a/shell/cmake/config.cmake b/shell/cmake/config.cmake index d1dfd59e4..056b7e74a 100644 --- a/shell/cmake/config.cmake +++ b/shell/cmake/config.cmake @@ -343,7 +343,7 @@ if (TARGET_NSW) # -DCMAKE_TOOLCHAIN_FILE=./cmake/devkitA64.cmake -DTARGET_NSW=ON message("HOST_OS ${HOST_OS}") add_definitions(-D__SWITCH__ -DGLES -DMESA_EGL_NO_X11_HEADERS) - add_definitions(-DTARGET_NO_THREADS -DTARGET_NO_EXCEPTIONS -DTARGET_NO_NIXPROF) + add_definitions(-DTARGET_NO_THREADS -DTARGET_NO_NVMEM -DTARGET_NO_NIXPROF) add_definitions(-DTARGET_NO_COREIO_HTTP -DTARGET_NO_WEBUI -UTARGET_SOFTREND) add_definitions(-D_GLIBCXX_USE_C99_MATH_TR1 -D_LDBL_EQ_DBL) @@ -355,7 +355,7 @@ if (TARGET_PS4) # -DCMAKE_TOOLCHAIN_FILE=./cmake/{ps4sdk,clang_scei}.cmake -DTAR add_definitions(-DPS4 -DTARGET_PS4 -DTARGET_BSD -D__ORBIS__ -DGLES -DMESA_EGL_NO_X11_HEADERS) ## last needed for __unix__ on eglplatform.h - add_definitions(-DTARGET_NO_THREADS -DTARGET_NO_EXCEPTIONS -DTARGET_NO_NIXPROF) + add_definitions(-DTARGET_NO_THREADS -DTARGET_NO_NVMEM -DTARGET_NO_NIXPROF) add_definitions(-DTARGET_NO_COREIO_HTTP -DTARGET_NO_WEBUI -UTARGET_SOFTREND) diff --git a/shell/emscripten/Makefile b/shell/emscripten/Makefile index 6f1054198..1c74e19b6 100644 --- a/shell/emscripten/Makefile +++ b/shell/emscripten/Makefile @@ -26,7 +26,7 @@ LDFLAGS := -Wl,-Map,$(notdir $@).map,--gc-sections -Wl,-O3 -Wl,--sort-common CXXONLYFLAGS := -std=c++11 -CXXFLAGS := -O3 -D GLES -D RELEASE -c -D TARGET_EMSCRIPTEN -D TARGET_NO_REC -D TARGET_NO_NVMEM -D TARGET_NO_WEBUI -D TARGET_NO_THREADS -D TARGET_BOUNDED_EXECUTION -D TARGET_NO_EXCEPTIONS -D TARGET_NO_COREIO_HTTP +CXXFLAGS := -O3 -D GLES -D RELEASE -c -D TARGET_EMSCRIPTEN -D TARGET_NO_REC -D TARGET_NO_NVMEM -D TARGET_NO_WEBUI -D TARGET_NO_THREADS -D TARGET_BOUNDED_EXECUTION -D TARGET_NO_COREIO_HTTP CXXFLAGS += -fno-strict-aliasing CXXFLAGS += -ffast-math diff --git a/shell/nacl/Makefile b/shell/nacl/Makefile index 733a7d538..e0d03834e 100644 --- a/shell/nacl/Makefile +++ b/shell/nacl/Makefile @@ -23,7 +23,7 @@ CFLAGS = -Wno-error -Wno-ignored-attributes CFLAGS += -O3 -fno-strict-aliasing -ffast-math CFLAGS += -I$(RZDCY_SRC_DIR) -I$(RZDCY_SRC_DIR)/deps CFLAGS += -D RELEASE -D TARGET_NO_JIT -D TARGET_NACL32 -DGLES -CFLAGS += -D TARGET_NO_EXCEPTIONS -D TARGET_NO_NVMEM -D TARGET_NO_WEBUI -D TARGET_NO_COREIO_HTTP +CFLAGS += -D TARGET_NO_NVMEM -D TARGET_NO_WEBUI -D TARGET_NO_COREIO_HTTP SOURCES = $(RZDCY_FILES) ../../core/nacl/nacl.cpp @@ -49,4 +49,4 @@ endif SHELL = sh -$(eval $(call NMF_RULE,$(TARGET),)) \ No newline at end of file +$(eval $(call NMF_RULE,$(TARGET),)) From 45b0e79f15c07b22566f5a96814ac8b3896d9f74 Mon Sep 17 00:00:00 2001 From: David Guillen Fandos Date: Fri, 10 May 2019 19:20:19 +0200 Subject: [PATCH 273/319] Enable modem support in Android builds, regresion of PR #1571 This affects bug #1591 --- shell/android-studio/reicast/src/main/jni/Android.mk | 1 + 1 file changed, 1 insertion(+) diff --git a/shell/android-studio/reicast/src/main/jni/Android.mk b/shell/android-studio/reicast/src/main/jni/Android.mk index 9bb894c11..0eb55d90a 100644 --- a/shell/android-studio/reicast/src/main/jni/Android.mk +++ b/shell/android-studio/reicast/src/main/jni/Android.mk @@ -22,6 +22,7 @@ WEBUI := 1 USE_GLES := 1 CHD5_LZMA := 1 CHD5_FLAC := 1 +USE_MODEM := 1 ifneq ($(TARGET_ARCH_ABI),armeabi-v7a) NOT_ARM := 1 From 2389903a30885367b3245443e7175fada01ef028 Mon Sep 17 00:00:00 2001 From: David Guillen Fandos Date: Fri, 10 May 2019 19:38:45 +0200 Subject: [PATCH 274/319] Add dummy serialization for non-modem builds (right now only Win) --- core/serialize.cpp | 85 +++++++++------------------------------------- 1 file changed, 16 insertions(+), 69 deletions(-) diff --git a/core/serialize.cpp b/core/serialize.cpp index 1d7fec35b..f9a40da7f 100644 --- a/core/serialize.cpp +++ b/core/serialize.cpp @@ -1050,18 +1050,19 @@ bool dc_serialize(void **data, unsigned int *total_size) REICAST_S(sch_list[modem_sched].tag) ; REICAST_S(sch_list[modem_sched].start) ; REICAST_S(sch_list[modem_sched].end) ; + #else + int modem_dummy = 0; + REICAST_S(modem_dummy); + REICAST_S(modem_dummy); + REICAST_S(modem_dummy); #endif REICAST_S(SCIF_SCFSR2); REICAST_S(SCIF_SCFRDR2); REICAST_S(SCIF_SCFDR2); - REICAST_S(BSC_PDTRA); - - - REICAST_SA(tmu_shift,3); REICAST_SA(tmu_mask,3); REICAST_SA(tmu_mask64,3); @@ -1069,14 +1070,8 @@ bool dc_serialize(void **data, unsigned int *total_size) REICAST_SA(tmu_ch_base,3); REICAST_SA(tmu_ch_base64,3); - - - REICAST_SA(CCN_QACR_TR,2); - - - REICAST_SA(UTLB,64); REICAST_SA(ITLB,4); #if defined(NO_MMU) @@ -1086,8 +1081,6 @@ bool dc_serialize(void **data, unsigned int *total_size) REICAST_S(mmu_error_TT); #endif - - REICAST_S(NullDriveDiscType); REICAST_SA(q_subchannel,96); @@ -1129,22 +1122,16 @@ bool dc_serialize(void **data, unsigned int *total_size) REICAST_S(div_som_reg2); REICAST_S(div_som_reg3); - - REICAST_S(LastAddr); REICAST_S(LastAddr_min); REICAST_SA(block_hash,1024); - REICAST_SA(RegisterWrite,sh4_reg_count); REICAST_SA(RegisterRead,sh4_reg_count); REICAST_S(fallback_blocks); REICAST_S(total_blocks); REICAST_S(REMOVED_OPS); - - - REICAST_SA(kcode,4); REICAST_SA(rt,4); REICAST_SA(lt,4); @@ -1245,12 +1232,7 @@ static bool dc_unserialize_libretro(void **data, unsigned int *total_size) //this is one-time init, no updates - don't need to serialize //extern _vmem_handler area0_handler; - - - - REICAST_USA(reply_11,16) ; - - + REICAST_USA(reply_11,16); REICAST_US(sns_asc); REICAST_US(sns_ascq); @@ -1364,8 +1346,6 @@ static bool dc_unserialize_libretro(void **data, unsigned int *total_size) REICAST_USA(mem_b.data, mem_b.size); - - REICAST_US(IRLPriority); REICAST_USA(InterruptEnvId,32); REICAST_USA(InterruptBit,32); @@ -1374,9 +1354,6 @@ static bool dc_unserialize_libretro(void **data, unsigned int *total_size) REICAST_US(interrupt_vmask); REICAST_US(decoded_srimask); - - - REICAST_US(i) ; if ( i == 0 ) do_sqw_nommu = &do_sqw_nommu_area_3 ; @@ -1401,9 +1378,6 @@ static bool dc_unserialize_libretro(void **data, unsigned int *total_size) REICAST_US(old_rm); REICAST_US(old_dn); - - - REICAST_US(sh4_sched_ffb); REICAST_US(sh4_sched_intr); @@ -1452,18 +1426,19 @@ static bool dc_unserialize_libretro(void **data, unsigned int *total_size) REICAST_US(sch_list[modem_sched].tag) ; REICAST_US(sch_list[modem_sched].start) ; REICAST_US(sch_list[modem_sched].end) ; + #else + int modem_dummy; + REICAST_US(modem_dummy); + REICAST_US(modem_dummy); + REICAST_US(modem_dummy); #endif REICAST_US(SCIF_SCFSR2); REICAST_US(SCIF_SCFRDR2); REICAST_US(SCIF_SCFDR2); - REICAST_US(BSC_PDTRA); - - - REICAST_USA(tmu_shift,3); REICAST_USA(tmu_mask,3); REICAST_USA(tmu_mask64,3); @@ -1471,14 +1446,8 @@ static bool dc_unserialize_libretro(void **data, unsigned int *total_size) REICAST_USA(tmu_ch_base,3); REICAST_USA(tmu_ch_base64,3); - - - REICAST_USA(CCN_QACR_TR,2); - - - REICAST_USA(UTLB,64); REICAST_USA(ITLB,4); #if defined(NO_MMU) @@ -1488,9 +1457,6 @@ static bool dc_unserialize_libretro(void **data, unsigned int *total_size) REICAST_US(mmu_error_TT); #endif - - - REICAST_US(NullDriveDiscType); REICAST_USA(q_subchannel,96); @@ -1545,9 +1511,6 @@ static bool dc_unserialize_libretro(void **data, unsigned int *total_size) REICAST_US(div_som_reg2); REICAST_US(div_som_reg3); - - - //REICAST_USA(CodeCache,CODE_SIZE) ; //REICAST_USA(SH4_TCB,CODE_SIZE+4096); REICAST_US(LastAddr); @@ -1611,7 +1574,6 @@ bool dc_unserialize(void **data, unsigned int *total_size) REICAST_US(timers[i].m_step); } - REICAST_USA(aica_ram.data,aica_ram.size) ; REICAST_US(VREG); REICAST_US(ARMRST); @@ -1619,8 +1581,6 @@ bool dc_unserialize(void **data, unsigned int *total_size) REICAST_USA(aica_reg,0x8000); - - REICAST_USA(volume_lut,16); REICAST_USA(tl_lut,256 + 768); REICAST_USA(AEG_ATT_SPS,64); @@ -1635,14 +1595,11 @@ bool dc_unserialize(void **data, unsigned int *total_size) REICAST_USA(mxlr,64); REICAST_US(samples_gen); - register_unserialize(sb_regs, data, total_size) ; REICAST_US(SB_ISTNRM); REICAST_US(SB_FFST_rc); REICAST_US(SB_FFST); - - //this is one-time init, no updates - don't need to serialize //extern RomChip sys_rom; REICAST_US(sys_nvmem.size); @@ -1754,8 +1711,6 @@ bool dc_unserialize(void **data, unsigned int *total_size) REICAST_USA(mem_b.data, mem_b.size); - - REICAST_US(IRLPriority); REICAST_USA(InterruptEnvId,32); REICAST_USA(InterruptBit,32); @@ -1764,9 +1719,6 @@ bool dc_unserialize(void **data, unsigned int *total_size) REICAST_US(interrupt_vmask); REICAST_US(decoded_srimask); - - - REICAST_US(i) ; if ( i == 0 ) do_sqw_nommu = &do_sqw_nommu_area_3 ; @@ -1842,6 +1794,11 @@ bool dc_unserialize(void **data, unsigned int *total_size) REICAST_US(sch_list[modem_sched].tag) ; REICAST_US(sch_list[modem_sched].start) ; REICAST_US(sch_list[modem_sched].end) ; + #else + int modem_dummy; + REICAST_US(modem_dummy); + REICAST_US(modem_dummy); + REICAST_US(modem_dummy); #endif REICAST_US(SCIF_SCFSR2); @@ -1879,8 +1836,6 @@ bool dc_unserialize(void **data, unsigned int *total_size) #endif - - REICAST_US(NullDriveDiscType); REICAST_USA(q_subchannel,96); @@ -1896,7 +1851,6 @@ bool dc_unserialize(void **data, unsigned int *total_size) // REICAST_US(i); // VRAM_MASK - REICAST_US(naomi_updates); REICAST_US(i); // BoardID REICAST_US(GSerialBuffer); @@ -1935,25 +1889,18 @@ bool dc_unserialize(void **data, unsigned int *total_size) REICAST_US(div_som_reg2); REICAST_US(div_som_reg3); - - - //REICAST_USA(CodeCache,CODE_SIZE) ; //REICAST_USA(SH4_TCB,CODE_SIZE+4096); REICAST_US(LastAddr); REICAST_US(LastAddr_min); REICAST_USA(block_hash,1024); - REICAST_USA(RegisterWrite,sh4_reg_count); REICAST_USA(RegisterRead,sh4_reg_count); REICAST_US(fallback_blocks); REICAST_US(total_blocks); REICAST_US(REMOVED_OPS); - - - REICAST_USA(kcode,4); REICAST_USA(rt,4); REICAST_USA(lt,4); From c0f21b7551bcb686840e53ed4c1262cf341cee6b Mon Sep 17 00:00:00 2001 From: David Guillen Fandos Date: Fri, 10 May 2019 19:41:36 +0200 Subject: [PATCH 275/319] Re-enable modem in Apple builds too. --- shell/apple/emulator-osx/reicast-osx.xcodeproj/project.pbxproj | 2 ++ 1 file changed, 2 insertions(+) diff --git a/shell/apple/emulator-osx/reicast-osx.xcodeproj/project.pbxproj b/shell/apple/emulator-osx/reicast-osx.xcodeproj/project.pbxproj index a12ad753f..cff1dafd5 100644 --- a/shell/apple/emulator-osx/reicast-osx.xcodeproj/project.pbxproj +++ b/shell/apple/emulator-osx/reicast-osx.xcodeproj/project.pbxproj @@ -2546,6 +2546,7 @@ TARGET_NO_AREC, XBYAK_NO_OP_NAMES, TARGET_NO_OPENMP, + ENABLE_MODEM, CHD5_LZMA, _7ZIP_ST, CHD5_FLAC, @@ -2602,6 +2603,7 @@ TARGET_NO_AREC, XBYAK_NO_OP_NAMES, TARGET_NO_OPENMP, + ENABLE_MODEM, CHD5_LZMA, _7ZIP_ST, CHD5_FLAC, From 08285cf49cfbcb252bc244d3f6909db691c43aed Mon Sep 17 00:00:00 2001 From: David Guillen Fandos Date: Sat, 11 May 2019 13:37:13 +0200 Subject: [PATCH 276/319] Hack-fix the x86 JIT so that it builds. I guess that's why Win32 fails too --- core/rec-x86/rec_x86_driver.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/core/rec-x86/rec_x86_driver.cpp b/core/rec-x86/rec_x86_driver.cpp index 1d19a46fa..727cf4eee 100644 --- a/core/rec-x86/rec_x86_driver.cpp +++ b/core/rec-x86/rec_x86_driver.cpp @@ -326,8 +326,8 @@ void ngen_Compile(RuntimeBlockInfo* block, SmcCheckEnum smc_checks, bool reset, if (prof.enable) { - if (force_checks) - x86e->Emit(op_add32,&prof.counters.blkrun.force_check,1); + //if (force_checks) + // x86e->Emit(op_add32,&prof.counters.blkrun.force_check,1); x86e->Emit(op_add32,&prof.counters.blkrun.cycles[block->guest_cycles],1); } From b74db6ef53cbbc3870453a6487d0b5854136bf06 Mon Sep 17 00:00:00 2001 From: David Guillen Fandos Date: Sat, 11 May 2019 22:09:52 +0200 Subject: [PATCH 277/319] Moving vmem to separate files with a proper interface. Works so far for Linux and Android, need to do some testing on Windows. --- core/hw/aica/aica_if.cpp | 2 +- core/hw/aica/aica_if.h | 2 +- core/hw/mem/_vmem.cpp | 469 +++++++------------------------------- core/hw/mem/_vmem.h | 29 ++- core/hw/pvr/pvr_mem.h | 4 +- core/hw/sh4/sh4_mem.cpp | 2 +- core/hw/sh4/sh4_mem.h | 2 +- core/linux/common.cpp | 72 +----- core/linux/context.cpp | 4 +- core/linux/posix_vmem.cpp | 177 ++++++++++++++ core/rend/TexCache.cpp | 5 +- core/serialize.cpp | 6 +- core/stdclass.h | 42 ++-- core/windows/win_vmem.cpp | 96 ++++++++ core/windows/winmain.cpp | 18 -- shell/cmake/config.cmake | 4 +- shell/emscripten/Makefile | 2 +- shell/nacl/Makefile | 2 +- 18 files changed, 432 insertions(+), 506 deletions(-) create mode 100644 core/linux/posix_vmem.cpp create mode 100644 core/windows/win_vmem.cpp diff --git a/core/hw/aica/aica_if.cpp b/core/hw/aica/aica_if.cpp index 9ba65c0a7..d562a0de1 100644 --- a/core/hw/aica/aica_if.cpp +++ b/core/hw/aica/aica_if.cpp @@ -13,7 +13,7 @@ #include -VArray2 aica_ram; +VLockedMemory aica_ram; u32 VREG;//video reg =P u32 ARMRST;//arm reset reg u32 rtc_EN=0; diff --git a/core/hw/aica/aica_if.h b/core/hw/aica/aica_if.h index 8c58b5326..13ee6fd97 100644 --- a/core/hw/aica/aica_if.h +++ b/core/hw/aica/aica_if.h @@ -2,7 +2,7 @@ #include "types.h" extern u32 VREG; -extern VArray2 aica_ram; +extern VLockedMemory aica_ram; extern u32 RealTimeClock; u32 ReadMem_aica_rtc(u32 addr,u32 sz); void WriteMem_aica_rtc(u32 addr,u32 data,u32 sz); diff --git a/core/hw/mem/_vmem.cpp b/core/hw/mem/_vmem.cpp index e621fa948..9a297330c 100644 --- a/core/hw/mem/_vmem.cpp +++ b/core/hw/mem/_vmem.cpp @@ -21,7 +21,6 @@ _vmem_WriteMem32FP* _vmem_WF32[HANDLER_COUNT]; //upper 8b of the address void* _vmem_MemInfo_ptr[0x100]; - void _vmem_get_ptrs(u32 sz,bool write,void*** vmap,void*** func) { *vmap=_vmem_MemInfo_ptr; @@ -385,10 +384,7 @@ void _vmem_reset() verify(_vmem_register_handler(0,0,0,0,0,0)==0); } -void _vmem_term() -{ - -} +void _vmem_term() {} #include "hw/pvr/pvr_mem.h" #include "hw/sh4/sh4_mem.h" @@ -409,414 +405,119 @@ void* malloc_pages(size_t size) { #endif } -bool _vmem_reserve_nonvmem() -{ - virt_ram_base = 0; - - p_sh4rcb=(Sh4RCB*)malloc_pages(sizeof(Sh4RCB)); - - mem_b.size=RAM_SIZE; - mem_b.data=(u8*)malloc_pages(RAM_SIZE); - - vram.size=VRAM_SIZE; - vram.data=(u8*)malloc_pages(VRAM_SIZE); - - aica_ram.size=ARAM_SIZE; - aica_ram.data=(u8*)malloc_pages(ARAM_SIZE); - - return true; -} - -void _vmem_bm_reset_nvmem(); - +// Resets the FPCB table (by either clearing it to the default val +// or by flushing it and making it fault on access again. void _vmem_bm_reset() { - if (virt_ram_base) { - #if !defined(TARGET_NO_NVMEM) - _vmem_bm_reset_nvmem(); - #endif - } - -#ifndef TARGET_IPHONE - if (!virt_ram_base) -#endif - { + // If we allocated it via vmem: + if (virt_ram_base) + vmem_platform_reset_mem(p_sh4rcb->fpcb, sizeof(p_sh4rcb->fpcb)); + else + // We allocated it via a regular malloc/new/whatever on the heap bm_vmem_pagefill((void**)p_sh4rcb->fpcb, sizeof(p_sh4rcb->fpcb)); - } } -static void _vmem_release_nonvmem() -{ - free(p_sh4rcb); - free(vram.data); - free(aica_ram.data); - free(mem_b.data); -} +// This gets called whenever there is a pagefault, it is possible that it lands +// on the fpcb memory range, which is allocated on miss. Returning true tells the +// fault handler this was us, and that the page is resolved and can continue the execution. +bool BM_LockedWrite(u8* address) { + if (!virt_ram_base) + return false; // No vmem, therefore not us who caused this. -#if !defined(TARGET_NO_NVMEM) + uintptr_t ptrint = (uintptr_t)address; + uintptr_t start = (uintptr_t)p_sh4rcb->fpcb; + uintptr_t end = start + sizeof(p_sh4rcb->fpcb); -#define MAP_RAM_START_OFFSET 0 -#define MAP_VRAM_START_OFFSET (MAP_RAM_START_OFFSET+RAM_SIZE) -#define MAP_ARAM_START_OFFSET (MAP_VRAM_START_OFFSET+VRAM_SIZE) - -#if HOST_OS==OS_WINDOWS -#include -HANDLE mem_handle; - -void* _nvmem_map_buffer(u32 dst,u32 addrsz,u32 offset,u32 size, bool w) -{ - void* ptr; - void* rv; - - u32 map_times=addrsz/size; - verify((addrsz%size)==0); - verify(map_times>=1); - - rv= MapViewOfFileEx(mem_handle,FILE_MAP_READ | (w?FILE_MAP_WRITE:0),0,offset,size,&virt_ram_base[dst]); - if (!rv) - return 0; - - for (u32 i=1;i - #include - #include - #include - #include - #include - -#ifndef MAP_NOSYNC -#define MAP_NOSYNC 0 //missing from linux :/ -- could be the cause of android slowness ? -#endif - -#ifdef _ANDROID -#include - -#ifndef ASHMEM_DEVICE -#define ASHMEM_DEVICE "/dev/ashmem" -#endif -int ashmem_create_region(const char *name, size_t size) -{ - int fd, ret; - - fd = open(ASHMEM_DEVICE, O_RDWR); - if (fd < 0) - return fd; - - if (name) { - char buf[ASHMEM_NAME_LEN]; - - strlcpy(buf, name, sizeof(buf)); - ret = ioctl(fd, ASHMEM_SET_NAME, buf); - if (ret < 0) - goto error; - } - - ret = ioctl(fd, ASHMEM_SET_SIZE, size); - if (ret < 0) - goto error; - - return fd; - -error: - close(fd); - return ret; -} -#endif - - int fd; - void* _nvmem_unused_buffer(u32 start,u32 end) - { - void* ptr=mmap(&virt_ram_base[start], end-start, PROT_NONE, MAP_FIXED | MAP_PRIVATE | MAP_ANON, -1, 0); - if (MAP_FAILED==ptr) - return 0; - return ptr; - } - - - void* _nvmem_map_buffer(u32 dst,u32 addrsz,u32 offset,u32 size, bool w) - { - void* ptr; - void* rv; - - printf("MAP %08X w/ %d\n",dst,offset); - u32 map_times=addrsz/size; - verify((addrsz%size)==0); - verify(map_times>=1); - u32 prot=PROT_READ|(w?PROT_WRITE:0); - rv= mmap(&virt_ram_base[dst], size, prot, MAP_SHARED | MAP_NOSYNC | MAP_FIXED, fd, offset); - if (MAP_FAILED==rv || rv!=(void*)&virt_ram_base[dst] || (mprotect(rv,size,prot)!=0)) - { - printf("MAP1 failed %d\n",errno); - return 0; - } - - for (u32 i=1;i slow and stuttery - { - fd = open("/data/data/com.reicast.emulator/files/dcnzorz_mem",O_CREAT|O_RDWR|O_TRUNC,S_IRWXU|S_IRWXG|S_IRWXO); - unlink("/data/data/com.reicast.emulator/files/dcnzorz_mem"); - } -#endif - - - - u32 sz = 512*1024*1024 + sizeof(Sh4RCB) + ARAM_SIZE_MAX + 0x10000; - void* rv=mmap(0, sz, PROT_NONE, MAP_PRIVATE | MAP_ANON, -1, 0); - verify(rv != NULL); - munmap(rv,sz); - return (u8*)rv + 0x10000 - unat(rv)%0x10000;//align to 64 KB (Needed for linaro mmap not to extend to next region) - } -#endif - -#define map_buffer(dsts,dste,offset,sz,w) {ptr=_nvmem_map_buffer(dsts,dste-dsts,offset,sz,w);if (!ptr) return false;} -#define unused_buffer(start,end) {ptr=_nvmem_unused_buffer(start,end);if (!ptr) return false;} - -u32 pagecnt; -void _vmem_bm_reset_nvmem() -{ - #if defined(TARGET_NO_NVMEM) - return; - #endif - - #ifdef TARGET_IPHONE - //On iOS & nacl we allways allocate all of the mapping table - mprotect(p_sh4rcb, sizeof(p_sh4rcb->fpcb), PROT_READ | PROT_WRITE); - return; - #endif - pagecnt=0; - -#if HOST_OS==OS_WINDOWS - VirtualFree(p_sh4rcb,sizeof(p_sh4rcb->fpcb),MEM_DECOMMIT); -#else - mprotect(p_sh4rcb, sizeof(p_sh4rcb->fpcb), PROT_NONE); - madvise(p_sh4rcb,sizeof(p_sh4rcb->fpcb),MADV_DONTNEED); - #ifdef MADV_REMOVE - madvise(p_sh4rcb,sizeof(p_sh4rcb->fpcb),MADV_REMOVE); - #else - //OSX, IOS - madvise(p_sh4rcb,sizeof(p_sh4rcb->fpcb),MADV_FREE); - #endif -#endif - - printf("Freeing fpcb\n"); -} - -bool BM_LockedWrite(u8* address) -{ - if (!_nvmem_enabled()) - return false; - -#if FEAT_SHREC != DYNAREC_NONE - u32 addr=address-(u8*)p_sh4rcb->fpcb; - - address=(u8*)p_sh4rcb->fpcb+ (addr&~PAGE_MASK); - - if (addrfpcb)) - { - //printf("Allocated %d PAGES [%08X]\n",++pagecnt,addr); - -#if HOST_OS==OS_WINDOWS - verify(VirtualAlloc(address,PAGE_SIZE,MEM_COMMIT,PAGE_READWRITE)); -#else - mprotect (address, PAGE_SIZE, PROT_READ | PROT_WRITE); -#endif - - bm_vmem_pagefill((void**)address,PAGE_SIZE); - + if (ptrint >= start && ptrint < end) { + // Alloc the page then and initialize it to default values + void *aligned_addr = (void*)(ptrint & (~PAGE_MASK)); + vmem_platform_ondemand_page(aligned_addr, PAGE_SIZE); + bm_vmem_pagefill((void**)aligned_addr, PAGE_SIZE); return true; } -#else -die("BM_LockedWrite and NO REC"); -#endif return false; } -bool _vmem_reserve() -{ - void* ptr=0; - +bool _vmem_reserve() { + // TODO: Static assert? verify((sizeof(Sh4RCB)%PAGE_SIZE)==0); - if (settings.dynarec.disable_nvmem) - return _vmem_reserve_nonvmem(); + VMemType vmemstatus = MemTypeError; - virt_ram_base=(u8*)_nvmem_alloc_mem(); + // Use vmem only if settings mandate so, and if we have proper exception handlers. + #ifndef TARGET_NO_EXCEPTIONS + if (!settings.dynarec.disable_nvmem) + vmemstatus = vmem_platform_init((void**)&virt_ram_base, (void**)&p_sh4rcb); + #endif - if (virt_ram_base==0) - return _vmem_reserve_nonvmem(); - - p_sh4rcb=(Sh4RCB*)virt_ram_base; + // Fallback to statically allocated buffers, this results in slow-ops being generated. + if (vmemstatus == MemTypeError) { + printf("Warning! nvmem is DISABLED (due to failure or not being built-in\n"); + virt_ram_base = 0; - // Map the sh4 context but protect access to Sh4RCB.fpcb[] -#if HOST_OS==OS_WINDOWS - //verify(p_sh4rcb==VirtualAlloc(p_sh4rcb,sizeof(Sh4RCB),MEM_RESERVE|MEM_COMMIT,PAGE_READWRITE)); - verify(p_sh4rcb==VirtualAlloc(p_sh4rcb,sizeof(Sh4RCB),MEM_RESERVE,PAGE_NOACCESS)); + // Allocate it all and initialize it. + p_sh4rcb = (Sh4RCB*)malloc_pages(sizeof(Sh4RCB)); + bm_vmem_pagefill((void**)p_sh4rcb->fpcb, sizeof(p_sh4rcb->fpcb)); - verify(VirtualAlloc((u8*)p_sh4rcb + sizeof(p_sh4rcb->fpcb),sizeof(Sh4RCB)-sizeof(p_sh4rcb->fpcb),MEM_COMMIT,PAGE_READWRITE)); -#else - verify(p_sh4rcb==mmap(p_sh4rcb,sizeof(Sh4RCB),PROT_NONE,MAP_PRIVATE | MAP_ANON, -1, 0)); - mprotect((u8*)p_sh4rcb + sizeof(p_sh4rcb->fpcb),sizeof(Sh4RCB)-sizeof(p_sh4rcb->fpcb),PROT_READ|PROT_WRITE); -#endif - virt_ram_base+=sizeof(Sh4RCB); + mem_b.size = RAM_SIZE; + mem_b.data = (u8*)malloc_pages(RAM_SIZE); - //Area 0 - //[0x00000000 ,0x00800000) -> unused - unused_buffer(0x00000000,0x00800000); + vram.size = VRAM_SIZE; + vram.data = (u8*)malloc_pages(VRAM_SIZE); - //I wonder, aica ram warps here ?.? - //I really should check teh docs before codin ;p - //[0x00800000,0x00A00000); - map_buffer(0x00800000,0x01000000,MAP_ARAM_START_OFFSET,ARAM_SIZE,false); - map_buffer(0x20000000,0x20000000+ARAM_SIZE,MAP_ARAM_START_OFFSET,ARAM_SIZE,true); + aica_ram.size = ARAM_SIZE; + aica_ram.data = (u8*)malloc_pages(ARAM_SIZE); + } + else { + printf("Info: nvmem is enabled, with addr space of size %s\n", vmemstatus == MemType4GB ? "4GB" : "512MB"); + // Map the different parts of the memory file into the new memory range we got. + #define MAP_RAM_START_OFFSET 0 + #define MAP_VRAM_START_OFFSET (MAP_RAM_START_OFFSET+RAM_SIZE) + #define MAP_ARAM_START_OFFSET (MAP_VRAM_START_OFFSET+VRAM_SIZE) + const vmem_mapping mem_mappings[] = { + {0x00000000, 0x00800000, 0, 0, false}, // Area 0 -> unused + {0x00800000, 0x01000000, MAP_ARAM_START_OFFSET, ARAM_SIZE, false}, // Aica, wraps too + {0x20000000, 0x20000000+ARAM_SIZE, MAP_ARAM_START_OFFSET, ARAM_SIZE, true}, + {0x01000000, 0x04000000, 0, 0, false}, // More unused + {0x04000000, 0x05000000, MAP_VRAM_START_OFFSET, VRAM_SIZE, true}, // Area 1 (vram, 16MB, wrapped on DC) + {0x05000000, 0x06000000, 0, 0, false}, // 32 bit path (unused) + {0x06000000, 0x07000000, MAP_VRAM_START_OFFSET, VRAM_SIZE, true}, // VRAM mirror + {0x07000000, 0x08000000, 0, 0, false}, // 32 bit path (unused) mirror + {0x08000000, 0x0C000000, 0, 0, false}, // Area 2 + {0x0C000000, 0x10000000, MAP_RAM_START_OFFSET, RAM_SIZE, true}, // Area 3 (main RAM + 3 mirrors) + {0x10000000, 0x20000000, 0, 0, false}, // Area 4-7 (unused) + }; + vmem_platform_create_mappings(&mem_mappings[0], sizeof(mem_mappings) / sizeof(mem_mappings[0])); - aica_ram.size=ARAM_SIZE; - aica_ram.data=(u8*)ptr; - //[0x01000000 ,0x04000000) -> unused - unused_buffer(0x01000000,0x04000000); - + // Point buffers to actual data pointers + aica_ram.size = ARAM_SIZE; + aica_ram.data = &virt_ram_base[0x20000000]; // Points to the writtable AICA addrspace - //Area 1 - //[0x04000000,0x05000000) -> vram (16mb, warped on dc) - map_buffer(0x04000000,0x05000000,MAP_VRAM_START_OFFSET,VRAM_SIZE,true); - - vram.size=VRAM_SIZE; - vram.data=(u8*)ptr; + vram.size = VRAM_SIZE; + vram.data = &virt_ram_base[0x04000000]; // Points to first vram mirror (writtable and lockable) - //[0x05000000,0x06000000) -> unused (32b path) - unused_buffer(0x05000000,0x06000000); - - //[0x06000000,0x07000000) -> vram mirror - map_buffer(0x06000000,0x07000000,MAP_VRAM_START_OFFSET,VRAM_SIZE,true); - - - //[0x07000000,0x08000000) -> unused (32b path) mirror - unused_buffer(0x07000000,0x08000000); - - //Area 2 - //[0x08000000,0x0C000000) -> unused - unused_buffer(0x08000000,0x0C000000); - - //Area 3 - //[0x0C000000,0x0D000000) -> main ram - //[0x0D000000,0x0E000000) -> main ram mirror - //[0x0E000000,0x0F000000) -> main ram mirror - //[0x0F000000,0x10000000) -> main ram mirror - map_buffer(0x0C000000,0x10000000,MAP_RAM_START_OFFSET,RAM_SIZE,true); - - mem_b.size=RAM_SIZE; - mem_b.data=(u8*)ptr; - - //Area 4 - //Area 5 - //Area 6 - //Area 7 - //all -> Unused - //[0x10000000,0x20000000) -> unused - unused_buffer(0x10000000,0x20000000); - - printf("vmem reserve: base: %08X, aram: %08x, vram: %08X, ram: %08X\n",virt_ram_base,aica_ram.data,vram.data,mem_b.data); + mem_b.size = RAM_SIZE; + mem_b.data = &virt_ram_base[0x0C000000]; // Main memory, first mirror + } + // Clear out memory aica_ram.Zero(); vram.Zero(); mem_b.Zero(); - printf("Mem alloc successful!\n"); - - return virt_ram_base!=0; + return true; } -void _vmem_release() -{ - if (!_nvmem_enabled()) - _vmem_release_nonvmem(); - else - { - if (virt_ram_base != NULL) - { -#if HOST_OS == OS_WINDOWS - VirtualFree(virt_ram_base, 0, MEM_RELEASE); -#else - munmap(virt_ram_base, 0x20000000); -#endif - virt_ram_base = NULL; - } -#if HOST_OS != OS_WINDOWS - close(fd); -#endif +#define freedefptr(x) \ + if (x) { free(x); x = NULL; } + +void _vmem_release() { + if (virt_ram_base) + vmem_platform_destroy(); + else { + freedefptr(p_sh4rcb); + freedefptr(vram.data); + freedefptr(aica_ram.data); + freedefptr(mem_b.data); } } -#else - -bool _vmem_reserve() -{ - return _vmem_reserve_nonvmem(); -} -void _vmem_release() -{ - _vmem_release_nonvmem(); -} -#endif diff --git a/core/hw/mem/_vmem.h b/core/hw/mem/_vmem.h index 8509fe3ac..f739b0454 100644 --- a/core/hw/mem/_vmem.h +++ b/core/hw/mem/_vmem.h @@ -1,6 +1,33 @@ #pragma once #include "types.h" +enum VMemType { + MemType4GB, + MemType512MB, + MemTypeError +}; + +struct vmem_mapping { + u32 start_address, end_address; + unsigned memoffset, memsize; + bool allow_writes; +}; + +// Platform specific vmemory API +// To initialize (maybe) the vmem subsystem +VMemType vmem_platform_init(void **vmem_base_addr, void **sh4rcb_addr); +// To reset the on-demand allocated pages. +void vmem_platform_reset_mem(void *ptr, unsigned size_bytes); +// To handle a fault&allocate an ondemand page. +void vmem_platform_ondemand_page(void *address, unsigned size_bytes); +// To create the mappings in the address space. +void vmem_platform_create_mappings(const vmem_mapping *vmem_maps, unsigned nummaps); +// Just tries to wipe as much as possible in the relevant area. +void vmem_platform_destroy(); + +// Note: if you want to disable vmem magic in any given platform, implement the +// above functions as empty functions and make vmem_platform_init return MemTypeError. + //Typedef's //ReadMem typedef u8 DYNACALL _vmem_ReadMem8FP(u32 Address); @@ -70,4 +97,4 @@ static inline bool _nvmem_enabled() { return virt_ram_base != 0; } -void _vmem_bm_reset(); \ No newline at end of file +void _vmem_bm_reset(); diff --git a/core/hw/pvr/pvr_mem.h b/core/hw/pvr/pvr_mem.h index 722d37bf6..f2584937d 100644 --- a/core/hw/pvr/pvr_mem.h +++ b/core/hw/pvr/pvr_mem.h @@ -9,7 +9,7 @@ f32 vrf(u32 addr); u32 vri(u32 addr); //vram 32-64b -extern VArray2 vram; +extern VLockedMemory vram; //read u8 DYNACALL pvr_read_area1_8(u32 addr); u16 DYNACALL pvr_read_area1_16(u32 addr); @@ -36,4 +36,4 @@ extern "C" void DYNACALL TAWriteSQ(u32 address,u8* sqb); void YUV_init(); //registers -#define PVR_BASE 0x005F8000 \ No newline at end of file +#define PVR_BASE 0x005F8000 diff --git a/core/hw/sh4/sh4_mem.cpp b/core/hw/sh4/sh4_mem.cpp index b05c2c419..e3a76f08f 100644 --- a/core/hw/sh4/sh4_mem.cpp +++ b/core/hw/sh4/sh4_mem.cpp @@ -17,7 +17,7 @@ //main system mem -VArray2 mem_b; +VLockedMemory mem_b; void _vmem_init(); void _vmem_reset(); diff --git a/core/hw/sh4/sh4_mem.h b/core/hw/sh4/sh4_mem.h index e9a03af47..31982ab3f 100644 --- a/core/hw/sh4/sh4_mem.h +++ b/core/hw/sh4/sh4_mem.h @@ -2,7 +2,7 @@ #include "types.h" //main system mem -extern VArray2 mem_b; +extern VLockedMemory mem_b; #include "hw/mem/_vmem.h" #include "modules/mmu.h" diff --git a/core/linux/common.cpp b/core/linux/common.cpp index 3cbd62a68..50df5f662 100644 --- a/core/linux/common.cpp +++ b/core/linux/common.cpp @@ -34,7 +34,7 @@ #include "hw/sh4/dyna/ngen.h" -#if !defined(TARGET_NO_NVMEM) +#if !defined(TARGET_NO_EXCEPTIONS) bool ngen_Rewrite(unat& addr,unat retadr,unat acc); u32* ngen_readm_fail_v2(u32* ptr,u32* regs,u32 saddr); bool VramLockedWrite(u8* address); @@ -124,79 +124,13 @@ void install_fault_handler(void) sigaction(SIGILL, &act, &segv_oact); #endif } -#else // !defined(TARGET_NO_NVMEM) +#else // !defined(TARGET_NO_EXCEPTIONS) // No exceptions/nvmem dummy handlers. void install_fault_handler(void) {} -#endif // !defined(TARGET_NO_NVMEM) +#endif // !defined(TARGET_NO_EXCEPTIONS) #include -void VArray2::LockRegion(u32 offset,u32 size) -{ - #if !defined(TARGET_NO_NVMEM) - u32 inpage=offset & PAGE_MASK; - u32 rv=mprotect (data+offset-inpage, size+inpage, PROT_READ ); - if (rv!=0) - { - printf("mprotect(%8s,%08X,R) failed: %d | %d\n",data+offset-inpage,size+inpage,rv,errno); - die("mprotect failed ..\n"); - } - - #else - //printf("VA2: LockRegion\n"); - #endif -} - -void print_mem_addr() -{ - FILE *ifp, *ofp; - - char outputFilename[] = "/data/data/com.reicast.emulator/files/mem_alloc.txt"; - - ifp = fopen("/proc/self/maps", "r"); - - if (ifp == NULL) { - fprintf(stderr, "Can't open input file /proc/self/maps!\n"); - exit(1); - } - - ofp = fopen(outputFilename, "w"); - - if (ofp == NULL) { - fprintf(stderr, "Can't open output file %s!\n", - outputFilename); -#if HOST_OS == OS_LINUX - ofp = stderr; -#else - exit(1); -#endif - } - - char line [ 512 ]; - while (fgets(line, sizeof line, ifp) != NULL) { - fprintf(ofp, "%s", line); - } - - fclose(ifp); - if (ofp != stderr) - fclose(ofp); -} - -void VArray2::UnLockRegion(u32 offset,u32 size) -{ - #if !defined(TARGET_NO_NVMEM) - u32 inpage=offset & PAGE_MASK; - u32 rv=mprotect (data+offset-inpage, size+inpage, PROT_READ | PROT_WRITE); - if (rv!=0) - { - print_mem_addr(); - printf("mprotect(%8p,%08X,RW) failed: %d | %d\n",data+offset-inpage,size+inpage,rv,errno); - die("mprotect failed ..\n"); - } - #else - //printf("VA2: UnLockRegion\n"); - #endif -} double os_GetSeconds() { timeval a; diff --git a/core/linux/context.cpp b/core/linux/context.cpp index 9884ad426..7c317c401 100644 --- a/core/linux/context.cpp +++ b/core/linux/context.cpp @@ -8,7 +8,7 @@ #define __USE_GNU 1 #endif - #if !defined(TARGET_NO_NVMEM) + #if !defined(TARGET_NO_EXCEPTIONS) #include #endif #endif @@ -29,7 +29,7 @@ void bicopy(Ta& rei, Tb& seg, bool to_segfault) { void context_segfault(rei_host_context_t* reictx, void* segfault_ctx, bool to_segfault) { -#if !defined(TARGET_NO_NVMEM) +#if !defined(TARGET_NO_EXCEPTIONS) #if HOST_CPU == CPU_ARM #if defined(__FreeBSD__) bicopy(reictx->pc, MCTX(.__gregs[_REG_PC]), to_segfault); diff --git a/core/linux/posix_vmem.cpp b/core/linux/posix_vmem.cpp new file mode 100644 index 000000000..8fcf57614 --- /dev/null +++ b/core/linux/posix_vmem.cpp @@ -0,0 +1,177 @@ + +// Implementation of the vmem related function for POSIX-like platforms. +// There's some minimal amount of platform specific hacks to support +// Android and OSX since they are slightly different in some areas. + +// This implements the VLockedMemory interface, as defined in _vmem.h +// The implementation allows it to be empty (that is, to not lock memory). + +#include +#include +#include +#include +#include +#include + +#include "hw/mem/_vmem.h" +#include "stdclass.h" + +#ifndef MAP_NOSYNC +#define MAP_NOSYNC 0 //missing from linux :/ -- could be the cause of android slowness ? +#endif + +#ifdef _ANDROID + #include + #ifndef ASHMEM_DEVICE + #define ASHMEM_DEVICE "/dev/ashmem" + #undef PAGE_MASK + #define PAGE_MASK (PAGE_SIZE-1) +#else + #define PAGE_SIZE 4096 + #define PAGE_MASK (PAGE_SIZE-1) +#endif + +// Android specific ashmem-device stuff for creating shared memory regions +int ashmem_create_region(const char *name, size_t size) { + int fd = open(ASHMEM_DEVICE, O_RDWR); + if (fd < 0) + return -1; + + if (ioctl(fd, ASHMEM_SET_SIZE, size) < 0) { + close(fd); + return -1; + } + + return fd; +} +#endif // #ifdef _ANDROID + +void VLockedMemory::LockRegion(unsigned offset, unsigned size_bytes) { + size_t inpage = offset & PAGE_MASK; + if (mprotect(&data[offset - inpage], size_bytes + inpage, PROT_READ)) { + die("mprotect failed ..\n"); + } +} + +void VLockedMemory::UnLockRegion(unsigned offset, unsigned size_bytes) { + size_t inpage = offset & PAGE_MASK; + if (mprotect(&data[offset - inpage], size_bytes + inpage, PROT_READ|PROT_WRITE)) { + // Add some way to see why it failed? gdb> info proc mappings + die("mprotect failed ..\n"); + } +} + +// Allocates memory via a fd on shmem/ahmem or even a file on disk +static int allocate_shared_filemem() { + int fd = -1; + #if defined(_ANDROID) + // Use Android's specific shmem stuff. + fd = ashmem_create_region(0, RAM_SIZE_MAX + VRAM_SIZE_MAX + ARAM_SIZE_MAX); + #else + #if HOST_OS != OS_DARWIN + fd = shm_open("/dcnzorz_mem", O_CREAT | O_EXCL | O_RDWR,S_IREAD | S_IWRITE); + shm_unlink("/dcnzorz_mem"); + #endif + + // if shmem does not work (or using OSX) fallback to a regular file on disk + if (fd < 0) { + string path = get_writable_data_path("/dcnzorz_mem"); + fd = open(path.c_str(), O_CREAT|O_RDWR|O_TRUNC, S_IRWXU|S_IRWXG|S_IRWXO); + unlink(path.c_str()); + } + // If we can't open the file, fallback to slow mem. + if (fd < 0) + return -1; + + // Finally make the file as big as we need! + if (ftruncate(fd, RAM_SIZE_MAX + VRAM_SIZE_MAX + ARAM_SIZE_MAX)) { + // Can't get as much memory as needed, fallback. + close(fd); + return -1; + } + #endif + + return fd; +} + +// Implement vmem initialization for RAM, ARAM, VRAM and SH4 context, fpcb etc. +// The function supports allocating 512MB or 4GB addr spaces. + +static int shmem_fd = -1; + +// vmem_base_addr points to an address space of 512MB (or 4GB) that can be used for fast memory ops. +// In negative offsets of the pointer (up to FPCB size, usually 65/129MB) the context and jump table +// can be found. If the platform init returns error, the user is responsible for initializing the +// memory using a fallback (that is, regular mallocs and falling back to slow memory JIT). +VMemType vmem_platform_init(void **vmem_base_addr, void **sh4rcb_addr) { + // Firt let's try to allocate the shm-backed memory + shmem_fd = allocate_shared_filemem(); + if (shmem_fd < 0) + return MemTypeError; + + // Now try to allocate a contiguous piece of memory. + unsigned memsize = 512*1024*1024 + sizeof(Sh4RCB) + ARAM_SIZE_MAX + 0x10000; + void *first_ptr = mmap(0, memsize, PROT_NONE, MAP_PRIVATE | MAP_ANON, -1, 0); + if (!first_ptr) { + close(shmem_fd); + return MemTypeError; + } + + // Align pointer to 64KB too, some Linaro bug (no idea but let's just be safe I guess). + uintptr_t ptrint = (uintptr_t)first_ptr; + ptrint = (ptrint + 0x10000 - 1) & (~0xffff); + *sh4rcb_addr = (void*)ptrint; + *vmem_base_addr = (void*)(ptrint + sizeof(Sh4RCB)); + void *sh4rcb_base_ptr = (void*)(ptrint + FPCB_SIZE); + + // Now map the memory for the SH4 context, do not include FPCB on purpose (paged on demand). + mprotect(sh4rcb_base_ptr, sizeof(Sh4RCB) - FPCB_SIZE, PROT_READ | PROT_WRITE); + + return MemType512MB; +} + +// Just tries to wipe as much as possible in the relevant area. +void vmem_platform_destroy() { + munmap(virt_ram_base, 0x20000000); +} + +// Resets a chunk of memory by deleting its data and setting its protection back. +void vmem_platform_reset_mem(void *ptr, unsigned size_bytes) { + // Mark them as non accessible. + mprotect(ptr, size_bytes, PROT_NONE); + // Tell the kernel to flush'em all (FIXME: perhaps unmap+mmap 'd be better?) + madvise(ptr, size_bytes, MADV_DONTNEED); + #ifdef MADV_REMOVE + madvise(ptr, size_bytes, MADV_REMOVE); + #endif + madvise(ptr, size_bytes, MADV_FREE); +} + +// Allocates a bunch of memory (page aligned and page-sized) +void vmem_platform_ondemand_page(void *address, unsigned size_bytes) { + verify(!mprotect(address, size_bytes, PROT_READ | PROT_WRITE)); +} + +// Creates mappings to the underlying file including mirroring sections +void vmem_platform_create_mappings(const vmem_mapping *vmem_maps, unsigned nummaps) { + for (unsigned i = 0; i < nummaps; i++) { + // Ignore unmapped stuff, it is already reserved as PROT_NONE + if (!vmem_maps[i].memsize) + continue; + + // Calculate the number of mirrors + unsigned address_range_size = vmem_maps[i].end_address - vmem_maps[i].start_address; + unsigned num_mirrors = (address_range_size) / vmem_maps[i].memsize; + int protection = vmem_maps[i].allow_writes ? (PROT_READ | PROT_WRITE) : PROT_READ; + verify((address_range_size % vmem_maps[i].memsize) == 0 && num_mirrors >= 1); + + for (unsigned j = 0; j < num_mirrors; j++) { + unsigned offset = vmem_maps[i].start_address + j * vmem_maps[i].memsize; + verify(!munmap(&virt_ram_base[offset], vmem_maps[i].memsize)); + verify(MAP_FAILED != mmap(&virt_ram_base[offset], vmem_maps[i].memsize, protection, + MAP_SHARED | MAP_NOSYNC | MAP_FIXED, shmem_fd, vmem_maps[i].memoffset)); + // ??? (mprotect(rv,size,prot)!=0) + } + } +} + diff --git a/core/rend/TexCache.cpp b/core/rend/TexCache.cpp index b0480c273..df904ed22 100644 --- a/core/rend/TexCache.cpp +++ b/core/rend/TexCache.cpp @@ -124,8 +124,7 @@ void palette_update() using namespace std; vector VramLocks[VRAM_SIZE/PAGE_SIZE]; -//vram 32-64b -VArray2 vram; +VLockedMemory vram; // vram 32-64b //List functions // @@ -207,7 +206,7 @@ vram_block* libCore_vramlock_Lock(u32 start_offset64,u32 end_offset64,void* user { vramlist_lock.Lock(); - vram.LockRegion(block->start,block->len); + vram.LockRegion(block->start, block->len); //TODO: Fix this for 32M wrap as well if (_nvmem_enabled() && VRAM_SIZE == 0x800000) { diff --git a/core/serialize.cpp b/core/serialize.cpp index 1d7fec35b..47b6eb0ca 100644 --- a/core/serialize.cpp +++ b/core/serialize.cpp @@ -101,7 +101,7 @@ extern AicaTimer timers[3]; //./core/hw/aica/aica_if.o -extern VArray2 aica_ram; +extern VLockedMemory aica_ram; extern u32 VREG;//video reg =P extern u32 ARMRST;//arm reset reg extern u32 rtc_EN; @@ -381,7 +381,7 @@ extern DECL_ALIGN(4) u32 SFaceOffsColor; //extern vector VramLocks[/*VRAM_SIZE*/(16*1024*1024)/PAGE_SIZE]; //maybe - probably not - just a locking mechanism //extern cMutex vramlist_lock; -extern VArray2 vram; +extern VLockedMemory vram; @@ -403,7 +403,7 @@ extern Array SCIF; //SCIF : 10 registers //./core/hw/sh4/sh4_mem.o -extern VArray2 mem_b; +extern VLockedMemory mem_b; //one-time init //extern _vmem_handler area1_32b; //one-time init diff --git a/core/stdclass.h b/core/stdclass.h index f5e87e6cb..04e9142db 100644 --- a/core/stdclass.h +++ b/core/stdclass.h @@ -279,29 +279,38 @@ string get_game_save_prefix(); string get_game_basename(); string get_game_dir(); -class VArray2 -{ + +// Locked memory class, used for texture invalidation purposes. +class VLockedMemory { public: - u8* data; - u32 size; - //void Init(void* data,u32 sz); - //void Term(); - void LockRegion(u32 offset,u32 size); - void UnLockRegion(u32 offset,u32 size); + unsigned size; - void Zero() - { - UnLockRegion(0,size); - memset(data,0,size); + void SetRegion(void* ptr, unsigned size) { + this->data = (u8*)ptr; + this->size = size; + } + void *getPtr() const { return data; } + unsigned getSize() const { return size; } + + #ifdef TARGET_NO_EXCEPTIONS + void LockRegion(unsigned offset, unsigned size_bytes) {} + void UnLockRegion(unsigned offset, unsigned size_bytes) {} + #else + void LockRegion(unsigned offset, unsigned size_bytes); + void UnLockRegion(unsigned offset, unsigned size_bytes); + #endif + + void Zero() { + UnLockRegion(0, size); + memset(data, 0, size); } - INLINE u8& operator [](const u32 i) - { + INLINE u8& operator [](unsigned i) { #ifdef MEM_BOUND_CHECK - if (i>=size) + if (i >= size) { - printf("Error: VArray2 , index out of range (%d>%d)\n",i,size-1); + printf("Error: VLockedMemory , index out of range (%d > %d)\n", i, size-1); MEM_DO_BREAK; } #endif @@ -309,6 +318,7 @@ public: } }; + int msgboxf(const wchar* text,unsigned int type,...); diff --git a/core/windows/win_vmem.cpp b/core/windows/win_vmem.cpp new file mode 100644 index 000000000..b1434b7e2 --- /dev/null +++ b/core/windows/win_vmem.cpp @@ -0,0 +1,96 @@ + +#define _WIN32_WINNT 0x0500 +#include +#include + +// Implementation of the vmem related function for Windows platforms. +// For now this probably does some assumptions on the CPU/platform. + +// This implements the VLockedMemory interface, as defined in _vmem.h +// The implementation allows it to be empty (that is, to not lock memory). + +void VLockedMemory::LockRegion(unsigned offset, unsigned size) { + verify(offset + size < this->size && size != 0); + DWORD old; + VirtualProtect(&data[offset], size, PAGE_READONLY, &old); +} + +void VLockedMemory::UnLockRegion(unsigned offset, unsigned size) { + verify(offset + size <= this->size && size != 0); + DWORD old; + VirtualProtect(&data[offset], size, PAGE_READWRITE, &old); +} + +static HANDLE mem_handle = INVALID_HANDLE_VALUE; +static char * base_alloc = NULL; + +// Implement vmem initialization for RAM, ARAM, VRAM and SH4 context, fpcb etc. +// The function supports allocating 512MB or 4GB addr spaces. + +// Plase read the POSIX implementation for more information. On Windows this is +// rather straightforward. +VMemType vmem_platform_init(void *vmem_base_addr, void *sh4rcb_addr) { + // Firt let's try to allocate the in-memory file + mem_handle = CreateFileMapping(INVALID_HANDLE_VALUE, 0, PAGE_READWRITE, 0, RAM_SIZE_MAX + VRAM_SIZE_MAX + ARAM_SIZE_MAX, 0); + + // Now allocate the actual address space (it will be 64KB aligned on windows). + unsigned memsize = 512*1024*1024 + sizeof(Sh4RCB) + ARAM_SIZE_MAX; + base_alloc = (char*)VirtualAlloc(0, memsize, MEM_RESERVE, PAGE_NOACCESS); + + // Calculate pointers now + sh4rcb_addr = &base_alloc[0]; + vmem_base_addr = &base_alloc[sizeof(Sh4RCB)]; + + return MemType512MB; +} + +// Just tries to wipe as much as possible in the relevant area. +void vmem_platform_destroy() { + VirtualFree(base_alloc, 0, MEM_RELEASE); + CloseHandle(mem_handle); +} + +// Resets a chunk of memory by deleting its data and setting its protection back. +void vmem_platform_reset_mem(void *ptr, unsigned size_bytes) { + VirtualFree(ptr, size_bytes, MEM_DECOMMIT); +} + +// Allocates a bunch of memory (page aligned and page-sized) +void vmem_platform_ondemand_page(void *address, unsigned size_bytes) { + verify(VirtualAlloc(address, size_bytes, MEM_COMMIT, PAGE_READWRITE)); +} + +/// Creates mappings to the underlying file including mirroring sections +void vmem_platform_create_mappings(const vmem_mapping *vmem_maps, unsigned nummaps) { + // Since this is tricky to get right in Windows (in posix one can just unmap sections and remap later) + // we unmap the whole thing only to remap it later. + + // Unmap the whole section + VirtualFree(base_alloc, 0, MEM_RELEASE); + + for (unsigned i = 0; i < nummaps; i++) { + unsigned address_range_size = vmem_maps[i].end_address - vmem_maps[i].start_address; + DWORD protection = vmem_maps[i].allow_writes ? (FILE_MAP_READ | FILE_MAP_WRITE) : FILE_MAP_READ; + + if (!vmem_maps[i].memsize) { + // Unmapped stuff goes with a protected area or memory. Prevent anything from allocating here + void *ptr = VirtualAlloc(&virt_ram_base[vmem_maps[i].start_address], address_range_size, MEM_RESERVE, PAGE_NOACCESS); + verify(ptr == &virt_ram_base[vmem_maps[i].start_address]); + } + else { + // Calculate the number of mirrors + unsigned num_mirrors = (address_range_size) / vmem_maps[i].memsize; + verify((address_range_size % vmem_maps[i].memsize) == 0 && num_mirrors >= 1); + + // Remap the views one by one + for (unsigned j = 0; j < num_mirrors; j++) { + unsigned offset = vmem_maps[i].start_address + j * vmem_maps[i].memsize; + + void *ptr = MapViewOfFileEx(mem_handle, protection, 0, vmem_maps[i].memoffset, + vmem_maps[i].memsize, &virt_ram_base[vmem_maps[i].start_address]); + verify(ptr == &virt_ram_base[vmem_maps[i].start_address]); + } + } + } +} + diff --git a/core/windows/winmain.cpp b/core/windows/winmain.cpp index 029ba1246..44633ab0d 100644 --- a/core/windows/winmain.cpp +++ b/core/windows/winmain.cpp @@ -148,12 +148,10 @@ LONG ExeptionHandler(EXCEPTION_POINTERS *ExceptionInfo) { return EXCEPTION_CONTINUE_EXECUTION; } -#ifndef TARGET_NO_NVMEM else if (BM_LockedWrite(address)) { return EXCEPTION_CONTINUE_EXECUTION; } -#endif #if FEAT_SHREC == DYNAREC_JIT && HOST_CPU == CPU_X86 else if ( ngen_Rewrite((unat&)ep->ContextRecord->Eip,*(unat*)ep->ContextRecord->Esp,ep->ContextRecord->Eax) ) { @@ -800,21 +798,5 @@ void os_DoEvents() } } - -void VArray2::LockRegion(u32 offset,u32 size) -{ - //verify(offset+sizesize); - verify(size!=0); - DWORD old; - VirtualProtect(((u8*)data)+offset , size, PAGE_READONLY,&old); -} -void VArray2::UnLockRegion(u32 offset,u32 size) -{ - //verify(offset+size<=this->size); - verify(size!=0); - DWORD old; - VirtualProtect(((u8*)data)+offset , size, PAGE_READWRITE,&old); -} - int get_mic_data(u8* buffer) { return 0; } int push_vmu_screen(u8* buffer) { return 0; } diff --git a/shell/cmake/config.cmake b/shell/cmake/config.cmake index 056b7e74a..d1dfd59e4 100644 --- a/shell/cmake/config.cmake +++ b/shell/cmake/config.cmake @@ -343,7 +343,7 @@ if (TARGET_NSW) # -DCMAKE_TOOLCHAIN_FILE=./cmake/devkitA64.cmake -DTARGET_NSW=ON message("HOST_OS ${HOST_OS}") add_definitions(-D__SWITCH__ -DGLES -DMESA_EGL_NO_X11_HEADERS) - add_definitions(-DTARGET_NO_THREADS -DTARGET_NO_NVMEM -DTARGET_NO_NIXPROF) + add_definitions(-DTARGET_NO_THREADS -DTARGET_NO_EXCEPTIONS -DTARGET_NO_NIXPROF) add_definitions(-DTARGET_NO_COREIO_HTTP -DTARGET_NO_WEBUI -UTARGET_SOFTREND) add_definitions(-D_GLIBCXX_USE_C99_MATH_TR1 -D_LDBL_EQ_DBL) @@ -355,7 +355,7 @@ if (TARGET_PS4) # -DCMAKE_TOOLCHAIN_FILE=./cmake/{ps4sdk,clang_scei}.cmake -DTAR add_definitions(-DPS4 -DTARGET_PS4 -DTARGET_BSD -D__ORBIS__ -DGLES -DMESA_EGL_NO_X11_HEADERS) ## last needed for __unix__ on eglplatform.h - add_definitions(-DTARGET_NO_THREADS -DTARGET_NO_NVMEM -DTARGET_NO_NIXPROF) + add_definitions(-DTARGET_NO_THREADS -DTARGET_NO_EXCEPTIONS -DTARGET_NO_NIXPROF) add_definitions(-DTARGET_NO_COREIO_HTTP -DTARGET_NO_WEBUI -UTARGET_SOFTREND) diff --git a/shell/emscripten/Makefile b/shell/emscripten/Makefile index 1c74e19b6..1b8ef4c22 100644 --- a/shell/emscripten/Makefile +++ b/shell/emscripten/Makefile @@ -26,7 +26,7 @@ LDFLAGS := -Wl,-Map,$(notdir $@).map,--gc-sections -Wl,-O3 -Wl,--sort-common CXXONLYFLAGS := -std=c++11 -CXXFLAGS := -O3 -D GLES -D RELEASE -c -D TARGET_EMSCRIPTEN -D TARGET_NO_REC -D TARGET_NO_NVMEM -D TARGET_NO_WEBUI -D TARGET_NO_THREADS -D TARGET_BOUNDED_EXECUTION -D TARGET_NO_COREIO_HTTP +CXXFLAGS := -O3 -D GLES -D RELEASE -c -D TARGET_EMSCRIPTEN -D TARGET_NO_REC -D TARGET_NO_EXCEPTIONS -D TARGET_NO_WEBUI -D TARGET_NO_THREADS -D TARGET_BOUNDED_EXECUTION -D TARGET_NO_COREIO_HTTP CXXFLAGS += -fno-strict-aliasing CXXFLAGS += -ffast-math diff --git a/shell/nacl/Makefile b/shell/nacl/Makefile index e0d03834e..fec9cf250 100644 --- a/shell/nacl/Makefile +++ b/shell/nacl/Makefile @@ -23,7 +23,7 @@ CFLAGS = -Wno-error -Wno-ignored-attributes CFLAGS += -O3 -fno-strict-aliasing -ffast-math CFLAGS += -I$(RZDCY_SRC_DIR) -I$(RZDCY_SRC_DIR)/deps CFLAGS += -D RELEASE -D TARGET_NO_JIT -D TARGET_NACL32 -DGLES -CFLAGS += -D TARGET_NO_NVMEM -D TARGET_NO_WEBUI -D TARGET_NO_COREIO_HTTP +CFLAGS += -D TARGET_NO_EXCEPTIONS -D TARGET_NO_WEBUI -D TARGET_NO_COREIO_HTTP SOURCES = $(RZDCY_FILES) ../../core/nacl/nacl.cpp From 4458dac49ab4a27637f253aa3b8bfee4d301a07c Mon Sep 17 00:00:00 2001 From: David Guillen Fandos Date: Sat, 11 May 2019 22:35:17 +0200 Subject: [PATCH 278/319] Fixing some missing imports and a bug in pointer tracking. --- core/windows/win_vmem.cpp | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/core/windows/win_vmem.cpp b/core/windows/win_vmem.cpp index b1434b7e2..44d3deabc 100644 --- a/core/windows/win_vmem.cpp +++ b/core/windows/win_vmem.cpp @@ -3,6 +3,8 @@ #include #include +#include "hw/mem/_vmem.h" + // Implementation of the vmem related function for Windows platforms. // For now this probably does some assumptions on the CPU/platform. @@ -29,7 +31,7 @@ static char * base_alloc = NULL; // Plase read the POSIX implementation for more information. On Windows this is // rather straightforward. -VMemType vmem_platform_init(void *vmem_base_addr, void *sh4rcb_addr) { +VMemType vmem_platform_init(void **vmem_base_addr, void **sh4rcb_addr) { // Firt let's try to allocate the in-memory file mem_handle = CreateFileMapping(INVALID_HANDLE_VALUE, 0, PAGE_READWRITE, 0, RAM_SIZE_MAX + VRAM_SIZE_MAX + ARAM_SIZE_MAX, 0); @@ -38,8 +40,8 @@ VMemType vmem_platform_init(void *vmem_base_addr, void *sh4rcb_addr) { base_alloc = (char*)VirtualAlloc(0, memsize, MEM_RESERVE, PAGE_NOACCESS); // Calculate pointers now - sh4rcb_addr = &base_alloc[0]; - vmem_base_addr = &base_alloc[sizeof(Sh4RCB)]; + *sh4rcb_addr = &base_alloc[0]; + *vmem_base_addr = &base_alloc[sizeof(Sh4RCB)]; return MemType512MB; } From 4e5053be20c765286837dff99ee1e411314bfde1 Mon Sep 17 00:00:00 2001 From: David Guillen Fandos Date: Sat, 11 May 2019 22:38:57 +0200 Subject: [PATCH 279/319] Adding win_vmem.cpp to VC project files. --- shell/reicast.vcxproj | 3 ++- shell/reicast.vcxproj.filters | 5 ++++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/shell/reicast.vcxproj b/shell/reicast.vcxproj index 3d10f789a..93904ce9a 100644 --- a/shell/reicast.vcxproj +++ b/shell/reicast.vcxproj @@ -322,6 +322,7 @@ + @@ -998,4 +999,4 @@ for /f "delims=" %%i in ('date /T') do echo #define BUILD_DATE "%%i" >>$(P -
\ No newline at end of file + diff --git a/shell/reicast.vcxproj.filters b/shell/reicast.vcxproj.filters index 1f347b584..59289b0b3 100644 --- a/shell/reicast.vcxproj.filters +++ b/shell/reicast.vcxproj.filters @@ -129,6 +129,9 @@ windows + + windows + profiler @@ -1397,4 +1400,4 @@ - \ No newline at end of file + From 1a4323c1ce3579e797ef00a2dd2a3454f0da6597 Mon Sep 17 00:00:00 2001 From: David Guillen Fandos Date: Sat, 11 May 2019 23:04:22 +0200 Subject: [PATCH 280/319] Fix runtime issue in Windows target, was not mapping pages correctly. --- core/windows/win_vmem.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/core/windows/win_vmem.cpp b/core/windows/win_vmem.cpp index 44d3deabc..32e250a50 100644 --- a/core/windows/win_vmem.cpp +++ b/core/windows/win_vmem.cpp @@ -89,8 +89,8 @@ void vmem_platform_create_mappings(const vmem_mapping *vmem_maps, unsigned numma unsigned offset = vmem_maps[i].start_address + j * vmem_maps[i].memsize; void *ptr = MapViewOfFileEx(mem_handle, protection, 0, vmem_maps[i].memoffset, - vmem_maps[i].memsize, &virt_ram_base[vmem_maps[i].start_address]); - verify(ptr == &virt_ram_base[vmem_maps[i].start_address]); + vmem_maps[i].memsize, &virt_ram_base[offset]); + verify(ptr == &virt_ram_base[offset]); } } } From 6cba98b70ae490621f65a73860226a869de3f842 Mon Sep 17 00:00:00 2001 From: David Guillen Fandos Date: Sun, 12 May 2019 00:02:24 +0200 Subject: [PATCH 281/319] Allocate missing SH4CB and make lock more forgiving. --- core/windows/win_vmem.cpp | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/core/windows/win_vmem.cpp b/core/windows/win_vmem.cpp index 32e250a50..2e456198c 100644 --- a/core/windows/win_vmem.cpp +++ b/core/windows/win_vmem.cpp @@ -12,13 +12,13 @@ // The implementation allows it to be empty (that is, to not lock memory). void VLockedMemory::LockRegion(unsigned offset, unsigned size) { - verify(offset + size < this->size && size != 0); + //verify(offset + size < this->size && size != 0); DWORD old; VirtualProtect(&data[offset], size, PAGE_READONLY, &old); } void VLockedMemory::UnLockRegion(unsigned offset, unsigned size) { - verify(offset + size <= this->size && size != 0); + //verify(offset + size <= this->size && size != 0); DWORD old; VirtualProtect(&data[offset], size, PAGE_READWRITE, &old); } @@ -70,6 +70,12 @@ void vmem_platform_create_mappings(const vmem_mapping *vmem_maps, unsigned numma // Unmap the whole section VirtualFree(base_alloc, 0, MEM_RELEASE); + // Map the SH4CB block too + void *base_ptr = VirtualAlloc(base_alloc, sizeof(Sh4RCB), MEM_RESERVE, PAGE_NOACCESS); + verify(base_ptr == base_alloc); + void *cntx_ptr = VirtualAlloc((u8*)p_sh4rcb + sizeof(p_sh4rcb->fpcb), sizeof(Sh4RCB) - sizeof(p_sh4rcb->fpcb), MEM_COMMIT, PAGE_READWRITE); + verify(cntx_ptr == (u8*)p_sh4rcb + sizeof(p_sh4rcb->fpcb)); + for (unsigned i = 0; i < nummaps; i++) { unsigned address_range_size = vmem_maps[i].end_address - vmem_maps[i].start_address; DWORD protection = vmem_maps[i].allow_writes ? (FILE_MAP_READ | FILE_MAP_WRITE) : FILE_MAP_READ; From 55e7c170302d3a0f6d98673ce787e4d7b999b3cd Mon Sep 17 00:00:00 2001 From: David Guillen Fandos Date: Sun, 12 May 2019 13:40:18 +0200 Subject: [PATCH 282/319] Fix minor build issue with madvise flags. --- core/hw/mem/_vmem.cpp | 2 +- core/linux/posix_vmem.cpp | 7 ++++--- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/core/hw/mem/_vmem.cpp b/core/hw/mem/_vmem.cpp index 9a297330c..3fbfdf6b5 100644 --- a/core/hw/mem/_vmem.cpp +++ b/core/hw/mem/_vmem.cpp @@ -478,7 +478,7 @@ bool _vmem_reserve() { {0x00800000, 0x01000000, MAP_ARAM_START_OFFSET, ARAM_SIZE, false}, // Aica, wraps too {0x20000000, 0x20000000+ARAM_SIZE, MAP_ARAM_START_OFFSET, ARAM_SIZE, true}, {0x01000000, 0x04000000, 0, 0, false}, // More unused - {0x04000000, 0x05000000, MAP_VRAM_START_OFFSET, VRAM_SIZE, true}, // Area 1 (vram, 16MB, wrapped on DC) + {0x04000000, 0x05000000, MAP_VRAM_START_OFFSET, VRAM_SIZE, true}, // Area 1 (vram, 16MB, wrapped on DC as 2x8MB) {0x05000000, 0x06000000, 0, 0, false}, // 32 bit path (unused) {0x06000000, 0x07000000, MAP_VRAM_START_OFFSET, VRAM_SIZE, true}, // VRAM mirror {0x07000000, 0x08000000, 0, 0, false}, // 32 bit path (unused) mirror diff --git a/core/linux/posix_vmem.cpp b/core/linux/posix_vmem.cpp index 8fcf57614..3601eef1b 100644 --- a/core/linux/posix_vmem.cpp +++ b/core/linux/posix_vmem.cpp @@ -141,10 +141,11 @@ void vmem_platform_reset_mem(void *ptr, unsigned size_bytes) { mprotect(ptr, size_bytes, PROT_NONE); // Tell the kernel to flush'em all (FIXME: perhaps unmap+mmap 'd be better?) madvise(ptr, size_bytes, MADV_DONTNEED); - #ifdef MADV_REMOVE + #if defined(MADV_REMOVE) madvise(ptr, size_bytes, MADV_REMOVE); - #endif - madvise(ptr, size_bytes, MADV_FREE); + #elif defined(MADV_FREE) + madvise(ptr, size_bytes, MADV_FREE); + #endif } // Allocates a bunch of memory (page aligned and page-sized) From 15ce3ab5fae15c61edfb93094bc8572dc80d28ab Mon Sep 17 00:00:00 2001 From: Flyinghead Date: Sun, 12 May 2019 16:53:30 +0200 Subject: [PATCH 283/319] OSX build fixes --- core/linux/common.cpp | 4 ---- core/oslib/audiobackend_coreaudio.cpp | 2 +- core/stdclass.cpp | 4 ++++ .../apple/emulator-osx/reicast-osx.xcodeproj/project.pbxproj | 4 ++++ 4 files changed, 9 insertions(+), 5 deletions(-) diff --git a/core/linux/common.cpp b/core/linux/common.cpp index 50df5f662..ce9194436 100644 --- a/core/linux/common.cpp +++ b/core/linux/common.cpp @@ -23,10 +23,6 @@ #include #include #endif -#if HOST_OS == OS_DARWIN -#include -#include -#endif #include #include "hw/sh4/dyna/blockmanager.h" diff --git a/core/oslib/audiobackend_coreaudio.cpp b/core/oslib/audiobackend_coreaudio.cpp index 8e0e9b344..4f7cb44a8 100644 --- a/core/oslib/audiobackend_coreaudio.cpp +++ b/core/oslib/audiobackend_coreaudio.cpp @@ -28,7 +28,7 @@ static u8 samples_temp[BUFSIZE]; static std::atomic samples_wptr; static std::atomic samples_rptr; -static cResetEvent bufferEmpty(false, true); +static cResetEvent bufferEmpty; static OSStatus coreaudio_callback(void* ctx, AudioUnitRenderActionFlags* flags, const AudioTimeStamp* ts, UInt32 bus, UInt32 frames, AudioBufferList* abl) diff --git a/core/stdclass.cpp b/core/stdclass.cpp index 05cc49013..76921d170 100644 --- a/core/stdclass.cpp +++ b/core/stdclass.cpp @@ -5,6 +5,10 @@ #include "types.h" #include "cfg/cfg.h" #include "stdclass.h" +#if HOST_OS == OS_DARWIN +#include +#include +#endif #if COMPILER_VC_OR_CLANG_WIN32 diff --git a/shell/apple/emulator-osx/reicast-osx.xcodeproj/project.pbxproj b/shell/apple/emulator-osx/reicast-osx.xcodeproj/project.pbxproj index cff1dafd5..835041b08 100644 --- a/shell/apple/emulator-osx/reicast-osx.xcodeproj/project.pbxproj +++ b/shell/apple/emulator-osx/reicast-osx.xcodeproj/project.pbxproj @@ -266,6 +266,7 @@ AEE6278E2224762000EC7E89 /* imgui_impl_opengl3.cpp in Sources */ = {isa = PBXBuildFile; fileRef = AEE6278B2224762000EC7E89 /* imgui_impl_opengl3.cpp */; }; AEE6279422247C0A00EC7E89 /* gui_util.cpp in Sources */ = {isa = PBXBuildFile; fileRef = AEE6279222247C0A00EC7E89 /* gui_util.cpp */; }; AEE6279622247C2B00EC7E89 /* keyboard_device.cpp in Sources */ = {isa = PBXBuildFile; fileRef = AEE6279522247C2B00EC7E89 /* keyboard_device.cpp */; }; + AEF2564822886A2E00348550 /* posix_vmem.cpp in Sources */ = {isa = PBXBuildFile; fileRef = AEF2564722886A2E00348550 /* posix_vmem.cpp */; }; AEFF7ECC214AEC810068CE11 /* modem.cpp in Sources */ = {isa = PBXBuildFile; fileRef = AEFF7EC7214AEC800068CE11 /* modem.cpp */; }; AEFF7F4D214D9D590068CE11 /* pico_arp.c in Sources */ = {isa = PBXBuildFile; fileRef = AEFF7EFA214D9D590068CE11 /* pico_arp.c */; }; AEFF7F4E214D9D590068CE11 /* pico_dev_ppp.c in Sources */ = {isa = PBXBuildFile; fileRef = AEFF7EFE214D9D590068CE11 /* pico_dev_ppp.c */; }; @@ -803,6 +804,7 @@ AEE6279222247C0A00EC7E89 /* gui_util.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = gui_util.cpp; sourceTree = ""; }; AEE6279322247C0A00EC7E89 /* gui_util.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = gui_util.h; sourceTree = ""; }; AEE6279522247C2B00EC7E89 /* keyboard_device.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = keyboard_device.cpp; sourceTree = ""; }; + AEF2564722886A2E00348550 /* posix_vmem.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = posix_vmem.cpp; sourceTree = ""; }; AEFF7EC7214AEC800068CE11 /* modem.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = modem.cpp; sourceTree = ""; }; AEFF7EC8214AEC800068CE11 /* modem.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = modem.h; sourceTree = ""; }; AEFF7EC9214AEC800068CE11 /* modem_regs.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = modem_regs.h; sourceTree = ""; }; @@ -1556,6 +1558,7 @@ 84B7BE661B72720100F9733F /* context.cpp */, 84B7BE671B72720100F9733F /* context.h */, 84B7BE681B72720100F9733F /* nixprof */, + AEF2564722886A2E00348550 /* posix_vmem.cpp */, 84B7BE6A1B72720100F9733F /* typedefs.h */, ); name = linux; @@ -2310,6 +2313,7 @@ 84B7BF131B72720200F9733F /* zip_unchange_all.c in Sources */, 84B7BF541B72720200F9733F /* sh4_interrupts.cpp in Sources */, AE2A2D7F21D6851E004B308D /* 7zFile.c in Sources */, + AEF2564822886A2E00348550 /* posix_vmem.cpp in Sources */, 84B7BF6B1B72720200F9733F /* audiostream.cpp in Sources */, 84B7BEFB1B72720200F9733F /* zip_get_file_comment.c in Sources */, 84B7BF301B72720200F9733F /* holly_intc.cpp in Sources */, From cbc2af29ad306975be77c9227e9c6e3380ce1a6c Mon Sep 17 00:00:00 2001 From: David Guillen Fandos Date: Sun, 12 May 2019 17:41:42 +0200 Subject: [PATCH 284/319] Fix small issues for mingw builds on Linux Doenst like the paths, big surprise. I tipically build it like: make platform=win32 CXX=x86_64-w64-mingw32-g++ \ WINDRES=x86_64-w64-mingw32-windres \ CC=x86_64-w64-mingw32-gcc --- core/imgread/ioctl.cpp | 4 ++-- core/rend/d3d11/d3d11.cpp | 6 +++--- core/rend/gles/gles.cpp | 2 +- core/stdclass.h | 2 +- core/windows/winmain.cpp | 10 +++++----- core/windows/xinput_gamepad.h | 2 +- shell/linux/Makefile | 3 ++- 7 files changed, 15 insertions(+), 14 deletions(-) diff --git a/core/imgread/ioctl.cpp b/core/imgread/ioctl.cpp index 4486dc99a..9dfd66b43 100644 --- a/core/imgread/ioctl.cpp +++ b/core/imgread/ioctl.cpp @@ -4,7 +4,7 @@ #include "common.h" #include -#include +#include #include #include "SCSIDEFS.H" @@ -388,4 +388,4 @@ Disc* ioctl_parse(const wchar* file) } } -#endif \ No newline at end of file +#endif diff --git a/core/rend/d3d11/d3d11.cpp b/core/rend/d3d11/d3d11.cpp index bf059af21..974ac94e8 100644 --- a/core/rend/d3d11/d3d11.cpp +++ b/core/rend/d3d11/d3d11.cpp @@ -1,6 +1,6 @@ #include -#include "hw\pvr\Renderer_if.h" -#include "oslib\oslib.h" +#include "hw/pvr/Renderer_if.h" +#include "oslib/oslib.h" #pragma comment(lib,"d3d11.lib") @@ -102,4 +102,4 @@ struct d3d11 : Renderer Renderer* rend_D3D11() { return new d3d11(); -} \ No newline at end of file +} diff --git a/core/rend/gles/gles.cpp b/core/rend/gles/gles.cpp index 9e7c812a2..ea67babf0 100644 --- a/core/rend/gles/gles.cpp +++ b/core/rend/gles/gles.cpp @@ -783,7 +783,7 @@ GLuint fogTextureId; return rv; } - #include + #include void gl_swap() { wglSwapLayerBuffers(ourWindowHandleToDeviceContext,WGL_SWAP_MAIN_PLANE); diff --git a/core/stdclass.h b/core/stdclass.h index 04e9142db..e7f8d2685 100644 --- a/core/stdclass.h +++ b/core/stdclass.h @@ -7,7 +7,7 @@ #if HOST_OS!=OS_WINDOWS #include #else -#include +#include #endif diff --git a/core/windows/winmain.cpp b/core/windows/winmain.cpp index 44633ab0d..adec1ffa9 100644 --- a/core/windows/winmain.cpp +++ b/core/windows/winmain.cpp @@ -1,6 +1,6 @@ -#include "oslib\oslib.h" -#include "oslib\audiostream.h" -#include "imgread\common.h" +#include "oslib/oslib.h" +#include "oslib/audiostream.h" +#include "imgread/common.h" #include "stdclass.h" #include "cfg/cfg.h" #include "xinput_gamepad.h" @@ -10,8 +10,8 @@ #include #include -#include -#include "hw\maple\maple_cfg.h" +#include +#include "hw/maple/maple_cfg.h" #pragma comment(lib, "XInput9_1_0.lib") PCHAR* diff --git a/core/windows/xinput_gamepad.h b/core/windows/xinput_gamepad.h index 336df20e4..beb311413 100644 --- a/core/windows/xinput_gamepad.h +++ b/core/windows/xinput_gamepad.h @@ -1,4 +1,4 @@ -#include +#include #include "input/gamepad_device.h" #include "rend/gui.h" diff --git a/shell/linux/Makefile b/shell/linux/Makefile index f48d5eb08..c3a32668a 100644 --- a/shell/linux/Makefile +++ b/shell/linux/Makefile @@ -18,6 +18,7 @@ CC=${CC_PREFIX}gcc AS=${CC_PREFIX}as STRIP=${CC_PREFIX}strip LD=${CC} +WINDRES=${CC_PREFIX}windres CHD5_LZMA := 1 CHD5_FLAC := 1 @@ -432,7 +433,7 @@ OBJECTS:=$(patsubst $(RZDCY_SRC_DIR)/%,$(BUILDDIR)/%,$(OBJECTS)) ifdef FOR_WINDOWS OBJECTS+=$(BUILDDIR)/reicastres.build_obj $(BUILDDIR)/reicastres.build_obj: $(LOCAL_PATH)/../windows/reicast.rc $(LOCAL_PATH)/../windows/reicast.ico $(RZDCY_SRC_DIR)/version.h - windres $< $@ + $(WINDRES) $< $@ endif DEPDIR := .dep-$(BUILDDIR) From 1a2b1c867204236054f5c9e90d085c1e25df102b Mon Sep 17 00:00:00 2001 From: David Guillen Fandos Date: Sun, 12 May 2019 19:47:11 +0200 Subject: [PATCH 285/319] Remove traces of memory manager from Jit driver. Still stuff to be removed in the Naomi file handler and the ARM AREC, which need to get ported to the new vmem interface. --- core/hw/mem/_vmem.h | 3 ++ core/hw/sh4/dyna/driver.cpp | 66 ++++++------------------------------- core/linux/posix_vmem.cpp | 17 ++++++++++ core/windows/win_vmem.cpp | 41 +++++++++++++++++++++++ 4 files changed, 71 insertions(+), 56 deletions(-) diff --git a/core/hw/mem/_vmem.h b/core/hw/mem/_vmem.h index f739b0454..3213ce3be 100644 --- a/core/hw/mem/_vmem.h +++ b/core/hw/mem/_vmem.h @@ -24,6 +24,9 @@ void vmem_platform_ondemand_page(void *address, unsigned size_bytes); void vmem_platform_create_mappings(const vmem_mapping *vmem_maps, unsigned nummaps); // Just tries to wipe as much as possible in the relevant area. void vmem_platform_destroy(); +// Given a block of data in the .text section, prepares it for JIT action. +// both code_area and size are page aligned. +void vmem_platform_prepare_jit_block(void *code_area, unsigned size, void **code_area_rwx); // Note: if you want to disable vmem magic in any given platform, implement the // above functions as empty functions and make vmem_platform_init return MemTypeError. diff --git a/core/hw/sh4/dyna/driver.cpp b/core/hw/sh4/dyna/driver.cpp index 56e66724e..1a840e693 100644 --- a/core/hw/sh4/dyna/driver.cpp +++ b/core/hw/sh4/dyna/driver.cpp @@ -1,18 +1,12 @@ #include "types.h" -#if HOST_OS==OS_WINDOWS -#include -#elif HOST_OS==OS_LINUX -#include -#include -#endif - #include "../sh4_interpreter.h" #include "../sh4_opcode_list.h" #include "../sh4_core.h" #include "../sh4_if.h" #include "hw/sh4/sh4_interrupts.h" +#include "hw/mem/_vmem.h" #include "hw/sh4/sh4_mem.h" #include "hw/pvr/pvr_mem.h" #include "hw/aica/aica_if.h" @@ -26,9 +20,7 @@ #include "decoder.h" #if FEAT_SHREC != DYNAREC_NONE -//uh uh -#if !defined(_WIN64) u8 SH4_TCB[CODE_SIZE+4096] #if HOST_OS == OS_WINDOWS || FEAT_SHREC != DYNAREC_JIT ; @@ -39,7 +31,6 @@ u8 SH4_TCB[CODE_SIZE+4096] #else #error SH4_TCB ALLOC #endif -#endif u8* CodeCache; @@ -455,55 +446,17 @@ void recSh4_Init() if (_nvmem_enabled()) { verify(mem_b.data==((u8*)p_sh4rcb->sq_buffer+512+0x0C000000)); } - -#if defined(_WIN64) -#ifdef _MSC_VER - for (int i = 10; i < 1300; i++) { + // Prepare some pointer to the pre-allocated code cache: + void *candidate_ptr = (void*)(((unat)SH4_TCB + 4095) & ~4095); - //align to next page .. - u8* ptr = (u8*)recSh4_Init - i * 1024 * 1024; - - CodeCache = (u8*)VirtualAlloc(ptr, CODE_SIZE, MEM_RESERVE | MEM_COMMIT, PAGE_EXECUTE_READWRITE);//; (u8*)(((unat)SH4_TCB+4095)& ~4095); - - if (CodeCache) - break; - } -#else - CodeCache = (u8*)VirtualAlloc(NULL, CODE_SIZE, MEM_RESERVE | MEM_COMMIT, PAGE_EXECUTE_READWRITE); -#endif + // Call the platform-specific magic to make the pages RWX + CodeCache = NULL; + vmem_platform_prepare_jit_block(candidate_ptr, CODE_SIZE, (void**)&CodeCache); + // Ensure the pointer returned is non-null verify(CodeCache != NULL); -#else - CodeCache = (u8*)(((unat)SH4_TCB+4095)& ~4095); -#endif -#if HOST_OS == OS_DARWIN - munmap(CodeCache, CODE_SIZE); - CodeCache = (u8*)mmap(CodeCache, CODE_SIZE, PROT_READ | PROT_WRITE | PROT_EXEC, MAP_FIXED | MAP_PRIVATE | MAP_ANON, 0, 0); -#endif - -#if HOST_OS == OS_WINDOWS - DWORD old; - VirtualProtect(CodeCache,CODE_SIZE,PAGE_EXECUTE_READWRITE,&old); -#elif HOST_OS == OS_LINUX || HOST_OS == OS_DARWIN - - printf("\n\t CodeCache addr: %p | from: %p | addr here: %p\n", CodeCache, CodeCache, recSh4_Init); - - #if FEAT_SHREC == DYNAREC_JIT - if (mprotect(CodeCache, CODE_SIZE, PROT_READ|PROT_WRITE|PROT_EXEC)) - { - perror("\n\tError,Couldn’t mprotect CodeCache!"); - die("Couldn’t mprotect CodeCache"); - } - #endif - -#if TARGET_IPHONE - memset((u8*)mmap(CodeCache, CODE_SIZE, PROT_READ | PROT_WRITE | PROT_EXEC, MAP_FIXED | MAP_PRIVATE | MAP_ANON, 0, 0),0xFF,CODE_SIZE); -#else - memset(CodeCache,0xFF,CODE_SIZE); -#endif - -#endif + memset(CodeCache, 0xFF, CODE_SIZE); ngen_init(); } @@ -532,4 +485,5 @@ void Get_Sh4Recompiler(sh4_if* rv) rv->IsCpuRunning = recSh4_IsCpuRunning; rv->ResetCache = recSh4_ClearCache; } -#endif + +#endif // FEAT_SHREC != DYNAREC_NONE diff --git a/core/linux/posix_vmem.cpp b/core/linux/posix_vmem.cpp index 3601eef1b..c04bf1e39 100644 --- a/core/linux/posix_vmem.cpp +++ b/core/linux/posix_vmem.cpp @@ -176,3 +176,20 @@ void vmem_platform_create_mappings(const vmem_mapping *vmem_maps, unsigned numma } } +// Prepares the code region for JIT operations, thus marking it as RWX +void vmem_platform_prepare_jit_block(void *code_area, unsigned size, void **code_area_rwx) { + // Try to map is as RWX, this fails apparently on OSX (and perhaps other systems?) + if (mprotect(code_area, size, PROT_READ | PROT_WRITE | PROT_EXEC)) { + // Well it failed, use another approach, unmap the memory area and remap it back. + // Seems it works well on Darwin according to reicast code :P + munmap(code_area, size); + void *ret_ptr = mmap(code_area, size, PROT_READ | PROT_WRITE | PROT_EXEC, MAP_FIXED | MAP_PRIVATE | MAP_ANON, 0, 0); + // Ensure it's the area we requested + verify(ret_ptr == code_area); + } + + // Pointer location should be same: + *code_area_rwx = code_area; +} + + diff --git a/core/windows/win_vmem.cpp b/core/windows/win_vmem.cpp index 2e456198c..2ccfe1c12 100644 --- a/core/windows/win_vmem.cpp +++ b/core/windows/win_vmem.cpp @@ -102,3 +102,44 @@ void vmem_platform_create_mappings(const vmem_mapping *vmem_maps, unsigned numma } } +// Prepares the code region for JIT operations, thus marking it as RWX +void vmem_platform_prepare_jit_block(void *code_area, unsigned size, void **code_area_rwx) { + // Several issues on Windows: can't protect arbitrary pages due to (I guess) the way + // kernel tracks mappings, so only stuff that has been allocated with VirtualAlloc can be + // protected (the entire allocation IIUC). + + // Strategy: ignore code_area and allocate a new one. Protect it properly. + // More issues: the area should be "close" to the .text stuff so that code gen works. + // Remember that on x64 we have 4 byte jump/load offset immediates, no issues on x86 :D + + // Take this function addr as reference. + uintptr_t base_addr = reinterpret_cast(&vmem_platform_prepare_jit_block); + + // Probably safe to assume reicast code is <200MB (today seems to be <16MB on every platform I've seen). + for (unsigned i = 0; i < 1800*1024*1024; i += 10*1024*1024) { // Some arbitrary step size. + uintptr_t try_addr_above = base_addr + i; + uintptr_t try_addr_below = base_addr - i; + + // We need to make sure there's no address wrap around the end of the addrspace (meaning: int overflow). + if (try_addr_above > base_addr) { + void *ptr = VirtualAlloc((void*)try_addr_above, size, MEM_RESERVE | MEM_COMMIT, PAGE_EXECUTE_READWRITE); + if (ptr) { + *code_area_rwx = ptr; + break; + } + } + if (try_addr_below < base_addr) { + void *ptr = *code_area_rwx = VirtualAlloc((void*)try_addr_below, size, MEM_RESERVE | MEM_COMMIT, PAGE_EXECUTE_READWRITE); + if (ptr) { + *code_area_rwx = ptr; + break; + } + } + } + + printf("Found code area at %p, not too far away from %p\n", *code_area_rwx, (void*)base_addr); + + // We should have found some area in the addrspace, after all size is ~tens of megabytes. + // Pages are already RWX, all done +} + From 3b760f9869069682c6833626130dfdafc3de22c7 Mon Sep 17 00:00:00 2001 From: David Guillen Fandos Date: Sun, 12 May 2019 22:02:57 +0200 Subject: [PATCH 286/319] Add FEAT_NO_RWX_PAGES and implement it for x64 CPU (Win and Linux) Tested: Both with and without the feature, works only for x64 CPUs for now, but supported in both windows and linux (see vmem implementation for it, using mem-mapped files). --- core/build.h | 9 +++- core/hw/mem/_vmem.h | 7 ++- core/hw/sh4/dyna/blockmanager.cpp | 29 +++++++----- core/hw/sh4/dyna/driver.cpp | 35 +++++++------- core/hw/sh4/dyna/ngen.h | 13 ++++++ core/linux/posix_vmem.cpp | 44 ++++++++++++++---- core/rec-x64/rec_x64.cpp | 13 ++++-- core/windows/win_vmem.cpp | 77 ++++++++++++++++++++++++------- 8 files changed, 166 insertions(+), 61 deletions(-) diff --git a/core/build.h b/core/build.h index 57a00a46c..ba2b4c8c0 100755 --- a/core/build.h +++ b/core/build.h @@ -148,7 +148,7 @@ #define CPU_GENERIC 0x20000005 //used for pnacl, emscripten, etc #define CPU_PPC 0x20000006 #define CPU_PPC64 0x20000007 -#define CPU_A64 0x20000008 +#define CPU_ARM64 0x20000008 #define CPU_MIPS64 0x20000009 //BUILD_COMPILER @@ -326,6 +326,13 @@ #endif +// Some restrictions on FEAT_NO_RWX_PAGES +#if defined(FEAT_NO_RWX_PAGES) && FEAT_SHREC == DYNAREC_JIT +#if HOST_CPU != CPU_X64 && HOST_CPU != CPU_ARM64 +#error "FEAT_NO_RWX_PAGES Only implemented for X64 and ARMv8" +#endif +#endif + // TARGET PLATFORM diff --git a/core/hw/mem/_vmem.h b/core/hw/mem/_vmem.h index 3213ce3be..394194146 100644 --- a/core/hw/mem/_vmem.h +++ b/core/hw/mem/_vmem.h @@ -25,8 +25,11 @@ void vmem_platform_create_mappings(const vmem_mapping *vmem_maps, unsigned numma // Just tries to wipe as much as possible in the relevant area. void vmem_platform_destroy(); // Given a block of data in the .text section, prepares it for JIT action. -// both code_area and size are page aligned. -void vmem_platform_prepare_jit_block(void *code_area, unsigned size, void **code_area_rwx); +// both code_area and size are page aligned. Returns success. +bool vmem_platform_prepare_jit_block(void *code_area, unsigned size, void **code_area_rwx); +// Same as above but uses two address spaces one with RX and RW protections. +// Note: this function doesnt have to be implemented, it's a fallback for the above one. +bool vmem_platform_prepare_jit_block(void *code_area, unsigned size, void **code_area_rw, uintptr_t *rx_offset); // Note: if you want to disable vmem magic in any given platform, implement the // above functions as empty functions and make vmem_platform_init return MemTypeError. diff --git a/core/hw/sh4/dyna/blockmanager.cpp b/core/hw/sh4/dyna/blockmanager.cpp index 0994c3469..e61e8c1dc 100644 --- a/core/hw/sh4/dyna/blockmanager.cpp +++ b/core/hw/sh4/dyna/blockmanager.cpp @@ -86,49 +86,55 @@ u32 bm_gc_luc,bm_gcf_luc; #define FPCA(x) ((DynarecCodeEntryPtr&)sh4rcb.fpcb[(x>>1)&FPCB_MASK]) +// This returns an executable address DynarecCodeEntryPtr DYNACALL bm_GetCode(u32 addr) { - //rdv_FailedToFindBlock_pc=addr; - DynarecCodeEntryPtr rv=(DynarecCodeEntryPtr)FPCA(addr); + DynarecCodeEntryPtr rv = (DynarecCodeEntryPtr)FPCA(addr); return (DynarecCodeEntryPtr)rv; } +// This returns an executable address DynarecCodeEntryPtr DYNACALL bm_GetCode2(u32 addr) { return (DynarecCodeEntryPtr)bm_GetCode(addr); } +// This returns an executable address RuntimeBlockInfo* DYNACALL bm_GetBlock(u32 addr) { - DynarecCodeEntryPtr cde=bm_GetCode(addr); + DynarecCodeEntryPtr cde = bm_GetCode(addr); // Returns RX ptr - if (cde==ngen_FailedToFindBlock) + if (cde == ngen_FailedToFindBlock) return 0; else - return bm_GetBlock((void*)cde); + return bm_GetBlock((void*)cde); // Returns RX pointer } +// This takes a RX address and returns the info block ptr (RW space) RuntimeBlockInfo* bm_GetBlock(void* dynarec_code) { - blkmap_t::iterator iter=blkmap.find((RuntimeBlockInfo*)dynarec_code); - if (iter!=blkmap.end()) + void *dynarecrw = CC_RX2RW(dynarec_code); + blkmap_t::iterator iter = blkmap.find((RuntimeBlockInfo*)dynarecrw); + if (iter != blkmap.end()) { - verify((*iter)->contains_code((u8*)dynarec_code)); + verify((*iter)->contains_code((u8*)dynarecrw)); return *iter; } else { - printf("bm_GetBlock(%p) failed ..\n",dynarec_code); + printf("bm_GetBlock(%p) failed ..\n", dynarec_code); return 0; } } +// Takes RX pointer and returns a RW pointer RuntimeBlockInfo* bm_GetStaleBlock(void* dynarec_code) { + void *dynarecrw = CC_RX2RW(dynarec_code); for(u32 i=0;icontains_code((u8*)dynarec_code)) + if (del_blocks[i]->contains_code((u8*)dynarecrw)) return del_blocks[i]; } @@ -145,9 +151,8 @@ void bm_AddBlock(RuntimeBlockInfo* blk) } blkmap.insert(blk); - verify((void*)bm_GetCode(blk->addr)==(void*)ngen_FailedToFindBlock); - FPCA(blk->addr)=blk->code; + FPCA(blk->addr) = (DynarecCodeEntryPtr)CC_RW2RX(blk->code); #ifdef DYNA_OPROF if (oprofHandle) diff --git a/core/hw/sh4/dyna/driver.cpp b/core/hw/sh4/dyna/driver.cpp index 1a840e693..a93717783 100644 --- a/core/hw/sh4/dyna/driver.cpp +++ b/core/hw/sh4/dyna/driver.cpp @@ -33,7 +33,7 @@ u8 SH4_TCB[CODE_SIZE+4096] #endif u8* CodeCache; - +uintptr_t cc_rx_offset; u32 LastAddr; u32 LastAddr_min; @@ -266,7 +266,7 @@ DynarecCodeEntryPtr DYNACALL rdv_FailedToFindBlock(u32 pc) //printf("rdv_FailedToFindBlock ~ %08X\n",pc); next_pc=pc; - return rdv_CompilePC(); + return (DynarecCodeEntryPtr)CC_RW2RX(rdv_CompilePC()); } static void ngen_FailedToFindBlock_internal() { @@ -305,35 +305,27 @@ DynarecCodeEntryPtr DYNACALL rdv_BlockCheckFail(u32 pc) { next_pc=pc; recSh4_ClearCache(); - return rdv_CompilePC(); -} - -DynarecCodeEntryPtr rdv_FindCode() -{ - DynarecCodeEntryPtr rv=bm_GetCode(next_pc); - if (rv==ngen_FailedToFindBlock) - return 0; - - return rv; + return (DynarecCodeEntryPtr)CC_RW2RX(rdv_CompilePC()); } DynarecCodeEntryPtr rdv_FindOrCompile() { - DynarecCodeEntryPtr rv=bm_GetCode(next_pc); - if (rv==ngen_FailedToFindBlock) - rv=rdv_CompilePC(); + DynarecCodeEntryPtr rv = bm_GetCode(next_pc); // Returns exec addr + if (rv == ngen_FailedToFindBlock) + rv = (DynarecCodeEntryPtr)CC_RW2RX(rdv_CompilePC()); // Returns rw addr return rv; } void* DYNACALL rdv_LinkBlock(u8* code,u32 dpc) { - RuntimeBlockInfo* rbi=bm_GetBlock(code); + // code is the RX addr to return after, however bm_GetBlock returns RW + RuntimeBlockInfo* rbi = bm_GetBlock(code); if (!rbi) { printf("Stale block .."); - rbi=bm_GetStaleBlock(code); + rbi = bm_GetStaleBlock(code); } verify(rbi != NULL); @@ -356,7 +348,7 @@ void* DYNACALL rdv_LinkBlock(u8* code,u32 dpc) next_pc=rbi->NextBlock; } - DynarecCodeEntryPtr rv=rdv_FindOrCompile(); + DynarecCodeEntryPtr rv = rdv_FindOrCompile(); // Returns rx ptr bool do_link=bm_GetBlock(code)==rbi; @@ -452,12 +444,17 @@ void recSh4_Init() // Call the platform-specific magic to make the pages RWX CodeCache = NULL; - vmem_platform_prepare_jit_block(candidate_ptr, CODE_SIZE, (void**)&CodeCache); + #ifdef FEAT_NO_RWX_PAGES + verify(vmem_platform_prepare_jit_block(candidate_ptr, CODE_SIZE, (void**)&CodeCache, &cc_rx_offset)); + #else + verify(vmem_platform_prepare_jit_block(candidate_ptr, CODE_SIZE, (void**)&CodeCache)); + #endif // Ensure the pointer returned is non-null verify(CodeCache != NULL); memset(CodeCache, 0xFF, CODE_SIZE); ngen_init(); + bm_Reset(); } void recSh4_Term() diff --git a/core/hw/sh4/dyna/ngen.h b/core/hw/sh4/dyna/ngen.h index e7d2aff35..3ec9a8101 100644 --- a/core/hw/sh4/dyna/ngen.h +++ b/core/hw/sh4/dyna/ngen.h @@ -48,6 +48,19 @@ #define CODE_SIZE (10*1024*1024) +// When NO_RWX is enabled there's two address-spaces, one executable and +// one writtable. The emitter and most of the code in rec-* will work with +// the RW pointer. However the fpcb table and other pointers during execution +// (ie. exceptions) are RX pointers. These two macros convert between them by +// sub/add the pointer offset. CodeCache will point to the RW pointer for simplicity. +#ifdef FEAT_NO_RWX_PAGES + extern uintptr_t cc_rx_offset; + #define CC_RW2RX(ptr) (void*)(((uintptr_t)ptr) + cc_rx_offset) + #define CC_RX2RW(ptr) (void*)(((uintptr_t)ptr) - cc_rx_offset) +#else + #define CC_RW2RX(ptr) (ptr) + #define CC_RX2RW(ptr) (ptr) +#endif //alternative emit ptr, set to 0 to use the main buffer extern u32* emit_ptr; diff --git a/core/linux/posix_vmem.cpp b/core/linux/posix_vmem.cpp index c04bf1e39..8547955e1 100644 --- a/core/linux/posix_vmem.cpp +++ b/core/linux/posix_vmem.cpp @@ -62,14 +62,14 @@ void VLockedMemory::UnLockRegion(unsigned offset, unsigned size_bytes) { } // Allocates memory via a fd on shmem/ahmem or even a file on disk -static int allocate_shared_filemem() { +static int allocate_shared_filemem(unsigned size) { int fd = -1; #if defined(_ANDROID) // Use Android's specific shmem stuff. - fd = ashmem_create_region(0, RAM_SIZE_MAX + VRAM_SIZE_MAX + ARAM_SIZE_MAX); + fd = ashmem_create_region(0, size); #else #if HOST_OS != OS_DARWIN - fd = shm_open("/dcnzorz_mem", O_CREAT | O_EXCL | O_RDWR,S_IREAD | S_IWRITE); + fd = shm_open("/dcnzorz_mem", O_CREAT | O_EXCL | O_RDWR, S_IREAD | S_IWRITE); shm_unlink("/dcnzorz_mem"); #endif @@ -84,7 +84,7 @@ static int allocate_shared_filemem() { return -1; // Finally make the file as big as we need! - if (ftruncate(fd, RAM_SIZE_MAX + VRAM_SIZE_MAX + ARAM_SIZE_MAX)) { + if (ftruncate(fd, size)) { // Can't get as much memory as needed, fallback. close(fd); return -1; @@ -97,7 +97,7 @@ static int allocate_shared_filemem() { // Implement vmem initialization for RAM, ARAM, VRAM and SH4 context, fpcb etc. // The function supports allocating 512MB or 4GB addr spaces. -static int shmem_fd = -1; +static int shmem_fd = -1, shmem_fd2 = -1; // vmem_base_addr points to an address space of 512MB (or 4GB) that can be used for fast memory ops. // In negative offsets of the pointer (up to FPCB size, usually 65/129MB) the context and jump table @@ -105,7 +105,7 @@ static int shmem_fd = -1; // memory using a fallback (that is, regular mallocs and falling back to slow memory JIT). VMemType vmem_platform_init(void **vmem_base_addr, void **sh4rcb_addr) { // Firt let's try to allocate the shm-backed memory - shmem_fd = allocate_shared_filemem(); + shmem_fd = allocate_shared_filemem(RAM_SIZE_MAX + VRAM_SIZE_MAX + ARAM_SIZE_MAX); if (shmem_fd < 0) return MemTypeError; @@ -177,7 +177,7 @@ void vmem_platform_create_mappings(const vmem_mapping *vmem_maps, unsigned numma } // Prepares the code region for JIT operations, thus marking it as RWX -void vmem_platform_prepare_jit_block(void *code_area, unsigned size, void **code_area_rwx) { +bool vmem_platform_prepare_jit_block(void *code_area, unsigned size, void **code_area_rwx) { // Try to map is as RWX, this fails apparently on OSX (and perhaps other systems?) if (mprotect(code_area, size, PROT_READ | PROT_WRITE | PROT_EXEC)) { // Well it failed, use another approach, unmap the memory area and remap it back. @@ -185,11 +185,39 @@ void vmem_platform_prepare_jit_block(void *code_area, unsigned size, void **code munmap(code_area, size); void *ret_ptr = mmap(code_area, size, PROT_READ | PROT_WRITE | PROT_EXEC, MAP_FIXED | MAP_PRIVATE | MAP_ANON, 0, 0); // Ensure it's the area we requested - verify(ret_ptr == code_area); + if (ret_ptr != code_area) + return false; // Couldn't remap it? Perhaps RWX is disabled? This should never happen in any supported Unix platform. } // Pointer location should be same: *code_area_rwx = code_area; + return true; +} + +// Use two addr spaces: need to remap something twice, therefore use allocate_shared_filemem() +bool vmem_platform_prepare_jit_block(void *code_area, unsigned size, void **code_area_rw, uintptr_t *rx_offset) { + shmem_fd2 = allocate_shared_filemem(size); + if (shmem_fd2 < 0) + return false; + + // Need to unmap the section we are about to use (it might be already unmapped but nevertheless...) + munmap(code_area, size); + + // Map the RX bits on the code_area, for proximity, as usual. + void *ptr_rx = mmap(code_area, size, PROT_READ | PROT_EXEC, + MAP_SHARED | MAP_NOSYNC | MAP_FIXED, shmem_fd2, 0); + if (ptr_rx != code_area) + return false; + + // Now remap the same memory as RW in some location we don't really care at all. + void *ptr_rw = mmap(NULL, size, PROT_READ | PROT_WRITE, + MAP_SHARED | MAP_NOSYNC, shmem_fd2, 0); + + *code_area_rw = ptr_rw; + *rx_offset = (char*)ptr_rx - (char*)ptr_rw; + printf("Info: Using NO_RWX mode, rx ptr: %p, rw ptr: %p, offset: %p\n", ptr_rx, ptr_rw, *rx_offset); + + return (ptr_rw != MAP_FAILED); } diff --git a/core/rec-x64/rec_x64.cpp b/core/rec-x64/rec_x64.cpp index 5717f73b1..02b098d68 100644 --- a/core/rec-x64/rec_x64.cpp +++ b/core/rec-x64/rec_x64.cpp @@ -251,7 +251,14 @@ public: regalloc.DoAlloc(block); + #ifdef FEAT_NO_RWX_PAGES + // Use absolute addressing for this one + // TODO(davidgfnet) remove the ifsef using CC_RX2RW/CC_RW2RX + mov(rax, (uintptr_t)&cycle_counter); + sub(dword[rax], block->guest_cycles); + #else sub(dword[rip + &cycle_counter], block->guest_cycles); + #endif #ifdef PROFILING mov(rax, (uintptr_t)&guest_cpu_cycles); mov(ecx, block->guest_cycles); @@ -1104,7 +1111,7 @@ private: mov(rax, reinterpret_cast(ptr)); mov(edx, *(u32*)ptr); cmp(dword[rax], edx); - jne(reinterpret_cast(&ngen_blockcheckfail)); + jne(reinterpret_cast(CC_RX2RW(&ngen_blockcheckfail))); } } break; @@ -1140,7 +1147,7 @@ private: sz -= 2; sa += 2; } - jne(reinterpret_cast(&ngen_blockcheckfail)); + jne(reinterpret_cast(CC_RX2RW(&ngen_blockcheckfail))); ptr = (void*)GetMemPtr(sa, sz > 8 ? 8 : sz); } } @@ -1184,7 +1191,7 @@ private: movd(ptr[rsp + 12], xmm11); #endif - call(function); + call(CC_RX2RW(function)); #ifndef _WIN32 movd(xmm8, ptr[rsp + 0]); diff --git a/core/windows/win_vmem.cpp b/core/windows/win_vmem.cpp index 2ccfe1c12..d62d138cf 100644 --- a/core/windows/win_vmem.cpp +++ b/core/windows/win_vmem.cpp @@ -23,7 +23,7 @@ void VLockedMemory::UnLockRegion(unsigned offset, unsigned size) { VirtualProtect(&data[offset], size, PAGE_READWRITE, &old); } -static HANDLE mem_handle = INVALID_HANDLE_VALUE; +static HANDLE mem_handle = INVALID_HANDLE_VALUE, mem_handle2 = INVALID_HANDLE_VALUE; static char * base_alloc = NULL; // Implement vmem initialization for RAM, ARAM, VRAM and SH4 context, fpcb etc. @@ -102,8 +102,10 @@ void vmem_platform_create_mappings(const vmem_mapping *vmem_maps, unsigned numma } } -// Prepares the code region for JIT operations, thus marking it as RWX -void vmem_platform_prepare_jit_block(void *code_area, unsigned size, void **code_area_rwx) { +typedef void* (*mapper_fn) (void *addr, unsigned size); + +// This is a tempalted function since it's used twice +static void* vmem_platform_prepare_jit_block_template(void *code_area, unsigned size, mapper_fn mapper) { // Several issues on Windows: can't protect arbitrary pages due to (I guess) the way // kernel tracks mappings, so only stuff that has been allocated with VirtualAlloc can be // protected (the entire allocation IIUC). @@ -113,33 +115,76 @@ void vmem_platform_prepare_jit_block(void *code_area, unsigned size, void **code // Remember that on x64 we have 4 byte jump/load offset immediates, no issues on x86 :D // Take this function addr as reference. - uintptr_t base_addr = reinterpret_cast(&vmem_platform_prepare_jit_block); + uintptr_t base_addr = reinterpret_cast(&vmem_platform_init) & ~0xFFFFF; // Probably safe to assume reicast code is <200MB (today seems to be <16MB on every platform I've seen). - for (unsigned i = 0; i < 1800*1024*1024; i += 10*1024*1024) { // Some arbitrary step size. + for (unsigned i = 0; i < 1800*1024*1024; i += 8*1024*1024) { // Some arbitrary step size. uintptr_t try_addr_above = base_addr + i; uintptr_t try_addr_below = base_addr - i; // We need to make sure there's no address wrap around the end of the addrspace (meaning: int overflow). if (try_addr_above > base_addr) { - void *ptr = VirtualAlloc((void*)try_addr_above, size, MEM_RESERVE | MEM_COMMIT, PAGE_EXECUTE_READWRITE); - if (ptr) { - *code_area_rwx = ptr; - break; - } + void *ptr = mapper((void*)try_addr_above, size); + if (ptr) + return ptr; } if (try_addr_below < base_addr) { - void *ptr = *code_area_rwx = VirtualAlloc((void*)try_addr_below, size, MEM_RESERVE | MEM_COMMIT, PAGE_EXECUTE_READWRITE); - if (ptr) { - *code_area_rwx = ptr; - break; - } + void *ptr = mapper((void*)try_addr_below, size); + if (ptr) + return ptr; } } + return NULL; +} - printf("Found code area at %p, not too far away from %p\n", *code_area_rwx, (void*)base_addr); +static void* mem_alloc(void *addr, unsigned size) { + return VirtualAlloc(addr, size, MEM_RESERVE | MEM_COMMIT, PAGE_EXECUTE_READWRITE); +} + +// Prepares the code region for JIT operations, thus marking it as RWX +bool vmem_platform_prepare_jit_block(void *code_area, unsigned size, void **code_area_rwx) { + // Get the RWX page close to the code_area + void *ptr = vmem_platform_prepare_jit_block_template(code_area, size, &mem_alloc); + if (!ptr) + return false; + + *code_area_rwx = ptr; + printf("Found code area at %p, not too far away from %p\n", *code_area_rwx, &vmem_platform_init); // We should have found some area in the addrspace, after all size is ~tens of megabytes. // Pages are already RWX, all done + return true; +} + + +static void* mem_file_map(void *addr, unsigned size) { + // Maps the entire file at the specified addr. + void *ptr = VirtualAlloc(addr, size, MEM_RESERVE, PAGE_NOACCESS); + if (!ptr) + return NULL; + VirtualFree(ptr, 0, MEM_RELEASE); + if (ptr != addr) + return NULL; + + return MapViewOfFileEx(mem_handle2, FILE_MAP_READ | FILE_MAP_EXECUTE, 0, 0, size, addr); +} + +// Use two addr spaces: need to remap something twice, therefore use CreateFileMapping() +bool vmem_platform_prepare_jit_block(void *code_area, unsigned size, void **code_area_rw, uintptr_t *rx_offset) { + mem_handle2 = CreateFileMapping(INVALID_HANDLE_VALUE, 0, PAGE_EXECUTE_READWRITE, 0, size, 0); + + // Get the RX page close to the code_area + void *ptr_rx = vmem_platform_prepare_jit_block_template(code_area, size, &mem_file_map); + if (!ptr_rx) + return false; + + // Ok now we just remap the RW segment at any position (dont' care). + void *ptr_rw = MapViewOfFileEx(mem_handle2, FILE_MAP_READ | FILE_MAP_WRITE, 0, 0, size, NULL); + + *code_area_rw = ptr_rw; + *rx_offset = (char*)ptr_rx - (char*)ptr_rw; + printf("Info: Using NO_RWX mode, rx ptr: %p, rw ptr: %p, offset: %p\n", ptr_rx, ptr_rw, *rx_offset); + + return (ptr_rw != NULL); } From aa4fc8dd601e5a832318bc907115a40ac7535a04 Mon Sep 17 00:00:00 2001 From: David Guillen Fandos Date: Sun, 12 May 2019 23:25:43 +0200 Subject: [PATCH 287/319] Move cache invalidation to vmem_platform and implement NO_RWX on arm64 Still not functional yet, this commit breaks arm64 on android for instance. Rewrites are not yet figured out, will push them in the next commit. --- core/hw/aica/dsp_arm64.cpp | 14 +++---- core/hw/arm7/arm64.cpp | 8 ++-- core/hw/mem/_vmem.cpp | 1 + core/hw/mem/_vmem.h | 2 + core/linux/posix_vmem.cpp | 56 +++++++++++++++++++++++++++- core/rec-ARM64/rec_arm64.cpp | 71 ++++++++++++------------------------ core/windows/win_vmem.cpp | 2 +- 7 files changed, 95 insertions(+), 59 deletions(-) diff --git a/core/hw/aica/dsp_arm64.cpp b/core/hw/aica/dsp_arm64.cpp index ba5515ed4..cac7b4c71 100644 --- a/core/hw/aica/dsp_arm64.cpp +++ b/core/hw/aica/dsp_arm64.cpp @@ -27,7 +27,7 @@ #include "deps/vixl/aarch64/macro-assembler-aarch64.h" using namespace vixl::aarch64; -extern void Arm64CacheFlush(void* start, void* end); +extern void vmem_platform_flush_cache(void *icache_start, void *icache_end, void *dcache_start, void *dcache_end); class DSPAssembler : public MacroAssembler { @@ -54,9 +54,9 @@ public: Stp(xzr, xzr, MemOperand(x0, 48)); Ret(); FinalizeCode(); -#ifdef _ANDROID - Arm64CacheFlush(GetBuffer()->GetStartAddress(), GetBuffer()->GetEndAddress()); -#endif + vmem_platform_flush_cache( + GetBuffer()->GetStartAddress(), GetBuffer()->GetEndAddress(), + GetBuffer()->GetStartAddress(), GetBuffer()->GetEndAddress()); return; } @@ -387,9 +387,9 @@ public: #endif FinalizeCode(); -#ifdef _ANDROID - Arm64CacheFlush(GetBuffer()->GetStartAddress(), GetBuffer()->GetEndAddress()); -#endif + vmem_platform_flush_cache( + GetBuffer()->GetStartAddress(), GetBuffer()->GetEndAddress(), + GetBuffer()->GetStartAddress(), GetBuffer()->GetEndAddress()); } private: diff --git a/core/hw/arm7/arm64.cpp b/core/hw/arm7/arm64.cpp index 89e57ab66..e712855bd 100644 --- a/core/hw/arm7/arm64.cpp +++ b/core/hw/arm7/arm64.cpp @@ -28,7 +28,7 @@ using namespace vixl::aarch64; //#include "deps/vixl/aarch32/disasm-aarch32.h" -extern void Arm64CacheFlush(void* start, void* end); +extern void vmem_platform_flush_cache(void *icache_start, void *icache_end, void *dcache_start, void *dcache_end); extern u32 arm_single_op(u32 opcode); extern "C" void arm_dispatch(); extern "C" void arm_exit(); @@ -41,7 +41,7 @@ extern reg_pair arm_Reg[RN_ARM_REG_COUNT]; MacroAssembler *assembler; extern "C" void armFlushICache(void *bgn, void *end) { - Arm64CacheFlush(bgn, end); + vmem_platform_flush_cache(bgn, end, bgn, end); } static MemOperand arm_reg_operand(u32 regn) @@ -143,7 +143,9 @@ void armv_end(void* codestart, u32 cycl) assembler->FinalizeCode(); verify(assembler->GetBuffer()->GetCursorOffset() <= assembler->GetBuffer()->GetCapacity()); - Arm64CacheFlush(codestart, assembler->GetBuffer()->GetEndAddress()); + vmem_platform_flush_cache( + codestart, assembler->GetBuffer()->GetEndAddress(), + codestart, assembler->GetBuffer()->GetEndAddress()); icPtr += assembler->GetBuffer()->GetSizeInBytes(); #if 0 diff --git a/core/hw/mem/_vmem.cpp b/core/hw/mem/_vmem.cpp index 3fbfdf6b5..cabe7941c 100644 --- a/core/hw/mem/_vmem.cpp +++ b/core/hw/mem/_vmem.cpp @@ -469,6 +469,7 @@ bool _vmem_reserve() { } else { printf("Info: nvmem is enabled, with addr space of size %s\n", vmemstatus == MemType4GB ? "4GB" : "512MB"); + printf("Info: p_sh4rcb: %p virt_ram_base: %p\n", p_sh4rcb, virt_ram_base); // Map the different parts of the memory file into the new memory range we got. #define MAP_RAM_START_OFFSET 0 #define MAP_VRAM_START_OFFSET (MAP_RAM_START_OFFSET+RAM_SIZE) diff --git a/core/hw/mem/_vmem.h b/core/hw/mem/_vmem.h index 394194146..dca2c09e3 100644 --- a/core/hw/mem/_vmem.h +++ b/core/hw/mem/_vmem.h @@ -30,6 +30,8 @@ bool vmem_platform_prepare_jit_block(void *code_area, unsigned size, void **code // Same as above but uses two address spaces one with RX and RW protections. // Note: this function doesnt have to be implemented, it's a fallback for the above one. bool vmem_platform_prepare_jit_block(void *code_area, unsigned size, void **code_area_rw, uintptr_t *rx_offset); +// This might not need an implementation (ie x86/64 cpus). +void vmem_platform_flush_cache(void *icache_start, void *icache_end, void *dcache_start, void *dcache_end); // Note: if you want to disable vmem magic in any given platform, implement the // above functions as empty functions and make vmem_platform_init return MemTypeError. diff --git a/core/linux/posix_vmem.cpp b/core/linux/posix_vmem.cpp index 8547955e1..9a573cdae 100644 --- a/core/linux/posix_vmem.cpp +++ b/core/linux/posix_vmem.cpp @@ -215,9 +215,63 @@ bool vmem_platform_prepare_jit_block(void *code_area, unsigned size, void **code *code_area_rw = ptr_rw; *rx_offset = (char*)ptr_rx - (char*)ptr_rw; - printf("Info: Using NO_RWX mode, rx ptr: %p, rw ptr: %p, offset: %p\n", ptr_rx, ptr_rw, *rx_offset); + printf("Info: Using NO_RWX mode, rx ptr: %p, rw ptr: %p, offset: %lu\n", ptr_rx, ptr_rw, (unsigned long)*rx_offset); return (ptr_rw != MAP_FAILED); } +// Some OSes restrict cache flushing, cause why not right? :D + +#if HOST_CPU == CPU_ARM64 + +// Code borrowed from Dolphin https://github.com/dolphin-emu/dolphin +static void Arm64_CacheFlush(void* start, void* end) { + if (start == end) + return; + +#if HOST_OS == OS_DARWIN + // Header file says this is equivalent to: sys_icache_invalidate(start, end - start); + sys_cache_control(kCacheFunctionPrepareForExecution, start, end - start); +#else + // Don't rely on GCC's __clear_cache implementation, as it caches + // icache/dcache cache line sizes, that can vary between cores on + // big.LITTLE architectures. + u64 addr, ctr_el0; + static size_t icache_line_size = 0xffff, dcache_line_size = 0xffff; + size_t isize, dsize; + + __asm__ volatile("mrs %0, ctr_el0" : "=r"(ctr_el0)); + isize = 4 << ((ctr_el0 >> 0) & 0xf); + dsize = 4 << ((ctr_el0 >> 16) & 0xf); + + // use the global minimum cache line size + icache_line_size = isize = icache_line_size < isize ? icache_line_size : isize; + dcache_line_size = dsize = dcache_line_size < dsize ? dcache_line_size : dsize; + + addr = (u64)start & ~(u64)(dsize - 1); + for (; addr < (u64)end; addr += dsize) + // use "civac" instead of "cvau", as this is the suggested workaround for + // Cortex-A53 errata 819472, 826319, 827319 and 824069. + __asm__ volatile("dc civac, %0" : : "r"(addr) : "memory"); + __asm__ volatile("dsb ish" : : : "memory"); + + addr = (u64)start & ~(u64)(isize - 1); + for (; addr < (u64)end; addr += isize) + __asm__ volatile("ic ivau, %0" : : "r"(addr) : "memory"); + + __asm__ volatile("dsb ish" : : : "memory"); + __asm__ volatile("isb" : : : "memory"); +#endif +} + + +void vmem_platform_flush_cache(void *icache_start, void *icache_end, void *dcache_start, void *dcache_end) { + Arm64_CacheFlush(dcache_start, dcache_end); + + // Dont risk it and flush and invalidate icache&dcache for both ranges just in case. + if (icache_start != dcache_start) + Arm64_CacheFlush(icache_start, icache_end); +} + +#endif // #if HOST_CPU == CPU_ARM64 diff --git a/core/rec-ARM64/rec_arm64.cpp b/core/rec-ARM64/rec_arm64.cpp index 2a9108349..03795ab13 100644 --- a/core/rec-ARM64/rec_arm64.cpp +++ b/core/rec-ARM64/rec_arm64.cpp @@ -45,12 +45,11 @@ using namespace vixl::aarch64; extern "C" void no_update(); extern "C" void intc_sched(); extern "C" void ngen_blockcheckfail(u32 pc); - extern "C" void ngen_LinkBlock_Generic_stub(); extern "C" void ngen_LinkBlock_cond_Branch_stub(); extern "C" void ngen_LinkBlock_cond_Next_stub(); - extern "C" void ngen_FailedToFindBlock_(); +extern void vmem_platform_flush_cache(void *icache_start, void *icache_end, void *dcache_start, void *dcache_end); struct DynaRBI : RuntimeBlockInfo { @@ -61,47 +60,6 @@ struct DynaRBI : RuntimeBlockInfo } }; -// Code borrowed from Dolphin https://github.com/dolphin-emu/dolphin -void Arm64CacheFlush(void* start, void* end) -{ - if (start == end) - return; - -#if HOST_OS == OS_DARWIN - // Header file says this is equivalent to: sys_icache_invalidate(start, end - start); - sys_cache_control(kCacheFunctionPrepareForExecution, start, end - start); -#else - // Don't rely on GCC's __clear_cache implementation, as it caches - // icache/dcache cache line sizes, that can vary between cores on - // big.LITTLE architectures. - u64 addr, ctr_el0; - static size_t icache_line_size = 0xffff, dcache_line_size = 0xffff; - size_t isize, dsize; - - __asm__ volatile("mrs %0, ctr_el0" : "=r"(ctr_el0)); - isize = 4 << ((ctr_el0 >> 0) & 0xf); - dsize = 4 << ((ctr_el0 >> 16) & 0xf); - - // use the global minimum cache line size - icache_line_size = isize = icache_line_size < isize ? icache_line_size : isize; - dcache_line_size = dsize = dcache_line_size < dsize ? dcache_line_size : dsize; - - addr = (u64)start & ~(u64)(dsize - 1); - for (; addr < (u64)end; addr += dsize) - // use "civac" instead of "cvau", as this is the suggested workaround for - // Cortex-A53 errata 819472, 826319, 827319 and 824069. - __asm__ volatile("dc civac, %0" : : "r"(addr) : "memory"); - __asm__ volatile("dsb ish" : : : "memory"); - - addr = (u64)start & ~(u64)(isize - 1); - for (; addr < (u64)end; addr += isize) - __asm__ volatile("ic ivau, %0" : : "r"(addr) : "memory"); - - __asm__ volatile("dsb ish" : : : "memory"); - __asm__ volatile("isb" : : : "memory"); -#endif -} - double host_cpu_time; u64 guest_cpu_cycles; @@ -147,7 +105,7 @@ __asm__ "ngen_LinkBlock_Shared_stub: \n\t" "mov x0, lr \n\t" "sub x0, x0, #4 \n\t" // go before the call - "bl rdv_LinkBlock \n\t" + "bl rdv_LinkBlock \n\t" // returns an RX addr "br x0 \n" ".hidden ngen_FailedToFindBlock_ \n\t" @@ -1013,7 +971,7 @@ public: Ldr(w29, sh4_context_mem_operand(&next_pc)); - GenBranch(no_update); + GenBranchRuntime(no_update); break; default: @@ -1038,7 +996,12 @@ public: emit_Skip(block->host_code_size); } - Arm64CacheFlush(GetBuffer()->GetStartAddress(), GetBuffer()->GetEndAddress()); + + // Flush and invalidate caches + vmem_platform_flush_cache( + CC_RW2RX(GetBuffer()->GetStartAddress()), CC_RW2RX(GetBuffer()->GetEndAddress()), + GetBuffer()->GetStartAddress(), GetBuffer()->GetEndAddress()); + #if 0 // if (rewrite) { @@ -1060,10 +1023,13 @@ public: } private: + // Runtime branches/calls need to be adjusted if rx space is different to rw space. + // Therefore can't mix GenBranch with GenBranchRuntime! + template void GenCallRuntime(R (*function)(P...)) { - ptrdiff_t offset = reinterpret_cast(function) - GetBuffer()->GetStartAddress(); + ptrdiff_t offset = reinterpret_cast(function) - reinterpret_cast(CC_RW2RX(GetBuffer()->GetStartAddress())); verify(offset >= -128 * 1024 * 1024 && offset <= 128 * 1024 * 1024); verify((offset & 3) == 0); Label function_label; @@ -1071,6 +1037,17 @@ private: Bl(&function_label); } + template + void GenBranchRuntime(R (*target)(P...)) + { + ptrdiff_t offset = reinterpret_cast(target) - reinterpret_cast(CC_RW2RX(GetBuffer()->GetStartAddress())); + verify(offset >= -128 * 1024 * 1024 && offset <= 128 * 1024 * 1024); + verify((offset & 3) == 0); + Label target_label; + BindToOffset(&target_label, offset); + B(&target_label); + } + template void GenBranch(R (*code)(P...), Condition cond = al) { diff --git a/core/windows/win_vmem.cpp b/core/windows/win_vmem.cpp index d62d138cf..edb4b21cd 100644 --- a/core/windows/win_vmem.cpp +++ b/core/windows/win_vmem.cpp @@ -183,7 +183,7 @@ bool vmem_platform_prepare_jit_block(void *code_area, unsigned size, void **code *code_area_rw = ptr_rw; *rx_offset = (char*)ptr_rx - (char*)ptr_rw; - printf("Info: Using NO_RWX mode, rx ptr: %p, rw ptr: %p, offset: %p\n", ptr_rx, ptr_rw, *rx_offset); + printf("Info: Using NO_RWX mode, rx ptr: %p, rw ptr: %p, offset: %lu\n", ptr_rx, ptr_rw, (unsigned long)*rx_offset); return (ptr_rw != NULL); } From 65e221879210d46ee2925a8c49593f1204d802cc Mon Sep 17 00:00:00 2001 From: David Guillen Fandos Date: Mon, 13 May 2019 00:23:18 +0200 Subject: [PATCH 288/319] Fix ARM64 rewrites with NO_RWX_PAGES Tested on nvidia jetson and Android, so far works great. --- core/hw/sh4/dyna/ngen.h | 4 ++-- core/linux/common.cpp | 6 +----- core/rec-ARM64/rec_arm64.cpp | 7 ++++--- 3 files changed, 7 insertions(+), 10 deletions(-) diff --git a/core/hw/sh4/dyna/ngen.h b/core/hw/sh4/dyna/ngen.h index 3ec9a8101..88157a304 100644 --- a/core/hw/sh4/dyna/ngen.h +++ b/core/hw/sh4/dyna/ngen.h @@ -55,8 +55,8 @@ // sub/add the pointer offset. CodeCache will point to the RW pointer for simplicity. #ifdef FEAT_NO_RWX_PAGES extern uintptr_t cc_rx_offset; - #define CC_RW2RX(ptr) (void*)(((uintptr_t)ptr) + cc_rx_offset) - #define CC_RX2RW(ptr) (void*)(((uintptr_t)ptr) - cc_rx_offset) + #define CC_RW2RX(ptr) (void*)(((uintptr_t)(ptr)) + cc_rx_offset) + #define CC_RX2RW(ptr) (void*)(((uintptr_t)(ptr)) - cc_rx_offset) #else #define CC_RW2RX(ptr) (ptr) #define CC_RX2RW(ptr) (ptr) diff --git a/core/linux/common.cpp b/core/linux/common.cpp index ce9194436..39e3efd40 100644 --- a/core/linux/common.cpp +++ b/core/linux/common.cpp @@ -59,12 +59,8 @@ void fault_handler (int sn, siginfo_t * si, void *segfault_ctx) context_from_segfault(&ctx, segfault_ctx); - bool dyna_cde = ((unat)ctx.pc>(unat)CodeCache) && ((unat)ctx.pc<(unat)(CodeCache + CODE_SIZE)); + bool dyna_cde = ((unat)CC_RX2RW(ctx.pc) > (unat)CodeCache) && ((unat)CC_RX2RW(ctx.pc) < (unat)(CodeCache + CODE_SIZE)); - //ucontext_t* ctx=(ucontext_t*)ctxr; - //printf("mprot hit @ ptr 0x%08X @@ code: %08X, %d\n",si->si_addr,ctx->uc_mcontext.arm_pc,dyna_cde); - - if (VramLockedWrite((u8*)si->si_addr) || BM_LockedWrite((u8*)si->si_addr)) return; #if FEAT_SHREC == DYNAREC_JIT diff --git a/core/rec-ARM64/rec_arm64.cpp b/core/rec-ARM64/rec_arm64.cpp index 03795ab13..cfca5c4c3 100644 --- a/core/rec-ARM64/rec_arm64.cpp +++ b/core/rec-ARM64/rec_arm64.cpp @@ -1442,13 +1442,14 @@ void ngen_CC_Finish(shil_opcode* op) bool ngen_Rewrite(unat& host_pc, unat, unat) { //printf("ngen_Rewrite pc %p\n", host_pc); - RuntimeBlockInfo *block = bm_GetBlock((void *)host_pc); + void *host_pc_rw = CC_RX2RW(host_pc); + RuntimeBlockInfo *block = bm_GetBlock((void*)host_pc); if (block == NULL) { printf("ngen_Rewrite: Block at %p not found\n", (void *)host_pc); return false; } - u32 *code_ptr = (u32*)host_pc; + u32 *code_ptr = (u32*)host_pc_rw; auto it = block->memory_accesses.find(code_ptr); if (it == block->memory_accesses.end()) { @@ -1466,7 +1467,7 @@ bool ngen_Rewrite(unat& host_pc, unat, unat) assembler->GenWriteMemorySlow(op); assembler->Finalize(true); delete assembler; - host_pc = (unat)(code_ptr - 2); + host_pc = (unat)CC_RW2RX(code_ptr - 2); return true; } From 3463872fdf4f690b61a74585fb2670962ddb0e99 Mon Sep 17 00:00:00 2001 From: David Guillen Fandos Date: Mon, 13 May 2019 01:35:40 +0200 Subject: [PATCH 289/319] Fix small clang build issue. --- core/rec-ARM64/rec_arm64.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/rec-ARM64/rec_arm64.cpp b/core/rec-ARM64/rec_arm64.cpp index cfca5c4c3..65afd83d0 100644 --- a/core/rec-ARM64/rec_arm64.cpp +++ b/core/rec-ARM64/rec_arm64.cpp @@ -1442,7 +1442,7 @@ void ngen_CC_Finish(shil_opcode* op) bool ngen_Rewrite(unat& host_pc, unat, unat) { //printf("ngen_Rewrite pc %p\n", host_pc); - void *host_pc_rw = CC_RX2RW(host_pc); + void *host_pc_rw = (void*)CC_RX2RW(host_pc); RuntimeBlockInfo *block = bm_GetBlock((void*)host_pc); if (block == NULL) { From d9f2f9ae1d45a3d2478a68b7969719471af6d28e Mon Sep 17 00:00:00 2001 From: Flyinghead Date: Wed, 15 May 2019 12:54:03 +0200 Subject: [PATCH 290/319] rend: define GLES2 and GLES3 GLES2 will only use GL ES 2.0 API functions, compile-time linked GLES3 loads functions dynamically and supports both GL ES 2.0 and 3.0 --- core/build.h | 4 ++++ core/rend/gles/gl32funcs.c | 3 ++- core/rend/gles/gldraw.cpp | 4 ++-- core/rend/gles/gles.cpp | 6 ++---- core/rend/gles/gles.h | 2 +- core/rend/gles/imgui_impl_opengl3.cpp | 8 ++++---- shell/android-studio/reicast/src/main/jni/Android.mk | 8 ++------ shell/linux/Makefile | 7 +++---- 8 files changed, 20 insertions(+), 22 deletions(-) diff --git a/core/build.h b/core/build.h index 57a00a46c..dc665872a 100755 --- a/core/build.h +++ b/core/build.h @@ -431,3 +431,7 @@ #define SH4_RAM_CLOCK (100*1000*1000) //[100000000] XTal(13.5) -> PLL (33.3) -> PLL 1:3 (100) , also suplied to HOLLY chip #define G2_BUS_CLOCK (25*1000*1000) //[25000000] from Holly, from SH4_RAM_CLOCK w/ 2 2:1 plls +#if defined(GLES) && !defined(GLES3) +// Only use GL ES 2.0 API functions +#define GLES2 +#endif diff --git a/core/rend/gles/gl32funcs.c b/core/rend/gles/gl32funcs.c index de06ae4ad..55625a428 100644 --- a/core/rend/gles/gl32funcs.c +++ b/core/rend/gles/gl32funcs.c @@ -2,10 +2,11 @@ #include #include #include "gl32funcs.h" +#include "build.h" void load_gles_symbols() { -#ifdef _ANDROID +#ifndef GLES2 for (int i = 0; rglgen_symbol_map[i].sym != NULL; i++) *(void **)rglgen_symbol_map[i].ptr = eglGetProcAddress(rglgen_symbol_map[i].sym); #endif diff --git a/core/rend/gles/gldraw.cpp b/core/rend/gles/gldraw.cpp index a8d3288c3..f25369414 100644 --- a/core/rend/gles/gldraw.cpp +++ b/core/rend/gles/gldraw.cpp @@ -948,7 +948,7 @@ void SetMVS_Mode(ModifierVolumeMode mv_mode, ISP_Modvol ispc) static void SetupMainVBO() { -#if !defined(GLES) || defined(_ANDROID) +#ifndef GLES2 if (gl.gl_major >= 3) glBindVertexArray(gl.vbo.vao); #endif @@ -971,7 +971,7 @@ static void SetupMainVBO() void SetupModvolVBO() { -#if !defined(GLES) || defined(_ANDROID) +#ifndef GLES2 if (gl.gl_major >= 3) glBindVertexArray(gl.vbo.vao); #endif diff --git a/core/rend/gles/gles.cpp b/core/rend/gles/gles.cpp index 487981db3..0f65d02b4 100644 --- a/core/rend/gles/gles.cpp +++ b/core/rend/gles/gles.cpp @@ -565,9 +565,7 @@ GLuint fogTextureId; } #ifdef GLES // EGL only supports runtime loading with android? TDB -#ifdef _ANDROID load_gles_symbols(); -#endif #else egl_makecurrent(); if (gl3wInit()) @@ -1205,7 +1203,7 @@ bool gl_create_resources() //create vao //This is really not "proper", vaos are supposed to be defined once //i keep updating the same one to make the es2 code work in 3.1 context -#if !defined(GLES) || defined(_ANDROID) +#ifndef GLES2 glGenVertexArrays(1, &gl.vbo.vao); #endif } @@ -2006,7 +2004,7 @@ struct glesrend : Renderer void DrawOSD(bool clear_screen) { -#if !defined(GLES) || defined(_ANDROID) +#ifndef GLES2 if (gl.gl_major >= 3) glBindVertexArray(gl.vbo.vao); #endif diff --git a/core/rend/gles/gles.h b/core/rend/gles/gles.h index 6c2b892b2..dec2f287d 100755 --- a/core/rend/gles/gles.h +++ b/core/rend/gles/gles.h @@ -17,7 +17,7 @@ #endif #include #include -#ifdef _ANDROID +#ifndef GLES2 #include "gl32funcs.h" #endif diff --git a/core/rend/gles/imgui_impl_opengl3.cpp b/core/rend/gles/imgui_impl_opengl3.cpp index 4026341f1..4d2936b10 100644 --- a/core/rend/gles/imgui_impl_opengl3.cpp +++ b/core/rend/gles/imgui_impl_opengl3.cpp @@ -141,7 +141,7 @@ void ImGui_ImplOpenGL3_RenderDrawData(ImDrawData* draw_data, bool save_backgr if (save_background) { -#if !defined(GLES) || defined(_ANDROID) +#ifndef GLES2 if (!gl.is_gles && glReadBuffer != NULL) glReadBuffer(GL_FRONT); @@ -190,12 +190,12 @@ void ImGui_ImplOpenGL3_RenderDrawData(ImDrawData* draw_data, bool save_backgr glcache.UseProgram(g_ShaderHandle); glUniform1i(g_AttribLocationTex, 0); glUniformMatrix4fv(g_AttribLocationProjMtx, 1, GL_FALSE, &ortho_projection[0][0]); -#if !defined(GLES) || defined(_ANDROID) +#ifndef GLES2 if (gl.gl_major >= 3 && glBindSampler != NULL) glBindSampler(0, 0); // We use combined texture/sampler state. Applications using GL 3.3 may set that otherwise. #endif GLuint vao_handle = 0; -#if !defined(GLES) || defined(_ANDROID) +#ifndef GLES2 if (gl.gl_major >= 3) { // Recreate the VAO every time @@ -252,7 +252,7 @@ void ImGui_ImplOpenGL3_RenderDrawData(ImDrawData* draw_data, bool save_backgr idx_buffer_offset += pcmd->ElemCount; } } -#if !defined(GLES) || defined(_ANDROID) +#ifndef GLES2 if (vao_handle != 0) glDeleteVertexArrays(1, &vao_handle); #endif diff --git a/shell/android-studio/reicast/src/main/jni/Android.mk b/shell/android-studio/reicast/src/main/jni/Android.mk index 0eb55d90a..3e0b41f8b 100644 --- a/shell/android-studio/reicast/src/main/jni/Android.mk +++ b/shell/android-studio/reicast/src/main/jni/Android.mk @@ -76,19 +76,15 @@ ifdef CHD5_FLAC endif ifdef NAOMI -LOCAL_CFLAGS += -DTARGET_NAOMI=1 -LOCAL_CPPFLAGS += -DTARGET_NAOMI=1 -LOCAL_CXXFLAGS += -DTARGET_NAOMI=1 + LOCAL_CFLAGS += -DTARGET_NAOMI=1 endif -# LOCAL_CFLAGS += -std=c++11 +LOCAL_CFLAGS += -DGLES3 LOCAL_CXXFLAGS += -std=c++11 -fopenmp LOCAL_LDFLAGS += -fopenmp ifeq ($(TARGET_ARCH_ABI),x86) LOCAL_CFLAGS+= -DTARGET_NO_AREC -DTARGET_NO_OPENMP - LOCAL_CXXFLAGS+= -DTARGET_NO_AREC -fpermissive -DTARGET_NO_OPENMP - LOCAL_CPPFLAGS+= -DTARGET_NO_AREC -DTARGET_NO_OPENMP endif LOCAL_CPP_FEATURES := diff --git a/shell/linux/Makefile b/shell/linux/Makefile index c3a32668a..355903c09 100644 --- a/shell/linux/Makefile +++ b/shell/linux/Makefile @@ -171,8 +171,7 @@ else ifneq (,$(findstring rpi,$(platform))) INCS += -I/opt/vc/include/ -I/opt/vc/include/interface/vmcs_host/linux -I/opt/vc/include/interface/vcos/pthreads LIBS += -L/opt/vc/lib/ -lbcm_host -ldl LIBS += -lbrcmEGL -lbrcmGLESv2 - CFLAGS += -D TARGET_VIDEOCORE - CXXFLAGS += -DGLES + CFLAGS += -D TARGET_VIDEOCORE -DGLES USE_OMX := 1 USE_DISPMANX := 1 endif @@ -208,7 +207,7 @@ else ifneq (,$(findstring odroid,$(platform))) ifneq (,$(findstring odroidxu3,$(platform))) MFLAGS += -march=armv7ve -mtune=cortex-a15.cortex-a7 ASFLAGS += -march=armv7ve - + CFLAGS += -DGLES3 # Other ODROIDs else MFLAGS += -march=armv7-a @@ -382,7 +381,7 @@ endif # GLES on x11? ifdef USE_GLES - CXXFLAGS += -DGLES + CFLAGS += -DGLES LIBS += -lEGL -lGLESv2 else ifdef FOR_LINUX LIBS += -ldl -lGL #for desktop gl From 6053a465f799a2fa4ec3680c05450704c4fada14 Mon Sep 17 00:00:00 2001 From: Flyinghead Date: Wed, 15 May 2019 13:07:52 +0200 Subject: [PATCH 291/319] fix win32 build --- core/rend/gles/gl32funcs.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/rend/gles/gl32funcs.c b/core/rend/gles/gl32funcs.c index 55625a428..05e54c41e 100644 --- a/core/rend/gles/gl32funcs.c +++ b/core/rend/gles/gl32funcs.c @@ -6,7 +6,7 @@ void load_gles_symbols() { -#ifndef GLES2 +#ifdef GLES3 for (int i = 0; rglgen_symbol_map[i].sym != NULL; i++) *(void **)rglgen_symbol_map[i].ptr = eglGetProcAddress(rglgen_symbol_map[i].sym); #endif From c92a997f11381e99beddede783337fc9d109b257 Mon Sep 17 00:00:00 2001 From: drhilarius Date: Thu, 16 May 2019 03:05:16 -0700 Subject: [PATCH 292/319] Fixes for cmake + msvc Also now builds .asm file for dynarec jit with msvc --- CMakeLists.txt | 2 ++ core/rend/gui.cpp | 2 +- shell/cmake/config.cmake | 4 ---- 3 files changed, 3 insertions(+), 5 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 80342e0d2..b4ccfb012 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -5,6 +5,7 @@ set(TNAME reicast) project(${TNAME}) enable_language(ASM) +enable_language(ASM_MASM) set(DEBUG_CMAKE ON) @@ -177,6 +178,7 @@ list(APPEND osd_SRCS ${d_aout}/audiostream.cpp) if (${HOST_OS} EQUAL ${OS_WINDOWS}) list(APPEND osd_SRCS ${d_core}/windows/winmain.cpp) + list(APPEND osd_SRCS ${d_core}/windows/win_vmem.cpp) list(APPEND osd_SRCS ${d_aout}/audiobackend_directsound.cpp) diff --git a/core/rend/gui.cpp b/core/rend/gui.cpp index b6f0b30aa..4feec179b 100644 --- a/core/rend/gui.cpp +++ b/core/rend/gui.cpp @@ -1112,7 +1112,7 @@ static void gui_display_settings() std::string cur = check ? "1" : "0"; (*cfg_entries)[options->cfg_name] = cur; } - else if (options->type == list) + else if (options->type == ::list) { if (ImGui::BeginCombo(options->caption.c_str(), value.c_str(), ImGuiComboFlags_None)) { diff --git a/shell/cmake/config.cmake b/shell/cmake/config.cmake index d1dfd59e4..da6b9fa44 100644 --- a/shell/cmake/config.cmake +++ b/shell/cmake/config.cmake @@ -270,10 +270,6 @@ endif() if ((${BUILD_COMPILER} EQUAL ${COMPILER_VC}) OR (${BUILD_COMPILER} EQUAL ${COMPILER_CLANG}) AND (${HOST_OS} STREQUAL ${OS_WINDOWS})) - if((${HOST_CPU} EQUAL ${CPU_X64}) AND (${FEAT_SHREC} EQUAL ${DYNAREC_JIT})) # AND NOT "${NINJA}" STREQUAL "") - set(FEAT_SHREC ${DYNAREC_CPP}) - message("---x64 rec disabled for VC x64 via NINJA") - endif() add_definitions(/D_CRT_SECURE_NO_WARNINGS /D_CRT_SECURE_CPP_OVERLOAD_STANDARD_NAMES=1) From b0b63d4894f11c654e404676388ccdf7f7ae68ca Mon Sep 17 00:00:00 2001 From: hissingshark Date: Thu, 16 May 2019 16:05:47 +0100 Subject: [PATCH 293/319] Fix typo induced build error --- core/sdl/sdl_gamepad.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/sdl/sdl_gamepad.h b/core/sdl/sdl_gamepad.h index 9f74f578c..edf23ae9d 100644 --- a/core/sdl/sdl_gamepad.h +++ b/core/sdl/sdl_gamepad.h @@ -182,7 +182,7 @@ public: SDLKbGamepadDevice(int maple_port) : GamepadDevice(maple_port, "SDL") { _name = "Keyboard"; - _unique_id = "sdl_keyboard; + _unique_id = "sdl_keyboard"; if (!find_mapping()) input_mapper = new KbInputMapping(); } From 69dc87d23d218e1dfd4732861e65863979987951 Mon Sep 17 00:00:00 2001 From: drhilarius Date: Fri, 17 May 2019 02:36:43 -0700 Subject: [PATCH 294/319] Add cmake msvc build to appveyor.yml --- appveyor.yml | 61 ++++++++++++++++++++++++++++++++++------------------ 1 file changed, 40 insertions(+), 21 deletions(-) diff --git a/appveyor.yml b/appveyor.yml index 51b133577..50c9db088 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -1,31 +1,50 @@ version: git-{branch}-{build} image: Visual Studio 2017 -environment: - matrix: - - EXTRA_PATH: C:\mingw-w64\x86_64-7.2.0-posix-seh-rt_v5-rev1\mingw64\bin\ - LAUNCH_BUILD: mingw32-make platform=win32 - LAUNCH_PATH: shell\linux\ +configuration: +- RelWithDebInfo +- fast +platform: +- x64 + +before_build: + - cmd: >- + if "%CONFIGURATION%"=="RelWithDebInfo" (set BUILD_PATH=build) + + if "%CONFIGURATION%"=="fast" (set BUILD_PATH=shell\linux) + + set EXTRA_PATH=C:\mingw-w64\x86_64-7.2.0-posix-seh-rt_v5-rev1\mingw64\bin\ + + set PATH=%EXTRA_PATH%;%PATH% + + if not exist %BUILD_PATH% (mkdir %BUILD_PATH%) + + if "%CONFIGURATION%"=="RelWithDebInfo" (cmake -B %BUILD_PATH% -G "Visual Studio 15 2017 Win64" -DCMAKE_CONFIGURATION_TYPES=RelWithDebInfo) + + cd %BUILD_PATH% + build_script: -- cmd: >- - set PATH=%EXTRA_PATH%;%PATH% - - cd %LAUNCH_PATH% - - %LAUNCH_BUILD% - + - cmd: >- + if "%CONFIGURATION%"=="RelWithDebInfo" (msbuild reicast.sln /logger:"C:\Program Files\AppVeyor\BuildAgent\Appveyor.MSBuildLogger.dll") + + if "%CONFIGURATION%"=="fast" (mingw32-make platform=win32) + after_build: - - cmd: cd ..\.. - - cmd: mkdir artifacts - - cmd: move shell\linux\nosym-reicast.exe artifacts/reicast-win_x64-fast-%APPVEYOR_REPO_COMMIT%.exe - - cmd: copy %EXTRA_PATH%\libgcc_s_seh-1.dll artifacts - - cmd: copy %EXTRA_PATH%\libwinpthread-1.dll artifacts - - cmd: copy %EXTRA_PATH%\libgomp-1.dll artifacts + - cmd: >- + if "%CONFIGURATION%"=="RelWithDebInfo" (cd .. && set EXE_PATH=build\RelWithDebInfo\reicast.exe) + + if "%CONFIGURATION%"=="fast" (cd ..\.. && set EXE_PATH=shell\linux\nosym-reicast.exe) + + mkdir artifacts + + move %EXE_PATH% artifacts\reicast-win_%PLATFORM%-%CONFIGURATION%-%APPVEYOR_REPO_COMMIT%.exe + + if "%CONFIGURATION%"=="fast" (copy %EXTRA_PATH%\libgcc_s_seh-1.dll artifacts && copy %EXTRA_PATH%\libwinpthread-1.dll artifacts && copy %EXTRA_PATH%\libgomp-1.dll artifacts) artifacts: - path: artifacts - name: reicast-win_x64-fast-$(APPVEYOR_REPO_COMMIT) + name: reicast-win_$(PLATFORM)-$(CONFIGURATION)-$(APPVEYOR_REPO_COMMIT) deploy: - provider: S3 @@ -35,5 +54,5 @@ deploy: region: eu-west-1 bucket: reicast-builds-windows folder: 'builds/heads/$(APPVEYOR_REPO_BRANCH)-$(APPVEYOR_REPO_COMMIT)' - artifact: reicast-win_x64-fast-$(APPVEYOR_REPO_COMMIT) - set_public: true + artifact: reicast-win_$(PLATFORM)-$(CONFIGURATION)-$(APPVEYOR_REPO_COMMIT) + set_public: true \ No newline at end of file From 07b3f8334318656fc7113af6adf1bd8deb562ad0 Mon Sep 17 00:00:00 2001 From: Flyinghead Date: Mon, 20 May 2019 19:12:28 +0200 Subject: [PATCH 295/319] audio: new option for automatic limitFPS LimitFPS now can be enabled, disabled or automatic. The latter option doesn't wait for audio if the emulator is running slow (over the last 2 secs) Renamed `Audio Backend` to `Audio Driver` in the UI. --- core/nullDC.cpp | 6 ++--- core/oslib/audiostream.cpp | 3 ++- core/rend/gui.cpp | 47 ++++++++++++++++++++++++-------------- core/types.h | 7 +++++- 4 files changed, 41 insertions(+), 22 deletions(-) diff --git a/core/nullDC.cpp b/core/nullDC.cpp index 1deeebad7..1937b8018 100755 --- a/core/nullDC.cpp +++ b/core/nullDC.cpp @@ -499,7 +499,7 @@ void InitSettings() settings.dreamcast.language = 6; // default settings.dreamcast.FullMMU = false; settings.dynarec.SmcCheckLevel = FullCheck; - settings.aica.LimitFPS = true; + settings.aica.LimitFPS = LimitFPSEnabled; settings.aica.NoBatch = false; // This also controls the DSP. Disabled by default settings.aica.NoSound = false; settings.audio.backend = "auto"; @@ -580,7 +580,7 @@ void LoadSettings(bool game_specific) settings.dreamcast.broadcast = cfgLoadInt(config_section, "Dreamcast.Broadcast", settings.dreamcast.broadcast); settings.dreamcast.language = cfgLoadInt(config_section, "Dreamcast.Language", settings.dreamcast.language); settings.dreamcast.FullMMU = cfgLoadBool(config_section, "Dreamcast.FullMMU", settings.dreamcast.FullMMU); - settings.aica.LimitFPS = cfgLoadBool(config_section, "aica.LimitFPS", settings.aica.LimitFPS); + settings.aica.LimitFPS = (LimitFPSEnum)cfgLoadInt(config_section, "aica.LimitFPS", (int)settings.aica.LimitFPS); settings.aica.NoBatch = cfgLoadBool(config_section, "aica.NoBatch", settings.aica.NoBatch); settings.aica.NoSound = cfgLoadBool(config_section, "aica.NoSound", settings.aica.NoSound); settings.audio.backend = cfgLoadStr(audio_section, "backend", settings.audio.backend.c_str()); @@ -717,7 +717,7 @@ void SaveSettings() cfgSaveInt("config", "Dynarec.SmcCheckLevel", (int)settings.dynarec.SmcCheckLevel); cfgSaveInt("config", "Dreamcast.Language", settings.dreamcast.language); - cfgSaveBool("config", "aica.LimitFPS", settings.aica.LimitFPS); + cfgSaveInt("config", "aica.LimitFPS", (int)settings.aica.LimitFPS); cfgSaveBool("config", "aica.NoBatch", settings.aica.NoBatch); cfgSaveBool("config", "aica.NoSound", settings.aica.NoSound); cfgSaveStr("audio", "backend", settings.audio.backend.c_str()); diff --git a/core/oslib/audiostream.cpp b/core/oslib/audiostream.cpp index 1d6f4f079..d0f1ebeb4 100644 --- a/core/oslib/audiostream.cpp +++ b/core/oslib/audiostream.cpp @@ -150,7 +150,8 @@ void WriteSample(s16 r, s16 l) if (WritePtr==(SAMPLE_COUNT-1)) { - bool do_wait = settings.aica.LimitFPS && (mspdf <= 11); + bool do_wait = settings.aica.LimitFPS == LimitFPSEnabled + || (settings.aica.LimitFPS == LimitFPSAuto && mspdf <= 11); PushAudio(RingBuffer,SAMPLE_COUNT, do_wait); } diff --git a/core/rend/gui.cpp b/core/rend/gui.cpp index 4feec179b..58872b54b 100644 --- a/core/rend/gui.cpp +++ b/core/rend/gui.cpp @@ -1029,9 +1029,28 @@ static void gui_display_settings() ImGui::Checkbox("Enable DSP", &settings.aica.NoBatch); ImGui::SameLine(); ShowHelpMarker("Enable the Dreamcast Digital Sound Processor. Only recommended on fast and arm64 platforms"); - ImGui::Checkbox("Limit FPS", &settings.aica.LimitFPS); - ImGui::SameLine(); - ShowHelpMarker("Use the sound output to limit the speed of the emulator. Recommended in most cases"); + const char *preview = settings.aica.LimitFPS == LimitFPSDisabled ? "Disabled" : settings.aica.LimitFPS == LimitFPSAuto ? "Automatic" : "Enabled"; + if (ImGui::BeginCombo("Limit Emulator Speed", preview, ImGuiComboFlags_None)) + { + bool is_selected = settings.aica.LimitFPS == LimitFPSDisabled; + if (ImGui::Selectable("Disabled", &is_selected)) + settings.aica.LimitFPS = LimitFPSDisabled; + if (is_selected) + ImGui::SetItemDefaultFocus(); + is_selected = settings.aica.LimitFPS == LimitFPSAuto; + if (ImGui::Selectable("Automatic", &is_selected)) + settings.aica.LimitFPS = LimitFPSAuto; + if (is_selected) + ImGui::SetItemDefaultFocus(); + is_selected = settings.aica.LimitFPS == LimitFPSEnabled; + if (ImGui::Selectable("Enabled", &is_selected)) + settings.aica.LimitFPS = LimitFPSEnabled; + if (is_selected) + ImGui::SetItemDefaultFocus(); + ImGui::EndCombo(); + } + ImGui::SameLine(); + ShowHelpMarker("Whether to limit the emulator speed using the audio output. Enabled recommended"); audiobackend_t* backend = NULL;; std::string backend_name = settings.audio.backend; @@ -1045,19 +1064,15 @@ static void gui_display_settings() SortAudioBackends(); audiobackend_t* current_backend = backend; - if (ImGui::BeginCombo("Audio Backend", backend_name.c_str(), ImGuiComboFlags_None)) + if (ImGui::BeginCombo("Audio Driver", backend_name.c_str(), ImGuiComboFlags_None)) { bool is_selected = (settings.audio.backend == "auto"); - if (ImGui::Selectable("auto", &is_selected)) + if (ImGui::Selectable("auto - Automatic driver selection", &is_selected)) settings.audio.backend = "auto"; - ImGui::SameLine(); ImGui::Text("-"); - ImGui::SameLine(); ImGui::Text("Autoselect audio backend"); is_selected = (settings.audio.backend == "none"); - if (ImGui::Selectable("none", &is_selected)) + if (ImGui::Selectable("none - No audio driver", &is_selected)) settings.audio.backend = "none"; - ImGui::SameLine(); ImGui::Text("-"); - ImGui::SameLine(); ImGui::Text("No audio backend"); for (int i = 0; i < GetAudioBackendCount(); i++) { @@ -1067,17 +1082,15 @@ static void gui_display_settings() if (is_selected) current_backend = backend; - if (ImGui::Selectable(backend->slug.c_str(), &is_selected)) + if (ImGui::Selectable((backend->slug + " - " + backend->name).c_str(), &is_selected)) settings.audio.backend = backend->slug; - ImGui::SameLine(); ImGui::Text("-"); - ImGui::SameLine(); ImGui::Text(backend->name.c_str()); - if (is_selected) - ImGui::SetItemDefaultFocus(); + if (is_selected) + ImGui::SetItemDefaultFocus(); } ImGui::EndCombo(); } - ImGui::SameLine(); - ShowHelpMarker("The audio backend to use"); + ImGui::SameLine(); + ShowHelpMarker("The audio driver to use"); if (current_backend != NULL && current_backend->get_options != NULL) { diff --git a/core/types.h b/core/types.h index 8b1a039a8..b8f2e67bf 100644 --- a/core/types.h +++ b/core/types.h @@ -607,6 +607,11 @@ enum SmcCheckEnum { FastCheck = 1, NoCheck = 2 }; +enum LimitFPSEnum { + LimitFPSDisabled = 0, + LimitFPSAuto = 1, + LimitFPSEnabled = 2 +}; struct settings_t { @@ -669,7 +674,7 @@ struct settings_t { u32 HW_mixing; //(0) -> SW , 1 -> HW , 2 -> Auto u32 BufferSize; //In samples ,*4 for bytes (1024) - bool LimitFPS; // defaults to true + LimitFPSEnum LimitFPS; u32 GlobalFocus; //0 -> only hwnd , (1) -> Global u32 BufferCount; //BufferCount+2 buffers used , max 60 , default 0 u32 CDDAMute; From 4cfcf2ea835b37f3268ecdb58fba6b483178ed08 Mon Sep 17 00:00:00 2001 From: Flyinghead Date: Mon, 20 May 2019 19:13:08 +0200 Subject: [PATCH 296/319] alsa: don't spam console if underrun --- core/oslib/audiobackend_alsa.cpp | 21 ++------------------- 1 file changed, 2 insertions(+), 19 deletions(-) diff --git a/core/oslib/audiobackend_alsa.cpp b/core/oslib/audiobackend_alsa.cpp index 47c4f348c..2ac08fe46 100644 --- a/core/oslib/audiobackend_alsa.cpp +++ b/core/oslib/audiobackend_alsa.cpp @@ -159,30 +159,13 @@ static u32 alsa_push(void* frame, u32 samples, bool wait) if (rc == -EPIPE) { /* EPIPE means underrun */ - fprintf(stderr, "ALSA: underrun occurred\n"); snd_pcm_prepare(handle); // Write some silence then our samples const size_t silence_size = period_size * 4; void *silence = alloca(silence_size * 4); memset(silence, 0, silence_size * 4); - rc = snd_pcm_writei(handle, silence, silence_size); - if (rc < 0) - fprintf(stderr, "ALSA: error from writei(silence): %s\n", snd_strerror(rc)); - else if (rc < silence_size) - fprintf(stderr, "ALSA: short write from writei(silence): %d/%ld frames\n", rc, silence_size); - rc = snd_pcm_writei(handle, frame, samples); - if (rc < 0) - fprintf(stderr, "ALSA: error from writei(again): %s\n", snd_strerror(rc)); - else if (rc < samples) - fprintf(stderr, "ALSA: short write from writei(again): %d/%d frames\n", rc, samples); - } - else if (rc < 0) - { - fprintf(stderr, "ALSA: error from writei: %s\n", snd_strerror(rc)); - } - else if (rc != samples) - { - fprintf(stderr, "ALSA: short write, wrote %d frames of %d\n", rc, samples); + snd_pcm_writei(handle, silence, silence_size); + snd_pcm_writei(handle, frame, samples); } return 1; } From 433590b610c4775032f7686bc40e6a5ea21ef56e Mon Sep 17 00:00:00 2001 From: Flyinghead Date: Fri, 17 May 2019 19:48:25 +0200 Subject: [PATCH 297/319] rend: Add per-strip TR sort option --- core/hw/pvr/ta.h | 2 -- core/nullDC.cpp | 3 +++ core/rend/gles/gldraw.cpp | 21 +++++++++++---------- core/rend/gui.cpp | 37 +++++++++++++++++++++++++++++-------- core/types.h | 1 + 5 files changed, 44 insertions(+), 20 deletions(-) diff --git a/core/hw/pvr/ta.h b/core/hw/pvr/ta.h index eb8505bf6..e4e649090 100644 --- a/core/hw/pvr/ta.h +++ b/core/hw/pvr/ta.h @@ -20,5 +20,3 @@ void DYNACALL ta_vtx_data32(void* data); void ta_vtx_data(u32* data, u32 size); bool ta_parse_vdrc(TA_context* ctx); - -#define TRIG_SORT 1 diff --git a/core/nullDC.cpp b/core/nullDC.cpp index 1deeebad7..c31e6d5c4 100755 --- a/core/nullDC.cpp +++ b/core/nullDC.cpp @@ -521,6 +521,7 @@ void InitSettings() settings.rend.Fog = true; settings.rend.FloatVMUs = false; settings.rend.Rotate90 = false; + settings.rend.PerStripSorting = false; settings.pvr.ta_skip = 0; settings.pvr.rend = 0; @@ -609,6 +610,7 @@ void LoadSettings(bool game_specific) settings.rend.Fog = cfgLoadBool(config_section, "rend.Fog", settings.rend.Fog); settings.rend.FloatVMUs = cfgLoadBool(config_section, "rend.FloatVMUs", settings.rend.FloatVMUs); settings.rend.Rotate90 = cfgLoadBool(config_section, "rend.Rotate90", settings.rend.Rotate90); + settings.rend.PerStripSorting = cfgLoadBool(config_section, "rend.PerStripSorting", settings.rend.PerStripSorting); settings.pvr.ta_skip = cfgLoadInt(config_section, "ta.skip", settings.pvr.ta_skip); settings.pvr.rend = cfgLoadInt(config_section, "pvr.rend", settings.pvr.rend); @@ -759,6 +761,7 @@ void SaveSettings() cfgSaveBool("config", "rend.Rotate90", settings.rend.Rotate90); cfgSaveInt("config", "ta.skip", settings.pvr.ta_skip); cfgSaveInt("config", "pvr.rend", settings.pvr.rend); + cfgSaveBool("config", "rend.PerStripSorting", settings.rend.PerStripSorting); cfgSaveInt("config", "pvr.MaxThreads", settings.pvr.MaxThreads); cfgSaveBool("config", "pvr.SynchronousRendering", settings.pvr.SynchronousRender); diff --git a/core/rend/gles/gldraw.cpp b/core/rend/gles/gldraw.cpp index f25369414..8c23dd58b 100644 --- a/core/rend/gles/gldraw.cpp +++ b/core/rend/gles/gldraw.cpp @@ -260,11 +260,9 @@ __forceinline glcache.DepthFunc(Zfunction[gp->isp.DepthMode]); } -#if TRIG_SORT - if (SortingEnabled) + if (SortingEnabled && !settings.rend.PerStripSorting) glcache.DepthMask(GL_FALSE); else -#endif glcache.DepthMask(!gp->isp.ZWriteDis); } @@ -1105,13 +1103,16 @@ void DrawStrips() { if (current_pass.autosort) { -#if TRIG_SORT - GenSorted(previous_pass.tr_count, current_pass.tr_count - previous_pass.tr_count); - DrawSorted(render_pass < pvrrc.render_passes.used() - 1); -#else - SortPParams(previous_pass.tr_count, current_pass.tr_count - previous_pass.tr_count); - DrawList(pvrrc.global_param_tr, previous_pass.tr_count, current_pass.tr_count - previous_pass.tr_count); -#endif + if (!settings.rend.PerStripSorting) + { + GenSorted(previous_pass.tr_count, current_pass.tr_count - previous_pass.tr_count); + DrawSorted(render_pass < pvrrc.render_passes.used() - 1); + } + else + { + SortPParams(previous_pass.tr_count, current_pass.tr_count - previous_pass.tr_count); + DrawList(pvrrc.global_param_tr, previous_pass.tr_count, current_pass.tr_count - previous_pass.tr_count); + } } else DrawList(pvrrc.global_param_tr, previous_pass.tr_count, current_pass.tr_count - previous_pass.tr_count); diff --git a/core/rend/gui.cpp b/core/rend/gui.cpp index 4feec179b..d3cfddad8 100644 --- a/core/rend/gui.cpp +++ b/core/rend/gui.cpp @@ -942,20 +942,41 @@ static void gui_display_settings() if (ImGui::BeginTabItem("Video")) { ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, normal_padding); -#if !defined(GLES) && HOST_OS != OS_DARWIN - if (!gl.is_gles && gl.gl_major >= 4 && ImGui::CollapsingHeader("Transparent Sorting", ImGuiTreeNodeFlags_DefaultOpen)) + int renderer = settings.pvr.rend == 3 ? 2 : settings.rend.PerStripSorting ? 1 : 0; + bool has_per_pixel = !gl.is_gles && gl.gl_major >= 4; + if (ImGui::CollapsingHeader("Transparent Sorting", ImGuiTreeNodeFlags_DefaultOpen)) { - ImGui::Columns(2, "renderers", false); - ImGui::RadioButton("Per Triangle", (int *)&settings.pvr.rend, 0); + ImGui::Columns(has_per_pixel ? 3 : 2, "renderers", false); + ImGui::RadioButton("Per Triangle", &renderer, 0); ImGui::SameLine(); ShowHelpMarker("Sort transparent polygons per triangle. Fast but may produce graphical glitches"); - ImGui::NextColumn(); - ImGui::RadioButton("Per Pixel", (int *)&settings.pvr.rend, 3); + ImGui::NextColumn(); + ImGui::RadioButton("Per Strip", &renderer, 1); ImGui::SameLine(); - ShowHelpMarker("Sort transparent polygons per pixel. Slower but accurate"); + ShowHelpMarker("Sort transparent polygons per strip. Faster but may produce graphical glitches"); + if (has_per_pixel) + { + ImGui::NextColumn(); + ImGui::RadioButton("Per Pixel", &renderer, 2); + ImGui::SameLine(); + ShowHelpMarker("Sort transparent polygons per pixel. Slower but accurate"); + } ImGui::Columns(1, NULL, false); + switch (renderer) + { + case 0: + settings.pvr.rend = 0; + settings.rend.PerStripSorting = false; + break; + case 1: + settings.pvr.rend = 0; + settings.rend.PerStripSorting = true; + break; + case 2: + settings.pvr.rend = 3; + break; + } } -#endif if (ImGui::CollapsingHeader("Rendering Options", ImGuiTreeNodeFlags_DefaultOpen)) { ImGui::Checkbox("Synchronous Rendering", &settings.pvr.SynchronousRender); diff --git a/core/types.h b/core/types.h index 8b1a039a8..e73863133 100644 --- a/core/types.h +++ b/core/types.h @@ -638,6 +638,7 @@ struct settings_t bool Fog; bool FloatVMUs; bool Rotate90; // Rotate the screen 90 deg CC + bool PerStripSorting; } rend; struct From 74844c8940e529715ed21bf4c2e834c1e8464dfb Mon Sep 17 00:00:00 2001 From: Flyinghead Date: Fri, 17 May 2019 20:05:01 +0200 Subject: [PATCH 298/319] disable per-pixel renderer on OSX --- core/rend/gui.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/core/rend/gui.cpp b/core/rend/gui.cpp index d3cfddad8..a2fc9eccf 100644 --- a/core/rend/gui.cpp +++ b/core/rend/gui.cpp @@ -943,7 +943,11 @@ static void gui_display_settings() { ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, normal_padding); int renderer = settings.pvr.rend == 3 ? 2 : settings.rend.PerStripSorting ? 1 : 0; +#if HOST_OS != OS_DARWIN bool has_per_pixel = !gl.is_gles && gl.gl_major >= 4; +#else + bool has_per_pixel = false; +#endif if (ImGui::CollapsingHeader("Transparent Sorting", ImGuiTreeNodeFlags_DefaultOpen)) { ImGui::Columns(has_per_pixel ? 3 : 2, "renderers", false); From 05f461c3c5c61853f43d837fa36513d878165595 Mon Sep 17 00:00:00 2001 From: Flyinghead Date: Sun, 19 May 2019 19:45:05 +0200 Subject: [PATCH 299/319] Use FB_R_SOF2 only to detect direct FB writes Fixes flashing video in Virtua Fighter 3tb --- core/hw/pvr/Renderer_if.cpp | 12 ++++-------- core/hw/pvr/Renderer_if.h | 6 ++---- core/hw/pvr/pvr_mem.cpp | 8 ++------ 3 files changed, 8 insertions(+), 18 deletions(-) diff --git a/core/hw/pvr/Renderer_if.cpp b/core/hw/pvr/Renderer_if.cpp index 71c954e34..199d8ae5c 100644 --- a/core/hw/pvr/Renderer_if.cpp +++ b/core/hw/pvr/Renderer_if.cpp @@ -88,10 +88,8 @@ cResetEvent rs, re; int max_idx,max_mvo,max_op,max_pt,max_tr,max_vtx,max_modt, ovrn; static bool render_called = false; -u32 fb1_watch_addr_start; -u32 fb1_watch_addr_end; -u32 fb2_watch_addr_start; -u32 fb2_watch_addr_end; +u32 fb_watch_addr_start; +u32 fb_watch_addr_end; bool fb_dirty; TA_context* _pvrrc; @@ -558,10 +556,8 @@ void rend_vblank() void check_framebuffer_write() { u32 fb_size = (FB_R_SIZE.fb_y_size + 1) * (FB_R_SIZE.fb_x_size + FB_R_SIZE.fb_modulus) * 4; - fb1_watch_addr_start = FB_R_SOF1 & VRAM_MASK; - fb1_watch_addr_end = fb1_watch_addr_start + fb_size; - fb2_watch_addr_start = FB_R_SOF2 & VRAM_MASK; - fb2_watch_addr_end = fb2_watch_addr_start + fb_size; + fb_watch_addr_start = FB_R_SOF2 & VRAM_MASK; + fb_watch_addr_end = fb_watch_addr_start + fb_size; } void rend_cancel_emu_wait() diff --git a/core/hw/pvr/Renderer_if.h b/core/hw/pvr/Renderer_if.h index 8bd991835..af7f63019 100644 --- a/core/hw/pvr/Renderer_if.h +++ b/core/hw/pvr/Renderer_if.h @@ -61,10 +61,8 @@ Renderer* rend_GL4(); Renderer* rend_norend(); Renderer* rend_softrend(); -extern u32 fb1_watch_addr_start; -extern u32 fb1_watch_addr_end; -extern u32 fb2_watch_addr_start; -extern u32 fb2_watch_addr_end; +extern u32 fb_watch_addr_start; +extern u32 fb_watch_addr_end; extern bool fb_dirty; void check_framebuffer_write(); diff --git a/core/hw/pvr/pvr_mem.cpp b/core/hw/pvr/pvr_mem.cpp index c9af98a90..9378590a9 100644 --- a/core/hw/pvr/pvr_mem.cpp +++ b/core/hw/pvr/pvr_mem.cpp @@ -233,9 +233,7 @@ void DYNACALL pvr_write_area1_8(u32 addr,u8 data) void DYNACALL pvr_write_area1_16(u32 addr,u16 data) { u32 vaddr = addr & VRAM_MASK; - if (!fb_dirty - && ((vaddr >= fb1_watch_addr_start && vaddr < fb1_watch_addr_end) - || (vaddr >= fb2_watch_addr_start && vaddr < fb2_watch_addr_end))) + if (!fb_dirty && vaddr >= fb_watch_addr_start && vaddr < fb_watch_addr_end) { fb_dirty = true; } @@ -244,9 +242,7 @@ void DYNACALL pvr_write_area1_16(u32 addr,u16 data) void DYNACALL pvr_write_area1_32(u32 addr,u32 data) { u32 vaddr = addr & VRAM_MASK; - if (!fb_dirty - && ((vaddr >= fb1_watch_addr_start && vaddr < fb1_watch_addr_end) - || (vaddr >= fb2_watch_addr_start && vaddr < fb2_watch_addr_end))) + if (!fb_dirty && vaddr >= fb_watch_addr_start && vaddr < fb_watch_addr_end) { fb_dirty = true; } From 25a7fede2e54ea63975ec1e167cdf06598bb71f7 Mon Sep 17 00:00:00 2001 From: Flyinghead Date: Sun, 19 May 2019 21:30:29 +0200 Subject: [PATCH 300/319] save states: don't serialize gamepad state --- core/hw/arm7/arm7.cpp | 6 ---- core/serialize.cpp | 77 ++++++++++++++++++------------------------- 2 files changed, 32 insertions(+), 51 deletions(-) diff --git a/core/hw/arm7/arm7.cpp b/core/hw/arm7/arm7.cpp index 9906f96de..1d980c77e 100644 --- a/core/hw/arm7/arm7.cpp +++ b/core/hw/arm7/arm7.cpp @@ -69,12 +69,6 @@ bool Arm7Enabled=false; u8 cpuBitsSet[256]; -bool intState = false; -bool stopState = false; -bool holdState = false; - - - void CPUSwitchMode(int mode, bool saveState, bool breakLoop=true); extern "C" void CPUFiq(); void CPUUpdateCPSR(); diff --git a/core/serialize.cpp b/core/serialize.cpp index 8b7e29900..e29f6700b 100644 --- a/core/serialize.cpp +++ b/core/serialize.cpp @@ -54,9 +54,6 @@ extern bool armFiqEnable; extern int armMode; extern bool Arm7Enabled; extern u8 cpuBitsSet[256]; -extern bool intState ; -extern bool stopState ; -extern bool holdState ; /* if AREC dynarec enabled: vector ops; @@ -257,15 +254,6 @@ extern u32 FrameCount; //extern cThread rthd; extern bool pend_rend; -//these will all get cleared out after a few frames - no need to serialize -//static bool render_called = false; -//u32 fb1_watch_addr_start; -//u32 fb1_watch_addr_end; -//u32 fb2_watch_addr_start; -//u32 fb2_watch_addr_end; -//bool fb_dirty; - - //maybe //extern u32 memops_t,memops_l; @@ -722,18 +710,6 @@ extern u32 fallback_blocks; extern u32 total_blocks; extern u32 REMOVED_OPS; - - - -//./core/linux-dist/main.cpp, ./core/windows/winmain.cpp , ... -extern u16 kcode[4]; -extern u8 rt[4]; -extern u8 lt[4]; -extern u32 vks[4]; -extern s8 joyx[4]; -extern s8 joyy[4]; - - bool rc_serialize(void *src, unsigned int src_size, void **dest, unsigned int *total_size) { if ( *dest != NULL ) @@ -812,9 +788,10 @@ bool dc_serialize(void **data, unsigned int *total_size) REICAST_S(armMode); REICAST_S(Arm7Enabled); REICAST_SA(cpuBitsSet,256); - REICAST_S(intState); - REICAST_S(stopState); - REICAST_S(holdState); + bool dummy; + REICAST_S(dummy); + REICAST_S(dummy); + REICAST_S(dummy); REICAST_S(dsp); @@ -1132,12 +1109,16 @@ bool dc_serialize(void **data, unsigned int *total_size) REICAST_S(total_blocks); REICAST_S(REMOVED_OPS); - REICAST_SA(kcode,4); - REICAST_SA(rt,4); - REICAST_SA(lt,4); - REICAST_SA(vks,4); - REICAST_SA(joyx,4); - REICAST_SA(joyy,4); + REICAST_S(i); //REICAST_SA(kcode,4); + REICAST_S(i); + REICAST_S(i); //REICAST_SA(rt,4); + REICAST_S(i); //REICAST_SA(lt,4); + REICAST_S(i); //REICAST_SA(vks,4); + REICAST_S(i); + REICAST_S(i); + REICAST_S(i); + REICAST_S(i); //REICAST_SA(joyx,4); + REICAST_S(i); //REICAST_SA(joyy,4); REICAST_S(settings.dreamcast.broadcast); REICAST_S(settings.dreamcast.cable); @@ -1166,9 +1147,10 @@ static bool dc_unserialize_libretro(void **data, unsigned int *total_size) REICAST_US(armMode); REICAST_US(Arm7Enabled); REICAST_USA(cpuBitsSet,256); - REICAST_US(intState); - REICAST_US(stopState); - REICAST_US(holdState); + bool dummy; + REICAST_US(dummy); + REICAST_US(dummy); + REICAST_US(dummy); REICAST_US(dsp); @@ -1562,9 +1544,10 @@ bool dc_unserialize(void **data, unsigned int *total_size) REICAST_US(armMode); REICAST_US(Arm7Enabled); REICAST_USA(cpuBitsSet,256); - REICAST_US(intState); - REICAST_US(stopState); - REICAST_US(holdState); + bool dummy; + REICAST_US(dummy); + REICAST_US(dummy); + REICAST_US(dummy); REICAST_US(dsp); @@ -1901,12 +1884,16 @@ bool dc_unserialize(void **data, unsigned int *total_size) REICAST_US(total_blocks); REICAST_US(REMOVED_OPS); - REICAST_USA(kcode,4); - REICAST_USA(rt,4); - REICAST_USA(lt,4); - REICAST_USA(vks,4); - REICAST_USA(joyx,4); - REICAST_USA(joyy,4); + REICAST_US(i); //REICAST_USA(kcode,4); + REICAST_US(i); + REICAST_US(i); //REICAST_USA(rt,4); + REICAST_US(i); //REICAST_USA(lt,4); + REICAST_US(i); //REICAST_USA(vks,4); + REICAST_US(i); + REICAST_US(i); + REICAST_US(i); + REICAST_US(i); //REICAST_USA(joyx,4); + REICAST_US(i); //REICAST_USA(joyy,4); REICAST_US(settings.dreamcast.broadcast); From a5ae8eeef0140e22256ad8c1a401f792f434b9e6 Mon Sep 17 00:00:00 2001 From: Flyinghead Date: Sun, 19 May 2019 21:34:25 +0200 Subject: [PATCH 301/319] Reset flashrom on reset --- core/hw/aica/aica_if.cpp | 2 ++ core/hw/flashrom/flashrom.h | 13 +++---------- core/hw/holly/sb_mem.cpp | 6 +++++- 3 files changed, 10 insertions(+), 11 deletions(-) diff --git a/core/hw/aica/aica_if.cpp b/core/hw/aica/aica_if.cpp index d562a0de1..c1799793b 100644 --- a/core/hw/aica/aica_if.cpp +++ b/core/hw/aica/aica_if.cpp @@ -160,6 +160,8 @@ void aica_Init() void aica_Reset(bool Manual) { aica_Init(); + VREG = 0; + ARMRST = 0; } void aica_Term() diff --git a/core/hw/flashrom/flashrom.h b/core/hw/flashrom/flashrom.h index d3282e003..8e0e3e665 100644 --- a/core/hw/flashrom/flashrom.h +++ b/core/hw/flashrom/flashrom.h @@ -21,7 +21,7 @@ struct MemChip this->mask=size-1;//must be power of 2 this->write_protect_size = write_protect_size; } - ~MemChip() { delete[] data; } + virtual ~MemChip() { delete[] data; } virtual u8 Read8(u32 addr) { @@ -118,14 +118,11 @@ struct MemChip printf("Saved %s as %s\n\n",path,title.c_str()); } + virtual void Reset() {} }; struct RomChip : MemChip { RomChip(u32 sz, u32 write_protect_size = 0) : MemChip(sz, write_protect_size) {} - void Reset() - { - //nothing, its permanent read only ;p - } void Write(u32 addr,u32 data,u32 sz) { die("Write to RomChip is not possible, address=%x, data=%x, size=%d"); @@ -135,10 +132,6 @@ struct SRamChip : MemChip { SRamChip(u32 sz, u32 write_protect_size = 0) : MemChip(sz, write_protect_size) {} - void Reset() - { - //nothing, its battery backed up storage - } void Write(u32 addr,u32 val,u32 sz) { addr&=mask; @@ -234,7 +227,7 @@ struct DCFlashChip : MemChip }; FlashState state; - void Reset() + virtual void Reset() override { //reset the flash chip state state = FS_Normal; diff --git a/core/hw/holly/sb_mem.cpp b/core/hw/holly/sb_mem.cpp index c60e4a7ba..b86bf3603 100644 --- a/core/hw/holly/sb_mem.cpp +++ b/core/hw/holly/sb_mem.cpp @@ -41,7 +41,7 @@ bool LoadRomFiles(const string& root) { #if DC_PLATFORM == DC_PLATFORM_DREAMCAST // Dreamcast absolutely needs a BIOS - msgboxf("Unable to find bios in \n%s\nExiting...", MBX_ICONERROR, root.c_str()); + msgboxf("Unable to find bios in %s. Exiting...", MBX_ICONERROR, root.c_str()); return false; #endif } @@ -348,6 +348,10 @@ void sh4_area0_Init() void sh4_area0_Reset(bool Manual) { sb_Reset(Manual); + sys_rom.Reset(); +#if defined(FLASH_SIZE) || defined(BBSRAM_SIZE) + sys_nvmem.Reset(); +#endif } void sh4_area0_Term() From 121e6b200bc5c5bd4dd9b8c20bc19eab1e801c59 Mon Sep 17 00:00:00 2001 From: Flyinghead Date: Sun, 19 May 2019 21:36:08 +0200 Subject: [PATCH 302/319] Reset gdrom params when changing disk --- core/hw/gdrom/gdromv3.cpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/core/hw/gdrom/gdromv3.cpp b/core/hw/gdrom/gdromv3.cpp index 0d8f82e38..239708b7b 100644 --- a/core/hw/gdrom/gdromv3.cpp +++ b/core/hw/gdrom/gdromv3.cpp @@ -322,6 +322,13 @@ u32 GetFAD(u8* data, bool msf) void libCore_gdrom_disc_change() { gd_setdisc(); + read_params = { 0 }; + set_mode_offset = 0; + packet_cmd = { 0 }; + read_buff = { 0 }; + pio_buff = { gds_waitcmd, 0 }; + ata_cmd = { 0 }; + cdda = { 0 }; } //This handles the work of setting up the pio regs/state :) From 74e7a954843803652b4b4f989718caf5eeec3a2e Mon Sep 17 00:00:00 2001 From: Flyinghead Date: Mon, 20 May 2019 19:26:19 +0200 Subject: [PATCH 303/319] simplify test --- core/hw/pvr/pvr_mem.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/core/hw/pvr/pvr_mem.cpp b/core/hw/pvr/pvr_mem.cpp index 9378590a9..45fb1a721 100644 --- a/core/hw/pvr/pvr_mem.cpp +++ b/core/hw/pvr/pvr_mem.cpp @@ -233,7 +233,7 @@ void DYNACALL pvr_write_area1_8(u32 addr,u8 data) void DYNACALL pvr_write_area1_16(u32 addr,u16 data) { u32 vaddr = addr & VRAM_MASK; - if (!fb_dirty && vaddr >= fb_watch_addr_start && vaddr < fb_watch_addr_end) + if (vaddr >= fb_watch_addr_start && vaddr < fb_watch_addr_end) { fb_dirty = true; } @@ -242,7 +242,7 @@ void DYNACALL pvr_write_area1_16(u32 addr,u16 data) void DYNACALL pvr_write_area1_32(u32 addr,u32 data) { u32 vaddr = addr & VRAM_MASK; - if (!fb_dirty && vaddr >= fb_watch_addr_start && vaddr < fb_watch_addr_end) + if (vaddr >= fb_watch_addr_start && vaddr < fb_watch_addr_end) { fb_dirty = true; } From a17b8355e5c3157a9583c250322dc7357b2eb196 Mon Sep 17 00:00:00 2001 From: Flyinghead Date: Tue, 21 May 2019 11:34:51 +0200 Subject: [PATCH 304/319] gdrom: use memset to reset read_buff --- core/hw/gdrom/gdromv3.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/hw/gdrom/gdromv3.cpp b/core/hw/gdrom/gdromv3.cpp index 239708b7b..e714f9c22 100644 --- a/core/hw/gdrom/gdromv3.cpp +++ b/core/hw/gdrom/gdromv3.cpp @@ -325,7 +325,7 @@ void libCore_gdrom_disc_change() read_params = { 0 }; set_mode_offset = 0; packet_cmd = { 0 }; - read_buff = { 0 }; + memset(&read_buff, 0, sizeof(read_buff)); pio_buff = { gds_waitcmd, 0 }; ata_cmd = { 0 }; cdda = { 0 }; From c0d71c7bcf50f9d874e569b8a69fba8e097ec482 Mon Sep 17 00:00:00 2001 From: Flyinghead Date: Tue, 21 May 2019 12:14:05 +0200 Subject: [PATCH 305/319] OSX: fix project file and schemes --- .../reicast-osx.xcodeproj/project.pbxproj | 54 ++++------ .../xcschemes/reicast-ios.xcscheme | 102 ------------------ .../xcschemes/reicast-osx.xcscheme | 14 --- 3 files changed, 20 insertions(+), 150 deletions(-) delete mode 100644 shell/apple/reicast.xcworkspace/xcshareddata/xcschemes/reicast-ios.xcscheme diff --git a/shell/apple/emulator-osx/reicast-osx.xcodeproj/project.pbxproj b/shell/apple/emulator-osx/reicast-osx.xcodeproj/project.pbxproj index 835041b08..c52e44cab 100644 --- a/shell/apple/emulator-osx/reicast-osx.xcodeproj/project.pbxproj +++ b/shell/apple/emulator-osx/reicast-osx.xcodeproj/project.pbxproj @@ -193,9 +193,6 @@ AE2A2D5C21D68470004B308D /* awcartridge.cpp in Sources */ = {isa = PBXBuildFile; fileRef = AE2A2D5321D6846F004B308D /* awcartridge.cpp */; }; AE2A2D5D21D68470004B308D /* m4cartridge.cpp in Sources */ = {isa = PBXBuildFile; fileRef = AE2A2D5621D68470004B308D /* m4cartridge.cpp */; }; AE2A2D5E21D68470004B308D /* decrypt.cpp in Sources */ = {isa = PBXBuildFile; fileRef = AE2A2D5921D68470004B308D /* decrypt.cpp */; }; - AE2A2D6621D684DB004B308D /* 7zArchive.cpp in Sources */ = {isa = PBXBuildFile; fileRef = AE2A2D6121D684DB004B308D /* 7zArchive.cpp */; }; - AE2A2D6721D684DB004B308D /* archive.cpp in Sources */ = {isa = PBXBuildFile; fileRef = AE2A2D6221D684DB004B308D /* archive.cpp */; }; - AE2A2D6821D684DB004B308D /* ZipArchive.cpp in Sources */ = {isa = PBXBuildFile; fileRef = AE2A2D6321D684DB004B308D /* ZipArchive.cpp */; }; AE2A2D7821D6851E004B308D /* 7zCrc.c in Sources */ = {isa = PBXBuildFile; fileRef = AE2A2D6A21D6851C004B308D /* 7zCrc.c */; }; AE2A2D7921D6851E004B308D /* 7zStream.c in Sources */ = {isa = PBXBuildFile; fileRef = AE2A2D6B21D6851D004B308D /* 7zStream.c */; }; AE2A2D7A21D6851E004B308D /* 7zArcIn.c in Sources */ = {isa = PBXBuildFile; fileRef = AE2A2D6C21D6851D004B308D /* 7zArcIn.c */; }; @@ -206,7 +203,6 @@ AE2A2D7F21D6851E004B308D /* 7zFile.c in Sources */ = {isa = PBXBuildFile; fileRef = AE2A2D7321D6851D004B308D /* 7zFile.c */; }; AE2A2D8021D6851E004B308D /* 7zDec.c in Sources */ = {isa = PBXBuildFile; fileRef = AE2A2D7721D6851E004B308D /* 7zDec.c */; }; AE2A2D8321D7DB78004B308D /* CustomTexture.cpp in Sources */ = {isa = PBXBuildFile; fileRef = AE2A2D8121D7DB77004B308D /* CustomTexture.cpp */; }; - AE60BDA7222B802A00FA8A5B /* version.cpp in Sources */ = {isa = PBXBuildFile; fileRef = AE60BDA6222B802A00FA8A5B /* version.cpp */; }; AE649BB62188689000EF4A81 /* xxhash.c in Sources */ = {isa = PBXBuildFile; fileRef = AE649BB42188689000EF4A81 /* xxhash.c */; }; AE649BF3218C552500EF4A81 /* bitmath.c in Sources */ = {isa = PBXBuildFile; fileRef = AE649BCD218C552500EF4A81 /* bitmath.c */; }; AE649BF4218C552500EF4A81 /* bitreader.c in Sources */ = {isa = PBXBuildFile; fileRef = AE649BCE218C552500EF4A81 /* bitreader.c */; }; @@ -267,6 +263,9 @@ AEE6279422247C0A00EC7E89 /* gui_util.cpp in Sources */ = {isa = PBXBuildFile; fileRef = AEE6279222247C0A00EC7E89 /* gui_util.cpp */; }; AEE6279622247C2B00EC7E89 /* keyboard_device.cpp in Sources */ = {isa = PBXBuildFile; fileRef = AEE6279522247C2B00EC7E89 /* keyboard_device.cpp */; }; AEF2564822886A2E00348550 /* posix_vmem.cpp in Sources */ = {isa = PBXBuildFile; fileRef = AEF2564722886A2E00348550 /* posix_vmem.cpp */; }; + AEF256502294060400348550 /* ZipArchive.cpp in Sources */ = {isa = PBXBuildFile; fileRef = AEF2564A2294060300348550 /* ZipArchive.cpp */; }; + AEF256512294060400348550 /* 7zArchive.cpp in Sources */ = {isa = PBXBuildFile; fileRef = AEF2564C2294060300348550 /* 7zArchive.cpp */; }; + AEF256522294060400348550 /* archive.cpp in Sources */ = {isa = PBXBuildFile; fileRef = AEF2564E2294060300348550 /* archive.cpp */; }; AEFF7ECC214AEC810068CE11 /* modem.cpp in Sources */ = {isa = PBXBuildFile; fileRef = AEFF7EC7214AEC800068CE11 /* modem.cpp */; }; AEFF7F4D214D9D590068CE11 /* pico_arp.c in Sources */ = {isa = PBXBuildFile; fileRef = AEFF7EFA214D9D590068CE11 /* pico_arp.c */; }; AEFF7F4E214D9D590068CE11 /* pico_dev_ppp.c in Sources */ = {isa = PBXBuildFile; fileRef = AEFF7EFE214D9D590068CE11 /* pico_dev_ppp.c */; }; @@ -649,12 +648,6 @@ AE2A2D5721D68470004B308D /* decrypt.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = decrypt.h; sourceTree = ""; }; AE2A2D5821D68470004B308D /* naomi_roms_input.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = naomi_roms_input.h; sourceTree = ""; }; AE2A2D5921D68470004B308D /* decrypt.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = decrypt.cpp; sourceTree = ""; }; - AE2A2D6021D684DB004B308D /* archive.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = archive.h; path = ../../../../core/archive/archive.h; sourceTree = ""; }; - AE2A2D6121D684DB004B308D /* 7zArchive.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = 7zArchive.cpp; path = ../../../../core/archive/7zArchive.cpp; sourceTree = ""; }; - AE2A2D6221D684DB004B308D /* archive.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = archive.cpp; path = ../../../../core/archive/archive.cpp; sourceTree = ""; }; - AE2A2D6321D684DB004B308D /* ZipArchive.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = ZipArchive.cpp; path = ../../../../core/archive/ZipArchive.cpp; sourceTree = ""; }; - AE2A2D6421D684DB004B308D /* ZipArchive.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ZipArchive.h; path = ../../../../core/archive/ZipArchive.h; sourceTree = ""; }; - AE2A2D6521D684DB004B308D /* 7zArchive.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = 7zArchive.h; path = ../../../../core/archive/7zArchive.h; sourceTree = ""; }; AE2A2D6921D6851C004B308D /* 7z.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = 7z.h; sourceTree = ""; }; AE2A2D6A21D6851C004B308D /* 7zCrc.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = 7zCrc.c; sourceTree = ""; }; AE2A2D6B21D6851D004B308D /* 7zStream.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = 7zStream.c; sourceTree = ""; }; @@ -674,8 +667,6 @@ AE2A2D8221D7DB78004B308D /* CustomTexture.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CustomTexture.h; sourceTree = ""; }; AE60BD9F22256E2500FA8A5B /* osx_keyboard.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = osx_keyboard.h; sourceTree = ""; }; AE60BDA02225725800FA8A5B /* osx_gamepad.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = osx_gamepad.h; sourceTree = ""; }; - AE60BDA3222B7E1F00FA8A5B /* version.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = version.h; sourceTree = ""; }; - AE60BDA6222B802A00FA8A5B /* version.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = version.cpp; sourceTree = ""; }; AE649BB42188689000EF4A81 /* xxhash.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = xxhash.c; sourceTree = ""; }; AE649BB52188689000EF4A81 /* xxhash.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = xxhash.h; sourceTree = ""; }; AE649BBA218C552500EF4A81 /* all.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = all.h; sourceTree = ""; }; @@ -805,6 +796,12 @@ AEE6279322247C0A00EC7E89 /* gui_util.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = gui_util.h; sourceTree = ""; }; AEE6279522247C2B00EC7E89 /* keyboard_device.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = keyboard_device.cpp; sourceTree = ""; }; AEF2564722886A2E00348550 /* posix_vmem.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = posix_vmem.cpp; sourceTree = ""; }; + AEF2564A2294060300348550 /* ZipArchive.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ZipArchive.cpp; sourceTree = ""; }; + AEF2564B2294060300348550 /* archive.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = archive.h; sourceTree = ""; }; + AEF2564C2294060300348550 /* 7zArchive.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = 7zArchive.cpp; sourceTree = ""; }; + AEF2564D2294060300348550 /* ZipArchive.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ZipArchive.h; sourceTree = ""; }; + AEF2564E2294060300348550 /* archive.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = archive.cpp; sourceTree = ""; }; + AEF2564F2294060400348550 /* 7zArchive.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = 7zArchive.h; sourceTree = ""; }; AEFF7EC7214AEC800068CE11 /* modem.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = modem.cpp; sourceTree = ""; }; AEFF7EC8214AEC800068CE11 /* modem.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = modem.h; sourceTree = ""; }; AEFF7EC9214AEC800068CE11 /* modem_regs.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = modem_regs.h; sourceTree = ""; }; @@ -1001,7 +998,6 @@ AE1E293E20A96B0B00FC6BA2 /* rec-x64 */, 84B7BE8F1B72720200F9733F /* reios */, 84B7BE981B72720200F9733F /* rend */, - AE60BDA1222B7E1F00FA8A5B /* version */, AE80EDB62157D4D500F7800F /* serialize.cpp */, 84B7BEA71B72720200F9733F /* stdclass.cpp */, 84B7BEA81B72720200F9733F /* stdclass.h */, @@ -1685,24 +1681,15 @@ AE2A2D5F21D684B9004B308D /* archive */ = { isa = PBXGroup; children = ( - AE2A2D6121D684DB004B308D /* 7zArchive.cpp */, - AE2A2D6521D684DB004B308D /* 7zArchive.h */, - AE2A2D6221D684DB004B308D /* archive.cpp */, - AE2A2D6021D684DB004B308D /* archive.h */, - AE2A2D6321D684DB004B308D /* ZipArchive.cpp */, - AE2A2D6421D684DB004B308D /* ZipArchive.h */, + AEF2564C2294060300348550 /* 7zArchive.cpp */, + AEF2564F2294060400348550 /* 7zArchive.h */, + AEF2564E2294060300348550 /* archive.cpp */, + AEF2564B2294060300348550 /* archive.h */, + AEF2564A2294060300348550 /* ZipArchive.cpp */, + AEF2564D2294060300348550 /* ZipArchive.h */, ); - path = archive; - sourceTree = ""; - }; - AE60BDA1222B7E1F00FA8A5B /* version */ = { - isa = PBXGroup; - children = ( - AE60BDA6222B802A00FA8A5B /* version.cpp */, - AE60BDA3222B7E1F00FA8A5B /* version.h */, - ); - name = version; - path = ../../../core/version; + name = archive; + path = ../../../core/archive; sourceTree = ""; }; AE649BB32188689000EF4A81 /* xxhash */ = { @@ -2233,6 +2220,7 @@ AEFF7F5E214D9D590068CE11 /* pico_tcp.c in Sources */, AE80EDBE2157D4E600F7800F /* naomi.cpp in Sources */, 84B7BEEB1B72720200F9733F /* zip_error_get.c in Sources */, + AEF256522294060400348550 /* archive.cpp in Sources */, 84B7BEB31B72720200F9733F /* coreio.cpp in Sources */, 84B7BF281B72720200F9733F /* dsp.cpp in Sources */, 84B7BF3D1B72720200F9733F /* Renderer_if.cpp in Sources */, @@ -2246,7 +2234,6 @@ AE649C29218C553A00EF4A81 /* Delta.c in Sources */, 84B7BF431B72720200F9733F /* decoder.cpp in Sources */, 84B7BEBB1B72720200F9733F /* elf64.cpp in Sources */, - AE2A2D6621D684DB004B308D /* 7zArchive.cpp in Sources */, 84B7BF1B1B72720200F9733F /* inffast.c in Sources */, EBDF374F1BB96581001191B5 /* audiobackend_coreaudio.cpp in Sources */, 84967CC31B8F49EE005F1140 /* pngmem.c in Sources */, @@ -2292,7 +2279,6 @@ AE8C27342111A31100D4D8F4 /* dsp_interp.cpp in Sources */, 84B7BF421B72720200F9733F /* blockmanager.cpp in Sources */, 84B7BEE21B72720200F9733F /* zip_add_dir.c in Sources */, - AE60BDA7222B802A00FA8A5B /* version.cpp in Sources */, 84967CC91B8F49EE005F1140 /* pngset.c in Sources */, 84B7BEE61B72720200F9733F /* zip_entry_free.c in Sources */, 84B7BF511B72720200F9733F /* tmu.cpp in Sources */, @@ -2350,11 +2336,11 @@ AE649C3D218C555600EF4A81 /* chd.c in Sources */, AE649C25218C553A00EF4A81 /* Bra.c in Sources */, 84967CCD1B8F49EE005F1140 /* pngwtran.c in Sources */, - AE2A2D6821D684DB004B308D /* ZipArchive.cpp in Sources */, 84B7BF401B72720200F9733F /* ta_ctx.cpp in Sources */, 84B7BF011B72720200F9733F /* zip_open.c in Sources */, AE2A2D5A21D68470004B308D /* gdcartridge.cpp in Sources */, 84967CCA1B8F49EE005F1140 /* pngtrans.c in Sources */, + AEF256502294060400348550 /* ZipArchive.cpp in Sources */, 84B7BF4D1B72720200F9733F /* intc.cpp in Sources */, 84B7BEB01B72720200F9733F /* cl.cpp in Sources */, 84B7BF391B72720200F9733F /* drkPvr.cpp in Sources */, @@ -2418,6 +2404,7 @@ 84B7BF251B72720200F9733F /* aica.cpp in Sources */, 84B7BF791B72720200F9733F /* gldraw.cpp in Sources */, 84B7BF4E1B72720200F9733F /* mmu.cpp in Sources */, + AEF256512294060400348550 /* 7zArchive.cpp in Sources */, 84967CCE1B8F49EE005F1140 /* pngwutil.c in Sources */, 84B7BEBA1B72720200F9733F /* elf32.cpp in Sources */, 84B7BEE91B72720200F9733F /* zip_error.c in Sources */, @@ -2441,7 +2428,6 @@ 84B7BEF71B72720200F9733F /* zip_fread.c in Sources */, AEE6277E220D7B7E00EC7E89 /* imgui_widgets.cpp in Sources */, 84B7BF771B72720200F9733F /* reios_elf.cpp in Sources */, - AE2A2D6721D684DB004B308D /* archive.cpp in Sources */, 84B7BF5C1B72720200F9733F /* common.cpp in Sources */, 84967CC21B8F49EE005F1140 /* pngget.c in Sources */, 84B7BF681B72720200F9733F /* audiobackend_directsound.cpp in Sources */, diff --git a/shell/apple/reicast.xcworkspace/xcshareddata/xcschemes/reicast-ios.xcscheme b/shell/apple/reicast.xcworkspace/xcshareddata/xcschemes/reicast-ios.xcscheme deleted file mode 100644 index 208a001a2..000000000 --- a/shell/apple/reicast.xcworkspace/xcshareddata/xcschemes/reicast-ios.xcscheme +++ /dev/null @@ -1,102 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/shell/apple/reicast.xcworkspace/xcshareddata/xcschemes/reicast-osx.xcscheme b/shell/apple/reicast.xcworkspace/xcshareddata/xcschemes/reicast-osx.xcscheme index 1301943e5..84df9d8d7 100644 --- a/shell/apple/reicast.xcworkspace/xcshareddata/xcschemes/reicast-osx.xcscheme +++ b/shell/apple/reicast.xcworkspace/xcshareddata/xcschemes/reicast-osx.xcscheme @@ -34,20 +34,6 @@ ReferencedContainer = "container:emulator-osx/reicast-osx.xcodeproj"> - - - - Date: Tue, 21 May 2019 12:19:44 +0200 Subject: [PATCH 306/319] OSX: fix schemes --- .../xcshareddata/xcschemes/reicast-osx.xcscheme | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) rename shell/apple/{reicast.xcworkspace => emulator-osx/reicast-osx.xcodeproj}/xcshareddata/xcschemes/reicast-osx.xcscheme (88%) diff --git a/shell/apple/reicast.xcworkspace/xcshareddata/xcschemes/reicast-osx.xcscheme b/shell/apple/emulator-osx/reicast-osx.xcodeproj/xcshareddata/xcschemes/reicast-osx.xcscheme similarity index 88% rename from shell/apple/reicast.xcworkspace/xcshareddata/xcschemes/reicast-osx.xcscheme rename to shell/apple/emulator-osx/reicast-osx.xcodeproj/xcshareddata/xcschemes/reicast-osx.xcscheme index 84df9d8d7..f5b44b43d 100644 --- a/shell/apple/reicast.xcworkspace/xcshareddata/xcschemes/reicast-osx.xcscheme +++ b/shell/apple/emulator-osx/reicast-osx.xcodeproj/xcshareddata/xcschemes/reicast-osx.xcscheme @@ -17,7 +17,7 @@ BlueprintIdentifier = "84A388B21B1CDD3E000166C0" BuildableName = "Reicast.app" BlueprintName = "reicast-osx" - ReferencedContainer = "container:emulator-osx/reicast-osx.xcodeproj"> + ReferencedContainer = "container:reicast-osx.xcodeproj"> + ReferencedContainer = "container:reicast-osx.xcodeproj"> @@ -50,7 +50,7 @@ BlueprintIdentifier = "84A388C21B1CDD3F000166C0" BuildableName = "reicast-osxTests.xctest" BlueprintName = "reicast-osxTests" - ReferencedContainer = "container:emulator-osx/reicast-osx.xcodeproj"> + ReferencedContainer = "container:reicast-osx.xcodeproj"> @@ -60,7 +60,7 @@ BlueprintIdentifier = "84A388B21B1CDD3E000166C0" BuildableName = "Reicast.app" BlueprintName = "reicast-osx" - ReferencedContainer = "container:emulator-osx/reicast-osx.xcodeproj"> + ReferencedContainer = "container:reicast-osx.xcodeproj"> @@ -84,7 +84,7 @@ BlueprintIdentifier = "84A388B21B1CDD3E000166C0" BuildableName = "Reicast.app" BlueprintName = "reicast-osx" - ReferencedContainer = "container:emulator-osx/reicast-osx.xcodeproj"> + ReferencedContainer = "container:reicast-osx.xcodeproj"> @@ -103,7 +103,7 @@ BlueprintIdentifier = "84A388B21B1CDD3E000166C0" BuildableName = "Reicast.app" BlueprintName = "reicast-osx" - ReferencedContainer = "container:emulator-osx/reicast-osx.xcodeproj"> + ReferencedContainer = "container:reicast-osx.xcodeproj"> From 115f7dc93031f3a9805bf6fc65f3d3a7b10fc5f7 Mon Sep 17 00:00:00 2001 From: Flyinghead Date: Tue, 21 May 2019 12:38:53 +0200 Subject: [PATCH 307/319] OSX: Fix bitrise.yml --- bitrise.yml | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/bitrise.yml b/bitrise.yml index a330c64b4..696cf1423 100644 --- a/bitrise.yml +++ b/bitrise.yml @@ -1,7 +1,7 @@ --- -format_version: '5' +format_version: '7' default_step_lib_source: https://github.com/bitrise-io/bitrise-steplib.git -project_type: ios +project_type: macos trigger_map: - push_branch: "*" workflow: primary @@ -10,45 +10,45 @@ trigger_map: workflows: deploy: steps: - - activate-ssh-key@3.1.1: + - activate-ssh-key@4.0.3: run_if: '{{getenv "SSH_RSA_PRIVATE_KEY" | ne ""}}' - - git-clone@4.0.11: {} + - git-clone@4.0.14: {} - cache-pull@2.0.1: {} - script@1.1.5: title: Do anything with Script step - - certificate-and-profile-installer@1.9.3: {} + - certificate-and-profile-installer@1.10.1: {} - recreate-user-schemes@1.0.2: inputs: - project_path: "$BITRISE_PROJECT_PATH" - - xcode-archive@2.4.8: + - xcode-archive-mac@1.6.2: inputs: - project_path: "$BITRISE_PROJECT_PATH" - scheme: "$BITRISE_SCHEME" - export_method: "$BITRISE_EXPORT_METHOD" - - deploy-to-bitrise-io@1.3.12: {} - - cache-push@2.0.5: {} + - deploy-to-bitrise-io@1.3.19: {} + - cache-push@2.1.1: {} primary: steps: - - activate-ssh-key@3.1.1: + - activate-ssh-key@4.0.3: run_if: '{{getenv "SSH_RSA_PRIVATE_KEY" | ne ""}}' - - git-clone@4.0.11: {} + - git-clone@4.0.14: {} - cache-pull@2.0.1: {} - script@1.1.5: title: Do anything with Script step - - certificate-and-profile-installer@1.9.2: {} + - certificate-and-profile-installer@1.10.1: {} - recreate-user-schemes@1.0.2: inputs: - project_path: "$BITRISE_PROJECT_PATH" - - deploy-to-bitrise-io@1.3.10: {} - - cache-push@2.0.5: {} + - deploy-to-bitrise-io@1.3.19: {} + - cache-push@2.1.1: {} app: envs: - opts: is_expand: false - BITRISE_PROJECT_PATH: shell/apple/emulator-ios/reicast-ios.xcodeproj + BITRISE_PROJECT_PATH: shell/apple/reicast.xcworkspace - opts: is_expand: false - BITRISE_SCHEME: reicast-ios + BITRISE_SCHEME: reicast-osx - opts: is_expand: false - BITRISE_EXPORT_METHOD: ad-hoc \ No newline at end of file + BITRISE_EXPORT_METHOD: none From a8f4b7a46e4ad44dcc5df65be0d7e66000df7113 Mon Sep 17 00:00:00 2001 From: Flyinghead Date: Tue, 21 May 2019 15:04:03 +0200 Subject: [PATCH 308/319] OSX: add naomi xcconfig and scheme --- .../emulator-osx/AppDelegate.swift | 3 + .../emulator-osx/Base.lproj/MainMenu.xib | 6 +- .../emulator-osx/DreamcastConfig.xcconfig | 11 + .../emulator-osx/emulator-osx/Info.plist | 4 +- .../emulator-osx/NaomiConfig.xcconfig | 10 + .../reicast-osx.xcodeproj/project.pbxproj | 251 +++++++++++++++++- .../xcschemes/reicast-osx-naomi.xcscheme | 93 +++++++ 7 files changed, 368 insertions(+), 10 deletions(-) create mode 100644 shell/apple/emulator-osx/emulator-osx/DreamcastConfig.xcconfig create mode 100644 shell/apple/emulator-osx/emulator-osx/NaomiConfig.xcconfig create mode 100644 shell/apple/emulator-osx/reicast-osx.xcodeproj/xcshareddata/xcschemes/reicast-osx-naomi.xcscheme diff --git a/shell/apple/emulator-osx/emulator-osx/AppDelegate.swift b/shell/apple/emulator-osx/emulator-osx/AppDelegate.swift index e389b7cb6..5aeee8c4b 100644 --- a/shell/apple/emulator-osx/emulator-osx/AppDelegate.swift +++ b/shell/apple/emulator-osx/emulator-osx/AppDelegate.swift @@ -15,6 +15,9 @@ class AppDelegate: NSObject, NSApplicationDelegate { func applicationDidFinishLaunching(_ aNotification: Notification) { + if let name = Bundle.main.infoDictionary?["CFBundleDisplayName"] as? String { + window.title = name + } } func applicationWillTerminate(_ aNotification: Notification) { diff --git a/shell/apple/emulator-osx/emulator-osx/Base.lproj/MainMenu.xib b/shell/apple/emulator-osx/emulator-osx/Base.lproj/MainMenu.xib index d877445c1..15a2e3655 100644 --- a/shell/apple/emulator-osx/emulator-osx/Base.lproj/MainMenu.xib +++ b/shell/apple/emulator-osx/emulator-osx/Base.lproj/MainMenu.xib @@ -13,7 +13,7 @@ - + @@ -172,8 +172,8 @@ - - + + diff --git a/shell/apple/emulator-osx/emulator-osx/DreamcastConfig.xcconfig b/shell/apple/emulator-osx/emulator-osx/DreamcastConfig.xcconfig new file mode 100644 index 000000000..355eb64af --- /dev/null +++ b/shell/apple/emulator-osx/emulator-osx/DreamcastConfig.xcconfig @@ -0,0 +1,11 @@ +// +// DreamcastConfig.xcconfig +// reicast-osx +// +// Created by flyinghead on 21/05/2019. +// +REI_APP_NAME = Reicast +PRODUCT_NAME = Reicast +CFLAGS = + + diff --git a/shell/apple/emulator-osx/emulator-osx/Info.plist b/shell/apple/emulator-osx/emulator-osx/Info.plist index c0bca831f..b42ba3893 100644 --- a/shell/apple/emulator-osx/emulator-osx/Info.plist +++ b/shell/apple/emulator-osx/emulator-osx/Info.plist @@ -2,10 +2,10 @@ - CFBundleDisplayName - Reicast CFBundleDevelopmentRegion en + CFBundleDisplayName + $(REI_APP_NAME) CFBundleExecutable $(EXECUTABLE_NAME) CFBundleIconFile diff --git a/shell/apple/emulator-osx/emulator-osx/NaomiConfig.xcconfig b/shell/apple/emulator-osx/emulator-osx/NaomiConfig.xcconfig new file mode 100644 index 000000000..551b2b6bb --- /dev/null +++ b/shell/apple/emulator-osx/emulator-osx/NaomiConfig.xcconfig @@ -0,0 +1,10 @@ +// +// NaomiConfig.xcconfig +// reicast-osx +// +// Created by flyinghead on 21/05/2019. +// +REI_APP_NAME = Reicast Naomi +PRODUCT_NAME = Reicast Naomi +CFLAGS = DC_PLATFORM=2 + diff --git a/shell/apple/emulator-osx/reicast-osx.xcodeproj/project.pbxproj b/shell/apple/emulator-osx/reicast-osx.xcodeproj/project.pbxproj index c52e44cab..d74188c40 100644 --- a/shell/apple/emulator-osx/reicast-osx.xcodeproj/project.pbxproj +++ b/shell/apple/emulator-osx/reicast-osx.xcodeproj/project.pbxproj @@ -329,7 +329,7 @@ 84967CBA1B8F49EE005F1140 /* pngwrite.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = pngwrite.c; sourceTree = ""; }; 84967CBB1B8F49EE005F1140 /* pngwtran.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = pngwtran.c; sourceTree = ""; }; 84967CBC1B8F49EE005F1140 /* pngwutil.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = pngwutil.c; sourceTree = ""; }; - 84A388B31B1CDD3E000166C0 /* reicast-osx.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; name = "reicast-osx.app"; path = Reicast.app; sourceTree = BUILT_PRODUCTS_DIR; }; + 84A388B31B1CDD3E000166C0 /* Reicast.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Reicast.app; sourceTree = BUILT_PRODUCTS_DIR; }; 84A388B71B1CDD3E000166C0 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 84A388B81B1CDD3E000166C0 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 84A388BA1B1CDD3E000166C0 /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Images.xcassets; sourceTree = ""; }; @@ -634,6 +634,8 @@ AE1E292B20947D4700FC6BA2 /* xbyak_util.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = xbyak_util.h; sourceTree = ""; }; AE1E293A2095FB1600FC6BA2 /* rec_cpp.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = rec_cpp.cpp; sourceTree = ""; }; AE1E293F20A96B0B00FC6BA2 /* rec_x64.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = rec_x64.cpp; sourceTree = ""; }; + AE2A234922941A0500DD3034 /* NaomiConfig.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = NaomiConfig.xcconfig; sourceTree = ""; }; + AE2A234A22941AF000DD3034 /* DreamcastConfig.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = DreamcastConfig.xcconfig; sourceTree = ""; }; AE2A2D4921D6820D004B308D /* glcache.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = glcache.h; sourceTree = ""; }; AE2A2D4D21D6846F004B308D /* awcartridge.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = awcartridge.h; sourceTree = ""; }; AE2A2D4E21D6846F004B308D /* gdcartridge.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = gdcartridge.cpp; sourceTree = ""; }; @@ -928,7 +930,7 @@ 84A388B41B1CDD3E000166C0 /* Products */ = { isa = PBXGroup; children = ( - 84A388B31B1CDD3E000166C0 /* reicast-osx.app */, + 84A388B31B1CDD3E000166C0 /* Reicast.app */, 84A388C31B1CDD3F000166C0 /* reicast-osxTests.xctest */, ); name = Products; @@ -946,6 +948,8 @@ 84B7BF851B72871600F9733F /* EmuGLView.swift */, AE60BD9F22256E2500FA8A5B /* osx_keyboard.h */, AE60BDA02225725800FA8A5B /* osx_gamepad.h */, + AE2A234922941A0500DD3034 /* NaomiConfig.xcconfig */, + AE2A234A22941AF000DD3034 /* DreamcastConfig.xcconfig */, ); name = "reicast-osx"; path = "emulator-osx"; @@ -2093,7 +2097,7 @@ ); name = "reicast-osx"; productName = "reicast-osx"; - productReference = 84A388B31B1CDD3E000166C0 /* reicast-osx.app */; + productReference = 84A388B31B1CDD3E000166C0 /* Reicast.app */; productType = "com.apple.product-type.application"; }; 84A388C21B1CDD3F000166C0 /* reicast-osxTests */ = { @@ -2508,6 +2512,7 @@ /* Begin XCBuildConfiguration section */ 84A388CB1B1CDD3F000166C0 /* Debug */ = { isa = XCBuildConfiguration; + baseConfigurationReference = AE2A234A22941AF000DD3034 /* DreamcastConfig.xcconfig */; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; @@ -2565,6 +2570,7 @@ }; 84A388CC1B1CDD3F000166C0 /* Release */ = { isa = XCBuildConfiguration; + baseConfigurationReference = AE2A234A22941AF000DD3034 /* DreamcastConfig.xcconfig */; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; @@ -2620,6 +2626,7 @@ }; 84A388CE1B1CDD3F000166C0 /* Debug */ = { isa = XCBuildConfiguration; + baseConfigurationReference = AE2A234A22941AF000DD3034 /* DreamcastConfig.xcconfig */; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; COMBINE_HIDPI_IMAGES = YES; @@ -2647,7 +2654,7 @@ ); INFOPLIST_FILE = "emulator-osx/Info.plist"; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks"; - PRODUCT_NAME = Reicast; + PRODUCT_NAME = "$(REI_APP_NAME)"; SWIFT_OBJC_BRIDGING_HEADER = "emulator-osx/emulator-osx-Bridging-Header.h"; SWIFT_VERSION = 3.0; }; @@ -2655,6 +2662,7 @@ }; 84A388CF1B1CDD3F000166C0 /* Release */ = { isa = XCBuildConfiguration; + baseConfigurationReference = AE2A234A22941AF000DD3034 /* DreamcastConfig.xcconfig */; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; COMBINE_HIDPI_IMAGES = YES; @@ -2679,7 +2687,7 @@ ); INFOPLIST_FILE = "emulator-osx/Info.plist"; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks"; - PRODUCT_NAME = Reicast; + PRODUCT_NAME = "$(REI_APP_NAME)"; SWIFT_OBJC_BRIDGING_HEADER = "emulator-osx/emulator-osx-Bridging-Header.h"; SWIFT_VERSION = 3.0; }; @@ -2687,6 +2695,7 @@ }; 84A388D11B1CDD3F000166C0 /* Debug */ = { isa = XCBuildConfiguration; + baseConfigurationReference = AE2A234A22941AF000DD3034 /* DreamcastConfig.xcconfig */; buildSettings = { BUNDLE_LOADER = "$(TEST_HOST)"; COMBINE_HIDPI_IMAGES = YES; @@ -2708,6 +2717,7 @@ }; 84A388D21B1CDD3F000166C0 /* Release */ = { isa = XCBuildConfiguration; + baseConfigurationReference = AE2A234A22941AF000DD3034 /* DreamcastConfig.xcconfig */; buildSettings = { BUNDLE_LOADER = "$(TEST_HOST)"; COMBINE_HIDPI_IMAGES = YES; @@ -2723,6 +2733,231 @@ }; name = Release; }; + AE2A23432294179900DD3034 /* DebugNaomi */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = AE2A234922941A0500DD3034 /* NaomiConfig.xcconfig */; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + CODE_SIGN_IDENTITY = "-"; + COPY_PHASE_STRIP = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_DYNAMIC_NO_PIC = NO; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + TARGET_NO_WEBUI, + TARGET_NO_NIXPROF, + TARGET_NO_COREIO_HTTP, + TARGET_NO_AREC, + XBYAK_NO_OP_NAMES, + TARGET_NO_OPENMP, + ENABLE_MODEM, + CHD5_LZMA, + _7ZIP_ST, + CHD5_FLAC, + "PACKAGE_VERSION=\\\"1.3.2\\\"", + "FLAC__HAS_OGG=0", + FLAC__NO_DLL, + HAVE_LROUND, + HAVE_STDINT_H, + HAVE_STDLIB_H, + HAVE_SYS_PARAM_H, + "${CFLAGS}", + ); + GCC_SYMBOLS_PRIVATE_EXTERN = NO; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + MACOSX_DEPLOYMENT_TARGET = 10.9; + MTL_ENABLE_DEBUG_INFO = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = macosx; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + }; + name = DebugNaomi; + }; + AE2A23442294179900DD3034 /* DebugNaomi */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = AE2A234922941A0500DD3034 /* NaomiConfig.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + COMBINE_HIDPI_IMAGES = YES; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + "GCC_PREPROCESSOR_DEFINITIONS[arch=i386]" = ( + TARGET_OSX, + "$(inherited)", + ); + "GCC_PREPROCESSOR_DEFINITIONS[arch=x86_64]" = ( + TARGET_OSX_X64, + "$(inherited)", + ); + HEADER_SEARCH_PATHS = ( + "$(inherited)", + /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include, + ../../../core/deps, + ../../../core, + ../../../core/khronos, + "../../../core/deps/picotcp/**", + ../../../core/deps/flac/include, + ../../../core/deps/flac/src/libFLAC/include, + ); + INFOPLIST_FILE = "emulator-osx/Info.plist"; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks"; + PRODUCT_NAME = "$(REI_APP_NAME)"; + SWIFT_OBJC_BRIDGING_HEADER = "emulator-osx/emulator-osx-Bridging-Header.h"; + SWIFT_VERSION = 3.0; + }; + name = DebugNaomi; + }; + AE2A23452294179900DD3034 /* DebugNaomi */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = AE2A234922941A0500DD3034 /* NaomiConfig.xcconfig */; + buildSettings = { + BUNDLE_LOADER = "$(TEST_HOST)"; + COMBINE_HIDPI_IMAGES = YES; + FRAMEWORK_SEARCH_PATHS = ( + "$(DEVELOPER_FRAMEWORKS_DIR)", + "$(inherited)", + ); + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + INFOPLIST_FILE = "emulator-osxTests/Info.plist"; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks"; + PRODUCT_NAME = "reicast-osxTests"; + SWIFT_VERSION = 3.0; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/reicast-osx.app/Contents/MacOS/reicast-osx"; + }; + name = DebugNaomi; + }; + AE2A2346229417A400DD3034 /* ReleaseNaomi */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = AE2A234922941A0500DD3034 /* NaomiConfig.xcconfig */; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + CODE_SIGN_IDENTITY = "-"; + COPY_PHASE_STRIP = YES; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_PREPROCESSOR_DEFINITIONS = ( + TARGET_NO_WEBUI, + TARGET_NO_NIXPROF, + TARGET_NO_COREIO_HTTP, + TARGET_NO_AREC, + XBYAK_NO_OP_NAMES, + TARGET_NO_OPENMP, + ENABLE_MODEM, + CHD5_LZMA, + _7ZIP_ST, + CHD5_FLAC, + "PACKAGE_VERSION=\\\"1.3.2\\\"", + "FLAC__HAS_OGG=0", + FLAC__NO_DLL, + HAVE_LROUND, + HAVE_STDINT_H, + HAVE_STDLIB_H, + HAVE_SYS_PARAM_H, + RELEASE, + "${CFLAGS}", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + MACOSX_DEPLOYMENT_TARGET = 10.9; + MTL_ENABLE_DEBUG_INFO = NO; + SDKROOT = macosx; + }; + name = ReleaseNaomi; + }; + AE2A2347229417A400DD3034 /* ReleaseNaomi */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = AE2A234922941A0500DD3034 /* NaomiConfig.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + COMBINE_HIDPI_IMAGES = YES; + GCC_PREPROCESSOR_DEFINITIONS = "$(inherited)"; + "GCC_PREPROCESSOR_DEFINITIONS[arch=i386]" = ( + TARGET_OSX, + "$(inherited)", + ); + "GCC_PREPROCESSOR_DEFINITIONS[arch=x86_64]" = ( + TARGET_OSX_X64, + "$(inherited)", + ); + HEADER_SEARCH_PATHS = ( + "$(inherited)", + /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include, + ../../../core/deps, + ../../../core, + ../../../core/khronos, + "../../../core/deps/picotcp/**", + ../../../core/deps/flac/include, + ../../../core/deps/flac/src/libFLAC/include, + ); + INFOPLIST_FILE = "emulator-osx/Info.plist"; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks"; + PRODUCT_NAME = "$(REI_APP_NAME)"; + SWIFT_OBJC_BRIDGING_HEADER = "emulator-osx/emulator-osx-Bridging-Header.h"; + SWIFT_VERSION = 3.0; + }; + name = ReleaseNaomi; + }; + AE2A2348229417A400DD3034 /* ReleaseNaomi */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = AE2A234922941A0500DD3034 /* NaomiConfig.xcconfig */; + buildSettings = { + BUNDLE_LOADER = "$(TEST_HOST)"; + COMBINE_HIDPI_IMAGES = YES; + FRAMEWORK_SEARCH_PATHS = ( + "$(DEVELOPER_FRAMEWORKS_DIR)", + "$(inherited)", + ); + INFOPLIST_FILE = "emulator-osxTests/Info.plist"; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks"; + PRODUCT_NAME = "reicast-osxTests"; + SWIFT_VERSION = 3.0; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/reicast-osx.app/Contents/MacOS/reicast-osx"; + }; + name = ReleaseNaomi; + }; /* End XCBuildConfiguration section */ /* Begin XCConfigurationList section */ @@ -2730,7 +2965,9 @@ isa = XCConfigurationList; buildConfigurations = ( 84A388CB1B1CDD3F000166C0 /* Debug */, + AE2A23432294179900DD3034 /* DebugNaomi */, 84A388CC1B1CDD3F000166C0 /* Release */, + AE2A2346229417A400DD3034 /* ReleaseNaomi */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; @@ -2739,7 +2976,9 @@ isa = XCConfigurationList; buildConfigurations = ( 84A388CE1B1CDD3F000166C0 /* Debug */, + AE2A23442294179900DD3034 /* DebugNaomi */, 84A388CF1B1CDD3F000166C0 /* Release */, + AE2A2347229417A400DD3034 /* ReleaseNaomi */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; @@ -2748,7 +2987,9 @@ isa = XCConfigurationList; buildConfigurations = ( 84A388D11B1CDD3F000166C0 /* Debug */, + AE2A23452294179900DD3034 /* DebugNaomi */, 84A388D21B1CDD3F000166C0 /* Release */, + AE2A2348229417A400DD3034 /* ReleaseNaomi */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; diff --git a/shell/apple/emulator-osx/reicast-osx.xcodeproj/xcshareddata/xcschemes/reicast-osx-naomi.xcscheme b/shell/apple/emulator-osx/reicast-osx.xcodeproj/xcshareddata/xcschemes/reicast-osx-naomi.xcscheme new file mode 100644 index 000000000..d84d006af --- /dev/null +++ b/shell/apple/emulator-osx/reicast-osx.xcodeproj/xcshareddata/xcschemes/reicast-osx-naomi.xcscheme @@ -0,0 +1,93 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + From e2a5895ea57c05c013d44844ff970e91120c631d Mon Sep 17 00:00:00 2001 From: Flyinghead Date: Tue, 21 May 2019 15:44:41 +0200 Subject: [PATCH 309/319] OSX: use bitrise.yml in repo --- bitrise.yml | 26 +++++++++++++++++++------- 1 file changed, 19 insertions(+), 7 deletions(-) diff --git a/bitrise.yml b/bitrise.yml index 696cf1423..e29fae404 100644 --- a/bitrise.yml +++ b/bitrise.yml @@ -4,11 +4,15 @@ default_step_lib_source: https://github.com/bitrise-io/bitrise-steplib.git project_type: macos trigger_map: - push_branch: "*" - workflow: primary + workflow: deploy - pull_request_source_branch: "*" - workflow: primary + workflow: deploy workflows: - deploy: + deploy-naomi: + envs: + - opts: + is_expand: false + BITRISE_SCHEME: reicast-osx-naomi steps: - activate-ssh-key@4.0.3: run_if: '{{getenv "SSH_RSA_PRIVATE_KEY" | ne ""}}' @@ -27,7 +31,11 @@ workflows: - export_method: "$BITRISE_EXPORT_METHOD" - deploy-to-bitrise-io@1.3.19: {} - cache-push@2.1.1: {} - primary: + deploy: + envs: + - opts: + is_expand: false + BITRISE_SCHEME: reicast-osx steps: - activate-ssh-key@4.0.3: run_if: '{{getenv "SSH_RSA_PRIVATE_KEY" | ne ""}}' @@ -39,16 +47,20 @@ workflows: - recreate-user-schemes@1.0.2: inputs: - project_path: "$BITRISE_PROJECT_PATH" + - xcode-archive-mac@1.6.2: + inputs: + - project_path: "$BITRISE_PROJECT_PATH" + - scheme: "$BITRISE_SCHEME" + - export_method: "$BITRISE_EXPORT_METHOD" - deploy-to-bitrise-io@1.3.19: {} - cache-push@2.1.1: {} + after_run: + - deploy-naomi app: envs: - opts: is_expand: false BITRISE_PROJECT_PATH: shell/apple/reicast.xcworkspace - - opts: - is_expand: false - BITRISE_SCHEME: reicast-osx - opts: is_expand: false BITRISE_EXPORT_METHOD: none From 1b132a82eec643b639616279d628ab727a2a7980 Mon Sep 17 00:00:00 2001 From: Flyinghead Date: Tue, 21 May 2019 16:12:33 +0200 Subject: [PATCH 310/319] travis: no release build, no deployment --- .travis.yml | 26 -------------------------- 1 file changed, 26 deletions(-) diff --git a/.travis.yml b/.travis.yml index 4a76546f4..0be3fa93a 100755 --- a/.travis.yml +++ b/.travis.yml @@ -1,5 +1,3 @@ -notifications: - irc: chat.freenode.net#reicast language: android android: components: @@ -22,10 +20,6 @@ cache: - "$HOME/.gradle/wrapper/" - "$HOME/.android/build-cache" before_install: -- test -z "$encrypted_c726d225a9d9_key" || openssl aes-256-cbc -K $encrypted_c726d225a9d9_key -iv $encrypted_c726d225a9d9_iv -in secure.tar.enc -out secure.tar -d -- test -z "$encrypted_c726d225a9d9_key" || tar -xvf secure.tar -- test -z "$encrypted_c726d225a9d9_key" || mv reicast-beta-cd.jks shell/android-studio/reicast-beta-cd.jks -- test -z "$encrypted_c726d225a9d9_key" || mv reicast-beta-cd.p12 shell/android-studio/reicast-beta-cd.p12 - GIT_HASH=`git log --pretty=format:'%h' -n 1` - GIT_BUILD=`git describe --all --always`-$GIT_HASH - mkdir "$ANDROID_HOME/licenses" || true @@ -41,7 +35,6 @@ install: - export PATH=${PATH}:$ANDROID_HOME/platform-tools - export PATH=${PATH}:$NDK - mkdir -p ~/.android -- test -z "$encrypted_c726d225a9d9_key" || mv debug.keystore ~/.android/debug.keystore before_script: script: - git fetch --depth 250 @@ -49,22 +42,3 @@ script: - export NUMBER_OF_PROCESSORS=2 - sudo chmod 755 travis-build.sh - "./travis-build.sh" -- test -z "$encrypted_c726d225a9d9_key" || ./travis-release.sh -before_deploy: -- cd ../../ -- mkdir -p artifacts/$GIT_BUILD/ -- cp shell/android-studio/reicast/build/outputs/apk/dreamcast/debug/reicast-dreamcast-debug.apk artifacts/$GIT_BUILD/reicast-android-debug-$GIT_HASH.apk -deploy: - provider: s3 - access_key_id: AKIAJR5J3OHAQUP5BHHQ - secret_access_key: - secure: dDmBiadS1cc2XCWWDZ1kwZGIoLkwEOLsdi5nzVdn1dHMsEKxN3u+3WFuuOfJHZYcdrtN7VhojCarPa+eGDDeiVme4m3y+QKMbI9lecHX9+t4jDNUyYep5TpeIVUkZ7fj4JsMi7U9/YFBTg7QnqvVsMAHE9vobkvjegjAYToBrlM= - bucket: reicast-builds - local-dir: artifacts - upload-dir: builds - skip_cleanup: true - on: - all_branches: true - repo: reicast/reicast-emulator -after_deploy: -- echo "done" From 3d67d391879db6dee9656a77d3cd8f532b170a8f Mon Sep 17 00:00:00 2001 From: Flyinghead Date: Tue, 21 May 2019 16:50:38 +0200 Subject: [PATCH 311/319] fix warning --- core/build.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/build.h b/core/build.h index 14f4cd2dd..1b30a736e 100755 --- a/core/build.h +++ b/core/build.h @@ -315,7 +315,7 @@ // Compiler Related -#define COMPILER_VC_OR_CLANG_WIN32 ((BUILD_COMPILER == COMPILER_VC) || (BUILD_COMPILER == COMPILER_CLANG) && defined(WIN32)) +#define COMPILER_VC_OR_CLANG_WIN32 ((BUILD_COMPILER == COMPILER_VC) || (BUILD_COMPILER == COMPILER_CLANG) && HOST_OS == OS_WINDOWS) #if BUILD_COMPILER!=COMPILER_VC #define ATTR_USED __attribute__((used)) From 036b9b015a2b2acc24c69b6b20d6b503e44eb159 Mon Sep 17 00:00:00 2001 From: Flyinghead Date: Tue, 21 May 2019 16:50:55 +0200 Subject: [PATCH 312/319] fix travis build --- shell/android-studio/reicast/build.gradle | 2 ++ shell/android-studio/travis-build.sh | 4 ++-- shell/android-studio/travis-release.sh | 2 +- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/shell/android-studio/reicast/build.gradle b/shell/android-studio/reicast/build.gradle index 7ccb431ab..c077e393b 100644 --- a/shell/android-studio/reicast/build.gradle +++ b/shell/android-studio/reicast/build.gradle @@ -58,6 +58,7 @@ android { zipAlignEnabled true proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.txt' } + /* release { debuggable false minifyEnabled true @@ -65,6 +66,7 @@ android { proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.txt' signingConfig signingConfigs.release } + */ } flavorDimensions "systemtype" diff --git a/shell/android-studio/travis-build.sh b/shell/android-studio/travis-build.sh index 3e42bd164..52acd32c2 100755 --- a/shell/android-studio/travis-build.sh +++ b/shell/android-studio/travis-build.sh @@ -1,7 +1,7 @@ #!/bin/bash set -ev if [ "${TRAVIS_PULL_REQUEST}" = "false" ]; then - ./gradlew build --configure-on-demand + ./gradlew build --configure-on-demand --console=plain else - ./gradlew assembleDebug --configure-on-demand + ./gradlew assembleDebug --configure-on-demand --console=plain fi diff --git a/shell/android-studio/travis-release.sh b/shell/android-studio/travis-release.sh index c589e340b..b2a20b050 100755 --- a/shell/android-studio/travis-release.sh +++ b/shell/android-studio/travis-release.sh @@ -3,6 +3,6 @@ set -ev echo travis release: pr = "${TRAVIS_PULL_REQUEST}" - branch = "${TRAVIS_BRANCH}" if [ "${TRAVIS_PULL_REQUEST}" = "false" ]; then if [ "${TRAVIS_BRANCH}" = "master" ]; then - ./gradlew publishApkDreamcastRelease + ./gradlew publishApkDreamcastRelease --console=plain fi fi From 484f0a616e8cf558da65b8b99d4243a5190292ff Mon Sep 17 00:00:00 2001 From: Flyinghead Date: Tue, 21 May 2019 17:16:38 +0200 Subject: [PATCH 313/319] travis: only build debug build --- shell/android-studio/reicast/build.gradle | 2 -- shell/android-studio/travis-build.sh | 8 ++++---- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/shell/android-studio/reicast/build.gradle b/shell/android-studio/reicast/build.gradle index c077e393b..7ccb431ab 100644 --- a/shell/android-studio/reicast/build.gradle +++ b/shell/android-studio/reicast/build.gradle @@ -58,7 +58,6 @@ android { zipAlignEnabled true proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.txt' } - /* release { debuggable false minifyEnabled true @@ -66,7 +65,6 @@ android { proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.txt' signingConfig signingConfigs.release } - */ } flavorDimensions "systemtype" diff --git a/shell/android-studio/travis-build.sh b/shell/android-studio/travis-build.sh index 52acd32c2..6d787e793 100755 --- a/shell/android-studio/travis-build.sh +++ b/shell/android-studio/travis-build.sh @@ -1,7 +1,7 @@ #!/bin/bash set -ev -if [ "${TRAVIS_PULL_REQUEST}" = "false" ]; then - ./gradlew build --configure-on-demand --console=plain -else +#if [ "${TRAVIS_PULL_REQUEST}" = "false" ]; then +# ./gradlew build --configure-on-demand --console=plain +#else ./gradlew assembleDebug --configure-on-demand --console=plain -fi +#fi From 6055c13e77459c61fc4fbe5b0bedc7fcfcc0a6b5 Mon Sep 17 00:00:00 2001 From: Flyinghead Date: Tue, 21 May 2019 17:21:52 +0200 Subject: [PATCH 314/319] appveyor: no deploy --- appveyor.yml | 13 +------------ 1 file changed, 1 insertion(+), 12 deletions(-) diff --git a/appveyor.yml b/appveyor.yml index 50c9db088..feb4e19bd 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -2,8 +2,8 @@ version: git-{branch}-{build} image: Visual Studio 2017 configuration: -- RelWithDebInfo - fast +- RelWithDebInfo platform: - x64 @@ -45,14 +45,3 @@ after_build: artifacts: - path: artifacts name: reicast-win_$(PLATFORM)-$(CONFIGURATION)-$(APPVEYOR_REPO_COMMIT) - -deploy: -- provider: S3 - access_key_id: AKIAJETEBUZSLZF3YNKA - secret_access_key: - secure: ZxrysCeqMK99dOOzKyoeN4R5aO8JjA4RTrwVAQPQHz8sjyyKuVDZT2fC+9m6lUIi - region: eu-west-1 - bucket: reicast-builds-windows - folder: 'builds/heads/$(APPVEYOR_REPO_BRANCH)-$(APPVEYOR_REPO_COMMIT)' - artifact: reicast-win_$(PLATFORM)-$(CONFIGURATION)-$(APPVEYOR_REPO_COMMIT) - set_public: true \ No newline at end of file From 582c9f358cba808fc27f7324a082799ed7e376c6 Mon Sep 17 00:00:00 2001 From: Flyinghead Date: Tue, 21 May 2019 18:18:47 +0200 Subject: [PATCH 315/319] rec-cpp: crash when running rom --- core/rec-cpp/rec_cpp.cpp | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/core/rec-cpp/rec_cpp.cpp b/core/rec-cpp/rec_cpp.cpp index caa403bdd..208978db1 100644 --- a/core/rec-cpp/rec_cpp.cpp +++ b/core/rec-cpp/rec_cpp.cpp @@ -811,13 +811,19 @@ struct opcode_check_block : public opcodeExec { opcodeExec* setup(RuntimeBlockInfo* block) { this->block = block; ptr = GetMemPtr(block->addr, 4); - code.resize(sz == -1 ? block->sh4_code_size : sz); - memcpy(&code[0], ptr, sz == -1 ? block->sh4_code_size : sz); + if (ptr != NULL) + { + code.resize(sz == -1 ? block->sh4_code_size : sz); + memcpy(&code[0], ptr, sz == -1 ? block->sh4_code_size : sz); + } return this; } void execute() { + if (code.empty()) + return; + switch (sz) { case 4: From 37f379fe6b77fbff5416fbffafa7f7851d4a4af8 Mon Sep 17 00:00:00 2001 From: Flyinghead Date: Tue, 21 May 2019 18:27:15 +0200 Subject: [PATCH 316/319] Better HBlank implementation --- core/hw/pvr/spg.cpp | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/core/hw/pvr/spg.cpp b/core/hw/pvr/spg.cpp index b0970c3f1..a83a482af 100755 --- a/core/hw/pvr/spg.cpp +++ b/core/hw/pvr/spg.cpp @@ -119,6 +119,20 @@ int spg_line_sched(int tag, int cycl, int jit) SPG_STATUS.vsync=in_vblank; SPG_STATUS.scanline=prv_cur_scanline; + switch (SPG_HBLANK_INT.hblank_int_mode) + { + case 0x0: + if (prv_cur_scanline == SPG_HBLANK_INT.line_comp_val) + asic_RaiseInterrupt(holly_HBLank); + break; + case 0x2: + asic_RaiseInterrupt(holly_HBLank); + break; + default: + die("Unimplemented HBLANK INT mode"); + break; + } + //Vblank start -- really need to test the scanline values if (prv_cur_scanline==0) { @@ -129,7 +143,6 @@ int spg_line_sched(int tag, int cycl, int jit) //Vblank counter vblk_cnt++; - asic_RaiseInterrupt(holly_HBLank);// -> This turned out to be HBlank btw , needs to be emulated ;( //TODO : rend_if_VBlank(); rend_vblank();//notify for vblank :) From 2e5eaa6e2bcf1c0ba99e907b6176e6fb6dd251a8 Mon Sep 17 00:00:00 2001 From: Flyinghead Date: Tue, 21 May 2019 18:27:43 +0200 Subject: [PATCH 317/319] Log error when writing to flash ram --- core/hw/holly/sb_mem.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/core/hw/holly/sb_mem.cpp b/core/hw/holly/sb_mem.cpp index b86bf3603..21c593c43 100644 --- a/core/hw/holly/sb_mem.cpp +++ b/core/hw/holly/sb_mem.cpp @@ -85,7 +85,9 @@ bool LoadRomFiles(const string& root) if (settings.dreamcast.language <= 5) syscfg.lang = settings.dreamcast.language; - sys_nvmem.WriteBlock(FLASH_PT_USER, FLASH_USER_SYSCFG, &syscfg); + if (sys_nvmem.WriteBlock(FLASH_PT_USER, FLASH_USER_SYSCFG, &syscfg) != 1) + printf("Failed to save time and language to flash RAM\n"); + #endif #if DC_PLATFORM == DC_PLATFORM_ATOMISWAVE From d55d803c169c23d2c3d268bd0e628fd9f19d61f3 Mon Sep 17 00:00:00 2001 From: Flyinghead Date: Tue, 21 May 2019 19:11:47 +0200 Subject: [PATCH 318/319] gles2 build fix --- core/rend/gles/gldraw.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/core/rend/gles/gldraw.cpp b/core/rend/gles/gldraw.cpp index 8c23dd58b..66c208886 100644 --- a/core/rend/gles/gldraw.cpp +++ b/core/rend/gles/gldraw.cpp @@ -1174,6 +1174,7 @@ bool render_output_framebuffer() } else { +#ifndef GLES2 if (gl.ofbo.fbo == 0) return false; glBindFramebuffer(GL_READ_FRAMEBUFFER, gl.ofbo.fbo); @@ -1182,6 +1183,7 @@ bool render_output_framebuffer() 0, 0, screen_width, screen_height, GL_COLOR_BUFFER_BIT, GL_LINEAR); glBindFramebuffer(GL_FRAMEBUFFER, 0); +#endif } return true; } From fb76efb08d88bcdb272aa68d8512ae2a44479770 Mon Sep 17 00:00:00 2001 From: Flyinghead Date: Wed, 22 May 2019 11:41:12 +0200 Subject: [PATCH 319/319] Standard functions for virtual mem alloc/protect/map/... --- core/hw/aica/dsp.cpp | 8 +--- core/hw/aica/dsp_arm64.cpp | 3 +- core/hw/arm7/arm7.cpp | 21 +-------- core/hw/naomi/naomi_cart.cpp | 44 +++++++------------ core/hw/naomi/naomi_cart.h | 2 +- core/hw/sh4/dyna/driver.cpp | 4 -- core/linux-dist/main.cpp | 2 - core/linux/common.cpp | 2 - core/linux/posix_vmem.cpp | 82 +++++++++++++++++++++++++++--------- core/oslib/oslib.h | 1 - core/rec-ARM/rec_arm.cpp | 1 - core/rec-ARM64/rec_arm64.cpp | 1 - core/stdclass.h | 19 +++++++-- core/windows/win_vmem.cpp | 47 +++++++++++++++++---- core/windows/winmain.cpp | 6 --- 15 files changed, 139 insertions(+), 104 deletions(-) diff --git a/core/hw/aica/dsp.cpp b/core/hw/aica/dsp.cpp index cf3899aee..676c03978 100644 --- a/core/hw/aica/dsp.cpp +++ b/core/hw/aica/dsp.cpp @@ -186,13 +186,7 @@ void dsp_init() dsp.RBP=0; dsp.regs.MDEC_CT=1; - - //os_MakeExecutable(dsp.DynCode,sizeof(dsp.DynCode)); -#if HOST_OS == OS_WINDOWS - DWORD old; - VirtualProtect(dsp.DynCode, sizeof(dsp.DynCode), PAGE_EXECUTE_READWRITE, &old); -#endif - + mem_region_set_exec(dsp.DynCode, sizeof(dsp.DynCode)); } void dsp_recompile(); diff --git a/core/hw/aica/dsp_arm64.cpp b/core/hw/aica/dsp_arm64.cpp index cac7b4c71..77b5ef39b 100644 --- a/core/hw/aica/dsp_arm64.cpp +++ b/core/hw/aica/dsp_arm64.cpp @@ -21,7 +21,6 @@ #if HOST_CPU == CPU_ARM64 && FEAT_DSPREC != DYNAREC_NONE -#include #include "dsp.h" #include "hw/aica/aica_if.h" #include "deps/vixl/aarch64/macro-assembler-aarch64.h" @@ -522,7 +521,7 @@ void dsp_init() dsp.regs.MDEC_CT = 1; dsp.dyndirty = true; - if (mprotect(dsp.DynCode, sizeof(dsp.DynCode), PROT_EXEC | PROT_READ | PROT_WRITE)) + if (!mem_region_set_exec(dsp.DynCode, sizeof(dsp.DynCode))) { perror("Couldn’t mprotect DSP code"); die("mprotect failed in arm64 dsp"); diff --git a/core/hw/arm7/arm7.cpp b/core/hw/arm7/arm7.cpp index 1d980c77e..4d5f0e117 100644 --- a/core/hw/arm7/arm7.cpp +++ b/core/hw/arm7/arm7.cpp @@ -422,7 +422,7 @@ void arm_Run(u32 CycleCount) { } #else // FEAT_AREC != DYNAREC_NONE -#if HOST_OS == OS_LINUX || HOST_OS == OS_DARWIN +#if HOST_OS == OS_DARWIN #include #endif @@ -1557,10 +1557,6 @@ naked void arm_exit() * */ -//mprotect and stuff .. - -#include - void armEmit32(u32 emit32) { if (icPtr >= (ICache+ICacheSize-1024)) @@ -2183,25 +2179,12 @@ void armt_init() ICache = (u8*)mmap(ICache, ICacheSize, PROT_READ | PROT_WRITE | PROT_EXEC, MAP_FIXED | MAP_PRIVATE | MAP_ANON, 0, 0); #endif -#if HOST_OS == OS_WINDOWS - DWORD old; - VirtualProtect(ICache,ICacheSize,PAGE_EXECUTE_READWRITE,&old); -#elif HOST_OS == OS_LINUX || HOST_OS == OS_DARWIN - - printf("\n\t ARM7_TCB addr: %p | from: %p | addr here: %p\n", ICache, ARM7_TCB, armt_init); - - if (mprotect(ICache, ICacheSize, PROT_EXEC|PROT_READ|PROT_WRITE)) - { - perror("\n\tError - Couldn’t mprotect ARM7_TCB!"); - verify(false); - } + mem_region_set_exec(ICache, ICacheSize); #if TARGET_IPHONE memset((u8*)mmap(ICache, ICacheSize, PROT_READ | PROT_WRITE | PROT_EXEC, MAP_FIXED | MAP_PRIVATE | MAP_ANON, 0, 0),0xFF,ICacheSize); #else memset(ICache,0xFF,ICacheSize); -#endif - #endif icPtr=ICache; diff --git a/core/hw/naomi/naomi_cart.cpp b/core/hw/naomi/naomi_cart.cpp index e9b18e2c0..55bfdd5c1 100644 --- a/core/hw/naomi/naomi_cart.cpp +++ b/core/hw/naomi/naomi_cart.cpp @@ -24,7 +24,6 @@ bool bios_loaded = false; #include #include - #include #include #endif @@ -485,15 +484,9 @@ bool naomi_cart_LoadRom(char* file) RomCacheMapCount = (u32)files.size(); RomCacheMap = new fd_t[files.size()](); - //Allocate space for the ram, so we are sure we have a segment of continius ram -#if HOST_OS == OS_WINDOWS - RomPtr = (u8*)VirtualAlloc(0, RomSize, MEM_RESERVE, PAGE_NOACCESS); -#else - RomPtr = (u8*)mmap(0, RomSize, PROT_NONE, MAP_PRIVATE | MAP_ANON, -1, 0); -#endif - - verify(RomPtr != 0); - verify(RomPtr != (void*)-1); + //Allocate space for the ram, so we are sure we have a segment of continuous ram + RomPtr = (u8*)mem_region_reserve(NULL, RomSize); + verify(RomPtr != NULL); bool load_error = false; @@ -550,11 +543,7 @@ bool naomi_cart_LoadRom(char* file) } //Release the segment we reserved so we can map the files there -#if HOST_OS == OS_WINDOWS - verify(VirtualFree(RomPtr, 0, MEM_RELEASE)); -#else - munmap(RomPtr, RomSize); -#endif + mem_region_release(RomPtr, RomSize); if (load_error) { @@ -574,23 +563,13 @@ bool naomi_cart_LoadRom(char* file) if (RomCacheMap[i] == INVALID_FD) { //printf("-Reserving ram at 0x%08X, size 0x%08X\n", fstart[i], fsize[i]); - -#if HOST_OS == OS_WINDOWS - bool mapped = RomDest == VirtualAlloc(RomDest, fsize[i], MEM_RESERVE, PAGE_NOACCESS); -#else - bool mapped = RomDest == (u8*)mmap(RomDest, RomSize, PROT_NONE, MAP_PRIVATE, 0, 0); -#endif - + bool mapped = RomDest == (u8 *)mem_region_reserve(RomDest, fsize[i]); verify(mapped); } else { //printf("-Mapping \"%s\" at 0x%08X, size 0x%08X\n", files[i].c_str(), fstart[i], fsize[i]); -#if HOST_OS == OS_WINDOWS - bool mapped = RomDest == MapViewOfFileEx(RomCacheMap[i], FILE_MAP_READ, 0, 0, fsize[i], RomDest); -#else - bool mapped = RomDest == mmap(RomDest, fsize[i], PROT_READ, MAP_PRIVATE, RomCacheMap[i], 0 ); -#endif + bool mapped = RomDest == (u8 *)mem_region_map_file((void *)(uintptr_t)RomCacheMap[i], RomDest, fsize[i], 0, false); if (!mapped) { printf("-Mapping ROM FAILED: %s @ %08x size %x\n", files[i].c_str(), fstart[i], fsize[i]); @@ -653,7 +632,8 @@ Cartridge::Cartridge(u32 size) Cartridge::~Cartridge() { - free(RomPtr); + if (RomPtr != NULL) + free(RomPtr); } bool Cartridge::Read(u32 offset, u32 size, void* dst) @@ -1036,3 +1016,11 @@ void M2Cartridge::Unserialize(void** data, unsigned int* total_size) { REICAST_US(naomi_cart_ram); NaomiCartridge::Unserialize(data, total_size); } + +DecryptedCartridge::~DecryptedCartridge() +{ + // TODO this won't work on windows -> need to unmap each file first + mem_region_release(RomPtr, RomSize); + // Avoid crash when freeing vmem + RomPtr = NULL; +} diff --git a/core/hw/naomi/naomi_cart.h b/core/hw/naomi/naomi_cart.h index 918de1b72..85758fb28 100644 --- a/core/hw/naomi/naomi_cart.h +++ b/core/hw/naomi/naomi_cart.h @@ -58,7 +58,7 @@ class DecryptedCartridge : public NaomiCartridge { public: DecryptedCartridge(u8 *rom_ptr, u32 size) : NaomiCartridge(size) { RomPtr = rom_ptr; } - // FIXME Must do a munmap and close for each segment + virtual ~DecryptedCartridge() override; }; class M2Cartridge : public NaomiCartridge diff --git a/core/hw/sh4/dyna/driver.cpp b/core/hw/sh4/dyna/driver.cpp index a93717783..4261b9f99 100644 --- a/core/hw/sh4/dyna/driver.cpp +++ b/core/hw/sh4/dyna/driver.cpp @@ -417,10 +417,6 @@ void recSh4_Reset(bool Manual) Sh4_int_Reset(Manual); } -#if HOST_OS == OS_DARWIN -#include -#endif - void recSh4_Init() { printf("recSh4 Init\n"); diff --git a/core/linux-dist/main.cpp b/core/linux-dist/main.cpp index 044fa9aa6..b6931a125 100644 --- a/core/linux-dist/main.cpp +++ b/core/linux-dist/main.cpp @@ -4,13 +4,11 @@ #if HOST_OS==OS_LINUX #include #include -//#include #include #include #include #include #include -#include #include #include "hw/sh4/dyna/blockmanager.h" #include "hw/maple/maple_cfg.h" diff --git a/core/linux/common.cpp b/core/linux/common.cpp index 39e3efd40..f1113e99d 100644 --- a/core/linux/common.cpp +++ b/core/linux/common.cpp @@ -11,13 +11,11 @@ #include #include #endif -//#include #include #include #include #include #include -#include #include #if !defined(TARGET_BSD) && !defined(_ANDROID) && !defined(TARGET_IPHONE) && !defined(TARGET_NACL32) && !defined(TARGET_EMSCRIPTEN) && !defined(TARGET_OSX) && !defined(TARGET_OSX_X64) #include diff --git a/core/linux/posix_vmem.cpp b/core/linux/posix_vmem.cpp index 9a573cdae..2f76b4d88 100644 --- a/core/linux/posix_vmem.cpp +++ b/core/linux/posix_vmem.cpp @@ -46,19 +46,64 @@ int ashmem_create_region(const char *name, size_t size) { } #endif // #ifdef _ANDROID -void VLockedMemory::LockRegion(unsigned offset, unsigned size_bytes) { - size_t inpage = offset & PAGE_MASK; - if (mprotect(&data[offset - inpage], size_bytes + inpage, PROT_READ)) { - die("mprotect failed ..\n"); - } +bool mem_region_lock(void *start, size_t len) +{ + size_t inpage = (uintptr_t)start & PAGE_MASK; + if (mprotect((u8*)start - inpage, len + inpage, PROT_READ)) + die("mprotect failed..."); + return true; } -void VLockedMemory::UnLockRegion(unsigned offset, unsigned size_bytes) { - size_t inpage = offset & PAGE_MASK; - if (mprotect(&data[offset - inpage], size_bytes + inpage, PROT_READ|PROT_WRITE)) { +bool mem_region_unlock(void *start, size_t len) +{ + size_t inpage = (uintptr_t)start & PAGE_MASK; + if (mprotect((u8*)start - inpage, len + inpage, PROT_READ | PROT_WRITE)) // Add some way to see why it failed? gdb> info proc mappings - die("mprotect failed ..\n"); + die("mprotect failed..."); + return true; +} + +bool mem_region_set_exec(void *start, size_t len) +{ + size_t inpage = (uintptr_t)start & PAGE_MASK; + if (mprotect((u8*)start - inpage, len + inpage, PROT_READ | PROT_WRITE | PROT_EXEC)) + die("mprotect failed..."); + return true; +} + +void *mem_region_reserve(void *start, size_t len) +{ + void *p = mmap(start, len, PROT_NONE, MAP_PRIVATE | MAP_ANON, -1, 0); + if (p == MAP_FAILED) + { + perror("mmap"); + return NULL; } + else + return p; +} + +bool mem_region_release(void *start, size_t len) +{ + return munmap(start, len) == 0; +} + +void *mem_region_map_file(void *file_handle, void *dest, size_t len, size_t offset, bool readwrite) +{ + int flags = MAP_SHARED | MAP_NOSYNC | (dest != NULL ? MAP_FIXED : 0); + void *p = mmap(dest, len, PROT_READ | (readwrite ? PROT_WRITE : 0), flags, (int)(uintptr_t)file_handle, offset); + if (p == MAP_FAILED) + { + perror("mmap"); + return NULL; + } + else + return p; +} + +bool mem_region_unmap_file(void *start, size_t len) +{ + return mem_region_release(start, len); } // Allocates memory via a fd on shmem/ahmem or even a file on disk @@ -111,7 +156,7 @@ VMemType vmem_platform_init(void **vmem_base_addr, void **sh4rcb_addr) { // Now try to allocate a contiguous piece of memory. unsigned memsize = 512*1024*1024 + sizeof(Sh4RCB) + ARAM_SIZE_MAX + 0x10000; - void *first_ptr = mmap(0, memsize, PROT_NONE, MAP_PRIVATE | MAP_ANON, -1, 0); + void *first_ptr = mem_region_reserve(NULL, memsize); if (!first_ptr) { close(shmem_fd); return MemTypeError; @@ -125,14 +170,14 @@ VMemType vmem_platform_init(void **vmem_base_addr, void **sh4rcb_addr) { void *sh4rcb_base_ptr = (void*)(ptrint + FPCB_SIZE); // Now map the memory for the SH4 context, do not include FPCB on purpose (paged on demand). - mprotect(sh4rcb_base_ptr, sizeof(Sh4RCB) - FPCB_SIZE, PROT_READ | PROT_WRITE); + mem_region_unlock(sh4rcb_base_ptr, sizeof(Sh4RCB) - FPCB_SIZE); return MemType512MB; } // Just tries to wipe as much as possible in the relevant area. void vmem_platform_destroy() { - munmap(virt_ram_base, 0x20000000); + mem_region_release(virt_ram_base, 0x20000000); } // Resets a chunk of memory by deleting its data and setting its protection back. @@ -150,7 +195,7 @@ void vmem_platform_reset_mem(void *ptr, unsigned size_bytes) { // Allocates a bunch of memory (page aligned and page-sized) void vmem_platform_ondemand_page(void *address, unsigned size_bytes) { - verify(!mprotect(address, size_bytes, PROT_READ | PROT_WRITE)); + verify(mem_region_unlock(address, size_bytes)); } // Creates mappings to the underlying file including mirroring sections @@ -163,15 +208,13 @@ void vmem_platform_create_mappings(const vmem_mapping *vmem_maps, unsigned numma // Calculate the number of mirrors unsigned address_range_size = vmem_maps[i].end_address - vmem_maps[i].start_address; unsigned num_mirrors = (address_range_size) / vmem_maps[i].memsize; - int protection = vmem_maps[i].allow_writes ? (PROT_READ | PROT_WRITE) : PROT_READ; verify((address_range_size % vmem_maps[i].memsize) == 0 && num_mirrors >= 1); for (unsigned j = 0; j < num_mirrors; j++) { unsigned offset = vmem_maps[i].start_address + j * vmem_maps[i].memsize; - verify(!munmap(&virt_ram_base[offset], vmem_maps[i].memsize)); - verify(MAP_FAILED != mmap(&virt_ram_base[offset], vmem_maps[i].memsize, protection, - MAP_SHARED | MAP_NOSYNC | MAP_FIXED, shmem_fd, vmem_maps[i].memoffset)); - // ??? (mprotect(rv,size,prot)!=0) + verify(mem_region_unmap_file(&virt_ram_base[offset], vmem_maps[i].memsize)); + verify(mem_region_map_file((void*)(uintptr_t)shmem_fd, &virt_ram_base[offset], + vmem_maps[i].memsize, vmem_maps[i].memoffset, vmem_maps[i].allow_writes) != NULL); } } } @@ -179,7 +222,8 @@ void vmem_platform_create_mappings(const vmem_mapping *vmem_maps, unsigned numma // Prepares the code region for JIT operations, thus marking it as RWX bool vmem_platform_prepare_jit_block(void *code_area, unsigned size, void **code_area_rwx) { // Try to map is as RWX, this fails apparently on OSX (and perhaps other systems?) - if (mprotect(code_area, size, PROT_READ | PROT_WRITE | PROT_EXEC)) { + if (!mem_region_set_exec(code_area, size)) + { // Well it failed, use another approach, unmap the memory area and remap it back. // Seems it works well on Darwin according to reicast code :P munmap(code_area, size); diff --git a/core/oslib/oslib.h b/core/oslib/oslib.h index da7d41745..8e5e9e433 100644 --- a/core/oslib/oslib.h +++ b/core/oslib/oslib.h @@ -2,7 +2,6 @@ #include "types.h" void os_SetWindowText(const char* text); -void os_MakeExecutable(void* ptr, u32 sz); double os_GetSeconds(); void os_DoEvents(); diff --git a/core/rec-ARM/rec_arm.cpp b/core/rec-ARM/rec_arm.cpp index 933ff19d2..33109d11d 100644 --- a/core/rec-ARM/rec_arm.cpp +++ b/core/rec-ARM/rec_arm.cpp @@ -1,5 +1,4 @@ #include -#include #include "types.h" #if FEAT_SHREC == DYNAREC_JIT diff --git a/core/rec-ARM64/rec_arm64.cpp b/core/rec-ARM64/rec_arm64.cpp index 65afd83d0..def343005 100644 --- a/core/rec-ARM64/rec_arm64.cpp +++ b/core/rec-ARM64/rec_arm64.cpp @@ -22,7 +22,6 @@ #if FEAT_SHREC == DYNAREC_JIT #include -#include #include #include "deps/vixl/aarch64/macro-assembler-aarch64.h" diff --git a/core/stdclass.h b/core/stdclass.h index e7f8d2685..39640f5d8 100644 --- a/core/stdclass.h +++ b/core/stdclass.h @@ -279,6 +279,13 @@ string get_game_save_prefix(); string get_game_basename(); string get_game_dir(); +bool mem_region_lock(void *start, size_t len); +bool mem_region_unlock(void *start, size_t len); +bool mem_region_set_exec(void *start, size_t len); +void *mem_region_reserve(void *start, size_t len); +bool mem_region_release(void *start, size_t len); +void *mem_region_map_file(void *file_handle, void *dest, size_t len, size_t offset, bool readwrite); +bool mem_region_unmap_file(void *start, size_t len); // Locked memory class, used for texture invalidation purposes. class VLockedMemory { @@ -297,8 +304,15 @@ public: void LockRegion(unsigned offset, unsigned size_bytes) {} void UnLockRegion(unsigned offset, unsigned size_bytes) {} #else - void LockRegion(unsigned offset, unsigned size_bytes); - void UnLockRegion(unsigned offset, unsigned size_bytes); + void LockRegion(unsigned offset, unsigned size_bytes) + { + mem_region_lock(&data[offset], size_bytes); + } + + void UnLockRegion(unsigned offset, unsigned size_bytes) + { + mem_region_unlock(&data[offset], size_bytes); + } #endif void Zero() { @@ -318,7 +332,6 @@ public: } }; - int msgboxf(const wchar* text,unsigned int type,...); diff --git a/core/windows/win_vmem.cpp b/core/windows/win_vmem.cpp index edb4b21cd..682d6b8c4 100644 --- a/core/windows/win_vmem.cpp +++ b/core/windows/win_vmem.cpp @@ -8,19 +8,50 @@ // Implementation of the vmem related function for Windows platforms. // For now this probably does some assumptions on the CPU/platform. -// This implements the VLockedMemory interface, as defined in _vmem.h // The implementation allows it to be empty (that is, to not lock memory). -void VLockedMemory::LockRegion(unsigned offset, unsigned size) { - //verify(offset + size < this->size && size != 0); +bool mem_region_lock(void *start, size_t len) +{ DWORD old; - VirtualProtect(&data[offset], size, PAGE_READONLY, &old); + if (!VirtualProtect(start, len, PAGE_READONLY, &old)) + die("VirtualProtect failed ..\n"); + return true; } -void VLockedMemory::UnLockRegion(unsigned offset, unsigned size) { - //verify(offset + size <= this->size && size != 0); +bool mem_region_unlock(void *start, size_t len) +{ DWORD old; - VirtualProtect(&data[offset], size, PAGE_READWRITE, &old); + if (!VirtualProtect(start, len, PAGE_READWRITE, &old)) + die("VirtualProtect failed ..\n"); + return true; +} + +bool mem_region_set_exec(void *start, size_t len) +{ + DWORD old; + if (!VirtualProtect(start, len, PAGE_EXECUTE_READWRITE, &old)) + die("VirtualProtect failed ..\n"); + return true; +} + +void *mem_region_reserve(void *start, size_t len) +{ + return VirtualAlloc(start, len, MEM_RESERVE, PAGE_NOACCESS); +} + +bool mem_region_release(void *start, size_t len) +{ + return VirtualFree(start, 0, MEM_RELEASE); +} + +void *mem_region_map_file(void *file_handle, void *dest, size_t len, size_t offset, bool readwrite) +{ + return MapViewOfFileEx((HANDLE)file_handle, readwrite ? FILE_MAP_WRITE : FILE_MAP_READ, (DWORD)(offset >> 32), (DWORD)offset, len, dest); +} + +bool mem_region_unmap_file(void *start, size_t len) +{ + return UnmapViewOfFile(start); } static HANDLE mem_handle = INVALID_HANDLE_VALUE, mem_handle2 = INVALID_HANDLE_VALUE; @@ -37,7 +68,7 @@ VMemType vmem_platform_init(void **vmem_base_addr, void **sh4rcb_addr) { // Now allocate the actual address space (it will be 64KB aligned on windows). unsigned memsize = 512*1024*1024 + sizeof(Sh4RCB) + ARAM_SIZE_MAX; - base_alloc = (char*)VirtualAlloc(0, memsize, MEM_RESERVE, PAGE_NOACCESS); + base_alloc = (char*)mem_region_reserve(NULL, memsize); // Calculate pointers now *sh4rcb_addr = &base_alloc[0]; diff --git a/core/windows/winmain.cpp b/core/windows/winmain.cpp index adec1ffa9..ee50bc527 100644 --- a/core/windows/winmain.cpp +++ b/core/windows/winmain.cpp @@ -465,12 +465,6 @@ void os_SetWindowText(const char* text) } } -void os_MakeExecutable(void* ptr, u32 sz) -{ - DWORD old; - VirtualProtect(ptr, sz, PAGE_EXECUTE_READWRITE, &old); // sizeof(sz) really? -} - void ReserveBottomMemory() { #if defined(_WIN64) && defined(_DEBUG)