stadium_import.lua
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
-- Driver: import a Stadium ROM through the OPTIONS row, with the modal file -- dialog stubbed out. -- -- Everything except the dialog itself is the real path: the row is built the -- way the OPTIONS menu builds it, its step() is pressed, StadiumRomPick reads -- the file with io.open (love.filesystem cannot see an absolute path), -- StadiumInstall builds from those bytes, and the loading screen goes up over -- whatever asked. A modal dialog cannot be driven, so choose() is replaced -- with the answer a player would have given it. return function(game) local U = dofile("tests/drivers/util.lua") local DIR = os.getenv("SHOT_DIR") or ".scratchpad" local lib = game.mods.exports.DRAMATIC_SHAPE.lib local Pick = lib.require("StadiumRomPick") local Install = lib.require("StadiumInstall") local Pack = lib.require("StadiumPack") local Battles = lib.require("OverworldBattle") U.teleport(game, "ROUTE_1", 5, 8, "down") U.wait(30) -- FIRST let the automatic first-run build finish. A ROM is sitting in -- baseroms/ in this checkout, so StadiumScreen.maybePush has already pushed -- a loading screen and started one -- and import() refuses to start a -- second while that runs, so every step below would silently no-op against -- it. maybePush asks ONCE per boot, so once this is drained it stays -- drained and the purge below cannot be undone behind our backs. local drain = 0 while (Install.pending() or Install.status.state == "building") and drain < 5400 do U.wait(10); drain = drain + 10 end while game.stack:top() and game.stack:top() ~= game.overworld do U.wait(10) end U.log(("drained the automatic first-run build in %d frames"):format(drain)) -- back to a machine that has never built them, whatever earlier runs left for _, name in ipairs(love.filesystem.getDirectoryItems(Pack.CACHE_DIR)) do love.filesystem.remove(Pack.CACHE_DIR .. "/" .. name) end Install.forget() Pack.forget() local function settle(what) local waited = 0 while Install.status.state == "building" and waited < 5400 do U.wait(10) waited = waited + 10 end U.log(("%s: %d frames (%.1fs) -- state=%s error=%s") :format(what, waited, waited / 60, tostring(Install.status.state), tostring(Install.status.error))) return waited end U.log(("picker available on this platform: %s"):format( tostring(Pick.available()))) U.log(("BEFORE: models available = %s, 3D-BTL offers %d rungs") :format(tostring(Install.available()), Battles.setting:rungs())) local row = Pick.row() if not row then U.log("no import row on this platform") return end U.log(("row reads: %s = %s"):format(row.label, row.value())) -- ------- the wrong file first, because a refusal must not leave a mess U.log("-- picking a file that is not a Stadium ROM --") Pick.choose = function() return "/not/a/real/path.z64" end local realRead = Pick.read Pick.read = function() return ("\x80\x37\x12\x40"):rep(4096) end row.step(game) settle("wrong file") U.shot(game, ("%s/import_1_refused.png"):format(DIR)) U.log((" still not installed: %s, row still reads %s") :format(tostring(not Install.available()), row.value())) U.wait(300) -- let the failure notice time out and pop itself -- ------- and now the real one U.log("-- picking the real ROM --") Pick.read = realRead local abs = love.filesystem.getWorkingDirectory() .. "/baseroms/baserom.z64" local picked = false Pick.choose = function() picked = true return abs end Install.status.state = "idle" row.step(game) U.log((" choose() called: %s, state = %s, loading screen on top: %s") :format(tostring(picked), tostring(Install.status.state), tostring(game.stack:top() ~= game.overworld))) U.wait(30) U.shot(game, ("%s/import_2_building.png"):format(DIR)) settle("real ROM") U.wait(120) U.log(("AFTER: models available = %s, 3D-BTL offers %d rungs, row reads %s") :format(tostring(Install.available()), Battles.setting:rungs(), row.value())) Battles.setting:setValue("stadium", game) U.log((" and 3D-BTL can now be set to STADIUM A: %s") :format(tostring(Battles.setting:get() == "stadium"))) -- and the ROM was NOT copied anywhere: the only baserom physfs can see is -- the one in the game folder this checkout already had, never a copy in -- the save directory (which is where a mod's writes land) local where = love.filesystem.getRealDirectory("baseroms/baserom.z64") U.log((" the only ROM on the read path is the game folder's own: %s (%s)") :format(tostring(where == love.filesystem.getWorkingDirectory()), tostring(where))) U.log((" the save directory has no baseroms folder: %s") :format(tostring(love.filesystem.getInfo( love.filesystem.getSaveDirectory() .. "/baseroms") == nil))) -- ------- and the wrong file, now that a GOOD set is installed -- -- The serious case. The marker is the only thing that makes 151 files on -- disk count as installed, so a refusal that writes one anyway would -- UNINSTALL a working set -- and the player would have pressed a row called -- STADIUM ROM, picked the wrong file, and lost the models they already had. U.log("-- picking the wrong file again, with models already installed --") Pick.read = function() return ("7@"):rep(4096) end Install.status.state = "idle" row.step(game) settle("wrong file over a good install") U.wait(300) Install.forget() Pack.forget() U.log((" the good install SURVIVED: %s, row reads %s, 3D-BTL offers %d rungs") :format(tostring(Install.available()), row.value(), Battles.setting:rungs())) U.log((" and a real model still loads: %s") :format(tostring(Pack.load(25) ~= nil))) U.log("done -- " .. DIR) end