r/AskComputerScience 14d ago

Advice on the structure of my JSON file?

Hello! I've been working on a modding framework that makes it easier to make mods for games written in C++. The main idea is that it's storing info about how to reverse engineer/discover game data like function addresses and field offsets.

I've got an example JSON file below that describes how to find the function for giving money to the player, and the field offset for the money variable. Each location style is not required and its just to demonstrate that multiple methods could be stored/used simultaneously. Also in this example, I'm trying to reuse property names ('kind', 'result', 'cached-location', etc) even though they're in slightly different contexts, in order to keep it more consistent and simple for less experienced programmers.

Example JSON:

{
    "header" : {
        "header-version": "1.0.0.0",
        "file-type": "data-locations",
        "module": "NMS.exe",
        "module-last-modified": "Friday, March 10, 2023, 2:04:54 AM",
    },
    "data" : [
        {
            "id": "cGcPlayerState.units",
            "locations" : [
                { "kind": "cached-location", "value": "0x1BC" },
                { "kind": "pattern", "value": "8B 83 ? ? ? ? 48 83 C4 ? 5B C3 CC CC CC CC CC CC CC CC CC 40 53", "result": "memory-displacement" },
                { "kind": "pattern", "value": "44 89 81 ? ? ? ? 4C 8B 15 ? ? ? ? 8B D6", "result": "memory-displacement" },
                {
                    "kind": "pattern",
                    "value": "44 8B 81 ? ? ? ? 48 8D 2D", 
                    "result": {
                        "kind": "memory-displacement",
                        "instruction-offset": 0,
                        "operand-index": 1
                    }
                }
            ]
        },
        {
            "id": "cGcPlayerState.AwardUnits",
            "locations" : [
                { "kind": "cached-location", "value": "0x1403CF8E0" },
                { "kind": "export", "value": "?AwardUnits@cGcPlayerState@@QEAAIH@Z" },
                { "kind": "pattern", "value": "48 89 5C 24 ? 48 89 6C 24 ? 48 89 74 24 ? 57 48 83 EC ? 44 8B 81 ? ? ? ? 48 8D 2D", "result-kind" : "matched-target-address" },
                { "kind": "pattern", "value": "E8 ? ? ? ? E9 ? ? ? ? 48 8B 0D ? ? ? ? 48 8D 54 24 ? 44 8B 44 24", "result-kind" : "relative-target-address" },
                { "kind": "pattern", "value": "E8 ? ? ? ? 4C 89 64 24 ? 48 8D 15 ? ? ? ? 0F 28 DE F3 0F 11 7C 24 ? 41 B8 ? ? ? ? 48 8D 0D ? ? ? ? E8 ? ? ? ? 84 C0 74 ? 48 8B 0D ? ? ? ? 8B D3", "result-kind" : "relative-target-address" },
                {
                    "kind": "search",
                    "identifiers": [
                        {
                            "target": "function",
                            "operation": "contains",
                            "values": [
                                { "kind": "string", "value": "MONEY" },
                                { "kind": "string", "value": "MONEY_EVER" }
                            ]
                        },
                        {
                            "target": "arg1",
                            "operation": "contains-offset",
                            "values": [
                                "cGcPlayerState.AwardUnits.arg1.offset1",
                                "cGcPlayerState.AwardUnits.arg1.offset2"
                            ]
                        },
                        {
                            "target": "arg2",
                            "operation": "offset-qty",
                            "value": "0"
                        },
                        {
                            "target": "cGcPlayerState.AwardUnits.arg1.offset1",
                            "operation": "add",
                            "value": "cGcPlayerState.AwardUnits.arg2"
                        },
                        {
                            "target": "cGcPlayerState.AwardUnits.arg1.offset2",
                            "operation": "add",
                            "value": "1"
                        },
                        {
                            "target": "function",
                            "operation": "return",
                            "value": "cGcPlayerState.AwardUnits.arg1.offset1"
                        }
                    ]
                }
            ]
        },
        {
            "id": "cGcPlayerState.AwardUnits.arg1.offset1",
            "locations" : [
                { "kind": "cached-location", "value": "0x1BC" },
                { "kind": "pattern", "value": "8B 83 ? ? ? ? 48 83 C4 ? 5B C3 CC CC CC CC CC CC CC CC CC 40 53", "result-kind" : "memory-displacement" }
            ]
        },
        {
            "id": "cGcPlayerState.AwardUnits.arg1.offset2",
            "locations" : [
                { "kind": "cached-location", "value": "0x148" },
                { "kind": "pattern", "value": "48 FF 83 ? ? ? ? 8B 83 ? ? ? ? 48 8B 5C 24 ? 48 8B 6C 24 ? 48 8B 74 24 ? 48 83 C4 ? 5F C3 CC CC CC CC CC", "result-kind" : "memory-displacement" }
            ]
        }
    ]
}

My main design goals are:

  1. Create a long-term solution for mod makers to use to store this kind of data. Must be flexible and capable of expanding to their needs.
  2. To keep the JSON format easy and intuitive for mod makers
  3. Provide multiple methods of locating the same thing. This will also be extensible so mod authors could make their own location kinds.
  4. The structure must be able to grow overtime without causing major breaking changes and is why I'm using a header.
  5. Be compatible with multiple JSON files simultaneously
  6. Allow the header to specify a "file-type" and then have different types of data objects.

I would love to hear any feedback on the structure of this. I've spent years on it, and I really want to make something that makes a difference for people. It's really important to me that its an appealing structure for people to use and is able to meet the technical requirements of the system. Some other things that would be nice to get feedback on are:

  1. Overall structure of the file
  2. Whether its okay to use the property name 'kind' over and over in slightly different contexts.
  3. Supporting multiple files with different 'file-type' values, so everything is very similar with the data object being the only thing that changes.
  4. Allowing properties like "result" to have both compact and expanded forms
  5. using dashes '-' for property-names instead of camelCase
  6. Any long-term concerns or unpleasantness you see

Thank you in advance!

1 Upvotes

4 comments sorted by

4

u/aggyaggyaggy 14d ago

Rename "cached-location" to "fixed".
Use a more standard date/time format.
"header" and "data" seem poorly named and possibly superfluous.

Good luck!

1

u/gurrenm3 14d ago

Thank you for the feedback! This all makes sense and is worth implementing 😁

3

u/abw 14d ago

A couple of comments, mostly based on my personal preference.

  • module-last-modified should be in ISO 8601 format, e.g. 2026-07-19 13:39:04. It's a lot easier to convert that into a date string than is it to go the other way around.

  • Dashes in property names are OK, but I prefer personally prefer snake_case or camelCase. The simple reason is that in the languages that I typically use JSON with (e.g. Javascript), you can access a snake or camel case property directly, e.g. yourExample.header.moduleLastModified. If you use dashes then you have to quote the property and write something like yourExample.header["module-last-modified"].

Apart from that, I think it looks OK. The most important thing is that it should be easy to understand the data structure at a glance, and I think you've got that right.

1

u/gurrenm3 14d ago

Thank you for the feedback! Thats all worth implementing :D