diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..4c30a7e --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,131 @@ +cmake_minimum_required( VERSION 2.4 ) +if( COMMAND cmake_policy ) + cmake_policy( SET CMP0003 NEW ) +endif( COMMAND cmake_policy ) + +include( CheckFunctionExists ) +include( CheckCXXCompilerFlag ) + +project( WADEXT ) + +IF( NOT CMAKE_BUILD_TYPE ) + SET( CMAKE_BUILD_TYPE Debug CACHE STRING + "Choose the type of build, options are: Debug Release RelWithDebInfo MinSizeRel." + FORCE ) +ENDIF( NOT CMAKE_BUILD_TYPE ) + +set( SSE_MATTERS NO ) + +if( CMAKE_COMPILER_IS_GNUCXX ) + set( GPROF 0 CACHE BOOL "Enable profiling with gprof for Debug and RelWithDebInfo build types." ) + set( PROFILE 0 CACHE INT "1=Generate profile coverage info, 2=Use it" ) +endif( CMAKE_COMPILER_IS_GNUCXX ) + +find_package( ZLIB ) + +if( MSVC ) + # Eliminate unreferenced functions and data + # Perform identical COMDAT folding + set( REL_LINKER_FLAGS "/opt:ref /opt:icf /nodefaultlib:msvcrt" ) + + # String pooling + # Function-level linking + # Disable run-time type information + set( ALL_C_FLAGS "/GF /Gy /GR-" ) + + # Avoid CRT DLL dependancies in release builds + set( REL_C_FLAGS "/MT" ) + + # Disable warnings for unsecure CRT functions from VC8+ + if( MSVC_VERSION GREATER 1399 ) + set( ALL_C_FLAGS "${ALL_C_FLAGS} /wd4996" ) + endif( MSVC_VERSION GREATER 1399 ) + + # The CMake configurations set /GR and /MD by default, which conflict with our settings. + string(REPLACE "/MD " " " CMAKE_CXX_FLAGS_RELEASE ${CMAKE_CXX_FLAGS_RELEASE} ) + string(REPLACE "/MD " " " CMAKE_CXX_FLAGS_MINSIZEREL ${CMAKE_CXX_FLAGS_MINSIZEREL} ) + string(REPLACE "/MD " " " CMAKE_CXX_FLAGS_RELWITHDEBINFO ${CMAKE_CXX_FLAGS_RELWITHDEBINFO} ) + string(REPLACE "/MD " " " CMAKE_C_FLAGS_RELEASE ${CMAKE_C_FLAGS_RELEASE} ) + string(REPLACE "/MD " " " CMAKE_C_FLAGS_MINSIZEREL ${CMAKE_C_FLAGS_MINSIZEREL} ) + string(REPLACE "/MD " " " CMAKE_C_FLAGS_RELWITHDEBINFO ${CMAKE_C_FLAGS_RELWITHDEBINFO} ) + string(REPLACE " /GR" " " CMAKE_CXX_FLAGS ${CMAKE_CXX_FLAGS} ) +endif( MSVC ) + +if( CMAKE_COMPILER_IS_GNUCXX ) + set( ALL_C_FLAGS "${ALL_C_FLAGS} -ffast-math -pipe" ) + if( GPROF ) + set( ALL_C_FLAGS "${ALL_C_FLAGS} -pg -g" ) + else( GPROF ) + set( REL_C_FLAGS "${REL_C_FLAGS} -fomit-frame-pointer" ) + endif( GPROF ) + if( PROFILE EQUAL 1 ) + message( STATUS "Generating profile coverage information" ) + set( ALL_C_FLAGS "${ALL_C_FLAGS} -fprofile-generate" ) + set( PROF_LIB "gcov" ) + elseif( PROFILE EQUAL 2 ) + message( STATUS "Using profile coverage information" ) + set( ALL_C_FLAGS "${ALL_C_FLAGS} -fprofile-use" ) + endif( PROFILE EQUAL 1 ) +endif( CMAKE_COMPILER_IS_GNUCXX ) + +#if( WIN32 AND "${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang" ) +# set( ALL_C_FLAGS "${ALL_C_FLAGS} -Wno-deprecated-declarations -Wno-format" ) +#endif( WIN32 AND "${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang" ) + +if( ZLIB_FOUND ) + message( STATUS "Using system zlib" ) +else( ZLIB_FOUND ) + message( STATUS "Using internal zlib" ) + add_subdirectory( zlib ) + set( ZLIB_INCLUDE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/zlib ) + set( ZLIB_LIBRARIES z ) + set( ZLIB_LIBRARY z ) +endif( ZLIB_FOUND ) + +CHECK_FUNCTION_EXISTS( stricmp STRICMP_EXISTS ) +if( NOT STRICMP_EXISTS ) + add_definitions( -Dstricmp=strcasecmp ) +endif( NOT STRICMP_EXISTS ) + +CHECK_FUNCTION_EXISTS( strnicmp STRNICMP_EXISTS ) +if( NOT STRNICMP_EXISTS ) + add_definitions( -Dstrnicmp=strncasecmp ) +endif( NOT STRNICMP_EXISTS ) + +set( WADEXT_LIBS "${ZLIB_LIBRARIES}" ) + +set( HEADERS + fileformat.h + resourcefile.h + tarray.h + wadext.h + wadman.h + ) + +set( SOURCES + convert.cpp + fileformat.cpp + main.cpp + wadext.cpp + wadfile.cpp +) + +set( CMAKE_EXE_LINKER_FLAGS_RELEASE "${CMAKE_EXE_LINKER_FLAGS_RELEASE} ${REL_LINKER_FLAGS}" ) +set( CMAKE_EXE_LINKER_FLAGS_MINSIZEREL "${CMAKE_EXE_LINKER_FLAGS_MINSIZEREL} ${REL_LINKER_FLAGS}" ) +set( CMAKE_EXE_LINKER_FLAGS_RELWITHDEBINFO "${CMAKE_EXE_LINKER_FLAGS_RELWITHDEBINFO} ${REL_LINKER_FLAGS}" ) + +set( CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${ALL_C_FLAGS}" ) +set( CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} ${REL_C_FLAGS}" ) +set( CMAKE_C_FLAGS_MINSIZEREL "${CMAKE_C_FLAGS_MINSIZEREL} ${REL_C_FLAGS}" ) +set( CMAKE_C_FLAGS_RELWITHDEBINFO "${CMAKE_C_FLAGS_RELWITHDEBINFO} ${REL_C_FLAGS}" ) +set( CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} ${DEB_C_FLAGS} -D_DEBUG" ) + +set( CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${ALL_C_FLAGS}" ) +set( CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} ${REL_C_FLAGS}" ) +set( CMAKE_CXX_FLAGS_MINSIZEREL "${CMAKE_CXX_FLAGS_MINSIZEREL} ${REL_C_FLAGS}" ) +set( CMAKE_CXX_FLAGS_RELWITHDEBINFO "${CMAKE_CXX_FLAGS_RELWITHDEBINFO} ${REL_C_FLAGS}" ) +set( CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} ${DEB_C_FLAGS} -D_DEBUG" ) + +add_executable( wadext ${SOURCES} ${HEADERS} ) +target_link_libraries( wadext ${WADEXT_LIBS} ${PROF_LIB} ) +include_directories( "${ZLIB_INCLUDE_DIR}" ) diff --git a/convert.cpp b/convert.cpp new file mode 100644 index 0000000..ec601a3 --- /dev/null +++ b/convert.cpp @@ -0,0 +1,771 @@ +// +//--------------------------------------------------------------------------- +// +// Copyright(C) 2016 Christoph Oelckers +// All rights reserved. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 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 General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see http://www.gnu.org/licenses/ +// +//-------------------------------------------------------------------------- +// +// Portions of this file from ZDoom under the following license: +/* +** m_png.cpp +** Routines for manipulating PNG files. +** +**--------------------------------------------------------------------------- +** Copyright 2002-2006 Randy Heit +** All rights reserved. +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** +** 1. Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** 2. Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in the +** documentation and/or other materials provided with the distribution. +** 3. The name of the author may not be used to endorse or promote products +** derived from this software without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR +** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, +** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +**--------------------------------------------------------------------------- +** +*/ + + +#include +#include +#include +#include +#include "zlib/zlib.h" +#include "wadext.h" +#include "ResourceFile.h" +#include "fileformat.h" +#include "tarray.h" + +#pragma warning(disable:4996) + +const uint8_t doompal[]={ + 0, 0, 0, 31, 23, 11, 23, 15, 7, 75, 75, 75,255,255,255, 27, + 27, 27, 19, 19, 19, 11, 11, 11, 7, 7, 7, 47, 55, 31, 35, 43, + 15, 23, 31, 7, 15, 23, 0, 79, 59, 43, 71, 51, 35, 63, 43, 27, +255,183,183,247,171,171,243,163,163,235,151,151,231,143,143,223, +135,135,219,123,123,211,115,115,203,107,107,199, 99, 99,191, 91, + 91,187, 87, 87,179, 79, 79,175, 71, 71,167, 63, 63,163, 59, 59, +155, 51, 51,151, 47, 47,143, 43, 43,139, 35, 35,131, 31, 31,127, + 27, 27,119, 23, 23,115, 19, 19,107, 15, 15,103, 11, 11, 95, 7, + 7, 91, 7, 7, 83, 7, 7, 79, 0, 0, 71, 0, 0, 67, 0, 0, +255,235,223,255,227,211,255,219,199,255,211,187,255,207,179,255, +199,167,255,191,155,255,187,147,255,179,131,247,171,123,239,163, +115,231,155,107,223,147, 99,215,139, 91,207,131, 83,203,127, 79, +191,123, 75,179,115, 71,171,111, 67,163,107, 63,155, 99, 59,143, + 95, 55,135, 87, 51,127, 83, 47,119, 79, 43,107, 71, 39, 95, 67, + 35, 83, 63, 31, 75, 55, 27, 63, 47, 23, 51, 43, 19, 43, 35, 15, +239,239,239,231,231,231,223,223,223,219,219,219,211,211,211,203, +203,203,199,199,199,191,191,191,183,183,183,179,179,179,171,171, +171,167,167,167,159,159,159,151,151,151,147,147,147,139,139,139, +131,131,131,127,127,127,119,119,119,111,111,111,107,107,107, 99, + 99, 99, 91, 91, 91, 87, 87, 87, 79, 79, 79, 71, 71, 71, 67, 67, + 67, 59, 59, 59, 55, 55, 55, 47, 47, 47, 39, 39, 39, 35, 35, 35, +119,255,111,111,239,103,103,223, 95, 95,207, 87, 91,191, 79, 83, +175, 71, 75,159, 63, 67,147, 55, 63,131, 47, 55,115, 43, 47, 99, + 35, 39, 83, 27, 31, 67, 23, 23, 51, 15, 19, 35, 11, 11, 23, 7, +191,167,143,183,159,135,175,151,127,167,143,119,159,135,111,155, +127,107,147,123, 99,139,115, 91,131,107, 87,123, 99, 79,119, 95, + 75,111, 87, 67,103, 83, 63, 95, 75, 55, 87, 67, 51, 83, 63, 47, +159,131, 99,143,119, 83,131,107, 75,119, 95, 63,103, 83, 51, 91, + 71, 43, 79, 59, 35, 67, 51, 27,123,127, 99,111,115, 87,103,107, + 79, 91, 99, 71, 83, 87, 59, 71, 79, 51, 63, 71, 43, 55, 63, 39, +255,255,115,235,219, 87,215,187, 67,195,155, 47,175,123, 31,155, + 91, 19,135, 67, 7,115, 43, 0,255,255,255,255,219,219,255,187, +187,255,155,155,255,123,123,255, 95, 95,255, 63, 63,255, 31, 31, +255, 0, 0,239, 0, 0,227, 0, 0,215, 0, 0,203, 0, 0,191, + 0, 0,179, 0, 0,167, 0, 0,155, 0, 0,139, 0, 0,127, 0, + 0,115, 0, 0,103, 0, 0, 91, 0, 0, 79, 0, 0, 67, 0, 0, +231,231,255,199,199,255,171,171,255,143,143,255,115,115,255, 83, + 83,255, 55, 55,255, 27, 27,255, 0, 0,255, 0, 0,227, 0, 0, +203, 0, 0,179, 0, 0,155, 0, 0,131, 0, 0,107, 0, 0, 83, +255,255,255,255,235,219,255,215,187,255,199,155,255,179,123,255, +163, 91,255,143, 59,255,127, 27,243,115, 23,235,111, 15,223,103, + 15,215, 95, 11,203, 87, 7,195, 79, 0,183, 71, 0,175, 67, 0, +255,255,255,255,255,215,255,255,179,255,255,143,255,255,107,255, +255, 71,255,255, 35,255,255, 0,167, 63, 0,159, 55, 0,147, 47, + 0,135, 35, 0, 79, 59, 39, 67, 47, 27, 55, 35, 19, 47, 27, 11, + 0, 0, 83, 0, 0, 71, 0, 0, 59, 0, 0, 47, 0, 0, 35, 0, + 0, 23, 0, 0, 11, 0, 0, 0,255,159, 67,255,231, 75,255,123, +255,255, 0,255,207, 0,207,159, 0,155,111, 0,107,167,107,107, + }; + +const uint8_t hereticpal[]={ + 2, 2, 2, 2, 2, 2, 16, 16, 16, 24, 24, 24, 31, 31, 31, 36, + 36, 36, 44, 44, 44, 48, 48, 48, 55, 55, 55, 63, 63, 63, 70, 70, + 70, 78, 78, 78, 86, 86, 86, 93, 93, 93,101,101,101,108,108,108, +116,116,116,124,124,124,131,131,131,139,139,139,146,146,146,154, +154,154,162,162,162,169,169,169,177,177,177,184,184,184,192,192, +192,200,200,200,207,207,207,210,210,210,215,215,215,222,222,222, +228,228,228,236,236,236,245,245,245,255,255,255, 50, 50, 50, 59, + 60, 59, 69, 72, 68, 78, 80, 77, 88, 93, 86, 97,100, 95,109,112, +104,116,123,112,125,131,121,134,141,130,144,151,139,153,161,148, +163,171,157,172,181,166,181,189,176,189,196,185, 20, 16, 36, 24, + 24, 44, 36, 36, 60, 52, 52, 80, 68, 68, 96, 88, 88,116,108,108, +136,124,124,152,148,148,172,164,164,184,180,184,200,192,196,208, +208,208,216,224,224,224, 27, 15, 8, 38, 20, 11, 49, 27, 14, 61, + 31, 14, 65, 35, 18, 74, 37, 19, 83, 43, 19, 87, 47, 23, 95, 51, + 27,103, 59, 31,115, 67, 35,123, 75, 39,131, 83, 47,143, 91, 51, +151, 99, 59,160,108, 64,175,116, 74,180,126, 81,192,135, 91,204, +143, 93,213,151,103,216,159,115,220,167,126,223,175,138,227,183, +149,230,190,161,233,198,172,237,206,184,240,214,195, 62, 40, 11, + 75, 50, 16, 84, 59, 23, 95, 67, 30,103, 75, 38,110, 83, 47,123, + 95, 55,137,107, 62,150,118, 75,163,129, 84,171,137, 92,180,146, +101,188,154,109,196,162,117,204,170,125,208,176,133, 37, 20, 4, + 47, 24, 4, 57, 28, 6, 68, 33, 4, 76, 36, 3, 84, 40, 0, 97, + 47, 2,114, 54, 0,125, 63, 6,141, 75, 9,155, 83, 17,162, 95, + 21,169,103, 26,180,113, 32,188,124, 20,204,136, 24,220,148, 28, +236,160, 23,244,172, 47,252,187, 57,252,194, 70,251,201, 83,251, +208, 97,251,214,110,251,221,123,250,228,136,157, 51, 4,170, 65, + 2,185, 86, 4,213,118, 4,236,164, 3,248,190, 3,255,216, 43, +255,255, 0, 67, 0, 0, 79, 0, 0, 91, 0, 0,103, 0, 0,115, + 0, 0,127, 0, 0,139, 0, 0,155, 0, 0,167, 0, 0,179, 0, + 0,191, 0, 0,203, 0, 0,215, 0, 0,227, 0, 0,239, 0, 0, +255, 0, 0,255, 52, 52,255, 74, 74,255, 95, 95,255,123,123,255, +155,155,255,179,179,255,201,201,255,215,215, 60, 12, 88, 80, 8, +108,104, 8,128,128, 0,144,152, 0,176,184, 0,224,216, 44,252, +224,120,240, 37, 6,129, 60, 33,147, 82, 61,165,105, 88,183,128, +116,201,151,143,219,173,171,237,196,198,255, 2, 4, 41, 2, 5, + 49, 6, 8, 57, 2, 5, 65, 2, 5, 79, 0, 4, 88, 0, 4, 96, + 0, 4,104, 2, 5,121, 2, 5,137, 6, 9,159, 12, 16,184, 32, + 40,200, 56, 60,220, 80, 80,253, 80,108,252, 80,136,252, 80,164, +252, 80,196,252, 72,220,252, 80,236,252, 84,252,252,152,252,252, +188,252,244, 11, 23, 7, 19, 35, 11, 23, 51, 15, 31, 67, 23, 39, + 83, 27, 47, 99, 35, 55,115, 43, 63,131, 47, 67,147, 55, 75,159, + 63, 83,175, 71, 91,191, 79, 95,207, 87,103,223, 95,111,239,103, +119,255,111, 23, 31, 23, 27, 35, 27, 31, 43, 31, 35, 51, 35, 43, + 55, 43, 47, 63, 47, 51, 71, 51, 59, 75, 55, 63, 83, 59, 67, 91, + 67, 75, 95, 71, 79,103, 75, 87,111, 79, 91,115, 83, 95,123, 87, +103,131, 95,255,223, 0,255,191, 0,255,159, 0,255,127, 0,255, + 95, 0,255, 63, 0,244, 14, 3, 55, 0, 0, 47, 0, 0, 39, 0, + 0, 23, 0, 0, 15, 15, 15, 11, 11, 11, 7, 7, 7,255,255,255, +}; + + + +const uint8_t hexenpal[]={ + 2, 2, 2, 4, 4, 4, 15, 15, 15, 19, 19, 19, 27, 27, 27, 28, + 28, 28, 33, 33, 33, 39, 39, 39, 45, 45, 45, 51, 51, 51, 57, 57, + 57, 63, 63, 63, 69, 69, 69, 75, 75, 75, 81, 81, 81, 86, 86, 86, + 92, 92, 92, 98, 98, 98,104,104,104,112,112,112,121,121,121,130, +130,130,139,139,139,147,147,147,157,157,157,166,166,166,176,176, +176,185,185,185,194,194,194,203,203,203,212,212,212,221,221,221, +230,230,230, 29, 32, 29, 38, 40, 37, 50, 50, 50, 59, 60, 59, 69, + 72, 68, 78, 80, 77, 88, 93, 86, 97,100, 95,109,112,104,116,123, +112,125,131,121,134,141,130,144,151,139,153,161,148,163,171,157, +172,181,166,181,189,176,189,196,185, 22, 29, 22, 27, 36, 27, 31, + 43, 31, 35, 51, 35, 43, 55, 43, 47, 63, 47, 51, 71, 51, 59, 75, + 55, 63, 83, 59, 67, 91, 67, 75, 95, 71, 79,103, 75, 87,111, 79, + 91,115, 83, 95,123, 87,103,131, 95, 20, 16, 36, 30, 26, 46, 40, + 36, 57, 50, 46, 67, 59, 57, 78, 69, 67, 88, 79, 77, 99, 89, 87, +109, 99, 97,120,109,107,130,118,118,141,128,128,151,138,138,162, +148,148,172, 62, 40, 11, 75, 50, 16, 84, 59, 23, 95, 67, 30,103, + 75, 38,110, 83, 47,123, 95, 55,137,107, 62,150,118, 75,163,129, + 84,171,137, 92,180,146,101,188,154,109,196,162,117,204,170,125, +208,176,133, 27, 15, 8, 38, 20, 11, 49, 27, 14, 61, 31, 14, 65, + 35, 18, 74, 37, 19, 83, 43, 19, 87, 47, 23, 95, 51, 27,103, 59, + 31,115, 67, 35,123, 75, 39,131, 83, 47,143, 91, 51,151, 99, 59, +160,108, 64,175,116, 74,180,126, 81,192,135, 91,204,143, 93,213, +151,103,216,159,115,220,167,126,223,175,138,227,183,149, 37, 20, + 4, 47, 24, 4, 57, 28, 6, 68, 33, 4, 76, 36, 3, 84, 40, 0, + 97, 47, 2,114, 54, 0,125, 63, 6,141, 75, 9,155, 83, 17,162, + 95, 21,169,103, 26,180,113, 32,188,124, 20,204,136, 24,220,148, + 28,236,160, 23,244,172, 47,252,187, 57,252,194, 70,251,201, 83, +251,208, 97,251,221,123, 2, 4, 41, 2, 5, 49, 6, 8, 57, 2, + 5, 65, 2, 5, 79, 0, 4, 88, 0, 4, 96, 0, 4,104, 4, 6, +121, 2, 5,137, 20, 23,152, 38, 41,167, 56, 59,181, 74, 77,196, + 91, 94,211,109,112,226,127,130,240,145,148,255, 31, 4, 4, 39, + 0, 0, 47, 0, 0, 55, 0, 0, 67, 0, 0, 79, 0, 0, 91, 0, + 0,103, 0, 0,115, 0, 0,127, 0, 0,139, 0, 0,155, 0, 0, +167, 0, 0,185, 0, 0,202, 0, 0,220, 0, 0,237, 0, 0,255, + 0, 0,255, 46, 46,255, 91, 91,255,137,137,255,171,171, 20, 16, + 4, 13, 24, 9, 17, 33, 12, 21, 41, 14, 24, 50, 17, 28, 57, 20, + 32, 65, 24, 35, 73, 28, 39, 80, 31, 44, 86, 37, 46, 95, 38, 51, +104, 43, 60,122, 51, 68,139, 58, 77,157, 66, 85,174, 73, 94,192, + 81,157, 51, 4,170, 65, 2,185, 86, 4,213,119, 6,234,147, 5, +255,178, 6,255,195, 26,255,216, 45, 4,133, 4, 8,175, 8, 2, +215, 2, 3,234, 3, 42,252, 42,121,255,121, 3, 3,184, 15, 41, +220, 28, 80,226, 41,119,233, 54,158,239, 67,197,246, 80,236,252, +244, 14, 3,255, 63, 0,255, 95, 0,255,127, 0,255,159, 0,255, +195, 26,255,223, 0, 43, 13, 64, 61, 14, 89, 90, 15,122,120, 16, +156,149, 16,189,178, 17,222,197, 74,232,215,129,243,234,169,253, + 61, 16, 16, 90, 36, 33,118, 56, 49,147, 77, 66,176, 97, 83,204, +117, 99, 71, 53, 2, 81, 63, 6, 96, 72, 0,108, 80, 0,120, 88, + 0,128, 96, 0,149,112, 1,181,136, 3,212,160, 4,255,255,255, +}; + +const uint8_t strifepal[]={ + 0, 0, 0,231,227,227,223,219,219,215,211,211,207,203,203,199, +195,195,191,191,191,183,183,183,179,175,175,171,167,167,163,159, +159,155,151,151,147,147,147,139,139,139,131,131,131,123,123,123, +119,115,115,111,111,111,103,103,103, 95, 95, 95, 87, 87, 87, 79, + 79, 79, 71, 71, 71, 67, 63, 63, 59, 59, 59, 51, 51, 51, 43, 43, + 43, 35, 35, 35, 27, 27, 27, 19, 19, 19, 11, 11, 11, 7, 7, 7, +187,191,183,179,183,171,167,179,159,163,171,147,155,167,139,147, +159,127,139,155,119,131,147,107,127,143,103,119,135, 91,115,131, + 83,107,123, 75,103,119, 67, 99,111, 63, 91,107, 55, 87, 99, 47, + 83, 95, 43, 75, 87, 35, 71, 83, 31, 67, 75, 27, 63, 71, 23, 59, + 63, 19, 51, 59, 15, 47, 51, 11, 43, 47, 7, 39, 43, 7, 31, 35, + 7, 27, 31, 0, 23, 23, 0, 15, 19, 0, 11, 11, 0, 7, 7, 0, +219, 43, 43,203, 35, 35,191, 31, 31,175, 27, 27,163, 23, 23,147, + 19, 19,135, 15, 15,119, 11, 11,107, 7, 7, 91, 7, 7, 79, 0, + 0, 63, 0, 0, 51, 0, 0, 39, 0, 0, 23, 0, 0, 11, 0, 0, +235,231, 0,231,211, 0,215,179, 0,199,151, 0,183,127, 0,167, +103, 0,151, 83, 0,135, 63, 0,119, 47, 0,103, 35, 0, 87, 23, + 0, 71, 11, 0, 55, 7, 0, 39, 0, 0, 23, 0, 0, 11, 0, 0, +183,231,127,163,215,111,143,199, 95,127,183, 79,107,171, 67, 91, +155, 55, 75,139, 43, 63,123, 35, 47,111, 27, 35, 95, 19, 23, 79, + 11, 15, 67, 7, 7, 51, 7, 0, 35, 0, 0, 19, 0, 0, 7, 0, +199,207,255,183,187,239,163,171,219,151,155,203,135,139,187,123, +127,171,107,111,155, 95, 99,139, 83, 83,123, 67, 71,107, 55, 59, + 91, 47, 47, 75, 35, 35, 59, 23, 23, 43, 15, 15, 27, 0, 0, 11, +199,191,147,179,171,131,167,155,119,155,139,111,143,127, 99,131, +111, 91,119, 99, 79,107, 87, 71, 91, 71, 59, 79, 59, 51, 67, 47, + 43, 55, 39, 35, 43, 27, 27, 31, 19, 19, 19, 11, 11, 7, 7, 0, +143,195,211,123,179,195,107,167,183, 91,155,167, 75,139,155, 59, +127,139, 47,115,127, 35,103,115, 27, 91, 99, 19, 79, 87, 11, 67, + 71, 7, 55, 59, 0, 43, 43, 0, 31, 31, 0, 19, 19, 0, 7, 7, +211,191,175,203,179,163,195,171,151,191,159,143,183,151,131,175, +143,123,171,135,115,163,123,103,155,115, 95,151,107, 87,143, 99, + 79,139, 91, 71,131, 83, 67,123, 75, 59,119, 67, 51,111, 59, 47, +103, 55, 39, 99, 47, 35, 91, 43, 31, 83, 35, 27, 79, 31, 23, 71, + 27, 19, 63, 19, 15, 59, 15, 11, 51, 11, 7, 43, 7, 7, 39, 7, + 0, 31, 0, 0, 27, 0, 0, 19, 0, 0, 11, 0, 0, 7, 0, 0, +211,199,187,203,191,179,195,183,171,191,175,163,183,167,155,175, +159,147,171,151,139,163,143,135,155,139,127,151,131,119,143,123, +111,135,115,107,131,107, 99,123,103, 95,115, 95, 87,111, 87, 83, +103, 83, 75, 95, 75, 71, 91, 67, 63, 83, 63, 59, 79, 55, 51, 71, + 51, 47, 63, 43, 43, 59, 39, 39, 51, 35, 31, 43, 27, 27, 39, 23, + 23, 31, 19, 19, 23, 15, 15, 19, 11, 11, 11, 7, 7, 7, 7, 0, +239,239, 0,231,215, 0,227,191, 0,219,171, 0,215,151, 0,211, +131, 0,203,111, 0,199, 91, 0,191, 75, 0,187, 59, 0,183, 43, + 0,255, 0, 0,223, 0, 0,191, 0, 0,159, 0, 0,127, 0, 0, + 0, 47, 47,139,199,103,107,171, 75, 79,143, 55, 55,115, 35, 35, + 87, 19, 19, 63, 11,215,223,255,187,203,247,143,167,219, 99,131, +195, 63, 91,167,203,203,203,215,215,215,223,223,223,235,235,235, +}; + +#pragma pack(1) +struct IHDR +{ + uint32_t Width; + uint32_t Height; + uint8_t BitDepth; + uint8_t ColorType; + uint8_t Compression; + uint8_t Filter; + uint8_t Interlace; +}; +#pragma pack() + + +uint8_t palette[768]; +const uint8_t *ppal = NULL; +bool done; + +const uint8_t *GetPalette(int flags) +{ + if (ppal) return ppal; + int pplump = mainwad->FindLump("PLAYPAL"); + if (pplump >= 0) + { + WadItemList w(pplump); + memcpy(palette, w.Address(), 768); + ppal = palette; + } + else + { + if (flags&DO_HERETIC_PAL) ppal = hereticpal; + else if (flags&DO_HEXEN_PAL) ppal = hexenpal; + else if (flags&DO_STRIFE) ppal = strifepal; + else ppal = doompal; + } + return ppal; +} + +inline uint32_t BigLong(uint32_t nLongNumber) +{ + return (((nLongNumber & 0x000000FF) << 24) + ((nLongNumber & 0x0000FF00) << 8) + + ((nLongNumber & 0x00FF0000) >> 8) + ((nLongNumber & 0xFF000000) >> 24)); +} + +inline const uint32_t *GetCRCTable() { return (const uint32_t *)get_crc_table(); } +inline uint32_t CalcCRC32(const uint8_t *buf, unsigned int len) +{ + return crc32(0, buf, len); +} +inline uint32_t AddCRC32(uint32_t crc, const uint8_t *buf, unsigned int len) +{ + return crc32(crc, buf, len); +} +inline uint32_t CRC1(uint32_t crc, const uint8_t c, const uint32_t *crcTable) +{ + return crcTable[(crc & 0xff) ^ c] ^ (crc >> 8); +} + + +//========================================================================== +// +// M_AppendPNGChunk +// +// Writes a PNG-compliant chunk to the file. +// +//========================================================================== + +bool M_AppendPNGChunk(FILE *file, uint32_t chunkID, const void *_chunkData, uint32_t len) +{ + const uint8_t *chunkData = (uint8_t*)_chunkData; + uint32_t head[2] = { BigLong((unsigned int)len), chunkID }; + uint32_t crc; + + if (fwrite(head, 1, 8, file) == 8 && + (len == 0 || fwrite(chunkData, 1, len, file) == len)) + { + crc = CalcCRC32((uint8_t *)&head[1], 4); + if (len != 0) + { + crc = AddCRC32(crc, chunkData, len); + } + crc = BigLong((unsigned int)crc); + return fwrite(&crc, 1, 4, file) == 4; + } + return false; +} + + +//========================================================================== +// +// M_SaveBitmap +// +// Given a bitmap, creates one or more IDAT chunks in the given file. +// Returns true on success. +// +//========================================================================== +#define PNG_WRITE_SIZE 32768 +#define MAXWIDTH 8192 + +bool M_SaveBitmap(const uint8_t *from, int width, int height, int pitch, FILE *file) +{ + uint8_t temprow[1][1 + MAXWIDTH * 3]; + uint8_t buffer[PNG_WRITE_SIZE]; + z_stream stream; + int err; + int y; + + stream.next_in = Z_NULL; + stream.avail_in = 0; + stream.zalloc = Z_NULL; + stream.zfree = Z_NULL; + err = deflateInit(&stream, 9); + + if (err != Z_OK) + { + return false; + } + + y = height; + stream.next_out = buffer; + stream.avail_out = sizeof(buffer); + + temprow[0][0] = 0; + + while (y-- > 0 && err == Z_OK) + { + memcpy(&temprow[0][1], from, width); + // always use filter type 0 for paletted images + stream.next_in = temprow[0]; + stream.avail_in = width + 1; + + from += pitch; + + err = deflate(&stream, (y == 0) ? Z_FINISH : 0); + if (err != Z_OK) + { + break; + } + while (stream.avail_out == 0) + { + if (!M_AppendPNGChunk(file, MAKE_ID('I', 'D', 'A', 'T'), buffer, sizeof(buffer))) + { + return false; + } + stream.next_out = buffer; + stream.avail_out = sizeof(buffer); + if (stream.avail_in != 0) + { + err = deflate(&stream, (y == 0) ? Z_FINISH : 0); + if (err != Z_OK) + { + break; + } + } + } + } + + while (err == Z_OK) + { + err = deflate(&stream, Z_FINISH); + if (err != Z_OK) + { + break; + } + if (stream.avail_out == 0) + { + if (!M_AppendPNGChunk(file, MAKE_ID('I','D','A','T'), buffer, sizeof(buffer))) + { + return false; + } + stream.next_out = buffer; + stream.avail_out = sizeof(buffer); + } + } + + deflateEnd(&stream); + + if (err != Z_STREAM_END) + { + return false; + } + return M_AppendPNGChunk(file, MAKE_ID('I', 'D', 'A', 'T'), buffer, sizeof(buffer) - stream.avail_out); +} + + +//========================================================================== +// +// +// +//========================================================================== + +void WritePNG(const uint8_t *bufferRGBA, int width, int height, int leftofs, int topofs, const char *filename) +{ + int numpix = width * height; + bool hastrans = false; + TMap palmap; + uint8_t *bufferPaletted = new uint8_t[numpix]; + uint8_t palette[768]; + for (int i = 0; i < numpix; i++) + { + if (bufferRGBA[i * 4 + 3] == 0) + { + hastrans = true; + break; + } + } + int curcol = 0; + if (hastrans) + { + palmap[0] = 0; + palette[0] = palette[1] = palette[2] = 0; + curcol++; + } + for (int i = 0; i < numpix; i++) + { + const uint8_t *pix = &bufferRGBA[i * 4]; + uint32_t rgba = pix[0] + 256 * pix[1] + 256 * 256 * pix[2] + 256 * 256 * 256 * pix[3]; + uint8_t *pIndex = palmap.CheckKey(rgba); + uint8_t index; + if (pIndex != NULL) index = *pIndex; + else + { + palmap[rgba] = curcol; + index = curcol; + palette[curcol * 3] = pix[0]; + palette[curcol * 3+1] = pix[1]; + palette[curcol * 3+2] = pix[2]; + curcol++; + } + bufferPaletted[i] = index; + } + + FILE * f = fopen(filename, "wb"); + if (f == NULL) return; + + const char sig[] = "\x89PNG\r\n\x1a\n"; + IHDR ihdr; + + ihdr.Width = BigLong(width); + ihdr.Height = BigLong(height); + ihdr.BitDepth = 8; + ihdr.ColorType = 3; + ihdr.Compression = 0; + ihdr.Filter = 0; + ihdr.Interlace = 0; + + fwrite(sig, 1, 8, f); + M_AppendPNGChunk(f, MAKE_ID('I', 'H', 'D', 'R'), &ihdr, sizeof(ihdr)); + if (leftofs != 0 || topofs != 0) + { + uint32_t ofs[] = { BigLong(leftofs), BigLong(topofs) }; + M_AppendPNGChunk(f, MAKE_ID('g', 'r', 'A', 'b'), ofs, 8); + } + if (curcol > 0) + { + M_AppendPNGChunk(f, MAKE_ID('P', 'L', 'T', 'E'), palette, curcol * 3); + } + if (hastrans) + { + char nul = 0; + M_AppendPNGChunk(f, MAKE_ID('t', 'R', 'N', 'S'), &nul, 1); + } + + M_SaveBitmap(bufferPaletted, width, height, width, f); + + static const uint8_t iend[12] = { 0,0,0,0,73,69,78,68,174,66,96,130 }; + fwrite(iend, 1, 12, f); + fclose(f); +} + + +bool FlatToPng(int options, const uint8_t *data, int length, const char *pngpath) +{ + int width, height; + + // This uses the same logic as ZDoom, except for the 320x200 case which is for Raven's fullscreen images + switch (length) + { + default: + case 64 * 64: width = height = 64; break; + case 8 * 8: width = height = 8; break; + case 16 * 16: width = height = 16; break; + case 32 * 32: width = height = 32; break; + case 128 * 128: width = height = 128; break; + case 256 * 256: width = height = 256; break; + case 64000: width = 320; height = 200; break; + } + + uint8_t *image = new uint8_t[width * height * 4]; + + const unsigned char *bytes = (const unsigned char *)data; + const uint8_t *palette = GetPalette(options); + + for (int y = 0; ycolumnofs; + int x, x2 = realpatch->width; + + if (realpatch->height == 256) + { + for (x = 0; x < x2; ++x) + { + const column_t *col = (column_t*)((BYTE*)realpatch + cofs[x]); + if (col->topdelta != 0 || col->length != 0) + { + return false; // It's not bad! + } + col = (column_t *)((BYTE *)col + 256 + 4); + if (col->topdelta != 0xFF) + { + return false; // More than one post in a column! + } + } + if (x == x2) + { + return true; + } + } + return false; +} + +bool PatchToPng(int options, const uint8_t *ldata, int length, const char *pngpath) +{ + const unsigned char *palette = GetPalette(options); + + const column_t *maxcol; + int x; + + const patch_t *patch = (const patch_t *)ldata; + + int height = patch->height; + int width = patch->width; + uint8_t *image = new uint8_t[width * height * 4]; + memset(image, 0, width*height * 4); + + maxcol = (const column_t *)((const BYTE *)patch + length - 3); + + // detect broken patches + if (IsHack(ldata, length)) + { + // Draw the image to the buffer + for (x = 0; x < width; ++x) + { + const BYTE *in = (const BYTE *)patch + patch->columnofs[x] + 3; + + for (int y = height; y > 0; --y) + { + unsigned char *ddata = &image[(x + y*width) * 4]; + ddata[0] = palette[*in * 3 + 0]; + ddata[1] = palette[*in * 3 + 1]; + ddata[2] = palette[*in * 3 + 2]; + ddata[3] = 255; + in++; + } + } + } + else + { + + // Draw the image to the buffer + for (x = 0; x < width; ++x) + { + const column_t *column = (const column_t *)((const BYTE *)patch + patch->columnofs[x]); + int top = -1; + + while (column < maxcol && column->topdelta != 0xFF) + { + if (column->topdelta <= top) + { + top += column->topdelta; + } + else + { + top = column->topdelta; + } + + int len = column->length; + + if (len != 0) + { + if (top + len > height) // Clip posts that extend past the bottom + { + len = height - top; + } + if (len > 0) + { + const BYTE *in = (const BYTE *)column + 3; + for (int i = 0; i < len; ++i) + { + unsigned char *ddata = &image[(x + (top+i)*width) * 4]; + ddata[0] = palette[in[i] * 3 + 0]; + ddata[1] = palette[in[i] * 3 + 1]; + ddata[2] = palette[in[i] * 3 + 2]; + ddata[3] = 255; + } + } + } + column = (const column_t *)((const BYTE *)column + column->length + 4); + } + } + } + + WritePNG(image, width, height, patch->leftoffset, patch->topoffset, pngpath); + delete[]image; + return true; +} + + +struct DoomSoundHead +{ + unsigned short three; + unsigned short samplerate; + unsigned short samples; + unsigned short zero; +}; + +struct WavSoundHead +{ + char riff[4]; + unsigned size; + char wave[4]; + char fmt[4]; + unsigned x10; + unsigned short x01; + unsigned short channels; + unsigned rate; + unsigned bytesPerSecond; + unsigned short bytesPerSample; + unsigned short bitsPerSample; + char data[4]; + unsigned length; + + WavSoundHead() + { + riff[0] = 'R'; riff[1] = 'I'; riff[2] = 'F'; riff[3] = 'F'; + wave[0] = 'W'; wave[1] = 'A'; wave[2] = 'V'; wave[3] = 'E'; + fmt[0] = 'f'; fmt[1] = 'm'; fmt[2] = 't'; fmt[3] = ' '; + x10 = 0x10; // length of format chunk + x01 = 0x01; + channels = 1; + bytesPerSample = 1; // 8 bit mono + bitsPerSample = 8; // 8 bit mono + data[0] = 'd'; data[1] = 'a'; data[2] = 't'; data[3] = 'a'; + } + +}; + +bool DoomSndToWav(const uint8_t *data, int length, const char *filename) +{ + + DoomSoundHead *head = (DoomSoundHead *)data; + uint32_t headSize = sizeof(DoomSoundHead); + uint32_t dataSize = length - headSize; + + WavSoundHead wh; + wh.length = dataSize; + wh.size = dataSize + 36; + wh.rate = wh.bytesPerSecond = head->samplerate; + + FILE *f = fopen(filename, "wb"); + if (f != NULL) + { + fwrite(&wh, 1, sizeof(WavSoundHead), f); + fwrite(data + headSize, 1, dataSize, f); + fclose(f); + return true; + } + return false; +}; diff --git a/copying.txt b/copying.txt new file mode 100644 index 0000000..94a9ed0 --- /dev/null +++ b/copying.txt @@ -0,0 +1,674 @@ + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, 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 +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If 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 convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU 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 +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "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 PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM 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 PROGRAM (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 PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state 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 program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + Copyright (C) + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +. + + The GNU General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications with +the library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. But first, please read +. diff --git a/fileformat.cpp b/fileformat.cpp new file mode 100644 index 0000000..d390502 --- /dev/null +++ b/fileformat.cpp @@ -0,0 +1,645 @@ +// +//--------------------------------------------------------------------------- +// +// Copyright(C) 2016 Christoph Oelckers +// All rights reserved. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 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 General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see http://www.gnu.org/licenses/ +// +//-------------------------------------------------------------------------- +// + +#include +#include +#include +#include +#include "wadext.h" +#include "fileformat.h" + +#pragma warning(disable:4996) + +//============================================================================ +// +// +// +//============================================================================ + +struct Detector +{ + bool(*checker)(const uint8_t *pbuffer, int len); + FileType ret; +}; + +bool IsMagic(const uint8_t *s, const char *magic, int len = 4) +{ + return !memcmp(s, magic, len); +} + +//============================================================================ +// +// Doom patch detector +// +//============================================================================ + +bool isPatch(const uint8_t *data, int length) +{ + if (length < 13) return false; // minimum length of a valid Doom patch + + const patch_t *foo = (const patch_t *)data; + + int height = foo->height; + int width = foo->width; + + if (height <= 4096 && width <= 4096 && width < length / 4 && abs(foo->leftoffset) < 4096 && abs(foo->topoffset) < 4096) + { + // The dimensions seem like they might be valid for a patch, so + // check the column directory for extra security. At least one + // column must begin exactly at the end of the column directory, + // and none of them must point past the end of the patch. + bool gapAtStart = true; + int x; + + for (x = 0; x < width; ++x) + { + uint32_t ofs = foo->columnofs[x]; + if (ofs == (uint32_t)width * 4 + 8) + { + gapAtStart = false; + } + else if (ofs >= (uint32_t)length) // Need one byte for an empty column (but there's patches that don't know that!) + { + return false; + } + } + return !gapAtStart; + } + return false; +} + +//============================================================================ +// +// +// +//============================================================================ + +bool isPng(const uint8_t *data, int length) +{ + const uint32_t *dwdata = (const uint32_t*)data; + if (length < 16) return false; + // Check up to the IHDR so that this rejects Apple's homegrown image format that masquerades as a PNG but isn't one. + return + dwdata[0] == MAKE_ID(137, 'P', 'N', 'G') && + dwdata[1] == MAKE_ID(13, 10, 26, 10) && + dwdata[2] == MAKE_ID(0, 0, 0, 13) && + dwdata[3] == MAKE_ID('I', 'H', 'D', 'R'); +} + +//============================================================================ +// +// +// +//============================================================================ + +bool isJpg(const uint8_t *data, int length) +{ + return (*data == 0xff && IsMagic(data + 6, "JFIF")); +} + +//============================================================================ +// +// +// +//============================================================================ + +bool isTGA(const uint8_t *data, int length) +{ + const TGAHeader *hdr = (const TGAHeader *)data; + if (length < sizeof(TGAHeader)) return false; + + // Not much that can be done here because TGA does not have a proper + // header to be identified with. + if (hdr->has_cm != 0 && hdr->has_cm != 1) return false; + if (hdr->width <= 0 || hdr->height <= 0) return false; + if (hdr->bpp != 8 && hdr->bpp != 15 && hdr->bpp != 16 && hdr->bpp != 24 && hdr->bpp != 32) return false; + if (hdr->img_type <= 0 || hdr->img_type > 11) return false; + if (hdr->img_type >= 4 && hdr->img_type <= 8) return false; + if ((hdr->img_desc & 16) != 0) return false; + return true; +} + +//============================================================================ +// +// +// +//============================================================================ + +bool isPCX(const uint8_t *data, int length) +{ + const PCXHeader *hdr = (const PCXHeader*)data; + + if (length < sizeof(PCXHeader)) return false; + + if (hdr->manufacturer != 10 || hdr->encoding != 1) return false; + if (hdr->version != 0 && hdr->version != 2 && hdr->version != 3 && hdr->version != 4 && hdr->version != 5) return false; + if (hdr->bitsPerPixel != 1 && hdr->bitsPerPixel != 8) return false; + if (hdr->bitsPerPixel == 1 && hdr->numColorPlanes != 1 && hdr->numColorPlanes != 4) return false; + if (hdr->bitsPerPixel == 8 && hdr->uint8_tsPerScanLine != ((hdr->xmax - hdr->xmin + 2)&~1)) return false; + + for (int i = 0; i < 54; i++) + { + if (hdr->padding[i] != 0) return false; + } + return true; +} + +//============================================================================ +// +// +// +//============================================================================ + +bool isDDS(const uint8_t *data, int length) +{ + const DDSFileHeader *Header = (const DDSFileHeader*)data; + if (length < sizeof(DDSFileHeader)) return false; + + return Header->Magic == ID_DDS && + (Header->Desc.Size == sizeof(DDSURFACEDESC2) || Header->Desc.Size == ID_DDS) && + Header->Desc.PixelFormat.Size == sizeof(DDPIXELFORMAT) && + (Header->Desc.Flags & (DDSD_CAPS | DDSD_PIXELFORMAT | DDSD_WIDTH | DDSD_HEIGHT)) == (DDSD_CAPS | DDSD_PIXELFORMAT | DDSD_WIDTH | DDSD_HEIGHT) && + Header->Desc.Width != 0 && + Header->Desc.Height != 0; +} + + +//============================================================================ +// +// +// +//============================================================================ + +bool isImgz(const uint8_t *pbuffer, int len) +{ + return IsMagic(pbuffer, "IMGZ"); +} + +//============================================================================ +// +// +// +//============================================================================ + +bool isMid(const uint8_t *pbuffer, int len) +{ + return IsMagic(pbuffer, "MThd"); +} + +//============================================================================ +// +// +// +//============================================================================ + +bool isMus(const uint8_t *pbuffer, int len) +{ + return IsMagic(pbuffer, "MUS\x1a"); +} + +//============================================================================ +// +// +// +//============================================================================ + +bool isIt(const uint8_t *pbuffer, int len) +{ + return IsMagic(pbuffer, "IMPM"); +} + +//============================================================================ +// +// +// +//============================================================================ + +bool isS3m(const uint8_t *pbuffer, int len) +{ + return len >= 0x30 && IsMagic(pbuffer+0x3c, "SCRM"); +} + +//============================================================================ +// +// +// +//============================================================================ + +bool isXt(const uint8_t *pbuffer, int len) +{ + return len >= 0x34 && IsMagic(pbuffer + 0x26, "FastTracker", 11); +} + +//============================================================================ +// +// +// +//============================================================================ + +bool isMod(const uint8_t *pbuffer, int len) +{ + if (len < 1084) return false; + + const uint8_t *s = pbuffer + 1080; + return (IsMagic(s, "M.K.") || IsMagic(s, "M!K!") || IsMagic(s, "M&K!") || IsMagic(s, "N.T.") || + IsMagic(s, "CD81") || IsMagic(s, "OKTA") || IsMagic(s, "16CN") || IsMagic(s, "32CN") || + ((s[0] == 'F') && (s[1] == 'L') && (s[2] == 'T') && (s[3] >= '4') && (s[3] <= '9')) || + ((s[0] >= '2') && (s[0] <= '9') && (s[1] == 'C') && (s[2] == 'H') && (s[3] == 'N')) || + ((s[0] == '1') && (s[1] >= '0') && (s[1] <= '9') && (s[2] == 'C') && (s[3] == 'H')) || + ((s[0] == '2') && (s[1] >= '0') && (s[1] <= '9') && (s[2] == 'C') && (s[3] == 'H')) || + ((s[0] == '3') && (s[1] >= '0') && (s[1] <= '2') && (s[2] == 'C') && (s[3] == 'H')) || + ((s[0] == 'T') && (s[1] == 'D') && (s[2] == 'Z') && (s[3] >= '4') && (s[3] <= '9')) + ); +} + +//============================================================================ +// +// +// +//============================================================================ + +bool isSpc(const uint8_t *pbuffer, int len) +{ + return len >= 27 && IsMagic(pbuffer, "SNES - SPC700 Sound File Data v0.30\x1A\x1A", 27); +} + +//============================================================================ +// +// +// +//============================================================================ + +bool isHmi(const uint8_t *pbuffer, int len) +{ + return len >= 12 && IsMagic(pbuffer, "HMI-MIDISONG", 12); +} + +//============================================================================ +// +// +// +//============================================================================ + +bool isHmp(const uint8_t *pbuffer, int len) +{ + return len >= 12 && IsMagic(pbuffer, "HMIMIDIP", 8); +} + +//============================================================================ +// +// +// +//============================================================================ + +bool isXmi(const uint8_t *pbuffer, int len) +{ + return len >= 12 && + (IsMagic(pbuffer, "FORM") && IsMagic(pbuffer + 8, "XDIR") || + IsMagic(pbuffer, "FORM") && IsMagic(pbuffer + 8, "XMID") || + IsMagic(pbuffer, "CAT ") && IsMagic(pbuffer + 8, "XMID")); +} + +//============================================================================ +// +// Doom sound format has virtually no reliable features to identify. +// All we can go by is to check if the little info in the header makes sense. +// +//============================================================================ + +bool isDoomSound(const uint8_t *data, int len) +{ + if (len < 8) return false; + uint32_t dmxlen = GetInt(data + 4); + int32_t freq = GetShort(data + 2) & 0xffff; + return data[0] == 3 && data[1] == 0 /*&& (freq == 0 || freq == 11025 || freq == 22050 || freq == 44100)*/ && dmxlen <= unsigned(len - 8); +} + +//============================================================================ +// +// +// +//============================================================================ + +bool isWav(const uint8_t *pbuffer, int len) +{ + return len > 16 && IsMagic(pbuffer, "RIFF") && IsMagic(pbuffer + 8, "WAVE"); +} + +//============================================================================ +// +// +// +//============================================================================ + +bool isAiff(const uint8_t *pbuffer, int len) +{ + return len > 12 && IsMagic(pbuffer, "FORM") && IsMagic(pbuffer+8, "AIFF"); +} + +bool isAifc(const uint8_t *pbuffer, int len) +{ + return len > 12 && IsMagic(pbuffer, "FORM") && IsMagic(pbuffer + 8, "AIFC"); +} + +//============================================================================ +// +// +// +//============================================================================ + +bool isFlac(const uint8_t *pbuffer, int len) +{ + return IsMagic(pbuffer, "fLaC"); +} + +//============================================================================ +// +// +// +//============================================================================ + +bool isOgg(const uint8_t *pbuffer, int len) +{ + return IsMagic(pbuffer, "OggS"); +} + +//============================================================================ +// +// Almost nothing to check here, so this format is checked last of all. +// +//============================================================================ + +bool isMP3(const uint8_t *data, int len) +{ + if (IsMagic(data, "ID3\3")) return true; + return data[0] == 0xff && ((data[1] & 0xfe) == 0xfa/*MPEG-1*/ || (data[1] & 0xfe) == 0xf2/*MPEG-2*/); +} + + + +//============================================================================ +// +// +// +//============================================================================ + +bool isVoc(const uint8_t *pbuffer, int len) +{ + return len > 19 && IsMagic(pbuffer, "Creative Voice File", 19); +} + +//============================================================================ +// +// +// +//============================================================================ + +bool isFon1(const uint8_t *pbuffer, int len) +{ + return IsMagic(pbuffer, "FON1") && pbuffer[5] == 0 && pbuffer[7] == 0; +} + +//============================================================================ +// +// +// +//============================================================================ + +bool isFon2(const uint8_t *pbuffer, int len) +{ + return IsMagic(pbuffer, "FON2") && pbuffer[5] == 0 && pbuffer[6] <= pbuffer[7]; +} + +//============================================================================ +// +// +// +//============================================================================ + +bool isBmf(const uint8_t *data, int len) +{ + return (data[0] == 0xE1 && data[1] == 0xE6 && data[2] == 0xD5 && data[3] == 0x1A); +} + +//========================================================================== +// +// +// +//========================================================================== + +bool isKVX(const uint8_t *data, int length) +{ + // This isn't reliable enough. + return false; +#if 0 + const kvxslab_t *slabs[MAXVOXMIPS]; + const uint8_t *rawmip; + int mip, maxmipsize; + int i, j, n; + + const uint8_t *rawvoxel = data; + int voxelsize = length - 1; + + // Oh, KVX, why couldn't you have a proper header? We'll just go through + // and collect each MIP level, doing lots of range checking, and if the + // last one doesn't end exactly 768 uint8_ts before the end of the file, + // we'll reject it. + + for (mip = 0, rawmip = rawvoxel, maxmipsize = voxelsize - 768 - 4; + mip < MAXVOXMIPS; + mip++) + { + int numuint8_ts = GetInt(rawmip); + if (numuint8_ts > maxmipsize || numuint8_ts < 24) + { + break; + } + rawmip += 4; + + FVoxelMipLevel mipl; + + // Load header data. + mipl.SizeX = GetInt(rawmip + 0); + mipl.SizeY = GetInt(rawmip + 4); + mipl.SizeZ = GetInt(rawmip + 8); + if (mipl.SizeX > 255 || mipl.SizeY > 255 || mipl.SizeZ > 255) return false; + + // How much space do we have for voxdata? + int offsetsize = (mipl.SizeX + 1) * 4 + mipl.SizeX * (mipl.SizeY + 1) * 2; + int voxdatasize = numuint8_ts - 24 - offsetsize; + if (voxdatasize < 0) + { // Clearly, not enough. + return false; + } + if (voxdatasize != 0) + { // This mip level is not empty. + // Allocate slab data space. + mipl.OffsetX = new int[(numuint8_ts - 24 + 3) / 4]; + mipl.OffsetXY = (short *)(mipl.OffsetX + mipl.SizeX + 1); + mipl.SlabData = (uint8_t *)(mipl.OffsetXY + mipl.SizeX * (mipl.SizeY + 1)); + + // Load x offsets. + for (i = 0, n = mipl.SizeX; i <= n; ++i) + { + // The X offsets stored in the KVX file are relative to the start of the + // X offsets array. Make them relative to voxdata instead. + mipl.OffsetX[i] = GetInt(rawmip + 24 + i * 4) - offsetsize; + } + + // The first X offset must be 0 (since we subtracted offsetsize), according to the spec: + // NOTE: xoffset[0] = (xsiz+1)*4 + xsiz*(ysiz+1)*2 (ALWAYS) + if (mipl.OffsetX[0] != 0) + { + return false; + } + // And the final X offset must point just past the end of the voxdata. + if (mipl.OffsetX[mipl.SizeX] != voxdatasize) + { + break; + } + + // Load xy offsets. + i = 24 + i * 4; + for (j = 0, n *= mipl.SizeY + 1; j < n; ++j) + { + mipl.OffsetXY[j] = GetShort(rawmip + i + j * 2); + } + + // Ensure all offsets are within bounds. + for (i = 0; i < (int)mipl.SizeX; ++i) + { + int xoff = mipl.OffsetX[i]; + for (j = 0; j < (int)mipl.SizeY; ++j) + { + int yoff = mipl.OffsetXY[(mipl.SizeY + 1) * i + j]; + if (unsigned(xoff + yoff) > unsigned(voxdatasize)) + { + return false; + } + } + } + + // Record slab location for the end. + slabs[mip] = (kvxslab_t *)(rawmip + 24 + offsetsize); + } + + // Time for the next mip Level. + rawmip += numuint8_ts; + maxmipsize -= numuint8_ts + 4; + } + // Did we get any mip levels, and if so, does the last one leave just + // enough room for the palette after it? + if (mip == 0 || rawmip != rawvoxel + voxelsize - 768) + { + return false; + } + return true; +#endif +} + + +// formats are listed in descending order of detection reliability, not grouped by types! +static Detector detectors[] = +{ + // format which have an identifier at the beginning of the file + { isPng,{ FT_PNG, ".PNG" } }, + { isJpg,{ FT_JPG, ".JPG" }}, + { isMid, { FT_MID, ".MID" }}, + { isOgg, { FT_OGGBASE, ".OGG" }}, + { isMus, { FT_MUS, ".MUS" }}, + { isFlac,{ FT_FLAC, ".FLAC" }}, + { isAiff, { FT_AIFF, ".AIF" }}, + { isAifc, { FT_AIFF, ".AIFC" }}, + { isIt,{ FT_AIFF, ".IT" } }, + { isSpc,{ FT_SPC, ".SPC" } }, + { isVoc,{ FT_VOC, ".VOC" }}, + { isWav,{ FT_WAV, ".WAV" } }, + { isImgz,{ FT_VOC, ".IMGZ" } }, + { isHmi, { FT_HMI, ".HMI" }}, + { isHmp,{ FT_HMP, ".HMP" } }, + { isXmi,{ FT_XMI, ".XMI" } }, + { isBmf,{ FT_BMF, ".BMF" } }, + { isFon1,{ FT_FON1, ".FON1" } }, + { isFon2,{ FT_FON2, ".FON2" } }, + + // formats which have an identifier near the beginning of a file + { isS3m, { FT_S3M, ".S3M" }}, + { isXt, { FT_XM, ".XM" }}, + + // format which can only be detected by checking the header (MOD has an identifier in the middle, but too far from the start to be considered safe.) + { isTGA,{ FT_TGA, ".TGA" } }, + { isPCX,{ FT_PCX, ".PCX" } }, + { isDDS,{ FT_DDS, ".DDS" } }, + { isMod,{ FT_MOD, ".MOD" } }, + { isPatch,{ FT_DOOMGFX, ".GFX" } }, + + // formats which cannot easily be detected reliably + { isKVX, { FT_KVX, ".KVX" }}, + { isDoomSound, { FT_DOOMSND, ".DMX" }}, + { isMP3,{ FT_MP3BASE, ".MP3" } }, +}; + + +FileType resolveUnknown(FileType ft, const char *name, int length) +{ + switch (ft.type) + { + case FT_OGGBASE: + case FT_MP3BASE: + // todo: Try to be a bit smarter about deciding whether this is a music or sound lump. + if (!strnicmp(name, "DS", 2)) ft.type |= FG_SND; + else if (!strnicmp(name, "D_", 2)) ft.type |= FG_MUS; + else if (length < 400000) ft.type |= FG_SND; + else ft.type |= FG_MUS; + break; + } + return ft; +} + +FileType IdentifyFileType(const char *name, const uint8_t *data, int length) +{ + if (length > 4) + { + for (auto &t : detectors) + { + if (t.checker(data, length)) + { + return resolveUnknown(t.ret, name, length); + } + } + } + if (length == 0) + { + return{ FT_BINARY, ".LMP" }; + } + // Identify pure text files. If any character < 32, except \t, \n, \r is present, a binary file is assumed + for (int i = 0; i < length; i++) + { + if (data[i] < 9 || data[i] == 11 || data[i] == 12 || (data[i] >= 14 && data[i] < 32)) + { + return{ FT_BINARY, ".LMP" }; + } + } + return{ FT_TEXT, ".TXT" }; +} + + diff --git a/fileformat.h b/fileformat.h new file mode 100644 index 0000000..07db194 --- /dev/null +++ b/fileformat.h @@ -0,0 +1,249 @@ +#include + +enum +{ + FG_UNKNOWN = 0, + FG_GFX = 0x10000, + FG_SND = 0x20000, + FG_MUS = 0x30000, + FG_VOX = 0x40000, + FG_FONT = 0x50000, + FG_OTHER = 0x60000, + FG_MASK = 0xff0000, +}; + +enum +{ + FT_MP3BASE = 1, + FT_OGGBASE = 2, + + FT_DOOMGFX = FG_GFX + 1, + FT_PNG = FG_GFX + 2, + FT_JPG = FG_GFX + 3, + FT_PCX = FG_GFX + 4, + FT_TGA = FG_GFX + 5, + FT_DDS = FG_GFX + 6, + FT_IMGZ = FG_GFX + 7, + + FT_MP3 = FG_SND + FT_MP3BASE, + FT_OGG = FG_SND + FT_OGGBASE, + FT_DOOMSND = FG_SND + 3, + FT_WAV = FG_SND + 4, + FT_VOC = FG_SND + 5, + FT_AIFF = FG_SND + 6, + FT_FLAC = FG_SND + 7, + + FT_MP3MUS = FG_MUS + FT_MP3BASE, + FT_OGGMUS = FG_MUS + FT_OGGBASE, + FT_MUS = FG_MUS + 3, + FT_MID = FG_MUS + 4, + FT_XMI = FG_MUS + 5, + FT_HMP = FG_MUS + 6, + FT_HMI = FG_MUS + 7, + FT_SPC = FG_MUS + 8, + FT_MOD = FG_MUS + 9, + FT_IT = FG_MUS + 10, + FT_XM = FG_MUS + 11, + FT_S3M = FG_MUS + 12, + // Todo: add more specialized formats if needed + + FT_KVX = FG_VOX + 1, + + FT_FON1 = FG_FONT + 1, + FT_FON2 = FG_FONT + 2, + FT_BMF = FG_FONT + 3, + + FT_TEXT = FG_OTHER + 1, + FT_BINARY = FG_OTHER + 2, + FT_LEVEL = FG_OTHER + 3, + FT_MARKER = FG_OTHER + 4, +}; + +struct FileType +{ + int type; + const char *extension; +}; + +// various file format headers for type detection. + +// posts are runs of non masked source pixels +struct column_t +{ + BYTE topdelta; // -1 is the last post in a column + BYTE length; // length data bytes follows +}; + +struct patch_t +{ + uint16_t width; // bounding box size + uint16_t height; + int16_t leftoffset; // pixels to the left of origin + int16_t topoffset; // pixels below the origin + uint32_t columnofs[1]; // only [width] used + // the [0] is &columnofs[width] +}; + +#pragma pack(1) + +struct TGAHeader +{ + uint8_t id_len; + uint8_t has_cm; + uint8_t img_type; + int16_t cm_first; + int16_t cm_length; + uint8_t cm_size; + + int16_t x_origin; + int16_t y_origin; + int16_t width; + int16_t height; + uint8_t bpp; + uint8_t img_desc; +}; + +struct PCXHeader +{ + uint8_t manufacturer; + uint8_t version; + uint8_t encoding; + uint8_t bitsPerPixel; + + uint16_t xmin, ymin; + uint16_t xmax, ymax; + uint16_t horzRes, vertRes; + + uint8_t palette[48]; + uint8_t reserved; + uint8_t numColorPlanes; + + uint16_t uint8_tsPerScanLine; + uint16_t paletteType; + uint16_t horzSize, vertSize; + + uint8_t padding[54]; + +}; + +#ifndef __BIG_ENDIAN__ +#define MAKE_ID(a,b,c,d) ((DWORD)((a)|((b)<<8)|((c)<<16)|((d)<<24))) +#else +#define MAKE_ID(a,b,c,d) ((DWORD)((d)|((c)<<8)|((b)<<16)|((a)<<24))) +#endif + +#pragma pack() + +#define ID_DDS MAKE_ID('D','D','S',' ') +#define ID_DXT1 MAKE_ID('D','X','T','1') +#define ID_DXT2 MAKE_ID('D','X','T','2') +#define ID_DXT3 MAKE_ID('D','X','T','3') +#define ID_DXT4 MAKE_ID('D','X','T','4') +#define ID_DXT5 MAKE_ID('D','X','T','5') + +// Bits in dwFlags +#define DDSD_CAPS 0x00000001 +#define DDSD_HEIGHT 0x00000002 +#define DDSD_WIDTH 0x00000004 +#define DDSD_PITCH 0x00000008 +#define DDSD_PIXELFORMAT 0x00001000 +#define DDSD_MIPMAPCOUNT 0x00020000 +#define DDSD_LINEARSIZE 0x00080000 +#define DDSD_DEPTH 0x00800000 + +// Bits in ddpfPixelFormat +#define DDPF_ALPHAPIXELS 0x00000001 +#define DDPF_FOURCC 0x00000004 +#define DDPF_RGB 0x00000040 + +// Bits in DDSCAPS2.dwCaps1 +#define DDSCAPS_COMPLEX 0x00000008 +#define DDSCAPS_TEXTURE 0x00001000 +#define DDSCAPS_MIPMAP 0x00400000 + +// Bits in DDSCAPS2.dwCaps2 +#define DDSCAPS2_CUBEMAP 0x00000200 +#define DDSCAPS2_CUBEMAP_POSITIVEX 0x00000400 +#define DDSCAPS2_CUBEMAP_NEGATIVEX 0x00000800 +#define DDSCAPS2_CUBEMAP_POSITIVEY 0x00001000 +#define DDSCAPS2_CUBEMAP_NEGATIVEY 0x00002000 +#define DDSCAPS2_CUBEMAP_POSITIVEZ 0x00004000 +#define DDSCAPS2_CUBEMAP_NEGATIZEZ 0x00008000 +#define DDSCAPS2_VOLUME 0x00200000 + +struct DDPIXELFORMAT +{ + uint32_t Size; // Must be 32 + uint32_t Flags; + uint32_t FourCC; + uint32_t RGBBitCount; + uint32_t RBitMask, GBitMask, BBitMask; + uint32_t RGBAlphaBitMask; +}; + +struct DDCAPS2 +{ + uint32_t Caps1, Caps2; + uint32_t Reserved[2]; +}; + +struct DDSURFACEDESC2 +{ + uint32_t Size; // Must be 124. DevIL claims some writers set it to 'DDS ' instead. + uint32_t Flags; + uint32_t Height; + uint32_t Width; + union + { + int32_t Pitch; + uint32_t LinearSize; + }; + uint32_t Depth; + uint32_t MipMapCount; + uint32_t Reserved1[11]; + DDPIXELFORMAT PixelFormat; + DDCAPS2 Caps; + uint32_t Reserved2; +}; + +struct DDSFileHeader +{ + uint32_t Magic; + DDSURFACEDESC2 Desc; +}; + +#define MAXVOXMIPS 5 + +struct kvxslab_t +{ + uint8_t ztop; // starting z coordinate of top of slab + uint8_t zleng; // # of uint8_ts in the color array - slab height + uint8_t backfacecull; // low 6 bits tell which of 6 faces are exposed + uint8_t col[1/*zleng*/];// color data from top to bottom +}; + +struct FVoxelMipLevel +{ + unsigned int SizeX; + unsigned int SizeY; + unsigned int SizeZ; + int *OffsetX; + short *OffsetXY; + uint8_t *SlabData; +}; + +inline int GetInt(const unsigned char *foo) +{ + return *(const int *)foo; +} + +inline int GetShort(const unsigned char *foo) +{ + return *(const short *)foo; +} + +FileType IdentifyFileType(const char *name, const uint8_t *data, int length); + +bool FlatToPng(int options, const uint8_t *data, int length, const char *pngpath); +bool PatchToPng(int options, const uint8_t *ldata, int length, const char *pngpath); +bool DoomSndToWav(const uint8_t *data, int length, const char *filename); diff --git a/main.cpp b/main.cpp new file mode 100644 index 0000000..a61a364 --- /dev/null +++ b/main.cpp @@ -0,0 +1,93 @@ +// +//--------------------------------------------------------------------------- +// +// Copyright(C) 2016 Christoph Oelckers +// All rights reserved. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 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 General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see http://www.gnu.org/licenses/ +// +//-------------------------------------------------------------------------- +// + +#include +#include +#include +#include +#include +#include "wadext.h" + +void BuildWad(char * wadfilename,int options); +void ExtractWad(char * wadfilename,int options); +void ConvertTextureX(); + +int ParseOptions(int argc,char ** argv,int & index) +{ + struct parseinf + { + char * opt; + int flag; + int argc; + } pi[]={ + {"-nogfxconvert",NO_CONVERT_GFX}, + {"-nosndconvert",NO_CONVERT_SND }, + {"-heretic",DO_HERETIC_PAL}, + {"-hexen",DO_HEXEN_PAL}, + {"-strife",DO_STRIFE}, + {"-strip",DO_STRIP}, + {NULL,0} + }; + + int def_opt=0; + + for(int i=2;i1 && !strcmp(argv[1], "-tx")) + { + ConvertTextureX(); + } + else if (argc>2) + { + int index; + int f=ParseOptions(argc,argv,index); + ExtractWad(argv[1],f); + } + else if (argc > 1) + { + ExtractWad(argv[1],DO_STRIP); + } + else + { + printf("WadExt v1.0 (c) 2016 Christoph Oelckers\nUsage: wadext [options] filename\n"); + printf("Options:\n"); + printf(" -nogfxconvert : Leave Doom format patches and flats in their original form, if not specified they will be converted to PNG.\n"); + printf(" -nosndconvert : Leave Doom format sounds in their original form, if not specified they will be converted to WAV.\n"); + printf(" -heretic, -hexen, -strife: Force usage of the named game's palette if the WAD does not contain one. Default is Doom palette\n"); + printf(" -strip: Remove node lumps from extracted maps.\n"); + printf(" -tx: Converts a set of TEXTURE1/TEXTURE2/PNAMES in the current directory to a textures.txt file.\n"); + } + return 0; +} \ No newline at end of file diff --git a/resourcefile.h b/resourcefile.h new file mode 100644 index 0000000..2e47dab --- /dev/null +++ b/resourcefile.h @@ -0,0 +1,88 @@ +#ifndef __RESOURCEFILE_H +#define __RESOURCEFILE_H + +#include +#include +#include "tarray.h" + +//========================================================================== +// +// +// +//========================================================================== + +struct CWADLump +{ + uint32_t offset; + uint32_t length; + char name[9]; +}; + +class CWADFile +{ + TArray lumps; + FILE *m_File; + + char * m_Filename; + int m_NumLumps; + uint64_t m_LumpStart; + +public: + CWADFile(const char * filename); + CWADFile(const char * name, const char * memory); + ~CWADFile(void); + + const char * GetName() const + { + return m_Filename; + } + int NumLumps() + { + return m_NumLumps; + } + + virtual int FindLump(const char * name); + virtual int GetLumpSize(int lump); + virtual void * ReadLump(int lump); + virtual void ReadLump(int lump, void * buffer); + virtual const char * GetLumpName(int lump); +}; + +extern CWADFile *mainwad; + +struct WadItemList +{ + int mLump; + TArray mData; + + WadItemList(int lump) + { + mLump = lump; + } + + char * Address() + { + if (mData.Size() != Length()) + { + mData.Resize(Length()); + } + mainwad->ReadLump(mLump, &mData[0]); + return &mData[0]; + } + + int Length() const + { + return mainwad->GetLumpSize(mLump); + } + + void Release() + { + mData.Clear(); + } + + const char * Name() const { return mainwad->GetLumpName(mLump); } +}; + +void OpenMainWad(char * filename); + +#endif \ No newline at end of file diff --git a/tarray.h b/tarray.h new file mode 100644 index 0000000..1047d00 --- /dev/null +++ b/tarray.h @@ -0,0 +1,1125 @@ +/* +** tarray.h +** Templated, automatically resizing array +** +**--------------------------------------------------------------------------- +** Copyright 1998-2007 Randy Heit +** All rights reserved. +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** +** 1. Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** 2. Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in the +** documentation and/or other materials provided with the distribution. +** 3. The name of the author may not be used to endorse or promote products +** derived from this software without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR +** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, +** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +**--------------------------------------------------------------------------- +** +*/ + +#ifndef __TARRAY_H__ +#define __TARRAY_H__ + +#include +#include +#include +#include +#include + +#if !defined(_WIN32) +#include // for intptr_t +#else +#include // for mingw +#endif + +class FArchive; + + +template class TIterator +{ +public: + TIterator(T* ptr = nullptr) { m_ptr = ptr; } + bool operator==(const TIterator& other) const { return (m_ptr == other.m_ptr); } + bool operator!=(const TIterator& other) const { return (m_ptr != other.m_ptr); } + TIterator &operator++() { ++m_ptr; return (*this); } + T &operator*() { return *m_ptr; } + const T &operator*() const { return *m_ptr; } + T* operator->() { return m_ptr; } + +protected: + T* m_ptr; +}; + + +// TArray ------------------------------------------------------------------- + +// Must match TArray's layout. +struct FArray +{ + void *Array; + unsigned int Most; + unsigned int Count; +}; + +// T is the type stored in the array. +// TT is the type returned by operator(). +template +class TArray +{ + template friend FArchive &operator<< (FArchive &arc, TArray &self); + +public: + + typedef TIterator iterator; + typedef TIterator const_iterator; + + iterator begin() + { + return &Array[0]; + } + const_iterator begin() const + { + return &Array[0]; + } + const_iterator cbegin() const + { + return &Array[0]; + } + + iterator end() + { + return &Array[Count]; + } + const_iterator end() const + { + return &Array[Count]; + } + const_iterator cend() const + { + return &Array[Count]; + } + + + + //////// + // This is a dummy constructor that does nothing. The purpose of this + // is so you can create a global TArray in the data segment that gets + // used by code before startup without worrying about the constructor + // resetting it after it's already been used. You MUST NOT use it for + // heap- or stack-allocated TArrays. + enum ENoInit + { + NoInit + }; + TArray (ENoInit dummy) + { + } + //////// + TArray () + { + Most = 0; + Count = 0; + Array = NULL; + } + TArray (int max) + { + Most = max; + Count = 0; + Array = (T *)malloc (sizeof(T)*max); + } + TArray (const TArray &other) + { + DoCopy (other); + } + TArray (TArray &&other) + { + Array = other.Array; other.Array = NULL; + Most = other.Most; other.Most = 0; + Count = other.Count; other.Count = 0; + } + TArray &operator= (const TArray &other) + { + if (&other != this) + { + if (Array != NULL) + { + if (Count > 0) + { + DoDelete (0, Count-1); + } + free (Array); + } + DoCopy (other); + } + return *this; + } + TArray &operator= (TArray &&other) + { + if (Array) + { + if (Count > 0) + { + DoDelete (0, Count-1); + } + free (Array); + } + Array = other.Array; other.Array = NULL; + Most = other.Most; other.Most = 0; + Count = other.Count; other.Count = 0; + return *this; + } + ~TArray () + { + if (Array) + { + if (Count > 0) + { + DoDelete (0, Count-1); + } + free (Array); + Array = NULL; + Count = 0; + Most = 0; + } + } + // Check equality of two arrays + bool operator==(const TArray &other) const + { + if (Count != other.Count) + { + return false; + } + for (unsigned int i = 0; i < Count; ++i) + { + if (Array[i] != other.Array[i]) + { + return false; + } + } + return true; + } + // Return a reference to an element + T &operator[] (size_t index) const + { + return Array[index]; + } + // Returns the value of an element + TT operator() (size_t index) const + { + return Array[index]; + } + // Returns a reference to the last element + T &Last() const + { + return Array[Count-1]; + } + + unsigned int Find(const T& item) const + { + unsigned int i; + for(i = 0;i < Count;++i) + { + if(Array[i] == item) + break; + } + return i; + } + + unsigned int Push (const T &item) + { + Grow (1); + ::new((void*)&Array[Count]) T(item); + return Count++; + } + bool Pop () + { + if (Count > 0) + { + Array[--Count].~T(); + return true; + } + return false; + } + bool Pop (T &item) + { + if (Count > 0) + { + item = Array[--Count]; + Array[Count].~T(); + return true; + } + return false; + } + void Delete (unsigned int index) + { + if (index < Count) + { + Array[index].~T(); + if (index < --Count) + { + memmove (&Array[index], &Array[index+1], sizeof(T)*(Count - index)); + } + } + } + + void Delete (unsigned int index, int deletecount) + { + if (index + deletecount > Count) + { + deletecount = Count - index; + } + if (deletecount > 0) + { + for (int i = 0; i < deletecount; i++) + { + Array[index + i].~T(); + } + Count -= deletecount; + if (index < Count) + { + memmove (&Array[index], &Array[index+deletecount], sizeof(T)*(Count - index)); + } + } + } + + // Inserts an item into the array, shifting elements as needed + void Insert (unsigned int index, const T &item) + { + if (index >= Count) + { + // Inserting somewhere past the end of the array, so we can + // just add it without moving things. + Resize (index + 1); + ::new ((void *)&Array[index]) T(item); + } + else + { + // Inserting somewhere in the middle of the array, + // so make room for it + Resize (Count + 1); + + // Now move items from the index and onward out of the way + memmove (&Array[index+1], &Array[index], sizeof(T)*(Count - index - 1)); + + // And put the new element in + ::new ((void *)&Array[index]) T(item); + } + } + void ShrinkToFit () + { + if (Most > Count) + { + Most = Count; + if (Most == 0) + { + if (Array != NULL) + { + free (Array); + Array = NULL; + } + } + else + { + DoResize (); + } + } + } + // Grow Array to be large enough to hold amount more entries without + // further growing. + void Grow (unsigned int amount) + { + if (Count + amount > Most) + { + const unsigned int choicea = Count + amount; + const unsigned int choiceb = Most = (Most >= 16) ? Most + Most / 2 : 16; + Most = (choicea > choiceb ? choicea : choiceb); + DoResize (); + } + } + // Resize Array so that it has exactly amount entries in use. + void Resize (unsigned int amount) + { + if (Count < amount) + { + // Adding new entries + Grow (amount - Count); + for (unsigned int i = Count; i < amount; ++i) + { + ::new((void *)&Array[i]) T; + } + } + else if (Count != amount) + { + // Deleting old entries + DoDelete (amount, Count - 1); + } + Count = amount; + } + // Reserves amount entries at the end of the array, but does nothing + // with them. + unsigned int Reserve (unsigned int amount) + { + Grow (amount); + unsigned int place = Count; + Count += amount; + for (unsigned int i = place; i < Count; ++i) + { + ::new((void *)&Array[i]) T; + } + return place; + } + unsigned int Size () const + { + return Count; + } + unsigned int Max () const + { + return Most; + } + void Clear () + { + if (Count > 0) + { + DoDelete (0, Count-1); + Count = 0; + } + } +private: + T *Array; + unsigned int Most; + unsigned int Count; + + void DoCopy (const TArray &other) + { + Most = Count = other.Count; + if (Count != 0) + { + Array = (T *)malloc (sizeof(T)*Most); + for (unsigned int i = 0; i < Count; ++i) + { + ::new(&Array[i]) T(other.Array[i]); + } + } + else + { + Array = NULL; + } + } + + void DoResize () + { + size_t allocsize = sizeof(T)*Most; + Array = (T *)realloc (Array, allocsize); + } + + void DoDelete (unsigned int first, unsigned int last) + { + assert (last != ~0u); + for (unsigned int i = first; i <= last; ++i) + { + Array[i].~T(); + } + } +}; + +// TDeletingArray ----------------------------------------------------------- +// An array that deletes its elements when it gets deleted. +template +class TDeletingArray : public TArray +{ +public: + TDeletingArray() : TArray() {} + TDeletingArray(TDeletingArray &&other) : TArray(std::move(other)) {} + TDeletingArray &operator=(TDeletingArray &&other) + { + TArray::operator=(std::move(other)); + return *this; + } + + ~TDeletingArray () + { + for (unsigned int i = 0; i < TArray::Size(); ++i) + { + if ((*this)[i] != NULL) + delete (*this)[i]; + } + } + void DeleteAndClear() + { + for (unsigned int i = 0; i < TArray::Size(); ++i) + { + if ((*this)[i] != NULL) + delete (*this)[i]; + } + this->Clear(); + } +}; + +// TAutoGrowArray ----------------------------------------------------------- +// An array with accessors that automatically grow the array as needed. +// It can still be used as a normal TArray if needed. ACS uses this for +// world and global arrays. + +template +class TAutoGrowArray : public TArray +{ +public: + T GetVal (unsigned int index) + { + if (index >= this->Size()) + { + return 0; + } + return (*this)[index]; + } + void SetVal (unsigned int index, T val) + { + if ((int)index < 0) return; // These always result in an out of memory condition. + + if (index >= this->Size()) + { + this->Resize (index + 1); + } + (*this)[index] = val; + } +}; + +// TMap --------------------------------------------------------------------- +// An associative array, similar in concept to the STL extension +// class hash_map. It is implemented using Lua's table algorithm: +/* +** Hash uses a mix of chained scatter table with Brent's variation. +** A main invariant of these tables is that, if an element is not +** in its main position (i.e. the `original' position that its hash gives +** to it), then the colliding element is in its own main position. +** Hence even when the load factor reaches 100%, performance remains good. +*/ +/****************************************************************************** +* Copyright (C) 1994-2006 Lua.org, PUC-Rio. All rights reserved. +* +* Permission is hereby granted, free of charge, to any person obtaining +* a copy of this software and associated documentation files (the +* "Software"), to deal in the Software without restriction, including +* without limitation the rights to use, copy, modify, merge, publish, +* distribute, sublicense, and/or sell copies of the Software, and to +* permit persons to whom the Software is furnished to do so, subject to +* the following conditions: +* +* The above copyright notice and this permission notice shall be +* included in all copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +******************************************************************************/ + +typedef unsigned int hash_t; + +template struct THashTraits +{ + // Returns the hash value for a key. + hash_t Hash(const KT key) { return (hash_t)(intptr_t)key; } + hash_t Hash(double key) + { + hash_t keyhash[2]; + memcpy(&keyhash, &key, sizeof(keyhash)); + return keyhash[0] ^ keyhash[1]; + } + + // Compares two keys, returning zero if they are the same. + int Compare(const KT left, const KT right) { return left != right; } +}; + +template<> struct THashTraits +{ + // Use all bits when hashing singles instead of converting them to ints. + hash_t Hash(float key) + { + hash_t keyhash; + memcpy(&keyhash, &key, sizeof(keyhash)); + return keyhash; + } + int Compare(float left, float right) { return left != right; } +}; + +template<> struct THashTraits +{ + // Use all bits when hashing doubles instead of converting them to ints. + hash_t Hash(double key) + { + hash_t keyhash[2]; + memcpy(&keyhash, &key, sizeof(keyhash)); + return keyhash[0] ^ keyhash[1]; + } + int Compare(double left, double right) { return left != right; } +}; + +template struct TValueTraits +{ + // Initializes a value for TMap. If a regular constructor isn't + // good enough, you can override it. + void Init(VT &value) + { + ::new(&value) VT; + } +}; + +// Must match layout of TMap +struct FMap +{ + void *Nodes; + void *LastFree; + hash_t Size; + hash_t NumUsed; +}; + +template class TMapIterator; +template class TMapConstIterator; + +template, class ValueTraits=TValueTraits > +class TMap +{ + template friend class TMapIterator; + template friend class TMapConstIterator; + +public: + typedef class TMap MyType; + typedef class TMapIterator Iterator; + typedef class TMapConstIterator ConstIterator; + typedef struct { const KT Key; VT Value; } Pair; + typedef const Pair ConstPair; + + TMap() { NumUsed = 0; SetNodeVector(1); } + TMap(hash_t size) { NumUsed = 0; SetNodeVector(size); } + ~TMap() { ClearNodeVector(); } + + TMap(const TMap &o) + { + NumUsed = 0; + SetNodeVector(o.CountUsed()); + CopyNodes(o.Nodes, o.Size); + } + + TMap &operator= (const TMap &o) + { + NumUsed = 0; + ClearNodeVector(); + SetNodeVector(o.CountUsed()); + CopyNodes(o.Nodes, o.Size); + return *this; + } + + //======================================================================= + // + // TransferFrom + // + // Moves the contents from one TMap to another, leaving the TMap moved + // from empty. + // + //======================================================================= + + void TransferFrom(TMap &o) + { + // Clear all our nodes. + NumUsed = 0; + ClearNodeVector(); + + // Copy all of o's nodes. + Nodes = o.Nodes; + LastFree = o.LastFree; + Size = o.Size; + NumUsed = o.NumUsed; + + // Tell o it doesn't have any nodes. + o.Nodes = NULL; + o.Size = 0; + o.LastFree = NULL; + o.NumUsed = 0; + + // Leave o functional with one empty node. + o.SetNodeVector(1); + } + + //======================================================================= + // + // Clear + // + // Empties out the table and resizes it with room for count entries. + // + //======================================================================= + + void Clear(hash_t count=1) + { + ClearNodeVector(); + SetNodeVector(count); + } + + //======================================================================= + // + // CountUsed + // + // Returns the number of entries in use in the table. + // + //======================================================================= + + hash_t CountUsed() const + { +#ifdef _DEBUG + hash_t used = 0; + hash_t ct = Size; + for (Node *n = Nodes; ct-- > 0; ++n) + { + if (!n->IsNil()) + { + ++used; + } + } + assert (used == NumUsed); +#endif + return NumUsed; + } + + //======================================================================= + // + // operator[] + // + // Returns a reference to the value associated with a particular key, + // creating the pair if the key isn't already in the table. + // + //======================================================================= + + VT &operator[] (const KT key) + { + return GetNode(key)->Pair.Value; + } + + const VT &operator[] (const KT key) const + { + return GetNode(key)->Pair.Value; + } + + //======================================================================= + // + // CheckKey + // + // Returns a pointer to the value associated with a particular key, or + // NULL if the key isn't in the table. + // + //======================================================================= + + VT *CheckKey (const KT key) + { + Node *n = FindKey(key); + return n != NULL ? &n->Pair.Value : NULL; + } + + const VT *CheckKey (const KT key) const + { + const Node *n = FindKey(key); + return n != NULL ? &n->Pair.Value : NULL; + } + + //======================================================================= + // + // Insert + // + // Adds a key/value pair to the table if key isn't in the table, or + // replaces the value for the existing pair if the key is in the table. + // + // This is functionally equivalent to (*this)[key] = value; but can be + // slightly faster if the pair needs to be created because it doesn't run + // the constructor on the value part twice. + // + //======================================================================= + + VT &Insert(const KT key, const VT &value) + { + Node *n = FindKey(key); + if (n != NULL) + { + n->Pair.Value = value; + } + else + { + n = NewKey(key); + ::new(&n->Pair.Value) VT(value); + } + return n->Pair.Value; + } + + //======================================================================= + // + // Remove + // + // Removes the key/value pair for a particular key if it is in the table. + // + //======================================================================= + + void Remove(const KT key) + { + DelKey(key); + } + +protected: + struct IPair // This must be the same as Pair above, but with a + { // non-const Key. + KT Key; + VT Value; + }; + struct Node + { + Node *Next; + IPair Pair; + void SetNil() + { + Next = (Node *)1; + } + bool IsNil() const + { + return Next == (Node *)1; + } + }; + + /* This is used instead of memcpy, because Node is likely to be small, + * such that the time spent calling a function would eclipse the time + * spent copying. */ + struct NodeSizedStruct { unsigned char Pads[sizeof(Node)]; }; + + Node *Nodes; + Node *LastFree; /* any free position is before this position */ + hash_t Size; /* must be a power of 2 */ + hash_t NumUsed; + + const Node *MainPosition(const KT k) const + { + HashTraits Traits; + return &Nodes[Traits.Hash(k) & (Size - 1)]; + } + + Node *MainPosition(const KT k) + { + HashTraits Traits; + return &Nodes[Traits.Hash(k) & (Size - 1)]; + } + + void SetNodeVector(hash_t size) + { + // Round size up to nearest power of 2 + for (Size = 1; Size < size; Size <<= 1) + { } + Nodes = (Node *)malloc(Size * sizeof(Node)); + LastFree = &Nodes[Size]; /* all positions are free */ + for (hash_t i = 0; i < Size; ++i) + { + Nodes[i].SetNil(); + } + } + + void ClearNodeVector() + { + for (hash_t i = 0; i < Size; ++i) + { + if (!Nodes[i].IsNil()) + { + Nodes[i].~Node(); + } + } + free(Nodes); + Nodes = NULL; + Size = 0; + LastFree = NULL; + NumUsed = 0; + } + + void Resize(hash_t nhsize) + { + hash_t i, oldhsize = Size; + Node *nold = Nodes; + /* create new hash part with appropriate size */ + SetNodeVector(nhsize); + /* re-insert elements from hash part */ + NumUsed = 0; + for (i = 0; i < oldhsize; ++i) + { + if (!nold[i].IsNil()) + { + Node *n = NewKey(nold[i].Pair.Key); + ::new(&n->Pair.Value) VT(nold[i].Pair.Value); + nold[i].~Node(); + } + } + free(nold); + } + + void Rehash() + { + Resize (Size << 1); + } + + Node *GetFreePos() + { + while (LastFree-- > Nodes) + { + if (LastFree->IsNil()) + { + return LastFree; + } + } + return NULL; /* could not find a free place */ + } + + /* + ** Inserts a new key into a hash table; first, check whether key's main + ** position is free. If not, check whether colliding node is in its main + ** position or not: if it is not, move colliding node to an empty place and + ** put new key in its main position; otherwise (colliding node is in its main + ** position), new key goes to an empty position. + ** + ** The Value field is left unconstructed. + */ + Node *NewKey(const KT key) + { + Node *mp = MainPosition(key); + if (!mp->IsNil()) + { + Node *othern; + Node *n = GetFreePos(); /* get a free place */ + if (n == NULL) /* cannot find a free place? */ + { + Rehash(); /* grow table */ + return NewKey(key); /* re-insert key into grown table */ + } + othern = MainPosition(mp->Pair.Key); + if (othern != mp) /* is colliding node out of its main position? */ + { /* yes; move colliding node into free position */ + while (othern->Next != mp) /* find previous */ + { + othern = othern->Next; + } + othern->Next = n; /* redo the chain with 'n' in place of 'mp' */ + CopyNode(n, mp); /* copy colliding node into free pos. (mp->Next also goes) */ + mp->Next = NULL; /* now 'mp' is free */ + } + else /* colliding node is in its own main position */ + { /* new node will go into free position */ + n->Next = mp->Next; /* chain new position */ + mp->Next = n; + mp = n; + } + } + else + { + mp->Next = NULL; + } + ++NumUsed; + ::new(&mp->Pair.Key) KT(key); + return mp; + } + + void DelKey(const KT key) + { + Node *mp = MainPosition(key), **mpp; + HashTraits Traits; + + if (mp->IsNil()) + { + /* the key is definitely not present, because there is nothing at its main position */ + } + else if (!Traits.Compare(mp->Pair.Key, key)) /* the key is in its main position */ + { + if (mp->Next != NULL) /* move next node to its main position */ + { + Node *n = mp->Next; + mp->~Node(); /* deconstruct old node */ + CopyNode(mp, n); /* copy next node */ + n->SetNil(); /* next node is now nil */ + } + else + { + mp->~Node(); + mp->SetNil(); /* there is no chain, so main position is nil */ + } + --NumUsed; + } + else /* the key is either not present or not in its main position */ + { + for (mpp = &mp->Next, mp = *mpp; mp != NULL && Traits.Compare(mp->Pair.Key, key); mpp = &mp->Next, mp = *mpp) + { } /* look for the key */ + if (mp != NULL) /* found it */ + { + *mpp = mp->Next; /* rechain so this node is skipped */ + mp->~Node(); + mp->SetNil(); /* because this node is now nil */ + --NumUsed; + } + } + } + + Node *FindKey(const KT key) + { + HashTraits Traits; + Node *n = MainPosition(key); + while (n != NULL && !n->IsNil() && Traits.Compare(n->Pair.Key, key)) + { + n = n->Next; + } + return n == NULL || n->IsNil() ? NULL : n; + } + + const Node *FindKey(const KT key) const + { + HashTraits Traits; + const Node *n = MainPosition(key); + while (n != NULL && !n->IsNil() && Traits.Compare(n->Pair.Key, key)) + { + n = n->Next; + } + return n == NULL || n->IsNil() ? NULL : n; + } + + Node *GetNode(const KT key) + { + Node *n = FindKey(key); + if (n != NULL) + { + return n; + } + n = NewKey(key); + ValueTraits traits; + traits.Init(n->Pair.Value); + return n; + } + + /* Perform a bit-wise copy of the node. Used when relocating a node in the table. */ + void CopyNode(Node *dst, const Node *src) + { + *(NodeSizedStruct *)dst = *(const NodeSizedStruct *)src; + } + + /* Copy all nodes in the node vector to this table. */ + void CopyNodes(const Node *nodes, hash_t numnodes) + { + for (; numnodes-- > 0; ++nodes) + { + if (!nodes->IsNil()) + { + Node *n = NewKey(nodes->Pair.Key); + ::new(&n->Pair.Value) VT(nodes->Pair.Value); + } + } + } +}; + +// TMapIterator ------------------------------------------------------------- +// A class to iterate over all the pairs in a TMap. + +template > +class TMapIterator +{ +public: + TMapIterator(MapType &map) + : Map(map), Position(0) + { + } + + //======================================================================= + // + // NextPair + // + // Returns false if there are no more entries in the table. Otherwise, it + // returns true, and pair is filled with a pointer to the pair in the + // table. + // + //======================================================================= + + bool NextPair(typename MapType::Pair *&pair) + { + if (Position >= Map.Size) + { + return false; + } + do + { + if (!Map.Nodes[Position].IsNil()) + { + pair = reinterpret_cast(&Map.Nodes[Position].Pair); + Position += 1; + return true; + } + } while (++Position < Map.Size); + return false; + } + + //======================================================================= + // + // Reset + // + // Restarts the iteration so you can do it all over again. + // + //======================================================================= + + void Reset() + { + Position = 0; + } + +protected: + MapType ⤅ + hash_t Position; +}; + +// TMapConstIterator -------------------------------------------------------- +// Exactly the same as TMapIterator, but it works with a const TMap. + +template > +class TMapConstIterator +{ +public: + TMapConstIterator(const MapType &map) + : Map(map), Position(0) + { + } + + bool NextPair(typename MapType::ConstPair *&pair) + { + if (Position >= Map.Size) + { + return false; + } + do + { + if (!Map.Nodes[Position].IsNil()) + { + pair = reinterpret_cast(&Map.Nodes[Position].Pair); + Position += 1; + return true; + } + } while (++Position < Map.Size); + return false; + } + +protected: + const MapType ⤅ + hash_t Position; +}; + +#endif //__TARRAY_H__ diff --git a/wadext.cpp b/wadext.cpp new file mode 100644 index 0000000..c3047d9 --- /dev/null +++ b/wadext.cpp @@ -0,0 +1,570 @@ +// +//--------------------------------------------------------------------------- +// +// Copyright(C) 2016 Christoph Oelckers +// All rights reserved. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 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 General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see http://www.gnu.org/licenses/ +// +//-------------------------------------------------------------------------- +// + +#include +#include +#include +#include +#include "wadext.h" +#include "ResourceFile.h" +#include "fileformat.h" + +#pragma warning(disable:4996) + +bool isLevel(WadItemList * we); +char profile[256]; +char maindir[128]; + +bool pstartfound=false; +WadItemList PNames(-1); + + +char * getdir(const char * lump) +{ + static char d[2]=""; + + if (*lump>='A' && *lump<='Z') d[0]=*lump; + else if (*lump>='a' && *lump<='z') d[0]=*lump-32; + else if (*lump>='0' && *lump<='9') d[0]='0'; + else d[0]='@'; + return d; +} + +bool isPatch(const char * n) +{ + if (!pstartfound || PNames.mLump < 0) return false; + long * l = (long*)PNames.Address(); + char * c = (char*)(l + 1); + char nn[9]; + + + if (!l) return false; + + strncpy(nn, n, 8); + nn[8] = 0; + strupr(nn); + + for (int i = 0; i<*l; i++) + { + if (!strncmp(c, nn, 8)) return true; + c += 8; + } + return false; +} + + +const char * MakeFileName(const char * base, const char * ext) +{ + char basebuffer[20]; + static char buffer[40]; + + strncpy(basebuffer,base,8); + basebuffer[8]=0; + + for (int i = 0; i < 8; i++) + { + // replace special path characters in the lump name + if (basebuffer[i] == '\\') basebuffer[i] = '^'; + if (basebuffer[i] == '/') basebuffer[i] = '§'; + if (basebuffer[i] == ':') basebuffer[i] = '¡'; + if (basebuffer[i] == '*') basebuffer[i] = '²'; + if (basebuffer[i] == '?') basebuffer[i] = '¿'; + } + + for(int i=0;;i++) + { + if (i > 0) sprintf(buffer,"%s(%d)%s", basebuffer, i, ext); + else sprintf(buffer, "%s%s", basebuffer, ext); + strlwr(buffer); + FILE *f = fopen(buffer, "rb"); + if (f == NULL) return buffer; + fclose(f); + } +} + +void Extract(WadItemList *w,char * dir,const char * ext, int options, bool isflat) //It is completely impossible to detect flats, so we have to flag it when extracting the F_ namespace +{ + FileType ft = IdentifyFileType(w->Name(), (uint8_t*)w->Address(), w->Length()); + const char *filename; + + if (dir == NULL) + { + switch (ft.type & FG_MASK) + { + case FG_GFX: + if (isPatch(w->Name())) dir = "patches"; + else dir = "graphics"; + break; + + case FG_SND: + dir = "sounds"; + break; + + case FG_MUS: + dir = "music"; + break; + + case FG_VOX: + dir = "voxels"; + break; + + } + } + // Flats are easy to misidentify so if we are in the flat namespace and the size is exactly 4096 bytes, let it pass as flat + if (isflat && (ft.type & FG_MASK) != FG_GFX && (ft.type == FT_DOOMSND || ft.type == FT_MP3 || w->Length() == 4096)) + { + ft = { FT_BINARY, ".LMP"}; + } + + if (dir != NULL) + { + mkdir(dir); + chdir(dir); + } + printf("processing %.8s\n", w->Name()); + + if (ft.type == FT_DOOMGFX && !(options & NO_CONVERT_GFX)) + { + filename = MakeFileName(w->Name(), ".png"); + PatchToPng(options, (const uint8_t*)w->Address(), w->Length(), filename); + } + else if (w->Length() > 0 && ft.type == FT_BINARY && !(options & NO_CONVERT_GFX) && (isflat || (w->Length() == 64000 && options&(DO_HEXEN_PAL | DO_HERETIC_PAL)))) + { + filename = MakeFileName(w->Name(), ".png"); + FlatToPng(options, (const uint8_t*)w->Address(), w->Length(), filename); + } + else if (ft.type == FT_DOOMSND && !(options & NO_CONVERT_SND)) + { + filename = MakeFileName(w->Name(), ".wav"); + DoomSndToWav((const uint8_t*)w->Address(), w->Length(), filename); + } + else + { + filename = MakeFileName(w->Name(), ft.extension); + + FILE *f = fopen(filename, "wb"); + if (f == NULL) + { + printf("%s: Unable to create file\n", filename); + chdir(maindir); + return; + } + fwrite(w->Address(), 1, w->Length(), f); + fclose(f); + } + chdir(maindir); +} + +int ListExtract(int startmarker,char * dir,char * endm,char * endm2,bool isflat,int options,int pack=0) +{ + int ev=0; + char dirbuf[200]; + + mkdir(dir); + strcpy(dirbuf,dir); + WadItemList ww(startmarker + 1); + WadItemList *w = &ww; + while (strncmp(w->Name(),endm,8) && strncmp(w->Name(),endm2,8)) + { + Extract(w, dir, NULL, options, isflat); + ww = ww.mLump + 1; + } + return ww.mLump; +} + +bool isLevel(int lump) +{ + if (lump >= mainwad->NumLumps() - 3) return false; // any level is at least 3 lumps + WadItemList w(lump + 1); + if (!stricmp(w.Name(), "THINGS")) return true; + if (!stricmp(w.Name(), "TEXTMAP")) return true; + return false; +} + +int MapExtract(int startmarker,char *dir,int options) +{ + int i; + char buffer[40]; + int packlength[13]={0}; + TArray directory; + + mkdir(dir); + chdir(dir); + + WadItemList ww(startmarker); + sprintf(buffer,"%.8s.wad", ww.Name()); + printf("processing %s\n",buffer); + FILE *f=fopen(buffer, "wb"); + + fwrite("PWAD\xb\0\0\0\0\0\0\0",1, 12, f); + + + CWADLump lmp; + lmp.offset = ftell(f); + strncpy(lmp.name, ww.Name(), 8); + lmp.length = ww.Length(); + fwrite(ww.Address(), 1, ww.Length(), f); + directory.Push(lmp); + ww = ww.mLump + 1; + + if (!stricmp(ww.Name(), "TEXTMAP")) + { + // UDMF + while (true) + { + if (!(options & DO_STRIP) || (stricmp(ww.Name(), "ZNODES") && stricmp(ww.Name(), "BLOCKMAP") && stricmp(ww.Name(), "REJECT"))) + { + CWADLump lmp; + lmp.offset = ftell(f); + strncpy(lmp.name, ww.Name(), 8); + lmp.length = ww.Length(); + fwrite(ww.Address(), 1, ww.Length(), f); + directory.Push(lmp); + } + + if (!stricmp(ww.Name(), "ENDMAP")) + { + goto finalize; + } + int lump = ww.mLump + 1; + if (lump == mainwad->NumLumps()) + { + printf("Unexpected end of UDMF map found\n"); + return lump; + } + ww = lump; + } + } + else + { + static const char *maplumps[] = { "THINGS", "LINEDEFS", "SIDEDEFS", "VERTEXES", "SEGS", "SSECTORS", "NODES", "SECTORS", "REJECT", "BLOCKMAP", "BEHAVIOR", "SCRIPTS" }; + + for (i = 0; i < 12; i++) + { + if (ww.Name() != NULL && !stricmp(ww.Name(), maplumps[i])) + { + CWADLump lmp; + lmp.offset = ftell(f); + strncpy(lmp.name, ww.Name(), 8); + if ((options & DO_STRIP) && (i == 4 || i == 5 || i == 6 || i == 8 || i == 9)) + { + lmp.length = 0; + } + else + { + lmp.length = ww.Length(); + fwrite(ww.Address(), 1, ww.Length(), f); + } + directory.Push(lmp); + ww = ww.mLump + 1; + } + } + } +finalize: + uint32_t lumpc = directory.Size(); + uint32_t dpos = ftell(f); + for (auto &p : directory) + { + fwrite(&p, 1, 16, f); + } + fseek(f, 4, 0); + fwrite(&lumpc, 1, 4, f); + fwrite(&dpos, 1, 4, f); + fclose(f); + chdir(".."); + return directory.Size(); +} + + +typedef char pname[8]; + +bool AddWallTexture(FILE * fo,maptexture_t * wt,pname * pNames, bool first) +{ + int k; + char buffer[9]; + + sprintf(buffer,"%.8s",wt->name); + strupr(buffer); + bool cc = strspn(buffer, "ABCDEFGHIJKLMNOPQRSTUVWXYZ_0123456789") < strlen(buffer) || *buffer <= '9'; + fprintf(fo,"WallTexture %s%s%s, %d, %d\n{\n",cc? "\"":"", buffer,cc? "\"":"", wt->width,wt->height); + if (wt->ScaleX) fprintf(fo,"\tXScale %f\n",wt->ScaleX/8.0f); + if (wt->ScaleY) fprintf(fo,"\tYScale %f\n",wt->ScaleY/8.0f); + if (wt->Flags&0x8000) fprintf(fo,"\tWorldPanning\n"); + if (first) fprintf(fo,"\tNullTexture\n"); + for(k=0;kpatchcount;k++) + { + sprintf(buffer,"%.8s",pNames[wt->patches[k].patch]); + strupr(buffer); + cc = strspn(buffer, "ABCDEFGHIJKLMNOPQRSTUVWXYZ_0123456789") < strlen(buffer) || *buffer <= '9'; + fprintf(fo,"\tPatch %s%.8s%s, %d, %d\n",cc? "\"":"", buffer,cc? "\"":"", wt->patches[k].originx,wt->patches[k].originy); + } + fprintf(fo,"}\n"); + return true; +} + +bool AddWallTexture(FILE * fo,strifemaptexture_t * wt,pname * pNames, bool first) +{ + int k; + char buffer[9]; + + sprintf(buffer,"%.8s",wt->name); + strupr(buffer); + bool cc = strspn(buffer, "ABCDEFGHIJKLMNOPQRSTUVWXYZ_0123456789") < strlen(buffer) || *buffer <= '9'; + fprintf(fo,"WallTexture %s%s%s, %d, %d\n{\n",cc? "\"":"", buffer,cc? "\"":"", wt->width,wt->height); + if (wt->ScaleX) fprintf(fo,"\tXScale %f\n",wt->ScaleX/8.0f); + if (wt->ScaleY) fprintf(fo,"\tYScale %f\n",wt->ScaleY/8.0f); + if (wt->Flags&0x8000) fprintf(fo,"\tWorldPanning\n"); + for(k=0;kpatchcount;k++) + { + sprintf(buffer,"%.8s",pNames[wt->patches[k].patch]); + strupr(buffer); + cc = strspn(buffer, "ABCDEFGHIJKLMNOPQRSTUVWXYZ_0123456789") < strlen(buffer) || *buffer <= '9'; + fprintf(fo,"\tPatch %s%.8s%s, %d, %d\n",cc? "\"":"", buffer,cc? "\"":"", wt->patches[k].originx,wt->patches[k].originy); + } + fprintf(fo,"}\n"); + return true; +} + +void AnimExtract(WadItemList *w) +{ + int i,numentries; + char * c; + char buffer[30]; + + mkdir("decompiled"); + chdir("decompiled"); + + strcpy(buffer,"animdefs.animated"); + printf("processing %s\n",buffer); + + numentries=w->Length()/23; + + c=(char*)w->Address(); + + FILE * fo=fopen(buffer,"wt"); + for(i=0;iLength()/20; + + c=(char*)w->Address(); + + FILE * fo=fopen(buffer,"wt"); + for(i=0;iName()[7]); + bool nulltex = !strnicmp(pTex->Name(), "TEXTURE1", 8); + GenerateTextureFile(buffer, (const char*)pTex->Address(), (const char *)pPNam->Address(), options, nulltex); +} + +void ExtractWad(char * wadfilename,int options) +{ + WadItemList pTex1(-1), pTex2(-1), pPnam(-1); + + OpenMainWad(wadfilename); + + PNames = mainwad->FindLump("PNAMES"); + getcwd(maindir, 128); + for(int i=0;iNumLumps();i++) + { + WadItemList ww(i); + WadItemList *w = &ww; + { + if (!strcmp(w->Name(),".")) + { + w->Release(); + } + else if (!strnicmp(w->Name(),"S_START",8) || !strnicmp(w->Name(),"SS_START",8) ) + { + i=ListExtract(i,"SPRITES","S_END","SS_END",false,options,false); + } + else if (!strnicmp(w->Name(),"F_START",8) || !strnicmp(w->Name(),"FF_START",8) ) + { + i=ListExtract(i,"FLATS","F_END","FF_END",true,options,false); + } + else if (!strnicmp(w->Name(),"C_START",8)) + { + i=ListExtract(i,"COLORMAPS","C_END","CC_END",false,false,options); + } + else if (!strnicmp(w->Name(),"A_START",8)) + { + i=ListExtract(i,"ACS","A_END","AA_END",false,false,options); + } + else if (!strnicmp(w->Name(),"P_START",8) || !strnicmp(w->Name(),"PP_START",8)) + { + pstartfound=true; + } + else if (!strnicmp(w->Name(),"P_END",8) || !strnicmp(w->Name(),"PP_END",8)) // ignore + { + } + else if (!strnicmp(w->Name(),"TX_START",8)) + { + i=ListExtract(i,"TEXTURES","TX_END","TX_END",false,options); + } + else if (!strnicmp(w->Name(),"HI_START",8)) + { + i=ListExtract(i,"HIRES","HI_END","HI_END",false,options); + } + else if (!strnicmp(w->Name(),"VX_START",8)) + { + i=ListExtract(i,"VOXELS","VX_END","VX_END",false,false,options); + } + else if (!strnicmp(w->Name(),"GL_VERT",8)) // ignore + { + // skip GL nodes + } + else if (!strnicmp(w->Name(),"GL_SEGS",8)) // ignore + { + // skip GL nodes + } + else if (!strnicmp(w->Name(),"GL_SSECT",8)) // ignore + { + // skip GL nodes + } + else if (!strnicmp(w->Name(),"GL_NODES",8)) // ignore + { + // skip GL nodes + } + else if (!strnicmp(w->Name(), "GL_PVS", 8)) // ignore + { + // skip GL nodes + } + else if (isLevel(i)) + { + i += MapExtract(i,"MAPS",options) - 1; + } + else + { + if (!strnicmp(w->Name(), "TEXTURE1", 8)) pTex1 = *w; + else if (!strnicmp(w->Name(), "TEXTURE2", 8)) pTex2 = *w; + else if (!strnicmp(w->Name(), "PNAMES", 8)) pPnam = *w; + else if (!strnicmp(w->Name(), "ANIMATED", 8)) + { + AnimExtract(w); + } + else if (!strnicmp(w->Name(), "SWITCHES", 8)) + { + SwitchExtract(w); + } + + Extract(w, NULL, NULL, options, false); + } + + if (pTex1.mLump >= 0 && pPnam.mLump >= 0) + { + GenerateTextureFile(&pTex1,&pPnam, options); + pTex1 = -1; + } + if (pTex2.mLump >= 0 && pPnam.mLump >= 0) + { + GenerateTextureFile(&pTex2,&pPnam, options); + pTex2=-1; + } + } + } +} + +void ConvertTextureX() +{ + FILE *ft1 = fopen("texture1", "rb"); + if (!ft1) ft1 = fopen("texture1.lmp", "rb"); + + FILE *ft2 = fopen("texture2", "rb"); + if (!ft2) ft2 = fopen("texture2.lmp", "rb"); + + FILE *pnm = fopen("pnames", "rb"); + if (!pnm) pnm = fopen("pnames.lmp", "rb"); + + if (!pnm) exit(1); + + static char bt1[100000], bt2[100000], bpn[100000]; + + if (ft1) fread(bt1, 1, 100000, ft1), fclose(ft1); + if (ft2) fread(bt2, 1, 100000, ft2), fclose(ft2); + if (pnm) fread(bpn, 1, 100000, pnm), fclose(pnm); + + if (ft1) GenerateTextureFile("textures.txt", bt1, bpn, 0, true); + if (ft2) GenerateTextureFile("textures.txt2", bt2, bpn, 0, false); +} + diff --git a/wadext.h b/wadext.h new file mode 100644 index 0000000..cb782b3 --- /dev/null +++ b/wadext.h @@ -0,0 +1,82 @@ + +#include + +enum ExtractOpt +{ + NO_CONVERT_GFX=1, + DO_HERETIC_PAL=2, + DO_HEXEN_PAL=4, + DO_STRIFE=8, + DO_STRIP=16, // strip map lumps ZDoom does not need (nodes, blockmap, reject) + NO_CONVERT_SND = 32, +}; + + + + + +//-------------------------------------------------------------------------- +// +// Texture definition +// +//-------------------------------------------------------------------------- + +// +// Each texture is composed of one or more patches, with patches being lumps +// stored in the WAD. The lumps are referenced by number, and patched into +// the rectangular texture space using origin and possibly other attributes. +// +typedef struct +{ + short originx; + short originy; + short patch; + short stepdir; + short colormap; +} mappatch_t; + +// +// A wall texture is a list of patches which are to be combined in a +// predefined order. +// +typedef struct +{ + char name[8]; + uint16_t Flags; // [RH] Was unused + uint8_t ScaleX; // [RH] Scaling (8 is normal) + uint8_t ScaleY; // [RH] Same as above + short width; + short height; + uint8_t columndirectory[4]; // OBSOLETE + short patchcount; + mappatch_t patches[1]; +} maptexture_t; + +#define MAPTEXF_WORLDPANNING 0x8000 + +// [RH] Just for documentation purposes, here's what I think the +// Strife versions of the above two structures are: + +typedef struct +{ + short originx; + short originy; + short patch; +} strifemappatch_t; + +// +// A wall texture is a list of patches which are to be combined in a +// predefined order. +// +typedef struct +{ + char name[8]; + uint16_t Flags; // [RH] Was nused + uint8_t ScaleX; // [RH] Scaling (8 is normal) + uint8_t ScaleY; // [RH] Same as above + short width; + short height; + short patchcount; + strifemappatch_t patches[1]; +} strifemaptexture_t; + diff --git a/wadfile.cpp b/wadfile.cpp new file mode 100644 index 0000000..4b235da --- /dev/null +++ b/wadfile.cpp @@ -0,0 +1,189 @@ +// +//--------------------------------------------------------------------------- +// +// Copyright(C) 2005-2016 Christoph Oelckers +// All rights reserved. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 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 General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see http://www.gnu.org/licenses/ +// +//-------------------------------------------------------------------------- +// + +#include +#include +#include "ResourceFile.h" + +//========================================================================== +// +// +// +//========================================================================== + +CWADFile::CWADFile(const char * filename) +{ + char type[4]; + int diroffset; + int numlumps; + + m_NumLumps=0; + m_LumpStart=0; + + m_Filename = strdup(filename); + m_File=NULL; + + if (filename == NULL) + { + printf("No file name specified\n"); + return; + } + + FILE *f = fopen(filename, "rb"); + if (!f) + { + printf("%s: unable to open\n", filename); + return; + } + + m_File = f; + fread(&type, 1, 4, f); + + if (memcmp(type, "IWAD", 4) && memcmp(type, "PWAD", 4)) + { + printf("%s: not a WAD file\n", filename); + return; + } + fread(&numlumps, 4, 1, f); + fread(&diroffset, 4, 1, f); + m_NumLumps = numlumps; + + fseek(f, diroffset, SEEK_SET); + + CWADLump lump; + + for (int i = 0; i < numlumps; i++) + { + fread(&lump.offset, 4, 1, f); + fread(&lump.length, 4, 1, f); + fread(lump.name, 1, 8, f); + lump.name[8]=0; + + lumps.Push(lump); + + } +} + +//========================================================================== +// +// +// +//========================================================================== + +CWADFile::~CWADFile(void) +{ + if (m_File) fclose(m_File); + if (m_Filename!=NULL) free(m_Filename); +} + +//========================================================================== +// +// +// +//========================================================================== + +int CWADFile::FindLump(const char * name) +{ + for (int i = lumps.Size()-1; i>=0; i--) + { + if (!stricmp(name, lumps[i].name)) return i; + } + return -1; +} + +//========================================================================== +// +// +// +//========================================================================== + +int CWADFile::GetLumpSize(int lump) +{ + if ((unsigned)lump < lumps.Size() ) return lumps[lump].length; + else return -1; +} + +//========================================================================== +// +// +// +//========================================================================== + +void * CWADFile::ReadLump(int lump) +{ + if ((unsigned)lump < lumps.Size() ) + { + void * Ptr= malloc(lumps[lump].length); + ReadLump(lump, Ptr); + return Ptr; + } + return NULL; +} + +//========================================================================== +// +// +// +//========================================================================== + +void CWADFile::ReadLump(int lump, void * Ptr) +{ + if ((unsigned)lump < lumps.Size() ) + { + fseek(m_File, lumps[lump].offset, SEEK_SET); + fread(Ptr, 1, lumps[lump].length, m_File); + } +} + +//========================================================================== +// +// +// +//========================================================================== + +const char * CWADFile::GetLumpName(int lump) +{ + if ((unsigned)lump < lumps.Size() ) + { + return lumps[lump].name; + } + else + { + return NULL; + } +} + +//========================================================================== +// +// +// +//========================================================================== +CWADFile *mainwad; + +void OpenMainWad(char *filename) +{ + mainwad = new CWADFile(filename); + if (mainwad->NumLumps() == 0) + { + exit(1); + } +} diff --git a/wadman.h b/wadman.h new file mode 100644 index 0000000..c56ddee --- /dev/null +++ b/wadman.h @@ -0,0 +1,248 @@ + +#pragma pack(1) +#pragma warning(disable:4200) + +#include "ResourceFile.h" + +extern CWADFile *mainwad; + +struct CVertex +{ + short x; + short y; +}; + + +struct CThing +{ + short xpos; /* x position */ + short ypos; /* y position */ + short angle; /* facing angle */ + short type; /* thing type */ + short when; /* appears when? */ +}; + + + +/* + this data structure contains the information about the LINEDEFS +*/ +struct CLineDef +{ + unsigned short start; /* from this vertex ... */ + unsigned short end; /* ... to this vertex */ + short flags; /* see NAMES.C for more info */ + short type; /* see NAMES.C for more info */ + short tag; /* crossing this linedef activates the sector wi*/ + short sidedef1; /* sidedef */ + short sidedef2; /* only if this line adjoins 2 sectors */ +}; + + + +/* + this data structure contains the information about the SIDEDEFS +*/ +struct CSideDef +{ + short xoff; /* X offset for texture */ + short yoff; /* Y offset for texture */ + char texUpper[8]; /* texture name for the part above */ + char texLower[8]; /* texture name for the part below */ + char texNormal[8]; /* texture name for the regular part */ + short sector; /* adjacent sector */ +}; + + +struct CSector +{ + short floorh; + short ceilh; + char floort[8]; + char ceilt[8]; + short light; + short special; + short tag; +}; + + +struct CWadFileHeader +{ + char id[4]; + long numEntries; + long DirOffset; +}; + +struct CWadFileEntry +{ + long FileOffset; + long EntryLength; + char name[8]; +}; + +struct CWad3FileEntry +{ + long FileOffset; + long EntryLength; + long EntryLength2; + long type; + char name[16]; +}; + +struct WadItemList; + +struct CLevelInfo +{ + CThing * pThings; + CLineDef * pLineDefs; + CSideDef * pSideDefs; + CVertex * pVertexes; + CSector * pSectors; + + char * pNodes; + char * pSegs; + char * pSSector; + + char * pBlockmap; + char * pReject; + + char * pBehavior; + char * pScript; + + int nThings; + int nLineDefs; + int nSideDefs; + int nVertexes; + int nSectors; + + int nNodes; + int nSegs; + int nSSectors; + + int cReject; + int cBlockmap; + int cBehavior; + int cScript; + + char * pGLVerts; + char * pGLSegs; + char * pGLSSectors; + char * pGLNodes; + + int nGLVertexes; + int nGLNodes; + int nGLSegs; + int nGLSSectors; + + int NameTag; + + CLevelInfo() + { + NameTag=0; + } + + void Release(); + + ~CLevelInfo() + { + Release(); + } + + bool HasBehavior() + { + return pBehavior!=NULL; + } + bool HasScript() + { + return pBehavior!=NULL; + } +}; + + + +/* +struct WadInfo +{ + HANDLE hFile; + HANDLE hMapping; + CWadFileHeader * pwAddr; + union + { + CWadFileEntry * pwDir; + CWad3FileEntry * pw3Dir; + }; + int wtype; + char filename[256]; +}; + + +struct WadList +{ + WadInfo * pwInfo; + WadList * pNext; +}; + +struct WadItemList +{ + CWadFileEntry * pwInfo; + int type; + + WadInfo * pwWad; + WadItemList * pSuccessor; // the overloaded entry + WadItemList * pNext; + + WadItemList * Next() { return pNext; } + void* PackedAddress() const { return (void*)(((char*)pwWad->pwAddr)+pwInfo->FileOffset); } + + bool isPacked() const + { + return false; + } + + char * Address() + { + return (char *)PackedAddress(); + } + + int Length() const + { + return pwInfo->EntryLength; + } + + void Release() + { + } + char * Name() const { return pwInfo->name; } + char * WadFileName() { return pwWad->filename; } + + WadItemList() { } + ~WadItemList() { } +}; +*/ + +struct ImportInf +{ + char name[9]; + int insX,insY; + int transparentCol; +}; + +//extern WadItemList * GlobalDirectory; + + + +//void CloseWadFile(WadInfo * wi); +void OpenMainWad(char * filename); +void ClosePatchWad(const char * filename); +void * GetWadItem(char * name,int * pLength); +void FreeWadItem(char * name,char * blockstart=NULL); +void NameError(char * level,char * section); +bool GetLevelInfo(char * name,CLevelInfo * pLi); +//int GetLevelFileInfo(char * fn,CLevelInfo * pli,WadInfo **pp=NULL); + + +inline void CLevelInfo::Release() +{ + NameTag=-1; +} +