UnofficialCrusaderPatch / UnofficialCrusaderPatch2

Unofficial balancing patch installer for Stronghold Crusader 1
MIT License
430 stars 59 forks source link

Is maps bigger than 400x400 still can not be created? #781

Open ExtremeDotneting opened 3 years ago

ExtremeDotneting commented 3 years ago

Hi. I've tried to edit .map files and change size, tried to edit process memory (via Cheat Engine). I was able to create a map, but it has a lot of bugs (mirroring, black places) and it crashes when trying to save.

So, is it possible to create maps bigger than 400x400 and how can i make it&

LeSpec commented 3 years ago

No, unfortunately. The game logic is based on 400 sized maps. All the arrays and operations on it work with a fixed size of 80400 tiles.

J-T-de commented 3 years ago

For cross reference: The known parts of the map file format can be found here (some data is outdated, best ask on the sourcehold discord if you need anything specific).

Yeah, it really sits on the impossible side of the spectrum on what is feasable. Everything is hardcoded, as LeSpec wrote, like 80400 tiles, 400 mapsize, 399 is map_size-1, 200 is mapsize/2, ...) and more. I am really curious what you've edited so that the game did not get an immediate stroke, but only died when saving. I would even guess it cannot even render the "larger map" at all...

LordHansCapon commented 3 years ago

On startup the game allocates an array with the max size of the map. This can be intercepted, but won't change much as everything is hardcoded, from AI to pathfinding. And it does not help that probably the rendering (especially the minimap) probably has some pre-calculated magic numbers iirc (not 100% sure). So even if we could override everything that says "400" or "399" (because they use that sometimes instead), then we would still crash the game with existing maps for example. As J-T-de said, it iss too close to the impossible sadly. Maybe one day. (let's hope they will share the source of the game)

ExtremeDotneting commented 3 years ago

I am really curious what you've edited so that the game did not get an immediate stroke, but only died when saving.

I created map 400x400 and replaced 6x000a9920 value 90 02 (400 in hex, in BigEndian) to 20 03 (800 in hex). After this i loaded map in game, even can open it. Minimap work fine, but when i scroll to the edges or trying to save game - it crashes. Another intresting fact - it seams some parts of the map has same reference, because when i draw on one part of map - this changes shown on another parts too. s1 s2

ExtremeDotneting commented 3 years ago

You can see my files and try to open map, maybe it can help you, guys.

Maps.zip

ExtremeDotneting commented 3 years ago

Another trick i've tried is to rewrite array of created file in game with Cheat Engine and Lua script below, save it and load normally. It worked for me to define original map (400x400), but chashed with 800x800 map.

require("util")

-- This is the base address that is used for any map data accesses
local MapDataBaseAddressName = '"Stronghold Crusader.exe"+1693208'
-- The following two offsets are probably used when iterating over the positions that belong to a building
local MapDataSecondOffset    = 0x554990
local MapDataFirstOffset     = 0x55498C
local MapDataAreaOffset      = 0x554984

-- entry = 196 * [BUILDING SIZE] + [TILE_ID]
local TileFirstCoordinateOffsetArray = MakeStridedArray('"Stronghold Crusader.exe"+AD31E0', 24, 4, "First Coordinate Tile Offsets")
local TileFirstCoordinateOffsetArray = MakeStridedArray('"Stronghold Crusader.exe"+AD31E4', 24, 4, "Second Coordinate Tile Offsets")

local FirstCoordinateOffsetArray = MakeStridedArray('"Stronghold Crusader.exe"+1F37300', 12, 4, "First Coordinate Offsets")

-- Maps
BuildingIndexMap1 = MakeStridedArray(MapDataBaseAddressName .. '+2029B0', 2, 2)
BuildingIndexMap2 = MakeStridedArray(MapDataBaseAddressName .. '+1895BB8', 2, 2)            -- alternative: "Stronghold Crusader.exe"+1895BB8
FlagsMap         = MakeStridedArray(MapDataBaseAddressName .. '+165160', 4, 4)          -- alt:  -- 1024 is set when placing a building? // 10000000
HeightMap1        = MakeStridedArray(MapDataBaseAddressName .. '+29FA30', 2, 2)
HeightMap2        = MakeStridedArray(MapDataBaseAddressName .. '+1932C38', 1, 1)            -- alt: "Stronghold Crusader.exe"+1932C38
UnknownMap      = MakeStridedArray(MapDataBaseAddressName .. '+1C7B80', 1, 1)           -- Set to 2 when building is placed
BuildingTypeMap  = MakeStridedArray(MapDataBaseAddressName .. '+229DD0', 1, 1)

GlobalBuildingLookupOffset = 0x002029B0
GlobalBuildingLookupStride = 2

function Coordinate2BuildingOffset(first, second)
    local base_address = getAddress(MapDataBaseAddressName)
    local result_address = '"Stronghold Crusader.exe"+1F37300'
    local result = FirstCoordinateOffsetArray:read(second)
    return result + first
end

function read_map_raw(source_array)
    local data = {}
    for i=0,159 do
        data[i] = {}
                print(i)
        for j=0, 159 do
            data[i][j] = source_array:read(160*i+j)
                        --print(i, " ", j)
        end
    end
    return data
end

function write_map_raw(source_array, item)
    for i=0,799 do
                print(i)
        for j=0, 799 do
            source_array:write(800*i+j, item)
                        --print(i, " ", j)
        end
    end
end

function read_map_coordinates(source_array)
    local data = {}
    for i=14, 386 do
        data[i-13] = {}
        for j=0, 399 do
            local value = source_array:read(Coordinate2BuildingOffset(j, i))
            if value ~= 0 then 
                print(i, " ", j)
            end
            data[i-13][j+1] = value
        end
    end
    return data
end

function DEBUG_save_map_csv(target_file, data, sep)
    if sep == nil then 
        sep = ";"
    end
    local file = io.open(target_file, "w")
    for i, row in ipairs(data) do
        for j, v in ipairs(row) do
                         if v == nil then
                            v="nil"
                         end
            file:write(v)
                        file:write(sep)
        end
        file:write("\n")
    end
    file:close()
end

function DEBUG_maps()
         write_map_raw(FlagsMap,0)
         write_map_raw(UnknownMap,0)
         write_map_raw(BuildingIndexMap1,0)
         --write_map_raw(BuildingIndexMap2)
         write_map_raw(HeightMap1,32768)
         --write_map_raw(HeightMap2)
         write_map_raw(BuildingTypeMap, 0)

    --DEBUG_save_map_csv("BuildingIndexMap1.csv", read_map_raw(BuildingIndexMap1))
    --DEBUG_save_map_csv("BuildingIndexMap2.csv", read_map_raw(BuildingIndexMap2))
        --DEBUG_save_map_csv("FlagsMap.csv", read_map_raw(FlagsMap))
        --DEBUG_save_map_csv("HeightMap1.csv", read_map_raw(HeightMap1))
        --DEBUG_save_map_csv("HeightMap2.csv", read_map_raw(HeightMap2))
        --DEBUG_save_map_csv("UnknownMap.csv", read_map_raw(UnknownMap))
    --DEBUG_save_map_csv("BuildingTypeMap.csv", read_map_raw(BuildingTypeMap))

end

DEBUG_maps()
ExtremeDotneting commented 3 years ago

Original script i found here https://github.com/J-T-de/CrusaderData

p.s. Just now noticed that this is your code

J-T-de commented 3 years ago

Credits for this part of the repo goes to ngc92, I actually didn't even review this part :) (always happy if people find this repo and find it useful)

If you check out the sourcehold-maps wiki, there are some more map sections listed, maybe this brings you closer to what you want to achieve. You may also pass by the UCP discord or drop me a line in discord, we already collected a lot of knowledge, and we can obviously help you to some extend.

ExtremeDotneting commented 3 years ago

Research

Tried to edit numeric constants in assembly yesterday (woth OllyDbg). Just replace all calls with one cosntant to calls with another.

What was replaced:

399 to 799
398 to 798
80400 to 321600 (4x) - possible tiles count
160000 (400*400) to 640000 (4x) - possible array length

For various replacement combinations i build new .exe and tested it. Maximum i get is this: s1 s2

Exe on screenshot wasn`t crashed immediately or when scroll or saving, but still not useful.

After changing all listed constants app crushes reading 0x00000000 (don't know why).

Сonclusion

  1. It seams in stronghold logic for defferent size map splitted buy if, i mean that hardcoded, map size used like enums too.

  2. Some changes work, but some cause exception. Don't know how can i debug stonghold, all debuggers i tried (VS debuger, OllyDbg, Cheat Engine) stuck when Stronghold start to render (i think it's because it run in fullscreen mode, maybe). Did you know how can i normally debug it?

J-T-de commented 3 years ago

After thinking about it a bit more, and digging through your code, I understood what you've done and why things don't work as you expect. Long story short: You simply overwrite memory regions. Basically, most map sections lie behind each other in memory, and overwriting it borks the memory. Also, wild guess, 6x000a9920 (0x000a9920?) is probably the changed displayed number, but I cannot check currently.

J-T-de commented 3 years ago

160000 (400*400) to 640000 (4x) - possible array length is definitely wrong, a map has 80400 tiles (as written above)

After changing all listed constants app crushes reading 0x00000000 (don't know why).

the error must be way before that, there is going to be some invalid pointer somewhere...

just changing the magic numbers won't do it unfortunately, you also need to adapt memory, and a lot of logic...

ExtremeDotneting commented 3 years ago

just changing the magic numbers won't do it unfortunately

Even if i edit every static 400 value (0190 in hex) in assembler? My idea was to edit all array definitions too (when you pass array length), but maybe in assembly arrays works not like i think. I'm not strong at с/с++ and assembler, usually i'am working with c#.

ExtremeDotneting commented 3 years ago

Seams it is imposible, sadly. Hope one day you will release Sourcehold with all functional. Please, use json for config variables, don't be like Firefly Studio :)

J-T-de commented 3 years ago

Even if i edit every static 400 value (0190 in hex) in assembler? My idea was to edit all array definitions too (when you pass array length), but maybe in assembly arrays works not like i think. I'm not strong at с/с++ and assembler, usually i'am working with c#.

Jep, it isn't enough. You need to also attack memory management. Imagine having a shelf with three drawers (the classical analogy for arrays). Just saying, you have 4 drawers won't do it, you actually need more drawers. Also, you need to change the logic to access the drawers (if you continue to only use 3 drawers, you didn't won anything). And if you build a new draw, you also have to tell everyone where the new drawer is...

Seams it is imposible, sadly. Hope one day you will release Sourcehold with all functional. Please, use json for config variables, don't be like Firefly Studio :)

If it would be easy, someone would already have done it :P Soucehold is a different project, unrelated to the UCP (but some people are involved in both projects), but as an open source port, you will be able to do anything you like with it :)

GRhin commented 3 years ago

Seams it is imposible, sadly. Hope one day you will release Sourcehold with all functional. Please, use json for config variables, don't be like Firefly Studio :)

To be fair, json probably wasnt around when firefly was making this, neither was most modern programming standards for that matter. And thats before taking into account the efficiency changes of the compiler that pretty much means that even if it was setup with the times equivalent of json, it may not end up in the exe as anything remotely similar.

BGiantMountain commented 2 years ago

I asked this question in 2006, it was said that it is not possible because the producer did not intend to give the source codes yet. 20 years have passed and I think we can solve this issue by making a collective request with the producer somehow. :/

J-T-de commented 2 years ago

People asked firefly about releasing the source code, but it was a clear no.

AtlasLeng commented 2 years ago

Hi all, I love this game very much, spent countless hours building castles and landscape and of course making maps and right away the map max 400x 400 is just wayyyyyy too small , what the people in firefly is thinking????? I really support you guys keep on pushing or modding the game to be able to have mega maps that have more room for both the human player and AI to breath , I have constantly wondering this, waiting people to do it, even until now 2022 April 19th just done clearing all the mission in Extreme , if only it has bigger maps more multiple players and AI

AtlasLeng commented 2 years ago

Even better yet, we can choose the colours and style of our castle walls from dark stone , grey, brown, redish brown, white stones to sandy stones, the combinations is endless meanwhile able to select to build different types of building styles from middle east arab to europe styles , imagine it and add in few more units and siege engines