Delete obsolete AoE3Ed code
This was SVN commit r10909.
This commit is contained in:
parent
c9cdde256f
commit
e4b2de4bba
@ -249,23 +249,6 @@ extern_lib_defs = {
|
|||||||
})
|
})
|
||||||
end,
|
end,
|
||||||
},
|
},
|
||||||
devil = {
|
|
||||||
compile_settings = function()
|
|
||||||
if os.is("windows") then
|
|
||||||
add_default_include_paths("devil")
|
|
||||||
end
|
|
||||||
end,
|
|
||||||
link_settings = function()
|
|
||||||
-- On Windows, it uses #pragma comment(lib ...) to link the library,
|
|
||||||
-- so we only need to include the library-search-path
|
|
||||||
if os.is("windows") then
|
|
||||||
add_default_lib_paths("devil")
|
|
||||||
end
|
|
||||||
add_default_links({
|
|
||||||
unix_names = { "IL", "ILU" },
|
|
||||||
})
|
|
||||||
end,
|
|
||||||
},
|
|
||||||
enet = {
|
enet = {
|
||||||
compile_settings = function()
|
compile_settings = function()
|
||||||
if not _OPTIONS["with-system-enet"] then
|
if not _OPTIONS["with-system-enet"] then
|
||||||
@ -565,23 +548,6 @@ extern_lib_defs = {
|
|||||||
})
|
})
|
||||||
end,
|
end,
|
||||||
},
|
},
|
||||||
xerces = {
|
|
||||||
compile_settings = function()
|
|
||||||
if os.is("windows") then
|
|
||||||
add_default_include_paths("xerces")
|
|
||||||
end
|
|
||||||
end,
|
|
||||||
link_settings = function()
|
|
||||||
if os.is("windows") then
|
|
||||||
add_default_lib_paths("xerces")
|
|
||||||
end
|
|
||||||
add_default_links({
|
|
||||||
win_names = { "xerces-c_2" },
|
|
||||||
unix_names = { "xerces-c" },
|
|
||||||
no_delayload = 1,
|
|
||||||
})
|
|
||||||
end,
|
|
||||||
},
|
|
||||||
zlib = {
|
zlib = {
|
||||||
compile_settings = function()
|
compile_settings = function()
|
||||||
if os.is("windows") then
|
if os.is("windows") then
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
newoption { trigger = "atlas", description = "Include Atlas scenario editor projects" }
|
newoption { trigger = "atlas", description = "Include Atlas scenario editor projects" }
|
||||||
newoption { trigger = "collada", description = "Include COLLADA projects (requires FCollada library)" }
|
newoption { trigger = "collada", description = "Include COLLADA projects (requires FCollada library)" }
|
||||||
newoption { trigger = "coverage", description = "Enable code coverage data collection (GCC only)" }
|
newoption { trigger = "coverage", description = "Enable code coverage data collection (GCC only)" }
|
||||||
newoption { trigger = "aoe3ed", description = "Include AoE3Ed" }
|
|
||||||
newoption { trigger = "icc", description = "Use Intel C++ Compiler (Linux only; should use either \"--cc icc\" or --without-pch too, and then set CXX=icpc before calling make)" }
|
newoption { trigger = "icc", description = "Use Intel C++ Compiler (Linux only; should use either \"--cc icc\" or --without-pch too, and then set CXX=icpc before calling make)" }
|
||||||
newoption { trigger = "outpath", description = "Location for generated project files" }
|
newoption { trigger = "outpath", description = "Location for generated project files" }
|
||||||
newoption { trigger = "without-tests", description = "Disable generation of test projects" }
|
newoption { trigger = "without-tests", description = "Disable generation of test projects" }
|
||||||
@ -730,10 +729,6 @@ function setup_atlas_project(project_name, target_type, rel_source_dirs, rel_inc
|
|||||||
project_add_contents(source_root, rel_source_dirs, rel_include_dirs, extra_params)
|
project_add_contents(source_root, rel_source_dirs, rel_include_dirs, extra_params)
|
||||||
project_add_extern_libs(extern_libs, target_type)
|
project_add_extern_libs(extern_libs, target_type)
|
||||||
|
|
||||||
if _OPTIONS["aoe3ed"] then
|
|
||||||
defines { "USE_AOE3ED" }
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Platform Specifics
|
-- Platform Specifics
|
||||||
if os.is("windows") then
|
if os.is("windows") then
|
||||||
defines { "_UNICODE" }
|
defines { "_UNICODE" }
|
||||||
@ -812,12 +807,6 @@ function setup_atlas_projects()
|
|||||||
"AtlasObject",
|
"AtlasObject",
|
||||||
"AtlasScript",
|
"AtlasScript",
|
||||||
}
|
}
|
||||||
if _OPTIONS["aoe3ed"] then
|
|
||||||
table.insert(atlas_src, "ArchiveViewer")
|
|
||||||
table.insert(atlas_src, "FileConverter")
|
|
||||||
table.insert(atlas_extra_links, "DatafileIO")
|
|
||||||
table.insert(atlas_extra_links, "xerces-c")
|
|
||||||
end
|
|
||||||
|
|
||||||
atlas_extern_libs = {
|
atlas_extern_libs = {
|
||||||
"boost",
|
"boost",
|
||||||
@ -830,9 +819,6 @@ function setup_atlas_projects()
|
|||||||
"x11",
|
"x11",
|
||||||
"zlib",
|
"zlib",
|
||||||
}
|
}
|
||||||
if _OPTIONS["aoe3ed"] then
|
|
||||||
table.insert(atlas_extern_libs, "devil")
|
|
||||||
end
|
|
||||||
|
|
||||||
setup_atlas_project("AtlasUI", "SharedLib", atlas_src,
|
setup_atlas_project("AtlasUI", "SharedLib", atlas_src,
|
||||||
{ -- include
|
{ -- include
|
||||||
@ -847,25 +833,6 @@ function setup_atlas_projects()
|
|||||||
extra_links = atlas_extra_links,
|
extra_links = atlas_extra_links,
|
||||||
extra_files = { "Misc/atlas.rc" }
|
extra_files = { "Misc/atlas.rc" }
|
||||||
})
|
})
|
||||||
|
|
||||||
if _OPTIONS["aoe3ed"] then
|
|
||||||
setup_atlas_project("DatafileIO", "StaticLib",
|
|
||||||
{ -- src
|
|
||||||
".",
|
|
||||||
"BAR",
|
|
||||||
"DDT",
|
|
||||||
"SCN",
|
|
||||||
"Stream",
|
|
||||||
"XMB"
|
|
||||||
},{ -- include
|
|
||||||
},{ -- extern_libs
|
|
||||||
"devil",
|
|
||||||
"xerces",
|
|
||||||
"zlib"
|
|
||||||
},{ -- extra_params
|
|
||||||
})
|
|
||||||
end
|
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
@ -899,9 +866,6 @@ function setup_atlas_frontend_project (project_name)
|
|||||||
project_add_manifest()
|
project_add_manifest()
|
||||||
|
|
||||||
else -- Non-Windows, = Unix
|
else -- Non-Windows, = Unix
|
||||||
if _OPTIONS["aoe3ed"] then
|
|
||||||
links { "DatafileIO" }
|
|
||||||
end
|
|
||||||
links { "AtlasObject" }
|
links { "AtlasObject" }
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -911,10 +875,6 @@ end
|
|||||||
|
|
||||||
function setup_atlas_frontends()
|
function setup_atlas_frontends()
|
||||||
setup_atlas_frontend_project("ActorEditor")
|
setup_atlas_frontend_project("ActorEditor")
|
||||||
if _OPTIONS["aoe3ed"] then
|
|
||||||
setup_atlas_frontend_project("ArchiveViewer")
|
|
||||||
setup_atlas_frontend_project("FileConverter")
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
@ -1,43 +0,0 @@
|
|||||||
/* Copyright (C) 2009 Wildfire Games.
|
|
||||||
* This file is part of 0 A.D.
|
|
||||||
*
|
|
||||||
* 0 A.D. is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 2 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* 0 A.D. is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with 0 A.D. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifdef _WIN32
|
|
||||||
|
|
||||||
# define WIN32_LEAN_AND_MEAN
|
|
||||||
# include <windows.h>
|
|
||||||
|
|
||||||
// Use WinXP-style controls
|
|
||||||
# if _MSC_VER >= 1400 // (can't be bothered to implement this for VC7.1...)
|
|
||||||
# pragma comment(linker, "\"/manifestdependency:type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='X86' publicKeyToken='6595b64144ccf1df'\"")
|
|
||||||
# endif
|
|
||||||
|
|
||||||
# define ATLASDLLIMPEXP extern "C" __declspec(dllimport)
|
|
||||||
#else
|
|
||||||
# define ATLASDLLIMPEXP extern "C"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include "AtlasUI/Misc/DLLInterface.h"
|
|
||||||
|
|
||||||
#ifdef _WIN32
|
|
||||||
int APIENTRY wWinMain(HINSTANCE, HINSTANCE, LPWSTR, int)
|
|
||||||
#else
|
|
||||||
int main()
|
|
||||||
#endif
|
|
||||||
{
|
|
||||||
Atlas_StartWindow(L"ArchiveViewer");
|
|
||||||
return 0;
|
|
||||||
}
|
|
@ -1 +0,0 @@
|
|||||||
IDI_ICON1 ICON "..\\AtlasUI\\Misc\\Graphics\\ArchiveViewer.ico"
|
|
@ -1,43 +0,0 @@
|
|||||||
/* Copyright (C) 2009 Wildfire Games.
|
|
||||||
* This file is part of 0 A.D.
|
|
||||||
*
|
|
||||||
* 0 A.D. is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 2 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* 0 A.D. is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with 0 A.D. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifdef _WIN32
|
|
||||||
|
|
||||||
# define WIN32_LEAN_AND_MEAN
|
|
||||||
# include <windows.h>
|
|
||||||
|
|
||||||
// Use WinXP-style controls
|
|
||||||
# if _MSC_VER >= 1400 // (can't be bothered to implement this for VC7.1...)
|
|
||||||
# pragma comment(linker, "\"/manifestdependency:type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='X86' publicKeyToken='6595b64144ccf1df'\"")
|
|
||||||
# endif
|
|
||||||
|
|
||||||
# define ATLASDLLIMPEXP extern "C" __declspec(dllimport)
|
|
||||||
#else
|
|
||||||
# define ATLASDLLIMPEXP extern "C"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include "AtlasUI/Misc/DLLInterface.h"
|
|
||||||
|
|
||||||
#ifdef _WIN32
|
|
||||||
int APIENTRY wWinMain(HINSTANCE, HINSTANCE, LPWSTR, int)
|
|
||||||
#else
|
|
||||||
int main()
|
|
||||||
#endif
|
|
||||||
{
|
|
||||||
Atlas_StartWindow(L"FileConverter");
|
|
||||||
return 0;
|
|
||||||
}
|
|
@ -1 +0,0 @@
|
|||||||
IDI_ICON1 ICON "..\\AtlasUI\\Misc\\Graphics\\FileConverter.ico"
|
|
@ -8,15 +8,6 @@ my @progs = (
|
|||||||
PROJECT_NAME => "ActorEditor",
|
PROJECT_NAME => "ActorEditor",
|
||||||
WINDOW_NAME => "ActorEditor",
|
WINDOW_NAME => "ActorEditor",
|
||||||
},
|
},
|
||||||
{
|
|
||||||
PROJECT_NAME => "FileConverter",
|
|
||||||
WINDOW_NAME => "FileConverter",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
PROJECT_NAME => "ArchiveViewer",
|
|
||||||
WINDOW_NAME => "ArchiveViewer",
|
|
||||||
},
|
|
||||||
|
|
||||||
);
|
);
|
||||||
|
|
||||||
for my $p (@progs) {
|
for my $p (@progs) {
|
||||||
|
@ -1,652 +0,0 @@
|
|||||||
/* Copyright (C) 2009 Wildfire Games.
|
|
||||||
* This file is part of 0 A.D.
|
|
||||||
*
|
|
||||||
* 0 A.D. is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 2 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* 0 A.D. is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with 0 A.D. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "precompiled.h"
|
|
||||||
|
|
||||||
#include "ArchiveViewer.h"
|
|
||||||
|
|
||||||
#include "Misc/Version.h"
|
|
||||||
|
|
||||||
#include <set>
|
|
||||||
#include <algorithm>
|
|
||||||
|
|
||||||
#include "DatafileIO/BAR/BAR.h"
|
|
||||||
#include "DatafileIO/Stream/Stream.h"
|
|
||||||
#include "DatafileIO/Stream/wx.h"
|
|
||||||
using namespace DatafileIO;
|
|
||||||
|
|
||||||
#include "wx/dnd.h"
|
|
||||||
#include "wx/confbase.h"
|
|
||||||
#include "wx/wfstream.h"
|
|
||||||
#include "wx/tooltip.h"
|
|
||||||
#include "wx/progdlg.h"
|
|
||||||
#include "wx/regex.h"
|
|
||||||
#ifdef __WXMSW__
|
|
||||||
# include "wx/msw/registry.h"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// Possible TODO: Remove duplication of AtlasWindow code
|
|
||||||
|
|
||||||
enum {
|
|
||||||
ID_ExtractSelected,
|
|
||||||
ID_ExtractAll,
|
|
||||||
ID_EnablePreview,
|
|
||||||
ID_About
|
|
||||||
};
|
|
||||||
|
|
||||||
BEGIN_EVENT_TABLE(ArchiveViewer, wxFrame)
|
|
||||||
EVT_MENU(wxID_OPEN, ArchiveViewer::OnOpen)
|
|
||||||
EVT_MENU_RANGE(wxID_FILE1, wxID_FILE9, ArchiveViewer::OnMRUFile)
|
|
||||||
EVT_MENU(wxID_EXIT, ArchiveViewer::OnQuit)
|
|
||||||
EVT_MENU(ID_ExtractSelected, ArchiveViewer::OnExtractSelected)
|
|
||||||
EVT_MENU(ID_ExtractAll, ArchiveViewer::OnExtractAll)
|
|
||||||
EVT_MENU(ID_EnablePreview, ArchiveViewer::OnEnablePreview)
|
|
||||||
EVT_MENU(ID_About, ArchiveViewer::OnAbout)
|
|
||||||
EVT_CLOSE(ArchiveViewer::OnClose)
|
|
||||||
END_EVENT_TABLE()
|
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
|
|
||||||
class FilterTextCtrl : public wxTextCtrl
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
FilterTextCtrl(wxWindow* parent, ArchiveViewer* archViewWin, const wxSize& size)
|
|
||||||
: wxTextCtrl(parent, wxID_ANY, wxEmptyString, wxDefaultPosition, size),
|
|
||||||
m_archViewWin(archViewWin) {}
|
|
||||||
private:
|
|
||||||
void OnChange(wxCommandEvent& WXUNUSED(event))
|
|
||||||
{
|
|
||||||
m_archViewWin->SetFilter(GetValue());
|
|
||||||
}
|
|
||||||
ArchiveViewer* m_archViewWin;
|
|
||||||
DECLARE_EVENT_TABLE();
|
|
||||||
};
|
|
||||||
BEGIN_EVENT_TABLE(FilterTextCtrl, wxTextCtrl)
|
|
||||||
EVT_TEXT(wxID_ANY, FilterTextCtrl::OnChange)
|
|
||||||
END_EVENT_TABLE()
|
|
||||||
|
|
||||||
class FileListCtrl : public wxListCtrl
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
FileListCtrl(wxWindow* parent, ArchiveViewer* archViewWin)
|
|
||||||
: wxListCtrl(parent, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLC_REPORT),
|
|
||||||
m_archViewWin(archViewWin) {}
|
|
||||||
void OnSelect(wxListEvent& event)
|
|
||||||
{
|
|
||||||
m_archViewWin->UpdatePreview(event.GetIndex());
|
|
||||||
}
|
|
||||||
private:
|
|
||||||
ArchiveViewer* m_archViewWin;
|
|
||||||
DECLARE_EVENT_TABLE();
|
|
||||||
};
|
|
||||||
BEGIN_EVENT_TABLE(FileListCtrl, wxListCtrl)
|
|
||||||
EVT_LIST_ITEM_SELECTED(wxID_ANY, FileListCtrl::OnSelect)
|
|
||||||
END_EVENT_TABLE()
|
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
// Allow drag-and-drop of files onto the window, as a convenient way of opening them
|
|
||||||
//class DropTarget : public wxFileDropTarget
|
|
||||||
//{
|
|
||||||
//public:
|
|
||||||
// DropTarget(ColourTesterImageCtrl* imgctrl)
|
|
||||||
// : m_ImageCtrl(imgctrl)
|
|
||||||
// {}
|
|
||||||
//
|
|
||||||
// bool OnDropFiles(wxCoord WXUNUSED(x), wxCoord WXUNUSED(y), const wxArrayString& filenames)
|
|
||||||
// {
|
|
||||||
// if (filenames.GetCount() >= 1)
|
|
||||||
// m_ImageCtrl->SetImageFile(filenames[0]);
|
|
||||||
// return true;
|
|
||||||
// }
|
|
||||||
//private:
|
|
||||||
// ColourTesterImageCtrl* m_ImageCtrl;
|
|
||||||
//};
|
|
||||||
|
|
||||||
|
|
||||||
ArchiveViewer::ArchiveViewer(wxWindow* parent)
|
|
||||||
: wxFrame(parent, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize(600, 371))
|
|
||||||
, m_FileHistory(_T("Archive Viewer"))
|
|
||||||
, m_WindowTitle(wxString::Format(_("%s - Archive Viewer"), g_ProgramNameVersion.c_str()))
|
|
||||||
, m_BARStream(NULL), m_BARReader(NULL), m_PreviewEnabled(false)
|
|
||||||
{
|
|
||||||
SetIcon(wxIcon(_T("ICON_ArchiveViewer")));
|
|
||||||
|
|
||||||
wxToolTip::Enable(true);
|
|
||||||
|
|
||||||
wxMenuBar* menuBar = new wxMenuBar;
|
|
||||||
SetMenuBar(menuBar);
|
|
||||||
|
|
||||||
wxMenu *menuFile = new wxMenu;
|
|
||||||
menuBar->Append(menuFile, _("&File"));
|
|
||||||
{
|
|
||||||
menuFile->Append(wxID_OPEN, _("&Open..."));
|
|
||||||
menuFile->AppendSeparator();//-----------
|
|
||||||
menuFile->Append(wxID_EXIT, _("E&xit"));
|
|
||||||
m_FileHistory.UseMenu(menuFile);//-------
|
|
||||||
m_FileHistory.AddFilesToMenu();
|
|
||||||
}
|
|
||||||
|
|
||||||
wxMenu *menuView = new wxMenu;
|
|
||||||
menuBar->Append(menuView, _("&View"));
|
|
||||||
{
|
|
||||||
menuView->AppendCheckItem(ID_EnablePreview, _("&Preview"));
|
|
||||||
}
|
|
||||||
|
|
||||||
wxMenu *menuExtract = new wxMenu;
|
|
||||||
menuBar->Append(menuExtract, _("&Extract"));
|
|
||||||
{
|
|
||||||
menuExtract->Append(ID_ExtractAll, _("&All files..."));
|
|
||||||
menuExtract->Append(ID_ExtractSelected, _("&Selected..."));
|
|
||||||
}
|
|
||||||
|
|
||||||
wxMenu *menuHelp = new wxMenu;
|
|
||||||
menuBar->Append(menuHelp, _("&Help"));
|
|
||||||
{
|
|
||||||
menuHelp->Append(ID_About, _("&About"));
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
m_FileHistory.Load(*wxConfigBase::Get());
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
m_Splitter = new SnapSplitterWindow(this, wxSP_NOBORDER | wxSP_3DSASH);
|
|
||||||
|
|
||||||
wxPanel* leftPanel = new wxPanel(m_Splitter);
|
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////////
|
|
||||||
// Set up sizers:
|
|
||||||
|
|
||||||
wxBoxSizer* vertSizer = new wxBoxSizer(wxVERTICAL);
|
|
||||||
leftPanel->SetSizer(vertSizer);
|
|
||||||
|
|
||||||
wxBoxSizer* filterSizer = new wxBoxSizer(wxHORIZONTAL);
|
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////////
|
|
||||||
// Add things to sizers:
|
|
||||||
|
|
||||||
m_FileListCtrl = new FileListCtrl(leftPanel, this);
|
|
||||||
m_FileListCtrl->InsertColumn(0, _("Filename"), wxLIST_FORMAT_LEFT, 300);
|
|
||||||
m_FileListCtrl->InsertColumn(1, _("File size (bytes)"), wxLIST_FORMAT_RIGHT, 100);
|
|
||||||
m_FileListCtrl->InsertColumn(2, _("Last modified date"), wxLIST_FORMAT_LEFT, 150);
|
|
||||||
vertSizer->Add(m_FileListCtrl, wxSizerFlags().Expand().Proportion(1).Border(wxALL, 5));
|
|
||||||
|
|
||||||
vertSizer->Add(filterSizer,
|
|
||||||
wxSizerFlags().Expand().Border(wxLEFT|wxRIGHT|wxBOTTOM, 5));
|
|
||||||
|
|
||||||
|
|
||||||
m_PreviewWindow = new FilePreviewer(m_Splitter);
|
|
||||||
m_PreviewWindow->Hide();
|
|
||||||
|
|
||||||
m_Splitter->Initialize(leftPanel);
|
|
||||||
|
|
||||||
filterSizer->Add(new wxStaticText(leftPanel, wxID_ANY, _("&Search for file:")),
|
|
||||||
wxSizerFlags().Centre().Border(wxALL, 5));
|
|
||||||
|
|
||||||
FilterTextCtrl* filterTextCtrl = new FilterTextCtrl(leftPanel, this, wxSize(150, -1));
|
|
||||||
filterTextCtrl->SetToolTip(_("You may use * to match any sequence characters, and ? to match a single character"));
|
|
||||||
filterSizer->Add(filterTextCtrl, wxSizerFlags().Border(wxALL, 5));
|
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
SetCurrentFilename();
|
|
||||||
}
|
|
||||||
|
|
||||||
void ArchiveViewer::OnAbout(wxCommandEvent& WXUNUSED(event))
|
|
||||||
{
|
|
||||||
wxMessageBox(wxString::Format(_("%s - created by Philip Taylor (philip@wildfiregames.com / philip@zaynar.demon.co.uk)"), g_ProgramNameVersion.c_str()),
|
|
||||||
_("About"), wxOK|wxCENTRE|wxICON_INFORMATION);
|
|
||||||
}
|
|
||||||
|
|
||||||
ArchiveViewer::~ArchiveViewer()
|
|
||||||
{
|
|
||||||
delete m_BARStream;
|
|
||||||
delete m_BARReader;
|
|
||||||
}
|
|
||||||
|
|
||||||
void ArchiveViewer::OnClose(wxCloseEvent& event)
|
|
||||||
{
|
|
||||||
event.Skip();
|
|
||||||
m_FileHistory.Save(*wxConfigBase::Get());
|
|
||||||
}
|
|
||||||
|
|
||||||
//struct compare_bar {
|
|
||||||
// bool operator()(const BAREntry& x, const BAREntry& y) { return (x.filename < y.filename); }
|
|
||||||
//};
|
|
||||||
|
|
||||||
void ArchiveViewer::RedrawFileList(bool keepSelection)
|
|
||||||
{
|
|
||||||
if (!m_BARReader || !m_BARStream)
|
|
||||||
// No BAR file loaded
|
|
||||||
return;
|
|
||||||
|
|
||||||
std::vector<bool> filesSelected;
|
|
||||||
filesSelected.resize(m_CachedFileData.size(), false);
|
|
||||||
|
|
||||||
// If desired, remember a list of selected and focussed items
|
|
||||||
int focussed = -1;
|
|
||||||
if (keepSelection)
|
|
||||||
{
|
|
||||||
// Iterate through selected items
|
|
||||||
long item = m_FileListCtrl->GetNextItem(-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);
|
|
||||||
while (item != -1)
|
|
||||||
{
|
|
||||||
filesSelected[ m_FileListCtrl->GetItemData(item) ] = true;
|
|
||||||
item = m_FileListCtrl->GetNextItem(item, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Remember single focussed item
|
|
||||||
item = m_FileListCtrl->GetNextItem(-1, wxLIST_NEXT_ALL, wxLIST_STATE_FOCUSED);
|
|
||||||
if (item != -1)
|
|
||||||
focussed = m_FileListCtrl->GetItemData(item);
|
|
||||||
}
|
|
||||||
|
|
||||||
wxString filter;
|
|
||||||
enum { NONE, SUBSTRING, WILDCARD } filterType = NONE; // attempt to use most efficient matching system
|
|
||||||
if (m_Filter.Len())
|
|
||||||
{
|
|
||||||
if (m_Filter.Contains(_T("*")) || m_Filter.Contains(_T("?")))
|
|
||||||
{
|
|
||||||
filterType = WILDCARD;
|
|
||||||
filter = _T("*") + m_Filter.Lower() + _T("*"); // match any substring, and case insensitive
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
filterType = SUBSTRING;
|
|
||||||
filter = m_Filter.Lower();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
m_FileListCtrl->Freeze();
|
|
||||||
m_FileListCtrl->DeleteAllItems();
|
|
||||||
|
|
||||||
for (size_t i = 0; i < m_CachedFileData.size(); ++i)
|
|
||||||
{
|
|
||||||
if ( (filterType == SUBSTRING && !m_CachedFileData[i].NameLower.Contains(filter))
|
|
||||||
|| (filterType == WILDCARD && !m_CachedFileData[i].NameLower.Matches(filter)))
|
|
||||||
continue;
|
|
||||||
|
|
||||||
wxListItem info;
|
|
||||||
info.SetText(m_CachedFileData[i].Name);
|
|
||||||
info.SetData((long)i);
|
|
||||||
info.SetId((long)i);
|
|
||||||
if (keepSelection)
|
|
||||||
{
|
|
||||||
int state = (filesSelected[i] ? wxLIST_STATE_SELECTED : 0) | (focussed == (int)i ? wxLIST_STATE_FOCUSED : 0);
|
|
||||||
if (state)
|
|
||||||
{
|
|
||||||
info.SetState(state);
|
|
||||||
info.SetStateMask(wxLIST_STATE_SELECTED|wxLIST_STATE_FOCUSED);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
long idx = m_FileListCtrl->InsertItem(info);
|
|
||||||
wxASSERT(idx != -1);
|
|
||||||
m_FileListCtrl->SetItem(idx, 1, m_CachedFileData[i].Size);
|
|
||||||
m_FileListCtrl->SetItem(idx, 2, m_CachedFileData[i].Date);
|
|
||||||
}
|
|
||||||
|
|
||||||
m_FileListCtrl->Thaw();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void ArchiveViewer::UpdateFileList()
|
|
||||||
{
|
|
||||||
if (!m_BARReader || !m_BARStream)
|
|
||||||
// No BAR file loaded
|
|
||||||
return;
|
|
||||||
|
|
||||||
wxRegEx commas (_T("(\\d+)(\\d{3})"), wxRE_ADVANCED); // for comma-ising filesizes
|
|
||||||
|
|
||||||
// Get list of all files
|
|
||||||
// std::vector<BAREntry> files = m_BARReader->GetFileList(); // non-constref copy, so we can sort it
|
|
||||||
// std::sort(files.begin(), files.end(), compare_bar());
|
|
||||||
const std::vector<BAREntry>& files = m_BARReader->GetFileList();
|
|
||||||
|
|
||||||
m_CachedFileData.clear();
|
|
||||||
m_CachedFileData.resize(files.size());
|
|
||||||
|
|
||||||
for (size_t i = 0; i < files.size(); ++i)
|
|
||||||
{
|
|
||||||
m_CachedFileData[i].Name = wxString( utf16tow(files[i].filename).c_str() );
|
|
||||||
m_CachedFileData[i].NameLower = m_CachedFileData[i].Name.Lower();
|
|
||||||
|
|
||||||
m_CachedFileData[i].Size = wxString::Format(_T("%d"), files[i].filesize);
|
|
||||||
while (commas.Replace(&m_CachedFileData[i].Size, _T("\\1,\\2"))) ;
|
|
||||||
|
|
||||||
if (files[i].modified.year == 0)
|
|
||||||
m_CachedFileData[i].Date = _("Unknown");
|
|
||||||
else
|
|
||||||
{
|
|
||||||
wxDateTime date (files[i].modified.day, (wxDateTime::Month)(wxDateTime::Jan + files[i].modified.month-1), files[i].modified.year,
|
|
||||||
files[i].modified.hour, files[i].modified.minute, files[i].modified.second, files[i].modified.msecond);
|
|
||||||
m_CachedFileData[i].Date = wxString::Format(
|
|
||||||
_T("%s %s.%03d"),
|
|
||||||
date.FormatISODate().c_str(),
|
|
||||||
date.FormatISOTime().c_str(),
|
|
||||||
date.GetMillisecond());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void ArchiveViewer::OpenFile(const wxString& filename)
|
|
||||||
{
|
|
||||||
wxFFileInputStream* fileStream = new wxFFileInputStream(filename);
|
|
||||||
if (! fileStream->Ok())
|
|
||||||
{
|
|
||||||
wxLogError(_("Failed to open file"));
|
|
||||||
delete fileStream;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
SeekableInputStream* stream = new SeekableInputStreamFromWx(fileStream);
|
|
||||||
BARReader* reader = new BARReader(*stream);
|
|
||||||
if (! reader->Initialise())
|
|
||||||
{
|
|
||||||
wxLogError(_("Failed to read BAR file"));
|
|
||||||
delete reader;
|
|
||||||
delete stream;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Delete old data, replace with new data
|
|
||||||
delete m_BARStream;
|
|
||||||
delete m_BARReader;
|
|
||||||
m_BARStream = stream;
|
|
||||||
m_BARReader = reader;
|
|
||||||
|
|
||||||
UpdateFileList();
|
|
||||||
RedrawFileList(false);
|
|
||||||
|
|
||||||
m_FileHistory.AddFileToHistory(filename);
|
|
||||||
SetCurrentFilename(filename);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
void ArchiveViewer::OnExtractSelected(wxCommandEvent& WXUNUSED(event))
|
|
||||||
{
|
|
||||||
ExtractFiles(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
void ArchiveViewer::OnExtractAll(wxCommandEvent& WXUNUSED(event))
|
|
||||||
{
|
|
||||||
ExtractFiles(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
void ArchiveViewer::ExtractFiles(bool onlySelected)
|
|
||||||
{
|
|
||||||
long item = -1;
|
|
||||||
|
|
||||||
if (onlySelected)
|
|
||||||
{
|
|
||||||
// Find the first selected item, and make sure there is actually a selection
|
|
||||||
item = m_FileListCtrl->GetNextItem(-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);
|
|
||||||
|
|
||||||
if (item == -1)
|
|
||||||
{
|
|
||||||
wxMessageBox(_("Please select one or more files to extract."), _("No files selected"), wxICON_INFORMATION);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Work out where to extract the files to:
|
|
||||||
|
|
||||||
// Assume same as last place, else default (from registry), else cwd
|
|
||||||
wxString dir = m_LastExtractDir;
|
|
||||||
if (! dir.Len())
|
|
||||||
{
|
|
||||||
dir = GetDefaultOpenDirectory();
|
|
||||||
if (! dir.Len())
|
|
||||||
dir = wxFileName::GetCwd();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Let user choose final location
|
|
||||||
wxDirDialog dlg(this, wxDirSelectorPromptStr, dir);
|
|
||||||
if (dlg.ShowModal() != wxID_OK)
|
|
||||||
return;
|
|
||||||
|
|
||||||
// Remember the selection
|
|
||||||
dir = dlg.GetPath();
|
|
||||||
dir += wxFileName::GetPathSeparator();
|
|
||||||
m_LastExtractDir = dir;
|
|
||||||
wxFileName rootDir (dir);
|
|
||||||
|
|
||||||
|
|
||||||
// Construct a list of items to extract:
|
|
||||||
const std::vector<BAREntry>& files = m_BARReader->GetFileList();
|
|
||||||
std::vector<int> selection;
|
|
||||||
if (onlySelected)
|
|
||||||
{
|
|
||||||
while (item != -1)
|
|
||||||
{
|
|
||||||
selection.push_back(m_FileListCtrl->GetItemData(item)); // item was set at top of function
|
|
||||||
item = m_FileListCtrl->GetNextItem(item, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// Extract all files
|
|
||||||
for (int i = 0; i < (int)files.size(); ++i)
|
|
||||||
selection.push_back(i);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool aborted = false;
|
|
||||||
|
|
||||||
{
|
|
||||||
// Create the required directory structure:
|
|
||||||
|
|
||||||
// Extract the list of all directories:
|
|
||||||
std::set<std::wstring> dirs;
|
|
||||||
for (size_t sel = 0; sel < selection.size(); ++sel)
|
|
||||||
{
|
|
||||||
const BAREntry& file = files[selection[sel]];
|
|
||||||
wxString filename = wxString( utf16tow(file.filename).c_str() );
|
|
||||||
int lastSlash = filename.Find(_T('\\'), true);
|
|
||||||
if (lastSlash != -1)
|
|
||||||
dirs.insert(filename.Mid(0, lastSlash+1).wc_str());
|
|
||||||
}
|
|
||||||
|
|
||||||
// Construct the directory tree:
|
|
||||||
for (std::set<std::wstring>::iterator it = dirs.begin(); it != dirs.end(); ++it)
|
|
||||||
{
|
|
||||||
// Append name-in-archive to target root directory, one dir
|
|
||||||
// at a time, calling mkdir at each step if necessary
|
|
||||||
wxFileName fullFilePath (rootDir);
|
|
||||||
wxFileName filePath (it->c_str(), wxPATH_WIN);
|
|
||||||
const wxArrayString& filePathDirs = filePath.GetDirs();
|
|
||||||
for (size_t i = 0; i < filePathDirs.Count(); ++i)
|
|
||||||
{
|
|
||||||
fullFilePath.AppendDir(filePathDirs[i]);
|
|
||||||
if (! wxDirExists(fullFilePath.GetPath()))
|
|
||||||
wxMkdir(fullFilePath.GetPath());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create progress display
|
|
||||||
wxProgressDialog progress (_("Extracting files"), _("Please wait"), (int)selection.size(), this,
|
|
||||||
wxPD_APP_MODAL | wxPD_CAN_ABORT | wxPD_ELAPSED_TIME | wxPD_REMAINING_TIME | wxPD_SMOOTH);
|
|
||||||
|
|
||||||
// Repeat for each selection file
|
|
||||||
for (size_t sel = 0; sel < selection.size(); ++sel)
|
|
||||||
{
|
|
||||||
const BAREntry& file = files[selection[sel]];
|
|
||||||
|
|
||||||
if (! progress.Update((int)sel, wxString::Format(_("Extracting %s"), file.filename.c_str())))
|
|
||||||
{
|
|
||||||
aborted = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Append name-in-archive to target root directory, one dir
|
|
||||||
// at a time, calling mkdir at each step if necessary
|
|
||||||
wxFileName filePath (wxString( utf16tow(file.filename).c_str() ), wxPATH_WIN);
|
|
||||||
filePath.MakeAbsolute(rootDir.GetPath());
|
|
||||||
|
|
||||||
// Output to disk
|
|
||||||
wxFFileOutputStream outStream(filePath.GetFullPath());
|
|
||||||
if (! outStream.Ok())
|
|
||||||
wxLogError(wxString::Format(_("Error opening output file %s"), filePath.GetFullPath().c_str()));
|
|
||||||
else
|
|
||||||
{
|
|
||||||
SeekableOutputStreamFromWx str (outStream);
|
|
||||||
m_BARReader->TransferFile(file, str);
|
|
||||||
outStream.Close();
|
|
||||||
|
|
||||||
// Set the file's timestamps to match the one specified in the archive
|
|
||||||
if (file.modified.year != 0)
|
|
||||||
{
|
|
||||||
wxDateTime date (file.modified.day, (wxDateTime::Month)(wxDateTime::Jan + file.modified.month-1), file.modified.year,
|
|
||||||
file.modified.hour, file.modified.minute, file.modified.second, file.modified.msecond);
|
|
||||||
filePath.SetTimes(NULL, &date, &date);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (aborted)
|
|
||||||
wxMessageBox(_("Extraction aborted."), _("Stopped"));
|
|
||||||
else
|
|
||||||
wxMessageBox(_("Extraction complete."), _("Finished"));
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void ArchiveViewer::OnEnablePreview(wxCommandEvent& event)
|
|
||||||
{
|
|
||||||
m_PreviewEnabled = event.IsChecked();
|
|
||||||
|
|
||||||
if (m_PreviewEnabled)
|
|
||||||
{
|
|
||||||
// Make sure the window's not far too small
|
|
||||||
if (GetSize().GetWidth() < 700)
|
|
||||||
SetSize(-1, -1, 900, -1); // nobody is still using 800x600, are they?
|
|
||||||
|
|
||||||
m_Splitter->SetDefaultSashPosition(600);
|
|
||||||
m_Splitter->SplitVertically(m_Splitter->GetWindow1(), m_PreviewWindow);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
m_Splitter->Unsplit();
|
|
||||||
|
|
||||||
UpdatePreview();
|
|
||||||
}
|
|
||||||
|
|
||||||
void ArchiveViewer::UpdatePreview(long item)
|
|
||||||
{
|
|
||||||
if (! m_PreviewEnabled)
|
|
||||||
return;
|
|
||||||
|
|
||||||
// If the user opens a large archive file, enables the preview, selects the
|
|
||||||
// bottom file, and presses shift+home, it attempts to redraw the preview
|
|
||||||
// for every single file in sequence. That takes forever; so provide a way
|
|
||||||
// to abort the preview update.
|
|
||||||
if (wxGetKeyState(WXK_ESCAPE))
|
|
||||||
return;
|
|
||||||
|
|
||||||
// If none is specified, preview the first selected item
|
|
||||||
if (item == -1)
|
|
||||||
item = m_FileListCtrl->GetNextItem(-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);
|
|
||||||
|
|
||||||
if (item == -1) // none selected
|
|
||||||
return;
|
|
||||||
|
|
||||||
const BAREntry& file = m_BARReader->GetFileList()[ m_FileListCtrl->GetItemData(item) ];
|
|
||||||
SeekableInputStream* str = m_BARReader->GetFile(file);
|
|
||||||
m_PreviewWindow->PreviewFile(wxString(utf16tow(file.filename).c_str()), *str);
|
|
||||||
delete str;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
void ArchiveViewer::OnOpen(wxCommandEvent& WXUNUSED(event))
|
|
||||||
{
|
|
||||||
wxString path, name;
|
|
||||||
if (GetCurrentFilename().IsOk())
|
|
||||||
{
|
|
||||||
path = GetCurrentFilename().GetPath();
|
|
||||||
name = GetCurrentFilename().GetFullName();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
path = GetDefaultOpenDirectory();
|
|
||||||
}
|
|
||||||
|
|
||||||
wxFileDialog dlg (this, _("Select archive file to open"), path, name, _("BAR files (*.bar)|*.bar|All files (*.*)|*.*"), wxOPEN);
|
|
||||||
|
|
||||||
if (dlg.ShowModal() != wxID_OK)
|
|
||||||
return;
|
|
||||||
|
|
||||||
OpenFile(dlg.GetPath());
|
|
||||||
}
|
|
||||||
|
|
||||||
void ArchiveViewer::OnMRUFile(wxCommandEvent& event)
|
|
||||||
{
|
|
||||||
wxString file (m_FileHistory.GetHistoryFile(event.GetId() - wxID_FILE1));
|
|
||||||
if (file.Len())
|
|
||||||
OpenFile(file);
|
|
||||||
}
|
|
||||||
|
|
||||||
void ArchiveViewer::OnQuit(wxCommandEvent& WXUNUSED(event))
|
|
||||||
{
|
|
||||||
Close();
|
|
||||||
}
|
|
||||||
|
|
||||||
void ArchiveViewer::SetCurrentFilename(wxFileName filename)
|
|
||||||
{
|
|
||||||
m_CurrentFilename = filename;
|
|
||||||
|
|
||||||
if (filename.IsOk())
|
|
||||||
SetTitle(m_WindowTitle + _T(" - ") + filename.GetFullName());
|
|
||||||
else
|
|
||||||
SetTitle(m_WindowTitle);
|
|
||||||
}
|
|
||||||
|
|
||||||
wxString ArchiveViewer::GetDefaultOpenDirectory()
|
|
||||||
{
|
|
||||||
// TODO
|
|
||||||
/*
|
|
||||||
wxRegKey key (_T("HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Microsoft Games\\Age of Empires 3\\1.0"));
|
|
||||||
if (key.Exists())
|
|
||||||
{
|
|
||||||
wxString dir;
|
|
||||||
|
|
||||||
// The path might be the default value under the AppPath key, or it
|
|
||||||
// might be the AppPath value; so check both.
|
|
||||||
wxRegKey subkey (key, _T("AppPath"));
|
|
||||||
if (subkey.Exists())
|
|
||||||
{
|
|
||||||
if (subkey.QueryValue(_T(""), dir)) // default value
|
|
||||||
return dir;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (key.HasValue(_T("AppPath")) && key.QueryValue(_T("AppPath"), dir))
|
|
||||||
return dir;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
return _T("");
|
|
||||||
}
|
|
||||||
|
|
||||||
void ArchiveViewer::SetFilter(const wxString& filter)
|
|
||||||
{
|
|
||||||
m_Filter = filter;
|
|
||||||
RedrawFileList(true);
|
|
||||||
}
|
|
@ -1,92 +0,0 @@
|
|||||||
/* Copyright (C) 2009 Wildfire Games.
|
|
||||||
* This file is part of 0 A.D.
|
|
||||||
*
|
|
||||||
* 0 A.D. is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 2 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* 0 A.D. is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with 0 A.D. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "FileHistory/FileHistory.h"
|
|
||||||
#include "SnapSplitterWindow/SnapSplitterWindow.h"
|
|
||||||
#include "FilePreviewer.h"
|
|
||||||
|
|
||||||
#include "wx/filename.h"
|
|
||||||
#include "wx/listctrl.h"
|
|
||||||
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
namespace DatafileIO
|
|
||||||
{
|
|
||||||
class BARReader;
|
|
||||||
struct BAREntry;
|
|
||||||
class SeekableInputStream;
|
|
||||||
}
|
|
||||||
|
|
||||||
class ArchiveViewer : public wxFrame
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
ArchiveViewer(wxWindow* parent);
|
|
||||||
~ArchiveViewer();
|
|
||||||
|
|
||||||
void OnOpen(wxCommandEvent& event);
|
|
||||||
void OnMRUFile(wxCommandEvent& event);
|
|
||||||
void OnQuit(wxCommandEvent& event);
|
|
||||||
void OnExtractSelected(wxCommandEvent& event);
|
|
||||||
void OnExtractAll(wxCommandEvent& event);
|
|
||||||
void OnEnablePreview(wxCommandEvent& event);
|
|
||||||
void OnAbout(wxCommandEvent& event);
|
|
||||||
|
|
||||||
void OnClose(wxCloseEvent& event);
|
|
||||||
|
|
||||||
void SetFilter(const wxString& filter);
|
|
||||||
void UpdatePreview(long index = -1);
|
|
||||||
|
|
||||||
private:
|
|
||||||
void OpenFile(const wxString& filename);
|
|
||||||
void UpdateFileList(); // must be called after opening a new file
|
|
||||||
void RedrawFileList(bool keepSelection);
|
|
||||||
|
|
||||||
void ExtractFiles(bool onlySelected);
|
|
||||||
|
|
||||||
void SetCurrentFilename(wxFileName filename = wxString());
|
|
||||||
wxFileName GetCurrentFilename() { return m_CurrentFilename; }
|
|
||||||
|
|
||||||
wxString GetDefaultOpenDirectory();
|
|
||||||
|
|
||||||
wxFileName m_CurrentFilename;
|
|
||||||
wxString m_WindowTitle;
|
|
||||||
|
|
||||||
wxString m_Filter;
|
|
||||||
bool m_PreviewEnabled;
|
|
||||||
FilePreviewer* m_PreviewWindow;
|
|
||||||
SnapSplitterWindow* m_Splitter;
|
|
||||||
|
|
||||||
wxString m_LastExtractDir;
|
|
||||||
|
|
||||||
// wxStatusBar* m_StatusBar;
|
|
||||||
|
|
||||||
wxListCtrl* m_FileListCtrl;
|
|
||||||
DatafileIO::SeekableInputStream* m_BARStream;
|
|
||||||
DatafileIO::BARReader* m_BARReader;
|
|
||||||
|
|
||||||
struct FileData {
|
|
||||||
wxString NameLower; // for filtering
|
|
||||||
wxString Name;
|
|
||||||
wxString Size;
|
|
||||||
wxString Date;
|
|
||||||
};
|
|
||||||
std::vector<FileData> m_CachedFileData;
|
|
||||||
|
|
||||||
FileHistory m_FileHistory;
|
|
||||||
|
|
||||||
DECLARE_EVENT_TABLE();
|
|
||||||
};
|
|
@ -1,259 +0,0 @@
|
|||||||
/* Copyright (C) 2009 Wildfire Games.
|
|
||||||
* This file is part of 0 A.D.
|
|
||||||
*
|
|
||||||
* 0 A.D. is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 2 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* 0 A.D. is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with 0 A.D. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "precompiled.h"
|
|
||||||
|
|
||||||
#include "FilePreviewer.h"
|
|
||||||
|
|
||||||
#include "DatafileIO/XMB/XMB.h"
|
|
||||||
#include "DatafileIO/DDT/DDT.h"
|
|
||||||
|
|
||||||
#include "wx/sound.h"
|
|
||||||
#include "wx/image.h"
|
|
||||||
|
|
||||||
using namespace DatafileIO;
|
|
||||||
|
|
||||||
class ImagePanel : public /*wxPanel*/wxScrolledWindow
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
ImagePanel(wxWindow* parent, const wxImage& img)
|
|
||||||
: m_Bmp(img),
|
|
||||||
// wxPanel(parent, wxID_ANY, wxDefaultPosition, wxSize(img.GetWidth(), img.GetHeight()))
|
|
||||||
wxScrolledWindow(parent)
|
|
||||||
{
|
|
||||||
SetScrollbars(1, 1, img.GetWidth(), img.GetHeight());
|
|
||||||
}
|
|
||||||
|
|
||||||
void OnPaint(wxPaintEvent& WXUNUSED(event))
|
|
||||||
{
|
|
||||||
wxPaintDC dc(this);
|
|
||||||
dc.Clear();
|
|
||||||
if (m_Bmp.Ok())
|
|
||||||
{
|
|
||||||
dc.DrawBitmap(m_Bmp, CalcScrolledPosition(wxPoint(0, 0)));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
wxBitmap m_Bmp;
|
|
||||||
DECLARE_EVENT_TABLE();
|
|
||||||
};
|
|
||||||
|
|
||||||
BEGIN_EVENT_TABLE(ImagePanel, /*wxPanel*/wxScrolledWindow)
|
|
||||||
EVT_PAINT(ImagePanel::OnPaint)
|
|
||||||
END_EVENT_TABLE()
|
|
||||||
|
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
FilePreviewer::FilePreviewer(wxWindow* parent)
|
|
||||||
: wxPanel(parent), m_ContentPanel(NULL)
|
|
||||||
{
|
|
||||||
m_MainSizer = new wxBoxSizer(wxVERTICAL);
|
|
||||||
SetSizer(m_MainSizer);
|
|
||||||
}
|
|
||||||
|
|
||||||
enum FileType
|
|
||||||
{
|
|
||||||
UNKNOWN,
|
|
||||||
XMB,
|
|
||||||
XML,
|
|
||||||
DDT,
|
|
||||||
WAV,
|
|
||||||
UNKNOWN_TEXT
|
|
||||||
};
|
|
||||||
|
|
||||||
void FilePreviewer::PreviewFile(const wxString& filename, SeekableInputStream& stream)
|
|
||||||
{
|
|
||||||
if (m_ContentPanel)
|
|
||||||
{
|
|
||||||
m_MainSizer->Detach(m_ContentPanel);
|
|
||||||
m_ContentPanel->Destroy();
|
|
||||||
}
|
|
||||||
|
|
||||||
Freeze();
|
|
||||||
|
|
||||||
m_ContentPanel = new wxPanel(this);
|
|
||||||
m_MainSizer->Add(m_ContentPanel, wxSizerFlags().Expand().Proportion(1));
|
|
||||||
|
|
||||||
wxSizer* sizer = new wxBoxSizer(wxVERTICAL);
|
|
||||||
wxStaticText* fileInfo = new wxStaticText(m_ContentPanel, wxID_ANY, wxString::Format(_("Filename: %s"), filename.c_str()));
|
|
||||||
sizer->Add(fileInfo, wxSizerFlags().Border(wxALL, 5));
|
|
||||||
m_ContentPanel->SetSizer(sizer);
|
|
||||||
|
|
||||||
FileType type = UNKNOWN;
|
|
||||||
|
|
||||||
wxString extn;
|
|
||||||
int dot = filename.Find(_T('.'), true);
|
|
||||||
if (dot != -1)
|
|
||||||
extn = filename.Mid(dot).Lower();
|
|
||||||
|
|
||||||
#define X(x) extn==_T(x)
|
|
||||||
if (X(".xmb"))
|
|
||||||
type = XMB;
|
|
||||||
|
|
||||||
else if (X(".xml") || X(".lgt") || X(".dtd") || X(".shp"))
|
|
||||||
type = XML;
|
|
||||||
|
|
||||||
else if (X(".ddt"))
|
|
||||||
type = DDT;
|
|
||||||
|
|
||||||
else if (X(".wav"))
|
|
||||||
type = WAV;
|
|
||||||
|
|
||||||
else if (X(".txt") || X(".xs") || X(".psh") || X(".vsh") || X(".hlsl") || X(".inc"))
|
|
||||||
type = UNKNOWN_TEXT;
|
|
||||||
#undef X
|
|
||||||
|
|
||||||
wxString extraFileInfo;
|
|
||||||
|
|
||||||
if (type == XMB)
|
|
||||||
{
|
|
||||||
DatafileIO::XMBFile* file = DatafileIO::XMBFile::LoadFromXMB(stream);
|
|
||||||
std::wstring text = file->SaveAsXML();
|
|
||||||
delete file;
|
|
||||||
|
|
||||||
wxString wtext (text.c_str(), text.length());
|
|
||||||
wtext = L"<!-- converted from XMB to XML -->\n" + wtext;
|
|
||||||
|
|
||||||
sizer->Add(new wxTextCtrl(m_ContentPanel, wxID_ANY, wtext, wxDefaultPosition, wxDefaultSize,
|
|
||||||
wxTE_MULTILINE | wxTE_READONLY | wxHSCROLL | wxTE_RICH), // use RICH so we can display big files
|
|
||||||
wxSizerFlags().Expand().Proportion(1).Border(wxALL, 5));
|
|
||||||
|
|
||||||
extraFileInfo = _("Format: XMB");
|
|
||||||
}
|
|
||||||
|
|
||||||
else if (type == XML || type == UNKNOWN_TEXT)
|
|
||||||
{
|
|
||||||
void* buf;
|
|
||||||
size_t bufSize;
|
|
||||||
if (! stream.AcquireBuffer(buf, bufSize))
|
|
||||||
{
|
|
||||||
wxFAIL_MSG(_T("Buffer acquisition failed"));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// Assume UTF8, and convert to wxString
|
|
||||||
wxString wtext ((char*)buf, wxConvUTF8, bufSize);
|
|
||||||
|
|
||||||
sizer->Add(new wxTextCtrl(m_ContentPanel, wxID_ANY, wtext, wxDefaultPosition, wxDefaultSize,
|
|
||||||
wxTE_MULTILINE | wxTE_READONLY | wxHSCROLL),
|
|
||||||
wxSizerFlags().Expand().Proportion(1).Border(wxALL, 5));
|
|
||||||
|
|
||||||
if (type == XML)
|
|
||||||
extraFileInfo = _("Format: XML");
|
|
||||||
else
|
|
||||||
extraFileInfo = _("Format: unidentified text");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
else if (type == DDT)
|
|
||||||
{
|
|
||||||
DDTFile file(stream);
|
|
||||||
if (! file.Read(DDTFile::DDT))
|
|
||||||
{
|
|
||||||
wxLogError(_("Failed to read DDT file"));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
wxArrayString formatStrings;
|
|
||||||
switch (file.m_Type_Usage) {
|
|
||||||
case DDTFile::BUMP: formatStrings.Add(_("normal map")); break;
|
|
||||||
case DDTFile::CUBE: formatStrings.Add(_("cube map")); break;
|
|
||||||
}
|
|
||||||
switch (file.m_Type_Alpha) {
|
|
||||||
case DDTFile::NONE: formatStrings.Add(_("no alpha")); break;
|
|
||||||
// case DDTFile::PLAYER: formatStrings.Add(_("player colour")); break;
|
|
||||||
// case DDTFile::TRANS: formatStrings.Add(_("transparency")); break;
|
|
||||||
case DDTFile::BLEND: formatStrings.Add(_("terrain blend")); break;
|
|
||||||
}
|
|
||||||
switch (file.m_Type_Format) {
|
|
||||||
case DDTFile::BGRA: formatStrings.Add(_("32-bit BGRA")); break;
|
|
||||||
case DDTFile::DXT1: formatStrings.Add(_("DXT1")); break;
|
|
||||||
case DDTFile::GREY: formatStrings.Add(_("8-bit grey")); break;
|
|
||||||
case DDTFile::DXT3: formatStrings.Add(_("DXT3")); break;
|
|
||||||
case DDTFile::NORMSPEC: formatStrings.Add(_("specular+normal")); break;
|
|
||||||
}
|
|
||||||
|
|
||||||
wxString formatString;
|
|
||||||
for (size_t i = 0; i < formatStrings.GetCount(); ++i)
|
|
||||||
formatString += (i ? _T(", ") : _T("")) + formatStrings[i];
|
|
||||||
if (! formatString.Len())
|
|
||||||
formatString = _("unknown");
|
|
||||||
|
|
||||||
extraFileInfo = wxString::Format(_("Format: DDT texture\nSubformat: %d %d %d (%s)"),
|
|
||||||
file.m_Type_Usage, file.m_Type_Alpha, file.m_Type_Format, formatString.c_str());
|
|
||||||
|
|
||||||
void* buffer;
|
|
||||||
int width, height;
|
|
||||||
if (! file.GetImageData(buffer, width, height, false))
|
|
||||||
{
|
|
||||||
extraFileInfo += _("\n(Unrecognised format - unable to display)");
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
sizer->Add(new ImagePanel(m_ContentPanel, wxImage(width, height, (unsigned char*)buffer)),
|
|
||||||
wxSizerFlags().Expand().Proportion(1).Border(wxALL, 5));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
else if (type == WAV)
|
|
||||||
{
|
|
||||||
void* buf;
|
|
||||||
size_t bufSize;
|
|
||||||
if (! stream.AcquireBuffer(buf, bufSize))
|
|
||||||
{
|
|
||||||
wxFAIL_MSG(_T("Buffer acquisition failed"));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// HACK: If we destroy the sound object before it's finished playing,
|
|
||||||
// it deallocates the audio data and fails to play (or crashes).
|
|
||||||
// (That's assuming we fix wxSound to not just leak the memory.)
|
|
||||||
// So, just use a static object, and hope it stops playing before
|
|
||||||
// the program is exited.
|
|
||||||
// The wxSound-from memory constructor does not exist on OS X, so
|
|
||||||
// just show a warning there.
|
|
||||||
#ifdef __APPLE__
|
|
||||||
wxFAIL_MSG(_T("WAV playback not available on Mac OS X"));
|
|
||||||
#else
|
|
||||||
static wxSound snd;
|
|
||||||
snd.Stop();
|
|
||||||
// HACK, FIXME, XXX: I'd like to call the wx people idiots for
|
|
||||||
// having different API:s on different platforms, as well as for
|
|
||||||
// having public non-API methods.
|
|
||||||
#if __APPLE__
|
|
||||||
snd.~wxSound();
|
|
||||||
new (&snd) wxSound((int)bufSize, (const wxByte*)buf);
|
|
||||||
#else
|
|
||||||
snd.Create((int)bufSize, (const wxByte*)buf);
|
|
||||||
#endif
|
|
||||||
snd.Play();
|
|
||||||
#endif
|
|
||||||
stream.ReleaseBuffer(buf);
|
|
||||||
}
|
|
||||||
|
|
||||||
extraFileInfo = _("Format: WAV audio");
|
|
||||||
}
|
|
||||||
|
|
||||||
fileInfo->SetLabel(fileInfo->GetLabel() + _T("\n") + extraFileInfo);
|
|
||||||
|
|
||||||
m_MainSizer->Layout();
|
|
||||||
|
|
||||||
Thaw();
|
|
||||||
}
|
|
@ -1,31 +0,0 @@
|
|||||||
/* Copyright (C) 2009 Wildfire Games.
|
|
||||||
* This file is part of 0 A.D.
|
|
||||||
*
|
|
||||||
* 0 A.D. is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 2 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* 0 A.D. is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with 0 A.D. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "DatafileIO/Stream/Stream.h"
|
|
||||||
|
|
||||||
class FilePreviewer : public wxPanel
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
FilePreviewer(wxWindow* parent);
|
|
||||||
|
|
||||||
// stream can be deleted by callers after this function returns
|
|
||||||
void PreviewFile(const wxString& filename, DatafileIO::SeekableInputStream& stream);
|
|
||||||
|
|
||||||
private:
|
|
||||||
wxPanel* m_ContentPanel; // gets deleted and rebuilt when previewing a new file
|
|
||||||
wxSizer* m_MainSizer;
|
|
||||||
};
|
|
@ -1,367 +0,0 @@
|
|||||||
/* Copyright (C) 2009 Wildfire Games.
|
|
||||||
* This file is part of 0 A.D.
|
|
||||||
*
|
|
||||||
* 0 A.D. is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 2 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* 0 A.D. is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with 0 A.D. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "precompiled.h"
|
|
||||||
|
|
||||||
#include "FileConverter.h"
|
|
||||||
|
|
||||||
#include "Misc/Version.h"
|
|
||||||
|
|
||||||
#include "AtlasObject/AtlasObject.h"
|
|
||||||
#include "DatafileIO/XMB/XMB.h"
|
|
||||||
#include "DatafileIO/DDT/DDT.h"
|
|
||||||
#include "DatafileIO/Stream/wx.h"
|
|
||||||
#include "DatafileIO/Stream/Memory.h"
|
|
||||||
|
|
||||||
#include "wx/wfstream.h"
|
|
||||||
#include "wx/progdlg.h"
|
|
||||||
#include "wx/config.h"
|
|
||||||
#include "wx/regex.h"
|
|
||||||
|
|
||||||
using namespace DatafileIO;
|
|
||||||
|
|
||||||
//#define QUIET
|
|
||||||
|
|
||||||
enum FileType { XMB, XML, DDT, TGA };
|
|
||||||
|
|
||||||
const wxChar* xmlExtensions[] = {
|
|
||||||
_T("amt"), _T("blueprint"), _T("d3dconfig"), _T("dmg"), _T("effect"), _T("impacteffect"),
|
|
||||||
_T("lgt"), _T("multieffect"), _T("multips"), _T("multirs"),_T("multitechnique"),
|
|
||||||
_T("multitss"), _T("multivs"), _T("particle"), _T("physics"), _T("ps"), _T("rs"),
|
|
||||||
_T("tactics"), _T("technique"), _T("tss"), _T("vs"), _T("xml"),
|
|
||||||
NULL };
|
|
||||||
|
|
||||||
bool IsXMLExtension(const wxString& str)
|
|
||||||
{
|
|
||||||
const wxChar** e = xmlExtensions;
|
|
||||||
while (*e)
|
|
||||||
if (str == wxString(_T(".")) + *e++)
|
|
||||||
return true;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool ConvertFiles(const wxArrayString& files, wxWindow* parent);
|
|
||||||
|
|
||||||
bool ConvertFile(const wxString& sourceFilename, FileType sourceType,
|
|
||||||
const wxString& targetFilename, FileType targetType,
|
|
||||||
XMLReader* io);
|
|
||||||
|
|
||||||
FileConverter::FileConverter(wxWindow* parent)
|
|
||||||
: wxFrame(parent, wxID_ANY, wxString::Format(_("%s - File Converter"), g_ProgramNameVersion.c_str()))
|
|
||||||
{
|
|
||||||
SetIcon(wxIcon(_T("ICON_FileConverter")));
|
|
||||||
|
|
||||||
m_Transient = true;
|
|
||||||
|
|
||||||
bool succeeded = false;
|
|
||||||
wxApp* app = wxTheApp;
|
|
||||||
if (app->argc > 1)
|
|
||||||
{
|
|
||||||
wxArrayString files;
|
|
||||||
for (int i = 1; i < app->argc; ++i)
|
|
||||||
files.Add(app->argv[i]);
|
|
||||||
succeeded = ConvertFiles(files, this);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
wxConfigBase* cfg = wxConfigBase::Get(false);
|
|
||||||
wxString defaultDir;
|
|
||||||
if (cfg)
|
|
||||||
cfg->Read(_T("File Converter/OpenDir"), &defaultDir);
|
|
||||||
|
|
||||||
wxString extns = _("Recognised files");
|
|
||||||
extns += _T(" (*.xmb, *.ddt, *.xml, *.tga)|*.xmb;*.ddt;*.xml;*.tga");
|
|
||||||
// Add all the extra XML types
|
|
||||||
const wxChar** e = xmlExtensions;
|
|
||||||
while (*e)
|
|
||||||
{
|
|
||||||
extns += _T(";*.");
|
|
||||||
extns += *e++;
|
|
||||||
}
|
|
||||||
extns += wxString(_T("|")) + _("All files") + _T(" (*.*)|*.*");
|
|
||||||
|
|
||||||
wxFileDialog dlg (this, _("Select file(s) to convert"), defaultDir, _T(""),
|
|
||||||
extns, wxOPEN|/*wxFILE_MUST_EXIST|*/wxMULTIPLE); // for some reason, it complains that files don't exist when they actually do...
|
|
||||||
if (dlg.ShowModal() == wxID_OK)
|
|
||||||
{
|
|
||||||
wxArrayString files;
|
|
||||||
dlg.GetPaths(files);
|
|
||||||
succeeded = ConvertFiles(files, this);
|
|
||||||
|
|
||||||
if (cfg)
|
|
||||||
cfg->Write(_T("File Converter/OpenDir"), dlg.GetDirectory());
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
succeeded = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
wxLog::FlushActive(); // ensure errors are displayed before the "finished" message
|
|
||||||
|
|
||||||
#ifndef QUIET
|
|
||||||
if (succeeded)
|
|
||||||
wxMessageBox(_("Conversion complete."), _("Finished"), wxOK|wxICON_INFORMATION);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
Destroy();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool FileConverter::Show(bool show)
|
|
||||||
{
|
|
||||||
if (m_Transient)
|
|
||||||
return true;
|
|
||||||
else
|
|
||||||
return wxFrame::Show(show);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
bool ConvertFiles(const wxArrayString& files, wxWindow* parent)
|
|
||||||
{
|
|
||||||
XMLReader io; // TODO: don't create this unless necessary, because Xerces is slow
|
|
||||||
|
|
||||||
wxProgressDialog progress(_("Converting files"), _("Please wait"), (int)files.GetCount(), parent,
|
|
||||||
wxPD_APP_MODAL | wxPD_CAN_ABORT | wxPD_ELAPSED_TIME | wxPD_REMAINING_TIME | wxPD_SMOOTH);
|
|
||||||
|
|
||||||
for (size_t i = 0; i < files.GetCount(); ++i)
|
|
||||||
{
|
|
||||||
wxString sourceFilename = files[i];
|
|
||||||
wxString targetFilename;
|
|
||||||
FileType sourceType, targetType;
|
|
||||||
|
|
||||||
if (! progress.Update((int)i, wxString::Format(_("Converting %s"), sourceFilename.c_str())))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
wxString sourceExtn, sourceName;
|
|
||||||
{
|
|
||||||
int dot = sourceFilename.Find(_T('.'), true);
|
|
||||||
if (dot == -1)
|
|
||||||
{
|
|
||||||
wxLogError(_("No file extension for %s - don't know how to convert"), sourceFilename.c_str());
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
sourceExtn = sourceFilename.Mid(dot).Lower();
|
|
||||||
sourceName = sourceFilename.Mid(0, dot); // no extension
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (sourceExtn == _T(".xmb"))
|
|
||||||
{
|
|
||||||
sourceType = XMB;
|
|
||||||
targetType = XML;
|
|
||||||
// Ignore trailing .xmb
|
|
||||||
targetFilename = sourceName;
|
|
||||||
}
|
|
||||||
else if (sourceExtn == _T(".ddt"))
|
|
||||||
{
|
|
||||||
sourceType = DDT;
|
|
||||||
targetType = TGA; // TODO: allow BMP
|
|
||||||
targetFilename = sourceName + _T(".tga");
|
|
||||||
}
|
|
||||||
else if (sourceExtn == _T(".tga"))
|
|
||||||
{
|
|
||||||
sourceType = TGA; // TODO: allow BMP
|
|
||||||
targetType = DDT;
|
|
||||||
targetFilename = sourceName + _T(".ddt");
|
|
||||||
}
|
|
||||||
else if (IsXMLExtension(sourceExtn))
|
|
||||||
{
|
|
||||||
sourceType = XML;
|
|
||||||
targetType = XMB;
|
|
||||||
// Add a trailing .xmb (in addition to the normal .xml/etc)
|
|
||||||
targetFilename = sourceFilename + _T(".xmb");
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
wxLogError(_("Unknown file extension for %s - don't know how to convert"), sourceFilename.c_str());
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
#ifndef QUIET
|
|
||||||
// Warn about overwriting files
|
|
||||||
if (wxFile::Exists(targetFilename))
|
|
||||||
{
|
|
||||||
int ret = wxMessageBox(wxString::Format(_("Output file already exists: %s\nOverwrite file?"), targetFilename.c_str()), _("Overwrite?"), wxYES_NO|wxCANCEL);
|
|
||||||
if (ret == wxCANCEL) return false;
|
|
||||||
else if (ret == wxNO) continue;
|
|
||||||
else { /* carry on converting */ }
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// Do the actual conversion
|
|
||||||
ConvertFile(sourceFilename, sourceType, targetFilename, targetType, &io);
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool ConvertFile(const wxString& sourceFilename, FileType sourceType,
|
|
||||||
const wxString& targetFilename, FileType targetType,
|
|
||||||
XMLReader* io)
|
|
||||||
{
|
|
||||||
// Open input file (in binary read mode)
|
|
||||||
wxFFileInputStream file (sourceFilename);
|
|
||||||
if (! file.Ok())
|
|
||||||
{
|
|
||||||
wxLogError(_("Failed to open input file %s"), sourceFilename.c_str());
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
// Decompress input file if necessary (for any file type)
|
|
||||||
//std::auto_ptr<InputStream> inStream (new Maybel33tInputStream(new SeekableInputStreamFromWx(file)));
|
|
||||||
Maybel33tInputStream inStream (new SeekableInputStreamFromWx(file));
|
|
||||||
if (! inStream.IsOk())
|
|
||||||
{
|
|
||||||
wxLogError(_("Failed to decompress input file %s"), sourceFilename.c_str());
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Handle XMB<->XML conversions
|
|
||||||
if (sourceType == XMB || sourceType == XML)
|
|
||||||
{
|
|
||||||
std::auto_ptr<XMBFile> data (NULL);
|
|
||||||
|
|
||||||
// Read data with the appropriate format
|
|
||||||
if (sourceType == XML)
|
|
||||||
{
|
|
||||||
// if (! io) io = new XMLReader(); // TODO - see earlier comment
|
|
||||||
data.reset(io->LoadFromXML(inStream));
|
|
||||||
data->format = XMBFile::AOE3; // TODO: let users control this?
|
|
||||||
}
|
|
||||||
else if (sourceType == XMB)
|
|
||||||
{
|
|
||||||
data.reset(XMBFile::LoadFromXMB(inStream));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Write data with the appropriate format
|
|
||||||
if (targetType == XML)
|
|
||||||
{
|
|
||||||
std::wstring xml = data->SaveAsXML();
|
|
||||||
wxFFileOutputStream out(targetFilename, _T("w")); // open in text mode
|
|
||||||
if (! out.Ok())
|
|
||||||
{
|
|
||||||
wxLogError(_("Failed to open output file %s"), targetFilename.c_str());
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Output with UTF-8 encoding
|
|
||||||
xml = L"<?xml version=\"1.0\" encoding=\"utf-8\"?>\n" + xml;
|
|
||||||
wxCharBuffer buf = wxString(xml.c_str()).mb_str(wxConvUTF8);
|
|
||||||
out.Write(buf.data(), strlen(buf.data()));
|
|
||||||
}
|
|
||||||
else if (targetType == XMB)
|
|
||||||
{
|
|
||||||
wxFFileOutputStream out (targetFilename, _T("wb"));
|
|
||||||
if (! out.Ok())
|
|
||||||
{
|
|
||||||
wxLogError(_("Failed to open output file %s"), targetFilename.c_str());
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
SeekableOutputStreamFromWx out2 (out);
|
|
||||||
data->SaveAsXMB(out2);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
else if (sourceType == DDT)
|
|
||||||
{
|
|
||||||
DDTFile ddt(inStream);
|
|
||||||
if (! ddt.Read(DDTFile::DDT))
|
|
||||||
{
|
|
||||||
wxLogError(_("Failed to read DDT file %s"), sourceFilename.c_str());
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
// Stick some format-identifying data just before the extension
|
|
||||||
// part of the filename:
|
|
||||||
wxRegEx re (_T("(.*)\\."), wxRE_ADVANCED);
|
|
||||||
wxString newFilename = targetFilename;
|
|
||||||
re.ReplaceFirst(&newFilename,
|
|
||||||
wxString::Format(_T("\\1.(%d,%d,%d,%d)."),
|
|
||||||
ddt.m_Type_Usage, ddt.m_Type_Alpha, ddt.m_Type_Format, ddt.m_Type_Levels));
|
|
||||||
|
|
||||||
wxFFileOutputStream out(newFilename, _T("wb"));
|
|
||||||
if (! out.Ok())
|
|
||||||
{
|
|
||||||
wxLogError(_("Failed to open output file %s"), newFilename.c_str());
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
SeekableOutputStreamFromWx out2 (out);
|
|
||||||
ddt.SaveFile(out2, DDTFile::TGA);
|
|
||||||
}
|
|
||||||
|
|
||||||
else if (sourceType == TGA)
|
|
||||||
{
|
|
||||||
DDTFile ddt(inStream);
|
|
||||||
if (! ddt.Read(DDTFile::TGA))
|
|
||||||
{
|
|
||||||
wxLogError(_("Failed to read TGA file %s"), sourceFilename.c_str());
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
// Extract the format-identifying data from just before the extension
|
|
||||||
// part of the filename:
|
|
||||||
wxRegEx re (_T("\\.\\((\\d+),(\\d+),(\\d+),(\\d+)\\)\\."), wxRE_ADVANCED); // regexps in C++ are ugly :-(
|
|
||||||
wxString newFilename = targetFilename;
|
|
||||||
if (re.Matches(newFilename.c_str()))
|
|
||||||
{
|
|
||||||
wxASSERT(re.GetMatchCount() == 5);
|
|
||||||
long l0 = 0, l1 = 0, l2 = 0, l3 = 0;
|
|
||||||
if (re.GetMatch(newFilename, 1).ToLong(&l0)
|
|
||||||
&& re.GetMatch(newFilename, 2).ToLong(&l1)
|
|
||||||
&& re.GetMatch(newFilename, 3).ToLong(&l2)
|
|
||||||
&& re.GetMatch(newFilename, 4).ToLong(&l3))
|
|
||||||
{
|
|
||||||
ddt.m_Type_Usage = (DDTFile::Type_Usage)l0;
|
|
||||||
ddt.m_Type_Alpha = (DDTFile::Type_Alpha)l1;
|
|
||||||
ddt.m_Type_Format = (DDTFile::Type_Format)l2;
|
|
||||||
ddt.m_Type_Levels = l3;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// TODO: ask the user for settings? or at least be more helpful
|
|
||||||
wxLogError(_("Invalid filename - should be something.(n,n,n,n).tga"));
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
// Remove the format-identifying part when constructing the DDT filename
|
|
||||||
re.ReplaceFirst(&newFilename, _T("."));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
wxLogError(_("Invalid filename - should be something.(n,n,n,n).tga"));
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
wxFFileOutputStream out(newFilename, _T("wb"));
|
|
||||||
if (! out.Ok())
|
|
||||||
{
|
|
||||||
wxLogError(_("Failed to open output file %s"), newFilename.c_str());
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
SeekableOutputStreamFromWx out2 (out);
|
|
||||||
ddt.SaveFile(out2, DDTFile::DDT);
|
|
||||||
}
|
|
||||||
|
|
||||||
else
|
|
||||||
{
|
|
||||||
wxFAIL_MSG(_T("TODO"));
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
@ -1,26 +0,0 @@
|
|||||||
/* Copyright (C) 2009 Wildfire Games.
|
|
||||||
* This file is part of 0 A.D.
|
|
||||||
*
|
|
||||||
* 0 A.D. is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 2 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* 0 A.D. is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with 0 A.D. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
class FileConverter : public wxFrame
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
FileConverter(wxWindow* parent);
|
|
||||||
virtual bool Show(bool);
|
|
||||||
|
|
||||||
private:
|
|
||||||
bool m_Transient; // if true, the window won't be shown (since it's assumed to have been destroyed immediately)
|
|
||||||
};
|
|
@ -24,8 +24,6 @@
|
|||||||
#include "General/Datafile.h"
|
#include "General/Datafile.h"
|
||||||
|
|
||||||
#include "ActorEditor/ActorEditor.h"
|
#include "ActorEditor/ActorEditor.h"
|
||||||
#include "ArchiveViewer/ArchiveViewer.h"
|
|
||||||
#include "FileConverter/FileConverter.h"
|
|
||||||
#include "ScenarioEditor/ScenarioEditor.h"
|
#include "ScenarioEditor/ScenarioEditor.h"
|
||||||
#include "ErrorReporter/ErrorReporter.h"
|
#include "ErrorReporter/ErrorReporter.h"
|
||||||
|
|
||||||
@ -203,15 +201,11 @@ public:
|
|||||||
|
|
||||||
// Display the appropriate window
|
// Display the appropriate window
|
||||||
wxFrame* frame;
|
wxFrame* frame;
|
||||||
#define MAYBE(t) if (g_InitialWindowType == _T(#t)) frame = new t(NULL); else
|
if (g_InitialWindowType == _T("ActorEditor"))
|
||||||
MAYBE(ActorEditor)
|
{
|
||||||
#ifdef USE_AOE3ED
|
frame = new ActorEditor(NULL);
|
||||||
MAYBE(ArchiveViewer)
|
}
|
||||||
MAYBE(FileConverter)
|
else if (g_InitialWindowType == _T("ScenarioEditor"))
|
||||||
#endif
|
|
||||||
#undef MAYBE
|
|
||||||
// else
|
|
||||||
if (g_InitialWindowType == _T("ScenarioEditor"))
|
|
||||||
{
|
{
|
||||||
m_ScriptInterface = new ScriptInterface(&ScenarioEditorSubmitCommand);
|
m_ScriptInterface = new ScriptInterface(&ScenarioEditorSubmitCommand);
|
||||||
frame = new ScenarioEditor(NULL, *m_ScriptInterface);
|
frame = new ScenarioEditor(NULL, *m_ScriptInterface);
|
||||||
|
@ -1,18 +0,0 @@
|
|||||||
/* Copyright (C) 2009 Wildfire Games.
|
|
||||||
* This file is part of 0 A.D.
|
|
||||||
*
|
|
||||||
* 0 A.D. is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 2 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* 0 A.D. is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with 0 A.D. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
const wxString g_ProgramNameVersion = _("AoE3Ed v0.9");
|
|
@ -1,142 +0,0 @@
|
|||||||
/* Copyright (C) 2011 Wildfire Games.
|
|
||||||
* This file is part of 0 A.D.
|
|
||||||
*
|
|
||||||
* 0 A.D. is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 2 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* 0 A.D. is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with 0 A.D. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "precompiled.h"
|
|
||||||
|
|
||||||
#include "BAR.h"
|
|
||||||
#include "Util.h"
|
|
||||||
#include "Stream/Stream.h"
|
|
||||||
#include "Stream/Memory.h"
|
|
||||||
|
|
||||||
#include <cassert>
|
|
||||||
#include <cstdio>
|
|
||||||
#include <cstring>
|
|
||||||
|
|
||||||
using namespace DatafileIO;
|
|
||||||
|
|
||||||
BARReader::BARReader(SeekableInputStream& stream)
|
|
||||||
: m_Stream(stream)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
#define CHECK(expr) if (!(expr)) { assert(!(expr)); return false; }
|
|
||||||
|
|
||||||
bool BARReader::Initialise()
|
|
||||||
{
|
|
||||||
assert(m_FileList.size() == 0); // Only call Init once
|
|
||||||
|
|
||||||
char head[4];
|
|
||||||
m_Stream.Read(head, 4);
|
|
||||||
CHECK(strncmp(head, "ESPN", 4) == 0);
|
|
||||||
|
|
||||||
uint32_t unknown;
|
|
||||||
m_Stream.Read(&unknown, 4);
|
|
||||||
CHECK(unknown == 2);
|
|
||||||
|
|
||||||
m_Stream.Read(&unknown, 4);
|
|
||||||
CHECK(unknown == 0x44332211);
|
|
||||||
|
|
||||||
for (int i = 0; i < 66; ++i)
|
|
||||||
{
|
|
||||||
m_Stream.Read(&unknown, 4);
|
|
||||||
CHECK(unknown == 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
m_Stream.Read(&unknown, 4); // TODO: checksum?
|
|
||||||
|
|
||||||
uint32_t numFiles, filetableOffset;
|
|
||||||
m_Stream.Read(&numFiles, 4);
|
|
||||||
m_Stream.Read(&filetableOffset, 4);
|
|
||||||
|
|
||||||
m_Stream.Read(&unknown, 4);
|
|
||||||
// 0 in AoE3
|
|
||||||
// Non-zero (e.g. 466a800a, 8df1e438) in AoEO; purpose unknown
|
|
||||||
|
|
||||||
m_Stream.Seek(filetableOffset, Stream::FROM_START);
|
|
||||||
|
|
||||||
utf16string rootName = ReadUString(m_Stream);
|
|
||||||
uint32_t numRootFiles;
|
|
||||||
m_Stream.Read(&numRootFiles, 4);
|
|
||||||
CHECK(numRootFiles == numFiles);
|
|
||||||
|
|
||||||
m_FileList.reserve(numFiles);
|
|
||||||
for (uint32_t i = 0; i < numFiles; ++i)
|
|
||||||
{
|
|
||||||
BAREntry file;
|
|
||||||
|
|
||||||
uint32_t offset, length0, length1;
|
|
||||||
m_Stream.Read(&offset, 4);
|
|
||||||
m_Stream.Read(&length0, 4);
|
|
||||||
m_Stream.Read(&length1, 4);
|
|
||||||
CHECK(length0 == length1); // ??
|
|
||||||
|
|
||||||
file.offset = offset;
|
|
||||||
file.filesize = length0;
|
|
||||||
|
|
||||||
// Ranges: 1995-2005, 1-12, 0-5, 1-31, 0+10-23, 0-59, 0-59, 0-999
|
|
||||||
m_Stream.Read(&file.modified.year, 2);
|
|
||||||
m_Stream.Read(&file.modified.month, 2);
|
|
||||||
m_Stream.Read(&file.modified.dayofweek, 2);
|
|
||||||
m_Stream.Read(&file.modified.day, 2);
|
|
||||||
m_Stream.Read(&file.modified.hour, 2);
|
|
||||||
m_Stream.Read(&file.modified.minute, 2);
|
|
||||||
m_Stream.Read(&file.modified.second, 2);
|
|
||||||
m_Stream.Read(&file.modified.msecond, 2);
|
|
||||||
|
|
||||||
if (file.modified.year == 0xCCCC) // no date specified
|
|
||||||
memset(&file.modified, 0, sizeof(file.modified));
|
|
||||||
|
|
||||||
file.filename = rootName + ReadUString(m_Stream);
|
|
||||||
|
|
||||||
m_FileList.push_back(file);
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: check this is really EOF
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct buffer_releaser
|
|
||||||
{
|
|
||||||
SeekableInputStream* stream;
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
SeekableInputStream* BARReader::GetFile(const BAREntry& file) const
|
|
||||||
{
|
|
||||||
void* buffer;
|
|
||||||
size_t size;
|
|
||||||
m_Stream.Seek((off_t)file.offset, Stream::FROM_START);
|
|
||||||
m_Stream.AcquireBuffer(buffer, size, file.filesize);
|
|
||||||
return new SeekableInputStream_mem((char*)buffer, size, new SeekableInputStream_mem::Releaser_StreamBuffer(&m_Stream));
|
|
||||||
}
|
|
||||||
|
|
||||||
void BARReader::TransferFile(const BAREntry& file, OutputStream& stream) const
|
|
||||||
{
|
|
||||||
m_Stream.Seek((off_t)file.offset, Stream::FROM_START);
|
|
||||||
const size_t bufSize = 128*1024; // people
|
|
||||||
static char* buffer[bufSize];
|
|
||||||
size_t bytesLeft = file.filesize;
|
|
||||||
while (bytesLeft)
|
|
||||||
{
|
|
||||||
size_t bytesRead = m_Stream.Read(buffer, std::min(bufSize, bytesLeft));
|
|
||||||
stream.Write(buffer, bytesRead);
|
|
||||||
bytesLeft -= bytesRead;
|
|
||||||
if (bytesRead == 0)
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,67 +0,0 @@
|
|||||||
/* Copyright (C) 2009 Wildfire Games.
|
|
||||||
* This file is part of 0 A.D.
|
|
||||||
*
|
|
||||||
* 0 A.D. is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 2 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* 0 A.D. is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with 0 A.D. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <string>
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
#include "../Util.h"
|
|
||||||
|
|
||||||
namespace DatafileIO
|
|
||||||
{
|
|
||||||
class SeekableInputStream;
|
|
||||||
class OutputStream;
|
|
||||||
|
|
||||||
struct BAREntry
|
|
||||||
{
|
|
||||||
utf16string filename; // includes root name - e.g. "Data\tactics\warwagon.tactics.xmb"
|
|
||||||
size_t filesize; // in bytes
|
|
||||||
struct {
|
|
||||||
unsigned short year, month, day, dayofweek; // 2005 etc, 1..12, 1..31, 0..6 (from Sunday)
|
|
||||||
unsigned short hour, minute, second, msecond; // 1..24, 0..59, 0..59, 0..999
|
|
||||||
// ...unless there's no date specified, in which case these will all be zero
|
|
||||||
} modified;
|
|
||||||
|
|
||||||
private: // implementation details
|
|
||||||
friend class BARReader;
|
|
||||||
size_t offset; // (assume all BARs are <4GB)
|
|
||||||
};
|
|
||||||
|
|
||||||
class BARReader
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
BARReader(SeekableInputStream& stream);
|
|
||||||
|
|
||||||
// Read the header and file table.
|
|
||||||
bool Initialise();
|
|
||||||
|
|
||||||
// Get list of files.
|
|
||||||
const std::vector<BAREntry>& GetFileList() const { return m_FileList; }
|
|
||||||
|
|
||||||
// Get a seekable input stream for the specified file.
|
|
||||||
// Multiple file streams can be open at the same time.
|
|
||||||
SeekableInputStream* GetFile(const BAREntry& file) const;
|
|
||||||
// Copy a file's contents from the archive to an output stream
|
|
||||||
void TransferFile(const BAREntry& file, OutputStream& stream) const;
|
|
||||||
|
|
||||||
private:
|
|
||||||
SeekableInputStream& m_Stream;
|
|
||||||
std::vector<BAREntry> m_FileList;
|
|
||||||
|
|
||||||
BARReader& operator=(const BARReader&);
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
|
@ -1,467 +0,0 @@
|
|||||||
/* Copyright (C) 2009 Wildfire Games.
|
|
||||||
* This file is part of 0 A.D.
|
|
||||||
*
|
|
||||||
* 0 A.D. is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 2 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* 0 A.D. is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with 0 A.D. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "precompiled.h"
|
|
||||||
|
|
||||||
#include "DDT.h"
|
|
||||||
|
|
||||||
#include "Stream/Stream.h"
|
|
||||||
#include "Util.h"
|
|
||||||
|
|
||||||
#include "IL/il.h"
|
|
||||||
#include "IL/ilu.h"
|
|
||||||
|
|
||||||
#include <cassert>
|
|
||||||
#include <string.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
|
|
||||||
using namespace DatafileIO;
|
|
||||||
|
|
||||||
DDTFile::DDTFile(SeekableInputStream& stream)
|
|
||||||
: m_Stream(stream)
|
|
||||||
{
|
|
||||||
// TODO: allow multiple nested DDTFiles, with ref-counted init
|
|
||||||
ilInit();
|
|
||||||
ilGenImages(1, &m_Image);
|
|
||||||
}
|
|
||||||
|
|
||||||
DDTFile::~DDTFile()
|
|
||||||
{
|
|
||||||
ilDeleteImages(1, &m_Image);
|
|
||||||
ilShutDown();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
struct DDTImage
|
|
||||||
{
|
|
||||||
int width, height;
|
|
||||||
off_t offset;
|
|
||||||
size_t length;
|
|
||||||
};
|
|
||||||
|
|
||||||
#ifdef USE_DEVIL_DXT
|
|
||||||
static void LoadDXT(int dxtType, unsigned char* oldData);
|
|
||||||
static void SaveDXT(int dxtType); // saves the currently bound image
|
|
||||||
static void SwizzleAGBR();
|
|
||||||
#endif
|
|
||||||
#ifdef USE_HACKED_DEVIL
|
|
||||||
static void ToggleOrigin(); // urgh
|
|
||||||
#endif
|
|
||||||
|
|
||||||
bool DDTFile::Read(FileType type)
|
|
||||||
{
|
|
||||||
ilBindImage(m_Image);
|
|
||||||
|
|
||||||
if (type == DDT)
|
|
||||||
{
|
|
||||||
char head[4];
|
|
||||||
m_Stream.Read(head, 4);
|
|
||||||
if (strncmp(head, "RTS3", 4) != 0)
|
|
||||||
{
|
|
||||||
// TODO: report helpful error message
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
char format[3];
|
|
||||||
m_Stream.Read(format, 3);
|
|
||||||
m_Type_Usage = (Type_Usage)format[0];
|
|
||||||
m_Type_Alpha = (Type_Alpha)format[1];
|
|
||||||
m_Type_Format = (Type_Format)format[2];
|
|
||||||
|
|
||||||
char mipmapLevels;
|
|
||||||
m_Stream.Read(&mipmapLevels, 1);
|
|
||||||
m_Type_Levels = mipmapLevels;
|
|
||||||
|
|
||||||
uint32_t baseWidth, baseHeight;
|
|
||||||
m_Stream.Read(&baseWidth, 4);
|
|
||||||
m_Stream.Read(&baseHeight, 4);
|
|
||||||
|
|
||||||
int numImagesPerLevel = (m_Type_Usage == CUBE ? 6 : 1);
|
|
||||||
int numImages = mipmapLevels * numImagesPerLevel;
|
|
||||||
|
|
||||||
std::vector<DDTImage> Images;
|
|
||||||
|
|
||||||
Images.resize(numImages);
|
|
||||||
for (int i = 0; i < numImages; ++i)
|
|
||||||
{
|
|
||||||
int width = baseWidth >> (i/numImagesPerLevel); if (width < 1) width = 1;
|
|
||||||
int height = baseHeight >> (i/numImagesPerLevel); if (height < 1) height = 1;
|
|
||||||
uint32_t offset, length;
|
|
||||||
m_Stream.Read(&offset, 4);
|
|
||||||
m_Stream.Read(&length, 4);
|
|
||||||
Images[i].width = width;
|
|
||||||
Images[i].height = height;
|
|
||||||
Images[i].offset = offset;
|
|
||||||
Images[i].length = length;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Read the first image. (TODO: cubemaps)
|
|
||||||
|
|
||||||
int w = Images[0].width;
|
|
||||||
int h = Images[0].height;
|
|
||||||
ilTexImage(w,h,1, 4, IL_RGBA, IL_UNSIGNED_BYTE, NULL);
|
|
||||||
|
|
||||||
unsigned char* newData = (unsigned char*)ilGetData();
|
|
||||||
switch (m_Type_Format)
|
|
||||||
{
|
|
||||||
case BGRA:
|
|
||||||
{
|
|
||||||
unsigned char* oldData = new unsigned char[w*h*4];
|
|
||||||
m_Stream.Read(oldData, w*h*4);
|
|
||||||
for (int i = 0; i < w*h; ++i)
|
|
||||||
{
|
|
||||||
newData[i*4+0] = oldData[i*4+2];
|
|
||||||
newData[i*4+1] = oldData[i*4+1];
|
|
||||||
newData[i*4+2] = oldData[i*4+0];
|
|
||||||
newData[i*4+3] = oldData[i*4+3];
|
|
||||||
}
|
|
||||||
delete[] oldData;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
#ifdef USE_DEVIL_DXT
|
|
||||||
case DXT1:
|
|
||||||
case DXT3:
|
|
||||||
case NORMSPEC:
|
|
||||||
{
|
|
||||||
int dxtType = (m_Type_Format == DXT1 ? 1 : m_Type_Format == DXT3 ? 3 : 5);
|
|
||||||
int pixPerByte = (m_Type_Format == DXT1 ? 2 : 1);
|
|
||||||
unsigned char* oldData = new unsigned char[w*h/pixPerByte];
|
|
||||||
m_Stream.Read(oldData, w*h/pixPerByte);
|
|
||||||
LoadDXT(dxtType, oldData);
|
|
||||||
if (m_Type_Format == NORMSPEC)
|
|
||||||
SwizzleAGBR();
|
|
||||||
delete[] oldData;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
case GREY:
|
|
||||||
{
|
|
||||||
unsigned char* oldData = new unsigned char[w*h];
|
|
||||||
m_Stream.Read(oldData, w*h);
|
|
||||||
for (int i = 0; i < w*h; ++i)
|
|
||||||
{
|
|
||||||
newData[i*4+0] =
|
|
||||||
newData[i*4+1] =
|
|
||||||
newData[i*4+2] = oldData[i];
|
|
||||||
newData[i*4+3] = 255;
|
|
||||||
}
|
|
||||||
delete[] oldData;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
//assert(! "Unhandled format");
|
|
||||||
ilClearColour(1.0f, 0.0f, 1.0f, 1.0f);
|
|
||||||
ilClearImage();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef USE_HACKED_DEVIL
|
|
||||||
ToggleOrigin(); // use this instead of iluFlip because we don't want to change the actual data
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
else if (type == TGA)
|
|
||||||
{
|
|
||||||
void* buffer;
|
|
||||||
size_t size;
|
|
||||||
m_Stream.AcquireBuffer(buffer, size);
|
|
||||||
ilLoadL(IL_TGA, buffer, (ILuint)size);
|
|
||||||
m_Stream.ReleaseBuffer(buffer);
|
|
||||||
ilConvertImage(IL_RGBA, IL_UNSIGNED_BYTE);
|
|
||||||
iluFlipImage();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
assert(! "Invalid type");
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
bool DDTFile::GetImageData(void*& buffer, int& width, int& height, bool realAlpha)
|
|
||||||
{
|
|
||||||
ilBindImage(m_Image);
|
|
||||||
|
|
||||||
width = ilGetInteger(IL_IMAGE_WIDTH);
|
|
||||||
height = ilGetInteger(IL_IMAGE_HEIGHT);
|
|
||||||
if (realAlpha)
|
|
||||||
{
|
|
||||||
buffer = malloc(width*height * 4);
|
|
||||||
memcpy(buffer, ilGetData(), width*height * 4);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
buffer = malloc(width*height * 3 * 2);
|
|
||||||
unsigned char* newData = (unsigned char*)buffer;
|
|
||||||
unsigned char* oldData = (unsigned char*)ilGetData();
|
|
||||||
|
|
||||||
for (int i = 0; i < width*height; ++i)
|
|
||||||
{
|
|
||||||
newData[i*3+0] = oldData[i*4+0];
|
|
||||||
newData[i*3+1] = oldData[i*4+1];
|
|
||||||
newData[i*3+2] = oldData[i*4+2];
|
|
||||||
}
|
|
||||||
for (int i = 0; i < width*height; ++i)
|
|
||||||
{
|
|
||||||
newData[(i+width*height)*3+0] =
|
|
||||||
newData[(i+width*height)*3+1] =
|
|
||||||
newData[(i+width*height)*3+2] = oldData[i*4+3];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
height *= 2;
|
|
||||||
|
|
||||||
return true;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
// DevIL code: (slightly nasty, since DevIL doesn't seem to be flexible enough
|
|
||||||
// to do what I need it to do...)
|
|
||||||
|
|
||||||
struct ILOutputStream
|
|
||||||
{
|
|
||||||
static OutputStream* stream;
|
|
||||||
static ILHANDLE ILAPIENTRY Open(const ILstring)
|
|
||||||
{
|
|
||||||
return (void*)-1;
|
|
||||||
}
|
|
||||||
static void ILAPIENTRY Close(ILHANDLE)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
static ILint ILAPIENTRY Putc(ILubyte c, ILHANDLE)
|
|
||||||
{
|
|
||||||
stream->Write(&c, 1);
|
|
||||||
return c;
|
|
||||||
}
|
|
||||||
static ILint ILAPIENTRY Seek(ILHANDLE, ILint /*offset*/, ILint /*whence*/)
|
|
||||||
{
|
|
||||||
assert(! "Not implemented");
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
static ILint ILAPIENTRY Tell(ILHANDLE)
|
|
||||||
{
|
|
||||||
return stream->Tell();
|
|
||||||
}
|
|
||||||
static ILint ILAPIENTRY Write(const void* data, ILuint size, ILuint count, ILHANDLE)
|
|
||||||
{
|
|
||||||
if (size*count)
|
|
||||||
stream->Write(data, size*count);
|
|
||||||
return count;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
OutputStream* ILOutputStream::stream = NULL;
|
|
||||||
|
|
||||||
extern "C" {
|
|
||||||
extern ILboolean ilSaveTargaF(ILHANDLE File);
|
|
||||||
// because DevIL doesn't want to write to things that aren't
|
|
||||||
// really files, so we have to use its internal writing functions
|
|
||||||
extern void iSetOutputFile(ILHANDLE File);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool DDTFile::SaveFile(OutputStream& stream, FileType outputType)
|
|
||||||
{
|
|
||||||
ilBindImage(m_Image);
|
|
||||||
|
|
||||||
ilSetWrite(&ILOutputStream::Open, &ILOutputStream::Close, &ILOutputStream::Putc,
|
|
||||||
&ILOutputStream::Seek, &ILOutputStream::Tell, &ILOutputStream::Write);
|
|
||||||
iSetOutputFile(NULL); // make sure it's using the right output functions
|
|
||||||
|
|
||||||
ILOutputStream::stream = &stream;
|
|
||||||
|
|
||||||
if (outputType == TGA)
|
|
||||||
{
|
|
||||||
ilSaveTargaF(NULL);
|
|
||||||
}
|
|
||||||
else if (outputType == DDT)
|
|
||||||
{
|
|
||||||
int bpp;
|
|
||||||
switch (m_Type_Format)
|
|
||||||
{
|
|
||||||
case BGRA: bpp = 32; break;
|
|
||||||
case GREY: bpp = 8; break;
|
|
||||||
case DXT1: bpp = 4; break;
|
|
||||||
case DXT3: bpp = 8; break;
|
|
||||||
case NORMSPEC: bpp = 8; break;
|
|
||||||
default: assert(! "Invalid format"); return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
stream.Write("RTS3", 4);
|
|
||||||
|
|
||||||
char format[4];
|
|
||||||
format[0] = (char)m_Type_Usage;
|
|
||||||
format[1] = (char)m_Type_Alpha;
|
|
||||||
format[2] = (char)m_Type_Format;
|
|
||||||
format[3] = (char)m_Type_Levels;
|
|
||||||
stream.Write(format, 4);
|
|
||||||
|
|
||||||
uint32_t baseWidth, baseHeight;
|
|
||||||
baseWidth = ilGetInteger(IL_IMAGE_WIDTH);
|
|
||||||
baseHeight = ilGetInteger(IL_IMAGE_HEIGHT);
|
|
||||||
stream.Write(&baseWidth, 4);
|
|
||||||
stream.Write(&baseHeight, 4);
|
|
||||||
|
|
||||||
int numImagesPerLevel = 1; // TODO: cubemaps
|
|
||||||
int numImages = m_Type_Levels * numImagesPerLevel;
|
|
||||||
uint32_t imgOffset = 16 + 8*numImages;
|
|
||||||
|
|
||||||
for (int i = 0; i < numImages; ++i)
|
|
||||||
{
|
|
||||||
int width = baseWidth >> (i/numImagesPerLevel); if (width < 1) width = 1;
|
|
||||||
int height = baseHeight >> (i/numImagesPerLevel); if (height < 1) height = 1;
|
|
||||||
uint32_t length = width*height * bpp / 8;
|
|
||||||
stream.Write(&imgOffset, 4);
|
|
||||||
stream.Write(&length, 4);
|
|
||||||
imgOffset += length;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (int i = 0; i < numImages; ++i)
|
|
||||||
{
|
|
||||||
int width = baseWidth >> (i/numImagesPerLevel); if (width < 1) width = 1;
|
|
||||||
int height = baseHeight >> (i/numImagesPerLevel); if (height < 1) height = 1;
|
|
||||||
|
|
||||||
ilBindImage(m_Image);
|
|
||||||
|
|
||||||
ILuint img = ilCloneCurImage();
|
|
||||||
ilBindImage(img);
|
|
||||||
iluImageParameter(ILU_FILTER, ILU_SCALE_BOX); // TODO - proper mipmapping
|
|
||||||
iluScale(width, height, 1);
|
|
||||||
|
|
||||||
switch (m_Type_Format)
|
|
||||||
{
|
|
||||||
case BGRA:
|
|
||||||
{
|
|
||||||
unsigned char* newData = new unsigned char[width*height*4];
|
|
||||||
unsigned char* oldData = (unsigned char*)ilGetData();
|
|
||||||
for (int i = 0; i < width*height; ++i)
|
|
||||||
{
|
|
||||||
newData[i*4+0] = oldData[i*4+2];
|
|
||||||
newData[i*4+1] = oldData[i*4+1];
|
|
||||||
newData[i*4+2] = oldData[i*4+0];
|
|
||||||
newData[i*4+3] = oldData[i*4+3];
|
|
||||||
}
|
|
||||||
stream.Write(newData, width*height*4);
|
|
||||||
delete[] newData;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case GREY:
|
|
||||||
{
|
|
||||||
unsigned char* newData = new unsigned char[width*height];
|
|
||||||
unsigned char* oldData = (unsigned char*)ilGetData();
|
|
||||||
for (int i = 0; i < width*height; ++i)
|
|
||||||
{
|
|
||||||
newData[i] = oldData[i*4+0];
|
|
||||||
}
|
|
||||||
stream.Write(newData, width*height);
|
|
||||||
delete[] newData;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
#ifdef USE_DEVIL_DXT
|
|
||||||
case DXT1:
|
|
||||||
SaveDXT(1);
|
|
||||||
break;
|
|
||||||
case DXT3:
|
|
||||||
SaveDXT(3);
|
|
||||||
break;
|
|
||||||
case NORMSPEC:
|
|
||||||
SwizzleAGBR();
|
|
||||||
SaveDXT(5);
|
|
||||||
break;
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
ilDeleteImages(1, &img);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ilResetWrite();
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// Evilness:
|
|
||||||
#ifdef USE_HACKED_DEVIL
|
|
||||||
#include "IL/devil_internal_exports.h"
|
|
||||||
extern "C"
|
|
||||||
{
|
|
||||||
extern ILboolean DecompressDXT1();
|
|
||||||
extern ILboolean DecompressDXT3();
|
|
||||||
extern ILboolean DecompressDXT5();
|
|
||||||
extern ILuint Compress(ILimage* Image, ILenum DXTCFormat);
|
|
||||||
extern ILimage* iCurImage;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void ToggleOrigin()
|
|
||||||
{
|
|
||||||
iCurImage->Origin = (iCurImage->Origin == IL_ORIGIN_UPPER_LEFT ? IL_ORIGIN_LOWER_LEFT : IL_ORIGIN_UPPER_LEFT);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef USE_DEVIL_DXT
|
|
||||||
extern "C"
|
|
||||||
{
|
|
||||||
extern ILubyte* CompData;
|
|
||||||
extern ILint Depth, Width, Height;
|
|
||||||
extern ILimage* Image;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void LoadDXT(int dxtType, unsigned char* oldData)
|
|
||||||
{
|
|
||||||
// More evilness, that assumes a lot about DevIL's internals:
|
|
||||||
CompData = (ILubyte*)oldData;
|
|
||||||
Image = iCurImage;
|
|
||||||
Width = Image->Width;
|
|
||||||
Height = Image->Height;
|
|
||||||
Depth = Image->Depth;
|
|
||||||
|
|
||||||
switch (dxtType)
|
|
||||||
{
|
|
||||||
case 1: DecompressDXT1(); break;
|
|
||||||
case 3: DecompressDXT3(); break;
|
|
||||||
case 5: DecompressDXT5(); break;
|
|
||||||
default: assert(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
CompData = NULL;
|
|
||||||
Image = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void SwizzleAGBR()
|
|
||||||
{
|
|
||||||
ILubyte* data = ilGetData();
|
|
||||||
ILint pixels = ilGetInteger(IL_IMAGE_WIDTH)*ilGetInteger(IL_IMAGE_HEIGHT);
|
|
||||||
for (ILint i = 0; i < pixels; ++i)
|
|
||||||
{
|
|
||||||
ILubyte t = data[i*4+0];
|
|
||||||
data[i*4+0] = data[i*4+3];
|
|
||||||
data[i*4+3] = t;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void SaveDXT(int dxtType)
|
|
||||||
{
|
|
||||||
Compress(ilGetCurImage(), dxtType==1 ? IL_DXT1 : dxtType==3 ? IL_DXT3 : IL_DXT5);
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
@ -1,91 +0,0 @@
|
|||||||
/* Copyright (C) 2009 Wildfire Games.
|
|
||||||
* This file is part of 0 A.D.
|
|
||||||
*
|
|
||||||
* 0 A.D. is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 2 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* 0 A.D. is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with 0 A.D. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
typedef unsigned int ILuint;
|
|
||||||
|
|
||||||
namespace DatafileIO
|
|
||||||
{
|
|
||||||
class SeekableInputStream;
|
|
||||||
class OutputStream;
|
|
||||||
|
|
||||||
class DDTFile
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
enum FileType { TGA, BMP, DDT };
|
|
||||||
|
|
||||||
// Initialises the file, but doesn't actually read anything
|
|
||||||
DDTFile(SeekableInputStream& stream);
|
|
||||||
|
|
||||||
~DDTFile();
|
|
||||||
|
|
||||||
// Attempts to read the file, and returns true on success
|
|
||||||
bool Read(FileType type);
|
|
||||||
|
|
||||||
bool SaveFile(OutputStream& stream, FileType outputType);
|
|
||||||
|
|
||||||
// All arguments are outputs. buffer is allocated by malloc, and
|
|
||||||
// must be freed by the caller. If realAlpha is true, the buffer
|
|
||||||
// will be RGBA, else it'll be RGB (with height doubled and the alpha
|
|
||||||
// stuck on the bottom)
|
|
||||||
bool GetImageData(void*& buffer, int& width, int& height, bool realAlpha);
|
|
||||||
|
|
||||||
enum Type_Usage {
|
|
||||||
UNK0 = 0, // ??
|
|
||||||
UNK1 = 1, // ??
|
|
||||||
BUMP = 6,
|
|
||||||
UNK2 = 7, // ??
|
|
||||||
CUBE = 8
|
|
||||||
};
|
|
||||||
enum Type_Alpha {
|
|
||||||
NONE = 0, // ??
|
|
||||||
PLAYER = 1, // ?? }
|
|
||||||
TRANS = 4, // ?? } these names are completely incorrect
|
|
||||||
BLEND = 8, // mostly unused
|
|
||||||
};
|
|
||||||
enum Type_Format {
|
|
||||||
BGRA = 1,
|
|
||||||
DXT1 = 4,
|
|
||||||
GREY = 7,
|
|
||||||
DXT3 = 8,
|
|
||||||
NORMSPEC = 9 // DXT5, with spec in R channel, XYZ in AGB channels.
|
|
||||||
// (See e.g. http://www.ati.com/developer/NormalMapCompression.pdf)
|
|
||||||
};
|
|
||||||
|
|
||||||
// (These values are not guaranteed to actually be in the enums)
|
|
||||||
Type_Usage m_Type_Usage;
|
|
||||||
Type_Alpha m_Type_Alpha;
|
|
||||||
Type_Format m_Type_Format;
|
|
||||||
|
|
||||||
int m_Type_Levels; // of mipmaps
|
|
||||||
|
|
||||||
// struct Image
|
|
||||||
// {
|
|
||||||
// int width, height;
|
|
||||||
// off_t offset;
|
|
||||||
// size_t length;
|
|
||||||
// };
|
|
||||||
// std::vector<Image> m_Images;
|
|
||||||
ILuint m_Image;
|
|
||||||
|
|
||||||
private:
|
|
||||||
SeekableInputStream& m_Stream;
|
|
||||||
|
|
||||||
DDTFile& operator=(const DDTFile&);
|
|
||||||
};
|
|
||||||
}
|
|
@ -1,148 +0,0 @@
|
|||||||
/* Copyright (C) 2009 Wildfire Games.
|
|
||||||
* This file is part of 0 A.D.
|
|
||||||
*
|
|
||||||
* 0 A.D. is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 2 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* 0 A.D. is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with 0 A.D. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "precompiled.h"
|
|
||||||
|
|
||||||
#include "Memory.h"
|
|
||||||
|
|
||||||
#include "../Util.h"
|
|
||||||
|
|
||||||
#include "zlib.h"
|
|
||||||
|
|
||||||
#include <algorithm>
|
|
||||||
#include <cassert>
|
|
||||||
#include <memory.h>
|
|
||||||
|
|
||||||
using namespace DatafileIO;
|
|
||||||
|
|
||||||
SeekableInputStream_mem::SeekableInputStream_mem(char* data, size_t size, Releaser* releaser)
|
|
||||||
: m_Data(data), m_Size(size), m_Cursor(0), m_Releaser(releaser)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
SeekableInputStream_mem::~SeekableInputStream_mem()
|
|
||||||
{
|
|
||||||
m_Releaser->release(m_Data);
|
|
||||||
delete m_Releaser;
|
|
||||||
}
|
|
||||||
|
|
||||||
off_t SeekableInputStream_mem::Tell() const
|
|
||||||
{
|
|
||||||
return m_Cursor;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool SeekableInputStream_mem::IsOk() const
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void SeekableInputStream_mem::Seek(off_t pos, Stream::whence mode)
|
|
||||||
{
|
|
||||||
if (mode == FROM_START)
|
|
||||||
m_Cursor = pos;
|
|
||||||
else if (mode == FROM_CURRENT)
|
|
||||||
m_Cursor += pos;
|
|
||||||
else if (mode == FROM_END)
|
|
||||||
m_Cursor = (off_t)m_Size - pos;
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t SeekableInputStream_mem::Read(void* buffer, size_t size)
|
|
||||||
{
|
|
||||||
if (m_Cursor >= (off_t)m_Size)
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
size_t amount = size;
|
|
||||||
if (m_Cursor + amount > m_Size)
|
|
||||||
amount = m_Size - m_Cursor;
|
|
||||||
|
|
||||||
std::copy(m_Data+m_Cursor, m_Data+m_Cursor+amount, (char*)buffer);
|
|
||||||
m_Cursor += (off_t)amount;
|
|
||||||
|
|
||||||
return amount;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool SeekableInputStream_mem::AcquireBuffer(void*& buffer, size_t& size, size_t max_size)
|
|
||||||
{
|
|
||||||
buffer = m_Data + m_Cursor;
|
|
||||||
size = m_Size - m_Cursor;
|
|
||||||
if (size > max_size) size = max_size;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
void SeekableInputStream_mem::ReleaseBuffer(void* /*buffer*/)
|
|
||||||
{
|
|
||||||
/* do nothing */
|
|
||||||
}
|
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
Maybel33tInputStream::Maybel33tInputStream(SeekableInputStream* stream)
|
|
||||||
: m_Stream(NULL)
|
|
||||||
{
|
|
||||||
char head[8];
|
|
||||||
size_t bytes = stream->Read(head, 8);
|
|
||||||
if (bytes == 8 && strncmp(head, "l33t", 4) == 0)
|
|
||||||
{
|
|
||||||
uint32_t uncompressedSize;
|
|
||||||
memcpy(&uncompressedSize, &head[4], 4);
|
|
||||||
|
|
||||||
void* buffer;
|
|
||||||
size_t size;
|
|
||||||
if (! stream->AcquireBuffer(buffer, size))
|
|
||||||
{
|
|
||||||
assert(!"Buffer acquisition unsuccessful");
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
char* uncompressedBuffer = new char[uncompressedSize];
|
|
||||||
uLongf newUncomprSize = (uLongf)uncompressedSize;
|
|
||||||
int err = uncompress((Bytef*)uncompressedBuffer, &newUncomprSize, (Bytef*)buffer, (uLong)size);
|
|
||||||
if (err != Z_OK)
|
|
||||||
{
|
|
||||||
assert(!"Decompression failed");
|
|
||||||
delete[] uncompressedBuffer;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
assert(newUncomprSize == uncompressedSize);
|
|
||||||
m_Stream = new SeekableInputStream_mem(uncompressedBuffer, uncompressedSize, new SeekableInputStream_mem::Releaser_NewArray);
|
|
||||||
}
|
|
||||||
stream->ReleaseBuffer(buffer);
|
|
||||||
}
|
|
||||||
delete stream;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
stream->Seek(-(off_t)bytes, FROM_CURRENT);
|
|
||||||
m_Stream = stream;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
Maybel33tInputStream::~Maybel33tInputStream()
|
|
||||||
{
|
|
||||||
delete m_Stream;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Maybel33tInputStream::IsOk() const
|
|
||||||
{
|
|
||||||
return (m_Stream != NULL);
|
|
||||||
}
|
|
||||||
|
|
||||||
off_t Maybel33tInputStream::Tell() const { return m_Stream->Tell(); }
|
|
||||||
void Maybel33tInputStream::Seek(off_t pos, Stream::whence mode) { m_Stream->Seek(pos, mode); }
|
|
||||||
size_t Maybel33tInputStream::Read(void* buffer, size_t size) { return m_Stream->Read(buffer, size); }
|
|
||||||
bool Maybel33tInputStream::AcquireBuffer(void*& buffer, size_t& size, size_t max_size) { return m_Stream->AcquireBuffer(buffer, size, max_size); }
|
|
||||||
void Maybel33tInputStream::ReleaseBuffer(void* buffer) { m_Stream->ReleaseBuffer(buffer); }
|
|
@ -1,81 +0,0 @@
|
|||||||
/* Copyright (C) 2009 Wildfire Games.
|
|
||||||
* This file is part of 0 A.D.
|
|
||||||
*
|
|
||||||
* 0 A.D. is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 2 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* 0 A.D. is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with 0 A.D. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "Stream.h"
|
|
||||||
|
|
||||||
namespace DatafileIO
|
|
||||||
{
|
|
||||||
|
|
||||||
class SeekableInputStream_mem : public SeekableInputStream
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
// 'Release' provides common buffer-release mechanisms, called
|
|
||||||
// when this stream is destroyed
|
|
||||||
struct Releaser {
|
|
||||||
virtual ~Releaser() {}
|
|
||||||
virtual void release(char* buffer) = 0;
|
|
||||||
};
|
|
||||||
struct Releaser_None : public Releaser {
|
|
||||||
virtual void release(char* /*buffer*/) {};
|
|
||||||
};
|
|
||||||
struct Releaser_StreamBuffer : public Releaser {
|
|
||||||
Releaser_StreamBuffer(InputStream* s) : stream(s) {}
|
|
||||||
InputStream* stream;
|
|
||||||
virtual void release(char* buffer) {
|
|
||||||
stream->ReleaseBuffer(buffer);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
struct Releaser_NewArray : public Releaser {
|
|
||||||
virtual void release(char* buffer) {
|
|
||||||
delete[] buffer;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
SeekableInputStream_mem(char* data, size_t size, Releaser* releaser);
|
|
||||||
~SeekableInputStream_mem();
|
|
||||||
virtual off_t Tell() const;
|
|
||||||
virtual bool IsOk() const;
|
|
||||||
virtual void Seek(off_t pos, Stream::whence mode);
|
|
||||||
virtual size_t Read(void* buffer, size_t size);
|
|
||||||
virtual bool AcquireBuffer(void*& buffer, size_t& size, size_t max_size);
|
|
||||||
virtual void ReleaseBuffer(void* buffer);
|
|
||||||
private:
|
|
||||||
Releaser* m_Releaser;
|
|
||||||
char* m_Data;
|
|
||||||
size_t m_Size;
|
|
||||||
off_t m_Cursor;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
// Magically decompresses l33t-compressed files.
|
|
||||||
class Maybel33tInputStream : public SeekableInputStream
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
// Take ownership of a SeekableInputStream
|
|
||||||
Maybel33tInputStream(SeekableInputStream* stream);
|
|
||||||
~Maybel33tInputStream();
|
|
||||||
|
|
||||||
virtual off_t Tell() const;
|
|
||||||
virtual bool IsOk() const;
|
|
||||||
virtual void Seek(off_t pos, Stream::whence mode);
|
|
||||||
virtual size_t Read(void* buffer, size_t size);
|
|
||||||
virtual bool AcquireBuffer(void*& buffer, size_t& size, size_t max_size);
|
|
||||||
virtual void ReleaseBuffer(void* buffer);
|
|
||||||
private:
|
|
||||||
SeekableInputStream* m_Stream;
|
|
||||||
};
|
|
||||||
}
|
|
@ -1,52 +0,0 @@
|
|||||||
/* Copyright (C) 2009 Wildfire Games.
|
|
||||||
* This file is part of 0 A.D.
|
|
||||||
*
|
|
||||||
* 0 A.D. is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 2 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* 0 A.D. is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with 0 A.D. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "precompiled.h"
|
|
||||||
|
|
||||||
#include "Stream.h"
|
|
||||||
|
|
||||||
#include <cassert>
|
|
||||||
#include <string>
|
|
||||||
#include <memory.h>
|
|
||||||
|
|
||||||
using namespace DatafileIO;
|
|
||||||
|
|
||||||
bool InputStream::AcquireBuffer(void*& buffer, size_t& size, size_t max_size)
|
|
||||||
{
|
|
||||||
std::string data;
|
|
||||||
const size_t tempBufSize = 65536;
|
|
||||||
static char tempBuffer[tempBufSize];
|
|
||||||
size = 0;
|
|
||||||
size_t bytesLeft = max_size;
|
|
||||||
size_t bytesRead;
|
|
||||||
while (bytesLeft > 0 && (bytesRead = Read(tempBuffer, std::min(bytesLeft, tempBufSize))) != 0)
|
|
||||||
{
|
|
||||||
size += bytesRead;
|
|
||||||
bytesLeft -= bytesRead;
|
|
||||||
data += std::string(tempBuffer, bytesRead);
|
|
||||||
}
|
|
||||||
|
|
||||||
buffer = new char[size];
|
|
||||||
memcpy(buffer, data.data(), size);
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void InputStream::ReleaseBuffer(void* buffer)
|
|
||||||
{
|
|
||||||
delete[] (char*)buffer;
|
|
||||||
}
|
|
@ -1,90 +0,0 @@
|
|||||||
/* Copyright (C) 2009 Wildfire Games.
|
|
||||||
* This file is part of 0 A.D.
|
|
||||||
*
|
|
||||||
* 0 A.D. is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 2 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* 0 A.D. is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with 0 A.D. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef INCLUDED_STREAM
|
|
||||||
#define INCLUDED_STREAM
|
|
||||||
|
|
||||||
/*
|
|
||||||
Stream: A system for input/output of data, particularly with chained streams
|
|
||||||
(e.g. file -> zlib decompressor -> data processor).
|
|
||||||
|
|
||||||
Similar to wxWidget's streams, but rewritten so that code can use this without
|
|
||||||
requiring wx.
|
|
||||||
|
|
||||||
TODO: This concept might not actually be any good; consider alternatives.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <sys/types.h>
|
|
||||||
#include <limits>
|
|
||||||
|
|
||||||
namespace DatafileIO
|
|
||||||
{
|
|
||||||
class Stream
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
enum whence { FROM_START, FROM_END, FROM_CURRENT };
|
|
||||||
|
|
||||||
virtual ~Stream() {}
|
|
||||||
virtual off_t Tell() const = 0;
|
|
||||||
virtual bool IsOk() const = 0;
|
|
||||||
};
|
|
||||||
|
|
||||||
class SeekableStream
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
virtual ~SeekableStream() {}
|
|
||||||
virtual void Seek(off_t pos, Stream::whence mode) = 0;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
class InputStream : public Stream
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
// Try to read up to 'size' bytes into buffer, and return the amount
|
|
||||||
// actually read.
|
|
||||||
virtual size_t Read(void* buffer, size_t size) = 0;
|
|
||||||
|
|
||||||
// Sets 'buffer' and 'size' to point to the data from the current cursor
|
|
||||||
// position up to max_size bytes (or the end of the file, whichever is
|
|
||||||
// reached first) and returns true if successful.
|
|
||||||
// If a derived stream doesn't implement these, a default (which just
|
|
||||||
// calls Read to get all the data) will be used instead. (It is implemented
|
|
||||||
// in e.g. streams that already have a pointer to all the data, so they
|
|
||||||
// don't need to do any memory copying.)
|
|
||||||
virtual bool AcquireBuffer(void*& buffer, size_t& size, size_t max_size = std::numeric_limits<size_t>::max());
|
|
||||||
virtual void ReleaseBuffer(void* buffer);
|
|
||||||
};
|
|
||||||
|
|
||||||
class OutputStream : public Stream
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
virtual void Write(const void* buffer, size_t size) = 0;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
// Specialisations to indicate that a stream allows seeking.
|
|
||||||
class SeekableInputStream : public InputStream, public SeekableStream
|
|
||||||
{
|
|
||||||
};
|
|
||||||
|
|
||||||
class SeekableOutputStream : public OutputStream, public SeekableStream
|
|
||||||
{
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif // INCLUDED_STREAM
|
|
@ -1,115 +0,0 @@
|
|||||||
/* Copyright (C) 2009 Wildfire Games.
|
|
||||||
* This file is part of 0 A.D.
|
|
||||||
*
|
|
||||||
* 0 A.D. is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 2 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* 0 A.D. is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with 0 A.D. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "Stream.h"
|
|
||||||
|
|
||||||
#include "wx/mstream.h"
|
|
||||||
#include "wx/zstream.h"
|
|
||||||
|
|
||||||
namespace DatafileIO
|
|
||||||
{
|
|
||||||
|
|
||||||
// Wrappers around wx streams, so they can be used as [non-wx] Streams.
|
|
||||||
// (This code is all inline so that the project can be compiled without
|
|
||||||
// requiring wxWidgets.)
|
|
||||||
|
|
||||||
class SeekableInputStreamFromWx : public SeekableInputStream
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
// Take ownership of a wxInputStream
|
|
||||||
SeekableInputStreamFromWx(wxInputStream* stream)
|
|
||||||
: m_Stream(stream), m_Owner(true) {}
|
|
||||||
|
|
||||||
// Use an externally-owned wxInputStream
|
|
||||||
SeekableInputStreamFromWx(wxInputStream& stream)
|
|
||||||
: m_Stream(&stream), m_Owner(false) {}
|
|
||||||
|
|
||||||
~SeekableInputStreamFromWx()
|
|
||||||
{
|
|
||||||
if (m_Owner)
|
|
||||||
delete m_Stream;
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual off_t Tell() const { return m_Stream->TellI(); }
|
|
||||||
virtual bool IsOk() const { return m_Stream->IsOk(); }
|
|
||||||
|
|
||||||
virtual void Seek(off_t pos, Stream::whence mode)
|
|
||||||
{
|
|
||||||
m_Stream->SeekI(pos,
|
|
||||||
mode==Stream::FROM_START ? wxFromStart
|
|
||||||
: mode==Stream::FROM_END ? wxFromEnd
|
|
||||||
: /*mode==Stream::FROM_CURRENT*/ wxFromCurrent);
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual size_t Read(void* buffer, size_t size)
|
|
||||||
{
|
|
||||||
m_Stream->Read(buffer, size);
|
|
||||||
return m_Stream->LastRead();
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
wxInputStream* m_Stream;
|
|
||||||
bool m_Owner;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
class SeekableOutputStreamFromWx : public SeekableOutputStream
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
// Take ownership of a wxInputStream
|
|
||||||
SeekableOutputStreamFromWx(wxOutputStream* stream)
|
|
||||||
: m_Stream(stream), m_Owner(true) {}
|
|
||||||
|
|
||||||
// Use an externally-owned wxInputStream
|
|
||||||
SeekableOutputStreamFromWx(wxOutputStream& stream)
|
|
||||||
: m_Stream(&stream), m_Owner(false) {}
|
|
||||||
|
|
||||||
~SeekableOutputStreamFromWx()
|
|
||||||
{
|
|
||||||
if (m_Owner)
|
|
||||||
delete m_Stream;
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual off_t Tell() const
|
|
||||||
{
|
|
||||||
return m_Stream->TellO();
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual bool IsOk() const
|
|
||||||
{
|
|
||||||
return m_Stream->IsOk();
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual void Seek(off_t pos, Stream::whence mode)
|
|
||||||
{
|
|
||||||
m_Stream->SeekO(pos,
|
|
||||||
mode==Stream::FROM_START ? wxFromStart
|
|
||||||
: mode==Stream::FROM_END ? wxFromEnd
|
|
||||||
: /*mode==Stream::FROM_CURRENT*/ wxFromCurrent);
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual void Write(const void* buffer, size_t size)
|
|
||||||
{
|
|
||||||
m_Stream->Write(buffer, size);
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
wxOutputStream* m_Stream;
|
|
||||||
bool m_Owner;
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
|
@ -1,57 +0,0 @@
|
|||||||
/* Copyright (C) 2009 Wildfire Games.
|
|
||||||
* This file is part of 0 A.D.
|
|
||||||
*
|
|
||||||
* 0 A.D. is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 2 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* 0 A.D. is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with 0 A.D. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "precompiled.h"
|
|
||||||
|
|
||||||
#include "Util.h"
|
|
||||||
#include "Stream/Stream.h"
|
|
||||||
|
|
||||||
using namespace DatafileIO;
|
|
||||||
|
|
||||||
utf16string DatafileIO::ReadUString(InputStream& stream)
|
|
||||||
{
|
|
||||||
uint32_t length;
|
|
||||||
stream.Read(&length, 4);
|
|
||||||
|
|
||||||
utf16string ret;
|
|
||||||
ret.resize(length);
|
|
||||||
stream.Read(&ret[0], length*2);
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
void DatafileIO::WriteUString(OutputStream& stream, const utf16string& string)
|
|
||||||
{
|
|
||||||
uint32_t length = (uint32_t)string.length();
|
|
||||||
stream.Write(&length, 4);
|
|
||||||
stream.Write((utf16_t*)&string[0], length*2);
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifndef _WIN32
|
|
||||||
// TODO In reality, these two should be able to de/encode UTF-16 to/from UCS-4
|
|
||||||
// instead of just treating UTF-16 as UCS-2
|
|
||||||
|
|
||||||
std::wstring DatafileIO::utf16tow(const utf16string &str)
|
|
||||||
{
|
|
||||||
return std::wstring(str.begin(), str.end());
|
|
||||||
}
|
|
||||||
|
|
||||||
utf16string DatafileIO::wtoutf16(const std::wstring &str)
|
|
||||||
{
|
|
||||||
return utf16string(str.begin(), str.end());
|
|
||||||
}
|
|
||||||
#endif
|
|
@ -1,64 +0,0 @@
|
|||||||
/* Copyright (C) 2009 Wildfire Games.
|
|
||||||
* This file is part of 0 A.D.
|
|
||||||
*
|
|
||||||
* 0 A.D. is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 2 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* 0 A.D. is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with 0 A.D. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef INCLUDED_UTIL
|
|
||||||
#define INCLUDED_UTIL
|
|
||||||
|
|
||||||
#include <string>
|
|
||||||
#include <cassert>
|
|
||||||
#include <stdint.h>
|
|
||||||
|
|
||||||
#ifndef C_ASSERT
|
|
||||||
#define C_ASSERT(e) typedef char __C_ASSERT__[(e)?1:-1]
|
|
||||||
#endif
|
|
||||||
|
|
||||||
namespace DatafileIO
|
|
||||||
{
|
|
||||||
#ifdef _WIN32
|
|
||||||
// TODO: proper portability
|
|
||||||
typedef int int32_t;
|
|
||||||
typedef unsigned int uint32_t;
|
|
||||||
typedef unsigned short uint16_t;
|
|
||||||
typedef std::wstring utf16string;
|
|
||||||
typedef wchar_t utf16_t;
|
|
||||||
#else
|
|
||||||
typedef uint16_t utf16_t;
|
|
||||||
typedef std::basic_string<utf16_t> utf16string;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
C_ASSERT(sizeof(int32_t) == 4);
|
|
||||||
C_ASSERT(sizeof(uint32_t) == 4);
|
|
||||||
C_ASSERT(sizeof(uint16_t) == 2);
|
|
||||||
C_ASSERT(sizeof(utf16_t) == 2);
|
|
||||||
|
|
||||||
class InputStream;
|
|
||||||
class OutputStream;
|
|
||||||
|
|
||||||
// Read/write 4-byte length + UCS-2 string
|
|
||||||
utf16string ReadUString(InputStream& stream);
|
|
||||||
void WriteUString(OutputStream& stream, const utf16string& string);
|
|
||||||
|
|
||||||
#ifdef _WIN32
|
|
||||||
# define utf16tow(_str) _str
|
|
||||||
# define wtoutf16(_str) _str
|
|
||||||
#else
|
|
||||||
std::wstring utf16tow(const utf16string &str);
|
|
||||||
utf16string wtoutf16(const std::wstring &str);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif // INCLUDED_UTIL
|
|
@ -1,672 +0,0 @@
|
|||||||
/* Copyright (C) 2009 Wildfire Games.
|
|
||||||
* This file is part of 0 A.D.
|
|
||||||
*
|
|
||||||
* 0 A.D. is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 2 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* 0 A.D. is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with 0 A.D. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "precompiled.h"
|
|
||||||
|
|
||||||
#include "XMB.h"
|
|
||||||
#include "Stream/Stream.h"
|
|
||||||
|
|
||||||
#include <cassert>
|
|
||||||
#include <set>
|
|
||||||
#include <map>
|
|
||||||
#include <stack>
|
|
||||||
|
|
||||||
#ifdef _MSC_VER // shut up about Xerces headers
|
|
||||||
# pragma warning(disable: 4127) // conditional expression is constant
|
|
||||||
# pragma warning(disable: 4671) // the copy constructor is inaccessible
|
|
||||||
# pragma warning(disable: 4673) // throwing 'x' the following types will not be considered at the catch site
|
|
||||||
# pragma warning(disable: 4244) // 'return' : conversion from '__w64 int' to 'unsigned long', possible loss of data
|
|
||||||
# pragma warning(disable: 4267) // 'argument' : conversion from 'size_t' to 'const unsigned int', possible loss of data
|
|
||||||
#endif
|
|
||||||
|
|
||||||
|
|
||||||
#include <xercesc/sax/ErrorHandler.hpp>
|
|
||||||
#include <xercesc/sax/InputSource.hpp>
|
|
||||||
#include <xercesc/sax/Locator.hpp>
|
|
||||||
#include <xercesc/sax/SAXParseException.hpp>
|
|
||||||
|
|
||||||
#include <xercesc/sax2/Attributes.hpp>
|
|
||||||
#include <xercesc/sax2/XMLReaderFactory.hpp>
|
|
||||||
#include <xercesc/sax2/DefaultHandler.hpp>
|
|
||||||
|
|
||||||
#include <xercesc/util/BinInputStream.hpp>
|
|
||||||
#include <xercesc/util/XMLString.hpp>
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
XERCES_CPP_NAMESPACE_USE
|
|
||||||
|
|
||||||
using namespace DatafileIO;
|
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
// XMBFile functions:
|
|
||||||
|
|
||||||
static void DeallocateElement(XMLElement* el)
|
|
||||||
{
|
|
||||||
size_t i;
|
|
||||||
|
|
||||||
for (i = 0; i < el->childs.size(); ++i)
|
|
||||||
DeallocateElement(el->childs[i]);
|
|
||||||
|
|
||||||
delete el;
|
|
||||||
}
|
|
||||||
|
|
||||||
XMBFile::XMBFile()
|
|
||||||
: root(NULL)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
XMBFile::~XMBFile()
|
|
||||||
{
|
|
||||||
DeallocateElement(root);
|
|
||||||
}
|
|
||||||
|
|
||||||
// XMLReader init/shutdown:
|
|
||||||
|
|
||||||
DatafileIO::XMLReader::XMLReader()
|
|
||||||
{
|
|
||||||
XMLPlatformUtils::Initialize();
|
|
||||||
}
|
|
||||||
|
|
||||||
DatafileIO::XMLReader::~XMLReader()
|
|
||||||
{
|
|
||||||
XMLPlatformUtils::Terminate();
|
|
||||||
}
|
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
// Input utility functions:
|
|
||||||
|
|
||||||
#define READ(type, name) type name; stream.Read(&name, sizeof(type))
|
|
||||||
#define READARRAY(type, n, name) type name[n]; stream.Read(&name, sizeof(type)*n)
|
|
||||||
#define CHECK(expr, ret) if (!(expr)) { assert(!(expr)); return (ret); }
|
|
||||||
|
|
||||||
static void StringReplace(std::wstring& s, const std::wstring& pattern, const std::wstring& replacement)
|
|
||||||
{
|
|
||||||
size_t pos = 0;
|
|
||||||
while (pos != s.npos)
|
|
||||||
{
|
|
||||||
pos = s.find(pattern, pos);
|
|
||||||
if (pos != s.npos)
|
|
||||||
{
|
|
||||||
s.replace(pos, pattern.length(), replacement);
|
|
||||||
pos += replacement.length();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
// XMB reading:
|
|
||||||
|
|
||||||
static XMLElement* ParseNode(InputStream& stream, XMBFile* file, const std::vector<utf16string>& ElementNames, const std::vector<utf16string>& AttributeNames)
|
|
||||||
{
|
|
||||||
READARRAY(char, 2, Head); CHECK(strncmp(Head, "XN", 2) == 0, NULL);
|
|
||||||
READ(int32_t, Length);
|
|
||||||
|
|
||||||
XMLElement* node = new XMLElement;
|
|
||||||
|
|
||||||
node->text = ReadUString(stream);
|
|
||||||
|
|
||||||
READ(int32_t, Name);
|
|
||||||
node->name = ElementNames[Name];
|
|
||||||
|
|
||||||
if (file->format == XMBFile::AOE3)
|
|
||||||
{
|
|
||||||
READ(int32_t, LineNumber);
|
|
||||||
node->linenum = LineNumber;
|
|
||||||
}
|
|
||||||
|
|
||||||
READ(int32_t, NumAttributes);
|
|
||||||
node->attrs.reserve(NumAttributes);
|
|
||||||
for (int32_t i = 0; i < NumAttributes; ++i)
|
|
||||||
{
|
|
||||||
READ(int32_t, AttrID);
|
|
||||||
|
|
||||||
XMLAttribute attr;
|
|
||||||
attr.name = AttributeNames[AttrID];
|
|
||||||
attr.value = ReadUString(stream);
|
|
||||||
node->attrs.push_back(attr);
|
|
||||||
}
|
|
||||||
|
|
||||||
READ(int32_t, NumChildren);
|
|
||||||
node->childs.reserve(NumChildren);
|
|
||||||
for (int32_t i = 0; i < NumChildren; ++i)
|
|
||||||
{
|
|
||||||
XMLElement* child = ParseNode(stream, file, ElementNames, AttributeNames);
|
|
||||||
if (! child)
|
|
||||||
{
|
|
||||||
DeallocateElement(node);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
node->childs.push_back(child);
|
|
||||||
}
|
|
||||||
|
|
||||||
return node;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
static bool ParseXMB(InputStream& stream, XMBFile* file)
|
|
||||||
{
|
|
||||||
READARRAY(char, 2, Header); CHECK(strncmp(Header, "X1", 2) == 0, false);
|
|
||||||
READ(int32_t, DataLength);
|
|
||||||
READARRAY(char, 2, RootHeader); CHECK(strncmp(RootHeader, "XR", 2) == 0, false);
|
|
||||||
READARRAY(int32_t, 2, Unknown);
|
|
||||||
|
|
||||||
CHECK(Unknown[0] == 4, false);
|
|
||||||
|
|
||||||
if (Unknown[1] == 7)
|
|
||||||
file->format = XMBFile::AOM;
|
|
||||||
else if (Unknown[1] == 8)
|
|
||||||
file->format = XMBFile::AOE3;
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// wxLogWarning(_("Unrecognised XMB format - assuming AoE3"));
|
|
||||||
assert(! "Unrecognised format");
|
|
||||||
file->format = XMBFile::AOE3;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::vector<utf16string> ElementNames;
|
|
||||||
READ(int, NumElements);
|
|
||||||
ElementNames.reserve(NumElements);
|
|
||||||
for (int i = 0; i < NumElements; ++i)
|
|
||||||
ElementNames.push_back(ReadUString(stream));
|
|
||||||
|
|
||||||
std::vector<utf16string> AttributeNames;
|
|
||||||
READ(int, NumAttributes);
|
|
||||||
AttributeNames.reserve(NumAttributes);
|
|
||||||
for (int i = 0; i < NumAttributes; ++i)
|
|
||||||
AttributeNames.push_back(ReadUString(stream));
|
|
||||||
|
|
||||||
XMLElement* root = ParseNode(stream, file, ElementNames, AttributeNames);
|
|
||||||
if (! root)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
file->root = root;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
XMBFile* XMBFile::LoadFromXMB(InputStream& stream)
|
|
||||||
{
|
|
||||||
XMBFile* file = new XMBFile();
|
|
||||||
|
|
||||||
if (! ParseXMB(stream, file))
|
|
||||||
{
|
|
||||||
delete file;
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
return file;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
// XMB writing:
|
|
||||||
|
|
||||||
|
|
||||||
typedef std::map<utf16string, uint32_t> StringTable;
|
|
||||||
|
|
||||||
static void ExtractStrings(XMLElement* node, StringTable& elements, StringTable& attributes)
|
|
||||||
{
|
|
||||||
if (elements.find(node->name) == elements.end())
|
|
||||||
elements.insert(std::make_pair(node->name, (uint32_t)elements.size()));
|
|
||||||
|
|
||||||
for (size_t i = 0; i < node->attrs.size(); ++i)
|
|
||||||
if (attributes.find(node->attrs[i].name) == attributes.end())
|
|
||||||
attributes.insert(std::make_pair(node->attrs[i].name, (uint32_t)attributes.size()));
|
|
||||||
|
|
||||||
for (size_t i = 0; i < node->childs.size(); ++i)
|
|
||||||
ExtractStrings(node->childs[i], elements, attributes);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
static void WriteNode(SeekableOutputStream& stream, XMBFile* file, XMLElement* node, const StringTable& elements, const StringTable& attributes)
|
|
||||||
{
|
|
||||||
stream.Write("XN", 2);
|
|
||||||
|
|
||||||
off_t Length_off = stream.Tell();
|
|
||||||
stream.Write("????", 4);
|
|
||||||
|
|
||||||
WriteUString(stream, node->text);
|
|
||||||
|
|
||||||
uint32_t Name = elements.find(node->name)->second;
|
|
||||||
stream.Write(&Name, 4);
|
|
||||||
|
|
||||||
if (file->format == XMBFile::AOE3)
|
|
||||||
{
|
|
||||||
int32_t lineNum = node->linenum;
|
|
||||||
stream.Write(&lineNum, 4);
|
|
||||||
}
|
|
||||||
|
|
||||||
uint32_t NumAttributes = (uint32_t)node->attrs.size();
|
|
||||||
stream.Write(&NumAttributes, 4);
|
|
||||||
for (uint32_t i = 0; i < NumAttributes; ++i)
|
|
||||||
{
|
|
||||||
uint32_t n = attributes.find(node->attrs[i].name)->second;
|
|
||||||
stream.Write(&n, 4);
|
|
||||||
WriteUString(stream, node->attrs[i].value);
|
|
||||||
}
|
|
||||||
|
|
||||||
uint32_t NumChildren = (uint32_t)node->childs.size();
|
|
||||||
stream.Write(&NumChildren, 4);
|
|
||||||
for (uint32_t i = 0; i < NumChildren; ++i)
|
|
||||||
WriteNode(stream, file, node->childs[i], elements, attributes);
|
|
||||||
|
|
||||||
off_t NodeEnd = stream.Tell();
|
|
||||||
stream.Seek(Length_off, Stream::FROM_START);
|
|
||||||
int Length = NodeEnd - (Length_off+4);
|
|
||||||
stream.Write(&Length, 4);
|
|
||||||
stream.Seek(NodeEnd, Stream::FROM_START);
|
|
||||||
}
|
|
||||||
|
|
||||||
void XMBFile::SaveAsXMB(SeekableOutputStream& stream)
|
|
||||||
{
|
|
||||||
stream.Write("X1", 2);
|
|
||||||
|
|
||||||
off_t Length_off = stream.Tell();
|
|
||||||
stream.Write("????", 4);
|
|
||||||
|
|
||||||
stream.Write("XR", 2);
|
|
||||||
|
|
||||||
int version[2] = { 4, -1 };
|
|
||||||
if (format == XMBFile::AOM)
|
|
||||||
version[1] = 7;
|
|
||||||
else if (format == XMBFile::AOE3)
|
|
||||||
version[1] = 8;
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// wxLogWarning(_("Unknown XMB format - assuming AoE3"));
|
|
||||||
format = XMBFile::AOE3;
|
|
||||||
version[1] = 8;
|
|
||||||
}
|
|
||||||
stream.Write(version, 8);
|
|
||||||
|
|
||||||
// Get the list of element/attribute names, sorted by first appearance
|
|
||||||
StringTable ElementNames;
|
|
||||||
StringTable AttributeNames;
|
|
||||||
ExtractStrings(root, ElementNames, AttributeNames);
|
|
||||||
|
|
||||||
|
|
||||||
// Convert into handy vector format for outputting
|
|
||||||
std::vector<utf16string> ElementNamesByID;
|
|
||||||
ElementNamesByID.resize(ElementNames.size());
|
|
||||||
for (StringTable::iterator it = ElementNames.begin(); it != ElementNames.end(); ++it)
|
|
||||||
ElementNamesByID[it->second] = it->first;
|
|
||||||
|
|
||||||
// Output element names
|
|
||||||
uint32_t NumElements = (uint32_t)ElementNamesByID.size();
|
|
||||||
stream.Write(&NumElements, 4);
|
|
||||||
for (uint32_t i = 0; i < NumElements; ++i)
|
|
||||||
WriteUString(stream, ElementNamesByID[i]);
|
|
||||||
|
|
||||||
|
|
||||||
// Convert into handy vector format for outputting
|
|
||||||
std::vector<utf16string> AttributeNamesByID;
|
|
||||||
AttributeNamesByID.resize(AttributeNames.size());
|
|
||||||
for (StringTable::iterator it = AttributeNames.begin(); it != AttributeNames.end(); ++it)
|
|
||||||
AttributeNamesByID[it->second] = it->first;
|
|
||||||
|
|
||||||
// Output attribute names
|
|
||||||
uint32_t NumAttributes = (uint32_t)AttributeNamesByID.size();
|
|
||||||
stream.Write(&NumAttributes, 4);
|
|
||||||
for (uint32_t i = 0; i < NumAttributes; ++i)
|
|
||||||
WriteUString(stream, AttributeNamesByID[i]);
|
|
||||||
|
|
||||||
// Output root node, plus all descendants
|
|
||||||
WriteNode(stream, this, root, ElementNames, AttributeNames);
|
|
||||||
|
|
||||||
// Fill in data-length field near the beginning
|
|
||||||
off_t DataEnd = stream.Tell();
|
|
||||||
stream.Seek(Length_off, Stream::FROM_START);
|
|
||||||
int Length = DataEnd - (Length_off+4);
|
|
||||||
stream.Write(&Length, 4);
|
|
||||||
stream.Seek(DataEnd, Stream::FROM_START);
|
|
||||||
}
|
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
// XML writing:
|
|
||||||
|
|
||||||
const wchar_t* in = L"\t";
|
|
||||||
void OutputNode(std::wstring& output, const XMLElement* node, const std::wstring& indent)
|
|
||||||
{
|
|
||||||
// Build up strings using lots of += instead of +, because it gives
|
|
||||||
// a significant (>30%) performance increase.
|
|
||||||
output += L"\n";
|
|
||||||
output += indent;
|
|
||||||
output += L"<";
|
|
||||||
output += utf16tow(node->name);
|
|
||||||
|
|
||||||
for (size_t i = 0; i < node->attrs.size(); ++i)
|
|
||||||
{
|
|
||||||
std::wstring value = utf16tow(node->attrs[i].value);
|
|
||||||
// Escape funny characters
|
|
||||||
if (value.find_first_of(L"&<>\"") != value.npos)
|
|
||||||
{
|
|
||||||
StringReplace(value, L"&", L"&");
|
|
||||||
StringReplace(value, L"<", L"<");
|
|
||||||
StringReplace(value, L">", L">");
|
|
||||||
StringReplace(value, L"\"", L""");
|
|
||||||
}
|
|
||||||
|
|
||||||
output += L" ";
|
|
||||||
output += utf16tow(node->attrs[i].name);
|
|
||||||
output += L"=\"";
|
|
||||||
output += value;
|
|
||||||
output += L"\"";
|
|
||||||
}
|
|
||||||
|
|
||||||
// Output the element's contents, in an attemptedly nice readable format:
|
|
||||||
|
|
||||||
std::wstring name = utf16tow(node->name);
|
|
||||||
if (node->text.length())
|
|
||||||
{
|
|
||||||
std::wstring text = utf16tow(node->text);
|
|
||||||
|
|
||||||
// Wrap text in CDATA when necessary. (Maybe we should use < etc
|
|
||||||
// in some cases?)
|
|
||||||
if (text.find_first_of(L"<>&") != text.npos)
|
|
||||||
{
|
|
||||||
// (According to the XML spec, ">" is allowed in non-CDATA non-escaped
|
|
||||||
// content, but we avoid that because it's confusing.)
|
|
||||||
|
|
||||||
StringReplace(text, L"]]>", L"]]>]]><![CDATA["); // paranoia
|
|
||||||
|
|
||||||
std::wstring newtext = L"<![CDATA[\n";
|
|
||||||
newtext += indent;
|
|
||||||
newtext += in;
|
|
||||||
newtext += text;
|
|
||||||
newtext += L"\n"; // (All this extra whitespace will be trimmed during XML->XMB)
|
|
||||||
newtext += indent;
|
|
||||||
newtext += L"]]>";
|
|
||||||
std::swap(newtext, text);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (node->childs.size())
|
|
||||||
{
|
|
||||||
/* <el>
|
|
||||||
Text
|
|
||||||
<child>...
|
|
||||||
</el> */
|
|
||||||
output += L">\n";
|
|
||||||
output += indent;
|
|
||||||
output += in;
|
|
||||||
output += text;
|
|
||||||
std::wstring nextIndent = indent + in;
|
|
||||||
for (size_t i = 0; i < node->childs.size(); ++i)
|
|
||||||
OutputNode(output, node->childs[i], nextIndent);
|
|
||||||
output += L"\n";
|
|
||||||
output += indent;
|
|
||||||
output += L"</";
|
|
||||||
output += name;
|
|
||||||
output += L">";
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
/* <el>Text</el> */
|
|
||||||
output += L">";
|
|
||||||
output += text;
|
|
||||||
output += L"</";
|
|
||||||
output += name;
|
|
||||||
output += L">";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (node->childs.size())
|
|
||||||
{
|
|
||||||
/* <el>
|
|
||||||
<child>...
|
|
||||||
</el> */
|
|
||||||
output += L">";
|
|
||||||
std::wstring nextIndent = indent + in;
|
|
||||||
for (size_t i = 0; i < node->childs.size(); ++i)
|
|
||||||
OutputNode(output, node->childs[i], nextIndent);
|
|
||||||
output += L"\n";
|
|
||||||
output += indent;
|
|
||||||
output += L"</";
|
|
||||||
output += name;
|
|
||||||
output += L">";
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
/* <el/> */
|
|
||||||
output += L"/>";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
std::wstring XMBFile::SaveAsXML()
|
|
||||||
{
|
|
||||||
std::wstring output;
|
|
||||||
OutputNode(output, root, L"");
|
|
||||||
return output;
|
|
||||||
}
|
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
// XML reading:
|
|
||||||
|
|
||||||
class XeroHandler : public DefaultHandler
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
XeroHandler() : Root(NULL), m_locator(NULL) {}
|
|
||||||
~XeroHandler() { if (Root) DeallocateElement(Root); }
|
|
||||||
|
|
||||||
// SAX2 event handlers:
|
|
||||||
virtual void startDocument();
|
|
||||||
virtual void endDocument();
|
|
||||||
virtual void startElement(const XMLCh* const uri, const XMLCh* const localname, const XMLCh* const qname, const Attributes& attrs);
|
|
||||||
virtual void endElement(const XMLCh* const uri, const XMLCh* const localname, const XMLCh* const qname);
|
|
||||||
virtual void characters(const XMLCh* const chars, const unsigned int length);
|
|
||||||
|
|
||||||
// Locator, for determining line numbers of elements
|
|
||||||
const Locator* m_locator;
|
|
||||||
virtual void setDocumentLocator(const Locator* const locator) {
|
|
||||||
m_locator = locator;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Return and remove the root element from this object (so it won't get deleted)
|
|
||||||
XMLElement* ReleaseRoot() { XMLElement* t = Root; Root = NULL; return t; }
|
|
||||||
|
|
||||||
private:
|
|
||||||
XMLElement* Root;
|
|
||||||
std::stack<XMLElement*> ElementStack; // so the event handlers can be aware of the current element
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
class XeroErrorHandler : public ErrorHandler
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
XeroErrorHandler() : sawErrors(false) {}
|
|
||||||
~XeroErrorHandler() {}
|
|
||||||
void warning(const SAXParseException& err) { complain(err, L"warning"); }
|
|
||||||
void error(const SAXParseException& err) { complain(err, L"error"); }
|
|
||||||
void fatalError(const SAXParseException& err) { complain(err, L"fatal error"); };
|
|
||||||
void resetErrors() { sawErrors = false; }
|
|
||||||
bool getSawErrors() const { return sawErrors; }
|
|
||||||
utf16string getErrorText() { return errorText; }
|
|
||||||
private:
|
|
||||||
bool sawErrors;
|
|
||||||
utf16string errorText;
|
|
||||||
void complain(const SAXParseException& err, const wchar_t* severity) {
|
|
||||||
sawErrors = true;
|
|
||||||
C_ASSERT(sizeof(utf16_t) == sizeof(XMLCh));
|
|
||||||
errorText += wtoutf16(L"XML ");
|
|
||||||
errorText += wtoutf16(severity);
|
|
||||||
errorText += wtoutf16(L": ");
|
|
||||||
errorText += (utf16_t*)err.getSystemId();
|
|
||||||
errorText += wtoutf16(L" / ");
|
|
||||||
errorText += (utf16_t*)err.getMessage();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
class XeroBinInputStream : public BinInputStream
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
XeroBinInputStream(InputStream& stream) : m_stream(stream) {}
|
|
||||||
virtual XMLFilePos curPos () const
|
|
||||||
{
|
|
||||||
return m_stream.Tell();
|
|
||||||
}
|
|
||||||
virtual XMLSize_t readBytes (XMLByte *const toFill, const XMLSize_t maxToRead)
|
|
||||||
{
|
|
||||||
return (unsigned int)m_stream.Read(toFill, (unsigned int)maxToRead);
|
|
||||||
}
|
|
||||||
virtual const XMLCh* getContentType() const
|
|
||||||
{
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
private:
|
|
||||||
InputStream& m_stream;
|
|
||||||
};
|
|
||||||
|
|
||||||
class XeroInputSource : public InputSource
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
XeroInputSource(InputStream& stream) : m_stream(stream) {}
|
|
||||||
virtual BinInputStream *makeStream() const
|
|
||||||
{
|
|
||||||
return new XeroBinInputStream(m_stream);
|
|
||||||
}
|
|
||||||
private:
|
|
||||||
InputStream& m_stream;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
XMBFile* DatafileIO::XMLReader::LoadFromXML(InputStream& stream)
|
|
||||||
{
|
|
||||||
XMBFile* ret = new XMBFile();
|
|
||||||
|
|
||||||
// I don't like Xerces :-(
|
|
||||||
|
|
||||||
SAX2XMLReader* Parser = XMLReaderFactory::createXMLReader();
|
|
||||||
|
|
||||||
// DTD validation:
|
|
||||||
// Parser->setFeature(XMLUni::fgSAX2CoreValidation, true);
|
|
||||||
// Parser->setFeature(XMLUni::fgXercesDynamic, true);
|
|
||||||
|
|
||||||
XeroHandler handler;
|
|
||||||
Parser->setContentHandler(&handler);
|
|
||||||
|
|
||||||
XeroErrorHandler errorHandler;
|
|
||||||
Parser->setErrorHandler(&errorHandler);
|
|
||||||
|
|
||||||
|
|
||||||
// Build the XMLElement tree inside the XeroHandler
|
|
||||||
XeroInputSource src(stream);
|
|
||||||
Parser->parse(src);
|
|
||||||
|
|
||||||
delete Parser;
|
|
||||||
|
|
||||||
if (errorHandler.getSawErrors())
|
|
||||||
{
|
|
||||||
//wxLogError(_("Error in XML file %s"), filename);
|
|
||||||
// TODO URGENT: report errors
|
|
||||||
delete ret;
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
XMLElement* root = handler.ReleaseRoot();
|
|
||||||
ret->root = root->childs[0];
|
|
||||||
delete root;
|
|
||||||
}
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void XeroHandler::startDocument()
|
|
||||||
{
|
|
||||||
Root = new XMLElement;
|
|
||||||
ElementStack.push(Root);
|
|
||||||
}
|
|
||||||
|
|
||||||
void XeroHandler::endDocument()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
void XeroHandler::startElement(const XMLCh* const /*uri*/, const XMLCh* const localname, const XMLCh* const /*qname*/, const Attributes& attrs)
|
|
||||||
{
|
|
||||||
utf16string elementName = (utf16_t*)localname;
|
|
||||||
|
|
||||||
// Create a new element
|
|
||||||
XMLElement* e = new XMLElement;
|
|
||||||
e->name = elementName;
|
|
||||||
e->linenum = m_locator->getLineNumber();
|
|
||||||
|
|
||||||
// Store all the attributes in the new element
|
|
||||||
for (unsigned int i = 0; i < attrs.getLength(); ++i)
|
|
||||||
{
|
|
||||||
utf16string attrName = (utf16_t*)attrs.getLocalName(i);
|
|
||||||
XMLAttribute attr;
|
|
||||||
attr.name = attrName;
|
|
||||||
attr.value = (utf16_t*)attrs.getValue(i);
|
|
||||||
e->attrs.push_back(attr);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add the element to its parent
|
|
||||||
ElementStack.top()->childs.push_back(e);
|
|
||||||
|
|
||||||
// Set as parent of following elements
|
|
||||||
ElementStack.push(e);
|
|
||||||
}
|
|
||||||
|
|
||||||
void XeroHandler::endElement(const XMLCh* const /*uri*/, const XMLCh* const /*localname*/, const XMLCh* const /*qname*/)
|
|
||||||
{
|
|
||||||
// Trim whitespace around the element's contents
|
|
||||||
|
|
||||||
std::string whitespaceA = " \t\r\n";
|
|
||||||
utf16string whitespace (whitespaceA.begin(), whitespaceA.end());
|
|
||||||
|
|
||||||
XMLElement* el = ElementStack.top();
|
|
||||||
// Find the start of the non-whitespace section
|
|
||||||
size_t first = el->text.find_first_not_of(whitespace);
|
|
||||||
|
|
||||||
if (first == el->text.npos)
|
|
||||||
// Entirely whitespace - easy to handle
|
|
||||||
el->text = utf16string();
|
|
||||||
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// Count the number of \n being cut off,
|
|
||||||
// and add them to the line number
|
|
||||||
// utf16string trimmed (el->text.begin(), el->text.begin()+first);
|
|
||||||
// el->linenum += (int)std::count(trimmed.begin(), trimmed.end(), (utf16_t)'\n');
|
|
||||||
// (Only use this when the line number should point to the start of the
|
|
||||||
// content, rather than the element tag. TODO: enable it in that situation.)
|
|
||||||
|
|
||||||
// Find the end of the non-whitespace section,
|
|
||||||
// and trim off everything else
|
|
||||||
size_t last = el->text.find_last_not_of(whitespace);
|
|
||||||
el->text = el->text.substr(first, 1+last-first);
|
|
||||||
}
|
|
||||||
|
|
||||||
ElementStack.pop();
|
|
||||||
}
|
|
||||||
|
|
||||||
void XeroHandler::characters(const XMLCh* const chars, const unsigned int length)
|
|
||||||
{
|
|
||||||
ElementStack.top()->text += utf16string((utf16_t*)chars, length);
|
|
||||||
}
|
|
@ -1,70 +0,0 @@
|
|||||||
/* Copyright (C) 2009 Wildfire Games.
|
|
||||||
* This file is part of 0 A.D.
|
|
||||||
*
|
|
||||||
* 0 A.D. is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 2 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* 0 A.D. is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with 0 A.D. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <string>
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
#include "../Util.h"
|
|
||||||
|
|
||||||
namespace DatafileIO
|
|
||||||
{
|
|
||||||
class InputStream;
|
|
||||||
class SeekableOutputStream;
|
|
||||||
|
|
||||||
// Convenient storage for the internal tree
|
|
||||||
struct XMLAttribute {
|
|
||||||
utf16string name;
|
|
||||||
utf16string value;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct XMLElement {
|
|
||||||
utf16string name;
|
|
||||||
int linenum;
|
|
||||||
utf16string text;
|
|
||||||
std::vector<XMLElement*> childs;
|
|
||||||
std::vector<XMLAttribute> attrs;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct XMBFile;
|
|
||||||
|
|
||||||
// Enforces initialisation/termination the XML system
|
|
||||||
struct XMLReader {
|
|
||||||
XMLReader();
|
|
||||||
~XMLReader();
|
|
||||||
|
|
||||||
XMBFile* LoadFromXML(InputStream& stream);
|
|
||||||
};
|
|
||||||
|
|
||||||
struct XMBFile {
|
|
||||||
XMLElement* root;
|
|
||||||
enum { UNKNOWN, AOM, AOE3 } format;
|
|
||||||
|
|
||||||
XMBFile();
|
|
||||||
~XMBFile();
|
|
||||||
|
|
||||||
// This does *not* understand l33t-compressed data. Please
|
|
||||||
// decompress them first.
|
|
||||||
static XMBFile* LoadFromXMB(InputStream& stream);
|
|
||||||
|
|
||||||
// Caller should convert the returned data to some encoding, and
|
|
||||||
// prepend "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n" (or whatever
|
|
||||||
// is appropriate for that encoding)
|
|
||||||
std::wstring SaveAsXML();
|
|
||||||
|
|
||||||
void SaveAsXMB(SeekableOutputStream& stream);
|
|
||||||
};
|
|
||||||
};
|
|
@ -1,18 +0,0 @@
|
|||||||
/* Copyright (C) 2009 Wildfire Games.
|
|
||||||
* This file is part of 0 A.D.
|
|
||||||
*
|
|
||||||
* 0 A.D. is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 2 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* 0 A.D. is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with 0 A.D. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "precompiled.h"
|
|
@ -1,37 +0,0 @@
|
|||||||
/* Copyright (C) 2009 Wildfire Games.
|
|
||||||
* This file is part of 0 A.D.
|
|
||||||
*
|
|
||||||
* 0 A.D. is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 2 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* 0 A.D. is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with 0 A.D. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
// Precompiled headers:
|
|
||||||
|
|
||||||
#ifdef _MSC_VER
|
|
||||||
# define _SCL_SECURE_NO_DEPRECATE // shut up, std::copy isn't deprecated
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef USING_PCH
|
|
||||||
|
|
||||||
// Exclude rarely-used stuff from Windows headers
|
|
||||||
#ifdef _WIN32
|
|
||||||
# define WIN32_LEAN_AND_MEAN
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include <vector>
|
|
||||||
#include <string>
|
|
||||||
#include <set>
|
|
||||||
#include <stack>
|
|
||||||
#include <map>
|
|
||||||
|
|
||||||
#endif // USING_PCH
|
|
Loading…
Reference in New Issue
Block a user