Extract all information from MMP files.

This commit is contained in:
lzwdgc 2015-08-05 18:04:39 +03:00
parent 93850b0864
commit 7f214ed2ec
16 changed files with 445 additions and 87 deletions

View file

@ -18,7 +18,7 @@ if (MSVC)
#set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} /MTd")
#set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} /MT")
else()
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} --std=c++11")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} --std=c++14")
endif(MSVC)
if (NOT DATABASE_MANAGER_DIR)

View file

@ -9,15 +9,20 @@ add_executable(db_extractor ${db_extractor_src})
target_link_libraries(db_extractor common)
endif()
file(GLOB obj_extractor_src "obj_extractor/*")
add_executable(obj_extractor ${obj_extractor_src})
target_link_libraries(obj_extractor DatabaseManager common)
file(GLOB script2txt_src "script2txt/*")
add_executable(script2txt ${script2txt_src})
file(GLOB mmm_extractor_src "mmm_extractor/*")
add_executable(mmm_extractor ${mmm_extractor_src})
target_link_libraries(mmm_extractor DatabaseManager common)
file(GLOB mmo_extractor_src "mmo_extractor/*")
add_executable(mmo_extractor ${mmo_extractor_src})
target_link_libraries(mmo_extractor DatabaseManager common)
file(GLOB mmp_extractor_src "mmp_extractor/*")
add_executable(mmp_extractor ${mmp_extractor_src})
target_link_libraries(mmp_extractor common)
file(GLOB mod_converter_src "mod_converter/*")
add_executable(mod_converter ${mod_converter_src})

29
src/mmm_extractor/mmm.cpp Normal file
View file

@ -0,0 +1,29 @@
/*
* AIM mmm_extractor
* Copyright (C) 2015 lzwdgc
*
* 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 3 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, see <http://www.gnu.org/licenses/>.
*/
#include "mmm.h"
void mmm::load(buffer &b)
{
READ(b, unk1);
READ(b, unk2);
READ(b, width);
READ(b, height);
data.resize(width * height / 16);
READ_N(b, data[0], data.size() * 16);
}

39
src/mmm_extractor/mmm.h Normal file
View file

@ -0,0 +1,39 @@
/*
* AIM mmm_extractor
* Copyright (C) 2015 lzwdgc
*
* 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 3 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, see <http://www.gnu.org/licenses/>.
*/
#include <stdint.h>
#include <string>
#include <vector>
#include <common.h>
struct dxt5_record
{
char unk[0x10];
};
struct mmm
{
uint32_t unk1;
uint32_t unk2;
uint32_t width;
uint32_t height;
std::vector<dxt5_record> data;
void load(buffer &b);
};

View file

@ -0,0 +1,67 @@
/*
* AIM mmm_extractor
* Copyright (C) 2015 lzwdgc
*
* 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 3 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, see <http://www.gnu.org/licenses/>.
*/
#include <iostream>
#include <stdint.h>
#include <string>
#include <sstream>
#include "mmm.h"
using namespace std;
mmm read_mmm(string fn)
{
buffer b(readFile(fn));
mmm m;
m.load(b);
if (!b.eof())
{
stringstream ss;
ss << hex << b.index() << " != " << hex << b.size();
throw std::logic_error(ss.str());
}
return m;
}
int main(int argc, char *argv[])
try
{
#ifdef NDEBUG
if (argc != 2)
{
cout << "Usage:\n" << argv[0] << " file.mmp" << "\n";
return 1;
}
read_mmm(argv[1]);
#else
auto loc1 = read_mmm("h:\\Games\\AIM\\data\\minimaps.pak.dir\\location1.mmm");
#endif
return 0;
}
catch (std::exception &e)
{
printf("error: %s\n", e.what());
return 1;
}
catch (...)
{
printf("error: unknown exception\n");
return 1;
}

View file

@ -1,2 +1,2 @@
python obj_extractor.py --db "h:\Games\Epic Games\Projects\Polygon4\Mods\db.sqlite" --dir "h:\Games\AIM\data\maps.pak.dir" --prefix m1
python obj_extractor.py --db "h:\Games\Epic Games\Projects\Polygon4\Mods\db.sqlite" --dir "h:\Games\Steam\steamapps\common\AIM2\Data\mmo.pak\DATA\LOCS" --prefix m2
python mmo_extractor.py --db "h:\Games\Epic Games\Projects\Polygon4\Mods\db.sqlite" --dir "h:\Games\AIM\data\maps.pak.dir" --prefix m1
python mmo_extractor.py --db "h:\Games\Epic Games\Projects\Polygon4\Mods\db.sqlite" --dir "h:\Games\Steam\steamapps\common\AIM2\Data\mmo.pak\DATA\LOCS" --prefix m2

View file

@ -23,7 +23,7 @@ def run(dir, db, prefix):
if os.path.isdir(file) or os.path.splitext(file)[1].lower() != ".mmo":
continue
print('loading: ' + file)
p = subprocess.Popen(['obj_extractor.exe', db, dir + '/' + file, prefix])
p = subprocess.Popen(['mmo_extractor.exe', db, dir + '/' + file, prefix])
p.communicate()
if __name__ == '__main__':

View file

@ -18,40 +18,114 @@
#include "mmp.h"
#define FREAD(var) fread(&var, 1, sizeof(var), f)
#define FREAD_N(var, n) fread(&var, 1, n, f)
size_t file_size(FILE *f)
void unk_segment1::load(buffer &b)
{
auto old = ftell(f);
fseek(f, 0, SEEK_END);
auto sz = ftell(f);
fseek(f, old, SEEK_SET);
return sz;
while (!b.eof())
{
unk_segment1_1 s;
s.load(b);
segments.push_back(s);
}
}
void mmp::load(FILE *f)
void unk_segment1_1::load(buffer &b)
{
FREAD(unk1);
FREAD(unk2);
FREAD(width);
FREAD(height);
FREAD(unk3);
FREAD(unk4);
FREAD(unk5);
FREAD(unk6);
FREAD(unk7);
FREAD(unk8);
READ(b, unk0);
READ(b, name1);
READ(b, unk1);
READ(b, unk2);
READ(b, unk3);
READ(b, unk4);
READ(b, name2);
READ(b, unk5);
}
int n_seg = width / 64 * height / 64;
int sz = n_seg * sizeof(segment);
int fsz = file_size(f);
int off = fsz - sz;
fseek(f, off, SEEK_SET);
void unk_segment2::load(buffer &b)
{
READ(b, n_segs);
segments.resize(n_segs);
READ(b, name);
for (auto &s : segments)
s.load(b);
}
segments = vector<segment>(n_seg);
for (int i = 0; i < n_seg; i++)
FREAD(segments[i]);
void unk_segment2_1::load(buffer &b)
{
READ(b, name1);
READ(b, name2);
READ(b, name3);
READ(b, name4);
READ(b, name5);
READ(b, unk0);
READ(b, unk1);
READ(b, tex_name0);
READ(b, unk2);
READ(b, unk_name0);
READ(b, unk3);
READ(b, tex_name1);
READ(b, tex_name2);
READ(b, unk4);
}
assert(ftell(f) == fsz);
header_segment *header::create_segment(buffer &b)
{
HeaderSegmentType type;
READ(b, type);
header_segment *segment = 0;
switch (type)
{
case HeaderSegmentType::unk1:
segment = new unk_segment1;
break;
case HeaderSegmentType::unk2:
segment = new unk_segment2;
break;
default:
throw std::logic_error("unknown header segment type " + std::to_string((int)type));
break;
}
if (segment)
{
segment->type = type;
READ(b, segment->unk0);
READ(b, segment->len);
}
return segment;
}
void header::load(buffer &b)
{
READ(b, unk0);
READ(b, name1);
READ(b, name2);
READ(b, width);
READ(b, height);
READ(b, n_segs);
segments.resize(n_segs);
READ(b, name);
for (auto &s : segments)
{
s = create_segment(b);
buffer b2(b, s->len);
if (!b2.eof())
s->load(b2);
}
}
void segment::load(buffer &b)
{
READ(b, desc);
buffer b2(b);
b2.seek(desc.offset);
READ(b2, d);
}
void mmp::load(buffer &b)
{
h.load(b);
int n_segs = h.width / 64 * h.height / 64;
segments.resize(n_segs);
for (auto &s : segments)
s.load(b);
}

View file

@ -16,45 +16,158 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <assert.h>
#include <iostream>
#include <map>
#include <stdint.h>
#include <stdio.h>
#include <string>
#include <vector>
using namespace std;
#include <common.h>
enum class HeaderSegmentType : uint32_t
{
unk1 = 1,
unk2 = 2,
};
struct header_segment
{
HeaderSegmentType type;
uint32_t unk0;
uint32_t len;
virtual void load(buffer &b) = 0;
};
struct unk_segment1_1
{
float unk0[6];
char name1[0x20];
uint32_t unk1;
float unk2;
uint32_t unk3[16];
float unk4;
char name2[0x20];
uint32_t unk5[16];
void load(buffer &b);
};
struct unk_segment1 : public header_segment
{
std::vector<unk_segment1_1> segments;
virtual void load(buffer &b) override;
};
struct unk_segment2_1
{
char name1[0x20];
char name2[0x20];
char name3[0x20];
char name4[0x20];
char name5[0x20];
float unk0[2];
uint32_t unk1[6];
char tex_name0[0x20];
uint32_t unk2[3];
char unk_name0[0x20];
float unk3;
char tex_name1[0x20];
char tex_name2[0x20];
uint32_t unk4[23];
void load(buffer &b);
};
struct unk_segment2 : public header_segment
{
uint32_t n_segs;
char name[0xA0];
std::vector<unk_segment2_1> segments;
virtual void load(buffer &b) override;
};
struct header
{
uint32_t unk0;
wchar_t name1[0x20];
wchar_t name2[0x20];
uint32_t width;
uint32_t height;
uint32_t n_segs;
char name[0xA0];
std::vector<header_segment*> segments;
void load(buffer &b);
private:
header_segment *create_segment(buffer &b);
};
struct segment
{
struct description
{
uint32_t offset;
float Xmin;
float Ymin;
float Zmin;
float Xmax;
float Ymax;
float Zmax;
float unk0[5];
uint32_t unk1[7];
};
struct data
{
struct info
{
uint16_t render_flags;
uint16_t texture_index;
};
struct color
{
uint8_t b;
uint8_t g;
uint8_t r;
uint8_t a;
};
struct shadow
{
uint8_t unk[4];
};
struct normal
{
int16_t x;
int16_t y;
};
struct old_data
{
uint16_t Heightmap[1089 * 2];
color Colormap[1089];
uint32_t unk0[1089];
normal unk1[1089]; // normals?
};
uint32_t MagicNumber;
old_data old;
float Heightmap[4225];
info Infomap[4225];
color Colormap[4225];
shadow Shadowmap[4225];
normal Normalmap[4225];
};
description desc;
data d;
void load(buffer &b);
};
struct mmp
{
struct segment
{
uint32_t MagicNumber;
uint32_t MiniLayer1[1089];
uint32_t MiniLayer2[1089];
uint32_t MiniLayer3[1089];
uint32_t MiniLayer4[1089];
float Heightmap[4225];
uint32_t Infomap[4225];
uint32_t Colormap[4225];
uint32_t Shadowmap[4225];
uint32_t Normalmap[4225];
};
header h;
std::vector<segment> segments;
uint32_t unk1;
char unk2[0x80];
uint32_t width;
uint32_t height;
uint32_t unk3;
char unk4[0xA0];
uint32_t unk5[7];
char unk6[0xA0];
char unk7[0x1AC];
char unk8[0x1AC];
uint32_t offset; // the beginning of segments
vector<segment> segments;
void load(FILE *f);
void load(buffer &b);
};

View file

@ -0,0 +1,2 @@
python mmp_extractor.py --dir "h:\Games\AIM\data\maps.pak.dir"
python mmp_extractor.py --dir "h:\Games\Steam\steamapps\common\AIM2\Data\mmp.pak\DATA\LOCS"

View file

@ -16,14 +16,10 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <algorithm>
#include <fstream>
#include <iostream>
#include <map>
#include <set>
#include <stdio.h>
#include <stdint.h>
#include <string>
#include <sstream>
#include "mmp.h"
@ -31,27 +27,35 @@ using namespace std;
mmp read_mmp(string fn)
{
buffer b(readFile(fn));
mmp m;
FILE *f = fopen(fn.c_str(), "rb");
if (!f)
return m;
m.load(f);
fclose(f);
m.load(b);
return m;
}
int main(int argc, char *argv[])
void process_mmp(mmp &m, string fn)
{
}
int main(int argc, char *argv[])
try
{
#ifdef NDEBUG
if (argc != 2)
{
cout << "Usage:\n" << argv[0] << " file.mmp" << "\n";
return 1;
}
read_mmp(argv[1]);
#else
auto arena = read_mmp("h:\\Games\\AIM\\data\\maps.pak.dir\\arena.mmp");
auto loc1 = read_mmp("h:\\Games\\AIM\\data\\maps.pak.dir\\location1.mmp");
#endif
auto m = read_mmp(argv[1]);
process_mmp(m, argv[1]);
return 0;
}
catch (std::exception &e)
{
printf("error: %s\n", e.what());
return 1;
}
catch (...)
{
printf("error: unknown exception\n");
return 1;
}

View file

@ -0,0 +1,25 @@
#!/usr/bin/python3
# -*- coding: utf-8 -*-
import argparse
import os
import subprocess
def main():
parser = argparse.ArgumentParser(description='Batch MMP extractor')
parser.add_argument('--dir', dest='dir', help='path to directory with maps')
pargs = parser.parse_args()
if pargs.dir:
run(pargs.dir)
def run(dir):
for file in sorted(os.listdir(dir)):
if os.path.isdir(file) or os.path.splitext(file)[1].lower() != ".mmp":
continue
print('processing: ' + file)
p = subprocess.Popen(['mmp_extractor.exe', dir + '/' + file])
p.communicate()
if __name__ == '__main__':
main()