diff --git a/library/CMakeLists.txt b/library/CMakeLists.txt index 2dd4b40..0d8deee 100644 --- a/library/CMakeLists.txt +++ b/library/CMakeLists.txt @@ -141,9 +141,9 @@ endif() add_subdirectory(formats) add_subdirectory(libgldemo) add_subdirectory(libvitaboy) +add_subdirectory(tools) if(BUILD_EXAMPLES) - add_subdirectory(renderdemo_ray) add_subdirectory(rlgldemo) endif() diff --git a/library/libvitaboy/CMakeLists.txt b/library/libvitaboy/CMakeLists.txt index 3ff4278..87c9b9c 100644 --- a/library/libvitaboy/CMakeLists.txt +++ b/library/libvitaboy/CMakeLists.txt @@ -46,8 +46,8 @@ else() #### Static library (uncomment to build) add_library(libvitaboy_static STATIC ${LIBVITABOY_SOURCES}) set_target_properties(libvitaboy_static PROPERTIES - OUTPUT_NAME "vitaboy" - CLEAN_DIRECT_OUTPUT 1) + OUTPUT_NAME "vitaboy" + CLEAN_DIRECT_OUTPUT 1) set(VITABOY_LIB libvitaboy_static) endif() # BUILD_SHARED_LIBS diff --git a/library/tools/CMakeLists.txt b/library/tools/CMakeLists.txt new file mode 100644 index 0000000..5709862 --- /dev/null +++ b/library/tools/CMakeLists.txt @@ -0,0 +1,5 @@ +add_subdirectory(FARDive) +add_subdirectory(hitutils) +add_subdirectory(iff2html) +add_subdirectory(rtti-reader) +add_subdirectory(tsoscan) \ No newline at end of file diff --git a/library/tools/FARDive/CMakeLists.txt b/library/tools/FARDive/CMakeLists.txt new file mode 100644 index 0000000..b31d59e --- /dev/null +++ b/library/tools/FARDive/CMakeLists.txt @@ -0,0 +1,18 @@ +cmake_minimum_required(VERSION 2.6) +project(FARDive) + +if(WIN32) + set(FARDIVE_SOURCES + windows/common.cpp + windows/Interaction.cpp + windows/MainWindow.cpp + windows/ReadPNGIcon.cpp + windows/Resource.rc + windows/Startup.cpp + windows/Dialog/AddToArchive.cpp + windows/Dialog/NewArchive.cpp + ) + include_directories(${LIBPNG_INCLUDE}) + add_executable(FARDive WIN32 ${FARDIVE_SOURCES}) + target_link_libraries(FARDive ole32 uxtheme FileHandler_shared) +endif() \ No newline at end of file diff --git a/library/tools/FARDive/FARDive.hpp b/library/tools/FARDive/FARDive.hpp new file mode 100644 index 0000000..bbe9e1e --- /dev/null +++ b/library/tools/FARDive/FARDive.hpp @@ -0,0 +1,16 @@ +#include "version.hpp" + +namespace Archive { + extern wchar_t Path[1024], Filename[1024]; + + extern bool IsOpen; + extern bool IsModified; + + bool Add(const wchar_t * Path); + bool Close(); + bool Open(); + bool PopulateEntries(); + bool Save(); + bool SaveAs(); + bool SetWorkspace(); +} \ No newline at end of file diff --git a/library/tools/FARDive/resources/icons/accessories-text-editor.png b/library/tools/FARDive/resources/icons/accessories-text-editor.png new file mode 100644 index 0000000..0d84b76 Binary files /dev/null and b/library/tools/FARDive/resources/icons/accessories-text-editor.png differ diff --git a/library/tools/FARDive/resources/icons/applications-other.png b/library/tools/FARDive/resources/icons/applications-other.png new file mode 100644 index 0000000..708636a Binary files /dev/null and b/library/tools/FARDive/resources/icons/applications-other.png differ diff --git a/library/tools/FARDive/resources/icons/audio-x-generic.png b/library/tools/FARDive/resources/icons/audio-x-generic.png new file mode 100644 index 0000000..c3814f8 Binary files /dev/null and b/library/tools/FARDive/resources/icons/audio-x-generic.png differ diff --git a/library/tools/FARDive/resources/icons/document-new.png b/library/tools/FARDive/resources/icons/document-new.png new file mode 100644 index 0000000..04a290f Binary files /dev/null and b/library/tools/FARDive/resources/icons/document-new.png differ diff --git a/library/tools/FARDive/resources/icons/document-open.png b/library/tools/FARDive/resources/icons/document-open.png new file mode 100644 index 0000000..6bbea74 Binary files /dev/null and b/library/tools/FARDive/resources/icons/document-open.png differ diff --git a/library/tools/FARDive/resources/icons/document-properties.png b/library/tools/FARDive/resources/icons/document-properties.png new file mode 100644 index 0000000..a3a1b62 Binary files /dev/null and b/library/tools/FARDive/resources/icons/document-properties.png differ diff --git a/library/tools/FARDive/resources/icons/document-save-as.png b/library/tools/FARDive/resources/icons/document-save-as.png new file mode 100644 index 0000000..2e013d0 Binary files /dev/null and b/library/tools/FARDive/resources/icons/document-save-as.png differ diff --git a/library/tools/FARDive/resources/icons/document-save.png b/library/tools/FARDive/resources/icons/document-save.png new file mode 100644 index 0000000..e340775 Binary files /dev/null and b/library/tools/FARDive/resources/icons/document-save.png differ diff --git a/library/tools/FARDive/resources/icons/edit-clear.png b/library/tools/FARDive/resources/icons/edit-clear.png new file mode 100644 index 0000000..b2874fb Binary files /dev/null and b/library/tools/FARDive/resources/icons/edit-clear.png differ diff --git a/library/tools/FARDive/resources/icons/edit-copy.png b/library/tools/FARDive/resources/icons/edit-copy.png new file mode 100644 index 0000000..f4721e1 Binary files /dev/null and b/library/tools/FARDive/resources/icons/edit-copy.png differ diff --git a/library/tools/FARDive/resources/icons/edit-find.png b/library/tools/FARDive/resources/icons/edit-find.png new file mode 100644 index 0000000..10ce515 Binary files /dev/null and b/library/tools/FARDive/resources/icons/edit-find.png differ diff --git a/library/tools/FARDive/resources/icons/edit-select-all.png b/library/tools/FARDive/resources/icons/edit-select-all.png new file mode 100644 index 0000000..0867e94 Binary files /dev/null and b/library/tools/FARDive/resources/icons/edit-select-all.png differ diff --git a/library/tools/FARDive/resources/icons/emblem-unreadable.png b/library/tools/FARDive/resources/icons/emblem-unreadable.png new file mode 100644 index 0000000..41a3313 Binary files /dev/null and b/library/tools/FARDive/resources/icons/emblem-unreadable.png differ diff --git a/library/tools/FARDive/resources/icons/go-first.png b/library/tools/FARDive/resources/icons/go-first.png new file mode 100644 index 0000000..3d990f2 Binary files /dev/null and b/library/tools/FARDive/resources/icons/go-first.png differ diff --git a/library/tools/FARDive/resources/icons/go-jump.png b/library/tools/FARDive/resources/icons/go-jump.png new file mode 100644 index 0000000..82d7896 Binary files /dev/null and b/library/tools/FARDive/resources/icons/go-jump.png differ diff --git a/library/tools/FARDive/resources/icons/go-last.png b/library/tools/FARDive/resources/icons/go-last.png new file mode 100644 index 0000000..d6ffccc Binary files /dev/null and b/library/tools/FARDive/resources/icons/go-last.png differ diff --git a/library/tools/FARDive/resources/icons/help-about.png b/library/tools/FARDive/resources/icons/help-about.png new file mode 100644 index 0000000..89fc9cb Binary files /dev/null and b/library/tools/FARDive/resources/icons/help-about.png differ diff --git a/library/tools/FARDive/resources/icons/info.bmp b/library/tools/FARDive/resources/icons/info.bmp new file mode 100644 index 0000000..74be0eb Binary files /dev/null and b/library/tools/FARDive/resources/icons/info.bmp differ diff --git a/library/tools/FARDive/resources/icons/internet-web-browser.png b/library/tools/FARDive/resources/icons/internet-web-browser.png new file mode 100644 index 0000000..3254b04 Binary files /dev/null and b/library/tools/FARDive/resources/icons/internet-web-browser.png differ diff --git a/library/tools/FARDive/resources/icons/list-add.png b/library/tools/FARDive/resources/icons/list-add.png new file mode 100644 index 0000000..7c479da Binary files /dev/null and b/library/tools/FARDive/resources/icons/list-add.png differ diff --git a/library/tools/FARDive/resources/icons/list-remove.png b/library/tools/FARDive/resources/icons/list-remove.png new file mode 100644 index 0000000..f737065 Binary files /dev/null and b/library/tools/FARDive/resources/icons/list-remove.png differ diff --git a/library/tools/FARDive/resources/icons/mail-send-receive.png b/library/tools/FARDive/resources/icons/mail-send-receive.png new file mode 100644 index 0000000..dea3ad3 Binary files /dev/null and b/library/tools/FARDive/resources/icons/mail-send-receive.png differ diff --git a/library/tools/FARDive/resources/icons/package-x-generic-selected.png b/library/tools/FARDive/resources/icons/package-x-generic-selected.png new file mode 100644 index 0000000..421843d Binary files /dev/null and b/library/tools/FARDive/resources/icons/package-x-generic-selected.png differ diff --git a/library/tools/FARDive/resources/icons/package-x-generic.png b/library/tools/FARDive/resources/icons/package-x-generic.png new file mode 100644 index 0000000..8a5c86a Binary files /dev/null and b/library/tools/FARDive/resources/icons/package-x-generic.png differ diff --git a/library/tools/FARDive/resources/icons/preferences-desktop.png b/library/tools/FARDive/resources/icons/preferences-desktop.png new file mode 100644 index 0000000..6021664 Binary files /dev/null and b/library/tools/FARDive/resources/icons/preferences-desktop.png differ diff --git a/library/tools/FARDive/resources/icons/preferences-system-windows.png b/library/tools/FARDive/resources/icons/preferences-system-windows.png new file mode 100644 index 0000000..973af71 Binary files /dev/null and b/library/tools/FARDive/resources/icons/preferences-system-windows.png differ diff --git a/library/tools/FARDive/resources/icons/system-file-manager.png b/library/tools/FARDive/resources/icons/system-file-manager.png new file mode 100644 index 0000000..09b03d6 Binary files /dev/null and b/library/tools/FARDive/resources/icons/system-file-manager.png differ diff --git a/library/tools/FARDive/resources/icons/system-log-out.png b/library/tools/FARDive/resources/icons/system-log-out.png new file mode 100644 index 0000000..39d9e13 Binary files /dev/null and b/library/tools/FARDive/resources/icons/system-log-out.png differ diff --git a/library/tools/FARDive/resources/icons/utilities-terminal.png b/library/tools/FARDive/resources/icons/utilities-terminal.png new file mode 100644 index 0000000..000c79b Binary files /dev/null and b/library/tools/FARDive/resources/icons/utilities-terminal.png differ diff --git a/library/tools/FARDive/resources/optimize.bat b/library/tools/FARDive/resources/optimize.bat new file mode 100644 index 0000000..e8daa13 --- /dev/null +++ b/library/tools/FARDive/resources/optimize.bat @@ -0,0 +1,108 @@ +optipng -zc1-9 -zm1-9 -zs0-3 -f0-5 -i0 -zw256 accessories-text-editor.png +optipng -zc1-9 -zm1-9 -zs0-3 -f0-5 -i0 -zw512 accessories-text-editor.png +optipng -zc1-9 -zm1-9 -zs0-3 -f0-5 -i0 -zw1k accessories-text-editor.png +optipng -zc1-9 -zm1-9 -zs0-3 -f0-5 -i0 -zw256 applications-other.png +optipng -zc1-9 -zm1-9 -zs0-3 -f0-5 -i0 -zw512 applications-other.png +optipng -zc1-9 -zm1-9 -zs0-3 -f0-5 -i0 -zw1k applications-other.png +optipng -zc1-9 -zm1-9 -zs0-3 -f0-5 -i0 -zw256 audio-x-generic.png +optipng -zc1-9 -zm1-9 -zs0-3 -f0-5 -i0 -zw512 audio-x-generic.png +optipng -zc1-9 -zm1-9 -zs0-3 -f0-5 -i0 -zw1k audio-x-generic.png +optipng -zc1-9 -zm1-9 -zs0-3 -f0-5 -i0 -zw256 document-new.png +optipng -zc1-9 -zm1-9 -zs0-3 -f0-5 -i0 -zw512 document-new.png +optipng -zc1-9 -zm1-9 -zs0-3 -f0-5 -i0 -zw1k document-new.png +optipng -zc1-9 -zm1-9 -zs0-3 -f0-5 -i0 -zw256 document-open.png +optipng -zc1-9 -zm1-9 -zs0-3 -f0-5 -i0 -zw512 document-open.png +optipng -zc1-9 -zm1-9 -zs0-3 -f0-5 -i0 -zw1k document-open.png +optipng -zc1-9 -zm1-9 -zs0-3 -f0-5 -i0 -zw256 document-properties.png +optipng -zc1-9 -zm1-9 -zs0-3 -f0-5 -i0 -zw512 document-properties.png +optipng -zc1-9 -zm1-9 -zs0-3 -f0-5 -i0 -zw1k document-properties.png +optipng -zc1-9 -zm1-9 -zs0-3 -f0-5 -i0 -zw256 document-save-as.png +optipng -zc1-9 -zm1-9 -zs0-3 -f0-5 -i0 -zw512 document-save-as.png +optipng -zc1-9 -zm1-9 -zs0-3 -f0-5 -i0 -zw1k document-save-as.png +optipng -zc1-9 -zm1-9 -zs0-3 -f0-5 -i0 -zw256 document-save.png +optipng -zc1-9 -zm1-9 -zs0-3 -f0-5 -i0 -zw512 document-save.png +optipng -zc1-9 -zm1-9 -zs0-3 -f0-5 -i0 -zw1k document-save.png +optipng -zc1-9 -zm1-9 -zs0-3 -f0-5 -i0 -zw256 edit-clear.png +optipng -zc1-9 -zm1-9 -zs0-3 -f0-5 -i0 -zw512 edit-clear.png +optipng -zc1-9 -zm1-9 -zs0-3 -f0-5 -i0 -zw1k edit-clear.png +optipng -zc1-9 -zm1-9 -zs0-3 -f0-5 -i0 -zw256 edit-copy.png +optipng -zc1-9 -zm1-9 -zs0-3 -f0-5 -i0 -zw512 edit-copy.png +optipng -zc1-9 -zm1-9 -zs0-3 -f0-5 -i0 -zw1k edit-copy.png +optipng -zc1-9 -zm1-9 -zs0-3 -f0-5 -i0 -zw256 edit-find.png +optipng -zc1-9 -zm1-9 -zs0-3 -f0-5 -i0 -zw512 edit-find.png +optipng -zc1-9 -zm1-9 -zs0-3 -f0-5 -i0 -zw1k edit-find.png +optipng -zc1-9 -zm1-9 -zs0-3 -f0-5 -i0 -zw256 edit-select-all.png +optipng -zc1-9 -zm1-9 -zs0-3 -f0-5 -i0 -zw512 edit-select-all.png +optipng -zc1-9 -zm1-9 -zs0-3 -f0-5 -i0 -zw1k edit-select-all.png +optipng -zc1-9 -zm1-9 -zs0-3 -f0-5 -i0 -zw256 emblem-unreadable.png +optipng -zc1-9 -zm1-9 -zs0-3 -f0-5 -i0 -zw512 emblem-unreadable.png +optipng -zc1-9 -zm1-9 -zs0-3 -f0-5 -i0 -zw1k emblem-unreadable.png +optipng -zc1-9 -zm1-9 -zs0-3 -f0-5 -i0 -zw256 go-first.png +optipng -zc1-9 -zm1-9 -zs0-3 -f0-5 -i0 -zw512 go-first.png +optipng -zc1-9 -zm1-9 -zs0-3 -f0-5 -i0 -zw1k go-first.png +optipng -zc1-9 -zm1-9 -zs0-3 -f0-5 -i0 -zw256 go-jump.png +optipng -zc1-9 -zm1-9 -zs0-3 -f0-5 -i0 -zw512 go-jump.png +optipng -zc1-9 -zm1-9 -zs0-3 -f0-5 -i0 -zw1k go-jump.png +optipng -zc1-9 -zm1-9 -zs0-3 -f0-5 -i0 -zw256 go-last.png +optipng -zc1-9 -zm1-9 -zs0-3 -f0-5 -i0 -zw512 go-last.png +optipng -zc1-9 -zm1-9 -zs0-3 -f0-5 -i0 -zw1k go-last.png +optipng -zc1-9 -zm1-9 -zs0-3 -f0-5 -i0 -zw256 internet-web-browser.png +optipng -zc1-9 -zm1-9 -zs0-3 -f0-5 -i0 -zw512 internet-web-browser.png +optipng -zc1-9 -zm1-9 -zs0-3 -f0-5 -i0 -zw1k internet-web-browser.png +optipng -zc1-9 -zm1-9 -zs0-3 -f0-5 -i0 -zw256 list-add.png +optipng -zc1-9 -zm1-9 -zs0-3 -f0-5 -i0 -zw512 list-add.png +optipng -zc1-9 -zm1-9 -zs0-3 -f0-5 -i0 -zw1k list-add.png +optipng -zc1-9 -zm1-9 -zs0-3 -f0-5 -i0 -zw256 list-remove.png +optipng -zc1-9 -zm1-9 -zs0-3 -f0-5 -i0 -zw512 list-remove.png +optipng -zc1-9 -zm1-9 -zs0-3 -f0-5 -i0 -zw1k list-remove.png +optipng -zc1-9 -zm1-9 -zs0-3 -f0-5 -i0 -zw256 mail-send-receive.png +optipng -zc1-9 -zm1-9 -zs0-3 -f0-5 -i0 -zw512 mail-send-receive.png +optipng -zc1-9 -zm1-9 -zs0-3 -f0-5 -i0 -zw1k mail-send-receive.png +optipng -zc1-9 -zm1-9 -zs0-3 -f0-5 -i0 -zw256 package-x-generic.png +optipng -zc1-9 -zm1-9 -zs0-3 -f0-5 -i0 -zw512 package-x-generic.png +optipng -zc1-9 -zm1-9 -zs0-3 -f0-5 -i0 -zw1k package-x-generic.png +optipng -zc1-9 -zm1-9 -zs0-3 -f0-5 -i0 -zw256 package-x-generic-selected.png +optipng -zc1-9 -zm1-9 -zs0-3 -f0-5 -i0 -zw512 package-x-generic-selected.png +optipng -zc1-9 -zm1-9 -zs0-3 -f0-5 -i0 -zw1k package-x-generic-selected.png +optipng -zc1-9 -zm1-9 -zs0-3 -f0-5 -i0 -zw256 preferences-desktop.png +optipng -zc1-9 -zm1-9 -zs0-3 -f0-5 -i0 -zw512 preferences-desktop.png +optipng -zc1-9 -zm1-9 -zs0-3 -f0-5 -i0 -zw1k preferences-desktop.png +optipng -zc1-9 -zm1-9 -zs0-3 -f0-5 -i0 -zw256 preferences-system-windows.png +optipng -zc1-9 -zm1-9 -zs0-3 -f0-5 -i0 -zw512 preferences-system-windows.png +optipng -zc1-9 -zm1-9 -zs0-3 -f0-5 -i0 -zw1k preferences-system-windows.png +optipng -zc1-9 -zm1-9 -zs0-3 -f0-5 -i0 -zw256 system-file-manager.png +optipng -zc1-9 -zm1-9 -zs0-3 -f0-5 -i0 -zw512 system-file-manager.png +optipng -zc1-9 -zm1-9 -zs0-3 -f0-5 -i0 -zw1k system-file-manager.png +optipng -zc1-9 -zm1-9 -zs0-3 -f0-5 -i0 -zw256 system-log-out.png +optipng -zc1-9 -zm1-9 -zs0-3 -f0-5 -i0 -zw512 system-log-out.png +optipng -zc1-9 -zm1-9 -zs0-3 -f0-5 -i0 -zw1k system-log-out.png +optipng -zc1-9 -zm1-9 -zs0-3 -f0-5 -i0 -zw256 utilities-terminal.png +optipng -zc1-9 -zm1-9 -zs0-3 -f0-5 -i0 -zw512 utilities-terminal.png +optipng -zc1-9 -zm1-9 -zs0-3 -f0-5 -i0 -zwutilities-terminal.png +advpng -z -4 accessories-text-editor.png +advpng -z -4 applications-other.png +advpng -z -4 audio-x-generic.png +advpng -z -4 document-new.png +advpng -z -4 document-open.png +advpng -z -4 document-properties.png +advpng -z -4 document-save-as.png +advpng -z -4 document-save.png +advpng -z -4 emblem-unreadable.png +advpng -z -4 edit-clear.png +advpng -z -4 edit-copy.png +advpng -z -4 edit-find.png +advpng -z -4 edit-select-all.png +advpng -z -4 go-first.png +advpng -z -4 go-jump.png +advpng -z -4 go-last.png +advpng -z -4 internet-web-browser.png +advpng -z -4 list-add.png +advpng -z -4 list-remove.png +advpng -z -4 mail-send-receive.png +advpng -z -4 package-x-generic-selected.png +advpng -z -4 package-x-generic.png +advpng -z -4 preferences-desktop.png +advpng -z -4 preferences-system-windows.png +advpng -z -4 system-file-manager.png +advpng -z -4 system-log-out.png +advpng -z -4 utilities-terminal.png \ No newline at end of file diff --git a/library/tools/FARDive/resources/windows/FARDive.exe.manifest b/library/tools/FARDive/resources/windows/FARDive.exe.manifest new file mode 100644 index 0000000..c68bfef --- /dev/null +++ b/library/tools/FARDive/resources/windows/FARDive.exe.manifest @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/library/tools/FARDive/resources/windows/FARDive.ico b/library/tools/FARDive/resources/windows/FARDive.ico new file mode 100644 index 0000000..d551aa3 Binary files /dev/null and b/library/tools/FARDive/resources/windows/FARDive.ico differ diff --git a/library/tools/FARDive/version.hpp b/library/tools/FARDive/version.hpp new file mode 100644 index 0000000..40529e2 --- /dev/null +++ b/library/tools/FARDive/version.hpp @@ -0,0 +1,17 @@ +#define FD_VERSION_A 0 +#define FD_VERSION_B 0 +#define FD_VERSION_C 1 +#define FD_REVISION 1 + +//You don't have to touch the following +#define xstr(x) str(x) +#define str(x) #x //Yes, double levels is required. See +#define FDVERSION L"" \ + xstr(FD_VERSION_A) \ + L"." \ + xstr(FD_VERSION_B) \ + L"." \ + xstr(FD_VERSION_C) \ + L" (rev. " \ + xstr(FD_REVISION) \ + L")" diff --git a/library/tools/FARDive/windows/Dialog/AddToArchive.cpp b/library/tools/FARDive/windows/Dialog/AddToArchive.cpp new file mode 100644 index 0000000..77bd767 --- /dev/null +++ b/library/tools/FARDive/windows/Dialog/AddToArchive.cpp @@ -0,0 +1,53 @@ +#include "../Windows.hpp" +#include "../GUI.hpp" + +namespace AddToArchive { + +INT_PTR CALLBACK DlgProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam){ + switch (message){ + case WM_INITDIALOG: { + CenterDialog(hDlg); + + HWND hBox = GetDlgItem(hDlg, IDC_A2A_FILE); + SendMessage(hBox, EM_SETLIMITTEXT, 1023, 0); + hBox = GetDlgItem(hDlg, IDC_A2A_FILENAME); + SendMessage(hBox, EM_SETLIMITTEXT, 1023, 0); + + if(lParam){ + HWND hBox = GetDlgItem(hDlg, IDC_A2A_FILE); + SetWindowText(hBox, (wchar_t*) lParam); + hBox = GetDlgItem(hDlg, IDC_A2A_FILENAME); + wchar_t Filename[1024]; + GetFileTitle((wchar_t*) lParam, Filename, 1024); + SetWindowText(hBox, Filename); + } + + } return TRUE; + case WM_COMMAND: + switch(LOWORD(wParam)){ + case IDC_A2A_BROWSE: { + wchar_t Path[1024] = L"", Filename[1024] = L""; + ofn.hwndOwner = hDlg; + ofn.lpstrFilter = FILTER_FILES; + ofn.lpstrCustomFilter = ArchiveAddFilter; + ofn.lpstrFile = Path; + ofn.lpstrFileTitle = Filename; + ofn.Flags = OFN_DONTADDTORECENT | OFN_HIDEREADONLY; + if(GetOpenFileName(&ofn)){ + SetWindowText(GetDlgItem(hDlg, IDC_A2A_FILE), Path); + SetWindowText(GetDlgItem(hDlg, IDC_A2A_FILENAME), Filename); + } + } break; + case IDOK: + EndDialog(hDlg, true); + break; + case IDCANCEL: + EndDialog(hDlg, false); + } break; + case WM_CLOSE: + EndDialog(hDlg, false); + } + return 0; +} + +} \ No newline at end of file diff --git a/library/tools/FARDive/windows/Dialog/NewArchive.cpp b/library/tools/FARDive/windows/Dialog/NewArchive.cpp new file mode 100644 index 0000000..65811e1 --- /dev/null +++ b/library/tools/FARDive/windows/Dialog/NewArchive.cpp @@ -0,0 +1,118 @@ +#include "../Windows.hpp" +#include "../GUI.hpp" + +#ifndef TTI_INFO_LARGE +#define TTI_INFO_LARGE 4 +#endif + +namespace NewArchive { + +enum {TYPE_FAR, TYPE_DBPF}; +void SetType(HWND hDlg, int type){ + for(int i=IDC_NA_FARVERSIONTEXT; i<=IDC_NA_FARVERSION; i++) + EnableWindow(GetDlgItem(hDlg, i), type==TYPE_FAR); + for(int i=IDC_NA_DBPFVERSIONTEXT; i<=IDC_NA_DBPFCOMPRESS; i++) + EnableWindow(GetDlgItem(hDlg, i), type==TYPE_DBPF); +} + +INT_PTR CALLBACK DlgProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam){ + switch (message){ + case WM_INITDIALOG: { + CenterDialog(hDlg); + + //Fill the drop-down boxes + HWND hBox = GetDlgItem(hDlg, IDC_NA_TYPE); + SendMessage(hBox, CB_INSERTSTRING, -1, (LPARAM) L"FAR"); + SendMessage(hBox, CB_INSERTSTRING, -1, (LPARAM) L"DBPF"); + SendMessage(hBox, CB_SETCURSEL, 0, 0); + hBox = GetDlgItem(hDlg, IDC_NA_FARVERSION); + SendMessage(hBox, CB_INSERTSTRING, -1, (LPARAM) L"1a"); + SendMessage(hBox, CB_INSERTSTRING, -1, (LPARAM) L"1b"); + SendMessage(hBox, CB_INSERTSTRING, -1, (LPARAM) L"3"); + SendMessage(hBox, CB_SETCURSEL, 0, 0); + hBox = GetDlgItem(hDlg, IDC_NA_DBPFVERSION); + SendMessage(hBox, CB_INSERTSTRING, -1, (LPARAM) L"1.0"); + SendMessage(hBox, CB_INSERTSTRING, -1, (LPARAM) L"1.1"); + SendMessage(hBox, CB_INSERTSTRING, -1, (LPARAM) L"2.0"); + SendMessage(hBox, CB_SETCURSEL, 0, 0); + hBox = GetDlgItem(hDlg, IDC_NA_INDEXVERSION); + SendMessage(hBox, CB_INSERTSTRING, -1, (LPARAM) L"7.0"); + SendMessage(hBox, CB_INSERTSTRING, -1, (LPARAM) L"7.1"); + SendMessage(hBox, CB_INSERTSTRING, -1, (LPARAM) L"3.0"); + SendMessage(hBox, CB_SETCURSEL, 0, 0); + + //Create the tooltips + HWND FARInfo = CreateWindowEx(0, TOOLTIPS_CLASS, NULL, + WS_POPUP | TTS_NOPREFIX | TTS_BALLOON | TTS_ALWAYSTIP, + CW_USEDEFAULT, CW_USEDEFAULT, + CW_USEDEFAULT, CW_USEDEFAULT, + hDlg, NULL, hInst, NULL), + DBPFInfo = CreateWindowEx(0, TOOLTIPS_CLASS, NULL, + WS_POPUP | TTS_NOPREFIX | TTS_BALLOON | TTS_ALWAYSTIP, + CW_USEDEFAULT, CW_USEDEFAULT, + CW_USEDEFAULT, CW_USEDEFAULT, + hDlg, NULL, hInst, NULL); + + TOOLINFO tinfo = { + sizeof(TOOLINFO), //cbSize + TTF_IDISHWND | TTF_SUBCLASS, //uFlags + hDlg, //hwnd + 0, //uId + NULL, //rect + 0 //hinst + }; + + tinfo.uId = (UINT_PTR) GetDlgItem(hDlg, IDC_NA_FARINFO); + tinfo.lpszText = (wchar_t*) + L"FAR version 1a is found in The Sims 1.\r\n\r\n" + L"Version 1b appears to be a mistake, in which it was intended to take on the version number 2.\r\n\r\n" + L"1b and 3 are both found exclusively in The Sims Online."; + SendMessage(FARInfo, TTM_SETMAXTIPWIDTH, 2000, 200); + SendMessage(FARInfo, TTM_SETTITLE, TTI_INFO_LARGE, (LPARAM) L"FAR version"); + SendMessage(FARInfo, TTM_ADDTOOL, 0, (LPARAM) &tinfo); + SendMessage(FARInfo, TTM_SETDELAYTIME, TTDT_AUTOPOP, 12000); + tinfo.uId = (UINT_PTR) GetDlgItem(hDlg, IDC_NA_DBPFINFO); + tinfo.lpszText = (wchar_t*) + L"DBPF version numbers can be shortened to the form a.b;ix,y, where " + L"a.b is the Archive version and x.y is the Index version.\r\n\r\n" + L"DBPF 1.0;i7.0 is found in The Sims Online and SimCity 4.\r\n\r\n" + L"1.0;i7.0 and 1.1;i7.1 are found in The Sims 2.\r\n\r\n" + L"2.0;i3.0 is found in Spore."; + SendMessage(DBPFInfo, TTM_SETMAXTIPWIDTH, 2000, 200); + SendMessage(DBPFInfo, TTM_SETTITLE, TTI_INFO_LARGE, (LPARAM) L"DBPF version"); + SendMessage(DBPFInfo, TTM_ADDTOOL, 0, (LPARAM) &tinfo); + SendMessage(DBPFInfo, TTM_SETDELAYTIME, TTDT_AUTOPOP, 20000); + + SetType(hDlg, TYPE_FAR); + } return TRUE; + case WM_CTLCOLORSTATIC: + if((HWND) lParam == GetDlgItem(hDlg, IDC_NA_TYPETEXT)){ + SetBkColor((HDC) wParam, GetSysColor(COLOR_WINDOW)); + return (INT_PTR) GetSysColorBrush(COLOR_WINDOW); + } + break; + case WM_COMMAND: + switch(LOWORD(wParam)){ + case IDC_NA_TYPE: + if(HIWORD(wParam) == CBN_SELCHANGE) + SetType(hDlg, SendMessage((HWND)lParam, CB_GETCURSEL, 0, 0)); + break; + case IDOK: + Archive::IsOpen = true; + Archive::IsModified = true; + + Archive::Path[0] = '\0'; //No actual path + wcscpy(Archive::Filename, L"Untitled"); + Archive::SetWorkspace(); + + EndDialog(hDlg, 0); + case IDCANCEL: + EndDialog(hDlg, 0); + } break; + case WM_CLOSE: + EndDialog(hDlg, 0); + } + return 0; +} + +} \ No newline at end of file diff --git a/library/tools/FARDive/windows/GUI.hpp b/library/tools/FARDive/windows/GUI.hpp new file mode 100644 index 0000000..8da4042 --- /dev/null +++ b/library/tools/FARDive/windows/GUI.hpp @@ -0,0 +1,59 @@ +extern HINSTANCE hInst; + +extern HMENU hMenu, listmenu; +extern HDC hDC; +extern OPENFILENAME ofn; +extern unsigned int statusbarheight; + +extern HBITMAP PNGIcon[MENUICONS]; +enum { + BMP_FILE_NEW, + BMP_FILE_OPEN, + BMP_FILE_SAVE, + BMP_FILE_SAVEAS, + BMP_FILE_ADD, + BMP_FILE_EXPORTALL, + BMP_FILE_EXPORTSELECTED, + BMP_FILE_CHANGETYPE, + BMP_FILE_CLOSE, + BMP_FILE_EXIT, + BMP_EDIT_DUPLICATE, + BMP_EDIT_REMOVE, + BMP_EDIT_RENAME, + BMP_EDIT_PROPERTIES, + BMP_EDIT_CONTENTS, + BMP_EDIT_FIND, + BMP_EDIT_FINDNEXT, + BMP_EDIT_FINDPREVIOUS, + BMP_EDIT_MATCHES, + BMP_EDIT_GOTO, + BMP_EDIT_PREFERENCES, + BMP_TOOLS_RECOMPRESS, + BMP_TOOLS_RESORT, + BMP_TOOLS_REMOVEHOLES, + BMP_TOOLS_BATCH, + BMP_HELP_HOWTOUSE +}; + +static const wchar_t FILTER_ARCHIVES_FILES[] = + L"All supported archives\0*.far;*.dbpf;*.dat;*.package\0" + L"FAR (*.far, *.dat, *.package)\0*.far;*.dat;*.package\0" + L"DBPF (*.dbpf, *.dat, *.package)\0*.dbpf;*.dat;*.package\0" + L"All Files\0*.*\0\0"; + +static const wchar_t FILTER_ARCHIVES_ONLY[] = + L"All supported archives\0*.far;*.dbpf;*.dat;*.package\0" + L"FAR (*.far, *.dat, *.package)\0*.far;*.dat;*.package\0" + L"DBPF (*.dbpf, *.dat, *.package)\0*.dbpf;*.dat;*.package\0" + L"All Files\0*.*\0\0"; + +static const wchar_t FILTER_FILES[] = + L"All Files\0*.*\0\0"; + +extern wchar_t ArchiveOpenFilter[128], ArchiveAddFilter[128], ArchiveSaveFilter[128]; + +//Controls +extern HWND hWnd, statusbar, hList; + +void CenterDialog(HWND hDlg); +HBITMAP ReadPNGIcon(int ID); \ No newline at end of file diff --git a/library/tools/FARDive/windows/Interaction.cpp b/library/tools/FARDive/windows/Interaction.cpp new file mode 100644 index 0000000..e0f8add --- /dev/null +++ b/library/tools/FARDive/windows/Interaction.cpp @@ -0,0 +1,174 @@ +#include "Windows.hpp" +#include +#include "GUI.hpp" + +namespace Archive { + +wchar_t Path[1024], Filename[1024]; + +bool IsOpen = false; +bool IsModified; + +bool Add(const wchar_t * Path){ + return true; +} + +bool Close(){ + if(!Archive::IsOpen) return true; + + if(Archive::IsModified){ + //Ask for consent + int result = MessageBox(hWnd, L"Save changes?", L"Save changes?", MB_YESNOCANCEL); + if(result == IDYES){ + if(!Archive::Save()) + return false; + }else if(result != IDNO) + return false; + } + + //Close the workspace + + Archive::IsOpen = false; + + SetWindowText(hWnd, L"FARDive"); + SendMessage(statusbar, SB_SETTEXT, 0, (LPARAM) L""); + + MENUITEMINFO mii; + mii.cbSize = sizeof(MENUITEMINFO); + mii.fMask = MIIM_STATE; + mii.fState = MFS_DISABLED; + + int DisableItems[] = { + ID_FILE_SAVE, + ID_FILE_SAVEAS, + ID_FILE_ADD, + ID_FILE_EXPORTALL, + ID_FILE_EXPORTSELECTED, + ID_FILE_CHANGETYPE, + ID_FILE_CLOSE, + ID_EDIT_DUPLICATE, + ID_EDIT_REMOVE, + ID_EDIT_RENAME, + ID_EDIT_PROPERTIES, + ID_EDIT_CONTENTS, + ID_EDIT_FIND, + ID_EDIT_FINDNEXT, + ID_EDIT_FINDPREVIOUS, + ID_EDIT_MATCHES, + ID_EDIT_GOTO, + ID_TOOLS_RECOMPRESS, + ID_TOOLS_RESORT, + ID_TOOLS_REMOVETRASH, + 0}; + for(int i=0; DisableItems[i]; i++) + SetMenuItemInfo(hMenu, DisableItems[i], FALSE, &mii); + + DestroyWindow(hList); + + return true; +} + +bool Open(){ + if(!Archive::Close()) return false; + + GetFileTitle(Archive::Path, Archive::Filename, 1024); + + Archive::IsOpen = true; + Archive::IsModified = false; + + SetWorkspace(); +} + +bool PopulateEntries(){ + unsigned EntryCount = 1; + LVITEM item; + memset(&item, 0x00, sizeof(LVITEM)); + item.mask = LVIF_TEXT; + item.iItem = 0; + item.pszText = (LPTSTR) L"Test"; + SendMessage(hList, LVM_SETITEMCOUNT, EntryCount, LVSICF_NOSCROLL); + SendMessage(hList, LVM_INSERTITEM, 0, (LPARAM) &item); + + wchar_t buffer[17]; + wsprintf(buffer, L"%u file%s", EntryCount, (EntryCount == 1) ? L"" : L"s"); + SendMessage(statusbar, SB_SETTEXT, 0, (LPARAM) buffer); + + MENUITEMINFO mii; + mii.cbSize = sizeof(MENUITEMINFO); + mii.fMask = MIIM_STATE; + mii.fState = (EntryCount > 0) ? MFS_ENABLED : MFS_DISABLED; + SetMenuItemInfo(hMenu, ID_FILE_EXPORTALL, FALSE, &mii); + SetMenuItemInfo(hMenu, ID_EDIT_FIND, FALSE, &mii); + SetMenuItemInfo(hMenu, ID_EDIT_FINDNEXT, FALSE, &mii); + SetMenuItemInfo(hMenu, ID_EDIT_FINDPREVIOUS, FALSE, &mii); + SetMenuItemInfo(hMenu, ID_EDIT_MATCHES, FALSE, &mii); + SetMenuItemInfo(hMenu, ID_EDIT_GOTO, FALSE, &mii); + SetMenuItemInfo(hMenu, ID_TOOLS_RECOMPRESS, FALSE, &mii); + SetMenuItemInfo(hMenu, ID_TOOLS_RESORT, FALSE, &mii); + SetMenuItemInfo(hMenu, ID_TOOLS_REMOVETRASH, FALSE, &mii); + + return true; +} + +bool Save(){ + if(!Archive::IsModified) return true; + if(Path[0] == '\0'){ //No path because we're dealing with a newly created archive + return SaveAs(); + } + + Archive::IsModified = false; + wchar_t WindowTitle[1024+10]; + wsprintf(WindowTitle, L"%s%s - FARDive", Archive::Filename, L""); + SetWindowText(hWnd, WindowTitle); + return true; +} + +bool SaveAs(){ + wchar_t OldPath[1024], OldFilename[1024]; + //Backup old settings + wcscpy(OldPath, Archive::Path); + wcscpy(OldFilename, Archive::Filename); + + ofn.hwndOwner = hWnd; + ofn.lpstrFilter = FILTER_ARCHIVES_ONLY; + ofn.lpstrCustomFilter = ArchiveSaveFilter; + ofn.lpstrFile = Path; + ofn.lpstrFileTitle = Filename; + ofn.Flags = OFN_DONTADDTORECENT | OFN_PATHMUSTEXIST | OFN_FILEMUSTEXIST; + if(GetSaveFileName(&ofn) && Save()) + return true; + + //Restore old settings + wcscpy(Archive::Path, OldPath); + wcscpy(Archive::Filename, OldFilename); + return false; +} + +bool SetWorkspace(){ + wchar_t WindowTitle[1024+11]; + wsprintf(WindowTitle, L"%s%s - FARDive", Archive::Filename, Archive::IsModified ? L"*" : L""); + SetWindowText(hWnd, WindowTitle); + + MENUITEMINFO mii; + mii.cbSize = sizeof(MENUITEMINFO); + mii.fMask = MIIM_STATE; + mii.fState = MFS_ENABLED; + + SetMenuItemInfo(hMenu, ID_FILE_SAVE, FALSE, &mii); + SetMenuItemInfo(hMenu, ID_FILE_SAVEAS, FALSE, &mii); + SetMenuItemInfo(hMenu, ID_FILE_ADD, FALSE, &mii); + SetMenuItemInfo(hMenu, ID_FILE_CHANGETYPE, FALSE, &mii); + SetMenuItemInfo(hMenu, ID_FILE_CLOSE, FALSE, &mii); + + RECT ClientRect; + GetClientRect(hWnd, &ClientRect); + hList = CreateWindowEx(WS_EX_CLIENTEDGE | LVS_EX_DOUBLEBUFFER | WS_EX_COMPOSITED, WC_LISTVIEW, NULL, LVS_LIST | LVS_SHOWSELALWAYS | WS_CHILD | WS_VISIBLE, + 5, 5, 192, ClientRect.bottom-statusbarheight-10, hWnd, NULL, NULL, NULL); + SetWindowTheme(hList, L"Explorer", NULL); + + PopulateEntries(); + + return true; +} + +} \ No newline at end of file diff --git a/library/tools/FARDive/windows/MainWindow.cpp b/library/tools/FARDive/windows/MainWindow.cpp new file mode 100644 index 0000000..322da49 --- /dev/null +++ b/library/tools/FARDive/windows/MainWindow.cpp @@ -0,0 +1,167 @@ +#include "Windows.hpp" +#include "GUI.hpp" + +HWND hList; + +LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) +{ + switch(message){ + + case WM_SIZE: + SendMessage(statusbar, WM_SIZE, 0, 0); + + if(Archive::IsOpen) + SetWindowPos(hList, 0, 0, 0, 192, HIWORD(lParam)-statusbarheight-10, + SWP_ASYNCWINDOWPOS | SWP_NOMOVE | SWP_NOOWNERZORDER); + break; + + case WM_COMMAND: + switch(LOWORD(wParam)) + { + case ID_FILE_NEW: + if(Archive::Close()) + DialogBoxParam(hInst, MAKEINTRESOURCE(IDD_NEWARCHIVE), hWnd, NewArchive::DlgProc, NULL); + break; + case ID_FILE_OPEN: + if(!Archive::Close()) break; + ofn.hwndOwner = hWnd; + ofn.lpstrFilter = FILTER_ARCHIVES_FILES; + ofn.lpstrCustomFilter = ArchiveOpenFilter; + ofn.lpstrFile = Archive::Path; + ofn.lpstrFileTitle = NULL; + ofn.Flags = OFN_DONTADDTORECENT | OFN_PATHMUSTEXIST | OFN_FILEMUSTEXIST; + if(GetOpenFileName(&ofn)) + Archive::Open(); + break; + case ID_FILE_SAVE: + Archive::Save(); + break; + case ID_FILE_SAVEAS: + Archive::SaveAs(); + break; + case ID_FILE_ADD: { + DialogBoxParam(hInst, MAKEINTRESOURCE(IDD_ADDTOARCHIVE_FAR1), hWnd, AddToArchive::DlgProc, 0); + } break; + case ID_HELP_ABOUT: + MessageBox(hWnd, L"FARDive version " FDVERSION + L"\r\n\r\nThis is an alpha release of FARDive. The About box is not yet complete.\r\n\r\n" + L"Don't worry, file writing will not be implemented until it is guaranteed stable.\r\n\r\n" + L"Saving the archive through File -> Save (or Save as) will not actually " + L"take place until we reach that point.\r\n\r\n" + L"=-----=\r\n\r\n" + L"FARDive - Copyright (c) 2011 Fatbag \r\n\r\n" + L"Permission to use, copy, modify, and/or distribute this software for any " + L"purpose with or without fee is hereby granted, provided that the above " + L"copyright notice and this permission notice appear in all copies.\r\n\r\n" + L"THE SOFTWARE IS PROVIDED \"AS IS\" AND THE AUTHOR DISCLAIMS ALL WARRANTIES " + L"WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF " + L"MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR " + L"ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES " + L"WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN " + L"ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF " + L"OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.\r\n\r\n", L"About", MB_OK); + break; + case ID_FILE_CLOSE: + Archive::Close(); + break; + case ID_FILE_EXIT: + if(Archive::Close()) + PostQuitMessage(0); + break; + } + break; + + case WM_NOTIFY: { + NMHDR *nmhdr = (NMHDR*) lParam; + if(nmhdr->hwndFrom == hList){ + switch(nmhdr->code){ + case LVN_ITEMCHANGED: { + unsigned selected = SendMessage(hList, LVM_GETSELECTEDCOUNT, 0, 0); + + MENUITEMINFO mii; + mii.cbSize = sizeof(MENUITEMINFO); + mii.fMask = MIIM_STATE; + + mii.fState = (selected > 0) ? MFS_ENABLED : MFS_DISABLED; + SetMenuItemInfo(hMenu, ID_FILE_EXPORTSELECTED, FALSE, &mii); + SetMenuItemInfo(hMenu, ID_EDIT_DUPLICATE, FALSE, &mii); + SetMenuItemInfo(hMenu, ID_EDIT_REMOVE, FALSE, &mii); + + mii.fState = (selected == 1) ? MFS_ENABLED : MFS_DISABLED; + SetMenuItemInfo(hMenu, ID_EDIT_RENAME, FALSE, &mii); + SetMenuItemInfo(hMenu, ID_EDIT_PROPERTIES, FALSE, &mii); + SetMenuItemInfo(hMenu, ID_EDIT_CONTENTS, FALSE, &mii); + } break; + + case NM_RCLICK: { + unsigned selected = SendMessage(hList, LVM_GETSELECTEDCOUNT, 0, 0); + + if(listmenu) DestroyMenu(listmenu); + listmenu = CreatePopupMenu(); + + #define AddSeparator() \ + mii.fMask = MIIM_TYPE; \ + InsertMenuItem(listmenu, ++position, TRUE, &mii); \ + mii.fMask = MIIM_ID | MIIM_STRING | MIIM_BITMAP + #define AddItem(x,y,z) \ + mii.wID = x; \ + mii.dwTypeData = (wchar_t*) y; \ + mii.hbmpItem = PNGIcon[z]; \ + InsertMenuItem(listmenu, ++position, TRUE, &mii) + + unsigned position = 0; + MENUITEMINFO mii; + mii.cbSize = sizeof(MENUITEMINFO); + mii.fMask = MIIM_ID | MIIM_STRING | MIIM_BITMAP; + mii.fType = MFT_SEPARATOR; + + AddItem(ID_LISTMENU_ADD, L"A&dd to archive", BMP_FILE_ADD); + + if(selected > 0){ + AddSeparator(); + AddItem(ID_LISTMENU_EXPORTSELECTED, L"Export &selected...", BMP_FILE_EXPORTSELECTED); + AddItem(ID_LISTMENU_DUPLICATE, L"D&uplicate", BMP_EDIT_DUPLICATE); + AddItem(ID_LISTMENU_REMOVE, L"&Remove", BMP_EDIT_REMOVE); + if(selected == 1){ + AddSeparator(); + AddItem(ID_LISTMENU_RENAME, L"Re&name", BMP_EDIT_RENAME); + AddItem(ID_LISTMENU_PROPERTIES, L"Change pr&operties...", BMP_EDIT_PROPERTIES); + AddItem(ID_LISTMENU_CONTENTS, L"Change file &contents...", BMP_EDIT_CONTENTS); + } + } + + POINT p; + GetCursorPos(&p); + TrackPopupMenu(listmenu, TPM_RIGHTBUTTON, p.x, p.y, 0, hWnd, NULL); + PostMessage(hWnd, WM_NULL, 0, 0); + } break; + } + } + } break; + + case WM_DROPFILES: + if(!Archive::IsOpen){ + //Open as an archive + unsigned strlen = DragQueryFile((HDROP) wParam, 0, Archive::Path, 1024); + DragFinish((HDROP) wParam); + if(strlen) + Archive::Open(); + }else{ + //Add to the current archive + wchar_t EntryPath[1024]; + unsigned strlen = DragQueryFile((HDROP) wParam, 0, EntryPath, 1024); + DragFinish((HDROP) wParam); + DialogBoxParam(hInst, MAKEINTRESOURCE(IDD_ADDTOARCHIVE_FAR1), hWnd, AddToArchive::DlgProc, (LPARAM) EntryPath); + } + break; + + case WM_CLOSE: + if(Archive::Close()) + PostQuitMessage(0); + break; + + default: + return DefWindowProc(hWnd, message, wParam, lParam); + } + return 0; +} \ No newline at end of file diff --git a/library/tools/FARDive/windows/ReadPNGIcon.cpp b/library/tools/FARDive/windows/ReadPNGIcon.cpp new file mode 100644 index 0000000..faa6704 --- /dev/null +++ b/library/tools/FARDive/windows/ReadPNGIcon.cpp @@ -0,0 +1,67 @@ +#include +#include + +extern HDC hDC; + +char * buffer; + +void user_read_data(png_structp, png_bytep data, png_size_t length){ + memcpy(data, buffer, length); + buffer += length; +} + +HBITMAP ReadPNGIcon(int ID){ + HRSRC resource = FindResource(NULL, MAKEINTRESOURCE(ID), RT_RCDATA); + buffer = (char *) LockResource(LoadResource(NULL, resource)); + + png_structp png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); + png_set_read_fn(png_ptr, NULL, user_read_data); + png_infop info_ptr = png_create_info_struct(png_ptr); + png_read_info(png_ptr, info_ptr); + + png_uint_32 width, height; + int bit_depth, color_type; + + png_get_IHDR(png_ptr, info_ptr, &width, &height, &bit_depth, &color_type, NULL, NULL, NULL); + if(color_type == PNG_COLOR_TYPE_GRAY || color_type == PNG_COLOR_TYPE_GRAY_ALPHA) + png_set_gray_to_rgb(png_ptr); + else if(color_type == PNG_COLOR_TYPE_PALETTE || color_type == PNG_COLOR_MASK_PALETTE) + png_set_palette_to_rgb(png_ptr); + + png_set_add_alpha(png_ptr, 0xFF, PNG_FILLER_AFTER); + png_set_bgr(png_ptr); + + png_read_update_info(png_ptr, info_ptr); + BITMAPINFOHEADER bmi = { + sizeof(BITMAPINFOHEADER), //biSize + width, //biWidth + height, //biHeight + 1, //biPlanes + 32, //biBitCount + BI_RGB, //biCompression + width*height*4, //biSizeImage + 0, //biXPelsPerMeter + 0, //biYPelsPerMeter + 0, //biClrUsed + 0 //biClrImportant + }; + + unsigned char *buffer; + png_bytep row_pointers[height]; + HBITMAP hBmp = CreateDIBSection(hDC, (BITMAPINFO*) &bmi, DIB_RGB_COLORS, (void**) &buffer, NULL, 0); + for(unsigned i=0; i + +HBITMAP PNGIcon[MENUICONS]; + +HINSTANCE hInst; + +HWND hWnd, statusbar; +HDC hDC; +unsigned int statusbarheight; + +OPENFILENAME ofn; + +wchar_t ArchiveOpenFilter[128] = L"", ArchiveAddFilter[128] = L"", ArchiveSaveFilter[128] = L""; + +HMENU hMenu, listmenu = 0; + +int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE, LPSTR, int){ + hInst = hInstance; + + WNDCLASS wc = { + CS_HREDRAW | CS_VREDRAW, //style + WndProc, //lpfnWndProc + 0, //cbClsExtra + 0, //cbWndExtra + hInst, //hInstance + (HICON) LoadImage(hInst, MAKEINTRESOURCE(IDI_FARDIVE), IMAGE_ICON, 16, 16, LR_DEFAULTCOLOR), //hIcon + (HCURSOR) LoadImage(NULL, IDC_ARROW, IMAGE_CURSOR, 0, 0, LR_SHARED | LR_DEFAULTSIZE), //hCursor + (HBRUSH) (COLOR_MENU+1), //hbrBackground + MAKEINTRESOURCE(IDM_FARDIVE), //lpszMenuName + L"F" //lpszClassName + }; + RegisterClass(&wc); + + hWnd = CreateWindowEx(WS_EX_ACCEPTFILES | WS_EX_COMPOSITED, L"F", L"FARDive", + WS_OVERLAPPEDWINDOW | WS_VISIBLE, CW_USEDEFAULT, CW_USEDEFAULT, 616, 616, 0, 0, hInst, 0); + statusbar = CreateWindowEx(WS_EX_COMPOSITED, L"msctls_statusbar32", NULL, WS_CHILD | WS_VISIBLE, 0, 0, 0, 0, hWnd, 0, hInst, 0); + RECT rect; + GetWindowRect(statusbar, &rect); + statusbarheight = rect.bottom - rect.top; + + hDC = GetDC(NULL); + hMenu = GetMenu(hWnd); + + memset(&ofn, sizeof(ofn), 0x00); + ofn.lStructSize = sizeof(OPENFILENAME); + ofn.nMaxCustFilter = 128; + ofn.nMaxFile = 1024; + ofn.nMaxFileTitle = 1024; + ofn.lpstrDefExt = L"dat"; + + MENUITEMINFO mii; + mii.cbSize = sizeof(MENUITEMINFO); + mii.fMask = MIIM_BITMAP; + + static const short iconmenulist[] = { + 2100, 2101, 2102, 2103, 2104, 2105, 2106, 2107, + 2108, 2109, 2200, 2201, 2202, 2203, 2204, 2205, + 2206, 2207, 2208, 2209, 2210, 2300, 2301, 2302, + 2303, 2400, 2401 + }; + + for(unsigned i=0; i +#include +#include "../FARDive.hpp" +#include "resource.hpp" + +#define DefineDialog(x) namespace x {INT_PTR CALLBACK DlgProc(HWND, UINT, WPARAM, LPARAM);} + +LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); +DefineDialog(AddToArchive); +DefineDialog(NewArchive); \ No newline at end of file diff --git a/library/tools/FARDive/windows/common.cpp b/library/tools/FARDive/windows/common.cpp new file mode 100644 index 0000000..c9e5da6 --- /dev/null +++ b/library/tools/FARDive/windows/common.cpp @@ -0,0 +1,11 @@ +#include "Windows.hpp" +#include "GUI.hpp" + +void CenterDialog(HWND hDlg){ + RECT parent, child; + GetWindowRect(hWnd, &parent); + GetWindowRect(hDlg, &child); + int x = (parent.right + parent.left - child.right + child.left)/2, + y = (parent.bottom + parent.top - child.bottom + child.top)/2; + SetWindowPos(hDlg, 0, x, y, 0, 0, SWP_NOOWNERZORDER | SWP_NOSIZE | SWP_NOZORDER); +} \ No newline at end of file diff --git a/library/tools/FARDive/windows/resource.hpp b/library/tools/FARDive/windows/resource.hpp new file mode 100644 index 0000000..2c96dbb --- /dev/null +++ b/library/tools/FARDive/windows/resource.hpp @@ -0,0 +1,102 @@ +#define ID_VERSIONINFO 1 +#define ID_MANIFEST 1 //Not a typo +#define IDI_FARDIVE 2 + +#define ID_ACCELERATOR 100 +#define IDB_INFO 101 + +#define IDC_STATIC -1 + +#define IDM_FARDIVE 1000 +#define ID_FILE_NEW 1100 +#define ID_FILE_OPEN 1101 +#define ID_FILE_SAVE 1102 +#define ID_FILE_SAVEAS 1103 +#define ID_FILE_ADD 1104 +#define ID_FILE_EXPORTALL 1105 +#define ID_FILE_EXPORTSELECTED 1106 +#define ID_FILE_CHANGETYPE 1107 +#define ID_FILE_CLOSE 1108 +#define ID_FILE_EXIT 1109 +#define ID_EDIT_DUPLICATE 1200 +#define ID_EDIT_REMOVE 1201 +#define ID_EDIT_RENAME 1202 +#define ID_EDIT_PROPERTIES 1203 +#define ID_EDIT_CONTENTS 1204 +#define ID_EDIT_FIND 1205 +#define ID_EDIT_FINDNEXT 1206 +#define ID_EDIT_FINDPREVIOUS 1207 +#define ID_EDIT_MATCHES 1208 +#define ID_EDIT_GOTO 1209 +#define ID_EDIT_PREFERENCES 1210 +#define ID_TOOLS_RECOMPRESS 1300 +#define ID_TOOLS_RESORT 1301 +#define ID_TOOLS_REMOVETRASH 1302 +#define ID_TOOLS_BATCH 1303 +#define ID_HELP_WEBSITE 1400 +#define ID_HELP_ABOUT 1401 + +#define MENUICONS 27 + #define IDI_FILE_NEW 2100 + #define IDI_FILE_OPEN 2101 + #define IDI_FILE_SAVE 2102 + #define IDI_FILE_SAVEAS 2103 + #define IDI_FILE_ADD 2104 + #define IDI_FILE_EXPORTALL 2105 + #define IDI_FILE_EXPORTSELECTED 2106 + #define IDI_FILE_CHANGETYPE 2107 + #define IDI_FILE_CLOSE 2108 + #define IDI_FILE_EXIT 2109 + #define IDI_EDIT_DUPLICATE 2200 + #define IDI_EDIT_REMOVE 2201 + #define IDI_EDIT_RENAME 2202 + #define IDI_EDIT_PROPERTIES 2203 + #define IDI_EDIT_CONTENTS 2204 + #define IDI_EDIT_FIND 2205 + #define IDI_EDIT_FINDNEXT 2206 + #define IDI_EDIT_FINDPREVIOUS 2207 + #define IDI_EDIT_MATCHES 2208 + #define IDI_EDIT_GOTO 2209 + #define IDI_EDIT_PREFERENCES 2210 + #define IDI_TOOLS_RECOMPRESS 2300 + #define IDI_TOOLS_RESORT 2301 + #define IDI_TOOLS_REMOVEHOLES 2302 + #define IDI_TOOLS_BATCH 2303 + #define IDI_HELP_WEBSITE 2400 + #define IDI_HELP_ABOUT 2401 + +//Right-click menu items +#define ID_LISTMENU_ADD 3100 +#define ID_LISTMENU_EXPORTSELECTED 3101 +#define ID_LISTMENU_DUPLICATE 3102 +#define ID_LISTMENU_REMOVE 3103 +#define ID_LISTMENU_RENAME 3104 +#define ID_LISTMENU_PROPERTIES 3105 +#define ID_LISTMENU_CONTENTS 3106 + +//Dialogs +#define IDD_NEWARCHIVE 3000 +#define IDD_ADDTOARCHIVE_FAR1 3001 +#define IDD_ADDTOARCHIVE_FAR3 3002 +#define IDD_ADDTOARCHIVE_DBPF 3003 + +//New Archive +#define IDC_NA_TYPETEXT 4000 +#define IDC_NA_TYPE 4001 +#define IDC_NA_FARINFO 4002 +#define IDC_NA_DBPFINFO 4003 +#define IDC_NA_FARVERSIONTEXT 4004 +#define IDC_NA_FARVERSION 4005 +#define IDC_NA_DBPFVERSIONTEXT 4006 +#define IDC_NA_DBPFVERSION 4007 +#define IDC_NA_INDEXVERSIONTEXT 4008 +#define IDC_NA_INDEXVERSION 4009 +#define IDC_NA_DBPFCOMPRESS 4010 + +//Add to Archive +#define IDC_A2A_FILE 4000 +#define IDC_A2A_BROWSE 4001 +#define IDC_A2A_FILENAME 4002 +#define IDC_A2A_TYPEID 4003 +#define IDC_A2A_INSTANCEID 4004 +#define IDC_A2A_INSTANCEID2 4005 \ No newline at end of file diff --git a/library/tools/FARDive/windows/resource.rc b/library/tools/FARDive/windows/resource.rc new file mode 100644 index 0000000..7266628 --- /dev/null +++ b/library/tools/FARDive/windows/resource.rc @@ -0,0 +1,170 @@ +#define WIN32_LEAN_AND_MEAN +#include +#include "../version.hpp" +#include "resource.hpp" + +ID_MANIFEST RT_MANIFEST "../resources/Windows/FARDive.exe.manifest" +IDI_FARDIVE ICON "../resources/Windows/FARDive.ico" + +ID_VERSIONINFO VERSIONINFO +FILEVERSION FD_VERSION_A,FD_VERSION_B,FD_VERSION_C,FD_REVISION +PRODUCTVERSION FD_VERSION_A,FD_VERSION_B,FD_VERSION_C,FD_REVISION +FILEOS 0x00040000L //Windows 32-bit+ +FILETYPE 1 //1 is exe, 2 is dll, and so on. + //The list can be found at +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904B0" + BEGIN + VALUE "CompanyName", "Written by Fatbag." + VALUE "FileDescription", "An interactive file browser for FAR/DBPF" + VALUE "FileVersion", FDVERSION + VALUE "InternalName", "FARDive" + VALUE "LegalCopyright", "X11 license" + VALUE "OriginalFilename", "FARDive.exe" + VALUE "ProductName", "FARDive" + VALUE "ProductVersion", FDVERSION + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x0409, 0x04B0 + END +END + +IDM_FARDIVE MENUEX +BEGIN + POPUP "&File" + BEGIN + MENUITEM "&New...\tCtrl+N", ID_FILE_NEW + MENUITEM "&Open...\tCtrl+O", ID_FILE_OPEN + MENUITEM "&Save\tCtrl+S", ID_FILE_SAVE, MFT_STRING,MFS_GRAYED + MENUITEM "Save &As...", ID_FILE_SAVEAS, MFT_STRING,MFS_GRAYED + MENUITEM SEPARATOR + MENUITEM "A&dd to archive...", ID_FILE_ADD, MFT_STRING,MFS_GRAYED + MENUITEM "Export a&ll...", ID_FILE_EXPORTALL, MFT_STRING,MFS_GRAYED + MENUITEM "Export s&elected...", ID_FILE_EXPORTSELECTED, MFT_STRING,MFS_GRAYED + MENUITEM "Change archive &type...", ID_FILE_CHANGETYPE, MFT_STRING,MFS_GRAYED + MENUITEM SEPARATOR + MENUITEM "&Close archive\tCtrl+W", ID_FILE_CLOSE, MFT_STRING,MFS_GRAYED + MENUITEM "E&xit", ID_FILE_EXIT + END + POPUP "&Edit" + BEGIN + MENUITEM "&Duplicate\tCtrl+D", ID_EDIT_DUPLICATE, MFT_STRING,MFS_GRAYED + MENUITEM "&Remove\tDel", ID_EDIT_REMOVE, MFT_STRING,MFS_GRAYED + MENUITEM SEPARATOR + MENUITEM "Re&name", ID_EDIT_RENAME, MFT_STRING,MFS_GRAYED + MENUITEM "File pr&operties...", ID_EDIT_PROPERTIES, MFT_STRING,MFS_GRAYED + MENUITEM "Change file &contents...", ID_EDIT_CONTENTS, MFT_STRING,MFS_GRAYED + MENUITEM SEPARATOR + MENUITEM "&Find...\tCtrl+F", ID_EDIT_FIND, MFT_STRING,MFS_GRAYED + MENUITEM "Find ne&xt\tF3", ID_EDIT_FINDNEXT, MFT_STRING,MFS_GRAYED + MENUITEM "Find pre&vious\tShift+F3", ID_EDIT_FINDPREVIOUS, MFT_STRING,MFS_GRAYED + MENUITEM "&Show all matches", ID_EDIT_MATCHES, MFT_STRING,MFS_GRAYED + MENUITEM "&Go to...\tCtrl+G", ID_EDIT_GOTO, MFT_STRING,MFS_GRAYED + MENUITEM SEPARATOR + MENUITEM "&Preferences...", ID_EDIT_PREFERENCES + END + POPUP "&Tools" + BEGIN + MENUITEM "Re&compress...", ID_TOOLS_RECOMPRESS, MFT_STRING,MFS_GRAYED + MENUITEM "Re-&sort files...", ID_TOOLS_RESORT, MFT_STRING,MFS_GRAYED + MENUITEM "Remove t&rash files...", ID_TOOLS_REMOVETRASH, MFT_STRING,MFS_GRAYED + MENUITEM "&Batch processing...", ID_TOOLS_BATCH + END + POPUP "&Help" + BEGIN + MENUITEM "Visit &Website...", ID_HELP_WEBSITE + MENUITEM SEPARATOR + MENUITEM "&About", ID_HELP_ABOUT + END +END + +IDI_FILE_NEW RCDATA "../resources/icons/document-new.png" +IDI_FILE_OPEN RCDATA "../resources/icons/document-open.png" +IDI_FILE_SAVE RCDATA "../resources/icons/document-save.png" +IDI_FILE_SAVEAS RCDATA "../resources/icons/document-save-as.png" +IDI_FILE_ADD RCDATA "../resources/icons/list-add.png" +IDI_FILE_EXPORTALL RCDATA "../resources/icons/package-x-generic.png" +IDI_FILE_EXPORTSELECTED RCDATA "../resources/icons/package-x-generic-selected.png" +IDI_FILE_CHANGETYPE RCDATA "../resources/icons/applications-other.png" +IDI_FILE_CLOSE RCDATA "../resources/icons/emblem-unreadable.png" +IDI_FILE_EXIT RCDATA "../resources/icons/system-log-out.png" +IDI_EDIT_DUPLICATE RCDATA "../resources/icons/edit-copy.png" +IDI_EDIT_REMOVE RCDATA "../resources/icons/list-remove.png" +IDI_EDIT_RENAME RCDATA "../resources/icons/edit-select-all.png" +IDI_EDIT_PROPERTIES RCDATA "../resources/icons/document-properties.png" +IDI_EDIT_CONTENTS RCDATA "../resources/icons/accessories-text-editor.png" +IDI_EDIT_FIND RCDATA "../resources/icons/edit-find.png" +IDI_EDIT_FINDNEXT RCDATA "../resources/icons/go-last.png" +IDI_EDIT_FINDPREVIOUS RCDATA "../resources/icons/go-first.png" +IDI_EDIT_MATCHES RCDATA "../resources/icons/preferences-system-windows.png" +IDI_EDIT_GOTO RCDATA "../resources/icons/go-jump.png" +IDI_EDIT_PREFERENCES RCDATA "../resources/icons/preferences-desktop.png" +IDI_TOOLS_RECOMPRESS RCDATA "../resources/icons/system-file-manager.png" +IDI_TOOLS_RESORT RCDATA "../resources/icons/mail-send-receive.png" +IDI_TOOLS_REMOVEHOLES RCDATA "../resources/icons/edit-clear.png" +IDI_TOOLS_BATCH RCDATA "../resources/icons/utilities-terminal.png" +IDI_HELP_WEBSITE RCDATA "../resources/icons/internet-web-browser.png" +IDI_HELP_ABOUT RCDATA "../resources/icons/help-about.png" + +IDB_INFO BITMAP "../resources/icons/info.bmp" + +ID_ACCELERATOR ACCELERATORS +BEGIN + "^N", ID_FILE_NEW, ASCII + "^O", ID_FILE_OPEN, ASCII + "^S", ID_FILE_SAVE, ASCII + "^W", ID_FILE_CLOSE, ASCII + + "^D", ID_EDIT_DUPLICATE, ASCII + "^F", ID_EDIT_FIND, ASCII + VK_F3, ID_EDIT_FINDNEXT, VIRTKEY + VK_F3, ID_EDIT_FINDPREVIOUS, SHIFT, VIRTKEY + "^G", ID_EDIT_GOTO, ASCII +END + +IDD_NEWARCHIVE DIALOGEX 0, 0, 200, 161 +STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "New archive" +FONT 8, "MS Shell Dlg", 0, 0, 0x1 +BEGIN + LTEXT "",IDC_STATIC,-2,-2,204,27,SS_WHITERECT | WS_BORDER + RTEXT "Archive type:",IDC_NA_TYPETEXT,12,9,80,8 + COMBOBOX IDC_NA_TYPE,108,7,50,12,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP + GROUPBOX "FAR options",IDC_STATIC,7,33,186,30 + RTEXT "Version:",IDC_NA_FARVERSIONTEXT,12,44,80,8 + COMBOBOX IDC_NA_FARVERSION,108,43,50,12,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP + CONTROL IDB_INFO,IDC_NA_FARINFO,"STATIC",SS_BITMAP | SS_NOTIFY | SS_REALSIZEIMAGE,164,44,11,10 + GROUPBOX "DBPF options",IDC_STATIC,7,71,186,62 + RTEXT "Archive Version:",IDC_NA_DBPFVERSIONTEXT,12,83,80,8 + COMBOBOX IDC_NA_DBPFVERSION,108,81,50,12,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP + RTEXT "Index Version:",IDC_NA_INDEXVERSIONTEXT,12,103,80,8 + COMBOBOX IDC_NA_INDEXVERSION,108,101,50,12,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP + CONTROL IDB_INFO,IDC_NA_DBPFINFO,"STATIC",SS_BITMAP | SS_NOTIFY | SS_REALSIZEIMAGE,164,92,11,10 + CHECKBOX "Use QFS compression",IDC_NA_DBPFCOMPRESS,12,118,100,10,BS_AUTOCHECKBOX | WS_TABSTOP + DEFPUSHBUTTON "OK",IDOK,34,140,50,14 + PUSHBUTTON "Cancel",IDCANCEL,116,140,50,14 +END + +IDD_ADDTOARCHIVE_FAR1 DIALOGEX 0, 0, 200, 149 +STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "Add to archive" +FONT 8, "MS Shell Dlg", 0, 0, 0x1 +BEGIN + GROUPBOX "Source",IDC_STATIC,7,7,186,30 + CTEXT "File:",IDC_STATIC,12,18,30,8 + EDITTEXT IDC_A2A_FILE,44,16,90,14,ES_AUTOHSCROLL + PUSHBUTTON "Browse...",IDC_A2A_BROWSE,138,16,50,14 + GROUPBOX "Attributes",IDC_STATIC,7,44,186,58 + CTEXT "File name:",IDC_STATIC,12,54,80,8 + EDITTEXT IDC_A2A_FILENAME,96,52,92,14,ES_AUTOHSCROLL + CTEXT "Type ID:",IDC_STATIC,12,70,80,8 + EDITTEXT IDC_A2A_TYPEID,96,68,92,14,ES_AUTOHSCROLL + CTEXT "Instance ID:",IDC_STATIC,12,86,80,8 + EDITTEXT IDC_A2A_INSTANCEID,96,84,92,14,ES_AUTOHSCROLL + DEFPUSHBUTTON "OK",IDOK,34,128,50,14 + PUSHBUTTON "Cancel",IDCANCEL,116,128,50,14 +END \ No newline at end of file diff --git a/library/tools/Server GUI/CMakeLists.txt b/library/tools/Server GUI/CMakeLists.txt new file mode 100644 index 0000000..e69de29 diff --git a/library/tools/TSOSimulatorClient/Readme.txt b/library/tools/TSOSimulatorClient/Readme.txt new file mode 100644 index 0000000..2974fff --- /dev/null +++ b/library/tools/TSOSimulatorClient/Readme.txt @@ -0,0 +1,19 @@ +DLLs in The Sims Online (and also SimCity 4 and The Sims 2) use a special +interface, based on Microsoft COM. + +These DLLs each export exactly one function: +void * GZDllGetGZCOMDirector(void) + +This function creates and sets up a C++ object, with variables and member +functions, and returns a pointer to that object. This is your standard +C++ v-table. + +TSOSimulatorClientD.dll is the most important DLL in the game. It implements +the SimAntics virtual machine which executes all the objects in the game. +In our situation, we need to figure out everything it does, because we lack +any information regarding the SimAntics instruction set architecture. +A text dump of this DLL is not nearly enough to find this. The files in the +objectdata/globals folder are not nearly enough. The page on +simtech.sourceforge.net documenting all they know about SimAntics is not +nearly enough. We need to run this DLL in a disassembler and figure out the +meaning of every opcode used in every behavior script of the game. \ No newline at end of file diff --git a/library/tools/TSOSimulatorClient/TSOEdithEditor/TSOEdithEditor.cpp b/library/tools/TSOSimulatorClient/TSOEdithEditor/TSOEdithEditor.cpp new file mode 100644 index 0000000..c9350a2 --- /dev/null +++ b/library/tools/TSOSimulatorClient/TSOEdithEditor/TSOEdithEditor.cpp @@ -0,0 +1,52 @@ +/* + TSOEdithEditor - TSOEdithEditorD.dll injector + TSOEdithEditor.cpp - Copyright (c) 2012 Niotso Project + Author(s): Fatbag + + Permission to use, copy, modify, and/or distribute this software for any + purpose with or without fee is hereby granted, provided that the above + copyright notice and this permission notice appear in all copies. + + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +*/ + +#include +#include +#include "TSOEdithEditor.hpp" + +int main(){ + HMODULE dllmodule = LoadLibrary("TSOEdithEditorD.dll"); + if(dllmodule == NULL){ + printf("TSOEdithEditor: Error: Failed to load DLL \"TSOEdithEditorD.dll\"."); + return -1; + } + + cTSOEdithEditorDCOMDirector * (__stdcall *GZDllGetGZCOMDirector)(void) = + (cTSOEdithEditorDCOMDirector * (__stdcall *)(void)) GetProcAddress(dllmodule, "GZDllGetGZCOMDirector"); + if(GZDllGetGZCOMDirector == NULL){ + printf("TSOEdithEditor: Error: Failed to find GZDllGetGZCOMDirector() in TSOEdithEditorD.dll."); + return -1; + } + + printf("TSOEdithEditor: Calling GZDllGetGZCOMDirector() ...\n"); + cTSOEdithEditorDCOMDirector * Edith = GZDllGetGZCOMDirector(); + printf("TSOEdithEditor: Finished calling GZDllGetGZCOMDirector().\nThe value returned was: %p.\n", (void *) Edith); + + while(true){ + char buffer[8]; + printf("\nCall a function (0, 1, 2, ...) or q to exit. "); + //fgets(buffer, 8, stdin); + //if(buffer[0] == 'q') break; + //Edith->Object1.vtable5[atoi(buffer)](); + } + + printf("TSOEdithEditor: Exiting.\n"); + FreeLibrary(dllmodule); + return 0; +} \ No newline at end of file diff --git a/library/tools/TSOSimulatorClient/TSOEdithEditor/TSOEdithEditor.hpp b/library/tools/TSOSimulatorClient/TSOEdithEditor/TSOEdithEditor.hpp new file mode 100644 index 0000000..1d99ad9 --- /dev/null +++ b/library/tools/TSOSimulatorClient/TSOEdithEditor/TSOEdithEditor.hpp @@ -0,0 +1,144 @@ +/* + TSOEdithEditor - TSOEdithEditorD.dll injector + TSOEdithEditor.hpp - Copyright (c) 2012 Fatbag + Author(s): Fatbag + + Permission to use, copy, modify, and/or distribute this software for any + purpose with or without fee is hereby granted, provided that the above + copyright notice and this permission notice appear in all copies. + + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +*/ + +#include +#pragma pack(0) + +DECLARE_INTERFACE(cRZString) +{ + void * vtable1_cRZString; + char * mpBegin; //Pointer to beginning of string + char * mpEnd; //Pointer to null terminator + char * mpCapacity; //mpEnd + 1 + DWORD mAllocator; //0 + DWORD Zero1; //0 +}; + +struct stringstruct +{ + DWORD StringID; + DWORD Unknown; + char * PointerToBuffer; //Buffer + DWORD SizeOfBuffer; //256 + char Buffer[256]; +}; + +DECLARE_INTERFACE(cEdithEditorCOMDirector) +{ + void * vtable_1_cEdithEditorCOMDirector; + void * vtable_2_cEdithEditorCOMDirector; + DWORD Zero1; + DWORD Zero2; + cRZString string; + DWORD Zero5; + DWORD Zero6; + DWORD Zero7; + DWORD Zero8; + DWORD Zero9; + DWORD Zero10; + DWORD Zero11; + void * ptr; + DWORD Value1; //2 + DWORD Value2; //1 + float Value3; //1.0f + DWORD Value4; //0x40000000 + DWORD Value5; //2 + DWORD Value6; //0 + DWORD Value7; //1 + DWORD Value8; //0 + stringstruct string0; //StringID:0, Unknown:40, value:"index" + stringstruct string1; //StringID:1, Unknown:40, value:"value" + stringstruct string2; //StringID:2, Unknown:150, value:"Name" + stringstruct string3; //StringID:3, Unknown:200, value:"Description" + DWORD Value9; //0 + DWORD Value10; //0 + stringstruct string4; //StringID:0, Unknown:90, value:"Calling Tree" + stringstruct string5; //StringID:1, Unknown:86, value:"Type" + stringstruct string6; //StringID:2, Unknown:83, value:"Title" + stringstruct string7; //StringID:3, Unknown:65, value:"Yes" + stringstruct string8; //StringID:4, Unknown:65, value:"No" + stringstruct string9; //StringID:5, Unknown:65, value:"Cancel" + stringstruct string10; //StringID:6, Unknown:300, value:"Message" + stringstruct string11; //StringID:7, Unknown:45, value:"Tree ID" + stringstruct string12; //StringID:8, Unknown:50, value:"Node #" +}; + +DECLARE_INTERFACE(cTSOEdithEditorDCOMDirector) +{ + void * vtable1_cTSOEdithEditorDCOMDirector; + void * vtable2_cTSOEdithEditorDCOMDirector; + DWORD Zero1; + DWORD Zero2; + cRZString String1; + DWORD Zero5; + DWORD Zero6; + cEdithEditorCOMDirector ** memptr_1; + void ** memptr_2; + void ** memptr_3; //Same as memptr_2 + DWORD Zero7; + DWORD Zero8; + void * dllptr_4_100B5834; //CMemoryException TD + DWORD Value1; //1 + DWORD Value2; //0 + float Value3; //1.0f + DWORD Value4; //0x40000000 + DWORD Value5; //0 + DWORD Value6; //0 + DWORD Value7; //1 + cRZString String2; + cRZString String3; + cRZString String4; + cRZString String5; + cRZString String6; + cRZString String7; + cRZString String8; + cRZString String9; + DWORD Zero9; + DWORD Zero10; + DWORD Zero11; + DWORD Zero12; + DWORD Zero13; + DWORD Zero14; + DWORD Zero15; + cRZString String10; + cRZString String11; + cRZString String12; + cRZString String13; + cRZString String14; + DWORD Zero16; + DWORD Zero17; + DWORD Zero18; + DWORD Zero19; + DWORD Zero20; + cRZString String15; + cRZString String16; + cRZString String17; + cRZString String18; + cRZString String19; + cRZString String20; + cRZString String21; + cRZString String22; + cRZString String23; + cRZString String24; + cRZString String25; + cRZString String26; + cRZString String27; + cRZString String28; + cRZString String29; + cRZString String30; +}; \ No newline at end of file diff --git a/library/tools/TSOSimulatorClient/TSOEdithEditor/compile.bat b/library/tools/TSOSimulatorClient/TSOEdithEditor/compile.bat new file mode 100644 index 0000000..4d8af3e --- /dev/null +++ b/library/tools/TSOSimulatorClient/TSOEdithEditor/compile.bat @@ -0,0 +1 @@ +gcc -Wall -Wextra -Wabi -pedantic -fno-exceptions -m32 -o TSOEdithEditor.exe TSOEdithEditor.cpp -mconsole \ No newline at end of file diff --git a/library/tools/TSOSimulatorClient/TSOSimulatorClient.cpp b/library/tools/TSOSimulatorClient/TSOSimulatorClient.cpp new file mode 100644 index 0000000..204e7b3 --- /dev/null +++ b/library/tools/TSOSimulatorClient/TSOSimulatorClient.cpp @@ -0,0 +1,46 @@ +/* + TSOSimulatorClient - TSOSimulatorClientD.dll injector + TSOSimulatorClient.cpp - Copyright (c) 2012 Niotso Project + Author(s): Fatbag + + Permission to use, copy, modify, and/or distribute this software for any + purpose with or without fee is hereby granted, provided that the above + copyright notice and this permission notice appear in all copies. + + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +*/ + +#include +#include +#include "TSOSimulatorClient.hpp" + +int main(){ + HMODULE dllmodule = LoadLibrary("TSOSimulatorClientD.dll"); + if(dllmodule == NULL){ + printf("TSOSimulatorClient: Error: Failed to load DLL \"TSOSimulatorClientD.dll\"."); + return -1; + } + + cTSOSimulatorClientDCOMDirector * (__stdcall *GZDllGetGZCOMDirector)(void) = + (cTSOSimulatorClientDCOMDirector * (__stdcall *)(void)) GetProcAddress(dllmodule, "GZDllGetGZCOMDirector"); + if(GZDllGetGZCOMDirector == NULL){ + printf("TSOSimulatorClient: Error: Failed to find GZDllGetGZCOMDirector() in TSOSimulatorClientD.dll."); + return -1; + } + + printf("TSOSimulatorClient: Calling GZDllGetGZCOMDirector() ...\n"); + cTSOSimulatorClientDCOMDirector * Simulator = GZDllGetGZCOMDirector(); + printf("TSOSimulatorClient: Finished calling GZDllGetGZCOMDirector().\nThe value returned was: %p.\n", (void *) Simulator); + + printf("%s\n%s\n%s\n", Simulator->String1.Strings1[0], Simulator->String1.Strings2[0], Simulator->String1.Strings3[0]); + + printf("TSOSimulatorClient: Exiting.\n"); + FreeLibrary(dllmodule); + return 0; +} \ No newline at end of file diff --git a/library/tools/TSOSimulatorClient/TSOSimulatorClient.hpp b/library/tools/TSOSimulatorClient/TSOSimulatorClient.hpp new file mode 100644 index 0000000..6621c92 --- /dev/null +++ b/library/tools/TSOSimulatorClient/TSOSimulatorClient.hpp @@ -0,0 +1,70 @@ +/* + TSOSimulatorClient - TSOSimulatorClientD.dll injector + TSOSimulatorClient.hpp - Copyright (c) 2012 Fatbag + Author(s): Fatbag + + Permission to use, copy, modify, and/or distribute this software for any + purpose with or without fee is hereby granted, provided that the above + copyright notice and this permission notice appear in all copies. + + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +*/ + +#include +#pragma pack(0) + +DECLARE_INTERFACE(cRZString) +{ + //Base classes: cRZString, std::char_traits, ?$_String_base@DV?$__default_alloc_template@$00$0A@@std@@@std, cIGZString, cIGZUnknown + DWORD Zero1; + DWORD Zero2; + void * vtable5; + char ** Strings1; + char ** Strings2; + char ** Strings3; + DWORD Zero3; + DWORD Zero4; + DWORD Zero5; + void ** Pointer1; //12 bytes + void ** Pointer2; //4 bytes + void ** Pointer3; + DWORD Flags; + DWORD * Pointer4; //4 bytes + void * Pointer5; + void * Pointer6; + DWORD Unknown11; + DWORD Unknown12; +}; + +DECLARE_INTERFACE(cTSOSimulatorClientDCOMDirector) +{ + //Base classes: cTSOSimulatorClientDCOMDirector, cRZCOMDllDirector, cIGZCOMDirector, cIGZUnknown, cIGZFrameWorkHooks, cIGZUnknown + void * vtable2; + void * vtable1; + cRZString String1; + void * vtable4; + void * vtable3; + cRZString String2; + cRZString String3; + + DWORD Zero1; + DWORD Zero2; + DWORD Zero3; + DWORD Zero4; + DWORD Zero5; + DWORD Zero6; + DWORD Zero7; + DWORD Zero8; + DWORD Zero9; + DWORD Zero10; + DWORD Unknown1; + DWORD Pointer1; + DWORD Pointer2; + DWORD Zero11; +}; \ No newline at end of file diff --git a/library/tools/TSOSimulatorClient/compile.bat b/library/tools/TSOSimulatorClient/compile.bat new file mode 100644 index 0000000..4355932 --- /dev/null +++ b/library/tools/TSOSimulatorClient/compile.bat @@ -0,0 +1 @@ +gcc -Wall -Wextra -Wabi -pedantic -fno-exceptions -m32 -o TSOSimulatorClient.exe TSOSimulatorClient.cpp -mconsole \ No newline at end of file diff --git a/library/tools/TSOSimulatorClient/memory map.txt b/library/tools/TSOSimulatorClient/memory map.txt new file mode 100644 index 0000000..e0725af --- /dev/null +++ b/library/tools/TSOSimulatorClient/memory map.txt @@ -0,0 +1,106 @@ +CPU Dump +Address Hex dump +10102AF8 C4 61 0D 10|94 61 0D 10|00 00 00 00|00 00 00 00| 0 +10102B08 08 7A 0D 10|F8 30 3D 00|F8 30 3D 00|00 31 3D 00| 4 +10102B18 00 00 00 00|00 00 00 00|00 00 00 00|70 3B 3D 00| 8 +10102B28 7C 3B 3D 00|80 3B 3D 00|10 10 10 00|E0 32 3D 00| 12 +10102B38 E4 35 3D 00|E4 35 3D 00|00 00 00 00|01 00 00 00| 16 +10102B48 A4 62 0D 10|78 62 0D 10|00 00 00 00|00 00 00 00| 20 +10102B58 08 7A 0D 10|10 31 3D 00|10 31 3D 00|18 31 3D 00| 24 +10102B68 00 00 00 00|00 00 00 00|00 00 00 00|00 00 00 00| 28 +10102B78 00 00 00 00|00 00 00 00|10 10 10 00|58 3C 3D 00| 32 +10102B88 5C 3F 3D 00|5C 3F 3D 00|0D 00 00 00|01 00 00 00| 36 +10102B98 00 00 00 00|00 00 00 00|08 7A 0D 10|08 09 3D 00| 40 +10102BA8 1D 09 3D 00|1E 09 3D 00|00 00 00 00|00 00 00 00| 44 +10102BB8 00 00 00 00|18 31 3D 00|80 3B 3D 00|88 0A 3D 00| 48 +10102BC8 A8 2D 3D 00|00 00 00 00|00 00 00 00|00 00 00 00| 52 +10102BD8 00 00 00 00|00 00 00 00|00 00 00 00|00 00 00 00| 56 +10102BE8 00 00 00 00|00 00 00 00|00 00 00 00|00 00 00 00| 60 +10102BF8 00 00 00 00|00 00 00 00|00 00 00 00|00 00 00 00| 64 +10102C08 10 0C 00 00|B0 3B 3D 00|40 3C 3D 00|00 00 00 00| 68 + +Offset: Meaning +0: Pointer to v-table 2 - 100D61C4 +1: Pointer to v-table 1 - 100D6194 + +2: 0 +3: 0 +4: Pointer to v-table 5 - 100D7A08 +5: Pointer +6: Pointer +7: Pointer +8: 0 +9: 0 +10: 0 +11: Pointer +12: Pointer +13: Pointer +14: Flags? - 0x00101010 +15: Pointer +16: Pointer +17: Pointer +18: 0 +19: 1 + +20: Pointer to v-table 4 - 100D62A4 +21: Pointer to v-table 3 - 100D6278 + +22: 0 +23: 0 +24: Pointer to v-table 5 - 100D7A08 +25: Pointer +26: Pointer +27: Pointer +28: 0 +29: 0 +30: 0 +31: 0 +32: 0 +33: 0 +34: Flags? - 0x00101010 +35: Pointer +36: Pointer +37: Pointer +38: 13 +39: 1 + +40: 0 +41: 0 +42: Pointer to v-table 5 - 100D7A08 +43: Pointer +44: Pointer +45: Pointer +46: 0 +47: 0 +48: 0 +49: Pointer +50: Pointer +51: Pointer +52: Pointer +53: 0 +54: 0 +55: 0 +56: 0 +57: 0 + +58: 0 +59: 0 +60: 0 +61: 0 +62: 0 +63: 0 +64: 0 +65: 0 +66: 0 +67: 0 +68: 3088 +69: Pointer +70: Pointer +71: 0 + +5 v-tables: +100D6194 (12 entries) +100D61C4 (17 entries) +100D6278 (11 entries) +100D62A4 (344 entries) +100D7A08 (695 entries) \ No newline at end of file diff --git a/library/tools/Translate Tool/CMakeLists.txt b/library/tools/Translate Tool/CMakeLists.txt new file mode 100644 index 0000000..e69de29 diff --git a/library/tools/Translate Tool/TranslateTool.hpp b/library/tools/Translate Tool/TranslateTool.hpp new file mode 100644 index 0000000..7b604e6 --- /dev/null +++ b/library/tools/Translate Tool/TranslateTool.hpp @@ -0,0 +1,32 @@ +/* + The Sims Online Translation Tool - Graphical Translation Writer for TSO + TranslateTool.hpp - Copyright (c) 2012 Niotso Project + Author(s): Fatbag + + Permission to use, copy, modify, and/or distribute this software for any + purpose with or without fee is hereby granted, provided that the above + copyright notice and this permission notice appear in all copies. + + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +*/ + +namespace Translation { + extern wchar_t Path[1024], Filename[1024]; + + extern bool IsOpen; + extern bool IsModified; + + bool Add(const wchar_t * Path); + bool Close(); + bool Open(); + bool PopulateEntries(); + bool Save(); + bool SaveAs(); + bool SetWorkspace(); +} \ No newline at end of file diff --git a/library/tools/hitutils/CMakeLists.txt b/library/tools/hitutils/CMakeLists.txt new file mode 100644 index 0000000..97c1029 --- /dev/null +++ b/library/tools/hitutils/CMakeLists.txt @@ -0,0 +1,18 @@ +cmake_minimum_required(VERSION 2.6) +project(hitutils) + +set(HITDUMP_SOURCES + hitdump.c +) + +set(HITASM_SOURCES + hitasm.c +) + +set(HITLD_SOURCES + hitld.c +) + +add_executable(hitdump ${HITDUMP_SOURCES}) +add_executable(hitasm ${HITASM_SOURCES}) +add_executable(hitld ${HITLD_SOURCES}) \ No newline at end of file diff --git a/library/tools/hitutils/Design.txt b/library/tools/hitutils/Design.txt new file mode 100644 index 0000000..c628e39 --- /dev/null +++ b/library/tools/hitutils/Design.txt @@ -0,0 +1,59 @@ +1. Dumping + +A HIT file is dumped by running this command: + +$ hitdump -o example.txt example.hit + +This will disassemble example.hit into example.txt using MakeTrax syntax. +* Instructions, arguments, registers, and globals are rewritten using the mnemonics in + example.hsm. +* The disassembly is split into BINARY[] sections and logical addresses are defined names + using the data provided by example.hsm, example.hot, example.evt, and the HIT symbol table. + All user-created constants in example.hsm that begin with "tkd_" are interpreted by + hitutils as track data. + + +2. Reassembling + +A source file is assembled into an intermediate object by running this command: + +$ hitasm -o example.o example.txt + +This will assemble example.txt into example.o according to MakeTrax syntax. +* Instructions, arguments, and registers are interpreted using the mnemonics in example.hsm. +* The logical addresses defined in example.hsm are ignored. + + +3. Relinking into the game + +Object files are relinked into the game by running this command: + +$ hitld -o example.hit example1.o example2.o ... + +This will link example1.o, example2.o, and so on into example.hit and reconstruct the +relocation tables in example.hot and example.hsm. + + +To clarify, the user cannot change the events signaled by objects without changing the +SimAntics of each of those objects. The HIT subroutines invoked by those events, however, can +be changed or swapped out quite easily. + + +Appendix A. Address labeling strategies + +In TSO, the HIT symbol table lists the logical address of each exported function, along with +the Track File ID associated with it. + +In TSO, the EVT file lists the name of each exported function, along with the Track File ID +associated with it. + +* The HIT file can be used to locate each exported function for placement in BINARY[] sections. +* In the case of tsov2.hit, the HIT and EVT files can be used together to name these exported + functions, seeing that the HSM file is not provided. + +The HOT file lists all addresses that were labelled, along with the Track File ID associated +with it, in the TrackData section. Because the HIT symbol table is not included in The Sims 1, +the TrackData section can be used instead. + +The HSM file lists the name of each labelled address, along with its logical address in the +HIT file. (This is done with a Ctrl+F for "tkd_"). \ No newline at end of file diff --git a/library/tools/hitutils/elf.dat b/library/tools/hitutils/elf.dat new file mode 100644 index 0000000..fcf6fa7 Binary files /dev/null and b/library/tools/hitutils/elf.dat differ diff --git a/library/tools/hitutils/elf.txt b/library/tools/hitutils/elf.txt new file mode 100644 index 0000000..bd2ef7e --- /dev/null +++ b/library/tools/hitutils/elf.txt @@ -0,0 +1,120 @@ +ELF Header: +* e_ident: + * Elf32_Word EI_MAG0: (7F 45 4C 46) + * unsigned char EI_CLASS: ELFCLASS32 (01) + * unsigned char EI_DATA: ELFDATA2LSB (01) + * unsigned char EI_VERSION: EV_CURRENT (01) + * unsigned char EI_PAD[9]: (00 00 00 00 00 00 00 00 00) +* Elf32_Half e_type: ET_REL (01 00) +* Elf32_Half e_machine: EM_NONE (00 00) +* Elf32_Word e_version: EV_CURRENT (01 00 00 00) +* Elf32_Addr e_entry: 0 (00 00 00 00) +* Elf32_Off e_phoff: 0 (00 00 00 00) +* Elf32_Off e_shoff: 64 (40 00 00 00) +* Elf32_Word e_flags: 0x00000001 (01 00 00 00) +* Elf32_Half e_ehsize: 52 (34 00) +* Elf32_Half e_phentsize: 0 (00 00) +* Elf32_Half e_phnum: 0 (00 00) +* Elf32_Half e_shentsize: 40 (28 00) +* Elf32_Half e_shnum: 6 (06 00) +* Elf32_Half e_shstrndx: 2 (02 00) +* unsigned char padding[12] = (00 00 00 00 00 00 00 00 00 00 00 00) + +Section headers +0 (""): All 0s +1 (".text"): +* Elf32_Word sh_name: 1 (01 00 00 00) +* Elf32_Word sh_type: SHT_PROGBITS (01 00 00 00) +* Elf32_Word sh_flags: SHF_ALLOC | SHF_EXECINSTR (06 00 00 00) +* Elf32_Addr sh_addr: 0 (00 00 00 00) +* Elf32_Off sh_offset: (?? ?? ?? ??) +* Elf32_Word sh_size: (?? ?? ?? ??) +* Elf32_Word sh_link: 0 (00 00 00 00) +* Elf32_Word sh_info: 0 (00 00 00 00) +* Elf32_Word sh_addralign: 1 (01 00 00 00) +* Elf32_Word sh_entsize: 0 (00 00 00 00) +2 (".shstrtab"): +* Elf32_Word sh_name: 7 (07 00 00 00) +* Elf32_Word sh_type: SHT_STRTAB (03 00 00 00) +* Elf32_Word sh_flags: 0 (00 00 00 00) +* Elf32_Addr sh_addr: 0 (00 00 00 00) +* Elf32_Off sh_offset: (?? ?? ?? ??) +* Elf32_Word sh_size: (?? ?? ?? ??) +* Elf32_Word sh_link: 0 (00 00 00 00) +* Elf32_Word sh_info: 0 (00 00 00 00) +* Elf32_Word sh_addralign: 1 (01 00 00 00) +* Elf32_Word sh_entsize: 0 (00 00 00 00) +3 (".symtab"): +* Elf32_Word sh_name: 17 (11 00 00 00) +* Elf32_Word sh_type: SHT_SYMTAB (02 00 00 00) +* Elf32_Word sh_flags: 0 (00 00 00 00) +* Elf32_Addr sh_addr: 0 (00 00 00 00) +* Elf32_Off sh_offset: (?? ?? ?? ??) +* Elf32_Word sh_size: (?? ?? ?? ??) +* Elf32_Word sh_link: 4 (04 00 00 00) +* Elf32_Word sh_info: 5 (05 00 00 00) +* Elf32_Word sh_addralign: 1 (01 00 00 00) +* Elf32_Word sh_entsize: 16 (10 00 00 00) +4 (".strtab"): +* Elf32_Word sh_name: 25 (19 00 00 00) +* Elf32_Word sh_type: SHT_STRTAB (03 00 00 00) +* Elf32_Word sh_flags: 0 (00 00 00 00) +* Elf32_Addr sh_addr: 0 (00 00 00 00) +* Elf32_Off sh_offset: (?? ?? ?? ??) +* Elf32_Word sh_size: (?? ?? ?? ??) +* Elf32_Word sh_link: 0 (00 00 00 00) +* Elf32_Word sh_info: 0 (00 00 00 00) +* Elf32_Word sh_addralign: 1 (01 00 00 00) +* Elf32_Word sh_entsize: 0 (00 00 00 00) +5 (".rel.text"): +* Elf32_Word sh_name: 33 (21 00 00 00) +* Elf32_Word sh_type: SHT_REL (09 00 00 00) +* Elf32_Word sh_flags: 0 (00 00 00 00) +* Elf32_Addr sh_addr: 0 (00 00 00 00) +* Elf32_Off sh_offset: (?? ?? ?? ??) +* Elf32_Word sh_size: (?? ?? ?? ??) +* Elf32_Word sh_link: 3 (03 00 00 00) +* Elf32_Word sh_info: 1 (01 00 00 00) +* Elf32_Word sh_addralign: 1 (01 00 00 00) +* Elf32_Word sh_entsize: 8 (08 00 00 00) + +Symbol format: (4+4+4+1+1+2= 16 bytes) +Undefined symbol: +* Elf32_Word st_name: 0 (00 00 00 00) +* Elf32_Addr st_value: 0 (00 00 00 00) +* Elf32_Word st_size: 0 (00 00 00 00) +* unsigned char st_info: 0 (00) +* unsigned char st_other: 0 (00) +* Elf32_Half st_shndx: 0 (00 00) +Source file symbol: +* Elf32_Word st_name: 1 (01 00 00 00) +* Elf32_Addr st_value: 0 (00 00 00 00) +* Elf32_Word st_size: 0 (00 00 00 00) +* unsigned char st_info: ELF32_ST_INFO(STB_LOCAL, STT_FILE) (04) +* unsigned char st_other: 0 (00) +* Elf32_Half st_shndx: SHN_ABS (f1 ff) +Text section symbol: +* Elf32_Word st_name: 0 (00 00 00 00) +* Elf32_Addr st_value: 0 (00 00 00 00) +* Elf32_Word st_size: 0 (00 00 00 00) +* unsigned char st_info: ELF32_ST_INFO(STB_LOCAL, STT_SECTION) (03) +* unsigned char st_other: 0 (00) +* Elf32_Half st_shndx: 1 (01 00) +Undefined symbol: +* Elf32_Word st_name: (?? ?? ?? ??) +* Elf32_Addr st_value: 0 (00 00 00 00) +* Elf32_Word st_size: 0 (00 00 00 00) +* unsigned char st_info: ELF32_ST_INFO(STB_GLOBAL, STT_FUNC) (12) +* unsigned char st_other: 0 (00) +* Elf32_Half st_shndx: 0 (00 00) +Defined symbol: +* Elf32_Word st_name: (?? ?? ?? ??) +* Elf32_Addr st_value: (?? ?? ?? ??) +* Elf32_Word st_size: 0 (00 00 00 00) +* unsigned char st_info: ELF32_ST_INFO(STB_GLOBAL, STT_FUNC) (12) +* unsigned char st_other: 0 (00) +* Elf32_Half st_shndx: 1 (01 00) + +Relocation format: (4+4=8) +* Elf32_Addr r_offset: (?? ?? ?? ??) +* Elf32_Word r_info: ELF32_R_INFO(?, R_386_PC32) (?? ?? ?? ??) \ No newline at end of file diff --git a/library/tools/hitutils/hitasm.c b/library/tools/hitutils/hitasm.c new file mode 100644 index 0000000..3fe3139 --- /dev/null +++ b/library/tools/hitutils/hitasm.c @@ -0,0 +1,602 @@ +/* + hitutils - The Sims HIT (dis)assembler and linker + hitasm.c - Copyright (c) 2012 Niotso Project + Author(s): Fatbag + + Permission to use, copy, modify, and/or distribute this software for any + purpose with or without fee is hereby granted, provided that the above + copyright notice and this permission notice appear in all copies. + + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +*/ + +#include +#include +#include +#include +#include +#include +#define HITASM_HEADERS +#include "hitutils.h" + +static uint8_t ObjectHeader[] = { + 0x7F, 0x45, 0x4C, 0x46, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x34, 0x00, 0x00, 0x00, 0x00, 0x00, 0x28, 0x00, + 0x06, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x07, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x19, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00 +}; + +enum { + txt, out, filecount +}; + +typedef struct { + uint8_t * Data; + size_t Position; + size_t Size; + char * Name; +} ByteWriterContext; + +static void bw_expand(ByteWriterContext *bwc){ + void * ptr; + if(bwc->Size > SIZE_MAX/2 || !(ptr = realloc(bwc->Data, bwc->Size<<=1))) + Shutdown_M("%sCould not allocate memory for %s section.\n", "hitasm: Error: ", bwc->Name); + bwc->Data = ptr; +} + +static void bw_write32(ByteWriterContext *bwc, uint32_t value){ + if(bwc->Size-bwc->Position < 4) + bw_expand(bwc); + write_uint32(bwc->Data+bwc->Position, value); + bwc->Position += 4; +} + +static void bw_write8(ByteWriterContext *bwc, uint8_t value){ + if(bwc->Size-bwc->Position < 1) + bw_expand(bwc); + bwc->Data[bwc->Position] = value; + bwc->Position += 1; +} + +static void bw_write_memory(ByteWriterContext *bwc, const uint8_t *ptr, size_t size){ + while(bwc->Size-bwc->Position < size) + bw_expand(bwc); + do bwc->Data[bwc->Position++] = *ptr++; + while(--size); +} + +static void bw_write_string(ByteWriterContext *bwc, const char *string){ + do bw_write8(bwc, *string); + while(*string++); +} + +typedef struct { + char * Token; + size_t Line, NextLine; + size_t Col, NextCol; + int GaveLastToken; + ByteReaderContext brc; +} ParserContext; + +enum { + TK_CROSSLINES = 1, + CN_LABELONLY = 1 +}; + +enum Sections { + Text, SymbolTable, StringTable, RelocationTable, SectionCount +}; + +static FILE *hFile = NULL; +static char *path[filecount] = {NULL}; +static uint8_t *data[filecount] = {NULL}; +static ByteWriterContext Section[] = { + {0,0,1024,"text"}, + {0,0,1024,"symtab"}, + {0,0,1024,"strtab"}, + {0,0,1024,".rel.text"} +}; + +static uint8_t * add_symbol(const char * Name){ + bw_write32(&Section[SymbolTable], Section[StringTable].Position); + bw_write32(&Section[SymbolTable], 0); + bw_write32(&Section[SymbolTable], 0); + bw_write32(&Section[SymbolTable], 18); + bw_write_string(&Section[StringTable], Name); + return Section[SymbolTable].Data + Section[SymbolTable].Position - 16; +} + +static uint8_t * find_symbol_by_name(const char * Name, uint32_t * SymbolIndex){ + uint32_t p; + for(p=48; p>4; + return Section[SymbolTable].Data + p; + } + } + + if(SymbolIndex) *SymbolIndex = p>>4; + return NULL; +} + +static __inline int whitespace(const uint8_t *Data){ + return (Data[0] == ' ' || Data[0] == '\t' || Data[0] == '\r' || Data[0] == '\n' || Data[0] == '\0'); +} + +static __inline int comment(const uint8_t *Data){ + return (Data[0] == ';' || (Data[0] == '#' && whitespace(Data+1))); +} + +static int parser_next_token(ParserContext *pc, int CrossLines){ + if(!pc->brc.Size) return 0; + + if(pc->GaveLastToken){ + pc->GaveLastToken = 0; + /* find the start of the next token */ + while(1){ + if(whitespace(pc->brc.Data)){ + if(pc->brc.Data[0] == '\n'){ + pc->NextLine++; pc->NextCol = 1; + }else if(pc->brc.Data[0] == '\t'){ + pc->NextCol += 4 - ((pc->NextCol-1)%4); + }else{ + pc->NextCol++; + } + }else if(comment(pc->brc.Data)){ + /* skip to the end of the line */ + pc->NextLine++; pc->NextCol = 1; + do { + if(!--pc->brc.Size) return 0; + pc->brc.Data++; + } while(pc->brc.Data[0] != '\n'); + }else break; + + if(!--pc->brc.Size) return 0; + pc->brc.Data++; + } + } + if(!CrossLines && pc->Line != pc->NextLine) return 0; + pc->Token = (char*)pc->brc.Data; + pc->Line = pc->NextLine; + pc->Col = pc->NextCol++; + pc->GaveLastToken = 1; + + /* mark the end of this token with a null character */ + while(1){ + if(!--pc->brc.Size) return 1; + pc->brc.Data++; + + if(whitespace(pc->brc.Data)){ + if(pc->brc.Data[0] == '\n'){ + pc->NextLine++; pc->NextCol = 1; + }else if(pc->brc.Data[0] == '\t'){ + pc->NextCol += 4 - ((pc->NextCol-1)%4); + }else{ + pc->NextCol++; + } + break; + }else if(comment(pc->brc.Data)){ + /* skip to the end of the line */ + pc->NextLine++; pc->NextCol = 1; + do { + if(!--pc->brc.Size) return 1; + pc->brc.Data++; + } while(pc->brc.Data[0] != '\n'); + break; + }else pc->NextCol++; + } + pc->brc.Size--; + pc->brc.Data[0] = '\0'; + pc->brc.Data++; + + return 1; +} + +static __inline void verify_eol(ParserContext *pc){ + if(parser_next_token(pc, 0)) + Shutdown_M("%s:%u:%u: error: expected newline before '%s'\n", + path[txt], pc->Line, pc->Col, pc->Token); +} + +static void verify_name(const ParserContext *pc, const char *thistoken, const char *nexttoken){ + const char *Name; + for(Name = pc->Token; *Name; Name++) + if((Name == pc->Token || *Name < '0' || *Name > '9') + && (*Name < 'A' || *Name > 'Z') && (*Name < '_' || *Name > '_') && (*Name < 'a' || *Name > 'z')) + Shutdown_M("%s:%u:%u: error: expected %s before '%c'\n", + path[txt], pc->Line, pc->Col, (Name == pc->Token) ? thistoken : nexttoken, *Name); +} + +static __inline void parser_add_symbol(const ParserContext *pc){ + uint8_t * Symbol; + verify_name(pc, "label or integer constant", "newline"); + + /* we're using the ELF st_size field (offset 8) to hold the line number temporarily */ + Symbol = find_symbol_by_name(pc->Token, NULL); + if(Symbol){ + if(read_uint32(Symbol+4)) + Shutdown_M("%s:%u:%u: error: label '%s' redefined (previous definition at %s:%u:1)\n", + path[txt], pc->Line, pc->Col, pc->Token, path[txt], read_uint32(Symbol+8)); + }else Symbol = add_symbol(pc->Token); + + write_uint32(Symbol+4, Section[Text].Position); + write_uint32(Symbol+8, pc->Line); +} + +static __inline void parser_add_reference(const ParserContext *pc){ + uint32_t SymbolIndex; + verify_name(pc, "label", "operand or newline"); + + if(!find_symbol_by_name(pc->Token, &SymbolIndex)) + add_symbol(pc->Token); + + bw_write32(&Section[RelocationTable], Section[Text].Position); + bw_write32(&Section[RelocationTable], (SymbolIndex<<8)|0x02); +} + +static uint32_t read_integer(const ParserContext *pc, uint32_t maxval){ + unsigned long value; + char * endptr; + + if(pc->Token[0] == '-') + Shutdown_M("%s:%u:%u: error: integer constant '%s' may not be negative\n", + path[txt], pc->Line, pc->Col, pc->Token); + if(pc->Token[0] < '0' || pc->Token[0] > '9') + Shutdown_M("%s:%u:%u: error: expected integer constant before '%s'\n", + path[txt], pc->Line, pc->Col, pc->Token); + if(pc->Token[0] == '0' && (pc->Token[1] == 'x' || pc->Token[1] == '0' || pc->Token[1] == 'o')) + Shutdown_M("%s:%u:%u: error: illegal %s prefix '%s%c' on integer constant '%s'\n", + path[txt], pc->Line, pc->Col, pc->Token[1] == 'x' ? "hexadecimal" : "octal", + pc->Token[1] != '0' ? "0" : "", pc->Token[1], pc->Token); + + value = strtoul(pc->Token, &endptr, 10); + if(*endptr == '.') + Shutdown_M("%s:%u:%u: error: illegal floating point constant '%s'\n", + path[txt], pc->Line, pc->Col, endptr, pc->Token); + if(*endptr != '\0') + Shutdown_M("%s:%u:%u: error: illegal suffix '%s' on integer constant '%s'\n", + path[txt], pc->Line, pc->Col, endptr, pc->Token); + if(value > (unsigned long) maxval || errno == ERANGE) + Shutdown_M("%s:%u:%u: error: integer constant '%s' is out of range\n", + path[txt], pc->Line, pc->Col, pc->Token); + + return (uint32_t)value; +} + +static uint32_t read_constant(ParserContext *pc, int Type, uint32_t maxval){ + unsigned i; + if(pc->Token[0] == '#') /* note that the tokens "#" and "" will never be returned by the parser */ + pc->Token++; + + if((pc->Token[0] >= '0' && pc->Token[0] <= '9') || pc->Token[0] == '-' || pc->Token[0] == '.'){ + if(Type == CN_LABELONLY) + Shutdown_M("%s:%u:%u: error: explicit address '%s' is forbidden\n", + path[txt], pc->Line, pc->Col, pc->Token); + return read_integer(pc, maxval); + } + + verify_name(pc, "constant or label", "operand or newline"); + for(i=0; iToken)){ + if(Constants[i].Value > maxval) + Shutdown_M("%s:%u:%u: error: integer constant '%s' (%u) is out of range\n", + path[txt], pc->Line, pc->Col, pc->Token, Constants[i].Value); + return Constants[i].Value; + } + } + + if(maxval != 0xFFFFFFFF) + Shutdown_M("%s:%u:%u: error: address referenced by label '%s' is out of range\n", + path[txt], pc->Line, pc->Col, pc->Token); + parser_add_reference(pc); + return 0; +} + +static __inline uint8_t read_instruction(const ParserContext *pc, uint32_t *operands){ + uint8_t i; + verify_name(pc, "instruction", "operand or newline"); + + for(i=0; iToken, Instructions[i].Name)){ + *operands = Instructions[i].Operands; + return i+1; + } + } + + return 0; +} + +static __inline uint8_t read_variable(const ParserContext *pc, const variable_t *Variables, size_t VariableCount){ + unsigned i; + verify_name(pc, "variable", "operand or newline"); + + for(i=0; iToken)) + return Variables[i].Value; + + Shutdown_M("%s:%u:%u: error: unrecognized variable '%s'\n", + path[txt], pc->Line, pc->Col, pc->Token); + return 0; +} + +static void Shutdown(){ + unsigned i; + for(i=0; i.\n" + "hitutils is maintained by the Niotso project.\n" + "Home page: \n"); + return 0; + } + for(i=1; i<(unsigned)argc-1; i++){ + if(!strcmp(argv[i], "-ts1")) SimsVersion = VERSION_TS1; + else if(!strcmp(argv[i], "-tso")) SimsVersion = VERSION_TSO; + else if(!strcmp(argv[i], "-f")) overwrite = 1; + else if(i != (unsigned)argc-2){ + if(!strcmp(argv[i], "-o")) path[out] = argv[++i]; + else break; + } + else break; + } + path[txt] = argv[i]; + for(i=0; i= '0' && pc.Token[0] <= '9') || pc.Token[0] == '-' || pc.Token[0] == '.' + || pc.Token[0] == '#' || !(opcode = read_instruction(&pc, &operands))){ + /* declare bytes (db and dd pseudo-instructions) */ + do { + if(pc.Token[0] != '#') + bw_write8(&Section[Text], read_integer(&pc, 0x000000FF)); + else + bw_write32(&Section[Text], read_constant(&pc, 0, 0xFFFFFFFF)); + } while(parser_next_token(&pc, 0)); + continue; + }else{ + const char * InstructionName = pc.Token; + bw_write8(&Section[Text], opcode); + + for(i=0; (operands >>= 4) != 0; i++){ + int type = operands & 15; + const char *position[] = {"one","two","three","four"}; + + if(!parser_next_token(&pc, 0)){ + int j; + for(j=i+1; (operands >>= 4) != 0; j++); + Shutdown_M("%s:%u:%u: error: instruction '%s' wants %s operands but only %s supplied\n", + path[txt], pc.Line, pc.Col, pc.Token, InstructionName, position[j], position[i]); + } + + if(type == o_byte) + bw_write8(&Section[Text], read_constant(&pc, 0, 0x000000FF)); + else if(type == o_dword) + bw_write32(&Section[Text], read_constant(&pc, 0, 0xFFFFFFFF)); + else if(type == o_address) + bw_write32(&Section[Text], read_constant(&pc, CN_LABELONLY, 0xFFFFFFFF)); + else if(type == o_variable) + bw_write8(&Section[Text], read_variable(&pc, Variables, VariableCount)); + else if(type == o_jump){ + /* TODO: Change this */ + bw_write32(&Section[Text], read_constant(&pc, CN_LABELONLY, 0xFFFFFFFF)); + } + } + } + } + + verify_eol(&pc); + } + + if(InBinary) + Shutdown_M("%s:%u:%u: error: expected ']' for end of BINARY section before end of file\n", + path[txt], pc.Line, pc.Col); + + /**** + ** Prepare and write out the ELF object header and all sections + */ + + for(i=48+8; i + Author(s): Fatbag + + Permission to use, copy, modify, and/or distribute this software for any + purpose with or without fee is hereby granted, provided that the above + copyright notice and this permission notice appear in all copies. + + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +*/ + +#include +#include +#include +#include +#include +#include "hitutils.h" + +enum { + hsm, hot, evt, hit, out, filecount +}; + +static __inline const char * find_variable(uint32_t x, const variable_t * Variables, size_t VariableCount){ + size_t i; + for(i=0; iData; + const char * Pattern; + size_t Length; + void * Destination; + enum TokenizeType Type; + + Pattern = va_arg(args, const char *); + if(Pattern == NULL){ + va_end(args); + return 1; + } + + for(Length = strlen(Pattern); ; brc->Data++, brc->Size--){ + if(brc->Size < Length){ + va_end(args); + return 0; + } + if(!memcmp(brc->Data, Pattern, Length)) break; + } + *brc->Data = '\0'; + brc->Data += Length; brc->Size -= Length; + + Destination = va_arg(args, void *); + if(Destination == NULL) + continue; + + Type = va_arg(args, enum TokenizeType); + if(Type == TK_STRING) + *((char**)Destination) = (char*)Start; + else + *((uint32_t*)Destination) = strtoul((char*)Start, NULL, 0); + } +} + +typedef struct { + uint32_t LogicalAddress; + uint32_t TrackID; + uint32_t SoundID; + char * Name; + uint32_t Exported; +} address_t; + +typedef struct { + size_t SizeAllocated; + size_t Count; + address_t * Entries; +} addresslist_t; + +static address_t * add_address(addresslist_t * List){ + if(List->Count*sizeof(address_t) == List->SizeAllocated){ + void * ptr; + if(List->SizeAllocated > SIZE_MAX/2 || !(ptr = realloc(List->Entries, List->SizeAllocated<<=1))) + Shutdown_M("%sCould not allocate memory for address list.\n", "hitdump: Error: "); + List->Entries = ptr; + } + return memset(List->Entries + List->Count++, 0, sizeof(address_t)); +} + +static __inline address_t * find_address_by_track_id(addresslist_t * List, uint32_t TrackID){ + unsigned i; + for(i=0; iCount; i++){ + if(List->Entries[i].TrackID == TrackID) + return List->Entries + i; + } + return NULL; +} + +static __inline address_t * find_address_by_sound_id(addresslist_t * List, uint32_t SoundID){ + unsigned i; + for(i=0; iCount; i++){ + if(List->Entries[i].SoundID == SoundID) + return List->Entries + i; + } + return NULL; +} + +static __inline address_t * find_address_by_logical_address(addresslist_t * List, uint32_t LogicalAddress){ + unsigned i; + for(i=0; iCount; i++){ + if(List->Entries[i].LogicalAddress == LogicalAddress) + return List->Entries + i; + } + return NULL; +} + +static __inline address_t * find_address_by_name(addresslist_t * List, const char * Name){ + unsigned i; + for(i=0; iCount; i++){ + if(List->Entries[i].Name && !strcmp(List->Entries[i].Name, Name)) + return List->Entries + i; + } + return NULL; +} + +static __inline void read_hit_addresses(uint8_t * Data, size_t Size, addresslist_t * AddressList, uint32_t * SymbolTable){ + uint8_t * TableData; + unsigned i, count = 0; + ByteReaderContext brc; + brc.Data = Data; brc.Size = Size; + + if(!parser_find(&brc, "ENTP", NULL, NULL) || brc.Size < 4) return; + TableData = brc.Data; + *SymbolTable = TableData - 4 - Data; + + while(memcmp(Data, "EENT", 4)){ + if(Size < 12) return; + Data+=8; Size-=8; + count++; + } + + for(i=0; iExported = 1; + Address->TrackID = read_uint32(TableData); TableData+=4; + Address->LogicalAddress = read_uint32(TableData); TableData+=4; + } +} + +static __inline void read_evt_addresses(uint8_t * Data, size_t Size, addresslist_t * AddressList){ + ByteReaderContext brc; + brc.Data = Data; brc.Size = Size; + + while(1){ + address_t * Address; + char *Name; + uint32_t TrackID; + if(!parser_find(&brc, + ",", &Name, TK_STRING, + ",", NULL, + ",", &TrackID, TK_ID, + NULL)) return; + + Address = find_address_by_track_id(AddressList, TrackID); + if(!Address){ + Address = add_address(AddressList); + Address->Exported = 1; + Address->TrackID = TrackID; + } + Address->Name = Name; + if(!parser_find(&brc, "\n", NULL, NULL)) return; + } +} + +static __inline void read_hsm_addresses(uint8_t * Data, size_t Size, addresslist_t * AddressList){ + ByteReaderContext brc; + brc.Data = Data; brc.Size = Size; + + while(1){ + address_t * Address; + char * Name; + uint32_t SoundID, LogicalAddress; + if(!parser_find(&brc, + "\ntkd_", NULL, + " ", &Name, TK_STRING, + " ", &SoundID, TK_ID, + " ", NULL, + " ", &LogicalAddress, TK_ID, + NULL)) return; + + Address = find_address_by_logical_address(AddressList, LogicalAddress); + if(!Address){ + Address = find_address_by_name(AddressList, (char*)Name); + if(!Address){ + Address = add_address(AddressList); + Address->Name = (char*)Name; + } + Address->LogicalAddress = LogicalAddress; + } else Address->Name = Name; + Address->SoundID = SoundID; + } +} + +static __inline void read_hot_trackdata(uint8_t * Data, size_t Size, addresslist_t * AddressList){ + ByteReaderContext brc; + brc.Data = Data; brc.Size = Size; + + if(!parser_find(&brc, "[TrackData]", NULL, NULL)) return; + + while(1){ + address_t * Address; + uint32_t SoundID, LogicalAddress; + if(!brc.Size || *brc.Data == '\r' || *brc.Data == '\n' || *brc.Data == '[') return; + if(!parser_find(&brc, + "=", &SoundID, TK_ID, + "\n", &LogicalAddress, TK_ID, + NULL)) return; + + Address = find_address_by_logical_address(AddressList, LogicalAddress); + if(!Address){ + Address = find_address_by_sound_id(AddressList, SoundID); + if(!Address){ + Address = add_address(AddressList); + Address->SoundID = SoundID; + } + Address->LogicalAddress = LogicalAddress; + } else Address->SoundID = SoundID; + } +} + +static __inline void read_hot_track(uint8_t * Data, size_t Size, addresslist_t * AddressList){ + ByteReaderContext brc; + brc.Data = Data; brc.Size = Size; + + if(!parser_find(&brc, "[Track]", NULL, NULL)) return; + + while(1){ + address_t * Address; + char * Name; + uint32_t TrackID; + if(!brc.Size || *brc.Data == '\r' || *brc.Data == '\n' || *brc.Data == '[') return; + if(!parser_find(&brc, + "=", &TrackID, TK_ID, + ",", NULL, + ",", &Name, TK_STRING, + NULL)) return; + + Address = find_address_by_name(AddressList, (char*)Name); + if(!Address){ + Address = find_address_by_track_id(AddressList, TrackID); + if(!Address){ + Address = add_address(AddressList); + Address->TrackID = TrackID; + } + Address->Name = Name; + } else Address->TrackID = TrackID; + Address->Exported = 1; + + if(!parser_find(&brc, "\n", NULL, NULL)) return; + } +} + +static __inline void read_hot_addresses(uint8_t * Data, size_t Size, addresslist_t * AddressList){ + read_hot_trackdata(Data, Size, AddressList); + read_hot_track(Data, Size, AddressList); +} + +static FILE *hFile = NULL; +static char *path[filecount] = {NULL}; +static uint8_t *data[filecount] = {NULL}; +static char *basename = NULL; +static addresslist_t AddressList = {0}; + +static void Shutdown(){ + unsigned i; + for(i=0; i.\n" + "hitutils is maintained by the Niotso project.\n" + "Home page: \n"); + return 0; + } + + for(i=1; i<(unsigned)argc-1; i++){ + if(!strcmp(argv[i], "-ts1")) SimsVersion = VERSION_TS1; + else if(!strcmp(argv[i], "-tso")) SimsVersion = VERSION_TSO; + else if(!strcmp(argv[i], "-f")) overwrite = 1; + else if(!strcmp(argv[i], "-a")) ShowAddresses = 1; + else if(i != (unsigned)argc-2){ + if(!strcmp(argv[i], "-o")) path[out] = argv[++i]; + else if(!strcmp(argv[i], "-hsm")) path[hsm] = argv[++i]; + else if(!strcmp(argv[i], "-hot")) path[hot] = argv[++i]; + else if(!strcmp(argv[i], "-evt")) path[evt] = argv[++i]; + else break; + } + else break; + } + path[hit] = argv[i]; + for(i=0; iName) fprintf(hFile, "%s", Address->Name); + else fprintf(hFile, "%u", LogicalAddress); + } + + if(addr-4 != SymbolTable) + fprintf(hFile, "\r\n"); + fprintf(hFile, "\r\n" + "\t69\r\n" + "\t69\r\n" + "\t78\r\n" + "\t84"); + + if(addr+4 == filesize[hit]) + break; + fprintf(hFile, "\r\n]\r\n\r\nBINARY\r\n["); + + addr += 4; + SymbolTable = 0; + HadSymbolTable++; + } + + Address = find_address_by_logical_address(&AddressList, addr); + if(Address){ + if(!HadSymbolTable && addr != 16 && Address->Exported) + fprintf(hFile, "\r\n]\r\n\r\nBINARY\r\n["); + if(Address->Name) + fprintf(hFile, "\r\n%s", Address->Name); + } + + opcode = data[hit][addr]; + if(opcode == 0 || opcode > InstructionCount) + Shutdown_M("%sIllegal opcode 0x%02X at address 0x%08X.\n", "hitdump: Error: ", opcode, addr); + + instruction = Instructions + opcode - 1; + operands = instruction->Operands; + if(operands == UNIMPLEMENTED) + Shutdown_M("%sUnimplemented instruction '%s' at address 0x%08X.\n", "hitdump: Error: ", instruction->Name, addr); + + addr++; + + if(filesize[hit] - addr < (operands & 15)) + Shutdown_M("%sInsufficient operand bytes for '%s' instruction at address 0x%08X (%u of %u supplied).\n", + "hitdump: Error: ", instruction->Name, addr, filesize[hit] - addr, instruction->Operands); + + fprintf(hFile, "\r\n\t\t%s", instruction->Name); + for(i=0; (operands >>= 4) != 0; i++){ + int type = operands & 15; + const char *position[] = {"first","second","third","fourth"}; + if(type == o_byte){ + fprintf(hFile, " #%u", data[hit][addr]); + addr += 1; + }else if(type == o_dword){ + fprintf(hFile, " #%u", read_uint32(data[hit]+addr)); + addr += 4; + }else if(type == o_address){ + int LogicalAddress = read_uint32(data[hit]+addr); + + Address = find_address_by_logical_address(&AddressList, LogicalAddress); + if(Address && Address->Name) fprintf(hFile, " #%s", Address->Name); + else fprintf(hFile, " #%u", LogicalAddress); + addr += 4; + }else if(type == o_variable){ + unsigned x = data[hit][addr]; + const char * Variable = find_variable(x, Variables, VariableCount); + if(Variable == NULL) + Shutdown_M("%sInvalid %s operand 0x%02X for '%s' instruction at address 0x%08X (expected variable).\n", + "hitdump: Error: ", position[i], x, instruction->Name, addr); + fprintf(hFile, " %s", Variable); + addr += 1; + }else if(type == o_jump){ + unsigned x = 0; + + if(filesize[hit]-addr >= 4) + x = read_uint32(data[hit]+addr); + else if(data[hit][addr] != 0x05 && data[hit][addr] != 0x06) + Shutdown_M("%sInsufficient operand bytes for '%s' instruction at address 0x%08X (%u of %u supplied).\n", + "hitdump: Error: ", instruction->Name, addr, filesize[hit] - addr, 4); + + if(x >= 16 && x < filesize[hit]){ + Address = find_address_by_logical_address(&AddressList, x); + if(Address && Address->Name) fprintf(hFile, " #%s", Address->Name); + else fprintf(hFile, " #%u", x); + addr += 4; + }else{ + const char * Variable; + x = data[hit][addr]; + Variable = find_variable(x, Variables, VariableCount); + if(Variable == NULL) + Shutdown_M("%sInvalid %s operand 0x%02X for '%s' instruction at address 0x%08X (expected variable).\n", + "hitdump: Error: ", position[i], x, instruction->Name, addr); + fprintf(hFile, " %s", Variable); + addr += (data[hit][addr] != 0x05 && data[hit][addr] != 0x06) ? 4 : 1; + } + } + } + } + + fprintf(hFile, "\r\n]\r\n\r\n"); + + Shutdown(); + + return 0; +} \ No newline at end of file diff --git a/library/tools/hitutils/hitld.c b/library/tools/hitutils/hitld.c new file mode 100644 index 0000000..37ac5b5 --- /dev/null +++ b/library/tools/hitutils/hitld.c @@ -0,0 +1,174 @@ +/* + hitutils - The Sims HIT (dis)assembler and linker + hitld.c - Copyright (c) 2012 Niotso Project + Author(s): Fatbag + + Permission to use, copy, modify, and/or distribute this software for any + purpose with or without fee is hereby granted, provided that the above + copyright notice and this permission notice appear in all copies. + + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +*/ + +#include +#include +#include +#include +#include +#include "hitutils.h" + +static const uint8_t ObjectHeader[] = {0x7F, 0x45, 0x4C, 0x46, 0x01, 0x01, 0x01}; +static const uint8_t ArchiveHeader[] = {'!', '<', 'a', 'r', 'c', 'h', '>', '\n'}; + +enum { + hsm, hot, out, filecount +}; + +typedef struct { + char *path; + uint8_t *data; +} object_t; + +typedef struct { + size_t SizeAllocated; + size_t Count; + object_t * Entries; +} objectlist_t; + +static object_t * add_object(objectlist_t * List){ + if(List->Count*sizeof(object_t) == List->SizeAllocated){ + void * ptr; + if(List->SizeAllocated > SIZE_MAX/2 || !(ptr = realloc(List->Entries, List->SizeAllocated<<=1))) + Shutdown_M("%sCould not allocate memory for object list.\n", "hitld: Error: "); + List->Entries = ptr; + } + return memset(List->Entries + List->Count++, 0, sizeof(object_t)); +} + +static FILE *hFile = NULL; +static char *path[filecount] = {NULL}; +static uint8_t *data[filecount] = {NULL}; +static objectlist_t ObjectList = {0}; + +static void Shutdown(){ + unsigned i; + for(i=0; i.\n" + "hitutils is maintained by the Niotso project.\n" + "Home page: \n"); + return 0; + } + + for(i=1; i<(unsigned)argc-1; i++){ + if(!strcmp(argv[i], "-ts1")) SimsVersion = VERSION_TS1; + else if(!strcmp(argv[i], "-tso")) SimsVersion = VERSION_TSO; + else if(!strcmp(argv[i], "-f")) overwrite = 1; + else if(i != (unsigned)argc-2){ + if(!strcmp(argv[i], "-o")) path[out] = argv[++i]; + else if(!strcmp(argv[i], "-hsm")) path[hsm] = argv[++i]; + else if(!strcmp(argv[i], "-hot")) path[hot] = argv[++i]; + else break; + } + else break; + } + ObjectArg = i; + for(i=0; i + Author(s): Fatbag + + Permission to use, copy, modify, and/or distribute this software for any + purpose with or without fee is hereby granted, provided that the above + copyright notice and this permission notice appear in all copies. + + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +*/ + +#ifndef min + #define min(x,y) ((x) < (y) ? (x) : (y)) +#endif +#ifndef max + #define max(x,y) ((x) > (y) ? (x) : (y)) +#endif + +#ifndef read_int32 + #define read_uint32(x) (unsigned)(((x)[0]<<(8*0)) | ((x)[1]<<(8*1)) | ((x)[2]<<(8*2)) | ((x)[3]<<(8*3))) + #define read_uint16(x) (unsigned)(((x)[0]<<(8*0)) | ((x)[1]<<(8*1))) +#endif + +#ifndef write_int32 + #define write_uint32(dest, src) do {\ + (dest)[0] = ((src)&0x000000FF)>>(8*0); \ + (dest)[1] = ((src)&0x0000FF00)>>(8*1); \ + (dest)[2] = ((src)&0x00FF0000)>>(8*2); \ + (dest)[3] = ((src)&0xFF000000)>>(8*3); \ + } while(0) + #define write_uint16(dest, src) do {\ + (dest)[0] = ((src)&0x00FF)>>(8*0); \ + (dest)[1] = ((src)&0xFF00)>>(8*1); \ + } while(0) +#endif + +extern char *strdup (const char *__s); + +static void Shutdown(); + +static void Shutdown_M(const char * Message, ...){ + va_list args; + va_start(args, Message); + vfprintf(stderr, Message, args); + va_end(args); + + Shutdown(); + exit(EXIT_FAILURE); +} + +enum { + VERSION_TS1 = 1, VERSION_TSO +}; + +#define OPERAND_BYTES(x) (x) +#define OPERAND(x, y) ((y)<<((x)*4+4)) +#define UNIMPLEMENTED ((uint32_t)~0) + +enum operand_t { + o_byte = 1, + o_dword, + o_address, + o_variable, + o_jump +}; + +typedef struct { + const char * Name; + uint32_t Operands; +} instruction_t; + +typedef struct { + const char * Name; + uint32_t Value; +} variable_t; + +typedef struct { + uint8_t * Data; + size_t Size; +} ByteReaderContext; + +static const uint8_t HITHeader[] = {'H','I','T','!',0x01,0x00,0x00,0x00,0x08,0x00,0x00,0x00,'T','R','A','X'}; + +#define InstructionCount 96 +static const instruction_t Instructions[] = { + {"note", UNIMPLEMENTED}, + {"note_on", OPERAND_BYTES(1) | OPERAND(0, o_variable)}, + {"note_off", OPERAND_BYTES(1) | OPERAND(0, o_variable)}, + {"loadb", OPERAND_BYTES(2) | OPERAND(0, o_variable) | OPERAND(1, o_byte)}, + {"loadl", OPERAND_BYTES(5) | OPERAND(0, o_variable) | OPERAND(1, o_dword)}, + {"set", OPERAND_BYTES(2) | OPERAND(0, o_variable) | OPERAND(1, o_variable)}, + {"call", OPERAND_BYTES(4) | OPERAND(0, o_address)}, + {"return", OPERAND_BYTES(0)}, + {"wait", OPERAND_BYTES(1) | OPERAND(0, o_variable)}, + {"callentrypoint", UNIMPLEMENTED}, + {"wait_samp", OPERAND_BYTES(0)}, + {"end", OPERAND_BYTES(0)}, + {"jump", OPERAND_BYTES(1) | OPERAND(0, o_jump)}, + {"test", OPERAND_BYTES(1) | OPERAND(0, o_variable)}, + {"nop", OPERAND_BYTES(0)}, + {"add", OPERAND_BYTES(2) | OPERAND(0, o_variable) | OPERAND(1, o_variable)}, + {"sub", OPERAND_BYTES(2) | OPERAND(0, o_variable) | OPERAND(1, o_variable)}, + {"div", OPERAND_BYTES(2) | OPERAND(0, o_variable) | OPERAND(1, o_variable)}, + {"mul", OPERAND_BYTES(2) | OPERAND(0, o_variable) | OPERAND(1, o_variable)}, + {"cmp", OPERAND_BYTES(2) | OPERAND(0, o_variable) | OPERAND(1, o_variable)}, + {"less", UNIMPLEMENTED}, + {"greater", UNIMPLEMENTED}, + {"not", UNIMPLEMENTED}, + {"rand", OPERAND_BYTES(3) | OPERAND(0, o_variable) | OPERAND(1, o_variable) | OPERAND(2, o_variable)}, + {"abs", UNIMPLEMENTED}, + {"limit", UNIMPLEMENTED}, + {"error", UNIMPLEMENTED}, + {"assert", UNIMPLEMENTED}, + {"add_to_group", UNIMPLEMENTED}, + {"remove_from_group", UNIMPLEMENTED}, + {"get_var", UNIMPLEMENTED}, + {"loop", OPERAND_BYTES(0)}, + {"set_loop", OPERAND_BYTES(0)}, + {"callback", UNIMPLEMENTED}, + {"smart_add", UNIMPLEMENTED}, + {"smart_remove", UNIMPLEMENTED}, + {"smart_removeall", UNIMPLEMENTED}, + {"smart_setcrit", UNIMPLEMENTED}, + {"smart_choose", OPERAND_BYTES(1) | OPERAND(0, o_variable)}, + {"and", UNIMPLEMENTED}, + {"nand", UNIMPLEMENTED}, + {"or", UNIMPLEMENTED}, + {"nor", UNIMPLEMENTED}, + {"xor", UNIMPLEMENTED}, + {"max", OPERAND_BYTES(5) | OPERAND(0, o_variable) | OPERAND(1, o_dword)}, + {"min", OPERAND_BYTES(5) | OPERAND(0, o_variable) | OPERAND(1, o_dword)}, + {"inc", OPERAND_BYTES(1) | OPERAND(0, o_variable)}, + {"dec", OPERAND_BYTES(1) | OPERAND(0, o_variable)}, + {"printreg", UNIMPLEMENTED}, + {"play_trk", OPERAND_BYTES(1) | OPERAND(0, o_variable)}, + {"kill_trk", OPERAND_BYTES(1) | OPERAND(0, o_variable)}, + {"push", UNIMPLEMENTED}, + {"push_mask", UNIMPLEMENTED}, + {"push_vars", UNIMPLEMENTED}, + {"call_mask", UNIMPLEMENTED}, + {"call_push", UNIMPLEMENTED}, + {"pop", UNIMPLEMENTED}, + {"test1", OPERAND_BYTES(0)}, + {"test2", OPERAND_BYTES(0)}, + {"test3", OPERAND_BYTES(0)}, + {"test4", OPERAND_BYTES(0)}, + {"ifeq", OPERAND_BYTES(4) | OPERAND(0, o_address)}, + {"ifne", OPERAND_BYTES(4) | OPERAND(0, o_address)}, + {"ifgt", OPERAND_BYTES(4) | OPERAND(0, o_address)}, + {"iflt", OPERAND_BYTES(4) | OPERAND(0, o_address)}, + {"ifge", OPERAND_BYTES(4) | OPERAND(0, o_address)}, + {"ifle", OPERAND_BYTES(4) | OPERAND(0, o_address)}, + {"smart_setlist", OPERAND_BYTES(1) | OPERAND(0, o_variable)}, + {"seqgroup_kill", OPERAND_BYTES(1) | OPERAND(0, o_byte)}, + {"seqgroup_wait", UNIMPLEMENTED}, + {"seqgroup_return", OPERAND_BYTES(1) | OPERAND(0, o_byte)}, + {"getsrcdatafield", OPERAND_BYTES(3) | OPERAND(0, o_variable) | OPERAND(1, o_variable) | OPERAND(2, o_variable)}, + {"seqgroup_trkid", OPERAND_BYTES(2) | OPERAND(0, o_byte) | OPERAND(1, o_byte)}, + {"setll", OPERAND_BYTES(2) | OPERAND(0, o_variable) | OPERAND(1, o_variable)}, + {"setlt", OPERAND_BYTES(2) | OPERAND(0, o_variable) | OPERAND(1, o_variable)}, + {"settl", OPERAND_BYTES(2) | OPERAND(0, o_variable) | OPERAND(1, o_variable)}, + {"waiteq", OPERAND_BYTES(2) | OPERAND(0, o_variable) | OPERAND(1, o_variable)}, + {"waitne", OPERAND_BYTES(2) | OPERAND(0, o_variable) | OPERAND(1, o_variable)}, + {"waitgt", OPERAND_BYTES(2) | OPERAND(0, o_variable) | OPERAND(1, o_variable)}, + {"waitlt", OPERAND_BYTES(2) | OPERAND(0, o_variable) | OPERAND(1, o_variable)}, + {"waitge", OPERAND_BYTES(2) | OPERAND(0, o_variable) | OPERAND(1, o_variable)}, + {"waitle", OPERAND_BYTES(2) | OPERAND(0, o_variable) | OPERAND(1, o_variable)}, + {"duck", OPERAND_BYTES(0)}, + {"unduck", OPERAND_BYTES(0)}, + {"testx", UNIMPLEMENTED}, + {"setlg", OPERAND_BYTES(5) | OPERAND(0, o_variable) | OPERAND(1, o_dword)}, + {"setgl", OPERAND_BYTES(5) | OPERAND(0, o_variable) | OPERAND(1, o_dword)}, + {"throw", UNIMPLEMENTED}, + {"setsrcdatafield", OPERAND_BYTES(3) | OPERAND(0, o_variable) | OPERAND(1, o_variable) | OPERAND(2, o_variable)}, + {"stop_trk", OPERAND_BYTES(1) | OPERAND(0, o_variable)}, + {"setchanreg", UNIMPLEMENTED}, + {"play_note", UNIMPLEMENTED}, + {"stop_note", UNIMPLEMENTED}, + {"kill_note", UNIMPLEMENTED}, + {"smart_index", OPERAND_BYTES(2) | OPERAND(0, o_variable) | OPERAND(1, o_variable)}, + {"note_on_loop", OPERAND_BYTES(1) | OPERAND(0, o_variable)} +}; + +#define TSOVariableCount 82 +static const variable_t TSOVariables[] = { + {"arg0", 0x00}, + {"arg1", 0x01}, + {"arg2", 0x02}, + {"arg3", 0x03}, + {"arg4", 0x04}, + {"v1", 0x05}, + {"v2", 0x06}, + {"v3", 0x07}, + {"v4", 0x08}, + {"v5", 0x09}, + {"v6", 0x0A}, + {"v7", 0x0B}, + {"v8", 0x0C}, + {"h1", 0x0D}, + {"h2", 0x0E}, + {"h3", 0x0F}, + {"argstype", 0x10}, + {"trackdatasource", 0x11}, + {"patch", 0x12}, + {"priority", 0x13}, + {"vol", 0x14}, + {"extvol", 0x15}, + {"pan", 0x16}, + {"pitch", 0x17}, + {"paused", 0x18}, + {"fxtype", 0x19}, + {"fxlevel", 0x1a}, + {"duckpri", 0x1b}, + {"Is3d", 0x1c}, + {"IsHeadRelative", 0x1d}, + {"MinDistance", 0x1e}, + {"MaxDistance", 0x1f}, + {"X", 0x20}, + {"Y", 0x21}, + {"Z", 0x22}, + {"attack", 0x23}, + {"decay", 0x24}, + {"IsStreamed", 0x25}, + {"bufsizemult", 0x26}, + {"fade_dest", 0x27}, + {"fade_var", 0x28}, + {"fade_speed", 0x29}, + {"fade_on", 0x2a}, + {"Preload", 0x2b}, + {"isplaying", 0x2c}, + {"whattodowithupdate", 0x2d}, + {"tempo", 0x2e}, + {"target", 0x2f}, + {"ctrlgroup", 0x30}, + {"interrupt", 0x31}, + {"ispositioned", 0x32}, + {"AppObjectId", 0x34}, + {"callbackarg", 0x35}, + {"pitchrandmin", 0x36}, + {"pitchrandmax", 0x37}, + {"spl", 0x38}, + {"sem", 0x39}, + {"starttrackid", 0x3a}, + {"endtrackid", 0x3b}, + {"startdelay", 0x3c}, + {"fadeinspeed", 0x3d}, + {"fadeoutspeed", 0x3e}, + {"hitlist", 0x3f}, + {"SimSpeed", 0x64}, + {"test_g1", 0x65}, + {"test_g2", 0x66}, + {"test_g3", 0x67}, + {"test_g4", 0x68}, + {"test_g5", 0x69}, + {"test_g6", 0x6a}, + {"test_g7", 0x6b}, + {"test_g8", 0x6c}, + {"test_g9", 0x6d}, + {"main_songnum", 0x6e}, + {"main_musichitlistid", 0x6f}, + {"campfire_nexttrack", 0x70}, + {"campfire_busy", 0x71}, + {"main_duckpri", 0x7b}, + {"main_vol", 0x7c}, + {"main_fxtype", 0x7d}, + {"main_fxlevel", 0x7e}, + {"main_pause", 0x7f}, +}; + +#define TS1VariableCount 87 +static const variable_t TS1Variables[] = { + {"arg0", 0x00}, + {"arg1", 0x01}, + {"arg2", 0x02}, + {"arg3", 0x03}, + {"arg4", 0x04}, + {"v1", 0x05}, + {"v2", 0x06}, + {"v3", 0x07}, + {"v4", 0x08}, + {"v5", 0x09}, + {"v6", 0x0a}, + {"v7", 0x0b}, + {"v8", 0x0c}, + {"h1", 0x0d}, + {"h2", 0x0e}, + {"h3", 0x0f}, + {"priority", 0x11}, + {"vol", 0x12}, + {"extvol", 0x13}, + {"pan", 0x14}, + {"pitch", 0x15}, + {"paused", 0x16}, + {"fxtype", 0x17}, + {"fxlevel", 0x18}, + {"duckpri", 0x19}, + {"Is3d", 0x1a}, + {"IsHeadRelative", 0x1b}, + {"MinDistance", 0x1c}, + {"MaxDistance", 0x1d}, + {"X", 0x1e}, + {"Y", 0x1f}, + {"Z", 0x20}, + {"filter_type", 0x21}, + {"filter_cutoff", 0x22}, + {"filter_level", 0x23}, + {"attack", 0x24}, + {"decay", 0x25}, + {"IsStreamed", 0x26}, + {"BufSizeMult", 0x27}, + {"fade_dest", 0x28}, + {"fade_var", 0x29}, + {"fade_speed", 0x2a}, + {"Preload", 0x2b}, + {"IsLooped", 0x2c}, + {"fade_on", 0x2d}, + {"isplaying", 0x2e}, + {"source", 0x2f}, + {"patch", 0x32}, + {"WhatToDoWithUpdate", 0x33}, + {"tempo", 0x34}, + {"target", 0x35}, + {"mutegroup", 0x36}, + {"interrupt", 0x37}, + {"IsPositioned", 0x38}, + {"Spl", 0x39}, + {"MultipleInstances", 0x3a}, + {"AssociatedTrack1", 0x3b}, + {"AssociatedTrack2", 0x3c}, + {"AssociatedTrack3", 0x3d}, + {"AssociatedTrack4", 0x3e}, + {"AssociatedTrack5", 0x3f}, + {"AssociatedTrack6", 0x40}, + {"AssociatedTrack7", 0x41}, + {"AssociatedTrack8", 0x42}, + {"SimSpeed", 0x64}, + {"test_g1", 0x65}, + {"test_g2", 0x66}, + {"test_g3", 0x67}, + {"test_g4", 0x68}, + {"test_g5", 0x69}, + {"test_g6", 0x6a}, + {"test_g7", 0x6b}, + {"test_g8", 0x6c}, + {"test_g9", 0x6d}, + {"main_songnum", 0x6e}, + {"main_musichitlistid", 0x6f}, + {"campfire_nexttrack", 0x70}, + {"campfire_busy", 0x71}, + {"main_duckpri", 0x7b}, + {"main_vol", 0x7c}, + {"main_fxtype", 0x7d}, + {"main_fxlevel", 0x7e}, + {"main_pause", 0x7f}, +}; + +#define ConstantCount 72 +static const variable_t Constants[] = { + {"duckpri_always", 0x0}, + {"duckpri_low", 0xa}, + {"duckpri_normal", 0x14}, + {"duckpri_high", 0x1e}, + {"duckpri_higher", 0x28}, + {"duckpri_evenhigher", 0x32}, + {"duckpri_never", 0x64}, + {"spl_infinite", 0x0}, + {"spl_loud", 0xa}, + {"spl_normal", 0x14}, + {"spl_quiet", 0x64}, + {"Instance", 0x0}, + {"Gender", 0x1}, + {"GroupMusic", 0x1}, + {"GroupDialogMain", 0x2}, + {"GroupDialogOverlay", 0x3}, + {"PchDishwasherClose", 0x32}, + {"PchDishwasherLoad", 0x33}, + {"PchDishwasherLoad2", 0x34}, + {"PchDishwasherOpen", 0x35}, + {"PchDishwasherTurnDial", 0x39}, + {"PchDishwasherTurnDial2", 0x3a}, + {"TrkRadioStationCountry", 0x104}, + {"TrkRadioStationBossaNova", 0x10e}, + {"TrkRadioStationClassical", 0x10d}, + {"TrkRadioStationRock", 0x118}, + {"kSndobPlay", 1}, + {"kSndobStop", 2}, + {"kSndobKill", 3}, + {"kSndobUpdate", 4}, + {"kSndobSetVolume", 5}, + {"kSndobSetPitch", 6}, + {"kSndobSetPan", 7}, + {"kSndobSetPosition", 8}, + {"kSndobSetFxType", 9}, + {"kSndobSetFxLevel", 10}, + {"kSndobPause", 11}, + {"kSndobUnpause", 12}, + {"kSndobLoad", 13}, + {"kSndobUnload", 14}, + {"kSndobCache", 15}, + {"kSndobUncache", 16}, + {"kSndobCancelNote", 19}, + {"kPlayPiano", 43}, + {"kSetMusicMode", 36}, + {"kLive", 0}, + {"kBuy", 1}, + {"kBuild", 2}, + {"kHood", 3}, + {"kFrontEnd", 4}, + {"kGroupSfx", 1}, + {"kGroupMusic", 2}, + {"kGroupVox", 3}, + {"kAction", 1000}, + {"kComedy", 1001}, + {"kRomance", 1002}, + {"kNews", 1003}, + {"kCountry", 1004}, + {"kRock", 1005}, + {"kJazz", 1006}, + {"kClassical", 1007}, + {"kArgsNormal", 0}, + {"kArgsVolPan", 1}, + {"kArgsIdVolPan", 2}, + {"kArgsXYZ", 3}, + {"kKillAll", 20}, + {"kPause", 21}, + {"kUnpause", 22}, + {"kKillInstance", 23}, + {"kTurnOnTv", 30}, + {"kTurnOffTv", 31}, + {"kUpdateSourceVolPan", 32}, +}; + +static const char SHStringTable[] = + "\0.text" + "\0.shstrtab" + "\0.symtab" + "\0.strtab" + "\0.rel.text" +; + +static const uint8_t SymbolTableHeader[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0xF1, 0xFF, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x01, 0x00 +}; \ No newline at end of file diff --git a/library/tools/iff2html/CMakeLists.txt b/library/tools/iff2html/CMakeLists.txt new file mode 100644 index 0000000..bce052c --- /dev/null +++ b/library/tools/iff2html/CMakeLists.txt @@ -0,0 +1,19 @@ +cmake_minimum_required(VERSION 2.6) +project(iff2html) + +include_directories(${LIBPNG_INCLUDE}) +include_directories(${ZLIB_INCLUDE}) + +include_directories( "${CMAKE_SOURCE_DIR}/formats") + +set(IFF2HTML_SOURCES + iff2html.c + md5.c + image.c + opngreduc.c + "${CMAKE_SOURCE_DIR}/formats/iff/iff.h" + "${CMAKE_SOURCE_DIR}/formats/bmp/read_bmp.c" +) + +add_executable(iff2html ${IFF2HTML_SOURCES}) +target_link_libraries(iff2html iff_static FileHandler_static ${LIBPNG_LINK} ${ZLIB_LINK}) diff --git a/library/tools/iff2html/iff2html.c b/library/tools/iff2html/iff2html.c new file mode 100644 index 0000000..768b7e1 --- /dev/null +++ b/library/tools/iff2html/iff2html.c @@ -0,0 +1,713 @@ +/* + iff2html - iff web page description generator + iff2html.c - Copyright (c) 2012 Niotso Project + Author(s): Fatbag + Ahmed El-Mahdawy + + Permission to use, copy, modify, and/or distribute this software for any + purpose with or without fee is hereby granted, provided that the above + copyright notice and this permission notice appear in all copies. + + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +*/ + +#include +#include +#include +#include +#include "md5.h" +#include "image.h" + +#ifndef min + #define min(x,y) ((x) < (y) ? (x) : (y)) +#endif +#ifndef max + #define max(x,y) ((x) > (y) ? (x) : (y)) +#endif + +static void printsize(FILE * hFile, size_t FileSize){ + /* For our purposes, our units are best described in kB and MB, if not bytes */ + size_t temp = FileSize; + unsigned position = 1; + if(FileSize >= 1048576) + fprintf(hFile, "%.1f MB (", (float)FileSize/1048576); + else + fprintf(hFile, "%.1f kB (", (float)FileSize/1024); + while((temp/=1000) != 0) + position *= 1000; + fprintf(hFile, "%u", (unsigned) FileSize/position); + FileSize -= (FileSize/position)*position; + while((position/=1000) != 0){ + fprintf(hFile, ",%.3u", (unsigned) FileSize/position); + FileSize -= (FileSize/position)*position; + } + fprintf(hFile, " bytes)"); +} + +int main(int argc, char *argv[]){ + unsigned c; + int slash; + FILE * hFile; + int overwrite = 0; + char *InFile, *OutFile = NULL, *FileName, *OutDir = NULL; + size_t FileSize; + struct MD5Context md5c; + unsigned char digest[16]; + uint8_t * IFFData; + IFFFile IFFFileInfo; + IFFChunk * ChunkData; + + if(argc == 1 || !strcmp(argv[1], "-h") || !strcmp(argv[1], "--help")){ + printf("Usage: iff2html [-f] infile (outfile)\n" + "Produce an HTML webpage describing an EA IFF file.\n" + "Use -f to force overwriting without confirmation.\n" + "If outfile is unspecified, file.iff will output to file.html.\n" + "\n" + "Report bugs to .\n" + "iff2html is maintained by the Niotso project.\n" + "Home page: \n"); + return 0; + } + + if(argc >= 4 && !strcmp(argv[1], "-f")){ + overwrite++; + InFile = argv[2]; + OutFile = argv[3]; + }else if(argc == 3){ + if(!strcmp(argv[1], "-f")){ + overwrite++; + InFile = argv[2]; + }else{ + InFile = argv[1]; + OutFile = argv[2]; + } + }else InFile = argv[1]; + if(OutFile == NULL){ + unsigned length = strlen(InFile); + OutFile = malloc(max(length+2, 6)); + strcpy(OutFile, InFile); + strcpy(max(OutFile+length-4, OutFile), ".html"); + } + + for(c=0, slash=-1; OutFile[c]; c++) + if(OutFile[c] == '/' || OutFile[c] == '\\') slash = c; + if(slash >= 0){ + OutDir = malloc(slash+2); + memcpy(OutDir, OutFile, slash+1); + OutDir[slash+1] = 0x00; + }else OutDir = ""; + + for(c=0, slash=-1; InFile[c]; c++) + if(InFile[c] == '/' || InFile[c] == '\\') slash = c; + FileName = InFile + slash + 1; + + /**** + ** Open the file and read in entire contents to memory + */ + + hFile = fopen(InFile, "rb"); + if(hFile == NULL){ + printf("%sThe specified input file does not exist or could not be opened for reading.", "iff2html: error: "); + return -1; + } + fseek(hFile, 0, SEEK_END); + FileSize = ftell(hFile); + if(FileSize < 64){ + fclose(hFile); + printf("%sNot a valid IFF file.", "iff2html: error: "); + return -1; + } + fseek(hFile, 0, SEEK_SET); + + IFFData = malloc(FileSize); + if(IFFData == NULL){ + fclose(hFile); + printf("%sMemory for this file could not be allocated.", "iff2html: error: "); + return -1; + } + if(fread(IFFData, 1, FileSize, hFile) != FileSize){ + fclose(hFile); + printf("%sThe input file could not be read.", "iff2html: error: "); + return -1; + } + fclose(hFile); + + /**** + ** Load header information + */ + + if(!iff_create(&IFFFileInfo)){ + printf("%sMemory for this file could not be allocated.", "iff2html: error: "); + return -1; + } + if(!iff_read_header(&IFFFileInfo, IFFData, FileSize)){ + printf("%sNot a valid IFF file.", "iff2html: error: "); + return -1; + } + + /**** + ** Load entry information + */ + + if(!iff_enumerate_chunks(&IFFFileInfo, IFFData+64, FileSize-64)){ + printf("%sChunk data is corrupt.", "iff2html: error: "); + return -1; + } + + /* Calculate the MD5, and then we can free the IFF data because we're done with it */ + MD5Init(&md5c); + MD5Update(&md5c, IFFData, FileSize); + MD5Final(digest, &md5c); + free(IFFData); + + for(c = 0, ChunkData = IFFFileInfo.Chunks; c < IFFFileInfo.ChunkCount; c++, ChunkData++) + iff_parse_chunk(ChunkData, ChunkData->Data); + + /**** + ** Open the output file and write the header + */ + if(!overwrite){ + hFile = fopen(OutFile, "rb"); + if(hFile != NULL){ + /* File exists */ + char c; + fclose(hFile); + printf("File \"%s\" exists.\nContinue anyway? (y/n) ", OutFile); + c = getchar(); + if(c != 'y' && c != 'Y'){ + printf("\nAborted."); + return -1; + } + } + } + hFile = fopen(OutFile, "wb"); + if(hFile == NULL){ + printf("%sThe output file could not be opened for writing.", "iff2html: error: "); + return -1; + } + + /**** + ** We're splitting fprintf by line to guarantee compatibility; + ** even C99 compilers are only required to support 4096 byte strings in printf()-related functions + */ + fprintf(hFile, + "\n"); + fprintf(hFile, "\n"); + fprintf(hFile, "\n"); + fprintf(hFile, "\n"); + fprintf(hFile, "\n"); + fprintf(hFile, "\n"); + fprintf(hFile, "\n", FileName); + fprintf(hFile, "\n"); + fprintf(hFile, "%s (iff2html)\n", FileName); + fprintf(hFile, "\n"); + fprintf(hFile, "\n"); + fprintf(hFile, "\n"); + fprintf(hFile, "

%s

\n", FileName); + fprintf(hFile, "
\n"); + fprintf(hFile, "
"); + for(c=0; c<16; c++) + fprintf(hFile, "%.2x", digest[c]); + fprintf(hFile, " (md5), "); + printsize(hFile, FileSize); + fprintf(hFile, "
\n"); + fprintf(hFile, "
Dumped by iff2html.
\n"); + fprintf(hFile, "\n"); + fprintf(hFile, "
Contents – %u chunks
\n", IFFFileInfo.ChunkCount); + fprintf(hFile, "
    \n"); + for(c=1, ChunkData = IFFFileInfo.Chunks; c <= IFFFileInfo.ChunkCount; c++, ChunkData++) + fprintf(hFile, "
  • %u [%s] (%.4X)%s%s
  • \n", + c, ChunkData->ChunkID, c, ChunkData->Type, ChunkData->ChunkID, + (ChunkData->Label[0] != 0x00) ? " – " : "", ChunkData->Label); + fprintf(hFile, "
\n"); + fprintf(hFile, "
\n"); + fprintf(hFile, "\n"); + + for(c=0, ChunkData = IFFFileInfo.Chunks; c < IFFFileInfo.ChunkCount; c++, ChunkData++){ + fprintf(hFile, "

%u [%s] (%.4X)%s%s (Jump)

\n", + c+1, ChunkData->ChunkID, c+1, ChunkData->Type, ChunkData->ChunkID, + (ChunkData->Label[0] != 0x00) ? " – " : "", ChunkData->Label, + c+1, ChunkData->ChunkID); + fprintf(hFile, "
\n"); + + if(ChunkData->FormattedData == NULL){ + int success = 0; + /* The iff library does not parse BMP_ or FBMP chunks */ + if(!strcmp(ChunkData->Type, "BMP_") || !strcmp(ChunkData->Type, "FBMP")){ + int bmp = !strcmp(ChunkData->Type, "BMP_"); + size_t Width, Height; + char filename[32]; + sprintf(filename, "%s%s_%u_%.4x.png", OutDir, bmp ? "bmp" : "fbmp", c+1, ChunkData->ChunkID); + + if(WritePNG(filename, ChunkData, 0, NULL, &Width, &Height)){ + fprintf(hFile, "\n"); + fprintf(hFile, "\n"); + fprintf(hFile, "\n", + bmp ? "bmp" : "fbmp", c+1, ChunkData->ChunkID, (unsigned) Width, (unsigned) Height); + fprintf(hFile, "
Image
\"\"
\n"); + success++; + } + } + if(!success) + fprintf(hFile, "The contents of this chunk could not be parsed.\n"); + }else if(!strcmp(ChunkData->Type, "STR#") || + !strcmp(ChunkData->Type, "CTSS") || + !strcmp(ChunkData->Type, "FAMs") || + !strcmp(ChunkData->Type, "TTAs") || + !strcmp(ChunkData->Type, "CST") ){ + /**** + ** STR# parsing + */ + + IFFString * StringData = ChunkData->FormattedData; + fprintf(hFile, "\n"); + fprintf(hFile, "\n"); + fprintf(hFile, "
Format:"); + switch(StringData->Format){ + case 0: fprintf(hFile, "00 00 (0)"); break; + case -1: fprintf(hFile, "FF FF (−1)"); break; + case -2: fprintf(hFile, "FE FF (−2)"); break; + case -3: fprintf(hFile, "FD FF (−3)"); break; + case -4: fprintf(hFile, "FC FF (−4)"); break; + default: fprintf(hFile, "Unrecognized"); break; + } + fprintf(hFile, "
\n"); + if(StringData->Format >= -4 && StringData->Format <= 0){ + unsigned LanguageSet; + const char * LanguageStrings[] = { + "English (US)", + "English (International)", + "French", + "German", + "Italian", + "Spanish", + "Dutch", + "Danish", + "Swedish", + "Norwegian", + "Finnish", + "Hebrew", + "Russian", + "Portuguese", + "Japanese", + "Polish", + "Simplified Chinese", + "Traditional Chinese", + "Thai", + "Korean" + }; + fprintf(hFile, "
\n"); + fprintf(hFile, "\n"); + fprintf(hFile, "\n"); + + for(LanguageSet=0; LanguageSet<20; LanguageSet++){ + IFFStringPair * Pair; + unsigned PairIndex; + if(StringData->LanguageSets[LanguageSet].PairCount == 0) + continue; + + fprintf(hFile, "\n", StringData->LanguageSets[LanguageSet].PairCount, + LanguageStrings[LanguageSet]); + for(PairIndex=1, Pair = StringData->LanguageSets[LanguageSet].Pairs; + PairIndex <= StringData->LanguageSets[LanguageSet].PairCount; PairIndex++, Pair++){ + if(PairIndex != 1) + fprintf(hFile, ""); + fprintf(hFile, "\n", PairIndex, + (Pair->Key) != NULL ? Pair->Key : "", + (Pair->Value) != NULL ? Pair->Value : ""); + } + } + + fprintf(hFile, "
LanguageString pairs
%s
%u%s%s
\n"); + } + }else if(!strcmp(ChunkData->Type, "CATS")){ + /**** + ** Regular string pair + */ + + IFFStringPair * Pair = ChunkData->FormattedData; + + fprintf(hFile, "\n"); + fprintf(hFile, "\n"); + fprintf(hFile, "\n", + (Pair->Key) != NULL ? Pair->Key : "", + (Pair->Value) != NULL ? Pair->Value : ""); + fprintf(hFile, "
KeyValue
%s%s
\n"); + }else if(!strcmp(ChunkData->Type, "FWAV") || !strcmp(ChunkData->Type, "GLOB")){ + /**** + ** Regular string + */ + + fprintf(hFile, "\n"); + fprintf(hFile, "\n"); + fprintf(hFile, "\n", ChunkData->FormattedData ? (char*) ChunkData->FormattedData : ""); + fprintf(hFile, "
String
%s
\n"); + }else if(!strcmp(ChunkData->Type, "BCON")){ + /**** + ** BCON parsing + */ + + IFF_BCON * BCONData = ChunkData->FormattedData; + fprintf(hFile, "\n"); + fprintf(hFile, "\n", BCONData->Flags, BCONData->Flags); + fprintf(hFile, "
Flags:%02X (%u)
\n"); + if(BCONData->ConstantCount > 0){ + unsigned i; + + fprintf(hFile, "
\n"); + fprintf(hFile, "\n"); + fprintf(hFile, "\n"); + for(i=0; iConstantCount; i++) + fprintf(hFile, "\n", i+1, BCONData->Constants[i]); + fprintf(hFile, "
Constant Value
%u%u
\n"); + } + }else if(!strcmp(ChunkData->Type, "FCNS")){ + /**** + ** FCNS parsing + */ + + IFFConstantList * List = ChunkData->FormattedData; + fprintf(hFile, "\n"); + fprintf(hFile, "\n", List->Version); + fprintf(hFile, "
Version:%u
\n"); + if(List->ConstantCount > 0){ + IFFConstant * Constant; + unsigned i; + + fprintf(hFile, "
\n"); + fprintf(hFile, "\n"); + fprintf(hFile, "\n"); + for(i=0, Constant=List->Constants; iConstantCount; i++, Constant++) + fprintf(hFile, "\n", + i+1, + Constant->Name ? Constant->Name : "", + Constant->Value, + Constant->Description ? Constant->Description : ""); + fprintf(hFile, "
NameValueDescription
%u%s%g%s
\n"); + } + }else if(!strcmp(ChunkData->Type, "TMPL")){ + /**** + ** TMPL parsing + */ + + IFFTemplate * Template = ChunkData->FormattedData; + IFFTemplateField * Field; + unsigned i; + fprintf(hFile, "\n"); + fprintf(hFile, "\n"); + for(i=0, Field=Template->Fields; iFieldCount; i++, Field++) + fprintf(hFile, "\n", + i+1, + Field->Name ? Field->Name : "", + Field->Type ? Field->Type : ""); + fprintf(hFile, "
NameType
%u%s%s
\n"); + }else if(!strcmp(ChunkData->Type, "TRCN")){ + /**** + ** TRCN parsing + */ + + IFFRangeSet * RangeSet = ChunkData->FormattedData; + fprintf(hFile, "\n"); + fprintf(hFile, "\n", RangeSet->Version); + fprintf(hFile, "
Version:%u
\n"); + if(RangeSet->RangeCount > 0){ + unsigned i; + IFFRangeEntry * Range; + + fprintf(hFile, "
\n"); + fprintf(hFile, "\n"); + fprintf(hFile, "" + "\n"); + for(i=0, Range=RangeSet->Ranges; iRangeCount; i++, Range++) + fprintf(hFile, + "\n", + i+1, + Range->IsUsed ? "Yes" : "No", Range->DefaultValue, + Range->Name ? Range->Name : "", + Range->Comment ? Range->Comment : "", + Range->Enforced ? "Yes" : "No", + Range->RangeMin, Range->RangeMax); + fprintf(hFile, "
In useDefault valueNameCommentRange is enforcedMinimumMaximum
%u%s%u%s%s%s%u%u
\n"); + } + }else if(!strcmp(ChunkData->Type, "PALT")){ + /**** + ** PALT parsing + */ + + IFFPalette * Palette = ChunkData->FormattedData; + uint8_t * Data = Palette->Data; + unsigned i, j; + + fprintf(hFile, "\n"); + fprintf(hFile, ""); + for(i=0; i<16; i++) fprintf(hFile, "", i); + fprintf(hFile, "\n"); + for(i=0; i<16; i++){ + fprintf(hFile, "", i); + for(j=0; j<16; j++){ + if(i*16 + j < Palette->ColorCount){ + unsigned red = *(Data++); + unsigned green = *(Data++); + unsigned blue = *(Data++); + + fprintf(hFile, "\n", + red, green, blue, i*16 + j, red, green, blue); + }else + fprintf(hFile, "\n"); + } + fprintf(hFile, "\n"); + } + fprintf(hFile, "
%X
%X
\n"); + }else if(!strcmp(ChunkData->Type, "SPR#") || !strcmp(ChunkData->Type, "SPR2")){ + /**** + ** SPR# and SPR2 parsing + */ + + int spr1 = !strcmp(ChunkData->Type, "SPR#"); + IFFSpriteList * SpriteList = ChunkData->FormattedData; + IFFChunk * Palette = NULL; + IFFPalette BlankPalette; + IFFPalette * PaletteData; + unsigned i; + + fprintf(hFile, "\n"); + fprintf(hFile, "\n", SpriteList->Version); + fprintf(hFile, "\n", SpriteList->PaletteID); + fprintf(hFile, "
Version:%u
Palette ID:%.4X
\n"); + + if(SpriteList->PaletteID < 0xFFFF){ + Palette = iff_find_chunk(&IFFFileInfo, "PALT", SpriteList->PaletteID); + if(!Palette || !Palette->FormattedData) Palette = iff_find_chunk(&IFFFileInfo, "PALT", ChunkData->ChunkID); + if(!Palette || !Palette->FormattedData) Palette = iff_find_chunk(&IFFFileInfo, "PALT", -1); + } + if(!Palette || !Palette->FormattedData){ + memset(&BlankPalette, 0, sizeof(IFFPalette)); + BlankPalette.Version = 1; + BlankPalette.ColorCount = 256; + PaletteData = &BlankPalette; + }else PaletteData = Palette->FormattedData; + + fprintf(hFile, "\n"); + fprintf(hFile, ""); + if(!spr1) fprintf(hFile, ""); + fprintf(hFile, "\n"); + for(i=0; iSpriteCount; i++){ + IFFSprite * Sprite = &SpriteList->Sprites[i]; + char filename[32]; + sprintf(filename, "%s%s_%u_%.4x_%u.png", OutDir, spr1 ? "spr1" : "spr2", c+1, ChunkData->ChunkID, i+1); + + fprintf(hFile, "IndexData && iff_depalette(Sprite, PaletteData)){ + WritePNG(filename, NULL, 0, Sprite, NULL, NULL); + fprintf(hFile, ">\"\"", + spr1 ? "spr1" : "spr2", c+1, ChunkData->ChunkID, i+1, Sprite->Width, Sprite->Height); + if(!spr1){ + sprintf(filename, "%sspr2_%u_%.4x_%u_z.png", OutDir, c+1, ChunkData->ChunkID, i+1); + if(Sprite->ZBuffer){ + WritePNG(filename, NULL, 1, Sprite, NULL, NULL); + fprintf(hFile, "\n"); + } + fprintf(hFile, "
SpriteZ-Buffer
%u\"\"", + c+1, ChunkData->ChunkID, i+1, Sprite->Width, Sprite->Height); + }else + fprintf(hFile, "None provided"); + } + }else + fprintf(hFile, Sprite->InvalidDimensions ? "%sBlank sprite" : "%sThis sprite cannot be displayed.", + !spr1 ? " colspan=\"2\">" : ">"); + fprintf(hFile, "
\n"); + }else if(!strcmp(ChunkData->Type, "DGRP")){ + /**** + ** DGRP parsing + */ + + IFFDrawGroup * Group = ChunkData->FormattedData; + IFFDrawAngle * Angle; + IFFSpriteInfo * Sprite; + unsigned i,j; + const char * Zooms[] = {"Far", "Middle", "Close"}; + + fprintf(hFile, "\n"); + fprintf(hFile, "\n", Group->Version); + fprintf(hFile, "
Version:%u
\n"); + fprintf(hFile, "
\n"); + + fprintf(hFile, "\n"); + fprintf(hFile, "\n"); + for(i=0, Angle=Group->DrawAngles; i<12; i++, Angle++){ + const char * Direction = + (Angle->Direction == IFFDIRECTION_NORTHEAST) ? "North east" : + (Angle->Direction == IFFDIRECTION_SOUTHEAST) ? "South east" : + (Angle->Direction == IFFDIRECTION_NORTHWEST) ? "North west" : + "South west"; + + if(Angle->SpriteCount){ + fprintf(hFile, + "" + "" + "\n", + 1+Angle->SpriteCount, Direction, 1+Angle->SpriteCount, Zooms[Angle->Zoom-1]); + for(j=0, Sprite = Angle->SpriteInfo; jSpriteCount; j++, Sprite++) + fprintf(hFile, "" + "", + j+1, Sprite->Type, Sprite->ChunkID, Sprite->SpriteIndex, Sprite->Flags, + Sprite->SpriteX, Sprite->SpriteY, Sprite->ObjectX, Sprite->ObjectY, Sprite->ObjectZ); + + }else{ + fprintf(hFile, "", Direction, Zooms[Angle->Zoom-1]); + } + } + fprintf(hFile, "
DirectionZoomSprite
%s%s#TypeChunk IDSprite indexFlagsSprite offsetObject offset
%u%u%.4X%u%u(%+d,%+d)(%+g,%+g,%+g)
%s%sNone specified
\n"); + }else if(!strcmp(ChunkData->Type, "BHAV")){ + /**** + ** BHAV parsing + */ + + IFFBehavior * Behavior = ChunkData->FormattedData; + IFFInstruction * Instruction; + + fprintf(hFile, "\n"); + fprintf(hFile, "\n", Behavior->Version); + fprintf(hFile, "\n", Behavior->Type); + fprintf(hFile, "\n", Behavior->ArgumentCount); + fprintf(hFile, "\n", Behavior->LocalCount); + fprintf(hFile, "\n", Behavior->Flags); + fprintf(hFile, "
Version:%u
Type:%u
Arguments:%u
Locals:%u
Flags:%.4X
\n"); + + if(Behavior->InstructionCount > 0){ + unsigned i; + + fprintf(hFile, "
\n"); + fprintf(hFile, "\n"); + fprintf(hFile, "\n"); + for(i=0, Instruction = Behavior->Instructions; iInstructionCount; i++, Instruction++) + fprintf(hFile, "" + "\n", + i, Instruction->Opcode, Instruction->TDest, Instruction->FDest, + Instruction->Operands[0], Instruction->Operands[1], + Instruction->Operands[2], Instruction->Operands[3], + Instruction->Operands[4], Instruction->Operands[5], + Instruction->Operands[6], Instruction->Operands[7]); + fprintf(hFile, "
OpcodeT-DestF-DestOperand data
%u%.4X%u%u%.2X %.2X %.2X %.2X %.2X %.2X %.2X %.2X
\n"); + } + }else if(!strcmp(ChunkData->Type, "OBJf")){ + /**** + ** OBJf parsing + */ + + IFFFunctionTable * Table = ChunkData->FormattedData; + fprintf(hFile, "\n"); + fprintf(hFile, "\n", Table->Version); + fprintf(hFile, "
Version:%u
\n"); + + if(Table->FunctionCount > 0){ + unsigned i; + + fprintf(hFile, "
\n"); + fprintf(hFile, "\n"); + fprintf(hFile, "\n"); + for(i=0; iFunctionCount; i++) + fprintf(hFile, + "\n", + i+1, Table->Functions[i].ConditionID, Table->Functions[i].ActionID); + fprintf(hFile, "
Condition functionAction function
%u%.4X%.4X
\n"); + } + }else{ + fprintf(hFile, "The contents of this chunk cannot be shown on this page.\n"); + } + + fprintf(hFile, "
\n\n"); + } + iff_delete(&IFFFileInfo); + + fprintf(hFile, + "
This page was generated by the use of iff2html.\n"); + fprintf(hFile, "The content of this page may be subject to copyright by the author(s) of the original iff file.
\n"); + fprintf(hFile, "\n"); + fprintf(hFile, ""); + fclose(hFile); + + printf("Wrote contents to '%s'.\n", OutFile); + return 0; +} diff --git a/library/tools/iff2html/image.c b/library/tools/iff2html/image.c new file mode 100644 index 0000000..6991658 --- /dev/null +++ b/library/tools/iff2html/image.c @@ -0,0 +1,149 @@ +/* + iff2html - iff web page description generator + image.c - Copyright (c) 2012 Niotso Project + Author(s): Fatbag + + Permission to use, copy, modify, and/or distribute this software for any + purpose with or without fee is hereby granted, provided that the above + copyright notice and this permission notice appear in all copies. + + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +*/ + +#include +#include +#include +#include +#include /* Used for libpng */ +#include "opngreduc.h" + +int WritePNG(const char * OutName, const IFFChunk * ChunkData, int ZBuffer, + const IFFSprite * Sprite, size_t * Width, size_t * Height){ + FILE * hFile; + png_structp png_ptr; + png_infop info_ptr; + png_bytep * row_pointers; + unsigned i; + + struct { + size_t Width; + size_t Height; + uint8_t * Data; + } Image; + + /* We must swap from BGR to RGB; this cannot be done with libpng when you use + ** opng_reduce_image due to the state that it leaves png_ptr in */ + + if(ChunkData){ + /* BMP_ or FBMP chunk */ + bmpheader_t BMPHeader; + + if(!bmp_read_header(&BMPHeader, ChunkData->Data, ChunkData->Size)) + return 0; + + Image.Data = malloc(BMPHeader.DecompressedSize); + if(Image.Data == NULL) + return 0; + if(!bmp_read_data(&BMPHeader, ChunkData->Data, Image.Data)){ + free(Image.Data); + return 0; + } + + Image.Width = BMPHeader.biWidth; + Image.Height = BMPHeader.biHeight; + + for(i=0; iWidth; + Image.Height = Sprite->Height; + Image.Data = (!ZBuffer) ? Sprite->BGRA32Data : Sprite->ZBuffer; + + if(!ZBuffer){ + for(i=0; i + Author(s): Fatbag + + Permission to use, copy, modify, and/or distribute this software for any + purpose with or without fee is hereby granted, provided that the above + copyright notice and this permission notice appear in all copies. + + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +*/ + +int WritePNG(const char * OutName, const IFFChunk * ChunkData, int ZBuffer, + const IFFSprite * Sprite, size_t * Width, size_t * Height); \ No newline at end of file diff --git a/library/tools/iff2html/md5.c b/library/tools/iff2html/md5.c new file mode 100644 index 0000000..a0a7335 --- /dev/null +++ b/library/tools/iff2html/md5.c @@ -0,0 +1,255 @@ +/* + * This code implements the MD5 message-digest algorithm. + * The algorithm is due to Ron Rivest. This code was + * written by Colin Plumb in 1993, no copyright is claimed. + * This code is in the public domain; do with it what you wish. + * + * Equivalent code is available from RSA Data Security, Inc. + * This code has been tested against that, and is equivalent, + * except that you don't need to include two pages of legalese + * with every copy. + * + * To compute the message digest of a chunk of bytes, declare an + * MD5Context structure, pass it to MD5Init, call MD5Update as + * needed on buffers full of bytes, and then call MD5Final, which + * will fill a supplied 16-byte array with the digest. + */ + +/* Brutally hacked by John Walker back from ANSI C to K&R (no + prototypes) to maintain the tradition that Netfone will compile + with Sun's original "cc". */ + +#include /* for memcpy() */ +#include "md5.h" + +#ifndef HIGHFIRST +#define byteReverse(buf, len) /* Nothing */ +#else +/* + * Note: this code is harmless on little-endian machines. + */ +void byteReverse(buf, longs) + unsigned char *buf; unsigned longs; +{ + uint32 t; + do { + t = (uint32) ((unsigned) buf[3] << 8 | buf[2]) << 16 | + ((unsigned) buf[1] << 8 | buf[0]); + *(uint32 *) buf = t; + buf += 4; + } while (--longs); +} +#endif + +/* + * Start MD5 accumulation. Set bit count to 0 and buffer to mysterious + * initialization constants. + */ +void MD5Init(ctx) + struct MD5Context *ctx; +{ + ctx->buf[0] = 0x67452301; + ctx->buf[1] = 0xefcdab89; + ctx->buf[2] = 0x98badcfe; + ctx->buf[3] = 0x10325476; + + ctx->bits[0] = 0; + ctx->bits[1] = 0; +} + +/* + * Update context to reflect the concatenation of another buffer full + * of bytes. + */ +void MD5Update(ctx, buf, len) + struct MD5Context *ctx; unsigned char *buf; unsigned len; +{ + uint32 t; + + /* Update bitcount */ + + t = ctx->bits[0]; + if ((ctx->bits[0] = t + ((uint32) len << 3)) < t) + ctx->bits[1]++; /* Carry from low to high */ + ctx->bits[1] += len >> 29; + + t = (t >> 3) & 0x3f; /* Bytes already in shsInfo->data */ + + /* Handle any leading odd-sized chunks */ + + if (t) { + unsigned char *p = (unsigned char *) ctx->in.c + t; + + t = 64 - t; + if (len < t) { + memcpy(p, buf, len); + return; + } + memcpy(p, buf, t); + byteReverse(ctx->in.c, 16); + MD5Transform(ctx->buf, (uint32 *) ctx->in.c); + buf += t; + len -= t; + } + /* Process data in 64-byte chunks */ + + while (len >= 64) { + memcpy(ctx->in.c, buf, 64); + byteReverse(ctx->in.c, 16); + MD5Transform(ctx->buf, (uint32 *) ctx->in.c); + buf += 64; + len -= 64; + } + + /* Handle any remaining bytes of data. */ + + memcpy(ctx->in.c, buf, len); +} + +/* + * Final wrapup - pad to 64-byte boundary with the bit pattern + * 1 0* (64-bit count of bits processed, MSB-first) + */ +void MD5Final(digest, ctx) + unsigned char digest[16]; struct MD5Context *ctx; +{ + unsigned count; + unsigned char *p; + + /* Compute number of bytes mod 64 */ + count = (ctx->bits[0] >> 3) & 0x3F; + + /* Set the first char of padding to 0x80. This is safe since there is + always at least one byte free */ + p = ctx->in.c + count; + *p++ = 0x80; + + /* Bytes of padding needed to make 64 bytes */ + count = 64 - 1 - count; + + /* Pad out to 56 mod 64 */ + if (count < 8) { + /* Two lots of padding: Pad the first block to 64 bytes */ + memset(p, 0, count); + byteReverse(ctx->in.c, 16); + MD5Transform(ctx->buf, (uint32 *) ctx->in.c); + + /* Now fill the next block with 56 bytes */ + memset(ctx->in.c, 0, 56); + } else { + /* Pad block to 56 bytes */ + memset(p, 0, count - 8); + } + byteReverse(ctx->in.c, 14); + + /* Append length in bits and transform */ + ctx->in.i[14] = ctx->bits[0]; + ctx->in.i[15] = ctx->bits[1]; + + MD5Transform(ctx->buf, (uint32 *) ctx->in.c); + byteReverse((unsigned char *) ctx->buf, 4); + memcpy(digest, ctx->buf, 16); + memset(ctx, 0, sizeof(ctx)); /* In case it's sensitive */ +} + + +/* The four core functions - F1 is optimized somewhat */ + +/* #define F1(x, y, z) (x & y | ~x & z) */ +#define F1(x, y, z) (z ^ (x & (y ^ z))) +#define F2(x, y, z) F1(z, x, y) +#define F3(x, y, z) (x ^ y ^ z) +#define F4(x, y, z) (y ^ (x | ~z)) + +/* This is the central step in the MD5 algorithm. */ +#define MD5STEP(f, w, x, y, z, data, s) \ + ( w += f(x, y, z) + data, w = w<>(32-s), w += x ) + +/* + * The core of the MD5 algorithm, this alters an existing MD5 hash to + * reflect the addition of 16 longwords of new data. MD5Update blocks + * the data and converts bytes into longwords for this routine. + */ +void MD5Transform(buf, in) + uint32 buf[4]; uint32 in[16]; +{ + register uint32 a, b, c, d; + + a = buf[0]; + b = buf[1]; + c = buf[2]; + d = buf[3]; + + MD5STEP(F1, a, b, c, d, in[0] + 0xd76aa478, 7); + MD5STEP(F1, d, a, b, c, in[1] + 0xe8c7b756, 12); + MD5STEP(F1, c, d, a, b, in[2] + 0x242070db, 17); + MD5STEP(F1, b, c, d, a, in[3] + 0xc1bdceee, 22); + MD5STEP(F1, a, b, c, d, in[4] + 0xf57c0faf, 7); + MD5STEP(F1, d, a, b, c, in[5] + 0x4787c62a, 12); + MD5STEP(F1, c, d, a, b, in[6] + 0xa8304613, 17); + MD5STEP(F1, b, c, d, a, in[7] + 0xfd469501, 22); + MD5STEP(F1, a, b, c, d, in[8] + 0x698098d8, 7); + MD5STEP(F1, d, a, b, c, in[9] + 0x8b44f7af, 12); + MD5STEP(F1, c, d, a, b, in[10] + 0xffff5bb1, 17); + MD5STEP(F1, b, c, d, a, in[11] + 0x895cd7be, 22); + MD5STEP(F1, a, b, c, d, in[12] + 0x6b901122, 7); + MD5STEP(F1, d, a, b, c, in[13] + 0xfd987193, 12); + MD5STEP(F1, c, d, a, b, in[14] + 0xa679438e, 17); + MD5STEP(F1, b, c, d, a, in[15] + 0x49b40821, 22); + + MD5STEP(F2, a, b, c, d, in[1] + 0xf61e2562, 5); + MD5STEP(F2, d, a, b, c, in[6] + 0xc040b340, 9); + MD5STEP(F2, c, d, a, b, in[11] + 0x265e5a51, 14); + MD5STEP(F2, b, c, d, a, in[0] + 0xe9b6c7aa, 20); + MD5STEP(F2, a, b, c, d, in[5] + 0xd62f105d, 5); + MD5STEP(F2, d, a, b, c, in[10] + 0x02441453, 9); + MD5STEP(F2, c, d, a, b, in[15] + 0xd8a1e681, 14); + MD5STEP(F2, b, c, d, a, in[4] + 0xe7d3fbc8, 20); + MD5STEP(F2, a, b, c, d, in[9] + 0x21e1cde6, 5); + MD5STEP(F2, d, a, b, c, in[14] + 0xc33707d6, 9); + MD5STEP(F2, c, d, a, b, in[3] + 0xf4d50d87, 14); + MD5STEP(F2, b, c, d, a, in[8] + 0x455a14ed, 20); + MD5STEP(F2, a, b, c, d, in[13] + 0xa9e3e905, 5); + MD5STEP(F2, d, a, b, c, in[2] + 0xfcefa3f8, 9); + MD5STEP(F2, c, d, a, b, in[7] + 0x676f02d9, 14); + MD5STEP(F2, b, c, d, a, in[12] + 0x8d2a4c8a, 20); + + MD5STEP(F3, a, b, c, d, in[5] + 0xfffa3942, 4); + MD5STEP(F3, d, a, b, c, in[8] + 0x8771f681, 11); + MD5STEP(F3, c, d, a, b, in[11] + 0x6d9d6122, 16); + MD5STEP(F3, b, c, d, a, in[14] + 0xfde5380c, 23); + MD5STEP(F3, a, b, c, d, in[1] + 0xa4beea44, 4); + MD5STEP(F3, d, a, b, c, in[4] + 0x4bdecfa9, 11); + MD5STEP(F3, c, d, a, b, in[7] + 0xf6bb4b60, 16); + MD5STEP(F3, b, c, d, a, in[10] + 0xbebfbc70, 23); + MD5STEP(F3, a, b, c, d, in[13] + 0x289b7ec6, 4); + MD5STEP(F3, d, a, b, c, in[0] + 0xeaa127fa, 11); + MD5STEP(F3, c, d, a, b, in[3] + 0xd4ef3085, 16); + MD5STEP(F3, b, c, d, a, in[6] + 0x04881d05, 23); + MD5STEP(F3, a, b, c, d, in[9] + 0xd9d4d039, 4); + MD5STEP(F3, d, a, b, c, in[12] + 0xe6db99e5, 11); + MD5STEP(F3, c, d, a, b, in[15] + 0x1fa27cf8, 16); + MD5STEP(F3, b, c, d, a, in[2] + 0xc4ac5665, 23); + + MD5STEP(F4, a, b, c, d, in[0] + 0xf4292244, 6); + MD5STEP(F4, d, a, b, c, in[7] + 0x432aff97, 10); + MD5STEP(F4, c, d, a, b, in[14] + 0xab9423a7, 15); + MD5STEP(F4, b, c, d, a, in[5] + 0xfc93a039, 21); + MD5STEP(F4, a, b, c, d, in[12] + 0x655b59c3, 6); + MD5STEP(F4, d, a, b, c, in[3] + 0x8f0ccc92, 10); + MD5STEP(F4, c, d, a, b, in[10] + 0xffeff47d, 15); + MD5STEP(F4, b, c, d, a, in[1] + 0x85845dd1, 21); + MD5STEP(F4, a, b, c, d, in[8] + 0x6fa87e4f, 6); + MD5STEP(F4, d, a, b, c, in[15] + 0xfe2ce6e0, 10); + MD5STEP(F4, c, d, a, b, in[6] + 0xa3014314, 15); + MD5STEP(F4, b, c, d, a, in[13] + 0x4e0811a1, 21); + MD5STEP(F4, a, b, c, d, in[4] + 0xf7537e82, 6); + MD5STEP(F4, d, a, b, c, in[11] + 0xbd3af235, 10); + MD5STEP(F4, c, d, a, b, in[2] + 0x2ad7d2bb, 15); + MD5STEP(F4, b, c, d, a, in[9] + 0xeb86d391, 21); + + buf[0] += a; + buf[1] += b; + buf[2] += c; + buf[3] += d; +} diff --git a/library/tools/iff2html/md5.h b/library/tools/iff2html/md5.h new file mode 100644 index 0000000..c2c48ab --- /dev/null +++ b/library/tools/iff2html/md5.h @@ -0,0 +1,57 @@ +#ifndef MD5_H +#define MD5_H + +/* The following tests optimise behaviour on little-endian + machines, where there is no need to reverse the byte order + of 32 bit words in the MD5 computation. By default, + HIGHFIRST is defined, which indicates we're running on a + big-endian (most significant byte first) machine, on which + the byteReverse function in md5.c must be invoked. However, + byteReverse is coded in such a way that it is an identity + function when run on a little-endian machine, so calling it + on such a platform causes no harm apart from wasting time. + If the platform is known to be little-endian, we speed + things up by undefining HIGHFIRST, which defines + byteReverse as a null macro. Doing things in this manner + insures we work on new platforms regardless of their byte + order. */ + +#define HIGHFIRST + +#ifdef __i386__ +#undef HIGHFIRST +#endif + +/* On machines where "long" is 64 bits, we need to declare + uint32 as something guaranteed to be 32 bits. */ + +#ifdef __alpha +typedef unsigned int uint32; +#else +typedef unsigned long uint32; +#endif + +struct MD5Context { + uint32 buf[4]; + uint32 bits[2]; + union { + unsigned char c[64]; + uint32 i[16]; + } in; +}; + +extern void MD5Init(); +extern void MD5Update(); +extern void MD5Final(); +extern void MD5Transform(); + +/* + * This is needed to make RSAREF happy on some MS-DOS compilers. + */ +typedef struct MD5Context MD5_CTX; + +/* Define CHECK_HARDWARE_PROPERTIES to have main,c verify + byte order and uint32 settings. */ +#define CHECK_HARDWARE_PROPERTIES + +#endif /* !MD5_H */ diff --git a/library/tools/iff2html/opngreduc.c b/library/tools/iff2html/opngreduc.c new file mode 100644 index 0000000..538cb7b --- /dev/null +++ b/library/tools/iff2html/opngreduc.c @@ -0,0 +1,1311 @@ +/* + * opngreduc.c - libpng extension: lossless image reductions. + * + * Copyright (C) 2003-2011 Cosmin Truta. + * This software is distributed under the same licensing and warranty terms + * as libpng. + */ + +/* CAUTION: + * Image reductions do not work well under certain transformations. + * + * Transformations like PNG_BGR, PNG_SWAP_BYTES, PNG_FILLER, PNG_INVERT_ALPHA, + * and possibly others, require special treatment. However, the libpng API + * does not currently convey the effect of transformations on its internal + * state or on the layout of pixel data. + * + * Transformations which affect pixel depth (e.g. PNG_FILLER) are especially + * dangerous when used in conjunction with this code, and should be avoided. + */ + +#include "opngreduc.h" + +#ifndef OPNG_ASSERT +#include +#define OPNG_ASSERT(cond) assert(cond) +#define OPNG_ASSERT_MSG(cond, msg) assert(cond) +#endif + +#ifdef png_debug +#define opng_debug(level, msg) png_debug(level, msg) +#else +#define opng_debug(level, msg) ((void)0) +#endif + + +#ifdef PNG_INFO_IMAGE_SUPPORTED + +/* + * Check if the image information is valid. + * The image information is said to be valid if all the required + * critical chunk data is present in the png structures. + * The function returns 1 if this information is valid, and 0 otherwise. + */ +int PNGAPI +opng_validate_image(png_structp png_ptr, png_infop info_ptr) +{ + opng_debug(1, "in opng_validate_image"); + + /* Validate IHDR. */ + if (png_get_bit_depth(png_ptr, info_ptr) == 0) + return 0; + + /* Validate PLTE. */ + if (png_get_color_type(png_ptr, info_ptr) & PNG_COLOR_MASK_PALETTE) + { + if (!png_get_valid(png_ptr, info_ptr, PNG_INFO_PLTE)) + return 0; + } + + /* Validate IDAT. */ + if (!png_get_valid(png_ptr, info_ptr, PNG_INFO_IDAT)) + return 0; + + return 1; +} + +#endif /* PNG_INFO_IMAGE_SUPPORTED */ + + +#ifdef OPNG_IMAGE_REDUCTIONS_SUPPORTED + +#define OPNG_CMP_RGB(R1, G1, B1, R2, G2, B2) \ + (((int)(R1) != (int)(R2)) ? \ + ((int)(R1) - (int)(R2)) : \ + (((int)(G1) != (int)(G2)) ? \ + ((int)(G1) - (int)(G2)) : \ + ((int)(B1) - (int)(B2)))) + +#define OPNG_CMP_ARGB(A1, R1, G1, B1, A2, R2, G2, B2) \ + (((int)(A1) != (int)(A2)) ? \ + ((int)(A1) - (int)(A2)) : \ + (((int)(R1) != (R2)) ? \ + ((int)(R1) - (int)(R2)) : \ + (((int)(G1) != (int)(G2)) ? \ + ((int)(G1) - (int)(G2)) : \ + ((int)(B1) - (int)(B2))))) + +/* + * Build a color+alpha palette in which the entries are sorted by + * (alpha, red, green, blue), in this particular order. + * Use the insertion sort algorithm. + * The alpha value is ignored if it is not in the range [0 .. 255]. + * The function returns: + * 1 if the insertion is successful; *index = position of new entry. + * 0 if the insertion is unnecessary; *index = position of crt entry. + * -1 if overflow; *num_palette = *num_trans = *index = -1. + */ +static int /* PRIVATE */ +opng_insert_palette_entry(png_colorp palette, int *num_palette, + png_bytep trans_alpha, int *num_trans, int max_tuples, + unsigned int red, unsigned int green, unsigned int blue, unsigned int alpha, + int *index) +{ + int low, high, mid, cmp; + int i; + + OPNG_ASSERT(*num_palette >= 0 && *num_palette <= max_tuples); + OPNG_ASSERT(*num_trans >= 0 && *num_trans <= *num_palette); + + if (alpha < 255) + { + /* Do a binary search among transparent tuples. */ + low = 0; + high = *num_trans - 1; + while (low <= high) + { + mid = (low + high) / 2; + cmp = OPNG_CMP_ARGB(alpha, red, green, blue, + trans_alpha[mid], + palette[mid].red, palette[mid].green, palette[mid].blue); + if (cmp < 0) + high = mid - 1; + else if (cmp > 0) + low = mid + 1; + else + { + *index = mid; + return 0; + } + } + } + else /* alpha == 255 || alpha not in [0 .. 255] */ + { + /* Do a (faster) binary search among opaque tuples. */ + low = *num_trans; + high = *num_palette - 1; + while (low <= high) + { + mid = (low + high) / 2; + cmp = OPNG_CMP_RGB(red, green, blue, + palette[mid].red, palette[mid].green, palette[mid].blue); + if (cmp < 0) + high = mid - 1; + else if (cmp > 0) + low = mid + 1; + else + { + *index = mid; + return 0; + } + } + } + if (alpha > 255) + { + /* The binary search among opaque tuples has failed. */ + /* Do a linear search among transparent tuples, ignoring alpha. */ + for (i = 0; i < *num_trans; ++i) + { + cmp = OPNG_CMP_RGB(red, green, blue, + palette[i].red, palette[i].green, palette[i].blue); + if (cmp == 0) + { + *index = i; + return 0; + } + } + } + + /* Check for overflow. */ + if (*num_palette >= max_tuples) + { + *num_palette = *num_trans = *index = -1; + return -1; + } + + /* Insert new tuple at [low]. */ + OPNG_ASSERT(low >= 0 && low <= *num_palette); + for (i = *num_palette; i > low; --i) + palette[i] = palette[i - 1]; + palette[low].red = (png_byte)red; + palette[low].green = (png_byte)green; + palette[low].blue = (png_byte)blue; + ++(*num_palette); + if (alpha < 255) + { + OPNG_ASSERT(low <= *num_trans); + for (i = *num_trans; i > low; --i) + trans_alpha[i] = trans_alpha[i - 1]; + trans_alpha[low] = (png_byte)alpha; + ++(*num_trans); + } + *index = low; + return 1; +} + +/* + * Retrieve the alpha samples from the given image row. + */ +static void /* PRIVATE */ +opng_get_alpha_row(png_row_infop row_info_ptr, png_color_16p trans_color, + png_bytep row, png_bytep alpha_row) +{ + png_bytep sample_ptr; + png_uint_32 width; + int color_type, bit_depth, channels; + png_byte trans_red, trans_green, trans_blue, trans_gray; + png_uint_32 i; + + width = row_info_ptr->width; + color_type = row_info_ptr->color_type; + bit_depth = row_info_ptr->bit_depth; + channels = row_info_ptr->channels; + + OPNG_ASSERT(!(color_type & PNG_COLOR_MASK_PALETTE)); + OPNG_ASSERT(bit_depth == 8); + + if (!(color_type & PNG_COLOR_MASK_ALPHA)) + { + if (trans_color == NULL) + { + /* All pixels are fully opaque. */ + memset(alpha_row, 255, (size_t)width); + return; + } + if (color_type == PNG_COLOR_TYPE_RGB) + { + OPNG_ASSERT(channels == 3); + trans_red = (png_byte)trans_color->red; + trans_green = (png_byte)trans_color->green; + trans_blue = (png_byte)trans_color->blue; + sample_ptr = row; + for (i = 0; i < width; ++i, sample_ptr += 3) + alpha_row[i] = (png_byte) + ((sample_ptr[0] == trans_red && + sample_ptr[1] == trans_green && + sample_ptr[2] == trans_blue) ? 0 : 255); + } + else + { + OPNG_ASSERT(color_type == PNG_COLOR_TYPE_GRAY); + OPNG_ASSERT(channels == 1); + trans_gray = (png_byte)trans_color->gray; + for (i = 0; i < width; ++i) + alpha_row[i] = (png_byte)((row[i] == trans_gray) ? 0 : 255); + } + return; + } + + /* There is a real alpha channel. The alpha sample is last in RGBA tuple. */ + OPNG_ASSERT(channels > 1); + sample_ptr = row + (channels - 1); + for (i = 0; i < width; ++i, sample_ptr += channels, ++alpha_row) + *alpha_row = *sample_ptr; +} + +/* + * Analyze the redundancy of bits inside the image. + * The parameter reductions indicates the intended reductions. + * The function returns the possible reductions. + */ +static png_uint_32 /* PRIVATE */ +opng_analyze_bits(png_structp png_ptr, png_infop info_ptr, + png_uint_32 reductions) +{ + png_bytepp row_ptr; + png_bytep component_ptr; + png_uint_32 height, width; + int bit_depth, color_type, byte_depth, channels, sample_size, offset_alpha; +#ifdef PNG_bKGD_SUPPORTED + png_color_16p background; +#endif + png_uint_32 i, j; + + opng_debug(1, "in opng_analyze_bits"); + + png_get_IHDR(png_ptr, info_ptr, &width, &height, &bit_depth, &color_type, + NULL, NULL, NULL); + if (bit_depth < 8) + return OPNG_REDUCE_NONE; /* not applicable */ + if (color_type & PNG_COLOR_MASK_PALETTE) + return OPNG_REDUCE_NONE; /* let opng_reduce_palette() handle it */ + + byte_depth = bit_depth / 8; + channels = png_get_channels(png_ptr, info_ptr); + sample_size = channels * byte_depth; + offset_alpha = (channels - 1) * byte_depth; + + /* Select the applicable reductions. */ + reductions &= (OPNG_REDUCE_16_TO_8 | + OPNG_REDUCE_RGB_TO_GRAY | OPNG_REDUCE_STRIP_ALPHA); + if (bit_depth <= 8) + reductions &= ~OPNG_REDUCE_16_TO_8; + if (!(color_type & PNG_COLOR_MASK_COLOR)) + reductions &= ~OPNG_REDUCE_RGB_TO_GRAY; + if (!(color_type & PNG_COLOR_MASK_ALPHA)) + reductions &= ~OPNG_REDUCE_STRIP_ALPHA; + + /* Check if the ancillary information allows these reductions. */ +#ifdef PNG_bKGD_SUPPORTED + if (png_get_bKGD(png_ptr, info_ptr, &background)) + { + if (reductions & OPNG_REDUCE_16_TO_8) + { + if (background->red % 257 != 0 || + background->green % 257 != 0 || + background->blue % 257 != 0 || + background->gray % 257 != 0) + reductions &= ~OPNG_REDUCE_16_TO_8; + } + if (reductions & OPNG_REDUCE_RGB_TO_GRAY) + { + if (background->red != background->green || + background->red != background->blue) + reductions &= ~OPNG_REDUCE_RGB_TO_GRAY; + } + } +#endif + + /* Check for each possible reduction, row by row. */ + row_ptr = png_get_rows(png_ptr, info_ptr); + for (i = 0; i < height; ++i, ++row_ptr) + { + if (reductions == OPNG_REDUCE_NONE) + return OPNG_REDUCE_NONE; /* no need to go any further */ + + /* Check if it is possible to reduce the bit depth to 8. */ + if (reductions & OPNG_REDUCE_16_TO_8) + { + component_ptr = *row_ptr; + for (j = 0; j < channels * width; ++j, component_ptr += 2) + { + if (component_ptr[0] != component_ptr[1]) + { + reductions &= ~OPNG_REDUCE_16_TO_8; + break; + } + } + } + + if (bit_depth == 8) + { + /* Check if it is possible to reduce rgb --> gray. */ + if (reductions & OPNG_REDUCE_RGB_TO_GRAY) + { + component_ptr = *row_ptr; + for (j = 0; j < width; ++j, component_ptr += sample_size) + { + if (component_ptr[0] != component_ptr[1] || + component_ptr[0] != component_ptr[2]) + { + reductions &= ~OPNG_REDUCE_RGB_TO_GRAY; + break; + } + } + } + + /* Check if it is possible to strip the alpha channel. */ + if (reductions & OPNG_REDUCE_STRIP_ALPHA) + { + component_ptr = *row_ptr + offset_alpha; + for (j = 0; j < width; ++j, component_ptr += sample_size) + { + if (component_ptr[0] != 255) + { + reductions &= ~OPNG_REDUCE_STRIP_ALPHA; + break; + } + } + } + } + else /* bit_depth == 16 */ + { + /* Check if it is possible to reduce rgb --> gray. */ + if (reductions & OPNG_REDUCE_RGB_TO_GRAY) + { + component_ptr = *row_ptr; + for (j = 0; j < width; ++j, component_ptr += sample_size) + { + if (component_ptr[0] != component_ptr[2] || + component_ptr[0] != component_ptr[4] || + component_ptr[1] != component_ptr[3] || + component_ptr[1] != component_ptr[5]) + { + reductions &= ~OPNG_REDUCE_RGB_TO_GRAY; + break; + } + } + } + + /* Check if it is possible to strip the alpha channel. */ + if (reductions & OPNG_REDUCE_STRIP_ALPHA) + { + component_ptr = *row_ptr + offset_alpha; + for (j = 0; j < width; ++j, component_ptr += sample_size) + { + if (component_ptr[0] != 255 || component_ptr[1] != 255) + { + reductions &= ~OPNG_REDUCE_STRIP_ALPHA; + break; + } + } + } + } + } + + return reductions; +} + +/* + * Reduce the image type to a lower bit depth and color type, + * by removing redundant bits. + * Possible reductions: 16bpp to 8bpp; RGB to gray; strip alpha. + * The parameter reductions indicates the intended reductions. + * The function returns the successful reductions. + * All reductions are performed in a single step. + */ +static png_uint_32 /* PRIVATE */ +opng_reduce_bits(png_structp png_ptr, png_infop info_ptr, + png_uint_32 reductions) +{ + png_bytepp row_ptr; + png_bytep src_ptr, dest_ptr; + png_uint_32 width, height; + int interlace_type, compression_type, filter_type; + int src_bit_depth, dest_bit_depth; + int src_byte_depth, dest_byte_depth; + int src_color_type, dest_color_type; + int src_channels, dest_channels; + int src_sample_size, dest_sample_size; + int tran_tbl[8]; + png_color_16p trans_color; +#ifdef PNG_bKGD_SUPPORTED + png_color_16p background; +#endif +#ifdef PNG_sBIT_SUPPORTED + png_color_8p sig_bits; +#endif + png_uint_32 i, j; + int k; + + opng_debug(1, "in opng_reduce_bits"); + + /* See which reductions may be performed. */ + reductions = opng_analyze_bits(png_ptr, info_ptr, reductions); + if (reductions == OPNG_REDUCE_NONE) + return OPNG_REDUCE_NONE; /* exit early */ + + png_get_IHDR(png_ptr, info_ptr, &width, &height, + &src_bit_depth, &src_color_type, + &interlace_type, &compression_type, &filter_type); + + /* Compute the new image parameters bit_depth, color_type, etc. */ + OPNG_ASSERT(src_bit_depth >= 8); + if (reductions & OPNG_REDUCE_16_TO_8) + { + OPNG_ASSERT(src_bit_depth == 16); + dest_bit_depth = 8; + } + else + dest_bit_depth = src_bit_depth; + + src_byte_depth = src_bit_depth / 8; + dest_byte_depth = dest_bit_depth / 8; + + dest_color_type = src_color_type; + if (reductions & OPNG_REDUCE_RGB_TO_GRAY) + { + OPNG_ASSERT(src_color_type & PNG_COLOR_MASK_COLOR); + dest_color_type &= ~PNG_COLOR_MASK_COLOR; + } + if (reductions & OPNG_REDUCE_STRIP_ALPHA) + { + OPNG_ASSERT(src_color_type & PNG_COLOR_MASK_ALPHA); + dest_color_type &= ~PNG_COLOR_MASK_ALPHA; + } + + src_channels = png_get_channels(png_ptr, info_ptr); + dest_channels = + ((dest_color_type & PNG_COLOR_MASK_COLOR) ? 3 : 1) + + ((dest_color_type & PNG_COLOR_MASK_ALPHA) ? 1 : 0); + + src_sample_size = src_channels * src_byte_depth; + dest_sample_size = dest_channels * dest_byte_depth; + + /* Pre-compute the intra-sample translation table. */ + for (k = 0; k < 4 * dest_byte_depth; ++k) + tran_tbl[k] = k * src_bit_depth / dest_bit_depth; + /* If rgb --> gray, shift the alpha component two positions to the left. */ + if ((reductions & OPNG_REDUCE_RGB_TO_GRAY) && + (dest_color_type & PNG_COLOR_MASK_ALPHA)) + { + tran_tbl[dest_byte_depth] = tran_tbl[3 * dest_byte_depth]; + if (dest_byte_depth == 2) + tran_tbl[dest_byte_depth + 1] = tran_tbl[3 * dest_byte_depth + 1]; + } + + /* Translate the samples to the new image type. */ + OPNG_ASSERT(src_sample_size > dest_sample_size); + row_ptr = png_get_rows(png_ptr, info_ptr); + for (i = 0; i < height; ++i, ++row_ptr) + { + src_ptr = dest_ptr = *row_ptr; + for (j = 0; j < width; ++j) + { + for (k = 0; k < dest_sample_size; ++k) + dest_ptr[k] = src_ptr[tran_tbl[k]]; + src_ptr += src_sample_size; + dest_ptr += dest_sample_size; + } + } + + /* Update the ancillary information. */ + if (png_get_tRNS(png_ptr, info_ptr, NULL, NULL, &trans_color)) + { + if (reductions & OPNG_REDUCE_16_TO_8) + { + if (trans_color->red % 257 == 0 && + trans_color->green % 257 == 0 && + trans_color->blue % 257 == 0 && + trans_color->gray % 257 == 0) + { + trans_color->red &= 255; + trans_color->green &= 255; + trans_color->blue &= 255; + trans_color->gray &= 255; + } + else + { + /* 16-bit tRNS in 8-bit samples: all pixels are 100% opaque. */ + png_free_data(png_ptr, info_ptr, PNG_FREE_TRNS, -1); + png_set_invalid(png_ptr, info_ptr, PNG_INFO_tRNS); + } + } + if (reductions & OPNG_REDUCE_RGB_TO_GRAY) + { + if (trans_color->red == trans_color->green || + trans_color->red == trans_color->blue) + trans_color->gray = trans_color->red; + else + { + /* Non-gray tRNS in grayscale image: all pixels are 100% opaque. */ + png_free_data(png_ptr, info_ptr, PNG_FREE_TRNS, -1); + png_set_invalid(png_ptr, info_ptr, PNG_INFO_tRNS); + } + } + } +#ifdef PNG_bKGD_SUPPORTED + if (png_get_bKGD(png_ptr, info_ptr, &background)) + { + if (reductions & OPNG_REDUCE_16_TO_8) + { + background->red &= 255; + background->green &= 255; + background->blue &= 255; + background->gray &= 255; + } + if (reductions & OPNG_REDUCE_RGB_TO_GRAY) + background->gray = background->red; + } +#endif +#ifdef PNG_sBIT_SUPPORTED + if (png_get_sBIT(png_ptr, info_ptr, &sig_bits)) + { + if (reductions & OPNG_REDUCE_16_TO_8) + { + if (sig_bits->red > 8) + sig_bits->red = 8; + if (sig_bits->green > 8) + sig_bits->green = 8; + if (sig_bits->blue > 8) + sig_bits->blue = 8; + if (sig_bits->gray > 8) + sig_bits->gray = 8; + if (sig_bits->alpha > 8) + sig_bits->alpha = 8; + } + if (reductions & OPNG_REDUCE_RGB_TO_GRAY) + { + png_byte max_sig_bits = sig_bits->red; + if (max_sig_bits < sig_bits->green) + max_sig_bits = sig_bits->green; + if (max_sig_bits < sig_bits->blue) + max_sig_bits = sig_bits->blue; + sig_bits->gray = max_sig_bits; + } + } +#endif + + /* Update the image information. */ + png_set_IHDR(png_ptr, info_ptr, width, height, + dest_bit_depth, dest_color_type, + interlace_type, compression_type, filter_type); + + return reductions; +} + +/* + * Reduce the bit depth of a palette image to the lowest possible value. + * The parameter reductions should contain OPNG_REDUCE_8_TO_4_2_1. + * The function returns OPNG_REDUCE_8_TO_4_2_1 if successful. + */ +static png_uint_32 /* PRIVATE */ +opng_reduce_palette_bits(png_structp png_ptr, png_infop info_ptr, + png_uint_32 reductions) +{ + png_bytepp row_ptr; + png_bytep src_sample_ptr, dest_sample_ptr; + png_uint_32 width, height; + int color_type, interlace_type, compression_type, filter_type; + int src_bit_depth, dest_bit_depth; + unsigned int src_mask_init, src_mask, src_shift, dest_shift; + unsigned int sample, dest_buf; + png_colorp palette; + int num_palette; + png_uint_32 i, j; + + opng_debug(1, "in opng_reduce_palette_bits"); + + /* Check if the reduction applies. */ + if (!(reductions & OPNG_REDUCE_8_TO_4_2_1)) + return OPNG_REDUCE_NONE; + png_get_IHDR(png_ptr, info_ptr, &width, &height, &src_bit_depth, + &color_type, &interlace_type, &compression_type, &filter_type); + if (color_type != PNG_COLOR_TYPE_PALETTE) + return OPNG_REDUCE_NONE; + if (!png_get_PLTE(png_ptr, info_ptr, &palette, &num_palette)) + num_palette = 0; + + /* Find the smallest possible bit depth. */ + if (num_palette > 16) + return OPNG_REDUCE_NONE; + else if (num_palette > 4) /* 5 .. 16 entries */ + dest_bit_depth = 4; + else if (num_palette > 2) /* 3 or 4 entries */ + dest_bit_depth = 2; + else /* 1 or 2 entries */ + { + OPNG_ASSERT(num_palette > 0); + dest_bit_depth = 1; + } + + if (src_bit_depth <= dest_bit_depth) + { + OPNG_ASSERT(src_bit_depth == dest_bit_depth); + return OPNG_REDUCE_NONE; + } + + /* Iterate through all sample values. */ + row_ptr = png_get_rows(png_ptr, info_ptr); + if (src_bit_depth == 8) + { + for (i = 0; i < height; ++i, ++row_ptr) + { + src_sample_ptr = dest_sample_ptr = *row_ptr; + dest_shift = 8; + dest_buf = 0; + for (j = 0; j < width; ++j) + { + dest_shift -= dest_bit_depth; + if (dest_shift > 0) + dest_buf |= *src_sample_ptr << dest_shift; + else + { + *dest_sample_ptr++ = (png_byte)(dest_buf | *src_sample_ptr); + dest_shift = 8; + dest_buf = 0; + } + ++src_sample_ptr; + } + if (dest_shift != 0) + *dest_sample_ptr = (png_byte)dest_buf; + } + } + else /* src_bit_depth < 8 */ + { + src_mask_init = (1 << (8 + src_bit_depth)) - (1 << 8); + for (i = 0; i < height; ++i, ++row_ptr) + { + src_sample_ptr = dest_sample_ptr = *row_ptr; + src_shift = dest_shift = 8; + src_mask = src_mask_init; + dest_buf = 0; + for (j = 0; j < width; ++j) + { + src_shift -= src_bit_depth; + src_mask >>= src_bit_depth; + sample = (*src_sample_ptr & src_mask) >> src_shift; + dest_shift -= dest_bit_depth; + if (dest_shift > 0) + dest_buf |= sample << dest_shift; + else + { + *dest_sample_ptr++ = (png_byte)(dest_buf | sample); + dest_shift = 8; + dest_buf = 0; + } + if (src_shift == 0) + { + src_shift = 8; + src_mask = src_mask_init; + ++src_sample_ptr; + } + } + if (dest_shift != 0) + *dest_sample_ptr = (png_byte)dest_buf; + } + } + + /* Update the image information. */ + png_set_IHDR(png_ptr, info_ptr, width, height, dest_bit_depth, + color_type, interlace_type, compression_type, filter_type); + return OPNG_REDUCE_8_TO_4_2_1; +} + +/* + * Reduce the image type from grayscale(+alpha) or RGB(+alpha) to palette, + * if possible. + * The parameter reductions indicates the intended reductions. + * The function returns the successful reductions. + */ +static png_uint_32 /* PRIVATE */ +opng_reduce_to_palette(png_structp png_ptr, png_infop info_ptr, + png_uint_32 reductions) +{ + png_uint_32 result; + png_row_info row_info; + png_bytepp row_ptr; + png_bytep sample_ptr, alpha_row; + png_uint_32 height, width; + int color_type, interlace_type, compression_type, filter_type; + int src_bit_depth, dest_bit_depth, channels; + png_color palette[256]; + png_byte trans_alpha[256]; + png_color_16p trans_color; + int num_palette, num_trans, index; + unsigned int gray, red, green, blue, alpha; + unsigned int prev_gray, prev_red, prev_green, prev_blue, prev_alpha; +#ifdef PNG_bKGD_SUPPORTED + png_color_16p background; +#endif + png_uint_32 i, j; + + opng_debug(1, "in opng_reduce_to_palette"); + + png_get_IHDR(png_ptr, info_ptr, &width, &height, &src_bit_depth, + &color_type, &interlace_type, &compression_type, &filter_type); + if (src_bit_depth != 8) + return OPNG_REDUCE_NONE; /* nothing is done in this case */ + OPNG_ASSERT(!(color_type & PNG_COLOR_MASK_PALETTE)); + + row_ptr = png_get_rows(png_ptr, info_ptr); + channels = png_get_channels(png_ptr, info_ptr); + alpha_row = (png_bytep)png_malloc(png_ptr, width); + + row_info.width = width; + row_info.rowbytes = 0; /* not used */ + row_info.color_type = (png_byte)color_type; + row_info.bit_depth = (png_byte)src_bit_depth; + row_info.channels = (png_byte)channels; + row_info.pixel_depth = 0; /* not used */ + + /* Analyze the possibility of this reduction. */ + num_palette = num_trans = 0; + trans_color = NULL; + png_get_tRNS(png_ptr, info_ptr, NULL, NULL, &trans_color); + prev_gray = prev_red = prev_green = prev_blue = prev_alpha = 256; + for (i = 0; i < height; ++i, ++row_ptr) + { + sample_ptr = *row_ptr; + opng_get_alpha_row(&row_info, trans_color, *row_ptr, alpha_row); + if (color_type & PNG_COLOR_MASK_COLOR) + { + for (j = 0; j < width; ++j, sample_ptr += channels) + { + red = sample_ptr[0]; + green = sample_ptr[1]; + blue = sample_ptr[2]; + alpha = alpha_row[j]; + /* Check the cache first. */ + if (red != prev_red || green != prev_green || blue != prev_blue || + alpha != prev_alpha) + { + prev_red = red; + prev_green = green; + prev_blue = blue; + prev_alpha = alpha; + if (opng_insert_palette_entry(palette, &num_palette, + trans_alpha, &num_trans, 256, + red, green, blue, alpha, &index) < 0) /* overflow */ + { + OPNG_ASSERT(num_palette < 0); + i = height; /* forced exit from outer loop */ + break; + } + } + } + } + else /* grayscale */ + { + for (j = 0; j < width; ++j, sample_ptr += channels) + { + gray = sample_ptr[0]; + alpha = alpha_row[j]; + /* Check the cache first. */ + if (gray != prev_gray || alpha != prev_alpha) + { + prev_gray = gray; + prev_alpha = alpha; + if (opng_insert_palette_entry(palette, &num_palette, + trans_alpha, &num_trans, 256, + gray, gray, gray, alpha, &index) < 0) /* overflow */ + { + OPNG_ASSERT(num_palette < 0); + i = height; /* forced exit from outer loop */ + break; + } + } + } + } + } +#ifdef PNG_bKGD_SUPPORTED + if ((num_palette >= 0) && png_get_bKGD(png_ptr, info_ptr, &background)) + { + /* bKGD has an alpha-agnostic palette entry. */ + if (color_type & PNG_COLOR_MASK_COLOR) + { + red = background->red; + green = background->green; + blue = background->blue; + } + else + red = green = blue = background->gray; + opng_insert_palette_entry(palette, &num_palette, + trans_alpha, &num_trans, 256, + red, green, blue, 256, &index); + if (index >= 0) + background->index = (png_byte)index; + } +#endif + + /* Continue only if the uncompressed indexed image (pixels + PLTE + tRNS) + * is smaller than the uncompressed RGB(A) image. + * Casual overhead (headers, CRCs, etc.) is ignored. + * + * Compare: + * num_pixels * (src_bit_depth * channels - dest_bit_depth) / 8 + * vs. + * sizeof(PLTE) + sizeof(tRNS) + */ + if (num_palette >= 0) + { + OPNG_ASSERT(num_palette > 0 && num_palette <= 256); + OPNG_ASSERT(num_trans >= 0 && num_trans <= num_palette); + if (num_palette <= 2) + dest_bit_depth = 1; + else if (num_palette <= 4) + dest_bit_depth = 2; + else if (num_palette <= 16) + dest_bit_depth = 4; + else + dest_bit_depth = 8; + /* Do the comparison in a way that does not cause overflow. */ + if (channels * 8 == dest_bit_depth || + (3 * num_palette + num_trans) * 8 / (channels * 8 - dest_bit_depth) + / width / height >= 1) + num_palette = -1; + } + + if (num_palette < 0) /* can't reduce */ + { + png_free(png_ptr, alpha_row); + return OPNG_REDUCE_NONE; + } + + /* Reduce. */ + row_ptr = png_get_rows(png_ptr, info_ptr); + index = -1; + prev_red = prev_green = prev_blue = prev_alpha = (unsigned int)(-1); + for (i = 0; i < height; ++i, ++row_ptr) + { + sample_ptr = *row_ptr; + opng_get_alpha_row(&row_info, trans_color, *row_ptr, alpha_row); + if (color_type & PNG_COLOR_MASK_COLOR) + { + for (j = 0; j < width; ++j, sample_ptr += channels) + { + red = sample_ptr[0]; + green = sample_ptr[1]; + blue = sample_ptr[2]; + alpha = alpha_row[j]; + /* Check the cache first. */ + if (red != prev_red || green != prev_green || blue != prev_blue || + alpha != prev_alpha) + { + prev_red = red; + prev_green = green; + prev_blue = blue; + prev_alpha = alpha; + if (opng_insert_palette_entry(palette, &num_palette, + trans_alpha, &num_trans, 256, + red, green, blue, alpha, &index) != 0) + index = -1; /* this should not happen */ + } + OPNG_ASSERT(index >= 0); + (*row_ptr)[j] = (png_byte)index; + } + } + else /* grayscale */ + { + for (j = 0; j < width; ++j, sample_ptr += channels) + { + gray = sample_ptr[0]; + alpha = alpha_row[j]; + /* Check the cache first. */ + if (gray != prev_gray || alpha != prev_alpha) + { + prev_gray = gray; + prev_alpha = alpha; + if (opng_insert_palette_entry(palette, &num_palette, + trans_alpha, &num_trans, 256, + gray, gray, gray, alpha, &index) != 0) + index = -1; /* this should not happen */ + } + OPNG_ASSERT(index >= 0); + (*row_ptr)[j] = (png_byte)index; + } + } + } + + /* Update the image information. */ + png_set_IHDR(png_ptr, info_ptr, width, height, 8, PNG_COLOR_TYPE_PALETTE, + interlace_type, compression_type, filter_type); + png_set_PLTE(png_ptr, info_ptr, palette, num_palette); + if (num_trans > 0) + png_set_tRNS(png_ptr, info_ptr, trans_alpha, num_trans, NULL); + /* bKGD (if present) is automatically updated. */ + + png_free(png_ptr, alpha_row); + + result = OPNG_REDUCE_RGB_TO_PALETTE; + if (reductions & OPNG_REDUCE_8_TO_4_2_1) + result |= opng_reduce_palette_bits(png_ptr, info_ptr, reductions); + return result; +} + +/* + * Analyze the usage of samples. + * The output value usage_map[n] indicates whether the sample n + * is used. The usage_map[] array must have 256 entries. + * The function requires a valid bit depth between 1 and 8. + */ +static void /* PRIVATE */ +opng_analyze_sample_usage(png_structp png_ptr, png_infop info_ptr, + png_bytep usage_map) +{ + png_bytepp row_ptr; + png_bytep sample_ptr; + png_uint_32 width, height; + int bit_depth, init_shift, init_mask, shift, mask; +#ifdef PNG_bKGD_SUPPORTED + png_color_16p background; +#endif + png_uint_32 i, j; + + opng_debug(1, "in opng_analyze_sample_usage"); + + height = png_get_image_height(png_ptr, info_ptr); + width = png_get_image_width(png_ptr, info_ptr); + bit_depth = png_get_bit_depth(png_ptr, info_ptr); + row_ptr = png_get_rows(png_ptr, info_ptr); + + /* Initialize the output entries with 0. */ + memset(usage_map, 0, 256); + + /* Iterate through all sample values. */ + if (bit_depth == 8) + { + for (i = 0; i < height; ++i, ++row_ptr) + { + for (j = 0, sample_ptr = *row_ptr; j < width; ++j, ++sample_ptr) + usage_map[*sample_ptr] = 1; + } + } + else + { + OPNG_ASSERT(bit_depth < 8); + init_shift = 8 - bit_depth; + init_mask = (1 << 8) - (1 << init_shift); + for (i = 0; i < height; ++i, ++row_ptr) + { + for (j = 0, sample_ptr = *row_ptr; j < width; ++sample_ptr) + { + mask = init_mask; + shift = init_shift; + do + { + usage_map[(*sample_ptr & mask) >> shift] = 1; + mask >>= bit_depth; + shift -= bit_depth; + ++j; + } while (mask > 0 && j < width); + } + } + } + +#ifdef PNG_bKGD_SUPPORTED + /* bKGD also counts as a used sample. */ + if (png_get_bKGD(png_ptr, info_ptr, &background)) + usage_map[background->index] = 1; +#endif +} + +/* + * Set the number of PLTE entries to a new value. + * Setting info_ptr->num_palette to num_palette, avoiding the temporary buffer, + * should have been sufficient, but can't be done using the current libpng API. + */ +static void /* PRIVATE */ +opng_set_num_palette(png_structp png_ptr, png_infop info_ptr, int num_palette) +{ + png_color buffer[PNG_MAX_PALETTE_LENGTH]; + png_colorp palette; + int src_num_palette; + + opng_debug(1, "in opng_set_num_palette"); + + OPNG_ASSERT(num_palette > 0); + src_num_palette = 0; + png_get_PLTE(png_ptr, info_ptr, &palette, &src_num_palette); + if (num_palette == src_num_palette) + return; + memcpy(buffer, palette, num_palette * sizeof(png_color)); + if (num_palette > src_num_palette) + memset(buffer + src_num_palette, 0, + (num_palette - src_num_palette) * sizeof(png_color)); + png_set_PLTE(png_ptr, info_ptr, buffer, num_palette); +} + +/* + * Set the number of tRNS entries to a new value. + * Setting info_ptr->num_trans to num_trans, avoiding the temporary buffer, + * should have been sufficient, but can't be done using the current libpng API. + */ +static void /* PRIVATE */ +opng_set_num_trans(png_structp png_ptr, png_infop info_ptr, int num_trans) +{ + png_byte buffer[PNG_MAX_PALETTE_LENGTH]; + png_bytep trans_alpha; + int src_num_trans; + + opng_debug(1, "in opng_set_num_trans"); + + OPNG_ASSERT(num_trans > 0); /* tRNS should be invalidated in this case */ + src_num_trans = 0; + png_get_tRNS(png_ptr, info_ptr, &trans_alpha, &src_num_trans, NULL); + if (num_trans == src_num_trans) + return; + memcpy(buffer, trans_alpha, (size_t)num_trans); + if (num_trans > src_num_trans) + memset(buffer + src_num_trans, 0, num_trans - src_num_trans); + png_set_tRNS(png_ptr, info_ptr, buffer, num_trans, NULL); +} + +/* + * Reduce the palette. (Only the fast method is implemented.) + * The parameter reductions indicates the intended reductions. + * The function returns the successful reductions. + */ +static png_uint_32 /* PRIVATE */ +opng_reduce_palette(png_structp png_ptr, png_infop info_ptr, + png_uint_32 reductions) +{ + png_uint_32 result; + png_colorp palette; + png_bytep trans_alpha; + png_bytepp row_ptr; + png_uint_32 width, height; + int bit_depth, color_type, interlace_type, compression_type, filter_type; + int src_num_palette, src_num_trans; + int last_color_index, last_trans_index; + png_byte crt_trans_value, last_trans_value; + png_byte is_used[256]; + png_color_16 gray_trans; + int is_gray; +#ifdef PNG_bKGD_SUPPORTED + png_color_16p background; +#endif +#ifdef PNG_hIST_SUPPORTED + png_uint_16p hist; +#endif +#ifdef PNG_sBIT_SUPPORTED + png_color_8p sig_bits; +#endif + png_uint_32 i, j; + int k; + + opng_debug(1, "in opng_reduce_palette"); + + png_get_IHDR(png_ptr, info_ptr, &width, &height, &bit_depth, + &color_type, &interlace_type, &compression_type, &filter_type); + row_ptr = png_get_rows(png_ptr, info_ptr); + if (!png_get_PLTE(png_ptr, info_ptr, &palette, &src_num_palette)) + { + palette = NULL; + src_num_palette = 0; + } + if (!png_get_tRNS(png_ptr, info_ptr, &trans_alpha, &src_num_trans, NULL)) + { + trans_alpha = NULL; + src_num_trans = 0; + } + else + OPNG_ASSERT(trans_alpha != NULL && src_num_trans > 0); + + opng_analyze_sample_usage(png_ptr, info_ptr, is_used); + /* Palette-to-gray does not work (yet) if the bit depth is below 8. */ + is_gray = (reductions & OPNG_REDUCE_PALETTE_TO_GRAY) && (bit_depth == 8); + last_color_index = last_trans_index = -1; + for (k = 0; k < 256; ++k) + { + if (!is_used[k]) + continue; + last_color_index = k; + if (k < src_num_trans && trans_alpha[k] < 255) + last_trans_index = k; + if (is_gray) + if (palette[k].red != palette[k].green || + palette[k].red != palette[k].blue) + is_gray = 0; + } + OPNG_ASSERT(last_color_index >= 0); + OPNG_ASSERT(last_color_index >= last_trans_index); + + /* Check the integrity of PLTE and tRNS. */ + if (last_color_index >= src_num_palette) + { + png_warning(png_ptr, "Too few colors in palette"); + /* Fix the palette by adding blank entries at the end. */ + src_num_palette = last_color_index + 1; + opng_set_num_palette(png_ptr, info_ptr, src_num_palette); + } + if (src_num_trans > src_num_palette) + { + png_warning(png_ptr, "Too many alpha values in tRNS"); + /* Transparency will be fixed further below. */ + } + + /* Check if tRNS can be reduced to grayscale. */ + if (is_gray && last_trans_index >= 0) + { + gray_trans.gray = palette[last_trans_index].red; + last_trans_value = trans_alpha[last_trans_index]; + for (k = 0; k <= last_color_index; ++k) + { + if (!is_used[k]) + continue; + if (k <= last_trans_index) + { + crt_trans_value = trans_alpha[k]; + /* Cannot reduce if different colors have transparency. */ + if (crt_trans_value < 255 && palette[k].red != gray_trans.gray) + { + is_gray = 0; + break; + } + } + else + crt_trans_value = 255; + /* Cannot reduce if same color has multiple transparency levels. */ + if (palette[k].red == gray_trans.gray && + crt_trans_value != last_trans_value) + { + is_gray = 0; + break; + } + } + } + + /* Initialize result value. */ + result = OPNG_REDUCE_NONE; + + /* Remove tRNS if it is entirely sterile. */ + if (src_num_trans > 0 && last_trans_index < 0) + { + src_num_trans = 0; + png_free_data(png_ptr, info_ptr, PNG_FREE_TRNS, -1); + png_set_invalid(png_ptr, info_ptr, PNG_INFO_tRNS); + result = OPNG_REDUCE_PALETTE_FAST; + } + + if (reductions & OPNG_REDUCE_PALETTE_FAST) + { + if (src_num_palette != last_color_index + 1) + { + /* Reduce PLTE. */ + /* hIST is reduced automatically. */ + opng_set_num_palette(png_ptr, info_ptr, last_color_index + 1); + result = OPNG_REDUCE_PALETTE_FAST; + } + + if (src_num_trans > 0 && src_num_trans != last_trans_index + 1) + { + /* Reduce tRNS. */ + opng_set_num_trans(png_ptr, info_ptr, last_trans_index + 1); + result = OPNG_REDUCE_PALETTE_FAST; + } + } + + if (reductions & OPNG_REDUCE_8_TO_4_2_1) + { + result |= opng_reduce_palette_bits(png_ptr, info_ptr, reductions); + /* Refresh the image information. */ + bit_depth = png_get_bit_depth(png_ptr, info_ptr); + } + if ((bit_depth < 8) || !is_gray) + return result; + + /* Reduce palette --> grayscale. */ + for (i = 0; i < height; ++i) + { + for (j = 0; j < width; ++j) + row_ptr[i][j] = palette[row_ptr[i][j]].red; + } + + /* Update the ancillary information. */ + if (src_num_trans > 0) + png_set_tRNS(png_ptr, info_ptr, NULL, 0, &gray_trans); +#ifdef PNG_bKGD_SUPPORTED + if (png_get_bKGD(png_ptr, info_ptr, &background)) + background->gray = palette[background->index].red; +#endif +#ifdef PNG_hIST_SUPPORTED + if (png_get_hIST(png_ptr, info_ptr, &hist)) + { + png_free_data(png_ptr, info_ptr, PNG_FREE_HIST, -1); + png_set_invalid(png_ptr, info_ptr, PNG_INFO_hIST); + } +#endif +#ifdef PNG_sBIT_SUPPORTED + if (png_get_sBIT(png_ptr, info_ptr, &sig_bits)) + { + png_byte max_sig_bits = sig_bits->red; + if (max_sig_bits < sig_bits->green) + max_sig_bits = sig_bits->green; + if (max_sig_bits < sig_bits->blue) + max_sig_bits = sig_bits->blue; + sig_bits->gray = max_sig_bits; + } +#endif + + /* Update the image information. */ + png_set_IHDR(png_ptr, info_ptr, width, height, bit_depth, + PNG_COLOR_TYPE_GRAY, interlace_type, compression_type, filter_type); + png_free_data(png_ptr, info_ptr, PNG_FREE_PLTE, -1); + png_set_invalid(png_ptr, info_ptr, PNG_INFO_PLTE); + return OPNG_REDUCE_PALETTE_TO_GRAY; /* ignore the former result */ +} + +/* + * Reduce the image (bit depth + color type + palette) without + * losing any information. The palette (if applicable) and the + * image data must be present (e.g. by calling png_set_rows(), + * or by loading IDAT). + * The parameter reductions indicates the intended reductions. + * The function returns the successful reductions. + */ +png_uint_32 PNGAPI +opng_reduce_image(png_structp png_ptr, png_infop info_ptr, + png_uint_32 reductions) +{ + png_uint_32 result; + int color_type; + + opng_debug(1, "in opng_reduce_image_type"); + + if (!opng_validate_image(png_ptr, info_ptr)) + { + png_warning(png_ptr, + "Image reduction requires the presence of all critical information"); + return OPNG_REDUCE_NONE; + } + + color_type = png_get_color_type(png_ptr, info_ptr); + + /* The reductions below must be applied in this particular order. */ + + /* Try to reduce the high bits and color/alpha channels. */ + result = opng_reduce_bits(png_ptr, info_ptr, reductions); + + /* Try to reduce the palette image. */ + if (color_type == PNG_COLOR_TYPE_PALETTE && + (reductions & + (OPNG_REDUCE_PALETTE_TO_GRAY | + OPNG_REDUCE_PALETTE_FAST | + OPNG_REDUCE_8_TO_4_2_1))) + result |= opng_reduce_palette(png_ptr, info_ptr, reductions); + + /* Try to reduce RGB to palette or grayscale to palette. */ + if (((color_type & ~PNG_COLOR_MASK_ALPHA) == PNG_COLOR_TYPE_GRAY && + (reductions & OPNG_REDUCE_GRAY_TO_PALETTE)) || + ((color_type & ~PNG_COLOR_MASK_ALPHA) == PNG_COLOR_TYPE_RGB && + (reductions & OPNG_REDUCE_RGB_TO_PALETTE))) + { + if (!(result & OPNG_REDUCE_PALETTE_TO_GRAY)) + result |= opng_reduce_to_palette(png_ptr, info_ptr, reductions); + } + + return result; +} + +#endif /* OPNG_IMAGE_REDUCTIONS_SUPPORTED */ diff --git a/library/tools/iff2html/opngreduc.h b/library/tools/iff2html/opngreduc.h new file mode 100644 index 0000000..6e5018c --- /dev/null +++ b/library/tools/iff2html/opngreduc.h @@ -0,0 +1,97 @@ +/* + * opngreduc.h - libpng extension: lossless image reductions. + * + * Copyright (C) 2003-2011 Cosmin Truta. + * This software is distributed under the same licensing and warranty terms + * as libpng. + * + * This code is functional, although it is still work in progress. + * Upon completion, it will be submitted for incorporation into libpng. + */ + +#ifndef OPNGREDUC_H +#define OPNGREDUC_H + +#include + + +#ifdef __cplusplus +extern "C" { +#endif + + +#ifdef PNG_INFO_IMAGE_SUPPORTED + +/* + * Indicate whether the image information is valid, i.e. + * all the required critical information is present in the png structures. + */ +int PNGAPI opng_validate_image(png_structp png_ptr, png_infop info_ptr); + +#endif /* PNG_INFO_IMAGE_SUPPORTED */ + + +#ifndef OPNG_NO_IMAGE_REDUCTIONS +#define OPNG_IMAGE_REDUCTIONS_SUPPORTED +#endif + +#ifdef OPNG_IMAGE_REDUCTIONS_SUPPORTED + +#ifndef PNG_INFO_IMAGE_SUPPORTED +#error OPNG_IMAGE_REDUCTIONS_SUPPORTED requires PNG_INFO_IMAGE_SUPPORTED +#endif + +#ifndef PNG_tRNS_SUPPORTED +#error OPNG_IMAGE_REDUCTIONS_SUPPORTED requires proper transparency support +#endif + +/* + * Reduce the image (bit depth + color type + palette) without + * losing any information. The image data must be present + * (e.g. after calling png_set_rows(), or after loading IDAT). + */ +png_uint_32 PNGAPI opng_reduce_image(png_structp png_ptr, png_infop info_ptr, + png_uint_32 reductions); + +/* + * PNG reduction flags. + */ +#define OPNG_REDUCE_NONE 0x0000 +#define OPNG_REDUCE_16_TO_8 0x0001 /* discard bits 8-15 */ +#define OPNG_REDUCE_8_TO_4_2_1 0x0002 /* discard bits 4-7, 2-7 or 1-7 */ +#define OPNG_REDUCE_RGB_TO_GRAY 0x0004 /* ...also RGBA to GA */ +#define OPNG_REDUCE_STRIP_ALPHA 0x0008 /* ...and create tRNS if needed */ +#define OPNG_REDUCE_RGB_TO_PALETTE 0x0010 /* ...also RGBA to palette/tRNS */ +#define OPNG_REDUCE_PALETTE_TO_RGB 0x0020 /* TODO */ +#define OPNG_REDUCE_GRAY_TO_PALETTE 0x0040 /* ...also GA to palette/tRNS */ +#define OPNG_REDUCE_PALETTE_TO_GRAY 0x0080 /* ...also palette/tRNS to GA */ +#define OPNG_REDUCE_PALETTE_SLOW 0x0100 /* TODO: remove all sterile entries + and reorder PLTE */ +#define OPNG_REDUCE_PALETTE_FAST 0x0200 /* remove trailing sterile entries + only; do not reorder PLTE */ +#define OPNG_REDUCE_ANCILLARY 0x1000 /* TODO */ + +#define OPNG_REDUCE_BIT_DEPTH \ + (OPNG_REDUCE_16_TO_8 | OPNG_REDUCE_8_TO_4_2_1) + +#define OPNG_REDUCE_COLOR_TYPE \ + (OPNG_REDUCE_RGB_TO_GRAY | OPNG_REDUCE_STRIP_ALPHA | \ + OPNG_REDUCE_RGB_TO_PALETTE | OPNG_REDUCE_PALETTE_TO_RGB | \ + OPNG_REDUCE_GRAY_TO_PALETTE | OPNG_REDUCE_PALETTE_TO_GRAY) + +#define OPNG_REDUCE_PALETTE \ + (OPNG_REDUCE_PALETTE_SLOW | OPNG_REDUCE_PALETTE_FAST) + +#define OPNG_REDUCE_ALL \ + (OPNG_REDUCE_BIT_DEPTH | OPNG_REDUCE_COLOR_TYPE | \ + OPNG_REDUCE_PALETTE | OPNG_REDUCE_ANCILLARY) + +#endif /* OPNG_IMAGE_REDUCTIONS_SUPPORTED */ + + +#ifdef __cplusplus +} /* extern "C" */ +#endif + + +#endif /* OPNGREDUC_H */ diff --git a/library/tools/inputtest/Readme.txt b/library/tools/inputtest/Readme.txt new file mode 100644 index 0000000..cb105bb --- /dev/null +++ b/library/tools/inputtest/Readme.txt @@ -0,0 +1 @@ +This is for testing Windows input messages, at the moment. \ No newline at end of file diff --git a/library/tools/inputtest/compile.bat b/library/tools/inputtest/compile.bat new file mode 100644 index 0000000..ccaed26 --- /dev/null +++ b/library/tools/inputtest/compile.bat @@ -0,0 +1 @@ +gcc -Wall -Wextra -Wabi -pedantic -m32 -o inputtest.exe inputtest.cpp -mconsole \ No newline at end of file diff --git a/library/tools/inputtest/inputtest.cpp b/library/tools/inputtest/inputtest.cpp new file mode 100644 index 0000000..0cec43d --- /dev/null +++ b/library/tools/inputtest/inputtest.cpp @@ -0,0 +1,96 @@ +/* + inputtest - Windows input testing + inputtest.cpp - Copyright (c) 2012 Niotso Project + Author(s): Fatbag + + Permission to use, copy, modify, and/or distribute this software for any + purpose with or without fee is hereby granted, provided that the above + copyright notice and this permission notice appear in all copies. + + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +*/ + +#include +#include + +static LRESULT CALLBACK WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam){ + switch(uMsg){ + + case WM_KEYDOWN: + printf("[WM_KEYDOWN] key = %u, lParam = %lu\n", wParam, lParam); + return 0; + case WM_KEYUP: + printf("[WM_KEYUP] key = %u, lParam = %lu\n", wParam, lParam); + return 0; + case WM_CHAR: + printf("[WM_CHAR] key = %u, lParam = %lu\n", wParam, lParam); + return 0; + case WM_DEADCHAR: + printf("[WM_DEADCHAR] key = %u, lParam = %lu\n", wParam, lParam); + return 0; + case WM_SYSKEYDOWN: + printf("[WM_SYSKEYDOWN] key = %u, lParam = %lu\n", wParam, lParam); + return 0; + case WM_SYSKEYUP: + printf("[WM_SYSKEYUP] key = %u, lParam = %lu\n", wParam, lParam); + return 0; + case WM_SYSDEADCHAR: + printf("[WM_SYSDEADCHAR] key = %u, lParam = %lu\n", wParam, lParam); + return 0; + case WM_HOTKEY: + printf("[WM_HOTKEY] keys1 = %u, keys2 = %lu\n", wParam, lParam); + return 0; + case WM_APPCOMMAND: + printf("[WM_APPCOMMAND] cmd = %u, device = %u, keys = %u\n", GET_APPCOMMAND_LPARAM(lParam), GET_DEVICE_LPARAM(lParam), GET_KEYSTATE_LPARAM(lParam)); + return 0; + + case WM_CLOSE: + PostQuitMessage(0); + return 0; + + } + + printf("Received message %u with wParam = %u, lParam = %lu\n", uMsg, wParam, lParam); + return DefWindowProc(hWnd, uMsg, wParam, lParam); +} + +int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE, LPSTR, int) +{ + const WNDCLASS wc = { + 0, //style + WndProc, //lpfnWndProc + 0, //cbClsExtra + 0, //cbWndExtra + hInstance, //hInstance + (HICON) LoadImage(NULL, IDI_APPLICATION, //hIcon + IMAGE_ICON, 0, 0, LR_SHARED | LR_DEFAULTSIZE), + (HCURSOR) LoadImage(NULL, IDC_ARROW, //hCursor + IMAGE_CURSOR, 0, 0, LR_SHARED | LR_DEFAULTSIZE), + (HBRUSH) COLOR_WINDOW, //hbrBackground + NULL, //lpszMenuName + "INPUTTEST" //lpszClassName + }; + + RegisterClass(&wc); + + // Create the main window. + const HWND hWnd = CreateWindowEx(WS_EX_APPWINDOW, "INPUTTEST", "Input test", WS_OVERLAPPEDWINDOW | WS_VISIBLE, + CW_USEDEFAULT, CW_USEDEFAULT, 200, 200, 0, 0, hInstance, NULL); + + SetForegroundWindow(hWnd); + SetFocus(hWnd); + + MSG msg; + while(GetMessage(&msg, NULL, 0, 0)){ + TranslateMessage(&msg); + DispatchMessage(&msg); + } + + return 0; +} \ No newline at end of file diff --git a/library/tools/rtti-reader/CMakeLists.txt b/library/tools/rtti-reader/CMakeLists.txt new file mode 100644 index 0000000..251b0eb --- /dev/null +++ b/library/tools/rtti-reader/CMakeLists.txt @@ -0,0 +1,8 @@ +cmake_minimum_required(VERSION 2.6) +project(rtti-reader) + +set(RTTIREADER_SOURCES + rtti-reader.cpp +) + +add_executable(rtti-reader ${RTTIREADER_SOURCES}) \ No newline at end of file diff --git a/library/tools/rtti-reader/rtti-reader.cpp b/library/tools/rtti-reader/rtti-reader.cpp new file mode 100644 index 0000000..33cb0c6 --- /dev/null +++ b/library/tools/rtti-reader/rtti-reader.cpp @@ -0,0 +1,527 @@ +/* + rtti-reader - The Sims Online MSVC RTTI Class Hierarchy Extractor + rtti-reader.cpp - Copyright (c) 2012 Niotso Project + Author(s): Fatbag + + Permission to use, copy, modify, and/or distribute this software for any + purpose with or without fee is hereby granted, provided that the above + copyright notice and this permission notice appear in all copies. + + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +*/ + +//For information about MSVC RTTI, read: +// +// + +//For information about the Windows PE header, read: +// + +#include +#include +#include +#include +#include +#include + +#ifndef read_int32 + #define read_uint32(x) (unsigned)(((x)[0]<<(8*0)) | ((x)[1]<<(8*1)) | ((x)[2]<<(8*2)) | ((x)[3]<<(8*3))) + #define read_uint16(x) (unsigned)(((x)[0]<<(8*0)) | ((x)[1]<<(8*1))) +#endif +#ifndef write_int32 + #define write_uint32(dest, src) do { \ + (dest)[0] = ((src)&0x000000FF)>>(8*0); \ + (dest)[1] = ((src)&0x0000FF00)>>(8*1); \ + (dest)[2] = ((src)&0x00FF0000)>>(8*2); \ + (dest)[3] = ((src)&0xFF000000)>>(8*3); \ + } while(0) +#endif + +static void Shutdown_M(const char * Message); + +struct Segment { + size_t size, offset; + Segment() : size(0) {} +}; + +struct ByteReaderContext { + size_t start, position, end; + bool seek(size_t pos){ + if(pos > end) + return false; + position = pos; + return true; + } + bool skip(int pos = 1){ + if(position + pos > end) + return false; + position += pos; + return true; + } +}; + +struct PaddingTest { + uint32_t A; + uint32_t B; +}; + +template +struct RTTIVector { + size_t Count, SizeAllocated; + T * Buffer; + + void init(){ + Count = 0, SizeAllocated = sizeof(T); + if(!(Buffer = (T*) malloc(sizeof(T)))) + Shutdown_M("Failed to allocate memory"); + } + T& add(){ + if((Count+1)*sizeof(T) > SizeAllocated){ + void * ptr; + if(SizeAllocated > SIZE_MAX/2 || !(ptr = (T*) realloc(Buffer, SizeAllocated<<=1))) + Shutdown_M("Failed to allocate memory"); + Buffer = (T *) ptr; + } + + return Buffer[Count++]; + } +}; + +struct RTTITypeDescriptor { + struct { + uint32_t Address; + uint32_t VTableAddress; + uint32_t Reserved; + } Fields; + char * Name; + char * UnmangledName; +}; + +struct RTTIBaseClassDescriptor { + struct { + uint32_t Address; + uint32_t TypeDescriptorAddress; + uint32_t BaseClassCount; + uint32_t MemberOffset; + uint32_t COLAddressOffset; + uint32_t VTableOffset; + uint32_t Attributes; + } Fields; + RTTITypeDescriptor TD; +}; + +struct RTTIClassHierarchyDescriptor { + struct { + uint32_t Address; + uint32_t Reserved; + uint32_t Attributes; + uint32_t BaseClassCount; + uint32_t BaseClassListAddress; + } Fields; + RTTIVector BCDL; +}; + +struct RTTICompleteObjectLocator { + struct { + uint32_t Address; + uint32_t Reserved; + uint32_t Offset; + uint32_t CDOffset; + uint32_t TypeDescriptorAddress; + uint32_t ClassDescriptorAddress; + } Fields; + uint32_t VTableAddress; +}; + +struct RTTIClass { + RTTIVector COLL; + RTTITypeDescriptor TD; + RTTIClassHierarchyDescriptor CHD; + void init(){ + COLL.init(); + CHD.BCDL.init(); + } + bool DependsOn(const RTTIClass& X) const { + for(uint32_t i=1; i(Aptr); + const RTTIClass& B = *reinterpret_cast(Bptr); + + if(A.DependsOn(B)) return 1; //If A depends on B, A > B + if(B.DependsOn(A)) return -1; //If B depends on A, B > A + return strcmp(A.TD.UnmangledName, B.TD.UnmangledName); + } +}; + +struct PEFile { + static PEFile * ptr; + FILE * hFile; + uint8_t * Data; + Segment rdata, data; + ByteReaderContext brc; + + PEFile(const char * filename) : Data(NULL) { + PEFile::ptr = this; + + hFile = fopen(filename, "rb"); + if(!hFile) + Shutdown_M("The specified input file does not exist or could not be opened for reading"); + + fseek(hFile, 0, SEEK_END); + size_t FileSize = ftell(hFile); + if(FileSize < 64) + Shutdown_M("Not a valid Windows PE file"); + fseek(hFile, 0, SEEK_SET); + + Data = (uint8_t*) malloc(FileSize); + if(!Data) + Shutdown_M("Failed to allocate memory"); + if(fread(Data, 1, FileSize, hFile) != FileSize) + Shutdown_M("Failed to read input file"); + + fclose(hFile); + + brc.start = brc.position = 0; + brc.end = FileSize; + } + ~PEFile(){ + if(hFile) + fclose(hFile); + free(Data); + } + + inline bool seek(size_t pos, int offset = 0){ + return brc.seek(pos + offset); + } + + inline bool skip(size_t pos = 1, int offset = 0){ + return brc.skip(pos + offset); + } + int nextchar(){ + if(!brc.skip()) + return EOF; + return Data[brc.position-1]; + } + void lookat(Segment& segment){ + brc.start = brc.position = segment.offset; + brc.end = segment.offset + segment.size; + } + + uint32_t read32(){ + return brc.skip(4) ? read_uint32(Data+brc.position-4) : -1; + } + uint16_t read16(){ + return brc.skip(2) ? read_uint16(Data+brc.position-2) : -1; + } + size_t strlen(){ + size_t i = (size_t)-1; + int byte; + do { + byte = nextchar(); + if(byte == EOF) + return -1; + i++; + } while(byte); + skip(-(int)i-1); //Seek back + return i; + } + bool strcpy(char * dest){ + int i = 0; + do { + int byte = nextchar(); + if(byte == EOF) + return false; + *dest = (char) byte; + i--; + } while(*dest++); + skip(i); //Seek back + return true; + } + int strcmp(const char * data){ + int i = 0; + int byte; + do { + byte = nextchar(); + if(byte == EOF) + return -1; + i--; + } while(*data++ && (char)byte == *(data-1)); + skip(i); //Seek back + return byte - *(data-1); + } + enum { Parse_QuestionMark = 1}; + bool memfind(const char * data, size_t size, int MemParse = 0){ + size_t i = 0; + do { + int byte = nextchar(); + if(byte == EOF) + return false; + else if((char)byte != data[i] && (!MemParse || data[i] != '?')){ + skip(-(int)i); + i = 0; + } else i++; + } while(i brc.end - brc.position) + return false; + + uint32_t *field = reinterpret_cast(ptr); + *field = brc.position; //The Address field always comes first + + do { + ptr += padding; count -= padding; + field = reinterpret_cast(ptr); + *field = read32(); + } while(count); + + return true; + } + + template + inline bool Fill(T& context) { + return GenericFill(reinterpret_cast(&context.Fields), sizeof(context.Fields)); + } +}; + +PEFile * PEFile::ptr; + +static void Shutdown_M(const char * Message){ + fprintf(stderr, "rtti-reader: error: %s.\n", Message); + if(PEFile::ptr) + PEFile::ptr->~PEFile(); + exit(EXIT_FAILURE); +} + +int main(int argc, char *argv[]){ + unsigned i; + const char * InFile, * BaseName; + + if(argc != 2 || !strcmp(argv[1], "-h") || !strcmp(argv[1], "--help")){ + printf("Usage: rtti-reader infile\n" + "Extract class information from an EXE or DLL using MSVC RTTI.\n" + "\n" + "Report bugs to .\n" + "rtti-reader is maintained by the Niotso project.\n" + "Home page: \n"); + return 0; + } + + InFile = argv[1]; + + int slash; + for(i=0, slash=-1; InFile[i]; i++) + if(InFile[i] == '/' || InFile[i] == '\\') slash = i; + BaseName = InFile + slash + 1; + + PEFile DLL(InFile); + if(DLL.read16() != 0x5A4D) //"MZ" + Shutdown_M("Not a valid Windows PE file"); + + DLL.seek(60); + DLL.seek(DLL.read32(), 6); unsigned SegmentCount = DLL.read16(); + DLL.skip(12); unsigned OptionalHeaderSize = DLL.read16(); + DLL.skip(30); unsigned ImageBase = DLL.read32(); + DLL.skip(OptionalHeaderSize, -32); + + for(i=0; i UINT_MAX-DLL.rdata.offset || DLL.rdata.size+DLL.rdata.offset > DLL.brc.end) + Shutdown_M(".rdata segment is invalid"); + else if(DLL.data.size > UINT_MAX-DLL.data.offset || DLL.data.size+DLL.data.offset > DLL.brc.end) + Shutdown_M(".data segment is invalid"); + + printf("\n****\n** [ 1 of 2] RTTI Report for %s\n****\n", BaseName); + + RTTIVector RCL; + RCL.init(); + + DLL.lookat(DLL.data); + unsigned TotalClassCount = 0; + while(DLL.skip(8) && DLL.memfind(".?AV", 4, PEFile::Parse_QuestionMark)){ + TotalClassCount++; + size_t length = DLL.strlen(); + if(length == (unsigned)-1) + Shutdown_M("Unexpectedly reached end of binary"); + + size_t TDAddress = DLL.brc.position + ImageBase - 8, datapos = DLL.brc.position + length + 1; + DLL.lookat(DLL.rdata); + + RTTIClass * RCPointer = NULL; + + for(size_t rdatapos = DLL.brc.position + 12; + DLL.seek(rdatapos) && DLL.find32(TDAddress); rdatapos += 4, DLL.lookat(DLL.rdata)){ + //Find all complete object locators that belong to this class + rdatapos = DLL.brc.position; + if(!DLL.skip(4)) + continue; + size_t CDAddress = DLL.read32() - ImageBase; + if(CDAddress < DLL.brc.start || CDAddress > DLL.brc.end-4) + continue; //This was a base class descriptor + + //Add this COL to our respective RTTIClass + bool newclass = false; + if(RCPointer == NULL){ + //This is a new class; add it to the RCL + newclass = true; + RTTIClass& RC = RCL.add(); + RCPointer = &RC; + RC.init(); + } + RTTIClass& RC = *RCPointer; + + RTTICompleteObjectLocator& COL = RC.COLL.add(); + + DLL.seek(rdatapos,-12); + size_t COLAddress = DLL.brc.position + ImageBase; + if(!DLL.Fill(COL)) + Shutdown_M("Unexpectedly reached end of binary"); + + DLL.lookat(DLL.rdata); + COL.VTableAddress = (DLL.find32(COLAddress)) ? DLL.brc.position + ImageBase + 4 : (uint32_t)-1; + + if(newclass){ + if(!DLL.seek(COL.Fields.ClassDescriptorAddress - ImageBase)) + Shutdown_M("Unexpectedly reached end of binary"); + RTTIClassHierarchyDescriptor& CHD = RC.CHD; + if(!DLL.Fill(CHD)) + Shutdown_M("Unexpectedly reached end of binary"); + + if(!DLL.seek(CHD.Fields.BaseClassListAddress - ImageBase)) + Shutdown_M("Unexpectedly reached end of binary"); + size_t bcdlpos; + for(i=0, bcdlpos = DLL.brc.position; i= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') || + (c >= '0' && c <= '9') || c == '_') + ? c : ' '; + } + BCD.TD.UnmangledName[length-6] = '\0'; + + if(newclass){ + newclass = false; + memcpy(&RC.TD, &BCD.TD, sizeof(RTTITypeDescriptor)); + } + } + } + } + + DLL.lookat(DLL.data); + DLL.seek(datapos); + } + + for (i=0; i 1){ + //The first BCD always refers to the class itself, e.g. class A "depends on class A". + printf(" : %s", RC.CHD.BCDL.Buffer[1].TD.UnmangledName); + for(uint32_t j=2; j + Author(s): Ahmed El-Mahdawy + + Permission to use, copy, modify, and/or distribute this software for any + purpose with or without fee is hereby granted, provided that the above + copyright notice and this permission notice appear in all copies. + + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +*/ + +#include +#include +#include "tsoscan.h" + +CommandLineArgs* cmd_parse_args(int argc, char *argv[]){ + CommandLineArgs *CmdArgs = calloc(1, sizeof(CommandLineArgs)); + int i, InDirIndex = 0; + + if(CmdArgs == NULL) return 0; + + for(i=1; iInDirCount++; + } + if(CmdArgs->InDirCount > 0){ + CmdArgs->InDirs = calloc(CmdArgs->InDirCount, sizeof(char*)); + if(CmdArgs->InDirs == NULL) return 0; + } + + for(i=1; iForceWrite = 1; + }else if(!strcmp(argv[i], "-o")){ + if(i == argc-1 || CmdArgs->OutFile != NULL) + return NULL; + CmdArgs->OutFile = argv[++i]; + }else{ + CmdArgs->InDirs[InDirIndex] = argv[i]; + InDirIndex++; + } + } + + return CmdArgs; +} + +void cmd_delete(CommandLineArgs *args){ + unsigned i; + + if(args == NULL) return; + if(args->InDirs != NULL){ + for(i=0; iInDirCount; i++) + free(args->InDirs[i]); + free(args->InDirs); + } + free(args->OutFile); + free(args); +} \ No newline at end of file diff --git a/library/tools/tsoscan/stats.c b/library/tools/tsoscan/stats.c new file mode 100644 index 0000000..fa35f0b --- /dev/null +++ b/library/tools/tsoscan/stats.c @@ -0,0 +1,108 @@ +/* + tsoscan - IFF statistical webpage generator + stats.c - Copyright (c) 2012 Niotso Project + Author(s): Ahmed El-Mahdawy + + Permission to use, copy, modify, and/or distribute this software for any + purpose with or without fee is hereby granted, provided that the above + copyright notice and this permission notice appear in all copies. + + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +*/ + +#include +#include +#include +#include "tsoscan.h" + +int stats_create(IFFStats *stats){ + stats->AverageChunkCount = -1; + return 0; +} + +int stats_version_increment(IFFStats *stats, char *type, unsigned version){ + ChunkStats *chunk = NULL; + VersionInfo *verinfo = NULL; + unsigned i; + + if(stats == NULL) return 0; + if(type == NULL) return 0; + + for(i=0; iChunkTypeCount; i++){ + if(!strcmp(stats->ChunkTypes[i].Type, type)){ + chunk = stats->ChunkTypes+i; + if(chunk == NULL) return 0; + } + } + if(chunk == NULL){ + stats->ChunkTypes = realloc(stats->ChunkTypes, ++(stats->ChunkTypeCount)*sizeof(ChunkStats)); + if(stats->ChunkTypes == NULL) return 0; + chunk = stats->ChunkTypes + stats->ChunkTypeCount - 1; + memset(chunk, 0, sizeof(ChunkStats)); + strcpy(chunk->Type, type); + } + chunk->ChunkCount++; + + for(i=0; iVersionCount; i++){ + if(chunk->Versions[i].Version == version){ + verinfo = chunk->Versions+i; + if(verinfo == NULL) return 0; + } + } + if(verinfo == NULL){ + chunk->Versions = realloc(chunk->Versions, ++(chunk->VersionCount)*sizeof(VersionInfo)); + if(chunk->Versions == NULL) return 0; + verinfo = chunk->Versions + chunk->VersionCount - 1; + memset(verinfo, 0, sizeof(VersionInfo)); + verinfo->Version = version; + } + verinfo->Count++; + + return 1; +} + +unsigned stats_get_version(char *type, uint8_t *data){ + /* I could've used iff_parse_chunk instead to determine chunk versions, but this + would be unnecessarily slow and would require all chunk parsers to be written, + defeating the purpose of this tool. */ + + if(!strcmp(type, "STR#") || !strcmp(type, "CTSS") || !strcmp(type, "FAMs") || + !strcmp(type, "TTAs") || !strcmp(type, "CST") || !strcmp(type, "BHAV") || + !strcmp(type, "DGRP") || !strcmp(type, "POSI")) + return read_uint16le(data); + + if(!strcmp(type, "FCNS") || !strcmp(type, "OBJf") || !strcmp(type, "Optn") || + !strcmp(type, "Rcon") || !strcmp(type, "TPRP") || !strcmp(type, "SLOT") || + !strcmp(type, "TRCN") || !strcmp(type, "rsmp")) + return read_uint32le(data+4); + + if(!strcmp(type, "OBJD") || !strcmp(type, "PALT") || !strcmp(type, "SPR2")) + return read_uint32le(data); + + if(!strcmp(type, "TTAB")) + return read_uint16le(data+2); + + if(!strcmp(type, "SPR#")){ + if(data[0] == 0) return read_uint32be(data); + else return read_uint32le(data); + } + + return -1; +} + +void stats_delete(IFFStats *stats){ + unsigned i; + + if(stats == NULL) return; + if(stats->ChunkTypes != NULL){ + for(i=0; iChunkTypeCount; i++) + free(stats->ChunkTypes[i].Versions); + free(stats->ChunkTypes); + } +} \ No newline at end of file diff --git a/library/tools/tsoscan/tsoscan.c b/library/tools/tsoscan/tsoscan.c new file mode 100644 index 0000000..ec11fdb --- /dev/null +++ b/library/tools/tsoscan/tsoscan.c @@ -0,0 +1,328 @@ +/* + tsoscan - IFF statistical webpage generator + tsoscan.c - Copyright (c) 2012 Niotso Project + Author(s): Ahmed El-Mahdawy + Fatbag + + Permission to use, copy, modify, and/or distribute this software for any + purpose with or without fee is hereby granted, provided that the above + copyright notice and this permission notice appear in all copies. + + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +*/ + +#include +#include +#include +#include +#include +#include "tsoscan.h" + +static void print_usage(){ + printf("Usage: tsoscan [-f] [-o outfile] indir1 [indir2 [...]]\n" + "Generate a statistical HTML page based on a number of IFF files.\n" + "Use -f to force overwriting without confirmation.\n" + "If outfile is unspecified, the output HTML page will be written to stats.html.\n" + "\n" + "Report bugs to .\n" + "tsoscan is maintained by the Niotso project.\n" + "Home page: \n"); +} + +int main(int argc, char *argv[]){ + CommandLineArgs *CmdArgs; + unsigned i, version, FileCount = 0; + char **Files = NULL; + IFFStats Stats; + FILE *OutFile; + + if(!stats_create(&Stats)){ + fprintf(stderr, "%sUnable to allocate enough memory.\n", TSOSCAN_ERROR); + return -1; + } + + if(argc == 1 || !strcmp(argv[1], "-h") || !strcmp(argv[1], "--help")){ + print_usage(); + return 0; + } + + CmdArgs = cmd_parse_args(argc, argv); + if(CmdArgs == NULL || CmdArgs->InDirCount == 0){ + print_usage(); + return -1; + } + if(CmdArgs->OutFile == NULL){ + CmdArgs->OutFile = "stats.html"; + } + + /**** + ** List selected input directories + */ + + for(i=0; iInDirCount; i++){ + DIR *dir = opendir(CmdArgs->InDirs[i]); + struct dirent *entry; + unsigned DirStartIndex; + + if(dir == NULL){ + fprintf(stderr, "%sUnable to open the specified directory '%s'. Skipping.\n", TSOSCAN_WARNING, CmdArgs->InDirs[i]); + continue; + } + + DirStartIndex = FileCount; + while((entry = readdir(dir)) != NULL){ + if(strcmp(entry->d_name, ".") && strcmp(entry->d_name, "..")) + FileCount++; + } + rewinddir(dir); + Files = realloc(Files, FileCount*sizeof(char**)); + if(Files == NULL){ + fprintf(stderr, "%sUnable to allocate enough memory.\n", TSOSCAN_ERROR); + return -1; + } + + for(; DirStartIndexd_name, ".") && strcmp(entry->d_name, "..")){ + int dirlen = strlen(CmdArgs->InDirs[i]); + int pathlen = strlen(entry->d_name); + Files[DirStartIndex] = malloc(dirlen+pathlen+2); + if(Files[DirStartIndex] == NULL){ + fprintf(stderr, "%sUnable to allocate enough memory.\n", TSOSCAN_ERROR); + return -1; + } + + memcpy(Files[DirStartIndex], CmdArgs->InDirs[i], dirlen); + Files[DirStartIndex][dirlen] = PATH_SEP; + memcpy(Files[DirStartIndex]+dirlen+1, entry->d_name, pathlen); + Files[DirStartIndex][dirlen+pathlen+1] = '\0'; + + DirStartIndex++; + } + } + + closedir(dir); + } + + /**** + ** Load and parse IFF files + */ + + for(i=0; iType, ChunkData->Data); + if(!stats_version_increment(&Stats, ChunkData->Type, version)){ + fprintf(stderr, "%sUnable to allocate enough memory.\n", TSOSCAN_ERROR); + return -1; + } + } + iff_delete(&iff); + } + + /**** + ** Write output file + */ + + if(!CmdArgs->ForceWrite){ + OutFile = fopen(CmdArgs->OutFile, "rb"); + if(OutFile != NULL){ + char c; + fclose(OutFile); + printf("File \"%s\" exists. Continue anyway? (y/n) ", CmdArgs->OutFile); + c = getchar(); + if(c != 'y' && c != 'Y') + return -1; + } + } + OutFile = fopen(CmdArgs->OutFile, "wb"); + if(OutFile == NULL){ + fprintf(stderr, "%sThe output file '%s' could not be opened for writing.", TSOSCAN_ERROR, CmdArgs->OutFile); + return -1; + } + + fprintf(OutFile, + "\n"); + fprintf(OutFile, "\n"); + fprintf(OutFile, "\n"); + fprintf(OutFile, "\n"); + fprintf(OutFile, "\n"); + fprintf(OutFile, "\n"); + fprintf(OutFile, "\n"); + fprintf(OutFile, "\n"); + fprintf(OutFile, "IFF Chunk Statistics (tsostats)\n"); + fprintf(OutFile, "\n"); + fprintf(OutFile, "\n"); + fprintf(OutFile, "\n"); + fprintf(OutFile, "

IFF Chunk Statistics (tsostats)

\n"); + fprintf(OutFile, "
\n"); + fprintf(OutFile, "\n"); + fprintf(OutFile, "\n", Stats.FileCount); + fprintf(OutFile, "\n", Stats.AverageChunkCount); + fprintf(OutFile, "
Number of IFF files:%u
Average chunk count:%.1f
\n"); + fprintf(OutFile, "
\n"); + + fprintf(OutFile, "
Contents – %u chunk types
\n", Stats.ChunkTypeCount); + fprintf(OutFile, "
    \n"); + for(i=0; i%u %s\n", i, i+1, Stats.ChunkTypes[i].Type); + fprintf(OutFile, "
\n"); + fprintf(OutFile, "
\n"); + fprintf(OutFile, "\n"); + + for(i=0; i%u %s (Jump)\n", i, i+1, Stats.ChunkTypes[i].Type, i); + fprintf(OutFile, "
\n"); + + fprintf(OutFile, "\n"); + fprintf(OutFile, "\n", chunk->ChunkCount); + if(chunk->VersionCount == 1 && chunk->Versions[0].Version == (unsigned)-1) + fprintf(OutFile, "\n"); + else + fprintf(OutFile, "\n", chunk->VersionCount); + fprintf(OutFile, "
Number of occurrences:%u
Number of versions:N/A
Number of versions:%u
\n"); + + if(chunk->VersionCount > 1 || + (chunk->VersionCount == 1 && chunk->Versions[0].Version != (unsigned)-1)){ + fprintf(OutFile, "\n"); + fprintf(OutFile, "\n"); + for(version=0; versionVersionCount; version++){ + VersionInfo *verinfo = chunk->Versions+version; + float percentage = (float)verinfo->Count / chunk->ChunkCount * 100; + + fprintf(OutFile, "\n", + version+1, verinfo->Version, verinfo->Version, verinfo->Count, percentage); + } + fprintf(OutFile, "
VersionCount
%u%u (0x%x)%u (%.1f%%)
\n"); + } + + fprintf(OutFile, "
\n\n"); + } + + fprintf(OutFile, + "
This page was generated by the use of tsostats.
\n"); + fprintf(OutFile, "\n"); + fprintf(OutFile, ""); + fclose(OutFile); + + printf("Generated statistics based on %u IFF files.\n", Stats.FileCount); + cmd_delete(CmdArgs); + stats_delete(&Stats); + return 0; +} \ No newline at end of file diff --git a/library/tools/tsoscan/tsoscan.h b/library/tools/tsoscan/tsoscan.h new file mode 100644 index 0000000..4351496 --- /dev/null +++ b/library/tools/tsoscan/tsoscan.h @@ -0,0 +1,65 @@ +/* + tsoscan - IFF statistical webpage generator + tsoscan.h - Copyright (c) 2012 Niotso Project + Author(s): Ahmed El-Mahdawy + + Permission to use, copy, modify, and/or distribute this software for any + purpose with or without fee is hereby granted, provided that the above + copyright notice and this permission notice appear in all copies. + + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +*/ + +#include + +#define TSOSCAN_WARNING "tsoscan: warning: " +#define TSOSCAN_ERROR "tsoscan: error: " +#ifdef _WIN32 + #define PATH_SEP '\\' +#else + #define PATH_SEP '/' +#endif + +typedef struct CommandLineArgs_s +{ + int ForceWrite; + char * OutFile; + unsigned InDirCount; + char ** InDirs; +} CommandLineArgs; + +typedef struct VersionInfo_s +{ + unsigned Version; + unsigned Count; +} VersionInfo; + +typedef struct ChunkStats_s +{ + char Type[5]; + unsigned ChunkCount; + unsigned VersionCount; + VersionInfo * Versions; +} ChunkStats; + +typedef struct IFFStats_s +{ + unsigned FileCount; + float AverageChunkCount; + unsigned ChunkTypeCount; + ChunkStats * ChunkTypes; +} IFFStats; + +CommandLineArgs* cmd_parse_args(int argc, char *argv[]); +void cmd_delete(CommandLineArgs *args); + +int stats_create(IFFStats *stats); +int stats_version_increment(IFFStats *stats, char *type, unsigned version); +unsigned stats_get_version(char *type, uint8_t *data); +void stats_delete(IFFStats *stats); \ No newline at end of file diff --git a/library/tools/zbuffer/Instructions.txt b/library/tools/zbuffer/Instructions.txt new file mode 100644 index 0000000..cd399b4 --- /dev/null +++ b/library/tools/zbuffer/Instructions.txt @@ -0,0 +1,11 @@ +Add these files: + +* Chair-Liv-Deco_large_front_a.png +* Chair-Liv-Deco_large_front_p.png +* Chair-Liv-Deco_large_front_z.png +* tower_large_ne_a.png +* tower_large_ne_p.png +* tower_large_ne_z.png + +Then open index.html in a web browser which supports the HTML5 canvas, +and use the arrow keys. \ No newline at end of file diff --git a/library/tools/zbuffer/background.png b/library/tools/zbuffer/background.png new file mode 100644 index 0000000..ef4c612 Binary files /dev/null and b/library/tools/zbuffer/background.png differ diff --git a/library/tools/zbuffer/index.html b/library/tools/zbuffer/index.html new file mode 100644 index 0000000..e863077 --- /dev/null +++ b/library/tools/zbuffer/index.html @@ -0,0 +1,152 @@ + + +
+ \ No newline at end of file