From 25afdee39bd8576239b3edbe1bb100755eb3cf97 Mon Sep 17 00:00:00 2001 From: staphen Date: Thu, 18 Nov 2021 09:20:11 -0500 Subject: [PATCH] Replace libsmacker with libsmackerdec --- 3rdParty/libsmacker/.editorconfig | 6 - 3rdParty/libsmacker/COPYING | 502 ------- 3rdParty/libsmacker/README | 55 - 3rdParty/libsmacker/smacker.c | 1801 ----------------------- 3rdParty/libsmacker/smacker.h | 106 -- 3rdParty/libsmacker/smk_malloc.h | 56 - 3rdParty/libsmackerdec/CMakeLists.txt | 23 + CMakeLists.txt | 7 +- Source/storm/storm_svid.cpp | 108 +- Source/utils/sdl_rwops_file_wrapper.cpp | 70 - Source/utils/sdl_rwops_file_wrapper.hpp | 17 - 11 files changed, 76 insertions(+), 2675 deletions(-) delete mode 100644 3rdParty/libsmacker/.editorconfig delete mode 100644 3rdParty/libsmacker/COPYING delete mode 100644 3rdParty/libsmacker/README delete mode 100644 3rdParty/libsmacker/smacker.c delete mode 100644 3rdParty/libsmacker/smacker.h delete mode 100644 3rdParty/libsmacker/smk_malloc.h create mode 100644 3rdParty/libsmackerdec/CMakeLists.txt delete mode 100644 Source/utils/sdl_rwops_file_wrapper.cpp delete mode 100644 Source/utils/sdl_rwops_file_wrapper.hpp diff --git a/3rdParty/libsmacker/.editorconfig b/3rdParty/libsmacker/.editorconfig deleted file mode 100644 index 1455edc1f..000000000 --- a/3rdParty/libsmacker/.editorconfig +++ /dev/null @@ -1,6 +0,0 @@ -[*] -indent_style = tab -end_of_line = lf -charset = utf-8 -trim_trailing_whitespace = true -insert_final_newline = true diff --git a/3rdParty/libsmacker/COPYING b/3rdParty/libsmacker/COPYING deleted file mode 100644 index 4362b4915..000000000 --- a/3rdParty/libsmacker/COPYING +++ /dev/null @@ -1,502 +0,0 @@ - GNU LESSER GENERAL PUBLIC LICENSE - Version 2.1, February 1999 - - Copyright (C) 1991, 1999 Free Software Foundation, Inc. - 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - Everyone is permitted to copy and distribute verbatim copies - of this license document, but changing it is not allowed. - -[This is the first released version of the Lesser GPL. It also counts - as the successor of the GNU Library Public License, version 2, hence - the version number 2.1.] - - Preamble - - The licenses for most software are designed to take away your -freedom to share and change it. By contrast, the GNU General Public -Licenses are intended to guarantee your freedom to share and change -free software--to make sure the software is free for all its users. - - This license, the Lesser General Public License, applies to some -specially designated software packages--typically libraries--of the -Free Software Foundation and other authors who decide to use it. You -can use it too, but we suggest you first think carefully about whether -this license or the ordinary General Public License is the better -strategy to use in any particular case, based on the explanations below. - - When we speak of free software, we are referring to freedom of use, -not price. Our General Public Licenses are designed to make sure that -you have the freedom to distribute copies of free software (and charge -for this service if you wish); that you receive source code or can get -it if you want it; that you can change the software and use pieces of -it in new free programs; and that you are informed that you can do -these things. - - To protect your rights, we need to make restrictions that forbid -distributors to deny you these rights or to ask you to surrender these -rights. These restrictions translate to certain responsibilities for -you if you distribute copies of the library or if you modify it. - - For example, if you distribute copies of the library, whether gratis -or for a fee, you must give the recipients all the rights that we gave -you. You must make sure that they, too, receive or can get the source -code. If you link other code with the library, you must provide -complete object files to the recipients, so that they can relink them -with the library after making changes to the library and recompiling -it. And you must show them these terms so they know their rights. - - We protect your rights with a two-step method: (1) we copyright the -library, and (2) we offer you this license, which gives you legal -permission to copy, distribute and/or modify the library. - - To protect each distributor, we want to make it very clear that -there is no warranty for the free library. Also, if the library is -modified by someone else and passed on, the recipients should know -that what they have is not the original version, so that the original -author's reputation will not be affected by problems that might be -introduced by others. - - Finally, software patents pose a constant threat to the existence of -any free program. We wish to make sure that a company cannot -effectively restrict the users of a free program by obtaining a -restrictive license from a patent holder. Therefore, we insist that -any patent license obtained for a version of the library must be -consistent with the full freedom of use specified in this license. - - Most GNU software, including some libraries, is covered by the -ordinary GNU General Public License. This license, the GNU Lesser -General Public License, applies to certain designated libraries, and -is quite different from the ordinary General Public License. We use -this license for certain libraries in order to permit linking those -libraries into non-free programs. - - When a program is linked with a library, whether statically or using -a shared library, the combination of the two is legally speaking a -combined work, a derivative of the original library. The ordinary -General Public License therefore permits such linking only if the -entire combination fits its criteria of freedom. The Lesser General -Public License permits more lax criteria for linking other code with -the library. - - We call this license the "Lesser" General Public License because it -does Less to protect the user's freedom than the ordinary General -Public License. It also provides other free software developers Less -of an advantage over competing non-free programs. These disadvantages -are the reason we use the ordinary General Public License for many -libraries. However, the Lesser license provides advantages in certain -special circumstances. - - For example, on rare occasions, there may be a special need to -encourage the widest possible use of a certain library, so that it becomes -a de-facto standard. To achieve this, non-free programs must be -allowed to use the library. A more frequent case is that a free -library does the same job as widely used non-free libraries. In this -case, there is little to gain by limiting the free library to free -software only, so we use the Lesser General Public License. - - In other cases, permission to use a particular library in non-free -programs enables a greater number of people to use a large body of -free software. For example, permission to use the GNU C Library in -non-free programs enables many more people to use the whole GNU -operating system, as well as its variant, the GNU/Linux operating -system. - - Although the Lesser General Public License is Less protective of the -users' freedom, it does ensure that the user of a program that is -linked with the Library has the freedom and the wherewithal to run -that program using a modified version of the Library. - - The precise terms and conditions for copying, distribution and -modification follow. Pay close attention to the difference between a -"work based on the library" and a "work that uses the library". The -former contains code derived from the library, whereas the latter must -be combined with the library in order to run. - - GNU LESSER GENERAL PUBLIC LICENSE - TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION - - 0. This License Agreement applies to any software library or other -program which contains a notice placed by the copyright holder or -other authorized party saying it may be distributed under the terms of -this Lesser General Public License (also called "this License"). -Each licensee is addressed as "you". - - A "library" means a collection of software functions and/or data -prepared so as to be conveniently linked with application programs -(which use some of those functions and data) to form executables. - - The "Library", below, refers to any such software library or work -which has been distributed under these terms. A "work based on the -Library" means either the Library or any derivative work under -copyright law: that is to say, a work containing the Library or a -portion of it, either verbatim or with modifications and/or translated -straightforwardly into another language. (Hereinafter, translation is -included without limitation in the term "modification".) - - "Source code" for a work means the preferred form of the work for -making modifications to it. For a library, complete source code means -all the source code for all modules it contains, plus any associated -interface definition files, plus the scripts used to control compilation -and installation of the library. - - Activities other than copying, distribution and modification are not -covered by this License; they are outside its scope. The act of -running a program using the Library is not restricted, and output from -such a program is covered only if its contents constitute a work based -on the Library (independent of the use of the Library in a tool for -writing it). Whether that is true depends on what the Library does -and what the program that uses the Library does. - - 1. You may copy and distribute verbatim copies of the Library's -complete source code as you receive it, in any medium, provided that -you conspicuously and appropriately publish on each copy an -appropriate copyright notice and disclaimer of warranty; keep intact -all the notices that refer to this License and to the absence of any -warranty; and distribute a copy of this License along with the -Library. - - You may charge a fee for the physical act of transferring a copy, -and you may at your option offer warranty protection in exchange for a -fee. - - 2. You may modify your copy or copies of the Library or any portion -of it, thus forming a work based on the Library, and copy and -distribute such modifications or work under the terms of Section 1 -above, provided that you also meet all of these conditions: - - a) The modified work must itself be a software library. - - b) You must cause the files modified to carry prominent notices - stating that you changed the files and the date of any change. - - c) You must cause the whole of the work to be licensed at no - charge to all third parties under the terms of this License. - - d) If a facility in the modified Library refers to a function or a - table of data to be supplied by an application program that uses - the facility, other than as an argument passed when the facility - is invoked, then you must make a good faith effort to ensure that, - in the event an application does not supply such function or - table, the facility still operates, and performs whatever part of - its purpose remains meaningful. - - (For example, a function in a library to compute square roots has - a purpose that is entirely well-defined independent of the - application. Therefore, Subsection 2d requires that any - application-supplied function or table used by this function must - be optional: if the application does not supply it, the square - root function must still compute square roots.) - -These requirements apply to the modified work as a whole. If -identifiable sections of that work are not derived from the Library, -and can be reasonably considered independent and separate works in -themselves, then this License, and its terms, do not apply to those -sections when you distribute them as separate works. But when you -distribute the same sections as part of a whole which is a work based -on the Library, the distribution of the whole must be on the terms of -this License, whose permissions for other licensees extend to the -entire whole, and thus to each and every part regardless of who wrote -it. - -Thus, it is not the intent of this section to claim rights or contest -your rights to work written entirely by you; rather, the intent is to -exercise the right to control the distribution of derivative or -collective works based on the Library. - -In addition, mere aggregation of another work not based on the Library -with the Library (or with a work based on the Library) on a volume of -a storage or distribution medium does not bring the other work under -the scope of this License. - - 3. You may opt to apply the terms of the ordinary GNU General Public -License instead of this License to a given copy of the Library. To do -this, you must alter all the notices that refer to this License, so -that they refer to the ordinary GNU General Public License, version 2, -instead of to this License. (If a newer version than version 2 of the -ordinary GNU General Public License has appeared, then you can specify -that version instead if you wish.) Do not make any other change in -these notices. - - Once this change is made in a given copy, it is irreversible for -that copy, so the ordinary GNU General Public License applies to all -subsequent copies and derivative works made from that copy. - - This option is useful when you wish to copy part of the code of -the Library into a program that is not a library. - - 4. You may copy and distribute the Library (or a portion or -derivative of it, under Section 2) in object code or executable form -under the terms of Sections 1 and 2 above provided that you accompany -it with the complete corresponding machine-readable source code, which -must be distributed under the terms of Sections 1 and 2 above on a -medium customarily used for software interchange. - - If distribution of object code is made by offering access to copy -from a designated place, then offering equivalent access to copy the -source code from the same place satisfies the requirement to -distribute the source code, even though third parties are not -compelled to copy the source along with the object code. - - 5. A program that contains no derivative of any portion of the -Library, but is designed to work with the Library by being compiled or -linked with it, is called a "work that uses the Library". Such a -work, in isolation, is not a derivative work of the Library, and -therefore falls outside the scope of this License. - - However, linking a "work that uses the Library" with the Library -creates an executable that is a derivative of the Library (because it -contains portions of the Library), rather than a "work that uses the -library". The executable is therefore covered by this License. -Section 6 states terms for distribution of such executables. - - When a "work that uses the Library" uses material from a header file -that is part of the Library, the object code for the work may be a -derivative work of the Library even though the source code is not. -Whether this is true is especially significant if the work can be -linked without the Library, or if the work is itself a library. The -threshold for this to be true is not precisely defined by law. - - If such an object file uses only numerical parameters, data -structure layouts and accessors, and small macros and small inline -functions (ten lines or less in length), then the use of the object -file is unrestricted, regardless of whether it is legally a derivative -work. (Executables containing this object code plus portions of the -Library will still fall under Section 6.) - - Otherwise, if the work is a derivative of the Library, you may -distribute the object code for the work under the terms of Section 6. -Any executables containing that work also fall under Section 6, -whether or not they are linked directly with the Library itself. - - 6. As an exception to the Sections above, you may also combine or -link a "work that uses the Library" with the Library to produce a -work containing portions of the Library, and distribute that work -under terms of your choice, provided that the terms permit -modification of the work for the customer's own use and reverse -engineering for debugging such modifications. - - You must give prominent notice with each copy of the work that the -Library is used in it and that the Library and its use are covered by -this License. You must supply a copy of this License. If the work -during execution displays copyright notices, you must include the -copyright notice for the Library among them, as well as a reference -directing the user to the copy of this License. Also, you must do one -of these things: - - a) Accompany the work with the complete corresponding - machine-readable source code for the Library including whatever - changes were used in the work (which must be distributed under - Sections 1 and 2 above); and, if the work is an executable linked - with the Library, with the complete machine-readable "work that - uses the Library", as object code and/or source code, so that the - user can modify the Library and then relink to produce a modified - executable containing the modified Library. (It is understood - that the user who changes the contents of definitions files in the - Library will not necessarily be able to recompile the application - to use the modified definitions.) - - b) Use a suitable shared library mechanism for linking with the - Library. A suitable mechanism is one that (1) uses at run time a - copy of the library already present on the user's computer system, - rather than copying library functions into the executable, and (2) - will operate properly with a modified version of the library, if - the user installs one, as long as the modified version is - interface-compatible with the version that the work was made with. - - c) Accompany the work with a written offer, valid for at - least three years, to give the same user the materials - specified in Subsection 6a, above, for a charge no more - than the cost of performing this distribution. - - d) If distribution of the work is made by offering access to copy - from a designated place, offer equivalent access to copy the above - specified materials from the same place. - - e) Verify that the user has already received a copy of these - materials or that you have already sent this user a copy. - - For an executable, the required form of the "work that uses the -Library" must include any data and utility programs needed for -reproducing the executable from it. However, as a special exception, -the materials to be distributed need not include anything that is -normally distributed (in either source or binary form) with the major -components (compiler, kernel, and so on) of the operating system on -which the executable runs, unless that component itself accompanies -the executable. - - It may happen that this requirement contradicts the license -restrictions of other proprietary libraries that do not normally -accompany the operating system. Such a contradiction means you cannot -use both them and the Library together in an executable that you -distribute. - - 7. You may place library facilities that are a work based on the -Library side-by-side in a single library together with other library -facilities not covered by this License, and distribute such a combined -library, provided that the separate distribution of the work based on -the Library and of the other library facilities is otherwise -permitted, and provided that you do these two things: - - a) Accompany the combined library with a copy of the same work - based on the Library, uncombined with any other library - facilities. This must be distributed under the terms of the - Sections above. - - b) Give prominent notice with the combined library of the fact - that part of it is a work based on the Library, and explaining - where to find the accompanying uncombined form of the same work. - - 8. You may not copy, modify, sublicense, link with, or distribute -the Library except as expressly provided under this License. Any -attempt otherwise to copy, modify, sublicense, link with, or -distribute the Library is void, and will automatically terminate your -rights under this License. However, parties who have received copies, -or rights, from you under this License will not have their licenses -terminated so long as such parties remain in full compliance. - - 9. You are not required to accept this License, since you have not -signed it. However, nothing else grants you permission to modify or -distribute the Library or its derivative works. These actions are -prohibited by law if you do not accept this License. Therefore, by -modifying or distributing the Library (or any work based on the -Library), you indicate your acceptance of this License to do so, and -all its terms and conditions for copying, distributing or modifying -the Library or works based on it. - - 10. Each time you redistribute the Library (or any work based on the -Library), the recipient automatically receives a license from the -original licensor to copy, distribute, link with or modify the Library -subject to these terms and conditions. You may not impose any further -restrictions on the recipients' exercise of the rights granted herein. -You are not responsible for enforcing compliance by third parties with -this License. - - 11. If, as a consequence of a court judgment or allegation of patent -infringement or for any other reason (not limited to patent issues), -conditions are imposed on you (whether by court order, agreement or -otherwise) that contradict the conditions of this License, they do not -excuse you from the conditions of this License. If you cannot -distribute so as to satisfy simultaneously your obligations under this -License and any other pertinent obligations, then as a consequence you -may not distribute the Library at all. For example, if a patent -license would not permit royalty-free redistribution of the Library by -all those who receive copies directly or indirectly through you, then -the only way you could satisfy both it and this License would be to -refrain entirely from distribution of the Library. - -If any portion of this section is held invalid or unenforceable under any -particular circumstance, the balance of the section is intended to apply, -and the section as a whole is intended to apply in other circumstances. - -It is not the purpose of this section to induce you to infringe any -patents or other property right claims or to contest validity of any -such claims; this section has the sole purpose of protecting the -integrity of the free software distribution system which is -implemented by public license practices. Many people have made -generous contributions to the wide range of software distributed -through that system in reliance on consistent application of that -system; it is up to the author/donor to decide if he or she is willing -to distribute software through any other system and a licensee cannot -impose that choice. - -This section is intended to make thoroughly clear what is believed to -be a consequence of the rest of this License. - - 12. If the distribution and/or use of the Library is restricted in -certain countries either by patents or by copyrighted interfaces, the -original copyright holder who places the Library under this License may add -an explicit geographical distribution limitation excluding those countries, -so that distribution is permitted only in or among countries not thus -excluded. In such case, this License incorporates the limitation as if -written in the body of this License. - - 13. The Free Software Foundation may publish revised and/or new -versions of the Lesser General Public License from time to time. -Such new versions will be similar in spirit to the present version, -but may differ in detail to address new problems or concerns. - -Each version is given a distinguishing version number. If the Library -specifies a version number of this License which applies to it and -"any later version", you have the option of following the terms and -conditions either of that version or of any later version published by -the Free Software Foundation. If the Library does not specify a -license version number, you may choose any version ever published by -the Free Software Foundation. - - 14. If you wish to incorporate parts of the Library into other free -programs whose distribution conditions are incompatible with these, -write to the author to ask for permission. For software which is -copyrighted by the Free Software Foundation, write to the Free -Software Foundation; we sometimes make exceptions for this. Our -decision will be guided by the two goals of preserving the free status -of all derivatives of our free software and of promoting the sharing -and reuse of software generally. - - NO WARRANTY - - 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO -WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. -EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR -OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY -KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE -IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE -LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME -THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. - - 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN -WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY -AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU -FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR -CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE -LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING -RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A -FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF -SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH -DAMAGES. - - END OF TERMS AND CONDITIONS - - How to Apply These Terms to Your New Libraries - - If you develop a new library, and you want it to be of the greatest -possible use to the public, we recommend making it free software that -everyone can redistribute and change. You can do so by permitting -redistribution under these terms (or, alternatively, under the terms of the -ordinary General Public License). - - To apply these terms, attach the following notices to the library. It is -safest to attach them to the start of each source file to most effectively -convey the exclusion of warranty; and each file should have at least the -"copyright" line and a pointer to where the full notice is found. - - - Copyright (C) - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. - - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with this library; if not, write to the Free Software - Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - -Also add information on how to contact you by electronic and paper mail. - -You should also get your employer (if you work as a programmer) or your -school, if any, to sign a "copyright disclaimer" for the library, if -necessary. Here is a sample; alter the names: - - Yoyodyne, Inc., hereby disclaims all copyright interest in the - library `Frob' (a library for tweaking knobs) written by James Random Hacker. - - , 1 April 1990 - Ty Coon, President of Vice - -That's all there is to it! diff --git a/3rdParty/libsmacker/README b/3rdParty/libsmacker/README deleted file mode 100644 index 5f9840317..000000000 --- a/3rdParty/libsmacker/README +++ /dev/null @@ -1,55 +0,0 @@ -libsmacker -A C library for decoding .smk Smacker Video files - -version 1.2.0 -2021-06-02 - -(c) Greg Kennedy 2013-2021 -http://libsmacker.sourceforge.net ----- - ---- -Introduction ---- -libsmacker is a cross-platform C library which can be used for decoding Smacker Video files produced by RAD Game Tools. Smacker Video was the king of video middleware in the 1990s, and its 256-color compressed video format was used in over 2600 software titles. - -libsmacker implements the minimum feature set required from smackw32.dll to get an smk off a disk and the frames / audio into a buffer in the correct order. - ---- -License ---- -libsmacker is released under the Lesser GNU Public License, v2.1. See the file COPYING for more information. - ---- -Usage ---- -See the webpage for sample code and function documentation. The source package additionally includes a pair of driver programs: -* driver.c - dumps all frames of a file to a bmp/ subdirectory, and all audio as raw streams to out_*.raw files in CWD -* smk2avi.c - converts smk file(s) to AVI files - uncompressed 24-bit color and PCM audio stream. - -Though the libraries are "bulletproofed" the sample apps are not: be cautious if you plan to implement in some critical environment. - ---- -Changelog ---- -1.2.0 -* Major refactor of data structures for performance -* Merge all files into a single unified .c source file -* Support audio-only .smk files -1.1.1 -* Re-license under LGPL 2.1 -1.1 -* Switch to autotools-based build -* Incorporates patches from Dalerank Slim, Gennady Trafimenkov, and Bianca van Schaik -* Performance improvements and code cleanup / safety. -1.0 -* Initial revision - ---- -Contact ---- -Questions/comments: -* by email: kennedy.greg@gmail.com -* by website: http://libsmacker.sourceforge.net - -Enjoy! diff --git a/3rdParty/libsmacker/smacker.c b/3rdParty/libsmacker/smacker.c deleted file mode 100644 index a66a1fb93..000000000 --- a/3rdParty/libsmacker/smacker.c +++ /dev/null @@ -1,1801 +0,0 @@ -/** - libsmacker - A C library for decoding .smk Smacker Video files - Copyright (C) 2012-2021 Greg Kennedy - - See smacker.h for more information. - - smacker.c - Main implementation file of libsmacker. - Open, close, query, render, advance and seek an smk -*/ - -#include "smacker.h" - -#include "smk_malloc.h" - -#include -#include -#include -#include - -/* ************************************************************************* */ -/* BITSTREAM Structure */ -/* ************************************************************************* */ -/* Wraps a block of memory and adds functions to read 1 or 8 bits at a time */ -struct smk_bit_t { - const unsigned char * buffer, * end; - unsigned int bit_num; -}; - -/* ************************************************************************* */ -/* BITSTREAM Functions */ -/* ************************************************************************* */ -/** Initialize a bitstream wrapper */ -static void smk_bs_init(struct smk_bit_t * const bs, const unsigned char * const b, const size_t size) -{ - /* null check */ - assert(bs); - assert(b); - /* set up the pointer to bitstream start and end, and set the bit pointer to 0 */ - bs->buffer = b; - bs->end = b + size; - bs->bit_num = 0; -} - -/* Reads a bit - Returns -1 if error encountered */ -static int smk_bs_read_1(struct smk_bit_t * const bs) -{ - int ret; - /* null check */ - assert(bs); - - /* don't die when running out of bits, but signal */ - if (bs->buffer >= bs->end) { - fputs("libsmacker::smk_bs_read_1(): ERROR: bitstream exhausted.\n", stderr); - return -1; - } - - /* get next bit and store for return */ - ret = (*bs->buffer >> bs->bit_num) & 1; - - /* advance to next bit */ - if (bs->bit_num >= 7) { - /* Out of bits in this byte: next! */ - bs->buffer ++; - bs->bit_num = 0; - } else - bs->bit_num ++; - - return ret; -} - -/* Reads a byte - Returns -1 if error. */ -static int smk_bs_read_8(struct smk_bit_t * const bs) -{ - /* null check */ - assert(bs); - - /* don't die when running out of bits, but signal */ - if (bs->buffer + (bs->bit_num > 0) >= bs->end) { - fputs("libsmacker::smk_bs_read_8(): ERROR: bitstream exhausted.\n", stderr); - return -1; - } - - if (bs->bit_num) { - /* unaligned read */ - int ret = *bs->buffer >> bs->bit_num; - bs->buffer ++; - return ret | (*bs->buffer << (8 - bs->bit_num) & 0xFF); - } - - /* aligned read */ - return *bs->buffer++; -} - -/* ************************************************************************* */ -/* HUFF8 Structure */ -/* ************************************************************************* */ -#define SMK_HUFF8_BRANCH 0x8000 -#define SMK_HUFF8_LEAF_MASK 0x7FFF - -struct smk_huff8_t { - /* Unfortunately, smk files do not store the alloc size of a small tree. - 511 entries is the pessimistic case (N codes and N-1 branches, - with N=256 for 8 bits) */ - size_t size; - unsigned short tree[511]; -}; - -/* ************************************************************************* */ -/* HUFF8 Functions */ -/* ************************************************************************* */ -/* Recursive sub-func for building a tree into an array. */ -static int _smk_huff8_build_rec(struct smk_huff8_t * const t, struct smk_bit_t * const bs) -{ - int bit, value; - assert(t); - assert(bs); - - /* Make sure we aren't running out of bounds */ - if (t->size >= 511) { - fputs("libsmacker::_smk_huff8_build_rec() - ERROR: size exceeded\n", stderr); - return 0; - } - - /* Read the next bit */ - if ((bit = smk_bs_read_1(bs)) < 0) { - fputs("libsmacker::_smk_huff8_build_rec() - ERROR: get_bit returned -1\n", stderr); - return 0; - } - - if (bit) { - /* Bit set: this forms a Branch node. - what we have to do is build the left-hand branch, - assign the "jump" address, - then build the right hand branch from there. - */ - /* track the current index */ - value = t->size ++; - - /* go build the left branch */ - if (! _smk_huff8_build_rec(t, bs)) { - fputs("libsmacker::_smk_huff8_build_rec() - ERROR: failed to build left sub-tree\n", stderr); - return 0; - } - - /* now go back to our current location, and - mark our location as a "jump" */ - t->tree[value] = SMK_HUFF8_BRANCH | t->size; - - /* continue building the right side */ - if (! _smk_huff8_build_rec(t, bs)) { - fputs("libsmacker::_smk_huff8_build_rec() - ERROR: failed to build right sub-tree\n", stderr); - return 0; - } - } else { - /* Bit unset signifies a Leaf node. */ - /* Attempt to read value */ - if ((value = smk_bs_read_8(bs)) < 0) { - fputs("libsmacker::_smk_huff8_build_rec() - ERROR: get_byte returned -1\n", stderr); - return 0; - } - - /* store to tree */ - t->tree[t->size ++] = value; - } - - return 1; -} - -/** - Build an 8-bit Hufftree out of a Bitstream. -*/ -static int smk_huff8_build(struct smk_huff8_t * const t, struct smk_bit_t * const bs) -{ - int bit; - /* null check */ - assert(t); - assert(bs); - - /* Smacker huff trees begin with a set-bit. */ - if ((bit = smk_bs_read_1(bs)) < 0) { - fputs("libsmacker::smk_huff8_build() - ERROR: initial get_bit returned -1\n", stderr); - return 0; - } - - /* OK to fill out the struct now */ - t->size = 0; - - /* First bit indicates whether a tree is present or not. */ - /* Very small or audio-only files may have no tree. */ - if (bit) { - if (! _smk_huff8_build_rec(t, bs)) { - fputs("libsmacker::smk_huff8_build() - ERROR: tree build failed\n", stderr); - return 0; - } - } else - t->tree[0] = 0; - - /* huff trees end with an unset-bit */ - if ((bit = smk_bs_read_1(bs)) < 0) { - fputs("libsmacker::smk_huff8_build() - ERROR: final get_bit returned -1\n", stderr); - return 0; - } - - /* a 0 is expected here, a 1 generally indicates a problem! */ - if (bit) { - fputs("libsmacker::smk_huff8_build() - ERROR: final get_bit returned 1\n", stderr); - return 0; - } - - return 1; -} - -/* Look up an 8-bit value from a basic huff tree. - Return -1 on error. */ -static int smk_huff8_lookup(const struct smk_huff8_t * const t, struct smk_bit_t * const bs) -{ - int bit, index = 0; - /* null check */ - assert(t); - assert(bs); - - while (t->tree[index] & SMK_HUFF8_BRANCH) { - if ((bit = smk_bs_read_1(bs)) < 0) { - fputs("libsmacker::smk_huff8_lookup() - ERROR: get_bit returned -1\n", stderr); - return -1; - } - - if (bit) { - /* take the right branch */ - index = t->tree[index] & SMK_HUFF8_LEAF_MASK; - } else { - /* take the left branch */ - index ++; - } - } - - /* at leaf node. return the value at this point. */ - return t->tree[index]; -} - -/* ************************************************************************* */ -/* HUFF16 Structure */ -/* ************************************************************************* */ -#define SMK_HUFF16_BRANCH 0x80000000 -#define SMK_HUFF16_CACHE 0x40000000 -#define SMK_HUFF16_LEAF_MASK 0x3FFFFFFF - -struct smk_huff16_t { - unsigned int * tree; - size_t size; - - /* recently-used values cache */ - unsigned short cache[3]; -}; - -/* ************************************************************************* */ -/* HUFF16 Functions */ -/* ************************************************************************* */ -/* Recursive sub-func for building a tree into an array. */ -static int _smk_huff16_build_rec(struct smk_huff16_t * const t, struct smk_bit_t * const bs, const struct smk_huff8_t * const low8, const struct smk_huff8_t * const hi8, const size_t limit) -{ - int bit, value; - assert(t); - assert(bs); - assert(low8); - assert(hi8); - - /* Make sure we aren't running out of bounds */ - if (t->size >= limit) { - fputs("libsmacker::_smk_huff16_build_rec() - ERROR: size exceeded\n", stderr); - return 0; - } - - /* Read the first bit */ - if ((bit = smk_bs_read_1(bs)) < 0) { - fputs("libsmacker::_smk_huff16_build_rec() - ERROR: get_bit returned -1\n", stderr); - return 0; - } - - if (bit) { - /* See tree-in-array explanation for HUFF8 above */ - /* track the current index */ - value = t->size ++; - - /* go build the left branch */ - if (! _smk_huff16_build_rec(t, bs, low8, hi8, limit)) { - fputs("libsmacker::_smk_huff16_build_rec() - ERROR: failed to build left sub-tree\n", stderr); - return 0; - } - - /* now go back to our current location, and - mark our location as a "jump" */ - t->tree[value] = SMK_HUFF16_BRANCH | t->size; - - /* continue building the right side */ - if (! _smk_huff16_build_rec(t, bs, low8, hi8, limit)) { - fputs("libsmacker::_smk_huff16_build_rec() - ERROR: failed to build right sub-tree\n", stderr); - return 0; - } - } else { - /* Bit unset signifies a Leaf node. */ - /* Attempt to read LOW value */ - if ((value = smk_huff8_lookup(low8, bs)) < 0) { - fputs("libsmacker::_smk_huff16_build_rec() - ERROR: get LOW value returned -1\n", stderr); - return 0; - } - - t->tree[t->size] = value; - - /* now read HIGH value */ - if ((value = smk_huff8_lookup(hi8, bs)) < 0) { - fputs("libsmacker::_smk_huff16_build_rec() - ERROR: get HIGH value returned -1\n", stderr); - return 0; - } - - /* Looks OK: we got low and hi values. Return a new LEAF */ - t->tree[t->size] |= (value << 8); - - /* Last: when building the tree, some Values may correspond to cache positions. - Identify these values and set the Escape code byte accordingly. */ - if (t->tree[t->size] == t->cache[0]) - t->tree[t->size] = SMK_HUFF16_CACHE; - else if (t->tree[t->size] == t->cache[1]) - t->tree[t->size] = SMK_HUFF16_CACHE | 1; - else if (t->tree[t->size] == t->cache[2]) - t->tree[t->size] = SMK_HUFF16_CACHE | 2; - - t->size ++; - } - - return 1; -} - -/* Entry point for building a big 16-bit tree. */ -static int smk_huff16_build(struct smk_huff16_t * const t, struct smk_bit_t * const bs, const unsigned int alloc_size) -{ - struct smk_huff8_t low8, hi8; - size_t limit; - int value, i, bit; - /* null check */ - assert(t); - assert(bs); - - /* Smacker huff trees begin with a set-bit. */ - if ((bit = smk_bs_read_1(bs)) < 0) { - fputs("libsmacker::smk_huff16_build() - ERROR: initial get_bit returned -1\n", stderr); - return 0; - } - - t->size = 0; - - /* First bit indicates whether a tree is present or not. */ - /* Very small or audio-only files may have no tree. */ - if (bit) { - /* build low-8-bits tree */ - if (! smk_huff8_build(&low8, bs)) { - fputs("libsmacker::smk_huff16_build() - ERROR: failed to build LOW tree\n", stderr); - return 0; - } - - /* build hi-8-bits tree */ - if (! smk_huff8_build(&hi8, bs)) { - fputs("libsmacker::smk_huff16_build() - ERROR: failed to build HIGH tree\n", stderr); - return 0; - } - - /* Init the escape code cache. */ - for (i = 0; i < 3; i ++) { - if ((value = smk_bs_read_8(bs)) < 0) { - fprintf(stderr, "libsmacker::smk_huff16_build() - ERROR: get LOW value for cache %d returned -1\n", i); - return 0; - } - - t->cache[i] = value; - - /* now read HIGH value */ - if ((value = smk_bs_read_8(bs)) < 0) { - fprintf(stderr, "libsmacker::smk_huff16_build() - ERROR: get HIGH value for cache %d returned -1\n", i); - return 0; - } - - t->cache[i] |= (value << 8); - } - - /* Everything looks OK so far. Time to malloc structure. */ - if (alloc_size < 12 || alloc_size % 4) { - fprintf(stderr, "libsmacker::smk_huff16_build() - ERROR: illegal value %u for alloc_size\n", alloc_size); - return 0; - } - - limit = (alloc_size - 12) / 4; - - if ((t->tree = malloc(limit * sizeof(unsigned int))) == NULL) { - perror("libsmacker::smk_huff16_build() - ERROR: failed to malloc() huff16 tree"); - return 0; - } - - /* Finally, call recursive function to retrieve the Bigtree. */ - if (! _smk_huff16_build_rec(t, bs, &low8, &hi8, limit)) { - fputs("libsmacker::smk_huff16_build() - ERROR: failed to build huff16 tree\n", stderr); - free(t->tree); - t->tree = NULL; - return 0; - } - - /* check that we completely filled the tree */ - if (limit != t->size) { - fputs("libsmacker::smk_huff16_build() - ERROR: failed to completely decode huff16 tree\n", stderr); - free(t->tree); - t->tree = NULL; - return 0; - } - } else { - if ((t->tree = malloc(sizeof(unsigned int))) == NULL) { - perror("libsmacker::smk_huff16_build() - ERROR: failed to malloc() huff16 tree"); - return 0; - } - - t->tree[0] = 0; - //t->cache[0] = t->cache[1] = t->cache[2] = 0; - } - - /* Check final end tag. */ - if ((bit = smk_bs_read_1(bs)) < 0) { - fputs("libsmacker::smk_huff16_build() - ERROR: final get_bit returned -1\n", stderr); - free(t->tree); - t->tree = NULL; - return 0; - } - - /* a 0 is expected here, a 1 generally indicates a problem! */ - if (bit) { - fputs("libsmacker::smk_huff16_build() - ERROR: final get_bit returned 1\n", stderr); - free(t->tree); - t->tree = NULL; - return 0; - } - - return 1; -} - -/* Look up a 16-bit value from a large huff tree. - Return -1 on error. - Note that this also updates the recently-used-values cache. */ -static int smk_huff16_lookup(struct smk_huff16_t * const t, struct smk_bit_t * const bs) -{ - int bit, value, index = 0; - /* null check */ - assert(t); - assert(bs); - - while (t->tree[index] & SMK_HUFF16_BRANCH) { - if ((bit = smk_bs_read_1(bs)) < 0) { - fputs("libsmacker::smk_huff16_lookup() - ERROR: get_bit returned -1\n", stderr); - return -1; - } - - if (bit) { - /* take the right branch */ - index = t->tree[index] & SMK_HUFF16_LEAF_MASK; - } else { - /* take the left branch */ - index ++; - } - } - - /* Get the value at this point */ - value = t->tree[index]; - - if (value & SMK_HUFF16_CACHE) { - /* uses cached value instead of actual value */ - value = t->cache[value & SMK_HUFF16_LEAF_MASK]; - } - - if (t->cache[0] != value) { - /* Update the cache, by moving val to the front of the queue, - if it isn't already there. */ - t->cache[2] = t->cache[1]; - t->cache[1] = t->cache[0]; - t->cache[0] = value; - } - - return value; -} - -/* ************************************************************************* */ -/* SMACKER Structure */ -/* ************************************************************************* */ -/* tree processing order */ -#define SMK_TREE_MMAP 0 -#define SMK_TREE_MCLR 1 -#define SMK_TREE_FULL 2 -#define SMK_TREE_TYPE 3 - -struct smk_t { - /* meta-info */ - /* file mode: see flags, smacker.h */ - unsigned char mode; - - /* microsec per frame - stored as a double to handle scaling - (large positive millisec / frame values may overflow a ul) */ - double usf; - - /* total frames */ - unsigned long f; - /* does file have a ring frame? (in other words, does file loop?) */ - unsigned char ring_frame; - - /* Index of current frame */ - unsigned long cur_frame; - - /* SOURCE union. - Where the data is going to be read from (or be stored), - depending on the file mode. */ - union { - struct { - /* on-disk mode */ - FILE * fp; - unsigned long * chunk_offset; - } file; - - /* in-memory mode: unprocessed chunks */ - unsigned char ** chunk_data; - } source; - - /* shared array of "chunk sizes"*/ - unsigned long * chunk_size; - - /* Holds per-frame flags (i.e. 'keyframe') */ - unsigned char * keyframe; - /* Holds per-frame type mask (e.g. 'audio track 3, 2, and palette swap') */ - unsigned char * frame_type; - - /* video and audio structures */ - /* Video data type: enable/disable decode switch, - video info and flags, - pointer to last-decoded-palette */ - struct smk_video_t { - /* enable/disable decode switch */ - unsigned char enable; - - /* video info */ - unsigned long w; - unsigned long h; - /* Y scale mode (constants defined in smacker.h) - 0: unscaled - 1: doubled - 2: interlaced */ - unsigned char y_scale_mode; - - /* version ('2' or '4') */ - unsigned char v; - - /* Huffman trees */ - unsigned long tree_size[4]; - struct smk_huff16_t tree[4]; - - /* Palette data type: pointer to last-decoded-palette */ - unsigned char palette[256][3]; - /* Last-unpacked frame */ - unsigned char * frame; - } video; - - /* audio structure */ - struct smk_audio_t { - /* set if track exists in file */ - unsigned char exists; - - /* enable/disable switch (per track) */ - unsigned char enable; - - /* Info */ - unsigned char channels; - unsigned char bitdepth; - unsigned long rate; - long max_buffer; - - /* compression type - 0: raw PCM - 1: SMK DPCM - 2: Bink (Perceptual), unsupported */ - unsigned char compress; - - /* pointer to last-decoded-audio-buffer */ - void * buffer; - unsigned long buffer_size; - } audio[7]; -}; - -union smk_read_t { - FILE * file; - unsigned char * ram; -}; - -/* ************************************************************************* */ -/* SMACKER Functions */ -/* ************************************************************************* */ -/* An fread wrapper: consumes N bytes, or returns -1 - on failure (when size doesn't match expected) */ -static char smk_read_file(void * buf, const size_t size, FILE * fp) -{ - /* don't bother checking buf or fp, fread does it for us */ - size_t bytesRead = fread(buf, 1, size, fp); - - if (bytesRead != size) { - fprintf(stderr, "libsmacker::smk_read_file(buf,%lu,fp) - ERROR: Short read, %lu bytes returned\n", (unsigned long)size, (unsigned long)bytesRead); - perror("\tReason"); - return -1; - } - - return 0; -} - -/* A memcpy wrapper: consumes N bytes, or returns -1 - on failure (when size too low) */ -static char smk_read_memory(void * buf, const unsigned long size, unsigned char ** p, unsigned long * p_size) -{ - if (size > *p_size) { - fprintf(stderr, "libsmacker::smk_read_memory(buf,%lu,p,%lu) - ERROR: Short read\n", (unsigned long)size, (unsigned long)*p_size); - return -1; - } - - memcpy(buf, *p, size); - *p += size; - *p_size -= size; - return 0; -} - -/* Helper functions to do the reading, plus - byteswap from LE to host order */ -/* read n bytes from (source) into ret */ -#define smk_read(ret,n) \ -{ \ - if (m) \ - { \ - r = (smk_read_file(ret,n,fp.file)); \ - } \ - else \ - { \ - r = (smk_read_memory(ret,n,&fp.ram,&size)); \ - } \ - if (r < 0) \ - { \ - fprintf(stderr,"libsmacker::smk_read(...) - Errors encountered on read, bailing out (file: %s, line: %lu)\n", __FILE__, (unsigned long)__LINE__); \ - goto error; \ - } \ -} - -/* Calls smk_read, but returns a ul */ -#define smk_read_ul(p) \ -{ \ - smk_read(buf,4); \ - p = ((unsigned long) buf[3] << 24) | \ - ((unsigned long) buf[2] << 16) | \ - ((unsigned long) buf[1] << 8) | \ - ((unsigned long) buf[0]); \ -} - -/* PUBLIC FUNCTIONS */ -/* open an smk (from a generic Source) */ -static smk smk_open_generic(const unsigned char m, union smk_read_t fp, unsigned long size, const unsigned char process_mode) -{ - /* Smacker structure we intend to work on / return */ - smk s; - /* Temporary variables */ - long temp_l; - unsigned long temp_u; - /* r is used by macros above for return code */ - char r; - unsigned char buf[4] = {'\0'}; - /* video hufftrees are stored as a large chunk (bitstream) - these vars are used to load, then decode them */ - unsigned char * hufftree_chunk = NULL; - unsigned long tree_size; - /* a bitstream struct */ - struct smk_bit_t bs; - - /** **/ - /* safe malloc the structure */ - if ((s = calloc(1, sizeof(struct smk_t))) == NULL) { - perror("libsmacker::smk_open_generic() - ERROR: failed to malloc() smk structure"); - return NULL; - } - - /* Check for a valid signature */ - smk_read(buf, 3); - - if (buf[0] != 'S' || buf[1] != 'M' || buf[2] != 'K') { - fprintf(stderr, "libsmacker::smk_open_generic - ERROR: invalid SMKn signature (got: %s)\n", buf); - goto error; - } - - /* Read .smk file version */ - smk_read(&s->video.v, 1); - - if (s->video.v != '2' && s->video.v != '4') { - fprintf(stderr, "libsmacker::smk_open_generic - Warning: invalid SMK version %c (expected: 2 or 4)\n", s->video.v); - - /* take a guess */ - if (s->video.v < '4') - s->video.v = '2'; - else - s->video.v = '4'; - - fprintf(stderr, "\tProcessing will continue as type %c\n", s->video.v); - } - - /* width, height, total num frames */ - smk_read_ul(s->video.w); - smk_read_ul(s->video.h); - smk_read_ul(s->f); - /* frames per second calculation */ - smk_read_ul(temp_u); - temp_l = (int)temp_u; - - if (temp_l > 0) { - /* millisec per frame */ - s->usf = temp_l * 1000; - } else if (temp_l < 0) { - /* 10 microsec per frame */ - s->usf = temp_l * -10; - } else { - /* defaults to 10 usf (= 100000 microseconds) */ - s->usf = 100000; - } - - /* Video flags follow. - Ring frame is important to libsmacker. - Y scale / Y interlace go in the Video flags. - The user should scale appropriately. */ - smk_read_ul(temp_u); - - if (temp_u & 0x01) - s->ring_frame = 1; - - if (temp_u & 0x02) - s->video.y_scale_mode = SMK_FLAG_Y_DOUBLE; - - if (temp_u & 0x04) { - if (s->video.y_scale_mode == SMK_FLAG_Y_DOUBLE) - fputs("libsmacker::smk_open_generic - Warning: SMK file specifies both Y-Double AND Y-Interlace.\n", stderr); - - s->video.y_scale_mode = SMK_FLAG_Y_INTERLACE; - } - - /* Max buffer size for each audio track - used to pre-allocate buffers */ - for (temp_l = 0; temp_l < 7; temp_l ++) - smk_read_ul(s->audio[temp_l].max_buffer); - - /* Read size of "hufftree chunk" - save for later. */ - smk_read_ul(tree_size); - - /* "unpacked" sizes of each huff tree */ - for (temp_l = 0; temp_l < 4; temp_l ++) - smk_read_ul(s->video.tree_size[temp_l]); - - /* read audio rate data */ - for (temp_l = 0; temp_l < 7; temp_l ++) { - smk_read_ul(temp_u); - - if (temp_u & 0x40000000) { - /* Audio track specifies "exists" flag, malloc structure and copy components. */ - s->audio[temp_l].exists = 1; - /* and for all audio tracks */ - smk_malloc(s->audio[temp_l].buffer, s->audio[temp_l].max_buffer); - - if (temp_u & 0x80000000) - s->audio[temp_l].compress = 1; - - s->audio[temp_l].bitdepth = ((temp_u & 0x20000000) ? 16 : 8); - s->audio[temp_l].channels = ((temp_u & 0x10000000) ? 2 : 1); - - if (temp_u & 0x0c000000) { - fprintf(stderr, "libsmacker::smk_open_generic - Warning: audio track %ld is compressed with Bink (perceptual) Audio Codec: this is currently unsupported by libsmacker\n", temp_l); - s->audio[temp_l].compress = 2; - } - - /* Bits 25 & 24 are unused. */ - s->audio[temp_l].rate = (temp_u & 0x00FFFFFF); - } - } - - /* Skip over Dummy field */ - smk_read_ul(temp_u); - /* FrameSizes and Keyframe marker are stored together. */ - smk_malloc(s->keyframe, (s->f + s->ring_frame)); - smk_malloc(s->chunk_size, (s->f + s->ring_frame) * sizeof(unsigned long)); - - for (temp_u = 0; temp_u < (s->f + s->ring_frame); temp_u ++) { - smk_read_ul(s->chunk_size[temp_u]); - - /* Set Keyframe */ - if (s->chunk_size[temp_u] & 0x01) - s->keyframe[temp_u] = 1; - - /* Bits 1 is used, but the purpose is unknown. */ - s->chunk_size[temp_u] &= 0xFFFFFFFC; - } - - /* That was easy... Now read FrameTypes! */ - smk_malloc(s->frame_type, (s->f + s->ring_frame)); - - for (temp_u = 0; temp_u < (s->f + s->ring_frame); temp_u ++) - smk_read(&s->frame_type[temp_u], 1); - - /* HuffmanTrees - We know the sizes already: read and assemble into - something actually parse-able at run-time */ - smk_malloc(hufftree_chunk, tree_size); - smk_read(hufftree_chunk, tree_size); - /* set up a Bitstream */ - smk_bs_init(&bs, hufftree_chunk, tree_size); - - /* create some tables */ - for (temp_u = 0; temp_u < 4; temp_u ++) { - if (! smk_huff16_build(&s->video.tree[temp_u], &bs, s->video.tree_size[temp_u])) { - fprintf(stderr, "libsmacker::smk_open_generic - ERROR: failed to create huff16 tree %lu\n", temp_u); - goto error; - } - } - - /* clean up */ - smk_free(hufftree_chunk); - /* Go ahead and malloc storage for the video frame */ - smk_malloc(s->video.frame, s->video.w * s->video.h); - /* final processing: depending on ProcessMode, handle what to do with rest of file data */ - s->mode = process_mode; - - /* Handle the rest of the data. - For MODE_MEMORY, read the chunks and store */ - if (s->mode == SMK_MODE_MEMORY) { - smk_malloc(s->source.chunk_data, (s->f + s->ring_frame) * sizeof(unsigned char *)); - - for (temp_u = 0; temp_u < (s->f + s->ring_frame); temp_u ++) { - smk_malloc(s->source.chunk_data[temp_u], s->chunk_size[temp_u]); - smk_read(s->source.chunk_data[temp_u], s->chunk_size[temp_u]); - } - } else { - /* MODE_STREAM: don't read anything now, just precompute offsets. - use fseek to verify that the file is "complete" */ - smk_malloc(s->source.file.chunk_offset, (s->f + s->ring_frame) * sizeof(unsigned long)); - - for (temp_u = 0; temp_u < (s->f + s->ring_frame); temp_u ++) { - s->source.file.chunk_offset[temp_u] = ftell(fp.file); - - if (fseek(fp.file, s->chunk_size[temp_u], SEEK_CUR)) { - fprintf(stderr, "libsmacker::smk_open - ERROR: fseek to frame %lu not OK.\n", temp_u); - perror("\tError reported was"); - goto error; - } - } - } - - return s; -error: - smk_free(hufftree_chunk); - smk_close(s); - return NULL; -} - -/* open an smk (from a memory buffer) */ -smk smk_open_memory(const unsigned char * buffer, const unsigned long size) -{ - smk s = NULL; - union smk_read_t fp; - - if (buffer == NULL) { - fputs("libsmacker::smk_open_memory() - ERROR: buffer pointer is NULL\n", stderr); - return NULL; - } - - /* set up the read union for Memory mode */ - fp.ram = (unsigned char *)buffer; - - if (!(s = smk_open_generic(0, fp, size, SMK_MODE_MEMORY))) - fprintf(stderr, "libsmacker::smk_open_memory(buffer,%lu) - ERROR: Fatal error in smk_open_generic, returning NULL.\n", size); - - return s; -} - -/* open an smk (from a file) */ -smk smk_open_filepointer(FILE * file, const unsigned char mode) -{ - smk s = NULL; - union smk_read_t fp; - - if (file == NULL) { - fputs("libsmacker::smk_open_filepointer() - ERROR: file pointer is NULL\n", stderr); - return NULL; - } - - /* Copy file ptr to internal union */ - fp.file = file; - - if (!(s = smk_open_generic(1, fp, 0, mode))) { - fprintf(stderr, "libsmacker::smk_open_filepointer(file,%u) - ERROR: Fatal error in smk_open_generic, returning NULL.\n", mode); - fclose(fp.file); - goto error; - } - - if (mode == SMK_MODE_MEMORY) - fclose(fp.file); - else - s->source.file.fp = fp.file; - - /* fall through, return s or null */ -error: - return s; -} - -/* open an smk (from a file) */ -smk smk_open_file(const char * filename, const unsigned char mode) -{ - FILE * fp; - - if (filename == NULL) { - fputs("libsmacker::smk_open_file() - ERROR: filename is NULL\n", stderr); - return NULL; - } - - if (!(fp = fopen(filename, "rb"))) { - fprintf(stderr, "libsmacker::smk_open_file(%s,%u) - ERROR: could not open file\n", filename, mode); - perror("\tError reported was"); - goto error; - } - - /* kick processing to smk_open_filepointer */ - return smk_open_filepointer(fp, mode); - /* fall through, return s or null */ -error: - return NULL; -} - -/* close out an smk file and clean up memory */ -void smk_close(smk s) -{ - unsigned long u; - - if (s == NULL) { - fputs("libsmacker::smk_close() - ERROR: smk is NULL\n", stderr); - return; - } - - /* free video sub-components */ - for (u = 0; u < 4; u ++) { - if (s->video.tree[u].tree) free(s->video.tree[u].tree); - } - - smk_free(s->video.frame); - - /* free audio sub-components */ - for (u = 0; u < 7; u++) { - if (s->audio[u].buffer) - smk_free(s->audio[u].buffer); - } - - smk_free(s->keyframe); - smk_free(s->frame_type); - - if (s->mode == SMK_MODE_DISK) { - /* disk-mode */ - if (s->source.file.fp) - fclose(s->source.file.fp); - - smk_free(s->source.file.chunk_offset); - } else { - /* mem-mode */ - if (s->source.chunk_data != NULL) { - for (u = 0; u < (s->f + s->ring_frame); u++) - smk_free(s->source.chunk_data[u]); - - smk_free(s->source.chunk_data); - } - } - - smk_free(s->chunk_size); - smk_free(s); -} - -/* tell some info about the file */ -char smk_info_all(const smk object, unsigned long * frame, unsigned long * frame_count, double * usf) -{ - /* null check */ - if (object == NULL) { - fputs("libsmacker::smk_info_all() - ERROR: smk is NULL\n", stderr); - return -1; - } - - if (!frame && !frame_count && !usf) { - fputs("libsmacker::smk_info_all(object,frame,frame_count,usf) - ERROR: Request for info with all-NULL return references\n", stderr); - goto error; - } - - if (frame) - *frame = (object->cur_frame % object->f); - - if (frame_count) - *frame_count = object->f; - - if (usf) - *usf = object->usf; - - return 0; -error: - return -1; -} - -char smk_info_video(const smk object, unsigned long * w, unsigned long * h, unsigned char * y_scale_mode) -{ - /* null check */ - if (object == NULL) { - fputs("libsmacker::smk_info_video() - ERROR: smk is NULL\n", stderr); - return -1; - } - - if (!w && !h && !y_scale_mode) { - fputs("libsmacker::smk_info_all(object,w,h,y_scale_mode) - ERROR: Request for info with all-NULL return references\n", stderr); - return -1; - } - - if (w) - *w = object->video.w; - - if (h) - *h = object->video.h; - - if (y_scale_mode) - *y_scale_mode = object->video.y_scale_mode; - - return 0; -} - -char smk_info_audio(const smk object, unsigned char * track_mask, unsigned char channels[7], unsigned char bitdepth[7], unsigned long audio_rate[7]) -{ - unsigned char i; - - /* null check */ - if (object == NULL) { - fputs("libsmacker::smk_info_audio() - ERROR: smk is NULL\n", stderr); - return -1; - } - - if (!track_mask && !channels && !bitdepth && !audio_rate) { - fputs("libsmacker::smk_info_audio(object,track_mask,channels,bitdepth,audio_rate) - ERROR: Request for info with all-NULL return references\n", stderr); - return -1; - } - - if (track_mask) { - *track_mask = ((object->audio[0].exists) | - ((object->audio[1].exists) << 1) | - ((object->audio[2].exists) << 2) | - ((object->audio[3].exists) << 3) | - ((object->audio[4].exists) << 4) | - ((object->audio[5].exists) << 5) | - ((object->audio[6].exists) << 6)); - } - - if (channels) { - for (i = 0; i < 7; i ++) - channels[i] = object->audio[i].channels; - } - - if (bitdepth) { - for (i = 0; i < 7; i ++) - bitdepth[i] = object->audio[i].bitdepth; - } - - if (audio_rate) { - for (i = 0; i < 7; i ++) - audio_rate[i] = object->audio[i].rate; - } - - return 0; -} - -/* Enable-disable switches */ -char smk_enable_all(smk object, const unsigned char mask) -{ - unsigned char i; - - /* null check */ - if (object == NULL) { - fputs("libsmacker::smk_enable_all() - ERROR: smk is NULL\n", stderr); - return -1; - } - - /* set video-enable */ - object->video.enable = (mask & 0x80); - - for (i = 0; i < 7; i ++) { - if (object->audio[i].exists) - object->audio[i].enable = (mask & (1 << i)); - } - - return 0; -} - -char smk_enable_video(smk object, const unsigned char enable) -{ - /* null check */ - if (object == NULL) { - fputs("libsmacker::smk_enable_video() - ERROR: smk is NULL\n", stderr); - return -1; - } - - object->video.enable = enable; - return 0; -} - -char smk_enable_audio(smk object, const unsigned char track, const unsigned char enable) -{ - /* null check */ - if (object == NULL) { - fputs("libsmacker::smk_enable_audio() - ERROR: smk is NULL\n", stderr); - return -1; - } - - object->audio[track].enable = enable; - return 0; -} - -const unsigned char * smk_get_palette(const smk object) -{ - /* null check */ - if (object == NULL) { - fputs("libsmacker::smk_get_palette() - ERROR: smk is NULL\n", stderr); - return NULL; - } - - return (unsigned char *)object->video.palette; -} -const unsigned char * smk_get_video(const smk object) -{ - /* null check */ - if (object == NULL) { - fputs("libsmacker::smk_get_video() - ERROR: smk is NULL\n", stderr); - return NULL; - } - - return object->video.frame; -} -const unsigned char * smk_get_audio(const smk object, const unsigned char t) -{ - /* null check */ - if (object == NULL) { - fputs("libsmacker::smk_get_audio() - ERROR: smk is NULL\n", stderr); - return NULL; - } - - return object->audio[t].buffer; -} -unsigned long smk_get_audio_size(const smk object, const unsigned char t) -{ - /* null check */ - if (object == NULL) { - fputs("libsmacker::smk_get_audio_size() - ERROR: smk is NULL\n", stderr); - return 0; - } - - return object->audio[t].buffer_size; -} - -/* Decompresses a palette-frame. */ -static char smk_render_palette(struct smk_video_t * s, unsigned char * p, unsigned long size) -{ - /* Index into palette */ - unsigned short i = 0; - /* Helper variables */ - unsigned short count, src; - static unsigned char oldPalette[256][3]; - /* Smacker palette map: smk colors are 6-bit, this table expands them to 8. */ - const unsigned char palmap[64] = { - 0x00, 0x04, 0x08, 0x0C, 0x10, 0x14, 0x18, 0x1C, - 0x20, 0x24, 0x28, 0x2C, 0x30, 0x34, 0x38, 0x3C, - 0x41, 0x45, 0x49, 0x4D, 0x51, 0x55, 0x59, 0x5D, - 0x61, 0x65, 0x69, 0x6D, 0x71, 0x75, 0x79, 0x7D, - 0x82, 0x86, 0x8A, 0x8E, 0x92, 0x96, 0x9A, 0x9E, - 0xA2, 0xA6, 0xAA, 0xAE, 0xB2, 0xB6, 0xBA, 0xBE, - 0xC3, 0xC7, 0xCB, 0xCF, 0xD3, 0xD7, 0xDB, 0xDF, - 0xE3, 0xE7, 0xEB, 0xEF, 0xF3, 0xF7, 0xFB, 0xFF - }; - /* null check */ - assert(s); - assert(p); - /* Copy palette to old palette */ - memcpy(oldPalette, s->palette, 256 * 3); - - /* Loop until palette is complete, or we are out of bytes to process */ - while ((i < 256) && (size > 0)) { - if ((*p) & 0x80) { - /* 0x80: Skip block - (preserve C+1 palette entries from previous palette) */ - count = ((*p) & 0x7F) + 1; - p ++; - size --; - - /* check for overflow condition */ - if (i + count > 256) { - fprintf(stderr, "libsmacker::palette_render(s,p,size) - ERROR: overflow, 0x80 attempt to skip %d entries from %d\n", count, i); - goto error; - } - - /* finally: advance the index. */ - i += count; - } else if ((*p) & 0x40) { - /* 0x40: Color-shift block - Copy (c + 1) color entries of the previous palette, - starting from entry (s), - to the next entries of the new palette. */ - if (size < 2) { - fputs("libsmacker::palette_render(s,p,size) - ERROR: 0x40 ran out of bytes for copy\n", stderr); - goto error; - } - - /* pick "count" items to copy */ - count = ((*p) & 0x3F) + 1; - p ++; - size --; - /* start offset of old palette */ - src = *p; - p ++; - size --; - - /* overflow: see if we write/read beyond 256colors, or overwrite own palette */ - if (i + count > 256 || src + count > 256) { - fprintf(stderr, "libsmacker::palette_render(s,p,size) - ERROR: overflow, 0x40 attempt to copy %d entries from %d to %d\n", count, src, i); - goto error; - } - - /* OK! Copy the color-palette entries. */ - memmove(&s->palette[i][0], &oldPalette[src][0], count * 3); - i += count; - } else { - /* 0x00: Set Color block - Direct-set the next 3 bytes for palette index */ - if (size < 3) { - fprintf(stderr, "libsmacker::palette_render - ERROR: 0x3F ran out of bytes for copy, size=%lu\n", size); - goto error; - } - - for (count = 0; count < 3; count ++) { - if (*p > 0x3F) { - fprintf(stderr, "libsmacker::palette_render - ERROR: palette index exceeds 0x3F (entry [%u][%u])\n", i, count); - goto error; - } - - s->palette[i][count] = palmap[*p]; - p++; - size --; - } - - i ++; - } - } - - if (i < 256) { - fprintf(stderr, "libsmacker::palette_render - ERROR: did not completely fill palette (idx=%u)\n", i); - goto error; - } - - return 0; -error: - /* Error, return -1 - The new palette probably has errors but is preferrable to a black screen */ - return -1; -} - -static char smk_render_video(struct smk_video_t * s, unsigned char * p, unsigned int size) -{ - unsigned char * t = s->frame; - unsigned char s1, s2; - unsigned short temp; - unsigned long i, j, k, row, col, skip; - /* used for video decoding */ - struct smk_bit_t bs; - /* results from a tree lookup */ - int unpack; - /* unpack, broken into pieces */ - unsigned char type; - unsigned char blocklen; - unsigned char typedata; - char bit; - const unsigned short sizetable[64] = { - 1, 2, 3, 4, 5, 6, 7, 8, - 9, 10, 11, 12, 13, 14, 15, 16, - 17, 18, 19, 20, 21, 22, 23, 24, - 25, 26, 27, 28, 29, 30, 31, 32, - 33, 34, 35, 36, 37, 38, 39, 40, - 41, 42, 43, 44, 45, 46, 47, 48, - 49, 50, 51, 52, 53, 54, 55, 56, - 57, 58, 59, 128, 256, 512, 1024, 2048 - }; - /* null check */ - assert(s); - assert(p); - row = 0; - col = 0; - /* Set up a bitstream for video unpacking */ - smk_bs_init(&bs, p, size); - - /* Reset the cache on all bigtrees */ - for (i = 0; i < 4; i++) - memset(&s->tree[i].cache, 0, 3 * sizeof(unsigned short)); - - while (row < s->h) { - if ((unpack = smk_huff16_lookup(&s->tree[SMK_TREE_TYPE], &bs)) < 0) { - fputs("libsmacker::smk_render_video() - ERROR: failed to lookup from TYPE tree.\n", stderr); - return -1; - } - - type = ((unpack & 0x0003)); - blocklen = ((unpack & 0x00FC) >> 2); - typedata = ((unpack & 0xFF00) >> 8); - - /* support for v4 full-blocks */ - if (type == 1 && s->v == '4') { - bit = smk_bs_read_1(&bs); - - if (bit) - type = 4; - else { - bit = smk_bs_read_1(&bs); - - if (bit) - type = 5; - } - } - - for (j = 0; (j < sizetable[blocklen]) && (row < s->h); j ++) { - skip = (row * s->w) + col; - - switch (type) { - case 0: - if ((unpack = smk_huff16_lookup(&s->tree[SMK_TREE_MCLR], &bs)) < 0) { - fputs("libsmacker::smk_render_video() - ERROR: failed to lookup from MCLR tree.\n", stderr); - return -1; - } - - s1 = (unpack & 0xFF00) >> 8; - s2 = (unpack & 0x00FF); - - if ((unpack = smk_huff16_lookup(&s->tree[SMK_TREE_MMAP], &bs)) < 0) { - fputs("libsmacker::smk_render_video() - ERROR: failed to lookup from MMAP tree.\n", stderr); - return -1; - } - - temp = 0x01; - - for (k = 0; k < 4; k ++) { - for (i = 0; i < 4; i ++) { - if (unpack & temp) - t[skip + i] = s1; - else - t[skip + i] = s2; - - temp = temp << 1; - } - - skip += s->w; - } - - break; - - case 1: /* FULL BLOCK */ - for (k = 0; k < 4; k ++) { - if ((unpack = smk_huff16_lookup(&s->tree[SMK_TREE_FULL], &bs)) < 0) { - fputs("libsmacker::smk_render_video() - ERROR: failed to lookup from FULL tree.\n", stderr); - return -1; - } - - t[skip + 3] = ((unpack & 0xFF00) >> 8); - t[skip + 2] = (unpack & 0x00FF); - - if ((unpack = smk_huff16_lookup(&s->tree[SMK_TREE_FULL], &bs)) < 0) { - fputs("libsmacker::smk_render_video() - ERROR: failed to lookup from FULL tree.\n", stderr); - return -1; - } - - t[skip + 1] = ((unpack & 0xFF00) >> 8); - t[skip] = (unpack & 0x00FF); - skip += s->w; - } - - break; - - case 2: /* VOID BLOCK */ - /* break; - if (s->frame) - { - memcpy(&t[skip], &s->frame[skip], 4); - skip += s->w; - memcpy(&t[skip], &s->frame[skip], 4); - skip += s->w; - memcpy(&t[skip], &s->frame[skip], 4); - skip += s->w; - memcpy(&t[skip], &s->frame[skip], 4); - } */ - break; - - case 3: /* SOLID BLOCK */ - memset(&t[skip], typedata, 4); - skip += s->w; - memset(&t[skip], typedata, 4); - skip += s->w; - memset(&t[skip], typedata, 4); - skip += s->w; - memset(&t[skip], typedata, 4); - break; - - case 4: /* V4 DOUBLE BLOCK */ - for (k = 0; k < 2; k ++) { - if ((unpack = smk_huff16_lookup(&s->tree[SMK_TREE_FULL], &bs)) < 0) { - fputs("libsmacker::smk_render_video() - ERROR: failed to lookup from FULL tree.\n", stderr); - return -1; - } - - for (i = 0; i < 2; i ++) { - memset(&t[skip + 2], (unpack & 0xFF00) >> 8, 2); - memset(&t[skip], (unpack & 0x00FF), 2); - skip += s->w; - } - } - - break; - - case 5: /* V4 HALF BLOCK */ - for (k = 0; k < 2; k ++) { - if ((unpack = smk_huff16_lookup(&s->tree[SMK_TREE_FULL], &bs)) < 0) { - fputs("libsmacker::smk_render_video() - ERROR: failed to lookup from FULL tree.\n", stderr); - return -1; - } - - t[skip + 3] = ((unpack & 0xFF00) >> 8); - t[skip + 2] = (unpack & 0x00FF); - t[skip + s->w + 3] = ((unpack & 0xFF00) >> 8); - t[skip + s->w + 2] = (unpack & 0x00FF); - - if ((unpack = smk_huff16_lookup(&s->tree[SMK_TREE_FULL], &bs)) < 0) { - fputs("libsmacker::smk_render_video() - ERROR: failed to lookup from FULL tree.\n", stderr); - return -1; - } - - t[skip + 1] = ((unpack & 0xFF00) >> 8); - t[skip] = (unpack & 0x00FF); - t[skip + s->w + 1] = ((unpack & 0xFF00) >> 8); - t[skip + s->w] = (unpack & 0x00FF); - skip += (s->w << 1); - } - - break; - } - - col += 4; - - if (col >= s->w) { - col = 0; - row += 4; - } - } - } - - return 0; -} - -/* Decompress audio track i. */ -static char smk_render_audio(struct smk_audio_t * s, unsigned char * p, unsigned long size) -{ - unsigned int j, k; - unsigned char * t = s->buffer; - struct smk_bit_t bs; - char bit; - short unpack, unpack2; - /* used for audio decoding */ - struct smk_huff8_t aud_tree[4]; - /* null check */ - assert(s); - assert(p); - - if (!s->compress) { - /* Raw PCM data, update buffer size and perform copy */ - s->buffer_size = size; - memcpy(t, p, size); - } else if (s->compress == 1) { - /* SMACKER DPCM compression */ - /* need at least 4 bytes to process */ - if (size < 4) { - fputs("libsmacker::smk_render_audio() - ERROR: need 4 bytes to get unpacked output buffer size.\n", stderr); - goto error; - } - - /* chunk is compressed (huff-compressed dpcm), retrieve unpacked buffer size */ - s->buffer_size = ((unsigned int) p[3] << 24) | - ((unsigned int) p[2] << 16) | - ((unsigned int) p[1] << 8) | - ((unsigned int) p[0]); - p += 4; - size -= 4; - /* Compressed audio: must unpack here */ - /* Set up a bitstream */ - smk_bs_init(&bs, p, size); - bit = smk_bs_read_1(&bs); - - if (!bit) { - fputs("libsmacker::smk_render_audio - ERROR: initial get_bit returned 0\n", stderr); - goto error; - } - - bit = smk_bs_read_1(&bs); - - if (s->channels != (bit == 1 ? 2 : 1)) - fputs("libsmacker::smk_render - ERROR: mono/stereo mismatch\n", stderr); - - bit = smk_bs_read_1(&bs); - - if (s->bitdepth != (bit == 1 ? 16 : 8)) - fputs("libsmacker::smk_render - ERROR: 8-/16-bit mismatch\n", stderr); - - /* build the trees */ - smk_huff8_build(&aud_tree[0], &bs); - j = 1; - k = 1; - - if (s->bitdepth == 16) { - smk_huff8_build(&aud_tree[1], &bs); - k = 2; - } - - if (s->channels == 2) { - smk_huff8_build(&aud_tree[2], &bs); - j = 2; - k = 2; - - if (s->bitdepth == 16) { - smk_huff8_build(&aud_tree[3], &bs); - k = 4; - } - } - - /* read initial sound level */ - if (s->channels == 2) { - unpack = smk_bs_read_8(&bs); - - if (s->bitdepth == 16) { - ((short *)t)[1] = smk_bs_read_8(&bs); - ((short *)t)[1] |= (unpack << 8); - } else - ((unsigned char *)t)[1] = (unsigned char)unpack; - } - - unpack = smk_bs_read_8(&bs); - - if (s->bitdepth == 16) { - ((short *)t)[0] = smk_bs_read_8(&bs); - ((short *)t)[0] |= (unpack << 8); - } else - ((unsigned char *)t)[0] = (unsigned char)unpack; - - /* All set: let's read some DATA! */ - while (k < s->buffer_size) { - if (s->bitdepth == 8) { - unpack = smk_huff8_lookup(&aud_tree[0], &bs); - ((unsigned char *)t)[j] = (char)unpack + ((unsigned char *)t)[j - s->channels]; - j ++; - k++; - } else { - unpack = smk_huff8_lookup(&aud_tree[0], &bs); - unpack2 = smk_huff8_lookup(&aud_tree[1], &bs); - ((short *)t)[j] = (short)(unpack | (unpack2 << 8)) + ((short *)t)[j - s->channels]; - j ++; - k += 2; - } - - if (s->channels == 2) { - if (s->bitdepth == 8) { - unpack = smk_huff8_lookup(&aud_tree[2], &bs); - ((unsigned char *)t)[j] = (char)unpack + ((unsigned char *)t)[j - 2]; - j ++; - k++; - } else { - unpack = smk_huff8_lookup(&aud_tree[2], &bs); - unpack2 = smk_huff8_lookup(&aud_tree[3], &bs); - ((short *)t)[j] = (short)(unpack | (unpack2 << 8)) + ((short *)t)[j - 2]; - j ++; - k += 2; - } - } - } - } - - return 0; -error: - return -1; -} - -/* "Renders" (unpacks) the frame at cur_frame - Preps all the image and audio pointers */ -static char smk_render(smk s) -{ - unsigned long i, size; - unsigned char * buffer = NULL, * p, track; - /* null check */ - assert(s); - - /* Retrieve current chunk_size for this frame. */ - if (!(i = s->chunk_size[s->cur_frame])) { - fprintf(stderr, "libsmacker::smk_render(s) - Warning: frame %lu: chunk_size is 0.\n", s->cur_frame); - goto error; - } - - if (s->mode == SMK_MODE_DISK) { - /* Skip to frame in file */ - if (fseek(s->source.file.fp, s->source.file.chunk_offset[s->cur_frame], SEEK_SET)) { - fprintf(stderr, "libsmacker::smk_render(s) - ERROR: fseek to frame %lu (offset %lu) failed.\n", s->cur_frame, s->source.file.chunk_offset[s->cur_frame]); - perror("\tError reported was"); - goto error; - } - - /* In disk-streaming mode: make way for our incoming chunk buffer */ - if ((buffer = malloc(i)) == NULL) { - perror("libsmacker::smk_render() - ERROR: failed to malloc() buffer"); - return -1; - } - - /* Read into buffer */ - if (smk_read_file(buffer, s->chunk_size[s->cur_frame], s->source.file.fp) < 0) { - fprintf(stderr, "libsmacker::smk_render(s) - ERROR: frame %lu (offset %lu): smk_read had errors.\n", s->cur_frame, s->source.file.chunk_offset[s->cur_frame]); - goto error; - } - } else { - /* Just point buffer at the right place */ - if (!s->source.chunk_data[s->cur_frame]) { - fprintf(stderr, "libsmacker::smk_render(s) - ERROR: frame %lu: memory chunk is a NULL pointer.\n", s->cur_frame); - goto error; - } - - buffer = s->source.chunk_data[s->cur_frame]; - } - - p = buffer; - - /* Palette record first */ - if (s->frame_type[s->cur_frame] & 0x01) { - /* need at least 1 byte to process */ - if (!i) { - fprintf(stderr, "libsmacker::smk_render(s) - ERROR: frame %lu: insufficient data for a palette rec.\n", s->cur_frame); - goto error; - } - - /* Byte 1 in block, times 4, tells how many - subsequent bytes are present */ - size = 4 * (*p); - - /* If video rendering enabled, kick this off for decode. */ - if (s->video.enable) - smk_render_palette(&(s->video), p + 1, size - 1); - - p += size; - i -= size; - } - - /* Unpack audio chunks */ - for (track = 0; track < 7; track ++) { - if (s->frame_type[s->cur_frame] & (0x02 << track)) { - /* need at least 4 byte to process */ - if (i < 4) { - fprintf(stderr, "libsmacker::smk_render(s) - ERROR: frame %lu: insufficient data for audio[%u] rec.\n", s->cur_frame, track); - goto error; - } - - /* First 4 bytes in block tell how many - subsequent bytes are present */ - size = (((unsigned int) p[3] << 24) | - ((unsigned int) p[2] << 16) | - ((unsigned int) p[1] << 8) | - ((unsigned int) p[0])); - - /* If audio rendering enabled, kick this off for decode. */ - if (s->audio[track].enable) - smk_render_audio(&s->audio[track], p + 4, size - 4); - - p += size; - i -= size; - } else - s->audio[track].buffer_size = 0; - } - - /* Unpack video chunk */ - if (s->video.enable) { - if (smk_render_video(&(s->video), p, i) < 0) { - fprintf(stderr, "libsmacker::smk_render(s) - ERROR: frame %lu: failed to render video.\n", s->cur_frame); - goto error; - } - } - - if (s->mode == SMK_MODE_DISK) { - /* Remember that buffer we allocated? Trash it */ - smk_free(buffer); - } - - return 0; -error: - - if (s->mode == SMK_MODE_DISK) { - /* Remember that buffer we allocated? Trash it */ - smk_free(buffer); - } - - return -1; -} - -/* rewind to first frame and unpack */ -char smk_first(smk s) -{ - /* null check */ - if (s == NULL) { - fputs("libsmacker::smk_first() - ERROR: smk is NULL\n", stderr); - return -1; - } - - s->cur_frame = 0; - - if (smk_render(s) < 0) { - fprintf(stderr, "libsmacker::smk_first(s) - Warning: frame %lu: smk_render returned errors.\n", s->cur_frame); - return -1; - } - - if (s->f == 1) return SMK_LAST; - - return SMK_MORE; -} - -/* advance to next frame */ -char smk_next(smk s) -{ - /* null check */ - if (s == NULL) { - fputs("libsmacker::smk_next() - ERROR: smk is NULL\n", stderr); - return -1; - } - - if (s->cur_frame + 1 < (s->f + s->ring_frame)) { - s->cur_frame ++; - - if (smk_render(s) < 0) { - fprintf(stderr, "libsmacker::smk_next(s) - Warning: frame %lu: smk_render returned errors.\n", s->cur_frame); - return -1; - } - - if (s->cur_frame + 1 == (s->f + s->ring_frame)) - return SMK_LAST; - - return SMK_MORE; - } else if (s->ring_frame) { - s->cur_frame = 1; - - if (smk_render(s) < 0) { - fprintf(stderr, "libsmacker::smk_next(s) - Warning: frame %lu: smk_render returned errors.\n", s->cur_frame); - return -1; - } - - if (s->cur_frame + 1 == (s->f + s->ring_frame)) - return SMK_LAST; - - return SMK_MORE; - } - - return SMK_DONE; -} - -/* seek to a keyframe in an smk */ -char smk_seek_keyframe(smk s, unsigned long f) -{ - /* null check */ - if (s == NULL) { - fputs("libsmacker::smk_seek_keyframe() - ERROR: smk is NULL\n", stderr); - return -1; - } - - /* rewind (or fast forward!) exactly to f */ - s->cur_frame = f; - - /* roll back to previous keyframe in stream, or 0 if no keyframes exist */ - while (s->cur_frame > 0 && !(s->keyframe[s->cur_frame])) - s->cur_frame --; - - /* render the frame: we're ready */ - if (smk_render(s) < 0) { - fprintf(stderr, "libsmacker::smk_seek_keyframe(s,%lu) - Warning: frame %lu: smk_render returned errors.\n", f, s->cur_frame); - return -1; - } - - return 0; -} - -unsigned char smk_palette_updated(smk s) -{ - return s->frame_type[s->cur_frame] & 0x01; -} diff --git a/3rdParty/libsmacker/smacker.h b/3rdParty/libsmacker/smacker.h deleted file mode 100644 index 860ed9acf..000000000 --- a/3rdParty/libsmacker/smacker.h +++ /dev/null @@ -1,106 +0,0 @@ -/** - libsmacker - A C library for decoding .smk Smacker Video files - Copyright (C) 2012-2020 Greg Kennedy - - libsmacker is a cross-platform C library which can be used for - decoding Smacker Video files produced by RAD Game Tools. - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation, either version 2.1 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with this program. If not, see . -*/ - -#ifndef SMACKER_H -#define SMACKER_H - -/* includes - needed for FILE* here */ -#include - -/** forward-declaration for an struct */ -typedef struct smk_t * smk; - -/** a few defines as return codes from smk_next() */ -#define SMK_DONE 0x00 -#define SMK_MORE 0x01 -#define SMK_LAST 0x02 -#define SMK_ERROR -1 - -/** file-processing mode, pass to smk_open_file */ -#define SMK_MODE_DISK 0x00 -#define SMK_MODE_MEMORY 0x01 - -/** Y-scale meanings */ -#define SMK_FLAG_Y_NONE 0x00 -#define SMK_FLAG_Y_INTERLACE 0x01 -#define SMK_FLAG_Y_DOUBLE 0x02 - -/** track mask and enable bits */ -#define SMK_AUDIO_TRACK_0 0x01 -#define SMK_AUDIO_TRACK_1 0x02 -#define SMK_AUDIO_TRACK_2 0x04 -#define SMK_AUDIO_TRACK_3 0x08 -#define SMK_AUDIO_TRACK_4 0x10 -#define SMK_AUDIO_TRACK_5 0x20 -#define SMK_AUDIO_TRACK_6 0x40 -#define SMK_VIDEO_TRACK 0x80 - -/* PUBLIC FUNCTIONS */ -#ifdef __cplusplus -extern "C" { -#endif - -/* OPEN OPERATIONS */ -/** open an smk (from a file) */ -smk smk_open_file(const char * filename, unsigned char mode); -/** open an smk (from a file pointer) */ -smk smk_open_filepointer(FILE * file, unsigned char mode); -/** read an smk (from a memory buffer) */ -smk smk_open_memory(const unsigned char * buffer, unsigned long size); - -/* CLOSE OPERATIONS */ -/** close out an smk file and clean up memory */ -void smk_close(smk object); - -/* GET FILE INFO OPERATIONS */ -char smk_info_all(const smk object, unsigned long * frame, unsigned long * frame_count, double * usf); -char smk_info_video(const smk object, unsigned long * w, unsigned long * h, unsigned char * y_scale_mode); -char smk_info_audio(const smk object, unsigned char * track_mask, unsigned char channels[7], unsigned char bitdepth[7], unsigned long audio_rate[7]); - -/* ENABLE/DISABLE Switches */ -char smk_enable_all(smk object, unsigned char mask); -char smk_enable_video(smk object, unsigned char enable); -char smk_enable_audio(smk object, unsigned char track, unsigned char enable); - -/** Retrieve palette */ -const unsigned char * smk_get_palette(const smk object); -/** Retrieve video frame, as a buffer of size w*h */ -const unsigned char * smk_get_video(const smk object); -/** Retrieve decoded audio chunk, track N */ -const unsigned char * smk_get_audio(const smk object, unsigned char track); -/** Get size of currently pointed decoded audio chunk, track N */ -unsigned long smk_get_audio_size(const smk object, unsigned char track); - -/** rewind to first frame and unpack */ -char smk_first(smk object); -/** advance to next frame and unpack */ -char smk_next(smk object); -/** seek to first keyframe before/at N in an smk */ -char smk_seek_keyframe(smk object, unsigned long frame); - -/** was the pallet updated this frame */ -unsigned char smk_palette_updated(smk s); - -#ifdef __cplusplus -} -#endif - -#endif diff --git a/3rdParty/libsmacker/smk_malloc.h b/3rdParty/libsmacker/smk_malloc.h deleted file mode 100644 index cf944e7f9..000000000 --- a/3rdParty/libsmacker/smk_malloc.h +++ /dev/null @@ -1,56 +0,0 @@ -/** - libsmacker - A C library for decoding .smk Smacker Video files - Copyright (C) 2012-2017 Greg Kennedy - - See smacker.h for more information. - - smk_malloc.h - "Safe" implementations of malloc and free. - Verbose implementation of assert. -*/ - -#ifndef SMK_MALLOC_H -#define SMK_MALLOC_H - -/* assert */ -#include -/* calloc */ -#include -/* fprintf */ -#include - -/* Error messages from calloc */ -#include -#include - -/** - Safe free: attempts to prevent double-free by setting pointer to NULL. - Optionally warns on attempts to free a NULL pointer. -*/ -#define smk_free(p) \ -{ \ - assert (p); \ - free(p); \ - p = NULL; \ -} - -/** - Safe malloc: exits if calloc() returns NULL. - Also initializes blocks to 0. - Optionally warns on attempts to malloc over an existing pointer. - Ideally, one should not exit() in a library. However, if you cannot - calloc(), you probably have bigger problems. -*/ -#define smk_malloc(p, x) \ -{ \ - assert (p == NULL); \ - p = calloc(1, x); \ - if (!p) \ - { \ - fprintf(stderr, "libsmacker::smk_malloc(" #p ", %lu) - ERROR: calloc() returned NULL (file: %s, line: %lu)\n\tReason: [%d] %s\n", \ - (unsigned long) (x), __FILE__, (unsigned long)__LINE__, errno, strerror(errno)); \ - exit(EXIT_FAILURE); \ - } \ -} - -#endif diff --git a/3rdParty/libsmackerdec/CMakeLists.txt b/3rdParty/libsmackerdec/CMakeLists.txt new file mode 100644 index 000000000..5b5fc1920 --- /dev/null +++ b/3rdParty/libsmackerdec/CMakeLists.txt @@ -0,0 +1,23 @@ +include(FetchContent_MakeAvailableExcludeFromAll) + +include(FetchContent) +FetchContent_Declare(libsmackerdec + URL https://github.com/diasurgical/libsmackerdec/archive/3489f67f00b8ef5ca3f357a60783f71f70dc7550.tar.gz + URL_HASH MD5=48fcf7221af1272082e9ad0ecf71877e +) +FetchContent_MakeAvailableExcludeFromAll(libsmackerdec) + +add_library(libsmackerdec STATIC + ${libsmackerdec_SOURCE_DIR}/src/BitReader.cpp + ${libsmackerdec_SOURCE_DIR}/src/FileStream.cpp + ${libsmackerdec_SOURCE_DIR}/src/HuffmanVLC.cpp + ${libsmackerdec_SOURCE_DIR}/src/LogError.cpp + ${libsmackerdec_SOURCE_DIR}/src/SmackerDecoder.cpp) + +target_include_directories(libsmackerdec PUBLIC ${libsmackerdec_SOURCE_DIR}/include) + +if(USE_SDL1) + target_link_libraries(libsmackerdec PUBLIC ${SDL_LIBRARY}) +else() + target_link_libraries(libsmackerdec PUBLIC SDL2::SDL2) +endif() diff --git a/CMakeLists.txt b/CMakeLists.txt index 0358e6861..7aae23548 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -360,9 +360,7 @@ if(NOT NOSOUND) endif() endif() -add_library(smacker STATIC - 3rdParty/libsmacker/smacker.c) -target_include_directories(smacker PUBLIC 3rdParty/libsmacker) +add_subdirectory(3rdParty/libsmackerdec) if(WIN32) add_subdirectory(3rdParty/find_steam_game) @@ -484,7 +482,6 @@ set(libdevilutionx_SRCS Source/utils/logged_fstream.cpp Source/utils/paths.cpp Source/utils/sdl_bilinear_scale.cpp - Source/utils/sdl_rwops_file_wrapper.cpp Source/utils/sdl_thread.cpp Source/utils/utf8.cpp Source/DiabloUI/art.cpp @@ -880,7 +877,7 @@ endif() target_link_libraries(libdevilutionx PUBLIC PKWare libmpq - smacker + libsmackerdec simpleini hoehrmann_utf8) diff --git a/Source/storm/storm_svid.cpp b/Source/storm/storm_svid.cpp index 7662cecd7..4394776d6 100644 --- a/Source/storm/storm_svid.cpp +++ b/Source/storm/storm_svid.cpp @@ -4,7 +4,7 @@ #include #include -#include +#include #ifndef NOSOUND #include @@ -20,32 +20,32 @@ #include "utils/display.h" #include "utils/log.hpp" #include "utils/sdl_compat.h" -#include "utils/sdl_rwops_file_wrapper.hpp" #include "utils/sdl_wrap.h" #include "utils/stdcompat/optional.hpp" namespace devilution { namespace { +constexpr size_t SVidPaletteBufferSize = 768; + #ifndef NOSOUND std::optional SVidAudioStream; PushAulibDecoder *SVidAudioDecoder; std::uint8_t SVidAudioDepth; +std::unique_ptr SVidAudioBuffer; #endif -unsigned long SVidWidth, SVidHeight; +uint32_t SVidWidth, SVidHeight; double SVidFrameEnd; double SVidFrameLength; bool SVidLoop; -smk SVidSMK; +SmackerHandle SVidHandle; +std::unique_ptr SVidPaletteBuffer; +std::unique_ptr SVidFrameBuffer; SDL_Color SVidPreviousPalette[256]; SDLPaletteUniquePtr SVidPalette; SDLSurfaceUniquePtr SVidSurface; -#ifndef DEVILUTIONX_SDL_RWOPS_FILE_WRAPPER_AVAILABLE -std::unique_ptr SVidBuffer; -#endif - bool IsLandscapeFit(unsigned long srcW, unsigned long srcH, unsigned long dstW, unsigned long dstH) { return srcW * dstH > dstW * srcH; @@ -119,16 +119,19 @@ bool HasAudio() bool SVidLoadNextFrame() { - SVidFrameEnd += SVidFrameLength; - - if (smk_next(SVidSMK) == SMK_DONE) { + if (Smacker_GetCurrentFrameNum(SVidHandle) >= Smacker_GetNumFrames(SVidHandle)) { if (!SVidLoop) { return false; } - smk_first(SVidSMK); + Smacker_Rewind(SVidHandle); } + SVidFrameEnd += SVidFrameLength; + + Smacker_GetNextFrame(SVidHandle); + Smacker_GetFrame(SVidHandle, SVidFrameBuffer.get()); + return true; } @@ -143,44 +146,31 @@ bool SVidPlayBegin(const char *filename, int flags) SVidLoop = false; if ((flags & 0x40000) != 0) SVidLoop = true; - bool enableVideo = (flags & 0x100000) == 0; //0x8 // Non-interlaced //0x200, 0x800 // Upscale video //0x80000 // Center horizontally + //0x100000 // Disable video //0x800000 // Edge detection //0x200800 // Clear FB SDL_RWops *videoStream = OpenAsset(filename); -#ifdef DEVILUTIONX_SDL_RWOPS_FILE_WRAPPER_AVAILABLE - FILE *file = FILE_FromSDL_RWops(videoStream); - SVidSMK = smk_open_filepointer(file, SMK_MODE_DISK); -#else - size_t bytestoread = SDL_RWsize(videoStream); - SVidBuffer = std::unique_ptr { new uint8_t[bytestoread] }; - SDL_RWread(videoStream, SVidBuffer.get(), bytestoread, 1); - SDL_RWclose(videoStream); - SVidSMK = smk_open_memory(SVidBuffer.get(), bytestoread); -#endif - if (SVidSMK == nullptr) { + SVidHandle = Smacker_Open(videoStream); + if (!SVidHandle.isValid) { return false; } #ifndef NOSOUND const bool enableAudio = (flags & 0x1000000) == 0; - constexpr std::size_t MaxSmkChannels = 7; - unsigned char channels[MaxSmkChannels]; - unsigned char depth[MaxSmkChannels]; - unsigned long rate[MaxSmkChannels]; // NOLINT(google-runtime-int): Match `smk_info_audio` signature. - smk_info_audio(SVidSMK, nullptr, channels, depth, rate); - LogVerbose(LogCategory::Audio, "SVid audio depth={} channels={} rate={}", depth[0], channels[0], rate[0]); + auto audioInfo = Smacker_GetAudioTrackDetails(SVidHandle, 0); + LogVerbose(LogCategory::Audio, "SVid audio depth={} channels={} rate={}", audioInfo.bitsPerSample, audioInfo.nChannels, audioInfo.sampleRate); - if (enableAudio && depth[0] != 0) { + if (enableAudio && audioInfo.bitsPerSample != 0) { sound_stop(); // Stop in-progress music and sound effects - smk_enable_audio(SVidSMK, 0, 1); - SVidAudioDepth = depth[0]; - auto decoder = std::make_unique(channels[0], rate[0]); + SVidAudioDepth = audioInfo.bitsPerSample; + SVidAudioBuffer = std::unique_ptr { new int16_t[audioInfo.idealBufferSize] }; + auto decoder = std::make_unique(audioInfo.nChannels, audioInfo.sampleRate); SVidAudioDecoder = decoder.get(); SVidAudioStream.emplace(/*rwops=*/nullptr, std::move(decoder), std::make_unique(sgOptions.Audio.nResamplingQuality), /*closeRw=*/false); @@ -199,18 +189,21 @@ bool SVidPlayBegin(const char *filename, int flags) } #endif - unsigned long nFrames; - smk_info_all(SVidSMK, nullptr, &nFrames, &SVidFrameLength); - smk_info_video(SVidSMK, &SVidWidth, &SVidHeight, nullptr); + SVidFrameLength = 1000000.0 / Smacker_GetFrameRate(SVidHandle); + Smacker_GetFrameSize(SVidHandle, SVidWidth, SVidHeight); - smk_enable_video(SVidSMK, enableVideo ? 1 : 0); - smk_first(SVidSMK); // Decode first frame + SVidFrameBuffer = std::unique_ptr { new uint8_t[SVidWidth * SVidHeight] }; + + // Decode first frame + Smacker_GetNextFrame(SVidHandle); + Smacker_GetFrame(SVidHandle, SVidFrameBuffer.get()); - smk_info_video(SVidSMK, &SVidWidth, &SVidHeight, nullptr); #ifndef USE_SDL1 if (renderer != nullptr) { - texture = SDLWrap::CreateTexture(renderer, SDL_PIXELFORMAT_RGB888, SDL_TEXTUREACCESS_STREAMING, SVidWidth, SVidHeight); - if (SDL_RenderSetLogicalSize(renderer, SVidWidth, SVidHeight) <= -1) { + int renderWidth = static_cast(SVidWidth); + int renderHeight = static_cast(SVidHeight); + texture = SDLWrap::CreateTexture(renderer, SDL_PIXELFORMAT_RGB888, SDL_TEXTUREACCESS_STREAMING, renderWidth, renderHeight); + if (SDL_RenderSetLogicalSize(renderer, renderWidth, renderHeight) <= -1) { ErrSdl(); } } @@ -221,13 +214,14 @@ bool SVidPlayBegin(const char *filename, int flags) // Copy frame to buffer SVidSurface = SDLWrap::CreateRGBSurfaceWithFormatFrom( - (unsigned char *)smk_get_video(SVidSMK), - SVidWidth, - SVidHeight, + reinterpret_cast(SVidFrameBuffer.get()), + static_cast(SVidWidth), + static_cast(SVidHeight), 8, - SVidWidth, + static_cast(SVidWidth), SDL_PIXELFORMAT_INDEX8); + SVidPaletteBuffer = std::unique_ptr { new uint8_t[SVidPaletteBufferSize] }; SVidPalette = SDLWrap::AllocPalette(); if (SDLC_SetSurfaceColors(SVidSurface.get(), SVidPalette.get()) <= -1) { ErrSdl(); @@ -240,9 +234,10 @@ bool SVidPlayBegin(const char *filename, int flags) bool SVidPlayContinue() { - if (smk_palette_updated(SVidSMK) != 0) { + if (Smacker_DidPaletteChange(SVidHandle)) { SDL_Color colors[256]; - const unsigned char *paletteData = smk_get_palette(SVidSMK); + uint8_t *paletteData = SVidPaletteBuffer.get(); + Smacker_GetPalette(SVidHandle, paletteData); for (int i = 0; i < 256; i++) { colors[i].r = paletteData[i * 3 + 0]; @@ -270,10 +265,10 @@ bool SVidPlayContinue() #ifndef NOSOUND if (HasAudio()) { - const auto len = smk_get_audio_size(SVidSMK, 0); - const unsigned char *buf = smk_get_audio(SVidSMK, 0); + std::int16_t *buf = SVidAudioBuffer.get(); + const auto len = Smacker_GetAudioData(SVidHandle, 0, buf); if (SVidAudioDepth == 16) { - SVidAudioDecoder->PushSamples(reinterpret_cast(buf), len / 2); + SVidAudioDecoder->PushSamples(buf, len / 2); } else { SVidAudioDecoder->PushSamples(reinterpret_cast(buf), len); } @@ -352,18 +347,17 @@ void SVidPlayEnd() if (HasAudio()) { SVidAudioStream = std::nullopt; SVidAudioDecoder = nullptr; + SVidAudioBuffer = nullptr; } #endif - if (SVidSMK != nullptr) - smk_close(SVidSMK); - -#ifndef DEVILUTIONX_SDL_RWOPS_FILE_WRAPPER_AVAILABLE - SVidBuffer = nullptr; -#endif + if (SVidHandle.isValid) + Smacker_Close(SVidHandle); SVidPalette = nullptr; SVidSurface = nullptr; + SVidPaletteBuffer = nullptr; + SVidFrameBuffer = nullptr; memcpy(orig_palette, SVidPreviousPalette, sizeof(orig_palette)); #ifndef USE_SDL1 diff --git a/Source/utils/sdl_rwops_file_wrapper.cpp b/Source/utils/sdl_rwops_file_wrapper.cpp deleted file mode 100644 index 3fd0ff202..000000000 --- a/Source/utils/sdl_rwops_file_wrapper.cpp +++ /dev/null @@ -1,70 +0,0 @@ -#include "utils/sdl_rwops_file_wrapper.hpp" - -#ifdef DEVILUTIONX_SDL_RWOPS_FILE_WRAPPER_AVAILABLE - -#include "utils/log.hpp" - -namespace devilution { -extern "C" { - -#ifdef DEVILUTIONX_SDL_RWOPS_FILE_WRAPPER_IMPL_FOPENCOOKIE - -ssize_t SDL_RWops_CookieRead(void *cookie, char *buf, size_t nbytes) -{ - size_t numRead = SDL_RWread(static_cast(cookie), buf, nbytes, 1); - if (numRead == 0) { - Log("SDL_RWread error: {} ERROR CODE {}", SDL_GetError(), numRead); - } - return numRead * nbytes; -} - -int SDL_RWops_CookieSeek(void *cookie, off64_t *pos, int whence) -{ - int swhence; - switch (whence) { - case SEEK_SET: - swhence = RW_SEEK_SET; - break; - case SEEK_CUR: - swhence = RW_SEEK_CUR; - break; - case SEEK_END: - swhence = RW_SEEK_END; - break; - default: - return -1; - } - const Sint64 spos = SDL_RWseek(static_cast(cookie), *pos, swhence); - if (spos < 0) { - Log("SDL_RWops_RwSeek error: {}", SDL_GetError()); - return -1; - } - *pos = static_cast(spos); - return 0; -} - -int SDL_RWops_CookieClose(void *cookie) -{ - return SDL_RWclose(static_cast(cookie)); -} - -} // extern "C" -#endif - -FILE *FILE_FromSDL_RWops(SDL_RWops *handle) -{ -#ifdef DEVILUTIONX_SDL_RWOPS_FILE_WRAPPER_IMPL_FOPENCOOKIE - cookie_io_functions_t ioFns; - std::memset(&ioFns, 0, sizeof(ioFns)); - ioFns.read = &SDL_RWops_CookieRead; - ioFns.seek = &SDL_RWops_CookieSeek; - ioFns.close = &SDL_RWops_CookieClose; - return fopencookie(handle, "rb", ioFns); -#else -#error "unimplemented" -#endif -} - -} // namespace devilution - -#endif diff --git a/Source/utils/sdl_rwops_file_wrapper.hpp b/Source/utils/sdl_rwops_file_wrapper.hpp deleted file mode 100644 index b69842681..000000000 --- a/Source/utils/sdl_rwops_file_wrapper.hpp +++ /dev/null @@ -1,17 +0,0 @@ -/** A pointer to a Storm file as a `FILE *`. Only available on some platforms. */ -#pragma once -#if (defined(__linux__) && !defined(__ANDROID__)) || defined(__FreeBSD__) || defined(__DragonFly__) || defined(__HAIKU__) -#include - -#include - -#define DEVILUTIONX_SDL_RWOPS_FILE_WRAPPER_AVAILABLE -#define DEVILUTIONX_SDL_RWOPS_FILE_WRAPPER_IMPL_FOPENCOOKIE - -namespace devilution { - -FILE *FILE_FromSDL_RWops(SDL_RWops *handle); - -} // namespace devilution - -#endif