From: Akiko Date: Wed, 2 Oct 2013 15:13:16 +0000 (+0200) Subject: - initial commit of QTinNS project X-Git-Url: http://community.linux-addicted.net/gitweb/?a=commitdiff_plain;h=13811bdaf7ffea0f2486f55a8d2e8b9cd70e6584;p=qtinns.git - initial commit of QTinNS project - added basic verision of the QTinNS editor - added zlib compression tools - added not fully functioning vfs_tool (an older command line version of the GUI editor) - added a prototype for dat file parsing (just for testing if it can be done that way) - added full At based version of a class handling Neocron VFS/PAK files - added cmake module for adding Irrlicht to the project - added cleanup script for cleaning up of in-source build (out of source builds are not working yet) ! French language is missing (sorry, I had to learn Latin in my youth) ! patches and corrections will be accepted, but this repo will stay read only for now --- 13811bdaf7ffea0f2486f55a8d2e8b9cd70e6584 diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..772944d --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,30 @@ +cmake_minimum_required (VERSION 2.8.8) +project (QTinns) +set (CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/cmake_modules/") + +# needed packages +find_package (Qt5Core) +find_package (Qt5Widgets) +find_package (Qt5Network) +find_package (Qt5LinguistTools) +find_package (ZLIB REQUIRED) +find_package (Irrlicht REQUIRED) + +# includes and outputs +set (CMAKE_INCLUDE_CURRENT_DIR on) +include_directories (${PROJECT_SOURCE_DIR}) +set (CMAKE_RUNTIME_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}) +set (CMAKE_LIBRARY_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}) +set (CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}) + +# compiler settings +set (CMAKE_CXX_FLAGS "-std=c++11 -W -Wall -Wextra -Os") +set (CMAKE_C_FLAGS "-std=c++11 -W -Wall -Wextra -Os") + +#add_subdirectory (analyzer) +add_subdirectory (editor) +add_subdirectory (filesystem) +#add_subdirectory (renderer) +#add_subdirectory (server) +add_subdirectory (tests) +add_subdirectory (tools) diff --git a/COPYING b/COPYING new file mode 100644 index 0000000..d159169 --- /dev/null +++ b/COPYING @@ -0,0 +1,339 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Lesser General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. diff --git a/README b/README new file mode 100644 index 0000000..dc891ef --- /dev/null +++ b/README @@ -0,0 +1,7 @@ +This directory contains QTinNS, a Qt based reimplementation of TinNS +and corresponding tools, a Neocron server emulation. + +QTinNS is free software. See the files whose names start with COPYING +for copying permission. + +Copyright (C) 2013 Akiko . diff --git a/cmake_distclean.sh b/cmake_distclean.sh new file mode 100755 index 0000000..e67620c --- /dev/null +++ b/cmake_distclean.sh @@ -0,0 +1,91 @@ +#!/bin/sh + +rm -rf CMakeCache.txt +rm -rf CMakeFiles/ +rm -rf Makefile +rm -rf cmake_install.cmake +rm -rf *.qrc.depends +rm -rf moc_*.* + +rm -rf analyzer/CMakeCache.txt +rm -rf analyzer/CMakeFiles/ +rm -rf analyzer/Makefile +rm -rf analyzer/cmake_install.cmake +rm -rf analyzer/*.qrc.depends +rm -rf analyzer/moc_*.* + +rm -rf editor/CMakeCache.txt +rm -rf editor/CMakeFiles/ +rm -rf editor/Makefile +rm -rf editor/cmake_install.cmake +rm -rf editor/*.qrc.depends +rm -rf editor/moc_*.* + +rm -rf filesystem/CMakeCache.txt +rm -rf filesystem/CMakeFiles/ +rm -rf filesystem/Makefile +rm -rf filesystem/cmake_install.cmake +rm -rf filesystem/*.qrc.depends +rm -rf filesystem/moc_*.* + +rm -rf renderer/CMakeCache.txt +rm -rf renderer/CMakeFiles/ +rm -rf renderer/Makefile +rm -rf renderer/cmake_install.cmake +rm -rf renderer/*.qrc.depends +rm -rf renderer/moc_*.* + +rm -rf server/CMakeCache.txt +rm -rf server/CMakeFiles/ +rm -rf server/Makefile +rm -rf server/cmake_install.cmake +rm -rf server/*.qrc.depends +rm -rf server/moc_*.* + +rm -rf tests/CMakeCache.txt +rm -rf tests/CMakeFiles/ +rm -rf tests/Makefile +rm -rf tests/cmake_install.cmake +rm -rf tests/*.qrc.depends +rm -rf tests/moc_*.* + +rm -rf tools/CMakeCache.txt +rm -rf tools/CMakeFiles/ +rm -rf tools/Makefile +rm -rf tools/cmake_install.cmake +rm -rf tools/*.qrc.depends +rm -rf tools/moc_*.* + +# code counter +LOG="/tmp/log.$$" +LINES=0 +C=0 + +find| grep "\.c$" >$LOG +find| grep "\.cpp$" >>$LOG +find| grep "\.cxx$" >>$LOG +find| grep "\.h$" >>$LOG +find| grep "\.hpp$" >>$LOG +find| grep "\.hxx$" >>$LOG +find| grep "\.sh$" >>$LOG +find| grep "\.S$" >>$LOG +find| grep "\.ts$" >>$LOG +find| grep "\.ui$" >>$LOG +find| grep "\.qrc$" >>$LOG +find| grep "\.cmake$" >>$LOG +while read L +do + C=$((C+1)) + W=$(cat $L|wc -l) + S=$(cat $L|wc -c) + SIZE=$((SIZE+S)) + LINES=$((LINES+W)) +done <$LOG +rm $LOG + +echo +echo "--- REPOSITORY SUMMARY ---" +echo "source files: $C" +echo "lines of code: $LINES" +echo "size of code: $SIZE bytes" +echo diff --git a/cmake_modules/FindIrrlicht.cmake b/cmake_modules/FindIrrlicht.cmake new file mode 100644 index 0000000..d1cafb8 --- /dev/null +++ b/cmake_modules/FindIrrlicht.cmake @@ -0,0 +1,42 @@ +set (IRRLICHT_FOUND TRUE) + +find_path ( + IRRLICHT_INCLUDE_DIRS + NAMES + irrlicht.h + HINTS + ENV + IRRLICHT_ROOT + PATHS + /usr + /usr/local + PATH_SUFFIXES + include + irrlicht +) + +find_library ( + IRRLICHT_LIBRARY + Irrlicht + HINTS + ENV + IRRLICHT_ROOT + PATHS + /usr/lib + /usr/local/lib +) + +if (IRRLICHT_INCLUDE_DIRS AND EXISTS "${IRRLICHT_INCLUDE_DIRS}/IrrCompileConfig.h") + file (STRINGS "${IRRLICHT_INCLUDE_DIRS}/IrrCompileConfig.h" + verstr REGEX "^#define[ \t]+IRRLICHT_SDK_VERSION[ \t]+\".+\"" + ) + string (REGEX REPLACE "^#define[ \t]+IRRLICHT_SDK_VERSION[ \t]+\"([^\"]+)\".*" "\\1" + IRRLICHT_VERSION_STRING "${verstr}" + ) + unset (verstr) +endif () + +include (FindPackageHandleStandardArgs) +FIND_PACKAGE_HANDLE_STANDARD_ARGS (Irrlicht REQUIRED_VARS IRRLICHT_LIBRARY IRRLICHT_INCLUDE_DIRS VERSION_VAR IRRLICHT_VERSION_STRING) + +mark_as_advanced (IRRLICHT_INCLUDE_DIRS IRRLICHT_LIBRARIES) diff --git a/common/NCTypes.hxx b/common/NCTypes.hxx new file mode 100644 index 0000000..8250525 --- /dev/null +++ b/common/NCTypes.hxx @@ -0,0 +1,85 @@ +#pragma once + +#include +#include + +namespace NC +{ + namespace Meta + { + const quint32 Error = std::numeric_limits::max(); + + enum Type : quint64 { + T00 = 0x0, + T01 = 0x1, + T02 = 0x2, + T03 = 0x4, + T04 = 0x8, + T05 = 0x10, + T06 = 0x20, + T07 = 0x40, + T08 = 0x80, + T09 = 0x100, + T10 = 0x200, + T11 = 0x400, + T12 = 0x800, + T13 = 0x1000, + T14 = 0x2000, + T15 = 0x4000, + T16 = 0x8000, + T17 = 0x10000, + T18 = 0x20000, + T19 = 0x40000, + T20 = 0x80000, + T21 = 0x100000, + T22 = 0x200000, + T23 = 0x400000, + T24 = 0x800000, + T25 = 0x1000000, + T26 = 0x2000000, + T27 = 0x4000000, + T28 = 0x8000000, + T29 = 0x10000000, + T30 = 0x20000000, + T31 = 0x40000000, + T32 = 0x80000000, + T33 = 0x100000000, + T34 = 0x200000000, + T35 = 0x400000000, + T36 = 0x800000000, + T37 = 0x1000000000, + T38 = 0x2000000000, + T39 = 0x4000000000, + T40 = 0x8000000000 + }; + } + + namespace FileData + { + enum Type : quint8 { + None = Meta::Type::T00, + HSize = Meta::Type::T01, + Position = Meta::Type::T02, + CSize = Meta::Type::T03, + OSize = Meta::Type::T04, + NameLength = Meta::Type::T05, + Name = Meta::Type::T06, + CData = Meta::Type::T07, + OData = Meta::Type::T08, + All = HSize + Position + CSize + OSize + NameLength + Name + CData + OData + }; + } + + namespace Identify + { + enum Type : quint32 { + Error = Meta::Error, + Plain = Error - 1, + ZLIB = Error - 2, + VFS = 0x3D458CDE, + PAK1 = 0x883DF70A, + PAK2 = 0x93847584, + PAK3 = 0xABFDEFBD + }; + } +} diff --git a/editor/CMakeLists.txt b/editor/CMakeLists.txt new file mode 100644 index 0000000..cbe634d --- /dev/null +++ b/editor/CMakeLists.txt @@ -0,0 +1,17 @@ +# Qt sources +set (E_SOURCES Main.cxx MainWindow.cxx) +set (E_HEADERS MainWindow.hxx) +set (E_UI_FILES MainWindow.ui) +set (E_RES_FILES Resources.qrc) +set (E_TS_FILES i18n_english.ts i18n_french.ts i18n_german.ts) + +# wrappers +QT5_ADD_TRANSLATION (E_TS_SOURCES ${E_TS_FILES}) +QT5_WRAP_CPP (E_MOC_SOURCES ${E_HEADERS}) +QT5_WRAP_UI (E_UI_SOURCES ${E_UI_FILES}) +QT5_ADD_RESOURCES (E_RES_SOURCES ${E_RES_FILES}) + +# apps +add_executable (QTinNS_Editor ${E_SOURCES} ${E_MOC_SOURCES} ${E_UI_SOURCES} ${E_RES_SOURCES} ${E_TS_SOURCES}) +qt5_use_modules (QTinNS_Editor Core Widgets) +target_link_libraries (QTinNS_Editor Filesystem) diff --git a/editor/Main.cxx b/editor/Main.cxx new file mode 100644 index 0000000..e37fe84 --- /dev/null +++ b/editor/Main.cxx @@ -0,0 +1,12 @@ +#include +#include "MainWindow.hxx" + +int main(int argc, char *argv[]) +{ + QApplication app(argc, argv); + MainWindow w; + + w.show(); + + return app.exec(); +} diff --git a/editor/MainWindow.cxx b/editor/MainWindow.cxx new file mode 100644 index 0000000..0f29ce9 --- /dev/null +++ b/editor/MainWindow.cxx @@ -0,0 +1,753 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "MainWindow.hxx" +#include "ui_MainWindow.h" +#include "filesystem/NCFilesystem.hxx" + +// --- internal typedefs --- + +using QTW = QTableWidget; +using QTWI = QTableWidgetItem; +using QCH = QCryptographicHash; +using ColType = NC::FileData::Type; +using FileType = NC::Identify::Type; + +// --- internal constants --- + +const QColor Green(180, 255, 180); +const QColor Grey(180, 180, 180); +const QColor Red(255, 180, 180); +const QString TableHeader("header size;position;compressed size;original size;name length;name;SHA-1"); + +// --- constructors and deconstructors --- + +MainWindow::MainWindow(QWidget *parent) +: QMainWindow(parent), _ui(new Ui::MainWindow), _translator(new QTranslator(this)), + _settings(new QSettings("LinuxAddicted", "QTinNS Editor", this)), _vfs(new NCFilesystem) +{ + _ui->setupUi(this); + _ui->wid_table->setColumnCount(7); + _ui->wid_table->setHorizontalHeaderLabels(TableHeader.split(";")); + _ui->wid_table->setSelectionBehavior(QAbstractItemView::SelectRows); + + setLanguage(Language::English); + setupActions(); + + if (_settings) + { + move(_settings->value("mw_position", QPoint(40, 40)).toPoint()); + resize(_settings->value("mw_size", QSize(800, 600)).toSize()); + setMinimumSize(_settings->value("mw_minsize", QSize(640, 480)).toSize()); + } + else + { + move(40, 40); + resize(800, 600); + setMinimumSize(640, 480); + } + + slotStatusUpdate(); + setMode(Mode::Fresh); +} + +MainWindow::~MainWindow() +{ + if (_settings) + { + _settings->setValue("mw_position", pos()); + _settings->setValue("mw_size", size()); + _settings->setValue("mw_minsize", minimumSize()); + _settings->sync(); + } + + delete _vfs; + delete _settings; + delete _translator; + delete _ui; +} + +// --- slots --- + +void MainWindow::slotAppNew() +{ + _vfs->clear(); + setMode(Mode::Fresh); +} + +void MainWindow::slotAppOpen() +{ + QString filename = QFileDialog::getOpenFileName(this, tr("DIA-OPEN-VFS"), "./", tr("FLT-FILE-VFS")); + + if (_vfs->load(filename) != _vfs->Error) + { + _filename = filename; + slotStatusUpdate(); + setMode(Mode::Edited); + } +} + +void MainWindow::slotAppSave() +{ + if (_filename.isEmpty()) + slotAppSaveAs(); + else + _vfs->save(_filename); +} + +void MainWindow::slotAppSaveAs() +{ + QString filename = QFileDialog::getSaveFileName(this, tr("DIA-SAVE-VFS"), "./unnamed.pak", tr("FLT-FILE-VFS")); + + if (filename.size()) + { + _filename = filename; + _vfs->save(_filename); + } +} + +void MainWindow::slotAppQuit() +{ + close(); +} + +void MainWindow::slotFileAdd() +{ + QString filename = QFileDialog::getOpenFileName(this, tr("DIA-OPEN-FILE"), "./", tr("FLT-FILE-ANY")); + + if (filename.size()) + { + _vfs->fileAdd(filename); + slotStatusUpdate(); + } +} + +void MainWindow::slotFileReplace() +{ + QString filename = QFileDialog::getOpenFileName(this, tr("DIA-OPEN-FILE"), "./", tr("FLT-FILE-ANY")); + + if (filename.size()) + { + _vfs->fileReplace(filename, _ui->wid_table->currentRow()); + slotStatusUpdate(); + } +} + +void MainWindow::slotFileRemove() +{ + _vfs->fileRemove(_ui->wid_table->currentRow()); + slotStatusUpdate(); + setMode(Mode::Edited); +} + +void MainWindow::slotFileSavePlain() +{ + QString filename = QFileDialog::getSaveFileName(this, tr("DIA-SAVE-PLAIN"), "./file", tr("FLT-FILE-PLAIN")); + + if (filename.size()) + _vfs->fileSave(filename, _ui->wid_table->currentRow()); +} + +void MainWindow::slotFileSavePAK() +{ + QString filename = QFileDialog::getSaveFileName(this, tr("DIA-SAVE-PAK"), "./file", tr("FLT-FILE-PAK")); + + if (filename.size()) + _vfs->fileSave(filename, _ui->wid_table->currentRow(), FileType::PAK1); +} + +void MainWindow::slotFileSaveZLIB() +{ + QString filename = QFileDialog::getSaveFileName(this, tr("DIA-SAVE-ZLIB"), "./file", tr("FLT-FILE-ZLIB")); + + if (filename.size()) + _vfs->fileSave(filename, _ui->wid_table->currentRow(), FileType::ZLIB); +} + +void MainWindow::slotFileConvWin() +{ + QTW *table = _ui->wid_table; + NCFile entry = _vfs->fileGet(table->currentRow()); + + entry.setName(entry.name().replace("/", "\\")); + _vfs->fileReplace(entry, table->currentRow()); + slotTableSelected(); +} + +void MainWindow::slotFileConvLin() +{ + QTW *table = _ui->wid_table; + NCFile entry = _vfs->fileGet(table->currentRow()); + + entry.setName(entry.name().replace("\\", "/")); + _vfs->fileReplace(entry, table->currentRow()); + slotTableSelected(); +} + +void MainWindow::slotLangEnglish() +{ + setLanguage(Language::English); +} + +void MainWindow::slotLangFrench() +{ + setLanguage(Language::French); +} + +void MainWindow::slotLangGerman() +{ + setLanguage(Language::German); +} + +void MainWindow::slotStyleDefault() +{ + QApplication::setStyle(new QProxyStyle); +} + +void MainWindow::slotStyleFusion() +{ + QApplication::setStyle(QStyleFactory::create("Fusion")); +} + +void MainWindow::slotStyleWindows() +{ + QApplication::setStyle(QStyleFactory::create("Windows")); +} + +void MainWindow::slotStyleGTK() +{ + QApplication::setStyle(QStyleFactory::create("GTK+")); +} + +void MainWindow::slotAboutApp() +{ +} + +void MainWindow::slotAboutQt() +{ + qApp->aboutQt(); +} + +void MainWindow::slotStatusUpdate() +{ + QString msg1 = tr("MSG-UPD-FILE") + ": '" + _filename + "' " + tr("MSG-UPD-WITH") + " " + + QString::number(_vfs->count()) + " " + tr("MSG-UPD-ENTRIES"); + QString msg2 = tr("FRM-MAINWINDOW") + " - " + msg1; + + statusBar()->showMessage(msg1); + setWindowTitle(msg2); +} + +void MainWindow::slotTableSelected() +{ + QTW *table = _ui->wid_table; + NCFile entry = _vfs->fileGet(table->currentRow()); + + if (entry.name().count("/")) + _ui->act_file_conv_win->setEnabled(true); + else + _ui->act_file_conv_win->setEnabled(false); + + if (entry.name().count("\\")) + _ui->act_file_conv_lin->setEnabled(true); + else + _ui->act_file_conv_lin->setEnabled(false); + + setMode(Mode::Selected); +} + +void MainWindow::slotTableChange(qint32 row) +{ + QTW *table = _ui->wid_table; + NCFile entry = _vfs->fileGet(row); + + entry.setName(table->item(row, 5)->text()); + entry.setNameLength(entry.name().size() + 1); + entry.setHSize(entry.headerSize() + entry.nameLength()); + + _vfs->fileReplace(entry, row); + slotStatusUpdate(); +} + +void MainWindow::slotTableStatus(quint32 from_row, quint32 to_row, ColType cols) +{ + QTW *table = _ui->wid_table; + QTWI *it0 = 0; + QTWI *it1 = 0; + QTWI *it2 = 0; + QTWI *it3 = 0; + QTWI *it4 = 0; + QTWI *it5 = 0; + QTWI *it6 = 0; + + // if that happens, something is very very odd + if (to_row != _vfs->count()) + qDebug() << "it happend! oh my god, it happend!!"; + + // if these do not fit, we have a full refresh and have to build every single + // table item + if (to_row != static_cast(table->rowCount())) + { + table->setRowCount(to_row); + for (quint32 row = 0; row < to_row; ++row) + { + NCFile entry = _vfs->fileGet(row); + + it0 = new QTWI(QString::number(entry.hSize())); + it1 = new QTWI(QString::number(entry.position())); + it2 = new QTWI(QString::number(entry.cSize())); + it3 = new QTWI(QString::number(entry.oSize())); + it4 = new QTWI(QString::number(entry.nameLength())); + it5 = new QTWI(entry.name()); + it6 = new QTWI(QString(QCH::hash(entry.cData(), QCH::Sha1).toHex().toUpper())); + + it0->setBackground(Grey); + it0->setFlags(it0->flags() ^ Qt::ItemIsEditable); + it0->setTextAlignment(Qt::AlignRight); + + it1->setBackground(Grey); + it1->setFlags(it1->flags() ^ Qt::ItemIsEditable); + it1->setTextAlignment(Qt::AlignRight); + + it2->setBackground(Grey); + it2->setFlags(it2->flags() ^ Qt::ItemIsEditable); + it2->setTextAlignment(Qt::AlignRight); + + it3->setBackground(Grey); + it3->setFlags(it3->flags() ^ Qt::ItemIsEditable); + it3->setTextAlignment(Qt::AlignRight); + + it4->setBackground(Grey); + it4->setFlags(it4->flags() ^ Qt::ItemIsEditable); + it4->setTextAlignment(Qt::AlignRight); + + it5->setBackground(Green); + it5->setTextAlignment(Qt::AlignLeft); + + it6->setBackground(Grey); + it6->setFlags(it6->flags() ^ Qt::ItemIsEditable); + it6->setTextAlignment(Qt::AlignLeft); + + switch (cols) + { + case ColType::HSize: + it0->setBackground(Red); + break; + + case ColType::Position: + it1->setBackground(Red); + break; + + case ColType::CSize: + it2->setBackground(Red); + break; + + case ColType::OSize: + it3->setBackground(Red); + break; + + case ColType::NameLength: + it4->setBackground(Red); + break; + + case ColType::Name: + it5->setBackground(Red); + break; + + case ColType::CData: + case ColType::OData: + break; + + case ColType::All: + it0->setBackground(Red); + it1->setBackground(Red); + it2->setBackground(Red); + it3->setBackground(Red); + it4->setBackground(Red); + it5->setBackground(Red); + it6->setBackground(Red); + break; + + case ColType::None: + default: + it0->setBackground(Grey); + it1->setBackground(Grey); + it2->setBackground(Grey); + it3->setBackground(Grey); + it4->setBackground(Grey); + it5->setBackground(Green); + it6->setBackground(Grey); + } + + disconnect(table, SIGNAL(cellChanged(qint32, qint32)), + this, SLOT(slotTableChange(qint32))); + + table->setItem(row, 0, it0); + table->setItem(row, 1, it1); + table->setItem(row, 2, it2); + table->setItem(row, 3, it3); + table->setItem(row, 4, it4); + table->setItem(row, 5, it5); + table->setItem(row, 6, it6); + + connect(table, SIGNAL(cellChanged(qint32, qint32)), + this, SLOT(slotTableChange(qint32))); + } + } + else + { + disconnect(table, SIGNAL(cellChanged(qint32, qint32)), + this, SLOT(slotTableChange(qint32))); + + for (quint32 row = 0; row < to_row; ++row) + { + NCFile entry = _vfs->fileGet(row); + + table->item(row, 1)->setText(QString::number(entry.position())); + } + + for (quint32 row = from_row; row < to_row; ++row) + { + switch (cols) + { + case ColType::HSize: + table->item(row, 0)->setBackground(Red); + break; + + case ColType::Position: + table->item(row, 1)->setBackground(Red); + break; + + case ColType::CSize: + table->item(row, 2)->setBackground(Red); + break; + + case ColType::OSize: + table->item(row, 3)->setBackground(Red); + break; + + case ColType::NameLength: + table->item(row, 4)->setBackground(Red); + break; + + case ColType::Name: + table->item(row, 5)->setBackground(Red); + break; + + case ColType::CData: + case ColType::OData: + break; + + case ColType::All: + table->item(row, 0)->setBackground(Red); + table->item(row, 1)->setBackground(Red); + table->item(row, 2)->setBackground(Red); + table->item(row, 3)->setBackground(Red); + table->item(row, 4)->setBackground(Red); + table->item(row, 5)->setBackground(Red); + table->item(row, 6)->setBackground(Red); + break; + + case ColType::None: + default: + table->item(row, 0)->setBackground(Grey); + table->item(row, 1)->setBackground(Grey); + table->item(row, 2)->setBackground(Grey); + table->item(row, 3)->setBackground(Grey); + table->item(row, 4)->setBackground(Grey); + table->item(row, 5)->setBackground(Green); + table->item(row, 6)->setBackground(Grey); + } + } + + connect(table, SIGNAL(cellChanged(qint32, qint32)), + this, SLOT(slotTableChange(qint32))); + } + + table->resizeColumnsToContents(); +} + +void MainWindow::slotTableAdd(NCFile& entry, quint32 row) +{ + QTW *table = _ui->wid_table; + QTWI *it0 = new QTWI(QString::number(entry.hSize())); + QTWI *it1 = new QTWI(QString::number(entry.position())); + QTWI *it2 = new QTWI(QString::number(entry.cSize())); + QTWI *it3 = new QTWI(QString::number(entry.oSize())); + QTWI *it4 = new QTWI(QString::number(entry.nameLength())); + QTWI *it5 = new QTWI(entry.name()); + QTWI *it6 = new QTWI(QString(QCH::hash(entry.cData(), QCH::Sha1).toHex().toUpper())); + + it0->setBackground(Red); + it0->setFlags(it0->flags() ^ Qt::ItemIsEditable); + it0->setTextAlignment(Qt::AlignRight); + + it1->setBackground(Red); + it1->setFlags(it1->flags() ^ Qt::ItemIsEditable); + it1->setTextAlignment(Qt::AlignRight); + + it2->setBackground(Red); + it2->setFlags(it2->flags() ^ Qt::ItemIsEditable); + it2->setTextAlignment(Qt::AlignRight); + + it3->setBackground(Red); + it3->setFlags(it3->flags() ^ Qt::ItemIsEditable); + it3->setTextAlignment(Qt::AlignRight); + + it4->setBackground(Red); + it4->setFlags(it4->flags() ^ Qt::ItemIsEditable); + it4->setTextAlignment(Qt::AlignRight); + + it5->setBackground(Red); + it5->setTextAlignment(Qt::AlignLeft); + + it6->setBackground(Red); + it6->setFlags(it6->flags() ^ Qt::ItemIsEditable); + it6->setTextAlignment(Qt::AlignLeft); + + disconnect(table, SIGNAL(cellChanged(qint32, qint32)), + this, SLOT(slotTableChange(qint32))); + + table->insertRow(row); + table->setItem(row, 0, it0); + table->setItem(row, 1, it1); + table->setItem(row, 2, it2); + table->setItem(row, 3, it3); + table->setItem(row, 4, it4); + table->setItem(row, 5, it5); + table->setItem(row, 6, it6); + + connect(table, SIGNAL(cellChanged(qint32, qint32)), + this, SLOT(slotTableChange(qint32))); +} + +void MainWindow::slotTableReplace(NCFile& entry, quint32 row) +{ + QTW *table = _ui->wid_table; + + disconnect(table, SIGNAL(cellChanged(qint32, qint32)), + this, SLOT(slotTableChange(qint32))); + + table->item(row, 0)->setText(QString::number(entry.hSize())); + table->item(row, 0)->setBackground(Red); + + table->item(row, 1)->setText(QString::number(entry.position())); + table->item(row, 1)->setBackground(Red); + + table->item(row, 2)->setText(QString::number(entry.cSize())); + table->item(row, 2)->setBackground(Red); + + table->item(row, 3)->setText(QString::number(entry.oSize())); + table->item(row, 3)->setBackground(Red); + + table->item(row, 4)->setText(QString::number(entry.nameLength())); + table->item(row, 4)->setBackground(Red); + + table->item(row, 5)->setText(entry.name()); + table->item(row, 5)->setBackground(Red); + + table->item(row, 6)->setText(QString(QCH::hash(entry.cData(), QCH::Sha1).toHex().toUpper())); + table->item(row, 6)->setBackground(Red); + + connect(table, SIGNAL(cellChanged(qint32, qint32)), + this, SLOT(slotTableChange(qint32))); +} + +void MainWindow::slotTableRemove(quint32 row) +{ + _ui->wid_table->removeRow(row); +} + +// --- protected methods --- + +void MainWindow::changeEvent(QEvent *event) +{ + if (event->type() == QEvent::LanguageChange) + _ui->retranslateUi(this); + else + QMainWindow::changeEvent(event); +} + +void MainWindow::setLanguage(const Language::Type lang) +{ + QTranslator *translator = new QTranslator(this); + + if (translator) + { + switch (lang) + { + case Language::French: + translator->load(":/Language/French"); + break; + case Language::German: + translator->load(":/Language/German"); + break; + case Language::English: + default: + translator->load(":/Language/English"); + } + + if (!translator->isEmpty()) + { + qApp->removeTranslator(_translator); + delete _translator; + _translator = translator; + qApp->installTranslator(_translator); + + _ui->act_lang_english->setEnabled(true); + _ui->act_lang_french->setEnabled(true); + _ui->act_lang_german->setEnabled(true); + + switch (lang) + { + case Language::French: + _ui->act_lang_french->setEnabled(false); + break; + case Language::German: + _ui->act_lang_german->setEnabled(false); + break; + case Language::English: + default: + _ui->act_lang_english->setEnabled(false); + } + } + else + delete translator; + } +} + +void MainWindow::setMode(const Mode::Type mode) +{ + switch (mode) + { + case Mode::Fresh: + _filename = ""; + _ui->act_app_open->setEnabled(true); + _ui->act_app_save->setEnabled(false); + _ui->act_app_saveas->setEnabled(false); + _ui->act_file_replace->setEnabled(false); + _ui->act_file_remove->setEnabled(false); + _ui->act_file_save_plain->setEnabled(false); + _ui->act_file_save_pak->setEnabled(false); + _ui->act_file_save_zlib->setEnabled(false); + _ui->act_file_conv_win->setEnabled(false); + _ui->act_file_conv_lin->setEnabled(false); + break; + case Mode::Edited: + _ui->act_app_open->setEnabled(false); + _ui->act_app_save->setEnabled(true); + _ui->act_app_saveas->setEnabled(true); + _ui->act_file_replace->setEnabled(false); + _ui->act_file_remove->setEnabled(false); + _ui->act_file_save_plain->setEnabled(false); + _ui->act_file_save_pak->setEnabled(false); + _ui->act_file_save_zlib->setEnabled(false); + break; + case Mode::Selected: + _ui->act_app_open->setEnabled(false); + _ui->act_app_save->setEnabled(true); + _ui->act_app_saveas->setEnabled(true); + _ui->act_file_replace->setEnabled(true); + _ui->act_file_remove->setEnabled(true); + _ui->act_file_save_plain->setEnabled(true); + _ui->act_file_save_pak->setEnabled(true); + _ui->act_file_save_zlib->setEnabled(true); + break; + } +} + +void MainWindow::setupActions() +{ + QTW *table = _ui->wid_table; + QAction *ctx_sep1 = new QAction(this); + QAction *ctx_sep2 = new QAction(this); + QAction *ctx_sep3 = new QAction(this); + + // all the nice connections we need for the GUI + connect(_ui->act_app_new, SIGNAL(triggered()), + this, SLOT(slotAppNew())); + connect(_ui->act_app_open, SIGNAL(triggered()), + this, SLOT(slotAppOpen())); + connect(_ui->act_app_save, SIGNAL(triggered()), + this, SLOT(slotAppSave())); + connect(_ui->act_app_saveas, SIGNAL(triggered()), + this, SLOT(slotAppSaveAs())); + connect(_ui->act_app_quit, SIGNAL(triggered()), + this, SLOT(slotAppQuit())); + + connect(_ui->act_file_add, SIGNAL(triggered()), + this, SLOT(slotFileAdd())); + connect(_ui->act_file_replace, SIGNAL(triggered()), + this, SLOT(slotFileReplace())); + connect(_ui->act_file_remove, SIGNAL(triggered()), + this, SLOT(slotFileRemove())); + connect(_ui->act_file_save_plain, SIGNAL(triggered()), + this, SLOT(slotFileSavePlain())); + connect(_ui->act_file_save_pak, SIGNAL(triggered()), + this, SLOT(slotFileSavePAK())); + connect(_ui->act_file_save_zlib, SIGNAL(triggered()), + this, SLOT(slotFileSaveZLIB())); + connect(_ui->act_file_conv_win, SIGNAL(triggered()), + this, SLOT(slotFileConvWin())); + connect(_ui->act_file_conv_lin, SIGNAL(triggered()), + this, SLOT(slotFileConvLin())); + + connect(_ui->act_lang_english, SIGNAL(triggered()), + this, SLOT(slotLangEnglish())); + connect(_ui->act_lang_french, SIGNAL(triggered()), + this, SLOT(slotLangFrench())); + connect(_ui->act_lang_german, SIGNAL(triggered()), + this, SLOT(slotLangGerman())); + + connect(_ui->act_style_default, SIGNAL(triggered()), + this, SLOT(slotStyleDefault())); + connect(_ui->act_style_fusion, SIGNAL(triggered()), + this, SLOT(slotStyleFusion())); + connect(_ui->act_style_windows, SIGNAL(triggered()), + this, SLOT(slotStyleWindows())); + connect(_ui->act_style_gtk, SIGNAL(triggered()), + this, SLOT(slotStyleGTK())); + + connect(_ui->act_about_app, SIGNAL(triggered()), + this, SLOT(slotAboutApp())); + connect(_ui->act_about_qt, SIGNAL(triggered()), + this, SLOT(slotAboutQt())); + + // table update connections + connect(table, SIGNAL(cellPressed(qint32, qint32)), + this, SLOT(slotTableSelected())); + connect(table, SIGNAL(cellChanged(qint32, qint32)), + this, SLOT(slotTableChange(qint32))); + + connect(_vfs, SIGNAL(sigFileStatus(quint32, quint32, ColType)), + this, SLOT(slotTableStatus(quint32, quint32, ColType))); + connect(_vfs, SIGNAL(sigFileAdd(NCFile&, quint32)), + this, SLOT(slotTableAdd(NCFile&, quint32))); + connect(_vfs, SIGNAL(sigFileReplace(NCFile&, quint32)), + this, SLOT(slotTableReplace(NCFile&, quint32))); + connect(_vfs, SIGNAL(sigFileRemove(quint32)), + this, SLOT(slotTableRemove(quint32))); + + // context menus for the QTableWidget + ctx_sep1->setSeparator(true); + ctx_sep2->setSeparator(true); + ctx_sep3->setSeparator(true); + + table->addAction(_ui->act_file_add); + table->addAction(ctx_sep1); + table->addAction(_ui->act_file_replace); + table->addAction(_ui->act_file_remove); + table->addAction(ctx_sep2); + table->addAction(_ui->act_file_save_plain); + table->addAction(_ui->act_file_save_pak); + table->addAction(_ui->act_file_save_zlib); + table->addAction(ctx_sep3); + table->addAction(_ui->act_file_conv_win); + table->addAction(_ui->act_file_conv_lin); +} diff --git a/editor/MainWindow.hxx b/editor/MainWindow.hxx new file mode 100644 index 0000000..e598ff3 --- /dev/null +++ b/editor/MainWindow.hxx @@ -0,0 +1,95 @@ +#pragma once + +#include +#include "common/NCTypes.hxx" + +namespace Ui +{ + class MainWindow; +} +class QSettings; +class QTranslator; +class NCFile; +class NCFilesystem; + +namespace Language +{ + enum Type : quint8 { + English, + French, + German + }; +} + +namespace Mode +{ + enum Type : quint8 { + Fresh, + Edited, + Selected + }; +} + +class MainWindow : public QMainWindow { + Q_OBJECT +public: + // internal typedefs + using ColType = NC::FileData::Type; + + // constructors and deconstructors + explicit MainWindow(QWidget *parent = 0); + virtual ~MainWindow(); + +public slots: + // public slots + void slotAppNew(); + void slotAppOpen(); + void slotAppSave(); + void slotAppSaveAs(); + void slotAppQuit(); + + void slotFileAdd(); + void slotFileReplace(); + void slotFileRemove(); + void slotFileSavePlain(); + void slotFileSavePAK(); + void slotFileSaveZLIB(); + void slotFileConvWin(); + void slotFileConvLin(); + + void slotLangEnglish(); + void slotLangFrench(); + void slotLangGerman(); + + void slotStyleDefault(); + void slotStyleFusion(); + void slotStyleWindows(); + void slotStyleGTK(); + + void slotAboutApp(); + void slotAboutQt(); + + void slotStatusUpdate(); + + void slotTableSelected(); + void slotTableChange(qint32 row); + void slotTableStatus(quint32 from_row, quint32 to_row, ColType cols); + void slotTableAdd(NCFile& entry, quint32 row); + void slotTableReplace(NCFile& entry, quint32 row); + void slotTableRemove(quint32 row); + +protected: + // protected stuff + virtual void changeEvent(QEvent *event); + void setLanguage(const Language::Type lang = Language::English); + void setMode(const Mode::Type mode = Mode::Fresh); + void setupActions(); + +private: + Ui::MainWindow *_ui; + QTranslator *_translator; + QSettings *_settings; + NCFilesystem *_vfs; + QVector> _marks; + QString _filename; +}; diff --git a/editor/MainWindow.ui b/editor/MainWindow.ui new file mode 100644 index 0000000..5b36234 --- /dev/null +++ b/editor/MainWindow.ui @@ -0,0 +1,235 @@ + + + MainWindow + + + + 0 + 0 + 800 + 600 + + + + FRM-MAINWINDOW + + + + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + Qt::ActionsContextMenu + + + QAbstractItemView::SingleSelection + + + + + + + + + 0 + 0 + 800 + 25 + + + + + MM-APP + + + + + + + + + + + + MM-ABOUT + + + + + + + MM-CONF + + + + SM-LANG + + + + + + + + SM-STYLE + + + + + + + + + + + + + MM-FILE + + + + + + + + + + + + + + + + + + + + + + ME-APP-QUIT + + + + + ME-APP-NEW + + + + + ME-APP-OPEN + + + + + ME-APP-SAVE + + + + + ME-APP-SAVEAS + + + + + ME-ABOUT-APP + + + + + ME-ABOUT-QT + + + + + ME-LANG-ENGLISH + + + + + ME-LANG-FRENCH + + + + + ME-LANG-GERMAN + + + + + ME-STYLE-DEFAULT + + + + + ME-STYLE-WINDOWS + + + + + ME-STYLE-GTK + + + + + ME-STYLE-FUSION + + + + + ME-FILE-ADD + + + + + ME-FILE-REPLACE + + + + + ME-FILE-REMOVE + + + + + ME-FILE-SAVE-PLAIN + + + + + ME-FILE-SAVE-PAK + + + + + ME-FILE-SAVE-ZLIB + + + + + ME-FILE-CONV-WIN + + + + + ME-FILE-CONV-LIN + + + + + + + + diff --git a/editor/Resources.qrc b/editor/Resources.qrc new file mode 100644 index 0000000..41680fc --- /dev/null +++ b/editor/Resources.qrc @@ -0,0 +1,7 @@ + + + i18n_english.qm + i18n_french.qm + i18n_german.qm + + diff --git a/editor/i18n_english.ts b/editor/i18n_english.ts new file mode 100644 index 0000000..a2e3ebd --- /dev/null +++ b/editor/i18n_english.ts @@ -0,0 +1,230 @@ + + + + + MainWindow + + + + FRM-MAINWINDOW + QTinNS Editor + + + + MM-APP + Filesystem + + + + MM-ABOUT + About + + + + MM-CONF + Configuration + + + + SM-LANG + Language + + + + SM-STYLE + GUI Style + + + + MM-FILE + File + + + + ME-APP-QUIT + Quit + + + + ME-APP-NEW + New virtual filesystem + + + + ME-APP-OPEN + Open virtual filesystem ... + + + + ME-APP-SAVE + Save virtual filesystem + + + + ME-APP-SAVEAS + Save virtual filesystem as ... + + + + ME-ABOUT-APP + About QTinNS Editor ... + + + + ME-ABOUT-QT + About Qt framework ... + + + + ME-LANG-ENGLISH + English + + + + ME-LANG-FRENCH + French + + + + ME-LANG-GERMAN + German + + + + ME-STYLE-DEFAULT + System Default + + + + ME-STYLE-WINDOWS + ME-STYLE-CDE + Windows + + + + ME-STYLE-GTK + ME-STYLE-CLEANLOOKS + GTK+ + + + + ME-STYLE-FUSION + ME-STYLE-PLASTIQUE + Fusion + + + + ME-FILE-ADD + Add file(s) ... + + + + ME-FILE-REPLACE + Replace file ... + + + + ME-FILE-REMOVE + Remove file ... + + + + ME-FILE-SAVE-PLAIN + Save plain file as ... + + + + ME-FILE-SAVE-PAK + Save PAK file as ... + + + + ME-FILE-SAVE-ZLIB + Save ZLIB file as ... + + + + ME-FILE-CONV-WIN + Convert UNIX path to Windows path + + + + ME-FILE-CONV-LIN + Convert Windows path to UNIX path + + + + + DIA-OPEN-FILE + Open files ... + + + + + FLT-FILE-VFS + DIA-SAVE-FILE + Neocron VFS file (*.vfs *.pak) + + + + DIA-OPEN-VFS + Open virtual filesystem ... + + + + DIA-SAVE-VFS + Save virtual filesystem ... + + + + + FLT-FILE-ANY + File (*.*) + + + + DIA-SAVE-PLAIN + Save file ... + + + + FLT-FILE-PLAIN + File (*.*) + + + + DIA-SAVE-PAK + Save PAK file ... + + + + FLT-FILE-PAK + Neocron PAK file (*.pak) + + + + DIA-SAVE-ZLIB + Save ZLIB file ... + + + + FLT-FILE-ZLIB + ZLIB file (*.z) + + + + MSG-UPD-FILE + Virtual filesystem + + + + MSG-UPD-WITH + with + + + + MSG-UPD-ENTRIES + entries + + + diff --git a/editor/i18n_french.ts b/editor/i18n_french.ts new file mode 100644 index 0000000..2d3c6ee --- /dev/null +++ b/editor/i18n_french.ts @@ -0,0 +1,230 @@ + + + + + MainWindow + + + + FRM-MAINWINDOW + + + + + MM-APP + + + + + MM-ABOUT + + + + + MM-CONF + + + + + SM-LANG + + + + + SM-STYLE + + + + + MM-FILE + + + + + ME-APP-QUIT + + + + + ME-APP-NEW + + + + + ME-APP-OPEN + + + + + ME-APP-SAVE + + + + + ME-APP-SAVEAS + + + + + ME-ABOUT-APP + + + + + ME-ABOUT-QT + + + + + ME-LANG-ENGLISH + + + + + ME-LANG-FRENCH + + + + + ME-LANG-GERMAN + + + + + ME-STYLE-DEFAULT + + + + + ME-STYLE-WINDOWS + ME-STYLE-CDE + + + + + ME-STYLE-GTK + ME-STYLE-CLEANLOOKS + + + + + ME-STYLE-FUSION + ME-STYLE-PLASTIQUE + + + + + ME-FILE-ADD + + + + + ME-FILE-REPLACE + + + + + ME-FILE-REMOVE + + + + + ME-FILE-SAVE-PLAIN + + + + + ME-FILE-SAVE-PAK + + + + + ME-FILE-SAVE-ZLIB + + + + + ME-FILE-CONV-WIN + + + + + ME-FILE-CONV-LIN + + + + + + DIA-OPEN-FILE + + + + + + FLT-FILE-VFS + DIA-SAVE-FILE + + + + + DIA-OPEN-VFS + + + + + DIA-SAVE-VFS + + + + + + FLT-FILE-ANY + + + + + DIA-SAVE-PLAIN + + + + + FLT-FILE-PLAIN + + + + + DIA-SAVE-PAK + + + + + FLT-FILE-PAK + + + + + DIA-SAVE-ZLIB + + + + + FLT-FILE-ZLIB + + + + + MSG-UPD-FILE + + + + + MSG-UPD-WITH + + + + + MSG-UPD-ENTRIES + + + + diff --git a/editor/i18n_german.ts b/editor/i18n_german.ts new file mode 100644 index 0000000..b62454c --- /dev/null +++ b/editor/i18n_german.ts @@ -0,0 +1,230 @@ + + + + + MainWindow + + + + FRM-MAINWINDOW + QTinNS Editor + + + + MM-APP + Dateisystem + + + + MM-ABOUT + Über + + + + MM-CONF + Konfiguration + + + + SM-LANG + Sprache + + + + SM-STYLE + GUI Stil + + + + MM-FILE + Datei + + + + ME-APP-QUIT + Beenden + + + + ME-APP-NEW + Neues virtuelles Dateisystem + + + + ME-APP-OPEN + Virtuelles Dateisystem öffnen ... + + + + ME-APP-SAVE + Virtuelles Dateisystem speichern + + + + ME-APP-SAVEAS + Virtuelles Dateisystem speichern als ... + + + + ME-ABOUT-APP + Über QTinNS Editor ... + + + + ME-ABOUT-QT + Über Qt Framework ... + + + + ME-LANG-ENGLISH + Englisch + + + + ME-LANG-FRENCH + Französisch + + + + ME-LANG-GERMAN + Deutsch + + + + ME-STYLE-DEFAULT + System Standard + + + + ME-STYLE-WINDOWS + ME-STYLE-CDE + Windows + + + + ME-STYLE-GTK + ME-STYLE-CLEANLOOKS + GTK+ + + + + ME-STYLE-FUSION + ME-STYLE-PLASTIQUE + Fusion + + + + ME-FILE-ADD + Füge Datei(en) hinzu ... + + + + ME-FILE-REPLACE + Ersetze Datei ... + + + + ME-FILE-REMOVE + Entferne Datei ... + + + + ME-FILE-SAVE-PLAIN + Speichere normale Datei als ... + + + + ME-FILE-SAVE-PAK + Speichere PAK Datei als ... + + + + ME-FILE-SAVE-ZLIB + Speichere ZLIB Datei als ... + + + + ME-FILE-CONV-WIN + Konvertiere UNIX Pfad nach Windows + + + + ME-FILE-CONV-LIN + Konvertiere Windows Pfad nach UNIX + + + + + DIA-OPEN-FILE + Datei öffnen ... + + + + + FLT-FILE-VFS + DIA-SAVE-FILE + Neocron VFS Datei (*.vfs *.pak) + + + + DIA-OPEN-VFS + Virtuelles Dateisystem öffnen ... + + + + DIA-SAVE-VFS + Virtuelles Dateisystem speichern ... + + + + + FLT-FILE-ANY + Datei (*.*) + + + + DIA-SAVE-PLAIN + Datei speichern ... + + + + FLT-FILE-PLAIN + Datei (*.*) + + + + DIA-SAVE-PAK + PAK Datei speichern ... + + + + FLT-FILE-PAK + Neocron PAK Datei (*.pak) + + + + DIA-SAVE-ZLIB + ZLIB Datei speichern ... + + + + FLT-FILE-ZLIB + ZLIB Datei (*.z) + + + + MSG-UPD-FILE + Virtuelles Dateisystem + + + + MSG-UPD-WITH + mit + + + + MSG-UPD-ENTRIES + Einträgen + + + diff --git a/filesystem/CMakeLists.txt b/filesystem/CMakeLists.txt new file mode 100644 index 0000000..a6798f4 --- /dev/null +++ b/filesystem/CMakeLists.txt @@ -0,0 +1,17 @@ +# Qt sources +set (NCF_SOURCES FileIdentifier.cxx NCFile.cxx NCFilesystem.cxx) +set (NCF_HEADERS FileIdentifier.hxx NCFile.hxx NCFilesystem.hxx) +set (NCF_UI_FILES ) +set (NCF_RES_FILES ) +set (E_TS_FILES ) + +# wrappers +QT5_ADD_TRANSLATION (NCF_TS_SOURCES ${NCF_TS_FILES}) +QT5_WRAP_CPP (NCF_MOC_SOURCES ${NCF_HEADERS}) +QT5_WRAP_UI (NCF_UI_SOURCES ${NCF_UI_FILES}) +QT5_ADD_RESOURCES (NCF_RES_SOURCES ${NCF_RES_FILES}) + +# libs +add_library (Filesystem ${NCF_SOURCES} ${NCF_MOC_SOURCES} ${NCF_UI_SOURCES} ${NCF_RES_SOURCES} + ${NCF_TS_SOURCES}) +qt5_use_modules (Filesystem Core) diff --git a/filesystem/FileIdentifier.cxx b/filesystem/FileIdentifier.cxx new file mode 100644 index 0000000..b0146f7 --- /dev/null +++ b/filesystem/FileIdentifier.cxx @@ -0,0 +1,97 @@ +#include +#include "filesystem/FileIdentifier.hxx" + +// --- internal typedefs --- + +using IdentifyType = NC::Identify::Type; + +// --- constructors and deconstructors --- + +FileIdentifier::FileIdentifier(QObject *parent) +: QObject(parent) +{ +} + +FileIdentifier::~FileIdentifier() +{ +} + +// --- public methodes --- + +IdentifyType FileIdentifier::identify(const QString& filename) const +{ + QFile ifile(filename); + quint32 data = 0; + + if (!ifile.exists()) + return IdentifyType::Error; + + if (ifile.open(QIODevice::ReadOnly)) + { + ifile.read(reinterpret_cast(&data), sizeof (data)); + ifile.close(); + } + else + return IdentifyType::Error; + + return identifyData(data); +} + +IdentifyType FileIdentifier::identify(const QByteArray& stream) const +{ + if (stream.size() < static_cast(sizeof (quint32))) + return IdentifyType::Error; + + QByteArray data = stream.left(sizeof (quint32)); + + return identifyData(data.toUInt()); +} + +QString FileIdentifier::identifyName(const IdentifyType& type) const +{ + switch (type) + { + case Error: + return "file error"; + case Plain: + return "common file"; + case ZLIB: + return "ZLIB compressed file"; + case VFS: + return "Neocron VFS file"; + case PAK1: + return "Neocron PAK file"; + case PAK2: + return "Neocron PAK file (2nd header)"; + case PAK3: + return "Neocron PAK file (3rd header)"; + default: + return "unknown file format"; + } +} + +QString FileIdentifier::identifyName(const QString& filename) const +{ + return identifyName(identify(filename)); +} + +QString FileIdentifier::identifyName(const QByteArray& stream) const +{ + return identifyName(identify(stream)); +} + +// --- protected methods --- + +IdentifyType FileIdentifier::identifyData(const quint32& data) const +{ + if (data == IdentifyType::VFS) + return IdentifyType::VFS; + + if (data == IdentifyType::PAK1) + return IdentifyType::PAK1; + + if (reinterpret_cast(&data)[0] == 'x') + return IdentifyType::ZLIB; + + return IdentifyType::Plain; +} diff --git a/filesystem/FileIdentifier.hxx b/filesystem/FileIdentifier.hxx new file mode 100644 index 0000000..52ec6f9 --- /dev/null +++ b/filesystem/FileIdentifier.hxx @@ -0,0 +1,44 @@ +#pragma once + +#include +#include "common/NCTypes.hxx" + +class FileIdentifier : public QObject { + Q_OBJECT +public: + // internal typedefs + using IdentifyType = NC::Identify::Type; + + // internal constants + enum : quint32 { + Error = NC::Meta::Error, + VFS = NC::Identify::VFS, + PAK1 = NC::Identify::PAK1, + PAK2 = NC::Identify::PAK2, + PAK3 = NC::Identify::PAK3, + Plain = NC::Identify::Plain, + ZLIB = NC::Identify::ZLIB + }; + + // constructors and deconstructors + FileIdentifier(QObject *parent = 0); + FileIdentifier(const FileIdentifier& rhs) = delete; + FileIdentifier(const FileIdentifier&& rhs) = delete; + virtual ~FileIdentifier(); + + // operators + FileIdentifier& operator=(const FileIdentifier& rhs) = delete; + FileIdentifier& operator=(const FileIdentifier&& rhs) = delete; + + // public methodes + IdentifyType identify(const QString& filename) const; + IdentifyType identify(const QByteArray& stream) const; + + QString identifyName(const IdentifyType& type) const; + QString identifyName(const QString& filename) const; + QString identifyName(const QByteArray& stream) const; + +protected: + // protected methods + IdentifyType identifyData(const quint32& data) const; +}; diff --git a/filesystem/NCFile.cxx b/filesystem/NCFile.cxx new file mode 100644 index 0000000..ad0d369 --- /dev/null +++ b/filesystem/NCFile.cxx @@ -0,0 +1,197 @@ +#include +#include "filesystem/NCFile.hxx" + +/* + * qCompress()/qUncompress() does not behave like the real zlib compress2/uncompress functions, + * the Qt functions add a quint32 at the beginning of the QByteArray holding the original size + */ + +// --- internal constants and stuff --- + +const quint8 All = NC::FileData::HSize | NC::FileData::Position | NC::FileData::CSize | NC::FileData::OSize + | NC::FileData::NameLength | NC::FileData::Name | NC::FileData::CData | NC::FileData::OData; + +// --- constructors and deconstructors --- + +NCFile::NCFile(QObject *parent, + const quint32& hsize, const quint32& pos, const quint32& csize, const quint32& osize, + const quint32& nlen, const QString& name, const QByteArray& cdata) +: QObject(parent), _hsize(hsize), _position(pos), _csize(csize), _osize(osize), _name_length(nlen), _name(name), + _cdata(cdata) +{ + emit sigFileData(this, All); +} + +NCFile::NCFile(const NCFile& rhs) +: QObject(rhs.parent()), _hsize(rhs._hsize), _position(rhs._position), _csize(rhs._csize), _osize(rhs._osize), + _name_length(rhs._name_length), _name(rhs._name), _cdata(rhs._cdata) +{ + emit sigFileData(this, All); +} + +NCFile::NCFile(const NCFile&& rhs) +: QObject(std::move(rhs.parent())), _hsize(std::move(rhs._hsize)), _position(std::move(rhs._position)), + _csize(std::move(rhs._csize)), _osize(std::move(rhs._osize)), _name_length(std::move(rhs._name_length)), + _name(std::move(rhs._name)), _cdata(std::move(rhs._cdata)) +{ + emit sigFileData(this, All); +} + +NCFile::~NCFile() +{ +} + +// --- operators --- + +NCFile& NCFile::operator=(const NCFile& rhs) +{ + if (this != &rhs) + { + _hsize = rhs._hsize; + _position = rhs._position; + _csize = rhs._csize; + _osize = rhs._osize; + _name_length = rhs._name_length; + _name = rhs._name; + _cdata = rhs._cdata; + } + + return *this; +} + +NCFile& NCFile::operator=(const NCFile&& rhs) +{ + if (this != &rhs) + { + _hsize = std::move(rhs._hsize); + _position = std::move(rhs._position); + _csize = std::move(rhs._csize); + _osize = std::move(rhs._osize); + _name_length = std::move(rhs._name_length); + _name = std::move(rhs._name); + _cdata = std::move(rhs._cdata); + } + + return *this; +} + +bool NCFile::operator==(const NCFile& rhs) const +{ + return _hsize == rhs._hsize + && _position == rhs._position + && _csize == rhs._csize + && _osize == rhs._osize + && _name_length == rhs._name_length + && _name == rhs._name + && _cdata == rhs._cdata; +} + +bool NCFile::operator!=(const NCFile& rhs) const +{ + return !(*this == rhs); +} + +// --- setters and getters --- + +void NCFile::setHSize(const quint32& size) +{ + _hsize = size; +} + +quint32 NCFile::hSize() const +{ + return _hsize; +} + +void NCFile::setPosition(const quint32& pos) +{ + _position = pos; +} + +quint32 NCFile::position() const +{ + return _position; +} + +void NCFile::setCSize(const quint32& size) +{ + _csize = size; +} + +quint32 NCFile::cSize() const +{ + return _csize; +} + +void NCFile::setOSize(const quint32& size) +{ + _osize = size; +} + +quint32 NCFile::oSize() const +{ + return _osize; +} + +void NCFile::setNameLength(const quint32& len) +{ + _name_length = len; +} + +quint32 NCFile::nameLength() const +{ + return _name_length; +} + +void NCFile::setName(const QString& name) +{ + _name = name; +} + +QString NCFile::name() const +{ + return _name; +} + +void NCFile::setCData(const QByteArray& data) +{ + _cdata = data; +} + +QByteArray NCFile::cData() const +{ + return _cdata; +} + +void NCFile::setOData(const QByteArray& data) +{ + _cdata = qCompress(data, 9).mid(sizeof (quint32)); +} + +QByteArray NCFile::oData() const +{ + QByteArray size; + QDataStream stream(&size, QIODevice::WriteOnly); + + stream << _csize; + + return qUncompress(size + _cdata); +} + +// --- status methods --- + +quint32 NCFile::size() const +{ + return HSize + _name_length + _cdata.size(); +} + +quint32 NCFile::headerSize() const +{ + return HSize; +} + +bool NCFile::valid() const +{ + return !(_hsize == Error || _position == Error || _csize == Error || _osize == Error || _name_length == Error + || _name.isEmpty() || _cdata.isEmpty()); +} diff --git a/filesystem/NCFile.hxx b/filesystem/NCFile.hxx new file mode 100644 index 0000000..e0756d6 --- /dev/null +++ b/filesystem/NCFile.hxx @@ -0,0 +1,74 @@ +#pragma once + +#include +#include "common/NCTypes.hxx" + +class NCFile : public QObject { + Q_OBJECT +signals: + void sigFileData(NCFile *file, quint8 type); + +public: + // internal typedefs + enum : quint32 { + Error = NC::Meta::Error, + HSize = sizeof (quint32) * 5 + }; + + // constructors and deconstructors + //explicit NCFile(); + NCFile(QObject *parent = 0, + const quint32& hsize = Error, const quint32& pos = Error, const quint32& csize = Error, + const quint32& osize = Error, const quint32& nlen = Error, const QString& name = "", + const QByteArray& cdata = ""); + NCFile(const NCFile& rhs); + NCFile(const NCFile&& rhs); + virtual ~NCFile(); + + // operators + NCFile& operator=(const NCFile& rhs); + NCFile& operator=(const NCFile&& rhs); + + bool operator==(const NCFile& rhs) const; + bool operator!=(const NCFile& rhs) const; + + // setters and getters + void setHSize(const quint32& size = 0); + quint32 hSize() const; + + void setPosition(const quint32& pos = 0); + quint32 position() const; + + void setCSize(const quint32& size = 0); + quint32 cSize() const; + + void setOSize(const quint32& size = 0); + quint32 oSize() const; + + void setNameLength(const quint32& len = 0); + quint32 nameLength() const; + + void setName(const QString& name = ""); + QString name() const; + + void setCData(const QByteArray& data = ""); + QByteArray cData() const; + + void setOData(const QByteArray& data = ""); + QByteArray oData() const; + + // status stuff + quint32 size() const; + quint32 headerSize() const; + bool valid() const; + +private: + // data fields + quint32 _hsize; + quint32 _position; + quint32 _csize; + quint32 _osize; + quint32 _name_length; + QString _name; + QByteArray _cdata; +}; diff --git a/filesystem/NCFilesystem.cxx b/filesystem/NCFilesystem.cxx new file mode 100644 index 0000000..3f08a61 --- /dev/null +++ b/filesystem/NCFilesystem.cxx @@ -0,0 +1,740 @@ +#include +#include "filesystem/NCFilesystem.hxx" +#include "filesystem/FileIdentifier.hxx" + +// --- internal typedefs --- + +using IdentifyType = NC::Identify::Type; +using ColType = NC::FileData::Type; + +// --- internal objects --- + +FileIdentifier id; + +// --- constructors and deconstructors --- + +NCFilesystem::NCFilesystem(QObject *parent) +: QObject(parent) +{ + emit sigFileStatus(0, _files.count(), ColType::None); +} + +NCFilesystem::NCFilesystem(const NCFilesystem& rhs) +: QObject(rhs.parent()), _files(rhs._files) +{ + emit sigFileStatus(0, _files.count(), ColType::None); +} + +NCFilesystem::NCFilesystem(const NCFilesystem&& rhs) +: QObject(std::move(rhs.parent())), _files(std::move(rhs._files)) +{ + emit sigFileStatus(0, _files.count(), ColType::None); +} + +NCFilesystem::~NCFilesystem() +{ + _files.clear(); + emit sigFileStatus(0, _files.count(), ColType::None); +} + +// --- operators --- + +NCFilesystem& NCFilesystem::operator=(const NCFilesystem& rhs) +{ + if (this != &rhs) + { + _files = rhs._files; + emit sigFileStatus(0, _files.count(), ColType::All); + } + + return *this; +} + +NCFilesystem& NCFilesystem::operator=(const NCFilesystem&& rhs) +{ + if (this != &rhs) + { + _files = std::move(rhs._files); + emit sigFileStatus(0, _files.count(), ColType::All); + } + + return *this; +} + +bool NCFilesystem::operator==(const NCFilesystem& rhs) const +{ + return _files == rhs._files; +} + +bool NCFilesystem::operator!=(const NCFilesystem& rhs) const +{ + return _files != rhs._files; +} + +// --- basic and status methodes --- + +quint32 NCFilesystem::count() const +{ + return _files.count(); +} + +quint32 NCFilesystem::size() const +{ + quint32 result = HSize; + + for (auto& f : _files) + result += f.size(); + + return result; +} + +bool NCFilesystem::valid() const +{ + auto it = _files.begin(); + bool result = true; + + while (result && it != _files.end()) + { + result = result && (*it).valid(); + ++it; + } + + return result; +} + +void NCFilesystem::clear() +{ + _files.clear(); + emit sigFileStatus(0, _files.count(), ColType::None); +} + +QString NCFilesystem::info() const +{ + QString result; + + for (qint32 i = 0; i < _files.count(); ++i) + { + result.append("idx:").append(QString::number(i)); + result.append(" hs:").append(QString::number(_files[i].hSize())); + result.append(" pos:").append(QString::number(_files[i].position())); + result.append(" os:").append(QString::number(_files[i].oSize())); + result.append(" cs:").append(QString::number(_files[i].cSize())); + result.append(" ln:").append(QString::number(_files[i].nameLength())); + result.append(" name:'").append(_files[i].name()).append("'").append("\n"); + } + + return result; +} + +IdentifyType NCFilesystem::fileIdentify(const QString& filename) const +{ + return id.identify(filename); +} + +QString NCFilesystem::fileIdentifyName(const IdentifyType& type) const +{ + return id.identifyName(type); +} + +QString NCFilesystem::fileIdentifyName(const QString& filename) const +{ + return id.identifyName(fileIdentify(filename)); +} + +quint32 NCFilesystem::load(const QString& filename) +{ + if (fileIdentify(filename) == id.VFS) + return vfsLoader(filename, false); + + return Error; +} + +quint32 NCFilesystem::save(const QString& filename) const +{ + return vfsSaver(filename); +} + +quint32 NCFilesystem::fileLoad(const QString& filename) +{ + switch (fileIdentify(filename)) + { + case id.VFS: + return vfsLoader(filename, false); + case id.PAK1: + return pakLoader(filename, false); + case id.Plain: + return plainLoader(filename, false); + case id.ZLIB: + return zlibLoader(filename, false); + default: + break; + } + + return Error; +} + +quint32 NCFilesystem::fileAdd(const QString& filename) +{ + switch (fileIdentify(filename)) + { + case id.VFS: + return vfsLoader(filename, true); + case id.PAK1: + return pakLoader(filename, true); + case id.Plain: + return plainLoader(filename, true); + case id.ZLIB: + return zlibLoader(filename, true); + default: + break; + } + + return Error; +} + +quint32 NCFilesystem::fileAdd(const NCFile& entry) +{ + _files.push_back(entry); + recalc(_files); + + emit sigFileAdd(_files.back(), _files.count() - 1); + emit sigFileStatus(0, _files.count(), ColType::Position); + + return _files.size(); +} + +quint32 NCFilesystem::fileSave(const QString& filename, const QString& name) const +{ + return plainSaver(fileGet(name), filename); +} + +quint32 NCFilesystem::fileSave(const QString& filename, const quint32& index) const +{ + return plainSaver(fileGet(index), filename); +} + +quint32 NCFilesystem::fileSave(const QString& filename, const QString& name, const IdentifyType& type) const +{ + switch (type) + { + case id.PAK1: + return pakSaver(fileGet(name), filename); + case id.Plain: + return plainSaver(fileGet(name), filename); + case id.ZLIB: + return zlibSaver(fileGet(name), filename); + default: + break; + } + + return Error; +} + +quint32 NCFilesystem::fileSave(const QString& filename, const quint32& index, const IdentifyType& type) const +{ + switch (type) + { + case id.PAK1: + return pakSaver(fileGet(index), filename); + case id.Plain: + return plainSaver(fileGet(index), filename); + case id.ZLIB: + return zlibSaver(fileGet(index), filename); + default: + break; + } + + return Error; +} + +NCFile NCFilesystem::fileGet(const QString& name) const +{ + NCFile result; + + for (auto& f : _files) + { + if (name == f.name()) + { + result = f; + break; + } + } + + return result; +} + +NCFile NCFilesystem::fileGet(const quint32& index) const +{ + NCFile result; + + if (index < static_cast(_files.count())) + result = _files[index]; + + return result; +} + +NCFile NCFilesystem::fileReplace(const NCFile& entry, const QString& name) +{ + NCFile result; + + if (!entry.valid()) + return result; + + for (auto& f : _files) + { + if (name == f.name()) + { + result = f; + f = entry; + + emit sigFileReplace(f, _files.indexOf(f)); + emit sigFileStatus(0, _files.count(), ColType::Position); + + break; + } + } + recalc(_files); + + return result; +} + +NCFile NCFilesystem::fileReplace(const NCFile& entry, const quint32& index) +{ + NCFile result; + + if (!entry.valid()) + return result; + + if (index >= static_cast(_files.count())) + return result; + + result = _files[index]; + _files[index] = entry; + recalc(_files); + + emit sigFileReplace(_files[index], index); + emit sigFileStatus(0, _files.count(), ColType::Position); + + return result; +} + +NCFile NCFilesystem::fileReplace(const QString& filename, const QString& name) +{ + NCFile result; + + if (filename.isEmpty()) + return result; + + for (auto& f : _files) + { + if (name == f.name()) + { + NCFilesystem temp; + + temp.fileLoad(filename); + if (temp.count()) + { + const quint32 index = _files.indexOf(f); + const quint32 added = temp.count(); + + result = f; + _files.remove(index); + while (temp.count()) + { + _files.insert(index, temp.fileGet(temp.count() - 1)); + temp.fileRemove(temp.count() - 1); + } + recalc(_files); + + emit sigFileReplace(_files[index], index); + for (quint32 i = index; i < (index + added); ++i) + emit sigFileAdd(_files[i], i); + emit sigFileStatus(0, _files.count(), ColType::Position); + } + break; + } + } + + return result; +} + +NCFile NCFilesystem::fileReplace(const QString& filename, const quint32& index) +{ + NCFile result; + NCFilesystem temp; + + if (filename.isEmpty()) + return result; + + if (index >= static_cast(_files.count())) + return result; + + temp.fileLoad(filename); + if (temp.count()) + { + const quint32 added = temp.count(); + + result = _files[index]; + _files.remove(index); + while (temp.count()) + { + _files.insert(index, temp.fileGet(temp.count() - 1)); + temp.fileRemove(temp.count() - 1); + } + recalc(_files); + + emit sigFileReplace(_files[index], index); + for (quint32 i = (index + 1); i < (index + added); ++i) + emit sigFileAdd(_files[i], i); + emit sigFileStatus(0, _files.count(), ColType::Position); + } + + return result; +} + +NCFile NCFilesystem::fileRemove(const QString& name) +{ + NCFile result; + + if (name.isEmpty()) + return result; + + for (auto it = _files.begin(); it != _files.end(); ++it) + { + if (name == (*it).name()) + { + emit sigFileRemove(_files.indexOf(it)); + emit sigFileStatus(0, _files.count(), ColType::Position); + + result = *it; + _files.erase(it); + break; + } + } + recalc(_files); + + return result; +} + +NCFile NCFilesystem::fileRemove(const quint32& index) +{ + NCFile result; + + if (index >= static_cast(_files.count())) + return result; + + result = _files[index]; + _files.erase(_files.begin() + index); + recalc(_files); + + emit sigFileRemove(index); + emit sigFileStatus(0, _files.count(), ColType::Position); + + return result; +} + +// --- internal methodes --- + +void NCFilesystem::recalc(QVector& files) +{ + quint32 base_offset = 0; + quint32 csize_offset = 0; + + for (auto& f : files) + base_offset += f.headerSize() + f.nameLength(); + + for (auto& f : files) + { + f.setPosition(base_offset + csize_offset); + csize_offset += f.cSize(); + } +} + +quint32 NCFilesystem::vfsLoader(const QString& filename, const bool add) +{ + QFile ifile(filename); + + if (ifile.open(QIODevice::ReadOnly)) + { + QVector files; + const quint32 old_size = _files.count(); + quint32 data = 0; + quint32 count = 0; + QByteArray stream; + + ifile.seek(sizeof (id.VFS)); + ifile.read(reinterpret_cast(&count), sizeof (count)); + + for (quint32 i = 0; i < count; ++i) + { + NCFile entry; + quint32 pos = 0; + + // header + name + ifile.read(reinterpret_cast(&data), sizeof (data)); + entry.setHSize(data); + ifile.read(reinterpret_cast(&data), sizeof (data)); + entry.setPosition(data); + ifile.read(reinterpret_cast(&data), sizeof (data)); + entry.setCSize(data); + ifile.read(reinterpret_cast(&data), sizeof (data)); + entry.setOSize(data); + ifile.read(reinterpret_cast(&data), sizeof (data)); + entry.setNameLength(data); + stream.resize(data); + ifile.read(stream.data(), data); + stream.truncate(data); + entry.setName(stream); + // compressed data + pos = ifile.pos(); + ifile.seek(entry.position()); + stream.resize(entry.cSize()); + ifile.read(stream.data(), entry.cSize()); + entry.setCData(stream); + ifile.seek(pos); + + files.push_back(entry); + } + ifile.close(); + + if (add) + { + _files += files; + recalc(_files); + + emit sigFileStatus(0, _files.count(), ColType::Position); + emit sigFileStatus(old_size, _files.count(), ColType::All); + } + else + { + recalc(files); + _files = files; + + emit sigFileStatus(0, _files.count(), ColType::None); + } + + return _files.count(); + } + + return Error; +} + +quint32 NCFilesystem::pakLoader(const QString& filename, const bool add) +{ + const quint32 IDSize = sizeof (id.PAK1) + sizeof (id.PAK2) + sizeof (id.PAK3); + QFile ifile(filename); + + if (ifile.open(QIODevice::ReadOnly)) + { + quint32 data = 0; + QByteArray stream; + NCFile entry; + + // header data + entry.setPosition(0); + entry.setCSize(ifile.size() - IDSize); + ifile.seek(IDSize); + ifile.read(reinterpret_cast(&data), sizeof (data)); + entry.setOSize(data); + entry.setNameLength(filename.size() + 1); + entry.setName(filename); + entry.setHSize(entry.headerSize() + entry.nameLength()); + // compressed data + stream.resize(entry.cSize()); + ifile.read(stream.data(), entry.cSize()); + entry.setCData(stream); + ifile.close(); + + if (!add) + _files.clear(); + + _files.push_back(entry); + recalc(_files); + + emit sigFileAdd(_files.back(), _files.count() - 1); + emit sigFileStatus(0, _files.count(), ColType::Position); + + return _files.count(); + } + + return Error; +} + +quint32 NCFilesystem::plainLoader(const QString& filename, const bool add) +{ + QFile ifile(filename); + + if (ifile.open(QIODevice::ReadOnly)) + { + QByteArray stream; + NCFile entry; + + stream.resize(ifile.size()); + ifile.read(stream.data(), ifile.size()); + entry.setOData(stream); // odata compresses stream to cdata + entry.setPosition(0); + entry.setCSize(entry.cData().size()); + entry.setOSize(ifile.size()); + entry.setNameLength(filename.size() + 1); + entry.setName(filename); + entry.setHSize(entry.headerSize() + entry.nameLength()); + ifile.close(); + + if (!add) + _files.clear(); + + _files.push_back(entry); + recalc(_files); + + emit sigFileAdd(_files.back(), _files.count() - 1); + emit sigFileStatus(0, _files.count(), ColType::Position); + + return _files.count(); + } + + return Error; +} + +quint32 NCFilesystem::zlibLoader(const QString& filename, const bool add) +{ + QFile ifile(filename); + + if (ifile.open(QIODevice::ReadOnly)) + { + QByteArray stream; + NCFile entry; + + stream.resize(ifile.size()); + ifile.read(stream.data(), ifile.size()); + entry.setCData(stream); + entry.setPosition(0); + entry.setCSize(ifile.size()); + entry.setOSize(entry.oData().size()); + entry.setNameLength(filename.size() + 1); + entry.setName(filename); + entry.setHSize(entry.headerSize() + entry.nameLength()); + ifile.close(); + + if (!add) + _files.clear(); + + _files.push_back(entry); + recalc(_files); + + emit sigFileAdd(_files.back(), _files.count() - 1); + emit sigFileStatus(0, _files.count(), ColType::Position); + + return _files.count(); + } + + return Error; +} + +quint32 NCFilesystem::vfsSaver(const QString& filename) const +{ + QFile ofile(filename); + + if (!valid()) + return Error; + + if (ofile.open(QIODevice::WriteOnly)) + { + quint32 data = 0; + const quint32 count = _files.count(); + + data = id.VFS; + ofile.write(reinterpret_cast(&data), sizeof (data)); + ofile.write(reinterpret_cast(&count), sizeof (count)); + + for (auto& f : _files) + { + data = f.hSize(); + ofile.write(reinterpret_cast(&data), sizeof (data)); + data = f.position(); + ofile.write(reinterpret_cast(&data), sizeof (data)); + data = f.cSize(); + ofile.write(reinterpret_cast(&data), sizeof (data)); + data = f.oSize(); + ofile.write(reinterpret_cast(&data), sizeof (data)); + data = f.nameLength(); + ofile.write(reinterpret_cast(&data), sizeof (data)); + ofile.write(f.name().toStdString().c_str(), f.nameLength()); + } + + for (auto& f : _files) + ofile.write(f.cData()); + + ofile.close(); + + emit sigFileStatus(0, _files.count() - 1, ColType::None); + + return _files.count(); + } + + return Error; +} + +quint32 NCFilesystem::pakSaver(const NCFile& entry, const QString& filename) const +{ + QFile ofile(filename); + + if (!entry.valid()) + return Error; + + if (ofile.open(QIODevice::WriteOnly)) + { + quint32 data = 0; + + data = id.PAK1; + ofile.write(reinterpret_cast(&data), sizeof (data)); + data = id.PAK2; + ofile.write(reinterpret_cast(&data), sizeof (data)); + data = id.PAK3; + ofile.write(reinterpret_cast(&data), sizeof (data)); + data = entry.oSize(); + ofile.write(reinterpret_cast(&data), sizeof (data)); + ofile.write(entry.cData()); + + ofile.close(); + + return 1; + } + + return Error; +} + +quint32 NCFilesystem::plainSaver(const NCFile& entry, const QString& filename) const +{ + QFile ofile(filename); + + if (!entry.valid()) + return Error; + + if (ofile.open(QIODevice::WriteOnly)) + { + ofile.write(entry.oData()); + ofile.close(); + + return 1; + } + + return Error; +} + +quint32 NCFilesystem::zlibSaver(const NCFile& entry, const QString& filename) const +{ + QFile ofile(filename); + + if (!entry.valid()) + return Error; + + if (ofile.open(QIODevice::WriteOnly)) + { + ofile.write(entry.cData()); + ofile.close(); + + return 1; + } + + return Error; +} diff --git a/filesystem/NCFilesystem.hxx b/filesystem/NCFilesystem.hxx new file mode 100644 index 0000000..1b4f1b5 --- /dev/null +++ b/filesystem/NCFilesystem.hxx @@ -0,0 +1,95 @@ +#pragma once + +/* + * the QVector is NOT holding a pointer, this is C++11, we have rvalue references + */ + +#include +#include +#include "common/NCTypes.hxx" +#include "filesystem/NCFile.hxx" + +class NCFilesystem : public QObject { + Q_OBJECT + + // interna typedefs + using ColType = NC::FileData::Type; + using IdentifyType = NC::Identify::Type; +signals: + // signals to report every vfs change + void sigFileStatus(quint32 from_row, quint32 to_row, ColType col) const; + void sigFileAdd(NCFile& file, quint32 row) const; + void sigFileReplace(NCFile& file, quint32 row) const; + void sigFileRemove(quint32 row) const; + +public: + // internal constants + enum : quint32 { + Error = NC::Meta::Error, + HSize = sizeof (quint32) * 2 + }; + + // constructors and deconstructors + NCFilesystem(QObject *parent = 0); + NCFilesystem(const NCFilesystem& rhs); + NCFilesystem(const NCFilesystem&& rhs); + virtual ~NCFilesystem(); + + // operators + NCFilesystem& operator=(const NCFilesystem& rhs); + NCFilesystem& operator=(const NCFilesystem&& rhs); + + bool operator==(const NCFilesystem& rhs) const; + bool operator!=(const NCFilesystem& rhs) const; + + // basic and status methodes + quint32 count() const; + quint32 size() const; + bool valid() const; + void clear(); + QString info() const; + + IdentifyType fileIdentify(const QString& filename) const; + QString fileIdentifyName(const IdentifyType& type) const; + QString fileIdentifyName(const QString& filename) const; + + // setters and getters + quint32 load(const QString& filename); + quint32 save(const QString& filename) const; + + quint32 fileLoad(const QString& filename); + quint32 fileAdd(const QString& filename); + quint32 fileAdd(const NCFile& entry); + + quint32 fileSave(const QString& filename, const QString& name) const; + quint32 fileSave(const QString& filename, const quint32& index) const; + quint32 fileSave(const QString& filename, const QString& name, const IdentifyType& type) const; + quint32 fileSave(const QString& filename, const quint32& index, const IdentifyType& type) const; + + NCFile fileGet(const QString& name) const; + NCFile fileGet(const quint32& index) const; + + NCFile fileReplace(const NCFile& entry, const QString& name); + NCFile fileReplace(const NCFile& entry, const quint32& index); + NCFile fileReplace(const QString& filename, const QString& name); + NCFile fileReplace(const QString& filename, const quint32& index); + + NCFile fileRemove(const QString& name); + NCFile fileRemove(const quint32& index); + +protected: + void recalc(QVector& files); + + quint32 vfsLoader(const QString& filename, const bool add = false); + quint32 pakLoader(const QString& filename, const bool add = false); + quint32 plainLoader(const QString& filename, const bool add = false); + quint32 zlibLoader(const QString& filename, const bool add = false); + + quint32 vfsSaver(const QString& filename) const; + quint32 pakSaver(const NCFile& entry, const QString& filename) const; + quint32 plainSaver(const NCFile& entry, const QString& filename) const; + quint32 zlibSaver(const NCFile& entry, const QString& filename) const; + +private: + QVector _files; +}; diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt new file mode 100644 index 0000000..ff52526 --- /dev/null +++ b/tests/CMakeLists.txt @@ -0,0 +1,8 @@ +# apps +add_executable (test_file test_file.cxx) +qt5_use_modules (test_file Core) +target_link_libraries (test_file Filesystem) + +add_executable (test_filesystem test_filesystem.cxx) +qt5_use_modules (test_filesystem Core) +target_link_libraries (test_filesystem Filesystem) diff --git a/tests/test_file.cxx b/tests/test_file.cxx new file mode 100644 index 0000000..4e640c1 --- /dev/null +++ b/tests/test_file.cxx @@ -0,0 +1,46 @@ +#include +#include +#include "filesystem/NCFile.hxx" + +QTextStream out(stdout); +QTextStream err(stderr); + +int main(int argc, char **argv) +{ + QFile ifile; + NCFile entry; + QString filename = argv[0]; + QByteArray stream; + + if (argc == 2) + filename = argv[1]; + + ifile.setFileName(filename); + + if (!ifile.exists()) + { + err << "ERROR: unable to open file '" << filename << "'" << endl; + return 1; + } + + if (ifile.open(QIODevice::ReadOnly)) + { + stream.resize(ifile.size()); + ifile.read(stream.data(), ifile.size()); + ifile.close(); + } + + entry.setPosition(17); + entry.setOSize(stream.size()); + entry.setName(filename); + entry.setNameLength(filename.size() + 1); + entry.setOData(stream); + entry.setCSize(entry.cData().size()); + entry.setHSize(entry.headerSize() + entry.nameLength()); + + out << "file: i:" << entry.hSize() << " p:" << entry.position() << " c:" << entry.cSize() << " :o" + << entry.oSize() << " l:" << entry.nameLength() << " n:'" << entry.name() << "' -> " << entry.size() + << " bytes" << endl; + + return 0; +} diff --git a/tests/test_filesystem.cxx b/tests/test_filesystem.cxx new file mode 100644 index 0000000..81b660c --- /dev/null +++ b/tests/test_filesystem.cxx @@ -0,0 +1,20 @@ +#include +#include "filesystem/NCFilesystem.hxx" + +QTextStream out(stdout); + +int main(int argc, char **argv) +{ + QString filename = argv[0]; + NCFilesystem vfs; + + if (argc == 2) + filename = argv[1]; + + out << "loading of '" << filename << "' as VFS was" << (vfs.load(filename) == NC::Meta::Error ? " not " : " ") + << "successful" << endl; + + out << "files:" << endl << vfs.info() << endl; + + return 0; +} diff --git a/tools/CMakeLists.txt b/tools/CMakeLists.txt new file mode 100644 index 0000000..e9d8be9 --- /dev/null +++ b/tools/CMakeLists.txt @@ -0,0 +1,13 @@ +# apps +add_executable (vfs_tool vfs_tool.cxx) +qt5_use_modules (vfs_tool Core) +target_link_libraries (vfs_tool Filesystem) + +add_executable (dat_viewer dat_viewer.cxx) +qt5_use_modules (dat_viewer Core) + +add_executable (zcompress zcompress.cxx) +qt5_use_modules (zcompress Core) + +add_executable (zdecompress zdecompress.cxx) +qt5_use_modules (zdecompress Core) diff --git a/tools/dat_viewer.cxx b/tools/dat_viewer.cxx new file mode 100644 index 0000000..b92c425 --- /dev/null +++ b/tools/dat_viewer.cxx @@ -0,0 +1,106 @@ +#include +#include + +QTextStream out(stdout); +QTextStream err(stderr); + +struct Furniture { + float coord[3]; + float rot[3]; + float scale; + quint32 unk1; + quint16 mid; + quint32 unk2[2]; + quint16 unk3; + quint16 wid; + quint16 unk4; + qint32 oid; + float lower[3]; + float upper[3]; +} __attribute__((packed)); + +int main(int argc, char **argv) +{ + QFile ifile; + + if (argc == 2) + { + ifile.setFileName(argv[1]); + if (ifile.open(QIODevice::ReadOnly)) + { + QByteArray head; + + while (!ifile.atEnd()) + { + QByteArray head(5 * 4, '\0'); + quint32 counter = 0; + quint32 type = 0; + quint32 size = 0; + + ifile.read(reinterpret_cast(head.data()), head.size()); + ifile.read(reinterpret_cast(&type), sizeof (type)); + ifile.read(reinterpret_cast(&size), sizeof (size)); + + out << "head: 0x" << head.toHex().toUpper() << endl + << "type: " << type << endl + << "size: " << size << endl; + + while (size) + { + QByteArray id(2 * 4, '\0'); + quint32 objtype = 0; + quint32 objsize = 0; + + ifile.read(reinterpret_cast(id.data()), id.size()); + ifile.read(reinterpret_cast(&objtype), sizeof (objtype)); + ifile.read(reinterpret_cast(&objsize), sizeof (objsize)); + + size -= (16 + objsize); + + out << "---------------------------------------------------" << endl + << "index: " << counter << endl + << "id: 0x" << id.toHex().toUpper() << endl + << "objtype: " << objtype << endl + << "objsize: " << objsize << endl + << "leftsize: " << size << endl; + + switch (objtype) + { + case 1000003: + { + Furniture f; + + ifile.read(reinterpret_cast(&f), sizeof (f)); + + out << "furniture!" << endl + << "pos: " << f.coord[0] << ", " << f.coord[1] << ", " << f.coord[2] << endl + << "rot: " << f.rot[0] << ", " << f.rot[1] << ", " << f.rot[2] << endl + << "scale: " << f.scale << endl + << "unk1: " << f.unk1 << endl + << "model id: " << f.mid << endl + << "unk2: " << f.unk2[0] << ", " << f.unk2[1] << endl + << "unk3: " << f.unk3 << endl + << "world id: " << f.wid << endl + << "unk4: " << f.unk4 << endl + << "obj id: " << f.oid << endl + << "low box: " << f.lower[0] << ", " << f.lower[1] << ", " << f.lower[2] << endl + << "up box: " << f.upper[0] << ", " << f.upper[1] << ", " << f.upper[2] << endl; + + break; + } + default: + ifile.seek(ifile.pos() + objsize); + } + ++counter; + } + } + ifile.close(); + } + + return 0; + } + + out << "usage: " << argv[0] << " " << endl; + + return 0; +} diff --git a/tools/vfs_tool.cxx b/tools/vfs_tool.cxx new file mode 100644 index 0000000..673447b --- /dev/null +++ b/tools/vfs_tool.cxx @@ -0,0 +1,127 @@ +/* + * commands: + * - quit/exit : die :D + * - ls/list : vfs.info(); + * - cls/clear : vfs.clear() + * - size : vfs.size() + * - count : vfs.count() + * -------------------------------------------------------------------------- + * - load : vfs.fileLoad() + * - add : vfs.fileAdd() + * - remove/delete/rem/del : vfs.fileRemove() + * - save : vfs.save() + * -------------------------------------------------------------------------- + * - replace/rep : vfs.fileReplace() + * - save : vfs.fileSave() + */ + +#include +#include +#include "filesystem/NCFilesystem.hxx" + +QTextStream in(stdin); +QTextStream out(stdout); +QTextStream err(stderr); + +int main() +{ + NCFilesystem vfs; + QString input; + QStringList list; + + while (true) + { + out << "(" << vfs.count() << "|" << vfs.size() << ") > " << flush; + input = in.readLine(); + list = input.split(" "); + + switch (list.count()) + { + case 1: + { + if (list[0].compare("quit", Qt::CaseInsensitive) == 0 || list[0].compare("exit", Qt::CaseInsensitive) == 0) + return 0; + + if (list[0].compare("clear", Qt::CaseInsensitive) == 0 || list[0].compare("cls", Qt::CaseInsensitive) == 0) + vfs.clear(); + + if (list[0].compare("list", Qt::CaseInsensitive) == 0 || list[0].compare("ls", Qt::CaseInsensitive) == 0) + out << "list: " << endl << vfs.info() << endl; + + if (list[0].compare("size", Qt::CaseInsensitive) == 0) + out << "size: " << vfs.size() << " bytes" << endl; + + if (list[0].compare("count", Qt::CaseInsensitive) == 0) + out << "files: " << vfs.count() << endl; + + break; + } + case 2: + { + if (list[0].compare("load", Qt::CaseInsensitive) == 0) + out << "loading of '" << list[1] << "' " << (vfs.fileLoad(list[1]) != vfs.Error ? "successful" : "failed") + << endl; + + if (list[0].compare("add", Qt::CaseInsensitive) == 0) + out << "adding of '" << list[1] << "' " << (vfs.fileAdd(list[1]) != vfs.Error ? "successful" : "failed") + << endl; + + if (list[0].compare("remove", Qt::CaseInsensitive) == 0 || list[0].compare("delete", Qt::CaseInsensitive) == 0 + || list[0].compare("rem", Qt::CaseInsensitive) == 0 || list[0].compare("del", Qt::CaseInsensitive) == 0) + { + bool ok = true; + quint32 index = list[1].toUInt(&ok); + + if (ok) + out << "removing of file at index " << index << " " + << (vfs.fileRemove(index).valid() ? "successful" : "failed") << endl; + else + out << "removing of file '" << list[1] << "' " + << (vfs.fileRemove(list[1]).valid() ? "successful" : "failed") << endl; + } + + if (list[0].compare("save", Qt::CaseInsensitive) == 0) + out << "saving vfs as '" << list[1] << "' " << (vfs.save(list[1]) != vfs.Error ? "successful" : "failed") + << endl; + + break; + } + case 3: + { + /*if (list[0].compare("replace", Qt::CaseInsensitive) == 0 || list[0].compare("rep", Qt::CaseInsensitive) == 0) + { + bool ok = true; + quint32 index = list[1].toUInt(&ok); + + if (ok) + out << "replacing file at index " << index << " with file '" << list[2] << "' " + << (vfs.fileReplace(list[2], index).valid() ? "successful" : "failed") << endl; + else + out << "replacing file '" << list[1] << " with file " << list[2] << "' " + << (vfs.fileReplace(list[2], list[1]).valid() ? "successful" : "failed") << endl; + }*/ + + if (list[0].compare("save", Qt::CaseInsensitive) == 0) + { + bool ok = true; + quint32 index = list[1].toUInt(&ok); + + if (ok) + out << "saving file at index " << index << " as file '" << list[2] << "' " + << (vfs.fileSave(list[2], index) != vfs.Error ? "successful" : "failed") << endl; + else + out << "saving file '" << list[1] << "' as file '" << list[2] << "' " + << (vfs.fileSave(list[2], list[1]) != vfs.Error ? "successful" : "failed") << endl; + } + + break; + } + case 4: + break; + default: + err << "error: unknown command '" << input << "'" << endl; + } + } + + return 0; +} diff --git a/tools/zcompress.cxx b/tools/zcompress.cxx new file mode 100644 index 0000000..2b7ed2a --- /dev/null +++ b/tools/zcompress.cxx @@ -0,0 +1,59 @@ +#include +#include +#include + +QTextStream out(stdout); +QTextStream err(stderr); + +int main(int argc, char **argv) +{ + if (argc == 2) + { + QFile file(argv[1]); + + if (!file.exists()) + { + err << "ERROR: unable to open file '" << file.fileName() << "'" << endl; + return 1; + } + + if (file.open(QIODevice::ReadOnly)) + { + QByteArray stream(file.size(), '\0'); + + file.read(stream.data(), file.size()); + file.close(); + + if (stream[0] == 'x') + { + err << "ERROR: file '" << file.fileName() << "' is zlib compressed already" << endl; + return 1; + } + + stream = qCompress(stream, 9).mid(sizeof (quint32)); + + file.setFileName(file.fileName() + ".z"); + if (file.open(QIODevice::WriteOnly)) + { + file.write(stream); + file.close(); + + return 0; + } + else + { + err << "ERROR: file '" << file.fileName() << "' is not writeable" << endl; + return 1; + } + } + else + { + err << "ERROR: file '" << file.fileName() << "' is not readable" << endl; + return 1; + } + } + + out << "usage: " << argv[0] << " " << endl; + + return 0; +} diff --git a/tools/zdecompress.cxx b/tools/zdecompress.cxx new file mode 100644 index 0000000..cbf8246 --- /dev/null +++ b/tools/zdecompress.cxx @@ -0,0 +1,62 @@ +#include +#include +#include + +QTextStream out(stdout); +QTextStream err(stderr); + +int main(int argc, char **argv) +{ + if (argc == 2) + { + QFile file(argv[1]); + + if (!file.exists()) + { + err << "ERROR: unable to open file '" << file.fileName() << "'" << endl; + return 1; + } + + if (file.open(QIODevice::ReadOnly)) + { + // black maigc ;-) naaa, we need a quint32 at the beginning of the zlib stream to get qUncompress() + // to eat it + const quint32 tmp_size = sizeof (quint32); + QByteArray stream(file.size() + tmp_size, '\0'); + + file.read(stream.data() + 4, file.size()); + file.close(); + + if (stream[tmp_size] != 'x') + { + err << "ERROR: file '" << file.fileName() << "' is not zlib compressed" << endl; + return 1; + } + + stream = qUncompress(stream); + + file.setFileName(file.fileName() + ".decompressed"); + if (file.open(QIODevice::WriteOnly)) + { + file.write(stream); + file.close(); + + return 0; + } + else + { + err << "ERROR: file '" << file.fileName() << "' is not writeable" << endl; + return 1; + } + } + else + { + err << "ERROR: file '" << file.fileName() << "' is not readable" << endl; + return 1; + } + } + + out << "usage: " << argv[0] << " <.z file to decompress>" << endl; + + return 0; +}