I assumed that there was a lower granularity as Arklon mentioned. I don't think looking at it as individual tiles will work. That's the general feel that I've gotten.
Your mention of the seam issues confirms what I thought I'd noticed in a variety of maps. It tells me that the Dynamix developers went with a "this is good enough" approach. Perhaps we can do the same? It ultimately doesn't need to be perfect, just usable.
I'm also thinking that the way the tiles are set up, an algorithm won't be terribly effective. I imagined a lookup table of sorts... which meshes well with your suggestion of meta data. This is where the ruleset comes into play and we may need to actually manually set up the first set by actually looking at the individual tile indicies and then matching them up based on surrounding tiles. It'll be a bit of a chore but may be the only way to do it effectively.
I use a modified version of the OP2Editor code -- basically I stripped out the COM and rewrote the input/output using byte streams in-memory. This was before the work you and Hooman did on the OP2Editor backend so it might be better to dump what I did and replace it with yours.
I would like to see your editor's backend work massaged into OP2Utility so we can unify the backend code between the mapper, OP2MapImager, and OP2Archive in one reusable place. Dumping is probably the wrong word as you may have functions that do not yet exist in OP2Utility that would need to be transferred over. I would be willing to help on this endeavor though it is out of scope for this thread.
Can browse the code in GitHub here: https://github.com/OutpostUniverse/op2-landlord
Relevant files include the MapFile (header (https://github.com/OutpostUniverse/op2-landlord/blob/master/src/Map/MapFile.h), source (https://github.com/OutpostUniverse/op2-landlord/blob/master/src/Map/MapFile.cpp)), TileGroup (header (https://github.com/OutpostUniverse/op2-landlord/blob/master/src/Map/TileGroup.h), source (https://github.com/OutpostUniverse/op2-landlord/blob/master/src/Map/TileGroup.cpp)), TileSetManager (header (https://github.com/OutpostUniverse/op2-landlord/blob/master/src/Map/TileSetManager.h), source (https://github.com/OutpostUniverse/op2-landlord/blob/master/src/Map/TileSetManager.cpp)) and the StreamReader/Writer classes (source directory (https://github.com/OutpostUniverse/op2-landlord/tree/master/src/Stream)). Note that StreamWriter is currently just an interface with no actual implementation (I got lazy, don't judge me).
The most interesting (I think) code is in the MapFile class: MapFile::load
NOTE: slight modifications to get the code syntax highlighter working without barfing
void MapFile::load(const std::string& filename)
{
int temp = 0;
try
{
File _f = Utility<Filesystem>::get().open(filename);
File::RawByteStream stream = _f.raw_bytes();
StreamReader stream_reader(stream);
stream_reader.read(&mMapHeadInfo, sizeof(mMapHeadInfo));
/** Update map header fields */
mMapHeadInfo.tileHeight = RoundUpPowerOf2(mMapHeadInfo.tileHeight);
mTileWidth = 1 << mMapHeadInfo.lgTileWidth; /** Calculate map width */
mTileHeight = mMapHeadInfo.tileHeight;
int cellCount = mTileWidth * mTileHeight;
mTileData = new int[cellCount];
/** Read in the tile data */
stream_reader.read(mTileData, cellCount * MAP_CHUNK_SIZE);
stream_reader.read(&mClipRect, sizeof(mClipRect));
mTilesetManager = new TileSetManager(mMapHeadInfo.numTileSets, &stream_reader);
readTag(&stream_reader, mMapHeadInfo.tag);
/** Something about units between reads of the tag. */
readTag(&stream_reader, mMapHeadInfo.tag);
/** Load tile groups from file */
int group_count = 0;
stream_reader.read(&group_count, MAP_CHUNK_SIZE);
stream_reader.read(&temp, MAP_CHUNK_SIZE);
for (int i = 0; i < group_count; i++)
{
int width = 0, height = 0;
stream_reader.read(&width, MAP_CHUNK_SIZE);
stream_reader.read(&height, MAP_CHUNK_SIZE);
/** TileGroupInfo _tgi; */
TileGroup* _tg = new TileGroup(width, height, mTilesetManager);
/** Read in the tile data */
for (int y = 0; y < height; y++)
{
for (int x = 0; x < width; x++)
{
stream_reader.read(&temp, MAP_CHUNK_SIZE);
_tg->index(x, y, temp);
}
}
_readTileGroupName(stream_reader, *_tg);
}
}
catch (const std::string& errorMsg)
{
std::cout << errorMsg << std::endl;
}
catch (...)
{}
}
can be found in Common.cpp (https://github.com/OutpostUniverse/op2-landlord/blob/master/src/Common.cpp) -- it's just a simple helper function.
Much of this is unchanged from the original implementation but with changes regarding the use of the StreamReader class which was modified to remove COM and use a 'raw byte stream' (basically just an unsigned char* buffer implemented as a std::string for memory management).
So basically, to sum up an overly verbose post, I removed the non-portable Windows stuff from the code and reimplemented some functions through a virtual fileystem (via NAS2D) and changed the drawing code to also take advantage of NAS2D. The filesystem stuff can be replaced with calls to C++11/C++17 filesystem calls instead (which is probably much more portable). The raw byte stream is basically just an unsigned char* buffer.