diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 490eac9..c908016 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -2,4 +2,7 @@ file(GLOB unpaker_src "unpaker/*")
add_executable(unpaker ${unpaker_src})
file(GLOB db_extractor_src "db_extractor/*")
-add_executable(db_extractor ${db_extractor_src})
\ No newline at end of file
+add_executable(db_extractor ${db_extractor_src})
+
+file(GLOB obj_extractor_src "obj_extractor/*")
+add_executable(obj_extractor ${obj_extractor_src})
\ No newline at end of file
diff --git a/src/obj_extractor/obj_extractor.cpp b/src/obj_extractor/obj_extractor.cpp
new file mode 100644
index 0000000..e0e24d9
--- /dev/null
+++ b/src/obj_extractor/obj_extractor.cpp
@@ -0,0 +1,59 @@
+/*
+ * AIM obj_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 .
+ */
+
+#include
+#include
+#include
+
+#include "objects.h"
+#include "other.h"
+
+void read_mmo(string fn)
+{
+ FILE *f = fopen(fn.c_str(), "rb");
+ if (!f)
+ return;
+ Objects objects;
+ objects.load(f);
+ MechGroups mgs;
+ mgs.load(f);
+ if (feof(f))
+ {
+ // custom maps?
+ fclose(f);
+ return;
+ }
+ MapGoods mg;
+ mg.load(f);
+ MapMusic mm;
+ mm.load(f);
+ MapSounds ms;
+ ms.load(f);
+ fclose(f);
+}
+
+int main(int argc, char *argv[])
+{
+ if (argc != 2)
+ {
+ cout << "Usage:\n" << argv[0] << " file.mmo" << "\n";
+ return 1;
+ }
+ read_mmo(argv[1]);
+ return 0;
+}
\ No newline at end of file
diff --git a/src/obj_extractor/objects.cpp b/src/obj_extractor/objects.cpp
new file mode 100644
index 0000000..64af13f
--- /dev/null
+++ b/src/obj_extractor/objects.cpp
@@ -0,0 +1,99 @@
+/*
+ * AIM obj_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 .
+ */
+
+#include "objects.h"
+
+Segment *Segment::create_segment(FILE *f)
+{
+ SegmentType segment_type;
+ FREAD(segment_type);
+
+ Segment *segment = 0;
+ switch (segment_type)
+ {
+ case SegmentType::ROAD:
+ segment = new SegmentObjects;
+ break;
+ case SegmentType::BUILDING:
+ segment = new SegmentObjects;
+ break;
+ case SegmentType::SURFACE:
+ segment = new SegmentObjects;
+ break;
+ case SegmentType::STONE:
+ segment = new SegmentObjects;
+ break;
+ case SegmentType::HELPER:
+ segment = new SegmentObjects;
+ break;
+ case SegmentType::SHELL:
+ segment = new SegmentObjects;
+ break;
+ case SegmentType::IMAGE:
+ segment = new SegmentObjects;
+ break;
+ case SegmentType::EXPLOSION:
+ segment = new SegmentObjects;
+ break;
+ case SegmentType::SOUND:
+ segment = new SegmentObjects;
+ break;
+ case SegmentType::MUSIC:
+ segment = new SegmentObjects;
+ break;
+ case SegmentType::ANOMALY:
+ segment = new SegmentObjects;
+ break;
+ case SegmentType::TOWER:
+ segment = new SegmentObjects;
+ break;
+ case SegmentType::BOUNDARY:
+ segment = new SegmentObjects;
+ break;
+ case SegmentType::GOODS:
+ segment = new SegmentObjects;
+ break;
+ case SegmentType::unk1:
+ segment = new SegmentObjects;
+ break;
+ default:
+ assert(false);
+ break;
+ }
+ if (segment)
+ {
+ segment->segment_type = segment_type;
+ FREAD(segment->segment_len);
+ FREAD(segment->n_objects);
+ }
+ return segment;
+}
+
+void Objects::load(FILE *f)
+{
+ FREAD(n_segments);
+
+ for (int s = 0; s < n_segments; s++)
+ {
+ auto seg = Segment::create_segment(f);
+ if (!seg)
+ break;
+ seg->load(f);
+ segments.push_back(seg);
+ }
+}
diff --git a/src/obj_extractor/objects.h b/src/obj_extractor/objects.h
new file mode 100644
index 0000000..1701381
--- /dev/null
+++ b/src/obj_extractor/objects.h
@@ -0,0 +1,185 @@
+/*
+ * AIM obj_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 .
+ */
+
+#pragma once
+
+#include
+#include
+#include
+#include
+#include
+
+#define FREAD(var) fread(&var, 1, sizeof(var), f)
+#define FREAD_N(var, n) fread(&var, 1, n, f)
+
+using namespace std;
+
+enum class SegmentType : uint32_t
+{
+ TEXTURE = 0x1,
+ MODEL,
+ SURFACE,
+ STONE,
+ TREE,
+
+ GLIDER,
+ HELPER,
+ ROAD,
+ WEAPON,
+ CONFIG,
+
+ SHELL, // (AMMO,BULLET)
+ IMAGE,
+ EXPLOSION,
+ EQUIPMENT,
+ ORGANIZATION,
+
+ BUILDING,
+ LAMP,
+ COVERING,
+ SOUND,
+ MUSIC,
+
+ GOODS,
+ ANOMALY,
+ TOWER,
+ BOUNDARY,
+ SOUND_ZONE,
+
+ unk1 = 0x1b,
+};
+
+struct Segment
+{
+ SegmentType segment_type;
+ uint32_t segment_len;
+ uint32_t n_objects;
+
+ virtual ~Segment(){}
+ static Segment *create_segment(FILE *f);
+ virtual void load(FILE *f) = 0;
+};
+
+template
+struct SegmentObjects : public Segment
+{
+ vector objects;
+
+ virtual void load(FILE *f)
+ {
+ for (int i = 0; i < n_objects; i++)
+ {
+ T r;
+ r.load(f);
+ objects.push_back(r);
+ }
+ }
+};
+
+struct Common
+{
+ float unk2[2];
+ uint32_t unk3[2];
+ float unk4[2];
+ uint32_t unk5[2];
+ float unk6[8];
+
+ void load(FILE *f)
+ {
+ FREAD(unk2);
+ FREAD(unk3);
+ FREAD(unk4);
+ FREAD(unk5);
+ FREAD(unk6);
+ }
+};
+
+struct MapObject : public Common
+{
+ char name1[0x20];
+ char name2[0x20];
+
+ void load(FILE *f)
+ {
+ Common::load(f);
+
+ FREAD(name1);
+ FREAD(name2);
+ }
+};
+
+struct MapObjectWithArray : public MapObject
+{
+ uint32_t len;
+ vector unk7;
+
+ void load(FILE *f)
+ {
+ MapObject::load(f);
+
+ FREAD(len);
+ unk7.resize(len);
+ for (int i = 0; i < len; i++)
+ FREAD(unk7[i]);
+ }
+};
+
+struct Sound : public Common
+{
+ uint32_t unk1[11];
+ char name1[0x14];
+
+ void load(FILE *f)
+ {
+ Common::load(f);
+
+ FREAD(unk1);
+ FREAD(name1);
+ }
+};
+
+struct Road : public MapObjectWithArray {};
+struct Tower : public MapObjectWithArray {};
+
+#define KNOWN_OBJECT(name) \
+ struct name : public MapObject {}
+
+KNOWN_OBJECT(Surface);
+KNOWN_OBJECT(Helper);
+KNOWN_OBJECT(Shell);
+KNOWN_OBJECT(Stone);
+KNOWN_OBJECT(Explosion);
+KNOWN_OBJECT(Image);
+KNOWN_OBJECT(Music);
+KNOWN_OBJECT(Anomaly);
+KNOWN_OBJECT(Boundary);
+
+#define UNKNOWN_OBJECT(name) \
+ struct name : public MapObject { void load(FILE *f){ int pos = ftell(f); assert(false); } }
+
+UNKNOWN_OBJECT(Building);
+UNKNOWN_OBJECT(Goods);
+UNKNOWN_OBJECT(unk1);
+
+struct Objects
+{
+ uint32_t n_segments;
+ vector segments;
+
+ void load(FILE *f);
+};
diff --git a/src/obj_extractor/other.h b/src/obj_extractor/other.h
new file mode 100644
index 0000000..200f55f
--- /dev/null
+++ b/src/obj_extractor/other.h
@@ -0,0 +1,232 @@
+/*
+ * AIM obj_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 .
+ */
+
+#pragma once
+
+#include
+#include
+#include
+#include
+#include
+
+using namespace std;
+
+struct MechGroup
+{
+ char unk1[0x20];
+ char unk2[0x20];
+ uint32_t type1;
+ uint32_t len1;
+ char name1[0x70];
+ //{3,4
+ uint32_t unk30;
+ //}
+ //{2
+ uint32_t len;
+ vector unk11;
+ //}
+ //{1,0
+ uint32_t unk20;
+ uint32_t unk21;
+ //}
+ vector configs;
+ char unk100;
+
+ void load(FILE *f)
+ {
+ FREAD(unk1);
+ FREAD(unk2);
+ FREAD(type1);
+ FREAD(len1);
+ FREAD(name1);
+ if (type1 == 3 || type1 == 4)
+ {
+ FREAD(unk30);
+ }
+ else if (type1 == 2)
+ {
+ FREAD(len);
+ unk11.resize(len);
+ for (int i = 0; i < len; i++)
+ FREAD(unk11[i]);
+ }
+ else if (type1 == 1 || type1 == 0)
+ {
+ FREAD(unk20);
+ FREAD(unk21);
+ }
+ else
+ assert(false);
+ configs.resize(len1, string(0x20, 0));
+ for (int i = 0; i < len1; i++)
+ FREAD_N(configs[i][0], 0x20);
+ FREAD(unk100);
+ }
+};
+
+struct MechGroups
+{
+ uint32_t n;
+ char prefix[0x30];
+
+ vector mgs;
+
+ void load(FILE *f)
+ {
+ FREAD(n);
+ FREAD(prefix);
+
+ for (int s = 0; s < n; s++)
+ {
+ MechGroup mg;
+ mg.load(f);
+ mgs.push_back(mg);
+ }
+ }
+};
+
+struct Good
+{
+ char name[0x20];
+ char unk1[0x40];
+ float price;
+ float unk2[10];
+
+ void load(FILE *f)
+ {
+ FREAD(name);
+ FREAD(unk1);
+ FREAD(price);
+ FREAD(unk2);
+ }
+};
+
+struct BuildingGoods
+{
+ char name[0x20];
+ uint32_t n;
+
+ vector goods;
+
+ void load(FILE *f)
+ {
+ FREAD(name);
+ FREAD(n);
+
+ for (int i = 0; i < n; i++)
+ {
+ Good g;
+ g.load(f);
+ goods.push_back(g);
+ }
+ }
+};
+
+struct MapGoods
+{
+ uint32_t unk1;
+ uint32_t unk2;
+ uint32_t unk3;
+ uint32_t n;
+
+ vector bgs;
+
+ void load(FILE *f)
+ {
+ FREAD(unk1);
+ FREAD(unk2);
+ FREAD(unk3);
+ FREAD(n);
+
+ for (int i = 0; i < n; i++)
+ {
+ BuildingGoods bg;
+ bg.load(f);
+ bgs.push_back(bg);
+ }
+ }
+};
+
+struct MapMusic
+{
+ uint32_t unk1;
+ char name1[0x20];
+ char name2[0x20];
+
+ uint32_t n1;
+ vector names1;
+
+ uint32_t n2;
+ vector names2;
+
+ void load(FILE *f)
+ {
+ FREAD(unk1);
+ FREAD(name1);
+ FREAD(name2);
+
+ FREAD(n1);
+ for (int i = 0; i < n1; i++)
+ {
+ char name[0x20];
+ FREAD(name);
+ names1.push_back(name);
+ }
+
+ FREAD(n2);
+ for (int i = 0; i < n2; i++)
+ {
+ char name[0x20];
+ FREAD(name);
+ names2.push_back(name);
+ }
+ }
+};
+
+struct MapSound
+{
+ char name[0x20];
+ float unk1[4];
+ uint32_t unk2;
+ float unk3[4];
+
+ void load(FILE *f)
+ {
+ FREAD(name);
+ FREAD(unk1);
+ FREAD(unk2);
+ FREAD(unk3);
+ }
+};
+
+struct MapSounds
+{
+ uint32_t n;
+ vector sounds;
+
+ void load(FILE *f)
+ {
+ FREAD(n);
+ for (int i = 0; i < n; i++)
+ {
+ MapSound s;
+ s.load(f);
+ sounds.push_back(s);
+ }
+ }
+};
\ No newline at end of file