initial test code
This commit is contained in:
commit
0df35fc0c9
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
build
|
55
CMakeLists.txt
Normal file
55
CMakeLists.txt
Normal file
|
@ -0,0 +1,55 @@
|
|||
cmake_minimum_required(VERSION 3.14)
|
||||
cmake_policy(SET CMP0046 NEW)
|
||||
cmake_policy(SET CMP0063 NEW)
|
||||
cmake_policy(SET CMP0071 NEW)
|
||||
|
||||
project(agw-quake-tools CXX)
|
||||
|
||||
if(NOT MSVC)
|
||||
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -Wall -Wextra -Werror")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-rtti")
|
||||
endif()
|
||||
|
||||
macro(make_qt_project TARGET_NAME)
|
||||
target_include_directories(${TARGET_NAME} PUBLIC source)
|
||||
|
||||
set_target_properties(
|
||||
${TARGET_NAME}
|
||||
PROPERTIES
|
||||
CXX_STANDARD 17
|
||||
CXX_STANDARD_REQUIRED ON
|
||||
AUTOMOC ON
|
||||
AUTOUIC ON
|
||||
AUTORCC ON)
|
||||
|
||||
target_link_libraries(${TARGET_NAME} Qt5::Core Qt5::Gui Qt5::Widgets)
|
||||
|
||||
target_compile_definitions(
|
||||
${TARGET_NAME}
|
||||
PUBLIC
|
||||
-DQT_DEPRECATED_WARNINGS
|
||||
-DQT_STRICT_ITERATORS
|
||||
-DQT_NO_NARROWING_CONVERSIONS_IN_CONNECT)
|
||||
endmacro()
|
||||
|
||||
find_package(
|
||||
Qt5 5.13.0
|
||||
CONFIG
|
||||
REQUIRED
|
||||
COMPONENTS Core Widgets Gui)
|
||||
|
||||
add_executable(
|
||||
quam WIN32
|
||||
source/common.h
|
||||
source/quam/main.cc
|
||||
source/quam/main_window.cc
|
||||
source/quam/main_window.h
|
||||
source/quam/main_window.ui
|
||||
source/quam/pak.cc
|
||||
source/quam/pak.h)
|
||||
|
||||
make_qt_project(quam)
|
||||
|
||||
install(TARGETS quam)
|
||||
|
||||
## EOF
|
125
LICENSE
Normal file
125
LICENSE
Normal file
|
@ -0,0 +1,125 @@
|
|||
To the extent possible under law, I, Alison Watson, and all other contributors
|
||||
to quake-tools, have waived all copyright and related or neighboring rights to
|
||||
this Document as described by the Creative Commons Zero license as follows:
|
||||
|
||||
Creative Commons Legal Code
|
||||
|
||||
CC0 1.0 Universal
|
||||
|
||||
CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE
|
||||
LEGAL SERVICES. DISTRIBUTION OF THIS DOCUMENT DOES NOT CREATE AN
|
||||
ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS
|
||||
INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES
|
||||
REGARDING THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS
|
||||
PROVIDED HEREUNDER, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM
|
||||
THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED
|
||||
HEREUNDER.
|
||||
|
||||
Statement of Purpose
|
||||
|
||||
The laws of most jurisdictions throughout the world automatically confer
|
||||
exclusive Copyright and Related Rights (defined below) upon the creator
|
||||
and subsequent owner(s) (each and all, an "owner") of an original work of
|
||||
authorship and/or a database (each, a "Work").
|
||||
|
||||
Certain owners wish to permanently relinquish those rights to a Work for
|
||||
the purpose of contributing to a commons of creative, cultural and
|
||||
scientific works ("Commons") that the public can reliably and without fear
|
||||
of later claims of infringement build upon, modify, incorporate in other
|
||||
works, reuse and redistribute as freely as possible in any form whatsoever
|
||||
and for any purposes, including without limitation commercial purposes.
|
||||
These owners may contribute to the Commons to promote the ideal of a free
|
||||
culture and the further production of creative, cultural and scientific
|
||||
works, or to gain reputation or greater distribution for their Work in
|
||||
part through the use and efforts of others.
|
||||
|
||||
For these and/or other purposes and motivations, and without any
|
||||
expectation of additional consideration or compensation, the person
|
||||
associating CC0 with a Work (the "Affirmer"), to the extent that he or she
|
||||
is an owner of Copyright and Related Rights in the Work, voluntarily
|
||||
elects to apply CC0 to the Work and publicly distribute the Work under its
|
||||
terms, with knowledge of his or her Copyright and Related Rights in the
|
||||
Work and the meaning and intended legal effect of CC0 on those rights.
|
||||
|
||||
1. Copyright and Related Rights. A Work made available under CC0 may be
|
||||
protected by copyright and related or neighboring rights ("Copyright and
|
||||
Related Rights"). Copyright and Related Rights include, but are not
|
||||
limited to, the following:
|
||||
|
||||
i. the right to reproduce, adapt, distribute, perform, display,
|
||||
communicate, and translate a Work;
|
||||
ii. moral rights retained by the original author(s) and/or performer(s);
|
||||
iii. publicity and privacy rights pertaining to a person's image or
|
||||
likeness depicted in a Work;
|
||||
iv. rights protecting against unfair competition in regards to a Work,
|
||||
subject to the limitations in paragraph 4(a), below;
|
||||
v. rights protecting the extraction, dissemination, use and reuse of data
|
||||
in a Work;
|
||||
vi. database rights (such as those arising under Directive 96/9/EC of the
|
||||
European Parliament and of the Council of 11 March 1996 on the legal
|
||||
protection of databases, and under any national implementation
|
||||
thereof, including any amended or successor version of such
|
||||
directive); and
|
||||
vii. other similar, equivalent or corresponding rights throughout the
|
||||
world based on applicable law or treaty, and any national
|
||||
implementations thereof.
|
||||
|
||||
2. Waiver. To the greatest extent permitted by, but not in contravention
|
||||
of, applicable law, Affirmer hereby overtly, fully, permanently,
|
||||
irrevocably and unconditionally waives, abandons, and surrenders all of
|
||||
Affirmer's Copyright and Related Rights and associated claims and causes
|
||||
of action, whether now known or unknown (including existing as well as
|
||||
future claims and causes of action), in the Work (i) in all territories
|
||||
worldwide, (ii) for the maximum duration provided by applicable law or
|
||||
treaty (including future time extensions), (iii) in any current or future
|
||||
medium and for any number of copies, and (iv) for any purpose whatsoever,
|
||||
including without limitation commercial, advertising or promotional
|
||||
purposes (the "Waiver"). Affirmer makes the Waiver for the benefit of each
|
||||
member of the public at large and to the detriment of Affirmer's heirs and
|
||||
successors, fully intending that such Waiver shall not be subject to
|
||||
revocation, rescission, cancellation, termination, or any other legal or
|
||||
equitable action to disrupt the quiet enjoyment of the Work by the public
|
||||
as contemplated by Affirmer's express Statement of Purpose.
|
||||
|
||||
3. Public License Fallback. Should any part of the Waiver for any reason
|
||||
be judged legally invalid or ineffective under applicable law, then the
|
||||
Waiver shall be preserved to the maximum extent permitted taking into
|
||||
account Affirmer's express Statement of Purpose. In addition, to the
|
||||
extent the Waiver is so judged Affirmer hereby grants to each affected
|
||||
person a royalty-free, non transferable, non sublicensable, non exclusive,
|
||||
irrevocable and unconditional license to exercise Affirmer's Copyright and
|
||||
Related Rights in the Work (i) in all territories worldwide, (ii) for the
|
||||
maximum duration provided by applicable law or treaty (including future
|
||||
time extensions), (iii) in any current or future medium and for any number
|
||||
of copies, and (iv) for any purpose whatsoever, including without
|
||||
limitation commercial, advertising or promotional purposes (the
|
||||
"License"). The License shall be deemed effective as of the date CC0 was
|
||||
applied by Affirmer to the Work. Should any part of the License for any
|
||||
reason be judged legally invalid or ineffective under applicable law, such
|
||||
partial invalidity or ineffectiveness shall not invalidate the remainder
|
||||
of the License, and in such case Affirmer hereby affirms that he or she
|
||||
will not (i) exercise any of his or her remaining Copyright and Related
|
||||
Rights in the Work or (ii) assert any associated claims and causes of
|
||||
action with respect to the Work, in either case contrary to Affirmer's
|
||||
express Statement of Purpose.
|
||||
|
||||
4. Limitations and Disclaimers.
|
||||
|
||||
a. No trademark or patent rights held by Affirmer are waived, abandoned,
|
||||
surrendered, licensed or otherwise affected by this document.
|
||||
b. Affirmer offers the Work as-is and makes no representations or
|
||||
warranties of any kind concerning the Work, express, implied,
|
||||
statutory or otherwise, including without limitation warranties of
|
||||
title, merchantability, fitness for a particular purpose, non
|
||||
infringement, or the absence of latent or other defects, accuracy, or
|
||||
the present or absence of errors, whether or not discoverable, all to
|
||||
the greatest extent permissible under applicable law.
|
||||
c. Affirmer disclaims responsibility for clearing rights of other persons
|
||||
that may apply to the Work or any use thereof, including without
|
||||
limitation any person's Copyright and Related Rights in the Work.
|
||||
Further, Affirmer disclaims responsibility for obtaining any necessary
|
||||
consents, permissions or other rights required for any use of the
|
||||
Work.
|
||||
d. Affirmer understands and acknowledges that Creative Commons is not a
|
||||
party to this document and has no duty or obligation with respect to
|
||||
this CC0 or use of the Work.
|
76
source/common.h
Normal file
76
source/common.h
Normal file
|
@ -0,0 +1,76 @@
|
|||
#pragma once
|
||||
|
||||
#include <QtEndian>
|
||||
#include <QtGlobal>
|
||||
|
||||
#include <QCoreApplication>
|
||||
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <filesystem>
|
||||
#include <fstream>
|
||||
#include <ios>
|
||||
#include <iostream>
|
||||
#include <istream>
|
||||
#include <iterator>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
|
||||
static inline QString trMain(char const *sourceText,
|
||||
char const *disambiguation = nullptr,
|
||||
int n = -1) {
|
||||
return QCoreApplication::translate("main", sourceText, disambiguation, n);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
static inline T readLE(std::istream &st) {
|
||||
std::array<char, sizeof(T)> b;
|
||||
st.read(b.data(), b.size());
|
||||
return qFromLittleEndian<T>(b.data());
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
static inline T readBE(std::istream &st) {
|
||||
std::array<char, sizeof(T)> b;
|
||||
st.read(b.data(), b.size());
|
||||
return qFromBigEndian<T>(b.data());
|
||||
}
|
||||
|
||||
template<std::size_t N>
|
||||
static inline std::array<char, N> readBytes(std::istream &st) {
|
||||
std::array<char, N> b;
|
||||
st.read(b.data(), b.size());
|
||||
return std::move(b);
|
||||
}
|
||||
|
||||
template<typename It>
|
||||
static inline std::string escapeText(It begin, It end) {
|
||||
std::stringstream out;
|
||||
out << std::hex << std::uppercase;
|
||||
for(auto it = begin; it != end; ++it) {
|
||||
unsigned char c = *it;
|
||||
switch(c) {
|
||||
case '"': out << "\\\""; break;
|
||||
case '\0': out << "\\0"; break;
|
||||
case '\\': out << "\\\\"; break;
|
||||
case '\a': out << "\\a"; break;
|
||||
case '\b': out << "\\b"; break;
|
||||
case '\f': out << "\\f"; break;
|
||||
case '\n': out << "\\n"; break;
|
||||
case '\r': out << "\\r"; break;
|
||||
case '\t': out << "\\t"; break;
|
||||
case '\v': out << "\\v"; break;
|
||||
default:
|
||||
if(c >= ' ' && c <= '~') {
|
||||
out << c;
|
||||
} else {
|
||||
out << "\\x" << int(c);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
return out.str();
|
||||
}
|
||||
|
||||
// EOF
|
59
source/quam/main.cc
Normal file
59
source/quam/main.cc
Normal file
|
@ -0,0 +1,59 @@
|
|||
#include "common.h"
|
||||
#include "quam/main_window.h"
|
||||
#include "quam/pak.h"
|
||||
|
||||
#include <QApplication>
|
||||
#include <QCommandLineParser>
|
||||
#include <QCoreApplication>
|
||||
|
||||
static void setupAppl(QCoreApplication &appl) {
|
||||
appl.setApplicationName("QuAM!");
|
||||
appl.setApplicationVersion("1.0");
|
||||
appl.setOrganizationDomain("greyserv.net");
|
||||
appl.setOrganizationName("Project Golan");
|
||||
}
|
||||
|
||||
static int modeGui(int argc, char *argv[]) {
|
||||
QApplication appl{argc, argv};
|
||||
setupAppl(appl);
|
||||
MainWindow win;
|
||||
win.show();
|
||||
return appl.exec();
|
||||
}
|
||||
|
||||
static int modeText(int argc, char *argv[]) {
|
||||
QCoreApplication appl{argc, argv};
|
||||
setupAppl(appl);
|
||||
|
||||
QCommandLineParser par;
|
||||
|
||||
par.setApplicationDescription("Quake Archive Manager");
|
||||
par.setSingleDashWordOptionMode(
|
||||
QCommandLineParser::ParseAsCompactedShortOptions);
|
||||
|
||||
par.addHelpOption();
|
||||
par.addVersionOption();
|
||||
|
||||
QCommandLineOption fileNameOpt{QStringList{"f", "file"},
|
||||
trMain("Open the archive <file>."),
|
||||
trMain("file")};
|
||||
par.addOption(fileNameOpt);
|
||||
|
||||
par.process(appl);
|
||||
|
||||
auto fileName = par.value(fileNameOpt).toStdString();
|
||||
std::ifstream st{fileName, std::ios_base::in | std::ios_base::binary};
|
||||
readPakFile(st);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
if(argc == 0) {
|
||||
return modeGui(argc, argv);
|
||||
} else {
|
||||
return modeText(argc, argv);
|
||||
}
|
||||
}
|
||||
|
||||
// EOF
|
15
source/quam/main_window.cc
Normal file
15
source/quam/main_window.cc
Normal file
|
@ -0,0 +1,15 @@
|
|||
#include "common.h"
|
||||
#include "quam/main_window.h"
|
||||
#include "quam/pak.h"
|
||||
|
||||
MainWindow::MainWindow(QWidget *parent) :
|
||||
QMainWindow{parent},
|
||||
Ui::MainWindow{}
|
||||
{
|
||||
setupUi(this);
|
||||
}
|
||||
|
||||
void MainWindow::openTest() {
|
||||
}
|
||||
|
||||
// EOF
|
18
source/quam/main_window.h
Normal file
18
source/quam/main_window.h
Normal file
|
@ -0,0 +1,18 @@
|
|||
#pragma once
|
||||
|
||||
#include "quam/ui_main_window.h"
|
||||
|
||||
#include <QMainWindow>
|
||||
#include <QWidget>
|
||||
|
||||
class MainWindow : public QMainWindow, private Ui::MainWindow {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit MainWindow(QWidget *parent = nullptr);
|
||||
|
||||
public slots:
|
||||
void openTest();
|
||||
};
|
||||
|
||||
// EOF
|
67
source/quam/main_window.ui
Normal file
67
source/quam/main_window.ui
Normal file
|
@ -0,0 +1,67 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>MainWindow</class>
|
||||
<widget class="QMainWindow" name="MainWindow">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>640</width>
|
||||
<height>480</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Quam!</string>
|
||||
</property>
|
||||
<widget class="QWidget" name="centralwidget">
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_2">
|
||||
<item>
|
||||
<widget class="QPushButton" name="pushButton">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>PushButton</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<widget class="QMenuBar" name="menubar">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>640</width>
|
||||
<height>29</height>
|
||||
</rect>
|
||||
</property>
|
||||
</widget>
|
||||
<widget class="QStatusBar" name="statusbar"/>
|
||||
</widget>
|
||||
<resources/>
|
||||
<connections>
|
||||
<connection>
|
||||
<sender>pushButton</sender>
|
||||
<signal>clicked()</signal>
|
||||
<receiver>MainWindow</receiver>
|
||||
<slot>openTest()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>59</x>
|
||||
<y>53</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>319</x>
|
||||
<y>239</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
</connections>
|
||||
<slots>
|
||||
<slot>openTest()</slot>
|
||||
</slots>
|
||||
</ui>
|
76
source/quam/pak.cc
Normal file
76
source/quam/pak.cc
Normal file
|
@ -0,0 +1,76 @@
|
|||
#include "common.h"
|
||||
#include "quam/pak.h"
|
||||
|
||||
struct PakHeader {
|
||||
quint32 dirOffset;
|
||||
quint32 dirNum;
|
||||
};
|
||||
|
||||
struct PakEntry {
|
||||
std::string name;
|
||||
QByteArray data;
|
||||
};
|
||||
|
||||
static constexpr quint32 sizeOfPakEntry = 64;
|
||||
|
||||
static PakHeader readPakHeader(std::istream &st) {
|
||||
auto magic = readBytes<4>(st);
|
||||
|
||||
if(magic != std::array{'P', 'A', 'C', 'K'}) {
|
||||
throw std::runtime_error("not a pak file (invalid magic number)");
|
||||
}
|
||||
|
||||
auto dirOffset = readLE<quint32>(st);
|
||||
auto dirSize = readLE<quint32>(st);
|
||||
|
||||
if(dirSize % sizeOfPakEntry != 0) {
|
||||
throw std::runtime_error("invalid directory size");
|
||||
}
|
||||
|
||||
PakHeader hdr;
|
||||
hdr.dirOffset = dirOffset;
|
||||
hdr.dirNum = dirSize / sizeOfPakEntry;
|
||||
return hdr;
|
||||
}
|
||||
|
||||
static PakEntry readPakEntry(std::istream &st) {
|
||||
auto entName = readBytes<56>(st);
|
||||
auto entOffset = readLE<quint32>(st);
|
||||
auto entSize = readLE<quint32>(st);
|
||||
|
||||
auto pos = st.tellg();
|
||||
|
||||
st.seekg(entOffset);
|
||||
|
||||
QByteArray bytes;
|
||||
bytes.resize(entSize);
|
||||
st.read(bytes.data(), entSize);
|
||||
|
||||
st.seekg(pos);
|
||||
|
||||
auto zero = std::find(entName.cbegin(), entName.cend(), '\0');
|
||||
|
||||
std::string out;
|
||||
std::copy(entName.cbegin(), zero, std::back_inserter(out));
|
||||
|
||||
PakEntry ent;
|
||||
ent.name = std::move(out);
|
||||
ent.data = std::move(bytes);
|
||||
return ent;
|
||||
}
|
||||
|
||||
PakFile readPakFile(std::istream &st) {
|
||||
auto hdr = readPakHeader(st);
|
||||
st.seekg(hdr.dirOffset);
|
||||
|
||||
PakFile pak;
|
||||
|
||||
for(quint32 i = 0; i < hdr.dirNum; i++) {
|
||||
auto ent = readPakEntry(st);
|
||||
pak.emplace(std::move(ent.name), std::move(ent.data));
|
||||
}
|
||||
|
||||
return pak;
|
||||
}
|
||||
|
||||
// EOF
|
8
source/quam/pak.h
Normal file
8
source/quam/pak.h
Normal file
|
@ -0,0 +1,8 @@
|
|||
#pragma once
|
||||
|
||||
class PakFile : public std::map<std::string, QByteArray> {
|
||||
};
|
||||
|
||||
PakFile readPakFile(std::istream &st);
|
||||
|
||||
// EOF
|
Loading…
Reference in New Issue
Block a user