#!/usr/bin/env python3

#
# MapInfo - Extract scripts of Wild Arms maps
#
# Copyright (C) Christian Bauer <www.cebix.net>
#
# Permission to use, copy, modify, and/or distribute this software for any
# purpose with or without fee is hereby granted, provided that the above
# copyright notice and this permission notice appear in all copies.
#

__version__ = "1.2"

import sys
import os
import struct
import shutil

sys.stdout.reconfigure(encoding = "locale", errors = "backslashreplace")
sys.stderr.reconfigure(encoding = "locale", errors = "backslashreplace")

import wa
from wa.map import Op


# Print usage information and exit.
def usage(exitcode, error = None):
    print("Usage: %s [OPTION...] <game_dir_or_image> <output_dir>" % os.path.basename(sys.argv[0]))
    print("  -V, --version                   Display version information and exit")
    print("  -?, --help                      Show this help message")

    if error is not None:
        print("\nError:", error, file=sys.stderr)

    sys.exit(exitcode)


# Parse command line arguments
gamePath = None
outputDir = None

for arg in sys.argv[1:]:
    if arg == "--version" or arg == "-V":
        print("MapInfo", __version__)
        sys.exit(0)
    elif arg == "--help" or arg == "-?":
        usage(0)
    elif arg[0] == "-":
        usage(64, "Invalid option '%s'" % arg)
    else:
        if gamePath is None:
            gamePath = arg
        elif outputDir is None:
            outputDir = arg
        else:
            usage(64, "Unexpected extra argument '%s'" % arg)

if gamePath is None:
    usage(64, "No disc image or game data input directory specified")
if outputDir is None:
    usage(64, "No output directory specified")

try:

    # Open the input image
    image = wa.openImage(gamePath)

    # Create the output directory
    if os.path.isfile(outputDir):
        print("Cannot create output directory '%s': Path refers to a file" % outputDir, file=sys.stderr)
        sys.exit(1)

    if os.path.isdir(outputDir):
        answer = None
        while answer not in ["y", "n"]:
            answer = input("Output directory '%s' exists. Delete and overwrite it (y/n)? " % outputDir)

        if answer == 'y':
            shutil.rmtree(outputDir)
        else:
            sys.exit(0)

    try:
        os.makedirs(outputDir)
    except OSError as e:
        print("Cannot create output directory '%s': %s" % (outputDir, e.strerror), file=sys.stderr)
        sys.exit(1)

    # Retrieve the map file and game executable
    mapFile = image.openFile("BIN", "CDSTG.BIN")

    exeFile = image.openFile("EXE", "WILDARMS.EXE")
    exeData = exeFile.read()

    nameTableOffset = wa.data.mapNameTableOffset(image.version)

    # Process all maps
    for mapNumber in range(128):

        # Read the map data block
        block = mapFile.read(0x91000)

        # Map 25 is dummied out
        if mapNumber == 25:
            continue

        print("  map", mapNumber)

        # Create the output file
        filePath = os.path.join(outputDir, "map_%03d.txt" % mapNumber)
        try:
            f = open(filePath, "w", encoding = "utf-8")
        except IOError as e:
            print("Cannot create file '%s': %s" % (filePath, e.strerror), file=sys.stderr)
            sys.exit(1)

        # Find the map name
        p = struct.unpack_from("<L", exeData, nameTableOffset + mapNumber * 4)[0]
        o = p - 0x80011420 + 0x800
        mapName = wa.text.decode(exeData[o:exeData.index(b'\0', o)], image.version)

        print("##", file=f)
        print("## Map number %d (%s)" % (mapNumber, mapName), file=f)
        print("##", file=f)
        print(file=f)

        # Extract the scripts
        mapData = wa.map.MapData(block, mapNumber, image.version)

        entries = []
        entries.append(mapData.getGlobalEntries())
        entries.append(mapData.getScript1Entries())
        entries.append(mapData.getScript2Entries())

        script1 = mapData.getScript1()
        script2 = mapData.getScript2()

        for scriptName, script in [("Script 1", script1), ("Script 2", script2)]:
            print("#", file=f)
            print("#", scriptName, file=f)
            print("#", file=f)
            print(file=f)

            prevLineEmpty = False

            for instr in script:
                haveEntry = False

                for n in range(len(entries)):
                    for idx in [i for i, addr in enumerate(entries[n]) if addr == instr.addr]:
                        if not haveEntry and not prevLineEmpty:
                            print(file=f)

                        print("Entry %d.%d:" % (n, idx), file=f)
                        haveEntry = True
                        prevLineEmpty = False

                if haveEntry:
                    print(file=f)

                print("%04x:" % instr.addr, instr.disass, file=f)

                if instr.op in [Op.RETURN, Op.PTR]:
                    print(file=f)
                    prevLineEmpty = True

except Exception as e:

    # Pokemon exception handler
    print(e, file=sys.stderr)
    sys.exit(1)
