summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDennis Brentjes <d.brentjes@gmail.com>2016-06-19 11:58:16 +0200
committerDennis Brentjes <d.brentjes@gmail.com>2016-06-19 11:58:16 +0200
commitc66d1f5c0af70161f4ad4c4175f4280e95b55dfd (patch)
treef8e5e5d6dc921054182e478be742ac30258c56c6
downloadopenwar-c66d1f5c0af70161f4ad4c4175f4280e95b55dfd.tar.gz
openwar-c66d1f5c0af70161f4ad4c4175f4280e95b55dfd.tar.bz2
openwar-c66d1f5c0af70161f4ad4c4175f4280e95b55dfd.zip
Initial commit of a mz-header parser.
-rw-r--r--.gitignore2
-rw-r--r--CMakeLists.txt7
-rw-r--r--fusion-utils/CMakeLists.txt13
-rw-r--r--fusion-utils/index_list.hpp16
-rw-r--r--mz/CMakeLists.txt32
-rw-r--r--mz/mz_header.cpp116
-rw-r--r--mz/mz_header.hpp47
-rw-r--r--mz/mz_header_parser.cpp56
8 files changed, 289 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..6e83be0
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,2 @@
+build*
+CMakeLists.txt.user
diff --git a/CMakeLists.txt b/CMakeLists.txt
new file mode 100644
index 0000000..24fb61f
--- /dev/null
+++ b/CMakeLists.txt
@@ -0,0 +1,7 @@
+cmake_minimum_required(VERSION 3.5.2)
+
+project(openwar)
+
+add_subdirectory(fusion-utils)
+add_subdirectory(mz)
+
diff --git a/fusion-utils/CMakeLists.txt b/fusion-utils/CMakeLists.txt
new file mode 100644
index 0000000..50e8565
--- /dev/null
+++ b/fusion-utils/CMakeLists.txt
@@ -0,0 +1,13 @@
+find_package(Boost REQUIRED)
+
+add_library(fusion-utils STATIC
+ index_list.hpp
+)
+
+target_include_directories(fusion-utils
+ PUBLIC ${Boost_INCLUDE_DIRS}
+ INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}
+)
+
+set_target_properties(fusion-utils PROPERTIES LINKER_LANGUAGE CXX)
+
diff --git a/fusion-utils/index_list.hpp b/fusion-utils/index_list.hpp
new file mode 100644
index 0000000..f363dc3
--- /dev/null
+++ b/fusion-utils/index_list.hpp
@@ -0,0 +1,16 @@
+#pragma once
+
+template<int... Indices>
+struct indices {
+ typedef indices<Indices..., sizeof...(Indices)> next;
+};
+
+template<int N>
+struct build_indices {
+ typedef typename build_indices<N - 1>::type::next type;
+};
+
+template<>
+struct build_indices<0> {
+ typedef indices<> type;
+};
diff --git a/mz/CMakeLists.txt b/mz/CMakeLists.txt
new file mode 100644
index 0000000..aad239b
--- /dev/null
+++ b/mz/CMakeLists.txt
@@ -0,0 +1,32 @@
+add_library(mz STATIC
+ mz_header.hpp mz_header.cpp
+)
+
+target_include_directories(mz
+ INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}
+)
+
+target_link_libraries(mz
+ PUBLIC fusion-utils
+)
+
+target_compile_options(mz
+ PUBLIC "-std=c++14"
+)
+
+find_package(Boost COMPONENTS filesystem program_options system REQUIRED)
+
+add_executable(mz_header_parser
+ mz_header_parser.cpp
+)
+
+target_include_directories(mz_header_parser
+ PRIVATE ${Boost_INCLUDE_DIRS}
+)
+
+target_link_libraries(mz_header_parser
+ PRIVATE mz
+ PRIVATE ${Boost_LIBRARIES}
+)
+
+
diff --git a/mz/mz_header.cpp b/mz/mz_header.cpp
new file mode 100644
index 0000000..e6d4461
--- /dev/null
+++ b/mz/mz_header.cpp
@@ -0,0 +1,116 @@
+#include "mz_header.hpp"
+#include "index_list.hpp"
+
+#include <boost/fusion/adapted/struct.hpp>
+#include <boost/fusion/adapted/struct/detail/extension.hpp>
+
+#include <vector>
+#include <iomanip>
+
+UnexpectedEOS::UnexpectedEOS()
+: std::runtime_error("Unexpected end of stream.")
+{}
+
+UnexpectedEOS::UnexpectedEOS(std::__cxx11::string location)
+: std::runtime_error("Unexpected end of stream after " + location)
+{}
+
+NotAMZFileException::NotAMZFileException()
+: std::runtime_error("This stream does not contain a valid MZ executable")
+{}
+
+BOOST_FUSION_ADAPT_STRUCT(
+ MZHeader,
+ (Magic, magic)
+ (uint16_t, bytes_in_last_block)
+ (uint16_t, blocks_in_file)
+ (uint16_t, num_relocs)
+ (uint16_t, header_paragraphs)
+ (uint16_t, min_extra_paragraphs)
+ (uint16_t, max_extra_paragraphs)
+ (uint16_t, ss)
+ (uint16_t, sp)
+ (uint16_t, checksum)
+ (uint16_t, ip)
+ (uint16_t, cs)
+ (uint16_t, reloc_table_offset)
+ (uint16_t, overlay_number)
+)
+
+template<typename T>
+T parse(std::istream& is, std::string name);
+
+template<>
+uint16_t parse<uint16_t>(std::istream& is, std::string name) {
+ if(!is) {
+ throw UnexpectedEOS();
+ }
+ char data[2] = {0, 0};
+ is.read(data, 2);
+ if(!is) {
+ throw UnexpectedEOS(name);
+ }
+
+ return (((unsigned char) data[1]) << 8) | (unsigned char) data[0];
+}
+
+template<>
+Magic parse<Magic>(std::istream& is, std::string name) {
+ if(!is) {
+ throw UnexpectedEOS();
+ }
+ char data[2];
+ is.read(data, 2);
+ if(!is) {
+ throw UnexpectedEOS(name);
+ }
+
+ return static_cast<Magic>((data[1] << 8) | data[0]);
+}
+
+template <int... Indices>
+MZHeader parse_mz_file_impl(std::istream& is, indices<Indices...>) {
+
+ return {parse<typename std::decay<typename boost::fusion::result_of::at_c<MZHeader, Indices>::type>::type>(is, boost::fusion::extension::struct_member_name<MZHeader, Indices>::call())...};
+
+}
+
+MZHeader parse_mz_file(std::istream& is) {
+
+ typedef build_indices<boost::fusion::result_of::size<MZHeader>::value>::type indices;
+
+ return parse_mz_file_impl(is, indices{});
+}
+
+std::ostream& output_impl(std::ostream& os, MZHeader const&, indices<>) {
+ return os;
+}
+
+template<typename T>
+std::ostream& output(std::ostream& os, T rh) {
+ return os << std::hex << std::setw(sizeof(T) * 2) << std::setfill('0') << rh << std::endl;
+}
+
+std::string to_string(Magic magic) {
+ char* c = reinterpret_cast<char*>(&magic);
+ return std::string(c, sizeof(Magic));
+}
+
+template <>
+std::ostream& output<Magic>(std::ostream& os, Magic m) {
+ return os << std::hex << std::setw(4) << std::setfill('0') << m << " (" << to_string(m) << ")" << std::endl;
+}
+
+template <int I, int... Indices>
+std::ostream& output_impl(std::ostream& os, const MZHeader& header, indices<I, Indices...>) {
+ os << boost::fusion::extension::struct_member_name<MZHeader, I>::call() << ": ";
+ output(os, boost::fusion::at_c<I>(header));
+ return output_impl(os, header, indices<Indices...>{});
+}
+
+std::ostream& operator<<(std::ostream& os, MZHeader const& header)
+{
+ typedef build_indices<boost::fusion::result_of::size<MZHeader>::value>::type indices;
+
+ return output_impl(os, header, indices{});
+}
diff --git a/mz/mz_header.hpp b/mz/mz_header.hpp
new file mode 100644
index 0000000..ad5f419
--- /dev/null
+++ b/mz/mz_header.hpp
@@ -0,0 +1,47 @@
+#pragma once
+
+#include <cstdint>
+#include <cstddef>
+#include <stdexcept>
+#include <istream>
+#include <ostream>
+
+#include <boost/serialization/strong_typedef.hpp>
+
+BOOST_STRONG_TYPEDEF(uint16_t, Magic);
+
+struct NotAMZFileException : public std::runtime_error {
+ NotAMZFileException();
+};
+
+struct UnexpectedEOS : public std::runtime_error {
+ UnexpectedEOS();
+
+ UnexpectedEOS(std::string location);
+};
+
+struct MZHeader {
+ Magic magic;
+ uint16_t bytes_in_last_block;
+ uint16_t blocks_in_file;
+ uint16_t num_relocs;
+ uint16_t header_paragraphs;
+ uint16_t min_extra_paragraphs;
+ uint16_t max_extra_paragraphs;
+ uint16_t ss;
+ uint16_t sp;
+ uint16_t checksum;
+ uint16_t ip;
+ uint16_t cs;
+ uint16_t reloc_table_offset;
+ uint16_t overlay_number;
+};
+
+uint16_t parse_uint16_t(std::istream& is, std::string name = "");
+
+MZHeader parse_mz_file(std::istream& is);
+
+void print_mz_header(MZHeader const& header);
+
+std::ostream& operator<<(std::ostream& os, MZHeader const& header);
+
diff --git a/mz/mz_header_parser.cpp b/mz/mz_header_parser.cpp
new file mode 100644
index 0000000..d18a72e
--- /dev/null
+++ b/mz/mz_header_parser.cpp
@@ -0,0 +1,56 @@
+
+#include "mz_header.hpp"
+
+#include <boost/program_options.hpp>
+#include <boost/filesystem.hpp>
+
+#include <iostream>
+
+int main(int argc, char* argv[]) {
+ boost::program_options::options_description description;
+ description.add_options()
+ ("help,h", "produces this help message")
+ ("exe,e", boost::program_options::value<std::string>(), "The MZ executable to parse the header for.")
+ ;
+
+ boost::program_options::variables_map vm;
+ boost::program_options::store(boost::program_options::parse_command_line(argc, argv, description), vm);
+ boost::program_options::notify(vm);
+
+ if(vm.count("help")) {
+ std::cout << description << std::endl;
+ return 0;
+ }
+
+ boost::filesystem::path file_path;
+ if(vm.count("exe")) {
+ std::string exe_file = vm["exe"].as<std::string>();
+
+ if(boost::filesystem::exists(exe_file)) {
+ if(!boost::filesystem::is_directory(exe_file)) {
+ file_path = exe_file;
+ } else {
+ std::cerr << exe_file << " is a folder" << std::endl;
+ std::cerr << std::endl;
+ std::cerr << description << std::endl;
+ return -1;
+ }
+ } else {
+ std::cerr << "file: " << exe_file << " does not exist" << std::endl;
+ std::cerr << std::endl;
+ std::cerr << description << std::endl;
+ return -1;
+ }
+ } else {
+ std::cerr << "Option \"exe_file\" is required";
+ std::cerr << std::endl;
+ std::cerr << description << std::endl;
+ return -1;
+ }
+
+ std::ifstream file(file_path.string());
+ auto x = parse_mz_file(file);
+ std::cout << x << std::endl;
+
+ return 0;
+} \ No newline at end of file