├── tools └── wwd2html │ ├── .gitignore │ ├── wwd_open.py │ ├── index.mako │ ├── gen_image_sets_dict.py │ ├── wwd2html.py │ └── pywap32.py ├── src ├── utils.cpp ├── common.h ├── utils.h ├── io.cpp ├── wwd_io.h ├── buffer.cpp ├── errors.cpp ├── buffer.h ├── wwd.h ├── errors.h ├── io.h ├── wwd.cpp ├── wwd_read.cpp └── wwd_write.cpp ├── .clang-format ├── tests ├── wwd_files │ ├── Bushy.wwd │ ├── RETAIL01.WWD │ ├── RETAIL02.WWD │ ├── RETAIL03.WWD │ ├── RETAIL04.WWD │ ├── RETAIL05.WWD │ ├── RETAIL06.WWD │ ├── RETAIL07.WWD │ ├── RETAIL08.WWD │ ├── RETAIL09.WWD │ ├── RETAIL10.WWD │ ├── RETAIL11.WWD │ ├── RETAIL12.WWD │ ├── RETAIL13.WWD │ ├── RETAIL14.WWD │ ├── ParadiseCove.wwd │ ├── RockySwitch.wwd │ ├── LePortdeCoolness.wwd │ ├── RETAIL01_badsig.WWD │ ├── RETAIL01_nocompress.WWD │ └── RETAIL03_nocompress.WWD ├── CMakeLists.txt ├── demo.cpp └── wwd.cpp ├── README.md ├── include ├── wap32.h └── wap32 │ ├── errors.h │ ├── buffer.h │ ├── common.h │ ├── pid.h │ └── wwd.h ├── CMakeLists.txt ├── .gitattributes ├── .gitignore └── misc └── wwd_structs.h /tools/wwd2html/.gitignore: -------------------------------------------------------------------------------- 1 | *.WWD 2 | *.wwd 3 | *.html -------------------------------------------------------------------------------- /src/utils.cpp: -------------------------------------------------------------------------------- 1 | #include "utils.h" 2 | #include "errors.h" 3 | -------------------------------------------------------------------------------- /.clang-format: -------------------------------------------------------------------------------- 1 | BasedOnStyle: LLVM 2 | AlignConsecutiveAssignments: true 3 | IndentWidth: 4 4 | -------------------------------------------------------------------------------- /tests/wwd_files/Bushy.wwd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jakub-trzebiatowski/libwap32/HEAD/tests/wwd_files/Bushy.wwd -------------------------------------------------------------------------------- /tests/wwd_files/RETAIL01.WWD: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jakub-trzebiatowski/libwap32/HEAD/tests/wwd_files/RETAIL01.WWD -------------------------------------------------------------------------------- /tests/wwd_files/RETAIL02.WWD: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jakub-trzebiatowski/libwap32/HEAD/tests/wwd_files/RETAIL02.WWD -------------------------------------------------------------------------------- /tests/wwd_files/RETAIL03.WWD: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jakub-trzebiatowski/libwap32/HEAD/tests/wwd_files/RETAIL03.WWD -------------------------------------------------------------------------------- /tests/wwd_files/RETAIL04.WWD: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jakub-trzebiatowski/libwap32/HEAD/tests/wwd_files/RETAIL04.WWD -------------------------------------------------------------------------------- /tests/wwd_files/RETAIL05.WWD: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jakub-trzebiatowski/libwap32/HEAD/tests/wwd_files/RETAIL05.WWD -------------------------------------------------------------------------------- /tests/wwd_files/RETAIL06.WWD: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jakub-trzebiatowski/libwap32/HEAD/tests/wwd_files/RETAIL06.WWD -------------------------------------------------------------------------------- /tests/wwd_files/RETAIL07.WWD: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jakub-trzebiatowski/libwap32/HEAD/tests/wwd_files/RETAIL07.WWD -------------------------------------------------------------------------------- /tests/wwd_files/RETAIL08.WWD: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jakub-trzebiatowski/libwap32/HEAD/tests/wwd_files/RETAIL08.WWD -------------------------------------------------------------------------------- /tests/wwd_files/RETAIL09.WWD: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jakub-trzebiatowski/libwap32/HEAD/tests/wwd_files/RETAIL09.WWD -------------------------------------------------------------------------------- /tests/wwd_files/RETAIL10.WWD: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jakub-trzebiatowski/libwap32/HEAD/tests/wwd_files/RETAIL10.WWD -------------------------------------------------------------------------------- /tests/wwd_files/RETAIL11.WWD: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jakub-trzebiatowski/libwap32/HEAD/tests/wwd_files/RETAIL11.WWD -------------------------------------------------------------------------------- /tests/wwd_files/RETAIL12.WWD: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jakub-trzebiatowski/libwap32/HEAD/tests/wwd_files/RETAIL12.WWD -------------------------------------------------------------------------------- /tests/wwd_files/RETAIL13.WWD: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jakub-trzebiatowski/libwap32/HEAD/tests/wwd_files/RETAIL13.WWD -------------------------------------------------------------------------------- /tests/wwd_files/RETAIL14.WWD: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jakub-trzebiatowski/libwap32/HEAD/tests/wwd_files/RETAIL14.WWD -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | libwap32 2 | ======== 3 | 4 | Library supporting file formats of Monolith's WAP32 engine (Claw, Gruntz, Get Medieval) -------------------------------------------------------------------------------- /tests/wwd_files/ParadiseCove.wwd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jakub-trzebiatowski/libwap32/HEAD/tests/wwd_files/ParadiseCove.wwd -------------------------------------------------------------------------------- /tests/wwd_files/RockySwitch.wwd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jakub-trzebiatowski/libwap32/HEAD/tests/wwd_files/RockySwitch.wwd -------------------------------------------------------------------------------- /tests/wwd_files/LePortdeCoolness.wwd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jakub-trzebiatowski/libwap32/HEAD/tests/wwd_files/LePortdeCoolness.wwd -------------------------------------------------------------------------------- /tests/wwd_files/RETAIL01_badsig.WWD: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jakub-trzebiatowski/libwap32/HEAD/tests/wwd_files/RETAIL01_badsig.WWD -------------------------------------------------------------------------------- /tests/wwd_files/RETAIL01_nocompress.WWD: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jakub-trzebiatowski/libwap32/HEAD/tests/wwd_files/RETAIL01_nocompress.WWD -------------------------------------------------------------------------------- /tests/wwd_files/RETAIL03_nocompress.WWD: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jakub-trzebiatowski/libwap32/HEAD/tests/wwd_files/RETAIL03_nocompress.WWD -------------------------------------------------------------------------------- /include/wap32.h: -------------------------------------------------------------------------------- 1 | #ifndef wap32_h 2 | #define wap32_h 3 | 4 | #include "wap32/common.h" 5 | #include "wap32/errors.h" 6 | #include "wap32/wwd.h" 7 | 8 | #endif -------------------------------------------------------------------------------- /src/common.h: -------------------------------------------------------------------------------- 1 | #ifndef common_h 2 | #define common_h 3 | 4 | #include "wap32/common.h" 5 | #include 6 | #ifdef _WIN32 7 | #define NOMINMAX 8 | #include 9 | #undef NOMINMAX 10 | #endif 11 | 12 | #endif 13 | -------------------------------------------------------------------------------- /tests/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 2.6) 2 | 3 | include_directories(../include) 4 | 5 | add_executable(demo demo.cpp) 6 | target_link_libraries(demo wap32) 7 | 8 | add_executable(test_wwd wwd.cpp) 9 | target_link_libraries(test_wwd wap32) 10 | -------------------------------------------------------------------------------- /src/utils.h: -------------------------------------------------------------------------------- 1 | #ifndef utils_h 2 | #define utils_h 3 | 4 | #include "common.h" 5 | #include 6 | #include 7 | #include 8 | 9 | namespace wap { 10 | inline bool system_is_big_endian() { 11 | int n = 1; 12 | return *(char *)&n != 1; 13 | } 14 | } 15 | 16 | #endif 17 | -------------------------------------------------------------------------------- /tools/wwd2html/wwd_open.py: -------------------------------------------------------------------------------- 1 | import argparse 2 | import pywap32 3 | 4 | if __name__ == '__main__': 5 | parser = argparse.ArgumentParser() 6 | parser.add_argument('input', help='WWD file to open') 7 | args = parser.parse_args() 8 | print(args.input) 9 | try: 10 | wwd = pywap32.Wwd.open(args.input) 11 | except ValueError: 12 | print("ValueError:", args.input) -------------------------------------------------------------------------------- /src/io.cpp: -------------------------------------------------------------------------------- 1 | #include "io.h" 2 | 3 | namespace wap { 4 | std::vector read_whole_file(const char *file_path) { 5 | std::ifstream file(file_path, std::ios::binary); 6 | std::vector buffer((std::istreambuf_iterator(file)), 7 | std::istreambuf_iterator()); 8 | if (!file.good()) 9 | throw wap::Error(WAP_EFILE); 10 | return buffer; 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /include/wap32/errors.h: -------------------------------------------------------------------------------- 1 | #ifndef wap_errors_h 2 | #define wap_errors_h 3 | 4 | #include "common.h" 5 | 6 | WAP_BEGIN_DECLS 7 | 8 | typedef enum { 9 | WAP_OK = 0, 10 | WAP_ERROR = -1, /* Generic error */ 11 | WAP_ENOMEMORY = -2, /* Out of memory */ 12 | WAP_EFILE = -3, /* File access failed */ 13 | WAP_EINVALIDDATA = -4, /* Input data is invalid */ 14 | } wap_error_t; 15 | 16 | WAP_END_DECLS 17 | 18 | #endif 19 | -------------------------------------------------------------------------------- /include/wap32/buffer.h: -------------------------------------------------------------------------------- 1 | #ifndef wap_buffer_h 2 | #define wap_buffer_h 3 | 4 | #include "common.h" 5 | 6 | WAP_BEGIN_DECLS 7 | 8 | typedef struct { 9 | void *_private[4]; // opaque struct 10 | } wap_buffer; 11 | 12 | WAP_API void wap_buffer_init(wap_buffer *buffer); 13 | 14 | WAP_API char *wap_buffer_data(wap_buffer *buffer); 15 | 16 | WAP_API size_t wap_buffer_size(wap_buffer *buffer); 17 | 18 | WAP_API void wap_buffer_destroy(wap_buffer *buffer); 19 | 20 | WAP_END_DECLS 21 | 22 | #endif -------------------------------------------------------------------------------- /src/wwd_io.h: -------------------------------------------------------------------------------- 1 | #ifndef wwd_io_h 2 | #define wwd_io_h 3 | 4 | #include 5 | 6 | enum { 7 | WAP_WWD_HEADER_SIZE = 1524, 8 | WAP_WWD_PLANE_DESCRIPTION_SIZE = 160, 9 | WAP_WWD_OBJECT_DESCRIPTION_SIZE = 284, 10 | }; 11 | 12 | struct wwd_plane_offsets { 13 | unsigned tiles_offset, image_sets_offset, objects_offset; 14 | }; 15 | 16 | struct wwd_offsets { 17 | unsigned main_block_offset; 18 | unsigned tile_descriptions_offset; 19 | unsigned eof_offset; 20 | }; 21 | 22 | #endif 23 | -------------------------------------------------------------------------------- /src/buffer.cpp: -------------------------------------------------------------------------------- 1 | #include "buffer.h" 2 | #include "wap32/errors.h" 3 | #include 4 | 5 | void wap_buffer_init(wap_buffer *buffer) { 6 | new (buffer->_private) std::vector; 7 | } 8 | 9 | char *wap_buffer_data(wap_buffer *buffer) { 10 | return wap::cast_wap_buffer_to_vector(buffer)->data(); 11 | } 12 | 13 | size_t wap_buffer_size(wap_buffer *buffer) { 14 | return wap::cast_wap_buffer_to_vector(buffer)->size(); 15 | } 16 | 17 | void wap_buffer_destroy(wap_buffer *buffer) { 18 | wap::cast_wap_buffer_to_vector(buffer)->~vector(); 19 | } 20 | -------------------------------------------------------------------------------- /src/errors.cpp: -------------------------------------------------------------------------------- 1 | #include "errors.h" 2 | 3 | #include 4 | 5 | #include 6 | #include 7 | 8 | namespace wap { 9 | Error Error::from_zlib_error(int zlib_error_code) { 10 | int error_code; 11 | switch (zlib_error_code) { 12 | case Z_BUF_ERROR: 13 | case Z_DATA_ERROR: 14 | error_code = WAP_EINVALIDDATA; 15 | break; 16 | case Z_MEM_ERROR: 17 | error_code = WAP_ENOMEMORY; 18 | break; 19 | default: 20 | error_code = WAP_ERROR; 21 | } 22 | return Error(error_code); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/buffer.h: -------------------------------------------------------------------------------- 1 | #ifndef buffer_h 2 | #define buffer_h 3 | 4 | #include "common.h" 5 | #include "wap32/buffer.h" 6 | #include 7 | 8 | static_assert(sizeof(wap_buffer) >= sizeof(std::vector), 9 | "wap_buffer is not big enough to hold std::vector"); 10 | 11 | namespace wap { 12 | inline std::vector *cast_wap_buffer_to_vector(wap_buffer *buffer) { 13 | return reinterpret_cast *>(buffer); 14 | } 15 | inline wap_buffer *cast_vector_to_wap_buffer(std::vector *buffer) { 16 | return reinterpret_cast(buffer); 17 | } 18 | } 19 | 20 | #endif -------------------------------------------------------------------------------- /src/wwd.h: -------------------------------------------------------------------------------- 1 | #ifndef wwd_h 2 | #define wwd_h 3 | 4 | #include "wap32/wwd.h" 5 | #include "common.h" 6 | #include 7 | #include 8 | #include 9 | 10 | struct wap_object { 11 | wap_object_properties properties; 12 | std::string name; 13 | std::string logic; 14 | std::string image_set; 15 | std::string animation; 16 | }; 17 | 18 | struct wap_plane { 19 | uint32_t tiles_wide = 0; 20 | uint32_t tiles_high = 0; 21 | wap_plane_properties properties; 22 | std::vector tiles; 23 | std::vector image_sets; 24 | std::vector objects; 25 | }; 26 | 27 | struct wap_wwd { 28 | uint32_t checksum = 0; 29 | wap_wwd_properties properties; 30 | std::vector planes; 31 | std::vector tile_descriptions; 32 | }; 33 | 34 | #endif 35 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.0) 2 | project(libwap32) 3 | 4 | include_directories("include") 5 | file(GLOB SOURCES 6 | "include/wap32.h" 7 | "include/wap32/*.h" 8 | "src/*.h" 9 | "src/*.cpp" 10 | ) 11 | 12 | add_definitions(-DWAP_BUILD_SHARED) 13 | list(APPEND CMAKE_CXX_FLAGS "-std=c++11") 14 | 15 | add_library(wap32 SHARED ${SOURCES}) 16 | set_target_properties(wap32 PROPERTIES PREFIX "lib") 17 | 18 | find_package(ZLIB REQUIRED) 19 | if(ZLIB_FOUND) 20 | include_directories(${ZLIB_INCLUDE_DIRS}) 21 | target_link_libraries(wap32 ${ZLIB_LIBRARIES}) 22 | endif(ZLIB_FOUND) 23 | 24 | add_subdirectory(tests) 25 | enable_testing() 26 | add_test( 27 | NAME wwd 28 | COMMAND test_wwd RETAIL01_nocompress.WWD 29 | WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}/tests/wwd_files 30 | ) 31 | 32 | set(${PROJECT_NAME}_INCLUDE_DIRS ${PROJECT_SOURCE_DIR}/include 33 | ${ZLIB_INCLUDE_DIRS} 34 | CACHE INTERNAL "${PROJECT_NAME}: Include Directories" FORCE) 35 | -------------------------------------------------------------------------------- /src/errors.h: -------------------------------------------------------------------------------- 1 | #ifndef errors_h 2 | #define errors_h 3 | 4 | #include "wap32/errors.h" 5 | #include "utils.h" 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | #ifdef _WIN32 13 | #define BREAKPOINT DebugBreak(); 14 | #else 15 | #define BREAKPOINT __asm__("int $3") 16 | #endif 17 | 18 | namespace wap { 19 | class Error { 20 | public: 21 | inline Error(int error_code) : error_code_(error_code) {} 22 | inline int error_code() { return error_code_; } 23 | static Error from_zlib_error(int zlib_error_code); 24 | 25 | private: 26 | int error_code_; 27 | }; 28 | 29 | template 30 | int handle_exceptions(F f, Args &&... args) { 31 | try { 32 | f(std::forward(args)...); 33 | } catch (wap::Error &error) { 34 | return error.error_code(); 35 | } catch (std::bad_alloc &) { 36 | return WAP_ENOMEMORY; 37 | } 38 | return WAP_OK; 39 | } 40 | } 41 | 42 | #endif 43 | -------------------------------------------------------------------------------- /include/wap32/common.h: -------------------------------------------------------------------------------- 1 | #ifndef wap_common_h 2 | #define wap_common_h 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | #define WAP_INTEGER_ENCODING (-1 & 3) 9 | #define WAP_SIGN_AND_MAGNITUDE 1 10 | #define WAP_ONES_COMPLEMENT 2 11 | #define WAP_TWOS_COMPLEMENT 3 12 | 13 | #if WAP_INTEGER_ENCODING != WAP_TWOS_COMPLEMENT 14 | #error Support for integer encoding other than two’s complement is not implemented 15 | #endif 16 | 17 | #if CHAR_BIT != 8 18 | #error There is no support for non-8bit chars 19 | #endif 20 | 21 | #ifndef WAP_API 22 | #ifdef _WIN32 23 | #if defined(WAP_BUILD_SHARED) /* build dll */ 24 | #define WAP_API __declspec(dllexport) 25 | #elif !defined(WAP_BUILD_STATIC) /* use dll */ 26 | #define WAP_API __declspec(dllimport) 27 | #else /* static library */ 28 | #define WAP_API 29 | #endif 30 | #else 31 | #if __GNUC__ >= 4 32 | #define WAP_API __attribute__((visibility("default"))) 33 | #else 34 | #define WAP_API 35 | #endif 36 | #endif 37 | #endif 38 | 39 | #ifdef __cplusplus 40 | #define WAP_BEGIN_DECLS extern "C" { 41 | #define WAP_END_DECLS } 42 | #else 43 | #define WAP_BEGIN_DECLS 44 | #define WAP_END_DECLS 45 | #endif 46 | 47 | #endif 48 | -------------------------------------------------------------------------------- /include/wap32/pid.h: -------------------------------------------------------------------------------- 1 | #ifndef wap_pid_h 2 | #define wap_pid_h 3 | 4 | #include "common.h" 5 | #include "buffer.h" 6 | 7 | #include 8 | 9 | WAP_BEGIN_DECLS 10 | 11 | enum { 12 | WAP_PID_FLAG_TRANSPARENCY = 1 << 0, 13 | WAP_PID_FLAG_VIDEO_MEMORY = 1 << 1, 14 | WAP_PID_FLAG_SYSTEM_MEMORY = 1 << 2, 15 | WAP_PID_FLAG_MIRROR = 1 << 3, 16 | WAP_PID_FLAG_INVERT = 1 << 4, 17 | WAP_PID_FLAG_COMPRESSION = 1 << 5, 18 | WAP_PID_FLAG_LIGHTS = 1 << 6, 19 | WAP_PID_FLAG_EMBEDDED_PALETTE = 1 << 7, 20 | }; 21 | 22 | typedef struct wap_pid wap_pid; 23 | 24 | typedef struct { unsigned char r, g, b; } wap_rgb; 25 | 26 | typedef struct { 27 | uint32_t id; 28 | uint32_t flags; /* WAP_PID_FLAG_ flags */ 29 | uint32_t width; 30 | uint32_t height; 31 | uint32_t offset_x; 32 | uint32_t offset_y; 33 | } wap_pid_header; 34 | 35 | WAP_API wap_pid *wap_pid_create(); 36 | 37 | WAP_API void wap_pid_free(wap_pid *pid); 38 | 39 | WAP_API int wap_pid_read_header(wap_pid_header *header, const char *pid_buffer, 40 | size_t pid_buffer_size); 41 | 42 | WAP_API int wap_pid_read(wap_pid *pid, const char *pid_buffer, 43 | size_t pid_buffer_size); 44 | 45 | WAP_API int wap_pid_open(wap_pid *pid, const char *file_path); 46 | 47 | WAP_API int wap_pid_write(const wap_pid *pid, wap_buffer *out_pid_buffer); 48 | 49 | WAP_API int wap_pid_save(const wap_pid *pid, const char *file_path); 50 | 51 | WAP_END_DECLS 52 | 53 | #endif 54 | -------------------------------------------------------------------------------- /tests/demo.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include "../src/wwd.h" 3 | #include "../src/errors.h" 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | using namespace std; 12 | 13 | typedef unique_ptr unique_wwd_pointer; 14 | 15 | int main(int argc, const char **argv) { 16 | for (int i = 1; i < argc; ++i) { 17 | string filename = argv[i]; 18 | cerr << filename << endl; 19 | 20 | wap_wwd *wwd = wap_wwd_create(); 21 | unique_wwd_pointer wwd_ptr(wwd, wap_wwd_free); 22 | 23 | wap_buffer wwd_buffer_compressed, wwd_buffer_decompressed; 24 | wap_buffer_init(&wwd_buffer_decompressed); 25 | wap_buffer_init(&wwd_buffer_compressed); 26 | 27 | int error = 0; 28 | error = wap_wwd_open(wwd, filename.c_str()); 29 | assert(!error); 30 | assert(wap_wwd_get_properties(wwd)->flags & WAP_WWD_FLAG_COMPRESS); 31 | error = wap_wwd_write(wwd, &wwd_buffer_compressed); 32 | wap_wwd_get_properties(wwd)->flags = 0; 33 | error = wap_wwd_save(wwd, "their_decompressed.WWD"); 34 | assert(!error); 35 | error = wap_wwd_read(wwd, wap_buffer_data(&wwd_buffer_compressed), 36 | wap_buffer_size(&wwd_buffer_compressed)); 37 | assert(!error); 38 | wap_wwd_get_properties(wwd)->flags = 0; 39 | error = wap_wwd_save(wwd, "our_decompressed.WWD"); 40 | assert(!error); 41 | 42 | wap_buffer_destroy(&wwd_buffer_decompressed); 43 | wap_buffer_destroy(&wwd_buffer_compressed); 44 | } 45 | } -------------------------------------------------------------------------------- /tools/wwd2html/index.mako: -------------------------------------------------------------------------------- 1 | <% 2 | def get_image(image_set_id, index): 3 | try: 4 | return image_sets[image_set_id][index] 5 | except KeyError: 6 | return ("SADFACE", (32, 32), (0, 0)) 7 | 8 | used_images = set() 9 | for obj in objects: 10 | used_images.add((obj.image_set_id, obj.i)) 11 | for layer in layers: 12 | for tile in layer.tiles: 13 | if not tile.filled: 14 | used_images.add((layer.image_set_id, tile.index)) 15 | %>\ 16 | 37 | 38 | % for layer in layers: 39 |
40 | % for tile in layer.tiles: 41 |
50 | % endfor 51 |
52 | % endfor 53 | 54 | % for obj in objects: 55 | <% 56 | 57 | %>\ 58 |
66 | % endfor 67 | -------------------------------------------------------------------------------- /tools/wwd2html/gen_image_sets_dict.py: -------------------------------------------------------------------------------- 1 | import os 2 | from os import path 3 | from pprint import pprint 4 | import re 5 | import struct 6 | import sys 7 | 8 | assert len(sys.argv) == 2 9 | 10 | image_sets = {} 11 | unpacked_rez_path = sys.argv[1] 12 | for root, dirs, files in os.walk(unpacked_rez_path): 13 | if not dirs and files: 14 | pids = [pid_filename for pid_filename in files if pid_filename.endswith(".PID")] 15 | if pids: 16 | key = path.relpath(root, unpacked_rez_path).replace(os.sep, '_') # example: LEVEL3_IMAGES_CRATE 17 | image_set = dict() 18 | image_sets[key] = image_set 19 | for i, pid_filename in enumerate(pids): 20 | try: 21 | index = int(re.search(r'\d+', pid_filename).group(0)) 22 | except: 23 | if i != 0: 24 | continue 25 | index = None 26 | pid_path = path.join(root, pid_filename) 27 | rel_path = path.relpath(pid_path, unpacked_rez_path) 28 | with open(pid_path, 'rb') as pid_file: 29 | pid_file.read(8) 30 | size = struct.unpack(" 2 | #include "../src/buffer.h" 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | int main(int argc, const char **argv) { 11 | assert(argc == 2); 12 | 13 | std::string filename = argv[1]; 14 | std::ifstream file; 15 | file.open(filename.c_str()); 16 | assert(file.good()); 17 | 18 | std::vector wwd_buffer_loaded((std::istreambuf_iterator(file)), 19 | std::istreambuf_iterator()); 20 | 21 | wap_wwd *wwd = wap_wwd_create(); 22 | assert(wwd); 23 | 24 | int error = 0; 25 | error = 26 | wap_wwd_read(wwd, wwd_buffer_loaded.data(), wwd_buffer_loaded.size()); 27 | assert(!error); 28 | 29 | assert( 30 | !(wap_wwd_get_properties(wwd)->flags & 31 | WAP_WWD_FLAG_COMPRESS)); // It's best to test on non compressed files 32 | 33 | wap_buffer wwd_buffer_compressed; 34 | wap_buffer wwd_buffer_decompressed; 35 | wap_buffer_init(&wwd_buffer_compressed); 36 | wap_buffer_init(&wwd_buffer_decompressed); 37 | 38 | wap_wwd_get_properties(wwd)->flags |= WAP_WWD_FLAG_COMPRESS; 39 | 40 | error = wap_wwd_write(wwd, &wwd_buffer_compressed); 41 | assert(!error); 42 | 43 | error = wap_wwd_read(wwd, wap_buffer_data(&wwd_buffer_compressed), 44 | wap_buffer_size(&wwd_buffer_compressed)); 45 | assert(!error); 46 | 47 | wap_wwd_get_properties(wwd)->flags &= ~WAP_WWD_FLAG_COMPRESS; 48 | 49 | error = wap_wwd_write(wwd, &wwd_buffer_decompressed); 50 | assert(!error); 51 | 52 | // error = wap_wwd_save(wwd, filename.replace(filename.size()-4, 4, 53 | // "_libwap32.WWD").c_str()); 54 | // assert(!error); 55 | 56 | assert(wwd_buffer_loaded == 57 | *wap::cast_wap_buffer_to_vector(&wwd_buffer_decompressed)); 58 | 59 | wap_buffer_destroy(&wwd_buffer_compressed); 60 | wap_buffer_destroy(&wwd_buffer_decompressed); 61 | 62 | return 0; 63 | } -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | ############################################################################### 2 | # Set default behavior to automatically normalize line endings. 3 | ############################################################################### 4 | * text=auto 5 | 6 | ############################################################################### 7 | # Set default behavior for command prompt diff. 8 | # 9 | # This is need for earlier builds of msysgit that does not have it on by 10 | # default for csharp files. 11 | # Note: This is only used by command line 12 | ############################################################################### 13 | #*.cs diff=csharp 14 | 15 | ############################################################################### 16 | # Set the merge driver for project and solution files 17 | # 18 | # Merging from the command prompt will add diff markers to the files if there 19 | # are conflicts (Merging from VS is not affected by the settings below, in VS 20 | # the diff markers are never inserted). Diff markers may cause the following 21 | # file extensions to fail to load in VS. An alternative would be to treat 22 | # these files as binary and thus will always conflict and require user 23 | # intervention with every merge. To do so, just uncomment the entries below 24 | ############################################################################### 25 | #*.sln merge=binary 26 | #*.csproj merge=binary 27 | #*.vbproj merge=binary 28 | #*.vcxproj merge=binary 29 | #*.vcproj merge=binary 30 | #*.dbproj merge=binary 31 | #*.fsproj merge=binary 32 | #*.lsproj merge=binary 33 | #*.wixproj merge=binary 34 | #*.modelproj merge=binary 35 | #*.sqlproj merge=binary 36 | #*.wwaproj merge=binary 37 | 38 | ############################################################################### 39 | # behavior for image files 40 | # 41 | # image files are treated as binary by default. 42 | ############################################################################### 43 | #*.jpg binary 44 | #*.png binary 45 | #*.gif binary 46 | 47 | ############################################################################### 48 | # diff behavior for common document formats 49 | # 50 | # Convert binary document formats to text before diffing them. This feature 51 | # is only available from the command line. Turn it on by uncommenting the 52 | # entries below. 53 | ############################################################################### 54 | #*.doc diff=astextplain 55 | #*.DOC diff=astextplain 56 | #*.docx diff=astextplain 57 | #*.DOCX diff=astextplain 58 | #*.dot diff=astextplain 59 | #*.DOT diff=astextplain 60 | #*.pdf diff=astextplain 61 | #*.PDF diff=astextplain 62 | #*.rtf diff=astextplain 63 | #*.RTF diff=astextplain 64 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.pyc 2 | 3 | ## Ignore Visual Studio temporary files, build results, and 4 | ## files generated by popular Visual Studio add-ons. 5 | 6 | # User-specific files 7 | *.suo 8 | *.user 9 | *.sln.docstates 10 | 11 | # Build results 12 | 13 | [Dd]ebug/ 14 | [Rr]elease/ 15 | x64/ 16 | #build/ 17 | [Bb]in/ 18 | [Oo]bj/ 19 | 20 | # Enable "build/" folder in the NuGet Packages folder since NuGet packages use it for MSBuild targets 21 | !packages/*/build/ 22 | 23 | # MSTest test Results 24 | [Tt]est[Rr]esult*/ 25 | [Bb]uild[Ll]og.* 26 | 27 | *_i.c 28 | *_p.c 29 | *.ilk 30 | *.meta 31 | *.obj 32 | *.pch 33 | *.pdb 34 | *.pgc 35 | *.pgd 36 | *.rsp 37 | *.sbr 38 | *.tlb 39 | *.tli 40 | *.tlh 41 | *.tmp 42 | *.tmp_proj 43 | *.log 44 | *.vspscc 45 | *.vssscc 46 | .builds 47 | *.pidb 48 | *.log 49 | *.scc 50 | 51 | # Visual C++ cache files 52 | ipch/ 53 | *.aps 54 | *.ncb 55 | *.opensdf 56 | *.sdf 57 | *.cachefile 58 | 59 | # Visual Studio profiler 60 | *.psess 61 | *.vsp 62 | *.vspx 63 | 64 | # Guidance Automation Toolkit 65 | *.gpState 66 | 67 | # ReSharper is a .NET coding add-in 68 | _ReSharper*/ 69 | *.[Rr]e[Ss]harper 70 | 71 | # TeamCity is a build add-in 72 | _TeamCity* 73 | 74 | # DotCover is a Code Coverage Tool 75 | *.dotCover 76 | 77 | # NCrunch 78 | *.ncrunch* 79 | .*crunch*.local.xml 80 | 81 | # Installshield output folder 82 | [Ee]xpress/ 83 | 84 | # DocProject is a documentation generator add-in 85 | DocProject/buildhelp/ 86 | DocProject/Help/*.HxT 87 | DocProject/Help/*.HxC 88 | DocProject/Help/*.hhc 89 | DocProject/Help/*.hhk 90 | DocProject/Help/*.hhp 91 | DocProject/Help/Html2 92 | DocProject/Help/html 93 | 94 | # Click-Once directory 95 | publish/ 96 | 97 | # Publish Web Output 98 | *.Publish.xml 99 | 100 | # NuGet Packages Directory 101 | ## TODO: If you have NuGet Package Restore enabled, uncomment the next line 102 | #packages/ 103 | 104 | # Windows Azure Build Output 105 | csx 106 | *.build.csdef 107 | 108 | # Windows Store app package directory 109 | AppPackages/ 110 | 111 | # Others 112 | sql/ 113 | *.Cache 114 | ClientBin/ 115 | [Ss]tyle[Cc]op.* 116 | ~$* 117 | *~ 118 | *.dbmdl 119 | *.[Pp]ublish.xml 120 | *.pfx 121 | *.publishsettings 122 | 123 | # RIA/Silverlight projects 124 | Generated_Code/ 125 | 126 | # Backup & report files from converting an old project file to a newer 127 | # Visual Studio version. Backup files are not needed, because we have git ;-) 128 | _UpgradeReport_Files/ 129 | Backup*/ 130 | UpgradeLog*.XML 131 | UpgradeLog*.htm 132 | 133 | # SQL Server files 134 | App_Data/*.mdf 135 | App_Data/*.ldf 136 | 137 | 138 | #LightSwitch generated files 139 | GeneratedArtifacts/ 140 | _Pvt_Extensions/ 141 | ModelManifest.xml 142 | 143 | # ========================= 144 | # Windows detritus 145 | # ========================= 146 | 147 | # Windows image file caches 148 | Thumbs.db 149 | ehthumbs.db 150 | 151 | # Folder config file 152 | Desktop.ini 153 | 154 | # Recycle Bin used on file shares 155 | $RECYCLE.BIN/ 156 | 157 | # Mac desktop service store files 158 | .DS_Store 159 | -------------------------------------------------------------------------------- /misc/wwd_structs.h: -------------------------------------------------------------------------------- 1 | // structs in .wwd file 2 | 3 | struct wap32_WWD_Header { 4 | unsigned wwd_signature; 5 | unsigned null0; 6 | unsigned flags; 7 | unsigned null1; 8 | char name[64]; 9 | char author[64]; 10 | char birth[64]; 11 | char rez_file[256]; 12 | char image_dir[128]; 13 | char pal_rez[128]; 14 | unsigned start_x; 15 | unsigned start_y; 16 | unsigned null2; 17 | unsigned num_planes; 18 | unsigned planes_offset; 19 | unsigned tile_properties_offset; 20 | unsigned main_block_length; 21 | unsigned checksum; 22 | unsigned null3; 23 | char launch_app[128]; 24 | char image_set1[128]; 25 | char image_set2[128]; 26 | char image_set3[128]; 27 | char image_set4[128]; 28 | char prefix1[32]; 29 | char prefix2[32]; 30 | char prefix3[32]; 31 | char prefix4[32]; 32 | }; 33 | 34 | struct wap32_WWD_Plane { 35 | unsigned plane_signature; 36 | unsigned null0; 37 | unsigned flags; 38 | unsigned null1; 39 | char name[64]; 40 | unsigned height_px; 41 | unsigned width_px; 42 | unsigned tile_width; 43 | unsigned tile_height; 44 | unsigned tiles_wide; 45 | unsigned tiles_high; 46 | unsigned null2; 47 | unsigned null3; 48 | unsigned movement_x_percent; 49 | unsigned movement_y_percent; 50 | unsigned fill_color; 51 | unsigned num_image_sets; 52 | unsigned num_objects; 53 | unsigned tiles_offset; 54 | unsigned image_sets_offset; 55 | unsigned objects_offset; 56 | unsigned z_coord; 57 | unsigned null4; 58 | unsigned null5; 59 | unsigned null6; 60 | }; 61 | 62 | struct wap32_WWD_Object { 63 | unsigned id; 64 | unsigned object_name_len; 65 | unsigned logic_name_len; 66 | unsigned image_set_name_len; 67 | unsigned x; 68 | unsigned y; 69 | unsigned z; 70 | unsigned i; 71 | unsigned flags; 72 | unsigned dynamic_flags; 73 | unsigned draw_flags; 74 | unsigned user_flags; 75 | unsigned score; 76 | unsigned points; 77 | unsigned powerup; 78 | unsigned damage; 79 | unsigned smarts; 80 | unsigned health; 81 | wap32_Rect move_rect; 82 | wap32_Rect hit_rect; 83 | wap32_Rect attack_rect; 84 | wap32_Rect clip_rect; 85 | wap32_Rect user1_rect; 86 | wap32_Rect user2_rect; 87 | unsigned user1; 88 | unsigned user2; 89 | unsigned user3; 90 | unsigned user4; 91 | unsigned user5; 92 | unsigned user6; 93 | unsigned user7; 94 | unsigned user8; 95 | unsigned x_min; 96 | unsigned y_min; 97 | unsigned x_max; 98 | unsigned y_max; 99 | unsigned speed_x; 100 | unsigned speed_y; 101 | unsigned x_tweak; 102 | unsigned y_tweak; 103 | unsigned counter; 104 | unsigned speed; 105 | unsigned width; 106 | unsigned height; 107 | unsigned direction; 108 | unsigned face_dir; 109 | unsigned time_delay; 110 | unsigned frame_delay; 111 | unsigned object_type; 112 | unsigned hit_type_flags; 113 | unsigned x_move_res; 114 | unsigned y_move_res; 115 | }; 116 | -------------------------------------------------------------------------------- /tools/wwd2html/wwd2html.py: -------------------------------------------------------------------------------- 1 | import argparse 2 | import mako 3 | from mako.template import Template 4 | from os import path 5 | import pywap32 6 | import sys 7 | 8 | class _Layer: 9 | pass 10 | 11 | class _Tile: 12 | pass 13 | 14 | class _Object: 15 | pass 16 | 17 | def render(wwd, image_sets): 18 | tiles_dir = wwd.image_dir.replace('\\', '_') 19 | if tiles_dir.startswith('_'): 20 | tiles_dir = tiles_dir[1:] 21 | wwd_image_sets = [image_set.replace('\\', '_') for image_set in wwd.image_sets] 22 | 23 | layers = [] 24 | objects = [] 25 | 26 | for plane in wwd.planes: 27 | if plane.movement_x_percent == 100 and plane.movement_x_percent == 100: 28 | layer = _Layer() 29 | layer.image_set_id = tiles_dir + '_' + plane.image_sets[0] 30 | layer.tiles = [] 31 | layer.tile_width, layer.tile_height = plane.tile_width, plane.tile_height 32 | layer.z_index = plane.z_coord 33 | layer.fill_color = plane.fill_color 34 | w, h = plane.size 35 | for x in range(w): 36 | for y in range(h): 37 | tile_index = plane.get_tile((x, y)) 38 | tile = _Tile() 39 | tile.x = x 40 | tile.y = y 41 | if tile_index == 0xFFFFFFFF: 42 | continue 43 | elif tile_index == 0xEEEEEEEE: 44 | tile.filled = True 45 | else: 46 | tile.filled = False 47 | tile.index = tile_index 48 | layer.tiles.append(tile) 49 | layers.append(layer) 50 | for obj in plane.objects: 51 | object = _Object() 52 | for i, prefix in enumerate(wwd.prefixes): 53 | if prefix and obj.image_set.startswith(prefix): 54 | object.image_set_id = obj.image_set.replace(prefix, wwd_image_sets[i]) 55 | break 56 | else: 57 | object.image_set_id = tiles_dir + '_' + obj.image_set 58 | object.x, object.y = obj.x, obj.y 59 | object.z_index = obj.z 60 | object.i = obj.i 61 | object.mirrored = obj.draw_flags & pywap32.Object.DRAW_FLAG_MIRROR 62 | object.inverted = obj.draw_flags & pywap32.Object.DRAW_FLAG_INVERT 63 | objects.append(object) 64 | template = Template(filename='index.mako') 65 | return template.render(layers=layers, objects=objects, image_sets=image_sets) 66 | 67 | if __name__ == '__main__': 68 | parser = argparse.ArgumentParser() 69 | parser.add_argument('-o', metavar='', help='Write output to ') 70 | parser.add_argument('input', help='WWD file to process') 71 | args = parser.parse_args() 72 | 73 | out_filename = args.o or path.splitext(args.input)[0] + ".html" 74 | with open(out_filename, 'w') as out_file: 75 | wwd = pywap32.Wwd.open(args.input) 76 | with open('image_sets.dict') as image_sets_dict: 77 | image_sets = eval(image_sets_dict.read()) 78 | try: 79 | rendered = render(wwd, image_sets) 80 | except: 81 | rendered = mako.exceptions.html_error_template().render().decode() 82 | out_file.write(rendered) 83 | -------------------------------------------------------------------------------- /src/io.h: -------------------------------------------------------------------------------- 1 | #ifndef io_h 2 | #define io_h 3 | 4 | #include "common.h" 5 | #include "errors.h" 6 | #include "utils.h" 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | namespace wap { 14 | namespace detail { 15 | template struct BasicStreamHelper { 16 | static inline void memcpy(char *to, const char *from, size_t size) { 17 | ::memcpy(to, from, size); 18 | } 19 | }; 20 | template <> struct BasicStreamHelper { 21 | static void memcpy(const char *from, char *to, size_t size) { 22 | ::memcpy(to, from, size); 23 | } 24 | }; 25 | template class BasicStream { 26 | typedef typename std::conditional::type 27 | buffer_ptr_t; 28 | 29 | protected: 30 | inline BasicStream(buffer_ptr_t buffer, size_t buffer_size, 31 | unsigned offset = 0) 32 | : m_buffer(buffer), m_buffer_size(buffer_size), m_offset(offset) {} 33 | template 34 | void read_write(T &&val, Tail &&... tail) { 35 | read_write_impl(std::forward(val)); 36 | read_write(std::forward(tail)...); 37 | } 38 | template 39 | void read_write_buffer(T *buffer, size_t buffer_size) { 40 | for (size_t i = 0; i < buffer_size; ++i) 41 | read_write_impl(buffer[i]); 42 | } 43 | 44 | public: 45 | inline void seek(unsigned offset) { m_offset = offset; } 46 | inline unsigned tell() { return m_offset; } 47 | 48 | private: 49 | inline void read_write() {} 50 | template void read_write_impl(const T &val) { 51 | T tmp = val; 52 | read_write_impl(tmp); 53 | if (!Output && tmp != val) 54 | throw Error(WAP_EINVALIDDATA); 55 | } 56 | template void read_write_impl(T &val) { 57 | if (m_offset > m_buffer_size - sizeof(T)) { 58 | throw wap::Error(WAP_EINVALIDDATA); 59 | } 60 | char *val_ptr = reinterpret_cast(&val); 61 | BasicStreamHelper::memcpy(val_ptr, m_buffer + m_offset, 62 | sizeof(T)); 63 | if (wap::system_is_big_endian()) 64 | std::reverse(val_ptr, val_ptr + sizeof(T)); 65 | m_offset += sizeof(T); 66 | } 67 | template void read_write_impl(T(&buffer)[size]) { 68 | read_write_buffer(buffer, size); 69 | } 70 | 71 | buffer_ptr_t m_buffer; 72 | size_t m_buffer_size; 73 | unsigned m_offset = 0; 74 | }; 75 | } 76 | 77 | class InputStream : public detail::BasicStream { 78 | public: 79 | inline InputStream(const char *buffer, size_t buffer_size, 80 | unsigned offset = 0) 81 | : BasicStream(buffer, buffer_size, offset) {} 82 | template void read(Args &&... args) { 83 | read_write(std::forward(args)...); 84 | } 85 | template void read_buffer(T *buffer, size_t buffer_size) { 86 | read_write_buffer(buffer, buffer_size); 87 | } 88 | }; 89 | 90 | class OutputStream : public detail::BasicStream { 91 | public: 92 | inline OutputStream(char *buffer, size_t buffer_size, unsigned offset = 0) 93 | : BasicStream(buffer, buffer_size, offset) {} 94 | template void write(Args &&... args) { 95 | read_write(std::forward(args)...); 96 | } 97 | template void write_buffer(T *buffer, size_t buffer_size) { 98 | read_write_buffer(buffer, buffer_size); 99 | } 100 | }; 101 | 102 | std::vector read_whole_file(const char *file_path); 103 | } 104 | 105 | #endif 106 | -------------------------------------------------------------------------------- /src/wwd.cpp: -------------------------------------------------------------------------------- 1 | #include "wwd.h" 2 | #include "errors.h" 3 | #include 4 | #include 5 | #include 6 | 7 | wap_wwd *wap_wwd_create() { return new (std::nothrow) wap_wwd; } 8 | 9 | void wap_wwd_free(wap_wwd *wwd) { delete wwd; } 10 | 11 | unsigned wap_wwd_get_checksum(const wap_wwd *wwd) { return wwd->checksum; } 12 | 13 | unsigned wap_wwd_get_plane_count(const wap_wwd *wwd) { 14 | return wwd->planes.size(); 15 | } 16 | 17 | int wap_wwd_set_plane_count(wap_wwd *wwd, unsigned count) { 18 | return wap::handle_exceptions([=]() { wwd->planes.resize(count); }); 19 | } 20 | 21 | wap_plane *wap_wwd_get_plane(wap_wwd *wwd, unsigned plane_index) { 22 | return &wwd->planes[plane_index]; 23 | } 24 | 25 | unsigned wap_wwd_get_tile_description_count(const wap_wwd *wwd) { 26 | return wwd->tile_descriptions.size(); 27 | } 28 | 29 | int wap_wwd_set_tile_description_count(wap_wwd *wwd, unsigned count) { 30 | return wap::handle_exceptions( 31 | [=]() { wwd->tile_descriptions.resize(count); }); 32 | } 33 | 34 | wap_tile_description *wap_wwd_get_tile_description(wap_wwd *wwd, 35 | unsigned description_index) { 36 | return &wwd->tile_descriptions[description_index]; 37 | } 38 | 39 | wap_wwd_properties *wap_wwd_get_properties(wap_wwd *wwd) { 40 | return &wwd->properties; 41 | } 42 | 43 | void wap_plane_get_map_dimensions(wap_plane *plane, unsigned *w, unsigned *h) { 44 | *w = plane->tiles_wide; 45 | *h = plane->tiles_high; 46 | } 47 | 48 | int wap_plane_set_map_dimensions(wap_plane *plane, unsigned w, unsigned h) { 49 | return wap::handle_exceptions([=]() { 50 | plane->tiles.resize(w * h); 51 | plane->tiles_wide = w; 52 | plane->tiles_high = h; 53 | }); 54 | } 55 | 56 | unsigned wap_plane_get_tile(const wap_plane *plane, unsigned x, unsigned y) { 57 | return plane->tiles[y * plane->tiles_wide + x]; 58 | } 59 | 60 | void wap_plane_set_tile(wap_plane *plane, unsigned x, unsigned y, 61 | unsigned tile) { 62 | plane->tiles[y * plane->tiles_wide + x] = tile; 63 | } 64 | 65 | unsigned wap_plane_get_image_set_count(const wap_plane *plane) { 66 | return plane->image_sets.size(); 67 | } 68 | 69 | int wap_plane_set_image_set_count(wap_plane *plane, unsigned count) { 70 | return wap::handle_exceptions([=]() { plane->image_sets.resize(count); }); 71 | } 72 | 73 | const char *wap_plane_get_image_set(const wap_plane *plane, 74 | unsigned image_set_index) { 75 | return plane->image_sets[image_set_index].c_str(); 76 | } 77 | 78 | int wap_plane_set_image_set(wap_plane *plane, unsigned image_set_index, 79 | const char *image_set) { 80 | return wap::handle_exceptions( 81 | [=]() { plane->image_sets[image_set_index].assign(image_set); }); 82 | } 83 | 84 | unsigned wap_plane_get_object_count(const wap_plane *plane) { 85 | return plane->objects.size(); 86 | } 87 | 88 | int wap_plane_set_object_count(wap_plane *plane, unsigned count) { 89 | return wap::handle_exceptions([=]() { plane->objects.resize(count); }); 90 | } 91 | 92 | wap_object *wap_plane_get_object(wap_plane *plane, unsigned object_index) { 93 | return &plane->objects[object_index]; 94 | } 95 | 96 | wap_plane_properties *wap_plane_get_properties(wap_plane *plane) { 97 | return &plane->properties; 98 | } 99 | 100 | const char *wap_object_get_name(const wap_object *object) { 101 | return object->name.c_str(); 102 | } 103 | 104 | int wap_object_set_name(wap_object *object, const char *name) { 105 | return wap::handle_exceptions([=]() { object->name.assign(name); }); 106 | } 107 | 108 | const char *wap_object_get_logic(const wap_object *object) { 109 | return object->logic.c_str(); 110 | } 111 | 112 | int wap_object_set_logic(wap_object *object, const char *logic) { 113 | return wap::handle_exceptions([=]() { object->logic.assign(logic); }); 114 | } 115 | 116 | const char *wap_object_get_image_set(const wap_object *object) { 117 | return object->image_set.c_str(); 118 | } 119 | 120 | int wap_object_set_image_set(wap_object *object, const char *image_set) { 121 | return wap::handle_exceptions( 122 | [=]() { object->image_set.assign(image_set); }); 123 | } 124 | 125 | const char *wap_object_get_animation(const wap_object *object) { 126 | return object->animation.c_str(); 127 | } 128 | 129 | int wap_object_set_animation(wap_object *object, const char *animation) { 130 | return wap::handle_exceptions( 131 | [=]() { object->animation.assign(animation); }); 132 | } 133 | 134 | wap_object_properties *wap_object_get_properties(wap_object *object) { 135 | return &object->properties; 136 | } 137 | -------------------------------------------------------------------------------- /src/wwd_read.cpp: -------------------------------------------------------------------------------- 1 | #include "common.h" 2 | #include "wwd.h" 3 | 4 | #include "buffer.h" 5 | #include "errors.h" 6 | #include "io.h" 7 | #include "wwd_io.h" 8 | 9 | #include 10 | 11 | #include 12 | #include 13 | #include 14 | #include 15 | 16 | void decompress_buffer(char *out_buffer, size_t out_buffer_size, 17 | const char *in_buffer, size_t in_buffer_size) { 18 | z_stream strm; 19 | strm.zalloc = Z_NULL; 20 | strm.zfree = Z_NULL; 21 | strm.opaque = Z_NULL; 22 | strm.avail_in = in_buffer_size; 23 | strm.next_in = (unsigned char *)in_buffer; 24 | strm.avail_out = out_buffer_size; 25 | strm.next_out = (unsigned char *)out_buffer; 26 | 27 | int ret = inflateInit(&strm); 28 | if (ret != Z_OK) { 29 | inflateEnd(&strm); 30 | throw wap::Error::from_zlib_error(ret); 31 | } 32 | 33 | ret = inflate(&strm, Z_FINISH); 34 | inflateEnd(&strm); 35 | 36 | if (ret != Z_STREAM_END) { 37 | throw wap::Error::from_zlib_error(ret); 38 | } 39 | } 40 | 41 | static void read_rect(wap::InputStream &stream, wap_rect &rect) { 42 | stream.read(rect.left, rect.top, rect.right, rect.bottom); 43 | } 44 | 45 | static void read_string(wap::InputStream &stream, std::string &str, 46 | size_t len) { 47 | str.resize(len); 48 | stream.read_buffer(&str[0], str.size()); 49 | } 50 | 51 | static void read_nullterminated_string(wap::InputStream &stream, 52 | std::string &str) { 53 | str.erase(); 54 | char c = '\0'; 55 | while (1) { 56 | stream.read(c); 57 | if (!c) 58 | break; 59 | str += c; 60 | } 61 | } 62 | 63 | static void read_objects(wap::InputStream &stream, 64 | std::vector &objects) { 65 | for (wap_object &obj : objects) { 66 | auto &objp = obj.properties; 67 | 68 | uint32_t name_len, logic_len, image_set_len, animation_len; 69 | stream.read(objp.id, name_len, logic_len, image_set_len, animation_len, 70 | objp.x, objp.y, objp.z, objp.i, objp.add_flags, 71 | objp.dynamic_flags, objp.draw_flags, objp.user_flags, 72 | objp.score, objp.points, objp.powerup, objp.damage, 73 | objp.smarts, objp.health); 74 | 75 | read_rect(stream, objp.move_rect); 76 | read_rect(stream, objp.hit_rect); 77 | read_rect(stream, objp.attack_rect); 78 | read_rect(stream, objp.clip_rect); 79 | read_rect(stream, objp.user_rects[0]); 80 | read_rect(stream, objp.user_rects[1]); 81 | 82 | stream.read(objp.user_values); 83 | stream.read(objp.x_min, objp.y_min, objp.x_max, objp.y_max, 84 | objp.speed_x, objp.speed_y, objp.x_tweak, objp.y_tweak, 85 | objp.counter, objp.speed, objp.width, objp.height, 86 | objp.direction, objp.face_dir, objp.time_delay, 87 | objp.frame_delay, objp.object_type, objp.hit_type_flags, 88 | objp.x_move_res, objp.y_move_res); 89 | 90 | read_string(stream, obj.name, name_len); 91 | read_string(stream, obj.logic, logic_len); 92 | read_string(stream, obj.image_set, image_set_len); 93 | read_string(stream, obj.animation, animation_len); 94 | } 95 | } 96 | 97 | static void read_planes(wap::InputStream &stream, 98 | std::vector &planes) { 99 | std::vector planes_offsets(planes.size()); 100 | 101 | auto plane_offsets = planes_offsets.begin(); 102 | for (wap_plane &plane : planes) { 103 | auto &planep = plane.properties; 104 | uint32_t width_px, height_px; // These values are not actually checked 105 | uint32_t num_image_sets, num_objects; 106 | 107 | stream.read(160, 0, planep.flags, 0, planep.name, width_px, height_px, 108 | planep.tile_width, planep.tile_height, plane.tiles_wide, 109 | plane.tiles_high, 0, 0, planep.movement_x_percent, 110 | planep.movement_y_percent, planep.fill_color, 111 | num_image_sets, num_objects, plane_offsets->tiles_offset, 112 | plane_offsets->image_sets_offset, 113 | plane_offsets->objects_offset, planep.z_coord, 0, 0, 0); 114 | 115 | plane.tiles.resize(plane.tiles_wide * plane.tiles_high); 116 | plane.image_sets.resize(num_image_sets); 117 | plane.objects.resize(num_objects); 118 | ++plane_offsets; 119 | } 120 | 121 | plane_offsets = planes_offsets.begin(); 122 | for (wap_plane &plane : planes) { 123 | if (!plane.tiles.empty()) { 124 | stream.seek(plane_offsets->tiles_offset); 125 | for (uint32_t &tile : plane.tiles) 126 | stream.read(tile); 127 | } 128 | ++plane_offsets; 129 | } 130 | 131 | plane_offsets = planes_offsets.begin(); 132 | for (wap_plane &plane : planes) { 133 | if (!plane.image_sets.empty()) { 134 | stream.seek(plane_offsets->image_sets_offset); 135 | for (std::string &image_set : plane.image_sets) 136 | read_nullterminated_string(stream, image_set); 137 | } 138 | ++plane_offsets; 139 | } 140 | 141 | plane_offsets = planes_offsets.begin(); 142 | for (wap_plane &plane : planes) { 143 | if (!plane.objects.empty()) { 144 | stream.seek(plane_offsets->objects_offset); 145 | read_objects(stream, plane.objects); 146 | } 147 | ++plane_offsets; 148 | } 149 | } 150 | 151 | static void 152 | read_tile_descriptions(wap::InputStream &stream, 153 | std::vector &tile_descriptions) { 154 | uint32_t num_tile_descriptions; 155 | stream.read(32, 0, num_tile_descriptions, 0, 0, 0, 0, 0); 156 | 157 | tile_descriptions.resize(num_tile_descriptions); 158 | 159 | for (wap_tile_description &desc : tile_descriptions) { 160 | uint32_t unknown; 161 | stream.read(desc.type); 162 | stream.read(unknown, desc.width, desc.height); 163 | if (desc.type == WAP_TILE_TYPE_SINGLE) { 164 | stream.read(desc.inside_attrib); 165 | } else { 166 | stream.read(desc.outside_attrib, desc.inside_attrib); 167 | read_rect(stream, desc.rect); 168 | } 169 | } 170 | } 171 | 172 | static void 173 | read_main_block(wap::InputStream &stream, std::vector &planes, 174 | std::vector &tile_descriptions, 175 | const wwd_offsets &offsets) { 176 | stream.seek(offsets.main_block_offset); 177 | read_planes(stream, planes); 178 | 179 | stream.seek(offsets.tile_descriptions_offset); 180 | read_tile_descriptions(stream, tile_descriptions); 181 | } 182 | 183 | static void read_header(wap::InputStream &stream, wap_wwd &wwd, 184 | wwd_offsets &offsets, 185 | uint32_t &decompressed_main_block_size) { 186 | uint32_t signature; 187 | stream.read(signature); 188 | 189 | if (signature != WAP_WWD_HEADER_SIZE) 190 | throw wap::Error(WAP_EINVALIDDATA); 191 | 192 | wap_wwd_properties &wwdp = wwd.properties; 193 | uint32_t unknown; 194 | uint32_t num_planes; 195 | 196 | stream.read(0, wwdp.flags, 0, wwdp.level_name, wwdp.author, wwdp.birth, 197 | wwdp.rez_file, wwdp.image_dir, wwdp.pal_rez, wwdp.start_x, 198 | wwdp.start_y, unknown, num_planes, offsets.main_block_offset, 199 | offsets.tile_descriptions_offset, decompressed_main_block_size, 200 | wwd.checksum, 0, wwdp.launch_app, wwdp.image_sets[0], 201 | wwdp.image_sets[1], wwdp.image_sets[2], wwdp.image_sets[3], 202 | wwdp.prefixes[0], wwdp.prefixes[1], wwdp.prefixes[2], 203 | wwdp.prefixes[3]); 204 | 205 | wwd.planes.resize(num_planes); 206 | } 207 | 208 | void wwd_read(wap_wwd &out_wwd, const char *wwd_buffer, 209 | size_t wwd_buffer_size) { 210 | wap_wwd wwd; 211 | 212 | wap::InputStream stream(wwd_buffer, wwd_buffer_size); 213 | 214 | wap_wwd_properties &wwdp = wwd.properties; 215 | wwd_offsets offsets; 216 | uint32_t decompressed_main_block_size; 217 | read_header(stream, wwd, offsets, decompressed_main_block_size); 218 | 219 | if (wwdp.flags & WAP_WWD_FLAG_COMPRESS) { 220 | offsets.eof_offset = 221 | offsets.main_block_offset + decompressed_main_block_size; 222 | std::vector decompressed_buffer(offsets.eof_offset); 223 | memcpy(decompressed_buffer.data(), wwd_buffer, 224 | offsets.main_block_offset); 225 | const char *compressed_main_block = 226 | wwd_buffer + offsets.main_block_offset; 227 | size_t compressed_main_block_size = 228 | wwd_buffer_size - offsets.main_block_offset; 229 | char *decompressed_main_block = 230 | decompressed_buffer.data() + offsets.main_block_offset; 231 | decompress_buffer(decompressed_main_block, decompressed_main_block_size, 232 | compressed_main_block, compressed_main_block_size); 233 | wap::InputStream stream_decompressed( 234 | &decompressed_buffer[0], decompressed_buffer.size(), stream.tell()); 235 | read_main_block(stream_decompressed, wwd.planes, wwd.tile_descriptions, 236 | offsets); 237 | } else { 238 | offsets.eof_offset = wwd_buffer_size; 239 | read_main_block(stream, wwd.planes, wwd.tile_descriptions, offsets); 240 | } 241 | 242 | std::swap(wwd, out_wwd); 243 | } 244 | 245 | void wwd_open(wap_wwd &wwd, const char *file_path) { 246 | std::vector wwd_buffer = wap::read_whole_file(file_path); 247 | wwd_read(wwd, wwd_buffer.data(), wwd_buffer.size()); 248 | } 249 | 250 | int wap_wwd_read(wap_wwd *out_wwd, const char *wwd_buffer, 251 | size_t wwd_buffer_size) { 252 | return wap::handle_exceptions(wwd_read, *out_wwd, wwd_buffer, 253 | wwd_buffer_size); 254 | } 255 | 256 | int wap_wwd_open(wap_wwd *wwd, const char *file_path) { 257 | return wap::handle_exceptions(wwd_open, *wwd, file_path); 258 | } 259 | -------------------------------------------------------------------------------- /include/wap32/wwd.h: -------------------------------------------------------------------------------- 1 | #ifndef wap_wwd_h 2 | #define wap_wwd_h 3 | 4 | #include "common.h" 5 | #include "buffer.h" 6 | 7 | #include 8 | 9 | WAP_BEGIN_DECLS 10 | 11 | typedef enum { 12 | WAP_WWD_FLAG_USE_Z_COORDS = 1 << 0, 13 | WAP_WWD_FLAG_COMPRESS = 1 << 1, 14 | } wap_wwd_flags_t; 15 | 16 | typedef enum { 17 | WAP_PLANE_FLAG_MAIN_PLANE = 1 << 0, 18 | WAP_PLANE_FLAG_NO_DRAW = 1 << 1, 19 | WAP_PLANE_FLAG_X_WRAPPING = 1 << 2, 20 | WAP_PLANE_FLAG_Y_WRAPPING = 1 << 3, 21 | WAP_PLANE_FLAG_AUTO_TILE_SIZE = 1 << 4, 22 | } wap_plane_flags_t; 23 | 24 | typedef enum { 25 | WAP_OBJECT_ADD_FLAG_DIFFICULT = 1 << 0, 26 | WAP_OBJECT_ADD_FLAG_EYE_CANDY = 1 << 1, 27 | WAP_OBJECT_ADD_FLAG_HIGH_DETAIL = 1 << 2, 28 | WAP_OBJECT_ADD_FLAG_MULTIPLAYER = 1 << 3, 29 | WAP_OBJECT_ADD_FLAG_EXTRA_MEMORY = 1 << 4, 30 | WAP_OBJECT_ADD_FLAG_FAST_CPU = 1 << 5, 31 | } wap_object_add_flags_t; 32 | 33 | typedef enum { 34 | WAP_OBJECT_DRAW_FLAG_NO_DRAW = 1 << 0, 35 | WAP_OBJECT_DRAW_FLAG_MIRROR = 1 << 1, 36 | WAP_OBJECT_DRAW_FLAG_INVERT = 1 << 2, 37 | WAP_OBJECT_DRAW_FLAG_FLASH = 1 << 3, 38 | } wap_object_draw_flags_t; 39 | 40 | typedef enum { 41 | WAP_OBJECT_DYNAMIC_FLAG_NO_HIT = 1 << 0, 42 | WAP_OBJECT_DYNAMIC_FLAG_ALWAYS_ACTIVE = 1 << 1, 43 | WAP_OBJECT_DYNAMIC_FLAG_SAFE = 1 << 2, 44 | WAP_OBJECT_DYNAMIC_FLAG_AUTO_HIT_DAMAGE = 1 << 3, 45 | } wap_object_dynamic_flags_t; 46 | 47 | typedef enum { 48 | WAP_OBJECT_USER_FLAG_1 = 1 << 0, 49 | WAP_OBJECT_USER_FLAG_2 = 1 << 1, 50 | WAP_OBJECT_USER_FLAG_3 = 1 << 2, 51 | WAP_OBJECT_USER_FLAG_4 = 1 << 3, 52 | WAP_OBJECT_USER_FLAG_5 = 1 << 4, 53 | WAP_OBJECT_USER_FLAG_6 = 1 << 5, 54 | WAP_OBJECT_USER_FLAG_7 = 1 << 6, 55 | WAP_OBJECT_USER_FLAG_8 = 1 << 7, 56 | WAP_OBJECT_USER_FLAG_9 = 1 << 8, 57 | WAP_OBJECT_USER_FLAG_10 = 1 << 9, 58 | WAP_OBJECT_USER_FLAG_11 = 1 << 10, 59 | WAP_OBJECT_USER_FLAG_12 = 1 << 11, 60 | } wap_object_user_flags_t; 61 | 62 | typedef enum { 63 | WAP_OBJECT_TYPE_GENERIC = 1 << 0, 64 | WAP_OBJECT_TYPE_PLAYER = 1 << 1, 65 | WAP_OBJECT_TYPE_ENEMY = 1 << 2, 66 | WAP_OBJECT_TYPE_POWERUP = 1 << 3, 67 | WAP_OBJECT_TYPE_SHOT = 1 << 4, 68 | WAP_OBJECT_TYPE_PSHOT = 1 << 5, 69 | WAP_OBJECT_TYPE_ESHOT = 1 << 6, 70 | WAP_OBJECT_TYPE_SPECIAL = 1 << 7, 71 | WAP_OBJECT_TYPE_USER1 = 1 << 8, 72 | WAP_OBJECT_TYPE_USER2 = 1 << 9, 73 | WAP_OBJECT_TYPE_USER3 = 1 << 10, 74 | WAP_OBJECT_TYPE_USER4 = 1 << 11, 75 | } wap_object_type_flags_t; 76 | 77 | typedef enum { 78 | WAP_TILE_TYPE_SINGLE = 1, 79 | WAP_TILE_TYPE_DOUBLE = 2, 80 | } wap_tile_type_flags_t; 81 | 82 | typedef enum { 83 | WAP_TILE_ATTRIBUTE_CLEAR, 84 | WAP_TILE_ATTRIBUTE_SOLID = 1 << 0, 85 | WAP_TILE_ATTRIBUTE_GROUND = 1 << 1, 86 | WAP_TILE_ATTRIBUTE_CLIMB = 1 << 2, 87 | WAP_TILE_ATTRIBUTE_DEATH = 1 << 3, 88 | } wap_tile_attribute_flags_t; 89 | 90 | typedef struct wap_wwd wap_wwd; 91 | typedef struct wap_plane wap_plane; 92 | typedef struct wap_object wap_object; 93 | 94 | typedef struct { 95 | uint32_t left; 96 | uint32_t top; 97 | uint32_t right; 98 | uint32_t bottom; 99 | } wap_rect; 100 | 101 | typedef struct { 102 | uint32_t flags; /* WAP_WWD_FLAG_ flags */ 103 | /* WapWorld expects all these char buffers to be null-terminated */ 104 | char level_name[64]; 105 | char author[64]; 106 | char birth[64]; 107 | char rez_file[256]; 108 | char image_dir[128]; 109 | char pal_rez[128]; 110 | int32_t start_x; 111 | int32_t start_y; 112 | char launch_app[128]; 113 | char image_sets[4][128]; 114 | char prefixes[4][32]; 115 | } wap_wwd_properties; 116 | 117 | typedef struct { 118 | uint32_t flags; /* WAP_PLANE_FLAG_ flags */ 119 | char name[64]; /* WapWorld expects this char buffer to be null-terminated */ 120 | uint32_t tile_width; /* tile's width in pixels */ 121 | uint32_t tile_height; /* tile's height in pixels */ 122 | int32_t movement_x_percent; 123 | int32_t movement_y_percent; 124 | uint32_t fill_color; 125 | int32_t z_coord; 126 | } wap_plane_properties; 127 | 128 | typedef struct { 129 | int32_t id; /* Any value is accepted by WapWorld, but a good id should be 130 | positive and unique. */ 131 | int32_t x; 132 | int32_t y; 133 | int32_t z; 134 | int32_t i; 135 | uint32_t add_flags; /* WAP_OBJECT_ADD_FLAG_ flags */ 136 | uint32_t dynamic_flags; /* WAP_OBJECT_DYNAMIC_FLAG_ flags */ 137 | uint32_t draw_flags; /* WAP_OBJECT_DRAW_FLAG_ flags */ 138 | uint32_t user_flags; /* WAP_OBJECT_USER_FLAG_ flags */ 139 | int32_t score; 140 | int32_t points; 141 | int32_t powerup; 142 | int32_t damage; 143 | int32_t smarts; 144 | int32_t health; 145 | wap_rect move_rect; 146 | wap_rect hit_rect; 147 | wap_rect attack_rect; 148 | wap_rect clip_rect; 149 | wap_rect user_rects[2]; 150 | int32_t user_values[8]; 151 | int32_t x_min; 152 | int32_t y_min; 153 | int32_t x_max; 154 | int32_t y_max; 155 | int32_t speed_x; 156 | int32_t speed_y; 157 | int32_t x_tweak; 158 | int32_t y_tweak; 159 | int32_t counter; 160 | int32_t speed; 161 | int32_t width; 162 | int32_t height; 163 | int32_t direction; 164 | int32_t face_dir; 165 | int32_t time_delay; 166 | int32_t frame_delay; 167 | uint32_t object_type; /* WAP_OBJECT_TYPE_ single value */ 168 | uint32_t hit_type_flags; /* WAP_OBJECT_TYPE_ flags */ 169 | int32_t x_move_res; 170 | int32_t y_move_res; 171 | } wap_object_properties; 172 | 173 | typedef struct { 174 | uint32_t type; /* WAP_TILE_TYPE_ single value */ 175 | uint32_t width; /* in pixels */ 176 | uint32_t height; /* in pixels */ 177 | uint32_t inside_attrib; /* WAP_TILE_ATTRIBUTE_ */ 178 | /* outside_attrib and rect only if type == WAP_TILE_TYPE_DOUBLE */ 179 | uint32_t outside_attrib; /* WAP_TILE_ATTRIBUTE_ */ 180 | wap_rect rect; 181 | } wap_tile_description; 182 | 183 | /* NULL if out of memory */ 184 | WAP_API wap_wwd *wap_wwd_create(); 185 | 186 | WAP_API void wap_wwd_free(wap_wwd *wwd); 187 | 188 | /* Possible errors: WAP_ERROR, WAP_ENOMEMORY, WAP_EINVALIDDATA */ 189 | WAP_API int wap_wwd_read(wap_wwd *wwd, const char *wwd_buffer, 190 | size_t wwd_buffer_size); 191 | 192 | /* Possible errors: WAP_ERROR, WAP_ENOMEMORY, WAP_EFILE, WAP_EINVALIDDATA */ 193 | WAP_API int wap_wwd_open(wap_wwd *wwd, const char *file_path); 194 | 195 | /* Possible errors: WAP_ERROR, WAP_ENOMEMORY, */ 196 | WAP_API int wap_wwd_write(const wap_wwd *wwd, wap_buffer *out_wwd_buffer); 197 | 198 | /* Possible errors: WAP_ERROR, WAP_ENOMEMORY, WAP_EFILE */ 199 | WAP_API int wap_wwd_save(const wap_wwd *wwd, const char *file_path); 200 | 201 | /* Get the checksum calculated by a program that created the WWD buffer that was 202 | * read using wap_wwd_read or wap_wwd_open. 203 | * Returns zero if no such buffer was read. 204 | */ 205 | WAP_API uint32_t wap_wwd_get_checksum(const wap_wwd *wwd); 206 | 207 | WAP_API uint32_t wap_wwd_get_plane_count(const wap_wwd *wwd); 208 | 209 | /* Possible error: WAP_ENOMEMORY */ 210 | WAP_API int wap_wwd_set_plane_count(wap_wwd *wwd, uint32_t count); 211 | 212 | WAP_API wap_plane *wap_wwd_get_plane(wap_wwd *wwd, uint32_t plane_index); 213 | 214 | WAP_API uint32_t wap_wwd_get_tile_description_count(const wap_wwd *wwd); 215 | 216 | /* Possible error: WAP_ENOMEMORY */ 217 | WAP_API int wap_wwd_set_tile_description_count(wap_wwd *wwd, uint32_t count); 218 | 219 | WAP_API wap_tile_description * 220 | wap_wwd_get_tile_description(wap_wwd *wwd, uint32_t description_index); 221 | 222 | WAP_API wap_wwd_properties *wap_wwd_get_properties(wap_wwd *wwd); 223 | 224 | WAP_API void wap_plane_get_map_dimensions(wap_plane *plane, uint32_t *w, 225 | uint32_t *h); 226 | 227 | /* Note: after changing map's dimensions, value returned by 228 | * wap_plane_get_tile(plane, x, y) is undefined for any x, y until you 229 | * call wap_plane_set_tile(plane, x, y, tile) or read a new buffer with 230 | * wap_wwd_read or wap_wwd_open. 231 | * Possible error: WAP_ENOMEMORY 232 | */ 233 | WAP_API int wap_plane_set_map_dimensions(wap_plane *plane, uint32_t w, 234 | uint32_t h); 235 | 236 | WAP_API uint32_t wap_plane_get_tile(const wap_plane *plane, uint32_t x, 237 | uint32_t y); 238 | 239 | WAP_API void wap_plane_set_tile(wap_plane *plane, uint32_t x, uint32_t y, 240 | uint32_t tile); 241 | 242 | WAP_API uint32_t wap_plane_get_image_set_count(const wap_plane *plane); 243 | 244 | /* Possible error: WAP_ENOMEMORY */ 245 | WAP_API int wap_plane_set_image_set_count(wap_plane *plane, uint32_t count); 246 | 247 | WAP_API const char *wap_plane_get_image_set(const wap_plane *plane, 248 | uint32_t image_set_index); 249 | 250 | /* Possible error: WAP_ENOMEMORY */ 251 | WAP_API int wap_plane_set_image_set(wap_plane *plane, uint32_t image_set_index, 252 | const char *image_set); 253 | 254 | WAP_API uint32_t wap_plane_get_object_count(const wap_plane *plane); 255 | 256 | /* Possible error: WAP_ENOMEMORY */ 257 | WAP_API int wap_plane_set_object_count(wap_plane *plane, uint32_t count); 258 | 259 | WAP_API wap_object *wap_plane_get_object(wap_plane *plane, 260 | uint32_t object_index); 261 | 262 | WAP_API wap_plane_properties *wap_plane_get_properties(wap_plane *plane); 263 | 264 | WAP_API const char *wap_object_get_name(const wap_object *object); 265 | 266 | /* Possible error: WAP_ENOMEMORY */ 267 | WAP_API int wap_object_set_name(wap_object *object, const char *name); 268 | 269 | WAP_API const char *wap_object_get_logic(const wap_object *object); 270 | 271 | /* Possible error: WAP_ENOMEMORY */ 272 | WAP_API int wap_object_set_logic(wap_object *object, const char *logic); 273 | 274 | WAP_API const char *wap_object_get_image_set(const wap_object *object); 275 | 276 | /* Possible error: WAP_ENOMEMORY */ 277 | WAP_API int wap_object_set_image_set(wap_object *object, const char *image_set); 278 | 279 | WAP_API const char *wap_object_get_animation(const wap_object *object); 280 | 281 | /* Possible error: WAP_ENOMEMORY */ 282 | WAP_API int wap_object_set_animation(wap_object *object, const char *animation); 283 | 284 | WAP_API wap_object_properties *wap_object_get_properties(wap_object *object); 285 | 286 | WAP_END_DECLS 287 | 288 | #endif 289 | -------------------------------------------------------------------------------- /src/wwd_write.cpp: -------------------------------------------------------------------------------- 1 | #include "common.h" 2 | #include "wwd.h" 3 | 4 | #include "buffer.h" 5 | #include "errors.h" 6 | #include "io.h" 7 | #include "wwd_io.h" 8 | 9 | #include 10 | 11 | #include 12 | #include 13 | #include 14 | #include 15 | 16 | std::vector compress_buffer(const char *in_buffer, 17 | size_t in_buffer_size) { 18 | std::vector out_buffer; 19 | 20 | z_stream strm; 21 | strm.zalloc = Z_NULL; 22 | strm.zfree = Z_NULL; 23 | strm.opaque = Z_NULL; 24 | strm.avail_in = in_buffer_size; 25 | strm.next_in = (unsigned char *)in_buffer; 26 | 27 | int ret = deflateInit(&strm, 9); 28 | if (ret != Z_OK) { 29 | deflateEnd(&strm); 30 | throw wap::Error::from_zlib_error(ret); 31 | } 32 | 33 | out_buffer.resize(0); 34 | 35 | do { 36 | constexpr size_t step = 64 * 1024; 37 | out_buffer.resize(out_buffer.size() + step); 38 | strm.avail_out = out_buffer.capacity() - strm.total_out; 39 | strm.next_out = (unsigned char *)out_buffer.data() + strm.total_out; 40 | ret = deflate(&strm, Z_FINISH); 41 | } while (ret != Z_STREAM_END); 42 | 43 | deflateEnd(&strm); 44 | out_buffer.resize(out_buffer.capacity() - strm.avail_out); 45 | return out_buffer; 46 | } 47 | 48 | static void calculate_offsets(wwd_offsets &offsets, 49 | std::vector &planes_offsets, 50 | const wap_wwd &wwd) { 51 | size_t num_planes = wwd.planes.size(); 52 | planes_offsets.resize(num_planes); 53 | 54 | uint32_t offset = WAP_WWD_HEADER_SIZE; 55 | offsets.main_block_offset = offset; 56 | 57 | offset += num_planes * WAP_WWD_PLANE_DESCRIPTION_SIZE; 58 | offsets.tile_descriptions_offset = offset; 59 | 60 | for (size_t i = 0; i < num_planes; ++i) { 61 | planes_offsets[i].tiles_offset = offset; 62 | offset += wwd.planes[i].tiles.size() * 4; 63 | } 64 | 65 | for (size_t i = 0; i < num_planes; ++i) { 66 | planes_offsets[i].image_sets_offset = offset; 67 | for (const std::string &image_set : wwd.planes[i].image_sets) 68 | offset += image_set.size() + sizeof('\0'); 69 | } 70 | 71 | for (size_t i = 0; i < num_planes; ++i) { 72 | planes_offsets[i].objects_offset = offset; 73 | for (const wap_object &object : wwd.planes[i].objects) { 74 | offset += WAP_WWD_OBJECT_DESCRIPTION_SIZE; 75 | offset += object.name.size() + object.logic.size() + 76 | object.image_set.size() + object.animation.size(); 77 | } 78 | } 79 | 80 | offsets.tile_descriptions_offset = offset; 81 | offset += 8 * sizeof(uint32_t); 82 | 83 | for (const wap_tile_description &desc : wwd.tile_descriptions) { 84 | if (desc.type == WAP_TILE_TYPE_SINGLE) 85 | offset += 5 * sizeof(uint32_t); 86 | else 87 | offset += 10 * sizeof(uint32_t); 88 | } 89 | 90 | offsets.eof_offset = offset; 91 | } 92 | 93 | // This function tries to imitate WapWorld's checksum function. It's result is 94 | // different on all compressed and some 95 | // non compressed files. That's ok, because as far as I know it's only used to 96 | // check if two multiplayer clients have the same 97 | // version of map installed. Partial compatiblity with WapWorld's checksum is 98 | // just to allow comparing our wwd buffers with 99 | // WapWorld generated ones during testing. 100 | 101 | uint32_t wwd_checksum(const char *buffer, size_t buffer_size) { 102 | uint32_t checksum = UINT32_MAX - buffer_size - 159; 103 | for (size_t i = 0; i < buffer_size; ++i) { 104 | uint32_t delta = (i - (unsigned char)buffer[i]); 105 | checksum -= delta; 106 | } 107 | return checksum; 108 | } 109 | 110 | static void write_rect(wap::OutputStream &stream, const wap_rect &rect) { 111 | stream.write(rect.left, rect.top, rect.right, rect.bottom); 112 | } 113 | 114 | static void write_string(wap::OutputStream &stream, const std::string &str) { 115 | stream.write_buffer(str.data(), str.size()); 116 | } 117 | 118 | static void write_nullterminated_string(wap::OutputStream &stream, 119 | const std::string &str) { 120 | stream.write_buffer(str.c_str(), str.size() + 1); 121 | } 122 | 123 | static void write_objects(wap::OutputStream &stream, 124 | const std::vector &objects) { 125 | for (const wap_object &obj : objects) { 126 | auto &objp = obj.properties; 127 | 128 | stream.write(objp.id, (uint32_t)obj.name.size(), 129 | (uint32_t)obj.logic.size(), (uint32_t)obj.image_set.size(), 130 | (uint32_t)obj.animation.size(), objp.x, objp.y, objp.z, 131 | objp.i, objp.add_flags, objp.dynamic_flags, 132 | objp.draw_flags, objp.user_flags, objp.score, objp.points, 133 | objp.powerup, objp.damage, objp.smarts, objp.health); 134 | 135 | write_rect(stream, objp.move_rect); 136 | write_rect(stream, objp.hit_rect); 137 | write_rect(stream, objp.attack_rect); 138 | write_rect(stream, objp.clip_rect); 139 | write_rect(stream, objp.user_rects[0]); 140 | write_rect(stream, objp.user_rects[1]); 141 | 142 | stream.write(objp.user_values, objp.x_min, objp.y_min, objp.x_max, 143 | objp.y_max, objp.speed_x, objp.speed_y, objp.x_tweak, 144 | objp.y_tweak, objp.counter, objp.speed, objp.width, 145 | objp.height, objp.direction, objp.face_dir, 146 | objp.time_delay, objp.frame_delay, objp.object_type, 147 | objp.hit_type_flags, objp.x_move_res, objp.y_move_res); 148 | 149 | write_string(stream, obj.name); 150 | write_string(stream, obj.logic); 151 | write_string(stream, obj.image_set); 152 | write_string(stream, obj.animation); 153 | } 154 | } 155 | 156 | static void write_planes(wap::OutputStream &stream, 157 | const std::vector &planes, 158 | const std::vector &planes_offsets) { 159 | auto plane_offsets = planes_offsets.cbegin(); 160 | for (const wap_plane &plane : planes) { 161 | auto &planep = plane.properties; 162 | uint32_t height_px = plane.tiles_high * planep.tile_height; 163 | uint32_t width_px = plane.tiles_wide * planep.tile_width; 164 | uint32_t objects_offset = 165 | plane.objects.empty() ? 0 : plane_offsets->objects_offset; 166 | 167 | stream.write( 168 | 160, 0, planep.flags, 0, planep.name, width_px, height_px, 169 | planep.tile_width, planep.tile_height, plane.tiles_wide, 170 | plane.tiles_high, 0, 0, planep.movement_x_percent, 171 | planep.movement_y_percent, planep.fill_color, 172 | (uint32_t)plane.image_sets.size(), (uint32_t)plane.objects.size(), 173 | plane_offsets->tiles_offset, plane_offsets->image_sets_offset, 174 | objects_offset, planep.z_coord, 0, 0, 0); 175 | 176 | ++plane_offsets; 177 | } 178 | 179 | for (const wap_plane &plane : planes) { 180 | for (uint32_t tile : plane.tiles) 181 | stream.write(tile); 182 | } 183 | 184 | for (const wap_plane &plane : planes) { 185 | for (const std::string &image_set : plane.image_sets) 186 | write_nullterminated_string(stream, image_set); 187 | } 188 | 189 | for (const wap_plane &plane : planes) { 190 | write_objects(stream, plane.objects); 191 | } 192 | } 193 | 194 | static void write_tile_descriptions( 195 | wap::OutputStream &stream, 196 | const std::vector &tile_descriptions) { 197 | stream.write(32, 0, (uint32_t)tile_descriptions.size(), 0, 0, 0, 0, 0); 198 | 199 | for (const wap_tile_description &desc : tile_descriptions) { 200 | stream.write(desc.type, 0, desc.width, desc.height); 201 | if (desc.type == WAP_TILE_TYPE_SINGLE) { 202 | stream.write(desc.inside_attrib); 203 | } else { 204 | stream.write(desc.outside_attrib, desc.inside_attrib); 205 | write_rect(stream, desc.rect); 206 | } 207 | } 208 | } 209 | 210 | static void 211 | write_main_block(wap::OutputStream &stream, 212 | const std::vector &planes, 213 | const std::vector &planes_offsets, 214 | const std::vector &tile_descriptions) { 215 | write_planes(stream, planes, planes_offsets); 216 | write_tile_descriptions(stream, tile_descriptions); 217 | } 218 | 219 | static void write_header(wap::OutputStream &stream, const wap_wwd &wwd, 220 | const wwd_offsets &offsets, 221 | uint32_t decompressed_main_block_size, 222 | uint32_t checksum) { 223 | const wap_wwd_properties &wwdp = wwd.properties; 224 | uint32_t num_planes = wwd.planes.size(); 225 | stream.write(WAP_WWD_HEADER_SIZE); 226 | stream.write(0, wwdp.flags, 0, wwdp.level_name, wwdp.author, wwdp.birth, 227 | wwdp.rez_file, wwdp.image_dir, wwdp.pal_rez, wwdp.start_x, 228 | wwdp.start_y, 0, num_planes, offsets.main_block_offset, 229 | offsets.tile_descriptions_offset, decompressed_main_block_size, 230 | checksum, 0, wwdp.launch_app, wwdp.image_sets[0], 231 | wwdp.image_sets[1], wwdp.image_sets[2], wwdp.image_sets[3], 232 | wwdp.prefixes[0], wwdp.prefixes[1], wwdp.prefixes[2], 233 | wwdp.prefixes[3]); 234 | } 235 | 236 | void wwd_write(const wap_wwd *wwd, std::vector &out_wwd_buffer) { 237 | wwd_offsets offsets; 238 | std::vector planes_offsets; 239 | calculate_offsets(offsets, planes_offsets, *wwd); 240 | 241 | std::vector wwd_buffer; 242 | const wap_wwd_properties &wwdp = wwd->properties; 243 | 244 | if (wwdp.flags & WAP_WWD_FLAG_COMPRESS) { 245 | std::vector main_block(offsets.eof_offset - 246 | offsets.main_block_offset); 247 | wap::OutputStream main_block_stream(main_block.data(), 248 | main_block.size()); 249 | write_main_block(main_block_stream, wwd->planes, planes_offsets, 250 | wwd->tile_descriptions); 251 | std::vector compressed_main_block = 252 | compress_buffer(main_block.data(), main_block.size()); 253 | wwd_buffer.resize(offsets.main_block_offset + 254 | compressed_main_block.size()); 255 | wap::OutputStream stream(wwd_buffer.data(), wwd_buffer.size()); 256 | uint32_t checksum = wwd_checksum(main_block.data(), main_block.size()); 257 | write_header(stream, *wwd, offsets, main_block.size(), checksum); 258 | stream.write_buffer(compressed_main_block.data(), 259 | compressed_main_block.size()); 260 | } else { 261 | wwd_buffer.resize(offsets.eof_offset); 262 | wap::OutputStream stream(wwd_buffer.data(), wwd_buffer.size()); 263 | stream.seek(offsets.main_block_offset); 264 | write_main_block(stream, wwd->planes, planes_offsets, 265 | wwd->tile_descriptions); 266 | char *main_block = wwd_buffer.data() + offsets.main_block_offset; 267 | size_t main_block_size = wwd_buffer.size() - offsets.main_block_offset; 268 | uint32_t checksum = wwd_checksum(main_block, main_block_size); 269 | stream.seek(0); 270 | write_header(stream, *wwd, offsets, 0, checksum); 271 | } 272 | 273 | std::swap(wwd_buffer, out_wwd_buffer); 274 | } 275 | 276 | void wwd_save(const wap_wwd *wwd, const char *file_path) { 277 | std::ofstream file(file_path, std::ios::binary); 278 | if (!file.good()) 279 | throw wap::Error(WAP_EFILE); 280 | std::vector wwd_buffer; 281 | wwd_write(wwd, wwd_buffer); 282 | file.write(wwd_buffer.data(), wwd_buffer.size()); 283 | if (!file.good()) 284 | throw wap::Error(WAP_EFILE); 285 | } 286 | 287 | int wap_wwd_write(const wap_wwd *wwd, wap_buffer *out_wwd_buffer) { 288 | return wap::handle_exceptions( 289 | wwd_write, wwd, *wap::cast_wap_buffer_to_vector(out_wwd_buffer)); 290 | } 291 | 292 | int wap_wwd_save(const wap_wwd *wwd, const char *file_path) { 293 | return wap::handle_exceptions(wwd_save, wwd, file_path); 294 | } 295 | -------------------------------------------------------------------------------- /tools/wwd2html/pywap32.py: -------------------------------------------------------------------------------- 1 | from cffi import FFI 2 | import re 3 | 4 | _libwap32_headers = [ 5 | '../../include/wap32/buffer.h', 6 | '../../include/wap32/errors.h', 7 | '../../include/wap32/wwd.h' 8 | ] 9 | 10 | def _preprocess_lshifts(s): # cffi can't parse them 11 | return re.sub(r'(\d+) << (\d+)', lambda m: str(int(m.group(1)) << int(m.group(2))), s) 12 | 13 | _ffi = FFI() 14 | 15 | for header in _libwap32_headers: 16 | with open(header, 'r') as header_file: 17 | header_content = header_file.read() 18 | header_content = re.search(r'WAP_BEGIN_DECLS\n(.*?)WAP_END_DECLS\n', header_content, re.DOTALL).group(1) 19 | header_content = header_content.replace('WAP_API ', '') 20 | header_content = _preprocess_lshifts(header_content) 21 | _ffi.cdef(header_content) 22 | 23 | _libwap32 = _ffi.dlopen('../../build/Release/libwap32.dylib') 24 | 25 | _exception_map = { 26 | _libwap32.WAP_ENOMEMORY: MemoryError, 27 | _libwap32.WAP_EFILE: OSError, 28 | _libwap32.WAP_EINVALIDDATA: ValueError, 29 | } 30 | 31 | def _handle_error(error_code): 32 | if error_code < 0: 33 | error_str = _ffi.string(_ffi.cast("wap_error_t", error_code)) 34 | try: 35 | ExceptionClass = _exception_map[error_code] 36 | raise ExceptionClass(error_str) 37 | except KeyError: 38 | raise Exception(error_str) 39 | 40 | def _decode_string(str, encoding, maxlen=-1): 41 | bytes = _ffi.string(str, maxlen) 42 | if encoding is None: 43 | return bytes 44 | else: 45 | return bytes.decode(encoding) 46 | 47 | def _encode_string(str, encoding): 48 | if encoding is None: 49 | return bytes(str) 50 | else: 51 | return str.encode(encoding) 52 | 53 | def _getattr(super, obj, key): 54 | try: 55 | return getattr(obj, key) 56 | except TypeError: 57 | return obj[key] 58 | 59 | def _setattr(super, obj, key, value): 60 | try: 61 | setattr(obj, key, value) 62 | except TypeError: 63 | obj[key] = value 64 | 65 | def _make_accessors(ctype): 66 | if ctype.kind == 'array': 67 | length = ctype.length 68 | if ctype.item.cname == 'char': # fixed length string 69 | def get(super, obj, key): 70 | return _decode_string(_getattr(super, obj, key), super.encoding) 71 | def set(super, obj, key, value): 72 | encoding = super.encoding 73 | buffer = value 74 | if encoding is None: 75 | if not isinstance(value, bytes): 76 | raise TypeError 77 | else: 78 | if not isinstance(value, str): 79 | raise TypeError 80 | buffer = _encode_string(value, super.encoding) 81 | if len(buffer) > length: 82 | raise ValueError 83 | _setattr(super, obj, key, buffer) 84 | return (get, set) 85 | else: # array of primitives 86 | item_get, item_set = _make_accessors(ctype.item) 87 | def get(super, obj, key): 88 | array_cdata = _getattr(super, obj, key) 89 | return Array(super, array_cdata, item_get, item_set, lambda _: ctype.length, None) 90 | def set(super, obj, key, value): 91 | raise TypeError("arrays are not assignable") 92 | return (get, set) 93 | else: 94 | if ctype.cname == 'wap_rect': 95 | def get(super, obj, key): 96 | rect_cdata = _getattr(super, obj, key) 97 | return Rect(rect_cdata) 98 | def set(super, obj, key, rect): 99 | tpl = (rect.left, rect.top, rect.right, rect.bottom) 100 | _setattr(super, obj, key, tpl) 101 | return (get, set) 102 | else: # primitive property 103 | return (_getattr, _setattr) 104 | 105 | def _make_ctype_property(name, ctype): 106 | get, set = _make_accessors(ctype) 107 | return property(lambda self: get(self._super, self._properties, name), 108 | lambda self, value: set(self._super, self._properties, name, value)) 109 | 110 | def _make_string_property(str_get, str_set): 111 | def get(self): 112 | return _decode_string(str_get(self._cdata), self._super.encoding) 113 | def set(self, value): 114 | _handle_error(str_set(self._cdata, _encode_string(value, self._super.encoding))) 115 | return property(get, set) 116 | 117 | def _make_array_property(*args): 118 | return property(lambda self: Array(self._super, self._cdata, *args)) 119 | 120 | def _add_properties(cls, properties_struct): 121 | for name, field in properties_struct.fields: 122 | setattr(cls, name, _make_ctype_property(name, field.type)) 123 | 124 | def _add_flags(cls, prefix, flags_enum): 125 | for name, value in flags_enum.relements.items(): 126 | setattr(cls, name.replace(prefix, ''), value) 127 | 128 | class Array: 129 | def __init__(self, super, cdata, get_element, set_element, get_element_count, set_element_count): 130 | self._super = super 131 | self._cdata = cdata 132 | self._get_element = get_element 133 | self._set_element = set_element 134 | self._get_element_count = get_element_count 135 | self._set_element_count = set_element_count 136 | def __len__(self): 137 | return self._get_element_count(self._cdata) 138 | def __getitem__(self, key): 139 | return self._get_element(self._super, self._cdata, self._array_index(key)) 140 | def __setitem__(self, key, value): 141 | if not self._set_element: 142 | raise TypeError("array is read-only") 143 | self._set_element(self._super, self._cdata, self._array_index(key), value) 144 | def __repr__(self): 145 | return "Array([%s])" % ', '.join([repr(element) for element in self]) 146 | def resize(self, new_size): 147 | if not self.set_element_count: 148 | raise TypeError("cannot resize static array") 149 | if new_size < 0: 150 | raise ValueError 151 | _handle_error(self._set_element_count(self._cdata, new_size)) 152 | def _array_index(self, index): 153 | if not isinstance(index, int): 154 | raise TypeError 155 | length = len(self) 156 | if index < 0: 157 | index = length + index 158 | if index >= length: 159 | raise IndexError 160 | return index 161 | 162 | class Rect: 163 | def __init__(self, rect_cdata): 164 | self._super = None 165 | self._cdata = rect_cdata 166 | self._properties = rect_cdata 167 | def __repr__(self): 168 | return "Rect(left=%r, top=%d, right=%d, bottom=%d)" % (self.left, self.top, self.right, self.bottom) 169 | 170 | _add_properties(Rect, _ffi.typeof("wap_rect")) 171 | 172 | class Wwd: 173 | def __init__(self): 174 | self._cdata = _libwap32.wap_wwd_create() 175 | if not self._cdata: 176 | raise MemoryError 177 | self._super = self 178 | self._properties = _libwap32.wap_wwd_get_properties(self._cdata) 179 | def __repr__(self): 180 | return 'WWD "%s"' % self.level_name 181 | encoding = 'ascii' 182 | planes = _make_array_property(lambda super, ptr, idx: Plane(super, _libwap32.wap_wwd_get_plane(ptr, idx)), 183 | None, 184 | _libwap32.wap_wwd_get_plane_count, 185 | _libwap32.wap_wwd_set_plane_count) 186 | tile_descriptions = _make_array_property(lambda _, ptr, idx: TileDescription(_libwap32.wap_wwd_get_tile_description(ptr, idx)), 187 | None, 188 | _libwap32.wap_wwd_get_tile_description_count, 189 | _libwap32.wap_wwd_set_tile_description_count) 190 | def read(self, file): 191 | buffer = file.read() 192 | _handle_error(_libwap32.wap_wwd_read(wwd._cdata, buffer, len(buffer))) 193 | def write(self, file): 194 | buffer = _ffi.new("wap_buffer") 195 | _libwap32.wap_buffer_init(buffer) 196 | _handle_error(_libwap32.wap_wwd_write(wwd._cdata, buffer)) 197 | data, size = _libwap32.wap_buffer_data(buffer), _libwap32.wap_buffer_size(buffer) 198 | file.write(_ffi.buffer(data, size)) 199 | _libwap32.wap_buffer_destroy(buffer) 200 | @staticmethod 201 | def open(filename): 202 | with open(filename, 'rb') as file: 203 | buffer = file.read() 204 | wwd = Wwd() 205 | _handle_error(_libwap32.wap_wwd_read(wwd._cdata, buffer, len(buffer))) 206 | return wwd 207 | def save(self, filename): 208 | with open(filename, 'wb') as file: 209 | self.write(file) 210 | 211 | _add_properties(Wwd, _ffi.typeof("wap_wwd_properties")) 212 | _add_flags(Wwd, 'WAP_WWD_FLAG_', _ffi.typeof("wap_wwd_flags_t")) 213 | 214 | def _get_image_set(super, ptr, idx): 215 | return _decode_string(_libwap32.wap_plane_get_image_set(ptr, idx), super.encoding) 216 | 217 | def _set_image_set(super, ptr, idx, value): 218 | _handle_error(_libwap32.wap_plane_set_image_set(ptr, idx, _encode_string(value, encoding))) 219 | 220 | class Plane: 221 | def _check_map_bounds(self, xy): 222 | size = self.size 223 | x, y = xy 224 | if x < 0 or x >= size[0] or y < 0 or y >= size[1]: 225 | raise IndexError 226 | def __init__(self, wwd, plane_cdata): 227 | self._super = wwd 228 | self._cdata = plane_cdata 229 | self._properties = _libwap32.wap_plane_get_properties(self._cdata) 230 | self._dimensions = _ffi.new("uint32_t[2]") 231 | def get_tile(self, xy): 232 | self._check_map_bounds(xy) 233 | return _libwap32.wap_plane_get_tile(self._cdata, xy[0], xy[1]) 234 | def set_tile(self, xy, tile): 235 | self._check_map_bounds(xy) 236 | return _libwap32.wap_plane_set_tile(self._cdata, xy[0], xy[1], tile) 237 | @property 238 | def size(self): 239 | _libwap32.wap_plane_get_map_dimensions(self._cdata, self._dimensions, self._dimensions+1) 240 | return (self._dimensions[0], self._dimensions[1]) 241 | @size.setter 242 | def size(self, wh): 243 | _handle_error(_libwap32.wap_plane_set_map_dimensions(self._cdata, wh[0], wh[1])) 244 | objects = _make_array_property(lambda super, ptr, idx: Object(super, _libwap32.wap_plane_get_object(ptr, idx)), 245 | None, 246 | _libwap32.wap_plane_get_object_count, 247 | _libwap32.wap_plane_set_object_count) 248 | image_sets = _make_array_property(_get_image_set, 249 | _set_image_set, 250 | _libwap32.wap_plane_get_image_set_count, 251 | _libwap32.wap_plane_set_image_set_count) 252 | 253 | _add_properties(Plane, _ffi.typeof("wap_plane_properties")) 254 | _add_flags(Wwd, 'WAP_PLANE_FLAG_', _ffi.typeof("wap_plane_flags_t")) 255 | 256 | class Object: 257 | def __init__(self, wwd, obj_cdata): 258 | self._super = wwd 259 | self._cdata = obj_cdata 260 | self._properties = _libwap32.wap_object_get_properties(self._cdata) 261 | name = _make_string_property(_libwap32.wap_object_get_name, _libwap32.wap_object_set_name) 262 | logic = _make_string_property(_libwap32.wap_object_get_logic, _libwap32.wap_object_set_logic) 263 | image_set = _make_string_property(_libwap32.wap_object_get_image_set, _libwap32.wap_object_set_image_set) 264 | animation = _make_string_property(_libwap32.wap_object_get_animation, _libwap32.wap_object_set_animation) 265 | 266 | _add_properties(Object, _ffi.typeof("wap_object_properties")) 267 | _add_flags(Object, 'WAP_OBJECT_', _ffi.typeof("wap_object_add_flags_t")) 268 | _add_flags(Object, 'WAP_OBJECT_', _ffi.typeof("wap_object_draw_flags_t")) 269 | _add_flags(Object, 'WAP_OBJECT_', _ffi.typeof("wap_object_dynamic_flags_t")) 270 | _add_flags(Object, 'WAP_OBJECT_', _ffi.typeof("wap_object_user_flags_t")) 271 | _add_flags(Object, 'WAP_OBJECT_', _ffi.typeof("wap_object_type_flags_t")) 272 | 273 | class TileDescription: 274 | def __init__(self, desc_cdata): 275 | self._super = None 276 | self._cdata = desc_cdata 277 | self._properties = desc_cdata 278 | def _access_double_tile_attrs(self): 279 | if self.type != _libwap32.WAP_TILE_TYPE_DOUBLE: 280 | raise AttributeError("rect and outside_attrib are accesible iff type == WAP_TILE_TYPE_DOUBLE") 281 | _double_tile_attrs = ('outside_attrib', 'rect') 282 | 283 | _add_flags(TileDescription, 'WAP_TILE_', _ffi.typeof("wap_tile_type_flags_t")) 284 | _add_flags(TileDescription, 'WAP_TILE_', _ffi.typeof("wap_tile_attribute_flags_t")) 285 | 286 | for name, field in _ffi.typeof("wap_tile_description").fields: 287 | _name = name 288 | if name in TileDescription._double_tile_attrs: 289 | _name = '_' + name 290 | setattr(TileDescription, _name, _make_ctype_property(name, field.type)) 291 | 292 | for name in TileDescription._double_tile_attrs: 293 | def get(self): 294 | self._access_double_tile_attrs() 295 | return getattr(self, '_' + name) 296 | def set(self, value): 297 | self._access_double_tile_attrs() 298 | setattr(self, '_' + name, value) 299 | setattr(TileDescription, name, property(get, set)) 300 | --------------------------------------------------------------------------------