iff2html working

This commit is contained in:
Jip 2024-05-06 19:40:19 +02:00
parent deaf3327e0
commit 4cd716e94d
89 changed files with 7711 additions and 3 deletions

View file

@ -141,9 +141,9 @@ endif()
add_subdirectory(formats) add_subdirectory(formats)
add_subdirectory(libgldemo) add_subdirectory(libgldemo)
add_subdirectory(libvitaboy) add_subdirectory(libvitaboy)
add_subdirectory(tools)
if(BUILD_EXAMPLES) if(BUILD_EXAMPLES)
add_subdirectory(renderdemo_ray)
add_subdirectory(rlgldemo) add_subdirectory(rlgldemo)
endif() endif()

View file

@ -0,0 +1,5 @@
add_subdirectory(FARDive)
add_subdirectory(hitutils)
add_subdirectory(iff2html)
add_subdirectory(rtti-reader)
add_subdirectory(tsoscan)

View file

@ -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()

View file

@ -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();
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 409 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 518 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 614 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 405 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 598 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 346 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 616 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 666 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 682 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 404 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 507 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 328 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 396 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 607 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 591 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 610 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 596 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 822 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 758 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 217 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 188 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 357 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 512 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 469 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 384 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 351 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 247 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 725 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 610 B

View file

@ -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

View file

@ -0,0 +1 @@
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0"><dependency><dependentAssembly><assemblyIdentity type="win32" name="Microsoft.Windows.Common-Controls" version="6.0.0.0" processorArchitecture="*" publicKeyToken="6595b64144ccf1df"/></dependentAssembly></dependency></assembly>

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

View file

@ -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 <http://gcc.gnu.org/onlinedocs/cpp/Stringification.html>
#define FDVERSION L"" \
xstr(FD_VERSION_A) \
L"." \
xstr(FD_VERSION_B) \
L"." \
xstr(FD_VERSION_C) \
L" (rev. " \
xstr(FD_REVISION) \
L")"

View file

@ -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;
}
}

View file

@ -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;
}
}

View file

@ -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);

View file

@ -0,0 +1,174 @@
#include "Windows.hpp"
#include <uxtheme.h>
#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;
}
}

View file

@ -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 <X-Fi6@phppoll.org>\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;
}

View file

@ -0,0 +1,67 @@
#include <windows.h>
#include <png.h>
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<height; i++)
row_pointers[i] = buffer + width*4*(height-i-1);
png_read_image(png_ptr, row_pointers);
png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
//Convert from BGRA to pre-multiplied BGRA (PBGRA)
//Description here: http://msdn.microsoft.com/en-us/library/ee719797%28VS.85%29.aspx
for(unsigned i=0; i<width*height*4; i+=4){
unsigned alpha = buffer[i+3];
int j;
for(j=0; j<3; j++)
buffer[i+j] = (char)((unsigned) buffer[i+j]*alpha/255);
}
return hBmp;
}

View file

@ -0,0 +1,78 @@
#include "Windows.hpp"
#include "GUI.hpp"
#include <png.h>
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<MENUICONS; i++){
int ID = iconmenulist[i];
mii.hbmpItem = PNGIcon[i] = ReadPNGIcon(ID);
SetMenuItemInfo(hMenu, ID-1000, FALSE, &mii);
}
HACCEL acceltable = LoadAccelerators(hInst, MAKEINTRESOURCE(ID_ACCELERATOR));
MSG msg;
while(GetMessage(&msg, NULL, 0, 0)){
TranslateAccelerator(hWnd, acceltable, &msg);
DispatchMessage(&msg);
}
return 0;
}

View file

@ -0,0 +1,12 @@
#define UNICODE
#define WINVER 0x0600
#include <windows.h>
#include <commctrl.h>
#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);

View file

@ -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);
}

View file

@ -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

View file

@ -0,0 +1,170 @@
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#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 <http://msdn.microsoft.com/en-us/library/aa381058.aspx>
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

View file

View file

@ -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.

View file

@ -0,0 +1,52 @@
/*
TSOEdithEditor - TSOEdithEditorD.dll injector
TSOEdithEditor.cpp - Copyright (c) 2012 Niotso Project <http://niotso.org/>
Author(s): Fatbag <X-Fi6@phppoll.org>
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 <stdio.h>
#include <windows.h>
#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;
}

View file

@ -0,0 +1,144 @@
/*
TSOEdithEditor - TSOEdithEditorD.dll injector
TSOEdithEditor.hpp - Copyright (c) 2012 Fatbag <X-Fi6@phppoll.org>
Author(s): Fatbag <X-Fi6@phppoll.org>
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 <basetyps.h>
#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;
};

View file

@ -0,0 +1 @@
gcc -Wall -Wextra -Wabi -pedantic -fno-exceptions -m32 -o TSOEdithEditor.exe TSOEdithEditor.cpp -mconsole

View file

@ -0,0 +1,46 @@
/*
TSOSimulatorClient - TSOSimulatorClientD.dll injector
TSOSimulatorClient.cpp - Copyright (c) 2012 Niotso Project <http://niotso.org/>
Author(s): Fatbag <X-Fi6@phppoll.org>
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 <stdio.h>
#include <windows.h>
#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;
}

View file

@ -0,0 +1,70 @@
/*
TSOSimulatorClient - TSOSimulatorClientD.dll injector
TSOSimulatorClient.hpp - Copyright (c) 2012 Fatbag <X-Fi6@phppoll.org>
Author(s): Fatbag <X-Fi6@phppoll.org>
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 <basetyps.h>
#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;
};

View file

@ -0,0 +1 @@
gcc -Wall -Wextra -Wabi -pedantic -fno-exceptions -m32 -o TSOSimulatorClient.exe TSOSimulatorClient.cpp -mconsole

View file

@ -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)

View file

@ -0,0 +1,32 @@
/*
The Sims Online Translation Tool - Graphical Translation Writer for TSO
TranslateTool.hpp - Copyright (c) 2012 Niotso Project <http://niotso.org/>
Author(s): Fatbag <X-Fi6@phppoll.org>
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();
}

View file

@ -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})

View file

@ -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_").

Binary file not shown.

View file

@ -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) (?? ?? ?? ??)

View file

@ -0,0 +1,602 @@
/*
hitutils - The Sims HIT (dis)assembler and linker
hitasm.c - Copyright (c) 2012 Niotso Project <http://niotso.org/>
Author(s): Fatbag <X-Fi6@phppoll.org>
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 <stdarg.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <stdint.h>
#include <errno.h>
#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<Section[SymbolTable].Position; p+=16){
if(!strcmp((char*) Section[StringTable].Data + read_uint32(Section[SymbolTable].Data + p), Name)){
if(SymbolIndex) *SymbolIndex = 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; i<ConstantCount; i++){
if(!strcmp(Constants[i].Name, pc->Token)){
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; i<InstructionCount; i++){
if(!strcmp(pc->Token, 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; i<VariableCount; i++)
if(!strcmp(Variables[i].Name, pc->Token))
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<filecount; i++){
free(path[i]);
free(data[i]);
}
for(i=0; i<SectionCount; i++)
free(Section[i].Data);
if(hFile)
fclose(hFile);
}
int main(int argc, char *argv[]){
unsigned i;
int SimsVersion = 0;
int overwrite = 0;
size_t filesize[filecount-1];
unsigned slash;
const variable_t * Variables;
size_t VariableCount;
ParserContext pc;
int InBinary = 0;
uint8_t * SectionHeader;
size_t SectionOffset;
if(argc == 1 || !strcmp(argv[1], "-h") || !strcmp(argv[1], "--help")){
printf("Usage: hitasm [-ts1|-tso] [-f] [-o outfile.o] infile.txt\n"
"Assemble a HIT source file to an intermediary object file\n"
"which can be linked using hitld.\n"
"\n"
"Use -f to force overwriting without confirmation.\n"
"\n"
"Report bugs to <X-Fi6@phppoll.org>.\n"
"hitutils is maintained by the Niotso project.\n"
"Home page: <http://www.niotso.org/>\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<filecount; i++)
if(path[i])
path[i] = strdup(path[i]); /* necessary for free(path[i]) in Shutdown_M */
if(!SimsVersion)
Shutdown_M("%sSims version not specified. (Use -ts1 or -tso.)\n", "hitasm: Error: ");
if(SimsVersion == VERSION_TS1){
Variables = TS1Variables;
VariableCount = TS1VariableCount;
}else{
Variables = TSOVariables;
VariableCount = TSOVariableCount;
}
if(path[out] == NULL){
int length = strlen(path[txt]);
path[out] = malloc(max(length+1, 3));
strcpy(path[out], path[txt]);
strcpy(path[out] + max(length-4, 0), ".o");
}
/****
** Read all of the requested files
*/
for(i=0; i<filecount-1; i++){
size_t bytestransferred;
hFile = fopen(path[i], "rb");
if(hFile == NULL)
Shutdown_M("%sCould not open file: %s.\n", "hitasm: Error: ", path[i]);
fseek(hFile, 0, SEEK_END);
filesize[i] = ftell(hFile);
if(filesize[i] == 0 || filesize[i] == SIZE_MAX)
Shutdown_M("%sFile is invalid: %s.\n", "hitasm: Error: ", path[i]);
data[i] = malloc(filesize[i]+1);
if(data[i] == NULL)
Shutdown_M("%sCould not allocate memory for file: %s.\n", "hitasm: Error: ", path[i]);
fseek(hFile, 0, SEEK_SET);
bytestransferred = fread(data[i], 1, filesize[i], hFile);
fclose(hFile); hFile = NULL;
if(bytestransferred != filesize[i])
Shutdown_M("%sCould not read file: %s.\n", "hitasm: Error: ", path[i]);
data[i][filesize[i]++] = '\0'; /* add a null character to the end of the file */
}
/****
** Open the output file for writing
*/
if(!overwrite){
hFile = fopen(path[out], "rb");
if(hFile != NULL){
/* File exists */
char c;
fclose(hFile); hFile = NULL;
fprintf(stderr, "%sFile \"%s\" exists.\nContinue anyway? (y/n) ", "hitasm: ", path[out]);
c = getchar();
if(c != 'y' && c != 'Y')
Shutdown_M("\nAborted.\n");
}
}
hFile = fopen(path[out], "wb");
if(hFile == NULL)
Shutdown_M("%sCould not open file: %s.\n", "hitasm: Error: ", path[out]);
/****
** Perform the assembly
*/
for(i=0; i<SectionCount; i++){
Section[i].Data = malloc(Section[Text].Size);
if(!Section[i].Data)
Shutdown_M("%sCould not allocate memory for %s section.\n", "hitasm: Error: ", Section[i].Name);
}
pc.NextLine = 1;
pc.NextCol = 1;
pc.GaveLastToken = 1;
pc.brc.Data = data[txt];
pc.brc.Size = filesize[txt];
bw_write_memory(&Section[SymbolTable], SymbolTableHeader, sizeof(SymbolTableHeader));
for(i=slash=0; path[txt][i]; i++)
if(path[txt][i] == '/' || path[txt][i] == '\\') slash = i+1;
bw_write8(&Section[StringTable], '\0');
bw_write_string(&Section[StringTable], path[txt] + slash);
while(parser_next_token(&pc, TK_CROSSLINES)){
/* Unimplemented commands */
if(!strcmp(pc.Token, "BASEID_TRACKDATA") || !strcmp(pc.Token, "INCLUDE")
|| !strcmp(pc.Token, "include") || !strcmp(pc.Token, "INIFILE")
|| !strcmp(pc.Token, "LIST") || !strcmp(pc.Token, "SYMBOLFILE")){
if(InBinary)
Shutdown_M("%s:%u:%u: error: invalid use of '%s' inside BINARY section\n",
path[txt], pc.Line, pc.Col, pc.Token);
while(parser_next_token(&pc, 0)); /* skip to the end of the line */
continue;
}
if(!strcmp(pc.Token, "BINARY") && !InBinary){
InBinary++;
if(!parser_next_token(&pc, TK_CROSSLINES) || strcmp(pc.Token, "["))
Shutdown_M("%s:%u:%u: error: expected '[' for beginning of BINARY section before %s%s%s\n",
path[txt], pc.Line, pc.Col,
pc.Token ? "'" : "", pc.Token ? pc.Token : "end of file", pc.Token ? "'" : "");
}
else if(!strcmp(pc.Token, "]") && InBinary)
InBinary--;
else if(!InBinary)
Shutdown_M("%s:%u:%u: error: '%s' is not a valid command\n",
path[txt], pc.Line, pc.Col, pc.Token);
/****
** Inside a BINARY section
*/
else if(pc.Col == 1) /* no indent */
parser_add_symbol(&pc);
else{ /* indent */
uint8_t opcode;
uint32_t operands;
if((pc.Token[0] >= '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<Section[SymbolTable].Position; i+=16)
write_uint32(Section[SymbolTable].Data + i, 0); /* clear the st_size field we used temporarily */
if(SimsVersion == VERSION_TSO)
ObjectHeader[36]++; /* set the lsb of the processor flags to indicate that this code is for TSO */
for(i = 0, SectionHeader = ObjectHeader + 120, SectionOffset = 304; i < SectionCount; i++){
write_uint32(SectionHeader + 0, SectionOffset);
write_uint32(SectionHeader + 4, Section[i].Position);
SectionHeader += 40;
SectionOffset += Section[i].Position;
if(i == 0){
write_uint32(SectionHeader + 0, SectionOffset);
write_uint32(SectionHeader + 4, sizeof(SHStringTable));
SectionHeader += 40;
SectionOffset += sizeof(SHStringTable);
}
}
fwrite(ObjectHeader, 1, sizeof(ObjectHeader), hFile);
fwrite(Section[Text].Data, 1, Section[Text].Position, hFile);
fwrite(SHStringTable, 1, sizeof(SHStringTable), hFile);
for(i=1; i<SectionCount; i++)
fwrite(Section[i].Data, 1, Section[i].Position, hFile);
Shutdown();
return 0;
}

View file

@ -0,0 +1,638 @@
/*
hitutils - The Sims HIT (dis)assembler and linker
hitdump.c - Copyright (c) 2012 Niotso Project <http://niotso.org/>
Author(s): Fatbag <X-Fi6@phppoll.org>
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 <stdarg.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <stdint.h>
#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; i<VariableCount; i++)
if(Variables[i].Value == x)
return Variables[i].Name;
return NULL;
}
enum TokenizeType {
TK_STRING,
TK_ID
};
static int parser_find(ByteReaderContext *brc, ...){
va_list args;
va_start(args, brc);
while(1){
uint8_t * Start = brc->Data;
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; i<List->Count; 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; i<List->Count; 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; i<List->Count; 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; i<List->Count; 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; i<count; i++){
address_t * Address = add_address(AddressList);
Address->Exported = 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<filecount; i++){
free(path[i]);
free(data[i]);
}
free(basename);
free(AddressList.Entries);
if(hFile)
fclose(hFile);
}
int main(int argc, char *argv[]){
unsigned i, addr;
int SimsVersion = 0;
int overwrite = 0;
int ShowAddresses = 0;
int length;
size_t filesize[filecount-1];
const variable_t * Variables;
size_t VariableCount;
uint32_t SymbolTable = 0;
uint32_t BaseSoundID = 0, BaseSoundIDSet = 0;
/****
** Parse the command-line arguments
*/
if(argc == 1 || !strcmp(argv[1], "-h") || !strcmp(argv[1], "--help")){
printf("Usage: hitdump [-ts1|-tso] [-f] [-a] [-o outfile.txt] [-hsm infile.hsm]\n"
" [-hot infile.hot] [-evt infile.evt] infile.hit\n"
"Disassemble a HIT binary.\n"
"\n"
"The HSM, HOT, and EVT files are not strictly necessary but\n"
"each help in their own way to provide labels for addresses.\n"
"Use -f to force overwriting without confirmation.\n"
"Use -a to show addresses (verbose).\n"
"\n"
"Report bugs to <X-Fi6@phppoll.org>.\n"
"hitutils is maintained by the Niotso project.\n"
"Home page: <http://www.niotso.org/>\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; i<filecount; i++)
if(path[i])
path[i] = strdup(path[i]); /* necessary for free(path[i]) in Shutdown_M */
if(!SimsVersion)
Shutdown_M("%sSims version not specified. (Use -ts1 or -tso.)\n", "hitdump: Error: ");
if(SimsVersion == VERSION_TS1){
Variables = TS1Variables;
VariableCount = TS1VariableCount;
}else{
Variables = TSOVariables;
VariableCount = TSOVariableCount;
}
length = strlen(path[hit]);
if(path[out] == NULL){
path[out] = malloc(max(length+1, 5));
strcpy(path[out], path[hit]);
strcpy(path[out] + max(length-4, 0), ".txt");
}
length = max(length+1-4, 1);
basename = malloc(length);
memcpy(basename, path[hit], length-1);
basename[length-1] = '\0';
/****
** Read all of the requested files
*/
for(i=0; i<filecount-1; i++){
size_t bytestransferred;
if(!path[i]) continue;
hFile = fopen(path[i], "rb");
if(hFile == NULL){
if(i != hit){
fprintf(stderr, "%sCould not open file: %s.\n", "hitdump: Warning: ", path[i]);
continue;
}else
Shutdown_M("%sCould not open file: %s.\n", "hitdump: Error: ", path[i]);
}
fseek(hFile, 0, SEEK_END);
filesize[i] = ftell(hFile);
if(filesize[i] == 0){
fclose(hFile); hFile = NULL;
if(i != hit){
fprintf(stderr, "%sFile is invalid: %s.\n", "hitdump: Warning: ", path[i]);
continue;
}else
Shutdown_M("%sFile is invalid: %s.\n", "hitdump: Error: ", path[i]);
}
data[i] = malloc(filesize[i]);
if(data[i] == NULL){
fclose(hFile); hFile = NULL;
if(i != hit){
fprintf(stderr, "%sCould not allocate memory for file: %s.\n", "hitdump: Warning: ", path[i]);
continue;
}else
Shutdown_M("%sCould not allocate memory for file: %s.\n", "hitdump: Error: ", path[i]);
}
fseek(hFile, 0, SEEK_SET);
bytestransferred = fread(data[i], 1, filesize[i], hFile);
fclose(hFile); hFile = NULL;
if(bytestransferred != filesize[i]){
free(data[i]); data[i] = NULL;
if(i != hit){
fprintf(stderr, "%sCould not read file: %s.\n", "hitdump: Warning: ", path[i]);
continue;
}else
Shutdown_M("%sCould not read file: %s.\n", "hitdump: Error: ", path[i]);
}
}
/****
** Open the output file for writing
*/
if(!overwrite){
hFile = fopen(path[out], "rb");
if(hFile != NULL){
/* File exists */
char c;
fclose(hFile); hFile = NULL;
fprintf(stderr, "%sFile \"%s\" exists.\nContinue anyway? (y/n) ", "hitdump: ", path[out]);
c = getchar();
if(c != 'y' && c != 'Y')
Shutdown_M("\nAborted.\n");
}
}
hFile = fopen(path[out], "wb");
if(hFile == NULL)
Shutdown_M("%sCould not open file: %s.\n", "hitdump: Error: ", path[out]);
/****
** Verify the header of the HIT file
*/
if(filesize[hit] < 16 || memcmp(data[hit], HITHeader, 16))
Shutdown_M("%sFile is invalid: %s.\n", "hitdump: Error: ", path[hit]);
/****
** Build up the address list
*/
AddressList.SizeAllocated = 32 * sizeof(address_t);
AddressList.Count = 0;
AddressList.Entries = malloc(32 * sizeof(address_t));
read_hit_addresses(data[hit], filesize[hit], &AddressList, &SymbolTable);
if(data[evt]) read_evt_addresses(data[evt], filesize[evt], &AddressList);
if(data[hsm]) read_hsm_addresses(data[hsm], filesize[hsm], &AddressList);
if(data[hot]) read_hot_addresses(data[hot], filesize[hot], &AddressList);
/* scan_branch_destinations(data[hit], filesize[hit], &AddressList); */
for(i=0; i<AddressList.Count; i++){
if(AddressList.Entries[i].SoundID != 0 && (!BaseSoundIDSet || AddressList.Entries[i].SoundID < BaseSoundID)){
BaseSoundID = AddressList.Entries[i].SoundID;
BaseSoundIDSet = 1;
}
if(ShowAddresses){
printf("Address %u:\n Exported: %u\n TrackID: %u\n SoundID: %u\n Name: %s\n LogicalAddress: %u\n", i,
AddressList.Entries[i].Exported,
AddressList.Entries[i].TrackID,
AddressList.Entries[i].SoundID,
AddressList.Entries[i].Name ? AddressList.Entries[i].Name : "",
AddressList.Entries[i].LogicalAddress
);
}
}
/****
** Perform the disassembly
*/
fprintf(hFile, "BASEID_TRACKDATA %u\r\n"
"\r\n"
";\r\n"
"; generated by hitdump.\r\n"
";\r\n"
"\r\n"
"; useful symbols:\r\n"
"; kSndobPlay = 1\r\n"
"; tkd_Generic 1\r\n"
"; tkd_GenericLooped 2\r\n"
"; tkd_GenericHitList 3\r\n"
"\r\n"
"INCLUDE defaultsyms.txt\r\n"
"INCLUDE SimsVariables.txt\r\n"
"\r\n"
"LIST [Options] Version=1\r\n"
"LIST [Options] LoadPriority=2\r\n"
"\r\n"
";LIST [EventMappingEquate] kSndobPlay=1\r\n"
"; --- end of standard intro text ---\r\n"
"\r\n"
"SYMBOLFILE %s%s\r\n"
"INIFILE %s.ini", BaseSoundID, path[hsm] ? path[hsm] : basename, path[hsm] ? "" : ".hsm", basename);
fprintf(hFile, "\r\n\r\nBINARY\r\n[");
for(addr=16; addr<filesize[hit];){
unsigned i;
uint8_t opcode;
const instruction_t * instruction;
uint32_t operands;
const address_t * Address;
int HadSymbolTable = 0;
if(SymbolTable && addr == SymbolTable){
if(addr != 16)
fprintf(hFile, "\r\n]\r\n\r\nBINARY\r\n[");
fprintf(hFile, "\r\n"
"\t69\r\n"
"\t78\r\n"
"\t84\r\n"
"\t80\r\n");
for(addr+=4; memcmp(data[hit]+addr, "EENT", 4); addr+=8){
uint32_t TrackID = read_uint32(data[hit]+addr), LogicalAddress = read_uint32(data[hit]+addr+4);
Address = find_address_by_logical_address(&AddressList, LogicalAddress);
fprintf(hFile, "\r\n\t#%u\t\t#", TrackID);
if(Address && Address->Name) 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;
}

View file

@ -0,0 +1,174 @@
/*
hitutils - The Sims HIT (dis)assembler and linker
hitld.c - Copyright (c) 2012 Niotso Project <http://niotso.org/>
Author(s): Fatbag <X-Fi6@phppoll.org>
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 <stdarg.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <stdint.h>
#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<filecount; i++){
free(path[i]);
free(data[i]);
}
for(i=0; i<ObjectList.Count; i++){
free(ObjectList.Entries[i].path);
free(ObjectList.Entries[i].data);
}
free(ObjectList.Entries);
if(hFile)
fclose(hFile);
}
int main(int argc, char *argv[]){
unsigned i;
unsigned ObjectArg;
int SimsVersion = 0;
int overwrite = 0;
size_t filesize[filecount-1];
if(argc < 3){
printf("Usage: hitld [-ts1|-tso] [-f] [-o outfile.hit] [-hsm infile.hsm]\n"
" [-hot infile.hot] INFILES\n"
"Link object files produced by hitasm into a HIT binary, and\n"
"relink the game's HSM and HOT files.\n"
"Use -f to force overwriting without confirmation.\n"
"\n"
"Report bugs to <X-Fi6@phppoll.org>.\n"
"hitutils is maintained by the Niotso project.\n"
"Home page: <http://www.niotso.org/>\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<filecount; i++)
if(path[i])
path[i] = strdup(path[i]); /* necessary for free(path[i]) in Shutdown_M */
if(!SimsVersion)
Shutdown_M("%sSims version not specified. (Use -ts1 or -tso.)\n", "hitasm: Error: ");
if(path[out] == NULL){
int length = strlen(argv[ObjectArg]);
path[out] = malloc(max(length+1+2, 5));
strcpy(path[out], argv[ObjectArg]);
strcpy(path[out] + max(length-2, 0), ".hit");
}
/****
** Read all of the requested files
*/
for(i=0; i<filecount-1; i++){
size_t bytestransferred;
if(!path[i]) continue;
hFile = fopen(path[i], "rb");
if(hFile == NULL)
Shutdown_M("%sCould not open file: %s.\n", "hitasm: Error: ", path[i]);
fseek(hFile, 0, SEEK_END);
filesize[i] = ftell(hFile);
if(filesize[i] == 0)
Shutdown_M("%sFile is invalid: %s.\n", "hitasm: Error: ", path[i]);
data[i] = malloc(filesize[i]);
if(data[i] == NULL)
Shutdown_M("%sCould not allocate memory for file: %s.\n", "hitasm: Error: ", path[i]);
fseek(hFile, 0, SEEK_SET);
bytestransferred = fread(data[i], 1, filesize[i], hFile);
fclose(hFile); hFile = NULL;
if(bytestransferred != filesize[i])
Shutdown_M("%sCould not read file: %s.\n", "hitasm: Error: ", path[i]);
}
/****
** Open the output file for writing
*/
if(!overwrite){
hFile = fopen(path[out], "rb");
if(hFile != NULL){
/* File exists */
char c;
fclose(hFile); hFile = NULL;
fprintf(stderr, "%sFile \"%s\" exists.\nContinue anyway? (y/n) ", "hitdump: ", path[out]);
c = getchar();
if(c != 'y' && c != 'Y')
Shutdown_M("\nAborted.\n");
}
}
hFile = fopen(path[out], "wb");
if(hFile == NULL)
Shutdown_M("%sCould not open file: %s.\n", "hitdump: Error: ", path[out]);
fwrite(HITHeader, 1, sizeof(HITHeader), hFile);
Shutdown();
return 0;
}

View file

@ -0,0 +1,452 @@
/*
hitutils - The Sims HIT (dis)assembler and linker
hitutils.h - Copyright (c) 2012 Niotso Project <http://niotso.org/>
Author(s): Fatbag <X-Fi6@phppoll.org>
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
};

View file

@ -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})

View file

@ -0,0 +1,713 @@
/*
iff2html - iff web page description generator
iff2html.c - Copyright (c) 2012 Niotso Project <http://niotso.org/>
Author(s): Fatbag <X-Fi6@phppoll.org>
Ahmed El-Mahdawy <aa.mahdawy.10@gmail.com>
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 <stdlib.h>
#include <stdio.h>
#include <stdint.h>
#include <iff/iff.h>
#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 <X-Fi6@phppoll.org>.\n"
"iff2html is maintained by the Niotso project.\n"
"Home page: <http://www.niotso.org/>\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,
"<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Strict//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd\">\n");
fprintf(hFile, "<html xmlns=\"http://www.w3.org/1999/xhtml\" xml:lang=\"en\" lang=\"en\" dir=\"ltr\">\n");
fprintf(hFile, "<head>\n");
fprintf(hFile, "<meta http-equiv=\"Content-Type\" content=\"text/html; charset=iso-8859-1\" />\n");
fprintf(hFile, "<meta http-equiv=\"Content-Style-Type\" content=\"text/css; charset=iso-8859-1\" />\n");
fprintf(hFile, "<meta http-equiv=\"Content-Language\" content=\"en\" />\n");
fprintf(hFile, "<meta name=\"description\" content=\"%s (iff2html)\" />\n", FileName);
fprintf(hFile, "<meta name=\"generator\" content=\"iff2html\" />\n");
fprintf(hFile, "<title>%s (iff2html)</title>\n", FileName);
fprintf(hFile, "<style type=\"text/css\" media=\"all\">\n");
fprintf(hFile, "html, body {\n");
fprintf(hFile, " background: #fff;\n");
fprintf(hFile, " color: #000;\n");
fprintf(hFile, " font-family: sans-serif;\n");
fprintf(hFile, "}\n");
fprintf(hFile, "\n");
fprintf(hFile, "a:link, a:visited, a:hover, a:active { color: #00f; }\n");
fprintf(hFile, "a:link, a:visited { text-decoration: none; }\n");
fprintf(hFile, "a:hover, a:active { text-decoration: underline; }\n");
fprintf(hFile, "\n");
fprintf(hFile, "#attributes {\n");
fprintf(hFile, " border-left: 2px solid #888; padding-left: 4px; margin-bottom: 1em;\n");
fprintf(hFile, "}\n");
fprintf(hFile, "\n");
fprintf(hFile, "#toc {\n");
fprintf(hFile, " display: table-cell;\n");
fprintf(hFile, " margin-top: 1em;\n");
fprintf(hFile, " background: #eee; border: 1px solid #bbb;\n");
fprintf(hFile, " padding: .25em;\n");
fprintf(hFile, "}\n");
fprintf(hFile, "#toc div {\n");
fprintf(hFile, " border-bottom: 1px solid #aaa;\n");
fprintf(hFile, "}\n");
fprintf(hFile, "#toc ul {\n");
fprintf(hFile, " list-style-type: none;\n");
fprintf(hFile, " padding: 0;\n");
fprintf(hFile, "}\n");
fprintf(hFile, "ul ul {\n");
fprintf(hFile, " padding: 2em;\n");
fprintf(hFile, "}\n");
fprintf(hFile, "\n");
fprintf(hFile, "h2 {\n");
fprintf(hFile, " border-bottom: 1px solid #888;\n");
fprintf(hFile, " margin: 2em 0 0.25em 0;\n");
fprintf(hFile, "}\n");
fprintf(hFile, "h2 a {\n");
fprintf(hFile, " font-size: 9pt;\n");
fprintf(hFile, "}\n");
fprintf(hFile, "\n");
fprintf(hFile, "table {\n");
fprintf(hFile, " border: 1px #aaa solid;\n");
fprintf(hFile, " border-collapse: collapse;\n");
fprintf(hFile, "}\n");
fprintf(hFile, "th, td {\n");
fprintf(hFile, " border: 1px #aaa solid;\n");
fprintf(hFile, " padding: 0.2em;\n");
fprintf(hFile, " white-space: pre-wrap;\n");
fprintf(hFile, "}\n");
fprintf(hFile, "\n");
fprintf(hFile, ".center {\n");
fprintf(hFile, " margin: auto auto;\n");
fprintf(hFile, "}\n");
fprintf(hFile, ".centerall * {\n");
fprintf(hFile, " text-align: center;\n");
fprintf(hFile, " vertical-align: middle;\n");
fprintf(hFile, "}\n");
fprintf(hFile, "\n");
fprintf(hFile, ".palette td, .palette th {\n");
fprintf(hFile, " border: none;\n");
fprintf(hFile, " width: 16px;\n");
fprintf(hFile, " height: 16px;\n");
fprintf(hFile, " font-size: 12px;\n");
fprintf(hFile, " line-height: 16px;\n");
fprintf(hFile, "}\n");
fprintf(hFile, ".palette td[title] {\n");
fprintf(hFile, " border: 1px solid #000;\n");
fprintf(hFile, " cursor: help;\n");
fprintf(hFile, "}\n");
fprintf(hFile, "\n");
fprintf(hFile, "#footer {\n");
fprintf(hFile, " margin-top: 2em;\n");
fprintf(hFile, " padding-bottom: 0.5em;\n");
fprintf(hFile, " text-align: center;\n");
fprintf(hFile, "}\n");
fprintf(hFile, "</style>\n");
fprintf(hFile, "</head>\n");
fprintf(hFile, "<body>\n");
fprintf(hFile, "<h1>%s</h1>\n", FileName);
fprintf(hFile, "<div id=\"attributes\">\n");
fprintf(hFile, "<div>");
for(c=0; c<16; c++)
fprintf(hFile, "%.2x", digest[c]);
fprintf(hFile, " (md5), ");
printsize(hFile, FileSize);
fprintf(hFile, "</div>\n");
fprintf(hFile, "<div>Dumped by iff2html.</div></div>\n");
fprintf(hFile, "\n");
fprintf(hFile, "<div id=\"toc\"><div><b>Contents</b> &ndash; %u chunks</div>\n", IFFFileInfo.ChunkCount);
fprintf(hFile, "<ul>\n");
for(c=1, ChunkData = IFFFileInfo.Chunks; c <= IFFFileInfo.ChunkCount; c++, ChunkData++)
fprintf(hFile, "<li><a href=\"#chunk%u_%.4x\">%u [%s] (%.4X)%s%s</a></li>\n",
c, ChunkData->ChunkID, c, ChunkData->Type, ChunkData->ChunkID,
(ChunkData->Label[0] != 0x00) ? " &ndash; " : "", ChunkData->Label);
fprintf(hFile, "</ul>\n");
fprintf(hFile, "</div>\n");
fprintf(hFile, "\n");
for(c=0, ChunkData = IFFFileInfo.Chunks; c < IFFFileInfo.ChunkCount; c++, ChunkData++){
fprintf(hFile, "<h2 id=\"chunk%u_%.4x\">%u [%s] (%.4X)%s%s <a href=\"#chunk%u_%.4x\">(Jump)</a></h2>\n",
c+1, ChunkData->ChunkID, c+1, ChunkData->Type, ChunkData->ChunkID,
(ChunkData->Label[0] != 0x00) ? " &ndash; " : "", ChunkData->Label,
c+1, ChunkData->ChunkID);
fprintf(hFile, "<div>\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, "<table class=\"center centerall\">\n");
fprintf(hFile, "<tr><th>Image</th></tr>\n");
fprintf(hFile, "<tr><td><img src=\"%s_%u_%.4x.png\" width=\"%u\" height=\"%u\" alt=\"\" /></td></tr>\n",
bmp ? "bmp" : "fbmp", c+1, ChunkData->ChunkID, (unsigned) Width, (unsigned) Height);
fprintf(hFile, "</table>\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, "<table>\n");
fprintf(hFile, "<tr><td>Format:</td><td>");
switch(StringData->Format){
case 0: fprintf(hFile, "<tt>00 00</tt> (0)"); break;
case -1: fprintf(hFile, "<tt>FF FF</tt> (&minus;1)"); break;
case -2: fprintf(hFile, "<tt>FE FF</tt> (&minus;2)"); break;
case -3: fprintf(hFile, "<tt>FD FF</tt> (&minus;3)"); break;
case -4: fprintf(hFile, "<tt>FC FF</tt> (&minus;4)"); break;
default: fprintf(hFile, "Unrecognized"); break;
}
fprintf(hFile, "</td></tr>\n");
fprintf(hFile, "</table>\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, "<br />\n");
fprintf(hFile, "<table class=\"center\">\n");
fprintf(hFile, "<tr><th>Language</th><th colspan=\"3\">String pairs</th></tr>\n");
for(LanguageSet=0; LanguageSet<20; LanguageSet++){
IFFStringPair * Pair;
unsigned PairIndex;
if(StringData->LanguageSets[LanguageSet].PairCount == 0)
continue;
fprintf(hFile, "<tr><td rowspan=\"%u\">%s</td>\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, "<tr>");
fprintf(hFile, "<td>%u</td><td>%s</td><td>%s</td></tr>\n", PairIndex,
(Pair->Key) != NULL ? Pair->Key : "",
(Pair->Value) != NULL ? Pair->Value : "");
}
}
fprintf(hFile, "</table>\n");
}
}else if(!strcmp(ChunkData->Type, "CATS")){
/****
** Regular string pair
*/
IFFStringPair * Pair = ChunkData->FormattedData;
fprintf(hFile, "<table class=\"center\">\n");
fprintf(hFile, "<tr><th>Key</th><th>Value</th></tr>\n");
fprintf(hFile, "<tr><td>%s</td><td>%s</td></tr>\n",
(Pair->Key) != NULL ? Pair->Key : "",
(Pair->Value) != NULL ? Pair->Value : "");
fprintf(hFile, "</table>\n");
}else if(!strcmp(ChunkData->Type, "FWAV") || !strcmp(ChunkData->Type, "GLOB")){
/****
** Regular string
*/
fprintf(hFile, "<table class=\"center\">\n");
fprintf(hFile, "<tr><th>String</th></tr>\n");
fprintf(hFile, "<tr><td>%s</td></tr>\n", ChunkData->FormattedData ? (char*) ChunkData->FormattedData : "");
fprintf(hFile, "</table>\n");
}else if(!strcmp(ChunkData->Type, "BCON")){
/****
** BCON parsing
*/
IFF_BCON * BCONData = ChunkData->FormattedData;
fprintf(hFile, "<table>\n");
fprintf(hFile, "<tr><td>Flags:</td><td><tt>%02X</tt> (%u)</td></tr>\n", BCONData->Flags, BCONData->Flags);
fprintf(hFile, "</table>\n");
if(BCONData->ConstantCount > 0){
unsigned i;
fprintf(hFile, "<br />\n");
fprintf(hFile, "<table class=\"center\">\n");
fprintf(hFile, "<tr><th colspan=\"2\">Constant Value</th></tr>\n");
for(i=0; i<BCONData->ConstantCount; i++)
fprintf(hFile, "<tr><td>%u</td><td>%u</td></tr>\n", i+1, BCONData->Constants[i]);
fprintf(hFile, "</table>\n");
}
}else if(!strcmp(ChunkData->Type, "FCNS")){
/****
** FCNS parsing
*/
IFFConstantList * List = ChunkData->FormattedData;
fprintf(hFile, "<table>\n");
fprintf(hFile, "<tr><td>Version:</td><td>%u</td></tr>\n", List->Version);
fprintf(hFile, "</table>\n");
if(List->ConstantCount > 0){
IFFConstant * Constant;
unsigned i;
fprintf(hFile, "<br />\n");
fprintf(hFile, "<table class=\"center\">\n");
fprintf(hFile, "<tr><th colspan=\"2\">Name</th><th>Value</th><th>Description</th></tr>\n");
for(i=0, Constant=List->Constants; i<List->ConstantCount; i++, Constant++)
fprintf(hFile, "<tr><td>%u</td><td>%s</td><td>%g</td><td>%s</td></tr>\n",
i+1,
Constant->Name ? Constant->Name : "",
Constant->Value,
Constant->Description ? Constant->Description : "");
fprintf(hFile, "</table>\n");
}
}else if(!strcmp(ChunkData->Type, "TMPL")){
/****
** TMPL parsing
*/
IFFTemplate * Template = ChunkData->FormattedData;
IFFTemplateField * Field;
unsigned i;
fprintf(hFile, "<table class=\"center\">\n");
fprintf(hFile, "<tr><th colspan=\"2\">Name</th><th>Type</th>\n");
for(i=0, Field=Template->Fields; i<Template->FieldCount; i++, Field++)
fprintf(hFile, "<tr><td>%u</td><td>%s</td><td>%s</td></tr>\n",
i+1,
Field->Name ? Field->Name : "",
Field->Type ? Field->Type : "");
fprintf(hFile, "</table>\n");
}else if(!strcmp(ChunkData->Type, "TRCN")){
/****
** TRCN parsing
*/
IFFRangeSet * RangeSet = ChunkData->FormattedData;
fprintf(hFile, "<table>\n");
fprintf(hFile, "<tr><td>Version:</td><td>%u</td></tr>\n", RangeSet->Version);
fprintf(hFile, "</table>\n");
if(RangeSet->RangeCount > 0){
unsigned i;
IFFRangeEntry * Range;
fprintf(hFile, "<br />\n");
fprintf(hFile, "<table class=\"center\">\n");
fprintf(hFile, "<tr><th colspan=\"2\">In use</th><th>Default value</th><th>Name</th>"
"<th>Comment</th><th>Range is enforced</th><th>Minimum</th><th>Maximum</th></tr>\n");
for(i=0, Range=RangeSet->Ranges; i<RangeSet->RangeCount; i++, Range++)
fprintf(hFile,
"<tr><td>%u</td><td>%s</td><td>%u</td><td>%s</td><td>%s</td><td>%s</td><td>%u</td><td>%u</td></tr>\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, "</table>\n");
}
}else if(!strcmp(ChunkData->Type, "PALT")){
/****
** PALT parsing
*/
IFFPalette * Palette = ChunkData->FormattedData;
uint8_t * Data = Palette->Data;
unsigned i, j;
fprintf(hFile, "<table class=\"center palette\" border=\"0\">\n");
fprintf(hFile, "<tr><th></th>");
for(i=0; i<16; i++) fprintf(hFile, "<th>%X</th>", i);
fprintf(hFile, "</tr>\n");
for(i=0; i<16; i++){
fprintf(hFile, "<tr><th>%X</th>", 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<td style=\"background:#%.2x%.2x%.2x\" title=\"%u: #%.2x%.2x%.2x\"></td>",
red, green, blue, i*16 + j, red, green, blue);
}else
fprintf(hFile, "\n<td></td>");
}
fprintf(hFile, "</tr>\n");
}
fprintf(hFile, "</table>\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, "<table>\n");
fprintf(hFile, "<tr><td>Version:</td><td>%u</td></tr>\n", SpriteList->Version);
fprintf(hFile, "<tr><td>Palette ID:</td><td>%.4X</td></tr>\n", SpriteList->PaletteID);
fprintf(hFile, "</table>\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, "<table class=\"center centerall\">\n");
fprintf(hFile, "<tr><th colspan=\"2\">Sprite</th>");
if(!spr1) fprintf(hFile, "<th>Z-Buffer</th>");
fprintf(hFile, "</tr>\n");
for(i=0; i<SpriteList->SpriteCount; 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, "<tr><td>%u</td><td", i+1);
if(Sprite->IndexData && iff_depalette(Sprite, PaletteData)){
WritePNG(filename, NULL, 0, Sprite, NULL, NULL);
fprintf(hFile, "><img src=\"%s_%u_%.4x_%u.png\" width=\"%u\" height=\"%u\" alt=\"\" />",
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, "</td><td><img src=\"spr2_%u_%.4x_%u_z.png\" width=\"%u\" height=\"%u\" alt=\"\" />",
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, "</td></tr>\n");
}
fprintf(hFile, "</table>\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, "<table>\n");
fprintf(hFile, "<tr><td>Version:</td><td>%u</td></tr>\n", Group->Version);
fprintf(hFile, "</table>\n");
fprintf(hFile, "<br />\n");
fprintf(hFile, "<table class=\"center\">\n");
fprintf(hFile, "<tr><th>Direction</th><th>Zoom</th><th colspan=\"6\">Sprite</th></tr>\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,
"<tr><td rowspan=\"%u\">%s</td><td rowspan=\"%u\">%s</td>"
"<th>#</th><th>Type</th><th>Chunk ID</th><th>Sprite index</th>"
"<th>Flags</th><th>Sprite offset</th><th>Object offset</th></tr>\n",
1+Angle->SpriteCount, Direction, 1+Angle->SpriteCount, Zooms[Angle->Zoom-1]);
for(j=0, Sprite = Angle->SpriteInfo; j<Angle->SpriteCount; j++, Sprite++)
fprintf(hFile, "<tr><td>%u</td><td>%u</td><td>%.4X</td><td>%u</td><td>%u</td>"
"<td>(%+d,%+d)</td><td>(%+g,%+g,%+g)</td></tr>",
j+1, Sprite->Type, Sprite->ChunkID, Sprite->SpriteIndex, Sprite->Flags,
Sprite->SpriteX, Sprite->SpriteY, Sprite->ObjectX, Sprite->ObjectY, Sprite->ObjectZ);
}else{
fprintf(hFile, "<tr><td>%s</td><td>%s</td><td>None specified</td></tr>", Direction, Zooms[Angle->Zoom-1]);
}
}
fprintf(hFile, "</table>\n");
}else if(!strcmp(ChunkData->Type, "BHAV")){
/****
** BHAV parsing
*/
IFFBehavior * Behavior = ChunkData->FormattedData;
IFFInstruction * Instruction;
fprintf(hFile, "<table>\n");
fprintf(hFile, "<tr><td>Version:</td><td>%u</td></tr>\n", Behavior->Version);
fprintf(hFile, "<tr><td>Type:</td><td>%u</td></tr>\n", Behavior->Type);
fprintf(hFile, "<tr><td>Arguments:</td><td>%u</td></tr>\n", Behavior->ArgumentCount);
fprintf(hFile, "<tr><td>Locals:</td><td>%u</td></tr>\n", Behavior->LocalCount);
fprintf(hFile, "<tr><td>Flags:</td><td>%.4X</td></tr>\n", Behavior->Flags);
fprintf(hFile, "</table>\n");
if(Behavior->InstructionCount > 0){
unsigned i;
fprintf(hFile, "<br />\n");
fprintf(hFile, "<table class=\"center\">\n");
fprintf(hFile, "<tr><th colspan=\"2\">Opcode</th><th>T-Dest</th><th>F-Dest</th><th>Operand data</th></tr>\n");
for(i=0, Instruction = Behavior->Instructions; i<Behavior->InstructionCount; i++, Instruction++)
fprintf(hFile, "<tr><td>%u</td><td><tt>%.4X</tt></td><td>%u</td><td>%u</td>"
"<td><tt>%.2X %.2X %.2X %.2X %.2X %.2X %.2X %.2X</tt></td></tr>\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, "</table>\n");
}
}else if(!strcmp(ChunkData->Type, "OBJf")){
/****
** OBJf parsing
*/
IFFFunctionTable * Table = ChunkData->FormattedData;
fprintf(hFile, "<table>\n");
fprintf(hFile, "<tr><td>Version:</td><td>%u</td></tr>\n", Table->Version);
fprintf(hFile, "</table>\n");
if(Table->FunctionCount > 0){
unsigned i;
fprintf(hFile, "<br />\n");
fprintf(hFile, "<table class=\"center\">\n");
fprintf(hFile, "<tr><th colspan=\"2\">Condition function</th><th>Action function</th></tr>\n");
for(i=0; i<Table->FunctionCount; i++)
fprintf(hFile,
"<tr><td>%u</td><td>%.4X</td><td>%.4X</td></tr>\n",
i+1, Table->Functions[i].ConditionID, Table->Functions[i].ActionID);
fprintf(hFile, "</table>\n");
}
}else{
fprintf(hFile, "The contents of this chunk cannot be shown on this page.\n");
}
fprintf(hFile, "</div>\n\n");
}
iff_delete(&IFFFileInfo);
fprintf(hFile,
"<div id=\"footer\">This page was generated by the use of <a href=\"http://www.niotso.org/\">iff2html</a>.\n");
fprintf(hFile, "The content of this page may be subject to copyright by the author(s) of the original iff file.</div>\n");
fprintf(hFile, "</body>\n");
fprintf(hFile, "</html>");
fclose(hFile);
printf("Wrote contents to '%s'.\n", OutFile);
return 0;
}

View file

@ -0,0 +1,149 @@
/*
iff2html - iff web page description generator
image.c - Copyright (c) 2012 Niotso Project <http://niotso.org/>
Author(s): Fatbag <X-Fi6@phppoll.org>
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 <stdio.h>
#include <iff/iff.h>
#include <bmp/read_bmp.h>
#include <png.h>
#include <setjmp.h> /* 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; i<Image.Width*Image.Height; i++){
uint8_t temp = Image.Data[i*3 + 0];
Image.Data[i*3 + 0] = Image.Data[i*3 + 2];
Image.Data[i*3 + 2] = temp;
}
}else{
/* SPR# or SPR2 sprite */
Image.Width = Sprite->Width;
Image.Height = Sprite->Height;
Image.Data = (!ZBuffer) ? Sprite->BGRA32Data : Sprite->ZBuffer;
if(!ZBuffer){
for(i=0; i<Image.Width*Image.Height; i++){
uint8_t temp = Image.Data[i*4 + 0];
Image.Data[i*4 + 0] = Image.Data[i*4 + 2];
Image.Data[i*4 + 2] = temp;
}
}
}
row_pointers = malloc(Image.Height * sizeof(png_bytep));
if(row_pointers == NULL){
if(ChunkData) free(Image.Data);
return 0;
}
for(i=0; i<Image.Height; i++)
row_pointers[i] = Image.Data + Image.Width*((ChunkData) ? 3*(Image.Height-i-1) : ((!ZBuffer)?4:1)*i);
/****
** PNG handling
*/
/* Initialization */
hFile = fopen(OutName, "wb");
if(hFile == NULL){
free(row_pointers);
if(ChunkData) free(Image.Data);
return 0;
}
png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
if(png_ptr == NULL){
fclose(hFile);
free(row_pointers);
if(ChunkData) free(Image.Data);
return 0;
}
info_ptr = png_create_info_struct(png_ptr);
if(info_ptr == NULL){
png_destroy_write_struct(&png_ptr, NULL);
fclose(hFile);
free(row_pointers);
if(ChunkData) free(Image.Data);
return 0;
}
if(setjmp(png_jmpbuf(png_ptr))){
png_destroy_write_struct(&png_ptr, &info_ptr);
fclose(hFile);
free(row_pointers);
if(ChunkData) free(Image.Data);
return 0;
}
png_init_io(png_ptr, hFile);
png_set_filter(png_ptr, 0, PNG_ALL_FILTERS);
png_set_compression_level(png_ptr, 9);
png_set_compression_mem_level(png_ptr, 9);
png_set_compression_window_bits(png_ptr, 15);
png_set_compression_buffer_size(png_ptr, 32768);
png_set_IHDR(png_ptr, info_ptr, Image.Width, Image.Height, 8,
ChunkData ? PNG_COLOR_TYPE_RGB : (!ZBuffer) ? PNG_COLOR_TYPE_RGB_ALPHA : PNG_COLOR_TYPE_GRAY,
PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT);
png_set_rows(png_ptr, info_ptr, row_pointers);
opng_reduce_image(png_ptr, info_ptr, OPNG_REDUCE_ALL);
png_write_png(png_ptr, info_ptr, PNG_TRANSFORM_IDENTITY, NULL);
png_destroy_write_struct(&png_ptr, &info_ptr);
fclose(hFile);
free(row_pointers);
if(ChunkData){
free(Image.Data);
*Width = Image.Width;
*Height = Image.Height;
}
return 1;
}

View file

@ -0,0 +1,20 @@
/*
iff2html - iff web page description generator
image.h - Copyright (c) 2012 Niotso Project <http://niotso.org/>
Author(s): Fatbag <X-Fi6@phppoll.org>
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);

View file

@ -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 <memory.h> /* 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<<s | 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;
}

View file

@ -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 */

File diff suppressed because it is too large Load diff

View file

@ -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 <png.h>
#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 */

View file

@ -0,0 +1 @@
This is for testing Windows input messages, at the moment.

View file

@ -0,0 +1 @@
gcc -Wall -Wextra -Wabi -pedantic -m32 -o inputtest.exe inputtest.cpp -mconsole

View file

@ -0,0 +1,96 @@
/*
inputtest - Windows input testing
inputtest.cpp - Copyright (c) 2012 Niotso Project <http://niotso.org/>
Author(s): Fatbag <X-Fi6@phppoll.org>
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 <stdio.h>
#include <windows.h>
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;
}

View file

@ -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})

View file

@ -0,0 +1,527 @@
/*
rtti-reader - The Sims Online MSVC RTTI Class Hierarchy Extractor
rtti-reader.cpp - Copyright (c) 2012 Niotso Project <http://niotso.org/>
Author(s): Fatbag <X-Fi6@phppoll.org>
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:
//<http://www.openrce.org/articles/full_view/23>
//<https://www.blackhat.com/presentations/bh-dc-07/Sabanal_Yason/Paper/bh-dc-07-Sabanal_Yason-WP.pdf>
//For information about the Windows PE header, read:
//<https://en.wikibooks.org/wiki/X86_Disassembly/Windows_Executable_Files#File_Format>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <stddef.h>
#include <stdint.h>
#include <limits.h>
#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 <class T>
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<RTTIBaseClassDescriptor> 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<RTTICompleteObjectLocator> COLL;
RTTITypeDescriptor TD;
RTTIClassHierarchyDescriptor CHD;
void init(){
COLL.init();
CHD.BCDL.init();
}
bool DependsOn(const RTTIClass& X) const {
for(uint32_t i=1; i<CHD.BCDL.Count; i++)
if(CHD.BCDL.Buffer[i].TD.Fields.Address == X.TD.Fields.Address)
return true;
return false;
}
static int Compare(const void * Aptr, const void * Bptr){
const RTTIClass& A = *reinterpret_cast<const RTTIClass*>(Aptr);
const RTTIClass& B = *reinterpret_cast<const RTTIClass*>(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<size);
skip(-(int)i); //Seek back
return true;
}
bool find32(uint32_t address){
char buffer[4];
write_uint32(buffer, address);
return memfind(buffer, 4);
}
bool GenericFill(uint8_t *ptr, size_t count){
const size_t padding = offsetof(PaddingTest,B)-offsetof(PaddingTest,A);
count -= padding;
if(count > brc.end - brc.position)
return false;
uint32_t *field = reinterpret_cast<uint32_t*>(ptr);
*field = brc.position; //The Address field always comes first
do {
ptr += padding; count -= padding;
field = reinterpret_cast<uint32_t*>(ptr);
*field = read32();
} while(count);
return true;
}
template <class T>
inline bool Fill(T& context) {
return GenericFill(reinterpret_cast<uint8_t*>(&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 <X-Fi6@phppoll.org>.\n"
"rtti-reader is maintained by the Niotso project.\n"
"Home page: <http://www.niotso.org/>\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<SegmentCount; i++){
if(!DLL.strcmp(".rdata")){
DLL.skip(16); DLL.rdata.size = DLL.read32();
DLL.rdata.offset = DLL.read32();
DLL.skip(16);
} else if(!DLL.strcmp(".data")){
DLL.skip(16); DLL.data.size = DLL.read32();
DLL.data.offset = DLL.read32();
DLL.skip(16);
} else DLL.skip(40);
}
if(DLL.rdata.size == 0)
Shutdown_M("Missing .rdata segment");
else if(DLL.data.size == 0)
Shutdown_M("Missing .data segment");
else if(DLL.rdata.size > 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<RTTIClass> 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<CHD.Fields.BaseClassCount; i++, bcdlpos+=4){
DLL.lookat(DLL.rdata);
if(!DLL.seek(bcdlpos))
Shutdown_M("Unexpectedly reached end of binary");
uint32_t BCDAddress = DLL.read32();
if(!DLL.seek(BCDAddress - ImageBase))
Shutdown_M("Unexpectedly reached end of binary");
RTTIBaseClassDescriptor& BCD = CHD.BCDL.add();
if(!DLL.Fill(BCD))
Shutdown_M("Unexpectedly reached end of binary");
DLL.lookat(DLL.data);
if(!DLL.seek(BCD.Fields.TypeDescriptorAddress - ImageBase))
Shutdown_M("Unexpectedly reached end of binary");
if(!DLL.Fill(BCD.TD))
Shutdown_M("Unexpectedly reached end of binary");
length = DLL.strlen();
if(length == (unsigned)-1)
Shutdown_M("Unexpectedly reached end of binary");
BCD.TD.Name = (char*) malloc(length+1);
BCD.TD.UnmangledName = (char*) malloc(length+1-6);
if(!BCD.TD.Name || !BCD.TD.UnmangledName)
Shutdown_M("Failed to allocate memory");
DLL.strcpy(BCD.TD.Name);
for(size_t j=0; j<length-6; j++){
char c = BCD.TD.Name[j+4];
BCD.TD.UnmangledName[j] = (
(c >= '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<RCL.Count; i++){
RTTIClass& RC = RCL.Buffer[i];
RTTIClassHierarchyDescriptor& CHD = RC.CHD;
printf("\nClass %s (mangled: %s)\n", RC.TD.UnmangledName, RC.TD.Name);
printf(" * Class Hierarchy Descriptor: (Address %08X)\n", CHD.Fields.Address + ImageBase);
printf(" * Reserved: %u\n", CHD.Fields.Reserved);
printf(" * Attributes: %u\n", CHD.Fields.Attributes);
printf(" * Base class count: %u\n", CHD.Fields.BaseClassCount);
printf(" * Base class list address: %08X\n", CHD.Fields.BaseClassListAddress);
for(uint32_t j=0; j<CHD.Fields.BaseClassCount; j++){
RTTIBaseClassDescriptor& BCD = CHD.BCDL.Buffer[j];
printf(" * Base class descriptor #%u: (Address %08X)\n", j, BCD.Fields.Address + ImageBase);
printf(" * Base class count: %u\n", BCD.Fields.BaseClassCount);
printf(" * Member offset: %d\n", BCD.Fields.MemberOffset);
printf(" * COL address offset: %d\n", BCD.Fields.COLAddressOffset);
printf(" * v-table offset: %d\n", BCD.Fields.VTableOffset);
printf(" * Attributes: %u\n", BCD.Fields.Attributes);
printf(" * Type descriptor: (Address %08X)\n", BCD.Fields.TypeDescriptorAddress);
printf(" * v-table address: %08X\n", BCD.TD.Fields.VTableAddress);
printf(" * Reserved: %u\n", BCD.TD.Fields.Reserved);
printf(" * Name: %s (mangled: %s)\n", BCD.TD.UnmangledName, BCD.TD.Name);
}
for(uint32_t j=0; j<RC.COLL.Count; j++){
RTTICompleteObjectLocator& COL = RC.COLL.Buffer[j];
printf(" * Complete object locator #%u: (Address %08X)\n", j, COL.Fields.Address + ImageBase);
printf(" * Reserved: %u\n", COL.Fields.Reserved);
printf(" * Offset: %d\n", COL.Fields.Offset);
printf(" * CD Offset: %d\n", COL.Fields.CDOffset);
printf(" * Type descriptor address: %08X\n", COL.Fields.TypeDescriptorAddress);
printf(" * Class descriptor address: %08X\n", COL.Fields.ClassDescriptorAddress);
printf(" * v-table address: %08X\n", COL.VTableAddress);
}
}
printf("\n****\n** [ 2 of 2 ] Class Hierarchy for %s\n****\n", BaseName);
qsort(RCL.Buffer, RCL.Count, sizeof(RTTIClass), RTTIClass::Compare);
for(i=0; i<RCL.Count; i++){
RTTIClass& RC = RCL.Buffer[i];
printf("\nclass %s", RC.TD.UnmangledName);
if(RC.CHD.BCDL.Count > 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<RC.CHD.BCDL.Count; j++)
printf(", %s", RC.CHD.BCDL.Buffer[j].TD.UnmangledName);
}
printf(";");
}
printf("\n\nCompleted RTTI report.\nDependencies provided for %u of %u classes.\n", (unsigned)RCL.Count, TotalClassCount);
return 0;
}

View file

@ -0,0 +1,13 @@
cmake_minimum_required(VERSION 2.6)
project(tsoscan)
set(TSOSCAN_SOURCES
tsoscan.c
cmd.c
stats.c
)
include_directories(${FILEHANDLER_INCLUDE})
add_executable(tsoscan ${TSOSCAN_SOURCES})
target_link_libraries(tsoscan iff_static)

View file

@ -0,0 +1,67 @@
/*
tsoscan - IFF statistical webpage generator
cmd.c - Copyright (c) 2012 Niotso Project <http://niotso.org/>
Author(s): Ahmed El-Mahdawy <aa.mahdawy.10@gmail.com>
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 <stdlib.h>
#include <string.h>
#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; i<argc; i++){
if(!strcmp(argv[i], "-o"))
i++;
else if(strcmp(argv[i], "-f") != 0)
CmdArgs->InDirCount++;
}
if(CmdArgs->InDirCount > 0){
CmdArgs->InDirs = calloc(CmdArgs->InDirCount, sizeof(char*));
if(CmdArgs->InDirs == NULL) return 0;
}
for(i=1; i<argc; i++){
if(!strcmp(argv[i], "-f")){
CmdArgs->ForceWrite = 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; i<args->InDirCount; i++)
free(args->InDirs[i]);
free(args->InDirs);
}
free(args->OutFile);
free(args);
}

View file

@ -0,0 +1,108 @@
/*
tsoscan - IFF statistical webpage generator
stats.c - Copyright (c) 2012 Niotso Project <http://niotso.org/>
Author(s): Ahmed El-Mahdawy <aa.mahdawy.10@gmail.com>
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 <stdlib.h>
#include <string.h>
#include <iff/iff.h>
#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; i<stats->ChunkTypeCount; 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; i<chunk->VersionCount; 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; i<stats->ChunkTypeCount; i++)
free(stats->ChunkTypes[i].Versions);
free(stats->ChunkTypes);
}
}

View file

@ -0,0 +1,328 @@
/*
tsoscan - IFF statistical webpage generator
tsoscan.c - Copyright (c) 2012 Niotso Project <http://niotso.org/>
Author(s): Ahmed El-Mahdawy <aa.mahdawy.10@gmail.com>
Fatbag <X-Fi6@phppoll.org>
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 <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <dirent.h>
#include <iff/iff.h>
#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 <X-Fi6@phppoll.org>.\n"
"tsoscan is maintained by the Niotso project.\n"
"Home page: <http://www.niotso.org/>\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; i<CmdArgs->InDirCount; 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(; DirStartIndex<FileCount;){
entry = readdir(dir);
if(strcmp(entry->d_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; i<FileCount; i++){
FILE *file;
size_t FileSize;
uint8_t *data;
IFFFile iff;
IFFChunk *ChunkData;
unsigned ChunkIndex;
printf("(%d/%d)\r", i+1, FileCount);
file = fopen(Files[i], "rb");
if(file == NULL){
fprintf(stderr, "%sUnable to open the specified file '%s'. Skipping.\n", TSOSCAN_WARNING, Files[i]);
continue;
}
fseek(file, 0, SEEK_END);
FileSize = ftell(file);
fseek(file, 0, SEEK_SET);
data = malloc(FileSize);
if(data == NULL){
fprintf(stderr, "%sUnable to allocate memory for the specified files.\n", TSOSCAN_ERROR);
return -1;
}
if(fread(data, 1, FileSize, file) != FileSize){
fprintf(stderr, "%sUnable to read the specified file '%s'. Skipping.\n", TSOSCAN_WARNING, Files[i]);
free(data);
fclose(file);
continue;
}
fclose(file);
if(!iff_create(&iff)){
fprintf(stderr, "%sUnable to allocate memory for the specified files.\n", TSOSCAN_ERROR);
return -1;
}
if(!iff_read_header(&iff, data, FileSize) || !iff_enumerate_chunks(&iff, data+64, FileSize-64)){
/* Skip non-IFF files silently */
free(data);
continue;
}
free(data);
Stats.FileCount++;
if(Stats.AverageChunkCount == -1){
Stats.AverageChunkCount = iff.ChunkCount;
}else{
Stats.AverageChunkCount += iff.ChunkCount;
Stats.AverageChunkCount /= 2;
}
for(ChunkIndex = 0, ChunkData = iff.Chunks; ChunkIndex < iff.ChunkCount; ChunkIndex++, ChunkData++){
unsigned version = stats_get_version(ChunkData->Type, 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,
"<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Strict//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd\">\n");
fprintf(OutFile, "<html xmlns=\"http://www.w3.org/1999/xhtml\" xml:lang=\"en\" lang=\"en\" dir=\"ltr\">\n");
fprintf(OutFile, "<head>\n");
fprintf(OutFile, "<meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\" />\n");
fprintf(OutFile, "<meta http-equiv=\"Content-Style-Type\" content=\"text/css; charset=utf-8\" />\n");
fprintf(OutFile, "<meta http-equiv=\"Content-Language\" content=\"en\" />\n");
fprintf(OutFile, "<meta name=\"description\" content=\"tsostats\" />\n");
fprintf(OutFile, "<meta name=\"generator\" content=\"IFF Chunk Statistics (tsostats)\" />\n");
fprintf(OutFile, "<title>IFF Chunk Statistics (tsostats)</title>\n");
fprintf(OutFile, "<style type=\"text/css\" media=\"all\">\n");
fprintf(OutFile, "html, body {\n");
fprintf(OutFile, " background: #fff;\n");
fprintf(OutFile, " color: #000;\n");
fprintf(OutFile, " font-family: sans-serif;\n");
fprintf(OutFile, "}\n");
fprintf(OutFile, "\n");
fprintf(OutFile, "a:link, a:visited, a:hover, a:active { color: #00f; }\n");
fprintf(OutFile, "a:link, a:visited { text-decoration: none; }\n");
fprintf(OutFile, "a:hover, a:active { text-decoration: underline; }\n");
fprintf(OutFile, "\n");
fprintf(OutFile, "#attributes {\n");
fprintf(OutFile, " border-left: 2px solid #888; padding-left: 4px; margin-bottom: 1em;\n");
fprintf(OutFile, "}\n");
fprintf(OutFile, "\n");
fprintf(OutFile, "#toc {\n");
fprintf(OutFile, " display: table-cell;\n");
fprintf(OutFile, " margin-top: 1em;\n");
fprintf(OutFile, " background: #eee; border: 1px solid #bbb;\n");
fprintf(OutFile, " padding: .25em;\n");
fprintf(OutFile, "}\n");
fprintf(OutFile, "#toc div {\n");
fprintf(OutFile, " border-bottom: 1px solid #aaa;\n");
fprintf(OutFile, "}\n");
fprintf(OutFile, "#toc ul {\n");
fprintf(OutFile, " list-style-type: none;\n");
fprintf(OutFile, " padding: 0;\n");
fprintf(OutFile, "}\n");
fprintf(OutFile, "ul ul {\n");
fprintf(OutFile, " padding: 2em;\n");
fprintf(OutFile, "}\n");
fprintf(OutFile, "\n");
fprintf(OutFile, "h2 {\n");
fprintf(OutFile, " border-bottom: 1px solid #888;\n");
fprintf(OutFile, " margin: 2em 0 0.25em 0;\n");
fprintf(OutFile, "}\n");
fprintf(OutFile, "h2 a {\n");
fprintf(OutFile, " font-size: 9pt;\n");
fprintf(OutFile, "}\n");
fprintf(OutFile, "\n");
fprintf(OutFile, "table {\n");
fprintf(OutFile, " border: 1px #aaa solid;\n");
fprintf(OutFile, " border-collapse: collapse;\n");
fprintf(OutFile, "}\n");
fprintf(OutFile, "th, td {\n");
fprintf(OutFile, " border: 1px #aaa solid;\n");
fprintf(OutFile, " padding: 0.2em;\n");
fprintf(OutFile, " white-space: pre-wrap;\n");
fprintf(OutFile, "}\n");
fprintf(OutFile, "\n");
fprintf(OutFile, ".center {\n");
fprintf(OutFile, " margin: auto auto;\n");
fprintf(OutFile, "}\n");
fprintf(OutFile, "\n");
fprintf(OutFile, "#footer {\n");
fprintf(OutFile, " margin-top: 2em;\n");
fprintf(OutFile, " padding-bottom: 0.5em;\n");
fprintf(OutFile, " text-align: center;\n");
fprintf(OutFile, "}\n");
fprintf(OutFile, "</style>\n");
fprintf(OutFile, "</head>\n");
fprintf(OutFile, "<body>\n");
fprintf(OutFile, "<h1>IFF Chunk Statistics (tsostats)</h1>\n");
fprintf(OutFile, "<div id=\"attributes\">\n");
fprintf(OutFile, "<table>\n");
fprintf(OutFile, "<tr><td>Number of IFF files:</td><td>%u</td></tr>\n", Stats.FileCount);
fprintf(OutFile, "<tr><td>Average chunk count:</td><td>%.1f</td></tr>\n", Stats.AverageChunkCount);
fprintf(OutFile, "</table>\n");
fprintf(OutFile, "</div>\n");
fprintf(OutFile, "<div id=\"toc\"><div><b>Contents</b> &ndash; %u chunk types</div>\n", Stats.ChunkTypeCount);
fprintf(OutFile, "<ul>\n");
for(i=0; i<Stats.ChunkTypeCount; i++)
fprintf(OutFile, "<li><a href=\"#type%u\">%u %s</a></li>\n", i, i+1, Stats.ChunkTypes[i].Type);
fprintf(OutFile, "</ul>\n");
fprintf(OutFile, "</div>\n");
fprintf(OutFile, "\n");
for(i=0; i<Stats.ChunkTypeCount; i++){
ChunkStats *chunk = Stats.ChunkTypes+i;
fprintf(OutFile, "<h2 id=\"type%u\">%u %s <a href=\"#type%u\">(Jump)</a></h2>\n", i, i+1, Stats.ChunkTypes[i].Type, i);
fprintf(OutFile, "<div>\n");
fprintf(OutFile, "<table>\n");
fprintf(OutFile, "<tr><td>Number of occurrences:</td><td>%u</td></tr>\n", chunk->ChunkCount);
if(chunk->VersionCount == 1 && chunk->Versions[0].Version == (unsigned)-1)
fprintf(OutFile, "<tr><td>Number of versions:</td><td>N/A</td></tr>\n");
else
fprintf(OutFile, "<tr><td>Number of versions:</td><td>%u</td></tr>\n", chunk->VersionCount);
fprintf(OutFile, "</table>\n");
if(chunk->VersionCount > 1 ||
(chunk->VersionCount == 1 && chunk->Versions[0].Version != (unsigned)-1)){
fprintf(OutFile, "<table class=\"center\">\n");
fprintf(OutFile, "<tr><th></th><th>Version</th><th>Count</th></tr>\n");
for(version=0; version<chunk->VersionCount; version++){
VersionInfo *verinfo = chunk->Versions+version;
float percentage = (float)verinfo->Count / chunk->ChunkCount * 100;
fprintf(OutFile, "<tr><td>%u</td><td>%u (<tt>0x%x</tt>)</td><td>%u (%.1f%%)</td></tr>\n",
version+1, verinfo->Version, verinfo->Version, verinfo->Count, percentage);
}
fprintf(OutFile, "</table>\n");
}
fprintf(OutFile, "</div>\n\n");
}
fprintf(OutFile,
"<div id=\"footer\">This page was generated by the use of <a href=\"http://www.niotso.org/\">tsostats</a>.</div>\n");
fprintf(OutFile, "</body>\n");
fprintf(OutFile, "</html>");
fclose(OutFile);
printf("Generated statistics based on %u IFF files.\n", Stats.FileCount);
cmd_delete(CmdArgs);
stats_delete(&Stats);
return 0;
}

View file

@ -0,0 +1,65 @@
/*
tsoscan - IFF statistical webpage generator
tsoscan.h - Copyright (c) 2012 Niotso Project <http://niotso.org/>
Author(s): Ahmed El-Mahdawy <aa.mahdawy.10@gmail.com>
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 <stdint.h>
#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);

View file

@ -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.

Binary file not shown.

After

Width:  |  Height:  |  Size: 412 B

View file

@ -0,0 +1,152 @@
<script>
window.onload = init;
function LoadSpriteset(names) {
images = new Array(names.length);
imagesloaded = 0;
imagecount = 1 + names.length * 3;
ext = ['a', 'p', 'z'];
for (i = 0; i < names.length; i++) {
images[i] = new Array(3);
for (j = 0; j < 3; j++) {
img = new Image();
img.idi = i, img.idj = j;
img.src = names[i] + '_' + ext[j] + ".png";
img.onload = function() {
context.drawImage(this, 0, 0);
images[this.idi][this.idj] = context.getImageData(0, 0, 136, 384);
delete this;
if (++imagesloaded == imagecount) {
redraw = true;
setInterval(update, 16);
}
};
}
}
}
function init() {
// capture keys
left = 0;
right = 0;
up = 0;
down = 0;
window.onkeydown = function(evt) {
if (evt.keyCode == 37) left = 1;
if (evt.keyCode == 38) up = 1;
if (evt.keyCode == 39) right = 1;
if (evt.keyCode == 40) down = 1;
};
window.onkeyup = function(evt) {
if (evt.keyCode == 37) left = 0;
if (evt.keyCode == 38) up = 0;
if (evt.keyCode == 39) right = 0;
if (evt.keyCode == 40) down = 0;
};
// end capture keys
// The game does not use true isometric projection because each tile is 128x64 (close), 64x32 (medium), or 32x16 (far)
// which, when tilex or tiley is the hypotenuse, does not form a 30-60-90 triangle.
// It's actually arctan(1/2)-arctan(2/1)-90. The image is compressed vertically a bit to allow for power-of-2 texture sizes.
tilex = 0;
tiley = 0;
sin60 = 2.0/Math.sqrt(5); // sin(arctan(2)) or cos(arctan(1/2))
sin30 = 1.0/Math.sqrt(5); // sin(arctan(1/2)) or cos(arctan(2))
sqrt5120 = 32*Math.sqrt(5);
debug = document.getElementById('debug');
canvas = document.getElementById('draw');
context = canvas.getContext('2d');
background = new Image();
background.src = "background.png";
background.onload = function() {
context.drawImage(this, 0, 0);
delete this;
background = context.getImageData(0, 0, 136, 384);
if (++imagesloaded == imagecount) {
redraw = true;
setInterval(update, 16);
}
}
LoadSpriteset(["Chair-Liv-Deco_large_front", "tower_large_ne"]);
render = context.createImageData(136, 384);
}
function Blend(sourceid, soffset)
{
sa = images[sourceid][0].data[soffset+0] / 255.0;
if (sa == 0) return;
sr = images[sourceid][1].data[soffset+0];
sg = images[sourceid][1].data[soffset+1];
sb = images[sourceid][1].data[soffset+2];
if (sa == 255) {
render.data[dest+0] = sr;
render.data[dest+1] = sr;
render.data[dest+2] = sr;
return;
}
dr = render.data[dest+0];
dg = render.data[dest+1];
db = render.data[dest+2];
da = 1.0 - sa;
render.data[dest+0] = da * dr + sa * sr;
render.data[dest+1] = da * dg + sa * sg;
render.data[dest+2] = da * db + sa * sb;
}
function update() {
if (!left && !right && !up && !down && !redraw) return;
redraw = false;
if (left == 1) tilex -= 1;
if (right == 1) tilex += 1;
if (up == 1) tiley += 1;
if (down == 1) tiley -= 1;
screenx = Math.round((tilex + tiley)*sin60);
screeny = Math.round((tilex - tiley)*sin30);
z = screeny*1.5;
for (y = 0; y < 384; y++) {
for (x = 0; x < 136; x++) {
dest = (y*136+x)*4;
render.data[dest+0] = background.data[dest+0];
render.data[dest+1] = background.data[dest+1];
render.data[dest+2] = background.data[dest+2];
render.data[dest+3] = 255;
// Shift: read from the data so-many bytes ago. Subtract rather than add.
if (y-screeny >= 0 && y-screeny < 384 && x-screenx >= 0 && x-screenx < 136) {
source = ((y-screeny)*136 + x-screenx)*4;
if (images[1][2].data[source] != 255 && images[1][2].data[source] - z < images[0][2].data[dest]) {
// Object 2 on top
Blend(0, dest);
Blend(1, source);
} else {
// Object 1 on top
Blend(1, source);
Blend(0, dest);
}
} else {
// Object 1 only
Blend(0, dest);
}
}
}
context.putImageData(render, 0, 0);
debug.innerHTML = "Tile x: " + tilex/sqrt5120 + "<br />Tile y: " + tiley/sqrt5120 + "<br />Screen x: " + screenx + "<br />Screen y: " + screeny + "<br />z: " + z;
}
</script>
<div id="debug"></div>
<canvas width="136" height="384" id="draw" style="border: 1px solid #888"></canvas>