[
  {
    "path": ".editorconfig",
    "content": "root = true\n\n# Code\n[*.cs]\nindent_size = 4\nindent_style = space\ncsharp_new_line_before_open_brace = all:warning\n"
  },
  {
    "path": ".gitattributes",
    "content": "# Auto detect text files and perform LF normalization\n* text=auto\n\n# Custom for Visual Studio\n*.cs     diff=csharp\n*.sln    merge=union\n*.csproj merge=union\n*.vbproj merge=union\n*.fsproj merge=union\n*.dbproj merge=union\n\n# Standard to msysgit\n*.doc\t diff=astextplain\n*.DOC\t diff=astextplain\n*.docx diff=astextplain\n*.DOCX diff=astextplain\n*.dot  diff=astextplain\n*.DOT  diff=astextplain\n*.pdf  diff=astextplain\n*.PDF\t diff=astextplain\n*.rtf\t diff=astextplain\n*.RTF\t diff=astextplain\n"
  },
  {
    "path": ".github/RELEASE.md",
    "content": "This is an automated SaintCoinach release based on recent changes.\n\nPlease note that your Anti-Virus program may detect SaintCoinach as a false positive.\n"
  },
  {
    "path": ".github/workflows/build.yml",
    "content": "name: Build SaintCoinach\non: [push, pull_request]\nconcurrency: build-${{ github.ref }}\n\njobs:\n  build:\n    name: Build on Windows\n    runs-on: windows-2025\n    steps:\n      - uses: actions/checkout@v2\n      - name: Setup .NET\n        uses: actions/setup-dotnet@v1\n        with:\n          dotnet-version: '7.x.x'\n      - name: Define Version\n        id: define-version\n        run: |\n          $env:COMMIT = $env:GITHUB_SHA.Substring(0, 7)\n          echo \"::set-output name=commit::$env:COMMIT\"\n      - name: Restore dependencies\n        run: dotnet restore\n      - name: Build\n        run: dotnet build -c Release\n      - name: Create Release ZIPs\n        if: ${{ github.ref == 'refs/heads/master' }}\n        run: |\n          mkdir release-out\n          Compress-Archive -Path .\\Godbert\\bin\\Release\\net7.0-windows\\* -DestinationPath .\\release-out\\Godbert.zip -Force\n          Compress-Archive -Path .\\SaintCoinach.Cmd\\bin\\Release\\net7.0\\* -DestinationPath .\\release-out\\SaintCoinach.Cmd.zip -Force\n\n      - name: Create Release\n        uses: softprops/action-gh-release@v1\n        if: ${{ github.ref == 'refs/heads/master' }}\n        env:\n          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}\n        with:\n          files: release-out/**\n          name: Release for ${{ steps.define-version.outputs.commit }}\n          body_path:  .github\\RELEASE.md\n          prerelease: false\n          tag_name:  ${{ steps.define-version.outputs.commit }}\n"
  },
  {
    "path": ".github/workflows/json-validation.yaml",
    "content": "name: Validate ex JSON definitions\n# This workflow is triggered on pushes to the repository.\non: [push, pull_request]\n\njobs:\n  build:\n    name: validate-json\n    runs-on: ubuntu-latest\n    steps:\n      - uses: actions/checkout@v1\n      - name: Install jsonlint\n        run: sudo npm install jsonlint -g\n      - name: Validate schemas\n        run: set -e; for f in $(find SaintCoinach/Definitions/ -name *.json -print); do echo -n \"$f - \"; jsonlint $f -q; echo \"OK!\"; done\n"
  },
  {
    "path": ".gitignore",
    "content": "## Ignore Visual Studio temporary files, build results, and\n## files generated by popular Visual Studio add-ons.\n\n# User-specific files\n*.suo\n*.user\n*.sln.docstates\n\n# Build results\n[Dd]ebug/\n[Dd]ebugPublic/\n[Rr]elease/\n[Rr]eleases/\nx64/\nx86/\nbuild/\nbld/\n[Bb]in/\n[Oo]bj/\n\n# Roslyn cache directories\n*.ide/\n\n# MSTest test Results\n[Tt]est[Rr]esult*/\n[Bb]uild[Ll]og.*\n\n#NUNIT\n*.VisualState.xml\nTestResult.xml\n\n# Build Results of an ATL Project\n[Dd]ebugPS/\n[Rr]eleasePS/\ndlldata.c\n\n*_i.c\n*_p.c\n*_i.h\n*.ilk\n*.meta\n*.obj\n*.pch\n*.pdb\n*.pgc\n*.pgd\n*.rsp\n*.sbr\n*.tlb\n*.tli\n*.tlh\n*.tmp\n*.tmp_proj\n*.log\n*.vspscc\n*.vssscc\n.builds\n*.pidb\n*.svclog\n*.scc\n\n# Chutzpah Test files\n_Chutzpah*\n\n# Visual C++ cache files\nipch/\n*.aps\n*.ncb\n*.opensdf\n*.sdf\n*.cachefile\n.vs/\n\n# Visual Studio profiler\n*.psess\n*.vsp\n*.vspx\n\n# TFS 2012 Local Workspace\n$tf/\n\n# Guidance Automation Toolkit\n*.gpState\n\n# ReSharper is a .NET coding add-in\n_ReSharper*/\n*.[Rr]e[Ss]harper\n*.DotSettings.user\n\n# JustCode is a .NET coding addin-in\n.JustCode\n\n# TeamCity is a build add-in\n_TeamCity*\n\n# DotCover is a Code Coverage Tool\n*.dotCover\n\n# NCrunch\n_NCrunch_*\n.*crunch*.local.xml\n\n# MightyMoose\n*.mm.*\nAutoTest.Net/\n\n# Web workbench (sass)\n.sass-cache/\n\n# Installshield output folder\n[Ee]xpress/\n\n# DocProject is a documentation generator add-in\nDocProject/buildhelp/\nDocProject/Help/*.HxT\nDocProject/Help/*.HxC\nDocProject/Help/*.hhc\nDocProject/Help/*.hhk\nDocProject/Help/*.hhp\nDocProject/Help/Html2\nDocProject/Help/html\n\n# Click-Once directory\npublish/\n\n# Publish Web Output\n*.[Pp]ublish.xml\n*.azurePubxml\n# TODO: Comment the next line if you want to checkin your web deploy settings \n# but database connection strings (with potential passwords) will be unencrypted\n*.pubxml\n*.publishproj\n\n# NuGet Packages\n*.nupkg\n# The packages folder can be ignored because of Package Restore\n**/packages/*\n# except build/, which is used as an MSBuild target.\n!**/packages/build/\n# If using the old MSBuild-Integrated Package Restore, uncomment this:\n#!**/packages/repositories.config\n\n# Windows Azure Build Output\ncsx/\n*.build.csdef\n\n# Windows Store app package directory\nAppPackages/\n\n# Others\nsql/\n*.Cache\nClientBin/\n[Ss]tyle[Cc]op.*\n~$*\n*~\n*.dbmdl\n*.dbproj.schemaview\n*.pfx\n*.publishsettings\nnode_modules/\n\n# RIA/Silverlight projects\nGenerated_Code/\n\n# Backup & report files from converting an old project file\n# to a newer Visual Studio version. Backup files are not needed,\n# because we have git ;-)\n_UpgradeReport_Files/\nBackup*/\nUpgradeLog*.XML\nUpgradeLog*.htm\n\n# SQL Server files\n*.mdf\n*.ldf\n\n# Business Intelligence projects\n*.rdl.data\n*.bim.layout\n*.bim_*.settings\n\n# Microsoft Fakes\nFakesAssemblies/\n\n# =========================\n# Operating System Files\n# =========================\n\n# OSX\n# =========================\n\n.DS_Store\n.AppleDouble\n.LSOverride\n\n# Icon must end with two \\r\nIcon\n\n\n# Thumbnails\n._*\n\n# Files that might appear on external disk\n.Spotlight-V100\n.Trashes\n\n# Directories potentially created on remote AFP share\n.AppleDB\n.AppleDesktop\nNetwork Trash Folder\nTemporary Items\n.apdisk\n\n# Windows\n# =========================\n\n# Windows image file caches\nThumbs.db\nehthumbs.db\n\n# Folder config file\nDesktop.ini\n\n# Recycle Bin used on file shares\n$RECYCLE.BIN/\n\n# Windows Installer files\n*.cab\n*.msi\n*.msm\n*.msp\n\n# DX Things\n*.tkb\n*.StyleCop\n\n# JetBrains\n.idea\nSaintCoinach.Cmd/Properties/launchSettings.json\n"
  },
  {
    "path": "DotSquish/Alpha.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace DotSquish {\n    internal static class Alpha {\n        #region DXT3\n        private static int FloatToInt(float a, int limit) {\n            // Use ANSI round-to-zero behaviour to get round-to-nearest.\n            var i = (int)(a + .5f);\n\n            if (i < 0)\n                i = 0;\n            else if (i > limit)\n                i = limit;\n\n            return i;\n        }\n        public static void CompressAlphaDxt3(byte[] rgba, byte[] target, int targetOffset, int mask) {\n            // Quantise and pack alpha values pairwise.\n            for (int i = 0; i < 8; ++i) {\n                // Qnatise down to 4 bits.\n                var alpha1 = rgba[8 * i + 3] * (15f / 255f);\n                var alpha2 = rgba[8 * i + 7] * (15f / 255f);\n                var quant1 = FloatToInt(alpha1, 15);\n                var quant2 = FloatToInt(alpha2, 15);\n\n                // Set alpha to zero where masked.\n                var bit1 = 1 << (2 * i);\n                var bit2 = 1 << (2 * i + 1);\n                if ((mask & bit1) == 0)\n                    quant1 = 0;\n                if ((mask & bit2) == 0)\n                    quant2 = 0;\n\n                // Pack into the byte.\n                target[targetOffset + i] = (byte)(quant1 | (quant2 << 4));\n            }\n        }\n        public static void DecompressAlphaDxt3(byte[] block, int blockOffset, byte[] target, int targetOffset) {\n            // Unpack the alpha values pairwise.\n            for (int i = 0; i < 8; ++i) {\n                // Quantise down to 4 bits.\n                var quant = block[blockOffset + i];\n\n                // Unpack the values.\n                var lo = quant & 0x0f;\n                var hi = quant & 0xf0;\n\n                // Convert back up to bytes.\n                target[targetOffset + 8 * i + 3] = (byte)(lo | (lo << 4));\n                target[targetOffset + 8 * i + 7] = (byte)(hi | (hi >> 4));\n            }\n        }\n        #endregion\n\n        #region DXT5\n        private static void FixRange(ref int min, ref int max, int steps) {\n            if (max - min < steps)\n                max = Math.Min(min + steps, 255);\n            if (max - min < steps)\n                min = Math.Max(0, max - steps);\n        }\n        private static int FitCodes(byte[] rgba, int mask, byte[] codes, out byte[] indices) {\n            indices = new byte[16];\n\n            // Fit each alpha value to the codebook.\n            var err = 0;\n            for (int i = 0; i < 16; ++i) {\n                // Check this pixel is valid.\n                var bit = 1 << i;\n                if ((mask & bit) == 0) {\n                    // Use the first code.\n                    indices[i] = 0;\n                    continue;\n                }\n\n                // Find the least error and corresponding index.\n                var value = rgba[4 * i + 3];\n                var least = int.MaxValue;\n                var index = 0;\n                for (int j = 0; j < 8; ++j) {\n                    // Get the squared error from this code.\n                    var dist = ((int)value) - ((int)codes[j]);\n                    dist *= dist;\n\n                    // Compare with the best so far.\n                    if (dist < least) {\n                        least = dist;\n                        index = j;\n                    }\n                }\n\n                // Save this index and accumulate the error.\n                indices[i] = (byte)index;\n                err += least;\n            }\n\n            // Return the total error.\n            return err;\n        }\n        private static void WriteAlphaBlock(int alpha0, int alpha1, byte[] indices, byte[] target, int targetOffset) {\n            // Write the first two bytes.\n            target[targetOffset + 0] = (byte)alpha0;\n            target[targetOffset + 1] = (byte)alpha1;\n\n            var indOff = 0;\n            var retOff = 2;\n            for (int i = 0; i < 2; ++i) {\n                // Pack 8 3-bit values.\n                var value = 0;\n                for (int j = 0; j < 8; ++j) {\n                    var index = indices[indOff++];\n                    value |= (index << 3 * j);\n                }\n\n                // Store in 3 bytes\n                for (int j = 0; j < 3; ++j) {\n                    var b = (value >> (8 * j)) & 0xFF;\n                    target[targetOffset + retOff++] = (byte)b;\n                }\n            }\n        }\n        private static void WriteAlphaBlock5(int alpha0, int alpha1, byte[] indices, byte[] target, int targetOffset) {\n            // Check the relative values of the endpoints.\n            if (alpha0 > alpha1) {\n                var swapped = new byte[16];\n                for (int i = 0; i < 16; ++i) {\n                    var index = indices[i];\n                    if (index == 0)\n                        swapped[i] = 1;\n                    else if (index == 1)\n                        swapped[i] = 0;\n                    else if (index <= 5)\n                        swapped[i] = (byte)(7 - index);\n                    else\n                        swapped[i] = index;\n                }\n\n                // Write the block.\n                WriteAlphaBlock(alpha1, alpha0, swapped, target, targetOffset);\n            } else {\n                // Write the block.\n                WriteAlphaBlock(alpha0, alpha1, indices, target, targetOffset);\n            }\n        }\n        private static void WriteAlphaBlock7(int alpha0, int alpha1, byte[] indices, byte[] target, int targetOffset) {\n            // Check the relative values of the endpoints.\n            if (alpha0 > alpha1) {\n                var swapped = new byte[16];\n                for (int i = 0; i < 16; ++i) {\n                    var index = indices[i];\n                    if (index == 0)\n                        swapped[i] = 1;\n                    else if (index == 1)\n                        swapped[i] = 0;\n                    else\n                        swapped[i] = (byte)(9 - index);\n                }\n\n                // Write the block.\n                WriteAlphaBlock(alpha1, alpha0, swapped, target, targetOffset);\n            } else {\n                // Write the block.\n                WriteAlphaBlock(alpha0, alpha1, indices, target, targetOffset);\n            }\n        }\n        public static void CompressAlphaDxt5(byte[] rgba, int mask, byte[] target, int targetOffset) {\n            // Get the range for 5-alpha and 7-alpha interpolation.\n            int min5 = 255, max5 = 0;\n            int min7 = 255, max7 = 0;\n            for (int i = 0; i < 16; ++i) {\n                // Check this pixel is valid.\n                var bit = 1 << i;\n                if ((mask & bit) == 0)\n                    continue;\n\n                // Incorporate into the min/max.\n                int value = rgba[4 * i + 3];\n                if (value < min7)\n                    min7 = value;\n                if (value > max7)\n                    max7 = value;\n                if (value != 0 && value < min5)\n                    min5 = value;\n                if (value != 255 && value > max5)\n                    max5 = value;\n            }\n\n            // Handle the case that no valid range was found.\n            if (min5 > max5)\n                min5 = max5;\n            if (min7 > max7)\n                min7 = max7;\n\n            // Fix the range to be the minimum in each case.\n            FixRange(ref min5, ref max5, 5);\n            FixRange(ref min7, ref max7, 7);\n\n            // Set up the 5-alpha code book.\n            var codes5 = new byte[8];\n            codes5[0] = (byte)min5;\n            codes5[1] = (byte)max5;\n            for (int i = 1; i < 5; ++i)\n                codes5[i + 1] = (byte)(((5 - i) * min5 + i * max5) / 5);\n            codes5[6] = 0;\n            codes5[7] = 255;\n\n            // Set up the 7-alpha code book.\n            var codes7 = new byte[8];\n            codes7[0] = (byte)min7;\n            codes7[1] = (byte)max7;\n            for (int i = 1; i < 7; ++i)\n                codes7[i + 1] = (byte)(((7 - i) * min7 + i * max7) / 7);\n\n            // Fit the data to both code books.\n            byte[] indices5, indices7;\n            var err5 = FitCodes(rgba, mask, codes5, out indices5);\n            var err7 = FitCodes(rgba, mask, codes7, out indices7);\n\n            // Save the block with least error.\n            if (err5 <= err7)\n                WriteAlphaBlock5(min5, max5, indices5, target, targetOffset);\n            else\n                WriteAlphaBlock7(min7, max7, indices7, target, targetOffset);\n        }\n        public static void DecompressAlphaDxt5(byte[] block, int blockOffset, byte[] target, int targetOffset) {\n            // Get the two alpha values.\n            var alpha0 = block[blockOffset + 0];\n            var alpha1 = block[blockOffset + 1];\n\n            // Compare the values to build the codebook.\n            var codes = new byte[8];\n            codes[0] = alpha0;\n            codes[1] = alpha1;\n            if (alpha0 <= alpha1) {\n                // Use the 5-alpha codebook.\n                for (int i = 1; i < 5; ++i)\n                    codes[1 + i] = (byte)(((5 - i) * alpha0 + i * alpha1) / 5);\n                codes[6] = 0;\n                codes[7] = 255;\n            } else {\n                // Use the 7-alpha codebook.\n                for (int i = 1; i < 7; ++i)\n                    codes[1 + i] = (byte)(((7 - i) * alpha0 + i * alpha1) / 7);\n            }\n\n            // Decode the incdices\n            var indices = new byte[16];\n            var blOff = 2;\n            var indOff = 0;\n            for (int i = 0; i < 2; ++i) {\n                // Grab 3 bytes\n                int value = 0;\n                for (int j = 0; j < 3; ++j) {\n                    var b = block[blockOffset + blOff++];\n                    value |= (b << 8 * j);\n                }\n\n                // Unpack 8 3-bit values from it\n                for (int j = 0; j < 8; ++j) {\n                    var index = (value >> 3 * j) & 0x7;\n                    indices[indOff++] = (byte)index;\n                }\n            }\n\n            // Write out the index codebook values.\n            for (int i = 0; i < 16; ++i)\n                target[targetOffset + 4 * i + 3] = codes[indices[i]];\n        }\n        #endregion\n    }\n}\n"
  },
  {
    "path": "DotSquish/ClusterFit.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace DotSquish {\n    internal class ClusterFit : ColourFit {\n        const int MaxIterations = 8;\n\n        #region Fields\n        private int _IterationCount;\n        private Vector3 _Principle;\n        private byte[] _Order = new byte[16 * MaxIterations];\n        private Vector4[] _PointsWeight = new Vector4[16];\n        private Vector4 _XSumWSum;\n        private Vector4 _Metric;\n        private Vector4 _BestError;\n        #endregion\n\n        #region Constructor\n        protected ClusterFit(ColourSet colours, SquishOptions flags) : base(colours, flags) {\n            // Set the iteration count.\n            this._IterationCount = flags.HasFlag(SquishOptions.ColourIterativeClusterFit) ? MaxIterations : 1;\n\n            // Initialise the best error.\n            this._BestError = new Vector4(float.MaxValue);\n\n            // Initialize the metric\n            var perceptual = flags.HasFlag(SquishOptions.ColourMetricPerceptual);\n            if (perceptual)\n                this._Metric = new Vector4(0.2126f, 0.7152f, 0.0722f, 0.0f);\n            else\n                this._Metric = new Vector4(1.0f);\n\n            // Get the covariance matrix.\n            var covariance = Sym3x3.ComputeWeightedCovariance(colours.Count, colours.Points, colours.Weights);\n\n            // Compute the principle component\n            this._Principle = Sym3x3.ComputePrincipledComponent(covariance);\n        }\n        #endregion\n\n        #region Methods\n        protected bool ConstructOrdering(Vector3 axis, int iteration) {\n            // Build the list of dot products.\n            var dps = new float[16];\n            var ordOff = 16 * iteration;\n            for (int i = 0; i < _Colours.Count; ++i) {\n                dps[i] = Vector3.Dot(_Colours.Points[i], axis);\n                this._Order[ordOff + i] = (byte)i;\n            }\n\n            // Stable sort using them.\n            for (int i = 0; i < _Colours.Count; ++i) {\n                for (int j = i; j > 0 && dps[j] < dps[j - 1]; --j) {\n                    var _dps = dps[j];\n                    var _order = _Order[ordOff + j];\n\n                    dps[j] = dps[j - 1];\n                    dps[j - 1] = _dps;\n                    _Order[ordOff + j] = _Order[ordOff + j - 1];\n                    _Order[ordOff + j - 1] = _order;\n                }\n            }\n\n            // Check this ordering is unique\n            for (int it = 0; it < iteration; ++it) {\n                var prevOff = 16 * it;\n                var same = true;\n                for (int i = 0; i < _Colours.Count; ++i) {\n                    if (_Order[ordOff + i] != _Order[prevOff + i]) {\n                        same = false;\n                        break;\n                    }\n                }\n                if (same)\n                    return false;\n            }\n\n            // Copy the ordering and weight all the points\n            this._XSumWSum = new Vector4(0f);\n            for (int i = 0; i < _Colours.Count; ++i) {\n                var j = _Order[ordOff + i];\n                var p = new Vector4(_Colours.Points[j].X, _Colours.Points[j].Y, _Colours.Points[j].Z, 1f);\n                var w = new Vector4(_Colours.Weights[j]);\n                var x = p * w;\n                this._PointsWeight[i] = x;\n                this._XSumWSum += x;\n            }\n            return true;\n        }\n\n        protected override void Compress3(byte[] block) {\n           // Declare variables\n            var count = _Colours.Count;\n            var zero = new Vector4(0f);\n            var half = new Vector4(.5f);\n            var one = new Vector4(1f);\n            var two = new Vector4(2f);\n            var half_half2 = new Vector4(.5f, .5f, .5f, .25f);\n            var grid = new Vector4(31f, 63f, 31f, 0f);\n            var gridrcp = new Vector4(1f / 31f, 1f / 63f, 1f / 31f, 0f);\n\n            // Prepare the ordering using the principle axis.\n            ConstructOrdering(_Principle, 0);\n\n            // Check all possible clusters and iterate on the total order.\n            var bestStart = zero;\n            var bestEnd = zero;\n            var bestError = this._BestError;\n            var bestIndices = new byte[16];\n            //var bestIteration = 0;\n            //int besti = 0, bestj = 0;\n\n            //// Loop over iterations (we avoid the case that all points in first or last cluster)\n            //for (int iterationIndex = 0; ; ) {\n            //    throw new NotImplementedException();\n            //}\n\n            throw new NotImplementedException();\n        }\n        protected override void Compress4(byte[] block) {\n            throw new NotImplementedException();\n        }\n        #endregion\n    }\n}\n"
  },
  {
    "path": "DotSquish/ColourBlock.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace DotSquish {\n    internal static class ColourBlock {\n        private static int Unpack565(byte[] packed, int packedOffset, byte[] colour, int colourOffset) {\n            // Build the packed value.\n            var value = (int)packed[packedOffset] | ((int)packed[packedOffset + 1] << 8);\n\n            // Get the components in the stored range.\n            var red = (byte)((value >> 11) & 0x1F);\n            var green = (byte)((value >> 5) & 0x3F);\n            var blue = (byte)(value & 0x1F);\n\n            // Scale up to 8 bits\n            colour[colourOffset + 0] = (byte)((red << 3) | (red >> 2));\n            colour[colourOffset + 1] = (byte)((green << 2) | (green >> 4));\n            colour[colourOffset + 2] = (byte)((blue << 3) | (blue >> 2));\n            colour[colourOffset + 3] = 255;\n\n            return value;\n        }\n        public static byte[] DecompressColour(byte[] block, int blockOffset, bool isDxt1) {\n            // Unpack the endpoints\n            var codes = new byte[16];\n            var a = Unpack565(block, blockOffset + 0, codes, 0);\n            var b = Unpack565(block, blockOffset + 2, codes, 4);\n\n            // Generate the midpoints.\n            for (int i = 0; i < 3; ++i) {\n                var c = codes[i];\n                var d = codes[4 + i];\n\n                if (isDxt1 && a <= b) {\n                    codes[8 + i] = (byte)((c + d) / 2);\n                    codes[12 + i] = 0;\n                } else {\n                    codes[8 + i] = (byte)(((2 * c) + d) / 3);\n                    codes[12 + i] = (byte)((c + (2 * d)) / 3);\n                }\n            }\n\n            // Fill in alpha for the intermediate values.\n            codes[8 + 3] = 255;\n            codes[12 + 3] = (byte)((isDxt1 && a <= b) ? 0 : 255);\n\n            // Unpack the indices\n            var indices = new byte[16];\n            for (int i = 0; i < 4; i++) {\n                var packed = block[blockOffset + 4 + i];\n\n                indices[4 * i + 0] = (byte)(packed & 0x3);\n                indices[4 * i + 1] = (byte)((packed >> 2) & 0x3);\n                indices[4 * i + 2] = (byte)((packed >> 4) & 0x3);\n                indices[4 * i + 3] = (byte)((packed >> 6) & 0x3);\n            }\n\n            // Store the colours\n            var rgba = new byte[4 * 16];\n            for (int i = 0; i < 16; ++i) {\n                var offset = 4 * indices[i];\n                for (int j = 0; j < 4; ++j)\n                    rgba[4 * i + j] = codes[offset + j];\n            }\n            return rgba;\n        }\n    }\n}\n"
  },
  {
    "path": "DotSquish/ColourFit.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace DotSquish {\n    internal abstract class ColourFit {\n        #region Fields\n        protected ColourSet _Colours;\n        private SquishOptions _Flags;\n        #endregion\n\n        #region Constructor\n        protected ColourFit(ColourSet colours, SquishOptions flags) {\n            this._Colours = colours;\n            this._Flags = flags;\n        }\n        #endregion\n\n        #region Public\n        public void Compress(ref byte[] block) {\n            if (this._Flags.HasFlag(SquishOptions.DXT1)) {\n                Compress3(block);\n                if (!this._Colours.IsTransparent)\n                    Compress4(block);\n            } else\n                Compress4(block);\n        }\n        #endregion\n\n        #region Protected\n        protected abstract void Compress3(byte[] block);\n        protected abstract void Compress4(byte[] block);\n        #endregion\n    }\n}\n"
  },
  {
    "path": "DotSquish/ColourSet.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.IO;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace DotSquish {\n    internal class ColourSet {\n        #region Fields\n        private int _Count = 0;\n        private Vector3[] _Points = new Vector3[16];\n        private float[] _Weights = new float[16];\n        private int[] _Remap = new int[16];\n        private bool _IsTransparent = false;\n        #endregion\n\n        #region Properties\n        public int Count { get { return this._Count; } }\n        public Vector3[] Points { get { return this._Points; } }\n        public float[] Weights { get { return this._Weights; } }\n        public bool IsTransparent { get { return this._IsTransparent; } }\n        #endregion\n\n        #region Constructor\n        public ColourSet(byte[] rgba, int mask, SquishOptions flags) {\n            // Check the compression mode.\n            var isDxt1 = flags.HasFlag(SquishOptions.DXT1);\n            var weightByAlpha = flags.HasFlag(SquishOptions.WeightColourByAlpha);\n\n            // Create he minimal set.\n            for (int i = 0; i < 16; ++i) {\n                // Check this pixel is enabled.\n                int bit = 1 << i;\n                if ((mask & bit) == 0) {\n                    this._Remap[i] = -1;\n                    continue;\n                }\n\n                // Check for transparent pixels when using DXT1.\n                if (isDxt1 && rgba[4 * i + 3] < 128) {\n                    this._Remap[i] = -1;\n                    this._IsTransparent = true;\n                }\n\n                // Loop over previous points for a match.\n                for (int j = 0; ; ++j) {\n                    // Allocate a new point.\n                    if (j == i) {\n                        // Normalise coordinates to [0,1].\n                        var x = rgba[4 * i] / 255f;\n                        var y = rgba[4 * i + 1] / 255f;\n                        var z = rgba[4 * i + 2] / 255f;\n\n                        // Ensure there is always a non-zero weight even for zero alpha.\n                        var w = (rgba[4 * i + 3] + 1) / 256f;\n\n                        // Add the point.\n                        this._Points[this._Count] = new Vector3(x, y, z);\n                        this._Weights[this._Count] = w;\n                        this._Remap[i] = this._Count;\n\n                        // Advance.\n                        ++this._Count;\n                        break;\n                    }\n\n                    // Check for a match.\n                    int oldBit = 1 << j;\n                    var match = ((mask & oldBit) != 0)\n                        && (rgba[4 * i] == rgba[4 * j])\n                        && (rgba[4 * i + 1] == rgba[4 * j + 1])\n                        && (rgba[4 * i + 3] == rgba[4 * j + 2])\n                        && (rgba[4 * j + 3] >= 128 || !isDxt1);\n                    if (match) {\n                        // Get index of the match.\n                        var index = this._Remap[j];\n\n                        // Ensure there is always a non-zero weight even for zero alpha.\n                        var w = (rgba[4 * i + 3] + 1) / 256f;\n\n                        // Map this point and increase the weight.\n                        this._Weights[index] += (weightByAlpha ? w : 1f);\n                        this._Remap[i] = index;\n                        break;\n                    }\n                }\n            }\n\n            // Square root the weights.\n            for (int i = 0; i < this._Count; ++i)\n                this._Weights[i] = (float)Math.Sqrt(this._Weights[i]);\n        }\n        #endregion\n\n        #region Methods\n        public void RemapIndices(byte[] source, byte[] target, int targetOffset) {\n            for (int i = 0; i < 16; ++i) {\n                var j = this._Remap[i];\n                if (j == -1)\n                    target[i + targetOffset] = 3;\n                else\n                    target[i + targetOffset] = source[j];\n            }\n        }\n        #endregion\n    }\n}\n"
  },
  {
    "path": "DotSquish/DotSquish.csproj",
    "content": "﻿<Project Sdk=\"Microsoft.NET.Sdk\">\n  <PropertyGroup>\n    <ProjectGuid>{F9681545-4BEA-4FD3-9AB9-A67BD37AB36D}</ProjectGuid>\n    <TargetFramework>net7.0</TargetFramework>\n    <OutputPath>bin\\$(Configuration)\\</OutputPath>\n    <AllowUnsafeBlocks>true</AllowUnsafeBlocks>\n  </PropertyGroup>\n  <PropertyGroup Condition=\" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' \">\n    <DebugType>full</DebugType>\n  </PropertyGroup>\n  <PropertyGroup Condition=\" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' \">\n    <DebugType>pdbonly</DebugType>\n  </PropertyGroup>\n  <ItemGroup>\n    <PackageReference Include=\"System.Drawing.Common\" Version=\"7.0.0\" />\n  </ItemGroup>\n</Project>"
  },
  {
    "path": "DotSquish/Flags.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace DotSquish {\n    [Flags]\n    public enum SquishOptions {\n        /// <summary>\n        /// Use DXT1 compression.\n        /// </summary>\n        DXT1 = (1 << 0),\n        /// <summary>\n        /// Use DXT3 compression.\n        /// </summary>\n        DXT3 = (1 << 1),\n        /// <summary>\n        /// Use DXT5 compression.\n        /// </summary>\n        DXT5 = (1 << 2),\n        /// <summary>\n        /// Use a very slow but very high quality colour compressor.\n        /// </summary>\n        ColourIterativeClusterFit = (1 << 3),\n        /// <summary>\n        /// Use a slow but high quality colour compressor (default).\n        /// </summary>\n        ColourClusterFit = (1 << 4),\n        /// <summary>\n        /// Use a fast but low quality colour compressor.\n        /// </summary>\n        ColourRangeFit = (1 << 5),\n        /// <summary>\n        /// Use a perceptual metric for colour error (default).\n        /// </summary>\n        ColourMetricPerceptual = (1 << 6),\n        /// <summary>\n        /// Use a uniform metric for colour error.\n        /// </summary>\n        ColourMetricUniform = (1 << 7),\n        /// <summary>\n        /// Weight the colour by alpha during cluster fit (off by default).\n        /// </summary>\n        WeightColourByAlpha = (1 << 8),\n    }\n    public static class SquishOptionsExtensions {\n        public static SquishOptions GetMethod(this SquishOptions self) {\n            return (self & (SquishOptions.DXT1 | SquishOptions.DXT3 | SquishOptions.DXT5));\n        }\n        public static SquishOptions GetFit(this SquishOptions self) {\n            return (self & (SquishOptions.ColourIterativeClusterFit | SquishOptions.ColourClusterFit | SquishOptions.ColourRangeFit));\n        }\n        public static SquishOptions GetMetric(this SquishOptions self) {\n            return (self & (SquishOptions.ColourMetricPerceptual | SquishOptions.ColourMetricUniform));\n        }\n        public static SquishOptions GetExtra(this SquishOptions self) {\n            return (self & (SquishOptions.WeightColourByAlpha));\n        }\n    }\n}\n"
  },
  {
    "path": "DotSquish/Maths.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace DotSquish {\n    internal struct Vector3 {\n        public float X, Y, Z;\n\n        public Vector3(float value) {\n            this.X = this.Y = this.Z = value;\n        }\n        public Vector3(float x, float y, float z) {\n            this.X = x;\n            this.Y = y;\n            this.Z = z;\n        }\n\n        public static Vector3 operator +(Vector3 v1, Vector3 v2) {\n            return new Vector3(\n                v1.X + v2.X,\n                v1.Y + v2.Y,\n                v1.Z + v2.Z);\n        }\n        public static Vector3 operator -(Vector3 v) {\n            return new Vector3(\n                - v.X,\n                - v.Y,\n                - v.Z);\n        }\n        public static Vector3 operator -(Vector3 v1, Vector3 v2) {\n            return new Vector3(\n                v1.X - v2.X,\n                v1.Y - v2.Y,\n                v1.Z - v2.Z);\n        }\n        public static Vector3 operator *(Vector3 v1, Vector3 v2) {\n            return new Vector3(\n                v1.X * v2.X,\n                v1.Y * v2.Y,\n                v1.Z * v2.Z);\n        }\n        public static Vector3 operator *(Vector3 v1, float v2) {\n            return new Vector3(\n                v1.X * v2,\n                v1.Y * v2,\n                v1.Z * v2);\n        }\n        public static Vector3 operator *(float v1, Vector3 v2) {\n            return (v2 * v1);\n        }\n        public static Vector3 operator /(Vector3 v1, Vector3 v2) {\n            return new Vector3(\n                v1.X / v2.X,\n                v1.Y / v2.Y,\n                v1.Z / v2.Z);\n        }\n        public static Vector3 operator /(Vector3 v1, float v2) {\n            return new Vector3(\n                v1.X / v2,\n                v1.Y / v2,\n                v1.Z / v2);\n        }\n\n        public static float Dot(Vector3 v1, Vector3 v2) {\n            return v1.X * v2.X + v1.Y * v2.Y + v1.Z * v2.Z;\n        }\n        public static Vector3 Min(Vector3 v1, Vector3 v2) {\n            return new Vector3(\n                (float)Math.Min(v1.X, v2.X),\n                (float)Math.Min(v1.Y, v2.Y),\n                (float)Math.Min(v1.Z, v2.Z));\n        }\n        public static Vector3 Max(Vector3 v1, Vector3 v2) {\n            return new Vector3(\n                (float)Math.Max(v1.X, v2.X),\n                (float)Math.Max(v1.Y, v2.Y),\n                (float)Math.Max(v1.Z, v2.Z));\n        }\n        public static Vector3 Max(Vector3 v) {\n            return new Vector3(\n                (float)(v.X > 0f ? Math.Floor(v.X) : Math.Ceiling(v.X)),\n                (float)(v.Y > 0f ? Math.Floor(v.Y) : Math.Ceiling(v.Y)),\n                (float)(v.Z > 0f ? Math.Floor(v.Z) : Math.Ceiling(v.Z)));\n        }\n    }\n    internal struct Vector4 {\n        public float X, Y, Z, W;\n\n        public Vector4(float value) {\n            this.X = this.Y = this.Z = this.W = value;\n        }\n        public Vector4(float x, float y, float z, float w) {\n            this.X = x;\n            this.Y = y;\n            this.Z = z;\n            this.W = w;\n        }\n\n        public static Vector4 operator +(Vector4 v1, Vector4 v2) {\n            return new Vector4(\n                v1.X + v2.X,\n                v1.Y + v2.Y,\n                v1.Z + v2.Z,\n                v1.W + v2.W);\n        }\n        public static Vector4 operator -(Vector4 v) {\n            return new Vector4(\n                -v.X,\n                -v.Y,\n                -v.Z,\n                -v.W);\n        }\n        public static Vector4 operator -(Vector4 v1, Vector4 v2) {\n            return new Vector4(\n                v1.X - v2.X,\n                v1.Y - v2.Y,\n                v1.Z - v2.Z,\n                v1.W - v2.W);\n        }\n        public static Vector4 operator *(Vector4 v1, Vector4 v2) {\n            return new Vector4(\n                v1.X * v2.X,\n                v1.Y * v2.Y,\n                v1.Z * v2.Z,\n                v1.W * v2.W);\n        }\n        public static Vector4 operator *(Vector4 v1, float v2) {\n            return new Vector4(\n                v1.X * v2,\n                v1.Y * v2,\n                v1.Z * v2,\n                v1.W * v2);\n        }\n        public static Vector4 operator *(float v1, Vector4 v2) {\n            return (v2 * v1);\n        }\n        public static Vector4 operator /(Vector4 v1, Vector4 v2) {\n            return new Vector4(\n                v1.X / v2.X,\n                v1.Y / v2.Y,\n                v1.Z / v2.Z,\n                v1.W / v2.W);\n        }\n        public static Vector4 operator /(Vector4 v1, float v2) {\n            return new Vector4(\n                v1.X / v2,\n                v1.Y / v2,\n                v1.Z / v2,\n                v1.W / v2);\n        }\n    }\n\n    internal class Sym3x3 {\n        private float[] _X = new float[6];\n        public float this[int index] {\n            get { return this._X[index]; }\n            set { this._X[index] = value; }\n        }\n\n        public Sym3x3() { }\n        public Sym3x3(float s) {\n            for (int i = 0; i < 6; ++i)\n                this._X[i] = s;\n        }\n\n        public static Sym3x3 ComputeWeightedCovariance(int n, Vector3[] points, float[] weights) {\n            // Compute the centroid.\n            var total = 0f;\n            var centroid = new Vector3(0f);\n            for (int i = 0; i < n; ++i) {\n                total += weights[i];\n                centroid += weights[i] * points[i];\n            }\n            centroid /= total;\n\n            // Accumulate the covariance matrix.\n            var covariance = new Sym3x3(0f);\n            for (int i = 0; i < n; ++i) {\n                var a = points[i] - centroid;\n                var b = weights[i] * a;\n\n                covariance[0] += a.X * b.X;\n                covariance[1] += a.X * b.Y;\n                covariance[2] += a.X * b.Z;\n                covariance[3] += a.Y * b.Y;\n                covariance[4] += a.Y * b.Z;\n                covariance[5] += a.Z * b.Z;\n            }\n\n            return covariance;\n        }\n        private static Vector3 GetMultiplicity1Evector(Sym3x3 matrix, float evalue) {\n            // Compute M\n            var m = new Sym3x3();\n            m[0] = matrix[0] - evalue;\n            m[1] = matrix[1];\n            m[2] = matrix[2];\n            m[3] = matrix[3] - evalue;\n            m[4] = matrix[4];\n            m[5] = matrix[5] - evalue;\n\n            // Compute U\n            var u = new Sym3x3();\n            u[0] = (m[3] * m[5]) - (m[4] * m[4]);\n            u[1] = (m[2] * m[4]) - (m[1] * m[5]);\n            u[2] = (m[1] * m[4]) - (m[2] * m[3]);\n            u[3] = (m[0] * m[5]) - (m[2] * m[2]);\n            u[4] = (m[1] * m[2]) - (m[4] * m[0]);\n            u[5] = (m[0] * m[3]) - (m[1] * m[1]);\n\n            // Find the largest component.\n            var mc = Math.Abs(u[0]);\n            var mi = 0;\n            for (int i = 1; i < 6; ++i) {\n                var c = Math.Abs(u[i]);\n                if (c > mc) {\n                    mc = c;\n                    mi = i;\n                }\n            }\n\n            // Pick the column with this component.\n            switch (mi) {\n                case 0:\n                    return new Vector3(u[0], u[1], u[2]);\n                case 1:\n                case 3:\n                    return new Vector3(u[1], u[3], u[4]);\n                default:\n                    return new Vector3(u[2], u[4], u[5]);\n            }\n        }\n        private static Vector3 GetMultiplicity2Evector(Sym3x3 matrix, float evalue) {\n            // Compute M\n            var m = new Sym3x3();\n            m[0] = matrix[0] - evalue;\n            m[1] = matrix[1];\n            m[2] = matrix[2];\n            m[3] = matrix[3] - evalue;\n            m[4] = matrix[4];\n            m[5] = matrix[5] - evalue;\n\n            // Find the largest component.\n            var mc = Math.Abs(m[0]);\n            var mi = 0;\n            for (int i = 1; i < 6; ++i) {\n                var c = Math.Abs(m[i]);\n                if (c > mc) {\n                    mc = c;\n                    mi = i;\n                }\n            }\n\n            // pick the first eigenvector based on this index\n            switch (mi) {\n                case 0:\n                case 1:\n                    return new Vector3(-m[1], m[0], 0.0f);\n\n                case 2:\n                    return new Vector3(m[2], 0.0f, -m[0]);\n\n                case 3:\n                case 4:\n                    return new Vector3(0.0f, -m[4], m[3]);\n\n                default:\n                    return new Vector3(0.0f, -m[5], m[4]);\n            }\n        }\n        public static Vector3 ComputePrincipledComponent(Sym3x3 matrix) {\n            // Compute the cubic coefficients\n            var c0 =\n                (matrix[0] * matrix[3] * matrix[5])\n                + (matrix[1] * matrix[2] * matrix[4] * 2f)\n                - (matrix[0] * matrix[4] * matrix[4])\n                - (matrix[3] * matrix[2] * matrix[2])\n                - (matrix[5] * matrix[1] * matrix[1]);\n            var c1 =\n                (matrix[0] * matrix[3])\n                + (matrix[0] * matrix[5])\n                + (matrix[3] * matrix[5])\n                - (matrix[1] * matrix[1])\n                - (matrix[2] * matrix[2])\n                - (matrix[4] * matrix[4]);\n            var c2 = matrix[0] + matrix[3] + matrix[5];\n\n            // Compute the quadratic coefficients\n            var a = c1 - ((1f / 3f) * c2 * c2);\n            var b = ((-2f / 27f) * c2 * c2 * c2) + ((1f / 3f) * c1 * c2) - c0;\n\n            // Compute the root count check;\n            var Q = (.25f * b * b) + ((1f / 27f) * a * a * a);\n\n            // Test the multiplicity.\n            if (float.Epsilon < Q)\n                return new Vector3(1f); // Only one root, which implies we have a multiple of the identity.\n            else if (Q < -float.Epsilon) {\n                // Three distinct roots\n                var theta = Math.Atan2(Math.Sqrt(Q), -.5f * b);\n                var rho = Math.Sqrt((.25f * b * b) - Q);\n\n                var rt = Math.Pow(rho, 1f / 3f);\n                var ct = Math.Cos(theta / 3f);\n                var st = Math.Sin(theta / 3f);\n\n                var l1 = ((1f / 3f) * c2) + (2f * rt * ct);\n                var l2 = ((1f / 3f) * c2) - (rt * (ct + (Math.Sqrt(3f) * st)));\n                var l3 = ((1f / 3f) * c2) - (rt * (ct - (Math.Sqrt(3f) * st)));\n\n                // Pick the larger.\n                if (Math.Abs(l2) > Math.Abs(l1))\n                    l1 = l2;\n                if (Math.Abs(l3) > Math.Abs(l1))\n                    l1 = l3;\n\n                // Get the eigenvector\n                return GetMultiplicity1Evector(matrix, (float)l1);\n            } else {    // Q very close to 0\n                // Two roots\n                double rt;\n                if (b < 0.0f)\n                    rt = -Math.Pow(-.5f * b, 1f / 3f);\n                else\n                    rt = Math.Pow(.5f * b, 1f / 3f);\n\n                var l1 = ((1f / 3f) * c2) + rt;\n                var l2 = ((1f / 3f) * c2) - (2f * rt);\n\n                // Get the eigenvector\n                if (Math.Abs(l1) > Math.Abs(l2))\n                    return GetMultiplicity2Evector(matrix, (float)l1);\n                else\n                    return GetMultiplicity1Evector(matrix, (float)l2);\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "DotSquish/Squish.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Drawing;\nusing System.Drawing.Imaging;\nusing System.IO;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace DotSquish {\n    public static class Squish {\n        public static int GetStorageRequirements(int width, int height, SquishOptions flags) {\n            var blockCount = ((width + 3) / 4) * ((height + 3) / 4);\n            var blockSize = flags.HasFlag(SquishOptions.DXT1) ? 8 : 16;\n            return blockCount * blockSize;\n        }\n\n        #region On buffer\n        public static byte[] CompressBlock(byte[] rgba, SquishOptions flags) {\n            return CompressBlockMasked(rgba, 0xFFFF, flags);\n        }\n        public static byte[] CompressBlockMasked(byte[] rgba, int mask, SquishOptions flags) {\n            throw new NotImplementedException();\n        }\n        public static byte[] DecompressBlock(byte[] block, int blockOffset, SquishOptions flags) {\n            // Get the block locations\n            var colOff = blockOffset;\n            var alphaOff = blockOffset;\n            if ((flags & (SquishOptions.DXT3 | SquishOptions.DXT5)) != 0)\n                colOff += 8;\n\n            // Decompress colour.\n            var rgba = ColourBlock.DecompressColour(block, colOff, flags.HasFlag(SquishOptions.DXT1));\n\n            // Decompress alpha seperately if necessary.\n            if (flags.HasFlag(SquishOptions.DXT3))\n                Alpha.DecompressAlphaDxt3(block, alphaOff, rgba, 0);\n            else if (flags.HasFlag(SquishOptions.DXT5))\n                Alpha.DecompressAlphaDxt5(block, alphaOff, rgba, 0);\n\n            return rgba;\n        }\n\n        public static byte[] CompressImage(byte[] rgba, int width, int height, SquishOptions flags) {\n            throw new NotImplementedException();\n        }\n        public static byte[] DecompressImage(byte[] blocks, int width, int height, SquishOptions flags) {\n            return DecompressImage(blocks, 0, width, height, flags);\n        }\n        public static byte[] DecompressImage(byte[] blocks, int offset, int width, int height, SquishOptions flags) {\n            var argb = new byte[4 * width * height];\n            var bytesPerBlock = flags.HasFlag(SquishOptions.DXT1) ? 8 : 16;\n            \n            var blockOffset = offset;\n            // Loop over blocks.\n            for (int y = 0; y < height; y += 4) {\n                for (int x = 0; x < width; x += 4) {\n                    // Decompress the block.\n                    var targetRgba = DecompressBlock(blocks, blockOffset, flags);\n\n                    // Write the decompressed pixels to the correct image locations.\n                    var sourcePixelOffset = 0;\n                    for (int py = 0; py < 4; ++py) {\n                        for (int px = 0; px < 4; ++px) {\n                            // Get the target location.\n                            var sx = x + px;\n                            var sy = y + py;\n                            if (sx < width && sy < height) {\n                                var targetPixelOffset = 4 * ((width * sy) + sx);\n                                // Copy the rgba value\n                                argb[targetPixelOffset + 0] = targetRgba[sourcePixelOffset + 2];\n                                argb[targetPixelOffset + 1] = targetRgba[sourcePixelOffset + 1];\n                                argb[targetPixelOffset + 2] = targetRgba[sourcePixelOffset + 0];\n                                argb[targetPixelOffset + 3] = targetRgba[sourcePixelOffset + 3];\n                            }\n                            sourcePixelOffset += 4;\n                        }\n                    }\n\n                    // advance\n                    blockOffset += bytesPerBlock;\n                }\n            }\n            return argb;\n        }\n        public static Image DecompressToBitmap(byte[] blocks, int width, int height, SquishOptions flags) {\n            return DecompressToBitmap(blocks, 0, width, height, flags);\n        }\n        public static unsafe Image DecompressToBitmap(byte[] blocks, int offset, int width, int height, SquishOptions flags) {\n            var fullBuffer = new byte[4 * width * height];\n            var bufferOffset = 0;\n\n            var bytesPerBlock = flags.HasFlag(SquishOptions.DXT1) ? 8 : 16;\n            var blockOffset = offset;\n            // Loop over blocks.\n            for (int y = 0; y < height; y += 4) {\n                for (int x = 0; x < width; x += 4) {\n                    // Decompress the block.\n                    var targetRgba = DecompressBlock(blocks, blockOffset, flags);\n\n\n                    // Write the decompressed pixels to the correct image locations.\n                    var sourcePixelOffset = 0;\n                    for (int py = 0; py < 4; ++py) {\n                        for (int px = 0; px < 4; ++px) {\n                            // Get the target location.\n                            var sx = x + px;\n                            var sy = y + py;\n                            if (sx < width && sy < height) {\n                                var i = 4 * (sx + (sy * width));\n                                fullBuffer[bufferOffset + i + 0] = targetRgba[sourcePixelOffset + 2];\n                                fullBuffer[bufferOffset + i + 1] = targetRgba[sourcePixelOffset + 1];\n                                fullBuffer[bufferOffset + i + 2] = targetRgba[sourcePixelOffset + 0];\n                                fullBuffer[bufferOffset + i + 3] = targetRgba[sourcePixelOffset + 3];\n                            }\n\n                            sourcePixelOffset += 4; // Skip this pixel as it is outside the image.\n                        }\n                    }\n\n                    // advance\n                    blockOffset += bytesPerBlock;\n                }\n            }\n            Image ret;\n            fixed (byte* p = fullBuffer) {\n                var ptr = (IntPtr)p;\n                var tempImage = new Bitmap(width, height, 4 * width, System.Drawing.Imaging.PixelFormat.Format32bppArgb, ptr);\n                ret = new Bitmap(tempImage);\n            }\n            return ret;\n        }\n        #endregion\n\n        #region On stream\n        public static void CompressBlock(Stream input, Stream output, SquishOptions flags) {\n            CompressBlockMasked(input, output, 0xFFFF, flags);\n        }\n        public static void CompressBlockMasked(Stream input, Stream output, int mask, SquishOptions flags){\n            throw new NotImplementedException();\n        }\n        public static void DecompressBlock(Stream input, Stream output, SquishOptions flags) {\n            throw new NotImplementedException();\n        }\n        public static void CompressImage(Stream input, Stream output, int width, int height, SquishOptions flags) {\n            throw new NotImplementedException();\n        }\n        public static void DecompressImage(Stream input, Stream output, int width, int height, SquishOptions flags) {\n            throw new NotImplementedException();\n        }\n        #endregion\n    }\n}\n"
  },
  {
    "path": "Godbert/App.config",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<configuration>\n    <configSections>\n        <sectionGroup name=\"userSettings\" type=\"System.Configuration.UserSettingsGroup, System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089\">\n            <section name=\"Godbert.Properties.Settings\" type=\"System.Configuration.ClientSettingsSection, System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089\" allowExeDefinition=\"MachineToLocalUser\" requirePermission=\"false\"/>\n        </sectionGroup>\n    </configSections>\n    <startup> \n        <supportedRuntime version=\"v4.0\" sku=\".NETFramework,Version=v4.6.1\"/>\n    </startup>\n    <userSettings>\n        <Godbert.Properties.Settings>\n            <setting name=\"GamePath\" serializeAs=\"String\">\n                <value/>\n            </setting>\n        </Godbert.Properties.Settings>\n    </userSettings>\n  <runtime>\n    <assemblyBinding xmlns=\"urn:schemas-microsoft-com:asm.v1\">\n      <dependentAssembly>\n        <assemblyIdentity name=\"System.Data.SQLite\" publicKeyToken=\"db937bc2d44ff139\" culture=\"neutral\"/>\n        <bindingRedirect oldVersion=\"0.0.0.0-1.0.96.0\" newVersion=\"1.0.96.0\"/>\n      </dependentAssembly>\n    </assemblyBinding>\n  </runtime>\n</configuration>\n"
  },
  {
    "path": "Godbert/App.xaml",
    "content": "﻿<Application x:Class=\"Godbert.App\"\n             xmlns=\"http://schemas.microsoft.com/winfx/2006/xaml/presentation\"\n             xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\"\n             StartupUri=\"MainWindow.xaml\">\n    <Application.Resources>\n         \n    </Application.Resources>\n</Application>\n"
  },
  {
    "path": "Godbert/App.xaml.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Configuration;\nusing System.Data;\nusing System.IO;\nusing System.Linq;\nusing System.Threading.Tasks;\nusing System.Windows;\n\nusing Ookii.Dialogs.Wpf;\n\nnamespace Godbert {\n    /// <summary>\n    /// Interaction logic for App.xaml\n    /// </summary>\n    public partial class App : Application {\n        protected override void OnStartup(StartupEventArgs e) {\n            if (!RequestGamePath()) {\n                MainWindow = null;\n                Shutdown(1);\n                return;\n            }\n\n            SaintCoinach.Graphics.Viewer.Interop.HavokInterop.InitializeMTA();\n\n            base.OnStartup(e);\n\n            this.Exit += App_Exit;\n        }\n\n        private void App_Exit(object sender, ExitEventArgs e) {\n            Settings.Default.Save();\n        }\n\n        #region Startup\n\n        private static bool RequestGamePath() {\n            string path = Godbert.Properties.Settings.Default.GamePath;\n            if (!IsValidGamePath(path)) {\n                string programDir;\n                if (Environment.Is64BitProcess)\n                    programDir = Environment.GetFolderPath(Environment.SpecialFolder.ProgramFilesX86);\n                else\n                    programDir = Environment.GetFolderPath(Environment.SpecialFolder.ProgramFiles);\n\n                path = System.IO.Path.Combine(programDir, \"SquareEnix\", \"FINAL FANTASY XIV - A Realm Reborn\");\n\n                if (IsValidGamePath(path)) {\n                    var msgResult = System.Windows.MessageBox.Show(string.Format(\"Found game installation at \\\"{0}\\\". Is this correct?\", path), \"Confirm game installation\", MessageBoxButton.YesNo, MessageBoxImage.Question, MessageBoxResult.Yes);\n                    if (msgResult == MessageBoxResult.Yes) {\n                        Godbert.Properties.Settings.Default.GamePath = path;\n                        Godbert.Properties.Settings.Default.Save();\n\n                        return true;\n                    }\n\n                    path = null;\n                }\n            }\n\n            VistaFolderBrowserDialog dlg = null;\n\n            while (!IsValidGamePath(path)) {\n                var result = (dlg ?? (dlg = new VistaFolderBrowserDialog {\n                    Description = \"Please select the directory of your FFXIV:ARR game installation (should contain 'boot' and 'game' directories).\",\n                    ShowNewFolderButton = false,\n                })).ShowDialog();\n\n                if (!result.GetValueOrDefault(false)) {\n                    var msgResult = System.Windows.MessageBox.Show(\"Cannot continue without a valid game installation, quit the program?\", \"That's no good\", MessageBoxButton.YesNo, MessageBoxImage.Error, MessageBoxResult.No);\n                    if (msgResult == MessageBoxResult.Yes)\n                        return false;\n                }\n\n                path = dlg.SelectedPath;\n            }\n\n            Godbert.Properties.Settings.Default.GamePath = path;\n            Godbert.Properties.Settings.Default.Save();\n            return true;\n        }\n        public static bool IsValidGamePath(string path) {\n            if (string.IsNullOrWhiteSpace(path))\n                return false;\n\n            if (!Directory.Exists(path))\n                return false;\n\n            return File.Exists(Path.Combine(path, \"game\", \"ffxivgame.ver\"));\n        }\n        #endregion\n    }\n}\n"
  },
  {
    "path": "Godbert/Commands/DelegateCommand.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace Godbert.Commands {\n    public class DelegateCommand : System.Windows.Input.ICommand {\n        #region Fields\n        private Action _Target;\n        #endregion\n\n        #region Constructor\n        public DelegateCommand(Action target) {\n            _Target = target;\n        }\n        #endregion\n\n        #region ICommand Members\n\n        public bool CanExecute(object parameter) {\n            return true;\n        }\n\n\n        public void Execute(object parameter) {\n            _Target();\n        }\n\n        #endregion\n\n        #region ICommand Members\n\n        event EventHandler System.Windows.Input.ICommand.CanExecuteChanged {\n            add { }\n            remove { }\n        }\n\n        #endregion\n    }\n    public class DelegateCommand<T> : System.Windows.Input.ICommand {\n        #region Fields\n        private Action<T> _Target;\n        #endregion\n\n        #region Constructor\n        public DelegateCommand(Action<T> target) {\n            _Target = target;\n        }\n        #endregion\n\n        #region ICommand Members\n\n        public bool CanExecute(object parameter) {\n            return parameter == null || (parameter is T);\n        }\n\n        public void Execute(object parameter) {\n            _Target((T)parameter);\n        }\n\n        #endregion\n\n        #region ICommand Members\n\n        event EventHandler System.Windows.Input.ICommand.CanExecuteChanged {\n            add { }\n            remove { }\n        }\n\n        #endregion\n    }\n}\n"
  },
  {
    "path": "Godbert/Controls/AttachedImage.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Windows;\nusing System.Windows.Controls;\nusing System.Windows.Media;\nusing System.Windows.Media.Imaging;\n\nnamespace Godbert.Controls {\n    public static class AttachedImage {\n        public static readonly DependencyProperty ImageProperty = DependencyProperty.RegisterAttached(\"Image\",\n            typeof(SaintCoinach.Imaging.ImageFile), typeof(AttachedImage), new PropertyMetadata(AttachedImageChanged));\n\n        public static readonly DependencyProperty ImageTypeProperty = DependencyProperty.RegisterAttached(\"ImageType\",\n            typeof(string), typeof(AttachedImage), new PropertyMetadata(AttachedImageChanged));\n\n        public static readonly DependencyProperty UseImageTypeProperty = DependencyProperty.RegisterAttached(\"UseImageType\",\n            typeof(bool), typeof(AttachedImage), new PropertyMetadata(true, AttachedImageChanged));\n\n        public static readonly DependencyProperty IsSizeRestrictedProperty = DependencyProperty.RegisterAttached(\"IsSizeRestricted\",\n            typeof(bool?), typeof(AttachedImage), new PropertyMetadata(null, AttachedImageChanged));\n\n        private static readonly Dictionary<string, WeakReference<ImageSource>> _SourceCache =\n            new Dictionary<string, WeakReference<ImageSource>>();\n\n        public static bool GetUseImageType(DependencyObject o) {\n            return (bool)o.GetValue(UseImageTypeProperty);\n        }\n        public static void SetUseImageType(DependencyObject o, bool newValue) {\n            o.SetValue(UseImageTypeProperty, newValue);\n        }\n\n        public static bool? GetIsSizeRestricted(DependencyObject o) {\n            return (bool?)o.GetValue(IsSizeRestrictedProperty);\n        }\n\n        public static void SetIsSizeRestricted(DependencyObject o, bool? newValue) {\n            o.SetValue(IsSizeRestrictedProperty, newValue);\n        }\n\n        public static SaintCoinach.Imaging.ImageFile GetImage(DependencyObject o) {\n            return (SaintCoinach.Imaging.ImageFile)o.GetValue(ImageProperty);\n        }\n\n        public static void SetImage(DependencyObject o, SaintCoinach.Imaging.ImageFile v) {\n            o.SetValue(ImageProperty, v);\n        }\n\n        public static string GetImageType(DependencyObject o) {\n            return (string)o.GetValue(ImageTypeProperty);\n        }\n\n        public static void SetImageType(DependencyObject o, string v) {\n            o.SetValue(ImageTypeProperty, v);\n        }\n\n        private static void AttachedImageChanged(DependencyObject o, DependencyPropertyChangedEventArgs e) {\n            var img = (Image)o;\n\n            var srcImage = GetImage(o);\n            var srcImageType = GetImageType(o);\n            var useSrcImageType = GetUseImageType(o);\n            if (!useSrcImageType)\n                srcImageType = null;\n\n            double w = double.PositiveInfinity, h = double.PositiveInfinity;\n\n            if (srcImage != null) {\n                var useImage = GetImageOfType(srcImage, srcImageType);\n\n                w = useImage.Width;\n                h = useImage.Height;\n\n                img.Source = GetAsImageSource(useImage);\n            } else\n                img.Source = null;\n\n            var sizeRestrict = GetIsSizeRestricted(o);\n            if (sizeRestrict.HasValue) {\n                if (sizeRestrict.Value == false)\n                    w = h = double.PositiveInfinity;\n\n                img.MaxWidth = w;\n                img.MaxHeight = h;\n            }\n        }\n\n        private static ImageSource GetAsImageSource(SaintCoinach.Imaging.ImageFile file) {\n            if (file == null)\n                return null;\n\n            var key = file.Path;\n            WeakReference<ImageSource> targetRef;\n            ImageSource target;\n            if (!_SourceCache.TryGetValue(key, out targetRef) || !targetRef.TryGetTarget(out target)) {\n                target = CreateSource(file);\n                if (targetRef == null)\n                    _SourceCache.Add(key, new WeakReference<ImageSource>(target));\n                else\n                    targetRef.SetTarget(target);\n            }\n\n            return target;\n        }\n\n        private static SaintCoinach.Imaging.ImageFile GetImageOfType(SaintCoinach.Imaging.ImageFile original,\n                                                                     string type) {\n            if (string.IsNullOrWhiteSpace(type))\n                return original;\n            if (!original.Path.StartsWith(\"ui/icon/\"))\n                return original; // XXX: Exception instead?\n\n            var dirPath = original.Path.Substring(0, \"ui/icon/000000/\".Length);\n            var targetPath = string.Format(\"{0}{1}/{2}\", dirPath, type, original.Path.Split('/').Last());\n\n            SaintCoinach.IO.File typeFile;\n            if (original.Pack.Collection.TryGetFile(targetPath, out typeFile))\n                return (SaintCoinach.Imaging.ImageFile)typeFile;\n            return original;\n        }\n\n        private static ImageSource CreateSource(SaintCoinach.Imaging.ImageFile file) {\n            var argb = SaintCoinach.Imaging.ImageConverter.GetA8R8G8B8(file);\n            return BitmapSource.Create(\n                                       file.Width, file.Height,\n                96, 96,\n                PixelFormats.Bgra32, null,\n                argb, file.Width * 4);\n        }\n    }\n}\n"
  },
  {
    "path": "Godbert/Controls/ColumnFactory.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\nusing System.Windows;\nusing System.Windows.Controls;\nusing System.Windows.Data;\n\nusing SaintCoinach.Ex.Relational;\nusing SaintCoinach.Ex.Relational.Definition;\nusing SaintCoinach.Ex;\nusing SaintCoinach.Ex.DataReaders;\n\nnamespace Godbert.Controls {\n    static class ColumnFactory {\n        public static DataGridColumn Create(RelationalColumn column) {\n            var sheetDef = column.Header.SheetDefinition;\n            Type defType = null;\n            if (sheetDef != null)\n                defType = sheetDef.GetValueType(column.Index);\n            var targetType = defType ?? column.Reader.Type;\n\n            var header = BuildHeader(column);\n            var binding = CreateCellBinding(column);\n\n            DataGridColumn target = null;\n            if (typeof(SaintCoinach.Imaging.ImageFile).IsAssignableFrom(targetType))\n                target = new RawDataGridImageColumn(column) {\n                    Binding = binding,\n                };\n            else if (typeof(System.Drawing.Color).IsAssignableFrom(targetType))\n                target = new RawDataGridColorColumn(column) {\n                    Binding = binding\n                };\n\n            target = target ?? new RawDataGridTextColumn(column) {\n                Binding = binding\n            };\n\n            target.Header = header;\n            target.IsReadOnly = true;\n            target.CanUserSort = true;\n            return target;\n        }\n\n        private static string BuildHeader(RelationalColumn column) {\n            var sb = new StringBuilder();\n\n            sb.Append(column.Index);\n            if (!string.IsNullOrWhiteSpace(column.Name))\n                sb.AppendFormat(\": {0}\", column.Name);\n            sb.Append(Environment.NewLine);\n            sb.Append(column.Reader.Type.Name);\n\n            if (column.ValueType != column.Reader.Name)\n                sb.AppendFormat(\" > {0}\", column.ValueType);\n\n            if (Settings.Default.ShowOffsets) {\n                if (column.Reader is PackedBooleanDataReader)\n                    sb.AppendFormat(\" [{0:X}&{1:X2}]\", column.Offset, ((PackedBooleanDataReader)column.Reader).Mask);\n                else\n                    sb.AppendFormat(\" [{0:X}]\", column.Offset);\n            }\n\n            return sb.ToString();\n        }\n        private static Binding CreateCellBinding(RelationalColumn column) {\n            return new Binding {\n                Converter = CellConverterInstance,\n                ConverterParameter = column.Index,\n                Mode = BindingMode.OneWay\n            };\n        }\n\n        public static bool ForceRaw;\n        public static readonly System.Windows.Data.IValueConverter CellConverterInstance = new CellConverter();\n\n        private class CellConverter : System.Windows.Data.IValueConverter {\n\n\n\n            #region IValueConverter Members\n\n            public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) {\n                var row = value as IRow;\n                if (row == null)\n                    return null;\n\n                var i = System.Convert.ToInt32(parameter);\n\n                if (ForceRaw || RawDataGrid.ColumnSetToRaw[i])\n                    return row.GetRaw(i);\n\n                return row[i] ?? row.GetRaw(i);\n            }\n\n            public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) {\n                throw new NotImplementedException();\n            }\n\n            #endregion\n        }\n    }\n}\n"
  },
  {
    "path": "Godbert/Controls/INavigatable.cs",
    "content": "﻿using SaintCoinach.Ex;\nusing SaintCoinach.Ex.Relational;\nusing System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\nusing System.Windows;\n\nnamespace Godbert.Controls\n{\n    public interface INavigatable\n    {\n        IRow OnNavigate(object sender, RoutedEventArgs e);\n    }\n}\n"
  },
  {
    "path": "Godbert/Controls/IRawDataColumn.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\nusing SaintCoinach.Ex.Relational;\n\nnamespace Godbert.Controls {\n    public interface IRawDataColumn {\n        IComparer<object> GetComparer(System.ComponentModel.ListSortDirection direction);\n        RelationalColumn Column { get; }\n    }\n}\n"
  },
  {
    "path": "Godbert/Controls/RawDataGrid.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\nusing System.Windows;\nusing System.Windows.Controls;\nusing System.Windows.Data;\n\nusing System.ComponentModel;\nusing System.Windows.Input;\nusing SaintCoinach.Ex.Relational;\nusing Godbert.ViewModels;\nusing SaintCoinach.Xiv;\nusing System.Collections;\nusing System.Windows.Controls.Primitives;\nusing System.Windows.Media;\nusing SaintCoinach.Ex;\n\nnamespace Godbert.Controls {\n    public class RawDataGrid : DataGrid {\n        #region DependencyProperties\n        public static readonly DependencyProperty SheetProperty = DependencyProperty.Register(\"Sheet\", typeof(IRelationalSheet), typeof(RawDataGrid), new PropertyMetadata(OnSheetChanged));\n        public static readonly DependencyProperty FilterProperty = DependencyProperty.Register(\"Filter\", typeof(string), typeof(RawDataGrid), new PropertyMetadata(OnFilterChanged));\n\n        public IRelationalSheet Sheet {\n            get { return (IRelationalSheet)GetValue(SheetProperty); }\n            set { SetValue(SheetProperty, value); }\n        }\n\n        public string Filter {\n            get { return (string)GetValue(FilterProperty); }\n            set { SetValue(FilterProperty, value); }\n        }\n\n        private static void OnSheetChanged(DependencyObject o, DependencyPropertyChangedEventArgs e) {\n            ((RawDataGrid)o).OnSheetChanged((IRelationalSheet)e.OldValue, (IRelationalSheet)e.NewValue);\n        }\n\n        public static bool[] ColumnSetToRaw;\n\n        protected virtual void OnSheetChanged(IRelationalSheet oldValue, IRelationalSheet newValue) {\n            this.Columns.Clear();\n\n            if (newValue != null)\n            {\n                var keyPath = newValue.Header.Variant == 1 ? \"Key\" : \"FullKey\";\n\n                //prevent multiple enumeration\n                var columns = newValue.Header.Columns.ToList();\n\n                if (Settings.Default.SortByOffsets)\n                    columns.Sort((x, y) => x.Offset.CompareTo(y.Offset));\n\n                ColumnSetToRaw = new bool[columns.Count];\n\n                Columns.Add(new RawDataGridKeyColumn(keyPath) { CanUserSort = true });\n\n                foreach (var col in columns)\n                    Columns.Add(ColumnFactory.Create(col));\n\n                var source = new RawDataItemsSource(newValue);\n                if (!string.IsNullOrWhiteSpace(Filter))\n                    source.Filter = Filter;\n                this.ItemsSource = source;\n            }\n            else\n                this.ItemsSource = null;\n        }\n\n        private static void OnFilterChanged(DependencyObject o, DependencyPropertyChangedEventArgs e) {\n            ((RawDataGrid)o).OnFilterChanged((string)e.OldValue, (string)e.NewValue);\n        }\n\n        protected virtual void OnFilterChanged(string oldValue, string newValue) {\n            var src = this.ItemsSource as RawDataItemsSource;\n            if (src == null)\n                return;\n\n            if (string.IsNullOrWhiteSpace(newValue))\n                src.Filter = null;\n            else\n                src.Filter = newValue;\n        }\n\n        #endregion\n\n        protected override void OnSorting(DataGridSortingEventArgs eventArgs) {\n            eventArgs.Handled = true;\n\n            var src = this.ItemsSource as RawDataItemsSource;\n            var col = eventArgs.Column as IRawDataColumn;\n\n            if (src == null || col == null)\n                return;\n\n            var nextDir = (eventArgs.Column.SortDirection != ListSortDirection.Ascending) ? ListSortDirection.Ascending : ListSortDirection.Descending;\n            eventArgs.Column.SortDirection = nextDir;\n\n            var cmp = col.GetComparer(nextDir);\n            src.Comparer = cmp;\n        }\n\n        protected override void OnMouseDown(MouseButtonEventArgs e) {\n            base.OnMouseDown(e);\n\n            if (e.RightButton == MouseButtonState.Pressed)\n            {\n                var cell = GetClickedHeader(e);\n                if (cell == null)\n                    return;\n\n                if (cell.Column is RawDataGridImageColumn ||\n                    cell.Column is RawDataGridTextColumn ||\n                    cell.Column is RawDataGridColorColumn)\n                {\n                    ColumnFactory.ForceRaw = !ColumnFactory.ForceRaw;\n                    Items.Refresh();\n                    e.Handled = true;\n                }\n            }\n\n            if (e.MiddleButton == MouseButtonState.Pressed) \n            {\n\n                var cellHeader = GetClickedHeader(e);\n                if (cellHeader != null)\n                {\n\n                    if (cellHeader.Column is RawDataGridImageColumn ||\n                        cellHeader.Column is RawDataGridTextColumn ||\n                        cellHeader.Column is RawDataGridColorColumn)\n                    {\n\n                        var columnIndex = ((IRawDataColumn) cellHeader.Column).Column.Index;\n                        ColumnSetToRaw[columnIndex] = !ColumnSetToRaw[columnIndex];\n                        Items.Refresh();\n                        e.Handled = true;\n                    }\n\n\n                    return;\n                }\n\n                var cell = GetClickedCell(e);\n                if (cell == null)\n                    return;\n\n                var dataView = WpfHelper.FindParent<Views.DataView>(cell);\n                if (dataView == null)\n                    return;\n\n                var dataViewModel = dataView.DataContext as DataViewModel;\n                if (dataViewModel == null)\n                    return;\n\n                var row = cell.DataContext as IRow;\n                if (row == null)\n                    return;\n\n                var dataColumn = cell.Column as IRawDataColumn;\n                if (dataColumn == null)\n                    return;\n\n                var bookmark = new BookmarkViewModel() {\n                    SheetName = dataViewModel.SelectedSheetName,\n                    Key = row.Key,\n                    RowDefault = row.ToString(),\n                    ColumnName = dataColumn.Column?.Name,\n                    ColumnIndex = dataColumn.Column?.Index\n                };\n\n                if (dataViewModel.Bookmarks.Contains(bookmark)) {\n                    dataViewModel.Bookmarks.Remove(bookmark);\n                    return;\n                }\n\n                dataViewModel.Bookmarks.Add(bookmark);\n            }\n        }\n\n        private DataGridColumnHeader GetClickedHeader(MouseButtonEventArgs e)\n        {\n            DependencyObject dep = (DependencyObject)e.OriginalSource;\n\n            // iteratively traverse the visual tree\n            while ((dep != null) &&\n            !(dep is DataGridCell) &&\n            !(dep is DataGridColumnHeader))\n            {\n                dep = VisualTreeHelper.GetParent(dep);\n            }\n\n            return dep as DataGridColumnHeader;\n        }\n\n        protected override void OnMouseDoubleClick(MouseButtonEventArgs e) {\n            var cell = GetClickedCell(e);\n            if (cell == null)\n                return;\n\n            var navigatable = cell.Column as INavigatable;\n            if (navigatable == null)\n                return;\n\n            var mainWindow = WpfHelper.FindParent<MainWindow>(cell);\n            if (mainWindow == null)\n                return;\n\n            var mainViewModel = mainWindow.DataContext as MainViewModel;\n            if (mainViewModel == null)\n                return;\n\n            var row = navigatable.OnNavigate(cell, e);\n            if (row == null)\n                return;\n\n            mainViewModel.Data.SelectedSheetName = row.Sheet.Name;\n\n            SelectRow(row, null);\n        }\n\n        private static DataGridCell GetClickedCell(MouseButtonEventArgs e) {\n            if (e.OriginalSource is FrameworkElement source)\n                return WpfHelper.FindParent<DataGridCell>(source);\n\n            return null;\n        }\n\n        public void SelectRow(IRow row, int? columnIndex) {\n            this.SelectedItem = row;\n            this.UpdateLayout();\n\n            if (this.SelectedItem == null)\n                return;\n\n            DataGridColumn selectedColumn = null;\n            if (columnIndex != null)\n                selectedColumn = (DataGridColumn)this.Columns.OfType<IRawDataColumn>().FirstOrDefault(c => c.Column?.Index == columnIndex);\n\n            this.ScrollIntoView(this.SelectedItem, selectedColumn);\n        }\n    }\n}\n"
  },
  {
    "path": "Godbert/Controls/RawDataGridColorColumn.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\nusing System.Windows;\nusing System.Windows.Controls;\nusing System.Windows.Data;\nusing System.Windows.Input;\nusing System.Windows.Media;\nusing System.ComponentModel;\n\nusing SaintCoinach.Ex.Relational;\nusing SaintCoinach.Ex;\n\nnamespace Godbert.Controls {\n    public class RawDataGridColorColumn : DataGridBoundColumn, IRawDataColumn {\n        private RelationalColumn _Column;\n        private ContextMenu _ContextMenu;\n\n        public RawDataGridColorColumn(RelationalColumn column) {\n            _Column = column;\n\n            _ContextMenu = new ContextMenu();\n            var mi = new MenuItem {\n                Header = \"Copy\",\n            };\n            mi.Click += OnCopyClick;\n            _ContextMenu.Items.Add(mi);\n        }\n        void OnCopyClick(object sender, RoutedEventArgs e) {\n            var fe = sender as FrameworkElement;\n            if (fe == null)\n                return;\n\n            var ctx = fe.DataContext;\n            if (!(ctx is System.Drawing.Color))\n                return;\n\n            var src = (System.Drawing.Color)ctx;\n            System.Windows.Clipboard.SetDataObject(string.Format(\"#{0:X8}\", src.ToArgb()));\n        }\n\n        protected override FrameworkElement GenerateEditingElement(DataGridCell cell, object dataItem) {\n            throw new NotImplementedException();\n        }\n        protected override FrameworkElement GenerateElement(DataGridCell cell, object dataItem) {\n            Border obj = cell != null ? cell.Content as Border : null;\n            if (obj == null) {\n                obj = new Border {\n                    HorizontalAlignment = HorizontalAlignment.Stretch,\n                    VerticalAlignment = VerticalAlignment.Stretch,\n                };\n                obj.SetBinding(Border.BackgroundProperty, new Binding {\n                    Converter = ColorConverterInstance\n                });\n                obj.ContextMenu = _ContextMenu;\n            }\n\n            var bind = Binding;\n            if (bind == null)\n                BindingOperations.ClearBinding(obj, Border.DataContextProperty);\n            else\n                BindingOperations.SetBinding(obj, Border.DataContextProperty, bind);\n\n            return obj;\n        }\n\n\n        private static readonly ColorConverter ColorConverterInstance = new ColorConverter();\n        private class ColorConverter : System.Windows.Data.IValueConverter {\n\n            #region IValueConverter Members\n\n            public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) {\n                if (!(value is System.Drawing.Color))\n                    return null;\n\n                var src = (System.Drawing.Color)value;\n                return new SolidColorBrush(System.Windows.Media.Color.FromArgb(src.A, src.R, src.G, src.B));\n            }\n\n            public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) {\n                throw new NotImplementedException();\n            }\n\n            #endregion\n        }\n\n        #region IRawDataColumn Members\n        public RelationalColumn Column => _Column;\n\n        public IComparer<object> GetComparer(ListSortDirection direction) {\n            return new Comparer {\n                Column = _Column,\n                Direction = direction,\n            };\n        }\n\n\n        private class Comparer : IComparer<object> {\n            public ListSortDirection Direction;\n            public RelationalColumn Column;\n\n            #region IComparer<object> Members\n\n            public int Compare(object x, object y) {\n                if (Direction == ListSortDirection.Descending)\n                    return -InnerCompare(x, y);\n                return InnerCompare(x, y);\n            }\n\n            private int InnerCompare(object x, object y) {\n                var rx = x as IRow;\n                var ry = y as IRow;\n                if (rx == ry)\n                    return 0;\n                if (rx == null)\n                    return -1;\n                if (ry == null)\n                    return 1;\n\n                var vx = rx[Column.Index];\n                var vy = ry[Column.Index];\n\n                if (vx == vy)\n                    return 0;\n                if (!(vx is System.Drawing.Color))\n                    return -1;\n                if (!(vy is System.Drawing.Color))\n                    return 1;\n\n                var cx = (System.Drawing.Color)vx;\n                var cy = (System.Drawing.Color)vy;\n\n                return cx.ToArgb().CompareTo(cy.ToArgb());\n            }\n\n            #endregion\n        }\n        #endregion\n    }\n}\n"
  },
  {
    "path": "Godbert/Controls/RawDataGridImageColumn.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\nusing System.Windows;\nusing System.Windows.Controls;\nusing System.Windows.Data;\n\nusing System.ComponentModel;\nusing SaintCoinach.Ex.Relational;\nusing SaintCoinach.Ex;\n\nnamespace Godbert.Controls {\n    public class RawDataGridImageColumn : DataGridBoundColumn, IRawDataColumn {\n        private RelationalColumn _Column;\n        private ContextMenu _ContextMenu;\n\n        public RawDataGridImageColumn(RelationalColumn column) {\n            _Column = column;\n\n            _ContextMenu = new ContextMenu();\n            var mi = new MenuItem {\n                Header = \"Save image\",\n            };\n            mi.Click += OnSaveImageClick;\n            _ContextMenu.Items.Add(mi);\n        }\n\n        void OnSaveImageClick(object sender, RoutedEventArgs e) {\n            var fe = sender as FrameworkElement;\n            if (fe == null)\n                return;\n\n            var ctx = fe.DataContext as IRelationalRow;\n            if (ctx == null)\n                return;\n\n            var img = ctx[_Column.Index] as SaintCoinach.Imaging.ImageFile;\n            if (img == null)\n                return;\n\n            var dlg = new Microsoft.Win32.SaveFileDialog {\n                DefaultExt = \".png\",\n                Filter = \"PNG Files (*.png)|*.png\",\n                AddExtension = true,\n                OverwritePrompt = true,\n                FileName = SetExt(img.Path.Split('/').Last(), \"png\"),\n            };\n\n            if (dlg.ShowDialog().GetValueOrDefault(false))\n                img.GetImage().Save(dlg.FileName);\n        }\n        private static string SetExt(string orig, string ext) {\n            var sb = new StringBuilder(orig);\n\n            var idx = orig.LastIndexOf('.');\n            if (idx > 0)\n                sb.Remove(idx, sb.Length - idx);\n\n            sb.Append('.');\n            sb.Append(ext);\n            return sb.ToString();\n        }\n\n        protected override FrameworkElement GenerateEditingElement(DataGridCell cell, object dataItem) {\n            throw new NotImplementedException();\n        }\n        protected override FrameworkElement GenerateElement(DataGridCell cell, object dataItem) {\n            Image obj = cell != null ? cell.Content as Image : null;\n            if (obj == null) {\n                obj = new Image {\n                    Stretch = System.Windows.Media.Stretch.Uniform,\n                    ContextMenu = _ContextMenu,\n                };\n                AttachedImage.SetIsSizeRestricted(obj, true);\n            }\n\n            var bind = Binding;\n            if (bind == null)\n                BindingOperations.ClearBinding(obj, AttachedImage.ImageProperty);\n            else\n                BindingOperations.SetBinding(obj, AttachedImage.ImageProperty, bind);\n\n            return obj;\n        }\n\n        #region IRawDataColumn Members\n        public RelationalColumn Column => _Column;\n\n        public IComparer<object> GetComparer(ListSortDirection direction) {\n            return new Comparer {\n                Column = _Column,\n                Direction = direction,\n            };\n        }\n\n\n        private class Comparer : IComparer<object> {\n            public ListSortDirection Direction;\n            public RelationalColumn Column;\n\n            #region IComparer<object> Members\n\n            public int Compare(object x, object y) {\n                if (Direction == ListSortDirection.Descending)\n                    return -InnerCompare(x, y);\n                return InnerCompare(x, y);\n            }\n\n            private int InnerCompare(object x, object y) {\n                var rx = x as IRow;\n                var ry = y as IRow;\n                if (rx == ry)\n                    return 0;\n                if (rx == null)\n                    return -1;\n                if (ry == null)\n                    return 1;\n\n                var vx = rx[Column.Index] as SaintCoinach.Imaging.ImageFile;\n                var vy = ry[Column.Index] as SaintCoinach.Imaging.ImageFile;\n\n                if (vx == vy)\n                    return 0;\n                if (vx == null)\n                    return -1;\n                if (vy == null)\n                    return 1;\n\n                return vx.Path.CompareTo(vy.Path);\n            }\n\n            #endregion\n        }\n        #endregion\n    }\n}\n"
  },
  {
    "path": "Godbert/Controls/RawDataGridKeyColumn.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nusing System.ComponentModel;\nusing System.Windows;\nusing System.Windows.Controls;\nusing System.Windows.Data;\nusing SaintCoinach.Ex.Relational;\n\nnamespace Godbert.Controls {\n    public class RawDataGridKeyColumn : DataGridTextColumn, IRawDataColumn {\n        public RawDataGridKeyColumn(string keyPath) {\n\n            this.Header = \"Key\";\n            this.IsReadOnly = true;\n            this.Binding = new Binding(keyPath) { Mode = BindingMode.OneWay };\n        }\n\n        #region IRawDataColumn Members\n        public RelationalColumn Column => null;\n\n        public IComparer<object> GetComparer(System.ComponentModel.ListSortDirection direction) {\n            return new Comparer {\n                Direction = direction,\n            };\n        }\n\n\n\n        private class Comparer : IComparer<object> {\n            public ListSortDirection Direction;\n\n            #region IComparer<object> Members\n\n            public int Compare(object x, object y) {\n                if (Direction == ListSortDirection.Descending)\n                    return -InnerCompare(x, y);\n                return InnerCompare(x, y);\n            }\n\n            private int InnerCompare(object x, object y) {\n                var rx = x as IRelationalRow;\n                var ry = y as IRelationalRow;\n                if (rx == ry)\n                    return 0;\n                if (rx == null)\n                    return -1;\n                if (ry == null)\n                    return 1;\n\n                return rx.Key.CompareTo(ry.Key);\n            }\n\n            #endregion\n        }\n        #endregion\n    }\n}\n"
  },
  {
    "path": "Godbert/Controls/RawDataGridTextColumn.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\nusing System.Windows;\nusing System.Windows.Controls;\n\nusing System.ComponentModel;\nusing SaintCoinach.Ex.Relational;\nusing SaintCoinach.Ex;\n\nnamespace Godbert.Controls {\n    public class RawDataGridTextColumn : DataGridTextColumn, IRawDataColumn, INavigatable {\n        private RelationalColumn _Column;\n        private ContextMenu _ContextMenu;\n\n        public RawDataGridTextColumn(RelationalColumn column) {\n            _Column = column;\n\n            _ContextMenu = new ContextMenu();\n            var mi = new MenuItem {\n                Header = \"Copy\",\n            };\n            mi.Click += OnCopyClick;\n            _ContextMenu.Items.Add(mi);\n        }\n        void OnCopyClick(object sender, RoutedEventArgs e) {\n            var fe = sender as FrameworkElement;\n            if (fe == null)\n                return;\n\n            var ctx = fe.DataContext as IRelationalRow;\n            if (ctx == null)\n                return;\n\n            var val = ctx[_Column.Index];\n            if (val == null)\n                return;\n\n            System.Windows.Clipboard.SetDataObject(val.ToString());\n        }\n\n        public IRow OnNavigate(object sender, RoutedEventArgs e) {\n            var fe = sender as FrameworkElement;\n            if (fe == null)\n                return null;\n\n            var ctx = fe.DataContext as IRow;\n            if (ctx == null)\n                return null;\n\n            return ctx[_Column.Index] as IRow;\n        }\n \n        protected override FrameworkElement GenerateElement(DataGridCell cell, object dataItem) {\n            var ele = base.GenerateElement(cell, dataItem);\n            ele.ContextMenu = _ContextMenu;\n            return ele;\n        }\n\n        #region IRawDataColumn Members\n        public RelationalColumn Column => _Column;\n\n        public IComparer<object> GetComparer(ListSortDirection direction) {\n            return new Comparer {\n                Column = _Column,\n                Direction = direction,\n            };\n        }\n\n\n        private class Comparer : IComparer<object> {\n            private NaturalComparer _NaturalComparer = new NaturalComparer(StringComparer.OrdinalIgnoreCase);\n            public ListSortDirection Direction;\n            public RelationalColumn Column;\n\n            #region IComparer<object> Members\n\n            public int Compare(object x, object y) {\n                if (Direction == ListSortDirection.Descending)\n                    return -InnerCompare(x, y);\n                return InnerCompare(x, y);\n            }\n\n            private int InnerCompare(object x, object y) {\n                var rx = x as IRow;\n                var ry = y as IRow;\n                if (rx == ry)\n                    return 0;\n                if (rx == null)\n                    return -1;\n                if (ry == null)\n                    return 1;\n\n\n                var vx = ColumnFactory.ForceRaw || RawDataGrid.ColumnSetToRaw[Column.Index] ? rx.GetRaw(Column.Index) : rx[Column.Index];\n                var vy = ColumnFactory.ForceRaw || RawDataGrid.ColumnSetToRaw[Column.Index] ? ry.GetRaw(Column.Index) : ry[Column.Index];\n\n                if (vx == vy)\n                    return 0;\n                if (vx == null)\n                    return -1;\n                if (vy == null)\n                    return 1;\n\n                var sx = vx.ToString();\n                var sy = vy.ToString();\n\n                return _NaturalComparer.Compare(sx, sy);\n            }\n\n            #endregion\n        }\n        #endregion\n    }\n}\n"
  },
  {
    "path": "Godbert/Controls/RawDataItemsSource.cs",
    "content": "﻿using System;\nusing System.Collections;\nusing System.Collections.Concurrent;\nusing System.Collections.Generic;\nusing System.Collections.ObjectModel;\nusing System.Collections.Specialized;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\nusing SaintCoinach.Ex;\nusing SaintCoinach.Ex.Relational;\n\nnamespace Godbert.Controls {\n    public class RawDataItemsSource : IEnumerable, INotifyCollectionChanged {\n        #region Fields\n        private IRelationalSheet _Sheet;\n        private IComparer<object> _Comparer;\n        private object[] _Items;\n        private string _Filter;\n        private Tuple<IRow, string>[] _RowSearchIndex;\n        #endregion\n\n        #region Properties\n        public IComparer<object> Comparer {\n            get { return _Comparer; }\n            set {\n                _Comparer = value;\n                _Items = null;\n                CollectionChanged?.Invoke(this, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));\n            }\n        }\n        public string Filter {\n            get { return _Filter; }\n            set {\n                _Filter = value;\n                _Items = null;\n                CollectionChanged?.Invoke(this, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));\n            }\n        }\n        public IEnumerable Items {\n            get {\n                if (_Items != null)\n                    return Items;\n\n                if (_Sheet.Header.Variant == 1)\n                    return GetVariant1Items();\n                else\n                    return GetVariant2Items();\n            }\n        }\n\n        private IEnumerable GetVariant1Items()\n        {\n            if (_Comparer == null && _Filter == null)\n                return _Sheet;\n\n            _Items = FilterAndCompare(_Sheet.Cast<object>()).ToArray();\n            return _Items;\n        }\n\n        private IEnumerable GetVariant2Items()\n        {\n            var rows = _Sheet.Cast<SaintCoinach.Xiv.XivSubRow>();\n            _Items = FilterAndCompare(rows).ToArray();\n            return _Items;\n        }\n\n        private IEnumerable<object> FilterAndCompare(IEnumerable<object> results)\n        {\n            if (_Filter != null) {\n                var rows = results.OfType<IRow>().ToArray();\n                if (_RowSearchIndex == null)\n                    BuildSearchIndex(rows);\n                results = FilterMatchingRows();\n            }\n            if (_Comparer != null)\n                results = results.OrderBy(o => o, Comparer);\n            return results;\n        }\n\n        #endregion\n\n        #region Filter\n\n        private void BuildSearchIndex(IRow[] rows) {\n            if (rows.Length == 0)\n                return;\n\n            var start = DateTime.Now;\n            System.Diagnostics.Debug.WriteLine($\"Rebuilding search index for {_Sheet.Name}...\");\n\n            var newIndex = new ConcurrentBag<Tuple<IRow, string>>();\n            var columns = rows[0].Sheet.Header.Columns.ToArray();\n\n            Parallel.ForEach(rows, row => {\n                var index = new StringBuilder();\n                index.Append(row.Key.ToString());\n                index.Append(\"||\");\n\n                foreach (var value in row.ColumnValues()) {\n                    if (value != null) {\n                        index.Append(value.ToString());\n                        index.Append(\"||\");\n                    }\n                }\n\n                newIndex.Add(Tuple.Create(row, index.ToString()));\n            });\n\n            _RowSearchIndex = newIndex.OrderBy(r => r.Item1.Key).ToArray();\n            System.Diagnostics.Debug.WriteLine($\"{_Sheet.Name} search index complete.  Elapsed {DateTime.Now - start}.\");\n        }\n\n        private IRow[] FilterMatchingRows() {\n            return _RowSearchIndex\n                .Where(r => r.Item2.IndexOf(_Filter, StringComparison.OrdinalIgnoreCase) >= 0)\n                .Select(r => r.Item1)\n                .ToArray();\n        }\n\n        #endregion\n\n        #region Constructor\n\n        public RawDataItemsSource(IRelationalSheet sheet) {\n            _Sheet = sheet;\n        }\n\n        #endregion\n\n        #region INotifyCollectionChanged Members\n\n        public event NotifyCollectionChangedEventHandler CollectionChanged;\n\n        #endregion\n\n        #region IEnumerable Members\n\n        public IEnumerator GetEnumerator() {\n            return Items.GetEnumerator();\n        }\n\n        #endregion\n    }\n}\n"
  },
  {
    "path": "Godbert/EngineHelper.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading;\nusing System.Threading.Tasks;\n\nnamespace Godbert {\n    using SaintCoinach.Graphics.Viewer;\n    public class EngineHelper {\n        public delegate IComponent ComponentFunction(Engine engine);\n        public delegate IComponent[] MultiComponentFunction(Engine engine);\n\n        #region Fields\n        private List<EngineInstance> _Instances = new List<EngineInstance>();\n        #endregion\n\n        #region Things\n        public EngineInstance AddToLast(string title, ComponentFunction func) { return AddToLast(title, (e) => new IComponent[] { func(e) }); }\n        public EngineInstance AddToLast(string title, MultiComponentFunction func) {\n            EngineInstance target = null;\n            lock (_Instances) {\n                if (_Instances.Count > 0)\n                    target = _Instances[_Instances.Count - 1];\n            }\n            if (target == null)\n                return OpenInNew(title, func);\n\n            target.AddComponent(func);\n            target.SetTitle(target.Engine.Form.Text + \", \" + title);\n            return target;\n        }\n        public EngineInstance ReplaceInLast(string title, ComponentFunction func) { return ReplaceInLast(title, (e) => new IComponent[] { func(e) }); }\n        public EngineInstance ReplaceInLast(string title, MultiComponentFunction func) {\n            EngineInstance target = null;\n            lock (_Instances) {\n                if (_Instances.Count > 0)\n                    target = _Instances[_Instances.Count - 1];\n            }\n            if (target == null)\n                return OpenInNew(title, func);\n            \n            target.ReplaceComponents(func);\n            target.SetTitle(title);\n            return target;\n        }\n        public EngineInstance OpenInNew(string title, ComponentFunction func) { return OpenInNew(title, (e) => new IComponent[] { func(e) }); }\n        public EngineInstance OpenInNew(string title, MultiComponentFunction func) {\n            var instance = new EngineInstance(title);\n            lock (_Instances)\n                _Instances.Add(instance);\n            instance.Stopped += OnInstanceStopped;\n            \n            instance.AddComponent(func);\n            instance.RunAsync();\n\n            return instance;\n        }\n\n        public EngineInstance GetOrCreate(string title) {\n            EngineInstance target = null;\n            lock (_Instances) {\n                if (_Instances.Count > 0)\n                    target = _Instances[_Instances.Count - 1];\n            }\n            if (target == null)\n                return OpenNew(title);\n            return target;\n        }\n        public EngineInstance OpenNew(string title) {\n            var instance = new EngineInstance(title);\n            lock (_Instances)\n                _Instances.Add(instance);\n            instance.Stopped += OnInstanceStopped;\n\n            instance.RunAsync();\n\n            return instance;\n        }\n\n        void OnInstanceStopped(object sender, EventArgs e) {\n            lock (_Instances)\n                _Instances.Remove(sender as EngineInstance);\n        }\n        #endregion\n    }\n}\n"
  },
  {
    "path": "Godbert/EngineInstance.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading;\nusing System.Threading.Tasks;\n\nnamespace Godbert {\n    using SaintCoinach.Graphics.Viewer;\n    public class EngineInstance {\n        #region Helper class\n        class ComponentInjector : IUpdateableComponent, IDrawable3DComponent, IContentComponent {\n            internal List<EngineHelper.MultiComponentFunction> AddQueue = new List<EngineHelper.MultiComponentFunction>();\n            internal EngineHelper.MultiComponentFunction Replacement;\n            internal string SetTitle;\n\n            private FormEngine _Engine;\n            private ComponentContainer _InnerContainer = new ComponentContainer();\n\n            public ComponentInjector(FormEngine engine) {\n                _Engine = engine;\n            }\n\n            #region IUpdateableComponent Members\n\n            public bool IsEnabled {\n                get { return true; }\n            }\n\n            public void Update(EngineTime engineTime) {\n                if (!string.IsNullOrWhiteSpace(SetTitle)) {\n                    _Engine.Form.Text = SetTitle;\n                    SetTitle = null;\n                }\n                if (Replacement != null) {\n                    lock (AddQueue)\n                        AddQueue.Clear();\n                    _InnerContainer.Clear();\n                    var l = Replacement(_Engine);\n                    foreach (var c in l) {\n                        if (c != null)\n                            _InnerContainer.Add(c);\n                    }\n                    Replacement = null;\n                }\n                EngineHelper.MultiComponentFunction[] toAdd;\n                lock (AddQueue) {\n                    toAdd = AddQueue.ToArray();\n                    AddQueue.Clear();\n                }\n                foreach (var f in toAdd) {\n                    var l = f(_Engine);\n                    foreach (var c in l) {\n                        if (c != null)\n                            _InnerContainer.Add(c);\n                    }\n                }\n                \n\n                _InnerContainer.Update(engineTime);\n            }\n\n            #endregion\n\n            #region IDrawable3DComponent Members\n\n            public bool IsVisible {\n                get { return true; }\n            }\n\n            public void Draw(EngineTime time, ref SharpDX.Matrix world, ref SharpDX.Matrix view, ref SharpDX.Matrix projection) {\n                _InnerContainer.Draw(time, ref world, ref view, ref projection);\n            }\n\n            #endregion\n\n            #region IContentComponent Members\n            private bool _IsLoaded;\n            public bool IsLoaded {\n                get { return _IsLoaded; }\n                private set { _IsLoaded = value; }\n            }\n\n            public void LoadContent() {\n                _InnerContainer.LoadContent();\n                _IsLoaded = true;\n            }\n\n            public void UnloadContent() {\n                _IsLoaded = false;\n                _InnerContainer.UnloadContent();\n            }\n\n            #endregion\n        }\n        #endregion\n\n        #region Fields\n        private ComponentInjector _Injector;\n        #endregion\n\n        #region Properties\n        public FormEngine Engine { get; private set; }\n        #endregion\n\n        #region Event\n        public event EventHandler Stopped;\n        #endregion\n\n        #region Constructor\n        public EngineInstance(string title) {\n            Engine = new FormEngine(title);\n            Engine.Components.Add(_Injector = new ComponentInjector(Engine));\n        }\n        #endregion\n\n        #region Things\n        public void SetTitle(string newTitle) {\n            _Injector.SetTitle = newTitle;\n        }\n        public void AddComponent(EngineHelper.MultiComponentFunction component) {\n            lock (_Injector.AddQueue)\n                _Injector.AddQueue.Add(component);\n        }\n        public void ReplaceComponents(EngineHelper.MultiComponentFunction newComponents) {\n            _Injector.Replacement = newComponents;\n        }\n        public void RunAsync() {\n            var t = new Thread(this.Run);\n            t.IsBackground = true;\n            t.Name = \"Renderer\";\n            t.Start();\n        }\n\n        private void Run() {\n            try {\n                Engine.Run();\n            } catch (Exception e) {\n                System.Windows.MessageBox.Show(e.ToString(), \"Engine failure\", System.Windows.MessageBoxButton.OK, System.Windows.MessageBoxImage.Error);\n                System.Diagnostics.Debug.WriteLine(string.Format(\"Engine failure: {0}\", e));\n            } finally {\n                Engine = null;\n                _Injector = null;\n                GC.Collect();\n                Stopped?.Invoke(this, EventArgs.Empty);\n            }\n        }\n        #endregion\n    }\n}\n"
  },
  {
    "path": "Godbert/Godbert.csproj",
    "content": "﻿<Project Sdk=\"Microsoft.NET.Sdk\">\n  <PropertyGroup>\n    <ProjectGuid>{6A5DA7FF-791E-4A43-BF65-B6942917F7D9}</ProjectGuid>\n    <ProjectTypeGuids>{60dc8134-eba5-43b8-bcc9-bb4bc16c2548};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>\n    <OutputType>WinExe</OutputType>\n    <TargetFramework>net7.0-windows</TargetFramework>\n    <AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>\n    <ApplicationRevision>0</ApplicationRevision>\n    <ApplicationVersion>1.0.0.%2a</ApplicationVersion>\n    <BootstrapperEnabled>true</BootstrapperEnabled>\n    <AssemblyTitle>Godbert</AssemblyTitle>\n    <Product>Godbert</Product>\n    <Copyright>Copyright © Rogueadyn 2015</Copyright>\n    <OutputPath>bin\\$(Configuration)\\</OutputPath>\n    <UseWpf>true</UseWpf>\n    <PlatformTarget>x86</PlatformTarget>\n  </PropertyGroup>\n  <PropertyGroup Condition=\" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' \">\n    <DebugType>full</DebugType>\n  </PropertyGroup>\n  <PropertyGroup Condition=\" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' \">\n    <DebugType>pdbonly</DebugType>\n  </PropertyGroup>\n  <PropertyGroup>\n    <ApplicationIcon>Godbert.ico</ApplicationIcon>\n  </PropertyGroup>\n  <PropertyGroup>\n    <StartupObject />\n  </PropertyGroup>\n  <ItemGroup>\n    <PackageReference Include=\"Newtonsoft.Json\" Version=\"13.0.3\" />\n    <PackageReference Include=\"Ookii.Dialogs.Wpf\" Version=\"5.0.1\" />\n    <PackageReference Include=\"SharpDX\" Version=\"4.2.0\" />\n  </ItemGroup>\n  <ItemGroup>\n    <ProjectReference Include=\"..\\SaintCoinach.Graphics.Viewer\\SaintCoinach.Graphics.Viewer.csproj\" />\n    <ProjectReference Include=\"..\\SaintCoinach\\SaintCoinach.csproj\" />\n  </ItemGroup>\n  <ItemGroup>\n    <Resource Include=\"Godbert.ico\" />\n  </ItemGroup>\n</Project>"
  },
  {
    "path": "Godbert/MainWindow.xaml",
    "content": "﻿<Window x:Class=\"Godbert.MainWindow\"\n        xmlns=\"http://schemas.microsoft.com/winfx/2006/xaml/presentation\"\n        xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\"\n        xmlns:vm=\"clr-namespace:Godbert.ViewModels\"\n        xmlns:views=\"clr-namespace:Godbert.Views\"\n        xmlns:ex=\"clr-namespace:SaintCoinach.Ex;assembly=SaintCoinach\"\n        Title=\"Godbert\" Height=\"800\" Width=\"1024\" Icon=\"Godbert.ico\">\n    <Window.DataContext>\n        <vm:MainViewModel />\n    </Window.DataContext>\n    <DockPanel LastChildFill=\"True\">\n        <Menu DockPanel.Dock=\"Top\">\n            <MenuItem Header=\"Language\">\n                <MenuItem Header=\"English\"\n                          IsChecked=\"{Binding Path=IsEnglish, Mode=OneWay}\"\n                          Command=\"{Binding Path=LanguageCommand}\"\n                          CommandParameter=\"{x:Static ex:Language.English}\" />\n                <MenuItem Header=\"Japanese\"\n                          IsChecked=\"{Binding Path=IsJapanese, Mode=OneWay}\"\n                          Command=\"{Binding Path=LanguageCommand}\"\n                          CommandParameter=\"{x:Static ex:Language.Japanese}\" />\n                <MenuItem Header=\"German\"\n                          IsChecked=\"{Binding Path=IsGerman, Mode=OneWay}\"\n                          Command=\"{Binding Path=LanguageCommand}\"\n                          CommandParameter=\"{x:Static ex:Language.German}\" />\n                <MenuItem Header=\"French\"\n                          IsChecked=\"{Binding Path=IsFrench, Mode=OneWay}\"\n                          Command=\"{Binding Path=LanguageCommand}\"\n                          CommandParameter=\"{x:Static ex:Language.French}\"  />\n                <MenuItem Header=\"ChineseSimplified\"\n                          IsChecked=\"{Binding Path=IsChineseSimplified, Mode=OneWay}\"\n                          Command=\"{Binding Path=LanguageCommand}\"\n                          CommandParameter=\"{x:Static ex:Language.ChineseSimplified}\"  />\n                <MenuItem Header=\"Korean\"\n                          IsChecked=\"{Binding Path=IsKorean, Mode=OneWay}\"\n                          Command=\"{Binding Path=LanguageCommand}\"\n                          CommandParameter=\"{x:Static ex:Language.Korean}\"  />\n            </MenuItem>\n            <MenuItem Header=\"Various\">\n                <MenuItem Header=\"New Window\"\n                          Command=\"{Binding Path=NewWindowCommand}\" />\n                <Separator />\n                <MenuItem Header=\"Change game location\"\n                          Command=\"{Binding Path=GameLocationCommand}\" />\n            </MenuItem>\n            <MenuItem Header=\"Settings\">\n                <MenuItem Header=\"Show Offsets\"\n                          IsChecked=\"{Binding Path=ShowOffsets, Mode=OneWay}\"\n                          Command=\"{Binding Path=ShowOffsetsCommand}\" />\n                <MenuItem Header=\"Sort by Offsets\"\n                          IsChecked=\"{Binding Path=SortByOffsets, Mode=OneWay}\"\n                          Command=\"{Binding Path=SortByOffsetsCommand}\" />\n            </MenuItem>\n        </Menu>\n        <TabControl>\n            <TabItem Header=\"Data\" DataContext=\"{Binding Path=Data}\">\n                <views:DataView />\n            </TabItem>\n            <TabItem Header=\"3D\">\n                <TabControl>\n                    <TabItem Header=\"Equipment\" DataContext=\"{Binding Equipment}\">\n                        <views:EquipmentView />\n                    </TabItem>\n                    <TabItem Header=\"Furniture\" DataContext=\"{Binding Furniture}\">\n                        <views:FurnitureView />\n                    </TabItem>\n                    <TabItem Header=\"Monsters\" DataContext=\"{Binding Monsters}\">\n                        <views:MonstersView />\n                    </TabItem>\n                    <TabItem Header=\"Territories\" DataContext=\"{Binding Territories}\">\n                        <views:TerritoryView />\n                    </TabItem>\n                    <TabItem Header=\"Demi-humans\" DataContext=\"{Binding Demihuman}\">\n                        <views:DemihumansView />\n                    </TabItem>\n                    <TabItem Header=\"Help\">\n                        <views:Help3DView />\n                    </TabItem>\n                </TabControl>\n            </TabItem>\n        </TabControl>\n    </DockPanel>\n</Window>\n"
  },
  {
    "path": "Godbert/MainWindow.xaml.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.ComponentModel;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\nusing System.Windows;\nusing System.Windows.Controls;\nusing System.Windows.Data;\nusing System.Windows.Documents;\nusing System.Windows.Input;\nusing System.Windows.Media;\nusing System.Windows.Media.Imaging;\nusing System.Windows.Navigation;\nusing System.Windows.Shapes;\n\nnamespace Godbert {\n    /// <summary>\n    /// Interaction logic for MainWindow.xaml\n    /// </summary>\n    public partial class MainWindow : Window {\n        public MainWindow() {\n            InitializeComponent();\n\n            var settings = Settings.Default;\n            if (settings.MainWindowWidth > 0)\n                Width = Settings.Default.MainWindowWidth;\n            if (settings.MainWindowHeight > 0)\n                Height = Settings.Default.MainWindowHeight;\n            if (settings.MainWindowLeft > 0)\n                Left = Settings.Default.MainWindowLeft;\n            if (settings.MainWindowTop > 0)\n                Top = Settings.Default.MainWindowTop;\n        }\n\n        protected override void OnClosing(CancelEventArgs e) {\n            base.OnClosing(e);\n\n            Settings.Default.MainWindowHeight = Height;\n            Settings.Default.MainWindowWidth = Width;\n            Settings.Default.MainWindowLeft = Left;\n            Settings.Default.MainWindowTop = Top;\n        }\n    }\n\n}\n"
  },
  {
    "path": "Godbert/Models/ModelCharaHierarchy.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nusing SaintCoinach.Xiv;\n\nnamespace Godbert.Models {\n    public class ModelCharaHierarchy : IEnumerable<ModelCharaMain> {\n        #region Properties\n        public string MainFormat { get; set; }\n        public string SubFormat { get; set; }\n        public string VariantFormat { get; set; }\n        #endregion\n\n        #region Constructor\n        public ModelCharaHierarchy(string mainFormat, string subFormat, string variantFormat) {\n            this.MainFormat = mainFormat;\n            this.SubFormat = subFormat;\n            this.VariantFormat = variantFormat;\n        }\n        #endregion\n\n        #region Fields\n        private SortedDictionary<int, ModelCharaMain> _Items = new SortedDictionary<int, ModelCharaMain>();\n        #endregion\n\n        #region Collection\n        public void Add(ModelChara modelChara) {\n            Add(modelChara.ModelKey, modelChara.BaseKey, modelChara.Variant);\n        }\n        public void Add(int main, int sub, int variant) {\n            ModelCharaMain mcm;\n            if (!_Items.TryGetValue(main, out mcm))\n                _Items.Add(main, mcm = new ModelCharaMain(this, string.Format(MainFormat, main), main));\n            mcm.Add(sub, variant);\n        }\n        public bool Contains(int main, int sub, int variant) {\n            ModelCharaMain mcm;\n            if (!_Items.TryGetValue(main, out mcm))\n                return false;\n            return mcm.Contains(sub, variant);\n        }\n        public void Clear() {\n            _Items.Clear();\n        }\n        #endregion\n\n        #region IEnumerable<ModelCharaMain> Members\n\n        public IEnumerator<ModelCharaMain> GetEnumerator() {\n            return _Items.Values.GetEnumerator();\n        }\n\n        #endregion\n\n        #region IEnumerable Members\n\n        System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() {\n            return GetEnumerator();\n        }\n\n        #endregion\n    }\n}\n"
  },
  {
    "path": "Godbert/Models/ModelCharaMain.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace Godbert.Models {\n    public class ModelCharaMain : IEnumerable<ModelCharaSub> {\n        #region Properties\n        public int Value { get; set; }\n        public string DisplayName { get; set; }\n        public ModelCharaHierarchy Parent { get; private set; }\n        #endregion\n\n        #region Constructor\n        public ModelCharaMain(ModelCharaHierarchy parent, string displayName, int value) {\n            this.Parent = parent;\n            this.Value = value;\n            this.DisplayName = displayName;\n        }\n        #endregion\n\n        #region Fields\n        private SortedDictionary<int, ModelCharaSub> _Items = new SortedDictionary<int, ModelCharaSub>();\n        #endregion\n\n        #region Collection\n        public void Add(int sub, int variant) {\n            ModelCharaSub mcs;\n            if (!_Items.TryGetValue(sub, out mcs))\n                _Items.Add(sub, mcs = new ModelCharaSub(this, string.Format(Parent.SubFormat, sub), sub));\n            mcs.Add(variant);\n        }\n        public bool Contains(int sub, int variant) {\n            ModelCharaSub mcs;\n            if (!_Items.TryGetValue(sub, out mcs))\n                return false;\n            return mcs.Contains(variant);\n        }\n        public void Clear() {\n            _Items.Clear();\n        }\n        #endregion\n\n        #region IEnumerable<ModelCharaMain> Members\n\n        public IEnumerator<ModelCharaSub> GetEnumerator() {\n            return _Items.Values.GetEnumerator();\n        }\n\n        #endregion\n\n        #region IEnumerable Members\n\n        System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() {\n            return GetEnumerator();\n        }\n\n        #endregion\n\n        public override string ToString() {\n            return DisplayName;\n        }\n    }\n}\n"
  },
  {
    "path": "Godbert/Models/ModelCharaSub.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace Godbert.Models {\n    public class ModelCharaSub : IEnumerable<ModelCharaVariant> {\n        #region Properties\n        public int Value { get; set; }\n        public string DisplayName { get; set; }\n        public ModelCharaMain Parent { get; private set; }\n        #endregion\n\n        #region Constructor\n        public ModelCharaSub(ModelCharaMain parent, string displayName, int value) {\n            this.Parent = parent;\n            this.Value = value;\n            this.DisplayName = displayName;\n        }\n        #endregion\n\n        #region Fields\n        private SortedDictionary<int, ModelCharaVariant> _Items = new SortedDictionary<int, ModelCharaVariant>();\n        #endregion\n\n        #region Collection\n        public void Add(int variant) {\n            if (!_Items.ContainsKey(variant))\n                _Items.Add(variant, new ModelCharaVariant(this, string.Format(Parent.Parent.VariantFormat, variant), variant));\n        }\n        public bool Contains(int variant) {\n            return _Items.ContainsKey(variant);\n        }\n        public void Clear() {\n            _Items.Clear();\n        }\n        #endregion\n\n        #region IEnumerable<ModelCharaMain> Members\n\n        public IEnumerator<ModelCharaVariant> GetEnumerator() {\n            return _Items.Values.GetEnumerator();\n        }\n\n        #endregion\n\n        #region IEnumerable Members\n\n        System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() {\n            return GetEnumerator();\n        }\n\n        #endregion\n\n        public override string ToString() {\n            return string.Format(\"{0} / {1}\", Parent, DisplayName);\n        }\n    }\n}\n"
  },
  {
    "path": "Godbert/Models/ModelCharaVariant.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace Godbert.Models {\n    public class ModelCharaVariant {\n        #region Properties\n        public int Value { get; set; }\n        public string DisplayName { get; set; }\n        public ModelCharaSub Parent { get; private set; }\n        #endregion\n\n        #region Constructor\n        public ModelCharaVariant(ModelCharaSub parent, string displayName, int value) {\n            this.Parent = parent;\n            this.Value = value;\n            this.DisplayName = displayName;\n        }\n        #endregion\n\n        public override string ToString() {\n            return string.Format(\"{0} / {1}\", Parent, DisplayName);\n        }\n    }\n}\n"
  },
  {
    "path": "Godbert/NaturalComparer.cs",
    "content": "// Taken from http://www.codeproject.com/Articles/22517/Natural-Sort-Comparer\n// Modified to allow for different StringComparison\nusing System;\nusing System.Collections.Generic;\nusing System.Text.RegularExpressions;\n\nnamespace Godbert {\n    public class NaturalComparer : Comparer<string>, IDisposable {\n        private Dictionary<string, string[]> table;\n\n        public NaturalComparer() : this(StringComparer.Ordinal) { }\n        public NaturalComparer(StringComparer partsComparer) {\n            table = new Dictionary<string, string[]>();\n            this.PartsComparer = partsComparer;\n        }\n\n        public StringComparer PartsComparer { get; set; }\n\n        public void Dispose() {\n            table.Clear();\n            table = null;\n        }\n\n        public override int Compare(string x, string y) {\n            if (x == y) {\n                return 0;\n            }\n            if (x == null)\n                return -1;\n            if (y == null)\n                return 1;\n\n            string[] x1, y1;\n            if (!table.TryGetValue(x, out x1)) {\n                x1 = Regex.Split(x.Replace(\" \", \"\"), \"([0-9]+)\");\n                table.Add(x, x1);\n            }\n            if (!table.TryGetValue(y, out y1)) {\n                y1 = Regex.Split(y.Replace(\" \", \"\"), \"([0-9]+)\");\n                table.Add(y, y1);\n            }\n\n            for (int i = 0; i < x1.Length && i < y1.Length; i++) {\n                if (x1[i] != y1[i]) {\n                    // This always returned, modified to only return if not equal\n                    var partCmp = PartCompare(x1[i], y1[i]);\n                    if (partCmp != 0)\n                        return partCmp;\n                }\n            }\n            if (y1.Length > x1.Length) {\n                return 1;\n            } else if (x1.Length > y1.Length) {\n                return -1;\n            } else {\n                return 0;\n            }\n        }\n\n        private int PartCompare(string left, string right) {\n            int x, y;\n            if (!int.TryParse(left, out x) || !int.TryParse(right, out y))\n                return PartsComparer.Compare(left, right);\n\n            return x.CompareTo(y);\n        }\n    }\n}"
  },
  {
    "path": "Godbert/ObservableBase.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.ComponentModel;\nusing System.Linq;\nusing System.Linq.Expressions;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace Godbert {\n    public class ObservableBase : INotifyPropertyChanged {\n        #region INotifyPropertyChanged Members\n\n        public event PropertyChangedEventHandler PropertyChanged;\n\n        protected virtual void OnPropertyChanged(string propertyName) {\n            var h = PropertyChanged;\n            if (h != null)\n                h(this, new PropertyChangedEventArgs(propertyName));\n        }\n\n        protected virtual void OnPropertyChanged<TProperty>(Expression<Func<TProperty>> property) {\n            MemberExpression memberExpression;\n            if (property.Body is UnaryExpression) {\n                var unaryExpression = (UnaryExpression)property.Body;\n                memberExpression = (MemberExpression)unaryExpression.Operand;\n            } else {\n                memberExpression = (MemberExpression)property.Body;\n            }\n\n            OnPropertyChanged(memberExpression.Member.Name);\n        }\n\n        #endregion\n    }\n}\n"
  },
  {
    "path": "Godbert/Properties/AssemblyInfo.cs",
    "content": "﻿using System.Reflection;\nusing System.Resources;\nusing System.Runtime.CompilerServices;\nusing System.Runtime.InteropServices;\nusing System.Windows;\n\n// Setting ComVisible to false makes the types in this assembly not visible \n// to COM components.  If you need to access a type in this assembly from \n// COM, set the ComVisible attribute to true on that type.\n[assembly: ComVisible(false)]\n\n//In order to begin building localizable applications, set \n//<UICulture>CultureYouAreCodingWith</UICulture> in your .csproj file\n//inside a <PropertyGroup>.  For example, if you are using US english\n//in your source files, set the <UICulture> to en-US.  Then uncomment\n//the NeutralResourceLanguage attribute below.  Update the \"en-US\" in\n//the line below to match the UICulture setting in the project file.\n\n//[assembly: NeutralResourcesLanguage(\"en-US\", UltimateResourceFallbackLocation.Satellite)]\n\n\n[assembly: ThemeInfo(\n    ResourceDictionaryLocation.None, //where theme specific resource dictionaries are located\n    //(used if a resource is not found in the page, \n    // or application resource dictionaries)\n    ResourceDictionaryLocation.SourceAssembly //where the generic resource dictionary is located\n    //(used if a resource is not found in the page, \n    // app, or any theme specific resource dictionaries)\n)]\n\n\n// Version information for an assembly consists of the following four values:\n//\n//      Major Version\n//      Minor Version \n//      Build Number\n//      Revision\n//\n// You can specify all the values or you can default the Build and Revision Numbers \n// by using the '*' as shown below:\n// [assembly: AssemblyVersion(\"1.0.*\")]"
  },
  {
    "path": "Godbert/Properties/Resources.Designer.cs",
    "content": "﻿//------------------------------------------------------------------------------\n// <auto-generated>\n//     This code was generated by a tool.\n//     Runtime Version:4.0.30319.42000\n//\n//     Changes to this file may cause incorrect behavior and will be lost if\n//     the code is regenerated.\n// </auto-generated>\n//------------------------------------------------------------------------------\n\nnamespace Godbert.Properties {\n    using System;\n    \n    \n    /// <summary>\n    ///   A strongly-typed resource class, for looking up localized strings, etc.\n    /// </summary>\n    // This class was auto-generated by the StronglyTypedResourceBuilder\n    // class via a tool like ResGen or Visual Studio.\n    // To add or remove a member, edit your .ResX file then rerun ResGen\n    // with the /str option, or rebuild your VS project.\n    [global::System.CodeDom.Compiler.GeneratedCodeAttribute(\"System.Resources.Tools.StronglyTypedResourceBuilder\", \"15.0.0.0\")]\n    [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]\n    [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]\n    internal class Resources {\n        \n        private static global::System.Resources.ResourceManager resourceMan;\n        \n        private static global::System.Globalization.CultureInfo resourceCulture;\n        \n        [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute(\"Microsoft.Performance\", \"CA1811:AvoidUncalledPrivateCode\")]\n        internal Resources() {\n        }\n        \n        /// <summary>\n        ///   Returns the cached ResourceManager instance used by this class.\n        /// </summary>\n        [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]\n        internal static global::System.Resources.ResourceManager ResourceManager {\n            get {\n                if (object.ReferenceEquals(resourceMan, null)) {\n                    global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager(\"Godbert.Properties.Resources\", typeof(Resources).Assembly);\n                    resourceMan = temp;\n                }\n                return resourceMan;\n            }\n        }\n        \n        /// <summary>\n        ///   Overrides the current thread's CurrentUICulture property for all\n        ///   resource lookups using this strongly typed resource class.\n        /// </summary>\n        [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]\n        internal static global::System.Globalization.CultureInfo Culture {\n            get {\n                return resourceCulture;\n            }\n            set {\n                resourceCulture = value;\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "Godbert/Properties/Resources.resx",
    "content": "﻿<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<root>\n  <!-- \n    Microsoft ResX Schema \n    \n    Version 2.0\n    \n    The primary goals of this format is to allow a simple XML format \n    that is mostly human readable. The generation and parsing of the \n    various data types are done through the TypeConverter classes \n    associated with the data types.\n    \n    Example:\n    \n    ... ado.net/XML headers & schema ...\n    <resheader name=\"resmimetype\">text/microsoft-resx</resheader>\n    <resheader name=\"version\">2.0</resheader>\n    <resheader name=\"reader\">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>\n    <resheader name=\"writer\">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>\n    <data name=\"Name1\"><value>this is my long string</value><comment>this is a comment</comment></data>\n    <data name=\"Color1\" type=\"System.Drawing.Color, System.Drawing\">Blue</data>\n    <data name=\"Bitmap1\" mimetype=\"application/x-microsoft.net.object.binary.base64\">\n        <value>[base64 mime encoded serialized .NET Framework object]</value>\n    </data>\n    <data name=\"Icon1\" type=\"System.Drawing.Icon, System.Drawing\" mimetype=\"application/x-microsoft.net.object.bytearray.base64\">\n        <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>\n        <comment>This is a comment</comment>\n    </data>\n                \n    There are any number of \"resheader\" rows that contain simple \n    name/value pairs.\n    \n    Each data row contains a name, and value. The row also contains a \n    type or mimetype. Type corresponds to a .NET class that support \n    text/value conversion through the TypeConverter architecture. \n    Classes that don't support this are serialized and stored with the \n    mimetype set.\n    \n    The mimetype is used for serialized objects, and tells the \n    ResXResourceReader how to depersist the object. This is currently not \n    extensible. For a given mimetype the value must be set accordingly:\n    \n    Note - application/x-microsoft.net.object.binary.base64 is the format \n    that the ResXResourceWriter will generate, however the reader can \n    read any of the formats listed below.\n    \n    mimetype: application/x-microsoft.net.object.binary.base64\n    value   : The object must be serialized with \n            : System.Serialization.Formatters.Binary.BinaryFormatter\n            : and then encoded with base64 encoding.\n    \n    mimetype: application/x-microsoft.net.object.soap.base64\n    value   : The object must be serialized with \n            : System.Runtime.Serialization.Formatters.Soap.SoapFormatter\n            : and then encoded with base64 encoding.\n\n    mimetype: application/x-microsoft.net.object.bytearray.base64\n    value   : The object must be serialized into a byte array \n            : using a System.ComponentModel.TypeConverter\n            : and then encoded with base64 encoding.\n    -->\n  <xsd:schema id=\"root\" xmlns=\"\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" xmlns:msdata=\"urn:schemas-microsoft-com:xml-msdata\">\n    <xsd:element name=\"root\" msdata:IsDataSet=\"true\">\n      <xsd:complexType>\n        <xsd:choice maxOccurs=\"unbounded\">\n          <xsd:element name=\"metadata\">\n            <xsd:complexType>\n              <xsd:sequence>\n                <xsd:element name=\"value\" type=\"xsd:string\" minOccurs=\"0\" />\n              </xsd:sequence>\n              <xsd:attribute name=\"name\" type=\"xsd:string\" />\n              <xsd:attribute name=\"type\" type=\"xsd:string\" />\n              <xsd:attribute name=\"mimetype\" type=\"xsd:string\" />\n            </xsd:complexType>\n          </xsd:element>\n          <xsd:element name=\"assembly\">\n            <xsd:complexType>\n              <xsd:attribute name=\"alias\" type=\"xsd:string\" />\n              <xsd:attribute name=\"name\" type=\"xsd:string\" />\n            </xsd:complexType>\n          </xsd:element>\n          <xsd:element name=\"data\">\n            <xsd:complexType>\n              <xsd:sequence>\n                <xsd:element name=\"value\" type=\"xsd:string\" minOccurs=\"0\" msdata:Ordinal=\"1\" />\n                <xsd:element name=\"comment\" type=\"xsd:string\" minOccurs=\"0\" msdata:Ordinal=\"2\" />\n              </xsd:sequence>\n              <xsd:attribute name=\"name\" type=\"xsd:string\" msdata:Ordinal=\"1\" />\n              <xsd:attribute name=\"type\" type=\"xsd:string\" msdata:Ordinal=\"3\" />\n              <xsd:attribute name=\"mimetype\" type=\"xsd:string\" msdata:Ordinal=\"4\" />\n            </xsd:complexType>\n          </xsd:element>\n          <xsd:element name=\"resheader\">\n            <xsd:complexType>\n              <xsd:sequence>\n                <xsd:element name=\"value\" type=\"xsd:string\" minOccurs=\"0\" msdata:Ordinal=\"1\" />\n              </xsd:sequence>\n              <xsd:attribute name=\"name\" type=\"xsd:string\" use=\"required\" />\n            </xsd:complexType>\n          </xsd:element>\n        </xsd:choice>\n      </xsd:complexType>\n    </xsd:element>\n  </xsd:schema>\n  <resheader name=\"resmimetype\">\n    <value>text/microsoft-resx</value>\n  </resheader>\n  <resheader name=\"version\">\n    <value>2.0</value>\n  </resheader>\n  <resheader name=\"reader\">\n    <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>\n  </resheader>\n  <resheader name=\"writer\">\n    <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>\n  </resheader>\n</root>"
  },
  {
    "path": "Godbert/Properties/Settings.Designer.cs",
    "content": "﻿//------------------------------------------------------------------------------\n// <auto-generated>\n//     This code was generated by a tool.\n//     Runtime Version:4.0.30319.42000\n//\n//     Changes to this file may cause incorrect behavior and will be lost if\n//     the code is regenerated.\n// </auto-generated>\n//------------------------------------------------------------------------------\n\nnamespace Godbert.Properties {\n    \n    \n    [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]\n    [global::System.CodeDom.Compiler.GeneratedCodeAttribute(\"Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator\", \"15.3.0.0\")]\n    internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase {\n        \n        private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings())));\n        \n        public static Settings Default {\n            get {\n                return defaultInstance;\n            }\n        }\n        \n        [global::System.Configuration.UserScopedSettingAttribute()]\n        [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]\n        [global::System.Configuration.DefaultSettingValueAttribute(\"\")]\n        public string GamePath {\n            get {\n                return ((string)(this[\"GamePath\"]));\n            }\n            set {\n                this[\"GamePath\"] = value;\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "Godbert/Properties/Settings.settings",
    "content": "﻿<?xml version='1.0' encoding='utf-8'?>\n<SettingsFile xmlns=\"http://schemas.microsoft.com/VisualStudio/2004/01/settings\" CurrentProfile=\"(Default)\" GeneratedClassNamespace=\"Godbert.Properties\" GeneratedClassName=\"Settings\">\n  <Profiles />\n  <Settings>\n    <Setting Name=\"GamePath\" Type=\"System.String\" Scope=\"User\">\n      <Value Profile=\"(Default)\" />\n    </Setting>\n  </Settings>\n</SettingsFile>"
  },
  {
    "path": "Godbert/Settings.cs",
    "content": "﻿using Newtonsoft.Json;\nusing System;\nusing System.Collections.Generic;\nusing System.IO;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace Godbert {\n    public class Settings {\n        #region IO Boilerplate\n        public static Settings Default { get; } = Load();\n\n        private static string FileName => Path.Combine(\n            Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), \"Godbert\", \"settings.json\");\n\n        private static Settings Load() {\n            try {\n                if (File.Exists(FileName)) {\n                    var text = File.ReadAllText(FileName);\n                    return JsonConvert.DeserializeObject<Settings>(text) ?? new Settings();\n                }\n            } catch (Exception) {\n                // Error reading settings.  Return default.\n            }\n\n            return new Settings();\n        }\n\n        public void Save() {\n            var path = Path.GetDirectoryName(FileName);\n            if (!Directory.Exists(path))\n                Directory.CreateDirectory(path);\n\n            var text = JsonConvert.SerializeObject(this, Formatting.Indented);\n            try {\n                File.WriteAllText(FileName, text);\n            } catch (IOException) {\n                // Error saving settings.  Ignore.\n            }\n        }\n        #endregion\n\n        public double MainWindowLeft;\n        public double MainWindowTop;\n        public double MainWindowWidth;\n        public double MainWindowHeight;\n\n        public string SelectedSheetName;\n        public string FilterSheetTerm;\n        public string FilterDataTerm;\n        public bool ShowOffsets;\n        public bool SortByOffsets;\n    }\n}\n"
  },
  {
    "path": "Godbert/ViewModels/BookmarkViewModel.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace Godbert.ViewModels {\n    public class BookmarkViewModel {\n        public string SheetName { get; set; }\n        public int Key { get; set; }\n        public string RowDefault { get; set; }\n        public int? ColumnIndex { get; set; }\n        public string ColumnName { get; set; }\n\n        public override string ToString() {\n            var rowIdentifier = $\"{SheetName}#{Key}\";\n\n            var sb = new StringBuilder();\n            sb.Append(rowIdentifier);\n\n            if (ColumnName != null)\n                sb.Append($\" [{ColumnIndex} {ColumnName}]\");\n            else if (ColumnIndex != null)\n                sb.Append($\" [{ColumnIndex}]\");\n\n            if (!string.IsNullOrWhiteSpace(RowDefault) && RowDefault != rowIdentifier) {\n                sb.AppendLine();\n                sb.Append(RowDefault);\n            }\n\n            return sb.ToString();\n        }\n\n        public override bool Equals(object obj) {\n            var b = obj as BookmarkViewModel;\n            if (b == null)\n                return false;\n\n            return b.SheetName == SheetName && b.Key == Key && b.ColumnIndex == ColumnIndex;\n        }\n\n        public override int GetHashCode() {\n            return SheetName.GetHashCode() ^ Key.GetHashCode() ^ (ColumnIndex ?? 0).GetHashCode();\n        }\n    }\n}\n"
  },
  {
    "path": "Godbert/ViewModels/DataViewModel.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Collections.ObjectModel;\nusing System.IO;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\nusing System.Windows.Input;\nusing System.Windows;\n\nusing SaintCoinach.Ex.Relational;\n\nnamespace Godbert.ViewModels {\n    using Commands;\n\n    public class DataViewModel : ObservableBase {\n        #region Fields\n        private string _SelectedSheetName;\n        private IRelationalSheet _SelectedSheet;\n        private DelegateCommand _ExportCsvCommand;\n        private string _FilterSheetTerm;\n        private IEnumerable<string> _FilteredSheets;\n        private string _FilterDataTerm;\n        private ObservableCollection<BookmarkViewModel> _Bookmarks = new ObservableCollection<BookmarkViewModel>();\n        #endregion\n\n        #region Properties\n        public ICommand ExportCsvCommand { get { return _ExportCsvCommand ?? (_ExportCsvCommand = new DelegateCommand(OnExportCsv)); } }\n        public SaintCoinach.ARealmReversed Realm { get; private set; }\n        public MainViewModel Parent { get; private set; }\n\n        public IEnumerable<string> FilteredSheetNames { get { return _FilteredSheets; } }\n\n        public string SelectedSheetName {\n            get { return _SelectedSheetName; }\n            set {\n                _SelectedSheetName = value;\n                _SelectedSheet = null;\n                OnPropertyChanged(() => SelectedSheetName);\n                OnPropertyChanged(() => SelectedSheet);\n\n                Settings.Default.SelectedSheetName = value;\n            }\n        }\n        public IRelationalSheet SelectedSheet {\n            get {\n                if (string.IsNullOrWhiteSpace(SelectedSheetName))\n                    return null;\n                if (_SelectedSheet == null)\n                    _SelectedSheet = Realm.GameData.GetSheet(SelectedSheetName);\n                return _SelectedSheet;\n            }\n        }\n        public string FilterSheetTerm {\n            get { return _FilterSheetTerm; }\n            set {\n                _FilterSheetTerm = value;\n\n                if (string.IsNullOrWhiteSpace(value))\n                    _FilteredSheets = Realm.GameData.AvailableSheets;\n                else\n                    _FilteredSheets = Realm.GameData.AvailableSheets.Where(s => s.IndexOf(value, StringComparison.OrdinalIgnoreCase) >= 0).ToArray();\n\n                OnPropertyChanged(() => FilterSheetTerm);\n                OnPropertyChanged(() => FilteredSheetNames);\n\n                Settings.Default.FilterSheetTerm = value;\n            }\n        }\n\n        public string FilterDataTerm {\n            get { return _FilterDataTerm; }\n            set {\n                _FilterDataTerm = value;\n\n                OnPropertyChanged(() => FilterDataTerm);\n\n                Settings.Default.FilterDataTerm = value;\n            }\n        }\n\n        public ObservableCollection<BookmarkViewModel> Bookmarks {\n            get { return _Bookmarks; }\n        }\n\n        public Visibility BookmarksVisibility {\n            get { return _Bookmarks.Count > 0 ? Visibility.Visible : Visibility.Collapsed; }\n        }\n        #endregion\n\n        #region Constructor\n        public DataViewModel(SaintCoinach.ARealmReversed realm, MainViewModel parent) {\n            this.Realm = realm;\n            this.Parent = parent;\n\n            _FilteredSheets = Realm.GameData.AvailableSheets;\n\n            _SelectedSheetName = Settings.Default.SelectedSheetName;\n            _FilterDataTerm = Settings.Default.FilterDataTerm;\n            FilterSheetTerm = Settings.Default.FilterSheetTerm;\n\n            _Bookmarks.CollectionChanged += _Bookmarks_CollectionChanged;\n        }\n        #endregion\n\n        #region Export\n        private void OnExportCsv() {\n            if (SelectedSheet == null)\n                return;\n\n            var dlg = new Microsoft.Win32.SaveFileDialog {\n                DefaultExt = \".csv\",\n                Filter = \"CSV Files (*.csv)|*.csv\",\n                AddExtension = true,\n                OverwritePrompt = true,\n                FileName = FixName(SelectedSheet.Name) + \".csv\"\n            };\n\n            if (dlg.ShowDialog().GetValueOrDefault(false))\n                SaveAsCsv(SelectedSheet, dlg.FileName);\n        }\n        private static string FixName(string original) {\n            var idx = original.LastIndexOf('/');\n            if (idx >= 0)\n                return original.Substring(idx + 1);\n            return original;\n        }\n        static void SaveAsCsv(IRelationalSheet sheet, string path) {\n            using (var s = new StreamWriter(path, false, Encoding.UTF8)) {\n                var indexLine = new StringBuilder(\"key\");\n                var nameLine = new StringBuilder(\"#\");\n                var typeLine = new StringBuilder(\"int32\");\n\n                var colIndices = new List<int>();\n                foreach (var col in sheet.Header.Columns) {\n                    indexLine.AppendFormat(\",{0}\", col.Index);\n                    nameLine.AppendFormat(\",{0}\", col.Name);\n                    typeLine.AppendFormat(\",{0}\", col.ValueType);\n\n                    colIndices.Add(col.Index);\n                }\n\n                s.WriteLine(indexLine);\n                s.WriteLine(nameLine);\n                s.WriteLine(typeLine);\n\n                foreach (var row in sheet.Cast<SaintCoinach.Ex.IRow>().OrderBy(_ => _.Key)) {\n                    s.Write(row.Key);\n                    foreach (var col in colIndices) {\n                        var v = row[col];\n\n                        if (v == null)\n                            s.Write(\",\");\n                        else if (v is IDictionary<int, object>)\n                            WriteDict(s, v as IDictionary<int, object>);\n                        else if (IsUnescaped(v))\n                            s.Write(\",{0}\", v);\n                        else\n                            s.Write(\",\\\"{0}\\\"\", v.ToString().Replace(\"\\\"\", \"\\\"\\\"\"));\n                    }\n                    s.WriteLine();\n                }\n            }\n        }\n        static void WriteDict(StreamWriter s, IDictionary<int, object> v) {\n            s.Write(\",\\\"\");\n            var isFirst = true;\n            foreach (var kvp in v) {\n                if (isFirst)\n                    isFirst = false;\n                else\n                    s.Write(\",\");\n                s.Write(\"[{0},\", kvp.Key);\n                if (kvp.Value != null)\n                    s.Write(kvp.Value.ToString().Replace(\"\\\"\", \"\\\"\\\"\"));\n                s.Write(\"]\");\n            }\n            s.Write(\"\\\"\");\n        }\n        static bool IsUnescaped(object self) {\n            return (self is Boolean\n                || self is Byte\n                || self is SByte\n                || self is Int16\n                || self is Int32\n                || self is Int64\n                || self is UInt16\n                || self is UInt32\n                || self is UInt64\n                || self is Single\n                || self is Double);\n        }\n        #endregion\n\n        private void _Bookmarks_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e) {\n            OnPropertyChanged(() => BookmarksVisibility);\n        }\n    }\n}\n"
  },
  {
    "path": "Godbert/ViewModels/DemihumanViewModel.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\nusing System.Windows.Input;\n\nusing SaintCoinach.Graphics;\nusing SaintCoinach.Graphics.Viewer;\nusing SaintCoinach.Graphics.Viewer.Content;\nusing SaintCoinach.Xiv;\n\nnamespace Godbert.ViewModels {\n    using Commands;\n\n    public class DemihumanViewModel : ObservableBase {\n        const string ImcPathFormat = \"chara/demihuman/d{0:D4}/obj/equipment/e{1:D4}/e{1:D4}.imc\";\n        static readonly string[] ModelPathFormats = new string[] {\n            \"chara/demihuman/d{0:D4}/obj/equipment/e{1:D4}/model/d{0:D4}e{1:D4}_met.mdl\",\n            \"chara/demihuman/d{0:D4}/obj/equipment/e{1:D4}/model/d{0:D4}e{1:D4}_top.mdl\",\n            \"chara/demihuman/d{0:D4}/obj/equipment/e{1:D4}/model/d{0:D4}e{1:D4}_glv.mdl\",\n            \"chara/demihuman/d{0:D4}/obj/equipment/e{1:D4}/model/d{0:D4}e{1:D4}_dwn.mdl\",\n            \"chara/demihuman/d{0:D4}/obj/equipment/e{1:D4}/model/d{0:D4}e{1:D4}_sho.mdl\",\n        };\n\n\n        #region Fields\n        private Models.ModelCharaHierarchy _Entries;\n        private object _SelectedEntry;\n        private bool[] _SelectedParts = new bool[] {\n            true,\n            true,\n            true,\n            true,\n            true\n        };\n        #endregion\n\n        #region Properties\n        public MainViewModel Parent { get; private set; }\n        public Models.ModelCharaHierarchy Entries {\n            get { return _Entries; }\n            private set {\n                _Entries = value;\n                OnPropertyChanged(() => Entries);\n            }\n        }\n        public object SelectedEntry {\n            get { return _SelectedEntry; }\n            set {\n                _SelectedEntry = value;\n                OnPropertyChanged(() => SelectedEntry);\n                OnPropertyChanged(() => IsValidSelection);\n            }\n        }\n        public bool IsValidSelection { get { return SelectedEntry is Models.ModelCharaVariant; } }\n        public bool ShowPart0 {\n            get { return _SelectedParts[0]; }\n            set {\n                _SelectedParts[0] = value;\n                OnPropertyChanged(() => ShowPart0);\n            }\n        }\n        public bool ShowPart1 {\n            get { return _SelectedParts[1]; }\n            set {\n                _SelectedParts[1] = value;\n                OnPropertyChanged(() => ShowPart0);\n            }\n        }\n        public bool ShowPart2 {\n            get { return _SelectedParts[2]; }\n            set {\n                _SelectedParts[2] = value;\n                OnPropertyChanged(() => ShowPart0);\n            }\n        }\n        public bool ShowPart3 {\n            get { return _SelectedParts[3]; }\n            set {\n                _SelectedParts[3] = value;\n                OnPropertyChanged(() => ShowPart3);\n            }\n        }\n        public bool ShowPart4 {\n            get { return _SelectedParts[4]; }\n            set {\n                _SelectedParts[4] = value;\n                OnPropertyChanged(() => ShowPart4);\n            }\n        }\n        #endregion\n\n        #region Constructor\n        public DemihumanViewModel(MainViewModel parent) {\n            this.Parent = parent;\n\n            Entries = new Models.ModelCharaHierarchy(\"d{0:D4}\", \"e{0:D4}\", \"v{0:D4}\");\n            foreach (var mc in parent.Realm.GameData.GetSheet<ModelChara>().Where(mc => mc.Type == 2)) {\n                var imcPath = string.Format(ImcPathFormat, mc.ModelKey, mc.BaseKey);\n                if (parent.Realm.Packs.FileExists(imcPath))\n                    Entries.Add(mc);\n            }\n        }\n        #endregion\n\n        #region Commands\n        private ICommand _AddCommand;\n        private ICommand _ReplaceCommand;\n        private ICommand _NewCommand;\n\n        public ICommand AddCommand { get { return _AddCommand ?? (_AddCommand = new DelegateCommand(OnAdd)); } }\n        public ICommand ReplaceCommand { get { return _ReplaceCommand ?? (_ReplaceCommand = new DelegateCommand(OnReplace)); } }\n        public ICommand NewCommand { get { return _NewCommand ?? (_NewCommand = new DelegateCommand(OnNew)); } }\n\n        private void OnAdd() {\n            string title;\n            Tuple<ModelDefinition, ImcVariant>[] models;\n            if (TryGetModel(out title, out models))\n                Parent.EngineHelper.AddToLast(title, (e) => models.Select(m => new SaintCoinach.Graphics.Viewer.Content.ContentModel(e, m.Item2, m.Item1, ModelQuality.High)).ToArray());\n        }\n        private void OnReplace() {\n            string title;\n            Tuple<ModelDefinition, ImcVariant>[] models;\n            if (TryGetModel(out title, out models))\n                Parent.EngineHelper.ReplaceInLast(title, (e) => models.Select(m => new SaintCoinach.Graphics.Viewer.Content.ContentModel(e, m.Item2, m.Item1, ModelQuality.High)).ToArray());\n        }\n        private void OnNew() {\n            string title;\n            Tuple<ModelDefinition, ImcVariant>[] models;\n            if (TryGetModel(out title, out models))\n                Parent.EngineHelper.OpenInNew(title, (e) => models.Select(m => new SaintCoinach.Graphics.Viewer.Content.ContentModel(e, m.Item2, m.Item1, ModelQuality.High)).ToArray());\n        }\n\n        private bool TryGetModel(out string title, out Tuple<ModelDefinition, ImcVariant>[] models) {\n            title = null;\n            models = null;\n\n            var asVariant = SelectedEntry as Models.ModelCharaVariant;\n            if (asVariant == null)\n                return false;\n\n            title = asVariant.ToString();\n\n            int v = asVariant.Value;\n            int e = asVariant.Parent.Value;\n            var d = asVariant.Parent.Parent.Value;\n\n            var imcPath = string.Format(ImcPathFormat, d, e);\n\n            SaintCoinach.IO.File imcFileBase;\n            if (!Parent.Realm.Packs.TryGetFile(imcPath, out imcFileBase)) {\n                System.Windows.MessageBox.Show(string.Format(\"Unable to find files for {0}.\", title), \"File not found\", System.Windows.MessageBoxButton.OK, System.Windows.MessageBoxImage.Error);\n                return false;\n            }\n\n            try {\n                var imcFile = new ImcFile(imcFileBase);\n                var modelsList = new List<Tuple<ModelDefinition, ImcVariant>>();\n                foreach(var part in imcFile.Parts) {\n                    if (!_SelectedParts[part.Bit])\n                        continue;\n\n                    var variant = part.Variants[v];\n                    if(variant.Variant == 0)\n                        continue;\n\n                    var mdlPath = string.Format(ModelPathFormats[part.Bit], d, e);\n                    SaintCoinach.IO.File mdlBase;\n                    if(!Parent.Realm.Packs.TryGetFile(mdlPath, out mdlBase))\n                        continue;\n                    var mdl = ((ModelFile)mdlBase).GetModelDefinition();\n\n                    modelsList.Add(Tuple.Create(mdl, variant));\n                }\n\n                if(modelsList.Count == 0) {\n                    System.Windows.MessageBox.Show(string.Format(\"No models found for {0}.\", title), \"No models\", System.Windows.MessageBoxButton.OK, System.Windows.MessageBoxImage.Error);\n                    return false;\n                }\n                models = modelsList.ToArray();\n\n                return true;\n            } catch (Exception ex) {\n                System.Windows.MessageBox.Show(string.Format(\"Unable to load model for {0}:{1}{2}\", title, Environment.NewLine, ex), \"Failure to load\", System.Windows.MessageBoxButton.OK, System.Windows.MessageBoxImage.Error);\n                return false;\n            }\n        }\n        #endregion\n\n        #region Brute-force\n        private bool _IsBruteForceAvailable = true;\n        private ICommand _BruteForceCommand;\n\n        public bool IsBruteForceAvailable {\n            get { return _IsBruteForceAvailable; }\n            private set {\n                _IsBruteForceAvailable = value;\n                OnPropertyChanged(() => IsBruteForceAvailable);\n            }\n        }\n        public ICommand BruteForceCommand { get { return _BruteForceCommand ?? (_BruteForceCommand = new DelegateCommand(OnBruteForce)); } }\n\n        private void OnBruteForce() {\n            IsBruteForceAvailable = false;\n\n            var progDlg = new Ookii.Dialogs.Wpf.ProgressDialog();\n            progDlg.WindowTitle = \"Brute-forcing\";\n            progDlg.Text = \"This is going to take a while...\";\n            progDlg.DoWork += DoBruteForceWork;\n            progDlg.RunWorkerCompleted += OnBruteForceComplete;\n            progDlg.ShowDialog(System.Windows.Application.Current.MainWindow);\n            progDlg.ProgressBarStyle = Ookii.Dialogs.Wpf.ProgressBarStyle.ProgressBar;\n            progDlg.ShowTimeRemaining = true;\n        }\n\n        void OnBruteForceComplete(object sender, System.ComponentModel.RunWorkerCompletedEventArgs eventArgs) {\n            if (eventArgs.Cancelled)\n                IsBruteForceAvailable = true;\n        }\n\n        void DoBruteForceWork(object sender, System.ComponentModel.DoWorkEventArgs eventArgs) {\n            var dlg = (Ookii.Dialogs.Wpf.ProgressDialog)sender;\n\n            var newEntries = new Models.ModelCharaHierarchy(Entries.MainFormat, Entries.SubFormat, Entries.VariantFormat);\n            for (var d = 0; d < 10000; ++d) {\n                if (dlg.CancellationPending)\n                    return;\n                dlg.ReportProgress(d / 100, null, string.Format(\"Current progress: {0:P}\", d / 10000.0));\n                for (var e = 0; e < 10000; ++e) {\n                    //dlg.ReportProgress(((d * 10000) + e) / (10000 * 100));\n\n                    var imcPath = string.Format(ImcPathFormat, d, e);\n                    SaintCoinach.IO.File imcBase;\n                    if (!Parent.Realm.Packs.TryGetFile(imcPath, out imcBase))\n                        continue;\n                    try {\n                        var imc = new SaintCoinach.Graphics.ImcFile(imcBase);\n                        for (var v = 1; v < imc.Count; ++v) {\n                            /*if (Entries.Contains(d, e, v))\n                                continue;*/\n\n                            var any = false;\n                            foreach (var p in imc.Parts) {\n                                if (p.Variants[v].Variant != 0) {\n                                    any = true;\n                                    break;\n                                }\n                            }\n                            if (any)\n                                newEntries.Add(d, e, v);\n                        }\n                    } catch (Exception ex) {\n                        Console.Error.WriteLine(\"Failed parsing imc file {0}:{1}{2}\", imcPath, Environment.NewLine, ex);\n                    }\n                }\n            }\n            Entries = newEntries;\n        }\n        #endregion\n    }\n}\n"
  },
  {
    "path": "Godbert/ViewModels/EquipmentViewModel.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\nusing System.Windows.Input;\n\nusing SaintCoinach.Graphics;\nusing SaintCoinach.Graphics.Viewer;\nusing SaintCoinach.Xiv;\nusing SaintCoinach.Xiv.Items;\n\nnamespace Godbert.ViewModels {\n\n    using Commands;\n\n    public class EquipmentViewModel : ObservableBase {\n        #region Fields\n        private Equipment[] _AllEquipment;\n        private Equipment[] _FilteredEquipment;\n        private Equipment _SelectedEquipment;\n        private Stain _SelectedStain;\n        private string _FilterTerm;\n        #endregion\n\n        #region Properties\n        public MainViewModel Parent { get; private set; }\n        public IEnumerable<Equipment> AllEquipment { get { return _AllEquipment; } }\n        public IEnumerable<Equipment> FilteredEquipment { get { return _FilteredEquipment; } }\n        public Equipment SelectedEquipment {\n            get { return _SelectedEquipment; }\n            set {\n                _SelectedEquipment = value;\n                OnPropertyChanged(() => SelectedEquipment);\n            }\n        }\n        public IEnumerable<Stain> Stains { get; private set; }\n        public Stain SelectedStain {\n            get { return _SelectedStain; }\n            set {\n                _SelectedStain = value;\n                OnPropertyChanged(() => SelectedStain);\n            }\n        }\n        public string FilterTerm {\n            get { return _FilterTerm; }\n            set {\n                _FilterTerm = value;\n\n                if (string.IsNullOrWhiteSpace(value))\n                    _FilteredEquipment = _AllEquipment;\n                else\n                    _FilteredEquipment = _AllEquipment.Where(e => e.Name.ToString().IndexOf(value, StringComparison.OrdinalIgnoreCase) >= 0).ToArray();\n\n                OnPropertyChanged(() => FilterTerm);\n                OnPropertyChanged(() => FilteredEquipment);\n            }\n        }\n        #endregion\n\n        #region Constructor\n        public EquipmentViewModel(MainViewModel parent) {\n            this.Parent = parent;\n\n            // Ignore waist and soul crystals\n            _AllEquipment = Parent.Realm.GameData.GetSheet<Item>().OfType<Equipment>().Where(e => !e.EquipSlotCategory.PossibleSlots.Any(s => s.Key == 5 || s.Key == 13)).OrderBy(e => e.Name).ToArray();\n            Stains = Parent.Realm.GameData.GetSheet<Stain>();\n\n            _FilteredEquipment = _AllEquipment;\n        }\n        #endregion\n\n        #region Command\n        private ICommand _AddCommand;\n        private ICommand _ReplaceCommand;\n        private ICommand _NewCommand;\n\n        public ICommand AddCommand { get { return _AddCommand ?? (_AddCommand = new DelegateCommand(OnAdd)); } }\n        public ICommand ReplaceCommand { get { return _ReplaceCommand ?? (_ReplaceCommand = new DelegateCommand(OnReplace)); } }\n        public ICommand NewCommand { get { return _NewCommand ?? (_NewCommand = new DelegateCommand(OnNew)); } }\n\n        private void OnAdd() {\n            if (TryGetModel(out var model, out var variant))\n                Parent.EngineHelper.AddToLast(SelectedEquipment.Name, (e) => new SaintCoinach.Graphics.Viewer.Content.ContentModel(e, variant, model, ModelQuality.High));\n        }\n        private void OnReplace() {\n            if (TryGetModel(out var model, out var variant))\n                Parent.EngineHelper.ReplaceInLast(SelectedEquipment.Name, (e) => new SaintCoinach.Graphics.Viewer.Content.ContentModel(e, variant, model, ModelQuality.High));\n        }\n        private void OnNew() {\n            if (TryGetModel(out var model, out var variant))\n                Parent.EngineHelper.OpenInNew(SelectedEquipment.Name, (e) => new SaintCoinach.Graphics.Viewer.Content.ContentModel(e, variant, model, ModelQuality.High));\n        }\n\n        private bool TryGetModel(out ModelDefinition model, out ModelVariantIdentifier variant) {\n            model = null;\n            variant = default(ModelVariantIdentifier);\n            if (SelectedEquipment == null)\n                return false;\n\n            var charType = SelectedEquipment.GetModelCharacterType();\n            if (charType == 0)\n                return false;\n            try {\n                model = SelectedEquipment.GetModel(charType, out variant.ImcVariant);\n                if (SelectedEquipment.IsDyeable && SelectedStain != null)\n                    variant.StainKey = SelectedStain.Key;\n\n                var result = (model != null);\n                if (!result)\n                    System.Windows.MessageBox.Show(string.Format(\"Unable to find model for {0} (c{1:D4}).\", SelectedEquipment.Name, charType), \"Model not found\", System.Windows.MessageBoxButton.OK, System.Windows.MessageBoxImage.Error);\n                return result;\n            } catch (Exception e) {\n                System.Windows.MessageBox.Show(string.Format(\"Failed to load model for {0} (c{1:D4}):{2}{3}\", SelectedEquipment.Name, charType, Environment.NewLine, e), \"Read failure\", System.Windows.MessageBoxButton.OK, System.Windows.MessageBoxImage.Error);\n                return false;\n            }\n        }\n        #endregion\n\n        #region Refresh\n        public void Refresh() {\n            _FilteredEquipment = _FilteredEquipment.ToArray();\n            OnPropertyChanged(() => FilteredEquipment);\n        }\n        #endregion\n    }\n}\n"
  },
  {
    "path": "Godbert/ViewModels/FurnitureViewModel.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\nusing System.Windows.Input;\n\nusing SaintCoinach.Graphics;\nusing SaintCoinach.Graphics.Sgb;\nusing SaintCoinach.Graphics.Viewer;\nusing SaintCoinach.Xiv;\n\nnamespace Godbert.ViewModels {\n\n    using Commands;\n\n    public class FurnitureViewModel : ObservableBase {\n        #region Fields\n        private HousingItem[] _AllFurniture;\n        private HousingItem[] _FilteredFurniture;\n        private HousingItem _SelectedFurniture;\n        private Stain _SelectedStain;\n        private string _FilterTerm;\n        private SaintCoinach.Graphics.Viewer.Data.ParametersBase _Parameters = new SaintCoinach.Graphics.Viewer.Data.ParametersBase();\n        #endregion\n\n        #region Properties\n        public MainViewModel Parent { get; private set; }\n        public IEnumerable<HousingItem> AllFurniture { get { return _AllFurniture; } }\n        public IEnumerable<HousingItem> FilteredFurniture { get { return _FilteredFurniture; } }\n        public HousingItem SelectedFurniture {\n            get { return _SelectedFurniture; }\n            set {\n                _SelectedFurniture = value;\n                OnPropertyChanged(() => SelectedFurniture);\n            }\n        }\n        public IEnumerable<Stain> Stains { get; private set; }\n        public Stain SelectedStain {\n            get { return _SelectedStain; }\n            set {\n                if (value == null || value.Key == 0)\n                    _Parameters.Remove(SaintCoinach.Graphics.Viewer.Content.BgColorChangeMaterial.ColorParameterKey);\n                else\n                    _Parameters.Set(SaintCoinach.Graphics.Viewer.Content.BgColorChangeMaterial.ColorParameterKey, new SharpDX.Vector4(value.Color.R / 255f, value.Color.G / 255f, value.Color.B / 255f, value.Color.A / 255f));\n                _SelectedStain = value;\n                OnPropertyChanged(() => SelectedStain);\n            }\n        }\n        public string FilterTerm {\n            get { return _FilterTerm; }\n            set {\n                _FilterTerm = value;\n\n                if (string.IsNullOrWhiteSpace(value))\n                    _FilteredFurniture = _AllFurniture;\n                else\n                    _FilteredFurniture = _AllFurniture.Where(e => e.Item.Name.ToString().IndexOf(value, StringComparison.OrdinalIgnoreCase) >= 0).ToArray();\n\n                OnPropertyChanged(() => FilterTerm);\n                OnPropertyChanged(() => FilteredFurniture);\n            }\n        }\n        #endregion\n\n        #region Constructor\n        public FurnitureViewModel(MainViewModel parent) {\n            this.Parent = parent;\n\n            var indoor = Parent.Realm.GameData.GetSheet<HousingFurniture>();\n            var outdoor = Parent.Realm.GameData.GetSheet<HousingYardObject>();\n            _AllFurniture = indoor.Cast<HousingItem>().Concat(outdoor.Cast<HousingItem>()).Where(_ => _.Item != null && _.Item.Key != 0 && _.Item.Name.ToString().Length > 0).OrderBy(_ => _.Item.Name).ToArray();\n            Stains = Parent.Realm.GameData.GetSheet<Stain>();\n\n            _FilteredFurniture = _AllFurniture;\n        }\n        #endregion\n\n        #region Command\n        private ICommand _AddCommand;\n        private ICommand _ReplaceCommand;\n        private ICommand _NewCommand;\n\n        public ICommand AddCommand { get { return _AddCommand ?? (_AddCommand = new DelegateCommand(OnAdd)); } }\n        public ICommand ReplaceCommand { get { return _ReplaceCommand ?? (_ReplaceCommand = new DelegateCommand(OnReplace)); } }\n        public ICommand NewCommand { get { return _NewCommand ?? (_NewCommand = new DelegateCommand(OnNew)); } }\n\n        private void OnAdd() {\n            SgbFile scene;\n            if (TryGetModel(out scene))\n                Parent.EngineHelper.AddToLast(SelectedFurniture.Item.Name, (e) => new SaintCoinach.Graphics.Viewer.Content.ContentSgb(e, scene, (SaintCoinach.Graphics.Viewer.Data.ParametersBase)_Parameters.Clone()));\n        }\n        private void OnReplace() {\n            SgbFile scene;\n            if (TryGetModel(out scene))\n                Parent.EngineHelper.ReplaceInLast(SelectedFurniture.Item.Name, (e) => new SaintCoinach.Graphics.Viewer.Content.ContentSgb(e, scene, (SaintCoinach.Graphics.Viewer.Data.ParametersBase)_Parameters.Clone()));\n        }\n        private void OnNew() {\n            SgbFile scene;\n            if (TryGetModel(out scene))\n                Parent.EngineHelper.OpenInNew(SelectedFurniture.Item.Name, (e) => new SaintCoinach.Graphics.Viewer.Content.ContentSgb(e, scene, (SaintCoinach.Graphics.Viewer.Data.ParametersBase)_Parameters.Clone()));\n        }\n        \n        private bool TryGetModel(out SgbFile model) {\n            model = null;\n            if (SelectedFurniture == null)\n                return false;\n\n            try {\n                model = SelectedFurniture.GetScene();\n\n                var result = (model != null);\n                if (!result)\n                    System.Windows.MessageBox.Show(string.Format(\"Unable to find model for {0}.\", SelectedFurniture.Item.Name), \"Model not found\", System.Windows.MessageBoxButton.OK, System.Windows.MessageBoxImage.Error);\n                return result;\n            } catch (Exception e) {\n                System.Windows.MessageBox.Show(string.Format(\"Failed to load model for {0}:{2}{3}\", SelectedFurniture.Item.Name, Environment.NewLine, e), \"Read failure\", System.Windows.MessageBoxButton.OK, System.Windows.MessageBoxImage.Error);\n                return false;\n            }\n        }\n        #endregion\n\n        #region Refresh\n        public void Refresh() {\n            _FilteredFurniture = _FilteredFurniture.ToArray();\n            OnPropertyChanged(() => FilteredFurniture);\n        }\n        #endregion\n    }\n}\n"
  },
  {
    "path": "Godbert/ViewModels/MainViewModel.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.IO;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nusing System.Windows.Input;\n\nnamespace Godbert.ViewModels {\n    using Commands;\n\n    using SaintCoinach;\n    using SaintCoinach.Xiv;\n\n    public class MainViewModel : ObservableBase {\n        #region Properties\n        public ARealmReversed Realm { get; private set; }\n        public EngineHelper EngineHelper { get; private set; }\n        public EquipmentViewModel Equipment { get; private set; }\n        public FurnitureViewModel Furniture { get; private set; }\n        public MonstersViewModel Monsters { get; private set; }\n        public TerritoryViewModel Territories { get; private set; }\n        public DemihumanViewModel Demihuman { get; private set; }\n        public DataViewModel Data { get; private set; }\n\n        public bool IsEnglish { get { return Realm.GameData.ActiveLanguage == SaintCoinach.Ex.Language.English; } }\n        public bool IsJapanese { get { return Realm.GameData.ActiveLanguage == SaintCoinach.Ex.Language.Japanese; } }\n        public bool IsFrench { get { return Realm.GameData.ActiveLanguage == SaintCoinach.Ex.Language.French; } }\n        public bool IsGerman { get { return Realm.GameData.ActiveLanguage == SaintCoinach.Ex.Language.German; } }\n        public bool IsChineseSimplified { get { return Realm.GameData.ActiveLanguage == SaintCoinach.Ex.Language.ChineseSimplified; } }\n        public bool IsKorean { get { return Realm.GameData.ActiveLanguage == SaintCoinach.Ex.Language.Korean; } }\n\n        public bool SortByOffsets { get { return Settings.Default.SortByOffsets;} }\n        public bool ShowOffsets { get { return Settings.Default.ShowOffsets; } }\n        #endregion\n\n        #region Events\n        public event EventHandler DataGridChanged;\n        #endregion\n\n        #region Constructor\n        public MainViewModel() {\n            if (!App.IsValidGamePath(Properties.Settings.Default.GamePath))\n                return;\n\n            var languages = new[] { SaintCoinach.Ex.Language.English, SaintCoinach.Ex.Language.ChineseSimplified, SaintCoinach.Ex.Language.Korean };\n            NotSupportedException lastException = null;\n\n            foreach (var language in languages) {\n                try {\n                    var realm = new ARealmReversed(Properties.Settings.Default.GamePath, language);\n                    Initialize(realm);\n                    lastException = null;\n                    break;\n                }\n                catch (NotSupportedException e) {\n                    lastException = e;\n                    continue;\n                }\n            }\n\n            if (lastException != null) {\n                throw new AggregateException(new[] { lastException });\n            }\n        }\n\n        public MainViewModel(ARealmReversed realm) {\n            Initialize(realm);\n        }\n\n        void Initialize(ARealmReversed realm) {\n            realm.Packs.GetPack(new SaintCoinach.IO.PackIdentifier(\"exd\", SaintCoinach.IO.PackIdentifier.DefaultExpansion, 0)).KeepInMemory = true;\n\n            Realm = realm;\n            EngineHelper = new EngineHelper();\n            Equipment = new EquipmentViewModel(this);\n            Furniture = new FurnitureViewModel(this);\n            Monsters = new MonstersViewModel(this);\n            Territories = new TerritoryViewModel(this);\n            Demihuman = new DemihumanViewModel(this);\n            Data = new DataViewModel(Realm, this);\n        }\n        #endregion\n\n        #region Commands\n        private ICommand _LanguageCommand;\n        private ICommand _GameLocationCommand;\n        private ICommand _NewWindowCommand;\n        private ICommand _ShowOffsetsCommand;\n        private ICommand _SortByOffsetsCommand;\n\n        public ICommand LanguageCommand { get { return _LanguageCommand ?? (_LanguageCommand = new Commands.DelegateCommand<SaintCoinach.Ex.Language>(OnLanguage)); } }\n        public ICommand GameLocationCommand { get { return _GameLocationCommand ?? (_GameLocationCommand = new Commands.DelegateCommand(OnGameLocation)); } }\n        public ICommand NewWindowCommand { get { return _NewWindowCommand ?? (_NewWindowCommand = new Commands.DelegateCommand(OnNewWindowCommand)); } }\n        public ICommand ShowOffsetsCommand { get { return _ShowOffsetsCommand ?? (_ShowOffsetsCommand = new Commands.DelegateCommand(OnShowOffsetsCommand)); } }\n        public ICommand SortByOffsetsCommand { get { return _SortByOffsetsCommand ?? (_SortByOffsetsCommand= new Commands.DelegateCommand(OnSortByOffsetsCommand)); } }\n\n        private void OnLanguage(SaintCoinach.Ex.Language newLanguage) {\n            Realm.GameData.ActiveLanguage = newLanguage;\n\n            OnPropertyChanged(() => IsEnglish);\n            OnPropertyChanged(() => IsJapanese);\n            OnPropertyChanged(() => IsGerman);\n            OnPropertyChanged(() => IsFrench);\n            OnPropertyChanged(() => IsChineseSimplified);\n            OnPropertyChanged(() => IsKorean);\n\n            Equipment.Refresh();\n            Territories.Refresh();\n        }\n\n        private void OnGameLocation() {\n            var res = System.Windows.MessageBox.Show(\"If you continue the application will restart and ask you for the game's installation directory, do you want to continue?\", \"Change game location\", System.Windows.MessageBoxButton.YesNo, System.Windows.MessageBoxImage.Exclamation);\n            if (res == System.Windows.MessageBoxResult.Yes) {\n                Properties.Settings.Default.GamePath = null;\n                Properties.Settings.Default.Save();\n               \n                var current = System.Diagnostics.Process.GetCurrentProcess();\n                var startInfo = new System.Diagnostics.ProcessStartInfo(current.MainModule.FileName);\n                startInfo.WorkingDirectory = Directory.GetCurrentDirectory();\n                System.Diagnostics.Process.Start(startInfo);\n                App.Current.Shutdown();\n            }\n        }\n\n        private void OnNewWindowCommand() {\n            Mouse.OverrideCursor = Cursors.Wait;\n            try {\n                var viewModel = new MainViewModel(Realm);\n                var window = new MainWindow() { DataContext = viewModel };\n                window.Show();\n            }\n            finally {\n                Mouse.OverrideCursor = null;\n            }\n        }\n\n        private void OnShowOffsetsCommand() {\n            Settings.Default.ShowOffsets = !Settings.Default.ShowOffsets;\n\n            OnPropertyChanged(() => ShowOffsets);\n        }\n\n        private void OnSortByOffsetsCommand() {\n            Settings.Default.SortByOffsets = !Settings.Default.SortByOffsets;\n\n            OnPropertyChanged(() => SortByOffsets);\n        }\n        #endregion\n    }\n}\n"
  },
  {
    "path": "Godbert/ViewModels/MapsViewModel.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\nusing System.Windows.Input;\n\nusing SaintCoinach.Graphics;\nusing SaintCoinach.Graphics.Viewer;\nusing SaintCoinach.Xiv;\n\nnamespace Godbert.ViewModels {\n    using Commands;\n\n    public class MapsViewModel : ObservableBase {\n        #region Fields\n        private Map[] _Maps;\n        private Map _SelectedMap;\n        #endregion\n\n        #region Properties\n        public MainViewModel Parent { get; private set; }\n        public IEnumerable<Map> Maps { get { return _Maps; } }\n        public Map SelectedMap {\n            get { return _SelectedMap; }\n            set {\n                _SelectedMap = value;\n                OnPropertyChanged(() => SelectedMap);\n            }\n        }\n        #endregion\n\n        #region Constructor\n        public MapsViewModel(MainViewModel parent) {\n            this.Parent = parent;\n\n            var allMaps = parent.Realm.GameData.GetSheet<Map>();\n            _Maps = allMaps.Where(m => m.TerritoryType != null && m.TerritoryType.Key > 1 && m.PlaceName != null && m.PlaceName.Key != 0 && m.RegionPlaceName != null && m.RegionPlaceName.Key != 0).ToArray();\n        }\n        #endregion\n\n        #region Commands\n        private ICommand _OpenCommand;\n\n        public ICommand OpenCommand { get { return _OpenCommand ?? (_OpenCommand = new DelegateCommand(OnOpen)); } }\n\n        private void OnOpen() {\n            if (SelectedMap == null)\n                return;\n\n            var n = string.Format(\"({0}) {1}: {2}\", SelectedMap.Id, SelectedMap.RegionPlaceName, SelectedMap.PlaceName);\n            try {\n                var t = SelectedMap.GetTerritory();\n                if (t == null)\n                    System.Windows.MessageBox.Show(string.Format(\"Could not find territory data for {0}.\", n), \"Territory not found\", System.Windows.MessageBoxButton.OK, System.Windows.MessageBoxImage.Error);\n                else\n                    Parent.EngineHelper.OpenInNew(n, (e) => new SaintCoinach.Graphics.Viewer.Content.ContentTerritory(e, t));\n            } catch (Exception e) {\n                System.Windows.MessageBox.Show(string.Format(\"Error reading territory for {0}:{1}{2}\", n, Environment.NewLine, e), \"Failure to read territory\", System.Windows.MessageBoxButton.OK, System.Windows.MessageBoxImage.Error);\n            }\n        }\n        #endregion\n\n        #region Refresh\n        public void Refresh() {\n            _Maps = _Maps.ToArray();\n            OnPropertyChanged(() => Maps);\n        }\n        #endregion\n    }\n}\n"
  },
  {
    "path": "Godbert/ViewModels/MonstersViewModel.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.IO;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\nusing System.Windows;\nusing System.Windows.Controls;\nusing System.Windows.Forms;\nusing System.Windows.Input;\nusing Ookii.Dialogs.Wpf;\nusing SaintCoinach.Graphics;\nusing SaintCoinach.Graphics.Viewer;\nusing SaintCoinach.Graphics.Viewer.Content;\nusing SaintCoinach.Graphics.Viewer.Interop;\nusing SaintCoinach.IO;\nusing SaintCoinach.Xiv;\nusing SaintCoinach.Xiv.ItemActions;\nusing Directory = System.IO.Directory;\nusing File = SaintCoinach.IO.File;\nusing ProgressBarStyle = Ookii.Dialogs.Wpf.ProgressBarStyle;\n\nnamespace Godbert.ViewModels {\n    using Commands;\n\n    public class MonstersViewModel : ObservableBase {\n        const string ImcPathFormat = \"chara/monster/m{0:D4}/obj/body/b{1:D4}/b{1:D4}.imc\";\n        const string ModelPathFormat = \"chara/monster/m{0:D4}/obj/body/b{1:D4}/model/m{0:D4}b{1:D4}.mdl\";\n        const string SkeletonPathFormat = \"chara/monster/m{0:D4}/skeleton/base/b{1:D4}/skl_m{0:D4}b{1:D4}.sklb\";\n        const string PapPathFormat = \"chara/monster/m{0:D4}/animation/a0001/bt_common/resident/monster.pap\";\n\n        #region Fields\n        private Models.ModelCharaHierarchy _Entries;\n        private object _SelectedEntry;\n\n        private bool _IsExporting = false;\n\n        public bool IsExporting {\n            get { return _IsExporting; }\n            private set {\n                _IsExporting = value;\n                OnPropertyChanged(() => IsExporting);\n            }\n        }\n        #endregion\n\n        #region Properties\n        public Models.ModelCharaHierarchy Entries {\n            get { return _Entries; }\n            private set {\n                _Entries = value;\n                OnPropertyChanged(() => Entries);\n            }\n        }\n        public object SelectedEntry {\n            get { return _SelectedEntry; }\n            set {\n                _SelectedEntry = value;\n                OnPropertyChanged(() => SelectedEntry);\n                OnPropertyChanged(() => IsValidSelection);\n            }\n        }\n        public bool IsValidSelection { get { return SelectedEntry is Models.ModelCharaVariant; } }\n        public MainViewModel Parent { get; private set; }\n        #endregion\n\n        #region Constructor\n        public MonstersViewModel(MainViewModel parent) {\n            this.Parent = parent;\n\n            var modelCharaSheet = Parent.Realm.GameData.GetSheet<ModelChara>();\n\n            Entries = new Models.ModelCharaHierarchy(\"m{0:D4}\", \"b{0:D4}\", \"v{0:D4}\");\n            foreach(var mc in modelCharaSheet.Where(mc => mc.Type == 3)) {\n                var imcPath = string.Format(ImcPathFormat, mc.ModelKey, mc.BaseKey);\n                var mdlPath = string.Format(ModelPathFormat, mc.ModelKey, mc.BaseKey);\n                if(!Parent.Realm.Packs.FileExists(imcPath) ||!Parent.Realm.Packs.FileExists(mdlPath))\n                    continue;\n                Entries.Add(mc);\n            }\n        }\n        #endregion\n\n        #region Command\n        private ICommand _AddCommand;\n        private ICommand _ReplaceCommand;\n        private ICommand _ExportCommand;\n        private ICommand _NewCommand;\n\n        public ICommand AddCommand { get { return _AddCommand ?? (_AddCommand = new DelegateCommand(OnAdd)); } }\n        public ICommand ReplaceCommand { get { return _ReplaceCommand ?? (_ReplaceCommand = new DelegateCommand(OnReplace)); } }\n        public ICommand ExportCommand { get { return _ExportCommand ?? (_ExportCommand = new DelegateCommand(OnExport)); } }\n        public ICommand NewCommand { get { return _NewCommand ?? (_NewCommand = new DelegateCommand(OnNew)); } }\n\n        private void OnAdd() {\n            Skeleton skele;\n            ModelDefinition model;\n            ImcVariant variant;\n            int m, b;\n            if (TryGetModel(out skele, out model, out variant, out m, out b))\n                Parent.EngineHelper.AddToLast(SelectedEntry.ToString(), (e) => CreateModel(e, skele, model, variant, m, b));\n        }\n        private void OnReplace() {\n            Skeleton skele;\n            ModelDefinition model;\n            ImcVariant variant;\n            int m, b;\n            if (TryGetModel(out skele, out model, out variant, out m, out b))\n                Parent.EngineHelper.ReplaceInLast(SelectedEntry.ToString(), (e) => CreateModel(e, skele, model, variant, m, b));\n        }\n        private void OnExport()\n        {\n            Skeleton skele;\n            ModelDefinition model;\n            ImcVariant variant;\n            int m, b;\n            if (!TryGetModel(out skele, out model, out variant, out m, out b)) return;\n\n            List<PapFile> paps = SearchPaps(model.File.Path, m);\n\n            VistaFolderBrowserDialog dialog = new VistaFolderBrowserDialog\n            {\n                Description = \"Select folder to export to\",\n                UseDescriptionForTitle = true\n            };\n\n            bool? result = dialog.ShowDialog();\n\n            if (result.HasValue && result.Value && !string.IsNullOrEmpty(dialog.SelectedPath))\n            {\n                Task.Run(() =>\n                {\n                    string identifier = SelectedEntry.ToString().Replace(\" / \", \"_\");\n                    string folderName = Path.Combine(dialog.SelectedPath, identifier);\n                    Directory.CreateDirectory(folderName);\n\n                    string fileName = Path.Combine(folderName, identifier + \".fbx\");\n\n                    // Set IsExporting for feedback\n                    IsExporting = true;\n                    int exportResult = FbxExport.ExportFbx(fileName, model.GetModel(0).Meshes, skele, paps);\n                    FbxExport.ExportMonsterMaterials(Parent.Realm, folderName, model.GetModel(0).Definition.Materials, variant);\n                    IsExporting = false;\n                    \n                    if (exportResult == 0)\n                        System.Windows.MessageBox.Show(\"The export of \" + Path.GetFileName(fileName) + \" has completed.\",\n                            \"Export Complete\",\n                            MessageBoxButton.OK,\n                            MessageBoxImage.Information,\n                            MessageBoxResult.OK,\n                            System.Windows.MessageBoxOptions.DefaultDesktopOnly);\n                    else\n                        System.Windows.MessageBox.Show(\"The export of \" + Path.GetFileName(fileName) + \" has failed.\",\n                            \"Export Failed\",\n                            MessageBoxButton.OK,\n                            MessageBoxImage.Error,\n                            MessageBoxResult.OK,\n                            System.Windows.MessageBoxOptions.DefaultDesktopOnly);\n                });\n            }\n        }\n        private void OnNew() {\n            Skeleton skele;\n            ModelDefinition model;\n            ImcVariant variant;\n            int m, b;\n            if (TryGetModel(out skele, out model, out variant, out m, out b))\n                Parent.EngineHelper.OpenInNew(SelectedEntry.ToString(), (e) => CreateModel(e, skele, model, variant, m, b));\n        }\n\n        static string[] DefaultAnimationNames = { \"cbnm_id0\", \"cbbm_id0\" };\n\n        private List<PapFile> SearchPaps(string modelPath, int m)\n        {\n            /*  The files found in these paths will all be exported\n             *  If more paths are found, you can add them here\n             *  A good starting point might be attempting to determine how\n             *  a0002 animations are decided?   */\n            string[] searchPaths =\n            {\n                \"chara/monster/m{0:D4}/animation/a{1:D4}/bt_common/mon_sp/m{0:D4}\",\n                \"chara/monster/m{0:D4}/animation/a{1:D4}/bt_common/mon_sp/m{0:D4}/hide\",\n                \"chara/monster/m{0:D4}/animation/a{1:D4}/bt_common/mon_sp/m{0:D4}/show\",\n                \"chara/monster/m{0:D4}/animation/a{1:D4}/bt_common/event\",\n                \"chara/monster/m{0:D4}/animation/a{1:D4}/bt_common/minion\",\n                \"chara/monster/m{0:D4}/animation/a{1:D4}/bt_common/resident\",\n                \"chara/monster/m{0:D4}/animation/a{1:D4}/bt_common/specialpop\"\n            };\n\n            SaintCoinach.IO.Directory d;\n            List<PapFile> allFiles = new List<PapFile>();\n\n            // Animations are in the same pack as the model itself\n            Pack p = Parent.Realm.Packs.GetPack(PackIdentifier.Get(modelPath));\n\n            foreach (string path in searchPaths) {\n                string currentFullPath = string.Format(path, m, 1);\n                ((IndexSource)p.Source).TryGetDirectory(currentFullPath, out d);\n\n                if (d == null)\n                    continue;\n\n                IEnumerator<File> dirEnumerator = d.GetEnumerator();\n                while (dirEnumerator.MoveNext())\n                    allFiles.Add(new PapFile(dirEnumerator.Current));\n                dirEnumerator.Dispose();\n            }\n            return allFiles;\n        }\n\n        private IComponent CreateModel(Engine engine, Skeleton skeleton, ModelDefinition model, ImcVariant variant, int m, int b) {\n            \n            var component = new AnimatedModel(engine, skeleton, variant, model, ModelQuality.High) {};\n            var papPath = string.Format(PapPathFormat, m, b);\n\n            SaintCoinach.IO.File papFileBase;\n            if (Parent.Realm.Packs.TryGetFile(papPath, out papFileBase)) {\n                var anim = new AnimationContainer(skeleton, new PapFile(papFileBase));\n\n                var hasAnim = false;\n                for(var i = 0; i < DefaultAnimationNames.Length && !hasAnim; ++i) {\n                    var n = DefaultAnimationNames[i];\n                    if (anim.AnimationNames.Contains(n)) {\n                        component.AnimationPlayer.Animation = anim.Get(n);\n                        hasAnim = true;\n                    }\n                }\n                \n                if (!hasAnim)\n                    component.AnimationPlayer.Animation = anim.Get(0);\n            }\n            return component;\n        }\n\n        private bool TryGetModel(out Skeleton skeleton, out ModelDefinition model, out ImcVariant variant, out int m, out int b) {\n            model = null;\n            skeleton = null;\n            variant = ImcVariant.Default;\n            m = 0;\n            b = 0;\n\n            var asVariant = SelectedEntry as Models.ModelCharaVariant;\n            if (asVariant == null)\n                return false;\n\n            int v = asVariant.Value;\n            b = asVariant.Parent.Value;\n            m = asVariant.Parent.Parent.Value;\n\n            var imcPath = string.Format(ImcPathFormat, m, b);\n            var mdlPath = string.Format(ModelPathFormat, m, b);\n            var sklPath = string.Format(SkeletonPathFormat, m, 1);// b);\n\n            SaintCoinach.IO.File imcFileBase;\n            SaintCoinach.IO.File mdlFileBase;\n            if (!Parent.Realm.Packs.TryGetFile(imcPath, out imcFileBase) || !Parent.Realm.Packs.TryGetFile(mdlPath, out mdlFileBase) || !(mdlFileBase is ModelFile)) {\n                System.Windows.MessageBox.Show(string.Format(\"Unable to find files for {0}.\", asVariant), \"File not found\", System.Windows.MessageBoxButton.OK, System.Windows.MessageBoxImage.Error);\n                return false;\n            }\n            \n            SaintCoinach.IO.File sklFileBase;\n            if(!Parent.Realm.Packs.TryGetFile(sklPath, out sklFileBase)) {\n                System.Windows.MessageBox.Show(string.Format(\"Unable to find skeleton for {0}.\", asVariant), \"File not found\", System.Windows.MessageBoxButton.OK, System.Windows.MessageBoxImage.Error);\n                return false;\n            }\n\n            skeleton = new Skeleton(new SklbFile(sklFileBase));\n\n            try {\n                var imcFile = new ImcFile(imcFileBase);\n                model = ((ModelFile)mdlFileBase).GetModelDefinition();\n                variant = imcFile.GetVariant(v);\n\n                return true;\n            } catch (Exception e) {\n                System.Windows.MessageBox.Show(string.Format(\"Unable to load model for {0}:{1}{2}\", asVariant, Environment.NewLine, e), \"Failure to load\", System.Windows.MessageBoxButton.OK, System.Windows.MessageBoxImage.Error);\n                return false;\n            }\n        }\n        #endregion\n\n        #region Brute-force\n        private bool _IsBruteForceAvailable = true;\n        private ICommand _BruteForceCommand;\n\n        public bool IsBruteForceAvailable {\n            get { return _IsBruteForceAvailable; }\n            private set {\n                _IsBruteForceAvailable = value;\n                OnPropertyChanged(() => IsBruteForceAvailable);\n            }\n        }\n        public ICommand BruteForceCommand { get { return _BruteForceCommand ?? (_BruteForceCommand = new DelegateCommand(OnBruteForce)); } }\n\n        private void OnBruteForce() {\n            IsBruteForceAvailable = false;\n\n            var progDlg = new Ookii.Dialogs.Wpf.ProgressDialog();\n            progDlg.WindowTitle = \"Brute-forcing\";\n            progDlg.Text = \"This is going to take a while...\";\n            progDlg.DoWork += DoBruteForceWork;\n            progDlg.RunWorkerCompleted += OnBruteForceComplete;\n            progDlg.ShowDialog(System.Windows.Application.Current.MainWindow);\n            progDlg.ProgressBarStyle = Ookii.Dialogs.Wpf.ProgressBarStyle.ProgressBar;\n            progDlg.ShowTimeRemaining = true;\n        }\n\n        void OnBruteForceComplete(object sender, System.ComponentModel.RunWorkerCompletedEventArgs eventArgs) {\n            if (eventArgs.Cancelled)\n                IsBruteForceAvailable = true;\n        }\n\n        void DoBruteForceWork(object sender, System.ComponentModel.DoWorkEventArgs eventArgs) {\n            var dlg = (Ookii.Dialogs.Wpf.ProgressDialog)sender;\n\n            var newEntries = new Models.ModelCharaHierarchy(Entries.MainFormat, Entries.SubFormat, Entries.VariantFormat);\n            for (var m = 0; m < 10000; ++m) {\n                if (dlg.CancellationPending)\n                    return;\n                dlg.ReportProgress(m / 100, null, string.Format(\"Current progress: {0:P}\", m / 10000.0));\n                for (var b = 0; b < 10000; ++b) {\n\n                    var imcPath = string.Format(ImcPathFormat, m, b);\n                    SaintCoinach.IO.File imcBase;\n                    if (!Parent.Realm.Packs.TryGetFile(imcPath, out imcBase))\n                        continue;\n                    try {\n                        var imc = new SaintCoinach.Graphics.ImcFile(imcBase);\n                        for (var v = 1; v < imc.Count; ++v) {\n                            if (Entries.Contains(m, b, v))\n                                continue;\n\n                            var any = false;\n                            foreach (var p in imc.Parts) {\n                                if (p.Variants[v].Variant != 0) {\n                                    any = true;\n                                    break;\n                                }\n                            }\n                            if (any)\n                                newEntries.Add(m, b, v);\n                        }\n                    } catch (Exception ex) {\n                        Console.Error.WriteLine(\"Failed parsing imc file {0}:{1}{2}\", imcPath, Environment.NewLine, ex);\n                    }\n                }\n            }\n            Entries = newEntries;\n        }\n        #endregion\n    }\n}\n"
  },
  {
    "path": "Godbert/ViewModels/TerritoryViewModel.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Windows.Input;\nusing SaintCoinach.Graphics;\nusing SaintCoinach.Graphics.Viewer;\nusing SaintCoinach;\nusing SaintCoinach.Xiv;\n\nnamespace Godbert.ViewModels {\n    using Commands;\n    using SharpDX;\n    using SaintCoinach.Graphics;\n\n    public class TerritoryViewModel : ObservableBase {\n        class ExportCancelException : Exception {\n            public ExportCancelException(string message) : base(message) {\n\n            }\n        }\n        #region Fields\n        private TerritoryView[] _AllTerritories;\n        private TerritoryView[] _FilteredTerritories;\n        private TerritoryView _SelectedTerritory;\n        private string _FilterTerm;\n        private Ookii.Dialogs.Wpf.ProgressDialog _Progress;\n        #endregion\n\n        #region Properties\n        public MainViewModel Parent { get; private set; }\n        public IEnumerable<TerritoryView> AllTerritories { get { return _AllTerritories; } }\n        public IEnumerable<TerritoryView> FilteredTerritories { get { return _FilteredTerritories; } }\n        public TerritoryView SelectedTerritory {\n            get { return _SelectedTerritory; }\n            set {\n                _SelectedTerritory = value;\n                OnPropertyChanged(() => SelectedTerritory);\n            }\n        }\n        public string FilterTerm {\n            get { return _FilterTerm; }\n            set {\n                _FilterTerm = value;\n\n                if (string.IsNullOrWhiteSpace(value))\n                    _FilteredTerritories = _AllTerritories;\n                else\n                    _FilteredTerritories = _AllTerritories.Where(e => e.Name.IndexOf(value, StringComparison.OrdinalIgnoreCase) >= 0).ToArray();\n\n                OnPropertyChanged(() => FilterTerm);\n                OnPropertyChanged(() => FilteredTerritories);\n            }\n        }\n        #endregion\n\n        #region Constructor\n        public TerritoryViewModel(MainViewModel parent) {\n            this.Parent = parent;\n\n            var allTerritoryTypes = parent.Realm.GameData.GetSheet<TerritoryType>();\n\n            _AllTerritories = allTerritoryTypes\n                .Where(t => !string.IsNullOrEmpty(t.Bg.ToString()))\n                .Select(t => new TerritoryView(t))\n                .OrderBy(m => m.PlaceNames)\n                .ThenBy(m => m.TerritoryType.Key)\n                .ToArray();\n\n            _FilteredTerritories = _AllTerritories;\n        }\n        #endregion\n\n        #region Commands\n        private ICommand _OpenCommand;\n        private ICommand _ExportCommand;\n\n        public ICommand OpenCommand { get { return _OpenCommand ?? (_OpenCommand = new DelegateCommand(OnOpen)); } }\n        public ICommand ExportCommand { get { return _ExportCommand ?? (_ExportCommand = new DelegateCommand(OnExport)); } }\n        private void OnOpen() {\n            if (SelectedTerritory == null)\n                return;\n\n            try {\n                var t = new Territory(SelectedTerritory.TerritoryType);\n\n                if (t == null)\n                    System.Windows.MessageBox.Show(string.Format(\"Could not find territory data for {0}.\", SelectedTerritory.Name), \"Territory not found\", System.Windows.MessageBoxButton.OK, System.Windows.MessageBoxImage.Error);\n                else\n                    Parent.EngineHelper.OpenInNew(SelectedTerritory.Name, (e) => new SaintCoinach.Graphics.Viewer.Content.ContentTerritory(e, t));\n            } catch (Exception e) {\n                System.Windows.MessageBox.Show(string.Format(\"Error reading territory for {0}:{1}{2}\", SelectedTerritory.Name, Environment.NewLine, e), \"Failure to read territory\", System.Windows.MessageBoxButton.OK, System.Windows.MessageBoxImage.Error);\n            }\n        }\n\n        private void OnExport() {\n            \n            if (SelectedTerritory == null)\n                return;\n            try {\n                Territory territory = new Territory(SelectedTerritory.TerritoryType);\n                if (territory == null) {\n                    System.Windows.MessageBox.Show($\"Could not find territory data for {SelectedTerritory.Name}\", \"Territory not found\", System.Windows.MessageBoxButton.OK, System.Windows.MessageBoxImage.Error);\n                    return;\n                }\n                _Progress = new Ookii.Dialogs.Wpf.ProgressDialog() {\n                    WindowTitle = \"Exporting territory \" + territory.Name\n                };\n                _Progress.Show();\n                _Progress.DoWork += (object sender, System.ComponentModel.DoWorkEventArgs eventArgs) => {\n                    _Export(territory, (Ookii.Dialogs.Wpf.ProgressDialog)sender);\n                };\n            }\n            catch (Exception e) {\n                System.Windows.MessageBox.Show(string.Format(\"Error reading territory for {0}:{1}{2}\", SelectedTerritory.Name, Environment.NewLine, e), \"Failure to read territory\", System.Windows.MessageBoxButton.OK, System.Windows.MessageBoxImage.Error);\n            }\n            _Progress = null;\n        }\n        private void _Export(Territory territory, Ookii.Dialogs.Wpf.ProgressDialog progress) {\n\n            string teriName = territory.Name;\n\n            try {\n                string currentTitle = \"\";\n                var _ExportDirectory = $\"./{territory.Name}/\";\n                Dictionary<string, int> objCount = new Dictionary<string, int>();\n                if (!System.IO.Directory.Exists(Environment.CurrentDirectory + $\"{_ExportDirectory}\")) {\n                    System.IO.Directory.CreateDirectory(Environment.CurrentDirectory + $\"{_ExportDirectory}\");\n                }\n\n                var teriFileName = $\"./{_ExportDirectory}/{territory.Name}.obj\";\n                var fileName = teriFileName;\n                var lightsFileName = $\"./{_ExportDirectory}/{territory.Name}-lights.txt\";\n                var _ExportFileName = fileName;\n                {\n                    var f = System.IO.File.Create(fileName);\n                    f.Close();\n                }\n                System.IO.File.AppendAllText(fileName, $\"o {territory.Name}\\n\");\n                System.IO.File.WriteAllText(lightsFileName, \"\");\n                int lights = 0;\n                List<string> lightStrs = new List<string>() { \"import bpy\" };\n                List<string> vertStr = new List<string>();\n                Dictionary<string, bool> exportedPaths = new Dictionary<string, bool>();\n                UInt64 vs = 1, vt = 1, vn = 1, i = 0;\n                Matrix IdentityMatrix = Matrix.Identity;\n\n                void ExportMaterials(Material m, string path) {\n                    vertStr.Add($\"mtllib {path}.mtl\");\n                    bool found = false;\n                    if (exportedPaths.TryGetValue(path, out found)) {\n                        return;\n                    }\n                    exportedPaths.Add(path, true);\n                    System.IO.File.Delete($\"{_ExportDirectory}/{path}.mtl\");\n                    System.IO.File.AppendAllText($\"{_ExportDirectory}/{path}.mtl\", $\"newmtl {path}\\n\");\n                    foreach (var img in m.TexturesFiles) {\n                        var mtlName = img.Path.Replace('/', '_');\n                        if (exportedPaths.TryGetValue(path + mtlName, out found)) {\n                            continue;\n                        }\n\n                        //SaintCoinach.Imaging.ImageConverter.Convert(img).Save($\"{_ExportDirectory}/{mtlName}.png\");\n\n                        if (mtlName.Contains(\"_dummy_\"))\n                            continue;\n\n                        var ddsBytes = SaintCoinach.Imaging.ImageConverter.GetDDS(img);\n\n                        var fileExt = ddsBytes != null ? \".dds\" : \".png\";\n                        \n                        if (fileExt == \".dds\")\n                            System.IO.File.WriteAllBytes($\"{_ExportDirectory}/{mtlName}.dds\", ddsBytes);\n                        else\n                            SaintCoinach.Imaging.ImageConverter.Convert(img).Save($\"{_ExportDirectory}/{mtlName}.png\");\n\n\n                        if (mtlName.Contains(\"_n.tex\")) {\n                            System.IO.File.AppendAllText($\"./{_ExportDirectory}/{path}.mtl\", $\"bump {mtlName}{fileExt}\\n\");\n                        }\n                        else if (mtlName.Contains(\"_s.tex\")) {\n                            System.IO.File.AppendAllText($\"./{_ExportDirectory}/{path}.mtl\", $\"map_Ks {mtlName}{fileExt}\\n\");\n                        }\n                        else if (!mtlName.Contains(\"_a.tex\")) {\n                            System.IO.File.AppendAllText($\"./{_ExportDirectory}/{path}.mtl\", $\"map_Kd {mtlName}{fileExt}\\n\");\n                        }\n                        else {\n                            System.IO.File.AppendAllText($\"./{_ExportDirectory}/{path}.mtl\", $\"map_Ka {mtlName}{fileExt}\\n\");\n                        }\n\n                        exportedPaths.Add(path + mtlName, true);\n                    }\n                }\n\n                Matrix CreateMatrix(SaintCoinach.Graphics.Vector3 translation, SaintCoinach.Graphics.Vector3 rotation, SaintCoinach.Graphics.Vector3 scale) {\n                    return (Matrix.Scaling(scale.ToDx())\n                        * Matrix.RotationX(rotation.X)\n                        * Matrix.RotationY(rotation.Y)\n                        * Matrix.RotationZ(rotation.Z)\n                        * Matrix.Translation(translation.ToDx()));\n                }\n\n                void ExportMesh(ref Mesh mesh, ref Matrix lgbTransform, ref string materialName, ref string modelFilePath,\n                    ref Matrix rootGimTransform, ref Matrix currGimTransform, ref Matrix modelTransform) {\n                    i++;\n                    if (progress.CancellationPending)\n                        throw new ExportCancelException(\"User canceled export\");\n                    var k = 0;\n                    UInt64 tempVs = 0, tempVn = 0, tempVt = 0;\n                    foreach (var v in mesh.Vertices) {\n\n                        var x = v.Position.Value.X;\n                        var y = v.Position.Value.Y;\n                        var z = v.Position.Value.Z;\n                        var w = v.Position.Value.W;\n\n                        var transform = (modelTransform * rootGimTransform * currGimTransform) * lgbTransform;\n\n                        var t = Matrix.Translation(x, y, z) * transform;\n                        x = t.TranslationVector.X;\n                        y = t.TranslationVector.Y;\n                        z = t.TranslationVector.Z;\n\n                        // .Replace(',','.') cause decimal separator locale memes\n                        if (v.Color != null)\n                            vertStr.Add($\"v {x} {y} {z} {v.Color.Value.X} {v.Color.Value.Y} {v.Color.Value.Z} {v.Color.Value.W}\".Replace(',','.'));\n                        else\n                            vertStr.Add($\"v {x} {y} {z}\".Replace(',','.'));\n\n                        tempVs++;\n\n                        vertStr.Add($\"vn {v.Normal.Value.X} {v.Normal.Value.Y} {v.Normal.Value.Z}\".Replace(',', '.'));\n                        tempVn++;\n\n                        if (v.UV != null) {\n                            vertStr.Add($\"vt {v.UV.Value.X} {v.UV.Value.Y * -1.0}\".Replace(',', '.'));\n                            tempVt++;\n                        }\n                    }\n                    vertStr.Add($\"g {modelFilePath}_{i.ToString()}_{k.ToString()}\");\n                    vertStr.Add($\"usemtl {materialName}\");\n                    for (UInt64 j = 0; j + 3 < (UInt64)mesh.Indices.Length + 1; j += 3) {\n                        vertStr.Add(\n                            $\"f \" +\n                            $\"{mesh.Indices[j] + vs}/{mesh.Indices[j] + vt}/{mesh.Indices[j] + vn} \" +\n                            $\"{mesh.Indices[j + 1] + vs}/{mesh.Indices[j + 1] + vt}/{mesh.Indices[j + 1] + vn} \" +\n                            $\"{mesh.Indices[j + 2] + vs}/{mesh.Indices[j + 2] + vt}/{mesh.Indices[j + 2] + vn}\");\n                    }\n                    if (i % 1000 == 0) {\n                        System.IO.File.AppendAllLines(_ExportFileName, vertStr);\n                        vertStr.Clear();\n                    }\n                    vs += tempVs;\n                    vn += tempVn;\n                    vt += tempVt;\n                }\n\n                Dictionary<string, bool> exportedSgbFiles = new Dictionary<string, bool>();\n                void ExportSgbModels(SaintCoinach.Graphics.Sgb.SgbFile sgbFile, ref Matrix lgbTransform, ref Matrix rootGimTransform, ref Matrix currGimTransform) {\n                    foreach (var sgbGroup in sgbFile.Data.OfType<SaintCoinach.Graphics.Sgb.SgbGroup>()) {\n                        bool newGroup = true;\n\n                        foreach (var sgb1CEntry in sgbGroup.Entries.OfType<SaintCoinach.Graphics.Sgb.SgbGroup1CEntry>()) {\n                            if (sgb1CEntry.Gimmick != null) {\n                                ExportSgbModels(sgb1CEntry.Gimmick, ref lgbTransform, ref IdentityMatrix, ref IdentityMatrix);\n                                foreach (var subGimGroup in sgb1CEntry.Gimmick.Data.OfType<SaintCoinach.Graphics.Sgb.SgbGroup>()) {\n                                    foreach (var subGimEntry in subGimGroup.Entries.OfType<SaintCoinach.Graphics.Sgb.SgbGimmickEntry>()) {\n                                        var subGimTransform = CreateMatrix(subGimEntry.Header.Translation, subGimEntry.Header.Rotation, subGimEntry.Header.Scale);\n                                        ExportSgbModels(subGimEntry.Gimmick, ref lgbTransform, ref IdentityMatrix, ref subGimTransform);\n                                    }\n                                }\n                            }\n                        }\n\n                        foreach (var mdl in sgbGroup.Entries.OfType<SaintCoinach.Graphics.Sgb.SgbModelEntry>()) {\n                            Model hq = null;\n                            var filePath = mdl.ModelFilePath;\n                            var modelTransform = CreateMatrix(mdl.Header.Translation, mdl.Header.Rotation, mdl.Header.Scale);\n\n                            progress.ReportProgress(0, currentTitle, filePath);\n                            try {\n                                hq = mdl.Model.Model.GetModel(ModelQuality.High);\n                            }\n                            catch (Exception e) {\n                                System.Diagnostics.Debug.WriteLine($\"Unable to load model for {mdl.Name} path: {filePath}.  Exception: {e.Message}\");\n                                continue;\n                            }\n                            if (newGroup) {\n                                //vertStr.Add($\"o {sgbFile.File.Path}_{sgbGroup.Name}_{i}\");\n                                newGroup = false;\n                            }\n                            for (var j = 0; j < hq.Meshes.Length; ++j) {\n                                var mesh = hq.Meshes[j];\n                                var mtl = mesh.Material.Get();\n                                var path = mtl.File.Path.Replace('/', '_').Replace(\".mtrl\", \".tex\");\n\n                                ExportMaterials(mtl, path);\n                                ExportMesh(ref mesh, ref lgbTransform, ref path, ref filePath, ref rootGimTransform, ref currGimTransform, ref modelTransform);\n                            }\n                        }\n\n                        foreach (var light in sgbGroup.Entries.OfType<SaintCoinach.Graphics.Sgb.SgbLightEntry>()) {\n                            var pos = light.Header.Translation;\n                            var transform = (Matrix.Translation(pos.X, pos.Y, pos.Z) * (rootGimTransform * currGimTransform) * lgbTransform).TranslationVector;\n                            pos.X = transform.X;\n                            pos.Y = transform.Y;\n                            pos.Z = transform.Z;\n\n                            lightStrs.Add($\"#LIGHT_{lights++}_{light.Name}_{light.Header.UnknownId}\");\n                            lightStrs.Add($\"#pos {pos.X} {pos.Y} {pos.Z}\");\n                            lightStrs.Add($\"#UNKNOWNFLAGS 0x{light.Header.UnknownFlag1:X8} 0x{light.Header.UnknownFlag2:X8} 0x{light.Header.UnknownFlag3:X8} 0x{light.Header.UnknownFlag4:X8}\");\n                            lightStrs.Add($\"#UNKNOWN {light.Header.Rotation.X} {light.Header.Rotation.Y} {light.Header.Rotation.Z}\");\n                            lightStrs.Add($\"#UNKNOWN2 {light.Header.Scale.X} {light.Header.Scale.Y} {light.Header.Scale.Z}\");\n                            lightStrs.Add($\"#unk {light.Header.Entry1.X} {light.Header.Entry1.Y}\");\n                            lightStrs.Add($\"#unk2 {light.Header.Entry2.X} {light.Header.Entry2.Y}\");\n                            lightStrs.Add($\"#unk3 {light.Header.Entry3.X} {light.Header.Entry3.Y}\");\n                            lightStrs.Add($\"#unk4 {light.Header.Entry4.X} {light.Header.Entry4.Y}\");\n                            lightStrs.Add(\"\");\n                        }\n                    }\n                }\n\n                progress.ReportProgress(0, currentTitle = \"Terrain\", \"\");\n                if (territory.Terrain != null) {\n                    foreach (var part in territory.Terrain.Parts) {\n                        var hq = part.Model.GetModel(ModelQuality.High);\n                        var filePath = hq.Definition.File.Path;\n                        var lgbTransform = CreateMatrix(part.Translation, part.Rotation, part.Scale);\n\n                        progress.ReportProgress(0, currentTitle, part.Model.File.Path);\n\n                        for (var j = 0; j < hq.Meshes.Length; ++j) {\n                            var mesh = hq.Meshes[j];\n                            var mtl = mesh.Material.Get();\n                            var path = mtl.File.Path.Replace('/', '_').Replace(\".mtrl\", \".tex\");\n\n                            ExportMaterials(mtl, path);\n                            ExportMesh(ref mesh, ref lgbTransform, ref path, ref filePath, ref IdentityMatrix, ref IdentityMatrix, ref IdentityMatrix);\n                        }\n                    }\n                }\n\n                System.IO.File.AppendAllLines(_ExportFileName, vertStr);\n                vertStr.Clear();\n                vs = 1; vn = 1; vt = 1; i = 0;\n                foreach (var lgb in territory.LgbFiles) {\n                    foreach (var lgbGroup in lgb.Groups) {\n\n                        bool newGroup = true;\n                        foreach (var part in lgbGroup.Entries) {\n                            if (part == null)\n                                continue;\n\n                            if (newGroup && (part.Type == SaintCoinach.Graphics.Lgb.LgbEntryType.Model || part.Type == SaintCoinach.Graphics.Lgb.LgbEntryType.Gimmick || part.Type == SaintCoinach.Graphics.Lgb.LgbEntryType.Light)) {\n                                progress.WindowTitle = $\"Exporting {territory.Name} ({lgbGroup.Name})\";\n                                progress.ReportProgress(0, currentTitle = $\"Exporting {territory.Name} Group {lgbGroup.Name}\", $\"Group {lgbGroup.Name}\");\n\n                                newGroup = false;\n\n                                System.IO.File.AppendAllLines(_ExportFileName, vertStr);\n                                vertStr.Clear();\n\n                                //vertStr.Add($\"o {lgbGroup.Name}\");\n\n                                vs = 1; vn = 1; vt = 1; i = 0;\n                                _ExportFileName = $\"./{_ExportDirectory}/{teriName}-{lgbGroup.Name}.obj\";\n                                lightsFileName = $\"./{_ExportDirectory}/{teriName}-{lgbGroup.Name}-lights.txt\";\n\n                                var f = System.IO.File.Create(_ExportFileName);\n                                f.Close();\n                                f = System.IO.File.Create(lightsFileName);\n                                f.Close();\n                            }\n\n                            switch (part.Type) {\n                                case SaintCoinach.Graphics.Lgb.LgbEntryType.Model:\n                                    var asMdl = part as SaintCoinach.Graphics.Lgb.LgbModelEntry;\n                                    progress.ReportProgress(0, currentTitle = \"Exporting LgbModel\", asMdl.ModelFilePath);\n\n                                    if (asMdl.Model == null)\n                                        continue;\n\n                                    var hq = asMdl.Model.Model.GetModel(ModelQuality.High);\n                                    var lgbTransform = CreateMatrix(asMdl.Header.Translation, asMdl.Header.Rotation, asMdl.Header.Scale);\n                                    var filePath = asMdl.ModelFilePath;\n\n                                    for (var j = 0; j < hq.Meshes.Length; ++j) {\n                                        var mesh = hq.Meshes[j];\n                                        var mtl = mesh.Material.Get();\n                                        var path = mtl.File.Path.Replace('/', '_').Replace(\".mtrl\", \".tex\");\n\n                                        ExportMaterials(mtl, path);\n                                        ExportMesh(ref mesh, ref lgbTransform, ref path, ref filePath, ref IdentityMatrix, ref IdentityMatrix, ref IdentityMatrix);\n                                    }\n                                    break;\n                                case SaintCoinach.Graphics.Lgb.LgbEntryType.Gimmick:\n                                    var asGim = part as SaintCoinach.Graphics.Lgb.LgbGimmickEntry;\n                                    if (asGim.Gimmick == null)\n                                        continue;\n                                               \n                                    progress.ReportProgress(0, currentTitle = $\"Exporting Gimmick {asGim.Name} {asGim.Header.GimmickId}\", \"\");\n                                    \n                                    lgbTransform = CreateMatrix(asGim.Header.Translation, asGim.Header.Rotation, asGim.Header.Scale);\n\n                                    ExportSgbModels(asGim.Gimmick, ref lgbTransform, ref IdentityMatrix, ref IdentityMatrix);\n                                    foreach (var rootGimGroup in asGim.Gimmick.Data.OfType<SaintCoinach.Graphics.Sgb.SgbGroup>()) {\n                                        foreach (var rootGimEntry in rootGimGroup.Entries.OfType<SaintCoinach.Graphics.Sgb.SgbGimmickEntry>()) {\n                                            if (rootGimEntry.Gimmick != null) {\n                                                var rootGimTransform = CreateMatrix(rootGimEntry.Header.Translation, rootGimEntry.Header.Rotation, rootGimEntry.Header.Scale);\n                                                ExportSgbModels(rootGimEntry.Gimmick, ref lgbTransform, ref rootGimTransform, ref IdentityMatrix);\n                                                foreach (var subGimGroup in rootGimEntry.Gimmick.Data.OfType<SaintCoinach.Graphics.Sgb.SgbGroup>()) {\n                                                    foreach (var subGimEntry in subGimGroup.Entries.OfType<SaintCoinach.Graphics.Sgb.SgbGimmickEntry>()) {\n                                                        var subGimTransform = CreateMatrix(subGimEntry.Header.Translation, subGimEntry.Header.Rotation, subGimEntry.Header.Scale);\n                                                        ExportSgbModels(subGimEntry.Gimmick, ref lgbTransform, ref rootGimTransform, ref subGimTransform);\n                                                    }\n                                                }\n                                            }\n                                        }\n                                    }\n                                    break;\n                                case SaintCoinach.Graphics.Lgb.LgbEntryType.EventObject:\n                                    var asEobj = part as SaintCoinach.Graphics.Lgb.LgbEventObjectEntry;\n                                    if (asEobj.Gimmick == null)\n                                        continue;\n\n                                    progress.ReportProgress(0, currentTitle = $\"Exporting EObj {asEobj.Name} {asEobj.Header.EventObjectId} {asEobj.Header.GimmickId}\", \"\");\n\n                                    lgbTransform = CreateMatrix(asEobj.Header.Translation, asEobj.Header.Rotation, asEobj.Header.Scale);\n\n                                    ExportSgbModels(asEobj.Gimmick, ref lgbTransform, ref IdentityMatrix, ref IdentityMatrix);\n                                    foreach (var rootGimGroup in asEobj.Gimmick.Data.OfType<SaintCoinach.Graphics.Sgb.SgbGroup>()) {\n                                        foreach (var rootGimEntry in rootGimGroup.Entries.OfType<SaintCoinach.Graphics.Sgb.SgbGimmickEntry>()) {\n                                            if (rootGimEntry.Gimmick != null) {\n                                                var rootGimTransform = CreateMatrix(rootGimEntry.Header.Translation, rootGimEntry.Header.Rotation, rootGimEntry.Header.Scale);\n                                                ExportSgbModels(rootGimEntry.Gimmick, ref lgbTransform, ref rootGimTransform, ref IdentityMatrix);\n                                                foreach (var subGimGroup in rootGimEntry.Gimmick.Data.OfType<SaintCoinach.Graphics.Sgb.SgbGroup>()) {\n                                                    foreach (var subGimEntry in subGimGroup.Entries.OfType<SaintCoinach.Graphics.Sgb.SgbGimmickEntry>()) {\n                                                        var subGimTransform = CreateMatrix(subGimEntry.Header.Translation, subGimEntry.Header.Rotation, subGimEntry.Header.Scale);\n                                                        ExportSgbModels(subGimEntry.Gimmick, ref lgbTransform, ref rootGimTransform, ref subGimTransform);\n                                                    }\n                                                }\n                                            }\n                                        }\n                                    }\n                                    break;\n                                case SaintCoinach.Graphics.Lgb.LgbEntryType.Light:\n                                    var asLight = part as SaintCoinach.Graphics.Lgb.LgbLightEntry;\n                                    lightStrs.Add($\"#LIGHT_{lights++}_{asLight.Name}_{asLight.Header.UnknownId}\");\n                                    lightStrs.Add($\"#pos {asLight.Header.Translation.X} {asLight.Header.Translation.Y} {asLight.Header.Translation.Z}\");\n                                    lightStrs.Add($\"#UNKNOWNFLAGS 0x{asLight.Header.UnknownFlag1:X8} 0x{asLight.Header.UnknownFlag2:X8} 0x{asLight.Header.UnknownFlag3:X8} 0x{asLight.Header.UnknownFlag4:X8}\");\n                                    lightStrs.Add($\"#UNKNOWN {asLight.Header.Rotation.X} {asLight.Header.Rotation.Y} {asLight.Header.Rotation.Z}\");\n                                    lightStrs.Add($\"#UNKNOWN2 {asLight.Header.Scale.X} {asLight.Header.Scale.Y} {asLight.Header.Scale.Z}\");\n                                    lightStrs.Add($\"#unk {asLight.Header.Entry1.X} {asLight.Header.Entry1.Y}\");\n                                    lightStrs.Add($\"#unk2 {asLight.Header.Entry2.X} {asLight.Header.Entry2.Y}\");\n                                    lightStrs.Add($\"#unk3 {asLight.Header.Entry3.X} {asLight.Header.Entry3.Y}\");\n                                    lightStrs.Add($\"#unk4 {asLight.Header.Entry4.X} {asLight.Header.Entry4.Y}\");\n                                    lightStrs.Add(\"\");\n                                    break;\n                            }\n                        }\n                        System.IO.File.AppendAllLines(lightsFileName, lightStrs);\n                        lightStrs.Clear();\n                    }\n                }\n                System.IO.File.AppendAllLines(_ExportFileName, vertStr);\n                vertStr.Clear();\n                System.IO.File.AppendAllLines(lightsFileName, lightStrs);\n                lightStrs.Clear();\n                System.Windows.Forms.MessageBox.Show(\"Finished exporting \" + territory.Name, \"\", System.Windows.Forms.MessageBoxButtons.OK, System.Windows.Forms.MessageBoxIcon.Information);\n            }\n            catch (ExportCancelException e) {\n                System.Windows.Forms.MessageBox.Show(e.Message, $\"Canceled {teriName} export\");\n            }\n            catch (Exception e) {\n                System.Diagnostics.Debug.WriteLine(e.StackTrace);\n                System.Windows.Forms.MessageBox.Show(e.StackTrace, $\"Unable to export {teriName}\");\n            }\n        }\n        #endregion\n\n        #region Refresh\n        public void Refresh() {\n            _FilteredTerritories = _FilteredTerritories.ToArray();\n            OnPropertyChanged(() => FilteredTerritories);\n        }\n        #endregion\n    }\n\n    public class TerritoryView\n    {\n        public TerritoryView(TerritoryType territory)\n        {\n            TerritoryType = territory;\n\n            var places = new List<string>();\n            places.Add(territory.RegionPlaceName.Name.ToString());\n            places.Add(territory.ZonePlaceName.Name.ToString());\n            places.Add(territory.PlaceName.Name.ToString());\n            PlaceNames = string.Join(\" > \", places.Where(p => !string.IsNullOrEmpty(p)).Distinct());\n            \n            Name = string.Format(\"({0}) {1}\", territory.Name.ToString(), PlaceNames);\n        }\n\n        public TerritoryType TerritoryType { get; private set; }\n        public string Name { get; private set; }\n        public string PlaceNames { get; private set; }\n    }\n}\n"
  },
  {
    "path": "Godbert/Views/DataView.xaml",
    "content": "﻿<UserControl x:Class=\"Godbert.Views.DataView\"\n             xmlns=\"http://schemas.microsoft.com/winfx/2006/xaml/presentation\"\n             xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\"\n             xmlns:mc=\"http://schemas.openxmlformats.org/markup-compatibility/2006\" \n             xmlns:d=\"http://schemas.microsoft.com/expression/blend/2008\" \n             xmlns:ctrl=\"clr-namespace:Godbert.Controls\"\n             mc:Ignorable=\"d\" \n             d:DesignHeight=\"300\" d:DesignWidth=\"600\">\n    <UserControl.Resources>\n        <ContextMenu x:Key=\"SheetListContextMenu\">\n            <MenuItem Header=\"Export as CSV\" Command=\"{Binding ExportCsvCommand}\">\n                <MenuItem.Style>\n                    <Style TargetType=\"MenuItem\">\n                        <Style.Triggers>\n                            <DataTrigger Binding=\"{Binding Path=SelectedSheetName}\" Value=\"{x:Null}\">\n                                <Setter Property=\"IsEnabled\" Value=\"False\" />\n                            </DataTrigger>\n                        </Style.Triggers>\n                    </Style>\n                </MenuItem.Style>\n            </MenuItem>\n        </ContextMenu>\n    </UserControl.Resources>\n    <Grid>\n        <Grid.ColumnDefinitions>\n            <ColumnDefinition Width=\"250\" />\n            <ColumnDefinition Width=\"Auto\" />\n            <ColumnDefinition Width=\"*\" />\n            <ColumnDefinition Width=\"Auto\" />\n            <ColumnDefinition Width=\"Auto\" />\n        </Grid.ColumnDefinitions>\n\n        <DockPanel LastChildFill=\"True\" Grid.Column=\"0\">\n            <TextBox Text=\"{Binding Path=FilterSheetTerm, Mode=TwoWay, Delay=250, UpdateSourceTrigger=PropertyChanged}\"\n                        DockPanel.Dock=\"Top\"\n                        Margin=\"0,0,0,2\"></TextBox>\n            <ListBox ItemsSource=\"{Binding FilteredSheetNames}\"\n                        SelectedItem=\"{Binding SelectedSheetName, Mode=TwoWay}\"\n                        HorizontalAlignment=\"Stretch\"\n                        ContextMenu=\"{StaticResource SheetListContextMenu}\"/>\n        </DockPanel>\n\n        <GridSplitter Width=\"4\" Grid.Column=\"1\"\n                      IsEnabled=\"{Binding ElementName=SelectionExpander, Path=IsExpanded}\"\n                      HorizontalAlignment=\"Center\" VerticalAlignment=\"Stretch\"\n                      ResizeBehavior=\"PreviousAndNext\" ResizeDirection=\"Columns\" />\n\n        <DockPanel Grid.Column=\"2\" LastChildFill=\"True\">\n            <TextBox Text=\"{Binding Path=FilterDataTerm, Mode=TwoWay, Delay=250, UpdateSourceTrigger=PropertyChanged}\"\n                         DockPanel.Dock=\"Top\"\n                         Margin=\"0,0,0,2\"></TextBox>\n\n            <ctrl:RawDataGrid x:Name=\"_DataGrid\"\n                              Sheet=\"{Binding SelectedSheet}\"\n                              Filter=\"{Binding FilterDataTerm}\"\n                              AutoGenerateColumns=\"False\"\n                              IsReadOnly=\"True\"\n                              CanUserSortColumns=\"True\"\n                              EnableColumnVirtualization=\"True\"\n                              HorizontalGridLinesBrush=\"#dadada\"\n                              VerticalGridLinesBrush=\"#dadada\">\n                <ctrl:RawDataGrid.RowStyle>\n                    <Style TargetType=\"DataGridRow\">\n                        <EventSetter Event=\"SizeChanged\" Handler=\"RowSizeChanged\"/>\n                    </Style>\n                </ctrl:RawDataGrid.RowStyle>\n            </ctrl:RawDataGrid>\n        </DockPanel>\n\n        <GridSplitter Width=\"4\" Grid.Column=\"3\"\n                      HorizontalAlignment=\"Center\" VerticalAlignment=\"Stretch\"\n                      ResizeDirection=\"Columns\" />\n\n        <Grid Grid.Column=\"4\" Visibility=\"{Binding BookmarksVisibility}\">\n            <Grid.RowDefinitions>\n                <RowDefinition Height=\"Auto\" />\n                <RowDefinition Height=\"*\" />\n            </Grid.RowDefinitions>\n            \n            <Label Grid.Row=\"0\" FontWeight=\"Bold\" BorderBrush=\"#dadada\" BorderThickness=\"0 0 0 1\">Bookmarks</Label>\n            <ListBox Grid.Row=\"1\" x:Name=\"_BookmarksList\"\n                     ItemsSource=\"{Binding Bookmarks}\"\n                     SelectionChanged=\"_BookmarksList_SelectionChanged\"\n                     HorizontalAlignment=\"Stretch\"\n                     HorizontalContentAlignment=\"Stretch\">\n                <ListBox.ItemTemplate>\n                    <DataTemplate>\n                        <Border BorderBrush=\"#dadada\" BorderThickness=\"0 0 0 2\" Padding=\"2\" MouseDown=\"BookmarkListItem_MouseDown\">\n                            <TextBlock Text=\"{Binding}\" />\n                        </Border>\n                    </DataTemplate>\n                </ListBox.ItemTemplate>\n            </ListBox>\n        </Grid>\n    </Grid>\n</UserControl>\n"
  },
  {
    "path": "Godbert/Views/DataView.xaml.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\nusing System.Windows;\nusing System.Windows.Controls;\nusing System.Windows.Data;\nusing System.Windows.Documents;\nusing System.Windows.Input;\nusing System.Windows.Media;\nusing System.Windows.Media.Imaging;\nusing System.Windows.Navigation;\nusing System.Windows.Shapes;\nusing Godbert.ViewModels;\n\nnamespace Godbert.Views {\n    /// <summary>\n    /// Interaction logic for DataView.xaml\n    /// </summary>\n    public partial class DataView : UserControl {\n        public DataView() {\n            InitializeComponent();\n\n            DataContextChanged += DataView_DataContextChanged;\n        }\n\n        private void DataView_DataContextChanged(object sender, DependencyPropertyChangedEventArgs e) {\n            var viewModel = (DataViewModel)DataContext;\n            viewModel.Parent.DataGridChanged += MainViewModel_DataGridChanged;\n        }\n\n        private void MainViewModel_DataGridChanged(object sender, EventArgs e) {\n            // Hacky contents refresh.\n            var sheet = _DataGrid.Sheet;\n            _DataGrid.Sheet = null;\n            _DataGrid.Sheet = sheet;\n        }\n\n        public void RowSizeChanged(object sender, SizeChangedEventArgs e)\n        {\n            var row = sender as DataGridRow;\n            if (row == null)\n                return;\n\n            // Ensure row heights do not fluctuate as virtualized columns are\n            // loaded/unloaded.\n            if (row.MinHeight < e.NewSize.Height)\n                row.MinHeight = e.NewSize.Height;\n        }\n\n        private void _BookmarksList_SelectionChanged(object sender, SelectionChangedEventArgs e) {\n            var bookmark = e.AddedItems.OfType<BookmarkViewModel>().FirstOrDefault();\n            if (bookmark == null)\n                return;\n\n            var mainWindow = WpfHelper.FindParent<MainWindow>(this);\n            if (mainWindow == null)\n                return;\n\n            var mainViewModel = mainWindow.DataContext as MainViewModel;\n            if (mainViewModel == null)\n                return;\n\n            mainViewModel.Data.SelectedSheetName = bookmark.SheetName;\n            var row = mainViewModel.Data.SelectedSheet[bookmark.Key];\n            _DataGrid.SelectRow(row, bookmark.ColumnIndex);\n\n            // Remove selection.\n            _BookmarksList.SelectedIndex = -1;\n        }\n\n        private void BookmarkListItem_MouseDown(object sender, MouseButtonEventArgs e) {\n            if (e.MiddleButton == MouseButtonState.Pressed) {\n                var source = e.OriginalSource as FrameworkElement;\n                if (source == null)\n                    return;\n\n                var listItem = WpfHelper.FindParent<ListBoxItem>(source);\n                var bookmark = listItem?.DataContext as BookmarkViewModel;\n                if (bookmark == null)\n                    return;\n\n                var dataViewModel = (DataViewModel)DataContext;\n                dataViewModel.Bookmarks.Remove(bookmark);\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "Godbert/Views/DemihumansView.xaml",
    "content": "﻿<UserControl x:Class=\"Godbert.Views.DemihumansView\"\n             xmlns=\"http://schemas.microsoft.com/winfx/2006/xaml/presentation\"\n             xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\"\n             xmlns:mc=\"http://schemas.openxmlformats.org/markup-compatibility/2006\" \n             xmlns:d=\"http://schemas.microsoft.com/expression/blend/2008\" \n             mc:Ignorable=\"d\" \n             d:DesignHeight=\"300\" d:DesignWidth=\"300\">\n    <UserControl.Resources>\n        <HierarchicalDataTemplate x:Key=\"ModelCharaTree\" ItemsSource=\"{Binding}\">\n            <TextBlock Text=\"{Binding Path=DisplayName}\" />\n        </HierarchicalDataTemplate>\n        <BooleanToVisibilityConverter x:Key=\"BooleanToVisibilityConverter\" />\n    </UserControl.Resources>\n    <Grid>\n        <Grid.RowDefinitions>\n            <RowDefinition Height=\"Auto\" />\n            <RowDefinition Height=\"*\" />\n            <RowDefinition Height=\"Auto\" />\n            <RowDefinition Height=\"Auto\" />\n        </Grid.RowDefinitions>\n        <Grid.ColumnDefinitions>\n            <ColumnDefinition Width=\"*\" />\n            <ColumnDefinition Width=\"*\" />\n            <ColumnDefinition Width=\"*\" />\n            <ColumnDefinition Width=\"*\" />\n            <ColumnDefinition Width=\"*\" />\n        </Grid.ColumnDefinitions>\n\n        <Button Grid.Column=\"0\" Grid.Row=\"0\" Grid.ColumnSpan=\"5\"\n                        Content=\"Brute-force\"\n                        Command=\"{Binding Path=BruteForceCommand}\"\n                        Visibility=\"{Binding Path=IsBruteForceAvailable, Converter={StaticResource BooleanToVisibilityConverter}}\"\n                        Margin=\"2\" />\n\n        <TreeView x:Name=\"DemihumanTreeView\" x:FieldModifier=\"private\"\n                          Grid.Column=\"0\" Grid.Row=\"1\" Grid.ColumnSpan=\"5\"\n                          ItemsSource=\"{Binding Path=Entries}\"\n                          ItemTemplate=\"{StaticResource ModelCharaTree}\"\n                          SelectedItemChanged=\"DemihumanTreeView_SelectedItemChanged\" />\n\n        <CheckBox Grid.Column=\"0\" Grid.Row=\"2\"\n                          IsChecked=\"{Binding Path=ShowPart0, Mode=TwoWay}\"\n                          Content=\"met\"\n                          Margin=\"2\" />\n        <CheckBox Grid.Column=\"1\" Grid.Row=\"2\"\n                          IsChecked=\"{Binding Path=ShowPart1, Mode=TwoWay}\"\n                          Content=\"top\"\n                          Margin=\"2\" />\n        <CheckBox Grid.Column=\"2\" Grid.Row=\"2\"\n                          IsChecked=\"{Binding Path=ShowPart2, Mode=TwoWay}\"\n                          Content=\"glv\"\n                          Margin=\"2\" />\n        <CheckBox Grid.Column=\"3\" Grid.Row=\"2\"\n                          IsChecked=\"{Binding Path=ShowPart3, Mode=TwoWay}\"\n                          Content=\"dwn\"\n                          Margin=\"2\" />\n        <CheckBox Grid.Column=\"4\" Grid.Row=\"2\"\n                          IsChecked=\"{Binding Path=ShowPart4, Mode=TwoWay}\"\n                          Content=\"sho\"\n                          Margin=\"2\" />\n\n\n        <Grid Grid.Row=\"3\" Grid.Column=\"0\" Grid.ColumnSpan=\"5\" IsEnabled=\"{Binding Path=IsValidSelection}\">\n            <Grid.ColumnDefinitions>\n                <ColumnDefinition Width=\"*\" />\n                <ColumnDefinition Width=\"*\" />\n                <ColumnDefinition Width=\"*\" />\n            </Grid.ColumnDefinitions>\n\n            <Button Content=\"Add\" Command=\"{Binding Path=AddCommand}\"\n                            Grid.Row=\"0\" Grid.Column=\"0\"\n                            Margin=\"2\" />\n            <Button Content=\"Replace\" Command=\"{Binding Path=ReplaceCommand}\"\n                            Grid.Row=\"0\" Grid.Column=\"1\"\n                            Margin=\"2\" />\n            <Button Content=\"New window\" Command=\"{Binding Path=NewCommand}\"\n                            Grid.Row=\"0\" Grid.Column=\"2\"\n                            Margin=\"2\" />\n        </Grid>\n    </Grid>\n</UserControl>\n"
  },
  {
    "path": "Godbert/Views/DemihumansView.xaml.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\nusing System.Windows;\nusing System.Windows.Controls;\nusing System.Windows.Data;\nusing System.Windows.Documents;\nusing System.Windows.Input;\nusing System.Windows.Media;\nusing System.Windows.Media.Imaging;\nusing System.Windows.Navigation;\nusing System.Windows.Shapes;\n\nnamespace Godbert.Views {\n    /// <summary>\n    /// Interaction logic for DemihumansView.xaml\n    /// </summary>\n    public partial class DemihumansView : UserControl {\n        public DemihumansView() {\n            InitializeComponent();\n        }\n\n        private void DemihumanTreeView_SelectedItemChanged(object sender, RoutedPropertyChangedEventArgs<object> e) {\n            var vm = (ViewModels.DemihumanViewModel)DataContext;\n            vm.SelectedEntry = DemihumanTreeView.SelectedItem;\n        }\n    }\n}\n"
  },
  {
    "path": "Godbert/Views/EquipmentView.xaml",
    "content": "﻿<UserControl x:Class=\"Godbert.Views.EquipmentView\"\n             xmlns=\"http://schemas.microsoft.com/winfx/2006/xaml/presentation\"\n             xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\"\n             xmlns:mc=\"http://schemas.openxmlformats.org/markup-compatibility/2006\" \n             xmlns:d=\"http://schemas.microsoft.com/expression/blend/2008\" \n             mc:Ignorable=\"d\" \n             d:DesignHeight=\"300\" d:DesignWidth=\"300\">\n    <Grid>\n        <Grid.RowDefinitions>\n            <RowDefinition Height=\"Auto\" />\n            <RowDefinition Height=\"*\" />\n            <RowDefinition Height=\"Auto\" />\n            <RowDefinition Height=\"Auto\" />\n        </Grid.RowDefinitions>\n        <Grid.ColumnDefinitions>\n            <ColumnDefinition Width=\"*\" />\n            <ColumnDefinition Width=\"*\" />\n            <ColumnDefinition Width=\"*\" />\n        </Grid.ColumnDefinitions>\n\n        <TextBox Text=\"{Binding Path=FilterTerm, Mode=TwoWay, Delay=250, UpdateSourceTrigger=PropertyChanged}\"\n                         Grid.Column=\"0\" Grid.Row=\"0\" Grid.ColumnSpan=\"3\"\n                         Margin=\"2\" />\n\n        <ListBox Grid.Column=\"0\" Grid.Row=\"1\" Grid.ColumnSpan=\"3\"\n                         ItemsSource=\"{Binding Path=FilteredEquipment}\"\n                         SelectedItem=\"{Binding Path=SelectedEquipment, Mode=TwoWay}\" />\n\n        <ComboBox Grid.Row=\"2\" Grid.Column=\"0\" Grid.ColumnSpan=\"3\"\n                          IsEnabled=\"{Binding Path=SelectedEquipment.IsDyeable}\"\n                          ItemsSource=\"{Binding Path=Stains}\"\n                          SelectedItem=\"{Binding Path=SelectedStain, Mode=TwoWay}\"\n                          Margin=\"2\" />\n\n        <Button Content=\"Add\" Command=\"{Binding Path=AddCommand}\"\n                        Grid.Row=\"3\" Grid.Column=\"0\"\n                        Margin=\"2\" />\n        <Button Content=\"Replace\" Command=\"{Binding Path=ReplaceCommand}\"\n                        Grid.Row=\"3\" Grid.Column=\"1\"\n                        Margin=\"2\" />\n        <Button Content=\"New window\" Command=\"{Binding Path=NewCommand}\"\n                        Grid.Row=\"3\" Grid.Column=\"2\"\n                        Margin=\"2\" />\n    </Grid>\n</UserControl>\n"
  },
  {
    "path": "Godbert/Views/EquipmentView.xaml.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\nusing System.Windows;\nusing System.Windows.Controls;\nusing System.Windows.Data;\nusing System.Windows.Documents;\nusing System.Windows.Input;\nusing System.Windows.Media;\nusing System.Windows.Media.Imaging;\nusing System.Windows.Navigation;\nusing System.Windows.Shapes;\n\nnamespace Godbert.Views {\n    /// <summary>\n    /// Interaction logic for EquipmentView.xaml\n    /// </summary>\n    public partial class EquipmentView : UserControl {\n        public EquipmentView() {\n            InitializeComponent();\n        }\n    }\n}\n"
  },
  {
    "path": "Godbert/Views/FurnitureView.xaml",
    "content": "﻿<UserControl x:Class=\"Godbert.Views.FurnitureView\"\n             xmlns=\"http://schemas.microsoft.com/winfx/2006/xaml/presentation\"\n             xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\"\n             xmlns:mc=\"http://schemas.openxmlformats.org/markup-compatibility/2006\" \n             xmlns:d=\"http://schemas.microsoft.com/expression/blend/2008\" \n             mc:Ignorable=\"d\" \n             d:DesignHeight=\"300\" d:DesignWidth=\"300\">\n    <Grid>\n        <Grid.RowDefinitions>\n            <RowDefinition Height=\"Auto\" />\n            <RowDefinition Height=\"*\" />\n            <RowDefinition Height=\"Auto\" />\n            <RowDefinition Height=\"Auto\" />\n        </Grid.RowDefinitions>\n        <Grid.ColumnDefinitions>\n            <ColumnDefinition Width=\"*\" />\n            <ColumnDefinition Width=\"*\" />\n            <ColumnDefinition Width=\"*\" />\n        </Grid.ColumnDefinitions>\n\n        <TextBox Text=\"{Binding Path=FilterTerm, Mode=TwoWay, Delay=250, UpdateSourceTrigger=PropertyChanged}\"\n                         Grid.Column=\"0\" Grid.Row=\"0\" Grid.ColumnSpan=\"3\"\n                         Margin=\"2\" />\n\n        <ListBox Grid.Column=\"0\" Grid.Row=\"1\" Grid.ColumnSpan=\"3\"\n                 ItemsSource=\"{Binding Path=FilteredFurniture}\"\n                 SelectedItem=\"{Binding Path=SelectedFurniture, Mode=TwoWay}\" />\n\n        <ComboBox Grid.Row=\"2\" Grid.Column=\"0\" Grid.ColumnSpan=\"3\"\n                          IsEnabled=\"{Binding Path=SelectedFurniture.Item.IsDyeable}\"\n                          ItemsSource=\"{Binding Path=Stains}\"\n                          SelectedItem=\"{Binding Path=SelectedStain, Mode=TwoWay}\"\n                          Margin=\"2\" />\n\n        <Button Content=\"Add\" Command=\"{Binding Path=AddCommand}\"\n                        Grid.Row=\"3\" Grid.Column=\"0\"\n                        Margin=\"2\" />\n        <Button Content=\"Replace\" Command=\"{Binding Path=ReplaceCommand}\"\n                        Grid.Row=\"3\" Grid.Column=\"1\"\n                        Margin=\"2\" />\n        <Button Content=\"New window\" Command=\"{Binding Path=NewCommand}\"\n                        Grid.Row=\"3\" Grid.Column=\"2\"\n                        Margin=\"2\" />\n    </Grid>\n</UserControl>\n"
  },
  {
    "path": "Godbert/Views/FurnitureView.xaml.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\nusing System.Windows;\nusing System.Windows.Controls;\nusing System.Windows.Data;\nusing System.Windows.Documents;\nusing System.Windows.Input;\nusing System.Windows.Media;\nusing System.Windows.Media.Imaging;\nusing System.Windows.Navigation;\nusing System.Windows.Shapes;\n\nnamespace Godbert.Views {\n    /// <summary>\n    /// Interaction logic for FurnitureView.xaml\n    /// </summary>\n    public partial class FurnitureView : UserControl {\n        public FurnitureView() {\n            InitializeComponent();\n        }\n    }\n}\n"
  },
  {
    "path": "Godbert/Views/Help3DView.xaml",
    "content": "﻿<UserControl x:Class=\"Godbert.Views.Help3DView\"\n             xmlns=\"http://schemas.microsoft.com/winfx/2006/xaml/presentation\"\n             xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\"\n             xmlns:mc=\"http://schemas.openxmlformats.org/markup-compatibility/2006\" \n             xmlns:d=\"http://schemas.microsoft.com/expression/blend/2008\" \n             mc:Ignorable=\"d\" \n             d:DesignHeight=\"300\" d:DesignWidth=\"300\">\n    <Grid>\n        <RichTextBox IsReadOnly=\"True\" IsEnabled=\"False\">\n            <FlowDocument>\n                <Paragraph>\n                    <Bold FontSize=\"16\">Tabs</Bold>\n                    <LineBreak />\n                    <Bold>Equipment</Bold>\n                    <LineBreak />\n                    <Run>Allows for viewing of equipment.</Run>\n                    <LineBreak />\n                    <Run>• Enter text in the box to filter displayed items.</Run>\n                    <LineBreak />\n                    <Run>• Dyes can be selected in the box below the list.</Run>\n                    <LineBreak />\n                    \n                    <Bold>Furniture</Bold>\n                    <LineBreak />\n                    <Run>Allows for viewing of furniture.</Run>\n                    <LineBreak />\n                    <Run>• Enter text in the box to filter displayed items.</Run>\n                    <LineBreak />\n                    <Run>• Dyes can be selected in the box below the list.</Run>\n                    <LineBreak />\n\n                    <Bold>Monsters</Bold>\n                    <LineBreak />\n                    <Run>Allows for viewing of monster models.</Run>\n                    <LineBreak />\n                    <Run>• Have to select an item on the deepest level (v*) to display.</Run>\n                    <LineBreak />\n                    <Run>• Click the</Run>\n                    <Italic>Brute-force</Italic>\n                    <Run>button to search for monster models not present in the game's internal list (unlikely to find anything new.)</Run>\n                    <LineBreak />\n\n                    <Bold>Maps</Bold>\n                    <LineBreak />\n                    <Run>Allows for viewing of maps.</Run>\n                    <LineBreak />\n                    <Run>• A lot will be missing.</Run>\n                    <LineBreak />\n\n                    <Bold>Demi-humans</Bold>\n                    <LineBreak />\n                    <Run>Allows for viewing of demi-human models (non-playable races with exchangable equipment, such as beast tribes.)</Run>\n                    <LineBreak />\n                    <Run>• Click the</Run>\n                    <Italic>Brute-force</Italic>\n                    <Run>button to search for all demi-human models. This is recommended since the starting list only includes very few.</Run>\n                    <LineBreak />\n                </Paragraph>\n                <Paragraph>\n                    <Bold FontSize=\"16\">Buttons</Bold>\n                    <LineBreak />\n                    <Bold>• Add:</Bold>\n                    <Run>Adds the selected object to the current viewer.</Run>\n                    <LineBreak />\n                    <Bold>• Replace:</Bold>\n                    <Run>Replaces the content of the current viewer with the selected object.</Run>\n                    <LineBreak />\n                    <Bold>• New window:</Bold>\n                    <Run>Opens the selected object in a new window.</Run>\n                </Paragraph>\n                <Paragraph>\n                    <Bold FontSize=\"16\">Controls</Bold>\n                    <LineBreak />\n                    <Run>• Move around using WASD-keys.</Run>\n                    <LineBreak />\n                    <Run>• Control camera using arrow-keys or moving the mouse while the left mouse button is pressed.</Run>\n                </Paragraph>\n            </FlowDocument>\n        </RichTextBox>\n    </Grid>\n</UserControl>\n"
  },
  {
    "path": "Godbert/Views/Help3DView.xaml.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\nusing System.Windows;\nusing System.Windows.Controls;\nusing System.Windows.Data;\nusing System.Windows.Documents;\nusing System.Windows.Input;\nusing System.Windows.Media;\nusing System.Windows.Media.Imaging;\nusing System.Windows.Navigation;\nusing System.Windows.Shapes;\n\nnamespace Godbert.Views {\n    /// <summary>\n    /// Interaction logic for Help3DView.xaml\n    /// </summary>\n    public partial class Help3DView : UserControl {\n        public Help3DView() {\n            InitializeComponent();\n        }\n    }\n}\n"
  },
  {
    "path": "Godbert/Views/MapsView.xaml",
    "content": "﻿<UserControl x:Class=\"Godbert.Views.MapsView\"\n             xmlns=\"http://schemas.microsoft.com/winfx/2006/xaml/presentation\"\n             xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\"\n             xmlns:mc=\"http://schemas.openxmlformats.org/markup-compatibility/2006\" \n             xmlns:d=\"http://schemas.microsoft.com/expression/blend/2008\" \n             mc:Ignorable=\"d\" \n             d:DesignHeight=\"300\" d:DesignWidth=\"300\">\n    <Grid>\n        <Grid.RowDefinitions>\n            <RowDefinition Height=\"*\" />\n            <RowDefinition Height=\"Auto\" />\n        </Grid.RowDefinitions>\n\n        <ListBox Grid.Column=\"0\" Grid.Row=\"0\"\n                         ItemsSource=\"{Binding Path=Maps}\"\n                         SelectedItem=\"{Binding Path=SelectedMap, Mode=TwoWay}\"\n                         DisplayMemberPath=\"PlaceName\" />\n\n        <Button Content=\"Open\" Command=\"{Binding Path=OpenCommand}\"\n                        Grid.Row=\"1\" Grid.Column=\"0\"\n                        Margin=\"2\" />\n    </Grid>\n</UserControl>\n"
  },
  {
    "path": "Godbert/Views/MapsView.xaml.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\nusing System.Windows;\nusing System.Windows.Controls;\nusing System.Windows.Data;\nusing System.Windows.Documents;\nusing System.Windows.Input;\nusing System.Windows.Media;\nusing System.Windows.Media.Imaging;\nusing System.Windows.Navigation;\nusing System.Windows.Shapes;\n\nnamespace Godbert.Views {\n    /// <summary>\n    /// Interaction logic for MapsView.xaml\n    /// </summary>\n    public partial class MapsView : UserControl {\n        public MapsView() {\n            InitializeComponent();\n        }\n    }\n}\n"
  },
  {
    "path": "Godbert/Views/MonstersView.xaml",
    "content": "﻿<UserControl x:Class=\"Godbert.Views.MonstersView\"\n             xmlns=\"http://schemas.microsoft.com/winfx/2006/xaml/presentation\"\n             xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\"\n             xmlns:mc=\"http://schemas.openxmlformats.org/markup-compatibility/2006\" \n             xmlns:d=\"http://schemas.microsoft.com/expression/blend/2008\" \n             mc:Ignorable=\"d\" \n             d:DesignHeight=\"300\" d:DesignWidth=\"300\">\n    <UserControl.Resources>\n        <HierarchicalDataTemplate x:Key=\"ModelCharaTree\" ItemsSource=\"{Binding}\">\n            <TextBlock Text=\"{Binding Path=DisplayName}\" />\n        </HierarchicalDataTemplate>\n        <BooleanToVisibilityConverter x:Key=\"BooleanToVisibilityConverter\" />\n    </UserControl.Resources>\n    <Grid>\n        <Grid.RowDefinitions>\n            <RowDefinition Height=\"Auto\" />\n            <RowDefinition Height=\"*\" />\n            <RowDefinition Height=\"Auto\" />\n            <RowDefinition Height=\"Auto\" />\n        </Grid.RowDefinitions>\n        <Grid.ColumnDefinitions>\n            <ColumnDefinition Width=\"*\" />\n            <ColumnDefinition Width=\"*\" />\n            <ColumnDefinition Width=\"*\" />\n            <ColumnDefinition Width=\"*\" />\n        </Grid.ColumnDefinitions>\n\n        <Button Grid.Column=\"0\" Grid.Row=\"0\" Grid.ColumnSpan=\"4\"\n                        Content=\"Brute-force\"\n                        Command=\"{Binding Path=BruteForceCommand}\"\n                        Visibility=\"{Binding Path=IsBruteForceAvailable, Converter={StaticResource BooleanToVisibilityConverter}}\"\n                        Margin=\"2\" />\n\n        <TreeView x:Name=\"MonsterTreeView\" x:FieldModifier=\"private\"\n                          Grid.Column=\"0\" Grid.Row=\"1\" Grid.ColumnSpan=\"4\"\n                          ItemsSource=\"{Binding Path=Entries}\"\n                          ItemTemplate=\"{StaticResource ModelCharaTree}\"\n                          SelectedItemChanged=\"MonsterTreeView_SelectedItemChanged\" />\n\n        <ProgressBar x:Name=\"ExportProgress\" x:FieldModifier=\"private\"\n                     Grid.Column=\"0\" Grid.ColumnSpan=\"4\" Grid.Row=\"3\"\n                     IsEnabled=\"True\" IsIndeterminate=\"true\" Height=\"20\"\n                     Visibility=\"{Binding Path=IsExporting, Converter={StaticResource BooleanToVisibilityConverter}}\"\n                     />\n\n        <Button Content=\"Add\" Command=\"{Binding Path=AddCommand}\"\n                        Grid.Row=\"2\" Grid.Column=\"0\"\n                        IsEnabled=\"{Binding Path=IsValidSelection}\"\n                        Margin=\"2\" />\n        <Button Content=\"Replace\" Command=\"{Binding Path=ReplaceCommand}\"\n                        Grid.Row=\"2\" Grid.Column=\"1\"\n                        IsEnabled=\"{Binding Path=IsValidSelection}\"\n                        Margin=\"2\" />\n        <Button Content=\"Export FBX\" Command=\"{Binding Path=ExportCommand}\"\n                        Grid.Row=\"2\" Grid.Column=\"2\"\n                        IsEnabled=\"{Binding Path=IsValidSelection}\"\n                        Margin=\"2\" />\n        <Button Content=\"New window\" Command=\"{Binding Path=NewCommand}\"\n                        Grid.Row=\"2\" Grid.Column=\"3\"\n                        IsEnabled=\"{Binding Path=IsValidSelection}\"\n                        Margin=\"2\" />\n    </Grid>\n\n</UserControl>\n"
  },
  {
    "path": "Godbert/Views/MonstersView.xaml.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\nusing System.Windows;\nusing System.Windows.Controls;\nusing System.Windows.Data;\nusing System.Windows.Documents;\nusing System.Windows.Input;\nusing System.Windows.Media;\nusing System.Windows.Media.Imaging;\nusing System.Windows.Navigation;\nusing System.Windows.Shapes;\n\nnamespace Godbert.Views {\n    /// <summary>\n    /// Interaction logic for MonstersView.xaml\n    /// </summary>\n    public partial class MonstersView : UserControl {\n        public MonstersView() {\n            InitializeComponent();\n        }\n        private void MonsterTreeView_SelectedItemChanged(object sender, RoutedPropertyChangedEventArgs<object> e) {\n            var vm = (ViewModels.MonstersViewModel)DataContext;\n            vm.SelectedEntry = MonsterTreeView.SelectedItem;\n        }\n    }\n}\n"
  },
  {
    "path": "Godbert/Views/TerritoryView.xaml",
    "content": "﻿<UserControl x:Class=\"Godbert.Views.TerritoryView\"\n             xmlns=\"http://schemas.microsoft.com/winfx/2006/xaml/presentation\"\n             xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\"\n             xmlns:mc=\"http://schemas.openxmlformats.org/markup-compatibility/2006\" \n             xmlns:d=\"http://schemas.microsoft.com/expression/blend/2008\" \n             mc:Ignorable=\"d\" \n             d:DesignHeight=\"300\" d:DesignWidth=\"300\">\n    <Grid>\n        <Grid.RowDefinitions>\n            <RowDefinition Height=\"Auto\"/>\n            <RowDefinition Height=\"*\" />\n            <RowDefinition Height=\"Auto\" />\n        </Grid.RowDefinitions>\n        <Grid.ColumnDefinitions>\n            <ColumnDefinition Width=\"1*\"/>\n            <ColumnDefinition Width=\"1*\"/>\n        </Grid.ColumnDefinitions>\n        <TextBox Text=\"{Binding Path=FilterTerm, Mode=TwoWay, Delay=250, UpdateSourceTrigger=PropertyChanged}\"\n                 Margin=\"2\"\n                 Grid.Row=\"0\" Grid.Column=\"0\"\n                 Grid.ColumnSpan=\"2\"\n                 />\n\n        <ListBox ItemsSource=\"{Binding Path=FilteredTerritories}\"\n                 SelectedItem=\"{Binding Path=SelectedTerritory, Mode=TwoWay}\"\n                 DisplayMemberPath=\"Name\" \n                 Grid.Row=\"1\" Grid.Column=\"0\"\n                 Grid.ColumnSpan=\"2\"\n                 />\n\n        <Button Content=\"Open\" Command=\"{Binding Path=OpenCommand}\"\n                Grid.Row=\"2\" Grid.Column=\"0\"\n                Grid.ColumnSpan=\"1\" />\n        <Button Content=\"Export\" Command=\"{Binding Path=ExportCommand}\"\n                Grid.Row=\"2\" Grid.Column=\"1\"\n                Grid.ColumnSpan=\"1\" />\n    </Grid>\n</UserControl>\n"
  },
  {
    "path": "Godbert/Views/TerritoryView.xaml.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\nusing System.Windows;\nusing System.Windows.Controls;\nusing System.Windows.Data;\nusing System.Windows.Documents;\nusing System.Windows.Input;\nusing System.Windows.Media;\nusing System.Windows.Media.Imaging;\nusing System.Windows.Navigation;\nusing System.Windows.Shapes;\n\nnamespace Godbert.Views\n{\n    /// <summary>\n    /// Interaction logic for TerritoryView.xaml\n    /// </summary>\n    public partial class TerritoryView : UserControl {\n        public TerritoryView() {\n            InitializeComponent();\n        }\n    }\n}\n"
  },
  {
    "path": "Godbert/WpfHelper.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\nusing System.Windows;\nusing System.Windows.Media;\n\nnamespace Godbert {\n    public static class WpfHelper {\n        public static T FindParent<T>(DependencyObject child) where T : class {\n            var parent = VisualTreeHelper.GetParent(child);\n            if (parent == null)\n                return null;\n\n            var parentAsType = parent as T;\n            if (parentAsType != null)\n                return parentAsType;\n\n            return FindParent<T>(parent);\n        }\n    }\n}\n"
  },
  {
    "path": "LICENSE",
    "content": "            DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE\n                    Version 2, December 2004\n\n Copyright (C) 2004 Sam Hocevar <sam@hocevar.net>\n\n Everyone is permitted to copy and distribute verbatim or modified\n copies of this license document, and changing it is allowed as long\n as the name is changed.\n\n            DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE\n   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION\n\n  0. You just DO WHAT THE FUCK YOU WANT TO."
  },
  {
    "path": "NewAdditionsByPatch.txt",
    "content": "\n Channeling.json:\n{\n  \"index\": 2,\n  \"name\": \"AddedIn{5-3}\"\n}\n\n\n\n ContentFinderCondition.json:\n {\n   \"index\": 19,\n   \"name\": \"AddedIn{5-3}\"\n },\n\n\n ENpcDressUpDress.json:\n{\n  \"index\": 5,\n  \"name\": \"AddedIn{5-3}[0]\"\n},\n{\n\n{\n  \"index\": 8,\n  \"name\": \"AddedIn{5-3}[1]\"\n},\n{\n\n{\n  \"index\": 36,\n  \"name\": \"AddedIn{5-3}[2]\"\n},\n\n\n EObj.json:\n{\n  \"index\": 18,\n  \"name\": \"AddedIn{5-3}\"\n}\n\n\n GuildleveAssignment.json:\n{\n  \"index\": 1,\n  \"name\": \"AddedIn{5-3}\"\n},\n\n IndividualWeather.json:\n{\n  \"index\": 12,\n  \"name\": \"AddedIn{5-3}[0]\"\n},\n{\n  \"index\": 13,\n  \"name\": \"AddedIn{5-3}[1]\"\n},\n\n Resident.json:\n{\n  \"index\": 3,\n  \"name\": \"AddedIn{5-3}\"\n},\n\n SatisfactionNpc.json:\n{\n  \"index\": 72,\n  \"name\": \"AddedIn{5-3}[0]\"\n},\n{\n  \"index\": 73,\n  \"name\": \"AddedIn{5-3}[1]\"\n}\n\n TerritoryType.json:\n{\n  \"index\": 32,\n  \"name\": \"AddedIn{5-3}\"\n},\n\n{\n  \"index\": 40,\n  \"name\": \"Unknown[5-4]\"\n}\n\n ContentMemberType.json:\n{\n  \"index\": 13,\n  \"name\": \"Unknown[5-4]\"\n}\n\n CreditBackImage.json:\n{\n  \"index\": 2,\n  \"name\": \"Unknown[5-4]\"\n},\n\n CustomTalk.json:\n{\n  \"index\": 77,\n  \"name\": \"Unknown[5-4]\"\n}\n\n GatheringPointBonus.json:\n{\n  \"index\": 6,\n  \"name\": \"Unknown[5-3]\"\n},\n{\n  \"index\": 7,\n  \"name\": \"Unknown[5-4]\"\n}\n\n GroupPoseFrame.json:\n{\n  \"index\": 6,\n  \"name\": \"Unknown[5-4]\"\n},\n\n GroupPoseStamp.json:\n{\n  \"index\": 4,\n  \"name\": \"Unknown[5-4][0]\"\n},\n{\n  \"index\": 5,\n  \"name\": \"Unknown[5-4][1]\"\n},\n\n IKDFishParam.json:\n{\n  \"index\": 2,\n  \"name\": \"Unknown[5-4]\"\n}\n\n InstanceContent.json:\n{\n  \"index\": 61,\n  \"name\": \"Unknown[5-4][0]\"\n},\n{\n  \"index\": 62,\n  \"name\": \"Unknown[5-4][1]\"\n}\n\n InstanceContentCSBonus.json:\n{\n  \"index\": 3,\n  \"name\": \"Unknown[5-4][0]\"\n},\n{\n  \"index\": 4,\n  \"name\": \"Unknown[5-4][1]\"\n}\n\n Item.json:\n{\n  \"index\": 40,\n  \"name\": \"Unknown[5-4]\"\n},\n\n LotteryExchangeShop.json:\n\"count\": 32,\n\"definition\": {\n  \"name\": \"Unknown[5-4][0]\"\n}\n\n\n\"count\": 32,\n\"definition\": {\n  \"name\": \"Unknown[5-4][1]\"\n}\n\n MYCWarResultNotebook.json:\n{\n  \"index\": 1,\n  \"name\": \"Unknown[5-4][0]\"\n},\n{\n  \"index\": 2,\n  \"name\": \"Unknown[5-4][1]\"\n},\n\n NpcYell.json:\n{\n  \"index\": 9,\n  \"name\": \"Unknown[5-4]\"\n},\n\n PublicContent.json:\n{\n  \"index\": 13,\n  \"name\": \"Unknown[5-4][0]\"\n},\n{\n  \"index\": 14,\n  \"name\": \"Unknown[5-4][1]\"\n},\n{\n  \"index\": 15,\n  \"name\": \"Unknown[5-4][2]\"\n}\n\n Quest.json:\n{\n  \"index\": 1515,\n  \"name\": \"Unknown[5-4]\"\n}\n\n Race.json:\n{\n  \"index\": 10,\n  \"name\": \"Unknown[5-4]\"\n},\n\n ScenarioTree.json:\n{\n  \"index\": 3,\n  \"name\": \"Unknown[5-4][0]\"\n},\n{\n  \"index\": 4,\n  \"name\": \"Unknown[5-4][1]\"\n},\n{\n  \"index\": 5,\n  \"name\": \"Unknown[5-4][2]\"\n}\n\n Transformation.json:\n{\n  \"index\": 36,\n  \"name\": \"Unknown[5-4]\"\n}\n\n TripleTriadCardResident.json:\n{\n  \"index\": 11,\n  \"name\": \"Unknown[5-4]\"\n},\n\n TripleTriadRule.json:\n{\n  \"index\": 4,\n  \"name\": \"Unknown[5-4]\"\n}\n\n WebGuidance.json:\n{\n  \"index\": 3,\n  \"name\": \"Unknown[5-4]\"\n},"
  },
  {
    "path": "README.md",
    "content": "# Saint Coinach\n\nA .NET library written in C# for extracting game assets and reading game assets from **Final Fantasy XIV**, now with support for including the Libra Eorzea database.\n\n## Functionality \n### Fully implemented\n\n* Extraction of files from the game's SqPack files based on their friendly name (or by Int32 identifiers, if preferred).\n* Conversion of the game's textures to `System.Drawing.Image` objects.\n* Parsing and reading from the game's data files (`*.exh` and `*.exd`).\n* Decoding of OGG files stored in the game's pack files (some of the `*.scd`).\n* OO-representation of the most important game data.\n* Self-updating of the mapping between game data and their OO-representation, in case things move around inside the game's files betwen patches.\n\n### Partially implemented\n\n* Decoding of the string format used by the game. Will return a good string for most queries, but more advanced things like conditional texts are not supported.\n* Parsing of model data works for *most* models but is still incomplete.\n* Inclusion and parsing of data from the Libra Eorzea application.\n\n### To-do\n\n* Support for audio formats other than OGG.\n\n\n## Usage\n\n### Set-up\n\n**Note:** When building an application using this library make sure to include a copy of [`SaintCoinach/SaintCoinach.History.zip`](/SaintCoinach/SaintCoinach.History.zip) in the application's directory. This should be done automatically if the project is included in the solution and referenced from there.\n\nAll important data is exposed by the class `SaintCoinach.ARealmReversed`, so setting up access to it is fairly straightforward.\n\nThe following is an example using the game's default installation path and English as default language:\n\n```C#\nconst string GameDirectory = @\"C:\\Program Files (x86)\\SquareEnix\\FINAL FANTASY XIV - A Realm Reborn\";\nvar realm = new SaintCoinach.ARealmReversed(GameDirectory, SaintCoinach.Ex.Language.English);\n```\n\nIt's that simple. It is recommended, however, to check if the game has been updated since the last time, which is accomplished like this, in this example including the detection of data changes:\n\n```C#\nif (!realm.IsCurrentVersion) {\n    const bool IncludeDataChanges = true;\n    var updateReport = realm.Update(IncludeDataChanges);\n}\n```\n\n`ARealmReversed.Update()` can also take one additional parameter of type `IProgress<UpdateProgress>` to which progress is reported. The returned `UpdateReport` contains a list of changes that were detected during the update.\n\n### Accessing data\n\nGame files can be access directly through `ARealmReversed.Packs`, game data can be accessed through `ARealmReversed.GameData`.\n\n#### Game Data (`XivCollection`)\n\nSpecific collections can be retrieved using the `GetSheet<T>()` method.\n**Note:** This only works for objects whose game data files have the same name as the class. This applies for most classes directly inside the `SaintCoinach.Xiv` namespace, so there should be no need to worry about it in most cases.\n\nSpecial cases are exposed as properties:\n\n* `BNpcs`: This collection contains objects of type `BNpc` that include data of both `BNpcBase` and `BNpcName`. *Note:* Only available when Libra Eorzea data is available.\n* `ENpcs`: This collection contains objects of type `ENpc` that include data of both `ENpcBase` and `ENpcResident`.\n* `EquipSlots`: There is no actual data for specific equipment slots in the game data, but having access to them makes things more convenient, so they're available here.\n* `Items`: This collection combines both `EventItem` and `Item`.\n* `Shops`: This collection contains all types of shops.\n\nThe following is a simple example that outputs the name and colour of all `Stain` objects to the console:\n\n```C#\nvar stains = realm.GameData.GetSheet<SaintCoinach.Xiv.Stain>();\n\nforeach(var stain in stains) {\n    Console.WriteLine(\"#{0}: {1} is {2}\", stain.Key, stain.Name, stain.Color);\n}\n```\n\n## Notes\n\n### State of documentation\n\nOnly `SaintCoinach` contains documentation, and even that only in some places (anything directly in the `SaintCoinach` namespace as well as some things in `SaintCoinach.Xiv.*`), everything else is virtually void of documentation.\n\nThere should, however, be enough documentation available to know how to use the library for game data, but figuring out the internal workings might prove difficult.\n\n### SaintCoinach.Cmd\n\nThe project `SaintCoinach.Cmd` is a very basic console application that can be used to extract various assets.\nThe following commands are currently supported:\n\n* `lang`: Displays or changes the language used for data files. Valid arguments are `Japanese`, `English`, `German`, `French`. If no argument is supplied the currently used language is shown.\n* Raw file:\n  * `raw`: Exports a file from the game assets without any conversions. The argument should be the friendly name of the file.\n  * `rawexd` Export all data (default), or only specific data files, seperated by spaces. No post-processing is applied to values. \n* `image`: Exports a file from the game assets as a PNG-image. The argument should be the friendly name of the image file.\n* UI Icons:\n  * `ui`: Exports one or multiple UI icons as PNG-images. The argument can either be the number of a single UI icon, or the first and last number for a range of icons seperated by a space. Valid numbers are in the interval [0, 999999].\n  * `uihd`: As above, but HD icons\n* Game Sheets:\n  * `exd`: Exports all or a specified number of game data sheets as CSV-files. Arguments can either be empty to export all files, or a list of sheet names seperated by whitespace.\n  * `allexd`: As `exd`, but including all languages\n  * `allrawexd`: As `exd`, all languages, No post-processing is applied to values.\n  * `exdheader`: Export all data headers.                              \n  * `rawexd`: Exports all or a specified number of game data sheets as CSV-files without post-processing applied. Arguments can either be empty to export all files, or a list of sheet names seperated by whitespace.\n* `bgm`: Exports all sound files referenced in the BGM sheet as OGG-files.\n* `maps`: Export all map images. \n* `furn_mdl_tex`: Export all MDL Furniture/Yard files.\n* `sql`: Exports the EXD data as SQL schema and associated imports.\n\n#### SaintCoinach.Cmd Setup\n\nTo run SaintCoinach.Cmd, you will need to configure where the game install can be found. Either by sending it as an argument when you launch it, e.g:\n\n```powerscript\n.\\SaintCoinach.Cmd.exe \"C:\\Steam\\steamapps\\common\\FINAL FANTASY XIV Online\"\n```\n\nor update the application settings `SaintCoinach.Cmd.dll.config` to set the data path:\n\n```xml\n<SaintCoinach.Cmd.Properties.Settings>\n  <setting name=\"DataPath\" serializeAs=\"String\">\n    <value>C:\\Steam\\steamapps\\common\\FINAL FANTASY XIV Online</value>\n  </setting>\n</SaintCoinach.Cmd.Properties.Settings>\n```\n\n\n# Godbert\n\nGodbert is a simple application to display game data and 3D models from **Final Fantasy XIV** using the above-mentioned library.\n\n## Functionality \n### Fully implemented\n\n* Display of game data/text.\n* Rendering of equipment, including ability to dye (if the item supports it.)\n* Rendering of monsters.\n* Rendering of demi-humans (non-playable races with exchangable equipment.)\n\n### Partially implemented\n\n* Rendering of the game's areas. Only static and completely passive objects are displayed at the moment, anything that's more complex is not included.\n\n### To-Do\n\n* Add more things to show.\n* Store active language in config.\n"
  },
  {
    "path": "SaintCoinach/ARealmReversed.cs",
    "content": "﻿using System;\nusing System.Diagnostics;\nusing System.IO;\nusing System.Linq;\nusing System.Runtime.Serialization.Formatters.Binary;\nusing System.Text;\n\nusing Ionic.Zip;\nusing Newtonsoft.Json;\nusing SaintCoinach.Ex;\nusing SaintCoinach.Ex.Relational.Definition;\nusing SaintCoinach.Ex.Relational.Update;\nusing SaintCoinach.IO;\nusing SaintCoinach.Xiv;\n\nusing Directory = System.IO.Directory;\nusing File = System.IO.File;\n\nnamespace SaintCoinach {\n    /// <summary>\n    ///     Central class for accessing game assets.\n    /// </summary>\n    public class ARealmReversed {\n        #region Static\n\n        /// <summary>\n        ///     Default file name of the archive containing current and past data mappings.\n        /// </summary>\n        private const string DefaultStateFile = \"SaintCoinach.History.zip\";\n\n        /// <summary>\n        ///     File name containing the current version string.\n        /// </summary>\n        private const string VersionFile = \"ffxivgame.ver\";\n\n        /// <summary>\n        ///     Format string to create the file name for update reports in text form. {0} is the previous and {1} the new version.\n        /// </summary>\n        private const string UpdateReportTextFile = \"logs/report-{0}-{1}.log\";\n\n        /// <summary>\n        ///     Format string to create the file name for update reports in JSON form. {0} is the previous and {1} the new version.\n        /// </summary>\n        private const string UpdateReportJsonFile = \"logs/report-{0}-{1}.json\";\n\n        /// <summary>\n        ///     Format string to create the file name for update reports in binary form. {0} is the previous and {1} the new\n        ///     version.\n        /// </summary>\n        private const string UpdateReportBinFile = \"logs/report-{0}-{1}.bin\";\n\n        /// <summary>\n        ///     <see cref=\"Encoding\" /> to use inside the <see cref=\"ZipFile\" />.\n        /// </summary>\n        private static readonly Encoding ZipEncoding = Encoding.UTF8;\n\n        #endregion\n\n        #region Fields\n\n        /// <summary>\n        ///     Game data collection for the data files.\n        /// </summary>\n        private readonly XivCollection _GameData;\n\n        /// <summary>\n        ///     Root directory of the game installation.\n        /// </summary>\n        private readonly DirectoryInfo _GameDirectory;\n\n        /// <summary>\n        ///     Version of the game data.\n        /// </summary>\n        private readonly string _GameVersion;\n\n        /// <summary>\n        ///     Pack collection for the data files.\n        /// </summary>\n        private readonly PackCollection _Packs;\n\n        /// <summary>\n        ///     Archive file containing current and past data mappings. \n        /// </summary>\n        private readonly FileInfo _StateFile;\n\n        #endregion\n\n        #region Properties\n\n        /// <summary>\n        ///     Gets the root directory of the game installation.\n        /// </summary>\n        /// <value>The root directory of the game installation.</value>\n        public DirectoryInfo GameDirectory { get { return _GameDirectory; } }\n\n        /// <summary>\n        ///     Gets the pack collection for the data files.\n        /// </summary>\n        /// <value>The pack collection for the data files.</value>\n        public PackCollection Packs { get { return _Packs; } }\n\n        /// <summary>\n        ///     Gets the game data collection for the data files.\n        /// </summary>\n        /// <value>The game data collection for the data files.</value>\n        public XivCollection GameData { get { return _GameData; } }\n\n        /// <summary>\n        ///     Gets the version of the game data.\n        /// </summary>\n        /// <value>The version of the game data.</value>\n        public string GameVersion { get { return _GameVersion; } }\n\n        /// <summary>\n        ///     Gets the version of the loaded definition.\n        /// </summary>\n        /// <value>The version of the loaded definition.</value>\n        public string DefinitionVersion { get { return GameData.Definition.Version; } }\n\n        /// <summary>\n        ///     Gets a value indicating whether the loaded definition is the same as the game data version.\n        /// </summary>\n        /// <value>Whether the loaded definition is the same as the game data version.</value>\n        public bool IsCurrentVersion { get { return GameVersion == DefinitionVersion; } }\n\n        /// <summary>\n        ///     Gets the archive file containing current and past data mappings.\n        /// </summary>\n        /// <value>The archive file containing current and past data mappings.</value>\n        public FileInfo StateFile { get { return _StateFile; } }\n\n        #endregion\n\n        #region Setup\n\n        /// <summary>\n        ///     Perform first-time setup on the archive.\n        /// </summary>\n        /// <param name=\"zip\"><see cref=\"ZipFile\" /> used for storage.</param>\n        /// <returns>Returns the initial <see cref=\"RelationDefinition\" /> object.</returns>\n        private void Setup(ZipFile zip) {\n            var def = _GameData.Definition;\n            if (def.Version != GameVersion)\n                System.Diagnostics.Trace.WriteLine(string.Format(\"Definition and game version mismatch ({0} != {1})\", def.Version, GameVersion));\n\n            def.Version = GameVersion;\n            StoreDefinitionInZip(zip, def);\n            StorePacks(zip);\n            UpdateVersion(zip);\n\n            zip.Save();\n        }\n\n        #endregion\n\n        #region Constructors\n\n        /// <summary>\n        ///     Initializes a new instance of the <see cref=\"ARealmReversed\" /> class.\n        /// </summary>\n        /// <param name=\"gamePath\">Directory path to the game installation.</param>\n        /// <param name=\"language\">Initial language to use.</param>\n        public ARealmReversed(string gamePath, Language language) : this(new DirectoryInfo(gamePath), new FileInfo(DefaultStateFile), language, null) { }\n\n        /// <summary>\n        ///     Initializes a new instance of the <see cref=\"ARealmReversed\" /> class.\n        /// </summary>\n        /// <param name=\"gamePath\">Directory path to the game installation.</param>\n        /// <param name=\"storePath\">Path to the file used for storing definitions and history.</param>\n        /// <param name=\"language\">Initial language to use.</param>\n        public ARealmReversed(string gamePath, string storePath, Language language) : this(new DirectoryInfo(gamePath), new FileInfo(storePath), language, null) { }\n\n        /// <summary>\n        ///     Initializes a new instance of the <see cref=\"ARealmReversed\" /> class.\n        /// </summary>\n        /// <param name=\"gamePath\">Directory path to the game installation.</param>\n        /// <param name=\"storePath\">Path to the file used for storing definitions and history.</param>\n        /// <param name=\"language\">Initial language to use.</param>\n        /// <param name=\"libraPath\">Path to the Libra Eorzea database file.</param>\n        public ARealmReversed(string gamePath, string storePath, Language language, string libraPath) : this(new DirectoryInfo(gamePath), new FileInfo(storePath), language, new FileInfo(libraPath)) { }\n\n        /// <summary>\n        ///     Initializes a new instance of the <see cref=\"ARealmReversed\" /> class.\n        /// </summary>\n        /// <param name=\"gameDirectory\">Directory of the game installation.</param>\n        /// <param name=\"storeFile\">File used for storing definitions and history.</param>\n        /// <param name=\"language\">Initial language to use.</param>\n        /// <param name=\"libraFile\">Location of the Libra Eorzea database file, or <c>null</c> if it should not be used.</param>\n        public ARealmReversed(DirectoryInfo gameDirectory, FileInfo storeFile, Language language, FileInfo libraFile) {\n\n            // Fix for being referenced in a .Net Core 2.1+ application (https://stackoverflow.com/questions/50449314/ibm437-is-not-a-supported-encoding-name => https://stackoverflow.com/questions/44659499/epplus-error-reading-file)\n            // PM> dotnet add package System.Text.Encoding.CodePages\n            Encoding.RegisterProvider(CodePagesEncodingProvider.Instance);\n\n            _GameDirectory = gameDirectory;\n            _Packs = new PackCollection(Path.Combine(gameDirectory.FullName, \"game\", \"sqpack\"));\n            _GameData = new XivCollection(Packs, libraFile) {\n                ActiveLanguage = language\n            };\n\n            _GameVersion = File.ReadAllText(Path.Combine(gameDirectory.FullName, \"game\", \"ffxivgame.ver\"));\n            _StateFile = storeFile;\n            _GameData.Definition = ReadDefinition();\n\n            using (var zipFile = new ZipFile(StateFile.FullName, ZipEncoding)) {\n                if (!zipFile.ContainsEntry(VersionFile))\n                    Setup(zipFile);\n            }\n\n            _GameData.Definition.Compile();\n        }\n\n        #endregion\n\n        #region Shared\n\n        private RelationDefinition ReadDefinition() {\n            var versionPath = Path.Combine(\"Definitions\", \"game.ver\");\n            if (!File.Exists(versionPath))\n                throw new InvalidOperationException(\"Definitions\\\\game.ver must exist.\");\n\n            var version = File.ReadAllText(versionPath).Trim();\n            var def = new RelationDefinition() { Version = version };\n            foreach (var sheetFileName in Directory.EnumerateFiles(\"Definitions\", \"*.json\")) {\n                var json = File.ReadAllText(Path.Combine(sheetFileName), Encoding.UTF8);\n                var obj = JsonConvert.DeserializeObject<Newtonsoft.Json.Linq.JObject>(json);\n                var sheetDef = SheetDefinition.FromJson(obj);\n                def.SheetDefinitions.Add(sheetDef);\n\n                if (!_GameData.SheetExists(sheetDef.Name)) {\n                    var msg = $\"Defined sheet {sheetDef.Name} is missing.\";\n                    Debug.WriteLine(msg);\n                    Console.WriteLine(msg);\n                }\n            }\n\n            return def;\n        }\n\n        /// <summary>\n        ///     Deserialize a <see cref=\"RelationDefinition\" /> file inside a <see cref=\"ZipFile\" />.\n        /// </summary>\n        /// <param name=\"zip\"><see cref=\"ZipFile\" /> to read from.</param>\n        /// <param name=\"version\">Version of the definition to read.</param>\n        /// <returns>Returns the read <see cref=\"RelationDefinition\" />.</returns>\n        private static RelationDefinition ReadDefinition(ZipFile zip, string version) {\n            var def = new RelationDefinition() { Version = version };\n            var entries = zip.SelectEntries(\"*.json\", Path.Combine(version, \"Definitions\"));\n            foreach (var entry in entries) {\n                using (var stream = entry.OpenReader())\n                using (var reader = new StreamReader(stream)) {\n                    var json = reader.ReadToEnd();\n                    var obj = JsonConvert.DeserializeObject<Newtonsoft.Json.Linq.JObject>(json);\n                    def.SheetDefinitions.Add(SheetDefinition.FromJson(obj));\n                }\n            }\n            return def;\n        }\n\n        /// <summary>\n        ///     Store the current pack files in storage.\n        /// </summary>\n        /// <param name=\"zip\"><see cref=\"ZipFile\" /> to store the current packs in.</param>\n        private void StorePacks(ZipFile zip) {\n            const string ExdPackPattern = \"0a*.*\";\n\n            foreach (var file in Packs.DataDirectory.EnumerateFiles(ExdPackPattern, SearchOption.AllDirectories)) {\n                string targetDir = GameVersion + \"/\" + file.Directory.Name;\n                zip.UpdateFile(file.FullName, targetDir);\n            }\n        }\n\n        /// <summary>\n        ///     Updating the current version string in storage.\n        /// </summary>\n        /// <param name=\"zip\"><see cref=\"ZipFile\" /> to store the version string in.</param>\n        private void UpdateVersion(ZipFile zip) {\n            zip.UpdateEntry(VersionFile, GameVersion);\n        }\n\n        /// <summary>\n        ///     Serialize a <see cref=\"RelationDefinition\" /> into a <see cref=\"ZipFile\" />.\n        /// </summary>\n        /// <param name=\"zip\"><see cref=\"ZipFile\" /> to write to.</param>\n        /// <param name=\"definition\"><see cref=\"RelationDefinition\" /> to store.</param>\n        /// <param name=\"version\">Version these definitions are for.</param>\n        private static void StoreDefinitionInZip(ZipFile zip, RelationDefinition definition) {\n            // Since this method is only ever called to update the zip with the\n            // lateset definitions, store these for both the given version *and*\n            // root.\n\n            // todo: prior to storage, delete everything under \"Definitions\" to prevent\n            // dead sheets from resurrecting.\n\n            var versionBasePath = Path.Combine(definition.Version, \"Definitions\");\n            foreach (var sheetDef in definition.SheetDefinitions) {\n                var json = SheetToJson(sheetDef);\n                var sheetFileName = sheetDef.Name + \".json\";\n                zip.UpdateEntry(Path.Combine(versionBasePath, sheetFileName), json);\n                zip.UpdateEntry(Path.Combine(\"Definitions\", sheetFileName), json);\n            }\n\n            // Store version in root definition path for quick copying.\n            var versionPath = Path.Combine(\"Definitions\", \"game.ver\");\n            zip.UpdateEntry(versionPath, definition.Version);\n        }\n\n        private static void StoreDefinitionOnFilesystem(RelationDefinition definition, string basePath) {\n            foreach (var sheetDef in definition.SheetDefinitions) {\n                var sheetDefPath = Path.Combine(basePath, \"Definitions\", sheetDef.Name + \".json\");\n                File.WriteAllText(sheetDefPath, SheetToJson(sheetDef));\n            }\n\n            var versionPath = Path.Combine(basePath, \"Definitions\", \"game.ver\");\n            File.WriteAllText(versionPath, definition.Version);\n        }\n\n        private static string SheetToJson(SheetDefinition sheetDef) {\n            var obj = sheetDef.ToJson();\n            return JsonConvert.SerializeObject(obj, Formatting.Indented);\n        }\n\n        /// <summary>\n        ///     Store a <see cref=\"UpdateReport\" /> in a <see cref=\"ZipFile\" />.\n        /// </summary>\n        /// <param name=\"zip\"><see cref=\"ZipFile\" /> to write to.</param>\n        /// <param name=\"report\"><see cref=\"UpdateReport\" /> to store.</param>\n        private static void StoreReport(ZipFile zip, UpdateReport report) {\n            var textTarget = string.Format(UpdateReportTextFile, report.PreviousVersion, report.UpdateVersion);\n            zip.UpdateEntry(textTarget, string.Join(Environment.NewLine, report.Changes.Select(_ => _.ToString())),\n                ZipEncoding);\n\n            var jsonTarget = string.Format(UpdateReportJsonFile, report.PreviousVersion, report.UpdateVersion);\n            var obj = report.ToJson();\n            var json = JsonConvert.SerializeObject(obj, Formatting.Indented);\n            zip.UpdateEntry(jsonTarget, json);\n        }\n\n        #endregion\n\n        #region Update\n\n        /// <summary>\n        ///     Update to the current version.\n        /// </summary>\n        /// <param name=\"detectDataChanges\">Boolean indicating whether the update should also look for changes in data.</param>\n        /// <param name=\"progress\">Optional object to which update progress is reported.</param>\n        /// <returns>Returns the <see cref=\"UpdateReport\" /> containing all changes.</returns>\n        /// <exception cref=\"InvalidOperationException\">Definition is up-to-date.</exception>\n        public UpdateReport Update(bool detectDataChanges, IProgress<UpdateProgress> progress = null) {\n            if (DefinitionVersion == GameVersion)\n                throw new InvalidOperationException();\n\n            var previousVersion = DefinitionVersion;\n\n            var exdPackId = new PackIdentifier(\"exd\", PackIdentifier.DefaultExpansion, 0);\n            var exdPack = Packs.GetPack(exdPackId);\n            var exdOldKeepInMemory = exdPack.KeepInMemory;\n            exdPack.KeepInMemory = true;\n\n            string tempPath = null;\n            UpdateReport report;\n            try {\n                using (var zip = new ZipFile(StateFile.FullName, ZipEncoding)) {\n                    tempPath = ExtractPacks(zip, previousVersion);\n                    var previousPack = new PackCollection(Path.Combine(tempPath, previousVersion));\n                    previousPack.GetPack(exdPackId).KeepInMemory = true;\n\n                    RelationDefinition previousDefinition;\n                    if (previousVersion == _GameData.Definition.Version) {\n                        // Override previous definition when current definition version matches.\n                        // Definitions may have changed since this was recorded and we want to compare that.\n                        previousDefinition = _GameData.Definition;\n\n                    } else {\n                        // Otherwise, read the previous definition from the zip.\n                        previousDefinition = ReadDefinition(zip, previousVersion);\n                    }\n\n                    var updater = new RelationUpdater(previousPack, previousDefinition, Packs, GameVersion, progress);\n\n                    var changes = updater.Update(detectDataChanges);\n                    report = new UpdateReport(previousVersion, GameVersion, changes);\n\n                    var definition = updater.Updated;\n\n                    StorePacks(zip);\n                    StoreDefinitionInZip(zip, definition);\n                    StoreDefinitionOnFilesystem(definition, \"\");\n\n                    if (Debugger.IsAttached) {\n                        // Little QOL path - when updating with the debugger attached,\n                        // also write to the project definitions path so no need to copy\n                        // them manually afterward.\n                        var projectDefinitionsPath = \"../../../SaintCoinach\";\n                        if (Directory.Exists(projectDefinitionsPath))\n                            StoreDefinitionOnFilesystem(definition, projectDefinitionsPath);\n                    }\n\n                    StoreReport(zip, report);\n                    UpdateVersion(zip);\n                    zip.Save();\n\n                    GameData.Definition = definition;\n                    GameData.Definition.Compile();\n                }\n            } finally {\n                if (exdPack != null)\n                    exdPack.KeepInMemory = exdOldKeepInMemory;\n                if (tempPath != null) {\n                    try {\n                        Directory.Delete(tempPath, true);\n                    } catch {\n                        Console.Error.WriteLine(\"Failed to delete temporary directory {0}.\", tempPath);\n                    }\n                }\n            }\n            return report;\n        }\n\n        /// <summary>\n        ///     Extract the packs of a specific version from storage into a temporary directory.\n        /// </summary>\n        /// <param name=\"zip\"><see cref=\"ZipFile\" /> to read from.</param>\n        /// <param name=\"previousVersion\">Version of the packs to extract.</param>\n        /// <returns>Returns the path to the directory containing the extracted packs.</returns>\n        private static string ExtractPacks(ZipFile zip, string previousVersion) {\n            var tempPath = Path.GetTempFileName();\n            File.Delete(tempPath);\n            Directory.CreateDirectory(tempPath);\n\n            foreach (var entry in zip.Entries.Where(e => e.FileName.StartsWith(previousVersion)))\n                    entry.Extract(tempPath);\n\n            return tempPath;\n        }\n\n        #endregion\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/ByteArrayExtensions.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Runtime;\nusing System.Runtime.InteropServices;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace SaintCoinach {\n    public static class ByteArrayExtensions {\n        public static T ToStructure<T>(this byte[] bytes, int offset) where T : struct {\n            return ToStructure<T>(bytes, ref offset);\n        }\n        public static T ToStructure<T>(this byte[] bytes, ref int offset) where T : struct {\n            var t = typeof(T);\n            var size = Marshal.SizeOf(t);\n            IntPtr ptr = Marshal.AllocHGlobal(size);\n            try {\n                Marshal.Copy(bytes, offset, ptr, size);\n                offset += size;\n                return (T)Marshal.PtrToStructure(ptr, t);\n            } finally {\n                Marshal.FreeHGlobal(ptr);\n            }\n        }\n\n        public static T[] ToStructures<T>(this byte[] bytes, int count, int offset) where T : struct {\n            return ToStructures<T>(bytes, count, ref offset);\n        }\n        public static T[] ToStructures<T>(this byte[] bytes, int count, ref int offset) where T : struct {\n            T[] values = new T[count];\n            for (var i = 0; i < count; ++i)\n                values[i] = ToStructure<T>(bytes, ref offset);\n            return values;\n        }\n\n        public static string ReadString(this byte[] buffer, int offset) {\n            return ReadString(buffer, ref offset);\n        }\n        public static string ReadString(this byte[] buffer, ref int offset) {\n            var strEnd = offset - 1;\n            while (buffer[++strEnd] != 0) { }\n            var size = strEnd - offset;\n\n            var value = Encoding.ASCII.GetString(buffer, offset, size);\n\n            offset = strEnd + 1;\n            return value;\n        }\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Definitions/AOZArrangement.json",
    "content": "{\n  \"sheet\": \"AOZArrangement\",\n  \"definitions\": [\n    {\n      \"name\": \"AOZContentBriefingBNpc\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"AOZContentBriefingBNpc\"\n      }\n    },\n    {\n      \"index\": 1,\n      \"name\": \"Position\"\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/AOZBoss.json",
    "content": "{\n  \"sheet\": \"AOZBoss\",\n  \"defaultColumn\": \"Boss\",\n  \"definitions\": [\n    {\n      \"name\": \"Boss\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"AOZContentBriefingBNpc\"\n      }\n    },\n    {\n      \"index\": 1,\n      \"name\": \"Position\"\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/AOZContent.json",
    "content": "{\n  \"sheet\": \"AOZContent\",\n  \"definitions\": [\n    {\n      \"name\": \"StandardFinishTime\"\n    },\n    {\n      \"index\": 1,\n      \"name\": \"IdealFinishTime\"\n    },\n    {\n      \"index\": 2,\n      \"name\": \"Act1FightType\"\n    },\n    {\n      \"index\": 3,\n      \"name\": \"Act1\",\n      \"converter\": {\n        \"type\": \"complexlink\",\n        \"links\": [\n          {\n            \"when\": {\n              \"key\": \"Act1FightType\",\n              \"value\": 1\n            },\n            \"sheet\": \"AOZArrangement\"\n          },\n          {\n            \"when\": {\n              \"key\": \"Act1FightType\",\n              \"value\": 2\n            },\n            \"sheet\": \"AOZBoss\"\n          },\n          {\n            \"when\": {\n              \"key\": \"Act1FightType\",\n              \"value\": 3\n            },\n            \"sheet\": \"AOZBoss\"\n          }\n        ]\n      }\n    },\n    {\n      \"index\": 4,\n      \"name\": \"ArenaType[1]\"\n    },\n    {\n      \"index\": 6,\n      \"name\": \"Act2FightType\"\n    },\n    {\n      \"index\": 7,\n      \"name\": \"Act2\",\n      \"converter\": {\n        \"type\": \"complexlink\",\n        \"links\": [\n          {\n            \"when\": {\n              \"key\": \"Act2FightType\",\n              \"value\": 1\n            },\n            \"sheet\": \"AOZArrangement\"\n          },\n          {\n            \"when\": {\n              \"key\": \"Act2FightType\",\n              \"value\": 2\n            },\n            \"sheet\": \"AOZBoss\"\n          },\n          {\n            \"when\": {\n              \"key\": \"Act1FightType\",\n              \"value\": 3\n            },\n            \"sheet\": \"AOZBoss\"\n          }\n        ]\n      }\n    },\n    {\n      \"index\": 8,\n      \"name\": \"ArenaType[2]\"\n    },\n    {\n      \"index\": 10,\n      \"name\": \"Act3FightType\"\n    },\n    {\n      \"index\": 11,\n      \"name\": \"Act3\",\n      \"converter\": {\n        \"type\": \"complexlink\",\n        \"links\": [\n          {\n            \"when\": {\n              \"key\": \"Act3FightType\",\n              \"value\": 1\n            },\n            \"sheet\": \"AOZArrangement\"\n          },\n          {\n            \"when\": {\n              \"key\": \"Act3FightType\",\n              \"value\": 2\n            },\n            \"sheet\": \"AOZBoss\"\n          },\n          {\n            \"when\": {\n              \"key\": \"Act1FightType\",\n              \"value\": 3\n            },\n            \"sheet\": \"AOZBoss\"\n          }\n        ]\n      }\n    },\n    {\n      \"index\": 12,\n      \"name\": \"ArenaType[3]\"\n    },\n    {\n      \"index\": 14,\n      \"name\": \"ContentEntry\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"ContentEntry\"\n      }\n    },\n    {\n      \"index\": 15,\n      \"name\": \"Order\"\n    },\n    {\n      \"index\": 16,\n      \"name\": \"GilReward\"\n    },\n    {\n      \"index\": 17,\n      \"name\": \"AlliedSealsReward\"\n    },\n    {\n      \"index\": 18,\n      \"name\": \"TomestonesReward\"\n    }\n  ]\n}\n"
  },
  {
    "path": "SaintCoinach/Definitions/AOZContentBriefingBNpc.json",
    "content": "{\n  \"sheet\": \"AOZContentBriefingBNpc\",\n  \"defaultColumn\": \"BNpcName\",\n  \"definitions\": [\n    {\n      \"name\": \"BNpcName\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"BNpcName\"\n      }\n    },\n    {\n      \"index\": 1,\n      \"name\": \"TargetSmall\",\n      \"converter\": {\n        \"type\": \"icon\"\n      }\n    },\n    {\n      \"index\": 2,\n      \"name\": \"TargetLarge\",\n      \"converter\": {\n        \"type\": \"icon\"\n      }\n    },\n    {\n      \"index\": 3,\n      \"name\": \"HideStats\"\n    },\n    {\n      \"index\": 4,\n      \"name\": \"Endurance\"\n    },\n    {\n      \"index\": 5,\n      \"name\": \"Fire\"\n    },\n    {\n      \"index\": 6,\n      \"name\": \"Ice\"\n    },\n    {\n      \"index\": 7,\n      \"name\": \"Wind\"\n    },\n    {\n      \"index\": 8,\n      \"name\": \"Earth\"\n    },\n    {\n      \"index\": 9,\n      \"name\": \"Thunder\"\n    },\n    {\n      \"index\": 10,\n      \"name\": \"Water\"\n    },\n    {\n      \"index\": 11,\n      \"name\": \"Slashing\"\n    },\n    {\n      \"index\": 12,\n      \"name\": \"Piercing\"\n    },\n    {\n      \"index\": 13,\n      \"name\": \"Blunt\"\n    },\n    {\n      \"index\": 14,\n      \"name\": \"Magic\"\n    },\n    {\n      \"index\": 15,\n      \"name\": \"SlowVuln\"\n    },\n    {\n      \"index\": 16,\n      \"name\": \"PetrificationVuln\"\n    },\n    {\n      \"index\": 17,\n      \"name\": \"ParalysisVuln\"\n    },\n    {\n      \"index\": 18,\n      \"name\": \"InterruptionVuln\"\n    },\n    {\n      \"index\": 19,\n      \"name\": \"BlindVuln\"\n    },\n    {\n      \"index\": 20,\n      \"name\": \"StunVuln\"\n    },\n    {\n      \"index\": 21,\n      \"name\": \"SleepVuln\"\n    },\n    {\n      \"index\": 22,\n      \"name\": \"BindVuln\"\n    },\n    {\n      \"index\": 23,\n      \"name\": \"HeavyVuln\"\n    },\n    {\n      \"index\": 24,\n      \"name\": \"FlatOrDeathVuln\"\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/AOZContentBriefingObject.json",
    "content": "{\n  \"sheet\": \"AOZContentBriefingObject\",\n  \"definitions\": [\n    {\n      \"name\": \"Icon\",\n      \"converter\": {\n        \"type\": \"icon\"\n      }\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/AOZReport.json",
    "content": "{\n  \"sheet\": \"AOZReport\",\n  \"definitions\": [\n    {\n      \"index\": 1,\n      \"name\": \"Reward\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"AOZReportReward\"\n      }\n    },\n    {\n      \"index\": 2,\n      \"name\": \"Order\"\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/AOZScore.json",
    "content": "{\n  \"sheet\": \"AOZScore\",\n  \"defaultColumn\": \"Name\",\n  \"definitions\": [\n    {\n      \"name\": \"IsHidden\"\n    },\n    {\n      \"index\": 1,\n      \"name\": \"Score\"\n    },\n    {\n      \"index\": 2,\n      \"name\": \"Name\"\n    },\n    {\n      \"index\": 3,\n      \"name\": \"Description\"\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/Achievement.json",
    "content": "{\n  \"sheet\": \"Achievement\",\n  \"defaultColumn\": \"Name\",\n  \"definitions\": [\n    {\n      \"name\": \"AchievementCategory\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"AchievementCategory\"\n      }\n    },\n    {\n      \"index\": 1,\n      \"name\": \"Name\"\n    },\n    {\n      \"index\": 2,\n      \"name\": \"Description\"\n    },\n    {\n      \"index\": 3,\n      \"name\": \"AchievementTarget\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"AchievementTarget\"\n      }\n    },\n    {\n      \"index\": 5,\n      \"name\": \"Points\"\n    },\n    {\n      \"index\": 6,\n      \"name\": \"Title\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"Title\"\n      }\n    },\n    {\n      \"index\": 7,\n      \"name\": \"Item\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"Item\"\n      }\n    },\n    {\n      \"index\": 11,\n      \"name\": \"Icon\",\n      \"converter\": {\n        \"type\": \"icon\"\n      }\n    },\n    {\n      \"index\": 13,\n      \"name\": \"Type\"\n    },\n    {\n      \"index\": 14,\n      \"name\": \"Key\",\n      \"converter\": {\n        \"type\": \"complexlink\",\n        \"links\": [\n          {\n            \"when\": {\n              \"key\": \"Type\",\n              \"value\": 2\n            },\n            \"sheet\": \"Achievement\"\n          },\n          {\n            \"when\": {\n              \"key\": \"Type\",\n              \"value\": 3\n            },\n            \"sheet\": \"ClassJob\"\n          },\n          {\n            \"when\": {\n              \"key\": \"Type\",\n              \"value\": 6\n            },\n            \"sheet\": \"Quest\"\n          },\n          {\n            \"when\": {\n              \"key\": \"Type\",\n              \"value\": 7\n            },\n            \"sheet\": \"ClassJob\"\n          },\n          {\n            \"when\": {\n              \"key\": \"Type\",\n              \"value\": 8\n            },\n            \"sheet\": \"Map\"\n          },\n          {\n            \"when\": {\n              \"key\": \"Type\",\n              \"value\": 9\n            },\n            \"sheet\": \"Quest\"\n          },\n          {\n            \"when\": {\n              \"key\": \"Type\",\n              \"value\": 11\n            },\n            \"sheet\": \"GrandCompany\"\n          },\n          {\n            \"when\": {\n              \"key\": \"Type\",\n              \"value\": 14\n            },\n            \"sheet\": \"InstanceContent\"\n          },\n          {\n            \"when\": {\n              \"key\": \"Type\",\n              \"value\": 15\n            },\n            \"sheet\": \"BeastTribe\"\n          },\n          {\n            \"when\": {\n              \"key\": \"Type\",\n              \"value\": 18\n            },\n            \"sheet\": \"GrandCompany\"\n          },\n          {\n            \"when\": {\n              \"key\": \"Type\",\n              \"value\": 20\n            },\n            \"sheet\": \"AetherCurrentCompFlgSet\"\n          },\n          {\n            \"when\": {\n              \"key\": \"Type\",\n              \"value\": 24\n            },\n            \"sheet\": \"Quest\"\n          }\n        ]\n      }\n    },\n    {\n      \"index\": 15,\n      \"type\": \"repeat\",\n      \"count\": 8,\n      \"definition\": {\n        \"name\": \"Data\",\n        \"converter\": {\n          \"type\": \"complexlink\",\n          \"links\": [\n            {\n              \"when\": {\n                \"key\": \"Type\",\n                \"value\": 2\n              },\n              \"sheet\": \"Achievement\"\n            },\n            {\n              \"when\": {\n                \"key\": \"Type\",\n                \"value\": 6\n              },\n              \"sheet\": \"Quest\"\n            },\n            {\n              \"when\": {\n                \"key\": \"Type\",\n                \"value\": 9\n              },\n              \"sheet\": \"Quest\"\n            },\n            {\n              \"when\": {\n                \"key\": \"Type\",\n                \"value\": 15\n              },\n              \"sheet\": \"BeastReputationRank\"\n            },\n            {\n              \"when\": {\n                \"key\": \"Type\",\n                \"value\": 20\n              },\n              \"sheet\": \"AetherCurrentCompFlgSet\"\n            },\n            {\n              \"when\": {\n                \"key\": \"Type\",\n                \"value\": 24\n              },\n              \"sheets\": [\n                \"ClassJob\",\n                \"Quest\"\n              ]\n            }\n          ]\n        }\n      }\n    },\n    {\n      \"index\": 23,\n      \"name\": \"Order\"\n    },\n    {\n      \"index\": 25,\n      \"name\": \"AchievementHideCondition\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"AchievementHideCondition\"\n      }\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/AchievementCategory.json",
    "content": "{\n  \"sheet\": \"AchievementCategory\",\n  \"defaultColumn\": \"Name\",\n  \"definitions\": [\n    {\n      \"name\": \"Name\"\n    },\n    {\n      \"index\": 1,\n      \"name\": \"AchievementKind\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"AchievementKind\"\n      }\n    },\n    {\n      \"index\": 2,\n      \"name\": \"ShowComplete\"\n    },\n    {\n      \"index\": 3,\n      \"name\": \"HideCategory\"\n    },\n    {\n      \"index\": 4,\n      \"name\": \"Order\"\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/AchievementHideCondition.json",
    "content": "{\n  \"sheet\": \"AchievementHideCondition\",\n  \"definitions\": [\n    {\n      \"name\": \"HideAchievement\"\n    },\n    {\n      \"index\": 1,\n      \"name\": \"HideName\"\n    },\n    {\n      \"index\": 2,\n      \"name\": \"HideConditions\"\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/AchievementKind.json",
    "content": "{\n  \"sheet\": \"AchievementKind\",\n  \"defaultColumn\": \"Name\",\n  \"definitions\": [\n    {\n      \"name\": \"Name\"\n    },\n    {\n      \"index\": 1,\n      \"name\": \"Order\"\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/AchievementTarget.json",
    "content": "{\n  \"sheet\": \"AchievementTarget\",\n  \"defaultColumn\": \"Value\",\n  \"definitions\": [\n    {\n      \"name\": \"Type\"\n    },\n    {\n      \"index\": 1,\n      \"name\": \"Value\"\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/Action.json",
    "content": "{\n  \"sheet\": \"Action\",\n  \"defaultColumn\": \"Name\",\n  \"definitions\": [\n    {\n      \"name\": \"Name\"\n    },\n    {\n      \"index\": 2,\n      \"name\": \"Icon\",\n      \"converter\": {\n        \"type\": \"icon\"\n      }\n    },\n    {\n      \"index\": 3,\n      \"name\": \"ActionCategory\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"ActionCategory\"\n      }\n    },\n    {\n      \"index\": 5,\n      \"name\": \"Animation{Start}\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"ActionCastTimeline\"\n      }\n    },\n    {\n      \"index\": 6,\n      \"name\": \"VFX\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"ActionCastVFX\"\n      }\n    },\n    {\n      \"index\": 7,\n      \"name\": \"Animation{End}\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"ActionTimeline\"\n      }\n    },\n    {\n      \"index\": 8,\n      \"name\": \"ActionTimeline{Hit}\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"ActionTimeline\"\n      }\n    },\n    {\n      \"index\": 10,\n      \"name\": \"ClassJob\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"ClassJob\"\n      }\n    },\n    {\n      \"index\": 11,\n      \"name\": \"BehaviourType\"\n    },\n    {\n      \"index\": 12,\n      \"name\": \"ClassJobLevel\"\n    },\n    {\n      \"index\": 13,\n      \"name\": \"IsRoleAction\"\n    },\n    {\n      \"index\": 14,\n      \"name\": \"Range\"\n    },\n    {\n      \"index\": 15,\n      \"name\": \"CanTargetSelf\"\n    },\n    {\n      \"index\": 16,\n      \"name\": \"CanTargetParty\"\n    },\n    {\n      \"index\": 17,\n      \"name\": \"CanTargetFriendly\"\n    },\n    {\n      \"index\": 18,\n      \"name\": \"CanTargetHostile\"\n    },\n    {\n      \"index\": 21,\n      \"name\": \"TargetArea\"\n    },\n    {\n      \"index\": 25,\n      \"name\": \"CanTargetDead\"\n    },\n    {\n      \"index\": 27,\n      \"name\": \"CastType\"\n    },\n    {\n      \"index\": 28,\n      \"name\": \"EffectRange\"\n    },\n    {\n      \"index\": 29,\n      \"name\": \"XAxisModifier\"\n    },\n    {\n      \"index\": 31,\n      \"name\": \"PrimaryCost{Type}\"\n    },\n    {\n      \"index\": 32,\n      \"name\": \"PrimaryCost{Value}\"\n    },\n    {\n      \"index\": 33,\n      \"name\": \"SecondaryCost{Type}\"\n    },\n    {\n      \"index\": 34,\n      \"name\": \"SecondaryCost{Value}\",\n      \"converter\": {\n        \"type\": \"complexlink\",\n        \"links\": [\n          {\n            \"when\": {\n              \"key\": \"SecondaryCost{Type}\",\n              \"value\": 32\n            },\n            \"sheet\": \"Status\"\n          },\n          {\n            \"when\": {\n              \"key\": \"SecondaryCost{Type}\",\n              \"value\": 35\n            },\n            \"sheet\": \"Status\"\n          },\n          {\n            \"when\": {\n              \"key\": \"SecondaryCost{Type}\",\n              \"value\": 46\n            },\n            \"sheet\": \"Status\"\n          }\n        ]\n      }\n    },\n    {\n      \"index\": 35,\n      \"name\": \"Action{Combo}\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"Action\"\n      }\n    },\n    {\n      \"index\": 36,\n      \"name\": \"PreservesCombo\"\n    },\n    {\n      \"index\": 37,\n      \"name\": \"Cast<100ms>\"\n    },\n    {\n      \"index\": 39,\n      \"name\": \"Recast<100ms>\"\n    },\n    {\n      \"index\": 40,\n      \"name\": \"CooldownGroup\"\n    },\n    {\n      \"index\": 41,\n      \"name\": \"AdditionalCooldownGroup\"\n    },\n    {\n      \"index\": 42,\n      \"name\": \"MaxCharges\"\n    },\n    {\n      \"index\": 43,\n      \"name\": \"AttackType\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"AttackType\"\n      }\n    },\n    {\n      \"index\": 44,\n      \"name\": \"Aspect\"\n    },\n    {\n      \"index\": 45,\n      \"name\": \"ActionProcStatus\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"ActionProcStatus\"\n      }\n    },\n    {\n      \"index\": 47,\n      \"name\": \"Status{GainSelf}\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"Status\"\n      }\n    },\n    {\n      \"index\": 48,\n      \"name\": \"UnlockLink\",\n      \"converter\": {\n        \"type\": \"generic\"\n      }\n    },\n    {\n      \"index\": 49,\n      \"name\": \"ClassJobCategory\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"ClassJobCategory\"\n      }\n    },\n    {\n      \"index\": 52,\n      \"name\": \"AffectsPosition\"\n    },\n    {\n      \"index\": 53,\n      \"name\": \"Omen\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"Omen\"\n      }\n    },\n    {\n      \"index\": 55,\n      \"name\": \"IsPvP\"\n    },\n    {\n      \"index\": 67,\n      \"name\": \"IsPlayerAction\"\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/ActionCastTimeline.json",
    "content": "{\n  \"sheet\": \"ActionCastTimeline\",\n  \"defaultColumn\": \"Name\",\n  \"definitions\": [\n    {\n      \"name\": \"Name\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"ActionTimeline\"\n      }\n    },\n    {\n      \"index\": 1,\n      \"name\": \"VFX\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"VFX\"\n      }\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/ActionCastVFX.json",
    "content": "{\n  \"sheet\": \"ActionCastVFX\",\n  \"defaultColumn\": \"VFX\",\n  \"definitions\": [\n    {\n      \"name\": \"VFX\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"VFX\"\n      }\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/ActionCategory.json",
    "content": "{\n  \"sheet\": \"ActionCategory\",\n  \"defaultColumn\": \"Name\",\n  \"definitions\": [\n    {\n      \"name\": \"Name\"\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/ActionComboRoute.json",
    "content": "{\n  \"sheet\": \"ActionComboRoute\",\n  \"defaultColumn\": \"Name\",\n  \"definitions\": [\n    {\n      \"name\": \"Name\"\n    },\n    {\n      \"index\": 2,\n      \"type\": \"repeat\",\n      \"count\": 4,\n      \"definition\": {\n        \"name\": \"Action\",\n        \"converter\": {\n          \"type\": \"link\",\n          \"target\": \"Action\"\n        }\n      }\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/ActionIndirection.json",
    "content": "{\n  \"sheet\": \"ActionIndirection\",\n  \"defaultColumn\": \"Name\",\n  \"definitions\": [\n    {\n      \"name\": \"Name\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"Action\"\n      }\n    },\n    {\n      \"index\": 1,\n      \"name\": \"ClassJob\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"ClassJob\"\n      }\n    },\n    {\n      \"index\": 2,\n      \"name\": \"PreviousComboAction\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"Action\"\n      }\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/ActionParam.json",
    "content": "{\n  \"sheet\": \"ActionParam\",\n  \"defaultColumn\": \"Name\",\n  \"definitions\": [\n    {\n      \"name\": \"Name\"\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/ActionProcStatus.json",
    "content": "{\n  \"sheet\": \"ActionProcStatus\",\n  \"defaultColumn\": \"Status\",\n  \"definitions\": [\n    {\n      \"name\": \"Status\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"Status\"\n      }\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/ActionTimeline.json",
    "content": "{\n  \"sheet\": \"ActionTimeline\",\n  \"defaultColumn\": \"Key\",\n  \"definitions\": [\n    {\n      \"name\": \"Type\"\n    },\n    {\n      \"index\": 1,\n      \"name\": \"Priority\"\n    },\n    {\n      \"index\": 2,\n      \"name\": \"Pause\"\n    },\n    {\n      \"index\": 3,\n      \"name\": \"Stance\"\n    },\n    {\n      \"index\": 4,\n      \"name\": \"Slot\"\n    },\n    {\n      \"index\": 5,\n      \"name\": \"LookAtMode\"\n    },\n    {\n      \"index\": 6,\n      \"name\": \"Key\"\n    },\n    {\n      \"index\": 7,\n      \"name\": \"ActionTimelineIDMode\"\n    },\n    {\n      \"index\": 8,\n      \"name\": \"WeaponTimeline\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"WeaponTimeline\"\n      }\n    },\n    {\n      \"index\": 9,\n      \"name\": \"LoadType\"\n    },\n    {\n      \"index\": 10,\n      \"name\": \"StartAttach\"\n    },\n    {\n      \"index\": 11,\n      \"name\": \"ResidentPap\"\n    },\n    {\n      \"index\": 13,\n      \"name\": \"KillUpper\"\n    },\n    {\n      \"index\": 14,\n      \"name\": \"IsMotionCanceledByMoving\"\n    },\n    {\n      \"index\": 18,\n      \"name\": \"IsLoop\"\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/ActionTimelineMove.json",
    "content": "{\n  \"sheet\": \"ActionTimelineMove\",\n  \"definitions\": []\n}"
  },
  {
    "path": "SaintCoinach/Definitions/ActionTimelineReplace.json",
    "content": "{\n  \"sheet\": \"ActionTimelineReplace\",\n  \"definitions\": [\n    {\n      \"name\": \"Old\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"ActionTimeline\"\n      }\n    },\n    {\n      \"index\": 1,\n      \"name\": \"New\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"ActionTimeline\"\n      }\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/ActionTransient.json",
    "content": "{\n  \"sheet\": \"ActionTransient\",\n  \"defaultColumn\": \"Description\",\n  \"definitions\": [\n    {\n      \"name\": \"Description\"\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/ActivityFeedButtons.json",
    "content": "{\n  \"sheet\": \"ActivityFeedButtons\",\n  \"definitions\": [\n    {\n      \"index\": 1,\n      \"name\": \"BannerURL\"\n    },\n    {\n      \"index\": 2,\n      \"name\": \"Description\"\n    },\n    {\n      \"index\": 3,\n      \"name\": \"Language\"\n    },\n    {\n      \"index\": 4,\n      \"name\": \"PictureURL\"\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/ActivityFeedCaptions.json",
    "content": "{\n  \"sheet\": \"ActivityFeedCaptions\",\n  \"definitions\": [\n    {\n      \"name\": \"JA\"\n    },\n    {\n      \"index\": 1,\n      \"name\": \"EN\"\n    },\n    {\n      \"index\": 2,\n      \"name\": \"DE\"\n    },\n    {\n      \"index\": 3,\n      \"name\": \"FR\"\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/ActivityFeedGroupCaptions.json",
    "content": "{\n  \"sheet\": \"ActivityFeedGroupCaptions\",\n  \"definitions\": [\n    {\n      \"name\": \"JA\"\n    },\n    {\n      \"index\": 1,\n      \"name\": \"EN\"\n    },\n    {\n      \"index\": 2,\n      \"name\": \"DE\"\n    },\n    {\n      \"index\": 3,\n      \"name\": \"FR\"\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/ActivityFeedImages.json",
    "content": "{\n  \"sheet\": \"ActivityFeedImages\",\n  \"definitions\": [\n    {\n      \"name\": \"ExpansionImage\"\n    },\n    {\n      \"index\": 1,\n      \"name\": \"ActivityFeedJA\"\n    },\n    {\n      \"index\": 2,\n      \"name\": \"ActivityFeedEN\"\n    },\n    {\n      \"index\": 3,\n      \"name\": \"ActivityFeedDE\"\n    },\n    {\n      \"index\": 4,\n      \"name\": \"ActivityFeedFR\"\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/Addon.json",
    "content": "{\n  \"sheet\": \"Addon\",\n  \"defaultColumn\": \"Text\",\n  \"definitions\": [\n    {\n      \"name\": \"Text\"\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/Adventure.json",
    "content": "{\n  \"sheet\": \"Adventure\",\n  \"defaultColumn\": \"Name\",\n  \"definitions\": [\n    {\n      \"name\": \"Level\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"Level\"\n      }\n    },\n    {\n      \"index\": 1,\n      \"name\": \"MinLevel\"\n    },\n    {\n      \"index\": 2,\n      \"name\": \"MaxLevel\"\n    },\n    {\n      \"index\": 3,\n      \"name\": \"Emote\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"Emote\"\n      }\n    },\n    {\n      \"index\": 4,\n      \"name\": \"MinTime\"\n    },\n    {\n      \"index\": 5,\n      \"name\": \"MaxTime\"\n    },\n    {\n      \"index\": 6,\n      \"name\": \"PlaceName\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"PlaceName\"\n      }\n    },\n    {\n      \"index\": 7,\n      \"name\": \"Icon{List}\",\n      \"converter\": {\n        \"type\": \"icon\"\n      }\n    },\n    {\n      \"index\": 8,\n      \"name\": \"Icon{Discovered}\",\n      \"converter\": {\n        \"type\": \"icon\"\n      }\n    },\n    {\n      \"index\": 9,\n      \"name\": \"Icon{Undiscovered}\",\n      \"converter\": {\n        \"type\": \"icon\"\n      }\n    },\n    {\n      \"index\": 10,\n      \"name\": \"IsInitial\"\n    },\n    {\n      \"index\": 11,\n      \"name\": \"Name\"\n    },\n    {\n      \"index\": 12,\n      \"name\": \"Impression\"\n    },\n    {\n      \"index\": 13,\n      \"name\": \"Description\"\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/AdventureExPhase.json",
    "content": "{\n  \"sheet\": \"AdventureExPhase\",\n  \"definitions\": [\n    {\n      \"name\": \"Quest\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"Quest\"\n      }\n    },\n    {\n      \"index\": 1,\n      \"name\": \"Adventure{Begin}\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"Adventure\"\n      }\n    },\n    {\n      \"index\": 2,\n      \"name\": \"Adventure{End}\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"Adventure\"\n      }\n    },\n    {\n      \"index\": 3,\n      \"name\": \"Expansion\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"ExVersion\"\n      }\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/AetherCurrent.json",
    "content": "{\n  \"sheet\": \"AetherCurrent\",\n  \"defaultColumn\": \"Quest\",\n  \"definitions\": [\n    {\n      \"name\": \"Quest\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"Quest\"\n      }\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/AetherCurrentCompFlgSet.json",
    "content": "{\n  \"sheet\": \"AetherCurrentCompFlgSet\",\n  \"definitions\": [\n    {\n      \"name\": \"Territory\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"TerritoryType\"\n      }\n    },\n    {\n      \"index\": 1,\n      \"type\": \"repeat\",\n      \"count\": 15,\n      \"definition\": {\n        \"name\": \"AetherCurrent\",\n        \"converter\": {\n          \"type\": \"link\",\n          \"target\": \"AetherCurrent\"\n        }\n      }\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/AetherialWheel.json",
    "content": "{\n  \"sheet\": \"AetherialWheel\",\n  \"definitions\": [\n    {\n      \"name\": \"Item{Unprimed}\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"Item\"\n      }\n    },\n    {\n      \"index\": 1,\n      \"name\": \"Item{Primed}\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"Item\"\n      }\n    },\n    {\n      \"index\": 2,\n      \"name\": \"Grade\"\n    },\n    {\n      \"index\": 3,\n      \"name\": \"HoursRequired\"\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/Aetheryte.json",
    "content": "{\n  \"sheet\": \"Aetheryte\",\n  \"defaultColumn\": \"PlaceName\",\n  \"definitions\": [\n    {\n      \"name\": \"Singular\"\n    },\n    {\n      \"index\": 1,\n      \"name\": \"Adjective\"\n    },\n    {\n      \"index\": 2,\n      \"name\": \"Plural\"\n    },\n    {\n      \"index\": 3,\n      \"name\": \"PossessivePronoun\"\n    },\n    {\n      \"index\": 4,\n      \"name\": \"StartsWithVowel\"\n    },\n    {\n      \"index\": 6,\n      \"name\": \"Pronoun\"\n    },\n    {\n      \"index\": 7,\n      \"name\": \"Article\"\n    },\n    {\n      \"index\": 8,\n      \"name\": \"PlaceName\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"PlaceName\"\n      }\n    },\n    {\n      \"index\": 9,\n      \"name\": \"AethernetName\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"PlaceName\"\n      }\n    },\n    {\n      \"index\": 10,\n      \"name\": \"Territory\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"TerritoryType\"\n      }\n    },\n    {\n      \"index\": 11,\n      \"type\": \"repeat\",\n      \"count\": 4,\n      \"definition\": {\n        \"name\": \"Level\",\n        \"converter\": {\n          \"type\": \"link\",\n          \"target\": \"Level\"\n        }\n      }\n    },\n    {\n      \"index\": 15,\n      \"name\": \"IsAetheryte\"\n    },\n    {\n      \"index\": 18,\n      \"name\": \"AethernetGroup\"\n    },\n    {\n      \"index\": 19,\n      \"name\": \"Invisible\"\n    },\n    {\n      \"index\": 20,\n      \"name\": \"RequiredQuest\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"Quest\"\n      }\n    },\n    {\n      \"index\": 21,\n      \"name\": \"Map\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"Map\"\n      }\n    },\n    {\n      \"index\": 22,\n      \"name\": \"Aetherstream{X}\"\n    },\n    {\n      \"index\": 23,\n      \"name\": \"Aetherstream{Y}\"\n    },\n    {\n      \"index\": 24,\n      \"name\": \"Order\"\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/AetheryteSystemDefine.json",
    "content": "{\n  \"sheet\": \"AetheryteSystemDefine\",\n  \"defaultColumn\": \"Text\",\n  \"definitions\": [\n    {\n      \"name\": \"Text\"\n    },\n    {\n      \"index\": 1,\n      \"name\": \"DefineValue\"\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/AetheryteTransient.json",
    "content": "{\n  \"sheet\": \"AetheryteTransient\",\n  \"definitions\": []\n}"
  },
  {
    "path": "SaintCoinach/Definitions/AirshipExplorationLevel.json",
    "content": "{\n  \"sheet\": \"AirshipExplorationLevel\",\n  \"definitions\": [\n    {\n      \"name\": \"Capacity\"\n    },\n    {\n      \"index\": 1,\n      \"name\": \"ExpToNext\"\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/AirshipExplorationLog.json",
    "content": "{\n  \"sheet\": \"AirshipExplorationLog\",\n  \"defaultColumn\": \"Text\",\n  \"definitions\": [\n    {\n      \"name\": \"Text\"\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/AirshipExplorationParamType.json",
    "content": "{\n  \"sheet\": \"AirshipExplorationParamType\",\n  \"defaultColumn\": \"Name\",\n  \"definitions\": [\n    {\n      \"name\": \"Name\"\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/AirshipExplorationPart.json",
    "content": "{\n  \"sheet\": \"AirshipExplorationPart\",\n  \"definitions\": [\n    {\n      \"name\": \"Slot\"\n    },\n    {\n      \"index\": 1,\n      \"name\": \"Rank\"\n    },\n    {\n      \"index\": 2,\n      \"name\": \"Components\"\n    },\n    {\n      \"index\": 3,\n      \"name\": \"Surveillance\"\n    },\n    {\n      \"index\": 4,\n      \"name\": \"Retrieval\"\n    },\n    {\n      \"index\": 5,\n      \"name\": \"Speed\"\n    },\n    {\n      \"index\": 6,\n      \"name\": \"Range\"\n    },\n    {\n      \"index\": 7,\n      \"name\": \"Favor\"\n    },\n    {\n      \"index\": 8,\n      \"name\": \"Class\"\n    },\n    {\n      \"index\": 9,\n      \"name\": \"RepairMaterials\"\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/AirshipExplorationPoint.json",
    "content": "{\n  \"sheet\": \"AirshipExplorationPoint\",\n  \"defaultColumn\": \"Name\",\n  \"definitions\": [\n    {\n      \"name\": \"Name\"\n    },\n    {\n      \"index\": 1,\n      \"name\": \"Name{Short}\"\n    },\n    {\n      \"index\": 2,\n      \"name\": \"Passengers\"\n    },\n    {\n      \"index\": 3,\n      \"name\": \"X\"\n    },\n    {\n      \"index\": 4,\n      \"name\": \"Y\"\n    },\n    {\n      \"index\": 5,\n      \"name\": \"RankReq\"\n    },\n    {\n      \"index\": 6,\n      \"name\": \"CeruleumTankReq\"\n    },\n    {\n      \"index\": 7,\n      \"name\": \"SurveyDuration<min>\"\n    },\n    {\n      \"index\": 8,\n      \"name\": \"SurveyDistance\"\n    },\n    {\n      \"index\": 10,\n      \"name\": \"SurveillanceReq\"\n    },\n    {\n      \"index\": 13,\n      \"name\": \"ExpReward\"\n    }\n  ]\n}\n"
  },
  {
    "path": "SaintCoinach/Definitions/AkatsukiNote.json",
    "content": "{\n  \"sheet\": \"AkatsukiNote\",\n  \"definitions\": []\n}"
  },
  {
    "path": "SaintCoinach/Definitions/AkatsukiNoteString.json",
    "content": "{\n  \"sheet\": \"AkatsukiNoteString\",\n  \"definitions\": []\n}"
  },
  {
    "path": "SaintCoinach/Definitions/AnimaWeapon5.json",
    "content": "{\n  \"sheet\": \"AnimaWeapon5\",\n  \"definitions\": [\n    {\n      \"name\": \"Item\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"Item\"\n      }\n    },\n    {\n      \"index\": 2,\n      \"name\": \"SecondaryStatTotal\"\n    },\n    {\n      \"index\": 3,\n      \"type\": \"repeat\",\n      \"count\": 5,\n      \"definition\": {\n        \"name\": \"Parameter\",\n        \"converter\": {\n          \"type\": \"link\",\n          \"target\": \"AnimaWeapon5Param\"\n        }\n      }\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/AnimaWeapon5Param.json",
    "content": "{\n  \"sheet\": \"AnimaWeapon5Param\",\n  \"defaultColumn\": \"BaseParam\",\n  \"definitions\": [\n    {\n      \"name\": \"BaseParam\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"BaseParam\"\n      }\n    },\n    {\n      \"index\": 1,\n      \"name\": \"Name\"\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/AnimaWeapon5PatternGroup.json",
    "content": "{\n  \"sheet\": \"AnimaWeapon5PatternGroup\",\n  \"defaultColumn\": \"Name\",\n  \"definitions\": [\n    {\n      \"name\": \"Name\"\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/AnimaWeapon5SpiritTalk.json",
    "content": "{\n  \"sheet\": \"AnimaWeapon5SpiritTalk\",\n  \"defaultColumn\": \"Dialogue\",\n  \"definitions\": [\n    {\n      \"name\": \"Dialogue\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"AnimaWeapon5SpiritTalkParam\"\n      }\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/AnimaWeapon5SpiritTalkParam.json",
    "content": "{\n  \"sheet\": \"AnimaWeapon5SpiritTalkParam\",\n  \"defaultColumn\": \"Prologue\",\n  \"definitions\": [\n    {\n      \"name\": \"Prologue\"\n    },\n    {\n      \"index\": 1,\n      \"name\": \"Epilogue\"\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/AnimaWeapon5TradeItem.json",
    "content": "{\n  \"sheet\": \"AnimaWeapon5TradeItem\",\n  \"definitions\": [\n    {\n      \"index\": 1,\n      \"name\": \"CrystalSand\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"Item\"\n      }\n    },\n    {\n      \"index\": 2,\n      \"name\": \"Qty\"\n    },\n    {\n      \"index\": 3,\n      \"type\": \"repeat\",\n      \"count\": 8,\n      \"definition\": {\n        \"type\": \"group\",\n        \"members\": [\n          {\n            \"name\": \"ItemName\",\n            \"converter\": {\n              \"type\": \"link\",\n              \"target\": \"Item\"\n            }\n          },\n          {\n            \"name\": \"IsHQ\"\n          },\n          {\n            \"name\": \"Quantity\"\n          }\n        ]\n      }\n    },\n    {\n      \"index\": 27,\n      \"name\": \"Category\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"AnimaWeapon5PatternGroup\"\n      }\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/AnimaWeaponFUITalk.json",
    "content": "{\n  \"sheet\": \"AnimaWeaponFUITalk\",\n  \"defaultColumn\": \"Dialogue\",\n  \"definitions\": [\n    {\n      \"name\": \"Dialogue\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"AnimaWeaponFUITalkParam\"\n      }\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/AnimaWeaponFUITalkParam.json",
    "content": "{\n  \"sheet\": \"AnimaWeaponFUITalkParam\",\n  \"defaultColumn\": \"Prologue\",\n  \"definitions\": [\n    {\n      \"name\": \"Prologue\"\n    },\n    {\n      \"index\": 1,\n      \"name\": \"Epilogue\"\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/AnimaWeaponIcon.json",
    "content": "{\n  \"sheet\": \"AnimaWeaponIcon\",\n  \"definitions\": [\n    {\n      \"name\": \"Hyperconductive\",\n      \"converter\": {\n        \"type\": \"icon\"\n      }\n    },\n    {\n      \"index\": 1,\n      \"name\": \"Reborn\",\n      \"converter\": {\n        \"type\": \"icon\"\n      }\n    },\n    {\n      \"index\": 2,\n      \"name\": \"Sharpened\",\n      \"converter\": {\n        \"type\": \"icon\"\n      }\n    },\n    {\n      \"index\": 3,\n      \"name\": \"Zodiac\",\n      \"converter\": {\n        \"type\": \"icon\"\n      }\n    },\n    {\n      \"index\": 4,\n      \"name\": \"ZodiacLux\",\n      \"converter\": {\n        \"type\": \"icon\"\n      }\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/AnimaWeaponItem.json",
    "content": "{\n  \"sheet\": \"AnimaWeaponItem\",\n  \"definitions\": [\n    {\n      \"type\": \"repeat\",\n      \"count\": 14,\n      \"definition\": {\n        \"name\": \"Item\",\n        \"converter\": {\n          \"type\": \"link\",\n          \"target\": \"Item\"\n        }\n      }\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/AnimationLOD.json",
    "content": "{\n  \"sheet\": \"AnimationLOD\",\n  \"definitions\": [\n    {\n      \"name\": \"CameraDistance\"\n    },\n    {\n      \"index\": 1,\n      \"name\": \"SampleInterval\"\n    },\n    {\n      \"index\": 2,\n      \"name\": \"BoneLOD\"\n    },\n    {\n      \"index\": 3,\n      \"type\": \"repeat\",\n      \"count\": 8,\n      \"definition\": {\n        \"name\": \"AnimationEnable\"\n      }\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/AozAction.json",
    "content": "{\n  \"sheet\": \"AozAction\",\n  \"defaultColumn\": \"Action\",\n  \"definitions\": [\n    {\n      \"name\": \"Action\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"Action\"\n      }\n    },\n    {\n      \"index\": 1,\n      \"name\": \"Rank\"\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/AozActionTransient.json",
    "content": "{\n  \"sheet\": \"AozActionTransient\",\n  \"defaultColumn\": \"Action\",\n  \"definitions\": [\n    {\n      \"name\": \"Number\"\n    },\n    {\n      \"index\": 1,\n      \"name\": \"Icon\",\n      \"converter\": {\n        \"type\": \"icon\"\n      }\n    },\n    {\n      \"index\": 2,\n      \"name\": \"Stats\"\n    },\n    {\n      \"index\": 3,\n      \"name\": \"Description\"\n    },\n    {\n      \"index\": 4,\n      \"name\": \"LocationKey\"\n    },\n    {\n      \"index\": 5,\n      \"name\": \"Location\",\n      \"converter\": {\n        \"type\": \"complexlink\",\n        \"links\": [\n          {\n            \"when\": {\n              \"key\": \"LocationKey\",\n              \"value\": 1\n            },\n            \"sheet\": \"PlaceName\"\n          },\n          {\n            \"when\": {\n              \"key\": \"LocationKey\",\n              \"value\": 4\n            },\n            \"sheet\": \"ContentFinderCondition\"\n          }\n        ]\n      }\n    },\n    {\n      \"index\": 6,\n      \"name\": \"RequiredForQuest\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"Quest\"\n      }\n    },\n    {\n      \"index\": 7,\n      \"name\": \"PreviousQuest\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"Quest\"\n      }\n    },\n    {\n      \"index\": 8,\n      \"name\": \"TargetsEnemy\"\n    },\n    {\n      \"index\": 9,\n      \"name\": \"TargetsSelfOrAlly\"\n    },\n    {\n      \"index\": 10,\n      \"name\": \"CauseSlow\"\n    },\n    {\n      \"index\": 11,\n      \"name\": \"CausePetrify\"\n    },\n    {\n      \"index\": 12,\n      \"name\": \"CauseParalysis\"\n    },\n    {\n      \"index\": 13,\n      \"name\": \"CauseInterrupt\"\n    },\n    {\n      \"index\": 14,\n      \"name\": \"CauseBlind\"\n    },\n    {\n      \"index\": 15,\n      \"name\": \"CauseStun\"\n    },\n    {\n      \"index\": 16,\n      \"name\": \"CauseSleep\"\n    },\n    {\n      \"index\": 17,\n      \"name\": \"CauseBind\"\n    },\n    {\n      \"index\": 18,\n      \"name\": \"CauseHeavy\"\n    },\n    {\n      \"index\": 19,\n      \"name\": \"CauseDeath\"\n    }\n  ]\n}\n"
  },
  {
    "path": "SaintCoinach/Definitions/AquariumFish.json",
    "content": "{\n  \"sheet\": \"AquariumFish\",\n  \"defaultColumn\": \"Item\",\n  \"definitions\": [\n    {\n      \"name\": \"AquariumWater\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"AquariumWater\"\n      }\n    },\n    {\n      \"index\": 1,\n      \"name\": \"Size\"\n    },\n    {\n      \"index\": 2,\n      \"name\": \"Item\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"Item\"\n      }\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/AquariumWater.json",
    "content": "{\n  \"sheet\": \"AquariumWater\",\n  \"defaultColumn\": \"Name\",\n  \"definitions\": [\n    {\n      \"index\": 1,\n      \"name\": \"Name\"\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/ArchiveItem.json",
    "content": "{\n  \"sheet\": \"ArchiveItem\",\n  \"definitions\": []\n}"
  },
  {
    "path": "SaintCoinach/Definitions/ArrayEventHandler.json",
    "content": "{\n  \"sheet\": \"ArrayEventHandler\",\n  \"definitions\": [\n    {\n      \"type\": \"repeat\",\n      \"count\": 16,\n      \"definition\": {\n        \"name\": \"Data\",\n        \"converter\": {\n          \"type\": \"multiref\",\n          \"targets\": [\n            \"InstanceContentGuide\",\n            \"Story\",\n            \"Opening\",\n            \"CustomTalk\",\n            \"DefaultTalk\",\n            \"GilShop\",\n            \"Warp\",\n            \"Quest\"\n          ]\n        }\n      }\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/AttackType.json",
    "content": "{\n  \"sheet\": \"AttackType\",\n  \"defaultColumn\": \"Name\",\n  \"definitions\": [\n    {\n      \"name\": \"Name\"\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/Attract.json",
    "content": "{\n  \"sheet\": \"Attract\",\n  \"definitions\": [\n    {\n      \"name\": \"MaxDistance\"\n    },\n    {\n      \"index\": 1,\n      \"name\": \"Speed\"\n    },\n    {\n      \"index\": 2,\n      \"name\": \"MinRemainingDistance\"\n    },\n    {\n      \"index\": 3,\n      \"name\": \"UseDistanceBetweenHitboxes\"\n    },\n    {\n      \"index\": 4,\n      \"name\": \"Direction\"\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/BGM.json",
    "content": "{\n  \"sheet\": \"BGM\",\n  \"defaultColumn\": \"File\",\n  \"definitions\": [\n    {\n      \"name\": \"File\"\n    },\n    {\n      \"index\": 1,\n      \"name\": \"Priority\"\n    },\n    {\n      \"index\": 2,\n      \"name\": \"DisableRestartTimeOut\"\n    },\n    {\n      \"index\": 3,\n      \"name\": \"DisableRestart\"\n    },\n    {\n      \"index\": 4,\n      \"name\": \"PassEnd\"\n    },\n    {\n      \"index\": 5,\n      \"name\": \"DisableRestartResetTime\"\n    },\n    {\n      \"index\": 6,\n      \"name\": \"SpecialMode\"\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/BGMFade.json",
    "content": "{\n  \"sheet\": \"BGMFade\",\n  \"definitions\": [\n    {\n      \"name\": \"SceneOut\"\n    },\n    {\n      \"index\": 1,\n      \"name\": \"SceneIn\"\n    },\n    {\n      \"index\": 2,\n      \"name\": \"BGMFadeType\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"BGMFadeType\"\n      }\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/BGMFadeType.json",
    "content": "{\n  \"sheet\": \"BGMFadeType\",\n  \"definitions\": [\n    {\n      \"name\": \"FadeOutTime\"\n    },\n    {\n      \"index\": 1,\n      \"name\": \"FadeInTime\"\n    },\n    {\n      \"index\": 2,\n      \"name\": \"FadeInStartTime\"\n    },\n    {\n      \"index\": 3,\n      \"name\": \"ResumeFadeInTime\"\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/BGMScene.json",
    "content": "{\n  \"sheet\": \"BGMScene\",\n  \"definitions\": [\n    {\n      \"name\": \"EnableDisableRestart\"\n    },\n    {\n      \"index\": 1,\n      \"name\": \"Resume\"\n    },\n    {\n      \"index\": 2,\n      \"name\": \"EnablePassEnd\"\n    },\n    {\n      \"index\": 3,\n      \"name\": \"ForceAutoReset\"\n    },\n    {\n      \"index\": 4,\n      \"name\": \"IgnoreBattle\"\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/BGMSituation.json",
    "content": "{\n  \"sheet\": \"BGMSituation\",\n  \"definitions\": [\n    {\n      \"name\": \"DaytimeID\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"BGM\"\n      }\n    },\n    {\n      \"index\": 1,\n      \"name\": \"NightID\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"BGM\"\n      }\n    },\n    {\n      \"index\": 2,\n      \"name\": \"BattleID\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"BGM\"\n      }\n    },\n    {\n      \"index\": 3,\n      \"name\": \"DaybreakID\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"BGM\"\n      }\n    },\n    {\n      \"index\": 4,\n      \"name\": \"TwilightID\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"BGM\"\n      }\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/BGMSwitch.json",
    "content": "{\n  \"sheet\": \"BGMSwitch\",\n  \"definitions\": [\n    {\n      \"name\": \"BGMSystemDefine\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"BGMSystemDefine\"\n      }\n    },\n    {\n      \"index\": 1,\n      \"name\": \"Quest\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"Quest\"\n      }\n    },\n    {\n      \"index\": 3,\n      \"name\": \"BGM\",\n      \"converter\": {\n        \"type\": \"multiref\",\n        \"targets\": [\n          \"BGM\",\n          \"BGMSituation\"\n        ]\n      }\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/BGMSystemDefine.json",
    "content": "{\n  \"sheet\": \"BGMSystemDefine\",\n  \"defaultColumn\": \"Define\",\n  \"definitions\": [\n    {\n      \"name\": \"Define\"\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/BNpcAnnounceIcon.json",
    "content": "{\n  \"sheet\": \"BNpcAnnounceIcon\",\n  \"defaultColumn\": \"Icon\",\n  \"definitions\": [\n    {\n      \"name\": \"Icon\",\n      \"converter\": {\n        \"type\": \"icon\"\n      }\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/BNpcBase.json",
    "content": "{\n  \"sheet\": \"BNpcBase\",\n  \"definitions\": [\n    {\n      \"name\": \"Behavior\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"Behavior\"\n      }\n    },\n    {\n      \"index\": 1,\n      \"name\": \"Battalion\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"Battalion\"\n      }\n    },\n    {\n      \"index\": 2,\n      \"name\": \"LinkRace\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"LinkRace\"\n      }\n    },\n    {\n      \"index\": 3,\n      \"name\": \"Rank\"\n    },\n    {\n      \"index\": 4,\n      \"name\": \"Scale\"\n    },\n    {\n      \"index\": 5,\n      \"name\": \"ModelChara\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"ModelChara\"\n      }\n    },\n    {\n      \"index\": 6,\n      \"name\": \"BNpcCustomize\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"BNpcCustomize\"\n      }\n    },\n    {\n      \"index\": 7,\n      \"name\": \"NpcEquip\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"NpcEquip\"\n      }\n    },\n    {\n      \"index\": 8,\n      \"name\": \"Special\"\n    },\n    {\n      \"index\": 9,\n      \"name\": \"SEPack\"\n    },\n    {\n      \"index\": 11,\n      \"name\": \"ArrayEventHandler\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"ArrayEventHandler\"\n      }\n    },\n    {\n      \"index\": 13,\n      \"name\": \"BNpcParts\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"BNpcParts\"\n      }\n    },\n    {\n      \"index\": 16,\n      \"name\": \"IsTargetLine\"\n    },\n    {\n      \"index\": 17,\n      \"name\": \"IsDisplayLevel\"\n    }\n  ]\n}\n"
  },
  {
    "path": "SaintCoinach/Definitions/BNpcBasePopVfx.json",
    "content": "{\n  \"sheet\": \"BNpcBasePopVfx\",\n  \"definitions\": []\n}"
  },
  {
    "path": "SaintCoinach/Definitions/BNpcCustomize.json",
    "content": "{\n  \"sheet\": \"BNpcCustomize\",\n  \"definitions\": [\n    {\n      \"name\": \"Race\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"Race\"\n      }\n    },\n    {\n      \"index\": 1,\n      \"name\": \"Gender\"\n    },\n    {\n      \"index\": 2,\n      \"name\": \"BodyType\"\n    },\n    {\n      \"index\": 3,\n      \"name\": \"Height\"\n    },\n    {\n      \"index\": 4,\n      \"name\": \"Tribe\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"Tribe\"\n      }\n    },\n    {\n      \"index\": 5,\n      \"name\": \"Face\"\n    },\n    {\n      \"index\": 6,\n      \"name\": \"HairStyle\"\n    },\n    {\n      \"index\": 7,\n      \"name\": \"HairHighlight\"\n    },\n    {\n      \"index\": 8,\n      \"name\": \"SkinColor\"\n    },\n    {\n      \"index\": 9,\n      \"name\": \"EyeHeterochromia\"\n    },\n    {\n      \"index\": 10,\n      \"name\": \"HairColor\"\n    },\n    {\n      \"index\": 11,\n      \"name\": \"HairHighlightColor\"\n    },\n    {\n      \"index\": 12,\n      \"name\": \"FacialFeature\"\n    },\n    {\n      \"index\": 13,\n      \"name\": \"FacialFeatureColor\"\n    },\n    {\n      \"index\": 14,\n      \"name\": \"Eyebrows\"\n    },\n    {\n      \"index\": 15,\n      \"name\": \"EyeColor\"\n    },\n    {\n      \"index\": 16,\n      \"name\": \"EyeShape\"\n    },\n    {\n      \"index\": 17,\n      \"name\": \"Nose\"\n    },\n    {\n      \"index\": 18,\n      \"name\": \"Jaw\"\n    },\n    {\n      \"index\": 19,\n      \"name\": \"Mouth\"\n    },\n    {\n      \"index\": 20,\n      \"name\": \"LipColor\"\n    },\n    {\n      \"index\": 21,\n      \"name\": \"BustOrTone1\"\n    },\n    {\n      \"index\": 22,\n      \"name\": \"ExtraFeature1\"\n    },\n    {\n      \"index\": 23,\n      \"name\": \"ExtraFeature2OrBust\"\n    },\n    {\n      \"index\": 24,\n      \"name\": \"FacePaint\"\n    },\n    {\n      \"index\": 25,\n      \"name\": \"FacePaintColor\"\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/BNpcName.json",
    "content": "{\n  \"sheet\": \"BNpcName\",\n  \"defaultColumn\": \"Singular\",\n  \"definitions\": [\n    {\n      \"name\": \"Singular\"\n    },\n    {\n      \"index\": 1,\n      \"name\": \"Adjective\"\n    },\n    {\n      \"index\": 2,\n      \"name\": \"Plural\"\n    },\n    {\n      \"index\": 3,\n      \"name\": \"PossessivePronoun\"\n    },\n    {\n      \"index\": 4,\n      \"name\": \"StartsWithVowel\"\n    },\n    {\n      \"index\": 6,\n      \"name\": \"Pronoun\"\n    },\n    {\n      \"index\": 7,\n      \"name\": \"Article\"\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/BNpcParts.json",
    "content": "{\n  \"sheet\": \"BNpcParts\",\n  \"definitions\": [\n    {\n      \"name\": \"BNpcBase[1]\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"BNpcBase\"\n      }\n    },\n    {\n      \"index\": 1,\n      \"name\": \"PartSlot[1]\"\n    },\n    {\n      \"index\": 6,\n      \"name\": \"X[1]\"\n    },\n    {\n      \"index\": 7,\n      \"name\": \"Y[1]\"\n    },\n    {\n      \"index\": 8,\n      \"name\": \"Z[1]\"\n    },\n    {\n      \"index\": 10,\n      \"name\": \"Scale[1]\"\n    },\n    {\n      \"index\": 11,\n      \"name\": \"BNpcBase[2]\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"BNpcBase\"\n      }\n    },\n    {\n      \"index\": 12,\n      \"name\": \"PartSlot[2]\"\n    },\n    {\n      \"index\": 17,\n      \"name\": \"X[2]\"\n    },\n    {\n      \"index\": 18,\n      \"name\": \"Y[2]\"\n    },\n    {\n      \"index\": 19,\n      \"name\": \"Z[2]\"\n    },\n    {\n      \"index\": 21,\n      \"name\": \"Scale[2]\"\n    },\n    {\n      \"index\": 22,\n      \"name\": \"BNpcBase[3]\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"BNpcBase\"\n      }\n    },\n    {\n      \"index\": 23,\n      \"name\": \"PartSlot[3]\"\n    },\n    {\n      \"index\": 28,\n      \"name\": \"X[3]\"\n    },\n    {\n      \"index\": 29,\n      \"name\": \"Y[3]\"\n    },\n    {\n      \"index\": 30,\n      \"name\": \"Z[3]\"\n    },\n    {\n      \"index\": 31,\n      \"name\": \"Scale[3]\"\n    },\n    {\n      \"index\": 33,\n      \"name\": \"BNpcBase[4]\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"BNpcBase\"\n      }\n    },\n    {\n      \"index\": 34,\n      \"name\": \"PartSlot[4]\"\n    },\n    {\n      \"index\": 39,\n      \"name\": \"X[4]\"\n    },\n    {\n      \"index\": 40,\n      \"name\": \"Y[4]\"\n    },\n    {\n      \"index\": 41,\n      \"name\": \"Z[4]\"\n    },\n    {\n      \"index\": 43,\n      \"name\": \"Scale[4]\"\n    },\n    {\n      \"index\": 44,\n      \"name\": \"BNpcBase[5]\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"BNpcBase\"\n      }\n    },\n    {\n      \"index\": 45,\n      \"name\": \"PartSlot[5]\"\n    },\n    {\n      \"index\": 50,\n      \"name\": \"X[5]\"\n    },\n    {\n      \"index\": 51,\n      \"name\": \"Y[5]\"\n    },\n    {\n      \"index\": 52,\n      \"name\": \"Z[5]\"\n    },\n    {\n      \"index\": 54,\n      \"name\": \"Scale[5]\"\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/BNpcState.json",
    "content": "{\n  \"sheet\": \"BNpcState\",\n  \"definitions\": [\n    {\n      \"name\": \"Slot\"\n    },\n    {\n      \"index\": 1,\n      \"name\": \"OverRay\"\n    },\n    {\n      \"index\": 4,\n      \"name\": \"Idle\"\n    },\n    {\n      \"index\": 5,\n      \"name\": \"Attribute[0]\"\n    },\n    {\n      \"index\": 6,\n      \"name\": \"AttributeFlag[0]\"\n    },\n    {\n      \"index\": 7,\n      \"name\": \"Attribute[1]\"\n    },\n    {\n      \"index\": 8,\n      \"name\": \"AttributeFlag[1]\"\n    },\n    {\n      \"index\": 9,\n      \"name\": \"Attribute[2]\"\n    },\n    {\n      \"index\": 10,\n      \"name\": \"AttributeFlag[2]\"\n    },\n    {\n      \"index\": 11,\n      \"name\": \"Scale\"\n    },\n    {\n      \"index\": 13,\n      \"name\": \"LoopTimeline\"\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/BacklightColor.json",
    "content": "{\n  \"sheet\": \"BacklightColor\",\n  \"defaultColumn\": \"Color\",\n  \"definitions\": [\n    {\n      \"name\": \"Color\",\n      \"converter\": {\n        \"type\": \"color\"\n      }\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/Ballista.json",
    "content": "{\n  \"sheet\": \"Ballista\",\n  \"definitions\": [\n    {\n      \"name\": \"BNPC\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"BNpcBase\"\n      }\n    },\n    {\n      \"index\": 1,\n      \"name\": \"Near\"\n    },\n    {\n      \"index\": 2,\n      \"name\": \"Far\"\n    },\n    {\n      \"index\": 3,\n      \"name\": \"Angle\"\n    },\n    {\n      \"index\": 4,\n      \"name\": \"Bullet\"\n    },\n    {\n      \"index\": 7,\n      \"name\": \"Action[0]\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"Action\"\n      }\n    },\n    {\n      \"index\": 8,\n      \"name\": \"Action[1]\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"Action\"\n      }\n    },\n    {\n      \"index\": 9,\n      \"name\": \"Action[2]\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"Action\"\n      }\n    },\n    {\n      \"index\": 10,\n      \"name\": \"Action[3]\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"Action\"\n      }\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/Balloon.json",
    "content": "{\n  \"sheet\": \"Balloon\",\n  \"defaultColumn\": \"Dialogue\",\n  \"definitions\": [\n    {\n      \"name\": \"Slowly\"\n    },\n    {\n      \"index\": 1,\n      \"name\": \"Dialogue\"\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/BannerBg.json",
    "content": "{\n  \"sheet\": \"BannerBg\",\n  \"defaultColumn\": \"Name\",\n  \"definitions\": [\n    {\n      \"name\": \"Image\",\n      \"converter\": {\n        \"type\": \"icon\"\n      }\n    },\n    {\n      \"index\": 1,\n      \"name\": \"Icon\",\n      \"converter\": {\n        \"type\": \"icon\"\n      }\n    },\n    {\n      \"index\": 3,\n      \"name\": \"UnlockCondition\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"BannerCondition\"\n      }\n    },\n    {\n      \"index\": 7,\n      \"name\": \"SortKey\"\n    },\n    {\n      \"index\": 8,\n      \"name\": \"Name\"\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/BannerCondition.json",
    "content": "{\n  \"sheet\": \"BannerCondition\",\n  \"definitions\": [\n    {\n      \"name\": \"UnlockType1\"\n    },\n    {\n      \"index\": 1,\n      \"type\": \"repeat\",\n      \"count\": 6,\n      \"definition\": {\n        \"name\": \"UnlockCriteria1\",\n        \"converter\": {\n          \"type\": \"complexlink\",\n          \"links\": [\n            {\n              \"when\": {\n                \"key\": \"UnlockType1\",\n                \"value\": 1\n              },\n              \"sheet\": \"Quest\"\n            },\n            {\n              \"when\": {\n                \"key\": \"UnlockType1\",\n                \"value\": 4\n              },\n              \"sheet\": \"ContentFinderCondition\"\n            },\n            {\n              \"when\": {\n                \"key\": \"UnlockType1\",\n                \"value\": 5\n              },\n              \"sheet\": \"Emote\"\n            },\n            {\n              \"when\": {\n                \"key\": \"UnlockType1\",\n                \"value\": 6\n              },\n              \"sheet\": \"Companion\"\n            }\n          ]\n        }\n      }\n    },\n    {\n      \"index\": 7,\n      \"name\": \"UnlockType2\"\n    },\n    {\n      \"index\": 8,\n      \"name\": \"UnlockCriteria2\",\n      \"converter\": {\n        \"type\": \"complexlink\",\n        \"links\": [\n          {\n            \"when\": {\n              \"key\": \"UnlockType2\",\n              \"value\": 2\n            },\n            \"sheet\": \"Quest\"\n          },\n          {\n            \"when\": {\n              \"key\": \"UnlockType2\",\n              \"value\": 4\n            },\n            \"sheet\": \"ENpcResident\"\n          },\n          {\n            \"when\": {\n              \"key\": \"UnlockType2\",\n              \"value\": 5\n            },\n            \"sheet\": \"Item\"\n          },\n          {\n            \"when\": {\n              \"key\": \"UnlockType2\",\n              \"value\": 6\n            },\n            \"sheet\": \"Item\"\n          },\n          {\n            \"when\": {\n              \"key\": \"UnlockType2\",\n              \"value\": 7\n            },\n            \"sheet\": \"Item\"\n          },\n          {\n            \"when\": {\n              \"key\": \"UnlockType2\",\n              \"value\": 8\n            },\n            \"sheet\": \"Item\"\n          },\n          {\n            \"when\": {\n              \"key\": \"UnlockType2\",\n              \"value\": 11\n            },\n            \"sheet\": \"Achievement\"\n          },\n          {\n            \"when\": {\n              \"key\": \"UnlockType2\",\n              \"value\": 12\n            },\n            \"sheet\": \"Item\"\n          },\n          {\n            \"when\": {\n              \"key\": \"UnlockType2\",\n              \"value\": 13\n            },\n            \"sheet\": \"ENpcBase\"\n          },\n          {\n            \"when\": {\n              \"key\": \"UnlockType2\",\n              \"value\": 21\n            },\n            \"sheet\": \"ENpcBase\"\n          }\n        ]\n      }\n    },\n    {\n      \"index\": 9,\n      \"name\": \"UnlockCriteria3\",\n      \"converter\": {\n        \"type\": \"complexlink\",\n        \"links\": [\n          {\n            \"when\": {\n              \"key\": \"UnlockType2\",\n              \"value\": 4\n            },\n            \"sheet\": \"Level\"\n          },\n          {\n            \"when\": {\n              \"key\": \"UnlockType2\",\n              \"value\": 13\n            },\n            \"sheet\": \"Level\"\n          },\n          {\n            \"when\": {\n              \"key\": \"UnlockType2\",\n              \"value\": 21\n            },\n            \"sheet\": \"Level\"\n          }\n        ]\n      }\n    },\n    {\n      \"index\": 10,\n      \"name\": \"UnlockCriteria4\",\n      \"converter\": {\n        \"type\": \"complexlink\",\n        \"links\": [\n          {\n            \"when\": {\n              \"key\": \"UnlockType2\",\n              \"value\": 4\n            },\n            \"sheet\": \"Item\"\n          },\n          {\n            \"when\": {\n              \"key\": \"UnlockType2\",\n              \"value\": 21\n            },\n            \"sheet\": \"Item\"\n          }\n        ]\n      }\n    },\n    {\n      \"index\": 11,\n      \"name\": \"PrerequisiteType\"\n    },\n    {\n      \"index\": 12,\n      \"name\": \"Prerequisite\",\n      \"converter\": {\n        \"type\": \"complexlink\",\n        \"links\": [\n          {\n            \"when\": {\n              \"key\": \"PrerequisiteType\",\n              \"value\": 1\n            },\n            \"sheet\": \"Quest\"\n          },\n          {\n            \"when\": {\n              \"key\": \"PrerequisiteType\",\n              \"value\": 3\n            },\n            \"sheet\": \"ContentFinderCondition\"\n          },\n          {\n            \"when\": {\n              \"key\": \"PrerequisiteType\",\n              \"value\": 4\n            },\n            \"sheet\": \"ContentFinderCondition\"\n          }\n        ]\n      }\n    },\n    {\n      \"index\": 13,\n      \"name\": \"UnlockHint\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"BannerObtainHintType\"\n      }\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/BannerDecoration.json",
    "content": "{\n  \"sheet\": \"BannerDecoration\",\n  \"defaultColumn\": \"Name\",\n  \"definitions\": [\n    {\n      \"name\": \"Image\",\n      \"converter\": {\n        \"type\": \"icon\"\n      }\n    },\n    {\n      \"index\": 1,\n      \"name\": \"Icon\",\n      \"converter\": {\n        \"type\": \"icon\"\n      }\n    },\n    {\n      \"index\": 3,\n      \"name\": \"UnlockCondition\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"BannerCondition\"\n      }\n    },\n    {\n      \"index\": 6,\n      \"name\": \"SortKey\"\n    },\n    {\n      \"index\": 7,\n      \"name\": \"Name\"\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/BannerDesignPreset.json",
    "content": "{\n  \"sheet\": \"BannerDesignPreset\",\n  \"defaultColumn\": \"Name\",\n  \"definitions\": [\n    {\n      \"name\": \"Background\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"BannerBg\"\n      }\n    },\n    {\n      \"index\": 1,\n      \"name\": \"Frame\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"BannerFrame\"\n      }\n    },\n    {\n      \"index\": 2,\n      \"name\": \"Decoration\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"BannerDecoration\"\n      }\n    },\n    {\n      \"index\": 3,\n      \"name\": \"SortKey\"\n    },\n    {\n      \"index\": 4,\n      \"name\": \"Name\"\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/BannerFacial.json",
    "content": "{\n  \"sheet\": \"BannerFacial\",\n  \"definitions\": [\n    {\n      \"name\": \"Emote\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"Emote\"\n      }\n    },\n    {\n      \"index\": 1,\n      \"name\": \"UnlockCondition\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"BannerCondition\"\n      }\n    },\n    {\n      \"index\": 4,\n      \"name\": \"SortKey\"\n    }\n  ]\n}\n"
  },
  {
    "path": "SaintCoinach/Definitions/BannerFrame.json",
    "content": "{\n  \"sheet\": \"BannerFrame\",\n  \"defaultColumn\": \"Name\",\n  \"definitions\": [\n    {\n      \"name\": \"Image\",\n      \"converter\": {\n        \"type\": \"icon\"\n      }\n    },\n    {\n      \"index\": 1,\n      \"name\": \"Icon\",\n      \"converter\": {\n        \"type\": \"icon\"\n      }\n    },\n    {\n      \"index\": 3,\n      \"name\": \"UnlockCondition\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"BannerCondition\"\n      }\n    },\n    {\n      \"index\": 6,\n      \"name\": \"SortKey\"\n    },\n    {\n      \"index\": 7,\n      \"name\": \"Name\"\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/BannerObtainHintType.json",
    "content": "{\n  \"sheet\": \"BannerObtainHintType\",\n  \"defaultColumn\": \"Text\",\n  \"definitions\": [\n    {\n      \"name\": \"Text\"\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/BannerPreset.json",
    "content": "{\n  \"sheet\": \"BannerPreset\",\n  \"definitions\": []\n}"
  },
  {
    "path": "SaintCoinach/Definitions/BannerTimeline.json",
    "content": "{\n  \"sheet\": \"BannerTimeline\",\n  \"definitions\": [\n    {\n      \"name\": \"Type\"\n    },\n    {\n      \"index\": 1,\n      \"name\": \"AdditionalData\",\n      \"converter\": {\n        \"type\": \"complexlink\",\n        \"links\": [\n          {\n            \"when\": {\n              \"key\": \"Type\",\n              \"value\": 2\n            },\n            \"sheet\": \"Action\"\n          },\n          {\n            \"when\": {\n              \"key\": \"Type\",\n              \"value\": 11\n            },\n            \"sheet\": \"Emote\"\n          },\n          {\n            \"when\": {\n              \"key\": \"Type\",\n              \"value\": 20\n            },\n            \"sheet\": \"ActionTimeline\"\n          }\n        ]\n      }\n    },\n    {\n      \"index\": 2,\n      \"name\": \"AcceptClassJobCategory\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"ClassJobCategory\"\n      }\n    },\n    {\n      \"index\": 3,\n      \"name\": \"Category\"\n    },\n    {\n      \"index\": 4,\n      \"name\": \"UnlockCondition\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"BannerCondition\"\n      }\n    },\n    {\n      \"index\": 7,\n      \"name\": \"SortKey\"\n    },\n    {\n      \"index\": 8,\n      \"name\": \"Icon\",\n      \"converter\": {\n        \"type\": \"icon\"\n      }\n    },\n    {\n      \"index\": 9,\n      \"name\": \"Name\"\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/BaseParam.json",
    "content": "{\n  \"sheet\": \"BaseParam\",\n  \"defaultColumn\": \"Name\",\n  \"definitions\": [\n    {\n      \"name\": \"PacketIndex\"\n    },\n    {\n      \"index\": 1,\n      \"name\": \"Name\"\n    },\n    {\n      \"index\": 2,\n      \"name\": \"Description\"\n    },\n    {\n      \"index\": 3,\n      \"name\": \"OrderPriority\"\n    },\n    {\n      \"index\": 4,\n      \"name\": \"1HWpn%\"\n    },\n    {\n      \"index\": 5,\n      \"name\": \"OH<%>\"\n    },\n    {\n      \"index\": 6,\n      \"name\": \"Head<%>\"\n    },\n    {\n      \"index\": 7,\n      \"name\": \"Chest<%>\"\n    },\n    {\n      \"index\": 8,\n      \"name\": \"Hands<%>\"\n    },\n    {\n      \"index\": 9,\n      \"name\": \"Waist<%>\"\n    },\n    {\n      \"index\": 10,\n      \"name\": \"Legs<%>\"\n    },\n    {\n      \"index\": 11,\n      \"name\": \"Feet<%>\"\n    },\n    {\n      \"index\": 12,\n      \"name\": \"Earring<%>\"\n    },\n    {\n      \"index\": 13,\n      \"name\": \"Necklace<%>\"\n    },\n    {\n      \"index\": 14,\n      \"name\": \"Bracelet<%>\"\n    },\n    {\n      \"index\": 15,\n      \"name\": \"Ring<%>\"\n    },\n    {\n      \"index\": 16,\n      \"name\": \"2HWpn<%>\"\n    },\n    {\n      \"index\": 17,\n      \"name\": \"UnderArmor<%>\"\n    },\n    {\n      \"index\": 18,\n      \"name\": \"ChestHead<%>\"\n    },\n    {\n      \"index\": 19,\n      \"name\": \"ChestHeadLegsFeet<%>\"\n    },\n    {\n      \"index\": 21,\n      \"name\": \"LegsFeet<%>\"\n    },\n    {\n      \"index\": 22,\n      \"name\": \"HeadChestHandsLegsFeet<%>\"\n    },\n    {\n      \"index\": 23,\n      \"name\": \"ChestLegsGloves<%>\"\n    },\n    {\n      \"index\": 24,\n      \"name\": \"ChestLegsFeet<%>\"\n    },\n    {\n      \"index\": 26,\n      \"type\": \"repeat\",\n      \"count\": 13,\n      \"definition\": {\n        \"name\": \"MeldParam\"\n      }\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/BattleLeve.json",
    "content": "{\n  \"sheet\": \"BattleLeve\",\n  \"definitions\": [\n    {\n      \"type\": \"repeat\",\n      \"count\": 8,\n      \"definition\": {\n        \"name\": \"Time\"\n      }\n    },\n    {\n      \"index\": 8,\n      \"type\": \"repeat\",\n      \"count\": 8,\n      \"definition\": {\n        \"name\": \"BaseID\",\n        \"converter\": {\n          \"type\": \"multiref\",\n          \"targets\": [\n            \"EventItem\",\n            \"BNpcBase\"\n          ]\n        }\n      }\n    },\n    {\n      \"index\": 16,\n      \"type\": \"repeat\",\n      \"count\": 8,\n      \"definition\": {\n        \"name\": \"EnemyLevel\"\n      }\n    },\n    {\n      \"index\": 24,\n      \"type\": \"repeat\",\n      \"count\": 8,\n      \"definition\": {\n        \"name\": \"BNpcName\",\n        \"converter\": {\n          \"type\": \"link\",\n          \"target\": \"BNpcName\"\n        }\n      }\n    },\n    {\n      \"index\": 32,\n      \"type\": \"repeat\",\n      \"count\": 8,\n      \"definition\": {\n        \"name\": \"ItemsInvolved\",\n        \"converter\": {\n          \"type\": \"link\",\n          \"target\": \"EventItem\"\n        }\n      }\n    },\n    {\n      \"index\": 40,\n      \"type\": \"repeat\",\n      \"count\": 8,\n      \"definition\": {\n        \"name\": \"ItemsInvolvedQty\"\n      }\n    },\n    {\n      \"index\": 48,\n      \"type\": \"repeat\",\n      \"count\": 8,\n      \"definition\": {\n        \"name\": \"ItemDropRate\"\n      }\n    },\n    {\n      \"index\": 56,\n      \"type\": \"repeat\",\n      \"count\": 8,\n      \"definition\": {\n        \"name\": \"ToDoNumberInvolved\"\n      }\n    },\n    {\n      \"index\": 64,\n      \"type\": \"repeat\",\n      \"count\": 5,\n      \"definition\": {\n        \"type\": \"group\",\n        \"members\": [\n          {\n            \"type\": \"repeat\",\n            \"count\": 8,\n            \"definition\": {\n              \"name\": \"ToDoParam\"\n            }\n          }\n        ]\n      }\n    },\n    {\n      \"index\": 104,\n      \"type\": \"repeat\",\n      \"count\": 8,\n      \"definition\": {\n        \"type\": \"group\",\n        \"members\": [\n          {\n            \"type\": \"repeat\",\n            \"count\": 8,\n            \"definition\": {\n              \"name\": \"NumOfAppearance\"\n            }\n          }\n        ]\n      }\n    },\n    {\n      \"index\": 168,\n      \"type\": \"repeat\",\n      \"count\": 8,\n      \"definition\": {\n        \"name\": \"ToDoSequence\"\n      }\n    },\n    {\n      \"index\": 176,\n      \"name\": \"Rule\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"BattleLeveRule\"\n      }\n    },\n    {\n      \"index\": 177,\n      \"name\": \"Varient\"\n    },\n    {\n      \"index\": 178,\n      \"name\": \"Objective[0]\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"LeveString\"\n      }\n    },\n    {\n      \"index\": 179,\n      \"name\": \"Objective[1]\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"LeveString\"\n      }\n    },\n    {\n      \"index\": 180,\n      \"name\": \"Objective[2]\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"LeveString\"\n      }\n    },\n    {\n      \"index\": 181,\n      \"name\": \"Help[0]\"\n    },\n    {\n      \"index\": 182,\n      \"name\": \"Help[1]\"\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/BattleLeveRule.json",
    "content": "{\n  \"sheet\": \"BattleLeveRule\",\n  \"defaultColumn\": \"Rule\",\n  \"definitions\": [\n    {\n      \"name\": \"Rule\"\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/BeastRankBonus.json",
    "content": "{\n  \"sheet\": \"BeastRankBonus\",\n  \"definitions\": [\n    {\n      \"name\": \"Neutral\"\n    },\n    {\n      \"index\": 1,\n      \"name\": \"Recognized\"\n    },\n    {\n      \"index\": 2,\n      \"name\": \"Friendly\"\n    },\n    {\n      \"index\": 3,\n      \"name\": \"Trusted\"\n    },\n    {\n      \"index\": 4,\n      \"name\": \"Respected\"\n    },\n    {\n      \"index\": 5,\n      \"name\": \"Honored\"\n    },\n    {\n      \"index\": 6,\n      \"name\": \"Sworn\"\n    },\n    {\n      \"index\": 7,\n      \"name\": \"Allied/Bloodsworn\"\n    },\n    {\n      \"index\": 8,\n      \"name\": \"Item\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"Item\"\n      }\n    },\n    {\n      \"index\": 9,\n      \"type\": \"repeat\",\n      \"count\": 8,\n      \"definition\": {\n        \"name\": \"Item{Quantity}\"\n      }\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/BeastReputationRank.json",
    "content": "{\n  \"sheet\": \"BeastReputationRank\",\n  \"defaultColumn\": \"Name\",\n  \"definitions\": [\n    {\n      \"name\": \"RequiredReputation\"\n    },\n    {\n      \"index\": 1,\n      \"name\": \"Name\"\n    },\n    {\n      \"index\": 2,\n      \"name\": \"AlliedNames\"\n    },\n    {\n      \"index\": 3,\n      \"name\": \"Color\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"UIColor\"\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "SaintCoinach/Definitions/BeastTribe.json",
    "content": "{\n  \"sheet\": \"BeastTribe\",\n  \"defaultColumn\": \"Name\",\n  \"definitions\": [\n    {\n      \"index\": 1,\n      \"name\": \"MinLevel\"\n    },\n    {\n      \"index\": 2,\n      \"name\": \"BeastRankBonus\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"BeastRankBonus\"\n      }\n    },\n    {\n      \"index\": 3,\n      \"name\": \"Icon{Reputation}\",\n      \"converter\": {\n        \"type\": \"icon\"\n      }\n    },\n    {\n      \"index\": 4,\n      \"name\": \"Icon\",\n      \"converter\": {\n        \"type\": \"icon\"\n      }\n    },\n    {\n      \"index\": 5,\n      \"name\": \"MaxRank\"\n    },\n    {\n      \"index\": 6,\n      \"name\": \"Expansion\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"ExVersion\"\n      }\n    },\n    {\n      \"index\": 8,\n      \"name\": \"CurrencyItem\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"Item\"\n      }\n    },\n    {\n      \"index\": 9,\n      \"name\": \"DisplayOrder\"\n    },\n    {\n      \"index\": 10,\n      \"name\": \"Name\"\n    },\n    {\n      \"index\": 11,\n      \"name\": \"Adjective\"\n    },\n    {\n      \"index\": 12,\n      \"name\": \"Plural\"\n    },\n    {\n      \"index\": 13,\n      \"name\": \"PossessivePronoun\"\n    },\n    {\n      \"index\": 14,\n      \"name\": \"StartsWithVowel\"\n    },\n    {\n      \"index\": 15,\n      \"name\": \"Pronoun\"\n    },\n    {\n      \"index\": 16,\n      \"name\": \"Article\"\n    },\n    {\n      \"index\": 17,\n      \"name\": \"DEF\"\n    },\n    {\n      \"index\": 18,\n      \"name\": \"Name{Relation}\"\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/Behavior.json",
    "content": "{\n  \"sheet\": \"Behavior\",\n  \"definitions\": [\n    {\n      \"index\": 2,\n      \"name\": \"Condition[0]Target\"\n    },\n    {\n      \"index\": 3,\n      \"name\": \"Condition[0]Type\"\n    },\n    {\n      \"index\": 8,\n      \"name\": \"Balloon\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"Balloon\"\n      }\n    },\n    {\n      \"index\": 9,\n      \"name\": \"Condition[1]Target\"\n    },\n    {\n      \"index\": 10,\n      \"name\": \"Condition[1]Type\"\n    },\n    {\n      \"index\": 11,\n      \"name\": \"ContentArgument[0]\"\n    },\n    {\n      \"index\": 12,\n      \"name\": \"ContentArgument[1]\"\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/BehaviorPath.json",
    "content": "{\n  \"sheet\": \"BehaviorPath\",\n  \"definitions\": [\n    {\n      \"name\": \"IsTurnTransition\"\n    },\n    {\n      \"index\": 1,\n      \"name\": \"IsFadeOut\"\n    },\n    {\n      \"index\": 2,\n      \"name\": \"IsFadeIn\"\n    },\n    {\n      \"index\": 3,\n      \"name\": \"IsWalking\"\n    },\n    {\n      \"index\": 5,\n      \"name\": \"Speed\"\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/BenchmarkOverrideEquipment.json",
    "content": "{\n  \"sheet\": \"BenchmarkOverrideEquipment\",\n  \"definitions\": [\n    {\n      \"index\": 4,\n      \"name\": \"Model{MainHand}\"\n    },\n    {\n      \"index\": 5,\n      \"name\": \"Dye{MainHand}\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"Stain\"\n      }\n    },\n    {\n      \"index\": 6,\n      \"name\": \"Model{OffHand}\"\n    },\n    {\n      \"index\": 7,\n      \"name\": \"Dye{OffHand}\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"Stain\"\n      }\n    },\n    {\n      \"index\": 10,\n      \"name\": \"Model{Head}\"\n    },\n    {\n      \"index\": 11,\n      \"name\": \"Dye{Head}\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"Stain\"\n      }\n    },\n    {\n      \"index\": 12,\n      \"name\": \"Model{Body}\"\n    },\n    {\n      \"index\": 13,\n      \"name\": \"Dye{Body}\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"Stain\"\n      }\n    },\n    {\n      \"index\": 14,\n      \"name\": \"Model{Hands}\"\n    },\n    {\n      \"index\": 15,\n      \"name\": \"Dye{Hands}\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"Stain\"\n      }\n    },\n    {\n      \"index\": 16,\n      \"name\": \"Model{Legs}\"\n    },\n    {\n      \"index\": 17,\n      \"name\": \"Dye{Legs}\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"Stain\"\n      }\n    },\n    {\n      \"index\": 18,\n      \"name\": \"Model{Feet}\"\n    },\n    {\n      \"index\": 19,\n      \"name\": \"Dye{Feet}\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"Stain\"\n      }\n    },\n    {\n      \"index\": 20,\n      \"name\": \"Model{Ears}\"\n    },\n    {\n      \"index\": 21,\n      \"name\": \"Dye{Ears}\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"Stain\"\n      }\n    },\n    {\n      \"index\": 22,\n      \"name\": \"Model{Neck}\"\n    },\n    {\n      \"index\": 23,\n      \"name\": \"Dye{Neck}\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"Stain\"\n      }\n    },\n    {\n      \"index\": 24,\n      \"name\": \"Model{Wrists}\"\n    },\n    {\n      \"index\": 25,\n      \"name\": \"Dye{Wrists}\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"Stain\"\n      }\n    },\n    {\n      \"index\": 26,\n      \"name\": \"Model{LeftRing}\"\n    },\n    {\n      \"index\": 27,\n      \"name\": \"Dye{LeftRing}\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"Stain\"\n      }\n    },\n    {\n      \"index\": 28,\n      \"name\": \"Model{RightRing}\"\n    },\n    {\n      \"index\": 29,\n      \"name\": \"Dye{RightRing}\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"Stain\"\n      }\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/Booster.json",
    "content": "{\n  \"sheet\": \"Booster\",\n  \"definitions\": []\n}"
  },
  {
    "path": "SaintCoinach/Definitions/Buddy.json",
    "content": "{\n  \"sheet\": \"Buddy\",\n  \"definitions\": [\n    {\n      \"name\": \"Base\"\n    },\n    {\n      \"index\": 1,\n      \"name\": \"QuestRequirement{2}\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"Quest\"\n      }\n    },\n    {\n      \"index\": 2,\n      \"name\": \"QuestRequirement{1}\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"Quest\"\n      }\n    },\n    {\n      \"index\": 3,\n      \"name\": \"BaseEquip\"\n    },\n    {\n      \"index\": 4,\n      \"name\": \"SoundEffect{4}\"\n    },\n    {\n      \"index\": 5,\n      \"name\": \"SoundEffect{3}\"\n    },\n    {\n      \"index\": 6,\n      \"name\": \"SoundEffect{2}\"\n    },\n    {\n      \"index\": 7,\n      \"name\": \"SoundEffect{1}\"\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/BuddyAction.json",
    "content": "{\n  \"sheet\": \"BuddyAction\",\n  \"defaultColumn\": \"Name\",\n  \"definitions\": [\n    {\n      \"name\": \"Name\"\n    },\n    {\n      \"index\": 1,\n      \"name\": \"Description\"\n    },\n    {\n      \"index\": 2,\n      \"name\": \"Icon\",\n      \"converter\": {\n        \"type\": \"icon\"\n      }\n    },\n    {\n      \"index\": 3,\n      \"name\": \"Icon{Status}\",\n      \"converter\": {\n        \"type\": \"icon\"\n      }\n    },\n    {\n      \"index\": 4,\n      \"name\": \"Reward\"\n    },\n    {\n      \"index\": 5,\n      \"name\": \"Sort\"\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/BuddyEquip.json",
    "content": "{\n  \"sheet\": \"BuddyEquip\",\n  \"defaultColumn\": \"Name\",\n  \"definitions\": [\n    {\n      \"name\": \"Singular\"\n    },\n    {\n      \"index\": 1,\n      \"name\": \"Adjective\"\n    },\n    {\n      \"index\": 2,\n      \"name\": \"Plural\"\n    },\n    {\n      \"index\": 3,\n      \"name\": \"PossessivePronoun\"\n    },\n    {\n      \"index\": 4,\n      \"name\": \"StartsWithVowel\"\n    },\n    {\n      \"index\": 6,\n      \"name\": \"Pronoun\"\n    },\n    {\n      \"index\": 7,\n      \"name\": \"Article\"\n    },\n    {\n      \"index\": 8,\n      \"name\": \"Name\"\n    },\n    {\n      \"index\": 9,\n      \"name\": \"Model{Top}\"\n    },\n    {\n      \"index\": 10,\n      \"name\": \"Model{Body}\"\n    },\n    {\n      \"index\": 11,\n      \"name\": \"Model{Legs}\"\n    },\n    {\n      \"index\": 12,\n      \"name\": \"GrandCompany\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"GrandCompany\"\n      }\n    },\n    {\n      \"index\": 13,\n      \"name\": \"Icon{Head}\",\n      \"converter\": {\n        \"type\": \"icon\"\n      }\n    },\n    {\n      \"index\": 14,\n      \"name\": \"Icon{Body}\",\n      \"converter\": {\n        \"type\": \"icon\"\n      }\n    },\n    {\n      \"index\": 15,\n      \"name\": \"Icon{Legs}\",\n      \"converter\": {\n        \"type\": \"icon\"\n      }\n    },\n    {\n      \"index\": 16,\n      \"name\": \"Order\"\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/BuddyItem.json",
    "content": "{\n  \"sheet\": \"BuddyItem\",\n  \"definitions\": [\n    {\n      \"name\": \"Item\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"Item\"\n      }\n    },\n    {\n      \"index\": 1,\n      \"name\": \"UseField\"\n    },\n    {\n      \"index\": 2,\n      \"name\": \"UseTraining\"\n    },\n    {\n      \"index\": 4,\n      \"name\": \"Status\"\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/BuddyRank.json",
    "content": "{\n  \"sheet\": \"BuddyRank\",\n  \"definitions\": [\n    {\n      \"name\": \"ExpRequired\"\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/BuddySkill.json",
    "content": "{\n  \"sheet\": \"BuddySkill\",\n  \"definitions\": [\n    {\n      \"name\": \"BuddyLevel\"\n    },\n    {\n      \"index\": 1,\n      \"name\": \"IsActive\"\n    },\n    {\n      \"index\": 2,\n      \"name\": \"Defender\",\n      \"converter\": {\n        \"type\": \"multiref\",\n        \"targets\": [\n          \"Trait\",\n          \"Action\"\n        ]\n      }\n    },\n    {\n      \"index\": 3,\n      \"name\": \"Attacker\",\n      \"converter\": {\n        \"type\": \"multiref\",\n        \"targets\": [\n          \"Trait\",\n          \"Action\"\n        ]\n      }\n    },\n    {\n      \"index\": 4,\n      \"name\": \"Healer\",\n      \"converter\": {\n        \"type\": \"multiref\",\n        \"targets\": [\n          \"Trait\",\n          \"Action\"\n        ]\n      }\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/CSBonusContent.json",
    "content": "{\n  \"sheet\": \"CSBonusContent\",\n  \"defaultColumn\": \"Content[0]\",\n  \"definitions\": [\n  {\n      \"name\": \"ContentType\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"CSBonusContentType\"\n      }\n    },\n    {\n      \"index\": 1,\n      \"type\": \"repeat\",\n      \"count\": 2,\n      \"definition\": {\n        \"name\": \"Content\",\n        \"converter\": {\n          \"type\": \"link\",\n          \"target\": \"CSBonusContentIdentifier\"\n        }\n      }\n    },\n    {\n      \"index\": 3,\n      \"type\": \"repeat\",\n      \"count\": 6,\n      \"definition\": {\n        \"name\": \"Score\"\n      }\n    },\n    {\n      \"index\": 9,\n      \"type\": \"repeat\",\n      \"count\": 5,\n      \"definition\": {\n        \"name\": \"RewardCount\"\n      }\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/CSBonusContentIdentifier.json",
    "content": "{\n  \"sheet\": \"CSBonusContentIdentifier\",\n  \"defaultColumn\": \"Content\",\n  \"definitions\": [\n    {\n      \"name\": \"ContentLinkType\"\n    },\n    {\n      \"index\": 1,\n      \"name\": \"Content\",\n      \"converter\": {\n        \"type\": \"complexlink\",\n        \"links\": [\n          {\n            \"when\": {\n              \"key\": \"ContentLinkType\",\n              \"value\": 1\n            },\n            \"sheet\": \"InstanceContent\"\n          },\n          {\n            \"when\": {\n              \"key\": \"ContentLinkType\",\n              \"value\": 2\n            },\n            \"sheet\": \"GoldSaucerContent\"\n          },\n          {\n            \"when\": {\n              \"key\": \"ContentLinkType\",\n              \"value\": 3\n            },\n            \"sheet\": \"TerritoryType\"\n          },\n          {\n            \"when\": {\n              \"key\": \"ContentLinkType\",\n              \"value\": 4\n            },\n            \"sheet\": \"MobHuntOrderType\"\n          },\n          {\n            \"when\": {\n              \"key\": \"ContentLinkType\",\n              \"value\": 5\n            },\n            \"sheet\": \"TreasureHuntRank\"\n          },\n          {\n            \"when\": {\n              \"key\": \"ContentLinkType\",\n              \"value\": 6\n            },\n            \"sheet\": \"FishParameter\"\n          },\n          {\n            \"when\": {\n              \"key\": \"ContentLinkType\",\n              \"value\": 7\n            },\n            \"sheet\": \"ENpcResident\"\n          }\n        ]\n      }\n    },\n    {\n      \"index\": 3,\n      \"type\": \"repeat\",\n      \"count\": 3,\n      \"definition\": {\n        \"name\": \"UnlockQuest\",\n        \"converter\": {\n          \"type\": \"link\",\n          \"target\": \"Quest\"\n        }\n      }\n    },\n    {\n      \"index\": 7,\n      \"name\": \"Map\",\n      \"converter\": {\n        \"type\": \"complexlink\",\n        \"links\": [\n          {\n            \"when\": {\n              \"key\": \"ContentLinkType\",\n              \"value\": 1\n            },\n            \"sheet\": \"Level\"\n          },\n          {\n            \"when\": {\n              \"key\": \"ContentLinkType\",\n              \"value\": 2\n            },\n            \"sheet\": \"Level\"\n          },\n          {\n            \"when\": {\n              \"key\": \"ContentLinkType\",\n              \"value\": 3\n            },\n            \"sheet\": \"Fate\"\n          },\n          {\n            \"when\": {\n              \"key\": \"ContentLinkType\",\n              \"value\": 4\n            },\n            \"sheet\": \"Level\"\n          },\n          {\n            \"when\": {\n              \"key\": \"ContentLinkType\",\n              \"value\": 7\n            },\n            \"sheet\": \"Level\"\n          }\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "SaintCoinach/Definitions/CSBonusContentType.json",
    "content": "{\n  \"sheet\": \"CSBonusContentType\",\n  \"defaultColumn\": \"ContentType\",\n  \"definitions\": [\n    {\n      \"name\": \"ContentType\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"ContentType\"\n      }\n    },\n    {\n      \"index\": 1,\n      \"type\": \"repeat\",\n      \"count\": 4,\n      \"definition\": {\n        \"name\": \"Text\",\n        \"converter\": {\n          \"type\": \"link\",\n          \"target\": \"Addon\"\n        }\n      }\n    },\n    {\n      \"index\": 5,\n      \"name\": \"Image\",\n      \"converter\": {\n        \"type\": \"icon\"\n      }\n    },\n    {\n      \"index\": 7,\n      \"name\": \"UnlockQuest\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"Quest\"\n      }\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/CSBonusMission.json",
    "content": "{\n  \"sheet\": \"CSBonusMission\",\n  \"definitions\": [\n    {\n      \"index\": 0,\n      \"type\": \"repeat\",\n      \"count\": 2,\n      \"definition\": {\n        \"name\": \"Content\",\n        \"converter\": {\n          \"type\": \"link\",\n          \"target\": \"CSBonusContent\"\n        }\n      }\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/CSBonusMissionType.json",
    "content": "{\n  \"sheet\": \"CSBonusMissionType\",\n  \"definitions\": []\n}"
  },
  {
    "path": "SaintCoinach/Definitions/CSBonusSeason.json",
    "content": "{\n  \"sheet\": \"CSBonusSeason\",\n  \"definitions\": [\n    {\n      \"index\": 5,\n      \"name\": \"Item\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"Item\"\n      }\n    },\n    {\n      \"index\": 6,\n      \"type\": \"repeat\",\n      \"count\": 4,\n      \"definition\": {\n        \"name\": \"Category\"\n      }\n    },\n    {\n      \"index\": 10,\n      \"type\": \"repeat\",\n      \"count\": 2,\n      \"definition\": {\n        \"name\": \"Text\",\n        \"converter\": {\n          \"type\": \"link\",\n          \"target\": \"CSBonusTextData\"\n        }\n      }\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/CSBonusTextData.json",
    "content": "{\n  \"sheet\": \"CSBonusTextData\",\n  \"defaultColumn\": \"Text\",\n  \"definitions\": [\n    {\n      \"name\": \"Text\"\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/Cabinet.json",
    "content": "{\n  \"sheet\": \"Cabinet\",\n  \"defaultColumn\": \"Item\",\n  \"definitions\": [\n    {\n      \"name\": \"Item\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"Item\"\n      }\n    },\n    {\n      \"index\": 1,\n      \"name\": \"Order\"\n    },\n    {\n      \"index\": 2,\n      \"name\": \"Category\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"CabinetCategory\"\n      }\n    },\n    {\n      \"index\": 3,\n      \"name\": \"SortKey\"\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/CabinetCategory.json",
    "content": "{\n  \"sheet\": \"CabinetCategory\",\n  \"defaultColumn\": \"Category\",\n  \"definitions\": [\n    {\n      \"name\": \"MenuOrder\"\n    },\n    {\n      \"index\": 1,\n      \"name\": \"HideOrder\"\n    },\n    {\n      \"index\": 2,\n      \"name\": \"Icon\",\n      \"converter\": {\n        \"type\": \"icon\"\n      }\n    },\n    {\n      \"index\": 3,\n      \"name\": \"Category\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"Addon\"\n      }\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/Calendar.json",
    "content": "{\n  \"sheet\": \"Calendar\",\n  \"definitions\": [\n    {\n      \"type\": \"repeat\",\n      \"count\": 32,\n      \"definition\": {\n        \"name\": \"Month\"\n      }\n    },\n    {\n      \"index\": 32,\n      \"type\": \"repeat\",\n      \"count\": 32,\n      \"definition\": {\n        \"name\": \"Day\"\n      }\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/Carry.json",
    "content": "{\n  \"sheet\": \"Carry\",\n  \"definitions\": [\n    {\n      \"name\": \"Model\"\n    },\n    {\n      \"index\": 1,\n      \"name\": \"Timeline\"\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/Channeling.json",
    "content": "{\n  \"sheet\": \"Channeling\",\n  \"definitions\": [\n    {\n      \"name\": \"File\"\n    },\n    {\n      \"index\": 1,\n      \"name\": \"WidthScale\"\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/CharaCardBase.json",
    "content": "{\n  \"sheet\": \"CharaCardBase\",\n  \"defaultColumn\": \"Name\",\n  \"definitions\": [\n    {\n      \"name\": \"Image\",\n      \"converter\": {\n        \"type\": \"icon\"\n      }\n    },\n    {\n      \"index\": 1,\n      \"name\": \"FontColor\"\n    },\n    {\n      \"index\": 5,\n      \"name\": \"UnlockCondition\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"BannerCondition\"\n      }\n    },\n    {\n      \"index\": 9,\n      \"name\": \"SortKey\"\n    },\n    {\n      \"index\": 10,\n      \"name\": \"Name\"\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/CharaCardDecoration.json",
    "content": "{\n  \"sheet\": \"CharaCardDecoration\",\n  \"defaultColumn\": \"Name\",\n  \"definitions\": [\n    {\n      \"name\": \"Category\"\n    },\n    {\n      \"index\": 1,\n      \"name\": \"SubType\"\n    },\n    {\n      \"index\": 2,\n      \"name\": \"Image\",\n      \"converter\": {\n        \"type\": \"icon\"\n      }\n    },\n    {\n      \"index\": 4,\n      \"name\": \"UnlockCondition\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"BannerCondition\"\n      }\n    },\n    {\n      \"index\": 8,\n      \"name\": \"SortKey\"\n    },\n    {\n      \"index\": 9,\n      \"name\": \"Name\"\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/CharaCardDesignCategory.json",
    "content": "{\n  \"sheet\": \"CharaCardDesignCategory\",\n  \"definitions\": []\n}"
  },
  {
    "path": "SaintCoinach/Definitions/CharaCardDesignPreset.json",
    "content": "{\n  \"sheet\": \"CharaCardDesignPreset\",\n  \"defaultColumn\": \"Name\",\n  \"definitions\": [\n    {\n      \"name\": \"BasePlate\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"CharaCardBase\"\n      }\n    },\n    {\n      \"index\": 1,\n      \"name\": \"TopBorder\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"CharaCardHeader\"\n      }\n    },\n    {\n      \"index\": 2,\n      \"name\": \"BottomBorder\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"CharaCardHeader\"\n      }\n    },\n    {\n      \"index\": 3,\n      \"name\": \"Backing\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"CharaCardDecoration\"\n      }\n    },\n    {\n      \"index\": 4,\n      \"name\": \"PatternOverlay\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"CharaCardDecoration\"\n      }\n    },\n    {\n      \"index\": 5,\n      \"name\": \"PortraitFrame\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"CharaCardDecoration\"\n      }\n    },\n    {\n      \"index\": 6,\n      \"name\": \"PlateFrame\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"CharaCardDecoration\"\n      }\n    },\n    {\n      \"index\": 7,\n      \"name\": \"Accent\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"CharaCardDecoration\"\n      }\n    },\n    {\n      \"index\": 8,\n      \"name\": \"SortKey\"\n    },\n    {\n      \"index\": 9,\n      \"name\": \"Name\"\n    }\n  ]\n}\n"
  },
  {
    "path": "SaintCoinach/Definitions/CharaCardDesignType.json",
    "content": "{\n  \"sheet\": \"CharaCardDesignType\",\n  \"definitions\": []\n}"
  },
  {
    "path": "SaintCoinach/Definitions/CharaCardHeader.json",
    "content": "{\n  \"sheet\": \"CharaCardHeader\",\n  \"defaultColumn\": \"Name\",\n  \"definitions\": [\n    {\n      \"name\": \"TopImage\",\n      \"converter\": {\n        \"type\": \"icon\"\n      }\n    },\n    {\n      \"index\": 1,\n      \"name\": \"BottomImage\",\n      \"converter\": {\n        \"type\": \"icon\"\n      }\n    },\n    {\n      \"index\": 2,\n      \"name\": \"FontColor\"\n    },\n    {\n      \"index\": 6,\n      \"name\": \"UnlockCondition\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"BannerCondition\"\n      }\n    },\n    {\n      \"index\": 10,\n      \"name\": \"SortKey\"\n    },\n    {\n      \"index\": 11,\n      \"name\": \"Name\"\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/CharaCardPlayStyle.json",
    "content": "{\n  \"sheet\": \"CharaCardPlayStyle\",\n  \"defaultColumn\": \"Name\",\n  \"definitions\": [\n    {\n      \"name\": \"Icon\",\n      \"converter\": {\n        \"type\": \"icon\"\n      }\n    },\n    {\n      \"index\": 1,\n      \"name\": \"SortKey\"\n    },\n    {\n      \"index\": 2,\n      \"name\": \"Name\"\n    }\n  ]\n}\n"
  },
  {
    "path": "SaintCoinach/Definitions/CharaMakeClassEquip.json",
    "content": "{\n  \"sheet\": \"CharaMakeClassEquip\",\n  \"definitions\": [\n    {\n      \"name\": \"Helmet\"\n    },\n    {\n      \"index\": 1,\n      \"name\": \"Top\"\n    },\n    {\n      \"index\": 2,\n      \"name\": \"Glove\"\n    },\n    {\n      \"index\": 3,\n      \"name\": \"Down\"\n    },\n    {\n      \"index\": 4,\n      \"name\": \"Shoes\"\n    },\n    {\n      \"index\": 5,\n      \"name\": \"Weapon\"\n    },\n    {\n      \"index\": 6,\n      \"name\": \"SubWeapon\"\n    },\n    {\n      \"index\": 7,\n      \"name\": \"Class\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"ClassJob\"\n      }\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/CharaMakeCustomize.json",
    "content": "{\n  \"sheet\": \"CharaMakeCustomize\",\n  \"defaultColumn\": \"Icon\",\n  \"definitions\": [\n    {\n      \"name\": \"FeatureID\"\n    },\n    {\n      \"index\": 1,\n      \"name\": \"Icon\",\n      \"converter\": {\n        \"type\": \"icon\"\n      }\n    },\n    {\n      \"index\": 2,\n      \"name\": \"Data\"\n    },\n    {\n      \"index\": 3,\n      \"name\": \"IsPurchasable\"\n    },\n    {\n      \"index\": 4,\n      \"name\": \"Hint\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"Lobby\"\n      }\n    },\n    {\n      \"index\": 5,\n      \"name\": \"HintItem\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"Item\"\n      }\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/CharaMakeName.json",
    "content": "{\n  \"sheet\": \"CharaMakeName\",\n  \"definitions\": [\n    {\n      \"name\": \"HyurMidlander[Male]\"\n    },\n    {\n      \"index\": 1,\n      \"name\": \"HyurMidlander[Female]\"\n    },\n    {\n      \"index\": 2,\n      \"name\": \"HyurMidlander[LastName]\"\n    },\n    {\n      \"index\": 3,\n      \"name\": \"HyurHighlander[Male]\"\n    },\n    {\n      \"index\": 4,\n      \"name\": \"HyurHighlander[Female]\"\n    },\n    {\n      \"index\": 5,\n      \"name\": \"HyurHighlander[LastName]\"\n    },\n    {\n      \"index\": 6,\n      \"name\": \"Elezen[Male]\"\n    },\n    {\n      \"index\": 7,\n      \"name\": \"Elezen[Female]\"\n    },\n    {\n      \"index\": 8,\n      \"name\": \"ElezenWildwood[LastName]\"\n    },\n    {\n      \"index\": 9,\n      \"name\": \"ElezenDuskwight[LastName]\"\n    },\n    {\n      \"index\": 10,\n      \"name\": \"MiqoteSun[Male]\"\n    },\n    {\n      \"index\": 11,\n      \"name\": \"MiqoteSun[Female]\"\n    },\n    {\n      \"index\": 12,\n      \"name\": \"MiqoteSun[MaleLastName]\"\n    },\n    {\n      \"index\": 13,\n      \"name\": \"MiqoteSun[FemaleLastName]\"\n    },\n    {\n      \"index\": 14,\n      \"name\": \"MiqoteMoon[Male]\"\n    },\n    {\n      \"index\": 15,\n      \"name\": \"MiqoteMoon[Female]\"\n    },\n    {\n      \"index\": 16,\n      \"name\": \"MiqoteMoon[Lastname]\"\n    },\n    {\n      \"index\": 17,\n      \"name\": \"LalafellPlainsfolk[FirstNameStart]\"\n    },\n    {\n      \"index\": 18,\n      \"name\": \"LalafellPlainsfolk[LastNameStart]\"\n    },\n    {\n      \"index\": 19,\n      \"name\": \"LalafellPlainsfolk[EndOfNames]\"\n    },\n    {\n      \"index\": 20,\n      \"name\": \"LalafellDunesfolk[Male]\"\n    },\n    {\n      \"index\": 21,\n      \"name\": \"LalafellDunesfolk[MaleLastName]\"\n    },\n    {\n      \"index\": 22,\n      \"name\": \"LalafellDunesfolk[Female]\"\n    },\n    {\n      \"index\": 23,\n      \"name\": \"LalafellDunesfolk[FemaleLastName]\"\n    },\n    {\n      \"index\": 24,\n      \"name\": \"RoegadynSeaWolf[Male]\"\n    },\n    {\n      \"index\": 25,\n      \"name\": \"RoegadynSeaWolf[MaleLastName]\"\n    },\n    {\n      \"index\": 26,\n      \"name\": \"RoegadynSeaWolf[Female]\"\n    },\n    {\n      \"index\": 27,\n      \"name\": \"RoegadynSeaWolf[FemaleLastName]\"\n    },\n    {\n      \"index\": 28,\n      \"name\": \"RoegadynHellsguard[FirstName]\"\n    },\n    {\n      \"index\": 29,\n      \"name\": \"RoegadynHellsguard[MaleLastName]\"\n    },\n    {\n      \"index\": 30,\n      \"name\": \"RoegadynHellsguard[FemaleLastName]\"\n    },\n    {\n      \"index\": 31,\n      \"name\": \"AuRaRaen[Male]\"\n    },\n    {\n      \"index\": 32,\n      \"name\": \"AuRaRaen[Female]\"\n    },\n    {\n      \"index\": 33,\n      \"name\": \"AuRaRaen[LastName]\"\n    },\n    {\n      \"index\": 34,\n      \"name\": \"AuRaXaela[Male]\"\n    },\n    {\n      \"index\": 35,\n      \"name\": \"AuRaXaela[Female]\"\n    },\n    {\n      \"index\": 36,\n      \"name\": \"AuRaXaela[LastName]\"\n    },\n    {\n      \"index\": 37,\n      \"name\": \"HrothgarHellions[FirstName]\"\n    },\n    {\n      \"index\": 38,\n      \"name\": \"HrothgarHellions[LastName]\"\n    },\n    {\n      \"index\": 39,\n      \"name\": \"HrothgarLost[FirstName]\"\n    },\n    {\n      \"index\": 40,\n      \"name\": \"HrothgarLost[LastName]\"\n    },\n    {\n      \"index\": 47,\n      \"name\": \"Viera[FirstName]\"\n    },\n    {\n      \"index\": 48,\n      \"name\": \"VieraRava[LastName]\"\n    },\n    {\n      \"index\": 49,\n      \"name\": \"VieraVeena[LastName]\"\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/CharaMakeType.json",
    "content": "{\n  \"sheet\": \"CharaMakeType\",\n  \"definitions\": [\n    {\n      \"name\": \"Race\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"Race\"\n      }\n    },\n    {\n      \"index\": 1,\n      \"name\": \"Tribe\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"Tribe\"\n      }\n    },\n    {\n      \"index\": 2,\n      \"name\": \"Gender\"\n    },\n    {\n      \"index\": 3,\n      \"type\": \"repeat\",\n      \"count\": 28,\n      \"definition\": {\n        \"name\": \"Menu\",\n        \"converter\": {\n          \"type\": \"link\",\n          \"target\": \"Lobby\"\n        }\n      }\n    },\n    {\n      \"index\": 31,\n      \"type\": \"repeat\",\n      \"count\": 28,\n      \"definition\": {\n        \"name\": \"InitVal\"\n      }\n    },\n    {\n      \"index\": 59,\n      \"type\": \"repeat\",\n      \"count\": 28,\n      \"definition\": {\n        \"name\": \"SubMenuType\"\n      }\n    },\n    {\n      \"index\": 87,\n      \"type\": \"repeat\",\n      \"count\": 28,\n      \"definition\": {\n        \"name\": \"SubMenuNum\"\n      }\n    },\n    {\n      \"index\": 115,\n      \"type\": \"repeat\",\n      \"count\": 28,\n      \"definition\": {\n        \"name\": \"LookAt\"\n      }\n    },\n    {\n      \"index\": 143,\n      \"type\": \"repeat\",\n      \"count\": 28,\n      \"definition\": {\n        \"name\": \"SubMenuMask\"\n      }\n    },\n    {\n      \"index\": 171,\n      \"type\": \"repeat\",\n      \"count\": 28,\n      \"definition\": {\n        \"name\": \"Customize\"\n      }\n    },\n    {\n      \"index\": 199,\n      \"type\": \"repeat\",\n      \"count\": 100,\n      \"definition\": {\n        \"type\": \"group\",\n        \"members\": [\n          {\n            \"type\": \"repeat\",\n            \"count\": 28,\n            \"definition\": {\n              \"name\": \"SubMenuParam\"\n            }\n          }\n        ]\n      }\n    },\n    {\n      \"index\": 2999,\n      \"type\": \"repeat\",\n      \"count\": 10,\n      \"definition\": {\n        \"type\": \"group\",\n        \"members\": [\n          {\n            \"type\": \"repeat\",\n            \"count\": 28,\n            \"definition\": {\n              \"name\": \"SubMenuGraphic\"\n            }\n          }\n        ]\n      }\n    },\n    {\n      \"index\": 3279,\n      \"type\": \"repeat\",\n      \"count\": 12,\n      \"definition\": {\n        \"name\": \"VoiceStruct\"\n      }\n    },\n    {\n      \"index\": 3291,\n      \"type\": \"repeat\",\n      \"count\": 7,\n      \"definition\": {\n        \"type\": \"repeat\",\n        \"count\": 8,\n        \"definition\": {\n          \"name\": \"FacialFeatureOption\",\n          \"converter\": {\n            \"type\": \"icon\"\n          }\n        }\n      }\n    },\n    {\n      \"index\": 3347,\n      \"type\": \"repeat\",\n      \"count\": 1,\n      \"definition\": {\n        \"type\": \"group\",\n        \"members\": [\n          {\n            \"type\": \"repeat\",\n            \"count\": 3,\n            \"definition\": {\n              \"name\": \"Helmet\"\n            }\n          },\n          {\n            \"type\": \"repeat\",\n            \"count\": 3,\n            \"definition\": {\n              \"name\": \"Top\"\n            }\n          },\n          {\n            \"type\": \"repeat\",\n            \"count\": 3,\n            \"definition\": {\n              \"name\": \"Gloves\"\n            }\n          },\n          {\n            \"type\": \"repeat\",\n            \"count\": 3,\n            \"definition\": {\n              \"name\": \"Legs\"\n            }\n          },\n          {\n            \"type\": \"repeat\",\n            \"count\": 3,\n            \"definition\": {\n              \"name\": \"Shoes\"\n            }\n          },\n          {\n            \"type\": \"repeat\",\n            \"count\": 3,\n            \"definition\": {\n              \"name\": \"Weapon\"\n            }\n          },\n          {\n            \"type\": \"repeat\",\n            \"count\": 3,\n            \"definition\": {\n              \"name\": \"SubWeapon\"\n            }\n          }\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "SaintCoinach/Definitions/ChocoboRace.json",
    "content": "{\n  \"sheet\": \"ChocoboRace\",\n  \"definitions\": [\n    {\n      \"name\": \"ChocoboRaceRank\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"ChocoboRaceRank\"\n      }\n    },\n    {\n      \"index\": 1,\n      \"name\": \"ChocoboRaceTerritory\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"ChocoboRaceTerritory\"\n      }\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/ChocoboRaceAbility.json",
    "content": "{\n  \"sheet\": \"ChocoboRaceAbility\",\n  \"defaultColumn\": \"Name\",\n  \"definitions\": [\n    {\n      \"name\": \"Name\"\n    },\n    {\n      \"index\": 1,\n      \"name\": \"Description\"\n    },\n    {\n      \"index\": 2,\n      \"name\": \"Icon\",\n      \"converter\": {\n        \"type\": \"icon\"\n      }\n    },\n    {\n      \"index\": 3,\n      \"name\": \"ChocoboRaceAbilityType\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"ChocoboRaceAbilityType\"\n      }\n    },\n    {\n      \"index\": 4,\n      \"name\": \"Value\"\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/ChocoboRaceAbilityType.json",
    "content": "{\n  \"sheet\": \"ChocoboRaceAbilityType\",\n  \"defaultColumn\": \"IsActive\",\n  \"definitions\": [\n    {\n      \"name\": \"IsActive\"\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/ChocoboRaceItem.json",
    "content": "{\n  \"sheet\": \"ChocoboRaceItem\",\n  \"defaultColumn\": \"Name\",\n  \"definitions\": [\n    {\n      \"name\": \"Name\"\n    },\n    {\n      \"index\": 1,\n      \"name\": \"Description\"\n    },\n    {\n      \"index\": 2,\n      \"name\": \"Icon\",\n      \"converter\": {\n        \"type\": \"icon\"\n      }\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/ChocoboRaceRank.json",
    "content": "{\n  \"sheet\": \"ChocoboRaceRank\",\n  \"defaultColumn\": \"Name\",\n  \"definitions\": [\n    {\n      \"name\": \"Rating{Min}\"\n    },\n    {\n      \"index\": 1,\n      \"name\": \"Rating{Max}\"\n    },\n    {\n      \"index\": 2,\n      \"name\": \"Name\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"GoldSaucerTextData\"\n      }\n    },\n    {\n      \"index\": 3,\n      \"name\": \"Fee\"\n    },\n    {\n      \"index\": 4,\n      \"name\": \"Icon\",\n      \"converter\": {\n        \"type\": \"icon\"\n      }\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/ChocoboRaceStatus.json",
    "content": "{\n  \"sheet\": \"ChocoboRaceStatus\",\n  \"defaultColumn\": \"Status\",\n  \"definitions\": [\n    {\n      \"name\": \"Status\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"Status\"\n      }\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/ChocoboRaceTerritory.json",
    "content": "{\n  \"sheet\": \"ChocoboRaceTerritory\",\n  \"defaultColumn\": \"Name\",\n  \"definitions\": [\n    {\n      \"name\": \"Name\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"GoldSaucerTextData\"\n      }\n    },\n    {\n      \"index\": 1,\n      \"name\": \"Icon\",\n      \"converter\": {\n        \"type\": \"icon\"\n      }\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/ChocoboRaceTutorial.json",
    "content": "{\n  \"sheet\": \"ChocoboRaceTutorial\",\n  \"definitions\": [\n    {\n      \"type\": \"repeat\",\n      \"count\": 8,\n      \"definition\": {\n        \"name\": \"NpcYell\",\n        \"converter\": {\n          \"type\": \"link\",\n          \"target\": \"NpcYell\"\n        }\n      }\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/ChocoboRaceWeather.json",
    "content": "{\n  \"sheet\": \"ChocoboRaceWeather\",\n  \"definitions\": [\n    {\n      \"name\": \"WeatherType[1]\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"Weather\"\n      }\n    },\n    {\n      \"index\": 1,\n      \"name\": \"WeatherType[2]\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"Weather\"\n      }\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/ChocoboTaxi.json",
    "content": "{\n  \"sheet\": \"ChocoboTaxi\",\n  \"defaultColumn\": \"Location\",\n  \"definitions\": [\n    {\n      \"name\": \"Location\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"ChocoboTaxiStand\"\n      }\n    },\n    {\n      \"index\": 1,\n      \"name\": \"Fare\"\n    },\n    {\n      \"index\": 2,\n      \"name\": \"TimeRequired\"\n    }\n  ]\n}\n"
  },
  {
    "path": "SaintCoinach/Definitions/ChocoboTaxiStand.json",
    "content": "{\n  \"sheet\": \"ChocoboTaxiStand\",\n  \"defaultColumn\": \"PlaceName\",\n  \"isGenericReferenceTarget\": true,\n  \"definitions\": [\n    {\n      \"type\": \"repeat\",\n      \"count\": 8,\n      \"definition\": {\n        \"name\": \"TargetLocations\",\n        \"converter\": {\n          \"type\": \"link\",\n          \"target\": \"ChocoboTaxi\"\n        }\n      }\n    },\n    {\n      \"index\": 8,\n      \"name\": \"PlaceName\"\n    }\n  ]\n}\n"
  },
  {
    "path": "SaintCoinach/Definitions/CircleActivity.json",
    "content": "{\n  \"sheet\": \"CircleActivity\",\n  \"defaultColumn\": \"Name\",\n  \"definitions\": [\n    {\n      \"name\": \"Name\"\n    },\n    {\n      \"index\": 1,\n      \"name\": \"Icon\",\n      \"converter\": {\n        \"type\": \"icon\"\n      }\n    },\n    {\n      \"index\": 2,\n      \"name\": \"Order\"\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/ClassJob.json",
    "content": "{\n  \"sheet\": \"ClassJob\",\n  \"defaultColumn\": \"Name\",\n  \"definitions\": [\n    {\n      \"name\": \"Name\"\n    },\n    {\n      \"index\": 1,\n      \"name\": \"Abbreviation\"\n    },\n    {\n      \"index\": 3,\n      \"name\": \"ClassJobCategory\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"ClassJobCategory\"\n      }\n    },\n    {\n      \"index\": 4,\n      \"name\": \"ExpArrayIndex\"\n    },\n    {\n      \"index\": 5,\n      \"name\": \"BattleClassIndex\"\n    },\n    {\n      \"index\": 7,\n      \"name\": \"JobIndex\"\n    },\n    {\n      \"index\": 8,\n      \"name\": \"DohDolJobIndex\"\n    },\n    {\n      \"index\": 9,\n      \"name\": \"Modifier{HitPoints}\"\n    },\n    {\n      \"index\": 10,\n      \"name\": \"Modifier{ManaPoints}\"\n    },\n    {\n      \"index\": 11,\n      \"name\": \"Modifier{Strength}\"\n    },\n    {\n      \"index\": 12,\n      \"name\": \"Modifier{Vitality}\"\n    },\n    {\n      \"index\": 13,\n      \"name\": \"Modifier{Dexterity}\"\n    },\n    {\n      \"index\": 14,\n      \"name\": \"Modifier{Intelligence}\"\n    },\n    {\n      \"index\": 15,\n      \"name\": \"Modifier{Mind}\"\n    },\n    {\n      \"index\": 16,\n      \"name\": \"Modifier{Piety}\"\n    },\n    {\n      \"index\": 26,\n      \"name\": \"PvPActionSortRow\"\n    },\n    {\n      \"index\": 28,\n      \"name\": \"ClassJob{Parent}\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"ClassJob\"\n      }\n    },\n    {\n      \"index\": 29,\n      \"name\": \"Name{English}\"\n    },\n    {\n      \"index\": 30,\n      \"name\": \"Item{StartingWeapon}\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"Item\"\n      }\n    },\n    {\n      \"index\": 32,\n      \"name\": \"Role\"\n    },\n    {\n      \"index\": 33,\n      \"name\": \"StartingTown\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"Town\"\n      }\n    },\n    {\n      \"index\": 34,\n      \"name\": \"MonsterNote\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"MonsterNote\"\n      }\n    },\n    {\n      \"index\": 35,\n      \"name\": \"PrimaryStat\"\n    },\n    {\n      \"index\": 36,\n      \"name\": \"LimitBreak1\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"Action\"\n      }\n    },\n    {\n      \"index\": 37,\n      \"name\": \"LimitBreak2\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"Action\"\n      }\n    },\n    {\n      \"index\": 38,\n      \"name\": \"LimitBreak3\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"Action\"\n      }\n    },\n    {\n      \"index\": 39,\n      \"name\": \"UIPriority\"\n    },\n    {\n      \"index\": 40,\n      \"name\": \"Item{SoulCrystal}\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"Item\"\n      }\n    },\n    {\n      \"index\": 41,\n      \"name\": \"UnlockQuest\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"Quest\"\n      }\n    },\n    {\n      \"index\": 42,\n      \"name\": \"RelicQuest\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"Quest\"\n      }\n    },\n    {\n      \"index\": 43,\n      \"name\": \"Prerequisite\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"Quest\"\n      }\n    },\n    {\n      \"index\": 44,\n      \"name\": \"StartingLevel\"\n    },\n    {\n      \"index\": 45,\n      \"name\": \"PartyBonus\"\n    },\n    {\n      \"index\": 47,\n      \"name\": \"IsLimitedJob\"\n    },\n    {\n      \"index\": 48,\n      \"name\": \"CanQueueForDuty\"\n    }\n  ]\n}\n"
  },
  {
    "path": "SaintCoinach/Definitions/ClassJobActionSort.json",
    "content": "{\n  \"sheet\": \"ClassJobActionSort\",\n  \"definitions\": []\n}"
  },
  {
    "path": "SaintCoinach/Definitions/ClassJobCategory.json",
    "content": "{\n  \"sheet\": \"ClassJobCategory\",\n  \"defaultColumn\": \"Name\",\n  \"definitions\": [\n    {\n      \"name\": \"Name\"\n    },\n    {\n      \"index\": 1,\n      \"name\": \"ADV\"\n    },\n    {\n      \"index\": 2,\n      \"name\": \"GLA\"\n    },\n    {\n      \"index\": 3,\n      \"name\": \"PGL\"\n    },\n    {\n      \"index\": 4,\n      \"name\": \"MRD\"\n    },\n    {\n      \"index\": 5,\n      \"name\": \"LNC\"\n    },\n    {\n      \"index\": 6,\n      \"name\": \"ARC\"\n    },\n    {\n      \"index\": 7,\n      \"name\": \"CNJ\"\n    },\n    {\n      \"index\": 8,\n      \"name\": \"THM\"\n    },\n    {\n      \"index\": 9,\n      \"name\": \"CRP\"\n    },\n    {\n      \"index\": 10,\n      \"name\": \"BSM\"\n    },\n    {\n      \"index\": 11,\n      \"name\": \"ARM\"\n    },\n    {\n      \"index\": 12,\n      \"name\": \"GSM\"\n    },\n    {\n      \"index\": 13,\n      \"name\": \"LTW\"\n    },\n    {\n      \"index\": 14,\n      \"name\": \"WVR\"\n    },\n    {\n      \"index\": 15,\n      \"name\": \"ALC\"\n    },\n    {\n      \"index\": 16,\n      \"name\": \"CUL\"\n    },\n    {\n      \"index\": 17,\n      \"name\": \"MIN\"\n    },\n    {\n      \"index\": 18,\n      \"name\": \"BTN\"\n    },\n    {\n      \"index\": 19,\n      \"name\": \"FSH\"\n    },\n    {\n      \"index\": 20,\n      \"name\": \"PLD\"\n    },\n    {\n      \"index\": 21,\n      \"name\": \"MNK\"\n    },\n    {\n      \"index\": 22,\n      \"name\": \"WAR\"\n    },\n    {\n      \"index\": 23,\n      \"name\": \"DRG\"\n    },\n    {\n      \"index\": 24,\n      \"name\": \"BRD\"\n    },\n    {\n      \"index\": 25,\n      \"name\": \"WHM\"\n    },\n    {\n      \"index\": 26,\n      \"name\": \"BLM\"\n    },\n    {\n      \"index\": 27,\n      \"name\": \"ACN\"\n    },\n    {\n      \"index\": 28,\n      \"name\": \"SMN\"\n    },\n    {\n      \"index\": 29,\n      \"name\": \"SCH\"\n    },\n    {\n      \"index\": 30,\n      \"name\": \"ROG\"\n    },\n    {\n      \"index\": 31,\n      \"name\": \"NIN\"\n    },\n    {\n      \"index\": 32,\n      \"name\": \"MCH\"\n    },\n    {\n      \"index\": 33,\n      \"name\": \"DRK\"\n    },\n    {\n      \"index\": 34,\n      \"name\": \"AST\"\n    },\n    {\n      \"index\": 35,\n      \"name\": \"SAM\"\n    },\n    {\n      \"index\": 36,\n      \"name\": \"RDM\"\n    },\n    {\n      \"index\": 37,\n      \"name\": \"BLU\"\n    },\n    {\n      \"index\": 38,\n      \"name\": \"GNB\"\n    },\n    {\n      \"index\": 39,\n      \"name\": \"DNC\"\n    },\n    {\n      \"index\": 40,\n      \"name\": \"RPR\"\n    },\n    {\n      \"index\": 41,\n      \"name\": \"SGE\"\n    },\n    {\n      \"index\": 42,\n      \"name\": \"VPR\"\n    },\n    {\n      \"index\": 43,\n      \"name\": \"PCT\"\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/CollectablesShop.json",
    "content": "{\n  \"sheet\": \"CollectablesShop\",\n  \"defaultColumn\": \"Name\",\n  \"definitions\": [\n    {\n      \"name\": \"Name\"\n    },\n    {\n      \"index\": 1,\n      \"name\": \"Quest\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"Quest\"\n      }\n    },\n    {\n      \"index\": 2,\n      \"name\": \"RewardType\"\n    },\n    {\n      \"index\": 3,\n      \"type\": \"repeat\",\n      \"count\": 11,\n      \"definition\": {\n        \"name\": \"ShopItems\",\n        \"converter\": {\n          \"type\": \"link\",\n          \"target\": \"CollectablesShopItem\"\n        }\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "SaintCoinach/Definitions/CollectablesShopItem.json",
    "content": "{\n  \"sheet\": \"CollectablesShopItem\",\n  \"defaultColumn\": \"Item\",\n  \"definitions\": [\n    {\n      \"name\": \"Item\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"Item\"\n      }\n    },\n    {\n      \"index\": 1,\n      \"name\": \"CollectablesShopItemGroup\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"CollectablesShopItemGroup\"\n      }\n    },\n    {\n      \"index\": 2,\n      \"name\": \"LevelMin\"\n    },\n    {\n      \"index\": 4,\n      \"name\": \"LevelMax\"\n    },\n    {\n      \"index\": 5,\n      \"name\": \"Stars\"\n    },\n    {\n      \"index\": 6,\n      \"name\": \"Key\"\n    },\n    {\n      \"index\": 7,\n      \"name\": \"CollectablesShopRefine\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"CollectablesShopRefine\"\n      }\n    },\n    {\n      \"index\": 8,\n      \"name\": \"CollectablesShopRewardScrip\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"CollectablesShopRewardScrip\"\n      }\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/CollectablesShopItemGroup.json",
    "content": "{\n  \"sheet\": \"CollectablesShopItemGroup\",\n  \"defaultColumn\": \"Name\",\n  \"definitions\": [\n    {\n      \"name\": \"Name\"\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/CollectablesShopRefine.json",
    "content": "{\n  \"sheet\": \"CollectablesShopRefine\",\n  \"definitions\": [\n    {\n      \"name\": \"LowCollectability\"\n    },\n    {\n      \"index\": 1,\n      \"name\": \"MidCollectability\"\n    },\n    {\n      \"index\": 2,\n      \"name\": \"HighCollectability\"\n    }\n  ]\n}\n"
  },
  {
    "path": "SaintCoinach/Definitions/CollectablesShopRewardItem.json",
    "content": "{\n  \"sheet\": \"CollectablesShopRewardItem\",\n  \"defaultColumn\": \"Item\",\n  \"definitions\": [\n    {\n      \"name\": \"Item\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"Item\"\n      }\n    },\n    {\n      \"index\": 2,\n      \"name\": \"RewardLow\"\n    },\n    {\n      \"index\": 3,\n      \"name\": \"RewardMid\"\n    },\n    {\n      \"index\": 4,\n      \"name\": \"RewardHigh\"\n    }\n  ]\n}\n"
  },
  {
    "path": "SaintCoinach/Definitions/CollectablesShopRewardScrip.json",
    "content": "{\n  \"sheet\": \"CollectablesShopRewardScrip\",\n  \"defaultColumn\": \"Currency\",\n  \"definitions\": [\n    {\n      \"name\": \"Currency\"\n    },\n    {\n      \"index\": 1,\n      \"name\": \"LowReward\"\n    },\n    {\n      \"index\": 2,\n      \"name\": \"MidReward\"\n    },\n    {\n      \"index\": 3,\n      \"name\": \"HighReward\"\n    },\n    {\n      \"index\": 4,\n      \"name\": \"ExpRatioLow\"\n    },\n    {\n      \"index\": 5,\n      \"name\": \"ExpRatioMid\"\n    },\n    {\n      \"index\": 6,\n      \"name\": \"ExpRatioHigh\"\n    }\n  ]\n}\n"
  },
  {
    "path": "SaintCoinach/Definitions/Companion.json",
    "content": "{\n  \"sheet\": \"Companion\",\n  \"defaultColumn\": \"Singular\",\n  \"definitions\": [\n    {\n      \"name\": \"Singular\"\n    },\n    {\n      \"index\": 1,\n      \"name\": \"Adjective\"\n    },\n    {\n      \"index\": 2,\n      \"name\": \"Plural\"\n    },\n    {\n      \"index\": 3,\n      \"name\": \"PossessivePronoun\"\n    },\n    {\n      \"index\": 4,\n      \"name\": \"StartsWithVowel\"\n    },\n    {\n      \"index\": 6,\n      \"name\": \"Pronoun\"\n    },\n    {\n      \"index\": 7,\n      \"name\": \"Article\"\n    },\n    {\n      \"index\": 8,\n      \"name\": \"Model\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"ModelChara\"\n      }\n    },\n    {\n      \"index\": 9,\n      \"name\": \"Scale\"\n    },\n    {\n      \"index\": 10,\n      \"name\": \"InactiveIdle[0]\"\n    },\n    {\n      \"index\": 11,\n      \"name\": \"InactiveIdle[1]\"\n    },\n    {\n      \"index\": 12,\n      \"name\": \"InactiveBattle\"\n    },\n    {\n      \"index\": 13,\n      \"name\": \"InactiveWandering\"\n    },\n    {\n      \"index\": 14,\n      \"name\": \"Behavior\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"CompanionMove\"\n      }\n    },\n    {\n      \"index\": 15,\n      \"name\": \"Special\"\n    },\n    {\n      \"index\": 16,\n      \"name\": \"WanderingWait\"\n    },\n    {\n      \"index\": 17,\n      \"name\": \"Priority\"\n    },\n    {\n      \"index\": 23,\n      \"name\": \"Enemy\"\n    },\n    {\n      \"index\": 24,\n      \"name\": \"Battle\"\n    },\n    {\n      \"index\": 25,\n      \"name\": \"Roulette\"\n    },\n    {\n      \"index\": 28,\n      \"name\": \"Icon\",\n      \"converter\": {\n        \"type\": \"icon\"\n      }\n    },\n    {\n      \"index\": 29,\n      \"name\": \"Order\"\n    },\n    {\n      \"index\": 30,\n      \"name\": \"IdleAnimation\"\n    },\n    {\n      \"index\": 32,\n      \"name\": \"Cost\"\n    },\n    {\n      \"index\": 33,\n      \"name\": \"HP\"\n    },\n    {\n      \"index\": 35,\n      \"name\": \"Skill{Angle}\"\n    },\n    {\n      \"index\": 36,\n      \"name\": \"Skill{Cost}\"\n    },\n    {\n      \"index\": 39,\n      \"name\": \"MinionRace\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"MinionRace\"\n      }\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/CompanionMove.json",
    "content": "{\n  \"sheet\": \"CompanionMove\",\n  \"defaultColumn\": \"Name\",\n  \"definitions\": [\n    {\n      \"name\": \"Name\"\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/CompanionTransient.json",
    "content": "{\n  \"sheet\": \"CompanionTransient\",\n  \"defaultColumn\": \"Name\",\n  \"definitions\": [\n    {\n      \"name\": \"Description\"\n    },\n    {\n      \"index\": 1,\n      \"name\": \"Description{Enhanced}\"\n    },\n    {\n      \"index\": 2,\n      \"name\": \"Tooltip\"\n    },\n    {\n      \"index\": 3,\n      \"name\": \"SpecialAction{Name}\"\n    },\n    {\n      \"index\": 4,\n      \"name\": \"SpecialAction{Description}\"\n    },\n    {\n      \"index\": 5,\n      \"name\": \"Attack\"\n    },\n    {\n      \"index\": 6,\n      \"name\": \"Defense\"\n    },\n    {\n      \"index\": 7,\n      \"name\": \"Speed\"\n    },\n    {\n      \"index\": 8,\n      \"name\": \"HasAreaAttack\"\n    },\n    {\n      \"index\": 9,\n      \"name\": \"Strength{Gate}\"\n    },\n    {\n      \"index\": 10,\n      \"name\": \"Strength{Eye}\"\n    },\n    {\n      \"index\": 11,\n      \"name\": \"Strength{Shield}\"\n    },\n    {\n      \"index\": 12,\n      \"name\": \"Strength{Arcana}\"\n    },\n    {\n      \"index\": 13,\n      \"name\": \"MinionSkillType\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"MinionSkillType\"\n      }\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/CompanyAction.json",
    "content": "{\n  \"sheet\": \"CompanyAction\",\n  \"defaultColumn\": \"Name\",\n  \"definitions\": [\n    {\n      \"name\": \"Name\"\n    },\n    {\n      \"index\": 1,\n      \"name\": \"Description\"\n    },\n    {\n      \"index\": 2,\n      \"name\": \"Icon\",\n      \"converter\": {\n        \"type\": \"icon\"\n      }\n    },\n    {\n      \"index\": 3,\n      \"name\": \"FCRank\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"FCRank\"\n      }\n    },\n    {\n      \"index\": 4,\n      \"name\": \"Cost\"\n    },\n    {\n      \"index\": 5,\n      \"name\": \"Order\"\n    },\n    {\n      \"index\": 6,\n      \"name\": \"Purchasable\"\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/CompanyCraftDraft.json",
    "content": "{\n  \"sheet\": \"CompanyCraftDraft\",\n  \"defaultColumn\": \"Name\",\n  \"definitions\": [\n    {\n      \"name\": \"Name\"\n    },\n    {\n      \"index\": 1,\n      \"name\": \"CompanyCraftDraftCategory\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"CompanyCraftDraftCategory\"\n      }\n    },\n    {\n      \"index\": 2,\n      \"type\": \"repeat\",\n      \"count\": 3,\n      \"definition\": {\n        \"type\": \"group\",\n        \"members\": [\n          {\n            \"name\": \"RequiredItem\",\n            \"converter\": {\n              \"type\": \"link\",\n              \"target\": \"Item\"\n            }\n          },\n          {\n            \"name\": \"RequiredItemCount\"\n          }\n        ]\n      }\n    },\n    {\n      \"index\": 8,\n      \"name\": \"Order\"\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/CompanyCraftDraftCategory.json",
    "content": "{\n  \"sheet\": \"CompanyCraftDraftCategory\",\n  \"defaultColumn\": \"Name\",\n  \"definitions\": [\n    {\n      \"name\": \"Name\"\n    },\n    {\n      \"index\": 1,\n      \"type\": \"repeat\",\n      \"count\": 10,\n      \"definition\": {\n        \"type\": \"group\",\n        \"members\": [\n          {\n            \"name\": \"CompanyCraftType\",\n            \"converter\": {\n              \"type\": \"link\",\n              \"target\": \"CompanyCraftType\"\n            }\n          }\n        ]\n      }\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/CompanyCraftManufactoryState.json",
    "content": "{\n  \"sheet\": \"CompanyCraftManufactoryState\",\n  \"defaultColumn\": \"Name\",\n  \"definitions\": [\n    {\n      \"name\": \"Name\"\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/CompanyCraftPart.json",
    "content": "{\n  \"sheet\": \"CompanyCraftPart\",\n  \"definitions\": [\n    {\n      \"index\": 1,\n      \"name\": \"CompanyCraftType\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"CompanyCraftType\"\n      }\n    },\n    {\n      \"index\": 2,\n      \"type\": \"repeat\",\n      \"count\": 3,\n      \"definition\": {\n        \"name\": \"CompanyCraftProcess\",\n        \"converter\": {\n          \"type\": \"link\",\n          \"target\": \"CompanyCraftProcess\"\n        }\n      }\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/CompanyCraftProcess.json",
    "content": "{\n  \"sheet\": \"CompanyCraftProcess\",\n  \"definitions\": [\n    {\n      \"type\": \"repeat\",\n      \"count\": 12,\n      \"definition\": {\n        \"type\": \"group\",\n        \"members\": [\n          {\n            \"name\": \"SupplyItem\",\n            \"converter\": {\n              \"type\": \"link\",\n              \"target\": \"CompanyCraftSupplyItem\"\n            }\n          },\n          {\n            \"name\": \"SetQuantity\"\n          },\n          {\n            \"name\": \"SetsRequired\"\n          }\n        ]\n      }\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/CompanyCraftSequence.json",
    "content": "{\n  \"sheet\": \"CompanyCraftSequence\",\n  \"defaultColumn\": \"ResultItem\",\n  \"definitions\": [\n    {\n      \"name\": \"ResultItem\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"Item\"\n      }\n    },\n    {\n      \"index\": 1,\n      \"name\": \"Category\"\n    },\n    {\n      \"index\": 2,\n      \"name\": \"CompanyCraftDraftCategory\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"CompanyCraftDraftCategory\"\n      }\n    },\n    {\n      \"index\": 3,\n      \"name\": \"CompanyCraftType\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"CompanyCraftType\"\n      }\n    },\n    {\n      \"index\": 4,\n      \"name\": \"CompanyCraftDraft\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"CompanyCraftDraft\"\n      }\n    },\n    {\n      \"index\": 5,\n      \"type\": \"repeat\",\n      \"count\": 8,\n      \"definition\": {\n        \"name\": \"CompanyCraftPart\",\n        \"converter\": {\n          \"type\": \"link\",\n          \"target\": \"CompanyCraftPart\"\n        }\n      }\n    },\n    {\n      \"index\": 13,\n      \"name\": \"Order\"\n    }\n  ]\n}\n"
  },
  {
    "path": "SaintCoinach/Definitions/CompanyCraftSupplyItem.json",
    "content": "{\n  \"sheet\": \"CompanyCraftSupplyItem\",\n  \"defaultColumn\": \"Item\",\n  \"definitions\": [\n    {\n      \"name\": \"Item\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"Item\"\n      }\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/CompanyCraftType.json",
    "content": "{\n  \"sheet\": \"CompanyCraftType\",\n  \"defaultColumn\": \"Name\",\n  \"definitions\": [\n    {\n      \"name\": \"Name\"\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/CompanyLeve.json",
    "content": "{\n  \"sheet\": \"CompanyLeve\",\n  \"definitions\": [\n    {\n      \"type\": \"repeat\",\n      \"count\": 8,\n      \"definition\": {\n        \"name\": \"RoutePointTime\"\n      }\n    },\n    {\n      \"index\": 8,\n      \"type\": \"repeat\",\n      \"count\": 8,\n      \"definition\": {\n        \"name\": \"BaseID\",\n        \"converter\": {\n          \"type\": \"multiref\",\n          \"targets\": [\n            \"EventItem\",\n            \"BNpcBase\"\n          ]\n        }\n      }\n    },\n    {\n      \"index\": 16,\n      \"type\": \"repeat\",\n      \"count\": 8,\n      \"definition\": {\n        \"name\": \"EnemyLevel\"\n      }\n    },\n    {\n      \"index\": 24,\n      \"type\": \"repeat\",\n      \"count\": 8,\n      \"definition\": {\n        \"name\": \"BNpcName\",\n        \"converter\": {\n          \"type\": \"link\",\n          \"target\": \"BNpcName\"\n        }\n      }\n    },\n    {\n      \"index\": 32,\n      \"type\": \"repeat\",\n      \"count\": 8,\n      \"definition\": {\n        \"name\": \"ItemsInvolved\",\n        \"converter\": {\n          \"type\": \"link\",\n          \"target\": \"EventItem\"\n        }\n      }\n    },\n    {\n      \"index\": 40,\n      \"type\": \"repeat\",\n      \"count\": 8,\n      \"definition\": {\n        \"name\": \"ItemsInvolvedQty\"\n      }\n    },\n    {\n      \"index\": 48,\n      \"type\": \"repeat\",\n      \"count\": 8,\n      \"definition\": {\n        \"name\": \"ItemDropRate\"\n      }\n    },\n    {\n      \"index\": 56,\n      \"type\": \"repeat\",\n      \"count\": 6,\n      \"definition\": {\n        \"type\": \"group\",\n        \"members\": [\n          {\n            \"type\": \"repeat\",\n            \"count\": 8,\n            \"definition\": {\n              \"name\": \"ToDoParam\"\n            }\n          }\n        ]\n      }\n    },\n    {\n      \"index\": 104,\n      \"type\": \"repeat\",\n      \"count\": 8,\n      \"definition\": {\n        \"type\": \"group\",\n        \"members\": [\n          {\n            \"type\": \"repeat\",\n            \"count\": 8,\n            \"definition\": {\n              \"name\": \"NumOfAppearance\"\n            }\n          }\n        ]\n      }\n    },\n    {\n      \"index\": 168,\n      \"type\": \"repeat\",\n      \"count\": 8,\n      \"definition\": {\n        \"name\": \"ToDoSequence\"\n      }\n    },\n    {\n      \"index\": 176,\n      \"name\": \"Rule\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"CompanyLeveRule\"\n      }\n    },\n    {\n      \"index\": 177,\n      \"name\": \"RuleParam\"\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/CompanyLeveRule.json",
    "content": "{\n  \"sheet\": \"CompanyLeveRule\",\n  \"defaultColumn\": \"Type\",\n  \"definitions\": [\n    {\n      \"name\": \"Type\"\n    },\n    {\n      \"index\": 1,\n      \"name\": \"Objective\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"LeveString\"\n      }\n    },\n    {\n      \"index\": 2,\n      \"name\": \"Help\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"LeveString\"\n      }\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/CompleteJournal.json",
    "content": "{\n  \"sheet\": \"CompleteJournal\",\n  \"defaultColumn\": \"Name\",\n  \"definitions\": [\n    {\n      \"index\": 1,\n      \"name\": \"RequiredLevel\"\n    },\n    {\n      \"index\": 3,\n      \"name\": \"Icon\",\n      \"converter\": {\n        \"type\": \"icon\"\n      }\n    },\n    {\n      \"index\": 5,\n      \"name\": \"Name\"\n    },\n    {\n      \"index\": 6,\n      \"type\": \"repeat\",\n      \"count\": 24,\n      \"definition\": {\n        \"name\": \"Cutscene\",\n        \"converter\": {\n          \"type\": \"link\",\n          \"target\": \"Cutscene\"\n        }\n      }\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/CompleteJournalCategory.json",
    "content": "{\n  \"sheet\": \"CompleteJournalCategory\",\n  \"definitions\": [\n    {\n      \"name\": \"FirstQuest\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"CompleteJournal\"\n      }\n    },\n    {\n      \"index\": 1,\n      \"name\": \"LastQuest\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"CompleteJournal\"\n      }\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/Completion.json",
    "content": "{\n  \"sheet\": \"Completion\",\n  \"defaultColumn\": \"Text\",\n  \"definitions\": [\n    {\n      \"index\": 0,\n      \"name\": \"Group\"\n    },\n    {\n      \"index\": 1,\n      \"name\": \"Key\"\n    },\n    {\n      \"index\": 2,\n      \"name\": \"LookupTable\"\n    },\n    {\n      \"index\": 3,\n      \"name\": \"Text\"\n    },\n    {\n      \"index\": 4,\n      \"name\": \"GroupTitle\"\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/Condition.json",
    "content": "{\n  \"sheet\": \"Condition\",\n  \"definitions\": [\n    {\n      \"index\": 2,\n      \"name\": \"LogMessage\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"LogMessage\"\n      }\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/ConfigKey.json",
    "content": "{\n  \"sheet\": \"ConfigKey\",\n  \"definitions\": [\n    {\n      \"name\": \"Label\"\n    },\n    {\n      \"index\": 1,\n      \"name\": \"Param\"\n    },\n    {\n      \"index\": 2,\n      \"name\": \"Platform\"\n    },\n    {\n      \"index\": 3,\n      \"name\": \"Required\"\n    },\n    {\n      \"index\": 4,\n      \"name\": \"Category\"\n    },\n    {\n      \"index\": 7,\n      \"name\": \"Text\"\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/ContentCloseCycle.json",
    "content": "{\n  \"sheet\": \"ContentCloseCycle\",\n  \"definitions\": [\n    {\n      \"name\": \"Unixtime\"\n    },\n    {\n      \"index\": 1,\n      \"name\": \"Time{Seconds}\"\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/ContentEventItem.json",
    "content": "{\n  \"sheet\": \"ContentEventItem\",\n  \"definitions\": []\n}"
  },
  {
    "path": "SaintCoinach/Definitions/ContentExAction.json",
    "content": "{\n  \"sheet\": \"ContentExAction\",\n  \"definitions\": [\n    {\n      \"name\": \"Name\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"Action\"\n      }\n    },\n    {\n      \"index\": 2,\n      \"name\": \"Charges\"\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/ContentFinderCondition.json",
    "content": "{\n  \"sheet\": \"ContentFinderCondition\",\n  \"defaultColumn\": \"Name\",\n  \"definitions\": [\n    {\n      \"name\": \"ShortCode\"\n    },\n    {\n      \"index\": 1,\n      \"name\": \"TerritoryType\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"TerritoryType\"\n      }\n    },\n    {\n      \"index\": 2,\n      \"name\": \"ContentLinkType\"\n    },\n    {\n      \"index\": 3,\n      \"name\": \"Content\",\n      \"converter\": {\n        \"type\": \"complexlink\",\n        \"links\": [\n          {\n            \"when\": {\n              \"key\": \"ContentLinkType\",\n              \"value\": 1\n            },\n            \"sheet\": \"InstanceContent\"\n          },\n          {\n            \"when\": {\n              \"key\": \"ContentLinkType\",\n              \"value\": 2\n            },\n            \"sheet\": \"PartyContent\"\n          },\n          {\n            \"when\": {\n              \"key\": \"ContentLinkType\",\n              \"value\": 3\n            },\n            \"sheet\": \"PublicContent\"\n          },\n          {\n            \"when\": {\n              \"key\": \"ContentLinkType\",\n              \"value\": 4\n            },\n            \"sheet\": \"GoldSaucerContent\"\n          },\n          {\n            \"when\": {\n              \"key\": \"ContentLinkType\",\n              \"value\": 5\n            },\n            \"sheet\": \"QuestBattle\"\n          }\n        ]\n      }\n    },\n    {\n      \"index\": 4,\n      \"name\": \"PvP\"\n    },\n    {\n      \"index\": 9,\n      \"name\": \"AcceptClassJobCategory\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"ClassJobCategory\"\n      }\n    },\n    {\n      \"index\": 10,\n      \"name\": \"ContentMemberType\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"ContentMemberType\"\n      }\n    },\n    {\n      \"index\": 14,\n      \"name\": \"UnlockQuest\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"Quest\"\n      }\n    },\n    {\n      \"index\": 17,\n      \"name\": \"ClassJobLevel{Required}\"\n    },\n    {\n      \"index\": 18,\n      \"name\": \"ClassJobLevel{Sync}\"\n    },\n    {\n      \"index\": 19,\n      \"name\": \"ItemLevel{Required}\"\n    },\n    {\n      \"index\": 20,\n      \"name\": \"ItemLevel{Sync}\"\n    },\n    {\n      \"index\": 22,\n      \"name\": \"AllowUndersized\"\n    },\n    {\n      \"index\": 25,\n      \"name\": \"AllowReplacement\"\n    },\n    {\n      \"index\": 27,\n      \"name\": \"AllowExplorerMode\"\n    },\n    {\n      \"index\": 32,\n      \"name\": \"HighEndDuty\"\n    },\n    {\n      \"index\": 38,\n      \"name\": \"DutyRecorderAllowed\"\n    },\n    {\n      \"index\": 43,\n      \"name\": \"Name\"\n    },\n    {\n      \"index\": 44,\n      \"name\": \"NameShort\"\n    },\n    {\n      \"index\": 45,\n      \"name\": \"ContentType\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"ContentType\"\n      }\n    },\n    {\n      \"index\": 46,\n      \"name\": \"TransientKey\"\n    },\n    {\n      \"index\": 48,\n      \"name\": \"Transient\",\n      \"converter\": {\n        \"type\": \"complexlink\",\n        \"links\": [\n          {\n            \"when\": {\n              \"key\": \"TransientKey\",\n              \"value\": 6\n            },\n            \"sheet\": \"ContentFinderConditionTransient\"\n          },\n          {\n            \"when\": {\n              \"key\": \"TransientKey\",\n              \"value\": 7\n            },\n            \"sheet\": \"ContentFinderConditionTransient\"\n          }\n        ]\n      }\n    },\n    {\n      \"index\": 49,\n      \"name\": \"SortKey\"\n    },\n    {\n      \"index\": 50,\n      \"name\": \"Image\",\n      \"converter\": {\n        \"type\": \"icon\"\n      }\n    },\n    {\n      \"index\": 51,\n      \"name\": \"Icon\",\n      \"converter\": {\n        \"type\": \"icon\"\n      }\n    },\n    {\n      \"index\": 57,\n      \"name\": \"LevelingRoulette\"\n    },\n    {\n      \"index\": 58,\n      \"name\": \"HighLevelRoulette\"\n    },\n    {\n      \"index\": 59,\n      \"name\": \"MSQRoulette\"\n    },\n    {\n      \"index\": 60,\n      \"name\": \"GuildHestRoulette\"\n    },\n    {\n      \"index\": 61,\n      \"name\": \"ExpertRoulette\"\n    },\n    {\n      \"index\": 62,\n      \"name\": \"TrialRoulette\"\n    },\n    {\n      \"index\": 63,\n      \"name\": \"DailyFrontlineChallenge\"\n    },\n    {\n      \"index\": 64,\n      \"name\": \"LevelCapRoulette\"\n    },\n    {\n      \"index\": 65,\n      \"name\": \"MentorRoulette\"\n    },\n    {\n      \"index\": 71,\n      \"name\": \"AllianceRoulette\"\n    },\n    {\n      \"index\": 72,\n      \"name\": \"FeastTeamRoulette\"\n    },\n    {\n      \"index\": 73,\n      \"name\": \"NormalRaidRoulette\"\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/ContentFinderConditionTransient.json",
    "content": "{\n  \"sheet\": \"ContentFinderConditionTransient\",\n  \"defaultColumn\": \"Description\",\n  \"definitions\": [\n    {\n      \"name\": \"Description\"\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/ContentGauge.json",
    "content": "{\n  \"sheet\": \"ContentGauge\",\n  \"definitions\": [\n    {\n      \"name\": \"Name\",\n      \"index\": 1\n    },\n    {\n      \"index\": 2,\n      \"name\": \"Color\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"ContentGaugeColor\"\n      }\n    },\n    {\n      \"index\": 4,\n      \"name\": \"TextString\"\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/ContentGaugeColor.json",
    "content": "{\n  \"sheet\": \"ContentGaugeColor\",\n  \"definitions\": [\n    {\n      \"name\": \"AndroidColor{1}\"\n    },\n    {\n      \"index\": 1,\n      \"name\": \"AndroidColor{2}\"\n    },\n    {\n      \"index\": 2,\n      \"name\": \"AndroidColor{3}\"\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/ContentMemberType.json",
    "content": "{\n  \"sheet\": \"ContentMemberType\",\n  \"defaultColumn\": \"InstanceContent\",\n  \"definitions\": [\n    {\n      \"index\": 10,\n      \"name\": \"TanksPerParty\"\n    },\n    {\n      \"index\": 11,\n      \"name\": \"HealersPerParty\"\n    },\n    {\n      \"index\": 12,\n      \"name\": \"MeleesPerParty\"\n    },\n    {\n      \"index\": 13,\n      \"name\": \"RangedPerParty\"\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/ContentNpc.json",
    "content": "{\n  \"sheet\": \"ContentNpc\",\n  \"definitions\": []\n}"
  },
  {
    "path": "SaintCoinach/Definitions/ContentNpcTalk.json",
    "content": "{\n  \"sheet\": \"ContentNpcTalk\",\n  \"definitions\": [\n    {\n      \"name\": \"Type\"\n    },\n    {\n      \"index\": 1,\n      \"type\": \"repeat\",\n      \"count\": 8,\n      \"definition\": {\n        \"name\": \"ContentTalk\",\n        \"converter\": {\n          \"type\": \"link\",\n          \"target\": \"ContentTalk\"\n        }\n      }\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/ContentRandomSelect.json",
    "content": "{\n  \"sheet\": \"ContentRandomSelect\",\n  \"defaultColumn\": \"Name\",\n  \"definitions\": [\n    {\n      \"name\": \"Name\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"ContentFinderCondition\"\n      }\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/ContentRoulette.json",
    "content": "{\n  \"sheet\": \"ContentRoulette\",\n  \"defaultColumn\": \"Name\",\n  \"definitions\": [\n    {\n      \"name\": \"Name\"\n    },\n    {\n      \"index\": 1,\n      \"name\": \"Category\"\n    },\n    {\n      \"index\": 3,\n      \"name\": \"Description\"\n    },\n    {\n      \"index\": 4,\n      \"name\": \"DutyType\"\n    },\n    {\n      \"index\": 7,\n      \"name\": \"IsGoldSaucer\"\n    },\n    {\n      \"index\": 8,\n      \"name\": \"IsInDutyFinder\"\n    },\n    {\n      \"index\": 9,\n      \"name\": \"OpenRule\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"ContentRouletteOpenRule\"\n      }\n    },\n    {\n      \"index\": 10,\n      \"name\": \"IsPvP\"\n    },\n    {\n      \"index\": 11,\n      \"name\": \"RequiredLevel\"\n    },\n    {\n      \"index\": 13,\n      \"name\": \"ItemLevel{Required}\"\n    },\n    {\n      \"index\": 16,\n      \"name\": \"Icon\",\n      \"converter\": {\n        \"type\": \"icon\"\n      }\n    },\n    {\n      \"index\": 17,\n      \"name\": \"ContentRouletteRoleBonus\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"ContentRouletteRoleBonus\"\n      }\n    },\n    {\n      \"index\": 18,\n      \"name\": \"Reward{TomeA}\"\n    },\n    {\n      \"index\": 19,\n      \"name\": \"Reward{TomeB}\"\n    },\n    {\n      \"index\": 20,\n      \"name\": \"Reward{TomeC}\"\n    },\n    {\n      \"index\": 24,\n      \"name\": \"SortKey\"\n    },\n    {\n      \"index\": 26,\n      \"name\": \"ContentMemberType\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"ContentMemberType\"\n      }\n    },\n    {\n      \"index\": 37,\n      \"name\": \"RequireAllDuties\"\n    },\n    {\n      \"index\": 39,\n      \"name\": \"ContentRouletteOpenRule\"\n    },\n    {\n      \"index\": 40,\n      \"name\": \"InstanceContent\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"InstanceContent\"\n      }\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/ContentRouletteOpenRule.json",
    "content": "{\n  \"sheet\": \"ContentRouletteOpenRule\",\n  \"definitions\": [\n    {\n      \"index\": 1,\n      \"name\": \"Type\"\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/ContentRouletteRoleBonus.json",
    "content": "{\n  \"sheet\": \"ContentRouletteRoleBonus\",\n  \"definitions\": [\n    {\n      \"index\": 6,\n      \"name\": \"ItemRewardType\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"Item\"\n      }\n    },\n    {\n      \"index\": 7,\n      \"name\": \"RewardAmount\"\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/ContentTalk.json",
    "content": "{\n  \"sheet\": \"ContentTalk\",\n  \"defaultColumn\": \"Text\",\n  \"definitions\": [\n    {\n      \"name\": \"ContentTalkParam\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"ContentTalkParam\"\n      }\n    },\n    {\n      \"index\": 1,\n      \"name\": \"Text\"\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/ContentTalkParam.json",
    "content": "{\n  \"sheet\": \"ContentTalkParam\",\n  \"definitions\": [\n    {\n      \"name\": \"Param\"\n    },\n    {\n      \"index\": 2,\n      \"name\": \"TestAction\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"ActionTimeline\"\n      }\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/ContentType.json",
    "content": "{\n  \"sheet\": \"ContentType\",\n  \"defaultColumn\": \"Name\",\n  \"definitions\": [\n    {\n      \"name\": \"Name\"\n    },\n    {\n      \"index\": 1,\n      \"name\": \"Icon\",\n      \"converter\": {\n        \"type\": \"icon\"\n      }\n    },\n    {\n      \"index\": 2,\n      \"name\": \"Icon{DutyFinder}\",\n      \"converter\": {\n        \"type\": \"icon\"\n      }\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/ContentsNote.json",
    "content": "{\n  \"sheet\": \"ContentsNote\",\n  \"definitions\": [\n    {\n      \"name\": \"ContentType\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"ContentsNoteCategory\"\n      }\n    },\n    {\n      \"index\": 1,\n      \"name\": \"Icon\",\n      \"converter\": {\n        \"type\": \"icon\"\n      }\n    },\n    {\n      \"index\": 2,\n      \"name\": \"MenuOrder\"\n    },\n    {\n      \"index\": 3,\n      \"name\": \"RequiredAmount\"\n    },\n    {\n      \"index\": 4,\n      \"name\": \"Reward[0]\"\n    },\n    {\n      \"index\": 5,\n      \"name\": \"ExpMultiplier\"\n    },\n    {\n      \"index\": 6,\n      \"name\": \"Reward[1]\"\n    },\n    {\n      \"index\": 7,\n      \"name\": \"GilRward\"\n    },\n    {\n      \"index\": 8,\n      \"name\": \"LevelUnlock\"\n    },\n    {\n      \"index\": 9,\n      \"name\": \"HowTo\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"HowTo\"\n      }\n    },\n    {\n      \"index\": 10,\n      \"name\": \"ReqUnlock\",\n      \"converter\": {\n        \"type\": \"multiref\",\n        \"targets\": [\n          \"Quest\"\n        ]\n      }\n    },\n    {\n      \"index\": 11,\n      \"name\": \"Name\"\n    },\n    {\n      \"index\": 12,\n      \"name\": \"Description\"\n    },\n    {\n      \"index\": 13,\n      \"name\": \"ExpCap\"\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/ContentsTutorial.json",
    "content": "{\n  \"sheet\": \"ContentsTutorial\",\n  \"defaultColumn\": \"Name\",\n  \"definitions\": [\n    {\n      \"type\": \"repeat\",\n      \"count\": 8,\n      \"definition\": {\n        \"name\": \"Page\",\n        \"converter\": {\n          \"type\": \"link\",\n          \"target\": \"ContentsTutorialPage\"\n        }\n      }\n    },\n    {\n      \"index\": 8,\n      \"name\": \"Name\"\n    },\n    {\n      \"index\": 9,\n      \"name\": \"Description\"\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/ContentsTutorialPage.json",
    "content": "{\n  \"sheet\": \"ContentsTutorialPage\",\n  \"definitions\": [\n    {\n      \"name\": \"Image\",\n      \"converter\": {\n        \"type\": \"icon\"\n      }\n    },\n    {\n      \"index\": 1,\n      \"name\": \"Description\"\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/CraftAction.json",
    "content": "{\n  \"sheet\": \"CraftAction\",\n  \"defaultColumn\": \"Name\",\n  \"definitions\": [\n    {\n      \"name\": \"Name\"\n    },\n    {\n      \"index\": 1,\n      \"name\": \"Description\"\n    },\n    {\n      \"index\": 2,\n      \"name\": \"Animation{Start}\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"ActionTimeline\"\n      }\n    },\n    {\n      \"index\": 3,\n      \"name\": \"Animation{End}\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"ActionTimeline\"\n      }\n    },\n    {\n      \"index\": 4,\n      \"name\": \"Icon\",\n      \"converter\": {\n        \"type\": \"icon\"\n      }\n    },\n    {\n      \"index\": 5,\n      \"name\": \"ClassJob\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"ClassJob\"\n      }\n    },\n    {\n      \"index\": 6,\n      \"name\": \"ClassJobCategory\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"ClassJobCategory\"\n      }\n    },\n    {\n      \"index\": 7,\n      \"name\": \"ClassJobLevel\"\n    },\n    {\n      \"index\": 8,\n      \"name\": \"QuestRequirement\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"Quest\"\n      }\n    },\n    {\n      \"index\": 9,\n      \"name\": \"Specialist\"\n    },\n    {\n      \"index\": 11,\n      \"name\": \"Cost\"\n    },\n    {\n      \"index\": 12,\n      \"name\": \"CRP\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"CraftAction\"\n      }\n    },\n    {\n      \"index\": 13,\n      \"name\": \"BSM\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"CraftAction\"\n      }\n    },\n    {\n      \"index\": 14,\n      \"name\": \"ARM\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"CraftAction\"\n      }\n    },\n    {\n      \"index\": 15,\n      \"name\": \"GSM\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"CraftAction\"\n      }\n    },\n    {\n      \"index\": 16,\n      \"name\": \"LTW\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"CraftAction\"\n      }\n    },\n    {\n      \"index\": 17,\n      \"name\": \"WVR\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"CraftAction\"\n      }\n    },\n    {\n      \"index\": 18,\n      \"name\": \"ALC\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"CraftAction\"\n      }\n    },\n    {\n      \"index\": 19,\n      \"name\": \"CUL\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"CraftAction\"\n      }\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/CraftLeve.json",
    "content": "{\n  \"sheet\": \"CraftLeve\",\n  \"defaultColumn\": \"Leve\",\n  \"isGenericReferenceTarget\": true,\n  \"definitions\": [\n    {\n      \"name\": \"Leve\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"Leve\"\n      }\n    },\n    {\n      \"index\": 1,\n      \"name\": \"CraftLeveTalk\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"CraftLeveTalk\"\n      }\n    },\n    {\n      \"index\": 2,\n      \"name\": \"Repeats\"\n    },\n    {\n      \"index\": 3,\n      \"type\": \"repeat\",\n      \"count\": 4,\n      \"definition\": {\n        \"type\": \"group\",\n        \"members\": [\n          {\n            \"name\": \"Item\",\n            \"converter\": {\n              \"type\": \"link\",\n              \"target\": \"Item\"\n            }\n          },\n          {\n            \"name\": \"ItemCount\"\n          }\n        ]\n      }\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/CraftLeveTalk.json",
    "content": "{\n  \"sheet\": \"CraftLeveTalk\",\n  \"definitions\": [\n    {\n      \"index\": 36,\n      \"type\": \"repeat\",\n      \"count\": 6,\n      \"definition\": {\n        \"name\": \"Talk\"\n      }\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/CraftLevelDifference.json",
    "content": "{\n  \"sheet\": \"CraftLevelDifference\",\n  \"definitions\": [\n    {\n      \"name\": \"Difference\"\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/CraftType.json",
    "content": "{\n  \"sheet\": \"CraftType\",\n  \"defaultColumn\": \"Name\",\n  \"definitions\": [\n    {\n      \"name\": \"MainPhysical\"\n    },\n    {\n      \"index\": 1,\n      \"name\": \"SubPhysical\"\n    },\n    {\n      \"index\": 2,\n      \"name\": \"Name\"\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/Credit.json",
    "content": "{\n  \"sheet\": \"Credit\",\n  \"definitions\": [\n    {\n      \"index\": 1,\n      \"name\": \"Roles(1)\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"CreditCast\"\n      }\n    },\n    {\n      \"index\": 2,\n      \"name\": \"JapaneseCast(1)\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"CreditCast\"\n      }\n    },\n    {\n      \"index\": 3,\n      \"name\": \"EnglishCast(1)\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"CreditCast\"\n      }\n    },\n    {\n      \"index\": 4,\n      \"name\": \"FrenchCast(1)\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"CreditCast\"\n      }\n    },\n    {\n      \"index\": 5,\n      \"name\": \"GermanCast(1)\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"CreditCast\"\n      }\n    },\n    {\n      \"index\": 6,\n      \"name\": \"Roles(2)\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"CreditCast\"\n      }\n    },\n    {\n      \"index\": 7,\n      \"name\": \"JapaneseCast(2)\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"CreditCast\"\n      }\n    },\n    {\n      \"index\": 8,\n      \"name\": \"EnglishCast(2)\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"CreditCast\"\n      }\n    },\n    {\n      \"index\": 9,\n      \"name\": \"FrenchCast(2)\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"CreditCast\"\n      }\n    },\n    {\n      \"index\": 10,\n      \"name\": \"GermanCast(2)\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"CreditCast\"\n      }\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/CreditBackImage.json",
    "content": "{\n  \"sheet\": \"CreditBackImage\",\n  \"definitions\": [\n    {\n      \"index\": 5,\n      \"name\": \"BackImage\",\n      \"converter\": {\n        \"type\": \"icon\"\n      }\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/CreditCast.json",
    "content": "{\n  \"sheet\": \"CreditCast\",\n  \"defaultColumn\": \"Name\",\n  \"definitions\": [\n    {\n      \"name\": \"Name\"\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/CreditList.json",
    "content": "{\n  \"sheet\": \"CreditList\",\n  \"definitions\": [\n    {\n      \"name\": \"Scale\"\n    },\n    {\n      \"index\": 1,\n      \"name\": \"Icon\",\n      \"converter\": {\n        \"type\": \"icon\"\n      }\n    },\n    {\n      \"index\": 2,\n      \"name\": \"Font\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"CreditFont\"\n      }\n    },\n    {\n      \"index\": 5,\n      \"name\": \"Cast\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"CreditListText\"\n      }\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/CreditListText.json",
    "content": "{\n  \"sheet\": \"CreditListText\",\n  \"defaultColumn\": \"Name\",\n  \"definitions\": [\n    {\n      \"name\": \"Name\"\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/CustomTalk.json",
    "content": "{\n  \"sheet\": \"CustomTalk\",\n  \"defaultColumn\": \"Name\",\n  \"isGenericReferenceTarget\": true,\n  \"definitions\": [\n    {\n      \"name\": \"Icon{Actor}\",\n      \"converter\": {\n        \"type\": \"icon\"\n      }\n    },\n    {\n      \"index\": 1,\n      \"name\": \"Icon{Map}\",\n      \"converter\": {\n        \"type\": \"icon\"\n      }\n    },\n    {\n      \"index\": 2,\n      \"name\": \"Name\"\n    },\n    {\n      \"index\": 3,\n      \"type\": \"repeat\",\n      \"count\": 30,\n      \"definition\": {\n        \"name\": \"Script{Instruction}\"\n      }\n    },\n    {\n      \"index\": 33,\n      \"type\": \"repeat\",\n      \"count\": 30,\n      \"definition\": {\n        \"name\": \"Script{Arg}\"\n      }\n    },\n    {\n      \"index\": 64,\n      \"name\": \"MainOption\"\n    },\n    {\n      \"index\": 65,\n      \"name\": \"SubOption\"\n    },\n    {\n      \"index\": 75,\n      \"name\": \"SpecialLinks\",\n      \"converter\": {\n        \"type\": \"multiref\",\n        \"targets\": [\n          \"CollectablesShop\",\n          \"ContentEntry\",\n          \"SpecialShop\",\n          \"CustomTalkNestHandlers\"\n        ]\n      }\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/CustomTalkDefineClient.json",
    "content": "{\n  \"sheet\": \"CustomTalkDefineClient\",\n  \"definitions\": []\n}"
  },
  {
    "path": "SaintCoinach/Definitions/CustomTalkNestHandlers.json",
    "content": "{\n  \"sheet\": \"CustomTalkNestHandlers\",\n  \"definitions\": [\n    {\n      \"name\": \"NestHandler\",\n      \"converter\": {\n        \"type\": \"multiref\",\n        \"targets\": [\n          \"GilShop\",\n          \"SpecialShop\",\n          \"DisposalShop\",\n          \"ContentEntry\"\n        ]\n      }\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/CutSceneIncompQuest.json",
    "content": "{\n  \"sheet\": \"CutSceneIncompQuest\",\n  \"defaultColumn\": \"Quest\",\n  \"definitions\": [\n    {\n      \"name\": \"Quest\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"Quest\"\n      }\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/CutScreenImage.json",
    "content": "{\n  \"sheet\": \"CutScreenImage\",\n  \"definitions\": [\n    {\n      \"name\": \"Type\"\n    },\n    {\n      \"index\": 1,\n      \"name\": \"Image\"\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/Cutscene.json",
    "content": "{\n  \"sheet\": \"Cutscene\",\n  \"defaultColumn\": \"Path\",\n  \"definitions\": [\n    {\n      \"name\": \"Path\"\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/CutsceneMotion.json",
    "content": "{\n  \"sheet\": \"CutsceneMotion\",\n  \"definitions\": [\n    {\n      \"name\": \"WALK_LOOP_SPEED\"\n    },\n    {\n      \"index\": 1,\n      \"name\": \"RUN_LOOP_SPEED\"\n    },\n    {\n      \"index\": 2,\n      \"name\": \"SLOWWALK_LOOP_SPEED\"\n    },\n    {\n      \"index\": 3,\n      \"name\": \"SLOWRUN_LOOP_SPEED\"\n    },\n    {\n      \"index\": 4,\n      \"name\": \"BATTLEWALK_LOOP_SPEED\"\n    },\n    {\n      \"index\": 5,\n      \"name\": \"BATTLERUN_LOOP_SPEED\"\n    },\n    {\n      \"index\": 6,\n      \"name\": \"DASH_LOOP_SPEED\"\n    },\n    {\n      \"index\": 7,\n      \"name\": \"TURN_CW90_FRAME\"\n    },\n    {\n      \"index\": 8,\n      \"name\": \"TURN_CCW90_FRAME\"\n    },\n    {\n      \"index\": 9,\n      \"name\": \"TURN_CW180_FRAME\"\n    },\n    {\n      \"index\": 10,\n      \"name\": \"TURN_CCW180_FRAME\"\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/CutsceneWorkIndex.json",
    "content": "{\n  \"sheet\": \"CutsceneWorkIndex\",\n  \"definitions\": [\n    {\n      \"name\": \"WorkIndex\"\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/CycleTime.json",
    "content": "{\n  \"sheet\": \"CycleTime\",\n  \"definitions\": [\n    {\n      \"name\": \"FirstCycle\"\n    },\n    {\n      \"index\": 1,\n      \"name\": \"Cycle\"\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/DailySupplyItem.json",
    "content": "{\n  \"sheet\": \"DailySupplyItem\",\n  \"definitions\": [\n    {\n      \"type\": \"repeat\",\n      \"count\": 8,\n      \"definition\": {\n        \"type\": \"group\",\n        \"members\": [\n          {\n            \"name\": \"Item\",\n            \"converter\": {\n              \"type\": \"link\",\n              \"target\": \"Item\"\n            }\n          },\n          {\n            \"name\": \"Quantity\"\n          },\n          {\n            \"name\": \"RecipeLevel\"\n          }\n        ]\n      }\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/DawnContent.json",
    "content": "{\n  \"sheet\": \"DawnContent\",\n  \"defaultColumn\": \"Content\",\n  \"definitions\": [\n    {\n      \"name\": \"Content\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"ContentFinderCondition\"\n      }\n    },\n    {\n      \"index\": 4,\n      \"name\": \"Exp{BelowExMaxLvl}\"\n    },\n    {\n      \"index\": 5,\n      \"name\": \"Exp{AboveExMaxLvl}\"\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/DawnContentParticipable.json",
    "content": "{\n  \"sheet\": \"DawnContentParticipable\",\n  \"definitions\": []\n}"
  },
  {
    "path": "SaintCoinach/Definitions/DawnGrowMember.json",
    "content": "{\n  \"sheet\": \"DawnGrowMember\",\n  \"defaultColumn\": \"Member\",\n  \"definitions\": [\n    {\n      \"index\": 0,\n      \"type\": \"repeat\",\n      \"count\": 4,\n      \"definition\": {\n        \"name\": \"SelectImage\",\n        \"converter\": {\n          \"type\": \"icon\"\n        }\n      }\n    },\n    {\n      \"index\": 4,\n      \"type\": \"repeat\",\n      \"count\": 4,\n      \"definition\": {\n        \"name\": \"PortraitImage\",\n        \"converter\": {\n          \"type\": \"icon\"\n        }\n      }\n    },\n    {\n      \"index\": 8,\n      \"name\": \"Class\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"DawnMemberUIParam\"\n      }\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/DawnMember.json",
    "content": "{\n  \"sheet\": \"DawnMember\",\n  \"definitions\": []\n}"
  },
  {
    "path": "SaintCoinach/Definitions/DawnMemberUIParam.json",
    "content": "{\n  \"sheet\": \"DawnMemberUIParam\",\n  \"defaultColumn\": \"Class{Singular}\",\n  \"definitions\": [\n    {\n      \"name\": \"Class{Singular}\"\n    },\n    {\n      \"index\": 2,\n      \"name\": \"VoiceLine\"\n    },\n    {\n      \"index\": 3,\n      \"name\": \"Class{Plural}\"\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/DawnQuestMember.json",
    "content": "{\n  \"sheet\": \"DawnQuestMember\",\n  \"defaultColumn\": \"Member\",\n  \"definitions\": [\n    {\n      \"index\": 2,\n      \"name\": \"Member\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"ENpcResident\"\n      }\n    },\n    {\n      \"index\": 3,\n      \"name\": \"BigImage{Old}\",\n      \"converter\": {\n        \"type\": \"icon\"\n      }\n    },\n    {\n      \"index\": 4,\n      \"name\": \"BigImage{New}\",\n      \"converter\": {\n        \"type\": \"icon\"\n      }\n    },\n    {\n      \"index\": 5,\n      \"name\": \"Class\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"DawnMemberUIParam\"\n      }\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/DeepDungeon.json",
    "content": "{\n  \"sheet\": \"DeepDungeon\",\n  \"defaultColumn\": \"Name\",\n  \"definitions\": [\n    {\n      \"name\": \"AetherpoolArm\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"DeepDungeonEquipment\"\n      }\n    },\n    {\n      \"index\": 1,\n      \"name\": \"AetherpoolArmor\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"DeepDungeonEquipment\"\n      }\n    },\n    {\n      \"index\": 2,\n      \"type\": \"repeat\",\n      \"count\": 16,\n      \"definition\": {\n        \"name\": \"PomanderSlot\",\n        \"converter\": {\n          \"type\": \"link\",\n          \"target\": \"DeepDungeonItem\"\n        }\n      }\n    },\n    {\n      \"index\": 18,\n      \"name\": \"DeepDungeonType\"\n    },\n    {\n      \"index\": 19,\n      \"type\": \"repeat\",\n      \"count\": 4,\n      \"definition\": {\n        \"name\": \"StoneSlot\",\n        \"converter\": {\n          \"type\": \"complexlink\",\n          \"links\": [\n            {\n              \"when\": {\n                \"key\": \"DeepDungeonType\",\n                \"value\": 1\n              },\n              \"sheet\": \"DeepDungeonMagicStone\"\n            },\n            {\n              \"when\": {\n                \"key\": \"DeepDungeonType\",\n                \"value\": 2\n              },\n              \"sheet\": \"DeepDungeonDemiclone\"\n            }\n          ]\n        }\n      }\n    },\n    {\n      \"index\": 23,\n      \"name\": \"Name\"\n    },\n    {\n      \"index\": 24,\n      \"name\": \"ContentFinderCondition{Start}\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"ContentFinderCondition\"\n      }\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/DeepDungeonBan.json",
    "content": "{\n  \"sheet\": \"DeepDungeonBan\",\n  \"defaultColumn\": \"Name\",\n  \"definitions\": [\n    {\n      \"name\": \"ScreenImage\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"ScreenImage\"\n      }\n    },\n    {\n      \"index\": 1,\n      \"name\": \"LogMessage\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"LogMessage\"\n      }\n    },\n    {\n      \"index\": 2,\n      \"name\": \"Name\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"DeepDungeonFloorEffectUI\"\n      }\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/DeepDungeonDanger.json",
    "content": "{\n  \"sheet\": \"DeepDungeonDanger\",\n  \"definitions\": [\n    {\n      \"name\": \"ScreenImage\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"ScreenImage\"\n      }\n    },\n    {\n      \"index\": 1,\n      \"name\": \"LogMessage\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"LogMessage\"\n      }\n    },\n    {\n      \"index\": 2,\n      \"name\": \"Name\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"DeepDungeonFloorEffectUI\"\n      }\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/DeepDungeonDemiclone.json",
    "content": "{\n  \"sheet\": \"DeepDungeonDemiclone\",\n  \"defaultColumn\": \"TitleCase\",\n  \"definitions\": [\n    {\n      \"index\": 0,\n      \"name\": \"Icon\",\n      \"converter\": {\n        \"type\": \"icon\"\n      }\n    },\n    {\n      \"index\": 1,\n      \"name\": \"Singular\"\n    },\n    {\n      \"index\": 3,\n      \"name\": \"Plural\"\n    },\n    {\n      \"index\": 9,\n      \"name\": \"TitleCase\"\n    },\n    {\n      \"index\": 10,\n      \"name\": \"Description\"\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/DeepDungeonEquipment.json",
    "content": "{\n  \"sheet\": \"DeepDungeonEquipment\",\n  \"defaultColumn\": \"Name\",\n  \"definitions\": [\n    {\n      \"name\": \"Icon\",\n      \"converter\": {\n        \"type\": \"icon\"\n      }\n    },\n    {\n      \"index\": 1,\n      \"name\": \"Singular\"\n    },\n    {\n      \"index\": 2,\n      \"name\": \"Adjective\"\n    },\n    {\n      \"index\": 3,\n      \"name\": \"Plural\"\n    },\n    {\n      \"index\": 4,\n      \"name\": \"PossessivePronoun\"\n    },\n    {\n      \"index\": 5,\n      \"name\": \"StartsWithVowel\"\n    },\n    {\n      \"index\": 7,\n      \"name\": \"Pronoun\"\n    },\n    {\n      \"index\": 8,\n      \"name\": \"Article\"\n    },\n    {\n      \"index\": 9,\n      \"name\": \"Name\"\n    },\n    {\n      \"index\": 10,\n      \"name\": \"Description\"\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/DeepDungeonFloorEffectUI.json",
    "content": "{\n  \"sheet\": \"DeepDungeonFloorEffectUI\",\n  \"defaultColumn\": \"Name\",\n  \"definitions\": [\n    {\n      \"name\": \"Icon\",\n      \"converter\": {\n        \"type\": \"icon\"\n      }\n    },\n    {\n      \"index\": 1,\n      \"name\": \"Name\"\n    },\n    {\n      \"index\": 2,\n      \"name\": \"Description\"\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/DeepDungeonItem.json",
    "content": "{\n  \"sheet\": \"DeepDungeonItem\",\n  \"defaultColumn\": \"Name\",\n  \"definitions\": [\n    {\n      \"name\": \"Icon\",\n      \"converter\": {\n        \"type\": \"icon\"\n      }\n    },\n    {\n      \"index\": 1,\n      \"name\": \"Singular\"\n    },\n    {\n      \"index\": 2,\n      \"name\": \"Adjective\"\n    },\n    {\n      \"index\": 3,\n      \"name\": \"Plural\"\n    },\n    {\n      \"index\": 4,\n      \"name\": \"PossessivePronoun\"\n    },\n    {\n      \"index\": 5,\n      \"name\": \"StartsWithVowel\"\n    },\n    {\n      \"index\": 7,\n      \"name\": \"Pronoun\"\n    },\n    {\n      \"index\": 8,\n      \"name\": \"Article\"\n    },\n    {\n      \"index\": 9,\n      \"name\": \"Name\"\n    },\n    {\n      \"index\": 10,\n      \"name\": \"Tooltip\"\n    },\n    {\n      \"index\": 11,\n      \"name\": \"Action\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"Action\"\n      }\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/DeepDungeonLayer.json",
    "content": "{\n  \"sheet\": \"DeepDungeonLayer\",\n  \"definitions\": [\n    {\n      \"name\": \"DeepDungeon\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"DeepDungeon\"\n      }\n    },\n    {\n      \"index\": 1,\n      \"name\": \"FloorSet\"\n    },\n    {\n      \"index\": 2,\n      \"name\": \"RoomA\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"DeepDungeonMap5X\"\n      }\n    },\n    {\n      \"index\": 3,\n      \"name\": \"RoomB\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"DeepDungeonMap5X\"\n      }\n    },\n    {\n      \"index\": 4,\n      \"name\": \"RoomC\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"DeepDungeonMap5X\"\n      }\n    },\n    {\n      \"index\": 5,\n      \"name\": \"WepMinLv\"\n    },\n    {\n      \"index\": 6,\n      \"name\": \"ArmourMinLv\"\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/DeepDungeonMagicStone.json",
    "content": "{\n  \"sheet\": \"DeepDungeonMagicStone\",\n  \"defaultColumn\": \"Name\",\n  \"definitions\": [\n    {\n      \"name\": \"Icon\",\n      \"converter\": {\n        \"type\": \"icon\"\n      }\n    },\n    {\n      \"index\": 1,\n      \"name\": \"Singular\"\n    },\n    {\n      \"index\": 2,\n      \"name\": \"Adjective\"\n    },\n    {\n      \"index\": 3,\n      \"name\": \"Plural\"\n    },\n    {\n      \"index\": 4,\n      \"name\": \"PossessivePronoun\"\n    },\n    {\n      \"index\": 5,\n      \"name\": \"StartsWithVowel\"\n    },\n    {\n      \"index\": 7,\n      \"name\": \"Pronoun\"\n    },\n    {\n      \"index\": 8,\n      \"name\": \"Article\"\n    },\n    {\n      \"index\": 9,\n      \"name\": \"Name\"\n    },\n    {\n      \"index\": 10,\n      \"name\": \"Tooltip\"\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/DeepDungeonMap5X.json",
    "content": "{\n  \"sheet\": \"DeepDungeonMap5X\",\n  \"definitions\": [\n    {\n      \"type\": \"repeat\",\n      \"count\": 5,\n      \"definition\": {\n        \"name\": \"DeepDungeonRoom\",\n        \"converter\": {\n          \"type\": \"link\",\n          \"target\": \"DeepDungeonRoom\"\n        }\n      }\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/DeepDungeonRoom.json",
    "content": "{\n  \"sheet\": \"DeepDungeonRoom\",\n  \"definitions\": [\n    {\n      \"type\": \"repeat\",\n      \"count\": 5,\n      \"definition\": {\n        \"name\": \"Level\",\n        \"converter\": {\n          \"type\": \"link\",\n          \"target\": \"Level\"\n        }\n      }\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/DeepDungeonStatus.json",
    "content": "{\n  \"sheet\": \"DeepDungeonStatus\",\n  \"definitions\": [\n    {\n      \"name\": \"ScreenImage\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"ScreenImage\"\n      }\n    },\n    {\n      \"index\": 1,\n      \"name\": \"LogMessage\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"LogMessage\"\n      }\n    },\n    {\n      \"index\": 2,\n      \"name\": \"Name\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"DeepDungeonFloorEffectUI\"\n      }\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/DefaultTalk.json",
    "content": "{\n  \"sheet\": \"DefaultTalk\",\n  \"isGenericReferenceTarget\": true,\n  \"definitions\": [\n    {\n      \"index\": 5,\n      \"type\": \"repeat\",\n      \"count\": 3,\n      \"definition\": {\n        \"name\": \"ActionTimeline{Pose}\",\n        \"converter\": {\n          \"type\": \"link\",\n          \"target\": \"ActionTimeline\"\n        }\n      }\n    },\n    {\n      \"index\": 20,\n      \"type\": \"repeat\",\n      \"count\": 3,\n      \"definition\": {\n        \"name\": \"Text\"\n      }\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/DefaultTalkLipSyncType.json",
    "content": "{\n  \"sheet\": \"DefaultTalkLipSyncType\",\n  \"defaultColumn\": \"ActionTimeline\",\n  \"definitions\": [\n    {\n      \"name\": \"ActionTimeline\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"ActionTimeline\"\n      }\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/DeliveryQuest.json",
    "content": "{\n  \"sheet\": \"DeliveryQuest\",\n  \"defaultColumn\": \"Quest\",\n  \"definitions\": [\n    {\n      \"name\": \"Quest\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"Quest\"\n      }\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/Description.json",
    "content": "{\n  \"sheet\": \"Description\",\n  \"defaultColumn\": \"Text[Long]\",\n  \"definitions\": [\n    {\n      \"index\": 1,\n      \"name\": \"Quest\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"Quest\"\n      }\n    },\n    {\n      \"index\": 2,\n      \"name\": \"Text[Long]\"\n    },\n    {\n      \"index\": 3,\n      \"name\": \"Text[Short]\"\n    },\n    {\n      \"index\": 4,\n      \"name\": \"Text[Commentary]\"\n    },\n    {\n      \"index\": 6,\n      \"name\": \"Section\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"DescriptionSection\"\n      }\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/DescriptionPage.json",
    "content": "{\n  \"sheet\": \"DescriptionPage\",\n  \"definitions\": [\n    {\n      \"index\": 1,\n      \"name\": \"Quest\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"Quest\"\n      }\n    },\n    {\n      \"index\": 3,\n      \"type\": \"repeat\",\n      \"count\": 11,\n      \"definition\": {\n        \"type\": \"group\",\n        \"members\": [\n          {\n            \"name\": \"Text\",\n            \"converter\": {\n              \"type\": \"link\",\n              \"target\": \"DescriptionString\"\n            }\n          },\n          {\n            \"name\": \"Image\",\n            \"converter\": {\n              \"type\": \"icon\"\n            }\n          }\n        ]\n      }\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/DescriptionSection.json",
    "content": "{\n  \"sheet\": \"DescriptionSection\",\n  \"defaultColumn\": \"String\",\n  \"definitions\": [\n    {\n      \"name\": \"String\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"DescriptionString\"\n      }\n    },\n    {\n      \"index\": 1,\n      \"name\": \"Page\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"DescriptionPage\"\n      }\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/DescriptionString.json",
    "content": "{\n  \"sheet\": \"DescriptionString\",\n  \"defaultColumn\": \"Text\",\n  \"definitions\": [\n    {\n      \"name\": \"Text\"\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/DisposalShop.json",
    "content": "{\n  \"sheet\": \"DisposalShop\",\n  \"defaultColumn\": \"ShopName\",\n  \"definitions\": [\n    {\n      \"name\": \"ShopName\"\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/DisposalShopFilterType.json",
    "content": "{\n  \"sheet\": \"DisposalShopFilterType\",\n  \"defaultColumn\": \"Category\",\n  \"definitions\": [\n    {\n      \"name\": \"Category\"\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/DisposalShopItem.json",
    "content": "{\n  \"sheet\": \"DisposalShopItem\",\n  \"defaultColumn\": \"Item{Disposed}\",\n  \"definitions\": [\n    {\n      \"name\": \"Item{Disposed}\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"Item\"\n      }\n    },\n    {\n      \"index\": 2,\n      \"name\": \"Item{Received}\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"Item\"\n      }\n    },\n    {\n      \"index\": 4,\n      \"name\": \"Quantity{Received}\"\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/DpsChallenge.json",
    "content": "{\n  \"sheet\": \"DpsChallenge\",\n  \"defaultColumn\": \"Name\",\n  \"definitions\": [\n    {\n      \"name\": \"PlayerLevel\"\n    },\n    {\n      \"index\": 3,\n      \"name\": \"PlaceName\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"PlaceName\"\n      }\n    },\n    {\n      \"index\": 4,\n      \"name\": \"Icon\",\n      \"converter\": {\n        \"type\": \"icon\"\n      }\n    },\n    {\n      \"index\": 5,\n      \"name\": \"Order\"\n    },\n    {\n      \"index\": 6,\n      \"name\": \"Name\"\n    },\n    {\n      \"index\": 7,\n      \"name\": \"Description\"\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/DpsChallengeOfficer.json",
    "content": "{\n  \"sheet\": \"DpsChallengeOfficer\",\n  \"definitions\": [\n    {\n      \"name\": \"UnlockQuest\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"Quest\"\n      }\n    },\n    {\n      \"index\": 1,\n      \"type\": \"repeat\",\n      \"count\": 25,\n      \"definition\": {\n        \"name\": \"ChallengeName\",\n        \"converter\": {\n          \"type\": \"link\",\n          \"target\": \"DpsChallenge\"\n        }\n      }\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/DpsChallengeTransient.json",
    "content": "{\n  \"sheet\": \"DpsChallengeTransient\",\n  \"definitions\": [\n    {\n      \"name\": \"InstanceContent\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"InstanceContent\"\n      }\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/DynamicEvent.json",
    "content": "{\n  \"sheet\": \"DynamicEvent\",\n  \"defaultColumn\": \"Name\",\n  \"definitions\": [\n    {\n      \"name\": \"EventType\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"DynamicEventType\"\n      }\n    },\n    {\n      \"index\": 1,\n      \"name\": \"EnemyType\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"DynamicEventEnemyType\"\n      }\n    },\n    {\n      \"index\": 4,\n      \"name\": \"LGBEventObject\"\n    },\n    {\n      \"index\": 5,\n      \"name\": \"LGBMapRange\"\n    },\n    {\n      \"index\": 6,\n      \"name\": \"Quest\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"Quest\"\n      }\n    },\n    {\n      \"index\": 8,\n      \"name\": \"SingleBattle\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"DynamicEventSingleBattle\"\n      }\n    },\n    {\n      \"index\": 9,\n      \"name\": \"Announce\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"LogMessage\"\n      }\n    },\n    {\n      \"index\": 10,\n      \"name\": \"Name\"\n    },\n    {\n      \"index\": 11,\n      \"name\": \"Description\"\n    }\n  ]\n}\n"
  },
  {
    "path": "SaintCoinach/Definitions/DynamicEventEnemyType.json",
    "content": "{\n  \"sheet\": \"DynamicEventEnemyType\",\n  \"defaultColumn\": \"Name\",\n  \"definitions\": [\n    {\n      \"name\": \"Name\"\n    }\n  ]\n}\n"
  },
  {
    "path": "SaintCoinach/Definitions/DynamicEventSingleBattle.json",
    "content": "{\n  \"sheet\": \"DynamicEventSingleBattle\",\n  \"definitions\": [\n    {\n      \"name\": \"BNpcName\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"BNpcName\"\n      }\n    },\n    {\n      \"index\": 1,\n      \"name\": \"Icon\",\n      \"converter\": {\n        \"type\": \"icon\"\n      }\n    },\n    {\n      \"index\": 2,\n      \"name\": \"Text\"\n    }\n  ]\n}\n"
  },
  {
    "path": "SaintCoinach/Definitions/DynamicEventType.json",
    "content": "{\n  \"sheet\": \"DynamicEventType\",\n  \"definitions\": [\n    {\n      \"name\": \"Icon{Objective}[0]\",\n      \"converter\": {\n        \"type\": \"icon\"\n      }\n    },\n    {\n      \"index\": 1,\n      \"name\": \"Icon{Objective}[1]\",\n      \"converter\": {\n        \"type\": \"icon\"\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "SaintCoinach/Definitions/ENpcBase.json",
    "content": "{\n  \"sheet\": \"ENpcBase\",\n  \"definitions\": [\n    {\n      \"name\": \"EventHandler\"\n    },\n    {\n      \"index\": 1,\n      \"name\": \"Important\"\n    },\n    {\n      \"index\": 2,\n      \"type\": \"repeat\",\n      \"count\": 32,\n      \"definition\": {\n        \"name\": \"ENpcData\",\n        \"converter\": {\n          \"type\": \"generic\"\n        }\n      }\n    },\n    {\n      \"index\": 34,\n      \"name\": \"Scale\"\n    },\n    {\n      \"index\": 35,\n      \"name\": \"ModelChara\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"ModelChara\"\n      }\n    },\n    {\n      \"index\": 36,\n      \"name\": \"Race\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"Race\"\n      }\n    },\n    {\n      \"index\": 37,\n      \"name\": \"Gender\"\n    },\n    {\n      \"index\": 38,\n      \"name\": \"BodyType\"\n    },\n    {\n      \"index\": 39,\n      \"name\": \"Height\"\n    },\n    {\n      \"index\": 40,\n      \"name\": \"Tribe\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"Tribe\"\n      }\n    },\n    {\n      \"index\": 41,\n      \"name\": \"Face\"\n    },\n    {\n      \"index\": 42,\n      \"name\": \"HairStyle\"\n    },\n    {\n      \"index\": 43,\n      \"name\": \"HairHighlight\"\n    },\n    {\n      \"index\": 44,\n      \"name\": \"SkinColor\"\n    },\n    {\n      \"index\": 45,\n      \"name\": \"EyeHeterochromia\"\n    },\n    {\n      \"index\": 46,\n      \"name\": \"HairColor\"\n    },\n    {\n      \"index\": 47,\n      \"name\": \"HairHighlightColor\"\n    },\n    {\n      \"index\": 48,\n      \"name\": \"FacialFeature\"\n    },\n    {\n      \"index\": 49,\n      \"name\": \"FacialFeatureColor\"\n    },\n    {\n      \"index\": 50,\n      \"name\": \"Eyebrows\"\n    },\n    {\n      \"index\": 51,\n      \"name\": \"EyeColor\"\n    },\n    {\n      \"index\": 52,\n      \"name\": \"EyeShape\"\n    },\n    {\n      \"index\": 53,\n      \"name\": \"Nose\"\n    },\n    {\n      \"index\": 54,\n      \"name\": \"Jaw\"\n    },\n    {\n      \"index\": 55,\n      \"name\": \"Mouth\"\n    },\n    {\n      \"index\": 56,\n      \"name\": \"LipColor\"\n    },\n    {\n      \"index\": 57,\n      \"name\": \"BustOrTone1\"\n    },\n    {\n      \"index\": 58,\n      \"name\": \"ExtraFeature1\"\n    },\n    {\n      \"index\": 59,\n      \"name\": \"ExtraFeature2OrBust\"\n    },\n    {\n      \"index\": 60,\n      \"name\": \"FacePaint\"\n    },\n    {\n      \"index\": 61,\n      \"name\": \"FacePaintColor\"\n    },\n    {\n      \"index\": 63,\n      \"name\": \"NpcEquip\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"NpcEquip\"\n      }\n    },\n    {\n      \"index\": 64,\n      \"name\": \"Behavior\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"Behavior\"\n      }\n    },\n    {\n      \"index\": 65,\n      \"name\": \"Model{MainHand}\"\n    },\n    {\n      \"index\": 66,\n      \"name\": \"Dye{MainHand}\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"Stain\"\n      }\n    },\n    {\n      \"index\": 67,\n      \"name\": \"Dye2{MainHand}\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"Stain\"\n      }\n    },\n    {\n      \"index\": 68,\n      \"name\": \"Model{OffHand}\"\n    },\n    {\n      \"index\": 69,\n      \"name\": \"Dye{OffHand}\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"Stain\"\n      }\n    },\n    {\n      \"index\": 70,\n      \"name\": \"Dye2{OffHand}\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"Stain\"\n      }\n    },\n    {\n      \"index\": 71,\n      \"name\": \"Model{Head}\"\n    },\n    {\n      \"index\": 72,\n      \"name\": \"Dye{Head}\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"Stain\"\n      }\n    },\n    {\n      \"index\": 73,\n      \"name\": \"Dye2{Head}\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"Stain\"\n      }\n    },\n    {\n      \"index\": 74,\n      \"name\": \"Visor\"\n    },\n    {\n      \"index\": 75,\n      \"name\": \"Model{Body}\"\n    },\n    {\n      \"index\": 76,\n      \"name\": \"Dye{Body}\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"Stain\"\n      }\n    },\n    {\n      \"index\": 77,\n      \"name\": \"Dye2{Body}\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"Stain\"\n      }\n    },\n    {\n      \"index\": 78,\n      \"name\": \"Model{Hands}\"\n    },\n    {\n      \"index\": 79,\n      \"name\": \"Dye{Hands}\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"Stain\"\n      }\n    },\n    {\n      \"index\": 80,\n      \"name\": \"Dye2{Hands}\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"Stain\"\n      }\n    },\n    {\n      \"index\": 81,\n      \"name\": \"Model{Legs}\"\n    },\n    {\n      \"index\": 82,\n      \"name\": \"Dye{Legs}\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"Stain\"\n      }\n    },\n    {\n      \"index\": 83,\n      \"name\": \"Dye2{Legs}\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"Stain\"\n      }\n    },\n    {\n      \"index\": 84,\n      \"name\": \"Model{Feet}\"\n    },\n    {\n      \"index\": 85,\n      \"name\": \"Dye{Feet}\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"Stain\"\n      }\n    },\n    {\n      \"index\": 86,\n      \"name\": \"Dye2{Feet}\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"Stain\"\n      }\n    },\n    {\n      \"index\": 87,\n      \"name\": \"Model{Ears}\"\n    },\n    {\n      \"index\": 88,\n      \"name\": \"Dye{Ears}\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"Stain\"\n      }\n    },\n    {\n      \"index\": 89,\n      \"name\": \"Dye2{Ears}\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"Stain\"\n      }\n    },\n    {\n      \"index\": 90,\n      \"name\": \"Model{Neck}\"\n    },\n    {\n      \"index\": 91,\n      \"name\": \"Dye{Neck}\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"Stain\"\n      }\n    },\n    {\n      \"index\": 92,\n      \"name\": \"Dye2{Neck}\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"Stain\"\n      }\n    },\n    {\n      \"index\": 93,\n      \"name\": \"Model{Wrists}\"\n    },\n    {\n      \"index\": 94,\n      \"name\": \"Dye{Wrists}\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"Stain\"\n      }\n    },\n    {\n      \"index\": 95,\n      \"name\": \"Dye2{Wrists}\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"Stain\"\n      }\n    },\n    {\n      \"index\": 96,\n      \"name\": \"Model{LeftRing}\"\n    },\n    {\n      \"index\": 97,\n      \"name\": \"Dye{LeftRing}\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"Stain\"\n      }\n    },\n    {\n      \"index\": 98,\n      \"name\": \"Dye2{LeftRing}\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"Stain\"\n      }\n    },\n    {\n      \"index\": 99,\n      \"name\": \"Model{RightRing}\"\n    },\n    {\n      \"index\": 100,\n      \"name\": \"Dye{RightRing}\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"Stain\"\n      }\n    },\n    {\n      \"index\": 101,\n      \"name\": \"Dye2{RightRing}\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"Stain\"\n      }\n    },\n    {\n      \"index\": 104,\n      \"name\": \"Invisibility\"\n    },\n    {\n      \"index\": 105,\n      \"name\": \"Balloon\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"Balloon\"\n      }\n    },\n    {\n      \"index\": 106,\n      \"name\": \"NotRewriteHeight\"\n    },\n    {\n      \"index\": 107,\n      \"name\": \"DefaultBalloon\"\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/ENpcDressUp.json",
    "content": "{\n  \"sheet\": \"ENpcDressUp\",\n  \"definitions\": [\n    {\n      \"index\": 1,\n      \"name\": \"ENpcDressUpDress\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"ENpcDressUpDress\"\n      }\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/ENpcDressUpDress.json",
    "content": "{\n  \"sheet\": \"ENpcDressUpDress\",\n  \"definitions\": [\n    {\n      \"index\": 7,\n      \"name\": \"ENpc\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"ENpcResident\"\n      }\n    },\n    {\n      \"index\": 9,\n      \"name\": \"Behavior\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"Behavior\"\n      }\n    },\n    {\n      \"index\": 37,\n      \"name\": \"Model{MainHand}\"\n    },\n    {\n      \"index\": 38,\n      \"name\": \"Dye{MainHand}\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"Stain\"\n      }\n    },\n    {\n      \"index\": 39,\n      \"name\": \"Dye2{MainHand}\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"Stain\"\n      }\n    },\n    {\n      \"index\": 40,\n      \"name\": \"Model{OffHand}\"\n    },\n    {\n      \"index\": 41,\n      \"name\": \"Dye{OffHand}\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"Stain\"\n      }\n    },\n    {\n      \"index\": 42,\n      \"name\": \"Dye2{OffHand}\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"Stain\"\n      }\n    },\n    {\n      \"index\": 43,\n      \"name\": \"Model{Head}\"\n    },\n    {\n      \"index\": 44,\n      \"name\": \"Dye{Head}\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"Stain\"\n      }\n    },\n    {\n      \"index\": 45,\n      \"name\": \"Dye2{Head}\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"Stain\"\n      }\n    },\n    {\n      \"index\": 46,\n      \"name\": \"Model{Body}\"\n    },\n    {\n      \"index\": 47,\n      \"name\": \"Dye{Body}\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"Stain\"\n      }\n    },\n    {\n      \"index\": 48,\n      \"name\": \"Dye2{Body}\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"Stain\"\n      }\n    },\n    {\n      \"index\": 49,\n      \"name\": \"Model{Hands}\"\n    },\n    {\n      \"index\": 50,\n      \"name\": \"Dye{Hands}\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"Stain\"\n      }\n    },\n    {\n      \"index\": 51,\n      \"name\": \"Dye2{Hands}\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"Stain\"\n      }\n    },\n    {\n      \"index\": 52,\n      \"name\": \"Model{Legs}\"\n    },\n    {\n      \"index\": 53,\n      \"name\": \"Dye{Legs}\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"Stain\"\n      }\n    },\n    {\n      \"index\": 54,\n      \"name\": \"Dye2{Legs}\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"Stain\"\n      }\n    },\n    {\n      \"index\": 55,\n      \"name\": \"Model{Feet}\"\n    },\n    {\n      \"index\": 56,\n      \"name\": \"Dye{Feet}\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"Stain\"\n      }\n    },\n    {\n      \"index\": 57,\n      \"name\": \"Dye2{Feet}\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"Stain\"\n      }\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/ENpcResident.json",
    "content": "{\n  \"sheet\": \"ENpcResident\",\n  \"defaultColumn\": \"Singular\",\n  \"definitions\": [\n    {\n      \"name\": \"Singular\"\n    },\n    {\n      \"index\": 1,\n      \"name\": \"Adjective\"\n    },\n    {\n      \"index\": 2,\n      \"name\": \"Plural\"\n    },\n    {\n      \"index\": 3,\n      \"name\": \"PossessivePronoun\"\n    },\n    {\n      \"index\": 4,\n      \"name\": \"StartsWithVowel\"\n    },\n    {\n      \"index\": 6,\n      \"name\": \"Pronoun\"\n    },\n    {\n      \"index\": 7,\n      \"name\": \"Article\"\n    },\n    {\n      \"index\": 8,\n      \"name\": \"Title\"\n    },\n    {\n      \"index\": 9,\n      \"name\": \"Map\"\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/EObj.json",
    "content": "{\n  \"sheet\": \"EObj\",\n  \"defaultColumn\": \"Singular\",\n  \"definitions\": [\n    {\n      \"index\": 8,\n      \"name\": \"PopType\"\n    },\n    {\n      \"index\": 9,\n      \"name\": \"Data\"\n    },\n    {\n      \"index\": 10,\n      \"name\": \"Invisibility\"\n    },\n    {\n      \"index\": 11,\n      \"name\": \"SgbPath\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"ExportedSG\"\n      }\n    },\n    {\n      \"index\": 12,\n      \"name\": \"EyeCollision\"\n    },\n    {\n      \"index\": 13,\n      \"name\": \"DirectorControl\"\n    },\n    {\n      \"index\": 14,\n      \"name\": \"Target\"\n    },\n    {\n      \"index\": 15,\n      \"name\": \"EventHighAddition\"\n    },\n    {\n      \"index\": 18,\n      \"name\": \"AddedIn{5-3}\"\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/EObjName.json",
    "content": "{\n  \"sheet\": \"EObjName\",\n  \"defaultColumn\": \"Singular\",\n  \"definitions\": [\n    {\n      \"name\": \"Singular\"\n    },\n    {\n      \"index\": 1,\n      \"name\": \"Adjective\"\n    },\n    {\n      \"index\": 2,\n      \"name\": \"Plural\"\n    },\n    {\n      \"index\": 3,\n      \"name\": \"PossessivePronoun\"\n    },\n    {\n      \"index\": 4,\n      \"name\": \"StartsWithVowel\"\n    },\n    {\n      \"index\": 6,\n      \"name\": \"Pronoun\"\n    },\n    {\n      \"index\": 7,\n      \"name\": \"Article\"\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/EmjAddon.json",
    "content": "{\n  \"sheet\": \"EmjAddon\",\n  \"defaultColumn\": \"Text\",\n  \"definitions\": [\n    {\n      \"name\": \"Text\"\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/EmjDani.json",
    "content": "{\n  \"sheet\": \"EmjDani\",\n  \"definitions\": [\n    {\n      \"name\": \"Icon\",\n      \"converter\": {\n        \"type\": \"icon\"\n      }\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/Emote.json",
    "content": "{\n  \"sheet\": \"Emote\",\n  \"defaultColumn\": \"Name\",\n  \"definitions\": [\n    {\n      \"name\": \"Name\"\n    },\n    {\n      \"index\": 1,\n      \"type\": \"repeat\",\n      \"count\": 7,\n      \"definition\": {\n        \"name\": \"ActionTimeline\",\n        \"converter\": {\n          \"type\": \"link\",\n          \"target\": \"ActionTimeline\"\n        }\n      }\n    },\n    {\n      \"index\": 11,\n      \"name\": \"EmoteCategory\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"EmoteCategory\"\n      }\n    },\n    {\n      \"index\": 12,\n      \"name\": \"EmoteMode\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"EmoteMode\"\n      }\n    },\n    {\n      \"index\": 15,\n      \"name\": \"HasCancelEmote\"\n    },\n    {\n      \"index\": 16,\n      \"name\": \"DrawsWeapon\"\n    },\n    {\n      \"index\": 18,\n      \"name\": \"Order\"\n    },\n    {\n      \"index\": 19,\n      \"name\": \"TextCommand\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"TextCommand\"\n      }\n    },\n    {\n      \"index\": 20,\n      \"name\": \"Icon\",\n      \"converter\": {\n        \"type\": \"icon\"\n      }\n    },\n    {\n      \"index\": 21,\n      \"name\": \"LogMessage{Targeted}\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"LogMessage\"\n      }\n    },\n    {\n      \"index\": 22,\n      \"name\": \"LogMessage{Untargeted}\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"LogMessage\"\n      }\n    },\n    {\n      \"index\": 23,\n      \"name\": \"UnlockLink\"\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/EmoteCategory.json",
    "content": "{\n  \"sheet\": \"EmoteCategory\",\n  \"defaultColumn\": \"Name\",\n  \"definitions\": [\n    {\n      \"name\": \"Name\"\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/EmoteMode.json",
    "content": "{\n  \"sheet\": \"EmoteMode\",\n  \"defaultColumn\": \"Name\",\n  \"definitions\": [\n    {\n      \"name\": \"StartEmote\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"Emote\"\n      }\n    },\n    {\n      \"index\": 1,\n      \"name\": \"EndEmote\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"Emote\"\n      }\n    },\n    {\n      \"index\": 2,\n      \"name\": \"Move\"\n    },\n    {\n      \"index\": 3,\n      \"name\": \"Camera\"\n    },\n    {\n      \"index\": 4,\n      \"name\": \"EndOnRotate\"\n    },\n    {\n      \"index\": 5,\n      \"name\": \"EndOnEmote\"\n    },\n    {\n      \"index\": 6,\n      \"name\": \"ConditionMode\"\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/EquipRaceCategory.json",
    "content": "{\n  \"sheet\": \"EquipRaceCategory\",\n  \"definitions\": [\n    {\n      \"name\": \"Hyur\"\n    },\n    {\n      \"index\": 1,\n      \"name\": \"Elezen\"\n    },\n    {\n      \"index\": 2,\n      \"name\": \"Lalafell\"\n    },\n    {\n      \"index\": 3,\n      \"name\": \"Miqo'te\"\n    },\n    {\n      \"index\": 4,\n      \"name\": \"Roegadyn\"\n    },\n    {\n      \"index\": 5,\n      \"name\": \"Au Ra\"\n    },\n    {\n      \"index\": 8,\n      \"name\": \"Male\"\n    },\n    {\n      \"index\": 9,\n      \"name\": \"Female\"\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/EquipSlotCategory.json",
    "content": "{\n  \"sheet\": \"EquipSlotCategory\",\n  \"definitions\": [\n    {\n      \"name\": \"MainHand\"\n    },\n    {\n      \"index\": 1,\n      \"name\": \"OffHand\"\n    },\n    {\n      \"index\": 2,\n      \"name\": \"Head\"\n    },\n    {\n      \"index\": 3,\n      \"name\": \"Body\"\n    },\n    {\n      \"index\": 4,\n      \"name\": \"Gloves\"\n    },\n    {\n      \"index\": 5,\n      \"name\": \"Waist\"\n    },\n    {\n      \"index\": 6,\n      \"name\": \"Legs\"\n    },\n    {\n      \"index\": 7,\n      \"name\": \"Feet\"\n    },\n    {\n      \"index\": 8,\n      \"name\": \"Ears\"\n    },\n    {\n      \"index\": 9,\n      \"name\": \"Neck\"\n    },\n    {\n      \"index\": 10,\n      \"name\": \"Wrists\"\n    },\n    {\n      \"index\": 11,\n      \"name\": \"FingerL\"\n    },\n    {\n      \"index\": 12,\n      \"name\": \"FingerR\"\n    },\n    {\n      \"index\": 13,\n      \"name\": \"SoulCrystal\"\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/EurekaAetherItem.json",
    "content": "{\n  \"sheet\": \"EurekaAetherItem\",\n  \"defaultColumn\": \"Singular\",\n  \"definitions\": [\n    {\n      \"name\": \"Singular\"\n    },\n    {\n      \"index\": 1,\n      \"name\": \"Adjective\"\n    },\n    {\n      \"index\": 2,\n      \"name\": \"Plural\"\n    },\n    {\n      \"index\": 3,\n      \"name\": \"PossessivePronoun\"\n    },\n    {\n      \"index\": 4,\n      \"name\": \"StartsWithVowel\"\n    },\n    {\n      \"index\": 6,\n      \"name\": \"Pronoun\"\n    },\n    {\n      \"index\": 7,\n      \"name\": \"Article\"\n    },\n    {\n      \"index\": 8,\n      \"name\": \"Name\"\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/EurekaAethernet.json",
    "content": "{\n  \"sheet\": \"EurekaAethernet\",\n  \"defaultColumn\": \"Location\",\n  \"definitions\": [\n    {\n      \"name\": \"Location\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"PlaceName\"\n      }\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/EurekaDungeonPortal.json",
    "content": "{\n  \"sheet\": \"EurekaDungeonPortal\",\n  \"definitions\": [\n    {\n      \"name\": \"LevelId\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"Level\"\n      }\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/EurekaGrowData.json",
    "content": "{\n  \"sheet\": \"EurekaGrowData\",\n  \"defaultColumn\": \"BaseElementalPower\",\n  \"definitions\": [\n    {\n      \"name\": \"BaseResistance\"\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/EurekaLogosMixerProbability.json",
    "content": "{\n  \"sheet\": \"EurekaLogosMixerProbability\",\n  \"definitions\": [\n    {\n      \"name\": \"Probability<%>\"\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/EurekaMagiaAction.json",
    "content": "{\n  \"sheet\": \"EurekaMagiaAction\",\n  \"definitions\": [\n    {\n      \"name\": \"Action\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"Action\"\n      }\n    },\n    {\n      \"index\": 1,\n      \"name\": \"MaxUses\"\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/EurekaMagiciteItem.json",
    "content": "{\n  \"sheet\": \"EurekaMagiciteItem\",\n  \"definitions\": [\n    {\n      \"name\": \"EurekaMagiciteItemType\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"EurekaMagiciteItemType\"\n      }\n    },\n    {\n      \"index\": 1,\n      \"name\": \"ClassJobCategory\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"ClassJobCategory\"\n      }\n    },\n    {\n      \"index\": 2,\n      \"name\": \"Item\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"Item\"\n      }\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/EurekaMagiciteItemType.json",
    "content": "{\n  \"sheet\": \"EurekaMagiciteItemType\",\n  \"defaultColumn\": \"Type\",\n  \"definitions\": [\n    {\n      \"name\": \"Type\"\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/EurekaSphereElementAdjust.json",
    "content": "{\n  \"sheet\": \"EurekaSphereElementAdjust\",\n  \"defaultColumn\": \"PowerModifier\",\n  \"definitions\": [\n    {\n      \"name\": \"PowerModifier\"\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/EventAction.json",
    "content": "{\n  \"sheet\": \"EventAction\",\n  \"defaultColumn\": \"Name\",\n  \"definitions\": [\n    {\n      \"name\": \"Name\"\n    },\n    {\n      \"index\": 1,\n      \"name\": \"Icon\",\n      \"converter\": {\n        \"type\": \"icon\"\n      }\n    },\n    {\n      \"index\": 2,\n      \"name\": \"CastTime\"\n    },\n    {\n      \"index\": 3,\n      \"type\": \"repeat\",\n      \"count\": 3,\n      \"definition\": {\n        \"name\": \"Animation\",\n        \"converter\": {\n          \"type\": \"link\",\n          \"target\": \"ActionTimeline\"\n        }\n      }\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/EventCustomIconType.json",
    "content": "{\n  \"sheet\": \"EventCustomIconType\",\n  \"definitions\": [\n    {\n      \"type\": \"repeat\",\n      \"count\": 10,\n      \"definition\": {\n        \"name\": \"AnnounceQuest\",\n        \"converter\": {\n          \"type\": \"icon\"\n        }\n      }\n    },\n    {\n      \"index\": 10,\n      \"type\": \"repeat\",\n      \"count\": 10,\n      \"definition\": {\n        \"name\": \"AnnounceQuestLocked\",\n        \"converter\": {\n          \"type\": \"icon\"\n        }\n      }\n    },\n    {\n      \"index\": 20,\n      \"type\": \"repeat\",\n      \"count\": 10,\n      \"definition\": {\n        \"name\": \"MapAnnounceQuest[0]\",\n        \"converter\": {\n          \"type\": \"icon\"\n        }\n      }\n    },\n    {\n      \"index\": 30,\n      \"type\": \"repeat\",\n      \"count\": 10,\n      \"definition\": {\n        \"name\": \"MapAnnounceQuestLocked\",\n        \"converter\": {\n          \"type\": \"icon\"\n        }\n      }\n    },\n    {\n      \"index\": 40,\n      \"type\": \"repeat\",\n      \"count\": 10,\n      \"definition\": {\n        \"name\": \"MapAnnounceQuest[1]\",\n        \"converter\": {\n          \"type\": \"icon\"\n        }\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "SaintCoinach/Definitions/EventIconPriority.json",
    "content": "{\n  \"sheet\": \"EventIconPriority\",\n  \"definitions\": [\n    {\n      \"type\": \"repeat\",\n      \"count\": 19,\n      \"definition\": {\n        \"name\": \"Icon\",\n        \"converter\": {\n          \"type\": \"icon\"\n        }\n      }\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/EventIconPriorityPair.json",
    "content": "{\n  \"sheet\": \"EventIconPriorityPair\",\n  \"definitions\": [\n    {\n      \"name\": \"Icon\",\n      \"converter\": {\n        \"type\": \"icon\"\n      }\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/EventIconType.json",
    "content": "{\n  \"sheet\": \"EventIconType\",\n  \"definitions\": [\n    {\n      \"name\": \"NpcIcon{Available}\",\n      \"converter\": {\n        \"type\": \"icon\"\n      }\n    },\n    {\n      \"index\": 1,\n      \"name\": \"MapIcon{Available}\",\n      \"converter\": {\n        \"type\": \"icon\"\n      }\n    },\n    {\n      \"index\": 2,\n      \"name\": \"NpcIcon{Invalid}\",\n      \"converter\": {\n        \"type\": \"icon\"\n      }\n    },\n    {\n      \"index\": 3,\n      \"name\": \"MapIcon{Invalid}\",\n      \"converter\": {\n        \"type\": \"icon\"\n      }\n    },\n    {\n      \"index\": 4,\n      \"name\": \"IconRange\"\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/EventItem.json",
    "content": "{\n  \"sheet\": \"EventItem\",\n  \"defaultColumn\": \"Singular\",\n  \"definitions\": [\n    {\n      \"name\": \"Singular\"\n    },\n    {\n      \"index\": 1,\n      \"name\": \"Adjective\"\n    },\n    {\n      \"index\": 2,\n      \"name\": \"Plural\"\n    },\n    {\n      \"index\": 3,\n      \"name\": \"PossessivePronoun\"\n    },\n    {\n      \"index\": 4,\n      \"name\": \"StartsWithVowel\"\n    },\n    {\n      \"index\": 6,\n      \"name\": \"Pronoun\"\n    },\n    {\n      \"index\": 7,\n      \"name\": \"Article\"\n    },\n    {\n      \"index\": 9,\n      \"name\": \"Name\"\n    },\n    {\n      \"index\": 10,\n      \"name\": \"Icon\",\n      \"converter\": {\n        \"type\": \"icon\"\n      }\n    },\n    {\n      \"index\": 11,\n      \"name\": \"Action\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"Action\"\n      }\n    },\n    {\n      \"index\": 12,\n      \"name\": \"StackSize\"\n    },\n    {\n      \"index\": 14,\n      \"name\": \"Quest\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"Quest\"\n      }\n    },\n    {\n      \"index\": 15,\n      \"name\": \"CastTime\"\n    },\n    {\n      \"index\": 16,\n      \"name\": \"CastTimeline\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"EventItemCastTimeline\"\n      }\n    },\n    {\n      \"index\": 17,\n      \"name\": \"Timeline\"\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/EventItemCastTimeline.json",
    "content": "{\n  \"sheet\": \"EventItemCastTimeline\",\n  \"defaultColumn\": \"ActionTimeline\",\n  \"definitions\": [\n    {\n      \"name\": \"ActionTimeline\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"ActionTimeline\"\n      }\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/EventItemHelp.json",
    "content": "{\n  \"sheet\": \"EventItemHelp\",\n  \"defaultColumn\": \"Description\",\n  \"definitions\": [\n    {\n      \"name\": \"Description\"\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/EventItemTimeline.json",
    "content": "{\n  \"sheet\": \"EventItemTimeline\",\n  \"defaultColumn\": \"ActionTimeline\",\n  \"definitions\": [\n    {\n      \"name\": \"ActionTimeline\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"ActionTimeline\"\n      }\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/EventPathMove.json",
    "content": "{\n  \"sheet\": \"EventPathMove\",\n  \"definitions\": []\n}"
  },
  {
    "path": "SaintCoinach/Definitions/EventSystemDefine.json",
    "content": "{\n  \"sheet\": \"EventSystemDefine\",\n  \"defaultColumn\": \"Text\",\n  \"definitions\": [\n    {\n      \"name\": \"Text\"\n    },\n    {\n      \"index\": 1,\n      \"name\": \"DefineValue\"\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/ExVersion.json",
    "content": "{\n  \"sheet\": \"ExVersion\",\n  \"defaultColumn\": \"Name\",\n  \"definitions\": [\n    {\n      \"name\": \"Name\"\n    },\n    {\n      \"index\": 1,\n      \"name\": \"AcceptJingle\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"ScreenImage\"\n      }\n    },\n    {\n      \"index\": 2,\n      \"name\": \"CompleteJingle\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"ScreenImage\"\n      }\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/ExportedGatheringPoint.json",
    "content": "{\n  \"sheet\": \"ExportedGatheringPoint\",\n  \"definitions\": [\n    {\n      \"name\": \"X\"\n    },\n    {\n      \"index\": 1,\n      \"name\": \"Y\"\n    },\n    {\n      \"index\": 2,\n      \"name\": \"GatheringType\",\n       \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"GatheringType\"\n    }\n    },\n    {\n      \"index\": 3,\n      \"name\": \"GatheringPointType\"\n    },\n    {\n      \"index\": 4,\n      \"name\": \"Radius\"\n    }\n  ]\n}\n"
  },
  {
    "path": "SaintCoinach/Definitions/ExportedSG.json",
    "content": "{\n  \"sheet\": \"ExportedSG\",\n  \"defaultColumn\": \"SgbPath\",\n  \"definitions\": [\n    {\n      \"name\": \"SgbPath\"\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/ExtraCommand.json",
    "content": "{\n  \"sheet\": \"ExtraCommand\",\n  \"defaultColumn\": \"Name\",\n  \"definitions\": [\n    {\n      \"name\": \"Name\"\n    },\n    {\n      \"index\": 1,\n      \"name\": \"Description\"\n    },\n    {\n      \"index\": 2,\n      \"name\": \"Icon\",\n      \"converter\": {\n        \"type\": \"icon\"\n      }\n    },\n    {\n      \"index\": 3,\n      \"name\": \"Order\"\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/FCActivity.json",
    "content": "{\n  \"sheet\": \"FCActivity\",\n  \"defaultColumn\": \"Text\",\n  \"definitions\": [\n    {\n      \"name\": \"Text\"\n    },\n    {\n      \"index\": 1,\n      \"name\": \"SelfKind\"\n    },\n    {\n      \"index\": 2,\n      \"name\": \"TargetKind\"\n    },\n    {\n      \"index\": 3,\n      \"name\": \"NumParam\"\n    },\n    {\n      \"index\": 4,\n      \"name\": \"FCActivityCategory\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"FCActivityCategory\"\n      }\n    },\n    {\n      \"index\": 5,\n      \"name\": \"IconType\"\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/FCActivityCategory.json",
    "content": "{\n  \"sheet\": \"FCActivityCategory\",\n  \"defaultColumn\": \"Name\",\n  \"definitions\": [\n    {\n      \"name\": \"Priority\"\n    },\n    {\n      \"index\": 1,\n      \"name\": \"Name\"\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/FCAuthority.json",
    "content": "{\n  \"sheet\": \"FCAuthority\",\n  \"defaultColumn\": \"Name\",\n  \"definitions\": [\n    {\n      \"name\": \"Name\"\n    },\n    {\n      \"index\": 1,\n      \"name\": \"FCAuthorityCategory\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"FCAuthorityCategory\"\n      }\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/FCAuthorityCategory.json",
    "content": "{\n  \"sheet\": \"FCAuthorityCategory\",\n  \"defaultColumn\": \"Name\",\n  \"definitions\": [\n    {\n      \"name\": \"Name\"\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/FCChestName.json",
    "content": "{\n  \"sheet\": \"FCChestName\",\n  \"defaultColumn\": \"Name\",\n  \"definitions\": [\n    {\n      \"name\": \"Name\"\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/FCCrestSymbol.json",
    "content": "{\n  \"sheet\": \"FCCrestSymbol\",\n  \"definitions\": [\n    {\n      \"name\": \"ColorNum\"\n    },\n    {\n      \"index\": 1,\n      \"name\": \"FCRight\"\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/FCHierarchy.json",
    "content": "{\n  \"sheet\": \"FCHierarchy\",\n  \"defaultColumn\": \"Name\",\n  \"definitions\": [\n    {\n      \"name\": \"Name\"\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/FCProfile.json",
    "content": "{\n  \"sheet\": \"FCProfile\",\n  \"defaultColumn\": \"Name\",\n  \"definitions\": [\n    {\n      \"name\": \"Priority\"\n    },\n    {\n      \"index\": 1,\n      \"name\": \"Name\"\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/FCRank.json",
    "content": "{\n  \"sheet\": \"FCRank\",\n  \"definitions\": [\n    {\n      \"name\": \"NextPoint\"\n    },\n    {\n      \"index\": 1,\n      \"name\": \"CurrentPoint\"\n    },\n    {\n      \"index\": 2,\n      \"name\": \"Rights\"\n    },\n    {\n      \"index\": 5,\n      \"name\": \"FCActionActiveNum\"\n    },\n    {\n      \"index\": 6,\n      \"name\": \"FCActionStockNum\"\n    },\n    {\n      \"index\": 7,\n      \"name\": \"FCChestCompartments\"\n    }\n  ]\n}\n"
  },
  {
    "path": "SaintCoinach/Definitions/FCReputation.json",
    "content": "{\n  \"sheet\": \"FCReputation\",\n  \"defaultColumn\": \"Name\",\n  \"definitions\": [\n    {\n      \"name\": \"PointsToNext\"\n    },\n    {\n      \"index\": 1,\n      \"name\": \"RequiredPoints\"\n    },\n    {\n      \"index\": 2,\n      \"name\": \"DiscountRate\"\n    },\n    {\n      \"index\": 3,\n      \"name\": \"Color\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"UIColor\"\n      }\n    },\n    {\n      \"index\": 4,\n      \"name\": \"Name\"\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/FCRights.json",
    "content": "{\n  \"sheet\": \"FCRights\",\n  \"defaultColumn\": \"Name\",\n  \"definitions\": [\n    {\n      \"name\": \"Name\"\n    },\n    {\n      \"index\": 1,\n      \"name\": \"Description\"\n    },\n    {\n      \"index\": 2,\n      \"name\": \"Icon\",\n      \"converter\": {\n        \"type\": \"icon\"\n      }\n    },\n    {\n      \"index\": 3,\n      \"name\": \"FCRank\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"FCRank\"\n      }\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/FGSAddon.json",
    "content": "{\n  \"sheet\": \"FGSAddon\",\n  \"definitions\": []\n}"
  },
  {
    "path": "SaintCoinach/Definitions/FGSStageUI.json",
    "content": "{\n  \"sheet\": \"FGSStageUI\",\n  \"definitions\": []\n}"
  },
  {
    "path": "SaintCoinach/Definitions/FashionCheckThemeCategory.json",
    "content": "{\n  \"sheet\": \"FashionCheckThemeCategory\",\n  \"defaultColumn\": \"Name\",\n  \"definitions\": [\n    {\n      \"name\": \"Name\"\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/FashionCheckWeeklyTheme.json",
    "content": "{\n  \"sheet\": \"FashionCheckWeeklyTheme\",\n  \"defaultColumn\": \"Name\",\n  \"definitions\": [\n    {\n      \"name\": \"Name\"\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/Fate.json",
    "content": "{\n  \"sheet\": \"Fate\",\n  \"defaultColumn\": \"Name\",\n  \"definitions\": [\n    {\n      \"name\": \"Name\"\n    },\n    {\n      \"index\": 1,\n      \"name\": \"Description\"\n    },\n    {\n      \"index\": 2,\n      \"name\": \"Objective\"\n    },\n    {\n      \"index\": 3,\n      \"type\": \"repeat\",\n      \"count\": 3,\n      \"definition\": {\n        \"name\": \"StatusText\"\n      }\n    },\n    {\n      \"index\": 6,\n      \"name\": \"EurekaFate\"\n    },\n    {\n      \"index\": 7,\n      \"name\": \"Rule\"\n    },\n    {\n      \"index\": 8,\n      \"name\": \"FateRuleEx\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"FateRuleEx\"\n      }\n    },\n    {\n      \"index\": 9,\n      \"name\": \"Location\"\n    },\n    {\n      \"index\": 10,\n      \"name\": \"ClassJobLevel\"\n    },\n    {\n      \"index\": 11,\n      \"name\": \"ClassJobLevel{Max}\"\n    },\n    {\n      \"index\": 12,\n      \"name\": \"EventItem\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"EventItem\"\n      }\n    },\n    {\n      \"index\": 13,\n      \"type\": \"repeat\",\n      \"count\": 3,\n      \"definition\": {\n        \"name\": \"TypeToDoValue\"\n      }\n    },\n    {\n      \"index\": 16,\n      \"name\": \"Icon{Objective}\",\n      \"converter\": {\n        \"type\": \"icon\"\n      }\n    },\n    {\n      \"index\": 17,\n      \"name\": \"Icon{Map}\",\n      \"converter\": {\n        \"type\": \"icon\"\n      }\n    },\n    {\n      \"index\": 18,\n      \"name\": \"IconInactive{Map}\",\n      \"converter\": {\n        \"type\": \"icon\"\n      }\n    },\n    {\n      \"index\": 19,\n      \"name\": \"Music\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"BGM\"\n      }\n    },\n    {\n      \"index\": 20,\n      \"name\": \"LGBGuardNPCLocation\"\n    },\n    {\n      \"index\": 21,\n      \"name\": \"ScreenImage{Accept}\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"ScreenImage\"\n      }\n    },\n    {\n      \"index\": 22,\n      \"name\": \"ScreenImage{Complete}\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"ScreenImage\"\n      }\n    },\n    {\n      \"index\": 23,\n      \"name\": \"ScreenImage{Failed}\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"ScreenImage\"\n      }\n    },\n    {\n      \"index\": 25,\n      \"name\": \"RequiredQuest\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"Quest\"\n      }\n    },\n    {\n      \"index\": 26,\n      \"name\": \"SpecialFate\"\n    },\n    {\n      \"index\": 28,\n      \"name\": \"GivenStatus\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"Status\"\n      }\n    },\n    {\n      \"index\": 30,\n      \"name\": \"AdventEvent\"\n    },\n    {\n      \"index\": 31,\n      \"name\": \"MoonFaireEvent\"\n    },\n    {\n      \"index\": 33,\n      \"name\": \"FATEChain\"\n    },\n    {\n      \"index\": 36,\n      \"name\": \"ArrayIndex\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"ArrayEventHandler\"\n      }\n    },\n    {\n      \"index\": 38,\n      \"name\": \"ReqEventItem\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"EventItem\"\n      }\n    },\n    {\n      \"index\": 39,\n      \"name\": \"TurnInEventItem\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"EventItem\"\n      }\n    },\n    {\n      \"index\": 43,\n      \"type\": \"repeat\",\n      \"count\": 8,\n      \"definition\": {\n        \"name\": \"ObjectiveIcon\",\n        \"converter\": {\n          \"type\": \"icon\"\n        }\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "SaintCoinach/Definitions/FateEvent.json",
    "content": "{\n  \"sheet\": \"FateEvent\",\n  \"definitions\": [\n    {\n      \"type\": \"repeat\",\n      \"count\": 8,\n      \"definition\": {\n        \"name\": \"Turn\"\n      }\n    },\n    {\n      \"index\": 8,\n      \"type\": \"repeat\",\n      \"count\": 8,\n      \"definition\": {\n        \"name\": \"Gesture\"\n      }\n    },\n    {\n      \"index\": 16,\n      \"type\": \"repeat\",\n      \"count\": 8,\n      \"definition\": {\n        \"name\": \"LipSync\"\n      }\n    },\n    {\n      \"index\": 24,\n      \"type\": \"repeat\",\n      \"count\": 8,\n      \"definition\": {\n        \"name\": \"Facial\"\n      }\n    },\n    {\n      \"index\": 32,\n      \"type\": \"repeat\",\n      \"count\": 8,\n      \"definition\": {\n        \"name\": \"Shape\"\n      }\n    },\n    {\n      \"index\": 40,\n      \"type\": \"repeat\",\n      \"count\": 8,\n      \"definition\": {\n        \"name\": \"IsAutoShake\"\n      }\n    },\n    {\n      \"index\": 48,\n      \"type\": \"repeat\",\n      \"count\": 8,\n      \"definition\": {\n        \"name\": \"WidgetType\"\n      }\n    },\n    {\n      \"index\": 56,\n      \"type\": \"repeat\",\n      \"count\": 8,\n      \"definition\": {\n        \"name\": \"Text\"\n      }\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/FateMode.json",
    "content": "{\n  \"sheet\": \"FateMode\",\n  \"definitions\": [\n    {\n      \"index\": 1,\n      \"name\": \"MotivationIcon\",\n      \"converter\": {\n        \"type\": \"icon\"\n      }\n    },\n    {\n      \"index\": 2,\n      \"name\": \"MotivationMapMarker\",\n      \"converter\": {\n        \"type\": \"icon\"\n      }\n    },\n    {\n      \"index\": 3,\n      \"name\": \"ObjectiveIcon\",\n      \"converter\": {\n        \"type\": \"icon\"\n      }\n    },\n    {\n      \"index\": 4,\n      \"name\": \"ObjectiveMapMarker\",\n      \"converter\": {\n        \"type\": \"icon\"\n      }\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/FateProgressUI.json",
    "content": "{\n  \"sheet\": \"FateProgressUI\",\n  \"defaultColumn\": \"Name\",\n  \"definitions\": [\n    {\n      \"name\": \"Location\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"TerritoryType\"\n      }\n    },\n    {\n      \"index\": 1,\n      \"name\": \"ReqFatesToRank2\"\n    },\n    {\n      \"index\": 2,\n      \"name\": \"ReqFatesToRank3\"\n    },\n    {\n      \"index\": 3,\n      \"name\": \"ReqFatesToRank4\"\n    },\n    {\n      \"index\": 5,\n      \"name\": \"DisplayOrder\"\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/FateShop.json",
    "content": "{\n  \"sheet\": \"FateShop\",\n  \"definitions\": [\n    {\n      \"index\": 0,\n      \"type\": \"repeat\",\n      \"count\": 3,\n      \"definition\": {\n        \"name\": \"SpecialShop\",\n        \"converter\": {\n          \"type\": \"link\",\n          \"target\": \"SpecialShop\"\n        }\n      }\n    },\n    {\n      \"index\": 3,\n      \"type\": \"repeat\",\n      \"count\": 10,\n      \"definition\": {\n        \"name\": \"DefaultTalk\",\n        \"converter\": {\n          \"type\": \"link\",\n          \"target\": \"DefaultTalk\"\n        }\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "SaintCoinach/Definitions/FateTokenType.json",
    "content": "{\n  \"sheet\": \"FateTokenType\",\n  \"defaultColumn\": \"Currency\",\n  \"definitions\": [\n    {\n      \"name\": \"Currency\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"Item\"\n      }\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/FccShop.json",
    "content": "{\n  \"sheet\": \"FccShop\",\n  \"defaultColumn\": \"Name\",\n  \"isGenericReferenceTarget\": true,\n  \"definitions\": [\n    {\n      \"name\": \"Name\"\n    },\n    {\n      \"index\": 1,\n      \"type\": \"repeat\",\n      \"count\": 10,\n      \"definition\": {\n        \"type\": \"group\",\n        \"members\": [\n          {\n            \"name\": \"Item\",\n            \"converter\": {\n              \"type\": \"link\",\n              \"target\": \"Item\"\n            }\n          }\n        ]\n      }\n    },\n    {\n      \"index\": 11,\n      \"type\": \"repeat\",\n      \"count\": 10,\n      \"definition\": {\n        \"type\": \"group\",\n        \"members\": [\n          {\n            \"name\": \"Cost\"\n          }\n        ]\n      }\n    },\n    {\n      \"index\": 21,\n      \"type\": \"repeat\",\n      \"count\": 10,\n      \"definition\": {\n        \"type\": \"group\",\n        \"members\": [\n          {\n            \"name\": \"FCRank{Required}\",\n            \"converter\": {\n              \"type\": \"link\",\n              \"target\": \"FCRank\"\n            }\n          }\n        ]\n      }\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/Festival.json",
    "content": "{\n  \"sheet\": \"Festival\",\n  \"defaultColumn\": \"Name\",\n  \"definitions\": [\n    {\n      \"name\": \"Name\"\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/FieldMarker.json",
    "content": "{\n  \"sheet\": \"FieldMarker\",\n  \"defaultColumn\": \"Name\",\n  \"definitions\": [\n    {\n      \"name\": \"VFX\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"VFX\"\n      }\n    },\n    {\n      \"index\": 1,\n      \"name\": \"UiIcon\",\n      \"converter\": {\n        \"type\": \"icon\"\n      }\n    },\n    {\n      \"index\": 2,\n      \"name\": \"MapIcon\",\n      \"converter\": {\n        \"type\": \"icon\"\n      }\n    },\n    {\n      \"index\": 3,\n      \"name\": \"Name\"\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/FishParameter.json",
    "content": "{\n  \"sheet\": \"FishParameter\",\n  \"defaultColumn\": \"Item\",\n  \"definitions\": [\n    {\n      \"name\": \"Text\"\n    },\n    {\n      \"index\": 4,\n      \"name\": \"Item\",\n      \"converter\": {\n        \"type\": \"multiref\",\n        \"targets\": [\n          \"EventItem\",\n          \"Item\"\n        ]\n      }\n    },\n    {\n      \"index\": 5,\n      \"name\": \"GatheringItemLevel\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"GatheringItemLevelConvertTable\"\n      }\n    },\n    {\n      \"index\": 6,\n      \"name\": \"OceanStars\"\n    },\n    {\n      \"index\": 8,\n      \"name\": \"IsHidden\"\n    },\n    {\n      \"index\": 9,\n      \"name\": \"FishingRecordType\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"FishingRecordType\"\n      }\n    },\n    {\n      \"index\": 10,\n      \"name\": \"FishingSpot\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"FishingSpot\"\n      }\n    },\n    {\n      \"index\": 11,\n      \"name\": \"GatheringSubCategory\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"GatheringSubCategory\"\n      }\n    },\n    {\n      \"index\": 12,\n      \"name\": \"IsInLog\"\n    },\n    {\n      \"index\": 13,\n      \"name\": \"AchievementCredit\"\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/FishingBaitParameter.json",
    "content": "{\n  \"sheet\": \"FishingBaitParameter\",\n  \"definitions\": []\n}"
  },
  {
    "path": "SaintCoinach/Definitions/FishingNoteInfo.json",
    "content": "{\n  \"sheet\": \"FishingNoteInfo\",\n  \"defaultColumn\": \"Item\",\n  \"definitions\": [\n    {\n      \"index\": 0,\n      \"name\": \"Size\"\n    },\n    {\n      \"index\": 1,\n      \"name\": \"AquariumWater\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"AquariumWater\"\n      }\n    },\n    {\n      \"index\": 2,\n      \"name\": \"WeatherRestriction\"\n    },\n    {\n      \"index\": 3,\n      \"name\": \"TimeRestriction\"\n    },\n    {\n      \"index\": 4,\n      \"name\": \"SpecialConditions\"\n    },\n    {\n      \"index\": 5,\n      \"name\": \"IsCollectable\"\n    },\n    {\n      \"index\": 6,\n      \"name\": \"Item\",\n      \"converter\": {\n        \"type\": \"multiref\",\n        \"targets\": [\n          \"EventItem\",\n          \"Item\"\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "SaintCoinach/Definitions/FishingRecordType.json",
    "content": "{\n  \"sheet\": \"FishingRecordType\",\n  \"defaultColumn\": \"Addon\",\n  \"definitions\": [\n    {\n      \"name\": \"Addon\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"Addon\"\n      }\n    },\n    {\n      \"index\": 1,\n      \"name\": \"RankBRequirement\"\n    },\n    {\n      \"index\": 2,\n      \"name\": \"RankARequirement\"\n    },\n    {\n      \"index\": 3,\n      \"name\": \"RankAARequirement\"\n    },\n    {\n      \"index\": 4,\n      \"name\": \"RankAAARequirement\"\n    },\n    {\n      \"index\": 5,\n      \"name\": \"RankSRequirement\"\n    },\n    {\n      \"index\": 6,\n      \"name\": \"IsSpearfishing\"\n    }\n  ]\n}\n"
  },
  {
    "path": "SaintCoinach/Definitions/FishingRecordTypeTransient.json",
    "content": "{\n  \"sheet\": \"FishingRecordTypeTransient\",\n  \"defaultColumn\": \"Icon\",\n  \"definitions\": [\n    {\n      \"name\": \"Image\",\n      \"converter\": {\n        \"type\": \"icon\"\n      }\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/FishingSpot.json",
    "content": "{\n  \"sheet\": \"FishingSpot\",\n  \"defaultColumn\": \"PlaceName\",\n  \"definitions\": [\n    {\n      \"name\": \"GatheringLevel\"\n    },\n    {\n      \"index\": 1,\n      \"name\": \"BigFish{OnReach}\"\n    },\n    {\n      \"index\": 2,\n      \"name\": \"BigFish{OnEnd}\"\n    },\n    {\n      \"index\": 4,\n      \"name\": \"FishingSpotCategory\"\n    },\n    {\n      \"index\": 5,\n      \"name\": \"Rare\"\n    },\n    {\n      \"index\": 6,\n      \"name\": \"TerritoryType\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"TerritoryType\"\n      }\n    },\n    {\n      \"index\": 7,\n      \"name\": \"PlaceName{Main}\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"PlaceName\"\n      }\n    },\n    {\n      \"index\": 8,\n      \"name\": \"PlaceName{Sub}\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"PlaceName\"\n      }\n    },\n    {\n      \"index\": 9,\n      \"name\": \"X\"\n    },\n    {\n      \"index\": 10,\n      \"name\": \"Z\"\n    },\n    {\n      \"index\": 11,\n      \"name\": \"Radius\"\n    },\n    {\n      \"index\": 13,\n      \"type\": \"repeat\",\n      \"count\": 10,\n      \"definition\": {\n        \"name\": \"Item\",\n        \"converter\": {\n          \"type\": \"link\",\n          \"target\": \"Item\"\n        }\n      }\n    },\n    {\n      \"index\": 23,\n      \"name\": \"PlaceName\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"PlaceName\"\n      }\n    },\n    {\n      \"index\": 24,\n      \"name\": \"Order\"\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/FittingShop.json",
    "content": "{\n  \"sheet\": \"FittingShop\",\n  \"definitions\": []\n}"
  },
  {
    "path": "SaintCoinach/Definitions/FittingShopCategory.json",
    "content": "{\n  \"sheet\": \"FittingShopCategory\",\n  \"definitions\": []\n}"
  },
  {
    "path": "SaintCoinach/Definitions/FittingShopCategoryItem.json",
    "content": "{\n  \"sheet\": \"FittingShopCategoryItem\",\n  \"definitions\": []\n}"
  },
  {
    "path": "SaintCoinach/Definitions/FittingShopItemSet.json",
    "content": "{\n  \"sheet\": \"FittingShopItemSet\",\n  \"definitions\": []\n}"
  },
  {
    "path": "SaintCoinach/Definitions/Frontline03.json",
    "content": "{\n  \"sheet\": \"Frontline03\",\n  \"definitions\": [\n    {\n      \"index\": 9,\n      \"type\": \"repeat\",\n      \"count\": 3,\n      \"definition\": {\n        \"name\": \"EmptyIcon\",\n        \"converter\": {\n          \"type\": \"icon\"\n        }\n      }\n    },\n    {\n      \"index\": 12,\n      \"type\": \"repeat\",\n      \"count\": 3,\n      \"definition\": {\n        \"name\": \"MaelstromIcon\",\n        \"converter\": {\n          \"type\": \"icon\"\n        }\n      }\n    },\n    {\n      \"index\": 15,\n      \"type\": \"repeat\",\n      \"count\": 3,\n      \"definition\": {\n        \"name\": \"TwinAdderIcon\",\n        \"converter\": {\n          \"type\": \"icon\"\n        }\n      }\n    },\n    {\n      \"index\": 18,\n      \"type\": \"repeat\",\n      \"count\": 3,\n      \"definition\": {\n        \"name\": \"ImmortalFlamesIcon\",\n        \"converter\": {\n          \"type\": \"icon\"\n        }\n      }\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/FurnitureCatalogCategory.json",
    "content": "{\n  \"sheet\": \"FurnitureCatalogCategory\",\n  \"defaultColumn\": \"Category\",\n  \"definitions\": [\n    {\n      \"name\": \"Category\"\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/FurnitureCatalogItemList.json",
    "content": "{\n  \"sheet\": \"FurnitureCatalogItemList\",\n  \"defaultColumn\": \"Item\",\n  \"definitions\": [\n    {\n      \"name\": \"Category\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"FurnitureCatalogCategory\"\n      }\n    },\n    {\n      \"index\": 1,\n      \"name\": \"Item\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"Item\"\n      }\n    },\n    {\n      \"index\": 2,\n      \"name\": \"Patch\"\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/GCRankGridaniaFemaleText.json",
    "content": "{\n  \"sheet\": \"GCRankGridaniaFemaleText\",\n  \"defaultColumn\": \"Singular\",\n  \"definitions\": [\n    {\n      \"name\": \"Singular\"\n    },\n    {\n      \"index\": 1,\n      \"name\": \"Adjective\"\n    },\n    {\n      \"index\": 2,\n      \"name\": \"Plural\"\n    },\n    {\n      \"index\": 3,\n      \"name\": \"PossessivePronoun\"\n    },\n    {\n      \"index\": 4,\n      \"name\": \"StartsWithVowel\"\n    },\n    {\n      \"index\": 6,\n      \"name\": \"Pronoun\"\n    },\n    {\n      \"index\": 7,\n      \"name\": \"Article\"\n    },\n    {\n      \"index\": 8,\n      \"name\": \"Name{Rank}\"\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/GCRankGridaniaMaleText.json",
    "content": "{\n  \"sheet\": \"GCRankGridaniaMaleText\",\n  \"defaultColumn\": \"Singular\",\n  \"definitions\": [\n    {\n      \"name\": \"Singular\"\n    },\n    {\n      \"index\": 1,\n      \"name\": \"Adjective\"\n    },\n    {\n      \"index\": 2,\n      \"name\": \"Plural\"\n    },\n    {\n      \"index\": 3,\n      \"name\": \"PossessivePronoun\"\n    },\n    {\n      \"index\": 4,\n      \"name\": \"StartsWithVowel\"\n    },\n    {\n      \"index\": 6,\n      \"name\": \"Pronoun\"\n    },\n    {\n      \"index\": 7,\n      \"name\": \"Article\"\n    },\n    {\n      \"index\": 8,\n      \"name\": \"Name{Rank}\"\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/GCRankLimsaFemaleText.json",
    "content": "{\n  \"sheet\": \"GCRankLimsaFemaleText\",\n  \"defaultColumn\": \"Singular\",\n  \"definitions\": [\n    {\n      \"name\": \"Singular\"\n    },\n    {\n      \"index\": 1,\n      \"name\": \"Adjective\"\n    },\n    {\n      \"index\": 2,\n      \"name\": \"Plural\"\n    },\n    {\n      \"index\": 3,\n      \"name\": \"PossessivePronoun\"\n    },\n    {\n      \"index\": 4,\n      \"name\": \"StartsWithVowel\"\n    },\n    {\n      \"index\": 6,\n      \"name\": \"Pronoun\"\n    },\n    {\n      \"index\": 7,\n      \"name\": \"Article\"\n    },\n    {\n      \"index\": 8,\n      \"name\": \"Name{Rank}\"\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/GCRankLimsaMaleText.json",
    "content": "{\n  \"sheet\": \"GCRankLimsaMaleText\",\n  \"defaultColumn\": \"Singular\",\n  \"definitions\": [\n    {\n      \"name\": \"Singular\"\n    },\n    {\n      \"index\": 1,\n      \"name\": \"Adjective\"\n    },\n    {\n      \"index\": 2,\n      \"name\": \"Plural\"\n    },\n    {\n      \"index\": 3,\n      \"name\": \"PossessivePronoun\"\n    },\n    {\n      \"index\": 4,\n      \"name\": \"StartsWithVowel\"\n    },\n    {\n      \"index\": 6,\n      \"name\": \"Pronoun\"\n    },\n    {\n      \"index\": 7,\n      \"name\": \"Article\"\n    },\n    {\n      \"index\": 8,\n      \"name\": \"Name{Rank}\"\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/GCRankUldahFemaleText.json",
    "content": "{\n  \"sheet\": \"GCRankUldahFemaleText\",\n  \"defaultColumn\": \"Singular\",\n  \"definitions\": [\n    {\n      \"name\": \"Singular\"\n    },\n    {\n      \"index\": 1,\n      \"name\": \"Adjective\"\n    },\n    {\n      \"index\": 2,\n      \"name\": \"Plural\"\n    },\n    {\n      \"index\": 3,\n      \"name\": \"PossessivePronoun\"\n    },\n    {\n      \"index\": 4,\n      \"name\": \"StartsWithVowel\"\n    },\n    {\n      \"index\": 6,\n      \"name\": \"Pronoun\"\n    },\n    {\n      \"index\": 7,\n      \"name\": \"Article\"\n    },\n    {\n      \"index\": 8,\n      \"name\": \"Name{Rank}\"\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/GCRankUldahMaleText.json",
    "content": "{\n  \"sheet\": \"GCRankUldahMaleText\",\n  \"defaultColumn\": \"Singular\",\n  \"definitions\": [\n    {\n      \"name\": \"Singular\"\n    },\n    {\n      \"index\": 1,\n      \"name\": \"Adjective\"\n    },\n    {\n      \"index\": 2,\n      \"name\": \"Plural\"\n    },\n    {\n      \"index\": 3,\n      \"name\": \"PossessivePronoun\"\n    },\n    {\n      \"index\": 4,\n      \"name\": \"StartsWithVowel\"\n    },\n    {\n      \"index\": 6,\n      \"name\": \"Pronoun\"\n    },\n    {\n      \"index\": 7,\n      \"name\": \"Article\"\n    },\n    {\n      \"index\": 8,\n      \"name\": \"Name{Rank}\"\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/GCScripShopCategory.json",
    "content": "{\n  \"sheet\": \"GCScripShopCategory\",\n  \"definitions\": [\n    {\n      \"name\": \"GrandCompany\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"GrandCompany\"\n      }\n    },\n    {\n      \"index\": 1,\n      \"name\": \"Tier\"\n    },\n    {\n      \"index\": 2,\n      \"name\": \"SubCategory\"\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/GCScripShopItem.json",
    "content": "{\n  \"sheet\": \"GCScripShopItem\",\n  \"defaultColumn\": \"Item\",\n  \"definitions\": [\n    {\n      \"name\": \"Item\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"Item\"\n      }\n    },\n    {\n      \"index\": 1,\n      \"name\": \"Required{GrandCompanyRank}\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"GrandCompanyRank\"\n      }\n    },\n    {\n      \"index\": 2,\n      \"name\": \"Cost{GCSeals}\"\n    },\n    {\n      \"index\": 3,\n      \"name\": \"SortKey\"\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/GCShop.json",
    "content": "{\n  \"sheet\": \"GCShop\",\n  \"isGenericReferenceTarget\": true,\n  \"definitions\": [\n    {\n      \"name\": \"GrandCompany\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"GrandCompany\"\n      }\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/GCShopItemCategory.json",
    "content": "{\n  \"sheet\": \"GCShopItemCategory\",\n  \"defaultColumn\": \"Name\",\n  \"definitions\": [\n    {\n      \"name\": \"Name\"\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/GCSupplyDuty.json",
    "content": "{\n  \"sheet\": \"GCSupplyDuty\",\n  \"definitions\": [\n    {\n      \"type\": \"repeat\",\n      \"count\": 11,\n      \"definition\": {\n        \"type\": \"repeat\",\n        \"count\": 3,\n        \"definition\": {\n          \"type\": \"group\",\n          \"members\": [\n            {\n              \"name\": \"Item\",\n              \"converter\": {\n                \"type\": \"link\",\n                \"target\": \"Item\"\n              }\n            },\n            {\n              \"name\": \"ItemCount\"\n            }\n          ]\n        }\n      }\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/GCSupplyDutyReward.json",
    "content": "{\n  \"sheet\": \"GCSupplyDutyReward\",\n  \"definitions\": [\n    {\n      \"name\": \"Experience{Supply}\"\n    },\n    {\n      \"index\": 1,\n      \"name\": \"Experience{Provisioning}\"\n    },\n    {\n      \"index\": 2,\n      \"name\": \"Seals{ExpertDelivery}\"\n    },\n    {\n      \"index\": 3,\n      \"name\": \"Seals{Supply}\"\n    },\n    {\n      \"index\": 4,\n      \"name\": \"Seals{Provisioning}\"\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/GFATE.json",
    "content": "{\n  \"sheet\": \"GFATE\",\n  \"definitions\": [\n    {\n      \"index\": 7,\n      \"type\": \"repeat\",\n      \"count\": 15,\n      \"definition\": {\n        \"name\": \"LGB{PopRange}\",\n        \"converter\": {\n          \"type\": \"link\",\n          \"target\": \"Level\"\n        }\n      }\n    },\n    {\n      \"index\": 23,\n      \"type\": \"repeat\",\n      \"count\": 15,\n      \"definition\": {\n        \"name\": \"Icon\",\n        \"converter\": {\n          \"type\": \"icon\"\n        }\n      }\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/GFateClimbing2.json",
    "content": "{\n  \"sheet\": \"GFateClimbing2\",\n  \"definitions\": [\n    {\n      \"name\": \"ContentEntry\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"ContentEntry\"\n      }\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/GFateClimbing2Content.json",
    "content": "{\n  \"sheet\": \"GFateClimbing2Content\",\n  \"definitions\": [\n    {\n      \"name\": \"PublicContentTextData\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"PublicContentTextData\"\n      }\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/GFateClimbing2TotemType.json",
    "content": "{\n  \"sheet\": \"GFateClimbing2TotemType\",\n  \"definitions\": [\n    {\n      \"name\": \"PublicContentTextData\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"PublicContentTextData\"\n      }\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/GFateRideShooting.json",
    "content": "{\n  \"sheet\": \"GFateRideShooting\",\n  \"definitions\": [\n    {\n      \"name\": \"ContentEntry\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"ContentEntry\"\n      }\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/GFateType.json",
    "content": "{\n  \"sheet\": \"GFateType\",\n  \"definitions\": []\n}"
  },
  {
    "path": "SaintCoinach/Definitions/GameRewardObtainType.json",
    "content": "{\n  \"sheet\": \"GameRewardObtainType\",\n  \"definitions\": []\n}"
  },
  {
    "path": "SaintCoinach/Definitions/GardeningSeed.json",
    "content": "{\n  \"sheet\": \"GardeningSeed\",\n  \"defaultColumn\": \"Item\",\n  \"definitions\": [\n    {\n      \"name\": \"Item\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"Item\"\n      }\n    },\n    {\n      \"index\": 1,\n      \"name\": \"ModelID\"\n    },\n    {\n      \"index\": 2,\n      \"name\": \"Icon\",\n      \"converter\": {\n        \"type\": \"icon\"\n      }\n    },\n    {\n      \"index\": 3,\n      \"name\": \"SE\"\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/GathererCrafterTool.json",
    "content": "{\n  \"sheet\": \"GathererCrafterTool\",\n  \"definitions\": []\n}"
  },
  {
    "path": "SaintCoinach/Definitions/GathererReductionReward.json",
    "content": "{\n  \"sheet\": \"GathererReductionReward\",\n  \"definitions\": []\n}"
  },
  {
    "path": "SaintCoinach/Definitions/GatheringCondition.json",
    "content": "{\n  \"sheet\": \"GatheringCondition\",\n  \"defaultColumn\": \"Text\",\n  \"definitions\": [\n    {\n      \"name\": \"Text\"\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/GatheringExp.json",
    "content": "{\n  \"sheet\": \"GatheringExp\",\n  \"definitions\": [\n    {\n      \"name\": \"Exp\"\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/GatheringItem.json",
    "content": "{\n  \"sheet\": \"GatheringItem\",\n  \"defaultColumn\": \"Item\",\n  \"definitions\": [\n    {\n      \"name\": \"Item\",\n      \"converter\": {\n        \"type\": \"multiref\",\n        \"targets\": [\n          \"Item\",\n          \"EventItem\"\n        ]\n      }\n    },\n    {\n      \"index\": 1,\n      \"name\": \"GatheringItemLevel\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"GatheringItemLevelConvertTable\"\n      }\n    },\n    {\n      \"index\": 3,\n      \"name\": \"Quest\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"Quest\"\n      }\n    },\n    {\n      \"index\": 5,\n      \"name\": \"IsHidden\"\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/GatheringItemLevelConvertTable.json",
    "content": "{\n  \"sheet\": \"GatheringItemLevelConvertTable\",\n  \"definitions\": [\n    {\n      \"name\": \"GatheringItemLevel\"\n    },\n    {\n      \"index\": 1,\n      \"name\": \"Stars\"\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/GatheringItemPoint.json",
    "content": "{\n  \"sheet\": \"GatheringItemPoint\",\n  \"defaultColumn\": \"GatheringPoint\",\n  \"definitions\": [\n    {\n      \"name\": \"GatheringPoint\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"GatheringPoint\"\n      }\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/GatheringLeve.json",
    "content": "{\n  \"sheet\": \"GatheringLeve\",\n  \"definitions\": [\n    {\n      \"type\": \"repeat\",\n      \"count\": 4,\n      \"definition\": {\n        \"name\": \"Route\",\n        \"converter\": {\n          \"type\": \"link\",\n          \"target\": \"GatheringLeveRoute\"\n        }\n      }\n    },\n    {\n      \"index\": 4,\n      \"name\": \"RequiredItem[0]\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"EventItem\"\n      }\n    },\n    {\n      \"index\": 5,\n      \"name\": \"RequiredItemQty[0]\"\n    },\n    {\n      \"index\": 6,\n      \"name\": \"RequiredItem[1]\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"EventItem\"\n      }\n    },\n    {\n      \"index\": 7,\n      \"name\": \"RequiredItemQty[1]\"\n    },\n    {\n      \"index\": 8,\n      \"name\": \"RequiredItem[2]\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"EventItem\"\n      }\n    },\n    {\n      \"index\": 9,\n      \"name\": \"RequiredItemQty[2]\"\n    },\n    {\n      \"index\": 10,\n      \"name\": \"RequiredItem[3]\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"EventItem\"\n      }\n    },\n    {\n      \"index\": 11,\n      \"name\": \"RequiredItemQty[3]\"\n    },\n    {\n      \"index\": 12,\n      \"name\": \"ItemNumber\"\n    },\n    {\n      \"index\": 13,\n      \"name\": \"Rule\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"GatheringLeveRule\"\n      }\n    },\n    {\n      \"index\": 14,\n      \"name\": \"Varient\"\n    },\n    {\n      \"index\": 15,\n      \"name\": \"Objective[0]\"\n    },\n    {\n      \"index\": 16,\n      \"name\": \"Objective[1]\"\n    },\n    {\n      \"index\": 17,\n      \"name\": \"BNpcEntry\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"GatheringLeveBNpcEntry\"\n      }\n    },\n    {\n      \"index\": 18,\n      \"name\": \"UseSecondaryTool\"\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/GatheringLeveRoute.json",
    "content": "{\n  \"sheet\": \"GatheringLeveRoute\",\n  \"definitions\": [\n    {\n      \"type\": \"repeat\",\n      \"count\": 12,\n      \"definition\": {\n        \"type\": \"group\",\n        \"members\": [\n          {\n            \"name\": \"GatheringPoint\",\n            \"converter\": {\n              \"type\": \"link\",\n              \"target\": \"GatheringPoint\"\n            }\n          },\n          {\n            \"name\": \"PopRange\",\n            \"converter\": {\n              \"type\": \"link\",\n              \"target\": \"Level\"\n            }\n          }\n        ]\n      }\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/GatheringLeveRule.json",
    "content": "{\n  \"sheet\": \"GatheringLeveRule\",\n  \"defaultColumn\": \"Rule\",\n  \"definitions\": [\n    {\n      \"name\": \"Rule\"\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/GatheringNotebookList.json",
    "content": "{\n  \"sheet\": \"GatheringNotebookList\",\n  \"definitions\": [\n    {\n      \"index\": 1,\n      \"type\": \"repeat\",\n      \"count\": 100,\n      \"definition\": {\n        \"name\": \"GatheringItem\",\n        \"converter\": {\n          \"type\": \"link\",\n          \"target\": \"GatheringItem\"\n        }\n      }\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/GatheringPoint.json",
    "content": "{\n  \"sheet\": \"GatheringPoint\",\n  \"definitions\": [\n    {\n      \"name\": \"Type\"\n    },\n    {\n      \"index\": 2,\n      \"name\": \"GatheringPointBase\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"GatheringPointBase\"\n      }\n    },\n    {\n      \"index\": 3,\n      \"name\": \"Count\"\n    },\n    {\n      \"index\": 4,\n      \"type\": \"repeat\",\n      \"count\": 2,\n      \"definition\": {\n        \"name\": \"GatheringPointBonus\",\n        \"converter\": {\n          \"type\": \"link\",\n          \"target\": \"GatheringPointBonus\"\n        }\n      }\n    },\n    {\n      \"index\": 6,\n      \"name\": \"TerritoryType\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"TerritoryType\"\n      }\n    },\n    {\n      \"index\": 7,\n      \"name\": \"PlaceName\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"PlaceName\"\n      }\n    },\n    {\n      \"index\": 8,\n      \"name\": \"GatheringSubCategory\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"GatheringSubCategory\"\n      }\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/GatheringPointBase.json",
    "content": "{\n  \"sheet\": \"GatheringPointBase\",\n  \"definitions\": [\n    {\n      \"name\": \"GatheringType\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"GatheringType\"\n      }\n    },\n    {\n      \"index\": 1,\n      \"name\": \"GatheringLevel\"\n    },\n    {\n      \"index\": 2,\n      \"type\": \"repeat\",\n      \"count\": 8,\n      \"definition\": {\n        \"name\": \"Item\",\n        \"converter\": {\n          \"type\": \"multiref\",\n          \"targets\": [\n            \"GatheringItem\",\n            \"SpearfishingItem\"\n          ]\n        }\n      }\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/GatheringPointBonus.json",
    "content": "{\n  \"sheet\": \"GatheringPointBonus\",\n  \"definitions\": [\n    {\n      \"name\": \"Condition\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"GatheringCondition\"\n      }\n    },\n    {\n      \"index\": 1,\n      \"name\": \"ConditionValue\"\n    },\n    {\n      \"index\": 3,\n      \"name\": \"BonusType\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"GatheringPointBonusType\"\n      }\n    },\n    {\n      \"index\": 4,\n      \"name\": \"BonusValue\"\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/GatheringPointBonusType.json",
    "content": "{\n  \"sheet\": \"GatheringPointBonusType\",\n  \"defaultColumn\": \"Text\",\n  \"definitions\": [\n    {\n      \"name\": \"Text\"\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/GatheringPointName.json",
    "content": "{\n  \"sheet\": \"GatheringPointName\",\n  \"defaultColumn\": \"Singular\",\n  \"definitions\": [\n    {\n      \"name\": \"Singular\"\n    },\n    {\n      \"index\": 1,\n      \"name\": \"Adjective\"\n    },\n    {\n      \"index\": 2,\n      \"name\": \"Plural\"\n    },\n    {\n      \"index\": 3,\n      \"name\": \"PossessivePronoun\"\n    },\n    {\n      \"index\": 4,\n      \"name\": \"StartsWithVowel\"\n    },\n    {\n      \"index\": 6,\n      \"name\": \"Pronoun\"\n    },\n    {\n      \"index\": 7,\n      \"name\": \"Article\"\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/GatheringPointTransient.json",
    "content": "{\n  \"sheet\": \"GatheringPointTransient\",\n  \"definitions\": [\n    {\n      \"name\": \"EphemeralStartTime\"\n    },\n    {\n      \"index\": 1,\n      \"name\": \"EphemeralEndTime\"\n    },\n    {\n      \"index\": 2,\n      \"name\": \"GatheringRarePopTimeTable\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"GatheringRarePopTimeTable\"\n      }\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/GatheringRarePopTimeTable.json",
    "content": "{\n  \"sheet\": \"GatheringRarePopTimeTable\",\n  \"definitions\": [\n    {\n      \"type\": \"repeat\",\n      \"count\": 3,\n      \"definition\": {\n        \"type\": \"group\",\n        \"members\": [\n          {\n            \"name\": \"StartTime\"\n          },\n          {\n            \"name\": \"Duration(m)\"\n          }\n        ]\n      }\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/GatheringSubCategory.json",
    "content": "{\n  \"sheet\": \"GatheringSubCategory\",\n  \"defaultColumn\": \"FolkloreBook\",\n  \"definitions\": [\n    {\n      \"name\": \"GatheringType\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"GatheringType\"\n      }\n    },\n    {\n      \"index\": 1,\n      \"name\": \"ClassJob\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"ClassJob\"\n      }\n    },\n    {\n      \"index\": 2,\n      \"name\": \"Quest\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"Quest\"\n      }\n    },\n    {\n      \"index\": 3,\n      \"name\": \"Division\"\n    },\n    {\n      \"index\": 4,\n      \"name\": \"Item\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"Item\"\n      }\n    },\n    {\n      \"index\": 5,\n      \"name\": \"FolkloreBook\"\n    }\n  ]\n}\n"
  },
  {
    "path": "SaintCoinach/Definitions/GatheringType.json",
    "content": "{\n  \"sheet\": \"GatheringType\",\n  \"defaultColumn\": \"Name\",\n  \"definitions\": [\n    {\n      \"name\": \"Name\"\n    },\n    {\n      \"index\": 1,\n      \"name\": \"Icon{Main}\",\n      \"converter\": {\n        \"type\": \"icon\"\n      }\n    },\n    {\n      \"index\": 2,\n      \"name\": \"Icon{Off}\",\n      \"converter\": {\n        \"type\": \"icon\"\n      }\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/GcArmyCaptureTactics.json",
    "content": "{\n  \"sheet\": \"GcArmyCaptureTactics\",\n  \"defaultColumn\": \"Name\",\n  \"definitions\": [\n    {\n      \"name\": \"Name\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"Status\"\n      }\n    },\n    {\n      \"index\": 1,\n      \"name\": \"HP\"\n    },\n    {\n      \"index\": 2,\n      \"name\": \"DamageDealt\"\n    },\n    {\n      \"index\": 3,\n      \"name\": \"DamageReceived\"\n    },\n    {\n      \"index\": 4,\n      \"name\": \"Tactic\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"Addon\"\n      }\n    },\n    {\n      \"index\": 5,\n      \"name\": \"Icon\",\n      \"converter\": {\n        \"type\": \"icon\"\n      }\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/GcArmyEquipPreset.json",
    "content": "{\n  \"sheet\": \"GcArmyEquipPreset\",\n  \"definitions\": [\n    {\n      \"name\": \"MainHand\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"Item\"\n      }\n    },\n    {\n      \"index\": 1,\n      \"name\": \"OffHand\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"Item\"\n      }\n    },\n    {\n      \"index\": 2,\n      \"name\": \"Head\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"Item\"\n      }\n    },\n    {\n      \"index\": 3,\n      \"name\": \"Body\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"Item\"\n      }\n    },\n    {\n      \"index\": 4,\n      \"name\": \"Gloves\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"Item\"\n      }\n    },\n    {\n      \"index\": 5,\n      \"name\": \"Legs\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"Item\"\n      }\n    },\n    {\n      \"index\": 6,\n      \"name\": \"Feet\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"Item\"\n      }\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/GcArmyExpedition.json",
    "content": "{\n  \"sheet\": \"GcArmyExpedition\",\n  \"defaultColumn\": \"Name\",\n  \"definitions\": [\n    {\n      \"name\": \"Required{Flag}\"\n    },\n    {\n      \"index\": 1,\n      \"name\": \"Unlock{Flag}\"\n    },\n    {\n      \"index\": 2,\n      \"name\": \"Required{Level}\"\n    },\n    {\n      \"index\": 3,\n      \"name\": \"Required{Seals}\"\n    },\n    {\n      \"index\": 4,\n      \"name\": \"Reward{Experience}\"\n    },\n    {\n      \"index\": 5,\n      \"name\": \"Percent{Base}\"\n    },\n    {\n      \"index\": 7,\n      \"name\": \"GcArmyExpeditionType\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"GcArmyExpeditionType\"\n      }\n    },\n    {\n      \"index\": 8,\n      \"name\": \"Name\"\n    },\n    {\n      \"index\": 9,\n      \"name\": \"Description\"\n    },\n    {\n      \"index\": 10,\n      \"type\": \"repeat\",\n      \"count\": 6,\n      \"definition\": {\n        \"type\": \"group\",\n        \"members\": [\n          {\n            \"name\": \"Reward{Item}\",\n            \"converter\": {\n              \"type\": \"link\",\n              \"target\": \"Item\"\n            }\n          }\n        ]\n      }\n    },\n    {\n      \"index\": 16,\n      \"type\": \"repeat\",\n      \"count\": 6,\n      \"definition\": {\n        \"type\": \"group\",\n        \"members\": [\n          {\n            \"name\": \"Reward{Quantity}\"\n          }\n        ]\n      }\n    },\n    {\n      \"index\": 22,\n      \"type\": \"repeat\",\n      \"count\": 6,\n      \"definition\": {\n        \"type\": \"group\",\n        \"members\": [\n          {\n            \"name\": \"Required{Physical}\"\n          }\n        ]\n      }\n    },\n    {\n      \"index\": 28,\n      \"type\": \"repeat\",\n      \"count\": 6,\n      \"definition\": {\n        \"type\": \"group\",\n        \"members\": [\n          {\n            \"name\": \"Percent{PhysicalMet}\"\n          }\n        ]\n      }\n    },\n    {\n      \"index\": 34,\n      \"type\": \"repeat\",\n      \"count\": 6,\n      \"definition\": {\n        \"type\": \"group\",\n        \"members\": [\n          {\n            \"name\": \"Required{Mental}\"\n          }\n        ]\n      }\n    },\n    {\n      \"index\": 40,\n      \"type\": \"repeat\",\n      \"count\": 6,\n      \"definition\": {\n        \"type\": \"group\",\n        \"members\": [\n          {\n            \"name\": \"Percent{MentalMet}\"\n          }\n        ]\n      }\n    },\n    {\n      \"index\": 46,\n      \"type\": \"repeat\",\n      \"count\": 6,\n      \"definition\": {\n        \"type\": \"group\",\n        \"members\": [\n          {\n            \"name\": \"Required{Tactical}\"\n          }\n        ]\n      }\n    },\n    {\n      \"index\": 52,\n      \"type\": \"repeat\",\n      \"count\": 6,\n      \"definition\": {\n        \"type\": \"group\",\n        \"members\": [\n          {\n            \"name\": \"Percent{TacticalMet}\"\n          }\n        ]\n      }\n    },\n    {\n      \"index\": 58,\n      \"type\": \"repeat\",\n      \"count\": 6,\n      \"definition\": {\n        \"type\": \"group\",\n        \"members\": [\n          {\n            \"name\": \"Percent{AllMet}\"\n          }\n        ]\n      }\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/GcArmyExpeditionMemberBonus.json",
    "content": "{\n  \"sheet\": \"GcArmyExpeditionMemberBonus\",\n  \"definitions\": [\n    {\n      \"name\": \"Race\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"Race\"\n      }\n    },\n    {\n      \"index\": 1,\n      \"name\": \"ClassJob\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"ClassJob\"\n      }\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/GcArmyExpeditionType.json",
    "content": "{\n  \"sheet\": \"GcArmyExpeditionType\",\n  \"defaultColumn\": \"Name\",\n  \"definitions\": [\n    {\n      \"name\": \"Name\"\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/GcArmyMemberGrow.json",
    "content": "{\n  \"sheet\": \"GcArmyMemberGrow\",\n  \"definitions\": [\n    {\n      \"name\": \"ClassJob\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"ClassJob\"\n      }\n    },\n    {\n      \"index\": 1,\n      \"name\": \"ClassBook\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"Item\"\n      }\n    },\n    {\n      \"index\": 2,\n      \"type\": \"repeat\",\n      \"count\": 60,\n      \"definition\": {\n        \"name\": \"EquipPreset\",\n        \"converter\": {\n          \"type\": \"link\",\n          \"target\": \"GcArmyEquipPreset\"\n        }\n      }\n    },\n    {\n      \"index\": 63,\n      \"type\": \"repeat\",\n      \"count\": 60,\n      \"definition\": {\n        \"name\": \"Physical\"\n      }\n    },\n    {\n      \"index\": 124,\n      \"type\": \"repeat\",\n      \"count\": 60,\n      \"definition\": {\n        \"name\": \"Mental\"\n      }\n    },\n    {\n      \"index\": 185,\n      \"type\": \"repeat\",\n      \"count\": 60,\n      \"definition\": {\n        \"name\": \"Tactical\"\n      }\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/GcArmyTraining.json",
    "content": "{\n  \"sheet\": \"GcArmyTraining\",\n  \"defaultColumn\": \"Name\",\n  \"definitions\": [\n    {\n      \"name\": \"PhysicalBonus\"\n    },\n    {\n      \"index\": 1,\n      \"name\": \"MentalBonus\"\n    },\n    {\n      \"index\": 2,\n      \"name\": \"TacticalBonus\"\n    },\n    {\n      \"index\": 3,\n      \"name\": \"Experience\"\n    },\n    {\n      \"index\": 4,\n      \"name\": \"Name\"\n    },\n    {\n      \"index\": 5,\n      \"name\": \"Description\"\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/GeneralAction.json",
    "content": "{\n  \"sheet\": \"GeneralAction\",\n  \"defaultColumn\": \"Name\",\n  \"definitions\": [\n    {\n      \"name\": \"Name\"\n    },\n    {\n      \"index\": 1,\n      \"name\": \"Description\"\n    },\n    {\n      \"index\": 3,\n      \"name\": \"Action\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"Action\"\n      }\n    },\n    {\n      \"index\": 4,\n      \"name\": \"UnlockLink\"\n    },\n    {\n      \"index\": 5,\n      \"name\": \"Recast\"\n    },\n    {\n      \"index\": 6,\n      \"name\": \"UIPriority\"\n    },\n    {\n      \"index\": 7,\n      \"name\": \"Icon\",\n      \"converter\": {\n        \"type\": \"icon\"\n      }\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/GilShop.json",
    "content": "{\n  \"sheet\": \"GilShop\",\n  \"defaultColumn\": \"Name\",\n  \"isGenericReferenceTarget\": true,\n  \"definitions\": [\n    {\n      \"name\": \"Name\"\n    },\n    {\n      \"index\": 1,\n      \"name\": \"Icon\",\n      \"converter\": {\n        \"type\": \"icon\"\n      }\n    },\n    {\n      \"index\": 2,\n      \"name\": \"Quest\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"Quest\"\n      }\n    },\n    {\n      \"index\": 3,\n      \"name\": \"AcceptTalk\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"DefaultTalk\"\n      }\n    },\n    {\n      \"index\": 4,\n      \"name\": \"FailTalk\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"DefaultTalk\"\n      }\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/GilShopItem.json",
    "content": "{\n  \"sheet\": \"GilShopItem\",\n  \"defaultColumn\": \"Item\",\n  \"definitions\": [\n    {\n      \"name\": \"Item\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"Item\"\n      }\n    },\n    {\n      \"index\": 4,\n      \"type\": \"repeat\",\n      \"count\": 2,\n      \"definition\": {\n        \"name\": \"Quest{Required}\",\n        \"converter\": {\n          \"type\": \"link\",\n          \"target\": \"Quest\"\n        }\n      }\n    },\n    {\n      \"index\": 6,\n      \"name\": \"Achievement{Required}\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"Achievement\"\n      }\n    },\n    {\n      \"index\": 8,\n      \"name\": \"State{Required}\"\n    },\n    {\n      \"index\": 9,\n      \"name\": \"Patch\"\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/GimmickAccessor.json",
    "content": "{\n  \"sheet\": \"GimmickAccessor\",\n  \"definitions\": [\n    {\n      \"name\": \"Param0\"\n    },\n    {\n      \"index\": 1,\n      \"name\": \"Param1\"\n    },\n    {\n      \"index\": 2,\n      \"name\": \"Param2\"\n    },\n    {\n      \"index\": 3,\n      \"name\": \"Type\"\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/GimmickJump.json",
    "content": "{\n  \"sheet\": \"GimmickJump\",\n  \"definitions\": [\n    {\n      \"name\": \"FallDamage\"\n    },\n    {\n      \"index\": 1,\n      \"name\": \"Height\"\n    },\n    {\n      \"index\": 2,\n      \"name\": \"LoopMotion\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"ActionTimeline\"\n      }\n    },\n    {\n      \"index\": 3,\n      \"name\": \"EndMotion\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"ActionTimeline\"\n      }\n    },\n    {\n      \"index\": 4,\n      \"name\": \"StartClient\"\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/GimmickRect.json",
    "content": "{\n  \"sheet\": \"GimmickRect\",\n  \"definitions\": [\n    {\n      \"name\": \"LayoutID\"\n    },\n    {\n      \"index\": 1,\n      \"name\": \"TriggerIn\"\n    },\n    {\n      \"index\": 2,\n      \"name\": \"Param0\"\n    },\n    {\n      \"index\": 6,\n      \"name\": \"TriggerOut\"\n    },\n    {\n      \"index\": 7,\n      \"name\": \"Param1\"\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/Glasses.json",
    "content": "{\n  \"sheet\": \"Glasses\",\n  \"defaultColumn\": \"Name\",\n  \"definitions\": [\n    {\n      \"index\": 1,\n      \"name\": \"GlassesStyle\",\n        \"converter\": {\n          \"type\": \"link\",\n          \"target\": \"GlassesStyle\"\n        }\n    },\n    {\n      \"index\": 2,\n      \"name\": \"Icon\",\n      \"converter\": {\n        \"type\": \"icon\"\n      }\n    },\n    {\n      \"index\": 4,\n      \"name\": \"Singular\"\n    },\n    {\n      \"index\": 6,\n      \"name\": \"Plural\"\n    },\n    {\n      \"index\": 12,\n      \"name\": \"Description\"\n    },\n    {\n      \"index\": 13,\n      \"name\": \"Name\"\n    }\n  ]\n}\n"
  },
  {
    "path": "SaintCoinach/Definitions/GlassesStyle.json",
    "content": "{\n  \"sheet\": \"GlassesStyle\",\n  \"defaultColumn\": \"Name\",\n  \"definitions\": [\n    {\n      \"index\": 1,\n      \"name\": \"Icon\",\n      \"converter\": {\n        \"type\": \"icon\"\n      }\n    },\n    {\n      \"index\": 2,\n      \"name\": \"Order\"\n    },\n    {\n      \"index\": 3,\n      \"type\": \"repeat\",\n      \"count\": 12,\n      \"definition\": {\n        \"name\": \"Glasses\",\n        \"converter\": {\n          \"type\": \"link\",\n          \"target\": \"Glasses\"\n        }\n      }\n    },\n    {\n      \"index\": 15,\n      \"name\": \"Singular\"\n    },\n    {\n      \"index\": 17,\n      \"name\": \"Plural\"\n    },\n    {\n      \"index\": 23,\n      \"name\": \"Name\"\n    }\n  ]\n}\n"
  },
  {
    "path": "SaintCoinach/Definitions/GoldSaucerArcadeMachine.json",
    "content": "{\n  \"sheet\": \"GoldSaucerArcadeMachine\",\n  \"definitions\": [\n    {\n      \"index\": 6,\n      \"name\": \"Fail Image\",\n      \"converter\": {\n        \"type\": \"icon\"\n      }\n    },\n    {\n      \"index\": 35,\n      \"name\": \"Poor\",\n      \"converter\": {\n        \"type\": \"icon\"\n      }\n    },\n    {\n      \"index\": 36,\n      \"name\": \"Good\",\n      \"converter\": {\n        \"type\": \"icon\"\n      }\n    },\n    {\n      \"index\": 37,\n      \"name\": \"Great\",\n      \"converter\": {\n        \"type\": \"icon\"\n      }\n    },\n    {\n      \"index\": 38,\n      \"name\": \"Excellent\",\n      \"converter\": {\n        \"type\": \"icon\"\n      }\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/GoldSaucerTextData.json",
    "content": "{\n  \"sheet\": \"GoldSaucerTextData\",\n  \"defaultColumn\": \"Text\",\n  \"definitions\": [\n    {\n      \"name\": \"Text\"\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/GrandCompany.json",
    "content": "{\n  \"sheet\": \"GrandCompany\",\n  \"defaultColumn\": \"Name\",\n  \"definitions\": [\n    {\n      \"name\": \"Name\"\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/GrandCompanyRank.json",
    "content": "{\n  \"sheet\": \"GrandCompanyRank\",\n  \"definitions\": [\n    {\n      \"name\": \"Tier\"\n    },\n    {\n      \"index\": 1,\n      \"name\": \"Order\"\n    },\n    {\n      \"index\": 2,\n      \"name\": \"MaxSeals\"\n    },\n    {\n      \"index\": 3,\n      \"name\": \"RequiredSeals\"\n    },\n    {\n      \"index\": 4,\n      \"name\": \"Icon{Maelstrom}\",\n      \"converter\": {\n        \"type\": \"icon\"\n      }\n    },\n    {\n      \"index\": 5,\n      \"name\": \"Icon{Serpents}\",\n      \"converter\": {\n        \"type\": \"icon\"\n      }\n    },\n    {\n      \"index\": 6,\n      \"name\": \"Icon{Flames}\",\n      \"converter\": {\n        \"type\": \"icon\"\n      }\n    },\n    {\n      \"index\": 7,\n      \"name\": \"Quest{Maelstrom}\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"Quest\"\n      }\n    },\n    {\n      \"index\": 8,\n      \"name\": \"Quest{Serpents}\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"Quest\"\n      }\n    },\n    {\n      \"index\": 9,\n      \"name\": \"Quest{Flames}\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"Quest\"\n      }\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/GroupPoseFrame.json",
    "content": "{\n  \"sheet\": \"GroupPoseFrame\",\n  \"defaultColumn\": \"Text\",\n  \"definitions\": [\n    {\n      \"index\": 1,\n      \"name\": \"Image\",\n      \"converter\": {\n        \"type\": \"icon\"\n      }\n    },\n    {\n      \"index\": 2,\n      \"name\": \"GridText\"\n    },\n    {\n      \"index\": 7,\n      \"name\": \"Text\"\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/GroupPoseStamp.json",
    "content": "{\n  \"sheet\": \"GroupPoseStamp\",\n  \"defaultColumn\": \"Name\",\n  \"definitions\": [\n    {\n      \"name\": \"StampIcon\",\n      \"converter\": {\n        \"type\": \"icon\"\n      }\n    },\n    {\n      \"index\": 2,\n      \"name\": \"Category\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"GroupPoseStampCategory\"\n      }\n    },\n    {\n      \"index\": 10,\n      \"name\": \"Name\"\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/GroupPoseStampCategory.json",
    "content": "{\n  \"sheet\": \"GroupPoseStampCategory\",\n  \"defaultColumn\": \"Name\",\n  \"definitions\": [\n    {\n      \"index\": 1,\n      \"name\": \"Name\"\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/GuardianDeity.json",
    "content": "{\n  \"sheet\": \"GuardianDeity\",\n  \"defaultColumn\": \"Name\",\n  \"definitions\": [\n    {\n      \"name\": \"Name\"\n    },\n    {\n      \"index\": 1,\n      \"name\": \"Description\"\n    },\n    {\n      \"index\": 2,\n      \"name\": \"Icon\",\n      \"converter\": {\n        \"type\": \"icon\"\n      }\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/Guide.json",
    "content": "{\n  \"sheet\": \"Guide\",\n  \"definitions\": [\n    {\n      \"name\": \"GuideTitle\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"GuideTitle\"\n      }\n    },\n    {\n      \"index\": 1,\n      \"name\": \"GuidePage\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"GuidePage\"\n      }\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/GuidePage.json",
    "content": "{\n  \"sheet\": \"GuidePage\",\n  \"definitions\": [\n    {\n      \"index\": 0,\n      \"name\": \"Key\"\n    },\n    {\n      \"index\": 2,\n      \"name\": \"Output\",\n      \"converter\": {\n        \"type\": \"complexlink\",\n        \"links\": [\n          {\n            \"when\": {\n              \"key\": \"Key\",\n              \"value\": 1\n            },\n            \"sheet\": \"GuidePageString\"\n          },\n          {\n            \"when\": {\n              \"key\": \"Key\",\n              \"value\": 2\n            },\n            \"sheet\": \"GuidePageString\"\n          },\n          {\n            \"when\": {\n              \"key\": \"Key\",\n              \"value\": 4\n            },\n            \"sheet\": \"GuidePageString\"\n          },\n          {\n            \"when\": {\n              \"key\": \"Key\",\n              \"value\": 5\n            },\n            \"sheet\": \"GuidePageString\"\n          },\n          {\n            \"when\": {\n              \"key\": \"Key\",\n              \"value\": 6\n            },\n            \"sheet\": \"GuidePageString\"\n          }\n        ]\n      }\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/GuidePageString.json",
    "content": "{\n  \"sheet\": \"GuidePageString\",\n  \"defaultColumn\": \"String\",\n  \"definitions\": [\n    {\n      \"name\": \"String\"\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/GuideTitle.json",
    "content": "{\n  \"sheet\": \"GuideTitle\",\n  \"defaultColumn\": \"Title\",\n  \"definitions\": [\n    {\n      \"name\": \"Title\"\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/GuildOrder.json",
    "content": "{\n  \"sheet\": \"GuildOrder\",\n  \"definitions\": [\n    {\n      \"name\": \"ENpc[Name]\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"ENpcResident\"\n      }\n    },\n    {\n      \"index\": 1,\n      \"name\": \"Objective\"\n    },\n    {\n      \"index\": 2,\n      \"name\": \"Description{1}\"\n    },\n    {\n      \"index\": 3,\n      \"name\": \"Description{2}\"\n    },\n    {\n      \"index\": 4,\n      \"name\": \"Description{3}\"\n    },\n    {\n      \"index\": 5,\n      \"name\": \"CompletionBonusExp\"\n    },\n    {\n      \"index\": 6,\n      \"name\": \"RewardExp\"\n    },\n    {\n      \"index\": 7,\n      \"name\": \"CompletionBonusGil\"\n    },\n    {\n      \"index\": 8,\n      \"name\": \"RewardGil\"\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/GuildOrderGuide.json",
    "content": "{\n  \"sheet\": \"GuildOrderGuide\",\n  \"isGenericReferenceTarget\": true,\n  \"definitions\": []\n}"
  },
  {
    "path": "SaintCoinach/Definitions/GuildOrderOfficer.json",
    "content": "{\n  \"sheet\": \"GuildOrderOfficer\",\n  \"isGenericReferenceTarget\": true,\n  \"definitions\": []\n}"
  },
  {
    "path": "SaintCoinach/Definitions/GuildleveAssignment.json",
    "content": "{\n  \"sheet\": \"GuildleveAssignment\",\n  \"isGenericReferenceTarget\": true,\n  \"definitions\": [\n    {\n      \"name\": \"Type\"\n    },\n    {\n      \"index\": 2,\n      \"name\": \"AssignmentTalk\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"GuildleveAssignmentTalk\"\n      }\n    },\n    {\n      \"index\": 3,\n      \"type\": \"repeat\",\n      \"count\": 2,\n      \"definition\": {\n        \"name\": \"Quest\",\n        \"converter\": {\n          \"type\": \"link\",\n          \"target\": \"Quest\"\n        }\n      }\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/GuildleveAssignmentCategory.json",
    "content": "{\n  \"sheet\": \"GuildleveAssignmentCategory\",\n  \"defaultColumn\": \"\",\n  \"definitions\": [\n    {\n      \"type\": \"repeat\",\n      \"count\": 8,\n      \"definition\": {\n        \"name\": \"Category\",\n        \"converter\": {\n          \"type\": \"link\",\n          \"target\": \"LeveAssignmentType\"\n        }\n      }\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/GuildleveAssignmentTalk.json",
    "content": "{\n  \"sheet\": \"GuildleveAssignmentTalk\",\n  \"definitions\": [\n    {\n      \"index\": 30,\n      \"type\": \"repeat\",\n      \"count\": 8,\n      \"definition\": {\n        \"name\": \"Talk\"\n      }\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/HWDAnnounce.json",
    "content": "{\n  \"sheet\": \"HWDAnnounce\",\n  \"defaultColumn\": \"Text\",\n  \"definitions\": [\n    {\n      \"name\": \"Name\"\n    },\n    {\n      \"index\": 1,\n      \"name\": \"ENPC\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"ENpcResident\"\n      }\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/HWDCrafterSupply.json",
    "content": "{\n  \"sheet\": \"HWDCrafterSupply\",\n  \"definitions\": [\n    {\n      \"type\": \"repeat\",\n      \"count\": 23,\n      \"definition\": {\n        \"name\": \"Item{TradeIn}\",\n        \"converter\": {\n          \"type\": \"link\",\n          \"target\": \"Item\"\n        }\n      }\n    },\n    {\n      \"index\": 23,\n      \"type\": \"repeat\",\n      \"count\": 23,\n      \"definition\": {\n        \"name\": \"Level\"\n      }\n    },\n    {\n      \"index\": 46,\n      \"type\": \"repeat\",\n      \"count\": 23,\n      \"definition\": {\n        \"name\": \"LevelMax\"\n      }\n    },\n    {\n      \"index\": 92,\n      \"type\": \"repeat\",\n      \"count\": 23,\n      \"definition\": {\n        \"name\": \"BaseCollectable{Rating}\"\n      }\n    },\n    {\n      \"index\": 115,\n      \"type\": \"repeat\",\n      \"count\": 23,\n      \"definition\": {\n        \"name\": \"MidCollectable{Rating}\"\n      }\n    },\n    {\n      \"index\": 138,\n      \"type\": \"repeat\",\n      \"count\": 23,\n      \"definition\": {\n        \"name\": \"HighCollectable{Rating}\"\n      }\n    },\n    {\n      \"index\": 161,\n      \"type\": \"repeat\",\n      \"count\": 23,\n      \"definition\": {\n        \"name\": \"BaseCollectable{Reward}\",\n        \"converter\": {\n          \"type\": \"link\",\n          \"target\": \"HWDCrafterSupplyReward\"\n        }\n      }\n    },\n    {\n      \"index\": 184,\n      \"type\": \"repeat\",\n      \"count\": 23,\n      \"definition\": {\n        \"name\": \"MidCollectable{Reward}\",\n        \"converter\": {\n          \"type\": \"link\",\n          \"target\": \"HWDCrafterSupplyReward\"\n        }\n      }\n    },\n    {\n      \"index\": 207,\n      \"type\": \"repeat\",\n      \"count\": 23,\n      \"definition\": {\n        \"name\": \"HighCollectable{Reward}\",\n        \"converter\": {\n          \"type\": \"link\",\n          \"target\": \"HWDCrafterSupplyReward\"\n        }\n      }\n    },\n    {\n      \"index\": 230,\n      \"type\": \"repeat\",\n      \"count\": 23,\n      \"definition\": {\n        \"name\": \"BaseCollectable{Reward}[PostPhase]\",\n        \"converter\": {\n          \"type\": \"link\",\n          \"target\": \"HWDCrafterSupplyReward\"\n        }\n      }\n    },\n    {\n      \"index\": 253,\n      \"type\": \"repeat\",\n      \"count\": 23,\n      \"definition\": {\n        \"name\": \"MidCollectable{Reward}[PostPhase]\",\n        \"converter\": {\n          \"type\": \"link\",\n          \"target\": \"HWDCrafterSupplyReward\"\n        }\n      }\n    },\n    {\n      \"index\": 276,\n      \"type\": \"repeat\",\n      \"count\": 23,\n      \"definition\": {\n        \"name\": \"HighCollectable{Reward}[PostPhase]\",\n        \"converter\": {\n          \"type\": \"link\",\n          \"target\": \"HWDCrafterSupplyReward\"\n        }\n      }\n    },\n    {\n      \"index\": 299,\n      \"type\": \"repeat\",\n      \"count\": 23,\n      \"definition\": {\n        \"name\": \"TermName\",\n        \"converter\": {\n          \"type\": \"link\",\n          \"target\": \"HWDCrafterSupplyTerm\"\n        }\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "SaintCoinach/Definitions/HWDCrafterSupplyReward.json",
    "content": "{\n  \"sheet\": \"HWDCrafterSupplyReward\",\n  \"definitions\": [\n    {\n      \"name\": \"ScriptReward{Amount}\"\n    },\n    {\n      \"index\": 1,\n      \"name\": \"ExpReward\"\n    },\n    {\n      \"index\": 2,\n      \"name\": \" Points\"\n    }\n  ]\n}\n"
  },
  {
    "path": "SaintCoinach/Definitions/HWDCrafterSupplyTerm.json",
    "content": "{\r\n  \"sheet\": \"HWDCrafterSupplyTerm\",\r\n  \"defaultColumn\": \"Name\",\r\n  \"definitions\": [\r\n    {\r\n      \"name\": \"Name\"\r\n    }\r\n  ]\r\n}"
  },
  {
    "path": "SaintCoinach/Definitions/HWDDevLayerControl.json",
    "content": "{\n  \"sheet\": \"HWDDevLayerControl\",\n  \"definitions\": [\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/HWDDevLevelUI.json",
    "content": "{\n  \"sheet\": \"HWDDevLevelUI\",\n  \"definitions\": [\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/HWDDevLively.json",
    "content": "{\n  \"sheet\": \"HWDDevLively\",\n  \"defaultColumn\": \"ENPC\",\n  \"definitions\": [\n    {\n      \"name\": \"ENPC\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"ENpcBase\"\n      }\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/HWDDevProgress.json",
    "content": "{\n  \"sheet\": \"HWDDevProgress\",\n  \"defaultColumn\": \"CanGoNext\",\n  \"definitions\": [\n    {\n      \"name\": \"CanGoNext\"\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/HWDGathereInspectTerm.json",
    "content": "{\n  \"sheet\": \"HWDGathereInspectTerm\",\n  \"defaultColumn\": \"Name\",\n  \"definitions\": [\n    {\n      \"name\": \"Name\"\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/HWDGathererInspection.json",
    "content": "{\n  \"sheet\": \"HWDGathererInspection\",\n  \"definitions\": [\n    {\n      \"type\": \"repeat\",\n      \"count\": 79,\n      \"definition\": {\n        \"name\": \"ItemRequired\",\n        \"converter\": {\n          \"type\": \"link\",\n          \"target\": \"GatheringItem\"\n        }\n      }\n    },\n    {\n      \"index\": 79,\n      \"type\": \"repeat\",\n      \"count\": 79,\n      \"definition\": {\n        \"name\": \"FishParameter\",\n        \"converter\": {\n          \"type\": \"link\",\n          \"target\": \"FishParameter\"\n        }\n      }\n    },\n    {\n      \"index\": 158,\n      \"type\": \"repeat\",\n      \"count\": 79,\n      \"definition\": {\n        \"name\": \"AmountRequired\"\n      }\n    },\n    {\n      \"index\": 237,\n      \"type\": \"repeat\",\n      \"count\": 79,\n      \"definition\": {\n        \"name\": \"ItemReceived\",\n        \"converter\": {\n          \"type\": \"link\",\n          \"target\": \"Item\"\n        }\n      }\n    },\n    {\n      \"index\": 316,\n      \"type\": \"repeat\",\n      \"count\": 79,\n      \"definition\": {\n        \"name\": \"Reward[1]\",\n        \"converter\": {\n          \"type\": \"link\",\n          \"target\": \"HWDGathererInspectionReward\"\n        }\n      }\n    },\n    {\n      \"index\": 395,\n      \"type\": \"repeat\",\n      \"count\": 79,\n      \"definition\": {\n        \"name\": \"Reward[2]\",\n        \"converter\": {\n          \"type\": \"link\",\n          \"target\": \"HWDGathererInspectionReward\"\n        }\n      }\n    },\n    {\n      \"index\": 474,\n      \"type\": \"repeat\",\n      \"count\": 79,\n      \"definition\": {\n        \"name\": \"Phase\",\n        \"converter\": {\n          \"type\": \"link\",\n          \"target\": \"HWDGathereInspectTerm\"\n        }\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "SaintCoinach/Definitions/HWDGathererInspectionReward.json",
    "content": "{\n  \"sheet\": \"HWDGathererInspectionReward\",\n  \"defaultColumn\": \"Scrips\",\n  \"definitions\": [\n    {\n      \"name\": \"Scrips\"\n    },\n    {\n      \"index\": 1,\n      \"name\": \"Points\"\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/HWDInfoBoardArticle.json",
    "content": "{\n  \"sheet\": \"HWDInfoBoardArticle\",\n  \"defaultColumn\": \"Text\",\n  \"definitions\": [\n    {\n      \"name\": \"Type\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"HWDInfoBoardArticleType\"\n      }\n    },\n    {\n      \"index\": 4,\n      \"name\": \"Text\"\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/HWDInfoBoardArticleTransient.json",
    "content": "{\n  \"sheet\": \"HWDInfoBoardArticleTransient\",\n  \"defaultColumn\": \"Text\",\n  \"definitions\": [\n    {\n      \"name\": \"Image\",\n      \"converter\": {\n        \"type\": \"icon\"\n      }\n    },\n    {\n      \"index\": 1,\n      \"name\": \"Text\"\n    },\n    {\n      \"index\": 2,\n      \"name\": \"NpcName\"\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/HWDInfoBoardArticleType.json",
    "content": "{\n  \"sheet\": \"HWDInfoBoardArticleType\",\n  \"defaultColumn\": \"Type\",\n  \"definitions\": [\n    {\n      \"name\": \"Type\"\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/HWDLevelChangeDeception.json",
    "content": "{\n  \"sheet\": \"HWDLevelChangeDeception\",\n  \"defaultColumn\": \"Image\",\n  \"definitions\": [\n    {\n      \"name\": \"Image\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"ScreenImage\"\n      }\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/HWDSharedGroup.json",
    "content": "{\n  \"sheet\": \"HWDSharedGroup\",\n  \"defaultColumn\": \"LGB\",\n  \"definitions\": [\n    {\n      \"name\": \"LGB{SharedGroup}\"\n    },\n    {\n      \"index\": 1,\n      \"name\": \"Param\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"HWDSharedGroupControlParam\"\n      }\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/HWDSharedGroupControlParam.json",
    "content": "{\n  \"sheet\": \"HWDSharedGroupControlParam\",\n  \"definitions\": [\n    {\n      \"index\": 1,\n      \"name\": \"ParamValue\"\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/HairMakeType.json",
    "content": "{\n  \"sheet\": \"HairMakeType\",\n  \"definitions\": [\n    {\n      \"name\": \"Race\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"Race\"\n      }\n    },\n    {\n      \"index\": 1,\n      \"name\": \"Tribe\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"Tribe\"\n      }\n    },\n    {\n      \"index\": 2,\n      \"name\": \"Gender\"\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/HouseRetainerPose.json",
    "content": "{\n  \"sheet\": \"HouseRetainerPose\",\n  \"defaultColumn\": \"ActionTimeline\",\n  \"definitions\": [\n    {\n      \"name\": \"ActionTimeline\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"ActionTimeline\"\n      }\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/HousingAethernet.json",
    "content": "{\n  \"sheet\": \"HousingAethernet\",\n  \"definitions\": [\n    {\n      \"name\": \"Level\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"Level\"\n      }\n    },\n    {\n      \"index\": 1,\n      \"name\": \"TerritoryType\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"TerritoryType\"\n      }\n    },\n    {\n      \"index\": 2,\n      \"name\": \"PlaceName\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"PlaceName\"\n      }\n    },\n    {\n      \"index\": 3,\n      \"name\": \"Order\"\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/HousingAppeal.json",
    "content": "{\n  \"sheet\": \"HousingAppeal\",\n  \"defaultColumn\": \"Tag\",\n  \"definitions\": [\n    {\n      \"name\": \"Tag\"\n    },\n    {\n      \"index\": 1,\n      \"name\": \"Icon\",\n      \"converter\": {\n        \"type\": \"icon\"\n      }\n    },\n    {\n      \"index\": 2,\n      \"name\": \"Order\"\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/HousingEmploymentNpcList.json",
    "content": "{\n  \"sheet\": \"HousingEmploymentNpcList\",\n  \"definitions\": [\n    {\n      \"name\": \"Race\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"HousingEmploymentNpcRace\"\n      }\n    },\n    {\n      \"index\": 1,\n      \"type\": \"repeat\",\n      \"count\": 2,\n      \"definition\": {\n        \"name\": \"ENpcBase\",\n        \"converter\": {\n          \"type\": \"link\",\n          \"target\": \"ENpcBase\"\n        }\n      }\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/HousingEmploymentNpcRace.json",
    "content": "{\n  \"sheet\": \"HousingEmploymentNpcRace\",\n  \"defaultColumn\": \"Race\",\n  \"definitions\": [\n    {\n      \"name\": \"Race\"\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/HousingExterior.json",
    "content": "{\n  \"sheet\": \"HousingExterior\",\n  \"definitions\": [\n    {\n      \"index\": 2,\n      \"name\": \"PlaceName\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"PlaceName\"\n      }\n    },\n    {\n      \"index\": 3,\n      \"name\": \"HousingSize\"\n    },\n    {\n      \"index\": 4,\n      \"name\": \"Model\"\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/HousingFurniture.json",
    "content": "{\n  \"sheet\": \"HousingFurniture\",\n  \"defaultColumn\": \"Item\",\n  \"definitions\": [\n    {\n      \"name\": \"ModelKey\"\n    },\n    {\n      \"index\": 1,\n      \"name\": \"HousingItemCategory\"\n    },\n    {\n      \"index\": 2,\n      \"name\": \"UsageType\"\n    },\n    {\n      \"index\": 3,\n      \"name\": \"UsageParameter\"\n    },\n    {\n      \"index\": 5,\n      \"name\": \"AquariumTier\"\n    },\n    {\n      \"index\": 6,\n      \"name\": \"CustomTalk\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"CustomTalk\"\n      }\n    },\n    {\n      \"index\": 7,\n      \"name\": \"Item\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"Item\"\n      }\n    },\n    {\n      \"index\": 8,\n      \"name\": \"DestroyOnRemoval\"\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/HousingLandSet.json",
    "content": "{\n  \"sheet\": \"HousingLandSet\",\n  \"defaultColumn\": \"\",\n  \"definitions\": [\n    {\n      \"type\": \"repeat\",\n      \"count\": 60,\n      \"definition\": {\n        \"name\": \"PlotSize\"\n      }\n    },\n    {\n      \"index\": 60,\n      \"type\": \"repeat\",\n      \"count\": 60,\n      \"definition\": {\n        \"name\": \"MinPrice\"\n      }\n    },\n    {\n      \"index\": 240,\n      \"type\": \"repeat\",\n      \"count\": 60,\n      \"definition\": {\n        \"name\": \"InitialPrice\"\n      }\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/HousingMapMarkerInfo.json",
    "content": "{\n  \"sheet\": \"HousingMapMarkerInfo\",\n  \"definitions\": [\n    {\n      \"name\": \"X\"\n    },\n    {\n      \"index\": 1,\n      \"name\": \"Y\"\n    },\n    {\n      \"index\": 2,\n      \"name\": \"Z\"\n    },\n    {\n      \"index\": 4,\n      \"name\": \"Map\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"Map\"\n      }\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/HousingMerchantPose.json",
    "content": "{\n  \"sheet\": \"HousingMerchantPose\",\n  \"defaultColumn\": \"Pose\",\n  \"definitions\": [\n    {\n      \"name\": \"ActionTimeline\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"ActionTimeline\"\n      }\n    },\n    {\n      \"index\": 1,\n      \"name\": \"Pose\"\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/HousingPlacement.json",
    "content": "{\n  \"sheet\": \"HousingPlacement\",\n  \"defaultColumn\": \"Text\",\n  \"definitions\": [\n    {\n      \"name\": \"Text\"\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/HousingPreset.json",
    "content": "{\n  \"sheet\": \"HousingPreset\",\n  \"definitions\": [\n    {\n      \"name\": \"Singular\"\n    },\n    {\n      \"index\": 1,\n      \"name\": \"Adjective\"\n    },\n    {\n      \"index\": 2,\n      \"name\": \"Plural\"\n    },\n    {\n      \"index\": 3,\n      \"name\": \"PossessivePronoun\"\n    },\n    {\n      \"index\": 4,\n      \"name\": \"StartsWithVowel\"\n    },\n    {\n      \"index\": 6,\n      \"name\": \"Pronoun\"\n    },\n    {\n      \"index\": 7,\n      \"name\": \"Article\"\n    },\n    {\n      \"index\": 8,\n      \"name\": \"PlaceName\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"PlaceName\"\n      }\n    },\n    {\n      \"index\": 9,\n      \"name\": \"HousingSize\"\n    },\n    {\n      \"index\": 10,\n      \"name\": \"ExteriorRoof\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"Item\"\n      }\n    },\n    {\n      \"index\": 11,\n      \"name\": \"ExteriorWall\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"Item\"\n      }\n    },\n    {\n      \"index\": 12,\n      \"name\": \"ExteriorWindow\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"Item\"\n      }\n    },\n    {\n      \"index\": 13,\n      \"name\": \"ExteriorDoor\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"Item\"\n      }\n    },\n    {\n      \"index\": 14,\n      \"name\": \"InteriorWall\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"Item\"\n      }\n    },\n    {\n      \"index\": 15,\n      \"name\": \"InteriorFlooring\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"Item\"\n      }\n    },\n    {\n      \"index\": 16,\n      \"name\": \"InteriorLighting\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"Item\"\n      }\n    },\n    {\n      \"index\": 17,\n      \"name\": \"OtherFloorWall\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"Item\"\n      }\n    },\n    {\n      \"index\": 18,\n      \"name\": \"OtherFloorFlooring\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"Item\"\n      }\n    },\n    {\n      \"index\": 19,\n      \"name\": \"OtherFloorLighting\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"Item\"\n      }\n    },\n    {\n      \"index\": 20,\n      \"name\": \"BasementWall\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"Item\"\n      }\n    },\n    {\n      \"index\": 21,\n      \"name\": \"BasementFlooring\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"Item\"\n      }\n    },\n    {\n      \"index\": 22,\n      \"name\": \"BasementLighting\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"Item\"\n      }\n    },\n    {\n      \"index\": 23,\n      \"name\": \"MansionLighting\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"Item\"\n      }\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/HousingUnitedExterior.json",
    "content": "{\n  \"sheet\": \"HousingUnitedExterior\",\n  \"definitions\": [\n    {\n      \"index\": 1,\n      \"type\": \"repeat\",\n      \"count\": 8,\n      \"definition\": {\n        \"name\": \"Item\",\n        \"converter\": {\n          \"type\": \"link\",\n          \"target\": \"HousingExterior\"\n        }\n      }\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/HousingYardObject.json",
    "content": "{\n  \"sheet\": \"HousingYardObject\",\n  \"defaultColumn\": \"Item\",\n  \"definitions\": [\n    {\n      \"name\": \"ModelKey\"\n    },\n    {\n      \"index\": 1,\n      \"name\": \"HousingItemCategory\"\n    },\n    {\n      \"index\": 2,\n      \"name\": \"UsageType\"\n    },\n    {\n      \"index\": 3,\n      \"name\": \"UsageParameter\"\n    },\n    {\n      \"index\": 5,\n      \"name\": \"CustomTalk\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"CustomTalk\"\n      }\n    },\n    {\n      \"index\": 6,\n      \"name\": \"Item\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"Item\"\n      }\n    },\n    {\n      \"index\": 7,\n      \"name\": \"DestroyOnRemoval\"\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/HowTo.json",
    "content": "{\n  \"sheet\": \"HowTo\",\n  \"defaultColumn\": \"Name\",\n  \"definitions\": [\n    {\n      \"name\": \"Name\"\n    },\n    {\n      \"index\": 1,\n      \"name\": \"Announce\"\n    },\n    {\n      \"index\": 2,\n      \"type\": \"repeat\",\n      \"count\": 5,\n      \"definition\": {\n        \"name\": \"HowToPagePC\",\n        \"converter\": {\n          \"type\": \"link\",\n          \"target\": \"HowToPage\"\n        }\n      }\n    },\n    {\n      \"index\": 7,\n      \"type\": \"repeat\",\n      \"count\": 5,\n      \"definition\": {\n        \"name\": \"HowToPageController\",\n        \"converter\": {\n          \"type\": \"link\",\n          \"target\": \"HowToPage\"\n        }\n      }\n    },\n    {\n      \"index\": 12,\n      \"name\": \"Category\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"HowToCategory\"\n      }\n    },\n    {\n      \"index\": 13,\n      \"name\": \"Sort\"\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/HowToCategory.json",
    "content": "{\n  \"sheet\": \"HowToCategory\",\n  \"defaultColumn\": \"Category\",\n  \"definitions\": [\n    {\n      \"name\": \"Category\"\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/HowToPage.json",
    "content": "{\n  \"sheet\": \"HowToPage\",\n  \"defaultColumn\": \"Image\",\n  \"definitions\": [\n    {\n      \"name\": \"Type\"\n    },\n    {\n      \"index\": 1,\n      \"name\": \"IconType\"\n    },\n    {\n      \"index\": 2,\n      \"name\": \"Image\",\n      \"converter\": {\n        \"type\": \"icon\"\n      }\n    },\n    {\n      \"index\": 3,\n      \"name\": \"TextType\"\n    },\n    {\n      \"index\": 4,\n      \"type\": \"repeat\",\n      \"count\": 3,\n      \"definition\": {\n        \"name\": \"Text\"\n      }\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/HugeCraftworksNpc.json",
    "content": "{\n  \"sheet\": \"HugeCraftworksNpc\",\n  \"defaultColumn\": \"ENpcResident\",\n  \"definitions\": [\n    {\n      \"name\": \"ENpcResident\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"ENpcResident\"\n      }\n    },\n    {\n      \"index\": 1,\n      \"name\": \"ClassJobCategory\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"ClassJobCategory\"\n      }\n    },\n    {\n      \"index\": 3,\n      \"type\": \"repeat\",\n      \"count\": 4,\n      \"definition\": {\n        \"name\": \"ItemRequested\",\n        \"converter\": {\n          \"type\": \"link\",\n          \"target\": \"Item\"\n        }\n      }\n    },\n    {\n      \"index\": 9,\n      \"type\": \"repeat\",\n      \"count\": 4,\n      \"definition\": {\n        \"name\": \"QtyRequested\"\n      }\n    },\n    {\n      \"index\": 52,\n      \"type\": \"repeat\",\n      \"count\": 4,\n      \"definition\": {\n        \"name\": \"ItemReward\",\n        \"converter\": {\n          \"type\": \"link\",\n          \"target\": \"Item\"\n        }\n      }\n    },\n    {\n      \"index\": 64,\n      \"type\": \"repeat\",\n      \"count\": 4,\n      \"definition\": {\n        \"name\": \"QtyItemReward\"\n      }\n    },\n    {\n      \"index\": 70,\n      \"type\": \"repeat\",\n      \"count\": 4,\n      \"definition\": {\n        \"name\": \"ItemUnkown\",\n        \"converter\": {\n          \"type\": \"link\",\n          \"target\": \"Item\"\n        }\n      }\n    },\n    {\n      \"index\": 82,\n      \"type\": \"repeat\",\n      \"count\": 4,\n      \"definition\": {\n        \"name\": \"QtyItemUnkown\"\n      }\n    },\n    {\n      \"index\": 86,\n      \"name\": \"Transient\"\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/HugeCraftworksRank.json",
    "content": "{\n  \"sheet\": \"HugeCraftworksRank\",\n  \"definitions\": [\n    {\n      \"name\": \"CrafterLevel\"\n    },\n    {\n      \"index\": 1,\n      \"name\": \"ExpRewardPerItem\"\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/IKDContentBonus.json",
    "content": "{\n  \"sheet\": \"IKDContentBonus\",\n  \"defaultColumn\": \"Objective\",\n  \"definitions\": [\n    {\n      \"name\": \"Objective\"\n    },\n    {\n      \"index\": 1,\n      \"name\": \"Requirement\"\n    },\n    {\n      \"index\": 3,\n      \"name\": \"Image\",\n      \"converter\": {\n        \"type\": \"icon\"\n      }\n    },\n    {\n      \"index\": 4,\n      \"name\": \"Order\"\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/IKDFishParam.json",
    "content": "{\n  \"sheet\": \"IKDFishParam\",\n  \"defaultColumn\": \"Fish\",\n  \"definitions\": [\n    {\n      \"name\": \"Fish\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"FishParameter\"\n      }\n    },\n    {\n      \"index\": 1,\n      \"name\": \"IKDContentBonus\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"IKDContentBonus\"\n      }\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/IKDRoute.json",
    "content": "{\n  \"sheet\": \"IKDRoute\",\n  \"defaultColumn\": \"Name\",\n  \"definitions\": [\n    {\n      \"type\": \"repeat\",\n      \"count\": 3,\n      \"definition\": {\n        \"type\": \"group\",\n        \"members\": [\n          {\n            \"name\": \"Spot\",\n            \"converter\": {\n              \"type\": \"link\",\n              \"target\": \"IKDSpot\"\n            }\n          },\n          {\n            \"name\": \"Time\",\n            \"converter\": {\n              \"type\": \"link\",\n              \"target\": \"IKDTimeDefine\"\n            }\n          }\n        ]\n      }\n    },\n    {\n      \"index\": 6,\n      \"name\": \"Image\",\n      \"converter\": {\n        \"type\": \"icon\"\n      }\n    },\n    {\n      \"index\": 9,\n      \"name\": \"ContentFinderCondition\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"ContentFinderCondition\"\n      }\n    },\n    {\n      \"index\": 11,\n      \"name\": \"Name\"\n    }\n  ]\n}\n"
  },
  {
    "path": "SaintCoinach/Definitions/IKDRouteTable.json",
    "content": "{\n  \"sheet\": \"IKDRouteTable\",\n  \"defaultColumn\": \"Route\",\n  \"definitions\": [\n    {\n      \"name\": \"Route\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"IKDRoute\"\n      }\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/IKDSpot.json",
    "content": "{\n  \"sheet\": \"IKDSpot\",\n  \"defaultColumn\": \"SpotMain\",\n  \"definitions\": [\n    {\n      \"name\": \"SpotMain\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"FishingSpot\"\n      }\n    },\n    {\n      \"index\": 1,\n      \"name\": \"SpotSub\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"FishingSpot\"\n      }\n    },\n    {\n      \"index\": 2,\n      \"name\": \"PlaceName\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"PlaceName\"\n      }\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/IconLanguage.json",
    "content": "{\n  \"sheet\": \"IconLanguage\",\n  \"definitions\": []\n}"
  },
  {
    "path": "SaintCoinach/Definitions/InclusionShop.json",
    "content": "{\n  \"sheet\": \"InclusionShop\",\n  \"definitions\": [\n    {\n      \"index\": 3,\n      \"type\": \"repeat\",\n      \"count\": 30,\n      \"definition\": {\n        \"name\": \"Category\",\n        \"converter\": {\n          \"type\": \"link\",\n          \"target\": \"InclusionShopCategory\"\n        }\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "SaintCoinach/Definitions/InclusionShopCategory.json",
    "content": "{\n  \"sheet\": \"InclusionShopCategory\",\n  \"defaultColumn\": \"Name\",\n  \"definitions\": [\n    {\n      \"name\": \"Name\"\n    },\n    {\n      \"index\": 1,\n      \"name\": \"ClassJobCategory\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"ClassJobCategory\"\n      }\n    },\n    {\n      \"index\": 2,\n      \"name\": \"InclusionShopSeries\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"InclusionShopSeries\"\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "SaintCoinach/Definitions/InclusionShopSeries.json",
    "content": "{\n  \"sheet\": \"InclusionShopSeries\",\n  \"defaultColumn\": \"SpecialShop\",\n  \"definitions\": [\n    {\n      \"name\": \"SpecialShop\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"SpecialShop\"\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "SaintCoinach/Definitions/InclusionShopWelcom.json",
    "content": "{\n  \"sheet\": \"InclusionShopWelcom\",\n  \"definitions\": []\n}"
  },
  {
    "path": "SaintCoinach/Definitions/InclusionShopWelcomText.json",
    "content": "{\n  \"sheet\": \"InclusionShopWelcomText\",\n  \"definitions\": []\n}"
  },
  {
    "path": "SaintCoinach/Definitions/IndividualWeather.json",
    "content": "{\n  \"sheet\": \"IndividualWeather\",\n  \"defaultColumn\": \"Name\",\n  \"definitions\": [\n    {\n      \"type\": \"repeat\",\n      \"count\": 6,\n      \"definition\": {\n        \"name\": \"Weather\",\n        \"converter\": {\n          \"type\": \"link\",\n          \"target\": \"Weather\"\n        }\n      }\n    },\n    {\n      \"index\": 15,\n      \"type\": \"repeat\",\n      \"count\": 6,\n      \"definition\": {\n        \"name\": \"Quest\",\n        \"converter\": {\n          \"type\": \"link\",\n          \"target\": \"Quest\"\n        }\n      }\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/InstanceContent.json",
    "content": "{\n  \"sheet\": \"InstanceContent\",\n  \"definitions\": [\n    {\n      \"name\": \"InstanceContentType\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"InstanceContentType\"\n      }\n    },\n    {\n      \"index\": 1,\n      \"name\": \"WeekRestriction\"\n    },\n    {\n      \"index\": 2,\n      \"name\": \"TimeLimit{min}\"\n    },\n    {\n      \"index\": 4,\n      \"name\": \"BGM\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"BGM\"\n      }\n    },\n    {\n      \"index\": 5,\n      \"name\": \"WinBGM\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"BGM\"\n      }\n    },\n    {\n      \"index\": 6,\n      \"name\": \"Cutscene\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"Cutscene\"\n      }\n    },\n    {\n      \"index\": 7,\n      \"name\": \"LGBEventRange\"\n    },\n    {\n      \"index\": 8,\n      \"name\": \"Order\"\n    },\n    {\n      \"index\": 9,\n      \"name\": \"Colosseum\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"Colosseum\"\n      }\n    },\n    {\n      \"index\": 11,\n      \"name\": \"InstanceContentTextData{Boss}{Start}\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"InstanceContentTextData\"\n      }\n    },\n    {\n      \"index\": 12,\n      \"name\": \"InstanceContentTextData{Boss}{End}\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"InstanceContentTextData\"\n      }\n    },\n    {\n      \"index\": 13,\n      \"name\": \"BNpcBase{Boss}\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"BNpcBase\"\n      }\n    },\n    {\n      \"index\": 14,\n      \"name\": \"InstanceContentTextData{Objective}{Start}\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"InstanceContentTextData\"\n      }\n    },\n    {\n      \"index\": 15,\n      \"name\": \"InstanceContentTextData{Objective}{End}\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"InstanceContentTextData\"\n      }\n    },\n    {\n      \"index\": 16,\n      \"name\": \"SortKey\"\n    },\n    {\n      \"index\": 17,\n      \"name\": \"NewPlayerBonusGil\"\n    },\n    {\n      \"index\": 18,\n      \"name\": \"NewPlayerBonusExp\"\n    },\n    {\n      \"index\": 19,\n      \"name\": \"NewPlayerBonusA\"\n    },\n    {\n      \"index\": 20,\n      \"name\": \"NewPlayerBonusB\"\n    },\n    {\n      \"index\": 21,\n      \"name\": \"FinalBossExp\"\n    },\n    {\n      \"index\": 23,\n      \"name\": \"FinalBossCurrencyA\"\n    },\n    {\n      \"index\": 24,\n      \"name\": \"FinalBossCurrencyB\"\n    },\n    {\n      \"index\": 25,\n      \"name\": \"FinalBossCurrencyC\"\n    },\n    {\n      \"index\": 26,\n      \"type\": \"repeat\",\n      \"count\": 5,\n      \"definition\": {\n        \"type\": \"group\",\n        \"members\": [\n          {\n            \"name\": \"BossExp\"\n          }\n        ]\n      }\n    },\n    {\n      \"index\": 31,\n      \"type\": \"repeat\",\n      \"count\": 5,\n      \"definition\": {\n        \"type\": \"group\",\n        \"members\": [\n          {\n            \"name\": \"BossCurrencyA\"\n          }\n        ]\n      }\n    },\n    {\n      \"index\": 36,\n      \"type\": \"repeat\",\n      \"count\": 5,\n      \"definition\": {\n        \"type\": \"group\",\n        \"members\": [\n          {\n            \"name\": \"BossCurrencyB\"\n          }\n        ]\n      }\n    },\n    {\n      \"index\": 41,\n      \"type\": \"repeat\",\n      \"count\": 5,\n      \"definition\": {\n        \"type\": \"group\",\n        \"members\": [\n          {\n            \"name\": \"BossCurrencyC\"\n          }\n        ]\n      }\n    },\n    {\n      \"index\": 46,\n      \"name\": \"InstanceClearExp\"\n    },\n    {\n      \"index\": 47,\n      \"name\": \"InstanceClearGil\"\n    },\n    {\n      \"index\": 48,\n      \"name\": \"InstanceContentRewardItem\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"InstanceContentRewardItem\"\n      }\n    },\n    {\n      \"index\": 51,\n      \"name\": \"InstanceContentBuff\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"InstanceContentBuff\"\n      }\n    },\n    {\n      \"index\": 53,\n      \"name\": \"ReqInstance\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"InstanceContent\"\n      }\n    },\n    {\n      \"index\": 54,\n      \"name\": \"PartyCondition\"\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/InstanceContentBuff.json",
    "content": "{\n  \"sheet\": \"InstanceContentBuff\",\n  \"definitions\": [\n    {\n      \"name\": \"Echo{Start}\"\n    },\n    {\n      \"index\": 1,\n      \"name\": \"Echo{Death}\"\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/InstanceContentGuide.json",
    "content": "{\n  \"sheet\": \"InstanceContentGuide\",\n  \"definitions\": [\n    {\n      \"name\": \"Instance\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"InstanceContent\"\n      }\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/InstanceContentQICData.json",
    "content": "{\n  \"sheet\": \"InstanceContentQICData\",\n  \"definitions\": []\n}"
  },
  {
    "path": "SaintCoinach/Definitions/InstanceContentTextData.json",
    "content": "{\n  \"sheet\": \"InstanceContentTextData\",\n  \"defaultColumn\": \"Text\",\n  \"definitions\": [\n    {\n      \"name\": \"Text\"\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/Item.json",
    "content": "{\n  \"sheet\": \"Item\",\n  \"defaultColumn\": \"Name\",\n  \"definitions\": [\n    {\n      \"name\": \"Singular\"\n    },\n    {\n      \"index\": 1,\n      \"name\": \"Adjective\"\n    },\n    {\n      \"index\": 2,\n      \"name\": \"Plural\"\n    },\n    {\n      \"index\": 3,\n      \"name\": \"PossessivePronoun\"\n    },\n    {\n      \"index\": 4,\n      \"name\": \"StartsWithVowel\"\n    },\n    {\n      \"index\": 6,\n      \"name\": \"Pronoun\"\n    },\n    {\n      \"index\": 7,\n      \"name\": \"Article\"\n    },\n    {\n      \"index\": 8,\n      \"name\": \"Description\"\n    },\n    {\n      \"index\": 9,\n      \"name\": \"Name\"\n    },\n    {\n      \"index\": 10,\n      \"name\": \"Icon\",\n      \"converter\": {\n        \"type\": \"icon\"\n      }\n    },\n    {\n      \"index\": 11,\n      \"name\": \"Level{Item}\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"ItemLevel\"\n      }\n    },\n    {\n      \"index\": 12,\n      \"name\": \"Rarity\"\n    },\n    {\n      \"index\": 13,\n      \"name\": \"FilterGroup\"\n    },\n    {\n      \"index\": 14,\n      \"name\": \"AdditionalData\",\n      \"converter\": {\n        \"type\": \"complexlink\",\n        \"links\": [\n          {\n            \"when\": {\n              \"key\": \"FilterGroup\",\n              \"value\": 15\n            },\n            \"sheet\": \"Stain\"\n          },\n          {\n            \"when\": {\n              \"key\": \"FilterGroup\",\n              \"value\": 18\n            },\n            \"sheet\": \"TreasureHuntRank\"\n          },\n          {\n            \"when\": {\n              \"key\": \"FilterGroup\",\n              \"value\": 20\n            },\n            \"sheet\": \"GardeningSeed\"\n          },\n          {\n            \"when\": {\n              \"key\": \"FilterGroup\",\n              \"value\": 25\n            },\n            \"sheet\": \"AetherialWheel\"\n          },\n          {\n            \"when\": {\n              \"key\": \"FilterGroup\",\n              \"value\": 26\n            },\n            \"sheet\": \"CompanyAction\"\n          },\n          {\n            \"when\": {\n              \"key\": \"FilterGroup\",\n              \"value\": 27\n            },\n            \"sheet\": \"TripleTriadCard\"\n          },\n          {\n            \"when\": {\n              \"key\": \"FilterGroup\",\n              \"value\": 28\n            },\n            \"sheet\": \"AirshipExplorationPart\"\n          },\n          {\n            \"when\": {\n              \"key\": \"FilterGroup\",\n              \"value\": 32\n            },\n            \"sheet\": \"Orchestrion\"\n          },\n          {\n            \"when\": {\n              \"key\": \"FilterGroup\",\n              \"value\": 36\n            },\n            \"sheet\": \"SubmarinePart\"\n          },\n          {\n            \"when\": {\n              \"key\": \"FilterGroup\",\n              \"value\": 14\n            },\n            \"sheets\": [\n              \"HousingExterior\",\n              \"HousingInterior\",\n              \"HousingYardObject\",\n              \"HousingFurniture\",\n              \"HousingPreset\",\n              \"HousingUnitedExterior\"\n            ]\n          }\n        ]\n      }\n    },\n    {\n      \"index\": 15,\n      \"name\": \"ItemUICategory\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"ItemUICategory\"\n      }\n    },\n    {\n      \"index\": 16,\n      \"name\": \"ItemSearchCategory\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"ItemSearchCategory\"\n      }\n    },\n    {\n      \"index\": 17,\n      \"name\": \"EquipSlotCategory\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"EquipSlotCategory\"\n      }\n    },\n    {\n      \"index\": 18,\n      \"name\": \"ItemSortCategory\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"ItemSortCategory\"\n      }\n    },\n    {\n      \"index\": 20,\n      \"name\": \"StackSize\"\n    },\n    {\n      \"index\": 21,\n      \"name\": \"IsUnique\"\n    },\n    {\n      \"index\": 22,\n      \"name\": \"IsUntradable\"\n    },\n    {\n      \"index\": 23,\n      \"name\": \"IsIndisposable\"\n    },\n    {\n      \"index\": 24,\n      \"name\": \"Lot\"\n    },\n    {\n      \"index\": 25,\n      \"name\": \"Price{Mid}\"\n    },\n    {\n      \"index\": 26,\n      \"name\": \"Price{Low}\"\n    },\n    {\n      \"index\": 27,\n      \"name\": \"CanBeHq\"\n    },\n    {\n      \"index\": 28,\n      \"name\": \"DyeCount\"\n    },\n    {\n      \"index\": 29,\n      \"name\": \"IsCrestWorthy\"\n    },\n    {\n      \"index\": 30,\n      \"name\": \"ItemAction\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"ItemAction\"\n      }\n    },\n    {\n      \"index\": 31,\n      \"name\": \"CastTime<s>\"\n    },\n    {\n      \"index\": 32,\n      \"name\": \"Cooldown<s>\"\n    },\n    {\n      \"index\": 33,\n      \"name\": \"ClassJob{Repair}\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"ClassJob\"\n      }\n    },\n    {\n      \"index\": 34,\n      \"name\": \"Item{Repair}\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"ItemRepairResource\"\n      }\n    },\n    {\n      \"index\": 35,\n      \"name\": \"Item{Glamour}\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"Item\"\n      }\n    },\n    {\n      \"index\": 36,\n      \"name\": \"Desynth\"\n    },\n    {\n      \"index\": 37,\n      \"name\": \"IsCollectable\"\n    },\n    {\n      \"index\": 38,\n      \"name\": \"AlwaysCollectable\"\n    },\n    {\n      \"index\": 39,\n      \"name\": \"AetherialReduce\"\n    },\n    {\n      \"index\": 40,\n      \"name\": \"Level{Equip}\"\n    },\n    {\n      \"index\": 41,\n      \"name\": \"RequiredPvpRank\"\n    },\n    {\n      \"index\": 42,\n      \"name\": \"EquipRestriction\"\n    },\n    {\n      \"index\": 43,\n      \"name\": \"ClassJobCategory\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"ClassJobCategory\"\n      }\n    },\n    {\n      \"index\": 44,\n      \"name\": \"GrandCompany\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"GrandCompany\"\n      }\n    },\n    {\n      \"index\": 45,\n      \"name\": \"ItemSeries\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"ItemSeries\"\n      }\n    },\n    {\n      \"index\": 46,\n      \"name\": \"BaseParamModifier\"\n    },\n    {\n      \"index\": 47,\n      \"name\": \"Model{Main}\"\n    },\n    {\n      \"index\": 48,\n      \"name\": \"Model{Sub}\"\n    },\n    {\n      \"index\": 49,\n      \"name\": \"ClassJob{Use}\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"ClassJob\"\n      }\n    },\n    {\n      \"index\": 51,\n      \"name\": \"Damage{Phys}\"\n    },\n    {\n      \"index\": 52,\n      \"name\": \"Damage{Mag}\"\n    },\n    {\n      \"index\": 53,\n      \"name\": \"Delay<ms>\"\n    },\n    {\n      \"index\": 55,\n      \"name\": \"BlockRate\"\n    },\n    {\n      \"index\": 56,\n      \"name\": \"Block\"\n    },\n    {\n      \"index\": 57,\n      \"name\": \"Defense{Phys}\"\n    },\n    {\n      \"index\": 58,\n      \"name\": \"Defense{Mag}\"\n    },\n    {\n      \"index\": 59,\n      \"type\": \"repeat\",\n      \"count\": 6,\n      \"definition\": {\n        \"type\": \"group\",\n        \"members\": [\n          {\n            \"name\": \"BaseParam\",\n            \"converter\": {\n              \"type\": \"link\",\n              \"target\": \"BaseParam\"\n            }\n          },\n          {\n            \"name\": \"BaseParamValue\"\n          }\n        ]\n      }\n    },\n    {\n      \"index\": 71,\n      \"name\": \"ItemSpecialBonus\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"ItemSpecialBonus\"\n      }\n    },\n    {\n      \"index\": 72,\n      \"name\": \"ItemSpecialBonus{Param}\"\n    },\n    {\n      \"index\": 73,\n      \"type\": \"repeat\",\n      \"count\": 6,\n      \"definition\": {\n        \"type\": \"group\",\n        \"members\": [\n          {\n            \"name\": \"BaseParam{Special}\",\n            \"converter\": {\n              \"type\": \"link\",\n              \"target\": \"BaseParam\"\n            }\n          },\n          {\n            \"name\": \"BaseParamValue{Special}\"\n          }\n        ]\n      }\n    },\n    {\n      \"index\": 85,\n      \"name\": \"MaterializeType\"\n    },\n    {\n      \"index\": 86,\n      \"name\": \"MateriaSlotCount\"\n    },\n    {\n      \"index\": 87,\n      \"name\": \"IsAdvancedMeldingPermitted\"\n    },\n    {\n      \"index\": 88,\n      \"name\": \"IsPvP\"\n    },\n    {\n      \"index\": 89,\n      \"name\": \"SubStatCategory\"\n    },\n    {\n      \"index\": 90,\n      \"name\": \"IsGlamourous\"\n    }\n  ]\n}\n"
  },
  {
    "path": "SaintCoinach/Definitions/ItemAction.json",
    "content": "{\n  \"sheet\": \"ItemAction\",\n  \"definitions\": [\n    {\n      \"name\": \"CondLv\"\n    },\n    {\n      \"index\": 1,\n      \"name\": \"CondBattle\"\n    },\n    {\n      \"index\": 2,\n      \"name\": \"CondPVP\"\n    },\n    {\n      \"index\": 3,\n      \"name\": \"CondPVPOnly\"\n    },\n    {\n      \"index\": 4,\n      \"name\": \"Type\"\n    },\n    {\n      \"index\": 5,\n      \"type\": \"repeat\",\n      \"count\": 9,\n      \"definition\": {\n        \"name\": \"Data\"\n      }\n    },\n    {\n      \"index\": 14,\n      \"type\": \"repeat\",\n      \"count\": 9,\n      \"definition\": {\n        \"name\": \"Data{HQ}\"\n      }\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/ItemActionTelepo.json",
    "content": "{\n  \"sheet\": \"ItemActionTelepo\",\n  \"defaultColumn\": \"Requirement\",\n  \"definitions\": [\n    {\n      \"name\": \"Requirement\",\n      \"converter\": {\n        \"type\": \"multiref\",\n        \"targets\": [\n          \"Quest\",\n          \"GrandCompany\"\n        ]\n      }\n    },\n    {\n      \"index\": 1,\n      \"name\": \"DenyMessage\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"LogMessage\"\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "SaintCoinach/Definitions/ItemBarterCheck.json",
    "content": "{\n  \"sheet\": \"ItemBarterCheck\",\n  \"definitions\": [\n    {\n      \"name\": \"Category\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"AddonTransient\"\n      }\n    },\n    {\n      \"index\": 1,\n      \"name\": \"Question\",\n      \"converter\": {\n        \"type\": \"multiref\",\n        \"targets\": [\n          \"LogMessage\",\n          \"Addon\"\n        ]\n      }\n    },\n    {\n      \"index\": 2,\n      \"name\": \"Confirm\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"Addon\"\n      }\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/ItemFood.json",
    "content": "{\n  \"sheet\": \"ItemFood\",\n  \"definitions\": [\n    {\n      \"name\": \"EXPBonus%\"\n    },\n    {\n      \"index\": 1,\n      \"type\": \"repeat\",\n      \"count\": 3,\n      \"definition\": {\n        \"type\": \"group\",\n        \"members\": [\n          {\n            \"name\": \"BaseParam\",\n            \"converter\": {\n              \"type\": \"link\",\n              \"target\": \"BaseParam\"\n            }\n          },\n          {\n            \"name\": \"IsRelative\"\n          },\n          {\n            \"name\": \"Value\"\n          },\n          {\n            \"name\": \"Max\"\n          },\n          {\n            \"name\": \"Value{HQ}\"\n          },\n          {\n            \"name\": \"Max{HQ}\"\n          }\n        ]\n      }\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/ItemLevel.json",
    "content": "{\n  \"sheet\": \"ItemLevel\",\n  \"definitions\": [\n    {\n      \"name\": \"Strength\"\n    },\n    {\n      \"index\": 1,\n      \"name\": \"Dexterity\"\n    },\n    {\n      \"index\": 2,\n      \"name\": \"Vitality\"\n    },\n    {\n      \"index\": 3,\n      \"name\": \"Intelligence\"\n    },\n    {\n      \"index\": 4,\n      \"name\": \"Mind\"\n    },\n    {\n      \"index\": 5,\n      \"name\": \"Piety\"\n    },\n    {\n      \"index\": 6,\n      \"name\": \"HP\"\n    },\n    {\n      \"index\": 7,\n      \"name\": \"MP\"\n    },\n    {\n      \"index\": 8,\n      \"name\": \"TP\"\n    },\n    {\n      \"index\": 9,\n      \"name\": \"GP\"\n    },\n    {\n      \"index\": 10,\n      \"name\": \"CP\"\n    },\n    {\n      \"index\": 11,\n      \"name\": \"PhysicalDamage\"\n    },\n    {\n      \"index\": 12,\n      \"name\": \"MagicalDamage\"\n    },\n    {\n      \"index\": 13,\n      \"name\": \"Delay\"\n    },\n    {\n      \"index\": 14,\n      \"name\": \"AdditionalEffect\"\n    },\n    {\n      \"index\": 15,\n      \"name\": \"AttackSpeed\"\n    },\n    {\n      \"index\": 16,\n      \"name\": \"BlockRate\"\n    },\n    {\n      \"index\": 17,\n      \"name\": \"BlockStrength\"\n    },\n    {\n      \"index\": 18,\n      \"name\": \"Tenacity\"\n    },\n    {\n      \"index\": 19,\n      \"name\": \"AttackPower\"\n    },\n    {\n      \"index\": 20,\n      \"name\": \"Defense\"\n    },\n    {\n      \"index\": 21,\n      \"name\": \"DirectHitRate\"\n    },\n    {\n      \"index\": 22,\n      \"name\": \"Evasion\"\n    },\n    {\n      \"index\": 23,\n      \"name\": \"MagicDefense\"\n    },\n    {\n      \"index\": 24,\n      \"name\": \"CriticalHitPower\"\n    },\n    {\n      \"index\": 25,\n      \"name\": \"CriticalHitResilience\"\n    },\n    {\n      \"index\": 26,\n      \"name\": \"CriticalHit\"\n    },\n    {\n      \"index\": 27,\n      \"name\": \"CriticalHitEvasion\"\n    },\n    {\n      \"index\": 28,\n      \"name\": \"SlashingResistance\"\n    },\n    {\n      \"index\": 29,\n      \"name\": \"PiercingResistance\"\n    },\n    {\n      \"index\": 30,\n      \"name\": \"BluntResistance\"\n    },\n    {\n      \"index\": 31,\n      \"name\": \"ProjectileResistance\"\n    },\n    {\n      \"index\": 32,\n      \"name\": \"AttackMagicPotency\"\n    },\n    {\n      \"index\": 33,\n      \"name\": \"HealingMagicPotency\"\n    },\n    {\n      \"index\": 34,\n      \"name\": \"EnhancementMagicPotency\"\n    },\n    {\n      \"index\": 35,\n      \"name\": \"EnfeeblingMagicPotency\"\n    },\n    {\n      \"index\": 36,\n      \"name\": \"FireResistance\"\n    },\n    {\n      \"index\": 37,\n      \"name\": \"IceResistance\"\n    },\n    {\n      \"index\": 38,\n      \"name\": \"WindResistance\"\n    },\n    {\n      \"index\": 39,\n      \"name\": \"EarthResistance\"\n    },\n    {\n      \"index\": 40,\n      \"name\": \"LightningResistance\"\n    },\n    {\n      \"index\": 41,\n      \"name\": \"WaterResistance\"\n    },\n    {\n      \"index\": 42,\n      \"name\": \"MagicResistance\"\n    },\n    {\n      \"index\": 43,\n      \"name\": \"Determination\"\n    },\n    {\n      \"index\": 44,\n      \"name\": \"SkillSpeed\"\n    },\n    {\n      \"index\": 45,\n      \"name\": \"SpellSpeed\"\n    },\n    {\n      \"index\": 46,\n      \"name\": \"Haste\"\n    },\n    {\n      \"index\": 47,\n      \"name\": \"Morale\"\n    },\n    {\n      \"index\": 48,\n      \"name\": \"Enmity\"\n    },\n    {\n      \"index\": 49,\n      \"name\": \"EnmityReduction\"\n    },\n    {\n      \"index\": 50,\n      \"name\": \"CarefulDesynthesis\"\n    },\n    {\n      \"index\": 51,\n      \"name\": \"EXPBonus\"\n    },\n    {\n      \"index\": 52,\n      \"name\": \"Regen\"\n    },\n    {\n      \"index\": 53,\n      \"name\": \"Refresh\"\n    },\n    {\n      \"index\": 54,\n      \"name\": \"MovementSpeed\"\n    },\n    {\n      \"index\": 55,\n      \"name\": \"Spikes\"\n    },\n    {\n      \"index\": 56,\n      \"name\": \"SlowResistance\"\n    },\n    {\n      \"index\": 57,\n      \"name\": \"PetrificationResistance\"\n    },\n    {\n      \"index\": 58,\n      \"name\": \"ParalysisResistance\"\n    },\n    {\n      \"index\": 59,\n      \"name\": \"SilenceResistance\"\n    },\n    {\n      \"index\": 60,\n      \"name\": \"BlindResistance\"\n    },\n    {\n      \"index\": 61,\n      \"name\": \"PoisonResistance\"\n    },\n    {\n      \"index\": 62,\n      \"name\": \"StunResistance\"\n    },\n    {\n      \"index\": 63,\n      \"name\": \"SleepResistance\"\n    },\n    {\n      \"index\": 64,\n      \"name\": \"BindResistance\"\n    },\n    {\n      \"index\": 65,\n      \"name\": \"HeavyResistance\"\n    },\n    {\n      \"index\": 66,\n      \"name\": \"DoomResistance\"\n    },\n    {\n      \"index\": 67,\n      \"name\": \"ReducedDurabilityLoss\"\n    },\n    {\n      \"index\": 68,\n      \"name\": \"IncreasedSpiritbondGain\"\n    },\n    {\n      \"index\": 69,\n      \"name\": \"Craftsmanship\"\n    },\n    {\n      \"index\": 70,\n      \"name\": \"Control\"\n    },\n    {\n      \"index\": 71,\n      \"name\": \"Gathering\"\n    },\n    {\n      \"index\": 72,\n      \"name\": \"Perception\"\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/ItemRepairPrice.json",
    "content": "{\n  \"sheet\": \"ItemRepairPrice\",\n  \"definitions\": []\n}"
  },
  {
    "path": "SaintCoinach/Definitions/ItemRepairResource.json",
    "content": "{\n  \"sheet\": \"ItemRepairResource\",\n  \"defaultColumn\": \"Name\",\n  \"definitions\": [\n    {\n      \"name\": \"Item\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"Item\"\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "SaintCoinach/Definitions/ItemRetainerLevelUp.json",
    "content": "{\n  \"sheet\": \"ItemRetainerLevelUp\",\n  \"definitions\": []\n}"
  },
  {
    "path": "SaintCoinach/Definitions/ItemSearchCategory.json",
    "content": "{\n  \"sheet\": \"ItemSearchCategory\",\n  \"defaultColumn\": \"Name\",\n  \"definitions\": [\n    {\n      \"name\": \"Name\"\n    },\n    {\n      \"index\": 1,\n      \"name\": \"Icon\",\n      \"converter\": {\n        \"type\": \"icon\"\n      }\n    },\n    {\n      \"index\": 2,\n      \"name\": \"Category\"\n    },\n    {\n      \"index\": 3,\n      \"name\": \"Order\"\n    },\n    {\n      \"index\": 4,\n      \"name\": \"ClassJob\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"ClassJob\"\n      }\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/ItemSeries.json",
    "content": "{\n  \"sheet\": \"ItemSeries\",\n  \"defaultColumn\": \"Name\",\n  \"definitions\": [\n    {\n      \"name\": \"Name\"\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/ItemSortCategory.json",
    "content": "{\n  \"sheet\": \"ItemSortCategory\",\n  \"defaultColumn\": \"Param\",\n  \"definitions\": [\n    {\n      \"name\": \"Param\"\n    }\n  ]\n}\n"
  },
  {
    "path": "SaintCoinach/Definitions/ItemSpecialBonus.json",
    "content": "{\n  \"sheet\": \"ItemSpecialBonus\",\n  \"defaultColumn\": \"Name\",\n  \"definitions\": [\n    {\n      \"name\": \"Name\"\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/ItemStainCondition.json",
    "content": "{\n  \"sheet\": \"ItemStainCondition\",\n  \"definitions\": []\n}"
  },
  {
    "path": "SaintCoinach/Definitions/ItemUICategory.json",
    "content": "{\n  \"sheet\": \"ItemUICategory\",\n  \"defaultColumn\": \"Name\",\n  \"definitions\": [\n    {\n      \"name\": \"Name\"\n    },\n    {\n      \"index\": 1,\n      \"name\": \"Icon\",\n      \"converter\": {\n        \"type\": \"icon\"\n      }\n    },\n    {\n      \"index\": 2,\n      \"name\": \"Order{Minor}\"\n    },\n    {\n      \"index\": 3,\n      \"name\": \"Order{Major}\"\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/Jingle.json",
    "content": "{\n  \"sheet\": \"Jingle\",\n  \"defaultColumn\": \"Name\",\n  \"definitions\": [\n    {\n      \"name\": \"Name\"\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/JobHudManual.json",
    "content": "{\n  \"sheet\": \"JobHudManual\",\n  \"defaultColumn\": \"Action\",\n  \"definitions\": [\n    {\n      \"index\": 3,\n      \"name\": \"Action\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"Action\"\n      }\n    },\n    {\n      \"index\": 6,\n      \"name\": \"Guide\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"Guide\"\n      }\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/JobHudManualPriority.json",
    "content": "{\n  \"sheet\": \"JobHudManualPriority\",\n  \"definitions\": [\n    {\n      \"type\": \"repeat\",\n      \"count\": 8,\n      \"definition\": {\n        \"name\": \"JobHudManual\",\n        \"converter\": {\n          \"type\": \"link\",\n          \"target\": \"JobHudManual\"\n        }\n      }\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/JournalCategory.json",
    "content": "{\n  \"sheet\": \"JournalCategory\",\n  \"defaultColumn\": \"Name\",\n  \"definitions\": [\n    {\n      \"name\": \"Name\"\n    },\n    {\n      \"index\": 1,\n      \"name\": \"SeparateType\"\n    },\n    {\n      \"index\": 2,\n      \"name\": \"DataType\"\n    },\n    {\n      \"index\": 3,\n      \"name\": \"JournalSection\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"JournalSection\"\n      }\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/JournalGenre.json",
    "content": "{\n  \"sheet\": \"JournalGenre\",\n  \"defaultColumn\": \"Name\",\n  \"definitions\": [\n    {\n      \"name\": \"Icon\",\n      \"converter\": {\n        \"type\": \"icon\"\n      }\n    },\n    {\n      \"index\": 1,\n      \"name\": \"JournalCategory\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"JournalCategory\"\n      }\n    },\n    {\n      \"index\": 3,\n      \"name\": \"Name\"\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/JournalSection.json",
    "content": "{\n  \"sheet\": \"JournalSection\",\n  \"defaultColumn\": \"Name\",\n  \"definitions\": [\n    {\n      \"name\": \"Name\"\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/KineDriverOffGroup.json",
    "content": "{\n  \"sheet\": \"KineDriverOffGroup\",\n  \"definitions\": []\n}"
  },
  {
    "path": "SaintCoinach/Definitions/Knockback.json",
    "content": "{\n  \"sheet\": \"Knockback\",\n  \"definitions\": [\n    {\n      \"name\": \"Distance\"\n    },\n    {\n      \"index\": 1,\n      \"name\": \"Speed\"\n    },\n    {\n      \"index\": 2,\n      \"name\": \"Motion\"\n    },\n    {\n      \"index\": 3,\n      \"name\": \"NearDistance\"\n    },\n    {\n      \"index\": 4,\n      \"name\": \"Direction\"\n    },\n    {\n      \"index\": 5,\n      \"name\": \"DirectionArg\"\n    },\n    {\n      \"index\": 6,\n      \"name\": \"CancelMove\"\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/LegacyQuest.json",
    "content": "{\n  \"sheet\": \"LegacyQuest\",\n  \"definitions\": [\n    {\n      \"name\": \"LegacyQuestID\"\n    },\n    {\n      \"index\": 1,\n      \"name\": \"Text\"\n    },\n    {\n      \"index\": 2,\n      \"name\": \"String\"\n    },\n    {\n      \"index\": 3,\n      \"name\": \"SortKey\"\n    },\n    {\n      \"index\": 4,\n      \"name\": \"Genre\"\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/Leve.json",
    "content": "{\n  \"sheet\": \"Leve\",\n  \"defaultColumn\": \"Name\",\n  \"definitions\": [\n    {\n      \"name\": \"Name\"\n    },\n    {\n      \"index\": 1,\n      \"name\": \"Description\"\n    },\n    {\n      \"index\": 2,\n      \"name\": \"LeveClient\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"LeveClient\"\n      }\n    },\n    {\n      \"index\": 4,\n      \"name\": \"LeveAssignmentType\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"LeveAssignmentType\"\n      }\n    },\n    {\n      \"index\": 5,\n      \"name\": \"Town\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"Town\"\n      }\n    },\n    {\n      \"index\": 6,\n      \"name\": \"ClassJobLevel\"\n    },\n    {\n      \"index\": 7,\n      \"name\": \"TimeLimit\"\n    },\n    {\n      \"index\": 8,\n      \"name\": \"AllowanceCost\"\n    },\n    {\n      \"index\": 9,\n      \"name\": \"PlaceName{Start}\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"PlaceName\"\n      }\n    },\n    {\n      \"index\": 10,\n      \"name\": \"PlaceName{Issued}\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"PlaceName\"\n      }\n    },\n    {\n      \"index\": 11,\n      \"name\": \"FishingSpot\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"FishingSpot\"\n      }\n    },\n    {\n      \"index\": 13,\n      \"name\": \"ClassJobCategory\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"ClassJobCategory\"\n      }\n    },\n    {\n      \"index\": 14,\n      \"name\": \"JournalGenre\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"JournalGenre\"\n      }\n    },\n    {\n      \"index\": 15,\n      \"name\": \"PlaceName{StartZone}\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"PlaceName\"\n      }\n    },\n    {\n      \"index\": 16,\n      \"name\": \"Icon{CityState}\",\n      \"converter\": {\n        \"type\": \"icon\"\n      }\n    },\n    {\n      \"index\": 17,\n      \"name\": \"DataId\",\n      \"converter\": {\n        \"type\": \"multiref\",\n        \"targets\": [\n          \"CraftLeve\",\n          \"CompanyLeve\",\n          \"GatheringLeve\",\n          \"BattleLeve\"\n        ]\n      }\n    },\n    {\n      \"index\": 18,\n      \"name\": \"CanCancel\"\n    },\n    {\n      \"index\": 19,\n      \"name\": \"MaxDifficulty\"\n    },\n    {\n      \"index\": 20,\n      \"name\": \"ExpFactor\"\n    },\n    {\n      \"index\": 21,\n      \"name\": \"ExpReward\"\n    },\n    {\n      \"index\": 22,\n      \"name\": \"GilReward\"\n    },\n    {\n      \"index\": 23,\n      \"name\": \"LeveRewardItem\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"LeveRewardItem\"\n      }\n    },\n    {\n      \"index\": 24,\n      \"name\": \"LeveVfx\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"LeveVfx\"\n      }\n    },\n    {\n      \"index\": 25,\n      \"name\": \"LeveVfx{Frame}\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"LeveVfx\"\n      }\n    },\n    {\n      \"index\": 26,\n      \"name\": \"Level{Levemete}\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"Level\"\n      }\n    },\n    {\n      \"index\": 27,\n      \"name\": \"Icon{Issuer}\",\n      \"converter\": {\n        \"type\": \"icon\"\n      }\n    },\n    {\n      \"index\": 28,\n      \"name\": \"LockedLeve\"\n    },\n    {\n      \"index\": 29,\n      \"name\": \"Level{Start}\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"Level\"\n      }\n    },\n    {\n      \"index\": 30,\n      \"name\": \"BGM\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"BGM\"\n      }\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/LeveAssignmentType.json",
    "content": "{\n  \"sheet\": \"LeveAssignmentType\",\n  \"defaultColumn\": \"Name\",\n  \"definitions\": [\n    {\n      \"index\": 0,\n      \"name\": \"Icon\",\n      \"converter\": {\n        \"type\": \"icon\"\n      }\n    },\n    {\n      \"index\": 1,\n      \"name\": \"Name\"\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/LeveClient.json",
    "content": "{\n  \"sheet\": \"LeveClient\",\n  \"defaultColumn\": \"Name\",\n  \"definitions\": [\n    {\n      \"name\": \"Name\"\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/LeveRewardItem.json",
    "content": "{\n  \"sheet\": \"LeveRewardItem\",\n  \"definitions\": [\n    {\n      \"type\": \"repeat\",\n      \"count\": 8,\n      \"definition\": {\n        \"type\": \"group\",\n        \"members\": [\n          {\n            \"name\": \"LeveRewardItemGroup\",\n            \"converter\": {\n              \"type\": \"link\",\n              \"target\": \"LeveRewardItemGroup\"\n            }\n          },\n          {\n            \"name\": \"Probability<%>\"\n          }\n        ]\n      }\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/LeveRewardItemGroup.json",
    "content": "{\n  \"sheet\": \"LeveRewardItemGroup\",\n  \"definitions\": [\n    {\n      \"type\": \"repeat\",\n      \"count\": 9,\n      \"definition\": {\n        \"type\": \"group\",\n        \"members\": [\n          {\n            \"name\": \"Item\",\n            \"converter\": {\n              \"type\": \"link\",\n              \"target\": \"Item\"\n            }\n          },\n          {\n            \"name\": \"Count\"\n          },\n          {\n            \"name\": \"HQ\"\n          }\n        ]\n      }\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/LeveString.json",
    "content": "{\n  \"sheet\": \"LeveString\",\n  \"defaultColumn\": \"Objective\",\n  \"definitions\": [\n    {\n      \"name\": \"Objective\"\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/LeveVfx.json",
    "content": "{\n  \"sheet\": \"LeveVfx\",\n  \"defaultColumn\": \"Icon\",\n  \"definitions\": [\n    {\n      \"name\": \"Effect\"\n    },\n    {\n      \"index\": 1,\n      \"name\": \"Icon\",\n      \"converter\": {\n        \"type\": \"icon\"\n      }\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/Level.json",
    "content": "{\n  \"sheet\": \"Level\",\n  \"definitions\": [\n    {\n      \"name\": \"X\"\n    },\n    {\n      \"index\": 1,\n      \"name\": \"Y\"\n    },\n    {\n      \"index\": 2,\n      \"name\": \"Z\"\n    },\n    {\n      \"index\": 3,\n      \"name\": \"Yaw\"\n    },\n    {\n      \"index\": 4,\n      \"name\": \"Radius\"\n    },\n    {\n      \"index\": 5,\n      \"name\": \"Type\"\n    },\n    {\n      \"index\": 6,\n      \"name\": \"Object\",\n      \"converter\": {\n        \"type\": \"complexlink\",\n        \"links\": [\n          {\n            \"when\": {\n              \"key\": \"Type\",\n              \"value\": 8\n            },\n            \"sheet\": \"ENpcBase\"\n          },\n          {\n            \"when\": {\n              \"key\": \"Type\",\n              \"value\": 9\n            },\n            \"sheet\": \"BNpcBase\"\n          },\n          {\n            \"when\": {\n              \"key\": \"Type\",\n              \"value\": 12\n            },\n            \"sheet\": \"Aetheryte\"\n          },\n          {\n            \"when\": {\n              \"key\": \"Type\",\n              \"value\": 14\n            },\n            \"sheet\": \"GatheringPoint\"\n          },\n          {\n            \"when\": {\n              \"key\": \"Type\",\n              \"value\": 45\n            },\n            \"sheet\": \"EObj\"\n          }\n        ]\n      }\n    },\n    {\n      \"index\": 7,\n      \"name\": \"Map\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"Map\"\n      }\n    },\n    {\n      \"index\": 8,\n      \"name\": \"EventId\",\n      \"converter\": {\n        \"type\": \"multiref\",\n        \"targets\": [\n          \"TripleTriad\",\n          \"Adventure\",\n          \"Opening\",\n          \"Quest\"\n        ]\n      }\n    },\n    {\n      \"index\": 9,\n      \"name\": \"Territory\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"TerritoryType\"\n      }\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/Lobby.json",
    "content": "{\n  \"sheet\": \"Lobby\",\n  \"defaultColumn\": \"Text\",\n  \"definitions\": [\n    {\n      \"name\": \"TYPE\"\n    },\n    {\n      \"index\": 1,\n      \"name\": \"PARAM\"\n    },\n    {\n      \"index\": 2,\n      \"name\": \"LINK\"\n    },\n    {\n      \"index\": 3,\n      \"name\": \"Text\"\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/LogFilter.json",
    "content": "{\n  \"sheet\": \"LogFilter\",\n  \"defaultColumn\": \"Name\",\n  \"definitions\": [\n    {\n      \"name\": \"LogKind\"\n    },\n    {\n      \"index\": 1,\n      \"name\": \"Caster\"\n    },\n    {\n      \"index\": 2,\n      \"name\": \"Target\"\n    },\n    {\n      \"index\": 3,\n      \"name\": \"Category\"\n    },\n    {\n      \"index\": 4,\n      \"name\": \"DisplayOrder\"\n    },\n    {\n      \"index\": 5,\n      \"name\": \"Preset\"\n    },\n    {\n      \"index\": 6,\n      \"name\": \"Name\"\n    },\n    {\n      \"index\": 7,\n      \"name\": \"Example\"\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/LogKind.json",
    "content": "{\n  \"sheet\": \"LogKind\",\n  \"definitions\": [\n    {\n      \"index\": 1,\n      \"name\": \"Format\"\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/LogMessage.json",
    "content": "{\n  \"sheet\": \"LogMessage\",\n  \"defaultColumn\": \"Text\",\n  \"definitions\": [\n    {\n      \"name\": \"LogKind\"\n    },\n    {\n      \"index\": 5,\n      \"name\": \"Text\"\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/LotteryExchangeShop.json",
    "content": "{\n  \"sheet\": \"LotteryExchangeShop\",\n  \"definitions\": [\n    {\n      \"index\": 1,\n      \"type\": \"repeat\",\n      \"count\": 32,\n      \"definition\": {\n        \"name\": \"ItemAccepted\",\n        \"converter\": {\n          \"type\": \"link\",\n          \"target\": \"Item\"\n        }\n      }\n    },\n    {\n      \"index\": 33,\n      \"type\": \"repeat\",\n      \"count\": 32,\n      \"definition\": {\n        \"name\": \"AmountAccepted\"\n      }\n    },\n    {\n      \"index\": 129,\n      \"name\": \"Lua\"\n    },\n    {\n      \"index\": 130,\n      \"type\": \"repeat\",\n      \"count\": 3,\n      \"definition\": {\n        \"name\": \"LogMessage\",\n        \"converter\": {\n          \"type\": \"link\",\n          \"target\": \"LogMessage\"\n        }\n      }\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/MJIAnimals.json",
    "content": "{\n  \"sheet\": \"MJIAnimals\",\n  \"definitions\": [\n    {\n      \"name\": \"BNpcBase\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"BNpcBase\"\n      }\n    },\n    {\n      \"index\": 1,\n      \"name\": \"Size\"\n    },\n    {\n      \"index\": 2,\n      \"name\": \"Rarity\"\n    },\n    {\n      \"index\": 3,\n      \"name\": \"Sort\"\n    },\n    {\n      \"index\": 4,\n      \"type\": \"repeat\",\n      \"count\": 2,\n      \"definition\": {\n        \"name\": \"Reward\",\n        \"converter\": {\n          \"type\": \"link\",\n          \"target\": \"Item\"\n        }\n      }\n    },\n    {\n      \"index\": 6,\n      \"name\": \"Icon\",\n      \"converter\": {\n        \"type\": \"icon\"\n      }\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/MJIBuilding.json",
    "content": "{\n  \"sheet\": \"MJIBuilding\",\n  \"definitions\": [\n    {\n      \"name\": \"sgb[0]\",\n      \"index\": 1,\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"ExportedSG\"\n      }\n    },\n    {\n      \"name\": \"sgb[1]\",\n      \"index\": 4,\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"ExportedSG\"\n      }\n    },\n    {\n      \"name\": \"sgb[2]\",\n      \"index\": 6,\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"ExportedSG\"\n      }\n    },\n    {\n      \"name\": \"sgb[3]\",\n      \"index\": 8,\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"ExportedSG\"\n      }\n    },\n    {\n      \"name\": \"sgb[4]\",\n      \"index\": 10,\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"ExportedSG\"\n      }\n    },\n    {\n      \"index\": 19,\n      \"type\": \"repeat\",\n      \"count\": 5,\n      \"definition\": {\n        \"name\": \"Material\",\n        \"converter\": {\n          \"type\": \"link\",\n          \"target\": \"MJIItemPouch\"\n        }\n      }\n    },\n    {\n      \"index\": 24,\n      \"type\": \"repeat\",\n      \"count\": 5,\n      \"definition\": {\n        \"name\": \"Amount\"\n      }\n    },\n    {\n      \"name\": \"Name\",\n      \"index\": 29,\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"MJIText\"\n      }\n    },\n    {\n      \"index\": 31,\n      \"name\": \"Icon\",\n      \"converter\": {\n        \"type\": \"icon\"\n      }\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/MJIBuildingPlace.json",
    "content": "{\n  \"sheet\": \"MJIBuildingPlace\",\n  \"definitions\": [\n    {\n      \"name\": \"Name\",\n      \"index\": 1,\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"EObjName\"\n      }\n    },\n    {\n      \"name\": \"SGB\",\n      \"index\": 2,\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"ExportedSG\"\n      }\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/MJICraftworksObject.json",
    "content": "{\n  \"sheet\": \"MJICraftworksObject\",\n  \"defaultColumn\": \"Item\",\n  \"definitions\": [\n    {\n      \"name\": \"Item\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"Item\"\n      }\n    },\n    {\n      \"index\": 1,\n      \"type\": \"repeat\",\n      \"count\": 2,\n      \"definition\": {\n        \"name\": \"Theme\",\n        \"converter\": {\n          \"type\": \"link\",\n          \"target\": \"MJICraftworksObjectTheme\"\n        }\n      }\n    },\n    {\n      \"index\": 4,\n      \"type\": \"repeat\",\n      \"count\": 4,\n      \"definition\": {\n        \"type\": \"group\",\n        \"members\": [\n          {\n            \"name\": \"Material\",\n            \"converter\": {\n              \"type\": \"link\",\n              \"target\": \"MJIItemPouch\"\n            }\n          },\n          {\n            \"name\": \"Amount\"\n          }\n        ]\n      }\n    },\n    {\n      \"index\": 12,\n      \"name\": \"LevelReq\"\n    },\n    {\n      \"index\": 13,\n      \"name\": \"CraftingTime\"\n    },\n    {\n      \"index\": 14,\n      \"name\": \"Value\"\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/MJICraftworksObjectTheme.json",
    "content": "{\n  \"sheet\": \"MJICraftworksObjectTheme\",\n  \"defaultColumn\": \"Name\",\n  \"definitions\": [\n    {\n      \"name\": \"Name\"\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/MJICraftworksPopularity.json",
    "content": "{\n  \"sheet\": \"MJICraftworksPopularity\",\n  \"definitions\": [\n    {\n      \"type\": \"repeat\",\n      \"count\": 91,\n      \"definition\": {\n        \"name\": \"Popularity\",\n        \"converter\": {\n          \"type\": \"link\",\n          \"target\": \"MJICraftworksPopularityType\"\n        }\n      }\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/MJICraftworksPopularityType.json",
    "content": "{\n  \"sheet\": \"MJICraftworksPopularityType\",\n  \"defaultColumn\": \"Ratio\",\n  \"definitions\": [\n    {\n      \"name\": \"Ratio\"\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/MJICraftworksRankRatio.json",
    "content": "{\n  \"sheet\": \"MJICraftworksRankRatio\",\n  \"definitions\": [\n    {\n      \"name\": \"Ratio\"\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/MJICraftworksSupplyDefine.json",
    "content": "{\n  \"sheet\": \"MJICraftworksSupplyDefine\",\n  \"definitions\": [\n    {\n      \"name\": \"Supply\"\n    },\n    {\n      \"index\": 1,\n      \"name\": \"Ratio\"\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/MJICraftworksTension.json",
    "content": "{\n  \"sheet\": \"MJICraftworksTension\",\n  \"definitions\": []\n}"
  },
  {
    "path": "SaintCoinach/Definitions/MJICropSeed.json",
    "content": "{\n  \"sheet\": \"MJICropSeed\",\n  \"defaultColumn\": \"Item\",\n  \"definitions\": [\n    {\n      \"name\": \"Item\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"Item\"\n      }\n    },\n    {\n      \"index\": 1,\n      \"name\": \"SGB\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"ExportedSG\"\n      }\n    },\n    {\n      \"index\": 2,\n      \"name\": \"Name\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"EObjName\"\n      }\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/MJIDisposalShopItem.json",
    "content": "{\n  \"sheet\": \"MJIDisposalShopItem\",\n  \"definitions\": [\n    {\n      \"name\": \"Item\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"MJIItemPouch\"\n      }\n    },\n    {\n      \"index\": 1,\n      \"name\": \"Currency\"\n    },\n    {\n      \"index\": 2,\n      \"name\": \"Count\"\n    },\n    {\n      \"index\": 3,\n      \"name\": \"Category\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"MJIDisposalShopUICategory\"\n      }\n    },\n    {\n      \"index\": 4,\n      \"name\": \"Sort\"\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/MJIDisposalShopUICategory.json",
    "content": "{\n  \"sheet\": \"MJIDisposalShopUICategory\",\n  \"defaultColumn\": \"Category\",\n  \"definitions\": [\n    {\n      \"name\": \"Category\"\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/MJIFarmPastureRank.json",
    "content": "{\n  \"sheet\": \"MJIFarmPastureRank\",\n  \"definitions\": [\n    {\n      \"type\": \"repeat\",\n      \"count\": 4,\n      \"definition\": {\n        \"type\": \"group\",\n        \"members\": [\n          {\n            \"type\": \"repeat\",\n            \"count\": 4,\n            \"definition\": {\n              \"name\": \"SGB\",\n              \"converter\": {\n                \"type\": \"link\",\n                \"target\": \"ExportedSG\"\n              }\n            }\n          }\n        ]\n      }\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/MJIFunction.json",
    "content": "{\n  \"sheet\": \"MJIFunction\",\n  \"definitions\": []\n}"
  },
  {
    "path": "SaintCoinach/Definitions/MJIGardenscaping.json",
    "content": "{\n  \"sheet\": \"MJIGardenscaping\",\n  \"definitions\": [\n    {\n      \"name\": \"Level\"\n    },\n    {\n      \"name\": \"Item\",\n      \"index\": 5,\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"Item\"\n      }\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/MJIGathering.json",
    "content": "{\n  \"sheet\": \"MJIGathering\",\n  \"defaultColumn\": \"GatheringObject\",\n  \"definitions\": [\n    {\n      \"name\": \"GatheringObject\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"MJIGatheringObject\"\n      }\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/MJIGatheringItem.json",
    "content": "{\n  \"sheet\": \"MJIGatheringItem\",\n  \"defaultColumn\": \"Item\",\n  \"definitions\": [\n    {\n      \"name\": \"Item\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"Item\"\n      }\n    },\n    {\n      \"index\": 1,\n      \"name\": \"Sort\"\n    },\n    {\n      \"index\": 2,\n      \"name\": \"Tool\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"MJIGatheringTool\"\n      }\n    },\n    {\n      \"index\": 3,\n      \"name\": \"X\"\n    },\n    {\n      \"index\": 4,\n      \"name\": \"Y\"\n    },\n    {\n      \"index\": 5,\n      \"name\": \"Radius\"\n    },\n    {\n      \"index\": 6,\n      \"name\": \"Map\"\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/MJIGatheringObject.json",
    "content": "{\n  \"sheet\": \"MJIGatheringObject\",\n  \"defaultColumn\": \"Name\",\n  \"definitions\": [\n    {\n      \"name\": \"SGB\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"ExportedSG\"\n      }\n    },\n    {\n      \"index\": 1,\n      \"name\": \"MapIcon\",\n      \"converter\": {\n        \"type\": \"icon\"\n      }\n    },\n    {\n      \"index\": 3,\n      \"name\": \"Name\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"EObjName\"\n      }\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/MJIGatheringTool.json",
    "content": "{\n  \"sheet\": \"MJIGatheringTool\",\n  \"definitions\": [\n    {\n      \"name\": \"Item\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"MJIKeyItem\"\n      }\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/MJIHudMode.json",
    "content": "{\n  \"sheet\": \"MJIHudMode\",\n  \"defaultColumn\": \"Name\",\n  \"definitions\": [\n    {\n      \"name\": \"Name\"\n    },\n    {\n      \"index\": 1,\n      \"name\": \"Title\"\n    },\n    {\n      \"index\": 2,\n      \"name\": \"Icon\",\n      \"converter\": {\n        \"type\": \"icon\"\n      }\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/MJIItemCategory.json",
    "content": "{\n  \"sheet\": \"MJIItemCategory\",\n  \"defaultColumn\": \"Singular\",\n  \"definitions\": [\n    {\n      \"name\": \"Singular\"\n    },\n    {\n      \"index\": 1,\n      \"name\": \"Plural\"\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/MJIItemPouch.json",
    "content": "{\n  \"sheet\": \"MJIItemPouch\",\n  \"defaultColumn\": \"Item\",\n  \"definitions\": [\n    {\n      \"name\": \"Item\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"Item\"\n      }\n    },\n    {\n      \"index\": 1,\n      \"name\": \"Category\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"MJIItemCategory\"\n      }\n    },\n    {\n      \"index\": 2,\n      \"name\": \"Crop\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"MJICropSeed\"\n      }\n    },\n    {\n      \"index\": 3,\n      \"name\": \"Sort\"\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/MJIKeyItem.json",
    "content": "{\n  \"sheet\": \"MJIKeyItem\",\n  \"defaultColumn\": \"Item\",\n  \"definitions\": [\n    {\n      \"name\": \"Item\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"Item\"\n      }\n    },\n    {\n      \"index\": 1,\n      \"name\": \"Sort\"\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/MJILandmark.json",
    "content": "{\n  \"sheet\": \"MJILandmark\",\n  \"definitions\": [\n    {\n      \"index\": 3,\n      \"name\": \"SGB[0]\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"ExportedSG\"\n      }\n    },\n    {\n      \"index\": 4,\n      \"name\": \"SGB[1]\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"ExportedSG\"\n      }\n    },\n    {\n      \"index\": 5,\n      \"name\": \"SGB[2]\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"ExportedSG\"\n      }\n    },\n    {\n      \"index\": 7,\n      \"name\": \"SGB[3]\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"ExportedSG\"\n      }\n    },\n    {\n      \"index\": 9,\n      \"name\": \"SGB[4]\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"ExportedSG\"\n      }\n    },\n    {\n      \"index\": 11,\n      \"name\": \"SGB[5]\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"ExportedSG\"\n      }\n    },\n    {\n      \"index\": 13,\n      \"name\": \"SGB[6]\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"ExportedSG\"\n      }\n    },\n    {\n      \"index\": 20,\n      \"type\": \"repeat\",\n      \"count\": 5,\n      \"definition\": {\n        \"name\": \"Material\",\n        \"converter\": {\n          \"type\": \"link\",\n          \"target\": \"MJIItemPouch\"\n        }\n      }\n    },\n    {\n      \"index\": 25,\n      \"type\": \"repeat\",\n      \"count\": 5,\n      \"definition\": {\n        \"name\": \"Amount\"\n      }\n    },\n    {\n      \"index\": 30,\n      \"name\": \"Name\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"MJIText\"\n      }\n    },\n    {\n      \"index\": 32,\n      \"name\": \"Icon\",\n      \"converter\": {\n        \"type\": \"icon\"\n      }\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/MJILandmarkPlace.json",
    "content": "{\n  \"sheet\": \"MJILandmarkPlace\",\n  \"definitions\": [\n    {\n      \"index\": 1,\n      \"name\": \"Name\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"EObjName\"\n      }\n    },\n    {\n      \"index\": 3,\n      \"name\": \"SGB\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"ExportedSG\"\n      }\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/MJILivelyActor.json",
    "content": "{\n  \"sheet\": \"MJILivelyActor\",\n  \"definitions\": [\n    {\n      \"name\": \"ENPC\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"ENpcResident\"\n      }\n    },\n    {\n      \"index\": 1,\n      \"name\": \"Behavior\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"Behavior\"\n      }\n    },\n    {\n      \"index\": 2,\n      \"name\": \"X\"\n    },\n    {\n      \"index\": 3,\n      \"name\": \"Y\"\n    },\n    {\n      \"index\": 4,\n      \"name\": \"Z\"\n    },\n    {\n      \"index\": 5,\n      \"name\": \"Rot\"\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/MJIMinionPopAreas.json",
    "content": "{\n  \"sheet\": \"MJIMinionPopAreas\",\n  \"definitions\": []\n}"
  },
  {
    "path": "SaintCoinach/Definitions/MJIName.json",
    "content": "{\n  \"sheet\": \"MJIName\",\n  \"defaultColumn\": \"Name\",\n  \"definitions\": [\n    {\n      \"name\": \"Singular\"\n    },\n    {\n      \"index\": 1,\n      \"name\": \"Adjective\"\n    },\n    {\n      \"index\": 2,\n      \"name\": \"Plural\"\n    },\n    {\n      \"index\": 3,\n      \"name\": \"PossessivePronoun\"\n    },\n    {\n      \"index\": 4,\n      \"name\": \"StartsWithVowel\"\n    },\n    {\n      \"index\": 6,\n      \"name\": \"Pronoun\"\n    },\n    {\n      \"index\": 7,\n      \"name\": \"Article\"\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/MJINekomimiRequest.json",
    "content": "{\n  \"sheet\": \"MJINekomimiRequest\",\n  \"definitions\": []\n}"
  },
  {
    "path": "SaintCoinach/Definitions/MJIProgress.json",
    "content": "{\n  \"sheet\": \"MJIProgress\",\n  \"definitions\": [\n    {\n      \"name\": \"Vision\"\n    },\n    {\n      \"index\": 1,\n      \"name\": \"Objective\"\n    },\n    {\n      \"index\": 2,\n      \"name\": \"PreviousObjective\"\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/MJIRank.json",
    "content": "{\n  \"sheet\": \"MJIRank\",\n  \"definitions\": [\n    {\n      \"name\": \"ExpToNext\"\n    },\n    {\n      \"index\": 2,\n      \"type\": \"repeat\",\n      \"count\": 3,\n      \"definition\": {\n        \"name\": \"LogMessage\",\n        \"converter\": {\n          \"type\": \"link\",\n          \"target\": \"LogMessage\"\n        }\n      }\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/MJIRecipe.json",
    "content": "{\n  \"sheet\": \"MJIRecipe\",\n  \"defaultColumn\": \"KeyItem\",\n  \"definitions\": [\n    {\n      \"name\": \"LogMessage\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"LogMessage\"\n      }\n    },\n    {\n      \"index\": 1,\n      \"name\": \"KeyItem\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"MJIKeyItem\"\n      }\n    },\n    {\n      \"index\": 2,\n      \"name\": \"ItemPouch\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"MJIItemPouch\"\n      }\n    },\n    {\n      \"index\": 4,\n      \"type\": \"repeat\",\n      \"count\": 5,\n      \"definition\": {\n        \"type\": \"group\",\n        \"members\": [\n          {\n            \"name\": \"Material\",\n            \"converter\": {\n              \"type\": \"link\",\n              \"target\": \"MJIRecipeMaterial\"\n            }\n          },\n          {\n            \"name\": \"Amount\"\n          }\n        ]\n      }\n    },\n    {\n      \"index\": 14,\n      \"name\": \"Order\"\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/MJIRecipeMaterial.json",
    "content": "{\n  \"sheet\": \"MJIRecipeMaterial\",\n  \"defaultColumn\": \"ItemPouch\",\n  \"definitions\": [\n    {\n      \"name\": \"ItemPouch\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"MJIItemPouch\"\n      }\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/MJIStockyardManagementArea.json",
    "content": "{\n  \"sheet\": \"MJIStockyardManagementArea\",\n  \"defaultColumn\": \"Area\",\n  \"definitions\": [\n    {\n      \"name\": \"Rare Material\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"MJIItemPouch\"\n      }\n    },\n    {\n      \"index\": 2,\n      \"name\": \"Area\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"MJIText\"\n      }\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/MJIStockyardManagementTable.json",
    "content": "{\n  \"sheet\": \"MJIStockyardManagementTable\",\n  \"defaultColumn\": \"Material\",\n  \"definitions\": [\n    {\n      \"name\": \"Material\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"MJIItemPouch\"\n      }\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/MJIText.json",
    "content": "{\n  \"sheet\": \"MJIText\",\n  \"defaultColumn\": \"Text\",\n  \"definitions\": [\n    {\n      \"name\": \"Text\"\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/MJIVillageAppearanceSG.json",
    "content": "{\n  \"sheet\": \"MJIVillageAppearanceSG\",\n  \"definitions\": [\n    {\n      \"type\": \"repeat\",\n      \"count\": 5,\n      \"definition\": {\n        \"name\": \"SGB\",\n        \"converter\": {\n          \"type\": \"link\",\n          \"target\": \"ExportedSG\"\n        }\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "SaintCoinach/Definitions/MJIVillageAppearanceUI.json",
    "content": "{\n  \"sheet\": \"MJIVillageAppearanceUI\",\n  \"definitions\": [\n    {\n      \"name\": \"Floor\",\n      \"converter\": {\n        \"type\": \"icon\"\n      }\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/MJIVillageDevelopment.json",
    "content": "{\n  \"sheet\": \"MJIVillageDevelopment\",\n  \"definitions\": [\n    {\n      \"name\": \"ENPC\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"ENpcResident\"\n      }\n    },\n    {\n      \"name\": \"Behavior[0]\",\n      \"index\": 9,\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"Behavior\"\n      }\n    },\n    {\n      \"name\": \"Behavior[1]\",\n      \"index\": 11,\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"Behavior\"\n      }\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/MKDLore.json",
    "content": "{\n  \"sheet\": \"MKDLore\",\n  \"defaultColumn\": \"Name\",\n  \"definitions\": [\n    {\n      \"index\": 1,\n      \"name\": \"Image\",\n      \"converter\": {\n        \"type\": \"icon\"\n      }\n    },\n    {\n      \"index\": 5,\n      \"name\": \"Name\"\n    },\n    {\n      \"index\": 6,\n      \"name\": \"Description\"\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/MYCTemporaryItem.json",
    "content": "{\n  \"sheet\": \"MYCTemporaryItem\",\n  \"defaultColumn\": \"Action\",\n  \"definitions\": [\n    {\n      \"name\": \"Category\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"MYCTemporaryItemUICategory\"\n      }\n    },\n    {\n      \"index\": 1,\n      \"name\": \"Type\"\n    },\n    {\n      \"index\": 2,\n      \"name\": \"Action\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"Action\"\n      }\n    },\n    {\n      \"index\": 3,\n      \"name\": \"Max\"\n    },\n    {\n      \"index\": 4,\n      \"name\": \"Weight\"\n    },\n    {\n      \"index\": 5,\n      \"name\": \"Order\"\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/MYCTemporaryItemUICategory.json",
    "content": "{\n  \"sheet\": \"MYCTemporaryItemUICategory\",\n  \"defaultColumn\": \"Name\",\n  \"definitions\": [\n    {\n      \"name\": \"Name\"\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/MYCWarResultNotebook.json",
    "content": "{\n  \"sheet\": \"MYCWarResultNotebook\",\n  \"defaultColumn\": \"Name\",\n  \"definitions\": [\n    {\n      \"name\": \"Number\"\n    },\n    {\n      \"index\": 2,\n      \"name\": \"Link\"\n    },\n    {\n      \"index\": 3,\n      \"name\": \"Quest\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"Quest\"\n      }\n    },\n    {\n      \"index\": 5,\n      \"name\": \"Icon\",\n      \"converter\": {\n        \"type\": \"icon\"\n      }\n    },\n    {\n      \"index\": 6,\n      \"name\": \"Image\",\n      \"converter\": {\n        \"type\": \"icon\"\n      }\n    },\n    {\n      \"index\": 7,\n      \"name\": \"Rarity\"\n    },\n    {\n      \"index\": 8,\n      \"name\": \"NameJP\"\n    },\n    {\n      \"index\": 9,\n      \"name\": \"Name\"\n    },\n    {\n      \"index\": 10,\n      \"name\": \"Description\"\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/MacroIcon.json",
    "content": "{\n  \"sheet\": \"MacroIcon\",\n  \"defaultColumn\": \"Icon\",\n  \"definitions\": [\n    {\n      \"name\": \"Icon\",\n      \"converter\": {\n        \"type\": \"icon\"\n      }\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/MacroIconRedirectOld.json",
    "content": "{\n  \"sheet\": \"MacroIconRedirectOld\",\n  \"definitions\": [\n    {\n      \"name\": \"Icon{Old}\",\n      \"converter\": {\n        \"type\": \"icon\"\n      }\n    },\n    {\n      \"index\": 1,\n      \"name\": \"Icon{New}\",\n      \"converter\": {\n        \"type\": \"icon\"\n      }\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/MainCommand.json",
    "content": "{\n  \"sheet\": \"MainCommand\",\n  \"defaultColumn\": \"Name\",\n  \"definitions\": [\n    {\n      \"name\": \"Icon\",\n      \"converter\": {\n        \"type\": \"icon\"\n      }\n    },\n    {\n      \"index\": 1,\n      \"name\": \"Category\"\n    },\n    {\n      \"index\": 2,\n      \"name\": \"MainCommandCategory\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"MainCommandCategory\"\n      }\n    },\n    {\n      \"index\": 3,\n      \"name\": \"SortID\"\n    },\n    {\n      \"index\": 5,\n      \"name\": \"Name\"\n    },\n    {\n      \"index\": 6,\n      \"name\": \"Description\"\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/MainCommandCategory.json",
    "content": "{\n  \"sheet\": \"MainCommandCategory\",\n  \"defaultColumn\": \"Name\",\n  \"definitions\": [\n    {\n      \"index\": 1,\n      \"name\": \"Name\"\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/MandervilleWeaponEnhance.json",
    "content": "{\n  \"sheet\": \"MandervilleWeaponEnhance\",\n  \"definitions\": []\n}"
  },
  {
    "path": "SaintCoinach/Definitions/ManeuversArmor.json",
    "content": "{\n  \"sheet\": \"ManeuversArmor\",\n  \"definitions\": [\n    {\n      \"index\": 1,\n      \"type\": \"repeat\",\n      \"count\": 2,\n      \"definition\": {\n        \"name\": \"BNpcBase\",\n        \"converter\": {\n          \"type\": \"link\",\n          \"target\": \"BNpcName\"\n        }\n      }\n    },\n    {\n      \"index\": 5,\n      \"type\": \"repeat\",\n      \"count\": 5,\n      \"definition\": {\n        \"name\": \"Icon\",\n        \"converter\": {\n          \"type\": \"icon\"\n        }\n      }\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/Map.json",
    "content": "{\n  \"sheet\": \"Map\",\n  \"defaultColumn\": \"PlaceName\",\n  \"definitions\": [\n    {\n      \"name\": \"MapCondition\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"MapCondition\"\n      }\n    },\n    {\n      \"index\": 1,\n      \"name\": \"PriorityCategoryUI\"\n    },\n    {\n      \"index\": 2,\n      \"name\": \"PriorityUI\"\n    },\n    {\n      \"index\": 3,\n      \"name\": \"MapIndex\"\n    },\n    {\n      \"index\": 4,\n      \"name\": \"Hierarchy\"\n    },\n    {\n      \"index\": 5,\n      \"name\": \"MapMarkerRange\"\n    },\n    {\n      \"index\": 6,\n      \"name\": \"Id\"\n    },\n    {\n      \"index\": 7,\n      \"name\": \"SizeFactor\"\n    },\n    {\n      \"index\": 8,\n      \"name\": \"Offset{X}\"\n    },\n    {\n      \"index\": 9,\n      \"name\": \"Offset{Y}\"\n    },\n    {\n      \"index\": 10,\n      \"name\": \"PlaceName{Region}\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"PlaceName\"\n      }\n    },\n    {\n      \"index\": 11,\n      \"name\": \"PlaceName\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"PlaceName\"\n      }\n    },\n    {\n      \"index\": 12,\n      \"name\": \"PlaceName{Sub}\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"PlaceName\"\n      }\n    },\n    {\n      \"index\": 13,\n      \"name\": \"DiscoveryIndex\"\n    },\n    {\n      \"index\": 14,\n      \"name\": \"DiscoveryFlag\"\n    },\n    {\n      \"index\": 15,\n      \"name\": \"TerritoryType\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"TerritoryType\"\n      }\n    },\n    {\n      \"index\": 16,\n      \"name\": \"DiscoveryArrayByte\"\n    },\n    {\n      \"index\": 17,\n      \"name\": \"IsEvent\"\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/MapCondition.json",
    "content": "{\n  \"sheet\": \"MapCondition\",\n  \"defaultColumn\": \"Quest\",\n  \"definitions\": [\n    {\n      \"index\": 1,\n      \"name\": \"Quest\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"Quest\"\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "SaintCoinach/Definitions/MapExclusive.json",
    "content": "{\n  \"sheet\": \"MapExclusive\",\n  \"definitions\": []\n}"
  },
  {
    "path": "SaintCoinach/Definitions/MapMarker.json",
    "content": "{\n  \"sheet\": \"MapMarker\",\n  \"definitions\": [\n    {\n      \"name\": \"X\"\n    },\n    {\n      \"index\": 1,\n      \"name\": \"Y\"\n    },\n    {\n      \"index\": 2,\n      \"name\": \"Icon\",\n      \"converter\": {\n        \"type\": \"icon\"\n      }\n    },\n    {\n      \"index\": 3,\n      \"name\": \"PlaceName{Subtext}\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"PlaceName\"\n      }\n    },\n    {\n      \"index\": 4,\n      \"name\": \"SubtextOrientation\"\n    },\n    {\n      \"index\": 5,\n      \"name\": \"MapMarkerRegion\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"MapMarkerRegion\"\n      }\n    },\n    {\n      \"index\": 6,\n      \"name\": \"Type\"\n    },\n    {\n      \"index\": 7,\n      \"name\": \"Data{Type}\"\n    },\n    {\n      \"index\": 8,\n      \"name\": \"Data{Key}\",\n      \"converter\": {\n        \"type\": \"complexlink\",\n        \"links\": [\n          {\n            \"when\": {\n              \"key\": \"Data{Type}\",\n              \"value\": 1\n            },\n            \"sheet\": \"Map\"\n          },\n          {\n            \"when\": {\n              \"key\": \"Data{Type}\",\n              \"value\": 2\n            },\n            \"sheet\": \"Map\"\n          },\n          {\n            \"when\": {\n              \"key\": \"Data{Type}\",\n              \"value\": 3\n            },\n            \"sheet\": \"Aetheryte\"\n          },\n          {\n            \"when\": {\n              \"key\": \"Data{Type}\",\n              \"value\": 4\n            },\n            \"sheet\": \"PlaceName\"\n          }\n        ]\n      }\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/MapMarkerRegion.json",
    "content": "{\n  \"sheet\": \"MapMarkerRegion\",\n  \"defaultColumn\": \"X\",\n  \"definitions\": [\n    {\n      \"index\": 1,\n      \"name\": \"X\"\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/MapReplace.json",
    "content": "{\n  \"sheet\": \"MapReplace\",\n  \"definitions\": []\n}"
  },
  {
    "path": "SaintCoinach/Definitions/MapSymbol.json",
    "content": "{\n  \"sheet\": \"MapSymbol\",\n  \"definitions\": [\n    {\n      \"name\": \"Icon\",\n      \"converter\": {\n        \"type\": \"icon\"\n      }\n    },\n    {\n      \"index\": 1,\n      \"name\": \"PlaceName\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"PlaceName\"\n      }\n    },\n    {\n      \"index\": 2,\n      \"name\": \"DisplayNavi\"\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/MapTransientPvPMap.json",
    "content": "{\n  \"sheet\": \"MapTransientPvPMap\",\n  \"definitions\": []\n}"
  },
  {
    "path": "SaintCoinach/Definitions/MapType.json",
    "content": "{\n  \"sheet\": \"MapType\",\n  \"definitions\": []\n}"
  },
  {
    "path": "SaintCoinach/Definitions/Marker.json",
    "content": "{\n  \"sheet\": \"Marker\",\n  \"definitions\": [\n    {\n      \"name\": \"Icon\",\n      \"converter\": {\n        \"type\": \"icon\"\n      }\n    },\n    {\n      \"index\": 2,\n      \"name\": \"Name\"\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/Materia.json",
    "content": "{\n  \"sheet\": \"Materia\",\n  \"defaultColumn\": \"Item[0]\",\n  \"definitions\": [\n    {\n      \"type\": \"repeat\",\n      \"count\": 16,\n      \"definition\": {\n        \"name\": \"Item\",\n        \"converter\": {\n          \"type\": \"link\",\n          \"target\": \"Item\"\n        }\n      }\n    },\n    {\n      \"index\": 16,\n      \"name\": \"BaseParam\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"BaseParam\"\n      }\n    },\n    {\n      \"index\": 17,\n      \"type\": \"repeat\",\n      \"count\": 16,\n      \"definition\": {\n        \"name\": \"Value\"\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "SaintCoinach/Definitions/MateriaGrade.json",
    "content": "{\n  \"sheet\": \"MateriaGrade\",\n  \"definitions\": []\n}"
  },
  {
    "path": "SaintCoinach/Definitions/MateriaJoinRate.json",
    "content": "{\n  \"sheet\": \"MateriaJoinRate\",\n  \"definitions\": [\n    {\n      \"type\": \"repeat\",\n      \"count\": 4,\n      \"definition\": {\n        \"name\": \"[NQ]Overmeld%Slot\"\n      }\n    },\n    {\n      \"index\": 4,\n      \"type\": \"repeat\",\n      \"count\": 4,\n      \"definition\": {\n        \"name\": \"[HQ]Overmeld%Slot\"\n      }\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/MateriaJoinRateGatherCraft.json",
    "content": "{\n  \"sheet\": \"MateriaJoinRateGatherCraft\",\n  \"definitions\": [\n    {\n      \"type\": \"repeat\",\n      \"count\": 4,\n      \"definition\": {\n        \"name\": \"[NQ]Overmeld%Slot\"\n      }\n    },\n    {\n      \"index\": 4,\n      \"type\": \"repeat\",\n      \"count\": 4,\n      \"definition\": {\n        \"name\": \"[HQ]Overmeld%Slot\"\n      }\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/McGuffin.json",
    "content": "{\n  \"sheet\": \"McGuffin\",\n  \"definitions\": [\n    {\n      \"name\": \"UIData\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"McGuffinUIData\"\n      }\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/McGuffinUIData.json",
    "content": "{\n  \"sheet\": \"McGuffinUIData\",\n  \"definitions\": [\n    {\n      \"name\": \"Order\"\n    },\n    {\n      \"index\": 1,\n      \"name\": \"Icon\",\n      \"converter\": {\n        \"type\": \"icon\"\n      }\n    },\n    {\n      \"index\": 2,\n      \"name\": \"Name\"\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/MiniGameRA.json",
    "content": "{\n  \"sheet\": \"MiniGameRA\",\n  \"definitions\": [\n    {\n      \"index\": 1,\n      \"name\": \"Icon\",\n      \"converter\": {\n        \"type\": \"icon\"\n      }\n    },\n    {\n      \"index\": 2,\n      \"name\": \"Image\",\n      \"converter\": {\n        \"type\": \"icon\"\n      }\n    },\n    {\n      \"index\": 3,\n      \"name\": \"BGM\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"BGM\"\n      }\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/MiniGameTurnBreakAction.json",
    "content": "{\n  \"sheet\": \"MiniGameTurnBreakAction\",\n  \"definitions\": []\n}"
  },
  {
    "path": "SaintCoinach/Definitions/MiniGameTurnBreakConst.json",
    "content": "{\n  \"sheet\": \"MiniGameTurnBreakConst\",\n  \"definitions\": []\n}"
  },
  {
    "path": "SaintCoinach/Definitions/MiniGameTurnBreakEnemy.json",
    "content": "{\n  \"sheet\": \"MiniGameTurnBreakEnemy\",\n  \"definitions\": []\n}"
  },
  {
    "path": "SaintCoinach/Definitions/MiniGameTurnBreakPop.json",
    "content": "{\n  \"sheet\": \"MiniGameTurnBreakPop\",\n  \"definitions\": []\n}"
  },
  {
    "path": "SaintCoinach/Definitions/MiniGameTurnBreakPopOffset.json",
    "content": "{\n  \"sheet\": \"MiniGameTurnBreakPopOffset\",\n  \"definitions\": []\n}"
  },
  {
    "path": "SaintCoinach/Definitions/MiniGameTurnBreakStage.json",
    "content": "{\n  \"sheet\": \"MiniGameTurnBreakStage\",\n  \"definitions\": []\n}"
  },
  {
    "path": "SaintCoinach/Definitions/MiniGameTurnBreakStatus.json",
    "content": "{\n  \"sheet\": \"MiniGameTurnBreakStatus\",\n  \"definitions\": []\n}"
  },
  {
    "path": "SaintCoinach/Definitions/MinionRace.json",
    "content": "{\n  \"sheet\": \"MinionRace\",\n  \"defaultColumn\": \"Name\",\n  \"definitions\": [\n    {\n      \"name\": \"Name\"\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/MinionRules.json",
    "content": "{\n  \"sheet\": \"MinionRules\",\n  \"defaultColumn\": \"Rule\",\n  \"definitions\": [\n    {\n      \"name\": \"Rule\"\n    },\n    {\n      \"index\": 1,\n      \"name\": \"Description\"\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/MinionSkillType.json",
    "content": "{\n  \"sheet\": \"MinionSkillType\",\n  \"defaultColumn\": \"Name\",\n  \"definitions\": [\n    {\n      \"name\": \"Name\"\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/MirageStoreSetItem.json",
    "content": "{\n  \"sheet\": \"MirageStoreSetItem\",\n  \"definitions\": [\n    {\n      \"index\": 2,\n      \"type\": \"repeat\",\n      \"count\": 9,\n      \"definition\": {\n        \"name\": \"Item\",\n        \"converter\": {\n          \"type\": \"link\",\n          \"target\": \"Item\"\n        }\n      }\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/MirageStoreSetItemLookup.json",
    "content": "{\n  \"sheet\": \"MirageStoreSetItemLookup\",\n  \"definitions\": [\n    {\n      \"type\": \"repeat\",\n      \"count\": 2,\n      \"definition\": {\n        \"name\": \"MirageStoreSetItem\",\n        \"converter\": {\n          \"type\": \"link\",\n          \"target\": \"MirageStoreSetItem\"\n        }\n      }\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/MobHuntOrder.json",
    "content": "{\n  \"sheet\": \"MobHuntOrder\",\n  \"definitions\": [\n    {\n      \"name\": \"Target\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"MobHuntTarget\"\n      }\n    },\n    {\n      \"index\": 1,\n      \"name\": \"NeededKills\"\n    },\n    {\n      \"index\": 2,\n      \"name\": \"Type\"\n    },\n    {\n      \"index\": 3,\n      \"name\": \"Rank\"\n    },\n    {\n      \"index\": 4,\n      \"name\": \"MobHuntReward\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"MobHuntReward\"\n      }\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/MobHuntOrderType.json",
    "content": "{\n  \"sheet\": \"MobHuntOrderType\",\n  \"defaultColumn\": \"EventItem\",\n  \"definitions\": [\n    {\n      \"name\": \"Type\"\n    },\n    {\n      \"index\": 1,\n      \"name\": \"Quest\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"Quest\"\n      }\n    },\n    {\n      \"index\": 2,\n      \"name\": \"EventItem\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"EventItem\"\n      }\n    },\n    {\n      \"index\": 3,\n      \"name\": \"Order{Start}\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"MobHuntOrder\"\n      }\n    },\n    {\n      \"index\": 4,\n      \"name\": \"Order{Amount}\"\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/MobHuntReward.json",
    "content": "{\n  \"sheet\": \"MobHuntReward\",\n  \"definitions\": [\n    {\n      \"name\": \"ExpReward\"\n    },\n    {\n      \"index\": 1,\n      \"name\": \"GilReward\"\n    },\n    {\n      \"index\": 2,\n      \"name\": \"Expansion\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"ExVersion\"\n      }\n    },\n    {\n      \"index\": 3,\n      \"name\": \"CurrencyReward\"\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/MobHuntRewardCap.json",
    "content": "{\n  \"sheet\": \"MobHuntRewardCap\",\n  \"definitions\": [\n    {\n      \"name\": \"ExpCap\"\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/MobHuntTarget.json",
    "content": "{\n  \"sheet\": \"MobHuntTarget\",\n  \"defaultColumn\": \"Name\",\n  \"definitions\": [\n    {\n      \"name\": \"Name\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"BNpcName\"\n      }\n    },\n    {\n      \"index\": 1,\n      \"name\": \"FATE\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"Fate\"\n      }\n    },\n    {\n      \"index\": 2,\n      \"name\": \"Icon\",\n      \"converter\": {\n        \"type\": \"icon\"\n      }\n    },\n    {\n      \"index\": 3,\n      \"name\": \"TerritoryType\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"Map\"\n      }\n    },\n    {\n      \"index\": 4,\n      \"name\": \"PlaceName\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"PlaceName\"\n      }\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/ModelChara.json",
    "content": "{\n  \"sheet\": \"ModelChara\",\n  \"definitions\": [\n    {\n      \"name\": \"Type\"\n    },\n    {\n      \"index\": 1,\n      \"name\": \"Model\"\n    },\n    {\n      \"index\": 2,\n      \"name\": \"Base\"\n    },\n    {\n      \"index\": 3,\n      \"name\": \"Variant\"\n    },\n    {\n      \"index\": 4,\n      \"name\": \"SEPack\"\n    },\n    {\n      \"index\": 7,\n      \"name\": \"PapVariation\"\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/ModelSkeleton.json",
    "content": "{\n  \"sheet\": \"ModelSkeleton\",\n  \"definitions\": [\n    {\n      \"name\": \"Radius\"\n    },\n    {\n      \"index\": 1,\n      \"name\": \"Height\"\n    },\n    {\n      \"index\": 2,\n      \"name\": \"VFXScale\"\n    },\n    {\n      \"index\": 11,\n      \"name\": \"FloatHeight\"\n    },\n    {\n      \"index\": 12,\n      \"name\": \"FloatDown\"\n    },\n    {\n      \"index\": 13,\n      \"name\": \"FloatUp\"\n    },\n    {\n      \"index\": 15,\n      \"name\": \"MotionBlendType\"\n    },\n    {\n      \"index\": 16,\n      \"name\": \"LoopFlySE\"\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/ModelState.json",
    "content": "{\n  \"sheet\": \"ModelState\",\n  \"defaultColumn\": \"Start\",\n  \"definitions\": [\n    {\n      \"index\": 1,\n      \"name\": \"Start\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"ActionTimeline\"\n      }\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/MonsterNote.json",
    "content": "{\n  \"sheet\": \"MonsterNote\",\n  \"defaultColumn\": \"Name\",\n  \"definitions\": [\n    {\n      \"type\": \"repeat\",\n      \"count\": 4,\n      \"definition\": {\n        \"name\": \"MonsterNoteTarget\",\n        \"converter\": {\n          \"type\": \"link\",\n          \"target\": \"MonsterNoteTarget\"\n        }\n      }\n    },\n    {\n      \"index\": 4,\n      \"type\": \"repeat\",\n      \"count\": 4,\n      \"definition\": {\n        \"name\": \"Count\"\n      }\n    },\n    {\n      \"index\": 8,\n      \"name\": \"Reward\"\n    },\n    {\n      \"index\": 9,\n      \"name\": \"Name\"\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/MonsterNoteTarget.json",
    "content": "{\n  \"sheet\": \"MonsterNoteTarget\",\n  \"defaultColumn\": \"BNpcName\",\n  \"definitions\": [\n    {\n      \"name\": \"BNpcName\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"BNpcName\"\n      }\n    },\n    {\n      \"index\": 1,\n      \"name\": \"Icon\",\n      \"converter\": {\n        \"type\": \"icon\"\n      }\n    },\n    {\n      \"index\": 2,\n      \"name\": \"Town\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"Town\"\n      }\n    },\n    {\n      \"index\": 3,\n      \"type\": \"repeat\",\n      \"count\": 3,\n      \"definition\": {\n        \"type\": \"group\",\n        \"members\": [\n          {\n            \"name\": \"PlaceName{Zone}\",\n            \"converter\": {\n              \"type\": \"link\",\n              \"target\": \"PlaceName\"\n            }\n          },\n          {\n            \"name\": \"PlaceName{Location}\",\n            \"converter\": {\n              \"type\": \"link\",\n              \"target\": \"PlaceName\"\n            }\n          }\n        ]\n      }\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/MotionTimeline.json",
    "content": "{\n  \"sheet\": \"MotionTimeline\",\n  \"definitions\": [\n    {\n      \"name\": \"Filename\"\n    },\n    {\n      \"index\": 1,\n      \"name\": \"BlendGroup\"\n    },\n    {\n      \"index\": 2,\n      \"name\": \"IsLoop\"\n    },\n    {\n      \"index\": 3,\n      \"name\": \"IsBlinkEnable\"\n    },\n    {\n      \"index\": 4,\n      \"name\": \"IsLipEnable\"\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/MotionTimelineBlendTable.json",
    "content": "{\n  \"sheet\": \"MotionTimelineBlendTable\",\n  \"definitions\": [\n    {\n      \"name\": \"DestBlendGroup\"\n    },\n    {\n      \"index\": 1,\n      \"name\": \"SrcBlendGroup\"\n    },\n    {\n      \"index\": 2,\n      \"name\": \"BlendFrame_PC\"\n    },\n    {\n      \"index\": 3,\n      \"name\": \"BlendFram_TypeA\"\n    },\n    {\n      \"index\": 4,\n      \"name\": \"BlendFram_TypeB\"\n    },\n    {\n      \"index\": 5,\n      \"name\": \"BlendFram_TypeC\"\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/Mount.json",
    "content": "{\n  \"sheet\": \"Mount\",\n  \"defaultColumn\": \"Singular\",\n  \"definitions\": [\n    {\n      \"name\": \"Singular\"\n    },\n    {\n      \"index\": 1,\n      \"name\": \"Adjective\"\n    },\n    {\n      \"index\": 2,\n      \"name\": \"Plural\"\n    },\n    {\n      \"index\": 3,\n      \"name\": \"PossessivePronoun\"\n    },\n    {\n      \"index\": 4,\n      \"name\": \"StartsWithVowel\"\n    },\n    {\n      \"index\": 6,\n      \"name\": \"Pronoun\"\n    },\n    {\n      \"index\": 7,\n      \"name\": \"Article\"\n    },\n    {\n      \"index\": 8,\n      \"name\": \"ModelChara\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"ModelChara\"\n      }\n    },\n    {\n      \"index\": 9,\n      \"name\": \"MoveControl\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"MoveControl\"\n      }\n    },\n    {\n      \"index\": 10,\n      \"name\": \"FlyingCondition\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"MountFlyingCondition\"\n      }\n    },\n    {\n      \"index\": 14,\n      \"name\": \"IsFlying\"\n    },\n    {\n      \"index\": 16,\n      \"name\": \"MountCustomize\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"MountCustomize\"\n      }\n    },\n    {\n      \"index\": 17,\n      \"name\": \"RideBGM\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"BGM\"\n      }\n    },\n    {\n      \"index\": 21,\n      \"name\": \"ExitMoveDist\"\n    },\n    {\n      \"index\": 22,\n      \"name\": \"ExitMoveSpeed\"\n    },\n    {\n      \"index\": 24,\n      \"name\": \"IsEmote\"\n    },\n    {\n      \"index\": 25,\n      \"name\": \"EquipHead\"\n    },\n    {\n      \"index\": 26,\n      \"name\": \"EquipBody\"\n    },\n    {\n      \"index\": 27,\n      \"name\": \"EquipLeg\"\n    },\n    {\n      \"index\": 28,\n      \"name\": \"EquipFoot\"\n    },\n    {\n      \"index\": 29,\n      \"name\": \"Order\"\n    },\n    {\n      \"index\": 30,\n      \"name\": \"Icon\",\n      \"converter\": {\n        \"type\": \"icon\"\n      }\n    },\n    {\n      \"index\": 31,\n      \"name\": \"UIPriority\"\n    },\n    {\n      \"index\": 32,\n      \"name\": \"RadiusRate\"\n    },\n    {\n      \"index\": 33,\n      \"name\": \"BaseMotionSpeed_Run\"\n    },\n    {\n      \"index\": 34,\n      \"name\": \"BaseMotionSpeed_Walk\"\n    },\n    {\n      \"index\": 36,\n      \"name\": \"UnderwaterAnimationSpeed\"\n    },\n    {\n      \"index\": 37,\n      \"name\": \"ExtraSeats\"\n    },\n    {\n      \"index\": 38,\n      \"name\": \"MountAction\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"MountAction\"\n      }\n    },\n    {\n      \"index\": 39,\n      \"name\": \"IsAirborne\"\n    },\n    {\n      \"index\": 40,\n      \"name\": \"ExHotbarEnableConfig\"\n    },\n    {\n      \"index\": 41,\n      \"name\": \"UseEP\"\n    },\n    {\n      \"index\": 43,\n      \"name\": \"IsImmobile\"\n    },\n    {\n      \"index\": 47,\n      \"name\": \"HideHeadgear\"\n    },\n    {\n      \"index\": 48,\n      \"name\": \"TiltGround\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"TiltParam\"\n      }\n    },\n    {\n      \"index\": 49,\n      \"name\": \"TiltAir/Swim\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"TiltParam\"\n      }\n    },\n    {\n      \"index\": 50,\n      \"name\": \"TiltParam3\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"TiltParam\"\n      }\n    },\n    {\n      \"index\": 52,\n      \"name\": \"TiltParam4\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"TiltParam\"\n      }\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/MountAction.json",
    "content": "{\n  \"sheet\": \"MountAction\",\n  \"definitions\": [\n    {\n      \"type\": \"repeat\",\n      \"count\": 6,\n      \"definition\": {\n        \"name\": \"Action\",\n        \"converter\": {\n          \"type\": \"link\",\n          \"target\": \"Action\"\n        }\n      }\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/MountCustomize.json",
    "content": "{\n  \"sheet\": \"MountCustomize\",\n  \"definitions\": [\n    {\n      \"index\": 1,\n      \"name\": \"HyurMidlanderMaleScale\"\n    },\n    {\n      \"index\": 2,\n      \"name\": \"HyurMidlanderFemaleScale\"\n    },\n    {\n      \"index\": 3,\n      \"name\": \"HyurHighlanderMaleScale\"\n    },\n    {\n      \"index\": 4,\n      \"name\": \"HyurHighlanderFemaleScale\"\n    },\n    {\n      \"index\": 5,\n      \"name\": \"ElezenMaleScale\"\n    },\n    {\n      \"index\": 6,\n      \"name\": \"ElezenFemaleScale\"\n    },\n    {\n      \"index\": 7,\n      \"name\": \"LalaMaleScale\"\n    },\n    {\n      \"index\": 8,\n      \"name\": \"LalaFemaleScale\"\n    },\n    {\n      \"index\": 9,\n      \"name\": \"MiqoMaleScale\"\n    },\n    {\n      \"index\": 10,\n      \"name\": \"MiqoFemaleScale\"\n    },\n    {\n      \"index\": 11,\n      \"name\": \"RoeMaleScale\"\n    },\n    {\n      \"index\": 12,\n      \"name\": \"RoeFemaleScale\"\n    },\n    {\n      \"index\": 13,\n      \"name\": \"AuRaMaleScale\"\n    },\n    {\n      \"index\": 14,\n      \"name\": \"AuRaFemaleScale\"\n    },\n    {\n      \"index\": 15,\n      \"name\": \"HrothgarMaleScale\"\n    },\n    {\n      \"index\": 16,\n      \"name\": \"HrothgarFemaleScale\"\n    },\n    {\n      \"index\": 17,\n      \"name\": \"VieraMaleScale\"\n    },\n    {\n      \"index\": 18,\n      \"name\": \"VieraFemaleScale\"\n    },\n    {\n      \"index\": 19,\n      \"name\": \"HyurMidlanderMaleCameraHeight\"\n    },\n    {\n      \"index\": 20,\n      \"name\": \"HyurMidlanderFemaleCameraHeight\"\n    },\n    {\n      \"index\": 21,\n      \"name\": \"HyurHighlanderMaleCameraHeight\"\n    },\n    {\n      \"index\": 22,\n      \"name\": \"HyurHighlanderFemaleCameraHeight\"\n    },\n    {\n      \"index\": 23,\n      \"name\": \"ElezenMaleCameraHeight\"\n    },\n    {\n      \"index\": 24,\n      \"name\": \"ElezenFemaleCameraHeight\"\n    },\n    {\n      \"index\": 25,\n      \"name\": \"LalaMaleCameraHeight\"\n    },\n    {\n      \"index\": 26,\n      \"name\": \"LalaFemaleCameraHeight\"\n    },\n    {\n      \"index\": 27,\n      \"name\": \"MiqoMaleCameraHeight\"\n    },\n    {\n      \"index\": 28,\n      \"name\": \"MiqoFemaleCameraHeight\"\n    },\n    {\n      \"index\": 29,\n      \"name\": \"RoeMaleCameraHeight\"\n    },\n    {\n      \"index\": 30,\n      \"name\": \"RoeFemaleCameraHeight\"\n    },\n    {\n      \"index\": 31,\n      \"name\": \"AuRaMaleCameraHeight\"\n    },\n    {\n      \"index\": 32,\n      \"name\": \"AuRaFemaleCameraHeight\"\n    },\n    {\n      \"index\": 33,\n      \"name\": \"HrothgarMaleCameraHeight\"\n    },\n    {\n      \"index\": 34,\n      \"name\": \"VieraMaleCameraHeight\"\n    },\n    {\n      \"index\": 35,\n      \"name\": \"VieraFemaleCameraHeight\"\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/MountFlyingCondition.json",
    "content": "{\n  \"sheet\": \"MountFlyingCondition\",\n  \"defaultColumn\": \"Quest\",\n  \"definitions\": [\n    {\n      \"name\": \"Quest\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"Quest\"\n      }\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/MountSpeed.json",
    "content": "{\n  \"sheet\": \"MountSpeed\",\n  \"defaultColumn\": \"Quest\",\n  \"definitions\": [\n    {\n      \"name\": \"Quest\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"Quest\"\n      }\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/MountTransient.json",
    "content": "{\n  \"sheet\": \"MountTransient\",\n  \"definitions\": [\n    {\n      \"name\": \"Description\"\n    },\n    {\n      \"index\": 1,\n      \"name\": \"Description{Enhanced}\"\n    },\n    {\n      \"index\": 2,\n      \"name\": \"Tooltip\"\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/MoveTimeline.json",
    "content": "{\n  \"sheet\": \"MoveTimeline\",\n  \"defaultColumn\": \"Idle\",\n  \"definitions\": [\n    {\n      \"name\": \"Idle\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"ActionTimeline\"\n      }\n    },\n    {\n      \"index\": 1,\n      \"name\": \"MoveForward\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"ActionTimeline\"\n      }\n    },\n    {\n      \"index\": 2,\n      \"name\": \"MoveBack\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"ActionTimeline\"\n      }\n    },\n    {\n      \"index\": 3,\n      \"name\": \"MoveLeft\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"ActionTimeline\"\n      }\n    },\n    {\n      \"index\": 4,\n      \"name\": \"MoveRight\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"ActionTimeline\"\n      }\n    },\n    {\n      \"index\": 5,\n      \"name\": \"MoveUp\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"ActionTimeline\"\n      }\n    },\n    {\n      \"index\": 6,\n      \"name\": \"MoveDown\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"ActionTimeline\"\n      }\n    },\n    {\n      \"index\": 7,\n      \"name\": \"MoveTurnLeft\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"ActionTimeline\"\n      }\n    },\n    {\n      \"index\": 8,\n      \"name\": \"MoveTurnRight\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"ActionTimeline\"\n      }\n    },\n    {\n      \"index\": 9,\n      \"name\": \"Extra\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"ActionTimeline\"\n      }\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/MoveVfx.json",
    "content": "{\n  \"sheet\": \"MoveVfx\",\n  \"defaultColumn\": \"VFXNormal\",\n  \"definitions\": [\n    {\n      \"name\": \"VFXNormal\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"VFX\"\n      }\n    },\n    {\n      \"index\": 1,\n      \"name\": \"VFXWalking\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"VFX\"\n      }\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/MovieStaffList.json",
    "content": "{\n  \"sheet\": \"MovieStaffList\",\n  \"definitions\": [\n    {\n      \"name\": \"Image\",\n      \"converter\": {\n        \"type\": \"icon\"\n      }\n    },\n    {\n      \"index\": 1,\n      \"name\": \"StartTime\"\n    },\n    {\n      \"index\": 2,\n      \"name\": \"EndTime\"\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/MovieSubtitle.json",
    "content": "{\n  \"sheet\": \"MovieSubtitle\",\n  \"definitions\": [\n    {\n      \"name\": \"StartTime\"\n    },\n    {\n      \"index\": 1,\n      \"name\": \"EndTime\"\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/MovieSubtitle500.json",
    "content": "{\n  \"sheet\": \"MovieSubtitle500\",\n  \"definitions\": [\n    {\n      \"name\": \"StartTime\"\n    },\n    {\n      \"index\": 1,\n      \"name\": \"EndTime\"\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/MovieSubtitleVoyage.json",
    "content": "{\n  \"sheet\": \"MovieSubtitleVoyage\",\n  \"definitions\": [\n    {\n      \"name\": \"StartTime\"\n    },\n    {\n      \"index\": 1,\n      \"name\": \"EndTime\"\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/MultipleHelp.json",
    "content": "{\n  \"sheet\": \"MultipleHelp\",\n  \"definitions\": []\n}"
  },
  {
    "path": "SaintCoinach/Definitions/MultipleHelpPage.json",
    "content": "{\n  \"sheet\": \"MultipleHelpPage\",\n  \"definitions\": []\n}"
  },
  {
    "path": "SaintCoinach/Definitions/MultipleHelpString.json",
    "content": "{\n  \"sheet\": \"MultipleHelpString\",\n  \"definitions\": []\n}"
  },
  {
    "path": "SaintCoinach/Definitions/NotebookDivision.json",
    "content": "{\n  \"sheet\": \"NotebookDivision\",\n  \"defaultColumn\": \"Name\",\n  \"definitions\": [\n    {\n      \"name\": \"Name\"\n    },\n    {\n      \"index\": 1,\n      \"name\": \"NotebookDivisionCategory\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"NotebookDivisionCategory\"\n      }\n    },\n    {\n      \"index\": 2,\n      \"name\": \"CraftOpeningLevel\"\n    },\n    {\n      \"index\": 3,\n      \"name\": \"GatheringOpeningLevel\"\n    },\n    {\n      \"index\": 4,\n      \"name\": \"QuestUnlock\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"Quest\"\n      }\n    },\n    {\n      \"index\": 7,\n      \"name\": \"CRPCraft\"\n    },\n    {\n      \"index\": 8,\n      \"name\": \"BSMCraft\"\n    },\n    {\n      \"index\": 9,\n      \"name\": \"ARMCraft\"\n    },\n    {\n      \"index\": 10,\n      \"name\": \"GSMCraft\"\n    },\n    {\n      \"index\": 11,\n      \"name\": \"LTWCraft\"\n    },\n    {\n      \"index\": 12,\n      \"name\": \"WVRCraft\"\n    },\n    {\n      \"index\": 13,\n      \"name\": \"ALCCraft\"\n    },\n    {\n      \"index\": 14,\n      \"name\": \"CULCraft\"\n    }\n  ]\n}\n"
  },
  {
    "path": "SaintCoinach/Definitions/NotebookDivisionCategory.json",
    "content": "{\n  \"sheet\": \"NotebookDivisionCategory\",\n  \"defaultColumn\": \"Name\",\n  \"definitions\": [\n    {\n      \"name\": \"Name\"\n    },\n    {\n      \"index\": 1,\n      \"name\": \"Index\"\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/NotoriousMonster.json",
    "content": "{\n  \"sheet\": \"NotoriousMonster\",\n  \"defaultColumn\": \"BNpcName\",\n  \"definitions\": [\n    {\n      \"name\": \"BNpcBase\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"BNpcBase\"\n      }\n    },\n    {\n      \"index\": 1,\n      \"name\": \"Rank\"\n    },\n    {\n      \"index\": 2,\n      \"name\": \"BNpcName\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"BNpcName\"\n      }\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/NotoriousMonsterTerritory.json",
    "content": "{\n  \"sheet\": \"NotoriousMonsterTerritory\",\n  \"definitions\": [\n    {\n      \"type\": \"repeat\",\n      \"count\": 10,\n      \"definition\": {\n        \"name\": \"NotoriousMonsters\",\n        \"converter\": {\n          \"type\": \"link\",\n          \"target\": \"NotoriousMonster\"\n        }\n      }\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/NpcEquip.json",
    "content": "{\n  \"sheet\": \"NpcEquip\",\n  \"definitions\": [\n    {\n      \"name\": \"Model{MainHand}\"\n    },\n    {\n      \"index\": 1,\n      \"name\": \"Dye{MainHand}\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"Stain\"\n      }\n    },\n    {\n      \"index\": 2,\n      \"name\": \"Dye2{MainHand}\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"Stain\"\n      }\n    },\n    {\n      \"index\": 3,\n      \"name\": \"Model{OffHand}\"\n    },\n    {\n      \"index\": 4,\n      \"name\": \"Dye{OffHand}\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"Stain\"\n      }\n    },\n    {\n      \"index\": 5,\n      \"name\": \"Dye2{OffHand}\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"Stain\"\n      }\n    },\n    {\n      \"index\": 6,\n      \"name\": \"Model{Head}\"\n    },\n    {\n      \"index\": 7,\n      \"name\": \"Dye{Head}\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"Stain\"\n      }\n    },\n    {\n      \"index\": 8,\n      \"name\": \"Dye2{Head}\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"Stain\"\n      }\n    },\n    {\n      \"index\": 9,\n      \"name\": \"Visor\"\n    },\n    {\n      \"index\": 10,\n      \"name\": \"Model{Body}\"\n    },\n    {\n      \"index\": 11,\n      \"name\": \"Dye{Body}\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"Stain\"\n      }\n    },\n    {\n      \"index\": 12,\n      \"name\": \"Dye2{Body}\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"Stain\"\n      }\n    },\n    {\n      \"index\": 13,\n      \"name\": \"Model{Hands}\"\n    },\n    {\n      \"index\": 14,\n      \"name\": \"Dye{Hands}\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"Stain\"\n      }\n    },\n    {\n      \"index\": 15,\n      \"name\": \"Dye2{Hands}\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"Stain\"\n      }\n    },\n    {\n      \"index\": 16,\n      \"name\": \"Model{Legs}\"\n    },\n    {\n      \"index\": 17,\n      \"name\": \"Dye{Legs}\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"Stain\"\n      }\n    },\n    {\n      \"index\": 18,\n      \"name\": \"Dye2{Legs}\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"Stain\"\n      }\n    },\n    {\n      \"index\": 19,\n      \"name\": \"Model{Feet}\"\n    },\n    {\n      \"index\": 20,\n      \"name\": \"Dye{Feet}\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"Stain\"\n      }\n    },\n    {\n      \"index\": 21,\n      \"name\": \"Dye2{Feet}\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"Stain\"\n      }\n    },\n    {\n      \"index\": 22,\n      \"name\": \"Model{Ears}\"\n    },\n    {\n      \"index\": 23,\n      \"name\": \"Dye{Ears}\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"Stain\"\n      }\n    },\n    {\n      \"index\": 24,\n      \"name\": \"Dye2{Ears}\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"Stain\"\n      }\n    },\n    {\n      \"index\": 25,\n      \"name\": \"Model{Neck}\"\n    },\n    {\n      \"index\": 26,\n      \"name\": \"Dye{Neck}\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"Stain\"\n      }\n    },\n    {\n      \"index\": 27,\n      \"name\": \"Dye2{Neck}\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"Stain\"\n      }\n    },\n    {\n      \"index\": 28,\n      \"name\": \"Model{Wrists}\"\n    },\n    {\n      \"index\": 29,\n      \"name\": \"Dye{Wrists}\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"Stain\"\n      }\n    },\n    {\n      \"index\": 30,\n      \"name\": \"Dye2{Wrists}\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"Stain\"\n      }\n    },\n    {\n      \"index\": 31,\n      \"name\": \"Model{LeftRing}\"\n    },\n    {\n      \"index\": 32,\n      \"name\": \"Dye{LeftRing}\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"Stain\"\n      }\n    },\n    {\n      \"index\": 33,\n      \"name\": \"Dye2{LeftRing}\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"Stain\"\n      }\n    },\n    {\n      \"index\": 34,\n      \"name\": \"Model{RightRing}\"\n    },\n    {\n      \"index\": 35,\n      \"name\": \"Dye{RightRing}\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"Stain\"\n      }\n    },\n    {\n      \"index\": 36,\n      \"name\": \"Dye2{RightRing}\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"Stain\"\n      }\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/NpcYell.json",
    "content": "{\n  \"sheet\": \"NpcYell\",\n  \"defaultColumn\": \"Text\",\n  \"definitions\": [\n    {\n      \"index\": 4,\n      \"name\": \"OutputType\"\n    },\n    {\n      \"index\": 6,\n      \"name\": \"BalloonTime\"\n    },\n    {\n      \"index\": 7,\n      \"name\": \"IsBalloonSlow\"\n    },\n    {\n      \"index\": 8,\n      \"name\": \"BattleTalkTime\"\n    },\n    {\n      \"index\": 11,\n      \"name\": \"Text\"\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/Omen.json",
    "content": "{\n  \"sheet\": \"Omen\",\n  \"defaultColumn\": \"Path\",\n  \"definitions\": [\n    {\n      \"name\": \"Path\"\n    },\n    {\n      \"index\": 1,\n      \"name\": \"PathAlly\"\n    },\n    {\n      \"index\": 2,\n      \"name\": \"Type\"\n    },\n    {\n      \"index\": 3,\n      \"name\": \"RestrictYScale\"\n    },\n    {\n      \"index\": 4,\n      \"name\": \"LargeScale\"\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/Omikuji.json",
    "content": "{\n  \"sheet\": \"Omikuji\",\n  \"definitions\": []\n}"
  },
  {
    "path": "SaintCoinach/Definitions/OmikujiGuidance.json",
    "content": "{\n  \"sheet\": \"OmikujiGuidance\",\n  \"definitions\": []\n}"
  },
  {
    "path": "SaintCoinach/Definitions/OnlineStatus.json",
    "content": "{\n  \"sheet\": \"OnlineStatus\",\n  \"defaultColumn\": \"Name\",\n  \"definitions\": [\n    {\n      \"index\": 1,\n      \"name\": \"List\"\n    },\n    {\n      \"index\": 3,\n      \"name\": \"Priority\"\n    },\n    {\n      \"index\": 4,\n      \"name\": \"Icon\",\n      \"converter\": {\n        \"type\": \"icon\"\n      }\n    },\n    {\n      \"index\": 6,\n      \"name\": \"Name\"\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/OpenContent.json",
    "content": "{\n  \"sheet\": \"OpenContent\",\n  \"definitions\": [\n    {\n      \"type\": \"repeat\",\n      \"count\": 16,\n      \"definition\": {\n        \"name\": \"Content\",\n        \"converter\": {\n          \"type\": \"link\",\n          \"target\": \"ContentFinderCondition\"\n        }\n      }\n    },\n    {\n      \"index\": 16,\n      \"type\": \"repeat\",\n      \"count\": 16,\n      \"definition\": {\n        \"name\": \"CandidateName\",\n        \"converter\": {\n          \"type\": \"link\",\n          \"target\": \"OpenContentCandidateName\"\n        }\n      }\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/OpenContentCandidateName.json",
    "content": "{\n  \"sheet\": \"OpenContentCandidateName\",\n  \"defaultColumn\": \"Name\",\n  \"definitions\": [\n    {\n      \"name\": \"Name\"\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/Opening.json",
    "content": "{\n  \"sheet\": \"Opening\",\n  \"defaultColumn\": \"Name\",\n  \"definitions\": [\n    {\n      \"name\": \"Name\"\n    },\n    {\n      \"index\": 1,\n      \"name\": \"Quest\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"Quest\"\n      }\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/Orchestrion.json",
    "content": "{\n  \"sheet\": \"Orchestrion\",\n  \"definitions\": [\n    {\n      \"name\": \"Name\"\n    },\n    {\n      \"index\": 1,\n      \"name\": \"Description\"\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/OrchestrionCategory.json",
    "content": "{\n  \"sheet\": \"OrchestrionCategory\",\n  \"defaultColumn\": \"Name\",\n  \"definitions\": [\n    {\n      \"name\": \"Name\"\n    },\n    {\n      \"index\": 1,\n      \"name\": \"HideOrder\"\n    },\n    {\n      \"index\": 2,\n      \"name\": \"Icon\",\n      \"converter\": {\n        \"type\": \"icon\"\n      }\n    },\n    {\n      \"index\": 3,\n      \"name\": \"Order\"\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/OrchestrionPath.json",
    "content": "{\n  \"sheet\": \"OrchestrionPath\",\n  \"definitions\": [\n    {\n      \"name\": \"File\"\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/OrchestrionUiparam.json",
    "content": "{\n  \"sheet\": \"OrchestrionUiparam\",\n  \"definitions\": [\n    {\n      \"name\": \"OrchestrionCategory\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"OrchestrionCategory\"\n      }\n    },\n    {\n      \"index\": 1,\n      \"name\": \"Order\"\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/Ornament.json",
    "content": "{\n  \"sheet\": \"Ornament\",\n  \"defaultColumn\": \"Singular\",\n  \"definitions\": [\n    {\n      \"name\": \"Model\"\n    },\n    {\n      \"index\": 5,\n      \"name\": \"Order\"\n    },\n    {\n      \"index\": 6,\n      \"name\": \"Icon\",\n      \"converter\": {\n        \"type\": \"icon\"\n      }\n    },\n    {\n      \"index\": 7,\n      \"name\": \"Transient\"\n    },\n    {\n      \"index\": 8,\n      \"name\": \"Singular\"\n    },\n    {\n      \"index\": 9,\n      \"name\": \"Adjective\"\n    },\n    {\n      \"index\": 10,\n      \"name\": \"Plural\"\n    },\n    {\n      \"index\": 11,\n      \"name\": \"PossessivePronoun\"\n    },\n    {\n      \"index\": 12,\n      \"name\": \"StartsWithVowel\"\n    },\n    {\n      \"index\": 14,\n      \"name\": \"Pronoun\"\n    },\n    {\n      \"index\": 15,\n      \"name\": \"Article\"\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/OrnamentAction.json",
    "content": "{\n  \"sheet\": \"OrnamentAction\",\n  \"definitions\": []\n}"
  },
  {
    "path": "SaintCoinach/Definitions/ParamGrow.json",
    "content": "{\n  \"sheet\": \"ParamGrow\",\n  \"definitions\": [\n    {\n      \"name\": \"ExpToNext\"\n    },\n    {\n      \"index\": 1,\n      \"name\": \"AdditionalActions\"\n    },\n    {\n      \"index\": 2,\n      \"name\": \"ApplyAction\"\n    },\n    {\n      \"index\": 3,\n      \"name\": \"ScaledQuestXP\"\n    },\n    {\n      \"index\": 4,\n      \"name\": \"MpModifier\"\n    },\n    {\n      \"index\": 5,\n      \"name\": \"BaseSpeed\"\n    },\n    {\n      \"index\": 6,\n      \"name\": \"LevelModifier\"\n    },\n    {\n      \"index\": 7,\n      \"name\": \"QuestExpModifier\"\n    },\n    {\n      \"index\": 8,\n      \"name\": \"HpModifier\"\n    },\n    {\n      \"index\": 9,\n      \"name\": \"HuntingLogExpReward\"\n    },\n    {\n      \"index\": 10,\n      \"name\": \"MonsterNoteSeals\"\n    },\n    {\n      \"index\": 11,\n      \"name\": \"ItemLevelSync\"\n    },\n    {\n      \"index\": 12,\n      \"name\": \"ProperDungeon\"\n    },\n    {\n      \"index\": 13,\n      \"name\": \"ProperGuildOrder\"\n    },\n    {\n      \"index\": 14,\n      \"name\": \"CraftingLevel\"\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/PartyContent.json",
    "content": "{\n  \"sheet\": \"PartyContent\",\n  \"defaultColumn\": \"ContentFinderCondition\",\n  \"definitions\": [\n    {\n      \"name\": \"Key\"\n    },\n    {\n      \"index\": 1,\n      \"name\": \"TimeLimit\"\n    },\n    {\n      \"index\": 2,\n      \"name\": \"Name\"\n    },\n    {\n      \"index\": 3,\n      \"name\": \"TextData{Start}\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"PartyContentTextData\"\n      }\n    },\n    {\n      \"index\": 4,\n      \"name\": \"TextData{End}\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"PartyContentTextData\"\n      }\n    },\n    {\n      \"index\": 5,\n      \"type\": \"repeat\",\n      \"count\": 9,\n      \"definition\": {\n        \"name\": \"LGB[EventObject][0]\"\n      }\n    },\n    {\n      \"index\": 14,\n      \"type\": \"repeat\",\n      \"count\": 9,\n      \"definition\": {\n        \"name\": \"LGB[EventRange]\"\n      }\n    },\n    {\n      \"index\": 23,\n      \"type\": \"repeat\",\n      \"count\": 9,\n      \"definition\": {\n        \"name\": \"LGB[EventObject][1]\"\n      }\n    },\n    {\n      \"index\": 33,\n      \"name\": \"ContentFinderCondition\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"ContentFinderCondition\"\n      }\n    },\n    {\n      \"index\": 34,\n      \"name\": \"Image\",\n      \"converter\": {\n        \"type\": \"icon\"\n      }\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/PartyContentCutscene.json",
    "content": "{\n  \"sheet\": \"PartyContentCutscene\",\n  \"defaultColumn\": \"Name\",\n  \"definitions\": [\n    {\n      \"name\": \"Cutscene\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"Cutscene\"\n      }\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/PartyContentTextData.json",
    "content": "{\n  \"sheet\": \"PartyContentTextData\",\n  \"defaultColumn\": \"Data\",\n  \"definitions\": [\n    {\n      \"name\": \"Data\"\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/PatchMark.json",
    "content": "{\n  \"sheet\": \"PatchMark\",\n  \"definitions\": [\n    {\n      \"name\": \"Category\"\n    },\n    {\n      \"index\": 1,\n      \"name\": \"SubCategoryType\"\n    },\n    {\n      \"index\": 2,\n      \"name\": \"SubCategory\"\n    },\n    {\n      \"index\": 5,\n      \"name\": \"MarkID\"\n    },\n    {\n      \"index\": 6,\n      \"name\": \"Version\"\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/Perform.json",
    "content": "{\n  \"sheet\": \"Perform\",\n  \"defaultColumn\": \"Name\",\n  \"definitions\": [\n    {\n      \"name\": \"Name\"\n    },\n    {\n      \"index\": 2,\n      \"name\": \"ModelKey\"\n    },\n    {\n      \"index\": 3,\n      \"name\": \"Animation{Start}\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"ActionTimeline\"\n      }\n    },\n    {\n      \"index\": 4,\n      \"name\": \"Animation{End}\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"ActionTimeline\"\n      }\n    },\n    {\n      \"index\": 5,\n      \"name\": \"Animation{Idle}\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"ActionTimeline\"\n      }\n    },\n    {\n      \"index\": 6,\n      \"name\": \"Animation{Play01}\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"ActionTimeline\"\n      }\n    },\n    {\n      \"index\": 7,\n      \"name\": \"Animation{Play02}\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"ActionTimeline\"\n      }\n    },\n    {\n      \"index\": 8,\n      \"name\": \"StopAnimation\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"ActionTimeline\"\n      }\n    },\n    {\n      \"index\": 9,\n      \"name\": \"Instrument\"\n    },\n    {\n      \"index\": 10,\n      \"name\": \"Order\"\n    },\n    {\n      \"index\": 11,\n      \"name\": \"Transient\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"PerformTransient\"\n      }\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/PerformGroup.json",
    "content": "{\n  \"sheet\": \"PerformGroup\",\n  \"definitions\": [\n    {\n      \"type\": \"repeat\",\n      \"count\": 5,\n      \"definition\": {\n        \"name\": \"Perform\",\n        \"converter\": {\n          \"type\": \"link\",\n          \"target\": \"Perform\"\n        }\n      }\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/PerformTransient.json",
    "content": "{\n  \"sheet\": \"PerformTransient\",\n  \"defaultColumn\": \"Text\",\n  \"definitions\": [\n    {\n      \"name\": \"Text\"\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/Pet.json",
    "content": "{\n  \"sheet\": \"Pet\",\n  \"defaultColumn\": \"Name\",\n  \"definitions\": [\n    {\n      \"name\": \"Name\"\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/PetAction.json",
    "content": "{\n  \"sheet\": \"PetAction\",\n  \"defaultColumn\": \"Name\",\n  \"definitions\": [\n    {\n      \"name\": \"Name\"\n    },\n    {\n      \"index\": 1,\n      \"name\": \"Description\"\n    },\n    {\n      \"index\": 2,\n      \"name\": \"Icon\",\n      \"converter\": {\n        \"type\": \"icon\"\n      }\n    },\n    {\n      \"index\": 3,\n      \"name\": \"Action\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"Action\"\n      }\n    },\n    {\n      \"index\": 4,\n      \"name\": \"Pet\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"Pet\"\n      }\n    },\n    {\n      \"index\": 5,\n      \"name\": \"MasterOrder\"\n    },\n    {\n      \"index\": 6,\n      \"name\": \"DisableOrder\"\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/PetMirage.json",
    "content": "{\n  \"sheet\": \"PetMirage\",\n  \"defaultColumn\": \"Name\",\n  \"definitions\": [\n    {\n      \"index\": 2,\n      \"name\": \"Name\"\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/PhysicsGroup.json",
    "content": "{\n  \"sheet\": \"PhysicsGroup\",\n  \"definitions\": [\n    {\n      \"type\": \"repeat\",\n      \"count\": 6,\n      \"definition\": {\n        \"name\": \"SimulationTime\"\n      }\n    },\n    {\n      \"index\": 6,\n      \"type\": \"repeat\",\n      \"count\": 6,\n      \"definition\": {\n        \"name\": \"PS3SimulationTime\"\n      }\n    },\n    {\n      \"index\": 12,\n      \"name\": \"ResetByLookAt\"\n    },\n    {\n      \"index\": 13,\n      \"name\": \"RootFollowingGame\"\n    },\n    {\n      \"index\": 14,\n      \"name\": \"RootFollowingCutScene\"\n    },\n    {\n      \"index\": 15,\n      \"type\": \"repeat\",\n      \"count\": 3,\n      \"definition\": {\n        \"name\": \"ConfigSwitch\"\n      }\n    },\n    {\n      \"index\": 18,\n      \"name\": \"ForceAttractByPhysicsOff\"\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/PhysicsWind.json",
    "content": "{\n  \"sheet\": \"PhysicsWind\",\n  \"definitions\": [\n    {\n      \"name\": \"Threshold\"\n    },\n    {\n      \"index\": 1,\n      \"name\": \"Amplitude\"\n    },\n    {\n      \"index\": 2,\n      \"name\": \"AmplitudeFrequency\"\n    },\n    {\n      \"index\": 3,\n      \"name\": \"PowerMin\"\n    },\n    {\n      \"index\": 4,\n      \"name\": \"PowerMax\"\n    },\n    {\n      \"index\": 5,\n      \"name\": \"PowerFrequency\"\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/Picture.json",
    "content": "{\n  \"sheet\": \"Picture\",\n  \"defaultColumn\": \"Item\",\n  \"definitions\": [\n    {\n      \"name\": \"Image\",\n      \"converter\": {\n        \"type\": \"icon\"\n      }\n    },\n    {\n      \"index\": 1,\n      \"name\": \"Signature\"\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/PlaceName.json",
    "content": "{\n  \"sheet\": \"PlaceName\",\n  \"defaultColumn\": \"Name\",\n  \"definitions\": [\n    {\n      \"name\": \"Name\"\n    },\n    {\n      \"index\": 2,\n      \"name\": \"Name{NoArticle}\"\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/PlantPotFlowerSeed.json",
    "content": "{\n  \"sheet\": \"PlantPotFlowerSeed\",\n  \"definitions\": [\n    {\n      \"type\": \"repeat\",\n      \"count\": 9,\n      \"definition\": {\n        \"name\": \"SeedIcon\",\n        \"converter\": {\n          \"type\": \"icon\"\n        }\n      }\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/PlayerSearchLocation.json",
    "content": "{\n  \"sheet\": \"PlayerSearchLocation\",\n  \"definitions\": []\n}"
  },
  {
    "path": "SaintCoinach/Definitions/PlayerSearchSubLocation.json",
    "content": "{\n  \"sheet\": \"PlayerSearchSubLocation\",\n  \"definitions\": []\n}"
  },
  {
    "path": "SaintCoinach/Definitions/PreHandler.json",
    "content": "{\n  \"sheet\": \"PreHandler\",\n  \"defaultColumn\": \"Target\",\n  \"definitions\": [\n    {\n      \"index\": 1,\n      \"name\": \"Image\",\n      \"converter\": {\n        \"type\": \"icon\"\n      }\n    },\n    {\n      \"index\": 2,\n      \"name\": \"Target\",\n      \"converter\": {\n        \"type\": \"multiref\",\n        \"targets\": [\n          \"CollectablesShop\",\n          \"InclusionShop\",\n          \"GilShop\",\n          \"SpecialShop\",\n          \"Description\"\n        ]\n      }\n    },\n    {\n      \"index\": 3,\n      \"name\": \"UnlockQuest\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"Quest\"\n      }\n    },\n    {\n      \"index\": 4,\n      \"name\": \"AcceptMessage\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"DefaultTalk\"\n      }\n    },\n    {\n      \"index\": 5,\n      \"name\": \"DenyMessage\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"DefaultTalk\"\n      }\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/PresetCamera.json",
    "content": "{\n  \"sheet\": \"PresetCamera\",\n  \"definitions\": [\n    {\n      \"name\": \"EID\"\n    },\n    {\n      \"index\": 1,\n      \"name\": \"PosX\"\n    },\n    {\n      \"index\": 2,\n      \"name\": \"PosY\"\n    },\n    {\n      \"index\": 3,\n      \"name\": \"PosZ\"\n    },\n    {\n      \"index\": 4,\n      \"name\": \"Elezen\"\n    },\n    {\n      \"index\": 5,\n      \"name\": \"Lalafell\"\n    },\n    {\n      \"index\": 6,\n      \"name\": \"Miqote\"\n    },\n    {\n      \"index\": 7,\n      \"name\": \"Roe\"\n    },\n    {\n      \"index\": 8,\n      \"name\": \"Hrothgar\"\n    },\n    {\n      \"index\": 9,\n      \"name\": \"Viera\"\n    },\n    {\n      \"index\": 11,\n      \"name\": \"Hyur_F\"\n    },\n    {\n      \"index\": 12,\n      \"name\": \"Elezen_F\"\n    },\n    {\n      \"index\": 13,\n      \"name\": \"Lalafell_F\"\n    },\n    {\n      \"index\": 14,\n      \"name\": \"Miqote_F\"\n    },\n    {\n      \"index\": 15,\n      \"name\": \"Roe_F\"\n    },\n    {\n      \"index\": 16,\n      \"name\": \"Hrothgar_F\"\n    },\n    {\n      \"index\": 18,\n      \"name\": \"Viera_F\"\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/PresetCameraAdjust.json",
    "content": "{\n  \"sheet\": \"PresetCameraAdjust\",\n  \"definitions\": [\n    {\n      \"name\": \"Hyur_M\"\n    },\n    {\n      \"index\": 1,\n      \"name\": \"Hyur_F\"\n    },\n    {\n      \"index\": 2,\n      \"name\": \"Elezen_M\"\n    },\n    {\n      \"index\": 3,\n      \"name\": \"Elezen_F\"\n    },\n    {\n      \"index\": 4,\n      \"name\": \"Lalafell_M\"\n    },\n    {\n      \"index\": 5,\n      \"name\": \"Lalafell_F\"\n    },\n    {\n      \"index\": 6,\n      \"name\": \"Miqote_M\"\n    },\n    {\n      \"index\": 7,\n      \"name\": \"Miqote_F\"\n    },\n    {\n      \"index\": 8,\n      \"name\": \"Roe_M\"\n    },\n    {\n      \"index\": 9,\n      \"name\": \"Roe_F\"\n    },\n    {\n      \"index\": 10,\n      \"name\": \"Hrothgar_M\"\n    },\n    {\n      \"index\": 11,\n      \"name\": \"Hrothgar_F\"\n    },\n    {\n      \"index\": 12,\n      \"name\": \"Viera_M\"\n    },\n    {\n      \"index\": 13,\n      \"name\": \"Viera_F\"\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/PreviewableItems.json",
    "content": "{\n  \"sheet\": \"PreviewableItems\",\n  \"definitions\": []\n}"
  },
  {
    "path": "SaintCoinach/Definitions/PublicContent.json",
    "content": "{\n  \"sheet\": \"PublicContent\",\n  \"defaultColumn\": \"Name\",\n  \"definitions\": [\n    {\n      \"name\": \"Type\"\n    },\n    {\n      \"index\": 1,\n      \"name\": \"TimeLimit\"\n    },\n    {\n      \"index\": 2,\n      \"name\": \"MapIcon\",\n      \"converter\": {\n        \"type\": \"icon\"\n      }\n    },\n    {\n      \"index\": 3,\n      \"name\": \"Name\"\n    },\n    {\n      \"index\": 4,\n      \"name\": \"TextData{Start}\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"PublicContentTextData\"\n      }\n    },\n    {\n      \"index\": 5,\n      \"name\": \"TextData{End}\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"PublicContentTextData\"\n      }\n    },\n    {\n      \"index\": 6,\n      \"name\": \"StartCutscene\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"PublicContentCutscene\"\n      }\n    },\n    {\n      \"index\": 7,\n      \"name\": \"LGBEventRange\"\n    },\n    {\n      \"index\": 8,\n      \"name\": \"LGBPopRange\"\n    },\n    {\n      \"index\": 9,\n      \"name\": \"ContentFinderCondition\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"ContentFinderCondition\"\n      }\n    },\n    {\n      \"index\": 10,\n      \"name\": \"AdditionalData\",\n      \"converter\": {\n        \"type\": \"complexlink\",\n        \"links\": [\n          {\n            \"when\": {\n              \"key\": \"Type\",\n              \"value\": 3\n            },\n            \"sheet\": \"Eureka\"\n          },\n          {\n            \"when\": {\n              \"key\": \"Type\",\n              \"value\": 5\n            },\n            \"sheet\": \"GFateClimbing2Content\"\n          }\n        ]\n      }\n    },\n    {\n      \"index\": 16,\n      \"name\": \"EndCutscene\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"PublicContentCutscene\"\n      }\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/PublicContentCutscene.json",
    "content": "{\n  \"sheet\": \"PublicContentCutscene\",\n  \"definitions\": [\n    {\n      \"name\": \"Cutscene\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"Cutscene\"\n      }\n    },\n    {\n      \"index\": 1,\n      \"name\": \"Cutscene2\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"Cutscene\"\n      }\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/PublicContentTextData.json",
    "content": "{\n  \"sheet\": \"PublicContentTextData\",\n  \"defaultColumn\": \"TextData\",\n  \"definitions\": [\n    {\n      \"name\": \"TextData\"\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/PvPAction.json",
    "content": "{\n  \"sheet\": \"PvPAction\",\n  \"defaultColumn\": \"Action\",\n  \"definitions\": [\n    {\n      \"name\": \"Action\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"Action\"\n      }\n    },\n    {\n      \"index\": 5,\n      \"type\": \"repeat\",\n      \"count\": 3,\n      \"definition\": {\n        \"name\": \"GrandCompany\"\n      }\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/PvPActionSort.json",
    "content": "{\n  \"sheet\": \"PvPActionSort\",\n  \"defaultColumn\": \"Name\",\n  \"definitions\": [\n    {\n      \"name\": \"Action{Type}\"\n    },\n    {\n      \"index\": 1,\n      \"name\": \"Action\",\n      \"converter\": {\n        \"type\": \"complexlink\",\n        \"links\": [\n          {\n            \"when\": {\n              \"key\": \"Action{Type}\",\n              \"value\": 1\n            },\n            \"sheet\": \"Action\"\n          },\n          {\n            \"when\": {\n              \"key\": \"Action{Type}\",\n              \"value\": 2\n            },\n            \"sheet\": \"ActionComboRoute\"\n          }\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "SaintCoinach/Definitions/PvPBaseParamValue.json",
    "content": "{\n  \"sheet\": \"PvPBaseParamValue\",\n  \"definitions\": []\n}"
  },
  {
    "path": "SaintCoinach/Definitions/PvPRank.json",
    "content": "{\n  \"sheet\": \"PvPRank\",\n  \"definitions\": [\n    {\n      \"name\": \"ExpRequired\"\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/PvPSelectTrait.json",
    "content": "{\n  \"sheet\": \"PvPSelectTrait\",\n  \"definitions\": [\n    {\n      \"name\": \"Effect\"\n    },\n    {\n      \"index\": 1,\n      \"name\": \"Icon\",\n      \"converter\": {\n        \"type\": \"icon\"\n      }\n    },\n    {\n      \"index\": 2,\n      \"name\": \"Value\"\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/PvPSeries.json",
    "content": "{\n  \"sheet\": \"PvPSeries\",\n  \"definitions\": [\n    {\n      \"index\": 1,\n      \"type\": \"repeat\",\n      \"count\": 2,\n      \"definition\": {\n        \"type\": \"group\",\n        \"members\": [\n          {\n            \"type\": \"repeat\",\n            \"count\": 32,\n            \"definition\": {\n              \"name\": \"LevelRewardItem\",\n              \"converter\": {\n                \"type\": \"link\",\n                \"target\": \"Item\"\n              }\n            }\n          }\n        ]\n      }\n    },\n    {\n      \"index\": 65,\n      \"type\": \"repeat\",\n      \"count\": 2,\n      \"definition\": {\n        \"type\": \"group\",\n        \"members\": [\n          {\n            \"type\": \"repeat\",\n            \"count\": 32,\n            \"definition\": {\n              \"name\": \"LevelRewardCount\"\n            }\n          }\n        ]\n      }\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/PvPSeriesLevel.json",
    "content": "{\n  \"sheet\": \"PvPSeriesLevel\",\n  \"definitions\": []\n}"
  },
  {
    "path": "SaintCoinach/Definitions/PvPTrait.json",
    "content": "{\n  \"sheet\": \"PvPTrait\",\n  \"definitions\": [\n    {\n      \"name\": \"Trait{1}\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"Trait\"\n      }\n    },\n    {\n      \"index\": 1,\n      \"name\": \"Trait{2}\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"Trait\"\n      }\n    },\n    {\n      \"index\": 2,\n      \"name\": \"Trait{3}\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"Trait\"\n      }\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/Quest.json",
    "content": "{\n  \"sheet\": \"Quest\",\n  \"defaultColumn\": \"Name\",\n  \"isGenericReferenceTarget\": true,\n  \"definitions\": [\n    {\n      \"name\": \"Name\"\n    },\n    {\n      \"index\": 1,\n      \"name\": \"Id\"\n    },\n    {\n      \"index\": 2,\n      \"name\": \"Expansion\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"ExVersion\"\n      }\n    },\n    {\n      \"index\": 3,\n      \"name\": \"ClassJobCategory[0]\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"ClassJobCategory\"\n      }\n    },\n    {\n      \"index\": 4,\n      \"name\": \"ClassJobLevel[0]\"\n    },\n    {\n      \"index\": 5,\n      \"name\": \"QuestLevelOffset\"\n    },\n    {\n      \"index\": 6,\n      \"name\": \"ClassJobCategory[1]\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"ClassJobCategory\"\n      }\n    },\n    {\n      \"index\": 7,\n      \"name\": \"ClassJobLevel[1]\"\n    },\n    {\n      \"index\": 8,\n      \"name\": \"PreviousQuestJoin\"\n    },\n    {\n      \"index\": 9,\n      \"name\": \"PreviousQuest\",\n      \"type\": \"repeat\",\n      \"count\": 4,\n      \"definition\": {\n        \"name\": \"PreviousQuest\",\n        \"converter\": {\n          \"type\": \"link\",\n          \"target\": \"Quest\"\n        }\n      }\n    },\n    {\n      \"index\": 13,\n      \"name\": \"QuestLockJoin\"\n    },\n    {\n      \"index\": 14,\n      \"type\": \"repeat\",\n      \"count\": 2,\n      \"definition\": {\n        \"name\": \"QuestLock\",\n        \"converter\": {\n          \"type\": \"link\",\n          \"target\": \"Quest\"\n        }\n      }\n    },\n    {\n      \"index\": 16,\n      \"name\": \"Header\"\n    },\n    {\n      \"index\": 19,\n      \"name\": \"ClassJob{Unlock}\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"ClassJob\"\n      }\n    },\n    {\n      \"index\": 20,\n      \"name\": \"GrandCompany\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"GrandCompany\"\n      }\n    },\n    {\n      \"index\": 21,\n      \"name\": \"GrandCompanyRank\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"GrandCompanyRank\"\n      }\n    },\n    {\n      \"index\": 22,\n      \"name\": \"InstanceContentJoin\"\n    },\n    {\n      \"index\": 23,\n      \"type\": \"repeat\",\n      \"count\": 3,\n      \"definition\": {\n        \"name\": \"InstanceContent\",\n        \"converter\": {\n          \"type\": \"link\",\n          \"target\": \"InstanceContent\"\n        }\n      }\n    },\n    {\n      \"index\": 26,\n      \"name\": \"Festival\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"Festival\"\n      }\n    },\n    {\n      \"index\": 27,\n      \"name\": \"FestivalBegin\"\n    },\n    {\n      \"index\": 28,\n      \"name\": \"FestivalEnd\"\n    },\n    {\n      \"index\": 29,\n      \"name\": \"Bell{Start}\"\n    },\n    {\n      \"index\": 30,\n      \"name\": \"Bell{End}\"\n    },\n    {\n      \"index\": 31,\n      \"name\": \"BeastTribe\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"BeastTribe\"\n      }\n    },\n    {\n      \"index\": 32,\n      \"name\": \"BeastReputationRank\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"BeastReputationRank\"\n      }\n    },\n    {\n      \"index\": 33,\n      \"name\": \"BeastReputationValue\"\n    },\n    {\n      \"index\": 34,\n      \"name\": \"SatisfactionNpc\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"SatisfactionNpc\"\n      }\n    },\n    {\n      \"index\": 35,\n      \"name\": \"SatisfactionLevel\"\n    },\n    {\n      \"index\": 36,\n      \"name\": \"Mount{Required}\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"Mount\"\n      }\n    },\n    {\n      \"index\": 37,\n      \"name\": \"IsHouseRequired\"\n    },\n    {\n      \"index\": 38,\n      \"name\": \"DeliveryQuest\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"DeliveryQuest\"\n      }\n    },\n    {\n      \"index\": 39,\n      \"name\": \"Issuer{Start}\",\n      \"converter\": {\n        \"type\": \"multiref\",\n        \"targets\": [\n          \"EObjName\",\n          \"ENpcResident\"\n        ]\n      }\n    },\n    {\n      \"index\": 40,\n      \"name\": \"Issuer{Location}\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"Level\"\n      }\n    },\n    {\n      \"index\": 41,\n      \"name\": \"ClientBehavior\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"Behavior\"\n      }\n    },\n    {\n      \"index\": 42,\n      \"name\": \"Target{End}\",\n      \"converter\": {\n        \"type\": \"multiref\",\n        \"targets\": [\n          \"EObjName\",\n          \"ENpcResident\"\n        ]\n      }\n    },\n    {\n      \"index\": 43,\n      \"name\": \"IsRepeatable\"\n    },\n    {\n      \"index\": 44,\n      \"name\": \"RepeatIntervalType\"\n    },\n    {\n      \"index\": 45,\n      \"name\": \"QuestRepeatFlag\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"QuestRepeatFlag\"\n      }\n    },\n    {\n      \"index\": 46,\n      \"name\": \"CanCancel\"\n    },\n    {\n      \"index\": 47,\n      \"name\": \"Type\"\n    },\n    {\n      \"index\": 48,\n      \"name\": \"QuestClassJobSupply\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"QuestClassJobSupply\"\n      }\n    },\n    {\n      \"index\": 50,\n      \"type\": \"repeat\",\n      \"count\": 50,\n      \"definition\": {\n        \"name\": \"Script{Instruction}\"\n      }\n    },\n    {\n      \"index\": 100,\n      \"type\": \"repeat\",\n      \"count\": 50,\n      \"definition\": {\n        \"name\": \"Script{Arg}\"\n      }\n    },\n    {\n      \"index\": 150,\n      \"type\": \"repeat\",\n      \"count\": 64,\n      \"definition\": {\n        \"name\": \"ActorSpawnSeq\"\n      }\n    },\n    {\n      \"index\": 214,\n      \"type\": \"repeat\",\n      \"count\": 64,\n      \"definition\": {\n        \"name\": \"ActorDespawnSeq\"\n      }\n    },\n    {\n      \"index\": 278,\n      \"type\": \"repeat\",\n      \"count\": 64,\n      \"definition\": {\n        \"name\": \"Listener\"\n      }\n    },\n    {\n      \"index\": 342,\n      \"type\": \"repeat\",\n      \"count\": 32,\n      \"definition\": {\n        \"name\": \"QuestUInt8A\"\n      }\n    },\n    {\n      \"index\": 374,\n      \"type\": \"repeat\",\n      \"count\": 32,\n      \"definition\": {\n        \"name\": \"QuestUInt8B\"\n      }\n    },\n    {\n      \"index\": 406,\n      \"type\": \"repeat\",\n      \"count\": 64,\n      \"definition\": {\n        \"name\": \"ConditionType\"\n      }\n    },\n    {\n      \"index\": 470,\n      \"type\": \"repeat\",\n      \"count\": 64,\n      \"definition\": {\n        \"name\": \"ConditionValue\"\n      }\n    },\n    {\n      \"index\": 534,\n      \"type\": \"repeat\",\n      \"count\": 64,\n      \"definition\": {\n        \"name\": \"ConditionOperator\"\n      }\n    },\n    {\n      \"index\": 598,\n      \"type\": \"repeat\",\n      \"count\": 64,\n      \"definition\": {\n        \"name\": \"Behavior\"\n      }\n    },\n    {\n      \"index\": 662,\n      \"type\": \"repeat\",\n      \"count\": 64,\n      \"definition\": {\n        \"name\": \"VisibleBool\"\n      }\n    },\n    {\n      \"index\": 726,\n      \"type\": \"repeat\",\n      \"count\": 64,\n      \"definition\": {\n        \"name\": \"ConditionBool\"\n      }\n    },\n    {\n      \"index\": 790,\n      \"type\": \"repeat\",\n      \"count\": 64,\n      \"definition\": {\n        \"name\": \"ItemBool\"\n      }\n    },\n    {\n      \"index\": 854,\n      \"type\": \"repeat\",\n      \"count\": 64,\n      \"definition\": {\n        \"name\": \"AnnounceBool\"\n      }\n    },\n    {\n      \"index\": 918,\n      \"type\": \"repeat\",\n      \"count\": 64,\n      \"definition\": {\n        \"name\": \"BehaviorBool\"\n      }\n    },\n    {\n      \"index\": 982,\n      \"type\": \"repeat\",\n      \"count\": 64,\n      \"definition\": {\n        \"name\": \"AcceptBool\"\n      }\n    },\n    {\n      \"index\": 1046,\n      \"type\": \"repeat\",\n      \"count\": 64,\n      \"definition\": {\n        \"name\": \"QualifiedBool\"\n      }\n    },\n    {\n      \"index\": 1110,\n      \"type\": \"repeat\",\n      \"count\": 64,\n      \"definition\": {\n        \"name\": \"CanTargetBool\"\n      }\n    },\n    {\n      \"index\": 1174,\n      \"type\": \"repeat\",\n      \"count\": 24,\n      \"definition\": {\n        \"name\": \"ToDoCompleteSeq\"\n      }\n    },\n    {\n      \"index\": 1198,\n      \"type\": \"repeat\",\n      \"count\": 24,\n      \"definition\": {\n        \"name\": \"ToDoQty\"\n      }\n    },\n    {\n      \"index\": 1222,\n      \"type\": \"repeat\",\n      \"count\": 8,\n      \"definition\": {\n        \"type\": \"repeat\",\n        \"count\": 24,\n        \"definition\": {\n          \"name\": \"ToDoLocation\",\n          \"converter\": {\n            \"type\": \"link\",\n            \"target\": \"Level\"\n          }\n        }\n      }\n    },\n    {\n      \"index\": 1414,\n      \"type\": \"repeat\",\n      \"count\": 24,\n      \"definition\": {\n        \"name\": \"CountableNum\"\n      }\n    },\n    {\n      \"index\": 1438,\n      \"name\": \"Level{Max}\"\n    },\n    {\n      \"index\": 1439,\n      \"name\": \"ClassJob{Required}\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"ClassJob\"\n      }\n    },\n    {\n      \"index\": 1440,\n      \"name\": \"QuestRewardOtherDisplay\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"QuestRewardOther\"\n      }\n    },\n    {\n      \"index\": 1441,\n      \"name\": \"ExpFactor\"\n    },\n    {\n      \"index\": 1442,\n      \"name\": \"GilReward\"\n    },\n    {\n      \"index\": 1443,\n      \"name\": \"CurrencyReward\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"Item\"\n      }\n    },\n    {\n      \"index\": 1444,\n      \"name\": \"CurrencyRewardCount\"\n    },\n    {\n      \"index\": 1445,\n      \"type\": \"repeat\",\n      \"count\": 3,\n      \"definition\": {\n        \"name\": \"Item{Catalyst}\",\n        \"converter\": {\n          \"type\": \"link\",\n          \"target\": \"Item\"\n        }\n      }\n    },\n    {\n      \"index\": 1448,\n      \"type\": \"repeat\",\n      \"count\": 3,\n      \"definition\": {\n        \"name\": \"ItemCount{Catalyst}\"\n      }\n    },\n    {\n      \"index\": 1451,\n      \"name\": \"ItemRewardType\"\n    },\n    {\n      \"index\": 1452,\n      \"type\": \"repeat\",\n      \"count\": 7,\n      \"definition\": {\n        \"name\": \"Item{Reward}\",\n        \"converter\": {\n          \"type\": \"complexlink\",\n          \"links\": [\n            {\n              \"when\": {\n                \"key\": \"ItemRewardType\",\n                \"value\": 1\n              },\n              \"sheet\": \"Item\"\n            },\n            {\n              \"when\": {\n                \"key\": \"ItemRewardType\",\n                \"value\": 3\n              },\n              \"sheet\": \"Item\"\n            },\n            {\n              \"when\": {\n                \"key\": \"ItemRewardType\",\n                \"value\": 5\n              },\n              \"sheet\": \"Item\"\n            },\n            {\n              \"when\": {\n                \"key\": \"ItemRewardType\",\n                \"value\": 6\n              },\n              \"sheet\": \"QuestClassJobReward\"\n            },\n            {\n              \"when\": {\n                \"key\": \"ItemRewardType\",\n                \"value\": 7\n              },\n              \"sheet\": \"BeastRankBonus\"\n            }\n          ]\n        }\n      }\n    },\n    {\n      \"index\": 1459,\n      \"type\": \"repeat\",\n      \"count\": 7,\n      \"definition\": {\n        \"name\": \"ItemCount{Reward}\"\n      }\n    },\n    {\n      \"index\": 1473,\n      \"type\": \"repeat\",\n      \"count\": 7,\n      \"definition\": {\n        \"name\": \"Stain{Reward}\",\n        \"converter\": {\n          \"type\": \"link\",\n          \"target\": \"Stain\"\n        }\n      }\n    },\n    {\n      \"index\": 1480,\n      \"type\": \"repeat\",\n      \"count\": 5,\n      \"definition\": {\n        \"name\": \"OptionalItem{Reward}\",\n        \"converter\": {\n          \"type\": \"link\",\n          \"target\": \"Item\"\n        }\n      }\n    },\n    {\n      \"index\": 1485,\n      \"type\": \"repeat\",\n      \"count\": 5,\n      \"definition\": {\n        \"name\": \"OptionalItemCount{Reward}\"\n      }\n    },\n    {\n      \"index\": 1490,\n      \"type\": \"repeat\",\n      \"count\": 5,\n      \"definition\": {\n        \"name\": \"OptionalItemIsHQ{Reward}\"\n      }\n    },\n    {\n      \"index\": 1495,\n      \"type\": \"repeat\",\n      \"count\": 5,\n      \"definition\": {\n        \"name\": \"OptionalItemStain{Reward}\",\n        \"converter\": {\n          \"type\": \"link\",\n          \"target\": \"Stain\"\n        }\n      }\n    },\n    {\n      \"index\": 1500,\n      \"name\": \"Emote{Reward}\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"Emote\"\n      }\n    },\n    {\n      \"index\": 1501,\n      \"name\": \"Action{Reward}\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"Action\"\n      }\n    },\n    {\n      \"index\": 1502,\n      \"type\": \"repeat\",\n      \"count\": 2,\n      \"definition\": {\n        \"name\": \"GeneralAction{Reward}\",\n        \"converter\": {\n          \"type\": \"link\",\n          \"target\": \"GeneralAction\"\n        }\n      }\n    },\n    {\n      \"index\": 1504,\n      \"name\": \"System{Reward}[0]\"\n    },\n    {\n      \"index\": 1505,\n      \"name\": \"Other{Reward}\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"QuestRewardOther\"\n      }\n    },\n    {\n      \"index\": 1506,\n      \"name\": \"System{Reward}[1]\"\n    },\n    {\n      \"index\": 1507,\n      \"name\": \"GCType{Reward}\"\n    },\n    {\n      \"index\": 1508,\n      \"name\": \"InstanceContent{Unlock}\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"InstanceContent\"\n      }\n    },\n    {\n      \"index\": 1509,\n      \"name\": \"Tomestone\"\n    },\n    {\n      \"index\": 1510,\n      \"name\": \"Tomestone{Reward}\",\n      \"converter\": {\n        \"type\": \"complexlink\",\n        \"links\": [\n          {\n            \"project\": \"Item\",\n            \"key\": \"Tomestones\",\n            \"sheet\": \"TomestonesItem\"\n          }\n        ]\n      }\n    },\n    {\n      \"index\": 1511,\n      \"name\": \"TomestoneCount{Reward}\"\n    },\n    {\n      \"index\": 1512,\n      \"name\": \"ReputationReward\"\n    },\n    {\n      \"index\": 1513,\n      \"name\": \"PlaceName\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"PlaceName\"\n      }\n    },\n    {\n      \"index\": 1514,\n      \"name\": \"JournalGenre\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"JournalGenre\"\n      }\n    },\n    {\n      \"index\": 1516,\n      \"name\": \"Icon\",\n      \"converter\": {\n        \"type\": \"icon\"\n      }\n    },\n    {\n      \"index\": 1517,\n      \"name\": \"Icon{Special}\",\n      \"converter\": {\n        \"type\": \"icon\"\n      }\n    },\n    {\n      \"index\": 1518,\n      \"name\": \"Introduction\"\n    },\n    {\n      \"index\": 1519,\n      \"name\": \"HideOfferIcon\"\n    },\n    {\n      \"index\": 1520,\n      \"name\": \"EventIconType\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"EventIconType\"\n      }\n    },\n    {\n      \"index\": 1522,\n      \"name\": \"SortKey\"\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/QuestAcceptAdditionCondition.json",
    "content": "{\n  \"sheet\": \"QuestAcceptAdditionCondition\",\n  \"definitions\": [\n    {\n      \"name\": \"Requirement[0]\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"Quest\"\n      }\n    },\n    {\n      \"index\": 1,\n      \"name\": \"Requirement[1]\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"Quest\"\n      }\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/QuestBattle.json",
    "content": "{\n  \"sheet\": \"QuestBattle\",\n  \"definitions\": [\n    {\n      \"name\": \"Quest\",\n      \"converter\": {\n        \"type\": \"multiref\",\n        \"targets\": [\n          \"ArrayEventHandler\",\n          \"Quest\"\n        ]\n      }\n    },\n    {\n      \"index\": 1,\n      \"name\": \"QuestBattleScene\"\n    },\n    {\n      \"index\": 2,\n      \"name\": \"TimeLimit\"\n    },\n    {\n      \"index\": 3,\n      \"name\": \"LevelSync\"\n    },\n    {\n      \"index\": 4,\n      \"type\": \"repeat\",\n      \"count\": 220,\n      \"definition\": {\n        \"name\": \"ScriptInstruction\"\n      }\n    },\n    {\n      \"index\": 224,\n      \"type\": \"repeat\",\n      \"count\": 220,\n      \"definition\": {\n        \"name\": \"ScriptValue\"\n      }\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/QuestChapter.json",
    "content": "{\n  \"sheet\": \"QuestChapter\",\n  \"defaultColumn\": \"Quest\",\n  \"definitions\": [\n    {\n      \"name\": \"Quest\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"Quest\"\n      }\n    },\n    {\n      \"index\": 1,\n      \"name\": \"Redo\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"QuestRedoChapter\"\n      }\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/QuestClassJobReward.json",
    "content": "{\n  \"sheet\": \"QuestClassJobReward\",\n  \"definitions\": [\n    {\n      \"name\": \"ClassJobCategory\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"ClassJobCategory\"\n      }\n    },\n    {\n      \"index\": 1,\n      \"type\": \"repeat\",\n      \"count\": 4,\n      \"definition\": {\n        \"name\": \"Reward{Item}\",\n        \"converter\": {\n          \"type\": \"link\",\n          \"target\": \"Item\"\n        }\n      }\n    },\n    {\n      \"index\": 5,\n      \"type\": \"repeat\",\n      \"count\": 4,\n      \"definition\": {\n        \"name\": \"Reward{Amount}\"\n      }\n    },\n    {\n      \"index\": 9,\n      \"type\": \"repeat\",\n      \"count\": 4,\n      \"definition\": {\n        \"name\": \"Required{Item}\",\n        \"converter\": {\n          \"type\": \"link\",\n          \"target\": \"Item\"\n        }\n      }\n    },\n    {\n      \"index\": 13,\n      \"type\": \"repeat\",\n      \"count\": 4,\n      \"definition\": {\n        \"name\": \"Required{Amount}\"\n      }\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/QuestClassJobSupply.json",
    "content": "{\n  \"sheet\": \"QuestClassJobSupply\",\n  \"definitions\": [\n    {\n      \"name\": \"ClassJobCategory\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"ClassJobCategory\"\n      }\n    },\n    {\n      \"index\": 2,\n      \"name\": \"ENpcResident\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"ENpcResident\"\n      }\n    },\n    {\n      \"index\": 3,\n      \"name\": \"Item\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"Item\"\n      }\n    },\n    {\n      \"index\": 4,\n      \"name\": \"AmountRequired\"\n    },\n    {\n      \"index\": 5,\n      \"name\": \"ItemHQ\"\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/QuestDefineClient.json",
    "content": "{\n  \"sheet\": \"QuestDefineClient\",\n  \"definitions\": [\n    {\n      \"index\": 1,\n      \"name\": \"Target\",\n      \"converter\": {\n        \"type\": \"multiref\",\n        \"targets\": [\n          \"Item\",\n          \"Quest\",\n          \"ENpcBase\",\n          \"EObjName\",\n          \"Level\"\n        ]\n      }\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/QuestDerivedClass.json",
    "content": "{\n  \"sheet\": \"QuestDerivedClass\",\n  \"defaultColumn\": \"ClassJob\",\n  \"definitions\": [\n    {\n      \"name\": \"ClassJob\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"ClassJob\"\n      }\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/QuestEffect.json",
    "content": "{\n  \"sheet\": \"QuestEffect\",\n  \"definitions\": []\n}"
  },
  {
    "path": "SaintCoinach/Definitions/QuestEffectDefine.json",
    "content": "{\n  \"sheet\": \"QuestEffectDefine\",\n  \"defaultColumn\": \"Effect\",\n  \"definitions\": [\n    {\n      \"name\": \"Effect\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"QuestEffect\"\n      }\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/QuestEventAreaEntranceInfo.json",
    "content": "{\n  \"sheet\": \"QuestEventAreaEntranceInfo\",\n  \"definitions\": [\n    {\n      \"name\": \"Quest\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"Quest\"\n      }\n    },\n    {\n      \"index\": 2,\n      \"name\": \"Location\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"Level\"\n      }\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/QuestLinkMarker.json",
    "content": "{\n  \"sheet\": \"QuestLinkMarker\",\n  \"definitions\": [\n    {\n      \"index\": 0,\n      \"name\": \"SourceMap\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"Map\"\n      }\n    },\n    {\n      \"index\": 1,\n      \"name\": \"Level\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"Level\"\n      }\n    },\n    {\n      \"index\": 2,\n      \"name\": \"TargetMap\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"Map\"\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "SaintCoinach/Definitions/QuestLinkMarkerIcon.json",
    "content": "{\n  \"sheet\": \"QuestLinkMarkerIcon\",\n  \"definitions\": [\n    {\n      \"name\": \"Icon\",\n      \"converter\": {\n        \"type\": \"icon\"\n      }\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/QuestLinkMarkerSet.json",
    "content": "{\n  \"sheet\": \"QuestLinkMarkerSet\",\n  \"definitions\": []\n}"
  },
  {
    "path": "SaintCoinach/Definitions/QuestRedo.json",
    "content": "{\n  \"sheet\": \"QuestRedo\",\n  \"defaultColumn\": \"Quest\",\n  \"definitions\": [\n    {\n      \"name\": \"FinalQuest\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"Quest\"\n      }\n    },\n    {\n      \"index\": 3,\n      \"name\": \"Chapter\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"QuestRedoChapter\"\n      }\n    },\n    {\n      \"index\": 4,\n      \"type\": \"repeat\",\n      \"count\": 32,\n      \"definition\": {\n        \"name\": \"Quest\",\n        \"converter\": {\n          \"type\": \"link\",\n          \"target\": \"Quest\"\n        }\n      }\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/QuestRedoChapterUI.json",
    "content": "{\n  \"sheet\": \"QuestRedoChapterUI\",\n  \"defaultColumn\": \"Quest\",\n  \"definitions\": [\n    {\n      \"name\": \"Quest\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"Quest\"\n      }\n    },\n    {\n      \"index\": 2,\n      \"name\": \"UITab\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"QuestRedoChapterUITab\"\n      }\n    },\n    {\n      \"index\": 3,\n      \"name\": \"Category\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"QuestRedoChapterUICategory\"\n      }\n    },\n    {\n      \"index\": 5,\n      \"name\": \"QuestRedoUISmall\",\n      \"converter\": {\n        \"type\": \"icon\"\n      }\n    },\n    {\n      \"index\": 6,\n      \"name\": \"QuestRedoUILarge\",\n      \"converter\": {\n        \"type\": \"icon\"\n      }\n    },\n    {\n      \"index\": 7,\n      \"name\": \"QuestRedoUIWide\",\n      \"converter\": {\n        \"type\": \"icon\"\n      }\n    },\n    {\n      \"index\": 8,\n      \"name\": \"ChapterName\"\n    },\n    {\n      \"index\": 9,\n      \"name\": \"ChapterPart\"\n    },\n    {\n      \"index\": 10,\n      \"name\": \"Transient\"\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/QuestRedoChapterUICategory.json",
    "content": "{\n  \"sheet\": \"QuestRedoChapterUICategory\",\n  \"defaultColumn\": \"Expac\",\n  \"definitions\": [\n    {\n      \"index\": 1,\n      \"name\": \"Expac\"\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/QuestRedoChapterUITab.json",
    "content": "{\n  \"sheet\": \"QuestRedoChapterUITab\",\n  \"defaultColumn\": \"Text\",\n  \"definitions\": [\n    {\n      \"index\": 1,\n      \"name\": \"Icon1\",\n      \"converter\": {\n        \"type\": \"icon\"\n      }\n    },\n    {\n      \"index\": 2,\n      \"name\": \"Icon2\",\n      \"converter\": {\n        \"type\": \"icon\"\n      }\n    },\n    {\n      \"index\": 3,\n      \"name\": \"Text\"\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/QuestRedoIncompChapter.json",
    "content": "{\n  \"sheet\": \"QuestRedoIncompChapter\",\n  \"defaultColumn\": \"Chapter\",\n  \"definitions\": [\n    {\n      \"name\": \"Chapter\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"QuestRedoChapter\"\n      }\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/QuestRepeatFlag.json",
    "content": "{\n  \"sheet\": \"QuestRepeatFlag\",\n  \"defaultColumn\": \"Quest\",\n  \"definitions\": [\n    {\n      \"name\": \"Quest\",\n      \"converter\": {\n        \"type\": \"multiref\",\n        \"targets\": [\n          \"ArrayEventHandler\",\n          \"Quest\"\n        ]\n      }\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/QuestRewardOther.json",
    "content": "{\n  \"sheet\": \"QuestRewardOther\",\n  \"defaultColumn\": \"Name\",\n  \"definitions\": [\n    {\n      \"name\": \"Icon\",\n      \"converter\": {\n        \"type\": \"icon\"\n      }\n    },\n    {\n      \"index\": 1,\n      \"name\": \"Name\"\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/QuestSelectTitle.json",
    "content": "{\n  \"sheet\": \"QuestSelectTitle\",\n  \"definitions\": []\n}"
  },
  {
    "path": "SaintCoinach/Definitions/QuestSetDefine.json",
    "content": "{\n  \"sheet\": \"QuestSetDefine\",\n  \"definitions\": []\n}"
  },
  {
    "path": "SaintCoinach/Definitions/QuickChat.json",
    "content": "{\n  \"sheet\": \"QuickChat\",\n  \"defaultColumn\": \"Text\",\n  \"definitions\": [\n    {\n      \"name\": \"Name{Action}\"\n    },\n    {\n      \"index\": 1,\n      \"name\": \"Icon\",\n      \"converter\": {\n        \"type\": \"icon\"\n      }\n    },\n    {\n      \"index\": 2,\n      \"name\": \"Addon\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"Addon\"\n      }\n    },\n    {\n      \"index\": 3,\n      \"name\": \"QuickChatTransient\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"QuickChatTransient\"\n      }\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/QuickChatTransient.json",
    "content": "{\n  \"sheet\": \"QuickChatTransient\",\n  \"defaultColumn\": \"Text{Output}\",\n  \"definitions\": [\n    {\n      \"name\": \"Text{Output}\"\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/RPParameter.json",
    "content": "{\n  \"sheet\": \"RPParameter\",\n  \"defaultColumn\": \"BNpcName\",\n  \"definitions\": [\n    {\n      \"name\": \"BNpcName\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"BNpcName\"\n      }\n    },\n    {\n      \"index\": 1,\n      \"name\": \"ClassJob\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"ClassJob\"\n      }\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/Race.json",
    "content": "{\n  \"sheet\": \"Race\",\n  \"defaultColumn\": \"Feminine\",\n  \"definitions\": [\n    {\n      \"name\": \"Masculine\"\n    },\n    {\n      \"index\": 1,\n      \"name\": \"Feminine\"\n    },\n    {\n      \"index\": 2,\n      \"name\": \"RSE{M}{Body}\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"Item\"\n      }\n    },\n    {\n      \"index\": 3,\n      \"name\": \"RSE{M}{Hands}\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"Item\"\n      }\n    },\n    {\n      \"index\": 4,\n      \"name\": \"RSE{M}{Legs}\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"Item\"\n      }\n    },\n    {\n      \"index\": 5,\n      \"name\": \"RSE{M}{Feet}\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"Item\"\n      }\n    },\n    {\n      \"index\": 6,\n      \"name\": \"RSE{F}{Body}\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"Item\"\n      }\n    },\n    {\n      \"index\": 7,\n      \"name\": \"RSE{F}{Hands}\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"Item\"\n      }\n    },\n    {\n      \"index\": 8,\n      \"name\": \"RSE{F}{Legs}\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"Item\"\n      }\n    },\n    {\n      \"index\": 9,\n      \"name\": \"RSE{F}{Feet}\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"Item\"\n      }\n    },\n    {\n      \"index\": 11,\n      \"name\": \"ExPac\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"ExVersion\"\n      }\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/RacingChocoboItem.json",
    "content": "{\n  \"sheet\": \"RacingChocoboItem\",\n  \"defaultColumn\": \"Item\",\n  \"definitions\": [\n    {\n      \"name\": \"Item\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"Item\"\n      }\n    },\n    {\n      \"index\": 1,\n      \"name\": \"Category\"\n    },\n    {\n      \"index\": 2,\n      \"type\": \"repeat\",\n      \"count\": 2,\n      \"definition\": {\n        \"name\": \"Param\"\n      }\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/RacingChocoboName.json",
    "content": "{\n  \"sheet\": \"RacingChocoboName\",\n  \"defaultColumn\": \"Name\",\n  \"definitions\": [\n    {\n      \"name\": \"Name\"\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/RacingChocoboNameCategory.json",
    "content": "{\n  \"sheet\": \"RacingChocoboNameCategory\",\n  \"defaultColumn\": \"Name\",\n  \"definitions\": [\n    {\n      \"name\": \"SortKey\"\n    },\n    {\n      \"index\": 1,\n      \"name\": \"Name\"\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/RacingChocoboNameInfo.json",
    "content": "{\n  \"sheet\": \"RacingChocoboNameInfo\",\n  \"definitions\": [\n    {\n      \"name\": \"RacingChocoboNameCategory\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"RacingChocoboNameCategory\"\n      }\n    },\n    {\n      \"index\": 5,\n      \"type\": \"repeat\",\n      \"count\": 3,\n      \"definition\": {\n        \"name\": \"Name\",\n        \"converter\": {\n          \"type\": \"link\",\n          \"target\": \"RacingChocoboName\"\n        }\n      }\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/RacingChocoboParam.json",
    "content": "{\n  \"sheet\": \"RacingChocoboParam\",\n  \"defaultColumn\": \"Name\",\n  \"definitions\": [\n    {\n      \"name\": \"Name\"\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/RaidFinderParam.json",
    "content": "{\n  \"sheet\": \"RaidFinderParam\",\n  \"definitions\": []\n}"
  },
  {
    "path": "SaintCoinach/Definitions/ReactionEventObject.json",
    "content": "{\n  \"sheet\": \"ReactionEventObject\",\n  \"definitions\": []\n}"
  },
  {
    "path": "SaintCoinach/Definitions/ReactionEventObjectInfo.json",
    "content": "{\n  \"sheet\": \"ReactionEventObjectInfo\",\n  \"definitions\": []\n}"
  },
  {
    "path": "SaintCoinach/Definitions/RecastNavimesh.json",
    "content": "{\n  \"sheet\": \"RecastNavimesh\",\n  \"definitions\": [\n    {\n      \"index\": 1,\n      \"name\": \"Tile{Size}\"\n    },\n    {\n      \"index\": 2,\n      \"name\": \"Cell{Size}\"\n    },\n    {\n      \"index\": 3,\n      \"name\": \"Cell{Height}\"\n    },\n    {\n      \"index\": 4,\n      \"name\": \"Agent{Height}\"\n    },\n    {\n      \"index\": 5,\n      \"name\": \"Agent{Radius}\"\n    },\n    {\n      \"index\": 6,\n      \"name\": \"Agent{MaxClimb}\"\n    },\n    {\n      \"index\": 7,\n      \"name\": \"Agent{MaxSlope}\"\n    },\n    {\n      \"index\": 9,\n      \"name\": \"Region{MinSize}\"\n    },\n    {\n      \"index\": 10,\n      \"name\": \"Region{MergedSize}\"\n    },\n    {\n      \"index\": 12,\n      \"name\": \"MaxEdgeLength\"\n    },\n    {\n      \"index\": 13,\n      \"name\": \"MaxEdgeError\"\n    },\n    {\n      \"index\": 14,\n      \"name\": \"VertsPerPoly\"\n    },\n    {\n      \"index\": 15,\n      \"name\": \"DetailMesh{SampleDistance}\"\n    },\n    {\n      \"index\": 16,\n      \"name\": \"DetailMesh{MaxSampleError}\"\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/Recipe.json",
    "content": "{\n  \"sheet\": \"Recipe\",\n  \"defaultColumn\": \"Item{Result}\",\n  \"definitions\": [\n    {\n      \"index\": 0,\n      \"name\": \"Number\"\n    },\n    {\n      \"index\": 1,\n      \"name\": \"CraftType\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"CraftType\"\n      }\n    },\n    {\n      \"index\": 2,\n      \"name\": \"RecipeLevelTable\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"RecipeLevelTable\"\n      }\n    },\n    {\n      \"index\": 4,\n      \"name\": \"Item{Result}\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"Item\"\n      }\n    },\n    {\n      \"index\": 5,\n      \"name\": \"Amount{Result}\"\n    },\n    {\n      \"index\": 6,\n      \"type\": \"repeat\",\n      \"count\": 8,\n      \"definition\": {\n        \"type\": \"group\",\n        \"members\": [\n          {\n            \"name\": \"Item{Ingredient}\",\n            \"converter\": {\n              \"type\": \"link\",\n              \"target\": \"Item\"\n            }\n          },\n          {\n            \"name\": \"Amount{Ingredient}\"\n          }\n        ]\n      }\n    },\n    {\n      \"index\": 22,\n      \"name\": \"RecipeNotebookList\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"RecipeNotebookList\"\n      }\n    },\n    {\n      \"index\": 23,\n      \"name\": \"DisplayPriority\"\n    },\n    {\n      \"index\": 24,\n      \"name\": \"IsSecondary\"\n    },\n    {\n      \"index\": 25,\n      \"name\": \"MaterialQualityFactor\"\n    },\n    {\n      \"index\": 26,\n      \"name\": \"DifficultyFactor\"\n    },\n    {\n      \"index\": 27,\n      \"name\": \"QualityFactor\"\n    },\n    {\n      \"index\": 28,\n      \"name\": \"DurabilityFactor\"\n    },\n    {\n      \"index\": 29,\n      \"name\": \"RequiredQuality\"\n    },\n    {\n      \"index\": 30,\n      \"name\": \"RequiredCraftsmanship\"\n    },\n    {\n      \"index\": 31,\n      \"name\": \"RequiredControl\"\n    },\n    {\n      \"index\": 32,\n      \"name\": \"QuickSynthCraftsmanship\"\n    },\n    {\n      \"index\": 33,\n      \"name\": \"QuickSynthControl\"\n    },\n    {\n      \"index\": 34,\n      \"name\": \"SecretRecipeBook\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"SecretRecipeBook\"\n      }\n    },\n    {\n      \"index\": 35,\n      \"name\": \"Quest\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"Quest\"\n      }\n    },\n    {\n      \"index\": 36,\n      \"name\": \"CanQuickSynth\"\n    },\n    {\n      \"index\": 37,\n      \"name\": \"CanHq\"\n    },\n    {\n      \"index\": 38,\n      \"name\": \"ExpRewarded\"\n    },\n    {\n      \"index\": 39,\n      \"name\": \"Status{Required}\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"Status\"\n      }\n    },\n    {\n      \"index\": 40,\n      \"name\": \"Item{Required}\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"Item\"\n      }\n    },\n    {\n      \"index\": 41,\n      \"name\": \"IsSpecializationRequired\"\n    },\n    {\n      \"index\": 42,\n      \"name\": \"IsExpert\"\n    },\n    {\n      \"index\": 45,\n      \"name\": \"PatchNumber\"\n    }\n  ]\n}\n"
  },
  {
    "path": "SaintCoinach/Definitions/RecipeLevelTable.json",
    "content": "{\n  \"sheet\": \"RecipeLevelTable\",\n  \"definitions\": [\n    {\n      \"name\": \"ClassJobLevel\"\n    },\n    {\n      \"index\": 1,\n      \"name\": \"Stars\"\n    },\n    {\n      \"index\": 2,\n      \"name\": \"SuggestedCraftsmanship\"\n    },\n    {\n      \"index\": 3,\n      \"name\": \"Difficulty\"\n    },\n    {\n      \"index\": 4,\n      \"name\": \"Quality\"\n    },\n    {\n      \"index\": 5,\n      \"name\": \"ProgressDivider\"\n    },\n    {\n      \"index\": 6,\n      \"name\": \"QualityDivider\"\n    },\n    {\n      \"index\": 7,\n      \"name\": \"ProgressModifier\"\n    },\n    {\n      \"index\": 8,\n      \"name\": \"QualityModifier\"\n    },\n    {\n      \"index\": 9,\n      \"name\": \"Durability\"\n    },\n    {\n      \"index\": 10,\n      \"name\": \"ConditionsFlag\"\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/RecipeLookup.json",
    "content": "{\n  \"sheet\": \"RecipeLookup\",\n  \"definitions\": [\n    {\n      \"name\": \"CRP\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"Recipe\"\n      }\n    },\n    {\n      \"index\": 1,\n      \"name\": \"BSM\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"Recipe\"\n      }\n    },\n    {\n      \"index\": 2,\n      \"name\": \"ARM\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"Recipe\"\n      }\n    },\n    {\n      \"index\": 3,\n      \"name\": \"GSM\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"Recipe\"\n      }\n    },\n    {\n      \"index\": 4,\n      \"name\": \"LTW\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"Recipe\"\n      }\n    },\n    {\n      \"index\": 5,\n      \"name\": \"WVR\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"Recipe\"\n      }\n    },\n    {\n      \"index\": 6,\n      \"name\": \"ALC\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"Recipe\"\n      }\n    },\n    {\n      \"index\": 7,\n      \"name\": \"CUL\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"Recipe\"\n      }\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/RecipeNotebookList.json",
    "content": "{\n  \"sheet\": \"RecipeNotebookList\",\n  \"definitions\": [\n    {\n      \"name\": \"Count\"\n    },\n    {\n      \"index\": 1,\n      \"type\": \"repeat\",\n      \"count\": 160,\n      \"definition\": {\n        \"name\": \"Recipe\",\n        \"converter\": {\n          \"type\": \"link\",\n          \"target\": \"Recipe\"\n        }\n      }\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/RecommendContents.json",
    "content": "{\n  \"sheet\": \"RecommendContents\",\n  \"defaultColumn\": \"Level\",\n  \"definitions\": [\n    {\n      \"name\": \"Level\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"Level\"\n      }\n    },\n    {\n      \"index\": 1,\n      \"name\": \"ClassJob\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"ClassJob\"\n      }\n    },\n    {\n      \"index\": 2,\n      \"name\": \"MinLevel\"\n    },\n    {\n      \"index\": 3,\n      \"name\": \"MaxLevel\"\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/Relic.json",
    "content": "{\n  \"sheet\": \"Relic\",\n  \"defaultColumn\": \"Item{Animus}\",\n  \"definitions\": [\n    {\n      \"name\": \"Item{Atma}\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"Item\"\n      }\n    },\n    {\n      \"index\": 1,\n      \"name\": \"Item{Animus}\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"Item\"\n      }\n    },\n    {\n      \"index\": 2,\n      \"name\": \"Icon\",\n      \"converter\": {\n        \"type\": \"icon\"\n      }\n    },\n    {\n      \"index\": 3,\n      \"name\": \"Materia[0]\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"Materia\"\n      }\n    },\n    {\n      \"index\": 4,\n      \"name\": \"NoteMain[0]\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"RelicNote\"\n      }\n    },\n    {\n      \"index\": 5,\n      \"name\": \"NoteSub[0]\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"RelicNote\"\n      }\n    },\n    {\n      \"index\": 6,\n      \"name\": \"NoteSelection{1}[0]\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"RelicNote\"\n      }\n    },\n    {\n      \"index\": 7,\n      \"name\": \"Materia[1]\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"Materia\"\n      }\n    },\n    {\n      \"index\": 8,\n      \"name\": \"NoteMain[1]\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"RelicNote\"\n      }\n    },\n    {\n      \"index\": 9,\n      \"name\": \"NoteSub[1]\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"RelicNote\"\n      }\n    },\n    {\n      \"index\": 10,\n      \"name\": \"NoteSelection[1]\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"RelicNote\"\n      }\n    },\n    {\n      \"index\": 11,\n      \"name\": \"Materia[2]\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"Materia\"\n      }\n    },\n    {\n      \"index\": 12,\n      \"name\": \"NoteMain[2]\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"RelicNote\"\n      }\n    },\n    {\n      \"index\": 13,\n      \"name\": \"NoteSub[2]\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"RelicNote\"\n      }\n    },\n    {\n      \"index\": 14,\n      \"name\": \"Materia[3]\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"Materia\"\n      }\n    },\n    {\n      \"index\": 15,\n      \"name\": \"NoteSelection[3]\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"RelicNote\"\n      }\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/Relic3.json",
    "content": "{\n  \"sheet\": \"Relic3\",\n  \"defaultColumn\": \"Item{Novus}\",\n  \"definitions\": [\n    {\n      \"name\": \"Item{Animus}\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"Item\"\n      }\n    },\n    {\n      \"index\": 1,\n      \"name\": \"Item{Scroll}\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"Item\"\n      }\n    },\n    {\n      \"index\": 2,\n      \"name\": \"MateriaLimit\"\n    },\n    {\n      \"index\": 3,\n      \"name\": \"Item{Novus}\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"Item\"\n      }\n    },\n    {\n      \"index\": 4,\n      \"name\": \"Icon\",\n      \"converter\": {\n        \"type\": \"icon\"\n      }\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/RelicItem.json",
    "content": "{\n  \"sheet\": \"RelicItem\",\n  \"definitions\": [\n    {\n      \"index\": 1,\n      \"name\": \"Gladiator{Item}\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"Item\"\n      }\n    },\n    {\n      \"index\": 2,\n      \"name\": \"Pugilist{Item}\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"Item\"\n      }\n    },\n    {\n      \"index\": 3,\n      \"name\": \"Marauder{Item}\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"Item\"\n      }\n    },\n    {\n      \"index\": 4,\n      \"name\": \"Lancer{Item}\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"Item\"\n      }\n    },\n    {\n      \"index\": 5,\n      \"name\": \"Archer{Item}\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"Item\"\n      }\n    },\n    {\n      \"index\": 6,\n      \"name\": \"Conjurer{Item}\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"Item\"\n      }\n    },\n    {\n      \"index\": 7,\n      \"name\": \"Thaumaturge{Item}\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"Item\"\n      }\n    },\n    {\n      \"index\": 8,\n      \"name\": \"Arcanist(SMN){Item}\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"Item\"\n      }\n    },\n    {\n      \"index\": 9,\n      \"name\": \"Arcanist(SCH){Item}\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"Item\"\n      }\n    },\n    {\n      \"index\": 10,\n      \"name\": \"Shield{Item}\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"Item\"\n      }\n    },\n    {\n      \"index\": 11,\n      \"name\": \"Rogue{Item}\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"Item\"\n      }\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/RelicNote.json",
    "content": "{\n  \"sheet\": \"RelicNote\",\n  \"defaultColumn\": \"Name\",\n  \"definitions\": [\n    {\n      \"name\": \"EventItem\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"EventItem\"\n      }\n    },\n    {\n      \"index\": 1,\n      \"type\": \"repeat\",\n      \"count\": 10,\n      \"definition\": {\n        \"type\": \"group\",\n        \"members\": [\n          {\n            \"name\": \"MonsterNoteTarget{Common}\",\n            \"converter\": {\n              \"type\": \"link\",\n              \"target\": \"MonsterNoteTarget\"\n            }\n          },\n          {\n            \"name\": \"MonsterCount\"\n          }\n        ]\n      }\n    },\n    {\n      \"index\": 21,\n      \"type\": \"repeat\",\n      \"count\": 3,\n      \"definition\": {\n        \"name\": \"MonsterNoteTarget{NM}\",\n        \"converter\": {\n          \"type\": \"link\",\n          \"target\": \"MonsterNoteTarget\"\n        }\n      }\n    },\n    {\n      \"index\": 25,\n      \"type\": \"repeat\",\n      \"count\": 3,\n      \"definition\": {\n        \"type\": \"group\",\n        \"members\": [\n          {\n            \"name\": \"Fate\",\n            \"converter\": {\n              \"type\": \"link\",\n              \"target\": \"Fate\"\n            }\n          },\n          {\n            \"name\": \"PlaceName{Fate}\",\n            \"converter\": {\n              \"type\": \"link\",\n              \"target\": \"PlaceName\"\n            }\n          }\n        ]\n      }\n    },\n    {\n      \"index\": 31,\n      \"type\": \"repeat\",\n      \"count\": 3,\n      \"definition\": {\n        \"name\": \"Leve\",\n        \"converter\": {\n          \"type\": \"link\",\n          \"target\": \"Leve\"\n        }\n      }\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/RelicNoteCategory.json",
    "content": "{\n  \"sheet\": \"RelicNoteCategory\",\n  \"defaultColumn\": \"Text\",\n  \"definitions\": [\n    {\n      \"index\": 1,\n      \"name\": \"Text\"\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/ReplaceAction.json",
    "content": "{\n  \"sheet\": \"ReplaceAction\",\n  \"definitions\": [\n    {\n      \"index\": 0,\n      \"name\": \"Action\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"Action\"\n      }\n    },\n    {\n      \"index\": 1,\n      \"name\": \"Type1\"\n    },\n    {\n      \"index\": 2,\n      \"name\": \"Param1\",\n      \"converter\": {\n        \"type\": \"complexlink\",\n        \"links\": [\n          {\n            \"when\": {\n              \"key\": \"Type1\",\n              \"value\": 2\n            },\n            \"sheet\": \"Status\"\n          },\n          {\n            \"when\": {\n              \"key\": \"Type1\",\n              \"value\": 3\n            },\n            \"sheet\": \"Trait\"\n          },\n          {\n            \"when\": {\n              \"key\": \"Type1\",\n              \"value\": 4\n            },\n            \"sheet\": \"Status\"\n          },\n          {\n            \"when\": {\n              \"key\": \"Type1\",\n              \"value\": 7\n            },\n            \"sheet\": \"Status\"\n          },\n          {\n            \"when\": {\n              \"key\": \"Type1\",\n              \"value\": 12\n            },\n            \"sheet\": \"Status\"\n          },\n          {\n            \"when\": {\n              \"key\": \"Type1\",\n              \"value\": 13\n            },\n            \"sheet\": \"Status\"\n          }\n        ]\n      }\n    },\n    {\n      \"index\": 3,\n      \"name\": \"ReplaceAction1\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"Action\"\n      }\n    },\n    {\n      \"index\": 4,\n      \"name\": \"Type2\"\n    },\n    {\n      \"index\": 5,\n      \"name\": \"Param2\",\n      \"converter\": {\n        \"type\": \"complexlink\",\n        \"links\": [\n          {\n            \"when\": {\n              \"key\": \"Type2\",\n              \"value\": 2\n            },\n            \"sheet\": \"Status\"\n          },\n          {\n            \"when\": {\n              \"key\": \"Type2\",\n              \"value\": 3\n            },\n            \"sheet\": \"Trait\"\n          },\n          {\n            \"when\": {\n              \"key\": \"Type2\",\n              \"value\": 4\n            },\n            \"sheet\": \"Status\"\n          },\n          {\n            \"when\": {\n              \"key\": \"Type2\",\n              \"value\": 7\n            },\n            \"sheet\": \"Status\"\n          },\n          {\n            \"when\": {\n              \"key\": \"Type2\",\n              \"value\": 12\n            },\n            \"sheet\": \"Status\"\n          },\n          {\n            \"when\": {\n              \"key\": \"Type2\",\n              \"value\": 13\n            },\n            \"sheet\": \"Status\"\n          }\n        ]\n      }\n    },\n    {\n      \"index\": 6,\n      \"name\": \"ReplaceAction2\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"Action\"\n      }\n    },\n    {\n      \"index\": 7,\n      \"name\": \"Type3\"\n    },\n    {\n      \"index\": 8,\n      \"name\": \"Param3\",\n      \"converter\": {\n        \"type\": \"complexlink\",\n        \"links\": [\n          {\n            \"when\": {\n              \"key\": \"Type3\",\n              \"value\": 2\n            },\n            \"sheet\": \"Status\"\n          },\n          {\n            \"when\": {\n              \"key\": \"Type3\",\n              \"value\": 3\n            },\n            \"sheet\": \"Trait\"\n          },\n          {\n            \"when\": {\n              \"key\": \"Type3\",\n              \"value\": 4\n            },\n            \"sheet\": \"Status\"\n          },\n          {\n            \"when\": {\n              \"key\": \"Type3\",\n              \"value\": 7\n            },\n            \"sheet\": \"Status\"\n          },\n          {\n            \"when\": {\n              \"key\": \"Type3\",\n              \"value\": 12\n            },\n            \"sheet\": \"Status\"\n          },\n          {\n            \"when\": {\n              \"key\": \"Type3\",\n              \"value\": 13\n            },\n            \"sheet\": \"Status\"\n          }\n        ]\n      }\n    },\n    {\n      \"index\": 9,\n      \"name\": \"ReplaceAction3\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"Action\"\n      }\n    },\n    {\n      \"index\": 10,\n      \"name\": \"ReplaceSettable\"\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/Resident.json",
    "content": "{\n  \"sheet\": \"Resident\",\n  \"definitions\": [\n    {\n      \"index\": 1,\n      \"name\": \"Model\"\n    },\n    {\n      \"index\": 2,\n      \"name\": \"NpcYell\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"NpcYell\"\n      }\n    },\n    {\n      \"index\": 4,\n      \"name\": \"ResidentMotionType\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"ResidentMotionType\"\n      }\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/ResistanceWeaponAdjust.json",
    "content": "{\n  \"sheet\": \"ResistanceWeaponAdjust\",\n  \"definitions\": [\n    {\n      \"name\": \"MaxTotalStats\"\n    },\n    {\n      \"index\": 1,\n      \"name\": \"MaxEachStat\"\n    },\n    {\n      \"index\": 2,\n      \"type\": \"repeat\",\n      \"count\": 4,\n      \"definition\": {\n        \"name\": \"BaseParam\",\n        \"converter\": {\n          \"type\": \"link\",\n          \"target\": \"BaseParam\"\n        }\n      }\n    },\n    {\n      \"index\": 6,\n      \"name\": \"Image\",\n      \"converter\": {\n        \"type\": \"icon\"\n      }\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/RetainerFortuneRewardRange.json",
    "content": "{\n  \"sheet\": \"RetainerFortuneRewardRange\",\n  \"definitions\": [\n    {\n      \"name\": \"PercentOfLevel\"\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/RetainerTask.json",
    "content": "{\n  \"sheet\": \"RetainerTask\",\n  \"definitions\": [\n    {\n      \"name\": \"IsRandom\"\n    },\n    {\n      \"index\": 1,\n      \"name\": \"ClassJobCategory\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"ClassJobCategory\"\n      }\n    },\n    {\n      \"index\": 2,\n      \"name\": \"RetainerLevel\"\n    },\n    {\n      \"index\": 4,\n      \"name\": \"RetainerTaskParameter\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"RetainerTaskParameter\"\n      }\n    },\n    {\n      \"index\": 5,\n      \"name\": \"VentureCost\"\n    },\n    {\n      \"index\": 6,\n      \"name\": \"MaxTime{min}\"\n    },\n    {\n      \"index\": 7,\n      \"name\": \"Experience\"\n    },\n    {\n      \"index\": 8,\n      \"name\": \"RequiredItemLevel\"\n    },\n    {\n      \"index\": 9,\n      \"name\": \"ConditionParam[0]\"\n    },\n    {\n      \"index\": 10,\n      \"name\": \"ConditionParam[1]\"\n    },\n    {\n      \"index\": 11,\n      \"name\": \"RequiredGathering\"\n    },\n    {\n      \"index\": 13,\n      \"name\": \"Task\",\n      \"converter\": {\n        \"type\": \"multiref\",\n        \"targets\": [\n          \"RetainerTaskNormal\",\n          \"RetainerTaskRandom\"\n        ]\n      }\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/RetainerTaskLvRange.json",
    "content": "{\n  \"sheet\": \"RetainerTaskLvRange\",\n  \"definitions\": [\n    {\n      \"name\": \"Min\"\n    },\n    {\n      \"index\": 1,\n      \"name\": \"Max\"\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/RetainerTaskNormal.json",
    "content": "{\n  \"sheet\": \"RetainerTaskNormal\",\n  \"defaultColumn\": \"Item\",\n  \"definitions\": [\n    {\n      \"name\": \"Item\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"Item\"\n      }\n    },\n    {\n      \"index\": 1,\n      \"type\": \"repeat\",\n      \"count\": 5,\n      \"definition\": {\n        \"name\": \"Quantity\"\n      }\n    },\n    {\n      \"index\": 6,\n      \"name\": \"GatheringLog\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"GatheringItem\"\n      }\n    },\n    {\n      \"index\": 7,\n      \"name\": \"FishingLog\",\n      \"converter\": {\n        \"type\": \"multiref\",\n        \"targets\": [\n          \"SpearfishingItem\",\n          \"FishParameter\"\n        ]\n      }\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/RetainerTaskParameter.json",
    "content": "{\n  \"sheet\": \"RetainerTaskParameter\",\n  \"definitions\": [\n    {\n      \"type\": \"repeat\",\n      \"count\": 4,\n      \"definition\": {\n        \"name\": \"ItemLevel{DoW}\"\n      }\n    },\n    {\n      \"index\": 4,\n      \"type\": \"repeat\",\n      \"count\": 4,\n      \"definition\": {\n        \"name\": \"Perception{DoL}\"\n      }\n    },\n    {\n      \"index\": 8,\n      \"type\": \"repeat\",\n      \"count\": 4,\n      \"definition\": {\n        \"name\": \"Perception{FSH}\"\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "SaintCoinach/Definitions/RetainerTaskRandom.json",
    "content": "{\n  \"sheet\": \"RetainerTaskRandom\",\n  \"defaultColumn\": \"Name\",\n  \"definitions\": [\n    {\n      \"name\": \"Name\"\n    },\n    {\n      \"index\": 1,\n      \"name\": \"Requirement\"\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/RideShooting.json",
    "content": "{\n  \"sheet\": \"RideShooting\",\n  \"definitions\": [\n    {\n      \"name\": \"GFateRideShooting\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"GFateRideShooting\"\n      }\n    },\n    {\n      \"index\": 5,\n      \"name\": \"StartText\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"RideShootingTextData\"\n      }\n    },\n    {\n      \"index\": 14,\n      \"type\": \"repeat\",\n      \"count\": 8,\n      \"definition\": {\n        \"name\": \"PopRange\"\n      }\n    },\n    {\n      \"index\": 22,\n      \"type\": \"repeat\",\n      \"count\": 8,\n      \"definition\": {\n        \"name\": \"ENpc\",\n        \"converter\": {\n          \"type\": \"link\",\n          \"target\": \"ENpcBase\"\n        }\n      }\n    },\n    {\n      \"index\": 30,\n      \"type\": \"repeat\",\n      \"count\": 8,\n      \"definition\": {\n        \"name\": \"ENpcScale\"\n      }\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/RideShootingTargetType.json",
    "content": "{\n  \"sheet\": \"RideShootingTargetType\",\n  \"definitions\": [\n    {\n      \"name\": \"EObj\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"EObj\"\n      }\n    },\n    {\n      \"index\": 1,\n      \"name\": \"Score\"\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/RideShootingTextData.json",
    "content": "{\n  \"sheet\": \"RideShootingTextData\",\n  \"defaultColumn\": \"String\",\n  \"definitions\": [\n    {\n      \"name\": \"String\"\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/SatisfactionArbitration.json",
    "content": "{\n  \"sheet\": \"SatisfactionArbitration\",\n  \"defaultColumn\": \"Quest\",\n  \"definitions\": [\n    {\n      \"name\": \"SatisfactionLevel\"\n    },\n    {\n      \"index\": 1,\n      \"name\": \"SatisfactionNpc\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"SatisfactionNpc\"\n      }\n    },\n    {\n      \"index\": 2,\n      \"name\": \"Quest\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"Quest\"\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "SaintCoinach/Definitions/SatisfactionBonusGuarantee.json",
    "content": "{\n  \"sheet\": \"SatisfactionBonusGuarantee\",\n  \"definitions\": []\n}"
  },
  {
    "path": "SaintCoinach/Definitions/SatisfactionNpc.json",
    "content": "{\n  \"sheet\": \"SatisfactionNpc\",\n  \"defaultColumn\": \"Npc\",\n  \"definitions\": [\n    {\n      \"name\": \"Npc\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"ENpcResident\"\n      }\n    },\n    {\n      \"index\": 1,\n      \"name\": \"Quest{Required}\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"Quest\"\n      }\n    },\n    {\n      \"index\": 2,\n      \"name\": \"LevelUnlock\"\n    },\n    {\n      \"index\": 3,\n      \"name\": \"DeliveriesPerWeek\"\n    },\n    {\n      \"index\": 4,\n      \"type\": \"repeat\",\n      \"count\": 6,\n      \"definition\": {\n        \"name\": \"SupplyIndex\"\n      }\n    },\n    {\n      \"index\": 10,\n      \"type\": \"repeat\",\n      \"count\": 6,\n      \"definition\": {\n        \"name\": \"Satisfaction{Required}\"\n      }\n    },\n    {\n      \"index\": 16,\n      \"type\": \"repeat\",\n      \"count\": 3,\n      \"definition\": {\n        \"type\": \"group\",\n        \"members\": [\n          {\n            \"type\": \"repeat\",\n            \"count\": 6,\n            \"definition\": {\n              \"name\": \"Item\",\n              \"converter\": {\n                \"type\": \"link\",\n                \"target\": \"Item\"\n              }\n            }\n          },\n          {\n            \"type\": \"repeat\",\n            \"count\": 6,\n            \"definition\": {\n              \"name\": \"ItemCount\"\n            }\n          },\n          {\n            \"type\": \"repeat\",\n            \"count\": 6,\n            \"definition\": {\n              \"name\": \"IsHQ\"\n            }\n          }\n        ]\n      }\n    },\n    {\n      \"index\": 88,\n      \"name\": \"Icon\",\n      \"converter\": {\n        \"type\": \"icon\"\n      }\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/SatisfactionSupply.json",
    "content": "{\n  \"sheet\": \"SatisfactionSupply\",\n  \"defaultColumn\": \"Item\",\n  \"definitions\": [\n    {\n      \"name\": \"Slot\"\n    },\n    {\n      \"index\": 1,\n      \"name\": \"Probability<%>\"\n    },\n    {\n      \"index\": 2,\n      \"name\": \"Item\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"Item\"\n      }\n    },\n    {\n      \"index\": 3,\n      \"name\": \"Collectability{Low}\"\n    },\n    {\n      \"index\": 4,\n      \"name\": \"Collectability{Mid}\"\n    },\n    {\n      \"index\": 5,\n      \"name\": \"Collectability{High}\"\n    },\n    {\n      \"index\": 6,\n      \"name\": \"Reward\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"SatisfactionSupplyReward\"\n      }\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/SatisfactionSupplyReward.json",
    "content": "{\n  \"sheet\": \"SatisfactionSupplyReward\",\n  \"definitions\": [\n    {\n      \"index\": 1,\n      \"type\": \"repeat\",\n      \"count\": 2,\n      \"definition\": {\n        \"type\": \"group\",\n        \"members\": [\n          {\n            \"name\": \"Reward{Currency}\"\n          },\n          {\n            \"name\": \"Quantity{Low}\"\n          },\n          {\n            \"name\": \"Quantity{Mid}\"\n          },\n          {\n            \"name\": \"Quantity{High}\"\n          }\n        ]\n      }\n    },\n    {\n      \"index\": 10,\n      \"name\": \"Satisfaction{Low}\"\n    },\n    {\n      \"index\": 11,\n      \"name\": \"Satisfaction{Mid}\"\n    },\n    {\n      \"index\": 12,\n      \"name\": \"Satisfaction{High}\"\n    },\n    {\n      \"index\": 13,\n      \"name\": \"Gil{Low}\"\n    },\n    {\n      \"index\": 14,\n      \"name\": \"Gil{Mid}\"\n    },\n    {\n      \"index\": 15,\n      \"name\": \"Gil{High}\"\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/ScenarioTree.json",
    "content": "{\n  \"sheet\": \"ScenarioTree\",\n  \"defaultColumn\": \"Name\",\n  \"definitions\": [\n    {\n      \"name\": \"Type\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"ScenarioType\"\n      }\n    },\n    {\n      \"index\": 2,\n      \"name\": \"Addon\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"Addon\"\n      }\n    },\n    {\n      \"index\": 3,\n      \"name\": \"QuestChapter\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"QuestChapter\"\n      }\n    },\n    {\n      \"index\": 4,\n      \"name\": \"Name\"\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/ScenarioTreeTips.json",
    "content": "{\n  \"sheet\": \"ScenarioTreeTips\",\n  \"defaultColumn\": \"Type\",\n  \"definitions\": [\n    {\n      \"index\": 1,\n      \"name\": \"Tips1\"\n    },\n    {\n      \"index\": 3,\n      \"name\": \"Tips2\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"ScenarioTree\"\n      }\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/ScenarioTreeTipsClassQuest.json",
    "content": "{\n  \"sheet\": \"ScenarioTreeTipsClassQuest\",\n  \"defaultColumn\": \"Quest\",\n  \"definitions\": [\n    {\n      \"name\": \"Quest\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"Quest\"\n      }\n    },\n    {\n      \"index\": 1,\n      \"name\": \"RequiredLevel\"\n    },\n    {\n      \"index\": 2,\n      \"name\": \"RequiredExpansion\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"ExVersion\"\n      }\n    },\n    {\n      \"index\": 3,\n      \"name\": \"RequiredQuest\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"Quest\"\n      }\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/ScenarioType.json",
    "content": "{\n  \"sheet\": \"ScenarioType\",\n  \"defaultColumn\": \"Type\",\n  \"definitions\": [\n    {\n      \"name\": \"Type\"\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/ScreenImage.json",
    "content": "{\n  \"sheet\": \"ScreenImage\",\n  \"definitions\": [\n    {\n      \"name\": \"Image\",\n      \"converter\": {\n        \"type\": \"icon\"\n      }\n    },\n    {\n      \"index\": 1,\n      \"name\": \"Jingle\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"Jingle\"\n      }\n    },\n    {\n      \"index\": 2,\n      \"name\": \"Type\"\n    },\n    {\n      \"index\": 3,\n      \"name\": \"Lang\"\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/SecretRecipeBook.json",
    "content": "{\n  \"sheet\": \"SecretRecipeBook\",\n  \"defaultColumn\": \"Name\",\n  \"definitions\": [\n    {\n      \"name\": \"Item\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"Item\"\n      }\n    },\n    {\n      \"index\": 1,\n      \"name\": \"Name\"\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/SharlayanCraftWorks.json",
    "content": "{\n  \"sheet\": \"SharlayanCraftWorks\",\n  \"definitions\": []\n}"
  },
  {
    "path": "SaintCoinach/Definitions/SharlayanCraftWorksSupply.json",
    "content": "{\n  \"sheet\": \"SharlayanCraftWorksSupply\",\n  \"definitions\": []\n}"
  },
  {
    "path": "SaintCoinach/Definitions/ShellFixedFromCommand.json",
    "content": "{\n  \"sheet\": \"ShellFixedFromCommand\",\n  \"definitions\": []\n}"
  },
  {
    "path": "SaintCoinach/Definitions/SkyIsland2Mission.json",
    "content": "{\n  \"sheet\": \"SkyIsland2Mission\",\n  \"defaultColumn\": \"Item{1}\",\n  \"definitions\": [\n    {\n      \"name\": \"Item{1}\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"EventItem\"\n      }\n    },\n    {\n      \"index\": 1,\n      \"name\": \"Item{2}\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"EventItem\"\n      }\n    },\n    {\n      \"index\": 2,\n      \"name\": \"PlaceName\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"PlaceName\"\n      }\n    },\n    {\n      \"index\": 4,\n      \"name\": \"Objective{1}\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"SkyIsland2MissionDetail\"\n      }\n    },\n    {\n      \"index\": 5,\n      \"name\": \"PopRange[0]\"\n    },\n    {\n      \"index\": 6,\n      \"name\": \"RequiredAmount{1}\"\n    },\n    {\n      \"index\": 9,\n      \"name\": \"Objective2\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"SkyIsland2MissionDetail\"\n      }\n    },\n    {\n      \"index\": 10,\n      \"name\": \"PopRange[1]\"\n    },\n    {\n      \"index\": 11,\n      \"name\": \"RequiredAmount{2}\"\n    },\n    {\n      \"index\": 14,\n      \"name\": \"Objective3\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"SkyIsland2MissionDetail\"\n      }\n    },\n    {\n      \"index\": 15,\n      \"name\": \"PopRange[2]\"\n    },\n    {\n      \"index\": 20,\n      \"name\": \"Image\",\n      \"converter\": {\n        \"type\": \"icon\"\n      }\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/SkyIsland2MissionDetail.json",
    "content": "{\n  \"sheet\": \"SkyIsland2MissionDetail\",\n  \"defaultColumn\": \"Objective\",\n  \"definitions\": [\n    {\n      \"name\": \"Type\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"SkyIsland2MissionType\"\n      }\n    },\n    {\n      \"index\": 2,\n      \"name\": \"Range\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"SkyIsland2RangeType\"\n      }\n    },\n    {\n      \"index\": 4,\n      \"name\": \"EObj\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"EObjName\"\n      }\n    },\n    {\n      \"index\": 7,\n      \"name\": \"Objective\"\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/SkyIsland2MissionType.json",
    "content": "{\n  \"sheet\": \"SkyIsland2MissionType\",\n  \"defaultColumn\": \"Type\",\n  \"definitions\": [\n    {\n      \"name\": \"Type\"\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/SkyIsland2RangeType.json",
    "content": "{\n  \"sheet\": \"SkyIsland2RangeType\",\n  \"defaultColumn\": \"Type\",\n  \"definitions\": [\n    {\n      \"name\": \"Type\"\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/Snipe.json",
    "content": "{\n  \"sheet\": \"Snipe\",\n  \"definitions\": [\n    {\n      \"name\": \"LGB{TargetMarker}\"\n    },\n    {\n      \"index\": 11,\n      \"name\": \"VFX{Fire}\"\n    },\n    {\n      \"index\": 12,\n      \"name\": \"VFX{Hit}\"\n    },\n    {\n      \"index\": 13,\n      \"name\": \"VFX{Miss}\"\n    },\n    {\n      \"index\": 14,\n      \"name\": \"VFX{Additional}\"\n    },\n    {\n      \"index\": 17,\n      \"type\": \"repeat\",\n      \"count\": 8,\n      \"definition\": {\n        \"name\": \"LGB{EventNPC}[0]\"\n      }\n    },\n    {\n      \"index\": 73,\n      \"type\": \"repeat\",\n      \"count\": 8,\n      \"definition\": {\n        \"name\": \"LGB{EventNPC}[1]\"\n      }\n    },\n    {\n      \"index\": 93,\n      \"name\": \"Objective[0]\"\n    },\n    {\n      \"index\": 94,\n      \"name\": \"Hint[0]\"\n    },\n    {\n      \"index\": 95,\n      \"name\": \"Objective[1]\"\n    },\n    {\n      \"index\": 96,\n      \"name\": \"Hint[1]\"\n    },\n    {\n      \"index\": 104,\n      \"name\": \"ActionText\"\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/SnipeTalk.json",
    "content": "{\n  \"sheet\": \"SnipeTalk\",\n  \"definitions\": [\n    {\n      \"index\": 2,\n      \"name\": \"Name\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"SnipeTalkName\"\n      }\n    },\n    {\n      \"index\": 3,\n      \"name\": \"Text\"\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/SnipeTalkName.json",
    "content": "{\n  \"sheet\": \"SnipeTalkName\",\n  \"defaultColumn\": \"Name\",\n  \"definitions\": [\n    {\n      \"name\": \"Name\"\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/SpearfishingComboTarget.json",
    "content": "{\n  \"sheet\": \"SpearfishingComboTarget\",\n  \"definitions\": []\n}"
  },
  {
    "path": "SaintCoinach/Definitions/SpearfishingItem.json",
    "content": "{\n  \"sheet\": \"SpearfishingItem\",\n  \"defaultColumn\": \"Item\",\n  \"definitions\": [\n    {\n      \"name\": \"Description\"\n    },\n    {\n      \"index\": 1,\n      \"name\": \"Item\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"Item\"\n      }\n    },\n    {\n      \"index\": 2,\n      \"name\": \"GatheringItemLevel\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"GatheringItemLevelConvertTable\"\n      }\n    },\n    {\n      \"index\": 5,\n      \"name\": \"FishingRecordType\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"FishingRecordType\"\n      }\n    },\n    {\n      \"index\": 6,\n      \"name\": \"TerritoryType\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"TerritoryType\"\n      }\n    },\n    {\n      \"index\": 8,\n      \"name\": \"IsVisible\"\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/SpearfishingNotebook.json",
    "content": "{\n  \"sheet\": \"SpearfishingNotebook\",\n  \"defaultColumn\": \"PlaceName\",\n  \"definitions\": [\n    {\n      \"name\": \"GatheringLevel\"\n    },\n    {\n      \"index\": 1,\n      \"name\": \"IsShadowNode\"\n    },\n    {\n      \"index\": 2,\n      \"name\": \"TerritoryType\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"TerritoryType\"\n      }\n    },\n    {\n      \"index\": 3,\n      \"name\": \"X\"\n    },\n    {\n      \"index\": 4,\n      \"name\": \"Y\"\n    },\n    {\n      \"index\": 5,\n      \"name\": \"Radius\"\n    },\n    {\n      \"index\": 7,\n      \"name\": \"PlaceName\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"PlaceName\"\n      }\n    },\n    {\n      \"index\": 9,\n      \"name\": \"GatheringPointBase\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"GatheringPointBase\"\n      }\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/SpearfishingRecordPage.json",
    "content": "{\n  \"sheet\": \"SpearfishingRecordPage\",\n  \"definitions\": [\n    {\n      \"index\": 3,\n      \"name\": \"PlaceName\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"PlaceName\"\n      }\n    },\n    {\n      \"index\": 4,\n      \"name\": \"Image\",\n      \"converter\": {\n        \"type\": \"icon\"\n      }\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/SpearfishingSilhouette.json",
    "content": "{\n  \"sheet\": \"SpearfishingSilhouette\",\n  \"definitions\": []\n}"
  },
  {
    "path": "SaintCoinach/Definitions/SpecialShop.json",
    "content": "{\n  \"sheet\": \"SpecialShop\",\n  \"defaultColumn\": \"Name\",\n  \"isGenericReferenceTarget\": true,\n  \"definitions\": [\n    {\n      \"name\": \"Name\"\n    },\n    {\n      \"index\": 1,\n      \"type\": \"repeat\",\n      \"count\": 2,\n      \"definition\": {\n        \"type\": \"group\",\n        \"members\": [\n          {\n            \"type\": \"repeat\",\n            \"count\": 60,\n            \"definition\": {\n              \"name\": \"Item{Receive}\",\n              \"converter\": {\n                \"type\": \"link\",\n                \"target\": \"Item\"\n              }\n            }\n          },\n          {\n            \"type\": \"repeat\",\n            \"count\": 60,\n            \"definition\": {\n              \"name\": \"Count{Receive}\"\n            }\n          },\n          {\n            \"type\": \"repeat\",\n            \"count\": 60,\n            \"definition\": {\n              \"name\": \"SpecialShopItemCategory\",\n              \"converter\": {\n                \"type\": \"link\",\n                \"target\": \"SpecialShopItemCategory\"\n              }\n            }\n          },\n          {\n            \"type\": \"repeat\",\n            \"count\": 60,\n            \"definition\": {\n              \"name\": \"HQ{Receive}\"\n            }\n          }\n        ]\n      }\n    },\n    {\n      \"index\": 481,\n      \"type\": \"repeat\",\n      \"count\": 3,\n      \"definition\": {\n        \"type\": \"group\",\n        \"members\": [\n          {\n            \"type\": \"repeat\",\n            \"count\": 60,\n            \"definition\": {\n              \"name\": \"Item{Cost}\",\n              \"converter\": {\n                \"type\": \"link\",\n                \"target\": \"Item\"\n              }\n            }\n          },\n          {\n            \"type\": \"repeat\",\n            \"count\": 60,\n            \"definition\": {\n              \"name\": \"Count{Cost}\"\n            }\n          },\n          {\n            \"type\": \"repeat\",\n            \"count\": 60,\n            \"definition\": {\n              \"name\": \"HQ{Cost}\"\n            }\n          },\n          {\n            \"type\": \"repeat\",\n            \"count\": 60,\n            \"definition\": {\n              \"name\": \"CollectabilityRating{Cost}\"\n            }\n          }\n        ]\n      }\n    },\n    {\n      \"index\": 1201,\n      \"type\": \"repeat\",\n      \"count\": 60,\n      \"definition\": {\n        \"name\": \"Quest{Item}\",\n        \"converter\": {\n          \"type\": \"link\",\n          \"target\": \"Quest\"\n        }\n      }\n    },\n    {\n      \"index\": 1741,\n      \"type\": \"repeat\",\n      \"count\": 60,\n      \"definition\": {\n        \"name\": \"AchievementUnlock\",\n        \"converter\": {\n          \"type\": \"link\",\n          \"target\": \"Achievement\"\n        }\n      }\n    },\n    {\n      \"index\": 1981,\n      \"type\": \"repeat\",\n      \"count\": 60,\n      \"definition\": {\n        \"name\": \"PatchNumber\"\n      }\n    },\n    {\n      \"index\": 2041,\n      \"name\": \"UseCurrencyType\"\n    },\n    {\n      \"index\": 2042,\n      \"name\": \"Quest{Unlock}\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"Quest\"\n      }\n    },\n    {\n      \"index\": 2043,\n      \"name\": \"CompleteText\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"DefaultTalk\"\n      }\n    },\n    {\n      \"index\": 2044,\n      \"name\": \"NotCompleteText\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"DefaultTalk\"\n      }\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/SpecialShopItemCategory.json",
    "content": "{\n  \"sheet\": \"SpecialShopItemCategory\",\n  \"defaultColumn\": \"Name\",\n  \"definitions\": [\n    {\n      \"name\": \"Name\"\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/Stain.json",
    "content": "{\n  \"sheet\": \"Stain\",\n  \"defaultColumn\": \"Name\",\n  \"definitions\": [\n    {\n      \"name\": \"Color\",\n      \"converter\": {\n        \"type\": \"color\"\n      }\n    },\n    {\n      \"index\": 1,\n      \"name\": \"Shade\"\n    },\n    {\n      \"index\": 2,\n      \"name\": \"SubOrder\"\n    },\n    {\n      \"index\": 3,\n      \"name\": \"Name\"\n    },\n    {\n      \"index\": 4,\n      \"name\": \"Name2\"\n    }\n  ]\n}\n"
  },
  {
    "path": "SaintCoinach/Definitions/StainTransient.json",
    "content": "{\n  \"sheet\": \"StainTransient\",\n  \"defaultColumn\": \"Item{1}\",\n  \"definitions\": [\n    {\n      \"name\": \"Item{1}\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"Item\"\n      }\n    },\n    {\n      \"index\": 1,\n      \"name\": \"Item{2}\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"Item\"\n      }\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/StanceChange.json",
    "content": "{\n  \"sheet\": \"StanceChange\",\n  \"defaultColumn\": \"Action[0]\",\n  \"definitions\": [\n    {\n      \"index\": 1,\n      \"type\": \"repeat\",\n      \"count\": 2,\n      \"definition\": {\n        \"name\": \"Action\",\n        \"converter\": {\n          \"type\": \"link\",\n          \"target\": \"Action\"\n        }\n      }\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/Status.json",
    "content": "{\n  \"sheet\": \"Status\",\n  \"defaultColumn\": \"Name\",\n  \"definitions\": [\n    {\n      \"name\": \"Name\"\n    },\n    {\n      \"index\": 1,\n      \"name\": \"Description\"\n    },\n    {\n      \"index\": 2,\n      \"name\": \"Icon\",\n      \"converter\": {\n        \"type\": \"icon\"\n      }\n    },\n    {\n      \"index\": 4,\n      \"name\": \"MaxStacks\"\n    },\n    {\n      \"index\": 5,\n      \"name\": \"ClassJobCategory\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"ClassJobCategory\"\n      }\n    },\n    {\n      \"index\": 6,\n      \"name\": \"StatusCategory\"\n    },\n    {\n      \"index\": 7,\n      \"name\": \"HitEffect\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"StatusHitEffect\"\n      }\n    },\n    {\n      \"index\": 8,\n      \"name\": \"VFX\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"StatusLoopVFX\"\n      }\n    },\n    {\n      \"index\": 9,\n      \"name\": \"LockMovement\"\n    },\n    {\n      \"index\": 11,\n      \"name\": \"LockActions\"\n    },\n    {\n      \"index\": 12,\n      \"name\": \"LockControl\"\n    },\n    {\n      \"index\": 13,\n      \"name\": \"Transfiguration\"\n    },\n    {\n      \"index\": 14,\n      \"name\": \"IsGaze\"\n    },\n    {\n      \"index\": 15,\n      \"name\": \"CanDispel\"\n    },\n    {\n      \"index\": 16,\n      \"name\": \"InflictedByActor\"\n    },\n    {\n      \"index\": 17,\n      \"name\": \"IsPermanent\"\n    },\n    {\n      \"index\": 18,\n      \"name\": \"PartyListPriority\"\n    },\n    {\n      \"index\": 19,\n      \"name\": \"CanIncreaseRewards\"\n    },\n    {\n      \"index\": 22,\n      \"name\": \"ParamModifier\"\n    },\n    {\n      \"index\": 23,\n      \"name\": \"ParamEffect\"\n    },\n    {\n      \"index\": 24,\n      \"name\": \"CanStatusOff\"\n    },\n    {\n      \"index\": 25,\n      \"name\": \"Log\"\n    },\n    {\n      \"index\": 26,\n      \"name\": \"IsFcBuff\"\n    },\n    {\n      \"index\": 28,\n      \"name\": \"Invisibility\"\n    },\n    {\n      \"index\": 29,\n      \"name\": \"TargetType\"\n    },\n    {\n      \"index\": 30,\n      \"name\": \"Flags\"\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/StatusHitEffect.json",
    "content": "{\n  \"sheet\": \"StatusHitEffect\",\n  \"defaultColumn\": \"Location\",\n  \"definitions\": [\n    {\n      \"name\": \"Location\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"VFX\"\n      }\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/StatusLoopVFX.json",
    "content": "{\n  \"sheet\": \"StatusLoopVFX\",\n  \"defaultColumn\": \"VFX\",\n  \"definitions\": [\n    {\n      \"name\": \"FriendlyVFX\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"VFX\"\n      }\n    },\n    {\n      \"index\": 1,\n      \"name\": \"StackVFX1Trigger\"\n    },\n    {\n      \"index\": 2,\n      \"name\": \"StackVFX1\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"VFX\"\n      }\n    },\n    {\n      \"index\": 3,\n      \"name\": \"StackVFX2Trigger\"\n    },\n    {\n      \"index\": 4,\n      \"name\": \"StackVFX2\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"VFX\"\n      }\n    },\n    {\n      \"index\": 5,\n      \"name\": \"HostileVFX\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"VFX\"\n      }\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/Story.json",
    "content": "{\n  \"sheet\": \"Story\",\n  \"isGenericReferenceTarget\": true,\n  \"definitions\": [\n    {\n      \"name\": \"Script\"\n    },\n    {\n      \"index\": 1,\n      \"type\": \"repeat\",\n      \"count\": 40,\n      \"definition\": {\n        \"name\": \"Instruction\"\n      }\n    },\n    {\n      \"index\": 41,\n      \"type\": \"repeat\",\n      \"count\": 40,\n      \"definition\": {\n        \"name\": \"Argument\"\n      }\n    },\n    {\n      \"index\": 81,\n      \"type\": \"repeat\",\n      \"count\": 110,\n      \"definition\": {\n        \"name\": \"Sequence\"\n      }\n    },\n    {\n      \"index\": 191,\n      \"type\": \"repeat\",\n      \"count\": 110,\n      \"definition\": {\n        \"name\": \"CompletedQuestOperator\"\n      }\n    },\n    {\n      \"index\": 301,\n      \"type\": \"repeat\",\n      \"count\": 110,\n      \"definition\": {\n        \"name\": \"CompletedQuest[0]\",\n        \"converter\": {\n          \"type\": \"link\",\n          \"target\": \"Quest\"\n        }\n      }\n    },\n    {\n      \"index\": 411,\n      \"type\": \"repeat\",\n      \"count\": 110,\n      \"definition\": {\n        \"name\": \"CompletedQuest[1]\",\n        \"converter\": {\n          \"type\": \"link\",\n          \"target\": \"Quest\"\n        }\n      }\n    },\n    {\n      \"index\": 521,\n      \"type\": \"repeat\",\n      \"count\": 110,\n      \"definition\": {\n        \"name\": \"CompletedQuest[2]\",\n        \"converter\": {\n          \"type\": \"link\",\n          \"target\": \"Quest\"\n        }\n      }\n    },\n    {\n      \"index\": 631,\n      \"type\": \"repeat\",\n      \"count\": 110,\n      \"definition\": {\n        \"name\": \"AcceptedQuestOperator\"\n      }\n    },\n    {\n      \"index\": 741,\n      \"type\": \"repeat\",\n      \"count\": 110,\n      \"definition\": {\n        \"name\": \"AcceptedQuest[0]\",\n        \"converter\": {\n          \"type\": \"link\",\n          \"target\": \"Quest\"\n        }\n      }\n    },\n    {\n      \"index\": 851,\n      \"type\": \"repeat\",\n      \"count\": 110,\n      \"definition\": {\n        \"name\": \"AcceptedQuestSequence[0]\"\n      }\n    },\n    {\n      \"index\": 961,\n      \"type\": \"repeat\",\n      \"count\": 110,\n      \"definition\": {\n        \"name\": \"AcceptedQuest[1]\",\n        \"converter\": {\n          \"type\": \"link\",\n          \"target\": \"Quest\"\n        }\n      }\n    },\n    {\n      \"index\": 1071,\n      \"type\": \"repeat\",\n      \"count\": 110,\n      \"definition\": {\n        \"name\": \"AcceptedQuestSequence[1]\"\n      }\n    },\n    {\n      \"index\": 1181,\n      \"type\": \"repeat\",\n      \"count\": 110,\n      \"definition\": {\n        \"name\": \"AcceptedQuest[2]\",\n        \"converter\": {\n          \"type\": \"link\",\n          \"target\": \"Quest\"\n        }\n      }\n    },\n    {\n      \"index\": 1291,\n      \"type\": \"repeat\",\n      \"count\": 110,\n      \"definition\": {\n        \"name\": \"AcceptedQuestSequence[2]\"\n      }\n    },\n    {\n      \"index\": 1401,\n      \"type\": \"repeat\",\n      \"count\": 110,\n      \"definition\": {\n        \"name\": \"LayerSet[0]\"\n      }\n    },\n    {\n      \"index\": 1511,\n      \"type\": \"repeat\",\n      \"count\": 110,\n      \"definition\": {\n        \"name\": \"LayerSet[1]\"\n      }\n    },\n    {\n      \"index\": 1621,\n      \"type\": \"repeat\",\n      \"count\": 80,\n      \"definition\": {\n        \"name\": \"SequenceBegin\"\n      }\n    },\n    {\n      \"index\": 1701,\n      \"type\": \"repeat\",\n      \"count\": 80,\n      \"definition\": {\n        \"name\": \"SequenceEnd\"\n      }\n    },\n    {\n      \"index\": 1781,\n      \"type\": \"repeat\",\n      \"count\": 80,\n      \"definition\": {\n        \"name\": \"Listener\",\n        \"converter\": {\n          \"type\": \"multiref\",\n          \"targets\": [\n            \"EObjName\",\n            \"ENpcResident\"\n          ]\n        }\n      }\n    },\n    {\n      \"index\": 1861,\n      \"name\": \"LayerSetTerritoryType[0]\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"TerritoryType\"\n      }\n    },\n    {\n      \"index\": 1862,\n      \"name\": \"LayerSetTerritoryType[1]\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"TerritoryType\"\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "SaintCoinach/Definitions/SubmarineExploration.json",
    "content": "{\n  \"sheet\": \"SubmarineExploration\",\n  \"defaultColumn\": \"Destination\",\n  \"definitions\": [\n    {\n      \"name\": \"Destination\"\n    },\n    {\n      \"index\": 1,\n      \"name\": \"Location\"\n    },\n    {\n      \"index\": 2,\n      \"name\": \"X\"\n    },\n    {\n      \"index\": 3,\n      \"name\": \"Y\"\n    },\n    {\n      \"index\": 4,\n      \"name\": \"Z\"\n    },\n    {\n      \"index\": 5,\n      \"name\": \"Map\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"SubmarineMap\"\n      }\n    },\n    {\n      \"index\": 6,\n      \"name\": \"StartingPoint\"\n    },\n    {\n      \"index\": 7,\n      \"name\": \"Stars\"\n    },\n    {\n      \"index\": 8,\n      \"name\": \"RankReq\"\n    },\n    {\n      \"index\": 9,\n      \"name\": \"CeruleumTankReq\"\n    },\n    {\n      \"index\": 10,\n      \"name\": \"SurveyDuration<min>\"\n    },\n    {\n      \"index\": 11,\n      \"name\": \"SurveyDistance\"\n    },\n    {\n      \"index\": 12,\n      \"name\": \"ExpReward\"\n    }\n  ]\n}\n"
  },
  {
    "path": "SaintCoinach/Definitions/SubmarineMap.json",
    "content": "{\n  \"sheet\": \"SubmarineMap\",\n  \"defaultColumn\": \"Name\",\n  \"definitions\": [\n    {\n      \"name\": \"Name\"\n    },\n    {\n      \"index\": 1,\n      \"name\": \"Image\",\n      \"converter\": {\n        \"type\": \"icon\"\n      }\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/SubmarinePart.json",
    "content": "{\n  \"sheet\": \"SubmarinePart\",\n  \"defaultColumn\": \"Slot\",\n  \"definitions\": [\n    {\n      \"name\": \"Slot\"\n    },\n    {\n      \"index\": 1,\n      \"name\": \"Rank\"\n    },\n    {\n      \"index\": 2,\n      \"name\": \"Components\"\n    },\n    {\n      \"index\": 3,\n      \"name\": \"Surveillance\"\n    },\n    {\n      \"index\": 4,\n      \"name\": \"Retrieval\"\n    },\n    {\n      \"index\": 5,\n      \"name\": \"Speed\"\n    },\n    {\n      \"index\": 6,\n      \"name\": \"Range\"\n    },\n    {\n      \"index\": 7,\n      \"name\": \"Favor\"\n    },\n    {\n      \"index\": 8,\n      \"name\": \"Class\"\n    },\n    {\n      \"index\": 9,\n      \"name\": \"RepairMaterials\"\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/SubmarineRank.json",
    "content": "{\n  \"sheet\": \"SubmarineRank\",\n  \"definitions\": [\n    {\n      \"name\": \"Capacity\"\n    },\n    {\n      \"index\": 1,\n      \"name\": \"ExpToNext\"\n    },\n    {\n      \"index\": 2,\n      \"name\": \"SurveillanceBonus\"\n    },\n    {\n      \"index\": 3,\n      \"name\": \"RetrievalBonus\"\n    },\n    {\n      \"index\": 4,\n      \"name\": \"SpeedBonus\"\n    },\n    {\n      \"index\": 5,\n      \"name\": \"RangeBonus\"\n    },\n    {\n      \"index\": 6,\n      \"name\": \"FavorBonus\"\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/SwitchTalk.json",
    "content": "{\n  \"sheet\": \"SwitchTalk\",\n  \"isGenericReferenceTarget\": true,\n  \"definitions\": []\n}"
  },
  {
    "path": "SaintCoinach/Definitions/SwitchTalkVariation.json",
    "content": "{\n  \"sheet\": \"SwitchTalkVariation\",\n  \"defaultColumn\": \"Quest\",\n  \"definitions\": [\n    {\n      \"name\": \"Quest[0]\",\n      \"converter\": {\n        \"type\": \"multiref\",\n        \"targets\": [\n          \"Quest\"\n        ]\n      }\n    },\n    {\n      \"index\": 1,\n      \"name\": \"Quest[1]\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"Quest\"\n      }\n    },\n    {\n      \"index\": 3,\n      \"name\": \"DefaultTalk\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"DefaultTalk\"\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "SaintCoinach/Definitions/TelepoRelay.json",
    "content": "{\n  \"sheet\": \"TelepoRelay\",\n  \"definitions\": [\n    {\n      \"index\": 0,\n      \"type\": \"repeat\",\n      \"count\": 8,\n      \"definition\": {\n        \"name\": \"TerritoryType{Entry}\",\n        \"converter\": {\n          \"type\": \"complexlink\",\n          \"links\": [\n            {\n              \"sheet\": \"TerritoryType\",\n              \"project\": \"PlaceName\"\n            }\n          ]\n        }\n      }\n    },\n    {\n      \"index\": 8,\n      \"type\": \"repeat\",\n      \"count\": 8,\n      \"definition\": {\n        \"name\": \"TerritoryType{Exit}\",\n        \"converter\": {\n          \"type\": \"complexlink\",\n          \"links\": [\n            {\n              \"sheet\": \"TerritoryType\",\n              \"project\": \"PlaceName\"\n            }\n          ]\n        }\n      }\n    },\n    {\n      \"index\": 16,\n      \"type\": \"repeat\",\n      \"count\": 8,\n      \"definition\": {\n        \"name\": \"Cost\"\n      }\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/TerritoryType.json",
    "content": "{\n  \"sheet\": \"TerritoryType\",\n  \"defaultColumn\": \"Name\",\n  \"definitions\": [\n    {\n      \"name\": \"Name\"\n    },\n    {\n      \"index\": 1,\n      \"name\": \"Bg\"\n    },\n    {\n      \"index\": 2,\n      \"name\": \"BattalionMode\"\n    },\n    {\n      \"index\": 3,\n      \"name\": \"PlaceName{Region}\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"PlaceName\"\n      }\n    },\n    {\n      \"index\": 4,\n      \"name\": \"PlaceName{Zone}\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"PlaceName\"\n      }\n    },\n    {\n      \"index\": 5,\n      \"name\": \"PlaceName\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"PlaceName\"\n      }\n    },\n    {\n      \"index\": 6,\n      \"name\": \"Map\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"Map\"\n      }\n    },\n    {\n      \"index\": 7,\n      \"name\": \"LoadingImage\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"LoadingImage\"\n      }\n    },\n    {\n      \"index\": 8,\n      \"name\": \"ExclusiveType\"\n    },\n    {\n      \"index\": 9,\n      \"name\": \"TerritoryIntendedUse\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"TerritoryIntendedUse\"\n      }\n    },\n    {\n      \"index\": 10,\n      \"name\": \"ContentFinderCondition\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"ContentFinderCondition\"\n      }\n    },\n    {\n      \"index\": 12,\n      \"name\": \"WeatherRate\"\n    },\n    {\n      \"index\": 15,\n      \"name\": \"PCSearch\"\n    },\n    {\n      \"index\": 16,\n      \"name\": \"Stealth\"\n    },\n    {\n      \"index\": 17,\n      \"name\": \"Mount\"\n    },\n    {\n      \"index\": 19,\n      \"name\": \"BGM\",\n      \"converter\": {\n        \"type\": \"multiref\",\n        \"targets\": [\n          \"BGM\",\n          \"BGMSituation\"\n        ]\n      }\n    },\n    {\n      \"index\": 20,\n      \"name\": \"PlaceName{Region}Icon\",\n      \"converter\": {\n        \"type\": \"icon\"\n      }\n    },\n    {\n      \"index\": 21,\n      \"name\": \"PlaceNameIcon\",\n      \"converter\": {\n        \"type\": \"icon\"\n      }\n    },\n    {\n      \"index\": 22,\n      \"name\": \"ArrayEventHandler\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"ArrayEventHandler\"\n      }\n    },\n    {\n      \"index\": 23,\n      \"name\": \"QuestBattle\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"QuestBattle\"\n      }\n    },\n    {\n      \"index\": 24,\n      \"name\": \"Aetheryte\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"Aetheryte\"\n      }\n    },\n    {\n      \"index\": 25,\n      \"name\": \"FixedTime\"\n    },\n    {\n      \"index\": 26,\n      \"name\": \"Resident\"\n    },\n    {\n      \"index\": 27,\n      \"name\": \"AchievementIndex\"\n    },\n    {\n      \"index\": 28,\n      \"name\": \"IsPvpZone\"\n    },\n    {\n      \"index\": 29,\n      \"name\": \"ExVersion\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"ExVersion\"\n      }\n    },\n    {\n      \"index\": 33,\n      \"name\": \"MountSpeed\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"MountSpeed\"\n      }\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/TerritoryTypeTelepo.json",
    "content": "{\n  \"sheet\": \"TerritoryTypeTelepo\",\n  \"definitions\": [\n    {\n      \"index\": 0,\n      \"name\": \"X\"\n    },\n    {\n      \"index\": 1,\n      \"name\": \"Y\"\n    },\n    {\n      \"index\": 2,\n      \"name\": \"Expansion\"\n    },\n    {\n      \"index\": 3,\n      \"name\": \"TelepoRelay\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"TelepoRelay\"\n      }\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/TerritoryTypeTransient.json",
    "content": "{\n  \"sheet\": \"TerritoryTypeTransient\",\n  \"defaultColumn\": \"Offset{Z}\",\n  \"definitions\": [\n    {\n      \"name\": \"Offset{Z}\"\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/TextCommand.json",
    "content": "{\n  \"sheet\": \"TextCommand\",\n  \"defaultColumn\": \"Command\",\n  \"definitions\": [\n    {\n      \"index\": 5,\n      \"name\": \"Command\"\n    },\n    {\n      \"index\": 6,\n      \"name\": \"ShortCommand\"\n    },\n    {\n      \"index\": 7,\n      \"name\": \"Description\"\n    },\n    {\n      \"index\": 8,\n      \"name\": \"Alias\"\n    },\n    {\n      \"index\": 9,\n      \"name\": \"ShortAlias\"\n    },\n    {\n      \"index\": 10,\n      \"name\": \"Param\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"TextCommandParam\"\n      }\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/TextCommandParam.json",
    "content": "{\n  \"sheet\": \"TextCommandParam\",\n  \"defaultColumn\": \"Param\",\n  \"definitions\": [\n    {\n      \"name\": \"Param\"\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/TiltParam.json",
    "content": "{\n  \"sheet\": \"TiltParam\",\n  \"definitions\": [\n    {\n      \"index\": 0,\n      \"name\": \"RotationOriginOffset\"\n    },\n    {\n      \"index\": 1,\n      \"name\": \"MaxAngle\"\n    },\n    {\n      \"index\": 3,\n      \"name\": \"TiltRate\"\n    },\n    {\n      \"index\": 2,\n      \"name\": \"ReverseRotation\"\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/Title.json",
    "content": "{\n  \"sheet\": \"Title\",\n  \"defaultColumn\": \"Feminine\",\n  \"definitions\": [\n    {\n      \"name\": \"Masculine\"\n    },\n    {\n      \"index\": 1,\n      \"name\": \"Feminine\"\n    },\n    {\n      \"index\": 2,\n      \"name\": \"IsPrefix\"\n    },\n    {\n      \"index\": 3,\n      \"name\": \"Order\"\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/TofuEditParam.json",
    "content": "{\n  \"sheet\": \"TofuEditParam\",\n  \"definitions\": []\n}"
  },
  {
    "path": "SaintCoinach/Definitions/TofuObject.json",
    "content": "{\n  \"sheet\": \"TofuObject\",\n  \"definitions\": []\n}"
  },
  {
    "path": "SaintCoinach/Definitions/TofuObjectCategory.json",
    "content": "{\n  \"sheet\": \"TofuObjectCategory\",\n  \"definitions\": []\n}"
  },
  {
    "path": "SaintCoinach/Definitions/TofuPreset.json",
    "content": "{\n  \"sheet\": \"TofuPreset\",\n  \"definitions\": []\n}"
  },
  {
    "path": "SaintCoinach/Definitions/TofuPresetCategory.json",
    "content": "{\n  \"sheet\": \"TofuPresetCategory\",\n  \"definitions\": []\n}"
  },
  {
    "path": "SaintCoinach/Definitions/TofuPresetObject.json",
    "content": "{\n  \"sheet\": \"TofuPresetObject\",\n  \"definitions\": []\n}"
  },
  {
    "path": "SaintCoinach/Definitions/Tomestones.json",
    "content": "{\n  \"sheet\": \"Tomestones\",\n  \"definitions\": [\n    {\n      \"name\": \"WeeklyLimit\"\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/TomestonesItem.json",
    "content": "{\n  \"sheet\": \"TomestonesItem\",\n  \"defaultColumn\": \"Item\",\n  \"definitions\": [\n    {\n      \"name\": \"Item\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"Item\"\n      }\n    },\n    {\n      \"index\": 2,\n      \"name\": \"Tomestones\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"Tomestones\"\n      }\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/TopicSelect.json",
    "content": "{\n  \"sheet\": \"TopicSelect\",\n  \"defaultColumn\": \"Name\",\n  \"isGenericReferenceTarget\": true,\n  \"definitions\": [\n    {\n      \"name\": \"Name\"\n    },\n    {\n      \"index\": 4,\n      \"type\": \"repeat\",\n      \"count\": 10,\n      \"definition\": {\n        \"type\": \"group\",\n        \"members\": [\n          {\n            \"name\": \"Shop\",\n            \"converter\": {\n              \"type\": \"multiref\",\n              \"targets\": [\n                \"SpecialShop\",\n                \"GilShop\",\n                \"PreHandler\"\n              ]\n            }\n          }\n        ]\n      }\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/Town.json",
    "content": "{\n  \"sheet\": \"Town\",\n  \"defaultColumn\": \"Name\",\n  \"definitions\": [\n    {\n      \"name\": \"Name\"\n    },\n    {\n      \"index\": 1,\n      \"name\": \"Icon\",\n      \"converter\": {\n        \"type\": \"icon\"\n      }\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/Trait.json",
    "content": "{\n  \"sheet\": \"Trait\",\n  \"defaultColumn\": \"Name\",\n  \"definitions\": [\n    {\n      \"name\": \"Name\"\n    },\n    {\n      \"index\": 1,\n      \"name\": \"Icon\",\n      \"converter\": {\n        \"type\": \"icon\"\n      }\n    },\n    {\n      \"index\": 2,\n      \"name\": \"ClassJob\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"ClassJob\"\n      }\n    },\n    {\n      \"index\": 4,\n      \"name\": \"Level\"\n    },\n    {\n      \"index\": 5,\n      \"name\": \"Quest\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"Quest\"\n      }\n    },\n    {\n      \"index\": 6,\n      \"name\": \"Value\"\n    },\n    {\n      \"index\": 7,\n      \"name\": \"ClassJobCategory\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"ClassJobCategory\"\n      }\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/TraitRecast.json",
    "content": "{\n  \"sheet\": \"TraitRecast\",\n  \"defaultColumn\": \"Trait\",\n  \"definitions\": [\n    {\n      \"name\": \"Trait\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"Trait\"\n      }\n    },\n    {\n      \"index\": 1,\n      \"name\": \"Action\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"Action\"\n      }\n    },\n    {\n      \"index\": 2,\n      \"name\": \"Time<ds>\"\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/TraitTransient.json",
    "content": "{\n  \"sheet\": \"TraitTransient\",\n  \"defaultColumn\": \"Description\",\n  \"definitions\": [\n    {\n      \"name\": \"Description\"\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/Transformation.json",
    "content": "{\n  \"sheet\": \"Transformation\",\n  \"definitions\": [\n    {\n      \"index\": 1,\n      \"name\": \"Model\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"ModelChara\"\n      }\n    },\n    {\n      \"index\": 2,\n      \"name\": \"BNpcName\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"BNpcName\"\n      }\n    },\n    {\n      \"index\": 3,\n      \"name\": \"BNpcCustomize\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"BNpcCustomize\"\n      }\n    },\n    {\n      \"index\": 4,\n      \"name\": \"NpcEquip\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"NpcEquip\"\n      }\n    },\n    {\n      \"index\": 5,\n      \"name\": \"ExHotbarEnableConfig\"\n    },\n    {\n      \"index\": 6,\n      \"name\": \"Action[0]\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"Action\"\n      }\n    },\n    {\n      \"index\": 8,\n      \"name\": \"Action[1]\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"Action\"\n      }\n    },\n    {\n      \"index\": 10,\n      \"name\": \"Action[2]\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"Action\"\n      }\n    },\n    {\n      \"index\": 12,\n      \"name\": \"Action[3]\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"Action\"\n      }\n    },\n    {\n      \"index\": 14,\n      \"name\": \"Action[4]\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"Action\"\n      }\n    },\n    {\n      \"index\": 16,\n      \"name\": \"Action[5]\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"Action\"\n      }\n    },\n    {\n      \"index\": 18,\n      \"name\": \"RPParameter\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"RPParameter\"\n      }\n    },\n    {\n      \"index\": 19,\n      \"name\": \"RemoveAction\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"Action\"\n      }\n    },\n    {\n      \"index\": 24,\n      \"name\": \"Speed\"\n    },\n    {\n      \"index\": 25,\n      \"name\": \"Scale\"\n    },\n    {\n      \"index\": 26,\n      \"name\": \"IsPvP\"\n    },\n    {\n      \"index\": 27,\n      \"name\": \"IsEvent\"\n    },\n    {\n      \"index\": 28,\n      \"name\": \"PlayerCamera\"\n    },\n    {\n      \"index\": 31,\n      \"name\": \"StartVFX\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"VFX\"\n      }\n    },\n    {\n      \"index\": 32,\n      \"name\": \"EndVFX\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"VFX\"\n      }\n    },\n    {\n      \"index\": 33,\n      \"name\": \"Action[6]\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"Action\"\n      }\n    },\n    {\n      \"index\": 36,\n      \"name\": \"Action[7]\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"Action\"\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "SaintCoinach/Definitions/Treasure.json",
    "content": "{\n  \"sheet\": \"Treasure\",\n  \"defaultColumn\": \"Item\",\n  \"definitions\": [\n    {\n      \"index\": 8,\n      \"name\": \"SGB\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"ExportedSG\"\n      }\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/TreasureHuntRank.json",
    "content": "{\n  \"sheet\": \"TreasureHuntRank\",\n  \"defaultColumn\": \"ItemName\",\n  \"definitions\": [\n    {\n      \"index\": 1,\n      \"name\": \"Icon\",\n      \"converter\": {\n        \"type\": \"icon\"\n      }\n    },\n    {\n      \"index\": 2,\n      \"name\": \"ItemName\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"Item\"\n      }\n    },\n    {\n      \"index\": 3,\n      \"name\": \"KeyItemName\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"EventItem\"\n      }\n    },\n    {\n      \"index\": 4,\n      \"name\": \"InstanceMap\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"EventItem\"\n      }\n    },\n    {\n      \"index\": 5,\n      \"name\": \"MaxPartySize\"\n    },\n    {\n      \"index\": 6,\n      \"name\": \"TreasureHuntTexture\"\n    }\n  ]\n}\n"
  },
  {
    "path": "SaintCoinach/Definitions/TreasureModel.json",
    "content": "{\n  \"sheet\": \"TreasureModel\",\n  \"defaultColumn\": \"Path\",\n  \"definitions\": [\n    {\n      \"name\": \"Path\"\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/TreasureSpot.json",
    "content": "{\n  \"sheet\": \"TreasureSpot\",\n  \"defaultColumn\": \"Location\",\n  \"definitions\": [\n    {\n      \"name\": \"Location\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"Level\"\n      }\n    },\n    {\n      \"index\": 1,\n      \"name\": \"MapOffsetX\"\n    },\n    {\n      \"index\": 2,\n      \"name\": \"MapOffsetY\"\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/Tribe.json",
    "content": "{\n  \"sheet\": \"Tribe\",\n  \"defaultColumn\": \"Feminine\",\n  \"definitions\": [\n    {\n      \"name\": \"Masculine\"\n    },\n    {\n      \"index\": 1,\n      \"name\": \"Feminine\"\n    },\n    {\n      \"index\": 2,\n      \"name\": \"Hp\"\n    },\n    {\n      \"index\": 3,\n      \"name\": \"Mp\"\n    },\n    {\n      \"index\": 4,\n      \"name\": \"STR\"\n    },\n    {\n      \"index\": 5,\n      \"name\": \"VIT\"\n    },\n    {\n      \"index\": 6,\n      \"name\": \"DEX\"\n    },\n    {\n      \"index\": 7,\n      \"name\": \"INT\"\n    },\n    {\n      \"index\": 8,\n      \"name\": \"MND\"\n    },\n    {\n      \"index\": 9,\n      \"name\": \"PIE\"\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/TripleTriad.json",
    "content": "{\n  \"sheet\": \"TripleTriad\",\n  \"isGenericReferenceTarget\": true,\n  \"definitions\": [\n    {\n      \"type\": \"repeat\",\n      \"count\": 5,\n      \"definition\": {\n        \"name\": \"TripleTriadCard{Fixed}\",\n        \"converter\": {\n          \"type\": \"link\",\n          \"target\": \"TripleTriadCard\"\n        }\n      }\n    },\n    {\n      \"index\": 5,\n      \"type\": \"repeat\",\n      \"count\": 5,\n      \"definition\": {\n        \"name\": \"TripleTriadCard{Variable}\",\n        \"converter\": {\n          \"type\": \"link\",\n          \"target\": \"TripleTriadCard\"\n        }\n      }\n    },\n    {\n      \"index\": 10,\n      \"type\": \"repeat\",\n      \"count\": 2,\n      \"definition\": {\n        \"name\": \"TripleTriadRule\",\n        \"converter\": {\n          \"type\": \"link\",\n          \"target\": \"TripleTriadRule\"\n        }\n      }\n    },\n    {\n      \"index\": 12,\n      \"name\": \"UsesRegionalRules\"\n    },\n    {\n      \"index\": 13,\n      \"name\": \"Fee\"\n    },\n    {\n      \"index\": 14,\n      \"name\": \"PreviousQuestJoin\"\n    },\n    {\n      \"index\": 15,\n      \"type\": \"repeat\",\n      \"count\": 3,\n      \"definition\": {\n        \"name\": \"PreviousQuest\",\n        \"converter\": {\n          \"type\": \"link\",\n          \"target\": \"Quest\"\n        }\n      }\n    },\n    {\n      \"index\": 18,\n      \"name\": \"StartTime\"\n    },\n    {\n      \"index\": 19,\n      \"name\": \"EndTime\"\n    },\n    {\n      \"index\": 20,\n      \"name\": \"DefaultTalk{Challenge}\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"DefaultTalk\"\n      }\n    },\n    {\n      \"index\": 21,\n      \"name\": \"DefaultTalk{Unavailable}\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"DefaultTalk\"\n      }\n    },\n    {\n      \"index\": 22,\n      \"name\": \"DefaultTalk{NPCWin}\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"DefaultTalk\"\n      }\n    },\n    {\n      \"index\": 23,\n      \"name\": \"DefaultTalk{Draw}\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"DefaultTalk\"\n      }\n    },\n    {\n      \"index\": 24,\n      \"name\": \"DefaultTalk{PCWin}\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"DefaultTalk\"\n      }\n    },\n    {\n      \"index\": 26,\n      \"type\": \"repeat\",\n      \"count\": 4,\n      \"definition\": {\n        \"name\": \"Item{PossibleReward}\",\n        \"converter\": {\n          \"type\": \"link\",\n          \"target\": \"Item\"\n        }\n      }\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/TripleTriadCard.json",
    "content": "{\n  \"sheet\": \"TripleTriadCard\",\n  \"defaultColumn\": \"Name\",\n  \"definitions\": [\n    {\n      \"name\": \"Name\"\n    },\n    {\n      \"index\": 4,\n      \"name\": \"StartsWithVowel\"\n    },\n    {\n      \"index\": 8,\n      \"name\": \"Description\"\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/TripleTriadCardObtain.json",
    "content": "{\n  \"sheet\": \"TripleTriadCardObtain\",\n  \"definitions\": []\n}"
  },
  {
    "path": "SaintCoinach/Definitions/TripleTriadCardRarity.json",
    "content": "{\n  \"sheet\": \"TripleTriadCardRarity\",\n  \"defaultColumn\": \"Name\",\n  \"definitions\": [\n    {\n      \"name\": \"Stars\"\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/TripleTriadCardResident.json",
    "content": "{\n  \"sheet\": \"TripleTriadCardResident\",\n  \"definitions\": [\n    {\n      \"index\": 1,\n      \"name\": \"Top\"\n    },\n    {\n      \"index\": 2,\n      \"name\": \"Bottom\"\n    },\n    {\n      \"index\": 3,\n      \"name\": \"Left\"\n    },\n    {\n      \"index\": 4,\n      \"name\": \"Right\"\n    },\n    {\n      \"index\": 5,\n      \"name\": \"TripleTriadCardRarity\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"TripleTriadCardRarity\"\n      }\n    },\n    {\n      \"index\": 6,\n      \"name\": \"TripleTriadCardType\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"TripleTriadCardType\"\n      }\n    },\n    {\n      \"index\": 7,\n      \"name\": \"SaleValue\"\n    },\n    {\n      \"index\": 8,\n      \"name\": \"SortKey\"\n    },\n    {\n      \"index\": 9,\n      \"name\": \"Order\"\n    },\n    {\n      \"index\": 10,\n      \"name\": \"UIPriority\"\n    },\n    {\n      \"index\": 12,\n      \"name\": \"AcquisitionType\"\n    },\n    {\n      \"index\": 13,\n      \"name\": \"Acquisition\",\n      \"converter\": {\n        \"type\": \"complexlink\",\n        \"links\": [\n          {\n            \"when\": {\n              \"key\": \"AcquisitionType\",\n              \"value\": 2\n            },\n            \"sheet\": \"ContentFinderCondition\"\n          },\n          {\n            \"when\": {\n              \"key\": \"AcquisitionType\",\n              \"value\": 3\n            },\n            \"sheet\": \"ContentFinderCondition\"\n          },\n          {\n            \"when\": {\n              \"key\": \"AcquisitionType\",\n              \"value\": 4\n            },\n            \"sheet\": \"Fate\"\n          },\n          {\n            \"when\": {\n              \"key\": \"AcquisitionType\",\n              \"value\": 5\n            },\n            \"sheet\": \"Fate\"\n          },\n          {\n            \"when\": {\n              \"key\": \"AcquisitionType\",\n              \"value\": 6\n            },\n            \"sheet\": \"ENpcResident\"\n          },\n          {\n            \"when\": {\n              \"key\": \"AcquisitionType\",\n              \"value\": 8\n            },\n            \"sheet\": \"Item\"\n          },\n          {\n            \"when\": {\n              \"key\": \"AcquisitionType\",\n              \"value\": 9\n            },\n            \"sheet\": \"Item\"\n          },\n          {\n            \"when\": {\n              \"key\": \"AcquisitionType\",\n              \"value\": 10\n            },\n            \"sheet\": \"ENpcResident\"\n          },\n          {\n            \"when\": {\n              \"key\": \"AcquisitionType\",\n              \"value\": 11\n            },\n            \"sheet\": \"Achievement\"\n          },\n          {\n            \"when\": {\n              \"key\": \"AcquisitionType\",\n              \"value\": 12\n            },\n            \"sheet\": \"ContentFinderCondition\"\n          }\n        ]\n      }\n    },\n    {\n      \"index\": 14,\n      \"name\": \"Location\",\n      \"converter\": {\n        \"type\": \"complexlink\",\n        \"links\": [\n          {\n            \"when\": {\n              \"key\": \"AcquisitionType\",\n              \"value\": 4\n            },\n            \"sheet\": \"TerritoryType\"\n          },\n          {\n            \"when\": {\n              \"key\": \"AcquisitionType\",\n              \"value\": 5\n            },\n            \"sheet\": \"TerritoryType\"\n          },\n          {\n            \"when\": {\n              \"key\": \"AcquisitionType\",\n              \"value\": 6\n            },\n            \"sheet\": \"Level\"\n          },\n          {\n            \"when\": {\n              \"key\": \"AcquisitionType\",\n              \"value\": 10\n            },\n            \"sheet\": \"Level\"\n          }\n        ]\n      }\n    },\n    {\n      \"index\": 15,\n      \"name\": \"Quest\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"Quest\"\n      }\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/TripleTriadCardType.json",
    "content": "{\n  \"sheet\": \"TripleTriadCardType\",\n  \"defaultColumn\": \"Name\",\n  \"definitions\": [\n    {\n      \"name\": \"Name\"\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/TripleTriadCompetition.json",
    "content": "{\n  \"sheet\": \"TripleTriadCompetition\",\n  \"defaultColumn\": \"Name\",\n  \"definitions\": [\n    {\n      \"name\": \"Name\"\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/TripleTriadResident.json",
    "content": "{\n  \"sheet\": \"TripleTriadResident\",\n  \"definitions\": [\n    {\n      \"name\": \"Order\"\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/TripleTriadRule.json",
    "content": "{\n  \"sheet\": \"TripleTriadRule\",\n  \"defaultColumn\": \"Name\",\n  \"definitions\": [\n    {\n      \"name\": \"Name\"\n    },\n    {\n      \"index\": 1,\n      \"name\": \"Description\"\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/Tutorial.json",
    "content": "{\n  \"sheet\": \"Tutorial\",\n  \"defaultColumn\": \"Objective\",\n  \"definitions\": [\n    {\n      \"index\": 4,\n      \"name\": \"Exp\"\n    },\n    {\n      \"index\": 5,\n      \"name\": \"Gil\"\n    },\n    {\n      \"index\": 6,\n      \"name\": \"Reward{Tank}\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"Item\"\n      }\n    },\n    {\n      \"index\": 7,\n      \"name\": \"Reward{Melee}\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"Item\"\n      }\n    },\n    {\n      \"index\": 8,\n      \"name\": \"Reward{Ranged}\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"Item\"\n      }\n    },\n    {\n      \"index\": 9,\n      \"name\": \"Objective\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"InstanceContentTextData\"\n      }\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/TutorialDPS.json",
    "content": "{\n  \"sheet\": \"TutorialDPS\",\n  \"definitions\": [\n    {\n      \"name\": \"Objective\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"Tutorial\"\n      }\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/TutorialHealer.json",
    "content": "{\n  \"sheet\": \"TutorialHealer\",\n  \"definitions\": [\n    {\n      \"name\": \"Objective\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"Tutorial\"\n      }\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/TutorialTank.json",
    "content": "{\n  \"sheet\": \"TutorialTank\",\n  \"definitions\": [\n    {\n      \"name\": \"Objective\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"Tutorial\"\n      }\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/UDS_Event.json",
    "content": "{\n  \"sheet\": \"UDS_Event\",\n  \"defaultColumn\": \"Text\",\n  \"definitions\": [\n    {\n      \"name\": \"Text\"\n    },\n    {\n      \"index\": 1,\n      \"name\": \"Type\"\n    },\n    {\n      \"index\": 2,\n      \"type\": \"repeat\",\n      \"count\": 32,\n      \"definition\": {\n        \"name\": \"Property\",\n        \"converter\": {\n          \"type\": \"link\",\n          \"target\": \"UDS_Property\"\n        }\n      }\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/UDS_Property.json",
    "content": "{\n  \"sheet\": \"UDS_Property\",\n  \"defaultColumn\": \"Text\",\n  \"definitions\": [\n    {\n      \"name\": \"Text\"\n    },\n    {\n      \"index\": 1,\n      \"name\": \"Type\"\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/UIColor.json",
    "content": "{\n  \"sheet\": \"UIColor\",\n  \"defaultColumn\": \"UIForeground\",\n  \"definitions\": [\n    {\n      \"name\": \"UIForeground\"\n    },\n    {\n      \"index\": 1,\n      \"name\": \"UIGlow\"\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/UIConst.json",
    "content": "{\n  \"sheet\": \"UIConst\",\n  \"definitions\": []\n}"
  },
  {
    "path": "SaintCoinach/Definitions/UILevelLookup.json",
    "content": "{\n  \"sheet\": \"UILevelLookup\",\n  \"definitions\": []\n}"
  },
  {
    "path": "SaintCoinach/Definitions/VFX.json",
    "content": "{\n  \"sheet\": \"VFX\",\n  \"defaultColumn\": \"Location\",\n  \"definitions\": [\n    {\n      \"name\": \"Location\"\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/VVDData.json",
    "content": "{\n  \"sheet\": \"VVDData\",\n  \"definitions\": []\n}"
  },
  {
    "path": "SaintCoinach/Definitions/VVDNotebookContents.json",
    "content": "{\n  \"sheet\": \"VVDNotebookContents\",\n  \"defaultColumn\": \"Name\",\n  \"definitions\": [\n    {\n      \"name\": \"Icon\",\n      \"converter\": {\n        \"type\": \"icon\"\n      }\n    },\n    {\n      \"index\": 1,\n      \"name\": \"Image\",\n      \"converter\": {\n        \"type\": \"icon\"\n      }\n    },\n    {\n      \"index\": 2,\n      \"name\": \"Name\"\n    },\n    {\n      \"index\": 3,\n      \"name\": \"Description\"\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/VVDNotebookSeries.json",
    "content": "{\n  \"sheet\": \"VVDNotebookSeries\",\n  \"defaultColumn\": \"Name\",\n  \"definitions\": [\n    {\n      \"name\": \"Name\"\n    },\n    {\n      \"index\": 1,\n      \"type\": \"repeat\",\n      \"count\": 12,\n      \"definition\": {\n        \"name\": \"Contents\",\n        \"converter\": {\n          \"type\": \"link\",\n          \"target\": \"VVDNotebookContents\"\n        }\n      }\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/VVDRouteData.json",
    "content": "{\n  \"sheet\": \"VVDRouteData\",\n  \"definitions\": []\n}"
  },
  {
    "path": "SaintCoinach/Definitions/VVDVariantAction.json",
    "content": "{\n  \"sheet\": \"VVDVariantAction\",\n  \"defaultColumn\": \"Action\",\n  \"definitions\": [\n    {\n      \"name\": \"Action\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"Action\"\n      }\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/VaseFlower.json",
    "content": "{\n  \"sheet\": \"VaseFlower\",\n  \"definitions\": [\n    {\n      \"index\": 3,\n      \"name\": \"Item\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"Item\"\n      }\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/Warp.json",
    "content": "{\n  \"sheet\": \"Warp\",\n  \"isGenericReferenceTarget\": true,\n  \"definitions\": [\n    {\n      \"name\": \"PopRange\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"Level\"\n      }\n    },\n    {\n      \"index\": 1,\n      \"name\": \"TerritoryType\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"TerritoryType\"\n      }\n    },\n    {\n      \"index\": 2,\n      \"name\": \"ConditionSuccessEvent\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"DefaultTalk\"\n      }\n    },\n    {\n      \"index\": 3,\n      \"name\": \"ConditionFailEvent\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"DefaultTalk\"\n      }\n    },\n    {\n      \"index\": 4,\n      \"name\": \"ConfirmEvent\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"DefaultTalk\"\n      }\n    },\n    {\n      \"index\": 5,\n      \"name\": \"WarpCondition\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"WarpCondition\"\n      }\n    },\n    {\n      \"index\": 6,\n      \"name\": \"WarpLogic\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"WarpLogic\"\n      }\n    },\n    {\n      \"index\": 7,\n      \"name\": \"StartCutscene\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"Cutscene\"\n      }\n    },\n    {\n      \"index\": 8,\n      \"name\": \"EndCutscene\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"Cutscene\"\n      }\n    },\n    {\n      \"index\": 9,\n      \"name\": \"CanSkipCutscene\"\n    },\n    {\n      \"index\": 10,\n      \"name\": \"Name\"\n    },\n    {\n      \"index\": 11,\n      \"name\": \"Question\"\n    }\n  ]\n}\n"
  },
  {
    "path": "SaintCoinach/Definitions/WarpCondition.json",
    "content": "{\n  \"sheet\": \"WarpCondition\",\n  \"definitions\": [\n    {\n      \"name\": \"Gil\"\n    },\n    {\n      \"index\": 1,\n      \"name\": \"CompleteParam\"\n    },\n    {\n      \"index\": 2,\n      \"name\": \"RequiredQuest{1}\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"Quest\"\n      }\n    },\n    {\n      \"index\": 3,\n      \"name\": \"RequiredQuest{2}\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"Quest\"\n      }\n    },\n    {\n      \"index\": 4,\n      \"name\": \"RequiredQuest{3}\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"Quest\"\n      }\n    },\n    {\n      \"index\": 5,\n      \"name\": \"RequiredQuest{4}\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"Quest\"\n      }\n    },\n    {\n      \"index\": 6,\n      \"name\": \"QuestReward\"\n    },\n    {\n      \"index\": 7,\n      \"name\": \"Class{Level}\"\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/WarpLogic.json",
    "content": "{\n  \"sheet\": \"WarpLogic\",\n  \"defaultColumn\": \"WarpName\",\n  \"definitions\": [\n    {\n      \"index\": 1,\n      \"name\": \"WarpName\"\n    },\n    {\n      \"index\": 2,\n      \"name\": \"CanSkipCutscene\"\n    },\n    {\n      \"index\": 3,\n      \"type\": \"repeat\",\n      \"count\": 10,\n      \"definition\": {\n        \"name\": \"Function\"\n      }\n    },\n    {\n      \"index\": 13,\n      \"type\": \"repeat\",\n      \"count\": 10,\n      \"definition\": {\n        \"type\": \"group\",\n        \"members\": [\n          {\n            \"name\": \"Argument\",\n            \"converter\": {\n              \"type\": \"multiref\",\n              \"targets\": [\n                \"HowTo\",\n                \"Item\",\n                \"Quest\"\n              ]\n            }\n          }\n        ]\n      }\n    },\n    {\n      \"index\": 23,\n      \"name\": \"Question\"\n    },\n    {\n      \"index\": 24,\n      \"name\": \"Response{Yes}\"\n    },\n    {\n      \"index\": 25,\n      \"name\": \"Response{No}\"\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/WeaponTimeline.json",
    "content": "{\n  \"sheet\": \"WeaponTimeline\",\n  \"definitions\": [\n    {\n      \"name\": \"File\"\n    },\n    {\n      \"index\": 1,\n      \"name\": \"NextWeaponTimeline\"\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/Weather.json",
    "content": "{\n  \"sheet\": \"Weather\",\n  \"defaultColumn\": \"Name\",\n  \"definitions\": [\n    {\n      \"name\": \"Icon\",\n      \"converter\": {\n        \"type\": \"icon\"\n      }\n    },\n    {\n      \"index\": 1,\n      \"name\": \"Name\"\n    },\n    {\n      \"index\": 2,\n      \"name\": \"Description\"\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/WeatherGroup.json",
    "content": "{\n  \"sheet\": \"WeatherGroup\",\n  \"defaultColumn\": \"Name\",\n  \"definitions\": [\n    {\n      \"index\": 1,\n      \"name\": \"WeatherRate\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"WeatherRate\"\n      }\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/WeatherRate.json",
    "content": "{\n  \"sheet\": \"WeatherRate\",\n  \"defaultColumn\": \"Name\",\n  \"definitions\": [\n    {\n      \"type\": \"repeat\",\n      \"count\": 8,\n      \"definition\": {\n        \"type\": \"group\",\n        \"members\": [\n          {\n            \"name\": \"Weather\",\n            \"converter\": {\n              \"type\": \"link\",\n              \"target\": \"Weather\"\n            }\n          },\n          {\n            \"name\": \"Rate\"\n          }\n        ]\n      }\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/WeatherReportReplace.json",
    "content": "{\n  \"sheet\": \"WeatherReportReplace\",\n  \"definitions\": [\n    {\n      \"name\": \"PlaceName{Sub}\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"PlaceName\"\n      }\n    },\n    {\n      \"index\": 1,\n      \"name\": \"PlaceName{Parent}\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"PlaceName\"\n      }\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/WebGuidance.json",
    "content": "{\n  \"sheet\": \"WebGuidance\",\n  \"defaultColumn\": \"Name\",\n  \"definitions\": [\n    {\n      \"name\": \"Image\",\n      \"converter\": {\n        \"type\": \"icon\"\n      }\n    },\n    {\n      \"index\": 1,\n      \"name\": \"Url\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"WebURL\"\n      }\n    },\n    {\n      \"index\": 2,\n      \"name\": \"Name\"\n    },\n    {\n      \"index\": 4,\n      \"name\": \"Description\"\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/WebURL.json",
    "content": "{\n  \"sheet\": \"WebURL\",\n  \"defaultColumn\": \"URL\",\n  \"definitions\": [\n    {\n      \"name\": \"URL\"\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/WeddingBGM.json",
    "content": "{\n  \"sheet\": \"WeddingBGM\",\n  \"defaultColumn\": \"Song\",\n  \"definitions\": [\n    {\n      \"name\": \"Song\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"BGM\"\n      }\n    },\n    {\n      \"index\": 1,\n      \"name\": \"SongName\"\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/WeeklyBingoOrderData.json",
    "content": "{\n  \"sheet\": \"WeeklyBingoOrderData\",\n  \"definitions\": [\n    {\n      \"name\": \"Type\"\n    },\n    {\n      \"index\": 1,\n      \"name\": \"Data\",\n      \"converter\": {\n        \"type\": \"complexlink\",\n        \"links\": [\n          {\n            \"when\": {\n              \"key\": \"Type\",\n              \"value\": 0\n            },\n            \"sheet\": \"InstanceContent\"\n          }\n        ]\n      }\n    },\n    {\n      \"index\": 3,\n      \"name\": \"Text\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"WeeklyBingoText\"\n      }\n    },\n    {\n      \"index\": 4,\n      \"name\": \"Icon\",\n      \"converter\": {\n        \"type\": \"icon\"\n      }\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/WeeklyBingoRewardData.json",
    "content": "{\n  \"sheet\": \"WeeklyBingoRewardData\",\n  \"defaultColumn\": \"Reward\",\n  \"definitions\": [\n    {\n      \"type\": \"repeat\",\n      \"count\": 2,\n      \"definition\": {\n        \"type\": \"group\",\n        \"members\": [\n          {\n            \"name\": \"Reward{Type}\"\n          },\n          {\n            \"name\": \"Reward{Item}\",\n            \"converter\": {\n              \"type\": \"complexlink\",\n              \"links\": [\n                {\n                  \"project\": \"Item\",\n                  \"key\": \"Tomestones\",\n                  \"sheet\": \"TomestonesItem\"\n                },\n                {\n                  \"sheet\": \"Item\"\n                }\n              ]\n            }\n          },\n          {\n            \"name\": \"Reward{HQ}\"\n          },\n          {\n            \"name\": \"Reward{Quantity}\"\n          },\n          {\n            \"name\": \"Reward{Option}\",\n            \"converter\": {\n              \"type\": \"link\",\n              \"target\": \"WeeklyBingoRewardData\"\n            }\n          }\n        ]\n      }\n    },\n    {\n      \"index\": 10,\n      \"name\": \"Reward{Item}[2]\",\n      \"converter\": {\n        \"type\": \"tomestone\"\n      }\n    },\n    {\n      \"index\": 11,\n      \"name\": \"Reward{HQ}[2]\"\n    },\n    {\n      \"index\": 12,\n      \"name\": \"Reward{Quantity}[2]\"\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/WeeklyBingoText.json",
    "content": "{\n  \"sheet\": \"WeeklyBingoText\",\n  \"defaultColumn\": \"Description\",\n  \"definitions\": [\n    {\n      \"name\": \"Description\"\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/WeeklyLotBonus.json",
    "content": "{\n  \"sheet\": \"WeeklyLotBonus\",\n  \"definitions\": [\n    {\n      \"type\": \"repeat\",\n      \"count\": 31,\n      \"definition\": {\n        \"name\": \"WeeklyLotBonusThreshold\",\n        \"converter\": {\n          \"type\": \"link\",\n          \"target\": \"WeeklyLotBonusThreshold\"\n        }\n      }\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/World.json",
    "content": "{\n  \"sheet\": \"World\",\n  \"definitions\": [\n    {\n      \"name\": \"InternalName\"\n    },\n    {\n      \"index\": 1,\n      \"name\": \"Name\"\n    },\n    {\n      \"index\": 2,\n      \"name\": \"Region\"\n    },\n    {\n      \"index\": 3,\n      \"name\": \"UserType\"\n    },\n    {\n      \"index\": 4,\n      \"name\": \"DataCenter\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"WorldDCGroupType\"\n      }\n    },\n    {\n      \"index\": 5,\n      \"name\": \"IsPublic\"\n    }\n  ]\n}\n"
  },
  {
    "path": "SaintCoinach/Definitions/WorldDCGroupType.json",
    "content": "{\n  \"sheet\": \"WorldDCGroupType\",\n  \"defaultColumn\": \"Name\",\n  \"definitions\": [\n    {\n      \"name\": \"Name\"\n    },\n    {\n      \"index\": 1,\n      \"name\": \"Region\"\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/YKW.json",
    "content": "{\n  \"sheet\": \"YKW\",\n  \"definitions\": [\n    {\n      \"index\": 1,\n      \"name\": \"Item\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"Item\"\n      }\n    },\n    {\n      \"index\": 2,\n      \"type\": \"repeat\",\n      \"count\": 6,\n      \"definition\": {\n        \"name\": \"Location\",\n        \"converter\": {\n          \"type\": \"link\",\n          \"target\": \"TerritoryType\"\n        }\n      }\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/YardCatalogCategory.json",
    "content": "{\n  \"sheet\": \"YardCatalogCategory\",\n  \"defaultColumn\": \"Category\",\n  \"definitions\": [\n    {\n      \"name\": \"Category\"\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/YardCatalogItemList.json",
    "content": "{\n  \"sheet\": \"YardCatalogItemList\",\n  \"defaultColumn\": \"Item\",\n  \"definitions\": [\n    {\n      \"name\": \"Category\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"YardCatalogCategory\"\n      }\n    },\n    {\n      \"index\": 1,\n      \"name\": \"Item\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"Item\"\n      }\n    },\n    {\n      \"index\": 2,\n      \"name\": \"Patch\"\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/ZoneSharedGroup.json",
    "content": "{\n  \"sheet\": \"ZoneSharedGroup\",\n  \"definitions\": [\n    {\n      \"name\": \"LGB{SharedGroup}\"\n    },\n    {\n      \"index\": 2,\n      \"name\": \"Quest[0]\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"Quest\"\n      }\n    },\n    {\n      \"index\": 3,\n      \"name\": \"Seq[0]\"\n    },\n    {\n      \"index\": 6,\n      \"name\": \"Quest[1]\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"Quest\"\n      }\n    },\n    {\n      \"index\": 7,\n      \"name\": \"Seq[1]\"\n    },\n    {\n      \"index\": 10,\n      \"name\": \"Quest[2]\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"Quest\"\n      }\n    },\n    {\n      \"index\": 11,\n      \"name\": \"Seq[2]\"\n    },\n    {\n      \"index\": 14,\n      \"name\": \"Quest[3]\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"Quest\"\n      }\n    },\n    {\n      \"index\": 15,\n      \"name\": \"Seq[3]\"\n    },\n    {\n      \"index\": 18,\n      \"name\": \"Quest[4]\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"Quest\"\n      }\n    },\n    {\n      \"index\": 19,\n      \"name\": \"Seq[4]\"\n    },\n    {\n      \"index\": 22,\n      \"name\": \"Quest[5]\",\n      \"converter\": {\n        \"type\": \"link\",\n        \"target\": \"Quest\"\n      }\n    },\n    {\n      \"index\": 23,\n      \"name\": \"Seq[5]\"\n    }\n  ]\n}"
  },
  {
    "path": "SaintCoinach/Definitions/game.ver",
    "content": "2024.06.18.0000.0000"
  },
  {
    "path": "SaintCoinach/EorzeaDateTime.Formatter.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace SaintCoinach {\n    public partial class EorzeaDateTime : IFormattable {\n\n        #region IFormattable Members\n\n        /// <summary>\n        ///     Get the string representation of this <see cref=\"EorzeaDateTime\" /> using the default format string.\n        /// </summary>\n        /// <remarks>\n        ///     The format of the returned string is <c>Sun/Moon/Year Bell:Minute</c>, with all but Year having a leading zero of\n        ///     less than 10.\n        /// </remarks>\n        /// <returns>Returns the string representation of this <see cref=\"EorzeaDateTime\" />.</returns>\n        public override string ToString() {\n            return this.ToString(null, System.Globalization.CultureInfo.CurrentCulture);\n            //return string.Format(\"{0:D2}/{1:D2}/{2} {3:D2}:{4:D2}\", Sun, Moon, Year, Bell, Minute);\n        }\n\n        /// <summary>\n        ///    \tFormats the value of the current instance using the specified format, or the default format if none is provided.\n        /// </summary>\n        /// <remarks>\n        ///     Available standard formats are:\n        ///       G     Equivalent to 'dd/MM/y HH:mm'\n        ///       g     Equivalent to 'dd/MM/y hh:mm p'\n        ///       T     Equivalent to 'HH:mm'\n        ///       t     Equivalent to 'hh:mm p'\n        ///       \n        ///     Available custom specifiers are:\n        ///       d     The day of the month, from 1 through 32.\n        ///       dd    The day of the month, from 01 through 32.\n        ///       h     The hour, using a 12-hour clock from 1 through 12.\n        ///       hh    The hour, using a 12-hour clock from 01 through 12.\n        ///       H     The hour, using a 24-hour clock from 0 through 23.\n        ///       HH    The hour, using a 24-hour clock from 00 through 23.\n        ///       m     The minute, from 0 through 59.\n        ///       mm    The minute, from 00 through 59.\n        ///       M     The month, from 1 through 12.\n        ///       MM    The month, from 01 through 12.\n        ///       p     The AM/PM designator.\n        ///       y     The year.\n        ///       yyy   The year as a three-digit number.\n        ///       yyyy  The year as a four-digit number.\n        /// </remarks>\n        /// <param name=\"format\">The format to use.</param>\n        /// <param name=\"formatProvider\">The provider to use to format the value.</param>\n        /// <returns>The value of the current instance in the specified format.</returns>\n        public string ToString(string format, IFormatProvider formatProvider) {\n            return Formatter.Format(this, format, formatProvider);\n        }\n\n        #endregion\n\n        /// <summary>\n        ///     Static class for formatting instances of the <see cref=\"EorzeaDateTime\"/> class to strings.\n        /// </summary>\n        private static class Formatter {\n            /// <summary>\n            ///     The default format.\n            /// </summary>\n            public const string DefaultFormat = \"G\";\n\n            /// <summary>\n            ///     Mapping of characters to their format string.\n            /// </summary>\n            /// <remarks>\n            ///     The key character must be the first, and only, character in the original format string.\n            /// </remarks>\n            private static readonly Dictionary<char, string> StandardFormats = new Dictionary<char, string> {\n                { 'G', \"dd/MM/y HH:mm\" },\n                { 'g', \"dd/MM/y hh:mm p\" },\n                { 'T', \"HH:mm\" },\n                { 't', \"hh:mm p\" },\n            };\n\n            /// <summary>\n            ///     Mapping of valid specifiers.\n            /// </summary>\n            private static readonly Dictionary<char, Specifier> KnownSpecifiers = new Dictionary<char, Specifier> {\n                { 'd', new Specifier {\n                     MaximumCount = 2,\n                     FormatFunction = (dt, c, p) => dt.Sun.ToString(\"D\" + c.ToString(), p)\n                } },\n                { 'M', new Specifier {\n                     MaximumCount = 2,\n                     FormatFunction = (dt, c, p) => dt.Moon.ToString(\"D\" + c.ToString(), p)\n                } },\n                { 'y', new Specifier {\n                     MaximumCount = 4,\n                     FormatFunction = (dt, c, p) => dt.Year.ToString(\"D\" + c.ToString(), p)\n                } },\n                { 'H', new Specifier {\n                     MaximumCount = 2,\n                     FormatFunction = (dt, c, p) => dt.Bell.ToString(\"D\" + c.ToString(), p)\n                } },\n                { 'h', new Specifier {\n                     MaximumCount = 2,\n                     FormatFunction = (dt, c, p) => {\n                         var t = dt.Bell % 12;\n                         if(t == 0)\n                             t = 12;\n                         return t.ToString(\"D\" + c.ToString(), p);\n                     }\n                } },\n                { 'm', new Specifier {\n                     MaximumCount = 2,\n                     FormatFunction = (dt, c, p) => dt.Minute.ToString(\"D\" + c.ToString(), p)\n                } },\n                \n                { 'p', new Specifier {\n                     MaximumCount = 2,\n                     FormatFunction = (dt, c, p) => {\n                         var dtInfo = System.Globalization.DateTimeFormatInfo.GetInstance(p);\n                         return dt.Bell < 12 ? dtInfo.AMDesignator : dtInfo.PMDesignator;\n                     }\n                } },\n            };\n            /// <summary>\n            ///     Array of characters that can be used to encapsulate string literals.\n            /// </summary>\n            private static readonly char[] StringSpecifiers = new char[] { '\\'', '\"' };\n\n            /// <summary>\n            ///    \tFormats the value of the current instance using the specified format and format provider..\n            /// </summary>\n            /// <param name=\"dateTime\"><see cref=\"EorzeaDateTime\"/> to format.</param>\n            /// <param name=\"format\">Format string to use, or <c>null</c> to use the default format.</param>\n            /// <param name=\"formatProvider\">The provider to use to format the value, or <c>null</c> if the current culture's provider should be used.</param>\n            /// <returns>The value of <c>dateTime</c> formatted as a string according to <c>format</c>.</returns>\n            public static string Format(EorzeaDateTime dateTime, string format, IFormatProvider formatProvider) {\n                if (dateTime == null)\n                    throw new ArgumentNullException(\"dateTime\");\n                if (format == null)\n                    format = DefaultFormat;\n                if (formatProvider == null)\n                    formatProvider = System.Globalization.CultureInfo.CurrentCulture;\n\n                if (string.IsNullOrWhiteSpace(format))\n                    throw new ArgumentException();\n\n                if (format.Length == 1) {\n                    var standardKey = format[0];\n                    if (StandardFormats.ContainsKey(standardKey))\n                        format = StandardFormats[standardKey];\n                }\n\n                var sb = new StringBuilder();\n\n                char isInString = '\\0';\n                char currentSpecifier = '\\0';\n                var specifierCount = 0;\n                foreach (var c in format) {\n                    if (isInString != '\\0') {\n                        if (c == isInString)\n                            isInString = '\\0';\n                        else\n                            sb.Append(c);\n                    } else {\n                        if (c == currentSpecifier)\n                            ++specifierCount;\n                        else {\n                            TryFinishSequence(dateTime, sb, ref currentSpecifier, specifierCount, formatProvider);\n\n                            if (StringSpecifiers.Contains(c))\n                                isInString = c;\n                            else if (KnownSpecifiers.ContainsKey(c)) {\n                                currentSpecifier = c;\n                                specifierCount = 1;\n                            } else\n                                sb.Append(c);\n                        }\n\n                        if (currentSpecifier != '\\0') {\n                            if (specifierCount == KnownSpecifiers[currentSpecifier].MaximumCount)\n                                TryFinishSequence(dateTime, sb, ref currentSpecifier, specifierCount, formatProvider);\n                        }\n                    }\n                }\n                TryFinishSequence(dateTime, sb, ref currentSpecifier, specifierCount, formatProvider);\n\n                return sb.ToString();\n            }\n\n            /// <summary>\n            ///     Append the format for a specifier to the output, if applicable.\n            /// </summary>\n            /// <param name=\"dateTime\">The Source <see cref=\"EorzeaDateTime\"/>.</param>\n            /// <param name=\"stringBuilder\"><see cref=\"StringBuilder\"/> to write to.</param>\n            /// <param name=\"specifier\">Specifier to use for formatting, or \\0 should nothing be appended. When this method returns this value will always be \\0.</param>\n            /// <param name=\"count\">Subsequent occurences of <c>specifier</c>.</param>\n            /// <param name=\"formatProvider\">The provider to use to format the value.</param>\n            private static void TryFinishSequence(EorzeaDateTime dateTime, StringBuilder stringBuilder, ref char specifier, int count, IFormatProvider formatProvider) {\n                if (specifier == '\\0')\n                    return;\n\n                stringBuilder.Append(KnownSpecifiers[specifier].FormatFunction(dateTime, count, formatProvider));\n                specifier = '\\0';\n            }\n\n            /// <summary>\n            ///     Helper class for format specifiers.\n            /// </summary>\n            private class Specifier {\n                /// <summary>\n                ///     Gets or sets the function used format a value using the current specifier.\n                /// </summary>\n                /// <value>The function used format a value using the current specifier.</value>\n                public Func<EorzeaDateTime, int, IFormatProvider, string> FormatFunction { get; set; }\n                /// <summary>\n                ///     Gets or sets the maximum number of times the current specifier can be used in sequence.\n                /// </summary>\n                /// <value>The maximum number of times the current specifier can be used in sequence.</value>\n                public int MaximumCount { get; set; }\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/EorzeaDateTime.cs",
    "content": "﻿using System;\n\nnamespace SaintCoinach {\n    /// <summary>\n    ///     Class for operations using the Eorzean calendar and time.\n    /// </summary>\n    /// <remarks>\n    ///     One Eorzean day is 70 real-world minutes long, with zero starting at the Unix epoch (1. Jan 1970, 00:00.00).\n    ///     The Eorzean calendar is divided into <see cref=\"Year\" />, <see cref=\"Moon\" />, <see cref=\"Sun\" />,\n    ///     <see cref=\"Bell\" /> and <see cref=\"Minute\" />.\n    ///     One <see cref=\"Year\" /> is 12 <see cref=\"Moon\" />s.\n    ///     One <see cref=\"Moon\" /> is 32 <see cref=\"Sun\" />s.\n    ///     One <see cref=\"Bell\" /> is 24 <see cref=\"Minute\" />s.\n    /// </remarks>\n    public partial class EorzeaDateTime {\n        #region Static\n\n        /// <summary>\n        ///     Factor used to convert from real to Eorzean time.\n        /// </summary>\n        /// <remarks>\n        ///     60 * 24 Eorzean minutes (one day) per 70 real-world minutes.\n        /// </remarks>\n        public const double RealToEorzeanFactor = (60.0 * 24.0) / 70.0;\n\n        /// <summary>\n        ///     Interval in which the value of <see cref=\"Now\" /> is updated.\n        /// </summary>\n        private const long NowUpdateInterval = TimeSpan.TicksPerSecond;\n\n        /// <summary>\n        ///     <see cref=\"DateTime\" /> of the Unix epoch.\n        /// </summary>\n        public static readonly DateTime Zero = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc);\n\n        /// <summary>\n        ///     Last update of the value of <see cref=\"EorzeaDateTime.Now\" /> in ticks.\n        /// </summary>\n        private static long _LastNowUpdate = DateTime.UtcNow.Ticks;\n\n        /// <summary>\n        ///     Current value of <see cref=\"Now\" />.\n        /// </summary>\n        private static EorzeaDateTime _Now = new EorzeaDateTime();\n\n        #endregion\n\n        #region Fields\n\n        /// <summary>\n        ///     Current bell (hour) in Eorzean time.\n        /// </summary>\n        private int _Bell;\n\n        /// <summary>\n        ///     Current minute in Eorzean time.\n        /// </summary>\n        private int _Minute;\n\n        /// <summary>\n        ///     Current moon (month) in Eorzean time.\n        /// </summary>\n        private int _Moon;\n\n        /// <summary>\n        ///     Current sun (day) in Eorzean time.\n        /// </summary>\n        private int _Sun;\n\n        #endregion\n\n        #region Properties\n\n        /// <summary>\n        ///     Gets the current time in the Eorzean calendar.\n        /// </summary>\n        /// <value>The current time in the Eorzean calendar.</value>\n        public static EorzeaDateTime Now {\n            get {\n                var nt = DateTime.UtcNow.Ticks;\n                var d = nt - _LastNowUpdate;\n                if (d <= NowUpdateInterval) return _Now;\n\n                _Now = new EorzeaDateTime();\n                _LastNowUpdate = nt;\n                return _Now;\n            }\n        }\n\n        /// <summary>\n        ///     Gets or sets the current minute in the Eorzean calendar.\n        /// </summary>\n        /// <remarks>\n        ///     Setting values outside the 0..59 range will be adjusted accordingly and carry over to <see cref=\"Bell\" />.\n        /// </remarks>\n        /// <value>The current minute in the Eorzean calendar.</value>\n        public int Minute {\n            get { return _Minute; }\n            set {\n                _Minute = value;\n                while (_Minute < 0) {\n                    _Minute += 60;\n                    Bell--;\n                }\n                while (_Minute >= 60) {\n                    _Minute -= 60;\n                    Bell++;\n                }\n            }\n        }\n\n        /// <summary>\n        ///     Gets or sets the current bell (hour) in the Eorzean calendar.\n        /// </summary>\n        /// <remarks>\n        ///     Setting values outside the 0..23 range will be adjusted accordingly and carry over to <see cref=\"Sun\" />.\n        /// </remarks>\n        /// <value>The current bell (hour) in the Eorzean calendar.</value>\n        public int Bell {\n            get { return _Bell; }\n            set {\n                _Bell = value;\n                while (_Bell < 0) {\n                    _Bell += 24;\n                    Sun--;\n                }\n                while (_Bell >= 24) {\n                    _Bell -= 24;\n                    Sun++;\n                }\n            }\n        }\n\n        /// <summary>\n        ///     Gets or sets the current sun (day) in the Eorzean calendar.\n        /// </summary>\n        /// <remarks>\n        ///     Setting values outside the 1..32 range will be adjusted accordingly and carry over to <see cref=\"Moon\" />.\n        /// </remarks>\n        /// <value>The current sun (day) in the Eorzean calendar.</value>\n        public int Sun {\n            get { return _Sun; }\n            set {\n                _Sun = value;\n                while (_Sun < 1) {\n                    _Sun += 32;\n                    Moon--;\n                }\n                while (_Sun > 32) {\n                    _Sun -= 32;\n                    Moon++;\n                }\n            }\n        }\n\n        /// <summary>\n        ///     Gets or sets the current moon (month) in the Eorzean calendar.\n        /// </summary>\n        /// <remarks>\n        ///     Setting values outside the 1..12 range will be adjusted accordingly and carry over to <see cref=\"Year\" />.\n        /// </remarks>\n        /// <value>The current moon (month) in the Eorzean calendar.</value>\n        public int Moon {\n            get { return _Moon; }\n            set {\n                _Moon = value;\n                while (_Moon < 1) {\n                    _Moon += 12;\n                    Year--;\n                }\n                while (_Moon > 12) {\n                    _Moon -= 12;\n                    Year++;\n                }\n            }\n        }\n\n        /// <summary>\n        ///     Gets or sets the current year in the Eorzean calendar.\n        /// </summary>\n        /// <remarks>\n        ///     This value is not actually used anywhere in the game, it is only preserved here to make conversion to real-time\n        ///     possible.\n        /// </remarks>\n        /// <value>The current year in the Eorzean calendar.</value>\n        public int Year { get; set; }\n\n        /// <summary>\n        ///     Gets the total amount of minutes in the Eorzean calendar since zero.\n        /// </summary>\n        /// <value>The total amount of minutes in the Eorzean calendar since zero.</value>\n        public double TotalMinutes {\n            get { return Minute + (60.0 * (Bell + (24.0 * (Sun + (32.0 * (Moon + (12.0 * Year))))))); }\n        }\n\n        /// <summary>\n        ///     Gets the total amount of bells (hours) in the Eorzean calendar since zero.\n        /// </summary>\n        /// <value>The total amount of bells (hours) in the Eorzean calendar since zero.</value>\n        public double TotalBells {\n            get { return Bell + (Minute / 60.0) + (24 * (Sun + (32.0 * (Moon + (12.0 * Year))))); }\n        }\n\n        /// <summary>\n        ///     Gets the total amount of suns (days) in the Eorzean calendar since zero.\n        /// </summary>\n        /// <value>The total amount of suns (days) in the Eorzean calendar since zero.</value>\n        public double TotalSuns {\n            get { return Sun + ((Bell + (Minute / 60.0)) / 24.0) + (32 * (Moon + (12.0 * Year))); }\n        }\n\n        /// <summary>\n        ///     Gets the total amount of moons (months) in the Eorzean calendar since zero.\n        /// </summary>\n        /// <value>The total amount of moons (months) in the Eorzean calendar since zero.</value>\n        public double TotalMoons {\n            get { return Moon + ((Sun + ((Bell + (Minute / 60.0)) / 24.0)) / 32.0) + (12.0 * Year); }\n        }\n\n        /// <summary>\n        ///     Gets the total amount of years in the Eorzean calendar since zero.\n        /// </summary>\n        /// <value>The total amount of years in the Eorzean calendar since zero.</value>\n        public double TotalYears {\n            get { return Year + ((Moon + ((Sun + ((Bell + (Minute / 60.0)) / 24.0)) / 32.0)) / 12.0); }\n        }\n\n        #endregion\n\n        #region Constructors\n\n        /// <summary>\n        ///     Initializes a new instance of the <see cref=\"EorzeaDateTime\" /> class that represents the same time and date as\n        ///     another <see cref=\"EorzeaDateTime\" />.\n        /// </summary>\n        /// <param name=\"source\"><see cref=\"EorzeaDateTime\" /> whose values to copy.</param>\n        public EorzeaDateTime(EorzeaDateTime source) {\n            Minute = source.Minute;\n            Bell = source.Bell;\n            Sun = source.Sun;\n            Moon = source.Moon;\n            Year = source.Year;\n        }\n\n        /// <summary>\n        ///     Initializes a new instance of the <see cref=\"EorzeaDateTime\" /> class with the current time and date.\n        /// </summary>\n        public EorzeaDateTime() : this(DateTime.UtcNow) { }\n\n        /// <summary>\n        ///     Initializes a new instance of the <see cref=\"EorzeaDateTime\" /> class for the specified Unix timestamp.\n        /// </summary>\n        /// <param name=\"unixTime\">Unix timestamp of the time and date.</param>\n        public EorzeaDateTime(int unixTime) : this((long)unixTime) { }\n\n        /// <summary>\n        ///     Initializes a new instance of the <see cref=\"EorzeaDateTime\" /> class for the specified Unix timestamp.\n        /// </summary>\n        /// <param name=\"unixTime\">Unix timestamp of the time and date.</param>\n        public EorzeaDateTime(long unixTime) {\n            SetUnixTime(unixTime);\n        }\n\n        /// <summary>\n        ///     Initializes a new instance of the <see cref=\"EorzeaDateTime\" /> class with the Eorzean equivalent for\n        ///     <see cref=\"DateTime\" />.\n        /// </summary>\n        /// <param name=\"time\"><see cref=\"DateTime\" /> to convert to Eorzean time.</param>\n        public EorzeaDateTime(DateTime time) {\n            SetRealTime(time);\n        }\n\n        #endregion\n\n        /// <summary>\n        ///     Convert the current date and time to a <see cref=\"TimeSpan\" />.\n        /// </summary>\n        /// <returns>Returns the <see cref=\"TimeSpan\" /> of elapsed time since zero in the Eorzean calendar.</returns>\n        public TimeSpan GetTimeSpan() {\n            return new TimeSpan(Sun + (((Year * 12) + Moon) * 32), Bell, Minute, 0, 0);\n        }\n\n        /// <summary>\n        ///     Get the Unix timestamp.\n        /// </summary>\n        /// <returns>Returns the Unix timestamp.</returns>\n        public long GetUnixTime() {\n            var years = Year;\n            var moons = (years * 12.0) + Moon - 1;\n            var suns = (moons * 32.0) + Sun - 1;\n            var bells = (suns * 24.0) + Bell;\n            var minutes = (bells * 60.0) + Minute;\n            var seconds = minutes * 60.0;\n\n            return (long)(seconds / RealToEorzeanFactor);\n        }\n\n        /// <summary>\n        ///     Set the Unix timestamp.\n        /// </summary>\n        /// <param name=\"time\">Unix timestamp to use.</param>\n        /// <returns>Returns self.</returns>\n        public EorzeaDateTime SetUnixTime(long time) {\n            var eorzeaSeconds = time * RealToEorzeanFactor;\n\n            SetEorzeaTime(eorzeaSeconds);\n\n            return this;\n        }\n\n        /// <summary>\n        ///     Set the Eorzean time using the total elapsed seconds since zero.\n        /// </summary>\n        /// <param name=\"eorzeaSeconds\">Elapsed seconds since zero.</param>\n        private void SetEorzeaTime(double eorzeaSeconds) {\n            var minutes = eorzeaSeconds / 60;\n            var bells = minutes / 60;\n            var suns = bells / 24;\n            var moons = suns / 32;\n            var years = moons / 12;\n\n            Minute = (int)(minutes % 60);\n            Bell = (int)(bells % 24);\n            Sun = (int)(suns % 32) + 1;\n            Moon = (int)(moons % 12) + 1;\n            Year = (int)years;\n        }\n\n        /// <summary>\n        ///     Get the real-world <see cref=\"DateTime\" /> of this <see cref=\"EorzeaDateTime\" />.\n        /// </summary>\n        /// <returns>Returns the <see cref=\"DateTime\" /> of this <see cref=\"EorzeaDateTime\" />.</returns>\n        public DateTime GetRealTime() {\n            return Zero + TimeSpan.FromSeconds(GetUnixTime());\n        }\n\n        /// <summary>\n        ///     Set the value of this <see cref=\"EorzeaDateTime\" /> from a real-world <see cref=\"DateTime\" />.\n        /// </summary>\n        /// <param name=\"time\"><see cref=\"DateTime\" /> to convert.</param>\n        /// <returns>Returns self.</returns>\n        public EorzeaDateTime SetRealTime(DateTime time) {\n            var utc = time.ToUniversalTime();\n            var fromZero = utc - Zero;\n            return SetUnixTime((long)fromZero.TotalSeconds);\n        }\n\n        /// <summary>\n        ///     Increase the time and date of a <see cref=\"EorzeaDateTime\" /> by a <see cref=\"TimeSpan\" />.\n        /// </summary>\n        /// <param name=\"lh\"><see cref=\"EorzeaDateTime\" /> to use as base.</param>\n        /// <param name=\"rh\"><see cref=\"TimeSpan\" /> to add to the <see cref=\"EorzeaDateTime\" />.</param>\n        /// <returns>Returns a new <see cref=\"EorzeaDateTime\" /> object with the resulting time and date.</returns>\n        public static EorzeaDateTime operator+(EorzeaDateTime lh, TimeSpan rh) {\n            var copy = lh.Clone();\n            copy.Minute += (int)rh.TotalMinutes;\n            return copy;\n        }\n\n        /// <summary>\n        ///     Decrease the time and date of a <see cref=\"EorzeaDateTime\" /> by a <see cref=\"TimeSpan\" />.\n        /// </summary>\n        /// <param name=\"lh\"><see cref=\"EorzeaDateTime\" /> to use as base.</param>\n        /// <param name=\"rh\"><see cref=\"TimeSpan\" /> to subtract to the <see cref=\"EorzeaDateTime\" />.</param>\n        /// <returns>Returns a new <see cref=\"EorzeaDateTime\" /> object with the resulting time and date.</returns>\n        public static EorzeaDateTime operator-(EorzeaDateTime lh, TimeSpan rh) {\n            var copy = lh.Clone();\n            copy.Minute -= (int)rh.TotalMinutes;\n            return copy;\n        }\n\n        /// <summary>\n        ///     Calculate the difference in time between two <see cref=\"EorzeaDateTime\" />.\n        /// </summary>\n        /// <param name=\"lh\"><see cref=\"EorzeaDateTime\" /> to use as source.</param>\n        /// <param name=\"rh\"><see cref=\"EorzeaDateTime\" /> to use as destination.</param>\n        /// <returns>Returns the <see cref=\"TimeSpan\" /> from <see cref=\"lh\" /> to <see cref=\"rh\" />.</returns>\n        public static TimeSpan operator-(EorzeaDateTime lh, EorzeaDateTime rh) {\n            return lh.GetTimeSpan() - rh.GetTimeSpan();\n        }\n\n        /// <summary>\n        ///     Check if a <see cref=\"EorzeaDateTime\" /> is before or equal to another <see cref=\"EorzeaDateTime\" />.\n        /// </summary>\n        /// <param name=\"lh\"><see cref=\"EorzeaDateTime\" /> to check.</param>\n        /// <param name=\"rh\"><see cref=\"EorzeaDateTime\" /> to check against.</param>\n        /// <returns>Returns <c>true</c> if <see cref=\"lh\" /> is before or equal to <see cref=\"rh\" />; <c>false</c> otherwise.</returns>\n        public static bool operator<=(EorzeaDateTime lh, EorzeaDateTime rh) {\n            return lh.GetUnixTime() <= rh.GetUnixTime();\n        }\n\n        /// <summary>\n        ///     Check if a <see cref=\"EorzeaDateTime\" /> is before another <see cref=\"EorzeaDateTime\" />.\n        /// </summary>\n        /// <param name=\"lh\"><see cref=\"EorzeaDateTime\" /> to check.</param>\n        /// <param name=\"rh\"><see cref=\"EorzeaDateTime\" /> to check against.</param>\n        /// <returns>Returns <c>true</c> if <see cref=\"lh\" /> is before <see cref=\"rh\" />; <c>false</c> otherwise.</returns>\n        public static bool operator<(EorzeaDateTime lh, EorzeaDateTime rh) {\n            return lh.GetUnixTime() < rh.GetUnixTime();\n        }\n\n        /// <summary>\n        ///     Check if a <see cref=\"EorzeaDateTime\" /> is after or equal to another <see cref=\"EorzeaDateTime\" />.\n        /// </summary>\n        /// <param name=\"lh\"><see cref=\"EorzeaDateTime\" /> to check.</param>\n        /// <param name=\"rh\"><see cref=\"EorzeaDateTime\" /> to check against.</param>\n        /// <returns>Returns <c>true</c> if <see cref=\"lh\" /> is after or equal to <see cref=\"rh\" />; <c>false</c> otherwise.</returns>\n        public static bool operator>=(EorzeaDateTime lh, EorzeaDateTime rh) {\n            return lh.GetUnixTime() >= rh.GetUnixTime();\n        }\n\n        /// <summary>\n        ///     Check if a <see cref=\"EorzeaDateTime\" /> is after another <see cref=\"EorzeaDateTime\" />.\n        /// </summary>\n        /// <param name=\"lh\"><see cref=\"EorzeaDateTime\" /> to check.</param>\n        /// <param name=\"rh\"><see cref=\"EorzeaDateTime\" /> to check against.</param>\n        /// <returns>Returns <c>true</c> if <see cref=\"lh\" /> is after <see cref=\"rh\" />; <c>false</c> otherwise.</returns>\n        public static bool operator>(EorzeaDateTime lh, EorzeaDateTime rh) {\n            return lh.GetUnixTime() > rh.GetUnixTime();\n        }\n\n        /// <summary>\n        ///     Clones the value of this <see cref=\"EorzeaDateTime\" /> into a new <see cref=\"EorzeaDateTime\" />.\n        /// </summary>\n        /// <returns>Returns a new <seealso cref=\"EorzeaDateTime\" /> object with the same value as the current.</returns>\n        public EorzeaDateTime Clone() {\n            return new EorzeaDateTime(this);\n        }\n\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Ex/Column.cs",
    "content": "﻿namespace SaintCoinach.Ex {\n    /// <summary>\n    ///     Class for represeting columns inside EX files.\n    /// </summary>\n    public class Column {\n        #region Properties\n\n        /// <summary>\n        ///     Gets the <see cref=\"Header\" /> of the EX file the column is in.\n        /// </summary>\n        /// <value>The <see cref=\"Header\" /> of the EX file the column is in.</value>\n        public Header Header { get; private set; }\n\n        /// <summary>\n        ///     Gets the index of the column inside the EX file.\n        /// </summary>\n        /// <value>The index of the column inside the EX file.</value>\n        public int Index { get; private set; }\n\n        /// <summary>\n        ///     Gets the integer identifier for the type of the column's data.\n        /// </summary>\n        /// <remarks>\n        ///     This value is read from the source header to get the correct object for <see cref=\"Reader\" />, should not be\n        ///     required any further.\n        /// </remarks>\n        /// <value>The integer identifier for the type of the column's data.</value>\n        public int Type { get; private set; }\n\n        /// <summary>\n        ///     Gets the position of the column's data in a row.\n        /// </summary>\n        /// <value>The position of the column's data in a row.</value>\n        public int Offset { get; private set; }\n\n        /// <summary>\n        ///     Gets the <see cref=\"DataReader\" /> used to read column's data.\n        /// </summary>\n        /// <value>The <see cref=\"DataReader\" /> used to read column's data.</value>\n        public DataReader Reader { get; private set; }\n\n        /// <summary>\n        ///     Gets a string indicating what type the column's contents are.\n        /// </summary>\n        /// <value>A string indicating what type the column's contents are.</value>\n        public virtual string ValueType { get { return Reader.Name; } }\n\n        #endregion\n\n        #region Constructors\n\n        /// <summary>\n        ///     Initializes a new instance of the <see cref=\"Column\" /> class.\n        /// </summary>\n        /// <param name=\"header\">The <see cref=\"Header\" /> of the EX file the column is in.</param>\n        /// <param name=\"index\">The index of the column inside the EX file.</param>\n        /// <param name=\"buffer\">A byte-array containing the contents of the header file.</param>\n        /// <param name=\"offset\">The position of the column information inside <c>buffer</c>.</param>\n        public Column(Header header, int index, byte[] buffer, int offset) {\n            const int TypeOffset = 0x00;\n            const int PositionOffset = 0x02;\n\n            Header = header;\n            Index = index;\n            Type = OrderedBitConverter.ToUInt16(buffer, offset + TypeOffset, true);\n            Offset = OrderedBitConverter.ToUInt16(buffer, offset + PositionOffset, true);\n\n            Reader = DataReader.GetReader(Type);\n        }\n\n        #endregion\n\n        #region Read\n\n        /// <summary>\n        ///     Read the column's value in a row, possibly post-processed depending on the column's implementation.\n        /// </summary>\n        /// <param name=\"buffer\">A byte-array containing the contents of the data file.</param>\n        /// <param name=\"row\">The <see cref=\"IDataRow\" /> whose data should be read.</param>\n        /// <returns>Returns the column's value in <c>row</c>.</returns>\n        public virtual object Read(byte[] buffer, IDataRow row) {\n            return ReadRaw(buffer, row);\n        }\n\n        public virtual object Read(byte[] buffer, IDataRow row, int offset) {\n            return ReadRaw(buffer, row, offset);\n        }\n\n        /// <summary>\n        ///     Read the raw column's value in a row.\n        /// </summary>\n        /// <param name=\"buffer\">A byte-array containing the contents of the data file.</param>\n        /// <param name=\"row\">The <see cref=\"IDataRow\" /> whose data should be read.</param>\n        /// <returns>Returns the raw column's value in <c>row</c>.</returns>\n        public object ReadRaw(byte[] buffer, IDataRow row) {\n            return Reader.Read(buffer, this, row);\n        }\n\n        public object ReadRaw(byte[] buffer, IDataRow row, int offset) {\n            return Reader.Read(buffer, offset);\n        }\n\n        #endregion\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Ex/DataReader.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\n\nusing SaintCoinach.Ex.DataReaders;\nusing SaintCoinach.Xiv;\n\nnamespace SaintCoinach.Ex {\n    /// <summary>\n    ///     Base class used in reading data from EX data files.\n    /// </summary>\n    public abstract class DataReader {\n        #region Static\n\n        /// <summary>\n        ///     Mappings of type identifiers used in EX headers to their corresponding <see cref=\"DataReader\" />.\n        /// </summary>\n        private static readonly Dictionary<int, DataReader> DataReaders;\n\n        #endregion\n\n        #region Properties\n\n        /// <summary>\n        ///     Gets the name of the value type read.\n        /// </summary>\n        /// <value>The name of the value type read.</value>\n        public abstract string Name { get; }\n\n        /// <summary>\n        ///     Gets the length of the binary data in bytes.\n        /// </summary>\n        /// <remarks>\n        ///     Should only return the length for data inside the fixed-data part of a row, not variable-length data present after.\n        /// </remarks>\n        /// <value>The length of the binary data in bytes.</value>\n        public abstract int Length { get; }\n\n        /// <summary>\n        ///     Gets the <see cref=\"Type\" /> of the read values.\n        /// </summary>\n        /// <value>The <see cref=\"Type\" /> of the read values.</value>\n        public abstract Type Type { get; }\n\n        #endregion\n\n        #region Constructors\n\n        /// <summary>\n        /// Initializes static members of the <see cref=\"DataReader\"/> class.\n        /// </summary>\n        /// <remarks>\n        /// This creates the mappings of type identifiers and <see cref=\"DataReader\"/>s.\n        /// </remarks>\n        static DataReader() {\n            DataReaders = new Dictionary<int, DataReader> {\n                {\n                    0x0000, new StringDataReader()\n                }, {\n                    0x0001, new DelegateDataReader(\"bool\", 1, typeof(bool), (d, o) => d[o] != 0)\n                }, {\n                    0x0002, new DelegateDataReader(\"sbyte\", 1, typeof(sbyte), (d, o) => (sbyte)d[o])\n                }, {\n                    0x0003, new DelegateDataReader(\"byte\", 1, typeof(byte), (d, o) => d[o])\n                }, {\n                    0x0004,\n                    new DelegateDataReader(\"int16\", 2, typeof(short), (d, o) => OrderedBitConverter.ToInt16(d, o, true))\n                }, {\n                    0x0005,\n                    new DelegateDataReader(\"uint16\", 2, typeof(ushort),\n                        (d, o) => OrderedBitConverter.ToUInt16(d, o, true))\n                }, {\n                    0x0006,\n                    new DelegateDataReader(\"int32\", 4, typeof(int), (d, o) => OrderedBitConverter.ToInt32(d, o, true))\n                }, {\n                    0x0007,\n                    new DelegateDataReader(\"uint32\", 4, typeof(uint), (d, o) => OrderedBitConverter.ToUInt32(d, o, true))\n                }, {\n                    0x0009,\n                    new DelegateDataReader(\"single\", 4, typeof(Single),\n                        (d, o) => OrderedBitConverter.ToSingle(d, o, true))\n                }, {\n                    0x000B,\n                    new DelegateDataReader(\"int64\", 8, typeof(Quad), (d, o) => Quad.Read(d, o, true))\n                }\n            };\n\n\n            for (byte i = 0; i < 8; ++i)\n                DataReaders.Add(0x19 + i, new PackedBooleanDataReader((byte)(1 << i)));\n        }\n\n        #endregion\n\n        /// <summary>\n        ///     Get the <see cref=\"DataReader\" /> for a integer identifier.\n        /// </summary>\n        /// <param name=\"type\">The integer identifier of the data type.</param>\n        /// <returns>Returns the <see cref=\"DataReader\" /> assigned to the given <c>type</c>.</returns>\n        /// <exception cref=\"NotSupportedException\"><c>type</c> is not recognized.</exception>\n        public static DataReader GetReader(int type) {\n            DataReader reader;\n            if (!DataReaders.TryGetValue(type, out reader))\n                throw new NotSupportedException(string.Format(\"Unsupported data type {0:X4}h\", type));\n            return reader;\n        }\n\n        /// <summary>\n        ///     Read a column's data of a row.\n        /// </summary>\n        /// <param name=\"buffer\">A byte-array containing the contents of the EX data file.</param>\n        /// <param name=\"col\"><see cref=\"Column\" /> to read.</param>\n        /// <param name=\"row\"><see cref=\"IDataRow\" /> to read in.</param>\n        /// <returns>Returns the value read from the given <c>row</c> and <c>column</c>.</returns>\n        public abstract object Read(byte[] buffer, Column col, IDataRow row);\n\n        public abstract object Read(byte[] buffer, int offset);\n\n        /// <summary>\n        ///     Get the absolute offset in the EX data file of a column for a given row.\n        /// </summary>\n        /// <param name=\"col\"><see cref=\"Column\" /> whose offset should be returned.</param>\n        /// <param name=\"row\"><see cref=\"IDataRow\" /> to use as base.</param>\n        /// <returns>Returns the absolute offset in the EX data file <c>col</c> inside <c>row</c>.</returns>\n        protected static int GetFieldOffset(Column col, IDataRow row) {\n            return col.Offset + row.Offset;\n        }\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Ex/DataReaders/DelegateDataReader.cs",
    "content": "﻿using System;\n\nnamespace SaintCoinach.Ex.DataReaders {\n    /// <summary>\n    /// Implementation of <see cref=\"DataReader\"/> for reading buffer using a delegate.\n    /// </summary>\n    public class DelegateDataReader : DataReader {\n        /// <summary>\n        /// Delegate invoked when reading data.\n        /// </summary>\n        /// <param name=\"buffer\">A byte-array containing the contents of the EX buffer file.</param>\n        /// <param name=\"offset\">The position of the data inside <c>buffer</c>.</param>\n        /// <returns>Returns the read value at <c>offset</c> inside <c>buffer</c>.</returns>\n        public delegate object ReadDelegate(byte[] buffer, int offset);\n\n        #region Fields\n\n        /// <summary>\n        /// The delegate used to read from the buffer file.\n        /// </summary>\n        private readonly ReadDelegate _Func;\n        /// <summary>\n        /// The length of the binary buffer in bytes.\n        /// </summary>\n        private readonly int _Length;\n        /// <summary>\n        /// The name of the value type read.\n        /// </summary>\n        private readonly string _Name;\n        /// <summary>\n        /// The <see cref=\"Type\" /> of the read values.\n        /// </summary>\n        private readonly Type _Type;\n\n        #endregion\n\n        #region Properties\n\n        /// <summary>\n        ///     Gets the name of the value type read.\n        /// </summary>\n        /// <value>The name of the value type read.</value>\n        public override string Name { get { return _Name; } }\n        /// <summary>\n        ///     Gets the length of the binary buffer in bytes.\n        /// </summary>\n        /// <value>The length of the binary buffer in bytes.</value>\n        public override int Length { get { return _Length; } }\n        /// <summary>\n        ///     Gets the <see cref=\"Type\" /> of the read values.\n        /// </summary>\n        /// <value>The <see cref=\"Type\" /> of the read values.</value>\n        public override Type Type { get { return _Type; } }\n\n        #endregion\n\n        #region Constructors\n\n        /// <summary>\n        /// Initializes a new instance of the <see cref=\"DelegateDataReader\"/> class.\n        /// </summary>\n        /// <param name=\"name\">The name of the value type read.</param>\n        /// <param name=\"length\">The length of the binary buffer in bytes.</param>\n        /// <param name=\"type\">The <see cref=\"Type\" /> of the read values.</param>\n        /// <param name=\"func\">The delegate used to read from the buffer file.</param>\n        internal DelegateDataReader(string name, int length, Type type, ReadDelegate func) {\n            _Name = name;\n            _Length = length;\n            _Type = type;\n            _Func = func;\n        }\n\n        #endregion\n\n        /// <summary>\n        ///     Read a column's buffer of a row.\n        /// </summary>\n        /// <param name=\"buffer\">A byte-array containing the contents of the EX buffer file.</param>\n        /// <param name=\"col\"><see cref=\"Column\" /> to read.</param>\n        /// <param name=\"row\"><see cref=\"IDataRow\" /> to read in.</param>\n        /// <returns>Returns the value read from the given <c>row</c> and <c>column</c>.</returns>\n        public override object Read(byte[] buffer, Column col, IDataRow row) {\n            var offset = GetFieldOffset(col, row);\n            return _Func(buffer, offset);\n        }\n\n        public override object Read(byte[] buffer, int offset) {\n            return _Func(buffer, offset);\n        }\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Ex/DataReaders/PackedBooleanDataReader.cs",
    "content": "﻿using System;\n\nnamespace SaintCoinach.Ex.DataReaders {\n    /// <summary>\n    /// Implementation of <see cref=\"DataReader\"/> for reading a specific bit inside a byte.\n    /// </summary>\n    public class PackedBooleanDataReader : DataReader {\n        #region Fields\n\n        /// <summary>\n        /// Mask used to determine the resulting value.\n        /// </summary>\n        public readonly byte Mask;\n        /// <summary>\n        /// The name of the value type read.\n        /// </summary>\n        private readonly string _Name;\n\n        #endregion\n\n        #region Properties\n\n        /// <summary>\n        ///     Gets the length of the binary buffer in bytes.\n        /// </summary>\n        /// <value><c>1</c></value>\n        public override int Length { get { return 1; } }\n        /// <summary>\n        ///     Gets the name of the value type read.\n        /// </summary>\n        /// <value>The name of the value type read.</value>\n        public override string Name { get { return _Name; } }\n        /// <summary>\n        ///     Gets the <see cref=\"Type\" /> of the read values.\n        /// </summary>\n        /// <value>Value is always typeof(<see cref=\"bool\"/>).</value>\n        public override Type Type { get { return typeof(bool); } }\n\n        #endregion\n\n        #region Constructors\n\n        /// <summary>\n        /// Initializes a new instance of the <see cref=\"PackedBooleanDataReader\"/> class.\n        /// </summary>\n        /// <param name=\"mask\">Mask to match the read byte against.</param>\n        public PackedBooleanDataReader(byte mask) {\n            Mask = mask;\n            _Name = string.Format(\"bit&{0:X2}\", mask);\n        }\n\n        #endregion\n\n        #region Convert\n\n        /// <summary>\n        ///     Read a column's buffer of a row.\n        /// </summary>\n        /// <param name=\"buffer\">A byte-array containing the contents of the EX buffer file.</param>\n        /// <param name=\"col\"><see cref=\"Column\" /> to read.</param>\n        /// <param name=\"row\"><see cref=\"IDataRow\" /> to read in.</param>\n        /// <returns>Returns the value read from the given <c>row</c> and <c>column</c>.</returns>\n        public override object Read(byte[] buffer, Column col, IDataRow row) {\n            var offset = GetFieldOffset(col, row);\n            return (buffer[offset] & Mask) != 0;\n        }\n\n        public override object Read(byte[] buffer, int offset) {\n            return (buffer[offset] & Mask) != 0;\n        }\n\n        #endregion\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Ex/DataReaders/StringDataReader.cs",
    "content": "﻿using System;\n\nnamespace SaintCoinach.Ex.DataReaders {\n    /// <summary>\n    /// Implementation of <see cref=\"DataReader\"/> for reading strings.\n    /// </summary>\n    public class StringDataReader : DataReader {\n        #region Properties\n\n        /// <summary>\n        ///     Gets the length of the binary buffer in bytes.\n        /// </summary>\n        /// <remarks>\n        /// This is only the length of the integer used to store the position of the string. The actual strings are variable-length and appended at the end of a row.\n        /// </remarks>\n        /// <value><c>4</c></value>\n        public override int Length { get { return 4; } }\n        /// <summary>\n        ///     Gets the name of the value type read.\n        /// </summary>\n        /// <value>Value is always <c>str</c></value>\n        public override string Name { get { return \"str\"; } }\n        /// <summary>\n        ///     Gets the <see cref=\"Type\" /> of the read values.\n        /// </summary>\n        /// <value>Value is always typeof(<see cref=\"string\"/>).</value>\n        public override Type Type { get { return typeof(Text.XivString); } }\n\n        #endregion\n\n        #region Convert\n\n        /// <summary>\n        ///     Read a column's buffer of a row.\n        /// </summary>\n        /// <param name=\"buffer\">A byte-array containing the contents of the EX buffer file.</param>\n        /// <param name=\"col\"><see cref=\"Column\" /> to read.</param>\n        /// <param name=\"row\"><see cref=\"IDataRow\" /> to read in.</param>\n        /// <returns>Returns the value read from the given <c>row</c> and <c>column</c>.</returns>\n        public override object Read(byte[] buffer, Column col, IDataRow row) {\n            var fieldOffset = GetFieldOffset(col, row);\n            var endOfFixed = row.Offset + row.Sheet.Header.FixedSizeDataLength;\n\n            var start = endOfFixed + OrderedBitConverter.ToInt32(buffer, fieldOffset, true);\n            if (start < 0)\n                return null;\n\n            var end = start - 1;\n            while (++end < buffer.Length && buffer[end] != 0) { }\n            var len = end - start;\n\n            if (len == 0)\n                return Text.XivString.Empty;\n\n            var binaryString = new byte[len];\n            Array.Copy(buffer, start, binaryString, 0, len);\n\n            return Text.XivStringDecoder.Default.Decode(binaryString);//.ToString();\n        }\n\n        public override object Read(byte[] buffer, int offset) {\n            throw new NotSupportedException();\n        }\n\n        #endregion\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Ex/DataRowBase.cs",
    "content": "﻿using System;\nusing System.Collections.Concurrent;\nusing System.Collections.Generic;\n\nnamespace SaintCoinach.Ex {\n    public abstract class DataRowBase : IDataRow {\n        #region Fields\n\n        private ConcurrentDictionary<int, WeakReference<object>> _ValueReferences;\n\n        #endregion\n\n        #region Constructors\n\n        protected DataRowBase(IDataSheet sheet, int key, int offset) {\n            Sheet = sheet;\n            Key = key;\n            Offset = offset;\n            _ValueReferences = new ConcurrentDictionary<int, WeakReference<object>>();\n        }\n\n        #endregion\n\n        public IDataSheet Sheet { get; private set; }\n        ISheet IRow.Sheet { get { return Sheet; } }\n        public int Key { get; private set; }\n        public int Offset { get; private set; }\n\n        #region IRow Members\n\n        public virtual object this[int columnIndex] {\n            get {\n                if (_ValueReferences.ContainsKey(columnIndex) && _ValueReferences[columnIndex].TryGetTarget(out var value))\n                    return value;\n  \n                var column = Sheet.Header.GetColumn(columnIndex);\n                value = column.Read(Sheet.GetBuffer(), this);\n\n                _ValueReferences.AddOrUpdate(columnIndex,\n                    k => new WeakReference<object>(value),\n                    (k, r) => {\n                        r.SetTarget(value);\n                        return r;\n                    });\n  \n                return value;\n            }\n        }\n\n        public virtual object GetRaw(int columnIndex) {\n            var column = Sheet.Header.GetColumn(columnIndex);\n            return column.ReadRaw(Sheet.GetBuffer(), this);\n        }\n\n        public IEnumerable<object> ColumnValues() {\n            var buffer = Sheet.GetBuffer();\n            foreach (var column in Sheet.Header.Columns)\n                yield return column.Read(buffer, this);\n        }\n\n        #endregion\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Ex/DataSheet.Enumerator.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace SaintCoinach.Ex {\n    partial class DataSheet<T> {\n        private class Enumerator : IEnumerator<T> {\n            #region Fields\n            private DataSheet<T> _Sheet;\n            private IEnumerator<ISheet<T>> _PartialSheetsEnumerator;\n            private IEnumerator<T> _RowEnumerator;\n            #endregion\n\n            #region Constructor\n            public Enumerator(DataSheet<T> sheet) {\n                _Sheet = sheet;\n                sheet.CreateAllPartialSheets();\n\n                _PartialSheetsEnumerator = sheet._PartialSheets.Values.GetEnumerator();\n            }\n            #endregion\n\n            #region IEnumerator<T> Members\n\n            public T Current {\n                get { return _RowEnumerator.Current; }\n            }\n\n            #endregion\n\n            #region IDisposable Members\n\n            public void Dispose() {\n                _PartialSheetsEnumerator.Dispose();\n                if (_RowEnumerator != null)\n                    _RowEnumerator.Dispose();\n                _RowEnumerator = null;\n            }\n\n            #endregion\n\n            #region IEnumerator Members\n\n            object System.Collections.IEnumerator.Current {\n                get { return Current; }\n            }\n\n            public bool MoveNext() {\n                while (_RowEnumerator == null || !_RowEnumerator.MoveNext()) {\n                    if (!_PartialSheetsEnumerator.MoveNext())\n                        return false;\n\n                    _RowEnumerator = _PartialSheetsEnumerator.Current.GetEnumerator();\n                }\n                return true;\n            }\n\n            public void Reset() {\n                _PartialSheetsEnumerator.Reset();\n                if (_RowEnumerator != null)\n                    _RowEnumerator.Dispose();\n                _RowEnumerator = null;\n            }\n\n            #endregion\n        }\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Ex/DataSheet.cs",
    "content": "﻿using System;\nusing System.Collections;\nusing System.Collections.Concurrent;\nusing System.Collections.Generic;\nusing System.Linq;\n\nusing SaintCoinach.IO;\n\nnamespace SaintCoinach.Ex {\n    public partial class DataSheet<T> : IDataSheet<T> where T : IDataRow {\n        #region Fields\n\n        private bool _PartialSheetsCreated = false;\n        private readonly Dictionary<Range, ISheet<T>> _PartialSheets = new Dictionary<Range, ISheet<T>>();\n        private readonly Dictionary<int, ISheet<T>> _RowToPartialSheetMap = new Dictionary<int, ISheet<T>>();\n        private readonly object _partialSheetsLock = new object();\n\n        #endregion\n\n        #region Constructors\n\n        public DataSheet(ExCollection collection, Header header, Language language) {\n            Collection = collection;\n            Header = header;\n            Language = language;\n        }\n\n        #endregion\n\n        public ExCollection Collection { get; private set; }\n        public Header Header { get; private set; }\n        public Language Language { get; private set; }\n\n        public int Count {\n            get {\n                CreateAllPartialSheets();\n                return _PartialSheets.Values.Sum(_ => _.Count);\n            }\n        }\n\n        public IEnumerable<int> Keys {\n            get {\n                CreateAllPartialSheets();\n                return _RowToPartialSheetMap.Keys;\n            }\n        }\n\n        #region IEnumerable<T> Members\n\n        public IEnumerator<T> GetEnumerator() {\n            return new Enumerator(this);\n        }\n\n        #endregion\n\n        #region IEnumerable Members\n\n        IEnumerator IEnumerable.GetEnumerator() {\n            return GetEnumerator();\n        }\n\n        #endregion\n\n        #region IDataSheet Members\n\n        public byte[] GetBuffer() {\n            throw new NotSupportedException();\n        }\n\n        #endregion\n\n        #region Factory\n\n        protected virtual ISheet<T> CreatePartialSheet(Range range, File file) {\n            return new PartialDataSheet<T>(this, range, file);\n        }\n\n        #endregion\n\n        #region Helpers\n\n        protected File GetPartialFile(Range range) {\n            const string PartialFileNameFormat = \"exd/{0}_{1}{2}.exd\";\n\n            var partialFileName = string.Format(PartialFileNameFormat, Header.Name, range.Start, Language.GetSuffix());\n            var file = Collection.PackCollection.GetFile(partialFileName);\n\n            return file;\n        }\n\n        protected ISheet<T> GetPartialSheet(int row) {\n            if (_RowToPartialSheetMap.ContainsKey(row))\n                return _RowToPartialSheetMap[row];\n\n            var res = Header.DataFileRanges.Where(_ => _.Contains(row)).ToArray();\n            if (!res.Any())\n                throw new ArgumentOutOfRangeException();\n\n            lock (_partialSheetsLock) {\n                var range = res.First();\n                if (!_PartialSheets.TryGetValue(range, out var partial)) {\n                    partial = CreatePartialSheet(range);\n                }\n                return partial;\n            }\n        }\n\n        private void CreateAllPartialSheets() {\n            lock (_partialSheetsLock) {\n                if (_PartialSheetsCreated)\n                    return;\n                foreach (var range in Header.DataFileRanges.Where(range => !_PartialSheets.ContainsKey(range))) {\n                    CreatePartialSheet(range);\n                }\n                _PartialSheetsCreated = true;\n            }\n        }\n\n        private ISheet<T> CreatePartialSheet(Range range) {\n            var file = GetPartialFile(range);\n\n            var partial = CreatePartialSheet(range, file);\n            _PartialSheets.Add(range, partial);\n            foreach (var key in partial.Keys)\n                _RowToPartialSheetMap.Add(key, partial);\n            return partial;\n        }\n\n        #endregion\n\n        #region ISheet<T> Members\n\n        public T this[int row] { get { return GetPartialSheet(row)[row]; } }\n\n        #endregion\n\n        #region ISheet Members\n\n        public string Name { get { return Header.Name + Language.GetSuffix(); } }\n\n        public bool ContainsRow(int row) {\n            CreateAllPartialSheets();\n\n            return _RowToPartialSheetMap.ContainsKey(row);\n        }\n\n        IRow ISheet.this[int row] { get { return this[row]; } }\n\n        public object this[int row, int column] { get { return this[row]?[column]; } }\n\n        #endregion\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Ex/ExCollection.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.IO;\nusing System.Linq;\nusing System.Text;\n\nusing SaintCoinach.IO;\n\nusing File = SaintCoinach.IO.File;\nusing System.Collections.Concurrent;\n\nnamespace SaintCoinach.Ex {\n    public class ExCollection {\n        #region Fields\n\n        private readonly Dictionary<int, string> _SheetIdentifiers = new Dictionary<int, string>();\n\n        private readonly ConcurrentDictionary<string, WeakReference<ISheet>> _Sheets =\n            new ConcurrentDictionary<string, WeakReference<ISheet>>();\n\n        private HashSet<string> _AvailableSheets;\n\n        #endregion\n\n        #region Properties\n\n        public PackCollection PackCollection { get; private set; }\n        public Language ActiveLanguage { get; set; }\n        public string ActiveLanguageCode { get { return ActiveLanguage.GetCode(); } }\n        public IEnumerable<string> AvailableSheets { get { return _AvailableSheets; } }\n\n        #endregion\n\n        #region Constructors\n\n        #region Constructor\n\n        public ExCollection(PackCollection packCollection) {\n            PackCollection = packCollection;\n\n            BuildIndex();\n        }\n\n        #endregion\n\n        #endregion\n\n        #region Index\n\n        private void BuildIndex() {\n            var exRoot = PackCollection.GetFile(\"exd/root.exl\");\n\n            var available = new List<string>();\n\n            using (var ms = new MemoryStream(exRoot.GetData())) {\n                using (var s = new StreamReader(ms, Encoding.ASCII)) {\n                    s.ReadLine(); // EXLT,2\n\n                    while (!s.EndOfStream) {\n                        var line = s.ReadLine();\n                        if (string.IsNullOrWhiteSpace(line)) continue;\n\n                        var split = line.Split(',');\n                        if (split.Length != 2)\n                            continue;\n\n                        var name = split[0];\n                        var id = int.Parse(split[1]);\n\n                        available.Add(name);\n                        if (id >= 0)\n                            _SheetIdentifiers.Add(id, name);\n                    }\n                }\n            }\n\n            _AvailableSheets = new HashSet<string>(available);\n        }\n\n        #endregion\n\n        #region Get\n\n        public bool SheetExists(int id) {\n            return _SheetIdentifiers.ContainsKey(id);\n        }\n\n        public bool SheetExists(string name) {\n            //name = FixName(name);\n            return _AvailableSheets.Contains(name);\n        }\n\n        public ISheet<T> GetSheet<T>(int id) where T : IRow {\n            return (ISheet<T>)GetSheet(id);\n        }\n\n        public ISheet GetSheet(int id) {\n            var name = _SheetIdentifiers[id];\n            return GetSheet(name);\n        }\n\n        public ISheet<T> GetSheet<T>(string name) where T : IRow {\n            return (ISheet<T>)GetSheet(name);\n        }\n\n        public ISheet GetSheet(string name) {\n            const string ExHPathFormat = \"exd/{0}.exh\";\n\n            if (_Sheets.TryGetValue(name, out var sheetRef) && sheetRef.TryGetTarget(out var sheet)) return sheet;\n\n            //name = FixName(name);\n            if (!_AvailableSheets.Contains(name))\n                throw new KeyNotFoundException($\"Unknown sheet '{name}'\");\n\n            var exhPath = string.Format(ExHPathFormat, name);\n            var exh = PackCollection.GetFile(exhPath);\n\n            var header = CreateHeader(name, exh);\n            sheet = CreateSheet(header);\n\n            _Sheets.GetOrAdd(name, n => new WeakReference<ISheet>(sheet)).SetTarget(sheet);\n            return sheet;\n        }\n\n        public string FixName(string name) {\n            var res = _AvailableSheets.Where(_ => string.Equals(name, _, StringComparison.OrdinalIgnoreCase)).ToArray();\n\n            return res.Any() ? res.First() : name;\n        }\n\n        #endregion\n\n        #region Factory\n\n        protected virtual Header CreateHeader(string name, File file) {\n            return new Header(this, name, file);\n        }\n\n        protected virtual ISheet CreateSheet(Header header) {\n            if (header.Variant == 1)\n                return CreateSheet<Variant1.DataRow>(header);\n            return CreateSheet<Variant2.DataRow>(header);\n        }\n\n        private ISheet CreateSheet<T>(Header header) where T : IDataRow {\n            if (header.AvailableLanguagesCount >= 1)\n                return new MultiSheet<MultiRow, T>(this, header);\n            return new DataSheet<T>(this, header, header.AvailableLanguages.First());\n        }\n\n        #endregion\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Ex/Header.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.IO;\nusing System.Linq;\n\nusing File = SaintCoinach.IO.File;\n\nnamespace SaintCoinach.Ex {\n    public class Header {\n        #region Static\n\n        protected static readonly Dictionary<int, Language> LanguageMap = new Dictionary<int, Language> {\n            {\n                0, Language.None\n            }, {\n                1, Language.Japanese\n            }, {\n                2, Language.English\n            }, {\n                3, Language.German\n            }, {\n                4, Language.French\n            }, {\n                5, Language.ChineseSimplified /*\"chs\"*/\n            }, {\n                6, Language.ChineseTraditional /*\"cht\"*/\n            }, {\n                7, Language.Korean /*\"ko\"*/\n            }, {\n                8, Language.TraditionalChinese\n            }\n        };\n\n        #endregion\n\n        #region Fields\n\n        private Language[] _AvailableLanguages;\n        private Column[] _Columns;\n        private Range[] _DataFileRanges;\n\n        #endregion\n\n        #region Properties\n\n        public ExCollection Collection { get; private set; }\n        public File File { get; private set; }\n        public string Name { get; private set; }\n        public int Variant { get; private set; }\n        public IEnumerable<Column> Columns { get { return _Columns; } }\n        public int ColumnCount {  get { return _Columns.Length; } }\n        public IEnumerable<Range> DataFileRanges { get { return _DataFileRanges; } }\n        public IEnumerable<Language> AvailableLanguages { get { return _AvailableLanguages; } }\n        public int AvailableLanguagesCount { get { return _AvailableLanguages.Count(x => x != Language.None); } }\n        public int FixedSizeDataLength { get; private set; }\n\n        #endregion\n\n        #region Constructors\n\n        #region Constructor\n\n        public Header(ExCollection collection, string name, File file) {\n            Collection = collection;\n            Name = name;\n            File = file;\n\n            Build();\n        }\n\n        #endregion\n\n        #endregion\n\n        public Column GetColumn(int index) {\n            return _Columns[index];\n        }\n\n        #region Factory\n\n        protected virtual Column CreateColumn(int index, byte[] data, int offset) {\n            return new Column(this, index, data, offset);\n        }\n\n        #endregion\n\n        #region Build\n\n        private void Build() {\n            const uint Magic = 0x46485845; // EXHF\n            const int MinimumLength = 0x2E;\n            const int FixedSizeDataLengthOffset = 0x06;\n            const int VariantOffset = 0x10;\n            const int DataOffset = 0x20;\n\n            var buffer = File.GetData();\n\n            if (buffer.Length < MinimumLength)\n                throw new InvalidDataException(\"EXH file is too short\");\n            if (OrderedBitConverter.ToUInt32(buffer, 0, false) != Magic)\n                throw new InvalidDataException(\"File not a EX header\");\n\n\n            FixedSizeDataLength = OrderedBitConverter.ToUInt16(buffer, FixedSizeDataLengthOffset, true);\n            Variant = OrderedBitConverter.ToUInt16(buffer, VariantOffset, true);\n            if (Variant != 1 && Variant != 2)\n                throw new NotSupportedException();\n\n            var currentPosition = DataOffset;\n            ReadColumns(buffer, ref currentPosition);\n            ReadPartialFiles(buffer, ref currentPosition);\n            ReadSuffixes(buffer, ref currentPosition);\n        }\n\n        private void ReadColumns(byte[] buffer, ref int position) {\n            const int CountOffset = 0x08;\n            const int Length = 0x04;\n\n            var count = OrderedBitConverter.ToUInt16(buffer, CountOffset, true);\n            _Columns = new Column[count];\n            for (var i = 0; i < count; ++i) {\n                _Columns[i] = CreateColumn(i, buffer, position);\n                position += Length;\n            }\n        }\n\n        private void ReadPartialFiles(byte[] buffer, ref int position) {\n            const int CountOffset = 0x0A;\n            const int Length = 0x08;\n\n            var count = OrderedBitConverter.ToUInt16(buffer, CountOffset, true);\n            _DataFileRanges = new Range[count];\n            for (var i = 0; i < count; ++i) {\n                var min = OrderedBitConverter.ToInt32(buffer, position + 0x00, true);\n                var len = OrderedBitConverter.ToInt32(buffer, position + 0x04, true);\n\n                _DataFileRanges[i] = new Range(min, len);\n\n                position += Length;\n            }\n        }\n\n        private void ReadSuffixes(byte[] buffer, ref int position) {\n            const int CountOffset = 0x0C;\n            const int Length = 0x02;\n\n            // ScreenImage and CutScreenImage reference localized image files,\n            // however their available languages are only None.  Perhaps there\n            // is a flag to use a global list of available languages in this\n            // buffer?\n\n            var count = OrderedBitConverter.ToUInt16(buffer, CountOffset, true);\n            var langs = new List<Language>();\n            for (var i = 0; i < count; ++i) {\n                langs.Add(LanguageMap[buffer[position]]);\n                position += Length;\n            }\n            _AvailableLanguages = langs.Where(_ => _ != Language.Unsupported).ToArray();\n        }\n\n        #endregion\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Ex/IDataRow.cs",
    "content": "﻿namespace SaintCoinach.Ex {\n    public interface IDataRow : IRow {\n        #region Properties\n\n        int Offset { get; }\n        new IDataSheet Sheet { get; }\n\n        #endregion\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Ex/IDataSheet.Generic.cs",
    "content": "﻿namespace SaintCoinach.Ex {\n    public interface IDataSheet<out T> : ISheet<T>, IDataSheet\n        where T : IDataRow { }\n}\n"
  },
  {
    "path": "SaintCoinach/Ex/IDataSheet.cs",
    "content": "﻿namespace SaintCoinach.Ex {\n    public interface IDataSheet : ISheet {\n        #region Properties\n\n        Language Language { get; }\n\n        #endregion\n\n        byte[] GetBuffer();\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Ex/IMultiRow.cs",
    "content": "﻿namespace SaintCoinach.Ex {\n    public interface IMultiRow : IRow {\n        #region Properties\n\n        new IMultiSheet Sheet { get; }\n        object this[int columnIndex, Language language] { get; }\n\n        object GetRaw(int columnIndex, Language language);\n\n        #endregion\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Ex/IMultiSheet.Generic.cs",
    "content": "﻿using System.Collections.Generic;\n\nnamespace SaintCoinach.Ex {\n    public interface IMultiSheet<out TMulti, out TData> : ISheet<TMulti>, IMultiSheet\n        where TMulti : IMultiRow\n        where TData : IRow {\n        #region Properties\n\n        new ISheet<TData> ActiveSheet { get; }\n        new TMulti this[int row] { get; }\n\n        #endregion\n\n        new ISheet<TData> GetLocalisedSheet(Language language);\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Ex/IMultiSheet.cs",
    "content": "﻿using System.Collections.Generic;\n\nnamespace SaintCoinach.Ex {\n    public interface IMultiSheet : ISheet {\n        #region Properties\n\n        ISheet ActiveSheet { get; }\n        new IMultiRow this[int row] { get; }\n\n        #endregion\n\n        ISheet GetLocalisedSheet(Language language);\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Ex/IRow.cs",
    "content": "﻿using System.Collections.Generic;\n\nnamespace SaintCoinach.Ex {\n    public interface IRow {\n        #region Properties\n\n        ISheet Sheet { get; }\n        int Key { get; }\n        object this[int columnIndex] { get; }\n\n        object GetRaw(int columnIndex);\n\n        #endregion\n\n        IEnumerable<object> ColumnValues();\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Ex/ISheet.Generic.cs",
    "content": "﻿using System.Collections.Generic;\n\nnamespace SaintCoinach.Ex {\n    public interface ISheet<out T> : IEnumerable<T>, ISheet where T : IRow {\n        #region Properties\n\n        new T this[int row] { get; }\n\n        #endregion\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Ex/ISheet.cs",
    "content": "﻿using System.Collections;\nusing System.Collections.Generic;\n\nnamespace SaintCoinach.Ex {\n    public interface ISheet : IEnumerable {\n        #region Properties\n\n        string Name { get; }\n        Header Header { get; }\n        ExCollection Collection { get; }\n        int Count { get; }\n        IRow this[int row] { get; }\n        object this[int row, int column] { get; }\n        IEnumerable<int> Keys { get; }\n\n        #endregion\n\n        bool ContainsRow(int row);\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Ex/Language.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.ComponentModel;\nusing System.Linq;\nusing System.Reflection;\n\nnamespace SaintCoinach.Ex {\n    public enum Language {\n        [Description(\"\")] None,\n        [Description(\"ja\")] Japanese,\n        [Description(\"en\")] English,\n        [Description(\"de\")] German,\n        [Description(\"fr\")] French,\n        [Description(\"chs\")] ChineseSimplified,\n        [Description(\"cht\")] ChineseTraditional,\n        [Description(\"ko\")] Korean,\n        [Description(\"tc\")] TraditionalChinese,\n\n        [Description(\"?\")] Unsupported\n    }\n\n    public static class LanguageExtensions {\n        #region Static\n\n        private static readonly Dictionary<Language, string> LangToCode;\n        private static readonly Dictionary<string, Language> CodeToLang;\n\n        #endregion\n\n        #region Properties\n\n        public static IEnumerable<Language> Languages {\n            get {\n                yield return Language.Japanese;\n                yield return Language.English;\n                yield return Language.French;\n                yield return Language.German;\n                yield return Language.ChineseSimplified;\n                yield return Language.ChineseTraditional;\n                yield return Language.Korean;\n            }\n        }\n\n        #endregion\n\n        #region Constructors\n\n        static LanguageExtensions() {\n            LangToCode = new Dictionary<Language, string>();\n            CodeToLang = new Dictionary<string, Language>(StringComparer.OrdinalIgnoreCase);\n\n            foreach (var lang in Languages) {\n                var code = GetCode(lang);\n                LangToCode.Add(lang, code);\n                CodeToLang.Add(code, lang);\n            }\n        }\n\n        #endregion\n\n        public static string GetCode(this Language self) {\n            if (LangToCode.ContainsKey(self))\n                return LangToCode[self];\n            var type = typeof(Language);\n            var fields = type.GetFields(BindingFlags.Public | BindingFlags.Static);\n            var fi = fields.First(_ => (Language)_.GetValue(null) == self);\n            var attr =\n                fi.GetCustomAttributes(typeof(DescriptionAttribute), false).FirstOrDefault() as DescriptionAttribute;\n\n            return attr == null ? self.ToString() : attr.Description;\n        }\n\n        public static string GetSuffix(this Language self) {\n            var code = self.GetCode();\n            if (code.Length > 0)\n                return \"_\" + code;\n            return string.Empty;\n        }\n\n        public static Language GetFromCode(string code) {\n            return CodeToLang.ContainsKey(code) ? CodeToLang[code] : Language.Unsupported;\n        }\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Ex/MultiRow.cs",
    "content": "﻿using System.Collections.Generic;\n\nnamespace SaintCoinach.Ex {\n    public class MultiRow : IMultiRow {\n        #region Constructors\n\n        #region Constructor\n\n        public MultiRow(IMultiSheet sheet, int key) {\n            Sheet = sheet;\n            Key = key;\n        }\n\n        #endregion\n\n        #endregion\n\n        public IMultiSheet Sheet { get; private set; }\n        ISheet IRow.Sheet { get { return Sheet; } }\n        public int Key { get; private set; }\n\n        #region IMultiRow Members\n\n        public object this[int columnIndex, Language language] {\n            get { return Sheet.GetLocalisedSheet(language)[Key, columnIndex]; }\n        }\n\n        public object this[int columnIndex] { get { return Sheet.ActiveSheet[Key, columnIndex]; } }\n\n        object IRow.GetRaw(int columnIndex) {\n            return Sheet.ActiveSheet[Key].GetRaw(columnIndex);\n        }\n\n        object IMultiRow.GetRaw(int columnIndex, Language language) {\n            return Sheet.GetLocalisedSheet(language)[Key]?.GetRaw(columnIndex);\n        }\n\n        public IEnumerable<object> ColumnValues() {\n            return Sheet.ActiveSheet[Key].ColumnValues();\n        }\n\n        #endregion\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Ex/MultiSheet.Enumerator.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace SaintCoinach.Ex {\n    partial class MultiSheet<TMulti, TData> {\n        private class Enumerator : IEnumerator<TMulti> {\n            #region Fields\n            private MultiSheet<TMulti, TData> _Sheet;\n            private IEnumerator<int> _KeyEnumerator;\n            #endregion\n\n            #region Constructor\n            public Enumerator(MultiSheet<TMulti, TData> sheet) {\n                _Sheet = sheet;\n                _KeyEnumerator = sheet.ActiveSheet.Keys.GetEnumerator();\n            }\n            #endregion\n\n            #region IEnumerator<TMulti> Members\n\n            public TMulti Current {\n                get { return _Sheet[_KeyEnumerator.Current]; }\n            }\n\n            #endregion\n\n            #region IDisposable Members\n\n            public void Dispose() {\n                _KeyEnumerator.Dispose();\n            }\n\n            #endregion\n\n            #region IEnumerator Members\n\n            object System.Collections.IEnumerator.Current {\n                get { return Current; }\n            }\n\n            public bool MoveNext() {\n                return _KeyEnumerator.MoveNext();\n            }\n\n            public void Reset() {\n                _KeyEnumerator.Reset();\n            }\n\n            #endregion\n        }\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Ex/MultiSheet.cs",
    "content": "﻿using System;\nusing System.Collections;\nusing System.Collections.Concurrent;\nusing System.Collections.Generic;\nusing System.Linq;\n\nnamespace SaintCoinach.Ex {\n    public partial class MultiSheet<TMulti, TData> : IMultiSheet<TMulti, TData>\n        where TMulti : IMultiRow\n        where TData : IDataRow {\n        #region Fields\n\n        private readonly ConcurrentDictionary<Language, ISheet<TData>> _LocalisedSheets =\n            new ConcurrentDictionary<Language, ISheet<TData>>();\n\n        private ConcurrentDictionary<int, TMulti> _Rows = new ConcurrentDictionary<int, TMulti>();\n\n        #endregion\n\n        #region Constructors\n\n        public MultiSheet(ExCollection collection, Header header) {\n            Collection = collection;\n            Header = header;\n        }\n\n        #endregion\n\n        #region Properties\n\n        public ExCollection Collection { get; private set; }\n        public Header Header { get; private set; }\n        public ISheet<TData> ActiveSheet { get { return GetLocalisedSheet(Collection.ActiveLanguage); } }\n        public int Count { get { return ActiveSheet.Count; } }\n        public IEnumerable<int> Keys { get { return ActiveSheet.Keys; } }\n\n        #endregion\n\n        #region Get\n\n        public ISheet<TData> GetLocalisedSheet(Language language) {\n            return _LocalisedSheets.GetOrAdd(language, l => {\n                if (!Header.AvailableLanguages.Contains(l))\n                    throw new NotSupportedException();\n\n                return CreateLocalisedSheet(l);\n            });\n        }\n\n        #endregion\n\n        #region IEnumerable<TMulti> Members\n\n        public IEnumerator<TMulti> GetEnumerator() {\n            return new Enumerator(this);\n        }\n\n        #endregion\n\n        #region IEnumerable Members\n\n        IEnumerator IEnumerable.GetEnumerator() {\n            return GetEnumerator();\n        }\n\n        #endregion\n\n        #region Factory\n\n        protected virtual TMulti CreateMultiRow(int row) {\n            return (TMulti)Activator.CreateInstance(typeof(TMulti), this, row);\n        }\n\n        protected virtual ISheet<TData> CreateLocalisedSheet(Language language) {\n            return new DataSheet<TData>(Collection, Header, language);\n        }\n\n        #endregion\n\n        #region IMultiSheet<TMulti,TSource> Members\n\n        public TMulti this[int key] {\n            get {\n                return _Rows.GetOrAdd(key, k => CreateMultiRow(k));\n            }\n        }\n\n        #endregion\n\n        #region ISheet Members\n\n        public string Name { get { return Header.Name; } }\n\n        public bool ContainsRow(int row) {\n            return ActiveSheet.ContainsRow(row);\n        }\n\n        IRow ISheet.this[int row] { get { return this[row]; } }\n\n        public object this[int row, int column] { get { return this[row][column]; } }\n\n        #endregion\n\n        #region IMultiSheet Members\n\n        ISheet IMultiSheet.ActiveSheet { get { return ActiveSheet; } }\n\n        ISheet IMultiSheet.GetLocalisedSheet(Language language) {\n            return GetLocalisedSheet(language);\n        }\n\n        IMultiRow IMultiSheet.this[int row] { get { return this[row]; } }\n\n        #endregion\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Ex/PartialDataSheet.Enumerator.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace SaintCoinach.Ex {\n    partial class PartialDataSheet<T> {\n        private class Enumerator : IEnumerator<T> {\n            #region Fields\n            private PartialDataSheet<T> _Sheet;\n            private IEnumerator<KeyValuePair<int, int>> _InnerEnumerator;\n            #endregion\n\n            #region Constructor\n            public Enumerator(PartialDataSheet<T> sheet) {\n                _Sheet = sheet;\n                _InnerEnumerator = sheet._RowOffsets.GetEnumerator();\n            }\n            #endregion\n\n            #region IEnumerator<T> Members\n\n            public T Current {\n                get {\n                    var inner = _InnerEnumerator.Current;\n                    return _Sheet._Rows.GetOrAdd(inner.Key, k => _Sheet.CreateRow(k, inner.Value));\n                }\n            }\n\n            #endregion\n\n            #region IDisposable Members\n\n            public void Dispose() {\n                _InnerEnumerator.Dispose();\n            }\n\n            #endregion\n\n            #region IEnumerator Members\n\n            object System.Collections.IEnumerator.Current {\n                get { return Current; }\n            }\n\n            public bool MoveNext() {\n                return _InnerEnumerator.MoveNext();\n            }\n\n            public void Reset() {\n                _InnerEnumerator.Reset();\n            }\n\n            #endregion\n        }\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Ex/PartialDataSheet.cs",
    "content": "﻿using System;\nusing System.Collections;\nusing System.Collections.Concurrent;\nusing System.Collections.Generic;\nusing System.Linq;\n\nusing SaintCoinach.IO;\n\nnamespace SaintCoinach.Ex {\n    public partial class PartialDataSheet<T> : IDataSheet<T> where T : IDataRow {\n        #region Fields\n\n        private ConcurrentDictionary<int, T> _Rows;\n        private Dictionary<int, int> _RowOffsets = new Dictionary<int, int>();\n\n        #endregion\n\n        #region Properties\n\n        public IDataSheet<T> SourceSheet { get; private set; }\n        public Range Range { get; private set; }\n        public File File { get; private set; }\n        public IEnumerable<int> Keys { get { return _RowOffsets.Keys; } }\n\n        #endregion\n\n        #region Constructors\n\n        public PartialDataSheet(IDataSheet<T> sourceSheet, Range range, File file) {\n            SourceSheet = sourceSheet;\n            Range = range;\n            File = file;\n\n            Build();\n        }\n\n        #endregion\n\n        public Language Language { get { return SourceSheet.Language; } }\n        public int Count { get { return _RowOffsets.Count; } }\n\n        #region IEnumerable<T> Members\n\n        public IEnumerator<T> GetEnumerator() {\n            return new Enumerator(this);\n        }\n\n        #endregion\n\n        #region IEnumerable Members\n\n        IEnumerator IEnumerable.GetEnumerator() {\n            return GetEnumerator();\n        }\n\n        #endregion\n\n        #region IDataSheet Members\n\n        public byte[] GetBuffer() {\n            return File.GetData();\n        }\n\n        #endregion\n\n        #region Build\n\n        private void Build() {\n            const int HeaderLengthOffset = 0x08;\n\n            const int EntriesOffset = 0x20;\n            const int EntryLength = 0x08;\n\n            const int EntryKeyOffset = 0x00;\n            const int EntryPositionOffset = 0x04;\n\n            var buffer = File.GetData();\n\n            var headerLen = OrderedBitConverter.ToInt32(buffer, HeaderLengthOffset, true);\n            var count = headerLen / EntryLength;\n            var currentPosition = EntriesOffset;\n\n            _Rows = new ConcurrentDictionary<int, T>();\n            for (var i = 0; i < count; ++i) {\n                var key = OrderedBitConverter.ToInt32(buffer, currentPosition + EntryKeyOffset, true);\n                var off = OrderedBitConverter.ToInt32(buffer, currentPosition + EntryPositionOffset, true);\n                _RowOffsets.Add(key, off);\n                //_Rows.Add(key, CreateRow(key, off));\n                currentPosition += EntryLength;\n            }\n        }\n\n        #endregion\n\n        #region Factory\n\n        protected virtual T CreateRow(int key, int offset) {\n            return (T)Activator.CreateInstance(typeof(T), this, key, offset);\n        }\n\n        #endregion\n\n        #region ISheet<T> Members\n\n        public IEnumerable<T> GetAllRows() {\n            return _Rows.Values;\n        }\n\n        public T this[int key] {\n            get {\n                return _Rows.GetOrAdd(key, k => {\n                    if (_RowOffsets.TryGetValue(key, out var offset))\n                        return CreateRow(k, offset);\n                    else\n                        return default(T);\n                });\n            }\n        }\n\n        #endregion\n\n        #region ISheet Members\n\n        public string Name { get { return SourceSheet.Name + \"_\" + Range.Start; } }\n\n        public Header Header { get { return SourceSheet.Header; } }\n\n        public ExCollection Collection { get { return SourceSheet.Collection; } }\n\n        public bool ContainsRow(int row) {\n            return _RowOffsets.ContainsKey(row);\n        }\n\n        IRow ISheet.this[int row] { get { return this[row]; } }\n\n        public object this[int row, int column] { get { return this[row][column]; } }\n\n        #endregion\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Ex/Relational/Definition/GroupDataDefinition.cs",
    "content": "﻿using Newtonsoft.Json.Linq;\nusing System;\nusing System.Collections.Generic;\nusing System.Linq;\n\nnamespace SaintCoinach.Ex.Relational.Definition {\n    public class GroupDataDefinition : IDataDefinition {\n        #region Fields\n\n        private ICollection<IDataDefinition> _Members = new List<IDataDefinition>();\n        private int _Length;\n\n        #endregion\n\n        #region Properties\n\n        public ICollection<IDataDefinition> Members {\n            get { return _Members; }\n            internal set {\n                _Members = value;\n                _Length = _Members.Sum(_ => _.Length);\n            }\n        }\n\n        #endregion\n\n        public int Length { get { return _Length; } }\n\n        public IDataDefinition Clone() {\n            var clone = new GroupDataDefinition();\n\n            foreach (var member in Members)\n                clone.Members.Add(member.Clone());\n\n            return clone;\n        }\n\n        #region IDataDefinition Members\n\n        public object Convert(IDataRow row, object value, int index) {\n            if (index < 0 || index >= Length)\n                throw new ArgumentOutOfRangeException(\"index\");\n\n            var convertedValue = value;\n            var pos = 0;\n            foreach (var member in Members) {\n                var newPos = pos + member.Length;\n                if (newPos > index) {\n                    var innerIndex = index - pos;\n\n                    convertedValue = member.Convert(row, value, innerIndex);\n\n                    break;\n                }\n                pos = newPos;\n            }\n            return convertedValue;\n        }\n\n        public string GetName(int index) {\n            if (index < 0 || index >= Length)\n                throw new ArgumentOutOfRangeException(\"index\");\n\n            string value = null;\n            var pos = 0;\n            foreach (var member in Members) {\n                var newPos = pos + member.Length;\n                if (newPos > index) {\n                    var innerIndex = index - pos;\n                    value = member.GetName(innerIndex);\n                    break;\n                }\n                pos = newPos;\n            }\n            return value;\n        }\n\n        public string GetValueTypeName(int index) {\n            if (index < 0 || index >= Length)\n                throw new ArgumentOutOfRangeException(\"index\");\n\n            string value = null;\n            var pos = 0;\n            foreach (var member in Members) {\n                var newPos = pos + member.Length;\n                if (newPos > index) {\n                    var innerIndex = index - pos;\n                    value = member.GetValueTypeName(innerIndex);\n                    break;\n                }\n                pos = newPos;\n            }\n            return value;\n        }\n\n        public Type GetValueType(int index) {\n            if (index < 0 || index >= Length)\n                throw new ArgumentOutOfRangeException(\"index\");\n\n            Type value = null;\n            var pos = 0;\n            foreach (var member in Members) {\n                var newPos = pos + member.Length;\n                if (newPos > index) {\n                    var innerIndex = index - pos;\n                    value = member.GetValueType(innerIndex);\n                    break;\n                }\n                pos = newPos;\n            }\n            return value;\n        }\n\n        #endregion\n\n        #region Serialization\n\n        public JObject ToJson() {\n            return new JObject() {\n                [\"type\"] = \"group\",\n                [\"members\"] = new JArray(_Members.Select(m => m.ToJson())),\n            };\n        }\n\n        public static GroupDataDefinition FromJson(JToken obj) {\n            return new GroupDataDefinition() {\n                Members = obj[\"members\"].Select(m => DataDefinitionSerializer.FromJson(m)).ToList()\n            };\n        }\n\n        public void ResolveReferences(SheetDefinition sheetDef) {\n            foreach (var member in _Members)\n                member.ResolveReferences(sheetDef);\n        }\n\n        #endregion\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Ex/Relational/Definition/IDataDefinition.cs",
    "content": "﻿using Newtonsoft.Json.Linq;\nusing System;\n\nnamespace SaintCoinach.Ex.Relational.Definition {\n    public interface IDataDefinition {\n        #region Properties\n\n        int Length { get; }\n\n        #endregion\n\n        /// <param name=\"row\">The row to convert a value of.</param>\n        /// <param name=\"value\">Raw value as read from the file.</param>\n        /// <param name=\"index\">Index in the definition on which the method is called.</param>\n        object Convert(IDataRow row, object value, int index);\n\n        /// <param name=\"index\">Index in the definition on which the method is called.</param>\n        string GetName(int index);\n\n        /// <param name=\"index\">Index in the definition on which the method is called.</param>\n        string GetValueTypeName(int index);\n\n        /// <param name=\"index\">Index in the definition on which the method is called.</param>\n        Type GetValueType(int index);\n\n        IDataDefinition Clone();\n\n        JObject ToJson();\n\n        void ResolveReferences(SheetDefinition sheetDef);\n    }\n\n    public static class DataDefinitionSerializer {\n        public static IDataDefinition FromJson(JToken obj) {\n            var type = (string)obj[\"type\"];\n            if (type == null)\n                return SingleDataDefinition.FromJson(obj);\n            else if (type == \"group\")\n                return GroupDataDefinition.FromJson(obj);\n            else if (type == \"repeat\")\n                return RepeatDataDefinition.FromJson(obj);\n            else\n                throw new ArgumentException(\"Invalid definition type.\", \"obj\");\n        }\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Ex/Relational/Definition/PositionedDataDefinition.cs",
    "content": "﻿using Newtonsoft.Json.Linq;\nusing System;\n\nnamespace SaintCoinach.Ex.Relational.Definition {\n    public class PositionedDataDefinition {\n        #region Properties\n\n        public IDataDefinition InnerDefinition { get; set; }\n\n        public int Length { get { return InnerDefinition == null ? 0 : InnerDefinition.Length; } }\n\n        public int Index { get; set; }\n\n        #endregion\n\n        public PositionedDataDefinition Clone() {\n            var clone = new PositionedDataDefinition {\n                Index = Index,\n                InnerDefinition = InnerDefinition.Clone()\n            };\n\n\n            return clone;\n        }\n\n        #region Things\n\n        public object Convert(IDataRow row, object value, int index) {\n            var innerIndex = index - Index;\n            if (innerIndex < 0 || innerIndex >= Length)\n                throw new ArgumentOutOfRangeException(\"index\");\n\n            return InnerDefinition.Convert(row, value, innerIndex);\n        }\n\n        public string GetName(int index) {\n            var innerIndex = index - Index;\n            if (innerIndex < 0 || innerIndex >= Length)\n                throw new ArgumentOutOfRangeException(\"index\");\n\n            return InnerDefinition.GetName(innerIndex);\n        }\n\n        public string GetValueTypeName(int index) {\n            var innerIndex = index - Index;\n            if (innerIndex < 0 || innerIndex >= Length)\n                throw new ArgumentOutOfRangeException(\"index\");\n\n            return InnerDefinition.GetValueTypeName(innerIndex);\n        }\n\n        public Type GetValueType(int index) {\n            var innerIndex = index - Index;\n            if (innerIndex < 0 || innerIndex >= Length)\n                throw new ArgumentOutOfRangeException(\"index\");\n\n            return InnerDefinition.GetValueType(innerIndex);\n        }\n\n        #endregion\n\n        #region Serialization\n\n        public JObject ToJson() {\n            var obj = InnerDefinition.ToJson();\n            if (Index > 0)\n                obj.AddFirst(new JProperty(\"index\", Index));\n            return obj;\n        }\n\n        public static PositionedDataDefinition FromJson(JToken obj) {\n            return new PositionedDataDefinition() {\n                Index = (int?)obj[\"index\"] ?? 0,\n                InnerDefinition = DataDefinitionSerializer.FromJson(obj)\n            };\n        }\n\n        public void ResolveReferences(SheetDefinition sheetDef) {\n            InnerDefinition.ResolveReferences(sheetDef);\n        }\n\n        #endregion\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Ex/Relational/Definition/RelationDefinition.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.IO;\nusing System.Linq;\nusing System.Text;\n\nusing Newtonsoft.Json.Linq;\n\nnamespace SaintCoinach.Ex.Relational.Definition {\n    public class RelationDefinition {\n        #region Fields\n\n        private bool _IsCompiled;\n        private ICollection<SheetDefinition> _SheetDefinitions = new List<SheetDefinition>();\n        private Dictionary<string, SheetDefinition> _SheetMap;\n        private Dictionary<string, SheetDefinition> _SheetDefinitionMap = new Dictionary<string, SheetDefinition>();\n\n        #endregion\n\n        #region Properties\n\n        public ICollection<SheetDefinition> SheetDefinitions {\n            get { return _SheetDefinitions; }\n            internal set { _SheetDefinitions = value; }\n        }\n\n        public string Version { get; set; }\n\n        #endregion\n\n        #region Compile\n\n        public void Compile() {\n            _SheetMap = _SheetDefinitions.ToDictionary(_ => _.Name, _ => _);\n\n            foreach (var sheet in SheetDefinitions)\n                sheet.Compile();\n\n            _IsCompiled = true;\n        }\n\n        #endregion\n\n        #region Helpers\n\n        public bool TryGetSheet(string name, out SheetDefinition def) {\n            if (_IsCompiled)\n                return _SheetMap.TryGetValue(name, out def);\n\n            return _SheetDefinitionMap.TryGetValue(name, out def);\n        }\n\n        public SheetDefinition GetOrCreateSheet(string name) {\n            if (!TryGetSheet(name, out var def)) {\n                def = new SheetDefinition { Name = name };\n                _SheetDefinitions.Add(def);\n                _SheetDefinitionMap[name] = def;\n            }\n            return def;\n        }\n\n        #endregion\n\n        #region Serialization\n\n        public JObject ToJson() {\n            return new JObject {\n                [\"version\"] = Version,\n                [\"sheets\"] = new JArray(_SheetDefinitions.OrderBy(s => s.Name).Select(s => s.ToJson()))\n            };\n        }\n\n        public static RelationDefinition FromJson(JToken obj) {\n            return new RelationDefinition() {\n                Version = (string)obj[\"version\"],\n                SheetDefinitions = new List<SheetDefinition>(obj[\"sheets\"].Select(j => SheetDefinition.FromJson(j)))\n            };\n        }\n\n        public static RelationDefinition FromJson(string json) {\n            var obj = Newtonsoft.Json.JsonConvert.DeserializeObject<JObject>(json);\n            return RelationDefinition.FromJson(obj);\n        }\n\n        #endregion\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Ex/Relational/Definition/RepeatDataDefinition.cs",
    "content": "﻿using Newtonsoft.Json.Linq;\nusing System;\n\n\nnamespace SaintCoinach.Ex.Relational.Definition {\n    public class RepeatDataDefinition : IDataDefinition {\n        #region Properties\n\n        public int NamingOffset { get; set; }\n        public int RepeatCount { get; set; }\n        public IDataDefinition RepeatedDefinition { get; set; }\n\n        #endregion\n\n        #region Constructors\n\n        public RepeatDataDefinition() {\n            NamingOffset = 0;\n        }\n\n        #endregion\n\n        public int Length { get { return RepeatCount * (RepeatedDefinition == null ? 0 : RepeatedDefinition.Length); } }\n\n        public IDataDefinition Clone() {\n            return new RepeatDataDefinition {\n                NamingOffset = NamingOffset,\n                RepeatCount = RepeatCount,\n                RepeatedDefinition = RepeatedDefinition.Clone()\n            };\n        }\n\n        #region IDataDefinition Members\n\n        public object Convert(IDataRow row, object value, int index) {\n            if (index < 0 || index >= Length)\n                throw new ArgumentOutOfRangeException(\"index\");\n\n            var innerIndex = index % RepeatedDefinition.Length;\n\n            return RepeatedDefinition.Convert(row, value, innerIndex);\n        }\n\n        public string GetName(int index) {\n            if (index < 0 || index >= Length)\n                throw new ArgumentOutOfRangeException(\"index\");\n\n            var repeatNr = index / RepeatedDefinition.Length;\n            var innerIndex = index % RepeatedDefinition.Length;\n\n            var baseName = RepeatedDefinition.GetName(innerIndex);\n            return string.Format(\"{0}[{1}]\", baseName, repeatNr + NamingOffset);\n        }\n\n        public string GetValueTypeName(int index) {\n            if (index < 0 || index >= Length)\n                throw new ArgumentOutOfRangeException(\"index\");\n\n            var innerIndex = index % RepeatedDefinition.Length;\n\n            return RepeatedDefinition.GetValueTypeName(innerIndex);\n        }\n\n        public Type GetValueType(int index) {\n            if (index < 0 || index >= Length)\n                throw new ArgumentOutOfRangeException(\"index\");\n\n            var innerIndex = index % RepeatedDefinition.Length;\n\n            return RepeatedDefinition.GetValueType(innerIndex);\n        }\n\n        #endregion\n\n        #region Serialization\n\n        public JObject ToJson() {\n            return new JObject {\n                [\"type\"] = \"repeat\",\n                [\"count\"] = RepeatCount,\n                [\"definition\"] = RepeatedDefinition.ToJson()\n            };\n        }\n\n        public static RepeatDataDefinition FromJson(JToken obj) {\n            return new RepeatDataDefinition() {\n                RepeatCount = (int)obj[\"count\"],\n                RepeatedDefinition = DataDefinitionSerializer.FromJson(obj[\"definition\"])\n            };\n        }\n\n        public void ResolveReferences(SheetDefinition sheetDef) {\n            RepeatedDefinition.ResolveReferences(sheetDef);\n        }\n\n        #endregion\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Ex/Relational/Definition/SheetDefinition.cs",
    "content": "﻿using Newtonsoft.Json.Linq;\nusing System;\nusing System.Collections.Generic;\nusing System.Linq;\n\nnamespace SaintCoinach.Ex.Relational.Definition {\n    public class SheetDefinition {\n        #region Fields\n\n        private Dictionary<int, PositionedDataDefinition> _ColumnDefinitionMap;\n        private Dictionary<int, string> _ColumnIndexToNameMap;\n        private Dictionary<string, int> _ColumnNameToIndexMap;\n        private Dictionary<int, string> _ColumnValueTypeNames;\n        private Dictionary<int, Type> _ColumnValueTypes;\n        private ICollection<PositionedDataDefinition> _DataDefinitions = new List<PositionedDataDefinition>();\n        private int? _DefaultColumnIndex;\n        private bool _IsCompiled;\n\n        #endregion\n\n        #region Properties\n\n        public ICollection<PositionedDataDefinition> DataDefinitions {\n            get { return _DataDefinitions; }\n            internal set { _DataDefinitions = value; }\n        }\n\n        public string Name { get; set; }\n        public string DefaultColumn { get; set; }\n        public bool IsGenericReferenceTarget { get; set; }\n\n        #endregion\n\n        #region Compile\n\n        public void Compile() {\n            _ColumnDefinitionMap = new Dictionary<int, PositionedDataDefinition>();\n            _ColumnNameToIndexMap = new Dictionary<string, int>();\n            _ColumnIndexToNameMap = new Dictionary<int, string>();\n            _ColumnValueTypeNames = new Dictionary<int, string>();\n            _ColumnValueTypes = new Dictionary<int, Type>();\n            DataDefinitions = DataDefinitions.OrderBy(d => d.Index).ToList();\n            foreach (var def in DataDefinitions) {\n                for (var i = 0; i < def.Length; ++i) {\n                    var offset = def.Index + i;\n                    _ColumnDefinitionMap.Add(offset, def);\n\n                    var name = def.GetName(offset);\n                    _ColumnNameToIndexMap.Add(name, offset);\n                    _ColumnIndexToNameMap.Add(offset, name);\n                    _ColumnValueTypeNames.Add(offset, def.GetValueTypeName(offset));\n                    _ColumnValueTypes.Add(offset, def.GetValueType(offset));\n                }\n            }\n\n            if (!string.IsNullOrWhiteSpace(DefaultColumn)) {\n                if (_ColumnNameToIndexMap.TryGetValue(DefaultColumn, out var defCol))\n                    _DefaultColumnIndex = defCol;\n                else\n                    _DefaultColumnIndex = null;\n            } else\n                _DefaultColumnIndex = null;\n\n            _IsCompiled = true;\n        }\n\n        #endregion\n\n        #region Helpers\n\n        public bool TryGetDefinition(int index, out PositionedDataDefinition definition) {\n            if (_IsCompiled)\n                return _ColumnDefinitionMap.TryGetValue(index, out definition);\n\n            var res = DataDefinitions.Where(_ => _.Index <= index && index < (_.Index + _.Length)).ToArray();\n            definition = res.Any() ? res.First() : null;\n\n            return definition != null;\n        }\n\n        public int? GetDefaultColumnIndex() {\n            if (_IsCompiled)\n                return _DefaultColumnIndex;\n            return string.IsNullOrWhiteSpace(DefaultColumn) ? null : FindColumn(DefaultColumn);\n        }\n\n        public int? FindColumn(string columnName) {\n            if (_IsCompiled) {\n                if (_ColumnNameToIndexMap.ContainsKey(columnName))\n                    return _ColumnNameToIndexMap[columnName];\n                return null;\n            }\n\n            foreach (var def in DataDefinitions) {\n                for (var i = 0; i < def.Length; ++i) {\n                    var n = def.GetName(def.Index + i);\n                    if (string.Equals(columnName, n))\n                        return def.Index + i;\n                }\n            }\n\n            return null;\n        }\n\n        public IEnumerable<string> GetAllColumnNames() {\n            if (_IsCompiled) {\n                foreach (var n in _ColumnNameToIndexMap.Keys)\n                    yield return n;\n                yield break;\n            }\n\n            foreach (var def in DataDefinitions) {\n                for (var i = 0; i < def.Length; ++i)\n                    yield return def.GetName(def.Index + i);\n            }\n        }\n\n        public string GetColumnName(int index) {\n            if (_IsCompiled)\n                return _ColumnIndexToNameMap.ContainsKey(index) ? _ColumnIndexToNameMap[index] : null;\n\n            return TryGetDefinition(index, out var def) ? def.GetName(index) : null;\n        }\n\n        public string GetValueTypeName(int index) {\n            if (_IsCompiled)\n                return _ColumnValueTypeNames.ContainsKey(index) ? _ColumnValueTypeNames[index] : null;\n\n            return TryGetDefinition(index, out var def) ? def.GetValueTypeName(index) : null;\n        }\n\n        public Type GetValueType(int index) {\n            if (_IsCompiled)\n                return _ColumnValueTypes.ContainsKey(index) ? _ColumnValueTypes[index] : null;\n\n            return TryGetDefinition(index, out var def) ? def.GetValueType(index) : null;\n        }\n\n        public object Convert(IDataRow row, object value, int index) {\n            return TryGetDefinition(index, out var def) ? def.Convert(row, value, index) : value;\n        }\n\n        #endregion\n\n        #region Serialization\n\n        public JObject ToJson() {\n            var obj = new JObject { [\"sheet\"] = Name };\n            if (DefaultColumn != null)\n                obj[\"defaultColumn\"] = DefaultColumn;\n            if (IsGenericReferenceTarget)\n                obj[\"isGenericReferenceTarget\"] = true;\n            obj[\"definitions\"] = new JArray(_DataDefinitions.Select(dd => dd.ToJson()));\n            return obj;\n        }\n\n        public static SheetDefinition FromJson(JToken obj) {\n            var sheetDef = new SheetDefinition() {\n                Name = (string)obj[\"sheet\"],\n                DefaultColumn = (string)obj[\"defaultColumn\"],\n                IsGenericReferenceTarget = (bool?)obj[\"isGenericReferenceTarget\"] ?? false,\n                DataDefinitions = new List<PositionedDataDefinition>(obj[\"definitions\"].Select(j => PositionedDataDefinition.FromJson(j)))\n            };\n\n            foreach (var dataDef in sheetDef.DataDefinitions)\n                dataDef.ResolveReferences(sheetDef);\n\n            return sheetDef;\n        }\n\n        #endregion\n\n        public override string ToString() {\n            return Name;\n        }\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Ex/Relational/Definition/SingleDataDefinition.cs",
    "content": "﻿using Newtonsoft.Json.Linq;\nusing SaintCoinach.Ex.Relational.ValueConverters;\nusing System;\n\nnamespace SaintCoinach.Ex.Relational.Definition {\n    public class SingleDataDefinition : IDataDefinition {\n        #region Properties\n\n        public string Name { get; set; }\n        public IValueConverter Converter { get; set; }\n\n        #endregion\n\n        public int Length { get { return 1; } }\n\n        public IDataDefinition Clone() {\n            return new SingleDataDefinition {\n                Name = Name,\n                Converter = Converter\n            };\n        }\n\n        #region IDataDefinition Members\n\n        public object Convert(IDataRow row, object value, int index) {\n            if (index != 0)\n                throw new ArgumentOutOfRangeException(\"index\");\n\n            return Converter != null ? Converter.Convert(row, value) : value;\n        }\n\n        public string GetName(int index) {\n            if (index != 0)\n                throw new ArgumentOutOfRangeException(\"index\");\n\n            return Name;\n        }\n\n        public string GetValueTypeName(int index) {\n            if (index != 0)\n                throw new ArgumentOutOfRangeException(\"index\");\n\n            return Converter?.TargetTypeName;\n        }\n\n        public Type GetValueType(int index) {\n            if (index != 0)\n                throw new ArgumentOutOfRangeException(\"index\");\n\n            return Converter?.TargetType;\n        }\n\n        #endregion\n\n        #region Serialization\n\n        public JObject ToJson() {\n            var obj = new JObject() {\n                [\"name\"] = Name\n            };\n            if (Converter != null)\n                obj[\"converter\"] = Converter.ToJson();\n            return obj;\n        }\n\n        public static SingleDataDefinition FromJson(JToken obj) {\n            var converterObj = (JObject)obj[\"converter\"];\n            IValueConverter converter = null;\n            if (converterObj != null) {\n                var type = (string)converterObj[\"type\"];\n                if (type == \"color\")\n                    converter = ColorConverter.FromJson(converterObj);\n                else if (type == \"generic\")\n                    converter = GenericReferenceConverter.FromJson(converterObj);\n                else if (type == \"icon\")\n                    converter = IconConverter.FromJson(converterObj);\n                else if (type == \"multiref\")\n                    converter = MultiReferenceConverter.FromJson(converterObj);\n                else if (type == \"link\")\n                    converter = SheetLinkConverter.FromJson(converterObj);\n                else if (type == \"tomestone\")\n                    converter = TomestoneOrItemReferenceConverter.FromJson(converterObj);\n                else if (type == \"complexlink\")\n                    converter = ComplexLinkConverter.FromJson(converterObj);\n                else\n                    throw new ArgumentException(\"Invalid converter type.\", \"obj\");\n            }\n\n            return new SingleDataDefinition() {\n                Name = (string)obj[\"name\"],\n                Converter = converter\n            };\n        }\n\n        public void ResolveReferences(SheetDefinition sheetDef) {\n            if (Converter != null)\n                Converter.ResolveReferences(sheetDef);\n        }\n\n        #endregion\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Ex/Relational/Definition/ViewDefinition.cs",
    "content": "﻿using Newtonsoft.Json.Linq;\nusing SaintCoinach.Ex.Relational;\nusing SaintCoinach.Ex.Relational.ValueConverters;\nusing System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace SaintCoinach.Ex.Relational.Definition {\n    public class ViewDefinition {\n        public string SheetName { get; internal set; }\n        public ICollection<ViewColumnDefinition> ColumnDefinitions { get; internal set; }\n\n        public static ViewDefinition FromJson(JToken obj) {\n            return new ViewDefinition() {\n                SheetName = (string)obj[\"sheet\"],\n                ColumnDefinitions = obj[\"columns\"].Cast<JObject>().Select(ViewColumnDefinition.FromJson).ToList()\n            };\n        }\n    }\n\n    public class ViewColumnDefinition {\n        public string ColumnName { get; internal set; }\n        public IValueConverter Converter { get; internal set; }\n\n        public static ViewColumnDefinition FromJson(JObject obj) {\n            var converterObj = (JObject)obj[\"converter\"];\n            IValueConverter converter = null;\n            if (converterObj != null) {\n                var type = (string)converterObj[\"type\"];\n                if (type == \"quad\")\n                    converter = QuadConverter.FromJson(converterObj);\n                else\n                    throw new ArgumentException(\"Invalid converter type.\", \"obj\");\n            }\n\n            return new ViewColumnDefinition() {\n                ColumnName = (string)obj[\"name\"],\n                Converter = converter\n            };\n        }\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Ex/Relational/IRelationalDataRow.cs",
    "content": "﻿namespace SaintCoinach.Ex.Relational {\n    public interface IRelationalDataRow : IRelationalRow, IDataRow {\n        #region Properties\n\n        new IRelationalDataSheet Sheet { get; }\n\n        #endregion\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Ex/Relational/IRelationalDataSheet.Generic.cs",
    "content": "﻿namespace SaintCoinach.Ex.Relational {\n    public interface IRelationalDataSheet<out T> : IRelationalDataSheet, IDataSheet<T>, IRelationalSheet<T>\n        where T : IRelationalDataRow {\n        #region Properties\n\n        new T this[int key] { get; }\n\n        #endregion\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Ex/Relational/IRelationalDataSheet.cs",
    "content": "﻿namespace SaintCoinach.Ex.Relational {\n    public interface IRelationalDataSheet : IRelationalSheet, IDataSheet { }\n}\n"
  },
  {
    "path": "SaintCoinach/Ex/Relational/IRelationalMultiRow.cs",
    "content": "﻿namespace SaintCoinach.Ex.Relational {\n    public interface IRelationalMultiRow : IRelationalRow, IMultiRow {\n        #region Properties\n\n        new IRelationalMultiSheet Sheet { get; }\n        object this[string columnName, Language language] { get; }\n        object GetRaw(string columnName, Language language);\n\n        #endregion\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Ex/Relational/IRelationalMultiSheet.Generic.cs",
    "content": "﻿using System.Collections.Generic;\n\nnamespace SaintCoinach.Ex.Relational {\n    public interface IRelationalMultiSheet<out TMulti, out TData> : IMultiSheet<TMulti, TData>,\n                                                                    IRelationalSheet<TMulti>, IRelationalMultiSheet\n        where TMulti : IRelationalMultiRow\n        where TData : IRelationalDataRow {\n        #region Properties\n\n        new IRelationalSheet<TData> ActiveSheet { get; }\n        new TMulti this[int row] { get; }\n\n        #endregion\n\n        new IRelationalSheet<TData> GetLocalisedSheet(Language language);\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Ex/Relational/IRelationalMultiSheet.cs",
    "content": "﻿using System.Collections.Generic;\n\nnamespace SaintCoinach.Ex.Relational {\n    public interface IRelationalMultiSheet : IRelationalSheet, IMultiSheet {\n        #region Properties\n\n        new IRelationalSheet ActiveSheet { get; }\n        new IRelationalMultiRow this[int row] { get; }\n\n        #endregion\n\n        new IRelationalSheet GetLocalisedSheet(Language language);\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Ex/Relational/IRelationalRow.cs",
    "content": "﻿namespace SaintCoinach.Ex.Relational {\n    public interface IRelationalRow : IRow {\n        #region Properties\n\n        new IRelationalSheet Sheet { get; }\n        object DefaultValue { get; }\n        object this[string columnName] { get; }\n\n        object GetRaw(string columnName);\n\n        #endregion\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Ex/Relational/IRelationalSheet.Generic.cs",
    "content": "﻿namespace SaintCoinach.Ex.Relational {\n    public interface IRelationalSheet<out T> : ISheet<T>, IRelationalSheet where T : IRelationalRow {\n        #region Properties\n\n        new T this[int key] { get; }\n\n        #endregion\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Ex/Relational/IRelationalSheet.cs",
    "content": "﻿using System.Collections.Generic;\n\nnamespace SaintCoinach.Ex.Relational {\n    public interface IRelationalSheet : ISheet {\n        #region Properties\n\n        new RelationalHeader Header { get; }\n        new RelationalExCollection Collection { get; }\n        new IRelationalRow this[int row] { get; }\n        object this[int row, string columnName] { get; }\n\n        #endregion\n\n        IRelationalRow IndexedLookup(string index, int key);\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Ex/Relational/IValueConverter.cs",
    "content": "﻿using Newtonsoft.Json.Linq;\nusing SaintCoinach.Ex.Relational.Definition;\nusing System;\n\nnamespace SaintCoinach.Ex.Relational {\n    public interface IValueConverter {\n        #region Properties\n\n        string TargetTypeName { get; }\n        Type TargetType { get; }\n\n        #endregion\n\n        object Convert(IDataRow row, object rawValue);\n\n        JObject ToJson();\n\n        void ResolveReferences(SheetDefinition sheetDef);\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Ex/Relational/RelationalColumn.cs",
    "content": "﻿using SaintCoinach.Ex.Relational.Definition;\n\nnamespace SaintCoinach.Ex.Relational {\n    public class RelationalColumn : Column {\n        private bool _hasDefinition;\n        private PositionedDataDefinition _Definition;\n\n        #region Properties\n\n        public new RelationalHeader Header { get { return (RelationalHeader)base.Header; } }\n\n        public PositionedDataDefinition Definition {\n            get {\n                if (_hasDefinition)\n                    return _Definition;\n\n                if (Header.SheetDefinition != null) {\n                    if (Header.SheetDefinition.TryGetDefinition(Index, out var definition))\n                        _Definition = definition;\n                }\n\n                _hasDefinition = true;\n                return _Definition;\n            }\n        }\n\n        public string Name {\n            get {\n                return Header.SheetDefinition?.GetColumnName(Index);\n            }\n        }\n\n        public override string ValueType {\n            get {\n                var def = Header.SheetDefinition;\n                if (def == null) return base.ValueType;\n\n                var t = def.GetValueTypeName(Index);\n                return t ?? base.ValueType;\n            }\n        }\n\n        #endregion\n\n        #region Constructors\n\n        #region Constructor\n\n        public RelationalColumn(RelationalHeader header, int index, byte[] buffer, int offset)\n            : base(header, index, buffer, offset) { }\n\n        #endregion\n\n        #endregion\n\n        #region Read\n\n        public override object Read(byte[] buffer, IDataRow row) {\n            var baseVal = base.Read(buffer, row);\n\n            var def = Definition;\n            return def != null ? def.Convert(row, baseVal, Index) : baseVal;\n        }\n\n        public override object Read(byte[] buffer, IDataRow row, int offset) {\n            var baseVal = base.Read(buffer, row, offset);\n\n            var def = Definition;\n            return def != null ? def.Convert(row, baseVal, Index) : baseVal;\n        }\n\n        #endregion\n\n        public override string ToString() {\n            return Name ?? Index.ToString();\n        }\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Ex/Relational/RelationalDataIndex.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace SaintCoinach.Ex.Relational {\n    public class RelationalDataIndex<T> where T : IDataRow {\n        #region Fields\n\n        private Dictionary<int, T> _IndexedRows;\n\n        #endregion\n\n        #region Properties\n\n        public IDataSheet<T> SourceSheet { get; private set; }\n        public Column IndexColumn { get; private set; }\n\n        #endregion\n\n\n        public RelationalDataIndex (IDataSheet<T> sourceSheet, Column indexColumn) {\n            SourceSheet = sourceSheet;\n            IndexColumn = indexColumn;\n\n            BuildIndex();\n        }\n\n        private void BuildIndex() {\n            _IndexedRows = new Dictionary<int, T>();\n\n            var index = IndexColumn.Index;\n\n            foreach (var row in SourceSheet) {\n                var value = row.GetRaw(index);\n                _IndexedRows[Convert.ToInt32(value)] = row;\n            }\n        }\n\n        public T this[int key] {\n            get {\n                return _IndexedRows.TryGetValue(key, out var row) ? row : default(T);\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Ex/Relational/RelationalDataSheet.cs",
    "content": "﻿using System.Collections.Concurrent;\nusing System.Collections.Generic;\nusing System.Linq;\n\nusing SaintCoinach.IO;\n\nnamespace SaintCoinach.Ex.Relational {\n    public class RelationalDataSheet<T> : DataSheet<T>, IRelationalDataSheet<T> where T : IRelationalDataRow {\n        #region Fields\n\n        private ConcurrentDictionary<string, RelationalDataIndex<T>> _indexes = new ConcurrentDictionary<string, RelationalDataIndex<T>>();\n\n        #endregion\n\n\n        #region Constructors\n\n        public RelationalDataSheet(RelationalExCollection collection, RelationalHeader header, Language language)\n            : base(collection, header, language) { }\n\n        #endregion\n\n        public new RelationalExCollection Collection { get { return (RelationalExCollection)base.Collection; } }\n        public new RelationalHeader Header { get { return (RelationalHeader)base.Header; } }\n\n        #region Factory\n\n        protected override ISheet<T> CreatePartialSheet(Range range, File file) {\n            return new RelationalPartialDataSheet<T>(this, range, file);\n        }\n\n        #endregion\n\n        #region IRelationalSheet Members\n\n        IRelationalRow IRelationalSheet.this[int row] { get { return this[row]; } }\n\n        public object this[int row, string columnName] { get { return this[row][columnName]; } }\n\n        public IRelationalRow IndexedLookup(string indexName, int key) {\n            if (key == 0)\n                return null;\n\n            var index = _indexes.GetOrAdd(indexName, i => {\n                var column = Header.FindColumn(indexName);\n                if (column == null)\n                    throw new KeyNotFoundException();\n\n                return new RelationalDataIndex<T>(this, column);\n            });\n            return index[key];\n        }\n\n        #endregion\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Ex/Relational/RelationalExCollection.cs",
    "content": "﻿using System.Linq;\n\nusing SaintCoinach.Ex.Relational.Definition;\nusing SaintCoinach.IO;\n\nnamespace SaintCoinach.Ex.Relational {\n    public class RelationalExCollection : ExCollection {\n        #region Fields\n\n        private RelationDefinition _Definition = new RelationDefinition();\n\n        #endregion\n\n        #region Properties\n\n        public RelationDefinition Definition { get { return _Definition; } set { _Definition = value; } }\n\n        #endregion\n\n        #region Constructors\n\n        #region Constructor\n\n        public RelationalExCollection(PackCollection packCollection) : base(packCollection) { }\n\n        #endregion\n\n        #endregion\n\n        #region Factory\n\n        protected override Header CreateHeader(string name, File file) {\n            return new RelationalHeader(this, name, file);\n        }\n\n        protected override ISheet CreateSheet(Header header) {\n            var relHeader = (RelationalHeader)header;\n            if (relHeader.Variant == 2)\n                return CreateSheet<Variant2.RelationalDataRow>(relHeader);\n            return CreateSheet<Variant1.RelationalDataRow>(relHeader);\n        }\n\n        private ISheet CreateSheet<T>(RelationalHeader header) where T : IRelationalDataRow {\n            if (header.AvailableLanguagesCount >= 1)\n                return new RelationalMultiSheet<RelationalMultiRow, T>(this, header);\n            return new RelationalDataSheet<T>(this, header, header.AvailableLanguages.First());\n        }\n\n        #endregion\n\n        #region Get\n\n        public new IRelationalSheet<T> GetSheet<T>(int id) where T : IRelationalRow {\n            return (IRelationalSheet<T>)GetSheet(id);\n        }\n\n        public new IRelationalSheet GetSheet(int id) {\n            return (IRelationalSheet)base.GetSheet(id);\n        }\n\n        public new IRelationalSheet<T> GetSheet<T>(string name) where T : IRelationalRow {\n            return (IRelationalSheet<T>)GetSheet(name);\n        }\n\n        public new IRelationalSheet GetSheet(string name) {\n            return (IRelationalSheet)base.GetSheet(name);\n        }\n\n        #endregion\n\n\n        #region Find\n\n        public IRelationalRow FindReference(int key) {\n            // Optimization for unlikely references.\n            if (key <= 0)\n                return null;\n\n            foreach (var sheetDef in Definition.SheetDefinitions.Where(d => d.IsGenericReferenceTarget)) {\n                var sheet = GetSheet(sheetDef.Name);\n                if (!sheet.Header.DataFileRanges.Any(r => r.Contains(key)))\n                    continue;\n\n                if (!sheet.ContainsRow(key))\n                    continue;\n\n                return sheet[key];\n            }\n            return null;\n        }\n\n        #endregion\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Ex/Relational/RelationalHeader.cs",
    "content": "﻿using System.Collections.Generic;\nusing System.Linq;\n\nusing SaintCoinach.Ex.Relational.Definition;\nusing SaintCoinach.IO;\n\nnamespace SaintCoinach.Ex.Relational {\n    public class RelationalHeader : Header {\n        #region Fields\n\n        private readonly RelationalColumn[] _Columns;\n\n        #endregion\n\n        #region Properties\n\n        public new RelationalExCollection Collection { get { return (RelationalExCollection)base.Collection; } }\n        public new IEnumerable<RelationalColumn> Columns { get { return _Columns; } }\n\n        public RelationalColumn DefaultColumn {\n            get {\n                var def = SheetDefinition;\n                if (def == null) return null;\n\n                var i = def.GetDefaultColumnIndex();\n                return i.HasValue ? GetColumn(i.Value) : null;\n            }\n            set {\n                var def = GetOrCreateSheetDefinition();\n                def.DefaultColumn = value == null ? null : value.Name;\n            }\n        }\n\n        public SheetDefinition SheetDefinition {\n            get {\n                SheetDefinition def;\n                return Collection.Definition.TryGetSheet(Name, out def) ? def : null;\n            }\n        }\n\n        #endregion\n\n        #region Constructors\n\n        #region Constructor\n\n        public RelationalHeader(RelationalExCollection collection, string name, File file)\n            : base(collection, name, file) {\n            _Columns = base.Columns.Cast<RelationalColumn>().ToArray();\n        }\n\n        #endregion\n\n        #endregion\n\n        public new RelationalColumn GetColumn(int index) {\n            return _Columns[index];\n        }\n\n        public SheetDefinition GetOrCreateSheetDefinition() {\n            return Collection.Definition.GetOrCreateSheet(Name);\n        }\n\n        #region Factory\n\n        protected override Column CreateColumn(int index, byte[] data, int offset) {\n            return new RelationalColumn(this, index, data, offset);\n        }\n\n        #endregion\n\n        #region Helpers\n\n        public RelationalColumn FindColumn(string name) {\n            var def = SheetDefinition;\n            if (def == null) return null;\n\n            var i = def.FindColumn(name);\n            return i.HasValue ? GetColumn(i.Value) : null;\n        }\n\n        #endregion\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Ex/Relational/RelationalMultiRow.cs",
    "content": "﻿namespace SaintCoinach.Ex.Relational {\n    public class RelationalMultiRow : MultiRow, IRelationalMultiRow {\n        #region Constructors\n\n        #region Constructor\n\n        public RelationalMultiRow(IMultiSheet sheet, int key) : base(sheet, key) { }\n\n        #endregion\n\n        #endregion\n\n        public new IRelationalMultiSheet Sheet { get { return (IRelationalMultiSheet)base.Sheet; } }\n\n        #region IRelationalMultiRow Members\n\n        public object this[string columnName, Language language] {\n            get { return Sheet.GetLocalisedSheet(language)[Key, columnName]; }\n        }\n\n        #endregion\n\n        public override string ToString() {\n            var defCol = Sheet.Header.DefaultColumn;\n            return defCol == null\n                       ? string.Format(\"{0}#{1}\", Sheet.Header.Name, Key)\n                       : string.Format(\"{0}\", this[defCol.Index]);\n        }\n\n        #region IRelationalRow Members\n\n        IRelationalSheet IRelationalRow.Sheet { get { return Sheet; } }\n\n        public object DefaultValue { get { return Sheet.ActiveSheet[Key].DefaultValue; } }\n\n        public object this[string columnName] { get { return Sheet.ActiveSheet[Key, columnName]; } }\n\n        object IRelationalRow.GetRaw(string columnName) {\n            return Sheet.ActiveSheet[Key].GetRaw(columnName);\n        }\n\n        object IRelationalMultiRow.GetRaw(string columnName, Language language) {\n            return Sheet.GetLocalisedSheet(language)[Key].GetRaw(columnName);\n        }\n\n        #endregion\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Ex/Relational/RelationalMultiSheet.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\n\nnamespace SaintCoinach.Ex.Relational {\n    public class RelationalMultiSheet<TMulti, TData> : MultiSheet<TMulti, TData>, IRelationalMultiSheet<TMulti, TData>\n        where TMulti : IRelationalMultiRow\n        where TData : IRelationalDataRow {\n        #region Constructors\n\n        public RelationalMultiSheet(RelationalExCollection collection, RelationalHeader header)\n            : base(collection, header) { }\n\n        #endregion\n\n        public new RelationalExCollection Collection { get { return (RelationalExCollection)base.Collection; } }\n        public new RelationalHeader Header { get { return (RelationalHeader)base.Header; } }\n        public new IRelationalSheet<TData> ActiveSheet { get { return (IRelationalSheet<TData>)base.ActiveSheet; } }\n\n        #region IRelationalMultiSheet<TMulti,TSource> Members\n\n        public new IRelationalSheet<TData> GetLocalisedSheet(Language language) {\n            return (IRelationalSheet<TData>)base.GetLocalisedSheet(language);\n        }\n\n        #endregion\n\n        #region Factory\n\n        protected override ISheet<TData> CreateLocalisedSheet(Language language) {\n            return new RelationalDataSheet<TData>(Collection, Header, language);\n        }\n\n        #endregion\n\n        #region IRelationalSheet Members\n\n        IRelationalRow IRelationalSheet.this[int row] { get { return this[row]; } }\n\n        object IRelationalSheet.this[int row, string columnName] { get { return this[row][columnName]; } }\n\n        IRelationalRow IRelationalSheet.IndexedLookup(string index, int key) { return ActiveSheet.IndexedLookup(index, key); }\n\n        #endregion\n\n        #region IRelationalMultiSheet Members\n\n        IRelationalSheet IRelationalMultiSheet.ActiveSheet { get { return ActiveSheet; } }\n\n        IRelationalSheet IRelationalMultiSheet.GetLocalisedSheet(Language language) {\n            return GetLocalisedSheet(language);\n        }\n\n        IRelationalMultiRow IRelationalMultiSheet.this[int row] { get { return this[row]; } }\n\n        #endregion\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Ex/Relational/RelationalPartialDataSheet.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\n\nusing SaintCoinach.IO;\n\nnamespace SaintCoinach.Ex.Relational {\n    public class RelationalPartialDataSheet<T> : PartialDataSheet<T>, IRelationalDataSheet<T>\n        where T : IRelationalDataRow {\n        #region Properties\n\n        public new IRelationalDataSheet<T> SourceSheet { get { return (IRelationalDataSheet<T>)base.SourceSheet; } }\n\n        #endregion\n\n        #region Constructors\n\n        public RelationalPartialDataSheet(IRelationalDataSheet<T> sourceSheet, Range range, File file)\n            : base(sourceSheet, range, file) { }\n\n        #endregion\n\n        public new RelationalHeader Header { get { return (RelationalHeader)base.Header; } }\n        public new RelationalExCollection Collection { get { return (RelationalExCollection)base.Collection; } }\n\n        #region IRelationalSheet Members\n\n        IRelationalRow IRelationalSheet.this[int row] { get { return this[row]; } }\n\n        public object this[int row, string columnName] { get { return this[row][columnName]; } }\n\n        public IRelationalRow IndexedLookup(string index, int key) {\n            throw new NotImplementedException(\"Indexes are not supported in partial sheets.\");\n        }\n\n        #endregion\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Ex/Relational/Update/ChangeType.cs",
    "content": "﻿using System;\n\nnamespace SaintCoinach.Ex.Relational.Update {\n    [Flags]\n    public enum ChangeType {\n        None = 0x00,\n        Structure = 0x01,\n        Data = 0x02,\n        Breaking = 0x04\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Ex/Relational/Update/Changes/DefinitionMoved.cs",
    "content": "﻿using System;\n\nnamespace SaintCoinach.Ex.Relational.Update.Changes {\n    [Serializable]\n    public class DefinitionMoved : IChange {\n        #region Properties\n\n        public int PreviousIndex { get; private set; }\n        public int NewIndex { get; private set; }\n        public double Confidence { get; private set; }\n\n        #endregion\n\n        #region Constructors\n\n        public DefinitionMoved(string sheetName, int prevIndex, int newIndex, double c) {\n            SheetName = sheetName;\n            PreviousIndex = prevIndex;\n            NewIndex = newIndex;\n            Confidence = c;\n        }\n\n        #endregion\n\n        public ChangeType ChangeType { get { return ChangeType.Structure; } }\n        public string SheetName { get; private set; }\n\n        public override string ToString() {\n            return string.Format(\"Definition '{0}'@{1} was moved to {2} (c={3:P}).\", SheetName, PreviousIndex, NewIndex,\n                Confidence);\n        }\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Ex/Relational/Update/Changes/DefinitionRemoved.cs",
    "content": "﻿using System;\n\nnamespace SaintCoinach.Ex.Relational.Update.Changes {\n    [Serializable]\n    public class DefinitionRemoved : IChange {\n        #region Fields\n\n        private readonly int _NewIndex = -1;\n\n        #endregion\n\n        #region Properties\n\n        public int PreviousIndex { get; private set; }\n        public int NewIndex { get { return _NewIndex; } }\n        public double Confidence { get; private set; }\n\n        #endregion\n\n        #region Constructors\n\n        public DefinitionRemoved(string sheetName, int prevIndex) {\n            Confidence = 0.0;\n            SheetName = sheetName;\n            PreviousIndex = prevIndex;\n        }\n\n        public DefinitionRemoved(string sheetName, int prevIndex, int newIndex, double c) {\n            SheetName = sheetName;\n            PreviousIndex = prevIndex;\n            _NewIndex = newIndex;\n            Confidence = c;\n        }\n\n        #endregion\n\n        public ChangeType ChangeType { get { return ChangeType.Structure | ChangeType.Breaking; } }\n        public string SheetName { get; private set; }\n\n        public override string ToString() {\n            if (NewIndex >= 0)\n                return string.Format(\"Definition '{0}'@{1} was removed (highest match at {2} with c={3:P}).\", SheetName,\n                    PreviousIndex, NewIndex, Confidence);\n            return string.Format(\"Definition '{0}'@{1} was removed (no match).\", SheetName, PreviousIndex);\n        }\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Ex/Relational/Update/Changes/FieldChanged.cs",
    "content": "﻿using System;\n\nnamespace SaintCoinach.Ex.Relational.Update.Changes {\n    [Serializable]\n    public class FieldChanged : IChange {\n        #region Properties\n\n        public string ColumnName { get; private set; }\n        public int Row { get; private set; }\n        public object PreviousValue { get; private set; }\n        public object UpdatedValue { get; private set; }\n        public Language Language { get; private set; }\n\n        #endregion\n\n        #region Constructors\n\n        public FieldChanged(string sheetName,\n                            Language language,\n                            string columnName,\n                            int row,\n                            object previousVal,\n                            object updatedValue) {\n            SheetName = sheetName;\n            ColumnName = columnName;\n            Row = row;\n            Language = language;\n\n            if (previousVal is Text.XivString)\n                PreviousValue = previousVal.ToString();\n            else\n                PreviousValue = previousVal;\n\n            if (updatedValue is Text.XivString)\n                UpdatedValue = updatedValue.ToString();\n            else\n                UpdatedValue = updatedValue;\n        }\n\n        #endregion\n\n        public ChangeType ChangeType { get { return ChangeType.Data; } }\n        public string SheetName { get; private set; }\n\n        public override string ToString() {\n            return string.Format(\"'{0}':{1}{5}[{2}]: Changed from '{3}' to '{4}'.\", SheetName, Row, ColumnName,\n                PreviousValue, UpdatedValue, Language.GetSuffix());\n        }\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Ex/Relational/Update/Changes/RowAdded.cs",
    "content": "﻿using System;\n\nnamespace SaintCoinach.Ex.Relational.Update.Changes {\n    [Serializable]\n    public class RowAdded : IChange {\n        #region Properties\n\n        public int Row { get; private set; }\n\n        #endregion\n\n        #region Constructors\n\n        public RowAdded(string sheetName, int row) {\n            SheetName = sheetName;\n            Row = row;\n        }\n\n        #endregion\n\n        public ChangeType ChangeType { get { return ChangeType.Data; } }\n        public string SheetName { get; private set; }\n\n        public override string ToString() {\n            return string.Format(\"Row {0} was added to {1}.\", Row, SheetName);\n        }\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Ex/Relational/Update/Changes/RowRemoved.cs",
    "content": "﻿using System;\n\nnamespace SaintCoinach.Ex.Relational.Update.Changes {\n    [Serializable]\n    public class RowRemoved : IChange {\n        #region Properties\n\n        public int Row { get; private set; }\n\n        #endregion\n\n        #region Constructors\n\n        public RowRemoved(string sheetName, int row) {\n            SheetName = sheetName;\n            Row = row;\n        }\n\n        #endregion\n\n        public ChangeType ChangeType { get { return ChangeType.Data; } }\n        public string SheetName { get; private set; }\n\n        public override string ToString() {\n            return string.Format(\"Row {0} was removed from {1}.\", Row, SheetName);\n        }\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Ex/Relational/Update/Changes/SheetLanguageAdded.cs",
    "content": "﻿using System;\n\nnamespace SaintCoinach.Ex.Relational.Update.Changes {\n    [Serializable]\n    public class SheetLanguageAdded : IChange {\n        #region Properties\n\n        public Language Language { get; private set; }\n\n        #endregion\n\n        #region Constructors\n\n        public SheetLanguageAdded(string name, Language language) {\n            SheetName = name;\n            Language = language;\n        }\n\n        #endregion\n\n        public ChangeType ChangeType { get { return ChangeType.Structure | ChangeType.Data; } }\n        public string SheetName { get; private set; }\n\n        public override string ToString() {\n            return string.Format(\"{0} has been added to sheet {1}.\", Language, SheetName);\n        }\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Ex/Relational/Update/Changes/SheetLanguageRemoved.cs",
    "content": "﻿using System;\n\nnamespace SaintCoinach.Ex.Relational.Update.Changes {\n    [Serializable]\n    public class SheetLanguageRemoved : IChange {\n        #region Properties\n\n        public Language Language { get; private set; }\n\n        #endregion\n\n        #region Constructors\n\n        public SheetLanguageRemoved(string name, Language language) {\n            SheetName = name;\n            Language = language;\n        }\n\n        #endregion\n\n        public ChangeType ChangeType { get { return ChangeType.Structure | ChangeType.Data | ChangeType.Breaking; } }\n        public string SheetName { get; private set; }\n\n        public override string ToString() {\n            return string.Format(\"{0} has been removed from sheet {1}.\", Language, SheetName);\n        }\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Ex/Relational/Update/Changes/SheetRemoved.cs",
    "content": "﻿using System;\n\nnamespace SaintCoinach.Ex.Relational.Update.Changes {\n    [Serializable]\n    public class SheetRemoved : IChange {\n        #region Constructors\n\n        public SheetRemoved(string name) {\n            SheetName = name;\n        }\n\n        #endregion\n\n        public ChangeType ChangeType { get { return ChangeType.Structure | ChangeType.Breaking; } }\n        public string SheetName { get; private set; }\n\n        public override string ToString() {\n            return string.Format(\"Sheet '{0}' has been removed.\", SheetName);\n        }\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Ex/Relational/Update/Changes/SheetTypeChanged.cs",
    "content": "﻿using System;\n\nnamespace SaintCoinach.Ex.Relational.Update.Changes {\n    [Serializable]\n    public class SheetTypeChanged : IChange {\n        #region Constructors\n\n        public SheetTypeChanged(string name) {\n            SheetName = name;\n        }\n\n        #endregion\n\n        public ChangeType ChangeType { get { return ChangeType.Structure | ChangeType.Data | ChangeType.Breaking; } }\n        public string SheetName { get; private set; }\n\n        public override string ToString() {\n            return string.Format(\"Type of sheet '{0}' has been changed.\", SheetName);\n        }\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Ex/Relational/Update/ColumnComparer.cs",
    "content": "﻿using SaintCoinach.Xiv;\nusing System;\nusing System.Collections;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace SaintCoinach.Ex.Relational.Update {\n    public abstract class ColumnComparer {\n        private bool[] _compatibleColumnsByIndex;\n\n        public static ColumnComparer Create(Column column, IEnumerable<Column> candidates) {\n            ColumnComparer comparer;\n            var type = column.Reader.Type;\n\n            if (Comparer.IsPrimitiveType(type) || type == typeof(Quad))\n                comparer = new PrimitiveColumnComparer();\n            else if (type == typeof(Text.XivString) || type == typeof(string))\n                comparer = new StringColumnComparer();\n            else\n                return null;\n\n            comparer._compatibleColumnsByIndex = candidates.Select(c => comparer.IsCompatibleType(type, c.Reader.Type)).ToArray();\n            return comparer;\n        }\n\n        public bool IsCompatibleIndex(int index) {\n            return _compatibleColumnsByIndex[index];\n        }\n\n        public abstract bool IsCompatibleType(Type type, Type candidateType);\n\n        public abstract bool Compare(object v1, object v2);\n    }\n\n    public class PrimitiveColumnComparer : ColumnComparer {\n        public override bool Compare(object v1, object v2) {\n            return Equals(v1, v2);\n        }\n\n        public override bool IsCompatibleType(Type type, Type candidateType) {\n            return type == candidateType;\n        }\n    }\n\n    public class StringColumnComparer : ColumnComparer {\n        public const double RelativeLevenshteinDistance = 0.05;\n\n        public override bool Compare(object v1, object v2) {\n            string s1 = null, s2 = null;\n\n            if (v1 is Text.XivString)\n                s1 = ((Text.XivString)v1).ToString();\n            else if (v1 is string)\n                s1 = (string)v1;\n\n            if (v2 is Text.XivString)\n                s2 = ((Text.XivString)v2).ToString();\n            else if (v2 is string)\n                s2 = (string)v2;\n\n            if (s1 == null || s2 == null) return false;\n\n            var maxDistance = Math.Ceiling(RelativeLevenshteinDistance * (s1.Length + s2.Length) / 2.0);\n            var d = ComputeLevenshtein(s1, s2);\n            return (d <= maxDistance);\n        }\n\n        public static int ComputeLevenshtein(string s, string t)  {\n            if (s == t) {\n                // Optimization: Quick test of identical strings. \n                return 0;\n            }\n\n            var n = s.Length;\n            var m = t.Length;\n            var d = new int[n + 1, m + 1];\n\n            // Step 1\n            if (n == 0) {\n                return m;\n            }\n\n            if (m == 0) {\n                return n;\n            }\n\n            // Step 2\n            for (var i = 0; i <= n; d[i, 0] = i++) { }\n\n            for (var j = 0; j <= m; d[0, j] = j++) { }\n\n            // Step 3\n            for (var i = 1; i <= n; i++) {\n                //Step 4\n                for (var j = 1; j <= m; j++) {\n                    // Step 5\n                    var cost = (t[j - 1] == s[i - 1]) ? 0 : 1;\n\n                    // Step 6\n                    d[i, j] = Math.Min(\n                                       Math.Min(d[i - 1, j] + 1, d[i, j - 1] + 1),\n                        d[i - 1, j - 1] + cost);\n                }\n            }\n            // Step 7\n            return d[n, m];\n        }\n\n        public override bool IsCompatibleType(Type type, Type candidateType) {\n            return candidateType == typeof(Text.XivString) || candidateType == typeof(string);\n        }\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Ex/Relational/Update/Comparer.cs",
    "content": "﻿using SaintCoinach.Ex.Relational.Definition;\nusing SaintCoinach.Xiv;\nusing System;\nusing System.Collections;\nusing System.Collections.Generic;\nusing System.Linq;\n\nnamespace SaintCoinach.Ex.Relational.Update {\n    static class Comparer {\n        public static bool IsMatch(object v1, object v2) {\n            // This function is only used by variant 2 rows with subrows.\n            // When the updater handles those this will go away and the rest\n            // of its functions merged into the ColumnComparer family.\n            if (IsPrimitive(v1) && IsPrimitive(v2)) {\n                if (v1.GetType() != v2.GetType()) return false;\n\n                var d1 = ToDecimal(v1);\n                var d2 = ToDecimal(v2);\n\n                return d1 == d2;\n            }\n\n            if (v1 is Quad && v2 is Quad)\n                return Equals(v1, v2);\n\n            string s1 = null, s2 = null;\n\n            if (v1 is Text.XivString)\n                s1 = ((Text.XivString)v1).ToString();\n            else if (v1 is string)\n                s1 = (string)v1;\n\n            if (v2 is Text.XivString)\n                s2 = ((Text.XivString)v2).ToString();\n            else if (v2 is string)\n                s2 = (string)v2;\n\n            if (s1 == null || s2 == null) return false;\n\n\n            var maxDistance = Math.Ceiling(StringColumnComparer.RelativeLevenshteinDistance * (s1.Length + s2.Length) / 2.0);\n            var d = StringColumnComparer.ComputeLevenshtein(s1, s2);\n            return (d <= maxDistance);\n        }\n\n        public static bool IsMatch(IDictionary v1, IDictionary v2) {\n            foreach (var key in v1.Keys) {\n                if (!v2.Contains(key))\n                    continue;\n\n                if (!IsMatch(v1[key], v2[key]))\n                    return false;\n            }\n\n            return true;\n        }\n\n        private static bool IsPrimitive(object o) {\n            // The ordering of these checks is optimized based on type\n            // occurrence statistics.\n            if (o is UInt32) return true;\n            if (o is byte) return true;\n            if (o is bool) return true;\n            if (o is Int32) return true;\n            if (o is UInt16) return true;\n            // XivString\n            if (o is SByte) return true;\n            if (o is Int16) return true;\n            // Quad\n            if (o is Single) return true;\n            // Int64, UInt64, double have no occurrences.\n            if (o is Int64) return true;\n            if (o is UInt64) return true;\n            if (o is double) return true;\n            return false;\n        }\n\n        public static bool IsPrimitiveType(Type t) {\n            // The ordering of these checks is optimized based on type\n            // occurrence statistics.\n            return\n                t == typeof(UInt32) ||\n                t == typeof(byte) ||\n                t == typeof(bool) ||\n                t == typeof(Int32) ||\n                t == typeof(UInt16) ||\n                t == typeof(SByte) ||\n                t == typeof(Int16) ||\n                t == typeof(Single) ||\n                t == typeof(Int64) ||\n                t == typeof(UInt64) ||\n                t == typeof(double);\n        }\n\n        private static Decimal ToDecimal(object o) {\n            return Convert.ToDecimal(o);\n        }\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Ex/Relational/Update/DefinitionUpdater.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\n\nusing SaintCoinach.Ex.Relational.Definition;\n\nnamespace SaintCoinach.Ex.Relational.Update {\n    public class DefinitionUpdater {\n        #region Fields\n\n        private readonly Dictionary<int, List<double>> _IndexMatchConfidence = new Dictionary<int, List<double>>();\n        private readonly SheetDefinition _SheetDefinition;\n        private int _RowMatchCount;\n\n        #endregion\n\n        #region Properties\n\n        public PositionedDataDefinition DataDefinition { get; private set; }\n        \n        #endregion\n\n        #region Constructors\n        \n        public DefinitionUpdater(SheetDefinition sheetDefinition, PositionedDataDefinition dataDefinition) {\n            _SheetDefinition = sheetDefinition;\n            DataDefinition = dataDefinition;\n        }\n\n        #endregion\n\n        #region Match\n\n        public IDictionary<int, double>  GetMatches() {\n            return _IndexMatchConfidence.ToDictionary(_ => _.Key, _ => _.Value.Sum() / _RowMatchCount);\n        }\n\n        public void MatchRow(object[] previousRowData, object[] updatedRowData, ColumnComparer[] comparers) {\n            var defLength = DataDefinition.Length;\n            for (var updatedI = 0; updatedI <= updatedRowData.Length - defLength; ++updatedI) {\n                var matches = 0;\n\n                for (var i = 0; i < defLength; ++i) {\n                    var prevColumn = i + DataDefinition.Index;\n                    var upColumn = updatedI + i;\n\n                    if (prevColumn >= comparers.Length)\n                        continue; // Not compatible, index out of bounds.\n\n                    var comparer = comparers[prevColumn];\n                    if (comparer == null || !comparer.IsCompatibleIndex(upColumn))\n                        continue; // Not compatible.\n\n                    var prevVal = previousRowData[prevColumn];\n                    var upVal = updatedRowData[upColumn];\n\n                    if (comparer.Compare(prevVal, upVal))\n                        ++matches;\n                }\n\n                if (matches <= 0) continue;\n\n                var c = matches / (double)defLength;\n                if (!_IndexMatchConfidence.ContainsKey(updatedI))\n                    _IndexMatchConfidence.Add(updatedI, new List<double>());\n                _IndexMatchConfidence[updatedI].Add(c);\n            }\n\n            ++_RowMatchCount;\n        }\n\n        //public void MatchRow(object[] previousRowData, object[] updatedRowData) {\n        //    var defLength = DataDefinition.Length;\n        //    for (var updatedI = 0; updatedI <= updatedRowData.Length - defLength; ++updatedI) {\n        //        var matches = 0;\n\n        //        for (var i = 0; i < defLength; ++i) {\n        //            var prevColumn = i + DataDefinition.Index;\n        //            var upColumn = updatedI + i;\n\n        //            var prevVal = previousRowData[prevColumn];\n        //            var upVal = updatedRowData[upColumn];\n\n        //            if (Comparer.IsMatch(prevVal, upVal))\n        //                ++matches;\n        //        }\n\n        //        if (matches <= 0) continue;\n\n        //        var c = matches / (double)defLength;\n        //        if (!_IndexMatchConfidence.ContainsKey(updatedI))\n        //            _IndexMatchConfidence.Add(updatedI, new List<double>());\n        //        _IndexMatchConfidence[updatedI].Add(c);\n        //    }\n\n        //    ++_RowMatchCount;\n        //}\n\n        public PositionedDataDefinition GetDefinition(int index) {\n            var newDef = DataDefinition.Clone();\n            newDef.Index = index;\n            return newDef;\n        }\n        /*\n        public PositionedDataDefintion GetBestMatch(out double confidence) {\n            var index = GetBestMatchIndex(out confidence);\n            if (index < 0)\n                return null;\n\n            var newDef = DataDefinition.Clone();\n            newDef.Index = index;\n            return newDef;\n        }\n\n        private int GetBestMatchIndex(out double confidence) {\n            if (_IndexMatchConfidence.Count == 0) {\n                confidence = 1.0;\n                return -1;\n            }\n\n            var matches = _IndexMatchConfidence.Select(_ => new {\n                Index = _.Key,\n                _.Value.Count,\n                Sum = _.Value.Sum()\n            }).GroupBy(_ => _.Sum).OrderByDescending(_ => _.Key);\n\n            var bestMatch = matches.First();\n\n            confidence = bestMatch.Key / _RowMatchCount;\n\n            if (bestMatch.Count() > 1)\n                Console.WriteLine(\"Multiple possible matches on '{2}' @ '{3}' for {4} ({0} with c={1:P})\",\n                    bestMatch.Count(), confidence, _SheetDefinition.Name, DataDefinition.GetName(DataDefinition.Index),\n                    DataDefinition.Length);\n\n            return bestMatch.Any(_ => _.Index == DataDefinition.Index)\n                       ? DataDefinition.Index\n                       : bestMatch.OrderBy(_ => Math.Abs(DataDefinition.Index - _.Index)).Select(_ => _.Index).First();\n        }\n        */\n        #endregion\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Ex/Relational/Update/IChange.cs",
    "content": "﻿namespace SaintCoinach.Ex.Relational.Update {\n    public interface IChange {\n        #region Properties\n\n        ChangeType ChangeType { get; }\n        string SheetName { get; }\n\n        #endregion\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Ex/Relational/Update/RelationUpdater.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Threading.Tasks;\nusing System.Collections.Concurrent;\n\nusing SaintCoinach.Ex.Relational.Definition;\nusing SaintCoinach.Ex.Relational.Update.Changes;\nusing SaintCoinach.IO;\n\n\nnamespace SaintCoinach.Ex.Relational.Update {\n    public class RelationUpdater {\n        #region Static\n\n        private const Language UsedLanguage = Language.Japanese;\n\n        #endregion\n\n        #region Fields\n\n        private readonly RelationalExCollection _Previous;\n        private readonly IProgress<UpdateProgress> _Progress;\n        private readonly RelationalExCollection _Updated;\n\n        #endregion\n\n        #region Properties\n\n        public RelationDefinition Previous { get; private set; }\n        public RelationDefinition Updated { get; private set; }\n\n        #endregion\n\n        #region Constructors\n\n        public RelationUpdater(PackCollection previousPacks,\n                               RelationDefinition previousDefinition,\n                               PackCollection updatedPacks,\n                               string updatedVersion,\n                               IProgress<UpdateProgress> progress) {\n            _Progress = progress ?? new NullProgress();\n\n            _Previous = new RelationalExCollection(previousPacks);\n            Previous = previousDefinition;\n\n            _Updated = new RelationalExCollection(updatedPacks);\n            Updated = new RelationDefinition {\n                Version = updatedVersion\n            };\n\n            _Previous.ActiveLanguage = UsedLanguage;\n            _Updated.ActiveLanguage = UsedLanguage;\n        }\n\n        #endregion\n\n        #region Update\n\n        public IEnumerable<IChange> Update(bool detectDataChanges) {\n            var changes = new ConcurrentBag<IChange>();\n\n            var progress = new UpdateProgress {\n                CurrentOperation = \"Structure\",\n                TotalSteps = (detectDataChanges ? 2 : 1) * Previous.SheetDefinitions.Count\n            };\n\n            var sheetLock = new object();\n\n            Parallel.ForEach(Previous.SheetDefinitions, prevSheetDef =>\n            {\n                progress.CurrentFile = prevSheetDef.Name;\n                _Progress.Report(progress);\n\n                if (!_Updated.SheetExists(prevSheetDef.Name))\n                {\n                    changes.Add(new SheetRemoved(prevSheetDef.Name));\n                    return;\n                }\n\n                IRelationalSheet prevSheet, updatedSheet;\n                SheetDefinition updatedSheetDef;\n                lock (sheetLock)\n                {\n                    prevSheet = _Previous.GetSheet(prevSheetDef.Name);\n                    updatedSheet = _Updated.GetSheet(prevSheetDef.Name);\n                    updatedSheetDef = Updated.GetOrCreateSheet(prevSheetDef.Name);\n                }\n\n                var sheetUpdater = new SheetUpdater(prevSheet, prevSheetDef, updatedSheet, updatedSheetDef);\n                var sheetUpdates = MemoryGuard(() => sheetUpdater.Update().ToArray());\n                foreach (var change in sheetUpdates)\n                    changes.Add(change);\n\n                progress.IncrementStep();\n            });\n\n            Updated.Compile();\n\n            if (detectDataChanges) {\n                progress.CurrentOperation = \"Data\";\n\n                Parallel.ForEach(Previous.SheetDefinitions, prevSheetDef =>\n                {\n                    progress.CurrentFile = prevSheetDef.Name;\n                    _Progress.Report(progress);\n\n                    if (!_Updated.SheetExists(prevSheetDef.Name))\n                        return;\n\n                    IRelationalSheet prevSheet, updatedSheet;\n                    SheetDefinition updatedSheetDef;\n                    lock (sheetLock)\n                    {\n                        prevSheet = _Previous.GetSheet(prevSheetDef.Name);\n                        updatedSheet = _Updated.GetSheet(prevSheetDef.Name);\n                        updatedSheetDef = Updated.GetOrCreateSheet(prevSheetDef.Name);\n                    }\n\n                    var sheetComparer = new SheetComparer(prevSheet, prevSheetDef, updatedSheet, updatedSheetDef);\n                    var sheetChanges = MemoryGuard(() => sheetComparer.Compare().ToArray());\n                    foreach (var change in sheetChanges)\n                        changes.Add(change);\n\n                    progress.IncrementStep();\n                });\n            }\n\n            progress.CurrentOperation = \"Finished\";\n            progress.CurrentFile = null;\n            _Progress.Report(progress);\n\n            return changes;\n        }\n\n        private static T MemoryGuard<T>(Func<T> func) {\n            for (var i = 0; i < 5; i++) {\n                try {\n                    return func();\n                }\n                catch (OutOfMemoryException) {\n                    // Give another task time to progress and free memory.\n                    System.Threading.Thread.Sleep(300);\n                    GC.Collect();\n                }\n            }\n\n            throw new OutOfMemoryException(\"Could not execute function after 5 attempts.\");\n        }\n\n        #endregion\n\n        private class NullProgress : IProgress<UpdateProgress> {\n            #region IProgress<UpdateProgress> Members\n\n            public void Report(UpdateProgress value) { }\n\n            #endregion\n        }\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Ex/Relational/Update/SheetComparer.cs",
    "content": "﻿using System.Collections.Concurrent;\nusing System.Collections.Generic;\nusing System.IO;\nusing System.Linq;\n\nusing SaintCoinach.Ex.Relational.Definition;\nusing SaintCoinach.Ex.Relational.Update.Changes;\n\n// ReSharper disable PossibleMultipleEnumeration\n\nnamespace SaintCoinach.Ex.Relational.Update {\n    public class SheetComparer {\n        private static ConcurrentDictionary<Language, bool> _unavailableLanguages = new ConcurrentDictionary<Language, bool>();\n\n        #region Fields\n\n        private readonly SheetDefinition _PreviousDefinition;\n        private readonly IRelationalSheet _PreviousSheet;\n        private readonly SheetDefinition _UpdatedDefinition;\n        private readonly IRelationalSheet _UpdatedSheet;\n\n        #endregion\n\n        #region Constructors\n\n        #region Constructor\n\n        public SheetComparer(IRelationalSheet prevSheet,\n                             SheetDefinition prevDefinition,\n                             IRelationalSheet upSheet,\n                             SheetDefinition upDefinition) {\n            _PreviousSheet = prevSheet;\n            _PreviousDefinition = prevDefinition;\n\n            _UpdatedSheet = upSheet;\n            _UpdatedDefinition = upDefinition;\n        }\n\n        #endregion\n\n        #endregion\n\n        #region Detect\n\n        private class ColumnMap {\n            #region Properties\n\n            public string Name { get; set; }\n            public int NewIndex { get; set; }\n            public int PreviousIndex { get; set; }\n\n            #endregion\n        }\n\n        public IEnumerable<IChange> Compare() {\n            var changes = new List<IChange>();\n\n\n            var prevKeys = _PreviousSheet.Cast<IRow>().Select(_ => _.Key).ToArray();\n            var updatedKeys = _UpdatedSheet.Cast<IRow>().Select(_ => _.Key).ToArray();\n\n            changes.AddRange(updatedKeys.Except(prevKeys).Select(_ => new RowAdded(_UpdatedDefinition.Name, _)));\n            changes.AddRange(prevKeys.Except(updatedKeys).Select(_ => new RowRemoved(_PreviousDefinition.Name, _)));\n\n            var columns = _UpdatedDefinition.GetAllColumnNames().Select(_ => {\n                var previousColumn = _PreviousDefinition.FindColumn(_);\n                var newColumn = _UpdatedDefinition.FindColumn(_);\n\n                if (!previousColumn.HasValue || !newColumn.HasValue)\n                    throw new InvalidDataException();\n\n                return new ColumnMap {\n                    Name = _,\n                    PreviousIndex = previousColumn.Value,\n                    NewIndex = newColumn.Value\n                };\n            }).ToArray();\n\n            var prevIsMulti = _PreviousSheet is IMultiSheet;\n            var upIsMulti = _UpdatedSheet is IMultiSheet;\n            if (prevIsMulti == upIsMulti) {\n                if (prevIsMulti) {\n                    var prevMulti = (IMultiSheet)_PreviousSheet;\n                    var prevLang = _PreviousSheet.Header.AvailableLanguages;\n\n                    var upMulti = (IMultiSheet)_UpdatedSheet;\n                    var upLang = _UpdatedSheet.Header.AvailableLanguages;\n\n                    changes.AddRange(upLang.Except(prevLang).Select(_ => new SheetLanguageAdded(_UpdatedSheet.Name, _)));\n                    changes.AddRange(\n                                     prevLang.Except(upLang)\n                                             .Select(_ => new SheetLanguageRemoved(_PreviousDefinition.Name, _)));\n\n                    foreach (var lang in prevLang.Intersect(upLang)) {\n                        // Do not compare languages marked unavailable elsewhere.\n                        if (_unavailableLanguages.ContainsKey(lang))\n                            continue;\n\n                        var prevSheet = prevMulti.GetLocalisedSheet(lang);\n                        var upSheet = upMulti.GetLocalisedSheet(lang);\n\n                        try {\n                            changes.AddRange(Compare(prevSheet, upSheet, lang, columns));\n                        } catch (System.IO.FileNotFoundException) {\n                            // Usually caused by one language ahead of another\n                            // in patches, or that language data is not found.\n                            // Skip it and mark unavailable for other comparisons.\n                            _unavailableLanguages.TryAdd(lang, true);\n                            continue;\n                        }\n                    }\n                } else\n                    changes.AddRange(Compare(_PreviousSheet, _UpdatedSheet, Language.None, columns));\n            } else {\n                changes.Add(new SheetTypeChanged(_UpdatedDefinition.Name));\n                System.Console.Error.WriteLine(\"Type of sheet {0} has changed, unable to detect changes.\",\n                    _UpdatedDefinition.Name);\n            }\n\n            return changes;\n        }\n\n        private static IEnumerable<IChange> Compare(ISheet previousSheet,\n                                                    ISheet updatedSheet,\n                                                    Language language,\n                                                    ColumnMap[] columns) {\n            if (previousSheet.Header.Variant == 2) {\n                foreach (var result in CompareVariant2(previousSheet, updatedSheet, language, columns))\n                    yield return result;\n            }\n            else {\n                var prevRows = previousSheet.Cast<IRow>().ToArray();\n                var updatedRows = updatedSheet.Cast<IRow>().ToDictionary(_ => _.Key, _ => _);\n\n                foreach (var prevRow in prevRows) {\n                    if (!updatedRows.ContainsKey(prevRow.Key)) continue;\n\n                    var updatedRow = updatedRows[prevRow.Key];\n\n                    foreach (var col in columns) {\n                        var prevVal = prevRow[col.PreviousIndex];\n                        var upVal = updatedRow[col.NewIndex];\n\n                        if (!Comparer.IsMatch(prevVal, upVal))\n                            yield return\n                                new FieldChanged(updatedSheet.Header.Name, language, col.Name, updatedRow.Key, prevVal,\n                                    upVal);\n                    }\n                }\n            }\n        }\n\n        private static IEnumerable<IChange> CompareVariant2(ISheet previousSheet,\n                                            ISheet updatedSheet,\n                                            Language language,\n                                            ColumnMap[] columns) {\n            var prevRows = previousSheet.Cast<Variant2.RelationalDataRow>().SelectMany(r => r.SubRows).ToArray();\n            var updatedRows = updatedSheet.Cast<Variant2.RelationalDataRow>().SelectMany(r => r.SubRows).ToArray();\n            var updatedRowIndex = updatedRows.ToDictionary(r => r.FullKey);\n\n            foreach (var prevRow in prevRows) {\n                if (!updatedRowIndex.TryGetValue(prevRow.FullKey, out var updatedRow))\n                    continue;\n\n                foreach (var col in columns) {\n                    var prevVal = prevRow[col.PreviousIndex];\n                    var upVal = updatedRow[col.NewIndex];\n\n                    if (!Comparer.IsMatch(prevVal, upVal))\n                        yield return\n                            new FieldChanged(updatedSheet.Header.Name, language, col.Name, updatedRow.Key, prevVal,\n                                upVal);\n                }\n            }\n        }\n\n        #endregion\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Ex/Relational/Update/SheetUpdater.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\n\nusing SaintCoinach.Ex.Relational.Definition;\nusing SaintCoinach.Ex.Relational.Update.Changes;\nusing System.Collections;\n\nnamespace SaintCoinach.Ex.Relational.Update {\n    public class SheetUpdater {\n        #region Static\n\n        private const double RequiredMatchConfidence = 0.05; // One in twenty has to be an exact match\n        private const double ExactMatchConfidence = 0.95;\n\n        #endregion\n\n        #region Fields\n\n        private readonly SheetDefinition _PreviousDefinition;\n        private readonly IRelationalSheet _PreviousSheet;\n        private readonly SheetDefinition _UpdatedDefinition;\n        private readonly IRelationalSheet _UpdatedSheet;\n\n        #endregion\n\n        #region Constructors\n\n        #region Constructor\n\n        public SheetUpdater(IRelationalSheet prevSheet,\n                            SheetDefinition prevDefinition,\n                            IRelationalSheet upSheet,\n                            SheetDefinition upDefinition) {\n            _PreviousSheet = prevSheet;\n            _PreviousDefinition = prevDefinition;\n\n            _UpdatedSheet = upSheet;\n            _UpdatedDefinition = upDefinition;\n        }\n\n        #endregion\n\n        #endregion\n\n        #region Update\n\n        private Dictionary<DefinitionUpdater, IDictionary<int, double>> _Updaters;\n        private List<IChange> _ChangeLog;\n        private List<int> _UsedColumns;\n        private Dictionary<int, IDictionary<DefinitionUpdater, double>> _ColumnMatches;\n\n        void AddMoveChange(DefinitionUpdater updater, int newIndex, double confidence) {\n            if (newIndex != updater.DataDefinition.Index)\n                _ChangeLog.Add(new DefinitionMoved(_PreviousDefinition.Name, updater.DataDefinition.Index, newIndex, confidence));\n\n            _UpdatedDefinition.DataDefinitions.Add(updater.GetDefinition(newIndex));\n\n            _Updaters.Remove(updater);\n            foreach (var u in _Updaters)\n                u.Value.Remove(newIndex);\n\n            for (var i = 0; i < updater.DataDefinition.Length; ++i) {\n                _UsedColumns.Add(newIndex + i);\n                _ColumnMatches.Remove(newIndex + 1);\n            }\n        }\n        public IEnumerable<IChange> Update() {\n            _ChangeLog = new List<IChange>();\n\n            _UpdatedDefinition.DefaultColumn = _PreviousDefinition.DefaultColumn;\n            _UpdatedDefinition.IsGenericReferenceTarget = _PreviousDefinition.IsGenericReferenceTarget;\n\n            _Updaters = MatchRows().ToDictionary(_ => _, _ => _.GetMatches());\n            _UsedColumns = new List<int>();\n\n            ProcessDefinitionMatches();\n            ProcessColumnMatches();\n            ProcessLeftovers();\n            \n            /*\n            foreach (var def in defs) {\n                double c;\n                var updatedDef = def.GetBestMatch(out c);\n\n                if (updatedDef == null || double.IsNaN(c) || c < RequiredMatchConfidence) {\n                    if (updatedDef != null)\n                        changes.Add(new DefinitionRemoved(_PreviousDefinition.Name, def.DataDefinition.Index,\n                            updatedDef.Index, c));\n                    else\n                        changes.Add(new DefinitionRemoved(_PreviousDefinition.Name, def.DataDefinition.Index));\n                } else {\n                    if (updatedDef.Index != def.DataDefinition.Index)\n                        changes.Add(new DefinitionMoved(_PreviousDefinition.Name, def.DataDefinition.Index,\n                            updatedDef.Index, c));\n\n                    _UpdatedDefinition.DataDefinitions.Add(updatedDef);\n                }\n            }*/\n\n            return _ChangeLog;\n        }\n        bool IsUnusedColumnRange(int index, int length) {\n            return !_UsedColumns.Any(c => c >= index && c < (index + length));\n        }\n        void ProcessDefinitionMatches() {\n            _ColumnMatches = new Dictionary<int, IDictionary<DefinitionUpdater, double>>();\n\n            foreach (var updater in _Updaters.ToArray()) {\n                int bestMatchIndex = -1;\n                double bestMatchConfidence = double.NegativeInfinity;\n\n                // Purge unsatisfactory matches\n                foreach(var match in updater.Value.ToArray()) {\n                    if(match.Value > bestMatchConfidence || (match.Value == bestMatchConfidence && match.Key == updater.Key.DataDefinition.Index)) {\n                        bestMatchIndex = match.Key;\n                        bestMatchConfidence = match.Value;\n                    }\n\n                    if (match.Value < RequiredMatchConfidence)\n                        updater.Value.Remove(match.Key);\n                }\n\n                // No satisfactory match\n                if (updater.Value.Count == 0) {\n                    if(bestMatchIndex >= 0)\n                        _ChangeLog.Add(new DefinitionRemoved(_PreviousDefinition.Name, updater.Key.DataDefinition.Index, bestMatchIndex, bestMatchConfidence));\n                    else\n                        _ChangeLog.Add(new DefinitionRemoved(_PreviousDefinition.Name, updater.Key.DataDefinition.Index));\n\n                    continue;\n                }\n\n                // At least one match is considered to be exact\n                if(bestMatchConfidence >= ExactMatchConfidence) {\n                    AddMoveChange(updater.Key, bestMatchIndex, bestMatchConfidence);\n                    continue;\n                }\n\n                // Populate column matches\n                foreach(var match in updater.Value) {\n                    if (!_ColumnMatches.TryGetValue(match.Key, out var d))\n                        _ColumnMatches.Add(match.Key, d = new Dictionary<DefinitionUpdater, double>());\n                    d.Add(updater.Key, match.Value);\n                }\n            }\n        }\n        void ProcessColumnMatches() {\n            foreach(var col in _ColumnMatches.ToArray()) {\n                if (_UsedColumns.Contains(col.Key))\n                    continue;\n                var groups = col.Value\n                    .Where(_ => \n                        IsUnusedColumnRange(col.Key, _.Key.DataDefinition.Length)\n                        && _Updaters.ContainsKey(_.Key)\n                        && !_Updaters[_.Key].Values.Any(mc => mc > _.Value))\n                    .GroupBy(_ => _.Value)\n                    .OrderByDescending(_ => _.Key)\n                    .Select(_ => new {\n                        Confidence = _.Key,\n                        Matches = _.ToArray()\n                    }).ToArray();\n\n                if (groups.Length == 0)\n                    continue;\n\n                var bestGroup = groups[0];\n                KeyValuePair<DefinitionUpdater, double> bestMatch;\n                if(bestGroup.Matches.Length == 1) {\n                    bestMatch = bestGroup.Matches[0];\n                } else {\n                    Console.WriteLine(\"Multiple possible matches on '{0}' @ '{1}' with c={2:P} from {3}\", \n                        _PreviousDefinition.Name, col.Key, bestGroup.Confidence,\n                        string.Join(\", \", bestGroup.Matches.Select(m => string.Format(\"{0} @ {1}\", m.Key.DataDefinition.GetName(m.Key.DataDefinition.Index), m.Key.DataDefinition.Index))));\n\n                    bestMatch = bestGroup.Matches.OrderBy(_ => Math.Abs(_.Key.DataDefinition.Index - col.Key)).First();\n                }\n\n                AddMoveChange(bestMatch.Key, col.Key, bestMatch.Value);\n            }\n        }\n        void ProcessLeftovers() {\n            foreach(var updater in _Updaters.ToArray()) {\n                var remainingMatches = updater.Value.Where(r => IsUnusedColumnRange(r.Key, updater.Key.DataDefinition.Length)).OrderByDescending(m => m.Value).ToArray();\n                if (remainingMatches.Length == 0)\n                    _ChangeLog.Add(new DefinitionRemoved(_PreviousDefinition.Name, updater.Key.DataDefinition.Index));\n                else\n                    AddMoveChange(updater.Key, remainingMatches[0].Key, remainingMatches[0].Value);\n            }\n        }\n\n\n        private IEnumerable<DefinitionUpdater> MatchRows() {\n            var defUpdaters =\n                _PreviousDefinition.DataDefinitions.Select(_ => new DefinitionUpdater(_PreviousDefinition, _)).ToArray();\n\n            // Record a list of compatible indexes by previous sheet column.\n            // These are the only columns to be tested.\n            var comparers = _PreviousSheet.Header.Columns\n                .Select(c => ColumnComparer.Create(c, _UpdatedSheet.Header.Columns))\n                .ToArray();\n\n            if (_PreviousSheet.Header.Variant == 2)\n                return MatchVariant2Rows(defUpdaters, comparers);\n\n            foreach (IRow prevRow in _PreviousSheet) {\n                if (!_UpdatedSheet.ContainsRow(prevRow.Key)) continue;\n\n                var prevRowFields =\n                    _PreviousSheet.Header.Columns.OrderBy(_ => _.Index).Select(_ => prevRow[_.Index]).ToArray();\n                var updatedRow = _UpdatedSheet[prevRow.Key];\n                var updatedRowFields =\n                    _UpdatedSheet.Header.Columns.OrderBy(_ => _.Index).Select(_ => updatedRow[_.Index]).ToArray();\n\n                foreach (var def in defUpdaters)\n                    def.MatchRow(prevRowFields, updatedRowFields, comparers);\n            }\n\n            return defUpdaters;\n        }\n\n        private IEnumerable<DefinitionUpdater> MatchVariant2Rows(DefinitionUpdater[] defUpdaters, ColumnComparer[] comparers) {\n            var prevRows = _PreviousSheet.Cast<Variant2.RelationalDataRow>().SelectMany(r => r.SubRows).ToArray();\n            var updatedRows = _UpdatedSheet.Cast<Variant2.RelationalDataRow>().SelectMany(r => r.SubRows).ToArray();\n            var updatedRowIndex = updatedRows.ToDictionary(r => r.FullKey);\n\n            foreach (var prevRow in prevRows) {\n                if (!updatedRowIndex.TryGetValue(prevRow.FullKey, out var updatedRow))\n                    continue;\n\n                var prevRowFields =\n                    _PreviousSheet.Header.Columns.OrderBy(_ => _.Index).Select(_ => prevRow[_.Index]).ToArray();\n                var updatedRowFields =\n                    _UpdatedSheet.Header.Columns.OrderBy(_ => _.Index).Select(_ => updatedRow[_.Index]).ToArray();\n\n                foreach (var def in defUpdaters)\n                    def.MatchRow(prevRowFields, updatedRowFields, comparers);\n            }\n\n            return defUpdaters;\n        }\n\n        #endregion\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Ex/Relational/Update/UpdateProgress.cs",
    "content": "﻿using System.Text;\nusing System.Threading;\n\nnamespace SaintCoinach.Ex.Relational.Update {\n    public struct UpdateProgress {\n        private int _CurrentStep;\n\n        #region Properties\n\n        public int CurrentStep { get { return _CurrentStep; } }\n        public int TotalSteps { get; set; }\n        public double Percentage { get { return _CurrentStep / (double)TotalSteps; } }\n        public string CurrentFile { get; set; }\n        public string CurrentOperation { get; set; }\n\n        #endregion\n\n        public void IncrementStep()\n        {\n            Interlocked.Increment(ref _CurrentStep);\n        }\n\n        public override string ToString() {\n            var sb = new StringBuilder();\n\n            sb.AppendFormat(\"{0,4:P0} ({1} / {2}): {3}\", Percentage, CurrentStep, TotalSteps, CurrentOperation);\n            if (!string.IsNullOrWhiteSpace(CurrentFile))\n                sb.AppendFormat(\" > {0}\", CurrentFile);\n            return sb.ToString();\n        }\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Ex/Relational/ValueConverters/ColorConverter.cs",
    "content": "﻿using Newtonsoft.Json.Linq;\nusing SaintCoinach.Ex.Relational.Definition;\nusing System;\nusing System.Drawing;\n\nnamespace SaintCoinach.Ex.Relational.ValueConverters {\n    public class ColorConverter : IValueConverter {\n        #region Properties\n\n        public bool IncludesAlpha { get; set; }\n\n        #endregion\n\n        #region Constructors\n\n        public ColorConverter() {\n            IncludesAlpha = false;\n        }\n\n        #endregion\n\n        #region IValueConverter Members\n\n        public string TargetTypeName { get { return \"Color\"; } }\n        public Type TargetType { get { return typeof(Color); } }\n\n        public object Convert(IDataRow row, object rawValue) {\n            var argb = System.Convert.ToInt32(rawValue);\n\n            if (!IncludesAlpha)\n                argb = (int)(((uint)argb) | 0xFF000000);\n\n            return Color.FromArgb(argb);\n        }\n\n        #endregion\n\n        #region Serialization\n\n        public JObject ToJson() {\n            return new JObject() {\n                [\"type\"] = \"color\"\n            };\n        }\n\n        public static ColorConverter FromJson(JToken obj) {\n            return new ColorConverter();\n        }\n\n        public void ResolveReferences(SheetDefinition sheetDef) { }\n\n        #endregion\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Ex/Relational/ValueConverters/ComplexLinkConverter.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\nusing Newtonsoft.Json.Linq;\nusing SaintCoinach.Ex.Relational.Definition;\nusing SaintCoinach.Xiv;\n\nnamespace SaintCoinach.Ex.Relational.ValueConverters {\n    public class ComplexLinkConverter : IValueConverter {\n        SheetLinkData[] _Links;\n\n        #region IValueConverter Members\n\n        public string TargetTypeName => \"Row\";\n\n        public Type TargetType => typeof(IRelationalRow);\n\n        public object Convert(IDataRow row, object rawValue) {\n            var key = System.Convert.ToInt32(rawValue);\n            if (key == 0)\n                return null;\n\n            var coll = row.Sheet.Collection;\n\n            foreach (var link in _Links) {\n                if (link.When != null && !link.When.Match(row))\n                    continue;\n\n                var result = link.GetRow(key, coll);\n                if (result == null)\n                    continue;\n\n                return link.Projection.Project(result);\n            }\n\n            return null;\n        }\n\n\n        #endregion\n\n        #region Serialization\n\n        public JObject ToJson() {\n            return new JObject() {\n                [\"type\"] = \"complexlink\",\n                [\"links\"] = new JArray(_Links.Select(l => l.ToJson()))\n            };\n        }\n\n        public static ComplexLinkConverter FromJson(JToken obj) {\n            return new ComplexLinkConverter() {\n                _Links = obj[\"links\"].Select(o => SheetLinkData.FromJson((JObject)o)).ToArray()\n            };\n        }\n\n        #endregion\n\n        #region SheetLinkData\n\n        interface IRowProducer {\n            IRow GetRow(IRelationalSheet sheet, int key);\n        }\n\n        class PrimaryKeyRowProducer : IRowProducer {\n            public IRow GetRow(IRelationalSheet sheet, int key) {\n                return !sheet.ContainsRow(key) ? null : sheet[key];\n            }\n        }\n\n        class IndexedRowProducer : IRowProducer {\n            public string KeyColumnName;\n\n            public IRow GetRow(IRelationalSheet sheet, int key) {\n                return sheet.IndexedLookup(KeyColumnName, key);\n            }\n        }\n\n        interface IProjectable {\n            object Project(IRow row);\n        }\n\n        class IdentityProjection : IProjectable {\n            public object Project(IRow row) {\n                return row;\n            }\n        }\n\n        class ColumnProjection : IProjectable {\n            public string ProjectedColumnName;\n\n            public object Project(IRow row) {\n                var relationalRow = (IRelationalRow)row;\n                return relationalRow[ProjectedColumnName];\n            }\n        }\n\n        class LinkCondition {\n            public string KeyColumnName;\n            public int KeyColumnIndex;\n            public object Value;\n            bool _ValueTypeChanged;\n\n            public bool Match(IDataRow row) {\n                var rowValue = row[KeyColumnIndex];\n                if (!_ValueTypeChanged && rowValue != null) {\n                    Value = System.Convert.ChangeType(Value, rowValue.GetType());\n                    _ValueTypeChanged = true;\n                }\n                return Equals(rowValue, Value);\n            }\n        }\n\n        abstract class SheetLinkData {\n            public string ProjectedColumnName;\n            public string KeyColumnName;\n\n            public IRowProducer RowProducer;\n            public IProjectable Projection;\n\n            public LinkCondition When;\n\n            public abstract IRow GetRow(int key, ExCollection collection);\n\n            public virtual JObject ToJson() {\n                var obj = new JObject();\n                if (ProjectedColumnName != null)\n                    obj[\"project\"] = ProjectedColumnName;\n                if (KeyColumnName != null)\n                    obj[\"key\"] = KeyColumnName;\n                if (When != null) {\n                    obj[\"when\"] = new JObject() {\n                        [\"key\"] = When.KeyColumnName,\n                        [\"value\"] = new JValue(When.Value)\n                    };\n                }\n\n                return obj;\n            }\n\n            public static SheetLinkData FromJson(JObject obj) {\n                SheetLinkData data;\n                if (obj[\"sheet\"] != null) {\n                    data = new SingleSheetLinkData() {\n                        SheetName = (string)obj[\"sheet\"]\n                    };\n                } else if (obj[\"sheets\"] != null) {\n                    data = new MultiSheetLinkData() {\n                        SheetNames = ((JArray)obj[\"sheets\"]).Select(t => (string)t).ToArray()\n                    };\n                } else\n                    throw new InvalidOperationException(\"complexlink link must contain either 'sheet' or 'sheets'.\");\n\n                if (obj[\"project\"] == null)\n                    data.Projection = new IdentityProjection();\n                else {\n                    data.ProjectedColumnName = (string)obj[\"project\"];\n                    data.Projection = new ColumnProjection() { ProjectedColumnName = data.ProjectedColumnName };\n                }\n\n                if (obj[\"key\"] == null)\n                    data.RowProducer = new PrimaryKeyRowProducer();\n                else {\n                    data.KeyColumnName = (string)obj[\"key\"];\n                    data.RowProducer = new IndexedRowProducer() { KeyColumnName = data.KeyColumnName };\n                }\n\n                var when = obj[\"when\"];\n                if (when != null) {\n                    var condition = new LinkCondition();\n                    condition.KeyColumnName = (string)when[\"key\"];\n                    condition.Value = when[\"value\"].ToObject<object>();\n                    data.When = condition;\n                }\n\n                return data;\n            }\n        }\n\n        class SingleSheetLinkData : SheetLinkData {\n            public string SheetName;\n\n            public override JObject ToJson() {\n                var obj = base.ToJson();\n                obj[\"sheet\"] = SheetName;\n                return obj;\n            }\n\n            public override IRow GetRow(int key, ExCollection collection) {\n                var sheet = (IRelationalSheet)collection.GetSheet(SheetName);\n                return RowProducer.GetRow(sheet, key);\n            }\n        }\n\n        class MultiSheetLinkData : SheetLinkData {\n            public string[] SheetNames;\n\n            public override JObject ToJson() {\n                var obj = base.ToJson();\n                obj[\"sheets\"] = new JArray(SheetNames);\n                return obj;\n            }\n\n            public override IRow GetRow(int key, ExCollection collection) {\n                foreach (var sheetName in SheetNames) {\n                    var sheet = (IRelationalSheet)collection.GetSheet(sheetName);\n                    if (!sheet.Header.DataFileRanges.Any(r => r.Contains(key)))\n                        continue;\n\n                    var row = RowProducer.GetRow(sheet, key);\n                    if (row != null)\n                        return row;\n                }\n                return null;\n            }\n        }\n\n        public void ResolveReferences(SheetDefinition sheetDef) {\n            foreach (var link in _Links) {\n                if (link.When != null) {\n                    var keyDefinition = sheetDef.DataDefinitions\n                        .FirstOrDefault(d => d.InnerDefinition.GetName(0) == link.When.KeyColumnName);\n                    if (keyDefinition == null)\n                        throw new InvalidOperationException($\"Can't find conditional key column '{link.When.KeyColumnName}' in sheet '{sheetDef.Name}'\");\n\n                    link.When.KeyColumnIndex = keyDefinition.Index;\n                }\n            }\n        }\n\n        #endregion\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Ex/Relational/ValueConverters/GenericReferenceConverter.cs",
    "content": "﻿using Newtonsoft.Json.Linq;\nusing SaintCoinach.Ex.Relational.Definition;\nusing System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace SaintCoinach.Ex.Relational.ValueConverters {\n    public class GenericReferenceConverter : IValueConverter {\n\n        #region IValueConverter Members\n\n        public string TargetTypeName { get { return \"Row\"; } }\n        public Type TargetType { get { return typeof(IRelationalRow); } }\n\n        public object Convert(IDataRow row, object rawValue) {\n            var coll = (RelationalExCollection)row.Sheet.Collection;\n            var key = System.Convert.ToInt32(rawValue);\n            return coll.FindReference(key);\n        }\n\n        #endregion\n\n        #region Serialization\n\n        public JObject ToJson() {\n            return new JObject() {\n                [\"type\"] = \"generic\"\n            };\n        }\n\n        public static GenericReferenceConverter FromJson(JToken obj) {\n            return new GenericReferenceConverter();\n        }\n\n        public void ResolveReferences(SheetDefinition sheetDef) { }\n\n        #endregion\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Ex/Relational/ValueConverters/IconConverter.cs",
    "content": "﻿using System;\nusing SaintCoinach.IO;\n\nusing Newtonsoft.Json.Linq;\nusing SaintCoinach.Ex.Relational.Definition;\n\nnamespace SaintCoinach.Ex.Relational.ValueConverters {\n    public class IconConverter : IValueConverter {\n        #region IValueConverter Members\n\n        public string TargetTypeName { get { return \"Image\"; } }\n        public Type TargetType { get { return typeof(Imaging.ImageFile); } }\n\n        public object Convert(IDataRow row, object rawValue) {\n            var nr = System.Convert.ToInt32(rawValue);\n            if (nr <= 0 || nr > 999999)\n                return null;\n\n            var sheet = row.Sheet;\n            return Imaging.IconHelper.GetIcon(sheet.Collection.PackCollection, sheet.Language, nr);\n        }\n\n        #endregion\n\n        #region Serialization\n\n        public JObject ToJson() {\n            return new JObject() {\n                [\"type\"] = \"icon\"\n            };\n        }\n\n        public static IconConverter FromJson(JToken obj) {\n            return new IconConverter();\n        }\n\n        public void ResolveReferences(SheetDefinition sheetDef) { }\n\n        #endregion\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Ex/Relational/ValueConverters/MultiReferenceConverter.cs",
    "content": "﻿using Newtonsoft.Json.Linq;\nusing SaintCoinach.Ex.Relational.Definition;\nusing System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace SaintCoinach.Ex.Relational.ValueConverters {\n    public class MultiReferenceConverter : IValueConverter {\n        #region Properties\n\n        public string[] Targets { get; set; }\n\n        #endregion\n\n        #region IValueConverter Members\n\n        public string TargetTypeName { get { return \"Row\"; } }\n        public Type TargetType { get { return typeof(IRelationalRow); } }\n\n        public object Convert(IDataRow row, object rawValue) {\n            if (Targets == null)\n                return null;\n\n            var key = System.Convert.ToInt32(rawValue);\n\n            foreach (var target in Targets) {\n                var sheet = row.Sheet.Collection.GetSheet(target);\n                if (!sheet.Header.DataFileRanges.Any(r => r.Contains(key)))\n                    continue;\n\n                if (sheet.ContainsRow(key))\n                    return sheet[key];\n            }\n            return null;\n        }\n\n        #endregion\n\n        #region Serialization\n\n        public JObject ToJson() {\n            return new JObject() {\n                [\"type\"] = \"multiref\",\n                [\"targets\"] = new JArray(Targets)\n            };\n        }\n\n        public static MultiReferenceConverter FromJson(JToken obj) {\n            return new MultiReferenceConverter() {\n                Targets = obj[\"targets\"].Select(t => (string)t).ToArray()\n            };\n        }\n\n        public void ResolveReferences(SheetDefinition sheetDef) { }\n\n        #endregion\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Ex/Relational/ValueConverters/QuadConverter.cs",
    "content": "﻿using Newtonsoft.Json.Linq;\nusing SaintCoinach.Ex.Relational.Definition;\nusing SaintCoinach.Xiv;\nusing System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace SaintCoinach.Ex.Relational.ValueConverters {\n    public class QuadConverter : IValueConverter {\n\n        #region IValueConverter Members\n\n        public string TargetTypeName => \"Quad\";\n\n        public Type TargetType => typeof(Quad);\n\n        public object Convert(IDataRow row, object rawValue) {\n            return new Quad((long)rawValue);\n        }\n\n        #endregion\n\n        #region Serialization\n\n        public JObject ToJson() {\n            return new JObject() {\n                [\"type\"] = \"quad\"\n            };\n        }\n\n        public static QuadConverter FromJson(JToken obj) {\n            return new QuadConverter();\n        }\n\n        public void ResolveReferences(SheetDefinition sheetDef) { }\n\n        #endregion\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Ex/Relational/ValueConverters/SheetLinkConverter.cs",
    "content": "﻿using Newtonsoft.Json.Linq;\nusing SaintCoinach.Ex.Relational.Definition;\nusing System;\n\nnamespace SaintCoinach.Ex.Relational.ValueConverters {\n    public class SheetLinkConverter : IValueConverter {\n        #region Properties\n\n        public string TargetSheet { get; set; }\n\n        #endregion\n\n        #region IValueConverter Members\n\n        public string TargetTypeName { get { return TargetSheet; } }\n        public Type TargetType { get { return typeof(IRelationalRow); } }\n\n        public object Convert(IDataRow row, object rawValue) {\n            var coll = row.Sheet.Collection;\n            if (!coll.SheetExists(TargetSheet))\n                return null;\n\n            var sheet = coll.GetSheet(TargetSheet);\n\n            var key = System.Convert.ToInt32(rawValue);\n            return !sheet.ContainsRow(key) ? null : sheet[key];\n        }\n\n        #endregion\n\n        #region Serialization\n\n        public JObject ToJson() {\n            return new JObject() {\n                [\"type\"] = \"link\",\n                [\"target\"] = TargetSheet\n            };\n        }\n\n        public static SheetLinkConverter FromJson(JToken obj) {\n            return new SheetLinkConverter() {\n                TargetSheet = (string)obj[\"target\"]\n            };\n        }\n\n        public void ResolveReferences(SheetDefinition sheetDef) { }\n\n        #endregion\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Ex/Relational/ValueConverters/TomestoneOrItemReferenceConverter.cs",
    "content": "﻿using Newtonsoft.Json.Linq;\nusing SaintCoinach.Ex.Relational.Definition;\nusing SaintCoinach.Xiv;\nusing System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace SaintCoinach.Ex.Relational.ValueConverters {\n    public class TomestoneOrItemReferenceConverter : IValueConverter {\n        static Dictionary<int, Item> _tomestoneKeyByRewardIndex;\n\n        #region IValueConverter Members\n\n        public string TargetTypeName => \"Item\";\n        public Type TargetType => typeof(IRelationalRow);\n\n        public object Convert(IDataRow row, object rawValue) {\n            if (_tomestoneKeyByRewardIndex == null)\n                _tomestoneKeyByRewardIndex = BuildTomestoneRewardIndex(row.Sheet.Collection);\n\n            var key = System.Convert.ToInt32(rawValue);\n            if (_tomestoneKeyByRewardIndex.TryGetValue(key, out var item))\n                return item;\n\n            var items = row.Sheet.Collection.GetSheet(\"Item\");\n            return items.ContainsRow(key) ? items[key] : rawValue;\n        }\n\n        #endregion\n\n        private Dictionary<int, Item> BuildTomestoneRewardIndex(ExCollection coll) {\n            var index = new Dictionary<int, Item>();\n\n            var sheet = coll.GetSheet(\"TomestonesItem\");\n            foreach (XivRow row in sheet) {\n                var rewardIndex = (int)row.GetRaw(2); // For compatibility only.\n                if (rewardIndex > 0)\n                    index[rewardIndex] = row.As<Item>();\n            }\n\n            return index;\n        }\n\n        #region Serialization\n\n        public JObject ToJson() {\n            return new JObject() {\n                [\"type\"] = \"tomestone\"\n            };\n        }\n\n        public static TomestoneOrItemReferenceConverter FromJson(JToken obj) {\n            return new TomestoneOrItemReferenceConverter();\n        }\n\n        public void ResolveReferences(SheetDefinition sheetDef) { }\n\n        #endregion\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Ex/Variant1/DataRow.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.IO;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace SaintCoinach.Ex.Variant1 {\n    public class DataRow : DataRowBase {\n        const int MetadataLength = 0x06;\n\n        public int Length { get; private set; }\n\n        #region Constructors\n\n        public DataRow(IDataSheet sheet, int key, int offset) : base(sheet, key, offset + MetadataLength) {\n            var b = sheet.GetBuffer();\n            if (b.Length < offset + MetadataLength) throw new IndexOutOfRangeException();\n\n            Length = OrderedBitConverter.ToInt32(b, offset, true);\n            var c = OrderedBitConverter.ToInt16(b, offset + 4, true);\n            if (c != 1) throw new InvalidDataException();\n        }\n\n        #endregion\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Ex/Variant1/RelationalDataRow.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace SaintCoinach.Ex.Variant1 {\n    using Relational;\n    using System.Collections.Concurrent;\n\n    public class RelationalDataRow : DataRow, IRelationalDataRow {\n        #region Fields\n        private ConcurrentDictionary<string, WeakReference<object>> _ValueReferences = new ConcurrentDictionary<string, WeakReference<object>>();\n        #endregion\n\n        public new IRelationalDataSheet Sheet { get { return (IRelationalDataSheet)base.Sheet; } }\n\n        public override string ToString() {\n            var defCol = Sheet.Header.DefaultColumn;\n            return defCol == null\n                       ? string.Format(\"{0}#{1}\", Sheet.Header.Name, Key)\n                       : string.Format(\"{0}\", this[defCol.Index]);\n        }\n\n        #region Constructors\n\n        public RelationalDataRow(IDataSheet sheet, int key, int offset) : base(sheet, key, offset) { }\n\n        #endregion\n\n        #region IRelationalRow Members\n\n        IRelationalSheet IRelationalRow.Sheet { get { return Sheet; } }\n\n        public object DefaultValue {\n            get {\n                var defCol = Sheet.Header.DefaultColumn;\n                return defCol == null ? null : this[defCol.Index];\n            }\n        }\n\n        public object this[string columnName] {\n            get {\n                var valRef = _ValueReferences.GetOrAdd(columnName, c => new WeakReference<object>(GetColumnValue(c)));\n                if (valRef.TryGetTarget(out var val))\n                    return val;\n\n                val = GetColumnValue(columnName);\n                valRef.SetTarget(val);\n                return val;\n            }\n        }\n\n        private object GetColumnValue(string columnName) {\n            var col = Sheet.Header.FindColumn(columnName);\n            if (col == null)\n                throw new KeyNotFoundException();\n            return this[col.Index];\n        }\n\n        object IRelationalRow.GetRaw(string columnName) {\n            var column = Sheet.Header.FindColumn(columnName);\n            if (column == null)\n                throw new KeyNotFoundException();\n            return column.ReadRaw(Sheet.GetBuffer(), this);\n        }\n\n        #endregion\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Ex/Variant2/DataRow.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.IO;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace SaintCoinach.Ex.Variant2 {\n    public class DataRow : DataRowBase {\n        const int MetadataLength = 0x06;\n\n        private bool _IsRead;\n\n        private Dictionary<int, SubRow> _SubRows = new Dictionary<int, SubRow>();\n\n        public int Length { get; private set; }\n        public int SubRowCount { get; private set; }\n\n        public IEnumerable<int> SubRowKeys {\n            get {\n                if (!_IsRead) Read();\n                return _SubRows.Keys;\n            }\n        }\n        public IEnumerable<SubRow> SubRows {\n            get {\n                if (!_IsRead) Read();\n                return _SubRows.Values;\n            }\n        }\n\n        public SubRow GetSubRow(int key) {\n            if (!_IsRead) Read();\n            return _SubRows[key];\n        }\n\n        #region Constructors\n\n        public DataRow(IDataSheet sheet, int key, int offset) : base(sheet, key, offset + MetadataLength) {\n            var b = sheet.GetBuffer();\n            if (b.Length < offset + MetadataLength) throw new IndexOutOfRangeException();\n\n            Length = OrderedBitConverter.ToInt32(b, offset, true);\n            SubRowCount = OrderedBitConverter.ToInt16(b, offset + 4, true);\n        }\n\n        #endregion\n\n        protected virtual void Read() {\n            _SubRows.Clear();\n\n            var h = Sheet.Header;\n            var b = Sheet.GetBuffer();\n            var o = Offset;\n            for(var i = 0; i < SubRowCount; ++i) {\n                var key = OrderedBitConverter.ToInt16(b, o, true);\n                o += 2;\n\n                var r = new SubRow(this, key, o);\n                _SubRows.Add(key, r);\n\n                o += h.FixedSizeDataLength;\n            }\n\n            _IsRead = true;\n        }\n        \n        public override object this[int column] => throw new InvalidOperationException(\"Cannot get column on Variant 2 DataRow. Use GetSubRow instead.\");\n\n        public override object GetRaw(int column) {\n            throw new InvalidOperationException(\"Cannot get column on Variant 2 DataRow. Use GetSubRow instead.\");\n        }\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Ex/Variant2/RelationalDataRow.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace SaintCoinach.Ex.Variant2 {\n    using Relational;\n\n    public class RelationalDataRow : DataRow, IRelationalDataRow {\n        public new IRelationalDataSheet Sheet { get { return (IRelationalDataSheet)base.Sheet; } }\n\n        public override string ToString() {\n            var defCol = Sheet.Header.DefaultColumn;\n            return defCol == null\n                       ? string.Format(\"{0}#{1}\", Sheet.Header.Name, Key)\n                       : string.Format(\"{0}\", GetSubRow(defCol.Index).DefaultValue);\n        }\n\n        #region Constructors\n\n        public RelationalDataRow(IDataSheet sheet, int key, int offset) : base(sheet, key, offset) { }\n\n        #endregion\n\n        #region IRelationalRow Members\n\n        IRelationalSheet IRelationalRow.Sheet { get { return Sheet; } }\n\n        public object DefaultValue {\n            get {\n                var defCol = Sheet.Header.DefaultColumn;\n                return defCol == null ? null : this[defCol.Index];\n            }\n        }\n\n        public object this[string columnName] {\n            get {\n                var col = Sheet.Header.FindColumn(columnName);\n                if (col == null)\n                    throw new KeyNotFoundException();\n                return this[col.Index];\n            }\n        }\n\n        object IRelationalRow.GetRaw(string columnName) {\n            var column = Sheet.Header.FindColumn(columnName);\n            if (column == null)\n                throw new KeyNotFoundException();\n            return this.GetRaw(column.Index);\n        }\n\n        #endregion\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Ex/Variant2/SubRow.cs",
    "content": "﻿using SaintCoinach.Ex.Relational;\nusing System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace SaintCoinach.Ex.Variant2 {\n    public class SubRow : DataRowBase, IRelationalDataRow {\n        public IDataRow ParentRow { get; private set; }\n        public string FullKey {\n            get { return ParentRow.Key + \".\" + Key; }\n        }\n\n        #region Constructors\n\n        public SubRow(IDataRow parent, int key, int offset) : base(parent.Sheet, key, offset) {\n            ParentRow = parent;\n        }\n\n        #endregion\n\n        #region IRelationalRow Members\n\n        public new IRelationalDataSheet Sheet { get { return (IRelationalDataSheet)base.Sheet; } }\n\n        IRelationalDataSheet IRelationalDataRow.Sheet { get { return Sheet; } }\n\n        IRelationalSheet IRelationalRow.Sheet { get { return Sheet; } }\n\n        public object DefaultValue {\n            get {\n                var defCol = Sheet.Header.DefaultColumn;\n                return defCol == null ? null : this[defCol.Index];\n            }\n        }\n\n        public object this[string columnName] {\n            get {\n                var col = Sheet.Header.FindColumn(columnName);\n                if (col == null)\n                    throw new KeyNotFoundException();\n                return this[col.Index];\n            }\n        }\n\n        object IRelationalRow.GetRaw(string columnName) {\n            var column = Sheet.Header.FindColumn(columnName);\n            if (column == null)\n                throw new KeyNotFoundException();\n            return this.GetRaw(column.Index);\n        }\n\n        #endregion\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Graphics/Bone.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace SaintCoinach.Graphics {\n    [System.Diagnostics.DebuggerDisplay(\"{Name} ( {Unknown1.X}, {Unknown1.Y}, {Unknown1.Z}, {Unknown1.W} ) -> ( {Unknown2.X}, {Unknown2.Y}, {Unknown2.Z}, {Unknown2.W} )\")]\n    public class Bone {\n        #region Properties\n        public ModelDefinition Definition { get; private set; }\n        public string Name { get; private set; }\n        public int Index { get; private set; }\n        public Vector4 Unknown1 { get; private set; }\n        public Vector4 Unknown2 { get; private set; }\n        #endregion\n\n        #region Constructor\n        internal Bone(ModelDefinition definition, int index, byte[] buffer, ref int offset) {\n            this.Definition = definition;\n            this.Index = index;\n            this.Name = definition.BoneNames[index];\n\n            this.Unknown1 = buffer.ToStructure<Vector4>(ref offset);\n            this.Unknown2 = buffer.ToStructure<Vector4>(ref offset);\n        }\n        #endregion\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Graphics/BoundingBox.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Runtime.InteropServices;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace SaintCoinach.Graphics {\n    [StructLayout(LayoutKind.Sequential)]\n    public struct BoundingBox {\n        public Vector4 PointA;\n        public Vector4 PointB;\n\n        public BoundingBox Scale(float factor) { return Scale(new Vector3 { X = factor, Y = factor, Z = factor }); }\n        public BoundingBox Scale(Vector3 factor) {\n            var center = new Vector3 {\n                X = (PointA.X + PointB.X) / 2f,\n                Y = (PointA.Y + PointB.Y) / 2f,\n                Z = (PointA.Z + PointB.Z) / 2f\n            };\n            var d = new Vector3 {\n                X = Math.Abs(PointA.X - center.X) * factor.X,\n                Y = Math.Abs(PointA.Y - center.Y) * factor.Y,\n                Z = Math.Abs(PointA.Z - center.Z) * factor.Z\n            };\n\n            var retPointA = new Vector4 {\n                X = center.X + d.X,\n                Y = center.Y + d.Y,\n                Z = center.Z + d.Z,\n                W = PointA.W\n            };\n            var retPointB = new Vector4 {\n                X = center.X - d.X,\n                Y = center.Y - d.Y,\n                Z = center.Z - d.Z,\n                W = PointB.W\n            };\n\n            return new BoundingBox {\n                PointA = retPointA,\n                PointB = retPointB\n            };\n        }\n        public BoundingBox Grow(float change) { return Grow(new Vector3 { X = change, Y = change, Z = change }); }\n        public BoundingBox Grow(Vector3 change) {\n            var center = new Vector3 {\n                X = (PointA.X + PointB.X) / 2f,\n                Y = (PointA.Y + PointB.Y) / 2f,\n                Z = (PointA.Z + PointB.Z) / 2f\n            };\n            var d = new Vector3 {\n                X = Math.Abs(PointA.X - center.X) + change.X,\n                Y = Math.Abs(PointA.Y - center.Y) + change.Y,\n                Z = Math.Abs(PointA.Z - center.Z) + change.Z\n            };\n\n            var retPointA = new Vector4 {\n                X = center.X + d.X,\n                Y = center.Y + d.Y,\n                Z = center.Z + d.Z,\n                W = PointA.W\n            };\n            var retPointB = new Vector4 {\n                X = center.X - d.X,\n                Y = center.Y - d.Y,\n                Z = center.Z - d.Z,\n                W = PointB.W\n            };\n\n            return new BoundingBox {\n                PointA = retPointA,\n                PointB = retPointB\n            };\n        }\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Graphics/ColorMap.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Drawing;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace SaintCoinach.Graphics {\n    public class ColorMap {\n        public IO.File File { get; private set; }\n        public Color[] Colors { get; private set; }\n\n        public ColorMap(IO.File file) {\n            this.File = file;\n\n            Build();\n        }\n\n        private void Build() {\n            var buffer = File.GetData();\n            this.Colors = new Color[buffer.Length / 4];\n\n            for (var i = 0; i < buffer.Length; i += 4) {\n                var r = buffer[i];\n                var g = buffer[i + 1];\n                var b = buffer[i + 2];\n                var a = buffer[i + 3];\n                this.Colors[i / 4] = Color.FromArgb(a, r, g, b);\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Graphics/Exports/ModelExport.cs",
    "content": "﻿using SaintCoinach.Imaging;\nusing System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace SaintCoinach.Graphics.Exports {\n    public class ModelExport {\n        public Model Model { get; private set; }\n        public ImcVariant Variant { get; private set; }\n        public List<MeshExport> Meshes { get; private set; }\n\n        public string GetErrors() {\n            return string.Join(Environment.NewLine, Meshes.SelectMany(m => m.Errors));\n        }\n\n        private ModelExport() { }\n\n        public static ModelExport AsObj(Model model, ImcVariant variant) {\n            var export = new ModelExport();\n            export.Model = model;\n            export.Variant = variant;\n\n            export.Meshes = new List<MeshExport>();\n            foreach (var mesh in model.Meshes) {\n                var bytes = Obj.GetBytes(mesh);\n                var material = mesh.Material.Get(variant);\n                export.Meshes.Add(new MeshExport(bytes, material));\n            }\n            return export;\n        }\n    }\n\n    public class MeshExport {\n        public byte[] Bytes { get; private set; }\n        public ImageFile Diffuse { get; private set; }\n        public ImageFile Specular { get; private set; }\n        public ImageFile Normal { get; private set; }\n        public ImageFile Mask { get; private set; }\n        public ImageFile Table { get; private set; }\n        public ImageFile Emissive { get; private set; }\n        public ImageFile Alpha { get; private set; }\n        public string[] Errors { get; private set; }\n\n        public MeshExport(byte[] bytes, Material material) {\n            Bytes = bytes;\n\n            ExportTextures(material);\n        }\n\n        private void ExportTextures(Material material) {\n            var errors = new List<string>();\n\n            foreach (var param in material.TextureParameters) {\n                var tex = material.TexturesFiles[param.TextureIndex];\n\n                switch (param.ParameterId) {\n                    case 0x0C5EC1F1:\n                        Normal = tex;\n                        break;\n\n                    case 0x2005679F:\n                        Table = tex;\n                        break;\n\n                    case 0x8A4E82B6:\n                        Mask = tex;\n                        break;\n\n                    case 0x115306BE:\n                        Diffuse = tex;\n                        break;\n\n                    case 0x2B99E025:\n                        Specular = tex;\n                        break;\n\n                    // todo: emissive\n                    // todo: alpha\n\n                    default:\n                        errors.Add($\"Unknown character parameter {param.ParameterId:X8} for texture '{tex.Path}' in material '{material.File.Path}'\");\n                        break;\n                }\n            }\n\n            Errors = errors.ToArray();\n        }\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Graphics/Exports/Obj.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace SaintCoinach.Graphics.Exports {\n    public static class Obj {\n        public static byte[] GetBytes(Mesh mesh) {\n            var sb = new StringBuilder();\n\n            // Positions\n            foreach (var vertex in mesh.Vertices.Where(v => v.Position.HasValue)) {\n                var pos = vertex.Position.Value;\n                sb.AppendLine($\"v {pos.X:N5} {pos.Y:N5} {pos.Z:N5}\");\n            }\n\n            // UVs\n            foreach (var vertex in mesh.Vertices.Where(v => v.UV.HasValue)) {\n                var uv = vertex.UV.Value;\n                sb.AppendLine($\"vt {uv.X:N5} {(1 - uv.Y):N5}\");\n            }\n\n            // Normals\n            foreach (var vertex in mesh.Vertices.Where(v => v.Normal.HasValue)) {\n                var normal = vertex.Normal.Value;\n                sb.AppendLine($\"vn {normal.X:N5} {normal.Y:N5} {normal.Z:N5}\");\n            }\n\n            // Indices\n            for (var i = 0; i < mesh.Indices.Length; i += 3) {\n                var index1 = mesh.Indices[i] + 1;\n                var index2 = mesh.Indices[i + 1] + 1;\n                var index3 = mesh.Indices[i + 2] + 1;\n                sb.AppendLine($\"f {index1}/{index1}/{index1} {index2}/{index2}/{index2} {index3}/{index3}/{index3}\");\n            }\n\n            return Encoding.ASCII.GetBytes(sb.ToString());\n        }\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Graphics/ImcFile.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace SaintCoinach.Graphics {\n    public class ImcFile {\n        #region Fields\n        private Dictionary<byte, ImcPart> _Parts = new Dictionary<byte, ImcPart>();\n        #endregion\n\n        #region Properties\n        public IO.File SourceFile { get; private set; }\n        public IEnumerable<ImcPart> Parts { get { return _Parts.Values; } }\n        public short PartsMask { get; private set; }\n        public short Count { get; private set; }\n        #endregion\n\n        #region Constructor\n        public ImcFile(IO.File sourceFile) {\n            this.SourceFile = sourceFile;\n\n            var buffer = SourceFile.GetData();\n            this.Count = BitConverter.ToInt16(buffer, 0);\n            this.PartsMask  = BitConverter.ToInt16(buffer, 2);\n\n            var offset = 4;\n            for (byte bit = 0; bit < 8; ++bit) {\n                var match = 1 << bit;\n                if ((PartsMask & match) == match)\n                    _Parts.Add(bit, new ImcPart(buffer, bit, ref offset));\n            }\n\n            var rem = Count;\n            while (--rem >= 0) {\n                foreach (var part in _Parts.Values)\n                    part._Variants.Add(buffer.ToStructure<ImcVariant>(ref offset));\n            }\n        }\n        #endregion\n\n        #region Get\n        public ImcVariant GetVariant(int index) {\n            return Parts.First().Variants[index];\n        }\n        public ImcVariant GetVariant(byte partKey, int index) {\n            return _Parts[partKey].Variants[index];\n        }\n        #endregion\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Graphics/ImcPart.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Collections.ObjectModel;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace SaintCoinach.Graphics {\n    public class ImcPart {\n        #region Fields\n        internal List<ImcVariant> _Variants = new List<ImcVariant>();\n        private IReadOnlyList<ImcVariant> _ROVariants;\n        #endregion\n\n        #region Properties\n        public byte Bit { get; private set; }\n        public ImcVariant DefaultVariant { get { return _Variants[0]; } }\n        public IReadOnlyList<ImcVariant> Variants { get { return _ROVariants ?? (_ROVariants = new ReadOnlyCollection<ImcVariant>(_Variants)); } }\n        #endregion\n\n        #region Constructor\n        internal ImcPart(byte[] buffer, byte bit, ref int offset) {\n            this.Bit = bit;\n            _Variants.Add(buffer.ToStructure<ImcVariant>(ref offset));\n        }\n        #endregion\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Graphics/ImcVariant.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Runtime.InteropServices;\nusing System.Threading.Tasks;\n\nnamespace SaintCoinach.Graphics {\n    [StructLayout(LayoutKind.Sequential)]\n    public struct ImcVariant {\n        public short Variant;   // TODO: Only lower 8 bits are for v####, upper 8 bits unknown\n        public ushort PartVisibilityMask;\n        public byte Unknown3;\n        public byte Unknown4;\n\n        public static readonly ImcVariant Default = new ImcVariant {\n            Variant = 1,\n            PartVisibilityMask = 0x03FF,\n            Unknown3 = 0x00,\n            Unknown4 = 0x00\n        };\n        public static implicit operator ModelVariantIdentifier(ImcVariant imcVariant) {\n            return new ModelVariantIdentifier {\n                ImcVariant = imcVariant\n            };\n        }\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Graphics/Lgb/ILgbEntry.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace SaintCoinach.Graphics.Lgb {\n    public interface ILgbEntry {\n        LgbEntryType Type { get; }\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Graphics/Lgb/LgbENpcEntry.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Runtime.InteropServices;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace SaintCoinach.Graphics.Lgb {\n    public class LgbENpcEntry : ILgbEntry {\n        #region Struct\n        [StructLayout(LayoutKind.Sequential)]\n        public struct HeaderData {\n            public LgbEntryType Type;\n            public uint UnknownId;\n            public int NameOffset;\n            public Vector3 Translation;\n            public Vector3 Rotation;\n            public Vector3 Scale;\n            public uint ENpcId;\n            // 24 bytes of unknowns\n        }\n        #endregion\n\n        #region Properties\n        public LgbEntryType Type => Header.Type;\n        public HeaderData Header { get; private set; }\n        public string Name { get; private set; }\n        #endregion\n\n        #region Constructor\n        public LgbENpcEntry(IO.PackCollection packs, byte[] buffer, int offset) {\n            this.Header = buffer.ToStructure<HeaderData>(offset);\n            this.Name = buffer.ReadString(offset + Header.NameOffset);\n        }\n        #endregion\n\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Graphics/Lgb/LgbEntryType.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace SaintCoinach.Graphics.Lgb {\n\n\n    //From old client constructors were in the format Client::LayoutEngine::Layer::BgPartsLayoutInstance::`vftable'\n\n\n    public enum LgbEntryType : int {\n        BgParts = 1,\n        //Keep this for backwards compatability\n        Model = 1,\n        Light = 3,\n        Vfx = 4,\n        PositionMarker = 5,\n        Gimmick = 6,\n        SharedGroup6 = 6,// secondary variable is set to 2\n        Sound = 7,\n        EventNpc = 8,\n        BattleNpc = 9,\n        Aetheryte = 12,\n        EnvSpace = 13,\n        Gathering = 14,\n        SharedGroup15 = 15,// secondary variable is set to 13\n        Treasure = 16,\n        Weapon = 39,\n        PopRange = 40,\n        ExitRange = 41,\n        MapRange = 43,\n        NaviMeshRange = 44,\n        EventObject = 45,\n        EnvLocation = 47,\n        EventRange = 49,\n        QuestMarker = 51,\n        CollisionBox = 57,\n        DoorRange = 58,\n        LineVfx = 59,\n        ClientPath = 65,\n        ServerPath = 66,\n        GimmickRange = 67,\n        TargetMarker = 68,\n        ChairMarker = 69,\n        ClickableRange = 70,\n        PrefetchRange = 71,\n        FateRange = 72,\n        SphereCastRange = 75,\n\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Graphics/Lgb/LgbEventObjectEntry.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Runtime.InteropServices;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace SaintCoinach.Graphics.Lgb {\n    public class LgbEventObjectEntry : ILgbEntry {\n        private static Ex.ExCollection Collection;\n        private static Ex.ISheet EventObjectSheet;\n        private static Ex.ISheet ExportedSgSheet;\n\n        #region Struct\n        [StructLayout(LayoutKind.Sequential)]\n        public struct HeaderData {\n            public LgbEntryType Type;\n            public uint Unknown2;\n            public int NameOffset;\n            public Vector3 Translation;\n            public Vector3 Rotation;\n            public Vector3 Scale;\n            public uint EventObjectId;\n            public uint GimmickId;\n            // + unknowns\n        }\n        #endregion\n\n        #region Properties\n        LgbEntryType ILgbEntry.Type { get { return Header.Type; } }\n        public HeaderData Header { get; private set; }\n        public string Name { get; private set; }\n        public Sgb.SgbFile Gimmick { get; private set; }\n        #endregion\n\n        #region Constructor\n        public LgbEventObjectEntry(IO.PackCollection packs, byte[] buffer, int offset) {\n            this.Header = buffer.ToStructure<HeaderData>(offset);\n            this.Name = buffer.ReadString(offset + Header.NameOffset);\n\n            if (Collection == null) {\n                LoadSheets(packs);\n            }\n            foreach (Ex.IDataRow row in EventObjectSheet) {\n                if (row.Key == this.Header.EventObjectId) {\n                    var sg = row.GetRaw(11);\n                    foreach (Ex.IDataRow row2 in ExportedSgSheet) {\n                        if (row2.Key == (ushort)sg) {\n                            var path = ((SaintCoinach.Text.XivString)row2.GetRaw(0)).ToString();\n                            if (!string.IsNullOrEmpty(path)) {\n                                SaintCoinach.IO.File file;\n                                if (packs.TryGetFile(path, out file))\n                                    this.Gimmick = new Sgb.SgbFile(file);\n                            }\n                            break;\n                        }\n                    }\n                    break;\n                }\n            }\n        }\n\n        private void LoadSheets(IO.PackCollection packs) {\n            Collection = new Ex.ExCollection(packs);\n            EventObjectSheet = Collection.GetSheet(\"EObj\");\n            ExportedSgSheet = Collection.GetSheet(\"ExportedSG\");\n\n        }\n        #endregion\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Graphics/Lgb/LgbFile.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Runtime.InteropServices;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace SaintCoinach.Graphics.Lgb {\n    public class LgbFile {\n        #region Struct\n        [StructLayout(LayoutKind.Sequential)]\n        public struct HeaderData {\n            public uint Magic1;     // LGB1\n            public uint FileSize;\n            public uint Unknown1;\n            public uint Magic2;     // LGP1\n            public uint Unknown2;\n            public uint Unknown3;\n            public uint Unknown4;\n            public uint Unknown5;\n            public int GroupCount;\n        }\n        #endregion\n\n        #region Properties\n        public HeaderData Header { get; private set; }\n        public IO.File File { get; private set; }\n        public LgbGroup[] Groups { get; private set; }\n        #endregion\n\n        #region Constructor\n        public LgbFile(IO.File file) {\n            this.File = file;\n\n            Build();\n        }\n        #endregion\n\n        #region Build\n        private void Build() {\n            var buffer = File.GetData();\n\n            var baseOffset = 0;\n\n            this.Header = buffer.ToStructure<HeaderData>(ref baseOffset);\n            if (Header.Magic1 != 0x3142474C || Header.Magic2 != 0x3150474C)     // LGB1 & LGP1\n                throw new System.IO.InvalidDataException();\n\n            Groups = new LgbGroup[Header.GroupCount];\n            for (int i = 0; i < Header.GroupCount; ++i) {\n                var groupOffset = baseOffset + BitConverter.ToInt32(buffer, baseOffset + i * 4);\n                Groups[i] = new LgbGroup(this, buffer, groupOffset);\n            }\n        }\n        #endregion\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Graphics/Lgb/LgbGimmickEntry.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Runtime.InteropServices;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace SaintCoinach.Graphics.Lgb {\n    public class LgbGimmickEntry : ILgbEntry {\n        #region Struct\n        [StructLayout(LayoutKind.Sequential)]\n        public struct HeaderData {\n            public LgbEntryType Type;\n            public uint GimmickId;\n            public int NameOffset;\n            public Vector3 Translation;\n            public Vector3 Rotation;\n            public Vector3 Scale;\n            public int GimmickFileOffset;\n\n            // + 100 bytes of unknowns\n        }\n        #endregion\n\n        #region Properties\n        LgbEntryType ILgbEntry.Type { get { return Header.Type; } }\n        public HeaderData Header { get; private set; }\n        public string Name { get; private set; }\n        public Sgb.SgbFile Gimmick { get; private set; }\n        #endregion\n\n        #region Constructor\n        public LgbGimmickEntry(IO.PackCollection packs, byte[] buffer, int offset) {\n            this.Header = buffer.ToStructure<HeaderData>(offset);\n            \n            this.Name = buffer.ReadString(offset + Header.NameOffset);\n\n            var gimmickFilePath = buffer.ReadString(offset + Header.GimmickFileOffset);\n            if (!string.IsNullOrWhiteSpace(gimmickFilePath)) {\n                SaintCoinach.IO.File file;\n                if (packs.TryGetFile(gimmickFilePath, out file))\n                    this.Gimmick = new Sgb.SgbFile(file);\n            }\n        }\n        #endregion\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Graphics/Lgb/LgbGroup.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Diagnostics;\nusing System.Linq;\nusing System.Runtime.InteropServices;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace SaintCoinach.Graphics.Lgb {\n    public class LgbGroup {\n        #region Struct\n        [StructLayout(LayoutKind.Sequential)]\n        public struct HeaderData {\n            public uint Unknown1;\n            public int GroupNameOffset;\n            public int EntriesOffset;\n            public int EntryCount;\n            public uint Unknown2;\n            public uint Unknown3;\n            public uint FestivalId;\n            public uint Unknown5;\n            // Just a guess -\n            // This number corresponds to the last digits of Map.Id.  In\n            // territories with rotated subdivisions, it can be used to select\n            // the appropriate map for coordinate calculation.\n            // Possibly 3-4 bytes to match the first three columns in the Map\n            // exd.\n            public uint MapIndex;\n            public uint Unknown7;\n            public uint Unknown8;\n            public uint Unknown9;\n            public uint Unknown10;\n            public uint Unknown11;\n        }\n        #endregion\n\n        #region Properties\n        public LgbFile Parent { get; private set; }\n        public HeaderData Header { get; private set; }\n        public string Name { get; private set; }\n        public ILgbEntry[] Entries { get; private set; }\n        #endregion\n\n        #region Constructor\n        public LgbGroup(LgbFile parent, byte[] buffer, int offset) {\n            this.Parent = parent;\n            this.Header = buffer.ToStructure<HeaderData>(offset);\n            this.Name = buffer.ReadString(offset + Header.GroupNameOffset);\n\n            //uint[] Unknown = new uint[100];\n            //System.Buffer.BlockCopy(buffer, offset + System.Runtime.InteropServices.Marshal.SizeOf<HeaderData>(), Unknown, 0, 400);\n\n            \n            var entriesOffset = offset + Header.EntriesOffset;\n            Entries = new ILgbEntry[Header.EntryCount];\n            for(var i = 0; i < Header.EntryCount; ++i) {\n                var entryOffset = entriesOffset + BitConverter.ToInt32(buffer, entriesOffset + i * 4);\n                var type = (LgbEntryType)BitConverter.ToInt32(buffer, entryOffset);\n\n                try {\n                    switch (type) {\n                        case LgbEntryType.Model:\n                            Entries[i] = new LgbModelEntry(Parent.File.Pack.Collection, buffer, entryOffset);\n                            break;\n                        case LgbEntryType.Gimmick:\n                        case LgbEntryType.SharedGroup15:\n                            Entries[i] = new LgbGimmickEntry(Parent.File.Pack.Collection, buffer, entryOffset);\n                            break;\n                        case LgbEntryType.EventObject:\n                            Entries[i] = new LgbEventObjectEntry(Parent.File.Pack.Collection, buffer, entryOffset);\n                            break;\n                        case LgbEntryType.Light:\n                            Entries[i] = new LgbLightEntry(Parent.File.Pack.Collection, buffer, entryOffset);\n                            break;\n                        case LgbEntryType.EventNpc:\n                            Entries[i] = new LgbENpcEntry(Parent.File.Pack.Collection, buffer, entryOffset);\n                            break;\n                        case LgbEntryType.Vfx:\n                            Entries[i] = new LgbVfxEntry(Parent.File.Pack.Collection, buffer, entryOffset);\n                            break;\n                        default:\n                            // TODO: Work out other parts.\n                            //Debug.WriteLine($\"{Parent.File.Path} {type} at 0x{entryOffset:X} in {Name}: Can't read type.\");\n                            break;\n                    }\n                } catch (Exception ex) {\n                    Debug.WriteLine($\"{Parent.File.Path} {type} at 0x{entryOffset:X} in {Name} failure: {ex.Message}\");\n                }\n            }\n\n        }\n        #endregion\n\n        public override string ToString() => Name ?? \"(null)\";\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Graphics/Lgb/LgbLightEntry.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Runtime.InteropServices;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace SaintCoinach.Graphics.Lgb {\n    public class LgbLightEntry : ILgbEntry {\n        #region Struct\n        [StructLayout(LayoutKind.Sequential)]\n        public struct HeaderData {\n            public LgbEntryType Type;\n            public uint UnknownId;\n            public int NameOffset;\n            public Vector3 Translation;\n            public Vector3 Rotation;\n            public Vector3 Scale;\n            public uint EntryCount;\n            public Vector2 Entry1;\n            public int UnknownFlag1;\n            public Vector2 Entry2;\n            public int Entry2NameOffset;\n            public ushort Entry3NameOffset;\n            public short UnknownFlag2;\n            public Vector2 Entry3;\n            public short UnknownFlag3;\n            public short UnknownFlag4;\n            public Vector2 Entry4;\n            public Vector2 Entry5;\n            // + unknowns\n        }\n        #endregion\n\n        #region Properties\n        LgbEntryType ILgbEntry.Type { get { return Header.Type; } }\n        public HeaderData Header { get; private set; }\n        public string Name { get; private set; }\n        public string Entry2Name, Entry3Name;\n        public Sgb.SgbFile Gimmick { get; private set; }\n        #endregion\n\n        #region Constructor\n        public LgbLightEntry(IO.PackCollection packs, byte[] buffer, int offset) {\n            this.Header = buffer.ToStructure<HeaderData>(offset);\n        }\n        #endregion\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Graphics/Lgb/LgbModelEntry.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Diagnostics;\nusing System.Linq;\nusing System.Runtime.InteropServices;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace SaintCoinach.Graphics.Lgb {\n    public class LgbModelEntry : ILgbEntry {\n        #region Struct\n        [StructLayout(LayoutKind.Sequential)]\n        public struct HeaderData {\n            public LgbEntryType Type;\n            public uint Unknown2;\n            public int NameOffset;\n            public Vector3 Translation;\n            public Vector3 Rotation;\n            public Vector3 Scale;\n            public int ModelFileOffset;\n            public int CollisionFileOffset;\n            public int Unknown4;\n            public int Unknown5;\n            public int Unknown6;\n            public int Unknown7;\n            public int Unknown8;\n            public int Unknown9;\n        }\n        #endregion\n\n        #region Properties\n        LgbEntryType ILgbEntry.Type { get { return Header.Type; } }\n        public HeaderData Header { get; private set; }\n        public string Name { get; private set; }\n        public string ModelFilePath { get; private set; }\n        public string CollisionFilePath { get; private set; }\n        public TransformedModel Model { get; private set; }\n        public Pcb.PcbFile CollisionFile { get; private set; }\n        #endregion\n\n        #region Constructor\n        public LgbModelEntry(IO.PackCollection packs, byte[] buffer, int offset) {\n            this.Header = buffer.ToStructure<HeaderData>(offset);\n            this.Name = buffer.ReadString(offset + Header.NameOffset);\n\n            ModelFilePath = buffer.ReadString(offset + Header.ModelFileOffset);\n            CollisionFilePath = buffer.ReadString(offset + Header.CollisionFileOffset);\n\n            if (!string.IsNullOrWhiteSpace(ModelFilePath)) {\n                SaintCoinach.IO.File mdlFile;\n                if (packs.TryGetFile(ModelFilePath, out mdlFile))\n                    this.Model = new TransformedModel(((Graphics.ModelFile)mdlFile).GetModelDefinition(), Header.Translation, Header.Rotation, Header.Scale);\n            }\n            \n            if (!string.IsNullOrWhiteSpace(CollisionFilePath)) {\n                try\n                {\n                    SaintCoinach.IO.File pcbFile;\n                    if (packs.TryGetFile(CollisionFilePath, out pcbFile))\n                        this.CollisionFile = new Pcb.PcbFile(pcbFile);\n                }\n                catch (Exception ex)\n                {\n                    Debug.WriteLine($\"{Name} at 0x{offset:X} PcbFile failure: {ex.Message}\");\n                }\n            }\n        }\n        #endregion\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Graphics/Lgb/LgbVfxEntry.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Runtime.InteropServices;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace SaintCoinach.Graphics.Lgb {\n    public class LgbVfxEntry : ILgbEntry {\n        #region Struct\n        [StructLayout(LayoutKind.Sequential)]\n        public struct HeaderData {\n            public LgbEntryType Type;\n            public uint UnknownId;\n            public int NameOffset;\n            public Vector3 Translation;\n            public Vector3 Rotation;\n            public Vector3 Scale;\n            public uint SomeId;\n\n\n            // 24 bytes of unknowns\n        }\n        #endregion\n\n        #region Properties\n        public LgbEntryType Type => Header.Type;\n        public HeaderData Header { get; private set; }\n        public string Name { get; private set; }\n        #endregion\n\n        #region Constructor\n        public LgbVfxEntry(IO.PackCollection packs, byte[] buffer, int offset) {\n            this.Header = buffer.ToStructure<HeaderData>(offset);\n            this.Name = buffer.ReadString(offset + Header.NameOffset);\n        }\n        #endregion\n\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Graphics/Material.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace SaintCoinach.Graphics {\n    [System.Diagnostics.DebuggerDisplay(\"{File.Path}\")]\n    public class Material {\n        // TODO: One of the Unknowns most likely specifies what parts of /other/ models should be hidden\n        //       Example: Gauntlets hiding rings or boots hiding the bottom of pants.\n        private const string DummyTextureInMaterial = \"dummy.tex\";\n        private const string DummyTexturePath = \"common/graphics/texture/dummy.tex\";\n\n        #region Properties\n        public MaterialDefinition Definition { get; private set; }\n        public IO.File File { get; private set; }\n        public ImcVariant Variant { get; private set; }\n\n        public MaterialHeader Header { get; private set; }\n        public Imaging.ImageFile[] TexturesFiles { get; private set; }\n        public string[] Maps { get; private set; }\n        public string[] DataSets { get; private set; }\n        public string Shader { get; private set; }\n        public byte[] Unknown { get; private set; }\n        public byte[] DataSetData { get; private set; }\n        public MaterialMetadataHeader MetadataHeader { get; private set; }\n        public Unknowns.MaterialStruct1[] UnknownStructs1 { get; private set; }\n        public Unknowns.MaterialStruct2[] UnknownStructs2 { get; private set; }\n        public MaterialTextureParameter[] TextureParameters { get; private set; }\n        public byte[] Data { get; private set; }\n        #endregion\n\n        #region Constructor\n        public Material(MaterialDefinition definition, IO.File file, ImcVariant variant) {\n            this.Definition = definition;\n            this.File = file;\n            this.Variant = variant;\n\n            Build();\n        }\n        #endregion\n\n        #region Build\n        private void Build() {\n            var buffer = File.GetData();\n\n            var offset = 0;\n\n            this.Header = buffer.ToStructure<MaterialHeader>(ref offset);\n\n            var stringsStart = offset + (4 * (Header.TextureCount + Header.MapCount + Header.DataSetCount));\n            var texFileNames = ReadStrings(buffer, ref offset, stringsStart, Header.TextureCount);\n            var packs = File.Pack.Collection;\n\n            this.TexturesFiles = new Imaging.ImageFile[Header.TextureCount];\n            for (var i = 0; i < Header.TextureCount; ++i) {\n                var path = texFileNames[i];\n                if (path == DummyTextureInMaterial)\n                    path = DummyTexturePath;\n                this.TexturesFiles[i] = (Imaging.ImageFile)packs.GetFile(path);\n            }\n\n            this.Maps = ReadStrings(buffer, ref offset, stringsStart, Header.MapCount);\n            this.DataSets = ReadStrings(buffer, ref offset, stringsStart, Header.DataSetCount);\n\n            this.Shader = buffer.ReadString(stringsStart + Header.ShaderOffset);\n\n            offset = stringsStart + Header.StringsSize;\n\n            this.Unknown = new byte[Header.UnknownSize];\n            Array.Copy(buffer, offset, this.Unknown, 0, Header.UnknownSize);\n            offset += Header.UnknownSize;\n\n            this.DataSetData = new byte[Header.DataSetSize];\n            Array.Copy(buffer, offset, this.DataSetData, 0, Header.DataSetSize);\n            offset += Header.DataSetSize;\n\n            this.MetadataHeader = buffer.ToStructure<MaterialMetadataHeader>(ref offset);\n\n            this.UnknownStructs1 = buffer.ToStructures<Unknowns.MaterialStruct1>(MetadataHeader.UnknownStruct1Count, ref offset);\n            this.UnknownStructs2 = buffer.ToStructures<Unknowns.MaterialStruct2>(MetadataHeader.UnknownStruct2Count, ref offset);\n            this.TextureParameters = buffer.ToStructures<MaterialTextureParameter>(MetadataHeader.ParameterMappingCount, ref offset);\n\n            this.Data = new byte[MetadataHeader.DataSize];\n            Array.Copy(buffer, offset, this.Data, 0, MetadataHeader.DataSize);\n        }\n        private static string[] ReadStrings(byte[] buffer, ref int offsetOffset, int stringsOffset, int count) {\n            var values = new string[count];\n            for (var i = 0; i < count; ++i) {\n                var o = BitConverter.ToInt16(buffer, offsetOffset);\n                values[i] = buffer.ReadString(stringsOffset + o);\n                offsetOffset += 4;\n            }\n            return values;\n        }\n        #endregion\n\n        public unsafe System.Drawing.Image GetColorSet() {\n            // ColorSet is R16G16B16A16F.\n            const int width = 4;\n            const int height = 16;\n\n            var bmp = new System.Drawing.Bitmap(width, height);\n            var i = 0;\n            for (var y = 0; y < height; y++) {\n                for (var x = 0; x < width; x++) {\n                    var offset = i++ * 4 * 2;\n\n                    var red = GetColorValue(HalfHelper.Unpack(DataSetData, offset));\n                    var green = GetColorValue(HalfHelper.Unpack(DataSetData, offset + 2));\n                    var blue = GetColorValue(HalfHelper.Unpack(DataSetData, offset + 4));\n                    var alpha = GetColorValue(HalfHelper.Unpack(DataSetData, offset + 6));\n                    bmp.SetPixel(x, y, System.Drawing.Color.FromArgb(alpha, red, green, blue));\n                }\n            }\n\n            return bmp;\n        }\n\n        private static byte GetColorValue(float half) {\n            if (half > 1)\n                return byte.MaxValue;\n            if (half < 0)\n                return 0;\n            return (byte)(half * byte.MaxValue);\n        }\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Graphics/MaterialDefinition.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Text.RegularExpressions;\nusing System.Threading.Tasks;\n\nnamespace SaintCoinach.Graphics {\n    [System.Diagnostics.DebuggerDisplay(\"{Name}\")]\n    public class MaterialDefinition {\n        #region Path expansion\n        /* Found referenced in random models (no idea which ones), and still have to be located\n            /lambert1.mtrl\n            /Lambert139.mtrl\n            /Lambert140.mtrl\n            /Lambert141.mtrl\n            /Lambert148.mtrl\n            /motion_Black.mtrl\n            /motion_Black5.mtrl\n            /motion_gray5.mtrl\n            /motion_red.mtrl\n            /motion_Skin.mtrl\n            /motion_Skin5.mtrl\n            /motion_White.mtrl\n            /motion_White5.mtrl\n            /Scene_Material.mtrl\n            /Scene_Material0.mtrl\n            /Scene_Material11.mtrl\n            /Scene_Material12.mtrl\n         */\n\n        private class PathExpander {\n            public Regex Pattern { get; private set; }\n            public string Replacement { get; private set; }\n            public string StainReplacement { get; private set; }\n            public bool ContainsVariant { get; private set; }\n\n            public PathExpander(string pattern, string replacementFormat, string replacementStainFormat, bool containsVariant) {\n                this.Pattern = new Regex(pattern, RegexOptions.Singleline | RegexOptions.Compiled);\n                this.Replacement = replacementFormat;\n                this.StainReplacement = replacementStainFormat;\n                this.ContainsVariant = containsVariant;\n            }\n\n            public bool TryExpand(string input, out string path, out string stainedPath) {\n                if (!Pattern.IsMatch(input)) {\n                    path = null;\n                    stainedPath = null;\n                    return false;\n                }\n                path = Pattern.Replace(input, Replacement);\n                if (string.IsNullOrWhiteSpace(StainReplacement))\n                    stainedPath = null;\n                else\n                    stainedPath = Pattern.Replace(input, StainReplacement);\n                return true;\n            }\n        }\n\n        static PathExpander[] PathExpanders = new PathExpander[] {\n            new PathExpander(\n                @\"^/(?<basename>mt_c(?<c>[0-9]{4})a(?<a>[0-9]{4})_(?<suffix>[^\\.]+))\\.mtrl$\",\n                @\"chara/accessory/a${a}/material/v{0:D4}/${basename}.mtrl\",\n                @\"chara/accessory/a${a}/material/v{0:D4}/staining/${basename}_s{1:D4}.mtrl\",\n                true),\n            new PathExpander(\n                @\"^/(?<basename>mt_c(?<c>[0-9]{4})b(?<b>[0-9]{4})_(?<suffix>[^\\.]+))\\.mtrl$\",\n                @\"chara/human/c${c}/obj/body/b${b}/material/${basename}.mtrl\",\n                null,\n                false),\n            new PathExpander(\n                @\"^/(?<basename>mt_c(?<c>[0-9]{4})h(?<h>[0-9]{4})_(?<suffix>[^\\.]+))\\.mtrl$\",\n                @\"chara/human/c${c}/obj/hair/h${h}/material/v{0:D4}/${basename}.mtrl\",\n                null,\n                true),\n            new PathExpander(\n                @\"^/(?<basename>mt_c(?<c>[0-9]{4})f(?<f>[0-9]{4})_(?<suffix>[^\\.]+))\\.mtrl$\",\n                @\"chara/human/c${c}/obj/face/f${f}/material/${basename}.mtrl\",\n                null,\n                false),\n            new PathExpander(\n                @\"^/(?<basename>mt_c(?<c>[0-9]{4})t(?<t>[0-9]{4})_(?<suffix>[^\\.]+))\\.mtrl$\",\n                @\"chara/human/c${c}/obj/tail/t${t}/material/${basename}.mtrl\",\n                null,\n                false),\n            new PathExpander(\n                @\"^/(?<basename>mt_c(?<c>[0-9]{4})e(?<e>[0-9]{4})_(?<suffix>[^\\.]+))\\.mtrl$\",\n                @\"chara/equipment/e${e}/material/v{0:D4}/${basename}.mtrl\",\n                @\"chara/equipment/e${e}/material/v{0:D4}/staining/${basename}_s{1:D4}.mtrl\",\n                true),\n            new PathExpander(\n                @\"^/(?<basename>mt_d(?<d>[0-9]{4})e(?<e>[0-9]{4})_(?<suffix>[^\\.]+))\\.mtrl$\",\n                @\"chara/demihuman/d${d}/obj/equipment/e${e}/material/v{0:D4}/${basename}.mtrl\",\n                null,\n                true),\n            new PathExpander(\n                @\"^/(?<basename>mt_m(?<m>[0-9]{4})b(?<b>[0-9]{4})_(?<suffix>[^\\.]+))\\.mtrl$\",\n                @\"chara/monster/m${m}/obj/body/b${b}/material/v{0:D4}/${basename}.mtrl\",\n                null,\n                true),\n            new PathExpander(\n                @\"^/(?<basename>mt_w(?<w>[0-9]{4})b(?<b>[0-9]{4})_(?<suffix>[^\\.]+))\\.mtrl$\",\n                @\"chara/weapon/w${w}/obj/body/b${b}/material/v{0:D4}/${basename}.mtrl\",\n                @\"chara/weapon/w${w}/obj/body/b${b}/material/v{0:D4}/staining/${basename}_s{1:D4}.mtrl\",\n                true),\n        };\n\n        static bool TryExpand(string input, out string path, out string stainedPath, out bool containsVariants) {\n            var i = input.LastIndexOf('/');\n            var search = input.Substring(i);\n\n            foreach (var pe in PathExpanders) {\n                if (pe.TryExpand(search, out path, out stainedPath)) {\n                    containsVariants = pe.ContainsVariant;\n                    return true;\n                }\n            }\n            path = null;\n            stainedPath = null;\n            containsVariants = false;\n            return false;\n        }\n        #endregion\n\n        #region Fields\n        private string _DefaultPath;\n        private string _PathFormat;\n        private string _StainedPathFormat;\n        private bool _VariantsAvailable;\n        private IO.PackCollection _Packs;\n        #endregion\n\n        #region Properties\n        public ModelDefinition Definition { get; private set; }\n        public string Name { get { return Definition.MaterialNames[Index]; } }\n        public bool VariantsAvailable { get { return _VariantsAvailable; } }\n        public bool StainsAvailable { get { return _StainedPathFormat != null; } }\n        public int Index { get; private set; }\n        #endregion\n\n        #region Constructor\n        internal MaterialDefinition(ModelDefinition definition, int index) {\n            this.Definition = definition;\n            this.Index = index;\n            _Packs = definition.File.Pack.Collection;\n\n            if (_Packs.FileExists(Name)) {\n                _DefaultPath = Name;\n                _VariantsAvailable = false;\n                _StainedPathFormat = _PathFormat = null;\n            } else {\n                if (!TryExpand(Name, out _PathFormat, out _StainedPathFormat, out _VariantsAvailable))\n                    throw new NotSupportedException();\n\n                if (VariantsAvailable)\n                    _DefaultPath = string.Format(_PathFormat, 0);\n                else\n                    _DefaultPath = _PathFormat;\n            }\n        }\n        #endregion\n\n        #region Get\n        public Material Get(ModelVariantIdentifier variantId) {\n            if (variantId.StainKey.HasValue && StainsAvailable)\n                return Get(variantId.ImcVariant, variantId.StainKey.Value);\n            return Get(variantId.ImcVariant);\n        }\n        public Material Get() {\n            var path = _DefaultPath;\n            return Create(path, ImcVariant.Default);\n        }\n        public Material Get(ImcVariant variant) {\n            var path = _DefaultPath;\n            if (VariantsAvailable)\n                path = string.Format(_PathFormat, variant.Variant & 0xFF);\n            return Create(path, variant);\n        }\n        public Material Get(ImcVariant variant, int stainKey) {\n            if (!StainsAvailable)\n                throw new NotSupportedException();\n\n            var path = string.Format(_StainedPathFormat, variant.Variant & 0xFF, stainKey);\n            return Create(path, variant);\n        }\n\n        private Material Create(string path, ImcVariant variant) {\n            var file = _Packs.GetFile(path);\n            return new Material(this, file, variant);\n        }\n        #endregion\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Graphics/MaterialHeader.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Runtime.InteropServices;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace SaintCoinach.Graphics {\n    public struct MaterialHeader {\n        public byte Unknown1;\n        public byte Unknown2;\n        public byte Unknown3;\n        public byte Unknown4;\n        public ushort FileSize;\n        public ushort DataSetSize;\n        public ushort StringsSize;\n        public ushort ShaderOffset;\n        public byte TextureCount;\n        public byte MapCount;\n        public byte DataSetCount;\n        public byte UnknownSize;\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Graphics/MaterialMetadataHeader.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Runtime.InteropServices;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace SaintCoinach.Graphics {\n    [StructLayout(LayoutKind.Sequential)]\n    public struct MaterialMetadataHeader {\n        public ushort DataSize;     // Bytes at the end\n        public ushort UnknownStruct1Count;\n        public ushort UnknownStruct2Count;\n        public ushort ParameterMappingCount;\n        public ushort Unknown1;\n        public ushort Unknown2;\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Graphics/MaterialTextureParameter.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Runtime.InteropServices;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace SaintCoinach.Graphics {\n    [StructLayout(LayoutKind.Sequential)]\n    public struct MaterialTextureParameter {\n        public uint ParameterId;\n        public ushort Unknown1;\n        public ushort Unknown2;\n        public int TextureIndex;\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Graphics/Mesh.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace SaintCoinach.Graphics {\n    public class Mesh {\n        public const byte BytesPerIndex = 2;   // TODO: 99.999% sure this is constant, but you never know.\n\n        #region Fields\n\n        #endregion\n\n        #region Properties\n        public Model Model { get; private set; }\n        public MeshHeader Header { get { return Model.Definition.MeshHeaders[Index]; } }\n        public VertexFormat VertexFormat { get { return Model.Definition.VertexFormats[Index]; } }\n        public int Index { get; private set; }\n        public Vertex[] Vertices { get; private set; }\n        public ushort[] Indices { get; private set; }\n        public MeshPart[] Parts { get; private set; }\n        public MaterialDefinition Material { get { return Model.Definition.Materials[Header.MaterialIndex]; } }\n        #endregion\n\n        #region Constructor\n        public Mesh(Model model, int index, byte[] vertexBuffer, byte[] indexBuffer) {\n            this.Model = model;\n            this.Index = index;\n\n            this.Parts = new MeshPart[Header.PartCount];\n            for (var i = 0; i < Header.PartCount; ++i)\n                this.Parts[i] = new MeshPart(this, Model.Definition.MeshPartHeaders[Header.PartOffset + i], indexBuffer);\n\n            ReadVertices(vertexBuffer);\n            ReadIndices(indexBuffer);\n        }\n        #endregion\n\n        #region Build\n        private void ReadIndices(byte[] buffer) {\n            var position = Header.IndexBufferOffset * BytesPerIndex;\n            this.Indices = new ushort[Header.IndexCount];\n            for (var i = 0; i < Header.IndexCount; ++i) {\n                this.Indices[i] = BitConverter.ToUInt16(buffer, position);\n                position += BytesPerIndex;\n            }\n        }\n        private void ReadVertices(byte[] buffer) {\n            var header = Header;\n            var format = VertexFormat;\n\n            var offsets = new int[header.VertexBufferCount];\n            for (var oi = 0; oi < offsets.Length; ++oi)\n                offsets[oi] = header.VertexOffsets[oi];\n\n            Vertices = new Vertex[Header.VertexCount];\n            for (var i = 0; i < header.VertexCount; ++i) {\n                Vertices[i] = VertexReader.Read(buffer, format, offsets);\n                \n                for (var oi = 0; oi < offsets.Length; ++oi)\n                    offsets[oi] += header.BytesPerVertexPerBuffer[oi];\n            }\n        }\n        #endregion\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Graphics/MeshHeader.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Runtime.InteropServices;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace SaintCoinach.Graphics {\n    [StructLayout(LayoutKind.Sequential)]\n    public struct MeshHeader {\n        public int VertexCount;\n        public int IndexCount;\n        public short MaterialIndex;\n        public short PartOffset;\n        public short PartCount;\n        public short BoneListIndex;\n        public int IndexBufferOffset;\n        [MarshalAs(UnmanagedType.ByValArray, SizeConst = 3)]\n        public int[] VertexOffsets;\n        [MarshalAs(UnmanagedType.ByValArray, SizeConst = 3)]\n        public byte[] BytesPerVertexPerBuffer;\n        public byte VertexBufferCount;\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Graphics/MeshPart.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace SaintCoinach.Graphics {\n    public class MeshPart {\n        #region Properties\n        public Mesh Mesh { get; private set; }\n        public MeshPartHeader Header { get; private set; }\n        public int IndexOffset { get { return Header.IndexOffset; } }\n        public int IndexCount { get { return Header.IndexCount; } }\n\n        public ModelAttribute[] Attributes { get; private set; }\n        #endregion\n\n        #region Constructor\n        internal MeshPart(Mesh mesh, MeshPartHeader header, byte[] indexBuffer) {\n            this.Mesh = mesh;\n            this.Header = header;\n\n            var attr = new List<ModelAttribute>();\n\n            for (var i = 0; i < Mesh.Model.Definition.Attributes.Length; ++i) {\n                if(((Header.AttributesMask >> i) & 1) == 1)\n                    attr.Add(Mesh.Model.Definition.Attributes[i]);\n            }\n\n            Attributes = attr.ToArray();\n        }\n        #endregion\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Graphics/MeshPartHeader.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Runtime.InteropServices;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace SaintCoinach.Graphics {\n    [StructLayout(LayoutKind.Sequential)]\n    public struct MeshPartHeader {\n        public int IndexOffset;\n        public int IndexCount;\n        public int AttributesMask;\n        public short BoneReferenceOffset; // In ModelStruct8.Bones\n        public short BoneReferenceCount;  // In ModelStruct8.Bones\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Graphics/Model.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace SaintCoinach.Graphics {\n    public class Model {\n        #region Properties\n        public ModelDefinition Definition { get; private set; }\n        public ModelQuality Quality { get; private set; }\n        public ModelHeader Header { get { return Definition.ModelHeaders[(int)Quality]; } }\n        public Mesh[] Meshes { get; private set; }\n        #endregion\n\n        #region Constructor\n        public Model(ModelDefinition definition, ModelQuality quality) {\n            Definition = definition;\n            Quality = quality;\n\n            Build();\n        }\n        #endregion\n\n        #region Build\n        private void Build() {\n            const int VertexPartOffset = 2;\n            const int IndexPartOffset = 8;\n\n            var vertexBuffer = Definition.File.GetPart(VertexPartOffset + (int)Quality);\n            var indexBuffer = Definition.File.GetPart(IndexPartOffset + (int)Quality);\n\n            this.Meshes = new Mesh[Header.MeshCount];\n            for (var i = 0; i < Header.MeshCount; ++i) {\n                var mesh = new Mesh(this, Header.MeshOffset + i, vertexBuffer, indexBuffer);\n\n                Meshes[i] = mesh;\n            }\n        }\n        #endregion\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Graphics/ModelAttribute.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace SaintCoinach.Graphics {\n    public class ModelAttribute {\n        private static Dictionary<string, uint> MaskMap = new Dictionary<string, uint> {\n            // TODO: Not actually confirmed that these are all correct.\n            // tv_* seen on equipment > top\n            { \"atr_tv_a\", 1 << 0 },\n            { \"atr_tv_b\", 1 << 1 },\n            { \"atr_tv_c\", 1 << 2 },\n            { \"atr_tv_d\", 1 << 3 },\n            { \"atr_tv_e\", 1 << 4 },\n            { \"atr_tv_f\", 1 << 5 },\n            { \"atr_tv_g\", 1 << 6 },\n            { \"atr_tv_h\", 1 << 7 },\n            { \"atr_tv_i\", 1 << 8 },\n            { \"atr_tv_j\", 1 << 9 },\n\n            // mv_* seen on equipment > met\n            { \"atr_mv_a\", 1 << 0 },\n            { \"atr_mv_b\", 1 << 1 },\n            { \"atr_mv_c\", 1 << 2 },\n            { \"atr_mv_d\", 1 << 3 },\n            { \"atr_mv_e\", 1 << 4 },\n            { \"atr_mv_f\", 1 << 5 },\n            { \"atr_mv_g\", 1 << 6 },\n            { \"atr_mv_h\", 1 << 7 },\n            { \"atr_mv_i\", 1 << 8 },\n            { \"atr_mv_j\", 1 << 9 },\n\n            // bv_* seen on * > body\n            { \"atr_bv_a\", 1 << 0 },\n            { \"atr_bv_b\", 1 << 1 },\n            { \"atr_bv_c\", 1 << 2 },\n            { \"atr_bv_d\", 1 << 3 },\n            { \"atr_bv_e\", 1 << 4 },\n            { \"atr_bv_f\", 1 << 5 },\n            { \"atr_bv_g\", 1 << 6 },\n            { \"atr_bv_h\", 1 << 7 },\n            { \"atr_bv_i\", 1 << 8 },\n            { \"atr_bv_j\", 1 << 9 },\n\n            // gv_* (probably) on equipment > glv\n            { \"atr_gv_a\", 1 << 0 },\n            { \"atr_gv_b\", 1 << 1 },\n            { \"atr_gv_c\", 1 << 2 },\n            { \"atr_gv_d\", 1 << 3 },\n            { \"atr_gv_e\", 1 << 4 },\n            { \"atr_gv_f\", 1 << 5 },\n            { \"atr_gv_g\", 1 << 6 },\n            { \"atr_gv_h\", 1 << 7 },\n            { \"atr_gv_i\", 1 << 8 },\n            { \"atr_gv_j\", 1 << 9 },\n\n            // dv_* (probably) on equipment > dwn\n            { \"atr_dv_a\", 1 << 0 },\n            { \"atr_dv_b\", 1 << 1 },\n            { \"atr_dv_c\", 1 << 2 },\n            { \"atr_dv_d\", 1 << 3 },\n            { \"atr_dv_e\", 1 << 4 },\n            { \"atr_dv_f\", 1 << 5 },\n            { \"atr_dv_g\", 1 << 6 },\n            { \"atr_dv_h\", 1 << 7 },\n            { \"atr_dv_i\", 1 << 8 },\n            { \"atr_dv_j\", 1 << 9 },\n            \n            // sv_* (probably) on equipment > sho\n            { \"atr_sv_a\", 1 << 0 },\n            { \"atr_sv_b\", 1 << 1 },\n            { \"atr_sv_c\", 1 << 2 },\n            { \"atr_sv_d\", 1 << 3 },\n            { \"atr_sv_e\", 1 << 4 },\n            { \"atr_sv_f\", 1 << 5 },\n            { \"atr_sv_g\", 1 << 6 },\n            { \"atr_sv_h\", 1 << 7 },\n            { \"atr_sv_i\", 1 << 8 },\n            { \"atr_sv_j\", 1 << 9 },\n        };\n\n        #region Properties\n        public ModelDefinition Definition { get; private set; }\n        public string Name { get; private set; }\n        public int Index { get; private set; }\n        public uint AttributeMask { get; private set; }\n        #endregion\n\n        #region Constructor\n        public ModelAttribute(ModelDefinition definition, int index) {\n            this.Definition = definition;\n            this.Index = index;\n            this.Name = definition.AttributeNames[index];\n\n            if (MaskMap.ContainsKey(Name))\n                AttributeMask = MaskMap[Name];\n            else\n                AttributeMask = 0;\n        }\n        #endregion\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Graphics/ModelBoundingBoxes.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Runtime.InteropServices;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace SaintCoinach.Graphics {\n    [StructLayout(LayoutKind.Sequential)]\n    public struct ModelBoundingBoxes {  // 9 in hsl\n        // TODO: Purpose of having 4 boxes (or 8 vectors) is unknown.\n        public BoundingBox Value1;\n        public BoundingBox Value2;\n        public BoundingBox Value3;\n        public BoundingBox Value4;\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Graphics/ModelDefinition.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace SaintCoinach.Graphics {\n    public class ModelDefinition {\n        public const int StringsCountOffset = 0x00;\n        public const int StringsSizeOffset = 0x04;\n        public const int StringsOffset = 0x08;\n\n        public const int ModelCount = 3;\n\n        #region Fields\n        internal string[] MaterialNames;\n        internal string[] AttributeNames;\n        private Model[] _Models = new Model[ModelCount];\n        #endregion\n\n        #region Properties\n        public IEnumerable<ModelQuality> AvailableQualities { get; private set; }\n        public ModelDefinitionHeader Header { get; private set; }\n        public ModelFile File { get; private set; }\n        public VertexFormat[] VertexFormats { get; private set; }\n        public Unknowns.ModelStruct1[] UnknownStructs1 { get; private set; }\n        public ModelHeader[] ModelHeaders { get; private set; }\n        public MeshHeader[] MeshHeaders { get; private set; }\n        public ModelAttribute[] Attributes { get; private set; }\n        public Unknowns.ModelStruct2[] UnknownStructs2 { get; private set; }\n        public MeshPartHeader[] MeshPartHeaders { get; private set; }\n        public Unknowns.ModelStruct3[] UnknownStructs3 { get; private set; }\n        public MaterialDefinition[] Materials { get; private set; }\n        public string[] BoneNames { get; private set; }\n        public Unknowns.BoneList[] BoneLists { get; private set; }\n        public Unknowns.ModelStruct5[] UnknownStructs5 { get; private set; }\n        public Unknowns.ModelStruct6[] UnknownStructs6 { get; private set; }\n        public Unknowns.ModelStruct7[] UnknownStructs7 { get; private set; }\n        public Unknowns.BoneIndices BoneIndices { get; private set; }\n        // Here's padding, but not keeping a variable amount of 0s\n        public ModelBoundingBoxes BoundingBoxes { get; private set; }\n        public Bone[] Bones { get; private set; }\n        #endregion\n\n        #region Constructor\n        public ModelDefinition(ModelFile file) {\n            File = file;\n\n            Build();\n        }\n        #endregion\n\n        #region Get\n        public Model GetModel(int quality) { return GetModel((ModelQuality)quality); }\n        public Model GetModel(ModelQuality quality) {\n            var v = (int)quality;\n            if (_Models[v] == null)\n                _Models[v] = new Model(this, quality);\n            return _Models[v];\n        }\n        #endregion\n\n        #region Build\n        private void Build() {\n            const int DefinitionPart = 1;\n\n            // osg and bil_*_base.mdl workaround\n            // These models contain an extra 120 bytes after model headers\n            bool isOsg = File.Path.Contains(\"/osg_\");\n\n            var buffer = File.GetPart(DefinitionPart);\n            var stringsSize = BitConverter.ToInt32(buffer, StringsSizeOffset);\n\n            var offset = StringsOffset + stringsSize;    // Skipping those, they'll be read further along the road\n\n            this.Header = buffer.ToStructure<ModelDefinitionHeader>(ref offset);\n            this.UnknownStructs1 = buffer.ToStructures<Unknowns.ModelStruct1>(Header.UnknownStruct1Count, ref offset);\n            this.ModelHeaders = buffer.ToStructures<ModelHeader>(ModelCount, ref offset);\n\n            // Skip 120 bytes after model headers\n            if (isOsg)\n                offset += 120;\n\n            var availableQualities = new List<ModelQuality>();\n            for (var i = 0; i < this.ModelHeaders.Length; ++i) {\n                if (this.ModelHeaders[i].MeshCount > 0)\n                    availableQualities.Add((ModelQuality)i);\n            }\n            this.AvailableQualities = availableQualities;\n\n            this.MeshHeaders = buffer.ToStructures<MeshHeader>(Header.MeshCount, ref offset);\n\n            this.AttributeNames = ReadStrings(buffer, Header.AttributeCount, ref offset);\n            this.Attributes = new ModelAttribute[Header.AttributeCount];\n            for (var i = 0; i < Header.AttributeCount; ++i)\n                this.Attributes[i] = new ModelAttribute(this, i);\n\n            this.UnknownStructs2 = buffer.ToStructures<Unknowns.ModelStruct2>(Header.UnknownStruct2Count, ref offset);\n            this.MeshPartHeaders = buffer.ToStructures<MeshPartHeader>(Header.PartCount, ref offset);\n            this.UnknownStructs3 = buffer.ToStructures<Unknowns.ModelStruct3>(Header.UnknownStruct3Count, ref offset);\n\n            this.MaterialNames = ReadStrings(buffer, Header.MaterialCount, ref offset);\n            this.Materials = new MaterialDefinition[Header.MaterialCount];\n            for (var i = 0; i < Header.MaterialCount; ++i)\n                this.Materials[i] = new MaterialDefinition(this, i);\n\n\n            this.BoneNames = ReadStrings(buffer, Header.BoneCount, ref offset);\n            this.BoneLists = buffer.ToStructures<Unknowns.BoneList>(Header.UnknownStruct4Count, ref offset);\n            this.UnknownStructs5 = buffer.ToStructures<Unknowns.ModelStruct5>(Header.UnknownStruct5Count, ref offset);\n            this.UnknownStructs6 = buffer.ToStructures<Unknowns.ModelStruct6>(Header.UnknownStruct6Count, ref offset);\n            this.UnknownStructs7 = buffer.ToStructures<Unknowns.ModelStruct7>(Header.UnknownStruct7Count, ref offset);\n            this.BoneIndices = new Unknowns.BoneIndices(buffer, ref offset);\n\n            offset += buffer[offset] + 1;   // Just padding, first byte specifying how many 0-bytes follow.\n\n            this.BoundingBoxes = buffer.ToStructure<ModelBoundingBoxes>(ref offset);\n\n            this.Bones = new Bone[Header.BoneCount];\n            for (var i = 0; i < Header.BoneCount; ++i)\n                this.Bones[i] = new Bone(this, i, buffer, ref offset);\n\n            if (offset != buffer.Length) {\n                //System.Diagnostics.Debugger.Break();    // Something's not right here.\n            }\n\n            BuildVertexFormats();\n        }\n        private void BuildVertexFormats() {\n            const int FormatPart = 0;\n\n            var buffer = File.GetPart(FormatPart);\n\n            this.VertexFormats = new VertexFormat[Header.MeshCount];\n            var offset = 0;\n            for (var i = 0; i < Header.MeshCount; ++i)\n                this.VertexFormats[i] = new VertexFormat(buffer, ref offset);\n        }\n        private static string[] ReadStrings(byte[] buffer, int count, ref int offset) {\n            var values = new string[count];\n            for (var i = 0; i < count; ++i) {\n                var stringOffset = BitConverter.ToInt32(buffer, offset);\n                values[i] = buffer.ReadString(StringsOffset + stringOffset);\n                offset += 4;\n            }\n            return values;\n        }\n        #endregion\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Graphics/ModelDefinitionHeader.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Runtime.InteropServices;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace SaintCoinach.Graphics {\n    [StructLayout(LayoutKind.Sequential)]\n    public struct ModelDefinitionHeader {\n        public uint Unknown1;\n        public ushort MeshCount;\n        public ushort AttributeCount;\n        public ushort PartCount;\n        public ushort MaterialCount;\n        public ushort BoneCount;\n        public ushort UnknownStruct4Count;  // 3 in hsl\n        public ushort UnknownStruct5Count;  // 4 in hsl\n        public ushort UnknownStruct6Count;  // 5 in hsl\n        public ushort UnknownStruct7Count;  // 6 in hsl\n        public ushort Unknown2;\n        public ushort UnknownStruct1Count;  // 0 in hsl\n        public byte UnknownStruct2Count;  // 1 in hsl\n        public byte Unknown3;\n        [MarshalAs(UnmanagedType.ByValArray, SizeConst = 5)]\n        public ushort[] Unknown4;\n        public ushort UnknownStruct3Count;  // 7 in hsl\n        [MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)]\n        public ushort[] Unknown5;\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Graphics/ModelFile.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.IO;\n\nusing SaintCoinach.IO;\n\nusing Directory = SaintCoinach.IO.Directory;\nusing File = SaintCoinach.IO.File;\n\nnamespace SaintCoinach.Graphics {\n    /// <summary>\n    ///     Model file stored inside SqPack.\n    /// </summary>\n    public class ModelFile : File {\n        #region Static\n\n        public const int PartsCount = 0x0B;\n\n        #endregion\n\n        #region Fields\n\n        private readonly WeakReference<byte[]>[] _PartsCache = new WeakReference<byte[]>[PartsCount];\n        private int[] _BlockOffsets;\n        private WeakReference<byte[]> _CombinedCache;\n        private WeakReference<ModelDefinition> _DefinitionCache;\n\n        #endregion\n\n        #region Constructors\n\n        public ModelFile(Pack pack, FileCommonHeader commonHeader) : base(pack, commonHeader) { }\n\n        #endregion\n\n        #region Read\n\n        public ModelDefinition GetModelDefinition() {\n            ModelDefinition def;\n            if (_DefinitionCache != null && _DefinitionCache.TryGetTarget(out def))\n                return def;\n\n            def = new ModelDefinition(this);\n            if (_DefinitionCache == null)\n                _DefinitionCache = new WeakReference<ModelDefinition>(def);\n            else\n                _DefinitionCache.SetTarget(def);\n\n            return def;\n        }\n\n        public override byte[] GetData() {\n            byte[] data;\n\n            if (_CombinedCache != null && _CombinedCache.TryGetTarget(out data)) return data;\n\n            data = new byte[0];\n            var o = 0;\n\n            for (var i = 0; i < PartsCount; ++i) {\n                var part = GetPart(i);\n                Array.Resize(ref data, data.Length + part.Length);\n                Array.Copy(part, 0, data, o, part.Length);\n                o += part.Length;\n            }\n\n            if (_CombinedCache == null)\n                _CombinedCache = new WeakReference<byte[]>(data);\n            else\n                _CombinedCache.SetTarget(data);\n\n            return data;\n        }\n\n        public byte[] GetPart(int part) {\n            if (part >= PartsCount)\n                throw new ArgumentOutOfRangeException(\"part\");\n\n            byte[] buffer;\n\n            if (_PartsCache[part] != null && _PartsCache[part].TryGetTarget(out buffer)) return buffer;\n\n            buffer = ReadPart(part);\n\n            if (_PartsCache[part] == null)\n                _PartsCache[part] = new WeakReference<byte[]>(buffer);\n            else\n                _PartsCache[part].SetTarget(buffer);\n\n            return buffer;\n        }\n\n        private byte[] ReadPart(int part) {\n            const int MinimumBlockOffset = 0x9C;\n            const int BlockCountOffset = 0xB2;\n\n            if (_BlockOffsets == null)\n                _BlockOffsets = GetBlockOffsets();\n\n            var minBlock = BitConverter.ToInt16(CommonHeader._Buffer, MinimumBlockOffset + 2 * part);\n            var blockCount = BitConverter.ToInt16(CommonHeader._Buffer, BlockCountOffset + 2 * part);\n\n            var sourceStream = GetSourceStream();\n            byte[] buffer;\n\n            using (var dataStream = new MemoryStream()) {\n                for (var i = 0; i < blockCount; ++i) {\n                    sourceStream.Position = CommonHeader.EndOfHeader + _BlockOffsets[minBlock + i];\n                    ReadBlock(sourceStream, dataStream);\n                }\n\n                buffer = dataStream.ToArray();\n            }\n\n            return buffer;\n        }\n\n        private int[] GetBlockOffsets() {\n            const int BlockInfoOffset = 0xD0;\n\n            var currentOffset = 0;\n            var offsets = new List<int>();\n            for (var i = BlockInfoOffset; i + 2 <= CommonHeader._Buffer.Length; i += 2) {\n                var len = BitConverter.ToUInt16(CommonHeader._Buffer, i);\n                if (len == 0)\n                    break;\n                offsets.Add(currentOffset);\n                currentOffset += len;\n            }\n\n            return offsets.ToArray();\n        }\n\n        #endregion\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Graphics/ModelHeader.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Runtime.InteropServices;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace SaintCoinach.Graphics {\n    [StructLayout(LayoutKind.Sequential)]\n    public struct ModelHeader {\n        public short MeshOffset;\n        public short MeshCount;\n        [MarshalAs(UnmanagedType.ByValArray, SizeConst = 0x14)]\n        public ushort[] Unknown1;\n        public int VertexDataSize;\n        public int IndexDataSize;\n        [MarshalAs(UnmanagedType.ByValArray, SizeConst = 0x04)]\n        public ushort[] Unknown2;\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Graphics/ModelQuality.cs",
    "content": "﻿namespace SaintCoinach.Graphics {\n    public enum ModelQuality : int {\n        High = 0,\n        Medium = 1,\n        Low = 2\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Graphics/ModelVariantIdentifier.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace SaintCoinach.Graphics {\n    public struct ModelVariantIdentifier {\n        public ImcVariant ImcVariant;\n        public int? StainKey;\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Graphics/PapAnimation.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace SaintCoinach.Graphics {\n    public class PapAnimation {\n        #region Properties\n        public PapFile Parent { get; private set; }\n        public string Name { get; private set; }\n        public short Unknown20 { get; private set; }\n        public int Index { get; private set; }\n        public short Unknown26 { get; private set; }\n        #endregion\n\n        #region Constructor\n        internal PapAnimation(PapFile file, byte[] buffer, ref int offset) {\n            Parent = file;\n            Name = buffer.ReadString(offset);\n\n            offset += 0x20;\n            Unknown20 = BitConverter.ToInt16(buffer, offset);\n\n            offset += 0x02;\n            Index = BitConverter.ToInt32(buffer, offset);\n\n            offset += 0x04;\n            Unknown26 = BitConverter.ToInt16(buffer, offset);\n\n            offset += 0x02;\n        }\n        #endregion\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Graphics/PapFile.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Runtime.InteropServices;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace SaintCoinach.Graphics {\n    public class PapFile {\n        [StructLayout(LayoutKind.Sequential, Pack = 1)]\n        public struct HeaderData {\n            public uint Magic;\n            public short Unknown1;\n            public short Unknown2;\n            public short AnimationCount;\n            public short Unknown3;\n            public short Unknown4;\n            public short Unknown5;\n            public short Unknown6;\n            public int HavokDataOffset;\n            public int ParametersOffset;\n        }\n\n        #region Properties\n        public IO.File File { get; private set; }\n        public HeaderData Header { get; private set; }\n        public PapAnimation[] Animations { get; private set; }\n        public byte[] HavokData { get; private set; }\n        public byte[] Parameters { get; private set; }\n        #endregion\n\n        #region Constructor\n        public PapFile(IO.File file) {\n            this.File = file;\n\n            Build();\n        }\n        #endregion\n\n        #region Build\n        private void Build() {\n            var buffer = File.GetData();\n\n            var offset = 0;\n            Header = buffer.ToStructure<HeaderData>(ref offset);\n            if (Header.Magic != 0x20706170)\n                throw new System.IO.InvalidDataException();\n\n            Animations = new PapAnimation[Header.AnimationCount];\n            for (var i = 0; i < Header.AnimationCount; ++i)\n                Animations[i] = new PapAnimation(this, buffer, ref offset);\n\n            HavokData = new byte[Header.ParametersOffset - Header.HavokDataOffset];\n            Array.Copy(buffer, Header.HavokDataOffset, HavokData, 0, HavokData.Length);\n\n            Parameters = new byte[buffer.Length - Header.ParametersOffset];\n            Array.Copy(buffer, Header.ParametersOffset, Parameters, 0, Parameters.Length);\n        }\n        #endregion\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Graphics/Pcb/IPcbBlockData.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace SaintCoinach.Graphics.Pcb {\n    public interface IPcbBlockData {\n\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Graphics/Pcb/PcbBlockData.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace SaintCoinach.Graphics.Pcb {\n    public class PcbBlockData {\n        public struct VertexI16 {\n            ushort X;\n            ushort Y;\n            ushort Z;\n        };\n        public struct IndexData {\n            byte Index1;\n            byte Index2;\n            byte Index3;\n            byte Unknown1;\n            byte Unknown2;\n            byte Unknown3;\n            byte Unknown4;\n            byte Unknown5;\n            byte Unknown6;\n            byte Unknown7;\n            byte Unknown8;\n            byte Unknown9;\n        };\n\n        public Vector3[] Vertices;\n        public VertexI16[] VerticesI16;\n        public IndexData[] Indices;\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Graphics/Pcb/PcbBlockDataType.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace SaintCoinach.Graphics.Pcb {\n    public enum PcbBlockDataType {\n        Entry = 0,\n        Group = 0x30\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Graphics/Pcb/PcbBlockEntry.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Runtime.InteropServices;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace SaintCoinach.Graphics.Pcb {\n    public class PcbBlockEntry {\n        #region Struct\n        [StructLayout(LayoutKind.Sequential)]\n        public struct HeaderData\n        {\n            public PcbBlockDataType Type;       // 0 for entry, 0x30 for group\n            public uint BlockSize;       // when group size in bytes for the group block\n            public Vector3 Min;          // bounding box\n            public Vector3 Max;\n            public ushort VerticesI16Count; // number of vertices packed into 16 bit\n            public ushort IndicesCount;  // number of indices\n            public uint VerticesCount;   // number of normal float vertices\n        }\n        #endregion\n\n        #region Properties\n        public HeaderData Header { get; private set; }\n        public PcbFile Parent { get; private set; }\n        public PcbBlockData Data { get; private set; }\n        public int Offset { get; private set; }\n        #endregion\n\n        #region Constructor\n        public PcbBlockEntry(PcbFile parent, byte[] buffer, int offset)\n        {\n            this.Parent = parent;\n            Offset = offset;\n            this.Header = buffer.ToStructure<HeaderData>(ref offset);\n            this.Data = new PcbBlockData();\n            Build(buffer);\n        }\n        #endregion\n        #region Functions\n        private void ParseBlockEntry(byte[] buffer, int offset, int globalOffset) {\n\n        }\n        #endregion\n        #region Build\n        private void Build(byte[] buffer)\n        {\n\n        }\n        #endregion\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Graphics/Pcb/PcbFile.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Runtime.InteropServices;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace SaintCoinach.Graphics.Pcb\n{\n    // TODO: This class should be reworked and checked for errors - the problems they cause with certain PCBs are meanwhile caught in LgbModelEntry L55+\n    public class PcbFile\n    {\n        #region Struct\n        [StructLayout(LayoutKind.Sequential)]\n        public struct HeaderData {\n            public uint Unknown1;\n            public uint Unknown2;\n            public int EntryCount;\n            public int IndicesCount;\n            public ulong Padding;\n        }\n        #endregion\n\n        #region Properties\n        public HeaderData Header { get; private set; }\n        public IO.File File { get; private set; }\n        public List<PcbBlockEntry> Data { get; private set;}\n        #endregion\n\n        #region Constructor\n        public PcbFile(IO.File file)\n        {\n            this.File = file;\n\n            // todo: fix this\n            //Build();\n        }\n        #endregion\n\n        #region Build\n        private void Build()\n        {\n            var buffer = File.GetData();\n            int entryOffset = 0x18;\n\n            this.Header = buffer.ToStructure<HeaderData>(0);\n            Data = new List<PcbBlockEntry>(Header.EntryCount);\n            bool isGroup = true;\n            while (isGroup) {\n                PcbBlockEntry entry = new PcbBlockEntry(this, buffer, entryOffset);\n\n                if (isGroup = (entry.Header.Type == PcbBlockDataType.Group)) {\n                    ParsePcbBlockEntry(buffer, entryOffset + 0x30, entryOffset);\n                    entryOffset += (int)entry.Header.BlockSize;\n                }\n                else {\n                    ParsePcbBlockEntry(buffer, entryOffset, entryOffset);\n                }\n            }\n        }\n        #endregion\n\n        #region Functions\n        private void ParsePcbBlockEntry(byte[] buffer, int entryOffset, int globalOffset) {\n            int offset = 0;\n            bool isGroup = true;\n            while (isGroup) {\n                PcbBlockEntry entry = new PcbBlockEntry(this, buffer, entryOffset);\n\n                if (isGroup = (entry.Header.Type == PcbBlockDataType.Group)) {\n                    ParsePcbBlockEntry(buffer, entryOffset + offset + 0x30, globalOffset + offset + entryOffset);\n                    offset += (int)entry.Header.BlockSize;\n                }\n                else {\n                    var dOffset = Marshal.SizeOf<PcbBlockEntry.HeaderData>() + offset;\n                    var blockSize = Marshal.SizeOf<PcbBlockEntry.HeaderData>() +\n                        entry.Header.VerticesCount * 3 * 4 +\n                        entry.Header.VerticesI16Count * 3 * 2 +\n                        entry.Header.IndicesCount * 3 * 2;\n\n                    if (entry.Header.VerticesCount != 0) {\n                        entry.Data.Vertices = new Vector3[entry.Header.VerticesCount];\n                        for (var i = 0; i < entry.Header.VerticesCount; ++i) {\n                            entry.Data.Vertices[i] = buffer.ToStructure<Vector3>(ref dOffset);\n                        }\n                    }\n                    if (entry.Header.VerticesI16Count != 0) {\n                        entry.Data.VerticesI16 = new PcbBlockData.VertexI16[entry.Header.VerticesI16Count];\n                        for (var i = 0; i < entry.Header.VerticesI16Count; ++i) {\n                            entry.Data.VerticesI16[i] = buffer.ToStructure<PcbBlockData.VertexI16>(ref dOffset);\n                        }\n                    }\n                    if (entry.Header.IndicesCount != 0) {\n                        entry.Data.Indices = new PcbBlockData.IndexData[entry.Header.IndicesCount];\n                        for (var i = 0; i < entry.Header.IndicesCount; ++i) {\n                            entry.Data.Indices[i] = buffer.ToStructure<PcbBlockData.IndexData>(ref dOffset);\n                        }\n                    }\n                    Data.Add(entry);\n                }\n            }\n        }\n        #endregion\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Graphics/Sgb/ISgbData.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace SaintCoinach.Graphics.Sgb {\n    public interface ISgbData {\n        SgbDataType Type { get; }\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Graphics/Sgb/ISgbGroupEntry.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace SaintCoinach.Graphics.Sgb {\n    public interface ISgbGroupEntry {\n        SgbGroupEntryType Type { get; }\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Graphics/Sgb/SgbDataType.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace SaintCoinach.Graphics.Sgb {\n    public enum SgbDataType : int {\n        Unknown0008 = 0x0008,\n        Group = 0x0100,\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Graphics/Sgb/SgbFile.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Runtime.InteropServices;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace SaintCoinach.Graphics.Sgb {\n    public class SgbFile {\n        #region Struct\n        [StructLayout(LayoutKind.Sequential)]\n        public struct HeaderData {\n            public uint Magic1;     // SGB1\n            public uint FileSize;\n            public uint Unknown1;\n            public uint Magic2;     // SCN1\n\n            public uint Unknown10;\n            public int SharedOffset;\n            public uint Unknown18;\n            public int Offset1C;\n\n            public uint Unknown20;\n            public int StatesOffset;\n            public uint Unknown28;\n            public uint Unknown2C;\n\n            public uint Unknown30;\n            public uint Unknown34;\n            public uint Unknown38;\n            public uint Unknown3C;\n\n            public uint Unknown40;\n            public uint Unknown44;\n            public uint Unknown48;\n            public uint Unknown4C;\n\n            public uint Unknown50;\n            public uint Unknown54;\n        }\n        #endregion\n\n        #region Properties\n        public HeaderData Header { get; private set; }\n        public IO.File File { get; private set; }\n        public ISgbData[] Data { get; private set; }\n        #endregion\n\n        #region Constructor\n        public SgbFile(IO.File file) {\n            this.File = file;\n\n            Build();\n        }\n        #endregion\n\n        #region Build\n        private void Build() {\n            const int BaseOffset = 0x14;\n\n            var buffer = File.GetData();\n            \n            this.Header = buffer.ToStructure<HeaderData>(0);\n            if (Header.Magic1 != 0x31424753 || Header.Magic2 != 0x314E4353)     // LGB1 & SCN1\n                throw new System.IO.InvalidDataException();\n\n            var data = new List<ISgbData>();\n\n            try {\n                data.Add(new SgbGroup(this, buffer, BaseOffset + Header.SharedOffset));\n                data.Add(new SgbGroup(this, buffer, BaseOffset + Header.Offset1C, true));\n                //data.Add(new SgbGroup(this, buffer, BaseOffset + Header.Unknown1));\n            }\n            catch (Exception ex) {\n                System.Diagnostics.Debug.WriteLine(ex.Message);\n            }\n\n            this.Data = data.ToArray();\n        }\n        #endregion\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Graphics/Sgb/SgbGimmickEntry.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Runtime.InteropServices;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace SaintCoinach.Graphics.Sgb {\n\n    public class SgbGimmickEntry : ISgbGroupEntry {\n        #region Struct\n        [StructLayout(LayoutKind.Sequential)]\n        public struct HeaderData {\n            public SgbGroupEntryType Type;\n            public uint GimmickId;\n            public int NameOffset;\n            public Vector3 Translation;\n            public Vector3 Rotation;\n            public Vector3 Scale;\n            public int GimmickFileOffset;\n            public int CollisionFileOffset;\n            // SgbGimmickEntry size is around 152 bytes?\n        }\n        #endregion\n\n        #region Properties\n        SgbGroupEntryType ISgbGroupEntry.Type { get { return Header.Type; } }\n        public HeaderData Header { get; private set; }\n        public string Name { get; private set; }\n        public SgbFile Gimmick { get; private set; }\n        #endregion\n\n        #region Constructor\n        public SgbGimmickEntry(IO.PackCollection packs, byte[] buffer, int offset) {\n            this.Header = buffer.ToStructure<HeaderData>(offset);\n            this.Name = buffer.ReadString(offset + Header.NameOffset);\n\n            var sgbFileName = buffer.ReadString(offset + Header.GimmickFileOffset);\n            if (!string.IsNullOrWhiteSpace(sgbFileName)) {\n                SaintCoinach.IO.File file;\n                if (packs.TryGetFile(sgbFileName, out file))\n                    this.Gimmick = new SgbFile(file);\n            }\n        }\n        #endregion\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Graphics/Sgb/SgbGroup.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Runtime.InteropServices;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace SaintCoinach.Graphics.Sgb {\n    public class SgbGroup : ISgbData {\n        #region Struct\n        [StructLayout(LayoutKind.Sequential)]\n        public struct HeaderData {\n            public SgbDataType Type;\n            public int NameOffset;\n            public uint Unknown08;\n            public uint Unknown0C;\n\n            public uint Unknown10;\n            public uint Unknown14;\n            public uint Unknown18;\n            public uint Unknown1C;\n\n            public int EntryCount;\n            public uint Unknown24;\n            public uint Unknown28;\n            public uint Unknown2C;\n\n            public uint Unknown30;\n            public uint Unknown34;\n            public uint Unknown38;\n            public uint Unknown3C;\n\n            public uint Unknown40;\n            public uint Unknown44;\n        }\n\n        [StructLayout(LayoutKind.Sequential)]\n        public struct Offset1CHeaderData {\n            public SgbDataType Type;\n            public int NameOffset;\n            public uint Unknown08;\n\n            public int EntryCount;\n            public uint Unknown14;\n            public int ModelFileOffset;\n            public Vector3 UnknownFloat3;\n            public Vector3 UnknownFloat3_2;\n            public int StateOffset;\n            public int ModelFileOffset2;\n            public uint Unknown3;\n            public float Unknown4;\n            public int NameOffset2;\n            public Vector3 UnknownFloat3_3;\n        }\n        #endregion\n\n        #region Properties\n        public HeaderData Header { get; private set; }\n        public Offset1CHeaderData Offset1CHeader { get; private set; }\n        SgbDataType ISgbData.Type { get { return Header.Type; } }\n        public string Name, ModelFile1, ModelFile2, ModelFile3;// { get; private set; }\n        public List<string> States;\n        public SgbFile Parent { get; private set; }\n        public ISgbGroupEntry[] Entries { get; private set; }\n        #endregion\n\n        #region Constructor\n        \n        public SgbGroup(SgbFile parent, byte[] buffer, int offset, bool isOffset1C = false) {\n            this.Parent = parent;\n\n            var entriesOffset = offset;\n            int count = 0;\n            if (isOffset1C) {\n                this.Header = buffer.ToStructure<HeaderData>(entriesOffset);\n                this.Offset1CHeader = buffer.ToStructure<Offset1CHeaderData>(ref entriesOffset);\n                this.Name = buffer.ReadString(offset + Offset1CHeader.NameOffset);\n                this.ModelFile1 = buffer.ReadString(offset + Offset1CHeader.ModelFileOffset + 1);\n                this.ModelFile2 = buffer.ReadString(offset + Offset1CHeader.ModelFileOffset2 + 1);\n                this.ModelFile3 = buffer.ReadString(offset + Offset1CHeader.NameOffset2 + 64);\n\n                count = Offset1CHeader.EntryCount;\n                Entries = new ISgbGroupEntry[count];\n\n                for (var i = 0; i < count; ++i) {\n                    try {\n                        var entryOffset = entriesOffset + (i * 24);\n                        Entries[i] = new SgbGroup1CEntry(parent.File.Pack.Collection, buffer, entryOffset);\n                        break;\n                    }\n                    catch (Exception e) {\n                        System.Diagnostics.Debug.WriteLine(e.Message);\n                    }\n                }\n            }\n            else {\n                this.Header = buffer.ToStructure<HeaderData>(ref entriesOffset);\n                this.Name = buffer.ReadString(offset + Header.NameOffset);\n                count = Header.EntryCount;\n                Entries = new ISgbGroupEntry[count];\n\n                for (var i = 0; i < count; ++i) {\n                    try {\n                        var entryOffset = entriesOffset + BitConverter.ToInt32(buffer, entriesOffset + i * 4);\n\n                        var type = (SgbGroupEntryType)BitConverter.ToInt32(buffer, entryOffset);\n                        var typeStr = ((Lgb.LgbEntryType)type).ToString();\n                        switch (type) {\n                            case SgbGroupEntryType.Model:\n                                Entries[i] = new SgbModelEntry(Parent.File.Pack.Collection, buffer, entryOffset);\n                                break;\n                            case SgbGroupEntryType.Gimmick:\n                                Entries[i] = new SgbGimmickEntry(Parent.File.Pack.Collection, buffer, entryOffset);\n                                break;\n                            case SgbGroupEntryType.Light:\n                                Entries[i] = new SgbLightEntry(Parent.File.Pack.Collection, buffer, entryOffset);\n                                break;\n                            case SgbGroupEntryType.Vfx:\n                                Entries[i] = new SgbVfxEntry(Parent.File.Pack.Collection, buffer, entryOffset);\n                                break;\n                            default:\n                                //System.Diagnostics.Trace.WriteLine(string.Format(\"{0}: Type {1} at 0x{2:X} in {3}\", Parent.File.Path, type, entryOffset, Name));\n                                break;\n                                // TODO: Work out other parts.\n                        }\n                    }\n                    catch (Exception e) {\n                        System.Diagnostics.Debug.WriteLine(e.Message);\n                    }\n                }\n            }\n        }\n        #endregion\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Graphics/Sgb/SgbGroup1CEntry.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Runtime.InteropServices;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace SaintCoinach.Graphics.Sgb {\n\n    public class SgbGroup1CEntry : ISgbGroupEntry {\n        [StructLayout(LayoutKind.Sequential)]\n        public struct HeaderData {\n            public uint Unk;\n            public uint Unk2;\n            public int NameOffset;\n            public uint Index;\n            public uint Unk3;\n            public int ModelFileOffset;\n        }\n\n        SgbGroupEntryType ISgbGroupEntry.Type { get { return (SgbGroupEntryType)0xFF; } }\n        public HeaderData Header { get; private set; }\n        public string Name { get; private set; }\n        public Model Model { get; private set; }\n        public Sgb.SgbFile Gimmick { get; private set; }\n\n        public SgbGroup1CEntry(IO.PackCollection packs, byte[] buffer, int offset) {\n            this.Header = buffer.ToStructure<HeaderData>(offset);\n            this.Name = buffer.ReadString(offset + Header.NameOffset + 9);\n\n            var mdlFilePath = buffer.ReadString(offset + Header.ModelFileOffset + 9);\n            if (!string.IsNullOrWhiteSpace(mdlFilePath)) {\n                SaintCoinach.IO.File file;\n                if (mdlFilePath.Contains(\".mdl\")) {\n                    if (packs.TryGetFile(mdlFilePath, out file))\n                        this.Model = ((Graphics.ModelFile)file).GetModelDefinition().GetModel(ModelQuality.High);\n                }\n                else if (mdlFilePath.Contains(\".sgb\")) {\n                    if (packs.TryGetFile(mdlFilePath, out file))\n                        this.Gimmick = new Sgb.SgbFile(file);\n                }\n            }\n        }\n    };\n}\n"
  },
  {
    "path": "SaintCoinach/Graphics/Sgb/SgbGroupEntryType.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace SaintCoinach.Graphics.Sgb {\n    public enum SgbGroupEntryType : int {\n        Model = 0x01,\n        Light = 0x03,\n        Vfx = 0x04,\n        Gimmick = 0x06\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Graphics/Sgb/SgbLightEntry.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Runtime.InteropServices;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace SaintCoinach.Graphics.Sgb {\n    public class SgbLightEntry : ISgbGroupEntry {\n        #region Struct\n        [StructLayout(LayoutKind.Sequential)]\n        public struct HeaderData {\n            public SgbGroupEntryType Type;\n            public uint UnknownId;\n            public int NameOffset;\n            public Vector3 Translation;\n            public Vector3 Rotation;\n            public Vector3 Scale;\n            public uint EntryCount;\n            public Vector2 Entry1;\n            public int UnknownFlag1;\n            public Vector2 Entry2;\n            public int Entry2NameOffset;\n            public ushort Entry3NameOffset;\n            public short UnknownFlag2;\n            public Vector2 Entry3;\n            public short UnknownFlag3;\n            public short UnknownFlag4;\n            public Vector2 Entry4;\n            public Vector2 Entry5;\n            // + unknowns\n        }\n        #endregion\n\n        #region Properties\n        SgbGroupEntryType ISgbGroupEntry.Type { get { return Header.Type; } }\n        public HeaderData Header { get; private set; }\n        public string Name { get; private set; }\n        public string Entry2Name, Entry3Name;\n        public Sgb.SgbFile Gimmick { get; private set; }\n        #endregion\n\n        #region Constructor\n        public SgbLightEntry(IO.PackCollection packs, byte[] buffer, int offset) {\n            this.Header = buffer.ToStructure<HeaderData>(offset);\n        }\n        #endregion\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Graphics/Sgb/SgbModelEntry.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Runtime.InteropServices;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace SaintCoinach.Graphics.Sgb {\n\n    public class SgbModelEntry : ISgbGroupEntry {\n        #region Struct\n        [StructLayout(LayoutKind.Sequential)]\n        public struct HeaderData {\n            public SgbGroupEntryType Type;\n            public uint GimmickId;\n            public int NameOffset;\n            public Vector3 Translation;\n            public Vector3 Rotation;\n            public Vector3 Scale;\n            public int ModelFileOffset;\n            public int CollisionFileOffset;\n        }\n        #endregion\n\n        #region Properties\n        SgbGroupEntryType ISgbGroupEntry.Type { get { return Header.Type; } }\n        public HeaderData Header { get; private set; }\n        public string Name { get; private set; }\n        public string ModelFilePath { get; private set; }\n        public string CollisionFilePath { get; private set; }\n        public TransformedModel Model { get; private set; }\n        public Pcb.PcbFile CollisionFile { get; private set; }\n        #endregion\n\n        #region Constructor\n        public SgbModelEntry(IO.PackCollection packs, byte[] buffer, int offset) {\n            this.Header = buffer.ToStructure<HeaderData>(offset);\n            this.Name = buffer.ReadString(offset + Header.NameOffset);\n\n            ModelFilePath = buffer.ReadString(offset + Header.ModelFileOffset);\n            CollisionFilePath = buffer.ReadString(offset + Header.CollisionFileOffset);\n\n            if (!string.IsNullOrWhiteSpace(ModelFilePath)) {\n                SaintCoinach.IO.File mdlFile;\n                if (packs.TryGetFile(ModelFilePath, out mdlFile))\n                    this.Model = new TransformedModel(((Graphics.ModelFile)mdlFile).GetModelDefinition(), Header.Translation, Header.Rotation, Header.Scale);\n            }\n            if (!string.IsNullOrWhiteSpace(CollisionFilePath)) {\n                SaintCoinach.IO.File pcbFile;\n                if (packs.TryGetFile(CollisionFilePath, out pcbFile))\n                    this.CollisionFile = new Pcb.PcbFile(pcbFile);\n            }\n        }\n        #endregion\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Graphics/Sgb/SgbVfxEntry.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Runtime.InteropServices;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace SaintCoinach.Graphics.Sgb {\n    public class SgbVfxEntry : ISgbGroupEntry {\n        #region Struct\n        [StructLayout(LayoutKind.Sequential)]\n        public struct HeaderData {\n            public SgbGroupEntryType Type;\n            public uint UnknownId;\n            public int NameOffset;\n            public Vector3 Translation;\n            public Vector3 Rotation;\n            public Vector3 Scale;\n            public int AvfxFileOffset;\n            public int Unknown5_2;\n            public int Unknown5_3;\n            public int Unknown5_4;\n\n            public int Unknown6;\n\n            public Vector3 SomeVec3;\n\n//            public int AvfxFileOffset;\n            // unknowns\n        }\n        #endregion\n\n        #region Properties\n        public SgbGroupEntryType Type => Header.Type;\n        public HeaderData Header { get; private set; }\n        public string Name { get; private set; }\n        public string AvfxFilePath { get; private set; }\n        public IO.File AvfxFile { get; private set; }\n        #endregion\n\n        #region Constructor\n        public SgbVfxEntry(IO.PackCollection packs, byte[] buffer, int offset) {\n            this.Header = buffer.ToStructure<HeaderData>(offset);\n            this.Name = buffer.ReadString(offset + Header.NameOffset);\n            this.AvfxFilePath = buffer.ReadString(offset + Header.AvfxFileOffset);\n\n            if (!string.IsNullOrEmpty(AvfxFilePath))\n                this.AvfxFile = packs.GetFile(AvfxFilePath);\n        }\n        #endregion\n\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Graphics/ShPk/Parameter.cs",
    "content": "﻿using System.Text;\n\nnamespace SaintCoinach.Graphics.ShPk {\n    public class Parameter {\n        #region Properties\n\n        public ParameterHeader Header { get; private set; }\n        public ParameterType Type { get { return Header.Type; } }\n        public int Id { get { return Header.Id; } }\n        public string Name { get; private set; }\n\n        #endregion\n\n        #region Constructors\n\n        #region Constructor\n\n        public Parameter(ShPkFile file, ParameterHeader header, byte[] buffer) {\n            Header = header;\n            var off = file.Header.ParameterListOffset + header.NameOffset;\n            Name = Encoding.ASCII.GetString(buffer, off, header.NameLength);\n        }\n\n        #endregion\n\n        #endregion\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Graphics/ShPk/ParameterHeader.cs",
    "content": "﻿using System;\n\nnamespace SaintCoinach.Graphics.ShPk {\n    public class ParameterHeader {\n        #region Static\n\n        public const int Size = 0x10;\n\n        #endregion\n\n        #region Properties\n\n        public ParameterType Type { get; private set; }\n        public int Id { get; private set; }\n        public int NameOffset { get; private set; }\n        public int NameLength { get; private set; }\n\n        #endregion\n\n        #region Constructors\n\n        #region Constructor\n\n        public ParameterHeader(ParameterType type, byte[] buffer, int offset) {\n            Type = type;\n            Id = BitConverter.ToInt32(buffer, offset + 0x00);\n            NameOffset = BitConverter.ToInt32(buffer, offset + 0x04);\n            NameLength = BitConverter.ToInt32(buffer, offset + 0x08);\n        }\n\n        #endregion\n\n        #endregion\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Graphics/ShPk/ParameterType.cs",
    "content": "﻿namespace SaintCoinach.Graphics.ShPk {\n    public enum ParameterType {\n        Unknown,\n        Scalar,\n        Resource\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Graphics/ShPk/ShPkFile.cs",
    "content": "﻿using System.Collections.Generic;\nusing System.Collections.ObjectModel;\n\nusing SaintCoinach.IO;\n\nnamespace SaintCoinach.Graphics.ShPk {\n    public class ShPkFile {\n        #region Properties\n\n        public File SourceFile { get; private set; }\n        public ShPkHeader Header { get; private set; }\n        public IReadOnlyList<Parameter> Parameters { get; private set; }\n\n        #endregion\n\n        #region Constructors\n\n        #region Constructor\n\n        public ShPkFile(File sourceFile) {\n            SourceFile = sourceFile;\n\n            Build();\n        }\n\n        #endregion\n\n        #endregion\n\n        #region Build\n\n        private void Build() {\n            var buffer = SourceFile.GetData();\n\n            Header = new ShPkHeader(buffer);\n\n            // TODO: All the other things\n\n            var para = new List<Parameter>();\n            foreach (var paraHeader in Header.ParameterHeaders)\n                para.Add(new Parameter(this, paraHeader, buffer));\n            Parameters = new ReadOnlyCollection<Parameter>(para);\n        }\n\n        #endregion\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Graphics/ShPk/ShPkHeader.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Collections.ObjectModel;\n\nnamespace SaintCoinach.Graphics.ShPk {\n    public class ShPkHeader {\n        #region Static\n\n        public const int Size = 0x48;\n\n        #endregion\n\n        #region Properties\n\n        public int FileLength { get; private set; }\n        public int ShaderDataOffset { get; private set; }\n        public int ParameterListOffset { get; private set; }\n        public int VertexShaderCount { get; private set; }\n        public int PixelShaderCount { get; private set; }\n        public int ScalarParameterCount { get; private set; }\n        public int ResourceParameterCount { get; private set; }\n        public IReadOnlyList<ShaderHeader> ShaderHeaders { get; private set; }\n        public IReadOnlyList<ParameterHeader> ParameterHeaders { get; private set; }\n\n        #endregion\n\n        #region Constructors\n\n        #region Constructor\n\n        public ShPkHeader(byte[] buffer) {\n            const uint Magic = 0x6B506853;\n            const uint SupportedFormat = 0x00395844;\n\n            if (BitConverter.ToUInt32(buffer, 0x00) != Magic)\n                throw new NotSupportedException(\"File is not a ShPk.\");\n            if (BitConverter.ToUInt32(buffer, 0x08) != SupportedFormat)\n                throw new NotSupportedException(\"Shader format is not supported.\");\n\n            FileLength = BitConverter.ToInt32(buffer, 0x0C);\n            ShaderDataOffset = BitConverter.ToInt32(buffer, 0x10);\n            ParameterListOffset = BitConverter.ToInt32(buffer, 0x14);\n            VertexShaderCount = BitConverter.ToInt32(buffer, 0x18);\n            PixelShaderCount = BitConverter.ToInt32(buffer, 0x1C);\n\n            ScalarParameterCount = BitConverter.ToInt32(buffer, 0x28);\n            ResourceParameterCount = BitConverter.ToInt32(buffer, 0x2C);\n\n            var offset = Size;\n            var shs = new List<ShaderHeader>();\n            for (var i = 0; i < VertexShaderCount; ++i) {\n                var sh = new ShaderHeader(ShaderType.Vertex, buffer, offset);\n                offset += sh.Size;\n                shs.Add(sh);\n            }\n            for (var i = 0; i < PixelShaderCount; ++i) {\n                var sh = new ShaderHeader(ShaderType.Pixel, buffer, offset);\n                offset += sh.Size;\n                shs.Add(sh);\n            }\n            ShaderHeaders = new ReadOnlyCollection<ShaderHeader>(shs);\n\n\n            var c1 = BitConverter.ToInt32(buffer, 0x24);\n            offset += c1 * 0x08;\n\n            var para = new List<ParameterHeader>();\n            for (var i = 0; i < ScalarParameterCount; ++i) {\n                var p = new ParameterHeader(ParameterType.Scalar, buffer, offset);\n                offset += ParameterHeader.Size;\n                para.Add(p);\n            }\n            for (var i = 0; i < ResourceParameterCount; ++i) {\n                var p = new ParameterHeader(ParameterType.Resource, buffer, offset);\n                offset += ParameterHeader.Size;\n                para.Add(p);\n            }\n            ParameterHeaders = new ReadOnlyCollection<ParameterHeader>(para);\n        }\n\n        #endregion\n\n        #endregion\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Graphics/ShPk/ShaderHeader.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Collections.ObjectModel;\n\nnamespace SaintCoinach.Graphics.ShPk {\n    public class ShaderHeader {\n        #region Properties\n\n        public int Size { get; private set; }\n        public ShaderType Type { get; private set; }\n        public int DataOffset { get; private set; }\n        public int DataLength { get; private set; }\n        public IReadOnlyList<ShaderParameterReference> ParameterReferences { get; private set; }\n\n        #endregion\n\n        #region Constructors\n\n        public ShaderHeader(ShaderType type, byte[] buffer, int offset) {\n            Size = 0x10;\n\n            Type = type;\n\n            DataOffset = BitConverter.ToInt32(buffer, offset + 0x00);\n            DataLength = BitConverter.ToInt32(buffer, offset + 0x04);\n\n            var cScalar = BitConverter.ToInt16(buffer, offset + 0x08);\n            var cResource = BitConverter.ToInt16(buffer, offset + 0x0A);\n\n            var paramRef = new List<ShaderParameterReference>();\n            while (cScalar-- > 0) {\n                paramRef.Add(new ShaderParameterReference(ParameterType.Scalar, buffer, offset + Size));\n                Size += ShaderParameterReference.Size;\n            }\n            while (cResource-- > 0) {\n                paramRef.Add(new ShaderParameterReference(ParameterType.Resource, buffer, offset + Size));\n                Size += ShaderParameterReference.Size;\n            }\n\n            ParameterReferences = new ReadOnlyCollection<ShaderParameterReference>(paramRef);\n        }\n\n        #endregion\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Graphics/ShPk/ShaderParameterReference.cs",
    "content": "﻿using System;\n\nnamespace SaintCoinach.Graphics.ShPk {\n    public class ShaderParameterReference {\n        #region Static\n\n        public const int Size = 0x10;\n\n        #endregion\n\n        #region Properties\n\n        public ParameterType Type { get; private set; }\n        public int Id { get; private set; }\n\n        #endregion\n\n        #region Constructors\n\n        #region Constructor\n\n        public ShaderParameterReference(ParameterType type, byte[] buffer, int offset) {\n            Type = type;\n            Id = BitConverter.ToInt32(buffer, offset + 0x00);\n        }\n\n        #endregion\n\n        #endregion\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Graphics/ShPk/ShaderType.cs",
    "content": "﻿namespace SaintCoinach.Graphics.ShPk {\n    public enum ShaderType {\n        Unknown,\n        Vertex,\n        Pixel\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Graphics/SklbFile.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Runtime.InteropServices;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace SaintCoinach.Graphics {\n    public class SklbFile {\n        [StructLayout(LayoutKind.Sequential, Pack = 1)]\n        public struct HeaderData {\n            public uint Magic;\n            public uint Format;\n        }\n\n        #region Properties\n        public IO.File File { get; private set; }\n        public HeaderData Header { get; private set; }\n        public PapAnimation[] Animations { get; private set; }\n        public byte[] HavokData { get; private set; }\n        public byte[] Parameters { get; private set; }\n        #endregion\n\n        #region Constructor\n        public SklbFile(IO.File file) {\n            this.File = file;\n\n            Build();\n        }\n        #endregion\n\n        #region Build\n        private void Build() {\n            var buffer = File.GetData();\n\n            var offset = 0;\n            Header = buffer.ToStructure<HeaderData>(ref offset);\n            if (Header.Magic != 0x736B6C62)\n                throw new System.IO.InvalidDataException();\n\n\n            int havokOffset;\n            switch (Header.Format) {\n                case 0x31323030:    // 0021\n                    havokOffset = BitConverter.ToInt16(buffer, 0x0A);\n                    break;\n                case 0x31333030:    // 0031\n                    havokOffset = BitConverter.ToInt16(buffer, 0x0C);\n                    break;\n                default:\n                    throw new NotSupportedException();\n            }\n            HavokData = new byte[buffer.Length - havokOffset];\n            Array.Copy(buffer, havokOffset, HavokData, 0, HavokData.Length);\n        }\n        #endregion\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Graphics/Territory.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace SaintCoinach.Graphics {\n    public class Territory {\n        #region Properties\n        public IO.PackCollection Packs { get; private set; }\n        public TerritoryParts.Terrain Terrain { get; private set; }\n        public string BasePath { get; private set; }\n        public string Name { get; private set; }\n        public Lgb.LgbFile[] LgbFiles { get; private set; }\n        #endregion\n\n        #region Constructor\n        public Territory(Xiv.TerritoryType type) : this(type.Sheet.Collection.PackCollection, type.Name, type.Bg) { }\n\n        /// <param name=\"levelPath\">Not including bg/</param>\n        public Territory(IO.PackCollection packs, string name, string levelPath) {\n            this.Packs = packs;\n            this.Name = name;\n            var i = levelPath.IndexOf(\"/level/\");\n            this.BasePath = \"bg/\" + levelPath.Substring(0, i + 1);\n\n            Build();\n        }\n        #endregion\n\n        #region Build\n        private void Build() {\n            var terrainPath = BasePath + \"bgplate/terrain.tera\";\n            if (Packs.TryGetFile(terrainPath, out var terrainFile))\n                this.Terrain = new TerritoryParts.Terrain(terrainFile);\n\n            var lgbFiles = new List<Lgb.LgbFile>() { TryGetLgb(\"level/bg.lgb\"), TryGetLgb(\"level/planmap.lgb\"), TryGetLgb(\"level/planevent.lgb\") };\n            this.LgbFiles = lgbFiles.Where(l => l != null).ToArray();\n        }\n        private Lgb.LgbFile TryGetLgb(string name) {\n            var path = BasePath + name;\n            if (Packs.TryGetFile(path, out var file))\n                return new Lgb.LgbFile(file);\n            return null;\n        }\n        #endregion\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Graphics/TerritoryParts/Terrain.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace SaintCoinach.Graphics.TerritoryParts {\n    public class Terrain {\n        #region Properties\n        public IO.File File { get; private set; }\n        public TransformedModel[] Parts { get; private set; }\n        #endregion\n\n        #region Constructor\n        public Terrain(IO.File file) {\n            this.File = file;\n\n            Build();\n        }\n        #endregion\n\n        #region Build\n        private void Build() {\n            const int CountOffset = 0x04;\n            const int SizeOffset = 0x08;\n            const int BlockPositionsOffset = 0x34;\n            const int BlockPositionSize = 0x04;\n\n            var buffer = File.GetData();\n\n            var blockCount = BitConverter.ToInt32(buffer, CountOffset);\n            var blockSize = BitConverter.ToInt32(buffer, SizeOffset);\n\n            var blockDirectory = File.Path.Substring(0, File.Path.LastIndexOf('/') + 1);\n\n            Parts = new TransformedModel[blockCount];\n            for (var i = 0; i < blockCount; ++i) {\n                var blockPath = blockDirectory + string.Format(\"{0:D4}.mdl\", i);\n                var blockModelFile = (ModelFile)File.Pack.Collection.GetFile(blockPath);\n\n                var x = BitConverter.ToInt16(buffer, BlockPositionsOffset + BlockPositionSize * i + 0);\n                var z = BitConverter.ToInt16(buffer, BlockPositionsOffset + BlockPositionSize * i + 2);\n\n                var translation = new Vector3 {\n                    X = blockSize * (x + 0.5f),\n                    Y = 0,\n                    Z = blockSize * (z + 0.5f)\n                };\n                Parts[i] = new TransformedModel(blockModelFile.GetModelDefinition(), translation, Vector3.Zero, Vector3.One);\n            }\n        }\n        #endregion\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Graphics/TransformedModel.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace SaintCoinach.Graphics {\n    public class TransformedModel {\n        #region Properties\n        public Vector3 Translation { get; private set; }\n        public Vector3 Rotation { get; private set; }\n        public Vector3 Scale { get; private set; }\n        public ModelDefinition Model { get; private set; }\n        #endregion\n\n        #region Constructor\n        public TransformedModel(ModelDefinition model, Vector3 translation, Vector3 rotation, Vector3 scale) {\n            this.Model = model;\n            this.Translation = translation;\n            this.Rotation = rotation;\n            this.Scale = scale;\n        }\n        #endregion\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Graphics/Unknowns/BoneIndices.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Runtime.InteropServices;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace SaintCoinach.Graphics.Unknowns {\n    public struct BoneIndices {  // 8 in hsl, provides references to bones for MeshPartHeader\n        public readonly int DataSize;\n        public readonly ushort[] Bones;\n\n        public int Size { get { return DataSize + 4; } }\n\n        public BoneIndices(byte[] buffer, ref int offset) {\n            DataSize = BitConverter.ToInt32(buffer, offset);\n            offset += 4;\n            Bones = new ushort[DataSize / 2];\n            for (var i = 0; i < Bones.Length; ++i) {\n                Bones[i] = BitConverter.ToUInt16(buffer, offset);\n                offset += 2;\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Graphics/Unknowns/BoneList.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Runtime.InteropServices;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace SaintCoinach.Graphics.Unknowns {\n    [StructLayout(LayoutKind.Sequential)]\n    public struct BoneList {  // 3 in hsl, something to do with bones (hierarchy? though it looks nothing like a hierarchy)\n        [MarshalAs(UnmanagedType.ByValArray, SizeConst = 0x40)]\n        public ushort[] Bones;\n\n        public uint ActualCount;\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Graphics/Unknowns/MaterialStruct1.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Runtime.InteropServices;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace SaintCoinach.Graphics.Unknowns {\n    [StructLayout(LayoutKind.Sequential)]\n    public struct MaterialStruct1 {\n        public uint Unknown1;   // Probably an Id of some sort\n        public uint Unknown2;\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Graphics/Unknowns/MaterialStruct2.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Runtime.InteropServices;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace SaintCoinach.Graphics.Unknowns {\n    [StructLayout(LayoutKind.Sequential)]\n    public struct MaterialStruct2 {\n        public uint Unknown1;   // Probably an Id of some sort\n        public ushort Offset;   // Offset & Size are relative to the blob of data at the end, after ParameterMappings\n        public ushort Size;\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Graphics/Unknowns/ModelStruct1.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Runtime.InteropServices;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace SaintCoinach.Graphics.Unknowns {\n    [StructLayout(LayoutKind.Sequential)]\n    public struct ModelStruct1 {  // 0 in hsl\n        public uint Unknown1;\n        public uint Unknown2;\n        public uint Unknown3;\n        public uint Unknown4;\n        public uint Unknown5;\n        public uint Unknown6;\n        public uint Unknown7;\n        public uint Unknown8;\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Graphics/Unknowns/ModelStruct2.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Runtime.InteropServices;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace SaintCoinach.Graphics.Unknowns {\n    [StructLayout(LayoutKind.Sequential)]\n    public struct ModelStruct2 {  // 1 in hsl\n        public uint Unknown1;\n        public uint Unknown2;\n        public uint Unknown3;\n        public uint Unknown4;\n        public uint Unknown5;\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Graphics/Unknowns/ModelStruct3.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Runtime.InteropServices;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace SaintCoinach.Graphics.Unknowns {\n    [StructLayout(LayoutKind.Sequential)]\n    public struct ModelStruct3 {  // 7 in hsl\n        public uint Unknown1;\n        public uint Unknown2;\n        public uint Unknown3;\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Graphics/Unknowns/ModelStruct5.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Runtime.InteropServices;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace SaintCoinach.Graphics.Unknowns {\n    [StructLayout(LayoutKind.Sequential)]\n    public struct ModelStruct5 {  // 4 in hsl\n        public uint Unknown1;\n        public uint Unknown2;\n        public uint Unknown3;\n        public uint Unknown4;\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Graphics/Unknowns/ModelStruct6.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Runtime.InteropServices;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace SaintCoinach.Graphics.Unknowns {\n    [StructLayout(LayoutKind.Sequential)]\n    public struct ModelStruct6 {  // 5 in hsl\n        public uint Unknown;\n        public uint UnknownStruct4Count;    // 6 in hsl\n        public uint UnknownStruct4Offset;\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Graphics/Unknowns/ModelStruct7.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Runtime.InteropServices;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace SaintCoinach.Graphics.Unknowns {\n    [StructLayout(LayoutKind.Sequential)]\n    public struct ModelStruct7 {  // 6 in hsl\n        public ushort Index;\n        public ushort Vertex;\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Graphics/Vector2.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Runtime.InteropServices;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace SaintCoinach.Graphics {\n    [System.Diagnostics.DebuggerDisplay(\"( {X}, {Y} )\")]\n    [StructLayout(LayoutKind.Sequential)]\n    public struct Vector2 {\n        public float X;\n        public float Y;\n\n        public static readonly Vector2 One = new Vector2 { X = 1, Y = 1 };\n        public static readonly Vector2 Zero = new Vector2 { X = 0, Y = 0 };\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Graphics/Vector3.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Runtime.InteropServices;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace SaintCoinach.Graphics {\n    [System.Diagnostics.DebuggerDisplay(\"( {X}, {Y}, {Z} )\")]\n    [StructLayout(LayoutKind.Sequential)]\n    public struct Vector3 {\n        public float X;\n        public float Y;\n        public float Z;\n\n        public static readonly Vector3 One = new Vector3 { X = 1, Y = 1, Z = 1 };\n        public static readonly Vector3 Zero = new Vector3 { X = 0, Y = 0, Z = 0 };\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Graphics/Vector4.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Runtime.InteropServices;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace SaintCoinach.Graphics {\n    [System.Diagnostics.DebuggerDisplay(\"( {X}, {Y}, {Z}, {W} )\")]\n    [StructLayout(LayoutKind.Sequential)]\n    public struct Vector4 {\n        public float X;\n        public float Y;\n        public float Z;\n        public float W;\n\n        public static readonly Vector4 One = new Vector4 { X = 1, Y = 1, Z = 1, W = 1 };\n        public static readonly Vector4 Zero = new Vector4 { X = 0, Y = 0, Z = 0, W = 0 };\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Graphics/Vertex.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace SaintCoinach.Graphics {\n    public struct Vertex {\n        public Vector4? Position;\n        public Vector4? BlendWeights;\n        public uint? BlendIndices;\n        public Vector3? Normal;\n\n        // TODO: For dual textures it's two components per texture, unknown purpose of Z and W components on others.\n        public Vector4? UV;\n        public Vector4? Color;\n        public Vector4? Tangent2;\n        public Vector4? Tangent1;\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Graphics/VertexAttribute.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace SaintCoinach.Graphics {\n    public enum VertexAttribute : byte {\n        Position = 0x00,\n        BlendWeights = 0x01,\n        BlendIndices = 0x02,\n        Normal = 0x03,\n        UV = 0x04,\n        Tangent2 = 0x05, // Don't ask me why the second one (only present on dual-textured models) is first\n        Tangent1 = 0x06,\n        Color = 0x07,\n\n        // TODO: Check for additional types (chara/* models checked, most in bg/* too, but some couldn't be read)\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Graphics/VertexDataType.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace SaintCoinach.Graphics {\n    public enum VertexDataType : byte {\n        Single3 = 0x02,\n        Single4 = 0x03,\n        UInt = 0x05,\n        ByteFloat4 = 0x08,\n        Half2 = 0x0D,\n        Half4 = 0x0E,\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Graphics/VertexFormat.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Runtime.InteropServices;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace SaintCoinach.Graphics {\n    public class VertexFormat {\n        #region Properties\n        public VertexFormatElement[] Elements { get; private set; }\n        #endregion\n\n        #region Constructor\n        internal VertexFormat(byte[] buffer, ref int offset) {\n            var o = offset;\n\n            var elements = new List<VertexFormatElement>();\n            while (buffer[o] != 0xFF)\n                elements.Add(buffer.ToStructure<VertexFormatElement>(ref o));\n            this.Elements = elements.ToArray();\n\n            offset += 0x88;\n        }\n        #endregion\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Graphics/VertexFormatElement.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Runtime.InteropServices;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace SaintCoinach.Graphics {\n    [StructLayout(LayoutKind.Sequential)]\n    public struct VertexFormatElement {\n        public byte SourcePart;         // 0 = VertexData1 ; 1 = VertexData2 ; 255 = STAHP\n        public byte Offset;\n        public VertexDataType DataType;\n        public VertexAttribute Attribute;\n        public int Unknown;             // Always 0 so far\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Graphics/VertexReader.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace SaintCoinach.Graphics {\n    static class VertexReader {\n        public static Vertex Read(byte[] buffer, VertexFormat format, int[] offsets) {\n            Vertex vertex = new Vertex();\n\n            foreach (var element in format.Elements) {\n                int elementOffset = offsets[element.SourcePart];\n\n                ReadElement(buffer, element, elementOffset, ref vertex);\n            }\n\n            return vertex;\n        }\n        static void ReadElement(byte[] buffer, VertexFormatElement element, int offset, ref Vertex vertex) {\n            var data = ReadData(buffer, element.DataType, offset + element.Offset);\n\n            switch (element.Attribute) {\n                case VertexAttribute.BlendIndices:\n                    vertex.BlendIndices = (uint)data;\n                    break;\n                case VertexAttribute.BlendWeights:\n                    vertex.BlendWeights = (Vector4)data;\n                    break;\n                case VertexAttribute.Color:\n                    vertex.Color = (Vector4)data;\n                    break;\n                case VertexAttribute.Normal:\n                    vertex.Normal = ForceToVector3(data);\n                    break;\n                case VertexAttribute.Position:\n                    vertex.Position = ForceToVector4(data);\n                    break;\n                case VertexAttribute.Tangent2:\n                    vertex.Tangent2 = (Vector4)data;\n                    break;\n                case VertexAttribute.Tangent1:\n                    vertex.Tangent1 = (Vector4)data;\n                    break;\n                case VertexAttribute.UV:\n                    vertex.UV = ForceToVector4(data);\n                    break;\n                default:\n                    throw new NotSupportedException();\n            }\n        }\n        static Vector3 ForceToVector3(object value) {\n            if (value is Vector3)\n                return (Vector3)value;\n            if (value is Vector4) {\n                var v4 = (Vector4)value;\n                return new Vector3 {\n                    X = v4.X,\n                    Y = v4.Y,\n                    Z = v4.Z\n                };\n            }\n            throw new NotSupportedException();\n        }\n        static Vector4 ForceToVector4(object value) {\n            if (value is Vector4)\n                return (Vector4)value;\n            if (value is Vector2) {\n                var v2 = (Vector2)value;\n                return new Vector4 {\n                    X = v2.X,\n                    Y = v2.Y,\n                    Z = 0,\n                    W = 0\n                };\n            }\n            if (value is Vector3) {\n                var v3 = (Vector3)value;\n                return new Vector4 {\n                    X = v3.X,\n                    Y = v3.Y,\n                    Z = v3.Z,\n                    W = 1\n                };\n            }\n            throw new NotSupportedException();\n        }\n        static object ReadData(byte[] buffer, VertexDataType type, int offset) {\n            switch (type) {\n                case VertexDataType.Half2:\n                    return new Vector2 {\n                        X = HalfHelper.Unpack(buffer, offset + 0x00),\n                        Y = HalfHelper.Unpack(buffer, offset + 0x02)\n                    };\n                case VertexDataType.Half4:\n                    return new Vector4 {\n                        X = HalfHelper.Unpack(buffer, offset + 0x00),\n                        Y = HalfHelper.Unpack(buffer, offset + 0x02),\n                        Z = HalfHelper.Unpack(buffer, offset + 0x04),\n                        W = HalfHelper.Unpack(buffer, offset + 0x06)\n                    };\n                case VertexDataType.UInt:\n                    return BitConverter.ToUInt32(buffer, offset);\n                case VertexDataType.ByteFloat4:\n                    return new Vector4 {\n                        X = buffer[offset + 0] / 255f,\n                        Y = buffer[offset + 1] / 255f,\n                        Z = buffer[offset + 2] / 255f,\n                        W = buffer[offset + 3] / 255f\n                    };\n                case VertexDataType.Single3:\n                    return buffer.ToStructure<Vector3>(offset);\n                case VertexDataType.Single4:\n                    return buffer.ToStructure<Vector4>(offset);\n                default:\n                    throw new NotSupportedException();\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/HalfHelper.cs",
    "content": "﻿using System;\n\nnamespace SaintCoinach {\n    // I have to be honest at this point, I stole this class from Xna.\n    public static class HalfHelper {\n        // Methods\n        public static unsafe ushort Pack(float value) {\n            var num5 = *((uint*)&value);\n            var num3 = (uint)((num5 & -2147483648) >> 0x10);\n            var num = num5 & 0x7fffffff;\n            if (num > 0x47ffefff) {\n                return (ushort)(num3 | 0x7fff);\n            }\n            if (num >= 0x38800000) return (ushort)(num3 | ((((num + -939524096) + 0xfff) + ((num >> 13) & 1)) >> 13));\n\n            var num6 = (num & 0x7fffff) | 0x800000;\n            var num4 = 0x71 - ((int)(num >> 0x17));\n            num = (num4 > 0x1f) ? 0 : (num6 >> num4);\n            return (ushort)(num3 | (((num + 0xfff) + ((num >> 13) & 1)) >> 13));\n        }\n\n        public static float Unpack(byte[] buffer, int offset) {\n            return Unpack(BitConverter.ToUInt16(buffer, offset));\n        }\n\n        public static unsafe float Unpack(ushort value) {\n            uint num3;\n            if ((value & -33792) == 0) {\n                if ((value & 0x3ff) != 0) {\n                    var num2 = 0xfffffff2;\n                    var num = (uint)(value & 0x3ff);\n                    while ((num & 0x400) == 0) {\n                        num2--;\n                        num = num << 1;\n                    }\n                    num &= 0xfffffbff;\n                    num3 = ((uint)(((value & 0x8000) << 0x10) | ((num2 + 0x7f) << 0x17))) | (num << 13);\n                } else {\n                    num3 = (uint)((value & 0x8000) << 0x10);\n                }\n            } else {\n                num3 =\n                    (uint)\n                    ((((value & 0x8000) << 0x10) | (((((value >> 10) & 0x1f) - 15) + 0x7f) << 0x17))\n                     | ((value & 0x3ff) << 13));\n            }\n            return *(((float*)&num3));\n        }\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/IO/Directory.Enumeration.cs",
    "content": "﻿using System.Collections;\nusing System.Collections.Generic;\n\nnamespace SaintCoinach.IO {\n    partial class Directory : IEnumerable<File> {\n        #region Enumerator\n\n        private class Enumerator : IEnumerator<File> {\n            #region Fields\n\n            private readonly Directory _Directory;\n            private readonly IEnumerator<IndexFile> _InnerEnumerator;\n            private File _Current;\n\n            #endregion\n\n            #region Constructors\n\n            #region Constructor\n\n            public Enumerator(Directory dir) {\n                _Directory = dir;\n                _InnerEnumerator = dir.Index.Files.Values.GetEnumerator();\n            }\n\n            #endregion\n\n            #endregion\n\n            #region IEnumerator<SqPackDirectory> Members\n\n            public File Current { get { return _Current; } }\n\n            #endregion\n\n            #region IDisposable Members\n\n            public void Dispose() { }\n\n            #endregion\n\n            #region IEnumerator Members\n\n            object IEnumerator.Current { get { return Current; } }\n\n            public bool MoveNext() {\n                var res = _InnerEnumerator.MoveNext();\n                if (res)\n                    _Directory.TryGetFile(_InnerEnumerator.Current.FileKey, out _Current);\n                else\n                    _Current = null;\n                return res;\n            }\n\n            public void Reset() {\n                _InnerEnumerator.Reset();\n                _Current = null;\n            }\n\n            #endregion\n        }\n\n        #endregion\n\n        #region IEnumerable<SqPackFile> Members\n\n        public IEnumerator<File> GetEnumerator() {\n            return new Enumerator(this);\n        }\n\n        IEnumerator IEnumerable.GetEnumerator() {\n            return GetEnumerator();\n        }\n\n        #endregion\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/IO/Directory.cs",
    "content": "﻿using System;\nusing System.Collections.Concurrent;\nusing System.Collections.Generic;\n\nnamespace SaintCoinach.IO {\n    /// <summary>\n    ///     Directory inside a SqPack.\n    /// </summary>\n    public partial class Directory : IPackSource {\n        #region Fields\n\n        private readonly Dictionary<string, uint> _FileNameMap = new Dictionary<string, uint>();\n        private readonly ConcurrentDictionary<uint, WeakReference<File>> _Files = new ConcurrentDictionary<uint, WeakReference<File>>();\n        private string _Path;\n\n        #endregion\n\n        #region Properties\n\n        public Pack Pack { get; private set; }\n        public IndexDirectory Index { get; private set; }\n\n        public string Path {\n            get { return _Path ?? string.Join(\"/\", Pack.ToString(), Index.Key.ToString(\"X8\")); }\n            internal set { _Path = value; }\n        }\n\n        #endregion\n\n        #region Constructors\n\n        #region Constructor\n\n        public Directory(Pack pack, IndexDirectory index) {\n            Pack = pack;\n            Index = index;\n        }\n\n        #endregion\n\n        #endregion\n\n        public override string ToString() {\n            return Path;\n        }\n\n        #region Get\n\n        public bool FileExists(string name) {\n            uint hash;/*\n            if (!_FileNameMap.TryGetValue(name, out hash))\n                _FileNameMap.Add(name, hash = Hash.Compute(name));*/\n            hash = Hash.Compute(name);\n            return FileExists(hash);\n        }\n\n        public bool FileExists(uint fileKey) {\n            return Index.Files.ContainsKey(fileKey);\n        }\n\n        public File GetFile(string name) {\n            uint hash;/*\n            if (!_FileNameMap.TryGetValue(name, out hash))\n                _FileNameMap.Add(name, hash = Hash.Compute(name));*/\n            hash = Hash.Compute(name);\n\n            var file = GetFile(hash);\n            if (file == null)\n                throw new System.IO.FileNotFoundException(\"Pack file not found '\" + name + \"'\");\n            file.Path = string.Format(\"{0}/{1}\", this.Path, name);\n            return file;\n        }\n\n        public File GetFile(uint key) {\n            if (_Files.TryGetValue(key, out var fileRef) && fileRef.TryGetTarget(out var file))\n                return file;\n\n            if (!Index.Files.TryGetValue(key, out var index))\n                return null;\n\n            file = FileFactory.Get(this.Pack, index);\n            _Files.AddOrUpdate(key,\n                k => new WeakReference<File>(file),\n                (k, r) => {\n                    r.SetTarget(file);\n                    return r;\n                });\n            return file;\n        }\n\n        public bool TryGetFile(string name, out File file) {\n            uint hash;/*\n            if (!_FileNameMap.TryGetValue(name, out hash))\n                _FileNameMap.Add(name, hash = Hash.Compute(name));*/\n            hash = Hash.Compute(name);\n\n            var result = TryGetFile(hash, out file);\n            if (result)\n                file.Path = string.Format(\"{0}/{1}\", this.Path, name);\n            return result;\n        }\n\n        public bool TryGetFile(uint key, out File file) {\n            if (_Files.TryGetValue(key, out var fileRef) && fileRef.TryGetTarget(out file))\n                return true;\n\n            if (Index.Files.TryGetValue(key, out var index)) {\n                var theFile = FileFactory.Get(this.Pack, index);\n                _Files.AddOrUpdate(key,\n                    k => new WeakReference<File>(theFile),\n                    (k, r) => {\n                        r.SetTarget(theFile);\n                        return r;\n                    });\n                file = theFile;\n                return true;\n            }\n\n            file = null;\n            return false;\n        }\n\n        #endregion\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/IO/EmptyFile.cs",
    "content": "﻿namespace SaintCoinach.IO {\n    public class EmptyFile : File {\n        #region Constructors\n\n        public EmptyFile(Pack pack, FileCommonHeader header) : base(pack, header) { }\n\n        #endregion\n\n        public override byte[] GetData() {\n            return new byte[0];\n        }\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/IO/File.cs",
    "content": "﻿using System;\nusing System.IO;\nusing System.IO.Compression;\n\nnamespace SaintCoinach.IO {\n    /// <summary>\n    ///     Base class for files inside the SqPack.\n    /// </summary>\n    public abstract class File {\n        #region Fields\n\n        private string _Path;\n\n        #endregion\n\n        #region Properties\n\n        public Pack Pack { get; private set; }\n        public FileCommonHeader CommonHeader { get; private set; }\n        public IIndexFile Index { get { return CommonHeader.Index; } }\n        public string Path { get { return _Path ?? Index.FileKey.ToString(\"X8\"); } internal set { _Path = value; } }\n\n        #endregion\n\n        #region Constructors\n\n        protected File(Pack pack, FileCommonHeader commonHeader) {\n            Pack = pack;\n            CommonHeader = commonHeader;\n        }\n\n        #endregion\n\n        public override string ToString() {\n            return Path;\n        }\n\n        #region Abstracts\n\n        public abstract byte[] GetData();\n\n        public virtual Stream GetStream() {\n            return new MemoryStream(GetData());\n        }\n\n        #endregion\n\n        #region Helpers\n\n        protected Stream GetSourceStream() {\n            return this.Pack.GetDataStream(Index.DatFile);\n        }\n\n        protected static byte[] ReadBlock(Stream stream) {\n            byte[] block;\n            using (var msOut = new MemoryStream()) {\n                ReadBlock(stream, msOut);\n                block = msOut.ToArray();\n            }\n            return block;\n        }\n\n        protected static void ReadBlock(Stream inStream, Stream outStream) {\n            const uint Magic = 0x00000010;\n\n            const int HeaderLength = 0x10;\n            const int MagicOffset = 0x00;\n            const int SourceSizeOffset = 0x08;\n            const int RawSizeOffset = 0x0C;\n\n            const int BlockPadding = 0x80;\n\n            const int CompressionThreshold = 0x7D00;\n\n            /*\n             * Block:\n             * 10h  Header\n             * *    Data\n             * \n             * Header:\n             * 4h   Magic\n             * 4h   Unknown / Zero\n             * 4h   Size in source\n             * 4h   Raw size\n             * -> If size in source >= 7D00h then data is uncompressed\n             */\n\n            var header = new byte[HeaderLength];\n            if (inStream.Read(header, 0, HeaderLength) != HeaderLength)\n                throw new EndOfStreamException();\n\n            var magicCheck = BitConverter.ToInt32(header, MagicOffset);\n            var sourceSize = BitConverter.ToInt32(header, SourceSizeOffset);\n            var rawSize = BitConverter.ToInt32(header, RawSizeOffset);\n\n            if (magicCheck != Magic)\n                throw new NotSupportedException(\"Magic number not present (-> don't know how to continue).\");\n\n            var isCompressed = sourceSize < CompressionThreshold;\n\n            var blockSize = isCompressed ? sourceSize : rawSize;\n\n            // An uncompressed block in an ScdOggFile was corrupted due to this\n            // extra padding injecting extra 0s into the output stream.  I'm\n            // not certain padding is only applied to compressed blocks.  The\n            // padding algorithm may require more work if additional problems\n            // are found.\n            if (isCompressed && (blockSize + HeaderLength) % BlockPadding != 0)\n                blockSize += BlockPadding - ((blockSize + HeaderLength) % BlockPadding); // Add padding if necessary\n\n            var buffer = new byte[blockSize];\n            if (inStream.Read(buffer, 0, blockSize) != blockSize)\n                throw new EndOfStreamException();\n\n            if (isCompressed) {\n                var currentPosition = outStream.Position;\n                Inflate(buffer, outStream);\n                var dLen = outStream.Position - currentPosition;\n                if (dLen != rawSize)\n                    throw new InvalidDataException(\"Inflated block does not match indicated size.\");\n            } else {\n                outStream.Write(buffer, 0, buffer.Length);\n            }\n        }\n\n        private static void Inflate(byte[] buffer, Stream outStream) {\n            var unc = Ionic.Zlib.DeflateStream.UncompressBuffer(buffer);\n            outStream.Write(unc, 0, unc.Length);\n        }\n\n        #endregion\n\n        #region Equals\n        public override int GetHashCode() {\n            return Index.GetHashCode();\n        }\n        public override bool Equals(object obj) {\n            if (obj is File)\n                return ((File)obj).Index.Equals(this.Index);\n            return false;\n        }\n        #endregion\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/IO/FileCommonHeader.cs",
    "content": "﻿using System;\nusing System.IO;\n\nnamespace SaintCoinach.IO {\n    /// <summary>\n    ///     Shared header for a file inside SqPack.\n    /// </summary>\n    public class FileCommonHeader {\n        #region Fields\n\n        internal byte[] _Buffer;\n\n        #endregion\n\n        #region Properties\n\n        public IIndexFile Index { get; private set; }\n        public FileType FileType { get; private set; }\n        public long Length { get; private set; }\n        public long EndOfHeader { get; private set; }\n\n        #endregion\n\n        #region Constructors\n\n        public FileCommonHeader(IIndexFile index, Stream stream) {\n            if (index == null)\n                throw new ArgumentNullException(\"index\");\n            if (stream == null)\n                throw new ArgumentNullException(\"stream\");\n\n            Index = index;\n\n            Read(stream);\n        }\n\n        #endregion\n\n        public byte[] GetBuffer() {\n            var b = new byte[_Buffer.Length];\n            Array.Copy(_Buffer, b, b.Length);\n            return b;\n        }\n\n        #region Read\n\n        private void Read(Stream stream) {\n            const int FileTypeOffset = 0x04;\n            const int FileLengthOffset = 0x10;\n            const int FileLengthShift = 7;\n\n            if (!stream.CanSeek)\n                throw new NotSupportedException(\"Stream must be able to seek.\");\n\n            _Buffer = new byte[4];\n            if (stream.Read(_Buffer, 0, 4) != 4)\n                throw new EndOfStreamException();\n\n            var length = BitConverter.ToInt32(_Buffer, 0);\n            Array.Resize(ref _Buffer, length);\n\n            var remaining = length - 4;\n            if (stream.Read(_Buffer, 4, remaining) != remaining)\n                throw new EndOfStreamException();\n\n            FileType = (FileType)BitConverter.ToInt32(_Buffer, FileTypeOffset);\n            Length = BitConverter.ToInt32(_Buffer, FileLengthOffset) << FileLengthShift;\n            EndOfHeader = stream.Position;\n        }\n\n        #endregion\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/IO/FileDefault.cs",
    "content": "﻿using System;\nusing System.IO;\n\nnamespace SaintCoinach.IO {\n    /// <summary>\n    ///     A file inside SqPack without special attributes.\n    /// </summary>\n    public class FileDefault : File {\n        #region Fields\n\n        private WeakReference<byte[]> _BufferCache;\n\n        #endregion\n\n        #region Constructors\n\n        public FileDefault(Pack pack, FileCommonHeader header) : base(pack, header) { }\n\n        #endregion\n\n        #region Read\n\n        public override byte[] GetData() {\n            byte[] buffer;\n\n            if (_BufferCache != null && _BufferCache.TryGetTarget(out buffer)) return buffer;\n\n            buffer = Read();\n\n            if (_BufferCache == null)\n                _BufferCache = new WeakReference<byte[]>(buffer);\n            else\n                _BufferCache.SetTarget(buffer);\n\n            return buffer;\n        }\n\n        private byte[] Read() {\n            const int BlockCountOffset = 0x14;\n            const int BlockInfoOffset = 0x18;\n            const int BlockInfoLength = 0x08;\n\n            var sourceStream = GetSourceStream();\n            var blockCount = BitConverter.ToInt16(CommonHeader._Buffer, BlockCountOffset);\n\n            byte[] data;\n            using (var dataStream = new MemoryStream((int)CommonHeader.Length)) {\n                for (var i = 0; i < blockCount; ++i) {\n                    var blockOffset = BitConverter.ToInt32(CommonHeader._Buffer, BlockInfoOffset + i * BlockInfoLength);\n                    sourceStream.Position = CommonHeader.EndOfHeader + blockOffset;\n                    ReadBlock(sourceStream, dataStream);\n                }\n\n                data = dataStream.ToArray();\n            }\n\n            return data;\n        }\n\n        #endregion\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/IO/FileFactory.cs",
    "content": "﻿using System;\n\nusing SaintCoinach.Graphics;\nusing SaintCoinach.Imaging;\n\nnamespace SaintCoinach.IO {\n    public static class FileFactory {\n        public static File Get(Pack pack, IIndexFile file) {\n            var stream = pack.GetDataStream(file.DatFile);\n            stream.Position = file.Offset;\n\n            var header = new FileCommonHeader(file, stream);\n\n            switch (header.FileType) {\n                case FileType.Empty:\n                    return new EmptyFile(pack, header);\n                case FileType.Default:\n                    return new FileDefault(pack, header);\n                case FileType.Image:\n                    return new ImageFile(pack, header);\n                case FileType.Model:\n                    return new ModelFile(pack, header);\n                default:\n                    throw new NotSupportedException(string.Format(\"Unknown file type {0:X2}h\", (int)header.FileType));\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/IO/FileType.cs",
    "content": "﻿namespace SaintCoinach.IO {\n    public enum FileType {\n        Unknown = 0,\n        Empty = 1,\n        Default = 2,\n        Model = 3,\n        Image = 4\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/IO/Hash.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\n\nnamespace SaintCoinach.IO {\n    public static class Hash {\n        #region CRC32\n\n        #region Const / Static\n\n        public const UInt32 CrcInitialSeed = 0xFFFFFFFF;\n\n        private static readonly UInt32[] CrcTable = {\n            0x00000000, 0x77073096, 0xEE0E612C, 0x990951BA, 0x076DC419, 0x706AF48F, 0xE963A535, 0x9E6495A3, 0x0EDB8832,\n            0x79DCB8A4, 0xE0D5E91E, 0x97D2D988, 0x09B64C2B, 0x7EB17CBD, 0xE7B82D07, 0x90BF1D91,\n            0x1DB71064, 0x6AB020F2, 0xF3B97148, 0x84BE41DE, 0x1ADAD47D, 0x6DDDE4EB, 0xF4D4B551, 0x83D385C7, 0x136C9856,\n            0x646BA8C0, 0xFD62F97A, 0x8A65C9EC, 0x14015C4F, 0x63066CD9, 0xFA0F3D63, 0x8D080DF5,\n            0x3B6E20C8, 0x4C69105E, 0xD56041E4, 0xA2677172, 0x3C03E4D1, 0x4B04D447, 0xD20D85FD, 0xA50AB56B, 0x35B5A8FA,\n            0x42B2986C, 0xDBBBC9D6, 0xACBCF940, 0x32D86CE3, 0x45DF5C75, 0xDCD60DCF, 0xABD13D59,\n            0x26D930AC, 0x51DE003A, 0xC8D75180, 0xBFD06116, 0x21B4F4B5, 0x56B3C423, 0xCFBA9599, 0xB8BDA50F, 0x2802B89E,\n            0x5F058808, 0xC60CD9B2, 0xB10BE924, 0x2F6F7C87, 0x58684C11, 0xC1611DAB, 0xB6662D3D,\n            0x76DC4190, 0x01DB7106, 0x98D220BC, 0xEFD5102A, 0x71B18589, 0x06B6B51F, 0x9FBFE4A5, 0xE8B8D433, 0x7807C9A2,\n            0x0F00F934, 0x9609A88E, 0xE10E9818, 0x7F6A0DBB, 0x086D3D2D, 0x91646C97, 0xE6635C01,\n            0x6B6B51F4, 0x1C6C6162, 0x856530D8, 0xF262004E, 0x6C0695ED, 0x1B01A57B, 0x8208F4C1, 0xF50FC457, 0x65B0D9C6,\n            0x12B7E950, 0x8BBEB8EA, 0xFCB9887C, 0x62DD1DDF, 0x15DA2D49, 0x8CD37CF3, 0xFBD44C65,\n            0x4DB26158, 0x3AB551CE, 0xA3BC0074, 0xD4BB30E2, 0x4ADFA541, 0x3DD895D7, 0xA4D1C46D, 0xD3D6F4FB, 0x4369E96A,\n            0x346ED9FC, 0xAD678846, 0xDA60B8D0, 0x44042D73, 0x33031DE5, 0xAA0A4C5F, 0xDD0D7CC9,\n            0x5005713C, 0x270241AA, 0xBE0B1010, 0xC90C2086, 0x5768B525, 0x206F85B3, 0xB966D409, 0xCE61E49F, 0x5EDEF90E,\n            0x29D9C998, 0xB0D09822, 0xC7D7A8B4, 0x59B33D17, 0x2EB40D81, 0xB7BD5C3B, 0xC0BA6CAD,\n            0xEDB88320, 0x9ABFB3B6, 0x03B6E20C, 0x74B1D29A, 0xEAD54739, 0x9DD277AF, 0x04DB2615, 0x73DC1683, 0xE3630B12,\n            0x94643B84, 0x0D6D6A3E, 0x7A6A5AA8, 0xE40ECF0B, 0x9309FF9D, 0x0A00AE27, 0x7D079EB1,\n            0xF00F9344, 0x8708A3D2, 0x1E01F268, 0x6906C2FE, 0xF762575D, 0x806567CB, 0x196C3671, 0x6E6B06E7, 0xFED41B76,\n            0x89D32BE0, 0x10DA7A5A, 0x67DD4ACC, 0xF9B9DF6F, 0x8EBEEFF9, 0x17B7BE43, 0x60B08ED5,\n            0xD6D6A3E8, 0xA1D1937E, 0x38D8C2C4, 0x4FDFF252, 0xD1BB67F1, 0xA6BC5767, 0x3FB506DD, 0x48B2364B, 0xD80D2BDA,\n            0xAF0A1B4C, 0x36034AF6, 0x41047A60, 0xDF60EFC3, 0xA867DF55, 0x316E8EEF, 0x4669BE79,\n            0xCB61B38C, 0xBC66831A, 0x256FD2A0, 0x5268E236, 0xCC0C7795, 0xBB0B4703, 0x220216B9, 0x5505262F, 0xC5BA3BBE,\n            0xB2BD0B28, 0x2BB45A92, 0x5CB36A04, 0xC2D7FFA7, 0xB5D0CF31, 0x2CD99E8B, 0x5BDEAE1D,\n            0x9B64C2B0, 0xEC63F226, 0x756AA39C, 0x026D930A, 0x9C0906A9, 0xEB0E363F, 0x72076785, 0x05005713, 0x95BF4A82,\n            0xE2B87A14, 0x7BB12BAE, 0x0CB61B38, 0x92D28E9B, 0xE5D5BE0D, 0x7CDCEFB7, 0x0BDBDF21,\n            0x86D3D2D4, 0xF1D4E242, 0x68DDB3F8, 0x1FDA836E, 0x81BE16CD, 0xF6B9265B, 0x6FB077E1, 0x18B74777, 0x88085AE6,\n            0xFF0F6A70, 0x66063BCA, 0x11010B5C, 0x8F659EFF, 0xF862AE69, 0x616BFFD3, 0x166CCF45,\n            0xA00AE278, 0xD70DD2EE, 0x4E048354, 0x3903B3C2, 0xA7672661, 0xD06016F7, 0x4969474D, 0x3E6E77DB, 0xAED16A4A,\n            0xD9D65ADC, 0x40DF0B66, 0x37D83BF0, 0xA9BCAE53, 0xDEBB9EC5, 0x47B2CF7F, 0x30B5FFE9,\n            0xBDBDF21C, 0xCABAC28A, 0x53B39330, 0x24B4A3A6, 0xBAD03605, 0xCDD70693, 0x54DE5729, 0x23D967BF, 0xB3667A2E,\n            0xC4614AB8, 0x5D681B02, 0x2A6F2B94, 0xB40BBE37, 0xC30C8EA1, 0x5A05DF1B, 0x2D02EF8D\n        };\n\n        #endregion\n\n        #region Computation\n\n        private static uint Compute(uint[] table, uint seed, byte[] buffer, int start, int size) {\n            if (table == null)\n                throw new ArgumentNullException(\"table\");\n            if (buffer == null)\n                throw new ArgumentNullException(\"buffer\");\n\n            var crc = seed;\n            for (var i = start; i < start + size; i++)\n                unchecked {\n                    var b = buffer[i];\n                    if (b >= 0x41 && b <= 0x5A)\n                        b = (byte)(((uint)b) + 0x20);\n                    crc = (crc >> 8) ^ table[(byte)(b ^ crc)];\n                }\n            return crc;\n        }\n\n        public static uint Compute(uint seed, byte[] buffer, int start, int size) {\n            if (buffer == null)\n                throw new ArgumentNullException(\"buffer\");\n            return Compute(CrcTable, seed, buffer, start, size);\n        }\n\n        public static uint Compute(string value) {\n            if (value == null)\n                throw new ArgumentNullException(\"value\");\n            var b = Encoding.ASCII.GetBytes(value);\n            return Compute(CrcInitialSeed, b, 0, b.Length);\n        }\n\n        #endregion\n\n        #endregion\n\n        #region Sq\n\n        #region Const / Static\n\n        private static readonly Dictionary<string, byte> RootToSqMap =\n            new Dictionary<string, byte> {\n                {\n                    \"common\", 0x00\n                }, {\n                    \"bgcommon\", 0x01\n                }, {\n                    \"bg\", 0x02\n                }, {\n                    \"cut\", 0x03\n                }, {\n                    \"chara\", 0x04\n                }, {\n                    \"shader\", 0x05\n                }, {\n                    \"ui\", 0x06\n                }, {\n                    \"sound\", 0x07\n                }, {\n                    \"vfx\", 0x08\n                }, {\n                    \"ui_script\", 0x09\n                }, {\n                    \"exd\", 0x0a\n                }, {\n                    \"game_script\", 0x0b\n                }, {\n                    \"music\", 0x0c\n                }, {\n                    \"_sqpack_test\", 0x12\n                }, {\n                    \"_debug\", 0x13\n                }\n            };\n\n        private static readonly Dictionary<byte, string> SqToRootMap = RootToSqMap.ToDictionary(_ => _.Value,\n            _ => _.Key);\n\n        public static bool TryGetSqPackKey(string path, out byte key) {\n            var search = path;\n            var i = search.IndexOf('/');\n            if (i >= 0)\n                search = search.Substring(0, i);\n            return RootToSqMap.TryGetValue(search, out key);\n        }\n\n        public static bool TryGetSqPackName(byte key, out string name) {\n            return SqToRootMap.TryGetValue(key, out name);\n        }\n\n        #endregion\n\n        #endregion\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/IO/IIndexFile.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace SaintCoinach.IO {\n    public interface IIndexFile : IEquatable<IIndexFile> {\n        PackIdentifier PackId { get; }\n        uint FileKey { get; }\n        uint Offset { get; }\n        byte DatFile { get; }\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/IO/IPackSource.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace SaintCoinach.IO {\n    public interface IPackSource : IEnumerable<File> {\n        bool FileExists(string path);\n        bool TryGetFile(string path, out File value);\n        File GetFile(string path);\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/IO/Index.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Collections.ObjectModel;\nusing System.Diagnostics;\nusing System.IO;\nusing System.Linq;\nusing System.Text;\n\nnamespace SaintCoinach.IO {\n    using IOFile = System.IO.File;\n\n    /// <summary>\n    ///     Class representing the data inside a *.index file.\n    /// </summary>\n    public class Index {\n        #region Properties\n\n        public PackIdentifier PackId { get; private set; }\n        public IndexHeader Header { get; private set; }\n        public IReadOnlyDictionary<uint, IndexDirectory> Directories { get; private set; }\n\n        #endregion\n\n        #region Constructor\n\n        public Index(PackIdentifier packId, string path) {\n            PackId = packId;\n\n            using (var file = IOFile.OpenRead(path)) {\n                using (var reader = new BinaryReader(file))\n                    Build(reader);\n            }\n        }\n\n        public Index(PackIdentifier packId, Stream stream) {\n            PackId = packId;\n\n            using (var reader = new BinaryReader(stream, Encoding.Default, true))\n                Build(reader);\n        }\n\n        public Index(PackIdentifier packId, BinaryReader reader) {\n            PackId = packId;\n\n            Build(reader);\n        }\n\n        #endregion\n\n        #region Build\n\n        private void Build(BinaryReader reader) {\n            const UInt64 SqPackMagic = 0x00006B6361507153;\n\n            if (!reader.BaseStream.CanSeek)\n                throw new NotSupportedException(\"Stream must be able to seek.\");\n            var fileMagic = reader.ReadUInt64();\n            Debug.Assert(fileMagic == SqPackMagic, \"Input stream is not a SqPack file.\");\n\n            ReadHeader(reader);\n            ReadDirectories(reader);\n        }\n\n        private void ReadHeader(BinaryReader reader) {\n            const int HeaderOffsetOffset = 0x0C;\n\n            reader.BaseStream.Position = HeaderOffsetOffset;\n            var headerOffset = reader.ReadInt32();\n\n            reader.BaseStream.Position = headerOffset;\n            Header = new IndexHeader(reader);\n        }\n\n        private void ReadDirectories(BinaryReader reader) {\n            reader.BaseStream.Position = Header.DirectoriesOffset;\n\n            var rem = Header.DirectoriesCount;\n            var dirs = new List<IndexDirectory>();\n            while (rem-- > 0)\n                dirs.Add(new IndexDirectory(PackId, reader));\n\n            Directories = new ReadOnlyDictionary<uint, IndexDirectory>(dirs.ToDictionary(_ => _.Key, _ => _));\n        }\n\n        #endregion\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/IO/Index2.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Collections.ObjectModel;\nusing System.Diagnostics;\nusing System.IO;\nusing System.Linq;\nusing System.Text;\n\nnamespace SaintCoinach.IO {\n    using IOFile = System.IO.File;\n\n    /// <summary>\n    ///     Class representing the data inside a *.index2 file.\n    /// </summary>\n    public class Index2 {\n        #region Properties\n\n        public Index2Header Header { get; private set; }\n        public IReadOnlyDictionary<uint, Index2File> Files { get; private set; }\n        public PackIdentifier PackId { get; private set; }\n\n        #endregion\n        \n        #region Constructor\n\n        public Index2(PackIdentifier packId, string path) {\n            this.PackId = packId;\n\n            using (var file = IOFile.OpenRead(path)) {\n                using (var reader = new BinaryReader(file))\n                    Build(reader);\n            }\n        }\n\n        public Index2(PackIdentifier packId, Stream stream) {\n            this.PackId = packId;\n\n            using (var reader = new BinaryReader(stream, Encoding.Default, true))\n                Build(reader);\n        }\n\n        public Index2(PackIdentifier packId, BinaryReader reader) {\n            this.PackId = packId;\n\n            Build(reader);\n        }\n\n        #endregion\n\n        #region Build\n\n        private void Build(BinaryReader reader) {\n            const UInt64 SqPackMagic = 0x00006B6361507153;\n\n            if (!reader.BaseStream.CanSeek)\n                throw new NotSupportedException(\"Stream must be able to seek.\");\n            var fileMagic = reader.ReadUInt64();\n            Debug.Assert(fileMagic == SqPackMagic, \"Input stream is not a SqPack file.\");\n\n            ReadHeader(reader);\n            ReadFiles(reader);\n        }\n\n        private void ReadHeader(BinaryReader reader) {\n            const int HeaderOffsetOffset = 0x0C;\n\n            reader.BaseStream.Position = HeaderOffsetOffset;\n            var headerOffset = reader.ReadInt32();\n\n            reader.BaseStream.Position = headerOffset;\n            Header = new Index2Header(reader);\n        }\n\n        private void ReadFiles(BinaryReader reader) {\n            reader.BaseStream.Position = Header.FilesOffset;\n\n            var rem = Header.FileCount;\n            var files = new List<Index2File>();\n            while (rem-- > 0)\n                files.Add(new Index2File(PackId, reader));\n\n            Files = new ReadOnlyDictionary<uint, Index2File>(files.ToDictionary(_ => _.FileKey, _ => _));\n        }\n\n        #endregion\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/IO/Index2File.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.IO;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace SaintCoinach.IO {\n    public class Index2File : IIndexFile {\n        #region Properties\n\n        public PackIdentifier PackId { get; private set; }\n        public uint FileKey { get; private set; }\n        public uint Offset { get; private set; }\n\n        /// <summary>\n        ///     In which .dat* file the data is located.\n        /// </summary>\n        public byte DatFile { get; private set; }\n\n        #endregion\n        \n        #region Constructor\n\n        public Index2File(PackIdentifier packId, BinaryReader reader) {\n            PackId = packId;\n            FileKey = reader.ReadUInt32();\n\n            var baseOffset = reader.ReadInt32();\n            DatFile = (byte)((baseOffset & 0x7) >> 1);\n            Offset = (uint)((baseOffset & 0xFFFFFFF8) << 3);\n        }\n\n        #endregion\n\n        #region IEquatable<IIndexFile> Members\n\n        public override int GetHashCode() {\n            return (int)(((DatFile << 24) | PackId.GetHashCode()) ^ Offset);\n        }\n        public override bool Equals(object obj) {\n            if (obj is IIndexFile)\n                return Equals((IIndexFile)obj);\n            return false;\n        }\n        public bool Equals(IIndexFile other) {\n            return other.PackId.Equals(this.PackId)\n                && other.DatFile == this.DatFile\n                && other.Offset == this.Offset;\n        }\n\n        #endregion\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/IO/Index2Header.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.IO;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace SaintCoinach.IO {\n    /// <summary>\n    ///     Header of an index2 file.\n    /// </summary>\n    public class Index2Header {\n        #region Fields\n\n        private readonly int _FilesCount;\n        private readonly int _FilesOffset;\n\n        #endregion\n\n        #region Properties\n\n        public int FileCount { get { return _FilesCount; } }\n        public int FilesOffset { get { return _FilesOffset; } }\n\n        #endregion\n\n        #region Constructors\n\n        public Index2Header(BinaryReader reader) {\n            const int FileDataOffset = 0x08;\n\n            var start = reader.BaseStream.Position;\n\n            reader.BaseStream.Position = start + FileDataOffset;\n            _FilesOffset = reader.ReadInt32();\n            var filesLength = reader.ReadInt32();\n            _FilesCount = filesLength / 0x08;\n        }\n\n        #endregion\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/IO/Index2Source.Enumerator.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace SaintCoinach.IO {\n    partial class Index2Source {\n        public class Enumerator : IEnumerator<File> {\n            private readonly Index2Source _Source;\n            private readonly IEnumerator<Index2File> _FileEnumerator;\n\n            public Enumerator(Index2Source source) {\n                _Source = source;\n                _FileEnumerator = source.Index.Files.Values.GetEnumerator();\n            }\n\n            #region IEnumerator<File> Members\n\n            public File Current {\n                get { return _Source.GetFile(_FileEnumerator.Current.FileKey); }\n            }\n\n            #endregion\n\n            #region IDisposable Members\n\n            public void Dispose() {\n                _FileEnumerator.Dispose();\n            }\n\n            #endregion\n\n            #region IEnumerator Members\n\n            object System.Collections.IEnumerator.Current {\n                get { return Current; }\n            }\n\n            public bool MoveNext() {\n                return _FileEnumerator.MoveNext();\n            }\n\n            public void Reset() {\n                _FileEnumerator.Reset();\n            }\n\n            #endregion\n        }\n\n        public IEnumerator<File> GetEnumerator() {\n            return new Enumerator(this);\n        }\n        System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() {\n            return GetEnumerator();\n        }\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/IO/Index2Source.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace SaintCoinach.IO {\n    public partial class Index2Source : IPackSource {\n        #region Fields\n\n        private readonly Dictionary<uint, WeakReference<File>> _Files =\n            new Dictionary<uint, WeakReference<File>>();\n\n        private readonly Dictionary<string, uint> _FilePathMap = new Dictionary<string, uint>();\n\n        private readonly Pack _Pack;\n\n        private readonly Index2 _Index;\n\n        #endregion\n\n        #region Properties\n        public Index2 Index { get { return _Index; } }\n        public Pack Pack { get { return _Pack; } }\n        #endregion\n\n        #region Constructor\n        internal Index2Source(Pack pack, Index2 index) {\n            _Pack = pack;\n            _Index = index;\n        }\n        #endregion\n\n        #region IPackSource Members\n\n        public bool FileExists(string path) {\n            uint hash;/*\n            if (!_FilePathMap.TryGetValue(path, out hash))\n                _FilePathMap.Add(path, hash = Hash.Compute(path));*/\n            hash = Hash.Compute(path);\n            return FileExists(hash);\n        }\n\n        public bool FileExists(uint hash) {\n            return Index.Files.ContainsKey(hash);\n        }\n\n        public bool TryGetFile(string path, out File value) {\n            uint hash;/*\n            if (!_FilePathMap.TryGetValue(path, out hash))\n                _FilePathMap.Add(path, hash = Hash.Compute(path));*/\n            hash = Hash.Compute(path);\n            var result = TryGetFile(hash, out value);\n            if (result)\n                value.Path = path;\n            return result;\n        }\n\n        public bool TryGetFile(uint hash, out File value) {\n            if (_Files.TryGetValue(hash, out var fileRef) && fileRef.TryGetTarget(out value))\n                return true;\n\n            if (Index.Files.TryGetValue(hash, out var index)) {\n                value = FileFactory.Get(this.Pack, index);\n                if (_Files.ContainsKey(hash))\n                    _Files[hash].SetTarget(value);\n                else\n                    _Files.Add(hash, new WeakReference<File>(value));\n                return true;\n            }\n\n            value = null;\n            return false;\n        }\n\n        public File GetFile(string path) {\n            uint hash;/*\n            if (!_FilePathMap.TryGetValue(path, out hash))\n                _FilePathMap.Add(path, hash = Hash.Compute(path));*/\n            hash = Hash.Compute(path);\n            var f = GetFile(hash);\n            f.Path = path;\n            return f;\n        }\n\n        public File GetFile(uint hash) {\n            if (_Files.TryGetValue(hash, out var fileRef) && fileRef.TryGetTarget(out var file))\n                return file;\n\n            var index = Index.Files[hash];\n            file = FileFactory.Get(this.Pack, index);\n            if (_Files.ContainsKey(hash))\n                _Files[hash].SetTarget(file);\n            else\n                _Files.Add(hash, new WeakReference<File>(file));\n            return file;\n        }\n\n        #endregion\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/IO/IndexDirectory.cs",
    "content": "﻿using System.Collections.Generic;\nusing System.Collections.ObjectModel;\nusing System.IO;\nusing System.Linq;\n\nnamespace SaintCoinach.IO {\n    /// <summary>\n    ///     Directory-entry inside an index file.\n    /// </summary>\n    public class IndexDirectory {\n        #region Properties\n\n        public PackIdentifier PackId { get; private set; }\n        public uint Key { get; private set; }\n        public int Offset { get; private set; }\n        public int Count { get; private set; }\n        public IReadOnlyDictionary<uint, IndexFile> Files { get; private set; }\n\n        #endregion\n\n        #region Constructors\n\n        public IndexDirectory(PackIdentifier packId, BinaryReader reader) {\n            PackId = packId;\n\n            ReadMeta(reader);\n            var pos = reader.BaseStream.Position;\n            ReadFiles(reader);\n            reader.BaseStream.Position = pos;\n        }\n\n        #endregion\n\n        #region Build\n\n        private void ReadMeta(BinaryReader reader) {\n            Key = reader.ReadUInt32();\n            Offset = reader.ReadInt32();\n            var len = reader.ReadInt32();\n            Count = len / 0x10;\n\n            reader.ReadInt32(); // Zero\n        }\n\n        private void ReadFiles(BinaryReader reader) {\n            reader.BaseStream.Position = Offset;\n\n            var rem = Count;\n            var files = new List<IndexFile>();\n            while (rem-- > 0)\n                files.Add(new IndexFile(PackId, reader));\n\n            Files = new ReadOnlyDictionary<uint, IndexFile>(files.ToDictionary(_ => _.FileKey, _ => _));\n        }\n\n        #endregion\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/IO/IndexFile.cs",
    "content": "﻿using System.IO;\n\nnamespace SaintCoinach.IO {\n    /// <summary>\n    ///     File entry inside an index file.\n    /// </summary>\n    public class IndexFile : IIndexFile {\n        #region Properties\n\n        public PackIdentifier PackId { get; private set; }\n        public uint FileKey { get; private set; }\n        public uint DirectoryKey { get; private set; }\n        public uint Offset { get; private set; }\n\n        /// <summary>\n        ///     In which .dat* file the data is located.\n        /// </summary>\n        public byte DatFile { get; private set; }\n\n        #endregion\n\n        #region Constructors\n\n        public IndexFile(PackIdentifier packId, BinaryReader reader) {\n            PackId = packId;\n            FileKey = reader.ReadUInt32();\n            DirectoryKey = reader.ReadUInt32();\n\n            var baseOffset = reader.ReadInt32();\n            DatFile = (byte)((baseOffset & 0x000F) / 2);\n            Offset = (uint)(baseOffset - (baseOffset & 0x000F)) * 0x08;\n\n            reader.ReadInt32(); // Zero\n        }\n\n        #endregion\n\n        #region IEquatable<IIndexFile> Members\n\n        public override int GetHashCode() {\n            return (int)(((DatFile << 24) | PackId.GetHashCode()) ^ Offset);\n        }\n        public override bool Equals(object obj) {\n            if (obj is IIndexFile)\n                return Equals((IIndexFile)obj);\n            return false;\n        }\n        public bool Equals(IIndexFile other) {\n            return other.PackId.Equals(this.PackId)\n                && other.DatFile == this.DatFile\n                && other.Offset == this.Offset;\n        }\n\n        #endregion\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/IO/IndexHeader.cs",
    "content": "﻿using System.IO;\n\nnamespace SaintCoinach.IO {\n    /// <summary>\n    ///     Header of an index file.\n    /// </summary>\n    public struct IndexHeader {\n        #region Fields\n\n        private readonly int _DirectoriesCount;\n        private readonly int _DirectoriesOffset;\n        private readonly int _FilesCount;\n        private readonly int _FilesOffset;\n\n        #endregion\n\n        #region Properties\n\n        public int DirectoriesCount { get { return _DirectoriesCount; } }\n        public int DirectoriesOffset { get { return _DirectoriesOffset; } }\n        public int FilesCount { get { return _FilesCount; } }\n        public int FilesOffset { get { return _FilesOffset; } }\n\n        #endregion\n\n        #region Constructors\n\n        public IndexHeader(BinaryReader reader) {\n            const int FileDataOffset = 0x08;\n            const int DirectoryDataOffset = 0xE4;\n\n            var start = reader.BaseStream.Position;\n\n            reader.BaseStream.Position = start + FileDataOffset;\n            _FilesOffset = reader.ReadInt32();\n            var filesLength = reader.ReadInt32();\n            _FilesCount = filesLength / 0x10;\n\n            reader.BaseStream.Position = start + DirectoryDataOffset;\n            _DirectoriesOffset = reader.ReadInt32();\n            var dirLength = reader.ReadInt32();\n            _DirectoriesCount = dirLength / 0x10;\n        }\n\n        #endregion\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/IO/IndexSource.Enumerator.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace SaintCoinach.IO {\n    partial class IndexSource {\n        public class Enumerator : IEnumerator<File> {\n            private readonly IndexSource _Source;\n            private IEnumerator<IndexDirectory> _DirectoryEnumerator;\n            private IEnumerator<IndexFile> _FileEnumerator;\n\n            public Enumerator(IndexSource source) {\n                _Source = source;\n                _DirectoryEnumerator = source.Index.Directories.Values.GetEnumerator();\n            }\n\n            #region IEnumerator<File> Members\n\n            public File Current {\n                get {\n                    var index = _FileEnumerator.Current;\n                    return _Source.GetFile(index.DirectoryKey, index.FileKey);\n                }\n            }\n\n            #endregion\n\n            #region IDisposable Members\n\n            public void Dispose() {\n                if (_DirectoryEnumerator != null)\n                    _DirectoryEnumerator.Dispose();\n                _DirectoryEnumerator = null;\n\n                if (_FileEnumerator != null)\n                    _FileEnumerator.Dispose();\n                _FileEnumerator = null;\n            }\n\n            #endregion\n\n            #region IEnumerator Members\n\n            object System.Collections.IEnumerator.Current {\n                get { return Current; }\n            }\n\n            public bool MoveNext() {\n                while (_FileEnumerator == null || !_FileEnumerator.MoveNext()) {\n                    if (_FileEnumerator != null)\n                        _FileEnumerator.Dispose();\n                    _FileEnumerator = null;\n\n                    if (!_DirectoryEnumerator.MoveNext())\n                        return false;\n\n                    _FileEnumerator = _DirectoryEnumerator.Current.Files.Values.GetEnumerator();\n                }\n                return true;\n            }\n\n            public void Reset() {\n                _DirectoryEnumerator.Reset();\n                if (_FileEnumerator != null)\n                    _FileEnumerator.Dispose();\n                _FileEnumerator = null;\n            }\n\n            #endregion\n        }\n\n        public IEnumerator<File> GetEnumerator() {\n            return new Enumerator(this);\n        }\n        System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() {\n            return GetEnumerator();\n        }\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/IO/IndexSource.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace SaintCoinach.IO {\n    public partial class IndexSource : IPackSource {\n        #region Fields\n\n        private readonly Dictionary<uint, Directory> _Directories =\n            new Dictionary<uint, Directory>();\n\n        private readonly Dictionary<string, uint> _DirectoryPathMap = new Dictionary<string, uint>();\n\n        private readonly Pack _Pack;\n\n        private readonly Index _Index;\n\n        #endregion\n\n        #region Properties\n        public Index Index { get { return _Index; } }\n        public Pack Pack { get { return _Pack; } }\n        #endregion\n\n        #region Constructor\n        internal IndexSource(Pack pack, Index index) {\n            _Pack = pack;\n            _Index = index;\n        }\n        #endregion\n\n        #region Get\n\n        public bool DirectoryExists(string path) {\n            uint hash;/*\n            if (!_DirectoryPathMap.TryGetValue(path, out hash))\n                _DirectoryPathMap.Add(path, hash = Hash.Compute(path));*/\n            hash = Hash.Compute(path);\n            return DirectoryExists(hash);\n        }\n\n        public bool DirectoryExists(uint key) {\n            return Index.Directories.ContainsKey(key);\n        }\n\n        public Directory GetDirectory(string path) {\n            uint hash;/*\n            if (!_DirectoryPathMap.TryGetValue(path, out hash))\n                _DirectoryPathMap.Add(path, hash = Hash.Compute(path));*/\n            hash = Hash.Compute(path);\n\n            var dir = GetDirectory(hash);\n            dir.Path = path;\n            return dir;\n        }\n\n        public Directory GetDirectory(uint key) {\n            if (_Directories.TryGetValue(key, out var directory))\n                return directory;\n\n            var index = Index.Directories[key];\n            directory = new Directory(this.Pack, index);\n            _Directories.Add(key, directory);\n            return directory;\n        }\n\n        public bool TryGetDirectory(string path, out Directory directory) {\n            uint hash;/*\n            if (!_DirectoryPathMap.TryGetValue(path, out hash))\n                _DirectoryPathMap.Add(path, hash = Hash.Compute(path));*/\n            hash = Hash.Compute(path);\n\n            var result = TryGetDirectory(hash, out directory);\n            if (result)\n                directory.Path = path;\n            return result;\n        }\n\n        public bool TryGetDirectory(uint key, out Directory directory) {\n            if (_Directories.TryGetValue(key, out directory))\n                return true;\n\n            if (Index.Directories.TryGetValue(key, out var index)) {\n                directory = new Directory(this.Pack, index);\n                _Directories.Add(key, directory);\n                return true;\n            }\n\n            directory = null;\n            return false;\n        }\n\n        public bool FileExists(string path) {\n            var lastSeperator = path.LastIndexOf('/');\n            if (lastSeperator < 0)\n                throw new ArgumentException();\n\n            var dirPath = path.Substring(0, lastSeperator);\n            var baseName = path.Substring(lastSeperator + 1);\n            return TryGetDirectory(dirPath, out var dir) && dir.FileExists(baseName);\n        }\n\n        public File GetFile(string path) {\n            var lastSeperator = path.LastIndexOf('/');\n            if (lastSeperator < 0)\n                throw new ArgumentException();\n\n            var dirPath = path.Substring(0, lastSeperator);\n            var baseName = path.Substring(lastSeperator + 1);\n            var dir = GetDirectory(dirPath);\n            return dir.GetFile(baseName);\n        }\n\n        public File GetFile(uint directoryKey, uint fileKey) {\n            var dir = GetDirectory(directoryKey);\n            return dir.GetFile(fileKey);\n        }\n\n        public bool TryGetFile(string path, out File file) {\n            var lastSeperator = path.LastIndexOf('/');\n            if (lastSeperator < 0) {\n                file = null;\n                return false;\n            }\n\n            var dirPath = path.Substring(0, lastSeperator);\n            var baseName = path.Substring(lastSeperator + 1);\n            if (TryGetDirectory(dirPath, out var dir))\n                return dir.TryGetFile(baseName, out file);\n\n            file = null;\n            return false;\n        }\n\n        public bool TryGetFile(uint directoryKey, uint fileKey, out File file) {\n            if (TryGetDirectory(directoryKey, out var dir))\n                return dir.TryGetFile(fileKey, out file);\n\n            file = null;\n            return false;\n        }\n\n        #endregion\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/IO/Pack.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.IO;\nusing System.Linq;\nusing System.Threading;\n\nnamespace SaintCoinach.IO {\n    using IOFile = System.IO.File;\n\n    /// <summary>\n    ///     Class for a SqPack.\n    /// </summary>\n    public partial class Pack : IEnumerable<File> {\n        #region Static\n\n        private const string IndexFileFormat = \"{0:x2}{1:x2}{2:x2}.win32.index\";\n        private const string Index2FileFormat = \"{0:x2}{1:x2}{2:x2}.win32.index2\";\n        private const string DatFileFormat = \"{0:x2}{1:x2}{2:x2}.win32.dat{3}\";\n\n        #endregion\n\n        #region Fields\n\n        private readonly Dictionary<Tuple<Thread, byte>, WeakReference<Stream>> _DataStreams =\n            new Dictionary<Tuple<Thread, byte>, WeakReference<Stream>>();\n\n        private bool _KeepInMemory = false;\n        private Dictionary<int, byte[]> _Buffers = new Dictionary<int,byte[]>();\n\n        #endregion\n\n        #region Properties\n\n        public PackIdentifier Id { get; private set; }\n        public PackCollection Collection { get; private set; }\n        public DirectoryInfo DataDirectory { get; private set; }\n        public IPackSource Source { get; private set; }\n        public bool KeepInMemory {\n            get { return _KeepInMemory; }\n            set {\n                if (value == _KeepInMemory)\n                    return;\n\n                if (_DataStreams.Any(i => i.Value.TryGetTarget(out var t))) {\n                    Console.WriteLine($\"Failed to keep pack {Id} in memory.\");\n                    return;\n                }\n                _DataStreams.Clear();\n\n                _KeepInMemory = value;\n                if (!value)\n                    _Buffers.Clear();\n            }\n        }\n\n        #endregion\n\n        #region Helpers\n\n        public Stream GetDataStream(byte datFile = 0) {\n            var thread = Thread.CurrentThread;\n\n            var key = Tuple.Create(thread, datFile);\n            WeakReference<Stream> streamRef;\n            lock (_DataStreams)\n                _DataStreams.TryGetValue(key, out streamRef);\n\n            if (streamRef == null || !streamRef.TryGetTarget(out var stream))\n                stream = null;\n\n            if (stream != null) return stream;\n\n            var baseName = String.Format(DatFileFormat, Id.TypeKey, Id.ExpansionKey, Id.Number, datFile);\n            var fullPath = Path.Combine(DataDirectory.FullName, Id.Expansion, baseName);\n\n\n            if (KeepInMemory) {\n                if (!_Buffers.ContainsKey(datFile)) {\n                    _Buffers.Add(datFile, IOFile.ReadAllBytes(fullPath));\n                }\n                stream = new MemoryStream(_Buffers[datFile], false);\n            } else\n                stream = IOFile.OpenRead(fullPath);\n\n            lock (_DataStreams) {\n                if (_DataStreams.ContainsKey(key))\n                    _DataStreams[key].SetTarget(stream);\n                else\n                    _DataStreams.Add(key, new WeakReference<Stream>(stream));\n            }\n\n            return stream;\n        }\n\n        #endregion\n\n        public override string ToString() {\n            return string.Format(\"{0}/{1:x2}{2:x2}{3:x2}\", Id.Expansion, Id.TypeKey, Id.ExpansionKey, Id.Number);\n        }\n\n\n        #region Constructor\n\n        public Pack(string dataPath, PackIdentifier id) : this(null, new DirectoryInfo(dataPath), id) { }\n        public Pack(DirectoryInfo dataDirectory, PackIdentifier id) : this(null, dataDirectory, id) { }\n\n        public Pack(PackCollection collection, string dataPath, PackIdentifier id)\n            : this(collection, new DirectoryInfo(dataPath), id) { }\n\n        public Pack(PackCollection collection, DirectoryInfo dataDirectory, PackIdentifier id) {\n            if (dataDirectory == null)\n                throw new ArgumentNullException(\"dataDirectory\");\n            if (!dataDirectory.Exists)\n                throw new DirectoryNotFoundException();\n\n            Collection = collection;\n            DataDirectory = dataDirectory;\n            this.Id = id;\n\n            var indexPath = Path.Combine(DataDirectory.FullName, id.Expansion, string.Format(IndexFileFormat, Id.TypeKey, Id.ExpansionKey, Id.Number));\n            var index2Path = Path.Combine(DataDirectory.FullName, id.Expansion, string.Format(Index2FileFormat, Id.TypeKey, Id.ExpansionKey, Id.Number));\n            if (IOFile.Exists(indexPath))\n                Source = new IndexSource(this, new Index(id, indexPath));\n            else if (IOFile.Exists(index2Path))\n                Source = new Index2Source(this, new Index2(id, index2Path));\n            else\n                throw new FileNotFoundException();\n        }\n\n        #endregion\n\n        #region Fields\n\n        public bool FileExists(string path) {\n            return Source.FileExists(path);\n        }\n        public bool TryGetFile(string path, out File value) {\n            return Source.TryGetFile(path, out value);\n        }\n        public File GetFile(string path) {\n            return Source.GetFile(path);\n        }\n\n        #endregion\n\n        #region IEnumerable<File> Members\n\n        public IEnumerator<File> GetEnumerator() {\n            return Source.GetEnumerator();\n        }\n\n        #endregion\n\n        #region IEnumerable Members\n\n        System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() {\n            return GetEnumerator();\n        }\n\n        #endregion\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/IO/PackCollection.cs",
    "content": "﻿using System;\nusing System.Collections.Concurrent;\nusing System.Collections.Generic;\nusing System.IO;\n\nnamespace SaintCoinach.IO {\n    public class PackCollection {\n        #region Fields\n\n        private readonly ConcurrentDictionary<PackIdentifier, Pack> _Packs = new ConcurrentDictionary<PackIdentifier, Pack>();\n\n        #endregion\n\n        #region Properties\n\n        public DirectoryInfo DataDirectory { get; private set; }\n\n        public IEnumerable<Pack> Packs { get { return _Packs.Values; } }\n\n        #endregion\n\n        #region Constructor\n\n        public PackCollection(string dataDirectory) : this(new DirectoryInfo(dataDirectory)) { }\n\n        public PackCollection(DirectoryInfo dataDirectory) {\n            if (dataDirectory == null)\n                throw new ArgumentNullException(\"dataDirectory\");\n            if (!dataDirectory.Exists)\n                throw new DirectoryNotFoundException();\n\n            DataDirectory = dataDirectory;\n        }\n\n        #endregion\n\n        #region Get\n\n        public bool FileExists(string path) {\n            return TryGetPack(path, out var pack) && pack.FileExists(path);\n        }\n\n        public File GetFile(string path) {\n            var pack = GetPack(path);\n            return pack.GetFile(path);\n        }\n\n        public Pack GetPack(PackIdentifier id) {\n            return _Packs.GetOrAdd(id, i => new Pack(this, DataDirectory, id));\n        }\n\n        public Pack GetPack(string path) {\n            var id = PackIdentifier.Get(path);\n            return GetPack(id);\n        }\n\n        public bool TryGetFile(string path, out File file) {\n            if (TryGetPack(path, out var pack))\n                return pack.TryGetFile(path, out file);\n\n            file = null;\n            return false;\n        }\n\n        public bool TryGetPack(string path, out Pack pack) {\n            pack = null;\n\n            if (!PackIdentifier.TryGet(path, out var id))\n                return false;\n\n            pack =_Packs.GetOrAdd(id, i => new Pack(this, DataDirectory, id));\n            return true;\n        }\n\n        #endregion\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/IO/PackIdentifier.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace SaintCoinach.IO {\n    public struct PackIdentifier {\n        #region Static\n        public const string DefaultExpansion = \"ffxiv\";\n\n        public static readonly Dictionary<string, byte> TypeToKeyMap =\n            new Dictionary<string, byte> {\n                { \"common\", 0x00 },\n                { \"bgcommon\", 0x01 },\n                { \"bg\", 0x02 }, \n                { \"cut\", 0x03 }, \n                { \"chara\", 0x04 },\n                { \"shader\", 0x05 }, \n                { \"ui\", 0x06 }, \n                { \"sound\", 0x07 },\n                { \"vfx\", 0x08 }, \n                /*{ \"ui_script\", 0x09 },*/\n                { \"exd\", 0x0a },\n                { \"game_script\", 0x0b },\n                { \"music\", 0x0c },\n                { \"_sqpack_test\", 0x12 }, \n                { \"_debug\", 0x13 },\n            };\n\n        public static readonly Dictionary<byte, string> KeyToTypeMap = TypeToKeyMap.ToDictionary(_ => _.Value, _ => _.Key);\n\n        public static readonly Dictionary<string, byte> ExpansionToKeyMap =\n            new Dictionary<string, byte> {\n                { \"ffxiv\", 0x00 },\n                { \"ex1\", 0x01 },\n                { \"ex2\", 0x02 },\n                { \"ex3\", 0x03 },\n                { \"ex4\", 0x04 },\n                { \"ex5\", 0x05 }\n            };\n        public static readonly Dictionary<byte, string> KeyToExpansionMap = ExpansionToKeyMap.ToDictionary(_ => _.Value, _ => _.Key);\n        #endregion\n\n        private readonly string _Type;\n        private readonly byte _TypeKey;\n        private readonly string _Expansion;\n        private readonly byte _ExpansionKey;\n        private readonly byte _Number;\n\n        public string Type { get { return _Type; } }\n        public byte TypeKey { get { return _TypeKey; } }\n        public string Expansion { get { return _Expansion; } }\n        public byte ExpansionKey { get { return _ExpansionKey; } }\n        public byte Number { get { return _Number; } }\n\n        public PackIdentifier(byte type, byte expansion, byte number) {\n            _TypeKey = type;\n            _ExpansionKey = expansion;\n            _Number = number;\n\n            _Type = KeyToTypeMap[type];\n            _Expansion = KeyToExpansionMap[expansion];\n        }\n        public PackIdentifier(string type, string expansion, byte number) {\n            _Type = type;\n            _Expansion = expansion;\n            _Number = number;\n\n            _TypeKey = TypeToKeyMap[type];\n            _ExpansionKey = ExpansionToKeyMap[expansion];\n        }\n\n        public override int GetHashCode() {\n            return TypeKey << 16 | ExpansionKey << 8 | Number;\n        }\n        public override bool Equals(object obj) {\n            if (obj is PackIdentifier)\n                return Equals((PackIdentifier)obj);\n            return false;\n        }\n        public bool Equals(PackIdentifier other) {\n            return other.TypeKey == this.TypeKey && other.ExpansionKey == this.ExpansionKey && other.Number == this.Number;\n        }\n\n        public static PackIdentifier Get(string fullPath) {\n            if (!TryGet(fullPath, out var id))\n                throw new ArgumentException();\n            return id;\n        }\n        public static bool TryGet(string fullPath, out PackIdentifier value) {\n            value = default(PackIdentifier);\n\n            var typeSep = fullPath.IndexOf('/');\n            if (typeSep <= 0)\n                return false;\n            var type = fullPath.Substring(0, typeSep);\n            if (!TypeToKeyMap.ContainsKey(type))\n                return false;\n\n            var expSep = fullPath.IndexOf('/', typeSep + 1);\n\n            string expansion = null;\n            byte number = 0;\n            if (expSep > typeSep) {\n                expansion = fullPath.Substring(typeSep + 1, expSep - typeSep - 1);\n                var numberEnd = fullPath.IndexOf('_', expSep);\n                if (numberEnd - expSep == 3) {\n                    if (!byte.TryParse(\n                        fullPath.Substring(expSep + 1, 2),\n                        System.Globalization.NumberStyles.HexNumber | System.Globalization.NumberStyles.AllowHexSpecifier,\n                        System.Globalization.CultureInfo.CurrentCulture,\n                        out number))\n                        number = 0;\n                }\n            }\n\n            if (expansion == null || !ExpansionToKeyMap.ContainsKey(expansion))\n                expansion = DefaultExpansion;\n\n            value = new PackIdentifier(type, expansion, number);\n            return true;\n        }\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Imaging/GftdEntry.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Runtime.InteropServices;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace SaintCoinach.Imaging {\n    // TODO: Come up with a better name for this.\n    [StructLayout(LayoutKind.Sequential)]\n    public struct GftdEntry {\n        public short Id;\n        public short X;\n        public short Y;\n        public short Width;\n        public short Height;\n        public short Unknown1;\n        public short Unknown2;\n        public short Unknown3;\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Imaging/GraphicsFileTextureDefinition.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Collections.ObjectModel;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace SaintCoinach.Imaging {\n    // TODO: This name is what I think the .gfd extension and gftd identifier may stand for.\n    public class GraphicsFileTextureDefinition : IEnumerable<GftdEntry> {\n        #region Fields\n        private Dictionary<short, GftdEntry> _Entries = new Dictionary<short, GftdEntry>();\n        private ReadOnlyDictionary<short, GftdEntry> _ROEntries;\n        #endregion\n\n        #region Properties\n        public IO.File File { get; private set; }\n        public IReadOnlyDictionary<short, GftdEntry> Entries { get { return _ROEntries ?? (_ROEntries = new ReadOnlyDictionary<short, GftdEntry>(_Entries)); } }\n        #endregion\n\n        #region Constructor\n        public GraphicsFileTextureDefinition(IO.File file) {\n            this.File = file;\n\n            var buffer = file.GetData();\n            if (BitConverter.ToUInt64(buffer, 0) != 0x3030313064746667) // gftd0100\n                throw new ArgumentException();\n\n            var count = BitConverter.ToInt32(buffer, 8);\n\n            var offset = 0x10;\n            while (--count >= 0) {\n                var entry = buffer.ToStructure<GftdEntry>(ref offset);\n                _Entries.Add(entry.Id, entry);\n            }\n        }\n        #endregion\n\n        #region IEnumerable<GftdEntry> Members\n\n        IEnumerator<GftdEntry> IEnumerable<GftdEntry>.GetEnumerator() {\n            return _Entries.Values.GetEnumerator();\n        }\n\n        #endregion\n\n        #region IEnumerable Members\n\n        System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() {\n            return _Entries.Values.GetEnumerator();\n        }\n\n        #endregion\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Imaging/IconHelper.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace SaintCoinach.Imaging {\n    using Ex;\n    using IO;\n\n    public static class IconHelper {\n        static bool PreferHighRes = false;\n\n        const string IconFileFormat = \"ui/icon/{0:D3}000/{1}{2:D6}.tex\";\n        const string IconHr1FileFormat = \"ui/icon/{0:D3}000/{1}{2:D6}_hr1.tex\";\n\n        private static File GetIconFile(IO.PackCollection pack, string filePathFormat, string type, int nr) {\n            var filePath = string.Format(filePathFormat, nr / 1000, type, nr);\n            if (!pack.TryGetFile(filePath, out var file) && type.Length > 0) {\n                // Couldn't get specific type, try for generic version.\n                filePath = string.Format(filePathFormat, nr / 1000, string.Empty, nr);\n                if (!pack.TryGetFile(filePath, out file)) {\n                    // Couldn't get generic version either, that's a shame.\n                    file = null;\n                }\n            }\n            return file;\n        }\n\n        public static Imaging.ImageFile GetIcon(IO.PackCollection pack, int nr) {\n            return GetIcon(pack, string.Empty, nr);\n        }\n        public static Imaging.ImageFile GetIcon(IO.PackCollection pack, Language language, int nr) {\n            var type = language.GetCode();\n            if (type.Length > 0)\n                type = type + \"/\";\n            return GetIcon(pack, type, nr);\n        }\n        public static Imaging.ImageFile GetIcon(IO.PackCollection pack, string type, int nr) {\n            type = type ?? string.Empty;\n            if (type.Length > 0 && !type.EndsWith(\"/\"))\n                type = type + \"/\";\n\n            File file = null;\n            // we prefer hr1 then.\n            if (PreferHighRes) {\n                file = GetIconFile(pack, IconHr1FileFormat, type, nr);\n            }\n            if (file == null) {\n                file = GetIconFile(pack, IconFileFormat, type, nr);\n            }\n\n            return file as Imaging.ImageFile;\n        }\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Imaging/ImageConverter.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Drawing;\nusing System.Drawing.Imaging;\nusing System.Drawing.Text;\nusing BCnEncoder.Decoder;\nusing BCnEncoder.Shared;\nusing DotSquish;\n\nnamespace SaintCoinach.Imaging {\n\n    struct DDS_PIXELFORMAT {\n        public uint dwSize;\n        public uint dwFlags;\n        public uint dwFourCC;\n        public uint dwRGBBitCount;\n        public uint dwRBitMask;\n        public uint dwGBitMask;\n        public uint dwBBitMask;\n        public uint dwABitMask;\n    };\n\n    struct DDS_HEADER {\n        public uint dwSize;\n        public uint dwFlags;\n        public uint dwHeight;\n        public uint dwWidth;\n        public uint dwPitchOrLinearSize;\n        public uint dwDepth;\n        public uint dwMipMapCount;\n        public uint dwReserved0;\n        public uint dwReserved1;\n        public uint dwReserved2;\n        public uint dwReserved3;\n        public uint dwReserved4;\n        public uint dwReserved5;\n        public uint dwReserved6;\n        public uint dwReserved7;\n        public uint dwReserved8;\n        public uint dwReserved19;\n        public uint dwReserved10;\n\n        public DDS_PIXELFORMAT ddspf;\n        public uint dwCaps;\n        public uint dwCaps2;\n        public uint dwCaps3;\n        public uint dwCaps4;\n        public uint dwReserved11;\n    };\n\n    enum DDSD_ENUM : uint {\n        DDSD_CAPS = 0x1,//Required in every.dds file. \t\n        DDSD_HEIGHT = 0x2,//Required in every.dds file.\n        DDSD_WIDTH = 0x4,//Required in every.dds file.\n        DDSD_PITCH = 0x8,//Required when pitch is provided for an uncompressed texture.\n        DDSD_PIXELFORMAT = 0x1000,//Required in every.dds file.\n        DDSD_MIPMAPCOUNT = 0x20000,//Required in a mipmapped texture.\n        DDSD_LINEARSIZE = 0x80000,//Required when pitch is provided for a compressed texture.\n        DDSD_DEPTH = 0x800000,//Required in a depth texture\n    }\n\n    enum DDPF_ENUM : uint {\n        DDPF_ALPHAPIXELS = 0x1, // Texture contains alpha data; dwRGBAlphaBitMask contains valid data.\n        DDPF_ALPHA = 0x2, // Used in some older DDS files for alpha channel only uncompressed data (dwRGBBitCount contains the alpha channel bitcount; dwABitMask contains valid data)\n        DDPF_FOURCC = 0x4, // Texture contains compressed RGB data; dwFourCC contains valid data.\n        DDPF_RGB = 0x40, // Texture contains uncompressed RGB data; dwRGBBitCountand the RGB masks(dwRBitMask, dwGBitMask, dwBBitMask) contain valid data.\n    }\n    /// <summary>\n    ///     Helper class for converting image files as stored in SqPack\n    ///     to formats useable in .NET\n    /// </summary>\n    public class ImageConverter {\n        /// <summary>\n        ///     Method signature for processing data as stored in SqPack into ARGB.\n        /// </summary>\n        /// <param name=\"src\">Source buffer.</param>\n        /// <param name=\"dst\">_Buffer to write into.</param>\n        /// <param name=\"width\">Width of the image.</param>\n        /// <param name=\"height\">Height of the image.</param>\n        private delegate void Preprocessor(byte[] src, byte[] dst, int width, int height);\n\n        private static readonly Dictionary<ImageFormat, Preprocessor> Preprocessors =\n            new Dictionary<ImageFormat, Preprocessor> {\n                {\n                    ImageFormat.A16R16G16B16Float, ProcessA16R16G16B16_Float\n                }, {\n                    ImageFormat.A1R5G5B5, ProcessA1R5G5B5\n                }, {\n                    ImageFormat.A4R4G4B4, ProcessA4R4G4B4\n                }, {\n                    ImageFormat.A8R8G8B8_1, ProcessA8R8G8B8\n                }, {\n                    ImageFormat.A8R8G8B8_2, ProcessA8R8G8B8\n                }, {\n                    ImageFormat.A8R8G8B8_Cube, ProcessA8R8G8B8\n                }, {\n                    ImageFormat.A8R8G8B8_4, ProcessA8R8G8B8\n                }, {\n                    ImageFormat.A8R8G8B8_5, ProcessA8R8G8B8\n                }, {\n                    ImageFormat.Dxt1, ProcessDxt1\n                }, {\n                    ImageFormat.Dxt3, ProcessDxt3\n                }, {\n                    ImageFormat.Dxt5, ProcessDxt5\n                }, {\n                    ImageFormat.R3G3B2, ProcessR3G3B2\n                }, {\n                    ImageFormat.BC5, ProcessBC5\n                }, {\n                    ImageFormat.BC7, ProcessBC7\n                }\n            };\n\n        #region Convert\n\n        public static Image Convert(ImageFile file) {\n            return Convert(file.GetData(), file.Format, file.Width, file.Height);\n        }\n\n        public static unsafe Image Convert(byte[] src, ImageFormat format, int width, int height) {\n            var argb = GetA8R8G8B8(src, format, width, height);\n\n            Image image;\n            fixed (byte* p = argb) {\n                var ptr = (IntPtr)p;\n                using (var tempImage = new Bitmap(width, height, width * 4, PixelFormat.Format32bppArgb, ptr))\n                    image = new Bitmap(tempImage);\n            }\n            return image;\n        }\n\n        public static byte[] GetA8R8G8B8(ImageFile file) {\n            return GetA8R8G8B8(file.GetData(), file.Format, file.Width, file.Height);\n        }\n\n        public static byte[] GetA8R8G8B8(byte[] src, ImageFormat format, int width, int height) {\n            if (!Preprocessors.TryGetValue(format, out var proc))\n                throw new NotSupportedException(string.Format(\"Unsupported image format {0}\", format));\n\n            var argb = new byte[width * height * 4];\n            proc(src, argb, width, height);\n            return argb;\n        }\n\n        /// <summary>\n        /// Attempt to convert ImageFile to DDS file. Returns null if no DXT1/3/5 texture found.\n        /// </summary>\n\n        public static byte[] GetDDS(ImageFile file) {\n            var bytes2 = file.GetData();\n            //var offset = bytes2[file.ImageHeader.EndOfHeader];\n            var width = file.ImageHeader.Width;\n            var height = file.ImageHeader.Height;\n\n            DDS_HEADER header = new DDS_HEADER();\n            DDS_PIXELFORMAT format = header.ddspf;\n\n\n            format.dwFlags = (uint)(DDPF_ENUM.DDPF_ALPHAPIXELS | DDPF_ENUM.DDPF_FOURCC);\n            header.dwFlags |= (uint)(DDSD_ENUM.DDSD_CAPS | DDSD_ENUM.DDSD_HEIGHT | DDSD_ENUM.DDSD_WIDTH | DDSD_ENUM.DDSD_PIXELFORMAT | DDSD_ENUM.DDSD_LINEARSIZE);\n            header.dwFlags |= (uint)DDSD_ENUM.DDSD_MIPMAPCOUNT;\n\n            switch (file.ImageHeader.Format) {\n                case ImageFormat.Dxt1:\n                    format.dwFourCC = 0x31545844;\n                    header.dwPitchOrLinearSize = (uint)(Math.Max((uint)1, ((width + 3) / 4)) * Math.Max((uint)1, ((height + 3) / 4)) * 8);\n                    break;\n                case ImageFormat.Dxt3:\n                    format.dwFourCC = 0x33545844;\n                    header.dwPitchOrLinearSize = (uint)(Math.Max((uint)1, ((width + 3) / 4)) * Math.Max((uint)1, ((height + 3) / 4)) * 16);\n                    break;\n                case ImageFormat.Dxt5:\n                    format.dwFourCC = 0x35545844;\n                    header.dwPitchOrLinearSize = (uint)(Math.Max((uint)1, ((width + 3) / 4)) * Math.Max((uint)1, ((height + 3) / 4)) * 16);\n                    break;\n                /*\n                case ImageFormat.A8R8G8B8_1:\n                case ImageFormat.A8R8G8B8_2:\n                case ImageFormat.A8R8G8B8_4:\n                case ImageFormat.A8R8G8B8_5:\n                    format.dwFlags = (uint)(DDPF_ENUM.DDPF_ALPHA | DDPF_ENUM.DDPF_RGB);\n                    format.dwFourCC = 0x42475241;\n                    header.dwPitchOrLinearSize = (uint)(width * height);\n                    header.dwFlags &= ~(uint)DDSD_ENUM.DDSD_LINEARSIZE;\n                    header.dwFlags |= (uint)DDSD_ENUM.DDSD_PITCH;\n                    break;\n                */\n                default:\n                    System.Diagnostics.Debug.WriteLine(\"Texture format \" + file.ImageHeader.Format.ToString() + \" DDS export not supported!\\n\");\n                    return null;\n                    break;\n            }\n\n            format.dwSize = 32;\n\n            header.dwSize = 124; // why set this if it MUST be 124?\n            header.dwHeight = (uint)height;\n            header.dwWidth = (uint)width;\n            header.dwMipMapCount = file.ImageHeader._Buffer[0x0E];\n            header.dwCaps = 0x08 | 0x400000 | 0x1000; // DDSCAPS_COMPLEX | DDSCAPS_MIPMAP | DDSCAPS_TEXTURE\n\n            header.ddspf = format;\n\n            List<byte> data = new List<byte>();\n\n            int size = System.Runtime.InteropServices.Marshal.SizeOf<DDS_HEADER>();\n            byte[] headerBytes = new byte[size];\n\n            var ptr = System.Runtime.InteropServices.Marshal.AllocHGlobal(size);\n            System.Runtime.InteropServices.Marshal.StructureToPtr(header, ptr, false);\n            System.Runtime.InteropServices.Marshal.Copy(ptr, headerBytes, 0, size);\n            System.Runtime.InteropServices.Marshal.FreeHGlobal(ptr);\n\n            data.AddRange(System.Text.ASCIIEncoding.UTF8.GetBytes(\"DDS \"));\n            data.AddRange(headerBytes);\n            data.AddRange(bytes2);\n\n            return data.ToArray();\n        }\n\n        #endregion\n\n        #region Preprocessors\n\n        private static void ProcessA16R16G16B16_Float(byte[] src, byte[] dst, int width, int height) {\n            // Clipping can, and will occur since values go outside 0..1\n            for (var i = 0; i < width * height; ++i) {\n                var srcOff = i * 4 * 2;\n                var dstOff = i * 4;\n\n                for (var j = 0; j < 4; ++j)\n                    dst[dstOff + j] = (byte)(HalfHelper.Unpack(src, srcOff + j * 2) * byte.MaxValue);\n            }\n        }\n\n        private static void ProcessA1R5G5B5(byte[] src, byte[] dst, int width, int height) {\n            for (var i = 0; (i + 2) <= 2 * width * height; i += 2) {\n                var v = BitConverter.ToUInt16(src, i);\n\n                var a = (uint)(v & 0x8000);\n                var r = (uint)(v & 0x7C00);\n                var g = (uint)(v & 0x03E0);\n                var b = (uint)(v & 0x001F);\n\n                var rgb = ((r << 9) | (g << 6) | (b << 3));\n                var argbValue = (a * 0x1FE00 | rgb | ((rgb >> 5) & 0x070707));\n\n                for (var j = 0; j < 4; ++j)\n                    dst[i * 2 + j] = (byte)(argbValue >> (8 * j));\n            }\n        }\n\n        private static void ProcessA4R4G4B4(byte[] src, byte[] dst, int width, int height) {\n            for (var i = 0; (i + 2) <= 2 * width * height; i += 2) {\n                var v = BitConverter.ToUInt16(src, i);\n\n                for (var j = 0; j < 4; ++j)\n                    dst[i * 2 + j] = (byte)(((v >> (4 * j)) & 0x0F) << 4);\n            }\n        }\n\n        private static void ProcessA8R8G8B8(byte[] src, byte[] dst, int width, int height) {\n            // Some transparent images have larger dst lengths than their src.\n            var length = Math.Min(src.Length, dst.Length);\n            Array.Copy(src, dst, length);\n        }\n\n        private static void ProcessDxt1(byte[] src, byte[] dst, int width, int height) {\n            var dec = Squish.DecompressImage(src, width, height, SquishOptions.DXT1);\n            Array.Copy(dec, dst, dst.Length);\n        }\n\n        private static void ProcessDxt3(byte[] src, byte[] dst, int width, int height) {\n            var dec = Squish.DecompressImage(src, width, height, SquishOptions.DXT3);\n            Array.Copy(dec, dst, dst.Length);\n        }\n\n        private static void ProcessDxt5(byte[] src, byte[] dst, int width, int height) {\n            var dec = Squish.DecompressImage(src, width, height, SquishOptions.DXT5);\n            Array.Copy(dec, dst, dst.Length);\n        }\n\n        private static void ProcessR3G3B2(byte[] src, byte[] dst, int width, int height) {\n            for (var i = 0; i < width * height; ++i) {\n                var r = (uint)(src[i] & 0xE0);\n                var g = (uint)(src[i] & 0x1C);\n                var b = (uint)(src[i] & 0x03);\n\n                dst[i * 4 + 0] = (byte)(b | (b << 2) | (b << 4) | (b << 6));\n                dst[i * 4 + 1] = (byte)(g | (g << 3) | (g << 6));\n                dst[i * 4 + 2] = (byte)(r | (r << 3) | (r << 6));\n                dst[i * 4 + 3] = 0xFF;\n            }\n        }\n\n        // https://github.com/0ceal0t/Dalamud-VFXEditor/blob/main/VFXEditor/Formats/TextureFormat/TextureDataFile.cs#L275\n\n        private static void ProcessBC5(byte[] src, byte[] dst, int width, int height) {\n            ProcessBC(src, dst, width, height, CompressionFormat.Bc5);\n        }\n\n        private static void ProcessBC7(byte[] src, byte[] dst, int width, int height) {\n            ProcessBC(src, dst, width, height, CompressionFormat.Bc7);\n        }\n\n        private static void ProcessBC(byte[] src, byte[] dst, int width, int height, CompressionFormat format) {\n            var decoder = new BcDecoder();\n            var output = decoder.DecodeRaw2D(src, width, height, format).ToArray();\n\n            var p = 0;\n            for (var i = 0; i < height; i++) {\n                for (var j = 0; j < width; j++) {\n                    var pixel = output[i, j];\n                    dst[p] = pixel.b;\n                    dst[p + 1] = pixel.g;\n                    dst[p + 2] = pixel.r;\n                    dst[p + 3] = pixel.a;\n                    p += 4;\n                }\n            }\n        }\n        #endregion\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Imaging/ImageFile.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Diagnostics;\nusing System.Drawing;\nusing System.IO;\n\nusing SaintCoinach.IO;\n\nusing Directory = SaintCoinach.IO.Directory;\nusing File = SaintCoinach.IO.File;\n\nnamespace SaintCoinach.Imaging {\n    /// <summary>\n    ///     Image file stored inside SqPack.\n    /// </summary>\n    public class ImageFile : File {\n        #region Fields\n\n        private WeakReference<byte[]> _BufferCache;\n        private WeakReference<Image> _ImageCache;\n\n        #endregion\n\n        #region Properties\n\n        public ImageHeader ImageHeader { get; private set; }\n        public int Width { get { return ImageHeader.Width; } }\n        public int Height { get { return ImageHeader.Height; } }\n        public ImageFormat Format { get { return ImageHeader.Format; } }\n\n        #endregion\n\n        #region Constructors\n\n        public ImageFile(Pack pack, FileCommonHeader commonHeader)\n            : base(pack, commonHeader) {\n            var stream = GetSourceStream();\n            stream.Position = CommonHeader.EndOfHeader;\n            ImageHeader = new ImageHeader(stream);\n        }\n\n        // For atex texture files\n        public ImageFile(FileDefault file) : base(file.Pack, file.CommonHeader) {\n            var data = file.GetData();\n\n            var stream = new MemoryStream(data);\n            ImageHeader = new ImageHeader(stream);\n            var imgData = new byte[data.Length - ImageHeader.EndOfHeader];\n            Array.Copy(data, ImageHeader.EndOfHeader, imgData, 0, imgData.Length);\n            _BufferCache = new WeakReference<byte[]>(imgData);\n        }\n\n        #endregion\n\n        #region Read\n\n        public Image GetImage() {\n            if (_ImageCache != null && _ImageCache.TryGetTarget(out var image)) return image;\n\n            image = ImageConverter.Convert(this);\n\n            if (_ImageCache == null)\n                _ImageCache = new WeakReference<Image>(image);\n            else\n                _ImageCache.SetTarget(image);\n\n            return image;\n        }\n\n        public override byte[] GetData() {\n            if (_BufferCache != null && _BufferCache.TryGetTarget(out var buffer)) return buffer;\n\n            buffer = Read();\n\n            if (_BufferCache == null)\n                _BufferCache = new WeakReference<byte[]>(buffer);\n            else\n                _BufferCache.SetTarget(buffer);\n\n            return buffer;\n        }\n\n        private byte[] Read() {\n            var sourceStream = GetSourceStream();\n            var offsets = GetBlockOffsets();\n\n            byte[] data;\n            using (var dataStream = new MemoryStream((int)CommonHeader.Length)) {\n                foreach (var offset in offsets) {\n                    sourceStream.Position = ImageHeader.EndOfHeader + offset;\n                    ReadBlock(sourceStream, dataStream);\n                }\n                data = dataStream.ToArray();\n            }\n            return data;\n        }\n\n        private IEnumerable<int> GetBlockOffsets() {\n            const int CountOffset = 0x14;\n            const int EntryLength = 0x14;\n            const int BlockInfoOffset = 0x18;\n\n            var count = BitConverter.ToInt16(CommonHeader._Buffer, CountOffset);\n            var currentOffset = 0;\n            var offsets = new List<int>();\n\n            for (var i = BlockInfoOffset + count * EntryLength; i + 2 <= CommonHeader._Buffer.Length; i += 2) {\n                var len = BitConverter.ToUInt16(CommonHeader._Buffer, i);\n                if (len == 0)\n                    break;\n                offsets.Add(currentOffset);\n                currentOffset += len;\n            }\n\n            return offsets.ToArray();\n        }\n\n        #endregion\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Imaging/ImageFormat.cs",
    "content": "﻿namespace SaintCoinach.Imaging {\n    public enum ImageFormat {\n        Unknown = 0,\n        R16G16F = 0x2250,\n        A16R16G16B16Float = 0x2460,\n        A32R32G32B32Float = 0x2470,\n\n        A8R8G8B8_1 = 0x1131,\n        A8R8G8B8_2 = 0x1450,\n        A8R8G8B8_Cube = 0x1451,\n        A8R8G8B82 = 0x1452,\n        A8R8G8B8_4 = 0x2150,\n        A8R8G8B8_5 = 0x4401,\n\n        A4R4G4B4 = 0x1440,\n        A1R5G5B5 = 0x1441,\n        R3G3B2 = 0x1130,\n        L8 = 0x1130,\n        A8 = 0x1131,\n\n        Dxt1 = 0x3420,\n        Dxt3 = 0x3430,\n        Dxt5 = 0x3431,\n\n        D16 = 0x4140,\n        D24S8 = 0x4250,\n\n        Shadow16 = 0x5140,\n        Shadow24 = 0x5150,\n        BC5 = 0x6230,\n        BC7 = 0x6432,\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Imaging/ImageHeader.cs",
    "content": "﻿using System;\nusing System.IO;\n\nnamespace SaintCoinach.Imaging {\n    /// <summary>\n    ///     Header of an image file inside SqPack.\n    /// </summary>\n    public class ImageHeader {\n        #region Fields\n\n        #region Constructor\n\n        internal byte[] _Buffer;\n\n        #endregion\n\n        #endregion\n\n        #region Properties\n\n        public int Width { get; private set; }\n        public int Height { get; private set; }\n        public ImageFormat Format { get; private set; }\n        public long EndOfHeader { get; private set; }\n\n        #endregion\n\n        #region Constructors\n\n        #region Constructor\n\n        public ImageHeader(Stream stream) {\n            const int Length = 0x50;\n\n            const int FormatOffset = 0x04;\n            const int WidthOffset = 0x08;\n            const int HeightOffset = 0x0A;\n\n            _Buffer = new byte[Length];\n            if (stream.Read(_Buffer, 0, Length) != Length)\n                throw new EndOfStreamException();\n\n            Width = BitConverter.ToInt16(_Buffer, WidthOffset);\n            Height = BitConverter.ToInt16(_Buffer, HeightOffset);\n            Format = (ImageFormat)BitConverter.ToInt16(_Buffer, FormatOffset);\n            EndOfHeader = stream.Position;\n        }\n\n        #endregion\n\n        #endregion\n\n        public byte[] GetBuffer() {\n            var b = new byte[_Buffer.Length];\n            Array.Copy(_Buffer, b, b.Length);\n            return b;\n        }\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Libra/Achievement.cs",
    "content": "//------------------------------------------------------------------------------\n// <auto-generated>\n//     This code was generated from a template.\n//\n//     Manual changes to this file may cause unexpected behavior in your application.\n//     Manual changes to this file will be overwritten if the code is regenerated.\n// </auto-generated>\n//------------------------------------------------------------------------------\n\nnamespace SaintCoinach.Libra\n{\n    using System;\n    using System.Collections.Generic;\n    \n    public partial class Achievement\n    {\n        public long Key { get; set; }\n        public Nullable<long> Category { get; set; }\n        public string Name_ja { get; set; }\n        public string Name_en { get; set; }\n        public string Name_fr { get; set; }\n        public string Name_de { get; set; }\n        public string Help_ja { get; set; }\n        public string Help_en { get; set; }\n        public string Help_fr { get; set; }\n        public string Help_de { get; set; }\n        public Nullable<long> Point { get; set; }\n        public Nullable<long> Item { get; set; }\n        public Nullable<long> Icon { get; set; }\n        public Nullable<long> Title { get; set; }\n        public Nullable<long> Priority { get; set; }\n        public byte[] data { get; set; }\n        public string path { get; set; }\n        public string Index_ja { get; set; }\n        public string Index_en { get; set; }\n        public string Index_fr { get; set; }\n        public string Index_de { get; set; }\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Libra/AchievementCategory.cs",
    "content": "//------------------------------------------------------------------------------\n// <auto-generated>\n//     This code was generated from a template.\n//\n//     Manual changes to this file may cause unexpected behavior in your application.\n//     Manual changes to this file will be overwritten if the code is regenerated.\n// </auto-generated>\n//------------------------------------------------------------------------------\n\nnamespace SaintCoinach.Libra\n{\n    using System;\n    using System.Collections.Generic;\n    \n    public partial class AchievementCategory\n    {\n        public long Key { get; set; }\n        public Nullable<long> Kind { get; set; }\n        public string Name_ja { get; set; }\n        public string Name_en { get; set; }\n        public string Name_fr { get; set; }\n        public string Name_de { get; set; }\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Libra/AchievementKind.cs",
    "content": "//------------------------------------------------------------------------------\n// <auto-generated>\n//     This code was generated from a template.\n//\n//     Manual changes to this file may cause unexpected behavior in your application.\n//     Manual changes to this file will be overwritten if the code is regenerated.\n// </auto-generated>\n//------------------------------------------------------------------------------\n\nnamespace SaintCoinach.Libra\n{\n    using System;\n    using System.Collections.Generic;\n    \n    public partial class AchievementKind\n    {\n        public long Key { get; set; }\n        public string Name_ja { get; set; }\n        public string Name_en { get; set; }\n        public string Name_fr { get; set; }\n        public string Name_de { get; set; }\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Libra/Action.cs",
    "content": "//------------------------------------------------------------------------------\n// <auto-generated>\n//     This code was generated from a template.\n//\n//     Manual changes to this file may cause unexpected behavior in your application.\n//     Manual changes to this file will be overwritten if the code is regenerated.\n// </auto-generated>\n//------------------------------------------------------------------------------\n\nnamespace SaintCoinach.Libra\n{\n    using System;\n    using System.Collections.Generic;\n    \n    public partial class Action\n    {\n        public long Key { get; set; }\n        public Nullable<long> Icon { get; set; }\n        public string Name_ja { get; set; }\n        public string Name_en { get; set; }\n        public string Name_fr { get; set; }\n        public string Name_de { get; set; }\n        public string HelpWeb_ja { get; set; }\n        public string HelpWeb_en { get; set; }\n        public string HelpWeb_fr { get; set; }\n        public string HelpWeb_de { get; set; }\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Libra/BNpcName.Parse.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Data;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nusing Newtonsoft.Json;\n\nnamespace SaintCoinach.Libra {\n    partial class BNpcName {\n        #region Fields\n        private bool _IsParsed;\n\n        private Tuple<int, Tuple<int, int[]>[]>[] _Regions;\n        private int[] _Items;\n        private int[] _NonPops;\n        private int[] _InstanceContents;\n        #endregion\n\n        #region Properties\n        public IEnumerable<Tuple<int, Tuple<int, int[]>[]>> Regions { get { Parse(); return _Regions; } }\n        public IEnumerable<int> Items { get { Parse(); return _Items; } }\n        public IEnumerable<int> NonPops { get { Parse(); return _NonPops; } }\n        public IEnumerable<int> InstanceContents { get { Parse(); return _InstanceContents; } }\n\n        public long NameKey { get { return Key % 10000000000; } }\n        public long BaseKey { get { return Key / 10000000000; } }\n        #endregion\n\n        #region Parse\n        public void Parse() {\n            if (_IsParsed) return;\n\n            var json = Encoding.UTF8.GetString(this.data);\n            using (var strReader = new System.IO.StringReader(json)) {\n                using (var r = new JsonTextReader(strReader)) {\n                    while (r.Read()) {\n                        if (r.TokenType == JsonToken.PropertyName) {\n                            switch (r.Value.ToString()) {\n                                case \"region\":\n                                    ParseRegions(r);\n                                    break;\n                                case \"nonpop\":\n                                    _NonPops = r.ReadInt32Array();\n                                    break;\n                                case \"item\":\n                                    _Items = r.ReadInt32Array();\n                                    break;\n                                case \"instance_contents\":\n                                    _InstanceContents = r.ReadInt32Array();\n                                    break;\n                                default:\n                                    Console.Error.WriteLine(\"Unknown 'BNpcName' data key: {0}\", r.Value);\n                                    throw new NotSupportedException();\n                            }\n                        }\n                    }\n                }\n            }\n\n            _IsParsed = true;\n        }\n\n        private void ParseRegions(JsonTextReader reader) {\n            if (!reader.Read() || reader.TokenType != JsonToken.StartObject) throw new InvalidOperationException();\n\n            var values = new List<Tuple<int, Tuple<int, int[]>[]>>();\n            while (reader.Read() && reader.TokenType != JsonToken.EndObject) {\n                if (reader.TokenType != JsonToken.PropertyName) throw new InvalidOperationException();\n\n                var region = Convert.ToInt32(reader.Value);\n\n                if (!reader.Read() || reader.TokenType != JsonToken.StartObject) throw new InvalidOperationException();\n\n                var zones = new List<Tuple<int, int[]>>();\n                while (reader.Read() && reader.TokenType != JsonToken.EndObject) {\n                    if (reader.TokenType != JsonToken.PropertyName) throw new InvalidOperationException();\n\n                    var zone = Convert.ToInt32(reader.Value);\n\n                    if (!reader.Read() || reader.TokenType != JsonToken.StartArray) throw new InvalidOperationException();\n\n                    var levels = new List<int>();\n                    while (reader.Read() && reader.TokenType != JsonToken.EndArray) {\n                        if (!\"??\".Equals(reader.Value.ToString()))\n                            levels.Add(Convert.ToInt32(reader.Value));\n                    }\n\n                    zones.Add(Tuple.Create(zone, levels.ToArray()));\n                }\n                values.Add(Tuple.Create(region, zones.ToArray()));\n            }\n            _Regions = values.ToArray();\n        }\n        #endregion\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Libra/BNpcName.cs",
    "content": "//------------------------------------------------------------------------------\n// <auto-generated>\n//     This code was generated from a template.\n//\n//     Manual changes to this file may cause unexpected behavior in your application.\n//     Manual changes to this file will be overwritten if the code is regenerated.\n// </auto-generated>\n//------------------------------------------------------------------------------\n\nnamespace SaintCoinach.Libra\n{\n    using System;\n    using System.Collections.Generic;\n    \n    public partial class BNpcName\n    {\n        public long Key { get; set; }\n        public string SGL_ja { get; set; }\n        public string SGL_en { get; set; }\n        public string SGL_fr { get; set; }\n        public string SGL_de { get; set; }\n        public string area { get; set; }\n        public byte[] data { get; set; }\n        public string path { get; set; }\n        public string Index_ja { get; set; }\n        public string Index_en { get; set; }\n        public string Index_fr { get; set; }\n        public string Index_de { get; set; }\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Libra/BNpcName_PlaceName.cs",
    "content": "//------------------------------------------------------------------------------\n// <auto-generated>\n//     This code was generated from a template.\n//\n//     Manual changes to this file may cause unexpected behavior in your application.\n//     Manual changes to this file will be overwritten if the code is regenerated.\n// </auto-generated>\n//------------------------------------------------------------------------------\n\nnamespace SaintCoinach.Libra\n{\n    using System;\n    using System.Collections.Generic;\n    \n    public partial class BNpcName_PlaceName\n    {\n        public long BNpcName_Key { get; set; }\n        public long PlaceName_Key { get; set; }\n        public long region { get; set; }\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Libra/BaseParam.cs",
    "content": "//------------------------------------------------------------------------------\n// <auto-generated>\n//     This code was generated from a template.\n//\n//     Manual changes to this file may cause unexpected behavior in your application.\n//     Manual changes to this file will be overwritten if the code is regenerated.\n// </auto-generated>\n//------------------------------------------------------------------------------\n\nnamespace SaintCoinach.Libra\n{\n    using System;\n    using System.Collections.Generic;\n    \n    public partial class BaseParam\n    {\n        public long Key { get; set; }\n        public string Name_ja { get; set; }\n        public string Name_en { get; set; }\n        public string Name_fr { get; set; }\n        public string Name_de { get; set; }\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Libra/BeastTribe.cs",
    "content": "//------------------------------------------------------------------------------\n// <auto-generated>\n//     This code was generated from a template.\n//\n//     Manual changes to this file may cause unexpected behavior in your application.\n//     Manual changes to this file will be overwritten if the code is regenerated.\n// </auto-generated>\n//------------------------------------------------------------------------------\n\nnamespace SaintCoinach.Libra\n{\n    using System;\n    using System.Collections.Generic;\n    \n    public partial class BeastTribe\n    {\n        public long Key { get; set; }\n        public string SGL_ja { get; set; }\n        public string SGL_en { get; set; }\n        public string SGL_fr { get; set; }\n        public string SGL_de { get; set; }\n        public string ReputationValue_ja { get; set; }\n        public string ReputationValue_en { get; set; }\n        public string ReputationValue_fr { get; set; }\n        public string ReputationValue_de { get; set; }\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Libra/ClassJob.cs",
    "content": "//------------------------------------------------------------------------------\n// <auto-generated>\n//     This code was generated from a template.\n//\n//     Manual changes to this file may cause unexpected behavior in your application.\n//     Manual changes to this file will be overwritten if the code is regenerated.\n// </auto-generated>\n//------------------------------------------------------------------------------\n\nnamespace SaintCoinach.Libra\n{\n    using System;\n    using System.Collections.Generic;\n    \n    public partial class ClassJob\n    {\n        public long Key { get; set; }\n        public string Label { get; set; }\n        public string Name_ja { get; set; }\n        public string Name_en { get; set; }\n        public string Name_fr { get; set; }\n        public string Name_de { get; set; }\n        public string Abbreviation_ja { get; set; }\n        public string Abbreviation_en { get; set; }\n        public string Abbreviation_fr { get; set; }\n        public string Abbreviation_de { get; set; }\n        public Nullable<long> IsJob { get; set; }\n        public Nullable<long> UIPriority { get; set; }\n        public string filebase { get; set; }\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Libra/ClassJobCategory.cs",
    "content": "//------------------------------------------------------------------------------\n// <auto-generated>\n//     This code was generated from a template.\n//\n//     Manual changes to this file may cause unexpected behavior in your application.\n//     Manual changes to this file will be overwritten if the code is regenerated.\n// </auto-generated>\n//------------------------------------------------------------------------------\n\nnamespace SaintCoinach.Libra\n{\n    using System;\n    using System.Collections.Generic;\n    \n    public partial class ClassJobCategory\n    {\n        public long Key { get; set; }\n        public string Name_ja { get; set; }\n        public string Name_en { get; set; }\n        public string Name_fr { get; set; }\n        public string Name_de { get; set; }\n        public string classjob { get; set; }\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Libra/ClassJob_ClassJobCategory.cs",
    "content": "//------------------------------------------------------------------------------\n// <auto-generated>\n//     This code was generated from a template.\n//\n//     Manual changes to this file may cause unexpected behavior in your application.\n//     Manual changes to this file will be overwritten if the code is regenerated.\n// </auto-generated>\n//------------------------------------------------------------------------------\n\nnamespace SaintCoinach.Libra\n{\n    using System;\n    using System.Collections.Generic;\n    \n    public partial class ClassJob_ClassJobCategory\n    {\n        public long ClassJob_Key { get; set; }\n        public long ClassJobCategory_Key { get; set; }\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Libra/Colosseum.cs",
    "content": "//------------------------------------------------------------------------------\n// <auto-generated>\n//     This code was generated from a template.\n//\n//     Manual changes to this file may cause unexpected behavior in your application.\n//     Manual changes to this file will be overwritten if the code is regenerated.\n// </auto-generated>\n//------------------------------------------------------------------------------\n\nnamespace SaintCoinach.Libra\n{\n    using System;\n    using System.Collections.Generic;\n    \n    public partial class Colosseum\n    {\n        public long Key { get; set; }\n        public Nullable<long> WinExp { get; set; }\n        public Nullable<long> WinPoint { get; set; }\n        public Nullable<long> WinToken { get; set; }\n        public Nullable<long> LosePoint { get; set; }\n        public Nullable<long> LoseToken { get; set; }\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Libra/ContentRoulette.cs",
    "content": "//------------------------------------------------------------------------------\n// <auto-generated>\n//     This code was generated from a template.\n//\n//     Manual changes to this file may cause unexpected behavior in your application.\n//     Manual changes to this file will be overwritten if the code is regenerated.\n// </auto-generated>\n//------------------------------------------------------------------------------\n\nnamespace SaintCoinach.Libra\n{\n    using System;\n    using System.Collections.Generic;\n    \n    public partial class ContentRoulette\n    {\n        public long Key { get; set; }\n        public string Name_ja { get; set; }\n        public string Name_en { get; set; }\n        public string Name_fr { get; set; }\n        public string Name_de { get; set; }\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Libra/ContentType.cs",
    "content": "//------------------------------------------------------------------------------\n// <auto-generated>\n//     This code was generated from a template.\n//\n//     Manual changes to this file may cause unexpected behavior in your application.\n//     Manual changes to this file will be overwritten if the code is regenerated.\n// </auto-generated>\n//------------------------------------------------------------------------------\n\nnamespace SaintCoinach.Libra\n{\n    using System;\n    using System.Collections.Generic;\n    \n    public partial class ContentType\n    {\n        public long Key { get; set; }\n        public string Name_ja { get; set; }\n        public string Name_en { get; set; }\n        public string Name_fr { get; set; }\n        public string Name_de { get; set; }\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Libra/CraftType.cs",
    "content": "//------------------------------------------------------------------------------\n// <auto-generated>\n//     This code was generated from a template.\n//\n//     Manual changes to this file may cause unexpected behavior in your application.\n//     Manual changes to this file will be overwritten if the code is regenerated.\n// </auto-generated>\n//------------------------------------------------------------------------------\n\nnamespace SaintCoinach.Libra\n{\n    using System;\n    using System.Collections.Generic;\n    \n    public partial class CraftType\n    {\n        public long Key { get; set; }\n        public Nullable<long> ClassJob { get; set; }\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Libra/ENpcResident.Parse.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Data;\nusing System.Globalization;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nusing Newtonsoft.Json;\n\nnamespace SaintCoinach.Libra {\n    partial class ENpcResident {\n        #region Fields\n        private int[] _AsQuestClient;\n        private Tuple<int, System.Drawing.PointF[]>[] _Coordinates;\n        private int[] _Quests;\n        private Tuple<int, int[]>[] _Shops;\n\n        private bool _IsParsed = false;\n        #endregion\n\n        #region Properties\n        public IEnumerable<int> AsQuestClient { get { Parse(); return _AsQuestClient; } }\n        public IEnumerable<Tuple<int, System.Drawing.PointF[]>> Coordinates { get { Parse(); return _Coordinates; } }\n        public IEnumerable<int> Quests { get { Parse(); return _Quests; } }\n        public IEnumerable<Tuple<int, int[]>> Shops { get { Parse(); return _Shops; } }\n        #endregion\n\n        #region Parse\n        public void Parse() {\n            if (_IsParsed)\n                return;\n\n            var json = Encoding.UTF8.GetString(this.data);\n            using (var strReader = new System.IO.StringReader(json)) {\n                using (var r = new JsonTextReader(strReader)) {\n                    while (r.Read()) {\n                        if (r.TokenType == JsonToken.PropertyName) {\n                            switch (r.Value.ToString()) {\n                                case \"client_quest\":\n                                    _AsQuestClient = r.ReadInt32Array();\n                                    break;\n                                case \"coordinate\":\n                                    ParseCoordinate(r);\n                                    break;\n                                case \"quest\":\n                                    _Quests = r.ReadInt32Array();\n                                    break;\n                                case \"shop\":\n                                    ParseShops(r);\n                                    break;\n                                default:\n                                    Console.Error.WriteLine(\"Unknown 'ENpcResident' data key: {0}\", r.Value);\n                                    throw new NotSupportedException();\n                            }\n                        }\n                    }\n                }\n            }\n\n            _IsParsed = true;\n        }\n\n        private void ParseCoordinate(JsonTextReader reader) {\n            if (!reader.Read() || reader.TokenType != JsonToken.StartObject)\n                throw new InvalidOperationException();\n\n            var allCoord = new List<Tuple<int, System.Drawing.PointF[]>>();\n            while (reader.Read() && reader.TokenType != JsonToken.EndObject) {\n                if (reader.TokenType != JsonToken.PropertyName) throw new InvalidOperationException();\n\n                var key = Convert.ToInt32(reader.Value);\n\n                if (!reader.Read() || reader.TokenType != JsonToken.StartArray) throw new InvalidOperationException();\n\n                var coordinates = new List<System.Drawing.PointF>();\n                while (reader.Read() && reader.TokenType != JsonToken.EndArray) {\n                    if (reader.TokenType != JsonToken.StartArray) throw new InvalidOperationException();\n\n                    if (!reader.Read() || reader.TokenType != JsonToken.String) throw new InvalidOperationException();\n                    var x = float.Parse((string)reader.Value, CultureInfo.InvariantCulture);\n\n                    if (!reader.Read() || reader.TokenType != JsonToken.String) throw new InvalidOperationException();\n                    var y = float.Parse((string)reader.Value, CultureInfo.InvariantCulture);\n\n                    if (!reader.Read() || reader.TokenType != JsonToken.EndArray) throw new InvalidOperationException();\n\n                    coordinates.Add(new System.Drawing.PointF(x, y));\n                }\n\n                allCoord.Add(Tuple.Create(key, coordinates.ToArray()));\n            }\n\n            _Coordinates = allCoord.ToArray();\n        }\n        private void ParseShops(JsonTextReader reader) {\n            if (!reader.Read() || reader.TokenType != JsonToken.StartArray) throw new InvalidOperationException();\n\n            var shops = new List<Tuple<int, int[]>>();\n            while (reader.Read() && reader.TokenType != JsonToken.EndArray) {\n                if (reader.TokenType != JsonToken.StartObject) throw new InvalidOperationException();\n                if (!reader.Read() || reader.TokenType != JsonToken.PropertyName) throw new InvalidOperationException();\n\n                var key = Convert.ToInt32(reader.Value);\n\n                var items = new List<int>();\n                if (!reader.Read() || reader.TokenType != JsonToken.StartArray) throw new InvalidOperationException();\n                while (reader.Read() && reader.TokenType != JsonToken.EndArray) {\n                    if (reader.TokenType != JsonToken.StartObject) throw new InvalidOperationException();\n                    if (!reader.Read() || reader.TokenType != JsonToken.PropertyName) throw new InvalidOperationException();\n\n                    var itemKey = Convert.ToInt32(reader.Value);\n\n                    if (!reader.Read() || reader.TokenType != JsonToken.StartArray) throw new InvalidOperationException();\n                    while (reader.Read() && reader.TokenType != JsonToken.EndArray) { }\n\n                    items.Add(itemKey);\n\n                    if (!reader.Read() || reader.TokenType != JsonToken.EndObject) throw new InvalidOperationException();\n                }\n\n                shops.Add(Tuple.Create(key, items.ToArray()));\n\n                if (!reader.Read() || reader.TokenType != JsonToken.EndObject) throw new InvalidOperationException();\n            }\n\n            _Shops = shops.ToArray();\n        }\n        #endregion\n\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Libra/ENpcResident.cs",
    "content": "//------------------------------------------------------------------------------\n// <auto-generated>\n//     This code was generated from a template.\n//\n//     Manual changes to this file may cause unexpected behavior in your application.\n//     Manual changes to this file will be overwritten if the code is regenerated.\n// </auto-generated>\n//------------------------------------------------------------------------------\n\nnamespace SaintCoinach.Libra\n{\n    using System;\n    using System.Collections.Generic;\n    \n    public partial class ENpcResident\n    {\n        public long Key { get; set; }\n        public string SGL_ja { get; set; }\n        public string SGL_en { get; set; }\n        public string SGL_fr { get; set; }\n        public string SGL_de { get; set; }\n        public string area { get; set; }\n        public byte[] data { get; set; }\n        public string path { get; set; }\n        public Nullable<bool> has_shop { get; set; }\n        public Nullable<bool> has_quest { get; set; }\n        public Nullable<bool> has_condition_shop { get; set; }\n        public string Index_ja { get; set; }\n        public string Index_en { get; set; }\n        public string Index_fr { get; set; }\n        public string Index_de { get; set; }\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Libra/ENpcResident_PlaceName.cs",
    "content": "//------------------------------------------------------------------------------\n// <auto-generated>\n//     This code was generated from a template.\n//\n//     Manual changes to this file may cause unexpected behavior in your application.\n//     Manual changes to this file will be overwritten if the code is regenerated.\n// </auto-generated>\n//------------------------------------------------------------------------------\n\nnamespace SaintCoinach.Libra\n{\n    using System;\n    using System.Collections.Generic;\n    \n    public partial class ENpcResident_PlaceName\n    {\n        public long ENpcResident_Key { get; set; }\n        public long PlaceName_Key { get; set; }\n        public long region { get; set; }\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Libra/ENpcResident_Quest.cs",
    "content": "//------------------------------------------------------------------------------\n// <auto-generated>\n//     This code was generated from a template.\n//\n//     Manual changes to this file may cause unexpected behavior in your application.\n//     Manual changes to this file will be overwritten if the code is regenerated.\n// </auto-generated>\n//------------------------------------------------------------------------------\n\nnamespace SaintCoinach.Libra\n{\n    using System;\n    using System.Collections.Generic;\n    \n    public partial class ENpcResident_Quest\n    {\n        public long ENpcResident_Key { get; set; }\n        public long Quest_Key { get; set; }\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Libra/Emote.cs",
    "content": "//------------------------------------------------------------------------------\n// <auto-generated>\n//     This code was generated from a template.\n//\n//     Manual changes to this file may cause unexpected behavior in your application.\n//     Manual changes to this file will be overwritten if the code is regenerated.\n// </auto-generated>\n//------------------------------------------------------------------------------\n\nnamespace SaintCoinach.Libra\n{\n    using System;\n    using System.Collections.Generic;\n    \n    public partial class Emote\n    {\n        public long Key { get; set; }\n        public Nullable<long> Icon { get; set; }\n        public string Name_ja { get; set; }\n        public string Name_en { get; set; }\n        public string Name_fr { get; set; }\n        public string Name_de { get; set; }\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Libra/Entities.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace SaintCoinach.Libra {\n    public partial class Entities {\n        public Entities(string connectionString) : base(connectionString) { }\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Libra/FCHierarchy.cs",
    "content": "//------------------------------------------------------------------------------\n// <auto-generated>\n//     This code was generated from a template.\n//\n//     Manual changes to this file may cause unexpected behavior in your application.\n//     Manual changes to this file will be overwritten if the code is regenerated.\n// </auto-generated>\n//------------------------------------------------------------------------------\n\nnamespace SaintCoinach.Libra\n{\n    using System;\n    using System.Collections.Generic;\n    \n    public partial class FCHierarchy\n    {\n        public long Key { get; set; }\n        public string Name_ja { get; set; }\n        public string Name_en { get; set; }\n        public string Name_fr { get; set; }\n        public string Name_de { get; set; }\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Libra/FCRank.cs",
    "content": "//------------------------------------------------------------------------------\n// <auto-generated>\n//     This code was generated from a template.\n//\n//     Manual changes to this file may cause unexpected behavior in your application.\n//     Manual changes to this file will be overwritten if the code is regenerated.\n// </auto-generated>\n//------------------------------------------------------------------------------\n\nnamespace SaintCoinach.Libra\n{\n    using System;\n    using System.Collections.Generic;\n    \n    public partial class FCRank\n    {\n        public long Key { get; set; }\n        public Nullable<long> CurrentPoint { get; set; }\n        public Nullable<long> NextPoint { get; set; }\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Libra/FCReputation.cs",
    "content": "//------------------------------------------------------------------------------\n// <auto-generated>\n//     This code was generated from a template.\n//\n//     Manual changes to this file may cause unexpected behavior in your application.\n//     Manual changes to this file will be overwritten if the code is regenerated.\n// </auto-generated>\n//------------------------------------------------------------------------------\n\nnamespace SaintCoinach.Libra\n{\n    using System;\n    using System.Collections.Generic;\n    \n    public partial class FCReputation\n    {\n        public long Key { get; set; }\n        public Nullable<long> Color { get; set; }\n        public Nullable<long> CurrentPoint { get; set; }\n        public Nullable<long> NextPoint { get; set; }\n        public string Text_ja { get; set; }\n        public string Text_en { get; set; }\n        public string Text_fr { get; set; }\n        public string Text_de { get; set; }\n        public byte[] data { get; set; }\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Libra/Frontline01.cs",
    "content": "//------------------------------------------------------------------------------\n// <auto-generated>\n//     This code was generated from a template.\n//\n//     Manual changes to this file may cause unexpected behavior in your application.\n//     Manual changes to this file will be overwritten if the code is regenerated.\n// </auto-generated>\n//------------------------------------------------------------------------------\n\nnamespace SaintCoinach.Libra\n{\n    using System;\n    using System.Collections.Generic;\n    \n    public partial class Frontline01\n    {\n        public long Key { get; set; }\n        public string WinPvPExp { get; set; }\n        public string WinPvPToken { get; set; }\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Libra/GCRankGridaniaFemaleText.cs",
    "content": "//------------------------------------------------------------------------------\n// <auto-generated>\n//     This code was generated from a template.\n//\n//     Manual changes to this file may cause unexpected behavior in your application.\n//     Manual changes to this file will be overwritten if the code is regenerated.\n// </auto-generated>\n//------------------------------------------------------------------------------\n\nnamespace SaintCoinach.Libra\n{\n    using System;\n    using System.Collections.Generic;\n    \n    public partial class GCRankGridaniaFemaleText\n    {\n        public long Key { get; set; }\n        public string SGL_ja { get; set; }\n        public string SGL_en { get; set; }\n        public string SGL_fr { get; set; }\n        public string SGL_de { get; set; }\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Libra/GCRankGridaniaMaleText.cs",
    "content": "//------------------------------------------------------------------------------\n// <auto-generated>\n//     This code was generated from a template.\n//\n//     Manual changes to this file may cause unexpected behavior in your application.\n//     Manual changes to this file will be overwritten if the code is regenerated.\n// </auto-generated>\n//------------------------------------------------------------------------------\n\nnamespace SaintCoinach.Libra\n{\n    using System;\n    using System.Collections.Generic;\n    \n    public partial class GCRankGridaniaMaleText\n    {\n        public long Key { get; set; }\n        public string SGL_ja { get; set; }\n        public string SGL_en { get; set; }\n        public string SGL_fr { get; set; }\n        public string SGL_de { get; set; }\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Libra/GCRankLimsaFemaleText.cs",
    "content": "//------------------------------------------------------------------------------\n// <auto-generated>\n//     This code was generated from a template.\n//\n//     Manual changes to this file may cause unexpected behavior in your application.\n//     Manual changes to this file will be overwritten if the code is regenerated.\n// </auto-generated>\n//------------------------------------------------------------------------------\n\nnamespace SaintCoinach.Libra\n{\n    using System;\n    using System.Collections.Generic;\n    \n    public partial class GCRankLimsaFemaleText\n    {\n        public long Key { get; set; }\n        public string SGL_ja { get; set; }\n        public string SGL_en { get; set; }\n        public string SGL_fr { get; set; }\n        public string SGL_de { get; set; }\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Libra/GCRankLimsaMaleText.cs",
    "content": "//------------------------------------------------------------------------------\n// <auto-generated>\n//     This code was generated from a template.\n//\n//     Manual changes to this file may cause unexpected behavior in your application.\n//     Manual changes to this file will be overwritten if the code is regenerated.\n// </auto-generated>\n//------------------------------------------------------------------------------\n\nnamespace SaintCoinach.Libra\n{\n    using System;\n    using System.Collections.Generic;\n    \n    public partial class GCRankLimsaMaleText\n    {\n        public long Key { get; set; }\n        public string SGL_ja { get; set; }\n        public string SGL_en { get; set; }\n        public string SGL_fr { get; set; }\n        public string SGL_de { get; set; }\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Libra/GCRankUldahFemaleText.cs",
    "content": "//------------------------------------------------------------------------------\n// <auto-generated>\n//     This code was generated from a template.\n//\n//     Manual changes to this file may cause unexpected behavior in your application.\n//     Manual changes to this file will be overwritten if the code is regenerated.\n// </auto-generated>\n//------------------------------------------------------------------------------\n\nnamespace SaintCoinach.Libra\n{\n    using System;\n    using System.Collections.Generic;\n    \n    public partial class GCRankUldahFemaleText\n    {\n        public long Key { get; set; }\n        public string SGL_ja { get; set; }\n        public string SGL_en { get; set; }\n        public string SGL_fr { get; set; }\n        public string SGL_de { get; set; }\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Libra/GCRankUldahMaleText.cs",
    "content": "//------------------------------------------------------------------------------\n// <auto-generated>\n//     This code was generated from a template.\n//\n//     Manual changes to this file may cause unexpected behavior in your application.\n//     Manual changes to this file will be overwritten if the code is regenerated.\n// </auto-generated>\n//------------------------------------------------------------------------------\n\nnamespace SaintCoinach.Libra\n{\n    using System;\n    using System.Collections.Generic;\n    \n    public partial class GCRankUldahMaleText\n    {\n        public long Key { get; set; }\n        public string SGL_ja { get; set; }\n        public string SGL_en { get; set; }\n        public string SGL_fr { get; set; }\n        public string SGL_de { get; set; }\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Libra/Gathering.cs",
    "content": "//------------------------------------------------------------------------------\n// <auto-generated>\n//     This code was generated from a template.\n//\n//     Manual changes to this file may cause unexpected behavior in your application.\n//     Manual changes to this file will be overwritten if the code is regenerated.\n// </auto-generated>\n//------------------------------------------------------------------------------\n\nnamespace SaintCoinach.Libra\n{\n    using System;\n    using System.Collections.Generic;\n    \n    public partial class Gathering\n    {\n        public long Key { get; set; }\n        public Nullable<long> Item { get; set; }\n        public Nullable<long> GatheringType { get; set; }\n        public Nullable<long> Level { get; set; }\n        public Nullable<long> levelView { get; set; }\n        public Nullable<long> levelDiff { get; set; }\n        public Nullable<bool> is_hidden { get; set; }\n        public byte[] data { get; set; }\n        public Nullable<long> GatheringNotebookList { get; set; }\n        public Nullable<long> GatheringItemNo { get; set; }\n        public string path { get; set; }\n        public string Index_ja { get; set; }\n        public string Index_en { get; set; }\n        public string Index_fr { get; set; }\n        public string Index_de { get; set; }\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Libra/GatheringType.cs",
    "content": "//------------------------------------------------------------------------------\n// <auto-generated>\n//     This code was generated from a template.\n//\n//     Manual changes to this file may cause unexpected behavior in your application.\n//     Manual changes to this file will be overwritten if the code is regenerated.\n// </auto-generated>\n//------------------------------------------------------------------------------\n\nnamespace SaintCoinach.Libra\n{\n    using System;\n    using System.Collections.Generic;\n    \n    public partial class GatheringType\n    {\n        public long Key { get; set; }\n        public string Text_de { get; set; }\n        public string Text_en { get; set; }\n        public string Text_ja { get; set; }\n        public string Text_fr { get; set; }\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Libra/GeneralAction.cs",
    "content": "//------------------------------------------------------------------------------\n// <auto-generated>\n//     This code was generated from a template.\n//\n//     Manual changes to this file may cause unexpected behavior in your application.\n//     Manual changes to this file will be overwritten if the code is regenerated.\n// </auto-generated>\n//------------------------------------------------------------------------------\n\nnamespace SaintCoinach.Libra\n{\n    using System;\n    using System.Collections.Generic;\n    \n    public partial class GeneralAction\n    {\n        public long Key { get; set; }\n        public Nullable<long> Icon { get; set; }\n        public string Name_ja { get; set; }\n        public string Name_en { get; set; }\n        public string Name_fr { get; set; }\n        public string Name_de { get; set; }\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Libra/GrandCompany.cs",
    "content": "//------------------------------------------------------------------------------\n// <auto-generated>\n//     This code was generated from a template.\n//\n//     Manual changes to this file may cause unexpected behavior in your application.\n//     Manual changes to this file will be overwritten if the code is regenerated.\n// </auto-generated>\n//------------------------------------------------------------------------------\n\nnamespace SaintCoinach.Libra\n{\n    using System;\n    using System.Collections.Generic;\n    \n    public partial class GrandCompany\n    {\n        public long Key { get; set; }\n        public string SGL_ja { get; set; }\n        public string SGL_en { get; set; }\n        public string SGL_fr { get; set; }\n        public string SGL_de { get; set; }\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Libra/GuardianDeity.cs",
    "content": "//------------------------------------------------------------------------------\n// <auto-generated>\n//     This code was generated from a template.\n//\n//     Manual changes to this file may cause unexpected behavior in your application.\n//     Manual changes to this file will be overwritten if the code is regenerated.\n// </auto-generated>\n//------------------------------------------------------------------------------\n\nnamespace SaintCoinach.Libra\n{\n    using System;\n    using System.Collections.Generic;\n    \n    public partial class GuardianDeity\n    {\n        public long Key { get; set; }\n        public string Name_ja { get; set; }\n        public string Name_en { get; set; }\n        public string Name_fr { get; set; }\n        public string Name_de { get; set; }\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Libra/GuildOrder.cs",
    "content": "//------------------------------------------------------------------------------\n// <auto-generated>\n//     This code was generated from a template.\n//\n//     Manual changes to this file may cause unexpected behavior in your application.\n//     Manual changes to this file will be overwritten if the code is regenerated.\n// </auto-generated>\n//------------------------------------------------------------------------------\n\nnamespace SaintCoinach.Libra\n{\n    using System;\n    using System.Collections.Generic;\n    \n    public partial class GuildOrder\n    {\n        public long Key { get; set; }\n        public Nullable<long> Exp00 { get; set; }\n        public Nullable<long> Exp01 { get; set; }\n        public Nullable<long> Gil00 { get; set; }\n        public Nullable<long> Gil01 { get; set; }\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Libra/InstanceContent.cs",
    "content": "//------------------------------------------------------------------------------\n// <auto-generated>\n//     This code was generated from a template.\n//\n//     Manual changes to this file may cause unexpected behavior in your application.\n//     Manual changes to this file will be overwritten if the code is regenerated.\n// </auto-generated>\n//------------------------------------------------------------------------------\n\nnamespace SaintCoinach.Libra\n{\n    using System;\n    using System.Collections.Generic;\n    \n    public partial class InstanceContent\n    {\n        public long Key { get; set; }\n        public Nullable<long> Type { get; set; }\n        public Nullable<long> Sortkey { get; set; }\n        public string Name_ja { get; set; }\n        public string Name_en { get; set; }\n        public string Name_fr { get; set; }\n        public string Name_de { get; set; }\n        public string Description_ja { get; set; }\n        public string Description_en { get; set; }\n        public string Description_fr { get; set; }\n        public string Description_de { get; set; }\n        public Nullable<long> LevelMin { get; set; }\n        public Nullable<long> LevelMax { get; set; }\n        public Nullable<long> Time { get; set; }\n        public Nullable<bool> Halfway { get; set; }\n        public Nullable<long> RandomContentType { get; set; }\n        public Nullable<bool> Alliance { get; set; }\n        public Nullable<long> FinderPartyCondition { get; set; }\n        public Nullable<long> PartyMemberCount { get; set; }\n        public Nullable<long> TankCount { get; set; }\n        public Nullable<long> HealerCount { get; set; }\n        public Nullable<long> AttackerCount { get; set; }\n        public Nullable<long> RangeCount { get; set; }\n        public Nullable<bool> DifferentiateDPS { get; set; }\n        public Nullable<long> PartyCount { get; set; }\n        public Nullable<bool> FreeRole { get; set; }\n        public Nullable<long> ItemLevel { get; set; }\n        public Nullable<long> ItemLevelMax { get; set; }\n        public Nullable<long> Colosseum { get; set; }\n        public Nullable<long> Area { get; set; }\n        public Nullable<long> ForceCount { get; set; }\n        public byte[] data { get; set; }\n        public string path { get; set; }\n        public Nullable<bool> is_koeru_usually { get; set; }\n        public Nullable<bool> is_koeru_annihilation { get; set; }\n        public string Index_ja { get; set; }\n        public string Index_en { get; set; }\n        public string Index_fr { get; set; }\n        public string Index_de { get; set; }\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Libra/InstanceContentType.cs",
    "content": "//------------------------------------------------------------------------------\n// <auto-generated>\n//     This code was generated from a template.\n//\n//     Manual changes to this file may cause unexpected behavior in your application.\n//     Manual changes to this file will be overwritten if the code is regenerated.\n// </auto-generated>\n//------------------------------------------------------------------------------\n\nnamespace SaintCoinach.Libra\n{\n    using System;\n    using System.Collections.Generic;\n    \n    public partial class InstanceContentType\n    {\n        public long Type { get; set; }\n        public Nullable<long> Sortkey { get; set; }\n        public Nullable<long> ContentType { get; set; }\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Libra/Item.Parse.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nusing Newtonsoft.Json;\n\nnamespace SaintCoinach.Libra {\n    partial class Item {\n        #region Fields\n        private bool _IsParsed;\n\n        private bool _OnlyOne;\n        private bool _DisablePassedOthers;\n        private bool _Crest;\n        private long[] _BNpcs = new long[0];\n        private int[] _ShopENpcs = new int[0];\n        private int[] _InstanceContents = new int[0];\n        private int[] _Recipes = new int[0];\n        private int[] _Quests = new int[0];\n        private int[] _ClassJobs = new int[0];\n        private int[] _Achievements = new int[0];\n        private int _SellPrice;\n        private int _RepairClassJob;\n        private int _RepairItem;\n        private int _RepairPrice;\n        private int _MateriaSocket;\n        private int _MaterializeType;\n        private int _Stain;\n        private int _CondClassJob;\n        private int _Series;\n        private int _RecastTime;\n        private BasicParam[] _BasicParams = new BasicParam[0];\n        private BasicParam[] _BasicParamsHq = new BasicParam[0];\n        private Action[] _Actions = new Action[0];\n        private Action[] _ActionsHq = new Action[0];\n        private Bonus[] _Bonuses = new Bonus[0];\n        private Bonus[] _BonusesHq = new Bonus[0];\n        private SeriesBonus _SeriesBonuses;\n        private Bonus _Effect;\n        private System.Drawing.Color _Color;\n        #endregion\n\n        #region Helper structs\n        public class Bonus {\n            public int BaseParam;\n            public int Value;\n        }\n        public class BasicParam {\n            public string Param;\n            public float Value;\n        }\n        public abstract class Action {\n            public int BaseParam;\n        }\n        public class FixedAction : Action {\n            public int Value;\n        }\n        public class RelativeAction : Action {\n            public int Rate;\n            public int Limit;\n        }\n        public class SeriesBonus {\n            public string Series;\n            public string SpecialBonus;\n\n            public List<KeyValuePair<string, Bonus>> Bonuses = new List<KeyValuePair<string, Bonus>>();\n        }\n        #endregion\n\n        #region Properties\n        public bool OnlyOne { get { Parse(); return _OnlyOne; } }\n        public bool DisablePassedOthers { get { Parse(); return _DisablePassedOthers; } }\n        public bool Crest { get { Parse(); return _Crest; } }\n\n        public IEnumerable<long> BNpcs { get { Parse(); return _BNpcs; } }\n\n        public IEnumerable<int> ShopENpcs { get { Parse(); return _ShopENpcs; } }\n        public IEnumerable<int> InstanceContents { get { Parse(); return _InstanceContents; } }\n        public IEnumerable<int> Recipes { get { Parse(); return _Recipes; } }\n        public IEnumerable<int> Quests { get { Parse(); return _Quests; } }\n        public IEnumerable<int> ClassJobs { get { Parse(); return _ClassJobs; } }\n        public IEnumerable<int> Achievements { get { Parse(); return _Achievements; } }\n\n        public int SellPrice { get { Parse(); return _SellPrice; } }\n        public int RepairClassJob { get { Parse(); return _RepairClassJob; } }\n        public int RepairItem { get { Parse(); return _RepairItem; } }\n        public int RepairPrice { get { Parse(); return _RepairPrice; } }\n        public int MateriaSocket { get { Parse(); return _MateriaSocket; } }\n        public int MaterializeType { get { Parse(); return _MaterializeType; } }\n        public int Stain { get { Parse(); return _Stain; } }\n        public int CondClassJob { get { Parse(); return _CondClassJob; } }\n\n        // There's actually a column in the table for this.\n        // public int Series { get { Parse(); return _Series; } }\n        public int RecastTime { get { Parse(); return _RecastTime; } }\n\n        public IEnumerable<BasicParam> BasicParams { get { Parse(); return _BasicParams; } }\n        public IEnumerable<BasicParam> BasicParamsHq { get { Parse(); return _BasicParamsHq; } }\n        public IEnumerable<Action> Actions { get { Parse(); return _Actions; } }\n        public IEnumerable<Action> ActionsHq { get { Parse(); return _ActionsHq; } }\n        public IEnumerable<Bonus> Bonuses { get { Parse(); return _Bonuses; } }\n        public IEnumerable<Bonus> BonusesHq { get { Parse(); return _BonusesHq; } }\n\n        public SeriesBonus SeriesBonuses { get { Parse(); return _SeriesBonuses; } }\n        public Bonus Effect { get { Parse(); return _Effect; } }\n        public System.Drawing.Color Color { get { return _Color; } }\n        #endregion\n\n        #region Parse\n        public void Parse() {\n            if (_IsParsed) return;\n\n            var json = Encoding.UTF8.GetString(this.data);\n            using (var strReader = new System.IO.StringReader(json)) {\n                using (var r = new JsonTextReader(strReader)) {\n                    while (r.Read()) {\n                        if (r.TokenType == JsonToken.PropertyName) {\n                            switch (r.Value.ToString()) {\n                                #region Boolean\n                                case \"OnlyOne\":\n                                    _OnlyOne = r.ReadInt32() != 0;\n                                    break;\n                                case \"DisablePassedOthers\":\n                                    _DisablePassedOthers = r.ReadInt32() != 0;\n                                    break;\n                                case \"Crest\":\n                                    _Crest = r.ReadInt32() != 0;\n                                    break;\n                                #endregion\n\n                                #region Int64[]\n                                case \"bnpc\":\n                                    _BNpcs = r.ReadInt64Array();\n                                    break;\n                                #endregion\n\n                                #region Int32[]\n                                case \"shopnpc\":\n                                    _ShopENpcs = r.ReadInt32Array();\n                                    break;\n                                case \"instance_content\":\n                                    _InstanceContents = r.ReadInt32Array();\n                                    break;\n                                case \"recipe\":\n                                    _Recipes = r.ReadInt32Array();\n                                    break;\n                                case \"quest\":\n                                    _Quests = r.ReadInt32Array();\n                                    break;\n                                case \"classjob\":\n                                    _ClassJobs = r.ReadInt32Array();\n                                    break;\n                                case \"achievement\":\n                                    _Achievements = r.ReadInt32Array();\n                                    break;\n                                #endregion\n\n                                #region Int32\n                                case \"sell_price\":\n                                    _SellPrice = r.ReadInt32();\n                                    break;\n                                case \"Repair\":\n                                    _RepairClassJob = r.ReadInt32();\n                                    break;\n                                case \"RepairItem\":\n                                    _RepairItem = r.ReadInt32();\n                                    break;\n                                case \"repair_price\":\n                                    _RepairPrice = r.ReadInt32();\n                                    break;\n                                case \"MateriaSocket\":\n                                    _MateriaSocket = r.ReadInt32();\n                                    break;\n                                case \"MaterializeType\":\n                                    _MaterializeType = r.ReadInt32();\n                                    break;\n                                case \"Stain\":\n                                    _Stain = r.ReadInt32();\n                                    break;\n                                case \"CondClassJob\":\n                                    _CondClassJob = r.ReadInt32();\n                                    break;\n                                case \"Series\":\n                                    _Series = r.ReadInt32();\n                                    break;\n                                case \"RecastTime\":\n                                    _RecastTime = r.ReadInt32();\n                                    break;\n                                #endregion\n\n                                #region Obj\n                                case \"basic_param\":\n                                    _BasicParams = ParseBasicParam(r);\n                                    break;\n                                case \"basic_param_hq\":\n                                    _BasicParamsHq = ParseBasicParam(r);\n                                    break;\n                                case \"action\":\n                                    _Actions = ParseActions(r);\n                                    break;\n                                case \"action_hq\":\n                                    _ActionsHq = ParseActions(r);\n                                    break;\n                                case \"bonus\":\n                                    _Bonuses = ParseBonuses(r);\n                                    break;\n                                case \"bonus_hq\":\n                                    _BonusesHq = ParseBonuses(r);\n                                    break;\n                                case \"series_bonus\":\n                                    _SeriesBonuses = ParseSeriesBonuses(r);\n                                    break;\n                                case \"effect\":\n                                    if (!r.Read()) throw new InvalidOperationException();\n                                    _Effect = ParseBonus(r);\n                                    break;\n                                case \"color\":\n                                    _Color = ParseColor(r);\n                                    break;\n                                #endregion\n\n                                default:\n                                    Console.Error.WriteLine(\"Unknown 'Item' data key: {0}\", r.Value);\n                                    throw new NotSupportedException();\n                            }\n                        }\n                    }\n                }\n            }\n\n            _IsParsed = true;\n        }\n        private System.Drawing.Color ParseColor(JsonReader reader) {\n            if (!reader.Read() || reader.TokenType != JsonToken.StartArray) throw new InvalidOperationException();\n\n            var r = reader.ReadInt32();\n            var g = reader.ReadInt32();\n            var b = reader.ReadInt32();\n\n            if (!reader.Read() || reader.TokenType != JsonToken.EndArray) throw new InvalidOperationException();\n\n            return System.Drawing.Color.FromArgb(r, g, b);\n        }\n        private Action[] ParseActions(JsonReader r) {\n            if (!r.Read() || r.TokenType != JsonToken.StartArray) throw new InvalidOperationException();\n\n            var values = new List<Action>();\n            while (r.Read() && r.TokenType != JsonToken.EndArray) {\n                values.Add(ParseAction(r));\n            }\n            return values.ToArray();\n        }\n        private Action ParseAction(JsonReader r) {\n            if (r.TokenType != JsonToken.StartObject) throw new InvalidOperationException();\n            if (!r.Read() || r.TokenType != JsonToken.PropertyName) throw new InvalidOperationException();\n\n            var paramKey = Convert.ToInt32(r.Value);\n\n            if (!r.Read()) throw new InvalidOperationException();\n\n            Action value;\n            if (r.TokenType == JsonToken.StartObject) {\n                var act = new RelativeAction { BaseParam = paramKey };\n\n                while (r.Read() && r.TokenType != JsonToken.EndObject) {\n                    if (r.TokenType != JsonToken.PropertyName) throw new InvalidOperationException();\n\n                    switch (r.Value.ToString()) {\n                        case \"rate\":\n                            act.Rate = r.ReadInt32();\n                            break;\n                        case \"limit\":\n                            act.Limit = r.ReadInt32();\n                            break;\n                        default:\n                            Console.Error.WriteLine(\"Unknown 'Item'.'action' data key: {0}\", r.Value);\n                            throw new NotSupportedException();\n                    }\n                }\n\n                value = act;\n            } else if (r.TokenType == JsonToken.Integer || r.TokenType == JsonToken.String) {\n                value = new FixedAction { BaseParam = paramKey, Value = Convert.ToInt32(r.Value) };\n            } else\n                throw new InvalidOperationException();\n\n\n            if (!r.Read() || r.TokenType != JsonToken.EndObject) throw new InvalidOperationException();\n\n            return value;\n        }\n        private Bonus[] ParseBonuses(JsonReader r) {\n            if (!r.Read() || r.TokenType != JsonToken.StartArray) throw new InvalidOperationException();\n\n            var values = new List<Bonus>();\n            while (r.Read() && r.TokenType != JsonToken.EndArray) {\n                values.Add(ParseBonus(r));\n            }\n            return values.ToArray();\n        }\n        private Bonus ParseBonus(JsonReader r) {\n            if (r.TokenType != JsonToken.StartObject) throw new InvalidOperationException();\n            if (!r.Read() || r.TokenType != JsonToken.PropertyName) throw new InvalidOperationException();\n\n            var key = Convert.ToInt32(r.Value);\n            var value = r.ReadInt32();\n\n            if (!r.Read() || r.TokenType != JsonToken.EndObject) throw new InvalidOperationException();\n\n            return new Bonus { BaseParam = key, Value = value };\n        }\n        private BasicParam[] ParseBasicParam(JsonReader r) {\n            if (!r.Read() || r.TokenType != JsonToken.StartArray) throw new InvalidOperationException();\n\n            var values = new List<BasicParam>();\n            while (r.Read() && r.TokenType != JsonToken.EndArray) {\n                if (r.TokenType != JsonToken.StartObject) throw new InvalidOperationException();\n                if (!r.Read() || r.TokenType != JsonToken.PropertyName) throw new InvalidOperationException();\n\n                var key = r.Value.ToString();\n                var value = r.ReadSingle();\n\n                values.Add(new BasicParam { Param = key, Value = value });\n\n                if (!r.Read() || r.TokenType != JsonToken.EndObject) throw new InvalidOperationException();\n            }\n            return values.ToArray();\n        }\n        private SeriesBonus ParseSeriesBonuses(JsonReader r) {\n            if (!r.Read() || r.TokenType != JsonToken.StartObject) throw new InvalidOperationException();\n\n            var bonus = new SeriesBonus();\n            while (r.Read() && r.TokenType != JsonToken.EndObject) {\n                if (r.TokenType != JsonToken.PropertyName) throw new InvalidOperationException();\n\n                switch (r.Value.ToString()) {\n                    case \"SpecialBonus\":\n                        bonus.SpecialBonus = r.ReadAsString();\n                        break;\n                    case \"Series\":\n                        bonus.Series = r.ReadAsString();\n                        break;\n                    case \"bonus\":\n                        ParseSeriesBonus(r, bonus);\n                        break;\n                    default:\n                        Console.Error.WriteLine(\"Unknown 'Item'.'series_bonus' data key: {0}\", r.Value);\n                        throw new NotSupportedException();\n                }\n            }\n            return bonus;\n        }\n        private void ParseSeriesBonus(JsonReader r, SeriesBonus bonus) {\n            if (!r.Read() || r.TokenType != JsonToken.StartArray) throw new InvalidOperationException();\n\n            while (r.Read() && r.TokenType != JsonToken.EndArray) {\n                if (r.TokenType != JsonToken.StartObject) throw new InvalidOperationException();\n\n                if (!r.Read() || r.TokenType != JsonToken.PropertyName) throw new InvalidOperationException();\n\n                var bonusKey = r.Value.ToString();\n\n                if (!r.Read() || r.TokenType != JsonToken.StartObject) throw new InvalidOperationException();\n                if (!r.Read() || r.TokenType != JsonToken.PropertyName) throw new InvalidOperationException();\n\n                var paramKey = Convert.ToInt32(r.Value);\n                var value = r.ReadInt32();\n\n                bonus.Bonuses.Add(new KeyValuePair<string, Bonus>(bonusKey, new Bonus { BaseParam = paramKey, Value = value }));\n\n                if (!r.Read() || r.TokenType != JsonToken.EndObject) throw new InvalidOperationException();\n                if (!r.Read() || r.TokenType != JsonToken.EndObject) throw new InvalidOperationException();\n            }\n        }\n        #endregion\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Libra/Item.cs",
    "content": "//------------------------------------------------------------------------------\n// <auto-generated>\n//     This code was generated from a template.\n//\n//     Manual changes to this file may cause unexpected behavior in your application.\n//     Manual changes to this file will be overwritten if the code is regenerated.\n// </auto-generated>\n//------------------------------------------------------------------------------\n\nnamespace SaintCoinach.Libra\n{\n    using System;\n    using System.Collections.Generic;\n    \n    public partial class Item\n    {\n        public long Key { get; set; }\n        public Nullable<long> Category { get; set; }\n        public Nullable<long> UICategory { get; set; }\n        public string UIName_ja { get; set; }\n        public string UIName_en { get; set; }\n        public string UIName_fr { get; set; }\n        public string UIName_de { get; set; }\n        public string Help_ja { get; set; }\n        public string Help_en { get; set; }\n        public string Help_fr { get; set; }\n        public string Help_de { get; set; }\n        public Nullable<long> Level { get; set; }\n        public Nullable<long> EquipLevel { get; set; }\n        public Nullable<long> Rarity { get; set; }\n        public Nullable<bool> HQ { get; set; }\n        public Nullable<long> SpecialBonus { get; set; }\n        public Nullable<long> Series { get; set; }\n        public Nullable<long> Slot { get; set; }\n        public Nullable<long> Damage { get; set; }\n        public Nullable<long> Damage_hq { get; set; }\n        public Nullable<long> MagicDamage { get; set; }\n        public Nullable<long> MagicDamage_hq { get; set; }\n        public Nullable<long> Defense { get; set; }\n        public Nullable<long> Defense_hq { get; set; }\n        public Nullable<long> MagicDefense { get; set; }\n        public Nullable<long> MagicDefense_hq { get; set; }\n        public Nullable<long> ShieldRate { get; set; }\n        public Nullable<long> ShieldRate_hq { get; set; }\n        public Nullable<long> ShieldBlockRate { get; set; }\n        public Nullable<long> ShieldBlockRate_hq { get; set; }\n        public Nullable<long> Price { get; set; }\n        public Nullable<long> PriceMin { get; set; }\n        public Nullable<long> MirageItem { get; set; }\n        public string icon { get; set; }\n        public string icon_hq { get; set; }\n        public string classjob { get; set; }\n        public Nullable<long> Salvage { get; set; }\n        public byte[] data { get; set; }\n        public Nullable<bool> legacy { get; set; }\n        public string path { get; set; }\n        public string Index_ja { get; set; }\n        public string Index_en { get; set; }\n        public string Index_fr { get; set; }\n        public string Index_de { get; set; }\n        public Nullable<long> SortId { get; set; }\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Libra/ItemCategory.cs",
    "content": "//------------------------------------------------------------------------------\n// <auto-generated>\n//     This code was generated from a template.\n//\n//     Manual changes to this file may cause unexpected behavior in your application.\n//     Manual changes to this file will be overwritten if the code is regenerated.\n// </auto-generated>\n//------------------------------------------------------------------------------\n\nnamespace SaintCoinach.Libra\n{\n    using System;\n    using System.Collections.Generic;\n    \n    public partial class ItemCategory\n    {\n        public long Key { get; set; }\n        public string Name_ja { get; set; }\n        public string Name_en { get; set; }\n        public string Name_fr { get; set; }\n        public string Name_de { get; set; }\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Libra/ItemSery.cs",
    "content": "//------------------------------------------------------------------------------\n// <auto-generated>\n//     This code was generated from a template.\n//\n//     Manual changes to this file may cause unexpected behavior in your application.\n//     Manual changes to this file will be overwritten if the code is regenerated.\n// </auto-generated>\n//------------------------------------------------------------------------------\n\nnamespace SaintCoinach.Libra\n{\n    using System;\n    using System.Collections.Generic;\n    \n    public partial class ItemSery\n    {\n        public long Key { get; set; }\n        public string Name_ja { get; set; }\n        public string Name_en { get; set; }\n        public string Name_fr { get; set; }\n        public string Name_de { get; set; }\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Libra/ItemSpecialBonu.cs",
    "content": "//------------------------------------------------------------------------------\n// <auto-generated>\n//     This code was generated from a template.\n//\n//     Manual changes to this file may cause unexpected behavior in your application.\n//     Manual changes to this file will be overwritten if the code is regenerated.\n// </auto-generated>\n//------------------------------------------------------------------------------\n\nnamespace SaintCoinach.Libra\n{\n    using System;\n    using System.Collections.Generic;\n    \n    public partial class ItemSpecialBonu\n    {\n        public long Key { get; set; }\n        public string Name_ja { get; set; }\n        public string Name_en { get; set; }\n        public string Name_fr { get; set; }\n        public string Name_de { get; set; }\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Libra/ItemUICategory.cs",
    "content": "//------------------------------------------------------------------------------\n// <auto-generated>\n//     This code was generated from a template.\n//\n//     Manual changes to this file may cause unexpected behavior in your application.\n//     Manual changes to this file will be overwritten if the code is regenerated.\n// </auto-generated>\n//------------------------------------------------------------------------------\n\nnamespace SaintCoinach.Libra\n{\n    using System;\n    using System.Collections.Generic;\n    \n    public partial class ItemUICategory\n    {\n        public long Key { get; set; }\n        public Nullable<long> Kind { get; set; }\n        public string Name_ja { get; set; }\n        public string Name_en { get; set; }\n        public string Name_fr { get; set; }\n        public string Name_de { get; set; }\n        public Nullable<long> Priority { get; set; }\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Libra/ItemUIKind.cs",
    "content": "//------------------------------------------------------------------------------\n// <auto-generated>\n//     This code was generated from a template.\n//\n//     Manual changes to this file may cause unexpected behavior in your application.\n//     Manual changes to this file will be overwritten if the code is regenerated.\n// </auto-generated>\n//------------------------------------------------------------------------------\n\nnamespace SaintCoinach.Libra\n{\n    using System;\n    using System.Collections.Generic;\n    \n    public partial class ItemUIKind\n    {\n        public long Key { get; set; }\n        public string Name_ja { get; set; }\n        public string Name_en { get; set; }\n        public string Name_fr { get; set; }\n        public string Name_de { get; set; }\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Libra/Item_ClassJob.cs",
    "content": "//------------------------------------------------------------------------------\n// <auto-generated>\n//     This code was generated from a template.\n//\n//     Manual changes to this file may cause unexpected behavior in your application.\n//     Manual changes to this file will be overwritten if the code is regenerated.\n// </auto-generated>\n//------------------------------------------------------------------------------\n\nnamespace SaintCoinach.Libra\n{\n    using System;\n    using System.Collections.Generic;\n    \n    public partial class Item_ClassJob\n    {\n        public long Item_Key { get; set; }\n        public long ClassJob_Key { get; set; }\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Libra/JournalCategory.cs",
    "content": "//------------------------------------------------------------------------------\n// <auto-generated>\n//     This code was generated from a template.\n//\n//     Manual changes to this file may cause unexpected behavior in your application.\n//     Manual changes to this file will be overwritten if the code is regenerated.\n// </auto-generated>\n//------------------------------------------------------------------------------\n\nnamespace SaintCoinach.Libra\n{\n    using System;\n    using System.Collections.Generic;\n    \n    public partial class JournalCategory\n    {\n        public long Key { get; set; }\n        public string label { get; set; }\n        public string Name_ja { get; set; }\n        public string Name_en { get; set; }\n        public string Name_fr { get; set; }\n        public string Name_de { get; set; }\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Libra/JournalGenre.cs",
    "content": "//------------------------------------------------------------------------------\n// <auto-generated>\n//     This code was generated from a template.\n//\n//     Manual changes to this file may cause unexpected behavior in your application.\n//     Manual changes to this file will be overwritten if the code is regenerated.\n// </auto-generated>\n//------------------------------------------------------------------------------\n\nnamespace SaintCoinach.Libra\n{\n    using System;\n    using System.Collections.Generic;\n    \n    public partial class JournalGenre\n    {\n        public long Key { get; set; }\n        public Nullable<long> Category { get; set; }\n        public string label { get; set; }\n        public string Name_ja { get; set; }\n        public string Name_en { get; set; }\n        public string Name_fr { get; set; }\n        public string Name_de { get; set; }\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Libra/JsonReaderExtensions.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nusing Newtonsoft.Json;\n\nnamespace SaintCoinach.Libra {\n    public static class JsonReaderExtensions {\n        public static Int32 ReadInt32(this JsonReader r) {\n            if (!r.Read()) throw new InvalidOperationException();\n            if (r.TokenType != JsonToken.Integer && r.TokenType != JsonToken.String) throw new InvalidOperationException();\n\n            return Convert.ToInt32(r.Value);\n        }\n        public static Int32[] ReadInt32Array(this JsonReader r) {\n            if (!r.Read() || r.TokenType != JsonToken.StartArray) throw new InvalidOperationException();\n\n            var values = new List<Int32>();\n\n            while (r.Read() && r.TokenType != JsonToken.EndArray) {\n                if (r.TokenType != JsonToken.Integer && r.TokenType != JsonToken.String) throw new InvalidOperationException();\n\n                values.Add(Convert.ToInt32(r.Value));\n            }\n\n            return values.ToArray();\n        }\n        public static Int64 ReadInt64(this JsonReader r) {\n            if (!r.Read()) throw new InvalidOperationException();\n            if (r.TokenType != JsonToken.Integer && r.TokenType != JsonToken.String) throw new InvalidOperationException();\n\n            return Convert.ToInt64(r.Value);\n        }\n        public static Int64[] ReadInt64Array(this JsonReader r) {\n            if (!r.Read() || r.TokenType != JsonToken.StartArray) throw new InvalidOperationException();\n\n            var values = new List<Int64>();\n\n            while (r.Read() && r.TokenType != JsonToken.EndArray) {\n                if (r.TokenType != JsonToken.Integer && r.TokenType != JsonToken.String) throw new InvalidOperationException();\n\n                values.Add(Convert.ToInt64(r.Value));\n            }\n\n            return values.ToArray();\n        }\n        public static Single ReadSingle(this JsonReader r) {\n            if (!r.Read()) throw new InvalidOperationException();\n            if (r.TokenType != JsonToken.Integer && r.TokenType != JsonToken.String && r.TokenType != JsonToken.Float) throw new InvalidOperationException();\n\n            return Convert.ToSingle(r.Value);\n        }\n        public static Single[] ReadSingleArray(this JsonReader r) {\n            if (!r.Read() || r.TokenType != JsonToken.StartArray) throw new InvalidOperationException();\n\n            var values = new List<Single>();\n\n            while (r.Read() && r.TokenType != JsonToken.EndArray) {\n                if (r.TokenType != JsonToken.Integer && r.TokenType != JsonToken.String && r.TokenType != JsonToken.Float) throw new InvalidOperationException();\n\n                values.Add(Convert.ToSingle(r.Value));\n            }\n\n            return values.ToArray();\n        }\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Libra/LibraModel.Context.cs",
    "content": "﻿//------------------------------------------------------------------------------\n// <auto-generated>\n//     This code was generated from a template.\n//\n//     Manual changes to this file may cause unexpected behavior in your application.\n//     Manual changes to this file will be overwritten if the code is regenerated.\n// </auto-generated>\n//------------------------------------------------------------------------------\n\nnamespace SaintCoinach.Libra\n{\n    using System;\n    using System.Data.Entity;\n    using System.Data.Entity.Infrastructure;\n    \n    public partial class Entities : DbContext\n    {\n        public Entities()\n            : base(\"name=Entities\")\n        {\n        }\n    \n        protected override void OnModelCreating(DbModelBuilder modelBuilder)\n        {\n            throw new UnintentionalCodeFirstException();\n        }\n    \n        public virtual DbSet<Achievement> Achievements { get; set; }\n        public virtual DbSet<AchievementCategory> AchievementCategories { get; set; }\n        public virtual DbSet<AchievementKind> AchievementKinds { get; set; }\n        public virtual DbSet<Action> Actions { get; set; }\n        public virtual DbSet<BaseParam> BaseParams { get; set; }\n        public virtual DbSet<BeastTribe> BeastTribes { get; set; }\n        public virtual DbSet<BNpcName> BNpcNames { get; set; }\n        public virtual DbSet<BNpcName_PlaceName> BNpcName_PlaceName { get; set; }\n        public virtual DbSet<ClassJob> ClassJobs { get; set; }\n        public virtual DbSet<ClassJob_ClassJobCategory> ClassJob_ClassJobCategory { get; set; }\n        public virtual DbSet<ClassJobCategory> ClassJobCategories { get; set; }\n        public virtual DbSet<Colosseum> Colosseums { get; set; }\n        public virtual DbSet<ContentRoulette> ContentRoulettes { get; set; }\n        public virtual DbSet<ContentType> ContentTypes { get; set; }\n        public virtual DbSet<CraftType> CraftTypes { get; set; }\n        public virtual DbSet<Emote> Emotes { get; set; }\n        public virtual DbSet<ENpcResident> ENpcResidents { get; set; }\n        public virtual DbSet<ENpcResident_PlaceName> ENpcResident_PlaceName { get; set; }\n        public virtual DbSet<ENpcResident_Quest> ENpcResident_Quest { get; set; }\n        public virtual DbSet<FCHierarchy> FCHierarchies { get; set; }\n        public virtual DbSet<FCRank> FCRanks { get; set; }\n        public virtual DbSet<FCReputation> FCReputations { get; set; }\n        public virtual DbSet<Frontline01> Frontline01 { get; set; }\n        public virtual DbSet<Gathering> Gatherings { get; set; }\n        public virtual DbSet<GatheringType> GatheringTypes { get; set; }\n        public virtual DbSet<GCRankGridaniaFemaleText> GCRankGridaniaFemaleTexts { get; set; }\n        public virtual DbSet<GCRankGridaniaMaleText> GCRankGridaniaMaleTexts { get; set; }\n        public virtual DbSet<GCRankLimsaFemaleText> GCRankLimsaFemaleTexts { get; set; }\n        public virtual DbSet<GCRankLimsaMaleText> GCRankLimsaMaleTexts { get; set; }\n        public virtual DbSet<GCRankUldahFemaleText> GCRankUldahFemaleTexts { get; set; }\n        public virtual DbSet<GCRankUldahMaleText> GCRankUldahMaleTexts { get; set; }\n        public virtual DbSet<GeneralAction> GeneralActions { get; set; }\n        public virtual DbSet<GrandCompany> GrandCompanies { get; set; }\n        public virtual DbSet<GuardianDeity> GuardianDeities { get; set; }\n        public virtual DbSet<GuildOrder> GuildOrders { get; set; }\n        public virtual DbSet<InstanceContent> InstanceContents { get; set; }\n        public virtual DbSet<InstanceContentType> InstanceContentTypes { get; set; }\n        public virtual DbSet<Item> Items { get; set; }\n        public virtual DbSet<Item_ClassJob> Item_ClassJob { get; set; }\n        public virtual DbSet<ItemCategory> ItemCategories { get; set; }\n        public virtual DbSet<ItemSery> ItemSeries { get; set; }\n        public virtual DbSet<ItemSpecialBonu> ItemSpecialBonus { get; set; }\n        public virtual DbSet<ItemUICategory> ItemUICategories { get; set; }\n        public virtual DbSet<ItemUIKind> ItemUIKinds { get; set; }\n        public virtual DbSet<JournalCategory> JournalCategories { get; set; }\n        public virtual DbSet<JournalGenre> JournalGenres { get; set; }\n        public virtual DbSet<LodestoneSystemDefine> LodestoneSystemDefines { get; set; }\n        public virtual DbSet<NotebookDivision> NotebookDivisions { get; set; }\n        public virtual DbSet<PlaceName> PlaceNames { get; set; }\n        public virtual DbSet<Quest> Quests { get; set; }\n        public virtual DbSet<Quest_ClassJob> Quest_ClassJob { get; set; }\n        public virtual DbSet<QuestWebEx> QuestWebExes { get; set; }\n        public virtual DbSet<QuestWebType> QuestWebTypes { get; set; }\n        public virtual DbSet<Race> Races { get; set; }\n        public virtual DbSet<Recipe> Recipes { get; set; }\n        public virtual DbSet<RecipeElement> RecipeElements { get; set; }\n        public virtual DbSet<Shop> Shops { get; set; }\n        public virtual DbSet<Status> Status { get; set; }\n        public virtual DbSet<Title> Titles { get; set; }\n        public virtual DbSet<Tomestone> Tomestones { get; set; }\n        public virtual DbSet<Town> Towns { get; set; }\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Libra/LibraModel.Context.tt",
    "content": "﻿<#@ template language=\"C#\" debug=\"false\" hostspecific=\"true\"#>\n<#@ include file=\"EF6.Utility.CS.ttinclude\"#><#@\n output extension=\".cs\"#><#\n\nconst string inputFile = @\"LibraModel.edmx\";\nvar textTransform = DynamicTextTransformation.Create(this);\nvar code = new CodeGenerationTools(this);\nvar ef = new MetadataTools(this);\nvar typeMapper = new TypeMapper(code, ef, textTransform.Errors);\nvar loader = new EdmMetadataLoader(textTransform.Host, textTransform.Errors);\nvar itemCollection = loader.CreateEdmItemCollection(inputFile);\nvar modelNamespace = loader.GetModelNamespace(inputFile);\nvar codeStringGenerator = new CodeStringGenerator(code, typeMapper, ef);\n\nvar container = itemCollection.OfType<EntityContainer>().FirstOrDefault();\nif (container == null)\n{\n    return string.Empty;\n}\n#>\n//------------------------------------------------------------------------------\n// <auto-generated>\n// <#=CodeGenerationTools.GetResourceString(\"Template_GeneratedCodeCommentLine1\")#>\n//\n// <#=CodeGenerationTools.GetResourceString(\"Template_GeneratedCodeCommentLine2\")#>\n// <#=CodeGenerationTools.GetResourceString(\"Template_GeneratedCodeCommentLine3\")#>\n// </auto-generated>\n//------------------------------------------------------------------------------\n\n<#\n\nvar codeNamespace = code.VsNamespaceSuggestion();\nif (!String.IsNullOrEmpty(codeNamespace))\n{\n#>\nnamespace <#=code.EscapeNamespace(codeNamespace)#>\n{\n<#\n    PushIndent(\"    \");\n}\n\n#>\nusing System;\nusing System.Data.Entity;\nusing System.Data.Entity.Infrastructure;\n<#\nif (container.FunctionImports.Any())\n{\n#>\nusing System.Data.Entity.Core.Objects;\nusing System.Linq;\n<#\n}\n#>\n\n<#=Accessibility.ForType(container)#> partial class <#=code.Escape(container)#> : DbContext\n{\n    public <#=code.Escape(container)#>()\n        : base(\"name=<#=container.Name#>\")\n    {\n<#\nif (!loader.IsLazyLoadingEnabled(container))\n{\n#>\n        this.Configuration.LazyLoadingEnabled = false;\n<#\n}\n\nforeach (var entitySet in container.BaseEntitySets.OfType<EntitySet>())\n{\n    // Note: the DbSet members are defined below such that the getter and\n    // setter always have the same accessibility as the DbSet definition\n    if (Accessibility.ForReadOnlyProperty(entitySet) != \"public\")\n    {\n#>\n        <#=codeStringGenerator.DbSetInitializer(entitySet)#>\n<#\n    }\n}\n#>\n    }\n\n    protected override void OnModelCreating(DbModelBuilder modelBuilder)\n    {\n        throw new UnintentionalCodeFirstException();\n    }\n\n<#\n    foreach (var entitySet in container.BaseEntitySets.OfType<EntitySet>())\n    {\n#>\n    <#=codeStringGenerator.DbSet(entitySet)#>\n<#\n    }\n\n    foreach (var edmFunction in container.FunctionImports)\n    {\n        WriteFunctionImport(typeMapper, codeStringGenerator, edmFunction, modelNamespace, includeMergeOption: false);\n    }\n#>\n}\n<#\n\nif (!String.IsNullOrEmpty(codeNamespace))\n{\n    PopIndent();\n#>\n}\n<#\n}\n#>\n<#+\n\nprivate void WriteFunctionImport(TypeMapper typeMapper, CodeStringGenerator codeStringGenerator, EdmFunction edmFunction, string modelNamespace, bool includeMergeOption)\n{\n    if (typeMapper.IsComposable(edmFunction))\n    {\n#>\n\n    [DbFunction(\"<#=edmFunction.NamespaceName#>\", \"<#=edmFunction.Name#>\")]\n    <#=codeStringGenerator.ComposableFunctionMethod(edmFunction, modelNamespace)#>\n    {\n<#+\n        codeStringGenerator.WriteFunctionParameters(edmFunction, WriteFunctionParameter);\n#>\n        <#=codeStringGenerator.ComposableCreateQuery(edmFunction, modelNamespace)#>\n    }\n<#+\n    }\n    else\n    {\n#>\n\n    <#=codeStringGenerator.FunctionMethod(edmFunction, modelNamespace, includeMergeOption)#>\n    {\n<#+\n        codeStringGenerator.WriteFunctionParameters(edmFunction, WriteFunctionParameter);\n#>\n        <#=codeStringGenerator.ExecuteFunction(edmFunction, modelNamespace, includeMergeOption)#>\n    }\n<#+\n        if (typeMapper.GenerateMergeOptionFunction(edmFunction, includeMergeOption))\n        {\n            WriteFunctionImport(typeMapper, codeStringGenerator, edmFunction, modelNamespace, includeMergeOption: true);\n        }\n    }\n}\n\npublic void WriteFunctionParameter(string name, string isNotNull, string notNullInit, string nullInit)\n{\n#>\n        var <#=name#> = <#=isNotNull#> ?\n            <#=notNullInit#> :\n            <#=nullInit#>;\n\n<#+\n}\n\npublic const string TemplateId = \"CSharp_DbContext_Context_EF6\";\n\npublic class CodeStringGenerator\n{\n    private readonly CodeGenerationTools _code;\n    private readonly TypeMapper _typeMapper;\n    private readonly MetadataTools _ef;\n\n    public CodeStringGenerator(CodeGenerationTools code, TypeMapper typeMapper, MetadataTools ef)\n    {\n        ArgumentNotNull(code, \"code\");\n        ArgumentNotNull(typeMapper, \"typeMapper\");\n        ArgumentNotNull(ef, \"ef\");\n\n        _code = code;\n        _typeMapper = typeMapper;\n        _ef = ef;\n    }\n\n    public string Property(EdmProperty edmProperty)\n    {\n        return string.Format(\n            CultureInfo.InvariantCulture,\n            \"{0} {1} {2} {{ {3}get; {4}set; }}\",\n            Accessibility.ForProperty(edmProperty),\n            _typeMapper.GetTypeName(edmProperty.TypeUsage),\n            _code.Escape(edmProperty),\n            _code.SpaceAfter(Accessibility.ForGetter(edmProperty)),\n            _code.SpaceAfter(Accessibility.ForSetter(edmProperty)));\n    }\n\n    public string NavigationProperty(NavigationProperty navProp)\n    {\n        var endType = _typeMapper.GetTypeName(navProp.ToEndMember.GetEntityType());\n        return string.Format(\n            CultureInfo.InvariantCulture,\n            \"{0} {1} {2} {{ {3}get; {4}set; }}\",\n            AccessibilityAndVirtual(Accessibility.ForNavigationProperty(navProp)),\n            navProp.ToEndMember.RelationshipMultiplicity == RelationshipMultiplicity.Many ? (\"ICollection<\" + endType + \">\") : endType,\n            _code.Escape(navProp),\n            _code.SpaceAfter(Accessibility.ForGetter(navProp)),\n            _code.SpaceAfter(Accessibility.ForSetter(navProp)));\n    }\n    \n    public string AccessibilityAndVirtual(string accessibility)\n    {\n        return accessibility + (accessibility != \"private\" ? \" virtual\" : \"\");\n    }\n    \n    public string EntityClassOpening(EntityType entity)\n    {\n        return string.Format(\n            CultureInfo.InvariantCulture,\n            \"{0} {1}partial class {2}{3}\",\n            Accessibility.ForType(entity),\n            _code.SpaceAfter(_code.AbstractOption(entity)),\n            _code.Escape(entity),\n            _code.StringBefore(\" : \", _typeMapper.GetTypeName(entity.BaseType)));\n    }\n    \n    public string EnumOpening(SimpleType enumType)\n    {\n        return string.Format(\n            CultureInfo.InvariantCulture,\n            \"{0} enum {1} : {2}\",\n            Accessibility.ForType(enumType),\n            _code.Escape(enumType),\n            _code.Escape(_typeMapper.UnderlyingClrType(enumType)));\n        }\n    \n    public void WriteFunctionParameters(EdmFunction edmFunction, Action<string, string, string, string> writeParameter)\n    {\n        var parameters = FunctionImportParameter.Create(edmFunction.Parameters, _code, _ef);\n        foreach (var parameter in parameters.Where(p => p.NeedsLocalVariable))\n        {\n            var isNotNull = parameter.IsNullableOfT ? parameter.FunctionParameterName + \".HasValue\" : parameter.FunctionParameterName + \" != null\";\n            var notNullInit = \"new ObjectParameter(\\\"\" + parameter.EsqlParameterName + \"\\\", \" + parameter.FunctionParameterName + \")\";\n            var nullInit = \"new ObjectParameter(\\\"\" + parameter.EsqlParameterName + \"\\\", typeof(\" + TypeMapper.FixNamespaces(parameter.RawClrTypeName) + \"))\";\n            writeParameter(parameter.LocalVariableName, isNotNull, notNullInit, nullInit);\n        }\n    }\n    \n    public string ComposableFunctionMethod(EdmFunction edmFunction, string modelNamespace)\n    {\n        var parameters = _typeMapper.GetParameters(edmFunction);\n        \n        return string.Format(\n            CultureInfo.InvariantCulture,\n            \"{0} IQueryable<{1}> {2}({3})\",\n            AccessibilityAndVirtual(Accessibility.ForMethod(edmFunction)),\n            _typeMapper.GetTypeName(_typeMapper.GetReturnType(edmFunction), modelNamespace),\n            _code.Escape(edmFunction),\n            string.Join(\", \", parameters.Select(p => TypeMapper.FixNamespaces(p.FunctionParameterType) + \" \" + p.FunctionParameterName).ToArray()));\n    }\n    \n    public string ComposableCreateQuery(EdmFunction edmFunction, string modelNamespace)\n    {\n        var parameters = _typeMapper.GetParameters(edmFunction);\n        \n        return string.Format(\n            CultureInfo.InvariantCulture,\n            \"return ((IObjectContextAdapter)this).ObjectContext.CreateQuery<{0}>(\\\"[{1}].[{2}]({3})\\\"{4});\",\n            _typeMapper.GetTypeName(_typeMapper.GetReturnType(edmFunction), modelNamespace),\n            edmFunction.NamespaceName,\n            edmFunction.Name,\n            string.Join(\", \", parameters.Select(p => \"@\" + p.EsqlParameterName).ToArray()),\n            _code.StringBefore(\", \", string.Join(\", \", parameters.Select(p => p.ExecuteParameterName).ToArray())));\n    }\n    \n    public string FunctionMethod(EdmFunction edmFunction, string modelNamespace, bool includeMergeOption)\n    {\n        var parameters = _typeMapper.GetParameters(edmFunction);\n        var returnType = _typeMapper.GetReturnType(edmFunction);\n\n        var paramList = String.Join(\", \", parameters.Select(p => TypeMapper.FixNamespaces(p.FunctionParameterType) + \" \" + p.FunctionParameterName).ToArray());\n        if (includeMergeOption)\n        {\n            paramList = _code.StringAfter(paramList, \", \") + \"MergeOption mergeOption\";\n        }\n\n        return string.Format(\n            CultureInfo.InvariantCulture,\n            \"{0} {1} {2}({3})\",\n            AccessibilityAndVirtual(Accessibility.ForMethod(edmFunction)),\n            returnType == null ? \"int\" : \"ObjectResult<\" + _typeMapper.GetTypeName(returnType, modelNamespace) + \">\",\n            _code.Escape(edmFunction),\n            paramList);\n    }\n    \n    public string ExecuteFunction(EdmFunction edmFunction, string modelNamespace, bool includeMergeOption)\n    {\n        var parameters = _typeMapper.GetParameters(edmFunction);\n        var returnType = _typeMapper.GetReturnType(edmFunction);\n\n        var callParams = _code.StringBefore(\", \", String.Join(\", \", parameters.Select(p => p.ExecuteParameterName).ToArray()));\n        if (includeMergeOption)\n        {\n            callParams = \", mergeOption\" + callParams;\n        }\n        \n        return string.Format(\n            CultureInfo.InvariantCulture,\n            \"return ((IObjectContextAdapter)this).ObjectContext.ExecuteFunction{0}(\\\"{1}\\\"{2});\",\n            returnType == null ? \"\" : \"<\" + _typeMapper.GetTypeName(returnType, modelNamespace) + \">\",\n            edmFunction.Name,\n            callParams);\n    }\n    \n    public string DbSet(EntitySet entitySet)\n    {\n        return string.Format(\n            CultureInfo.InvariantCulture,\n            \"{0} virtual DbSet<{1}> {2} {{ get; set; }}\",\n            Accessibility.ForReadOnlyProperty(entitySet),\n            _typeMapper.GetTypeName(entitySet.ElementType),\n            _code.Escape(entitySet));\n    }\n\n    public string DbSetInitializer(EntitySet entitySet)\n    {\n        return string.Format(\n            CultureInfo.InvariantCulture,\n            \"{0} = Set<{1}>();\",\n            _code.Escape(entitySet),\n            _typeMapper.GetTypeName(entitySet.ElementType));\n    }\n\n    public string UsingDirectives(bool inHeader, bool includeCollections = true)\n    {\n        return inHeader == string.IsNullOrEmpty(_code.VsNamespaceSuggestion())\n            ? string.Format(\n                CultureInfo.InvariantCulture,\n                \"{0}using System;{1}\" +\n                \"{2}\",\n                inHeader ? Environment.NewLine : \"\",\n                includeCollections ? (Environment.NewLine + \"using System.Collections.Generic;\") : \"\",\n                inHeader ? \"\" : Environment.NewLine)\n            : \"\";\n    }\n}\n\npublic class TypeMapper\n{\n    private const string ExternalTypeNameAttributeName = @\"http://schemas.microsoft.com/ado/2006/04/codegeneration:ExternalTypeName\";\n\n    private readonly System.Collections.IList _errors;\n    private readonly CodeGenerationTools _code;\n    private readonly MetadataTools _ef;\n\n    public static string FixNamespaces(string typeName)\n    {\n        return typeName.Replace(\"System.Data.Spatial.\", \"System.Data.Entity.Spatial.\");\n    }\n\n    public TypeMapper(CodeGenerationTools code, MetadataTools ef, System.Collections.IList errors)\n    {\n        ArgumentNotNull(code, \"code\");\n        ArgumentNotNull(ef, \"ef\");\n        ArgumentNotNull(errors, \"errors\");\n\n        _code = code;\n        _ef = ef;\n        _errors = errors;\n    }\n\n    public string GetTypeName(TypeUsage typeUsage)\n    {\n        return typeUsage == null ? null : GetTypeName(typeUsage.EdmType, _ef.IsNullable(typeUsage), modelNamespace: null);\n    }\n\n    public string GetTypeName(EdmType edmType)\n    {\n        return GetTypeName(edmType, isNullable: null, modelNamespace: null);\n    }\n\n    public string GetTypeName(TypeUsage typeUsage, string modelNamespace)\n    {\n        return typeUsage == null ? null : GetTypeName(typeUsage.EdmType, _ef.IsNullable(typeUsage), modelNamespace);\n    }\n\n    public string GetTypeName(EdmType edmType, string modelNamespace)\n    {\n        return GetTypeName(edmType, isNullable: null, modelNamespace: modelNamespace);\n    }\n\n    public string GetTypeName(EdmType edmType, bool? isNullable, string modelNamespace)\n    {\n        if (edmType == null)\n        {\n            return null;\n        }\n\n        var collectionType = edmType as CollectionType;\n        if (collectionType != null)\n        {\n            return String.Format(CultureInfo.InvariantCulture, \"ICollection<{0}>\", GetTypeName(collectionType.TypeUsage, modelNamespace));\n        }\n\n        var typeName = _code.Escape(edmType.MetadataProperties\n                                .Where(p => p.Name == ExternalTypeNameAttributeName)\n                                .Select(p => (string)p.Value)\n                                .FirstOrDefault())\n            ?? (modelNamespace != null && edmType.NamespaceName != modelNamespace ?\n                _code.CreateFullName(_code.EscapeNamespace(edmType.NamespaceName), _code.Escape(edmType)) :\n                _code.Escape(edmType));\n\n        if (edmType is StructuralType)\n        {\n            return typeName;\n        }\n\n        if (edmType is SimpleType)\n        {\n            var clrType = UnderlyingClrType(edmType);\n            if (!IsEnumType(edmType))\n            {\n                typeName = _code.Escape(clrType);\n            }\n\n            typeName = FixNamespaces(typeName);\n\n            return clrType.IsValueType && isNullable == true ?\n                String.Format(CultureInfo.InvariantCulture, \"Nullable<{0}>\", typeName) :\n                typeName;\n        }\n\n        throw new ArgumentException(\"edmType\");\n    }\n    \n    public Type UnderlyingClrType(EdmType edmType)\n    {\n        ArgumentNotNull(edmType, \"edmType\");\n\n        var primitiveType = edmType as PrimitiveType;\n        if (primitiveType != null)\n        {\n            return primitiveType.ClrEquivalentType;\n        }\n\n        if (IsEnumType(edmType))\n        {\n            return GetEnumUnderlyingType(edmType).ClrEquivalentType;\n        }\n\n        return typeof(object);\n    }\n    \n    public object GetEnumMemberValue(MetadataItem enumMember)\n    {\n        ArgumentNotNull(enumMember, \"enumMember\");\n        \n        var valueProperty = enumMember.GetType().GetProperty(\"Value\");\n        return valueProperty == null ? null : valueProperty.GetValue(enumMember, null);\n    }\n    \n    public string GetEnumMemberName(MetadataItem enumMember)\n    {\n        ArgumentNotNull(enumMember, \"enumMember\");\n        \n        var nameProperty = enumMember.GetType().GetProperty(\"Name\");\n        return nameProperty == null ? null : (string)nameProperty.GetValue(enumMember, null);\n    }\n\n    public System.Collections.IEnumerable GetEnumMembers(EdmType enumType)\n    {\n        ArgumentNotNull(enumType, \"enumType\");\n\n        var membersProperty = enumType.GetType().GetProperty(\"Members\");\n        return membersProperty != null \n            ? (System.Collections.IEnumerable)membersProperty.GetValue(enumType, null)\n            : Enumerable.Empty<MetadataItem>();\n    }\n    \n    public bool EnumIsFlags(EdmType enumType)\n    {\n        ArgumentNotNull(enumType, \"enumType\");\n        \n        var isFlagsProperty = enumType.GetType().GetProperty(\"IsFlags\");\n        return isFlagsProperty != null && (bool)isFlagsProperty.GetValue(enumType, null);\n    }\n\n    public bool IsEnumType(GlobalItem edmType)\n    {\n        ArgumentNotNull(edmType, \"edmType\");\n\n        return edmType.GetType().Name == \"EnumType\";\n    }\n\n    public PrimitiveType GetEnumUnderlyingType(EdmType enumType)\n    {\n        ArgumentNotNull(enumType, \"enumType\");\n\n        return (PrimitiveType)enumType.GetType().GetProperty(\"UnderlyingType\").GetValue(enumType, null);\n    }\n\n    public string CreateLiteral(object value)\n    {\n        if (value == null || value.GetType() != typeof(TimeSpan))\n        {\n            return _code.CreateLiteral(value);\n        }\n\n        return string.Format(CultureInfo.InvariantCulture, \"new TimeSpan({0})\", ((TimeSpan)value).Ticks);\n    }\n    \n    public bool VerifyCaseInsensitiveTypeUniqueness(IEnumerable<string> types, string sourceFile)\n    {\n        ArgumentNotNull(types, \"types\");\n        ArgumentNotNull(sourceFile, \"sourceFile\");\n        \n        var hash = new HashSet<string>(StringComparer.InvariantCultureIgnoreCase);\n        if (types.Any(item => !hash.Add(item)))\n        {\n            _errors.Add(\n                new CompilerError(sourceFile, -1, -1, \"6023\",\n                    String.Format(CultureInfo.CurrentCulture, CodeGenerationTools.GetResourceString(\"Template_CaseInsensitiveTypeConflict\"))));\n            return false;\n        }\n        return true;\n    }\n    \n    public IEnumerable<SimpleType> GetEnumItemsToGenerate(IEnumerable<GlobalItem> itemCollection)\n    {\n        return GetItemsToGenerate<SimpleType>(itemCollection)\n            .Where(e => IsEnumType(e));\n    }\n    \n    public IEnumerable<T> GetItemsToGenerate<T>(IEnumerable<GlobalItem> itemCollection) where T: EdmType\n    {\n        return itemCollection\n            .OfType<T>()\n            .Where(i => !i.MetadataProperties.Any(p => p.Name == ExternalTypeNameAttributeName))\n            .OrderBy(i => i.Name);\n    }\n\n    public IEnumerable<string> GetAllGlobalItems(IEnumerable<GlobalItem> itemCollection)\n    {\n        return itemCollection\n            .Where(i => i is EntityType || i is ComplexType || i is EntityContainer || IsEnumType(i))\n            .Select(g => GetGlobalItemName(g));\n    }\n\n    public string GetGlobalItemName(GlobalItem item)\n    {\n        if (item is EdmType)\n        {\n            return ((EdmType)item).Name;\n        }\n        else\n        {\n            return ((EntityContainer)item).Name;\n        }\n    }\n\n    public IEnumerable<EdmProperty> GetSimpleProperties(EntityType type)\n    {\n        return type.Properties.Where(p => p.TypeUsage.EdmType is SimpleType && p.DeclaringType == type);\n    }\n    \n    public IEnumerable<EdmProperty> GetSimpleProperties(ComplexType type)\n    {\n        return type.Properties.Where(p => p.TypeUsage.EdmType is SimpleType && p.DeclaringType == type);\n    }\n    \n    public IEnumerable<EdmProperty> GetComplexProperties(EntityType type)\n    {\n        return type.Properties.Where(p => p.TypeUsage.EdmType is ComplexType && p.DeclaringType == type);\n    }\n    \n    public IEnumerable<EdmProperty> GetComplexProperties(ComplexType type)\n    {\n        return type.Properties.Where(p => p.TypeUsage.EdmType is ComplexType && p.DeclaringType == type);\n    }\n\n    public IEnumerable<EdmProperty> GetPropertiesWithDefaultValues(EntityType type)\n    {\n        return type.Properties.Where(p => p.TypeUsage.EdmType is SimpleType && p.DeclaringType == type && p.DefaultValue != null);\n    }\n    \n    public IEnumerable<EdmProperty> GetPropertiesWithDefaultValues(ComplexType type)\n    {\n        return type.Properties.Where(p => p.TypeUsage.EdmType is SimpleType && p.DeclaringType == type && p.DefaultValue != null);\n    }\n\n    public IEnumerable<NavigationProperty> GetNavigationProperties(EntityType type)\n    {\n        return type.NavigationProperties.Where(np => np.DeclaringType == type);\n    }\n    \n    public IEnumerable<NavigationProperty> GetCollectionNavigationProperties(EntityType type)\n    {\n        return type.NavigationProperties.Where(np => np.DeclaringType == type && np.ToEndMember.RelationshipMultiplicity == RelationshipMultiplicity.Many);\n    }\n    \n    public FunctionParameter GetReturnParameter(EdmFunction edmFunction)\n    {\n        ArgumentNotNull(edmFunction, \"edmFunction\");\n\n        var returnParamsProperty = edmFunction.GetType().GetProperty(\"ReturnParameters\");\n        return returnParamsProperty == null\n            ? edmFunction.ReturnParameter\n            : ((IEnumerable<FunctionParameter>)returnParamsProperty.GetValue(edmFunction, null)).FirstOrDefault();\n    }\n\n    public bool IsComposable(EdmFunction edmFunction)\n    {\n        ArgumentNotNull(edmFunction, \"edmFunction\");\n\n        var isComposableProperty = edmFunction.GetType().GetProperty(\"IsComposableAttribute\");\n        return isComposableProperty != null && (bool)isComposableProperty.GetValue(edmFunction, null);\n    }\n\n    public IEnumerable<FunctionImportParameter> GetParameters(EdmFunction edmFunction)\n    {\n        return FunctionImportParameter.Create(edmFunction.Parameters, _code, _ef);\n    }\n\n    public TypeUsage GetReturnType(EdmFunction edmFunction)\n    {\n        var returnParam = GetReturnParameter(edmFunction);\n        return returnParam == null ? null : _ef.GetElementType(returnParam.TypeUsage);\n    }\n    \n    public bool GenerateMergeOptionFunction(EdmFunction edmFunction, bool includeMergeOption)\n    {\n        var returnType = GetReturnType(edmFunction);\n        return !includeMergeOption && returnType != null && returnType.EdmType.BuiltInTypeKind == BuiltInTypeKind.EntityType;\n    }\n}\n\npublic static void ArgumentNotNull<T>(T arg, string name) where T : class\n{\n    if (arg == null)\n    {\n        throw new ArgumentNullException(name);\n    }\n}\n#>"
  },
  {
    "path": "SaintCoinach/Libra/LibraModel.Designer.cs",
    "content": "﻿// T4 code generation is enabled for model 'C:\\Users\\Kalce\\Doc\\Visual Studio 2013\\Projects\\SaintCoinach\\SaintCoinach\\Libra\\LibraModel.edmx'. \n// To enable legacy code generation, change the value of the 'Code Generation Strategy' designer\n// property to 'Legacy ObjectContext'. This property is available in the Properties Window when the model\n// is open in the designer.\n\n// If no context and entity classes have been generated, it may be because you created an empty model but\n// have not yet chosen which version of Entity Framework to use. To generate a context class and entity\n// classes for your model, open the model in the designer, right-click on the designer surface, and\n// select 'Update Model from Database...', 'Generate Database from Model...', or 'Add Code Generation\n// Item...'."
  },
  {
    "path": "SaintCoinach/Libra/LibraModel.cs",
    "content": "﻿//------------------------------------------------------------------------------\n// <auto-generated>\n//     This code was generated from a template.\n//\n//     Manual changes to this file may cause unexpected behavior in your application.\n//     Manual changes to this file will be overwritten if the code is regenerated.\n// </auto-generated>\n//------------------------------------------------------------------------------\n\n"
  },
  {
    "path": "SaintCoinach/Libra/LibraModel.edmx",
    "content": "﻿<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<edmx:Edmx Version=\"3.0\" xmlns:edmx=\"http://schemas.microsoft.com/ado/2009/11/edmx\">\n  <!-- EF Runtime content -->\n  <edmx:Runtime>\n    <!-- SSDL content -->\n    <edmx:StorageModels>\n      <Schema Namespace=\"LibraModel.Store\" Provider=\"System.Data.SQLite.EF6\" ProviderManifestToken=\"ISO8601\" Alias=\"Self\" xmlns:store=\"http://schemas.microsoft.com/ado/2007/12/edm/EntityStoreSchemaGenerator\" xmlns:customannotation=\"http://schemas.microsoft.com/ado/2013/11/edm/customannotation\" xmlns=\"http://schemas.microsoft.com/ado/2009/11/edm/ssdl\">\n        <EntityType Name=\"Achievement\">\n          <Key>\n            <PropertyRef Name=\"Key\" />\n          </Key>\n          <Property Name=\"Key\" Type=\"integer\" Nullable=\"false\" />\n          <Property Name=\"Category\" Type=\"integer\" />\n          <Property Name=\"Name_ja\" Type=\"varchar\" MaxLength=\"255\" />\n          <Property Name=\"Name_en\" Type=\"varchar\" MaxLength=\"255\" />\n          <Property Name=\"Name_fr\" Type=\"varchar\" MaxLength=\"255\" />\n          <Property Name=\"Name_de\" Type=\"varchar\" MaxLength=\"255\" />\n          <Property Name=\"Help_ja\" Type=\"varchar\" MaxLength=\"255\" />\n          <Property Name=\"Help_en\" Type=\"varchar\" MaxLength=\"255\" />\n          <Property Name=\"Help_fr\" Type=\"varchar\" MaxLength=\"255\" />\n          <Property Name=\"Help_de\" Type=\"varchar\" MaxLength=\"255\" />\n          <Property Name=\"Point\" Type=\"integer\" />\n          <Property Name=\"Item\" Type=\"integer\" />\n          <Property Name=\"Icon\" Type=\"integer\" />\n          <Property Name=\"Title\" Type=\"integer\" />\n          <Property Name=\"Priority\" Type=\"integer\" />\n          <Property Name=\"data\" Type=\"blob\" />\n          <Property Name=\"path\" Type=\"nvarchar\" MaxLength=\"2147483647\" />\n          <Property Name=\"Index_ja\" Type=\"nvarchar\" MaxLength=\"2147483647\" />\n          <Property Name=\"Index_en\" Type=\"nvarchar\" MaxLength=\"2147483647\" />\n          <Property Name=\"Index_fr\" Type=\"nvarchar\" MaxLength=\"2147483647\" />\n          <Property Name=\"Index_de\" Type=\"nvarchar\" MaxLength=\"2147483647\" />\n        </EntityType>\n        <EntityType Name=\"AchievementCategory\">\n          <Key>\n            <PropertyRef Name=\"Key\" />\n          </Key>\n          <Property Name=\"Key\" Type=\"integer\" Nullable=\"false\" />\n          <Property Name=\"Kind\" Type=\"integer\" />\n          <Property Name=\"Name_ja\" Type=\"varchar\" MaxLength=\"255\" />\n          <Property Name=\"Name_en\" Type=\"varchar\" MaxLength=\"255\" />\n          <Property Name=\"Name_fr\" Type=\"varchar\" MaxLength=\"255\" />\n          <Property Name=\"Name_de\" Type=\"varchar\" MaxLength=\"255\" />\n        </EntityType>\n        <EntityType Name=\"AchievementKind\">\n          <Key>\n            <PropertyRef Name=\"Key\" />\n          </Key>\n          <Property Name=\"Key\" Type=\"integer\" Nullable=\"false\" />\n          <Property Name=\"Name_ja\" Type=\"varchar\" MaxLength=\"255\" />\n          <Property Name=\"Name_en\" Type=\"varchar\" MaxLength=\"255\" />\n          <Property Name=\"Name_fr\" Type=\"varchar\" MaxLength=\"255\" />\n          <Property Name=\"Name_de\" Type=\"varchar\" MaxLength=\"255\" />\n        </EntityType>\n        <EntityType Name=\"Action\">\n          <Key>\n            <PropertyRef Name=\"Key\" />\n          </Key>\n          <Property Name=\"Key\" Type=\"integer\" Nullable=\"false\" />\n          <Property Name=\"Icon\" Type=\"integer\" />\n          <Property Name=\"Name_ja\" Type=\"varchar\" MaxLength=\"255\" />\n          <Property Name=\"Name_en\" Type=\"varchar\" MaxLength=\"255\" />\n          <Property Name=\"Name_fr\" Type=\"varchar\" MaxLength=\"255\" />\n          <Property Name=\"Name_de\" Type=\"varchar\" MaxLength=\"255\" />\n          <Property Name=\"HelpWeb_ja\" Type=\"varchar\" MaxLength=\"255\" />\n          <Property Name=\"HelpWeb_en\" Type=\"varchar\" MaxLength=\"255\" />\n          <Property Name=\"HelpWeb_fr\" Type=\"varchar\" MaxLength=\"255\" />\n          <Property Name=\"HelpWeb_de\" Type=\"varchar\" MaxLength=\"255\" />\n        </EntityType>\n        <!--Errors Found During Generation:\nwarning 6013: The table/view 'main.app_data' does not have a primary key defined and no valid primary key could be inferred. This table/view has been excluded. To use the entity, you will need to review your schema, add the correct keys, and uncomment it.\n        <EntityType Name=\"app_data\">\n          <Property Name=\"schema_version\" Type=\"varchar\" MaxLength=\"255\" />\n          <Property Name=\"data_version\" Type=\"varchar\" MaxLength=\"255\" />\n          <Property Name=\"data_sequence_no\" Type=\"integer\" />\n        </EntityType>-->\n        <EntityType Name=\"BaseParam\">\n          <Key>\n            <PropertyRef Name=\"Key\" />\n          </Key>\n          <Property Name=\"Key\" Type=\"integer\" Nullable=\"false\" />\n          <Property Name=\"Name_ja\" Type=\"varchar\" MaxLength=\"255\" />\n          <Property Name=\"Name_en\" Type=\"varchar\" MaxLength=\"255\" />\n          <Property Name=\"Name_fr\" Type=\"varchar\" MaxLength=\"255\" />\n          <Property Name=\"Name_de\" Type=\"varchar\" MaxLength=\"255\" />\n        </EntityType>\n        <EntityType Name=\"BeastTribe\">\n          <Key>\n            <PropertyRef Name=\"Key\" />\n          </Key>\n          <Property Name=\"Key\" Type=\"integer\" Nullable=\"false\" />\n          <Property Name=\"SGL_ja\" Type=\"varchar\" MaxLength=\"255\" />\n          <Property Name=\"SGL_en\" Type=\"varchar\" MaxLength=\"255\" />\n          <Property Name=\"SGL_fr\" Type=\"varchar\" MaxLength=\"255\" />\n          <Property Name=\"SGL_de\" Type=\"varchar\" MaxLength=\"255\" />\n          <Property Name=\"ReputationValue_ja\" Type=\"varchar\" MaxLength=\"255\" />\n          <Property Name=\"ReputationValue_en\" Type=\"varchar\" MaxLength=\"255\" />\n          <Property Name=\"ReputationValue_fr\" Type=\"varchar\" MaxLength=\"255\" />\n          <Property Name=\"ReputationValue_de\" Type=\"varchar\" MaxLength=\"255\" />\n        </EntityType>\n        <EntityType Name=\"BNpcName\">\n          <Key>\n            <PropertyRef Name=\"Key\" />\n          </Key>\n          <Property Name=\"Key\" Type=\"integer\" Nullable=\"false\" />\n          <Property Name=\"SGL_ja\" Type=\"varchar\" MaxLength=\"255\" />\n          <Property Name=\"SGL_en\" Type=\"varchar\" MaxLength=\"255\" />\n          <Property Name=\"SGL_fr\" Type=\"varchar\" MaxLength=\"255\" />\n          <Property Name=\"SGL_de\" Type=\"varchar\" MaxLength=\"255\" />\n          <Property Name=\"area\" Type=\"varchar\" MaxLength=\"255\" />\n          <Property Name=\"data\" Type=\"blob\" />\n          <Property Name=\"path\" Type=\"nvarchar\" MaxLength=\"2147483647\" />\n          <Property Name=\"Index_ja\" Type=\"nvarchar\" MaxLength=\"2147483647\" />\n          <Property Name=\"Index_en\" Type=\"nvarchar\" MaxLength=\"2147483647\" />\n          <Property Name=\"Index_fr\" Type=\"nvarchar\" MaxLength=\"2147483647\" />\n          <Property Name=\"Index_de\" Type=\"nvarchar\" MaxLength=\"2147483647\" />\n        </EntityType>\n        <EntityType Name=\"BNpcName_PlaceName\">\n          <Key>\n            <PropertyRef Name=\"BNpcName_Key\" />\n            <PropertyRef Name=\"PlaceName_Key\" />\n            <PropertyRef Name=\"region\" />\n          </Key>\n          <Property Name=\"BNpcName_Key\" Type=\"integer\" Nullable=\"false\" />\n          <Property Name=\"PlaceName_Key\" Type=\"integer\" Nullable=\"false\" />\n          <Property Name=\"region\" Type=\"integer\" Nullable=\"false\" />\n        </EntityType>\n        <EntityType Name=\"ClassJob\">\n          <Key>\n            <PropertyRef Name=\"Key\" />\n          </Key>\n          <Property Name=\"Key\" Type=\"integer\" Nullable=\"false\" />\n          <Property Name=\"Label\" Type=\"varchar\" MaxLength=\"255\" />\n          <Property Name=\"Name_ja\" Type=\"varchar\" MaxLength=\"255\" />\n          <Property Name=\"Name_en\" Type=\"varchar\" MaxLength=\"255\" />\n          <Property Name=\"Name_fr\" Type=\"varchar\" MaxLength=\"255\" />\n          <Property Name=\"Name_de\" Type=\"varchar\" MaxLength=\"255\" />\n          <Property Name=\"Abbreviation_ja\" Type=\"varchar\" MaxLength=\"255\" />\n          <Property Name=\"Abbreviation_en\" Type=\"varchar\" MaxLength=\"255\" />\n          <Property Name=\"Abbreviation_fr\" Type=\"varchar\" MaxLength=\"255\" />\n          <Property Name=\"Abbreviation_de\" Type=\"varchar\" MaxLength=\"255\" />\n          <Property Name=\"IsJob\" Type=\"integer\" />\n          <Property Name=\"UIPriority\" Type=\"integer\" />\n          <Property Name=\"filebase\" Type=\"varchar\" MaxLength=\"255\" />\n        </EntityType>\n        <EntityType Name=\"ClassJob_ClassJobCategory\">\n          <Key>\n            <PropertyRef Name=\"ClassJob_Key\" />\n            <PropertyRef Name=\"ClassJobCategory_Key\" />\n          </Key>\n          <Property Name=\"ClassJob_Key\" Type=\"integer\" Nullable=\"false\" />\n          <Property Name=\"ClassJobCategory_Key\" Type=\"integer\" Nullable=\"false\" />\n        </EntityType>\n        <EntityType Name=\"ClassJobCategory\">\n          <Key>\n            <PropertyRef Name=\"Key\" />\n          </Key>\n          <Property Name=\"Key\" Type=\"integer\" Nullable=\"false\" />\n          <Property Name=\"Name_ja\" Type=\"varchar\" MaxLength=\"255\" />\n          <Property Name=\"Name_en\" Type=\"varchar\" MaxLength=\"255\" />\n          <Property Name=\"Name_fr\" Type=\"varchar\" MaxLength=\"255\" />\n          <Property Name=\"Name_de\" Type=\"varchar\" MaxLength=\"255\" />\n          <Property Name=\"classjob\" Type=\"varchar\" MaxLength=\"255\" />\n        </EntityType>\n        <EntityType Name=\"Colosseum\">\n          <Key>\n            <PropertyRef Name=\"Key\" />\n          </Key>\n          <Property Name=\"Key\" Type=\"integer\" Nullable=\"false\" />\n          <Property Name=\"WinExp\" Type=\"integer\" />\n          <Property Name=\"WinPoint\" Type=\"integer\" />\n          <Property Name=\"WinToken\" Type=\"integer\" />\n          <Property Name=\"LosePoint\" Type=\"integer\" />\n          <Property Name=\"LoseToken\" Type=\"integer\" />\n        </EntityType>\n        <EntityType Name=\"ContentRoulette\">\n          <Key>\n            <PropertyRef Name=\"Key\" />\n          </Key>\n          <Property Name=\"Key\" Type=\"integer\" Nullable=\"false\" />\n          <Property Name=\"Name_ja\" Type=\"varchar\" MaxLength=\"255\" />\n          <Property Name=\"Name_en\" Type=\"varchar\" MaxLength=\"255\" />\n          <Property Name=\"Name_fr\" Type=\"varchar\" MaxLength=\"255\" />\n          <Property Name=\"Name_de\" Type=\"varchar\" MaxLength=\"255\" />\n        </EntityType>\n        <EntityType Name=\"ContentType\">\n          <Key>\n            <PropertyRef Name=\"Key\" />\n          </Key>\n          <Property Name=\"Key\" Type=\"integer\" Nullable=\"false\" />\n          <Property Name=\"Name_ja\" Type=\"varchar\" MaxLength=\"255\" />\n          <Property Name=\"Name_en\" Type=\"varchar\" MaxLength=\"255\" />\n          <Property Name=\"Name_fr\" Type=\"varchar\" MaxLength=\"255\" />\n          <Property Name=\"Name_de\" Type=\"varchar\" MaxLength=\"255\" />\n        </EntityType>\n        <EntityType Name=\"CraftType\">\n          <Key>\n            <PropertyRef Name=\"Key\" />\n          </Key>\n          <Property Name=\"Key\" Type=\"integer\" Nullable=\"false\" />\n          <Property Name=\"ClassJob\" Type=\"integer\" />\n        </EntityType>\n        <EntityType Name=\"Emote\">\n          <Key>\n            <PropertyRef Name=\"Key\" />\n          </Key>\n          <Property Name=\"Key\" Type=\"integer\" Nullable=\"false\" />\n          <Property Name=\"Icon\" Type=\"integer\" />\n          <Property Name=\"Name_ja\" Type=\"varchar\" MaxLength=\"255\" />\n          <Property Name=\"Name_en\" Type=\"varchar\" MaxLength=\"255\" />\n          <Property Name=\"Name_fr\" Type=\"varchar\" MaxLength=\"255\" />\n          <Property Name=\"Name_de\" Type=\"varchar\" MaxLength=\"255\" />\n        </EntityType>\n        <EntityType Name=\"ENpcResident\">\n          <Key>\n            <PropertyRef Name=\"Key\" />\n          </Key>\n          <Property Name=\"Key\" Type=\"integer\" Nullable=\"false\" />\n          <Property Name=\"SGL_ja\" Type=\"varchar\" MaxLength=\"255\" />\n          <Property Name=\"SGL_en\" Type=\"varchar\" MaxLength=\"255\" />\n          <Property Name=\"SGL_fr\" Type=\"varchar\" MaxLength=\"255\" />\n          <Property Name=\"SGL_de\" Type=\"varchar\" MaxLength=\"255\" />\n          <Property Name=\"area\" Type=\"varchar\" MaxLength=\"255\" />\n          <Property Name=\"data\" Type=\"blob\" />\n          <Property Name=\"path\" Type=\"nvarchar\" MaxLength=\"2147483647\" />\n          <Property Name=\"has_shop\" Type=\"bit\" />\n          <Property Name=\"has_quest\" Type=\"bit\" />\n          <Property Name=\"has_condition_shop\" Type=\"bit\" />\n          <Property Name=\"Index_ja\" Type=\"nvarchar\" MaxLength=\"2147483647\" />\n          <Property Name=\"Index_en\" Type=\"nvarchar\" MaxLength=\"2147483647\" />\n          <Property Name=\"Index_fr\" Type=\"nvarchar\" MaxLength=\"2147483647\" />\n          <Property Name=\"Index_de\" Type=\"nvarchar\" MaxLength=\"2147483647\" />\n        </EntityType>\n        <EntityType Name=\"ENpcResident_PlaceName\">\n          <Key>\n            <PropertyRef Name=\"ENpcResident_Key\" />\n            <PropertyRef Name=\"PlaceName_Key\" />\n            <PropertyRef Name=\"region\" />\n          </Key>\n          <Property Name=\"ENpcResident_Key\" Type=\"integer\" Nullable=\"false\" />\n          <Property Name=\"PlaceName_Key\" Type=\"integer\" Nullable=\"false\" />\n          <Property Name=\"region\" Type=\"integer\" Nullable=\"false\" />\n        </EntityType>\n        <EntityType Name=\"ENpcResident_Quest\">\n          <Key>\n            <PropertyRef Name=\"ENpcResident_Key\" />\n            <PropertyRef Name=\"Quest_Key\" />\n          </Key>\n          <Property Name=\"ENpcResident_Key\" Type=\"integer\" Nullable=\"false\" />\n          <Property Name=\"Quest_Key\" Type=\"integer\" Nullable=\"false\" />\n        </EntityType>\n        <EntityType Name=\"FCHierarchy\">\n          <Key>\n            <PropertyRef Name=\"Key\" />\n          </Key>\n          <Property Name=\"Key\" Type=\"integer\" Nullable=\"false\" />\n          <Property Name=\"Name_ja\" Type=\"varchar\" MaxLength=\"255\" />\n          <Property Name=\"Name_en\" Type=\"varchar\" MaxLength=\"255\" />\n          <Property Name=\"Name_fr\" Type=\"varchar\" MaxLength=\"255\" />\n          <Property Name=\"Name_de\" Type=\"varchar\" MaxLength=\"255\" />\n        </EntityType>\n        <EntityType Name=\"FCRank\">\n          <Key>\n            <PropertyRef Name=\"Key\" />\n          </Key>\n          <Property Name=\"Key\" Type=\"integer\" Nullable=\"false\" />\n          <Property Name=\"CurrentPoint\" Type=\"integer\" />\n          <Property Name=\"NextPoint\" Type=\"integer\" />\n        </EntityType>\n        <EntityType Name=\"FCReputation\">\n          <Key>\n            <PropertyRef Name=\"Key\" />\n          </Key>\n          <Property Name=\"Key\" Type=\"integer\" Nullable=\"false\" />\n          <Property Name=\"Color\" Type=\"integer\" />\n          <Property Name=\"CurrentPoint\" Type=\"integer\" />\n          <Property Name=\"NextPoint\" Type=\"integer\" />\n          <Property Name=\"Text_ja\" Type=\"varchar\" MaxLength=\"255\" />\n          <Property Name=\"Text_en\" Type=\"varchar\" MaxLength=\"255\" />\n          <Property Name=\"Text_fr\" Type=\"varchar\" MaxLength=\"255\" />\n          <Property Name=\"Text_de\" Type=\"varchar\" MaxLength=\"255\" />\n          <Property Name=\"data\" Type=\"blob\" />\n        </EntityType>\n        <EntityType Name=\"Frontline01\">\n          <Key>\n            <PropertyRef Name=\"Key\" />\n          </Key>\n          <Property Name=\"Key\" Type=\"integer\" Nullable=\"false\" />\n          <Property Name=\"WinPvPExp\" Type=\"nvarchar\" MaxLength=\"2147483647\" />\n          <Property Name=\"WinPvPToken\" Type=\"nvarchar\" MaxLength=\"2147483647\" />\n        </EntityType>\n        <EntityType Name=\"Gathering\">\n          <Key>\n            <PropertyRef Name=\"Key\" />\n          </Key>\n          <Property Name=\"Key\" Type=\"integer\" Nullable=\"false\" />\n          <Property Name=\"Item\" Type=\"integer\" />\n          <Property Name=\"GatheringType\" Type=\"integer\" />\n          <Property Name=\"Level\" Type=\"integer\" />\n          <Property Name=\"levelView\" Type=\"integer\" />\n          <Property Name=\"levelDiff\" Type=\"integer\" />\n          <Property Name=\"is_hidden\" Type=\"bit\" />\n          <Property Name=\"data\" Type=\"blob\" />\n          <Property Name=\"GatheringNotebookList\" Type=\"integer\" />\n          <Property Name=\"GatheringItemNo\" Type=\"integer\" />\n          <Property Name=\"path\" Type=\"nvarchar\" MaxLength=\"2147483647\" />\n          <Property Name=\"Index_ja\" Type=\"nvarchar\" MaxLength=\"2147483647\" />\n          <Property Name=\"Index_en\" Type=\"nvarchar\" MaxLength=\"2147483647\" />\n          <Property Name=\"Index_fr\" Type=\"nvarchar\" MaxLength=\"2147483647\" />\n          <Property Name=\"Index_de\" Type=\"nvarchar\" MaxLength=\"2147483647\" />\n        </EntityType>\n        <EntityType Name=\"GatheringType\">\n          <Key>\n            <PropertyRef Name=\"Key\" />\n          </Key>\n          <Property Name=\"Key\" Type=\"integer\" Nullable=\"false\" />\n          <Property Name=\"Text_de\" Type=\"varchar\" MaxLength=\"255\" />\n          <Property Name=\"Text_en\" Type=\"varchar\" MaxLength=\"255\" />\n          <Property Name=\"Text_ja\" Type=\"varchar\" MaxLength=\"255\" />\n          <Property Name=\"Text_fr\" Type=\"varchar\" MaxLength=\"255\" />\n        </EntityType>\n        <EntityType Name=\"GCRankGridaniaFemaleText\">\n          <Key>\n            <PropertyRef Name=\"Key\" />\n          </Key>\n          <Property Name=\"Key\" Type=\"integer\" Nullable=\"false\" />\n          <Property Name=\"SGL_ja\" Type=\"varchar\" MaxLength=\"255\" />\n          <Property Name=\"SGL_en\" Type=\"varchar\" MaxLength=\"255\" />\n          <Property Name=\"SGL_fr\" Type=\"varchar\" MaxLength=\"255\" />\n          <Property Name=\"SGL_de\" Type=\"varchar\" MaxLength=\"255\" />\n        </EntityType>\n        <EntityType Name=\"GCRankGridaniaMaleText\">\n          <Key>\n            <PropertyRef Name=\"Key\" />\n          </Key>\n          <Property Name=\"Key\" Type=\"integer\" Nullable=\"false\" />\n          <Property Name=\"SGL_ja\" Type=\"varchar\" MaxLength=\"255\" />\n          <Property Name=\"SGL_en\" Type=\"varchar\" MaxLength=\"255\" />\n          <Property Name=\"SGL_fr\" Type=\"varchar\" MaxLength=\"255\" />\n          <Property Name=\"SGL_de\" Type=\"varchar\" MaxLength=\"255\" />\n        </EntityType>\n        <EntityType Name=\"GCRankLimsaFemaleText\">\n          <Key>\n            <PropertyRef Name=\"Key\" />\n          </Key>\n          <Property Name=\"Key\" Type=\"integer\" Nullable=\"false\" />\n          <Property Name=\"SGL_ja\" Type=\"varchar\" MaxLength=\"255\" />\n          <Property Name=\"SGL_en\" Type=\"varchar\" MaxLength=\"255\" />\n          <Property Name=\"SGL_fr\" Type=\"varchar\" MaxLength=\"255\" />\n          <Property Name=\"SGL_de\" Type=\"varchar\" MaxLength=\"255\" />\n        </EntityType>\n        <EntityType Name=\"GCRankLimsaMaleText\">\n          <Key>\n            <PropertyRef Name=\"Key\" />\n          </Key>\n          <Property Name=\"Key\" Type=\"integer\" Nullable=\"false\" />\n          <Property Name=\"SGL_ja\" Type=\"varchar\" MaxLength=\"255\" />\n          <Property Name=\"SGL_en\" Type=\"varchar\" MaxLength=\"255\" />\n          <Property Name=\"SGL_fr\" Type=\"varchar\" MaxLength=\"255\" />\n          <Property Name=\"SGL_de\" Type=\"varchar\" MaxLength=\"255\" />\n        </EntityType>\n        <EntityType Name=\"GCRankUldahFemaleText\">\n          <Key>\n            <PropertyRef Name=\"Key\" />\n          </Key>\n          <Property Name=\"Key\" Type=\"integer\" Nullable=\"false\" />\n          <Property Name=\"SGL_ja\" Type=\"varchar\" MaxLength=\"255\" />\n          <Property Name=\"SGL_en\" Type=\"varchar\" MaxLength=\"255\" />\n          <Property Name=\"SGL_fr\" Type=\"varchar\" MaxLength=\"255\" />\n          <Property Name=\"SGL_de\" Type=\"varchar\" MaxLength=\"255\" />\n        </EntityType>\n        <EntityType Name=\"GCRankUldahMaleText\">\n          <Key>\n            <PropertyRef Name=\"Key\" />\n          </Key>\n          <Property Name=\"Key\" Type=\"integer\" Nullable=\"false\" />\n          <Property Name=\"SGL_ja\" Type=\"varchar\" MaxLength=\"255\" />\n          <Property Name=\"SGL_en\" Type=\"varchar\" MaxLength=\"255\" />\n          <Property Name=\"SGL_fr\" Type=\"varchar\" MaxLength=\"255\" />\n          <Property Name=\"SGL_de\" Type=\"varchar\" MaxLength=\"255\" />\n        </EntityType>\n        <EntityType Name=\"GeneralAction\">\n          <Key>\n            <PropertyRef Name=\"Key\" />\n          </Key>\n          <Property Name=\"Key\" Type=\"integer\" Nullable=\"false\" />\n          <Property Name=\"Icon\" Type=\"integer\" />\n          <Property Name=\"Name_ja\" Type=\"varchar\" MaxLength=\"255\" />\n          <Property Name=\"Name_en\" Type=\"varchar\" MaxLength=\"255\" />\n          <Property Name=\"Name_fr\" Type=\"varchar\" MaxLength=\"255\" />\n          <Property Name=\"Name_de\" Type=\"varchar\" MaxLength=\"255\" />\n        </EntityType>\n        <EntityType Name=\"GrandCompany\">\n          <Key>\n            <PropertyRef Name=\"Key\" />\n          </Key>\n          <Property Name=\"Key\" Type=\"integer\" Nullable=\"false\" />\n          <Property Name=\"SGL_ja\" Type=\"varchar\" MaxLength=\"255\" />\n          <Property Name=\"SGL_en\" Type=\"varchar\" MaxLength=\"255\" />\n          <Property Name=\"SGL_fr\" Type=\"varchar\" MaxLength=\"255\" />\n          <Property Name=\"SGL_de\" Type=\"varchar\" MaxLength=\"255\" />\n        </EntityType>\n        <EntityType Name=\"GuardianDeity\">\n          <Key>\n            <PropertyRef Name=\"Key\" />\n          </Key>\n          <Property Name=\"Key\" Type=\"integer\" Nullable=\"false\" />\n          <Property Name=\"Name_ja\" Type=\"varchar\" MaxLength=\"255\" />\n          <Property Name=\"Name_en\" Type=\"varchar\" MaxLength=\"255\" />\n          <Property Name=\"Name_fr\" Type=\"varchar\" MaxLength=\"255\" />\n          <Property Name=\"Name_de\" Type=\"varchar\" MaxLength=\"255\" />\n        </EntityType>\n        <EntityType Name=\"GuildOrder\">\n          <Key>\n            <PropertyRef Name=\"Key\" />\n          </Key>\n          <Property Name=\"Key\" Type=\"integer\" Nullable=\"false\" />\n          <Property Name=\"Exp00\" Type=\"integer\" />\n          <Property Name=\"Exp01\" Type=\"integer\" />\n          <Property Name=\"Gil00\" Type=\"integer\" />\n          <Property Name=\"Gil01\" Type=\"integer\" />\n        </EntityType>\n        <EntityType Name=\"InstanceContent\">\n          <Key>\n            <PropertyRef Name=\"Key\" />\n          </Key>\n          <Property Name=\"Key\" Type=\"integer\" Nullable=\"false\" />\n          <Property Name=\"Type\" Type=\"integer\" />\n          <Property Name=\"Sortkey\" Type=\"integer\" />\n          <Property Name=\"Name_ja\" Type=\"varchar\" MaxLength=\"255\" />\n          <Property Name=\"Name_en\" Type=\"varchar\" MaxLength=\"255\" />\n          <Property Name=\"Name_fr\" Type=\"varchar\" MaxLength=\"255\" />\n          <Property Name=\"Name_de\" Type=\"varchar\" MaxLength=\"255\" />\n          <Property Name=\"Description_ja\" Type=\"varchar\" MaxLength=\"255\" />\n          <Property Name=\"Description_en\" Type=\"varchar\" MaxLength=\"255\" />\n          <Property Name=\"Description_fr\" Type=\"varchar\" MaxLength=\"255\" />\n          <Property Name=\"Description_de\" Type=\"varchar\" MaxLength=\"255\" />\n          <Property Name=\"LevelMin\" Type=\"integer\" />\n          <Property Name=\"LevelMax\" Type=\"integer\" />\n          <Property Name=\"Time\" Type=\"integer\" />\n          <Property Name=\"Halfway\" Type=\"bit\" />\n          <Property Name=\"RandomContentType\" Type=\"integer\" />\n          <Property Name=\"Alliance\" Type=\"bit\" />\n          <Property Name=\"FinderPartyCondition\" Type=\"integer\" />\n          <Property Name=\"PartyMemberCount\" Type=\"integer\" />\n          <Property Name=\"TankCount\" Type=\"integer\" />\n          <Property Name=\"HealerCount\" Type=\"integer\" />\n          <Property Name=\"AttackerCount\" Type=\"integer\" />\n          <Property Name=\"RangeCount\" Type=\"integer\" />\n          <Property Name=\"DifferentiateDPS\" Type=\"bit\" />\n          <Property Name=\"PartyCount\" Type=\"integer\" />\n          <Property Name=\"FreeRole\" Type=\"bit\" />\n          <Property Name=\"ItemLevel\" Type=\"integer\" />\n          <Property Name=\"ItemLevelMax\" Type=\"integer\" />\n          <Property Name=\"Colosseum\" Type=\"integer\" />\n          <Property Name=\"Area\" Type=\"integer\" />\n          <Property Name=\"ForceCount\" Type=\"integer\" />\n          <Property Name=\"data\" Type=\"blob\" />\n          <Property Name=\"path\" Type=\"nvarchar\" MaxLength=\"2147483647\" />\n          <Property Name=\"is_koeru_usually\" Type=\"bit\" />\n          <Property Name=\"is_koeru_annihilation\" Type=\"bit\" />\n          <Property Name=\"Index_ja\" Type=\"nvarchar\" MaxLength=\"2147483647\" />\n          <Property Name=\"Index_en\" Type=\"nvarchar\" MaxLength=\"2147483647\" />\n          <Property Name=\"Index_fr\" Type=\"nvarchar\" MaxLength=\"2147483647\" />\n          <Property Name=\"Index_de\" Type=\"nvarchar\" MaxLength=\"2147483647\" />\n        </EntityType>\n        <EntityType Name=\"InstanceContentType\">\n          <Key>\n            <PropertyRef Name=\"Type\" />\n          </Key>\n          <Property Name=\"Type\" Type=\"integer\" Nullable=\"false\" />\n          <Property Name=\"Sortkey\" Type=\"integer\" />\n          <Property Name=\"ContentType\" Type=\"integer\" />\n        </EntityType>\n        <EntityType Name=\"Item\">\n          <Key>\n            <PropertyRef Name=\"Key\" />\n          </Key>\n          <Property Name=\"Key\" Type=\"integer\" Nullable=\"false\" />\n          <Property Name=\"Category\" Type=\"integer\" />\n          <Property Name=\"UICategory\" Type=\"integer\" />\n          <Property Name=\"UIName_ja\" Type=\"varchar\" MaxLength=\"255\" />\n          <Property Name=\"UIName_en\" Type=\"varchar\" MaxLength=\"255\" />\n          <Property Name=\"UIName_fr\" Type=\"varchar\" MaxLength=\"255\" />\n          <Property Name=\"UIName_de\" Type=\"varchar\" MaxLength=\"255\" />\n          <Property Name=\"Help_ja\" Type=\"varchar\" MaxLength=\"255\" />\n          <Property Name=\"Help_en\" Type=\"varchar\" MaxLength=\"255\" />\n          <Property Name=\"Help_fr\" Type=\"varchar\" MaxLength=\"255\" />\n          <Property Name=\"Help_de\" Type=\"varchar\" MaxLength=\"255\" />\n          <Property Name=\"Level\" Type=\"integer\" />\n          <Property Name=\"EquipLevel\" Type=\"integer\" />\n          <Property Name=\"Rarity\" Type=\"integer\" />\n          <Property Name=\"HQ\" Type=\"bit\" />\n          <Property Name=\"SpecialBonus\" Type=\"integer\" />\n          <Property Name=\"Series\" Type=\"integer\" />\n          <Property Name=\"Slot\" Type=\"integer\" />\n          <Property Name=\"Damage\" Type=\"integer\" />\n          <Property Name=\"Damage_hq\" Type=\"integer\" />\n          <Property Name=\"MagicDamage\" Type=\"integer\" />\n          <Property Name=\"MagicDamage_hq\" Type=\"integer\" />\n          <Property Name=\"Defense\" Type=\"integer\" />\n          <Property Name=\"Defense_hq\" Type=\"integer\" />\n          <Property Name=\"MagicDefense\" Type=\"integer\" />\n          <Property Name=\"MagicDefense_hq\" Type=\"integer\" />\n          <Property Name=\"ShieldRate\" Type=\"integer\" />\n          <Property Name=\"ShieldRate_hq\" Type=\"integer\" />\n          <Property Name=\"ShieldBlockRate\" Type=\"integer\" />\n          <Property Name=\"ShieldBlockRate_hq\" Type=\"integer\" />\n          <Property Name=\"AttackInterval\" Type=\"integer\" />\n          <Property Name=\"AttackInterval_hq\" Type=\"integer\" />\n          <Property Name=\"AutoAttack\" Type=\"integer\" />\n          <Property Name=\"AutoAttack_hq\" Type=\"integer\" />\n          <Property Name=\"Price\" Type=\"integer\" />\n          <Property Name=\"PriceMin\" Type=\"integer\" />\n          <Property Name=\"MirageItem\" Type=\"integer\" />\n          <Property Name=\"icon\" Type=\"varchar\" MaxLength=\"255\" />\n          <Property Name=\"icon_hq\" Type=\"varchar\" MaxLength=\"255\" />\n          <Property Name=\"classjob\" Type=\"varchar\" MaxLength=\"255\" />\n          <Property Name=\"Salvage\" Type=\"integer\" />\n          <Property Name=\"data\" Type=\"blob\" />\n          <Property Name=\"legacy\" Type=\"bit\" />\n          <Property Name=\"path\" Type=\"nvarchar\" MaxLength=\"2147483647\" />\n          <Property Name=\"Index_ja\" Type=\"nvarchar\" MaxLength=\"2147483647\" />\n          <Property Name=\"Index_en\" Type=\"nvarchar\" MaxLength=\"2147483647\" />\n          <Property Name=\"Index_fr\" Type=\"nvarchar\" MaxLength=\"2147483647\" />\n          <Property Name=\"Index_de\" Type=\"nvarchar\" MaxLength=\"2147483647\" />\n          <Property Name=\"SortId\" Type=\"integer\" />\n        </EntityType>\n        <EntityType Name=\"Item_ClassJob\">\n          <Key>\n            <PropertyRef Name=\"Item_Key\" />\n            <PropertyRef Name=\"ClassJob_Key\" />\n          </Key>\n          <Property Name=\"Item_Key\" Type=\"integer\" Nullable=\"false\" />\n          <Property Name=\"ClassJob_Key\" Type=\"integer\" Nullable=\"false\" />\n        </EntityType>\n        <EntityType Name=\"ItemCategory\">\n          <Key>\n            <PropertyRef Name=\"Key\" />\n          </Key>\n          <Property Name=\"Key\" Type=\"integer\" Nullable=\"false\" />\n          <Property Name=\"Name_ja\" Type=\"varchar\" MaxLength=\"255\" />\n          <Property Name=\"Name_en\" Type=\"varchar\" MaxLength=\"255\" />\n          <Property Name=\"Name_fr\" Type=\"varchar\" MaxLength=\"255\" />\n          <Property Name=\"Name_de\" Type=\"varchar\" MaxLength=\"255\" />\n        </EntityType>\n        <EntityType Name=\"ItemSeries\">\n          <Key>\n            <PropertyRef Name=\"Key\" />\n          </Key>\n          <Property Name=\"Key\" Type=\"integer\" Nullable=\"false\" />\n          <Property Name=\"Name_ja\" Type=\"varchar\" MaxLength=\"255\" />\n          <Property Name=\"Name_en\" Type=\"varchar\" MaxLength=\"255\" />\n          <Property Name=\"Name_fr\" Type=\"varchar\" MaxLength=\"255\" />\n          <Property Name=\"Name_de\" Type=\"varchar\" MaxLength=\"255\" />\n        </EntityType>\n        <EntityType Name=\"ItemSpecialBonus\">\n          <Key>\n            <PropertyRef Name=\"Key\" />\n          </Key>\n          <Property Name=\"Key\" Type=\"integer\" Nullable=\"false\" />\n          <Property Name=\"Name_ja\" Type=\"varchar\" MaxLength=\"255\" />\n          <Property Name=\"Name_en\" Type=\"varchar\" MaxLength=\"255\" />\n          <Property Name=\"Name_fr\" Type=\"varchar\" MaxLength=\"255\" />\n          <Property Name=\"Name_de\" Type=\"varchar\" MaxLength=\"255\" />\n        </EntityType>\n        <EntityType Name=\"ItemUICategory\">\n          <Key>\n            <PropertyRef Name=\"Key\" />\n          </Key>\n          <Property Name=\"Key\" Type=\"integer\" Nullable=\"false\" />\n          <Property Name=\"Kind\" Type=\"integer\" />\n          <Property Name=\"Name_ja\" Type=\"varchar\" MaxLength=\"255\" />\n          <Property Name=\"Name_en\" Type=\"varchar\" MaxLength=\"255\" />\n          <Property Name=\"Name_fr\" Type=\"varchar\" MaxLength=\"255\" />\n          <Property Name=\"Name_de\" Type=\"varchar\" MaxLength=\"255\" />\n          <Property Name=\"Priority\" Type=\"integer\" />\n        </EntityType>\n        <EntityType Name=\"ItemUIKind\">\n          <Key>\n            <PropertyRef Name=\"Key\" />\n          </Key>\n          <Property Name=\"Key\" Type=\"integer\" Nullable=\"false\" />\n          <Property Name=\"Name_ja\" Type=\"varchar\" MaxLength=\"255\" />\n          <Property Name=\"Name_en\" Type=\"varchar\" MaxLength=\"255\" />\n          <Property Name=\"Name_fr\" Type=\"varchar\" MaxLength=\"255\" />\n          <Property Name=\"Name_de\" Type=\"varchar\" MaxLength=\"255\" />\n        </EntityType>\n        <EntityType Name=\"JournalCategory\">\n          <Key>\n            <PropertyRef Name=\"Key\" />\n          </Key>\n          <Property Name=\"Key\" Type=\"integer\" Nullable=\"false\" />\n          <Property Name=\"label\" Type=\"varchar\" MaxLength=\"255\" />\n          <Property Name=\"Name_ja\" Type=\"varchar\" MaxLength=\"255\" />\n          <Property Name=\"Name_en\" Type=\"varchar\" MaxLength=\"255\" />\n          <Property Name=\"Name_fr\" Type=\"varchar\" MaxLength=\"255\" />\n          <Property Name=\"Name_de\" Type=\"varchar\" MaxLength=\"255\" />\n        </EntityType>\n        <EntityType Name=\"JournalGenre\">\n          <Key>\n            <PropertyRef Name=\"Key\" />\n          </Key>\n          <Property Name=\"Key\" Type=\"integer\" Nullable=\"false\" />\n          <Property Name=\"Category\" Type=\"integer\" />\n          <Property Name=\"label\" Type=\"varchar\" MaxLength=\"255\" />\n          <Property Name=\"Name_ja\" Type=\"varchar\" MaxLength=\"255\" />\n          <Property Name=\"Name_en\" Type=\"varchar\" MaxLength=\"255\" />\n          <Property Name=\"Name_fr\" Type=\"varchar\" MaxLength=\"255\" />\n          <Property Name=\"Name_de\" Type=\"varchar\" MaxLength=\"255\" />\n        </EntityType>\n        <EntityType Name=\"LodestoneSystemDefine\">\n          <Key>\n            <PropertyRef Name=\"Key\" />\n          </Key>\n          <Property Name=\"Key\" Type=\"integer\" Nullable=\"false\" />\n          <Property Name=\"DefineName\" Type=\"varchar\" MaxLength=\"255\" />\n          <Property Name=\"DefineValue\" Type=\"varchar\" MaxLength=\"255\" />\n        </EntityType>\n        <EntityType Name=\"NotebookDivision\">\n          <Key>\n            <PropertyRef Name=\"Key\" />\n          </Key>\n          <Property Name=\"Key\" Type=\"integer\" Nullable=\"false\" />\n          <Property Name=\"Name_ja\" Type=\"varchar\" MaxLength=\"255\" />\n          <Property Name=\"Name_en\" Type=\"varchar\" MaxLength=\"255\" />\n          <Property Name=\"Name_fr\" Type=\"varchar\" MaxLength=\"255\" />\n          <Property Name=\"Name_de\" Type=\"varchar\" MaxLength=\"255\" />\n        </EntityType>\n        <EntityType Name=\"PlaceName\">\n          <Key>\n            <PropertyRef Name=\"Key\" />\n          </Key>\n          <Property Name=\"Key\" Type=\"integer\" Nullable=\"false\" />\n          <Property Name=\"region\" Type=\"integer\" />\n          <Property Name=\"SGL_ja\" Type=\"varchar\" MaxLength=\"255\" />\n          <Property Name=\"SGL_en\" Type=\"varchar\" MaxLength=\"255\" />\n          <Property Name=\"SGL_fr\" Type=\"varchar\" MaxLength=\"255\" />\n          <Property Name=\"SGL_de\" Type=\"varchar\" MaxLength=\"255\" />\n        </EntityType>\n        <EntityType Name=\"Quest\">\n          <Key>\n            <PropertyRef Name=\"Key\" />\n          </Key>\n          <Property Name=\"Key\" Type=\"integer\" Nullable=\"false\" />\n          <Property Name=\"Genre\" Type=\"integer\" />\n          <Property Name=\"Area\" Type=\"integer\" />\n          <Property Name=\"Name_ja\" Type=\"varchar\" MaxLength=\"255\" />\n          <Property Name=\"Name_en\" Type=\"varchar\" MaxLength=\"255\" />\n          <Property Name=\"Name_fr\" Type=\"varchar\" MaxLength=\"255\" />\n          <Property Name=\"Name_de\" Type=\"varchar\" MaxLength=\"255\" />\n          <Property Name=\"CompanyPointType\" Type=\"integer\" />\n          <Property Name=\"CompanyPointNum\" Type=\"integer\" />\n          <Property Name=\"Gil\" Type=\"integer\" />\n          <Property Name=\"Client\" Type=\"integer\" />\n          <Property Name=\"ExpBonus\" Type=\"integer\" />\n          <Property Name=\"Header\" Type=\"integer\" />\n          <Property Name=\"ClassLevel\" Type=\"integer\" />\n          <Property Name=\"ClassLevel2\" Type=\"integer\" />\n          <Property Name=\"ClassJob\" Type=\"integer\" />\n          <Property Name=\"ClassJob2\" Type=\"integer\" />\n          <Property Name=\"QuestLevelOffset\" Type=\"integer\" />\n          <Property Name=\"Sort\" Type=\"integer\" />\n          <Property Name=\"BeastTribe\" Type=\"integer\" />\n          <Property Name=\"BeastReputationValueNum\" Type=\"integer\" />\n          <Property Name=\"WebType\" Type=\"integer\" />\n          <Property Name=\"data\" Type=\"blob\" />\n          <Property Name=\"path\" Type=\"nvarchar\" MaxLength=\"2147483647\" />\n          <Property Name=\"Index_ja\" Type=\"nvarchar\" MaxLength=\"2147483647\" />\n          <Property Name=\"Index_en\" Type=\"nvarchar\" MaxLength=\"2147483647\" />\n          <Property Name=\"Index_fr\" Type=\"nvarchar\" MaxLength=\"2147483647\" />\n          <Property Name=\"Index_de\" Type=\"nvarchar\" MaxLength=\"2147483647\" />\n        </EntityType>\n        <EntityType Name=\"Quest_ClassJob\">\n          <Key>\n            <PropertyRef Name=\"Quest_Key\" />\n            <PropertyRef Name=\"ClassJob_Key\" />\n          </Key>\n          <Property Name=\"Quest_Key\" Type=\"integer\" Nullable=\"false\" />\n          <Property Name=\"ClassJob_Key\" Type=\"integer\" Nullable=\"false\" />\n        </EntityType>\n        <EntityType Name=\"QuestWebEx\">\n          <Key>\n            <PropertyRef Name=\"Key\" />\n          </Key>\n          <Property Name=\"Key\" Type=\"integer\" Nullable=\"false\" />\n          <Property Name=\"Name_ja\" Type=\"varchar\" MaxLength=\"255\" />\n          <Property Name=\"Name_en\" Type=\"varchar\" MaxLength=\"255\" />\n          <Property Name=\"Name_fr\" Type=\"varchar\" MaxLength=\"255\" />\n          <Property Name=\"Name_de\" Type=\"varchar\" MaxLength=\"255\" />\n        </EntityType>\n        <EntityType Name=\"QuestWebType\">\n          <Key>\n            <PropertyRef Name=\"Key\" />\n          </Key>\n          <Property Name=\"Key\" Type=\"integer\" Nullable=\"false\" />\n          <Property Name=\"Text_de\" Type=\"varchar\" MaxLength=\"255\" />\n          <Property Name=\"Text_en\" Type=\"varchar\" MaxLength=\"255\" />\n          <Property Name=\"Text_ja\" Type=\"varchar\" MaxLength=\"255\" />\n          <Property Name=\"Text_fr\" Type=\"varchar\" MaxLength=\"255\" />\n        </EntityType>\n        <EntityType Name=\"Race\">\n          <Key>\n            <PropertyRef Name=\"Key\" />\n          </Key>\n          <Property Name=\"Key\" Type=\"integer\" Nullable=\"false\" />\n          <Property Name=\"NameFemale_ja\" Type=\"varchar\" MaxLength=\"255\" />\n          <Property Name=\"NameFemale_en\" Type=\"varchar\" MaxLength=\"255\" />\n          <Property Name=\"NameFemale_fr\" Type=\"varchar\" MaxLength=\"255\" />\n          <Property Name=\"NameFemale_de\" Type=\"varchar\" MaxLength=\"255\" />\n          <Property Name=\"Name_ja\" Type=\"varchar\" MaxLength=\"255\" />\n          <Property Name=\"Name_en\" Type=\"varchar\" MaxLength=\"255\" />\n          <Property Name=\"Name_fr\" Type=\"varchar\" MaxLength=\"255\" />\n          <Property Name=\"Name_de\" Type=\"varchar\" MaxLength=\"255\" />\n        </EntityType>\n        <EntityType Name=\"Recipe\">\n          <Key>\n            <PropertyRef Name=\"Key\" />\n          </Key>\n          <Property Name=\"Key\" Type=\"integer\" Nullable=\"false\" />\n          <Property Name=\"CanAutoCraft\" Type=\"integer\" />\n          <Property Name=\"CanHq\" Type=\"integer\" />\n          <Property Name=\"CraftItemId\" Type=\"integer\" />\n          <Property Name=\"CraftNum\" Type=\"integer\" />\n          <Property Name=\"CraftType\" Type=\"integer\" />\n          <Property Name=\"Level\" Type=\"integer\" />\n          <Property Name=\"levelView\" Type=\"integer\" />\n          <Property Name=\"levelDiff\" Type=\"integer\" />\n          <Property Name=\"Element\" Type=\"integer\" />\n          <Property Name=\"NeedCraftmanship\" Type=\"integer\" />\n          <Property Name=\"NeedControl\" Type=\"integer\" />\n          <Property Name=\"Number\" Type=\"integer\" />\n          <Property Name=\"NeedStatus\" Type=\"integer\" />\n          <Property Name=\"NeedEquipItem\" Type=\"integer\" />\n          <Property Name=\"data\" Type=\"blob\" />\n          <Property Name=\"path\" Type=\"nvarchar\" MaxLength=\"2147483647\" />\n          <Property Name=\"Index_ja\" Type=\"nvarchar\" MaxLength=\"2147483647\" />\n          <Property Name=\"Index_en\" Type=\"nvarchar\" MaxLength=\"2147483647\" />\n          <Property Name=\"Index_fr\" Type=\"nvarchar\" MaxLength=\"2147483647\" />\n          <Property Name=\"Index_de\" Type=\"nvarchar\" MaxLength=\"2147483647\" />\n        </EntityType>\n        <EntityType Name=\"RecipeElement\">\n          <Key>\n            <PropertyRef Name=\"Key\" />\n          </Key>\n          <Property Name=\"Key\" Type=\"integer\" Nullable=\"false\" />\n          <Property Name=\"Name_ja\" Type=\"varchar\" MaxLength=\"255\" />\n          <Property Name=\"Name_en\" Type=\"varchar\" MaxLength=\"255\" />\n          <Property Name=\"Name_fr\" Type=\"varchar\" MaxLength=\"255\" />\n          <Property Name=\"Name_de\" Type=\"varchar\" MaxLength=\"255\" />\n        </EntityType>\n        <EntityType Name=\"Shop\">\n          <Key>\n            <PropertyRef Name=\"Key\" />\n          </Key>\n          <Property Name=\"Key\" Type=\"integer\" Nullable=\"false\" />\n          <Property Name=\"Name_ja\" Type=\"varchar\" MaxLength=\"255\" />\n          <Property Name=\"Name_en\" Type=\"varchar\" MaxLength=\"255\" />\n          <Property Name=\"Name_fr\" Type=\"varchar\" MaxLength=\"255\" />\n          <Property Name=\"Name_de\" Type=\"varchar\" MaxLength=\"255\" />\n          <Property Name=\"BeastTribe\" Type=\"integer\" />\n        </EntityType>\n        <EntityType Name=\"Status\">\n          <Key>\n            <PropertyRef Name=\"Key\" />\n          </Key>\n          <Property Name=\"Key\" Type=\"integer\" Nullable=\"false\" />\n          <Property Name=\"Name_ja\" Type=\"varchar\" MaxLength=\"255\" />\n          <Property Name=\"Name_en\" Type=\"varchar\" MaxLength=\"255\" />\n          <Property Name=\"Name_fr\" Type=\"varchar\" MaxLength=\"255\" />\n          <Property Name=\"Name_de\" Type=\"varchar\" MaxLength=\"255\" />\n        </EntityType>\n        <EntityType Name=\"Title\">\n          <Key>\n            <PropertyRef Name=\"Key\" />\n          </Key>\n          <Property Name=\"Key\" Type=\"integer\" Nullable=\"false\" />\n          <Property Name=\"Female_de\" Type=\"varchar\" MaxLength=\"255\" />\n          <Property Name=\"Female_en\" Type=\"varchar\" MaxLength=\"255\" />\n          <Property Name=\"Female_fr\" Type=\"varchar\" MaxLength=\"255\" />\n          <Property Name=\"Female_ja\" Type=\"varchar\" MaxLength=\"255\" />\n          <Property Name=\"Front_de\" Type=\"bit\" />\n          <Property Name=\"Front_en\" Type=\"bit\" />\n          <Property Name=\"Front_fr\" Type=\"bit\" />\n          <Property Name=\"Front_ja\" Type=\"bit\" />\n          <Property Name=\"Male_de\" Type=\"varchar\" MaxLength=\"255\" />\n          <Property Name=\"Male_en\" Type=\"varchar\" MaxLength=\"255\" />\n          <Property Name=\"Male_fr\" Type=\"varchar\" MaxLength=\"255\" />\n          <Property Name=\"Male_ja\" Type=\"varchar\" MaxLength=\"255\" />\n        </EntityType>\n        <EntityType Name=\"Tomestones\">\n          <Key>\n            <PropertyRef Name=\"Key\" />\n          </Key>\n          <Property Name=\"Key\" Type=\"integer\" Nullable=\"false\" />\n          <Property Name=\"CatalogId\" Type=\"integer\" />\n          <Property Name=\"path\" Type=\"nvarchar\" MaxLength=\"2147483647\" />\n        </EntityType>\n        <EntityType Name=\"Town\">\n          <Key>\n            <PropertyRef Name=\"Key\" />\n          </Key>\n          <Property Name=\"Key\" Type=\"integer\" Nullable=\"false\" />\n          <Property Name=\"Name_ja\" Type=\"varchar\" MaxLength=\"255\" />\n          <Property Name=\"Name_en\" Type=\"varchar\" MaxLength=\"255\" />\n          <Property Name=\"Name_fr\" Type=\"varchar\" MaxLength=\"255\" />\n          <Property Name=\"Name_de\" Type=\"varchar\" MaxLength=\"255\" />\n        </EntityType>\n        <EntityContainer Name=\"LibraModelStoreContainer\">\n          <EntitySet Name=\"Achievement\" EntityType=\"Self.Achievement\" store:Type=\"Tables\" />\n          <EntitySet Name=\"AchievementCategory\" EntityType=\"Self.AchievementCategory\" store:Type=\"Tables\" />\n          <EntitySet Name=\"AchievementKind\" EntityType=\"Self.AchievementKind\" store:Type=\"Tables\" />\n          <EntitySet Name=\"Action\" EntityType=\"Self.Action\" store:Type=\"Tables\" />\n          <EntitySet Name=\"BaseParam\" EntityType=\"Self.BaseParam\" store:Type=\"Tables\" />\n          <EntitySet Name=\"BeastTribe\" EntityType=\"Self.BeastTribe\" store:Type=\"Tables\" />\n          <EntitySet Name=\"BNpcName\" EntityType=\"Self.BNpcName\" store:Type=\"Tables\" />\n          <EntitySet Name=\"BNpcName_PlaceName\" EntityType=\"Self.BNpcName_PlaceName\" store:Type=\"Tables\" />\n          <EntitySet Name=\"ClassJob\" EntityType=\"Self.ClassJob\" store:Type=\"Tables\" />\n          <EntitySet Name=\"ClassJob_ClassJobCategory\" EntityType=\"Self.ClassJob_ClassJobCategory\" store:Type=\"Tables\" />\n          <EntitySet Name=\"ClassJobCategory\" EntityType=\"Self.ClassJobCategory\" store:Type=\"Tables\" />\n          <EntitySet Name=\"Colosseum\" EntityType=\"Self.Colosseum\" store:Type=\"Tables\" />\n          <EntitySet Name=\"ContentRoulette\" EntityType=\"Self.ContentRoulette\" store:Type=\"Tables\" />\n          <EntitySet Name=\"ContentType\" EntityType=\"Self.ContentType\" store:Type=\"Tables\" />\n          <EntitySet Name=\"CraftType\" EntityType=\"Self.CraftType\" store:Type=\"Tables\" />\n          <EntitySet Name=\"Emote\" EntityType=\"Self.Emote\" store:Type=\"Tables\" />\n          <EntitySet Name=\"ENpcResident\" EntityType=\"Self.ENpcResident\" store:Type=\"Tables\" />\n          <EntitySet Name=\"ENpcResident_PlaceName\" EntityType=\"Self.ENpcResident_PlaceName\" store:Type=\"Tables\" />\n          <EntitySet Name=\"ENpcResident_Quest\" EntityType=\"Self.ENpcResident_Quest\" store:Type=\"Tables\" />\n          <EntitySet Name=\"FCHierarchy\" EntityType=\"Self.FCHierarchy\" store:Type=\"Tables\" />\n          <EntitySet Name=\"FCRank\" EntityType=\"Self.FCRank\" store:Type=\"Tables\" />\n          <EntitySet Name=\"FCReputation\" EntityType=\"Self.FCReputation\" store:Type=\"Tables\" />\n          <EntitySet Name=\"Frontline01\" EntityType=\"Self.Frontline01\" store:Type=\"Tables\" />\n          <EntitySet Name=\"Gathering\" EntityType=\"Self.Gathering\" store:Type=\"Tables\" />\n          <EntitySet Name=\"GatheringType\" EntityType=\"Self.GatheringType\" store:Type=\"Tables\" />\n          <EntitySet Name=\"GCRankGridaniaFemaleText\" EntityType=\"Self.GCRankGridaniaFemaleText\" store:Type=\"Tables\" />\n          <EntitySet Name=\"GCRankGridaniaMaleText\" EntityType=\"Self.GCRankGridaniaMaleText\" store:Type=\"Tables\" />\n          <EntitySet Name=\"GCRankLimsaFemaleText\" EntityType=\"Self.GCRankLimsaFemaleText\" store:Type=\"Tables\" />\n          <EntitySet Name=\"GCRankLimsaMaleText\" EntityType=\"Self.GCRankLimsaMaleText\" store:Type=\"Tables\" />\n          <EntitySet Name=\"GCRankUldahFemaleText\" EntityType=\"Self.GCRankUldahFemaleText\" store:Type=\"Tables\" />\n          <EntitySet Name=\"GCRankUldahMaleText\" EntityType=\"Self.GCRankUldahMaleText\" store:Type=\"Tables\" />\n          <EntitySet Name=\"GeneralAction\" EntityType=\"Self.GeneralAction\" store:Type=\"Tables\" />\n          <EntitySet Name=\"GrandCompany\" EntityType=\"Self.GrandCompany\" store:Type=\"Tables\" />\n          <EntitySet Name=\"GuardianDeity\" EntityType=\"Self.GuardianDeity\" store:Type=\"Tables\" />\n          <EntitySet Name=\"GuildOrder\" EntityType=\"Self.GuildOrder\" store:Type=\"Tables\" />\n          <EntitySet Name=\"InstanceContent\" EntityType=\"Self.InstanceContent\" store:Type=\"Tables\" />\n          <EntitySet Name=\"InstanceContentType\" EntityType=\"Self.InstanceContentType\" store:Type=\"Tables\" />\n          <EntitySet Name=\"Item\" EntityType=\"Self.Item\" store:Type=\"Tables\" />\n          <EntitySet Name=\"Item_ClassJob\" EntityType=\"Self.Item_ClassJob\" store:Type=\"Tables\" />\n          <EntitySet Name=\"ItemCategory\" EntityType=\"Self.ItemCategory\" store:Type=\"Tables\" />\n          <EntitySet Name=\"ItemSeries\" EntityType=\"Self.ItemSeries\" store:Type=\"Tables\" />\n          <EntitySet Name=\"ItemSpecialBonus\" EntityType=\"Self.ItemSpecialBonus\" store:Type=\"Tables\" />\n          <EntitySet Name=\"ItemUICategory\" EntityType=\"Self.ItemUICategory\" store:Type=\"Tables\" />\n          <EntitySet Name=\"ItemUIKind\" EntityType=\"Self.ItemUIKind\" store:Type=\"Tables\" />\n          <EntitySet Name=\"JournalCategory\" EntityType=\"Self.JournalCategory\" store:Type=\"Tables\" />\n          <EntitySet Name=\"JournalGenre\" EntityType=\"Self.JournalGenre\" store:Type=\"Tables\" />\n          <EntitySet Name=\"LodestoneSystemDefine\" EntityType=\"Self.LodestoneSystemDefine\" store:Type=\"Tables\" />\n          <EntitySet Name=\"NotebookDivision\" EntityType=\"Self.NotebookDivision\" store:Type=\"Tables\" />\n          <EntitySet Name=\"PlaceName\" EntityType=\"Self.PlaceName\" store:Type=\"Tables\" />\n          <EntitySet Name=\"Quest\" EntityType=\"Self.Quest\" store:Type=\"Tables\" />\n          <EntitySet Name=\"Quest_ClassJob\" EntityType=\"Self.Quest_ClassJob\" store:Type=\"Tables\" />\n          <EntitySet Name=\"QuestWebEx\" EntityType=\"Self.QuestWebEx\" store:Type=\"Tables\" />\n          <EntitySet Name=\"QuestWebType\" EntityType=\"Self.QuestWebType\" store:Type=\"Tables\" />\n          <EntitySet Name=\"Race\" EntityType=\"Self.Race\" store:Type=\"Tables\" />\n          <EntitySet Name=\"Recipe\" EntityType=\"Self.Recipe\" store:Type=\"Tables\" />\n          <EntitySet Name=\"RecipeElement\" EntityType=\"Self.RecipeElement\" store:Type=\"Tables\" />\n          <EntitySet Name=\"Shop\" EntityType=\"Self.Shop\" store:Type=\"Tables\" />\n          <EntitySet Name=\"Status\" EntityType=\"Self.Status\" store:Type=\"Tables\" />\n          <EntitySet Name=\"Title\" EntityType=\"Self.Title\" store:Type=\"Tables\" />\n          <EntitySet Name=\"Tomestones\" EntityType=\"Self.Tomestones\" store:Type=\"Tables\" />\n          <EntitySet Name=\"Town\" EntityType=\"Self.Town\" store:Type=\"Tables\" />\n        </EntityContainer>\n      </Schema>\n    </edmx:StorageModels>\n    <!-- CSDL content -->\n    <edmx:ConceptualModels>\n      <Schema Namespace=\"LibraModel\" Alias=\"Self\" annotation:UseStrongSpatialTypes=\"false\" xmlns:annotation=\"http://schemas.microsoft.com/ado/2009/02/edm/annotation\" xmlns:customannotation=\"http://schemas.microsoft.com/ado/2013/11/edm/customannotation\" xmlns=\"http://schemas.microsoft.com/ado/2009/11/edm\">\n        <EntityType Name=\"Achievement\">\n          <Key>\n            <PropertyRef Name=\"Key\" />\n          </Key>\n          <Property Name=\"Key\" Type=\"Int64\" Nullable=\"false\" />\n          <Property Name=\"Category\" Type=\"Int64\" />\n          <Property Name=\"Name_ja\" Type=\"String\" MaxLength=\"255\" FixedLength=\"false\" Unicode=\"false\" />\n          <Property Name=\"Name_en\" Type=\"String\" MaxLength=\"255\" FixedLength=\"false\" Unicode=\"false\" />\n          <Property Name=\"Name_fr\" Type=\"String\" MaxLength=\"255\" FixedLength=\"false\" Unicode=\"false\" />\n          <Property Name=\"Name_de\" Type=\"String\" MaxLength=\"255\" FixedLength=\"false\" Unicode=\"false\" />\n          <Property Name=\"Help_ja\" Type=\"String\" MaxLength=\"255\" FixedLength=\"false\" Unicode=\"false\" />\n          <Property Name=\"Help_en\" Type=\"String\" MaxLength=\"255\" FixedLength=\"false\" Unicode=\"false\" />\n          <Property Name=\"Help_fr\" Type=\"String\" MaxLength=\"255\" FixedLength=\"false\" Unicode=\"false\" />\n          <Property Name=\"Help_de\" Type=\"String\" MaxLength=\"255\" FixedLength=\"false\" Unicode=\"false\" />\n          <Property Name=\"Point\" Type=\"Int64\" />\n          <Property Name=\"Item\" Type=\"Int64\" />\n          <Property Name=\"Icon\" Type=\"Int64\" />\n          <Property Name=\"Title\" Type=\"Int64\" />\n          <Property Name=\"Priority\" Type=\"Int64\" />\n          <Property Name=\"data\" Type=\"Binary\" MaxLength=\"2147483647\" FixedLength=\"false\" />\n          <Property Name=\"path\" Type=\"String\" MaxLength=\"2147483647\" FixedLength=\"false\" Unicode=\"true\" />\n          <Property Name=\"Index_ja\" Type=\"String\" MaxLength=\"2147483647\" FixedLength=\"false\" Unicode=\"true\" />\n          <Property Name=\"Index_en\" Type=\"String\" MaxLength=\"2147483647\" FixedLength=\"false\" Unicode=\"true\" />\n          <Property Name=\"Index_fr\" Type=\"String\" MaxLength=\"2147483647\" FixedLength=\"false\" Unicode=\"true\" />\n          <Property Name=\"Index_de\" Type=\"String\" MaxLength=\"2147483647\" FixedLength=\"false\" Unicode=\"true\" />\n        </EntityType>\n        <EntityType Name=\"AchievementCategory\">\n          <Key>\n            <PropertyRef Name=\"Key\" />\n          </Key>\n          <Property Name=\"Key\" Type=\"Int64\" Nullable=\"false\" />\n          <Property Name=\"Kind\" Type=\"Int64\" />\n          <Property Name=\"Name_ja\" Type=\"String\" MaxLength=\"255\" FixedLength=\"false\" Unicode=\"false\" />\n          <Property Name=\"Name_en\" Type=\"String\" MaxLength=\"255\" FixedLength=\"false\" Unicode=\"false\" />\n          <Property Name=\"Name_fr\" Type=\"String\" MaxLength=\"255\" FixedLength=\"false\" Unicode=\"false\" />\n          <Property Name=\"Name_de\" Type=\"String\" MaxLength=\"255\" FixedLength=\"false\" Unicode=\"false\" />\n        </EntityType>\n        <EntityType Name=\"AchievementKind\">\n          <Key>\n            <PropertyRef Name=\"Key\" />\n          </Key>\n          <Property Name=\"Key\" Type=\"Int64\" Nullable=\"false\" />\n          <Property Name=\"Name_ja\" Type=\"String\" MaxLength=\"255\" FixedLength=\"false\" Unicode=\"false\" />\n          <Property Name=\"Name_en\" Type=\"String\" MaxLength=\"255\" FixedLength=\"false\" Unicode=\"false\" />\n          <Property Name=\"Name_fr\" Type=\"String\" MaxLength=\"255\" FixedLength=\"false\" Unicode=\"false\" />\n          <Property Name=\"Name_de\" Type=\"String\" MaxLength=\"255\" FixedLength=\"false\" Unicode=\"false\" />\n        </EntityType>\n        <EntityType Name=\"Action\">\n          <Key>\n            <PropertyRef Name=\"Key\" />\n          </Key>\n          <Property Name=\"Key\" Type=\"Int64\" Nullable=\"false\" />\n          <Property Name=\"Icon\" Type=\"Int64\" />\n          <Property Name=\"Name_ja\" Type=\"String\" MaxLength=\"255\" FixedLength=\"false\" Unicode=\"false\" />\n          <Property Name=\"Name_en\" Type=\"String\" MaxLength=\"255\" FixedLength=\"false\" Unicode=\"false\" />\n          <Property Name=\"Name_fr\" Type=\"String\" MaxLength=\"255\" FixedLength=\"false\" Unicode=\"false\" />\n          <Property Name=\"Name_de\" Type=\"String\" MaxLength=\"255\" FixedLength=\"false\" Unicode=\"false\" />\n          <Property Name=\"HelpWeb_ja\" Type=\"String\" MaxLength=\"255\" FixedLength=\"false\" Unicode=\"false\" />\n          <Property Name=\"HelpWeb_en\" Type=\"String\" MaxLength=\"255\" FixedLength=\"false\" Unicode=\"false\" />\n          <Property Name=\"HelpWeb_fr\" Type=\"String\" MaxLength=\"255\" FixedLength=\"false\" Unicode=\"false\" />\n          <Property Name=\"HelpWeb_de\" Type=\"String\" MaxLength=\"255\" FixedLength=\"false\" Unicode=\"false\" />\n        </EntityType>\n        <EntityType Name=\"BaseParam\">\n          <Key>\n            <PropertyRef Name=\"Key\" />\n          </Key>\n          <Property Name=\"Key\" Type=\"Int64\" Nullable=\"false\" />\n          <Property Name=\"Name_ja\" Type=\"String\" MaxLength=\"255\" FixedLength=\"false\" Unicode=\"false\" />\n          <Property Name=\"Name_en\" Type=\"String\" MaxLength=\"255\" FixedLength=\"false\" Unicode=\"false\" />\n          <Property Name=\"Name_fr\" Type=\"String\" MaxLength=\"255\" FixedLength=\"false\" Unicode=\"false\" />\n          <Property Name=\"Name_de\" Type=\"String\" MaxLength=\"255\" FixedLength=\"false\" Unicode=\"false\" />\n        </EntityType>\n        <EntityType Name=\"BeastTribe\">\n          <Key>\n            <PropertyRef Name=\"Key\" />\n          </Key>\n          <Property Name=\"Key\" Type=\"Int64\" Nullable=\"false\" />\n          <Property Name=\"SGL_ja\" Type=\"String\" MaxLength=\"255\" FixedLength=\"false\" Unicode=\"false\" />\n          <Property Name=\"SGL_en\" Type=\"String\" MaxLength=\"255\" FixedLength=\"false\" Unicode=\"false\" />\n          <Property Name=\"SGL_fr\" Type=\"String\" MaxLength=\"255\" FixedLength=\"false\" Unicode=\"false\" />\n          <Property Name=\"SGL_de\" Type=\"String\" MaxLength=\"255\" FixedLength=\"false\" Unicode=\"false\" />\n          <Property Name=\"ReputationValue_ja\" Type=\"String\" MaxLength=\"255\" FixedLength=\"false\" Unicode=\"false\" />\n          <Property Name=\"ReputationValue_en\" Type=\"String\" MaxLength=\"255\" FixedLength=\"false\" Unicode=\"false\" />\n          <Property Name=\"ReputationValue_fr\" Type=\"String\" MaxLength=\"255\" FixedLength=\"false\" Unicode=\"false\" />\n          <Property Name=\"ReputationValue_de\" Type=\"String\" MaxLength=\"255\" FixedLength=\"false\" Unicode=\"false\" />\n        </EntityType>\n        <EntityType Name=\"BNpcName\">\n          <Key>\n            <PropertyRef Name=\"Key\" />\n          </Key>\n          <Property Name=\"Key\" Type=\"Int64\" Nullable=\"false\" />\n          <Property Name=\"SGL_ja\" Type=\"String\" MaxLength=\"255\" FixedLength=\"false\" Unicode=\"false\" />\n          <Property Name=\"SGL_en\" Type=\"String\" MaxLength=\"255\" FixedLength=\"false\" Unicode=\"false\" />\n          <Property Name=\"SGL_fr\" Type=\"String\" MaxLength=\"255\" FixedLength=\"false\" Unicode=\"false\" />\n          <Property Name=\"SGL_de\" Type=\"String\" MaxLength=\"255\" FixedLength=\"false\" Unicode=\"false\" />\n          <Property Name=\"area\" Type=\"String\" MaxLength=\"255\" FixedLength=\"false\" Unicode=\"false\" />\n          <Property Name=\"data\" Type=\"Binary\" MaxLength=\"2147483647\" FixedLength=\"false\" />\n          <Property Name=\"path\" Type=\"String\" MaxLength=\"2147483647\" FixedLength=\"false\" Unicode=\"true\" />\n          <Property Name=\"Index_ja\" Type=\"String\" MaxLength=\"2147483647\" FixedLength=\"false\" Unicode=\"true\" />\n          <Property Name=\"Index_en\" Type=\"String\" MaxLength=\"2147483647\" FixedLength=\"false\" Unicode=\"true\" />\n          <Property Name=\"Index_fr\" Type=\"String\" MaxLength=\"2147483647\" FixedLength=\"false\" Unicode=\"true\" />\n          <Property Name=\"Index_de\" Type=\"String\" MaxLength=\"2147483647\" FixedLength=\"false\" Unicode=\"true\" />\n        </EntityType>\n        <EntityType Name=\"BNpcName_PlaceName\">\n          <Key>\n            <PropertyRef Name=\"BNpcName_Key\" />\n            <PropertyRef Name=\"PlaceName_Key\" />\n            <PropertyRef Name=\"region\" />\n          </Key>\n          <Property Name=\"BNpcName_Key\" Type=\"Int64\" Nullable=\"false\" />\n          <Property Name=\"PlaceName_Key\" Type=\"Int64\" Nullable=\"false\" />\n          <Property Name=\"region\" Type=\"Int64\" Nullable=\"false\" />\n        </EntityType>\n        <EntityType Name=\"ClassJob\">\n          <Key>\n            <PropertyRef Name=\"Key\" />\n          </Key>\n          <Property Name=\"Key\" Type=\"Int64\" Nullable=\"false\" />\n          <Property Name=\"Label\" Type=\"String\" MaxLength=\"255\" FixedLength=\"false\" Unicode=\"false\" />\n          <Property Name=\"Name_ja\" Type=\"String\" MaxLength=\"255\" FixedLength=\"false\" Unicode=\"false\" />\n          <Property Name=\"Name_en\" Type=\"String\" MaxLength=\"255\" FixedLength=\"false\" Unicode=\"false\" />\n          <Property Name=\"Name_fr\" Type=\"String\" MaxLength=\"255\" FixedLength=\"false\" Unicode=\"false\" />\n          <Property Name=\"Name_de\" Type=\"String\" MaxLength=\"255\" FixedLength=\"false\" Unicode=\"false\" />\n          <Property Name=\"Abbreviation_ja\" Type=\"String\" MaxLength=\"255\" FixedLength=\"false\" Unicode=\"false\" />\n          <Property Name=\"Abbreviation_en\" Type=\"String\" MaxLength=\"255\" FixedLength=\"false\" Unicode=\"false\" />\n          <Property Name=\"Abbreviation_fr\" Type=\"String\" MaxLength=\"255\" FixedLength=\"false\" Unicode=\"false\" />\n          <Property Name=\"Abbreviation_de\" Type=\"String\" MaxLength=\"255\" FixedLength=\"false\" Unicode=\"false\" />\n          <Property Name=\"IsJob\" Type=\"Int64\" />\n          <Property Name=\"UIPriority\" Type=\"Int64\" />\n          <Property Name=\"filebase\" Type=\"String\" MaxLength=\"255\" FixedLength=\"false\" Unicode=\"false\" />\n        </EntityType>\n        <EntityType Name=\"ClassJob_ClassJobCategory\">\n          <Key>\n            <PropertyRef Name=\"ClassJob_Key\" />\n            <PropertyRef Name=\"ClassJobCategory_Key\" />\n          </Key>\n          <Property Name=\"ClassJob_Key\" Type=\"Int64\" Nullable=\"false\" />\n          <Property Name=\"ClassJobCategory_Key\" Type=\"Int64\" Nullable=\"false\" />\n        </EntityType>\n        <EntityType Name=\"ClassJobCategory\">\n          <Key>\n            <PropertyRef Name=\"Key\" />\n          </Key>\n          <Property Name=\"Key\" Type=\"Int64\" Nullable=\"false\" />\n          <Property Name=\"Name_ja\" Type=\"String\" MaxLength=\"255\" FixedLength=\"false\" Unicode=\"false\" />\n          <Property Name=\"Name_en\" Type=\"String\" MaxLength=\"255\" FixedLength=\"false\" Unicode=\"false\" />\n          <Property Name=\"Name_fr\" Type=\"String\" MaxLength=\"255\" FixedLength=\"false\" Unicode=\"false\" />\n          <Property Name=\"Name_de\" Type=\"String\" MaxLength=\"255\" FixedLength=\"false\" Unicode=\"false\" />\n          <Property Name=\"classjob\" Type=\"String\" MaxLength=\"255\" FixedLength=\"false\" Unicode=\"false\" />\n        </EntityType>\n        <EntityType Name=\"Colosseum\">\n          <Key>\n            <PropertyRef Name=\"Key\" />\n          </Key>\n          <Property Name=\"Key\" Type=\"Int64\" Nullable=\"false\" />\n          <Property Name=\"WinExp\" Type=\"Int64\" />\n          <Property Name=\"WinPoint\" Type=\"Int64\" />\n          <Property Name=\"WinToken\" Type=\"Int64\" />\n          <Property Name=\"LosePoint\" Type=\"Int64\" />\n          <Property Name=\"LoseToken\" Type=\"Int64\" />\n        </EntityType>\n        <EntityType Name=\"ContentRoulette\">\n          <Key>\n            <PropertyRef Name=\"Key\" />\n          </Key>\n          <Property Name=\"Key\" Type=\"Int64\" Nullable=\"false\" />\n          <Property Name=\"Name_ja\" Type=\"String\" MaxLength=\"255\" FixedLength=\"false\" Unicode=\"false\" />\n          <Property Name=\"Name_en\" Type=\"String\" MaxLength=\"255\" FixedLength=\"false\" Unicode=\"false\" />\n          <Property Name=\"Name_fr\" Type=\"String\" MaxLength=\"255\" FixedLength=\"false\" Unicode=\"false\" />\n          <Property Name=\"Name_de\" Type=\"String\" MaxLength=\"255\" FixedLength=\"false\" Unicode=\"false\" />\n        </EntityType>\n        <EntityType Name=\"ContentType\">\n          <Key>\n            <PropertyRef Name=\"Key\" />\n          </Key>\n          <Property Name=\"Key\" Type=\"Int64\" Nullable=\"false\" />\n          <Property Name=\"Name_ja\" Type=\"String\" MaxLength=\"255\" FixedLength=\"false\" Unicode=\"false\" />\n          <Property Name=\"Name_en\" Type=\"String\" MaxLength=\"255\" FixedLength=\"false\" Unicode=\"false\" />\n          <Property Name=\"Name_fr\" Type=\"String\" MaxLength=\"255\" FixedLength=\"false\" Unicode=\"false\" />\n          <Property Name=\"Name_de\" Type=\"String\" MaxLength=\"255\" FixedLength=\"false\" Unicode=\"false\" />\n        </EntityType>\n        <EntityType Name=\"CraftType\">\n          <Key>\n            <PropertyRef Name=\"Key\" />\n          </Key>\n          <Property Name=\"Key\" Type=\"Int64\" Nullable=\"false\" />\n          <Property Name=\"ClassJob\" Type=\"Int64\" />\n        </EntityType>\n        <EntityType Name=\"Emote\">\n          <Key>\n            <PropertyRef Name=\"Key\" />\n          </Key>\n          <Property Name=\"Key\" Type=\"Int64\" Nullable=\"false\" />\n          <Property Name=\"Icon\" Type=\"Int64\" />\n          <Property Name=\"Name_ja\" Type=\"String\" MaxLength=\"255\" FixedLength=\"false\" Unicode=\"false\" />\n          <Property Name=\"Name_en\" Type=\"String\" MaxLength=\"255\" FixedLength=\"false\" Unicode=\"false\" />\n          <Property Name=\"Name_fr\" Type=\"String\" MaxLength=\"255\" FixedLength=\"false\" Unicode=\"false\" />\n          <Property Name=\"Name_de\" Type=\"String\" MaxLength=\"255\" FixedLength=\"false\" Unicode=\"false\" />\n        </EntityType>\n        <EntityType Name=\"ENpcResident\">\n          <Key>\n            <PropertyRef Name=\"Key\" />\n          </Key>\n          <Property Name=\"Key\" Type=\"Int64\" Nullable=\"false\" />\n          <Property Name=\"SGL_ja\" Type=\"String\" MaxLength=\"255\" FixedLength=\"false\" Unicode=\"false\" />\n          <Property Name=\"SGL_en\" Type=\"String\" MaxLength=\"255\" FixedLength=\"false\" Unicode=\"false\" />\n          <Property Name=\"SGL_fr\" Type=\"String\" MaxLength=\"255\" FixedLength=\"false\" Unicode=\"false\" />\n          <Property Name=\"SGL_de\" Type=\"String\" MaxLength=\"255\" FixedLength=\"false\" Unicode=\"false\" />\n          <Property Name=\"area\" Type=\"String\" MaxLength=\"255\" FixedLength=\"false\" Unicode=\"false\" />\n          <Property Name=\"data\" Type=\"Binary\" MaxLength=\"2147483647\" FixedLength=\"false\" />\n          <Property Name=\"path\" Type=\"String\" MaxLength=\"2147483647\" FixedLength=\"false\" Unicode=\"true\" />\n          <Property Name=\"has_shop\" Type=\"Boolean\" />\n          <Property Name=\"has_quest\" Type=\"Boolean\" />\n          <Property Name=\"has_condition_shop\" Type=\"Boolean\" />\n          <Property Name=\"Index_ja\" Type=\"String\" MaxLength=\"2147483647\" FixedLength=\"false\" Unicode=\"true\" />\n          <Property Name=\"Index_en\" Type=\"String\" MaxLength=\"2147483647\" FixedLength=\"false\" Unicode=\"true\" />\n          <Property Name=\"Index_fr\" Type=\"String\" MaxLength=\"2147483647\" FixedLength=\"false\" Unicode=\"true\" />\n          <Property Name=\"Index_de\" Type=\"String\" MaxLength=\"2147483647\" FixedLength=\"false\" Unicode=\"true\" />\n        </EntityType>\n        <EntityType Name=\"ENpcResident_PlaceName\">\n          <Key>\n            <PropertyRef Name=\"ENpcResident_Key\" />\n            <PropertyRef Name=\"PlaceName_Key\" />\n            <PropertyRef Name=\"region\" />\n          </Key>\n          <Property Name=\"ENpcResident_Key\" Type=\"Int64\" Nullable=\"false\" />\n          <Property Name=\"PlaceName_Key\" Type=\"Int64\" Nullable=\"false\" />\n          <Property Name=\"region\" Type=\"Int64\" Nullable=\"false\" />\n        </EntityType>\n        <EntityType Name=\"ENpcResident_Quest\">\n          <Key>\n            <PropertyRef Name=\"ENpcResident_Key\" />\n            <PropertyRef Name=\"Quest_Key\" />\n          </Key>\n          <Property Name=\"ENpcResident_Key\" Type=\"Int64\" Nullable=\"false\" />\n          <Property Name=\"Quest_Key\" Type=\"Int64\" Nullable=\"false\" />\n        </EntityType>\n        <EntityType Name=\"FCHierarchy\">\n          <Key>\n            <PropertyRef Name=\"Key\" />\n          </Key>\n          <Property Name=\"Key\" Type=\"Int64\" Nullable=\"false\" />\n          <Property Name=\"Name_ja\" Type=\"String\" MaxLength=\"255\" FixedLength=\"false\" Unicode=\"false\" />\n          <Property Name=\"Name_en\" Type=\"String\" MaxLength=\"255\" FixedLength=\"false\" Unicode=\"false\" />\n          <Property Name=\"Name_fr\" Type=\"String\" MaxLength=\"255\" FixedLength=\"false\" Unicode=\"false\" />\n          <Property Name=\"Name_de\" Type=\"String\" MaxLength=\"255\" FixedLength=\"false\" Unicode=\"false\" />\n        </EntityType>\n        <EntityType Name=\"FCRank\">\n          <Key>\n            <PropertyRef Name=\"Key\" />\n          </Key>\n          <Property Name=\"Key\" Type=\"Int64\" Nullable=\"false\" />\n          <Property Name=\"CurrentPoint\" Type=\"Int64\" />\n          <Property Name=\"NextPoint\" Type=\"Int64\" />\n        </EntityType>\n        <EntityType Name=\"FCReputation\">\n          <Key>\n            <PropertyRef Name=\"Key\" />\n          </Key>\n          <Property Name=\"Key\" Type=\"Int64\" Nullable=\"false\" />\n          <Property Name=\"Color\" Type=\"Int64\" />\n          <Property Name=\"CurrentPoint\" Type=\"Int64\" />\n          <Property Name=\"NextPoint\" Type=\"Int64\" />\n          <Property Name=\"Text_ja\" Type=\"String\" MaxLength=\"255\" FixedLength=\"false\" Unicode=\"false\" />\n          <Property Name=\"Text_en\" Type=\"String\" MaxLength=\"255\" FixedLength=\"false\" Unicode=\"false\" />\n          <Property Name=\"Text_fr\" Type=\"String\" MaxLength=\"255\" FixedLength=\"false\" Unicode=\"false\" />\n          <Property Name=\"Text_de\" Type=\"String\" MaxLength=\"255\" FixedLength=\"false\" Unicode=\"false\" />\n          <Property Name=\"data\" Type=\"Binary\" MaxLength=\"2147483647\" FixedLength=\"false\" />\n        </EntityType>\n        <EntityType Name=\"Frontline01\">\n          <Key>\n            <PropertyRef Name=\"Key\" />\n          </Key>\n          <Property Name=\"Key\" Type=\"Int64\" Nullable=\"false\" />\n          <Property Name=\"WinPvPExp\" Type=\"String\" MaxLength=\"2147483647\" FixedLength=\"false\" Unicode=\"true\" />\n          <Property Name=\"WinPvPToken\" Type=\"String\" MaxLength=\"2147483647\" FixedLength=\"false\" Unicode=\"true\" />\n        </EntityType>\n        <EntityType Name=\"Gathering\">\n          <Key>\n            <PropertyRef Name=\"Key\" />\n          </Key>\n          <Property Name=\"Key\" Type=\"Int64\" Nullable=\"false\" />\n          <Property Name=\"Item\" Type=\"Int64\" />\n          <Property Name=\"GatheringType\" Type=\"Int64\" />\n          <Property Name=\"Level\" Type=\"Int64\" />\n          <Property Name=\"levelView\" Type=\"Int64\" />\n          <Property Name=\"levelDiff\" Type=\"Int64\" />\n          <Property Name=\"is_hidden\" Type=\"Boolean\" />\n          <Property Name=\"data\" Type=\"Binary\" MaxLength=\"2147483647\" FixedLength=\"false\" />\n          <Property Name=\"GatheringNotebookList\" Type=\"Int64\" />\n          <Property Name=\"GatheringItemNo\" Type=\"Int64\" />\n          <Property Name=\"path\" Type=\"String\" MaxLength=\"2147483647\" FixedLength=\"false\" Unicode=\"true\" />\n          <Property Name=\"Index_ja\" Type=\"String\" MaxLength=\"2147483647\" FixedLength=\"false\" Unicode=\"true\" />\n          <Property Name=\"Index_en\" Type=\"String\" MaxLength=\"2147483647\" FixedLength=\"false\" Unicode=\"true\" />\n          <Property Name=\"Index_fr\" Type=\"String\" MaxLength=\"2147483647\" FixedLength=\"false\" Unicode=\"true\" />\n          <Property Name=\"Index_de\" Type=\"String\" MaxLength=\"2147483647\" FixedLength=\"false\" Unicode=\"true\" />\n        </EntityType>\n        <EntityType Name=\"GatheringType\">\n          <Key>\n            <PropertyRef Name=\"Key\" />\n          </Key>\n          <Property Name=\"Key\" Type=\"Int64\" Nullable=\"false\" />\n          <Property Name=\"Text_de\" Type=\"String\" MaxLength=\"255\" FixedLength=\"false\" Unicode=\"false\" />\n          <Property Name=\"Text_en\" Type=\"String\" MaxLength=\"255\" FixedLength=\"false\" Unicode=\"false\" />\n          <Property Name=\"Text_ja\" Type=\"String\" MaxLength=\"255\" FixedLength=\"false\" Unicode=\"false\" />\n          <Property Name=\"Text_fr\" Type=\"String\" MaxLength=\"255\" FixedLength=\"false\" Unicode=\"false\" />\n        </EntityType>\n        <EntityType Name=\"GCRankGridaniaFemaleText\">\n          <Key>\n            <PropertyRef Name=\"Key\" />\n          </Key>\n          <Property Name=\"Key\" Type=\"Int64\" Nullable=\"false\" />\n          <Property Name=\"SGL_ja\" Type=\"String\" MaxLength=\"255\" FixedLength=\"false\" Unicode=\"false\" />\n          <Property Name=\"SGL_en\" Type=\"String\" MaxLength=\"255\" FixedLength=\"false\" Unicode=\"false\" />\n          <Property Name=\"SGL_fr\" Type=\"String\" MaxLength=\"255\" FixedLength=\"false\" Unicode=\"false\" />\n          <Property Name=\"SGL_de\" Type=\"String\" MaxLength=\"255\" FixedLength=\"false\" Unicode=\"false\" />\n        </EntityType>\n        <EntityType Name=\"GCRankGridaniaMaleText\">\n          <Key>\n            <PropertyRef Name=\"Key\" />\n          </Key>\n          <Property Name=\"Key\" Type=\"Int64\" Nullable=\"false\" />\n          <Property Name=\"SGL_ja\" Type=\"String\" MaxLength=\"255\" FixedLength=\"false\" Unicode=\"false\" />\n          <Property Name=\"SGL_en\" Type=\"String\" MaxLength=\"255\" FixedLength=\"false\" Unicode=\"false\" />\n          <Property Name=\"SGL_fr\" Type=\"String\" MaxLength=\"255\" FixedLength=\"false\" Unicode=\"false\" />\n          <Property Name=\"SGL_de\" Type=\"String\" MaxLength=\"255\" FixedLength=\"false\" Unicode=\"false\" />\n        </EntityType>\n        <EntityType Name=\"GCRankLimsaFemaleText\">\n          <Key>\n            <PropertyRef Name=\"Key\" />\n          </Key>\n          <Property Name=\"Key\" Type=\"Int64\" Nullable=\"false\" />\n          <Property Name=\"SGL_ja\" Type=\"String\" MaxLength=\"255\" FixedLength=\"false\" Unicode=\"false\" />\n          <Property Name=\"SGL_en\" Type=\"String\" MaxLength=\"255\" FixedLength=\"false\" Unicode=\"false\" />\n          <Property Name=\"SGL_fr\" Type=\"String\" MaxLength=\"255\" FixedLength=\"false\" Unicode=\"false\" />\n          <Property Name=\"SGL_de\" Type=\"String\" MaxLength=\"255\" FixedLength=\"false\" Unicode=\"false\" />\n        </EntityType>\n        <EntityType Name=\"GCRankLimsaMaleText\">\n          <Key>\n            <PropertyRef Name=\"Key\" />\n          </Key>\n          <Property Name=\"Key\" Type=\"Int64\" Nullable=\"false\" />\n          <Property Name=\"SGL_ja\" Type=\"String\" MaxLength=\"255\" FixedLength=\"false\" Unicode=\"false\" />\n          <Property Name=\"SGL_en\" Type=\"String\" MaxLength=\"255\" FixedLength=\"false\" Unicode=\"false\" />\n          <Property Name=\"SGL_fr\" Type=\"String\" MaxLength=\"255\" FixedLength=\"false\" Unicode=\"false\" />\n          <Property Name=\"SGL_de\" Type=\"String\" MaxLength=\"255\" FixedLength=\"false\" Unicode=\"false\" />\n        </EntityType>\n        <EntityType Name=\"GCRankUldahFemaleText\">\n          <Key>\n            <PropertyRef Name=\"Key\" />\n          </Key>\n          <Property Name=\"Key\" Type=\"Int64\" Nullable=\"false\" />\n          <Property Name=\"SGL_ja\" Type=\"String\" MaxLength=\"255\" FixedLength=\"false\" Unicode=\"false\" />\n          <Property Name=\"SGL_en\" Type=\"String\" MaxLength=\"255\" FixedLength=\"false\" Unicode=\"false\" />\n          <Property Name=\"SGL_fr\" Type=\"String\" MaxLength=\"255\" FixedLength=\"false\" Unicode=\"false\" />\n          <Property Name=\"SGL_de\" Type=\"String\" MaxLength=\"255\" FixedLength=\"false\" Unicode=\"false\" />\n        </EntityType>\n        <EntityType Name=\"GCRankUldahMaleText\">\n          <Key>\n            <PropertyRef Name=\"Key\" />\n          </Key>\n          <Property Name=\"Key\" Type=\"Int64\" Nullable=\"false\" />\n          <Property Name=\"SGL_ja\" Type=\"String\" MaxLength=\"255\" FixedLength=\"false\" Unicode=\"false\" />\n          <Property Name=\"SGL_en\" Type=\"String\" MaxLength=\"255\" FixedLength=\"false\" Unicode=\"false\" />\n          <Property Name=\"SGL_fr\" Type=\"String\" MaxLength=\"255\" FixedLength=\"false\" Unicode=\"false\" />\n          <Property Name=\"SGL_de\" Type=\"String\" MaxLength=\"255\" FixedLength=\"false\" Unicode=\"false\" />\n        </EntityType>\n        <EntityType Name=\"GeneralAction\">\n          <Key>\n            <PropertyRef Name=\"Key\" />\n          </Key>\n          <Property Name=\"Key\" Type=\"Int64\" Nullable=\"false\" />\n          <Property Name=\"Icon\" Type=\"Int64\" />\n          <Property Name=\"Name_ja\" Type=\"String\" MaxLength=\"255\" FixedLength=\"false\" Unicode=\"false\" />\n          <Property Name=\"Name_en\" Type=\"String\" MaxLength=\"255\" FixedLength=\"false\" Unicode=\"false\" />\n          <Property Name=\"Name_fr\" Type=\"String\" MaxLength=\"255\" FixedLength=\"false\" Unicode=\"false\" />\n          <Property Name=\"Name_de\" Type=\"String\" MaxLength=\"255\" FixedLength=\"false\" Unicode=\"false\" />\n        </EntityType>\n        <EntityType Name=\"GrandCompany\">\n          <Key>\n            <PropertyRef Name=\"Key\" />\n          </Key>\n          <Property Name=\"Key\" Type=\"Int64\" Nullable=\"false\" />\n          <Property Name=\"SGL_ja\" Type=\"String\" MaxLength=\"255\" FixedLength=\"false\" Unicode=\"false\" />\n          <Property Name=\"SGL_en\" Type=\"String\" MaxLength=\"255\" FixedLength=\"false\" Unicode=\"false\" />\n          <Property Name=\"SGL_fr\" Type=\"String\" MaxLength=\"255\" FixedLength=\"false\" Unicode=\"false\" />\n          <Property Name=\"SGL_de\" Type=\"String\" MaxLength=\"255\" FixedLength=\"false\" Unicode=\"false\" />\n        </EntityType>\n        <EntityType Name=\"GuardianDeity\">\n          <Key>\n            <PropertyRef Name=\"Key\" />\n          </Key>\n          <Property Name=\"Key\" Type=\"Int64\" Nullable=\"false\" />\n          <Property Name=\"Name_ja\" Type=\"String\" MaxLength=\"255\" FixedLength=\"false\" Unicode=\"false\" />\n          <Property Name=\"Name_en\" Type=\"String\" MaxLength=\"255\" FixedLength=\"false\" Unicode=\"false\" />\n          <Property Name=\"Name_fr\" Type=\"String\" MaxLength=\"255\" FixedLength=\"false\" Unicode=\"false\" />\n          <Property Name=\"Name_de\" Type=\"String\" MaxLength=\"255\" FixedLength=\"false\" Unicode=\"false\" />\n        </EntityType>\n        <EntityType Name=\"GuildOrder\">\n          <Key>\n            <PropertyRef Name=\"Key\" />\n          </Key>\n          <Property Name=\"Key\" Type=\"Int64\" Nullable=\"false\" />\n          <Property Name=\"Exp00\" Type=\"Int64\" />\n          <Property Name=\"Exp01\" Type=\"Int64\" />\n          <Property Name=\"Gil00\" Type=\"Int64\" />\n          <Property Name=\"Gil01\" Type=\"Int64\" />\n        </EntityType>\n        <EntityType Name=\"InstanceContent\">\n          <Key>\n            <PropertyRef Name=\"Key\" />\n          </Key>\n          <Property Name=\"Key\" Type=\"Int64\" Nullable=\"false\" />\n          <Property Name=\"Type\" Type=\"Int64\" />\n          <Property Name=\"Sortkey\" Type=\"Int64\" />\n          <Property Name=\"Name_ja\" Type=\"String\" MaxLength=\"255\" FixedLength=\"false\" Unicode=\"false\" />\n          <Property Name=\"Name_en\" Type=\"String\" MaxLength=\"255\" FixedLength=\"false\" Unicode=\"false\" />\n          <Property Name=\"Name_fr\" Type=\"String\" MaxLength=\"255\" FixedLength=\"false\" Unicode=\"false\" />\n          <Property Name=\"Name_de\" Type=\"String\" MaxLength=\"255\" FixedLength=\"false\" Unicode=\"false\" />\n          <Property Name=\"Description_ja\" Type=\"String\" MaxLength=\"255\" FixedLength=\"false\" Unicode=\"false\" />\n          <Property Name=\"Description_en\" Type=\"String\" MaxLength=\"255\" FixedLength=\"false\" Unicode=\"false\" />\n          <Property Name=\"Description_fr\" Type=\"String\" MaxLength=\"255\" FixedLength=\"false\" Unicode=\"false\" />\n          <Property Name=\"Description_de\" Type=\"String\" MaxLength=\"255\" FixedLength=\"false\" Unicode=\"false\" />\n          <Property Name=\"LevelMin\" Type=\"Int64\" />\n          <Property Name=\"LevelMax\" Type=\"Int64\" />\n          <Property Name=\"Time\" Type=\"Int64\" />\n          <Property Name=\"Halfway\" Type=\"Boolean\" />\n          <Property Name=\"RandomContentType\" Type=\"Int64\" />\n          <Property Name=\"Alliance\" Type=\"Boolean\" />\n          <Property Name=\"FinderPartyCondition\" Type=\"Int64\" />\n          <Property Name=\"PartyMemberCount\" Type=\"Int64\" />\n          <Property Name=\"TankCount\" Type=\"Int64\" />\n          <Property Name=\"HealerCount\" Type=\"Int64\" />\n          <Property Name=\"AttackerCount\" Type=\"Int64\" />\n          <Property Name=\"RangeCount\" Type=\"Int64\" />\n          <Property Name=\"DifferentiateDPS\" Type=\"Boolean\" />\n          <Property Name=\"PartyCount\" Type=\"Int64\" />\n          <Property Name=\"FreeRole\" Type=\"Boolean\" />\n          <Property Name=\"ItemLevel\" Type=\"Int64\" />\n          <Property Name=\"ItemLevelMax\" Type=\"Int64\" />\n          <Property Name=\"Colosseum\" Type=\"Int64\" />\n          <Property Name=\"Area\" Type=\"Int64\" />\n          <Property Name=\"ForceCount\" Type=\"Int64\" />\n          <Property Name=\"data\" Type=\"Binary\" MaxLength=\"2147483647\" FixedLength=\"false\" />\n          <Property Name=\"path\" Type=\"String\" MaxLength=\"2147483647\" FixedLength=\"false\" Unicode=\"true\" />\n          <Property Name=\"is_koeru_usually\" Type=\"Boolean\" />\n          <Property Name=\"is_koeru_annihilation\" Type=\"Boolean\" />\n          <Property Name=\"Index_ja\" Type=\"String\" MaxLength=\"2147483647\" FixedLength=\"false\" Unicode=\"true\" />\n          <Property Name=\"Index_en\" Type=\"String\" MaxLength=\"2147483647\" FixedLength=\"false\" Unicode=\"true\" />\n          <Property Name=\"Index_fr\" Type=\"String\" MaxLength=\"2147483647\" FixedLength=\"false\" Unicode=\"true\" />\n          <Property Name=\"Index_de\" Type=\"String\" MaxLength=\"2147483647\" FixedLength=\"false\" Unicode=\"true\" />\n        </EntityType>\n        <EntityType Name=\"InstanceContentType\">\n          <Key>\n            <PropertyRef Name=\"Type\" />\n          </Key>\n          <Property Name=\"Type\" Type=\"Int64\" Nullable=\"false\" />\n          <Property Name=\"Sortkey\" Type=\"Int64\" />\n          <Property Name=\"ContentType\" Type=\"Int64\" />\n        </EntityType>\n        <EntityType Name=\"Item\">\n          <Key>\n            <PropertyRef Name=\"Key\" />\n          </Key>\n          <Property Name=\"Key\" Type=\"Int64\" Nullable=\"false\" />\n          <Property Name=\"Category\" Type=\"Int64\" />\n          <Property Name=\"UICategory\" Type=\"Int64\" />\n          <Property Name=\"UIName_ja\" Type=\"String\" MaxLength=\"255\" FixedLength=\"false\" Unicode=\"false\" />\n          <Property Name=\"UIName_en\" Type=\"String\" MaxLength=\"255\" FixedLength=\"false\" Unicode=\"false\" />\n          <Property Name=\"UIName_fr\" Type=\"String\" MaxLength=\"255\" FixedLength=\"false\" Unicode=\"false\" />\n          <Property Name=\"UIName_de\" Type=\"String\" MaxLength=\"255\" FixedLength=\"false\" Unicode=\"false\" />\n          <Property Name=\"Help_ja\" Type=\"String\" MaxLength=\"255\" FixedLength=\"false\" Unicode=\"false\" />\n          <Property Name=\"Help_en\" Type=\"String\" MaxLength=\"255\" FixedLength=\"false\" Unicode=\"false\" />\n          <Property Name=\"Help_fr\" Type=\"String\" MaxLength=\"255\" FixedLength=\"false\" Unicode=\"false\" />\n          <Property Name=\"Help_de\" Type=\"String\" MaxLength=\"255\" FixedLength=\"false\" Unicode=\"false\" />\n          <Property Name=\"Level\" Type=\"Int64\" />\n          <Property Name=\"EquipLevel\" Type=\"Int64\" />\n          <Property Name=\"Rarity\" Type=\"Int64\" />\n          <Property Name=\"HQ\" Type=\"Boolean\" />\n          <Property Name=\"SpecialBonus\" Type=\"Int64\" />\n          <Property Name=\"Series\" Type=\"Int64\" />\n          <Property Name=\"Slot\" Type=\"Int64\" />\n          <Property Name=\"Damage\" Type=\"Int64\" />\n          <Property Name=\"Damage_hq\" Type=\"Int64\" />\n          <Property Name=\"MagicDamage\" Type=\"Int64\" />\n          <Property Name=\"MagicDamage_hq\" Type=\"Int64\" />\n          <Property Name=\"Defense\" Type=\"Int64\" />\n          <Property Name=\"Defense_hq\" Type=\"Int64\" />\n          <Property Name=\"MagicDefense\" Type=\"Int64\" />\n          <Property Name=\"MagicDefense_hq\" Type=\"Int64\" />\n          <Property Name=\"ShieldRate\" Type=\"Int64\" />\n          <Property Name=\"ShieldRate_hq\" Type=\"Int64\" />\n          <Property Name=\"ShieldBlockRate\" Type=\"Int64\" />\n          <Property Name=\"ShieldBlockRate_hq\" Type=\"Int64\" />\n          <Property Name=\"Price\" Type=\"Int64\" />\n          <Property Name=\"PriceMin\" Type=\"Int64\" />\n          <Property Name=\"MirageItem\" Type=\"Int64\" />\n          <Property Name=\"icon\" Type=\"String\" MaxLength=\"255\" FixedLength=\"false\" Unicode=\"false\" />\n          <Property Name=\"icon_hq\" Type=\"String\" MaxLength=\"255\" FixedLength=\"false\" Unicode=\"false\" />\n          <Property Name=\"classjob\" Type=\"String\" MaxLength=\"255\" FixedLength=\"false\" Unicode=\"false\" />\n          <Property Name=\"Salvage\" Type=\"Int64\" />\n          <Property Name=\"data\" Type=\"Binary\" MaxLength=\"2147483647\" FixedLength=\"false\" />\n          <Property Name=\"legacy\" Type=\"Boolean\" />\n          <Property Name=\"path\" Type=\"String\" MaxLength=\"2147483647\" FixedLength=\"false\" Unicode=\"true\" />\n          <Property Name=\"Index_ja\" Type=\"String\" MaxLength=\"2147483647\" FixedLength=\"false\" Unicode=\"true\" />\n          <Property Name=\"Index_en\" Type=\"String\" MaxLength=\"2147483647\" FixedLength=\"false\" Unicode=\"true\" />\n          <Property Name=\"Index_fr\" Type=\"String\" MaxLength=\"2147483647\" FixedLength=\"false\" Unicode=\"true\" />\n          <Property Name=\"Index_de\" Type=\"String\" MaxLength=\"2147483647\" FixedLength=\"false\" Unicode=\"true\" />\n          <Property Name=\"SortId\" Type=\"Int64\" />\n        </EntityType>\n        <EntityType Name=\"Item_ClassJob\">\n          <Key>\n            <PropertyRef Name=\"Item_Key\" />\n            <PropertyRef Name=\"ClassJob_Key\" />\n          </Key>\n          <Property Name=\"Item_Key\" Type=\"Int64\" Nullable=\"false\" />\n          <Property Name=\"ClassJob_Key\" Type=\"Int64\" Nullable=\"false\" />\n        </EntityType>\n        <EntityType Name=\"ItemCategory\">\n          <Key>\n            <PropertyRef Name=\"Key\" />\n          </Key>\n          <Property Name=\"Key\" Type=\"Int64\" Nullable=\"false\" />\n          <Property Name=\"Name_ja\" Type=\"String\" MaxLength=\"255\" FixedLength=\"false\" Unicode=\"false\" />\n          <Property Name=\"Name_en\" Type=\"String\" MaxLength=\"255\" FixedLength=\"false\" Unicode=\"false\" />\n          <Property Name=\"Name_fr\" Type=\"String\" MaxLength=\"255\" FixedLength=\"false\" Unicode=\"false\" />\n          <Property Name=\"Name_de\" Type=\"String\" MaxLength=\"255\" FixedLength=\"false\" Unicode=\"false\" />\n        </EntityType>\n        <EntityType Name=\"ItemSery\">\n          <Key>\n            <PropertyRef Name=\"Key\" />\n          </Key>\n          <Property Name=\"Key\" Type=\"Int64\" Nullable=\"false\" />\n          <Property Name=\"Name_ja\" Type=\"String\" MaxLength=\"255\" FixedLength=\"false\" Unicode=\"false\" />\n          <Property Name=\"Name_en\" Type=\"String\" MaxLength=\"255\" FixedLength=\"false\" Unicode=\"false\" />\n          <Property Name=\"Name_fr\" Type=\"String\" MaxLength=\"255\" FixedLength=\"false\" Unicode=\"false\" />\n          <Property Name=\"Name_de\" Type=\"String\" MaxLength=\"255\" FixedLength=\"false\" Unicode=\"false\" />\n        </EntityType>\n        <EntityType Name=\"ItemSpecialBonu\">\n          <Key>\n            <PropertyRef Name=\"Key\" />\n          </Key>\n          <Property Name=\"Key\" Type=\"Int64\" Nullable=\"false\" />\n          <Property Name=\"Name_ja\" Type=\"String\" MaxLength=\"255\" FixedLength=\"false\" Unicode=\"false\" />\n          <Property Name=\"Name_en\" Type=\"String\" MaxLength=\"255\" FixedLength=\"false\" Unicode=\"false\" />\n          <Property Name=\"Name_fr\" Type=\"String\" MaxLength=\"255\" FixedLength=\"false\" Unicode=\"false\" />\n          <Property Name=\"Name_de\" Type=\"String\" MaxLength=\"255\" FixedLength=\"false\" Unicode=\"false\" />\n        </EntityType>\n        <EntityType Name=\"ItemUICategory\">\n          <Key>\n            <PropertyRef Name=\"Key\" />\n          </Key>\n          <Property Name=\"Key\" Type=\"Int64\" Nullable=\"false\" />\n          <Property Name=\"Kind\" Type=\"Int64\" />\n          <Property Name=\"Name_ja\" Type=\"String\" MaxLength=\"255\" FixedLength=\"false\" Unicode=\"false\" />\n          <Property Name=\"Name_en\" Type=\"String\" MaxLength=\"255\" FixedLength=\"false\" Unicode=\"false\" />\n          <Property Name=\"Name_fr\" Type=\"String\" MaxLength=\"255\" FixedLength=\"false\" Unicode=\"false\" />\n          <Property Name=\"Name_de\" Type=\"String\" MaxLength=\"255\" FixedLength=\"false\" Unicode=\"false\" />\n          <Property Name=\"Priority\" Type=\"Int64\" />\n        </EntityType>\n        <EntityType Name=\"ItemUIKind\">\n          <Key>\n            <PropertyRef Name=\"Key\" />\n          </Key>\n          <Property Name=\"Key\" Type=\"Int64\" Nullable=\"false\" />\n          <Property Name=\"Name_ja\" Type=\"String\" MaxLength=\"255\" FixedLength=\"false\" Unicode=\"false\" />\n          <Property Name=\"Name_en\" Type=\"String\" MaxLength=\"255\" FixedLength=\"false\" Unicode=\"false\" />\n          <Property Name=\"Name_fr\" Type=\"String\" MaxLength=\"255\" FixedLength=\"false\" Unicode=\"false\" />\n          <Property Name=\"Name_de\" Type=\"String\" MaxLength=\"255\" FixedLength=\"false\" Unicode=\"false\" />\n        </EntityType>\n        <EntityType Name=\"JournalCategory\">\n          <Key>\n            <PropertyRef Name=\"Key\" />\n          </Key>\n          <Property Name=\"Key\" Type=\"Int64\" Nullable=\"false\" />\n          <Property Name=\"label\" Type=\"String\" MaxLength=\"255\" FixedLength=\"false\" Unicode=\"false\" />\n          <Property Name=\"Name_ja\" Type=\"String\" MaxLength=\"255\" FixedLength=\"false\" Unicode=\"false\" />\n          <Property Name=\"Name_en\" Type=\"String\" MaxLength=\"255\" FixedLength=\"false\" Unicode=\"false\" />\n          <Property Name=\"Name_fr\" Type=\"String\" MaxLength=\"255\" FixedLength=\"false\" Unicode=\"false\" />\n          <Property Name=\"Name_de\" Type=\"String\" MaxLength=\"255\" FixedLength=\"false\" Unicode=\"false\" />\n        </EntityType>\n        <EntityType Name=\"JournalGenre\">\n          <Key>\n            <PropertyRef Name=\"Key\" />\n          </Key>\n          <Property Name=\"Key\" Type=\"Int64\" Nullable=\"false\" />\n          <Property Name=\"Category\" Type=\"Int64\" />\n          <Property Name=\"label\" Type=\"String\" MaxLength=\"255\" FixedLength=\"false\" Unicode=\"false\" />\n          <Property Name=\"Name_ja\" Type=\"String\" MaxLength=\"255\" FixedLength=\"false\" Unicode=\"false\" />\n          <Property Name=\"Name_en\" Type=\"String\" MaxLength=\"255\" FixedLength=\"false\" Unicode=\"false\" />\n          <Property Name=\"Name_fr\" Type=\"String\" MaxLength=\"255\" FixedLength=\"false\" Unicode=\"false\" />\n          <Property Name=\"Name_de\" Type=\"String\" MaxLength=\"255\" FixedLength=\"false\" Unicode=\"false\" />\n        </EntityType>\n        <EntityType Name=\"LodestoneSystemDefine\">\n          <Key>\n            <PropertyRef Name=\"Key\" />\n          </Key>\n          <Property Name=\"Key\" Type=\"Int64\" Nullable=\"false\" />\n          <Property Name=\"DefineName\" Type=\"String\" MaxLength=\"255\" FixedLength=\"false\" Unicode=\"false\" />\n          <Property Name=\"DefineValue\" Type=\"String\" MaxLength=\"255\" FixedLength=\"false\" Unicode=\"false\" />\n        </EntityType>\n        <EntityType Name=\"NotebookDivision\">\n          <Key>\n            <PropertyRef Name=\"Key\" />\n          </Key>\n          <Property Name=\"Key\" Type=\"Int64\" Nullable=\"false\" />\n          <Property Name=\"Name_ja\" Type=\"String\" MaxLength=\"255\" FixedLength=\"false\" Unicode=\"false\" />\n          <Property Name=\"Name_en\" Type=\"String\" MaxLength=\"255\" FixedLength=\"false\" Unicode=\"false\" />\n          <Property Name=\"Name_fr\" Type=\"String\" MaxLength=\"255\" FixedLength=\"false\" Unicode=\"false\" />\n          <Property Name=\"Name_de\" Type=\"String\" MaxLength=\"255\" FixedLength=\"false\" Unicode=\"false\" />\n        </EntityType>\n        <EntityType Name=\"PlaceName\">\n          <Key>\n            <PropertyRef Name=\"Key\" />\n          </Key>\n          <Property Name=\"Key\" Type=\"Int64\" Nullable=\"false\" />\n          <Property Name=\"region\" Type=\"Int64\" />\n          <Property Name=\"SGL_ja\" Type=\"String\" MaxLength=\"255\" FixedLength=\"false\" Unicode=\"false\" />\n          <Property Name=\"SGL_en\" Type=\"String\" MaxLength=\"255\" FixedLength=\"false\" Unicode=\"false\" />\n          <Property Name=\"SGL_fr\" Type=\"String\" MaxLength=\"255\" FixedLength=\"false\" Unicode=\"false\" />\n          <Property Name=\"SGL_de\" Type=\"String\" MaxLength=\"255\" FixedLength=\"false\" Unicode=\"false\" />\n        </EntityType>\n        <EntityType Name=\"Quest\">\n          <Key>\n            <PropertyRef Name=\"Key\" />\n          </Key>\n          <Property Name=\"Key\" Type=\"Int64\" Nullable=\"false\" />\n          <Property Name=\"Genre\" Type=\"Int64\" />\n          <Property Name=\"Area\" Type=\"Int64\" />\n          <Property Name=\"Name_ja\" Type=\"String\" MaxLength=\"255\" FixedLength=\"false\" Unicode=\"false\" />\n          <Property Name=\"Name_en\" Type=\"String\" MaxLength=\"255\" FixedLength=\"false\" Unicode=\"false\" />\n          <Property Name=\"Name_fr\" Type=\"String\" MaxLength=\"255\" FixedLength=\"false\" Unicode=\"false\" />\n          <Property Name=\"Name_de\" Type=\"String\" MaxLength=\"255\" FixedLength=\"false\" Unicode=\"false\" />\n          <Property Name=\"CompanyPointType\" Type=\"Int64\" />\n          <Property Name=\"CompanyPointNum\" Type=\"Int64\" />\n          <Property Name=\"Gil\" Type=\"Int64\" />\n          <Property Name=\"Client\" Type=\"Int64\" />\n          <Property Name=\"ExpBonus\" Type=\"Int64\" />\n          <Property Name=\"Header\" Type=\"Int64\" />\n          <Property Name=\"ClassLevel\" Type=\"Int64\" />\n          <Property Name=\"ClassLevel2\" Type=\"Int64\" />\n          <Property Name=\"ClassJob\" Type=\"Int64\" />\n          <Property Name=\"ClassJob2\" Type=\"Int64\" />\n          <Property Name=\"QuestLevelOffset\" Type=\"Int64\" />\n          <Property Name=\"Sort\" Type=\"Int64\" />\n          <Property Name=\"BeastTribe\" Type=\"Int64\" />\n          <Property Name=\"BeastReputationValueNum\" Type=\"Int64\" />\n          <Property Name=\"WebType\" Type=\"Int64\" />\n          <Property Name=\"data\" Type=\"Binary\" MaxLength=\"2147483647\" FixedLength=\"false\" />\n          <Property Name=\"path\" Type=\"String\" MaxLength=\"2147483647\" FixedLength=\"false\" Unicode=\"true\" />\n          <Property Name=\"Index_ja\" Type=\"String\" MaxLength=\"2147483647\" FixedLength=\"false\" Unicode=\"true\" />\n          <Property Name=\"Index_en\" Type=\"String\" MaxLength=\"2147483647\" FixedLength=\"false\" Unicode=\"true\" />\n          <Property Name=\"Index_fr\" Type=\"String\" MaxLength=\"2147483647\" FixedLength=\"false\" Unicode=\"true\" />\n          <Property Name=\"Index_de\" Type=\"String\" MaxLength=\"2147483647\" FixedLength=\"false\" Unicode=\"true\" />\n        </EntityType>\n        <EntityType Name=\"Quest_ClassJob\">\n          <Key>\n            <PropertyRef Name=\"Quest_Key\" />\n            <PropertyRef Name=\"ClassJob_Key\" />\n          </Key>\n          <Property Name=\"Quest_Key\" Type=\"Int64\" Nullable=\"false\" />\n          <Property Name=\"ClassJob_Key\" Type=\"Int64\" Nullable=\"false\" />\n        </EntityType>\n        <EntityType Name=\"QuestWebEx\">\n          <Key>\n            <PropertyRef Name=\"Key\" />\n          </Key>\n          <Property Name=\"Key\" Type=\"Int64\" Nullable=\"false\" />\n          <Property Name=\"Name_ja\" Type=\"String\" MaxLength=\"255\" FixedLength=\"false\" Unicode=\"false\" />\n          <Property Name=\"Name_en\" Type=\"String\" MaxLength=\"255\" FixedLength=\"false\" Unicode=\"false\" />\n          <Property Name=\"Name_fr\" Type=\"String\" MaxLength=\"255\" FixedLength=\"false\" Unicode=\"false\" />\n          <Property Name=\"Name_de\" Type=\"String\" MaxLength=\"255\" FixedLength=\"false\" Unicode=\"false\" />\n        </EntityType>\n        <EntityType Name=\"QuestWebType\">\n          <Key>\n            <PropertyRef Name=\"Key\" />\n          </Key>\n          <Property Name=\"Key\" Type=\"Int64\" Nullable=\"false\" />\n          <Property Name=\"Text_de\" Type=\"String\" MaxLength=\"255\" FixedLength=\"false\" Unicode=\"false\" />\n          <Property Name=\"Text_en\" Type=\"String\" MaxLength=\"255\" FixedLength=\"false\" Unicode=\"false\" />\n          <Property Name=\"Text_ja\" Type=\"String\" MaxLength=\"255\" FixedLength=\"false\" Unicode=\"false\" />\n          <Property Name=\"Text_fr\" Type=\"String\" MaxLength=\"255\" FixedLength=\"false\" Unicode=\"false\" />\n        </EntityType>\n        <EntityType Name=\"Race\">\n          <Key>\n            <PropertyRef Name=\"Key\" />\n          </Key>\n          <Property Name=\"Key\" Type=\"Int64\" Nullable=\"false\" />\n          <Property Name=\"NameFemale_ja\" Type=\"String\" MaxLength=\"255\" FixedLength=\"false\" Unicode=\"false\" />\n          <Property Name=\"NameFemale_en\" Type=\"String\" MaxLength=\"255\" FixedLength=\"false\" Unicode=\"false\" />\n          <Property Name=\"NameFemale_fr\" Type=\"String\" MaxLength=\"255\" FixedLength=\"false\" Unicode=\"false\" />\n          <Property Name=\"NameFemale_de\" Type=\"String\" MaxLength=\"255\" FixedLength=\"false\" Unicode=\"false\" />\n          <Property Name=\"Name_ja\" Type=\"String\" MaxLength=\"255\" FixedLength=\"false\" Unicode=\"false\" />\n          <Property Name=\"Name_en\" Type=\"String\" MaxLength=\"255\" FixedLength=\"false\" Unicode=\"false\" />\n          <Property Name=\"Name_fr\" Type=\"String\" MaxLength=\"255\" FixedLength=\"false\" Unicode=\"false\" />\n          <Property Name=\"Name_de\" Type=\"String\" MaxLength=\"255\" FixedLength=\"false\" Unicode=\"false\" />\n        </EntityType>\n        <EntityType Name=\"Recipe\">\n          <Key>\n            <PropertyRef Name=\"Key\" />\n          </Key>\n          <Property Name=\"Key\" Type=\"Int64\" Nullable=\"false\" />\n          <Property Name=\"CanAutoCraft\" Type=\"Int64\" />\n          <Property Name=\"CanHq\" Type=\"Int64\" />\n          <Property Name=\"CraftItemId\" Type=\"Int64\" />\n          <Property Name=\"CraftNum\" Type=\"Int64\" />\n          <Property Name=\"CraftType\" Type=\"Int64\" />\n          <Property Name=\"Level\" Type=\"Int64\" />\n          <Property Name=\"levelView\" Type=\"Int64\" />\n          <Property Name=\"levelDiff\" Type=\"Int64\" />\n          <Property Name=\"Element\" Type=\"Int64\" />\n          <Property Name=\"NeedCraftmanship\" Type=\"Int64\" />\n          <Property Name=\"NeedControl\" Type=\"Int64\" />\n          <Property Name=\"Number\" Type=\"Int64\" />\n          <Property Name=\"NeedStatus\" Type=\"Int64\" />\n          <Property Name=\"NeedEquipItem\" Type=\"Int64\" />\n          <Property Name=\"data\" Type=\"Binary\" MaxLength=\"2147483647\" FixedLength=\"false\" />\n          <Property Name=\"path\" Type=\"String\" MaxLength=\"2147483647\" FixedLength=\"false\" Unicode=\"true\" />\n          <Property Name=\"Index_ja\" Type=\"String\" MaxLength=\"2147483647\" FixedLength=\"false\" Unicode=\"true\" />\n          <Property Name=\"Index_en\" Type=\"String\" MaxLength=\"2147483647\" FixedLength=\"false\" Unicode=\"true\" />\n          <Property Name=\"Index_fr\" Type=\"String\" MaxLength=\"2147483647\" FixedLength=\"false\" Unicode=\"true\" />\n          <Property Name=\"Index_de\" Type=\"String\" MaxLength=\"2147483647\" FixedLength=\"false\" Unicode=\"true\" />\n        </EntityType>\n        <EntityType Name=\"RecipeElement\">\n          <Key>\n            <PropertyRef Name=\"Key\" />\n          </Key>\n          <Property Name=\"Key\" Type=\"Int64\" Nullable=\"false\" />\n          <Property Name=\"Name_ja\" Type=\"String\" MaxLength=\"255\" FixedLength=\"false\" Unicode=\"false\" />\n          <Property Name=\"Name_en\" Type=\"String\" MaxLength=\"255\" FixedLength=\"false\" Unicode=\"false\" />\n          <Property Name=\"Name_fr\" Type=\"String\" MaxLength=\"255\" FixedLength=\"false\" Unicode=\"false\" />\n          <Property Name=\"Name_de\" Type=\"String\" MaxLength=\"255\" FixedLength=\"false\" Unicode=\"false\" />\n        </EntityType>\n        <EntityType Name=\"Shop\">\n          <Key>\n            <PropertyRef Name=\"Key\" />\n          </Key>\n          <Property Name=\"Key\" Type=\"Int64\" Nullable=\"false\" />\n          <Property Name=\"Name_ja\" Type=\"String\" MaxLength=\"255\" FixedLength=\"false\" Unicode=\"false\" />\n          <Property Name=\"Name_en\" Type=\"String\" MaxLength=\"255\" FixedLength=\"false\" Unicode=\"false\" />\n          <Property Name=\"Name_fr\" Type=\"String\" MaxLength=\"255\" FixedLength=\"false\" Unicode=\"false\" />\n          <Property Name=\"Name_de\" Type=\"String\" MaxLength=\"255\" FixedLength=\"false\" Unicode=\"false\" />\n          <Property Name=\"BeastTribe\" Type=\"Int64\" />\n        </EntityType>\n        <EntityType Name=\"Status\">\n          <Key>\n            <PropertyRef Name=\"Key\" />\n          </Key>\n          <Property Name=\"Key\" Type=\"Int64\" Nullable=\"false\" />\n          <Property Name=\"Name_ja\" Type=\"String\" MaxLength=\"255\" FixedLength=\"false\" Unicode=\"false\" />\n          <Property Name=\"Name_en\" Type=\"String\" MaxLength=\"255\" FixedLength=\"false\" Unicode=\"false\" />\n          <Property Name=\"Name_fr\" Type=\"String\" MaxLength=\"255\" FixedLength=\"false\" Unicode=\"false\" />\n          <Property Name=\"Name_de\" Type=\"String\" MaxLength=\"255\" FixedLength=\"false\" Unicode=\"false\" />\n        </EntityType>\n        <EntityType Name=\"Title\">\n          <Key>\n            <PropertyRef Name=\"Key\" />\n          </Key>\n          <Property Name=\"Key\" Type=\"Int64\" Nullable=\"false\" />\n          <Property Name=\"Female_de\" Type=\"String\" MaxLength=\"255\" FixedLength=\"false\" Unicode=\"false\" />\n          <Property Name=\"Female_en\" Type=\"String\" MaxLength=\"255\" FixedLength=\"false\" Unicode=\"false\" />\n          <Property Name=\"Female_fr\" Type=\"String\" MaxLength=\"255\" FixedLength=\"false\" Unicode=\"false\" />\n          <Property Name=\"Female_ja\" Type=\"String\" MaxLength=\"255\" FixedLength=\"false\" Unicode=\"false\" />\n          <Property Name=\"Front_de\" Type=\"Boolean\" />\n          <Property Name=\"Front_en\" Type=\"Boolean\" />\n          <Property Name=\"Front_fr\" Type=\"Boolean\" />\n          <Property Name=\"Front_ja\" Type=\"Boolean\" />\n          <Property Name=\"Male_de\" Type=\"String\" MaxLength=\"255\" FixedLength=\"false\" Unicode=\"false\" />\n          <Property Name=\"Male_en\" Type=\"String\" MaxLength=\"255\" FixedLength=\"false\" Unicode=\"false\" />\n          <Property Name=\"Male_fr\" Type=\"String\" MaxLength=\"255\" FixedLength=\"false\" Unicode=\"false\" />\n          <Property Name=\"Male_ja\" Type=\"String\" MaxLength=\"255\" FixedLength=\"false\" Unicode=\"false\" />\n        </EntityType>\n        <EntityType Name=\"Tomestone\">\n          <Key>\n            <PropertyRef Name=\"Key\" />\n          </Key>\n          <Property Name=\"Key\" Type=\"Int64\" Nullable=\"false\" />\n          <Property Name=\"CatalogId\" Type=\"Int64\" />\n          <Property Name=\"path\" Type=\"String\" MaxLength=\"2147483647\" FixedLength=\"false\" Unicode=\"true\" />\n        </EntityType>\n        <EntityType Name=\"Town\">\n          <Key>\n            <PropertyRef Name=\"Key\" />\n          </Key>\n          <Property Name=\"Key\" Type=\"Int64\" Nullable=\"false\" />\n          <Property Name=\"Name_ja\" Type=\"String\" MaxLength=\"255\" FixedLength=\"false\" Unicode=\"false\" />\n          <Property Name=\"Name_en\" Type=\"String\" MaxLength=\"255\" FixedLength=\"false\" Unicode=\"false\" />\n          <Property Name=\"Name_fr\" Type=\"String\" MaxLength=\"255\" FixedLength=\"false\" Unicode=\"false\" />\n          <Property Name=\"Name_de\" Type=\"String\" MaxLength=\"255\" FixedLength=\"false\" Unicode=\"false\" />\n        </EntityType>\n        <EntityContainer Name=\"Entities\" annotation:LazyLoadingEnabled=\"true\">\n          <EntitySet Name=\"Achievements\" EntityType=\"Self.Achievement\" />\n          <EntitySet Name=\"AchievementCategories\" EntityType=\"Self.AchievementCategory\" />\n          <EntitySet Name=\"AchievementKinds\" EntityType=\"Self.AchievementKind\" />\n          <EntitySet Name=\"Actions\" EntityType=\"Self.Action\" />\n          <EntitySet Name=\"BaseParams\" EntityType=\"Self.BaseParam\" />\n          <EntitySet Name=\"BeastTribes\" EntityType=\"Self.BeastTribe\" />\n          <EntitySet Name=\"BNpcNames\" EntityType=\"Self.BNpcName\" />\n          <EntitySet Name=\"BNpcName_PlaceName\" EntityType=\"Self.BNpcName_PlaceName\" />\n          <EntitySet Name=\"ClassJobs\" EntityType=\"Self.ClassJob\" />\n          <EntitySet Name=\"ClassJob_ClassJobCategory\" EntityType=\"Self.ClassJob_ClassJobCategory\" />\n          <EntitySet Name=\"ClassJobCategories\" EntityType=\"Self.ClassJobCategory\" />\n          <EntitySet Name=\"Colosseums\" EntityType=\"Self.Colosseum\" />\n          <EntitySet Name=\"ContentRoulettes\" EntityType=\"Self.ContentRoulette\" />\n          <EntitySet Name=\"ContentTypes\" EntityType=\"Self.ContentType\" />\n          <EntitySet Name=\"CraftTypes\" EntityType=\"Self.CraftType\" />\n          <EntitySet Name=\"Emotes\" EntityType=\"Self.Emote\" />\n          <EntitySet Name=\"ENpcResidents\" EntityType=\"Self.ENpcResident\" />\n          <EntitySet Name=\"ENpcResident_PlaceName\" EntityType=\"Self.ENpcResident_PlaceName\" />\n          <EntitySet Name=\"ENpcResident_Quest\" EntityType=\"Self.ENpcResident_Quest\" />\n          <EntitySet Name=\"FCHierarchies\" EntityType=\"Self.FCHierarchy\" />\n          <EntitySet Name=\"FCRanks\" EntityType=\"Self.FCRank\" />\n          <EntitySet Name=\"FCReputations\" EntityType=\"Self.FCReputation\" />\n          <EntitySet Name=\"Frontline01\" EntityType=\"Self.Frontline01\" />\n          <EntitySet Name=\"Gatherings\" EntityType=\"Self.Gathering\" />\n          <EntitySet Name=\"GatheringTypes\" EntityType=\"Self.GatheringType\" />\n          <EntitySet Name=\"GCRankGridaniaFemaleTexts\" EntityType=\"Self.GCRankGridaniaFemaleText\" />\n          <EntitySet Name=\"GCRankGridaniaMaleTexts\" EntityType=\"Self.GCRankGridaniaMaleText\" />\n          <EntitySet Name=\"GCRankLimsaFemaleTexts\" EntityType=\"Self.GCRankLimsaFemaleText\" />\n          <EntitySet Name=\"GCRankLimsaMaleTexts\" EntityType=\"Self.GCRankLimsaMaleText\" />\n          <EntitySet Name=\"GCRankUldahFemaleTexts\" EntityType=\"Self.GCRankUldahFemaleText\" />\n          <EntitySet Name=\"GCRankUldahMaleTexts\" EntityType=\"Self.GCRankUldahMaleText\" />\n          <EntitySet Name=\"GeneralActions\" EntityType=\"Self.GeneralAction\" />\n          <EntitySet Name=\"GrandCompanies\" EntityType=\"Self.GrandCompany\" />\n          <EntitySet Name=\"GuardianDeities\" EntityType=\"Self.GuardianDeity\" />\n          <EntitySet Name=\"GuildOrders\" EntityType=\"Self.GuildOrder\" />\n          <EntitySet Name=\"InstanceContents\" EntityType=\"Self.InstanceContent\" />\n          <EntitySet Name=\"InstanceContentTypes\" EntityType=\"Self.InstanceContentType\" />\n          <EntitySet Name=\"Items\" EntityType=\"Self.Item\" />\n          <EntitySet Name=\"Item_ClassJob\" EntityType=\"Self.Item_ClassJob\" />\n          <EntitySet Name=\"ItemCategories\" EntityType=\"Self.ItemCategory\" />\n          <EntitySet Name=\"ItemSeries\" EntityType=\"Self.ItemSery\" />\n          <EntitySet Name=\"ItemSpecialBonus\" EntityType=\"Self.ItemSpecialBonu\" />\n          <EntitySet Name=\"ItemUICategories\" EntityType=\"Self.ItemUICategory\" />\n          <EntitySet Name=\"ItemUIKinds\" EntityType=\"Self.ItemUIKind\" />\n          <EntitySet Name=\"JournalCategories\" EntityType=\"Self.JournalCategory\" />\n          <EntitySet Name=\"JournalGenres\" EntityType=\"Self.JournalGenre\" />\n          <EntitySet Name=\"LodestoneSystemDefines\" EntityType=\"Self.LodestoneSystemDefine\" />\n          <EntitySet Name=\"NotebookDivisions\" EntityType=\"Self.NotebookDivision\" />\n          <EntitySet Name=\"PlaceNames\" EntityType=\"Self.PlaceName\" />\n          <EntitySet Name=\"Quests\" EntityType=\"Self.Quest\" />\n          <EntitySet Name=\"Quest_ClassJob\" EntityType=\"Self.Quest_ClassJob\" />\n          <EntitySet Name=\"QuestWebExes\" EntityType=\"Self.QuestWebEx\" />\n          <EntitySet Name=\"QuestWebTypes\" EntityType=\"Self.QuestWebType\" />\n          <EntitySet Name=\"Races\" EntityType=\"Self.Race\" />\n          <EntitySet Name=\"Recipes\" EntityType=\"Self.Recipe\" />\n          <EntitySet Name=\"RecipeElements\" EntityType=\"Self.RecipeElement\" />\n          <EntitySet Name=\"Shops\" EntityType=\"Self.Shop\" />\n          <EntitySet Name=\"Status\" EntityType=\"Self.Status\" />\n          <EntitySet Name=\"Titles\" EntityType=\"Self.Title\" />\n          <EntitySet Name=\"Tomestones\" EntityType=\"Self.Tomestone\" />\n          <EntitySet Name=\"Towns\" EntityType=\"Self.Town\" />\n        </EntityContainer>\n      </Schema>\n    </edmx:ConceptualModels>\n    <!-- C-S mapping content -->\n    <edmx:Mappings>\n      <Mapping Space=\"C-S\" xmlns=\"http://schemas.microsoft.com/ado/2009/11/mapping/cs\">\n        <EntityContainerMapping StorageEntityContainer=\"LibraModelStoreContainer\" CdmEntityContainer=\"Entities\">\n          <EntitySetMapping Name=\"Achievements\">\n            <EntityTypeMapping TypeName=\"LibraModel.Achievement\">\n              <MappingFragment StoreEntitySet=\"Achievement\">\n                <ScalarProperty Name=\"Key\" ColumnName=\"Key\" />\n                <ScalarProperty Name=\"Category\" ColumnName=\"Category\" />\n                <ScalarProperty Name=\"Name_ja\" ColumnName=\"Name_ja\" />\n                <ScalarProperty Name=\"Name_en\" ColumnName=\"Name_en\" />\n                <ScalarProperty Name=\"Name_fr\" ColumnName=\"Name_fr\" />\n                <ScalarProperty Name=\"Name_de\" ColumnName=\"Name_de\" />\n                <ScalarProperty Name=\"Help_ja\" ColumnName=\"Help_ja\" />\n                <ScalarProperty Name=\"Help_en\" ColumnName=\"Help_en\" />\n                <ScalarProperty Name=\"Help_fr\" ColumnName=\"Help_fr\" />\n                <ScalarProperty Name=\"Help_de\" ColumnName=\"Help_de\" />\n                <ScalarProperty Name=\"Point\" ColumnName=\"Point\" />\n                <ScalarProperty Name=\"Item\" ColumnName=\"Item\" />\n                <ScalarProperty Name=\"Icon\" ColumnName=\"Icon\" />\n                <ScalarProperty Name=\"Title\" ColumnName=\"Title\" />\n                <ScalarProperty Name=\"Priority\" ColumnName=\"Priority\" />\n                <ScalarProperty Name=\"data\" ColumnName=\"data\" />\n                <ScalarProperty Name=\"path\" ColumnName=\"path\" />\n                <ScalarProperty Name=\"Index_ja\" ColumnName=\"Index_ja\" />\n                <ScalarProperty Name=\"Index_en\" ColumnName=\"Index_en\" />\n                <ScalarProperty Name=\"Index_fr\" ColumnName=\"Index_fr\" />\n                <ScalarProperty Name=\"Index_de\" ColumnName=\"Index_de\" />\n              </MappingFragment>\n            </EntityTypeMapping>\n          </EntitySetMapping>\n          <EntitySetMapping Name=\"AchievementCategories\">\n            <EntityTypeMapping TypeName=\"LibraModel.AchievementCategory\">\n              <MappingFragment StoreEntitySet=\"AchievementCategory\">\n                <ScalarProperty Name=\"Key\" ColumnName=\"Key\" />\n                <ScalarProperty Name=\"Kind\" ColumnName=\"Kind\" />\n                <ScalarProperty Name=\"Name_ja\" ColumnName=\"Name_ja\" />\n                <ScalarProperty Name=\"Name_en\" ColumnName=\"Name_en\" />\n                <ScalarProperty Name=\"Name_fr\" ColumnName=\"Name_fr\" />\n                <ScalarProperty Name=\"Name_de\" ColumnName=\"Name_de\" />\n              </MappingFragment>\n            </EntityTypeMapping>\n          </EntitySetMapping>\n          <EntitySetMapping Name=\"AchievementKinds\">\n            <EntityTypeMapping TypeName=\"LibraModel.AchievementKind\">\n              <MappingFragment StoreEntitySet=\"AchievementKind\">\n                <ScalarProperty Name=\"Key\" ColumnName=\"Key\" />\n                <ScalarProperty Name=\"Name_ja\" ColumnName=\"Name_ja\" />\n                <ScalarProperty Name=\"Name_en\" ColumnName=\"Name_en\" />\n                <ScalarProperty Name=\"Name_fr\" ColumnName=\"Name_fr\" />\n                <ScalarProperty Name=\"Name_de\" ColumnName=\"Name_de\" />\n              </MappingFragment>\n            </EntityTypeMapping>\n          </EntitySetMapping>\n          <EntitySetMapping Name=\"Actions\">\n            <EntityTypeMapping TypeName=\"LibraModel.Action\">\n              <MappingFragment StoreEntitySet=\"Action\">\n                <ScalarProperty Name=\"Key\" ColumnName=\"Key\" />\n                <ScalarProperty Name=\"Icon\" ColumnName=\"Icon\" />\n                <ScalarProperty Name=\"Name_ja\" ColumnName=\"Name_ja\" />\n                <ScalarProperty Name=\"Name_en\" ColumnName=\"Name_en\" />\n                <ScalarProperty Name=\"Name_fr\" ColumnName=\"Name_fr\" />\n                <ScalarProperty Name=\"Name_de\" ColumnName=\"Name_de\" />\n                <ScalarProperty Name=\"HelpWeb_ja\" ColumnName=\"HelpWeb_ja\" />\n                <ScalarProperty Name=\"HelpWeb_en\" ColumnName=\"HelpWeb_en\" />\n                <ScalarProperty Name=\"HelpWeb_fr\" ColumnName=\"HelpWeb_fr\" />\n                <ScalarProperty Name=\"HelpWeb_de\" ColumnName=\"HelpWeb_de\" />\n              </MappingFragment>\n            </EntityTypeMapping>\n          </EntitySetMapping>\n          <EntitySetMapping Name=\"BaseParams\">\n            <EntityTypeMapping TypeName=\"LibraModel.BaseParam\">\n              <MappingFragment StoreEntitySet=\"BaseParam\">\n                <ScalarProperty Name=\"Key\" ColumnName=\"Key\" />\n                <ScalarProperty Name=\"Name_ja\" ColumnName=\"Name_ja\" />\n                <ScalarProperty Name=\"Name_en\" ColumnName=\"Name_en\" />\n                <ScalarProperty Name=\"Name_fr\" ColumnName=\"Name_fr\" />\n                <ScalarProperty Name=\"Name_de\" ColumnName=\"Name_de\" />\n              </MappingFragment>\n            </EntityTypeMapping>\n          </EntitySetMapping>\n          <EntitySetMapping Name=\"BeastTribes\">\n            <EntityTypeMapping TypeName=\"LibraModel.BeastTribe\">\n              <MappingFragment StoreEntitySet=\"BeastTribe\">\n                <ScalarProperty Name=\"Key\" ColumnName=\"Key\" />\n                <ScalarProperty Name=\"SGL_ja\" ColumnName=\"SGL_ja\" />\n                <ScalarProperty Name=\"SGL_en\" ColumnName=\"SGL_en\" />\n                <ScalarProperty Name=\"SGL_fr\" ColumnName=\"SGL_fr\" />\n                <ScalarProperty Name=\"SGL_de\" ColumnName=\"SGL_de\" />\n                <ScalarProperty Name=\"ReputationValue_ja\" ColumnName=\"ReputationValue_ja\" />\n                <ScalarProperty Name=\"ReputationValue_en\" ColumnName=\"ReputationValue_en\" />\n                <ScalarProperty Name=\"ReputationValue_fr\" ColumnName=\"ReputationValue_fr\" />\n                <ScalarProperty Name=\"ReputationValue_de\" ColumnName=\"ReputationValue_de\" />\n              </MappingFragment>\n            </EntityTypeMapping>\n          </EntitySetMapping>\n          <EntitySetMapping Name=\"BNpcNames\">\n            <EntityTypeMapping TypeName=\"LibraModel.BNpcName\">\n              <MappingFragment StoreEntitySet=\"BNpcName\">\n                <ScalarProperty Name=\"Key\" ColumnName=\"Key\" />\n                <ScalarProperty Name=\"SGL_ja\" ColumnName=\"SGL_ja\" />\n                <ScalarProperty Name=\"SGL_en\" ColumnName=\"SGL_en\" />\n                <ScalarProperty Name=\"SGL_fr\" ColumnName=\"SGL_fr\" />\n                <ScalarProperty Name=\"SGL_de\" ColumnName=\"SGL_de\" />\n                <ScalarProperty Name=\"area\" ColumnName=\"area\" />\n                <ScalarProperty Name=\"data\" ColumnName=\"data\" />\n                <ScalarProperty Name=\"path\" ColumnName=\"path\" />\n                <ScalarProperty Name=\"Index_ja\" ColumnName=\"Index_ja\" />\n                <ScalarProperty Name=\"Index_en\" ColumnName=\"Index_en\" />\n                <ScalarProperty Name=\"Index_fr\" ColumnName=\"Index_fr\" />\n                <ScalarProperty Name=\"Index_de\" ColumnName=\"Index_de\" />\n              </MappingFragment>\n            </EntityTypeMapping>\n          </EntitySetMapping>\n          <EntitySetMapping Name=\"BNpcName_PlaceName\">\n            <EntityTypeMapping TypeName=\"LibraModel.BNpcName_PlaceName\">\n              <MappingFragment StoreEntitySet=\"BNpcName_PlaceName\">\n                <ScalarProperty Name=\"BNpcName_Key\" ColumnName=\"BNpcName_Key\" />\n                <ScalarProperty Name=\"PlaceName_Key\" ColumnName=\"PlaceName_Key\" />\n                <ScalarProperty Name=\"region\" ColumnName=\"region\" />\n              </MappingFragment>\n            </EntityTypeMapping>\n          </EntitySetMapping>\n          <EntitySetMapping Name=\"ClassJobs\">\n            <EntityTypeMapping TypeName=\"LibraModel.ClassJob\">\n              <MappingFragment StoreEntitySet=\"ClassJob\">\n                <ScalarProperty Name=\"Key\" ColumnName=\"Key\" />\n                <ScalarProperty Name=\"Label\" ColumnName=\"Label\" />\n                <ScalarProperty Name=\"Name_ja\" ColumnName=\"Name_ja\" />\n                <ScalarProperty Name=\"Name_en\" ColumnName=\"Name_en\" />\n                <ScalarProperty Name=\"Name_fr\" ColumnName=\"Name_fr\" />\n                <ScalarProperty Name=\"Name_de\" ColumnName=\"Name_de\" />\n                <ScalarProperty Name=\"Abbreviation_ja\" ColumnName=\"Abbreviation_ja\" />\n                <ScalarProperty Name=\"Abbreviation_en\" ColumnName=\"Abbreviation_en\" />\n                <ScalarProperty Name=\"Abbreviation_fr\" ColumnName=\"Abbreviation_fr\" />\n                <ScalarProperty Name=\"Abbreviation_de\" ColumnName=\"Abbreviation_de\" />\n                <ScalarProperty Name=\"IsJob\" ColumnName=\"IsJob\" />\n                <ScalarProperty Name=\"UIPriority\" ColumnName=\"UIPriority\" />\n                <ScalarProperty Name=\"filebase\" ColumnName=\"filebase\" />\n              </MappingFragment>\n            </EntityTypeMapping>\n          </EntitySetMapping>\n          <EntitySetMapping Name=\"ClassJob_ClassJobCategory\">\n            <EntityTypeMapping TypeName=\"LibraModel.ClassJob_ClassJobCategory\">\n              <MappingFragment StoreEntitySet=\"ClassJob_ClassJobCategory\">\n                <ScalarProperty Name=\"ClassJob_Key\" ColumnName=\"ClassJob_Key\" />\n                <ScalarProperty Name=\"ClassJobCategory_Key\" ColumnName=\"ClassJobCategory_Key\" />\n              </MappingFragment>\n            </EntityTypeMapping>\n          </EntitySetMapping>\n          <EntitySetMapping Name=\"ClassJobCategories\">\n            <EntityTypeMapping TypeName=\"LibraModel.ClassJobCategory\">\n              <MappingFragment StoreEntitySet=\"ClassJobCategory\">\n                <ScalarProperty Name=\"Key\" ColumnName=\"Key\" />\n                <ScalarProperty Name=\"Name_ja\" ColumnName=\"Name_ja\" />\n                <ScalarProperty Name=\"Name_en\" ColumnName=\"Name_en\" />\n                <ScalarProperty Name=\"Name_fr\" ColumnName=\"Name_fr\" />\n                <ScalarProperty Name=\"Name_de\" ColumnName=\"Name_de\" />\n                <ScalarProperty Name=\"classjob\" ColumnName=\"classjob\" />\n              </MappingFragment>\n            </EntityTypeMapping>\n          </EntitySetMapping>\n          <EntitySetMapping Name=\"Colosseums\">\n            <EntityTypeMapping TypeName=\"LibraModel.Colosseum\">\n              <MappingFragment StoreEntitySet=\"Colosseum\">\n                <ScalarProperty Name=\"Key\" ColumnName=\"Key\" />\n                <ScalarProperty Name=\"WinExp\" ColumnName=\"WinExp\" />\n                <ScalarProperty Name=\"WinPoint\" ColumnName=\"WinPoint\" />\n                <ScalarProperty Name=\"WinToken\" ColumnName=\"WinToken\" />\n                <ScalarProperty Name=\"LosePoint\" ColumnName=\"LosePoint\" />\n                <ScalarProperty Name=\"LoseToken\" ColumnName=\"LoseToken\" />\n              </MappingFragment>\n            </EntityTypeMapping>\n          </EntitySetMapping>\n          <EntitySetMapping Name=\"ContentRoulettes\">\n            <EntityTypeMapping TypeName=\"LibraModel.ContentRoulette\">\n              <MappingFragment StoreEntitySet=\"ContentRoulette\">\n                <ScalarProperty Name=\"Key\" ColumnName=\"Key\" />\n                <ScalarProperty Name=\"Name_ja\" ColumnName=\"Name_ja\" />\n                <ScalarProperty Name=\"Name_en\" ColumnName=\"Name_en\" />\n                <ScalarProperty Name=\"Name_fr\" ColumnName=\"Name_fr\" />\n                <ScalarProperty Name=\"Name_de\" ColumnName=\"Name_de\" />\n              </MappingFragment>\n            </EntityTypeMapping>\n          </EntitySetMapping>\n          <EntitySetMapping Name=\"ContentTypes\">\n            <EntityTypeMapping TypeName=\"LibraModel.ContentType\">\n              <MappingFragment StoreEntitySet=\"ContentType\">\n                <ScalarProperty Name=\"Key\" ColumnName=\"Key\" />\n                <ScalarProperty Name=\"Name_ja\" ColumnName=\"Name_ja\" />\n                <ScalarProperty Name=\"Name_en\" ColumnName=\"Name_en\" />\n                <ScalarProperty Name=\"Name_fr\" ColumnName=\"Name_fr\" />\n                <ScalarProperty Name=\"Name_de\" ColumnName=\"Name_de\" />\n              </MappingFragment>\n            </EntityTypeMapping>\n          </EntitySetMapping>\n          <EntitySetMapping Name=\"CraftTypes\">\n            <EntityTypeMapping TypeName=\"LibraModel.CraftType\">\n              <MappingFragment StoreEntitySet=\"CraftType\">\n                <ScalarProperty Name=\"Key\" ColumnName=\"Key\" />\n                <ScalarProperty Name=\"ClassJob\" ColumnName=\"ClassJob\" />\n              </MappingFragment>\n            </EntityTypeMapping>\n          </EntitySetMapping>\n          <EntitySetMapping Name=\"Emotes\">\n            <EntityTypeMapping TypeName=\"LibraModel.Emote\">\n              <MappingFragment StoreEntitySet=\"Emote\">\n                <ScalarProperty Name=\"Key\" ColumnName=\"Key\" />\n                <ScalarProperty Name=\"Icon\" ColumnName=\"Icon\" />\n                <ScalarProperty Name=\"Name_ja\" ColumnName=\"Name_ja\" />\n                <ScalarProperty Name=\"Name_en\" ColumnName=\"Name_en\" />\n                <ScalarProperty Name=\"Name_fr\" ColumnName=\"Name_fr\" />\n                <ScalarProperty Name=\"Name_de\" ColumnName=\"Name_de\" />\n              </MappingFragment>\n            </EntityTypeMapping>\n          </EntitySetMapping>\n          <EntitySetMapping Name=\"ENpcResidents\">\n            <EntityTypeMapping TypeName=\"LibraModel.ENpcResident\">\n              <MappingFragment StoreEntitySet=\"ENpcResident\">\n                <ScalarProperty Name=\"Key\" ColumnName=\"Key\" />\n                <ScalarProperty Name=\"SGL_ja\" ColumnName=\"SGL_ja\" />\n                <ScalarProperty Name=\"SGL_en\" ColumnName=\"SGL_en\" />\n                <ScalarProperty Name=\"SGL_fr\" ColumnName=\"SGL_fr\" />\n                <ScalarProperty Name=\"SGL_de\" ColumnName=\"SGL_de\" />\n                <ScalarProperty Name=\"area\" ColumnName=\"area\" />\n                <ScalarProperty Name=\"data\" ColumnName=\"data\" />\n                <ScalarProperty Name=\"path\" ColumnName=\"path\" />\n                <ScalarProperty Name=\"has_shop\" ColumnName=\"has_shop\" />\n                <ScalarProperty Name=\"has_quest\" ColumnName=\"has_quest\" />\n                <ScalarProperty Name=\"has_condition_shop\" ColumnName=\"has_condition_shop\" />\n                <ScalarProperty Name=\"Index_ja\" ColumnName=\"Index_ja\" />\n                <ScalarProperty Name=\"Index_en\" ColumnName=\"Index_en\" />\n                <ScalarProperty Name=\"Index_fr\" ColumnName=\"Index_fr\" />\n                <ScalarProperty Name=\"Index_de\" ColumnName=\"Index_de\" />\n              </MappingFragment>\n            </EntityTypeMapping>\n          </EntitySetMapping>\n          <EntitySetMapping Name=\"ENpcResident_PlaceName\">\n            <EntityTypeMapping TypeName=\"LibraModel.ENpcResident_PlaceName\">\n              <MappingFragment StoreEntitySet=\"ENpcResident_PlaceName\">\n                <ScalarProperty Name=\"ENpcResident_Key\" ColumnName=\"ENpcResident_Key\" />\n                <ScalarProperty Name=\"PlaceName_Key\" ColumnName=\"PlaceName_Key\" />\n                <ScalarProperty Name=\"region\" ColumnName=\"region\" />\n              </MappingFragment>\n            </EntityTypeMapping>\n          </EntitySetMapping>\n          <EntitySetMapping Name=\"ENpcResident_Quest\">\n            <EntityTypeMapping TypeName=\"LibraModel.ENpcResident_Quest\">\n              <MappingFragment StoreEntitySet=\"ENpcResident_Quest\">\n                <ScalarProperty Name=\"ENpcResident_Key\" ColumnName=\"ENpcResident_Key\" />\n                <ScalarProperty Name=\"Quest_Key\" ColumnName=\"Quest_Key\" />\n              </MappingFragment>\n            </EntityTypeMapping>\n          </EntitySetMapping>\n          <EntitySetMapping Name=\"FCHierarchies\">\n            <EntityTypeMapping TypeName=\"LibraModel.FCHierarchy\">\n              <MappingFragment StoreEntitySet=\"FCHierarchy\">\n                <ScalarProperty Name=\"Key\" ColumnName=\"Key\" />\n                <ScalarProperty Name=\"Name_ja\" ColumnName=\"Name_ja\" />\n                <ScalarProperty Name=\"Name_en\" ColumnName=\"Name_en\" />\n                <ScalarProperty Name=\"Name_fr\" ColumnName=\"Name_fr\" />\n                <ScalarProperty Name=\"Name_de\" ColumnName=\"Name_de\" />\n              </MappingFragment>\n            </EntityTypeMapping>\n          </EntitySetMapping>\n          <EntitySetMapping Name=\"FCRanks\">\n            <EntityTypeMapping TypeName=\"LibraModel.FCRank\">\n              <MappingFragment StoreEntitySet=\"FCRank\">\n                <ScalarProperty Name=\"Key\" ColumnName=\"Key\" />\n                <ScalarProperty Name=\"CurrentPoint\" ColumnName=\"CurrentPoint\" />\n                <ScalarProperty Name=\"NextPoint\" ColumnName=\"NextPoint\" />\n              </MappingFragment>\n            </EntityTypeMapping>\n          </EntitySetMapping>\n          <EntitySetMapping Name=\"FCReputations\">\n            <EntityTypeMapping TypeName=\"LibraModel.FCReputation\">\n              <MappingFragment StoreEntitySet=\"FCReputation\">\n                <ScalarProperty Name=\"Key\" ColumnName=\"Key\" />\n                <ScalarProperty Name=\"Color\" ColumnName=\"Color\" />\n                <ScalarProperty Name=\"CurrentPoint\" ColumnName=\"CurrentPoint\" />\n                <ScalarProperty Name=\"NextPoint\" ColumnName=\"NextPoint\" />\n                <ScalarProperty Name=\"Text_ja\" ColumnName=\"Text_ja\" />\n                <ScalarProperty Name=\"Text_en\" ColumnName=\"Text_en\" />\n                <ScalarProperty Name=\"Text_fr\" ColumnName=\"Text_fr\" />\n                <ScalarProperty Name=\"Text_de\" ColumnName=\"Text_de\" />\n                <ScalarProperty Name=\"data\" ColumnName=\"data\" />\n              </MappingFragment>\n            </EntityTypeMapping>\n          </EntitySetMapping>\n          <EntitySetMapping Name=\"Frontline01\">\n            <EntityTypeMapping TypeName=\"LibraModel.Frontline01\">\n              <MappingFragment StoreEntitySet=\"Frontline01\">\n                <ScalarProperty Name=\"Key\" ColumnName=\"Key\" />\n                <ScalarProperty Name=\"WinPvPExp\" ColumnName=\"WinPvPExp\" />\n                <ScalarProperty Name=\"WinPvPToken\" ColumnName=\"WinPvPToken\" />\n              </MappingFragment>\n            </EntityTypeMapping>\n          </EntitySetMapping>\n          <EntitySetMapping Name=\"Gatherings\">\n            <EntityTypeMapping TypeName=\"LibraModel.Gathering\">\n              <MappingFragment StoreEntitySet=\"Gathering\">\n                <ScalarProperty Name=\"Key\" ColumnName=\"Key\" />\n                <ScalarProperty Name=\"Item\" ColumnName=\"Item\" />\n                <ScalarProperty Name=\"GatheringType\" ColumnName=\"GatheringType\" />\n                <ScalarProperty Name=\"Level\" ColumnName=\"Level\" />\n                <ScalarProperty Name=\"levelView\" ColumnName=\"levelView\" />\n                <ScalarProperty Name=\"levelDiff\" ColumnName=\"levelDiff\" />\n                <ScalarProperty Name=\"is_hidden\" ColumnName=\"is_hidden\" />\n                <ScalarProperty Name=\"data\" ColumnName=\"data\" />\n                <ScalarProperty Name=\"GatheringNotebookList\" ColumnName=\"GatheringNotebookList\" />\n                <ScalarProperty Name=\"GatheringItemNo\" ColumnName=\"GatheringItemNo\" />\n                <ScalarProperty Name=\"path\" ColumnName=\"path\" />\n                <ScalarProperty Name=\"Index_ja\" ColumnName=\"Index_ja\" />\n                <ScalarProperty Name=\"Index_en\" ColumnName=\"Index_en\" />\n                <ScalarProperty Name=\"Index_fr\" ColumnName=\"Index_fr\" />\n                <ScalarProperty Name=\"Index_de\" ColumnName=\"Index_de\" />\n              </MappingFragment>\n            </EntityTypeMapping>\n          </EntitySetMapping>\n          <EntitySetMapping Name=\"GatheringTypes\">\n            <EntityTypeMapping TypeName=\"LibraModel.GatheringType\">\n              <MappingFragment StoreEntitySet=\"GatheringType\">\n                <ScalarProperty Name=\"Key\" ColumnName=\"Key\" />\n                <ScalarProperty Name=\"Text_de\" ColumnName=\"Text_de\" />\n                <ScalarProperty Name=\"Text_en\" ColumnName=\"Text_en\" />\n                <ScalarProperty Name=\"Text_ja\" ColumnName=\"Text_ja\" />\n                <ScalarProperty Name=\"Text_fr\" ColumnName=\"Text_fr\" />\n              </MappingFragment>\n            </EntityTypeMapping>\n          </EntitySetMapping>\n          <EntitySetMapping Name=\"GCRankGridaniaFemaleTexts\">\n            <EntityTypeMapping TypeName=\"LibraModel.GCRankGridaniaFemaleText\">\n              <MappingFragment StoreEntitySet=\"GCRankGridaniaFemaleText\">\n                <ScalarProperty Name=\"Key\" ColumnName=\"Key\" />\n                <ScalarProperty Name=\"SGL_ja\" ColumnName=\"SGL_ja\" />\n                <ScalarProperty Name=\"SGL_en\" ColumnName=\"SGL_en\" />\n                <ScalarProperty Name=\"SGL_fr\" ColumnName=\"SGL_fr\" />\n                <ScalarProperty Name=\"SGL_de\" ColumnName=\"SGL_de\" />\n              </MappingFragment>\n            </EntityTypeMapping>\n          </EntitySetMapping>\n          <EntitySetMapping Name=\"GCRankGridaniaMaleTexts\">\n            <EntityTypeMapping TypeName=\"LibraModel.GCRankGridaniaMaleText\">\n              <MappingFragment StoreEntitySet=\"GCRankGridaniaMaleText\">\n                <ScalarProperty Name=\"Key\" ColumnName=\"Key\" />\n                <ScalarProperty Name=\"SGL_ja\" ColumnName=\"SGL_ja\" />\n                <ScalarProperty Name=\"SGL_en\" ColumnName=\"SGL_en\" />\n                <ScalarProperty Name=\"SGL_fr\" ColumnName=\"SGL_fr\" />\n                <ScalarProperty Name=\"SGL_de\" ColumnName=\"SGL_de\" />\n              </MappingFragment>\n            </EntityTypeMapping>\n          </EntitySetMapping>\n          <EntitySetMapping Name=\"GCRankLimsaFemaleTexts\">\n            <EntityTypeMapping TypeName=\"LibraModel.GCRankLimsaFemaleText\">\n              <MappingFragment StoreEntitySet=\"GCRankLimsaFemaleText\">\n                <ScalarProperty Name=\"Key\" ColumnName=\"Key\" />\n                <ScalarProperty Name=\"SGL_ja\" ColumnName=\"SGL_ja\" />\n                <ScalarProperty Name=\"SGL_en\" ColumnName=\"SGL_en\" />\n                <ScalarProperty Name=\"SGL_fr\" ColumnName=\"SGL_fr\" />\n                <ScalarProperty Name=\"SGL_de\" ColumnName=\"SGL_de\" />\n              </MappingFragment>\n            </EntityTypeMapping>\n          </EntitySetMapping>\n          <EntitySetMapping Name=\"GCRankLimsaMaleTexts\">\n            <EntityTypeMapping TypeName=\"LibraModel.GCRankLimsaMaleText\">\n              <MappingFragment StoreEntitySet=\"GCRankLimsaMaleText\">\n                <ScalarProperty Name=\"Key\" ColumnName=\"Key\" />\n                <ScalarProperty Name=\"SGL_ja\" ColumnName=\"SGL_ja\" />\n                <ScalarProperty Name=\"SGL_en\" ColumnName=\"SGL_en\" />\n                <ScalarProperty Name=\"SGL_fr\" ColumnName=\"SGL_fr\" />\n                <ScalarProperty Name=\"SGL_de\" ColumnName=\"SGL_de\" />\n              </MappingFragment>\n            </EntityTypeMapping>\n          </EntitySetMapping>\n          <EntitySetMapping Name=\"GCRankUldahFemaleTexts\">\n            <EntityTypeMapping TypeName=\"LibraModel.GCRankUldahFemaleText\">\n              <MappingFragment StoreEntitySet=\"GCRankUldahFemaleText\">\n                <ScalarProperty Name=\"Key\" ColumnName=\"Key\" />\n                <ScalarProperty Name=\"SGL_ja\" ColumnName=\"SGL_ja\" />\n                <ScalarProperty Name=\"SGL_en\" ColumnName=\"SGL_en\" />\n                <ScalarProperty Name=\"SGL_fr\" ColumnName=\"SGL_fr\" />\n                <ScalarProperty Name=\"SGL_de\" ColumnName=\"SGL_de\" />\n              </MappingFragment>\n            </EntityTypeMapping>\n          </EntitySetMapping>\n          <EntitySetMapping Name=\"GCRankUldahMaleTexts\">\n            <EntityTypeMapping TypeName=\"LibraModel.GCRankUldahMaleText\">\n              <MappingFragment StoreEntitySet=\"GCRankUldahMaleText\">\n                <ScalarProperty Name=\"Key\" ColumnName=\"Key\" />\n                <ScalarProperty Name=\"SGL_ja\" ColumnName=\"SGL_ja\" />\n                <ScalarProperty Name=\"SGL_en\" ColumnName=\"SGL_en\" />\n                <ScalarProperty Name=\"SGL_fr\" ColumnName=\"SGL_fr\" />\n                <ScalarProperty Name=\"SGL_de\" ColumnName=\"SGL_de\" />\n              </MappingFragment>\n            </EntityTypeMapping>\n          </EntitySetMapping>\n          <EntitySetMapping Name=\"GeneralActions\">\n            <EntityTypeMapping TypeName=\"LibraModel.GeneralAction\">\n              <MappingFragment StoreEntitySet=\"GeneralAction\">\n                <ScalarProperty Name=\"Key\" ColumnName=\"Key\" />\n                <ScalarProperty Name=\"Icon\" ColumnName=\"Icon\" />\n                <ScalarProperty Name=\"Name_ja\" ColumnName=\"Name_ja\" />\n                <ScalarProperty Name=\"Name_en\" ColumnName=\"Name_en\" />\n                <ScalarProperty Name=\"Name_fr\" ColumnName=\"Name_fr\" />\n                <ScalarProperty Name=\"Name_de\" ColumnName=\"Name_de\" />\n              </MappingFragment>\n            </EntityTypeMapping>\n          </EntitySetMapping>\n          <EntitySetMapping Name=\"GrandCompanies\">\n            <EntityTypeMapping TypeName=\"LibraModel.GrandCompany\">\n              <MappingFragment StoreEntitySet=\"GrandCompany\">\n                <ScalarProperty Name=\"Key\" ColumnName=\"Key\" />\n                <ScalarProperty Name=\"SGL_ja\" ColumnName=\"SGL_ja\" />\n                <ScalarProperty Name=\"SGL_en\" ColumnName=\"SGL_en\" />\n                <ScalarProperty Name=\"SGL_fr\" ColumnName=\"SGL_fr\" />\n                <ScalarProperty Name=\"SGL_de\" ColumnName=\"SGL_de\" />\n              </MappingFragment>\n            </EntityTypeMapping>\n          </EntitySetMapping>\n          <EntitySetMapping Name=\"GuardianDeities\">\n            <EntityTypeMapping TypeName=\"LibraModel.GuardianDeity\">\n              <MappingFragment StoreEntitySet=\"GuardianDeity\">\n                <ScalarProperty Name=\"Key\" ColumnName=\"Key\" />\n                <ScalarProperty Name=\"Name_ja\" ColumnName=\"Name_ja\" />\n                <ScalarProperty Name=\"Name_en\" ColumnName=\"Name_en\" />\n                <ScalarProperty Name=\"Name_fr\" ColumnName=\"Name_fr\" />\n                <ScalarProperty Name=\"Name_de\" ColumnName=\"Name_de\" />\n              </MappingFragment>\n            </EntityTypeMapping>\n          </EntitySetMapping>\n          <EntitySetMapping Name=\"GuildOrders\">\n            <EntityTypeMapping TypeName=\"LibraModel.GuildOrder\">\n              <MappingFragment StoreEntitySet=\"GuildOrder\">\n                <ScalarProperty Name=\"Key\" ColumnName=\"Key\" />\n                <ScalarProperty Name=\"Exp00\" ColumnName=\"Exp00\" />\n                <ScalarProperty Name=\"Exp01\" ColumnName=\"Exp01\" />\n                <ScalarProperty Name=\"Gil00\" ColumnName=\"Gil00\" />\n                <ScalarProperty Name=\"Gil01\" ColumnName=\"Gil01\" />\n              </MappingFragment>\n            </EntityTypeMapping>\n          </EntitySetMapping>\n          <EntitySetMapping Name=\"InstanceContents\">\n            <EntityTypeMapping TypeName=\"LibraModel.InstanceContent\">\n              <MappingFragment StoreEntitySet=\"InstanceContent\">\n                <ScalarProperty Name=\"Key\" ColumnName=\"Key\" />\n                <ScalarProperty Name=\"Type\" ColumnName=\"Type\" />\n                <ScalarProperty Name=\"Sortkey\" ColumnName=\"Sortkey\" />\n                <ScalarProperty Name=\"Name_ja\" ColumnName=\"Name_ja\" />\n                <ScalarProperty Name=\"Name_en\" ColumnName=\"Name_en\" />\n                <ScalarProperty Name=\"Name_fr\" ColumnName=\"Name_fr\" />\n                <ScalarProperty Name=\"Name_de\" ColumnName=\"Name_de\" />\n                <ScalarProperty Name=\"Description_ja\" ColumnName=\"Description_ja\" />\n                <ScalarProperty Name=\"Description_en\" ColumnName=\"Description_en\" />\n                <ScalarProperty Name=\"Description_fr\" ColumnName=\"Description_fr\" />\n                <ScalarProperty Name=\"Description_de\" ColumnName=\"Description_de\" />\n                <ScalarProperty Name=\"LevelMin\" ColumnName=\"LevelMin\" />\n                <ScalarProperty Name=\"LevelMax\" ColumnName=\"LevelMax\" />\n                <ScalarProperty Name=\"Time\" ColumnName=\"Time\" />\n                <ScalarProperty Name=\"Halfway\" ColumnName=\"Halfway\" />\n                <ScalarProperty Name=\"RandomContentType\" ColumnName=\"RandomContentType\" />\n                <ScalarProperty Name=\"Alliance\" ColumnName=\"Alliance\" />\n                <ScalarProperty Name=\"FinderPartyCondition\" ColumnName=\"FinderPartyCondition\" />\n                <ScalarProperty Name=\"PartyMemberCount\" ColumnName=\"PartyMemberCount\" />\n                <ScalarProperty Name=\"TankCount\" ColumnName=\"TankCount\" />\n                <ScalarProperty Name=\"HealerCount\" ColumnName=\"HealerCount\" />\n                <ScalarProperty Name=\"AttackerCount\" ColumnName=\"AttackerCount\" />\n                <ScalarProperty Name=\"RangeCount\" ColumnName=\"RangeCount\" />\n                <ScalarProperty Name=\"DifferentiateDPS\" ColumnName=\"DifferentiateDPS\" />\n                <ScalarProperty Name=\"PartyCount\" ColumnName=\"PartyCount\" />\n                <ScalarProperty Name=\"FreeRole\" ColumnName=\"FreeRole\" />\n                <ScalarProperty Name=\"ItemLevel\" ColumnName=\"ItemLevel\" />\n                <ScalarProperty Name=\"ItemLevelMax\" ColumnName=\"ItemLevelMax\" />\n                <ScalarProperty Name=\"Colosseum\" ColumnName=\"Colosseum\" />\n                <ScalarProperty Name=\"Area\" ColumnName=\"Area\" />\n                <ScalarProperty Name=\"ForceCount\" ColumnName=\"ForceCount\" />\n                <ScalarProperty Name=\"data\" ColumnName=\"data\" />\n                <ScalarProperty Name=\"path\" ColumnName=\"path\" />\n                <ScalarProperty Name=\"is_koeru_usually\" ColumnName=\"is_koeru_usually\" />\n                <ScalarProperty Name=\"is_koeru_annihilation\" ColumnName=\"is_koeru_annihilation\" />\n                <ScalarProperty Name=\"Index_ja\" ColumnName=\"Index_ja\" />\n                <ScalarProperty Name=\"Index_en\" ColumnName=\"Index_en\" />\n                <ScalarProperty Name=\"Index_fr\" ColumnName=\"Index_fr\" />\n                <ScalarProperty Name=\"Index_de\" ColumnName=\"Index_de\" />\n              </MappingFragment>\n            </EntityTypeMapping>\n          </EntitySetMapping>\n          <EntitySetMapping Name=\"InstanceContentTypes\">\n            <EntityTypeMapping TypeName=\"LibraModel.InstanceContentType\">\n              <MappingFragment StoreEntitySet=\"InstanceContentType\">\n                <ScalarProperty Name=\"Type\" ColumnName=\"Type\" />\n                <ScalarProperty Name=\"Sortkey\" ColumnName=\"Sortkey\" />\n                <ScalarProperty Name=\"ContentType\" ColumnName=\"ContentType\" />\n              </MappingFragment>\n            </EntityTypeMapping>\n          </EntitySetMapping>\n          <EntitySetMapping Name=\"Items\">\n            <EntityTypeMapping TypeName=\"LibraModel.Item\">\n              <MappingFragment StoreEntitySet=\"Item\">\n                <ScalarProperty Name=\"Key\" ColumnName=\"Key\" />\n                <ScalarProperty Name=\"Category\" ColumnName=\"Category\" />\n                <ScalarProperty Name=\"UICategory\" ColumnName=\"UICategory\" />\n                <ScalarProperty Name=\"UIName_ja\" ColumnName=\"UIName_ja\" />\n                <ScalarProperty Name=\"UIName_en\" ColumnName=\"UIName_en\" />\n                <ScalarProperty Name=\"UIName_fr\" ColumnName=\"UIName_fr\" />\n                <ScalarProperty Name=\"UIName_de\" ColumnName=\"UIName_de\" />\n                <ScalarProperty Name=\"Help_ja\" ColumnName=\"Help_ja\" />\n                <ScalarProperty Name=\"Help_en\" ColumnName=\"Help_en\" />\n                <ScalarProperty Name=\"Help_fr\" ColumnName=\"Help_fr\" />\n                <ScalarProperty Name=\"Help_de\" ColumnName=\"Help_de\" />\n                <ScalarProperty Name=\"Level\" ColumnName=\"Level\" />\n                <ScalarProperty Name=\"EquipLevel\" ColumnName=\"EquipLevel\" />\n                <ScalarProperty Name=\"Rarity\" ColumnName=\"Rarity\" />\n                <ScalarProperty Name=\"HQ\" ColumnName=\"HQ\" />\n                <ScalarProperty Name=\"SpecialBonus\" ColumnName=\"SpecialBonus\" />\n                <ScalarProperty Name=\"Series\" ColumnName=\"Series\" />\n                <ScalarProperty Name=\"Slot\" ColumnName=\"Slot\" />\n                <ScalarProperty Name=\"Damage\" ColumnName=\"Damage\" />\n                <ScalarProperty Name=\"Damage_hq\" ColumnName=\"Damage_hq\" />\n                <ScalarProperty Name=\"MagicDamage\" ColumnName=\"MagicDamage\" />\n                <ScalarProperty Name=\"MagicDamage_hq\" ColumnName=\"MagicDamage_hq\" />\n                <ScalarProperty Name=\"Defense\" ColumnName=\"Defense\" />\n                <ScalarProperty Name=\"Defense_hq\" ColumnName=\"Defense_hq\" />\n                <ScalarProperty Name=\"MagicDefense\" ColumnName=\"MagicDefense\" />\n                <ScalarProperty Name=\"MagicDefense_hq\" ColumnName=\"MagicDefense_hq\" />\n                <ScalarProperty Name=\"ShieldRate\" ColumnName=\"ShieldRate\" />\n                <ScalarProperty Name=\"ShieldRate_hq\" ColumnName=\"ShieldRate_hq\" />\n                <ScalarProperty Name=\"ShieldBlockRate\" ColumnName=\"ShieldBlockRate\" />\n                <ScalarProperty Name=\"ShieldBlockRate_hq\" ColumnName=\"ShieldBlockRate_hq\" />\n                <ScalarProperty Name=\"Price\" ColumnName=\"Price\" />\n                <ScalarProperty Name=\"PriceMin\" ColumnName=\"PriceMin\" />\n                <ScalarProperty Name=\"MirageItem\" ColumnName=\"MirageItem\" />\n                <ScalarProperty Name=\"icon\" ColumnName=\"icon\" />\n                <ScalarProperty Name=\"icon_hq\" ColumnName=\"icon_hq\" />\n                <ScalarProperty Name=\"classjob\" ColumnName=\"classjob\" />\n                <ScalarProperty Name=\"Salvage\" ColumnName=\"Salvage\" />\n                <ScalarProperty Name=\"data\" ColumnName=\"data\" />\n                <ScalarProperty Name=\"legacy\" ColumnName=\"legacy\" />\n                <ScalarProperty Name=\"path\" ColumnName=\"path\" />\n                <ScalarProperty Name=\"Index_ja\" ColumnName=\"Index_ja\" />\n                <ScalarProperty Name=\"Index_en\" ColumnName=\"Index_en\" />\n                <ScalarProperty Name=\"Index_fr\" ColumnName=\"Index_fr\" />\n                <ScalarProperty Name=\"Index_de\" ColumnName=\"Index_de\" />\n                <ScalarProperty Name=\"SortId\" ColumnName=\"SortId\" />\n              </MappingFragment>\n            </EntityTypeMapping>\n          </EntitySetMapping>\n          <EntitySetMapping Name=\"Item_ClassJob\">\n            <EntityTypeMapping TypeName=\"LibraModel.Item_ClassJob\">\n              <MappingFragment StoreEntitySet=\"Item_ClassJob\">\n                <ScalarProperty Name=\"Item_Key\" ColumnName=\"Item_Key\" />\n                <ScalarProperty Name=\"ClassJob_Key\" ColumnName=\"ClassJob_Key\" />\n              </MappingFragment>\n            </EntityTypeMapping>\n          </EntitySetMapping>\n          <EntitySetMapping Name=\"ItemCategories\">\n            <EntityTypeMapping TypeName=\"LibraModel.ItemCategory\">\n              <MappingFragment StoreEntitySet=\"ItemCategory\">\n                <ScalarProperty Name=\"Key\" ColumnName=\"Key\" />\n                <ScalarProperty Name=\"Name_ja\" ColumnName=\"Name_ja\" />\n                <ScalarProperty Name=\"Name_en\" ColumnName=\"Name_en\" />\n                <ScalarProperty Name=\"Name_fr\" ColumnName=\"Name_fr\" />\n                <ScalarProperty Name=\"Name_de\" ColumnName=\"Name_de\" />\n              </MappingFragment>\n            </EntityTypeMapping>\n          </EntitySetMapping>\n          <EntitySetMapping Name=\"ItemSeries\">\n            <EntityTypeMapping TypeName=\"LibraModel.ItemSery\">\n              <MappingFragment StoreEntitySet=\"ItemSeries\">\n                <ScalarProperty Name=\"Key\" ColumnName=\"Key\" />\n                <ScalarProperty Name=\"Name_ja\" ColumnName=\"Name_ja\" />\n                <ScalarProperty Name=\"Name_en\" ColumnName=\"Name_en\" />\n                <ScalarProperty Name=\"Name_fr\" ColumnName=\"Name_fr\" />\n                <ScalarProperty Name=\"Name_de\" ColumnName=\"Name_de\" />\n              </MappingFragment>\n            </EntityTypeMapping>\n          </EntitySetMapping>\n          <EntitySetMapping Name=\"ItemSpecialBonus\">\n            <EntityTypeMapping TypeName=\"LibraModel.ItemSpecialBonu\">\n              <MappingFragment StoreEntitySet=\"ItemSpecialBonus\">\n                <ScalarProperty Name=\"Key\" ColumnName=\"Key\" />\n                <ScalarProperty Name=\"Name_ja\" ColumnName=\"Name_ja\" />\n                <ScalarProperty Name=\"Name_en\" ColumnName=\"Name_en\" />\n                <ScalarProperty Name=\"Name_fr\" ColumnName=\"Name_fr\" />\n                <ScalarProperty Name=\"Name_de\" ColumnName=\"Name_de\" />\n              </MappingFragment>\n            </EntityTypeMapping>\n          </EntitySetMapping>\n          <EntitySetMapping Name=\"ItemUICategories\">\n            <EntityTypeMapping TypeName=\"LibraModel.ItemUICategory\">\n              <MappingFragment StoreEntitySet=\"ItemUICategory\">\n                <ScalarProperty Name=\"Key\" ColumnName=\"Key\" />\n                <ScalarProperty Name=\"Kind\" ColumnName=\"Kind\" />\n                <ScalarProperty Name=\"Name_ja\" ColumnName=\"Name_ja\" />\n                <ScalarProperty Name=\"Name_en\" ColumnName=\"Name_en\" />\n                <ScalarProperty Name=\"Name_fr\" ColumnName=\"Name_fr\" />\n                <ScalarProperty Name=\"Name_de\" ColumnName=\"Name_de\" />\n                <ScalarProperty Name=\"Priority\" ColumnName=\"Priority\" />\n              </MappingFragment>\n            </EntityTypeMapping>\n          </EntitySetMapping>\n          <EntitySetMapping Name=\"ItemUIKinds\">\n            <EntityTypeMapping TypeName=\"LibraModel.ItemUIKind\">\n              <MappingFragment StoreEntitySet=\"ItemUIKind\">\n                <ScalarProperty Name=\"Key\" ColumnName=\"Key\" />\n                <ScalarProperty Name=\"Name_ja\" ColumnName=\"Name_ja\" />\n                <ScalarProperty Name=\"Name_en\" ColumnName=\"Name_en\" />\n                <ScalarProperty Name=\"Name_fr\" ColumnName=\"Name_fr\" />\n                <ScalarProperty Name=\"Name_de\" ColumnName=\"Name_de\" />\n              </MappingFragment>\n            </EntityTypeMapping>\n          </EntitySetMapping>\n          <EntitySetMapping Name=\"JournalCategories\">\n            <EntityTypeMapping TypeName=\"LibraModel.JournalCategory\">\n              <MappingFragment StoreEntitySet=\"JournalCategory\">\n                <ScalarProperty Name=\"Key\" ColumnName=\"Key\" />\n                <ScalarProperty Name=\"label\" ColumnName=\"label\" />\n                <ScalarProperty Name=\"Name_ja\" ColumnName=\"Name_ja\" />\n                <ScalarProperty Name=\"Name_en\" ColumnName=\"Name_en\" />\n                <ScalarProperty Name=\"Name_fr\" ColumnName=\"Name_fr\" />\n                <ScalarProperty Name=\"Name_de\" ColumnName=\"Name_de\" />\n              </MappingFragment>\n            </EntityTypeMapping>\n          </EntitySetMapping>\n          <EntitySetMapping Name=\"JournalGenres\">\n            <EntityTypeMapping TypeName=\"LibraModel.JournalGenre\">\n              <MappingFragment StoreEntitySet=\"JournalGenre\">\n                <ScalarProperty Name=\"Key\" ColumnName=\"Key\" />\n                <ScalarProperty Name=\"Category\" ColumnName=\"Category\" />\n                <ScalarProperty Name=\"label\" ColumnName=\"label\" />\n                <ScalarProperty Name=\"Name_ja\" ColumnName=\"Name_ja\" />\n                <ScalarProperty Name=\"Name_en\" ColumnName=\"Name_en\" />\n                <ScalarProperty Name=\"Name_fr\" ColumnName=\"Name_fr\" />\n                <ScalarProperty Name=\"Name_de\" ColumnName=\"Name_de\" />\n              </MappingFragment>\n            </EntityTypeMapping>\n          </EntitySetMapping>\n          <EntitySetMapping Name=\"LodestoneSystemDefines\">\n            <EntityTypeMapping TypeName=\"LibraModel.LodestoneSystemDefine\">\n              <MappingFragment StoreEntitySet=\"LodestoneSystemDefine\">\n                <ScalarProperty Name=\"Key\" ColumnName=\"Key\" />\n                <ScalarProperty Name=\"DefineName\" ColumnName=\"DefineName\" />\n                <ScalarProperty Name=\"DefineValue\" ColumnName=\"DefineValue\" />\n              </MappingFragment>\n            </EntityTypeMapping>\n          </EntitySetMapping>\n          <EntitySetMapping Name=\"NotebookDivisions\">\n            <EntityTypeMapping TypeName=\"LibraModel.NotebookDivision\">\n              <MappingFragment StoreEntitySet=\"NotebookDivision\">\n                <ScalarProperty Name=\"Key\" ColumnName=\"Key\" />\n                <ScalarProperty Name=\"Name_ja\" ColumnName=\"Name_ja\" />\n                <ScalarProperty Name=\"Name_en\" ColumnName=\"Name_en\" />\n                <ScalarProperty Name=\"Name_fr\" ColumnName=\"Name_fr\" />\n                <ScalarProperty Name=\"Name_de\" ColumnName=\"Name_de\" />\n              </MappingFragment>\n            </EntityTypeMapping>\n          </EntitySetMapping>\n          <EntitySetMapping Name=\"PlaceNames\">\n            <EntityTypeMapping TypeName=\"LibraModel.PlaceName\">\n              <MappingFragment StoreEntitySet=\"PlaceName\">\n                <ScalarProperty Name=\"Key\" ColumnName=\"Key\" />\n                <ScalarProperty Name=\"region\" ColumnName=\"region\" />\n                <ScalarProperty Name=\"SGL_ja\" ColumnName=\"SGL_ja\" />\n                <ScalarProperty Name=\"SGL_en\" ColumnName=\"SGL_en\" />\n                <ScalarProperty Name=\"SGL_fr\" ColumnName=\"SGL_fr\" />\n                <ScalarProperty Name=\"SGL_de\" ColumnName=\"SGL_de\" />\n              </MappingFragment>\n            </EntityTypeMapping>\n          </EntitySetMapping>\n          <EntitySetMapping Name=\"Quests\">\n            <EntityTypeMapping TypeName=\"LibraModel.Quest\">\n              <MappingFragment StoreEntitySet=\"Quest\">\n                <ScalarProperty Name=\"Key\" ColumnName=\"Key\" />\n                <ScalarProperty Name=\"Genre\" ColumnName=\"Genre\" />\n                <ScalarProperty Name=\"Area\" ColumnName=\"Area\" />\n                <ScalarProperty Name=\"Name_ja\" ColumnName=\"Name_ja\" />\n                <ScalarProperty Name=\"Name_en\" ColumnName=\"Name_en\" />\n                <ScalarProperty Name=\"Name_fr\" ColumnName=\"Name_fr\" />\n                <ScalarProperty Name=\"Name_de\" ColumnName=\"Name_de\" />\n                <ScalarProperty Name=\"CompanyPointType\" ColumnName=\"CompanyPointType\" />\n                <ScalarProperty Name=\"CompanyPointNum\" ColumnName=\"CompanyPointNum\" />\n                <ScalarProperty Name=\"Gil\" ColumnName=\"Gil\" />\n                <ScalarProperty Name=\"Client\" ColumnName=\"Client\" />\n                <ScalarProperty Name=\"ExpBonus\" ColumnName=\"ExpBonus\" />\n                <ScalarProperty Name=\"Header\" ColumnName=\"Header\" />\n                <ScalarProperty Name=\"ClassLevel\" ColumnName=\"ClassLevel\" />\n                <ScalarProperty Name=\"ClassLevel2\" ColumnName=\"ClassLevel2\" />\n                <ScalarProperty Name=\"ClassJob\" ColumnName=\"ClassJob\" />\n                <ScalarProperty Name=\"ClassJob2\" ColumnName=\"ClassJob2\" />\n                <ScalarProperty Name=\"QuestLevelOffset\" ColumnName=\"QuestLevelOffset\" />\n                <ScalarProperty Name=\"Sort\" ColumnName=\"Sort\" />\n                <ScalarProperty Name=\"BeastTribe\" ColumnName=\"BeastTribe\" />\n                <ScalarProperty Name=\"BeastReputationValueNum\" ColumnName=\"BeastReputationValueNum\" />\n                <ScalarProperty Name=\"WebType\" ColumnName=\"WebType\" />\n                <ScalarProperty Name=\"data\" ColumnName=\"data\" />\n                <ScalarProperty Name=\"path\" ColumnName=\"path\" />\n                <ScalarProperty Name=\"Index_ja\" ColumnName=\"Index_ja\" />\n                <ScalarProperty Name=\"Index_en\" ColumnName=\"Index_en\" />\n                <ScalarProperty Name=\"Index_fr\" ColumnName=\"Index_fr\" />\n                <ScalarProperty Name=\"Index_de\" ColumnName=\"Index_de\" />\n              </MappingFragment>\n            </EntityTypeMapping>\n          </EntitySetMapping>\n          <EntitySetMapping Name=\"Quest_ClassJob\">\n            <EntityTypeMapping TypeName=\"LibraModel.Quest_ClassJob\">\n              <MappingFragment StoreEntitySet=\"Quest_ClassJob\">\n                <ScalarProperty Name=\"Quest_Key\" ColumnName=\"Quest_Key\" />\n                <ScalarProperty Name=\"ClassJob_Key\" ColumnName=\"ClassJob_Key\" />\n              </MappingFragment>\n            </EntityTypeMapping>\n          </EntitySetMapping>\n          <EntitySetMapping Name=\"QuestWebExes\">\n            <EntityTypeMapping TypeName=\"LibraModel.QuestWebEx\">\n              <MappingFragment StoreEntitySet=\"QuestWebEx\">\n                <ScalarProperty Name=\"Key\" ColumnName=\"Key\" />\n                <ScalarProperty Name=\"Name_ja\" ColumnName=\"Name_ja\" />\n                <ScalarProperty Name=\"Name_en\" ColumnName=\"Name_en\" />\n                <ScalarProperty Name=\"Name_fr\" ColumnName=\"Name_fr\" />\n                <ScalarProperty Name=\"Name_de\" ColumnName=\"Name_de\" />\n              </MappingFragment>\n            </EntityTypeMapping>\n          </EntitySetMapping>\n          <EntitySetMapping Name=\"QuestWebTypes\">\n            <EntityTypeMapping TypeName=\"LibraModel.QuestWebType\">\n              <MappingFragment StoreEntitySet=\"QuestWebType\">\n                <ScalarProperty Name=\"Key\" ColumnName=\"Key\" />\n                <ScalarProperty Name=\"Text_de\" ColumnName=\"Text_de\" />\n                <ScalarProperty Name=\"Text_en\" ColumnName=\"Text_en\" />\n                <ScalarProperty Name=\"Text_ja\" ColumnName=\"Text_ja\" />\n                <ScalarProperty Name=\"Text_fr\" ColumnName=\"Text_fr\" />\n              </MappingFragment>\n            </EntityTypeMapping>\n          </EntitySetMapping>\n          <EntitySetMapping Name=\"Races\">\n            <EntityTypeMapping TypeName=\"LibraModel.Race\">\n              <MappingFragment StoreEntitySet=\"Race\">\n                <ScalarProperty Name=\"Key\" ColumnName=\"Key\" />\n                <ScalarProperty Name=\"NameFemale_ja\" ColumnName=\"NameFemale_ja\" />\n                <ScalarProperty Name=\"NameFemale_en\" ColumnName=\"NameFemale_en\" />\n                <ScalarProperty Name=\"NameFemale_fr\" ColumnName=\"NameFemale_fr\" />\n                <ScalarProperty Name=\"NameFemale_de\" ColumnName=\"NameFemale_de\" />\n                <ScalarProperty Name=\"Name_ja\" ColumnName=\"Name_ja\" />\n                <ScalarProperty Name=\"Name_en\" ColumnName=\"Name_en\" />\n                <ScalarProperty Name=\"Name_fr\" ColumnName=\"Name_fr\" />\n                <ScalarProperty Name=\"Name_de\" ColumnName=\"Name_de\" />\n              </MappingFragment>\n            </EntityTypeMapping>\n          </EntitySetMapping>\n          <EntitySetMapping Name=\"Recipes\">\n            <EntityTypeMapping TypeName=\"LibraModel.Recipe\">\n              <MappingFragment StoreEntitySet=\"Recipe\">\n                <ScalarProperty Name=\"Key\" ColumnName=\"Key\" />\n                <ScalarProperty Name=\"CanAutoCraft\" ColumnName=\"CanAutoCraft\" />\n                <ScalarProperty Name=\"CanHq\" ColumnName=\"CanHq\" />\n                <ScalarProperty Name=\"CraftItemId\" ColumnName=\"CraftItemId\" />\n                <ScalarProperty Name=\"CraftNum\" ColumnName=\"CraftNum\" />\n                <ScalarProperty Name=\"CraftType\" ColumnName=\"CraftType\" />\n                <ScalarProperty Name=\"Level\" ColumnName=\"Level\" />\n                <ScalarProperty Name=\"levelView\" ColumnName=\"levelView\" />\n                <ScalarProperty Name=\"levelDiff\" ColumnName=\"levelDiff\" />\n                <ScalarProperty Name=\"Element\" ColumnName=\"Element\" />\n                <ScalarProperty Name=\"NeedCraftmanship\" ColumnName=\"NeedCraftmanship\" />\n                <ScalarProperty Name=\"NeedControl\" ColumnName=\"NeedControl\" />\n                <ScalarProperty Name=\"Number\" ColumnName=\"Number\" />\n                <ScalarProperty Name=\"NeedStatus\" ColumnName=\"NeedStatus\" />\n                <ScalarProperty Name=\"NeedEquipItem\" ColumnName=\"NeedEquipItem\" />\n                <ScalarProperty Name=\"data\" ColumnName=\"data\" />\n                <ScalarProperty Name=\"path\" ColumnName=\"path\" />\n                <ScalarProperty Name=\"Index_ja\" ColumnName=\"Index_ja\" />\n                <ScalarProperty Name=\"Index_en\" ColumnName=\"Index_en\" />\n                <ScalarProperty Name=\"Index_fr\" ColumnName=\"Index_fr\" />\n                <ScalarProperty Name=\"Index_de\" ColumnName=\"Index_de\" />\n              </MappingFragment>\n            </EntityTypeMapping>\n          </EntitySetMapping>\n          <EntitySetMapping Name=\"RecipeElements\">\n            <EntityTypeMapping TypeName=\"LibraModel.RecipeElement\">\n              <MappingFragment StoreEntitySet=\"RecipeElement\">\n                <ScalarProperty Name=\"Key\" ColumnName=\"Key\" />\n                <ScalarProperty Name=\"Name_ja\" ColumnName=\"Name_ja\" />\n                <ScalarProperty Name=\"Name_en\" ColumnName=\"Name_en\" />\n                <ScalarProperty Name=\"Name_fr\" ColumnName=\"Name_fr\" />\n                <ScalarProperty Name=\"Name_de\" ColumnName=\"Name_de\" />\n              </MappingFragment>\n            </EntityTypeMapping>\n          </EntitySetMapping>\n          <EntitySetMapping Name=\"Shops\">\n            <EntityTypeMapping TypeName=\"LibraModel.Shop\">\n              <MappingFragment StoreEntitySet=\"Shop\">\n                <ScalarProperty Name=\"Key\" ColumnName=\"Key\" />\n                <ScalarProperty Name=\"Name_ja\" ColumnName=\"Name_ja\" />\n                <ScalarProperty Name=\"Name_en\" ColumnName=\"Name_en\" />\n                <ScalarProperty Name=\"Name_fr\" ColumnName=\"Name_fr\" />\n                <ScalarProperty Name=\"Name_de\" ColumnName=\"Name_de\" />\n                <ScalarProperty Name=\"BeastTribe\" ColumnName=\"BeastTribe\" />\n              </MappingFragment>\n            </EntityTypeMapping>\n          </EntitySetMapping>\n          <EntitySetMapping Name=\"Status\">\n            <EntityTypeMapping TypeName=\"LibraModel.Status\">\n              <MappingFragment StoreEntitySet=\"Status\">\n                <ScalarProperty Name=\"Key\" ColumnName=\"Key\" />\n                <ScalarProperty Name=\"Name_ja\" ColumnName=\"Name_ja\" />\n                <ScalarProperty Name=\"Name_en\" ColumnName=\"Name_en\" />\n                <ScalarProperty Name=\"Name_fr\" ColumnName=\"Name_fr\" />\n                <ScalarProperty Name=\"Name_de\" ColumnName=\"Name_de\" />\n              </MappingFragment>\n            </EntityTypeMapping>\n          </EntitySetMapping>\n          <EntitySetMapping Name=\"Titles\">\n            <EntityTypeMapping TypeName=\"LibraModel.Title\">\n              <MappingFragment StoreEntitySet=\"Title\">\n                <ScalarProperty Name=\"Key\" ColumnName=\"Key\" />\n                <ScalarProperty Name=\"Female_de\" ColumnName=\"Female_de\" />\n                <ScalarProperty Name=\"Female_en\" ColumnName=\"Female_en\" />\n                <ScalarProperty Name=\"Female_fr\" ColumnName=\"Female_fr\" />\n                <ScalarProperty Name=\"Female_ja\" ColumnName=\"Female_ja\" />\n                <ScalarProperty Name=\"Front_de\" ColumnName=\"Front_de\" />\n                <ScalarProperty Name=\"Front_en\" ColumnName=\"Front_en\" />\n                <ScalarProperty Name=\"Front_fr\" ColumnName=\"Front_fr\" />\n                <ScalarProperty Name=\"Front_ja\" ColumnName=\"Front_ja\" />\n                <ScalarProperty Name=\"Male_de\" ColumnName=\"Male_de\" />\n                <ScalarProperty Name=\"Male_en\" ColumnName=\"Male_en\" />\n                <ScalarProperty Name=\"Male_fr\" ColumnName=\"Male_fr\" />\n                <ScalarProperty Name=\"Male_ja\" ColumnName=\"Male_ja\" />\n              </MappingFragment>\n            </EntityTypeMapping>\n          </EntitySetMapping>\n          <EntitySetMapping Name=\"Tomestones\">\n            <EntityTypeMapping TypeName=\"LibraModel.Tomestone\">\n              <MappingFragment StoreEntitySet=\"Tomestones\">\n                <ScalarProperty Name=\"Key\" ColumnName=\"Key\" />\n                <ScalarProperty Name=\"CatalogId\" ColumnName=\"CatalogId\" />\n                <ScalarProperty Name=\"path\" ColumnName=\"path\" />\n              </MappingFragment>\n            </EntityTypeMapping>\n          </EntitySetMapping>\n          <EntitySetMapping Name=\"Towns\">\n            <EntityTypeMapping TypeName=\"LibraModel.Town\">\n              <MappingFragment StoreEntitySet=\"Town\">\n                <ScalarProperty Name=\"Key\" ColumnName=\"Key\" />\n                <ScalarProperty Name=\"Name_ja\" ColumnName=\"Name_ja\" />\n                <ScalarProperty Name=\"Name_en\" ColumnName=\"Name_en\" />\n                <ScalarProperty Name=\"Name_fr\" ColumnName=\"Name_fr\" />\n                <ScalarProperty Name=\"Name_de\" ColumnName=\"Name_de\" />\n              </MappingFragment>\n            </EntityTypeMapping>\n          </EntitySetMapping>\n        </EntityContainerMapping>\n      </Mapping>\n    </edmx:Mappings>\n  </edmx:Runtime>\n  <!-- EF Designer content (DO NOT EDIT MANUALLY BELOW HERE) -->\n  <Designer xmlns=\"http://schemas.microsoft.com/ado/2009/11/edmx\">\n    <Connection>\n      <DesignerInfoPropertySet>\n        <DesignerProperty Name=\"MetadataArtifactProcessing\" Value=\"EmbedInOutputAssembly\" />\n      </DesignerInfoPropertySet>\n    </Connection>\n    <Options>\n      <DesignerInfoPropertySet>\n        <DesignerProperty Name=\"ValidateOnBuild\" Value=\"true\" />\n        <DesignerProperty Name=\"EnablePluralization\" Value=\"true\" />\n        <DesignerProperty Name=\"IncludeForeignKeysInModel\" Value=\"true\" />\n        <DesignerProperty Name=\"UseLegacyProvider\" Value=\"false\" />\n        <DesignerProperty Name=\"CodeGenerationStrategy\" Value=\"None\" />\n      </DesignerInfoPropertySet>\n    </Options>\n    <!-- Diagram content (shape and connector positions) -->\n    <Diagrams></Diagrams>\n  </Designer>\n</edmx:Edmx>"
  },
  {
    "path": "SaintCoinach/Libra/LibraModel.edmx.diagram",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<edmx:Edmx Version=\"3.0\" xmlns:edmx=\"http://schemas.microsoft.com/ado/2009/11/edmx\">\n <!-- EF Designer content (DO NOT EDIT MANUALLY BELOW HERE) -->\n  <edmx:Designer xmlns=\"http://schemas.microsoft.com/ado/2009/11/edmx\">\n    <!-- Diagram content (shape and connector positions) -->\n    <edmx:Diagrams>\n      <Diagram DiagramId=\"70da8f0e127442efacb0508823f6530d\" Name=\"Diagram1\">\n        <EntityTypeShape EntityType=\"LibraModel.Achievement\" Width=\"1.5\" PointX=\"0.75\" PointY=\"0.75\" IsExpanded=\"true\" />\n        <EntityTypeShape EntityType=\"LibraModel.AchievementCategory\" Width=\"1.5\" PointX=\"2.75\" PointY=\"0.75\" IsExpanded=\"true\" />\n        <EntityTypeShape EntityType=\"LibraModel.AchievementKind\" Width=\"1.5\" PointX=\"2.75\" PointY=\"3.75\" IsExpanded=\"true\" />\n        <EntityTypeShape EntityType=\"LibraModel.Action\" Width=\"1.5\" PointX=\"4.75\" PointY=\"0.75\" IsExpanded=\"true\" />\n        <EntityTypeShape EntityType=\"LibraModel.BaseParam\" Width=\"1.5\" PointX=\"4.75\" PointY=\"4.75\" IsExpanded=\"true\" />\n        <EntityTypeShape EntityType=\"LibraModel.BeastTribe\" Width=\"1.5\" PointX=\"6.75\" PointY=\"0.75\" IsExpanded=\"true\" />\n        <EntityTypeShape EntityType=\"LibraModel.BNpcName\" Width=\"1.5\" PointX=\"0.75\" PointY=\"6.75\" IsExpanded=\"true\" />\n        <EntityTypeShape EntityType=\"LibraModel.BNpcName_PlaceName\" Width=\"1.5\" PointX=\"6.75\" PointY=\"4.75\" IsExpanded=\"true\" />\n        <EntityTypeShape EntityType=\"LibraModel.ClassJob\" Width=\"1.5\" PointX=\"2.75\" PointY=\"7.75\" IsExpanded=\"true\" />\n        <EntityTypeShape EntityType=\"LibraModel.ClassJob_ClassJobCategory\" Width=\"1.5\" PointX=\"4.75\" PointY=\"7.75\" IsExpanded=\"true\" />\n        <EntityTypeShape EntityType=\"LibraModel.ClassJobCategory\" Width=\"1.5\" PointX=\"6.75\" PointY=\"7.75\" IsExpanded=\"true\" />\n        <EntityTypeShape EntityType=\"LibraModel.Colosseum\" Width=\"1.5\" PointX=\"8.75\" PointY=\"0.75\" IsExpanded=\"true\" />\n        <EntityTypeShape EntityType=\"LibraModel.ContentRoulette\" Width=\"1.5\" PointX=\"8.75\" PointY=\"3.75\" IsExpanded=\"true\" />\n        <EntityTypeShape EntityType=\"LibraModel.ContentType\" Width=\"1.5\" PointX=\"8.75\" PointY=\"6.75\" IsExpanded=\"true\" />\n        <EntityTypeShape EntityType=\"LibraModel.CraftType\" Width=\"1.5\" PointX=\"8.75\" PointY=\"9.75\" IsExpanded=\"true\" />\n        <EntityTypeShape EntityType=\"LibraModel.Emote\" Width=\"1.5\" PointX=\"10.75\" PointY=\"0.75\" IsExpanded=\"true\" />\n        <EntityTypeShape EntityType=\"LibraModel.ENpcResident\" Width=\"1.5\" PointX=\"10.75\" PointY=\"3.75\" IsExpanded=\"true\" />\n        <EntityTypeShape EntityType=\"LibraModel.ENpcResident_PlaceName\" Width=\"1.5\" PointX=\"4.75\" PointY=\"10.75\" IsExpanded=\"true\" />\n        <EntityTypeShape EntityType=\"LibraModel.ENpcResident_Quest\" Width=\"1.5\" PointX=\"10.75\" PointY=\"8.75\" IsExpanded=\"true\" />\n        <EntityTypeShape EntityType=\"LibraModel.FCHierarchy\" Width=\"1.5\" PointX=\"10.75\" PointY=\"11.75\" IsExpanded=\"true\" />\n        <EntityTypeShape EntityType=\"LibraModel.FCRank\" Width=\"1.5\" PointX=\"12.75\" PointY=\"0.75\" IsExpanded=\"true\" />\n        <EntityTypeShape EntityType=\"LibraModel.FCReputation\" Width=\"1.5\" PointX=\"0.75\" PointY=\"12.75\" IsExpanded=\"true\" />\n        <EntityTypeShape EntityType=\"LibraModel.Frontline01\" Width=\"1.5\" PointX=\"12.75\" PointY=\"3.75\" IsExpanded=\"true\" />\n        <EntityTypeShape EntityType=\"LibraModel.Gathering\" Width=\"1.5\" PointX=\"12.75\" PointY=\"6.75\" IsExpanded=\"true\" />\n        <EntityTypeShape EntityType=\"LibraModel.GatheringType\" Width=\"1.5\" PointX=\"6.75\" PointY=\"12.75\" IsExpanded=\"true\" />\n        <EntityTypeShape EntityType=\"LibraModel.GCRankGridaniaFemaleText\" Width=\"1.5\" PointX=\"12.75\" PointY=\"11.75\" IsExpanded=\"true\" />\n        <EntityTypeShape EntityType=\"LibraModel.GCRankGridaniaMaleText\" Width=\"1.5\" PointX=\"2.75\" PointY=\"13.75\" IsExpanded=\"true\" />\n        <EntityTypeShape EntityType=\"LibraModel.GCRankLimsaFemaleText\" Width=\"1.5\" PointX=\"14.75\" PointY=\"0.75\" IsExpanded=\"true\" />\n        <EntityTypeShape EntityType=\"LibraModel.GCRankLimsaMaleText\" Width=\"1.5\" PointX=\"14.75\" PointY=\"3.75\" IsExpanded=\"true\" />\n        <EntityTypeShape EntityType=\"LibraModel.GCRankUldahFemaleText\" Width=\"1.5\" PointX=\"14.75\" PointY=\"6.75\" IsExpanded=\"true\" />\n        <EntityTypeShape EntityType=\"LibraModel.GCRankUldahMaleText\" Width=\"1.5\" PointX=\"8.75\" PointY=\"14.75\" IsExpanded=\"true\" />\n        <EntityTypeShape EntityType=\"LibraModel.GeneralAction\" Width=\"1.5\" PointX=\"14.75\" PointY=\"9.75\" IsExpanded=\"true\" />\n        <EntityTypeShape EntityType=\"LibraModel.GrandCompany\" Width=\"1.5\" PointX=\"10.75\" PointY=\"14.75\" IsExpanded=\"true\" />\n        <EntityTypeShape EntityType=\"LibraModel.GuardianDeity\" Width=\"1.5\" PointX=\"14.75\" PointY=\"12.75\" IsExpanded=\"true\" />\n        <EntityTypeShape EntityType=\"LibraModel.GuildOrder\" Width=\"1.5\" PointX=\"4.75\" PointY=\"15.75\" IsExpanded=\"true\" />\n        <EntityTypeShape EntityType=\"LibraModel.InstanceContent\" Width=\"1.5\" PointX=\"12.75\" PointY=\"15.75\" IsExpanded=\"true\" />\n        <EntityTypeShape EntityType=\"LibraModel.InstanceContentType\" Width=\"1.5\" PointX=\"14.75\" PointY=\"15.75\" IsExpanded=\"true\" />\n        <EntityTypeShape EntityType=\"LibraModel.Item\" Width=\"1.5\" PointX=\"16.75\" PointY=\"0.75\" IsExpanded=\"true\" />\n        <EntityTypeShape EntityType=\"LibraModel.Item_ClassJob\" Width=\"1.5\" PointX=\"0.75\" PointY=\"16.75\" IsExpanded=\"true\" />\n        <EntityTypeShape EntityType=\"LibraModel.ItemCategory\" Width=\"1.5\" PointX=\"16.75\" PointY=\"12.75\" IsExpanded=\"true\" />\n        <EntityTypeShape EntityType=\"LibraModel.ItemSery\" Width=\"1.5\" PointX=\"16.75\" PointY=\"15.75\" IsExpanded=\"true\" />\n        <EntityTypeShape EntityType=\"LibraModel.ItemSpecialBonu\" Width=\"1.5\" PointX=\"6.75\" PointY=\"17.75\" IsExpanded=\"true\" />\n        <EntityTypeShape EntityType=\"LibraModel.ItemUICategory\" Width=\"1.5\" PointX=\"8.75\" PointY=\"17.75\" IsExpanded=\"true\" />\n        <EntityTypeShape EntityType=\"LibraModel.ItemUIKind\" Width=\"1.5\" PointX=\"18.75\" PointY=\"0.75\" IsExpanded=\"true\" />\n        <EntityTypeShape EntityType=\"LibraModel.JournalCategory\" Width=\"1.5\" PointX=\"2.75\" PointY=\"18.75\" IsExpanded=\"true\" />\n        <EntityTypeShape EntityType=\"LibraModel.JournalGenre\" Width=\"1.5\" PointX=\"18.75\" PointY=\"3.75\" IsExpanded=\"true\" />\n        <EntityTypeShape EntityType=\"LibraModel.LodestoneSystemDefine\" Width=\"1.5\" PointX=\"18.75\" PointY=\"7.75\" IsExpanded=\"true\" />\n        <EntityTypeShape EntityType=\"LibraModel.NotebookDivision\" Width=\"1.5\" PointX=\"18.75\" PointY=\"10.75\" IsExpanded=\"true\" />\n        <EntityTypeShape EntityType=\"LibraModel.PlaceName\" Width=\"1.5\" PointX=\"18.75\" PointY=\"13.75\" IsExpanded=\"true\" />\n        <EntityTypeShape EntityType=\"LibraModel.Quest\" Width=\"1.5\" PointX=\"14.75\" PointY=\"18.75\" IsExpanded=\"true\" />\n        <EntityTypeShape EntityType=\"LibraModel.Quest_ClassJob\" Width=\"1.5\" PointX=\"18.75\" PointY=\"16.75\" IsExpanded=\"true\" />\n        <EntityTypeShape EntityType=\"LibraModel.QuestWebEx\" Width=\"1.5\" PointX=\"16.75\" PointY=\"19.75\" IsExpanded=\"true\" />\n        <EntityTypeShape EntityType=\"LibraModel.QuestWebType\" Width=\"1.5\" PointX=\"18.75\" PointY=\"19.75\" IsExpanded=\"true\" />\n        <EntityTypeShape EntityType=\"LibraModel.Race\" Width=\"1.5\" PointX=\"20.75\" PointY=\"0.75\" IsExpanded=\"true\" />\n        <EntityTypeShape EntityType=\"LibraModel.Recipe\" Width=\"1.5\" PointX=\"20.75\" PointY=\"4.75\" IsExpanded=\"true\" />\n        <EntityTypeShape EntityType=\"LibraModel.RecipeElement\" Width=\"1.5\" PointX=\"4.75\" PointY=\"20.75\" IsExpanded=\"true\" />\n        <EntityTypeShape EntityType=\"LibraModel.Shop\" Width=\"1.5\" PointX=\"20.75\" PointY=\"10.75\" IsExpanded=\"true\" />\n        <EntityTypeShape EntityType=\"LibraModel.Status\" Width=\"1.5\" PointX=\"20.75\" PointY=\"13.75\" IsExpanded=\"true\" />\n        <EntityTypeShape EntityType=\"LibraModel.Title\" Width=\"1.5\" PointX=\"20.75\" PointY=\"16.75\" IsExpanded=\"true\" />\n        <EntityTypeShape EntityType=\"LibraModel.Tomestone\" Width=\"1.5\" PointX=\"0.75\" PointY=\"21.75\" IsExpanded=\"true\" />\n        <EntityTypeShape EntityType=\"LibraModel.Town\" Width=\"1.5\" PointX=\"6.75\" PointY=\"21.75\" IsExpanded=\"true\" />\n      </Diagram>\n    </edmx:Diagrams>\n  </edmx:Designer>\n</edmx:Edmx>"
  },
  {
    "path": "SaintCoinach/Libra/LibraModel.tt",
    "content": "﻿<#@ template language=\"C#\" debug=\"false\" hostspecific=\"true\"#>\n<#@ include file=\"EF6.Utility.CS.ttinclude\"#><#@ \n output extension=\".cs\"#><#\n\nconst string inputFile = @\"LibraModel.edmx\";\nvar textTransform = DynamicTextTransformation.Create(this);\nvar code = new CodeGenerationTools(this);\nvar ef = new MetadataTools(this);\nvar typeMapper = new TypeMapper(code, ef, textTransform.Errors);\nvar\tfileManager = EntityFrameworkTemplateFileManager.Create(this);\nvar itemCollection = new EdmMetadataLoader(textTransform.Host, textTransform.Errors).CreateEdmItemCollection(inputFile);\nvar codeStringGenerator = new CodeStringGenerator(code, typeMapper, ef);\n\nif (!typeMapper.VerifyCaseInsensitiveTypeUniqueness(typeMapper.GetAllGlobalItems(itemCollection), inputFile))\n{\n    return string.Empty;\n}\n\nWriteHeader(codeStringGenerator, fileManager);\n\nforeach (var entity in typeMapper.GetItemsToGenerate<EntityType>(itemCollection))\n{\n    fileManager.StartNewFile(entity.Name + \".cs\");\n    BeginNamespace(code);\n#>\n<#=codeStringGenerator.UsingDirectives(inHeader: false)#>\n<#=codeStringGenerator.EntityClassOpening(entity)#>\n{\n<#\n    var propertiesWithDefaultValues = typeMapper.GetPropertiesWithDefaultValues(entity);\n    var collectionNavigationProperties = typeMapper.GetCollectionNavigationProperties(entity);\n    var complexProperties = typeMapper.GetComplexProperties(entity);\n\n    if (propertiesWithDefaultValues.Any() || collectionNavigationProperties.Any() || complexProperties.Any())\n    {\n#>\n    public <#=code.Escape(entity)#>()\n    {\n<#\n        foreach (var edmProperty in propertiesWithDefaultValues)\n        {\n#>\n        this.<#=code.Escape(edmProperty)#> = <#=typeMapper.CreateLiteral(edmProperty.DefaultValue)#>;\n<#\n        }\n\n        foreach (var navigationProperty in collectionNavigationProperties)\n        {\n#>\n        this.<#=code.Escape(navigationProperty)#> = new HashSet<<#=typeMapper.GetTypeName(navigationProperty.ToEndMember.GetEntityType())#>>();\n<#\n        }\n\n        foreach (var complexProperty in complexProperties)\n        {\n#>\n        this.<#=code.Escape(complexProperty)#> = new <#=typeMapper.GetTypeName(complexProperty.TypeUsage)#>();\n<#\n        }\n#>\n    }\n\n<#\n    }\n\n    var simpleProperties = typeMapper.GetSimpleProperties(entity);\n    if (simpleProperties.Any())\n    {\n        foreach (var edmProperty in simpleProperties)\n        {\n#>\n    <#=codeStringGenerator.Property(edmProperty)#>\n<#\n        }\n    }\n\n    if (complexProperties.Any())\n    {\n#>\n\n<#\n        foreach(var complexProperty in complexProperties)\n        {\n#>\n    <#=codeStringGenerator.Property(complexProperty)#>\n<#\n        }\n    }\n\n    var navigationProperties = typeMapper.GetNavigationProperties(entity);\n    if (navigationProperties.Any())\n    {\n#>\n\n<#\n        foreach (var navigationProperty in navigationProperties)\n        {\n#>\n    <#=codeStringGenerator.NavigationProperty(navigationProperty)#>\n<#\n        }\n    }\n#>\n}\n<#\n    EndNamespace(code);\n}\n\nforeach (var complex in typeMapper.GetItemsToGenerate<ComplexType>(itemCollection))\n{\n    fileManager.StartNewFile(complex.Name + \".cs\");\n    BeginNamespace(code);\n#>\n<#=codeStringGenerator.UsingDirectives(inHeader: false, includeCollections: false)#>\n<#=Accessibility.ForType(complex)#> partial class <#=code.Escape(complex)#>\n{\n<#\n    var complexProperties = typeMapper.GetComplexProperties(complex);\n    var propertiesWithDefaultValues = typeMapper.GetPropertiesWithDefaultValues(complex);\n\n    if (propertiesWithDefaultValues.Any() || complexProperties.Any())\n    {\n#>\n    public <#=code.Escape(complex)#>()\n    {\n<#\n        foreach (var edmProperty in propertiesWithDefaultValues)\n        {\n#>\n        this.<#=code.Escape(edmProperty)#> = <#=typeMapper.CreateLiteral(edmProperty.DefaultValue)#>;\n<#\n        }\n\n        foreach (var complexProperty in complexProperties)\n        {\n#>\n        this.<#=code.Escape(complexProperty)#> = new <#=typeMapper.GetTypeName(complexProperty.TypeUsage)#>();\n<#\n        }\n#>\n    }\n\n<#\n    }\n\n    var simpleProperties = typeMapper.GetSimpleProperties(complex);\n    if (simpleProperties.Any())\n    {\n        foreach(var edmProperty in simpleProperties)\n        {\n#>\n    <#=codeStringGenerator.Property(edmProperty)#>\n<#\n        }\n    }\n\n    if (complexProperties.Any())\n    {\n#>\n\n<#\n        foreach(var edmProperty in complexProperties)\n        {\n#>\n    <#=codeStringGenerator.Property(edmProperty)#>\n<#\n        }\n    }\n#>\n}\n<#\n    EndNamespace(code);\n}\n\nforeach (var enumType in typeMapper.GetEnumItemsToGenerate(itemCollection))\n{\n    fileManager.StartNewFile(enumType.Name + \".cs\");\n    BeginNamespace(code);\n#>\n<#=codeStringGenerator.UsingDirectives(inHeader: false, includeCollections: false)#>\n<#\n    if (typeMapper.EnumIsFlags(enumType))\n    {\n#>\n[Flags]\n<#\n    }\n#>\n<#=codeStringGenerator.EnumOpening(enumType)#>\n{\n<#\n    var foundOne = false;\n    \n    foreach (MetadataItem member in typeMapper.GetEnumMembers(enumType))\n    {\n        foundOne = true;\n#>\n    <#=code.Escape(typeMapper.GetEnumMemberName(member))#> = <#=typeMapper.GetEnumMemberValue(member)#>,\n<#\n    }\n\n    if (foundOne)\n    {\n        this.GenerationEnvironment.Remove(this.GenerationEnvironment.Length - 3, 1);\n    }\n#>\n}\n<#\n    EndNamespace(code);\n}\n\nfileManager.Process();\n\n#>\n<#+\n\npublic void WriteHeader(CodeStringGenerator codeStringGenerator, EntityFrameworkTemplateFileManager fileManager)\n{\n    fileManager.StartHeader();\n#>\n//------------------------------------------------------------------------------\n// <auto-generated>\n// <#=CodeGenerationTools.GetResourceString(\"Template_GeneratedCodeCommentLine1\")#>\n//\n// <#=CodeGenerationTools.GetResourceString(\"Template_GeneratedCodeCommentLine2\")#>\n// <#=CodeGenerationTools.GetResourceString(\"Template_GeneratedCodeCommentLine3\")#>\n// </auto-generated>\n//------------------------------------------------------------------------------\n<#=codeStringGenerator.UsingDirectives(inHeader: true)#>\n<#+\n    fileManager.EndBlock();\n}\n\npublic void BeginNamespace(CodeGenerationTools code)\n{\n    var codeNamespace = code.VsNamespaceSuggestion();\n    if (!String.IsNullOrEmpty(codeNamespace))\n    {\n#>\nnamespace <#=code.EscapeNamespace(codeNamespace)#>\n{\n<#+\n        PushIndent(\"    \");\n    }\n}\n\npublic void EndNamespace(CodeGenerationTools code)\n{\n    if (!String.IsNullOrEmpty(code.VsNamespaceSuggestion()))\n    {\n        PopIndent();\n#>\n}\n<#+\n    }\n}\n\npublic const string TemplateId = \"CSharp_DbContext_Types_EF6\";\n\npublic class CodeStringGenerator\n{\n    private readonly CodeGenerationTools _code;\n    private readonly TypeMapper _typeMapper;\n    private readonly MetadataTools _ef;\n\n    public CodeStringGenerator(CodeGenerationTools code, TypeMapper typeMapper, MetadataTools ef)\n    {\n        ArgumentNotNull(code, \"code\");\n        ArgumentNotNull(typeMapper, \"typeMapper\");\n        ArgumentNotNull(ef, \"ef\");\n\n        _code = code;\n        _typeMapper = typeMapper;\n        _ef = ef;\n    }\n\n    public string Property(EdmProperty edmProperty)\n    {\n        return string.Format(\n            CultureInfo.InvariantCulture,\n            \"{0} {1} {2} {{ {3}get; {4}set; }}\",\n            Accessibility.ForProperty(edmProperty),\n            _typeMapper.GetTypeName(edmProperty.TypeUsage),\n            _code.Escape(edmProperty),\n            _code.SpaceAfter(Accessibility.ForGetter(edmProperty)),\n            _code.SpaceAfter(Accessibility.ForSetter(edmProperty)));\n    }\n\n    public string NavigationProperty(NavigationProperty navProp)\n    {\n        var endType = _typeMapper.GetTypeName(navProp.ToEndMember.GetEntityType());\n        return string.Format(\n            CultureInfo.InvariantCulture,\n            \"{0} {1} {2} {{ {3}get; {4}set; }}\",\n            AccessibilityAndVirtual(Accessibility.ForNavigationProperty(navProp)),\n            navProp.ToEndMember.RelationshipMultiplicity == RelationshipMultiplicity.Many ? (\"ICollection<\" + endType + \">\") : endType,\n            _code.Escape(navProp),\n            _code.SpaceAfter(Accessibility.ForGetter(navProp)),\n            _code.SpaceAfter(Accessibility.ForSetter(navProp)));\n    }\n    \n    public string AccessibilityAndVirtual(string accessibility)\n    {\n        return accessibility + (accessibility != \"private\" ? \" virtual\" : \"\");\n    }\n    \n    public string EntityClassOpening(EntityType entity)\n    {\n        return string.Format(\n            CultureInfo.InvariantCulture,\n            \"{0} {1}partial class {2}{3}\",\n            Accessibility.ForType(entity),\n            _code.SpaceAfter(_code.AbstractOption(entity)),\n            _code.Escape(entity),\n            _code.StringBefore(\" : \", _typeMapper.GetTypeName(entity.BaseType)));\n    }\n    \n    public string EnumOpening(SimpleType enumType)\n    {\n        return string.Format(\n            CultureInfo.InvariantCulture,\n            \"{0} enum {1} : {2}\",\n            Accessibility.ForType(enumType),\n            _code.Escape(enumType),\n            _code.Escape(_typeMapper.UnderlyingClrType(enumType)));\n        }\n    \n    public void WriteFunctionParameters(EdmFunction edmFunction, Action<string, string, string, string> writeParameter)\n    {\n        var parameters = FunctionImportParameter.Create(edmFunction.Parameters, _code, _ef);\n        foreach (var parameter in parameters.Where(p => p.NeedsLocalVariable))\n        {\n            var isNotNull = parameter.IsNullableOfT ? parameter.FunctionParameterName + \".HasValue\" : parameter.FunctionParameterName + \" != null\";\n            var notNullInit = \"new ObjectParameter(\\\"\" + parameter.EsqlParameterName + \"\\\", \" + parameter.FunctionParameterName + \")\";\n            var nullInit = \"new ObjectParameter(\\\"\" + parameter.EsqlParameterName + \"\\\", typeof(\" + TypeMapper.FixNamespaces(parameter.RawClrTypeName) + \"))\";\n            writeParameter(parameter.LocalVariableName, isNotNull, notNullInit, nullInit);\n        }\n    }\n    \n    public string ComposableFunctionMethod(EdmFunction edmFunction, string modelNamespace)\n    {\n        var parameters = _typeMapper.GetParameters(edmFunction);\n        \n        return string.Format(\n            CultureInfo.InvariantCulture,\n            \"{0} IQueryable<{1}> {2}({3})\",\n            AccessibilityAndVirtual(Accessibility.ForMethod(edmFunction)),\n            _typeMapper.GetTypeName(_typeMapper.GetReturnType(edmFunction), modelNamespace),\n            _code.Escape(edmFunction),\n            string.Join(\", \", parameters.Select(p => TypeMapper.FixNamespaces(p.FunctionParameterType) + \" \" + p.FunctionParameterName).ToArray()));\n    }\n    \n    public string ComposableCreateQuery(EdmFunction edmFunction, string modelNamespace)\n    {\n        var parameters = _typeMapper.GetParameters(edmFunction);\n        \n        return string.Format(\n            CultureInfo.InvariantCulture,\n            \"return ((IObjectContextAdapter)this).ObjectContext.CreateQuery<{0}>(\\\"[{1}].[{2}]({3})\\\"{4});\",\n            _typeMapper.GetTypeName(_typeMapper.GetReturnType(edmFunction), modelNamespace),\n            edmFunction.NamespaceName,\n            edmFunction.Name,\n            string.Join(\", \", parameters.Select(p => \"@\" + p.EsqlParameterName).ToArray()),\n            _code.StringBefore(\", \", string.Join(\", \", parameters.Select(p => p.ExecuteParameterName).ToArray())));\n    }\n    \n    public string FunctionMethod(EdmFunction edmFunction, string modelNamespace, bool includeMergeOption)\n    {\n        var parameters = _typeMapper.GetParameters(edmFunction);\n        var returnType = _typeMapper.GetReturnType(edmFunction);\n\n        var paramList = String.Join(\", \", parameters.Select(p => TypeMapper.FixNamespaces(p.FunctionParameterType) + \" \" + p.FunctionParameterName).ToArray());\n        if (includeMergeOption)\n        {\n            paramList = _code.StringAfter(paramList, \", \") + \"MergeOption mergeOption\";\n        }\n\n        return string.Format(\n            CultureInfo.InvariantCulture,\n            \"{0} {1} {2}({3})\",\n            AccessibilityAndVirtual(Accessibility.ForMethod(edmFunction)),\n            returnType == null ? \"int\" : \"ObjectResult<\" + _typeMapper.GetTypeName(returnType, modelNamespace) + \">\",\n            _code.Escape(edmFunction),\n            paramList);\n    }\n    \n    public string ExecuteFunction(EdmFunction edmFunction, string modelNamespace, bool includeMergeOption)\n    {\n        var parameters = _typeMapper.GetParameters(edmFunction);\n        var returnType = _typeMapper.GetReturnType(edmFunction);\n\n        var callParams = _code.StringBefore(\", \", String.Join(\", \", parameters.Select(p => p.ExecuteParameterName).ToArray()));\n        if (includeMergeOption)\n        {\n            callParams = \", mergeOption\" + callParams;\n        }\n        \n        return string.Format(\n            CultureInfo.InvariantCulture,\n            \"return ((IObjectContextAdapter)this).ObjectContext.ExecuteFunction{0}(\\\"{1}\\\"{2});\",\n            returnType == null ? \"\" : \"<\" + _typeMapper.GetTypeName(returnType, modelNamespace) + \">\",\n            edmFunction.Name,\n            callParams);\n    }\n    \n    public string DbSet(EntitySet entitySet)\n    {\n        return string.Format(\n            CultureInfo.InvariantCulture,\n            \"{0} virtual DbSet<{1}> {2} {{ get; set; }}\",\n            Accessibility.ForReadOnlyProperty(entitySet),\n            _typeMapper.GetTypeName(entitySet.ElementType),\n            _code.Escape(entitySet));\n    }\n\n    public string UsingDirectives(bool inHeader, bool includeCollections = true)\n    {\n        return inHeader == string.IsNullOrEmpty(_code.VsNamespaceSuggestion())\n            ? string.Format(\n                CultureInfo.InvariantCulture,\n                \"{0}using System;{1}\" +\n                \"{2}\",\n                inHeader ? Environment.NewLine : \"\",\n                includeCollections ? (Environment.NewLine + \"using System.Collections.Generic;\") : \"\",\n                inHeader ? \"\" : Environment.NewLine)\n            : \"\";\n    }\n}\n\npublic class TypeMapper\n{\n    private const string ExternalTypeNameAttributeName = @\"http://schemas.microsoft.com/ado/2006/04/codegeneration:ExternalTypeName\";\n\n    private readonly System.Collections.IList _errors;\n    private readonly CodeGenerationTools _code;\n    private readonly MetadataTools _ef;\n\n    public TypeMapper(CodeGenerationTools code, MetadataTools ef, System.Collections.IList errors)\n    {\n        ArgumentNotNull(code, \"code\");\n        ArgumentNotNull(ef, \"ef\");\n        ArgumentNotNull(errors, \"errors\");\n\n        _code = code;\n        _ef = ef;\n        _errors = errors;\n    }\n\n    public static string FixNamespaces(string typeName)\n    {\n        return typeName.Replace(\"System.Data.Spatial.\", \"System.Data.Entity.Spatial.\");\n    }\n\n    public string GetTypeName(TypeUsage typeUsage)\n    {\n        return typeUsage == null ? null : GetTypeName(typeUsage.EdmType, _ef.IsNullable(typeUsage), modelNamespace: null);\n    }\n\n    public string GetTypeName(EdmType edmType)\n    {\n        return GetTypeName(edmType, isNullable: null, modelNamespace: null);\n    }\n\n    public string GetTypeName(TypeUsage typeUsage, string modelNamespace)\n    {\n        return typeUsage == null ? null : GetTypeName(typeUsage.EdmType, _ef.IsNullable(typeUsage), modelNamespace);\n    }\n\n    public string GetTypeName(EdmType edmType, string modelNamespace)\n    {\n        return GetTypeName(edmType, isNullable: null, modelNamespace: modelNamespace);\n    }\n\n    public string GetTypeName(EdmType edmType, bool? isNullable, string modelNamespace)\n    {\n        if (edmType == null)\n        {\n            return null;\n        }\n\n        var collectionType = edmType as CollectionType;\n        if (collectionType != null)\n        {\n            return String.Format(CultureInfo.InvariantCulture, \"ICollection<{0}>\", GetTypeName(collectionType.TypeUsage, modelNamespace));\n        }\n\n        var typeName = _code.Escape(edmType.MetadataProperties\n                                .Where(p => p.Name == ExternalTypeNameAttributeName)\n                                .Select(p => (string)p.Value)\n                                .FirstOrDefault())\n            ?? (modelNamespace != null && edmType.NamespaceName != modelNamespace ?\n                _code.CreateFullName(_code.EscapeNamespace(edmType.NamespaceName), _code.Escape(edmType)) :\n                _code.Escape(edmType));\n\n        if (edmType is StructuralType)\n        {\n            return typeName;\n        }\n\n        if (edmType is SimpleType)\n        {\n            var clrType = UnderlyingClrType(edmType);\n            if (!IsEnumType(edmType))\n            {\n                typeName = _code.Escape(clrType);\n            }\n\n            typeName = FixNamespaces(typeName);\n\n            return clrType.IsValueType && isNullable == true ?\n                String.Format(CultureInfo.InvariantCulture, \"Nullable<{0}>\", typeName) :\n                typeName;\n        }\n\n        throw new ArgumentException(\"edmType\");\n    }\n    \n    public Type UnderlyingClrType(EdmType edmType)\n    {\n        ArgumentNotNull(edmType, \"edmType\");\n\n        var primitiveType = edmType as PrimitiveType;\n        if (primitiveType != null)\n        {\n            return primitiveType.ClrEquivalentType;\n        }\n\n        if (IsEnumType(edmType))\n        {\n            return GetEnumUnderlyingType(edmType).ClrEquivalentType;\n        }\n\n        return typeof(object);\n    }\n    \n    public object GetEnumMemberValue(MetadataItem enumMember)\n    {\n        ArgumentNotNull(enumMember, \"enumMember\");\n        \n        var valueProperty = enumMember.GetType().GetProperty(\"Value\");\n        return valueProperty == null ? null : valueProperty.GetValue(enumMember, null);\n    }\n    \n    public string GetEnumMemberName(MetadataItem enumMember)\n    {\n        ArgumentNotNull(enumMember, \"enumMember\");\n        \n        var nameProperty = enumMember.GetType().GetProperty(\"Name\");\n        return nameProperty == null ? null : (string)nameProperty.GetValue(enumMember, null);\n    }\n\n    public System.Collections.IEnumerable GetEnumMembers(EdmType enumType)\n    {\n        ArgumentNotNull(enumType, \"enumType\");\n\n        var membersProperty = enumType.GetType().GetProperty(\"Members\");\n        return membersProperty != null \n            ? (System.Collections.IEnumerable)membersProperty.GetValue(enumType, null)\n            : Enumerable.Empty<MetadataItem>();\n    }\n    \n    public bool EnumIsFlags(EdmType enumType)\n    {\n        ArgumentNotNull(enumType, \"enumType\");\n        \n        var isFlagsProperty = enumType.GetType().GetProperty(\"IsFlags\");\n        return isFlagsProperty != null && (bool)isFlagsProperty.GetValue(enumType, null);\n    }\n\n    public bool IsEnumType(GlobalItem edmType)\n    {\n        ArgumentNotNull(edmType, \"edmType\");\n\n        return edmType.GetType().Name == \"EnumType\";\n    }\n\n    public PrimitiveType GetEnumUnderlyingType(EdmType enumType)\n    {\n        ArgumentNotNull(enumType, \"enumType\");\n\n        return (PrimitiveType)enumType.GetType().GetProperty(\"UnderlyingType\").GetValue(enumType, null);\n    }\n\n    public string CreateLiteral(object value)\n    {\n        if (value == null || value.GetType() != typeof(TimeSpan))\n        {\n            return _code.CreateLiteral(value);\n        }\n\n        return string.Format(CultureInfo.InvariantCulture, \"new TimeSpan({0})\", ((TimeSpan)value).Ticks);\n    }\n    \n    public bool VerifyCaseInsensitiveTypeUniqueness(IEnumerable<string> types, string sourceFile)\n    {\n        ArgumentNotNull(types, \"types\");\n        ArgumentNotNull(sourceFile, \"sourceFile\");\n        \n        var hash = new HashSet<string>(StringComparer.InvariantCultureIgnoreCase);\n        if (types.Any(item => !hash.Add(item)))\n        {\n            _errors.Add(\n                new CompilerError(sourceFile, -1, -1, \"6023\",\n                    String.Format(CultureInfo.CurrentCulture, CodeGenerationTools.GetResourceString(\"Template_CaseInsensitiveTypeConflict\"))));\n            return false;\n        }\n        return true;\n    }\n    \n    public IEnumerable<SimpleType> GetEnumItemsToGenerate(IEnumerable<GlobalItem> itemCollection)\n    {\n        return GetItemsToGenerate<SimpleType>(itemCollection)\n            .Where(e => IsEnumType(e));\n    }\n    \n    public IEnumerable<T> GetItemsToGenerate<T>(IEnumerable<GlobalItem> itemCollection) where T: EdmType\n    {\n        return itemCollection\n            .OfType<T>()\n            .Where(i => !i.MetadataProperties.Any(p => p.Name == ExternalTypeNameAttributeName))\n            .OrderBy(i => i.Name);\n    }\n\n    public IEnumerable<string> GetAllGlobalItems(IEnumerable<GlobalItem> itemCollection)\n    {\n        return itemCollection\n            .Where(i => i is EntityType || i is ComplexType || i is EntityContainer || IsEnumType(i))\n            .Select(g => GetGlobalItemName(g));\n    }\n\n    public string GetGlobalItemName(GlobalItem item)\n    {\n        if (item is EdmType)\n        {\n            return ((EdmType)item).Name;\n        }\n        else\n        {\n            return ((EntityContainer)item).Name;\n        }\n    }\n\n    public IEnumerable<EdmProperty> GetSimpleProperties(EntityType type)\n    {\n        return type.Properties.Where(p => p.TypeUsage.EdmType is SimpleType && p.DeclaringType == type);\n    }\n    \n    public IEnumerable<EdmProperty> GetSimpleProperties(ComplexType type)\n    {\n        return type.Properties.Where(p => p.TypeUsage.EdmType is SimpleType && p.DeclaringType == type);\n    }\n    \n    public IEnumerable<EdmProperty> GetComplexProperties(EntityType type)\n    {\n        return type.Properties.Where(p => p.TypeUsage.EdmType is ComplexType && p.DeclaringType == type);\n    }\n    \n    public IEnumerable<EdmProperty> GetComplexProperties(ComplexType type)\n    {\n        return type.Properties.Where(p => p.TypeUsage.EdmType is ComplexType && p.DeclaringType == type);\n    }\n\n    public IEnumerable<EdmProperty> GetPropertiesWithDefaultValues(EntityType type)\n    {\n        return type.Properties.Where(p => p.TypeUsage.EdmType is SimpleType && p.DeclaringType == type && p.DefaultValue != null);\n    }\n    \n    public IEnumerable<EdmProperty> GetPropertiesWithDefaultValues(ComplexType type)\n    {\n        return type.Properties.Where(p => p.TypeUsage.EdmType is SimpleType && p.DeclaringType == type && p.DefaultValue != null);\n    }\n\n    public IEnumerable<NavigationProperty> GetNavigationProperties(EntityType type)\n    {\n        return type.NavigationProperties.Where(np => np.DeclaringType == type);\n    }\n    \n    public IEnumerable<NavigationProperty> GetCollectionNavigationProperties(EntityType type)\n    {\n        return type.NavigationProperties.Where(np => np.DeclaringType == type && np.ToEndMember.RelationshipMultiplicity == RelationshipMultiplicity.Many);\n    }\n    \n    public FunctionParameter GetReturnParameter(EdmFunction edmFunction)\n    {\n        ArgumentNotNull(edmFunction, \"edmFunction\");\n\n        var returnParamsProperty = edmFunction.GetType().GetProperty(\"ReturnParameters\");\n        return returnParamsProperty == null\n            ? edmFunction.ReturnParameter\n            : ((IEnumerable<FunctionParameter>)returnParamsProperty.GetValue(edmFunction, null)).FirstOrDefault();\n    }\n\n    public bool IsComposable(EdmFunction edmFunction)\n    {\n        ArgumentNotNull(edmFunction, \"edmFunction\");\n\n        var isComposableProperty = edmFunction.GetType().GetProperty(\"IsComposableAttribute\");\n        return isComposableProperty != null && (bool)isComposableProperty.GetValue(edmFunction, null);\n    }\n\n    public IEnumerable<FunctionImportParameter> GetParameters(EdmFunction edmFunction)\n    {\n        return FunctionImportParameter.Create(edmFunction.Parameters, _code, _ef);\n    }\n\n    public TypeUsage GetReturnType(EdmFunction edmFunction)\n    {\n        var returnParam = GetReturnParameter(edmFunction);\n        return returnParam == null ? null : _ef.GetElementType(returnParam.TypeUsage);\n    }\n    \n    public bool GenerateMergeOptionFunction(EdmFunction edmFunction, bool includeMergeOption)\n    {\n        var returnType = GetReturnType(edmFunction);\n        return !includeMergeOption && returnType != null && returnType.EdmType.BuiltInTypeKind == BuiltInTypeKind.EntityType;\n    }\n}\n\npublic static void ArgumentNotNull<T>(T arg, string name) where T : class\n{\n    if (arg == null)\n    {\n        throw new ArgumentNullException(name);\n    }\n}\n#>"
  },
  {
    "path": "SaintCoinach/Libra/LodestoneSystemDefine.cs",
    "content": "//------------------------------------------------------------------------------\n// <auto-generated>\n//     This code was generated from a template.\n//\n//     Manual changes to this file may cause unexpected behavior in your application.\n//     Manual changes to this file will be overwritten if the code is regenerated.\n// </auto-generated>\n//------------------------------------------------------------------------------\n\nnamespace SaintCoinach.Libra\n{\n    using System;\n    using System.Collections.Generic;\n    \n    public partial class LodestoneSystemDefine\n    {\n        public long Key { get; set; }\n        public string DefineName { get; set; }\n        public string DefineValue { get; set; }\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Libra/NotebookDivision.cs",
    "content": "//------------------------------------------------------------------------------\n// <auto-generated>\n//     This code was generated from a template.\n//\n//     Manual changes to this file may cause unexpected behavior in your application.\n//     Manual changes to this file will be overwritten if the code is regenerated.\n// </auto-generated>\n//------------------------------------------------------------------------------\n\nnamespace SaintCoinach.Libra\n{\n    using System;\n    using System.Collections.Generic;\n    \n    public partial class NotebookDivision\n    {\n        public long Key { get; set; }\n        public string Name_ja { get; set; }\n        public string Name_en { get; set; }\n        public string Name_fr { get; set; }\n        public string Name_de { get; set; }\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Libra/PlaceName.cs",
    "content": "//------------------------------------------------------------------------------\n// <auto-generated>\n//     This code was generated from a template.\n//\n//     Manual changes to this file may cause unexpected behavior in your application.\n//     Manual changes to this file will be overwritten if the code is regenerated.\n// </auto-generated>\n//------------------------------------------------------------------------------\n\nnamespace SaintCoinach.Libra\n{\n    using System;\n    using System.Collections.Generic;\n    \n    public partial class PlaceName\n    {\n        public long Key { get; set; }\n        public Nullable<long> region { get; set; }\n        public string SGL_ja { get; set; }\n        public string SGL_en { get; set; }\n        public string SGL_fr { get; set; }\n        public string SGL_de { get; set; }\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Libra/Quest.cs",
    "content": "//------------------------------------------------------------------------------\n// <auto-generated>\n//     This code was generated from a template.\n//\n//     Manual changes to this file may cause unexpected behavior in your application.\n//     Manual changes to this file will be overwritten if the code is regenerated.\n// </auto-generated>\n//------------------------------------------------------------------------------\n\nnamespace SaintCoinach.Libra\n{\n    using System;\n    using System.Collections.Generic;\n    \n    public partial class Quest\n    {\n        public long Key { get; set; }\n        public Nullable<long> Genre { get; set; }\n        public Nullable<long> Area { get; set; }\n        public string Name_ja { get; set; }\n        public string Name_en { get; set; }\n        public string Name_fr { get; set; }\n        public string Name_de { get; set; }\n        public Nullable<long> CompanyPointType { get; set; }\n        public Nullable<long> CompanyPointNum { get; set; }\n        public Nullable<long> Gil { get; set; }\n        public Nullable<long> Client { get; set; }\n        public Nullable<long> ExpBonus { get; set; }\n        public Nullable<long> Header { get; set; }\n        public Nullable<long> ClassLevel { get; set; }\n        public Nullable<long> ClassLevel2 { get; set; }\n        public Nullable<long> ClassJob { get; set; }\n        public Nullable<long> ClassJob2 { get; set; }\n        public Nullable<long> QuestLevelOffset { get; set; }\n        public Nullable<long> Sort { get; set; }\n        public Nullable<long> BeastTribe { get; set; }\n        public Nullable<long> BeastReputationValueNum { get; set; }\n        public Nullable<long> WebType { get; set; }\n        public byte[] data { get; set; }\n        public string path { get; set; }\n        public string Index_ja { get; set; }\n        public string Index_en { get; set; }\n        public string Index_fr { get; set; }\n        public string Index_de { get; set; }\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Libra/QuestWebEx.cs",
    "content": "//------------------------------------------------------------------------------\n// <auto-generated>\n//     This code was generated from a template.\n//\n//     Manual changes to this file may cause unexpected behavior in your application.\n//     Manual changes to this file will be overwritten if the code is regenerated.\n// </auto-generated>\n//------------------------------------------------------------------------------\n\nnamespace SaintCoinach.Libra\n{\n    using System;\n    using System.Collections.Generic;\n    \n    public partial class QuestWebEx\n    {\n        public long Key { get; set; }\n        public string Name_ja { get; set; }\n        public string Name_en { get; set; }\n        public string Name_fr { get; set; }\n        public string Name_de { get; set; }\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Libra/QuestWebType.cs",
    "content": "//------------------------------------------------------------------------------\n// <auto-generated>\n//     This code was generated from a template.\n//\n//     Manual changes to this file may cause unexpected behavior in your application.\n//     Manual changes to this file will be overwritten if the code is regenerated.\n// </auto-generated>\n//------------------------------------------------------------------------------\n\nnamespace SaintCoinach.Libra\n{\n    using System;\n    using System.Collections.Generic;\n    \n    public partial class QuestWebType\n    {\n        public long Key { get; set; }\n        public string Text_de { get; set; }\n        public string Text_en { get; set; }\n        public string Text_ja { get; set; }\n        public string Text_fr { get; set; }\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Libra/Quest_ClassJob.cs",
    "content": "//------------------------------------------------------------------------------\n// <auto-generated>\n//     This code was generated from a template.\n//\n//     Manual changes to this file may cause unexpected behavior in your application.\n//     Manual changes to this file will be overwritten if the code is regenerated.\n// </auto-generated>\n//------------------------------------------------------------------------------\n\nnamespace SaintCoinach.Libra\n{\n    using System;\n    using System.Collections.Generic;\n    \n    public partial class Quest_ClassJob\n    {\n        public long Quest_Key { get; set; }\n        public long ClassJob_Key { get; set; }\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Libra/Race.cs",
    "content": "//------------------------------------------------------------------------------\n// <auto-generated>\n//     This code was generated from a template.\n//\n//     Manual changes to this file may cause unexpected behavior in your application.\n//     Manual changes to this file will be overwritten if the code is regenerated.\n// </auto-generated>\n//------------------------------------------------------------------------------\n\nnamespace SaintCoinach.Libra\n{\n    using System;\n    using System.Collections.Generic;\n    \n    public partial class Race\n    {\n        public long Key { get; set; }\n        public string NameFemale_ja { get; set; }\n        public string NameFemale_en { get; set; }\n        public string NameFemale_fr { get; set; }\n        public string NameFemale_de { get; set; }\n        public string Name_ja { get; set; }\n        public string Name_en { get; set; }\n        public string Name_fr { get; set; }\n        public string Name_de { get; set; }\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Libra/Recipe.cs",
    "content": "//------------------------------------------------------------------------------\n// <auto-generated>\n//     This code was generated from a template.\n//\n//     Manual changes to this file may cause unexpected behavior in your application.\n//     Manual changes to this file will be overwritten if the code is regenerated.\n// </auto-generated>\n//------------------------------------------------------------------------------\n\nnamespace SaintCoinach.Libra\n{\n    using System;\n    using System.Collections.Generic;\n    \n    public partial class Recipe\n    {\n        public long Key { get; set; }\n        public Nullable<long> CanAutoCraft { get; set; }\n        public Nullable<long> CanHq { get; set; }\n        public Nullable<long> CraftItemId { get; set; }\n        public Nullable<long> CraftNum { get; set; }\n        public Nullable<long> CraftType { get; set; }\n        public Nullable<long> Level { get; set; }\n        public Nullable<long> levelView { get; set; }\n        public Nullable<long> levelDiff { get; set; }\n        public Nullable<long> Element { get; set; }\n        public Nullable<long> NeedCraftmanship { get; set; }\n        public Nullable<long> NeedControl { get; set; }\n        public Nullable<long> Number { get; set; }\n        public Nullable<long> NeedStatus { get; set; }\n        public Nullable<long> NeedEquipItem { get; set; }\n        public byte[] data { get; set; }\n        public string path { get; set; }\n        public string Index_ja { get; set; }\n        public string Index_en { get; set; }\n        public string Index_fr { get; set; }\n        public string Index_de { get; set; }\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Libra/RecipeElement.cs",
    "content": "//------------------------------------------------------------------------------\n// <auto-generated>\n//     This code was generated from a template.\n//\n//     Manual changes to this file may cause unexpected behavior in your application.\n//     Manual changes to this file will be overwritten if the code is regenerated.\n// </auto-generated>\n//------------------------------------------------------------------------------\n\nnamespace SaintCoinach.Libra\n{\n    using System;\n    using System.Collections.Generic;\n    \n    public partial class RecipeElement\n    {\n        public long Key { get; set; }\n        public string Name_ja { get; set; }\n        public string Name_en { get; set; }\n        public string Name_fr { get; set; }\n        public string Name_de { get; set; }\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Libra/Shop.cs",
    "content": "//------------------------------------------------------------------------------\n// <auto-generated>\n//     This code was generated from a template.\n//\n//     Manual changes to this file may cause unexpected behavior in your application.\n//     Manual changes to this file will be overwritten if the code is regenerated.\n// </auto-generated>\n//------------------------------------------------------------------------------\n\nnamespace SaintCoinach.Libra\n{\n    using System;\n    using System.Collections.Generic;\n    \n    public partial class Shop\n    {\n        public long Key { get; set; }\n        public string Name_ja { get; set; }\n        public string Name_en { get; set; }\n        public string Name_fr { get; set; }\n        public string Name_de { get; set; }\n        public Nullable<long> BeastTribe { get; set; }\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Libra/Status.cs",
    "content": "//------------------------------------------------------------------------------\n// <auto-generated>\n//     This code was generated from a template.\n//\n//     Manual changes to this file may cause unexpected behavior in your application.\n//     Manual changes to this file will be overwritten if the code is regenerated.\n// </auto-generated>\n//------------------------------------------------------------------------------\n\nnamespace SaintCoinach.Libra\n{\n    using System;\n    using System.Collections.Generic;\n    \n    public partial class Status\n    {\n        public long Key { get; set; }\n        public string Name_ja { get; set; }\n        public string Name_en { get; set; }\n        public string Name_fr { get; set; }\n        public string Name_de { get; set; }\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Libra/Title.cs",
    "content": "//------------------------------------------------------------------------------\n// <auto-generated>\n//     This code was generated from a template.\n//\n//     Manual changes to this file may cause unexpected behavior in your application.\n//     Manual changes to this file will be overwritten if the code is regenerated.\n// </auto-generated>\n//------------------------------------------------------------------------------\n\nnamespace SaintCoinach.Libra\n{\n    using System;\n    using System.Collections.Generic;\n    \n    public partial class Title\n    {\n        public long Key { get; set; }\n        public string Female_de { get; set; }\n        public string Female_en { get; set; }\n        public string Female_fr { get; set; }\n        public string Female_ja { get; set; }\n        public Nullable<bool> Front_de { get; set; }\n        public Nullable<bool> Front_en { get; set; }\n        public Nullable<bool> Front_fr { get; set; }\n        public Nullable<bool> Front_ja { get; set; }\n        public string Male_de { get; set; }\n        public string Male_en { get; set; }\n        public string Male_fr { get; set; }\n        public string Male_ja { get; set; }\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Libra/Tomestone.cs",
    "content": "//------------------------------------------------------------------------------\n// <auto-generated>\n//     This code was generated from a template.\n//\n//     Manual changes to this file may cause unexpected behavior in your application.\n//     Manual changes to this file will be overwritten if the code is regenerated.\n// </auto-generated>\n//------------------------------------------------------------------------------\n\nnamespace SaintCoinach.Libra\n{\n    using System;\n    using System.Collections.Generic;\n    \n    public partial class Tomestone\n    {\n        public long Key { get; set; }\n        public Nullable<long> CatalogId { get; set; }\n        public string path { get; set; }\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Libra/Town.cs",
    "content": "//------------------------------------------------------------------------------\n// <auto-generated>\n//     This code was generated from a template.\n//\n//     Manual changes to this file may cause unexpected behavior in your application.\n//     Manual changes to this file will be overwritten if the code is regenerated.\n// </auto-generated>\n//------------------------------------------------------------------------------\n\nnamespace SaintCoinach.Libra\n{\n    using System;\n    using System.Collections.Generic;\n    \n    public partial class Town\n    {\n        public long Key { get; set; }\n        public string Name_ja { get; set; }\n        public string Name_en { get; set; }\n        public string Name_fr { get; set; }\n        public string Name_de { get; set; }\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/OrderedBitConverter.cs",
    "content": "﻿using System;\n\nnamespace SaintCoinach {\n    /// <summary>\n    ///     Static class for reading values from byte buffers using a specified endianness.\n    /// </summary>\n    /// <remarks>\n    ///     This class is a wrapper around <see cref=\"BitConverter\" /> inverting the order of bytes, if necessary, before\n    ///     passing them on.\n    /// </remarks>\n    public static class OrderedBitConverter {\n        #region CheckEndian\n\n        /// <summary>\n        ///     Checks the requested endianness against the one used by the system, and reversed the byte buffer if neccessary.\n        /// </summary>\n        /// <param name=\"data\">Byte array containing the data to convert.</param>\n        /// <param name=\"bigEndian\">\n        ///     <c>true</c> if the data should be or is in big-endian order; <c>false</c> if it is in\n        ///     little-endian.\n        /// </param>\n        private static void CheckEndian(ref byte[] data, bool bigEndian) {\n            if (bigEndian == BitConverter.IsLittleEndian)\n                Array.Reverse(data);\n        }\n\n        #endregion\n\n        #region GetBytes\n\n        /// <summary>\n        ///     Returns the specified 16-bit signed integer as an array of bytes using the specified endianness.\n        /// </summary>\n        /// <param name=\"value\">The number to convert.</param>\n        /// <param name=\"bigEndian\">\n        ///     <c>true</c> if target data should be in big-endian order; <c>false</c> if it is in\n        ///     little-endian.\n        /// </param>\n        /// <returns>The specified 16-bit signed integer as an array of bytes using the specified endianness.</returns>\n        public static byte[] GetBytes(Int16 value, bool bigEndian) {\n            var ret = BitConverter.GetBytes(value);\n            CheckEndian(ref ret, bigEndian);\n            return ret;\n        }\n\n        /// <summary>\n        ///     Returns the specified 16-bit unsigned integer as an array of bytes using the specified endianness.\n        /// </summary>\n        /// <param name=\"value\">The number to convert.</param>\n        /// <param name=\"bigEndian\">\n        ///     <c>true</c> if target data should be in big-endian order; <c>false</c> if it is in\n        ///     little-endian.\n        /// </param>\n        /// <returns>The specified 16-bit unsigned integer as an array of bytes using the specified endianness.</returns>\n        public static byte[] GetBytes(UInt16 value, bool bigEndian) {\n            return GetBytes((Int16)value, bigEndian);\n        }\n\n        /// <summary>\n        ///     Returns the specified 32-bit signed integer as an array of bytes using the specified endianness.\n        /// </summary>\n        /// <param name=\"value\">The number to convert.</param>\n        /// <param name=\"bigEndian\">\n        ///     <c>true</c> if target data should be in big-endian order; <c>false</c> if it is in\n        ///     little-endian.\n        /// </param>\n        /// <returns>The specified 32-bit signed integer as an array of bytes using the specified endianness.</returns>\n        public static byte[] GetBytes(Int32 value, bool bigEndian) {\n            var ret = BitConverter.GetBytes(value);\n            CheckEndian(ref ret, bigEndian);\n            return ret;\n        }\n\n        /// <summary>\n        ///     Returns the specified 32-bit unsigned integer as an array of bytes using the specified endianness.\n        /// </summary>\n        /// <param name=\"value\">The number to convert.</param>\n        /// <param name=\"bigEndian\">\n        ///     <c>true</c> if target data should be in big-endian order; <c>false</c> if it is in\n        ///     little-endian.\n        /// </param>\n        /// <returns>The specified 32-bit unsigned integer as an array of bytes using the specified endianness.</returns>\n        public static byte[] GetBytes(UInt32 value, bool bigEndian) {\n            return GetBytes((Int32)value, bigEndian);\n        }\n\n        /// <summary>\n        ///     Returns the specified 64-bit signed integer as an array of bytes using the specified endianness.\n        /// </summary>\n        /// <param name=\"value\">The number to convert.</param>\n        /// <param name=\"bigEndian\">\n        ///     <c>true</c> if target data should be in big-endian order; <c>false</c> if it is in\n        ///     little-endian.\n        /// </param>\n        /// <returns>The specified 64-bit signed integer as an array of bytes using the specified endianness.</returns>\n        public static byte[] GetBytes(Int64 value, bool bigEndian) {\n            var ret = BitConverter.GetBytes(value);\n            CheckEndian(ref ret, bigEndian);\n            return ret;\n        }\n\n        /// <summary>\n        ///     Returns the specified 64-bit unsigned integer as an array of bytes using the specified endianness.\n        /// </summary>\n        /// <param name=\"value\">The number to convert.</param>\n        /// <param name=\"bigEndian\">\n        ///     <c>true</c> if target data should be in big-endian order; <c>false</c> if it is in\n        ///     little-endian.\n        /// </param>\n        /// <returns>The specified 64-bit unsigned integer as an array of bytes using the specified endianness.</returns>\n        public static byte[] GetBytes(UInt64 value, bool bigEndian) {\n            return GetBytes((Int64)value, bigEndian);\n        }\n\n        /// <summary>\n        ///     Returns the specified single-precision floating point value as an array of bytes using the specified endianness.\n        /// </summary>\n        /// <param name=\"value\">The number to convert.</param>\n        /// <param name=\"bigEndian\">\n        ///     <c>true</c> if target data should be in big-endian order; <c>false</c> if it is in\n        ///     little-endian.\n        /// </param>\n        /// <returns>The specified single-precision floating point value as an array of bytes using the specified endianness.</returns>\n        public static byte[] GetBytes(Single value, bool bigEndian) {\n            var ret = BitConverter.GetBytes(value);\n            CheckEndian(ref ret, bigEndian);\n            return ret;\n        }\n\n        /// <summary>\n        ///     Returns the specified double-precision floating point value as an array of bytes using the specified endianness.\n        /// </summary>\n        /// <param name=\"value\">The number to convert.</param>\n        /// <param name=\"bigEndian\">\n        ///     <c>true</c> if target data should be in big-endian order; <c>false</c> if it is in\n        ///     little-endian.\n        /// </param>\n        /// <returns>The specified double-precision floating point value as an array of bytes using the specified endianness.</returns>\n        public static byte[] GetBytes(Double value, bool bigEndian) {\n            var ret = BitConverter.GetBytes(value);\n            CheckEndian(ref ret, bigEndian);\n            return ret;\n        }\n\n        #endregion\n\n        #region ToX\n\n        /// <summary>\n        ///     Returns a 16-bit signed integer converted from two bytes at a specified position in a byte array using the\n        ///     specified endianness.\n        /// </summary>\n        /// <param name=\"buffer\">An array of bytes.</param>\n        /// <param name=\"offset\">The starting position within <c>buffer</c>.</param>\n        /// <param name=\"bigEndian\">\n        ///     <c>true</c> if target data should be in big-endian order; <c>false</c> if it is in\n        ///     little-endian.\n        /// </param>\n        /// <returns>\n        ///     A 16-bit signed integer converted from two bytes at a specified position in a byte array using the specified\n        ///     endianness.\n        /// </returns>\n        public static Int16 ToInt16(byte[] buffer, int offset, bool bigEndian) {\n            if (buffer == null)\n                throw new ArgumentNullException(\"buffer\");\n            if (offset < 0 || offset + 2 > buffer.Length)\n                throw new ArgumentOutOfRangeException(\"offset\");\n\n            var tmp = new byte[2];\n            Array.Copy(buffer, offset, tmp, 0, 2);\n            CheckEndian(ref tmp, bigEndian);\n            return BitConverter.ToInt16(tmp, 0);\n        }\n\n        /// <summary>\n        ///     Returns a 16-bit unsigned integer converted from two bytes at a specified position in a byte array using the\n        ///     specified endianness.\n        /// </summary>\n        /// <param name=\"buffer\">An array of bytes.</param>\n        /// <param name=\"offset\">The starting position within <c>buffer</c>.</param>\n        /// <param name=\"bigEndian\">\n        ///     <c>true</c> if target data should be in big-endian order; <c>false</c> if it is in\n        ///     little-endian.\n        /// </param>\n        /// <returns>\n        ///     A 16-bit unsigned integer converted from two bytes at a specified position in a byte array using the specified\n        ///     endianness.\n        /// </returns>\n        public static UInt16 ToUInt16(byte[] buffer, int offset, bool bigEndian) {\n            return (UInt16)ToInt16(buffer, offset, bigEndian);\n        }\n\n        /// <summary>\n        ///     Returns a 32-bit signed integer converted from four bytes at a specified position in a byte array using the\n        ///     specified endianness.\n        /// </summary>\n        /// <param name=\"buffer\">An array of bytes.</param>\n        /// <param name=\"offset\">The starting position within <c>buffer</c>.</param>\n        /// <param name=\"bigEndian\">\n        ///     <c>true</c> if target data should be in big-endian order; <c>false</c> if it is in\n        ///     little-endian.\n        /// </param>\n        /// <returns>\n        ///     A 32-bit signed integer converted from four bytes at a specified position in a byte array using the specified\n        ///     endianness.\n        /// </returns>\n        public static Int32 ToInt32(byte[] buffer, int offset, bool bigEndian) {\n            if (buffer == null)\n                throw new ArgumentNullException(\"buffer\");\n            if (offset < 0 || offset + 4 > buffer.Length)\n                throw new ArgumentOutOfRangeException(\"offset\");\n\n            var tmp = new byte[4];\n            Array.Copy(buffer, offset, tmp, 0, 4);\n            CheckEndian(ref tmp, bigEndian);\n            return BitConverter.ToInt32(tmp, 0);\n        }\n\n        /// <summary>\n        ///     Returns a 32-bit unsigned integer converted from four bytes at a specified position in a byte array using the\n        ///     specified endianness.\n        /// </summary>\n        /// <param name=\"buffer\">An array of bytes.</param>\n        /// <param name=\"offset\">The starting position within <c>buffer</c>.</param>\n        /// <param name=\"bigEndian\">\n        ///     <c>true</c> if target data should be in big-endian order; <c>false</c> if it is in\n        ///     little-endian.\n        /// </param>\n        /// <returns>\n        ///     A 32-bit unsigned integer converted from four bytes at a specified position in a byte array using the\n        ///     specified endianness.\n        /// </returns>\n        public static UInt32 ToUInt32(byte[] buffer, int offset, bool bigEndian) {\n            return (UInt32)ToInt32(buffer, offset, bigEndian);\n        }\n\n        /// <summary>\n        ///     Returns a 64-bit signed integer converted from eight bytes at a specified position in a byte array using the\n        ///     specified endianness.\n        /// </summary>\n        /// <param name=\"buffer\">An array of bytes.</param>\n        /// <param name=\"offset\">The starting position within <c>buffer</c>.</param>\n        /// <param name=\"bigEndian\">\n        ///     <c>true</c> if target data should be in big-endian order; <c>false</c> if it is in\n        ///     little-endian.\n        /// </param>\n        /// <returns>\n        ///     A 64-bit signed integer converted from eight bytes at a specified position in a byte array using the specified\n        ///     endianness.\n        /// </returns>\n        public static Int64 ToInt64(byte[] buffer, int offset, bool bigEndian) {\n            if (buffer == null)\n                throw new ArgumentNullException(\"buffer\");\n            if (offset < 0 || offset + 8 > buffer.Length)\n                throw new ArgumentOutOfRangeException(\"offset\");\n\n            var tmp = new byte[8];\n            Array.Copy(buffer, offset, tmp, 0, 8);\n            CheckEndian(ref tmp, bigEndian);\n            return BitConverter.ToInt64(tmp, 0);\n        }\n\n        /// <summary>\n        ///     Returns a 64-bit unsigned integer converted from eight bytes at a specified position in a byte array using the\n        ///     specified endianness.\n        /// </summary>\n        /// <param name=\"buffer\">An array of bytes.</param>\n        /// <param name=\"offset\">The starting position within <c>buffer</c>.</param>\n        /// <param name=\"bigEndian\">\n        ///     <c>true</c> if target data should be in big-endian order; <c>false</c> if it is in\n        ///     little-endian.\n        /// </param>\n        /// <returns>\n        ///     A 64-bit unsigned integer converted from eight bytes at a specified position in a byte array using the\n        ///     specified endianness.\n        /// </returns>\n        public static UInt64 ToUInt64(byte[] buffer, int offset, bool bigEndian) {\n            return (UInt64)ToInt64(buffer, offset, bigEndian);\n        }\n\n        /// <summary>\n        ///     Returns a single-precision floating point value converted from four bytes at a specified position in a byte array\n        ///     using the specified endianness.\n        /// </summary>\n        /// <param name=\"buffer\">An array of bytes.</param>\n        /// <param name=\"offset\">The starting position within <c>buffer</c>.</param>\n        /// <param name=\"bigEndian\">\n        ///     <c>true</c> if target data should be in big-endian order; <c>false</c> if it is in\n        ///     little-endian.\n        /// </param>\n        /// <returns>\n        ///     A single-precision floating point value converted from four bytes at a specified position in a byte array\n        ///     using the specified endianness.\n        /// </returns>\n        public static Single ToSingle(byte[] buffer, int offset, bool bigEndian) {\n            if (buffer == null)\n                throw new ArgumentNullException(\"buffer\");\n            if (offset < 0 || offset + 4 > buffer.Length)\n                throw new ArgumentOutOfRangeException(\"offset\");\n\n            var tmp = new byte[4];\n            Array.Copy(buffer, offset, tmp, 0, 4);\n            CheckEndian(ref tmp, bigEndian);\n            return BitConverter.ToSingle(tmp, 0);\n        }\n\n        /// <summary>\n        ///     Returns a double-precision floating point value converted from eight bytes at a specified position in a byte array\n        ///     using the specified endianness.\n        /// </summary>\n        /// <param name=\"buffer\">An array of bytes.</param>\n        /// <param name=\"offset\">The starting position within <c>buffer</c>.</param>\n        /// <param name=\"bigEndian\">\n        ///     <c>true</c> if target data should be in big-endian order; <c>false</c> if it is in\n        ///     little-endian.\n        /// </param>\n        /// <returns>\n        ///     A double-precision floating point value converted from eight bytes at a specified position in a byte array\n        ///     using the specified endianness.\n        /// </returns>\n        public static Double ToDouble(byte[] buffer, int offset, bool bigEndian) {\n            if (buffer == null)\n                throw new ArgumentNullException(\"buffer\");\n            if (offset < 0 || offset + 8 > buffer.Length)\n                throw new ArgumentOutOfRangeException(\"offset\");\n\n            var tmp = new byte[8];\n            Array.Copy(buffer, offset, tmp, 0, 8);\n            CheckEndian(ref tmp, bigEndian);\n            return BitConverter.ToDouble(tmp, 0);\n        }\n\n        #endregion\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Range.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\n\nnamespace SaintCoinach {\n    /// <summary>\n    ///     Helper structure to denote ranges.\n    /// </summary>\n    public struct Range {\n        #region Static\n\n        /// <summary>\n        ///     Empty range starting at 0.\n        /// </summary>\n        public static readonly Range Zero = new Range(0, 0);\n\n        #endregion\n\n        #region Fields\n\n        /// <summary>\n        ///     Length of the <see cref=\"Range\" />.\n        /// </summary>\n        private readonly int _Length;\n\n        /// <summary>\n        ///     Start of the <see cref=\"Range\" />.\n        /// </summary>\n        private readonly int _Start;\n\n        #endregion\n\n        #region Properties\n\n        /// <summary>\n        ///     Gets the end of the <see cref=\"Range\" />.\n        /// </summary>\n        /// <value>End of the <see cref=\"Range\" />.</value>\n        public int End { get { return _Start + _Length; } }\n\n        /// <summary>\n        ///     Gets the length of the <see cref=\"Range\" />.\n        /// </summary>\n        /// <value>Length of the <see cref=\"Range\" />.</value>\n        public int Length { get { return _Length; } }\n\n        /// <summary>\n        ///     Gets the start of the <see cref=\"Range\" />.\n        /// </summary>\n        /// <value>Start of the <see cref=\"Range\" />.</value>\n        public int Start { get { return _Start; } }\n\n        #endregion\n\n        #region Constructors\n\n        /// <summary>\n        ///     Initializes a new instance of the <see cref=\"Range\" /> struct.\n        /// </summary>\n        /// <param name=\"start\">Start of the <see cref=\"Range\" />.</param>\n        /// <param name=\"length\">Length of the <see cref=\"Range\" />.</param>\n        public Range(int start, int length) {\n            _Start = start;\n            _Length = length;\n        }\n\n        #endregion\n\n        /// <summary>\n        ///     Checks if a given value is contained inside this <see cref=\"Range\" />.\n        /// </summary>\n        /// <param name=\"value\">Value to check.</param>\n        /// <returns>Returns <c>true</c> if <c>value</c> is inside this <see cref=\"Range\" />; <c>false</c> otherwise.</returns>\n        public bool Contains(int value) {\n            return value >= _Start && value < End;\n        }\n\n        /// <summary>\n        ///     Checks if a given <see cref=\"Range\" /> intersects with this <see cref=\"Range\" />.\n        /// </summary>\n        /// <param name=\"other\"><see cref=\"Range\" /> to check.</param>\n        /// <returns>Returns <c>true</c> if <c>value</c> intersects with this <see cref=\"Range\" />; <c>false</c> otherwise.</returns>\n        public bool Intersects(Range other) {\n            return Contains(other._Start)\n                   || Contains(other.End - 1)\n                   || other.Contains(_Start)\n                   || other.Contains(End - 1);\n        }\n\n        /// <summary>\n        ///     Checks if a given value is contained inside any of the given <see cref=\"Range\" />.\n        /// </summary>\n        /// <param name=\"ranges\">Enumerable of <see cref=\"Range\" />s to check against.</param>\n        /// <param name=\"value\">Value to check.</param>\n        /// <returns>Returns <c>true</c> if <c>value</c> is inside any of the given <see cref=\"Range\" />s; <c>false</c> otherwise.</returns>\n        public static bool Contains(IEnumerable<Range> ranges, int value) {\n            return ranges.Any(_ => _.Contains(value));\n        }\n\n        /// <summary>\n        ///     Optimizes multiple ranges into the minimum number of ranges\n        ///     required to represent the same values.\n        /// </summary>\n        /// <param name=\"ranges\">The <see cref=\"Range\" />s to optimize.</param>\n        /// <returns>An array of the optimized ranges.</returns>\n        public static Range[] Combine(IEnumerable<Range> ranges) {\n            var arr = ranges.ToArray();\n            if (!arr.Any())\n                return new Range[0];\n\n            var combined = new List<Range>();\n\n            var ordered = arr.OrderBy(_ => _._Start);\n            var currentStart = ordered.First()._Start;\n            var currentEnd = currentStart;\n            foreach (var range in ordered) {\n                if (range._Start > currentEnd) {\n                    if (currentEnd > currentStart)\n                        combined.Add(new Range(currentStart, currentEnd - currentStart));\n\n                    currentStart = range._Start;\n                    currentEnd = range.End;\n                } else\n                    currentEnd = Math.Max(currentEnd, range.End);\n            }\n            if (currentEnd > currentStart)\n                combined.Add(new Range(currentStart, currentEnd - currentStart));\n\n            return combined.OrderBy(_ => _._Start).ToArray();\n        }\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/SaintCoinach.csproj",
    "content": "﻿<Project Sdk=\"Microsoft.NET.Sdk\">\n  <PropertyGroup>\n    <ProjectGuid>{8A7D65EB-3DE3-4631-B286-CC73A3126002}</ProjectGuid>\n    <TargetFramework>net7.0</TargetFramework>\n    <AssemblyTitle>SaintCoinach</AssemblyTitle>\n    <Product>SaintCoinach</Product>\n    <Copyright>Copyright © Rogueadyn 2014</Copyright>\n    <OutputPath>bin\\$(Configuration)\\</OutputPath>\n    <AllowUnsafeBlocks>true</AllowUnsafeBlocks>\n  </PropertyGroup>\n  <PropertyGroup Condition=\" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' \">\n    <DebugType>full</DebugType>\n  </PropertyGroup>\n  <PropertyGroup Condition=\" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' \">\n    <DebugType>pdbonly</DebugType>\n  </PropertyGroup>\n  <ItemGroup>\n    <ProjectReference Include=\"..\\DotSquish\\DotSquish.csproj\" />\n  </ItemGroup>\n  <ItemGroup>\n    <Content Include=\"Definitions\\*.json\">\n      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>\n    </Content>\n    <Content Include=\"Definitions\\game.ver\">\n      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>\n    </Content>\n  </ItemGroup>\n  <ItemGroup>\n    <PackageReference Include=\"BCnEncoder.Net\" Version=\"2.2.0\" />\n    <PackageReference Include=\"DotNetZip\" Version=\"1.16.0\" />\n    <PackageReference Include=\"EntityFramework\" Version=\"6.4.4\" />\n    <PackageReference Include=\"Newtonsoft.Json\" Version=\"13.0.3\" />\n    <PackageReference Include=\"System.Drawing.Common\" Version=\"7.0.0\" />\n    <PackageReference Include=\"System.Runtime.CompilerServices.Unsafe\" Version=\"6.0.0\" />\n    <PackageReference Include=\"System.Text.Encoding.CodePages\" Version=\"7.0.0\" />\n  </ItemGroup>\n</Project>"
  },
  {
    "path": "SaintCoinach/Sound/ScdAdpcmEntry.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace SaintCoinach.Sound {\n    public class ScdAdpcmEntry : ScdEntry {\n        #region Fields\n        private byte[] _Decoded;\n        #endregion\n\n        #region Constructor\n        internal ScdAdpcmEntry(ScdFile file, ScdEntryHeader header, int chunksOffset, int dataOffset)\n            : base(file, header) {\n            Decode(chunksOffset, dataOffset);\n        }\n        #endregion\n\n        public override byte[] GetDecoded() {\n            return _Decoded;\n        }\n\n        #region Decode\n        private void Decode(int chunksOffset, int dataOffset) {\n            const int WaveHeaderSize = 0x10;\n\n            var wavHeaderOffset = dataOffset;\n            var finalDataOffset = chunksOffset + Header.SamplesOffset;\n\n            _Decoded = new byte[0x1C + WaveHeaderSize + Header.DataSize];\n            var o = 0;\n            _Decoded[o++] = (byte)'R';\n            _Decoded[o++] = (byte)'I';\n            _Decoded[o++] = (byte)'F';\n            _Decoded[o++] = (byte)'F';\n\n            Array.Copy(BitConverter.GetBytes((int)(0x14 + WaveHeaderSize + Header.DataSize)), 0, _Decoded, o, 4);\n            o += 4;\n\n            _Decoded[o++] = (byte)'W';\n            _Decoded[o++] = (byte)'A';\n            _Decoded[o++] = (byte)'V';\n            _Decoded[o++] = (byte)'E';\n            _Decoded[o++] = (byte)'f';\n            _Decoded[o++] = (byte)'m';\n            _Decoded[o++] = (byte)'t';\n            _Decoded[o++] = (byte)' ';\n\n            Array.Copy(BitConverter.GetBytes((int)WaveHeaderSize), 0, _Decoded, o, 4);\n            o += 4;\n\n            Array.Copy(File._InputBuffer, wavHeaderOffset, _Decoded, o, WaveHeaderSize);\n            o += WaveHeaderSize;\n\n            _Decoded[o++] = (byte)'d';\n            _Decoded[o++] = (byte)'a';\n            _Decoded[o++] = (byte)'t';\n            _Decoded[o++] = (byte)'a';\n\n            Array.Copy(BitConverter.GetBytes((int)Header.DataSize), 0, _Decoded, o, 4);\n            o += 4;\n            Array.Copy(File._InputBuffer, finalDataOffset, _Decoded, o, Header.DataSize);\n        }\n        #endregion\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Sound/ScdCodec.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace SaintCoinach.Sound {\n    public enum ScdCodec : int {\n        None = 0x00,\n        OGG = 0x06,\n        MSADPCM = 0x0C\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Sound/ScdEntry.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace SaintCoinach.Sound {\n    public abstract class ScdEntry {\n        #region Fields\n\n        #endregion\n\n        #region Properties\n        public ScdFile File { get; private set; }\n        public ScdEntryHeader Header { get; private set; }\n        #endregion\n\n        #region Constructor\n        protected ScdEntry(ScdFile file, ScdEntryHeader header) {\n            this.File = file;\n            this.Header = header;\n        }\n        #endregion\n\n        public abstract byte[] GetDecoded();\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Sound/ScdEntryHeader.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Runtime.InteropServices;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace SaintCoinach.Sound {\n    [StructLayout(LayoutKind.Sequential)]\n    public struct ScdEntryHeader {\n        public int DataSize;\n        public int ChannelCount;\n        public int Frequency;\n        public ScdCodec Codec;\n        public int LoopStartSample;\n        public int LoopEndSample;\n        public int SamplesOffset;\n        public short AuxChunkCount;\n        public short Unknown1;\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Sound/ScdFile.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.IO;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace SaintCoinach.Sound {\n    public class ScdFile {\n        #region Fields\n        private bool _UseLittleEndian;\n        internal byte[] _InputBuffer;\n        #endregion\n\n        #region Properties\n        public IO.File SourceFile { get; private set; }\n        public ScdHeader ScdHeader { get; private set; }\n        public ScdEntryHeader[] EntryHeaders { get; private set; }\n        public ScdEntry[] Entries { get; private set; }\n        #endregion\n\n        #region Constructor\n        public ScdFile(IO.File sourceFile) {\n            this.SourceFile = sourceFile ?? throw new ArgumentNullException(\"sourceFile\");\n\n            Decode();\n        }\n        #endregion\n\n        #region Decode\n        private void Decode() {\n            _InputBuffer = SourceFile.GetData();\n\n            Init();\n\n            var fileHeaderSize = ReadInt16(0x0E);\n\n            ReadScdHeader(fileHeaderSize);\n            \n            var entryHeaders = new ScdEntryHeader[ScdHeader.EntryCount];\n            var entryChunkOffsets = new int[ScdHeader.EntryCount];\n            var entryDataOffsets = new int[ScdHeader.EntryCount];\n            for (var i = 0; i < ScdHeader.EntryCount; ++i) {\n                var headerOffset = ReadInt32(ScdHeader.EntryTableOffset + 4 * i);\n                entryHeaders[i] = ReadEntryHeader(headerOffset);\n\n                entryChunkOffsets[i] = headerOffset + System.Runtime.InteropServices.Marshal.SizeOf<ScdEntryHeader>();\n                entryDataOffsets[i] = entryChunkOffsets[i];\n                for (var j = 0; j < entryHeaders[i].AuxChunkCount; ++j)\n                    entryDataOffsets[i] += ReadInt32(entryDataOffsets[i] + 4);\n            }\n\n            this.Entries = new ScdEntry[ScdHeader.EntryCount];\n            for (var i = 0; i < ScdHeader.EntryCount; ++i)\n                this.Entries[i] = CreateEntry(entryHeaders[i], entryChunkOffsets[i], entryDataOffsets[i]);\n\n            _InputBuffer = null;\n        }\n        private void Init() {\n            // Check magic (SEDBSSCF)\n            if (ReadInt64(0, false) != 0x5345444253534346)\n                throw new InvalidDataException();\n\n            // Check endianness\n            var verBigEndian = ReadInt32(8, false);\n            var verLittleEndian = ReadInt32(8, true);\n\n            if (verBigEndian == 2 || verBigEndian == 3) {\n                _UseLittleEndian = false;\n            } else if (verLittleEndian == 2 || verLittleEndian == 3) {\n                _UseLittleEndian = true;\n            } else\n                throw new InvalidDataException();\n        }\n        private void ReadScdHeader(int offset) {\n            var h = new ScdHeader();\n\n            h.Unknown1Count = ReadInt16(offset + 0x00);\n            h.Unknown2Count = ReadInt16(offset + 0x02);\n            h.EntryCount = ReadInt16(offset + 0x04);\n            h.Unknown1 = ReadInt16(offset + 0x06);\n            h.Unknown1Offset = ReadInt32(offset + 0x08);\n            h.EntryTableOffset = ReadInt32(offset + 0x0C);\n            h.Unknown2Offset = ReadInt32(offset + 0x10);\n            h.Unknown2 = ReadInt32(offset + 0x14);\n            h.UnknownOffset1 = ReadInt32(offset + 0x18);\n\n            this.ScdHeader = h;\n        }\n        private ScdEntryHeader ReadEntryHeader(int offset) {\n            var h = new ScdEntryHeader();\n\n            h.DataSize = ReadInt32(offset + 0x00);\n            h.ChannelCount = ReadInt32(offset + 0x04);\n            h.Frequency = ReadInt32(offset + 0x08);\n            h.Codec = (ScdCodec)ReadInt32(offset + 0x0C);\n            h.LoopStartSample = ReadInt32(offset + 0x10);\n            h.LoopEndSample = ReadInt32(offset + 0x14);\n            h.SamplesOffset = ReadInt32(offset + 0x18);\n            h.AuxChunkCount = ReadInt16(offset + 0x1C);\n            h.Unknown1 = ReadInt16(offset + 0x1E);\n\n            return h;\n        }\n        private ScdEntry CreateEntry(ScdEntryHeader header, int chunksOffset, int dataOffset) {\n            if (header.DataSize == 0 || header.Codec == ScdCodec.None)\n                return null;\n\n            switch (header.Codec) {\n                case ScdCodec.OGG:\n                    return new ScdOggEntry(this, header, dataOffset);\n                case ScdCodec.MSADPCM:\n                    return new ScdAdpcmEntry(this, header, chunksOffset, dataOffset);\n                default:\n                    throw new NotSupportedException();\n            }\n        }\n        #endregion\n\n        #region Helpers\n\n        internal short ReadInt16(int offset) {\n            return ReadInt16(offset, _UseLittleEndian);\n        }\n\n        internal int ReadInt32(int offset) {\n            return ReadInt32(offset, _UseLittleEndian);\n        }\n\n        internal long ReadInt64(int offset) {\n            return ReadInt64(offset, _UseLittleEndian);\n        }\n\n        internal short ReadInt16(int offset, bool littleEndian) {\n            var buffer = new byte[2];\n\n            Array.Copy(_InputBuffer, offset, buffer, 0, buffer.Length);\n            if (BitConverter.IsLittleEndian != littleEndian)\n                Array.Reverse(buffer);\n\n            return BitConverter.ToInt16(buffer, 0);\n        }\n\n        internal int ReadInt32(int offset, bool littleEndian) {\n            var buffer = new byte[4];\n\n            Array.Copy(_InputBuffer, offset, buffer, 0, buffer.Length);\n            if (BitConverter.IsLittleEndian != littleEndian)\n                Array.Reverse(buffer);\n\n            return BitConverter.ToInt32(buffer, 0);\n        }\n\n        internal long ReadInt64(int offset, bool littleEndian) {\n            var buffer = new byte[8];\n\n            Array.Copy(_InputBuffer, offset, buffer, 0, buffer.Length);\n            if (BitConverter.IsLittleEndian != littleEndian)\n                Array.Reverse(buffer);\n\n            return BitConverter.ToInt64(buffer, 0);\n        }\n\n        #endregion\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Sound/ScdHeader.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Runtime.InteropServices;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace SaintCoinach.Sound {\n    [StructLayout(LayoutKind.Sequential)]\n    public struct ScdHeader {\n        public short Unknown1Count;\n        public short Unknown2Count;\n        public short EntryCount;\n        public short Unknown1;\n        public int Unknown1Offset;\n        public int EntryTableOffset;\n        public int Unknown2Offset;\n        public int Unknown2;\n        public int UnknownOffset1;\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Sound/ScdOggCryptType.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace SaintCoinach.Sound {\n    public enum ScdOggCryptType : short {\n        None = 0x0000,\n        VorbisHeaderXor = 0x2002,\n        FullXorUsingTable = 0x2003,\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Sound/ScdOggEntry.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace SaintCoinach.Sound {\n    public class ScdOggEntry : ScdEntry {\n        #region XorTable\n        private static readonly byte[] XorTable = new byte[256] { \n            0x3A, 0x32, 0x32, 0x32, 0x03, 0x7E, 0x12, 0xF7,\n            0xB2, 0xE2, 0xA2, 0x67, 0x32, 0x32, 0x22, 0x32,\n            0x32, 0x52, 0x16, 0x1B, 0x3C, 0xA1, 0x54, 0x7B,\n            0x1B, 0x97, 0xA6, 0x93, 0x1A, 0x4B, 0xAA, 0xA6,\n            0x7A, 0x7B, 0x1B, 0x97, 0xA6, 0xF7, 0x02, 0xBB,\n            0xAA, 0xA6, 0xBB, 0xF7, 0x2A, 0x51, 0xBE, 0x03,\n            0xF4, 0x2A, 0x51, 0xBE, 0x03, 0xF4, 0x2A, 0x51,\n            0xBE, 0x12, 0x06, 0x56, 0x27, 0x32, 0x32, 0x36,\n            0x32, 0xB2, 0x1A, 0x3B, 0xBC, 0x91, 0xD4, 0x7B,\n            0x58, 0xFC, 0x0B, 0x55, 0x2A, 0x15, 0xBC, 0x40,\n            0x92, 0x0B, 0x5B, 0x7C, 0x0A, 0x95, 0x12, 0x35,\n            0xB8, 0x63, 0xD2, 0x0B, 0x3B, 0xF0, 0xC7, 0x14,\n            0x51, 0x5C, 0x94, 0x86, 0x94, 0x59, 0x5C, 0xFC,\n            0x1B, 0x17, 0x3A, 0x3F, 0x6B, 0x37, 0x32, 0x32,\n            0x30, 0x32, 0x72, 0x7A, 0x13, 0xB7, 0x26, 0x60,\n            0x7A, 0x13, 0xB7, 0x26, 0x50, 0xBA, 0x13, 0xB4,\n            0x2A, 0x50, 0xBA, 0x13, 0xB5, 0x2E, 0x40, 0xFA,\n            0x13, 0x95, 0xAE, 0x40, 0x38, 0x18, 0x9A, 0x92,\n            0xB0, 0x38, 0x00, 0xFA, 0x12, 0xB1, 0x7E, 0x00,\n            0xDB, 0x96, 0xA1, 0x7C, 0x08, 0xDB, 0x9A, 0x91,\n            0xBC, 0x08, 0xD8, 0x1A, 0x86, 0xE2, 0x70, 0x39,\n            0x1F, 0x86, 0xE0, 0x78, 0x7E, 0x03, 0xE7, 0x64,\n            0x51, 0x9C, 0x8F, 0x34, 0x6F, 0x4E, 0x41, 0xFC,\n            0x0B, 0xD5, 0xAE, 0x41, 0xFC, 0x0B, 0xD5, 0xAE,\n            0x41, 0xFC, 0x3B, 0x70, 0x71, 0x64, 0x33, 0x32,\n            0x12, 0x32, 0x32, 0x36, 0x70, 0x34, 0x2B, 0x56,\n            0x22, 0x70, 0x3A, 0x13, 0xB7, 0x26, 0x60, 0xBA,\n            0x1B, 0x94, 0xAA, 0x40, 0x38, 0x00, 0xFA, 0xB2,\n            0xE2, 0xA2, 0x67, 0x32, 0x32, 0x12, 0x32, 0xB2,\n            0x32, 0x32, 0x32, 0x32, 0x75, 0xA3, 0x26, 0x7B,\n            0x83, 0x26, 0xF9, 0x83, 0x2E, 0xFF, 0xE3, 0x16,\n            0x7D, 0xC0, 0x1E, 0x63, 0x21, 0x07, 0xE3, 0x01,\n        };\n        #endregion\n\n        #region Fields\n        private byte[] _Decoded;\n        #endregion\n\n        #region Constructor\n        internal ScdOggEntry(ScdFile file, ScdEntryHeader header, int dataOffset)\n            : base(file, header) {\n            Decode(dataOffset);\n        }\n        #endregion\n\n        public override byte[] GetDecoded() {\n            return _Decoded;\n        }\n\n        #region Decode\n        private void Decode(int dataOffset) {\n            const int CryptTypeOffset = 0x00;\n            const int XorValueOffset = 0x02;\n            const int SeekTableSizeOffset = 0x10;\n            const int VorbisHeaderSizeOffset = 0x14;\n\n            var cryptType = (ScdOggCryptType)File.ReadInt16(dataOffset + CryptTypeOffset);\n\n            if (cryptType != ScdOggCryptType.None && cryptType != ScdOggCryptType.FullXorUsingTable && cryptType != ScdOggCryptType.VorbisHeaderXor)\n                throw new NotSupportedException();\n\n            var seekTableSize = File.ReadInt32(dataOffset + SeekTableSizeOffset);\n            var vorbisHeaderSize = File.ReadInt32(dataOffset + VorbisHeaderSizeOffset);\n\n            var vorbisHeaderOffset = dataOffset + 0x20 + seekTableSize;\n            var soundDataOffset = vorbisHeaderOffset + vorbisHeaderSize;\n\n            var vorbisHeader = new byte[vorbisHeaderSize];\n            Array.Copy(File._InputBuffer, vorbisHeaderOffset, vorbisHeader, 0, vorbisHeaderSize);\n\n            if (cryptType == ScdOggCryptType.VorbisHeaderXor) {\n                var xorVal = File._InputBuffer[dataOffset + XorValueOffset];\n                if (xorVal != 0) {\n                    for (var i = 0; i < vorbisHeader.Length; ++i)\n                        vorbisHeader[i] ^= xorVal;\n                }\n            }\n\n            _Decoded = new byte[vorbisHeader.Length + Header.DataSize];\n            Array.Copy(vorbisHeader, 0, _Decoded, 0, vorbisHeader.Length);\n            Array.Copy(File._InputBuffer, soundDataOffset, _Decoded, vorbisHeader.Length, Header.DataSize);\n\n            if (cryptType == ScdOggCryptType.FullXorUsingTable)\n                XorUsingTable();\n        }\n        private void XorUsingTable() {\n            var staticXor = (byte)(Header.DataSize & 0x7F);\n            var tableOffset = (byte)(Header.DataSize & 0x3F);\n            for (var i = 0; i < _Decoded.Length; ++i) {\n                _Decoded[i] ^= XorTable[(tableOffset + i) & 0xFF];\n                _Decoded[i] ^= staticXor;\n            }\n        }\n        #endregion\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Text/DecodeExpressionType.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace SaintCoinach.Text {\n    public enum DecodeExpressionType : byte {\n        GreaterThanOrEqualTo = 0xE0,    // Followed by two variables\n        GreaterThan = 0xE1,             // Followed by one variable\n        LessThanOrEqualTo = 0xE2,       // Followed by two variables\n        LessThan = 0xE3,                // Followed by one variable\n        Equal = 0xE4,                   // Followed by two variables\n        NotEqual = 0xE5,                // TODO: Probably\n\n        // TODO: I /think/ I got these right.\n        IntegerParameter = 0xE8,        // Followed by one variable\n        PlayerParameter = 0xE9,         // Followed by one variable\n        StringParameter = 0xEA,         // Followed by one variable\n        ObjectParameter = 0xEB,         // Followed by one variable\n        \n        Byte = 0xF0,\n        Int16_MinusOne = 0xF1,          // Followed by a Int16 that is one too high\n        Int16_1 = 0xF2,                 // Followed by a Int16\n        Int16_2 = 0xF4,                 // Followed by a Int16\n        Int24_MinusOne = 0xF5,          // Followed by a Int24 that is one too high\n        Int24 = 0xF6,                   // Followed by a Int24\n\n        Int24_SafeZero = 0xFA,          // Followed by a Int24, but 0xFF bytes set to 0 instead.\n        Int24_Lsh8 = 0xFD,              // Followed by a Int24, and left-shifted by 8 bits\n        Int32 = 0xFE,                   // Followed by a Int32\n\n        Decode = 0xFF,                  // Followed by length (inlcuding length) and data\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Text/DefaultEvaluationFunctionProvider.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace SaintCoinach.Text {\n    using Expressions;\n    using Nodes;\n    using Xiv;\n\n    public class DefaultEvaluationFunctionProvider : IEvaluationFunctionProvider {\n        public delegate IExpression GenericTagEvaluator(EvaluationParameters parameters, Nodes.GenericElement element);\n\n        #region Fields\n        private Dictionary<TagType, GenericTagEvaluator> _GenericTagEvaluators = new Dictionary<TagType, GenericTagEvaluator>();\n        private GenericTagEvaluator _DefaultGenericTagEvaluator;\n        #endregion\n\n        #region Properties\n        public XivCollection Data { get; private set; }\n        public GenericTagEvaluator DefaultGenericTagEvaluator {\n            get { return _DefaultGenericTagEvaluator; }\n            set {\n                if (value == null)\n                    throw new ArgumentNullException(\"value\");\n                _DefaultGenericTagEvaluator = value;\n            }\n        }\n        #endregion\n\n        #region Constructor\n        public DefaultEvaluationFunctionProvider(XivCollection data) {\n            if (data == null)\n                throw new ArgumentNullException(\"data\");\n            this.Data = data;\n\n            this.DefaultGenericTagEvaluator = EvaluateDefaultGenericElement;\n\n            SetEvaluator(TagType.Time, EvaluateTime);\n            SetEvaluator(TagType.Sheet, EvaluateSheet);\n            SetEvaluator(TagType.SheetDe, EvaluateSheetWithAttributive);\n            SetEvaluator(TagType.SheetEn, EvaluateSheetWithAttributive);\n            SetEvaluator(TagType.SheetFr, EvaluateSheetWithAttributive);\n            SetEvaluator(TagType.SheetJa, EvaluateSheetWithAttributive);\n            SetEvaluator(TagType.Value, (p, e) => e.Content.TryEvaluate(p));\n            SetEvaluator(TagType.TwoDigitValue, EvaluateTwoDigitValue);\n            SetEvaluator(TagType.ZeroPaddedValue, EvaluateZeroPaddedValue);\n        }\n        #endregion\n\n        #region Evals\n        public void SetEvaluator(TagType tag, GenericTagEvaluator evaluator) {\n            if (_GenericTagEvaluators.ContainsKey(tag))\n                _GenericTagEvaluators[tag] = evaluator;\n            else\n                _GenericTagEvaluators.Add(tag, evaluator);\n        }\n        #endregion\n\n        #region Generic\n\n        public IExpression EvaluateGenericElement(EvaluationParameters parameters, Nodes.GenericElement element) {\n            GenericTagEvaluator eval;\n            _GenericTagEvaluators.TryGetValue(element.Tag, out eval);\n            return (eval ?? DefaultGenericTagEvaluator)(parameters, element);\n        }\n        protected virtual IExpression EvaluateDefaultGenericElement(EvaluationParameters parameters, GenericElement element) {\n            var items = new List<IExpression>();\n            var hasArgs = element.Arguments.Any();\n            items.Add(new GenericExpression(StringTokens.TagOpen + element.Tag.ToString()));\n            if (hasArgs)\n                items.Add(new SurroundedExpression(StringTokens.ArgumentsOpen, new ExpressionCollection(element.Arguments.Select(_ => _.TryEvaluate(parameters))) { Separator = StringTokens.ArgumentsSeperator }, StringTokens.ArgumentsClose));\n\n            if (element.Content == null) {\n                items.Add(new GenericExpression(StringTokens.ElementClose + StringTokens.TagClose));\n            } else {\n                items.Add(new GenericExpression(StringTokens.TagClose));\n                items.Add(element.Content.TryEvaluate(parameters));\n                items.Add(new GenericExpression(StringTokens.TagOpen + StringTokens.ElementClose + element.Tag.ToString() + StringTokens.TagClose));\n            }\n\n            return new ExpressionCollection(items);\n        }\n        protected virtual IExpression EvaluateTwoDigitValue(EvaluationParameters parameters, Nodes.GenericElement element) {\n            var eval = element.Content.TryEvaluate(parameters);\n            var intVal = ToInteger(eval);\n            return new GenericExpression(intVal.ToString(\"D2\"));\n        }\n        protected virtual IExpression EvaluateZeroPaddedValue(EvaluationParameters parameters, Nodes.GenericElement element) {\n            var lenEval = element.Arguments.First().TryEvaluate(parameters);\n            var len = ToInteger(lenEval);\n            var eval = element.Content.TryEvaluate(parameters);\n            var intVal = ToInteger(eval);\n\n            return new GenericExpression(intVal.ToString(\"D\" + len.ToString()));\n        }\n        protected virtual IExpression EvaluateTime(EvaluationParameters parameters, Nodes.GenericElement element) {\n            /* Appears to set values for an input time.\n             * - 222 / DEh  Year\n             * - 221 / DDh  Month\n             * - 220 / DCh  Day of week\n             * - 219 / DBh  Day of month\n             * - 218 / DAh  Hour\n             * - 217 / D9h  Minute\n             */\n\n            var argEval = element.Arguments.First().TryEvaluate(parameters);\n            var argInt = ToInteger(argEval);\n\n            var utcTime = EorzeaDateTime.Zero.AddSeconds(argInt);\n            var localTime = utcTime.ToLocalTime();\n\n            parameters.TopLevelParameters[0xDE] = localTime.Year;\n            parameters.TopLevelParameters[0xDD] = localTime.Month;\n            parameters.TopLevelParameters[0xDC] = 1 + (int)localTime.DayOfWeek;\n            parameters.TopLevelParameters[0xDB] = localTime.Day;\n            parameters.TopLevelParameters[0xDA] = localTime.Hour;\n            parameters.TopLevelParameters[0xD9] = localTime.Minute;\n\n            return GenericExpression.Empty;\n        }\n\n        protected virtual IExpression EvaluateSheet(EvaluationParameters parameters, Nodes.GenericElement element) {\n            var evalArgs = element.Arguments.Select(_ => _.TryEvaluate(parameters)).ToArray();\n            if (evalArgs.Length < 2)\n                throw new InvalidOperationException();\n            var sheetName = evalArgs[0].ToString();\n            var rowKey = ToInteger(evalArgs[1]);\n            var colKey = 0;\n            if (evalArgs.Length > 2)\n                colKey = ToInteger(evalArgs[2]);\n\n            var row = Data.GetSheet(sheetName)[rowKey];\n            var value = row[colKey];\n            if (value is INode) {\n                var innerParams = new EvaluationParameters(parameters);\n                innerParams.InputParameters.Clear();\n                for (var i = 3; i < evalArgs.Length; ++i)\n                    innerParams.InputParameters[i - 2] = evalArgs[i];\n\n\n                value = EvaluationHelper.TryEvaluate((INode)value, innerParams);\n            }\n\n            return new GenericExpression(new ObjectWithDisplay(value, row));\n        }\n\n        const string AttributiveSheetName = \"Attributive\";\n        static readonly Dictionary<TagType, int> AttributiveColumnOffsets = new Dictionary<TagType, int> {\n            { TagType.SheetJa, 0 },\n            { TagType.SheetEn, 2 },\n            { TagType.SheetDe, 8 },\n            { TagType.SheetFr, 24 },\n        };\n        static readonly Dictionary<TagType, Ex.Language> TagToLanguageMap = new Dictionary<TagType, Ex.Language> {\n            { TagType.SheetJa, Ex.Language.Japanese },\n            { TagType.SheetEn, Ex.Language.English },\n            { TagType.SheetDe, Ex.Language.German },\n            { TagType.SheetFr, Ex.Language.French },\n        };\n        protected virtual IExpression EvaluateSheetWithAttributive(EvaluationParameters parameters, Nodes.GenericElement element) {\n            var evalArgs = element.Arguments.Select(_ => _.TryEvaluate(parameters)).ToArray();\n            if (evalArgs.Length < 3)\n                throw new InvalidOperationException();\n\n            var lang = TagToLanguageMap[element.Tag];\n\n            var sheetName = evalArgs[0].ToString();\n            var attributiveRowKey = ToInteger(evalArgs[1]);\n            var rowKey = ToInteger(evalArgs[2]);\n\n            var columnKey = 0;\n            if (evalArgs.Length > 3)\n                columnKey = ToInteger(evalArgs[3]);\n\n            var attributiveColumnKey = AttributiveColumnOffsets[element.Tag];\n            if (evalArgs.Length > 4)\n                attributiveColumnKey += ToInteger(evalArgs[4]);\n\n            var row = Data.GetSheet(sheetName)[rowKey];\n            object value;\n            if (row is Ex.IMultiRow)\n                value = ((Ex.IMultiRow)row)[columnKey, lang];\n            else\n                value = row[columnKey];\n\n            var attributiveRow = Data.GetSheet(AttributiveSheetName)[attributiveRowKey];\n            object attributiveValue;\n            if (attributiveRow is Ex.IMultiRow)\n                attributiveValue = ((Ex.IMultiRow)attributiveRow)[attributiveColumnKey, lang];\n            else\n                attributiveValue = attributiveRow[attributiveColumnKey];\n\n            var innerParams = new EvaluationParameters(parameters);\n            innerParams.InputParameters.Clear();\n            for (var i = 5; i < evalArgs.Length; ++i)\n                innerParams.InputParameters[i - 2] = evalArgs[i];\n\n            if (value is INode)\n                value = EvaluationHelper.TryEvaluate((INode)value, innerParams);\n            if (attributiveValue is INode)\n                attributiveValue = EvaluationHelper.TryEvaluate((INode)attributiveValue, innerParams);\n\n            return new SurroundedExpression(new ObjectWithDisplay(attributiveValue, attributiveRow), new ObjectWithDisplay(value, row), null);\n        }\n\n        #endregion\n\n        #region Compare\n        public bool Compare(EvaluationParameters parameters, DecodeExpressionType comparisonType, INode left, INode right) {\n            var evalLeft = left.TryEvaluate(parameters);\n            var evalRight = right.TryEvaluate(parameters);\n\n            switch (comparisonType) {\n                case DecodeExpressionType.GreaterThanOrEqualTo: {\n                        var iLeft = ToInteger(evalLeft);\n                        var iRight = ToInteger(evalRight);\n                        return iLeft >= iRight;\n                    }\n                case DecodeExpressionType.GreaterThan: {\n                        var iLeft = ToInteger(evalLeft);\n                        var iRight = ToInteger(evalRight);\n                        return iLeft > iRight;\n                    }\n                case DecodeExpressionType.LessThanOrEqualTo: {\n                        var iLeft = ToInteger(evalLeft);\n                        var iRight = ToInteger(evalRight);\n                        return iLeft <= iRight;\n                    }\n                case DecodeExpressionType.LessThan: {\n                        var iLeft = ToInteger(evalLeft);\n                        var iRight = ToInteger(evalRight);\n                        return iLeft < iRight;\n                    }\n                case DecodeExpressionType.Equal:\n                    return ExpressionsEqual(evalLeft, evalRight);\n                case DecodeExpressionType.NotEqual:\n                    return !ExpressionsEqual(evalLeft, evalRight);\n                default:\n                    throw new NotSupportedException();\n            }\n        }\n\n        protected bool ExpressionsEqual(IExpression left, IExpression right) {\n            var checkLeft = GetFinalObect(left);\n            var checkRight = GetFinalObect(right);\n            return EqualsEx(checkLeft, checkRight);\n        }\n\n        protected object GetFinalObect(IExpression expr) {\n            object result = expr;\n            if (expr is IValueExpression) {\n                result = ((IValueExpression)expr).Value;\n\n                while (result is ObjectWithDisplay)\n                    result = ((ObjectWithDisplay)result).Object;\n\n                if (result is IExpression)\n                    result = GetFinalObect((IExpression)result);\n            }\n\n            return result;\n        }\n\n        protected bool EqualsEx(object left, object right) {\n            if (left == null && right == null)\n                return true;\n            if (left == null || right == null)\n                return false;\n\n            if (object.ReferenceEquals(left, right))\n                return true;\n            if (object.Equals(left, right))\n                return true;\n\n            if (left is Ex.IRow && right is Ex.IRow) {\n                var lRow = left as Ex.IRow;\n                var rRow = right as Ex.IRow;\n\n                return lRow.Key == rRow.Key && lRow.Sheet.Name == rRow.Sheet.Name;\n            }\n\n            int iLeft, iRight;\n            if (TryConvert(left, out iLeft) && TryConvert(right, out iRight))\n                return iLeft == iRight;\n\n            if (!(left is string) && !(left is string))\n                return false;\n            var sLeft = left.ToString();\n            var sRight = right.ToString();\n\n            return string.Equals(sLeft, sRight);\n        }\n        \n        #endregion\n\n        #region IEvaluationFunctionProvider Members\n\n        public bool ToBoolean(IExpression value) {\n            if (value == null)\n                return false;\n            var asValue = value as IValueExpression;\n            if (asValue != null) {\n                bool boolVal;\n                if (TryConvert(asValue.Value, out boolVal))\n                    return boolVal;\n            }\n\n            var str = value.ToString().Trim();\n\n            bool b;\n            if (bool.TryParse(str, out b))\n                return b;\n\n            int i;\n            if (int.TryParse(str, out i))\n                return i != 0;\n\n            return str.Length > 0;\n        }\n\n        public int ToInteger(IExpression value) {\n            int result;\n            if (!TryConvertToInteger(value, out result))\n                throw new InvalidOperationException();\n            return result;\n        }\n        public bool TryConvertToInteger(IExpression value, out int result) {\n            if (value == null) {\n                result = 0;\n                return true;\n            }\n            var asValue = value as IValueExpression;\n            if (asValue != null) {\n                int intVal;\n                if (TryConvert(asValue.Value, out intVal)) {\n                    result = intVal;\n                    return true;\n                }\n            }\n\n            var str = value.ToString().Trim();\n\n            bool b;\n            if (bool.TryParse(str, out b)) {\n                result = b ? 1 : 0;\n                return true;\n            }\n\n            if (int.TryParse(str, out result))\n                return true;\n\n            result = 0;\n            return false;\n        }\n\n        bool TryConvert(object value, out bool result) {\n            while (value is ObjectWithDisplay)\n                value = (ObjectWithDisplay)value;\n\n            if (value == null) {\n                result = false;\n                return true;\n            }\n\n            if (value is IExpression) {\n                result = ToBoolean((IExpression)value);\n                return true;\n            }\n\n            string asStr = value as string;\n            if (asStr == null) {\n                if (value is bool) {\n                    result = (bool)value;\n                    return true;\n                }\n                if (value is int) {\n                    result = ((int)value) != 0;\n                    return true;\n                }\n\n                asStr = value.ToString();\n            }\n\n            if (bool.TryParse(asStr, out result))\n                return true;\n\n            int intVal;\n            if (int.TryParse(asStr, out intVal)) {\n                result = intVal != 0;\n                return true;\n            }\n\n            result = false;\n            return false;\n        }\n\n        bool TryConvert(object value, out int result) {\n            while (value is ObjectWithDisplay)\n                value = (ObjectWithDisplay)value;\n\n            if (value == null) {\n                result = 0;\n                return true;\n            }\n\n            if (value is IExpression) {\n                result = ToInteger((IExpression)value);\n                return true;\n            }\n\n            string asStr = value as string;\n            if (asStr == null) {\n                if (value is bool) {\n                    result = ((bool)value) ? 1 : 0;\n                    return true;\n                }\n                if (value is int) {\n                    result = (int)value;\n                    return true;\n                }\n                if (value is Ex.IRow) {\n                    result = ((Ex.IRow)value).Key;\n                    return true;\n                }\n\n                asStr = value.ToString();\n            }\n\n            bool boolVal;\n            if (bool.TryParse(asStr, out boolVal)) {\n                result = boolVal ? 1 : 0;\n                return true;\n            }\n\n            if (int.TryParse(asStr, out result))\n                return true;\n\n\n            result = 0;\n            return false;\n        }\n\n        #endregion\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Text/EvaluationHelper.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace SaintCoinach.Text {\n    using Nodes;\n\n    static class EvaluationHelper {\n        public static IExpression TryEvaluate(this INode self, EvaluationParameters parameters) {\n            if (self == null)\n                return null;\n            if (self.Flags.HasFlag(NodeFlags.IsExpression))\n                return ((IExpressionNode)self).Evaluate(parameters);\n            return new Expressions.GenericExpression(self);\n        }\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Text/EvaluationParameters.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace SaintCoinach.Text {\n    public class EvaluationParameters {\n        #region Fields\n        private object _FallbackValue = null;\n        private Dictionary<DecodeExpressionType, Parameters.ParameterBase> _Parameters = new Dictionary<DecodeExpressionType, Parameters.ParameterBase>();\n        private IEvaluationFunctionProvider _FunctionProvider;\n        private Parameters.ParameterBase _TopLevelParameters = new Parameters.ParameterBase();\n        #endregion\n\n        #region Properties\n        public object FallbackValue { get { return _FallbackValue; } set { _FallbackValue = value; } }\n        public IEvaluationFunctionProvider FunctionProvider { get { return _FunctionProvider; } }\n        public Parameters.ParameterBase TopLevelParameters { get { return _TopLevelParameters; } }\n        public Parameters.ParameterBase InputParameters { get { return _Parameters[DecodeExpressionType.IntegerParameter]; } }\n        public Parameters.PlayerParameters PlayerParameters { get { return (Parameters.PlayerParameters)_Parameters[DecodeExpressionType.PlayerParameter]; } }\n        public Parameters.ParameterBase StringParameters { get { return _Parameters[DecodeExpressionType.StringParameter]; } }\n        public Parameters.ObjectParameters ObjectParameters { get { return (Parameters.ObjectParameters)_Parameters[DecodeExpressionType.ObjectParameter]; } }\n        #endregion\n\n        #region Constructor\n        public EvaluationParameters(IEvaluationFunctionProvider functionProvider) {\n            if (functionProvider == null)\n                throw new ArgumentNullException(\"functionProvider\");\n            _FunctionProvider = functionProvider;\n            _Parameters.Add(DecodeExpressionType.IntegerParameter, new Parameters.ParameterBase() { FallbackValue = 0 });\n            _Parameters.Add(DecodeExpressionType.PlayerParameter, new Parameters.PlayerParameters());\n            _Parameters.Add(DecodeExpressionType.StringParameter, new Parameters.ParameterBase() { FallbackValue = string.Empty });\n            _Parameters.Add(DecodeExpressionType.ObjectParameter, new Parameters.ObjectParameters());\n        }\n        public EvaluationParameters(EvaluationParameters copyFrom) {\n            if (copyFrom == null)\n                throw new ArgumentNullException(\"copyFrom\");\n            _FunctionProvider = copyFrom.FunctionProvider;\n            _TopLevelParameters = new Parameters.ParameterBase(copyFrom.TopLevelParameters);\n            _Parameters.Add(DecodeExpressionType.IntegerParameter, new Parameters.ParameterBase(copyFrom._Parameters[DecodeExpressionType.IntegerParameter]));\n            _Parameters.Add(DecodeExpressionType.PlayerParameter, new Parameters.PlayerParameters(copyFrom._Parameters[DecodeExpressionType.PlayerParameter]));\n            _Parameters.Add(DecodeExpressionType.StringParameter, new Parameters.ParameterBase(copyFrom._Parameters[DecodeExpressionType.StringParameter]));\n            _Parameters.Add(DecodeExpressionType.ObjectParameter, new Parameters.ObjectParameters(copyFrom._Parameters[DecodeExpressionType.ObjectParameter]));\n        }\n        #endregion\n\n        #region Indexer\n        public object this[DecodeExpressionType type, int index] {\n            get {\n                Parameters.ParameterBase pb;\n                if (!_Parameters.TryGetValue(type, out pb))\n                    return FallbackValue;\n                return pb[index];\n            }\n            set {\n                Parameters.ParameterBase pb;\n                if (!_Parameters.TryGetValue(type, out pb))\n                    throw new NotSupportedException();\n                pb[index] = value;\n            }\n        }\n        #endregion\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Text/Expressions/CloseTag.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace SaintCoinach.Text.Expressions {\n    public class CloseTag : IExpression {\n        private readonly TagType _Tag;\n\n        public TagType Tag { get { return _Tag; } }\n\n        public CloseTag(TagType tag) {\n            _Tag = tag;\n        }\n\n        #region IExpression Members\n\n        public override string ToString() {\n            var sb = new StringBuilder();\n            ToString(sb);\n            return sb.ToString();\n        }\n        public void ToString(StringBuilder output) {\n            output.Append(StringTokens.TagOpen);\n            output.Append(StringTokens.ElementClose);\n            output.Append(Tag);\n            output.Append(StringTokens.TagClose);\n        }\n\n        #endregion\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Text/Expressions/ExpressionCollection.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace SaintCoinach.Text.Expressions {\n    public class ExpressionCollection : IExpression {\n        private readonly IExpression[] _Children;\n\n        public IEnumerable<IExpression> Children { get { return _Children; } }\n        public string Separator { get; set; }\n\n        public ExpressionCollection(params IExpression[] children) : this((IEnumerable<IExpression>)children) { }\n        public ExpressionCollection(IEnumerable<IExpression> children) {\n            if (children == null)\n                _Children = new IExpression[0];\n            else\n                _Children = children.ToArray();\n        }\n\n        public override string ToString() {\n            var sb = new StringBuilder();\n            ToString(sb);\n            return sb.ToString();\n        }\n        public void ToString(StringBuilder output) {\n            var addSeparator = false;\n            foreach (var c in Children) {\n                if (addSeparator)\n                    output.Append(Separator);\n                else\n                    addSeparator = true;\n\n                if (c != null)\n                    c.ToString(output);\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Text/Expressions/GenericExpression.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace SaintCoinach.Text.Expressions {\n    public class GenericExpression : IValueExpression {\n        public static readonly GenericExpression Empty = new GenericExpression(null);\n\n        private readonly object _Value;\n\n        public object Value { get { return _Value; } }\n\n        public GenericExpression(object value) {\n            _Value = value;\n        }\n\n        public override string ToString() {\n            if (Value == null)\n                return string.Empty;\n            return Value.ToString();\n        }\n        public void ToString(StringBuilder output) {\n            output.Append(Value);\n        }\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Text/Expressions/IValueExpression.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace SaintCoinach.Text.Expressions {\n    public interface IValueExpression : IExpression {\n        object Value { get; }\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Text/Expressions/ObjectWithDisplay.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace SaintCoinach.Text.Expressions {\n    public class ObjectWithDisplay {\n        private readonly object _Display;\n        private readonly object _Object;\n\n        public object Display { get { return _Display; } }\n        public object Object { get { return _Object; } }\n\n        public ObjectWithDisplay(object display, object obj) {\n            if (display == null)\n                throw new ArgumentNullException(\"display\");\n            if (obj == null)\n                throw new ArgumentNullException(\"obj\");\n            _Display = display;\n            _Object = obj;\n        }\n\n        public override string ToString() {\n            if (Display == null)\n                return string.Empty;\n            return Display.ToString();\n        }\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Text/Expressions/OpenTag.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace SaintCoinach.Text.Expressions {\n    public class OpenTag : IExpression {\n        private readonly TagType _Tag;\n        private readonly IExpression[] _Arguments;\n\n        public TagType Tag { get { return _Tag; } }\n        public IEnumerable<IExpression> Arguments { get { return _Arguments; } }\n\n        public OpenTag(TagType tag, IEnumerable<IExpression> arguments) {\n            _Tag = tag;\n            if (arguments == null)\n                _Arguments = new IExpression[0];\n            else\n                _Arguments = arguments.ToArray();\n        }\n\n        #region IExpression Members\n\n        public override string ToString() {\n            var sb = new StringBuilder();\n            ToString(sb);\n            return sb.ToString();\n        }\n        public void ToString(StringBuilder output) {\n            output.Append(StringTokens.TagOpen);\n            output.Append(Tag);\n\n            if (_Arguments.Length > 0) {\n                output.Append(StringTokens.ArgumentsOpen);\n                for (var i = 0; i < _Arguments.Length; ++i) {\n                    if (i > 0)\n                        output.Append(StringTokens.ArgumentsSeperator);\n                    _Arguments[i].ToString(output);\n                }\n                output.Append(StringTokens.ArgumentsClose);\n            }\n\n            output.Append(StringTokens.TagClose);\n        }\n\n        #endregion\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Text/Expressions/SurroundedExpression.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace SaintCoinach.Text.Expressions {\n    public class SurroundedExpression : IValueExpression {\n        private readonly object _Prefix;\n        private readonly object _Value;\n        private readonly object _Suffix;\n\n        public object Prefix { get { return _Prefix; } }\n        public object Value { get { return _Value; } }\n        public object Suffix { get { return _Suffix; } }\n\n        public SurroundedExpression(object prefix, object value, object suffix) {\n            _Prefix = prefix;\n            _Value = value;\n            _Suffix = suffix;\n        }\n\n        public override string ToString() {\n            var sb = new StringBuilder();\n            ToString(sb);\n            return sb.ToString();\n        }\n        public void ToString(StringBuilder output) {\n            output.Append(Prefix);\n            output.Append(Value);\n            output.Append(Suffix);\n        }\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Text/IEvaluationFunctionProvider.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace SaintCoinach.Text {\n    using Nodes;\n\n    public interface IEvaluationFunctionProvider {\n        bool Compare(EvaluationParameters parameters, DecodeExpressionType comparisonType, INode left, INode right);\n        IExpression EvaluateGenericElement(EvaluationParameters parameters, Nodes.GenericElement element);\n        bool ToBoolean(IExpression value);\n\n        bool TryConvertToInteger(IExpression value, out int result);\n        int ToInteger(IExpression value);\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Text/IExpression.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace SaintCoinach.Text {\n    public interface IExpression {\n        void ToString(StringBuilder output);\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Text/INode.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace SaintCoinach.Text {\n    public interface INode {\n        TagType Tag { get; }\n        NodeFlags Flags { get; }\n        void ToString(StringBuilder builder);\n        T Accept<T>(SaintCoinach.Text.Nodes.INodeVisitor<T> visitor);\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Text/IntegerType.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace SaintCoinach.Text {\n    public enum IntegerType {\n        Byte = 0xF0,\n        ByteTimes256 = 0xF1,\n        Int16 = 0xF2,\n        Int24 = 0xFA,\n        Int32 = 0xFE,\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Text/NodeFlags.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace SaintCoinach.Text {\n    [Flags]\n    public enum NodeFlags {\n        HasArguments,\n        HasChildren,\n        IsExpression,\n        IsConditional,\n        IsStatic,\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Text/Nodes/ArgumentCollection.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace SaintCoinach.Text.Nodes {\n    public class ArgumentCollection : IEnumerable<INode> {\n        private readonly INode[] _Items;\n\n        public bool HasItems { get { return _Items != null && _Items.Length > 0; } }\n        public IEnumerable<INode> Items { get { return _Items; } }\n\n        public ArgumentCollection(IEnumerable<INode> items) {\n            if (items != null)\n                _Items = items.ToArray();\n            else\n                _Items = new INode[0];\n        }\n\n        public override string ToString() {\n            var sb = new StringBuilder();\n            ToString(sb);\n            return sb.ToString();\n        }\n        public void ToString(StringBuilder builder) {\n            if (HasItems) {\n                builder.Append(StringTokens.ArgumentsOpen);\n                for (var i = 0; i < _Items.Length; ++i) {\n                    if (i > 0)\n                        builder.Append(StringTokens.ArgumentsSeperator);\n                    _Items[i].ToString(builder);\n                }\n                builder.Append(StringTokens.ArgumentsClose);\n            }\n        }\n\n        #region IEnumerable<IXivStringPart> Members\n\n        public IEnumerator<INode> GetEnumerator() {\n            return Items.GetEnumerator();\n        }\n\n        #endregion\n\n        #region IEnumerable Members\n\n        System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() {\n            return GetEnumerator();\n        }\n\n        #endregion\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Text/Nodes/CloseTag.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace SaintCoinach.Text.Nodes {\n    public class CloseTag : IExpressionNode {\n        private readonly TagType _Tag;\n\n        public TagType Tag { get { return _Tag; } }\n        NodeFlags INode.Flags { get { return NodeFlags.IsExpression | NodeFlags.IsStatic; } }\n\n        public CloseTag(TagType tag) {\n            _Tag = tag;\n        }\n\n        public override string ToString() {\n            var sb = new StringBuilder();\n            ToString(sb);\n            return sb.ToString();\n        }\n        public void ToString(StringBuilder builder) {\n            builder.Append(StringTokens.TagOpen);\n            builder.Append(StringTokens.ElementClose);\n            builder.Append(Tag);\n            builder.Append(StringTokens.TagClose);\n        }\n\n        public T Accept<T>(SaintCoinach.Text.Nodes.INodeVisitor<T> visitor) {\n            return visitor.Visit(this);\n        }\n\n        #region IExpressionNode Members\n\n        public IExpression Evaluate(EvaluationParameters parameters) {\n            return new Expressions.CloseTag(Tag);\n        }\n\n        #endregion\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Text/Nodes/Comparison.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace SaintCoinach.Text.Nodes {\n    public class Comparison : INode, IExpressionNode {\n        private readonly INode _Left;\n        private readonly DecodeExpressionType _ComparisonType;\n        private readonly INode _Right;\n\n        TagType INode.Tag { get { return TagType.None; } }\n        NodeFlags INode.Flags { get { return NodeFlags.IsExpression; } }\n        public INode Left { get { return _Left; } }\n        public DecodeExpressionType ComparisonType { get { return _ComparisonType; } }\n        public INode Right { get { return _Right; } }\n\n        public Comparison(DecodeExpressionType comparisonType, INode left, INode right) {\n            _ComparisonType = comparisonType;\n            _Left = left;\n            _Right = right;\n        }\n\n        public override string ToString() {\n            var sb = new StringBuilder();\n            ToString(sb);\n            return sb.ToString();\n        }\n        public void ToString(StringBuilder builder) {\n            builder.Append(ComparisonType);\n            builder.Append(StringTokens.ArgumentsOpen);\n            if (Left != null)\n                Left.ToString(builder);\n            builder.Append(StringTokens.ArgumentsSeperator);\n            if (Right != null)\n                Right.ToString(builder);\n            builder.Append(StringTokens.ArgumentsClose);\n        }\n\n        public T Accept<T>(SaintCoinach.Text.Nodes.INodeVisitor<T> visitor) {\n            return visitor.Visit(this);\n        }\n\n        #region IExpressionNode Members\n\n        public IExpression Evaluate(EvaluationParameters parameters) {\n            return new Expressions.GenericExpression(parameters.FunctionProvider.Compare(parameters, ComparisonType, Left, Right));\n        }\n\n        #endregion\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Text/Nodes/DefaultElement.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace SaintCoinach.Text.Nodes {\n    public class DefaultElement : INode {\n        private readonly TagType _Tag;\n        private readonly StaticByteArray _Data;\n\n        public TagType Tag { get { return _Tag; } }\n        public INode Data { get { return _Data; } }\n        NodeFlags INode.Flags { get { return NodeFlags.IsStatic; } }\n\n        public DefaultElement(TagType tag, byte[] innerBuffer) {\n            _Tag = tag;\n            _Data = new StaticByteArray(innerBuffer);\n        }\n\n        public override string ToString() {\n            var sb = new StringBuilder();\n            ToString(sb);\n            return sb.ToString();\n        }\n        public void ToString(StringBuilder builder) {\n            builder.Append(StringTokens.TagOpen);\n            builder.Append(Tag);\n            if (_Data.Value.Length == 0) {\n                builder.Append(StringTokens.ElementClose);\n                builder.Append(StringTokens.TagClose);\n            } else {\n                builder.Append(StringTokens.TagClose);\n\n                _Data.ToString(builder);\n\n                builder.Append(StringTokens.TagOpen);\n                builder.Append(StringTokens.ElementClose);\n                builder.Append(Tag);\n                builder.Append(StringTokens.TagClose);\n            }\n        }\n\n        public T Accept<T>(SaintCoinach.Text.Nodes.INodeVisitor<T> visitor) {\n            return visitor.Visit(this);\n        }\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Text/Nodes/EmptyElement.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace SaintCoinach.Text.Nodes {\n    public class EmptyElement : INode {\n        private readonly TagType _Tag;\n\n        public TagType Tag { get { return _Tag; } }\n        NodeFlags INode.Flags { get { return NodeFlags.IsStatic; } }\n\n        public EmptyElement(TagType tag) {\n            _Tag = tag;\n        }\n\n        public override string ToString() {\n            var sb = new StringBuilder();\n            ToString(sb);\n            return sb.ToString();\n        }\n        public void ToString(StringBuilder builder) {\n            builder.Append(StringTokens.TagOpen);\n            builder.Append(Tag);\n            builder.Append(StringTokens.ElementClose);\n            builder.Append(StringTokens.TagClose);\n        }\n\n        public T Accept<T>(SaintCoinach.Text.Nodes.INodeVisitor<T> visitor) {\n            return visitor.Visit(this);\n        }\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Text/Nodes/GenericElement.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace SaintCoinach.Text.Nodes {\n    public class GenericElement : INode, INodeWithChildren, INodeWithArguments, IExpressionNode {\n        private TagType _Tag;\n        private ArgumentCollection _Arguments;\n        private INode _Content;\n\n        public TagType Tag { get { return _Tag; } }\n        public IEnumerable<INode> Arguments { get { return _Arguments; } }\n        public INode Content { get { return _Content; } }\n        NodeFlags INode.Flags {\n            get {\n                var f = NodeFlags.IsExpression;\n                if (_Arguments.HasItems)\n                    f |= NodeFlags.HasArguments;\n                if (Content != null)\n                    f |= NodeFlags.HasChildren;\n                return f;\n            }\n        }\n\n        public GenericElement(TagType tag, INode content, params INode[] arguments) : this(tag, content, (IEnumerable<INode>)arguments) { }\n        public GenericElement(TagType tag, INode content, IEnumerable<INode> arguments) {\n            _Tag = tag;\n            _Arguments = new ArgumentCollection(arguments);\n            _Content = content;\n        }\n\n        public override string ToString() {\n            var sb = new StringBuilder();\n            ToString(sb);\n            return sb.ToString();\n        }\n        public void ToString(StringBuilder builder) {\n            builder.Append(StringTokens.TagOpen);\n            builder.Append(Tag);\n\n            _Arguments.ToString(builder);\n\n            if (Content == null) {\n                builder.Append(StringTokens.ElementClose);\n                builder.Append(StringTokens.TagClose);\n            } else {\n                builder.Append(StringTokens.TagClose);\n\n                Content.ToString(builder);\n\n                builder.Append(StringTokens.TagOpen);\n                builder.Append(StringTokens.ElementClose);\n                builder.Append(Tag);\n                builder.Append(StringTokens.TagClose);\n            }\n        }\n\n        public T Accept<T>(SaintCoinach.Text.Nodes.INodeVisitor<T> visitor) {\n            return visitor.Visit(this);\n        }\n\n        #region INodeWithChildren Members\n\n        IEnumerable<INode> INodeWithChildren.Children {\n            get { yield return Content; }\n        }\n\n        #endregion\n\n        #region IExpressionNode Members\n\n        public IExpression Evaluate(EvaluationParameters parameters) {\n            return parameters.FunctionProvider.EvaluateGenericElement(parameters, this);\n        }\n\n        #endregion\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Text/Nodes/IConditionalNode.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace SaintCoinach.Text.Nodes {\n    public interface IConditionalNode : IExpressionNode {\n        INode Condition { get; }\n        INode TrueValue { get; }\n        INode FalseValue { get; }\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Text/Nodes/IExpressionNode.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace SaintCoinach.Text.Nodes {\n    public interface IExpressionNode : INode {\n        IExpression Evaluate(EvaluationParameters parameters);\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Text/Nodes/INodeVisitor.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace SaintCoinach.Text.Nodes {\n    public interface INodeVisitor<T> {\n        T Visit(XivString xivString);\n        T Visit(OpenTag openTag);\n        T Visit(CloseTag closeTag);\n        T Visit(IfElement ifElement);\n        T Visit(GenericElement genericElement);\n        T Visit(EmptyElement emptyElement);\n        T Visit(DefaultElement defaultElement);\n        T Visit(Comparison comparison);\n        T Visit(TopLevelParameter topLevelParameter);\n        T Visit(SwitchElement switchElement);\n        T Visit(StaticString staticString);\n        T Visit(StaticInteger staticInteger);\n        T Visit(StaticByteArray staticByteArray);\n        T Visit(Parameter parameter);\n\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Text/Nodes/INodeWithArguments.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace SaintCoinach.Text.Nodes {\n    public interface INodeWithArguments : INode {\n        IEnumerable<INode> Arguments { get; }\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Text/Nodes/INodeWithChildren.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace SaintCoinach.Text.Nodes {\n    public interface INodeWithChildren : INode {\n        IEnumerable<INode> Children { get; }\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Text/Nodes/IStaticNode.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace SaintCoinach.Text.Nodes {\n    public interface IStaticNode : INode {\n        object Value { get; }\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Text/Nodes/IfElement.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace SaintCoinach.Text.Nodes {\n    public class IfElement : INode, IConditionalNode {\n        private readonly TagType _Tag;\n        private readonly INode _Condition;\n        private readonly INode _TrueValue;\n        private readonly INode _FalseValue;\n\n        public TagType Tag { get { return _Tag; } }\n        NodeFlags INode.Flags { get { return NodeFlags.IsExpression | NodeFlags.IsConditional; } }\n        public INode Condition { get { return _Condition; } }\n        public INode TrueValue { get { return _TrueValue; } }\n        public INode FalseValue { get { return _FalseValue; } }\n\n        public IfElement(TagType tag, INode condition, INode trueValue, INode falseValue) {\n            if (condition == null)\n                throw new ArgumentNullException(\"condition\");\n            _Tag = tag;\n            _Condition = condition;\n            _TrueValue = trueValue;\n            _FalseValue = falseValue;\n        }\n\n        public override string ToString() {\n            var sb = new StringBuilder();\n            ToString(sb);\n            return sb.ToString();\n        }\n        public void ToString(StringBuilder builder) {\n            builder.Append(StringTokens.TagOpen);\n            builder.Append(Tag);\n            builder.Append(StringTokens.ArgumentsOpen);\n            Condition.ToString(builder);\n            builder.Append(StringTokens.ArgumentsClose);\n            builder.Append(StringTokens.TagClose);\n\n            if (TrueValue != null)\n                TrueValue.ToString(builder);\n\n            if (FalseValue != null) {\n                builder.Append(StringTokens.ElseTag);\n                FalseValue.ToString(builder);\n            }\n\n            builder.Append(StringTokens.TagOpen);\n            builder.Append(StringTokens.ElementClose);\n            builder.Append(Tag);\n            builder.Append(StringTokens.TagClose);\n        }\n\n        public T Accept<T>(SaintCoinach.Text.Nodes.INodeVisitor<T> visitor) {\n            return visitor.Visit(this);\n        }\n\n        #region IExpressionNode Members\n\n        public IExpression Evaluate(EvaluationParameters parameters) {\n            var evalCond = Condition.TryEvaluate(parameters);\n            if (parameters.FunctionProvider.ToBoolean(evalCond))\n                return TrueValue.TryEvaluate(parameters);\n            return FalseValue.TryEvaluate(parameters);\n        }\n\n        #endregion\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Text/Nodes/IfEqualsElement.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace SaintCoinach.Text.Nodes {\n    public class IfEqualsElement : IfElement {\n        public IfEqualsElement(TagType tag, INode leftValue, INode rightValue, INode trueValue, INode falseValue)\n            : base(tag, new Comparison(DecodeExpressionType.Equal, leftValue, rightValue), trueValue, rightValue) {\n        }\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Text/Nodes/OpenTag.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace SaintCoinach.Text.Nodes {\n    public class OpenTag : INode, IExpressionNode {\n        private readonly TagType _Tag;\n        private readonly ArgumentCollection _Arguments;\n\n        public TagType Tag { get { return _Tag; } }\n        NodeFlags INode.Flags { get { return NodeFlags.IsExpression; } }\n        public IEnumerable<INode> Arguments { get { return _Arguments; } }\n\n        public OpenTag(TagType tag, params INode[] arguments) : this(tag, (IEnumerable<INode>)arguments) { }\n        public OpenTag(TagType tag, IEnumerable<INode> arguments) {\n            _Tag = tag;\n            _Arguments = new ArgumentCollection(arguments);\n        }\n\n        public override string ToString() {\n            var sb = new StringBuilder();\n            ToString(sb);\n            return sb.ToString();\n        }\n        public void ToString(StringBuilder builder) {\n            builder.Append(StringTokens.TagOpen);\n            builder.Append(Tag);\n\n            _Arguments.ToString(builder);\n\n            builder.Append(StringTokens.TagClose);\n        }\n\n        public T Accept<T>(SaintCoinach.Text.Nodes.INodeVisitor<T> visitor) {\n            return visitor.Visit(this);\n        }\n\n        #region IExpressionNode Members\n\n        public IExpression Evaluate(EvaluationParameters parameters) {\n            return new Expressions.OpenTag(Tag, Arguments.Select(_ => _.TryEvaluate(parameters)));\n        }\n\n        #endregion\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Text/Nodes/Parameter.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace SaintCoinach.Text.Nodes {\n    public class Parameter : INode, IExpressionNode {\n        private readonly DecodeExpressionType _ParameterType;\n        private readonly INode _ParameterIndex;\n\n        TagType INode.Tag { get { return TagType.None; } }\n        NodeFlags INode.Flags { get { return NodeFlags.IsExpression; } }\n        public DecodeExpressionType ParameterType { get { return _ParameterType; } }\n        public INode ParameterIndex { get { return _ParameterIndex; } }\n\n        public Parameter(DecodeExpressionType parameterType, INode parameterIndex) {\n            if (parameterIndex == null)\n                throw new ArgumentNullException(\"parameterIndex\");\n            _ParameterType = parameterType;\n            _ParameterIndex = parameterIndex;\n        }\n\n        public override string ToString() {\n            var sb = new StringBuilder();\n            ToString(sb);\n            return sb.ToString();\n        }\n        public void ToString(StringBuilder builder) {\n            builder.Append(ParameterType);\n            builder.Append(StringTokens.ArgumentsOpen);\n            ParameterIndex.ToString(builder);\n            builder.Append(StringTokens.ArgumentsClose);\n        }\n\n        public T Accept<T>(SaintCoinach.Text.Nodes.INodeVisitor<T> visitor) {\n            return visitor.Visit(this);\n        }\n\n        #region IExpressionNode Members\n\n        public IExpression Evaluate(EvaluationParameters parameters) {\n            var evalIndex = ParameterIndex.TryEvaluate(parameters);\n            var index = parameters.FunctionProvider.ToInteger(evalIndex);\n            return new Expressions.GenericExpression(parameters[this.ParameterType, index]);\n        }\n\n        #endregion\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Text/Nodes/StaticByteArray.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace SaintCoinach.Text.Nodes {\n    public class StaticByteArray : INode, IStaticNode {\n        private readonly byte[] _Value;\n\n        TagType INode.Tag { get { return TagType.None; } }\n        NodeFlags INode.Flags { get { return NodeFlags.IsStatic; } }\n        public byte[] Value { get { return _Value; } }\n\n        public StaticByteArray(byte[] value) {\n            _Value = value.ToArray();\n        }\n\n        public override string ToString() {\n            var sb = new StringBuilder();\n            ToString(sb);\n            return sb.ToString();\n        }\n        public void ToString(StringBuilder builder) {\n            for (var i = 0; i < Value.Length; ++i)\n                builder.Append(Value[i].ToString(\"X2\"));\n        }\n\n        public T Accept<T>(SaintCoinach.Text.Nodes.INodeVisitor<T> visitor) {\n            return visitor.Visit(this);\n        }\n\n        #region IDecodeNodeStatic Members\n\n        object IStaticNode.Value {\n            get { return ToString(); }\n        }\n\n        #endregion\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Text/Nodes/StaticInteger.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace SaintCoinach.Text.Nodes {\n    public class StaticInteger : IStaticNode {\n        private readonly int _Value;\n\n        TagType INode.Tag { get { return TagType.None; } }\n        NodeFlags INode.Flags { get { return NodeFlags.IsStatic; } }\n        public int Value { get { return _Value; } }\n\n        public StaticInteger(int value) {\n            _Value = value;\n        }\n\n        public override string ToString() {\n            return Value.ToString();\n        }\n        public void ToString(StringBuilder builder) {\n            builder.Append(Value);\n        }\n\n        public T Accept<T>(SaintCoinach.Text.Nodes.INodeVisitor<T> visitor) {\n            return visitor.Visit(this);\n        }\n\n        #region IDecodeNodeStatic Members\n\n        object IStaticNode.Value {\n            get { return Value; }\n        }\n\n        #endregion\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Text/Nodes/StaticString.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace SaintCoinach.Text.Nodes {\n    public class StaticString : IStaticNode {\n        private readonly string _Value;\n\n        TagType INode.Tag { get { return TagType.None; } }\n        NodeFlags INode.Flags { get { return NodeFlags.IsStatic; } }\n        public string Value { get { return _Value; } }\n\n        public StaticString(string value) {\n            _Value = value;\n        }\n\n        public override string ToString() {\n            return Value;\n        }\n        public void ToString(StringBuilder builder) {\n            builder.Append(Value);\n        }\n\n        public T Accept<T>(SaintCoinach.Text.Nodes.INodeVisitor<T> visitor) {\n            return visitor.Visit(this);\n        }\n\n        #region IDecodeNodeStatic Members\n\n        object IStaticNode.Value {\n            get { return Value; }\n        }\n\n        #endregion\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Text/Nodes/SwitchElement.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Collections.ObjectModel;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace SaintCoinach.Text.Nodes {\n    public class SwitchElement : INode, IExpressionNode {\n        private readonly TagType _Tag;\n        private readonly INode _CaseSwitch;\n        private readonly ReadOnlyDictionary<int, INode> _Cases;\n\n        public TagType Tag { get { return _Tag; } }\n        NodeFlags INode.Flags { get { return NodeFlags.IsExpression; } }\n        public INode CaseSwitch { get { return _CaseSwitch; } }\n        public IReadOnlyDictionary<int, INode> Cases { get { return _Cases; } }\n\n        public SwitchElement(TagType tag, INode caseSwitch, IDictionary<int, INode> cases) {\n            if (caseSwitch == null)\n                throw new ArgumentNullException(\"caseSwitch\");\n            if (cases == null)\n                throw new ArgumentNullException(\"cases\");\n            _Tag = tag;\n            _CaseSwitch = caseSwitch;\n            _Cases = new ReadOnlyDictionary<int, INode>(cases);\n        }\n\n        public override string ToString() {\n            var sb = new StringBuilder();\n            ToString(sb);\n            return sb.ToString();\n        }\n        public void ToString(StringBuilder builder) {\n            builder.Append(StringTokens.TagOpen);\n            builder.Append(Tag);\n            builder.Append(StringTokens.ArgumentsOpen);\n            CaseSwitch.ToString(builder);\n            builder.Append(StringTokens.ArgumentsClose);\n            builder.Append(StringTokens.TagClose);\n\n            foreach(var caseValue in Cases){\n                builder.Append(StringTokens.TagOpen);\n                builder.Append(StringTokens.CaseTagName);\n                builder.Append(StringTokens.ArgumentsOpen);\n                builder.Append(caseValue.Key);\n                builder.Append(StringTokens.ArgumentsClose);\n                builder.Append(StringTokens.TagClose);\n\n                caseValue.Value.ToString(builder);\n\n                builder.Append(StringTokens.TagOpen);\n                builder.Append(StringTokens.ElementClose);\n                builder.Append(StringTokens.CaseTagName);\n                builder.Append(StringTokens.TagClose);\n            }\n\n            builder.Append(StringTokens.TagOpen);\n            builder.Append(StringTokens.ElementClose);\n            builder.Append(Tag);\n            builder.Append(StringTokens.TagClose);\n        }\n\n        public T Accept<T>(SaintCoinach.Text.Nodes.INodeVisitor<T> visitor) {\n            return visitor.Visit(this);\n        }\n\n        #region IExpressionNode Members\n\n        public IExpression Evaluate(EvaluationParameters parameters) {\n            var evalSwitch = CaseSwitch.TryEvaluate(parameters);\n            var asInt = parameters.FunctionProvider.ToInteger(evalSwitch);\n\n            INode caseNode;\n            if (!_Cases.TryGetValue(asInt, out caseNode))\n                throw new InvalidOperationException();\n            return caseNode.TryEvaluate(parameters);\n        }\n\n        #endregion\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Text/Nodes/TopLevelParameter.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace SaintCoinach.Text.Nodes {\n    public class TopLevelParameter : INode, IExpressionNode {\n        private readonly int _Value;\n\n        public int Value { get { return _Value; } }\n\n        public TopLevelParameter(int value) {\n            _Value = value;\n        }\n\n        #region IExpressionNode Members\n\n        public IExpression Evaluate(EvaluationParameters parameters) {\n            return new Expressions.GenericExpression(parameters.TopLevelParameters[Value]);\n        }\n\n        #endregion\n\n        #region INode Members\n\n\n        TagType INode.Tag { get { return TagType.None; } }\n        NodeFlags INode.Flags { get { return NodeFlags.IsExpression; } }\n\n        public override string ToString() {\n            var sb = new StringBuilder();\n            ToString(sb);\n            return sb.ToString();\n        }\n        public void ToString(StringBuilder builder) {\n            builder.Append(StringTokens.TopLevelParameterName);\n            builder.Append(StringTokens.ArgumentsOpen);\n            builder.Append(Value);\n            builder.Append(StringTokens.ArgumentsClose);\n        }\n\n        public T Accept<T>(SaintCoinach.Text.Nodes.INodeVisitor<T> visitor) {\n            return visitor.Visit(this);\n        }\n        #endregion\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Text/Parameters/ObjectParameters.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace SaintCoinach.Text.Parameters {\n    public class ObjectParameters : ParameterBase {\n        const int LocalObjectIndex = 1;\n        const int SourceObjectIndex = 2;\n        const int TargetObjectIndex = 3;\n\n        #region Constructors\n        public ObjectParameters() { }\n        public ObjectParameters(ParameterBase copyFrom) : base(copyFrom) { }\n        #endregion\n\n        #region Properties\n        public object LocalObject { get { return this[LocalObjectIndex]; } }\n        public object SourceObject { get { return this[SourceObjectIndex]; } }\n        public object TargetObject { get { return this[TargetObjectIndex]; } }\n        #endregion\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Text/Parameters/ParameterBase.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace SaintCoinach.Text.Parameters {\n    public class ParameterBase {\n        #region Fields\n        private object _FallbackValue = null;\n        private Dictionary<int, object> _Values;\n        #endregion\n\n        #region Properties\n        public object FallbackValue { get { return _FallbackValue; } set { _FallbackValue = value; } }\n        #endregion\n\n        #region Constructors\n        public ParameterBase() {\n            _Values = new Dictionary<int, object>();\n        }\n        public ParameterBase(ParameterBase copyFrom) {\n            if (copyFrom == null)\n                throw new ArgumentNullException(\"copyFrom\");\n            _FallbackValue = copyFrom._FallbackValue;\n            _Values = new Dictionary<int, object>(copyFrom._Values);\n        }\n        #endregion\n\n        public void Clear() {\n            _Values.Clear();\n        }\n        public bool Remove(int index) {\n            return _Values.Remove(index);\n        }\n\n        #region Indexer\n        public object this[int index] {\n            get {\n                object v;\n                if (!_Values.TryGetValue(index, out v))\n                    v = FallbackValue;\n                return v;\n            }\n            set {\n                if (_Values.ContainsKey(index))\n                    _Values[index] = value;\n                else\n                    _Values.Add(index, value);\n            }\n        }\n        #endregion\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Text/Parameters/PlayerParameters.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace SaintCoinach.Text.Parameters {\n    using Xiv;\n\n    public class PlayerParameters : ParameterBase {\n        public const int ActiveClassJobIndex = 68;\n        public const int LevelIndex1 = 69;          // TODO: I have no idea what the difference between these is.\n        public const int LevelIndex2 = 72;          // 72 possibly JOB and 69 CLASS ?\n        public const int GamePadTypeIndex = 75;     // TODO: 0 for XInput, 1 for PS3, 2 for PS4?\n        public const int RegionIndex = 77;          // I think it is, anyway. Only found it for formatting dates. 0-2 = ?; 3 = EU?; 4+ = ?\n\n        #region Constructors\n        public PlayerParameters() { }\n        public PlayerParameters(ParameterBase copyFrom) : base(copyFrom) { }\n        #endregion\n\n        #region Properties\n        public int Level {\n            get { return Convert.ToInt32(this[LevelIndex1]); }\n            set {\n                this[LevelIndex1] = value;\n                this[LevelIndex2] = value;\n            }\n        }\n        public ClassJob ActiveClassJob {\n            get { return this[ActiveClassJobIndex] as ClassJob; }\n            set { this[ActiveClassJobIndex] = value; }\n        }\n        public int GamePadType {\n            get { return Convert.ToInt32(this[GamePadTypeIndex]); }\n            set { this[GamePadTypeIndex] = value; }\n        }\n        public int Region {\n            get { return Convert.ToInt32(this[RegionIndex]); }\n            set { this[RegionIndex] = value; }\n        }\n        #endregion\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Text/Processor/Expressions/EmptyExpression.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace SaintCoinach.Text.Processor.Expressions {\n    public class EmptyExpression : IExpression {\n        public static readonly EmptyExpression Instance = new EmptyExpression();\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Text/Processor/Expressions/ExpressionCollection.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace SaintCoinach.Text.Processor.Expressions {\n    public class ExpressionCollection : List<IExpression>, IExpression {\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Text/Processor/Expressions/StaticExpression.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace SaintCoinach.Text.Processor.Expressions {\n    public class StaticExpression : IExpression {\n        private readonly object _Value;\n\n        public object Value { get { return _Value; } }\n\n        public StaticExpression(object value) {\n            _Value = value;\n        }\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Text/Processor/IExpression.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace SaintCoinach.Text.Processor {\n    public interface IExpression {\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Text/StringTokens.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace SaintCoinach.Text {\n    public static class StringTokens {\n        public const string TagOpen = \"<\";\n        public const string TagClose = \">\";\n        public const string ElementClose = \"/\";\n        public const string ArgumentsOpen = \"(\";\n        public const string ArgumentsClose = \")\";\n        public const string ArgumentsSeperator = \",\";\n\n        public const string ElseTag = TagOpen + \"Else\" + ElementClose + TagClose;\n        public const string CaseTagName = \"Case\";\n\n        public const string InvertName = \"Not\";\n\n        public const string TopLevelParameterName = \"TopLevelParameter\";\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Text/TagType.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace SaintCoinach.Text {\n    public enum TagType : byte {\n        None            = 0x00,\n\n        /* Appears to set values for an input time.\n         * - 222 / DEh  Year\n         * - 221 / DDh  Month\n         * - 220 / DCh  Day of week\n         * - 219 / DBh  Day of month\n         * - 218 / DAh  Hour\n         * - 217 / D9h  Minute\n         */\n        ResetTime = 0x06,\n        Time = 0x07,     // TODO: It seems to set the time used further on.\n        If              = 0x08,\n        Switch          = 0x09,\n        IfEquals        = 0x0C,\n        Unknown0A       = 0x0A,     // TODO\n        LineBreak       = 0x10,\n        // Wait            = 0x11, // Not present anywhere in game data up to 2015.04.17.0001.0000\n\n        /* Font icon.\n         * GraphicsFileTextureDefinition is 'common/font/gfdata.gfd'\n         * Texture is one of:\n         * - 'common/font/fonticon_ps3.tex'\n         * - 'common/font/fonticon_ps4.tex'\n         * - 'common/font/fonticon_xinput.tex'\n         */\n        Gui             = 0x12,\n        Color           = 0x13,\n        Unknown14       = 0x14,     // TODO\n        SoftHyphen      = 0x16,\n        Unknown17       = 0x17,     // TODO: Used exclusively in Japanese and at start of new lines.\n        Emphasis2       = 0x19,     // TODO: See if this is bold, only used very little. 0x1A emphasis is italic.\n        Emphasis        = 0x1A,\n        Indent          = 0x1D,\n        CommandIcon     = 0x1E,\n        Dash            = 0x1F,\n        Value           = 0x20,\n        Format          = 0x22,\n        TwoDigitValue   = 0x24,     // A single-digit value is formatted with a leading zero. \n        // Time = 0x25, // Not present anywhere in game data up to 2015.04.17.0001.0000\n        Sheet           = 0x28,\n        Highlight       = 0x29,\n        Clickable       = 0x2B,     // Seemingly anything that has an action associated with it (NPCs, PCs, Items, etc.)\n        Split           = 0x2C,\n        Unknown2D       = 0x2D,     // TODO\n        Fixed           = 0x2E,\n        Unknown2F       = 0x2F,     // TODO\n        SheetJa         = 0x30,\n        SheetEn         = 0x31,\n        SheetDe         = 0x32,\n        SheetFr         = 0x33,\n        InstanceContent = 0x40,     // Presumably so it can be clicked?\n        UIForeground    = 0x48,\n        UIGlow          = 0x49,\n        RubyCharaters   = 0x4A,     // Mostly used on Japanese, which means \n        ZeroPaddedValue = 0x50,\n        Unknown60       = 0x60,     // TODO: Used as prefix in Gold Saucer announcements.\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Text/XivString.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace SaintCoinach.Text {\n    using Nodes;\n\n    public class XivString : INode, IExpressionNode, INodeWithChildren, IComparable, IComparable<XivString>, IComparable<string>, IEquatable<XivString>, IEquatable<string> {\n        public static readonly XivString Empty = new XivString(new INode[0]);\n\n        private readonly INode[] _Children;\n        private WeakReference<string> _StringCache;\n\n        TagType INode.Tag { get { return TagType.None; } }\n        NodeFlags INode.Flags { get { return NodeFlags.HasChildren | NodeFlags.IsExpression; } }\n        public IEnumerable<INode> Children { get { return _Children; } }\n\n        public bool IsEmpty {\n            get {\n                if (_Children.Length == 0)\n                    return true;\n                // TODO: This can be made more efficient (as in, without the need to convert to a string), but that's going to take lots of work.\n                return string.IsNullOrWhiteSpace(ToString());\n            }\n        }\n\n        public XivString(params INode[] children) : this((IEnumerable<INode>)children) { }\n        public XivString(IEnumerable<INode> children) {\n            _Children = children.ToArray();\n        }\n\n        public override string ToString() {\n            string v;\n            if (_StringCache != null && _StringCache.TryGetTarget(out v))\n                return v;\n            var sb = new StringBuilder();\n            ToString(sb);\n            v = sb.ToString();\n            if (_StringCache == null)\n                _StringCache = new WeakReference<string>(v);\n            else\n                _StringCache.SetTarget(v);\n            return v;\n        }\n        public void ToString(StringBuilder builder) {\n            foreach (var part in Children)\n                part.ToString(builder);\n        }\n\n        public static implicit operator string (XivString xivString) {\n            return xivString.ToString();\n        }\n\n        public override int GetHashCode() {\n            return ToString().GetHashCode();\n        }\n        public override bool Equals(object obj) {\n            if (obj is XivString)\n                return string.Equals(this.ToString(), obj.ToString());\n            if (obj is string)\n                return string.Equals(this.ToString(), (string)obj);\n            return false;\n        }\n        public static bool operator ==(XivString left, XivString right) {\n            var lNull = object.ReferenceEquals(null, left);\n            var rNull = object.ReferenceEquals(null, right);\n            if (lNull && rNull)\n                return true;\n            if (lNull != rNull)\n                return false;\n            return (left.ToString() == right.ToString());\n        }\n        public static bool operator !=(XivString left, XivString right) {\n            var lNull = object.ReferenceEquals(null, left);\n            var rNull = object.ReferenceEquals(null, right);\n            if (lNull && rNull)\n                return false;\n            if (lNull != rNull)\n                return true;\n            return (left.ToString() != right.ToString());\n        }\n        public static bool operator ==(XivString left, string right) {\n            var lNull = object.ReferenceEquals(null, left);\n            var rNull = object.ReferenceEquals(null, right);\n            if (lNull && rNull)\n                return true;\n            if (lNull != rNull)\n                return false;\n            return (left.ToString() == right);\n        }\n        public static bool operator !=(XivString left, string right) {\n            var lNull = object.ReferenceEquals(null, left);\n            var rNull = object.ReferenceEquals(null, right);\n            if (lNull && rNull)\n                return false;\n            if (lNull != rNull)\n                return true;\n            return (left.ToString() != right);\n        }\n\n        #region IComparable Members\n\n        public int CompareTo(object obj) {\n            if (obj is XivString)\n                return CompareTo((XivString)obj);\n            if (obj is string)\n                return CompareTo((string)obj);\n            if (obj == null)\n                return 1;\n            return -1;\n        }\n\n        #endregion\n\n        #region IComparable<XivString> Members\n\n        public int CompareTo(XivString other) {\n            return string.Compare(this.ToString(), other.ToString());\n        }\n\n        #endregion\n\n        #region IComparable<string> Members\n\n        public int CompareTo(string other) {\n            return string.Compare(this.ToString(), other);\n        }\n\n        #endregion\n\n        #region IEquatable<XivString> Members\n\n        public bool Equals(XivString other) {\n            return string.Equals(this.ToString(), other.ToString());\n        }\n\n        #endregion\n\n        #region IEquatable<string> Members\n\n        public bool Equals(string other) {\n            return string.Equals(this.ToString(), other);\n        }\n\n        #endregion\n\n        #region IExpressionNode Members\n\n        public IExpression Evaluate(EvaluationParameters parameters) {\n            return new Expressions.ExpressionCollection(Children.Select(c => c.TryEvaluate(parameters)));\n        }\n\n        #endregion\n\n        public T Accept<T>(SaintCoinach.Text.Nodes.INodeVisitor<T> visitor) {\n            return visitor.Visit(this);\n        }\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Text/XivStringDecoder.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.IO;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace SaintCoinach.Text {\n    using Nodes;\n\n    public class XivStringDecoder {\n        public delegate INode TagDecoder(BinaryReader input, TagType tag, int length);\n\n        static readonly Encoding UTF8NoBom = new UTF8Encoding(false);\n        const byte TagStartMarker = 0x02;\n        const byte TagEndMarker = 0x03;\n\n        private static XivStringDecoder _Default;\n        public static XivStringDecoder Default { get { return _Default ?? (_Default = new XivStringDecoder()); } }\n\n        #region Fields\n        private Encoding _Encoding = UTF8NoBom;\n        private Dictionary<TagType, TagDecoder> _TagDecoders = new Dictionary<TagType, TagDecoder>();\n        private TagDecoder _DefaultTagDecoder;\n        private string _Dash = \"–\";\n        private string _NewLine = Environment.NewLine;\n        #endregion\n\n        #region Properties\n        public Encoding Encoding {\n            get { return _Encoding; }\n            set { _Encoding = value; }\n        }\n        public TagDecoder DefaultTagDecoder {\n            get { return _DefaultTagDecoder; }\n            set {\n                if (value == null)\n                    throw new ArgumentNullException();\n                _DefaultTagDecoder = value;\n            }\n        }\n        public string Dash {\n            get { return _Dash; }\n            set { _Dash = value; }\n        }\n        public string NewLine {\n            get { return _NewLine; }\n            set { _NewLine = value; }\n        }\n        #endregion\n\n        #region Constructor\n        public XivStringDecoder() {\n            this.DefaultTagDecoder = DecodeTagDefault;\n\n            SetDecoder(TagType.Clickable, (i, t, l) => DecodeGenericElementWithVariableArguments(i, t, l, 1, int.MaxValue));    // I have no idea.\n            SetDecoder(TagType.Color, DecodeColor);\n            SetDecoder(TagType.CommandIcon, (i, t, l) => DecodeGenericElement(i, t, l, 1, false));\n            SetDecoder(TagType.Dash, (i, t, l) => new Nodes.StaticString(this.Dash));\n            SetDecoder(TagType.Emphasis, DecodeGenericSurroundingTag);\n            SetDecoder(TagType.Emphasis2, DecodeGenericSurroundingTag);\n            // TODO: Fixed\n            SetDecoder(TagType.Format, DecodeFormat);\n            SetDecoder(TagType.Gui, (i, t, l) => DecodeGenericElement(i, t, l, 1, false));\n            SetDecoder(TagType.Highlight, (i, t, l) => DecodeGenericElement(i, t, l, 0, true));\n            SetDecoder(TagType.If, DecodeIf);\n            SetDecoder(TagType.IfEquals, DecodeIfEquals);\n            // Indent\n            SetDecoder(TagType.InstanceContent, (i, t, l) => DecodeGenericElement(i, t, l, 0, true));\n            SetDecoder(TagType.LineBreak, (i, t, l) => new Nodes.StaticString(this.NewLine));\n            SetDecoder(TagType.Sheet, (i, t, l) => DecodeGenericElementWithVariableArguments(i, t, l, 2, int.MaxValue));   // Sheet name, Row[, Column[, Parameters]+]\n            SetDecoder(TagType.SheetDe, (i, t, l) => DecodeGenericElementWithVariableArguments(i, t, l, 3, int.MaxValue)); // Sheet name, Attributive row, Sheet row[, Sheet column[, Attributive index[, Parameters]+]\n            SetDecoder(TagType.SheetEn, (i, t, l) => DecodeGenericElementWithVariableArguments(i, t, l, 3, int.MaxValue)); // Sheet name, Attributive row, Sheet row[, Sheet column[, Attributive index[, Parameters]+]\n            SetDecoder(TagType.SheetFr, (i, t, l) => DecodeGenericElementWithVariableArguments(i, t, l, 3, int.MaxValue)); // Sheet name, Attributive row, Sheet row[, Sheet column[, Attributive index[, Parameters]+]\n            SetDecoder(TagType.SheetJa, (i, t, l) => DecodeGenericElementWithVariableArguments(i, t, l, 3, int.MaxValue)); // Sheet name, Attributive row, Sheet row[, Sheet column[, Attributive index[, Parameters]+]\n            SetDecoder(TagType.Split, (i, t, l) => DecodeGenericElement(i, t, l, 3, false));                    // Input expression, Seperator, Index to use\n            SetDecoder(TagType.Switch, DecodeSwitch);\n            SetDecoder(TagType.Time, (i, t, l) => DecodeGenericElement(i, t, l, 1, false));\n            SetDecoder(TagType.TwoDigitValue, (i, t, l) => DecodeGenericElement(i, t, l, 0, true));\n            // Unknowns\n            SetDecoder(TagType.Value, (i, t, l) => DecodeGenericElement(i, t, l, 0, true));\n            SetDecoder(TagType.ZeroPaddedValue, DecodeZeroPaddedValue);\n        }\n        #endregion\n\n        #region Helper\n        public void SetDecoder(TagType tag, TagDecoder decoder) {\n            if (_TagDecoders.ContainsKey(tag))\n                _TagDecoders[tag] = decoder;\n            else\n                _TagDecoders.Add(tag, decoder);\n        }\n        #endregion\n\n        #region Decode\n        public XivString Decode(byte[] buffer) {\n            using (var ms = new MemoryStream(buffer)) {\n                using (var r = new BinaryReader(ms, this.Encoding))\n                    return Decode(r, buffer.Length);\n            }\n        }\n        public XivString Decode(BinaryReader input, int length) {\n            if (length < 0)\n                throw new ArgumentOutOfRangeException(\"length\");\n            var end = input.BaseStream.Position + length;\n            if (end > input.BaseStream.Length)\n                throw new ArgumentOutOfRangeException(\"length\");\n\n            var parts = new List<INode>();\n            var pendingStatic = new List<byte>();\n\n            while (input.BaseStream.Position < end) {\n                var v = input.ReadByte();\n                if (v == TagStartMarker) {\n                    AddStatic(pendingStatic, parts);\n                    parts.Add(DecodeTag(input));\n                    if (input.BaseStream.Position > end)\n                        throw new InvalidOperationException();\n                } else\n                    pendingStatic.Add(v);\n            }\n\n            AddStatic(pendingStatic, parts);\n\n            return new XivString(parts);\n        }\n\n        private INode DecodeTag(BinaryReader input) {\n            var tag = (TagType)input.ReadByte();\n            var length = GetInteger(input);\n            var end = input.BaseStream.Position + length;\n            //System.Diagnostics.Trace.WriteLine(string.Format(\"{0} @ {1:X}h+{2:X}h\", tag, input.BaseStream.Position, length));\n            TagDecoder decoder = null;\n            _TagDecoders.TryGetValue(tag, out decoder);\n            var result = (decoder ?? DefaultTagDecoder)(input, tag, length);\n            if (input.BaseStream.Position != end)\n            {\n                // Triggered by two entries in LogMessage as of 3.15.\n                // Looks like a tag has some extra bits, as the end length is a proper TagEndMarker.\n                System.Diagnostics.Debug.WriteLine(string.Format(\"Position mismatch in XivStringDecoder.DecodeTag.  Position {0} != predicted {1}.\", input.BaseStream.Position, end));\n                input.BaseStream.Position = end;\n            }\n            if (input.ReadByte() != TagEndMarker)\n                throw new InvalidDataException();\n            return result;\n        }\n\n        private void AddStatic(List<byte> pending, List<INode> targetParts) {\n            if (pending.Count == 0)\n                return;\n            targetParts.Add(new Nodes.StaticString(this.Encoding.GetString(pending.ToArray())));\n            pending.Clear();\n        }\n        #endregion\n\n        #region Generic\n        protected INode DecodeTagDefault(BinaryReader input, TagType tag, int length) {\n            return new Nodes.DefaultElement(tag, input.ReadBytes(length));\n        }\n        protected INode DecodeExpression(BinaryReader input) {\n            var t = input.ReadByte();\n            return DecodeExpression(input, (DecodeExpressionType)t);\n        }\n        protected INode DecodeExpression(BinaryReader input, DecodeExpressionType exprType) {\n            var t = (byte)exprType;\n            if (t < 0xD0)\n                return new Nodes.StaticInteger(t - 1);\n            if (t < 0xE0)\n                return new Nodes.TopLevelParameter(t - 1);\n\n            switch (exprType) {\n                case DecodeExpressionType.Decode: {\n                        var len = GetInteger(input);\n                        return Decode(input, len);\n                    }\n                case DecodeExpressionType.Byte:\n                    return new Nodes.StaticInteger(GetInteger(input, IntegerType.Byte));\n                case DecodeExpressionType.Int16_MinusOne:\n                    return new Nodes.StaticInteger(GetInteger(input, IntegerType.Int16) - 1);\n                case DecodeExpressionType.Int16_1:\n                case DecodeExpressionType.Int16_2:\n                    return new Nodes.StaticInteger(GetInteger(input, IntegerType.Int16));\n                case DecodeExpressionType.Int24_MinusOne:\n                    return new Nodes.StaticInteger(GetInteger(input, IntegerType.Int24) - 1);\n                case DecodeExpressionType.Int24:\n                    return new Nodes.StaticInteger(GetInteger(input, IntegerType.Int24));\n                case DecodeExpressionType.Int24_Lsh8:\n                    return new Nodes.StaticInteger(GetInteger(input, IntegerType.Int24) << 8);\n                case DecodeExpressionType.Int24_SafeZero: {\n                        var v16 = input.ReadByte();\n                        var v8 = input.ReadByte();\n                        var v0 = input.ReadByte();\n\n                        int v = 0;\n                        if (v16 != byte.MaxValue)\n                            v |= v16 << 16;\n                        if (v8 != byte.MaxValue)\n                            v |= v8 << 8;\n                        if (v0 != byte.MaxValue)\n                            v |= v0;\n\n                        return new Nodes.StaticInteger(v);\n                    }\n                case DecodeExpressionType.Int32:\n                    return new Nodes.StaticInteger(GetInteger(input, IntegerType.Int32));\n                case DecodeExpressionType.GreaterThanOrEqualTo:\n                case DecodeExpressionType.GreaterThan:\n                case DecodeExpressionType.LessThanOrEqualTo:\n                case DecodeExpressionType.LessThan:\n                case DecodeExpressionType.NotEqual:\n                case DecodeExpressionType.Equal: {\n                        var left = DecodeExpression(input);\n                        var right = DecodeExpression(input);\n                        return new Nodes.Comparison(exprType, left, right);\n                    }\n                case DecodeExpressionType.IntegerParameter:\n                case DecodeExpressionType.PlayerParameter:\n                case DecodeExpressionType.StringParameter:\n                case DecodeExpressionType.ObjectParameter:\n                    return new Nodes.Parameter(exprType, DecodeExpression(input));\n                default:\n                    throw new NotSupportedException();\n            }\n        }\n        protected INode DecodeGenericElement(BinaryReader input, TagType tag, int length, int argCount, bool hasContent) {\n            if (length == 0) {\n                /*if (argCount > 0)\n                    throw new ArgumentOutOfRangeException(\"argCount\");*/\n                return new Nodes.EmptyElement(tag);\n            }\n            var arguments = new INode[argCount];\n            for (var i = 0; i < argCount; ++i)\n                arguments[i] = DecodeExpression(input);\n            INode content = null;\n            if (hasContent)\n                content = DecodeExpression(input);\n\n            return new Nodes.GenericElement(tag, content, arguments);\n        }\n        protected INode DecodeGenericElementWithVariableArguments(BinaryReader input, TagType tag, int length, int minCount, int maxCount) {\n            var end = input.BaseStream.Position + length;\n            var args = new List<INode>();\n            for (var i = 0; i < maxCount && input.BaseStream.Position < end; ++i)\n                args.Add(DecodeExpression(input));\n            return new Nodes.GenericElement(tag, null, args);\n        }\n        protected INode DecodeGenericSurroundingTag(BinaryReader input, TagType tag, int length) {\n            if (length != 1)\n                throw new ArgumentOutOfRangeException(\"length\");\n            var status = GetInteger(input);\n            if (status == 0)\n                return new Nodes.CloseTag(tag);\n            if (status == 1)\n                return new Nodes.OpenTag(tag, null);\n            throw new InvalidDataException();\n        }\n        #endregion\n\n        #region Specific\n        protected INode DecodeZeroPaddedValue(BinaryReader input, TagType tag, int length) {\n            var val = DecodeExpression(input);\n            var arg = DecodeExpression(input);\n            return new GenericElement(tag, val, arg);\n        }\n        protected INode DecodeColor(BinaryReader input, TagType tag, int length) {\n            var t = input.ReadByte();\n            if (length == 1 && t == 0xEC)\n                return new Nodes.CloseTag(tag);\n            var color = DecodeExpression(input, (DecodeExpressionType)t);\n            return new Nodes.OpenTag(tag, color);\n        }\n        protected INode DecodeFormat(BinaryReader input, TagType tag, int length) {\n            var end = input.BaseStream.Position + length;\n\n            var arg1 = DecodeExpression(input);\n            var arg2 = new Nodes.StaticByteArray(input.ReadBytes((int)(end - input.BaseStream.Position)));\n            return new Nodes.GenericElement(tag, null, arg1, arg2);\n        }\n        protected INode DecodeIf(BinaryReader input, TagType tag, int length) {\n            var end = input.BaseStream.Position + length;\n\n            var condition = DecodeExpression(input);\n            /*var trueValue = DecodeExpression(input);\n            INode falseValue = null;\n            if (input.BaseStream.Position != end)\n                falseValue = DecodeExpression(input);\n            */\n            INode trueValue, falseValue;\n            DecodeConditionalOutputs(input, (int)end, out trueValue, out falseValue);\n\n            return new Nodes.IfElement(tag, condition, trueValue, falseValue);\n        }\n        protected INode DecodeIfEquals(BinaryReader input, TagType tag, int length) {\n            var end = input.BaseStream.Position + length;\n\n            var left = DecodeExpression(input);\n            var right = DecodeExpression(input);\n            /*\n            var trueValue = DecodeExpression(input);\n            INode falseValue = null;\n            if (input.BaseStream.Position != end)\n                falseValue = DecodeExpression(input);*/\n\n            INode trueValue, falseValue;\n            DecodeConditionalOutputs(input, (int)end, out trueValue, out falseValue);\n\n            return new Nodes.IfEqualsElement(tag, left, right, trueValue, falseValue);\n        }\n        protected void DecodeConditionalOutputs(BinaryReader input, int end, out INode trueValue, out INode falseValue) {\n            var exprs = new List<INode>();\n            while (input.BaseStream.Position != end) {\n                var expr = DecodeExpression(input);\n                exprs.Add(expr);\n            }\n\n            // Only one instance with more than two expressions (LogMessage.en[1115][4])\n            // TODO: Not sure how it should be handled, discarding all but first and second for now.\n            if (exprs.Count > 0)\n                trueValue = exprs[0];\n            else\n                trueValue = null;\n\n            if (exprs.Count > 1)\n                falseValue = exprs[1];\n            else\n                falseValue = null;\n        }\n        protected INode DecodeSwitch(BinaryReader input, TagType tag, int length) {\n            var end = input.BaseStream.Position + length;\n            var caseSwitch = DecodeExpression(input);\n\n            var cases = new Dictionary<int, INode>();\n            var i = 1;\n            while (input.BaseStream.Position < end)\n                cases.Add(i++, DecodeExpression(input));\n\n            return new Nodes.SwitchElement(tag, caseSwitch, cases);\n        }\n        #endregion\n\n        #region Shared\n        protected static int GetInteger(BinaryReader input) {\n            var t = input.ReadByte();\n            var type = (IntegerType)t;\n            return GetInteger(input, type);\n        }\n        protected static int GetInteger(BinaryReader input, IntegerType type) {\n            const byte ByteLengthCutoff = 0xF0;\n\n            var t = (byte)type;\n            if (t < ByteLengthCutoff)\n                return t - 1;\n\n            switch (type) {\n                case IntegerType.Byte:\n                    return (input.ReadByte());\n                case IntegerType.ByteTimes256:\n                    return (input.ReadByte() * 256);\n                case IntegerType.Int16: {\n                        int v = 0;\n                        v |= input.ReadByte() << 8;\n                        v |= input.ReadByte();\n                        return (v);\n                    }\n                case IntegerType.Int24: {\n                        int v = 0;\n                        v |= input.ReadByte() << 16;\n                        v |= input.ReadByte() << 8;\n                        v |= input.ReadByte();\n                        return (v);\n                    }\n                case IntegerType.Int32: {\n                        int v = 0;\n                        v |= input.ReadByte() << 24;\n                        v |= input.ReadByte() << 16;\n                        v |= input.ReadByte() << 8;\n                        v |= input.ReadByte();\n                        return (v);\n                    }\n                default:\n                    throw new NotSupportedException();\n            }\n        }\n        #endregion\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/UpdateReport.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\n\nusing SaintCoinach.Ex.Relational.Update;\n\nusing Newtonsoft.Json.Linq;\n\nnamespace SaintCoinach {\n    /// <summary>\n    ///     Class containing the changes found during a definition update.\n    /// </summary>\n    public class UpdateReport {\n        #region Properties\n\n        /// <summary>\n        ///     Gets the version string before the update.\n        /// </summary>\n        /// <value>The version string before the update.</value>\n        public string PreviousVersion { get; internal set; }\n\n        /// <summary>\n        ///     Gets the version string after the update.\n        /// </summary>\n        /// <value>The version string after the update.</value>\n        public string UpdateVersion { get; internal set; }\n\n        /// <summary>\n        ///     Gets the collection of changes.\n        /// </summary>\n        /// <value>The collection of changes.</value>\n        public ICollection<IChange> Changes { get; internal set; }\n\n        #endregion\n\n        #region Constructors\n\n        /// <summary>\n        ///     Initializes a new instance of the <see cref=\"UpdateReport\" /> class.\n        /// </summary>\n        /// <param name=\"previousVersion\">Version string before the update.</param>\n        /// <param name=\"updatedVersion\">Version string after the update.</param>\n        /// <param name=\"changes\">Enumerable of the changes.</param>\n        public UpdateReport(string previousVersion, string updatedVersion, IEnumerable<IChange> changes) {\n            PreviousVersion = previousVersion;\n            UpdateVersion = updatedVersion;\n            Changes = new List<IChange>(changes);\n        }\n\n        #endregion\n\n        #region Serialization\n\n        public JObject ToJson() {\n            return new JObject() {\n                [\"previousVersion\"] = PreviousVersion,\n                [\"updateVersion\"] = UpdateVersion,\n                // I got lazy here.  Can add cleaner serialization for changes if needed.\n                [\"changes\"] = new JArray(Changes.Select(c => JObject.FromObject(c)))\n            };\n        }\n        \n        #endregion\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Xiv/Achievement.cs",
    "content": "﻿using SaintCoinach.Ex.Relational;\nusing SaintCoinach.Imaging;\n\nnamespace SaintCoinach.Xiv {\n    public class Achievement : XivRow, IItemSource {\n        #region Properties\n\n        public AchievementCategory AchievementCategory { get { return As<AchievementCategory>(); } }\n        public Text.XivString Name { get { return AsString(\"Name\"); } }\n        public Text.XivString Description { get { return AsString(\"Description\"); } }\n        public int Points { get { return AsInt32(\"Points\"); } }\n        public Title Title { get { return As<Title>(); } }\n        public Item Item { get { return As<Item>(\"Item\"); } }\n        public ImageFile Icon { get { return AsImage(\"Icon\"); } }\n        public int Order { get { return AsInt32(\"Order\"); } }\n\n        #endregion\n\n        #region Constructors\n\n        #region Constructor\n\n        public Achievement(IXivSheet sheet, IRelationalRow sourceRow) : base(sheet, sourceRow) { }\n\n        #endregion\n\n        #endregion\n\n        public override string ToString() {\n            return Name;\n        }\n\n        #region IItemSource Members\n\n        System.Collections.Generic.IEnumerable<Item> IItemSource.Items {\n            get { yield return Item; }\n        }\n\n        #endregion\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Xiv/AchievementCategory.cs",
    "content": "﻿using SaintCoinach.Ex.Relational;\n\nnamespace SaintCoinach.Xiv {\n    public class AchievementCategory : XivRow {\n        #region Properties\n\n        public Text.XivString Name { get { return AsString(\"Name\"); } }\n        public AchievementKind AchievementKind { get { return As<AchievementKind>(); } }\n\n        #endregion\n\n        #region Constructors\n\n        #region Constructor\n\n        public AchievementCategory(IXivSheet sheet, IRelationalRow sourceRow) : base(sheet, sourceRow) { }\n\n        #endregion\n\n        #endregion\n\n        public override string ToString() {\n            return Name;\n        }\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Xiv/AchievementKind.cs",
    "content": "﻿using SaintCoinach.Ex.Relational;\n\nnamespace SaintCoinach.Xiv {\n    public class AchievementKind : XivRow {\n        #region Properties\n\n        public Text.XivString Name { get { return AsString(\"Name\"); } }\n\n        #endregion\n\n        #region Constructors\n\n        #region Constructor\n\n        public AchievementKind(IXivSheet sheet, IRelationalRow sourceRow) : base(sheet, sourceRow) { }\n\n        #endregion\n\n        #endregion\n\n        public override string ToString() {\n            return Name;\n        }\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Xiv/Action.cs",
    "content": "using System;\n\nusing SaintCoinach.Ex.Relational;\n\nnamespace SaintCoinach.Xiv {\n    public class Action : ClassJobActionBase {\n        #region Properties\n\n        public ActionCategory ActionCategory => As<ActionCategory>();\n        public int SpellGroup => AsInt32(\"SpellGroup\");\n        public int Range => AsInt32(\"Range\");\n        public bool IsRoleAction => AsBoolean(\"IsRoleAction\");\n        public bool CanTargetSelf => AsBoolean(\"CanTargetSelf\");\n        public bool CanTargetParty => AsBoolean(\"CanTargetParty\");\n        public bool CanTargetFriendly => AsBoolean(\"CanTargetFriendly\");\n        public bool CanTargetHostile => AsBoolean(\"CanTargetHostile\");\n        public bool CanTargetDead => AsBoolean(\"CanTargetDead\");\n        public bool TargetArea => AsBoolean(\"TargetArea\");\n        public int EffectRange => AsInt32(\"EffectRange\");\n        public Action ComboFrom => As<Action>(\"Action{Combo}\");\n        public Status GainedStatus => As<Status>(\"Status{GainSelf}\");\n        public ActionCostType CostType => (ActionCostType)As<byte>(\"PrimaryCost{Type}\");\n        public int Cost => AsInt32(\"PrimaryCost{Value}\");\n\n        public TimeSpan CastTime {\n            get { return TimeSpan.FromTicks(TimeSpan.TicksPerMillisecond * 100 * AsInt32(\"Cast<100ms>\")); }\n        }\n\n        public TimeSpan RecastTime {\n            get { return TimeSpan.FromTicks(TimeSpan.TicksPerMillisecond * 100 * AsInt32(\"Recast<100ms>\")); }\n        }\n\n\n        #endregion\n\n        #region Constructors\n\n        #region Constructor\n\n        public Action(IXivSheet sheet, IRelationalRow sourceRow) : base(sheet, sourceRow) { }\n\n        #endregion\n\n        #endregion\n\n        #region Helper\n\n        public int GetMpCost(int level) {\n            var paramGrowSheet = Sheet.Collection.GetSheet<ParamGrow>();\n            if (!paramGrowSheet.ContainsRow(level))\n                return 0;\n            var paramGrow = paramGrowSheet[level];\n\n            return (int)(paramGrow.MpModifier * Cost);\n        }\n\n        #endregion\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Xiv/ActionBase.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nusing SaintCoinach.Ex.Relational;\n\nnamespace SaintCoinach.Xiv {\n    public abstract class ActionBase : XivRow {\n        #region Fields\n        private ActionTransient _ActionTransient;\n        #endregion\n\n        #region Properties\n\n        public Text.XivString Name { get { return AsString(\"Name\"); } }\n        public virtual Text.XivString Description { get { return ActionTransient.Description;  } }\n        public Imaging.ImageFile Icon { get { return AsImage(\"Icon\"); } }\n\n\n        public ActionTransient ActionTransient {  get { return _ActionTransient ?? (_ActionTransient = this.Sheet.Collection.GetSheet<ActionTransient>()[Key]); } }\n        #endregion\n\n        #region Constructors\n\n        protected ActionBase(IXivSheet sheet, IRelationalRow sourceRow) : base(sheet, sourceRow) { }\n\n        #endregion\n\n        public override string ToString() {\n            return Name;\n        }\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Xiv/ActionCategory.cs",
    "content": "using SaintCoinach.Ex.Relational;\n\nnamespace SaintCoinach.Xiv {\n    public class ActionCategory : XivRow {\n        #region Properties\n\n        public Text.XivString Name { get { return AsString(\"Name\"); } }\n\n        #endregion\n\n        #region Constructors\n\n        #region Constructor\n\n        public ActionCategory(IXivSheet sheet, IRelationalRow sourceRow) : base(sheet, sourceRow) { }\n\n        #endregion\n\n        #endregion\n\n        public override string ToString() {\n            return Name;\n        }\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Xiv/ActionTransient.cs",
    "content": "﻿using SaintCoinach.Ex.Relational;\nusing System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace SaintCoinach.Xiv\n{\n    public class ActionTransient : XivRow\n    {\n        #region Constructors\n\n        #region Constructor\n\n        public ActionTransient(IXivSheet sheet, IRelationalRow sourceRow) : base(sheet, sourceRow) { }\n\n        #endregion\n\n        #endregion\n\n        #region Properties\n\n        public Text.XivString Description { get { return AsString(\"Description\"); } }\n        #endregion\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Xiv/Addon.cs",
    "content": "using SaintCoinach.Ex.Relational;\n\nnamespace SaintCoinach.Xiv {\n    public class Addon : XivRow {\n        #region Properties\n\n        public Text.XivString Text { get { return AsString(\"Text\"); } }\n\n        #endregion\n\n        #region Constructors\n\n        #region Constructor\n\n        public Addon(IXivSheet sheet, IRelationalRow sourceRow) : base(sheet, sourceRow) { }\n\n        #endregion\n\n        #endregion\n\n        public override string ToString() {\n            return Text;\n        }\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Xiv/Adventure.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\n\nusing SaintCoinach.Ex.Relational;\n\nnamespace SaintCoinach.Xiv {\n    public class Adventure : XivRow, ILocatable {\n        #region Properties\n\n        public Level Level { get { return As<Level>(); } }\n\n        public Emote Emote { get { return As<Emote>(); } }\n\n        public int MinTime { get { return AsInt32(\"MinTime\"); } }\n\n        public int MaxTime { get { return AsInt32(\"MaxTime\"); } }\n\n        public PlaceName PlaceName { get { return As<PlaceName>(); } }\n\n        public Imaging.ImageFile ListIcon { get { return AsImage(\"Icon{List}\"); } }\n        public Imaging.ImageFile DiscoveredIcon { get { return AsImage(\"Icon{Discovered}\"); } }\n        public Imaging.ImageFile UndiscoveredIcon { get { return AsImage(\"Icon{Undiscovered}\"); } }\n\n        public bool IsInitial { get { return AsBoolean(\"IsInitial\"); } }\n\n        public Text.XivString Name { get { return AsString(\"Name\"); } }\n        public Text.XivString Impression { get { return AsString(\"Impression\"); } }\n        public Text.XivString Description { get { return AsString(\"Description\"); } }\n\n        IEnumerable<ILocation> ILocatable.Locations {\n            get { yield return Level; }\n        }\n\n        #endregion\n\n        #region Constructors\n\n        #region Constructor\n\n        public Adventure(IXivSheet sheet, IRelationalRow sourceRow) : base(sheet, sourceRow) { }\n\n        #endregion\n\n        #endregion\n\n        public override string ToString() {\n            return Name;\n        }\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Xiv/AetherCurrent.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace SaintCoinach.Xiv {\n    [Obsolete(\"Replaced by QuestRewardOther.\")]\n    public class AetherCurrent : XivRow, ILocatable {\n        #region Fields\n\n        bool _HasEObj = false;\n        EObj _EObj;\n\n        #endregion\n\n        #region Properties\n\n        public Quest Quest { get { return As<Quest>(); } }\n\n        public EObj EObj {\n            get {\n                if (!_HasEObj) {\n                    _EObj = Sheet.Collection.GetSheet<EObj>().FirstOrDefault(_ => _.Data == this.Key);\n                    _HasEObj = true;\n                }\n                return _EObj;\n            }\n        }\n\n        public IEnumerable<ILocation> Locations {\n            get {\n                if (EObj == null)\n                    return new ILocation[0];\n                return EObj.Locations;\n            }\n        }\n\n        #endregion\n\n        #region Constructors\n\n        public AetherCurrent(IXivSheet sheet, SaintCoinach.Ex.Relational.IRelationalRow sourceRow) : base(sheet, sourceRow) { }\n\n        #endregion\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Xiv/AirshipExplorationLevel.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace SaintCoinach.Xiv {\n    public class AirshipExplorationLevel : XivRow {\n        #region Properties\n\n        public int ExpToNext { get { return AsInt32(\"ExpToNext\"); } }\n\n        #endregion\n\n        #region Constructors\n\n        public AirshipExplorationLevel(IXivSheet sheet, SaintCoinach.Ex.Relational.IRelationalRow sourceRow) : base(sheet, sourceRow) { }\n\n        #endregion\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Xiv/AirshipExplorationLog.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace SaintCoinach.Xiv {\n    public class AirshipExplorationLog : XivRow {\n        #region Properties\n\n        public Text.XivString Text { get { return AsString(\"Text\"); } }\n\n        #endregion\n\n        #region Constructors\n\n        public AirshipExplorationLog(IXivSheet sheet, SaintCoinach.Ex.Relational.IRelationalRow sourceRow) : base(sheet, sourceRow) { }\n\n        #endregion\n\n        public override string ToString() {\n            return Text;\n        }\n    }\n}"
  },
  {
    "path": "SaintCoinach/Xiv/AirshipExplorationParamType.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace SaintCoinach.Xiv {\n    public class AirshipExplorationParamType : XivRow {\n        #region Properties\n\n        public Text.XivString Name { get { return AsString(\"Name\"); } }\n\n        #endregion\n\n        #region Constructors\n\n        public AirshipExplorationParamType(IXivSheet sheet, SaintCoinach.Ex.Relational.IRelationalRow sourceRow) : base(sheet, sourceRow) { }\n\n        #endregion\n\n        public override string ToString() {\n            return Name;\n        }\n    }\n}"
  },
  {
    "path": "SaintCoinach/Xiv/AirshipExplorationPoint.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace SaintCoinach.Xiv {\n    public class AirshipExplorationPoint  : XivRow {\n        #region Properties\n\n        public Text.XivString Name { get { return AsString(\"Name\"); } }\n        public Text.XivString ShortName { get { return AsString(\"Name{Short}\"); } }\n\n        public int RequiredLevel { get { return AsInt32(\"RequiredLevel\"); } }\n\n        public int RequiredFuel { get { return AsInt32(\"RequiredFuel\"); } }\n\n        public TimeSpan Duration { get { return TimeSpan.FromMinutes(AsInt32(\"Duration<min>\")); } }\n\n        public int ExpReward { get { return AsInt32(\"ExpReward\"); } }\n\n        #endregion\n\n        #region Constructors\n\n        public AirshipExplorationPoint(IXivSheet sheet, SaintCoinach.Ex.Relational.IRelationalRow sourceRow) : base(sheet, sourceRow) { }\n\n        #endregion\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Xiv/BGM.cs",
    "content": "using SaintCoinach.Ex.Relational;\n\nnamespace SaintCoinach.Xiv {\n    // ReSharper disable once InconsistentNaming\n    public class BGM : XivRow {\n        #region Properties\n\n        public Text.XivString File { get { return AsString(\"File\"); } }\n\n        #endregion\n\n        #region Constructors\n\n        #region Constructor\n\n        public BGM(IXivSheet sheet, IRelationalRow sourceRow) : base(sheet, sourceRow) { }\n\n        #endregion\n\n        #endregion\n\n        public override string ToString() {\n            return File;\n        }\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Xiv/BNpc.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace SaintCoinach.Xiv {\n    /// <summary>\n    /// Class representing a combination of <see cref=\"BNpcName\"/> and <see cref=\"BNpcBase\"/>.\n    /// </summary>\n    /// <remarks>\n    /// This class relies on information provided by Libra Eorzea.\n    /// </remarks>\n    public class BNpc : ILocatable, IItemSource {\n        #region Fields\n        private Libra.BNpcName _LibraRow;\n        private BNpcLocation[] _Locations;\n        private Item[] _Items;\n        private InstanceContent[] _InstanceContents;\n        #endregion\n\n        #region Properties\n\n        /// <summary>\n        /// Gets the key of the current BNpc.\n        /// </summary>\n        /// <value>The key of the current BNpc.</value>\n        /// <remarks>\n        /// This value is ((<c>Base.Key</c> * 10000000000) + <c>Name.Key</c>).\n        /// </remarks>\n        public long Key { get; private set; }\n\n        public string FullKey => Base.Key + \"-\" + Name.Key;\n\n        /// <summary>\n        /// Gets the <see cref=\"BNpcBase\"/> of the current BNpc.\n        /// </summary>\n        /// <value>The <see cref=\"BNpcBase\"/> of the current BNpc.</value>\n        public BNpcBase Base { get; private set; }\n\n        /// <summary>\n        /// Gets the <see cref=\"BNpcName\"/> of the current BNpc.\n        /// </summary>\n        /// <value>The <see cref=\"BNpcName\"/> of the current BNpc.</value>\n        public BNpcName Name { get; private set; }\n\n        /// <summary>\n        /// Gets the <see cref=\"Collections.BNpcCollection\"/> of the current BNpc.\n        /// </summary>\n        /// <value>The <see cref=\"Collections.BNpcCollection\"/> of the current BNpc.</value>\n        public Collections.BNpcCollection Collection { get; private set; }\n\n        /// <summary>\n        /// Gets the locations of the current object.\n        /// </summary>\n        /// <value>The locations of the current object.</value>\n        IEnumerable<ILocation> ILocatable.Locations { get { return this.Locations; } }\n\n        /// <summary>\n        /// Gets the locations of the current object.\n        /// </summary>\n        /// <value>The locations of the current object.</value>\n        public IEnumerable<BNpcLocation> Locations { get { return _Locations ?? (_Locations = BuildLocations(_LibraRow)); } }\n\n        /// <summary>\n        /// Gets the items dropped by the current object.\n        /// </summary>\n        /// <value>The items dropped by the current object.</value>\n        public IEnumerable<Item> Items { get { return _Items ?? (_Items = BuildItems(_LibraRow)); } }\n\n        /// <summary>\n        /// Gets the <see cref=\"InstanceContent\"/>s the current BNpc appears in.\n        /// </summary>\n        /// <value>The <see cref=\"InstanceContent\"/>s the current BNpc appears in.</value>\n        public IEnumerable<InstanceContent> InstanceContents { get { return _InstanceContents ?? (_InstanceContents = BuildInstanceContents(_LibraRow)); } }\n\n        #endregion\n\n        #region Constructor\n        public BNpc(Collections.BNpcCollection collection, Libra.BNpcName libra) {\n            this.Collection = collection;\n            _LibraRow = libra;\n            this.Key = libra.Key;\n            this.Base = collection.BaseSheet[(int)libra.BaseKey];\n            this.Name = collection.NameSheet[(int)libra.NameKey];\n        }\n        #endregion\n\n\n        #region Build\n        private BNpcLocation[] BuildLocations(Libra.BNpcName libraRow) {\n            var values = new List<BNpcLocation>();\n            var placeNames = Collection.Collection.GetSheet<PlaceName>();\n\n            foreach (var srcRegion in libraRow.Regions) {\n                var region = placeNames[srcRegion.Item1];\n\n                foreach (var srcZone in srcRegion.Item2) {\n                    var zone = placeNames[srcZone.Item1];\n                    int lvMin, lvMax;\n                    if (srcZone.Item2.Length > 0) {\n                        lvMin = srcZone.Item2.Min();\n                        lvMax = srcZone.Item2.Max();\n                    } else\n                        lvMax = lvMin = 0;\n\n                    values.Add(new BNpcLocation(region, zone, lvMin, lvMax));\n                }\n            }\n\n            return values.ToArray();\n        }\n\n        private Item[] BuildItems(Libra.BNpcName libraRow) {\n            var values = new List<Item>();\n            var items = Collection.Collection.GetSheet<Item>();\n\n            foreach (var srcItem in libraRow.Items) {\n                values.Add(items[srcItem]);\n            }\n\n            return values.ToArray();\n        }\n\n        private InstanceContent[] BuildInstanceContents(Libra.BNpcName libraRow) {\n            var values = new List<InstanceContent>();\n            var instanceContents = Collection.Collection.GetSheet<InstanceContent>();\n\n            foreach (var srcContent in libraRow.InstanceContents) {\n                values.Add(instanceContents[srcContent]);\n            }\n\n            return values.ToArray();\n        }\n        #endregion\n\n        public override string ToString() {\n            return Name.Singular;\n        }\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Xiv/BNpcBase.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nusing SaintCoinach.Ex.Relational;\n\nnamespace SaintCoinach.Xiv {\n    public class BNpcBase : XivRow {\n        #region Properties\n\n        /// <summary>\n        /// Gets the scale to use for the current BNpc.\n        /// </summary>\n        /// <value>The scale to use for the current BNpc.</value>\n        public double Scale { get { return AsDouble(\"Scale\"); } }\n\n        /// <summary>\n        /// Gets the <see cref=\"ModelChara\"/> to use for the current BNpc.\n        /// </summary>\n        /// <value>The <see cref=\"ModelChara\"/> to use for the current BNpc.</value>\n        public ModelChara ModelChara { get { return As<ModelChara>(); } }\n\n        /// <summary>\n        /// Gets the BNpcCustomize-row to use for the current BNpc.\n        /// </summary>\n        /// <value>The BNpcCustomize-row to use for the current BNpc.</value>\n        public IXivRow BNpcCustomize { get { return As<IXivRow>(\"BNpcCustomize\"); } }\n\n        /// <summary>\n        /// Gets the NpcEquip-row to use for the current BNpc.\n        /// </summary>\n        /// <value>The NpcEquip-row to use for the current BNpc.</value>\n        public IXivRow NpcEquip { get { return As<IXivRow>(\"NpcEquip\"); } }\n        #endregion\n\n        #region Constructors\n\n        /// <summary>\n        /// Initializes a new instance of the <see cref=\"BNpcBase\"/> class.\n        /// </summary>\n        /// <param name=\"sheet\"><see cref=\"IXivSheet\"/> containing this object.</param>\n        /// <param name=\"sourceRow\"><see cref=\"IRelationalRow\"/> to read data from.</param>\n        public BNpcBase(IXivSheet sheet, IRelationalRow sourceRow) : base(sheet, sourceRow) { }\n\n        #endregion\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Xiv/BNpcData.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace SaintCoinach.Xiv {\n    /// <summary>\n    /// Class containing data about a BNpc.\n    /// </summary>\n    public class BNpcData {\n        #region Fields\n        private BNpcBase _Base;\n        private BNpcName _Name;\n        #endregion\n\n        #region Constructor\n        public BNpcData(XivCollection collection, Libra.BNpcName libraRow) {\n            _Base = collection.GetSheet<BNpcBase>()[(int)libraRow.BaseKey];\n            _Name = collection.GetSheet<BNpcName>()[(int)libraRow.NameKey];\n        }\n        #endregion\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Xiv/BNpcLocation.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace SaintCoinach.Xiv {\n    public class BNpcLocation : ILocation {\n        #region Properties\n\n        double ILocation.MapX { get { return double.NaN; } }\n        double ILocation.MapY { get { return double.NaN; } }\n\n        public PlaceName RegionPlaceName{ get; private set; }\n        public PlaceName PlaceName { get; private set; }\n\n        public int MinimumLevel { get; private set; }\n        public int MaximumLevel { get; private set; }\n\n        #endregion\n\n        #region Constructors\n        public BNpcLocation(PlaceName regionPlaceName, PlaceName placeName, int minimumLevel, int maximumLevel) {\n            this.RegionPlaceName = regionPlaceName;\n            this.PlaceName = placeName;\n            this.MinimumLevel = minimumLevel;\n            this.MaximumLevel = maximumLevel;\n        }\n        #endregion\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Xiv/BNpcName.cs",
    "content": "using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing SaintCoinach.Ex.Relational;\n\nnamespace SaintCoinach.Xiv {\n    public class BNpcName : XivRow, ILocatable, IItemSource, IQuantifiableXivString {\n\n        #region Fields\n        private ILocation[] _Locations;\n        private Item[] _Items;\n        private InstanceContent[] _InstanceContents;\n        #endregion\n\n        #region Properties\n\n        public Text.XivString Singular { get { return AsString(\"Singular\"); } }\n        public Text.XivString Plural { get { return Sheet.Collection.ActiveLanguage == Ex.Language.Japanese ? Singular : AsString(\"Plural\"); } }\n\n        public IEnumerable<BNpc> BNpcs {\n            get {\n                if (!Sheet.Collection.IsLibraAvailable)\n                    return new BNpc[0];\n                return Sheet.Collection.BNpcs.Where(i => i.Name == this);\n            }\n        }\n        public IEnumerable<ILocation> Locations { get { return _Locations ?? (_Locations = BNpcs.SelectMany(i => i.Locations).ToArray()); } }\n        public IEnumerable<Item> Items { get { return _Items ?? (_Items = BNpcs.SelectMany(i => i.Items).ToArray()); } }\n        public IEnumerable<InstanceContent> InstanceContents { get { return _InstanceContents ?? (_InstanceContents = BNpcs.SelectMany(i => i.InstanceContents).ToArray()); } }\n\n        #endregion\n\n        #region Constructors\n\n        public BNpcName(IXivSheet sheet, IRelationalRow sourceRow) : base(sheet, sourceRow) { }\n\n        #endregion\n\n        public override string ToString() {\n            return Singular;\n        }\n\n        #region IQuantifiableName Members\n        string IQuantifiable.Singular {\n            get { return Singular; }\n        }\n\n        string IQuantifiable.Plural {\n            get { return Plural; }\n        }\n        #endregion\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Xiv/BaseParam.cs",
    "content": "using System;\n\nusing SaintCoinach.Ex.Relational;\n\nnamespace SaintCoinach.Xiv {\n    /// <summary>\n    /// Class representing parameters from the game data.\n    /// </summary>\n    public class BaseParam : XivRow {\n        #region Properties\n        /// <summary>\n        /// Gets the name of the parameter.\n        /// </summary>\n        /// <value>The name of the parameter.</value>\n        public Text.XivString Name { get { return AsString(\"Name\"); } }\n        /// <summary>\n        /// Gets the description of the parameter.\n        /// </summary>\n        /// <remarks>Not all parameters have a description.</remarks>\n        /// <value>The description of the parameter.</value>\n        public Text.XivString Description { get { return AsString(\"Description\"); } }\n\n        #endregion\n\n        #region Constructors\n\n        /// <summary>\n        /// Initializes a new instance of the <see cref=\"BaseParam\"/> class.\n        /// </summary>\n        /// <param name=\"sheet\"><see cref=\"IXivSheet\"/> containing this object.</param>\n        /// <param name=\"sourceRow\"><see cref=\"IRelationalRow\"/> to read data from.</param>\n        public BaseParam(IXivSheet sheet, IRelationalRow sourceRow) : base(sheet, sourceRow) { }\n\n        #endregion\n\n\n        /// <summary>\n        ///     Returns a string that represents the current <see cref=\"BaseParam\" />.\n        /// </summary>\n        /// <returns>Returns the value of <see cref=\"Name\" />.</returns>\n        public override string ToString() {\n            return Name;\n        }\n\n        #region Helpers\n\n        /// <summary>\n        /// Get the maximum value of the current <see cref=\"BaseParam\"/> for a specific <see cref=\"EquipSlotCategory\"/>.\n        /// </summary>\n        /// <param name=\"category\"><see cref=\"EquipSlotCategory\"/> to get the maximum parameter value for.</param>\n        /// <returns>Returns the maximum value for the current <see cref=\"BaseParam\"/> on <c>category</c>.</returns>\n        public int GetMaximum(EquipSlotCategory category) {\n            const int Offset = 3;\n            return category.Key == 0 ? 0 : Convert.ToInt32(this[Offset + category.Key]);\n        }\n\n        /// <summary>\n        /// Get the value modifier of the current <see cref=\"BaseParam\"/> for a certain role.\n        /// </summary>\n        /// <param name=\"role\">Role to get the modifier for.</param>\n        /// <returns>Returns the modifier for <c>role</c>, in percent.</returns>\n        public int GetModifier(int role) {\n            const int Offset = 25;\n            const int Maximum = 12;\n            if (role < 0 || role > Maximum)\n                return 0;\n            return Convert.ToInt32(this[Offset + role]);\n        }\n\n        #endregion\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Xiv/BeastReputationRank.cs",
    "content": "using SaintCoinach.Ex.Relational;\n\nnamespace SaintCoinach.Xiv {\n    public class BeastReputationRank : XivRow {\n        #region Properties\n\n        public Text.XivString Name { get { return AsString(\"Name\"); } }\n        public int RequiredReputation { get { return AsInt32(\"RequiredReputation\"); } }\n\n        #endregion\n\n        #region Constructors\n\n        #region Constructor\n\n        public BeastReputationRank(IXivSheet sheet, IRelationalRow sourceRow) : base(sheet, sourceRow) { }\n\n        #endregion\n\n        #endregion\n\n        public override string ToString() {\n            return Name;\n        }\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Xiv/BeastTribe.cs",
    "content": "using SaintCoinach.Ex.Relational;\nusing SaintCoinach.Imaging;\n\nnamespace SaintCoinach.Xiv {\n    public class BeastTribe : XivRow {\n        #region Properties\n\n        public Text.XivString Name { get { return AsString(\"Name\"); } }\n        public Text.XivString RelationName { get { return AsString(\"Name{Relation}\"); } }\n        public ImageFile Icon { get { return AsImage(\"Icon\"); } }\n        public ImageFile ReputationIcon { get { return AsImage(\"Icon{Reputation}\"); } }\n\n        #endregion\n\n        #region Constructors\n\n        #region Constructor\n\n        public BeastTribe(IXivSheet sheet, IRelationalRow sourceRow) : base(sheet, sourceRow) { }\n\n        #endregion\n\n        #endregion\n\n        public override string ToString() {\n            return Name;\n        }\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Xiv/BuddyAction.cs",
    "content": "using SaintCoinach.Ex.Relational;\nusing SaintCoinach.Imaging;\n\nnamespace SaintCoinach.Xiv {\n    public class BuddyAction : XivRow {\n        #region Properties\n\n        public Text.XivString Name { get { return AsString(\"Name\"); } }\n        public Text.XivString Description { get { return AsString(\"Description\"); } }\n        public ImageFile Icon { get { return AsImage(\"Icon\"); } }\n        public ImageFile StatusIcon { get { return AsImage(\"Icon{Status}\"); } }\n\n        #endregion\n\n        #region Constructors\n\n        #region Constructor\n\n        public BuddyAction(IXivSheet sheet, IRelationalRow sourceRow) : base(sheet, sourceRow) { }\n\n        #endregion\n\n        #endregion\n\n        public override string ToString() {\n            return Name;\n        }\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Xiv/BuddyEquip.cs",
    "content": "using SaintCoinach.Ex.Relational;\nusing SaintCoinach.Imaging;\n\nnamespace SaintCoinach.Xiv {\n    public class BuddyEquip : XivRow, IQuantifiableXivString {\n        #region Properties\n\n        public Text.XivString Name { get { return AsString(\"Name\"); } }\n        public Text.XivString Singular { get { return AsString(\"Singular\"); } }\n        public Text.XivString Plural { get { return Sheet.Collection.ActiveLanguage == Ex.Language.Japanese ? Singular : AsString(\"Plural\"); } }\n        public GrandCompany GrandCompany { get { return As<GrandCompany>(); } }\n        public ImageFile HeadIcon { get { return AsImage(\"Icon{Head}\"); } }\n        public ImageFile BodyIcon { get { return AsImage(\"Icon{Body}\"); } }\n        public ImageFile LegsIcon { get { return AsImage(\"Icon{Legs}\"); } }\n\n        #endregion\n\n        #region Constructors\n\n        #region Constructor\n\n        public BuddyEquip(IXivSheet sheet, IRelationalRow sourceRow) : base(sheet, sourceRow) { }\n\n        #endregion\n\n        #endregion\n\n        public override string ToString() {\n            return Name;\n        }\n\n        #region IQuantifiableName Members\n        string IQuantifiable.Singular {\n            get { return Singular; }\n        }\n\n        string IQuantifiable.Plural {\n            get { return Plural; }\n        }\n        #endregion\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Xiv/CharaMakeCustomize.cs",
    "content": "﻿using SaintCoinach.Ex.Relational;\nusing SaintCoinach.Imaging;\n\nnamespace SaintCoinach.Xiv {\n    public class CharaMakeCustomize : XivRow, IXivRow {\n        #region Properties\n        public int FeatureID { get { return AsInt32(\"FeatureID\"); } }\n        public ImageFile Icon { get { return AsImage(\"Icon\"); } }\n        public int Data { get { return AsInt32(\"Data\"); } }\n        public bool IsPurchasable => AsBoolean(\"IsPurchasable\");\n        #endregion\n\n        public CharaMakeCustomize(IXivSheet sheet, IRelationalRow sourceRow) : base(sheet, sourceRow) { }\n\n    }\n}"
  },
  {
    "path": "SaintCoinach/Xiv/CharaMakeType.cs",
    "content": "﻿using SaintCoinach.Ex.Relational;\nusing SaintCoinach.Imaging;\nusing System.Collections.Generic;\n\nnamespace SaintCoinach.Xiv {\n    public class CharaMakeType : XivRow, IXivRow {\n        private CharaMakeFeatureIcon[] _Items;\n        #region Properties\n        public Race Race { get { return As<Race>(); } }\n        public Tribe Tribe { get { return As<Tribe>(); } }\n        public IEnumerable<CharaMakeFeatureIcon> FacialFeatureIcon { get { return _Items ?? (_Items = BuildItems()); } }\n        public int Gender { get { return AsInt32(\"Gender\"); } }\n        #endregion\n\n\n        public CharaMakeType(IXivSheet sheet, IRelationalRow sourceRow) : base(sheet, sourceRow) { }\n\n        private CharaMakeFeatureIcon[] BuildItems() {\n            const int Count = 56;\n\n            var items = new List<CharaMakeFeatureIcon>();\n\n            for (var i = 0; i < Count; ++i) {\n                var item = AsImage(\"FacialFeatureIcon\", i);\n                if (item == null) continue;\n                items.Add(new CharaMakeFeatureIcon(item, i));\n            }\n\n            return items.ToArray();\n        }\n        public class CharaMakeFeatureIcon {\n            public ImageFile FacialFeatureIcon { get; private set; }\n            public int Count { get; private set; }\n            public CharaMakeFeatureIcon(ImageFile image, int count) {\n                FacialFeatureIcon = image;\n                Count = count;\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Xiv/ChocoboRace.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nusing SaintCoinach.Ex.Relational;\n\nnamespace SaintCoinach.Xiv {\n    public class ChocoboRace : XivRow {\n        #region Properties\n\n        public ChocoboRaceRank Rank { get { return As<ChocoboRaceRank>(); } }\n        public ChocoboRaceTerritory Territory { get { return As<ChocoboRaceTerritory>(); } }\n\n        #endregion\n\n        #region Constructors\n\n        public ChocoboRace(IXivSheet sheet, IRelationalRow sourceRow) : base(sheet, sourceRow) { }\n\n        #endregion\n\n        public override string ToString() {\n            return string.Format(\"{0}: {1}\", Rank.Name, Territory.Name);\n        }\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Xiv/ChocoboRaceAbility.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nusing SaintCoinach.Ex.Relational;\n\nnamespace SaintCoinach.Xiv {\n    public class ChocoboRaceAbility : ActionBase {\n        #region Properties\n\n        public ChocoboRaceAbilityType Type { get { return As<ChocoboRaceAbilityType>(); } }\n        public int Strength { get { return AsInt32(\"Value\"); } }\n\n        #endregion\n\n        #region Constructors\n\n        public ChocoboRaceAbility(IXivSheet sheet, IRelationalRow sourceRow) : base(sheet, sourceRow) { }\n\n        #endregion\n\n\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Xiv/ChocoboRaceAbilityType.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nusing SaintCoinach.Ex.Relational;\n\nnamespace SaintCoinach.Xiv {\n    public class ChocoboRaceAbilityType : XivRow {\n        #region Properties\n\n        public bool IsActive { get { return AsBoolean(\"IsActive\"); } }\n\n        #endregion\n\n        #region Constructors\n\n        public ChocoboRaceAbilityType(IXivSheet sheet, IRelationalRow sourceRow) : base(sheet, sourceRow) { }\n\n        #endregion\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Xiv/ChocoboRaceItem.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nusing SaintCoinach.Ex.Relational;\n\nnamespace SaintCoinach.Xiv {\n    public class ChocoboRaceItem : ActionBase {\n        #region Constructors\n\n        public ChocoboRaceItem(IXivSheet sheet, IRelationalRow sourceRow) : base(sheet, sourceRow) { }\n\n        #endregion\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Xiv/ChocoboRaceRank.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nusing SaintCoinach.Ex.Relational;\n\nnamespace SaintCoinach.Xiv {\n    public class ChocoboRaceRank : XivRow {\n        #region Properties\n\n        public Text.XivString Name { get { return As<GoldSaucerTextData>(\"Name\").Text; } }\n        public int MinimumRating { get { return AsInt32(\"Rating{Min}\"); } }\n        public int MaximumRating { get { return AsInt32(\"Rating{Max}\"); } }\n        public int Fee { get { return AsInt32(\"Fee\"); } }\n        public Imaging.ImageFile Icon { get { return AsImage(\"Icon\"); } }\n\n        #endregion\n\n        #region Constructors\n\n        public ChocoboRaceRank(IXivSheet sheet, IRelationalRow sourceRow) : base(sheet, sourceRow) { }\n\n        #endregion\n\n        public override string ToString() {\n            return Name;\n        }\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Xiv/ChocoboRaceStatus.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nusing SaintCoinach.Ex.Relational;\n\nnamespace SaintCoinach.Xiv {\n    public class ChocoboRaceStatus : XivRow {\n        #region Properties\n\n        public Status Status { get { return As<Status>(); } }\n\n        #endregion\n\n        #region Constructors\n\n        public ChocoboRaceStatus(IXivSheet sheet, IRelationalRow sourceRow) : base(sheet, sourceRow) { }\n\n        #endregion\n\n        public override string ToString() {\n            return Status.Name;\n        }\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Xiv/ChocoboRaceTerritory.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nusing SaintCoinach.Ex.Relational;\n\nnamespace SaintCoinach.Xiv {\n    public class ChocoboRaceTerritory : XivRow {\n        #region Properties\n\n        public Text.XivString Name { get { return As<GoldSaucerTextData>(\"Name\").Text; } }\n        public Imaging.ImageFile Icon { get { return AsImage(\"Icon\"); } }\n\n        #endregion\n\n        #region Constructors\n\n        public ChocoboRaceTerritory(IXivSheet sheet, IRelationalRow sourceRow) : base(sheet, sourceRow) { }\n\n        #endregion\n\n        public override string ToString() {\n            return Name;\n        }\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Xiv/ClassJob.cs",
    "content": "using SaintCoinach.Ex.Relational;\nusing SaintCoinach.Imaging;\nusing SaintCoinach.IO;\n\nnamespace SaintCoinach.Xiv {\n    public class ClassJob : XivRow {\n        #region Static\n\n        private const int IconOffset = 62000;\n        private const int FramedIconOffset = 62100;\n        private const string IconFormat = \"ui/icon/{0:D3}000/{1:D6}.tex\";\n\n        #endregion\n\n        #region Properties\n\n        public Text.XivString Name { get { return AsString(\"Name\"); } }\n        public Text.XivString Abbreviation { get { return AsString(\"Abbreviation\"); } }\n        public ClassJobCategory ClassJobCategory { get { return As<ClassJobCategory>(); } }\n        public ClassJob ParentClassJob { get { return As<ClassJob>(\"ClassJob{Parent}\"); } }\n        public Item StartingWeapon { get { return As<Item>(\"Item{StartingWeapon}\"); } }\n        public Item SoulCrystal { get { return As<Item>(\"Item{SoulCrystal}\"); } }\n        public byte StartingLevel {  get { return As<byte>(\"StartingLevel\"); } }\n\n        public ImageFile Icon {\n            get {\n                var nr = IconOffset + Key;\n                var path = string.Format(IconFormat, nr / 1000, nr);\n                if (Sheet.Collection.PackCollection.TryGetFile(path, out var file))\n                    return file as ImageFile;\n                return null;\n            }\n        }\n\n        public ImageFile FramedIcon {\n            get {\n                var nr = FramedIconOffset + Key;\n                var path = string.Format(IconFormat, nr / 1000, nr);\n                if (Sheet.Collection.PackCollection.TryGetFile(path, out var file))\n                    return file as ImageFile;\n                return null;\n            }\n        }\n\n\n        #endregion\n\n        #region Constructors\n\n        #region Constructor\n\n        public ClassJob(IXivSheet sheet, IRelationalRow sourceRow) : base(sheet, sourceRow) { }\n\n        #endregion\n\n        #endregion\n\n        public override string ToString() {\n            return Name;\n        }\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Xiv/ClassJobActionBase.cs",
    "content": "using System;\nusing SaintCoinach.Ex.Relational;\nusing SaintCoinach.Imaging;\n\nnamespace SaintCoinach.Xiv {\n    public enum ActionCostType : byte {\n        NoCost = 0,\n        HPPercent,\n        Unknown2,\n        MP,\n        MPAll,\n        TP,\n        Unknown6,\n        GP,\n        CP,\n        Unknown9,\n        StatusAll,\n        LimitBreakGauge,\n        Unknown12,\n        Unknown13,\n        AstrologianCard,\n        Unknown15,\n        StatusAmount,\n        Unknown17,\n        TPAll,\n        Unknown19,\n        Unknown20, // Contents Actions?\n        Unknown21,\n        BeastGauge,\n        Polyglot,\n        DreadwyrmAether,\n        BloodGauge,\n        Unknown26,\n        NinkiGauge,\n        Chakra,\n        GreasedLightning,\n        Aetherflow,\n        AethertrailAttunement,\n        Repertoire,\n        AstrologianCard2,\n        AstrologianCard3,\n        AstrologianCard4,\n        AstrologianCard5,\n        AstrologianCard6,\n        Unknown38,\n        KenkiGauge,\n        Unknown40,\n        OathGauge,\n        Unknown42,\n        BalanceGauge,\n        Unknown44,\n        KenkiGauge2,\n        FaerieGauge,\n        DreadwyrmTrance,\n        UnknownDragoon48,\n        LilyAll,\n        AstralFireOrUmbralIce,\n        MP2,\n        Ceruleum,\n        FourfoldFeather,\n        Espirit,\n        Cartridge,\n        BloodLily,\n        Lily,\n        SealsAll,\n        SoulVoice,\n        Unknown60,\n        Heat,\n        Battery,\n        Meditation,\n        Soul,\n        Shroud,\n        LemureShroud,\n        VoidShroud,\n        Addersgall,\n        Addersting, \n        Unknown70, // Don't know what's this. maybe a skill dismisser, used by Eukrasian Diagnosis\n        Arcanum, // This is strange, mark this, 1 is ifrit, 2 is titan, 3 is garuda\n        FireAttunement,\n        EarthAttunement,\n        WindAttunement,\n        Firstmind,\n        Paradox,\n        Unknown77,\n        ManaStack,\n        BeastChakra,\n        Unknown80,\n        Coda,\n        Enshrouded,\n        Unknown83, // Don't know what's this for, used by Eukrasian Prognosis\n    }\n\n    public abstract class ClassJobActionBase : ActionBase {\n        #region Properties\n\n        public ClassJob ClassJob { get { return As<ClassJob>(); } }\n        public ClassJobCategory ClassJobCategory { get { return As<ClassJobCategory>(); } }\n        public int ClassJobLevel { get { return AsInt32(\"ClassJobLevel\"); } }\n\n        #endregion\n\n        #region Constructors\n\n        protected ClassJobActionBase(IXivSheet sheet, IRelationalRow sourceRow) : base(sheet, sourceRow) { }\n\n        #endregion\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Xiv/ClassJobCategory.cs",
    "content": "using System;\nusing System.Collections.Generic;\n\nusing SaintCoinach.Ex.Relational;\n\nnamespace SaintCoinach.Xiv {\n    public class ClassJobCategory : XivRow {\n        #region Fields\n\n        private ClassJob[] _ClassJobs;\n\n        #endregion\n\n        #region Properties\n\n        public Text.XivString Name { get { return AsString(\"Name\"); } }\n        public IEnumerable<ClassJob> ClassJobs { get { return _ClassJobs ?? (_ClassJobs = BuildClassJobs()); } }\n\n        #endregion\n\n        #region Constructors\n\n        #region Constructor\n\n        public ClassJobCategory(IXivSheet sheet, IRelationalRow sourceRow) : base(sheet, sourceRow) { }\n\n        #endregion\n\n        #endregion\n\n        #region Build\n\n        private ClassJob[] BuildClassJobs() {\n            const int ColumnOffset = 1;\n\n            var cjs = new List<ClassJob>();\n            var cjSheet = Sheet.Collection.GetSheet<ClassJob>();\n            foreach (var cj in cjSheet) {\n                var isValid = Convert.ToBoolean(this[ColumnOffset + cj.Key]);\n                if (isValid)\n                    cjs.Add(cj);\n            }\n            return cjs.ToArray();\n        }\n\n        #endregion\n\n        public override string ToString() {\n            return Name;\n        }\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Xiv/Collections/BNpcCollection.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace SaintCoinach.Xiv.Collections {\n    public class BNpcCollection : IEnumerable<BNpc> {\n        #region Fields\n        private Dictionary<long, BNpc> _InnerDictionary = new Dictionary<long, BNpc>();\n        private IEnumerable<Libra.BNpcName> _LibraEntries;\n        #endregion\n\n        #region Properties\n        public XivCollection Collection { get; private set; }\n        public IXivSheet<BNpcBase> BaseSheet { get; private set; }\n        public IXivSheet<BNpcName> NameSheet { get; private set; }\n        #endregion\n\n        #region Constructor\n        public BNpcCollection(XivCollection collection) {\n            if (collection == null)\n                throw new ArgumentNullException(\"collection\");\n            if (!collection.IsLibraAvailable)\n                throw new NotSupportedException(\"BNpcs are only available when Libra Eorzea database is present.\");\n            Collection = collection;\n            BaseSheet = collection.GetSheet<BNpcBase>();\n            NameSheet = collection.GetSheet<BNpcName>();\n        }\n        #endregion\n\n        #region Things\n        public BNpc this[long key] { get { return GetOrCreate(key); } }\n        #endregion\n\n        #region Create\n        private BNpc GetOrCreate(long key) {\n            BNpc npc;\n            if (!_InnerDictionary.TryGetValue(key, out npc))\n                npc = Create(key);\n            return npc;\n        }\n        private BNpc Create(long key) {\n            return Create(Collection.Libra.BNpcNames.First(i => i.Key == key));\n        }\n        private BNpc GetOrCreate(Libra.BNpcName libra) {\n            BNpc npc;\n            if (!_InnerDictionary.TryGetValue(libra.Key, out npc))\n                npc = Create(libra);\n            return npc;\n        }\n        private BNpc Create(Libra.BNpcName libra) {\n            return new BNpc(this, libra);\n        }\n        #endregion\n\n        #region Enumerator\n        private class Enumerator : IEnumerator<BNpc> {\n            #region Fields\n            private IEnumerator<Libra.BNpcName> _LibraEnumerator;\n            private BNpcCollection _Collection;\n            #endregion\n\n            #region Constructor\n            public Enumerator(BNpcCollection collection) {\n                _Collection = collection;\n                if (collection._LibraEntries == null)\n                    collection._LibraEntries = collection.Collection.Libra.BNpcNames.ToArray();\n                _LibraEnumerator = collection._LibraEntries.GetEnumerator();\n            }\n            #endregion\n\n            #region IEnumerator<BNpc> Members\n\n            public BNpc Current {\n                get { return _Collection.GetOrCreate(_LibraEnumerator.Current); }\n            }\n\n            #endregion\n\n            #region IDisposable Members\n\n            public void Dispose() {\n                try {\n                    if (_LibraEnumerator != null)\n                        _LibraEnumerator.Dispose();\n                } finally { _LibraEnumerator = null; }\n            }\n\n            #endregion\n\n            #region IEnumerator Members\n\n            object System.Collections.IEnumerator.Current {\n                get { return Current; }\n            }\n\n            public bool MoveNext() {\n                return _LibraEnumerator.MoveNext();\n            }\n\n            public void Reset() {\n                _LibraEnumerator.Reset();\n            }\n\n            #endregion\n        }\n        #endregion\n\n        #region IEnumerable<BNpc> Members\n\n        public IEnumerator<BNpc> GetEnumerator() {\n            return new Enumerator(this);\n        }\n\n        #endregion\n\n        #region IEnumerable Members\n\n        System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() {\n            return GetEnumerator();\n        }\n\n        #endregion\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Xiv/Collections/ClassJobActionCollection.cs",
    "content": "﻿using System;\nusing System.Collections;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace SaintCoinach.Xiv.Collections {\n    public class ClassJobActionCollection : IEnumerable<ClassJobActionBase> {\n        #region Fields\n\n        private readonly Range[] _ActionRanges;\n        private readonly Range[] _CraftActionRanges;\n\n        #endregion\n\n        #region Properties\n\n        public XivCollection Collection { get; private set; }\n        public IXivSheet<Action> ActionSheet { get; private set; }\n        public IXivSheet<CraftAction> CraftActionSheet { get; private set; }\n\n        #endregion\n\n        #region Constructors\n\n        public ClassJobActionCollection(XivCollection collection) {\n            Collection = collection;\n\n            ActionSheet = collection.GetSheet<Action>();\n            CraftActionSheet = collection.GetSheet<CraftAction>();\n\n            _ActionRanges = Range.Combine(ActionSheet.Header.DataFileRanges);\n            _CraftActionRanges = Range.Combine(CraftActionSheet.Header.DataFileRanges);\n        }\n\n        #endregion\n\n        #region IEnumerable<ClassJobActionBase> Members\n\n        public IEnumerator<ClassJobActionBase> GetEnumerator() {\n            return new Enumerator(this);\n        }\n\n        #endregion\n\n        #region IEnumerable Members\n\n        IEnumerator IEnumerable.GetEnumerator() {\n            return GetEnumerator();\n        }\n\n        #endregion\n\n        #region Enumerator\n        class Enumerator : IEnumerator<ClassJobActionBase> {\n            #region Fields\n\n            private readonly IEnumerator<Action> _ActionEnumerator;\n            private readonly IEnumerator<CraftAction> _CraftActionEnumerator;\n            private int _State;\n\n            #endregion\n\n            #region Constructors\n\n            #region Constructor\n            \n            public Enumerator(ClassJobActionCollection collection) {\n                _ActionEnumerator = collection.ActionSheet.GetEnumerator();\n                _CraftActionEnumerator = collection.CraftActionSheet.GetEnumerator();\n            }\n\n            #endregion\n\n            #endregion\n\n            #region IEnumerator<Item> Members\n\n            public ClassJobActionBase Current { get; private set; }\n\n            #endregion\n\n            #region IDisposable Members\n\n            public void Dispose() {\n                _ActionEnumerator.Dispose();\n                _CraftActionEnumerator.Dispose();\n            }\n\n            #endregion\n\n            #region IEnumerator Members\n\n            object IEnumerator.Current { get { return Current; } }\n\n            public bool MoveNext() {\n                var result = false;\n\n                Current = null;\n                if (_State == 0) {\n                    result = _ActionEnumerator.MoveNext();\n                    if (result)\n                        Current = _ActionEnumerator.Current;\n                    else\n                        ++_State;\n                }\n                // ReSharper disable once InvertIf\n                if (_State == 1) {\n                    result = _CraftActionEnumerator.MoveNext();\n                    if (result)\n                        Current = _CraftActionEnumerator.Current;\n                    else\n                        ++_State;\n                }\n\n                return result;\n            }\n\n            public void Reset() {\n                _State = 0;\n                _ActionEnumerator.Reset();\n                _CraftActionEnumerator.Reset();\n            }\n\n            #endregion\n        }\n        #endregion\n\n\n        #region Things\n\n        public ClassJobActionBase this[int index] {\n            get {\n                if (Range.Contains(_ActionRanges, index))\n                    return ActionSheet[index];\n                if (Range.Contains(_CraftActionRanges, index))\n                    return CraftActionSheet[index];\n                throw new KeyNotFoundException();\n            }\n        }\n\n        public bool ContainsRow(int index) {\n            if (Range.Contains(_ActionRanges, index))\n                return ActionSheet.ContainsRow(index);\n            if (Range.Contains(_CraftActionRanges, index))\n                return CraftActionSheet.ContainsRow(index);\n            return false;\n        }\n\n        #endregion\n\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Xiv/Collections/ENpcCollection.cs",
    "content": "﻿using System.Collections;\nusing System.Collections.Generic;\n\nnamespace SaintCoinach.Xiv.Collections {\n    public class ENpcCollection : IEnumerable<ENpc> {\n        #region Fields\n\n        private readonly Dictionary<int, ENpc> _Inner = new Dictionary<int, ENpc>();\n        private Dictionary<int, List<ENpc>> _ENpcDataMap;\n\n        #endregion\n\n        #region Properties\n\n        public XivCollection Collection { get; private set; }\n        public IXivSheet<ENpcBase> BaseSheet { get; private set; }\n        public IXivSheet<ENpcResident> ResidentSheet { get; private set; }\n\n        #endregion\n\n        #region Constructors\n\n        public ENpcCollection(XivCollection xivCollection) {\n            Collection = xivCollection;\n            BaseSheet = xivCollection.GetSheet<ENpcBase>();\n            ResidentSheet = xivCollection.GetSheet<ENpcResident>();\n        }\n\n        #endregion\n\n        #region IEnumerable<ENpc> Members\n\n        public IEnumerator<ENpc> GetEnumerator() {\n            return new Enumerator(this);\n        }\n\n        #endregion\n\n        #region IEnumerable Members\n\n        IEnumerator IEnumerable.GetEnumerator() {\n            return GetEnumerator();\n        }\n\n        #endregion\n\n        #region Get\n\n        public ENpc this[int key] {\n            get { return Get(key); }\n        }\n        public ENpc Get(int key) {\n            if (_Inner.ContainsKey(key))\n                return _Inner[key];\n\n            var enpc = new ENpc(this, key);\n            _Inner.Add(key, enpc);\n            return enpc;\n        }\n\n        #endregion\n\n        #region Enumerator\n\n        private class Enumerator : IEnumerator<ENpc> {\n            #region Fields\n\n            private readonly IEnumerator<ENpcBase> _BaseEnumerator;\n            private readonly ENpcCollection _Collection;\n\n            #endregion\n\n            #region Constructors\n\n            #region Constructor\n\n            public Enumerator(ENpcCollection collection) {\n                _Collection = collection;\n                _BaseEnumerator = collection.BaseSheet.GetEnumerator();\n            }\n\n            #endregion\n\n            #endregion\n\n            #region IEnumerator<ENpc> Members\n\n            public ENpc Current { get; private set; }\n\n            #endregion\n\n            #region IDisposable Members\n\n            public void Dispose() {\n                _BaseEnumerator.Dispose();\n            }\n\n            #endregion\n\n            #region IEnumerator Members\n\n            object IEnumerator.Current { get { return Current; } }\n\n            public bool MoveNext() {\n                var result = _BaseEnumerator.MoveNext();\n                Current = result ? _Collection.Get(_BaseEnumerator.Current.Key) : null;\n                return result;\n            }\n\n            public void Reset() {\n                Current = null;\n                _BaseEnumerator.Reset();\n            }\n\n            #endregion\n        }\n\n        #endregion\n\n        #region Find\n\n        public IEnumerable<ENpc> FindWithData(int value) {\n            if (_ENpcDataMap == null)\n                _ENpcDataMap = BuildDataMap();\n            if (_ENpcDataMap.ContainsKey(value))\n                return _ENpcDataMap[value];\n            return new ENpc[0];\n        }\n\n        private Dictionary<int, List<ENpc>> BuildDataMap() {\n            var dataMap = new Dictionary<int, List<ENpc>>();\n\n            foreach (var npc in this) {\n                for (var i = 0; i < ENpcBase.DataCount; ++i) {\n                    var val = npc.Base.GetRawData(i);\n                    if (val == 0) continue;\n                    if (!dataMap.TryGetValue(val, out var l))\n                        dataMap.Add(val, l = new List<ENpc>());\n                    l.Add(npc);\n                }\n            }\n\n            return dataMap;\n        }\n\n        #endregion\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Xiv/Collections/EquipSlotCollection.cs",
    "content": "﻿using System.Collections;\nusing System.Collections.Generic;\n\nnamespace SaintCoinach.Xiv.Collections {\n    public class EquipSlotCollection : IEnumerable<EquipSlot> {\n        #region Fields\n\n        private readonly EquipSlot[] _EquipSlots;\n\n        #endregion\n\n        #region Properties\n\n        public XivCollection Collection { get; private set; }\n\n        #region this[]\n\n        public EquipSlot this[int key] { get { return _EquipSlots[key]; } }\n\n        #endregion\n\n        #endregion\n\n        #region Constructors\n\n        #region Constructor\n\n        protected internal EquipSlotCollection(XivCollection collection) {\n            const int SlotCount = 14;\n\n            Collection = collection;\n            _EquipSlots = new EquipSlot[SlotCount];\n            for (var i = 0; i < SlotCount; ++i)\n                _EquipSlots[i] = new EquipSlot(this, i);\n        }\n\n        #endregion\n\n        #endregion\n\n        #region IEnumerable<EquipSlot> Members\n\n        public IEnumerator<EquipSlot> GetEnumerator() {\n            return ((IEnumerable<EquipSlot>)_EquipSlots).GetEnumerator();\n        }\n\n        IEnumerator IEnumerable.GetEnumerator() {\n            return GetEnumerator();\n        }\n\n        #endregion\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Xiv/Collections/ItemCollection.cs",
    "content": "﻿using System.Collections;\nusing System.Collections.Generic;\n\nnamespace SaintCoinach.Xiv.Collections {\n    public class ItemCollection : IEnumerable<ItemBase> {\n        #region Fields\n\n        private readonly Range[] _EventRanges;\n        private readonly Range[] _InventoryRanges;\n\n        #endregion\n\n        #region Properties\n\n        public XivCollection Collection { get; private set; }\n        public IXivSheet<Item> InventoryItemSheet { get; private set; }\n        public IXivSheet<EventItem> EventItemSheet { get; private set; }\n\n        #endregion\n\n        #region Constructors\n\n        public ItemCollection(XivCollection collection) {\n            Collection = collection;\n\n            InventoryItemSheet = collection.GetSheet<Item>();\n            EventItemSheet = collection.GetSheet<EventItem>();\n\n            _InventoryRanges = Range.Combine(InventoryItemSheet.Header.DataFileRanges);\n            _EventRanges = Range.Combine(EventItemSheet.Header.DataFileRanges);\n        }\n\n        #endregion\n\n        #region IEnumerable<Item> Members\n\n        public IEnumerator<ItemBase> GetEnumerator() {\n            return new Enumerator(this);\n        }\n\n        #endregion\n\n        #region IEnumerable Members\n\n        IEnumerator IEnumerable.GetEnumerator() {\n            return GetEnumerator();\n        }\n\n        #endregion\n\n        #region Enumerator\n\n        private class Enumerator : IEnumerator<ItemBase> {\n            #region Fields\n\n            private readonly IEnumerator<EventItem> _EventItemEnumerator;\n            private readonly IEnumerator<Item> _InventoryItemEnumerator;\n            private int _State;\n\n            #endregion\n\n            #region Constructors\n\n            #region Constructor\n\n            public Enumerator(ItemCollection collection) {\n                _InventoryItemEnumerator = collection.InventoryItemSheet.GetEnumerator();\n                _EventItemEnumerator = collection.EventItemSheet.GetEnumerator();\n            }\n\n            #endregion\n\n            #endregion\n\n            #region IEnumerator<Item> Members\n\n            public ItemBase Current { get; private set; }\n\n            #endregion\n\n            #region IDisposable Members\n\n            public void Dispose() {\n                _InventoryItemEnumerator.Dispose();\n                _EventItemEnumerator.Dispose();\n            }\n\n            #endregion\n\n            #region IEnumerator Members\n\n            object IEnumerator.Current { get { return Current; } }\n\n            public bool MoveNext() {\n                var result = false;\n\n                Current = null;\n                if (_State == 0) {\n                    result = _InventoryItemEnumerator.MoveNext();\n                    if (result)\n                        Current = _InventoryItemEnumerator.Current;\n                    else\n                        ++_State;\n                }\n                // ReSharper disable once InvertIf\n                if (_State == 1) {\n                    result = _EventItemEnumerator.MoveNext();\n                    if (result)\n                        Current = _EventItemEnumerator.Current;\n                    else\n                        ++_State;\n                }\n\n                return result;\n            }\n\n            public void Reset() {\n                _State = 0;\n                _InventoryItemEnumerator.Reset();\n                _EventItemEnumerator.Reset();\n            }\n\n            #endregion\n        }\n\n        #endregion\n\n        #region Things\n\n        public ItemBase this[int index] {\n            get {\n                if (Range.Contains(_InventoryRanges, index))\n                    return InventoryItemSheet[index];\n                if (Range.Contains(_EventRanges, index))\n                    return EventItemSheet[index];\n                throw new KeyNotFoundException();\n            }\n        }\n\n        public bool ContainsRow(int index) {\n            if (Range.Contains(_InventoryRanges, index))\n                return InventoryItemSheet.ContainsRow(index);\n            if (Range.Contains(_EventRanges, index))\n                return EventItemSheet.ContainsRow(index);\n            return false;\n        }\n\n        #endregion\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Xiv/Collections/ShopCollection.cs",
    "content": "﻿using System.Collections;\nusing System.Collections.Generic;\n\nnamespace SaintCoinach.Xiv.Collections {\n    public class ShopCollection : IEnumerable<IShop> {\n        #region Properties\n\n        public XivCollection Collection { get; private set; }\n\n        #endregion\n\n        #region Constructors\n\n        #region Constructor\n\n        public ShopCollection(XivCollection collection) {\n            Collection = collection;\n        }\n\n        #endregion\n\n        #endregion\n\n        #region IEnumerable<IShop> Members\n\n        public IEnumerator<IShop> GetEnumerator() {\n            return new Enumerator(Collection);\n        }\n\n        #endregion\n\n        #region IEnumerable Members\n\n        IEnumerator IEnumerable.GetEnumerator() {\n            return GetEnumerator();\n        }\n\n        #endregion\n\n        #region Enumerator\n\n        private class Enumerator : IEnumerator<IShop> {\n            #region Fields\n\n            // ReSharper disable once InconsistentNaming\n            private readonly IEnumerator<GCShop> _GCShopEnumerator;\n            private readonly IEnumerator<GilShop> _GilShopEnumerator;\n            private readonly IEnumerator<SpecialShop> _SpecialShopEnumerator;\n            private readonly IEnumerator<FccShop> _FccShopEnumerator;\n            private int _State;\n\n            #endregion\n\n            #region Constructors\n\n            #region Constructor\n\n            public Enumerator(XivCollection collection) {\n                _GilShopEnumerator = collection.GetSheet<GilShop>().GetEnumerator();\n                _GCShopEnumerator = collection.GetSheet<GCShop>().GetEnumerator();\n                _SpecialShopEnumerator = collection.GetSheet<SpecialShop>().GetEnumerator();\n                _FccShopEnumerator = collection.GetSheet<FccShop>().GetEnumerator();\n            }\n\n            #endregion\n\n            #endregion\n\n            #region IEnumerator<Item> Members\n\n            public IShop Current { get; private set; }\n\n            #endregion\n\n            #region IDisposable Members\n\n            public void Dispose() {\n                _GilShopEnumerator.Dispose();\n                _GCShopEnumerator.Dispose();\n                _SpecialShopEnumerator.Dispose();\n            }\n\n            #endregion\n\n            #region IEnumerator Members\n\n            object IEnumerator.Current { get { return Current; } }\n\n            public bool MoveNext() {\n                var result = false;\n\n                Current = null;\n                if (_State == 0) {\n                    result = _GilShopEnumerator.MoveNext();\n                    if (result)\n                        Current = _GilShopEnumerator.Current;\n                    else\n                        ++_State;\n                }\n                if (_State == 1) {\n                    result = _GCShopEnumerator.MoveNext();\n                    if (result)\n                        Current = _GCShopEnumerator.Current;\n                    else\n                        ++_State;\n                }\n                // ReSharper disable once InvertIf\n                if (_State == 2) {\n                    result = _SpecialShopEnumerator.MoveNext();\n                    if (result)\n                        Current = _SpecialShopEnumerator.Current;\n                    else\n                        ++_State;\n                }\n\n                if(_State == 3) {\n                    result = _FccShopEnumerator.MoveNext();\n                    if (result)\n                        Current = _FccShopEnumerator.Current;\n                    else\n                        ++_State;\n                }\n\n                return result;\n            }\n\n            public void Reset() {\n                _State = 0;\n                _GilShopEnumerator.Reset();\n                _GCShopEnumerator.Dispose();\n                _SpecialShopEnumerator.Dispose();\n            }\n\n            #endregion\n        }\n\n        #endregion\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Xiv/Companion.cs",
    "content": "using SaintCoinach.Ex.Relational;\nusing SaintCoinach.Imaging;\nusing System;\n\nnamespace SaintCoinach.Xiv {\n    public class Companion : XivRow, IQuantifiableXivString {\n        #region Fields\n        private CompanionTransient _CompanionTransient;\n        #endregion\n\n        #region Properties\n\n        public Text.XivString Singular { get { return AsString(\"Singular\"); } }\n        public Text.XivString Plural { get { return Sheet.Collection.ActiveLanguage == Ex.Language.Japanese ? Singular : AsString(\"Plural\"); } }\n        public Text.XivString Description { get { return CompanionTransient.Description; } }\n        public Text.XivString GuideDescription { get { return CompanionTransient.GuideDescription; } }\n        public Text.XivString Tooltip { get { return CompanionTransient.Tooltip; } }\n        public ImageFile Icon { get { return AsImage(\"Icon\"); } }\n        public byte Cost {  get { return As<byte>(\"Cost\"); } }\n        public UInt16 HP { get { return As<UInt16>(\"HP\"); } }\n        public byte Attack {  get { return CompanionTransient.Attack; } }\n        public byte Defense { get { return CompanionTransient.Defense; } }\n        public byte Speed {  get { return CompanionTransient.Speed; } }\n        public bool HasAreaAttack {  get { return CompanionTransient.HasAreaAttack; } }\n        public bool StrongVsGate { get { return CompanionTransient.StrongVsGate; } }\n        public bool StrongVsEye { get { return CompanionTransient.StrongVsEye; } }\n        public bool StrongVsShield { get { return CompanionTransient.StrongVsShield; } }\n        public bool StrongVsArcana { get { return CompanionTransient.StrongVsArcana; } }\n        public MinionSkillType SkillType { get { return CompanionTransient.SkillType; } }\n        public byte SpecialActionCost { get { return As<byte>(\"Skill{Cost}\"); } }\n        public UInt16 SpecialActionAngle {  get { return As<UInt16>(\"Skill{Angle}\"); } }\n        public MinionRace Race {  get { return As<MinionRace>(); } }\n        public ModelChara ModelChara => As<ModelChara>(\"Model\");\n\n        public CompanionTransient CompanionTransient { get { return _CompanionTransient ?? (_CompanionTransient = BuildTransient()); } }\n\n        #endregion\n\n        #region Constructors\n\n        #region Constructor\n\n        public Companion(IXivSheet sheet, IRelationalRow sourceRow) : base(sheet, sourceRow) { }\n\n        #endregion\n\n        #endregion\n\n        public override string ToString() {\n            return Singular;\n        }\n\n        #region IQuantifiableName Members\n        string IQuantifiable.Singular {\n            get { return Singular; }\n        }\n\n        string IQuantifiable.Plural {\n            get { return Plural; }\n        }\n        #endregion\n\n        private CompanionTransient BuildTransient()\n        {\n            return this.Sheet.Collection.GetSheet<CompanionTransient>()[Key];\n        }\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Xiv/CompanionTransient.cs",
    "content": "﻿using SaintCoinach.Ex.Relational;\nusing System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace SaintCoinach.Xiv\n{\n    public class CompanionTransient : XivRow \n    {\n        #region Constructors\n\n        #region Constructor\n\n        public CompanionTransient(IXivSheet sheet, IRelationalRow sourceRow) : base(sheet, sourceRow) { }\n\n        #endregion\n\n        #endregion\n\n        #region Properties\n\n        public Text.XivString Description { get { return AsString(\"Description\"); } }\n        public Text.XivString GuideDescription { get { return AsString(\"Description{Enhanced}\"); } }\n        public Text.XivString Tooltip { get { return AsString(\"Tooltip\"); } }\n        public byte Attack { get { return As<byte>(\"Attack\"); } }\n        public byte Defense { get { return As<byte>(\"Defense\"); } }\n        public byte Speed { get { return As<byte>(\"Speed\"); } }\n        public bool HasAreaAttack { get { return AsBoolean(\"HasAreaAttack\"); } }\n        public bool StrongVsGate {  get { return AsBoolean(\"Strength{Gate}\"); } }\n        public bool StrongVsEye { get { return AsBoolean(\"Strength{Eye}\"); } }\n        public bool StrongVsShield { get { return AsBoolean(\"Strength{Shield}\"); } }\n        public bool StrongVsArcana { get { return AsBoolean(\"Strength{Arcana}\"); } }\n        public MinionSkillType SkillType { get { return As<MinionSkillType>(); } }\n        #endregion\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Xiv/CompanyAction.cs",
    "content": "using SaintCoinach.Ex.Relational;\nusing SaintCoinach.Imaging;\n\nnamespace SaintCoinach.Xiv {\n    public class CompanyAction : XivRow {\n        #region Properties\n\n        public Text.XivString Name { get { return AsString(\"Name\"); } }\n        public Text.XivString Description { get { return AsString(\"Description\"); } }\n        public ImageFile Icon { get { return AsImage(\"Icon\"); } }\n        // ReSharper disable once InconsistentNaming\n        public FCRank FCRank { get { return As<FCRank>(); } }\n        public int Cost { get { return AsInt32(\"Cost\"); } }\n        public int Order { get { return AsInt32(\"Order\"); } }\n\n        #endregion\n\n        #region Constructors\n\n        #region Constructor\n\n        public CompanyAction(IXivSheet sheet, IRelationalRow sourceRow) : base(sheet, sourceRow) { }\n\n        #endregion\n\n        #endregion\n\n        public override string ToString() {\n            return Name;\n        }\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Xiv/CompanyCraftDraft.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace SaintCoinach.Xiv {\n    public class CompanyCraftDraft : XivRow {\n        #region Fields\n\n        private RequiredItem[] _RequiredItems;\n        private CompanyCraftSequence[] _UnlockedSequences;\n\n        #endregion\n\n        #region Properties\n\n        public Text.XivString Name { get { return AsString(\"Name\"); } }\n\n        public CompanyCraftDraftCategory CompanyCraftDraftCategory { get { return As<CompanyCraftDraftCategory>(); } }\n\n        public IEnumerable<RequiredItem> RequiredItems { get { return _RequiredItems ?? (_RequiredItems = BuildRequiredItems()); } }\n\n        public int Order { get { return AsInt32(\"Order\"); } }\n\n        public IEnumerable<CompanyCraftSequence> UnlockedSequences { get { return _UnlockedSequences ?? (_UnlockedSequences = GetUnlockedSequences()); } }\n\n        #endregion\n\n        #region Constructors\n\n        public CompanyCraftDraft(IXivSheet sheet, SaintCoinach.Ex.Relational.IRelationalRow sourceRow) : base(sheet, sourceRow) { }\n\n        #endregion\n\n        RequiredItem[] BuildRequiredItems() {\n            const int Count = 3;\n\n            var items = new List<RequiredItem>();\n            for (var i = 0; i < Count; ++i) {\n                var item = As<Item>(\"RequiredItem\", i);\n                var count = AsInt32(\"RequiredItemCount\", i);\n                if (item == null || item.Key == 0 || count == 0)\n                    continue;\n                items.Add(new RequiredItem(item, count));\n            }\n            return items.ToArray();\n        }\n\n        CompanyCraftSequence[] GetUnlockedSequences() {\n            return Sheet.Collection.GetSheet<CompanyCraftSequence>().Where(s => s.CompanyCraftDraft == this).ToArray();\n        }\n\n        public override string ToString() {\n            return Name;\n        }\n\n        public class RequiredItem {\n            public Item Item { get; private set; }\n            public int Count { get; private set; }\n\n            internal RequiredItem(Item item, int count) {\n                Item = item;\n                Count = count;\n            }\n        }\n    }\n}"
  },
  {
    "path": "SaintCoinach/Xiv/CompanyCraftDraftCategory.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace SaintCoinach.Xiv {\n    public class CompanyCraftDraftCategory : XivRow {\n        #region Fields\n\n        private CompanyCraftType[] _CompanyCraftTypes;\n\n        #endregion\n\n        #region Properties\n\n        public Text.XivString Name { get { return AsString(\"Name\"); } }\n\n        public IEnumerable<CompanyCraftType> CompanyCraftTypes { get { return _CompanyCraftTypes ?? (_CompanyCraftTypes = BuildCompanyCraftTypes()); } }\n\n        #endregion\n\n        #region Constructors\n\n        public CompanyCraftDraftCategory(IXivSheet sheet, SaintCoinach.Ex.Relational.IRelationalRow sourceRow) : base(sheet, sourceRow) { }\n\n        #endregion\n\n        private CompanyCraftType[] BuildCompanyCraftTypes() {\n            const int Count = 10;\n\n            var craftTypes = new List<CompanyCraftType>();\n            for (var i = 0; i < Count; ++i) {\n                var type = As<CompanyCraftType>(i);\n                if (type != null && type.Key != 0)\n                    craftTypes.Add(type);\n            }\n\n            return craftTypes.ToArray();\n        }\n\n        public override string ToString() {\n            return Name;\n        }\n    }\n}"
  },
  {
    "path": "SaintCoinach/Xiv/CompanyCraftDraftCategoryTxt.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace SaintCoinach.Xiv {\n    public class CompanyCraftDraftCategoryTxt : XivRow {\n        #region Properties\n\n        public Text.XivString Name { get { return AsString(\"Name\"); } }\n\n        #endregion\n\n        #region Constructors\n\n        public CompanyCraftDraftCategoryTxt(IXivSheet sheet, SaintCoinach.Ex.Relational.IRelationalRow sourceRow) : base(sheet, sourceRow) { }\n\n        #endregion\n\n        public override string ToString() {\n            return Name;\n        }\n    }\n}"
  },
  {
    "path": "SaintCoinach/Xiv/CompanyCraftManufactoryState.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace SaintCoinach.Xiv {\n    public class CompanyCraftManufactoryState : XivRow {\n        #region Properties\n\n        public Text.XivString Name { get { return AsString(\"Name\"); } }\n\n        #endregion\n\n        #region Constructors\n\n        public CompanyCraftManufactoryState(IXivSheet sheet, SaintCoinach.Ex.Relational.IRelationalRow sourceRow) : base(sheet, sourceRow) { }\n\n        #endregion\n\n        public override string ToString() {\n            return Name;\n        }\n    }\n}"
  },
  {
    "path": "SaintCoinach/Xiv/CompanyCraftPart.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace SaintCoinach.Xiv {\n    public class CompanyCraftPart : XivRow {\n        #region Fields\n        private CompanyCraftProcess[] _CraftProcesses;\n        #endregion\n\n        #region Properties\n\n        public CompanyCraftType CompanyCraftType { get { return As<CompanyCraftType>(); } }\n\n        public IEnumerable<CompanyCraftProcess> CompanyCraftProcesses { get { return _CraftProcesses ?? (_CraftProcesses = BuildCraftProcesses()); } }\n\n        #endregion\n\n        #region Constructors\n\n        public CompanyCraftPart(IXivSheet sheet, SaintCoinach.Ex.Relational.IRelationalRow sourceRow) : base(sheet, sourceRow) { }\n\n        #endregion\n\n        private CompanyCraftProcess[] BuildCraftProcesses() {\n            const int Count = 3;\n\n            var procs = new List<CompanyCraftProcess>();\n\n            for(var i = 0; i < Count; ++i) {\n                var proc = As<CompanyCraftProcess>(i);\n                if (proc == null || proc.Key == 0)\n                    continue;\n\n                procs.Add(proc);\n            }\n\n            return procs.ToArray();\n        }\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Xiv/CompanyCraftProcess.Request.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace SaintCoinach.Xiv {\n    partial class CompanyCraftProcess {\n        public class Request {\n            public CompanyCraftSupplyItem SupplyItem { get; private set; }\n            public int QuantityPerSet { get; private set; }\n            public int RequiredSets { get; private set; }\n            public int TotalQuantity { get { return QuantityPerSet * RequiredSets; } }\n\n            internal Request(CompanyCraftSupplyItem supplyItem, int quantityPerSet, int requiredSets) {\n                SupplyItem = supplyItem;\n                QuantityPerSet = quantityPerSet;\n                RequiredSets = requiredSets;\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Xiv/CompanyCraftProcess.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace SaintCoinach.Xiv {\n    public partial class CompanyCraftProcess : XivRow {\n        #region Fields\n        private Request[] _Requests;\n        #endregion\n\n        #region Properties\n\n        public IEnumerable<Request> Requests { get { return _Requests ?? (_Requests = BuildRequests()); } }\n\n        #endregion\n\n        #region Constructors\n\n        public CompanyCraftProcess(IXivSheet sheet, SaintCoinach.Ex.Relational.IRelationalRow sourceRow) : base(sheet, sourceRow) { }\n\n        #endregion\n\n        private Request[] BuildRequests() {\n            const int Count = 12;\n\n            var reqs = new List<Request>();\n\n            for(var i = 0; i < Count; ++i) {\n                var supplyItem = As<CompanyCraftSupplyItem>(\"SupplyItem\", i);\n                var perSet = AsInt32(\"SetQuantity\", i);\n                var setCount = AsInt32(\"SetsRequired\", i);\n\n                if (supplyItem == null || supplyItem.Key == 0 || perSet == 0 || setCount == 0)\n                    continue;\n\n                reqs.Add(new Request(supplyItem, perSet, setCount));\n            }\n\n            return reqs.ToArray();\n        }\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Xiv/CompanyCraftSequence.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace SaintCoinach.Xiv {\n    public class CompanyCraftSequence : XivRow, IItemSource {\n        #region Fields\n        private CompanyCraftPart[] _CompanyCraftParts;\n        #endregion\n\n        #region Properties\n\n        public Item ResultItem { get { return As<Item>(\"ResultItem\"); } }\n        public CompanyCraftDraftCategory CompanyCraftDraftCategory { get { return As<CompanyCraftDraftCategory>(); } }\n        public CompanyCraftType CompanyCraftType { get { return As<CompanyCraftType>(); } }\n        public CompanyCraftDraft CompanyCraftDraft { get { return As<CompanyCraftDraft>(); } }\n        public IEnumerable<CompanyCraftPart> CompanyCraftParts { get { return _CompanyCraftParts ?? (_CompanyCraftParts = BuildCraftParts()); } }\n\n        IEnumerable<Item> IItemSource.Items { get { yield return ResultItem; } }\n\n        #endregion\n\n        #region Constructors\n\n        public CompanyCraftSequence(IXivSheet sheet, SaintCoinach.Ex.Relational.IRelationalRow sourceRow) : base(sheet, sourceRow) { }\n\n        #endregion\n\n        private CompanyCraftPart[] BuildCraftParts() {\n            const int Count = 8;\n\n            var parts = new List<CompanyCraftPart>();\n\n            for(var i = 0; i < Count; ++i) {\n                var part = As<CompanyCraftPart>(i);\n                if (part == null || part.Key == 0)\n                    continue;\n\n                parts.Add(part);\n            }\n\n            return parts.ToArray();\n        }\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Xiv/CompanyCraftSupplyItem.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace SaintCoinach.Xiv {\n    public class CompanyCraftSupplyItem : XivRow {\n\n        #region Properties\n\n        public Item Item { get { return As<Item>(); } }\n\n        #endregion\n        \n        #region Constructors\n\n        public CompanyCraftSupplyItem(IXivSheet sheet, SaintCoinach.Ex.Relational.IRelationalRow sourceRow) : base(sheet, sourceRow) { }\n\n        #endregion\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Xiv/CompanyCraftType.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nusing SaintCoinach.Ex.Relational;\n\nnamespace SaintCoinach.Xiv {\n    public class CompanyCraftType : XivRow {\n        #region Properties\n\n        public Text.XivString Name { get { return AsString(\"Name\"); } }\n\n        #endregion\n\n        #region Constructors\n\n        public CompanyCraftType(IXivSheet sheet, IRelationalRow sourceRow) : base(sheet, sourceRow) { }\n\n        #endregion\n\n        public override string ToString() {\n            return Name;\n        }\n    }\n}"
  },
  {
    "path": "SaintCoinach/Xiv/ContentBase.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nusing SaintCoinach.Ex.Relational;\nusing SaintCoinach.Imaging;\n\nnamespace SaintCoinach.Xiv {\n    /// <summary>\n    /// Base class representing duties.\n    /// </summary>\n    public abstract class ContentBase : XivRow {\n        #region Properties\n        \n        /// <summary>\n        /// Gets the name of the current content.\n        /// </summary>\n        /// <value>The name of the current content.</value>\n        public virtual Text.XivString Name { get { return AsString(\"Name\"); } }\n\n        /// <summary>\n        /// Gets the description of the current content.\n        /// </summary>\n        /// <value>The description of the current content.</value>\n        public virtual Text.XivString Description { get { return AsString(\"Description\"); } }\n\n        /// <summary>\n        /// Gets a value indicating whether the current content is shown in the duty finder.\n        /// </summary>\n        /// <value>A value indicating whether the current content is shown in the duty finder.</value>\n        public virtual bool IsInDutyFinder { get { return AsBoolean(\"IsInDutyFinder\"); } }\n\n        /// <summary>\n        /// Gets the rewards received upon completing the current content.\n        /// </summary>\n        /// <value>The rewards received upon completing the current content.</value>\n        public abstract IEnumerable<IContentReward> FixedRewards { get; }\n\n        #endregion\n\n        #region Constructors\n\n        /// <summary>\n        /// Initializes a new instance of the <see cref=\"ContentBase\"/> class.\n        /// </summary>\n        /// <param name=\"sheet\"><see cref=\"IXivSheet\"/> containing this object.</param>\n        /// <param name=\"sourceRow\"><see cref=\"IRelationalRow\"/> to read data from.</param>\n        protected ContentBase(IXivSheet sheet, IRelationalRow sourceRow) : base(sheet, sourceRow) { }\n\n        #endregion\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Xiv/ContentFinderCondition.cs",
    "content": "﻿using SaintCoinach.Ex.Relational;\nusing System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace SaintCoinach.Xiv {\n    public class ContentFinderCondition : XivRow {\n        #region Properties\n\n        public XivRow Content => As<XivRow>(\"Content\");\n\n        public ContentMemberType ContentMemberType => As<ContentMemberType>();\n\n        public ContentType ContentType => As<ContentType>();\n\n        public Text.XivString Name => AsString(\"Name\");\n\n        // 1 = InstanceContent, 2 = PartyContent, 3 = PublicContent, 4 = GoldSaucerContent, 5 = special or test event content?\n        public byte ContentLinkType => As<byte>(\"ContentLinkType\");\n\n        /// <summary>\n        /// Gets the description of the current content.\n        /// </summary>\n        /// <value>The description of the current content.</value>\n        public Text.XivString Description => (Text.XivString)Sheet.Collection.GetSheet(\"ContentFinderConditionTransient\")[this.Key][\"Description\"];\n\n        /// <summary>\n        /// Gets the minimum level required for the current content.\n        /// </summary>\n        /// <value>The minimum level required for the current content.</value>\n        public int RequiredClassJobLevel => AsInt32(\"ClassJobLevel{Required}\");\n\n        /// <summary>\n        /// Gets the maximum level for the current content.\n        /// </summary>\n        /// <value>The maximum level for the current content.</value>\n        public int ClassJobLevelSync => AsInt32(\"ClassJobLevel{Sync}\");\n\n        /// <summary>\n        /// Gets the item level required to enter the current content.\n        /// </summary>\n        /// <value>The item level required to enter the current content.</value>\n        public virtual int RequiredItemLevel => AsInt32(\"ItemLevel{Required}\");\n\n        /// <summary>\n        /// Gets the item level the current content gets synced to if it is higher.\n        /// </summary>\n        /// <value>The item level the current content gets synced to if it is higher.</value>\n        public int ItemLevelSync => AsInt32(\"ItemLevel{Sync}\");\n\n        public virtual Imaging.ImageFile Image => AsImage(\"Image\");\n\n        #endregion\n\n        #region Constructors\n\n        /// <summary>\n        /// Initializes a new instance of the <see cref=\"InstanceContent\"/> class.\n        /// </summary>\n        /// <param name=\"sheet\"><see cref=\"IXivSheet\"/> containing this object.</param>\n        /// <param name=\"sourceRow\"><see cref=\"IRelationalRow\"/> to read data from.</param>\n        public ContentFinderCondition(IXivSheet sheet, IRelationalRow sourceRow) : base(sheet, sourceRow) { }\n\n        #endregion\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Xiv/ContentMemberType.cs",
    "content": "﻿using SaintCoinach.Ex.Relational;\nusing System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace SaintCoinach.Xiv {\n    public class ContentMemberType : XivRow {\n        #region Properties\n\n        /// <summary>\n        /// Gets the number of tanks per party.\n        /// </summary>\n        /// <value>The number of tanks per party.</value>\n        /// <remarks>\n        /// This value is only relevant when registering without a complete preformed party, or for specific content (i.e. Wolves' Den).\n        /// </remarks>\n        public int TanksPerParty { get { return AsInt32(\"TanksPerParty\"); } }\n\n        /// <summary>\n        /// Gets the number of healers per party.\n        /// </summary>\n        /// <value>The number of healers per party.</value>\n        /// <remarks>\n        /// This value is only relevant when registering without a complete preformed party, or for specific content (i.e. Wolves' Den).\n        /// </remarks>\n        public int HealersPerParty { get { return AsInt32(\"HealersPerParty\"); } }\n\n        /// <summary>\n        /// Gets the number of melee DDs per party.\n        /// </summary>\n        /// <value>The number of melee DDs per party.</value>\n        /// <remarks>\n        /// This value is only relevant when registering without a complete preformed party, or for specific content (i.e. Wolves' Den).\n        /// For most content this and the value of <see cref=\"RangedPerParty\"/> are counted as one total for DDs of any type.\n        /// </remarks>\n        public int MeleesPerParty { get { return AsInt32(\"MeleesPerParty\"); } }\n\n        /// <summary>\n        /// Gets the number of ranged DDs per party.\n        /// </summary>\n        /// <value>The number of ranged DDs per party.</value>\n        /// <remarks>\n        /// This value is only relevant when registering without a complete preformed party, or for specific content (i.e. Wolves' Den).\n        /// For most content this and the value of <see cref=\"MeleesPerParty\"/> are counted as one total for DDs of any type.\n        /// </remarks>\n        public int RangedPerParty { get { return AsInt32(\"RangedPerParty\"); } }\n\n        #endregion\n\n        #region Constructors\n\n        /// <summary>\n        /// Initializes a new instance of the <see cref=\"InstanceContent\"/> class.\n        /// </summary>\n        /// <param name=\"sheet\"><see cref=\"IXivSheet\"/> containing this object.</param>\n        /// <param name=\"sourceRow\"><see cref=\"IRelationalRow\"/> to read data from.</param>\n        public ContentMemberType(IXivSheet sheet, IRelationalRow sourceRow) : base(sheet, sourceRow) { }\n\n        #endregion\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Xiv/ContentReward.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace SaintCoinach.Xiv {\n    /// <summary>\n    /// Class representing basic content reward.\n    /// </summary>\n    public class ContentReward : IContentReward {\n        #region Properties\n\n        /// <summary>\n        /// Gets the rewarded <see cref=\"Item\"/>.\n        /// </summary>\n        /// <value>The rewarded <see cref=\"Item\"/>.</value>\n        public Item Item { get; private set; }\n\n        /// <summary>\n        ///     Gets the count for the current reward.\n        /// </summary>\n        /// <value>The count for the current reward.</value>\n        public int Count { get; private set; }\n\n        #endregion\n\n        #region Constructors\n        /// <summary>\n        /// Initializes a new instance of the <see cref=\"ContentBase\"/> class.\n        /// </summary>\n        /// <param name=\"item\">The rewarded <see cref=\"Item\"/>.</param>\n        /// <param name=\"count\">The count for the current reward.</param>\n        public ContentReward(Item item, int count) {\n            this.Item = item;\n            this.Count = count;\n        }\n        #endregion\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Xiv/ContentRoulette.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nusing SaintCoinach.Ex.Relational;\nusing SaintCoinach.Imaging;\n\nnamespace SaintCoinach.Xiv {\n    /// <summary>\n    /// Class representing duty roulettes.\n    /// </summary>\n    public class ContentRoulette : ContentBase {\n        #region Properties\n\n        /// <summary>\n        /// Gets the type of the current content.\n        /// </summary>\n        /// <value>The type of the current content.</value>\n        public Text.XivString DutyType { get { return AsString(\"DutyType\"); } }\n\n        /// <summary>\n        /// Gets a value indicating whether all duties in the current roulette have to be unlocked.\n        /// </summary>\n        /// <value>A value indicating whether all duties in the current roulette have to be unlocked.</value>\n        public bool RequireAllDuties { get { return AsBoolean(\"RequireAllDuties\"); } }\n\n        /// <summary>\n        /// Gets the numeric order of the current roulette.\n        /// </summary>\n        /// <value>The numeric order of the current roulette.</value>\n        public int SortKey { get { return AsInt32(\"SortKey\"); } }\n\n        public virtual ContentMemberType ContentMemberType { get { return As<ContentMemberType>(); } }\n\n        /// <summary>\n        /// Gets the icon for the current content.\n        /// </summary>\n        /// <value>The icon for the current content.</value>\n        public virtual Imaging.ImageFile Icon { get { return AsImage(\"Icon\"); } }\n\n        /// <summary>\n        /// Gets the rewards received upon completing the current content.\n        /// </summary>\n        /// <value>The rewards received upon completing the current content.</value>\n        public override IEnumerable<IContentReward> FixedRewards {\n            get {\n                // XXX: Magic numbers here\n                const int SoldieryItemKey = 26;\n                const int PoeticsItemKey = 28;\n\n                var sold = AsInt32(\"Reward{Soldiery}\");\n                var poe = AsInt32(\"Reward{Poetics}\");\n                if (sold == 0 && poe == 0)\n                    yield break;\n\n                var items = Sheet.Collection.GetSheet<Item>();\n                if (sold != 0)\n                    yield return new ContentReward(items[SoldieryItemKey], sold);\n                if (poe != 0)\n                    yield return new ContentReward(items[PoeticsItemKey], poe);\n            }\n        }\n\n        #endregion\n\n        #region Constructors\n\n        /// <summary>\n        /// Initializes a new instance of the <see cref=\"ContentRoulette\"/> class.\n        /// </summary>\n        /// <param name=\"sheet\"><see cref=\"IXivSheet\"/> containing this object.</param>\n        /// <param name=\"sourceRow\"><see cref=\"IRelationalRow\"/> to read data from.</param>\n        public ContentRoulette(IXivSheet sheet, IRelationalRow sourceRow) : base(sheet, sourceRow) { }\n\n        #endregion\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Xiv/ContentType.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nusing SaintCoinach.Ex.Relational;\nusing SaintCoinach.Imaging;\n\nnamespace SaintCoinach.Xiv {\n    public class ContentType : XivRow {\n        #region Properties\n\n        /// <summary>\n        /// Gets the name of the current content type.\n        /// </summary>\n        /// <value>The name of the current content type.</value>\n        public Text.XivString Name { get { return AsString(\"Name\"); } }\n\n        /// <summary>\n        /// Gets the icon for the current content type.\n        /// </summary>\n        /// <value>The icon for the current content type.</value>\n        public ImageFile Icon { get { return AsImage(\"Icon\"); } }\n\n        /// <summary>\n        /// Gets the icon for the current content show in the duty finder.\n        /// </summary>\n        /// <value>The icon for the current content show in the duty finder.</value>\n        public ImageFile DutyFinderIcon { get { return AsImage(\"Icon{DutyFinder}\"); } }\n\n        #endregion\n\n        #region Constructors\n\n        /// <summary>\n        /// Initializes a new instance of the <see cref=\"ContentType\"/> class.\n        /// </summary>\n        /// <param name=\"sheet\"><see cref=\"IXivSheet\"/> containing this object.</param>\n        /// <param name=\"sourceRow\"><see cref=\"IRelationalRow\"/> to read data from.</param>\n        public ContentType(IXivSheet sheet, IRelationalRow sourceRow) : base(sheet, sourceRow) { }\n\n        #endregion\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Xiv/CraftAction.cs",
    "content": "using SaintCoinach.Ex.Relational;\n\nnamespace SaintCoinach.Xiv {\n    public class CraftAction : ClassJobActionBase {\n        #region Constructors\n\n        #region Constructor\n\n        public CraftAction(IXivSheet sheet, IRelationalRow sourceRow) : base(sheet, sourceRow) { }\n\n        #endregion\n\n        #endregion\n\n        public override Text.XivString Description => AsString(\"Description\");\n        public int Cost => AsInt32(\"Cost\");\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Xiv/CraftLeve.cs",
    "content": "using System.Collections.Generic;\n\nusing SaintCoinach.Ex.Relational;\n\nnamespace SaintCoinach.Xiv {\n    public class CraftLeve : XivRow {\n        #region Fields\n\n        private CraftLeveItem[] _Items;\n\n        #endregion\n\n        #region Properties\n\n        public Leve Leve { get { return As<Leve>(); } }\n        public int Repeats { get { return AsInt32(\"Repeats\"); } }\n        public IEnumerable<CraftLeveItem> Items { get { return _Items ?? (_Items = BuildItems()); } }\n\n        #endregion\n\n        #region Constructors\n\n        #region Constructor\n\n        public CraftLeve(IXivSheet sheet, IRelationalRow sourceRow) : base(sheet, sourceRow) { }\n\n        #endregion\n\n        #endregion\n\n        #region Build\n\n        private CraftLeveItem[] BuildItems() {\n            const int Count = 4;\n\n            var items = new List<CraftLeveItem>();\n\n            for (var i = 0; i < Count; ++i) {\n                var count = AsInt32(\"ItemCount\", i);\n                if (count == 0)\n                    continue;\n\n                var item = As<Item>(\"Item\", i);\n                if (item.Key == 0)\n                    continue;\n\n                items.Add(new CraftLeveItem(item, count, false));\n            }\n\n            return items.ToArray();\n        }\n\n        #endregion\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Xiv/CraftLeveItem.cs",
    "content": "﻿namespace SaintCoinach.Xiv {\n    public class CraftLeveItem {\n        #region Properties\n\n        public Item Item { get; private set; }\n        public int Count { get; private set; }\n        public bool RequireHq { get; private set; }\n\n        #endregion\n\n        #region Constructors\n\n        #region Constructor\n\n        public CraftLeveItem(Item item, int count, bool requireHq) {\n            Item = item;\n            Count = count;\n            RequireHq = requireHq;\n        }\n\n        #endregion\n\n        #endregion\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Xiv/CraftLevelDifference.cs",
    "content": "﻿using SaintCoinach.Ex.Relational;\n\nnamespace SaintCoinach.Xiv {\n    public class CraftLevelDifference : XivRow {\n        public int Difference => AsInt32(\"Difference\");\n        public int ProgressFactor => AsInt32(\"ProgressFactor\");\n        public int QualityFactor => AsInt32(\"QualityFactor\");\n\n        public CraftLevelDifference(IXivSheet sheet, IRelationalRow sourceRow) : base(sheet, sourceRow) { }\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Xiv/CraftType.cs",
    "content": "using SaintCoinach.Ex.Relational;\n\nnamespace SaintCoinach.Xiv {\n    public class CraftType : XivRow {\n        #region Static\n\n        // XXX: Here be magic number\n        private const int ClassJobOffset = 8;\n\n        #endregion\n\n        #region Properties\n\n        public Text.XivString Name { get { return AsString(\"Name\"); } }\n\n        public ClassJob ClassJob { get { return Sheet.Collection.GetSheet<ClassJob>()[ClassJobOffset + Key]; } }\n\n        #endregion\n\n        #region Constructors\n\n        #region Constructor\n\n        public CraftType(IXivSheet sheet, IRelationalRow sourceRow) : base(sheet, sourceRow) { }\n\n        #endregion\n\n        #endregion\n\n        public override string ToString() {\n            return Name;\n        }\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Xiv/CustomTalk.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace SaintCoinach.Xiv {\n    public class CustomTalk : XivRow {\n        public struct ScriptCommand {\n            public string Instruction;\n            public int Argument;\n        }\n\n        #region Fields\n\n        private ENpc[] _ENpcs;\n        private ScriptCommand[] _ScriptCommands;\n\n        #endregion\n\n        #region Properties\n\n        public Text.XivString Name { get { return AsString(\"Name\"); } }\n        public Text.XivString Text { get { return AsString(\"Text\"); } }\n\n        public IEnumerable<ENpc> ENpcs { get { return _ENpcs ?? (_ENpcs = Sheet.Collection.ENpcs.FindWithData(Key).ToArray()); } }\n        public IEnumerable<ScriptCommand> ScriptCommands { get { return _ScriptCommands ?? (_ScriptCommands = BuildScriptCommands()); } }\n\n        #endregion\n\n        #region Constructors\n        \n        public CustomTalk(IXivSheet sheet, SaintCoinach.Ex.Relational.IRelationalRow sourceRow) : base(sheet, sourceRow) { }\n        \n        #endregion\n\n        private ScriptCommand[] BuildScriptCommands() {\n            const int Count = 30;\n\n            var commands = new List<ScriptCommand>();\n\n            for(var i = 0; i < Count; ++i) {\n                var instr = AsString(\"Script{Instruction}\", i).ToString();\n                if (string.IsNullOrWhiteSpace(instr))\n                    continue;\n                commands.Add(new ScriptCommand {\n                    Instruction = instr,\n                    Argument = AsInt32(\"Script{Arg}\", i)\n                });\n            }\n\n            return commands.ToArray();\n        }\n\n        public override string ToString() {\n            return Name;\n        }\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Xiv/ENpc.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\n\nusing SaintCoinach.Xiv.Collections;\n\nnamespace SaintCoinach.Xiv {\n    public class ENpc : ILocatable, IQuantifiableXivString {\n        #region Fields\n\n        private ENpcBase _Base;\n        private ENpcResident _Resident;\n        private ILocation[] _Locations;\n\n        #endregion\n\n        #region Properties\n\n        public int Key { get; private set; }\n        public ENpcCollection Collection { get; private set; }\n        public ENpcResident Resident { get { return _Resident ?? (_Resident = Collection.ResidentSheet[Key]); } }\n        public ENpcBase Base { get { return _Base ?? (_Base = Collection.BaseSheet[Key]); } }\n        public Text.XivString Singular { get { return Resident.Singular; } }\n        public Text.XivString Plural { get { return Collection.Collection.ActiveLanguage == Ex.Language.Japanese ? Singular : Resident.Plural; } }\n        public Text.XivString Title { get { return Resident.Title; } }\n\n        public IEnumerable<ILocation> Locations { get { return _Locations ?? (_Locations = BuildLocations()); } }\n\n        #endregion\n\n        #region Constructors\n\n        public ENpc(ENpcCollection collection, int key) {\n            Key = key;\n            Collection = collection;\n        }\n\n        #endregion\n\n        #region Build\n\n        private Level[] BuildLevels() {\n            return Collection.Collection.GetSheet<Level>().Where(_ => _.Object?.Key == Key).ToArray();\n        }\n\n        private ILocation[] BuildLocations() {\n            var levelLocations = BuildLevels();\n\n            var coll = Collection.Collection;\n            if (!coll.IsLibraAvailable)\n                return levelLocations.Cast<ILocation>().ToArray();\n\n            var libraENpc = coll.Libra.ENpcResidents.FirstOrDefault(i => i.Key == this.Key);\n            if (libraENpc == null)\n                return levelLocations.ToArray();\n\n            var locations = new List<ILocation>();\n            locations.AddRange(levelLocations.Cast<ILocation>());\n\n            var placeNames = coll.GetSheet<PlaceName>();\n            var maps = coll.GetSheet<Map>();\n\n            if (libraENpc.Coordinates != null) {\n                foreach (var coord in libraENpc.Coordinates) {\n                    var placeName = placeNames.First(i => i.Key == coord.Item1);\n\n                    foreach (var c in coord.Item2) {\n                        // Only add if no Level exists in the same area.\n                        if (!levelLocations.Any(l => Math.Abs(l.MapX - c.X) < 1 && Math.Abs(l.MapY - c.Y) < 1 && (l.Map.LocationPlaceName == placeName || l.Map.PlaceName == placeName || l.Map.RegionPlaceName == placeName)))\n                            locations.Add(new GenericLocation(placeName, c.X, c.Y));\n                    }\n                }\n            }\n\n            return locations.ToArray();\n        }\n        #endregion\n\n        public override string ToString() {\n            return Resident.Singular;\n        }\n\n        #region IQuantifiableName Members\n        string IQuantifiable.Singular {\n            get { return Singular; }\n        }\n\n        string IQuantifiable.Plural {\n            get { return Plural; }\n        }\n        #endregion\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Xiv/ENpcBase.cs",
    "content": "using System;\nusing System.Collections.Generic;\nusing SaintCoinach.Ex.Relational;\n\nnamespace SaintCoinach.Xiv {\n    public class ENpcBase : XivRow {\n        #region Static\n\n        public const int DataCount = 32;\n\n        #endregion\n\n        #region Fields\n\n        private IRelationalRow[] _AssignedData;\n\n        #endregion\n\n        #region Properties\n        public ModelChara ModelChara { get { return As<ModelChara>(\"ModelChara\"); } }\n        public Race Race { get { return As<Race>(\"Race\"); } }\n        public int Gender { get { return AsInt32(\"Gender\"); } }\n        public int BodyType { get { return AsInt32(\"BodyType\"); } }\n        public int Height { get { return AsInt32(\"Height\"); } }\n        public Tribe Tribe { get { return As<Tribe>(\"Tribe\"); } }\n        public int Face { get { return AsInt32(\"Face\"); } }\n        public int HairStyle { get { return AsInt32(\"HairStyle\"); } }\n        public int HairHighlight { get { return AsInt32(\"HairHighlight\"); } }\n        public int SkinColor { get { return AsInt32(\"SkinColor\"); } }\n        public int EyeHeterochromia { get { return AsInt32(\"EyeHeterochromia\"); } }\n        public int HairColor { get { return AsInt32(\"HairColor\"); } }\n        public int HairHighlightColor { get { return AsInt32(\"HairHighlightColor\"); } }\n        public int FacialFeature { get { return AsInt32(\"FacialFeature\"); } }\n        public int FacialFeatureColor { get { return AsInt32(\"FacialFeatureColor\"); } }\n        public int Eyebrows { get { return AsInt32(\"Eyebrows\"); } }\n        public int EyeColor { get { return AsInt32(\"EyeColor\"); } }\n        public int EyeShape { get { return AsInt32(\"EyeShape\"); } }\n        public int Nose { get { return AsInt32(\"Nose\"); } }\n        public int Jaw { get { return AsInt32(\"Jaw\"); } }\n        public int Mouth { get { return AsInt32(\"Mouth\"); } }\n        public int LipColor { get { return AsInt32(\"LipColor\"); } }\n        public int BustOrTone1 { get { return AsInt32(\"BustOrTone1\"); } }\n\n        public int ExtraFeature1 { get { return AsInt32(\"ExtraFeature1\"); } }\n        public int ExtraFeature2OrBust { get { return AsInt32(\"ExtraFeature2OrBust\"); } }\n        public int FacePaint { get { return AsInt32(\"FacePaint\"); } }\n        public int FacePaintColor { get { return AsInt32(\"FacePaintColor\"); } }\n\n        public Quad ModelMain { get { return AsQuad(\"Model{MainHand}\"); } }\n        public Stain DyeMain { get { return As<Stain>(\"Dye{MainHand}\"); } }\n        public Quad ModelSub { get { return AsQuad(\"Model{OffHand}\"); } }\n        public Stain DyeOff { get { return As<Stain>(\"Dye{OffHand}\"); } }\n        public int[] ModelHead { get { return AsIntArray(\"Model{Head}\"); } }\n        public int[] ModelBody { get { return AsIntArray(\"Model{Body}\"); } }\n        public int[] ModelHands { get { return AsIntArray(\"Model{Hands}\"); } }\n        public int[] ModelLegs { get { return AsIntArray(\"Model{Legs}\"); } }\n        public int[] ModelFeet { get { return AsIntArray(\"Model{Feet}\"); } }\n        public int[] ModelEars { get { return AsIntArray(\"Model{Ears}\"); } }\n        public int[] ModelNeck { get { return AsIntArray(\"Model{Neck}\"); } }\n        public int[] ModelWrists { get { return AsIntArray(\"Model{Wrists}\"); } }\n        public int[] ModelLeftRing { get { return AsIntArray(\"Model{LeftRing}\"); } }\n        public int[] ModelRightRing { get { return AsIntArray(\"Model{RightRing}\"); } }\n        public Stain DyeHead { get { return As<Stain>(\"Dye{Head}\"); } }\n        public Stain DyeBody { get { return As<Stain>(\"Dye{Body}\"); } }\n        public Stain DyeHands { get { return As<Stain>(\"Dye{Hands}\"); } }\n        public Stain DyeLegs { get { return As<Stain>(\"Dye{Legs}\"); } }\n        public Stain DyeFeet { get { return As<Stain>(\"Dye{Feet}\"); } }\n        public Stain DyeEars { get { return As<Stain>(\"Dye{Ears}\"); } }\n        public Stain DyeNeck { get { return As<Stain>(\"Dye{Neck}\"); } }\n        public Stain DyeWrists { get { return As<Stain>(\"Dye{Wrists}\"); } }\n        public Stain DyeLeftRing { get { return As<Stain>(\"Dye{LeftRing}\"); } }\n        public Stain DyeRightRing { get { return As<Stain>(\"Dye{RightRing}\"); } }\n        public NpcEquip NpcEquip { get { return As<NpcEquip>(); } }\n\n        public IEnumerable<IRelationalRow> AssignedData { get { return _AssignedData ?? (_AssignedData = BuildAssignedData()); } }\n\n        #endregion\n\n        #region Constructors\n\n        public ENpcBase(IXivSheet sheet, IRelationalRow sourceRow) : base(sheet, sourceRow) { }\n        \n        #endregion\n\n        public IRelationalRow GetData(int index) {\n            return As<IRelationalRow>(\"ENpcData\", index);\n        }\n        public int GetRawData(int index) {\n            var fulCol = BuildColumnName(\"ENpcData\", index);\n            var raw = ((IRelationalRow)this).GetRaw(fulCol);\n            return Convert.ToInt32(raw);\n        }\n\n        private IRelationalRow[] BuildAssignedData() {\n            var data = new List<IRelationalRow>();\n\n            for (var i = 0; i < ENpcBase.DataCount; ++i) {\n                var val = GetData(i);\n                if (val != null)\n                    data.Add(val);\n            }\n\n            return data.ToArray();\n        }\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Xiv/ENpcResident.cs",
    "content": "using SaintCoinach.Ex.Relational;\n\nnamespace SaintCoinach.Xiv {\n    public class ENpcResident : XivRow, IQuantifiableXivString {\n        #region Properties\n\n        public Text.XivString Singular { get { return AsString(\"Singular\"); } }\n        public Text.XivString Plural { get { return Sheet.Collection.ActiveLanguage == Ex.Language.Japanese ? Singular : AsString(\"Plural\"); } }\n        public Text.XivString Title { get { return AsString(\"Title\"); } }\n\n        #endregion\n\n        #region Constructors\n\n        #region Constructor\n\n        public ENpcResident(IXivSheet sheet, IRelationalRow sourceRow) : base(sheet, sourceRow) { }\n\n        #endregion\n\n        #endregion\n\n        public override string ToString() {\n            return Singular;\n        }\n\n        #region IQuantifiableName Members\n        string IQuantifiable.Singular {\n            get { return Singular; }\n        }\n\n        string IQuantifiable.Plural {\n            get { return Plural; }\n        }\n        #endregion\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Xiv/EObj.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace SaintCoinach.Xiv {\n    public class EObj : XivRow, ILocatable {\n        #region Fields\n\n        private ILocation[] _Locations;\n\n        #endregion\n\n        #region Properties\n\n        // fixme: moved to EObjName, keys shared.\n        //public Text.XivString Singular { get { return AsString(\"Singular\"); } }\n        //public Text.XivString Plural { get { return Sheet.Collection.ActiveLanguage == Ex.Language.Japanese ? Singular : AsString(\"Plural\"); } }\n\n        public int Data { get { return AsInt32(\"Data\"); } }\n\n        public IEnumerable<ILocation> Locations { get { return _Locations ?? (_Locations = BuildLocations()); } }\n\n        #endregion\n\n        #region Constructors\n\n        public EObj(IXivSheet sheet, SaintCoinach.Ex.Relational.IRelationalRow sourceRow) : base(sheet, sourceRow) { }\n\n        #endregion\n\n        private Level[] BuildLocations() {\n            return Sheet.Collection.GetSheet<Level>().Where(_ => _.Object.Key == Key).ToArray();\n        }\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Xiv/Emote.cs",
    "content": "using SaintCoinach.Ex.Relational;\nusing SaintCoinach.Imaging;\n\nnamespace SaintCoinach.Xiv {\n    public class Emote : XivRow {\n        #region Properties\n\n        public Text.XivString Name { get { return AsString(\"Name\"); } }\n        public EmoteCategory EmoteCategory { get { return As<EmoteCategory>(); } }\n        public ImageFile Icon { get { return AsImage(\"Icon\"); } }\n        public LogMessage TargetedLogMessage { get { return As<LogMessage>(\"LogMessage{Targeted}\"); } }\n        public LogMessage UntargetedLogMessage { get { return As<LogMessage>(\"LogMessage{Untargeted}\"); } }\n\n        #endregion\n\n        #region Constructors\n\n        #region Constructor\n\n        public Emote(IXivSheet sheet, IRelationalRow sourceRow) : base(sheet, sourceRow) { }\n\n        #endregion\n\n        #endregion\n\n        public override string ToString() {\n            return Name;\n        }\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Xiv/EmoteCategory.cs",
    "content": "using SaintCoinach.Ex.Relational;\n\nnamespace SaintCoinach.Xiv {\n    public class EmoteCategory : XivRow {\n        #region Properties\n\n        public Text.XivString Name { get { return AsString(\"Name\"); } }\n\n        #endregion\n\n        #region Constructors\n\n        #region Constructor\n\n        public EmoteCategory(IXivSheet sheet, IRelationalRow sourceRow) : base(sheet, sourceRow) { }\n\n        #endregion\n\n        #endregion\n\n        public override string ToString() {\n            return Name;\n        }\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Xiv/EquipSlot.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\n\nusing SaintCoinach.Graphics;\nusing SaintCoinach.IO;\nusing SaintCoinach.Xiv.Collections;\n\nnamespace SaintCoinach.Xiv {\n    /// <summary>\n    ///     Class representing an equipment slot.\n    /// </summary>\n    /// <remarks>\n    ///     Equipment slots are not present in the game data, this class exists to make things nicer in code.\n    /// </remarks>\n    public class EquipSlot {\n        #region Static\n\n        /// <summary>\n        ///     Row key offset in the <c>Addon</c> sheet to get an equipment slot's name from.\n        /// </summary>\n        /// <remarks>\n        ///     This might change in updates, you never know with SE.\n        /// </remarks>\n        private const int AddonKeyOffset = 738;\n\n        #region Model helper class\n        private class ModelHelper {\n            #region Fields\n            public string ImcFileFormat { get; private set; }\n            public byte ImcPartKey { get; private set; }\n            public string ModelFileFormat { get; private set; }\n            public byte VariantIndexWord { get; private set; }\n            #endregion\n\n            public ModelHelper(string imcFileFormat, byte imcPartKey, string modelFileFormat, byte variantIndexWord) {\n                this.ImcFileFormat = imcFileFormat;\n                this.ImcPartKey = imcPartKey;\n                this.ModelFileFormat = modelFileFormat;\n                this.VariantIndexWord = variantIndexWord;\n            }\n        }\n        #endregion\n\n        private static readonly Dictionary<int, ModelHelper> ModelHelpers = new Dictionary<int, ModelHelper> {\n            {  // Main hand\n                0,\n                new ModelHelper(\n                   \"chara/weapon/w{0:D4}/obj/body/b{1:D4}/b{1:D4}.imc\",\n                   0,\n                   \"chara/weapon/w{0:D4}/obj/body/b{1:D4}/model/w{0:D4}b{1:D4}.mdl\",\n                   2)\n            },\n            {  // Off hand\n                1,\n                new ModelHelper(\n                   \"chara/weapon/w{0:D4}/obj/body/b{1:D4}/b{1:D4}.imc\",\n                   0,\n                   \"chara/weapon/w{0:D4}/obj/body/b{1:D4}/model/w{0:D4}b{1:D4}.mdl\",\n                   2)\n            },\n            {  // Head\n                2,\n                new ModelHelper(\n                   \"chara/equipment/e{0:D4}/e{0:D4}.imc\",\n                   0,\n                   \"chara/equipment/e{0:D4}/model/c{4:D4}e{0:D4}_met.mdl\",\n                   1)\n            },\n            {  // Body\n                3,\n                new ModelHelper(\n                   \"chara/equipment/e{0:D4}/e{0:D4}.imc\",\n                   1,\n                   \"chara/equipment/e{0:D4}/model/c{4:D4}e{0:D4}_top.mdl\",\n                   1)\n            },\n            {  // Hands\n                4,\n                new ModelHelper(\n                   \"chara/equipment/e{0:D4}/e{0:D4}.imc\",\n                   2,\n                   \"chara/equipment/e{0:D4}/model/c{4:D4}e{0:D4}_glv.mdl\",\n                   1)\n            },\n            {  // Waist\n                5,\n                null\n            },\n            {  // Legs\n                6,\n                new ModelHelper(\n                   \"chara/equipment/e{0:D4}/e{0:D4}.imc\",\n                   3,\n                   \"chara/equipment/e{0:D4}/model/c{4:D4}e{0:D4}_dwn.mdl\",\n                   1)\n            },\n            {  // Feet\n                7,\n                new ModelHelper(\n                   \"chara/equipment/e{0:D4}/e{0:D4}.imc\",\n                   4,\n                   \"chara/equipment/e{0:D4}/model/c{4:D4}e{0:D4}_sho.mdl\",\n                   1)\n            },\n            {  // Ears\n                8,\n                new ModelHelper(\n                   \"chara/accessory/a{0:D4}/a{0:D4}.imc\",\n                   0,\n                   \"chara/accessory/a{0:D4}/model/c{4:D4}a{0:D4}_ear.mdl\",\n                   1)\n            },\n            {  // Neck\n                9,\n                new ModelHelper(\n                   \"chara/accessory/a{0:D4}/a{0:D4}.imc\",\n                   1,\n                   \"chara/accessory/a{0:D4}/model/c{4:D4}a{0:D4}_nek.mdl\",\n                   1)\n            },\n            {  // Wrists\n                10,\n                new ModelHelper(\n                   \"chara/accessory/a{0:D4}/a{0:D4}.imc\",\n                   2,\n                   \"chara/accessory/a{0:D4}/model/c{4:D4}a{0:D4}_wrs.mdl\",\n                   1)\n            },\n            {  // R.Ring\n                11,\n                new ModelHelper(\n                   \"chara/accessory/a{0:D4}/a{0:D4}.imc\",\n                   3,\n                   \"chara/accessory/a{0:D4}/model/c{4:D4}a{0:D4}_rir.mdl\",\n                   1)\n            },\n            {  // L.Ring\n                12,\n                new ModelHelper(\n                   \"chara/accessory/a{0:D4}/a{0:D4}.imc\",\n                   4,\n                   \"chara/accessory/a{0:D4}/model/c{4:D4}a{0:D4}_ril.mdl\",\n                   1)\n            },\n            {  // Soul Crystal\n                13,\n                null\n            }\n        };\n\n        #endregion\n\n        #region Properties\n\n        /// <summary>\n        ///     Gets the key of the <see cref=\"EquipSlot\" />.\n        /// </summary>\n        /// <remarks>\n        ///     The keys are based on the columns in <see cref=\"EquipSlotCategory\" />.\n        /// </remarks>\n        /// <value>The key of the <see cref=\"EquipSlot\" />.</value>\n        public int Key { get; private set; }\n\n        /// <summary>\n        ///     Gets the <see cref=\"EquipSlotCollection\" />.\n        /// </summary>\n        /// <value>The <see cref=\"EquipSlotCollection\" />.</value>\n        public EquipSlotCollection Collection { get; private set; }\n\n        /// <summary>\n        ///     Gets the name of the <see cref=\"EquipSlot\" />.\n        /// </summary>\n        /// <value>The name of the <see cref=\"EquipSlot\" />.</value>\n        public string Name { get { return Collection.Collection.GetSheet<Addon>()[AddonKeyOffset + Key].Text; } }\n\n        #endregion\n\n        #region Constructors\n\n        /// <summary>\n        ///     Initializes a new instance of the <see cref=\"EquipSlot\" /> class.\n        /// </summary>\n        /// <param name=\"collection\"><see cref=\"EquipSlotCollection\" /> for this <see cref=\"EquipSlot\" />.</param>\n        /// <param name=\"key\">Key of the <see cref=\"EquipSlot\" />.</param>\n        protected internal EquipSlot(EquipSlotCollection collection, int key) {\n            Key = key;\n            Collection = collection;\n        }\n\n        #endregion\n\n        /// <summary>\n        ///     Returns a string that represents the current <see cref=\"EquipSlot\" />.\n        /// </summary>\n        /// <returns>Returns the value of <see cref=\"Name\" />.</returns>\n        public override string ToString() {\n            return Name;\n        }\n\n        #region Model\n\n        /// <summary>\n        ///     Character type fallbacks in case the requested one does not exist.\n        /// </summary>\n        private static readonly Dictionary<int, int> CharacterTypeFallback = new Dictionary<int, int> {\n            {\n                0201, 0101\n            }, {\n                0301, 0101\n            }, {\n                0401, 0201\n            }, {\n                0501, 0101\n            }, {\n                0601, 0201\n            }, {\n                0701, 0101\n            }, {\n                0801, 0201\n            }, {\n                0901, 0101\n            }, {\n                1001, 0201\n            }, {\n                1101, 0101\n            }, {\n                1201, 0201\n            }, {\n                1301, 0101\n            }, {\n                1401, 0201\n            }\n        };\n\n        /// <summary>\n        ///     Get the model for a specific QWord, character type, and the current <see cref=\"EquipSlot\" />.\n        /// </summary>\n        /// <param name=\"key\">The identifier of the model.</param>\n        /// <param name=\"characterType\">Character type to get the model for.</param>\n        /// <param name=\"materialVersion\">When this method returns, contains the variant contained within <c>key</c>.</param>\n        /// <returns>Returns the <see cref=\"Model\" /> for the specified <c>key</c> and <c>characterType</c>.</returns>\n        public ModelDefinition GetModel(Quad key, int characterType, out Graphics.ImcVariant variant) {\n            variant = Graphics.ImcVariant.Default;\n\n            if (!ModelHelpers.TryGetValue(Key, out var helper))\n                return null;\n            if (helper == null)\n                return null;\n\n            var packs = Collection.Collection.PackCollection;\n\n            var variantIndex = (int)((key.ToInt64() >> (helper.VariantIndexWord * 16)) & 0xFFFF);\n\n            var imcPath = string.Format(helper.ImcFileFormat, key.Value1, key.Value2, key.Value3, key.Value4, characterType);\n            if (!packs.TryGetFile(imcPath, out var imcBase))\n                return null;\n\n            var imc = new Graphics.ImcFile(imcBase);\n            variant = imc.GetVariant(helper.ImcPartKey, variantIndex);\n\n            IO.File modelBase = null;\n            while (!packs.TryGetFile(string.Format(helper.ModelFileFormat, key.Value1, key.Value2, key.Value3, key.Value4, characterType), out modelBase) && CharacterTypeFallback.TryGetValue(characterType, out characterType)) { }\n\n            var asModel = modelBase as Graphics.ModelFile;\n            if (asModel == null)\n                return null;\n            return asModel.GetModelDefinition();\n        }\n\n\n        public string GetModelKey(Quad key, int characterType) {\n            if (!ModelHelpers.TryGetValue(Key, out var helper))\n                return null;\n            if (helper == null)\n                return null;\n\n            return string.Format(helper.ModelFileFormat, key.Value1, key.Value2, key.Value3, key.Value4, characterType);\n        }\n        #endregion\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Xiv/EquipSlotCategory.cs",
    "content": "using System;\nusing System.Collections.Generic;\n\nusing SaintCoinach.Ex.Relational;\n\nnamespace SaintCoinach.Xiv {\n    /// <summary>\n    ///     Class representing equipment slot categories from the game data.\n    /// </summary>\n    public class EquipSlotCategory : XivRow {\n        #region Fields\n\n        /// <summary>\n        ///     <see cref=\"EquipSlot\" />s that get blocked when using the current <see cref=\"EquipSlotCategory\" />.\n        /// </summary>\n        private EquipSlot[] _BlockedSlots;\n\n        /// <summary>\n        ///     <see cref=\"EquipSlot\" />s on which the current <see cref=\"EquipSlotCategory\" /> can be used.\n        /// </summary>\n        private EquipSlot[] _PossibleSlots;\n\n        #endregion\n\n        #region Properties\n\n        /// <summary>\n        ///     Gets <see cref=\"EquipSlot\" />s that get blocked when using the current <see cref=\"EquipSlotCategory\" />.\n        /// </summary>\n        /// <value>The <see cref=\"EquipSlot\" />s that get blocked when using the current <see cref=\"EquipSlotCategory\" />.</value>\n        public IEnumerable<EquipSlot> BlockedSlots { get { return _BlockedSlots; } }\n\n        /// <summary>\n        ///     Gets <see cref=\"EquipSlot\" />s on which the current <see cref=\"EquipSlotCategory\" /> can be used.\n        /// </summary>\n        /// <value>The <see cref=\"EquipSlot\" />s on which the current <see cref=\"EquipSlotCategory\" /> can be used.</value>\n        public IEnumerable<EquipSlot> PossibleSlots { get { return _PossibleSlots; } }\n\n        #endregion\n\n        #region Constructors\n\n        /// <summary>\n        ///     Initializes a new instance of the <see cref=\"EquipSlotCategory\" /> class.\n        /// </summary>\n        /// <param name=\"sheet\"><see cref=\"IXivSheet\" /> containing this object.</param>\n        /// <param name=\"sourceRow\"><see cref=\"IRelationalRow\" /> to read data from.</param>\n        public EquipSlotCategory(IXivSheet sheet, IRelationalRow sourceRow) : base(sheet, sourceRow) {\n            Build();\n        }\n\n        #endregion\n\n        #region Build\n\n        /// <summary>\n        ///     Build the list of blocked and possible <see cref=\"EquipSlot\" />s for the current <see cref=\"EquipSlotCategory\" />.\n        /// </summary>\n        /// <remarks>\n        ///     Columns of the EquipSlotCategory are used as keys for <see cref=\"EquipSlot\" />s.\n        ///     A column value of -1 means that the <see cref=\"EquipSlot\" /> gets blocked and a value of 1 is used for possible\n        ///     <see cref=\"EquipSlot\" />s.\n        /// </remarks>\n        private void Build() {\n            var possible = new List<EquipSlot>();\n            var blocked = new List<EquipSlot>();\n\n            foreach (var slot in Sheet.Collection.EquipSlots) {\n                var val = Convert.ToInt32(this[slot.Key]);\n\n                if (val > 0)\n                    possible.Add(slot);\n                else if (val < 0)\n                    blocked.Add(slot);\n            }\n\n            _PossibleSlots = possible.ToArray();\n            _BlockedSlots = blocked.ToArray();\n        }\n\n        #endregion\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Xiv/EventAction.cs",
    "content": "using SaintCoinach.Ex.Relational;\nusing SaintCoinach.Imaging;\n\nnamespace SaintCoinach.Xiv {\n    public class EventAction : XivRow {\n        #region Properties\n\n        public Text.XivString Name { get { return AsString(\"Name\"); } }\n        public ImageFile Icon { get { return AsImage(\"Icon\"); } }\n\n        #endregion\n\n        #region Constructors\n\n        #region Constructor\n\n        public EventAction(IXivSheet sheet, IRelationalRow sourceRow) : base(sheet, sourceRow) { }\n\n        #endregion\n\n        #endregion\n\n        public override string ToString() {\n            return Name;\n        }\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Xiv/EventItem.cs",
    "content": "using SaintCoinach.Ex.Relational;\n\nnamespace SaintCoinach.Xiv {\n    /// <summary>\n    ///     Class representing key items.\n    /// </summary>\n    public class EventItem : ItemBase {\n        #region Properties\n\n        /// <summary>\n        ///     Gets the quest the current item is used for.\n        /// </summary>\n        /// <value>The quest the current item is used for.</value>\n        public Quest Quest { get { return As<Quest>(); } }\n\n        #endregion\n\n        #region Constructors\n\n        /// <summary>\n        ///     Initializes a new instance of the <see cref=\"EventItem\" /> class.\n        /// </summary>\n        /// <param name=\"sheet\"><see cref=\"IXivSheet\" /> containing this object.</param>\n        /// <param name=\"sourceRow\"><see cref=\"IRelationalRow\" /> to read data from.</param>\n        public EventItem(IXivSheet sheet, IRelationalRow sourceRow) : base(sheet, sourceRow) { }\n\n        #endregion\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Xiv/FCRank.cs",
    "content": "using SaintCoinach.Ex.Relational;\n\nnamespace SaintCoinach.Xiv {\n    // ReSharper disable once InconsistentNaming\n    public class FCRank : XivRow {\n        #region Constructors\n\n        #region Constructor\n\n        public FCRank(IXivSheet sheet, IRelationalRow sourceRow) : base(sheet, sourceRow) { }\n\n        #endregion\n\n        #endregion\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Xiv/Fate.cs",
    "content": "using SaintCoinach.Ex.Relational;\nusing SaintCoinach.Imaging;\n\nnamespace SaintCoinach.Xiv {\n    public class Fate : XivRow {\n        #region Properties\n\n        public Text.XivString Name { get { return AsString(\"Name\"); } }\n        public Text.XivString Description { get { return AsString(\"Description\"); } }\n        public Text.XivString Objective { get { return AsString(\"Objective\"); } }\n        public Text.XivString StatusText0 { get { return AsString(\"StatusText\", 0); } }\n        public Text.XivString StatusText1 { get { return AsString(\"StatusText\", 1); } }\n        public Text.XivString StatusText2 { get { return AsString(\"StatusText\", 2); } }\n        public int ClassJobLevel { get { return AsInt32(\"ClassJobLevel\"); } }\n        public int MaximumClassJobLevel { get { return AsInt32(\"ClassJobLevel{Max}\"); } }\n        public EventItem EventItem { get { return As<EventItem>(); } }\n        public ImageFile ObjectiveIcon { get { return AsImage(\"Icon{Objective}\"); } }\n        public ImageFile MapIcon { get { return AsImage(\"Icon{Map}\"); } }\n\n        #endregion\n\n        #region Constructors\n\n        #region Constructor\n\n        public Fate(IXivSheet sheet, IRelationalRow sourceRow) : base(sheet, sourceRow) { }\n\n        #endregion\n\n        #endregion\n\n        public override string ToString() {\n            return Name;\n        }\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Xiv/FccShop.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nusing SaintCoinach.Ex.Relational;\n\nnamespace SaintCoinach.Xiv {\n    public class FccShop : XivRow, IShop {\n        #region Static\n        \n        private ENpc[] _ENpcs;\n        private IShopListing[] _ShopListings;\n        private Item[] _Items;\n\n        #endregion\n\n        #region Properties\n\n        public Text.XivString Name { get { return AsString(\"Name\"); } }\n\n        string IShop.Name { get { return Name.ToString(); } }\n\n        public IEnumerable<ENpc> ENpcs {\n            get {\n                return _ENpcs ?? (_ENpcs = BuildENpcs());\n            }\n        }\n\n        public IEnumerable<IShopListing> ShopListings {\n            get {\n                return _ShopListings ?? (_ShopListings = BuildShopListings());\n            }\n        }\n        IEnumerable<Item> IItemSource.Items {\n            get {\n                return _Items ?? (_Items = BuildItems());\n            }\n        }\n\n        #endregion\n\n        #region Constructors\n\n        public FccShop(IXivSheet sheet, IRelationalRow sourceRow) : base(sheet, sourceRow) { }\n\n        #endregion\n\n        #region Build\n        private ENpc[] BuildENpcs() {\n            return Sheet.Collection.ENpcs.FindWithData(Key).ToArray();\n        }\n\n        const int ItemCount = 10;\n        private IShopListing[] BuildShopListings() {\n            const int CostItem = 6559;  // TODO: This is the company chest. Because there's no item for FC credit. :(\n\n            var costItem = Sheet.Collection.GetSheet<Item>()[CostItem];\n            var listings = new List<IShopListing>();\n            for(var i = 0; i < ItemCount; ++i) {\n                var item = As<Item>(\"Item\", i);\n                if (item == null || item.Key == 0)\n                    continue;\n\n                var cost = AsInt32(\"Cost\", i);\n                var requiredRank = As<FCRank>(\"FCRank{Required}\", i);\n\n                listings.Add(new Listing(this, item, costItem, cost, requiredRank));\n            }\n            return listings.ToArray();\n        }\n\n        private Item[] BuildItems() {\n            var items = new List<Item>();\n            for(var i = 0; i < ItemCount; ++i) {\n                var item = As<Item>(\"Item\", i);\n                if (item != null && item.Key != 0)\n                    items.Add(item);\n            }\n            return items.ToArray();\n        }\n\n        #endregion\n\n        public class Listing : IShopListing {\n            #region Fields\n            IShopListingItem _Cost;\n            IShopListingItem _Reward;\n            IShop _Shop;\n            #endregion\n\n            public Listing(FccShop shop, Item rewardItem, Item costItem, int costCount, FCRank requiredFcRank) {\n                _Shop = shop;\n                _Cost = new ShopListingItem(this, costItem, costCount, false, 0);\n                _Reward = new ShopListingItem(this, rewardItem, 1, false, 0);\n            }\n\n            public IEnumerable<IShopListingItem> Costs {\n                get {\n                    yield return _Cost;\n                }\n            }\n\n            public IEnumerable<IShopListingItem> Rewards {\n                get {\n                    yield return _Reward;\n                }\n            }\n\n            public IEnumerable<IShop> Shops {\n                get {\n                    yield return _Shop;\n                }\n            }\n        }\n\n        public override string ToString() {\n            return Name;\n        }\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Xiv/FishParameter.cs",
    "content": "using SaintCoinach.Ex.Relational;\n\nnamespace SaintCoinach.Xiv {\n    public class FishParameter : XivRow {\n        #region Properties\n\n        public Text.XivString Text => AsString(\"Text\");\n        public Item Item => As<Item>(\"Item\");\n        public bool IsInLog => AsBoolean(\"IsInLog\");\n        public bool TimeRestricted => AsBoolean(\"TimeRestricted\");\n        public bool WeatherRestricted => AsBoolean(\"WeatherRestricted\");\n\n        #endregion\n\n        #region Constructors\n\n        #region Constructor\n\n        public FishParameter(IXivSheet sheet, IRelationalRow sourceRow) : base(sheet, sourceRow) { }\n\n        #endregion\n\n        #endregion\n\n        public override string ToString() {\n            return string.Format(\"{0}\", Item);\n        }\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Xiv/FishingSpot.cs",
    "content": "using System;\nusing System.Collections.Generic;\n\nusing SaintCoinach.Ex.Relational;\n\nnamespace SaintCoinach.Xiv {\n    /// <summary>\n    ///     Class representing a fishing spot from the game data.\n    /// </summary>\n    public class FishingSpot : XivRow, IItemSource, ILocatable, ILocation {\n        #region Fields\n\n        /// <summary>\n        ///     Items that can be fished up at the current <see cref=\"FishingSpot\" />.\n        /// </summary>\n        private Item[] _Items;\n\n        #endregion\n\n        #region Properties\n\n        /// <summary>\n        ///     Gets the level of the current <see cref=\"FishingSpot\" />.\n        /// </summary>\n        /// <value>The level of the current <see cref=\"FishingSpot\" />.</value>\n        public int GatheringLevel { get { return AsInt32(\"GatheringLevel\"); } }\n\n        /// <summary>\n        ///     Gets the text indicating special conditions have been met.\n        /// </summary>\n        /// <value>The text indicating special conditions have been met.</value>\n        public Text.XivString OnReachBigFish { get { return AsString(\"BigFish{OnReach}\"); } }\n\n        /// <summary>\n        ///     Gets the text indicating special conditions have ended.\n        /// </summary>\n        /// <value>The text indicating special conditions have ended.</value>\n        public Text.XivString OnEndBigFish { get { return AsString(\"BigFish{OnEnd}\"); } }\n\n        /// <summary>\n        ///     Gets the FishingSpotCategory of the current <see cref=\"FishingSpot\" />.\n        /// </summary>\n        /// <value>The FishingSpotCategory of the current <see cref=\"FishingSpot\" />.</value>\n        public byte FishingSpotCategory { get { return As<byte>(\"FishingSpotCategory\"); } }\n\n        /// <summary>\n        ///     Gets the <see cref=\"TerritoryType\" /> the current <see cref=\"FishingSpot\" /> is in.\n        /// </summary>\n        /// <value>The <see cref=\"TerritoryType\" /> the current <see cref=\"FishingSpot\" /> is in.</value>\n        public TerritoryType TerritoryType { get { return As<TerritoryType>(); } }\n\n        /// <summary>\n        ///     Gets the X-coordinate of the fishing spot in the <see cref=\"TerritoryType\" />.\n        /// </summary>\n        /// <remarks>\n        ///     This coordinate is in 3D-space.\n        /// </remarks>\n        /// <value>The X-coordinate of the fishing spot in the <see cref=\"TerritoryType\" />.</value>\n        public int X { get { return AsInt32(\"X\"); } }\n\n        /// <summary>\n        ///     Gets the Z-coordinate of the fishing spot in the <see cref=\"TerritoryType\" />.\n        /// </summary>\n        /// <remarks>\n        ///     This coordinate is in 3D-space.\n        /// </remarks>\n        /// <value>The Z-coordinate of the fishing spot in the <see cref=\"TerritoryType\" />.</value>\n        public int Z { get { return AsInt32(\"Z\"); } }\n\n        /// <summary>\n        ///     Gets the radius of the current <see cref=\"FishingSpot\" />.\n        /// </summary>\n        /// <remarks>\n        ///     Value is most likely in map-space, and only used when displaying the map of fishing spot.\n        /// </remarks>\n        /// <value>The radius of the current <see cref=\"FishingSpot\" />.</value>\n        public int Radius { get { return AsInt32(\"Radius\"); } }\n\n        /// <summary>\n        ///     Gets the <see cref=\"PlaceName\" /> of the current <see cref=\"FishingSpot\" />.\n        /// </summary>\n        /// <value>The <see cref=\"PlaceName\" /> of the current <see cref=\"FishingSpot\" />.</value>\n        public PlaceName PlaceName { get { return As<PlaceName>(); } }\n\n        /// <summary>\n        ///     Gets the items that can be obtained at the current <see cref=\"FishingSpot\" />.\n        /// </summary>\n        /// <value>The items that can be obtained at the current <see cref=\"FishingSpot\" />.</value>\n        public IEnumerable<Item> Items { get { return _Items ?? (_Items = BuildItems()); } }\n\n        #endregion\n\n        #region Constructors\n\n        /// <summary>\n        ///     Initializes a new instance of the <see cref=\"FishingSpot\" /> class.\n        /// </summary>\n        /// <param name=\"sheet\"><see cref=\"IXivSheet\" /> containing this object.</param>\n        /// <param name=\"sourceRow\"><see cref=\"IRelationalRow\" /> to read data from.</param>\n        public FishingSpot(IXivSheet sheet, IRelationalRow sourceRow) : base(sheet, sourceRow) { }\n\n        #endregion\n\n        #region Build\n\n        /// <summary>\n        ///     Create an array of items that can be obtained at the current <see cref=\"FishingSpot\" />.\n        /// </summary>\n        /// <returns>Returns an array of items that can be obtained at the current <see cref=\"FishingSpot\" />.</returns>\n        private Item[] BuildItems() {\n            const int Count = 10;\n\n            var items = new List<Item>();\n            for (var i = 0; i < Count; ++i) {\n                var item = As<Item>(\"Item\", i);\n                if (item.Key != 0)\n                    items.Add(item);\n            }\n\n            return items.ToArray();\n        }\n\n        #endregion\n\n        #region Location\n        public double MapX { get { return TerritoryType.Map.ToMapCoordinate2d(X, TerritoryType.Map.OffsetX); } }\n\n        public double MapY { get { return TerritoryType.Map.ToMapCoordinate2d(Z, TerritoryType.Map.OffsetY); } }\n\n        IEnumerable<ILocation> ILocatable.Locations {\n            get {\n                yield return this;\n            }\n        }\n        #endregion\n\n        /// <summary>\n        ///     Returns a string that represents the current <see cref=\"FishingSpot\" />.\n        /// </summary>\n        /// <returns>Returns the value of <see cref=\"PlaceName\" />.</returns>\n        public override string ToString() {\n            return string.Format(\"{0}\", PlaceName);\n        }\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Xiv/GCScripShopCategory.cs",
    "content": "﻿using SaintCoinach.Ex.Relational;\n\nnamespace SaintCoinach.Xiv {\n    public class GCScripShopCategory : XivRow {\n        public GrandCompany GrandCompany => As<GrandCompany>();\n        public int Tier => AsInt32(\"Tier\");\n        public int SubCategory => AsInt32(\"SubCategory\");\n\n        public GCScripShopCategory(IXivSheet sheet, IRelationalRow sourceRow) : base(sheet, sourceRow) { }\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Xiv/GCScripShopItem.cs",
    "content": "using System.Collections.Generic;\nusing System.Linq;\n\nusing SaintCoinach.Ex.Relational;\n\nnamespace SaintCoinach.Xiv {\n    public class GCScripShopItem : XivSubRow, IShopListing, IShopListingItem {\n        #region Properties\n\n        // ReSharper disable once InconsistentNaming\n        public GCShop GCShop { get; private set; }\n        public ShopListingItem Cost { get; private set; }\n        // ReSharper disable once InconsistentNaming\n        public GCScripShopCategory GCScripShopCategory { get; private set; }\n\n        public Item Item => As<Item>();\n        public GrandCompanyRank RequiredGrandCompanyRank => As<GrandCompanyRank>(\"Required{GrandCompanyRank}\");\n        public int GCSealsCost => AsInt32(\"Cost{GCSeals}\");\n        public byte SortKey => As<byte>(\"SortKey\");\n\n        #endregion\n\n        #region Constructors\n\n        #region Constructor\n\n        public GCScripShopItem(IXivSheet sheet, IRelationalRow sourceRow) : base(sheet, sourceRow) {\n            GCScripShopCategory = Sheet.Collection.GetSheet<GCScripShopCategory>()[ParentKey];\n            GCShop = Sheet.Collection.GetSheet<GCShop>().FirstOrDefault(_ => _.GrandCompany.Key == GCScripShopCategory.GrandCompany.Key);\n\n            var sealItem = GCShop.GrandCompany.SealItem;\n            Cost = new ShopListingItem(this, sealItem, GCSealsCost, false, 0);\n        }\n\n        #endregion\n\n        #endregion\n\n        public override string ToString() => Item?.ToString();\n\n        #region IShopListing Members\n\n        IEnumerable<IShopListingItem> IShopListing.Rewards { get { yield return this; } }\n\n        IEnumerable<IShopListingItem> IShopListing.Costs { get { yield return Cost; } }\n\n        IEnumerable<IShop> IShopListing.Shops { get { yield return GCShop; } }\n\n        #endregion\n\n        #region IShopListingItem Members\n\n        Item IShopListingItem.Item => Item;\n\n        bool IShopListingItem.IsHq => false;\n\n        IShopListing IShopListingItem.ShopItem => this;\n\n        int IShopListingItem.CollectabilityRating => 0;\n\n        int IShopListingItem.Count => 1;\n\n        #endregion\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Xiv/GCShop.cs",
    "content": "using System.Collections.Generic;\nusing System.Linq;\n\nusing SaintCoinach.Ex.Relational;\n\nnamespace SaintCoinach.Xiv {\n    // ReSharper disable once InconsistentNaming\n    /// <summary>\n    ///     Class representing a Grand Company shop from the game data.\n    /// </summary>\n    public class GCShop : XivRow, IShop, ILocatable, IItemSource {\n        #region Fields\n\n        /// <summary>\n        ///     <see cref=\"ENpcs\" /> offering the current <see cref=\"GCShop\" />.\n        /// </summary>\n        private ENpc[] _ENpcs;\n\n        /// <summary>\n        ///     Items offered by the current <see cref=\"GCShop\" />.\n        /// </summary>\n        private GCScripShopItem[] _Items;\n\n        #endregion\n\n        #region Properties\n\n        /// <summary>\n        ///     Gets the <see cref=\"GrandCompany\" /> of the current <see cref=\"GCShop\" />.\n        /// </summary>\n        /// <value>The <see cref=\"GrandCompany\" /> of the current <see cref=\"GCShop\" />.</value>\n        public GrandCompany GrandCompany { get { return As<GrandCompany>(); } }\n\n        /// <summary>\n        ///     Gets the items offered by the current <see cref=\"GCShop\" />.\n        /// </summary>\n        /// <value>The items offered by the current <see cref=\"GCShop\" />.</value>\n        public IEnumerable<GCScripShopItem> Items { get { return _Items ?? (_Items = BuildItems()); } }\n\n        /// <summary>\n        /// Gets the locations of the current object.\n        /// </summary>\n        /// <value>The locations of the current object.</value>\n        public IEnumerable<ILocation> Locations { get { return ENpcs.SelectMany(_ => _.Locations); } }\n\n        #endregion\n\n        #region Constructors\n\n        /// <summary>\n        ///     Initializes a new instance of the <see cref=\"GCShop\" /> class.\n        /// </summary>\n        /// <param name=\"sheet\"><see cref=\"IXivSheet\" /> containing this object.</param>\n        /// <param name=\"sourceRow\"><see cref=\"IRelationalRow\" /> to read data from.</param>\n        public GCShop(IXivSheet sheet, IRelationalRow sourceRow) : base(sheet, sourceRow) { }\n\n        #endregion\n\n        /// <summary>\n        ///     Gets the <see cref=\"ENpcs\" /> offering the current shop.\n        /// </summary>\n        /// <value>The <see cref=\"ENpcs\" /> offering the current shop.</value>\n        public IEnumerable<ENpc> ENpcs { get { return _ENpcs ?? (_ENpcs = BuildENpcs()); } }\n\n        /// <summary>\n        ///     Gets the listings of the current shop.\n        /// </summary>\n        /// <value>The listings of the current shop.</value>\n        IEnumerable<IShopListing> IShop.ShopListings { get { return Items; } }\n\n        /// <summary>\n        ///     Gets the name of the current shop.\n        /// </summary>\n        /// <value>The name of the current shop's <see cref=\"GrandCompany\" />.</value>\n        string IShop.Name { get { return string.Format(\"{0}\", GrandCompany); } }\n\n        #region Build\n\n        /// <summary>\n        ///     Build the array of <see cref=\"ENpc\" />s offering this shop.\n        /// </summary>\n        /// <returns>An array of <see cref=\"ENpc\" />s offering this shop.</returns>\n        private ENpc[] BuildENpcs() {\n            return Sheet.Collection.ENpcs.FindWithData(Key).ToArray();\n        }\n\n        /// <summary>\n        ///     Build the array of <see cref=\"GCScripShopItem\" />s offered by this shop.\n        /// </summary>\n        /// <remarks>\n        ///     This method takes all <see cref=\"GCScripShopItem\" />s with keys from <see cref=\"Min\" /> to\n        ///     <see cref=\"Max\" /> (inclusive).\n        /// </remarks>\n        /// <returns>An array of <see cref=\"GCScripShopItem\" />s offered by this shop.</returns>\n        private GCScripShopItem[] BuildItems() {\n            var items = new List<GCScripShopItem>();\n\n            var gcShopCategoryKeys = Sheet.Collection.GetSheet<GCScripShopCategory>()\n                .Where(c => c.GrandCompany.Key == this.GrandCompany.Key)\n                .ToDictionary(c => c.Key);\n\n            var gcItems = Sheet.Collection.GetSheet2<GCScripShopItem>();\n            foreach (var gcItem in gcItems) {\n                if (gcItem.Item.Key != 0 && gcShopCategoryKeys.ContainsKey(gcItem.ParentKey))\n                    items.Add(gcItem);\n            }\n\n            return items.ToArray();\n        }\n\n        #endregion\n\n        #region IItemSource Members\n\n        private Item[] _ItemSourceItems;\n\n        /// <summary>\n        /// Gets the <see cref=\"Item\"/>s that can be obtained from the current object.\n        /// </summary>\n        /// <value>The <see cref=\"Item\"/>s that can be obtained from the current object.</value>\n        IEnumerable<Item> IItemSource.Items {\n            get { return _ItemSourceItems ?? (_ItemSourceItems = Items.Select(i => i.Item).ToArray()); }\n        }\n\n        #endregion\n\n        public override string ToString() {\n            return GrandCompany.ToString();\n        }\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Xiv/GCShopItemCategory.cs",
    "content": "using SaintCoinach.Ex.Relational;\n\nnamespace SaintCoinach.Xiv {\n    // ReSharper disable once InconsistentNaming\n    public class GCShopItemCategory : XivRow {\n        #region Properties\n\n        public Text.XivString Name { get { return AsString(\"Name\"); } }\n\n        #endregion\n\n        #region Constructors\n\n        #region Constructor\n\n        public GCShopItemCategory(IXivSheet sheet, IRelationalRow sourceRow) : base(sheet, sourceRow) { }\n\n        #endregion\n\n        #endregion\n\n        public override string ToString() {\n            return Name;\n        }\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Xiv/GatheringCondition.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace SaintCoinach.Xiv {\n    using Ex.Relational;\n\n    public class GatheringCondition : XivRow {\n        #region Properties\n\n        public Text.XivString Text { get { return AsString(\"Text\"); } }\n\n        #endregion\n\n        #region Constructors\n\n        public GatheringCondition(IXivSheet sheet, IRelationalRow sourceRow) : base(sheet, sourceRow) { }\n\n        #endregion\n\n        public override string ToString() {\n            return Text;\n        }\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Xiv/GatheringExp.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace SaintCoinach.Xiv {\n    using Ex.Relational;\n\n    public class GatheringExp : XivRow {\n        #region Properties\n\n        public int Exp { get { return AsInt32(\"Exp\"); } }\n\n        #endregion\n\n        #region Constructors\n\n        public GatheringExp(IXivSheet sheet, IRelationalRow sourceRow) : base(sheet, sourceRow) { }\n\n        #endregion\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Xiv/GatheringItem.cs",
    "content": "using SaintCoinach.Ex.Relational;\n\nnamespace SaintCoinach.Xiv {\n    public class GatheringItem : GatheringItemBase {\n        #region Properties\n\n        public bool IsHidden => AsBoolean(\"IsHidden\");\n\n        #endregion\n\n        #region Constructors\n\n        public GatheringItem(IXivSheet sheet, IRelationalRow sourceRow) : base(sheet, sourceRow) { }\n\n        #endregion\n\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Xiv/GatheringItemBase.cs",
    "content": "using SaintCoinach.Ex.Relational;\n\nnamespace SaintCoinach.Xiv {\n    public abstract class GatheringItemBase : XivRow {\n        #region Properties\n\n        public ItemBase Item => As<ItemBase>(\"Item\");\n\n        #endregion\n\n        #region Constructors\n\n        public GatheringItemBase(IXivSheet sheet, IRelationalRow sourceRow) : base(sheet, sourceRow) { }\n\n        #endregion\n\n        public override string ToString() {\n            return string.Format(\"{0}\", Item);\n        }\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Xiv/GatheringNotebookList.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace SaintCoinach.Xiv {\n    using Ex.Relational;\n\n    public class GatheringNotebookList : XivRow {\n        #region Fields\n        private GatheringItem[] _Items;\n        #endregion\n\n        #region Properties\n\n        public IEnumerable<GatheringItem> Items { get { return _Items ?? (_Items = BuildItems()); } }\n\n        #endregion\n\n        #region Constructors\n\n        public GatheringNotebookList(IXivSheet sheet, IRelationalRow sourceRow) : base(sheet, sourceRow) { }\n\n        #endregion\n\n        #region Build\n        private GatheringItem[] BuildItems() {\n            const int Count = 100;\n\n            var items = new List<GatheringItem>();\n\n            for (var i = 0; i < Count; ++i) {\n                var itm = As<GatheringItem>(i);\n                if (itm.Key != 0)\n                    items.Add(itm);\n            }\n            return items.ToArray();\n        }\n        #endregion\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Xiv/GatheringNotebookRegion.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace SaintCoinach.Xiv {\n    using Ex.Relational;\n\n    public class GatheringNotebookRegion : XivRow {\n        #region Properties\n\n        public PlaceName PlaceName { get { return As<PlaceName>(); } }\n\n        #endregion\n\n        #region Constructors\n\n        public GatheringNotebookRegion(IXivSheet sheet, IRelationalRow sourceRow) : base(sheet, sourceRow) { }\n\n        #endregion\n\n        public override string ToString() {\n            return PlaceName.Name;\n        }\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Xiv/GatheringPoint.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace SaintCoinach.Xiv {\n    using Ex.Relational;\n\n    public class GatheringPoint : XivRow {\n        #region Fields\n\n        private GatheringPointBonus[] _Bonuses;\n\n        #endregion\n\n        #region Properties\n\n        public GatheringPointBase Base { get { return As<GatheringPointBase>(); } }\n        public TerritoryType TerritoryType { get { return As<TerritoryType>(); } }\n        public PlaceName PlaceName { get { return As<PlaceName>(); } }\n        public GatheringPointBonus[] GatheringPointBonus { get { return _Bonuses ?? (_Bonuses = BuildGatheringPointBonus()); } }\n        public GatheringSubCategory GatheringSubCategory => As<GatheringSubCategory>();\n        #endregion\n\n        #region Constructors\n\n        public GatheringPoint(IXivSheet sheet, IRelationalRow sourceRow) : base(sheet, sourceRow) { }\n\n        #endregion\n\n        #region Build\n\n        public GatheringPointBonus[] BuildGatheringPointBonus() {\n            const int Count = 2;\n\n            var bonuses = new List<GatheringPointBonus>();\n            for (var i = 0; i < Count; ++i) {\n                var bonus = As<GatheringPointBonus>(i);\n                if (bonus.Key != 0)\n                    bonuses.Add(bonus);\n            }\n\n            return bonuses.ToArray();\n        }\n\n        #endregion\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Xiv/GatheringPointBase.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace SaintCoinach.Xiv {\n    using Ex.Relational;\n\n    public class GatheringPointBase : XivRow, IItemSource {\n        #region Fields\n        private GatheringItemBase[] _Items;\n        #endregion\n\n        #region Properties\n\n        public GatheringType Type { get { return As<GatheringType>(); } }\n        public int GatheringLevel { get { return AsInt32(\"GatheringLevel\"); } }\n        public IEnumerable<GatheringItemBase> Items { get { return _Items ?? (_Items = BuildItems()); } }\n        #endregion\n\n        #region Constructors\n\n        public GatheringPointBase(IXivSheet sheet, IRelationalRow sourceRow) : base(sheet, sourceRow) { }\n\n        #endregion\n\n        #region Build\n        private GatheringItemBase[] BuildItems() {\n            const int Count = 8;\n\n            var items = new GatheringItemBase[Count];\n            for (var i = 0; i < Count; ++i) {\n                var gib = (GatheringItemBase)this[\"Item[\" + i + \"]\"];\n                if (gib?.Key != null && gib?.Item?.Key != null)\n                    items[i] = gib;\n            }\n            return items;\n        }\n        #endregion\n\n        #region IItemSource Members\n\n        private Item[] _ItemSourceItems;\n        IEnumerable<Item> IItemSource.Items {\n            get { return _ItemSourceItems ?? (_ItemSourceItems = Items.Where(i => i != null).Select(i => i.Item).OfType<Item>().ToArray()); }\n        }\n\n        #endregion\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Xiv/GatheringPointBonus.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace SaintCoinach.Xiv {\n    using Ex.Relational;\n\n    public class GatheringPointBonus : XivRow {\n        #region Properties\n\n        public GatheringCondition Condition { get { return As<GatheringCondition>(\"Condition\"); } }\n        public int ConditionValue { get { return AsInt32(\"ConditionValue\"); } }\n        public GatheringPointBonusType BonusType { get { return As<GatheringPointBonusType>(\"BonusType\"); } }\n        public int BonusValue { get { return AsInt32(\"BonusValue\"); } }\n\n        #endregion\n\n        #region Constructors\n\n        public GatheringPointBonus(IXivSheet sheet, IRelationalRow sourceRow) : base(sheet, sourceRow) { }\n        \n        #endregion\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Xiv/GatheringPointBonusType.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace SaintCoinach.Xiv {\n    using Ex.Relational;\n\n    public class GatheringPointBonusType : XivRow {\n        #region Properties\n\n        public Text.XivString Text { get { return AsString(\"Text\"); } }\n\n        #endregion\n\n        #region Constructors\n\n        public GatheringPointBonusType(IXivSheet sheet, IRelationalRow sourceRow) : base(sheet, sourceRow) { }\n\n        #endregion\n\n        public override string ToString() {\n            return Text;\n        }\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Xiv/GatheringPointName.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace SaintCoinach.Xiv {\n    using Ex.Relational;\n\n    public class GatheringPointName : XivRow, IQuantifiableXivString {\n        #region Properties\n\n        public Text.XivString Singular { get { return AsString(\"Singular\"); } }\n        public Text.XivString Plural { get { return Sheet.Collection.ActiveLanguage == Ex.Language.Japanese ? Singular : AsString(\"Plural\"); } }\n\n        #endregion\n\n        #region Constructors\n\n        public GatheringPointName(IXivSheet sheet, IRelationalRow sourceRow) : base(sheet, sourceRow) { }\n\n        #endregion\n\n        public override string ToString() {\n            return Singular;\n        }\n\n        #region IQuantifiableName Members\n        string IQuantifiable.Singular {\n            get { return Singular; }\n        }\n\n        string IQuantifiable.Plural {\n            get { return Plural; }\n        }\n        #endregion\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Xiv/GatheringSubCategory.cs",
    "content": "﻿using SaintCoinach.Ex.Relational;\nusing System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace SaintCoinach.Xiv {\n    public class GatheringSubCategory : XivRow {\n        #region Properties\n\n        public Item Item => As<Item>();\n        public string FolkloreBookName => AsString(\"FolkloreBook\");\n\n        #endregion\n\n        public GatheringSubCategory(IXivSheet sheet, IRelationalRow sourceRow) : base(sheet, sourceRow) { }\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Xiv/GatheringType.cs",
    "content": "using SaintCoinach.Ex.Relational;\nusing SaintCoinach.Imaging;\n\nnamespace SaintCoinach.Xiv {\n    public class GatheringType : XivRow {\n        #region Properties\n\n        public Text.XivString Name { get { return AsString(\"Name\"); } }\n        public ImageFile MainIcon { get { return AsImage(\"Icon{Main}\"); } }\n        public ImageFile SubIcon { get { return AsImage(\"Icon{Off}\"); } }\n\n        #endregion\n\n        #region Constructors\n\n        public GatheringType(IXivSheet sheet, IRelationalRow sourceRow) : base(sheet, sourceRow) { }\n\n        #endregion\n\n        public override string ToString() {\n            return Name;\n        }\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Xiv/GeneralAction.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nusing SaintCoinach.Ex.Relational;\n\nnamespace SaintCoinach.Xiv {\n    public class GeneralAction : ActionBase {\n        #region Constructors\n\n        public GeneralAction(IXivSheet sheet, IRelationalRow sourceRow) : base(sheet, sourceRow) { }\n\n        #endregion\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Xiv/GenericLocation.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace SaintCoinach.Xiv {\n    public class GenericLocation : ILocation {\n        #region ILocation Members\n        public double MapX { get; private set; }\n\n        public double MapY { get; private set; }\n\n        public PlaceName PlaceName { get; private set; }\n        #endregion\n\n        #region Constructors\n        public GenericLocation(PlaceName placeName, double x, double y) {\n            this.MapX = x;\n            this.MapY = y;\n            this.PlaceName = placeName;\n        }\n        #endregion\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Xiv/GilShop.cs",
    "content": "﻿using System.Collections.Generic;\nusing System.Linq;\n\nusing SaintCoinach.Ex.Relational;\nusing SaintCoinach.Imaging;\n\nnamespace SaintCoinach.Xiv {\n    /// <summary>\n    ///     Class representing a shop that trades using gil.\n    /// </summary>\n    public class GilShop : XivRow, IShop, IItemSource {\n        #region Fields\n\n        /// <summary>\n        ///     <see cref=\"ENpc\" />s offering the current shop.\n        /// </summary>\n        private ENpc[] _ENpcs;\n\n        /// <summary>\n        ///     <see cref=\"ShopItem\" />s offered by the current shop.\n        /// </summary>\n        private GilShopItem[] _ShopItems;\n\n        #endregion\n\n        #region Properties\n\n        /// <summary>\n        ///     Gets the <see cref=\"GilShopItem\" />s offered by the current shop.\n        /// </summary>\n        /// <value>The <see cref=\"GilShopItem\" />s offered by the current shop.</value>\n        public IEnumerable<GilShopItem> Items { get { return _ShopItems ?? (_ShopItems = BuildShopItems()); } }\n\n        #endregion\n\n        #region Constructors\n\n        /// <summary>\n        ///     Initializes a new instance of the <see cref=\"GilShop\" /> class.\n        /// </summary>\n        /// <param name=\"sheet\"><see cref=\"IXivSheet\" /> containing this object.</param>\n        /// <param name=\"sourceRow\"><see cref=\"IRelationalRow\" /> to read data from.</param>\n        public GilShop(IXivSheet sheet, IRelationalRow sourceRow) : base(sheet, sourceRow) { }\n\n        #endregion\n\n        /// <summary>\n        ///     Gets the name of the current shop.\n        /// </summary>\n        /// <value>The name of the current shop.</value>\n        public Text.XivString Name { get { return AsString(\"Name\"); } }\n\n        /// <summary>\n        ///     Gets the <see cref=\"ENpc\" />s offering the current shop.\n        /// </summary>\n        /// <value>The <see cref=\"ENpc\" />s offering the current shop.</value>\n        public IEnumerable<ENpc> ENpcs { get { return _ENpcs ?? (_ENpcs = BuildENpcs()); } }\n\n        #region IShop Members\n\n        /// <summary>\n        ///     Gets the listings of the current shop.\n        /// </summary>\n        /// <value>The listings of the current shop.</value>\n        IEnumerable<IShopListing> IShop.ShopListings { get { return Items; } }\n\n        #endregion\n\n        /// <summary>\n        ///     Returns a string representation of the current shop.\n        /// </summary>\n        /// <returns>The value of <see cref=\"Name\" />.</returns>\n        public override string ToString() {\n            return Name;\n        }\n\n        #region Build\n\n        /// <summary>\n        ///     Build an array of the <see cref=\"ENpc\" />s offering the current shop.\n        /// </summary>\n        /// <returns>An array of the <see cref=\"ENpc\" />s offering the current shop.</returns>\n        private ENpc[] BuildENpcs() {\n            return Sheet.Collection.ENpcs.FindWithData(Key).ToArray();\n        }\n\n        /// <summary>\n        ///     Build an array of the <see cref=\"GilShopItem\" />s offered by the current shop.\n        /// </summary>\n        /// <returns>An array of the <see cref=\"GilShopItem\" />s offered by the current shop.</returns>\n        private GilShopItem[] BuildShopItems() {\n            return Sheet.Collection.GetSheet2<GilShopItem>()\n                .Cast<GilShopItem>()\n                .Where(r => r.ParentKey == this.Key)\n                .ToArray();\n        }\n\n        #endregion\n\n\n        #region IItemSource Members\n\n        private Item[] _ItemSourceItems;\n\n        /// <summary>\n        /// Gets the <see cref=\"Item\"/>s that can be obtained from the current object.\n        /// </summary>\n        /// <value>The <see cref=\"Item\"/>s that can be obtained from the current object.</value>\n        IEnumerable<Item> IItemSource.Items {\n            get { return _ItemSourceItems ?? (_ItemSourceItems = Items.Select(i => i.Item).ToArray()); }\n        }\n\n        #endregion\n\n        #region IShop Members\n\n        string IShop.Name {\n            get { return Name; }\n        }\n\n        #endregion\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Xiv/GilShopItem.cs",
    "content": "﻿using System.Collections.Generic;\nusing System.Linq;\n\nusing SaintCoinach.Ex.Relational;\n\nnamespace SaintCoinach.Xiv {\n    /// <summary>\n    ///     Class representing an <see cref=\"Item\" /> used in <see cref=\"GilShop\" />s.\n    /// </summary>\n    public class GilShopItem : XivSubRow, IShopListing, IShopListingItem {\n        #region Static\n\n        /// <summary>\n        ///     Key of the <see cref=\"Item\" /> used as currency (Gil).\n        /// </summary>\n        private const int GilItemKey = 1;\n\n        #endregion\n\n        #region Fields\n\n        /// <summary>\n        ///     Cost of the current shop item.\n        /// </summary>\n        private readonly ShopListingItem _Cost;\n\n        /// <summary>\n        ///     Shops offering the current item.\n        /// </summary>\n        private GilShop[] _Shops;\n\n        #endregion\n\n        #region Properties\n\n        /// <summary>\n        ///     Gets the associated <see cref=\"Item\" />.\n        /// </summary>\n        /// <value>The associated <see cref=\"Item\" />.</value>\n        public Item Item { get { return As<Item>(); } }\n\n        #endregion\n\n        #region Constructors\n\n        /// <summary>\n        ///     Initializes a new instance of the <see cref=\"ShopItem\" /> class.\n        /// </summary>\n        /// <param name=\"sheet\"><see cref=\"IXivSheet\" /> containing this object.</param>\n        /// <param name=\"sourceRow\"><see cref=\"IRelationalRow\" /> to read data from.</param>\n        public GilShopItem(IXivSheet sheet, IRelationalRow sourceRow) : base(sheet, sourceRow) {\n            _Cost = new ShopListingItem(this, Sheet.Collection.GetSheet<Item>()[GilItemKey],\n                Item.Ask, false, 0);\n        }\n\n        #endregion\n\n        /// <summary>\n        ///     Returns a string representation of the current shop item.\n        /// </summary>\n        /// <returns>The name of <see cref=\"Item\" />.</returns>\n        public override string ToString() {\n            return string.Format(\"{0}\", Item);\n        }\n\n        #region Build\n\n        /// <summary>\n        ///     Build an array of the <see cref=\"GilShop\" />s offering the current item.\n        /// </summary>\n        /// <returns>An array of the <see cref=\"GilShop\" />s offering the current item.</returns>\n        private GilShop[] BuildShops() {\n            var sSheet = Sheet.Collection.GetSheet<GilShop>();\n            return sSheet.Where(shop => shop.Items.Contains(this)).ToArray();\n        }\n\n        #endregion\n\n        #region IShopListing Members\n\n        /// <summary>\n        ///     Gets the rewards of the current listing.\n        /// </summary>\n        /// <value>The rewards of the current listing.</value>\n        IEnumerable<IShopListingItem> IShopListing.Rewards { get { yield return this; } }\n\n        /// <summary>\n        ///     Gets the costs of the current listing.\n        /// </summary>\n        /// <value>The costs of the current listing.</value>\n        IEnumerable<IShopListingItem> IShopListing.Costs { get { yield return _Cost; } }\n\n        /// <summary>\n        ///     Gets the shops offering the current listing.\n        /// </summary>\n        /// <value>The shops offering the current listing.</value>\n        public IEnumerable<IShop> Shops { get { return _Shops ?? (_Shops = BuildShops()); } }\n\n        #endregion\n\n        #region IShopListingItem Members\n\n        /// <summary>\n        ///     Gets the item of the current listing entry.\n        /// </summary>\n        /// <value>The item of the current listing entry.</value>\n        Item IShopListingItem.Item { get { return Item; } }\n\n        /// <summary>\n        ///     Gets the count for the current listing entry.\n        /// </summary>\n        /// <value>\n        ///     <value>1</value>\n        /// </value>\n        int IShopListingItem.Count { get { return 1; } }\n\n        /// <summary>\n        ///     Gets a value indicating whether the item is high-quality.\n        /// </summary>\n        /// <value>\n        ///     <c>false</c>\n        /// </value>\n        bool IShopListingItem.IsHq { get { return false; } }\n\n        /// <summary>\n        ///     Gets the collectability rating for the item.\n        /// </summary>\n        /// <value>\n        ///     <c>false</c>\n        /// </value>\n        int IShopListingItem.CollectabilityRating { get { return 0; } }\n\n        /// <summary>\n        ///     Gets the <see cref=\"IShopListing\" /> the current entry is for.\n        /// </summary>\n        /// <value>\n        ///     <c>this</c>\n        /// </value>\n        IShopListing IShopListingItem.ShopItem { get { return this; } }\n\n        #endregion\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Xiv/GoldSaucerTextData.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nusing SaintCoinach.Ex.Relational;\n\nnamespace SaintCoinach.Xiv {\n    public class GoldSaucerTextData : XivRow {\n        #region Properties\n\n        public Text.XivString Text { get { return AsString(\"Text\"); } }\n\n        #endregion\n\n        #region Constructors\n\n        public GoldSaucerTextData(IXivSheet sheet, IRelationalRow sourceRow) : base(sheet, sourceRow) { }\n\n        #endregion\n\n        public override string ToString() {\n            return Text;\n        }\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Xiv/GrandCompany.cs",
    "content": "using SaintCoinach.Ex.Relational;\n\nnamespace SaintCoinach.Xiv {\n    public class GrandCompany : XivRow {\n        #region Static\n\n        private const int SealItemOffset = 19;\n\n        #endregion\n\n        #region Properties\n\n        public Text.XivString Name { get { return AsString(\"Name\"); } }\n        public Item SealItem { get { return Sheet.Collection.GetSheet<Item>()[Key + SealItemOffset]; } }\n\n        #endregion\n\n        #region Constructors\n\n        #region Constructor\n\n        public GrandCompany(IXivSheet sheet, IRelationalRow sourceRow) : base(sheet, sourceRow) { }\n\n        #endregion\n\n        #endregion\n\n        public override string ToString() {\n            return Name;\n        }\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Xiv/GrandCompanyRank.cs",
    "content": "using SaintCoinach.Ex.Relational;\nusing SaintCoinach.Imaging;\n\nnamespace SaintCoinach.Xiv {\n    public class GrandCompanyRank : XivRow {\n        #region Properties\n\n        public int Tier { get { return AsInt32(\"Tier\"); } }\n        public int Order { get { return AsInt32(\"Order\"); } }\n        public int MaximumSeals { get { return AsInt32(\"MaxSeals\"); } }\n        public int RequiredSeals { get { return AsInt32(\"RequiredSeals\"); } }\n        public ImageFile MaelstromIcon { get { return AsImage(\"Icon{Maelstrom}\"); } }\n        public ImageFile SerpentsIcon { get { return AsImage(\"Icon{Serpents}\"); } }\n        public ImageFile FlamesIcon { get { return AsImage(\"Icon{Flames}\"); } }\n        public Quest MaelstromQuest { get { return As<Quest>(\"Quest{Maelstrom}\"); } }\n        public Quest SerpentsQuest { get { return As<Quest>(\"Quest{Serpents}\"); } }\n        public Quest FlamesQuest { get { return As<Quest>(\"Quest{Flames}\"); } }\n\n        #endregion\n\n        #region Constructors\n\n        #region Constructor\n\n        public GrandCompanyRank(IXivSheet sheet, IRelationalRow sourceRow) : base(sheet, sourceRow) { }\n\n        #endregion\n\n        #endregion\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Xiv/GuardianDeity.cs",
    "content": "using SaintCoinach.Ex.Relational;\nusing SaintCoinach.Imaging;\n\nnamespace SaintCoinach.Xiv {\n    public class GuardianDeity : XivRow {\n        #region Properties\n\n        public Text.XivString Name { get { return AsString(\"Name\"); } }\n        public Text.XivString Description { get { return AsString(\"Description\"); } }\n        public ImageFile Icon { get { return AsImage(\"Icon\"); } }\n\n        #endregion\n\n        #region Constructors\n\n        #region Constructor\n\n        public GuardianDeity(IXivSheet sheet, IRelationalRow sourceRow) : base(sheet, sourceRow) { }\n\n        #endregion\n\n        #endregion\n\n        public override string ToString() {\n            return Name;\n        }\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Xiv/HousingFurniture.cs",
    "content": "using SaintCoinach.Ex.Relational;\n\nnamespace SaintCoinach.Xiv {\n    public class HousingFurniture : HousingItem {\n        public const string SgbPathFormat = \"bgcommon/hou/indoor/general/{0:D4}/asset/fun_b0_m{0:D4}.sgb\";\n\n        #region Constructors\n\n        public HousingFurniture(IXivSheet sheet, IRelationalRow sourceRow) : base(sheet, sourceRow) { }\n\n        #endregion\n\n        public override Graphics.Sgb.SgbFile GetScene() {\n            var sgbPath = string.Format(SgbPathFormat, ModelKey);\n\n            IO.File baseFile;\n            if (!Sheet.Collection.PackCollection.TryGetFile(sgbPath, out baseFile))\n                return null;\n\n            return new Graphics.Sgb.SgbFile(baseFile);\n        }\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Xiv/HousingItem.cs",
    "content": "using SaintCoinach.Ex.Relational;\n\nnamespace SaintCoinach.Xiv {\n    public abstract class HousingItem : XivRow {\n        #region Properties\n\n        public int ModelKey => AsInt32(\"ModelKey\");\n        public HousingItemCategory HousingItemCategory => As<HousingItemCategory>();\n        public Item Item => As<Item>(\"Item\");\n        public HousingLayoutLimit HousingLayoutLimit => As<HousingLayoutLimit>();\n        public bool DestroyedOnRemoval => AsBoolean(\"DestroyOnRemoval\");\n\n        #endregion\n\n        #region Constructors\n\n        protected HousingItem(IXivSheet sheet, IRelationalRow sourceRow) : base(sheet, sourceRow) { }\n\n        #endregion\n\n        public abstract Graphics.Sgb.SgbFile GetScene();\n\n        public override string ToString() => string.Format(\"{0}\", Item);\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Xiv/HousingItemCategory.cs",
    "content": "using SaintCoinach.Ex.Relational;\n\nnamespace SaintCoinach.Xiv {\n    public class HousingItemCategory : XivRow {\n        #region Properties\n\n        public Text.XivString Name { get { return AsString(\"Name\"); } }\n\n        #endregion\n\n        #region Constructors\n\n        #region Constructor\n\n        public HousingItemCategory(IXivSheet sheet, IRelationalRow sourceRow) : base(sheet, sourceRow) { }\n\n        #endregion\n\n        #endregion\n\n        public override string ToString() {\n            return Name;\n        }\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Xiv/HousingLayoutLimit.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nusing SaintCoinach.Ex.Relational;\n\nnamespace SaintCoinach.Xiv {\n    public class HousingLayoutLimit : XivRow {\n        #region Properties\n\n        public int PersonalChamber { get { return AsInt32(\"PersonalChamber\"); } }\n        public int Cottage { get { return AsInt32(\"Small\"); } }\n        public int House { get { return AsInt32(\"Medium\"); } }\n        public int Mansion { get { return AsInt32(\"Large\"); } }\n\n        #endregion\n\n        #region Constructors\n\n        protected HousingLayoutLimit(IXivSheet sheet, IRelationalRow sourceRow) : base(sheet, sourceRow) { }\n\n        #endregion\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Xiv/HousingYardObject.cs",
    "content": "using SaintCoinach.Ex.Relational;\n\nnamespace SaintCoinach.Xiv {\n    public class HousingYardObject : HousingItem {\n        public const string SgbPathFormat = \"bgcommon/hou/outdoor/general/{0:D4}/asset/gar_b0_m{0:D4}.sgb\";\n\n        #region Constructors\n\n        public HousingYardObject(IXivSheet sheet, IRelationalRow sourceRow) : base(sheet, sourceRow) { }\n\n        #endregion\n\n        public override Graphics.Sgb.SgbFile GetScene() {\n            var sgbPath = string.Format(SgbPathFormat, ModelKey);\n\n            IO.File baseFile;\n            if (!Sheet.Collection.PackCollection.TryGetFile(sgbPath, out baseFile))\n                return null;\n\n            return new Graphics.Sgb.SgbFile(baseFile);\n        }\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Xiv/IContentReward.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace SaintCoinach.Xiv {\n    /// <summary>\n    /// Interface for content rewards.\n    /// </summary>\n    public interface IContentReward {\n        /// <summary>\n        /// Gets the rewarded <see cref=\"Item\"/>.\n        /// </summary>\n        /// <value>The rewarded <see cref=\"Item\"/>.</value>\n        Item Item { get; }\n\n        /// <summary>\n        ///     Gets the count for the current reward.\n        /// </summary>\n        /// <value>The count for the current reward.</value>\n        int Count { get; }\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Xiv/IItemSource.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace SaintCoinach.Xiv {\n    /// <summary>\n    /// Interface for objects from which <see cref=\"Item\"/>s can be obtained.\n    /// </summary>\n    public interface IItemSource {\n        /// <summary>\n        /// Gets the <see cref=\"Item\"/>s that can be obtained from the current object.\n        /// </summary>\n        /// <value>The <see cref=\"Item\"/>s that can be obtained from the current object.</value>\n        IEnumerable<Item> Items { get; }\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Xiv/ILocatable.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace SaintCoinach.Xiv {\n    /// <summary>\n    /// Interface for objects that have specific locations.\n    /// </summary>\n    public interface ILocatable {\n        /// <summary>\n        /// Gets the locations of the current object.\n        /// </summary>\n        /// <value>The locations of the current object.</value>\n        IEnumerable<ILocation> Locations { get; }\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Xiv/ILocation.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace SaintCoinach.Xiv {\n    /// <summary>\n    /// Interface for objects defining a location in a zone (in map-coordinates).\n    /// </summary>\n    public interface ILocation {\n        /// <summary>\n        /// Gets the x-coordinate of the current object.\n        /// </summary>\n        /// <value>The x-coordinate of the current object.</value>\n        double MapX { get; }\n\n        /// <summary>\n        /// Gets the y-coordinate of the current object.\n        /// </summary>\n        /// <value>The y-coordinate of the current object.</value>\n        double MapY { get; }\n\n        /// <summary>\n        /// Gets the <see cref=\"PlaceName\"/> of the current object's location.\n        /// </summary>\n        /// <value>The <see cref=\"PlaceName\"/> of the current object's location.</value>\n        PlaceName PlaceName { get; }\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Xiv/IParameterObject.cs",
    "content": "﻿using System.Collections.Generic;\n\nnamespace SaintCoinach.Xiv {\n    /// <summary>\n    ///     Interface for objects that offer parameter bonuses.\n    /// </summary>\n    public interface IParameterObject {\n        #region Properties\n\n        /// <summary>\n        ///     Gets the parameters offered by the current object.\n        /// </summary>\n        /// <value>The parameters offered by the current object.</value>\n        IEnumerable<Parameter> Parameters { get; }\n\n        #endregion\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Xiv/IQuantifiable.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace SaintCoinach.Xiv {\n    public interface IQuantifiable {\n        string Singular { get; }\n        string Plural { get; }\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Xiv/IQuantifiableXivString.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace SaintCoinach.Xiv {\n    public interface IQuantifiableXivString : IQuantifiable {\n        new Text.XivString Singular { get; }\n        new Text.XivString Plural { get; }\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Xiv/IShop.cs",
    "content": "﻿using System.Collections.Generic;\n\nnamespace SaintCoinach.Xiv {\n    /// <summary>\n    ///     Interface for shops.\n    /// </summary>\n    public interface IShop : IItemSource {\n        #region Properties\n\n        /// <summary>\n        ///     Gets the key of the current shop.\n        /// </summary>\n        /// <value>The key of the current shop.</value>\n        int Key { get; }\n\n        /// <summary>\n        ///     Gets the name of the current shop.\n        /// </summary>\n        /// <value>The name of the current shop.</value>\n        string Name { get; }\n\n        /// <summary>\n        ///     Gets the <see cref=\"ENpcs\" /> offering the current shop.\n        /// </summary>\n        /// <value>The <see cref=\"ENpcs\" /> offering the current shop.</value>\n        IEnumerable<ENpc> ENpcs { get; }\n\n        /// <summary>\n        ///     Gets the listings of the current shop.\n        /// </summary>\n        /// <value>The listings of the current shop.</value>\n        IEnumerable<IShopListing> ShopListings { get; }\n\n        #endregion\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Xiv/IShopListing.cs",
    "content": "﻿using System.Collections.Generic;\n\nnamespace SaintCoinach.Xiv {\n    /// <summary>\n    ///     Interface for listings of shops.\n    /// </summary>\n    public interface IShopListing {\n        #region Properties\n\n        /// <summary>\n        ///     Gets the rewards of the current listing.\n        /// </summary>\n        /// <value>The rewards of the current listing.</value>\n        IEnumerable<IShopListingItem> Rewards { get; }\n\n        /// <summary>\n        ///     Gets the costs of the current listing.\n        /// </summary>\n        /// <value>The costs of the current listing.</value>\n        IEnumerable<IShopListingItem> Costs { get; }\n\n        /// <summary>\n        ///     Gets the shops offering the current listing.\n        /// </summary>\n        /// <value>The shops offering the current listing.</value>\n        IEnumerable<IShop> Shops { get; }\n\n        #endregion\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Xiv/IShopListingItem.cs",
    "content": "﻿namespace SaintCoinach.Xiv {\n    /// <summary>\n    ///     Interface for items used in a <see cref=\"IShopListing\" />.\n    /// </summary>\n    public interface IShopListingItem {\n        #region Properties\n\n        /// <summary>\n        ///     Gets the item of the current listing entry.\n        /// </summary>\n        /// <value>The item of the current listing entry.</value>\n        Item Item { get; }\n\n        /// <summary>\n        ///     Gets the count for the current listing entry.\n        /// </summary>\n        /// <value>The count for the current listing entry.</value>\n        int Count { get; }\n\n        /// <summary>\n        ///     Gets a value indicating whether the item is high-quality.\n        /// </summary>\n        /// <value>A value indicating whether the item is high-quality.</value>\n        bool IsHq { get; }\n\n        /// <summary>\n        ///     Gets the collectability rating for the item.\n        /// </summary>\n        int CollectabilityRating { get; }\n\n        /// <summary>\n        ///     Gets the <see cref=\"IShopListing\" /> the current entry is for.\n        /// </summary>\n        /// <value>The <see cref=\"IShopListing\" /> the current entry is for.</value>\n        IShopListing ShopItem { get; }\n\n        #endregion\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Xiv/IXivRow.cs",
    "content": "﻿using SaintCoinach.Ex.Relational;\n\nnamespace SaintCoinach.Xiv {\n    public interface IXivRow : IRelationalRow {\n        #region Properties\n\n        IRelationalRow SourceRow { get; }\n\n        new IXivSheet Sheet { get; }\n\n        #endregion\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Xiv/IXivSheet.Generic.cs",
    "content": "﻿using SaintCoinach.Ex.Relational;\n\nnamespace SaintCoinach.Xiv {\n    public interface IXivSheet<out T> : IRelationalSheet<T>, IXivSheet where T : IXivRow {\n        #region Properties\n\n        new T this[int key] { get; }\n\n        #endregion\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Xiv/IXivSheet.cs",
    "content": "﻿using System.Collections.Generic;\n\nusing SaintCoinach.Ex.Relational;\n\nnamespace SaintCoinach.Xiv {\n    public interface IXivSheet : IRelationalSheet {\n        #region Properties\n\n        new XivCollection Collection { get; }\n        new IXivRow this[int row] { get; }\n\n        #endregion\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Xiv/IXivSubRow.cs",
    "content": "﻿using SaintCoinach.Ex;\nusing SaintCoinach.Ex.Relational;\nusing System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace SaintCoinach.Xiv {\n    public interface IXivSubRow : IXivRow {\n        #region Properties\n\n        IRow ParentRow { get; }\n\n        int ParentKey { get; }\n\n        #endregion\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Xiv/InstanceContent.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nusing SaintCoinach.Ex.Relational;\nusing SaintCoinach.Imaging;\n\nnamespace SaintCoinach.Xiv {\n    /// <summary>\n    /// Class representing a duty.\n    /// </summary>\n    public class InstanceContent : ContentBase, IItemSource {\n        #region Fields\n        private InstanceContentData _Data;\n        #endregion\n\n        #region Properties\n\n        /// <summary>\n        /// Gets the time limit to complete the current content.\n        /// </summary>\n        /// <value>The time limit to complete the current content.</value>\n        public TimeSpan TimeLimit { get { return TimeSpan.FromMinutes(AsInt32(\"TimeLimit{min}\")); } }\n\n        /// <summary>\n        /// Gets the number of parties for the current content.\n        /// </summary>\n        /// <value>The number of parties for the current content.</value>\n        public int PartyCount { get { return AsInt32(\"PartyCount\"); } }\n\n        /// <summary>\n        /// Gets a value indicating whether a specific party composition is required, even in preformed parties.\n        /// </summary>\n        /// <value>A value indicating whether a specific party composition is required, even in preformed parties.</value>\n        /// <seealso cref=\"PlayersPerParty\"/>\n        /// <seealso cref=\"TanksPerParty\"/>\n        /// <seealso cref=\"HealersPerParty\"/>\n        /// <seealso cref=\"MeleesPerParty\"/>\n        /// <seealso cref=\"RangedPerParty\"/>\n        public bool IsForcedPartyComposition { get { return AsBoolean(\"ForcePartySetup\"); } }\n\n        /// <summary>\n        /// Gets the description of the current content.\n        /// </summary>\n        /// <value>The description of the current content.</value>\n        public override Text.XivString Description { get { throw new InvalidOperationException(\"Use ContentFinderCondition.Description\"); } }\n\n        /// <summary>\n        /// Gets the numeric order of the current content.\n        /// </summary>\n        /// <value>The numeric order of the current content.</value>\n        public int SortKey { get { return AsInt32(\"SortKey\"); } }\n\n        /// <summary>\n        /// Gets a value indicating whether a preformed alliance of parties can register for the current content.\n        /// </summary>\n        /// <value>A value indicating whether a preformed alliance of parties can register for the current content.</value>\n        public bool AllowAllianceRegistration { get { return AsBoolean(\"AllowAllianceRegistration\"); } }\n\n        /// <summary>\n        /// Gets the bonus to Allagan Tomestones of Soldiery when completing the current content with a character new to it in the party.\n        /// </summary>\n        /// <value>The bonus to Allagan Tomestones of Soldiery when completing the current content with a character new to it in the party.</value>\n        public int NewPlayerBonus { get { return AsInt32(\"NewPlayerBonus\"); } }\n\n        /// <summary>\n        /// Gets the rewards received over the course of the duty.\n        /// </summary>\n        /// <value>The rewards received over the course of the duty.</value>\n        public override IEnumerable<IContentReward> FixedRewards {\n            get {\n                // XXX: Magic numbers here\n                const int TomestoneAKey = 3;    // Poetics\n                const int TomestoneBKey = 8;    // Verity\n                const int CurrencyCount = 5;\n\n                var tomestones = Sheet.Collection.GetSheet<TomestonesItem>();\n\n                var tomeA = tomestones[TomestoneAKey].Item;\n                var tomeB = tomestones[TomestoneBKey].Item;\n\n                var sumA = 0;\n                var sumB = 0;\n                for (var i = 0; i < CurrencyCount; ++i) {\n                    sumA += AsInt32(\"BossCurrencyA\", i);\n                    sumB += AsInt32(\"BossCurrencyB\", i);\n                }\n\n                if (sumA != 0)\n                    yield return new ContentReward(tomeA, sumA);\n                if (sumB != 0)\n                    yield return new ContentReward(tomeB, sumB);\n            }\n        }\n\n        public InstanceContentData Data { get { return _Data ?? (_Data = new InstanceContentData(this)); } }\n\n        public BNpcBase Boss { get { return As<BNpcBase>(\"BNpcBase{Boss}\"); } }\n\n        #endregion\n\n        #region Constructors\n\n        /// <summary>\n        /// Initializes a new instance of the <see cref=\"InstanceContent\"/> class.\n        /// </summary>\n        /// <param name=\"sheet\"><see cref=\"IXivSheet\"/> containing this object.</param>\n        /// <param name=\"sourceRow\"><see cref=\"IRelationalRow\"/> to read data from.</param>\n        public InstanceContent(IXivSheet sheet, IRelationalRow sourceRow) : base(sheet, sourceRow) { }\n\n        #endregion\n\n        #region IItemSource Members\n\n        private Item[] _ItemSourceItems;\n\n        IEnumerable<Item> IItemSource.Items {\n            get {\n                if (_ItemSourceItems != null)\n                    return _ItemSourceItems;\n\n                var items = new List<Item>();\n\n                if (FixedRewards != null) {\n                    foreach (var item in FixedRewards) items.Add(item.Item);\n                }\n\n                if (Data.MidBosses != null) {\n                    foreach (var boss in Data.MidBosses) {\n                        if (boss.RewardItems != null)\n                            foreach (var item in boss.RewardItems) items.Add(item.Item);\n                    }\n                }\n                if (Data.Boss != null && Data.Boss.RewardItems != null) {\n                    foreach (var item in Data.Boss.RewardItems) items.Add(item.Item);\n                }\n                if (Data.MapTreasures != null) {\n                    foreach (var coffer in Data.MapTreasures)\n                        foreach (var item in coffer.Items) items.Add(item);\n                }\n\n                return _ItemSourceItems = items.ToArray();\n            }\n        }\n\n        #endregion\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Xiv/InstanceContentData.Fight.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nusing Newtonsoft.Json;\n\nnamespace SaintCoinach.Xiv {\n    partial class InstanceContentData {\n        public class Fight {\n            #region Properties\n            public IEnumerable<RewardItem> RewardItems { get; private set; }\n            public IEnumerable<BNpc> PrimaryBNpcs { get; private set; }\n            public IEnumerable<BNpc> SecondaryBNpcs { get; private set; }\n            public IEnumerable<Treasure> Treasures { get; private set; }\n\n            public int CurrencyA { get; private set; }\n            public int CurrencyB { get; private set; }\n            public int CurrencyC { get; private set; }\n            #endregion\n\n            #region Constructor\n            internal Fight(JsonReader reader, XivCollection collection) {\n                if (reader.TokenType != JsonToken.StartObject) throw new InvalidOperationException();\n\n                var bnpcs = collection.BNpcs;\n\n                while (reader.Read() && reader.TokenType != JsonToken.EndObject) {\n                    if (reader.TokenType != JsonToken.PropertyName) throw new InvalidOperationException();\n\n                    switch (reader.Value.ToString()) {\n                        case \"RewardItems\":\n                            ReadRewardItems(reader, collection);\n                            break;\n                        case \"ClearB\":\n                            ReadCurrencyB(reader);\n                            break;\n                        case \"SubBNpcNames\":\n                            ReadSecondaryBNpcs(reader, bnpcs);\n                            break;\n                        case \"ClearA\":\n                            ReadCurrencyA(reader);\n                            break;\n                        case \"Treasure\":\n                            ReadTreasure(reader, collection);\n                            break;\n                        case \"BNpcNames\":\n                            ReadPrimaryBNpcs(reader, bnpcs);\n                            break;\n                        case \"ClearC\":\n                            ReadCurrencyC(reader);\n                            break;\n                        default:\n                            Console.Error.WriteLine(\"Unknown 'InstanceContent.Fight' data key: {0}\", reader.Value);\n                            throw new NotSupportedException();\n                    }\n                }\n            }\n            #endregion\n\n            #region Read\n            private void ReadRewardItems(JsonReader reader, XivCollection collection) {\n                if (!reader.Read() || reader.TokenType != JsonToken.StartArray) throw new InvalidOperationException();\n\n                var values = new List<RewardItem>();\n                while (reader.Read() && reader.TokenType != JsonToken.EndArray) {\n                    values.Add(new RewardItem(reader, collection));\n                }\n                this.RewardItems = values;\n            }\n            private void ReadCurrencyA(JsonReader reader) {\n                if (!reader.Read() || reader.TokenType != JsonToken.Integer) throw new InvalidOperationException();\n                this.CurrencyA = Convert.ToInt32(reader.Value);\n            }\n            private void ReadCurrencyB(JsonReader reader) {\n                if (!reader.Read() || reader.TokenType != JsonToken.Integer) throw new InvalidOperationException();\n                this.CurrencyB = Convert.ToInt32(reader.Value);\n            }\n            private void ReadCurrencyC(JsonReader reader) {\n                if (!reader.Read() || reader.TokenType != JsonToken.Integer) throw new InvalidOperationException();\n                this.CurrencyC = Convert.ToInt32(reader.Value);\n            }\n            private void ReadSecondaryBNpcs(JsonReader reader, Collections.BNpcCollection bnpcs) {\n                if (!reader.Read() || reader.TokenType != JsonToken.StartArray) throw new InvalidOperationException();\n\n                var values = new List<BNpc>();\n                while (reader.Read() && reader.TokenType != JsonToken.EndArray) {\n                    if (reader.TokenType != JsonToken.Integer) throw new InvalidOperationException();\n\n                    var key = Convert.ToInt64(reader.Value);\n                    values.Add(bnpcs[key]);\n                }\n                this.SecondaryBNpcs = values;\n            }\n            private void ReadPrimaryBNpcs(JsonReader reader, Collections.BNpcCollection bnpcs) {\n                if (!reader.Read() || reader.TokenType != JsonToken.StartArray) throw new InvalidOperationException();\n\n                var values = new List<BNpc>();\n                while (reader.Read() && reader.TokenType != JsonToken.EndArray) {\n                    if (reader.TokenType != JsonToken.Integer) throw new InvalidOperationException();\n\n                    var key = Convert.ToInt64(reader.Value);\n                    values.Add(bnpcs[key]);\n                }\n                this.PrimaryBNpcs = values;\n            }\n            private void ReadTreasure(JsonReader reader, XivCollection collection) {\n                if (!reader.Read() || reader.TokenType != JsonToken.StartArray) throw new InvalidOperationException();\n\n                var values = new List<Treasure>();\n                while (reader.Read() && reader.TokenType != JsonToken.EndArray) {\n                    values.Add(new Treasure(reader, collection));\n                }\n                this.Treasures = values;\n            }\n            #endregion\n        }\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Xiv/InstanceContentData.RewardItem.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nusing Newtonsoft.Json;\n\nnamespace SaintCoinach.Xiv {\n    partial class InstanceContentData {\n        public class RewardItem {\n            #region Properties\n            public Item Item { get; private set; }\n            public bool HasRateCondition { get; private set; }\n            public Quest RequiredQuest { get; private set; }\n            public bool HasWeekRestriction { get; private set; }\n            #endregion\n\n            #region Constructor\n            internal RewardItem(JsonReader reader, XivCollection collection) {\n                if (reader.TokenType != JsonToken.StartObject) throw new InvalidOperationException();\n\n                var allItems = collection.GetSheet<Item>();\n                var allQuests = collection.GetSheet<Quest>();\n                while (reader.Read() && reader.TokenType != JsonToken.EndObject) {\n                    if (reader.TokenType != JsonToken.PropertyName) throw new InvalidOperationException();\n\n                    switch (reader.Value.ToString()) {\n                        case \"has_rate_condition\":\n                            this.HasRateCondition = ReadCondition(reader);\n                            break;\n                        case \"Item\":\n                            ReadItem(reader, allItems);\n                            break;\n                        case \"Quest\":\n                            ReadRequiredQuest(reader, allQuests);\n                            break;\n                        case \"is_week_restriction_one\":\n                            this.HasWeekRestriction = ReadCondition(reader);\n                            break;\n                        default:\n                            Console.Error.WriteLine(\"Unknown 'InstanceContent.RewardItem' data key: {0}\", reader.Value);\n                            throw new NotSupportedException();\n                    }\n                }\n            }\n            #endregion\n\n            #region Read\n            private bool ReadCondition(JsonReader reader) {\n                if (!reader.Read() || reader.TokenType != JsonToken.Integer) throw new InvalidOperationException();\n                var r = Convert.ToInt32(reader.Value);\n                return r != 0;\n            }\n            private void ReadItem(JsonReader reader, IXivSheet<Item> allItems) {\n                if (!reader.Read()) throw new InvalidOperationException();\n\n                var key = Convert.ToInt32(reader.Value);\n                this.Item = allItems[key];\n            }\n            private void ReadRequiredQuest(JsonReader reader, IXivSheet<Quest> allQuests) {\n                if (!reader.Read()) throw new InvalidOperationException();\n\n                var key = Convert.ToInt32(reader.Value);\n                this.RequiredQuest = allQuests[key];\n            }\n            #endregion\n        }\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Xiv/InstanceContentData.Treasure.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Globalization;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nusing Newtonsoft.Json;\n\nnamespace SaintCoinach.Xiv {\n    partial class InstanceContentData {\n        public class Treasure : IItemSource {\n            #region Properties\n            public IEnumerable<Item> Items { get; private set; }\n            public bool HasWeeklyRestriction { get; private set; }\n            public System.Drawing.PointF? Coordinates { get; private set; }\n            #endregion\n\n            #region Constructor\n            internal Treasure(JsonReader reader, XivCollection collection) {\n                if (reader.TokenType != JsonToken.StartObject) throw new InvalidOperationException();\n\n                var allItems = collection.GetSheet<Item>();\n\n                while (reader.Read() && reader.TokenType != JsonToken.EndObject) {\n                    if (reader.TokenType != JsonToken.PropertyName) throw new InvalidOperationException();\n\n                    switch (reader.Value.ToString()) {\n                        case \"Currency\":\n                            ReadCurrency(reader);\n                            break;\n                        case \"Item\":\n                            ReadItems(reader, allItems);\n                            break;\n                        case \"WeekRestrictionIndex\":\n                            ReadWeeklyRestriction(reader);\n                            break;\n                        case \"coordinate\":\n                            ReadCoordinates(reader);\n                            break;\n                        default:\n                            Console.Error.WriteLine(\"Unknown 'InstanceContent.Treasure' data key: {0}\", reader.Value);\n                            throw new NotSupportedException();\n                    }\n                }\n            }\n            #endregion\n\n            #region Read\n            private void ReadCurrency(JsonReader reader) {\n                if (!reader.Read() || reader.TokenType != JsonToken.StartArray) throw new InvalidOperationException();\n                if (!reader.Read() || reader.TokenType != JsonToken.EndArray) throw new InvalidOperationException();\n            }\n            private void ReadItems(JsonReader reader, IXivSheet<Item> allItems) {\n                if (!reader.Read() || reader.TokenType != JsonToken.StartArray) throw new InvalidOperationException();\n\n                var items = new List<Item>();\n                while (reader.Read() && reader.TokenType != JsonToken.EndArray) {\n                    if (reader.TokenType != JsonToken.Integer) throw new InvalidOperationException();\n\n                    var key = Convert.ToInt32(reader.Value);\n                    items.Add(allItems[key]);\n                }\n                this.Items = items;\n            }\n            private void ReadWeeklyRestriction(JsonReader reader) {\n                if (!reader.Read() || reader.TokenType != JsonToken.Integer) throw new InvalidOperationException();\n                var r = Convert.ToInt32(reader.Value);\n                this.HasWeeklyRestriction = (r >= 0);\n            }\n            private void ReadCoordinates(JsonReader reader) {\n                if (!reader.Read() || reader.TokenType != JsonToken.StartArray) throw new InvalidOperationException();\n\n                if (!reader.Read() || reader.TokenType != JsonToken.String) throw new InvalidOperationException();\n                var x = float.Parse((string)reader.Value, CultureInfo.InvariantCulture);\n                if (!reader.Read() || reader.TokenType != JsonToken.String) throw new InvalidOperationException();\n                var y = float.Parse((string)reader.Value, CultureInfo.InvariantCulture);\n\n                if (x == 0 && y == 0)\n                    this.Coordinates = null;\n                else\n                    this.Coordinates = new System.Drawing.PointF(x, y);\n\n                if (!reader.Read() || reader.TokenType != JsonToken.EndArray) throw new InvalidOperationException();\n            }\n            #endregion\n        }\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Xiv/InstanceContentData.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nusing Newtonsoft.Json;\n\nnamespace SaintCoinach.Xiv {\n    public partial class InstanceContentData : IItemSource {\n        #region Properties\n\n        public InstanceContent InstanceContent { get; private set; }\n        public IEnumerable<Fight> AllBosses { get; private set; }\n        public Fight Boss { get; private set; }\n        public IEnumerable<Fight> MidBosses { get; private set; }\n        public IEnumerable<Treasure> MapTreasures { get; private set; }\n\n        #endregion\n\n        #region Constructor\n\n        public InstanceContentData(InstanceContent instanceContent) {\n            this.InstanceContent = instanceContent ?? throw new ArgumentNullException(\"instanceContent\");\n\n            var coll = instanceContent.Sheet.Collection;\n            if (!coll.IsLibraAvailable) return;\n\n            var libraRow = coll.Libra.InstanceContents.FirstOrDefault(i => i.Key == instanceContent.Key);\n            if (libraRow != null)\n                Parse(libraRow);\n        }\n        #endregion\n\n        #region Parse\n        private void Parse(Libra.InstanceContent libraRow) {\n            var json = Encoding.UTF8.GetString(libraRow.data);\n            using (var strReader = new System.IO.StringReader(json)) {\n                using (var r = new JsonTextReader(strReader)) {\n                    if (!r.Read() || r.TokenType != JsonToken.StartObject) throw new InvalidOperationException();\n\n                    while (r.Read() && r.TokenType != JsonToken.EndObject) {\n                        if (r.TokenType != JsonToken.PropertyName) throw new InvalidOperationException();\n\n                        switch (r.Value.ToString()) {\n                            case \"Boss\":\n                                ReadBoss(r);\n                                break;\n                            case \"MiddleBoss\":\n                                ReadMidBosses(r);\n                                break;\n                            case \"Map\":\n                                ReadMapTreasures(r);\n                                break;\n                            default:\n                                Console.Error.WriteLine(\"Unknown 'InstanceContent' data key: {0}\", r.Value);\n                                throw new NotSupportedException();\n                        }\n                    }\n                }\n            }\n            var allBosses = new List<Fight>();\n            if (MidBosses != null)\n                allBosses.AddRange(MidBosses);\n            if (Boss != null)\n                allBosses.Add(Boss);\n            this.AllBosses = allBosses;\n        }\n\n        private void ReadBoss(JsonReader reader) {\n            if (!reader.Read() || reader.TokenType != JsonToken.StartObject) throw new InvalidOperationException();\n\n            this.Boss = new Fight(reader, InstanceContent.Sheet.Collection);\n        }\n        private void ReadMidBosses(JsonReader reader) {\n            if (!reader.Read() || reader.TokenType != JsonToken.StartArray) throw new InvalidOperationException();\n\n            var values = new List<Fight>();\n            while (reader.Read() && reader.TokenType != JsonToken.EndArray) {\n                if (reader.TokenType != JsonToken.StartObject) throw new InvalidOperationException();\n\n                values.Add(new Fight(reader, InstanceContent.Sheet.Collection));\n            }\n            this.MidBosses = values;\n        }\n        private void ReadMapTreasures(JsonReader reader) {\n            if (!reader.Read() || reader.TokenType != JsonToken.StartArray) throw new InvalidOperationException();\n\n            var values = new List<Treasure>();\n            while (reader.Read() && reader.TokenType != JsonToken.EndArray) {\n                if (reader.TokenType != JsonToken.StartObject) throw new InvalidOperationException();\n\n                values.Add(new Treasure(reader, InstanceContent.Sheet.Collection));\n            }\n            this.MapTreasures = values;\n        }\n        #endregion\n\n        #region IItemSource Members\n\n        private Item[] _ItemSourceItems;\n        IEnumerable<Item> IItemSource.Items {\n            get {\n                if (_ItemSourceItems != null)\n                    return _ItemSourceItems;\n\n                IEnumerable<Item> v = new Item[0];\n\n                if (Boss != null) {\n                    v = v.Concat(Boss.RewardItems.Select(i => i.Item));\n                    v = v.Concat(Boss.Treasures.SelectMany(i => i.Items));\n                }\n                if (MidBosses != null) {\n                    v = v.Concat(MidBosses.SelectMany(f => f.RewardItems.Select(i => i.Item)));\n                    v = v.Concat(MidBosses.SelectMany(f => f.Treasures.SelectMany(i => i.Items)));\n                }\n                if (MapTreasures != null)\n                    v.Concat(MapTreasures.SelectMany(i => i.Items));\n\n                return _ItemSourceItems = v.Distinct().ToArray();\n            }\n        }\n\n        #endregion\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Xiv/Item.cs",
    "content": "using System;\nusing System.Collections.Generic;\nusing System.Linq;\n\nusing SaintCoinach.Ex.Relational;\n\nnamespace SaintCoinach.Xiv {\n    /// <summary>\n    ///     Class representing items that are in the default inventory.\n    /// </summary>\n    public class Item : ItemBase {\n        #region Fields\n\n        /// <summary>\n        ///     Sources from which to obtain the current item.\n        /// </summary>\n        private IItemSource[] _Sources;\n\n        /// <summary>\n        ///     Listings offering the current item as a reward.\n        /// </summary>\n        private IShopListing[] _AsShopItems;\n\n        /// <summary>\n        ///     Listings requiring the current item as payment.\n        /// </summary>\n        private IShopListingItem[] _AsShopPayment;\n\n        /// <summary>\n        ///     Recipes using the current item as material.\n        /// </summary>\n        private Recipe[] _RecipesAsMaterial;\n\n        /// <summary>\n        ///     Recipes creating the current item.\n        /// </summary>\n        private Recipe[] _RecipesAsResult;\n\n        #endregion\n\n        #region Properties\n\n        /// <summary>\n        ///     Gets the <see cref=\"ItemLevel\" /> of the current item.\n        /// </summary>\n        /// <value>The <see cref=\"ItemLevel\" /> of the current item.</value>\n        public ItemLevel ItemLevel { get { return As<ItemLevel>(\"Level{Item}\"); } }\n\n        /// <summary>\n        ///     Gets a value indicating whether the current item is unique.\n        /// </summary>\n        /// <value>A value indicating whether the current item is unique.</value>\n        public bool IsUnique { get { return AsBoolean(\"IsUnique\"); } }\n\n        /// <summary>\n        ///     Gets a value indicating whether the current item is untradable.\n        /// </summary>\n        /// <value>A value indicating whether the current item is untradable.</value>\n        public bool IsUntradable { get { return AsBoolean(\"IsUntradable\"); } }\n\n        /// <summary>\n        ///     Gets a value indicating whether the current item is indisposable.\n        /// </summary>\n        /// <remarks>If <c>true</c> the current item can not be disposed of or sold to NPCs.</remarks>\n        /// <value>A value indicating whether the current item is indisposable.</value>\n        public bool IsIndisposable { get { return AsBoolean(\"IsIndisposable\"); } }\n\n        /// <summary>\n        ///     Gets a value indicating whether the current item is dyeable.\n        /// </summary>\n        /// <value>A value indicating whether the current item is dyeable.</value>\n        public bool IsDyeable { get { return AsInt16(\"DyeCount\") > 0; } }\n\n        /// <summary>\n        /// Gets a value indicating whether the current item is collectable.\n        /// </summary>\n        /// <value>A value indicaating whether the current item is collectable.</value>\n        public bool IsCollectable { get { return AsBoolean(\"IsCollectable\"); } }\n\n        /// <summary>\n        ///     Gets a value indicating whether the current item can have materia melded with advanced melding.\n        /// </summary>\n        /// <value>A value indicating whether the current item can have materia melded with advanced melding.</value>\n        public bool IsAdvancedMeldingPermitted { get { return AsBoolean(\"IsAdvancedMeldingPermitted\"); } }\n\n        /// <summary>\n        ///     Gets the additional data row associated with the current item.\n        /// </summary>\n        /// <value>The additional data row associated with the current item.</value>\n        public IXivRow AdditionalData => As<IXivRow>(\"AdditionalData\");\n\n        /// <summary>\n        ///     Gets the <see cref=\"EquipSlotCategory\" /> of the current item.\n        /// </summary>\n        /// <value>The <see cref=\"EquipSlotCategory\" /> of the current item.</value>\n        public EquipSlotCategory EquipSlotCategory { get { return As<EquipSlotCategory>(); } }\n\n        // ReSharper disable once InconsistentNaming\n        /// <summary>\n        ///     Gets the <see cref=\"ItemUICategory\" /> of the current item.\n        /// </summary>\n        /// <value>The <see cref=\"ItemUICategory\" /> of the current item.</value>\n        public ItemUICategory ItemUICategory { get { return As<ItemUICategory>(); } }\n\n        /// <summary>\n        ///     Gets the <see cref=\"ItemSearchCategory\" /> of the current item.\n        /// </summary>\n        /// <value>The <see cref=\"ItemSearchCategory\" /> of the current item.</value>\n        public ItemSearchCategory ItemSearchCategory { get { return As<ItemSearchCategory>(); } }\n\n        /// <summary>\n        ///     Gets the <see cref=\"ItemAction\" /> invoked when the current item is used.\n        /// </summary>\n        /// <value>The <see cref=\"ItemAction\" /> invoked when the current item is used.</value>\n        public ItemAction ItemAction { get { return As<ItemAction>(); } }\n\n        /// <summary>\n        ///     Gets the price NPCs offer when selling the current item.\n        /// </summary>\n        /// <value>The price NPCs offer when selling the current item.</value>\n        public int Bid { get { return AsInt32(\"Price{Low}\"); } }\n\n        /// <summary>\n        ///     Gets the price in Gil of the current item in shops.\n        /// </summary>\n        /// <value>The price in Gil of the current item in shops.</value>\n        public int Ask { get { return AsInt32(\"Price{Mid}\"); } }\n\n        /// <summary>\n        ///     Gets the recipes using the current item as material.\n        /// </summary>\n        /// <remarks>\n        ///     This property does not include recipes using the current item as catalyst (crystals).\n        /// </remarks>\n        /// <value>The recipes using the current item as material.</value>\n        public IEnumerable<Recipe> RecipesAsMaterial {\n            get { return _RecipesAsMaterial ?? (_RecipesAsMaterial = BuildRecipesAsMaterial()); }\n        }\n\n        /// <summary>\n        ///     Gets the recipes creating the current item.\n        /// </summary>\n        /// <value>The recipes creating the current item.</value>\n        public IEnumerable<Recipe> RecipesAsResult {\n            get { return _RecipesAsResult ?? (_RecipesAsResult = BuildRecipesAsResult()); }\n        }\n\n        /// <summary>\n        ///     Gets the shop listings offering the current item as reward.\n        /// </summary>\n        /// <value>The shop listings offering the current item as reward.</value>\n        public IEnumerable<IShopListing> AsShopItems {\n            get { return _AsShopItems ?? (_AsShopItems = BuildAsShopItems()); }\n        }\n\n        /// <summary>\n        ///     Gets the shop listings requiring the current item as payment.\n        /// </summary>\n        /// <value>The shop listings requiring the current item as payment.</value>\n        public IEnumerable<IShopListingItem> AsShopPayment {\n            get { return _AsShopPayment ?? (_AsShopPayment = BuildAsShopPayment()); }\n        }\n\n        /// <summary>\n        ///     Gets the sources from which to obtain the current item.\n        /// </summary>\n        /// <value>The sources from which to obtain the current item.</value>\n        public IEnumerable<IItemSource> Sources {\n            get { return _Sources ?? (_Sources = BuildSources()); }\n        }\n\n        /// <summary>\n        ///    Gets the rarity of the current item.\n        /// </summary>\n        /// <value>The rarity of the current item.</value>\n        /// <remarks>\n        /// 1: Common (White)\n        /// 2: Uncommon (Green)\n        /// 3: Rare (Blue)\n        /// 4: Relic (Purple)\n        /// 7: Aetherial (Pink)\n        /// </remarks>\n        public int Rarity {\n            get { return AsInt32(\"Rarity\"); }\n        }\n\n        /// <summary>\n        /// Gets a flag indicating whether HQ versions of the current item exist.\n        /// </summary>\n        /// <value>A flag indicating whether HQ versions of the current item exist.</value>\n        public bool CanBeHq {\n            get { return AsBoolean(\"CanBeHq\"); }\n        }\n\n        public GrandCompany GrandCompany {\n            get { return As<GrandCompany>(); }\n        }\n\n        public Salvage Salvage {\n            get { return As<Salvage>(); }\n        }\n\n        public bool IsAetherialReducible {\n            get { return AsInt32(\"AetherialReduce\") > 0; }\n        }\n\n        public bool IsConvertable {\n            get { return As<byte>(\"MaterializeType\") > 0; }\n        }\n\n        public bool IsGlamourous => AsBoolean(\"IsGlamourous\");\n\n        public Quad ModelMain { get { return AsQuad(\"Model{Main}\"); } }\n\n        public Quad ModelSub { get { return AsQuad(\"Model{Sub}\"); } }\n\n        /// <summary>\n        ///     Gets the <see cref=\"ClassJob\" /> required to repair or desynth the current item.\n        /// </summary>\n        /// <value>The <see cref=\"ClassJob\" /> required to repair or desynth the current item.</value>\n        public ClassJob RepairClassJob => As<ClassJob>(\"ClassJob{Repair}\");\n        #endregion\n\n        #region Constructors\n\n        /// <summary>\n        ///     Initializes a new instance of the <see cref=\"Item\" /> class.\n        /// </summary>\n        /// <param name=\"sheet\"><see cref=\"IXivSheet\" /> containing this object.</param>\n        /// <param name=\"sourceRow\"><see cref=\"IRelationalRow\" /> to read data from.</param>\n        public Item(IXivSheet sheet, IRelationalRow sourceRow) : base(sheet, sourceRow) { }\n\n        #endregion\n\n        #region Build\n\n        /// <summary>\n        ///     Build an array of recipes using the current item as material.\n        /// </summary>\n        /// <returns>An array of recipes using the current item as material.</returns>\n        private Recipe[] BuildRecipesAsMaterial() {\n            var rSheet = Sheet.Collection.GetSheet<Recipe>();\n\n            return\n                rSheet.Where(\n                             recipe =>\n                             recipe.Ingredients.Any(ing => ing.Type == RecipeIngredientType.Material && ing.Item == this))\n                      .ToArray();\n        }\n\n        /// <summary>\n        ///     Build an array of recipes creating the current item.\n        /// </summary>\n        /// <returns>An array of recipes creating the current item.</returns>\n        private Recipe[] BuildRecipesAsResult() {\n            var rSheet = Sheet.Collection.GetSheet<Recipe>();\n\n            return rSheet.Where(recipe => recipe.ResultItem == this).ToArray();\n        }\n\n        /// <summary>\n        ///     Build an array of shop listings offering the current item as reward.\n        /// </summary>\n        /// <returns>An array of shop listings offering the current item as reward.</returns>\n        private IShopListing[] BuildAsShopItems() {\n            var shops = Sheet.Collection.Shops;\n\n            var shopItems = new List<IShopListing>();\n            foreach (var shop in shops)\n                shopItems.AddRange(shop.ShopListings.Where(l => l.Rewards.Any(li => li.Item == this)));\n            return shopItems.Distinct().ToArray();\n        }\n\n        /// <summary>\n        ///     Build an array of shop listings requiring the current item as payment.\n        /// </summary>\n        /// <returns>An array of shop listings requiring the current item as payment.</returns>\n        private IShopListingItem[] BuildAsShopPayment() {\n            if (Key == 1)\n                return new IShopListingItem[0]; // XXX: DO NOT BUILD THIS FOR GIL, THAT WOULD BE BAD.\n\n            var shops = Sheet.Collection.Shops;\n\n            var checkedItems = new List<IShopListing>();\n            var shopItemCosts = new List<IShopListingItem>();\n            foreach (var item in shops.SelectMany(shop => shop.ShopListings.Except(checkedItems).ToArray())) {\n                shopItemCosts.AddRange(item.Costs.Where(_ => _.Item == this));\n                checkedItems.Add(item);\n            }\n            return shopItemCosts.Distinct().ToArray();\n        }\n\n        /// <summary>\n        ///     Build an array of sources from which to obtain the current item.\n        /// </summary>\n        /// <returns>An array of sources from which to obtain the current item.</returns>\n        private IItemSource[] BuildSources() {\n            var sources = new List<IItemSource>();\n\n            Libra.Item libraRow = null;\n            if (Sheet.Collection.IsLibraAvailable)\n                libraRow = Sheet.Collection.Libra.Items.FirstOrDefault(i => i.Key == this.Key);\n\n            var recipes = Sheet.Collection.GetSheet<Recipe>();\n            var quests = Sheet.Collection.GetSheet<Quest>();\n            var achievements = Sheet.Collection.GetSheet<Achievement>();\n            var shops = Sheet.Collection.Shops;\n            var leves = Sheet.Collection.GetSheet<Leve>();\n            var fishingSpots = Sheet.Collection.GetSheet<FishingSpot>();\n            var retainerTasks = Sheet.Collection.GetSheet<RetainerTask>();\n            var companyCraft = Sheet.Collection.GetSheet<CompanyCraftSequence>();\n\n            if (libraRow != null) {\n                var bnpcColl = Sheet.Collection.BNpcs;\n                var instanceContents = Sheet.Collection.GetSheet<InstanceContent>();\n\n                foreach (var bnpc in libraRow.BNpcs)\n                    sources.Add(bnpcColl[bnpc]);\n                foreach (var ic in libraRow.InstanceContents)\n                    sources.Add(instanceContents[ic]);\n            }\n\n            /*sources.AddRange(bnpcColl.Where(i => i.Items.Contains(this)));\n            sources.AddRange(instanceContents.Cast<IItemSource>().Where(i => i.Items.Contains(this)));*/\n\n            // Not using Libra for these because it has a higher likelyhood of being incomplete.\n            sources.AddRange(recipes.Where(i => i.ResultItem == this));\n            sources.AddRange(quests.Cast<IItemSource>().Where(i => i.Items.Contains(this)));\n            sources.AddRange(achievements.Where(i => i.Item == this));\n            sources.AddRange(shops.Where(i => i.Items.Contains(this)));\n\n            // Not using Libra for this because it doesn't even have information about it\n            sources.AddRange(leves.Where(i => i.Items.Contains(this)));\n            sources.AddRange(fishingSpots.Where(i => i.Items.Contains(this)));\n            sources.AddRange(retainerTasks.Where(i => i.Items.Contains(this)));\n            sources.AddRange(companyCraft.Where(i => i.ResultItem == this));\n\n            return sources.ToArray();\n        }\n\n        #endregion\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Xiv/ItemAction.cs",
    "content": "using SaintCoinach.Ex.Relational;\n\nnamespace SaintCoinach.Xiv {\n    /// <summary>\n    ///     Class representing actions invoked when items are used.\n    /// </summary>\n    public class ItemAction : XivRow {\n        #region Static\n\n        /// <summary>\n        ///     The number of data fields present.\n        /// </summary>\n        /// <remarks>\n        ///     This value indicates how many fields are for NQ and HQ data each.\n        /// </remarks>\n        public const int DataCount = 8;\n\n        #endregion\n\n        #region Properties\n\n        /// <summary>\n        ///     Gets the type of the current item action.\n        /// </summary>\n        /// <value>The type of the current item action.</value>\n        public int Type { get { return AsInt32(\"Type\"); } }\n\n        #endregion\n\n        #region Constructors\n\n        /// <summary>\n        ///     Initializes a new instance of the <see cref=\"ItemAction\" /> class.\n        /// </summary>\n        /// <param name=\"sheet\"><see cref=\"IXivSheet\" /> containing this object.</param>\n        /// <param name=\"sourceRow\"><see cref=\"IRelationalRow\" /> to read data from.</param>\n        public ItemAction(IXivSheet sheet, IRelationalRow sourceRow) : base(sheet, sourceRow) { }\n\n        #endregion\n\n        #region Helpers\n\n        /// <summary>\n        ///     Get the integer value at a specified data field for NQ items.\n        /// </summary>\n        /// <param name=\"index\">Index of the data field to get.</param>\n        /// <returns>The integer value at a specified data field for NQ items.</returns>\n        public int GetData(int index) {\n            return AsInt32(\"Data\", index);\n        }\n\n        /// <summary>\n        ///     Get the integer value at a specified data field for HQ items.\n        /// </summary>\n        /// <param name=\"index\">Index of the data field to get.</param>\n        /// <returns>The integer value at a specified data field for HQ items.</returns>\n        public int GetHqData(int index) {\n            return AsInt32(\"Data{HQ}\", index);\n        }\n\n        #endregion\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Xiv/ItemActions/AchievementScroll.cs",
    "content": "﻿using SaintCoinach.Ex.Relational;\nusing System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace SaintCoinach.Xiv.ItemActions {\n    public class AchievementScroll : ItemAction {\n        // Used to get a certain achievement, such as Saint of Firmament.\n\n        const int AchievementKey = 0;\n\n        public AchievementScroll(IXivSheet sheet, IRelationalRow sourceRow) : base(sheet, sourceRow) { }\n\n        public Achievement Achievement {\n            get {\n                return Sheet.Collection.GetSheet<Achievement>()[GetData(AchievementKey)];\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Xiv/ItemActions/AdventureBook.cs",
    "content": "﻿using SaintCoinach.Ex.Relational;\nusing System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace SaintCoinach.Xiv.ItemActions {\n    public class AdventureBook : ItemAction {\n        // Used to skip parts of the story or level a class.\n\n        public AdventureBook(IXivSheet sheet, IRelationalRow sourceRow) : base(sheet, sourceRow) { }\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Xiv/ItemActions/AetherytePendulum.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace SaintCoinach.Xiv.ItemActions {\n    public class AetherytePendulum : ItemAction {\n        #region Constructors\n\n        public AetherytePendulum(IXivSheet sheet, Ex.Relational.IRelationalRow sourceRow) : base(sheet, sourceRow) { }\n\n        #endregion\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Xiv/ItemActions/AttributeReset.cs",
    "content": "﻿using SaintCoinach.Ex.Relational;\n\nnamespace SaintCoinach.Xiv.ItemActions {\n    public class AttributeReset : ItemAction {\n        #region Constructors\n\n        #region Constructor\n\n        public AttributeReset(IXivSheet sheet, IRelationalRow sourceRow) : base(sheet, sourceRow) { }\n\n        #endregion\n\n        #endregion\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Xiv/ItemActions/BuddyEquipUnlock.cs",
    "content": "using SaintCoinach.Ex.Relational;\n\nnamespace SaintCoinach.Xiv.ItemActions {\n    public class BuddyEquipUnlock : ItemAction {\n        #region Static\n\n        private const int BuddyEquipIndex = 0;\n\n        #endregion\n\n        #region Properties\n\n        public BuddyEquip BuddyEquip {\n            get {\n                var key = GetData(BuddyEquipIndex);\n                return Sheet.Collection.GetSheet<BuddyEquip>()[key];\n            }\n        }\n\n        #endregion\n\n        #region Constructors\n\n        #region Constructor\n\n        public BuddyEquipUnlock(IXivSheet sheet, IRelationalRow sourceRow) : base(sheet, sourceRow) { }\n\n        #endregion\n\n        #endregion\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Xiv/ItemActions/BuddySummon.cs",
    "content": "using SaintCoinach.Ex.Relational;\n\nnamespace SaintCoinach.Xiv.ItemActions {\n    public class BuddySummon : ItemAction {\n        #region Static\n\n        private const int MountKey = 0;\n\n        #endregion\n\n        #region Properties\n\n        public Mount Mount {\n            get {\n                var key = GetData(MountKey);\n                return Sheet.Collection.GetSheet<Mount>()[key];\n            }\n        }\n\n        #endregion\n\n        #region Constructors\n\n        #region Constructor\n\n        public BuddySummon(IXivSheet sheet, IRelationalRow sourceRow) : base(sheet, sourceRow) { }\n\n        #endregion\n\n        #endregion\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Xiv/ItemActions/ChocoboActionReset.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace SaintCoinach.Xiv.ItemActions {\n    public class ChocoboActionReset : ItemAction {\n        #region Constructors\n\n        public ChocoboActionReset(IXivSheet sheet, Ex.Relational.IRelationalRow sourceRow) : base(sheet, sourceRow) { }\n\n        #endregion\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Xiv/ItemActions/ChocoboFeed.cs",
    "content": "using SaintCoinach.Ex.Relational;\n\nnamespace SaintCoinach.Xiv.ItemActions {\n    public class ChocoboFeed : ItemAction {\n        #region Constructors\n\n        public ChocoboFeed(IXivSheet sheet, IRelationalRow sourceRow) : base(sheet, sourceRow) { }\n\n        #endregion\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Xiv/ItemActions/ChocoboLevelCapIncrease.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace SaintCoinach.Xiv.ItemActions {\n    public class ChocoboLevelCapIncrease : ItemAction {\n        #region Constructors\n\n        public ChocoboLevelCapIncrease(IXivSheet sheet, Ex.Relational.IRelationalRow sourceRow) : base(sheet, sourceRow) { }\n\n        #endregion\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Xiv/ItemActions/CompanionUnlock.cs",
    "content": "using SaintCoinach.Ex.Relational;\n\nnamespace SaintCoinach.Xiv.ItemActions {\n    public class CompanionUnlock : ItemAction {\n        #region Static\n\n        private const int CompanionKey = 0;\n\n        #endregion\n\n        #region Properties\n\n        public Companion Companion {\n            get {\n                var key = GetData(CompanionKey);\n                return Sheet.Collection.GetSheet<Companion>()[key];\n            }\n        }\n\n        #endregion\n\n        #region Constructors\n\n        #region Constructor\n\n        public CompanionUnlock(IXivSheet sheet, IRelationalRow sourceRow) : base(sheet, sourceRow) { }\n\n        #endregion\n\n        #endregion\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Xiv/ItemActions/CompanyEffect.cs",
    "content": "using SaintCoinach.Ex.Relational;\n\nnamespace SaintCoinach.Xiv.ItemActions {\n    public class CompanyEffect : ItemAction {\n        #region Constructors\n\n        #region Constructor\n\n        public CompanyEffect(IXivSheet sheet, IRelationalRow sourceRow) : base(sheet, sourceRow) { }\n\n        #endregion\n\n        #endregion\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Xiv/ItemActions/CustomizeUnlock.cs",
    "content": "﻿using SaintCoinach.Ex.Relational;\nusing System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace SaintCoinach.Xiv.ItemActions {\n    public class CustomizeUnlock : ItemAction {\n        #region Static\n\n        private const int CustomizeDataKey = 0;\n        private const int LogMessageKey = 1;\n\n        #endregion\n\n        #region Properties\n\n        public IEnumerable<IXivRow> CustomizeRows {\n            get {\n                var data = (UInt16)GetData(CustomizeDataKey);\n                var customize = Sheet.Collection.GetSheet(\"CharaMakeCustomize\");\n                return customize.Cast<IXivRow>().Where(r => (UInt16)r[\"Data\"] == data).ToArray();\n            }\n        }\n\n        #endregion\n\n        #region Constructors\n\n        #region Constructor\n\n        public CustomizeUnlock(IXivSheet sheet, IRelationalRow sourceRow) : base(sheet, sourceRow) { }\n\n        #endregion\n\n        #endregion\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Xiv/ItemActions/DesynthSkillReset.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace SaintCoinach.Xiv.ItemActions {\n    public class DesynthSkillReset : ItemAction {\n        #region Static\n\n        private const int ClassJobKey = 0;\n\n        #endregion\n        \n        #region Properties\n\n        public ClassJob ClassJob {\n            get {\n                var key = GetData(ClassJobKey);\n                return Sheet.Collection.GetSheet<ClassJob>()[key];\n            }\n        }\n\n        #endregion\n\n        #region Constructors\n\n        public DesynthSkillReset(IXivSheet sheet, Ex.Relational.IRelationalRow sourceRow) : base(sheet, sourceRow) { }\n\n        #endregion\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Xiv/ItemActions/Enhancement.cs",
    "content": "using System;\nusing System.Collections.Generic;\nusing System.Linq;\n\nusing SaintCoinach.Ex.Relational;\n\nnamespace SaintCoinach.Xiv.ItemActions {\n    public class Enhancement : ItemAction, IParameterObject {\n        #region Static\n\n        private const int GroupKey = 0;\n        private const int ItemFoodKey = 1;\n        private const int DurationKey = 2;\n\n        #endregion\n\n        #region Fields\n\n        private IEnumerable<Parameter> _Parameters;\n\n        #endregion\n\n        #region Properties\n\n        public int EnhancementGroup { get { return GetData(GroupKey); } }\n        public int EnhancementGroupHq { get { return GetHqData(GroupKey); } }\n\n        public ItemFood ItemFood {\n            get {\n                var key = GetData(ItemFoodKey);\n                return Sheet.Collection.GetSheet<ItemFood>()[key];\n            }\n        }\n\n        public ItemFood ItemFoodHq {\n            get {\n                var key = GetHqData(ItemFoodKey);\n                return Sheet.Collection.GetSheet<ItemFood>()[key];\n            }\n        }\n\n        public TimeSpan Duration { get { return TimeSpan.FromSeconds(GetData(DurationKey)); } }\n        public TimeSpan DurationHq { get { return TimeSpan.FromSeconds(GetHqData(DurationKey)); } }\n\n        #endregion\n\n        #region Constructors\n\n        #region Constructor\n\n        public Enhancement(IXivSheet sheet, IRelationalRow sourceRow) : base(sheet, sourceRow) { }\n\n        #endregion\n\n        #endregion\n\n        public IEnumerable<Parameter> Parameters { get { return _Parameters ?? (_Parameters = BuildParameters()); } }\n\n        #region Build\n\n        private ParameterCollection BuildParameters() {\n            var parameters = new ParameterCollection();\n\n            var f = ItemFood;\n            var fHq = ItemFoodHq;\n            if (f == fHq)\n                parameters.AddRange(f.Parameters);\n            else {\n                foreach (var p in f.Parameters) {\n                    foreach (var v in p.Where(_ => _.Type != ParameterType.Hq))\n                        parameters.AddParameterValue(p.BaseParam, v);\n                }\n                foreach (var p in fHq.Parameters) {\n                    foreach (var v in p.Where(_ => _.Type == ParameterType.Hq))\n                        parameters.AddParameterValue(p.BaseParam, v);\n                }\n            }\n            return parameters;\n        }\n\n        #endregion\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Xiv/ItemActions/EquipmentCoffer.cs",
    "content": "﻿using SaintCoinach.Ex.Relational;\nusing System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace SaintCoinach.Xiv.ItemActions {\n    public class EquipmentCoffer : ItemAction {\n        // Used to add several pieces of equipment to inventory.\n        // Only usuable within a major town.\n\n        public EquipmentCoffer(IXivSheet sheet, IRelationalRow sourceRow) : base(sheet, sourceRow) { }\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Xiv/ItemActions/EternalBondInvitation.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace SaintCoinach.Xiv.ItemActions {\n    public class EternalBondInvitation : ItemAction {\n        #region Constructors\n\n        public EternalBondInvitation(IXivSheet sheet, Ex.Relational.IRelationalRow sourceRow) : base(sheet, sourceRow) { }\n\n        #endregion\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Xiv/ItemActions/EternityRing.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace SaintCoinach.Xiv.ItemActions {\n    public class EternityRing : ItemAction {\n        #region Constructors\n\n        public EternityRing(IXivSheet sheet, Ex.Relational.IRelationalRow sourceRow) : base(sheet, sourceRow) { }\n\n        #endregion\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Xiv/ItemActions/Fantasia.cs",
    "content": "using SaintCoinach.Ex.Relational;\n\nnamespace SaintCoinach.Xiv.ItemActions {\n    public class Fantasia : ItemAction {\n        #region Constructors\n\n        #region Constructor\n\n        public Fantasia(IXivSheet sheet, IRelationalRow sourceRow) : base(sheet, sourceRow) { }\n\n        #endregion\n\n        #endregion\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Xiv/ItemActions/FateContentAction.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace SaintCoinach.Xiv.ItemActions {\n    public class FateContentAction : ItemAction {\n        // Currently used to remove Ixion's shield.\n\n        public FateContentAction(IXivSheet sheet, Ex.Relational.IRelationalRow sourceRow) : base(sheet, sourceRow) { }\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Xiv/ItemActions/FieldNoteUnlock.cs",
    "content": "﻿using SaintCoinach.Ex.Relational;\nusing System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace SaintCoinach.Xiv.ItemActions {\n    public class FieldNoteUnlock : ItemAction {\n        // Used to unlock a certain field note in bozja.\n\n        private const int FieldNoteKey = 0;\n\n        public FieldNoteUnlock(IXivSheet sheet, IRelationalRow sourceRow) : base(sheet, sourceRow) { }\n\n        public MYCWarResultNotebook FieldNote {\n            get {\n                return Sheet.Collection.GetSheet<MYCWarResultNotebook>()[GetData(FieldNoteKey)];\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Xiv/ItemActions/Fireworks.cs",
    "content": "using SaintCoinach.Ex.Relational;\n\nnamespace SaintCoinach.Xiv.ItemActions {\n    public class Fireworks : ItemAction {\n        #region Constructors\n\n        #region Constructor\n\n        public Fireworks(IXivSheet sheet, IRelationalRow sourceRow) : base(sheet, sourceRow) { }\n\n        #endregion\n\n        #endregion\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Xiv/ItemActions/FolkloreBook.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace SaintCoinach.Xiv.ItemActions {\n    public class FolkloreBook : ItemAction {\n        #region Constructors\n\n        public FolkloreBook(IXivSheet sheet, Ex.Relational.IRelationalRow sourceRow) : base(sheet, sourceRow) { }\n\n        #endregion\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Xiv/ItemActions/Food.cs",
    "content": "using SaintCoinach.Ex.Relational;\n\nnamespace SaintCoinach.Xiv.ItemActions {\n    public class Food : Enhancement {\n        #region Constructors\n\n        #region Constructor\n\n        public Food(IXivSheet sheet, IRelationalRow sourceRow) : base(sheet, sourceRow) { }\n\n        #endregion\n\n        #endregion\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Xiv/ItemActions/FriendlyEffect.cs",
    "content": "using SaintCoinach.Ex.Relational;\n\nnamespace SaintCoinach.Xiv.ItemActions {\n    public class FriendlyEffect : ItemAction {\n        #region Constructors\n\n        #region Constructor\n\n        public FriendlyEffect(IXivSheet sheet, IRelationalRow sourceRow) : base(sheet, sourceRow) { }\n\n        #endregion\n\n        #endregion\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Xiv/ItemActions/GpRecovery.cs",
    "content": "using System.Collections.Generic;\n\nusing SaintCoinach.Ex.Relational;\n\nnamespace SaintCoinach.Xiv.ItemActions {\n    public class GpRecovery : PointRecovery {\n        #region Static\n\n        private const int AmountKey = 0;\n\n        #endregion\n\n        #region Properties\n\n        public int Amount { get { return GetData(AmountKey); } }\n        public int AmountHq { get { return GetHqData(AmountKey); } }\n\n        #endregion\n\n        #region Constructors\n\n        #region Constructor\n\n        public GpRecovery(IXivSheet sheet, IRelationalRow sourceRow) : base(sheet, sourceRow) { }\n\n        #endregion\n\n        #endregion\n\n        #region Build\n\n        protected override IEnumerable<Parameter> GetParameters() {\n            const int GpBaseParamKey = 10; // XXX: Magic number!\n\n            var parameters = new ParameterCollection();\n\n            var bpSheet = Sheet.Collection.GetSheet<BaseParam>();\n            parameters.AddParameterValue(bpSheet[GpBaseParamKey], new ParameterValueFixed(ParameterType.Base, Amount, 0));\n            if (AmountHq != Amount)\n                parameters.AddParameterValue(bpSheet[GpBaseParamKey],\n                    new ParameterValueFixed(ParameterType.Hq, AmountHq, 0));\n\n            return parameters;\n        }\n\n        #endregion\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Xiv/ItemActions/Heavenscracker.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace SaintCoinach.Xiv.ItemActions {\n    public class Heavenscracker : ItemAction {\n        #region Constructors\n\n        public Heavenscracker(IXivSheet sheet, Ex.Relational.IRelationalRow sourceRow) : base(sheet, sourceRow) { }\n\n        #endregion\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Xiv/ItemActions/HostileEffect.cs",
    "content": "using SaintCoinach.Ex.Relational;\n\nnamespace SaintCoinach.Xiv.ItemActions {\n    public class HostileEffect : ItemAction {\n        #region Constructors\n\n        #region Constructor\n\n        public HostileEffect(IXivSheet sheet, IRelationalRow sourceRow) : base(sheet, sourceRow) { }\n\n        #endregion\n\n        #endregion\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Xiv/ItemActions/HpMpRecovery.cs",
    "content": "using System.Collections.Generic;\n\nusing SaintCoinach.Ex.Relational;\n\nnamespace SaintCoinach.Xiv.ItemActions {\n    public class HpMpRecovery : PointRecovery {\n        #region Static\n\n        private const int HpAmountKey = 0;\n        private const int HpMaximumKey = 1;\n        private const int MpAmountKey = 3;\n        private const int MpMaximumKey = 4;\n\n        #endregion\n\n        #region Properties\n\n        public int HpAmount { get { return GetData(HpAmountKey); } }\n        public int HpMaximum { get { return GetData(HpMaximumKey); } }\n        public int HpAmountHq { get { return GetHqData(HpAmountKey); } }\n        public int HpMaximumHq { get { return GetHqData(HpMaximumKey); } }\n        public int MpAmount { get { return GetData(MpAmountKey); } }\n        public int MpMaximum { get { return GetData(MpMaximumKey); } }\n        public int MpAmountHq { get { return GetHqData(MpAmountKey); } }\n        public int MpMaximumHq { get { return GetHqData(MpMaximumKey); } }\n\n        #endregion\n\n        #region Constructors\n\n        #region Constructor\n\n        public HpMpRecovery(IXivSheet sheet, IRelationalRow sourceRow) : base(sheet, sourceRow) { }\n\n        #endregion\n\n        #endregion\n\n        #region Build\n\n        protected override IEnumerable<Parameter> GetParameters() {\n            // XXX: Here be magic numbers\n            const int HpBaseParamKey = 7;\n            const int MpBaseParamKey = 8;\n\n            var parameters = new ParameterCollection();\n            var bpSheet = Sheet.Collection.GetSheet<BaseParam>();\n\n            if (HpMaximum > 0)\n                parameters.AddParameterValue(bpSheet[HpBaseParamKey],\n                    new ParameterValueRelativeLimited(ParameterType.Base, HpAmount / 100.0, HpMaximum, 0));\n            else\n                parameters.AddParameterValue(bpSheet[HpBaseParamKey],\n                    new ParameterValueFixed(ParameterType.Base, HpAmount, 0));\n\n            if (HpMaximumHq != HpMaximum && HpAmountHq != HpAmount) {\n                if (HpMaximumHq > 0)\n                    parameters.AddParameterValue(bpSheet[HpBaseParamKey],\n                        new ParameterValueRelativeLimited(ParameterType.Hq, HpAmountHq / 100.0, HpMaximumHq, 0));\n                else\n                    parameters.AddParameterValue(bpSheet[HpBaseParamKey],\n                        new ParameterValueFixed(ParameterType.Hq, HpAmountHq, 0));\n            }\n\n            if (MpMaximum > 0)\n                parameters.AddParameterValue(bpSheet[MpBaseParamKey],\n                    new ParameterValueRelativeLimited(ParameterType.Base, MpAmount / 100.0, MpMaximum, 0));\n            else\n                parameters.AddParameterValue(bpSheet[MpBaseParamKey],\n                    new ParameterValueFixed(ParameterType.Base, MpAmount, 0));\n\n            // ReSharper disable once InvertIf\n            if (MpMaximumHq != MpMaximum && MpAmountHq != MpAmount) {\n                if (MpMaximumHq > 0)\n                    parameters.AddParameterValue(bpSheet[MpBaseParamKey],\n                        new ParameterValueRelativeLimited(ParameterType.Hq, MpAmountHq / 100.0, MpMaximumHq, 0));\n                else\n                    parameters.AddParameterValue(bpSheet[MpBaseParamKey],\n                        new ParameterValueFixed(ParameterType.Hq, MpAmountHq, 0));\n            }\n\n            return parameters;\n        }\n\n        #endregion\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Xiv/ItemActions/HpRecovery.cs",
    "content": "using System.Collections.Generic;\n\nusing SaintCoinach.Ex.Relational;\n\nnamespace SaintCoinach.Xiv.ItemActions {\n    public class HpRecovery : PointRecovery {\n        #region Static\n\n        private const int AmountKey = 0;\n        private const int MaximumKey = 1;\n\n        #endregion\n\n        #region Properties\n\n        public int Amount { get { return GetData(AmountKey); } }\n        public int Maximum { get { return GetData(MaximumKey); } }\n        public int AmountHq { get { return GetHqData(AmountKey); } }\n        public int MaximumHq { get { return GetHqData(MaximumKey); } }\n\n        #endregion\n\n        #region Constructors\n\n        #region Constructor\n\n        public HpRecovery(IXivSheet sheet, IRelationalRow sourceRow) : base(sheet, sourceRow) { }\n\n        #endregion\n\n        #endregion\n\n        #region Build\n\n        protected override IEnumerable<Parameter> GetParameters() {\n            // XXX: Here be magic numbers\n            const int HpBaseParamKey = 7;\n\n            var parameters = new ParameterCollection();\n            var bpSheet = Sheet.Collection.GetSheet<BaseParam>();\n\n            if (Maximum > 0)\n                parameters.AddParameterValue(bpSheet[HpBaseParamKey],\n                    new ParameterValueRelativeLimited(ParameterType.Base, Amount / 100.0, Maximum, 0));\n            else\n                parameters.AddParameterValue(bpSheet[HpBaseParamKey],\n                    new ParameterValueFixed(ParameterType.Base, Amount, 0));\n\n            // ReSharper disable once InvertIf\n            if (MaximumHq != Maximum && AmountHq != Amount) {\n                if (MaximumHq > 0)\n                    parameters.AddParameterValue(bpSheet[HpBaseParamKey],\n                        new ParameterValueRelativeLimited(ParameterType.Hq, AmountHq / 100.0, MaximumHq, 0));\n                else\n                    parameters.AddParameterValue(bpSheet[HpBaseParamKey],\n                        new ParameterValueFixed(ParameterType.Hq, AmountHq, 0));\n            }\n\n            return parameters;\n        }\n\n        #endregion\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Xiv/ItemActions/ItemRoulette.cs",
    "content": "using SaintCoinach.Ex.Relational;\n\nnamespace SaintCoinach.Xiv.ItemActions {\n    public class ItemRoulette : ItemAction {\n        #region Constructors\n\n        #region Constructor\n\n        public ItemRoulette(IXivSheet sheet, IRelationalRow sourceRow) : base(sheet, sourceRow) { }\n\n        #endregion\n\n        #endregion\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Xiv/ItemActions/MgpCard.cs",
    "content": "﻿using SaintCoinach.Ex.Relational;\nusing System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace SaintCoinach.Xiv.ItemActions\n{\n    public class MgpCard : ItemAction {\n        #region Static\n\n        private const int AmountKey = 0;\n\n        #endregion\n\n        #region Properties\n\n        public int Amount {\n            get { return GetData(AmountKey); }\n        }\n\n        #endregion\n\n        #region Constructors\n\n        #region Constructor\n\n        public MgpCard(IXivSheet sheet, IRelationalRow sourceRow) : base(sheet, sourceRow) { }\n\n        #endregion\n\n        #endregion\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Xiv/ItemActions/MountUnlock.cs",
    "content": "using SaintCoinach.Ex.Relational;\n\nnamespace SaintCoinach.Xiv.ItemActions {\n    public class MountUnlock : ItemAction {\n        #region Static\n\n        private const int MountKey = 0;\n\n        #endregion\n\n        #region Properties\n\n        public Mount Mount {\n            get {\n                var key = GetData(MountKey);\n                return Sheet.Collection.GetSheet<Mount>()[key];\n            }\n        }\n\n        #endregion\n\n        #region Constructors\n\n        #region Constructor\n\n        public MountUnlock(IXivSheet sheet, IRelationalRow sourceRow) : base(sheet, sourceRow) { }\n\n        #endregion\n\n        #endregion\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Xiv/ItemActions/MpRecovery.cs",
    "content": "using System.Collections.Generic;\n\nusing SaintCoinach.Ex.Relational;\n\nnamespace SaintCoinach.Xiv.ItemActions {\n    public class MpRecovery : PointRecovery {\n        #region Static\n\n        private const int AmountKey = 0;\n        private const int MaximumKey = 1;\n\n        #endregion\n\n        #region Properties\n\n        public int Amount { get { return GetData(AmountKey); } }\n        public int Maximum { get { return GetData(MaximumKey); } }\n        public int AmountHq { get { return GetHqData(AmountKey); } }\n        public int MaximumHq { get { return GetHqData(MaximumKey); } }\n\n        #endregion\n\n        #region Constructors\n\n        #region Constructor\n\n        public MpRecovery(IXivSheet sheet, IRelationalRow sourceRow) : base(sheet, sourceRow) { }\n\n        #endregion\n\n        #endregion\n\n        #region Build\n\n        protected override IEnumerable<Parameter> GetParameters() {\n            // XXX: Here be magic numbers\n            const int MpBaseParamKey = 8;\n\n            var parameters = new ParameterCollection();\n            var bpSheet = Sheet.Collection.GetSheet<BaseParam>();\n\n            if (Maximum > 0)\n                parameters.AddParameterValue(bpSheet[MpBaseParamKey],\n                    new ParameterValueRelativeLimited(ParameterType.Base, Amount / 100.0, Maximum, 0));\n            else\n                parameters.AddParameterValue(bpSheet[MpBaseParamKey],\n                    new ParameterValueFixed(ParameterType.Base, Amount, 0));\n\n            // ReSharper disable once InvertIf\n            if (MaximumHq != Maximum && AmountHq != Amount) {\n                if (MaximumHq > 0)\n                    parameters.AddParameterValue(bpSheet[MpBaseParamKey],\n                        new ParameterValueRelativeLimited(ParameterType.Hq, AmountHq / 100.0, MaximumHq, 0));\n                else\n                    parameters.AddParameterValue(bpSheet[MpBaseParamKey],\n                        new ParameterValueFixed(ParameterType.Hq, AmountHq, 0));\n            }\n\n            return parameters;\n        }\n\n        #endregion\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Xiv/ItemActions/OrchestrionRollUnlock.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace SaintCoinach.Xiv.ItemActions {\n    public class OrchestrionRollUnlock : ItemAction {\n        #region Constructors\n\n        public OrchestrionRollUnlock(IXivSheet sheet, Ex.Relational.IRelationalRow sourceRow) : base(sheet, sourceRow) { }\n\n        #endregion\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Xiv/ItemActions/OrnamentUnlock.cs",
    "content": "﻿using SaintCoinach.Ex.Relational;\nusing System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace SaintCoinach.Xiv.ItemActions {\n    public class OrnamentUnlock : ItemAction {\n        // Used to unlock fashion accessory, such as parasol.\n\n        private const int OrnamentKey = 0;\n        \n        public OrnamentUnlock(IXivSheet sheet, IRelationalRow sourceRow) : base(sheet, sourceRow) { }\n\n        public Ornament Ornament { \n            get {\n                var key = GetData(OrnamentKey);\n                return Sheet.Collection.GetSheet<Ornament>()[key];\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Xiv/ItemActions/PointRecovery.cs",
    "content": "using System.Collections.Generic;\n\nusing SaintCoinach.Ex.Relational;\n\nnamespace SaintCoinach.Xiv.ItemActions {\n    public abstract class PointRecovery : ItemAction, IParameterObject {\n        #region Fields\n\n        private IEnumerable<Parameter> _Parameters;\n\n        #endregion\n\n        #region Constructors\n\n        #region Constructor\n\n        protected PointRecovery(IXivSheet sheet, IRelationalRow sourceRow) : base(sheet, sourceRow) { }\n\n        #endregion\n\n        #endregion\n\n        public IEnumerable<Parameter> Parameters { get { return _Parameters ?? (_Parameters = GetParameters()); } }\n\n        #region Build\n\n        protected abstract IEnumerable<Parameter> GetParameters();\n\n        #endregion\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Xiv/ItemActions/Raise.cs",
    "content": "using SaintCoinach.Ex.Relational;\n\nnamespace SaintCoinach.Xiv.ItemActions {\n    public class Raise : ItemAction {\n        #region Constructors\n\n        #region Constructor\n\n        public Raise(IXivSheet sheet, IRelationalRow sourceRow) : base(sheet, sourceRow) { }\n\n        #endregion\n\n        #endregion\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Xiv/ItemActions/RealmRebornRed.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace SaintCoinach.Xiv.ItemActions {\n    public class RealmRebornRed : ItemAction {\n        #region Constructors\n\n        public RealmRebornRed(IXivSheet sheet, Ex.Relational.IRelationalRow sourceRow) : base(sheet, sourceRow) { }\n\n        #endregion\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Xiv/ItemActions/RecipeBookUnlock.cs",
    "content": "using System.Collections.Generic;\nusing System.Linq;\n\nusing SaintCoinach.Ex.Relational;\n\nnamespace SaintCoinach.Xiv.ItemActions {\n    public class RecipeBookUnlock : ItemAction {\n        #region Static\n\n        private const int RecipeGroupKey = 0;\n\n        #endregion\n\n        #region Fields\n\n        private Recipe[] _Recipes;\n\n        #endregion\n\n        #region Properties\n\n        public int RecipeBook { get { return GetData(RecipeGroupKey); } }\n\n        public IEnumerable<Recipe> Recipes {\n            get {\n                return _Recipes\n                       ?? (_Recipes =\n                           Sheet.Collection.GetSheet<Recipe>().Where(_ => _.UnlockItem != null && _.UnlockItem.Key == RecipeBook).ToArray());\n            }\n        }\n\n        #endregion\n\n        #region Constructors\n\n        #region Constructor\n\n        public RecipeBookUnlock(IXivSheet sheet, IRelationalRow sourceRow) : base(sheet, sourceRow) { }\n\n        #endregion\n\n        #endregion\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Xiv/ItemActions/Sanction.cs",
    "content": "using SaintCoinach.Ex.Relational;\n\nnamespace SaintCoinach.Xiv.ItemActions {\n    public class Sanction : ItemAction {\n        #region Constructors\n\n        #region Constructor\n\n        public Sanction(IXivSheet sheet, IRelationalRow sourceRow) : base(sheet, sourceRow) { }\n\n        #endregion\n\n        #endregion\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Xiv/ItemActions/SphereScroll.cs",
    "content": "using SaintCoinach.Ex.Relational;\n\nnamespace SaintCoinach.Xiv.ItemActions {\n    public class SphereScroll : ItemAction {\n        #region Constructors\n\n        #region Constructor\n\n        public SphereScroll(IXivSheet sheet, IRelationalRow sourceRow) : base(sheet, sourceRow) { }\n\n        #endregion\n\n        #endregion\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Xiv/ItemActions/StatusRemoval.cs",
    "content": "using System.Collections.Generic;\nusing System.Linq;\n\nusing SaintCoinach.Ex.Relational;\n\nnamespace SaintCoinach.Xiv.ItemActions {\n    public class StatusRemoval : ItemAction {\n        #region Static\n\n        private const int StatusCount = 9;\n        private const int StatusOffset = 0;\n\n        #endregion\n\n        #region Fields\n\n        private FriendlyEffect[] _Statuses;\n        private FriendlyEffect[] _StatusesHq;\n\n        #endregion\n\n        #region Properties\n\n        public IEnumerable<FriendlyEffect> Statuses { get { return _Statuses ?? (_Statuses = BuildStatuses(false)); } }\n\n        public IEnumerable<FriendlyEffect> StatusesHq {\n            get { return _StatusesHq ?? (_StatusesHq = BuildStatuses(true)); }\n        }\n\n        #endregion\n\n        #region Constructors\n\n        #region Constructor\n\n        public StatusRemoval(IXivSheet sheet, IRelationalRow sourceRow) : base(sheet, sourceRow) { }\n\n        #endregion\n\n        #endregion\n\n        #region Build\n\n        private FriendlyEffect[] BuildStatuses(bool hq) {\n            var statuses = new List<FriendlyEffect>();\n            var statusSheet = Sheet.Collection.GetSheet<FriendlyEffect>();\n\n            for (var i = 0; i < StatusCount; ++i) {\n                var statusKey = hq ? GetHqData(StatusOffset + i) : GetData(StatusOffset + i);\n\n                if (statusKey == 0 || statuses.Any(_ => _.Key == statusKey))\n                    continue;\n\n                statuses.Add(statusSheet[statusKey]);\n            }\n\n            return statuses.ToArray();\n        }\n\n        #endregion\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Xiv/ItemActions/SustainPotion.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace SaintCoinach.Xiv.ItemActions {\n    public class SustainPotion : ItemAction {\n        public SustainPotion(IXivSheet sheet, Ex.Relational.IRelationalRow sourceRow) : base(sheet, sourceRow) { }\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Xiv/ItemActions/TeleportationTicket.cs",
    "content": "﻿using SaintCoinach.Ex.Relational;\nusing System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace SaintCoinach.Xiv.ItemActions {\n    public class TeleportationTicket : ItemAction {\n        // Used to teleport to some place.\n        public TeleportationTicket(IXivSheet sheet, IRelationalRow sourceRow) : base(sheet, sourceRow) { }\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Xiv/ItemActions/TpRecovery.cs",
    "content": "using System.Collections.Generic;\n\nusing SaintCoinach.Ex.Relational;\n\nnamespace SaintCoinach.Xiv.ItemActions {\n    public class TpRecovery : PointRecovery {\n        #region Static\n\n        private const int AmountKey = 0;\n\n        #endregion\n\n        #region Properties\n\n        public int Amount { get { return GetData(AmountKey); } }\n        public int AmountHq { get { return GetHqData(AmountKey); } }\n\n        #endregion\n\n        #region Constructors\n\n        #region Constructor\n\n        public TpRecovery(IXivSheet sheet, IRelationalRow sourceRow) : base(sheet, sourceRow) { }\n\n        #endregion\n\n        #endregion\n\n        #region Build\n\n        protected override IEnumerable<Parameter> GetParameters() {\n            const int TpBaseParamKey = 9; // XXX: Magic number!\n\n            var parameters = new ParameterCollection();\n\n            var bpSheet = Sheet.Collection.GetSheet<BaseParam>();\n            parameters.AddParameterValue(bpSheet[TpBaseParamKey], new ParameterValueFixed(ParameterType.Base, Amount, 0));\n            if (AmountHq != Amount)\n                parameters.AddParameterValue(bpSheet[TpBaseParamKey],\n                    new ParameterValueFixed(ParameterType.Hq, AmountHq, 0));\n\n            return parameters;\n        }\n\n        #endregion\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Xiv/ItemActions/TripleTriadCardUnlock.cs",
    "content": "﻿using SaintCoinach.Ex.Relational;\nusing System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace SaintCoinach.Xiv.ItemActions\n{\n    public class TripleTriadCardUnlock : ItemAction {\n        #region Static\n\n        private const int CardKey = 0;\n\n        #endregion\n\n        #region Properties\n\n        public TripleTriadCard TripleTriadCard {\n            get {\n                var key = GetData(CardKey);\n                return Sheet.Collection.GetSheet<TripleTriadCard>()[key];\n            }\n        }\n\n        #endregion\n\n        #region Constructors\n\n        #region Constructor\n\n        public TripleTriadCardUnlock(IXivSheet sheet, IRelationalRow sourceRow) : base(sheet, sourceRow) { }\n\n        #endregion\n\n        #endregion\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Xiv/ItemBase.cs",
    "content": "using SaintCoinach.Ex.Relational;\nusing SaintCoinach.Imaging;\n\nnamespace SaintCoinach.Xiv {\n    /// <summary>\n    /// Base class for items from the game data.\n    /// </summary>\n    /// <remarks>\n    /// Inherited by <see cref=\"EventItem\"/> and <see cref=\"Item\"/>.\n    /// </remarks>\n    public abstract class ItemBase : XivRow, IQuantifiableXivString {\n        #region Properties\n        /// <summary>\n        /// Gets the name of the current item.\n        /// </summary>\n        /// <value>The name of the current item.</value>\n        public Text.XivString Name { get { return AsString(\"Name\"); } }\n        /// <summary>\n        /// Gets the description of the current item.\n        /// </summary>\n        /// <value>The description of the current item.</value>\n        public Text.XivString Description { get { return AsString(\"Description\"); } }\n        /// <summary>\n        /// Gets the singular string of the current item.\n        /// </summary>\n        /// <value>The singular string of the current item.</value>\n        public Text.XivString Singular { get { return AsString(\"Singular\"); } }\n        /// <summary>\n        /// Gets the plural string of the current item.\n        /// </summary>\n        /// <value>The plural string of the current item.</value>\n        public Text.XivString Plural { get { return Sheet.Collection.ActiveLanguage == Ex.Language.Japanese ? Singular : AsString(\"Plural\"); } }\n        /// <summary>\n        /// Gets the icon of the current item.\n        /// </summary>\n        /// <value>The icon of the current item.</value>\n        public ImageFile Icon { get { return AsImage(\"Icon\"); } }\n        /// <summary>\n        /// Gets the stack size of the current item.\n        /// </summary>\n        /// <value>The stack size of the current item.</value>\n        public int StackSize { get { return AsInt32(\"StackSize\"); } }\n\n        #endregion\n\n        #region Constructors\n\n        /// <summary>\n        ///     Initializes a new instance of the <see cref=\"ItemBase\" /> class.\n        /// </summary>\n        /// <param name=\"sheet\"><see cref=\"IXivSheet\" /> containing this object.</param>\n        /// <param name=\"sourceRow\"><see cref=\"IRelationalRow\" /> to read data from.</param>\n        protected ItemBase(IXivSheet sheet, IRelationalRow sourceRow) : base(sheet, sourceRow) { }\n\n        #endregion\n\n        #region IQuantifiableName Members\n        string IQuantifiable.Singular {\n            get { return Singular; }\n        }\n\n        string IQuantifiable.Plural {\n            get { return Plural; }\n        }\n        #endregion\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Xiv/ItemComparer.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace SaintCoinach.Xiv {\n    public class ItemComparer : IComparer<SaintCoinach.Xiv.ItemBase> {\n        public enum Option {\n            None = 0,\n            Ascending = 1,\n            Descending = -1\n        }\n\n        #region Static\n\n        public static readonly ItemComparer Default = new ItemComparer();\n\n        #endregion\n\n        #region Fields\n\n        private Option _CategoryOption = Option.Ascending;\n        private Option _EquipLevelOption = Option.Descending;\n        private Option _ItemLevelOption = Option.Descending;\n\n        #endregion\n\n        #region Properties\n\n        public Option CategoryOption { get { return _CategoryOption; } set { _CategoryOption = value; } }\n        public Option EquipLevelOption { get { return _EquipLevelOption; } set { _EquipLevelOption = value; } }\n        public Option ItemLevelOption { get { return _ItemLevelOption; } set { _ItemLevelOption = value; } }\n\n        #endregion\n\n        #region IComparer<InventoryItem> Members\n\n        public int Compare(SaintCoinach.Xiv.ItemBase x, SaintCoinach.Xiv.ItemBase y) {\n            var ix = x as Item;\n            var iy = y as Item;\n            if (x == y)\n                return 0;\n            if (x == null)\n                return -1;\n\n            if (ix == null)\n                return -1;\n            if (iy == null)\n                return 1;\n            if (ix != null && iy != null) {\n                var comp = CompareCategoryMajor(ix, iy);\n                if (comp != 0)\n                    return comp;\n\n                comp = CompareCategoryMinor(ix, iy);\n                if (comp != 0)\n                    return comp;\n\n                comp = CompareEquipLevel(ix, iy);\n                if (comp != 0)\n                    return comp;\n\n                comp = CompareItemLevel(ix, iy);\n                if (comp != 0)\n                    return comp;\n            }\n\n            return x.Key.CompareTo(y.Key);\n        }\n\n        public int CompareCategoryMajor(SaintCoinach.Xiv.Item x, SaintCoinach.Xiv.Item y) {\n            return ((int)CategoryOption) * x.ItemUICategory.MajorOrder.CompareTo(y.ItemUICategory.MajorOrder);\n        }\n\n        public int CompareCategoryMinor(SaintCoinach.Xiv.Item x, SaintCoinach.Xiv.Item y) {\n            return ((int)CategoryOption) * x.ItemUICategory.MinorOrder.CompareTo(y.ItemUICategory.MinorOrder);\n        }\n\n        public int CompareEquipLevel(SaintCoinach.Xiv.Item x, SaintCoinach.Xiv.Item y) {\n            var ex = 0;\n            var ey = 0;\n            if (x is SaintCoinach.Xiv.Items.Equipment)\n                ex = ((SaintCoinach.Xiv.Items.Equipment)x).EquipmentLevel;\n            if (y is SaintCoinach.Xiv.Items.Equipment)\n                ey = ((SaintCoinach.Xiv.Items.Equipment)y).EquipmentLevel;\n            return ((int)EquipLevelOption) * ex.CompareTo(ey);\n        }\n\n        public int CompareItemLevel(SaintCoinach.Xiv.Item x, SaintCoinach.Xiv.Item y) {\n            return ((int)ItemLevelOption) * x.ItemLevel.Key.CompareTo(y.ItemLevel.Key);\n        }\n\n        #endregion\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Xiv/ItemFood.cs",
    "content": "using System.Collections.Generic;\n\nusing SaintCoinach.Ex.Relational;\n\nnamespace SaintCoinach.Xiv {\n    /// <summary>\n    ///     Class for bonuses granted by consumable items.\n    /// </summary>\n    public class ItemFood : XivRow, IParameterObject {\n        #region Fields\n\n        /// <summary>\n        ///     <see cref=\"ParameterCollection\" /> of the bonuses granted by the current consumable.\n        /// </summary>\n        private ParameterCollection _Parameters;\n\n        #endregion\n\n        #region Constructors\n\n        /// <summary>\n        ///     Initializes a new instance of the <see cref=\"ItemFood\" /> class.\n        /// </summary>\n        /// <param name=\"sheet\"><see cref=\"IXivSheet\" /> containing this object.</param>\n        /// <param name=\"sourceRow\"><see cref=\"IRelationalRow\" /> to read data from.</param>\n        public ItemFood(IXivSheet sheet, IRelationalRow sourceRow) : base(sheet, sourceRow) { }\n\n        #endregion\n\n        /// <summary>\n        ///     Gets the parameter bonuses granted the current consumable.\n        /// </summary>\n        /// <value>The parameter bonuses granted the current consumable.</value>\n        public IEnumerable<Parameter> Parameters { get { return _Parameters ?? (_Parameters = BuildParameters()); } }\n\n        #region Build\n\n        /// <summary>\n        ///     Build a <see cref=\"ParameterCollection\" /> for the bonuses granted by the current consumable.\n        /// </summary>\n        /// <returns>A <see cref=\"ParameterCollection\" /> for the bonuses granted by the current consumable.</returns>\n        private ParameterCollection BuildParameters() {\n            const int Count = 3;\n\n            var parameters = new ParameterCollection();\n            for (var i = 0; i < Count; ++i) {\n                var param = As<BaseParam>(i);\n                if (param.Key == 0)\n                    continue;\n\n                var isRel = AsBoolean(\"IsRelative\", i);\n                var val = AsInt32(\"Value\", i);\n                var valHq = AsInt32(\"Value{HQ}\", i);\n\n                if (isRel) {\n                    var max = AsInt32(\"Max\", i);\n                    var maxHq = AsInt32(\"Max{HQ}\", i);\n\n                    parameters.AddParameterValue(param,\n                        max == 0\n                            ? new ParameterValueRelative(ParameterType.Base, val / 100.0, i)\n                            : new ParameterValueRelativeLimited(ParameterType.Base, val / 100.0, max, i));\n\n                    if (maxHq == max && valHq == val) continue;\n\n                    parameters.AddParameterValue(param,\n                        maxHq == 0\n                            ? new ParameterValueRelative(ParameterType.Hq, valHq / 100.0, i)\n                            : new ParameterValueRelativeLimited(ParameterType.Hq, valHq / 100.0, maxHq, i));\n                } else {\n                    parameters.AddParameterValue(param, new ParameterValueFixed(ParameterType.Base, val, i));\n                    if (val != valHq)\n                        parameters.AddParameterValue(param, new ParameterValueFixed(ParameterType.Hq, valHq, i));\n                }\n            }\n            return parameters;\n        }\n\n        #endregion\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Xiv/ItemLevel.cs",
    "content": "using System;\n\nusing SaintCoinach.Ex.Relational;\n\nnamespace SaintCoinach.Xiv {\n    /// <summary>\n    ///     Class representing an item level.\n    /// </summary>\n    public class ItemLevel : XivRow {\n        #region Constructors\n\n        /// <summary>\n        ///     Initializes a new instance of the <see cref=\"ItemLevel\" /> class.\n        /// </summary>\n        /// <param name=\"sheet\"><see cref=\"IXivSheet\" /> containing this object.</param>\n        /// <param name=\"sourceRow\"><see cref=\"IRelationalRow\" /> to read data from.</param>\n        public ItemLevel(IXivSheet sheet, IRelationalRow sourceRow) : base(sheet, sourceRow) { }\n\n        #endregion\n\n        #region Helpers\n\n        /// <summary>\n        ///     Get the maximum for a <see cref=\"BaseParam\" /> at the current item level.\n        /// </summary>\n        /// <remarks>\n        ///     The value returned by this method is just the base on which further calculations are performed.\n        /// </remarks>\n        /// <param name=\"baseParam\">The <see cref=\"BaseParam\" /> for which to get the maximum.</param>\n        /// <returns>The maximum value of <c>baseParam</c> at the current item level.</returns>\n        public int GetMaximum(BaseParam baseParam) {\n            const int Offset = -1;\n            return baseParam.Key == 0 ? 0 : Convert.ToInt32(this[Offset + baseParam.Key]);\n        }\n\n        #endregion\n\n        /// <summary>\n        ///     Returns a string representation of the current item level.\n        /// </summary>\n        /// <returns>The string representation of the current item level.</returns>\n        public override string ToString() {\n            return Key.ToString();\n        }\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Xiv/ItemSearchCategory.cs",
    "content": "using SaintCoinach.Ex.Relational;\nusing SaintCoinach.Imaging;\n\nnamespace SaintCoinach.Xiv {\n    public class ItemSearchCategory : XivRow {\n        #region Properties\n\n        public Text.XivString Name { get { return AsString(\"Name\"); } }\n        public ImageFile Icon { get { return AsImage(\"Icon\"); } }\n        public int Category { get { return AsInt32(\"Category\"); } }\n        public int Order { get { return AsInt32(\"Order\"); } }\n        public ClassJob ClassJob { get { return As<ClassJob>(); } }\n\n        #endregion\n\n        #region Constructors\n\n        #region Constructor\n\n        public ItemSearchCategory(IXivSheet sheet, IRelationalRow sourceRow) : base(sheet, sourceRow) { }\n\n        #endregion\n\n        #endregion\n\n        public override string ToString() {\n            return Name;\n        }\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Xiv/ItemSeries.cs",
    "content": "using SaintCoinach.Ex.Relational;\n\nnamespace SaintCoinach.Xiv {\n    public class ItemSeries : XivRow {\n        #region Properties\n\n        public Text.XivString Name { get { return AsString(\"Name\"); } }\n\n        #endregion\n\n        #region Constructors\n\n        #region Constructor\n\n        public ItemSeries(IXivSheet sheet, IRelationalRow sourceRow) : base(sheet, sourceRow) { }\n\n        #endregion\n\n        #endregion\n\n        public override string ToString() {\n            return Name;\n        }\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Xiv/ItemSpecialBonus.cs",
    "content": "using SaintCoinach.Ex.Relational;\n\nnamespace SaintCoinach.Xiv {\n    public class ItemSpecialBonus : XivRow {\n        #region Properties\n\n        public Text.XivString Name { get { return AsString(\"Name\"); } }\n\n        #endregion\n\n        #region Constructors\n\n        #region Constructor\n\n        public ItemSpecialBonus(IXivSheet sheet, IRelationalRow sourceRow) : base(sheet, sourceRow) { }\n\n        #endregion\n\n        #endregion\n\n        public override string ToString() {\n            return Name;\n        }\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Xiv/ItemUICategory.cs",
    "content": "using SaintCoinach.Ex.Relational;\nusing SaintCoinach.Imaging;\n\nnamespace SaintCoinach.Xiv {\n    // ReSharper disable once InconsistentNaming\n    /// <summary>\n    ///     Class representing the category for items in the UI.\n    /// </summary>\n    /// <remarks>\n    ///     This class is used when displaying an item's category, as well as inventory sorting.\n    ///     When sorting an inventory with the Standard method it groups by <see cref=\"MajorOrder\" /> first, sorts the groups\n    ///     in ascending order by key, then does the same for <see cref=\"MinorOrder\" /> inside each group. The items inside\n    ///     minor groups are sorted according to additional rules.\n    /// </remarks>\n    public class ItemUICategory : XivRow {\n        #region Properties\n\n        /// <summary>\n        ///     Gets the name of the current category.\n        /// </summary>\n        /// <value>The name of the current category.</value>\n        public Text.XivString Name { get { return AsString(\"Name\"); } }\n\n        /// <summary>\n        ///     Gets the icon for the current category.\n        /// </summary>\n        /// <value>The icon of the current category.</value>\n        public ImageFile Icon { get { return AsImage(\"Icon\"); } }\n\n        /// <summary>\n        ///     Gets the minor order of the current category.\n        /// </summary>\n        /// <value>The minor order of the current category.</value>\n        public int MinorOrder { get { return AsInt32(\"Order{Minor}\"); } }\n\n        /// <summary>\n        ///     Gets the major order of the current category.\n        /// </summary>\n        /// <value>The major order of the current category.</value>\n        public int MajorOrder { get { return AsInt32(\"Order{Major}\"); } }\n\n        #endregion\n\n        #region Constructors\n\n        /// <summary>\n        ///     Initializes a new instance of the <see cref=\"ItemUICategory\" /> class.\n        /// </summary>\n        /// <param name=\"sheet\"><see cref=\"IXivSheet\" /> containing this object.</param>\n        /// <param name=\"sourceRow\"><see cref=\"IRelationalRow\" /> to read data from.</param>\n        public ItemUICategory(IXivSheet sheet, IRelationalRow sourceRow) : base(sheet, sourceRow) { }\n\n        #endregion\n\n        /// <summary>\n        ///     Returns a string representation of the current category.\n        /// </summary>\n        /// <returns>The value of <see cref=\"Name\" />.</returns>\n        public override string ToString() {\n            return Name;\n        }\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Xiv/Items/Accessory.cs",
    "content": "using SaintCoinach.Ex.Relational;\n\nnamespace SaintCoinach.Xiv.Items {\n    public class Accessory : Armour {\n        #region Constructors\n\n        #region Constructor\n\n        public Accessory(IXivSheet sheet, IRelationalRow sourceRow) : base(sheet, sourceRow) { }\n\n        #endregion\n\n        #endregion\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Xiv/Items/Armour.cs",
    "content": "using System.Collections.Generic;\n\nusing SaintCoinach.Ex.Relational;\n\nnamespace SaintCoinach.Xiv.Items {\n    /// <summary>\n    ///     Class representing armour (left side equipment).\n    /// </summary>\n    public class Armour : Equipment {\n        #region Fields\n\n        /// <summary>\n        ///     <see cref=\"ParameterCollection\" /> containing primary parameters of the current item.\n        /// </summary>\n        private ParameterCollection _PrimaryParameters;\n\n        #endregion\n\n        #region Properties\n\n        /// <summary>\n        ///     Gets the primary <see cref=\"Parameter\"/>s of the current item.\n        /// </summary>\n        /// <value>The primary <see cref=\"Parameter\"/>s of the current item.</value>\n        public override IEnumerable<Parameter> PrimaryParameters {\n            get { return _PrimaryParameters ?? (_PrimaryParameters = BuildPrimaryParameters()); }\n        }\n\n        /// <summary>\n        ///     Gets the physical defense of the current item.\n        /// </summary>\n        /// <value>The physical defense of the current item.</value>\n        public int PhysicalDefense { get { return AsInt32(\"Defense{Phys}\"); } }\n\n        /// <summary>\n        ///     Gets the magical defense of the current item.\n        /// </summary>\n        /// <value>The magical defense of the current item.</value>\n        public int MagicDefense { get { return AsInt32(\"Defense{Mag}\"); } }\n\n        #endregion\n\n        #region Constructors\n\n        /// <summary>\n        ///     Initializes a new instance of the <see cref=\"Armour\" /> class.\n        /// </summary>\n        /// <param name=\"sheet\"><see cref=\"IXivSheet\" /> containing this object.</param>\n        /// <param name=\"sourceRow\"><see cref=\"IRelationalRow\" /> to read data from.</param>\n        public Armour(IXivSheet sheet, IRelationalRow sourceRow) : base(sheet, sourceRow) { }\n\n        #endregion\n\n        #region Build\n\n        /// <summary>\n        ///     Build the <see cref=\"ParameterCollection\" /> containg the primary parameters of the current item.\n        /// </summary>\n        /// <returns>The <see cref=\"ParameterCollection\" /> containg the primary parameters of the current item.</returns>\n        private ParameterCollection BuildPrimaryParameters() {\n            var param = new ParameterCollection();\n\n            // XXX: Here be magic numbers\n            const int PhysicalDefenseKey = 21;\n            const int MagicDefenseKey = 24;\n\n            var paramSheet = Sheet.Collection.GetSheet<BaseParam>();\n            if (PhysicalDefense != 0)\n                param.AddParameterValue(paramSheet[PhysicalDefenseKey],\n                    new ParameterValueFixed(ParameterType.Primary, PhysicalDefense, 0));\n            if (MagicDefense != 0)\n                param.AddParameterValue(paramSheet[MagicDefenseKey],\n                    new ParameterValueFixed(ParameterType.Primary, MagicDefense, 0));\n\n            return param;\n        }\n\n        #endregion\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Xiv/Items/CraftingTool.cs",
    "content": "using System.Collections.Generic;\n\nusing SaintCoinach.Ex.Relational;\n\nnamespace SaintCoinach.Xiv.Items {\n    public class CraftingTool : Weapon {\n\n        #region Constructors\n\n        #region Constructor\n\n        public CraftingTool(IXivSheet sheet, IRelationalRow sourceRow) : base(sheet, sourceRow) { }\n\n        #endregion\n\n        #endregion\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Xiv/Items/Equipment.cs",
    "content": "using System;\nusing System.Collections.Generic;\nusing System.Linq;\n\nusing SaintCoinach.Ex.Relational;\nusing SaintCoinach.Graphics;\n\nnamespace SaintCoinach.Xiv.Items {\n    /// <summary>\n    ///     Base class for equipment items.\n    /// </summary>\n    public abstract class Equipment : Item, IParameterObject {\n        #region Fields\n\n        /// <summary>\n        ///     <see cref=\"ParameterCollection\" /> containing all parameters, primary and secondary, of the current item.\n        /// </summary>\n        private ParameterCollection _AllParameters;\n\n        /// <summary>\n        ///     <see cref=\"ParameterCollection\" /> containg all secondary parameters of the current item.\n        /// </summary>\n        private ParameterCollection _SecondaryParameters;\n\n        #endregion\n\n        #region Properties\n\n        public byte EquipRestriction {\n            get { return As<byte>(\"EquipRestriction\"); }\n        }\n\n        public bool IsCrestWorthy {\n            get { return AsBoolean(\"IsCrestWorthy\"); }\n        }\n\n        /// <summary>\n        ///     Gets the primary <see cref=\"Parameter\" />s of the current item.\n        /// </summary>\n        /// <value>The primary <see cref=\"Parameter\" />s of the current item.</value>\n        public abstract IEnumerable<Parameter> PrimaryParameters { get; }\n\n        /// <summary>\n        ///     Gets the secondary <see cref=\"Parameter\" />s of the current item.\n        /// </summary>\n        /// <value>The secondary <see cref=\"Parameter\" />s of the current item.</value>\n        public IEnumerable<Parameter> SecondaryParameters {\n            get { return _SecondaryParameters ?? (_SecondaryParameters = BuildSecondaryParameters()); }\n        }\n\n        /// <summary>\n        ///     Gets all <see cref=\"Parameter\" />s of the current item.\n        /// </summary>\n        /// <value>The all <see cref=\"Parameter\" />s of the current item.</value>\n        public IEnumerable<Parameter> AllParameters {\n            get {\n                if (_AllParameters != null) return _AllParameters;\n\n                _AllParameters = new ParameterCollection();\n                _AllParameters.AddRange(PrimaryParameters);\n                _AllParameters.AddRange(SecondaryParameters);\n                return _AllParameters;\n            }\n        }\n\n        /// <summary>\n        ///     Gets the level required to equip the current item.\n        /// </summary>\n        /// <value>The level required to equip the current item.</value>\n        public int EquipmentLevel { get { return AsInt32(\"Level{Equip}\"); } }\n\n        /// <summary>\n        ///     Gets the modifier used for <see cref=\"BaseParam\" />s on the current item.\n        /// </summary>\n        /// <value>The modifier used for <see cref=\"BaseParam\" />s on the current item.</value>\n        public int BaseParamModifier { get { return AsInt32(\"BaseParamModifier\"); } }\n\n        /// <summary>\n        ///     Gets the number of materia than can be fitted into the current item without overmelding.\n        /// </summary>\n        /// <value>The number of materia than can be fitted into the current item without overmelding.</value>\n        public int FreeMateriaSlots { get { return AsInt32(\"MateriaSlotCount\"); } }\n\n        /// <summary>\n        ///     Gets the <see cref=\"Item\" /> required to repair the current item.\n        /// </summary>\n        /// <value>The <see cref=\"Item\" /> required to repair the current item.</value>\n        public Item RepairItem { get { return As<XivRow>(\"Item{Repair}\").As<Item>(\"Item\"); } }\n\n        /// <summary>\n        ///     Gets the type of <see cref=\"ItemSpecialBonus\" /> required to grant additional bonuses of the current item.\n        /// </summary>\n        /// <value>The type of <see cref=\"ItemSpecialBonus\" /> required to grant additional bonuses of the current item.</value>\n        public ItemSpecialBonus ItemSpecialBonus { get { return As<ItemSpecialBonus>(); } }\n\n        /// <summary>\n        ///     Gets the parameter used for some <see cref=\"ItemSpecialBonus\"/> required to grant additional bonuses of the current item. \n        /// </summary>\n        /// <value>The parameter used for some <see cref=\"ItemSpecialBonus\"/> required to grant additional bonuses of the current item. </value>\n        public byte ItemSpecialBonusParam {  get { return As<byte>(\"ItemSpecialBonus{Param}\"); } }\n\n        /// <summary>\n        ///     Gets the <see cref=\"ItemSeries\" /> of the current item.\n        /// </summary>\n        /// <value>The <see cref=\"ItemSeries\" /> of the current item.</value>\n        public ItemSeries ItemSeries { get { return As<ItemSeries>(); } }\n\n        /// <summary>\n        ///     Gets the <see cref=\"ClassJobCategory\" /> required to equip the current item.\n        /// </summary>\n        /// <value>The <see cref=\"ClassJobCategory\" /> required to equip the current item.</value>\n        public ClassJobCategory ClassJobCategory { get { return As<ClassJobCategory>(); } }\n\n        /// <summary>\n        ///     Gets the PvP indicator of the item.\n        /// </summary>\n        /// <value>True if the item is PvP equipment, false otherwise.</value>\n        public bool IsPvP { get { return AsBoolean(\"IsPvP\"); } }\n\n        /// <summary>\n        ///     Gets the model identifier used for the current item's primary model.\n        /// </summary>\n        /// <value>The model identifier used for the current item's primary model.</value>\n        public Quad PrimaryModelKey { get { return AsQuad(\"Model{Main}\"); } }\n\n        /// <summary>\n        ///     Gets the model identifier used for the current item's secondary model.\n        /// </summary>\n        /// <value>The model identifier used for the current item's secondary model.</value>\n        public Quad SecondaryModelKey { get { return AsQuad(\"Model{Sub}\"); } }\n        \n        /// <summary>\n        ///     Gets the number of Grand Company seals rewarded for expert delivery of the item.\n        /// </summary>\n        /// <value>The number of Grand Company seals.</value>\n        public int ExpertDeliverySeals {\n            get {\n                if (Rarity <= 1 || AsInt32(\"Price{Low}\") <= 0)\n                    return 0;\n\n                // Formula used for GCSupplyDutyReward seals:\n                // For every item level 200 and below, you receive 5.75 seals.\n                // For every item level after 200, you receive 2 seals.\n\n                var rewards = Sheet.Collection.GetSheet(\"GCSupplyDutyReward\");\n                if (rewards.ContainsRow(ItemLevel.Key))\n                    return (int)(uint)rewards[ItemLevel.Key][\"Seals{ExpertDelivery}\"];\n                return 0;\n            }\n        }\n\n        #endregion\n\n        #region Constructors\n\n        /// <summary>\n        ///     Initializes a new instance of the <see cref=\"Equipment\" /> class.\n        /// </summary>\n        /// <param name=\"sheet\"><see cref=\"IXivSheet\" /> containing this object.</param>\n        /// <param name=\"sourceRow\"><see cref=\"IRelationalRow\" /> to read data from.</param>\n        protected Equipment(IXivSheet sheet, IRelationalRow sourceRow) : base(sheet, sourceRow) { }\n\n        #endregion\n\n        /// <summary>\n        ///     Gets all <see cref=\"Parameter\" />s of the current item.\n        /// </summary>\n        /// <value>The all <see cref=\"Parameter\" />s of the current item.</value>\n        /// <seealso cref=\"AllParameters\" />\n        IEnumerable<Parameter> IParameterObject.Parameters { get { return AllParameters; } }\n\n        /// <summary>\n        ///     Get the model for the current item and a specific character type.\n        /// </summary>\n        /// <param name=\"characterType\">Character type to get the model for.</param>\n        /// <param name=\"materialVersion\">When this method returns contains the variant of the model for the current item.</param>\n        /// <returns>The model for the current item and <c>characterType</c>.</returns>\n        public ModelDefinition GetModel(int characterType, out Graphics.ImcVariant variant) {\n            variant = Graphics.ImcVariant.Default;\n            var slot = EquipSlotCategory.PossibleSlots.FirstOrDefault();\n            return slot == null ? null : GetModel(slot, characterType, out variant);\n        }\n\n        /// <summary>\n        ///     Get the model for the current item and a specific character type and in a speific <see cref=\"EquipSlot\" />.\n        /// </summary>\n        /// <param name=\"equipSlot\"><see cref=\"EquipSlot\" /> for which to get the model.</param>\n        /// <param name=\"characterType\">Character type to get the model for.</param>\n        /// <param name=\"materialVersion\">When this method returns contains the variant of the model for the current item.</param>\n        /// <returns>The model for the current item and <c>characterType</c> in <c>equipSlot</c>.</returns>\n        public ModelDefinition GetModel(EquipSlot equipSlot, int characterType, out Graphics.ImcVariant variant) {\n            return equipSlot.GetModel(PrimaryModelKey, characterType, out variant);\n        }\n\n        #region Helpers\n\n        /// <summary>\n        ///     Get the maximum amount of <see cref=\"BaseParam\" /> that can be melded to the current item.\n        /// </summary>\n        /// <param name=\"baseParam\"><see cref=\"BaseParam\" /> for which to get the amount.</param>\n        /// <param name=\"onHq\">A value indicating whether bonuses for a hiqh-quality item should be taken into account.</param>\n        /// <returns>The maximum amount of <c>baseParam</c> that can be melded to the current item.</returns>\n        public int GetMateriaMeldCap(BaseParam baseParam, bool onHq) {\n            var max = GetMaximumParamValue(baseParam);\n            var present = GetParameterValue(baseParam, onHq);\n\n            return Math.Max(0, max - present);\n        }\n\n        /// <summary>\n        ///     Get the amount of <see cref=\"BaseParam\" /> present on the current item.\n        /// </summary>\n        /// <param name=\"param\"><see cref=\"BaseParam\" /> for which to get the amount.</param>\n        /// <param name=\"includeNonBase\">\n        ///     A value indicating whether to include bonuses that are not primary or the item's base\n        ///     parameter.\n        /// </param>\n        /// <returns>Returns the amount of <see cref=\"BaseParam\" /> present on the current item.</returns>\n        public int GetParameterValue(BaseParam baseParam, bool includeNonBase) {\n            var present = AllParameters.FirstOrDefault(_ => _.BaseParam == baseParam);\n            // ReSharper disable InvertIf\n            if (present == null) return 0;\n\n            if (includeNonBase)\n                return (int)present.Cast<ParameterValueFixed>().Sum(p => p.Amount);\n\n            return\n                (int)\n                present.Where(_ => _.Type == ParameterType.Base || _.Type == ParameterType.Primary)\n                       .Cast<ParameterValueFixed>()\n                       .Sum(p => p.Amount);\n        }\n\n        /// <summary>\n        ///     Get the maximum amount of a <see cref=\"BaseParam\" /> possible for the current item.\n        /// </summary>\n        /// <param name=\"baseParam\"><see cref=\"BaseParam\" /> for which to get the amount.</param>\n        /// <returns>The maximum amount of <c>baseParam</c> on the current item.</returns>\n        public int GetMaximumParamValue(BaseParam baseParam) {\n            // Base value for the param based on the item's level\n            var maxBase = ItemLevel.GetMaximum(baseParam);\n            // Factor, in percent, for the param when applied to the item's equip slot\n            var slotFactor = baseParam.GetMaximum(EquipSlotCategory);\n            // Factor, in percent, for the param when used for the item's role\n            var roleModifier = baseParam.GetModifier(BaseParamModifier);\n\n            // TODO: Not confirmed to use Round, could be Ceiling or Floor; or applied at different points\n            // Rounding appears to use AwayFromZero.  Tested with:\n            // Velveteen Work Gloves (#3601) for gathering (34.5 -> 35)\n            // Gryphonskin Ring (#4526) for wind resistance (4.5 -> 5)\n            // Fingerless Goatskin Gloves of Gathering (#3578) for GP (2.5 -> 3)\n            return (int)Math.Round(maxBase * slotFactor * roleModifier / 100000.0, MidpointRounding.AwayFromZero);\n        }\n\n        public int GetModelCharacterType() {\n            switch (EquipRestriction) {\n                case 0: return 0; // Not equippable\n                case 1: return 101; // Unrestricted, default to male hyur\n                case 2: return 101; // Any male\n                case 3: return 201; // Any female\n                case 4: return 101; // Hyur male\n                case 5: return 201; // Hyur female\n                case 6: return 501; // Elezen male\n                case 7: return 601; // Elezen female\n                case 8: return 1101; // Lalafell male\n                case 9: return 1201; // Lalafell female\n                case 10: return 701; // Miqo'te male\n                case 11: return 801; // Miqo'te female\n                case 12: return 901; // Roegadyn male\n                case 13: return 1001; // Roegadyn female\n                case 14: return 1301; // Au Ra male\n                case 15: return 1401; // Au Ra female\n                case 16: return 1501; // Hrothgar male\n                case 17: return 1801; // Viera female\n                case 18: return 1701; // Viera male\n                case 19: return 1601; // Hrothgar female\n                default:\n                    throw new NotImplementedException();\n            }\n        }\n\n        #endregion\n\n        #region Build\n\n        /// <summary>\n        ///     Build a <see cref=\"ParameterCollection\" /> for secondary parameters.\n        /// </summary>\n        /// <returns>A <see cref=\"ParameterCollection\" /> for secondary parameters.</returns>\n        protected virtual ParameterCollection BuildSecondaryParameters() {\n            var parameters = new ParameterCollection();\n\n            AddDefaultParameters(parameters);\n            AddSpecialParameters(parameters);\n\n            return parameters;\n        }\n\n        /// <summary>\n        ///     Add the default (base) parameters to a <see cref=\"ParameterCollection\" />.\n        /// </summary>\n        /// <param name=\"parameters\"><see cref=\"ParameterCollection\" /> to which to add the parameters.</param>\n        private void AddDefaultParameters(ParameterCollection parameters) {\n            const int Count = 6;\n\n            for (var i = 0; i < Count; ++i) {\n                var baseParam = As<BaseParam>(\"BaseParam\", i);\n                var value = AsInt32(\"BaseParamValue\", i);\n\n                AddParameter(parameters, ParameterType.Base, baseParam, value, i);\n            }\n        }\n\n        /// <summary>\n        ///     Add the special parameters to a <see cref=\"ParameterCollection\" />.\n        /// </summary>\n        /// <param name=\"parameters\"><see cref=\"ParameterCollection\" /> to which to add the parameters.</param>\n        private void AddSpecialParameters(ParameterCollection parameters) {\n            const int Count = 6;\n\n            ParameterType type;\n            switch (ItemSpecialBonus.Key) {\n                case 2:\n                    type = ParameterType.SetBonus;\n                    break;\n                case 4:\n                    type = ParameterType.Sanction;\n                    break;\n                case 6:\n                    type = ParameterType.SetBonusCapped;\n                    break;\n                case 7:\n                    type = ParameterType.EurekaEffect;\n                    break;\n                default:\n                    type = ParameterType.Hq;\n                    break;\n            }\n\n            for (var i = 0; i < Count; ++i) {\n                var baseParam = As<BaseParam>(\"BaseParam{Special}\", i);\n                var value = AsInt32(\"BaseParamValue{Special}\", i);\n\n                AddParameter(parameters, type, baseParam, value, i);\n            }\n        }\n\n        /// <summary>\n        ///     Attempt to add a parameter to a <see cref=\"ParameterCollection\" />.\n        /// </summary>\n        /// <param name=\"parameters\"><see cref=\"ParameterCollection\" /> to which to add the parameters.</param>\n        /// <param name=\"type\"><see cref=\"ParameterType\" /> of the parameter to be added.</param>\n        /// <param name=\"baseParam\"><see cref=\"BaseParam\" /> for which a parameter should be added.</param>\n        /// <param name=\"value\">Value of the parameter to be added.</param>\n        /// <param name=\"index\">Index of the parameter to be added.</param>\n        private static void AddParameter(ParameterCollection parameters,\n                                         ParameterType type,\n                                         BaseParam baseParam,\n                                         int value,\n                                         int index) {\n            if (baseParam.Key == 0)\n                return;\n\n            parameters.AddParameterValue(baseParam, new ParameterValueFixed(type, value, index));\n        }\n\n        #endregion\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Xiv/Items/GatheringTool.cs",
    "content": "using System.Collections.Generic;\n\nusing SaintCoinach.Ex.Relational;\n\nnamespace SaintCoinach.Xiv.Items {\n    public class GatheringTool : Weapon {\n\n        #region Constructors\n\n        #region Constructor\n\n        public GatheringTool(IXivSheet sheet, IRelationalRow sourceRow) : base(sheet, sourceRow) { }\n\n        #endregion\n\n        #endregion\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Xiv/Items/Housing.cs",
    "content": "using SaintCoinach.Ex.Relational;\n\nnamespace SaintCoinach.Xiv.Items {\n    public class Housing : Item {\n        #region Constructors\n\n        #region Constructor\n\n        public Housing(IXivSheet sheet, IRelationalRow sourceRow) : base(sheet, sourceRow) { }\n\n        #endregion\n\n        #endregion\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Xiv/Items/MagicWeapon.cs",
    "content": "using SaintCoinach.Ex.Relational;\n\nnamespace SaintCoinach.Xiv.Items {\n    public class MagicWeapon : Weapon {\n        #region Constructors\n\n        #region Constructor\n\n        public MagicWeapon(IXivSheet sheet, IRelationalRow sourceRow) : base(sheet, sourceRow) { }\n\n        #endregion\n\n        #endregion\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Xiv/Items/MateriaItem.cs",
    "content": "using System.Linq;\n\nusing SaintCoinach.Ex.Relational;\n\nnamespace SaintCoinach.Xiv.Items {\n    /// <summary>\n    ///     Class representing a materia item.\n    /// </summary>\n    public class MateriaItem : Item {\n        #region Fields\n\n        /// <summary>\n        ///     <see cref=\"BaseParam\" /> granted by the current materia item.\n        /// </summary>\n        private BaseParam _BaseParam;\n\n        /// <summary>\n        ///     Value indicating whether <see cref=\"BaseParam\" />, <see cref=\"Tier\" /> and <see cref=\"Value\" /> have been\n        ///     retrieved.\n        /// </summary>\n        private bool _IsBuilt;\n\n        /// <summary>\n        ///     Tier of the current materia item.\n        /// </summary>\n        private int _Tier;\n\n        /// <summary>\n        ///     Bonus granted to <see cref=\"BaseParam\" /> by the current materia item.\n        /// </summary>\n        private int _Value;\n\n        #endregion\n\n        #region Properties\n\n        /// <summary>\n        ///     Gets the <see cref=\"BaseParam\" /> granted by the current materia item.\n        /// </summary>\n        /// <value>The <see cref=\"BaseParam\" /> granted by the current materia item.</value>\n        public BaseParam BaseParam {\n            get {\n                Build();\n                return _BaseParam;\n            }\n        }\n\n        /// <summary>\n        ///     Gets the bonus granted to <see cref=\"BaseParam\" /> by the current materia item.\n        /// </summary>\n        /// <value>The bonus granted to <see cref=\"BaseParam\" /> by the current materia item.</value>\n        public int Value {\n            get {\n                Build();\n                return _Value;\n            }\n        }\n\n        /// <summary>\n        ///     Gets the tier of the current materia item.\n        /// </summary>\n        /// <value>The tier of the current materia item.</value>\n        public int Tier {\n            get {\n                Build();\n                return _Tier;\n            }\n        }\n\n        #endregion\n\n        #region Constructors\n\n        /// <summary>\n        ///     Initializes a new instance of the <see cref=\"MateriaItem\" /> class.\n        /// </summary>\n        /// <param name=\"sheet\"><see cref=\"IXivSheet\" /> containing this object.</param>\n        /// <param name=\"sourceRow\"><see cref=\"IRelationalRow\" /> to read data from.</param>\n        public MateriaItem(IXivSheet sheet, IRelationalRow sourceRow) : base(sheet, sourceRow) { }\n\n        #endregion\n\n        #region Build\n\n        /// <summary>\n        ///     Retrieve the <see cref=\"BaseParam\" />, <see cref=\"Value\" /> and <see cref=\"Tier\" /> from the <see cref=\"Materia\" />\n        ///     to which the current item is assigned.\n        /// </summary>\n        private void Build() {\n            if (_IsBuilt)\n                return;\n\n            var found = false;\n            var materiaSheet = Sheet.Collection.GetSheet<Materia>();\n            foreach (var materia in materiaSheet) {\n                var entry = materia.Items.FirstOrDefault(e => e.Item == this);\n                if (entry == null) continue;\n\n                _BaseParam = materia.BaseParam;\n                _Value = entry.Value;\n                _Tier = entry.Tier;\n                found = true;\n                break;\n            }\n\n            if (!found) {\n                _Tier = -1;\n                _Value = 0;\n                _BaseParam = Sheet.Collection.GetSheet<BaseParam>()[0];\n            }\n\n            _IsBuilt = true;\n        }\n\n        #endregion\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Xiv/Items/PhysicalWeapon.cs",
    "content": "using SaintCoinach.Ex.Relational;\n\nnamespace SaintCoinach.Xiv.Items {\n    public class PhysicalWeapon : Weapon {\n        #region Constructors\n\n        #region Constructor\n\n        public PhysicalWeapon(IXivSheet sheet, IRelationalRow sourceRow) : base(sheet, sourceRow) { }\n\n        #endregion\n\n        #endregion\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Xiv/Items/Shield.cs",
    "content": "using System.Collections.Generic;\n\nusing SaintCoinach.Ex.Relational;\n\nnamespace SaintCoinach.Xiv.Items {\n    /// <summary>\n    /// Class representing a shield.\n    /// </summary>\n    public class Shield : Equipment {\n        #region Fields\n\n        /// <summary>\n        ///     <see cref=\"ParameterCollection\" /> containing primary parameters of the current item.\n        /// </summary>\n        private ParameterCollection _PrimaryParameters;\n\n        #endregion\n\n        #region Properties\n\n        /// <summary>\n        ///     Gets the primary <see cref=\"Parameter\"/>s of the current item.\n        /// </summary>\n        /// <value>The primary <see cref=\"Parameter\"/>s of the current item.</value>\n        public override IEnumerable<Parameter> PrimaryParameters {\n            get { return _PrimaryParameters ?? (_PrimaryParameters = BuildPrimaryParameters()); }\n        }\n\n        /// <summary>\n        /// Gets the block strength of the current item.\n        /// </summary>\n        /// <value>The block strength of the current item.</value>\n        public int BlockStrength { get { return AsInt32(\"Block\"); } }\n        /// <summary>\n        /// Gets the block rate of the current item.\n        /// </summary>\n        /// <value>The block rate of the current item.</value>\n        public int BlockRate { get { return AsInt32(\"BlockRate\"); } }\n\n        #endregion\n\n        #region Constructors\n\n        /// <summary>\n        ///     Initializes a new instance of the <see cref=\"Shield\" /> class.\n        /// </summary>\n        /// <param name=\"sheet\"><see cref=\"IXivSheet\" /> containing this object.</param>\n        /// <param name=\"sourceRow\"><see cref=\"IRelationalRow\" /> to read data from.</param>\n        public Shield(IXivSheet sheet, IRelationalRow sourceRow) : base(sheet, sourceRow) { }\n\n        #endregion\n\n        #region Build\n\n        /// <summary>\n        ///     Build the <see cref=\"ParameterCollection\" /> containg the primary parameters of the current item.\n        /// </summary>\n        /// <returns>The <see cref=\"ParameterCollection\" /> containg the primary parameters of the current item.</returns>\n        private ParameterCollection BuildPrimaryParameters() {\n            var param = new ParameterCollection();\n\n            // XXX: Here be magic numbers\n            const int BlockStrengthKey = 18;\n            const int BlockRateKey = 17;\n\n            var paramSheet = Sheet.Collection.GetSheet<BaseParam>();\n            if (BlockStrength != 0)\n                param.AddParameterValue(paramSheet[BlockStrengthKey],\n                    new ParameterValueFixed(ParameterType.Primary, BlockStrength, 0));\n            if (BlockRate != 0)\n                param.AddParameterValue(paramSheet[BlockRateKey],\n                    new ParameterValueFixed(ParameterType.Primary, BlockRate, 0));\n\n            return param;\n        }\n\n        #endregion\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Xiv/Items/SoulCrystal.cs",
    "content": "using System.Collections.Generic;\n\nusing SaintCoinach.Ex.Relational;\n\nnamespace SaintCoinach.Xiv.Items {\n    public class SoulCrystal : Equipment {\n        #region Properties\n\n        public override IEnumerable<Parameter> PrimaryParameters { get { yield break; } }\n\n        #endregion\n\n        #region Constructors\n\n        #region Constructor\n\n        public SoulCrystal(IXivSheet sheet, IRelationalRow sourceRow) : base(sheet, sourceRow) { }\n\n        #endregion\n\n        #endregion\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Xiv/Items/Usable.cs",
    "content": "using System;\nusing System.Collections.Generic;\n\nusing SaintCoinach.Ex.Relational;\n\nnamespace SaintCoinach.Xiv.Items {\n    public class Usable : Item, IParameterObject {\n        #region Properties\n\n        public TimeSpan Cooldown { get { return TimeSpan.FromSeconds(AsDouble(\"Cooldown<s>\")); } }\n\n        #endregion\n\n        #region Constructors\n\n        #region Constructor\n\n        public Usable(IXivSheet sheet, IRelationalRow sourceRow) : base(sheet, sourceRow) { }\n\n        #endregion\n\n        #endregion\n\n        public IEnumerable<Parameter> Parameters {\n            get {\n                var actionAsParamObj = ItemAction as IParameterObject;\n                return actionAsParamObj != null ? actionAsParamObj.Parameters : new Parameter[0];\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Xiv/Items/Weapon.cs",
    "content": "using System;\nusing System.Collections.Generic;\n\nusing SaintCoinach.Ex.Relational;\n\nnamespace SaintCoinach.Xiv.Items {\n    /// <summary>\n    ///     Class representing a weapon.\n    /// </summary>\n    public class Weapon : Equipment {\n        #region Fields\n\n        /// <summary>\n        ///     <see cref=\"ParameterCollection\" /> containing primary parameters of the current item.\n        /// </summary>\n        private ParameterCollection _PrimaryParameters;\n\n        #endregion\n\n        #region Properties\n\n        /// <summary>\n        ///     Gets the primary <see cref=\"Parameter\" />s of the current item.\n        /// </summary>\n        /// <value>The primary <see cref=\"Parameter\" />s of the current item.</value>\n        public override IEnumerable<Parameter> PrimaryParameters {\n            get { return _PrimaryParameters ?? (_PrimaryParameters = BuildPrimaryParameters()); }\n        }\n\n        /// <summary>\n        ///     Gets the physical damage of the current item.\n        /// </summary>\n        /// <value>The physical damage of the current item.</value>\n        public int PhysicalDamage { get { return AsInt32(\"Damage{Phys}\"); } }\n\n        /// <summary>\n        ///     Gets the magic damage of the current item.\n        /// </summary>\n        /// <value>The magic damage of the current item.</value>\n        public int MagicDamage { get { return AsInt32(\"Damage{Mag}\"); } }\n\n        /// <summary>\n        ///     Gets the delay of the current item.\n        /// </summary>\n        /// <value>The delay of the current item.</value>\n        public TimeSpan Delay { get { return TimeSpan.FromMilliseconds(AsDouble(\"Delay<ms>\")); } }\n\n        #endregion\n\n        #region Constructors\n\n        /// <summary>\n        ///     Initializes a new instance of the <see cref=\"Weapon\" /> class.\n        /// </summary>\n        /// <param name=\"sheet\"><see cref=\"IXivSheet\" /> containing this object.</param>\n        /// <param name=\"sourceRow\"><see cref=\"IRelationalRow\" /> to read data from.</param>\n        public Weapon(IXivSheet sheet, IRelationalRow sourceRow) : base(sheet, sourceRow) { }\n\n        #endregion\n\n        #region Build\n\n        /// <summary>\n        ///     Build the <see cref=\"ParameterCollection\" /> containg the primary parameters of the current item.\n        /// </summary>\n        /// <returns>The <see cref=\"ParameterCollection\" /> containg the primary parameters of the current item.</returns>\n        private ParameterCollection BuildPrimaryParameters() {\n            var param = new ParameterCollection();\n\n            // XXX: Here be magic numbers\n            const int PhysicalDamageKey = 12;\n            const int MagicDamageKey = 13;\n            const int DelayKey = 14;\n\n            var paramSheet = Sheet.Collection.GetSheet<BaseParam>();\n            if (PhysicalDamage != 0)\n                param.AddParameterValue(paramSheet[PhysicalDamageKey],\n                    new ParameterValueFixed(ParameterType.Primary, PhysicalDamage, 0));\n            if (MagicDamage != 0)\n                param.AddParameterValue(paramSheet[MagicDamageKey],\n                    new ParameterValueFixed(ParameterType.Primary, MagicDamage, 0));\n            param.AddParameterValue(paramSheet[DelayKey],\n                new ParameterValueFixed(ParameterType.Primary, Delay.TotalSeconds, 0));\n\n            return param;\n        }\n\n        #endregion\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Xiv/JournalCategory.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nusing SaintCoinach.Ex.Relational;\n\nnamespace SaintCoinach.Xiv {\n    public class JournalCategory : XivRow {\n        #region Properties\n\n        public Text.XivString Name => AsString(\"Name\");\n        public JournalSection JournalSection => As<JournalSection>();\n\n        #endregion\n\n        #region Constructors\n\n        public JournalCategory(IXivSheet sheet, IRelationalRow sourceRow) : base(sheet, sourceRow) { }\n\n        #endregion\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Xiv/JournalGenre.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nusing SaintCoinach.Ex.Relational;\n\nnamespace SaintCoinach.Xiv {\n    public class JournalGenre : XivRow {\n        #region Properties\n\n        public Text.XivString Name { get { return AsString(\"Name\"); } }\n\n        public JournalCategory JournalCategory { get { return As<JournalCategory>(); } }\n\n        public Imaging.ImageFile Icon { get { return AsImage(\"Icon\"); } }\n\n        #endregion\n\n        #region Constructors\n\n        public JournalGenre(IXivSheet sheet, IRelationalRow sourceRow) : base(sheet, sourceRow) { }\n\n        #endregion\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Xiv/JournalSection.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nusing SaintCoinach.Ex.Relational;\n\nnamespace SaintCoinach.Xiv {\n    public class JournalSection : XivRow {\n        #region Properties\n\n        public Text.XivString Name { get { return AsString(\"Name\"); } }\n\n        #endregion\n\n        #region Constructors\n\n        public JournalSection(IXivSheet sheet, IRelationalRow sourceRow) : base(sheet, sourceRow) { }\n\n        #endregion\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Xiv/Leve.cs",
    "content": "using System;\nusing System.Collections.Generic;\nusing System.Linq;\n\nusing SaintCoinach.Ex.Relational;\nusing SaintCoinach.Imaging;\n\nnamespace SaintCoinach.Xiv {\n    /// <summary>\n    ///     Class representing a Levequest.\n    /// </summary>\n    public class Leve : XivRow, ILocatable, IItemSource {\n        #region Properties\n\n        /// <summary>\n        ///     Gets the name of the current leve.\n        /// </summary>\n        /// <value>The name of the current leve.</value>\n        public Text.XivString Name { get { return AsString(\"Name\"); } }\n\n        /// <summary>\n        ///     Gets the name of the current leve.\n        /// </summary>\n        /// <value>The name of the current leve.</value>\n        public Text.XivString Description { get { return AsString(\"Description\"); } }\n\n        /// <summary>\n        ///     Gets the <see cref=\"LeveClient\" /> of the current leve.\n        /// </summary>\n        /// <value>The <see cref=\"LeveClient\" /> of the current leve.</value>\n        public LeveClient LeveClient { get { return As<LeveClient>(); } }\n\n        /// <summary>\n        ///     Gets the <see cref=\"LeveAssignmentType\" /> of the current leve.\n        /// </summary>\n        /// <value>The <see cref=\"LeveAssignmentType\" /> of the current leve.</value>\n        public LeveAssignmentType LeveAssignmentType { get { return As<LeveAssignmentType>(); } }\n\n        /// <summary>\n        ///     Gets the <see cref=\"ClassJobCategory\" /> for the current leve.\n        /// </summary>\n        /// <value>The <see cref=\"ClassJobCategory\" /> for the current leve.</value>\n        public ClassJobCategory ClassJobCategory { get { return As<ClassJobCategory>(); } }\n\n        /// <summary>\n        ///     Gets the level of the current leve.\n        /// </summary>\n        /// <value>The level of the current leve.</value>\n        public int ClassJobLevel { get { return AsInt32(\"ClassJobLevel\"); } }\n\n        /// <summary>\n        ///     Gets the <see cref=\"PlaceName\" /> the current leve starts.\n        /// </summary>\n        /// <value>The <see cref=\"PlaceName\" /> the current leve starts.</value>\n        public PlaceName PlaceNameStart { get { return As<PlaceName>(\"PlaceName{Start}\"); } }\n\n        /// <summary>\n        ///     Gets the <see cref=\"PlaceName\" /> the current leve is issued.\n        /// </summary>\n        /// <value>The <see cref=\"PlaceName\" /> where the current leve is issued.</value>\n        public PlaceName PlaceNameIssued { get { return As<PlaceName>(\"PlaceName{Issued}\"); } }\n\n        /// <summary>\n        ///     Gets the <see cref=\"PlaceName\" /> of the zone the current leve takes place in.\n        /// </summary>\n        /// <value>The <see cref=\"PlaceName\" /> of the zone the current leve takes place in.</value>\n        public PlaceName PlaceNameStartZone { get { return As<PlaceName>(\"PlaceName{StartZone}\"); } }\n\n        /// <summary>\n        ///     Gets the icon of the city state that issued the current leve.\n        /// </summary>\n        /// <value>The icon of the city state that issued the current leve.</value>\n        public ImageFile CityStateIcon { get { return AsImage(\"Icon{CityState}\"); } }\n\n        /// <summary>\n        ///     Gets the integer key of the object containing additional information.\n        /// </summary>\n        /// <value>The integer key of the object containing additional information.</value>\n        public int DataId { get { return AsInt32(\"DataId\"); } }\n\n        /// <summary>\n        ///     Gets the <see cref=\"LeveRewardItem\" /> the current leve offers.\n        /// </summary>\n        /// <value>The <see cref=\"LeveRewardItem\" /> the current leve offers.</value>\n        public LeveRewardItem LeveRewardItem { get { return As<LeveRewardItem>(); } }\n\n        /// <summary>\n        ///     Gets the experience reward of the current leve.\n        /// </summary>\n        /// <value>The experience reward of the current leve.</value>\n        public int ExpReward { get { return AsInt32(\"ExpReward\"); } }\n\n        /// <summary>\n        ///     Gets the gil reward of the current leve.\n        /// </summary>\n        /// <value>The gil reward of the current leve.</value>\n        public int GilReward { get { return AsInt32(\"GilReward\"); } }\n\n        /// <summary>\n        ///     Gets the <see cref=\"Level\" /> object for the levemete of the current leve.\n        /// </summary>\n        /// <value>The <see cref=\"Level\" /> object for the levemete of the current leve.</value>\n        public Level LevemeteLevel { get { return As<Level>(\"Level{Levemete}\"); } }\n\n        /// <summary>\n        ///     Gets the <see cref=\"Level\" /> object for the start of the current leve.\n        /// </summary>\n        /// <value>The <see cref=\"Level\" /> object for the start of the current leve.</value>\n        public Level StartLevel { get { return As<Level>(\"Level{Start}\"); } }\n\n        /// <summary>\n        ///     Gets the icon for the location of the current leve.\n        /// </summary>\n        /// <value>The icon for the location of the current leve.</value>\n        public ImageFile IssuerIcon { get { return AsImage(\"Icon{Issuer}\"); } }\n\n        /// <summary>\n        /// Gets the locations of the current object.\n        /// </summary>\n        /// <value>The locations of the current object.</value>\n        public IEnumerable<ILocation> Locations {\n            get {\n                yield return this.LevemeteLevel;\n                if (StartLevel != null && StartLevel.Key != 0)\n                    yield return this.StartLevel;\n            }\n        }\n\n        /// <summary>\n        /// Gets the <see cref=\"LeveVfx\"/> used for the current leve.\n        /// </summary>\n        /// <value>The <see cref=\"LeveVfx\"/> used for the current leve.</value>\n        public LeveVfx LeveVfx { get { return As<LeveVfx>(); } }\n\n        /// <summary>\n        /// Gets the plate icon used for the current leve.\n        /// </summary>\n        /// <value>The plate icon used for the current leve.</value>\n        public Imaging.ImageFile PlateIcon { get { return LeveVfx.Icon; } }\n\n        /// <summary>\n        /// Gets the <see cref=\"LeveVfx\"/> used for the current leve.\n        /// </summary>\n        /// <value>The <see cref=\"LeveVfx\"/> used for the current leve.</value>\n        public LeveVfx FrameLeveVfx { get { return As<LeveVfx>(\"LeveVfx{Frame}\"); } }\n\n        /// <summary>\n        /// Gets the plate icon used for the current leve.\n        /// </summary>\n        /// <value>The plate icon used for the current leve.</value>\n        public Imaging.ImageFile FrameIcon { get { return FrameLeveVfx.Icon; } }\n\n        #endregion\n\n        #region Constructors\n\n        /// <summary>\n        ///     Initializes a new instance of the <see cref=\"Leve\" /> class.\n        /// </summary>\n        /// <param name=\"sheet\"><see cref=\"IXivSheet\" /> containing this object.</param>\n        /// <param name=\"sourceRow\"><see cref=\"IRelationalRow\" /> to read data from.</param>\n        public Leve(IXivSheet sheet, IRelationalRow sourceRow) : base(sheet, sourceRow) { }\n\n        #endregion\n\n        /// <summary>\n        ///     Returns a string representation of the current leve.\n        /// </summary>\n        /// <returns>The value of <see cref=\"Name\" />.</returns>\n        public override string ToString() {\n            return Name;\n        }\n\n        #region IItemSource Members\n\n        private Item[] _ItemSourceItems;\n        public IEnumerable<Item> Items {\n            get { return _ItemSourceItems ?? (_ItemSourceItems = LeveRewardItem.ItemGroups.SelectMany(g => g.Value.Items.Select(v => v.Item)).ToArray()); }\n        }\n\n        #endregion\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Xiv/LeveAssignmentType.cs",
    "content": "using SaintCoinach.Ex.Relational;\nusing SaintCoinach.Imaging;\n\nnamespace SaintCoinach.Xiv {\n    public class LeveAssignmentType : XivRow {\n        #region Properties\n\n        public Text.XivString Name { get { return AsString(\"Name\"); } }\n        public bool IsFaction { get { return AsBoolean(\"IsFaction\"); } }\n        public ImageFile Icon { get { return AsImage(\"Icon\"); } }\n\n        #endregion\n\n        #region Constructors\n\n        #region Constructor\n\n        public LeveAssignmentType(IXivSheet sheet, IRelationalRow sourceRow) : base(sheet, sourceRow) { }\n\n        #endregion\n\n        #endregion\n\n        public override string ToString() {\n            return Name;\n        }\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Xiv/LeveClient.cs",
    "content": "using SaintCoinach.Ex.Relational;\n\nnamespace SaintCoinach.Xiv {\n    public class LeveClient : XivRow {\n        #region Properties\n\n        public Text.XivString Name { get { return AsString(\"Name\"); } }\n\n        #endregion\n\n        #region Constructors\n\n        #region Constructor\n\n        public LeveClient(IXivSheet sheet, IRelationalRow sourceRow) : base(sheet, sourceRow) { }\n\n        #endregion\n\n        #endregion\n\n        public override string ToString() {\n            return Name;\n        }\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Xiv/LeveRewardItem.cs",
    "content": "using System.Collections.Generic;\n\nusing SaintCoinach.Ex.Relational;\n\nnamespace SaintCoinach.Xiv {\n    /// <summary>\n    /// Class representing possible item reward groups for leves.\n    /// </summary>\n    public class LeveRewardItem : XivRow {\n        #region Fields\n\n        /// <summary>\n        /// The <see cref=\"LeveRewardItemGroup\"/>s and their chance of appearing for the current reward.\n        /// </summary>\n        private ProbabilityPair<LeveRewardItemGroup>[] _ItemGroups;\n\n        #endregion\n\n        #region Properties\n\n        /// <summary>\n        /// Gets the <see cref=\"LeveRewardItemGroup\"/>s and their chance of appearing for the current reward.\n        /// </summary>\n        /// <value>The <see cref=\"LeveRewardItemGroup\"/>s and their chance of appearing for the current reward.</value>\n        public IEnumerable<ProbabilityPair<LeveRewardItemGroup>> ItemGroups {\n            get { return _ItemGroups ?? (_ItemGroups = BuildItemGroups()); }\n        }\n\n        #endregion\n\n        #region Constructors\n\n        /// <summary>\n        ///     Initializes a new instance of the <see cref=\"LeveRewardItem\" /> class.\n        /// </summary>\n        /// <param name=\"sheet\"><see cref=\"IXivSheet\" /> containing this object.</param>\n        /// <param name=\"sourceRow\"><see cref=\"IRelationalRow\" /> to read data from.</param>\n        public LeveRewardItem(IXivSheet sheet, IRelationalRow sourceRow) : base(sheet, sourceRow) { }\n\n        #endregion\n\n        #region Build\n        /// <summary>\n        /// Build an array of the <see cref=\"LeveRewardItemGroup\"/>s and their chance of appearing for the current reward.\n        /// </summary>\n        /// <returns>An array of the <see cref=\"LeveRewardItemGroup\"/>s and their chance of appearing for the current reward.</returns>\n        private ProbabilityPair<LeveRewardItemGroup>[] BuildItemGroups() {\n            const int Count = 8;\n\n            var itemGroups = new List<ProbabilityPair<LeveRewardItemGroup>>();\n            for (var i = 0; i < Count; ++i) {\n                var probability = AsInt32(\"Probability<%>\", i);\n                if (probability == 0)\n                    continue;\n\n                var group = As<LeveRewardItemGroup>(i);\n                if (group.Key == 0)\n                    continue;\n\n                itemGroups.Add(new ProbabilityPair<LeveRewardItemGroup>(group, probability / 100.0));\n            }\n\n            return itemGroups.ToArray();\n        }\n\n        #endregion\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Xiv/LeveRewardItemGroup.cs",
    "content": "using System.Collections.Generic;\n\nusing SaintCoinach.Ex.Relational;\n\nnamespace SaintCoinach.Xiv {\n    /// <summary>\n    ///     Class representing a group of items that can appear as rewards for leves.\n    /// </summary>\n    /// <remarks>\n    ///     This is not confirmed, but it seems like all entries in a group have the same chance of appearing if the group is\n    ///     picked.\n    /// </remarks>\n    public class LeveRewardItemGroup : XivRow {\n        #region Fields\n\n        /// <summary>\n        ///     The entries in the current group.\n        /// </summary>\n        private Entry[] _Items;\n\n        #endregion\n\n        #region Properties\n\n        /// <summary>\n        ///     Gets the entries in the current group.\n        /// </summary>\n        /// <value>The entries in the current group.</value>\n        public IEnumerable<Entry> Items { get { return _Items ?? (_Items = BuildItems()); } }\n\n        #endregion\n\n        #region Constructors\n\n        /// <summary>\n        ///     Initializes a new instance of the <see cref=\"LeveRewardItemGroup\" /> class.\n        /// </summary>\n        /// <param name=\"sheet\"><see cref=\"IXivSheet\" /> containing this object.</param>\n        /// <param name=\"sourceRow\"><see cref=\"IRelationalRow\" /> to read data from.</param>\n        public LeveRewardItemGroup(IXivSheet sheet, IRelationalRow sourceRow) : base(sheet, sourceRow) { }\n\n        #endregion\n\n        #region Build\n\n        /// <summary>\n        ///     Build an array of entries in the current <see cref=\"LeveRewardItemGroup\" />.\n        /// </summary>\n        /// <returns>An array of entries in the current <see cref=\"LeveRewardItemGroup\" />.</returns>\n        private Entry[] BuildItems() {\n            const int Count = 9;\n\n            var items = new List<Entry>();\n            for (var i = 0; i < Count; ++i) {\n                var count = AsInt32(\"Count\", i);\n                if (count == 0)\n                    continue;\n\n                var item = As<Item>(\"Item\", i);\n                if (item.Key == 0)\n                    continue;\n\n                var hq = AsBoolean(\"HQ\", i);\n\n                items.Add(new Entry(item, count, hq));\n            }\n\n            return items.ToArray();\n        }\n\n        #endregion\n\n        #region Helper class\n\n        /// <summary>\n        ///     Class representing an entry in a <see cref=\"LeveRewardItemGroup\" />.\n        /// </summary>\n        public class Entry {\n            #region Properties\n\n            /// <summary>\n            ///     Gets the item for the current entry.\n            /// </summary>\n            /// <value>The item for the current entry.</value>\n            public Item Item { get; private set; }\n\n            /// <summary>\n            ///     Gets the item count for the current entry.\n            /// </summary>\n            /// <value>The item count for the current entry.</value>\n            public int Count { get; private set; }\n\n            /// <summary>\n            ///     Gets a value indicating whether the <see cref=\"Item\" /> is high-quality.\n            /// </summary>\n            /// <value>A value indicating whether the <see cref=\"Item\" /> is high-quality.</value>\n            public bool IsHq { get; private set; }\n\n            #endregion\n\n            #region Constructors\n\n            /// <summary>\n            ///     Initializes a new instance of the <see cref=\"Entry\" /> class.\n            /// </summary>\n            /// <param name=\"item\"><see cref=\"Item\" /> for the entry.</param>\n            /// <param name=\"count\">Item count of the entry.</param>\n            /// <param name=\"isHq\">A value indicating whether the <c>item</c> is high-quality.</param>\n            public Entry(Item item, int count, bool isHq) {\n                Item = item;\n                Count = count;\n                IsHq = isHq;\n            }\n\n            #endregion\n        }\n\n        #endregion\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Xiv/LeveVfx.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nusing SaintCoinach.Ex.Relational;\n\nnamespace SaintCoinach.Xiv {\n    public class LeveVfx : XivRow {\n        #region Properties\n\n        public Text.XivString Effect { get { return AsString(\"Effect\"); } }\n\n        public Imaging.ImageFile Icon { get { return AsImage(\"Icon\"); } }\n\n        #endregion\n\n        #region Constructors\n\n        public LeveVfx(IXivSheet sheet, IRelationalRow sourceRow) : base(sheet, sourceRow) { }\n\n        #endregion\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Xiv/Level.cs",
    "content": "using SaintCoinach.Ex.Relational;\n\nnamespace SaintCoinach.Xiv {\n    /// <summary>\n    ///     Class representing a location in the game.\n    /// </summary>\n    public class Level : XivRow, ILocation {\n        #region Properties\n\n        /// <summary>\n        ///     Gets the X-coordinate in world-space.\n        /// </summary>\n        /// <value>The X-coordinate in world-space.</value>\n        public float X { get { return AsSingle(\"X\"); } }\n\n        /// <summary>\n        ///     Gets the Y-coordinate in world-space.\n        /// </summary>\n        /// <value>The Y-coordinate in world-space.</value>\n        public float Y { get { return AsSingle(\"Y\"); } }\n\n        /// <summary>\n        ///     Gets the Z-coordinate in world-space.\n        /// </summary>\n        /// <value>The Z-coordinate in world-space.</value>\n        public float Z { get { return AsSingle(\"Z\"); } }\n\n        /// <summary>\n        ///     Gets the X-coordinate on the 2D-map.\n        /// </summary>\n        /// <value>The X-coordinate on the 2D-map.</value>\n        public double MapX { get { return Map.ToMapCoordinate3d(X, Map.OffsetX); } }\n\n        /// <summary>\n        ///     Gets the Y-coordinate on the 2D-map.\n        /// </summary>\n        /// <value>The Y-coordinate on the 2D-map.</value>\n        public double MapY { get { return Map.ToMapCoordinate3d(Z, Map.OffsetY); } }\n\n        /// <summary>\n        ///     Gets the yaw of the current location.\n        /// </summary>\n        /// <value>The yaw of the current location.</value>\n        public float Yaw { get { return AsSingle(\"Yaw\"); } }\n\n        /// <summary>\n        ///     Gets the radius of the current location.\n        /// </summary>\n        /// <value>The radius of the current location.</value>\n        public float Radius { get { return AsSingle(\"Radius\"); } }\n\n        /// <summary>\n        ///     Gets the LgbEntryType of the current location.\n        /// </summary>\n        /// <value>The LgbEntryType of the current location.</value>\n        public int Type { get { return AsInt32(\"Type\"); } }\n\n        /// <summary>\n        ///     Gets the key of the object associated with the current location.\n        /// </summary>\n        /// <value>The key of the object associated with the current location.</value>\n        public IXivRow Object => As<IXivRow>(\"Object\");\n\n        /// <summary>\n        ///     Gets the <see cref=\"Map\" /> the current location is in.\n        /// </summary>\n        /// <value>The <see cref=\"Map\" /> the current location is in.</value>\n        public Map Map { get { return As<Map>(); } }\n\n        /// <summary>\n        /// Gets the <see cref=\"Map\"/> for the current object.\n        /// </summary>\n        /// <value>The <see cref=\"Map\"/> for the current object.</value>\n        PlaceName ILocation.PlaceName { get { return Map.PlaceName; } }\n\n        #endregion\n\n        #region Constructors\n\n        /// <summary>\n        ///     Initializes a new instance of the <see cref=\"Level\" /> class.\n        /// </summary>\n        /// <param name=\"sheet\"><see cref=\"IXivSheet\" /> containing this object.</param>\n        /// <param name=\"sourceRow\"><see cref=\"IRelationalRow\" /> to read data from.</param>\n        public Level(IXivSheet sheet, IRelationalRow sourceRow) : base(sheet, sourceRow) { }\n\n        #endregion\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Xiv/LogFilter.cs",
    "content": "using SaintCoinach.Ex.Relational;\n\nnamespace SaintCoinach.Xiv {\n    public class LogFilter : XivRow {\n        #region Properties\n\n        public Text.XivString Name { get { return AsString(\"Name\"); } }\n        public LogKind LogKind { get { return As<LogKind>(); } }\n        public Text.XivString Example { get { return AsString(\"Example\"); } }\n\n        #endregion\n\n        #region Constructors\n\n        #region Constructor\n\n        public LogFilter(IXivSheet sheet, IRelationalRow sourceRow) : base(sheet, sourceRow) { }\n\n        #endregion\n\n        #endregion\n\n        public override string ToString() {\n            return Name;\n        }\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Xiv/LogKind.cs",
    "content": "using SaintCoinach.Ex.Relational;\n\nnamespace SaintCoinach.Xiv {\n    public class LogKind : XivRow {\n        #region Properties\n\n        public Text.XivString Format { get { return AsString(\"Format\"); } }\n        public Text.XivString Name { get { return AsString(\"Name\"); } }\n        public Text.XivString Example { get { return AsString(\"Example\"); } }\n\n        #endregion\n\n        #region Constructors\n\n        #region Constructor\n\n        public LogKind(IXivSheet sheet, IRelationalRow sourceRow) : base(sheet, sourceRow) { }\n\n        #endregion\n\n        #endregion\n\n        public override string ToString() {\n            return Name;\n        }\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Xiv/LogKindCategoryText.cs",
    "content": "using SaintCoinach.Ex.Relational;\n\nnamespace SaintCoinach.Xiv {\n    public class LogKindCategoryText : XivRow {\n        #region Properties\n\n        public Text.XivString Text { get { return AsString(\"Text\"); } }\n\n        #endregion\n\n        #region Constructors\n\n        #region Constructor\n\n        public LogKindCategoryText(IXivSheet sheet, IRelationalRow sourceRow) : base(sheet, sourceRow) { }\n\n        #endregion\n\n        #endregion\n\n        public override string ToString() {\n            return Text;\n        }\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Xiv/LogMessage.cs",
    "content": "using SaintCoinach.Ex.Relational;\n\nnamespace SaintCoinach.Xiv {\n    public class LogMessage : XivRow {\n        #region Properties\n\n        public Text.XivString Text { get { return AsString(\"Text\"); } }\n        public LogKind LogKind { get { return As<LogKind>(); } }\n\n        #endregion\n\n        #region Constructors\n\n        #region Constructor\n\n        public LogMessage(IXivSheet sheet, IRelationalRow sourceRow) : base(sheet, sourceRow) { }\n\n        #endregion\n\n        #endregion\n\n        public override string ToString() {\n            return Text;\n        }\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Xiv/MYCWarResultNotebook.cs",
    "content": "﻿using SaintCoinach.Ex.Relational;\nusing SaintCoinach.Imaging;\n\nnamespace SaintCoinach.Xiv {\n    // This is for bozja field notes\n    public class MYCWarResultNotebook : XivRow {\n        #region Properties\n        public Text.XivString Name { get { return AsString(\"Name\"); } }\n        public Text.XivString Description { get { return AsString(\"Description\"); } }\n\n        public ImageFile Icon { get { return AsImage(\"Icon\"); } }\n        public ImageFile Image { get { return AsImage(\"Image\"); } }\n\n        public short Rarity { get { return AsInt16(\"Rarity\"); } }\n        #endregion\n\n        #region Constructor\n        public MYCWarResultNotebook(IXivSheet sheet, IRelationalRow sourceRow) : base(sheet, sourceRow) { }\n        #endregion\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Xiv/MainCommand.cs",
    "content": "using SaintCoinach.Ex.Relational;\nusing SaintCoinach.Imaging;\n\nnamespace SaintCoinach.Xiv {\n    public class MainCommand : XivRow {\n        #region Properties\n\n        public Text.XivString Name { get { return AsString(\"Name\"); } }\n        public Text.XivString Description { get { return AsString(\"Description\"); } }\n        public ImageFile Icon { get { return AsImage(\"Icon\"); } }\n        public MainCommandCategory MainCommandCategory { get { return As<MainCommandCategory>(); } }\n\n        #endregion\n\n        #region Constructors\n\n        #region Constructor\n\n        public MainCommand(IXivSheet sheet, IRelationalRow sourceRow) : base(sheet, sourceRow) { }\n\n        #endregion\n\n        #endregion\n\n        public override string ToString() {\n            return Name;\n        }\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Xiv/MainCommandCategory.cs",
    "content": "using SaintCoinach.Ex.Relational;\n\nnamespace SaintCoinach.Xiv {\n    public class MainCommandCategory : XivRow {\n        #region Properties\n\n        public Text.XivString Name { get { return AsString(\"Name\"); } }\n\n        #endregion\n\n        #region Constructors\n\n        #region Constructor\n\n        public MainCommandCategory(IXivSheet sheet, IRelationalRow sourceRow) : base(sheet, sourceRow) { }\n\n        #endregion\n\n        #endregion\n\n        public override string ToString() {\n            return Name;\n        }\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Xiv/Map.cs",
    "content": "using System;\nusing SaintCoinach.Ex.Relational;\nusing SaintCoinach.Imaging;\nusing System.Drawing;\nusing System.Drawing.Imaging;\n\nnamespace SaintCoinach.Xiv {\n    /// <summary>\n    ///     Class representing a map.\n    /// </summary>\n    public class Map : XivRow {\n        #region Fields\n        private WeakReference<Image> _MediumImage;\n        private WeakReference<Image> _SmallImage;\n        #endregion\n\n        #region Properties\n\n        public sbyte Index => As<sbyte>(\"MapIndex\");\n\n        /// <summary>\n        ///     Gets the identifier string of the current map.\n        /// </summary>\n        /// <value>The identifier string of the current map.</value>\n        public Text.XivString Id { get { return AsString(\"Id\"); } }\n\n        /// <summary>\n        ///     Gets the hierarchy level of the current map.\n        /// </summary>\n        /// <value>The hierarchy level of the current map.</value>\n        public int Hierarchy { get { return AsInt32(\"Hierarchy\"); } }\n\n        /// <summary>\n        ///     Gets the MapMarker parent key range of the current map.\n        /// </summary>\n        /// <value>The MapMarker parent key range of the current map.</value>\n        public int MapMarkerRange {  get { return AsInt32(\"MapMarkerRange\"); } }\n\n        /// <summary>\n        ///     Gets the size factor of the current map.\n        /// </summary>\n        /// <remarks>\n        ///     Base map size 41 units, the value of <c>SizeFactor</c> indicates by how much to divide this to get the size of the\n        ///     current map.\n        /// </remarks>\n        /// <value>The size of the current map.</value>\n        public int SizeFactor { get { return AsInt32(\"SizeFactor\"); } }\n\n        /// <summary>\n        ///     Gets the X value offset of the current map.\n        /// </summary>\n        public int OffsetX { get { return AsInt32(\"Offset{X}\"); } }\n\n        /// <summary>\n        ///     Gets the Y value offset of the current map.\n        /// </summary>\n        public int OffsetY { get { return AsInt32(\"Offset{Y}\"); } }\n\n        /// <summary>\n        ///     Gets the <see cref=\"PlaceName\" /> of the region the current map is in.\n        /// </summary>\n        /// <value>The <see cref=\"PlaceName\" /> of the region the current map is in.</value>\n        public PlaceName RegionPlaceName { get { return As<PlaceName>(\"PlaceName{Region}\"); } }\n\n        /// <summary>\n        ///     Gets the <see cref=\"PlaceName\" /> of current map is in.\n        /// </summary>\n        /// <value>The <see cref=\"PlaceName\" /> of current map is in.</value>\n        public PlaceName PlaceName { get { return As<PlaceName>(); } }\n\n        /// <summary>\n        ///     Gets the <see cref=\"PlaceName\" /> of the more specific location the current map is in.\n        /// </summary>\n        /// <value>The <see cref=\"PlaceName\" /> of the more specific location the current map is in.</value>\n        public PlaceName LocationPlaceName { get { return As<PlaceName>(\"PlaceName{Sub}\"); } }\n\n        /// <summary>\n        ///     Gets the <see cref=\"TerritoryType\" /> for the current map.\n        /// </summary>\n        /// <value>The <see cref=\"TerritoryType\" /> for the current map.</value>\n        public TerritoryType TerritoryType { get { return As<TerritoryType>(); } }\n\n        /// <summary>\n        ///     Gets the masked medium <see cref=\"Image\" /> of the current map.\n        /// </summary>\n        public Image MediumImage {\n            get {\n                if (_MediumImage != null && _MediumImage.TryGetTarget(out var image))\n                    return image;\n                image = BuildImage(\"m\");\n                if (_MediumImage == null)\n                    _MediumImage = new WeakReference<Image>(image);\n                else\n                    _MediumImage.SetTarget(image);\n                return image;\n            }\n        }\n\n        /// <summary>\n        ///     Gets the masked small <see cref=\"Image\" /> of the current map.\n        /// </summary>\n        public Image SmallImage {\n            get {\n                if (_SmallImage != null && _SmallImage.TryGetTarget(out var image))\n                    return image;\n                image = BuildImage(\"s\");\n                if (_SmallImage == null)\n                    _SmallImage = new WeakReference<Image>(image);\n                else\n                    _SmallImage.SetTarget(image);\n                return image;\n            }\n        }\n\n        #endregion\n\n        #region Constructors\n\n        /// <summary>\n        ///     Initializes a new instance of the <see cref=\"Map\" /> class.\n        /// </summary>\n        /// <param name=\"sheet\"><see cref=\"IXivSheet\" /> containing this object.</param>\n        /// <param name=\"sourceRow\"><see cref=\"IRelationalRow\" /> to read data from.</param>\n        public Map(IXivSheet sheet, IRelationalRow sourceRow) : base(sheet, sourceRow) { }\n\n        #endregion\n\n        #region Graphics\n        public Graphics.Territory GetTerritory() {\n            if (TerritoryType == null || TerritoryType.Key == 0)\n                return null;\n\n            var t = new Graphics.Territory(this.TerritoryType);\n            if (t.Terrain == null && t.LgbFiles.Length == 0)\n                return null;\n            return t;\n        }\n        #endregion\n\n        #region Build\n        private Image BuildImage(string size) {\n            const string MapFileFormat = \"ui/map/{0}/{1}{2}_{3}.tex\";\n\n            if (string.IsNullOrEmpty(Id))\n                return null;\n\n            var fileName = Id.ToString().Replace(\"/\", \"\");\n            var pack = Sheet.Collection.PackCollection;\n\n            var filePath = string.Format(MapFileFormat, Id, fileName, string.Empty, size);\n            if (!pack.TryGetFile(filePath, out var file))\n                return null;\n\n            var imageFile = new ImageFile(file.Pack, file.CommonHeader);\n\n            var maskPath = string.Format(MapFileFormat, Id, fileName, \"m\", size);\n            if (pack.TryGetFile(maskPath, out var mask))\n            {\n                // Multiply the mask against the file.\n                var maskFile = new ImageFile(mask.Pack, mask.CommonHeader);\n                return MultiplyBlend(imageFile, maskFile);\n            }\n\n            return imageFile.GetImage();\n        }\n\n        private static Image MultiplyBlend(ImageFile image, ImageFile mask) {\n            if (image.Width != mask.Width || image.Height != mask.Height)\n                throw new ArgumentException();\n            // Using 32bit color\n            const int BytesPerPixel = 4;\n\n            var aArgb = Imaging.ImageConverter.GetA8R8G8B8(image);\n            var bArgb = Imaging.ImageConverter.GetA8R8G8B8(mask);\n            var result = new byte[aArgb.Length];\n\n            for (var i = 0; i < aArgb.Length; i += BytesPerPixel) {\n                // There are other algorithms that can do this with any alpha,\n                // but I haven't the time to research them now.\n                var bAlpha = bArgb[i + 3];\n                if (bAlpha == 0) {\n                    // Mask pixel is transparent, do not blend.\n                    result[i] = aArgb[i];\n                    result[i + 1] = aArgb[i + 1];\n                    result[i + 2] = aArgb[i + 2];\n                } else { \n                    for (var j = 0; j < 3; ++j)     // Only blend RGB\n                        result[i + j] = (byte)((aArgb[i + j] * bArgb[i + j]) / 255);\n                }\n                result[i + 3] = aArgb[i + 3];  // Preserve alpha\n            }\n\n            Image output;\n            unsafe {\n                fixed (byte* p = result) {\n                    var ptr = (IntPtr)p;\n                    using (var tempImage = new Bitmap(image.Width, image.Height, image.Width * BytesPerPixel, PixelFormat.Format32bppArgb, ptr))\n                        output = new Bitmap(tempImage);\n                }\n            }\n            return output;\n        }\n        #endregion\n\n        #region Coordinates\n        /// <summary>\n        ///     Convert a X- or Y-coordinate into an offset, map-scaled 2D-coordinate.\n        /// </summary>\n        /// <param name=\"value\">The coordinate to convert.</param>\n        /// <param name=\"offset\">The coordinate offset from this map.</param>\n        /// <returns><c>value</c> converted and scaled to this map.</returns>\n        public double ToMapCoordinate2d(int value, int offset)\n        {\n            var c = SizeFactor / 100.0;\n            var offsetValue = value + offset;\n            return (41.0 / c) * (offsetValue / 2048.0) + 1;\n        }\n\n        /// <summary>\n        ///     Convert a X- or Z-coordinate from world-space into its 2D-coordinate.\n        /// </summary>\n        /// <param name=\"value\">The coordinate in world-space to convert.</param>\n        /// <param name=\"offset\">The coordinate offset from this map in world-space.</param>\n        /// <returns><c>value</c> converted into 2D-space.</returns>\n        public double ToMapCoordinate3d(double value, int offset)\n        {\n            var c = SizeFactor / 100.0;\n            var offsetValue = (value + offset) * c;\n            return ((41.0 / c) * ((offsetValue + 1024.0) / 2048.0)) + 1;\n        }\n        #endregion\n\n        /// <summary>\n        ///     Returns a string representation of the map.\n        /// </summary>\n        /// <returns>The value of <see cref=\"Id\" />.</returns>\n        public override string ToString() {\n            return Id;\n        }\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Xiv/MasterpieceSupplyDuty.CollectableItem.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace SaintCoinach.Xiv {\n    public partial class MasterpieceSupplyDuty {\n        public class CollectableItem {\n            private double _xpMultiplier, _xpMultiplier2;\n            private double _currencyMultiplier, _currencyMultiplier2;\n\n            #region Properties\n            public MasterpieceSupplyDuty MasterpieceSupplyDuty { get; private set; }\n            public Item RequiredItem { get; private set; }\n            public int Quantity { get; private set; }\n            public int CollectabilityHighBonus { get; private set; }\n            public int CollectabilityBonus { get; private set; }\n            public int CollectabilityBase { get; private set; }\n            public int Stars { get; private set; }\n            public int ScripRewards { get; private set; }\n            public int MaxClassJobLevel { get; private set; }\n            public int ExpModifier { get; private set; }\n            #endregion\n\n            #region Constructor\n            internal CollectableItem(MasterpieceSupplyDuty duty, int index) {\n                MasterpieceSupplyDuty = duty;\n                RequiredItem = duty.As<Item>(\"RequiredItem\", index);\n                Quantity = duty.AsInt32(\"Quantity\", index);\n                CollectabilityHighBonus = duty.AsInt32(\"Collectability{HighBonus}\", index);\n                CollectabilityBonus = duty.AsInt32(\"Collectability{Bonus}\", index);\n                CollectabilityBase = duty.AsInt32(\"Collectability{Base}\", index);\n                ExpModifier = duty.AsInt32(\"ExpModifier\", index);\n                ScripRewards = duty.AsInt32(\"Reward{Scrips}\", index);\n                MaxClassJobLevel = duty.AsInt32(\"ClassJobLevel{Max}\", index);\n                Stars = duty.AsInt32(\"Stars\", index);\n\n                var bonusMultiplierRow = duty.As<IXivRow>(\"BonusMultiplier\", index);\n                _xpMultiplier = ((double)(UInt16)bonusMultiplierRow[\"XpMultiplier[1]\"]) / 1000;\n                _xpMultiplier2 = ((double)(UInt16)bonusMultiplierRow[\"XpMultiplier[0]\"]) / 1000;\n                _currencyMultiplier = ((double)(UInt16)bonusMultiplierRow[\"CurrencyMultiplier[1]\"]) / 1000;\n                _currencyMultiplier2 = ((double)(UInt16)bonusMultiplierRow[\"CurrencyMultiplier[0]\"]) / 1000;\n            }\n            #endregion\n\n            public int[] CalculateExp(int level) {\n                // Constrain level by valid range for this collectable.\n                level = Math.Min(MasterpieceSupplyDuty.ClassJobLevel, level);\n                level = Math.Max(MaxClassJobLevel, level);\n\n                // Find the base XP.\n                var paramGrow = MasterpieceSupplyDuty.Sheet.Collection.GetSheet<ParamGrow>()[level];\n                var expPortion = ((double)1000 / ExpModifier);\n                var baseExp = (int)(paramGrow.ExpToNext / expPortion);\n\n                // Apply bonus multipliers.\n                return new int[] { baseExp, (int)(baseExp * _xpMultiplier), (int)(baseExp * _xpMultiplier2) };\n            }\n\n            public int[] CalculateScripRewards() {\n                return new int[] { ScripRewards, (int)(ScripRewards * _currencyMultiplier), (int)(ScripRewards * _currencyMultiplier2) };\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Xiv/MasterpieceSupplyDuty.cs",
    "content": "﻿using SaintCoinach.Ex.Relational;\nusing System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace SaintCoinach.Xiv {\n    public partial class MasterpieceSupplyDuty : XivRow {\n        #region Fields\n        private CollectableItem[] _CollectableItems;\n        #endregion\n        \n        #region Properties\n        public ClassJob ClassJob { get { return As<ClassJob>(); } }\n        public int ClassJobLevel { get { return AsInt32(\"ClassJobLevel\"); } }\n        public IEnumerable<CollectableItem> CollectableItems { get { return _CollectableItems ?? (_CollectableItems = BuildCollectableItems()); } }\n        #endregion\n\n        #region Constructors\n        public MasterpieceSupplyDuty(IXivSheet sheet, IRelationalRow sourceRow) : base(sheet, sourceRow) { }\n        #endregion\n\n        #region Build\n        private CollectableItem[] BuildCollectableItems() {\n            const int Count = 8;\n\n            var items = new CollectableItem[Count];\n            for (var i = 0; i < Count; ++i)\n                items[i] = new CollectableItem(this, i);\n            return items;\n        }\n        #endregion\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Xiv/Materia.cs",
    "content": "using System.Collections.Generic;\n\nusing SaintCoinach.Ex.Relational;\n\nnamespace SaintCoinach.Xiv {\n    /// <summary>\n    ///     Class representing materia groups.\n    /// </summary>\n    public class Materia : XivRow {\n        #region Fields\n\n        /// <summary>\n        ///     Item and value pairs for the current materia group.\n        /// </summary>\n        private ItemValue[] _Items;\n\n        #endregion\n\n        #region Properties\n\n        /// <summary>\n        ///     Gets the <see cref=\"BaseParam\" /> granted by the current materia group.\n        /// </summary>\n        /// <value>The <see cref=\"BaseParam\" /> granted by the current materia group.</value>\n        public BaseParam BaseParam { get { return As<BaseParam>(); } }\n\n        /// <summary>\n        ///     Gets the item and value pairs of the current materia group.\n        /// </summary>\n        /// <value>The item and value pairs of the current materia group.</value>\n        public IEnumerable<ItemValue> Items { get { return _Items ?? (_Items = BuildItems()); } }\n\n        #endregion\n\n        #region Constructors\n\n        /// <summary>\n        ///     Initializes a new instance of the <see cref=\"Materia\" /> class.\n        /// </summary>\n        /// <param name=\"sheet\"><see cref=\"IXivSheet\" /> containing this object.</param>\n        /// <param name=\"sourceRow\"><see cref=\"IRelationalRow\" /> to read data from.</param>\n        public Materia(IXivSheet sheet, IRelationalRow sourceRow) : base(sheet, sourceRow) { }\n\n        #endregion\n\n        #region Build\n\n        /// <summary>\n        ///     Build an array of item and value pairs of the current materia group.\n        /// </summary>\n        /// <returns>An array of item and value pairs of the current materia group.</returns>\n        private ItemValue[] BuildItems() {\n            const int Count = 10;\n\n            var items = new List<ItemValue>();\n\n            for (var i = 0; i < Count; ++i) {\n                var item = As<Item>(\"Item\", i);\n                if (item.Key == 0)\n                    continue;\n\n                var val = AsInt32(\"Value\", i);\n                items.Add(new ItemValue(item, val, i));\n            }\n\n            return items.ToArray();\n        }\n\n        #endregion\n\n        #region Item value helper class\n\n        /// <summary>\n        ///     Class representing a item and value pair for a materia group.\n        /// </summary>\n        public class ItemValue {\n            #region Properties\n\n            /// <summary>\n            ///     Gets the <see cref=\"Item\" /> of the current pair.\n            /// </summary>\n            /// <value>The <see cref=\"Item\" /> of the current pair.</value>\n            public Item Item { get; private set; }\n\n            /// <summary>\n            ///     Gets the value of the current pair.\n            /// </summary>\n            /// <value>The value of the current pair.</value>\n            public int Value { get; private set; }\n\n            /// <summary>\n            ///     Gets the materia tier of the current pair.\n            /// </summary>\n            /// <value>The materia tier of the current pair.</value>\n            public int Tier { get; private set; }\n\n            #endregion\n\n            #region Constructors\n\n            /// <summary>\n            ///     Initializes a new instance of the <see cref=\"ItemValue\" /> class.\n            /// </summary>\n            /// <param name=\"item\"><see cref=\"Item\" /> of the pair.</param>\n            /// <param name=\"value\">Value of the pair.</param>\n            /// <param name=\"tier\">Tier of the pair.</param>\n            public ItemValue(Item item, int value, int tier) {\n                Item = item;\n                Value = value;\n                Tier = tier;\n            }\n\n            #endregion\n        }\n\n        #endregion\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Xiv/MinionRace.cs",
    "content": "﻿using SaintCoinach.Ex.Relational;\nusing System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace SaintCoinach.Xiv\n{\n    public class MinionRace : XivRow\n    {\n        #region Constructors\n\n        #region Constructor\n\n        public MinionRace(IXivSheet sheet, IRelationalRow sourceRow) : base(sheet, sourceRow) { }\n\n        #endregion\n\n        #endregion\n\n        #region Properties\n\n        public Text.XivString Name { get { return AsString(\"Name\"); } }\n\n        #endregion\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Xiv/MinionSkillType.cs",
    "content": "﻿using SaintCoinach.Ex.Relational;\nusing System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace SaintCoinach.Xiv\n{\n    public class MinionSkillType : XivRow\n    {\n        #region Constructors\n\n        #region Constructor\n\n        public MinionSkillType(IXivSheet sheet, IRelationalRow sourceRow) : base(sheet, sourceRow) { }\n\n        #endregion\n\n        #endregion\n\n        #region Properties\n\n        public Text.XivString Name { get { return AsString(\"Name\"); } }\n\n        #endregion\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Xiv/ModelChara.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nusing SaintCoinach.Ex.Relational;\nusing SaintCoinach.Graphics;\n\nnamespace SaintCoinach.Xiv {\n    /// <summary>\n    /// Class representing model data (non-humanoid).\n    /// </summary>\n    public class ModelChara : XivRow {\n        #region Properties\n        /// <summary>\n        /// Gets the type of the current model.\n        /// </summary>\n        /// <value>The type of the current model.</value>\n        /// <remarks>\n        /// Confirmed values are:\n        ///   2: Demihuman (chara/demihuman/d{Model}/obj/equipment/e{Base}/model/d{Model}e{Base}_{Variant}.mdl)\n        ///      -> TODO: Variant to str map\n        ///   3: Monster (chara/monster/m{Model}/base/b{Base}/model/m{Model}b{Base}.mdl)\n        ///   4: Static object (?)\n        ///   5: Attached to other NPC, Golem Soulstone, Titan Heart, etc.\n        /// Unconfirmed:\n        ///   1: Special body type? Gaius, Nero, Rhitatyn have this\n        /// </remarks>\n        public int Type { get { return AsInt32(\"Type\"); } }\n\n        /// <summary>\n        /// Gets the key for the current model's file.\n        /// </summary>\n        /// <value>The key for the current model's file.</value>\n        public int ModelKey { get { return AsInt32(\"Model\"); } }\n\n        /// <summary>\n        /// Gets the key for the base of the model to use.\n        /// </summary>\n        /// <value>The key for the base of the model to use.</value>\n        public int BaseKey { get { return AsInt32(\"Base\"); } }\n\n        /// <summary>\n        /// Gets the variant of the model to use.\n        /// </summary>\n        /// <value>The variant of the model to use.</value>\n        public int Variant { get { return AsInt32(\"Variant\"); } }\n        #endregion\n\n        #region Constructors\n\n        /// <summary>\n        /// Initializes a new instance of the <see cref=\"ModelChara\"/> class.\n        /// </summary>\n        /// <param name=\"sheet\"><see cref=\"IXivSheet\"/> containing this object.</param>\n        /// <param name=\"sourceRow\"><see cref=\"IRelationalRow\"/> to read data from.</param>\n        public ModelChara(IXivSheet sheet, IRelationalRow sourceRow) : base(sheet, sourceRow) { }\n\n        #endregion\n\n        #region Models\n\n        public Tuple<ModelDefinition, ImcVariant> GetModelDefinition() {\n            switch (Type) {\n                case 3:\n                    return GetMonsterModelDefinition();\n                default:\n                    throw new InvalidOperationException();\n            }\n        }\n\n        private Tuple<ModelDefinition, ImcVariant> GetMonsterModelDefinition() {\n            const string ImcPathFormat = \"chara/monster/m{0:D4}/obj/body/b{1:D4}/b{1:D4}.imc\";\n            const string ModelPathFormat = \"chara/monster/m{0:D4}/obj/body/b{1:D4}/model/m{0:D4}b{1:D4}.mdl\";\n\n            var imcPath = string.Format(ImcPathFormat, ModelKey, BaseKey);\n            var mdlPath = string.Format(ModelPathFormat, ModelKey, BaseKey);\n\n            var packs = this.Sheet.Collection.PackCollection;\n\n            if (!packs.TryGetFile(imcPath, out var imcFileBase) || !packs.TryGetFile(mdlPath, out var mdlFileBase) || !(mdlFileBase is ModelFile))\n                throw new InvalidOperationException($\"Unable to find files for {this}.\");\n\n            try {\n                var model = ((ModelFile)mdlFileBase).GetModelDefinition();\n                var imcFile = new ImcFile(imcFileBase);\n                var variant = imcFile.GetVariant(Variant);\n\n                return Tuple.Create(model, variant);\n            } catch (Exception ex) {\n                throw new InvalidOperationException($\"Unable to load model for {this}.\", ex);\n            }\n        }\n\n        #endregion\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Xiv/MonsterNote.cs",
    "content": "using System.Collections.Generic;\n\nusing SaintCoinach.Ex.Relational;\n\nnamespace SaintCoinach.Xiv {\n    public class MonsterNote : XivRow {\n        #region Fields\n\n        private Target[] _Targets;\n\n        #endregion\n\n        #region Properties\n\n        public Text.XivString Name { get { return AsString(\"Name\"); } }\n        public int Reward { get { return AsInt32(\"Reward\"); } }\n        public IEnumerable<Target> Targets { get { return _Targets ?? (_Targets = BuildTargets()); } }\n\n        #endregion\n\n        #region Constructors\n\n        #region Constructor\n\n        public MonsterNote(IXivSheet sheet, IRelationalRow sourceRow) : base(sheet, sourceRow) { }\n\n        #endregion\n\n        #endregion\n\n        #region Build\n\n        private Target[] BuildTargets() {\n            const int Count = 4;\n\n            var targets = new List<Target>();\n            for (var i = 0; i < Count; ++i) {\n                var target = As<MonsterNoteTarget>(\"MonsterNoteTarget\", i);\n                var count = AsInt32(\"Count\", i);\n                if (target.Key != 0 && count != 0)\n                    targets.Add(new Target(target, count));\n            }\n\n            return targets.ToArray();\n        }\n\n        #endregion\n\n        public override string ToString() {\n            return Name;\n        }\n\n        #region Helper class\n\n        public class Target {\n            #region Properties\n\n            public MonsterNoteTarget MonsterNoteTarget { get; private set; }\n            public int Count { get; private set; }\n\n            #endregion\n\n            #region Constructors\n\n            #region Constructor\n\n            public Target(MonsterNoteTarget monsterNoteTarget, int count) {\n                MonsterNoteTarget = monsterNoteTarget;\n                Count = count;\n            }\n\n            #endregion\n\n            #endregion\n        }\n\n        #endregion\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Xiv/MonsterNoteTarget.cs",
    "content": "using System;\nusing System.Linq;\nusing System.Collections.Generic;\n\nusing SaintCoinach.Ex.Relational;\nusing SaintCoinach.Imaging;\n\nnamespace SaintCoinach.Xiv {\n    public class MonsterNoteTarget : XivRow, ILocatable {\n        #region Fields\n\n        private IEnumerable<ILocation> _Locations;\n\n        #endregion\n\n        #region Properties\n\n        public BNpcName BNpcName { get { return As<BNpcName>(); } }\n        public ImageFile Icon { get { return AsImage(\"Icon\"); } }\n        public IEnumerable<ILocation> Locations { get { return _Locations ?? (_Locations = BuildLocations()); } }\n\n        #endregion\n\n        #region Constructors\n\n        #region Constructor\n\n        public MonsterNoteTarget(IXivSheet sheet, IRelationalRow sourceRow) : base(sheet, sourceRow) { }\n\n        #endregion\n\n        #endregion\n\n        #region Build\n\n        private IEnumerable<ILocation> BuildLocations() {\n            if (Sheet.Collection.IsLibraAvailable)\n                return BNpcName.Locations;\n\n            const int Count = 3;\n\n            var locations = new List<ILocation>();\n\n            var maps = Sheet.Collection.GetSheet<Map>();\n\n            for (var i = 0; i < Count; ++i) {\n                var zone = As<PlaceName>(\"PlaceName{Zone}\", i);\n                var location = As<PlaceName>(\"PlaceName{Location}\", i);\n\n                if (zone.Key != 0)\n                    locations.Add(new Location(zone, location));\n            }\n\n            return locations.ToArray();\n        }\n\n        #endregion\n\n        #region Location class\n\n        public class Location : ILocation {\n            #region Properties\n\n            double ILocation.MapX { get { return double.NaN; } }\n            double ILocation.MapY { get { return double.NaN; } }\n\n            public PlaceName PlaceName { get; private set; }\n            public PlaceName LocationPlaceName { get; private set; }\n\n            #endregion\n\n            #region Constructors\n\n            public Location(PlaceName zonePlace, PlaceName locationPlace) {\n                PlaceName = zonePlace;\n                LocationPlaceName = locationPlace;\n            }\n\n            #endregion\n        }\n\n        #endregion\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Xiv/Mount.cs",
    "content": "using SaintCoinach.Ex.Relational;\nusing SaintCoinach.Imaging;\n\nnamespace SaintCoinach.Xiv {\n    public class Mount : XivRow, IQuantifiableXivString {\n        #region Properties\n\n        public Text.XivString Singular { get { return AsString(\"Singular\"); } }\n        public Text.XivString Plural { get { return Sheet.Collection.ActiveLanguage == Ex.Language.Japanese ? Singular : AsString(\"Plural\"); } }\n        public Text.XivString Description { get { return AsString(\"Description\"); } }\n        public Text.XivString GuideDescription { get { return AsString(\"Description{Enhanced}\"); } }\n        public Text.XivString Tooltip { get { return AsString(\"Tooltip\"); } }\n        public ImageFile Icon { get { return AsImage(\"Icon\"); } }\n        public ModelChara ModelChara => As<ModelChara>();\n\n        #endregion\n\n        #region Constructors\n\n        #region Constructor\n\n        public Mount(IXivSheet sheet, IRelationalRow sourceRow) : base(sheet, sourceRow) { }\n\n        #endregion\n\n        #endregion\n\n        public override string ToString() {\n            return Singular;\n        }\n\n        #region IQuantifiableName Members\n        string IQuantifiable.Singular {\n            get { return Singular; }\n        }\n\n        string IQuantifiable.Plural {\n            get { return Plural; }\n        }\n        #endregion\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Xiv/NpcEquip.cs",
    "content": "﻿using SaintCoinach.Ex.Relational;\n\nnamespace SaintCoinach.Xiv {\n    public class NpcEquip : XivRow {\n        #region Properties\n        public Quad ModelMain { get { return AsQuad(\"Model{MainHand}\"); } }\n        public Stain DyeMain { get { return As<Stain>(\"Dye{MainHand}\"); } }\n\n        public Quad ModelSub { get { return AsQuad(\"Model{OffHand}\"); } }\n        public Stain DyeOff { get { return As<Stain>(\"Dye{OffHand}\"); } }\n        public int[] ModelHead { get { return AsIntArray(\"Model{Head}\"); } }\n        public int[] ModelBody { get { return AsIntArray(\"Model{Body}\"); } }\n        public int[] ModelHands { get { return AsIntArray(\"Model{Hands}\"); } }\n        public int[] ModelLegs { get { return AsIntArray(\"Model{Legs}\"); } }\n        public int[] ModelFeet { get { return AsIntArray(\"Model{Feet}\"); } }\n        public int[] ModelEars { get { return AsIntArray(\"Model{Ears}\"); } }\n        public int[] ModelNeck { get { return AsIntArray(\"Model{Neck}\"); } }\n        public int[] ModelWrists { get { return AsIntArray(\"Model{Wrists}\"); } }\n        public int[] ModelLeftRing { get { return AsIntArray(\"Model{LeftRing}\"); } }\n        public int[] ModelRightRing { get { return AsIntArray(\"Model{RightRing}\"); } }\n        public Stain DyeHead { get { return As<Stain>(\"Dye{Head}\"); } }\n        public Stain DyeBody { get { return As<Stain>(\"Dye{Body}\"); } }\n        public Stain DyeHands { get { return As<Stain>(\"Dye{Hands}\"); } }\n        public Stain DyeLegs { get { return As<Stain>(\"Dye{Legs}\"); } }\n        public Stain DyeFeet { get { return As<Stain>(\"Dye{Feet}\"); } }\n        public Stain DyeEars { get { return As<Stain>(\"Dye{Ears}\"); } }\n        public Stain DyeNeck { get { return As<Stain>(\"Dye{Neck}\"); } }\n        public Stain DyeWrists { get { return As<Stain>(\"Dye{Wrists}\"); } }\n        public Stain DyeLeftRing { get { return As<Stain>(\"Dye{LeftRing}\"); } }\n        public Stain DyeRightRing { get { return As<Stain>(\"Dye{RightRing}\"); } }\n\n        #endregion\n        #region Constructors\n\n        /// <summary>\n        ///     Initializes a new instance of the <see cref=\"NpcEquip\" /> class.\n        /// </summary>\n        /// <param name=\"sheet\"><see cref=\"IXivSheet\" /> containing this object.</param>\n        /// <param name=\"sourceRow\"><see cref=\"IRelationalRow\" /> to read data from.</param>\n        public NpcEquip(IXivSheet sheet, IRelationalRow sourceRow) : base(sheet, sourceRow) { }\n\n        #endregion\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Xiv/OnlineStatus.cs",
    "content": "using SaintCoinach.Ex.Relational;\nusing SaintCoinach.Imaging;\n\nnamespace SaintCoinach.Xiv {\n    public class OnlineStatus : XivRow {\n        #region Properties\n\n        public Text.XivString Name { get { return AsString(\"Name\"); } }\n        public ImageFile Icon { get { return AsImage(\"Icon\"); } }\n\n        #endregion\n\n        #region Constructors\n\n        #region Constructor\n\n        public OnlineStatus(IXivSheet sheet, IRelationalRow sourceRow) : base(sheet, sourceRow) { }\n\n        #endregion\n\n        #endregion\n\n        public override string ToString() {\n            return Name;\n        }\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Xiv/Ornament.cs",
    "content": "﻿using SaintCoinach.Ex.Relational;\nusing SaintCoinach.Imaging;\n\nnamespace SaintCoinach.Xiv {\n    public class Ornament : XivRow, IQuantifiableXivString {\n        #region Properties\n\n        public Text.XivString Singular { get { return AsString(\"Singular\"); } }\n        public Text.XivString Plural { get { return Sheet.Collection.ActiveLanguage == Ex.Language.Japanese ? Singular : AsString(\"Plural\"); } }\n        public ImageFile Icon { get { return AsImage(\"Icon\"); } }\n\n        #endregion\n\n        #region Constructors\n\n        #region Constructor\n\n        public Ornament(IXivSheet sheet, IRelationalRow sourceRow) : base(sheet, sourceRow) { }\n\n        #endregion\n\n        #endregion\n\n        public override string ToString() {\n            return Singular;\n        }\n\n        #region IQuantifiableName Members\n        string IQuantifiable.Singular {\n            get { return Singular; }\n        }\n\n        string IQuantifiable.Plural {\n            get { return Plural; }\n        }\n        #endregion\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Xiv/ParamGrow.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nusing SaintCoinach.Ex.Relational;\n\nnamespace SaintCoinach.Xiv {\n    public class ParamGrow : XivRow {\n        #region Properties\n        public int ExpToNext { get { return AsInt32(\"ExpToNext\"); } }\n        public int AdditionalActions { get { return AsInt32(\"AdditionalActions\"); } }\n        public double MpModifier { get { return AsInt32(\"MpModifier\") / 100.0; } }\n        #endregion\n\n        #region Constructors\n\n        public ParamGrow(IXivSheet sheet, IRelationalRow sourceRow) : base(sheet, sourceRow) { }\n\n        #endregion\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Xiv/Parameter.cs",
    "content": "﻿using System.Collections;\nusing System.Collections.Generic;\n\nnamespace SaintCoinach.Xiv {\n    /// <summary>\n    ///     Class representing a collection of bonuses for a specific <see cref=\"BaseParam\" />.\n    /// </summary>\n    public class Parameter : IEnumerable<ParameterValue> {\n        #region Fields\n\n        /// <summary>\n        ///     List of bonuses for the current parameter.\n        /// </summary>\n        private readonly List<ParameterValue> _Values = new List<ParameterValue>();\n\n        #endregion\n\n        #region Properties\n\n        /// <summary>\n        ///     Gets the <see cref=\"BaseParam\" /> associated with the current object.\n        /// </summary>\n        /// <value>The <see cref=\"BaseParam\" /> associated with the current object.</value>\n        public BaseParam BaseParam { get; private set; }\n\n        /// <summary>\n        ///     Gets the bonuses of the current object.\n        /// </summary>\n        /// <value>The bonuses of the current object.</value>\n        public IEnumerable<ParameterValue> Values { get { return _Values; } }\n\n        #endregion\n\n        #region Constructors\n\n        /// <summary>\n        ///     Initializes a new instance of the <see cref=\"Parameter\" /> class.\n        /// </summary>\n        /// <param name=\"baseParam\"><see cref=\"BaseParam\" /> to associate with the <see cref=\"Parameter\" />.</param>\n        public Parameter(BaseParam baseParam) {\n            BaseParam = baseParam;\n        }\n\n        #endregion\n\n        #region IEnumerable<ParameterValue> Members\n\n        /// <summary>\n        ///     Returns an enumerator that iterates through the <see cref=\"Parameter\"/>.\n        /// </summary>\n        /// <returns>An enumerator that iterates through the <see cref=\"Parameter\"/>.</returns>\n        public IEnumerator<ParameterValue> GetEnumerator() {\n            return _Values.GetEnumerator();\n        }\n\n        #endregion\n\n        #region IEnumerable Members\n\n        /// <summary>\n        ///     Returns an enumerator that iterates through the <see cref=\"Parameter\"/>.\n        /// </summary>\n        /// <returns>An enumerator that iterates through the <see cref=\"Parameter\"/>.</returns>\n        IEnumerator IEnumerable.GetEnumerator() {\n            return GetEnumerator();\n        }\n\n        #endregion\n\n        #region Add\n\n        /// <summary>\n        ///     Add a value to the current <see cref=\"Parameter\" />.\n        /// </summary>\n        /// <param name=\"value\"><see cref=\"ParameterValue\" /> to add.</param>\n        public void AddValue(ParameterValue value) {\n            _Values.Add(value);\n        }\n\n        #endregion\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Xiv/ParameterCollection.cs",
    "content": "﻿using System.Collections;\nusing System.Collections.Generic;\n\nnamespace SaintCoinach.Xiv {\n    /// <summary>\n    ///     Class representing a collection of parameter bonuses.\n    /// </summary>\n    public class ParameterCollection : IEnumerable<Parameter> {\n        #region Fields\n\n        /// <summary>\n        ///     Dictionary mapping <see cref=\"BaseParam\" /> to their respective <see cref=\"Parameter\" />.\n        /// </summary>\n        private readonly Dictionary<BaseParam, Parameter> _Parameters = new Dictionary<BaseParam, Parameter>();\n\n        #endregion\n\n        #region Properties\n\n        /// <summary>\n        ///     Gets the <see cref=\"Parameter\" /> bonuses for the current collection.\n        /// </summary>\n        /// <value>The <see cref=\"Parameter\" /> bonuses for the current collection.</value>\n        public IEnumerable<Parameter> Parameters { get { return _Parameters.Values; } }\n\n        #endregion\n\n        #region Add\n\n        /// <summary>\n        ///     Add a value to a specific <see cref=\"BaseParam\" />.\n        /// </summary>\n        /// <param name=\"baseParam\"><see cref=\"BaseParam\" /> to which to add a value.</param>\n        /// <param name=\"value\"><see cref=\"ParameterValue\" /> to add to <c>baseParam</c>.</param>\n        public void AddParameterValue(BaseParam baseParam, ParameterValue value) {\n            Parameter param;\n            if (!_Parameters.TryGetValue(baseParam, out param))\n                _Parameters.Add(baseParam, param = new Parameter(baseParam));\n\n            param.AddValue(value);\n        }\n\n        /// <summary>\n        ///     Copy the values from given <see cref=\"Parameter\" />s to the current collection.\n        /// </summary>\n        /// <param name=\"other\"><see cref=\"Parameter\" />s from which to add the values.</param>\n        public void AddRange(IEnumerable<Parameter> other) {\n            foreach (var p in other) {\n                foreach (var v in p)\n                    AddParameterValue(p.BaseParam, v);\n            }\n        }\n\n        #endregion\n\n        #region IEnumerable<Parameter> Members\n\n        /// <summary>\n        ///     Returns an enumerator that iterates through the <see cref=\"ParameterCollection\" />.\n        /// </summary>\n        /// <returns>An enumerator that iterates through the <see cref=\"ParameterCollection\" />.</returns>\n        public IEnumerator<Parameter> GetEnumerator() {\n            return _Parameters.Values.GetEnumerator();\n        }\n\n        /// <summary>\n        ///     Returns an enumerator that iterates through the <see cref=\"ParameterCollection\" />.\n        /// </summary>\n        /// <returns>An enumerator that iterates through the <see cref=\"ParameterCollection\" />.</returns>\n        IEnumerator IEnumerable.GetEnumerator() {\n            return GetEnumerator();\n        }\n\n        #endregion\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Xiv/ParameterType.cs",
    "content": "﻿namespace SaintCoinach.Xiv {\n    /// <summary>\n    ///     Enumeration of known types of parameter bonuses.\n    /// </summary>\n    public enum ParameterType {\n        /// <summary>\n        ///     Value indicating the bonus is always present on an object.\n        /// </summary>\n        Base,\n\n        /// <summary>\n        ///     Value indicating the bonus is a primary parameter and always present on an object.\n        /// </summary>\n        Primary,\n\n        /// <summary>\n        ///     Value indicating the bonus is only valid when set conditions are fulfilled.\n        /// </summary>\n        SetBonus,\n\n        /// <summary>\n        ///     Value indicating the bonus is only valid while a Grand Company sanction is active.\n        /// </summary>\n        Sanction,\n\n        /// <summary>\n        ///     Value indicating the bonus is only valid when an object is high-quality.\n        /// </summary>\n        Hq,\n\n        /// <summary>\n        ///     Value indicating the bonus is variable and can differ between instance.\n        /// </summary>\n        Variable,\n\n        /// <summary>\n        ///     Value indicating the bonus is only valid when set and level conditions are fulfilled.\n        /// </summary>\n        SetBonusCapped,\n\n        /// <summary>\n        ///     Value indicating the bonus is only valid inside Eureka.\n        /// </summary>\n        EurekaEffect,\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Xiv/ParameterValue.cs",
    "content": "﻿namespace SaintCoinach.Xiv {\n    /// <summary>\n    ///     Base class for representing a parameter bonus.\n    /// </summary>\n    public abstract class ParameterValue {\n        #region Properties\n\n        /// <summary>\n        ///     Gets the <see cref=\"ParameterType\" /> of the current bonus.\n        /// </summary>\n        /// <value>The <see cref=\"ParameterType\" /> of the current bonus.</value>\n        public ParameterType Type { get; private set; }\n\n        /// <summary>\n        ///     Gets the index of the current bonus.\n        /// </summary>\n        /// <value>The index of the current bonus.</value>\n        public int Index { get; private set; }\n\n        #endregion\n\n        #region Constructors\n\n        /// <summary>\n        ///     Initializes a new instance of the <see cref=\"ParameterValue\" /> class.\n        /// </summary>\n        /// <param name=\"type\"><see cref=\"ParameterType\" /> of the bonus.</param>\n        /// <param name=\"index\">Index of the bonus.</param>\n        protected ParameterValue(ParameterType type, int index) {\n            Type = type;\n            Index = index;\n        }\n\n        #endregion\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Xiv/ParameterValueFixed.cs",
    "content": "﻿using System.Text;\n\nnamespace SaintCoinach.Xiv {\n    /// <summary>\n    /// Class representing a fixed-value parameter bonus.\n    /// </summary>\n    public class ParameterValueFixed : ParameterValue {\n        #region Properties\n\n        /// <summary>\n        /// Gets the current bonus amount.\n        /// </summary>\n        /// <value>The current bonus amount.</value>\n        public double Amount { get; private set; }\n\n        #endregion\n\n        #region Constructors\n\n        /// <summary>\n        /// Initializes a new instance of the <see cref=\"ParameterValueFixed\"/> class.\n        /// </summary>\n        /// <param name=\"type\"><see cref=\"ParameterType\"/> of the bonus.</param>\n        /// <param name=\"amount\">Bonus amount.</param>\n        /// <param name=\"index\">Parameter index.</param>\n        public ParameterValueFixed(ParameterType type, double amount, int index)\n            : base(type, index) {\n            Amount = amount;\n        }\n\n        #endregion\n\n        /// <summary>\n        ///     Returns a string that represents the current bonus.\n        /// </summary>\n        /// <returns>A string that represents the current bonus.</returns>\n        public override string ToString() {\n            var sb = new StringBuilder();\n\n            if (Amount > 0 && Type != ParameterType.Primary && Type != ParameterType.Base)\n                sb.Append('+');\n            sb.Append(Amount);\n\n            return sb.ToString();\n        }\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Xiv/ParameterValueRelative.cs",
    "content": "﻿using System.Text;\n\nnamespace SaintCoinach.Xiv {\n    /// <summary>\n    ///     Class representing a relative parameter bonus.\n    /// </summary>\n    public class ParameterValueRelative : ParameterValue {\n        #region Properties\n\n        /// <summary>\n        ///     Gets the current relative bonus.\n        /// </summary>\n        /// <value>The current relative bonus.</value>\n        public double Amount { get; private set; }\n\n        #endregion\n\n        #region Constructors\n\n        /// <summary>\n        ///     Initializes a new instance of the <see cref=\"ParameterValueRelative\" /> class.\n        /// </summary>\n        /// <param name=\"type\"><see cref=\"ParameterType\" /> of the bonus.</param>\n        /// <param name=\"amount\">Relative bonus.</param>\n        /// <param name=\"index\">Index of the bonus.</param>\n        public ParameterValueRelative(ParameterType type, double amount, int index)\n            : base(type, index) {\n            Amount = amount;\n        }\n\n        #endregion\n\n        /// <summary>\n        ///     Returns a string that represents the current bonus.\n        /// </summary>\n        /// <returns>A string that represents the current bonus.</returns>\n        public override string ToString() {\n            var sb = new StringBuilder();\n\n            if (Amount > 0 && Type != ParameterType.Primary && Type != ParameterType.Base)\n                sb.Append('+');\n            sb.Append(Amount.ToString(\"p0\"));\n\n            return sb.ToString();\n        }\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Xiv/ParameterValueRelativeLimited.cs",
    "content": "﻿namespace SaintCoinach.Xiv {\n    /// <summary>\n    ///     Class representing a relative parameter bonus that can only grant up to a certain amount.\n    /// </summary>\n    public class ParameterValueRelativeLimited : ParameterValueRelative {\n        #region Properties\n\n        /// <summary>\n        ///     Gets the maximum bonus granted.\n        /// </summary>\n        /// <value>The maximum bonus granted.</value>\n        public int Maximum { get; private set; }\n\n        #endregion\n\n        #region Constructors\n\n        /// <summary>\n        ///     Initializes a new instance of the <see cref=\"ParameterValueRelativeLimited\" /> class.\n        /// </summary>\n        /// <param name=\"type\"><see cref=\"ParameterType\" /> of the bonus.</param>\n        /// <param name=\"amount\">Relative bonus.</param>\n        /// <param name=\"maximum\">Maximum bonus granted.</param>\n        /// <param name=\"index\">Index of the bonus.</param>\n        public ParameterValueRelativeLimited(ParameterType type, double amount, int maximum, int index)\n            : base(type, amount, index) {\n            Maximum = maximum;\n        }\n\n        #endregion\n\n        /// <summary>\n        ///     Returns a string that represents the current bonus.\n        /// </summary>\n        /// <returns>A string that represents the current bonus.</returns>\n        public override string ToString() {\n            return base.ToString() + string.Format(\" (max. {0})\", Maximum);\n        }\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Xiv/PlaceName.cs",
    "content": "using SaintCoinach.Ex.Relational;\n\nnamespace SaintCoinach.Xiv {\n    public class PlaceName : XivRow {\n        #region Properties\n\n        public Text.XivString Name { get { return AsString(\"Name\"); } }\n        public Text.XivString NameWithoutArticle { get { return AsString(\"Name{NoArticle}\"); } }\n\n        #endregion\n\n        #region Constructors\n\n        #region Constructor\n\n        public PlaceName(IXivSheet sheet, IRelationalRow sourceRow) : base(sheet, sourceRow) { }\n\n        #endregion\n\n        #endregion\n\n        public override string ToString() {\n            return Name;\n        }\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Xiv/PrerequisiteQuestsRequirement.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace SaintCoinach.Xiv {\n    public class PrerequisiteQuestsRequirement {\n        #region Properties\n        public IEnumerable<Quest> Quests { get; private set; }\n        public PrerequisiteQuestsRequirementType Type { get; private set; }\n        #endregion\n\n        #region Constructor\n        internal PrerequisiteQuestsRequirement(Quest quest) {\n            const int QuestCount = 3;\n\n            this.Type = (PrerequisiteQuestsRequirementType)quest.AsInt32(\"PreviousQuestJoin\");\n\n            var quests = new List<Quest>();\n            for (var i = 0; i < QuestCount; ++i) {\n                var q = quest.As<Quest>(\"PreviousQuest\", i);\n\n                if (q.Key != 0)\n                    quests.Add(q);\n            }\n            this.Quests = quests;\n        }\n\n        internal PrerequisiteQuestsRequirement(PrerequisiteQuestsRequirementType type, IEnumerable<Quest> quests) {\n            this.Type = type;\n            this.Quests = quests;\n        }\n        #endregion\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Xiv/PrerequisiteQuestsRequirementType.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace SaintCoinach.Xiv {\n    public enum PrerequisiteQuestsRequirementType : byte {\n        None = 0,\n        All = 1,\n        Any = 2,\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Xiv/ProbabilityPair.cs",
    "content": "﻿namespace SaintCoinach.Xiv {\n    /// <summary>\n    ///     Class representing an object that has a probability attached.\n    /// </summary>\n    /// <typeparam name=\"T\">Type of the contained object.</typeparam>\n    public class ProbabilityPair<T> {\n        #region Properties\n\n        /// <summary>\n        ///     Gets the object.\n        /// </summary>\n        /// <value>The object.</value>\n        public T Value { get; private set; }\n\n        /// <summary>\n        ///     Gets the probability attached to the current object.\n        /// </summary>\n        /// <value>The probability attached to the current object.</value>\n        public double Probability { get; private set; }\n\n        #endregion\n\n        #region Constructors\n\n        /// <summary>\n        ///     Initializes a new instance of the <see cref=\"ProbabilityPair{T}\" /> class.\n        /// </summary>\n        /// <param name=\"value\">Object to attach the probability to.</param>\n        /// <param name=\"probability\">Probability to attach to <c>value</c>.</param>\n        public ProbabilityPair(T value, double probability) {\n            Value = value;\n            Probability = probability;\n        }\n\n        #endregion\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Xiv/Quad.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Runtime.Serialization;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace SaintCoinach.Xiv {\n    [Serializable]\n    public struct Quad {\n        public short Value1;\n        public short Value2;\n        public short Value3;\n        public short Value4;\n\n        public Quad(long data) {\n            Value1 = (short)data;\n            Value2 = (short)(data >> 16);\n            Value3 = (short)(data >> 32);\n            Value4 = (short)(data >> 48);\n        }\n\n        public static Quad Read(byte[] buffer, int offset, bool bigEndian) {\n            var data = OrderedBitConverter.ToInt64(buffer, offset, bigEndian);\n            return new Quad(data);\n        }\n\n        public override string ToString() {\n            return Value1 + \", \" + Value2 + \", \" + Value3 + \", \" + Value4;\n        }\n\n        public Int64 ToInt64() {\n            return (Int64)Value1 + ((Int64)Value2 << 16) + ((Int64)Value3 << 32) + ((Int64)Value4 << 48);\n        }\n\n        public bool IsEmpty {\n            get { return Value1 == 0 && Value2 == 0 && Value3 == 0 && Value4 == 0; }\n        }\n\n        #region Operators\n\n        public static bool operator==(Quad lhs, Quad rhs) {\n            return rhs.Value1 == lhs.Value1 && rhs.Value2 == lhs.Value2 && rhs.Value3 == lhs.Value3 && rhs.Value4 == lhs.Value4;\n        }\n\n        public static bool operator!=(Quad lhs, Quad rhs) {\n            return rhs.Value1 != lhs.Value1 || rhs.Value2 != lhs.Value2 || rhs.Value3 != lhs.Value3 || rhs.Value4 != lhs.Value4;\n        }\n\n        public override bool Equals(object obj) {\n            if (obj is Quad)\n                return (Quad)obj == this;\n            return false;\n        }\n\n        public override int GetHashCode() {\n            return ToInt64().GetHashCode();\n        }\n\n        #endregion\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Xiv/Quest.cs",
    "content": "using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing SaintCoinach.Ex.Relational;\n\nnamespace SaintCoinach.Xiv {\n    public class Quest : XivRow, IItemSource {\n        #region Fields\n        private QuestRequirements _Requirements;\n        private QuestRewards _Rewards;\n        private ENpc[] _InvolvedENpcs;\n        #endregion\n\n        #region Properties\n\n        public Text.XivString Name { get { return AsString(\"Name\"); } }\n\n        public Text.XivString Id { get { return AsString(\"Id\"); } }\n\n        public QuestRequirements Requirements { get { return _Requirements ?? (_Requirements = new QuestRequirements(this)); } }\n\n        public BeastTribe BeastTribe { get { return As<BeastTribe>(); } }\n\n        public ENpc IssuingENpc {\n            get {\n                var resident = this[\"Issuer{Start}\"] as ENpcResident;\n                return resident == null ? null : Sheet.Collection.ENpcs[resident.Key];\n            }\n        }\n        public ENpc TargetENpc {\n            get {\n                var resident = this[\"Target{End}\"] as ENpcResident;\n                return resident == null ? null : Sheet.Collection.ENpcs[resident.Key];\n            }\n        }\n\n        public bool IsRepeatable { get { return AsBoolean(\"IsRepeatable\"); } }\n        public QuestRepeatInterval RepeatInterval { get { return (QuestRepeatInterval)AsInt32(\"RepeatIntervalType\"); } }\n\n        public PlaceName PlaceName { get { return As<PlaceName>(); } }\n\n        public JournalGenre JournalGenre { get { return As<JournalGenre>(); } }\n\n        public Imaging.ImageFile Icon { get { return AsImage(\"Icon\"); } }\n        public Imaging.ImageFile SpecialIcon { get { return AsImage(\"Icon{Special}\"); } }\n\n        public int SortKey { get { return AsInt32(\"SortKey\"); } }\n\n        public QuestRewards Rewards { get { return _Rewards ?? (_Rewards = new QuestRewards(this)); } }\n\n        public IEnumerable<ENpc> InvolvedENpcs { get { return _InvolvedENpcs ?? (_InvolvedENpcs = BuildInvolvedENpcs()); } }\n        #endregion\n\n        #region Constructors\n\n        public Quest(IXivSheet sheet, IRelationalRow sourceRow) : base(sheet, sourceRow) { }\n\n        #endregion\n\n        public override string ToString() {\n            return Name;\n        }\n\n        #region IItemSource Members\n\n        private Item[] _ItemSourceItems;\n        System.Collections.Generic.IEnumerable<Item> IItemSource.Items {\n            get {\n                return _ItemSourceItems ?? (_ItemSourceItems =  Rewards.Items.SelectMany(g => g.Items.Select(i => i.Item)).ToArray());\n            }\n        }\n\n        #endregion\n\n        #region Build\n        private ENpc[] BuildInvolvedENpcs() {\n            var enpcs = new List<ENpc> { IssuingENpc };\n            if (Sheet.Collection.IsLibraAvailable) {\n                var enpcColl = Sheet.Collection.ENpcs;\n                var libraRows = Sheet.Collection.Libra.ENpcResident_Quest.Where(i => i.Quest_Key == this.Key);\n                foreach (var r in libraRows)\n                    enpcs.Add(enpcColl[(int)r.ENpcResident_Key]);\n            }\n\n            enpcs.Add(TargetENpc);\n\n            return enpcs.Distinct().ToArray();\n        }\n        #endregion\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Xiv/QuestRepeatInterval.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace SaintCoinach.Xiv {\n    public enum QuestRepeatInterval : byte {\n        Immediate = 0,\n        Daily = 1,\n        Weekly = 2,\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Xiv/QuestRequirements.ClassJobRequirement.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace SaintCoinach.Xiv {\n    partial class QuestRequirements {\n        public class ClassJobRequirement {\n            #region Properties\n            public ClassJobCategory ClassJobCategory { get; private set; }\n            public int Level { get; private set; }\n            #endregion\n\n            #region Constructors\n            internal ClassJobRequirement(ClassJobCategory category, int level) {\n                this.ClassJobCategory = category;\n                this.Level = level;\n            }\n            #endregion\n        }\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Xiv/QuestRequirements.InstanceContentRequirement.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace SaintCoinach.Xiv {\n    partial class QuestRequirements {\n        public class InstanceContentRequirement {\n            #region Properties\n            public IEnumerable<InstanceContent> InstanceContents { get; private set; }\n            public PrerequisiteQuestsRequirementType Type { get; private set; }\n            #endregion\n\n            #region Constructor\n            internal InstanceContentRequirement(Quest quest) {\n                const int QuestCount = 3;\n\n                this.Type = (PrerequisiteQuestsRequirementType)quest.AsInt32(\"InstanceContentJoin\");\n\n                var contents = new List<InstanceContent>();\n                for (var i = 0; i < QuestCount; ++i) {\n                    var c = quest.As<InstanceContent>(\"InstanceContent\", i);\n\n                    if (c.Key != 0)\n                        contents.Add(c);\n                }\n                this.InstanceContents = contents;\n            }\n            #endregion\n\n        }\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Xiv/QuestRequirements.PreviousQuestRequirement.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace SaintCoinach.Xiv {\n    partial class QuestRequirements {\n        public class PreviousQuestRequirement {\n            #region Properties\n            public IEnumerable<Quest> PreviousQuests { get; private set; }\n            public PrerequisiteQuestsRequirementType Type { get; private set; }\n            #endregion\n\n            #region Constructor\n            internal PreviousQuestRequirement(Quest quest) {\n                const int QuestCount = 3;\n\n                this.Type = (PrerequisiteQuestsRequirementType)quest.AsInt32(\"PreviousQuestJoin\");\n\n                var contents = new List<Quest>();\n                for (var i = 0; i < QuestCount; ++i) {\n                    var c = quest.As<Quest>(\"PreviousQuest\", i);\n\n                    if (c != null && c.Key != 0)\n                        contents.Add(c);\n                }\n                this.PreviousQuests = contents;\n            }\n            #endregion\n        }\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Xiv/QuestRequirements.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace SaintCoinach.Xiv {\n    public partial class QuestRequirements {\n        #region Fields\n        private readonly Quest _Quest;\n        private ClassJobRequirement[] _ClassJobs;\n        private Quest[] _QuestExclusion;\n        private InstanceContentRequirement _InstanceContents;\n        private PreviousQuestRequirement _PreviousQuests;\n        #endregion\n\n        #region Properties\n        public Quest Quest { get { return _Quest; } }\n        public IEnumerable<ClassJobRequirement> ClassJobs { get { return _ClassJobs ?? (_ClassJobs = BuildClassJobs()); } }\n        public InstanceContentRequirement InstanceContent { get { return _InstanceContents ?? (_InstanceContents = new InstanceContentRequirement(_Quest)); } }\n        public PreviousQuestRequirement PreviousQuest { get { return _PreviousQuests ?? (_PreviousQuests = new PreviousQuestRequirement(_Quest)); } }\n        public int QuestLevelOffset { get { return Quest.AsInt32(\"QuestLevelOffset\"); } }\n        public IEnumerable<Quest> QuestExclusion { get { return _QuestExclusion ?? (_QuestExclusion = BuildQuestExclusion()); } }\n        public GrandCompany GrandCompany { get { return Quest.As<GrandCompany>(); } }\n        public GrandCompanyRank GrandCompanyRank { get { return Quest.As<GrandCompanyRank>(); } }\n\n        public int StartBell { get { return Quest.AsInt32(\"Bell{Start}\"); } }\n        public int EndBell { get { return Quest.AsInt32(\"Bell{End}\"); } }\n\n        public BeastReputationRank BeastReputationRank { get { return Quest.As<BeastReputationRank>(); } }\n\n        public Mount Mount { get { return Quest.As<Mount>(\"Mount{Required}\"); } }\n\n        public bool RequiresHousing { get { return Quest.AsBoolean(\"IsHouseRequired\"); } }\n        #endregion\n\n        #region Constructors\n        public QuestRequirements(Quest quest) {\n            _Quest = quest;\n        }\n        #endregion\n\n        #region Build\n        private ClassJobRequirement[] BuildClassJobs() {\n            const int Count = 2;\n\n            var cjr = new List<ClassJobRequirement>();\n            for (var i = 0; i < Count; ++i) {\n                var cjc = Quest.As<ClassJobCategory>(i);\n                var lv = Quest.AsInt32(\"ClassJobLevel\", i);\n\n                if (cjc.Key != 0 && lv > 0)\n                    cjr.Add(new ClassJobRequirement(cjc, lv));\n            }\n\n            return cjr.ToArray();\n        }\n        private Quest[] BuildQuestExclusion() {\n            const int Count = 2;\n\n            var quests = new List<Quest>();\n            for (var i = 0; i < Count; ++i) {\n                var q = Quest.As<Quest>(\"QuestLock\", i);\n                if (q != null && q.Key != 0)\n                    quests.Add(q);\n            }\n            return quests.ToArray();\n        }\n        #endregion\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Xiv/QuestRewardGroupType.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace SaintCoinach.Xiv {\n    public enum QuestRewardGroupType {\n        Unknown,\n        All,\n        One,\n        GenderSpecificMale,\n        GenderSpecificFemale,\n        OnePerDay,       // Used by 2014 Starlight\n        BeastRankBonus,\n        ClassJob\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Xiv/QuestRewardItem.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace SaintCoinach.Xiv {\n    public class QuestRewardItem {\n        #region Properties\n        public Item Item { get; private set; }\n        public int[] Counts { get; private set; }\n        public Stain Stain { get; private set; }\n        public bool IsHq { get; private set; }\n        #endregion\n\n        #region Constructors\n        public QuestRewardItem(Item item, int count, Stain stain, bool isHq) :\n            this(item, new[] { count }, stain, isHq) { }\n\n        public QuestRewardItem(Item item, IEnumerable<int> counts, Stain stain, bool isHq) {\n            this.Item = item;\n            this.Counts = counts.ToArray();\n            this.Stain = stain;\n            this.IsHq = isHq;\n        }\n        #endregion\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Xiv/QuestRewardItemGroup.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace SaintCoinach.Xiv {\n    public class QuestRewardItemGroup {\n        #region Properties\n        public IEnumerable<QuestRewardItem> Items { get; private set; }\n        public QuestRewardGroupType Type { get; private set; }\n        public ClassJobCategory ClassJobCategory { get; private set; }\n        #endregion\n\n        #region Constructors\n        public QuestRewardItemGroup(IEnumerable<QuestRewardItem> items, QuestRewardGroupType type, ClassJobCategory category) {\n            this.Items = items;\n            this.Type = type;\n            this.ClassJobCategory = category;\n        }\n        #endregion\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Xiv/QuestRewardOther.cs",
    "content": "﻿using SaintCoinach.Ex.Relational;\nusing SaintCoinach.Imaging;\n\nnamespace SaintCoinach.Xiv\n{\n    public class QuestRewardOther : XivRow\n    {\n        #region Properties\n\n        public Text.XivString Name { get { return AsString(\"Name\"); } }\n\n        #endregion\n\n        #region Constructors\n\n        #region Constructor\n\n        public QuestRewardOther(IXivSheet sheet, IRelationalRow sourceRow) : base(sheet, sourceRow) { }\n\n        #endregion\n\n        #endregion\n\n        public override string ToString()\n        {\n            return Name;\n        }\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Xiv/QuestRewards.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace SaintCoinach.Xiv {\n    public class QuestRewards {\n        #region Fields\n        private readonly Quest _Quest;\n        private QuestRewardItemGroup[] _Items;\n        #endregion\n\n        #region Properties\n        public Quest Quest { get { return _Quest; } }\n        public ClassJob ClassJob { get { return Quest.As<ClassJob>(\"ClassJob{Unlock}\"); } }\n        public int Gil { get { return Quest.AsInt32(\"GilReward\"); } }\n        public int ExpFactor { get { return Quest.AsInt32(\"ExpFactor\"); } }\n        public int GrandCompanySeals { get { return Quest.AsInt32(\"GCSeals\"); } }\n        public IEnumerable<QuestRewardItemGroup> Items { get { return _Items ?? (_Items = BuildItems()); } }\n        public Emote Emote { get { return Quest.As<Emote>(\"Emote{Reward}\"); } }\n        public Action Action { get { return Quest.As<Action>(\"Action{Reward}\"); } }\n        public GeneralAction GeneralAction { get { return Quest.As<GeneralAction>(\"GeneralAction{Reward}\"); } }\n        public InstanceContent InstanceContent { get { return Quest.As<InstanceContent>(\"InstanceContent{Unlock}\"); } }\n        public int Reputation { get { return Quest.AsInt32(\"ReputationReward\"); } }\n        public QuestRewardOther QuestRewardOther { get { return Quest.As<QuestRewardOther>(\"Other{Reward}\"); } }\n        #endregion\n\n        #region Constructors\n        public QuestRewards(Quest quest) {\n            _Quest = quest;\n        }\n        #endregion\n\n        #region Build\n        private QuestRewardItemGroup[] BuildItems() {\n            const int CatalystCount = 3;\n            const int Group1Count = 6;\n            const int Group2Count = 5;\n\n            var groupsType = Quest.AsInt32(\"ItemRewardType\");\n            var t1 = QuestRewardGroupType.Unknown;\n            var t2 = QuestRewardGroupType.Unknown;\n            switch (groupsType) {\n                case 0:\n                    return new QuestRewardItemGroup[0];\n                case 1:\n                    t1 = QuestRewardGroupType.All;\n                    t2 = QuestRewardGroupType.One;\n                    break;\n                case 3: // Gender-specific rewards.\n                case 7: // Beast rank bonuses.\n                    // Special handler\n                    break;\n                case 5:\n                    t1 = QuestRewardGroupType.OnePerDay;\n                    t2 = QuestRewardGroupType.OnePerDay;\n                    break;\n                case 6:\n                    // Relic quests\n                    break;\n            }\n\n            var groups = new List<QuestRewardItemGroup>();\n\n            var catalysts = BuildItemGroup(QuestRewardGroupType.All, \"Item{Catalyst}\", \"ItemCount{Catalyst}\", null, null, CatalystCount);\n            groups.Add(catalysts);\n\n            var tomestoneCount = Quest.AsInt32(\"TomestoneCount{Reward}\");\n            if (tomestoneCount > 0) {\n                var tomestoneItem = Quest.As<Item>(\"Tomestone{Reward}\");\n                if (tomestoneItem != null)\n                {\n                    groups.Add(\n                        new QuestRewardItemGroup(\n                            new[] { new QuestRewardItem(tomestoneItem, tomestoneCount, null, false) },\n                            QuestRewardGroupType.All, null));\n                }\n            }\n\n            if (groupsType == 3) {\n                {\n                    var mItem = Quest.As<Item>(\"Item{Reward}\", 0);\n                    var mCount = Quest.AsInt32(\"ItemCount{Reward}\", 0);\n                    var mStain = Quest.As<Stain>(\"Stain{Reward}\", 0);\n\n                    groups.Add(\n                        new QuestRewardItemGroup(\n                            new[] { new QuestRewardItem(mItem, mCount, mStain, false) },\n                            QuestRewardGroupType.GenderSpecificMale, null));\n                }\n                {\n                    var fItem = Quest.As<Item>(\"Item{Reward}\", 1);\n                    var fCount = Quest.AsInt32(\"ItemCount{Reward}\", 1);\n                    var fStain = Quest.As<Stain>(\"Stain{Reward}\", 1);\n\n                    groups.Add(\n                        new QuestRewardItemGroup(\n                            new[] { new QuestRewardItem(fItem, fCount, fStain, false) },\n                            QuestRewardGroupType.GenderSpecificFemale, null));\n                }\n            }\n            else if (groupsType == 6) {\n                groups.AddRange(BuildClassQuestJobRewardItemGroups(\"Item{Reward}\", Group1Count));\n                groups.Add(BuildItemGroup(t2, \"OptionalItem{Reward}\", \"OptionalItemCount{Reward}\", \"OptionalItemStain{Reward}\", \"OptionalItemIsHQ{Reward}\", Group2Count));\n            }\n            else if (groupsType == 7) {\n                var beastRankBonus = (XivRow)Quest.BeastTribe[\"BeastRankBonus\"];\n                var item = beastRankBonus.As<Item>();\n                var counts = new List<int>();\n                for (var i = 0; i < 8; i++)\n                    counts.Add(beastRankBonus.AsInt32(\"Item{Quantity}\", i));\n                groups.Add(new QuestRewardItemGroup(new[] { new QuestRewardItem(item, counts.Distinct(), null, false) }, QuestRewardGroupType.BeastRankBonus, null));\n            } else {\n                groups.Add(BuildItemGroup(t1, \"Item{Reward}\", \"ItemCount{Reward}\", \"Stain{Reward}\", null, Group1Count));\n                groups.Add(BuildItemGroup(t2, \"OptionalItem{Reward}\", \"OptionalItemCount{Reward}\", \"OptionalItemStain{Reward}\", \"OptionalItemIsHQ{Reward}\", Group2Count));\n            }\n\n            return groups.Where(g => g.Items.Any()).ToArray();\n        }\n        private QuestRewardItemGroup BuildItemGroup(QuestRewardGroupType type, string itemPrefix, string countPrefix, string stainPrefix, string hqPrefix, int count) {\n            var items = new List<QuestRewardItem>();\n\n            for (var i = 0; i < count; ++i) {\n                var itm = Quest.As<Item>(itemPrefix, i);\n                var c = Quest.AsInt32(countPrefix, i);\n\n                if (itm == null || itm.Key == 0 || c == 0)\n                    continue;\n\n                Stain s = null;\n                if (stainPrefix != null)\n                    s = Quest.As<Stain>(stainPrefix, i);\n\n                var isHq = false;\n                if (hqPrefix != null)\n                    isHq = Quest.AsBoolean(hqPrefix, i);\n\n                items.Add(new QuestRewardItem(itm, c, s, isHq));\n            }\n\n            return new QuestRewardItemGroup(items, type, null);\n        }\n\n        private List<QuestRewardItemGroup> BuildClassQuestJobRewardItemGroups(string itemPrefix, int count) {\n            var groups = new List<QuestRewardItemGroup>();\n            \n            for (var i = 0; i < count; ++i) {\n                var row = (XivRow)Quest[$\"{itemPrefix}[{i}]\"];\n                if (row == null)\n                    continue;\n\n                var parentRow = (SaintCoinach.Ex.Variant2.DataRow)row.SourceRow;\n                foreach (var subRow in parentRow.SubRows) {\n                    var category = (ClassJobCategory)subRow[\"ClassJobCategory\"];\n                    var items = new List<QuestRewardItem>();\n                    for (var ii = 0; ii < 3; ++ii) {\n                        var itm = (Item)subRow[\"Reward{Item}[\" + ii + \"]\"];\n                        var c = (byte)subRow[\"Reward{Amount}[\" + ii + \"]\"];\n                        if (itm.Key == 0 || c == 0 || category.Key == 0)\n                            continue;\n\n                        items.Add(new QuestRewardItem(itm, c, null, false));\n                    }\n                    groups.Add(new QuestRewardItemGroup(items, QuestRewardGroupType.ClassJob, category));\n                }\n            }\n\n            return groups;\n        }\n        #endregion\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Xiv/Race.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nusing SaintCoinach.Ex.Relational;\n\nnamespace SaintCoinach.Xiv {\n    public class Race : XivRow {\n        #region Properties\n\n        public Text.XivString Masculine { get { return AsString(\"Masculine\"); } }\n        public Text.XivString Feminine { get { return AsString(\"Feminine\"); } }\n\n        public IEnumerable<Item> MaleRse {\n            get {\n                yield return As<Item>(\"RSE{M}{Body}\");\n                yield return As<Item>(\"RSE{M}{Hands}\");\n                yield return As<Item>(\"RSE{M}{Legs}\");\n                yield return As<Item>(\"RSE{M}{Feet}\");\n            }\n        }\n        public IEnumerable<Item> FemaleRse {\n            get {\n                yield return As<Item>(\"RSE{F}{Body}\");\n                yield return As<Item>(\"RSE{F}{Hands}\");\n                yield return As<Item>(\"RSE{F}{Legs}\");\n                yield return As<Item>(\"RSE{F}{Feet}\");\n            }\n        }\n\n        #endregion\n\n        #region Constructor\n\n        public Race(IXivSheet sheet, IRelationalRow sourceRow) : base(sheet, sourceRow) { }\n\n        #endregion\n\n        public override string ToString() {\n            return Feminine;\n        }\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Xiv/RacingChocoboItem.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nusing SaintCoinach.Ex.Relational;\n\nnamespace SaintCoinach.Xiv {\n    public class RacingChocoboItem : XivRow {\n        #region Properties\n\n        public Item Item { get { return As<Item>(); } }\n        public int Category { get { return AsInt32(\"Category\"); } }\n        public int Param1 { get { return AsInt32(\"Param\", 0); } }\n        public int Param2 { get { return AsInt32(\"Param\", 1); } }\n\n        #endregion\n\n        #region Constructors\n\n        public RacingChocoboItem(IXivSheet sheet, IRelationalRow sourceRow) : base(sheet, sourceRow) { }\n\n        #endregion\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Xiv/RacingChocoboName.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nusing SaintCoinach.Ex.Relational;\n\nnamespace SaintCoinach.Xiv {\n    public class RacingChocoboName : XivRow {\n        #region Fields\n        private RacingChocoboNameInfo _Info;\n        #endregion\n\n        #region Properties\n\n        public Text.XivString Name { get { return AsString(\"Name\"); } }\n\n        public RacingChocoboNameInfo Info {\n            get { return _Info ?? (_Info = Sheet.Collection.GetSheet<RacingChocoboNameInfo>()[this.Key]); }\n        }\n\n        public RacingChocoboNameCategory Category { get { return Info.Category; } }\n\n        #endregion\n\n        #region Constructors\n\n        public RacingChocoboName(IXivSheet sheet, IRelationalRow sourceRow) : base(sheet, sourceRow) { }\n\n        #endregion\n\n        public override string ToString() {\n            return Name;\n        }\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Xiv/RacingChocoboNameCategory.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nusing SaintCoinach.Ex.Relational;\n\nnamespace SaintCoinach.Xiv {\n    public class RacingChocoboNameCategory : XivRow {\n        #region Properties\n\n        public Text.XivString Name { get { return AsString(\"Name\"); } }\n\n        public int SortKey { get { return AsInt32(\"SortKey\"); } }\n\n        #endregion\n\n        #region Constructors\n\n        public RacingChocoboNameCategory(IXivSheet sheet, IRelationalRow sourceRow) : base(sheet, sourceRow) { }\n\n        #endregion\n\n        public override string ToString() {\n            return Name;\n        }\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Xiv/RacingChocoboNameInfo.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nusing SaintCoinach.Ex.Relational;\n\nnamespace SaintCoinach.Xiv {\n    public class RacingChocoboNameInfo : XivRow {\n        #region Properties\n\n        public RacingChocoboNameCategory Category { get { return As<RacingChocoboNameCategory>(); } }\n\n        #endregion\n\n        #region Constructors\n\n        public RacingChocoboNameInfo(IXivSheet sheet, IRelationalRow sourceRow) : base(sheet, sourceRow) { }\n\n        #endregion\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Xiv/RacingChocoboParam.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nusing SaintCoinach.Ex.Relational;\n\nnamespace SaintCoinach.Xiv {\n    public class RacingChocoboParam : XivRow {\n        #region Properties\n\n        public Text.XivString Name { get { return AsString(\"Name\"); } }\n\n        #endregion\n\n        #region Constructors\n\n        public RacingChocoboParam(IXivSheet sheet, IRelationalRow sourceRow) : base(sheet, sourceRow) { }\n\n        #endregion\n\n        public override string ToString() {\n            return Name;\n        }\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Xiv/Recipe.cs",
    "content": "using System;\nusing System.Collections.Generic;\nusing System.Linq;\n\nusing SaintCoinach.Ex.Relational;\n\nnamespace SaintCoinach.Xiv {\n    /// <summary>\n    ///     Class representing a crafting recipe.\n    /// </summary>\n    public class Recipe : XivRow, IItemSource {\n        #region Fields\n\n        /// <summary>\n        ///     Ingredients of the current recipe.\n        /// </summary>\n        private RecipeIngredient[] _Ingredients;\n\n        /// <summary>\n        ///     <see cref=\"RecipeLevel\" /> of the current recipe.\n        /// </summary>\n        private RecipeLevel _RecipeLevel;\n\n        /// <summary>\n        ///     A value indicating whether the <see cref=\"UnlockItem\"/> has been fetched.\n        /// </summary>\n        private bool _UnlockItemFetched;\n\n        /// <summary>\n        ///     <see cref=\"Item\"/> required to unlock the current recipe.\n        /// </summary>\n        private Item _UnlockItem;\n\n        #endregion\n\n        #region Properties\n\n        /// <summary>\n        ///     Gets the <see cref=\"CraftType\" /> of the current recipe.\n        /// </summary>\n        /// <value>The <see cref=\"CraftType\" /> of the current recipe.</value>\n        public CraftType CraftType { get { return As<CraftType>(); } }\n\n        /// <summary>\n        ///     Gets the <see cref=\"ClassJob\" /> of the current recipe.\n        /// </summary>\n        /// <value>The <see cref=\"ClassJob\" /> of the current recipe.</value>\n        public ClassJob ClassJob { get { return CraftType.ClassJob; } }\n\n        /// <summary>\n        ///     Gets the <see cref=\"RecipeLevel\" /> of the current recipe.\n        /// </summary>\n        /// <value>The <see cref=\"RecipeLevel\" /> of the current recipe.</value>\n        public RecipeLevel RecipeLevel { get { return _RecipeLevel ?? (_RecipeLevel = new RecipeLevel(this)); } }\n\n        /// <summary>\n        ///     Gets the <see cref=\"Item\" /> created by the current recipe.\n        /// </summary>\n        /// <value>The <see cref=\"Item\" /> created by the current recipe.</value>\n        public Item ResultItem { get { return As<Item>(\"Item{Result}\"); } }\n\n        /// <summary>\n        ///     Gets the number of items created by the current recipe.\n        /// </summary>\n        /// <value>The number of items created by the current recipe.</value>\n        public int ResultCount { get { return AsInt32(\"Amount{Result}\"); } }\n\n        /// <summary>\n        ///     Gets the <see cref=\"RecipeIngredient\" />s of the current recipe.\n        /// </summary>\n        /// <value>The <see cref=\"RecipeIngredient\" />s of the current recipe.</value>\n        public IEnumerable<RecipeIngredient> Ingredients {\n            get { return _Ingredients ?? (_Ingredients = BuildIngredients()); }\n        }\n\n        /// <summary>\n        ///     Gets a value indicating whether the current recipe uses the secondary tool.\n        /// </summary>\n        /// <value>A value indicating whether the current recipe uses the secondary tool.</value>\n        public bool IsSecondary { get { return AsBoolean(\"IsSecondary\"); } }\n\n        /// <summary>\n        ///     Gets a value indicating whether the current recipe can be used for quick synthesis.\n        /// </summary>\n        /// <value>A value indicating whether the current recipe can be used for quick synthesis..</value>\n        public bool CanQuickSynth { get { return AsBoolean(\"CanQuickSynth\"); } }\n\n        /// <summary>\n        ///     Gets a value indicating whether the current recipe can produce high-quality items.\n        /// </summary>\n        /// <value>A value indicating whether the current recipe can produce high-quality items.</value>\n        public bool CanHq { get { return AsBoolean(\"CanHq\"); } }\n\n        /// <summary>\n        ///     Gets the required craftsmanship value to attempt the current recipe.\n        /// </summary>\n        /// <value>The required craftsmanship value to attempt the current recipe.</value>\n        public int RequiredCraftsmanship { get { return AsInt32(\"RequiredCraftsmanship\"); } }\n\n        /// <summary>\n        ///     Gets the required control value to attempt the current recipe.\n        /// </summary>\n        /// <value>The required control value to attempt the current recipe.</value>\n        public int RequiredControl { get { return AsInt32(\"RequiredControl\"); } }\n\n        /// <summary>\n        ///     Gets the required craftsmanship value to quick synth the current recipe.\n        /// </summary>\n        /// <value>The required craftsmanship value to quick synth the current recipe.</value>\n        public int QuickSynthCraftsmanship { get { return AsInt32(\"QuickSynthCraftsmanship\"); } }\n\n        /// <summary>\n        ///     Gets the required control value to quick synth the current recipe.\n        /// </summary>\n        /// <value>The required control value to quick synth the current recipe.</value>\n        public int QuickSynthControl { get { return AsInt32(\"QuickSynthControl\"); } }\n\n        /// <summary>\n        ///     Gets the required <see cref=\"Status\" /> to attempt the current recipe.\n        /// </summary>\n        /// <value>The required <see cref=\"Status\" /> to attempt the current recipe.</value>\n        public Status RequiredStatus { get { return As<Status>(\"Status{Required}\"); } }\n\n        /// <summary>\n        ///     Gets the required equipped <see cref=\"Item\" /> to attempt the current recipe.\n        /// </summary>\n        /// <value>The required equipped <see cref=\"Item\" /> to attempt the current recipe.</value>\n        public Item RequiredItem { get { return As<Item>(\"Item{Required}\"); } }\n\n        /// <summary>\n        ///     Gets the <see cref=\"RecipeLevelTable\" /> used by the current recipe.\n        /// </summary>\n        /// <value>The <see cref=\"RecipeLevelTable\" /> used by the current recipe.</value>\n        public RecipeLevelTable RecipeLevelTable { get { return As<RecipeLevelTable>(); } }\n\n        /// <summary>\n        ///     Gets the factor, in percent, to apply to the difficulty of the the current recipe's <see cref=\"RecipeLevelTable\" />\n        ///     .\n        /// </summary>\n        /// <value>The factor, in percent, to apply to the difficulty of the the current recipe's <see cref=\"RecipeLevelTable\" />.</value>\n        public int DifficultyFactor { get { return AsInt32(\"DifficultyFactor\"); } }\n\n        /// <summary>\n        ///     Gets the factor, in percent, to apply to the quality of the the current recipe's <see cref=\"RecipeLevelTable\" />.\n        /// </summary>\n        /// <value>The factor, in percent, to apply to the quality of the the current recipe's <see cref=\"RecipeLevelTable\" />.</value>\n        public int QualityFactor { get { return AsInt32(\"QualityFactor\"); } }\n\n        /// <summary>\n        ///     Gets the factor, in percent, to apply to the durability of the the current recipe's <see cref=\"RecipeLevelTable\" />.\n        /// </summary>\n        /// <value>The factor, in percent, to apply to the durability of the the current recipe's <see cref=\"RecipeLevelTable\" />.</value>\n        public int DurabilityFactor { get { return AsInt32(\"DurabilityFactor\"); } }\n\n        /// <summary>\n        ///     Gets the factor, in percent, that high quality materials contribute to the current recipe's quality.\n        /// </summary>\n        /// <value>The factor, in percent, that high quality materials contribute to the current recipe's quality.</value>\n        public int MaterialQualityFactor => AsInt32(\"MaterialQualityFactor\");\n\n        /// <summary>\n        ///     Gets the <see cref=\"Item\"/> required to unlock the current recipe.\n        /// </summary>\n        /// <value>The <see cref=\"Item\"/> required to unlock the current recipe.</value>\n        public Item UnlockItem {\n            get {\n                if (!_UnlockItemFetched) {\n                    var secretRecipeBook = (IXivRow)this[\"SecretRecipeBook\"];\n                    if (secretRecipeBook != null && secretRecipeBook.Key != 0)\n                        _UnlockItem = (Item)secretRecipeBook[\"Item\"];\n                    _UnlockItemFetched = true;\n                }\n                return _UnlockItem;\n            }\n        }\n\n        /// <summary>\n        ///     Gets a value indicating whether the current recipe requires a specialization soul.\n        /// </summary>\n        /// <value>A value indicating whether the current recipe requires a specialization soul.</value>\n        public bool IsSpecializationRequired {  get { return AsBoolean(\"IsSpecializationRequired\"); } }\n        #endregion\n\n        #region Constructors\n\n        /// <summary>\n        ///     Initializes a new instance of the <see cref=\"Recipe\" /> class.\n        /// </summary>\n        /// <param name=\"sheet\"><see cref=\"IXivSheet\" /> containing this object.</param>\n        /// <param name=\"sourceRow\"><see cref=\"IRelationalRow\" /> to read data from.</param>\n        public Recipe(IXivSheet sheet, IRelationalRow sourceRow) : base(sheet, sourceRow) { }\n\n        #endregion\n\n        #region Build\n\n        /// <summary>\n        ///     Build an array of <see cref=\"RecipeIngredient\" />s used in the current recipe.\n        /// </summary>\n        /// <returns>An array of <see cref=\"RecipeIngredient\" />s used in the current recipe.</returns>\n        private RecipeIngredient[] BuildIngredients() {\n            const int MaterialCount = 8;\n            const int CrystalCategory = 59;\n\n            var ingredients = new List<RecipeIngredient>();\n\n            for (var i = 0; i < MaterialCount; ++i) {\n                var item = As<Item>(\"Item{Ingredient}\", i);\n                if (item == null || item.Key == 0)\n                    continue;\n\n                var count = AsInt32(\"Amount{Ingredient}\", i);\n                if (count == 0)\n                    continue;\n\n                var type = item.ItemUICategory.Key == CrystalCategory ? RecipeIngredientType.Crystal : RecipeIngredientType.Material;\n                ingredients.Add(new RecipeIngredient(type, item, count));\n            }\n            var itemLevelSum =\n                ingredients.Where(_ => _.Type == RecipeIngredientType.Material)\n                           .Sum(_ => _.Count * _.Item.ItemLevel.Key);\n            var qualityFromMats = RecipeLevel.Quality * ((float)MaterialQualityFactor / 100);\n            foreach (var mat in ingredients.Where(_ => _.Type == RecipeIngredientType.Material))\n                mat.QualityPerItem = mat.Item.ItemLevel.Key * qualityFromMats / itemLevelSum;\n\n            return ingredients.ToArray();\n        }\n\n        #endregion\n\n        public int BaseProgress(int craftsmanship, int crafterLevel) {\n            var diff = GetCraftLevelDifference(crafterLevel);\n            if (diff == null)\n                throw new ArgumentException(\"Invalid crafter level / recipe level difference\", \"crafterLevel\");\n\n            return (craftsmanship + 10000)\n                / (RecipeLevelTable.SuggestedCraftsmanship + 10000)\n                * ((craftsmanship * 21) / 100 + 2)\n                * diff.ProgressFactor / 100;\n        }\n\n        public int BaseQuality(int control, int crafterLevel) {\n            var diff = GetCraftLevelDifference(crafterLevel);\n            if (diff == null)\n                throw new ArgumentException(\"Invalid crafter level / recipe level difference\", \"crafterLevel\");\n            return (control + 10000)\n                / (RecipeLevelTable.SuggestedControl + 10000)\n                * ((control * 35) / 100 + 35)\n                * diff.QualityFactor / 100;\n        }\n\n        public CraftLevelDifference GetCraftLevelDifference(int crafterLevel) {\n            var levelDiff = crafterLevel - RecipeLevelTable.Key;\n            var sheet = Sheet.Collection.GetSheet<CraftLevelDifference>();\n            return sheet.ContainsRow(levelDiff) ? sheet[levelDiff] : null;\n        }\n\n        #region IItemSource Members\n\n        IEnumerable<Item> IItemSource.Items {\n            get { yield return this.ResultItem; }\n        }\n\n        #endregion\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Xiv/RecipeElement.cs",
    "content": "using SaintCoinach.Ex.Relational;\n\nnamespace SaintCoinach.Xiv {\n    public class RecipeElement : XivRow {\n        #region Properties\n\n        public Text.XivString Name { get { return AsString(\"Name\"); } }\n\n        #endregion\n\n        #region Constructors\n\n        #region Constructor\n\n        public RecipeElement(IXivSheet sheet, IRelationalRow sourceRow) : base(sheet, sourceRow) { }\n\n        #endregion\n\n        #endregion\n\n        public override string ToString() {\n            return Name;\n        }\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Xiv/RecipeIngredient.cs",
    "content": "﻿namespace SaintCoinach.Xiv {\n    /// <summary>\n    ///     Class representing an ingredient in a <see cref=\"Recipe\" />.\n    /// </summary>\n    public class RecipeIngredient {\n        #region Properties\n\n        /// <summary>\n        ///     Gets the <see cref=\"RecipeIngredientType\" /> of the current ingredient.\n        /// </summary>\n        /// <value>The <see cref=\"RecipeIngredientType\" /> of the current ingredient.</value>\n        public RecipeIngredientType Type { get; private set; }\n\n        /// <summary>\n        ///     Gets the <see cref=\"Item\" /> of the current ingredient.\n        /// </summary>\n        /// <value>The <see cref=\"Item\" /> of the current ingredient.</value>\n        public Item Item { get; private set; }\n\n        /// <summary>\n        ///     Gets the item count for the current ingredient.\n        /// </summary>\n        /// <value>The item count for the current ingredient.</value>\n        public int Count { get; private set; }\n\n        /// <summary>\n        ///     Gets the quality gained per high-quality item used for the current ingredient.\n        /// </summary>\n        /// <value>The quality gained per high-quality item used for the current ingredient.</value>\n        public float QualityPerItem { get; internal set; }\n\n        #endregion\n\n        #region Constructors\n\n        /// <summary>\n        ///     Initializes a new instance of the <see cref=\"RecipeIngredient\" /> class.\n        /// </summary>\n        /// <param name=\"type\"><see cref=\"RecipeIngredientType\" /> for the ingredient.</param>\n        /// <param name=\"item\"><see cref=\"Item\" /> for the ingredient.</param>\n        /// <param name=\"count\">Item count for the ingredient.</param>\n        public RecipeIngredient(RecipeIngredientType type, Item item, int count) {\n            Type = type;\n            Item = item;\n            Count = count;\n        }\n\n        #endregion\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Xiv/RecipeIngredientType.cs",
    "content": "﻿namespace SaintCoinach.Xiv {\n    /// <summary>\n    /// Enumeration of types of ingredients used by recipes.\n    /// </summary>\n    public enum RecipeIngredientType {\n        /// <summary>\n        /// Value indicating an ingredient is a regular material.\n        /// </summary>\n        Material,\n\n        /// <summary>\n        /// Value indicating an ingredient is used as catalyst.\n        /// </summary>\n        Crystal\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Xiv/RecipeLevel.cs",
    "content": "﻿namespace SaintCoinach.Xiv {\n    /// <summary>\n    ///     Class representing the level and parameters for a specific <see cref=\"Recipe\" />.\n    /// </summary>\n    public class RecipeLevel {\n        #region Properties\n\n        /// <summary>\n        ///     Gets the <see cref=\"RecipeLevelTable\" /> used for the current recipe.\n        /// </summary>\n        /// <value>The <see cref=\"RecipeLevelTable\" /> used for the current recipe.</value>\n        public RecipeLevelTable RecipeLevelTable { get; private set; }\n\n        /// <summary>\n        ///     Gets the difficulty (required progress) of the current recipe.\n        /// </summary>\n        /// <value>The difficulty (required progress) of the current recipe.</value>\n        public int Difficulty { get; private set; }\n\n        /// <summary>\n        ///     Gets the maximum quality of the current recipe.\n        /// </summary>\n        /// <value>The maximum quality of the current recipe.</value>\n        public int Quality { get; private set; }\n\n        /// <summary>\n        ///     Gets the maximum durability of the current recipe.\n        /// </summary>\n        /// <value>The maximum durability of the current recipe.</value>\n        public int Durability { get; private set; }\n\n        #endregion\n\n        #region Constructors\n\n        /// <summary>\n        ///     Initializes a new instance of the <see cref=\"RecipeLevel\" /> class.\n        /// </summary>\n        /// <param name=\"recipe\"><see cref=\"Recipe\" /> for which to fill the properties.</param>\n        public RecipeLevel(Recipe recipe) {\n            RecipeLevelTable = recipe.RecipeLevelTable;\n            Difficulty = (RecipeLevelTable.Difficulty * recipe.DifficultyFactor) / 100;\n            Quality = (RecipeLevelTable.Quality * recipe.QualityFactor) / 100;\n            Durability = (RecipeLevelTable.Durability * recipe.DurabilityFactor) / 100;\n        }\n\n        #endregion\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Xiv/RecipeLevelTable.cs",
    "content": "using SaintCoinach.Ex.Relational;\n\nnamespace SaintCoinach.Xiv {\n    /// <summary>\n    ///     Class representing the table used as base for difficulty, quality and durability operations on\n    ///     <see cref=\"Recipe\" />s.\n    /// </summary>\n    public class RecipeLevelTable : XivRow {\n        #region Properties\n\n        public int ClassJobLevel => AsInt32(\"ClassJobLevel\");\n\n        /// <summary>\n        ///     Gets the base difficulty for the current level.\n        /// </summary>\n        /// <value>The base difficulty for the current level.</value>\n        public int Difficulty => AsInt32(\"Difficulty\");\n\n        /// <summary>\n        ///     Gets the base quality for the current level.\n        /// </summary>\n        /// <value>The base quality for the current level.</value>\n        public int Quality => AsInt32(\"Quality\");\n\n        /// <summary>\n        ///     Gets the base durability for the current level.\n        /// </summary>\n        /// <value>The base durability for the current level.</value>\n        public int Durability => AsInt32(\"Durability\");\n\n        /// <summary>\n        ///     Gets the base star difficulty for the current level.\n        /// </summary>\n        /// <value>The base star difficulty for the current level.</value>\n        public int Stars => AsInt32(\"Stars\");\n\n        public int SuggestedCraftsmanship => AsInt32(\"SuggestedCraftsmanship\");\n        public int SuggestedControl => AsInt32(\"SuggestedControl\");\n\n        #endregion\n\n        #region Constructors\n\n        /// <summary>\n        ///     Initializes a new instance of the <see cref=\"RecipeLevelTable\" /> class.\n        /// </summary>\n        /// <param name=\"sheet\"><see cref=\"IXivSheet\" /> containing this object.</param>\n        /// <param name=\"sourceRow\"><see cref=\"IRelationalRow\" /> to read data from.</param>\n        public RecipeLevelTable(IXivSheet sheet, IRelationalRow sourceRow) : base(sheet, sourceRow) { }\n\n        #endregion\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Xiv/RelicNote.cs",
    "content": "using System.Collections.Generic;\n\nusing SaintCoinach.Ex.Relational;\n\nnamespace SaintCoinach.Xiv {\n    public class RelicNote : XivRow {\n        #region Fields\n\n        private FateTarget[] _Fates;\n        private Leve[] _Leves;\n        private MonsterNoteTarget[] _NotoriousTarget;\n        private Target[] _Targets;\n\n        #endregion\n\n        #region Properties\n\n        public EventItem EventItem { get { return As<EventItem>(); } }\n        public IEnumerable<Target> Targets { get { return _Targets ?? (_Targets = BuildTargets()); } }\n\n        public IEnumerable<MonsterNoteTarget> NotoriousTargets {\n            get { return _NotoriousTarget ?? (_NotoriousTarget = BuildNotoriousTargets()); }\n        }\n\n        public IEnumerable<FateTarget> Fates { get { return _Fates ?? (_Fates = BuildFates()); } }\n        public IEnumerable<Leve> Leves { get { return _Leves ?? (_Leves = BuildLeves()); } }\n\n        #endregion\n\n        #region Constructors\n\n        #region Constructor\n\n        public RelicNote(IXivSheet sheet, IRelationalRow sourceRow) : base(sheet, sourceRow) { }\n\n        #endregion\n\n        #endregion\n\n        #region Target\n\n        public class Target {\n            #region Properties\n\n            public MonsterNoteTarget MonsterNoteTarget { get; private set; }\n            public int Count { get; private set; }\n\n            #endregion\n\n            #region Constructors\n\n            #region Constructor\n\n            public Target(MonsterNoteTarget monsterNoteTarget, int count) {\n                MonsterNoteTarget = monsterNoteTarget;\n                Count = count;\n            }\n\n            #endregion\n\n            #endregion\n        }\n\n        #endregion\n\n        #region Fate\n\n        public class FateTarget {\n            #region Properties\n\n            public Fate Fate { get; private set; }\n            public PlaceName PlaceName { get; private set; }\n\n            #endregion\n\n            #region Constructors\n\n            #region Constructor\n\n            public FateTarget(Fate fate, PlaceName placeName) {\n                Fate = fate;\n                PlaceName = placeName;\n            }\n\n            #endregion\n\n            #endregion\n        }\n\n        #endregion\n\n        #region Build\n\n        private Target[] BuildTargets() {\n            const int Count = 10;\n\n            var targets = new Target[Count];\n            for (var i = 0; i < targets.Length; ++i) {\n                var monster = As<MonsterNoteTarget>(\"MonsterNoteTarget{Common}\", i);\n                var count = AsInt32(\"MonsterCount\", i);\n\n                targets[i] = new Target(monster, count);\n            }\n\n            return targets;\n        }\n\n        private MonsterNoteTarget[] BuildNotoriousTargets() {\n            const int Count = 3;\n\n            var targets = new MonsterNoteTarget[Count];\n            for (var i = 0; i < targets.Length; ++i)\n                targets[i] = As<MonsterNoteTarget>(\"MonsterNoteTarget{NM}\", i);\n\n            return targets;\n        }\n\n        private FateTarget[] BuildFates() {\n            const int Count = 3;\n\n            var fates = new FateTarget[Count];\n            for (var i = 0; i < fates.Length; ++i) {\n                var fate = As<Fate>(\"Fate\", i);\n                var place = As<PlaceName>(\"PlaceName{Fate}\", i);\n\n                fates[i] = new FateTarget(fate, place);\n            }\n\n            return fates;\n        }\n\n        private Leve[] BuildLeves() {\n            const int Count = 3;\n\n            var leves = new Leve[Count];\n            for (var i = 0; i < leves.Length; ++i)\n                leves[i] = As<Leve>(\"Leve\", i);\n            return leves;\n        }\n\n        #endregion\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Xiv/RelicNoteCategory.cs",
    "content": "using SaintCoinach.Ex.Relational;\n\nnamespace SaintCoinach.Xiv {\n    public class RelicNoteCategory : XivRow {\n        #region Properties\n\n        public Text.XivString Text { get { return AsString(\"Text\"); } }\n        public RelicNoteCategoryText RelicNoteCategoryText { get { return As<RelicNoteCategoryText>(); } }\n\n        #endregion\n\n        #region Constructors\n\n        #region Constructor\n\n        public RelicNoteCategory(IXivSheet sheet, IRelationalRow sourceRow) : base(sheet, sourceRow) { }\n\n        #endregion\n\n        #endregion\n\n        public override string ToString() {\n            return Text;\n        }\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Xiv/RelicNoteCategoryText.cs",
    "content": "using SaintCoinach.Ex.Relational;\n\nnamespace SaintCoinach.Xiv {\n    public class RelicNoteCategoryText : XivRow {\n        #region Properties\n\n        public Text.XivString Text { get { return AsString(\"Text\"); } }\n\n        #endregion\n\n        #region Constructors\n\n        #region Constructor\n\n        public RelicNoteCategoryText(IXivSheet sheet, IRelationalRow sourceRow) : base(sheet, sourceRow) { }\n\n        #endregion\n\n        #endregion\n\n        public override string ToString() {\n            return Text;\n        }\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Xiv/RetainerTask.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace SaintCoinach.Xiv {\n    public class RetainerTask : XivRow, IItemSource {\n        #region Properties\n\n        public ClassJobCategory ClassJobCategory { get { return As<ClassJobCategory>(); } }\n        public bool IsRandom { get { return AsBoolean(\"IsRandom\"); } }\n        public int RetainerLevel { get { return AsInt32(\"RetainerLevel\"); } }\n        public int VentureCost { get { return AsInt32(\"VentureCost\"); } }\n        public TimeSpan BaseDuration { get { return TimeSpan.FromMinutes(AsInt32(\"MaxTime{min}\")); } }\n        public int Experience { get { return AsInt32(\"Experience\"); } }\n        public int RequiredItemLevel { get { return AsInt32(\"RequiredItemLevel\"); } }\n        public int RequiredGathering { get { return AsInt32(\"RequiredGathering\"); } }\n        public RetainerTaskBase Task { get { return As<RetainerTaskBase>(\"Task\"); } }\n\n        public IEnumerable<Item> Items { get { return ((IItemSource)Task).Items; } }\n\n        #endregion\n\n        #region Constructors\n\n        public RetainerTask(IXivSheet sheet, SaintCoinach.Ex.Relational.IRelationalRow sourceRow) : base(sheet, sourceRow) { }\n\n        #endregion\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Xiv/RetainerTaskBase.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace SaintCoinach.Xiv {\n    public abstract class RetainerTaskBase : XivRow, IItemSource {\n        #region Properties\n\n        public abstract IEnumerable<Item> Items { get; }\n\n        #endregion\n\n        #region Constructors\n\n        protected RetainerTaskBase(IXivSheet sheet, SaintCoinach.Ex.Relational.IRelationalRow sourceRow) : base(sheet, sourceRow) { }\n\n        #endregion\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Xiv/RetainerTaskNormal.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace SaintCoinach.Xiv {\n    public class RetainerTaskNormal : RetainerTaskBase {\n        #region Properties\n\n        public Item Item { get { return As<Item>(); } }\n        public IEnumerable<int> Quantities {\n            get {\n                const int Count = 5;\n                for (var i = 0; i < Count; ++i)\n                    yield return AsInt32(\"Quantity\", i);\n            }\n        }\n        public override IEnumerable<Item> Items { get { yield return Item; } }\n\n        #endregion\n\n        #region Constructors\n\n        public RetainerTaskNormal(IXivSheet sheet, SaintCoinach.Ex.Relational.IRelationalRow sourceRow) : base(sheet, sourceRow) { }\n\n        #endregion\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Xiv/RetainerTaskRandom.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace SaintCoinach.Xiv {\n    public class RetainerTaskRandom : RetainerTaskBase {\n        #region Properties\n        \n        public Text.XivString Name { get { return AsString(\"Name\"); } }\n        public int Requirement { get { return AsInt32(\"Requirement\"); } }\n        public override IEnumerable<Item> Items { get { yield break; } }\n\n        #endregion\n\n        #region Constructors\n\n        public RetainerTaskRandom(IXivSheet sheet, SaintCoinach.Ex.Relational.IRelationalRow sourceRow) : base(sheet, sourceRow) { }\n\n        #endregion\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Xiv/Salvage.cs",
    "content": "﻿using SaintCoinach.Ex.Relational;\nusing System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace SaintCoinach.Xiv {\n    public class Salvage : XivRow {\n        #region Properties\n\n        public int OptimalSkill { get { return AsInt32(\"OptimalSkill\"); } }\n\n        #endregion\n\n        #region Constructors\n\n        #region Constructor\n\n        public Salvage(IXivSheet sheet, IRelationalRow sourceRow) : base(sheet, sourceRow) { }\n\n        #endregion\n\n        #endregion\n\n        public override string ToString() {\n            return OptimalSkill.ToString();\n        }\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Xiv/Sheets/InventoryItemSheet.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Reflection;\n\nusing SaintCoinach.Ex.Relational;\nusing SaintCoinach.Xiv.Items;\n\nnamespace SaintCoinach.Xiv.Sheets {\n    public class InventoryItemSheet : XivSheet<Item> {\n        #region Static\n\n        // Types as defined in ItemUICategory\n        private static readonly Type DefaultItemType = typeof(Item);\n\n        private static readonly Dictionary<int, Type> ItemTypes = new Dictionary<int, Type> {\n            // \n            {\n                1, typeof(PhysicalWeapon)   // PGL\n            }, {\n                2, typeof(PhysicalWeapon)   // GLD\n            }, {\n                3, typeof(PhysicalWeapon)   // MRD\n            }, {\n                4, typeof(PhysicalWeapon)   // ARC\n            }, {\n                5, typeof(PhysicalWeapon)   // LNC\n            }, {\n                84, typeof(PhysicalWeapon)  // ROG\n            }, {\n                87, typeof(PhysicalWeapon)  // DRK\n            }, {\n                88, typeof(PhysicalWeapon)  // MCH\n            }, {\n                96, typeof(PhysicalWeapon)  // SAM\n            }, {\n                106, typeof(PhysicalWeapon) // GNB\n            }, {\n                107, typeof(PhysicalWeapon) // DNC\n            }, { \n                108, typeof(PhysicalWeapon) // RPR\n            },\n\n            // \n            {\n                6, typeof(MagicWeapon)      // THM (one-handed)\n            }, {\n                7, typeof(MagicWeapon)      // THM (two-handed)\n            }, {\n                8, typeof(MagicWeapon)      // CNJ (one-handed)\n            }, {\n                9, typeof(MagicWeapon)      // CNJ (two-handed)\n            }, {\n                10, typeof(MagicWeapon)     // ACN\n            }, {\n                89, typeof(MagicWeapon)     // AST\n            }, {\n                97, typeof(MagicWeapon)     // RDM\n            }, {\n                98, typeof(MagicWeapon)     // SCH\n            }, {\n                105, typeof(MagicWeapon)    // BLU\n            }, { \n                109, typeof(MagicWeapon)    // SGE\n            },\n            // \n            {\n                11, typeof(Shield)\n            },\n            // \n            {\n                12, typeof(CraftingTool)    // CRP (main)\n            }, {\n                13, typeof(CraftingTool)    // CRP (off)\n            }, {\n                14, typeof(CraftingTool)    // BSM\n            }, {\n                15, typeof(CraftingTool)    // BSM\n            }, {\n                16, typeof(CraftingTool)    // ARM\n            }, {\n                17, typeof(CraftingTool)    // ARM\n            }, {\n                18, typeof(CraftingTool)    // GSM\n            }, {\n                19, typeof(CraftingTool)    // GSM\n            }, {\n                20, typeof(CraftingTool)    // LTW\n            }, {\n                21, typeof(CraftingTool)    // LTW\n            }, {\n                22, typeof(CraftingTool)    // WVR\n            }, {\n                23, typeof(CraftingTool)    // WVR\n            }, {\n                24, typeof(CraftingTool)    // ALC\n            }, {\n                25, typeof(CraftingTool)    // ALC\n            }, {\n                26, typeof(CraftingTool)    // CUL\n            }, {\n                27, typeof(CraftingTool)    // CUL\n            },\n            // \n            {\n                28, typeof(GatheringTool)   // MIN (main)\n            }, {\n                29, typeof(GatheringTool)   // MIN (sub)\n            }, {\n                30, typeof(GatheringTool)   // BTN\n            }, {\n                31, typeof(GatheringTool)   // BTN\n            }, {\n                32, typeof(GatheringTool)   // FSH\n            }, {\n                33, typeof(GatheringTool)   // FSH (tackle)\n            }, {\n                99, typeof(GatheringTool)   // FSH (sub)\n            },\n\n            // \n            {\n                34, typeof(Armour)          // Head\n            }, {\n                35, typeof(Armour)          // Body\n            }, {\n                36, typeof(Armour)          // Legs\n            }, {\n                37, typeof(Armour)          // Hands\n            }, {\n                38, typeof(Armour)          // Feet\n            }, {\n                39, typeof(Armour)          // Waist\n            },\n            // \n            {\n                40, typeof(Accessory)       // Neck\n            }, {\n                41, typeof(Accessory)       // Ears\n            }, {\n                42, typeof(Accessory)       // Wrists\n            }, {\n                43, typeof(Accessory)       // Fingers\n            },\n            // \n            {\n                44, typeof(Usable)          // Medicine\n            }, {\n                45, typeof(Usable)          // Ingredient\n            }, {\n                46, typeof(Usable)          // Meal\n            },\n            //\n            {\n                57, typeof(Housing)         // Furnishing\n            },\n            // \n            {\n                58, typeof(MateriaItem)     // Materia\n            },\n            // \n            {\n                62, typeof(SoulCrystal)     // Soul Crystal\n            },\n            //\n            {\n                64, typeof(Housing)         // Construction Permit\n            }, {\n                65, typeof(Housing)         // Root\n            }, {\n                66, typeof(Housing)         // Exterior Wall\n            }, {\n                67, typeof(Housing)         // Window\n            }, {\n                68, typeof(Housing)         // Door\n            }, {\n                69, typeof(Housing)         // Roof Decoration\n            }, {\n                70, typeof(Housing)         // Exterior Wall Decoration\n            }, {\n                71, typeof(Housing)         // Placard\n            }, {\n                72, typeof(Housing)         // Fence\n            }, {\n                73, typeof(Housing)         // Interior Wall\n            }, {\n                74, typeof(Housing)         // Flooring\n            }, {\n                75, typeof(Housing)         // Ceiling Light\n            }, {\n                76, typeof(Housing)         // Outdoor Furnishing\n            }, {\n                77, typeof(Housing)         // Table\n            }, {\n                78, typeof(Housing)         // Tabletop\n            }, {\n                79, typeof(Housing)         // Wall-mounted\n            }, {\n                80, typeof(Housing)         // Rug\n            }\n        };\n\n        #endregion\n\n        #region Constructors\n\n        public InventoryItemSheet(XivCollection collection, IRelationalSheet source) : base(collection, source) { }\n\n        #endregion\n\n        #region Factory\n\n        protected override Item CreateRow(IRelationalRow sourceRow) {\n            var uiCategory = (ItemUICategory)sourceRow[\"ItemUICategory\"];\n\n            if (!ItemTypes.TryGetValue(uiCategory.Key, out Type type))\n                type = DefaultItemType;\n            var args = new object[] {\n                this, sourceRow\n            };\n\n            const BindingFlags ActivatorBindFlags =\n                BindingFlags.Instance | BindingFlags.CreateInstance | BindingFlags.Public\n                | BindingFlags.NonPublic;\n            return (Item)Activator.CreateInstance(type, ActivatorBindFlags, null, args, null);\n        }\n\n        #endregion\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Xiv/Sheets/ItemActionSheet.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Diagnostics;\nusing System.Reflection;\n\nusing SaintCoinach.Ex.Relational;\n\nnamespace SaintCoinach.Xiv.Sheets {\n    using IA = ItemActions;\n\n    public class ItemActionSheet : XivSheet<ItemAction> {\n        #region Static\n\n        private static readonly Type DefaultItemActionType = typeof(ItemAction);\n\n        private static readonly Dictionary<int, Type> ItemActionTypes = new Dictionary<int, Type> {\n            {\n                // Just so it stops giving messages.\n                0, typeof(ItemAction)\n            }, {\n                816, typeof(IA.CompanyEffect)\n            }, {\n                841, typeof(IA.HostileEffect)\n            }, {\n                842, typeof(IA.StatusRemoval)\n            }, {\n                843, typeof(IA.FriendlyEffect)\n            }, {\n                844, typeof(IA.Food)\n            }, {\n                845, typeof(IA.Food)\n            }, {\n                846, typeof(IA.Enhancement)\n            }, {\n                847, typeof(IA.HpRecovery)\n            }, {\n                848, typeof(IA.MpRecovery)\n            }, {\n                849, typeof(IA.HpMpRecovery)\n            }, {\n                850, typeof(IA.Sanction)\n            }, {\n                851, typeof(IA.BuddySummon)\n            }, {\n                852, typeof(IA.Fireworks)\n            }, {\n                853, typeof(IA.CompanionUnlock)\n            }, {\n                944, typeof(IA.MgpCard)\n            }, {\n                1013, typeof(IA.BuddyEquipUnlock)\n            }, {\n                1053, typeof(IA.Raise)\n            }, {\n                1054, typeof(IA.AttributeReset)\n            }, {\n                1055, typeof(IA.GpRecovery)\n            }, {\n                1058, typeof(IA.SphereScroll)\n            }, {\n                1322, typeof(IA.MountUnlock)\n            }, {\n                1326, typeof(IA.Fantasia)\n            }, {\n                1767, typeof(IA.TpRecovery)\n            }, {\n                2120, typeof(IA.ItemRoulette)\n            }, {\n                2136, typeof(IA.RecipeBookUnlock)\n            }, {\n                2483, typeof(IA.ChocoboFeed)\n            }, {\n                2633, typeof(IA.CustomizeUnlock)\n            }, {\n                2634, typeof(IA.ChocoboActionReset)\n            }, {\n                2645, typeof(IA.RealmRebornRed)\n            }, {\n                2894, typeof(IA.EternityRing)\n            }, {\n                3237, typeof(IA.DesynthSkillReset)\n            }, {\n                3240, typeof(IA.EternalBondInvitation)\n            }, {\n                3292, typeof(IA.ChocoboLevelCapIncrease)\n            }, {\n                3357, typeof(IA.TripleTriadCardUnlock)\n            }, {\n                3800, typeof(IA.MgpCard)\n            }, {\n                4107, typeof(IA.FolkloreBook)\n            }, {\n                4647, typeof(IA.EquipmentCoffer)\n            }, {\n                5136, typeof(IA.Heavenscracker)\n            }, {\n                5564, typeof(IA.AdventureBook)\n            }, {\n                5845, typeof(IA.OrchestrionRollUnlock)\n            }, {\n                8667, typeof(IA.SustainPotion)\n            }, {\n                9343, typeof(IA.FateContentAction)\n            }, { \n                9994, typeof(IA.TeleportationTicket)\n            }, { \n                18083, typeof(IA.AchievementScroll)\n            }, {\n                19743, typeof(IA.FieldNoteUnlock)\n            }, {\n                20086, typeof(IA.OrnamentUnlock)\n            }, {\n                25174, typeof(IA.AdventureBook)\n            }, {\n                25183, typeof(IA.OrchestrionRollUnlock)\n            }\n        };\n\n        #endregion\n\n        #region Constructors\n\n        public ItemActionSheet(XivCollection collection, IRelationalSheet source) : base(collection, source) { }\n\n        #endregion\n\n        #region Factory\n\n        protected override ItemAction CreateRow(IRelationalRow sourceRow) {\n            var typeKey = Convert.ToInt32(sourceRow[\"Type\"]);\n\n            if (!ItemActionTypes.TryGetValue(typeKey, out var type)) {\n                type = DefaultItemActionType;\n                Trace.WriteLine(string.Format(\"Unhandled item action type '{0}'.\", typeKey));\n            }\n            var args = new object[] {\n                this, sourceRow\n            };\n            const BindingFlags ActivatorBindFlags =\n                BindingFlags.Instance | BindingFlags.CreateInstance | BindingFlags.Public\n                | BindingFlags.NonPublic;\n            return (ItemAction)Activator.CreateInstance(type, ActivatorBindFlags, null, args, null);\n        }\n\n        #endregion\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Xiv/ShopListingItem.cs",
    "content": "﻿using System.Text;\n\nnamespace SaintCoinach.Xiv {\n    /// <summary>\n    ///     General-purpose class for items to use in <see cref=\"IShopListing\" />.\n    /// </summary>\n    public class ShopListingItem : IShopListingItem {\n        #region Constructors\n\n        /// <summary>\n        ///     Initializes a new instance of the <see cref=\"ShopListingItem\" /> class.\n        /// </summary>\n        /// <param name=\"shopItem\">The <see cref=\"IShopListing\" /> the entry is for.</param>\n        /// <param name=\"item\">The item of the entry.</param>\n        /// <param name=\"count\">The count for the entry.</param>\n        /// <param name=\"isHq\">A value indicating whether the <c>item</c> is high-quality.</param>\n        /// <param name=\"collectabilityRating\">The collectability rating of the entry.</param>\n        public ShopListingItem(IShopListing shopItem, Item item, int count, bool isHq, int collectabilityRating) {\n            ShopItem = shopItem;\n            Item = item;\n            Count = count;\n            IsHq = isHq;\n            CollectabilityRating = collectabilityRating;\n        }\n\n        #endregion\n\n        /// <summary>\n        ///     Gets the <see cref=\"IShopListing\" /> the current entry is for.\n        /// </summary>\n        /// <value>The <see cref=\"IShopListing\" /> the current entry is for.</value>\n        public IShopListing ShopItem { get; private set; }\n\n        /// <summary>\n        ///     Gets the item of the current listing entry.\n        /// </summary>\n        /// <value>The item of the current listing entry.</value>\n        public Item Item { get; private set; }\n\n        /// <summary>\n        ///     Gets the count for the current listing entry.\n        /// </summary>\n        /// <value>The count for the current listing entry.</value>\n        public int Count { get; private set; }\n\n        /// <summary>\n        ///     Gets a value indicating whether the item is high-quality.\n        /// </summary>\n        /// <value>A value indicating whether the item is high-quality.</value>\n        public bool IsHq { get; private set; }\n\n        /// <summary>\n        ///     Gets the collectability rating for the item.\n        /// </summary>\n        /// <value>The collectability rating of the item.</value>\n        public int CollectabilityRating { get; private set; }\n\n        /// <summary>\n        ///     Returns a string that represents the current <see cref=\"ShopListingItem\" />.\n        /// </summary>\n        /// <returns>A string that represents the current <see cref=\"ShopListingItem\" />.</returns>\n        public override string ToString() {\n            var sb = new StringBuilder();\n\n            if (Count > 1)\n                sb.AppendFormat(\"{0} \", Count);\n            sb.Append(Item);\n            if (IsHq)\n                sb.Append(\" (HQ)\");\n            return sb.ToString();\n        }\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Xiv/SpearfishingItem.cs",
    "content": "using SaintCoinach.Ex.Relational;\n\nnamespace SaintCoinach.Xiv {\n    public class SpearfishingItem : GatheringItemBase {\n        #region Properties\n\n        public int ItemLevel => AsInt32(\"ItemLevel\");\n        public bool IsVisible => AsBoolean(\"IsVisible\");\n\n        #endregion\n\n        #region Constructors\n\n        public SpearfishingItem(IXivSheet sheet, IRelationalRow sourceRow) : base(sheet, sourceRow) { }\n\n        #endregion\n\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Xiv/SpecialShop.cs",
    "content": "using System.Collections.Generic;\nusing System.Linq;\n\nusing SaintCoinach.Ex.Relational;\n\nnamespace SaintCoinach.Xiv {\n    /// <summary>\n    ///     Class representing a shop in which items are traded for other items.\n    /// </summary>\n    public class SpecialShop : XivRow, IShop, IItemSource {\n        #region Fields\n\n        /// <summary>\n        ///     The <see cref=\"ENpcs\" /> offering the current shop.\n        /// </summary>\n        private ENpc[] _ENpcs;\n\n        /// <summary>\n        ///     <see cref=\"SpecialShopListing\" />s of the current shop.\n        /// </summary>\n        private SpecialShopListing[] _ShopItems;\n\n        #endregion\n\n        #region Properties\n\n        /// <summary>\n        ///     Gets the <see cref=\"SpecialShopListing\" />s of the current shop.\n        /// </summary>\n        /// <value>The <see cref=\"SpecialShopListing\" />s of the current shop.</value>\n        public IEnumerable<SpecialShopListing> Items { get { return _ShopItems ?? (_ShopItems = BuildShopItems()); } }\n\n        #endregion\n\n        #region Constructors\n\n        /// <summary>\n        ///     Initializes a new instance of the <see cref=\"SpecialShop\" /> class.\n        /// </summary>\n        /// <param name=\"sheet\"><see cref=\"IXivSheet\" /> containing this object.</param>\n        /// <param name=\"sourceRow\"><see cref=\"IRelationalRow\" /> to read data from.</param>\n        public SpecialShop(IXivSheet sheet, IRelationalRow sourceRow) : base(sheet, sourceRow) { }\n\n        #endregion\n\n        /// <summary>\n        ///     Gets the name of the current shop.\n        /// </summary>\n        /// <value>The name of the current shop.</value>\n        public Text.XivString Name { get { return AsString(\"Name\"); } }\n\n        /// <summary>\n        ///     Gets the <see cref=\"ENpc\" />s offering the current shop.\n        /// </summary>\n        /// <value>The <see cref=\"ENpc\" />s offering the current shop.</value>\n        public IEnumerable<ENpc> ENpcs { get { return _ENpcs ?? (_ENpcs = BuildENpcs()); } }\n\n        #region IShop Members\n\n        /// <summary>\n        ///     Gets the listings of the current shop.\n        /// </summary>\n        /// <value>The listings of the current shop.</value>\n        IEnumerable<IShopListing> IShop.ShopListings { get { return Items; } }\n\n        #endregion\n\n        /// <summary>\n        ///     Returns a string representation of the current shop.\n        /// </summary>\n        /// <returns>The value of <see cref=\"Name\" />.</returns>\n        public override string ToString() {\n            return Name;\n        }\n\n        #region Build\n\n        /// <summary>\n        ///     Build an array of the <see cref=\"ENpc\" />s offering the current shop.\n        /// </summary>\n        /// <returns>An array of the <see cref=\"ENpc\" />s offering the current shop.</returns>\n        private ENpc[] BuildENpcs() {\n            return Sheet.Collection.ENpcs.FindWithData(Key).ToArray();\n        }\n\n        /// <summary>\n        ///     Build an array of the <see cref=\"SpecialShopListing\" />s of the current shop.\n        /// </summary>\n        /// <returns>An array of the <see cref=\"SpecialShopListing\" />s of the current shop.</returns>\n        private SpecialShopListing[] BuildShopItems() {\n            const int Count = 60;\n\n            var items = new List<SpecialShopListing>();\n            for (var i = 0; i < Count; ++i) {\n                var item = new SpecialShopListing(this, i);\n                if (item.Rewards.Any())\n                    items.Add(item);\n            }\n\n            return items.ToArray();\n        }\n\n        #endregion\n\n\n        #region IItemSource Members\n\n        private Item[] _ItemSourceItems;\n\n        /// <summary>\n        /// Gets the <see cref=\"Item\"/>s that can be obtained from the current object.\n        /// </summary>\n        /// <value>The <see cref=\"Item\"/>s that can be obtained from the current object.</value>\n        IEnumerable<Item> IItemSource.Items {\n            get { return _ItemSourceItems ?? (_ItemSourceItems = Items.SelectMany(i => i.Rewards.Select(r => r.Item).OfType<Item>()).ToArray()); }\n        }\n\n        #endregion\n\n        #region IShop Members\n\n        string IShop.Name {\n            get { return Name; }\n        }\n\n        #endregion\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Xiv/SpecialShopListing.cs",
    "content": "﻿using System.Collections.Generic;\nusing System.Linq;\n\nnamespace SaintCoinach.Xiv {\n    /// <summary>\n    ///     Class representing a listing in a <see cref=\"SpecialShop\" />.\n    /// </summary>\n    public class SpecialShopListing : IShopListing {\n        #region Currency\n\n        private static Dictionary<int, int> _Currencies = new Dictionary<int, int>() {\n            { 1, 28 },\n            { 2, 25199 },\n            { 4, 25200 },\n            { 6, 33913 },\n            { 7, 33914 }\n        };\n\n        private static Dictionary<int, int> _Tomestones;\n\n        private void BuildTomestones() {\n            // Tomestone currencies rotate across patches.\n            // These keys correspond to currencies A, B, and C.\n            var sTomestonesItems = SpecialShop.Sheet.Collection.GetSheet<TomestonesItem>()\n                .Where(t => t.Tomestone.Key > 0)\n                .OrderBy(t => t.Tomestone.Key)\n                .ToArray();\n\n            _Tomestones = new Dictionary<int, int>();\n\n            for (int i = 0; i < sTomestonesItems.Length; i++) {\n                _Tomestones[i + 1] = sTomestonesItems[i].Item.Key;\n            }\n        }\n\n        #endregion\n\n        #region Fields\n\n        /// <summary>\n        ///     Costs of the current listing.\n        /// </summary>\n        private readonly ShopListingItem[] _Costs;\n\n        /// <summary>\n        ///     Rewards of the current listing.\n        /// </summary>\n        private readonly ShopListingItem[] _Rewards;\n\n        #endregion\n\n        #region Properties\n\n        /// <summary>\n        ///     Gets the <see cref=\"SpecialShop\" /> the current listing is from.\n        /// </summary>\n        /// <value>The <see cref=\"SpecialShop\" /> the current listing is from.</value>\n        public SpecialShop SpecialShop { get; private set; }\n\n        /// <summary>\n        ///     Gets the <see cref=\"Quest\" /> required for the current listing.\n        /// </summary>\n        /// <value>The <see cref=\"Quest\" /> required for the current listing.</value>\n        public Quest Quest { get; private set; }\n\n        #endregion\n\n        #region Constructors\n\n        /// <summary>\n        ///     Initializes a new instance of the <see cref=\"SpecialShopListing\" /> class.\n        /// </summary>\n        /// <param name=\"shop\"><see cref=\"SpecialShop\" /> for which the listing is.</param>\n        /// <param name=\"index\">Position of the listing in the <c>shop</c>'s data.</param>\n        public SpecialShopListing(SpecialShop shop, int index) {\n            SpecialShop = shop;\n\n            const int RewardCount = 2;\n            var rewards = new List<ShopListingItem>();\n            for (var i = 0; i < RewardCount; ++i) {\n                var item = shop.As<Item>(\"Item{Receive}\", index, i);\n                if (item.Key == 0)\n                    continue;\n\n                var count = shop.AsInt32(\"Count{Receive}\", index, i);\n                if (count == 0)\n                    continue;\n\n                var hq = shop.AsBoolean(\"HQ{Receive}\", index, i);\n\n                rewards.Add(new ShopListingItem(this, item, count, hq, 0));\n            }\n            _Rewards = rewards.ToArray();\n            Quest = shop.As<Quest>(\"Quest{Item}\", index);\n\n            int UseCurrencyType = shop.As<byte>(\"UseCurrencyType\");\n\n            const int CostCount = 3;\n            var costs = new List<ShopListingItem>();\n            for (var i = 0; i < CostCount; ++i) {\n                var item = shop.As<Item>(\"Item{Cost}\", index, i);\n\n                if (item.Key == 0)\n                    continue;\n\n                var count = shop.AsInt32(\"Count{Cost}\", index, i);\n                if (count == 0)\n                    continue;\n\n                var hq = shop.AsBoolean(\"HQ{Cost}\", index, i);\n\n                if (item.Key < 8) {\n                    switch (UseCurrencyType) {\n                        case 16:\n                            item = shop.Sheet.Collection.GetSheet<Item>()\n                                [_Currencies[item.Key]];\n                            break;\n                        case 8:\n                            item = shop.Sheet.Collection.GetSheet<Item>()\n                                [1];\n                            break;\n                        case 4:\n                            if (_Tomestones == null) {\n                                BuildTomestones();\n                            }\n                            item = shop.Sheet.Collection.GetSheet<Item>()\n                                [_Tomestones[item.Key]];\n                            break;\n                    }\n                    hq = false;\n                }\n                \n                var collectabilityRating = shop.AsInt16(\"CollectabilityRating{Cost}\", index, i);\n\n                costs.Add(new ShopListingItem(this, item, count, hq, collectabilityRating));\n            }\n            _Costs = costs.ToArray();\n        }\n\n        #endregion\n\n        /// <summary>\n        ///     Gets the rewards of the current listing.\n        /// </summary>\n        /// <value>The rewards of the current listing.</value>\n        public IEnumerable<IShopListingItem> Rewards { get { return _Rewards; } }\n\n        /// <summary>\n        ///     Gets the costs of the current listing.\n        /// </summary>\n        /// <value>The costs of the current listing.</value>\n        public IEnumerable<IShopListingItem> Costs { get { return _Costs; } }\n\n        #region IShopItem Members\n\n        /// <summary>\n        ///     Gets the shops offering the current listing.\n        /// </summary>\n        /// <value>The shops offering the current listing.</value>\n        IEnumerable<IShop> IShopListing.Shops { get { yield return SpecialShop; } }\n\n        #endregion\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Xiv/Stain.cs",
    "content": "using System.Drawing;\n\nusing SaintCoinach.Ex.Relational;\n\nnamespace SaintCoinach.Xiv {\n    /// <summary>\n    ///     Class representing a dye.\n    /// </summary>\n    public class Stain : XivRow {\n        #region Properties\n\n        /// <summary>\n        ///     Gets the name of the current stain.\n        /// </summary>\n        /// <value>The name of the current stain.</value>\n        public Text.XivString Name { get { return AsString(\"Name\"); } }\n\n        /// <summary>\n        ///     Gets the <see cref=\"Item\" /> used for the current stain.\n        /// </summary>\n        /// <value>The <see cref=\"Item\" /> used for the current stain.</value>\n        public Item Item { get { return As<Item>(\"Item\"); } }\n\n        /// <summary>\n        ///     Gets the key of the shade for the current stain.\n        /// </summary>\n        /// <value>The key of the shade for the current stain.</value>\n        public int Shade { get { return AsInt32(\"Shade\"); } }\n\n        /// <summary>\n        ///     Gets the colour of the current stain.\n        /// </summary>\n        /// <value>The colour of the current stain.</value>\n        public Color Color { get { return As<Color>(); } }\n\n        #endregion\n\n        #region Constructors\n\n        /// <summary>\n        ///     Initializes a new instance of the <see cref=\"Stain\" /> class.\n        /// </summary>\n        /// <param name=\"sheet\"><see cref=\"IXivSheet\" /> containing this object.</param>\n        /// <param name=\"sourceRow\"><see cref=\"IRelationalRow\" /> to read data from.</param>\n        public Stain(IXivSheet sheet, IRelationalRow sourceRow) : base(sheet, sourceRow) { }\n\n        #endregion\n\n        /// <summary>\n        ///     Returns a string representation of the current shop.\n        /// </summary>\n        /// <returns>The value of <see cref=\"Name\" />.</returns>\n        public override string ToString() {\n            return Name;\n        }\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Xiv/Status.cs",
    "content": "using SaintCoinach.Ex.Relational;\nusing SaintCoinach.Imaging;\n\nnamespace SaintCoinach.Xiv {\n    public class Status : XivRow {\n        #region Properties\n\n        public Text.XivString Name { get { return AsString(\"Name\"); } }\n        public Text.XivString Description { get { return AsString(\"Description\"); } }\n        public ImageFile Icon { get { return AsImage(\"Icon\"); } }\n        public bool CanDispel { get { return AsBoolean(\"CanDispel\"); } }\n        public byte Category { get { return As<byte>(\"StatusCategory\"); } }\n\n        #endregion\n\n        #region Constructors\n\n        #region Constructor\n\n        public Status(IXivSheet sheet, IRelationalRow sourceRow) : base(sheet, sourceRow) { }\n\n        #endregion\n\n        #endregion\n\n        public override string ToString() {\n            return Name;\n        }\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Xiv/TerritoryType.cs",
    "content": "﻿using System.Collections.Generic;\nusing System.Linq;\n\nusing SaintCoinach.Ex.Relational;\n\nnamespace SaintCoinach.Xiv {\n    /// <summary>\n    ///     Class representing a territory (zone).\n    /// </summary>\n    public class TerritoryType : XivRow {\n        #region Static\n\n        /// <summary>\n        ///     Mappings of special weather rates.\n        /// </summary>\n        private static Dictionary<int, WeatherRate> WeatherGroups;\n\n        #endregion\n\n        #region Fields\n\n        /// <summary>\n        ///     <see cref=\"WeatherRate\" /> of the current territory.\n        /// </summary>\n        private WeatherRate _WeatherRate;\n\n        private Dictionary<uint, Map> _MapsByIndex;\n        #endregion\n\n        #region Properties\n\n        /// <summary>\n        ///     Gets the name of the current territory.\n        /// </summary>\n        /// <value>The name of the current territory.</value>\n        public Text.XivString Name { get { return AsString(\"Name\"); } }\n\n        /// <summary>\n        ///     Gets the identifier used for the current territory.\n        /// </summary>\n        /// <value>The identifier used for the current territory.</value>\n        public Text.XivString Bg { get { return AsString(\"Bg\"); } }\n\n        /// <summary>\n        ///     Gets the <see cref=\"Map\" /> of the current territory.\n        /// </summary>\n        /// <value>The <see cref=\"Map\" /> of the current territory.</value>\n        public Map Map { get { return As<Map>(); } }\n\n        /// <summary>\n        ///     Gets the <see cref=\"PlaceName\" /> of the current territory.\n        /// </summary>\n        /// <value>The <see cref=\"PlaceName\" /> of the current territory.</value>\n        public PlaceName PlaceName { get { return As<PlaceName>(\"PlaceName\"); } }\n\n        /// <summary>\n        ///     Gets the <see cref=\"PlaceName\" /> of the region the current map is in.\n        /// </summary>\n        /// <value>The <see cref=\"PlaceName\" /> of the region the current map is in.</value>\n        public PlaceName RegionPlaceName { get { return As<PlaceName>(\"PlaceName{Region}\"); } }\n\n        /// <summary>\n        ///     Gets the <see cref=\"PlaceName\" /> of the zone the current territory is in.\n        /// </summary>\n        /// <value>The <see cref=\"PlaceName\" /> of the zone the current territory is in.</value>\n        public PlaceName ZonePlaceName { get { return As<PlaceName>(\"PlaceName{Zone}\"); } }\n\n        /// <summary>\n        ///     Gets the <see cref=\"WeatherRate\" /> of the current territory.\n        /// </summary>\n        /// <value>The <see cref=\"WeatherRate\" /> of the current territory.</value>\n        public WeatherRate WeatherRate {\n            get {\n                if (_WeatherRate != null) return _WeatherRate;\n\n                var rateKey = AsInt32(\"WeatherRate\");\n                try { \n                    return _WeatherRate = Sheet.Collection.GetSheet<WeatherRate>()[rateKey];\n                } catch (KeyNotFoundException notFound) {\n\n                    // Weather Groups seems already deprecated\n\n                    /*\n                    if (WeatherGroups == null)\n                        WeatherGroups = BuildWeatherGroups();\n\n                    if (!WeatherGroups.TryGetValue(rateKey, out _WeatherRate))\n                        _WeatherRate = Sheet.Collection.GetSheet<WeatherRate>()[rateKey];\n                    return _WeatherRate;\n                    */\n                    throw;\n                }\n            }\n        }\n\n        #endregion\n\n        #region Constructors\n\n        /// <summary>\n        ///     Initializes a new instance of the <see cref=\"TerritoryType\" /> class.\n        /// </summary>\n        /// <param name=\"sheet\"><see cref=\"IXivSheet\" /> containing this object.</param>\n        /// <param name=\"sourceRow\"><see cref=\"IRelationalRow\" /> to read data from.</param>\n        public TerritoryType(IXivSheet sheet, IRelationalRow sourceRow) : base(sheet, sourceRow) { }\n\n        #endregion\n\n        public Map GetRelatedMap(uint index) {\n            if (_MapsByIndex == null)\n                _MapsByIndex = BuildMapIndex();\n\n            if (_MapsByIndex.TryGetValue(index, out var map))\n                return map;\n\n            // Fallback to the default map.  This may not be accurate.\n            return Map;\n        }\n\n        private Dictionary<int, WeatherRate> BuildWeatherGroups() {\n            var map = new Dictionary<int, WeatherRate>();\n            foreach (var weatherGroup in Sheet.Collection.GetSheet2(\"WeatherGroup\")) {\n                // Not sure what the other rows are used for.\n                if (weatherGroup.Key != 0)\n                    continue;\n\n                map[weatherGroup.ParentRow.Key] = (WeatherRate)weatherGroup[\"WeatherRate\"];\n            }\n            return map;\n        }\n\n        private Dictionary<uint, Map> BuildMapIndex() {\n            var maps = Sheet.Collection.GetSheet<Map>()\n                .Where(m => m.TerritoryType != null && m.TerritoryType.Key == Key);\n\n            var index = new Dictionary<uint, Map>();\n\n            foreach (var map in maps) {\n                var mapId = map.Id.ToString();\n                if (string.IsNullOrEmpty(mapId))\n                    continue;\n\n                var mapIndex = mapId.Substring(mapId.IndexOf(\"/\") + 1);\n                var convertedIndex = uint.Parse(mapIndex);\n                if (index.ContainsKey(convertedIndex))\n                    continue; // Skip it for now.\n\n                index[convertedIndex] = map;\n            }\n\n            return index;\n        }\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Xiv/Title.cs",
    "content": "using SaintCoinach.Ex.Relational;\n\nnamespace SaintCoinach.Xiv {\n    public class Title : XivRow {\n        #region Properties\n\n        public Text.XivString Masculine { get { return AsString(\"Masculine\"); } }\n        public Text.XivString Feminine { get { return AsString(\"Feminine\"); } }\n        public bool IsPrefix { get { return AsBoolean(\"IsPrefix\"); } }\n\n        #endregion\n\n        #region Constructors\n\n        #region Constructor\n\n        public Title(IXivSheet sheet, IRelationalRow sourceRow) : base(sheet, sourceRow) { }\n\n        #endregion\n\n        #endregion\n\n        public override string ToString() {\n            return string.Format(\"{0} / {1}\", Feminine, Masculine);\n        }\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Xiv/Tomestone.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nusing SaintCoinach.Ex.Relational;\n\nnamespace SaintCoinach.Xiv {\n    [XivSheet(\"Tomestones\")]\n    public class Tomestone : XivRow {\n        #region Properties\n\n        public int WeeklyLimit { get { return AsInt32(\"WeeklyLimit\"); } }\n\n        #endregion\n\n        #region Constructors\n\n        public Tomestone(IXivSheet sheet, IRelationalRow sourceRow) : base(sheet, sourceRow) { }\n\n        #endregion\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Xiv/TomestonesItem.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nusing SaintCoinach.Ex.Relational;\n\nnamespace SaintCoinach.Xiv {\n    public class TomestonesItem : XivRow {\n        #region Properties\n\n        public Item Item => As<Item>();\n        public Tomestone Tomestone => As<Tomestone>(\"Tomestones\");\n\n        #endregion\n\n        #region Constructors\n\n        public TomestonesItem(IXivSheet sheet, IRelationalRow sourceRow) : base(sheet, sourceRow) { }\n\n        #endregion\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Xiv/Trait.cs",
    "content": "using SaintCoinach.Ex.Relational;\nusing SaintCoinach.Imaging;\n\nnamespace SaintCoinach.Xiv {\n    public class Trait : XivRow {\n        #region Properties\n\n        public Text.XivString Name { get { return AsString(\"Name\"); } }\n        public Text.XivString Description { get { return AsString(\"Description\"); } }\n        public ImageFile Icon { get { return AsImage(\"Icon\"); } }\n        public ClassJob ClassJob { get { return As<ClassJob>(); } }\n        public int Level { get { return AsInt32(\"Level\"); } }\n        public ClassJobCategory ClassJobCategory { get { return As<ClassJobCategory>(); } }\n        public int Value { get { return AsInt32(\"Value\"); } }\n\n        #endregion\n\n        #region Constructors\n\n        #region Constructor\n\n        public Trait(IXivSheet sheet, IRelationalRow sourceRow) : base(sheet, sourceRow) { }\n\n        #endregion\n\n        #endregion\n\n        public override string ToString() {\n            return Name;\n        }\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Xiv/Tribe.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nusing SaintCoinach.Ex.Relational;\n\nnamespace SaintCoinach.Xiv {\n    public class Tribe : XivRow {\n        #region Static\n        // XXX: Magic happening here\n        static readonly Dictionary<int, Tuple<short, short>> ModelKeys = new Dictionary<int, Tuple<short, short>> {\n            {  1, Tuple.Create<short, short>(0101, 0201) },      // Hyur Midlander\n            {  2, Tuple.Create<short, short>(0301, 0401) },      // Hyur Highlander\n            {  3, Tuple.Create<short, short>(0501, 0601) },      // Elezen Wildwood\n            {  4, Tuple.Create<short, short>(0501, 0601) },      // Elezen Duskwight\n            {  5, Tuple.Create<short, short>(1101, 1201) },      // Lalafell Plainsfolk\n            {  6, Tuple.Create<short, short>(1101, 1201) },      // Lalafell Dunesfolk\n            {  7, Tuple.Create<short, short>(0701, 0801) },      // Miqo'te Seeker of the Sun\n            {  8, Tuple.Create<short, short>(0701, 0801) },      // Miqo'te Keeper of the Moon\n            {  9, Tuple.Create<short, short>(0901, 1001) },      // Roegadyn Sea Wolf\n            { 10, Tuple.Create<short, short>(0901, 1001) },      // Roegadyn Hellsguard\n            { 11, Tuple.Create<short, short>(1301, 1401) },      // Au Ra Raen\n            { 12, Tuple.Create<short, short>(1301, 1401) },      // Au Ra Xaela\n        };\n        #endregion\n\n        #region Properties\n\n        public short MaleModelTypeKey { get { return ModelKeys[this.Key].Item1; } }\n        public short FemaleModelTypeKey { get { return ModelKeys[this.Key].Item2; } }\n\n        public Text.XivString Masculine { get { return AsString(\"Masculine\"); } }\n        public Text.XivString Feminine { get { return AsString(\"Feminine\"); } }\n\n        public int StrengthBonus { get { return AsInt32(\"STR\"); } }\n        public int VitalityBonus { get { return AsInt32(\"VIT\"); } }\n        public int DexterityBonus { get { return AsInt32(\"DEX\"); } }\n        public int IntelligenceBonus { get { return AsInt32(\"INT\"); } }\n        public int MindBonus { get { return AsInt32(\"MND\"); } }\n        public int PietyBonus { get { return AsInt32(\"PIE\"); } }\n\n        #endregion\n\n        #region Constructor\n\n        public Tribe(IXivSheet sheet, IRelationalRow sourceRow) : base(sheet, sourceRow) { }\n\n        #endregion\n\n        public override string ToString() {\n            return Feminine;\n        }\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Xiv/TripleTriad.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nusing SaintCoinach.Ex.Relational;\n\nnamespace SaintCoinach.Xiv {\n    public class TripleTriad : XivRow, ILocatable {\n        #region Fields\n        private ENpc[] _ENpcs;\n        private TripleTriadCard[] _FixedCards;\n        private TripleTriadCard[] _VariableCards;\n        private TripleTriadRule[] _FixedRules;\n        private Item[] _RewardItems;\n        private PrerequisiteQuestsRequirement _QuestRequirement;\n        #endregion\n\n        #region Properties\n\n        public IEnumerable<ENpc> ENpcs { get { return _ENpcs ?? (_ENpcs = BuildENpcs()); } }\n        public IEnumerable<TripleTriadCard> AllCards { get { return FixedCards.Concat(VariableCards); } }\n        public IEnumerable<TripleTriadCard> FixedCards { get { return _FixedCards ?? (_FixedCards = BuildFixedCards()); } }\n        public IEnumerable<TripleTriadCard> VariableCards { get { return _VariableCards ?? (_VariableCards = BuildVariableCards()); } }\n        public IEnumerable<TripleTriadRule> FixedRules { get { return _FixedRules ?? (_FixedRules = BuildRules()); } }\n        public IEnumerable<Item> RewardItems => _RewardItems ?? (_RewardItems = BuildRewardItems());\n        public bool UsesRegionalRules { get { return AsBoolean(\"UsesRegionalRules\"); } }\n        public int Fee { get { return AsInt32(\"Fee\"); } }\n        public PrerequisiteQuestsRequirement QuestRequirement { get { return _QuestRequirement ?? (_QuestRequirement = BuildQuestRequirement()); } }\n\n        public int StartTime { get { return AsInt32(\"StartTime\"); } }\n        public int EndTime { get { return AsInt32(\"EndTime\"); } }\n        \n        // TODO: DefaultTalk\n\n        #endregion\n\n        #region Constructors\n\n        public TripleTriad(IXivSheet sheet, IRelationalRow sourceRow) : base(sheet, sourceRow) { }\n\n        #endregion\n\n        #region Build\n        private ENpc[] BuildENpcs() {\n            return Sheet.Collection.ENpcs.FindWithData(this.Key).ToArray();\n        }\n        private TripleTriadCard[] BuildFixedCards() {\n            const int Count = 5;\n\n            var cards = new List<TripleTriadCard>();\n            for (var i = 0; i < Count; ++i) {\n                var card = As<TripleTriadCard>(\"TripleTriadCard{Fixed}\", i);\n                if (card.Key != 0)\n                    cards.Add(card);\n            }\n\n            return cards.ToArray();\n        }\n        private TripleTriadCard[] BuildVariableCards() {\n            const int Count = 5;\n\n            var cards = new List<TripleTriadCard>();\n            for (var i = 0; i < Count; ++i) {\n                var card = As<TripleTriadCard>(\"TripleTriadCard{Variable}\", i);\n                if (card.Key != 0)\n                    cards.Add(card);\n            }\n\n            return cards.ToArray();\n        }\n        private Item[] BuildRewardItems() {\n            const int Count = 4;\n\n            var cards = new List<Item>();\n            for (var i = 0; i < Count; ++i) {\n                var card = As<Item>(\"Item{PossibleReward}\", i);\n                if (card.Key != 0)\n                    cards.Add(card);\n            }\n\n            return cards.ToArray();\n\n        }\n        private TripleTriadRule[] BuildRules() {\n            const int Count = 2;\n\n            var rules = new List<TripleTriadRule>();\n            for (var i = 0; i < Count; ++i) {\n                var rule = As<TripleTriadRule>(\"TripleTriadRule\", i);\n                if (rule.Key != 0)\n                    rules.Add(rule);\n            }\n            return rules.ToArray();\n        }\n        private PrerequisiteQuestsRequirement BuildQuestRequirement() {\n            const int Count = 3;\n\n            var type = (PrerequisiteQuestsRequirementType)AsInt32(\"PreviousQuestJoin\");\n            var quests = new List<Quest>();\n            for (var i = 0; i < Count; ++i) {\n                var q = As<Quest>(\"PreviousQuest\", i);\n                if (q != null && q.Key != 0)\n                    quests.Add(q);\n            }\n\n            return new PrerequisiteQuestsRequirement(type, quests);\n        }\n        #endregion\n\n        #region ILocatable Members\n\n        IEnumerable<ILocation> ILocatable.Locations {\n            get { return ENpcs.SelectMany(i => i.Locations); }\n        }\n\n        #endregion\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Xiv/TripleTriadCard.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nusing SaintCoinach.Ex.Relational;\n\nnamespace SaintCoinach.Xiv {\n    public class TripleTriadCard : XivRow {\n        public const int PlateIconOffset = 87000;\n        public const int IconOffset = 88000;\n\n        #region Fields\n        private TripleTriadCardResident _Resident;\n        #endregion\n\n        #region Properties\n\n        public Text.XivString Name { get { return AsString(\"Name\"); } }\n        public Text.XivString Description { get { return AsString(\"Description\"); } }\n\n        public Imaging.ImageFile Icon { get { return Imaging.IconHelper.GetIcon(Sheet.Collection.PackCollection, Sheet.Collection.ActiveLanguage, IconOffset + Key); } }\n        public Imaging.ImageFile PlateIcon { get { return Imaging.IconHelper.GetIcon(Sheet.Collection.PackCollection, Sheet.Collection.ActiveLanguage, PlateIconOffset + Key); } }\n\n        public TripleTriadCardResident TripleTriadCardResident {\n            get { return _Resident ?? (_Resident = Sheet.Collection.GetSheet<TripleTriadCardResident>()[this.Key]); }\n        }\n\n        public int Top { get { return TripleTriadCardResident.Top; } }\n        public int Bottom { get { return TripleTriadCardResident.Bottom; ; } }\n        public int Left { get { return TripleTriadCardResident.Left; ; } }\n        public int Right { get { return TripleTriadCardResident.Right; ; } }\n\n        public TripleTriadCardRarity TripleTriadCardRarity {\n            get { return TripleTriadCardResident.TripleTriadCardRarity; ; }\n        }\n        public TripleTriadCardType TripleTriadCardType {\n            get { return TripleTriadCardResident.TripleTriadCardType; ; }\n        }\n\n        public int SaleValue { get { return TripleTriadCardResident.SaleValue; ; } }\n        public int SortKey { get { return TripleTriadCardResident.SortKey; ; } }\n\n        #endregion\n\n        #region Constructors\n\n        public TripleTriadCard(IXivSheet sheet, IRelationalRow sourceRow) : base(sheet, sourceRow) { }\n\n        #endregion\n\n        public override string ToString() {\n            return Name;\n        }\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Xiv/TripleTriadCardRarity.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nusing SaintCoinach.Ex.Relational;\n\nnamespace SaintCoinach.Xiv {\n    public class TripleTriadCardRarity : XivRow {\n        #region Constructors\n\n        public TripleTriadCardRarity(IXivSheet sheet, IRelationalRow sourceRow) : base(sheet, sourceRow) { }\n\n        #endregion\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Xiv/TripleTriadCardResident.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nusing SaintCoinach.Ex.Relational;\n\nnamespace SaintCoinach.Xiv {\n    public class TripleTriadCardResident : XivRow {\n        #region Properties\n\n        public int Top { get { return AsInt32(\"Top\"); } }\n        public int Bottom { get { return AsInt32(\"Bottom\"); } }\n        public int Left { get { return AsInt32(\"Left\"); } }\n        public int Right { get { return AsInt32(\"Right\"); } }\n\n        public TripleTriadCardRarity TripleTriadCardRarity { get { return As<TripleTriadCardRarity>(); } }\n        public TripleTriadCardType TripleTriadCardType { get { return As<TripleTriadCardType>(); } }\n\n        public int SaleValue { get { return AsInt32(\"SaleValue\"); } }\n        public int SortKey { get { return AsInt32(\"SortKey\"); } }\n\n        #endregion\n\n        #region Constructors\n\n        public TripleTriadCardResident(IXivSheet sheet, IRelationalRow sourceRow) : base(sheet, sourceRow) { }\n\n        #endregion\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Xiv/TripleTriadCardType.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nusing SaintCoinach.Ex.Relational;\n\nnamespace SaintCoinach.Xiv {\n    public class TripleTriadCardType : XivRow {\n        #region Properties\n\n        public Text.XivString Name { get { return AsString(\"Name\"); } }\n\n        #endregion\n\n        #region Constructors\n\n        public TripleTriadCardType(IXivSheet sheet, IRelationalRow sourceRow) : base(sheet, sourceRow) { }\n\n        #endregion\n\n        public override string ToString() {\n            return Name;\n        }\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Xiv/TripleTriadCompetition.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nusing SaintCoinach.Ex.Relational;\n\nnamespace SaintCoinach.Xiv {\n    public class TripleTriadCompetition : XivRow {\n        #region Properties\n\n        public Text.XivString Name { get { return AsString(\"Name\"); } }\n\n        #endregion\n\n        #region Constructors\n\n        public TripleTriadCompetition(IXivSheet sheet, IRelationalRow sourceRow) : base(sheet, sourceRow) { }\n\n        #endregion\n\n        public override string ToString() {\n            return Name;\n        }\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Xiv/TripleTriadRule.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nusing SaintCoinach.Ex.Relational;\n\nnamespace SaintCoinach.Xiv {\n    public class TripleTriadRule : XivRow {\n        #region Properties\n\n        public Text.XivString Name { get { return AsString(\"Name\"); } }\n\n        #endregion\n\n        #region Constructors\n\n        public TripleTriadRule(IXivSheet sheet, IRelationalRow sourceRow) : base(sheet, sourceRow) { }\n\n        #endregion\n\n        public override string ToString() {\n            return Name;\n        }\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Xiv/Weather.cs",
    "content": "﻿using SaintCoinach.Ex.Relational;\nusing SaintCoinach.Imaging;\n\nnamespace SaintCoinach.Xiv {\n    public class Weather : XivRow {\n        #region Properties\n\n        public Text.XivString Name { get { return AsString(\"Name\"); } }\n        public Text.XivString Description { get { return AsString(\"Description\"); } }\n        public ImageFile Icon { get { return AsImage(\"Icon\"); } }\n\n        #endregion\n\n        #region Constructors\n\n        #region Constructor\n\n        public Weather(IXivSheet sheet, IRelationalRow sourceRow) : base(sheet, sourceRow) { }\n\n        #endregion\n\n        #endregion\n\n        #region ToString\n\n        public override string ToString() {\n            return Name;\n        }\n\n        #endregion\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Xiv/WeatherRate.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\n\nusing SaintCoinach.Ex.Relational;\n\nnamespace SaintCoinach.Xiv {\n    /// <summary>\n    ///     Class enabling the prediction of weather in Eorzea.\n    /// </summary>\n    public class WeatherRate : XivRow {\n        #region Static\n\n        /// <summary>\n        ///     Interval in which the <see cref=\"Weather\" /> can change.\n        /// </summary>\n        public static readonly TimeSpan WeatherChangeInterval = TimeSpan.FromHours(8);\n\n        #endregion\n\n        #region Fields\n\n        /// <summary>\n        ///     <see cref=\"Weather\" />s possible for the current group.\n        /// </summary>\n        private readonly Weather[] _PossibleWeathers;\n\n        /// <summary>\n        ///     Helper field for <see cref=\"Forecast\" />. <c>Item1</c> is the maximum predication value for <c>Item2</c> to happen.\n        /// </summary>\n        private readonly Tuple<int, Weather>[] _WeatherRates;\n\n        #endregion\n\n        #region Properties\n\n        /// <summary>\n        ///     Gets the <see cref=\"Weather\" />s possible for the current group.\n        /// </summary>\n        /// <value>The <see cref=\"Weather\" />s possible for the current group.</value>\n        public IEnumerable<Weather> PossibleWeathers { get { return _PossibleWeathers; } }\n\n        #endregion\n\n        #region Constructors\n\n        /// <summary>\n        ///     Initializes a new instance of the <see cref=\"WeatherRate\" /> class.\n        /// </summary>\n        /// <param name=\"sheet\"><see cref=\"IXivSheet\" /> containing this object.</param>\n        /// <param name=\"sourceRow\"><see cref=\"IRelationalRow\" /> to read data from.</param>\n        public WeatherRate(IXivSheet sheet, IRelationalRow sourceRow) : base(sheet, sourceRow) {\n            const int Count = 8;\n\n            var w = new List<Weather>();\n            var wr = new List<Tuple<int, Weather>>();\n            var min = 0;\n            for (var i = 0; i < Count; ++i) {\n                var suffix = string.Format(\"[{0}]\", i);\n\n                var weather = (Weather)this[\"Weather\" + suffix];\n                var rate = Convert.ToInt32(this[\"Rate\" + suffix]);\n\n                w.Add(weather);\n                wr.Add(Tuple.Create(min + rate, weather));\n\n                min += rate;\n            }\n            _PossibleWeathers = w.Distinct().ToArray();\n            _WeatherRates = wr.ToArray();\n        }\n\n        #endregion\n\n        #region Forecast\n\n        /// <summary>\n        ///     Forecast the <see cref=\"Weather\" /> for the current location at a specific <see cref=\"EorzeaDateTime\" />.\n        /// </summary>\n        /// <param name=\"time\"><see cref=\"EorzeaDateTime\" /> for which to forecast the weather.</param>\n        /// <returns>The <see cref=\"Weather\" /> at the current location at <c>time</c>.</returns>\n        public Weather Forecast(EorzeaDateTime time) {\n            var target = CalculateTarget(time);\n\n            return _WeatherRates.Where(_ => target < _.Item1).Select(_ => _.Item2).FirstOrDefault();\n        }\n\n        /// <summary>\n        ///     Calculate the value used for the <see cref=\"Forecast\" /> at a specific <see cref=\"EorzeaDateTime\" />.\n        /// </summary>\n        /// <param name=\"time\"><see cref=\"EorzeaDateTime\" /> for which to calculate the value.</param>\n        /// <returns>The value from 0..99 (inclusive) calculated based on <c>time</c>.</returns>\n        private static int CalculateTarget(EorzeaDateTime time) {\n            var unix = time.GetUnixTime();\n            // Get Eorzea hour for weather start\n            var bell = unix / 175;\n            // Do the magic 'cause for calculations 16:00 is 0, 00:00 is 8 and 08:00 is 16\n            var increment = ((uint)(bell + 8 - (bell % 8))) % 24;\n\n            // Take Eorzea days since unix epoch\n            var totalDays = (uint)(unix / 4200);\n\n            var calcBase = (totalDays * 0x64) + increment;\n\n            var step1 = (calcBase << 0xB) ^ calcBase;\n            var step2 = (step1 >> 8) ^ step1;\n\n            return (int)(step2 % 0x64);\n        }\n\n        #endregion\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Xiv/XivCollection.cs",
    "content": "﻿using System;\nusing System.Collections.Concurrent;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Reflection;\n\nusing SaintCoinach.Ex;\nusing SaintCoinach.Ex.Relational;\nusing SaintCoinach.IO;\nusing SaintCoinach.Xiv.Collections;\nusing SaintCoinach.Xiv.Sheets;\n\nnamespace SaintCoinach.Xiv {\n    /// <summary>\n    ///     <see cref=\"RelationalExCollection\" /> for OO representations of FFXIV game data.\n    /// </summary>\n    /// <remarks>\n    /// To access data either use <see cref=\"GetSheet{T}()\"/> with the request data type as <c>T</c> or get a collection using one of the exposed properties.\n    /// </remarks>\n    public class XivCollection : RelationalExCollection {\n        #region Fields\n\n        /// <summary>\n        ///     Mapping of sheet names to the object types to use for them.\n        /// </summary>\n        private ConcurrentDictionary<string, Type> _SheetNameToTypeMap;\n\n        /// <summary>\n        ///     Collection of <see cref=\"BNpc\"/> objects.\n        /// </summary>\n        private BNpcCollection _BNpcs;\n\n        /// <summary>\n        ///     Collection of <see cref=\"ClassJobActionBase\"/> (containing both <see cref=\"Action\" /> and <see cref=\"CraftAction\" />).\n        /// </summary>\n        private ClassJobActionCollection _ClassJobActions;\n\n        /// <summary>\n        ///     Collection of ENpc objects (containg data of both <see cref=\"ENpcBase\" /> and <see cref=\"ENpcResident\" />).\n        /// </summary>\n        private ENpcCollection _ENpcs;\n\n        /// <summary>\n        ///     Collection of equipment slots.\n        /// </summary>\n        private EquipSlotCollection _EquipSlots;\n\n        /// <summary>\n        ///     Collection of items (containing both <see cref=\"Item\" /> and <see cref=\"EventItem\" />).\n        /// </summary>\n        private ItemCollection _Items;\n\n        /// <summary>\n        ///     Collection of all shops.\n        /// </summary>\n        private ShopCollection _Shops;\n\n        /// <summary>\n        ///     Database connection to Libra Eorzea data.\n        /// </summary>\n        private Libra.Entities _Libra;\n\n        #endregion\n\n        #region Properties\n\n        /// <summary>\n        ///     Gets the collection of <see cref=\"BNpc\"/> objects.\n        /// </summary>\n        /// <value>The collection of <see cref=\"BNpc\"/> objects.</value>\n        /// <remarks>\n        /// This property is only supported when the Libra Eorzea database is present.\n        /// </remarks>\n        public BNpcCollection BNpcs {\n            get {\n                if (_BNpcs != null) return _BNpcs;\n                if (!IsLibraAvailable)\n                    throw new NotSupportedException(\"BNpcs are only available when Libra Eorzea database is present.\");\n\n                _BNpcs = new BNpcCollection(this);\n                return _BNpcs;\n            }\n        }\n\n        /// <summary>\n        ///     Gets the collection of <see cref=\"ClassJobActionBase\"/> (containing both <see cref=\"Action\" /> and <see cref=\"CraftAction\" />).\n        /// </summary>\n        /// <value>The collection of <see cref=\"ClassJobActionBase\"/> (containing both <see cref=\"Action\" /> and <see cref=\"CraftAction\" />).</value>\n        public ClassJobActionCollection ClassJobActions { get { return _ClassJobActions ?? (_ClassJobActions = new ClassJobActionCollection(this)); } }\n\n        /// <summary>\n        ///     Gets the collection of ENpc objects (containg data of both <see cref=\"ENpcBase\" /> and <see cref=\"ENpcResident\" />\n        ///     ).\n        /// </summary>\n        /// <value>The collection of ENpc objects (containg data of both <see cref=\"ENpcBase\" /> and <see cref=\"ENpcResident\" />).</value>\n        public ENpcCollection ENpcs { get { return _ENpcs ?? (_ENpcs = new ENpcCollection(this)); } }\n\n        /// <summary>\n        ///     Gets the collection of equipment slots.\n        /// </summary>\n        /// <value>The collection of equipment slots.</value>\n        public EquipSlotCollection EquipSlots {\n            get { return _EquipSlots ?? (_EquipSlots = new EquipSlotCollection(this)); }\n        }\n\n        /// <summary>\n        ///     Gets the collection of items (containing both <see cref=\"Item\" /> and <see cref=\"EventItem\" />).\n        /// </summary>\n        /// <value>The collection of items (containing both <see cref=\"Item\" /> and <see cref=\"EventItem\" />).</value>\n        public ItemCollection Items { get { return _Items ?? (_Items = new ItemCollection(this)); } }\n\n        /// <summary>\n        ///     Gets the collection of all shops.\n        /// </summary>\n        /// <value>The collection of all shops.</value>\n        public ShopCollection Shops { get { return _Shops ?? (_Shops = new ShopCollection(this)); } }\n\n        /// <summary>\n        /// Gets a value indicating whether the Libra Eorzea database is available.\n        /// </summary>\n        /// <value>A value indicating whether the Libra Eorzea database is available.</value>\n        public bool IsLibraAvailable { get { return _Libra != null; } }\n\n        /// <summary>\n        /// Gets the connection to the Libra Eorzea database.\n        /// </summary>\n        /// <value>The connection to the Libra Eorzea database.</value>\n        public Libra.Entities Libra { get { return _Libra; } }\n\n        #endregion\n\n        #region Constructors\n        \n        /// <summary>\n        ///     Initializes a new instance of the <see cref=\"XivCollection\" /> class.\n        /// </summary>\n        /// <param name=\"packCollection\">The <see cref=\"PackCollection\" /> to use to access game data.</param>\n        public XivCollection(PackCollection packCollection) : this(packCollection, null) { }\n\n        /// <summary>\n        ///     Initializes a new instance of the <see cref=\"XivCollection\" /> class.\n        /// </summary>\n        /// <param name=\"packCollection\">The <see cref=\"PackCollection\" /> to use to access game data.</param>\n        /// <param name=\"libraDatabase\"><see cref=\"FileInfo\"/> of the Libra Eorzea database file, or <c>null</c> if Libra data should be disabled.</param>\n        public XivCollection(PackCollection packCollection, System.IO.FileInfo libraDatabase)\n            : base(packCollection) {\n\n            if (libraDatabase != null && libraDatabase.Exists) {\n                const string LibraConnectionStringFormat = @\"metadata=res://*/Libra.LibraModel.csdl|res://*/Libra.LibraModel.ssdl|res://*/Libra.LibraModel.msl;provider=System.Data.SQLite.EF6;provider connection string='data source=\"\"{0}\"\"'\";\n                var connStr = string.Format(LibraConnectionStringFormat, libraDatabase.FullName);\n                _Libra = new Libra.Entities(connStr);\n            }\n        }\n\n        #endregion\n\n        #region Get\n\n        /// <summary>\n        ///     Get the <see cref=\"IXivSheet{T}\" /> for a specific type.\n        /// </summary>\n        /// <typeparam name=\"T\">Type of the rows to get the <see cref=\"IXivSheet{T}\" /> for.</typeparam>\n        /// <returns>Returns the <see cref=\"IXivSheet{T}\" /> for the specified <c>T</c>.</returns>\n        public IXivSheet<T> GetSheet<T>() where T : IXivRow {\n            var t = typeof(T);\n\n            var attr = t.GetCustomAttribute<XivSheetAttribute>();\n            if (attr != null)\n                return GetSheet<T>(attr.SheetName);\n\n            var name = t.FullName.Substring(t.FullName.IndexOf(\".Xiv.\", StringComparison.OrdinalIgnoreCase) + 5);\n            return GetSheet<T>(name);\n        }\n\n        /// <summary>\n        ///     Get the <see cref=\"XivSheet2{T}\" /> for a specific type.\n        /// </summary>\n        /// <typeparam name=\"T\">Type of the rows to get the <see cref=\"XivSheet2{T}\" /> for.</typeparam>\n        /// <returns>Returns the <see cref=\"XivSheet2{T}\" /> for the specified <c>T</c>.</returns>\n        public XivSheet2<T> GetSheet2<T>() where T : IXivSubRow\n        {\n            var t = typeof(T);\n\n            var attr = t.GetCustomAttribute<XivSheetAttribute>();\n            if (attr != null)\n                return GetSheet2<T>(attr.SheetName);\n\n            var name = t.FullName.Substring(t.FullName.IndexOf(\".Xiv.\", StringComparison.OrdinalIgnoreCase) + 5);\n            return GetSheet2<T>(name);\n        }\n\n        /// <summary>\n        ///     Get the <see cref=\"IXivSheet{T}\" /> for a specific type using a sheet identifier.\n        /// </summary>\n        /// <typeparam name=\"T\">Type of the rows to get the <see cref=\"IXivSheet{T}\" /> for.</typeparam>\n        /// <param name=\"id\">Sheet identifier.</param>\n        /// <returns>Returns the <see cref=\"IXivSheet{T}\" /> for a specific type using <c>id</c> as sheet identifier.</returns>\n        public new IXivSheet<T> GetSheet<T>(int id) where T : IXivRow {\n            return (IXivSheet<T>)GetSheet(id);\n        }\n\n        /// <summary>\n        ///     Get the <see cref=\"IXivSheet\" /> for a sheet identifier.\n        /// </summary>\n        /// <param name=\"id\">Sheet identifier.</param>\n        /// <returns>Returns the <see cref=\"IXivSheet\" /> with <c>id</c> as sheet identifier.</returns>\n        public new IXivSheet GetSheet(int id) {\n            return (IXivSheet)base.GetSheet(id);\n        }\n\n        /// <summary>\n        ///     Get the <see cref=\"IXivSheet{T}\" /> for a specific type using a sheet name.\n        /// </summary>\n        /// <typeparam name=\"T\">Type of the rows to get the <see cref=\"IXivSheet{T}\" /> for.</typeparam>\n        /// <param name=\"name\">Name of the sheet.</param>\n        /// <returns>Returns the <see cref=\"IXivSheet{T}\" /> for a specific type using <c>name</c> as sheet name.</returns>\n        public new IXivSheet<T> GetSheet<T>(string name) where T : IXivRow {\n            return (IXivSheet<T>)GetSheet(name);\n        }\n\n        /// <summary>\n        ///     Get the <see cref=\"XivSheet2{T}\" /> for a specific type using a sheet name.\n        /// </summary>\n        /// <typeparam name=\"T\">Type of the rows to get the <see cref=\"IXivSheet{T}\" /> for.</typeparam>\n        /// <param name=\"name\">Name of the sheet.</param>\n        /// <returns>Returns the <see cref=\"XivSheet2{T}\" /> for a specific type using <c>name</c> as sheet name.</returns>\n        public XivSheet2<T> GetSheet2<T>(string name) where T : IXivSubRow {\n            return (XivSheet2<T>)base.GetSheet(name);\n        }\n\n        /// <summary>\n        ///     Get the <see cref=\"IXivSheet\" /> for a sheet with a specific name.\n        /// </summary>\n        /// <param name=\"name\">Name of the sheet.</param>\n        /// <returns>Returns the <see cref=\"IXivSheet\" /> with <c>name</c> as sheet name.</returns>\n        public new IXivSheet<XivRow> GetSheet(string name) {\n            return (IXivSheet<XivRow>)base.GetSheet(name);\n        }\n\n        /// <summary>\n        ///     Get the <see cref=\"XivSheet2\" /> for a sheet with a specific name.\n        /// </summary>\n        /// <param name=\"name\">Name of the sheet.</param>\n        /// <returns>Returns the <see cref=\"XivSheet2\" /> with <c>name</c> as sheet name.</returns>\n        public XivSheet2<XivSubRow> GetSheet2(string name) {\n            return (XivSheet2<XivSubRow>)base.GetSheet(name);\n        }\n\n        #endregion\n\n        #region Factory\n\n        /// <summary>\n        ///     Delegate used to create non-generic <see cref=\"IXivSheet\" />s.\n        /// </summary>\n        /// <param name=\"collection\"><see cref=\"XivCollection\" /> for which to create the <see cref=\"IXivSheet\" />.</param>\n        /// <param name=\"sourceSheet\"><see cref=\"IRelationalSheet\" /> on top of which to create the <see cref=\"IXivSheet\" />.</param>\n        /// <returns>Returns the <see cref=\"IXivSheet\" /> created on top of <c>sourceSheet</c>.</returns>\n        protected delegate IXivSheet XivSheetCreator(XivCollection collection, IRelationalSheet sourceSheet);\n\n        /// <summary>\n        ///     Mappings of source sheets to special <see cref=\"XivSheetCreator\" />s.\n        /// </summary>\n        protected static readonly Dictionary<string, XivSheetCreator> SpecialSheetTypes =\n            new Dictionary<string, XivSheetCreator> {\n                {\n                    \"Item\", (c, s) => new InventoryItemSheet(c, s)\n                }, {\n                    \"ItemAction\", (c, s) => new ItemActionSheet(c, s)\n                }\n            };\n\n        /// <summary>\n        /// Create a <see cref=\"ISheet\"/> for a <see cref=\"Header\"/>.\n        /// </summary>\n        /// <param name=\"header\"><see cref=\"Header\"/> to create the sheet for.</param>\n        /// <returns>Returns the created <see cref=\"ISheet\"/>.</returns>\n        protected override ISheet CreateSheet(Header header) {\n            var baseSheet = (IRelationalSheet)base.CreateSheet(header);\n\n            var xivSheet = TryCreateXivSheet(baseSheet);\n            if (xivSheet != null)\n                return xivSheet;\n\n            if (header.Variant == 2)\n                return new XivSheet2<XivSubRow>(this, baseSheet);\n            return new XivSheet<XivRow>(this, baseSheet);\n        }\n\n        /// <summary>\n        /// Attempt to create a <see cref=\"IXivSheet{T}\"/> with a specific <c>T</c> based on the name of the sheet.\n        /// </summary>\n        /// <remarks>\n        /// Uses the mappings in <see cref=\"SpecialSheetTypes\"/> if present; otherwise looks for a type matching the sheet's name and creates a generic <see cref=\"XivSheet{T}\"/>; returns <c>null</c> if neither is found.\n        /// </remarks>\n        /// <param name=\"sourceSheet\"><see cref=\"IRelationalSheet\"/> to access the source data.</param>\n        /// <returns>Returns a <see cref=\"IXivSheet\"/> if a matching one could be created; <c>null</c> otherwise.</returns>\n        protected virtual IXivSheet TryCreateXivSheet(IRelationalSheet sourceSheet) {\n            if (SpecialSheetTypes.TryGetValue(sourceSheet.Name, out var specialCreator))\n                return specialCreator(this, sourceSheet);\n\n            var match = GetXivRowType(sourceSheet.Name);\n            if (match == null)\n                return null;\n\n            var genericType = sourceSheet.Header.Variant == 2 ? typeof(XivSheet2<>) : typeof(XivSheet<>);\n\n            var constructedType = genericType.MakeGenericType(match);\n            var constructor = constructedType.GetConstructor(\n                                                             BindingFlags.Instance | BindingFlags.NonPublic\n                                                             | BindingFlags.Public,\n                null,\n                new[] {\n                    typeof(XivCollection), typeof(IRelationalSheet)\n                },\n                null);\n            return (IXivSheet)constructor.Invoke(new object[] {\n                this, sourceSheet\n            });\n        }\n\n        private Type GetXivRowType(string sheetName) {\n            if (_SheetNameToTypeMap == null)\n                BuildSheetToTypeMap();\n\n            // Record failed matches too.\n            return _SheetNameToTypeMap.GetOrAdd(sheetName, s => {\n                var search = \"Xiv.\" + sheetName.Replace('/', '.');\n                return _IXivRowTypes.FirstOrDefault(_ => _.FullName.EndsWith(search));\n            });\n        }\n\n        private Type[] _IXivRowTypes;\n        private void BuildSheetToTypeMap() {\n            var allTypes = Assembly.GetExecutingAssembly().GetTypes();\n            var attrTypes = allTypes.Select(t => new { Type = t, Attr = t.GetCustomAttribute<XivSheetAttribute>() }).Where(t => t.Attr != null);\n            _IXivRowTypes = allTypes.Where(t => typeof(IXivRow).IsAssignableFrom(t)).ToArray();\n\n            _SheetNameToTypeMap = new ConcurrentDictionary<string, Type>(attrTypes.ToDictionary(i => i.Attr.SheetName, i => i.Type));\n        }\n\n        #endregion\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Xiv/XivRow.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\n\nusing SaintCoinach.Ex;\nusing SaintCoinach.Ex.Relational;\nusing SaintCoinach.Imaging;\n\nnamespace SaintCoinach.Xiv {\n    /// <summary>\n    ///     Base class for rows inside FFXIV's data sheets.\n    /// </summary>\n    public class XivRow : IXivRow {\n        #region Fields\n\n        /// <summary>\n        ///     <see cref=\"IRelationalRow\" /> the current row reads data from.\n        /// </summary>\n        private readonly IRelationalRow _SourceRow;\n\n        #endregion\n\n        #region Properties\n\n        public IRelationalRow SourceRow { get { return _SourceRow; } }\n\n        #endregion\n\n        #region Constructors\n\n        /// <summary>\n        ///     Initializes a new instance of the <see cref=\"XivRow\" /> class.\n        /// </summary>\n        /// <param name=\"sheet\"><see cref=\"IXivSheet\" /> containing this object.</param>\n        /// <param name=\"sourceRow\"><see cref=\"IRelationalRow\" /> to read data from.</param>\n        public XivRow(IXivSheet sheet, IRelationalRow sourceRow) {\n            Sheet = sheet;\n            _SourceRow = sourceRow;\n        }\n\n        #endregion\n\n        /// <summary>\n        ///     Gets the <see cref=\"IXivSheet\" /> the current row is in.\n        /// </summary>\n        /// <value>The <see cref=\"IXivSheet\" /> the current row is in.</value>\n        public IXivSheet Sheet { get; private set; }\n\n        /// <summary>\n        ///     Returns a string representation of the current row.\n        /// </summary>\n        /// <returns>A string representation of the current row.</returns>\n        public override string ToString() {\n            return _SourceRow.ToString();\n        }\n\n        #region IRelationalRow Members\n\n        IRelationalSheet IRelationalRow.Sheet { get { return Sheet; } }\n\n        public object DefaultValue { get { return _SourceRow.DefaultValue; } }\n\n        public object this[string columnName] { get { return _SourceRow[columnName]; } }\n\n        public object GetRaw(int columnIndex) { return _SourceRow.GetRaw(columnIndex); }\n        public object GetRaw(string columnName) { return _SourceRow.GetRaw(columnName); }\n\n        #endregion\n\n        #region IRow Members\n\n        ISheet IRow.Sheet { get { return Sheet; } }\n\n        public int Key { get { return _SourceRow.Key; } }\n\n        public object this[int columnIndex] { get { return _SourceRow[columnIndex]; } }\n\n        public IEnumerable<object> ColumnValues() => _SourceRow.ColumnValues();\n\n        #endregion\n\n        #region Type helpers\n\n        /// <summary>\n        ///     Build the full column name from a base and additional indices.\n        /// </summary>\n        /// <param name=\"column\">Base name of the column.</param>\n        /// <param name=\"indices\">Indices for the full name.</param>\n        /// <returns>The full column name built using <c>column</c> and <c>indices</c>.</returns>\n        public static string BuildColumnName(string column, params int[] indices) {\n            var sb = new StringBuilder();\n            sb.Append(column);\n            foreach (var i in indices) {\n                sb.Append('[');\n                sb.Append(i);\n                sb.Append(']');\n            }\n            return sb.ToString();\n        }\n\n        /// <summary>\n        ///     Get the value of the field in the column with the same name as a specific type.\n        /// </summary>\n        /// <typeparam name=\"T\">The type that should be returned and also the name of the column.</typeparam>\n        /// <returns>The value of the field in the column with the same name as the name of type <c>T</c>.</returns>\n        public T As<T>() {\n            return As<T>(typeof(T).Name);\n        }\n\n        /// <summary>\n        ///     Get the value of the field in the column with the same base name as a specific type and given indices.\n        /// </summary>\n        /// <typeparam name=\"T\">The type that should be returned and also the name of the column.</typeparam>\n        /// <param name=\"indices\">Indices for the full column.</param>\n        /// <returns>The value of the field in the column with the same base name as the name of type <c>T</c> and <c>indices</c>.</returns>\n        public T As<T>(params int[] indices) {\n            return As<T>(typeof(T).Name, indices);\n        }\n\n        /// <summary>\n        ///     Get the value of a field in a specific column.\n        /// </summary>\n        /// <typeparam name=\"T\">The type that should be returned.</typeparam>\n        /// <param name=\"column\">Name of the column from which to read.</param>\n        /// <returns>The value of the field in <c>column</c>.</returns>\n        public T As<T>(string column) {\n            return (T)this[column];\n        }\n\n        /// <summary>\n        ///     Get the value of a field in a specific column and indices.\n        /// </summary>\n        /// <typeparam name=\"T\">The type that should be returned.</typeparam>\n        /// <param name=\"column\">Name of the column from which to read.</param>\n        /// <param name=\"indices\">Indices for the full column.</param>\n        /// <returns>The value of the field in <c>column</c> at <c>indices</c>.</returns>\n        public T As<T>(string column, params int[] indices) {\n            return As<T>(BuildColumnName(column, indices));\n        }\n\n        /// <summary>\n        ///     Gets the value of a field from a specific column as an <see cref=\"ImageFile\" />.\n        /// </summary>\n        /// <param name=\"column\">Name of the column from which to read.</param>\n        /// <returns>The <see cref=\"ImageFile\" /> in the <c>column</c> of the current row.</returns>\n        public ImageFile AsImage(string column) {\n            return (ImageFile)this[column];\n        }\n\n        /// <summary>\n        ///     Gets the value of a field from a specific column and indices as an <see cref=\"ImageFile\" />.\n        /// </summary>\n        /// <param name=\"column\">Name of the column from which to read.</param>\n        /// <param name=\"indices\">Indices for the full column.</param>\n        /// <returns>The <see cref=\"ImageFile\" /> in the <c>column</c> at <c>indices</c> of the current row.</returns>\n        public ImageFile AsImage(string column, params int[] indices) {\n            return AsImage(BuildColumnName(column, indices));\n        }\n\n        /// <summary>\n        ///     Gets the value of a field from a specific column as a string.\n        /// </summary>\n        /// <param name=\"column\">Name of the column from which to read.</param>\n        /// <returns>The string value of the field in <c>column</c> of the current row.</returns>\n        public Text.XivString AsString(string column) {\n            return (Text.XivString)this[column];\n        }\n\n        /// <summary>\n        ///     Gets the value of a field from a specific column and indices as a string.\n        /// </summary>\n        /// <param name=\"column\">Name of the column from which to read.</param>\n        /// <param name=\"indices\">Indices for the full column.</param>\n        /// <returns>The string value of the field in <c>column</c> and <c>indices</c> of the current row.</returns>\n        public Text.XivString AsString(string column, params int[] indices) {\n            return AsString(BuildColumnName(column, indices));\n        }\n\n        /// <summary>\n        ///     Gets the value of a field from a specific column as a boolean.\n        /// </summary>\n        /// <param name=\"column\">Name of the column from which to read.</param>\n        /// <returns>The boolean value of the field in <c>column</c> of the current row.</returns>\n        public Boolean AsBoolean(string column) {\n            return Convert.ToBoolean(this[column]);\n        }\n\n        /// <summary>\n        ///     Gets the value of a field from a specific column and indices as a boolean.\n        /// </summary>\n        /// <param name=\"column\">Name of the column from which to read.</param>\n        /// <param name=\"indices\">Indices for the full column.</param>\n        /// <returns>The boolean value of the field in <c>column</c> and <c>indices</c> of the current row.</returns>\n        public Boolean AsBoolean(string column, params int[] indices) {\n            return AsBoolean(BuildColumnName(column, indices));\n        }\n\n        /// <summary>\n        ///     Gets the value of a field from a specific column as a 16-bit signed integer.\n        /// </summary>\n        /// <param name=\"column\">Name of the column from which to read.</param>\n        /// <returns>The 16-bit signed integer value of the field in <c>column</c> of the current row.</returns>\n        public Int16 AsInt16(string column) {\n            return Convert.ToInt16(this[column]);\n        }\n\n        /// <summary>\n        ///     Gets the value of a field from a specific column and indices as a 16-bit signed integer.\n        /// </summary>\n        /// <param name=\"column\">Name of the column from which to read.</param>\n        /// <param name=\"indices\">Indices for the full column.</param>\n        /// <returns>The 16-bit signed integer value of the field in <c>column</c> and <c>indices</c> of the current row.</returns>\n        public Int16 AsInt16(string column, params int[] indices) {\n            return AsInt16(BuildColumnName(column, indices));\n        }\n\n        /// <summary>\n        ///     Gets the value of a field from a specific column as a 32-bit signed integer.\n        /// </summary>\n        /// <param name=\"column\">Name of the column from which to read.</param>\n        /// <returns>The 32-bit signed integer value of the field in <c>column</c> of the current row.</returns>\n        public Int32 AsInt32(string column) {\n            return Convert.ToInt32(this[column]);\n        }\n\n        /// <summary>\n        ///     Gets the value of a field from a specific column and indices as a 32-bit signed integer.\n        /// </summary>\n        /// <param name=\"column\">Name of the column from which to read.</param>\n        /// <param name=\"indices\">Indices for the full column.</param>\n        /// <returns>The 32-bit signed integer value of the field in <c>column</c> and <c>indices</c> of the current row.</returns>\n        public Int32 AsInt32(string column, params int[] indices) {\n            return AsInt32(BuildColumnName(column, indices));\n        }\n\n        /// <summary>\n        ///     Gets the value of a field from a specific column as a 64-bit signed integer.\n        /// </summary>\n        /// <param name=\"column\">Name of the column from which to read.</param>\n        /// <returns>The 64-bit signed integer value of the field in <c>column</c> of the current row.</returns>\n        public Int64 AsInt64(string column) {\n            return Convert.ToInt64(this[column]);\n        }\n\n        /// <summary>\n        ///     Gets the value of a field from a specific column and indices as a 64-bit signed integer.\n        /// </summary>\n        /// <param name=\"column\">Name of the column from which to read.</param>\n        /// <param name=\"indices\">Indices for the full column.</param>\n        /// <returns>The 64-bit signed integer value of the field in <c>column</c> and <c>indices</c> of the current row.</returns>\n        public Int64 AsInt64(string column, params int[] indices) {\n            return AsInt64(BuildColumnName(column, indices));\n        }\n\n        /// <summary>\n        ///     Gets the value of a field from a specific column as a single-precision floating point value.\n        /// </summary>\n        /// <param name=\"column\">Name of the column from which to read.</param>\n        /// <returns>The single-precision floating point value of the field in <c>column</c> of the current row.</returns>\n        public Single AsSingle(string column) {\n            return Convert.ToSingle(this[column]);\n        }\n\n        /// <summary>\n        ///     Gets the value of a field from a specific column and indices as a single-precision floating point value.\n        /// </summary>\n        /// <param name=\"column\">Name of the column from which to read.</param>\n        /// <param name=\"indices\">Indices for the full column.</param>\n        /// <returns>The single-precision floating point value of the field in <c>column</c> and <c>indices</c> of the current row.</returns>\n        public Single AsSingle(string column, params int[] indices) {\n            return AsSingle(BuildColumnName(column, indices));\n        }\n\n        /// <summary>\n        ///     Gets the value of a field from a specific column as a double-precision floating point value.\n        /// </summary>\n        /// <param name=\"column\">Name of the column from which to read.</param>\n        /// <returns>The double-precision floating point value of the field in <c>column</c> of the current row.</returns>\n        public Double AsDouble(string column) {\n            return Convert.ToDouble(this[column]);\n        }\n\n        /// <summary>\n        ///     Gets the value of a field from a specific column and indices as a double-precision floating point value.\n        /// </summary>\n        /// <param name=\"column\">Name of the column from which to read.</param>\n        /// <param name=\"indices\">Indices for the full column.</param>\n        /// <returns>The double-precision floating point value of the field in <c>column</c> and <c>indices</c> of the current row.</returns>\n        public Double AsDouble(string column, params int[] indices) {\n            return AsDouble(BuildColumnName(column, indices));\n        }\n\n        /// <summary>\n        ///     Gets the value of a field from a specific column as a Quad value.\n        /// </summary>\n        /// <param name=\"column\">Name of the column from which to read.</param>\n        /// <returns>The quad value of the field in <c>column</c> of the current row.</returns>\n        public Quad AsQuad(string column) {\n            return (Quad)this[column];\n        }\n\n        /// <summary>\n        ///     Gets the value of a field from a specific column and indices as a Quad value.\n        /// </summary>\n        /// <param name=\"column\">Name of the column from which to read.</param>\n        /// <param name=\"indices\">Indices for the full column.</param>\n        /// <returns>The quad value of the field in <c>column</c> and <c>indices</c> of the current row.</returns>\n        public Quad AsQuad(string column, params int[] indices) {\n            return AsQuad(BuildColumnName(column, indices));\n        }\n\n        /// <summary>\n        ///     Gets the value of a field from a specific column and indices as a Int array.\n        /// </summary>\n        /// <param name=\"column\">Name of the column from which to read.</param>\n        /// <param name=\"indices\">Indices for the full column.</param>\n        /// <returns>The Int array of the field in <c>column</c> of the current row.</returns>\n        public int[] AsIntArray(string column) {\n            byte[] input = BitConverter.GetBytes(Convert.ToUInt32(this[column]));\n            int[] NewArray = new int[input.Length];\n            for (int i = 0; i < input.Length; i++) {\n                NewArray[i] = input[i] & 0xff;\n            }\n            return NewArray;\n        }\n        #endregion\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Xiv/XivSheet.Enumerator.cs",
    "content": "﻿using System;\nusing System.Collections;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace SaintCoinach.Xiv {\n    partial class XivSheet<T> {\n        private class Enumerator : IEnumerator<T> {\n            #region Fields\n            private XivSheet<T> _Sheet;\n            private IEnumerator _SourceEnumerator;\n            #endregion\n\n            #region Constructors\n            public Enumerator(XivSheet<T> sheet) {\n                _Sheet = sheet;\n                _SourceEnumerator = sheet._Source.GetEnumerator();\n            }\n            #endregion\n\n            #region IEnumerator<T> Members\n\n            public T Current {\n                get {\n                    var srcRow = (Ex.Relational.IRelationalRow)_SourceEnumerator.Current;\n                    return _Sheet._Rows.GetOrAdd(srcRow.Key, k => _Sheet.CreateRow(srcRow));\n                }\n            }\n\n            #endregion\n\n            #region IDisposable Members\n\n            public void Dispose() {\n                \n            }\n\n            #endregion\n\n            #region IEnumerator Members\n\n            object IEnumerator.Current {\n                get { return Current; }\n            }\n\n            public bool MoveNext() {\n                return _SourceEnumerator.MoveNext();\n            }\n\n            public void Reset() {\n                _SourceEnumerator.Reset();\n            }\n\n            #endregion\n        }\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Xiv/XivSheet.cs",
    "content": "﻿using System;\nusing System.Collections;\nusing System.Collections.Concurrent;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Reflection;\n\nusing SaintCoinach.Ex;\nusing SaintCoinach.Ex.Relational;\n\nnamespace SaintCoinach.Xiv {\n    public partial class XivSheet<T> : IXivSheet<T> where T : IXivRow {\n        #region Fields\n\n        protected readonly ConcurrentDictionary<int, T> _Rows = new ConcurrentDictionary<int, T>();\n        protected readonly IRelationalSheet _Source;\n        private ConstructorInfo _RowConstructor;\n\n        #endregion\n\n        #region Properties\n\n        private ConstructorInfo RowConstructor {\n            get {\n                if (_RowConstructor != null) return _RowConstructor;\n                return _RowConstructor = GetRowConstructor(typeof(T), GetType());\n            }\n        }\n\n        protected static ConstructorInfo GetRowConstructor(Type type, Type sheetType) {\n            var ctors =\n                type.GetConstructors(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance);\n\n            foreach (var ctor in ctors) {\n                var param = ctor.GetParameters();\n                if (param.Length != 2) continue;\n                if (!param[0].ParameterType.IsAssignableFrom(sheetType)) continue;\n                if (!param[1].ParameterType.IsAssignableFrom(typeof(IRelationalRow))) continue;\n\n                return ctor;\n            }\n\n            throw new NotSupportedException(\"No matching constructor found.\");\n        }\n\n        public IEnumerable<int> Keys { get { return _Source.Keys; } }\n\n        #endregion\n\n        #region Constructors\n\n        public XivSheet(XivCollection collection, IRelationalSheet source) {\n            Collection = collection;\n            _Source = source;\n        }\n\n        #endregion\n\n        public XivCollection Collection { get; private set; }\n\n        #region IEnumerable<T> Members\n\n        public IEnumerator<T> GetEnumerator() {\n            return new Enumerator(this);\n        }\n\n        #endregion\n\n        #region IEnumerable Members\n\n        IEnumerator IEnumerable.GetEnumerator() {\n            return GetEnumerator();\n        }\n\n        #endregion\n\n        #region Factory\n\n        protected virtual T CreateRow(IRelationalRow sourceRow) {\n            if (RowConstructor == null)\n                throw new NotSupportedException(\"No matching constructor found.\");\n\n            return (T)RowConstructor.Invoke(new object[] {\n                this, sourceRow\n            });\n        }\n\n        #endregion\n\n        #region ISheet<T> Members\n\n        public T this[int key] {\n            get {\n                return _Rows.GetOrAdd(key, k => {\n                    if (!_Source.ContainsRow(k))\n                        throw new KeyNotFoundException();\n\n                    return CreateRow(_Source[key]);\n                });\n            }\n        }\n\n        #endregion\n\n        #region ISheet Members\n\n        string ISheet.Name { get { return _Source.Name; } }\n\n        Header ISheet.Header { get { return _Source.Header; } }\n\n        ExCollection ISheet.Collection { get { return Collection; } }\n\n        public int Count { get { return _Source.Count; } }\n\n        public bool ContainsRow(int row) {\n            return _Source.ContainsRow(row);\n        }\n\n        IRow ISheet.this[int row] { get { return this[row]; } }\n\n        public object this[int row, int column] { get { return this[row][column]; } }\n\n        #endregion\n\n        #region IRelationalSheet Members\n\n        RelationalHeader IRelationalSheet.Header { get { return _Source.Header; } }\n\n        RelationalExCollection IRelationalSheet.Collection { get { return Collection; } }\n\n        IRelationalRow IRelationalSheet.this[int row] { get { return this[row]; } }\n\n        public object this[int row, string columnName] { get { return this[row][columnName]; } }\n\n        public IRelationalRow IndexedLookup(string index, int key) { return _Source.IndexedLookup(index, key); }\n\n        #endregion\n\n        #region IXivSheet Members\n\n        IXivRow IXivSheet.this[int row] { get { return this[row]; } }\n\n        #endregion\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Xiv/XivSheet2.Enumerator.cs",
    "content": "﻿using System;\nusing System.Collections;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace SaintCoinach.Xiv {\n    partial class XivSheet2<T> {\n        private class Enumerator : IEnumerator<T> {\n            #region Fields\n            private XivSheet2<T> _Sheet;\n            private Ex.Variant2.DataRow _CurrentParent;\n            private IEnumerator _SourceParentEnumerator;\n            private IEnumerator _SourceSubEnumerator;\n            #endregion\n\n            #region Constructors\n            public Enumerator(XivSheet2<T> sheet) {\n                _Sheet = sheet;\n                _SourceParentEnumerator = sheet._Source.GetEnumerator();\n            }\n            #endregion\n\n            #region IEnumerator<T> Members\n\n            public T Current {\n                get {\n                    var srcRow = (Ex.Relational.IRelationalRow)_SourceSubEnumerator.Current;\n                    var key = Tuple.Create(_CurrentParent.Key, srcRow.Key);\n\n                    if (_Sheet._SubRows.TryGetValue(key, out var row)) return row;\n\n                    row = _Sheet.CreateSubRow(srcRow);\n                    _Sheet._SubRows.Add(key, row);\n                    return row;\n                }\n            }\n\n            #endregion\n\n            #region IDisposable Members\n\n            public void Dispose() {\n                \n            }\n\n            #endregion\n\n            #region IEnumerator Members\n\n            object IEnumerator.Current {\n                get { return Current; }\n            }\n\n            public bool MoveNext() {\n                if (_SourceSubEnumerator != null && _SourceSubEnumerator.MoveNext())\n                    return true;\n\n                if (!_SourceParentEnumerator.MoveNext())\n                    return false;\n\n                _CurrentParent = (Ex.Variant2.DataRow)_SourceParentEnumerator.Current;\n                _SourceSubEnumerator = _CurrentParent.SubRows.GetEnumerator();\n                return _SourceSubEnumerator.MoveNext(); // Should always be true.\n            }\n\n            public void Reset() {\n                _SourceParentEnumerator.Reset();\n                _SourceSubEnumerator = null;\n                _CurrentParent = null;\n            }\n\n            #endregion\n        }\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Xiv/XivSheet2.cs",
    "content": "﻿using System;\nusing System.Collections;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Reflection;\n\nusing SaintCoinach.Ex;\nusing SaintCoinach.Ex.Relational;\n\nnamespace SaintCoinach.Xiv {\n    public partial class XivSheet2<T> : XivSheet<XivRow>, IEnumerable where T : IXivSubRow {\n        #region Fields\n\n        private ConstructorInfo _SubRowConstructor;\n        private Dictionary<Tuple<int, int>, T> _SubRows = new Dictionary<Tuple<int, int>, T>();\n\n        #endregion\n\n        #region Properties\n\n        private ConstructorInfo SubRowConstructor {\n            get {\n                if (_SubRowConstructor != null) return _SubRowConstructor;\n                return _SubRowConstructor = GetRowConstructor(typeof(T), GetType());\n            }\n        }\n\n        #endregion\n\n        #region Constructors\n\n        public XivSheet2(XivCollection collection, IRelationalSheet source):\n            base(collection, source) { }\n\n        #endregion\n\n        #region IEnumerable<T> Members\n\n        public new IEnumerator<T> GetEnumerator() {\n            return new Enumerator(this);\n        }\n\n        #endregion\n\n        #region IEnumerable Members\n\n        IEnumerator IEnumerable.GetEnumerator()\n        {\n            return GetEnumerator();\n        }\n\n        #endregion\n\n        #region Factory\n\n        protected virtual T CreateSubRow(IRelationalRow sourceRow) {\n            if (SubRowConstructor == null)\n                throw new NotSupportedException(\"No matching constructor found.\");\n\n            return (T)SubRowConstructor.Invoke(new object[] {\n                this, sourceRow\n            });\n        }\n\n        #endregion\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Xiv/XivSheetAttribute.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace SaintCoinach.Xiv {\n    [AttributeUsage(AttributeTargets.Class, AllowMultiple = false, Inherited = true)]\n    public class XivSheetAttribute : Attribute {\n        public string SheetName { get; set; }\n\n        public XivSheetAttribute(string sheetName) {\n            this.SheetName = sheetName;\n        }\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/Xiv/XivSubRow.cs",
    "content": "﻿using SaintCoinach.Ex;\nusing SaintCoinach.Ex.Relational;\nusing SaintCoinach.Ex.Variant2;\nusing System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace SaintCoinach.Xiv {\n    public class XivSubRow : XivRow, IXivSubRow {\n        private readonly SubRow _SourceSubRow;\n\n        #region Constructors\n\n        /// <summary>\n        ///     Initializes a new instance of the <see cref=\"XivSubRow\" /> class.\n        /// </summary>\n        /// <param name=\"sheet\"><see cref=\"IXivSheet\" /> containing this object.</param>\n        /// <param name=\"sourceRow\"><see cref=\"IRelationalRow\" /> to read data from.</param>\n        public XivSubRow(IXivSheet sheet, IRelationalRow sourceRow)\n            : base(sheet, sourceRow) {\n            _SourceSubRow = (SubRow)sourceRow;\n        }\n\n        #endregion\n\n        public string FullKey => _SourceSubRow.FullKey;\n\n        #region IXivSubRow Members\n\n        public IRow ParentRow => _SourceSubRow.ParentRow;\n\n        public int ParentKey => _SourceSubRow.ParentRow.Key;\n\n        #endregion\n    }\n}\n"
  },
  {
    "path": "SaintCoinach/app.config",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<configuration>\n  <configSections>\n    <section name=\"entityFramework\" type=\"System.Data.Entity.Internal.ConfigFile.EntityFrameworkSection, EntityFramework, Version=6.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089\" requirePermission=\"false\"/>\n    <!-- For more information on Entity Framework configuration, visit http://go.microsoft.com/fwlink/?LinkID=237468 -->\n    <section name=\"system.data\" type=\"System.Data.Common.DbProviderFactoriesConfigurationHandler, System.Data, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089\" />\n  </configSections>\n  <startup>\n    \n  </startup>\n  <entityFramework>\n    <providers>\n      <provider invariantName=\"System.Data.SqlClient\" type=\"System.Data.Entity.SqlServer.SqlProviderServices, EntityFramework.SqlServer\"/>\n      <provider invariantName=\"System.Data.SQLite.EF6\" type=\"System.Data.SQLite.EF6.SQLiteProviderServices, System.Data.SQLite.EF6\"/>\n    </providers>\n    <defaultConnectionFactory type=\"System.Data.Entity.Infrastructure.LocalDbConnectionFactory, EntityFramework\">\n      <parameters>\n        <parameter value=\"mssqllocaldb\"/>\n      </parameters>\n    </defaultConnectionFactory>\n  </entityFramework>\n  <runtime>\n    <assemblyBinding xmlns=\"urn:schemas-microsoft-com:asm.v1\">\n      <dependentAssembly>\n        <assemblyIdentity name=\"System.Data.SQLite\" publicKeyToken=\"db937bc2d44ff139\" culture=\"neutral\"/>\n        <bindingRedirect oldVersion=\"0.0.0.0-1.0.96.0\" newVersion=\"1.0.96.0\"/>\n      </dependentAssembly>\n    </assemblyBinding>\n  </runtime>\n</configuration>\n"
  },
  {
    "path": "SaintCoinach/packages.config",
    "content": "﻿<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<packages>\n  <package id=\"DotNetZip\" version=\"1.12.0\" targetFramework=\"net461\" />\n  <package id=\"EntityFramework\" version=\"6.2.0\" targetFramework=\"net451\" />\n  <package id=\"Newtonsoft.Json\" version=\"12.0.1\" targetFramework=\"net461\" />\n  <package id=\"System.Data.SQLite\" version=\"1.0.105.2\" targetFramework=\"net451\" />\n  <package id=\"System.Data.SQLite.Core\" version=\"1.0.105.2\" targetFramework=\"net451\" requireReinstallation=\"true\" />\n  <package id=\"System.Data.SQLite.EF6\" version=\"1.0.105.2\" targetFramework=\"net451\" requireReinstallation=\"true\" />\n  <package id=\"System.Data.SQLite.Linq\" version=\"1.0.105.2\" targetFramework=\"net451\" requireReinstallation=\"true\" />\n  <package id=\"System.Runtime.CompilerServices.Unsafe\" version=\"4.7.1\" targetFramework=\"net461\" />\n  <package id=\"System.Text.Encoding.CodePages\" version=\"4.7.0\" targetFramework=\"net461\" />\n</packages>"
  },
  {
    "path": "SaintCoinach.Cmd/App.config",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<configuration>\n  <configSections>\n    <sectionGroup name=\"applicationSettings\" type=\"System.Configuration.ApplicationSettingsGroup, System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089\">\n      <section name=\"SaintCoinach.Cmd.Properties.Settings\" type=\"System.Configuration.ClientSettingsSection, System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089\" requirePermission=\"false\"/>\n    </sectionGroup>\n    <section name=\"entityFramework\" type=\"System.Data.Entity.Internal.ConfigFile.EntityFrameworkSection, EntityFramework, Version=6.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089\" requirePermission=\"false\"/>\n    <section name=\"system.data\" type=\"System.Data.Common.DbProviderFactoriesConfigurationHandler, System.Data, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089\" />\n    <!-- For more information on Entity Framework configuration, visit http://go.microsoft.com/fwlink/?LinkID=237468 -->\n  </configSections>\n  <startup>\n    <supportedRuntime version=\"v4.0\" sku=\".NETFramework,Version=v4.6.1\"/>\n  </startup>\n  <applicationSettings>\n    <SaintCoinach.Cmd.Properties.Settings>\n      <setting name=\"DataPath\" serializeAs=\"String\">\n        <value />\n      </setting>\n    </SaintCoinach.Cmd.Properties.Settings>\n  </applicationSettings>\n  <entityFramework>\n    <providers>\n      <provider invariantName=\"System.Data.SqlClient\" type=\"System.Data.Entity.SqlServer.SqlProviderServices, EntityFramework.SqlServer\"/>\n      <provider invariantName=\"System.Data.SQLite.EF6\" type=\"System.Data.SQLite.EF6.SQLiteProviderServices, System.Data.SQLite.EF6\"/>\n    </providers>\n    <defaultConnectionFactory type=\"System.Data.Entity.Infrastructure.LocalDbConnectionFactory, EntityFramework\">\n      <parameters>\n        <parameter value=\"mssqllocaldb\"/>\n      </parameters>\n    </defaultConnectionFactory>\n  </entityFramework>\n  <runtime>\n    <assemblyBinding xmlns=\"urn:schemas-microsoft-com:asm.v1\">\n      <dependentAssembly>\n        <assemblyIdentity name=\"System.Data.SQLite\" publicKeyToken=\"db937bc2d44ff139\" culture=\"neutral\"/>\n        <bindingRedirect oldVersion=\"0.0.0.0-1.0.96.0\" newVersion=\"1.0.96.0\"/>\n      </dependentAssembly>\n    </assemblyBinding>\n  </runtime>\n</configuration>\n"
  },
  {
    "path": "SaintCoinach.Cmd/Commands/AllExdCommand.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.IO;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\n\nusing SaintCoinach;\nusing SaintCoinach.Ex;\nusing SaintCoinach.Xiv;\nusing Tharga.Console.Commands.Base;\n\n#pragma warning disable CS1998\n\nnamespace SaintCoinach.Cmd.Commands {\n    public class AllExdCommand : AsyncActionCommandBase {\n        private ARealmReversed _Realm;\n\n        public AllExdCommand(ARealmReversed realm)\n            : base(\"allexd\", \"Export all data (default), or only specific data files, seperated by spaces; including all languages.\") {\n            _Realm = realm;\n        }\n\n        public override async Task InvokeAsync(string[] paramList) {\n            const string CsvFileFormat = \"exd-all/{0}{1}.csv\";\n\n            IEnumerable<string> filesToExport;\n\n            if (paramList.Length == 0)\n                filesToExport = _Realm.GameData.AvailableSheets;\n            else\n                filesToExport = paramList.Select(_ => _Realm.GameData.FixName(_));\n\n            var successCount = 0;\n            var failCount = 0;\n            foreach (var name in filesToExport) {\n                var sheet = _Realm.GameData.GetSheet(name);\n                foreach(var lang in sheet.Header.AvailableLanguages) {\n                    var code = lang.GetCode();\n                    if (code.Length > 0)\n                        code = \".\" + code;\n                    var target = new FileInfo(Path.Combine(_Realm.GameVersion, string.Format(CsvFileFormat, name, code)));\n                    try {\n                        if (!target.Directory.Exists)\n                            target.Directory.Create();\n                        ExdHelper.SaveAsCsv(sheet, lang, target.FullName, false);\n                        ++successCount;\n                    } catch (Exception e) {\n                        OutputError($\"Export of {name} failed: {e.Message}\");\n                        try { if (target.Exists) { target.Delete(); } } catch { }\n                        ++failCount;\n                    }\n                }\n                \n            }\n            OutputInformation($\"{successCount} files exported, {failCount} failed\");\n        }\n    }\n}\n"
  },
  {
    "path": "SaintCoinach.Cmd/Commands/AllExdRawCommand.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.IO;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nusing SaintCoinach.Ex;\nusing Tharga.Console.Commands.Base;\n\n#pragma warning disable CS1998\n\nnamespace SaintCoinach.Cmd.Commands\n{\n    public class AllExdRawCommand : AsyncActionCommandBase\n    {\n        private ARealmReversed _Realm;\n\n        /// <summary>\n        /// Setup the command\n        /// </summary>\n        /// <param name=\"realm\"></param>\n        public AllExdRawCommand(ARealmReversed realm)\n            : base(\"allrawexd\", \"Export all data (default), or only specific data files, seperated by spaces; including all languages. No post-processing is applied to values.\")\n        {\n            _Realm = realm;\n        }\n\n        /// <summary>\n        /// Obtain game sheets from the game data\n        /// </summary>\n        /// <param name=\"paramList\"></param>\n        /// <returns></returns>\n        public override async Task InvokeAsync(string[] paramList)\n        {\n            var versionPath = _Realm.GameVersion;\n            if (paramList?.Contains(\"/UseDefinitionVersion\") ?? false)\n                versionPath = _Realm.DefinitionVersion;\n            \n            const string CsvFileFormat = \"raw-exd-all/{0}{1}.csv\";\n\n            IEnumerable<string> filesToExport;\n\n            // Gather files to export, may be split by params.\n            if (paramList.Length == 0)\n                filesToExport = _Realm.GameData.AvailableSheets;\n            else\n                filesToExport = paramList.Select(_ => _Realm.GameData.FixName(_));\n\n            // Action counts\n            var successCount = 0;\n            var failCount = 0;\n            var currentCount = 0;\n            var total = filesToExport.Count();\n\n            // Process game files.\n            foreach (var name in filesToExport)\n            {\n                currentCount++;\n                var sheet = _Realm.GameData.GetSheet(name);\n\n                // Loop through all available languages\n                foreach (var lang in sheet.Header.AvailableLanguages)\n                {\n                    var code = lang.GetCode();\n                    if (code.Length > 0)\n                        code = \".\" + code;\n\n                    var target = new FileInfo(Path.Combine(versionPath, string.Format(CsvFileFormat, name, code)));\n\n                    try\n                    {\n                        if (!target.Directory.Exists)\n                            target.Directory.Create();\n\n                        // Save\n                        OutputInformation($\"[{currentCount}/{total}] Processing: {name} - Language: {lang.GetSuffix()}\");\n                        ExdHelper.SaveAsCsv(sheet, lang, target.FullName, true);\n                        ++successCount;\n                    }\n                    catch (Exception e)\n                    {\n                        OutputError($\"Export of {name} failed: {e.Message}\");\n                        try { if (target.Exists) { target.Delete(); } } catch { }\n                        ++failCount;\n                    }\n                }\n            }\n            OutputInformation($\"{successCount} files exported, {failCount} failed\");\n        }\n    }\n}"
  },
  {
    "path": "SaintCoinach.Cmd/Commands/BgmCommand.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\nusing Tharga.Console.Commands.Base;\n\n#pragma warning disable CS1998\n\nnamespace SaintCoinach.Cmd.Commands {\n    public class BgmCommand : AsyncActionCommandBase {\n        private ARealmReversed _Realm;\n\n        public BgmCommand(ARealmReversed realm)\n            : base(\"bgm\", \"Export all BGM files (default), or only those matching specific strings, separated by spaces (e.g. bgm_ride bgm_orch)\") {\n            _Realm = realm;\n        }\n\n        public override async Task InvokeAsync(string[] paramList) {\n            var bgms = _Realm.GameData.GetSheet(\"BGM\");\n            string[] searchStrings;\n\n            if (paramList.Length == 0)\n                searchStrings = Array.Empty<String>();\n            else\n                searchStrings = paramList;\n\n            var successCount = 0;\n            var failCount = 0;\n            foreach (Xiv.IXivRow bgm in bgms) {\n                var filePath = bgm[\"File\"].ToString();\n\n                try {\n                    if (string.IsNullOrWhiteSpace(filePath) || !IsMatch(searchStrings, filePath))\n                        continue;\n\n                    if (ExportFile(filePath, null)) {\n                        ++successCount;\n                    } else {\n                        OutputError($\"File {filePath} not found.\");\n                        ++failCount;\n                    }\n                } catch(Exception e) {\n                    OutputError($\"Export of {filePath} failed!\");\n                    OutputError(e, true);\n                    ++failCount;\n                }\n            }\n\n            var orchestrion = _Realm.GameData.GetSheet(\"Orchestrion\");\n            var orchestrionPath = _Realm.GameData.GetSheet(\"OrchestrionPath\");\n            foreach (Xiv.IXivRow orchestrionInfo in orchestrion) {\n                var path = orchestrionPath[orchestrionInfo.Key];\n                var name = orchestrionInfo[\"Name\"].ToString();\n                var filePath = path[\"File\"].ToString();\n\n                if (string.IsNullOrWhiteSpace(filePath) || !IsMatch(searchStrings, filePath))\n                    continue;\n\n                try {\n                    if (ExportFile(filePath, name)) {\n                        ++successCount;\n                    } else {\n                        OutputError($\"File {filePath} not found.\");\n                        ++failCount;\n                    }\n                }\n                catch (Exception e) {\n                    OutputError($\"Export of {filePath} failed!\");\n                    OutputError(e, true);\n                    ++failCount;\n                }\n            }\n\n            OutputInformation($\"{successCount} files exported, {failCount} failed\");\n        }\n\n        private bool ExportFile(string filePath, string suffix) {\n            if (!_Realm.Packs.TryGetFile(filePath, out var file))\n                return false;\n\n            var scdFile = new Sound.ScdFile(file);\n            var count = 0;\n            for (var i = 0; i < scdFile.ScdHeader.EntryCount; ++i) {\n                var e = scdFile.Entries[i];\n                if (e == null)\n                    continue;\n\n                var fileNameWithoutExtension = System.IO.Path.GetFileNameWithoutExtension(filePath);\n                if (suffix != null)\n                    fileNameWithoutExtension += \"-\" + suffix;\n                if (++count > 1)\n                    fileNameWithoutExtension += \"-\" + count;\n\n                foreach (var invalidChar in System.IO.Path.GetInvalidFileNameChars())\n                    fileNameWithoutExtension = fileNameWithoutExtension.Replace(invalidChar.ToString(), \"\");\n\n                var targetPath = System.IO.Path.Combine(_Realm.GameVersion, System.IO.Path.GetDirectoryName(filePath), fileNameWithoutExtension);\n\n                switch (e.Header.Codec) {\n                    case Sound.ScdCodec.MSADPCM:\n                        targetPath += \".wav\";\n                        break;\n                    case Sound.ScdCodec.OGG:\n                        targetPath += \".ogg\";\n                        break;\n                    default:\n                        throw new NotSupportedException();\n                }\n\n                var fInfo = new System.IO.FileInfo(targetPath);\n\n                if (!fInfo.Directory.Exists)\n                    fInfo.Directory.Create();\n                System.IO.File.WriteAllBytes(fInfo.FullName, e.GetDecoded());\n            }\n\n            return true;\n        }\n\n        private bool IsMatch(String[] searchStrings, string filePath) {\n            return searchStrings.Length == 0 || searchStrings.Any(searchString => filePath.IndexOf(searchString, StringComparison.OrdinalIgnoreCase) > 0);\n        }\n    }\n}"
  },
  {
    "path": "SaintCoinach.Cmd/Commands/ExdCommand.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.IO;\nusing System.Linq;\nusing System.Threading.Tasks;\nusing Tharga.Console.Commands.Base;\n\n#pragma warning disable CS1998\n\nnamespace SaintCoinach.Cmd.Commands {\n    public class ExdCommand : AsyncActionCommandBase {\n        private ARealmReversed _Realm;\n\n        public ExdCommand(ARealmReversed realm)\n            : base(\"exd\", \"Export all data (default), or only specific data files, seperated by spaces.\") {\n            _Realm = realm;\n        }\n\n        public override async Task InvokeAsync(string[] paramList) {\n            const string CsvFileFormat = \"exd/{0}.csv\";\n\n            IEnumerable<string> filesToExport;\n\n            if (paramList.Length == 0)\n                filesToExport = _Realm.GameData.AvailableSheets;\n            else\n                filesToExport = paramList.Select(_ => _Realm.GameData.FixName(_));\n\n            var successCount = 0;\n            var failCount = 0;\n            foreach (var name in filesToExport) {\n                var target = new FileInfo(Path.Combine(_Realm.GameVersion, string.Format(CsvFileFormat, name)));\n                try {\n                    var sheet = _Realm.GameData.GetSheet(name);\n\n                    if (!target.Directory.Exists)\n                        target.Directory.Create();\n\n                    ExdHelper.SaveAsCsv(sheet, Ex.Language.None, target.FullName, false);\n\n                    ++successCount;\n                } catch (Exception e) {\n                    OutputError($\"Export of {name} failed: {e.Message}\");\n                    try { if (target.Exists) { target.Delete(); } } catch { }\n                    ++failCount;\n                }\n            }\n            OutputInformation($\"{successCount} files exported, {failCount} failed\");\n        }\n    }\n}\n"
  },
  {
    "path": "SaintCoinach.Cmd/Commands/ExdHeaderCommand.cs",
    "content": "﻿using Newtonsoft.Json;\nusing Newtonsoft.Json.Linq;\nusing System;\nusing System.Collections.Generic;\nusing System.IO;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\nusing Tharga.Console.Commands.Base;\n\nnamespace SaintCoinach.Cmd.Commands {\n    public class ExdHeaderCommand : AsyncActionCommandBase {\n        private ARealmReversed _Realm;\n\n        public ExdHeaderCommand(ARealmReversed realm)\n            :base(\"exdheader\", \"Export all data headers.\") {\n            _Realm = realm;\n        }\n\n        #pragma warning disable CS1998\n        public override async Task InvokeAsync(string[] paramList) {\n            var headers = new List<SheetHeader>();\n            foreach (var name in _Realm.GameData.AvailableSheets) {\n                var header = new SheetHeader() { Name = name, Columns = new List<SheetColumn>() };\n                headers.Add(header);\n\n                var sheet = _Realm.GameData.GetSheet(name);\n                foreach (var relationalColumn in sheet.Header.Columns) {\n                    header.Columns.Add(new SheetColumn() {\n                        Name = relationalColumn.Name,\n                        Type = relationalColumn.Reader.Type.Name\n                    });\n                }\n            }\n\n            System.IO.Directory.CreateDirectory(_Realm.GameVersion);\n\n            var settings = new JsonSerializerSettings() {\n                Formatting = Formatting.Indented,\n                NullValueHandling = NullValueHandling.Ignore\n            };\n\n            var json = JsonConvert.SerializeObject(headers, settings);\n            File.WriteAllText(Path.Combine(_Realm.GameVersion, \"exd-header.json\"), json);\n        }\n\n        private class SheetHeader {\n            public string Name;\n            public List<SheetColumn> Columns;\n        }\n\n        private class SheetColumn {\n            public string Name;\n            public string Type;\n        }\n    }\n}\n"
  },
  {
    "path": "SaintCoinach.Cmd/Commands/FurnitureExpCommand.cs",
    "content": "﻿using System;\nusing System.IO;\nusing System.Linq;\nusing System.Threading.Tasks;\n\nusing SaintCoinach.Xiv;\nusing SaintCoinach.Cmd.Commands;\nusing Tharga.Console.Commands.Base;\n\n#pragma warning disable CS1998\n\n/// <summary>\n/// Class responsible for saving mdl and texture files for furniteres and housing yards.\n/// </summary>\nnamespace SaintCoinach.Cmd.Commands {\n    public class MdlCommand : AsyncActionCommandBase {\n        private ARealmReversed _Realm;\n        private HousingItem[] _AllFurniture;\n\n        public MdlCommand(ARealmReversed realm)\n            : base(\"furn_mdl_tex\", \"Export all MDL Furniture/Yard files.\") {\n            _Realm = realm;\n        }\n\n        /// <summary>\n        /// Loads all furniture and yard information in order to export them.\n        /// </summary>\n        /// <param name=\"paramList\">List of parameters being used in this function</param>\n        /// <return name=\"result\">A boolean indicating if the process failed or not.</return>\n        public override async Task InvokeAsync(string[] paramList) {\n            var indoor = _Realm.GameData.GetSheet(\"HousingFurniture\");\n            var outdoor = _Realm.GameData.GetSheet(\"HousingYardObject\");\n            _AllFurniture = indoor.Cast<HousingItem>().Concat(outdoor.Cast<HousingItem>()).Where(_ => _.Item != null && _.Item.Key != 0 && _.Item.Name.ToString().Length > 0).OrderBy(_ => _.Item.Name).ToArray();\n\n            var successCount = 0;\n            var failCount = 0;\n            foreach (HousingItem outdoorItem in _AllFurniture) {\n                var filePath = outdoorItem.GetScene().File.ToString();\n\n                try {\n\n                    if (string.IsNullOrWhiteSpace(filePath))\n                        continue;\n\n                    if (ExportFile(filePath)) {\n                        ++successCount;\n                    }\n                    else {\n                        OutputError($\"File {filePath} not found.\");\n                        ++failCount;\n                    }\n                }\n                catch (Exception e) {\n                    OutputError(\"Export of {filePath} failed!\");\n                    OutputError(e, true);\n                    ++failCount;\n                }\n            }\n            OutputInformation($\"{successCount} files exported, {failCount} failed\");\n        }\n\n        /// <summary>\n        /// Exports files to mdl and text using the function that exports any asset object type.\n        /// </summary>\n        /// <param name=\"filePath\">File path to where the item's sgb file is located</param>\n        /// <return name=\"result\">A boolean indicating if the file was saved correctly.</return>\n        private bool ExportFile(string filePath) {\n            var result = false;\n            var resultMdl = false;\n            var resultTexD = false;\n            var resultTexN = false;\n            var resultTexS = false;\n\n            try {\n                RawCommand command = new RawCommand(_Realm);\n\n                //Running for mdl\n                resultMdl = WriteFunction(filePath.Replace(\"asset\", \"bgparts\").Replace(\"sgb\", \"mdl\"));\n\n                //Running for all three textures\n                resultTexD = WriteFunction(filePath.Replace(\"asset\", \"texture\").Replace(\".sgb\", \"_1a_d.tex\"));\n                resultTexN = WriteFunction(filePath.Replace(\"asset\", \"texture\").Replace(\".sgb\", \"_1a_n.tex\"));\n                resultTexS = WriteFunction(filePath.Replace(\"asset\", \"texture\").Replace(\".sgb\", \"_1a_s.tex\"));\n\n                // Only return true when mdl exports was sucessfull\n                result = resultMdl;\n            }\n            catch (Exception e) {\n                OutputError(e.Message);\n                return result;\n            }\n\n            return result;\n        }\n\n        private bool WriteFunction(string filePath) {\n            if (!_Realm.Packs.TryGetFile(filePath, out var file)) return false;\n            try {\n                var target = new FileInfo(Path.Combine(_Realm.GameVersion, file.Path));\n                if (!target.Directory.Exists)\n                    target.Directory.Create();\n\n                var data = file.GetData();\n                File.WriteAllBytes(target.FullName, data);\n            }\n            catch (Exception e) {\n                OutputError(e.Message);\n                return false;\n            }\n\n            return true;\n        }\n\n    }\n}"
  },
  {
    "path": "SaintCoinach.Cmd/Commands/HDUiCommand.cs",
    "content": "﻿using System;\nusing System.IO;\nusing System.Threading.Tasks;\nusing Tharga.Console.Commands.Base;\n\n#pragma warning disable CS1998\n\nnamespace SaintCoinach.Cmd.Commands {\n    public class HDUiCommand : AsyncActionCommandBase {\n        const string UiImagePathFormat = \"ui/icon/{0:D3}000{1}/{2:D6}_hr1.tex\";\n        static readonly string[] UiVersions = new string[] {\n            \"\",\n            \"/en\",\n            \"/ja\",\n            \"/fr\",\n            \"/de\",\n            \"/hq\",\n            \"/chs\"\n        };\n\n        private ARealmReversed _Realm;\n\n        public HDUiCommand(ARealmReversed realm)\n            : base(\"uiHD\", \"Export all, a single, or a range of HD UI icons.\") {\n            _Realm = realm;\n        }\n\n        public override async Task InvokeAsync(string[] paramList) {\n            var min = 0;\n            var max = 999999;\n\n            if (paramList.Length != 0) {\n                if (paramList.Length == 1) {\n                    if (int.TryParse(paramList[0], out var parsed))\n                        min = max = parsed;\n                    else {\n                        OutputError(\"Failed to parse parameters.\");\n                    }\n                }\n                else if (paramList.Length == 2) {\n                    if (!int.TryParse(paramList[0], out min) || !int.TryParse(paramList[1], out max)) {\n                        OutputError(\"Failed to parse parameters.\");\n                        return;\n                    }\n\n                    if (max < min) {\n                        OutputError(\"Invalid parameters.\");\n                        return;\n                    }\n                }\n                else {\n                    OutputError(\"Failed to parse parameters.\");\n                    return;\n                }\n            }\n\n            var count = 0;\n            for (int i = min; i <= max; ++i) {\n                try {\n                    count += Process(i);\n                }\n                catch (Exception e) {\n                    OutputError($\"{i:D6}: {e.Message}\");\n                }\n            }\n            OutputInformation($\"{count} images processed\");\n        }\n\n        private int Process(int i) {\n            var count = 0;\n            foreach (var v in UiVersions) {\n                if (Process(i, v))\n                    ++count;\n            }\n            return count;\n        }\n        private bool Process(int i, string version) {\n            var filePath = string.Format(UiImagePathFormat, i / 1000, version, i);\n\n            if (_Realm.Packs.TryGetFile(filePath, out var file)) {\n                if (file is Imaging.ImageFile imgFile) {\n                    var img = imgFile.GetImage();\n\n                    var target = new FileInfo(Path.Combine(_Realm.GameVersion, file.Path));\n                    if (!target.Directory.Exists)\n                        target.Directory.Create();\n                    var pngPath = target.FullName.Substring(0, target.FullName.Length - target.Extension.Length) + \".png\";\n                    img.Save(pngPath);\n\n                    return true;\n                }\n                else {\n                    OutputError($\"{filePath} is not an image.\");\n                }\n            }\n            return false;\n        }\n    }\n}\n"
  },
  {
    "path": "SaintCoinach.Cmd/Commands/ImageCommand.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.IO;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\nusing Tharga.Console.Commands.Base;\n\n#pragma warning disable CS1998\n\nnamespace SaintCoinach.Cmd.Commands {\n    public class ImageCommand : AsyncActionCommandBase {\n        private ARealmReversed _Realm;\n\n        public ImageCommand(ARealmReversed realm)\n            : base(\"image\", \"Export an image file.\") {\n            _Realm = realm;\n        }\n\n        public override async Task InvokeAsync(string[] paramList) {\n            try {\n                var path = paramList[0].Trim();\n                if (_Realm.Packs.TryGetFile(path, out var file)) {\n                    Imaging.ImageFile imgFile = null;\n                    if (path.EndsWith(\".atex\")) {\n                        imgFile = new Imaging.ImageFile((IO.FileDefault) file);\n                    } else if (file is Imaging.ImageFile) {\n                        imgFile = (Imaging.ImageFile) file;\n                    }\n\n                    if (imgFile is null) {\n                        OutputError($\"File is not an image (actual: {file.CommonHeader.FileType}).\");\n                        return;\n                    }\n\n                    var img = imgFile.GetImage();\n\n                    var target = new FileInfo(Path.Combine(_Realm.GameVersion, file.Path));\n                    if (!target.Directory.Exists)\n                        target.Directory.Create();\n                    var pngPath = target.FullName.Substring(0, target.FullName.Length - target.Extension.Length) + \".png\";\n                    img.Save(pngPath);\n                        \n                } else\n                    OutputError(\"File not found.\");\n            } catch (Exception e) {\n                OutputError(e.Message);\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "SaintCoinach.Cmd/Commands/LanguageCommand.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\nusing Tharga.Console.Commands.Base;\n\n#pragma warning disable CS1998\n\nnamespace SaintCoinach.Cmd.Commands {\n    using Ex;\n\n    public class LanguageCommand : AsyncActionCommandBase {\n        private ARealmReversed _Realm;\n\n        public LanguageCommand(ARealmReversed realm)\n            : base(\"lang\", \"Change the language.\") {\n            _Realm = realm;\n        }\n\n        public override async Task InvokeAsync(string[] paramList) {\n            if (paramList.Length == 0) {\n                OutputInformation($\"Current language: {_Realm.GameData.ActiveLanguage}\");\n                return;\n            }\n            var param = paramList[0].Trim();\n            if (!Enum.TryParse<Language>(param, out var newLang)) {\n                newLang = LanguageExtensions.GetFromCode(param);\n                if (newLang == Language.Unsupported) {\n                    OutputError($\"Unknown language: {param}\");\n                    return;\n                }\n            }\n            _Realm.GameData.ActiveLanguage = newLang;\n        }\n    }\n}\n"
  },
  {
    "path": "SaintCoinach.Cmd/Commands/MapCommand.cs",
    "content": "using System;\nusing System.Collections.Generic;\nusing System.Drawing.Imaging;\nusing System.IO;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\nusing Tharga.Console.Commands.Base;\n\n#pragma warning disable CS1998\n\nnamespace SaintCoinach.Cmd.Commands {\n    public class MapCommand : AsyncActionCommandBase {\n        private ARealmReversed _Realm;\n\n        public MapCommand(ARealmReversed realm)\n            : base(\"maps\", \"Export all map images.\") {\n            _Realm = realm;\n        }\n\n        public override async Task InvokeAsync(string[] paramList) {\n            var format = ImageFormat.Png;\n            var rawFileNames = false;\n\n            if (paramList.Length != 0) {\n                var parameters = paramList;\n                rawFileNames = parameters.Contains(\"raw\");\n                if (parameters.Contains(\"jpg\"))\n                    format = ImageFormat.Jpeg;\n                else if (paramList.Contains(\"png\"))\n                    format = ImageFormat.Png;\n                else\n                    OutputError($\"Invalid map format {paramList}\");\n            }\n\n            var c = 0;\n            var allMaps = _Realm.GameData.GetSheet<SaintCoinach.Xiv.Map>()\n                .Where(m => m.PlaceName != null);\n\n            var fileSet = new Dictionary<string, int>();\n            foreach (var map in allMaps) {\n                var img = map.MediumImage;\n                if (img == null)\n                    continue;\n\n                var outPathSb = new StringBuilder(\"ui/map/\");\n                if(rawFileNames) {\n                    outPathSb.AppendFormat(\"{0}/{1}\", map.Id.ToString().Split('/')[0], map.Id.ToString().Replace(\"/\", \".\"));\n                    outPathSb.Append(FormatToExtension(format));\n                } else {\n                    var territoryName = map.TerritoryType?.Name?.ToString();\n                    if (!string.IsNullOrEmpty(territoryName)) {\n                        if (territoryName.Length < 3) {\n                            outPathSb.AppendFormat(\"{0}/\", territoryName);\n                        }\n                        else {\n                            outPathSb.AppendFormat(\"{0}/\", territoryName.Substring(0, 3));\n                        }\n\n                        outPathSb.AppendFormat(\"{0} - \", territoryName);\n                    }\n                    outPathSb.AppendFormat(\"{0}\", ToPathSafeString(map.PlaceName.Name.ToString()));\n                    if (map.LocationPlaceName != null && map.LocationPlaceName.Key != 0 && !map.LocationPlaceName.Name.IsEmpty)\n                        outPathSb.AppendFormat(\" - {0}\", ToPathSafeString(map.LocationPlaceName.Name.ToString()));\n                    var mapKey = outPathSb.ToString();\n                    fileSet.TryGetValue(mapKey, out int mapIndex);\n                    if (mapIndex > 0) {\n                        outPathSb.AppendFormat(\" - {0}\", mapIndex);\n                    }\n                    fileSet[mapKey] = mapIndex + 1;\n                    outPathSb.Append(FormatToExtension(format));\n                }\n\n                var outFile = new FileInfo(Path.Combine(_Realm.GameVersion, outPathSb.ToString()));\n                if (!outFile.Directory.Exists)\n                    outFile.Directory.Create();\n\n                img.Save(outFile.FullName, format);\n                ++c;\n            }\n            OutputInformation($\"{c} maps saved\");\n        }\n        \n        static string FormatToExtension(ImageFormat format) {\n            if (format == ImageFormat.Png)\n                return \".png\";\n            if (format == ImageFormat.Jpeg)\n                return \".jpg\";\n\n            throw new NotImplementedException();\n        }\n\n        static string ToPathSafeString(string input, char invalidReplacement = '_') {\n            var sb = new StringBuilder(input);\n            var invalid = Path.GetInvalidFileNameChars();\n            foreach (var c in invalid)\n                sb.Replace(c, invalidReplacement);\n            return sb.ToString();\n        }\n    }\n}\n"
  },
  {
    "path": "SaintCoinach.Cmd/Commands/RawCommand.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.IO;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\nusing Tharga.Console.Commands.Base;\n\n#pragma warning disable CS1998\n\nnamespace SaintCoinach.Cmd.Commands {\n    public class RawCommand : AsyncActionCommandBase {\n        private ARealmReversed _Realm;\n\n        public RawCommand(ARealmReversed realm)\n            : base(\"raw\", \"Save raw contents of a file.\") {\n            _Realm = realm;\n        }\n\n        public override async Task InvokeAsync(string[] paramList) {\n            if (paramList == null)\n                return;\n            \n            try {\n                if (_Realm.Packs.TryGetFile(paramList[0].Trim(), out var file)) {\n                    var target = new FileInfo(Path.Combine(_Realm.GameVersion, file.Path));\n                    if (!target.Directory.Exists)\n                        target.Directory.Create();\n\n                    var data = file.GetData();\n                    File.WriteAllBytes(target.FullName, data);\n                } else\n                    OutputError(\"File not found.\");\n            } catch (Exception e) {\n                OutputError(e, true);\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "SaintCoinach.Cmd/Commands/RawExdCommand.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.IO;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\nusing Tharga.Console.Commands.Base;\n\n#pragma warning disable CS1998\n\nnamespace SaintCoinach.Cmd.Commands {\n    public class RawExdCommand : AsyncActionCommandBase {\n        private ARealmReversed _Realm;\n\n        public RawExdCommand(ARealmReversed realm)\n            : base(\"rawexd\", \"Export all data (default), or only specific data files, seperated by spaces. No post-processing is applied to values.\") {\n            _Realm = realm;\n        }\n\n        public override async Task InvokeAsync(string[] paramList) {\n            const string CsvFileFormat = \"rawexd/{0}.csv\";\n\n            IEnumerable<string> filesToExport;\n\n            if (paramList.Length == 0)\n                filesToExport = _Realm.GameData.AvailableSheets;\n            else\n                filesToExport = paramList.Select(_ => _Realm.GameData.FixName(_));\n\n            var successCount = 0;\n            var failCount = 0;\n            foreach (var name in filesToExport) {\n                var target = new FileInfo(Path.Combine(_Realm.GameVersion, string.Format(CsvFileFormat, name)));\n                try {\n                    var sheet = _Realm.GameData.GetSheet(name);\n\n                    if (!target.Directory.Exists)\n                        target.Directory.Create();\n\n                    ExdHelper.SaveAsCsv(sheet, Ex.Language.None, target.FullName, true);\n\n                    ++successCount;\n                } catch (Exception e) {\n                    OutputError($\"Export of {name} failed: {e.Message}\");\n                    try { if (target.Exists) { target.Delete(); } } catch { }\n                    ++failCount;\n                }\n            }\n            OutputInformation($\"{successCount} files exported, {failCount} failed\");\n        }\n    }\n}\n"
  },
  {
    "path": "SaintCoinach.Cmd/Commands/SqlExport.cs",
    "content": "using System;\nusing System.Collections.Generic;\nusing System.IO;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\nusing SaintCoinach.Xiv;\nusing SaintCoinach.Ex;\nusing SaintCoinach.Ex.Relational;\nusing Tharga.Console.Commands.Base;\n\n#pragma warning disable CS1998\n\nnamespace SaintCoinach.Cmd.Commands\n{\n    public class SqlExport : AsyncActionCommandBase\n    {\n        private ARealmReversed _Realm;\n        \n        public SqlExport(ARealmReversed realm) : \n            base(\"sql\", \"Exports the EXD data as SQL schema and associated imports.\")\n        {\n            _Realm = realm;\n        }\n\n        private string GetSqlType(Type type)\n        {\n            switch (type.Name)\n            {\n                case \"UInt32\":\n                    return \"INT UNSIGNED\";\n                case \"Int32\":\n                    return \"INT\";\n                case \"SByte\":\n                    return \"TINYINT\";\n                case \"Byte\":\n                    return \"TINYINT UNSIGNED\";\n                case \"XivString\":\n                    return \"TEXT\";\n                case \"Boolean\":\n                    return \"BIT\";\n                case \"UInt16\":\n                    return \"SMALLINT UNSIGNED\";\n                case \"Int16\":\n                    return \"SMALLINT\";\n                case \"Single\":\n                    return \"FLOAT\";\n                case \"Double\":\n                    return \"DOUBLE\";\n                case \"Quad\":\n                    return \"BIGINT UNSIGNED\";\n                \n                default:\n                    throw new NotImplementedException( $\"The type {type.Name} doesn't have an SQL type mapping\");\n            }\n        }\n        \n        static bool IsUnescaped(object self) {\n            return (self is Boolean\n                    || self is Byte\n                    || self is SByte\n                    || self is Int16\n                    || self is Int32\n                    || self is Int64\n                    || self is UInt16\n                    || self is UInt32\n                    || self is UInt64\n                    || self is Single\n                    || self is Double);\n        }\n\n        private string GetTableName(ISheet sheet)\n        {\n            return $\"`{sheet.Name.Replace(\"/\", \"_\")}`\";\n        }\n\n        private void DoRowData(ISheet sheet, XivRow row, List<string> data, StringBuilder sb)\n        {\n            for (int i = 0; i < sheet.Header.ColumnCount; i++)\n            {\n                var o = row.GetRaw(i);\n\n                if (o is Quad)\n                {\n                    var q = (Quad)o;\n                    data.Add(q.ToInt64().ToString());\n                    continue;\n                }\n\n                if (IsUnescaped(o))\n                {\n                    data.Add(o.ToString());\n                    continue;\n                }\n                    \n                string d = o.ToString();\n                if (string.IsNullOrEmpty(d))\n                    d = \"NULL\";\n                else\n                {\n                    d = $\"'{d.Replace(\"'\", \"\\\\'\")}'\";\n                }\n                data.Add(d);\n            }\n            \n            sb.AppendLine($\"  ( {string.Join(\", \", data)} ),\");\n        }\n\n        private void WriteVariant1Rows(ISheet sheet, StringBuilder sb)\n        {\n            var rows = sheet.Cast<XivRow>();\n            var cols = new List<string>();\n            \n            cols.Add(\"`_Key`\");\n\n            foreach (var col in sheet.Header.Columns.Cast<RelationalColumn>())\n            {\n                string name = string.IsNullOrEmpty(col.Name) ? $\"unk{col.Index}\" : col.Name;\n                \n                cols.Add($\"`{name}`\");\n            }\n\n            sb.AppendLine($\"INSERT INTO {GetTableName(sheet)} ({string.Join(\", \", cols)}) VALUES \");\n            \n            foreach (var row in rows)\n            {\n                var data = new List<string>();\n                data.Add(row.Key.ToString());\n\n                DoRowData(sheet, row, data, sb);\n            }\n\n            sb.Remove(sb.Length - 3, 3);\n            sb.AppendLine(\";\");\n        }\n\n        private void WriteVairant2Rows(ISheet sheet, StringBuilder sb)\n        {\n            var rows = sheet.Cast<XivSubRow>();\n            var cols = new List<string>();\n            \n            cols.Add(\"`_Key`\");\n            cols.Add(\"`_SubKey`\");\n\n            foreach (var col in sheet.Header.Columns.Cast<RelationalColumn>())\n            {\n                string name = string.IsNullOrEmpty(col.Name) ? $\"unk{col.Index}\" : col.Name;\n                \n                cols.Add($\"`{name}`\");\n            }\n\n            sb.AppendLine($\"INSERT INTO {GetTableName(sheet)} ({string.Join(\", \", cols)}) VALUES \");\n\n            foreach (var row in rows)\n            {\n                var data = new List<string>();\n                data.Add(row.ParentRow.Key.ToString());\n                data.Add(row.Key.ToString());\n\n                DoRowData(sheet, row, data, sb);\n            }\n\n            sb.Remove(sb.Length - 3, 3);\n            sb.AppendLine(\";\");\n        }\n\n        private void WriteRows(ISheet sheet, StringBuilder sb)\n        {\n            if (sheet.Header.Variant == 1)\n                WriteVariant1Rows(sheet, sb);\n            else\n                WriteVairant2Rows(sheet, sb);\n        }\n\n        public override async Task InvokeAsync(string[] paramList)\n        {\n            var imports = new List<string>();\n            \n            // .Where(n => !n.Contains(\"quest/\") && !n.Contains(\"custom/\"))\n            foreach (var name in _Realm.GameData.AvailableSheets)\n            {\n                var sheet = _Realm.GameData.GetSheet(name);\n                var variant = sheet.Header.Variant;\n                var sheet2 = sheet as XivSheet2<XivSubRow>;\n                \n                Console.WriteLine($\"Sheet: {name}, variant: {variant}\");\n\n                if (sheet.Count == 0)\n                    continue;\n\n                var sb = new StringBuilder();\n\n                sb.AppendLine($\"CREATE TABLE {GetTableName(sheet)} (\");\n                \n                // key meme\n                if (sheet.Header.Variant == 1)\n                {\n                    sb.AppendLine($\"  `_Key` INT NOT NULL,\");\n                }\n                else\n                {\n                    sb.AppendLine($\"  `_Key` INT NOT NULL,\");\n                    sb.AppendLine($\"  `_SubKey` INT NOT NULL,\");\n                }\n                \n                // add cols\n                foreach (var column in sheet.Header.Columns)\n                {\n                    var colName = column.Name;\n                    if (string.IsNullOrEmpty(colName))\n                        colName = $\"unk{column.Index}\";\n                    \n                    sb.AppendLine($\"  `{colName}` {GetSqlType(column.Reader.Type)},\");\n                }\n                \n                // primary key\n                if (sheet.Header.Variant == 1)\n                {\n                    sb.AppendLine($\"  PRIMARY KEY (`_Key`)\");\n                }\n                else\n                {\n                    sb.AppendLine($\"  PRIMARY KEY (`_Key`, `_SubKey`)\");\n                }\n            \n                sb.AppendLine(\") COLLATE='utf8mb4_unicode_ci' ENGINE=MyISAM;\");\n                sb.AppendLine();\n                \n                WriteRows(sheet, sb);\n                \n                imports.Add(sb.ToString());\n            }\n            \n            File.WriteAllText(\"schema.sql\", string.Join(Environment.NewLine, imports));\n        }\n    }\n}"
  },
  {
    "path": "SaintCoinach.Cmd/Commands/UiCommand.cs",
    "content": "﻿using System;\nusing System.IO;\nusing System.Threading.Tasks;\nusing Tharga.Console.Commands.Base;\n\n#pragma warning disable CS1998\n\nnamespace SaintCoinach.Cmd.Commands {\n    public class UiCommand : AsyncActionCommandBase {\n        const string UiImagePathFormat = \"ui/icon/{0:D3}000{1}/{2:D6}.tex\";\n        static readonly string[] UiVersions = new string[] {\n            \"\",\n            \"/en\",\n            \"/ja\",\n            \"/fr\",\n            \"/de\",\n            \"/hq\",\n            \"/chs\"\n        };\n\n        private ARealmReversed _Realm;\n\n        public UiCommand(ARealmReversed realm)\n            : base(\"ui\", \"Export all, a single, or a range of UI icons.\") {\n            _Realm = realm;\n        }\n\n        public override async Task InvokeAsync(string[] paramList) {\n            var min = 0;\n            var max = 999999;\n\n            if (paramList.Length != 0) {\n                if (paramList.Length == 1) {\n                    if (int.TryParse(paramList[0], out var parsed))\n                        min = max = parsed;\n                    else {\n                        OutputError(\"Failed to parse parameters.\");\n                        return;\n                    }\n                } else if (paramList.Length == 2) {\n                    if (!int.TryParse(paramList[0], out min) || !int.TryParse(paramList[1], out max)) {\n                        OutputError(\"Failed to parse parameters.\");\n                        return;\n                    }\n\n                    if (max < min) {\n                        OutputError(\"Invalid parameters.\");\n                        return;\n                    }\n                } else {\n                    OutputError(\"Failed to parse parameters.\");\n                    return;\n                }\n            }\n\n            var count = 0;\n            for (int i = min; i <= max; ++i) {\n                try {\n                    count += Process(i);\n                } catch (Exception e) {\n                    OutputError($\"{i:D6}: {e.Message}\");\n                }\n            }\n            OutputInformation($\"{count} images processed\");\n        }\n\n        private int Process(int i) {\n            var count = 0;\n            foreach (var v in UiVersions) {\n                if (Process(i, v))\n                    ++count;\n            }\n            return count;\n        }\n        private bool Process(int i, string version) {\n            var filePath = string.Format(UiImagePathFormat, i / 1000, version, i);\n\n            if (_Realm.Packs.TryGetFile(filePath, out var file)) {\n                if (file is Imaging.ImageFile imgFile) {\n                    var img = imgFile.GetImage();\n\n                    var target = new FileInfo(Path.Combine(_Realm.GameVersion, file.Path));\n                    if (!target.Directory.Exists)\n                        target.Directory.Create();\n                    var pngPath = target.FullName.Substring(0, target.FullName.Length - target.Extension.Length) + \".png\";\n                    img.Save(pngPath);\n\n                    return true;\n                } else {\n                    OutputError($\"{filePath} is not an image.\");\n                }\n            }\n            return false;\n        }\n    }\n}\n"
  },
  {
    "path": "SaintCoinach.Cmd/ExdHelper.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Globalization;\nusing System.IO;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace SaintCoinach.Cmd {\n    using Ex;\n    using Xiv;\n\n    static class ExdHelper {\n        static CultureInfo _culture = new CultureInfo(\"en-US\", false);\n\n        public static void SaveAsCsv(Ex.Relational.IRelationalSheet sheet, Language language, string path, bool writeRaw) {\n            using (var s = new StreamWriter(path, false, Encoding.UTF8)) {\n                var indexLine = new StringBuilder(\"key\");\n                var nameLine = new StringBuilder(\"#\");\n                var typeLine = new StringBuilder(\"int32\");\n\n                var colIndices = new List<int>();\n                foreach (var col in sheet.Header.Columns) {\n                    indexLine.AppendFormat(\",{0}\", col.Index);\n                    nameLine.AppendFormat(\",{0}\", col.Name);\n                    typeLine.AppendFormat(\",{0}\", col.ValueType);\n\n                    colIndices.Add(col.Index);\n                }\n\n                s.WriteLine(indexLine);\n                s.WriteLine(nameLine);\n                s.WriteLine(typeLine);\n\n                ExdHelper.WriteRows(s, sheet, language, colIndices, writeRaw);\n            }\n        }\n\n        public static void WriteRows(StreamWriter s, ISheet sheet, Language language, IEnumerable<int> colIndices, bool writeRaw) {\n            if (sheet.Header.Variant == 1)\n                WriteRowsCore(s, sheet.Cast<Ex.IRow>(), language, colIndices, writeRaw, WriteRowKey);\n            else {\n                var rows = sheet.Cast<XivRow>().Select(_ => (Ex.Variant2.DataRow)_.SourceRow);\n                foreach (var parentRow in rows.OrderBy(_ => _.Key))\n                    WriteRowsCore(s, parentRow.SubRows, language, colIndices, writeRaw, WriteSubRowKey);\n            }\n        }\n\n        static void WriteRowsCore(StreamWriter s, IEnumerable<Ex.IRow> rows, Language language, IEnumerable<int> colIndices, bool writeRaw, Action<StreamWriter, Ex.IRow> writeKey) {\n            foreach (var row in rows.OrderBy(_ => _.Key)) {\n                var useRow = row;\n\n                if (useRow is IXivRow)\n                    useRow = ((IXivRow)row).SourceRow;\n                var multiRow = useRow as IMultiRow;\n\n                writeKey(s, useRow);\n                foreach (var col in colIndices) {\n                    object v;\n\n                    if (language == Language.None || multiRow == null)\n                        v = writeRaw ? useRow.GetRaw(col) : useRow[col];\n                    else\n                        v = writeRaw ? multiRow.GetRaw(col, language) : multiRow[col, language];\n\n                    s.Write(\",\");\n                    if (v == null)\n                        continue;\n                    else if (IsUnescaped(v))\n                        s.Write(string.Format(_culture, \"{0}\", v));\n                    else\n                        s.Write(\"\\\"{0}\\\"\", v.ToString().Replace(\"\\\"\", \"\\\"\\\"\"));\n                }\n                s.WriteLine();\n\n                s.Flush();\n            }\n        }\n\n        static void WriteRowKey(StreamWriter s, Ex.IRow row) {\n            s.Write(row.Key);\n        }\n\n        static void WriteSubRowKey(StreamWriter s, Ex.IRow row) {\n            var subRow = (Ex.Variant2.SubRow)row;\n            s.Write(subRow.FullKey);\n        }\n\n        static bool IsUnescaped(object self) {\n            return (self is Boolean\n                || self is Byte\n                || self is SByte\n                || self is Int16\n                || self is Int32\n                || self is Int64\n                || self is UInt16\n                || self is UInt32\n                || self is UInt64\n                || self is Single\n                || self is Double);\n        }\n    }\n}\n"
  },
  {
    "path": "SaintCoinach.Cmd/Program.cs",
    "content": "﻿using System;\nusing System.Linq;\nusing Tharga.Console;\nusing Tharga.Console.Commands;\nusing Tharga.Console.Commands.Base;\n\nnamespace SaintCoinach.Cmd {\n    class ConsoleProgressReporter : IProgress<Ex.Relational.Update.UpdateProgress> {\n\n        #region IProgress<UpdateProgress> Members\n\n        public void Report(Ex.Relational.Update.UpdateProgress value) {\n            Console.WriteLine(value);\n        }\n\n        #endregion\n    }\n    class Program {\n        private static void Main(string[] args) {\n            var dataPath = Properties.Settings.Default.DataPath;\n\n            if (args.Length > 0) {\n                dataPath = args[0];\n                args = args.Skip(1).ToArray();\n            }\n            if (string.IsNullOrWhiteSpace(dataPath))\n                dataPath = SearchForDataPaths().FirstOrDefault(p => System.IO.Directory.Exists(p));\n            if (string.IsNullOrWhiteSpace(dataPath) || !System.IO.Directory.Exists(dataPath)) {\n                Console.WriteLine($\"Need data!  The path '{dataPath}' doesn't exist.\");\n                Console.WriteLine(\"Press any key to exit...\");\n                Console.ReadKey();\n                return;\n            }\n\n            var realm = new ARealmReversed(dataPath, @\"SaintCoinach.History.zip\", Ex.Language.English, @\"app_data.sqlite\");\n            realm.Packs.GetPack(new IO.PackIdentifier(\"exd\", IO.PackIdentifier.DefaultExpansion, 0)).KeepInMemory = true;\n\n            Console.WriteLine(\"Game version: {0}\", realm.GameVersion);\n            Console.WriteLine(\"Definition version: {0}\", realm.DefinitionVersion);\n            \n            if (!realm.IsCurrentVersion) {\n                Console.Write(\"Update is available, perform update (Y/n)? \");\n                var updateQuery = Console.ReadLine();\n                if (string.IsNullOrEmpty(updateQuery) || string.Equals(\"y\", updateQuery, StringComparison.OrdinalIgnoreCase)) {\n                    var stopWatch = new System.Diagnostics.Stopwatch();\n                    stopWatch.Start();\n                    realm.Update(true, new ConsoleProgressReporter());\n                    stopWatch.Stop();\n                    Console.WriteLine(stopWatch.Elapsed);\n                } else\n                    Console.WriteLine(\"Skipping update\");\n            }\n            \n            var cns = new Tharga.Console.Consoles.ClientConsole();\n            var cmd = new RootCommand(cns);\n\n            Setup(cmd, realm);\n\n            (new CommandEngine(cmd)).Start(args);\n        }\n\n        static void Setup(RootCommand rootCmd, ARealmReversed realm) {\n            var assembly = typeof(Program).Assembly;\n            foreach (var t in assembly.GetTypes().Where(t => typeof(AsyncActionCommandBase).IsAssignableFrom(t)))\n            {\n                var cons = t.GetConstructor(new[] { typeof(ARealmReversed) });\n                rootCmd.RegisterCommand((AsyncActionCommandBase)cons.Invoke(new[] { realm }));\n            }\n        }\n\n        static string[] SearchForDataPaths() {\n            const string gameFolder = \"FINAL FANTASY XIV - A Realm Reborn\";\n\n            string programDir;\n            if (Environment.Is64BitProcess)\n                programDir = Environment.GetFolderPath(Environment.SpecialFolder.ProgramFilesX86);\n            else\n                programDir = Environment.GetFolderPath(Environment.SpecialFolder.ProgramFiles);\n\n            return new string[] {\n                System.IO.Path.Combine(programDir, \"SquareEnix\", gameFolder),\n                System.IO.Path.Combine(@\"D:\\Games\\SteamApps\\common\", gameFolder)\n            };\n        }\n    }\n}\n"
  },
  {
    "path": "SaintCoinach.Cmd/Properties/Settings.Designer.cs",
    "content": "﻿//------------------------------------------------------------------------------\n// <auto-generated>\n//     This code was generated by a tool.\n//     Runtime Version:4.0.30319.42000\n//\n//     Changes to this file may cause incorrect behavior and will be lost if\n//     the code is regenerated.\n// </auto-generated>\n//------------------------------------------------------------------------------\n\nnamespace SaintCoinach.Cmd.Properties {\n    \n    \n    [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]\n    [global::System.CodeDom.Compiler.GeneratedCodeAttribute(\"Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator\", \"17.7.0.0\")]\n    public sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase {\n        \n        private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings())));\n        \n        public static Settings Default {\n            get {\n                return defaultInstance;\n            }\n        }\n        \n        [global::System.Configuration.ApplicationScopedSettingAttribute()]\n        [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]\n        [global::System.Configuration.DefaultSettingValueAttribute(\"\")]\n        public string DataPath {\n            get {\n                return ((string)(this[\"DataPath\"]));\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "SaintCoinach.Cmd/Properties/Settings.settings",
    "content": "﻿<?xml version='1.0' encoding='utf-8'?>\n<SettingsFile xmlns=\"http://schemas.microsoft.com/VisualStudio/2004/01/settings\" CurrentProfile=\"(Default)\" GeneratedClassNamespace=\"SaintCoinach.Cmd.Properties\" GeneratedClassName=\"Settings\">\n  <Profiles />\n  <Settings>\n    <Setting Name=\"DataPath\" Type=\"System.String\" Scope=\"Application\">\n      <Value Profile=\"(Default)\" />\n    </Setting>\n  </Settings>\n</SettingsFile>"
  },
  {
    "path": "SaintCoinach.Cmd/SaintCoinach.Cmd.csproj",
    "content": "﻿<Project Sdk=\"Microsoft.NET.Sdk\">\n  <PropertyGroup>\n    <ProjectGuid>{957699E4-8310-4D1E-BA83-13B7A4932807}</ProjectGuid>\n    <OutputType>Exe</OutputType>\n    <TargetFramework>net7.0</TargetFramework>\n    <AssemblyTitle>SaintCoinach.Cmd</AssemblyTitle>\n    <Product>SaintCoinach.Cmd</Product>\n    <Copyright>Copyright © Rogueadyn 2014</Copyright>\n    <AssemblyVersion>0.1</AssemblyVersion>\n    <FileVersion>0.1</FileVersion>\n    <OutputPath>bin\\$(Configuration)\\</OutputPath>\n  </PropertyGroup>\n  <PropertyGroup Condition=\" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' \">\n    <DebugType>full</DebugType>\n  </PropertyGroup>\n  <PropertyGroup Condition=\" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' \">\n    <DebugType>pdbonly</DebugType>\n  </PropertyGroup>\n  <PropertyGroup>\n    <ApplicationIcon>Cmd.ico</ApplicationIcon>\n  </PropertyGroup>\n  <ItemGroup>\n    <PackageReference Include=\"EntityFramework\" Version=\"6.4.4\" />\n    <PackageReference Include=\"Newtonsoft.Json\" Version=\"13.0.3\" />\n    <PackageReference Include=\"Tharga.Console\" Version=\"3.3.2\" />\n  </ItemGroup>\n  <ItemGroup>\n    <Content Include=\"Cmd.ico\" />\n  </ItemGroup>\n  <ItemGroup>\n    <ProjectReference Include=\"..\\SaintCoinach\\SaintCoinach.csproj\" />\n  </ItemGroup>\n  <ItemGroup>\n    <Compile Update=\"Properties\\Settings.Designer.cs\">\n      <DesignTimeSharedInput>True</DesignTimeSharedInput>\n      <AutoGen>True</AutoGen>\n      <DependentUpon>Settings.settings</DependentUpon>\n    </Compile>\n  </ItemGroup>\n  <ItemGroup>\n    <None Update=\"Properties\\Settings.settings\">\n      <Generator>PublicSettingsSingleFileGenerator</Generator>\n      <LastGenOutput>Settings.Designer.cs</LastGenOutput>\n    </None>\n  </ItemGroup>\n</Project>"
  },
  {
    "path": "SaintCoinach.Graphics.Viewer/AnimatedModel.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace SaintCoinach.Graphics.Viewer {\n    using SharpDX;\n    using Viewer;\n\n    public class AnimatedModel : Viewer.Content.ContentModel {\n        #region Fields\n        private AnimationPlayer _AnimationPlayer;\n        private int[] _BoneMap;\n        private Matrix[] _InvertedReferencePose;\n        private Matrix[] _AnimationPose;\n        #endregion\n\n        #region Properties\n        public Skeleton Skeleton { get; private set; }\n        public bool DisplayReferenceJoints { get; set; }\n        public bool DisplayAnimationJoints { get; set; }\n        public AnimationPlayer AnimationPlayer { get { return _AnimationPlayer; } }\n        #endregion\n\n        #region Constructor\n        public AnimatedModel(Engine engine, Skeleton skeleton, ModelVariantIdentifier variant, ModelFile file) : this(engine, skeleton, variant, file.GetModelDefinition(), ModelQuality.High) { }\n        public AnimatedModel(Engine engine, Skeleton skeleton, ModelVariantIdentifier variant, ModelFile file, ModelQuality quality) : this(engine, skeleton, variant, file.GetModelDefinition(), quality) { }\n        public AnimatedModel(Engine engine, Skeleton skeleton, ModelVariantIdentifier variant, ModelDefinition definition) : this(engine, skeleton, variant, definition, ModelQuality.High) { }\n        public AnimatedModel(Engine engine, Skeleton skeleton, ModelVariantIdentifier variant, ModelDefinition definition, ModelQuality quality)\n            : base(engine, variant, definition, quality) {\n\n            Skeleton = skeleton;\n            _AnimationPlayer = new AnimationPlayer();\n\n            var nameMap = new Dictionary<string, int>();\n            for (var i = 0; i < Skeleton.BoneNames.Length; ++i)\n                nameMap.Add(Skeleton.BoneNames[i], i);\n            _BoneMap = Definition.BoneNames.Select(n => nameMap[n]).ToArray();\n            _InvertedReferencePose = Skeleton.ReferencePose.Select(_ => Matrix.Invert(_)).ToArray();\n        }\n        #endregion\n\n        public override void Update(EngineTime engineTime) {\n            if (AnimationPlayer.Animation != null) {\n                AnimationPlayer.Update(engineTime);\n\n                _AnimationPose = _AnimationPlayer.GetPose();\n\n                for(var i = 0; i < JointMatrixArray.Length; ++i) {\n                    var skeletonBoneIndex = _BoneMap[i];\n\n                    var invRef = _InvertedReferencePose[skeletonBoneIndex];\n                    var pose = _AnimationPose[skeletonBoneIndex];\n\n                    JointMatrixArray[i] = invRef * pose;\n                }\n            } else {\n                for (var i = 0; i < JointMatrixArray.Length; ++i)\n                    JointMatrixArray[i] = Matrix.Identity;\n            }\n\n            base.Update(engineTime);\n        }\n\n        public override void Draw(EngineTime time, ref Matrix world, ref Matrix view, ref Matrix projection) {\n            base.Draw(time, ref world, ref view, ref projection);\n\n            if (AnimationPlayer.Animation != null) {\n                if (DisplayAnimationJoints) {\n                    foreach (var i in _BoneMap) {\n                        var m = _AnimationPose[i];\n                        var w = Matrix.Scaling(0.025f) * m * world;\n                        Engine.Cube.Draw(time, ref w, ref view, ref projection);\n                    }\n                }\n            }\n            if (DisplayReferenceJoints) {\n                foreach (var i in _BoneMap) {\n                    var m = Skeleton.ReferencePose[i];\n                    var w = Matrix.Scaling(0.025f) * m * world;\n                    Engine.Cube.Draw(time, ref w, ref view, ref projection);\n                }\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "SaintCoinach.Graphics.Viewer/Animation.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Runtime.InteropServices;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace SaintCoinach.Graphics.Viewer {\n    using SharpDX;\n    using Interop;\n\n    public class Animation {\n        #region Interop\n        static class Interop {\n            [DllImport(\"hkAnimationInterop.dll\", CallingConvention = CallingConvention.Cdecl)]\n            public static extern float getDuration(IntPtr ptr);\n            [DllImport(\"hkAnimationInterop.dll\", CallingConvention = CallingConvention.Cdecl)]\n            public static extern float getFrameDuration(IntPtr ptr);\n            [DllImport(\"hkAnimationInterop.dll\", CallingConvention = CallingConvention.Cdecl)]\n            public static extern int getNumFrames(IntPtr ptr);\n            [DllImport(\"hkAnimationInterop.dll\", CallingConvention = CallingConvention.Cdecl)]\n            public static extern int getPose(IntPtr ptr, float timestamp, [In, Out] InteropTransform[] output);\n        }\n        #endregion\n\n        #region Fields\n        private IntPtr _UnmanagedPtr;\n        #endregion\n\n        #region Properties\n        public AnimationContainer Container { get; private set; }\n        public float Duration { get; private set; }\n        public float FrameDuration { get; private set; }\n        public int FrameCount { get; private set; }\n        public string Name { get; private set; }\n        #endregion\n\n        #region Constructor\n        internal Animation(AnimationContainer container, IntPtr ptr, string name) {\n            Container = container;\n            _UnmanagedPtr = ptr;\n            Name = name;\n\n            Duration = HavokInterop.Execute(() => Interop.getDuration(_UnmanagedPtr));\n            FrameCount = HavokInterop.Execute(() => Interop.getNumFrames(_UnmanagedPtr));\n            FrameDuration = Duration / FrameCount;\n        }\n        #endregion\n\n        #region Get\n        public Matrix[] GetPose(float timestamp) {\n            var transforms = new InteropTransform[Container.Skeleton.BoneCount];\n            HavokInterop.Execute(() => Interop.getPose(_UnmanagedPtr, timestamp, transforms));\n            return transforms.Select(t => t.ToTransformationMatrix()).ToArray();\n        }\n        #endregion\n    }\n}\n"
  },
  {
    "path": "SaintCoinach.Graphics.Viewer/AnimationContainer.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Runtime.InteropServices;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace SaintCoinach.Graphics.Viewer {\n    using SharpDX;\n    using Interop;\n\n    public class AnimationContainer : IDisposable {\n        #region Interop\n        static class Interop {\n            [DllImport(\"hkAnimationInterop.dll\", CallingConvention = CallingConvention.Cdecl)]\n            public static extern IntPtr loadAnimationContainer(IntPtr skeleton, byte[] animationData, int animationSize);\n\n            [DllImport(\"hkAnimationInterop.dll\", CallingConvention = CallingConvention.Cdecl)]\n            public static extern void unloadAnimationContainer(IntPtr ptr);\n\n            [DllImport(\"hkAnimationInterop.dll\", CallingConvention = CallingConvention.Cdecl)]\n            public static extern int getNumAnimations(IntPtr ptr);\n\n            [DllImport(\"hkAnimationInterop.dll\", CallingConvention = CallingConvention.Cdecl)]\n            public static extern IntPtr getAnimation(IntPtr ptr, int index);\n        }\n        #endregion\n\n        #region Fields\n        private IntPtr _UnmanagedPtr;\n        private Dictionary<string, int> _AnimationNameMap;\n        private Dictionary<int, Animation> _Animations = new Dictionary<int, Animation>();\n        #endregion\n\n        #region Properties\n        public Skeleton Skeleton { get; private set; }\n        public PapFile File { get; private set; }\n        public IEnumerable<string> AnimationNames { get { return _AnimationNameMap.Keys; } }\n        public int AnimationCount { get { return File.Header.AnimationCount; } }\n        #endregion\n\n        #region Constructor\n        public AnimationContainer(Skeleton skeleton, PapFile file) {\n            Skeleton = skeleton;\n            File = file;\n\n            _AnimationNameMap = file.Animations.ToDictionary(_ => _.Name, _ => _.Index);\n            _UnmanagedPtr = HavokInterop.Execute(() => Interop.loadAnimationContainer(skeleton._UnmanagedPtr, file.HavokData, file.HavokData.Length));\n\n            var numAnim = HavokInterop.Execute(() => Interop.getNumAnimations(_UnmanagedPtr));\n            if (AnimationCount != numAnim)\n                throw new System.IO.InvalidDataException();\n        }\n        #endregion\n\n        #region Things\n        public Animation Get(string name) {\n            return Get(_AnimationNameMap[name]);\n        }\n        public Animation Get(int index) {\n            Animation anim;\n            if (!_Animations.TryGetValue(index, out anim))\n                _Animations.Add(index, anim = new Animation(this, HavokInterop.Execute(() => Interop.getAnimation(_UnmanagedPtr, index)), _AnimationNameMap.Where(_ => _.Value == index).Select(_ => _.Key).FirstOrDefault()));\n            return anim;\n        }\n        #endregion\n\n        #region IDisposable Support\n        private bool _IsDisposed = false; // To detect redundant calls\n\n        protected virtual void Dispose(bool disposing) {\n            if (!_IsDisposed) {\n                if (_UnmanagedPtr != IntPtr.Zero)\n                    HavokInterop.Execute(() => Interop.unloadAnimationContainer(_UnmanagedPtr));\n                _UnmanagedPtr = IntPtr.Zero;\n\n                _IsDisposed = true;\n            }\n        }\n\n        ~AnimationContainer() {\n            // Do not change this code. Put cleanup code in Dispose(bool disposing) above.\n            Dispose(false);\n        }\n\n        // This code added to correctly implement the disposable pattern.\n        public void Dispose() {\n            // Do not change this code. Put cleanup code in Dispose(bool disposing) above.\n            Dispose(true);\n\n            GC.SuppressFinalize(this);\n        }\n        #endregion\n    }\n}\n"
  },
  {
    "path": "SaintCoinach.Graphics.Viewer/AnimationPlayer.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace SaintCoinach.Graphics.Viewer {\n    using SharpDX;\n    using Viewer;\n\n    public class AnimationPlayer : IUpdateableComponent {\n        #region Fields\n        private double _PlaybackSpeed = 1f;\n\n        private bool _IsAnimating;\n        private bool _IsEnabled = true;\n        private bool _IsLooping = true;\n\n        private Animation _Animation;\n\n        private double _CurrentPlaybackPosition = 0f;\n\n        private bool _IsDirty = true;\n        private Matrix[] _BoneTransformationMatrices;\n        #endregion\n\n        #region Properties\n        public double PlaybackSpeed { get { return _PlaybackSpeed; } set { _PlaybackSpeed = value; } }\n        public bool IsLooping { get { return _IsLooping; } set { _IsLooping = value; _IsAnimating = true; } }\n        public Animation Animation {\n            get { return _Animation; }\n            set {\n                _Animation = value;\n                Reset();\n            }\n        }\n        public bool IsAnimating { get { return _IsAnimating; } }\n        public double CurrentPlaybackPosition { get { return _CurrentPlaybackPosition; } set { _CurrentPlaybackPosition = value; _IsAnimating = true; _IsDirty = true; } }\n        #endregion\n\n        #region Constructor\n        #endregion\n\n        #region Update\n        public Matrix[] GetPose() {\n            if (Animation == null)\n                return new Matrix[0];\n\n            if (_IsDirty) {\n                _BoneTransformationMatrices = Animation.GetPose((float)CurrentPlaybackPosition);\n                _IsDirty = false;\n            }\n\n            return _BoneTransformationMatrices;\n        }\n        public void Reset() {\n            _IsAnimating = true;\n            CurrentPlaybackPosition = 0f;\n        }\n\n        public bool IsEnabled { get { return _IsEnabled; } set { _IsEnabled = value; } }\n\n        public void Update(EngineTime engineTime) {\n            if (!IsEnabled || !IsAnimating || Animation == null || Math.Abs(PlaybackSpeed) <= float.Epsilon)\n                return;\n\n            _IsDirty = true;\n            _CurrentPlaybackPosition += engineTime.ElapsedTime.TotalSeconds * PlaybackSpeed;\n            if (IsLooping) {\n                while (_CurrentPlaybackPosition < 0)\n                    _CurrentPlaybackPosition += Animation.Duration;\n                while (_CurrentPlaybackPosition > Animation.Duration)\n                    _CurrentPlaybackPosition -= Animation.Duration;\n            } else if (_CurrentPlaybackPosition <= 0 || _CurrentPlaybackPosition > Animation.Duration)\n                _IsAnimating = false;\n        }\n        #endregion\n    }\n}\n"
  },
  {
    "path": "SaintCoinach.Graphics.Viewer/Camera.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nusing Keys = System.Windows.Forms.Keys;\n\nnamespace SaintCoinach.Graphics.Viewer {\n    using SharpDX;\n\n    public class Camera : IUpdateableComponent {\n        #region Fields\n        private Vector3 _CameraPosition = Vector3.UnitX;\n        private Vector3 _Up = Vector3.Up;\n\n        private float _Yaw = 0;\n        private float _Pitch = 0;\n        private Matrix _Projection;\n        private Matrix _View;\n        private float _FoV = 0.9f;\n        private float _Speed = 1.0f;\n\n        const float RotationSpeed = (float)(Math.PI / 2f);\n        const float MoveSpeed = 20.0f;\n        const float MouseRotationSpeedYaw = RotationSpeed / 500f;\n        const float MouseRotationSpeedPitch = RotationSpeed / 300f;\n\n        private Engine _Engine;\n\n        private MouseState _PreviousMouseState;\n        private MouseState _CurrentMouseState;\n        #endregion\n\n        #region Properties\n        public Matrix Projection { get { return _Projection; } }\n        public Matrix View { get { return _View; } }\n        public Vector3 CameraPosition {\n            get { return _CameraPosition; }\n            set { _CameraPosition = value; }\n        }\n        public float Yaw {\n            get { return _Yaw; }\n            set { _Yaw = value; }\n        }\n        public float Pitch {\n            get { return _Pitch; }\n            set { _Pitch = value; }\n        }\n        public float FoV {\n            get { return _FoV; }\n            set { _FoV = value; }\n        }\n        #endregion\n\n        #region Constructor\n        public Camera(Engine engine) {\n            _Engine = engine;\n\n            Reset();\n        }\n        #endregion\n\n        #region Control\n        public void Reset() {\n            _CameraPosition = Vector3.Zero + 2f * Vector3.BackwardRH + 1f * Vector3.Up;\n            _Yaw = 0;\n            _Pitch = 0;\n\n            UpdateViewMatrix();\n        }\n        public Matrix GetRotation() {\n            return Matrix.RotationYawPitchRoll(_Yaw, _Pitch, 0);\n        }\n        private void UpdateViewMatrix() {\n            var rotation = GetRotation();\n\n            Vector3 cameraOriginalTarget = new Vector3(0, 0, -1);\n            Vector3 cameraRotatedTarget = (Vector3)Vector3.Transform(cameraOriginalTarget, rotation);\n            Vector3 cameraFinalTarget = _CameraPosition + cameraRotatedTarget;\n\n            Vector3 cameraOriginalUpVector = new Vector3(0, 1, 0);\n            Vector3 cameraRotatedUpVector = (Vector3)Vector3.Transform(cameraOriginalUpVector, rotation);\n\n            _View = Matrix.LookAtRH(_CameraPosition, cameraFinalTarget, cameraRotatedUpVector);\n        }\n        public void AddToCameraPosition(Vector3 vectorToAdd) {\n            var rotation = GetRotation();\n\n            Vector3 rotatedVector = (Vector3)Vector3.Transform(vectorToAdd, rotation);\n            _CameraPosition += MoveSpeed * rotatedVector;\n        }\n        #endregion\n\n        #region Visibility test\n        public bool Contains(BoundingBox bbox) {\n            var frustum = new BoundingFrustum(_View * _Projection);\n            return frustum.Contains(bbox) != ContainmentType.Disjoint;\n        }\n        public bool IsVisible(BoundingBox bounds) {\n            return IsVisible(bounds.Minimum) || IsVisible(bounds.Maximum);\n        }\n        public bool IsVisible(Vector3 point) {\n            return false;\n        }\n        public bool IsVisible(Vector4 point) {\n            return IsVisible(new Vector3(point.X, point.Y, point.Z));\n        }\n        #endregion\n\n        private bool _IsEnabled = true;\n        public bool IsEnabled { get { return _IsEnabled; } set { _IsEnabled = value; } }\n\n        public void Update(EngineTime time) {\n            _PreviousMouseState = _CurrentMouseState;\n            _CurrentMouseState = _Engine.Mouse.GetState();\n\n            var amount = (float)(time.ElapsedTime.TotalMilliseconds / 2000f);\n            Vector3 moveVector = new Vector3(0, 0, 0);\n            var aspectRatio = (float)(_Engine.ViewportSize.Width / (float)_Engine.ViewportSize.Height);\n\n            if (_Engine.IsActive) {\n                var modFactor = _Speed;\n\n\n                if (_Engine.Keyboard.IsKeyDown(Keys.OemCloseBrackets)) {\n                    _Speed -= 1f;\n                    _Speed = _Speed < 0f ? 2f : _Speed;\n                }\n                if (_Engine.Keyboard.IsKeyDown(Keys.OemOpenBrackets)) {\n                    _Speed += 1f;\n                }\n\n                if (_Engine.Keyboard.IsKeyDown(Keys.Space))\n                    modFactor *= 2f;\n                if (_Engine.Keyboard.IsKeyDown(Keys.ShiftKey))\n                    amount *= modFactor;\n                if (_Engine.Keyboard.IsKeyDown(Keys.ControlKey))\n                    amount /= modFactor;\n\n                if (_Engine.Keyboard.IsKeyDown(Keys.W))\n                    moveVector += new Vector3(0, 0, -1);\n                if (_Engine.Keyboard.IsKeyDown(Keys.S))\n                    moveVector += new Vector3(0, 0, 1);\n                if (_Engine.Keyboard.IsKeyDown(Keys.D))\n                    moveVector += new Vector3(1, 0, 0);\n                if (_Engine.Keyboard.IsKeyDown(Keys.A))\n                    moveVector += new Vector3(-1, 0, 0);\n                if (_Engine.Keyboard.IsKeyDown(Keys.Q))\n                    moveVector += new Vector3(0, 1, 0);\n                if (_Engine.Keyboard.IsKeyDown(Keys.Z))\n                    moveVector += new Vector3(0, -1, 0);\n                if (_Engine.Keyboard.IsKeyDown(Keys.R))\n                    Reset();\n\n                if (_Engine.Keyboard.IsKeyDown(Keys.Left))\n                    _Yaw += RotationSpeed * amount * 2;\n                if (_Engine.Keyboard.IsKeyDown(Keys.Right))\n                    _Yaw -= RotationSpeed * amount * 2;\n\n                if (_Engine.Keyboard.IsKeyDown(Keys.Up))\n                    _Pitch += RotationSpeed * amount * 2;\n                if (_Engine.Keyboard.IsKeyDown(Keys.Down))\n                    _Pitch -= RotationSpeed * amount * 2;\n\n                if (_CurrentMouseState.LeftButton) {\n                    if (_CurrentMouseState.RightButton)\n                        moveVector += new Vector3(0, 0, -1);\n                    var mouseMove = _CurrentMouseState.AbsolutePosition - _PreviousMouseState.AbsolutePosition;\n                    _Yaw -= mouseMove.X * MouseRotationSpeedYaw;\n                    _Pitch -= mouseMove.Y * MouseRotationSpeedPitch;\n                }\n\n                AddToCameraPosition(moveVector * amount);\n            }\n\n            UpdateViewMatrix();\n\n            _Projection = Matrix.PerspectiveFovRH(FoV, aspectRatio, 0.1f, 10000.0f);\n        }\n    }\n}\n"
  },
  {
    "path": "SaintCoinach.Graphics.Viewer/Component.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace SaintCoinach.Graphics.Viewer {\n    public abstract class Component : IUpdateableComponent, IContentComponent {\n\n        #region IContentComponent Members\n\n        public bool IsLoaded {\n            get;\n            protected set;\n        }\n\n        public virtual void LoadContent() {\n            IsLoaded = true;\n        }\n\n        public virtual void UnloadContent() {\n            IsLoaded = false;\n        }\n\n        #endregion\n\n        #region IUpdateableComponent Members\n\n        private bool _IsEnabled = true;\n        public bool IsEnabled { get { return _IsEnabled; } set { _IsEnabled = value; } }\n\n        public virtual void Update(EngineTime engineTime) {\n\n        }\n\n        #endregion\n    }\n}\n"
  },
  {
    "path": "SaintCoinach.Graphics.Viewer/ComponentContainer.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace SaintCoinach.Graphics.Viewer {\n    public class ComponentContainer : System.Collections.ObjectModel.Collection<IComponent>, IDrawable3DComponent, IUpdateableComponent, IContentComponent {\n        #region IDrawable3DComponent Members\n\n        private bool _IsVisible = true;\n        public bool IsVisible { get { return _IsVisible; } set { _IsVisible = value; } }\n\n        public void Draw(EngineTime time, ref SharpDX.Matrix world, ref SharpDX.Matrix view, ref SharpDX.Matrix projection) {\n            foreach (var component in this.OfType<IDrawable3DComponent>().Where(c => c.IsVisible).ToArray())\n                component.Draw(time, ref world, ref view, ref projection);\n        }\n\n        #endregion\n\n        #region IUpdateableComponent Members\n\n        private bool _IsEnabled = true;\n        public bool IsEnabled { get { return _IsEnabled; } set { _IsEnabled = value; } }\n\n        public void Update(EngineTime engineTime) {\n            foreach (var component in this.OfType<IUpdateableComponent>().Where(c => c.IsEnabled).ToArray())\n                component.Update(engineTime);\n        }\n\n        #endregion\n\n        #region IContentComponent Members\n\n        public bool IsLoaded {\n            get;\n            private set;\n        }\n\n        public void LoadContent() {\n            IsLoaded = true;\n            foreach (var component in this.OfType<IContentComponent>().Where(c => !c.IsLoaded).ToArray())\n                component.LoadContent();\n        }\n\n        public void UnloadContent() {\n            IsLoaded = false;\n            foreach (var component in this.OfType<IContentComponent>().Where(c => c.IsLoaded).ToArray())\n                component.LoadContent();\n        }\n\n        #endregion\n\n        #region Overrides\n        protected override void InsertItem(int index, IComponent item) {\n            base.InsertItem(index, item);\n\n            var asContent = item as IContentComponent;\n            if (asContent != null && this.IsLoaded && !asContent.IsLoaded)\n                asContent.LoadContent();\n        }\n        protected override void SetItem(int index, IComponent item) {\n            base.SetItem(index, item);\n\n            var asContent = item as IContentComponent;\n            if (asContent != null && this.IsLoaded && !asContent.IsLoaded)\n                asContent.LoadContent();\n        }\n        #endregion\n    }\n}\n"
  },
  {
    "path": "SaintCoinach.Graphics.Viewer/Content/BgColorChangeMaterial.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace SaintCoinach.Graphics.Viewer.Content {\n    using SharpDX;\n    using SharpDX.Direct3D11;\n\n    public class BgColorChangeMaterial : MaterialBase {\n        public const string ColorParameterKey = \"BgColorChangeColor\";\n        public static Vector4 DefaultColor = new Vector4(228 / 255f, 223 / 255f, 208 / 208f, 1f);\n\n        #region Param map\n        [Flags]\n        public enum BgColorChangeParameter : int {\n            None = 0,\n            ColorMap0 = 1 << 0,\n            SpecularMap0 = 1 << 1,\n            NormalMap0 = 1 << 2,\n        }\n\n        private static readonly IDictionary<uint, BgColorChangeParameter> CharacterParameterMap = new Dictionary<uint, BgColorChangeParameter> {\n            // { 0xF0BAD919, \"g_CameraParameter\" },\n            // { 0xC7DB2357, \"g_InstancingData\" },\n            // { 0x64D12851, \"g_MaterialParameter\" },\n            // { 0xEC4CCAA5, \"g_InstanceData\" },\n            // { 0xCA0A5505, \"g_RoofParameter\" },\n            // { 0xA9442826, \"g_CommonParameter\" },\n            // { 0xA296769F, \"g_AmbientParam\" },\n            // { 0xC8755A51, \"g_BGAmbientParameter\" },\n            // { 0x4D9F3ACD, \"g_WetnessParameter\" },\n            { 0xAAB4D9E9, BgColorChangeParameter.NormalMap0 }, // { 0xAAB4D9E9, \"g_SamplerNormalMap0\" },\n            { 0x1BBC2F12, BgColorChangeParameter.SpecularMap0 }, // { 0x1BBC2F12, \"g_SamplerSpecularMap0\" },\n            { 0x1E6FEF9C, BgColorChangeParameter.ColorMap0 }, // { 0x1E6FEF9C, \"g_SamplerColorMap0\" },\n            // { 0xBA8D7950, \"g_SamplerFresnel\" },\n            // { 0xEBBB29BD, \"g_SamplerGBuffer\" },\n            // { 0x23D0F850, \"g_SamplerLightDiffuse\" },\n            // { 0x6C19ACA4, \"g_SamplerLightSpecular\" },\n            // { 0x32667BD7, \"g_SamplerOcclusion\" },\n            // { 0x9F467267, \"g_SamplerDither\" },\n            };\n        #endregion\n\n        #region Properties\n        public ShaderResourceView ColorMap0 { get; private set; }\n        public ShaderResourceView SpecularMap0 { get; private set; }\n        public ShaderResourceView NormalMap0 { get; private set; }\n\n        public BgColorChangeParameter Navin { get; private set; }\n\n        public new Effects.BgColorChangeEffect Effect { get { return (Effects.BgColorChangeEffect)base.Effect; } }\n        #endregion\n\n        #region Constructor\n        public BgColorChangeMaterial(Engine engine, Material baseMaterial)\n            : base(engine, baseMaterial) {\n\n            Navin = BgColorChangeParameter.None;\n\n            foreach (var param in baseMaterial.TextureParameters) {\n                var tex = baseMaterial.TexturesFiles[param.TextureIndex];\n                BgColorChangeParameter currentParam;\n                if (!CharacterParameterMap.TryGetValue(param.ParameterId, out currentParam)) {\n                    System.Diagnostics.Trace.WriteLine(string.Format(\"Unknown character parameter {0:X8} for texture '{1}' in material '{2}'.\", param.ParameterId, tex.Path, baseMaterial.File.Path));\n                    continue;\n                }\n\n                Navin |= currentParam;\n                switch (currentParam) {\n                    case BgColorChangeParameter.ColorMap0:\n                        ColorMap0 = Engine.TextureFactory.GetResource(tex);\n                        break;\n                    case BgColorChangeParameter.SpecularMap0:\n                        SpecularMap0 = Engine.TextureFactory.GetResource(tex);\n                        break;\n                    case BgColorChangeParameter.NormalMap0:\n                        NormalMap0 = Engine.TextureFactory.GetResource(tex);\n                        break;\n                }\n            }\n\n            CurrentTechniqueName = \"BgColorChange\"; // TechniqueNames[Navin];\n        }\n        #endregion\n\n        #region Apply\n        public override void Apply(Data.ParametersBase parameters) {\n            Effect.ColorMap0 = this.ColorMap0;\n            Effect.SpecularMap0 = this.SpecularMap0;\n            Effect.NormalMap0 = this.NormalMap0;\n            Effect.Color = parameters.GetValueOrDefault(ColorParameterKey, DefaultColor);\n        }\n        #endregion\n    }\n}\n"
  },
  {
    "path": "SaintCoinach.Graphics.Viewer/Content/BgMaterial.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace SaintCoinach.Graphics.Viewer.Content {\n    using SharpDX;\n    using SharpDX.Direct3D11;\n\n    public class BgMaterial : MaterialBase {\n\n        #region Param map\n        [Flags]\n        public enum BgParameter : int {\n            None = 0,\n            Diffuse0 = 1 << 0,\n            Specular0 = 1 << 1,\n            Normal0 = 1 << 2,\n            Diffuse1 = 1 << 3,\n            Specular1 = 1 << 4,\n            Normal1 = 1 << 5,\n        }\n\n        private static readonly IDictionary<BgParameter, string> TechniqueNames = new Dictionary<BgParameter, string> {\n            { BgParameter.Normal0 | BgParameter.Diffuse0, \"Single\" },\n            { BgParameter.Normal0 | BgParameter.Diffuse0 | BgParameter.Specular0, \"SingleSpecular\" },\n            { BgParameter.Normal0 | BgParameter.Diffuse0 | BgParameter.Diffuse1 | BgParameter.Normal1, \"Dual\" },\n            { BgParameter.Normal0 | BgParameter.Diffuse0 | BgParameter.Specular0 | BgParameter.Diffuse1 | BgParameter.Normal1 | BgParameter.Specular1, \"DualSpecular\" },\n        };\n\n        private static readonly IDictionary<uint, BgParameter> CharacterParameterMap = new Dictionary<uint, BgParameter> {\n                // { 0xF0BAD919, \"g_CameraParameter\" },\n                // { 0xC7DB2357, \"g_InstancingData\" },\n                // { 0x64D12851, \"g_MaterialParameter\" },\n                // { 0xEC4CCAA5, \"g_InstanceData\" },\n                // { 0x1FDE0907, \"g_PlateEadg\" },\n                // { 0xA9EAE61E, \"g_EadgBias\" },\n                // { 0x73B31397, \"g_WavingParam\" },\n                // { 0xCA0A5505, \"g_RoofParameter\" },\n                // { 0x4D9F3ACD, \"g_WetnessParameter\" },\n                // { 0xA9442826, \"g_CommonParameter\" },\n                // { 0xA296769F, \"g_AmbientParam\" },\n                // { 0xC8755A51, \"g_BGAmbientParameter\" },\n                { 0x1BBC2F12, BgParameter.Specular0 }, // { 0x1BBC2F12, \"g_SamplerSpecularMap0\" },\n                { 0x1E6FEF9C, BgParameter.Diffuse0 }, // { 0x1E6FEF9C, \"g_SamplerColorMap0\" },\n                // { 0xBA8D7950, \"g_SamplerFresnel\" },\n                // { 0xEBBB29BD, \"g_SamplerGBuffer\" },\n                // { 0x23D0F850, \"g_SamplerLightDiffuse\" },\n                // { 0x6C19ACA4, \"g_SamplerLightSpecular\" },\n                // { 0x32667BD7, \"g_SamplerOcclusion\" },\n                // { 0x9F467267, \"g_SamplerDither\" },\n                { 0xAAB4D9E9, BgParameter.Normal0 }, // { 0xAAB4D9E9, \"g_SamplerNormalMap0\" },\n                { 0x6CBB1F84, BgParameter.Specular1 }, // { 0x6CBB1F84, \"g_SamplerSpecularMap1\" },\n                { 0x6968DF0A, BgParameter.Diffuse1 }, // { 0x6968DF0A, \"g_SamplerColorMap1\" },\n                { 0xDDB3E97F, BgParameter.Normal1 }, // { 0xDDB3E97F, \"g_SamplerNormalMap1\" },\n            };\n        #endregion\n\n        #region Properties\n        public ShaderResourceView Diffuse0 { get; private set; }\n        public ShaderResourceView Specular0 { get; private set; }\n        public ShaderResourceView Normal0 { get; private set; }\n        public ShaderResourceView Diffuse1 { get; private set; }\n        public ShaderResourceView Specular1 { get; private set; }\n        public ShaderResourceView Normal1 { get; private set; }\n\n        public BgParameter Navin { get; private set; }\n\n        public new Effects.BgEffect Effect { get { return (Effects.BgEffect)base.Effect; } }\n        #endregion\n\n        #region Constructor\n        public BgMaterial(Engine engine, Material baseMaterial)\n            : base(engine, baseMaterial) {\n\n            Navin = BgParameter.None;\n\n            foreach (var param in baseMaterial.TextureParameters) {\n                var tex = baseMaterial.TexturesFiles[param.TextureIndex];\n                BgParameter currentParam;\n                if (!CharacterParameterMap.TryGetValue(param.ParameterId, out currentParam)) {\n                    System.Diagnostics.Trace.WriteLine(string.Format(\"Unknown character parameter {0:X8} for texture '{1}' in material '{2}'.\", param.ParameterId, tex.Path, baseMaterial.File.Path));\n                    continue;\n                }\n\n                Navin |= currentParam;\n                switch (currentParam) {\n                    case BgParameter.Diffuse0:\n                        Diffuse0 = Engine.TextureFactory.GetResource(tex);\n                        break;\n                    case BgParameter.Specular0:\n                        Specular0 = Engine.TextureFactory.GetResource(tex);\n                        break;\n                    case BgParameter.Normal0:\n                        Normal0 = Engine.TextureFactory.GetResource(tex);\n                        break;\n                    case BgParameter.Diffuse1:\n                        Diffuse1 = Engine.TextureFactory.GetResource(tex);\n                        break;\n                    case BgParameter.Specular1:\n                        Specular1 = Engine.TextureFactory.GetResource(tex);\n                        break;\n                    case BgParameter.Normal1:\n                        Normal1 = Engine.TextureFactory.GetResource(tex);\n                        break;\n                }\n            }\n\n            CurrentTechniqueName = TechniqueNames[Navin];\n        }\n        #endregion\n\n        #region Apply\n        public override void Apply(Data.ParametersBase parameters) {\n            Effect.Diffuse0 = this.Diffuse0;\n            Effect.Specular0 = this.Specular0;\n            Effect.Normal0 = this.Normal0;\n            Effect.Diffuse1 = this.Diffuse1;\n            Effect.Specular1 = this.Specular1;\n            Effect.Normal1 = this.Normal1;\n        }\n        #endregion\n    }\n}\n"
  },
  {
    "path": "SaintCoinach.Graphics.Viewer/Content/CharacterMaterial.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace SaintCoinach.Graphics.Viewer.Content {\n    using SharpDX;\n    using SharpDX.Direct3D11;\n\n    public class CharacterMaterial : MaterialBase {\n\n        #region Param map\n        [Flags]\n        public enum CharacterParameter : int {\n            None        = 0,\n            Diffuse     = 1 << 0,\n            Specular    = 1 << 1,\n            Normal      = 1 << 2,\n            Mask        = 1 << 3,\n            Table       = 1 << 4,\n        }\n        \n        private static readonly IDictionary<CharacterParameter, string> TechniqueNames = new Dictionary<CharacterParameter, string> {\n            { CharacterParameter.Normal | CharacterParameter.Diffuse, \"Diffuse\" },\n            { CharacterParameter.Normal | CharacterParameter.Diffuse | CharacterParameter.Specular, \"DiffuseSpecular\" },\n            { CharacterParameter.Normal | CharacterParameter.Diffuse | CharacterParameter.Specular | CharacterParameter.Table, \"DiffuseSpecularTable\" },\n            { CharacterParameter.Normal | CharacterParameter.Diffuse | CharacterParameter.Table, \"DiffuseTable\" },\n            { CharacterParameter.Normal | CharacterParameter.Mask | CharacterParameter.Table, \"MaskTable\" },\n            { CharacterParameter.Normal | CharacterParameter.Mask, \"Mask\" },\n        };\n\n        private static readonly IDictionary<uint, CharacterParameter> CharacterParameterMap = new Dictionary<uint, CharacterParameter> {\n                // { 0xF0BAD919, \"g_CameraParameter\" },\n                // { 0x76BB3DC0, \"g_WorldViewMatrix\" },\n                // { 0x20A30B34, \"g_InstanceParameter\" },\n                // { 0x88AA546A, \"g_JointMatrixArray\" },\n                // { 0x64D12851, \"g_MaterialParameter\" },\n                // { 0x3D086484, \"g_SceneParameter\" },\n                // { 0xA9442826, \"g_CommonParameter\" },\n                // { 0xA296769F, \"g_AmbientParam\" },\n                // { 0x77F6BFB3, \"g_MaterialParameterDynamic\" },\n                // { 0x5B0F708C, \"g_DecalColor\" },\n                // { 0xEF4E7491, \"g_LightDirection\" },\n                { 0x0C5EC1F1, CharacterParameter.Normal }, // { 0x0C5EC1F1, \"g_SamplerNormal\" },\n                // { 0x565F8FD8, \"g_SamplerIndex\" },\n                { 0x2005679F, CharacterParameter.Table }, // { 0x2005679F, \"g_SamplerTable\" },\n                // { 0x92F03E53, \"g_SamplerTileNormal\" },\n                // { 0xEBBB29BD, \"g_SamplerGBuffer\" },\n                // { 0x23D0F850, \"g_SamplerLightDiffuse\" },\n                // { 0x6C19ACA4, \"g_SamplerLightSpecular\" },\n                { 0x8A4E82B6, CharacterParameter.Mask }, // { 0x8A4E82B6, \"g_SamplerMask\" },\n                // { 0x32667BD7, \"g_SamplerOcclusion\" },\n                // { 0x87F6474D, \"g_SamplerReflection\" },\n                // { 0x29156A85, \"g_SamplerTileDiffuse\" },\n                // { 0x9F467267, \"g_SamplerDither\" },\n                { 0x115306BE, CharacterParameter.Diffuse }, // { 0x115306BE, \"g_SamplerDiffuse\" },\n                { 0x2B99E025, CharacterParameter.Specular }, // { 0x2B99E025, \"g_SamplerSpecular\" },\n                // { 0x0237CB94, \"g_SamplerDecal\" },\n            };\n        #endregion\n\n        #region Properties\n        public ShaderResourceView Diffuse { get; private set; }\n        public ShaderResourceView Specular { get; private set; }\n        public ShaderResourceView Normal { get; private set; }\n        public ShaderResourceView Mask { get; private set; }\n        public ShaderResourceView Table { get; private set; }\n\n        public CharacterParameter Navin { get; private set; }\n\n        public new Effects.CharacterEffect Effect { get { return (Effects.CharacterEffect)base.Effect; } }\n        #endregion\n\n        #region Constructor\n        public CharacterMaterial(Engine engine, Material baseMaterial)\n            : base(engine, baseMaterial) {\n\n            Navin = CharacterParameter.None;\n\n            foreach (var param in baseMaterial.TextureParameters) {\n                if (param.TextureIndex == byte.MaxValue)\n                    continue;   // TODO: See if this may actually refer to dummy texture (even though others just have a dummy.tex entry)\n                var tex = baseMaterial.TexturesFiles[param.TextureIndex];\n                CharacterParameter currentParam;\n                if (!CharacterParameterMap.TryGetValue(param.ParameterId, out currentParam)) {\n                    System.Diagnostics.Trace.WriteLine(string.Format(\"Unknown character parameter {0:X8} for texture '{1}' in material '{2}'.\", param.ParameterId, tex.Path, baseMaterial.File.Path));\n                    continue;\n                }\n\n                Navin |= currentParam;\n                switch (currentParam) {\n                    case CharacterParameter.Diffuse:\n                        Diffuse = Engine.TextureFactory.GetResource(tex);\n                        break;\n                    case CharacterParameter.Specular:\n                        Specular = Engine.TextureFactory.GetResource(tex);\n                        break;\n                    case CharacterParameter.Normal:\n                        Normal = Engine.TextureFactory.GetResource(tex);\n                        break;\n                    case CharacterParameter.Mask:\n                        Mask = Engine.TextureFactory.GetResource(tex);\n                        break;\n                    case CharacterParameter.Table:\n                        Table = Engine.TextureFactory.GetResource(tex);\n                        break;\n                }\n            }\n\n            CurrentTechniqueName = TechniqueNames[Navin];\n        }\n        #endregion\n\n        #region Apply\n        public override void Apply(Data.ParametersBase parameters) {\n            Effect.Diffuse = this.Diffuse;\n            Effect.Specular = this.Specular;\n            Effect.Normal = this.Normal;\n            Effect.Mask = this.Mask;\n            Effect.Table = this.Table;\n        }\n        #endregion\n    }\n}\n"
  },
  {
    "path": "SaintCoinach.Graphics.Viewer/Content/ContentMesh.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace SaintCoinach.Graphics.Viewer.Content {\n    using SharpDX;\n    using SharpDX.Direct3D11;\n\n    public class ContentMesh : Drawable3DComponent {\n        #region Properties\n        public ContentModel Model { get; private set; }\n        public ModelVariantIdentifier Variant { get; private set; }\n        public PrimitiveMesh Primitive { get; private set; }\n        public Matrix Transformation { get; set; }\n        public ContentMeshPart[] Parts { get; private set; }\n        public MaterialBase Material { get; private set; }\n        #endregion\n\n        #region Constructor\n        public ContentMesh(ContentModel model, PrimitiveMesh primitive, ModelVariantIdentifier variant)\n            : base(model.Engine) {\n            this.Model = model;\n            this.Primitive = primitive;\n            this.Variant = variant;\n            this.Transformation = Matrix.Identity;\n        }\n        #endregion\n\n\n        #region Content\n        public override void LoadContent() {\n            Parts = new ContentMeshPart[Primitive.BaseMesh.Parts.Length];\n            for (var i = 0; i < Primitive.BaseMesh.Parts.Length; ++i)\n                Parts[i] = new ContentMeshPart(this, Primitive.BaseMesh.Parts[i]);\n\n            Material = Engine.MaterialFactory.Get(Primitive.BaseMesh.Material.Get(Variant));\n\n            base.LoadContent();\n        }\n        public override void UnloadContent() {\n            Parts = null;\n            Material = null;\n            base.UnloadContent();\n        }\n        #endregion\n\n        #region Draw\n        public override void Draw(EngineTime time, ref SharpDX.Matrix world, ref SharpDX.Matrix view, ref SharpDX.Matrix projection) {\n            if (Material == null)\n                return;\n            var transformedWorld = Transformation * world;\n\n            var ia = Engine.Device.ImmediateContext.InputAssembler;\n\n            EffectTechnique tech = Material.CurrentTechnique;\n\n            Material.Effect.Apply(ref world, ref view, ref projection);\n            Material.Apply(Model.Parameters);\n            \n            var skinnedEffect = Material.Effect as Effects.SkinnedEffect;\n            if (skinnedEffect != null) {\n                var boneList = Model.Definition.BoneLists[Primitive.BaseMesh.Header.BoneListIndex];\n                Matrix[] jointMatrix = new Matrix[boneList.ActualCount];\n                for (var i = 0; i < boneList.ActualCount; ++i)\n                    jointMatrix[i] = Model.JointMatrixArray[boneList.Bones[i]];\n\n                skinnedEffect.JointMatrixArray = jointMatrix;\n            }\n\n            ia.PrimitiveTopology = SharpDX.Direct3D.PrimitiveTopology.TriangleList;\n            ia.SetVertexBuffers(0, Primitive.VertexBufferBinding);\n            ia.SetIndexBuffer(Primitive.IndexBuffer, SharpDX.DXGI.Format.R16_UInt, 0);\n\n            InputLayout inputLayout = Material.Effect.GetInputLayout(tech.GetPassByIndex(0));\n            ia.InputLayout = inputLayout;\n\n            for (var i = 0; i < tech.Description.PassCount; ++i) {\n                var pass = tech.GetPassByIndex(i);\n\n                pass.Apply(Engine.Device.ImmediateContext);\n\n                if (Parts.Length == 0) {\n                    Engine.Device.ImmediateContext.DrawIndexed(Primitive.BaseMesh.Header.IndexCount, 0, 0);\n                } else {\n                    foreach (var part in Parts)\n                        part.Draw(Engine.Device);\n                }\n            }\n        }\n        #endregion\n    }\n}\n"
  },
  {
    "path": "SaintCoinach.Graphics.Viewer/Content/ContentMeshPart.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace SaintCoinach.Graphics.Viewer.Content {\n    using Device = SharpDX.Direct3D11.Device;\n    public class ContentMeshPart {\n        #region Properties\n        public ContentMesh Mesh { get; private set; }\n        public MeshPart BasePart { get; private set; }\n        #endregion\n\n        #region Constructor\n        public ContentMeshPart(ContentMesh mesh, MeshPart basePart) {\n            this.Mesh = mesh;\n            this.BasePart = basePart;\n        }\n        #endregion\n\n        #region IDrawable3DComponent Members\n\n        private bool? _VisibilityOverride = null;\n        public bool? VisibilityOverride { get { return _VisibilityOverride; } set { _VisibilityOverride = value; } }\n\n        public void Draw(Device device) {\n            uint fullMask = 0;\n            foreach (var attr in BasePart.Attributes)\n                fullMask |= attr.AttributeMask;\n\n            //var fromMask = (this.Mesh.Variant.ImcVariant.PartVisibilityMask & BasePart.Header.VisibilityMask) == BasePart.Header.VisibilityMask;\n            var fromMask = (this.Mesh.Variant.ImcVariant.PartVisibilityMask & fullMask) == fullMask;\n            if (VisibilityOverride.GetValueOrDefault(fromMask))\n                device.ImmediateContext.DrawIndexed(BasePart.IndexCount, BasePart.IndexOffset - Mesh.Primitive.BaseMesh.Header.IndexBufferOffset, 0);\n        }\n\n        #endregion\n    }\n}\n"
  },
  {
    "path": "SaintCoinach.Graphics.Viewer/Content/ContentModel.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace SaintCoinach.Graphics.Viewer.Content {\n    using SharpDX;\n\n    public class ContentModel : Drawable3DComponent {\n        #region Fields\n        private ComponentContainer _MeshContainer = new ComponentContainer();\n        #endregion\n\n        #region Properties\n        public ModelDefinition Definition { get; private set; }\n        public ModelVariantIdentifier Variant { get; private set; }\n        public ModelQuality Quality { get; private set; }\n        public ContentMesh[] Meshes { get; private set; }\n        public Matrix Transformation { get; set; }\n        public PrimitiveModel Primitive { get; private set; }\n        public Data.ParametersBase Parameters { get; set; }\n        public Matrix[] JointMatrixArray { get; private set; }\n        #endregion\n\n        #region Constructor\n        public ContentModel(Engine engine, ModelVariantIdentifier variant, ModelFile file) : this(engine, variant, file.GetModelDefinition(), ModelQuality.High) { }\n        public ContentModel(Engine engine, ModelVariantIdentifier variant, ModelFile file, ModelQuality quality) : this(engine, variant, file.GetModelDefinition(), quality) { }\n        public ContentModel(Engine engine, ModelVariantIdentifier variant, ModelDefinition definition) : this(engine, variant, definition, ModelQuality.High) { }\n        public ContentModel(Engine engine, ModelVariantIdentifier variant, ModelDefinition definition, ModelQuality quality)\n            : base(engine) {\n            this.Definition = definition;\n            this.Quality = quality;\n            this.Variant = variant;\n            this.Transformation = Matrix.Identity;\n\n            Init();\n        }\n        public ContentModel(Engine engine, TransformedModel transformedModel) : this(engine, transformedModel, ModelQuality.High) { }\n        public ContentModel(Engine engine, TransformedModel transformedModel, ModelQuality quality) : base(engine) {\n            this.Parameters = new Data.ParametersBase();\n            this.Definition = transformedModel.Model;\n            this.Quality = Quality;\n            this.Variant = new ModelVariantIdentifier {\n                ImcVariant = ImcVariant.Default,\n                StainKey = null\n            };\n            this.Transformation =\n                Matrix.Scaling(transformedModel.Scale.ToDx())\n                * Matrix.RotationX(transformedModel.Rotation.X)\n                * Matrix.RotationY(transformedModel.Rotation.Y)\n                * Matrix.RotationZ(transformedModel.Rotation.Z)\n                * Matrix.Translation(transformedModel.Translation.ToDx());\n\n            Init();\n        }\n\n        private void Init() {\n            JointMatrixArray = new Matrix[Definition.Bones.Length];\n            for(var i = 0; i < JointMatrixArray.Length; ++i) {\n                var b = Definition.Bones[i];\n                JointMatrixArray[i] = Matrix.Identity;\n            }\n        }\n        #endregion\n\n        #region Content\n        public override void LoadContent() {\n            try {\n                Primitive = Engine.ModelFactory.Get(Definition, Quality);\n                Meshes = new ContentMesh[Primitive.Meshes.Length];\n                for (var i = 0; i < Primitive.Meshes.Length; ++i) {\n                    Meshes[i] = new ContentMesh(this, Primitive.Meshes[i], Variant);\n                    _MeshContainer.Add(Meshes[i]);\n                }\n\n                _MeshContainer.LoadContent();\n                base.LoadContent();\n            }catch(Exception e) {\n                System.Diagnostics.Trace.WriteLine(string.Format(\"Failed to load model '{0}': {1}\", Definition.File.Path, e));\n                IsLoaded = false;\n            }\n        }\n        public override void UnloadContent() {\n            base.UnloadContent();\n            _MeshContainer.UnloadContent();\n            _MeshContainer.Clear();\n            Meshes = null;\n        }\n        #endregion\n\n        #region Draw\n        public override void Draw(EngineTime time, ref SharpDX.Matrix world, ref SharpDX.Matrix view, ref SharpDX.Matrix projection) {\n            if (!IsLoaded)\n                return;\n            var transformedWorld = Transformation * world;\n            _MeshContainer.Draw(time, ref transformedWorld, ref view, ref projection);\n        }\n        #endregion\n\n        #region Update\n        public override void Update(EngineTime engineTime) {\n            _MeshContainer.Update(engineTime);\n            base.Update(engineTime);\n        }\n        #endregion\n    }\n}\n"
  },
  {
    "path": "SaintCoinach.Graphics.Viewer/Content/ContentSgb.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\nusing SharpDX;\n\nnamespace SaintCoinach.Graphics.Viewer.Content {\n    public class ContentSgb : Drawable3DComponent {\n        #region Fields\n        private ComponentContainer _Content = new ComponentContainer();\n        #endregion\n\n        #region Properties\n        public Sgb.SgbFile SgbFile{ get; private set; }\n        public Data.ParametersBase Parameters { get; set; }\n        public Matrix Transformation { get; set; }\n        #endregion\n\n        #region Constructor\n        public ContentSgb(Engine engine, Sgb.SgbFile sgbFile, Data.ParametersBase parameters) :this(engine, sgbFile, parameters, Matrix.Identity) {  }\n        public ContentSgb(Engine engine, Sgb.SgbFile sgbFile):this(engine, sgbFile, null, Matrix.Identity) { }\n        public ContentSgb(Engine engine, Sgb.SgbFile sgbFile, Data.ParametersBase parameters, Matrix EObjTransform) : base(engine) {\n            this.SgbFile = sgbFile;\n            this.Parameters = parameters;\n            this.Transformation = Matrix.Identity;\n\n            // todo: fixme!\n\n            bool LoadModels(Sgb.SgbFile file, Matrix rootTransform, Matrix gimTransform) {\n                if (file == null)\n                    return false;\n\n                foreach (var group in file.Data.OfType<Sgb.SgbGroup>()) {\n                    foreach (var mdl in group.Entries.OfType<Sgb.SgbModelEntry>()) {\n                        var content = new ContentModel(engine, mdl.Model) { Parameters = parameters };\n\n                        if (EObjTransform == Matrix.Identity)\n                            this.Transformation = Matrix.Identity;\n                        content.Transformation = content.Transformation * gimTransform * rootTransform * EObjTransform;\n                        \n                        _Content.Add(content);\n                    }\n                }\n                return true;\n            }\n\n            Matrix CreateMatrix(Vector3 translation, Vector3 rotation, Vector3 scale) {\n                return (Matrix.Scaling(scale.ToDx())\n                    * Matrix.RotationX(rotation.X)\n                    * Matrix.RotationY(rotation.Y)\n                    * Matrix.RotationZ(rotation.Z)\n                    * Matrix.Translation(translation.ToDx()));\n            }\n            void LoadSgbFile(Sgb.SgbFile file) {\n                if (LoadModels(file, Matrix.Identity, Matrix.Identity)) {\n                    foreach (var rootGimGroup in file.Data.OfType<Sgb.SgbGroup>()) {\n                        foreach (var rootGimEntry in rootGimGroup.Entries.OfType<Sgb.SgbGimmickEntry>()) {\n                            var rootGimTransform = CreateMatrix(rootGimEntry.Header.Translation, rootGimEntry.Header.Rotation, rootGimEntry.Header.Scale);\n                            if (LoadModels(rootGimEntry.Gimmick, rootGimTransform, Matrix.Identity)) {\n                                foreach (var subGimGroup in rootGimEntry.Gimmick.Data.OfType<Sgb.SgbGroup>()) {\n                                    foreach (var subGim in subGimGroup.Entries.OfType<Sgb.SgbGimmickEntry>()) {\n                                        var subGimTransform = CreateMatrix(subGim.Header.Translation, subGim.Header.Rotation, subGim.Header.Scale);\n                                        LoadModels(subGim.Gimmick, rootGimTransform, subGimTransform);\n                                    }\n                                }\n                            }\n                        }\n                    }\n                }\n            }\n            LoadSgbFile(sgbFile);\n        }\n        #endregion\n        \n        public override void LoadContent() {\n            _Content.LoadContent();\n            base.LoadContent();\n        }\n        public override void UnloadContent() {\n            _Content.UnloadContent();\n            base.UnloadContent();\n        }\n        public override void Update(EngineTime engineTime) {\n            _Content.Update(engineTime);\n            base.Update(engineTime);\n        }\n        public override void Draw(EngineTime time, ref SharpDX.Matrix world, ref SharpDX.Matrix view, ref SharpDX.Matrix projection) {\n            var adjustedWorld = Transformation * world;\n            _Content.Draw(time, ref adjustedWorld, ref view, ref projection);\n        }\n    }\n}\n"
  },
  {
    "path": "SaintCoinach.Graphics.Viewer/Content/ContentTerritory.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace SaintCoinach.Graphics.Viewer.Content {\n    using SharpDX;\n    using System.Numerics;\n\n    public class ContentTerritory : Drawable3DComponent {\n        #region Fields\n        private ComponentContainer _TerrainContainer = new ComponentContainer();\n        private ComponentContainer _LgbPartsContainer = new ComponentContainer();\n        #endregion\n\n        #region Properties\n        public Territory Territory { get; private set; }\n        #endregion\n\n        #region Constructor\n        public ContentTerritory(Engine engine, Territory territory) : base(engine) {\n            this.Territory = territory;\n\n            if (territory.Terrain != null) {\n                foreach (var part in territory.Terrain.Parts) {\n                    _TerrainContainer.Add(new ContentModel(engine, part));\n                }\n            }\n            foreach (var lgb in territory.LgbFiles) {\n                foreach(var group in lgb.Groups) {\n                    foreach(var part in group.Entries) {\n                        var asMdl = part as Lgb.LgbModelEntry;\n                        var asGim = part as Lgb.LgbGimmickEntry;\n                        var asEobj = part as Lgb.LgbEventObjectEntry;\n\n                        if (asMdl != null && asMdl.Model != null) {\n                            _LgbPartsContainer.Add(new ContentModel(engine, asMdl.Model));\n                        }\n                        if (asGim != null && asGim.Gimmick != null) {\n                            var transform = Matrix.Scaling(asGim.Header.Scale.ToDx())\n                                    * Matrix.RotationX(asGim.Header.Rotation.X)\n                                    * Matrix.RotationY(asGim.Header.Rotation.Y)\n                                    * Matrix.RotationZ(asGim.Header.Rotation.Z)\n                                    * Matrix.Translation(asGim.Header.Translation.ToDx());\n                            _LgbPartsContainer.Add(new ContentSgb(engine, asGim.Gimmick) {\n                                Transformation =\n                                    Matrix.Scaling(asGim.Header.Scale.ToDx())\n                                    * Matrix.RotationX(asGim.Header.Rotation.X)\n                                    * Matrix.RotationY(asGim.Header.Rotation.Y)\n                                    * Matrix.RotationZ(asGim.Header.Rotation.Z)\n                                    * Matrix.Translation(asGim.Header.Translation.ToDx())\n                            });\n                        }\n                        if (asEobj != null && asEobj.Gimmick != null) {\n                            var transformation = Matrix.Scaling(asEobj.Header.Scale.ToDx())\n                                    * Matrix.RotationX(asEobj.Header.Rotation.X)\n                                    * Matrix.RotationY(asEobj.Header.Rotation.Y)\n                                    * Matrix.RotationZ(asEobj.Header.Rotation.Z)\n                                    * Matrix.Translation(asEobj.Header.Translation.ToDx());\n\n                            _LgbPartsContainer.Add(new ContentSgb(engine, asEobj.Gimmick) {\n                                Transformation = transformation\n                            });\n                            foreach (var rootGimGroup in asEobj.Gimmick.Data.OfType<Sgb.SgbGroup>()) {\n                                foreach (var sgb1CEntry in rootGimGroup.Entries.OfType<Sgb.SgbGroup1CEntry>()) {\n                                    var rootGimEntry = sgb1CEntry;\n                                    if (rootGimEntry.Gimmick != null) {\n                                        _LgbPartsContainer.Add(new ContentSgb(engine, sgb1CEntry.Gimmick) {\n                                            Transformation = transformation\n                                        });\n                                        foreach (var subGimGroup in rootGimEntry.Gimmick.Data.OfType<Sgb.SgbGroup>()) {\n                                            foreach (var subGimEntry in subGimGroup.Entries.OfType<Sgb.SgbGimmickEntry>()) {\n                                                _LgbPartsContainer.Add(new ContentSgb(engine, subGimEntry.Gimmick) {\n                                                    Transformation = transformation\n                                                });\n                                            }\n                                        }\n                                    }\n                                }\n                            }\n                        }\n                    }\n                }\n            }\n        }\n        #endregion\n        public override void LoadContent() {\n            _TerrainContainer.LoadContent();\n            _LgbPartsContainer.LoadContent();\n            base.LoadContent();\n        }\n        public override void UnloadContent() {\n            _TerrainContainer.UnloadContent();\n            _LgbPartsContainer.UnloadContent();\n            base.UnloadContent();\n        }\n        public override void Update(EngineTime engineTime) {\n            _TerrainContainer.Update(engineTime);\n            _LgbPartsContainer.Update(engineTime);\n            base.Update(engineTime);\n        }\n        public override void Draw(EngineTime time, ref SharpDX.Matrix world, ref SharpDX.Matrix view, ref SharpDX.Matrix projection) {\n            _TerrainContainer.Draw(time, ref world, ref view, ref projection);\n            _LgbPartsContainer.Draw(time, ref world, ref view, ref projection);\n        }\n    }\n}\n"
  },
  {
    "path": "SaintCoinach.Graphics.Viewer/Content/CrystalMaterial.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace SaintCoinach.Graphics.Viewer.Content {\n    using SharpDX;\n    using SharpDX.Direct3D11;\n\n    public class CrystalMaterial : MaterialBase {\n        public const string ColorParameterKey = \"BgColorChangeColor\";\n        public static Vector4 DefaultColor = new Vector4(228 / 255f, 223 / 255f, 208 / 208f, 1f);\n\n        #region Param map\n        [Flags]\n        public enum CrystalParameter : int {\n            None = 0,\n            ColorMap0 = 1 << 0,\n            SpecularMap0 = 1 << 1,\n            NormalMap0 = 1 << 2,\n            EnvironmentMap = 1 << 3,\n        }\n\n        private static readonly IDictionary<uint, CrystalParameter> CharacterParameterMap = new Dictionary<uint, CrystalParameter> {\n            // { 0xF0BAD919, \"g_CameraParameter\" },\n            // { 0xC7DB2357, \"g_InstancingData\" },\n            // { 0x64D12851, \"g_MaterialParameter\" },\n            // { 0xEC4CCAA5, \"g_InstanceData\" },\n            // { 0xCA0A5505, \"g_RoofParameter\" },\n            // { 0xA9442826, \"g_CommonParameter\" },\n            // { 0xA296769F, \"g_AmbientParam\" },\n            { 0x1BBC2F12, CrystalParameter.SpecularMap0 }, // { 0x1BBC2F12, \"g_SamplerSpecularMap0\" },\n            { 0x1E6FEF9C, CrystalParameter.ColorMap0 }, // { 0x1E6FEF9C, \"g_SamplerColorMap0\" },\n            { 0xF8D7957A, CrystalParameter.EnvironmentMap }, // { 0xF8D7957A, \"g_SamplerEnvMap\" },\n            // { 0xBA8D7950, \"g_SamplerFresnel\" },\n            // { 0xEBBB29BD, \"g_SamplerGBuffer\" },\n            // { 0x23D0F850, \"g_SamplerLightDiffuse\" },\n            // { 0x6C19ACA4, \"g_SamplerLightSpecular\" },\n            { 0xAAB4D9E9, CrystalParameter.NormalMap0 }, // { 0xAAB4D9E9, \"g_SamplerNormalMap0\" },\n            // { 0x9F467267, \"g_SamplerDither\" },\n        };\n        #endregion\n\n        #region Properties\n        public ShaderResourceView ColorMap0 { get; private set; }\n        public ShaderResourceView SpecularMap0 { get; private set; }\n        public ShaderResourceView NormalMap0 { get; private set; }\n        public ShaderResourceView EnvironmentMap { get; private set; }\n\n        public CrystalParameter Navin { get; private set; }\n\n        public new Effects.CrystalEffect Effect { get { return (Effects.CrystalEffect)base.Effect; } }\n        #endregion\n\n        #region Constructor\n        public CrystalMaterial(Engine engine, Material baseMaterial)\n            : base(engine, baseMaterial) {\n\n            Navin = CrystalParameter.None;\n\n            foreach (var param in baseMaterial.TextureParameters) {\n                var tex = baseMaterial.TexturesFiles[param.TextureIndex];\n                CrystalParameter currentParam;\n                if (!CharacterParameterMap.TryGetValue(param.ParameterId, out currentParam)) {\n                    System.Diagnostics.Trace.WriteLine(string.Format(\"Unknown character parameter {0:X8} for texture '{1}' in material '{2}'.\", param.ParameterId, tex.Path, baseMaterial.File.Path));\n                    continue;\n                }\n\n                Navin |= currentParam;\n                switch (currentParam) {\n                    case CrystalParameter.ColorMap0:\n                        ColorMap0 = Engine.TextureFactory.GetResource(tex);\n                        break;\n                    case CrystalParameter.SpecularMap0:\n                        SpecularMap0 = Engine.TextureFactory.GetResource(tex);\n                        break;\n                    case CrystalParameter.NormalMap0:\n                        NormalMap0 = Engine.TextureFactory.GetResource(tex);\n                        break;\n                    case CrystalParameter.EnvironmentMap:\n                        EnvironmentMap = Engine.TextureFactory.GetResource(tex);\n                        break;\n                }\n            }\n\n            CurrentTechniqueName = \"Crystal\"; // TechniqueNames[Navin];\n        }\n        #endregion\n\n        #region Apply\n        public override void Apply(Data.ParametersBase parameters) {\n            Effect.ColorMap0 = this.ColorMap0;\n            Effect.SpecularMap0 = this.SpecularMap0;\n            Effect.NormalMap0 = this.NormalMap0;\n            Effect.EnvironmentMap = this.EnvironmentMap;\n        }\n        #endregion\n    }\n}\n"
  },
  {
    "path": "SaintCoinach.Graphics.Viewer/Content/Cube.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace SaintCoinach.Graphics.Viewer.Content {\n    using SharpDX;\n    using SharpDX.D3DCompiler;\n    using SharpDX.DXGI;\n    using SharpDX.Direct3D11;\n\n    public class Cube : Drawable3DComponent {\n        public Buffer VertexBuffer { get; private set; }\n        public VertexBufferBinding VertexBufferBinding { get; private set; }\n        public Buffer ConstantBuffer { get; private set; }\n        public PixelShader PixelShader { get; private set; }\n        public VertexShader VertexShader { get; private set; }\n        public InputLayout InputLayout { get; private set; }\n\n        public Cube(Engine engine) : base(engine) {\n\n        }\n\n        public override void LoadContent() {\n            VertexBuffer = Buffer.Create(Engine.Device, BindFlags.VertexBuffer, new[]\n                                  {\n                                      new Vector4(-1.0f, -1.0f, -1.0f, 1.0f), new Vector4(1.0f, 0.0f, 0.0f, 1.0f), // Front\n                                      new Vector4(-1.0f,  1.0f, -1.0f, 1.0f), new Vector4(1.0f, 0.0f, 0.0f, 1.0f),\n                                      new Vector4( 1.0f,  1.0f, -1.0f, 1.0f), new Vector4(1.0f, 0.0f, 0.0f, 1.0f),\n                                      new Vector4(-1.0f, -1.0f, -1.0f, 1.0f), new Vector4(1.0f, 0.0f, 0.0f, 1.0f),\n                                      new Vector4( 1.0f,  1.0f, -1.0f, 1.0f), new Vector4(1.0f, 0.0f, 0.0f, 1.0f),\n                                      new Vector4( 1.0f, -1.0f, -1.0f, 1.0f), new Vector4(1.0f, 0.0f, 0.0f, 1.0f),\n\n                                      new Vector4(-1.0f, -1.0f,  1.0f, 1.0f), new Vector4(0.0f, 1.0f, 0.0f, 1.0f), // Back\n                                      new Vector4( 1.0f,  1.0f,  1.0f, 1.0f), new Vector4(0.0f, 1.0f, 0.0f, 1.0f),\n                                      new Vector4(-1.0f,  1.0f,  1.0f, 1.0f), new Vector4(0.0f, 1.0f, 0.0f, 1.0f),\n                                      new Vector4(-1.0f, -1.0f,  1.0f, 1.0f), new Vector4(0.0f, 1.0f, 0.0f, 1.0f),\n                                      new Vector4( 1.0f, -1.0f,  1.0f, 1.0f), new Vector4(0.0f, 1.0f, 0.0f, 1.0f),\n                                      new Vector4( 1.0f,  1.0f,  1.0f, 1.0f), new Vector4(0.0f, 1.0f, 0.0f, 1.0f),\n\n                                      new Vector4(-1.0f, 1.0f, -1.0f,  1.0f), new Vector4(0.0f, 0.0f, 1.0f, 1.0f), // Top\n                                      new Vector4(-1.0f, 1.0f,  1.0f,  1.0f), new Vector4(0.0f, 0.0f, 1.0f, 1.0f),\n                                      new Vector4( 1.0f, 1.0f,  1.0f,  1.0f), new Vector4(0.0f, 0.0f, 1.0f, 1.0f),\n                                      new Vector4(-1.0f, 1.0f, -1.0f,  1.0f), new Vector4(0.0f, 0.0f, 1.0f, 1.0f),\n                                      new Vector4( 1.0f, 1.0f,  1.0f,  1.0f), new Vector4(0.0f, 0.0f, 1.0f, 1.0f),\n                                      new Vector4( 1.0f, 1.0f, -1.0f,  1.0f), new Vector4(0.0f, 0.0f, 1.0f, 1.0f),\n\n                                      new Vector4(-1.0f,-1.0f, -1.0f,  1.0f), new Vector4(1.0f, 1.0f, 0.0f, 1.0f), // Bottom\n                                      new Vector4( 1.0f,-1.0f,  1.0f,  1.0f), new Vector4(1.0f, 1.0f, 0.0f, 1.0f),\n                                      new Vector4(-1.0f,-1.0f,  1.0f,  1.0f), new Vector4(1.0f, 1.0f, 0.0f, 1.0f),\n                                      new Vector4(-1.0f,-1.0f, -1.0f,  1.0f), new Vector4(1.0f, 1.0f, 0.0f, 1.0f),\n                                      new Vector4( 1.0f,-1.0f, -1.0f,  1.0f), new Vector4(1.0f, 1.0f, 0.0f, 1.0f),\n                                      new Vector4( 1.0f,-1.0f,  1.0f,  1.0f), new Vector4(1.0f, 1.0f, 0.0f, 1.0f),\n\n                                      new Vector4(-1.0f, -1.0f, -1.0f, 1.0f), new Vector4(1.0f, 0.0f, 1.0f, 1.0f), // Left\n                                      new Vector4(-1.0f, -1.0f,  1.0f, 1.0f), new Vector4(1.0f, 0.0f, 1.0f, 1.0f),\n                                      new Vector4(-1.0f,  1.0f,  1.0f, 1.0f), new Vector4(1.0f, 0.0f, 1.0f, 1.0f),\n                                      new Vector4(-1.0f, -1.0f, -1.0f, 1.0f), new Vector4(1.0f, 0.0f, 1.0f, 1.0f),\n                                      new Vector4(-1.0f,  1.0f,  1.0f, 1.0f), new Vector4(1.0f, 0.0f, 1.0f, 1.0f),\n                                      new Vector4(-1.0f,  1.0f, -1.0f, 1.0f), new Vector4(1.0f, 0.0f, 1.0f, 1.0f),\n\n                                      new Vector4( 1.0f, -1.0f, -1.0f, 1.0f), new Vector4(0.0f, 1.0f, 1.0f, 1.0f), // Right\n                                      new Vector4( 1.0f,  1.0f,  1.0f, 1.0f), new Vector4(0.0f, 1.0f, 1.0f, 1.0f),\n                                      new Vector4( 1.0f, -1.0f,  1.0f, 1.0f), new Vector4(0.0f, 1.0f, 1.0f, 1.0f),\n                                      new Vector4( 1.0f, -1.0f, -1.0f, 1.0f), new Vector4(0.0f, 1.0f, 1.0f, 1.0f),\n                                      new Vector4( 1.0f,  1.0f, -1.0f, 1.0f), new Vector4(0.0f, 1.0f, 1.0f, 1.0f),\n                                      new Vector4( 1.0f,  1.0f,  1.0f, 1.0f), new Vector4(0.0f, 1.0f, 1.0f, 1.0f),\n                            });\n            VertexBufferBinding = new SharpDX.Direct3D11.VertexBufferBinding(VertexBuffer, Utilities.SizeOf<Vector4>() * 2, 0);\n            ConstantBuffer = new Buffer(Engine.Device, Utilities.SizeOf<Matrix>(), ResourceUsage.Default, BindFlags.ConstantBuffer, CpuAccessFlags.None, ResourceOptionFlags.None, 0);\n\n            // Compile Vertex and Pixel shaders\n            var vertexShaderByteCode = ShaderBytecode.CompileFromFile(\"./Effects/HLSL/BasicEffect.fx\", \"VS\", \"vs_4_0\");\n            VertexShader = new VertexShader(Engine.Device, vertexShaderByteCode);\n\n            var pixelShaderByteCode = ShaderBytecode.CompileFromFile(\"./Effects/HLSL/BasicEffect.fx\", \"PS\", \"ps_4_0\");\n            PixelShader = new PixelShader(Engine.Device, pixelShaderByteCode);\n\n            var signature = ShaderSignature.GetInputSignature(vertexShaderByteCode);\n            // Layout from VertexShader input signature\n            InputLayout = new InputLayout(Engine.Device, signature, new[]\n                    {\n                        new InputElement(\"POSITION\", 0, Format.R32G32B32A32_Float, 0, 0),\n                        new InputElement(\"COLOR\", 0, Format.R32G32B32A32_Float, 16, 0)\n                    });\n\n            base.LoadContent();\n        }\n        public override void UnloadContent() {\n            base.UnloadContent();\n        }\n\n        public override void Draw(EngineTime time, ref SharpDX.Matrix world, ref SharpDX.Matrix view, ref SharpDX.Matrix projection) {\n            var ia = Engine.Device.ImmediateContext.InputAssembler;\n            var vs = Engine.Device.ImmediateContext.VertexShader;\n            var ps = Engine.Device.ImmediateContext.PixelShader;\n\n            var worldViewProj = world * view * projection;\n            worldViewProj.Transpose();\n            Engine.Device.ImmediateContext.UpdateSubresource(ref worldViewProj, ConstantBuffer);\n\n            ia.InputLayout = InputLayout;\n            ia.PrimitiveTopology = SharpDX.Direct3D.PrimitiveTopology.TriangleList;\n            ia.SetVertexBuffers(0, VertexBufferBinding);\n            vs.SetConstantBuffer(0, ConstantBuffer);\n            vs.Set(VertexShader);\n            ps.Set(PixelShader);\n\n            Engine.Device.ImmediateContext.Draw(36, 0);\n        }\n    }\n}\n"
  },
  {
    "path": "SaintCoinach.Graphics.Viewer/Content/HairMaterial.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace SaintCoinach.Graphics.Viewer.Content {\n    using SharpDX;\n    using SharpDX.Direct3D11;\n\n    public class HairMaterial : MaterialBase {\n\n        #region Param map\n        [Flags]\n        public enum HairParameter : int {\n            None = 0,\n            Normal = 1 << 0,\n            Mask = 1 << 1,\n        }\n\n        private static readonly IDictionary<uint, HairParameter> CharacterParameterMap = new Dictionary<uint, HairParameter> {\n                { 0x0C5EC1F1, HairParameter.Normal }, // { 0x0C5EC1F1, \"g_SamplerNormal\" },\n                { 0x8A4E82B6, HairParameter.Mask }, // { 0x8A4E82B6, \"g_SamplerMask\" },\n            };\n        #endregion\n\n        #region Properties\n        public ShaderResourceView Normal { get; private set; }\n        public ShaderResourceView Mask { get; private set; }\n\n        public HairParameter Navin { get; private set; }\n\n        public new Effects.HairEffect Effect { get { return (Effects.HairEffect)base.Effect; } }\n        #endregion\n\n        #region Constructor\n        public HairMaterial(Engine engine, Material baseMaterial)\n            : base(engine, baseMaterial) {\n\n            Navin = HairParameter.None;\n\n            foreach (var param in baseMaterial.TextureParameters) {\n                var tex = baseMaterial.TexturesFiles[param.TextureIndex];\n                HairParameter currentParam;\n                if (!CharacterParameterMap.TryGetValue(param.ParameterId, out currentParam)) {\n                    System.Diagnostics.Trace.WriteLine(string.Format(\"Unknown character parameter {0:X8} for texture '{1}' in material '{2}'.\", param.ParameterId, tex.Path, baseMaterial.File.Path));\n                    continue;\n                }\n\n                Navin |= currentParam;\n                switch (currentParam) {\n                    case HairParameter.Normal:\n                        Normal = Engine.TextureFactory.GetResource(tex);\n                        break;\n                    case HairParameter.Mask:\n                        Mask = Engine.TextureFactory.GetResource(tex);\n                        break;\n                }\n            }\n\n            CurrentTechniqueName = \"Hair\";\n        }\n        #endregion\n\n        #region Apply\n        public override void Apply(Data.ParametersBase parameters) {\n            Effect.Normal = this.Normal;\n            Effect.Mask = this.Mask;\n\n            Effect.CustomizeParameters = parameters.GetValueOrDefault(Data.CustomizeParameters.CustomizeParametersKey, Data.CustomizeParameters.Default);\n        }\n        #endregion\n    }\n}\n"
  },
  {
    "path": "SaintCoinach.Graphics.Viewer/Content/IrisMaterial.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace SaintCoinach.Graphics.Viewer.Content {\n    using SharpDX;\n    using SharpDX.Direct3D11;\n\n    public class IrisMaterial : MaterialBase {\n\n        #region Param map\n        [Flags]\n        public enum IrisParameter : int {\n            None = 0,\n            Normal = 1 << 0,\n            Mask = 1 << 1,\n        }\n\n        private static readonly IDictionary<uint, IrisParameter> CharacterParameterMap = new Dictionary<uint, IrisParameter> {\n                { 0x0C5EC1F1, IrisParameter.Normal }, // { 0x0C5EC1F1, \"g_SamplerNormal\" },\n                // { 0xFEA0F3D2, \"g_SamplerCatchlight\" },\n                // { 0xEBBB29BD, \"g_SamplerGBuffer\" },\n                // { 0x23D0F850, \"g_SamplerLightDiffuse\" },\n                // { 0x6C19ACA4, \"g_SamplerLightSpecular\" },\n                { 0x8A4E82B6, IrisParameter.Mask }, // { 0x8A4E82B6, \"g_SamplerMask\" },\n                // { 0x32667BD7, \"g_SamplerOcclusion\" },\n                // { 0x87F6474D, \"g_SamplerReflection\" },\n                // { 0x9F467267, \"g_SamplerDither\" },\n            };\n        #endregion\n\n        #region Properties\n        public ShaderResourceView Normal { get; private set; }\n        public ShaderResourceView Mask { get; private set; }\n\n        public IrisParameter Navin { get; private set; }\n\n        public new Effects.IrisEffect Effect { get { return (Effects.IrisEffect)base.Effect; } }\n        #endregion\n\n        #region Constructor\n        public IrisMaterial(Engine engine, Material baseMaterial)\n            : base(engine, baseMaterial) {\n\n            Navin = IrisParameter.None;\n\n            foreach (var param in baseMaterial.TextureParameters) {\n                var tex = baseMaterial.TexturesFiles[param.TextureIndex];\n                IrisParameter currentParam;\n                if (!CharacterParameterMap.TryGetValue(param.ParameterId, out currentParam)) {\n                    System.Diagnostics.Trace.WriteLine(string.Format(\"Unknown character parameter {0:X8} for texture '{1}' in material '{2}'.\", param.ParameterId, tex.Path, baseMaterial.File.Path));\n                    continue;\n                }\n\n                Navin |= currentParam;\n                switch (currentParam) {\n                    case IrisParameter.Normal:\n                        Normal = Engine.TextureFactory.GetResource(tex);\n                        break;\n                    case IrisParameter.Mask:\n                        Mask = Engine.TextureFactory.GetResource(tex);\n                        break;\n                }\n            }\n\n            CurrentTechniqueName = \"Iris\";\n        }\n        #endregion\n\n        #region Apply\n        public override void Apply(Data.ParametersBase parameters) {\n            Effect.Normal = this.Normal;\n            Effect.Mask = this.Mask;\n\n            Effect.CustomizeParameters = parameters.GetValueOrDefault(Data.CustomizeParameters.CustomizeParametersKey, Data.CustomizeParameters.Default);\n        }\n        #endregion\n    }\n}\n"
  },
  {
    "path": "SaintCoinach.Graphics.Viewer/Content/MaterialBase.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace SaintCoinach.Graphics.Viewer.Content {\n    using SharpDX;\n    using SharpDX.Direct3D11;\n\n    public abstract class MaterialBase {\n        #region Properties\n        private EffectTechnique _CurrentTechnique;\n        private string _CurrentTechniqueName;\n\n        public Engine Engine { get; private set; }\n        public Material BaseMaterial { get; private set; }\n        public Effects.EffectBase Effect { get; private set; }\n        public EffectTechnique CurrentTechnique {\n            get { return _CurrentTechnique; }\n            set {\n                _CurrentTechnique = value;\n                _CurrentTechniqueName = value.Description.Name;\n            }\n        }\n        public string CurrentTechniqueName {\n            get { return _CurrentTechniqueName; }\n            set {\n                _CurrentTechniqueName = value;\n                _CurrentTechnique = Effect.GetTechniqueByName(value);\n            }\n        }\n        #endregion\n\n        #region Constructor\n        protected MaterialBase(Engine engine, Material baseMaterial) {\n            this.Engine = engine;\n            this.BaseMaterial = baseMaterial;\n\n            this.Effect = engine.EffectFactory.GetEffect(BaseMaterial.Shader);\n        }\n        #endregion\n\n        #region Apply\n        public abstract void Apply(Data.ParametersBase parameters);\n        #endregion\n    }\n}\n"
  },
  {
    "path": "SaintCoinach.Graphics.Viewer/Content/PrimitiveMesh.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\n\nnamespace SaintCoinach.Graphics.Viewer.Content {\n    using SharpDX;\n    using SharpDX.Direct3D11;\n    using Device = SharpDX.Direct3D11.Device;\n    using Buffer = SharpDX.Direct3D11.Buffer;\n\n    public class PrimitiveMesh : IDisposable {\n        #region Properties\n        public Mesh BaseMesh { get; private set; }\n        public Buffer IndexBuffer { get; private set; }\n        public Buffer VertexBuffer { get; private set; }\n        public VertexBufferBinding VertexBufferBinding { get; private set; }\n        #endregion\n\n        #region Constructor\n        public PrimitiveMesh(Device device, Mesh baseMesh) {\n            BaseMesh = baseMesh;\n\n            var vert3d = new Vertex3D[baseMesh.Vertices.Length];\n            for (var i = 0; i < vert3d.Length; ++i)\n                vert3d[i] = new Vertex3D(baseMesh.Vertices[i]);\n\n            IndexBuffer = Buffer.Create<ushort>(device, BindFlags.IndexBuffer, baseMesh.Indices);\n            VertexBuffer = Buffer.Create<Vertex3D>(device, BindFlags.VertexBuffer, vert3d);\n            VertexBufferBinding = new VertexBufferBinding(VertexBuffer, VertexBuffer.Description.SizeInBytes / baseMesh.Vertices.Length, 0);\n        }\n        #endregion\n\n        #region IDisposable Members\n\n        public void Dispose() {\n            if (IndexBuffer != null)\n                IndexBuffer.Dispose();\n            IndexBuffer = null;\n            if (VertexBuffer != null)\n                VertexBuffer.Dispose();\n            VertexBuffer = null;\n        }\n\n        #endregion\n    }\n}\n"
  },
  {
    "path": "SaintCoinach.Graphics.Viewer/Content/PrimitiveModel.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nusing Device = SharpDX.Direct3D11.Device;\n\nnamespace SaintCoinach.Graphics.Viewer.Content {\n    using SharpDX;\n\n    public class PrimitiveModel : IDisposable {\n        #region Properties\n        public Model BaseModel { get; private set; }\n        public PrimitiveMesh[] Meshes { get; private set; }\n        #endregion\n\n        #region Constructor\n        public PrimitiveModel(Device device, Model baseModel) {\n            this.BaseModel = baseModel;\n            this.Meshes = BaseModel.Meshes.Select(m => new PrimitiveMesh(device, m)).ToArray();\n        }\n        #endregion\n\n        #region IDisposable Members\n\n        public void Dispose() {\n            if (Meshes != null) {\n                foreach (var m in Meshes)\n                    m.Dispose();\n            }\n            Meshes = null;\n        }\n\n        #endregion\n    }\n}\n"
  },
  {
    "path": "SaintCoinach.Graphics.Viewer/Content/SkinMaterial.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace SaintCoinach.Graphics.Viewer.Content {\n    using SharpDX;\n    using SharpDX.Direct3D11;\n\n    public class SkinMaterial : MaterialBase {\n\n        #region Param map\n        [Flags]\n        public enum SkinParameter : int {\n            None = 0,\n            Diffuse = 1 << 0,\n            Normal = 1 << 1,\n            Mask = 1 << 2,\n        }\n\n        private static readonly IDictionary<uint, SkinParameter> CharacterParameterMap = new Dictionary<uint, SkinParameter> {\n                { 0x0C5EC1F1, SkinParameter.Normal }, // { 0x0C5EC1F1, \"g_SamplerNormal\" },\n                { 0x8A4E82B6, SkinParameter.Mask }, // { 0x8A4E82B6, \"g_SamplerMask\" },\n                { 0x115306BE, SkinParameter.Diffuse }, // { 0x115306BE, \"g_SamplerDiffuse\" },\n            };\n        #endregion\n\n        #region Properties\n        public ShaderResourceView Diffuse { get; private set; }\n        public ShaderResourceView Normal { get; private set; }\n        public ShaderResourceView Mask { get; private set; }\n\n        public SkinParameter Navin { get; private set; }\n\n        public new Effects.SkinEffect Effect { get { return (Effects.SkinEffect)base.Effect; } }\n        #endregion\n\n        #region Constructor\n        public SkinMaterial(Engine engine, Material baseMaterial)\n            : base(engine, baseMaterial) {\n\n            Navin = SkinParameter.None;\n\n            foreach (var param in baseMaterial.TextureParameters) {\n                var tex = baseMaterial.TexturesFiles[param.TextureIndex];\n                SkinParameter currentParam;\n                if (!CharacterParameterMap.TryGetValue(param.ParameterId, out currentParam)) {\n                    System.Diagnostics.Trace.WriteLine(string.Format(\"Unknown character parameter {0:X8} for texture '{1}' in material '{2}'.\", param.ParameterId, tex.Path, baseMaterial.File.Path));\n                    continue;\n                }\n\n                Navin |= currentParam;\n                switch (currentParam) {\n                    case SkinParameter.Diffuse:\n                        Diffuse = Engine.TextureFactory.GetResource(tex);\n                        break;\n                    case SkinParameter.Normal:\n                        Normal = Engine.TextureFactory.GetResource(tex);\n                        break;\n                    case SkinParameter.Mask:\n                        Mask = Engine.TextureFactory.GetResource(tex);\n                        break;\n                }\n            }\n\n            CurrentTechniqueName = \"Skin\";\n        }\n        #endregion\n\n        #region Apply\n        public override void Apply(Data.ParametersBase parameters) {\n            Effect.Normal = this.Normal;\n            Effect.Mask = this.Mask;\n\n            Effect.CustomizeParameters = parameters.GetValueOrDefault(Data.CustomizeParameters.CustomizeParametersKey, Data.CustomizeParameters.Default);\n        }\n        #endregion\n    }\n}\n"
  },
  {
    "path": "SaintCoinach.Graphics.Viewer/Data/CustomizeParameters.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Runtime.InteropServices;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace SaintCoinach.Graphics.Viewer.Data {\n    using SharpDX;\n\n    public class CustomizeParameters : ParametersBase {\n        public static readonly CustomizeParameters Default = new CustomizeParameters();\n\n        public const string CustomizeParametersKey = \"CustomizeParameters\";\n\n        public const string SkinColorKey = \"SkinColor\";\n        public const string LipColorKey = \"LipColor\";\n        public const string HairColorKey = \"HairColor\";\n        public const string MeshColorKey = \"MeshColor\";\n        public const string LeftEyeColorKey = \"LeftEyeColor\";\n        public const string RightEyeColorKey = \"RightEyeColor\";\n\n        public static Vector4 DefaultSkinColor = new Vector4(210 / 255f, 180 / 255f, 140 / 255f, 1);\n        public static Vector4 DefaultLipColor = new Vector4(1, 0, 0, .25f);\n        public static Vector3 DefaultHairColor = new Vector3(165 / 255f, 42 / 255f, 42 / 255f);\n        public static Vector3 DefaultMeshColor = new Vector3(121 / 255f, 68 / 255f, 59 / 255f);\n        public static Vector3 DefaultLeftEyeColor = new Vector3(165 / 255f, 42 / 255f, 42 / 255f);\n        public static Vector3 DefaultRightEyeColor = new Vector3(121 / 255f, 68 / 255f, 59 / 255f);\n\n        public Vector4 SkinColor {\n            get { return this.GetValueOrDefault(SkinColorKey, DefaultSkinColor); }\n            set { Set(SkinColorKey, value); }\n        }\n        public Vector4 LipColor {\n            get { return this.GetValueOrDefault(LipColorKey, DefaultLipColor); }\n            set { Set(LipColorKey, value); }\n        }\n        public Vector3 HairColor {\n            get { return this.GetValueOrDefault(HairColorKey, DefaultHairColor); }\n            set { Set(HairColorKey, value); }\n        }\n        public Vector3 MeshColor {\n            get { return this.GetValueOrDefault(MeshColorKey, DefaultMeshColor); }\n            set { Set(MeshColorKey, value); }\n        }\n        public Vector3 LeftEyeColor {\n            get { return this.GetValueOrDefault(LeftEyeColorKey, DefaultLeftEyeColor); }\n            set { Set(LeftEyeColorKey, value); }\n        }\n        public Vector3 RightEyeColor {\n            get { return this.GetValueOrDefault(RightEyeColorKey, DefaultRightEyeColor); }\n            set { Set(RightEyeColorKey, value); }\n        }\n\n        #region Constructor\n        public CustomizeParameters() { Set(CustomizeParametersKey, this); }\n        public CustomizeParameters(ParametersBase copyFrom) : base(copyFrom) {\n            Set(CustomizeParametersKey, this);\n        }\n        #endregion\n\n        public override object Clone() {\n            return new CustomizeParameters(this);\n        }\n    }\n}\n"
  },
  {
    "path": "SaintCoinach.Graphics.Viewer/Data/ParametersBase.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace SaintCoinach.Graphics.Viewer.Data {\n    public class ParametersBase : ICloneable {\n        #region Fields\n        private Dictionary<string, object> _Entries = new Dictionary<string, object>();\n        #endregion\n\n        #region Constructor\n        public ParametersBase() { }\n        public ParametersBase(ParametersBase copyFrom) {\n            this._Entries = new Dictionary<string, object>(copyFrom._Entries);\n        }\n        #endregion\n\n        #region Get/Set\n        public void Set<T>(string key, T value) {\n            if (_Entries.ContainsKey(key))\n                _Entries[key] = value;\n            else\n                _Entries.Add(key, value);\n        }\n        public bool Contains(string key) {\n            return _Entries.ContainsKey(key);\n        }\n        public bool Contains<T>(string key) {\n            return _Entries.ContainsKey(key) && _Entries[key] is T;\n        }\n        public bool Remove(string key) {\n            return _Entries.Remove(key);\n        }\n        public T Get<T>(string key) {\n            return (T)_Entries[key];\n        }\n        public bool TryGetValue<T>(string key, out T value) {\n            value = default(T);\n            object asObj;\n            if (!_Entries.TryGetValue(key, out asObj) || !(asObj is T))\n                return false;\n            value = (T)asObj;\n            return true;\n        }\n        #endregion\n\n        #region Clone\n        public virtual object Clone() {\n            return new ParametersBase(this);\n        }\n        #endregion\n    }\n}\n"
  },
  {
    "path": "SaintCoinach.Graphics.Viewer/Drawable3DComponent.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace SaintCoinach.Graphics.Viewer {\n    public abstract class Drawable3DComponent : Component, IDrawable3DComponent {\n        #region Fields\n        private Engine _Engine;\n        #endregion\n\n        #region Properties\n        public Engine Engine { get { return _Engine; } }\n        #endregion\n\n        #region Constructor\n        protected Drawable3DComponent(Engine engine) {\n            _Engine = engine;\n        }\n        #endregion\n\n        #region IDrawable3DComponent Members\n\n        private bool _IsVisible = true;\n        public bool IsVisible { get { return _IsVisible; } set { _IsVisible = value; } }\n\n        public abstract void Draw(EngineTime time, ref SharpDX.Matrix world, ref SharpDX.Matrix view, ref SharpDX.Matrix projection);\n\n        #endregion\n    }\n}\n"
  },
  {
    "path": "SaintCoinach.Graphics.Viewer/EffectFactory.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.IO;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace SaintCoinach.Graphics.Viewer {\n    using SharpDX;\n    using SharpDX.D3DCompiler;\n    using SharpDX.Direct3D11;\n\n    public class EffectFactory : IDisposable {\n        #region Compiler include\n        class ShaderCompilerInclude : Include {\n        #region Fields\n        private DirectoryInfo _SourceDirectory;\n        private List<Stream> _OpenStreams = new List<Stream>();\n        private IDisposable _Shadow;\n        #endregion\n\n        #region Constructor\n        public ShaderCompilerInclude(string path) {\n            _SourceDirectory = new DirectoryInfo(path);\n        }\n        #endregion\n\n        #region Include Members\n\n        public void Close(Stream stream) {\n            stream.Dispose();\n            _OpenStreams.Remove(stream);\n        }\n\n        public Stream Open(IncludeType type, string fileName, Stream parentStream) {\n            var s = File.OpenRead(Path.Combine(_SourceDirectory.FullName, fileName));\n            _OpenStreams.Add(s);\n            return s;\n        }\n\n        #endregion\n\n        #region ICallbackable Members\n\n        IDisposable SharpDX.ICallbackable.Shadow {\n            get {\n                return _Shadow;\n            }\n            set {\n                _Shadow = value;\n            }\n        }\n\n        #endregion\n\n        #region IDisposable Members\n\n        public void Dispose() {\n            foreach (var s in _OpenStreams)\n                s.Dispose();\n            _OpenStreams.Clear();\n\n            if (_Shadow != null)\n                _Shadow.Dispose();\n            _Shadow = null;\n        }\n\n        #endregion\n    }\n        #endregion\n\n        #region Fields\n        private Engine _Engine;\n        private Dictionary<string, CompilationResult> _CompilationResults = new Dictionary<string, CompilationResult>();\n        private Dictionary<string, Effects.EffectBase> _Effects = new Dictionary<string, Effects.EffectBase>();\n        #endregion\n\n        #region Constructor\n        public EffectFactory(Engine engine) {\n            _Engine = engine;\n        }\n        #endregion\n\n        #region Get\n        public Effects.EffectBase GetEffect(string name) {\n            switch (name) {\n                case \"character.shpk\":\n                    return GetEffect(\n                        name,\n                        \"./Effects/HLSL/Character.fx\",\n                        (bc, ef) => new Effects.CharacterEffect(_Engine.Device, bc, ef));\n                case \"hair.shpk\":\n                    return GetEffect(\n                        name,\n                        \"./Effects/HLSL/Hair.fx\",\n                        (bc, ef) => new Effects.HairEffect(_Engine.Device, bc, ef));\n                case \"skin.shpk\":\n                    return GetEffect(\n                        name,\n                        \"./Effects/HLSL/Skin.fx\",\n                        (bc, ef) => new Effects.SkinEffect(_Engine.Device, bc, ef));\n                case \"bg.shpk\":\n                case \"bguvscroll.shpk\":     // TODO: Actually make it UV-scroll.\n                    return GetEffect(\n                        name,\n                        \"./Effects/HLSL/Bg.fx\",\n                        (bc, ef) => new Effects.BgEffect(_Engine.Device, bc, ef));\n                case \"iris.shpk\":\n                    return GetEffect(\n                        name,\n                        \"./Effects/HLSL/Iris.fx\",\n                        (bc, ef) => new Effects.IrisEffect(_Engine.Device, bc, ef));\n                case \"bgcolorchange.shpk\":\n                    return GetEffect(\n                        name,\n                        \"./Effects/HLSL/BgColorChange.fx\",\n                        (bc, ef) => new Effects.BgColorChangeEffect(_Engine.Device, bc, ef));\n                case \"crystal.shpk\":\n                    return GetEffect(\n                        name,\n                        \"./Effects/HLSL/Crystal.fx\",\n                        (bc, ef) => new Effects.CrystalEffect(_Engine.Device, bc, ef));\n                default:\n                    throw new NotSupportedException();\n            }\n        }\n        private Effects.EffectBase GetEffect(string key, string file, Func<CompilationResult, EffectFlags, Effects.EffectBase> factory) {\n            Effects.EffectBase effect;\n            if (_Effects.TryGetValue(key, out effect))\n                return effect;\n\n            var cr = GetCompilationResult(key, file);\n            effect = factory(cr, EffectFlags.None);\n            _Effects.Add(key, effect);\n            return effect;\n        }\n        private CompilationResult GetCompilationResult(string key, string file) {\n            CompilationResult result;\n            if (_CompilationResults.TryGetValue(key, out result))\n                return result;\n            result = ShaderBytecode.CompileFromFile(\n                        file,\n                        \"fx_5_0\",\n                        ShaderFlags.None,\n                        EffectFlags.None,\n                        new SharpDX.Direct3D.ShaderMacro[] { new SharpDX.Direct3D.ShaderMacro(\"SM4\", \"SM4\") },\n                        new ShaderCompilerInclude(System.IO.Path.Combine(\".\", \"Effects\", \"HLSL\")));\n            _CompilationResults.Add(key, result);\n\n            return result;\n        }\n        #endregion\n\n        #region IDisposable Members\n\n        public void Dispose() {\n            UnloadAll();\n        }\n\n        public void UnloadAll() {\n            foreach (var v in _CompilationResults.Values)\n                v.Dispose();\n            foreach (var v in _Effects.Values)\n                v.Dispose();\n            _CompilationResults.Clear();\n            _Effects.Clear();\n        }\n        #endregion\n    }\n}\n"
  },
  {
    "path": "SaintCoinach.Graphics.Viewer/Effects/BgColorChangeEffect.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace SaintCoinach.Graphics.Viewer.Effects {\n    using SharpDX;\n    using SharpDX.D3DCompiler;\n    using SharpDX.Direct3D11;\n\n    public class BgColorChangeEffect : EffectBase {\n        #region Fields\n        private EffectShaderResourceVariable _ColorMap0Var;\n        private EffectShaderResourceVariable _SpecularMap0Var;\n        private EffectShaderResourceVariable _NormalMap0Var;\n        private EffectVectorVariable _ColorVar;\n        #endregion\n\n        #region Properties\n        public ShaderResourceView ColorMap0 {\n            set { _ColorMap0Var.SetResource(value); }\n        }\n        public ShaderResourceView SpecularMap0 {\n            set { _SpecularMap0Var.SetResource(value); }\n        }\n        public ShaderResourceView NormalMap0 {\n            set { _NormalMap0Var.SetResource(value); }\n        }\n        public Vector4 Color {\n            set { _ColorVar.Set(value); }\n        }\n        #endregion\n\n        #region Constructor\n        public BgColorChangeEffect(Device device, byte[] byteCode) : this(device, byteCode, EffectFlags.None) { }\n        public BgColorChangeEffect(Device device, byte[] byteCode, EffectFlags flags)\n            : base(device, byteCode, flags) {\n            Init();\n        }\n        #endregion\n\n        #region Init\n        private void Init() {\n            _ColorMap0Var = GetVariableByName(\"g_ColorMap0\").AsShaderResource();\n            _SpecularMap0Var = GetVariableByName(\"g_SpecularMap0\").AsShaderResource();\n            _NormalMap0Var = GetVariableByName(\"g_NormalMap0\").AsShaderResource();\n            _ColorVar = GetVariableByName(\"g_Color\").AsVector();\n        }\n        #endregion\n    }\n}\n"
  },
  {
    "path": "SaintCoinach.Graphics.Viewer/Effects/BgEffect.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace SaintCoinach.Graphics.Viewer.Effects {\n    using SharpDX;\n    using SharpDX.D3DCompiler;\n    using SharpDX.Direct3D11;\n\n    public class BgEffect : EffectBase {\n        #region Fields\n        private EffectShaderResourceVariable _Diffuse0Var;\n        private EffectShaderResourceVariable _Specular0Var;\n        private EffectShaderResourceVariable _Normal0Var;\n        private EffectShaderResourceVariable _Diffuse1Var;\n        private EffectShaderResourceVariable _Specular1Var;\n        private EffectShaderResourceVariable _Normal1Var;\n        #endregion\n\n        #region Properties\n        public ShaderResourceView Diffuse0 {\n            set { _Diffuse0Var.SetResource(value); }\n        }\n        public ShaderResourceView Specular0 {\n            set { _Specular0Var.SetResource(value); }\n        }\n        public ShaderResourceView Normal0 {\n            set { _Normal0Var.SetResource(value); }\n        }\n        public ShaderResourceView Diffuse1 {\n            set { _Diffuse1Var.SetResource(value); }\n        }\n        public ShaderResourceView Specular1 {\n            set { _Specular1Var.SetResource(value); }\n        }\n        public ShaderResourceView Normal1 {\n            set { _Normal1Var.SetResource(value); }\n        }\n        #endregion\n\n        #region Constructor\n        public BgEffect(Device device, byte[] byteCode) : this(device, byteCode, EffectFlags.None) { }\n        public BgEffect(Device device, byte[] byteCode, EffectFlags flags)\n            : base(device, byteCode, flags) {\n            Init();\n        }\n        #endregion\n\n        #region Init\n        private void Init() {\n            _Diffuse0Var = GetVariableByName(\"g_Diffuse0\").AsShaderResource();\n            _Specular0Var = GetVariableByName(\"g_Specular0\").AsShaderResource();\n            _Normal0Var = GetVariableByName(\"g_Normal0\").AsShaderResource();\n            _Diffuse1Var = GetVariableByName(\"g_Diffuse1\").AsShaderResource();\n            _Specular1Var = GetVariableByName(\"g_Specular1\").AsShaderResource();\n            _Normal1Var = GetVariableByName(\"g_Normal1\").AsShaderResource();\n        }\n        #endregion\n    }\n}\n"
  },
  {
    "path": "SaintCoinach.Graphics.Viewer/Effects/CharacterEffect.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace SaintCoinach.Graphics.Viewer.Effects {\n    using SharpDX;\n    using SharpDX.D3DCompiler;\n    using SharpDX.Direct3D11;\n\n    public class CharacterEffect : SkinnedEffect {\n        #region Fields\n        private EffectShaderResourceVariable _DiffuseVar;\n        private EffectShaderResourceVariable _SpecularVar;\n        private EffectShaderResourceVariable _NormalVar;\n        private EffectShaderResourceVariable _MaskVar;\n        private EffectShaderResourceVariable _TableVar;\n        #endregion\n\n        #region Properties\n        public ShaderResourceView Diffuse {\n            set { _DiffuseVar.SetResource(value); }\n        }\n        public ShaderResourceView Specular {\n            set { _SpecularVar.SetResource(value); }\n        }\n        public ShaderResourceView Normal {\n            set { _NormalVar.SetResource(value); }\n        }\n        public ShaderResourceView Mask {\n            set { _MaskVar.SetResource(value); }\n        }\n        public ShaderResourceView Table {\n            set { _TableVar.SetResource(value); }\n        }\n        #endregion\n\n        #region Constructor\n        public CharacterEffect(Device device, byte[] byteCode) : this(device, byteCode, EffectFlags.None) { }\n        public CharacterEffect(Device device, byte[] byteCode, EffectFlags flags)\n            : base(device, byteCode, flags) {\n            Init();\n        }\n        #endregion\n\n        #region Init\n        private void Init() {\n            _DiffuseVar = GetVariableByName(\"g_Diffuse\").AsShaderResource();\n            _SpecularVar = GetVariableByName(\"g_Specular\").AsShaderResource();\n            _NormalVar = GetVariableByName(\"g_Normal\").AsShaderResource();\n            _MaskVar = GetVariableByName(\"g_Mask\").AsShaderResource();\n            _TableVar = GetVariableByName(\"g_Table\").AsShaderResource();\n        }\n        #endregion\n    }\n}\n"
  },
  {
    "path": "SaintCoinach.Graphics.Viewer/Effects/CrystalEffect.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace SaintCoinach.Graphics.Viewer.Effects {\n    using SharpDX;\n    using SharpDX.D3DCompiler;\n    using SharpDX.Direct3D11;\n\n    public class CrystalEffect : EffectBase {\n        #region Fields\n        private EffectShaderResourceVariable _ColorMap0Var;\n        private EffectShaderResourceVariable _SpecularMap0Var;\n        private EffectShaderResourceVariable _NormalMap0Var;\n        private EffectShaderResourceVariable _EnvironmentMap;\n        #endregion\n\n        #region Properties\n        public ShaderResourceView ColorMap0 {\n            set { _ColorMap0Var.SetResource(value); }\n        }\n        public ShaderResourceView SpecularMap0 {\n            set { _SpecularMap0Var.SetResource(value); }\n        }\n        public ShaderResourceView NormalMap0 {\n            set { _NormalMap0Var.SetResource(value); }\n        }\n        public ShaderResourceView EnvironmentMap {\n            set { _EnvironmentMap.SetResource(value); }\n        }\n        #endregion\n\n        #region Constructor\n        public CrystalEffect(Device device, byte[] byteCode) : this(device, byteCode, EffectFlags.None) { }\n        public CrystalEffect(Device device, byte[] byteCode, EffectFlags flags)\n            : base(device, byteCode, flags) {\n            Init();\n        }\n        #endregion\n\n        #region Init\n        private void Init() {\n            _ColorMap0Var = GetVariableByName(\"g_ColorMap0\").AsShaderResource();\n            _SpecularMap0Var = GetVariableByName(\"g_SpecularMap0\").AsShaderResource();\n            _NormalMap0Var = GetVariableByName(\"g_NormalMap0\").AsShaderResource();\n            _EnvironmentMap = GetVariableByName(\"g_EnvMap\").AsShaderResource();\n        }\n        #endregion\n    }\n}\n"
  },
  {
    "path": "SaintCoinach.Graphics.Viewer/Effects/CustomizeParameterEffectVariable.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace SaintCoinach.Graphics.Viewer.Effects {\n    using SharpDX;\n    using SharpDX.Direct3D11;\n\n    public class CustomizeParameterEffectVariable {\n        #region Fields\n        private EffectVectorVariable _SkinColorVar;\n        private EffectVectorVariable _LipColorVar;\n        private EffectVectorVariable _HairColorVar;\n        private EffectVectorVariable _MeshColorVar;\n        private EffectVectorVariable _LeftColorVar;\n        private EffectVectorVariable _RightColorVar;\n        #endregion\n\n        #region Properties\n        public Vector4 SkinColor {\n            set { _SkinColorVar.Set(value); }\n        }\n        public Vector4 LipColor {\n            set { _LipColorVar.Set(value); }\n        }\n        public Vector3 HairColor {\n            set { _HairColorVar.Set(value); }\n        }\n        public Vector3 MeshColor {\n            set { _MeshColorVar.Set(value); }\n        }\n        public Vector3 LeftColor {\n            set { _LeftColorVar.Set(value); }\n        }\n        public Vector3 RightColor {\n            set { _RightColorVar.Set(value); }\n        }\n        #endregion\n\n        #region Constructor\n        public CustomizeParameterEffectVariable(EffectBase effect) {\n            _HairColorVar = effect.GetVariableByName(\"m_HairColor\").AsVector();\n            _MeshColorVar = effect.GetVariableByName(\"m_MeshColor\").AsVector();\n            _LeftColorVar = effect.GetVariableByName(\"m_LeftColor\").AsVector();\n            _RightColorVar = effect.GetVariableByName(\"m_RightColor\").AsVector();\n            _SkinColorVar = effect.GetVariableByName(\"m_SkinColor\").AsVector();\n            _LipColorVar = effect.GetVariableByName(\"m_LipColor\").AsVector();\n        }\n        #endregion\n\n        public void Set(Data.CustomizeParameters parmeters) {\n            this.SkinColor = parmeters.SkinColor;\n            this.LipColor = parmeters.LipColor;\n            this.HairColor = parmeters.HairColor;\n            this.MeshColor = parmeters.MeshColor;\n            this.LeftColor = parmeters.LeftEyeColor;\n            this.RightColor = parmeters.RightEyeColor;\n        }\n    }\n}\n"
  },
  {
    "path": "SaintCoinach.Graphics.Viewer/Effects/DirectionalLight.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Runtime.InteropServices;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace SaintCoinach.Graphics.Viewer.Effects {\n    using SharpDX;\n\n    [StructLayout(LayoutKind.Sequential)]\n    public struct DirectionalLight {\n        public Vector3 Direction;\n        public Vector4 Diffuse;\n        public Vector4 Specular;\n    }\n}\n"
  },
  {
    "path": "SaintCoinach.Graphics.Viewer/Effects/EffectBase.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Collections.ObjectModel;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace SaintCoinach.Graphics.Viewer.Effects {\n    using SharpDX;\n    using SharpDX.D3DCompiler;\n    using SharpDX.Direct3D11;\n\n    public abstract class EffectBase : Effect {\n        public static readonly ReadOnlyCollection<string> SupportedShaders = new ReadOnlyCollection<string>(new List<string> {\n            \"character.shpk\",\n            \"bg.shpk\",\n            \"skin.shpk\",\n            \"hair.shpk\"\n        });\n\n        #region Effect variables\n        private EffectVectorVariable _EyePositionVar;\n\n        private EffectDirectionalLightVariable _DirectionalLight0Var;\n        private EffectDirectionalLightVariable _DirectionalLight1Var;\n        private EffectDirectionalLightVariable _DirectionalLight2Var;\n\n        private EffectVectorVariable _DiffuseColorVar;\n        private EffectVectorVariable _EmissiveColorVar;\n        private EffectVectorVariable _AmbientColorVar;\n        private EffectVectorVariable _SpecularColorVar;\n        private EffectScalarVariable _SpecularPowerVar;\n\n        private EffectMatrixVariable _WorldVar;\n        private EffectMatrixVariable _WorldInverseTransposeVar;\n        private EffectMatrixVariable _WorldViewProjectionVar;\n        private EffectMatrixVariable _ViewInverseVar;\n        #endregion\n\n        #region Variable values\n        public Vector3 EyePosition {\n            get { return _EyePositionVar.GetVector<Vector3>(); }\n            set { _EyePositionVar.Set(value); }\n        }\n        public DirectionalLight Light0 {\n            get { return _DirectionalLight0Var.Get(); }\n            set { _DirectionalLight0Var.Set(value); }\n        }\n        public DirectionalLight Light1 {\n            get { return _DirectionalLight1Var.Get(); }\n            set { _DirectionalLight1Var.Set(value); }\n        }\n        public DirectionalLight Light2 {\n            get { return _DirectionalLight2Var.Get(); }\n            set { _DirectionalLight2Var.Set(value); }\n        }\n\n        public Vector4 DiffuseColor {\n            get { return _DiffuseColorVar.GetVector<Vector4>(); }\n            set { _DiffuseColorVar.Set(value); }\n        }\n        public Vector3 EmissiveColor {\n            get { return _EmissiveColorVar.GetVector<Vector3>(); }\n            set { _EmissiveColorVar.Set(value); }\n        }\n        public Vector3 AmbientColor {\n            get { return _AmbientColorVar.GetVector<Vector3>(); }\n            set { _AmbientColorVar.Set(value); }\n        }\n        public Vector3 SpecularColor {\n            get { return _SpecularColorVar.GetVector<Vector3>(); }\n            set { _SpecularColorVar.Set(value); }\n        }\n        public float SpecularPower {\n            get { return _SpecularPowerVar.GetFloat(); }\n            set { _SpecularPowerVar.Set(value); }\n        }\n\n        public Matrix World {\n            get { return _WorldVar.GetMatrix(); }\n            set { _WorldVar.SetMatrix(value); }\n        }\n        public Matrix WorldInverseTranspose {\n            get { return _WorldInverseTransposeVar.GetMatrix(); }\n            set { _WorldInverseTransposeVar.SetMatrix(value); }\n        }\n        public Matrix WorldViewProjection {\n            get { return _WorldViewProjectionVar.GetMatrix(); }\n            set { _WorldViewProjectionVar.SetMatrix(value); }\n        }\n        public Matrix ViewInverse {\n            get { return _ViewInverseVar.GetMatrix(); }\n            set { _ViewInverseVar.SetMatrix(value); }\n        }\n        #endregion\n\n        #region Constructor\n        protected EffectBase(Device device, byte[] byteCode) : this(device, byteCode, EffectFlags.None) { }\n        protected EffectBase(Device device, byte[] byteCode, EffectFlags flags)\n            : base(device, byteCode, flags) {\n\n            Init();\n        }\n        #endregion\n\n        #region Init\n        private void Init() {\n            _WorldVar = GetVariableByName(\"g_World\").AsMatrix();\n            _WorldInverseTransposeVar = GetVariableByName(\"g_WorldInverseTranspose\").AsMatrix();\n            _WorldViewProjectionVar = GetVariableByName(\"g_WorldViewProjection\").AsMatrix();\n\n            _ViewInverseVar = GetVariableByName(\"m_ViewInverse\").AsMatrix();\n\n            _EyePositionVar = GetVariableByName(\"m_EyePosition\").AsVector();\n            _DirectionalLight0Var = new EffectDirectionalLightVariable(GetVariableByName(\"m_Light0\"));\n            _DirectionalLight1Var = new EffectDirectionalLightVariable(GetVariableByName(\"m_Light1\"));\n            _DirectionalLight2Var = new EffectDirectionalLightVariable(GetVariableByName(\"m_Light2\"));\n\n            _DiffuseColorVar = GetVariableByName(\"m_DiffuseColor\").AsVector();\n            _EmissiveColorVar = GetVariableByName(\"m_EmissiveColor\").AsVector();\n            _AmbientColorVar = GetVariableByName(\"m_AmbientColor\").AsVector();\n            _SpecularColorVar = GetVariableByName(\"m_SpecularColor\").AsVector();\n            _SpecularPowerVar = GetVariableByName(\"m_SpecularPower\").AsScalar();\n\n            SetDefaults();\n        }\n\n        public virtual void SetDefaults() {\n            SpecularColor = Vector3.One;\n            SpecularPower = 64;\n\n            DiffuseColor = Vector4.One;\n            Light0 = new DirectionalLight {\n                Direction = new Vector3(0.5f, 0.25f, 1),\n                Diffuse = Vector4.One,\n                Specular = Vector4.One * 0.75f,\n            };\n            Light1 = new DirectionalLight {\n                Direction = new Vector3(0, -1, 0),\n                Diffuse = Vector4.One,\n                Specular = Vector4.One * 0.75f,\n            };\n            Light2 = new DirectionalLight {\n                Direction = new Vector3(-0.5f, 0.25f, -1),\n                Diffuse = Vector4.One,\n                Specular = Vector4.One * 0.75f,\n            };\n            /*\n            Light0 = new DirectionalLight {\n                Direction = new Vector3(-0.5265408f, -0.5735765f, -0.6275069f),\n                Diffuse = new Vector4(1, 0.9607844f, 0.8078432f, 1),\n                Specular = new Vector4(1, 0.9607844f, 0.8078432f, 1),\n            };\n            Light1 = new DirectionalLight {\n                Direction = new Vector3(0.7198464f, 0.3420201f, 0.6040227f),\n                Diffuse = new Vector4(0.9647059f, 0.7607844f, 0.4078432f, 1),\n                Specular = Vector4.One,\n            };\n            Light2 = new DirectionalLight {\n                Direction = new Vector3(0.4545195f, -0.7660444f, 0.4545195f),\n                Diffuse = new Vector4(0.3231373f, 0.3607844f, 0.3937255f, 1),\n                Specular = new Vector4(0.3231373f, 0.3607844f, 0.3937255f, 1),\n            };\n            */\n            this.AmbientColor = new Vector3(0.05333332f, 0.09882354f, 0.1819608f);\n        }\n        #endregion\n\n        #region Apply\n        public void Apply(ref Matrix world, ref Matrix view, ref Matrix projection) {\n            this.World = world;\n\n            Matrix worldTranspose;\n            Matrix worldInverseTranspose;\n            Matrix.Invert(ref world, out worldTranspose);\n            Matrix.Transpose(ref worldTranspose, out worldInverseTranspose);\n            this.WorldInverseTranspose = worldInverseTranspose;\n\n            Matrix viewProjection;\n            Matrix worldViewProjection;\n            Matrix.Multiply(ref view, ref projection, out viewProjection);\n            Matrix.Multiply(ref world, ref viewProjection, out worldViewProjection);\n            this.WorldViewProjection = worldViewProjection;\n\n            Matrix viewInverse;\n            Matrix.Invert(ref view, out viewInverse);\n            this.ViewInverse = viewInverse;\n\n            EyePosition = viewInverse.TranslationVector;\n        }\n        #endregion\n\n        #region Input layouts\n        private Dictionary<long, InputLayout> _InputLayouts = new Dictionary<long, InputLayout>();\n        public InputLayout GetInputLayout(EffectPass pass) {\n\n            var key = pass.NativePointer.ToInt64();\n            if (_InputLayouts.ContainsKey(key))\n                return _InputLayouts[key];\n\n            var elements = Vertex3D.InputElements;\n            var layout = new InputLayout(Device, pass.Description.Signature, elements);\n\n            _InputLayouts.Add(key, layout);\n\n            return layout;\n        }\n        #endregion\n\n        protected override void Dispose(bool disposing) {\n            foreach (var il in _InputLayouts.Values)\n                il.Dispose();\n            _InputLayouts.Clear();\n            base.Dispose(disposing);\n        }\n    }\n}\n"
  },
  {
    "path": "SaintCoinach.Graphics.Viewer/Effects/EffectDirectionalLightVariable.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace SaintCoinach.Graphics.Viewer.Effects {\n    using SharpDX;\n    using SharpDX.D3DCompiler;\n    using SharpDX.Direct3D11;\n    \n    public class EffectDirectionalLightVariable {\n        #region Fields\n        private EffectVariable _StructureVariable;\n        private EffectVectorVariable _DirectionMember;\n        private EffectVectorVariable _DiffuseMember;\n        private EffectVectorVariable _SpecularMember;\n        #endregion\n\n        #region Properties\n        public Vector3 Direction {\n            get { return _DirectionMember.GetVector<Vector3>(); }\n            set { _DirectionMember.Set(value); }\n        }\n        public Vector4 Diffuse {\n            get { return _DiffuseMember.GetVector<Vector4>(); }\n            set { _DiffuseMember.Set(value); }\n        }\n        public Vector4 Specular {\n            get { return _SpecularMember.GetVector<Vector4>(); }\n            set { _SpecularMember.Set(value); }\n        }\n        #endregion\n\n        #region Constructor\n        public EffectDirectionalLightVariable(EffectVariable structureVar) {\n            _StructureVariable = structureVar;\n\n            _DirectionMember = structureVar.GetMemberByName(\"Direction\").AsVector();\n            _DiffuseMember = structureVar.GetMemberByName(\"Diffuse\").AsVector();\n            _SpecularMember = structureVar.GetMemberByName(\"Specular\").AsVector();\n        }\n        #endregion\n\n        public void Set(DirectionalLight light) {\n            this.Direction = light.Direction;\n            this.Diffuse = light.Diffuse;\n            this.Specular = light.Specular;\n        }\n\n        public DirectionalLight Get() {\n            DirectionalLight light = new DirectionalLight();\n\n            light.Direction = this.Direction;\n            light.Diffuse = this.Diffuse;\n            light.Specular = this.Specular;\n\n            return light;\n        }\n    }\n}\n"
  },
  {
    "path": "SaintCoinach.Graphics.Viewer/Effects/EffectTextureVariable.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace SaintCoinach.Graphics.Viewer.Effects {\n    using SharpDX;\n    using SharpDX.D3DCompiler;\n    using SharpDX.Direct3D11;\n\n    public class EffectTextureVariable {\n        #region Fields\n        private Effect _Effect;\n        private EffectShaderResourceVariable _TextureVar;\n        private EffectSamplerVariable _SamplerVar;\n        #endregion\n\n        #region Properties\n        public SamplerState SamplerState {\n            get { return _SamplerVar.GetSampler(); }\n            set { _SamplerVar.SetSampler(0, value); }\n        }\n        public ShaderResourceView Texture {\n            set { _TextureVar.SetResource(value); }\n        }\n        #endregion\n\n        #region Constructor\n        public EffectTextureVariable(Effect effect, string name) {\n            _Effect = effect;\n            _TextureVar = effect.GetVariableByName(name).AsShaderResource();\n            _SamplerVar = effect.GetVariableByName(name + \"Sampler\").AsSampler();\n        }\n        #endregion\n    }\n}\n"
  },
  {
    "path": "SaintCoinach.Graphics.Viewer/Effects/HLSL/BasicEffect.fx",
    "content": "﻿struct VS_IN\n{\n    float4 pos : POSITION;\n    float4 col : COLOR;\n};\n\nstruct PS_IN\n{\n    float4 pos : SV_POSITION;\n    float4 col : COLOR;\n};\n\nfloat4x4 worldViewProj;\n\nPS_IN VS(VS_IN input)\n{\n    PS_IN output = (PS_IN) 0;\n\n    output.pos = mul(input.pos, worldViewProj);\n    output.col = input.col;\n\n    return output;\n}\n\nfloat4 PS(PS_IN input) : SV_Target\n{\n    return input.col;\n}"
  },
  {
    "path": "SaintCoinach.Graphics.Viewer/Effects/HLSL/Bg.fx",
    "content": "#include \"Structures.fxh\"\n\nTexture2D<float4> g_Diffuse0     : register(t0);\nTexture2D<float4> g_Normal0      : register(t1);\nTexture2D<float4> g_Specular0    : register(t2);\nTexture2D<float4> g_Diffuse1     : register(t3);\nTexture2D<float4> g_Normal1      : register(t4);\nTexture2D<float4> g_Specular1    : register(t5);\n\nsampler g_Diffuse0Sampler        : register(s0)\n{\n    AddressU = Wrap;\n    AddressV = Wrap;\n    Filter = MIN_MAG_MIP_LINEAR;\n};\nsampler g_Normal0Sampler         : register(s1)\n{\n    AddressU = Wrap;\n    AddressV = Wrap;\n    Filter = MIN_MAG_MIP_LINEAR;\n};\nsampler g_Specular0Sampler       : register(s2)\n{\n    AddressU = Wrap;\n    AddressV = Wrap;\n    Filter = MIN_MAG_MIP_LINEAR;\n};\nsampler g_Diffuse1Sampler        : register(s3)\n{\n    AddressU = Wrap;\n    AddressV = Wrap;\n    Filter = MIN_MAG_MIP_LINEAR;\n};\nsampler g_Normal1Sampler         : register(s4)\n{\n    AddressU = Wrap;\n    AddressV = Wrap;\n    Filter = MIN_MAG_MIP_LINEAR;\n};\nsampler g_Specular1Sampler       : register(s5)\n{\n    AddressU = Wrap;\n    AddressV = Wrap;\n    Filter = MIN_MAG_MIP_LINEAR;\n};\n\n#include \"Common.fxh\"\n#include \"Lighting.fxh\"\n\n/*\nfloat4 ComputeCommon(VSOutputDualTexture pin, float4 diffuse, float4 mapNormal, float4 specular)\n{\n    //float3 normal = CalculateNormal(pin.WorldNormal, pin.WorldTangent, pin.WorldBinormal, mapNormal.xyz);\n    float3 normal = pin.WorldNormal; // XXX: My tangent calculation results in 0/0/0 at times, that makes things fail.\n    \n    float3 eyeVector = normalize(m_EyePosition - pin.PositionWS);\n    LightResult light = ComputeLights(eyeVector, normal, 3);\n    \n    float4 color = diffuse;\n    \n    color.rgb *= light.Diffuse.rgb;\n    color.rgb += light.Specular.rgb * specular * color.a;\n    \n    return color;\n};\n*/\nfloat4 ComputeCommon(VSOutput pin, float4 diffuse, float4 specular, float3 bump, float3 tangent)\n{\n    float3 binorm = cross(pin.NormalWS.xyz, tangent.xyz);\n    float3 bumpNormal = (bump.x * tangent) + (bump.y * binorm) + (bump.z * pin.NormalWS);\n    bumpNormal = normalize(bumpNormal);\n\n    float3 eyeVector = normalize(m_EyePosition - pin.PositionWS);\n    Lighting light = GetLight(m_EyePosition, eyeVector, bumpNormal);\n\n    float4 color = diffuse;\n    clip(color.a <= 0.5 ? -1 : 1);\n    color.a = 1;\n\n    color.rgb *= light.Diffuse.rgb;\n    color.rgb += light.Specular.rgb * specular.rgb * color.a;\n    return color;\n};\n\nbool IsDummy(float4 value)\n{\n    // TODO: Actually figure out what's wrong here.\n    if (value.x == value.y && value.y == value.z && value.z == value.w && value.w == 1)\n    {\n        return true;\n    }\n    return false;\n}\n\nfloat4 PSSingle(VSOutput pin) : SV_Target0\n{\n    float4 texDiffuse0 = g_Diffuse0.Sample(g_Diffuse0Sampler, pin.UV.xy);\n    float4 texNormal0 = g_Normal0.Sample(g_Normal0Sampler, pin.UV.xy);\n\n    return ComputeCommon(pin, texDiffuse0, (0).xxxx, (texNormal0.xyz - 0.5) * 2.0, pin.Tangent1WS.xyz);\n}\nfloat4 PSSingleSpecular(VSOutput pin) : SV_Target0\n{\n    float4 texDiffuse0 = g_Diffuse0.Sample(g_Diffuse0Sampler, pin.UV.xy);\n    float4 texSpecular0 = g_Specular0.Sample(g_Specular0Sampler, pin.UV.xy);\n    float4 texNormal0 = g_Normal0.Sample(g_Normal0Sampler, pin.UV.xy);\n\n    return ComputeCommon(pin, texDiffuse0, float4(texSpecular0.rrr, 1), (texNormal0.xyz - 0.5) * 2.0, pin.Tangent1WS.xyz);\n}\n\nfloat4 PSDual(VSOutput pin) : SV_Target0\n{\n    float4 texDiffuse0 = g_Diffuse0.Sample(g_Diffuse0Sampler, pin.UV.xy);\n    float4 texNormal0 = g_Normal0.Sample(g_Normal0Sampler, pin.UV.xy);\n\n    float4 texDiffuse1 = g_Diffuse1.Sample(g_Diffuse1Sampler, pin.UV.zw);\n    float4 texNormal1 = g_Normal1.Sample(g_Normal1Sampler, pin.UV.zw);\n\n    if (IsDummy(texDiffuse1))\n    {\n        texDiffuse1 = texDiffuse0;\n    }\n    if (IsDummy(texNormal1))\n    {\n        texNormal1 = texNormal0;\n    }\n\n    float4 diffuse = lerp(texDiffuse0, texDiffuse1, pin.Color.w);\n    float3 bump = (texNormal0.xyz - 0.5) * 2.0;// lerp((texNormal0.xyz - 0.5) * 2.0, (texNormal1.xyz - 0.5) * 2.0, pin.Color.w);\n    float4 specular = (0).xxxx;\n    float3 tangent = lerp(pin.Tangent1WS.xyz, pin.Tangent2WS.xyz, pin.Color.w);\n\n    return ComputeCommon(pin, diffuse, specular, bump, tangent);\n}\n\nfloat4 PSDualSpecular(VSOutput pin) : SV_Target0\n{\n    float4 texDiffuse0 = g_Diffuse0.Sample(g_Diffuse0Sampler, pin.UV.xy);\n    float4 texNormal0 = g_Normal0.Sample(g_Normal0Sampler, pin.UV.xy);\n    float4 texSpecular0 = g_Specular0.Sample(g_Specular0Sampler, pin.UV.xy);\n\n    float4 texDiffuse1 = g_Diffuse1.Sample(g_Diffuse1Sampler, pin.UV.zw);\n    float4 texNormal1 = g_Normal1.Sample(g_Normal1Sampler, pin.UV.zw);\n    float4 texSpecular1 = g_Specular1.Sample(g_Specular1Sampler, pin.UV.zw);\n\n    if (IsDummy(texDiffuse1))\n    {\n        texDiffuse1 = texDiffuse0;\n    }\n    if (IsDummy(texNormal1))\n    {\n        texNormal1 = texNormal0;\n    }\n    if (IsDummy(texSpecular1))\n    {\n        texSpecular1 = texSpecular0;\n    }\n\n    float4 diffuse = lerp(texDiffuse0, texDiffuse1, pin.Color.w);\n    float3 bump = (texNormal0.xyz - 0.5) * 2.0;// lerp((texNormal0.xyz - 0.5) * 2.0, (texNormal1.xyz - 0.5) * 2.0, pin.Color.w);\n    float3 specular = lerp(texSpecular0.rrr, texSpecular1.rrr, pin.Color.w);\n    float3 tangent = lerp(pin.Tangent1WS.xyz, pin.Tangent2WS.xyz, pin.Color.w);\n\n    return ComputeCommon(pin, diffuse, float4(specular, 1), bump, pin.Tangent1WS.xyz);\n}\n\ntechnique11 Single\n{\n    pass P0 {\n        SetGeometryShader(0);\n        SetVertexShader(CompileShader(vs_4_0, VSCommon()));\n        SetPixelShader(CompileShader(ps_4_0, PSSingle()));\n    }\n}\ntechnique11 SingleSpecular\n{\n    pass P0 {\n        SetGeometryShader(0);\n        SetVertexShader(CompileShader(vs_4_0, VSCommon()));\n        SetPixelShader(CompileShader(ps_4_0, PSSingleSpecular()));\n    }\n}\ntechnique11 Dual\n{\n    pass P0 {\n        SetGeometryShader(0);\n        SetVertexShader(CompileShader(vs_4_0, VSCommon()));\n        SetPixelShader(CompileShader(ps_4_0, PSDual()));\n    }\n}\ntechnique11 DualSpecular\n{\n    pass P0 {\n        SetGeometryShader(0);\n        SetVertexShader(CompileShader(vs_4_0, VSCommon()));\n        SetPixelShader(CompileShader(ps_4_0, PSDualSpecular()));\n    }\n}"
  },
  {
    "path": "SaintCoinach.Graphics.Viewer/Effects/HLSL/BgColorChange.fx",
    "content": "#include \"Structures.fxh\"\n\nfloat4 g_Color                   : register(b2);\n\nTexture2D<float4> g_ColorMap0    : register(t0);\nTexture2D<float4> g_NormalMap0   : register(t1);\nTexture2D<float4> g_SpecularMap0 : register(t2);\n\nsampler g_ColorMap0Sampler       : register(s0)\n{\n    AddressU = Wrap;\n    AddressV = Wrap;\n    Filter = MIN_MAG_MIP_LINEAR;\n};\nsampler g_Normal0MapSampler      : register(s1)\n{\n    AddressU = Wrap;\n    AddressV = Wrap;\n    Filter = MIN_MAG_MIP_LINEAR;\n};\nsampler g_Specular0MapSampler    : register(s2)\n{\n    AddressU = Wrap;\n    AddressV = Wrap;\n    Filter = MIN_MAG_MIP_LINEAR;\n};\n\n#include \"Common.fxh\"\n#include \"Lighting.fxh\"\n\nfloat4 ComputeCommon(VSOutput pin, float4 diffuse, float4 specular, float3 bump, float3 tangent)\n{\n    float3 binorm = cross(pin.NormalWS.xyz, tangent.xyz);\n    float3 bumpNormal = (bump.x * tangent) + (bump.y * binorm) + (bump.z * pin.NormalWS);\n    bumpNormal = normalize(bumpNormal);\n\n    float3 eyeVector = normalize(m_EyePosition - pin.PositionWS);\n    Lighting light = GetLight(m_EyePosition, eyeVector, bumpNormal);\n\n    float4 color = (1).xxxx;\n    color.rgb = diffuse.rgb * lerp((1).xxx, g_Color.rgb, diffuse.a);\n\n    color.rgb *= light.Diffuse.rgb;\n    color.rgb += light.Specular.rgb * specular.rgb * color.a;\n    return color;\n};\n\nfloat4 PSColorChange(VSOutput pin) : SV_Target0\n{\n    float4 texColor0 = g_ColorMap0.Sample(g_ColorMap0Sampler, pin.UV.xy);\n    float4 texNormal0 = g_NormalMap0.Sample(g_Normal0MapSampler, pin.UV.xy);\n    float4 texSpecular0 = g_SpecularMap0.Sample(g_Specular0MapSampler, pin.UV.xy);\n\n    return ComputeCommon(pin, texColor0, texSpecular0.gggg, (texNormal0.xyz - 0.5) * 2.0, pin.Tangent1WS.xyz);\n}\n\ntechnique11 BgColorChange\n{\n    pass P0 {\n        SetGeometryShader(0);\n        SetVertexShader(CompileShader(vs_4_0, VSCommon()));\n        SetPixelShader(CompileShader(ps_4_0, PSColorChange()));\n    }\n}"
  },
  {
    "path": "SaintCoinach.Graphics.Viewer/Effects/HLSL/BgUvScroll.fx",
    "content": "#include \"Structures.fxh\"\n\nTexture2D<float4> g_Diffuse0     : register(t0);\nTexture2D<float4> g_Normal0      : register(t1);\nTexture2D<float4> g_Specular0    : register(t2);\nTexture2D<float4> g_Diffuse1     : register(t3);\nTexture2D<float4> g_Normal1      : register(t4);\nTexture2D<float4> g_Specular1    : register(t5);\n\nsampler g_Diffuse0Sampler        : register(s0)\n{\n    AddressU = Wrap;\n    AddressV = Wrap;\n    Filter = MIN_MAG_MIP_LINEAR;\n};\nsampler g_Normal0Sampler         : register(s1)\n{\n    AddressU = Wrap;\n    AddressV = Wrap;\n    Filter = MIN_MAG_MIP_LINEAR;\n};\nsampler g_Specular0Sampler       : register(s2)\n{\n    AddressU = Wrap;\n    AddressV = Wrap;\n    Filter = MIN_MAG_MIP_LINEAR;\n};\nsampler g_Diffuse1Sampler        : register(s3)\n{\n    AddressU = Wrap;\n    AddressV = Wrap;\n    Filter = MIN_MAG_MIP_LINEAR;\n};\nsampler g_Normal1Sampler         : register(s4)\n{\n    AddressU = Wrap;\n    AddressV = Wrap;\n    Filter = MIN_MAG_MIP_LINEAR;\n};\nsampler g_Specular1Sampler       : register(s5)\n{\n    AddressU = Wrap;\n    AddressV = Wrap;\n    Filter = MIN_MAG_MIP_LINEAR;\n};\n\n#include \"Common.fxh\"\n#include \"Lighting.fxh\"\n\n/*\nfloat4 ComputeCommon(VSOutputDualTexture pin, float4 diffuse, float4 mapNormal, float4 specular)\n{\n    //float3 normal = CalculateNormal(pin.WorldNormal, pin.WorldTangent, pin.WorldBinormal, mapNormal.xyz);\n    float3 normal = pin.WorldNormal; // XXX: My tangent calculation results in 0/0/0 at times, that makes things fail.\n    \n    float3 eyeVector = normalize(m_EyePosition - pin.PositionWS);\n    LightResult light = ComputeLights(eyeVector, normal, 3);\n    \n    float4 color = diffuse;\n    \n    color.rgb *= light.Diffuse.rgb;\n    color.rgb += light.Specular.rgb * specular * color.a;\n    \n    return color;\n};\n*/\nfloat4 ComputeCommon(VSOutput pin, float4 diffuse, float4 specular, float3 bump, float3 tangent)\n{\n    float3 binorm = cross(pin.NormalWS.xyz, tangent.xyz);\n    float3 bumpNormal = (bump.x * tangent) + (bump.y * binorm) + (bump.z * pin.NormalWS);\n    bumpNormal = normalize(bumpNormal);\n\n    float3 eyeVector = normalize(m_EyePosition - pin.PositionWS);\n    Lighting light = GetLight(m_EyePosition, eyeVector, bumpNormal);\n\n    float4 color = diffuse;\n    clip(color.a <= 0.5 ? -1 : 1);\n    color.a = 1;\n\n    color.rgb *= light.Diffuse.rgb;\n    color.rgb += light.Specular.rgb * specular.rgb * color.a;\n    return color;\n};\n\nbool IsDummy(float4 value)\n{\n    // TODO: Actually figure out what's wrong here.\n    if (value.x == value.y && value.y == value.z && value.z == value.w && value.w == 1)\n    {\n        return true;\n    }\n    return false;\n}\n\n\nVSOutput VSUvScroll(VSInput vin)\n{\n    VSOutput vout;\n\n    vout.PositionPS = mul(vin.Position, g_WorldViewProjection);\n    vout.PositionWS = mul(vin.Position, g_World);\n    vout.NormalWS = normalize(mul(vin.Normal, g_WorldInverseTranspose));\n\n    vout.UV = vin.UV;   // TODO: Figured this out.\n    vout.BlendWeight = vin.BlendWeight;\n    vout.BlendIndices = vin.BlendIndices;\n    vout.Color = vin.Color;\n\n    // Going to pretend these are tangents\n    float4 t1 = (vin.Tangent1 - 0.5) * 2.0;\n    vout.Tangent1WS.xyz = normalize(mul(t1.xyz, g_WorldInverseTranspose));\n    vout.Tangent1WS.w = t1.w;\n\n    float4 t2 = (vin.Tangent2 - 0.5) * 2.0;\n    vout.Tangent2WS.xyz = normalize(mul(t2.xyz, g_WorldInverseTranspose));\n    vout.Tangent2WS.w = t2.w;\n\n    return vout;\n};\n\nfloat4 PSSingle(VSOutput pin) : SV_Target0\n{\n    float4 texDiffuse0 = g_Diffuse0.Sample(g_Diffuse0Sampler, pin.UV.xy);\n    float4 texNormal0 = g_Normal0.Sample(g_Normal0Sampler, pin.UV.xy);\n\n    return ComputeCommon(pin, texDiffuse0, (0).xxxx, (texNormal0.xyz - 0.5) * 2.0, pin.Tangent1WS.xyz);\n}\nfloat4 PSSingleSpecular(VSOutput pin) : SV_Target0\n{\n    float4 texDiffuse0 = g_Diffuse0.Sample(g_Diffuse0Sampler, pin.UV.xy);\n    float4 texSpecular0 = g_Specular0.Sample(g_Specular0Sampler, pin.UV.xy);\n    float4 texNormal0 = g_Normal0.Sample(g_Normal0Sampler, pin.UV.xy);\n\n    return ComputeCommon(pin, texDiffuse0, float4(texSpecular0.rrr, 1), (texNormal0.xyz - 0.5) * 2.0, pin.Tangent1WS.xyz);\n}\n\nfloat4 PSDual(VSOutput pin) : SV_Target0\n{\n    float4 texDiffuse0 = g_Diffuse0.Sample(g_Diffuse0Sampler, pin.UV.xy);\n    float4 texNormal0 = g_Normal0.Sample(g_Normal0Sampler, pin.UV.xy);\n\n    float4 texDiffuse1 = g_Diffuse1.Sample(g_Diffuse1Sampler, pin.UV.zw);\n    float4 texNormal1 = g_Normal1.Sample(g_Normal1Sampler, pin.UV.zw);\n\n    if (IsDummy(texDiffuse1))\n    {\n        texDiffuse1 = texDiffuse0;\n    }\n    if (IsDummy(texNormal1))\n    {\n        texNormal1 = texNormal0;\n    }\n\n    float4 diffuse = lerp(texDiffuse0, texDiffuse1, pin.Color.w);\n    float3 bump = (texNormal0.xyz - 0.5) * 2.0;// lerp((texNormal0.xyz - 0.5) * 2.0, (texNormal1.xyz - 0.5) * 2.0, pin.Color.w);\n    float4 specular = (0).xxxx;\n    float3 tangent = lerp(pin.Tangent1WS.xyz, pin.Tangent2WS.xyz, pin.Color.w);\n\n    return ComputeCommon(pin, diffuse, specular, bump, tangent);\n}\n\nfloat4 PSDualSpecular(VSOutput pin) : SV_Target0\n{\n    float4 texDiffuse0 = g_Diffuse0.Sample(g_Diffuse0Sampler, pin.UV.xy);\n    float4 texNormal0 = g_Normal0.Sample(g_Normal0Sampler, pin.UV.xy);\n    float4 texSpecular0 = g_Specular0.Sample(g_Specular0Sampler, pin.UV.xy);\n\n    float4 texDiffuse1 = g_Diffuse1.Sample(g_Diffuse1Sampler, pin.UV.zw);\n    float4 texNormal1 = g_Normal1.Sample(g_Normal1Sampler, pin.UV.zw);\n    float4 texSpecular1 = g_Specular1.Sample(g_Specular1Sampler, pin.UV.zw);\n\n    if (IsDummy(texDiffuse1))\n    {\n        texDiffuse1 = texDiffuse0;\n    }\n    if (IsDummy(texNormal1))\n    {\n        texNormal1 = texNormal0;\n    }\n    if (IsDummy(texSpecular1))\n    {\n        texSpecular1 = texSpecular0;\n    }\n\n    float4 diffuse = lerp(texDiffuse0, texDiffuse1, pin.Color.w);\n    float3 bump = (texNormal0.xyz - 0.5) * 2.0;// lerp((texNormal0.xyz - 0.5) * 2.0, (texNormal1.xyz - 0.5) * 2.0, pin.Color.w);\n    float3 specular = lerp(texSpecular0.rrr, texSpecular1.rrr, pin.Color.w);\n    float3 tangent = lerp(pin.Tangent1WS.xyz, pin.Tangent2WS.xyz, pin.Color.w);\n\n    return ComputeCommon(pin, diffuse, float4(specular, 1), bump, pin.Tangent1WS.xyz);\n}\n\ntechnique11 Single\n{\n    pass P0 {\n        SetGeometryShader(0);\n        SetVertexShader(CompileShader(vs_4_0, VSUvScroll()));\n        SetPixelShader(CompileShader(ps_4_0, PSSingle()));\n    }\n}\ntechnique11 SingleSpecular\n{\n    pass P0 {\n        SetGeometryShader(0);\n        SetVertexShader(CompileShader(vs_4_0, VSUvScroll()));\n        SetPixelShader(CompileShader(ps_4_0, PSSingleSpecular()));\n    }\n}\ntechnique11 Dual\n{\n    pass P0 {\n        SetGeometryShader(0);\n        SetVertexShader(CompileShader(vs_4_0, VSUvScroll()));\n        SetPixelShader(CompileShader(ps_4_0, PSDual()));\n    }\n}\ntechnique11 DualSpecular\n{\n    pass P0 {\n        SetGeometryShader(0);\n        SetVertexShader(CompileShader(vs_4_0, VSUvScroll()));\n        SetPixelShader(CompileShader(ps_4_0, PSDualSpecular()));\n    }\n}"
  },
  {
    "path": "SaintCoinach.Graphics.Viewer/Effects/HLSL/Character.fx",
    "content": "#include \"Structures.fxh\"\n\nTexture2D<float4> g_Diffuse     : register(t0);\nTexture2D<float4> g_Normal      : register(t1);\nTexture2D<float4> g_Specular    : register(t2);\nTexture2D<float4> g_Mask        : register(t3);\nTexture2D<float4> g_Table       : register(t4);\n\nsampler g_DiffuseSampler        : register(s0)\n{\n    AddressU = Wrap;\n    AddressV = Wrap;\n    Filter = MIN_MAG_MIP_LINEAR;\n};\nsampler g_NormalSampler         : register(s1)\n{\n    AddressU = Wrap;\n    AddressV = Wrap;\n    Filter = MIN_MAG_MIP_LINEAR;\n};\nsampler g_NormalPointSampler    : register(s2)\n{\n    AddressU = Wrap;\n    AddressV = Wrap;\n    Filter = MIN_MAG_MIP_POINT;\n};\nsampler g_SpecularSampler       : register(s3)\n{\n    AddressU = Wrap;\n    AddressV = Wrap;\n    Filter = MIN_MAG_MIP_LINEAR;\n};\nsampler g_MaskSampler           : register(s4)\n{\n    AddressU = Wrap;\n    AddressV = Wrap;\n    Filter = MIN_MAG_MIP_LINEAR;\n};\nsampler g_TableSampler          : register(s5)\n{\n    AddressU = Wrap;\n    AddressV = Wrap;\n    Filter = MIN_MAG_MIP_LINEAR;\n};\n\n#include \"Common.Skinned.fxh\"\n#include \"Lighting.fxh\"\n\nstruct TableSamples\n{\n    float4 Diffuse;\n    float4 Specular;\n    float4 Blend;\n};\n\n\nTableSamples GetTableSamples(float2 uv)\n{\n    float4 texNormalPoint = g_Normal.Sample(g_NormalPointSampler, uv);\n\n        int tableKey = floor(texNormalPoint.a * 15);\n    float tableY = (tableKey + 0.5) * 0.0625;\n\n    float2 diffuseUV = float2(0.125, tableY);\n        float2 specularUV = float2(0.375, tableY);\n        float2 blendUV = float2(0.625, tableY);\n\n        TableSamples samples;\n\n    samples.Diffuse = g_Table.Sample(g_TableSampler, diffuseUV);\n    samples.Specular = g_Table.Sample(g_TableSampler, specularUV);\n    samples.Blend = g_Table.Sample(g_TableSampler, blendUV);\n\n    return samples;\n};\n\nfloat ApplyTable(float2 uv, inout float4 diffuse, inout float4 specular, float specBlendFactor)\n{\n    TableSamples table = GetTableSamples(uv);\n\n    diffuse.rgb *= lerp(table.Diffuse.rgb, table.Specular.rgb * specBlendFactor, table.Blend.w);\n    specular.rgb *= lerp(table.Specular.rgb, specular.rgb, table.Blend.w);\n\n    return table.Specular.w;\n};\n\nfloat4 ComputeCommon(VSOutput pin, float4 diffuse, float4 specular)\n{\n    float4 texNormal = g_Normal.Sample(g_NormalSampler, pin.UV.xy);\n        float a = texNormal.b;\n    clip(a <= 0.5 ? -1 : 1);\n    float3 bump = (texNormal.xyz - 0.5) * 2.0;\n\n        float3 binorm = cross(pin.NormalWS.xyz, pin.Tangent1WS.xyz);\n        float3 bumpNormal = (bump.x * pin.Tangent1WS) + (bump.y * binorm) + (bump.z * pin.NormalWS);\n        bumpNormal = normalize(bumpNormal);\n\n    float3 eyeVector = normalize(m_EyePosition - pin.PositionWS);\n        Lighting light = GetLight(m_EyePosition, eyeVector, bumpNormal);\n\n    float4 color = float4(diffuse.rgb, a);\n\n        color.rgb *= light.Diffuse.rgb;\n    color.rgb += light.Specular.rgb * specular.rgb * color.a;\n    return color;\n};\n\nfloat4 PSDiffuse(VSOutput pin) : SV_Target0\n{\n    float4 texDiffuse = g_Diffuse.Sample(g_DiffuseSampler, pin.UV.xy);\n\n    return ComputeCommon(pin, texDiffuse, (0).xxxx);\n}\nfloat4 PSDiffuseSpecular(VSOutput pin) : SV_Target0\n{\n    float4 texDiffuse = g_Diffuse.Sample(g_DiffuseSampler, pin.UV.xy);\n    float4 texSpecular = g_Specular.Sample(g_SpecularSampler, pin.UV.xy);\n\n    return ComputeCommon(pin, texDiffuse, float4(texSpecular.ggg, 1));\n};\nfloat4 PSDiffuseSpecularTable(VSOutput pin) : SV_Target0\n{\n    float4 texDiffuse = g_Diffuse.Sample(g_DiffuseSampler, pin.UV.xy);\n    float4 texSpecular = g_Specular.Sample(g_SpecularSampler, pin.UV.xy);\n\n    float4 diffuse = texDiffuse;\n    diffuse.a = 1;\n    float4 specular = float4(texSpecular.rrr, 1);\n        ApplyTable(pin.UV.xy, diffuse, specular, texSpecular.g);\n\n    return ComputeCommon(pin, diffuse, specular);\n};\nfloat4 PSDiffuseTable(VSOutput pin) : SV_Target0\n{\n    float4 texDiffuse = g_Diffuse.Sample(g_DiffuseSampler, pin.UV.xy);\n\n    float4 diffuse = texDiffuse;\n    float4 specular = (1).xxxx;\n    ApplyTable(pin.UV.xy, diffuse, specular, 1);\n\n    return ComputeCommon(pin, diffuse, specular);\n};\nfloat4 PSMaskTable(VSOutput pin) : SV_Target0\n{\n    float4 texMask = g_Mask.Sample(g_MaskSampler, pin.UV.xy);\n\n    float4 diffuse = (1).xxxx;\n    float4 specular = (1).xxxx;\n\n    float specPow = ApplyTable(pin.UV.xy, diffuse, specular, texMask.g);\n\n    diffuse.rgb *= texMask.r;\n    specular.rgb *= texMask.b;\n\n    return ComputeCommon(pin, diffuse, specular);\n};\nfloat4 PSMask(VSOutput pin) : SV_Target0\n{\n    float4 texMask = g_Mask.Sample(g_MaskSampler, pin.UV.xy);\n\n    float4 diffuse = texMask;\n    float4 specular = (1).xxxx;\n\n    return ComputeCommon(pin, diffuse, specular);\n};\n\ntechnique11 Diffuse\n{\n    pass P0 {\n        SetGeometryShader(0);\n        SetVertexShader(CompileShader(vs_4_0, VSCommon()));\n        SetPixelShader(CompileShader(ps_4_0, PSDiffuse()));\n    }\n}\ntechnique11 DiffuseSpecular\n{\n    pass P0 {\n        SetGeometryShader(0);\n        SetVertexShader(CompileShader(vs_4_0, VSCommon()));\n        SetPixelShader(CompileShader(ps_4_0, PSDiffuseSpecular()));\n    }\n}\ntechnique11 DiffuseSpecularTable\n{\n    pass P0 {\n        SetGeometryShader(0);\n        SetVertexShader(CompileShader(vs_4_0, VSCommon()));\n        SetPixelShader(CompileShader(ps_4_0, PSDiffuseSpecularTable()));\n    }\n}\ntechnique11 DiffuseTable\n{\n    pass P0 {\n        SetGeometryShader(0);\n        SetVertexShader(CompileShader(vs_4_0, VSCommon()));\n        SetPixelShader(CompileShader(ps_4_0, PSDiffuseTable()));\n    }\n}\ntechnique11 MaskTable\n{\n    pass P0 {\n        SetGeometryShader(0);\n        SetVertexShader(CompileShader(vs_4_0, VSCommon()));\n        SetPixelShader(CompileShader(ps_4_0, PSMaskTable()));\n    }\n}\ntechnique11 Mask\n{\n    pass P0 {\n        SetGeometryShader(0);\n        SetVertexShader(CompileShader(vs_4_0, VSCommon()));\n        SetPixelShader(CompileShader(ps_4_0, PSMask()));\n    }\n}"
  },
  {
    "path": "SaintCoinach.Graphics.Viewer/Effects/HLSL/Common.Skinned.fxh",
    "content": "#define MAX_BONES 64\n\nrow_major float4x4 g_World;\nrow_major float4x4 g_WorldInverseTranspose;\nrow_major float4x4 g_WorldViewProjection;\n\ncbuffer g_CameraParameters : register(b0)\n{\n    row_major float3x4 m_View                       : packoffset(c0);\n    row_major float4x3 m_ViewInverse                : packoffset(c4);\n    row_major float4x4 m_ViewProjection             : packoffset(c8);\n    row_major float4x4 m_ViewProjectionInverse      : packoffset(c12);\n    row_major float4x4 m_Projection                 : packoffset(c16);\n    row_major float4x4 m_ProjectionInverse          : packoffset(c20);\n\n    float3 m_EyePosition                            : packoffset(c24);\n    float4x3 g_JointMatrixArray[64]       : packoffset(c25);\n};\n\nvoid ApplySkinning(inout VSInput vin)\n{\n    float4 pos = 0;\n    float3 norm = 0;\n    float3 t1 = 0;\n    float3 t2 = 0;\n\n    [unroll]\n    for(int i = 0; i < 4; i++)\n    {\n        float4x3 joint = g_JointMatrixArray[vin.BlendIndices[i]];\n        float w = vin.BlendWeight[i];\n\n        pos.xyz += mul(vin.Position, joint) * w;\n        norm += mul(vin.Normal, joint) * w;\n        t1 += mul(vin.Tangent1, joint) * w;\n        t2 += mul(vin.Tangent2, joint) * w;\n    }\n\n    vin.Position.xyz = pos.xyz;\n\n    vin.Normal = normalize(norm);\n    vin.Tangent1.xyz = normalize(t1);\n    vin.Tangent2.xyz = normalize(t2);\n};\n\nVSOutput VSCommon(VSInput vin)\n{\n    VSOutput vout;\n    \n    vout.UV = vin.UV;\n\n    vin.Tangent1 = (vin.Tangent1 - 0.5) * 2.0;\n    vin.Tangent2 = (vin.Tangent2 - 0.5) * 2.0;\n\n    ApplySkinning(vin);\n\n    vout.PositionPS = mul(vin.Position, g_WorldViewProjection);\n    vout.PositionWS = mul(vin.Position, g_World);\n    vout.NormalWS = normalize(mul(vin.Normal, g_WorldInverseTranspose));\n\n    vout.UV = vin.UV;\n    vout.BlendWeight = vin.BlendWeight;\n    vout.BlendIndices = vin.BlendIndices;\n    vout.Color = vin.Color;\n\n    // Going to pretend these are tangents\n    vout.Tangent1WS.xyz = normalize(mul(vin.Tangent1.xyz, g_WorldInverseTranspose));\n    vout.Tangent1WS.w = vin.Tangent1.w;\n\n    vout.Tangent2WS.xyz = normalize(mul(vin.Tangent2.xyz, g_WorldInverseTranspose));\n    vout.Tangent2WS.w = vin.Tangent2.w;\n    \n    return vout;\n};"
  },
  {
    "path": "SaintCoinach.Graphics.Viewer/Effects/HLSL/Common.fxh",
    "content": "row_major float4x4 g_World;\nrow_major float4x4 g_WorldInverseTranspose;\nrow_major float4x4 g_WorldViewProjection;\n\ncbuffer g_CameraParameters : register(b0)\n{\n    row_major float3x4 m_View                       : packoffset(c0);\n    row_major float4x3 m_ViewInverse                : packoffset(c4);\n    row_major float4x4 m_ViewProjection             : packoffset(c8);\n    row_major float4x4 m_ViewProjectionInverse      : packoffset(c12);\n    row_major float4x4 m_Projection                 : packoffset(c16);\n    row_major float4x4 m_ProjectionInverse          : packoffset(c20);\n\n    float3 m_EyePosition                            : packoffset(c24);\n};\n\nVSOutput VSCommon(VSInput vin)\n{\n    VSOutput vout;\n    \n    vout.UV = vin.UV;\n\n    vout.PositionPS = mul(vin.Position, g_WorldViewProjection);\n    vout.PositionWS = mul(vin.Position, g_World);\n    vout.NormalWS = normalize(mul(vin.Normal, g_WorldInverseTranspose));\n\n    vout.UV = vin.UV;\n    vout.BlendWeight = vin.BlendWeight;\n    vout.BlendIndices = vin.BlendIndices;\n    vout.Color = vin.Color;\n\n    // Going to pretend these are tangents\n    float4 t1 = (vin.Tangent1 - 0.5) * 2.0;\n    vout.Tangent1WS.xyz = normalize(mul(t1.xyz, g_WorldInverseTranspose));\n    vout.Tangent1WS.w = t1.w;\n\n    float4 t2 = (vin.Tangent2 - 0.5) * 2.0;\n    vout.Tangent2WS.xyz = normalize(mul(t2.xyz, g_WorldInverseTranspose));\n    vout.Tangent2WS.w = t2.w;\n    \n    return vout;\n};"
  },
  {
    "path": "SaintCoinach.Graphics.Viewer/Effects/HLSL/Crystal.fx",
    "content": "#include \"Structures.fxh\"\n\nTexture2D<float4> g_ColorMap0    : register(t0);\nTexture2D<float4> g_NormalMap0   : register(t1);\nTexture2D<float4> g_SpecularMap0 : register(t2);\nTextureCube<float4> g_EnvMap     : register(t3);\n\nsampler g_ColorMap0Sampler       : register(s0)\n{\n    AddressU = Wrap;\n    AddressV = Wrap;\n    Filter = MIN_MAG_MIP_LINEAR;\n};\nsampler g_Normal0MapSampler      : register(s1)\n{\n    AddressU = Wrap;\n    AddressV = Wrap;\n    Filter = MIN_MAG_MIP_LINEAR;\n};\nsampler g_Specular0MapSampler    : register(s2)\n{\n    AddressU = Wrap;\n    AddressV = Wrap;\n    Filter = MIN_MAG_MIP_LINEAR;\n};\nsampler g_EnvMapSampler          : register(s3)\n{\n    AddressU = Clamp;\n    AddressV = Clamp;\n    AddressW = Clamp;\n    Filter = MIN_MAG_MIP_LINEAR;\n};\n\n#include \"Common.fxh\"\n#include \"Lighting.fxh\"\n\nfloat4 ComputeCommon(VSOutput pin, float4 diffuse, float4 specular, float3 bump, float3 tangent)\n{\n    float3 binorm = cross(pin.NormalWS.xyz, tangent.xyz);\n    float3 bumpNormal = (bump.x * tangent) + (bump.y * binorm) + (bump.z * pin.NormalWS);\n    bumpNormal = normalize(bumpNormal);\n\n    float3 eyeVector = normalize(m_EyePosition - pin.PositionWS);\n    Lighting light = GetLight(m_EyePosition, eyeVector, bumpNormal);\n\n    float4 color = (1).xxxx;\n    color.rgb = diffuse.rgb;\n    color.rgb *= light.Diffuse.rgb;\n    color.rgb += light.Specular.rgb * specular.rgb * color.a;\n    return color;\n};\n\nfloat4 PSCrystal(VSOutput pin) : SV_Target0\n{\n    float3 viewVector = normalize(m_EyePosition - pin.PositionWS.xyz);\n    float3 reflection = normalize(reflect(viewVector, pin.NormalWS));\n\n    float4 texColor0 = g_ColorMap0.Sample(g_ColorMap0Sampler, pin.UV.xy);\n    float4 texNormal0 = g_NormalMap0.Sample(g_Normal0MapSampler, pin.UV.xy);\n    float4 texSpecular0 = g_SpecularMap0.Sample(g_Specular0MapSampler, pin.UV.xy);\n    float4 texEnvMap = g_EnvMap.Sample(g_EnvMapSampler, reflection);\n\n    // TODO: Get EnvMap to work.\n\n    return ComputeCommon(pin, texColor0, texSpecular0, (texNormal0.xyz - 0.5) * 2.0, pin.Tangent1WS.xyz);\n}\n\ntechnique11 Crystal\n{\n    pass P0 {\n        SetGeometryShader(0);\n        SetVertexShader(CompileShader(vs_4_0, VSCommon()));\n        SetPixelShader(CompileShader(ps_4_0, PSCrystal()));\n    }\n}"
  },
  {
    "path": "SaintCoinach.Graphics.Viewer/Effects/HLSL/CustomizeParameter.fxh",
    "content": "\ncbuffer g_CustomizeParameters : register(b2)\n{\n    float4 m_SkinColor      : packoffset(c0);\n    float4 m_LipColor       : packoffset(c1);\n    float3 m_RightColor     : packoffset(c2);\n    float3 m_HairColor      : packoffset(c3);\n    float3 m_MeshColor      : packoffset(c4);\n    float3 m_LeftColor      : packoffset(c5);\n};"
  },
  {
    "path": "SaintCoinach.Graphics.Viewer/Effects/HLSL/Hair.fx",
    "content": "#include \"Structures.fxh\"\n\nTexture2D<float4> g_Normal      : register(t1);\nTexture2D<float4> g_Mask        : register(t2);\n\nsampler g_NormalSampler         : register(s1)\n{\n    AddressU = Wrap;\n    AddressV = Wrap;\n    Filter = MIN_MAG_MIP_LINEAR;\n};\nsampler g_MaskSampler           : register(s2)\n{\n    AddressU = Wrap;\n    AddressV = Wrap;\n    Filter = MIN_MAG_MIP_LINEAR;\n};\n\n#include \"Common.Skinned.fxh\"\n#include \"Lighting.fxh\"\n#include \"CustomizeParameter.fxh\"\n\n/*\nfloat4 ComputeCommon(VSOutput pin, float4 diffuse, float4 specular)\n{\n    float4 texNormal = g_Normal.Sample(g_NormalSampler, pin.UV);\n\n    float3 normal = CalculateNormal(pin.WorldNormal, pin.WorldTangent, pin.WorldBinormal, texNormal.xyz);\n\n    float3 eyeVector = normalize(m_EyePosition - pin.PositionWS);\n    LightResult light = ComputeLights(eyeVector, normal, 3);\n\n    float4 color = float4(diffuse.rgb, texNormal.a);\n\n    color.rgb *= light.Diffuse.rgb;\n    color.rgb += light.Specular.rgb * specular.rgb * color.a;\n\n    return color;\n};*/\n\nfloat4 ComputeCommon(VSOutput pin, float4 diffuse, float4 specular)\n{\n    float4 texNormal = g_Normal.Sample(g_NormalSampler, pin.UV.xy);\n    float a = texNormal.a;\n    clip(a <= 0.5 ? -1 : 1);\n    float3 bump = (texNormal.xyz - 0.5) * 2.0;\n\n    float3 binorm = cross(pin.NormalWS.xyz, pin.Tangent1WS.xyz);\n    float3 bumpNormal = (bump.x * pin.Tangent1WS) + (bump.y * binorm) + (bump.z * pin.NormalWS);\n    bumpNormal = normalize(bumpNormal);\n\n    float3 eyeVector = normalize(m_EyePosition - pin.PositionWS);\n    Lighting light = GetLight(m_EyePosition, eyeVector, bumpNormal);\n\n    float4 color = float4(diffuse.rgb, a);\n\n    color.rgb *= light.Diffuse.rgb;\n    color.rgb += light.Specular.rgb * specular.rgb * color.a;\n\n    return color;\n};\n\nfloat4 PSHair(VSOutput pin) : SV_Target0\n{\n    float4 texMask = g_Mask.Sample(g_MaskSampler, pin.UV.xy);\n\n    float4 diffuse = float4(lerp(texMask.x * m_HairColor, texMask.y * m_MeshColor, texMask.w), 1);\n    float4 specular = (0.1).xxxx;\n\n    return ComputeCommon(pin, diffuse, specular);\n}\n\ntechnique11 Hair\n{\n    pass P0 {\n        SetGeometryShader(0);\n        SetVertexShader(CompileShader(vs_4_0, VSCommon()));\n        SetPixelShader(CompileShader(ps_4_0, PSHair()));\n    }\n}"
  },
  {
    "path": "SaintCoinach.Graphics.Viewer/Effects/HLSL/Iris.fx",
    "content": "#include \"Structures.fxh\"\n\nTexture2D<float4> g_Normal      : register(t1);\nTexture2D<float4> g_Mask        : register(t2);\n\nsampler g_NormalSampler         : register(s1)\n{\n    AddressU = Wrap;\n    AddressV = Wrap;\n    Filter = MIN_MAG_MIP_LINEAR;\n};\nsampler g_MaskSampler           : register(s2)\n{\n    AddressU = Wrap;\n    AddressV = Wrap;\n    Filter = MIN_MAG_MIP_LINEAR;\n};\n\n#include \"Common.Skinned.fxh\"\n#include \"Lighting.fxh\"\n#include \"CustomizeParameter.fxh\"\n\n/*\nfloat4 ComputeCommon(VSOutput pin, float4 diffuse, float4 specular)\n{\nfloat4 texNormal = g_Normal.Sample(g_NormalSampler, pin.UV);\n\nfloat3 normal = CalculateNormal(pin.WorldNormal, pin.WorldTangent, pin.WorldBinormal, texNormal.xyz);\n\nfloat3 eyeVector = normalize(m_EyePosition - pin.PositionWS);\nLightResult light = ComputeLights(eyeVector, normal, 3);\n\nfloat4 color = float4(diffuse.rgb, texNormal.a);\n\ncolor.rgb *= light.Diffuse.rgb;\ncolor.rgb += light.Specular.rgb * specular.rgb * color.a;\n\nreturn color;\n};*/\n\nfloat4 ComputeCommon(VSOutput pin, float4 diffuse, float4 specular)\n{\n    float4 texNormal = g_Normal.Sample(g_NormalSampler, pin.UV.xy);\n    float a = texNormal.a;\n    clip(a <= 0.5 ? -1 : 1);\n    float3 bump = (texNormal.xyz - 0.5) * 2.0;\n\n    float3 binorm = cross(pin.NormalWS.xyz, pin.Tangent1WS.xyz);\n    float3 bumpNormal = (bump.x * pin.Tangent1WS) + (bump.y * binorm) + (bump.z * pin.NormalWS);\n    bumpNormal = normalize(bumpNormal);\n\n    float3 eyeVector = normalize(m_EyePosition - pin.PositionWS);\n    Lighting light = GetLight(m_EyePosition, eyeVector, bumpNormal);\n\n    float4 color = float4(diffuse.rgb, a);\n\n    color.rgb *= light.Diffuse.rgb;\n    color.rgb += light.Specular.rgb * specular.rgb * color.a;\n\n    return color;\n};\n\nfloat4 PSIris(VSOutput pin) : SV_Target0\n{\n    float4 texMask = g_Mask.Sample(g_MaskSampler, pin.UV.xy);\n    float4 texNormal = g_Normal.Sample(g_NormalSampler, pin.UV.xy);\n\n    float4 diffuse = float4(0, 0, 0, 1);\n    diffuse.rgb = texMask.x * (pin.Color.x * m_LeftColor.rgb + pin.Color.y * m_RightColor.rgb);\n    float4 specular = (0.75).xxxx;\n\n    return ComputeCommon(pin, diffuse, specular);\n}\n\ntechnique11 Iris\n{\n    pass P0 {\n        SetGeometryShader(0);\n        SetVertexShader(CompileShader(vs_4_0, VSCommon()));\n        SetPixelShader(CompileShader(ps_4_0, PSIris()));\n    }\n}"
  },
  {
    "path": "SaintCoinach.Graphics.Viewer/Effects/HLSL/Lighting.fxh",
    "content": "cbuffer g_LightingParameters : register(b1)\n{\n    float4 m_DiffuseColor     : packoffset(c0);\n    float3 m_EmissiveColor    : packoffset(c1);\n    float3 m_AmbientColor     : packoffset(c2);\n    float3 m_SpecularColor    : packoffset(c3);\n    float  m_SpecularPower    : packoffset(c3.w);\n\n    DirectionalLight m_Light0 : packoffset(c4);\n    DirectionalLight m_Light1 : packoffset(c7);\n    DirectionalLight m_Light2 : packoffset(c10);\n};\n \n\nLighting GetLight( float3 pos3D, float3 eyeVector, float3 worldNormal )\n{\n    float3x3 lightDirections = 0;\n    float3x3 lightDiffuse = 0;\n    float3x3 lightSpecular = 0;\n    float3x3 halfVectors = 0;\n    \n    [unroll]\n    for (int i = 0; i < 3; i++)\n    {\n        lightDirections[i] = float3x3(m_Light0.Direction,     m_Light1.Direction,     m_Light2.Direction)    [i];\n        lightDiffuse[i]    = float3x3(m_Light0.Diffuse.xyz,   m_Light1.Diffuse.xyz,   m_Light2.Diffuse.xyz)  [i];\n        lightSpecular[i]   = float3x3(m_Light0.Specular.xyz,  m_Light1.Specular.xyz,  m_Light2.Specular.xyz) [i];\n        \n        halfVectors[i] = normalize(eyeVector - lightDirections[i]);\n    }\n\n    float3 dotL = mul(-lightDirections, worldNormal);\n    float3 dotH = mul(halfVectors, worldNormal);\n    \n    float3 zeroL = step(0, dotL);\n\n    float3 diffuse  = zeroL * dotL;\n    float3 specular = pow(max(dotH, 0) * zeroL, m_SpecularPower);\n\n    Lighting result;\n    \n    result.Diffuse  = mul(diffuse,  lightDiffuse)  * m_DiffuseColor.rgb + m_EmissiveColor;\n    result.Specular = mul(specular, lightSpecular) * m_SpecularColor;\n\n    return result;\n};"
  },
  {
    "path": "SaintCoinach.Graphics.Viewer/Effects/HLSL/Skin.fx",
    "content": "#include \"Structures.fxh\"\n\nTexture2D<float4> g_Diffuse     : register(t0);\nTexture2D<float4> g_Normal      : register(t1);\nTexture2D<float4> g_Mask        : register(t2);\n\nsampler g_DiffuseSampler        : register(s0)\n{\n    AddressU = Wrap;\n    AddressV = Wrap;\n    Filter = MIN_MAG_MIP_LINEAR;\n};\nsampler g_NormalSampler         : register(s1)\n{\n    AddressU = Wrap;\n    AddressV = Wrap;\n    Filter = MIN_MAG_MIP_LINEAR;\n};\nsampler g_MaskSampler           : register(s2)\n{\n    AddressU = Wrap;\n    AddressV = Wrap;\n    Filter = MIN_MAG_MIP_LINEAR;\n};\n\n#include \"Common.Skinned.fxh\"\n#include \"Lighting.fxh\"\n#include \"CustomizeParameter.fxh\"\n\n/*\nfloat4 ComputeCommon(VSOutputCommon pin, float4 diffuse, float4 specular)\n{\nfloat4 texNormal = g_Normal.Sample(g_NormalSampler, pin.TexCoord);\n\nfloat3 normal = CalculateNormal(pin.WorldNormal, pin.WorldTangent, pin.WorldBinormal, texNormal.xyz);\n\nfloat3 eyeVector = normalize(m_EyePosition - pin.PositionWS);\nLightResult light = ComputeLights(eyeVector, normal, 3);\n\nfloat4 color = float4(diffuse.rgb, texNormal.a);\n\ncolor.rgb *= light.Diffuse.rgb;\ncolor.rgb += light.Specular.rgb * specular.rgb * color.a;\n\nreturn color;\n};*/\n\nfloat4 ComputeCommon(VSOutput pin, float4 diffuse, float4 specular)\n{\n    float4 texNormal = g_Normal.Sample(g_NormalSampler, pin.UV.xy);\n    float a = texNormal.b;\n    clip(a <= 0.5 ? -1 : 1);\n    float3 bump = (texNormal.xyz - 0.5) * 2.0;\n\n    float3 binorm = cross(pin.NormalWS.xyz, pin.Tangent1WS.xyz);\n    float3 bumpNormal = (bump.x * pin.Tangent1WS) + (bump.y * binorm) + (bump.z * pin.NormalWS);\n    bumpNormal = normalize(bumpNormal);\n\n    float3 eyeVector = normalize(m_EyePosition - pin.PositionWS);\n    Lighting light = GetLight(m_EyePosition, eyeVector, bumpNormal);\n\n    float4 color = float4(diffuse.rgb, a);\n\n    color.rgb *= light.Diffuse.rgb;\n    color.rgb += light.Specular.rgb * specular.rgb * color.a;\n\n    return color;\n};\n\nfloat4 PSSkin(VSOutput pin) : SV_Target0\n{\n    float4 texDiffuse = g_Diffuse.Sample(g_DiffuseSampler, pin.UV.xy);\n    float4 texMask = g_Mask.Sample(g_MaskSampler, pin.UV.xy);\n\n    float3 diffSq = texDiffuse.rgb * texDiffuse.rgb;\n    float3 skinMask = m_SkinColor.rgb * texMask.r;\n    float3 skinMaskSq = skinMask * diffSq;\n\n    float3 lip = m_LipColor.rgb;\n    float3 lipAmount = texMask.b * m_LipColor.a;\n\n    float3 f = lerp(skinMask, lip, lipAmount);\n\n    float4 diffuse = float4(f, 1);\n\n    float4 specular = (0.1).xxxx;\n\n    return ComputeCommon(pin, diffuse, specular);\n}\n\ntechnique11 Skin\n{\n    pass P0 {\n        SetGeometryShader(0);\n        SetVertexShader(CompileShader(vs_4_0, VSCommon()));\n        SetPixelShader(CompileShader(ps_4_0, PSSkin()));\n    }\n}"
  },
  {
    "path": "SaintCoinach.Graphics.Viewer/Effects/HLSL/Structures.fxh",
    "content": "struct Lighting\n{\n    float3 Diffuse;\n    float3 Specular;\n};\n \nstruct DirectionalLight\n{\n\tfloat3 Direction;\n\tfloat4 Diffuse;\n\tfloat4 Specular;\n};\n\nstruct VSInput \n{\n    float4 Position     : SV_Position;\n    float3 Normal       : NORMAL;\n    float4 UV           : TEXCOORD0;\n    float4 BlendWeight  : BLENDWEIGHT;\n    int4   BlendIndices : BLENDINDICES;\n    float4 Color        : COLOR;\n    float4 Tangent1     : TANGENT0;\n    float4 Tangent2     : TANGENT1;\n};\n\nstruct VSOutput\n{\n    float4 PositionPS   : SV_Position;\n    float3 PositionWS   : POSITIONT;\n    float3 NormalWS     : NORMAL;\n\n    float4 UV           : TEXCOORD0;\n    float4 BlendWeight  : BLENDWEIGHT;\n    int4   BlendIndices : BLENDINDICES;\n    float4 Color        : COLOR;\n    float4 Tangent1WS   : TEXCOORD5;\n    float4 Tangent2WS   : TEXCOORD6;\n};"
  },
  {
    "path": "SaintCoinach.Graphics.Viewer/Effects/HairEffect.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace SaintCoinach.Graphics.Viewer.Effects {\n    using SharpDX;\n    using SharpDX.D3DCompiler;\n    using SharpDX.Direct3D11;\n\n    public class HairEffect : SkinnedEffect {\n        #region Fields\n        private EffectShaderResourceVariable _NormalVar;\n        private EffectShaderResourceVariable _MaskVar;\n        private CustomizeParameterEffectVariable _CustomizeParameterVar;\n        #endregion\n\n        #region Properties\n        public ShaderResourceView Normal {\n            set { _NormalVar.SetResource(value); }\n        }\n        public ShaderResourceView Mask {\n            set { _MaskVar.SetResource(value); }\n        }\n        public Data.CustomizeParameters CustomizeParameters {\n            set { _CustomizeParameterVar.Set(value); }\n        }\n        #endregion\n\n        #region Constructor\n        public HairEffect(Device device, byte[] byteCode) : this(device, byteCode, EffectFlags.None) { }\n        public HairEffect(Device device, byte[] byteCode, EffectFlags flags)\n            : base(device, byteCode, flags) {\n            Init();\n        }\n        #endregion\n\n        #region Init\n        private void Init() {\n            _NormalVar = GetVariableByName(\"g_Normal\").AsShaderResource();\n            _MaskVar = GetVariableByName(\"g_Mask\").AsShaderResource();\n\n            _CustomizeParameterVar = new CustomizeParameterEffectVariable(this);\n        }\n        #endregion\n    }\n}\n"
  },
  {
    "path": "SaintCoinach.Graphics.Viewer/Effects/IrisEffect.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace SaintCoinach.Graphics.Viewer.Effects {\n    using SharpDX;\n    using SharpDX.D3DCompiler;\n    using SharpDX.Direct3D11;\n\n    public class IrisEffect : SkinnedEffect {\n        #region Fields\n        private EffectShaderResourceVariable _NormalVar;\n        private EffectShaderResourceVariable _MaskVar;\n        private CustomizeParameterEffectVariable _CustomizeParameterVar;\n        #endregion\n\n        #region Properties\n        public ShaderResourceView Normal {\n            set { _NormalVar.SetResource(value); }\n        }\n        public ShaderResourceView Mask {\n            set { _MaskVar.SetResource(value); }\n        }\n        public Data.CustomizeParameters CustomizeParameters {\n            set { _CustomizeParameterVar.Set(value); }\n        }\n        #endregion\n\n        #region Constructor\n        public IrisEffect(Device device, byte[] byteCode) : this(device, byteCode, EffectFlags.None) { }\n        public IrisEffect(Device device, byte[] byteCode, EffectFlags flags)\n            : base(device, byteCode, flags) {\n            Init();\n        }\n        #endregion\n\n        #region Init\n        private void Init() {\n            _NormalVar = GetVariableByName(\"g_Normal\").AsShaderResource();\n            _MaskVar = GetVariableByName(\"g_Mask\").AsShaderResource();\n\n            _CustomizeParameterVar = new CustomizeParameterEffectVariable(this);\n        }\n        #endregion\n    }\n}\n"
  },
  {
    "path": "SaintCoinach.Graphics.Viewer/Effects/SkinEffect.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace SaintCoinach.Graphics.Viewer.Effects {\n    using SharpDX;\n    using SharpDX.D3DCompiler;\n    using SharpDX.Direct3D11;\n\n    public class SkinEffect : SkinnedEffect {\n        #region Fields\n        private EffectShaderResourceVariable _DiffuseVar;\n        private EffectShaderResourceVariable _NormalVar;\n        private EffectShaderResourceVariable _MaskVar;\n        private CustomizeParameterEffectVariable _CustomizeParameterVar;\n        #endregion\n\n        #region Properties\n        public ShaderResourceView Diffuse {\n            set { _DiffuseVar.SetResource(value); }\n        }\n        public ShaderResourceView Normal {\n            set { _NormalVar.SetResource(value); }\n        }\n        public ShaderResourceView Mask {\n            set { _MaskVar.SetResource(value); }\n        }\n        public Data.CustomizeParameters CustomizeParameters {\n            set { _CustomizeParameterVar.Set(value); }\n        }\n        #endregion\n\n        #region Constructor\n        public SkinEffect(Device device, byte[] byteCode) : this(device, byteCode, EffectFlags.None) { }\n        public SkinEffect(Device device, byte[] byteCode, EffectFlags flags)\n            : base(device, byteCode, flags) {\n            Init();\n        }\n        #endregion\n\n        #region Init\n        private void Init() {\n            _DiffuseVar = GetVariableByName(\"g_Diffuse\").AsShaderResource();\n            _NormalVar = GetVariableByName(\"g_Normal\").AsShaderResource();\n            _MaskVar = GetVariableByName(\"g_Mask\").AsShaderResource();\n\n            _CustomizeParameterVar = new CustomizeParameterEffectVariable(this);\n        }\n        #endregion\n    }\n}\n"
  },
  {
    "path": "SaintCoinach.Graphics.Viewer/Effects/SkinnedEffect.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace SaintCoinach.Graphics.Viewer.Effects {\n    using SharpDX;\n    using SharpDX.D3DCompiler;\n    using SharpDX.Direct3D11;\n\n    public abstract class SkinnedEffect : EffectBase {\n        public const int JointMatrixArrayMaxSize = 64;\n\n        #region Fields\n        private EffectMatrixVariable _JointMatrixArrayVar;\n        #endregion\n\n        #region Properties\n        public Matrix[] JointMatrixArray {\n            set {\n                if (value == null)\n                    throw new ArgumentNullException();\n                if (value.Length > JointMatrixArrayMaxSize)\n                    throw new ArgumentException();\n                _JointMatrixArrayVar.SetMatrix(value);\n            }\n        }\n        #endregion\n\n\n        #region Constructor\n        protected SkinnedEffect(Device device, byte[] byteCode) : this(device, byteCode, EffectFlags.None) { }\n        protected SkinnedEffect(Device device, byte[] byteCode, EffectFlags flags)\n            : base(device, byteCode, flags) {\n            _JointMatrixArrayVar = GetVariableByName(\"g_JointMatrixArray\").AsMatrix();\n        }\n        #endregion\n    }\n}\n"
  },
  {
    "path": "SaintCoinach.Graphics.Viewer/Engine.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Diagnostics;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nusing System.IO;\nusing System.Reflection;\n\nnamespace SaintCoinach.Graphics.Viewer {\n    using SharpDX;\n    using SharpDX.D3DCompiler;\n    using SharpDX.Direct3D;\n    using SharpDX.Direct3D11;\n    using SharpDX.DXGI;\n    using Buffer = SharpDX.Direct3D11.Buffer;\n    using Device = SharpDX.Direct3D11.Device;\n\n    public abstract class Engine : IDisposable {\n\n        #region Fields\n        private Device _Device;\n\n        private Keyboard _Keyboard;\n        private Mouse _Mouse;\n        private Camera _Camera;\n\n        private Texture2D _RenderTarget;\n        private RasterizerState _RasterizerState;\n        private DepthStencilState _StencilState;\n        private RenderTargetView _RenderTargetView;\n        private BlendState _BlendState;\n\n        private Texture2D _DepthStencil;\n        private DepthStencilView _DepthStencilView;\n\n        private Content.Cube _Cube;\n\n        private Stopwatch _RunTimer;\n        private long _TotalElapsedTime;\n\n        private ComponentContainer _CoreComponents;\n        private ComponentContainer _Components;\n\n        private ModelFactory _ModelFactory;\n        private TextureFactory _TextureFactory;\n        private EffectFactory _EffectFactory;\n        private MaterialFactory _MaterialFactory;\n\n        private Size2 _ViewportSize;\n        #endregion\n\n        #region Properties\n        protected Texture2D RenderTarget { get { return _RenderTarget; } }\n        protected RenderTargetView RenderTargetView { get { return _RenderTargetView; } }\n\n        public Device Device { get { return _Device; } }\n        public Keyboard Keyboard { get { return _Keyboard; } }\n        public Mouse Mouse { get { return _Mouse; } }\n        public Camera Camera { get { return _Camera; } }\n\n        public Content.Cube Cube { get { return _Cube; } }\n\n        protected ComponentContainer CoreComponents { get { return _CoreComponents; } }\n        public ComponentContainer Components { get { return _Components; } }\n\n        public ModelFactory ModelFactory { get { return _ModelFactory; } }\n        public TextureFactory TextureFactory { get { return _TextureFactory; } }\n        public EffectFactory EffectFactory { get { return _EffectFactory; } }\n        public MaterialFactory MaterialFactory { get { return _MaterialFactory; } }\n        public abstract IInputService InputService { get; }\n        public virtual Size2 ViewportSize { get { return _ViewportSize; } }\n        public abstract bool IsActive { get; }\n\n        public DepthStencilState DepthStencilState {\n            get { return _StencilState; }\n            protected set {\n                if (value == _StencilState)\n                    return;\n                if (value == null)\n                    throw new ArgumentNullException();\n                if (_StencilState != null)\n                    _StencilState.Dispose();\n                _StencilState = value;\n            }\n        }\n        public BlendState BlendState {\n            get { return _BlendState; }\n            protected set {\n                if (value == _BlendState)\n                    return;\n                if (value == null)\n                    throw new ArgumentNullException();\n                if (_BlendState != null)\n                    _BlendState.Dispose();\n                _BlendState = value;\n            }\n        }\n        public RasterizerState RasterizerState {\n            get { return _RasterizerState; }\n            protected set {\n                if (value == _RasterizerState)\n                    return;\n                if (value == null)\n                    throw new ArgumentNullException();\n                if (_RasterizerState != null)\n                    _RasterizerState.Dispose();\n                _RasterizerState = value;\n            }\n        }\n        #endregion\n\n        #region Constructor\n        protected Engine() {\n            _CoreComponents = new ComponentContainer();\n            _Components = new ComponentContainer();\n        }\n        #endregion\n\n        #region Shared\n        protected abstract Device CreateDevice(int width, int height);\n        protected abstract Texture2D CreateRenderTarget(int width, int height);\n        protected virtual void Resize(int newWidth, int newHeight) {\n            Device.ImmediateContext.OutputMerger.ResetTargets();\n\n            _RenderTargetView.Dispose();\n            _RenderTarget.Dispose();\n\n            _DepthStencilView.Dispose();\n            _DepthStencil.Dispose();\n\n            CreateView(newWidth, newHeight);\n        }\n        protected void SetUp(int width, int height) {\n            _Device = CreateDevice(width, height);\n\n            CreateView(width, height);\n\n            var depthDesc = DepthStencilStateDescription.Default();\n            _StencilState = new DepthStencilState(Device, depthDesc);\n            var blendDesc = BlendStateDescription.Default();\n            blendDesc.RenderTarget[0].IsBlendEnabled = true;\n            blendDesc.RenderTarget[0].SourceBlend = BlendOption.SourceAlpha;\n            blendDesc.RenderTarget[0].DestinationBlend = BlendOption.InverseSourceAlpha;\n            blendDesc.RenderTarget[0].BlendOperation = BlendOperation.Add;\n            blendDesc.RenderTarget[0].SourceAlphaBlend = BlendOption.One;\n            blendDesc.RenderTarget[0].DestinationAlphaBlend = BlendOption.One;\n            blendDesc.RenderTarget[0].AlphaBlendOperation = BlendOperation.Add;\n            blendDesc.RenderTarget[0].RenderTargetWriteMask = ColorWriteMaskFlags.All;\n            _BlendState = new BlendState(Device, blendDesc);\n\n            var raster = RasterizerStateDescription.Default();\n            raster.CullMode = CullMode.Front;\n            raster.IsMultisampleEnabled = RenderTarget.Description.SampleDescription.Count > 1;\n\n            _RasterizerState = new RasterizerState(Device, raster);\n        }\n\n        protected void CreateView(int width, int height) {\n            _ViewportSize = new Size2(width, height);\n            _RenderTarget = CreateRenderTarget(width, height);\n            _RenderTargetView = new RenderTargetView(Device, _RenderTarget);\n\n            var dsTexDesc = new Texture2DDescription {\n                Format = Format.D24_UNorm_S8_UInt,\n                ArraySize = 1,\n                MipLevels = 1,\n                Width = _RenderTarget.Description.Width,\n                Height = _RenderTarget.Description.Height,\n                SampleDescription = _RenderTarget.Description.SampleDescription,\n                Usage = ResourceUsage.Default,\n                BindFlags = BindFlags.DepthStencil,\n                CpuAccessFlags = CpuAccessFlags.None,\n                OptionFlags = ResourceOptionFlags.None,\n            };\n            _DepthStencil = new Texture2D(Device, dsTexDesc);\n            _DepthStencilView = new DepthStencilView(Device, _DepthStencil, new DepthStencilViewDescription {\n                Flags = DepthStencilViewFlags.None,\n                Dimension = DepthStencilViewDimension.Texture2DMultisampled,\n                Format = dsTexDesc.Format\n            });\n\n            Device.ImmediateContext.Rasterizer.SetViewport(new Viewport(0, 0, width, height));\n        }\n        #endregion\n\n        #region Initialize\n        protected virtual void Initialize() {\n            CoreComponents.Add(_Keyboard = new Keyboard(this));\n            CoreComponents.Add(_Mouse = new Mouse(this));\n            CoreComponents.Add(_Camera = new Camera(this));\n\n            _ModelFactory = new ModelFactory(this);\n            _TextureFactory = new TextureFactory(this);\n            _EffectFactory = new EffectFactory(this);\n            _MaterialFactory = new MaterialFactory(this);\n        }\n        #endregion\n\n        #region Content\n        protected void Load() {\n            CoreComponents.LoadContent();\n            Components.LoadContent();\n\n            _Cube = new Content.Cube(this);\n            _Cube.LoadContent();\n\n            LoadContent();\n        }\n        protected virtual void LoadContent() { }\n\n        protected void Unload() {\n            Components.UnloadContent();\n            CoreComponents.UnloadContent();\n\n            if (_Cube != null)\n                _Cube.UnloadContent();\n            _Cube = null;\n\n            ModelFactory.UnloadAll();\n            MaterialFactory.UnloadAll();\n            EffectFactory.UnloadAll();\n            TextureFactory.UnloadAll();\n\n            UnloadContent();\n        }\n        protected virtual void UnloadContent() { }\n        #endregion\n\n        #region Loop\n        protected void EngineLoop() {\n            if (_RunTimer == null) {\n                _RunTimer = new Stopwatch();\n                _RunTimer.Start();\n            }\n            var elapsed = _RunTimer.Elapsed;\n            _RunTimer.Restart();\n            _TotalElapsedTime += elapsed.Ticks;\n            var time = new EngineTime(TimeSpan.FromTicks(_TotalElapsedTime), elapsed);\n\n            EngineLoop(time);\n        }\n        protected void EngineLoop(EngineTime time) {\n            Update(time);\n\n            BeforeDraw(time);\n            Draw(time);\n            AfterDraw(time);\n\n            Present();\n        }\n\n        protected abstract void Present();\n\n        protected virtual void Update(EngineTime time) {\n            CoreComponents.Update(time);\n            Components.Update(time);\n        }\n        protected virtual void BeforeDraw(EngineTime time) {\n            Device.ImmediateContext.OutputMerger.SetDepthStencilState(DepthStencilState);\n            Device.ImmediateContext.OutputMerger.SetBlendState(BlendState);\n            Device.ImmediateContext.Rasterizer.State = RasterizerState;\n            Device.ImmediateContext.OutputMerger.SetTargets(_DepthStencilView, _RenderTargetView);\n            Device.ImmediateContext.ClearDepthStencilView(_DepthStencilView, DepthStencilClearFlags.Depth | DepthStencilClearFlags.Stencil, 1f, 0);\n        }\n        protected virtual void Draw(EngineTime time) {\n            var world = Matrix.Identity;\n            var view = Camera.View;\n            var proj = Camera.Projection;\n\n            Draw3D(time, ref world, ref view, ref proj);\n        }\n        protected virtual void AfterDraw(EngineTime time) {\n\n        }\n\n        private void Draw3D(EngineTime time, ref Matrix world, ref Matrix view, ref Matrix proj) {\n            CoreComponents.Draw(time, ref world, ref view, ref proj);\n            Components.Draw(time, ref world, ref view, ref proj);\n        }\n        #endregion\n\n        #region IDisposable Members\n\n        public void Dispose() {\n            Dispose(true);\n        }\n\n        protected virtual void Dispose(bool disposing) {\n            Unload();\n            if (disposing) {\n                if (_BlendState != null)\n                    _BlendState.Dispose();\n                _BlendState = null;\n\n                if (_DepthStencilView != null)\n                    _DepthStencilView.Dispose();\n                _DepthStencilView = null;\n\n                if (_DepthStencil != null)\n                    _DepthStencil.Dispose();\n                _DepthStencil = null;\n\n                if (_RasterizerState != null)\n                    _RasterizerState.Dispose();\n                _RasterizerState = null;\n\n                if (_RenderTargetView != null)\n                    _RenderTargetView.Dispose();\n                _RenderTargetView = null;\n\n                if (_RenderTarget != null)\n                    _RenderTarget.Dispose();\n                _RenderTarget = null;\n\n                if (_StencilState != null)\n                    _StencilState = null;\n                _StencilState = null;\n\n                if (_Device != null)\n                    _Device.Dispose();\n                _Device = null;\n            }\n        }\n        #endregion\n    }\n}\n"
  },
  {
    "path": "SaintCoinach.Graphics.Viewer/EngineTime.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace SaintCoinach.Graphics.Viewer {\n    public struct EngineTime {\n        public static readonly EngineTime Zero = new EngineTime(TimeSpan.Zero, TimeSpan.Zero);\n\n        public readonly TimeSpan TotalTime;\n        public readonly TimeSpan ElapsedTime;\n\n        public EngineTime(TimeSpan totalTime, TimeSpan elapsedTime) {\n            this.TotalTime = totalTime;\n            this.ElapsedTime = elapsedTime;\n        }\n    }\n}\n"
  },
  {
    "path": "SaintCoinach.Graphics.Viewer/FormEngine.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Diagnostics;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nusing System.IO;\nusing System.Reflection;\nusing SharpDX.Windows;\n\nnamespace SaintCoinach.Graphics.Viewer {\n    using SharpDX;\n    using SharpDX.D3DCompiler;\n    using SharpDX.Direct3D;\n    using SharpDX.Direct3D11;\n    using SharpDX.DXGI;\n    using Buffer = SharpDX.Direct3D11.Buffer;\n    using Device = SharpDX.Direct3D11.Device;\n\n    public class FormEngine : Engine {\n        const int ScreenshotWidth = 2048;\n        const int ScreenshotHeight = 2048;\n        const long ResizeDelay = 250;\n\n        #region Fields\n        private bool _IsResizePending;\n        private Stopwatch _ResizeTimer = new Stopwatch();\n        private SwapChain _SwapChain;\n\n        private IInputService _InputService;\n\n        private string _Title;\n        private RenderForm _Form;\n\n        private Texture2D _ScreenshotTarget;\n        private Texture2D _ScreenshotStencil;\n\n        private RenderTargetView _ScreenshotTargetView;\n        private DepthStencilView _ScreenshotStencilView;\n        #endregion\n\n        #region Properties\n        public override IInputService InputService {\n            get { return _InputService; }\n        }\n        public RenderForm Form { get { return _Form; } }\n        public override bool IsActive {\n            get { return Form.ContainsFocus; }\n        }\n        #endregion\n\n        #region Constructor\n        public FormEngine(string title) {\n            _Title = title;\n        }\n        #endregion\n\n        #region Run\n        public void Run() {\n            using (_Form = new RenderForm(_Title)) {\n                var assembly = Assembly.GetExecutingAssembly();\n                using (var iconStream = assembly.GetManifestResourceStream(\"SaintCoinach.Graphics.Viewer.Viewer.ico\"))\n                    _Form.Icon = new System.Drawing.Icon(iconStream);\n\n                SetUp(Form.ClientSize.Width, Form.ClientSize.Height);\n                Form.ClientSizeChanged += Form_ClientSizeChanged;\n\n                _InputService = new FormInputService(Form);\n\n                Initialize();\n                Load();\n\n                RenderLoop.Run(Form, EngineLoop);\n\n                Unload();\n            }\n        }\n        void Form_ClientSizeChanged(object sender, EventArgs e) {\n            _IsResizePending = true;\n            _ResizeTimer.Restart();\n        }\n        #endregion\n\n        protected override Device CreateDevice(int width, int height) {\n            var desc = new SwapChainDescription {\n                BufferCount = 1,\n                Flags = SwapChainFlags.None,\n                IsWindowed = true,\n                ModeDescription = new ModeDescription(\n                    width, height,\n                    new Rational(60, 1), Format.R8G8B8A8_UNorm),\n                OutputHandle = Form.Handle,\n                SampleDescription = new SampleDescription(8, 0), //new SampleDescription(8, Device.CheckMultisampleQualityLevels(Format.R8G8B8A8_UNorm, 8)),\n                SwapEffect = SwapEffect.Discard,\n                Usage = Usage.RenderTargetOutput | Usage.BackBuffer | Usage.Shared,\n            };\n\n            SharpDX.Direct3D11.Device device;\n            SharpDX.Direct3D11.Device.CreateWithSwapChain(DriverType.Hardware, DeviceCreationFlags.None, desc, out device, out _SwapChain);\n            return device;\n        }\n        private bool _IsResizing;\n        protected override void Resize(int newWidth, int newHeight) {\n            _IsResizing = true;\n            base.Resize(newWidth, newHeight);\n        }\n        protected override Texture2D CreateRenderTarget(int width, int height) {\n            if(_IsResizing)\n                _SwapChain.ResizeBuffers(1, width, height, Format.Unknown, SwapChainFlags.None);\n            _IsResizing = false;\n            return Texture2D.FromSwapChain<Texture2D>(_SwapChain, 0);\n        }\n        protected override void Draw(EngineTime time) {\n            Device.ImmediateContext.ClearRenderTargetView(RenderTargetView, Color.CornflowerBlue);\n\n            base.Draw(time);\n        }\n        protected override void Present() {\n            _SwapChain.Present(0, PresentFlags.None);\n        }\n        protected override void Dispose(bool disposing) {\n            base.Dispose(disposing);\n\n            if (disposing) {\n                if (_ScreenshotTargetView != null)\n                    _ScreenshotTargetView.Dispose();\n                if (_ScreenshotStencilView != null)\n                    _ScreenshotStencilView.Dispose();\n\n                if (_ScreenshotTarget != null)\n                    _ScreenshotTarget.Dispose();\n                if (_ScreenshotStencil != null)\n                    _ScreenshotStencil.Dispose();\n\n                _ScreenshotStencil = null;\n                _ScreenshotStencilView = null;\n                _ScreenshotTarget = null;\n                _ScreenshotTargetView = null;\n\n                if (_SwapChain != null)\n                    _SwapChain.Dispose();\n                _SwapChain = null;\n\n                if (_Form != null)\n                    _Form.Dispose();\n                _Form = null;\n            }\n        }\n        protected void ResizeToClient() {\n            Resize(Form.ClientSize.Width, Form.ClientSize.Height);\n        }\n        protected void ResizeToScreenshot() {\n            Resize(ScreenshotWidth, ScreenshotHeight);\n        }\n        protected void TakeScreenshot(EngineTime time) {\n            Camera.Update(time);\n\n            Device.ImmediateContext.OutputMerger.SetDepthStencilState(DepthStencilState);\n            Device.ImmediateContext.OutputMerger.SetBlendState(BlendState);\n            Device.ImmediateContext.Rasterizer.State = RasterizerState;\n            Device.ImmediateContext.OutputMerger.SetTargets(_ScreenshotStencilView, _ScreenshotTargetView);\n            Device.ImmediateContext.ClearDepthStencilView(_ScreenshotStencilView, DepthStencilClearFlags.Depth | DepthStencilClearFlags.Stencil, 1f, 0);\n\n            Device.ImmediateContext.ClearRenderTargetView(_ScreenshotTargetView, Color.Transparent);\n            base.Draw(time);\n\n            // Texture2D.ToFile(Device.ImmediateContext, _ScreenshotTarget, ImageFileFormat.Png, string.Format(\"SS-{0:yyyyMMdd-HHmmss}.png\", DateTime.Now));\n            ResizeToClient();\n        }\n        protected override void Update(EngineTime time) {\n            if (_IsResizePending) {\n                var elapsed = _ResizeTimer.ElapsedMilliseconds;\n                if (elapsed > ResizeDelay) {\n                    ResizeToClient();\n                    _ResizeTimer.Reset();\n                    _IsResizePending = false;\n                }\n            }\n            if (Keyboard.WasKeyPressed(System.Windows.Forms.Keys.F12)) {\n                if (_ScreenshotTargetView == null) {\n                    var rtTexDesc = RenderTarget.Description;\n                    rtTexDesc.SampleDescription = new SampleDescription(1, 0);\n                    rtTexDesc.Width = ScreenshotWidth;\n                    rtTexDesc.Height = ScreenshotHeight;\n\n                    _ScreenshotTarget = new Texture2D(Device, rtTexDesc);\n                    _ScreenshotTargetView = new RenderTargetView(Device, _ScreenshotTarget);\n\n                    var dsTexDesc = new Texture2DDescription {\n                        Format = Format.D24_UNorm_S8_UInt,\n                        ArraySize = 1,\n                        MipLevels = 1,\n                        Width = _ScreenshotTarget.Description.Width,\n                        Height = _ScreenshotTarget.Description.Height,\n                        SampleDescription = _ScreenshotTarget.Description.SampleDescription,\n                        Usage = ResourceUsage.Default,\n                        BindFlags = BindFlags.DepthStencil,\n                        CpuAccessFlags = CpuAccessFlags.None,\n                        OptionFlags = ResourceOptionFlags.None,\n                    };\n                    _ScreenshotStencil = new Texture2D(Device, dsTexDesc);\n                    _ScreenshotStencilView = new DepthStencilView(Device, _ScreenshotStencil, new DepthStencilViewDescription {\n                        Flags = DepthStencilViewFlags.None,\n                        Dimension = DepthStencilViewDimension.Texture2DMultisampled,\n                        Format = dsTexDesc.Format\n                    });\n                }\n                ResizeToScreenshot();\n                TakeScreenshot(time);\n                ResizeToClient();\n            }\n\n            base.Update(time);\n        }\n    }\n}\n"
  },
  {
    "path": "SaintCoinach.Graphics.Viewer/FormInputService.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nusing SharpDX.Multimedia;\nusing System.Windows.Forms;\n\nnamespace SaintCoinach.Graphics.Viewer {\n    using SharpDX;\n\n    public class FormInputService : IInputService {\n        #region Fields\n        private List<Keys> _DownKeys = new List<Keys>();\n        private Point _MousePosition;\n        private List<MouseButtons> _DownMouseButtons = new List<MouseButtons>();\n        #endregion\n\n        public Point MousePosition { get { return _MousePosition; } }\n\n        #region Constructor\n        public FormInputService(Form form) {\n            form.KeyDown += Form_KeyDown;\n            form.KeyUp += Form_KeyUp;\n            form.MouseMove += Form_MouseMove;\n            form.MouseDown += Form_MouseDown;\n            form.MouseUp += Form_MouseUp;\n            form.LostFocus += Form_LostFocus;\n        }\n        #endregion\n\n        #region Get\n        public IEnumerable<Keys> GetDownKeys() {\n            Keys[] keys;\n            lock (_DownKeys)\n                keys = _DownKeys.ToArray();\n            return keys;\n        }\n        public IEnumerable<MouseButtons> GetDownMouseButtons() {\n            MouseButtons[] buttons;\n            lock (_DownMouseButtons)\n                buttons = _DownMouseButtons.ToArray();\n            return buttons;\n        }\n        #endregion\n\n        #region Form events\n        void Form_LostFocus(object sender, EventArgs e) {\n            lock (_DownMouseButtons)\n                _DownMouseButtons.Clear();\n            lock (_DownKeys)\n                _DownKeys.Clear();\n        }\n        void Form_MouseUp(object sender, MouseEventArgs e) {\n            lock (_DownMouseButtons)\n                _DownMouseButtons.Remove(e.Button);\n        }\n\n        void Form_MouseDown(object sender, MouseEventArgs e) {\n            lock (_DownMouseButtons) {\n                if (!_DownMouseButtons.Contains(e.Button))\n                    _DownMouseButtons.Add(e.Button);\n            }\n        }\n\n        void Form_MouseMove(object sender, MouseEventArgs e) {\n            _MousePosition = new Point(e.X, e.Y);\n        }\n        void Form_KeyUp(object sender, KeyEventArgs e) {\n            lock (_DownKeys)\n                _DownKeys.Remove(e.KeyCode);\n        }\n\n        void Form_KeyDown(object sender, KeyEventArgs e) {\n            lock (_DownKeys) {\n                if (!_DownKeys.Contains(e.KeyCode))\n                    _DownKeys.Add(e.KeyCode);\n            }\n        }\n        #endregion\n    }\n}\n"
  },
  {
    "path": "SaintCoinach.Graphics.Viewer/IComponent.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace SaintCoinach.Graphics.Viewer {\n    public interface IComponent {\n\n    }\n}\n"
  },
  {
    "path": "SaintCoinach.Graphics.Viewer/IContentComponent.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace SaintCoinach.Graphics.Viewer {\n    public interface IContentComponent {\n        bool IsLoaded { get; }\n        void LoadContent();\n        void UnloadContent();\n    }\n}\n"
  },
  {
    "path": "SaintCoinach.Graphics.Viewer/IDrawable3DComponent.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nusing SharpDX;\nusing Device = SharpDX.Direct3D11.Device;\n\nnamespace SaintCoinach.Graphics.Viewer {\n    public interface IDrawable3DComponent : IComponent {\n        bool IsVisible { get; }\n        /// <summary>\n        /// While the arguments are refs, do not change them, it's just so they don't get cloned again and again and again.\n        /// </summary>\n        void Draw(EngineTime time, ref Matrix world, ref Matrix view, ref Matrix projection);\n    }\n}\n"
  },
  {
    "path": "SaintCoinach.Graphics.Viewer/IImageRendererSource.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.IO;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace SaintCoinach.Graphics.Viewer {\n    public interface IImageRendererSource {\n        IComponent CurrentComponent { get; }\n        BoundingBox CurrentBoundingBox { get; }\n        FileInfo CurrentTargetFile { get; }\n        string CurrentName { get; }\n        bool RenderFromOppositeSide { get; }\n\n        bool MoveNext();\n        void Reset(Engine engine);\n    }\n}\n"
  },
  {
    "path": "SaintCoinach.Graphics.Viewer/IInputService.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace SaintCoinach.Graphics.Viewer {\n    using SharpDX;\n    using SharpDX.Multimedia;\n    using System.Windows.Forms;\n\n    public interface IInputService {\n        Point MousePosition { get; }\n        IEnumerable<Keys> GetDownKeys();\n        IEnumerable<MouseButtons> GetDownMouseButtons();\n    }\n}\n"
  },
  {
    "path": "SaintCoinach.Graphics.Viewer/IUpdateableComponent.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace SaintCoinach.Graphics.Viewer {\n    public interface IUpdateableComponent : IComponent {\n        bool IsEnabled { get; }\n        void Update(EngineTime engineTime);\n    }\n}\n"
  },
  {
    "path": "SaintCoinach.Graphics.Viewer/ImageRenderer.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\nusing SharpDX.Direct3D10;\n\nnamespace SaintCoinach.Graphics.Viewer {\n    using SharpDX;\n    using SharpDX.Direct3D11;\n\n    public class ImageRenderer : Engine {\n        #region Input service\n        class NullInputService : IInputService {\n            public static readonly NullInputService Instance = new NullInputService();\n\n            #region IInputService Members\n\n            public SharpDX.Point MousePosition {\n                get { return SharpDX.Point.Zero; }\n            }\n\n            public IEnumerable<System.Windows.Forms.Keys> GetDownKeys() {\n                yield break;\n            }\n\n            public IEnumerable<System.Windows.Forms.MouseButtons> GetDownMouseButtons() {\n                yield break;\n            }\n\n            #endregion\n        }\n        #endregion\n\n        #region Fields\n        private IImageRendererSource _Source;\n        private int _RenderWidth;\n        private int _RenderHeight;\n        private ImageFileFormat _OutputFormat;\n        private Color _BackgroundColor;\n        #endregion\n\n        #region Constructor\n        public ImageRenderer(IImageRendererSource source, int renderWidth, int renderHeight) : this(source, renderWidth, renderHeight, ImageFileFormat.Png, Color.Transparent) { }\n        public ImageRenderer(IImageRendererSource source, int renderWidth, int renderHeight, ImageFileFormat outputFormat, Color backgroundColor) {\n            _Source = source;\n            _RenderWidth = renderWidth;\n            _RenderHeight = renderHeight;\n            _OutputFormat = outputFormat;\n            _BackgroundColor = backgroundColor;\n        }\n        #endregion\n\n        #region Run\n        public void Run() {\n            const int CacheClearInterval = 500;\n\n            SetUp(_RenderWidth, _RenderHeight);\n            Initialize();\n            Load();\n\n            var i = 0;\n            _Source.Reset(this);\n            while (_Source.MoveNext()) {\n                if (i++ % CacheClearInterval == 0) {\n                    Unload();\n                    Load();\n                    GC.Collect();\n                }\n                try {\n                    Components.Add(_Source.CurrentComponent);\n                    SetUpCamera(_Source.CurrentBoundingBox, _Source.RenderFromOppositeSide);\n                    EngineLoop(EngineTime.Zero);\n                } catch (Exception e) {\n                    Console.Error.WriteLine(\"Failed to render '{0}': {1}\", _Source.CurrentName, e.Message);\n                } finally {\n                    Components.Clear();\n                }\n            }\n\n            Unload();\n        }\n        #endregion\n\n        #region Viewport\n        protected virtual void SetUpCamera(Graphics.BoundingBox bbox, bool oppositeSide) {\n            const float Yaw = MathUtil.PiOverFour / 2;    // -MathUtil.PiOverTwo;\n            const float Pitch = -MathUtil.PiOverFour / 3;  // MathUtil.PiOverTwo;\n\n            Camera.Yaw = oppositeSide ? (Yaw - MathUtil.Pi) : Yaw;\n            Camera.Pitch = Pitch;\n\n            var center = (bbox.PointA.ToDx3() + bbox.PointB.ToDx3()) / 2f;\n\n            var dx = Math.Abs(bbox.PointA.X - bbox.PointB.X);\n            var dy = Math.Abs(bbox.PointA.Y - bbox.PointB.Y);\n            var dz = Math.Abs(bbox.PointA.Z - bbox.PointB.Z);\n\n            var maxAxis = Math.Max(dx, Math.Max(dy, dz)); // Add a bit because I'm too lazy to adjust FoV for the angle\n\n            var backDirection = -Vector3.Transform(Vector3.BackwardRH, Camera.GetRotation());\n            var d = -(maxAxis / 2) / Math.Sin(Camera.FoV / 2);\n\n            Camera.CameraPosition = center + new Vector3(backDirection.X, backDirection.Y, backDirection.Z) * (float)d;\n        }\n        #endregion\n\n        public override IInputService InputService {\n            get { return NullInputService.Instance; }\n        }\n\n        public override bool IsActive {\n            get { return true; }\n        }\n\n        protected override SharpDX.Direct3D11.Device CreateDevice(int width, int height) {\n            return new SharpDX.Direct3D11.Device(SharpDX.Direct3D.DriverType.Hardware, SharpDX.Direct3D11.DeviceCreationFlags.None, SharpDX.Direct3D.FeatureLevel.Level_11_0);\n        }\n\n        protected override SharpDX.Direct3D11.Texture2D CreateRenderTarget(int width, int height) {\n            var desc = new SharpDX.Direct3D11.Texture2DDescription {\n                ArraySize = 1,\n                BindFlags = SharpDX.Direct3D11.BindFlags.RenderTarget,\n                CpuAccessFlags = SharpDX.Direct3D11.CpuAccessFlags.None,\n                Format = SharpDX.DXGI.Format.R8G8B8A8_UNorm,\n                Height = height,\n                Width = width,\n                MipLevels = 1,\n                OptionFlags = SharpDX.Direct3D11.ResourceOptionFlags.Shared,\n                Usage = SharpDX.Direct3D11.ResourceUsage.Default,\n                SampleDescription = new SharpDX.DXGI.SampleDescription(1, 0)\n            };\n            return new SharpDX.Direct3D11.Texture2D(Device, desc);\n        }\n\n        protected override void Present() {\n            if (!_Source.CurrentTargetFile.Directory.Exists)\n                _Source.CurrentTargetFile.Directory.Create();\n\n            var ext = \".\" + _OutputFormat.ToString().ToLower();\n            // Texture2D.ToFile(Device.ImmediateContext, RenderTarget, _OutputFormat, _Source.CurrentTargetFile.FullName + ext);\n        }\n\n        protected override void Draw(EngineTime time) {\n            Device.ImmediateContext.ClearRenderTargetView(RenderTargetView, _BackgroundColor);\n\n            base.Draw(time);\n        }\n    }\n}\n"
  },
  {
    "path": "SaintCoinach.Graphics.Viewer/Interop/FbxExport.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.ComponentModel;\nusing System.IO;\nusing System.Linq;\nusing System.Runtime.ExceptionServices;\nusing System.Runtime.InteropServices;\nusing System.Text;\nusing System.Threading.Tasks;\nusing Ookii.Dialogs.Wpf;\nusing SaintCoinach.IO;\nusing Directory = System.IO.Directory;\n\nnamespace SaintCoinach.Graphics.Viewer.Interop\n{\n    public static class FbxExport\n    {\n        static class Interop\n        {\n            [DllImport(\"fbxInterop.dll\", CallingConvention = CallingConvention.Cdecl)]\n            public static extern int exportFbx([In, Out] IntPtr[] meshes, int numMeshes,\n                                                [In, Out] byte[] skeleton, int skeletonSize,\n                                                [In, Out] IntPtr[] anims, int totalAnims,\n                                                [In, Out] int[] boneMap, int mapLength,\n                                                string filename, int mode);\n        }\n\n        [HandleProcessCorruptedStateExceptions]\n        public static int ExportFbx(string fileName,\n                                        Mesh[] ma,\n                                        Skeleton skele,\n                                        List<PapFile> paps,\n                                        int mode = 0)\n        {\n            ModelDefinition thisDefinition = ma[0].Model.Definition;\n\n            // Create bonemap in the same manner that hkAnimationInterop does\n            var nameMap = new Dictionary<string, int>();\n            for (var i = 0; i < skele.BoneNames.Length; ++i)\n                nameMap.Add(skele.BoneNames[i], i);\n            var boneMap = thisDefinition.BoneNames.Select(n => nameMap[n]).ToArray();\n\n            // Get mesh ptrs\n            InteropMesh[] managedMeshes = new InteropMesh[ma.Length];\n            for (int i = 0; i < ma.Length; i++)\n                managedMeshes[i] = new InteropMesh(ma[i]);\n\n            // Get animations ptrs\n            InteropAnimation[] managedAnims = new InteropAnimation[paps.Count];\n            int numPaps = paps.Count;\n            if (paps.Count > 0)\n            {\n                for (int i = 0; i < paps.Count; i++)\n                    managedAnims[i] = new InteropAnimation(paps[i]);\n            }\n            else\n            {\n                managedAnims = new InteropAnimation[0];\n            }\n\n            int result = 0;\n\n            try\n            {\n                result = Interop.exportFbx(managedMeshes.Select(_ => _._UnmanagedPtr).ToArray(), managedMeshes.Length,\n                    skele.File.HavokData, skele.File.HavokData.Length,\n                    managedAnims.Select(_ => _._UnmanagedPtr).ToArray(), numPaps,\n                    boneMap, boneMap.Length,\n                    fileName, mode);\n            }\n            catch (Win32Exception e)\n            {\n                System.Diagnostics.Debug.WriteLine(\"Native error code \" + e.NativeErrorCode + \" encountered.\");\n                return 1;\n            }\n            catch (Exception e)\n            {\n                if (e is AccessViolationException || e is SEHException)\n                {\n                    System.Diagnostics.Debug.WriteLine($\"Access violation with\\n\" +\n                                                       $\"Model:\\t {thisDefinition.File.Path}\\n\" +\n                                                       $\"Anims:\\t {numPaps}\\n\" +\n                                                       $\"Skele:\\t {skele.File.File.Path}\\n\");\n                }\n\n                return 1;\n            }\n            \n            // Prevent collection of mesh and anim resources during unmanaged code\n            GC.KeepAlive(managedMeshes);\n            GC.KeepAlive(managedAnims);\n\n            return result;\n        }\n\n        // This is monster-specific because the loading of actual materials assumes b0000 for some reason?\n        public static void ExportMonsterMaterials(ARealmReversed realm, string folder, MaterialDefinition[] thisDefinitionMaterials, ImcVariant variant)\n        {\n            string format = \"chara/monster/m{0}/obj/body/b{1}/material/v{2:D4}{3}\";\n            \n            foreach (var material in thisDefinitionMaterials)\n            {\n                string path = material.Name;\n                string m = path.Substring(path.IndexOf(\"_m\") + 2, 4);\n                string b = path.Substring(path.IndexOf(\"_m\") + 7, 4);\n\n                Material mat = new Material(material, realm.Packs.GetFile(String.Format(format, m, b, variant.Variant, path)), variant);\n                foreach (var tex in mat.TexturesFiles)\n                {\n                    string texFile = tex.Path.Substring(tex.Path.LastIndexOf('/')).Replace(\".tex\", \".png\");\n                    string output = folder + '\\\\' + texFile;\n                    tex.GetImage().Save(output);\n                }\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "SaintCoinach.Graphics.Viewer/Interop/HavokInterop.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Diagnostics;\nusing System.Linq;\nusing System.Runtime.InteropServices;\nusing System.Text;\nusing System.Threading.Tasks;\nusing System.Windows;\n\nnamespace SaintCoinach.Graphics.Viewer.Interop {\n    public static class HavokInterop {\n        static object _Lock = new object();\n\n        static bool _Initialized;\n\n        static bool _IsThreaded;\n        static volatile  bool _IsLive;\n\n        static volatile bool _IsAwaiting = false;\n        static Func<object> _CurrentAction;\n        static object _CurrentResult;\n\n        static HavokInterop() {\n        }\n\n        public static void InitializeSTA() {\n            if (_Initialized)\n                throw new InvalidOperationException();\n            initHkInterop();\n\n            _IsThreaded = false;\n            _Initialized = true;\n        }\n        public static void InitializeMTA() {\n            if (_Initialized)\n                throw new InvalidOperationException();\n            var t = new System.Threading.Thread(HavokLoop);\n            t.Name = \"Havok thread\";\n            t.IsBackground = true;\n            t.Start();\n\n            _IsThreaded = true;\n            _IsLive = true;\n        }\n\n        internal static void Execute(Action action) {\n            Execute<object>(() => { action(); return null; });\n        }\n        internal static T Execute<T>(Func<T> func) {\n            if (!_Initialized)\n                throw new InvalidOperationException();\n\n            if (!_IsThreaded)\n                return func();\n\n            T result;\n            lock (_Lock) {\n                _CurrentAction = () => (object)func();\n                _IsAwaiting = true;\n                while (_IsAwaiting) {\n                    if (!_IsLive)\n                        throw new InvalidProgramException();\n                }\n                result = (T)_CurrentResult;\n            }\n            return result;\n        }\n\n        static void HavokLoop() {\n            try {\n                initHkInterop();\n                _Initialized = true;\n\n                while (true) {\n                    if (_IsAwaiting) {\n                        _CurrentResult = _CurrentAction();\n                        _IsAwaiting = false;\n                    } else\n                        System.Threading.Thread.Sleep(5);\n                }\n            } catch (BadImageFormatException e) {\n                var result = MessageBox.Show(\"Failed to load Havok interop library.\\nPlease ensure that the 32-bit (x86) Visual C++ Redistributable is installed.\\nWould you like to download it now?\", \"Havok interop error\", MessageBoxButton.YesNo, MessageBoxImage.Error);\n                if (result == MessageBoxResult.Yes)\n                {\n                    Process.Start(new ProcessStartInfo(\"https://www.microsoft.com/en-us/download/details.aspx?id=30679\")\n                    {\n                        UseShellExecute = true,\n                    });\n                    Environment.Exit(0);\n                }\n                else\n                {\n                    result = MessageBox.Show(\"Would you like to run Godbert anyways, without 3D animation support?\", \"Havok interop error\", MessageBoxButton.YesNo, MessageBoxImage.Error);\n                    if (result == MessageBoxResult.No)\n                    {\n                        Environment.Exit(0);\n                    }\n                }\n            } finally {\n                _IsLive = false;\n                if (_Initialized)\n                    quitHkInterop();\n            }\n        }\n\n        [DllImport(\"hkAnimationInterop.dll\", CallingConvention = CallingConvention.Cdecl)]\n        internal static extern void initHkInterop();\n        [DllImport(\"hkAnimationInterop.dll\", CallingConvention = CallingConvention.Cdecl)]\n        internal static extern void quitHkInterop();\n    }\n}\n"
  },
  {
    "path": "SaintCoinach.Graphics.Viewer/Interop/InteropAnimation.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Runtime.InteropServices;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace SaintCoinach.Graphics.Viewer.Interop {\n\n    /// <summary>\n    /// Obtains an IntPtr to an Animation object from fbxInterop.dll.\n    /// </summary>\n    class InteropAnimation : IDisposable {\n        static class Interop {\n            [DllImport(\"fbxInterop.dll\", CallingConvention = CallingConvention.Cdecl)]\n            public static extern IntPtr loadAnimation(int count, int length,\n                                                        [In, Out] byte[] data,\n                                                        [In, Out] string[] names);\n\n            [DllImport(\"fbxInterop.dll\", CallingConvention = CallingConvention.Cdecl)]\n            public static extern void unloadAnimation(IntPtr a);\n        }\n\n        internal IntPtr _UnmanagedPtr;\n\n        public InteropAnimation(PapFile pap)\n        {\n            _UnmanagedPtr = Interop.loadAnimation(pap.Animations.Length,\n                pap.HavokData.Length,\n                pap.HavokData,\n                pap.Animations.Select(_ => _.Name).ToArray());\n        }\n\n        private bool _IsDisposed = false;\n\n        protected virtual void Dispose(bool disposing) {\n\n            if (!_IsDisposed) {\n                if (_UnmanagedPtr != IntPtr.Zero)\n                    Interop.unloadAnimation(_UnmanagedPtr);\n                _UnmanagedPtr = IntPtr.Zero;\n\n                _IsDisposed = true;\n            }\n        }\n\n        ~InteropAnimation() {\n            Dispose(false);\n        }\n\n        public void Dispose() {\n            Dispose(true);\n            GC.SuppressFinalize(this);\n        }\n    }\n}\n"
  },
  {
    "path": "SaintCoinach.Graphics.Viewer/Interop/InteropMesh.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Runtime.InteropServices;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace SaintCoinach.Graphics.Viewer.Interop\n{\n    /// <summary>\n    /// Obtains an IntPtr to a Mesh object from fbxInterop.dll.\n    /// </summary>\n    class InteropMesh : IDisposable\n    {\n        static class Interop\n        {\n            [DllImport(\"fbxInterop.dll\", CallingConvention = CallingConvention.Cdecl)]\n            public static extern IntPtr loadMesh(int index,\n                                                [In, Out] InteropVertex[] vertices, int numv,\n                                                [In, Out] ushort[] indices, int numi,\n                                                [In, Out] ushort[] boneList, int boneListSize);\n\n            [DllImport(\"fbxInterop.dll\", CallingConvention = CallingConvention.Cdecl)]\n            public static extern void unloadMesh(IntPtr m);\n        }\n\n        internal IntPtr _UnmanagedPtr;\n        public int Index { get; set; }\n        public InteropVertex[] Vertices { get; set; }\n        public ushort[] Indices { get; set; }\n        public ushort[] BoneList { get; set; }\n        public int NumVertexes { get; set; }\n        public int NumIndices { get; set; }\n        public int BoneListSize { get; set; }\n\n        /// <summary>\n        /// Merges an array of meshes into one InteropMesh. Unused. Does not include BoneList\n        /// </summary>\n        /// <param name=\"ma\"></param>\n        public InteropMesh(Mesh[] ma)\n        {\n            int numMeshes = ma.Length;\n            int totalVertices = ma.Select(_ => _.Vertices.Length).Sum();\n            int totalIndices = ma.Select(_ => _.Indices.Length).Sum();\n\n            int vertAccumulator = 0;\n            ushort indexAccumulator = 0;\n\n            Vertices = new InteropVertex[totalVertices];\n            Indices = new ushort[totalIndices];\n            \n            for (int i = 0; i < numMeshes; i++)\n            {\n                int prevVertSum = 0;\n                int prevIndSum = 0;\n\n                // sum previous vertices\n                for (int j = 0; j < i; j++)\n                {\n                    prevVertSum += ma[j].Vertices.Length;\n                    prevIndSum += ma[j].Indices.Length;\n                }\n\n                foreach (var t in ma[i].Vertices)\n                {\n                    Vertices[vertAccumulator] = GetInteropVertex(ma[i].Vertices[vertAccumulator - prevVertSum]);\n                    vertAccumulator++;\n                }\n\n                foreach (var t in ma[i].Indices)\n                {\n                    if (i == 0)\n                    {\n                        Indices[indexAccumulator] = ma[i].Indices[indexAccumulator - prevIndSum];\n                        indexAccumulator++;\n                    }\n                    else\n                    {\n                        Indices[indexAccumulator] = (ushort) (ma[i].Indices[indexAccumulator - prevIndSum] + prevVertSum);\n                        indexAccumulator++;\n                    }\n                }\n            }\n\n//            _UnmanagedPtr = Interop.loadMesh(Index,\n//                Vertices, Vertices.Length,\n//                Indices, Indices.Length);\n        }\n\n        public InteropMesh(Mesh m)\n        {\n            Vertices = new InteropVertex[m.Vertices.Length];\n            Indices = new ushort[m.Indices.Length];\n\n            for (int i = 0; i < Vertices.Length; i++)\n                Vertices[i] = GetInteropVertex(m.Vertices[i]);\n\n            for (int i = 0; i < Indices.Length; i++)\n                Indices[i] = m.Indices[i];\n            \n            NumVertexes = m.Vertices.Length;\n            NumIndices = m.Indices.Length;\n\n            BoneListSize = (int) m.Model.Definition.BoneLists[m.Header.BoneListIndex].ActualCount;\n            BoneList = new ushort[BoneListSize];\n\n            // copy actual amount to boneList\n            for (int i = 0; i < BoneListSize; i++)\n                BoneList[i] = m.Model.Definition.BoneLists[m.Header.BoneListIndex].Bones[i];\n            \n            _UnmanagedPtr = Interop.loadMesh(Index,\n                                            Vertices, m.Vertices.Length,\n                                            Indices, m.Indices.Length,\n                                            BoneList, BoneListSize);\n        }\n\n        private InteropVector4 GetInteropVector4(Vector3 v3)\n        {\n            Vector4 v = new Vector4();\n            v.X = v3.X;\n            v.Y = v3.Y;\n            v.Z = v3.Z;\n            v.W = 1;\n\n            return GetInteropVector4(v);\n        }\n\n        private InteropVector4 GetInteropVector4(Vector4 v4)\n        {\n            InteropVector4 iv = new InteropVector4();\n            \n            iv.X = v4.X;\n            iv.Y = v4.Y;\n            iv.Z = v4.Z;\n            iv.W = v4.W;\n\n            return iv;\n        }\n\n        private InteropVector4 GetInteropVector4(Vector4? v4, InteropVector4 dv)\n        {\n            if (v4.HasValue)\n                return GetInteropVector4(v4.Value);\n            return dv;\n        }\n\n        private InteropVector4 GetInteropVector4(Vector3? v3, InteropVector4 dv) {\n            if (v3.HasValue)\n                return GetInteropVector4(v3.Value);\n            return dv;\n        }\n\n        private InteropVertex GetInteropVertex(Vertex v)\n        {\n            InteropVertex iv = new InteropVertex();\n\n            InteropVector4 dvOne = new InteropVector4();\n            dvOne.X = 1;\n            dvOne.Y = 1;\n            dvOne.Z = 1;\n            dvOne.W = 1;\n\n            InteropVector4 dvZero = new InteropVector4();\n            dvZero.X = 0;\n            dvZero.Y = 0;\n            dvZero.W = 0;\n            dvZero.W = 0;\n\n            iv.Position = GetInteropVector4(v.Position, dvZero);\n            iv.Normal = GetInteropVector4(v.Normal, dvZero);\n            iv.UV = GetInteropVector4(v.UV, dvZero);\n            iv.BlendWeights = GetInteropVector4(v.BlendWeights, dvZero);\n            iv.BlendIndices = v.BlendIndices.GetValueOrDefault(0);\n            iv.Color = GetInteropVector4(v.Color, dvOne);\n            iv.Tangent1 = GetInteropVector4(v.Tangent1, dvZero);\n            iv.Tangent2 = GetInteropVector4(v.Tangent2, dvZero);\n\n            return iv;\n        }\n\n        private bool _IsDisposed = false;\n\n        protected virtual void Dispose(bool disposing) {\n\n            if (!_IsDisposed)\n            {\n                if (_UnmanagedPtr != IntPtr.Zero)\n                    Interop.unloadMesh(_UnmanagedPtr);\n                _UnmanagedPtr = IntPtr.Zero;\n\n                _IsDisposed = true;\n            }\n        }\n\n        ~InteropMesh() {\n            Dispose(false);\n        }\n        \n        public void Dispose() {\n            Dispose(true);\n            GC.SuppressFinalize(this);\n        }\n    }\n}\n"
  },
  {
    "path": "SaintCoinach.Graphics.Viewer/Interop/InteropTransform.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace SaintCoinach.Graphics.Viewer.Interop {\n    [System.Runtime.InteropServices.StructLayout(System.Runtime.InteropServices.LayoutKind.Sequential)]\n    struct InteropTransform {\n        public InteropVector4 Translation;\n        public InteropVector4 Scale;\n        public InteropVector4 Rotation;\n\n        public SharpDX.Matrix ToTransformationMatrix() {\n            return SharpDX.Matrix.Scaling(Scale.X, Scale.Y, Scale.Z)\n                 * SharpDX.Matrix.RotationQuaternion(new SharpDX.Quaternion(Rotation.X, Rotation.Y, Rotation.Z, Rotation.W))\n                 * SharpDX.Matrix.Translation(Translation.X, Translation.Y, Translation.Z);\n        }\n    }\n}\n"
  },
  {
    "path": "SaintCoinach.Graphics.Viewer/Interop/InteropVector4.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace SaintCoinach.Graphics.Viewer.Interop {\n    [System.Runtime.InteropServices.StructLayout(System.Runtime.InteropServices.LayoutKind.Sequential)]\n    struct InteropVector4 {\n        public float X;\n        public float Y;\n        public float Z;\n        public float W;\n    }\n}\n"
  },
  {
    "path": "SaintCoinach.Graphics.Viewer/Interop/InteropVertex.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Runtime.InteropServices;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace SaintCoinach.Graphics.Viewer.Interop {\n    [System.Runtime.InteropServices.StructLayout(System.Runtime.InteropServices.LayoutKind.Sequential)]\n    struct InteropVertex {\n        public InteropVector4 Position;\n        public InteropVector4 BlendWeights;\n        public uint BlendIndices;\n        public InteropVector4 Normal;\n\n        public InteropVector4 UV;\n        public InteropVector4 Color;\n        public InteropVector4 Tangent1;\n        public InteropVector4 Tangent2;\n    }\n}\n"
  },
  {
    "path": "SaintCoinach.Graphics.Viewer/Keyboard.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nusing SharpDX.Multimedia;\n\nusing Keys = System.Windows.Forms.Keys;\n\nnamespace SaintCoinach.Graphics.Viewer {\n    public class Keyboard : IUpdateableComponent {\n        #region Fields\n        private List<Keys> _ReleasedKeys = new List<Keys>();\n        private List<Keys> _PressedKeys = new List<Keys>();\n        private List<Keys> _DownKeys = new List<Keys>();\n\n        private Engine _Engine;\n        #endregion\n\n        #region Constructor\n        public Keyboard(Engine engine) {\n            _Engine = engine;\n        }\n        #endregion\n\n        #region Query\n        public bool IsKeyDown(Keys key) {\n            return _DownKeys.Contains(key);\n        }\n        public bool IsKeyUp(Keys key) {\n            return !_DownKeys.Contains(key);\n        }\n        public bool WasKeyReleased(Keys key) {\n            return _ReleasedKeys.Contains(key);\n        }\n        public bool WasKeyPressed(Keys key) {\n            return _PressedKeys.Contains(key);\n        }\n        #endregion\n\n        #region IUpdateable Members\n        private bool _IsEnabled = true;\n        public bool IsEnabled { get { return _IsEnabled; } set { _IsEnabled = value; } }\n\n        public void Update(EngineTime time) {\n            _ReleasedKeys.Clear();\n            _PressedKeys.Clear();\n\n            var downKeys = _Engine.InputService.GetDownKeys();\n            _PressedKeys.AddRange(downKeys.Except(_DownKeys));\n            _ReleasedKeys.AddRange(_DownKeys.Except(downKeys));\n\n            _DownKeys.AddRange(_PressedKeys);\n            foreach (var k in _ReleasedKeys)\n                _DownKeys.Remove(k);\n        }\n        #endregion\n    }\n}\n"
  },
  {
    "path": "SaintCoinach.Graphics.Viewer/MaterialFactory.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace SaintCoinach.Graphics.Viewer {\n    public class MaterialFactory : IDisposable {\n        #region Fields\n        private Engine _Engine;\n        private Dictionary<string, Content.MaterialBase> _Materials = new Dictionary<string, Content.MaterialBase>();\n        #endregion\n\n        #region Constructor\n        public MaterialFactory(Engine engine) {\n            _Engine = engine;\n        }\n        #endregion\n\n        #region Get\n        public Content.MaterialBase Get(Material material) {\n            var key = material.File.Path;\n\n            Content.MaterialBase content;\n            if (_Materials.TryGetValue(key, out content))\n                return content;\n\n            switch (material.Shader) {\n                case \"character.shpk\":\n                    content = new Content.CharacterMaterial(_Engine, material);\n                    break;\n                case \"hair.shpk\":\n                    content = new Content.HairMaterial(_Engine, material);\n                    break;\n                case \"skin.shpk\":\n                    content = new Content.SkinMaterial(_Engine, material);\n                    break;\n                case \"bg.shpk\":\n                case \"bguvscroll.shpk\": // TODO: Actually make it UV-scroll\n                    content = new Content.BgMaterial(_Engine, material);\n                    break;\n                case \"iris.shpk\":\n                    content = new Content.IrisMaterial(_Engine, material);\n                    break;\n                case \"bgcolorchange.shpk\":\n                    content = new Content.BgColorChangeMaterial(_Engine, material);\n                    break;\n                case \"crystal.shpk\":\n                    content = new Content.CrystalMaterial(_Engine, material);\n                    break;\n                default:\n                    throw new NotSupportedException(string.Format(\"Shader type {0} is not supported.\", material.Shader));\n            }\n\n            _Materials.Add(key, content);\n            return content;\n        }\n        #endregion\n\n        #region IDisposable Members\n\n        public void Dispose() {\n            UnloadAll();\n        }\n\n        public void UnloadAll() {\n            _Materials.Clear();\n        }\n        #endregion\n    }\n}\n"
  },
  {
    "path": "SaintCoinach.Graphics.Viewer/ModelFactory.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace SaintCoinach.Graphics.Viewer {\n    public class ModelFactory : IDisposable {\n        #region Fields\n        private Engine _Engine;\n        private Dictionary<Tuple<string, ModelQuality>, Content.PrimitiveModel> _Primitives = new Dictionary<Tuple<string, ModelQuality>, Content.PrimitiveModel>();\n        #endregion\n\n        #region Constructor\n        public ModelFactory(Engine engine) {\n            _Engine = engine;\n        }\n        #endregion\n\n        #region Get\n        public Content.PrimitiveModel Get(ModelDefinition definition, ModelQuality quality) {\n            var key = Tuple.Create(definition.File.Path, quality);\n\n            Content.PrimitiveModel primitive;\n            if (_Primitives.TryGetValue(key, out primitive))\n                return primitive;\n\n            var mdl = definition.GetModel(quality);\n\n            primitive = new Content.PrimitiveModel(_Engine.Device, mdl);\n            _Primitives.Add(key, primitive);\n\n            return primitive;\n        }\n        #endregion\n\n        #region IDisposable Members\n\n        public void Dispose() {\n            UnloadAll();\n        }\n\n        public void UnloadAll() {\n            foreach (var p in _Primitives.Values)\n                p.Dispose();\n            _Primitives.Clear();\n        }\n\n        #endregion\n    }\n}\n"
  },
  {
    "path": "SaintCoinach.Graphics.Viewer/Mouse.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace SaintCoinach.Graphics.Viewer {\n    using SharpDX;\n    using SharpDX.Multimedia;\n\n    public class Mouse : IUpdateableComponent {\n        #region Fields\n        private MouseState _CurrentState;\n\n        private Engine _Engine;\n        #endregion\n\n        #region Properties\n        public MouseState GetState() {\n            return _CurrentState;\n        }\n        #endregion\n\n        #region Constructor\n        public Mouse(Engine engine) {\n            _Engine = engine;\n        }\n        #endregion\n\n        #region IUpdateableComponent Members\n\n        private bool _IsEnabled = true;\n        public bool IsEnabled {\n            get { return _IsEnabled; }\n            set { _IsEnabled = value; }\n        }\n\n        public void Update(EngineTime engineTime) {\n            var buttons = _Engine.InputService.GetDownMouseButtons();\n            var pos = _Engine.InputService.MousePosition;\n            _CurrentState.AbsolutePosition = new Vector2(pos.X, pos.Y);\n            _CurrentState.RelativePosition = new Vector2(_CurrentState.AbsolutePosition.X / (float)_Engine.ViewportSize.Width, _CurrentState.AbsolutePosition.Y / (float)_Engine.ViewportSize.Height);\n            _CurrentState.LeftButton = buttons.Contains(System.Windows.Forms.MouseButtons.Left);\n            _CurrentState.RightButton = buttons.Contains(System.Windows.Forms.MouseButtons.Right);\n            _CurrentState.MiddleButton = buttons.Contains(System.Windows.Forms.MouseButtons.Middle);\n        }\n\n        #endregion\n    }\n}\n"
  },
  {
    "path": "SaintCoinach.Graphics.Viewer/MouseState.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace SaintCoinach.Graphics.Viewer {\n    using SharpDX;\n\n    public struct MouseState {\n        public bool LeftButton { get; internal set; }\n        public bool RightButton { get; internal set; }\n        public bool MiddleButton { get; internal set; }\n\n        public Vector2 AbsolutePosition { get; internal set; }\n        public Vector2 RelativePosition { get; internal set; }\n    }\n}\n"
  },
  {
    "path": "SaintCoinach.Graphics.Viewer/ParametersExtensions.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace SaintCoinach.Graphics.Viewer {\n    static class ParametersExtensions {\n        public static T GetValueOrDefault<T>(this Data.ParametersBase self, string key) {\n            return self.GetValueOrDefault(key, default(T));\n        }\n        public static T GetValueOrDefault<T>(this Data.ParametersBase self, string key, T defaultValue) {\n            if (self == null)\n                return defaultValue;\n            T ret;\n            if (self.TryGetValue(key, out ret))\n                return ret;\n            return defaultValue;\n        }\n    }\n}\n"
  },
  {
    "path": "SaintCoinach.Graphics.Viewer/Properties/AssemblyInfo.cs",
    "content": "﻿using System.Reflection;\nusing System.Runtime.CompilerServices;\nusing System.Runtime.InteropServices;\n\n// Setting ComVisible to false makes the types in this assembly not visible \n// to COM components.  If you need to access a type in this assembly from \n// COM, set the ComVisible attribute to true on that type.\n[assembly: ComVisible(false)]\n\n// The following GUID is for the ID of the typelib if this project is exposed to COM\n[assembly: Guid(\"961284df-8448-4a90-9fd3-840e4b1a2f30\")]\n"
  },
  {
    "path": "SaintCoinach.Graphics.Viewer/RendererSources/BaseImageRendererSource.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace SaintCoinach.Graphics.Viewer.RendererSources {\n    public abstract class BaseImageRendererSource : IImageRendererSource {\n        #region Fields\n        protected Engine _Engine;\n        #endregion\n\n        #region IImageRendererSource Members\n\n        public string CurrentName { get; protected set; }\n\n        public IComponent CurrentComponent { get; protected set; }\n\n        public BoundingBox CurrentBoundingBox { get; protected set; }\n\n        public System.IO.FileInfo CurrentTargetFile { get; protected set; }\n\n        public bool RenderFromOppositeSide { get; protected set; }\n\n        public abstract bool MoveNext();\n\n        public virtual void Reset(Engine engine) {\n            _Engine = engine;\n        }\n\n        #endregion\n    }\n}\n"
  },
  {
    "path": "SaintCoinach.Graphics.Viewer/RendererSources/EquipmentImageRendererSource.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace SaintCoinach.Graphics.Viewer.RendererSources {\n    public class EquipmentImageRendererSource : BaseImageRendererSource {\n        #region Fields\n        private bool _IsCurrentStained;\n        private ModelDefinition _CurrentModel;\n        private ImcVariant _CurrentVariant;\n        private IEnumerator<Xiv.Items.Equipment> _EquipmentIterator;\n        private IEnumerator<Xiv.Stain> _StainEnumerator;\n        #endregion\n\n        #region Constructor\n        public EquipmentImageRendererSource(IEnumerable<Xiv.Item> items) : this(items, null) { }\n        public EquipmentImageRendererSource(IEnumerable<Xiv.Item> items, IEnumerable<Xiv.Stain> stains) : this((IEnumerable<Xiv.Items.Equipment>)items.OfType<Xiv.Items.Equipment>(), stains) { }\n        public EquipmentImageRendererSource(IEnumerable<Xiv.Items.Equipment> equipmentItems) : this(equipmentItems, null) { }\n        public EquipmentImageRendererSource(IEnumerable<Xiv.Items.Equipment> equipmentItems, IEnumerable<Xiv.Stain> stains) {\n            // Doing a ToList first LINQ enumerators can't reset.\n            _EquipmentIterator = equipmentItems.ToList().GetEnumerator();\n            if (stains != null)\n                _StainEnumerator = stains.ToList().GetEnumerator();\n        }\n        #endregion\n\n        #region IImageRendererSource Members\n\n        public override bool MoveNext() {\n            if (MoveNextStain())\n                return true;\n\n            while (_EquipmentIterator.MoveNext()) {\n                var eq = _EquipmentIterator.Current;\n                var charType = eq.GetModelCharacterType();\n                    charType += 100;\n\n                _CurrentModel = eq.GetModel(charType, out _CurrentVariant);\n                if (_CurrentModel == null)\n                    continue;\n\n                RenderFromOppositeSide = eq.ItemUICategory.Key == 1 || eq.ItemUICategory.Key == 10;     // PGL/MNK and books\n                CurrentBoundingBox = _CurrentModel.BoundingBoxes.Value2.Scale(1.35f);    // Not sure what the first one is for, but some are very strange.\n\n                if (_StainEnumerator == null)\n                    _IsCurrentStained = false;\n                else\n                    _IsCurrentStained = eq.IsDyeable;\n\n                if (_IsCurrentStained) {\n                    _StainEnumerator.Reset();\n                    MoveNextStain();\n                } else {\n                    CurrentComponent = new Content.ContentModel(_Engine, _CurrentVariant, _CurrentModel);\n                    CurrentTargetFile = GetTargetFile(_EquipmentIterator.Current);\n                    CurrentName = GetName(_EquipmentIterator.Current);\n                }\n\n                return true;\n            }\n            return false;\n        }\n        private bool MoveNextStain() {\n            if (!_IsCurrentStained)\n                return false;\n            if (!_StainEnumerator.MoveNext())\n                return false;\n\n            var variant = new ModelVariantIdentifier {\n                ImcVariant = _CurrentVariant,\n                StainKey = null\n            };\n            if(_StainEnumerator.Current.Key > 0)\n                variant.StainKey = _StainEnumerator.Current.Key;\n\n            CurrentComponent = new Content.ContentModel(_Engine, variant, _CurrentModel);\n            CurrentTargetFile = GetTargetFile(_EquipmentIterator.Current, _StainEnumerator.Current);\n            CurrentName = GetName(_EquipmentIterator.Current, _StainEnumerator.Current);\n\n            return true;\n        }\n\n        protected System.IO.FileInfo GetTargetFile(Xiv.Items.Equipment item) {\n            return GetTargetFile(item, null);\n        }\n        protected virtual System.IO.FileInfo GetTargetFile(Xiv.Items.Equipment item, Xiv.Stain stain) {\n            const int DirectorySeperationInterval = 100;\n\n            var fileName = string.Format(\"{0} - {1}\", item.Key, item.Name);\n            if (stain != null)\n                fileName += string.Format(\" (s{0:D4} {1})\", stain.Key, stain.Name);\n            return new System.IO.FileInfo(System.IO.Path.Combine(\"Renders\", (item.Key - item.Key % DirectorySeperationInterval).ToString(), fileName));\n        }\n        protected string GetName(Xiv.Items.Equipment item) {\n            return GetName(item, null);\n        }\n        protected virtual string GetName(Xiv.Items.Equipment item, Xiv.Stain stain) {\n            var sb = new StringBuilder();\n            sb.Append(item.Name);\n            if(stain != null && stain.Key > 0)\n                sb.AppendFormat(\" (s{0:D4} {1})\", stain.Key, stain.Name);\n            return sb.ToString();\n        }\n\n        public override void Reset(Engine engine) {\n            base.Reset(engine);\n\n            _EquipmentIterator.Reset();\n        }\n\n        #endregion\n    }\n}\n"
  },
  {
    "path": "SaintCoinach.Graphics.Viewer/RendererSources/MonsterImageRendererSource.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace SaintCoinach.Graphics.Viewer.RendererSources {\n    public class MonsterImageRendererSource : BaseImageRendererSource {\n        private IEnumerator<Xiv.ModelChara> _MonsterIterator;\n\n        public MonsterImageRendererSource(IEnumerable<Xiv.ModelChara> monsters) {\n            _MonsterIterator = monsters.ToList().GetEnumerator();\n        }\n\n        public override bool MoveNext() {\n            while (_MonsterIterator.MoveNext()) {\n                var modelChara = _MonsterIterator.Current;\n                var modelIdentifier = $\"{modelChara.ModelKey}-{modelChara.BaseKey}-{modelChara.Variant}\";\n                CurrentName = $\"{modelChara.Type}/{modelIdentifier}\";\n\n                try {\n                    var result = modelChara.GetModelDefinition();\n                    var model = result.Item1;\n                    var variant = result.Item2;\n                    CurrentComponent = new Content.ContentModel(_Engine, variant, model);\n                    CurrentTargetFile = new System.IO.FileInfo(System.IO.Path.Combine(\"Renders\", \"Monsters\", modelIdentifier));\n                    CurrentBoundingBox = model.BoundingBoxes.Value2.Scale(1.3f);\n                } catch (InvalidOperationException) {\n                    // Can't find this model def, move to the next.\n                    continue;\n                }\n\n                return true;\n            }\n\n            return false;\n        }\n\n        public override void Reset(Engine engine) {\n            base.Reset(engine);\n\n            _MonsterIterator.Reset();\n        }\n    }\n}\n"
  },
  {
    "path": "SaintCoinach.Graphics.Viewer/SaintCoinach.Graphics.Viewer.csproj",
    "content": "﻿<Project Sdk=\"Microsoft.NET.Sdk\">\n  <PropertyGroup>\n    <ProjectGuid>{226BF71E-334F-4585-8FEC-4239704CF539}</ProjectGuid>\n    <TargetFramework>net7.0-windows</TargetFramework>\n    <AssemblyTitle>SaintCoinach.Graphics.Viewer</AssemblyTitle>\n    <Product>SaintCoinach.Graphics.Viewer</Product>\n    <Copyright>Copyright ©  2015</Copyright>\n    <AllowUnsafeBlocks>true</AllowUnsafeBlocks>\n    <OutputPath>bin\\$(Configuration)\\</OutputPath>\n  </PropertyGroup>\n  <PropertyGroup Condition=\" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' \">\n    <DebugType>full</DebugType>\n  </PropertyGroup>\n  <PropertyGroup Condition=\" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' \">\n    <DebugType>pdbonly</DebugType>\n  </PropertyGroup>\n  <ItemGroup>\n    <PackageReference Include=\"Ookii.Dialogs.Wpf\" Version=\"5.0.1\" />\n    <PackageReference Include=\"SharpDX\" Version=\"4.2.0\" />\n    <PackageReference Include=\"SharpDX.D3DCompiler\" Version=\"4.2.0\" />\n    <PackageReference Include=\"SharpDX.Desktop\" Version=\"4.2.0\" />\n    <PackageReference Include=\"SharpDX.Direct3D10\" Version=\"4.2.0\" />\n    <PackageReference Include=\"SharpDX.Direct3D11\" Version=\"4.2.0\" />\n    <PackageReference Include=\"SharpDX.Direct3D11.Effects\" Version=\"4.2.0\" />\n    <PackageReference Include=\"SharpDX.DXGI\" Version=\"4.2.0\" />\n    <PackageReference Include=\"SharpDX.Mathematics\" Version=\"4.2.0\" />\n  </ItemGroup>\n  <ItemGroup>\n    <Compile Update=\"Keyboard.cs\">\n      <ExcludeFromStyleCop>True</ExcludeFromStyleCop>\n    </Compile>\n  </ItemGroup>\n  <ItemGroup>\n    <EmbeddedResource Include=\"Viewer.ico\" />\n    <Content Include=\"Effects\\HLSL\\BasicEffect.fx\">\n      <CopyToOutputDirectory>Always</CopyToOutputDirectory>\n    </Content>\n    <Content Include=\"Effects\\HLSL\\BgUvScroll.fx\">\n      <CopyToOutputDirectory>Always</CopyToOutputDirectory>\n    </Content>\n    <Content Include=\"Effects\\HLSL\\Crystal.fx\">\n      <CopyToOutputDirectory>Always</CopyToOutputDirectory>\n    </Content>\n    <Content Include=\"Effects\\HLSL\\BgColorChange.fx\">\n      <CopyToOutputDirectory>Always</CopyToOutputDirectory>\n    </Content>\n    <Content Include=\"Effects\\HLSL\\Bg.fx\">\n      <CopyToOutputDirectory>Always</CopyToOutputDirectory>\n    </Content>\n    <Content Include=\"Effects\\HLSL\\Common.fxh\">\n      <CopyToOutputDirectory>Always</CopyToOutputDirectory>\n    </Content>\n    <Content Include=\"Effects\\HLSL\\Hair.fx\">\n      <CopyToOutputDirectory>Always</CopyToOutputDirectory>\n    </Content>\n    <Content Include=\"Effects\\HLSL\\Lighting.fxh\">\n      <CopyToOutputDirectory>Always</CopyToOutputDirectory>\n    </Content>\n    <Content Include=\"Effects\\HLSL\\Iris.fx\">\n      <CopyToOutputDirectory>Always</CopyToOutputDirectory>\n    </Content>\n    <Content Include=\"Effects\\HLSL\\Skin.fx\">\n      <CopyToOutputDirectory>Always</CopyToOutputDirectory>\n    </Content>\n    <Content Include=\"Effects\\HLSL\\Structures.fxh\">\n      <CopyToOutputDirectory>Always</CopyToOutputDirectory>\n    </Content>\n    <Content Include=\"Effects\\HLSL\\CustomizeParameter.fxh\">\n      <CopyToOutputDirectory>Always</CopyToOutputDirectory>\n    </Content>\n    <Content Include=\"Effects\\HLSL\\Common.Skinned.fxh\">\n      <CopyToOutputDirectory>Always</CopyToOutputDirectory>\n    </Content>\n  </ItemGroup>\n  <ItemGroup>\n    <Content Include=\"Effects\\HLSL\\Character.fx\">\n      <CopyToOutputDirectory>Always</CopyToOutputDirectory>\n    </Content>\n    <Content Include=\"fbxInterop.dll\">\n      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>\n    </Content>\n    <Content Include=\"hkAnimationInterop.dll\">\n      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>\n    </Content>\n    <Content Include=\"libfbxsdk.dll\">\n      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>\n    </Content>\n    <Content Include=\"sharpdx_direct3d11_1_effects_x64.dll\">\n      <CopyToOutputDirectory>Always</CopyToOutputDirectory>\n    </Content>\n    <Content Include=\"sharpdx_direct3d11_1_effects_x86.dll\">\n      <CopyToOutputDirectory>Always</CopyToOutputDirectory>\n    </Content>\n  </ItemGroup>\n  <ItemGroup>\n    <ProjectReference Include=\"..\\SaintCoinach\\SaintCoinach.csproj\" />\n  </ItemGroup>\n</Project>"
  },
  {
    "path": "SaintCoinach.Graphics.Viewer/Skeleton.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Runtime.InteropServices;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace SaintCoinach.Graphics.Viewer {\n    using SharpDX;\n    using Interop;\n\n    public class Skeleton : IDisposable {\n        #region Interop\n        static class Interop {\n            [DllImport(\"hkAnimationInterop.dll\", CallingConvention = CallingConvention.Cdecl)]\n            public static extern IntPtr loadSkeleton(byte[] rigData, int rigSize);\n\n            [DllImport(\"hkAnimationInterop.dll\", CallingConvention = CallingConvention.Cdecl)]\n            public static extern void unloadSkeleton(IntPtr ptr);\n\n            [DllImport(\"hkAnimationInterop.dll\", CallingConvention = CallingConvention.Cdecl)]\n            public static extern int getNumBones(IntPtr ptr);\n\n            [DllImport(\"hkAnimationInterop.dll\", CallingConvention = CallingConvention.Cdecl)]\n            public static extern int getBoneNames(IntPtr ptr, [In, Out] string[] output);\n\n            [DllImport(\"hkAnimationInterop.dll\", CallingConvention = CallingConvention.Cdecl)]\n            public static extern int getReferencePose(IntPtr ptr, [In, Out] InteropTransform[] output);\n\n            [DllImport(\"hkAnimationInterop.dll\", CallingConvention = CallingConvention.Cdecl)]\n            public static extern int getParentIndices(IntPtr ptr, [In, Out] int[] output);\n        }\n        #endregion\n\n        #region Fields\n        internal IntPtr _UnmanagedPtr;\n        #endregion\n\n        #region Properties\n        public SklbFile File { get; private set; }\n        public int BoneCount { get; private set; }\n        public int[] ParentBoneIndices { get; private set; }\n        public Matrix[] ReferencePose { get; private set; }\n        public string[] BoneNames { get; private set; }\n        #endregion\n\n        #region Constructor\n        public Skeleton(SklbFile file) {\n            this.File = file;\n\n            _UnmanagedPtr = HavokInterop.Execute(() => Interop.loadSkeleton(file.HavokData, file.HavokData.Length));\n\n            BoneCount = HavokInterop.Execute(() => Interop.getNumBones(_UnmanagedPtr));\n\n            BoneNames = new string[BoneCount];\n            HavokInterop.Execute(() => Interop.getBoneNames(_UnmanagedPtr, BoneNames));\n\n            ParentBoneIndices = new int[BoneCount];\n            HavokInterop.Execute(() => Interop.getParentIndices(_UnmanagedPtr, ParentBoneIndices));\n\n            ReferencePose = new Matrix[BoneCount];\n            var referencePoseLocal = new InteropTransform[BoneCount];\n            HavokInterop.Execute(() => Interop.getReferencePose(_UnmanagedPtr, referencePoseLocal));\n            for (var target = 0; target < BoneCount; ++target) {\n                var current = target;\n                ReferencePose[target] = Matrix.Identity;\n                while (current >= 0) {\n                    ReferencePose[target] = ReferencePose[target] * referencePoseLocal[current].ToTransformationMatrix();\n\n                    current = ParentBoneIndices[current];\n                }\n            }\n        }\n        #endregion\n\n        #region IDisposable Support\n        private bool _IsDisposed = false; // To detect redundant calls\n\n        protected virtual void Dispose(bool disposing) {\n            if (!_IsDisposed) {\n                if (_UnmanagedPtr != IntPtr.Zero)\n                    HavokInterop.Execute(() => Interop.unloadSkeleton(_UnmanagedPtr));\n                _UnmanagedPtr = IntPtr.Zero;\n\n                _IsDisposed = true;\n            }\n        }\n\n        ~Skeleton() {\n            // Do not change this code. Put cleanup code in Dispose(bool disposing) above.\n            Dispose(false);\n        }\n\n        // This code added to correctly implement the disposable pattern.\n        public void Dispose() {\n            // Do not change this code. Put cleanup code in Dispose(bool disposing) above.\n            Dispose(true);\n\n            GC.SuppressFinalize(this);\n        }\n        #endregion\n    }\n}\n"
  },
  {
    "path": "SaintCoinach.Graphics.Viewer/TextureFactory.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\nusing SharpDX.Direct3D11;\n\nnamespace SaintCoinach.Graphics.Viewer {\n    using SharpDX;\n    using SharpDX.Direct3D11;\n\n    public class TextureFactory : IDisposable {\n        #region Fields\n        private Engine _Engine;\n        private Dictionary<string, ShaderResourceView> _Resources = new Dictionary<string, ShaderResourceView>();\n        private Dictionary<string, Texture2D> _Textures = new Dictionary<string, Texture2D>();\n        #endregion\n\n        #region Constructor\n        public TextureFactory(Engine engine) {\n            _Engine = engine;\n        }\n        #endregion\n\n        #region Get\n        public ShaderResourceView GetResource(Imaging.ImageFile source) {\n            var key = source.Path;\n\n            ShaderResourceView res;\n            if (_Resources.TryGetValue(key, out res))\n                return res;\n\n            res = new ShaderResourceView(_Engine.Device, GetTexture(source));\n            _Resources.Add(key, res);\n            return res;\n        }\n        public unsafe Texture2D GetTexture(Imaging.ImageFile source) {\n            var key = source.Path;\n\n            Texture2D tex;\n            if (_Textures.TryGetValue(key, out tex))\n                return tex;\n\n            byte[] buffer;\n            var desc = new Texture2DDescription {\n                ArraySize = 1,\n                BindFlags = BindFlags.ShaderResource,\n                CpuAccessFlags = CpuAccessFlags.None,\n                Height = source.Height,\n                Width = source.Width,\n                MipLevels = 1,\n                OptionFlags = ResourceOptionFlags.None,\n                SampleDescription = new SharpDX.DXGI.SampleDescription(1, 0),\n                Usage = ResourceUsage.Immutable\n            };\n            if (source.Format == Imaging.ImageFormat.A16R16G16B16Float) {\n                buffer = source.GetData();\n                desc.Format = SharpDX.DXGI.Format.R16G16B16A16_Float;\n            } else {\n                buffer = Imaging.ImageConverter.GetA8R8G8B8(source);\n                for (var i = 0; i < buffer.Length; i += 4) {\n                    var r = buffer[i + 0];\n                    var b = buffer[i + 2];\n\n                    buffer[i + 0] = b;\n                    buffer[i + 2] = r;\n                }\n                desc.Format = SharpDX.DXGI.Format.R8G8B8A8_UNorm;\n                if (source.Format == Imaging.ImageFormat.A8R8G8B8_Cube)\n                    desc.ArraySize = 6;\n            }\n\n            fixed (byte* p = buffer) {\n                var ptr = (IntPtr)p;\n                var pitch = SharpDX.DXGI.FormatHelper.SizeOfInBytes(desc.Format) * source.Width;\n                var dataRects = new DataRectangle[desc.ArraySize];\n                for (var i = 0; i < desc.ArraySize; ++i)\n                    dataRects[i] = new DataRectangle(ptr + i * pitch * source.Height, pitch);\n                tex = new Texture2D(_Engine.Device, desc, dataRects);\n            }\n\n            _Textures.Add(key, tex);\n            return tex;\n        }\n        #endregion\n\n        #region IDisposable Members\n\n        public void Dispose() {\n            UnloadAll();\n        }\n\n        public void UnloadAll() {\n            foreach (var v in _Resources.Values)\n                v.Dispose();\n            foreach (var v in _Textures.Values)\n                v.Dispose();\n            _Resources.Clear();\n            _Textures.Clear();\n        }\n        #endregion\n    }\n}\n"
  },
  {
    "path": "SaintCoinach.Graphics.Viewer/VectorConverter.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace SaintCoinach.Graphics.Viewer {\n    public static class VectorConverter {\n        public static SharpDX.Vector3 ToDx(this Graphics.Vector3 self) {\n            return new SharpDX.Vector3 {\n                X = self.X,\n                Y = self.Y,\n                Z = self.Z\n            };\n        }\n        public static SharpDX.Vector3 ToDx(this Graphics.Vector3? self, SharpDX.Vector3 defaultValue) {\n            if (self.HasValue)\n                return self.Value.ToDx();\n            return defaultValue;\n        }\n        public static SharpDX.Vector4 ToDx(this Graphics.Vector4 self) {\n            return new SharpDX.Vector4 {\n                X = self.X,\n                Y = self.Y,\n                Z = self.Z,\n                W = self.W\n            };\n        }\n        public static SharpDX.Vector3 ToDx3(this Graphics.Vector4 self) {\n            return new SharpDX.Vector3 {\n                X = self.X,\n                Y = self.Y,\n                Z = self.Z\n            };\n        }\n        public static SharpDX.Vector4 ToDx(this Graphics.Vector4? self, SharpDX.Vector4 defaultValue) {\n            if (self.HasValue)\n                return self.Value.ToDx();\n            return defaultValue;\n        }\n    }\n}\n"
  },
  {
    "path": "SaintCoinach.Graphics.Viewer/Vertex3D.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Runtime.InteropServices;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace SaintCoinach.Graphics.Viewer {\n    using SharpDX;\n    using SharpDX.Direct3D11;\n    using SharpDX.DXGI;\n\n    [StructLayout(LayoutKind.Sequential)]\n    public struct Vertex3D {\n        public Vector4 Position;\n        public Vector3 Normal;\n        public Vector4 UV;\n        public Vector4 BlendWeights;\n        public uint BlendIndices;\n        public Vector4 Color;\n        public Vector4 Tangent1;\n        public Vector4 Tangent2;\n\n        public static InputElement[] InputElements {\n            get {\n                return new[] {\n                    new InputElement(\"SV_Position\", 0, Format.R32G32B32A32_Float, InputElement.AppendAligned, 0),\n                    new InputElement(\"NORMAL\", 0, Format.R32G32B32_Float, InputElement.AppendAligned, 0),\n                    new InputElement(\"TEXCOORD\", 0, Format.R32G32B32A32_Float, InputElement.AppendAligned, 0),\n                    new InputElement(\"BLENDWEIGHT\", 0, Format.R32G32B32A32_Float, InputElement.AppendAligned, 0),\n                    new InputElement(\"BLENDINDICES\", 0, Format.R8G8B8A8_UInt, InputElement.AppendAligned, 0),\n                    new InputElement(\"COLOR\", 0, Format.R32G32B32A32_Float, InputElement.AppendAligned, 0),\n                    new InputElement(\"TANGENT\", 0, Format.R32G32B32A32_Float, InputElement.AppendAligned, 0),\n                    new InputElement(\"TANGENT\", 1, Format.R32G32B32A32_Float, InputElement.AppendAligned, 0),\n                };\n            }\n        }\n\n        public Vertex3D(Vertex src) {\n            this.Position = src.Position.ToDx(Vector4.Zero);\n            this.Position.W = 1;\n            this.Normal = src.Normal.ToDx(Vector3.Zero);\n            this.UV = src.UV.ToDx(Vector4.Zero);\n            this.BlendWeights = src.BlendWeights.ToDx(Vector4.Zero);\n            this.BlendIndices = src.BlendIndices.GetValueOrDefault(0);\n            this.Color = src.Color.ToDx(Vector4.One);\n            this.Tangent2 = src.Tangent2.ToDx(Vector4.Zero);\n            this.Tangent1 = src.Tangent1.ToDx(Vector4.Zero);\n        }\n    }\n}\n"
  },
  {
    "path": "SaintCoinach.Graphics.Viewer/app.config",
    "content": "﻿<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<configuration>\n  <runtime>\n    <assemblyBinding xmlns=\"urn:schemas-microsoft-com:asm.v1\">\n      <dependentAssembly>\n        <assemblyIdentity name=\"SharpDX\" publicKeyToken=\"b4dcf0f35e5521f1\" culture=\"neutral\" />\n        <bindingRedirect oldVersion=\"0.0.0.0-4.2.0.0\" newVersion=\"4.2.0.0\" />\n      </dependentAssembly>\n      <dependentAssembly>\n        <assemblyIdentity name=\"SharpDX.DXGI\" publicKeyToken=\"b4dcf0f35e5521f1\" culture=\"neutral\" />\n        <bindingRedirect oldVersion=\"0.0.0.0-4.2.0.0\" newVersion=\"4.2.0.0\" />\n      </dependentAssembly>\n    </assemblyBinding>\n  </runtime>\n</configuration>"
  },
  {
    "path": "SaintCoinach.sln",
    "content": "﻿\nMicrosoft Visual Studio Solution File, Format Version 12.00\n# Visual Studio Version 16\nVisualStudioVersion = 16.0.30611.23\nMinimumVisualStudioVersion = 10.0.40219.1\nProject(\"{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}\") = \"SaintCoinach\", \"SaintCoinach\\SaintCoinach.csproj\", \"{8A7D65EB-3DE3-4631-B286-CC73A3126002}\"\nEndProject\nProject(\"{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}\") = \"DotSquish\", \"DotSquish\\DotSquish.csproj\", \"{F9681545-4BEA-4FD3-9AB9-A67BD37AB36D}\"\nEndProject\nProject(\"{2150E333-8FDC-42A3-9474-1A3956D46DE8}\") = \"Solution Items\", \"Solution Items\", \"{E64299C5-7894-4ADE-8010-28BF09EB49F1}\"\n\tProjectSection(SolutionItems) = preProject\n\t\tLICENSE = LICENSE\n\t\tREADME.md = README.md\n\tEndProjectSection\nEndProject\nProject(\"{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}\") = \"SaintCoinach.Cmd\", \"SaintCoinach.Cmd\\SaintCoinach.Cmd.csproj\", \"{957699E4-8310-4D1E-BA83-13B7A4932807}\"\nEndProject\nProject(\"{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}\") = \"SaintCoinach.Graphics.Viewer\", \"SaintCoinach.Graphics.Viewer\\SaintCoinach.Graphics.Viewer.csproj\", \"{226BF71E-334F-4585-8FEC-4239704CF539}\"\nEndProject\nProject(\"{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}\") = \"Godbert\", \"Godbert\\Godbert.csproj\", \"{6A5DA7FF-791E-4A43-BF65-B6942917F7D9}\"\nEndProject\nGlobal\n\tGlobalSection(SolutionConfigurationPlatforms) = preSolution\n\t\tDebug|Any CPU = Debug|Any CPU\n\t\tRelease|Any CPU = Release|Any CPU\n\tEndGlobalSection\n\tGlobalSection(ProjectConfigurationPlatforms) = postSolution\n\t\t{8A7D65EB-3DE3-4631-B286-CC73A3126002}.Debug|Any CPU.ActiveCfg = Debug|Any CPU\n\t\t{8A7D65EB-3DE3-4631-B286-CC73A3126002}.Debug|Any CPU.Build.0 = Debug|Any CPU\n\t\t{8A7D65EB-3DE3-4631-B286-CC73A3126002}.Release|Any CPU.ActiveCfg = Release|Any CPU\n\t\t{8A7D65EB-3DE3-4631-B286-CC73A3126002}.Release|Any CPU.Build.0 = Release|Any CPU\n\t\t{F9681545-4BEA-4FD3-9AB9-A67BD37AB36D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU\n\t\t{F9681545-4BEA-4FD3-9AB9-A67BD37AB36D}.Debug|Any CPU.Build.0 = Debug|Any CPU\n\t\t{F9681545-4BEA-4FD3-9AB9-A67BD37AB36D}.Release|Any CPU.ActiveCfg = Release|Any CPU\n\t\t{F9681545-4BEA-4FD3-9AB9-A67BD37AB36D}.Release|Any CPU.Build.0 = Release|Any CPU\n\t\t{957699E4-8310-4D1E-BA83-13B7A4932807}.Debug|Any CPU.ActiveCfg = Debug|Any CPU\n\t\t{957699E4-8310-4D1E-BA83-13B7A4932807}.Debug|Any CPU.Build.0 = Debug|Any CPU\n\t\t{957699E4-8310-4D1E-BA83-13B7A4932807}.Release|Any CPU.ActiveCfg = Release|Any CPU\n\t\t{957699E4-8310-4D1E-BA83-13B7A4932807}.Release|Any CPU.Build.0 = Release|Any CPU\n\t\t{226BF71E-334F-4585-8FEC-4239704CF539}.Debug|Any CPU.ActiveCfg = Debug|Any CPU\n\t\t{226BF71E-334F-4585-8FEC-4239704CF539}.Debug|Any CPU.Build.0 = Debug|Any CPU\n\t\t{226BF71E-334F-4585-8FEC-4239704CF539}.Release|Any CPU.ActiveCfg = Release|Any CPU\n\t\t{226BF71E-334F-4585-8FEC-4239704CF539}.Release|Any CPU.Build.0 = Release|Any CPU\n\t\t{6A5DA7FF-791E-4A43-BF65-B6942917F7D9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU\n\t\t{6A5DA7FF-791E-4A43-BF65-B6942917F7D9}.Debug|Any CPU.Build.0 = Debug|Any CPU\n\t\t{6A5DA7FF-791E-4A43-BF65-B6942917F7D9}.Release|Any CPU.ActiveCfg = Release|Any CPU\n\t\t{6A5DA7FF-791E-4A43-BF65-B6942917F7D9}.Release|Any CPU.Build.0 = Release|Any CPU\n\tEndGlobalSection\n\tGlobalSection(SolutionProperties) = preSolution\n\t\tHideSolutionNode = FALSE\n\tEndGlobalSection\n\tGlobalSection(ExtensibilityGlobals) = postSolution\n\t\tSolutionGuid = {38B9D19B-5177-445E-8E3F-6C69A809DAB9}\n\t\tEnterpriseLibraryConfigurationToolBinariesPathV6 = packages\\EnterpriseLibrary.Common.6.0.1304.0\\lib\\NET45;packages\\EnterpriseLibrary.Logging.6.0.1304.0\\lib\\NET45;packages\\EnterpriseLibrary.ExceptionHandling.6.0.1304.0\\lib\\NET45\n\tEndGlobalSection\nEndGlobal\n"
  }
]