CF (CurrentFiles) – Methods

Here are psuedocode write-ups of the methods of CF. They should all be complete decompilations except for in the last “incomplete” section. Index of CF Methods (csv format).

All “get” and “set” methods are omitted; some are included in their context on the commits page. The ones named “DataCur” get/set file FC; “DataA” and “DataB” get/set FA/FB rsp.

chk <data> is a macro meaning sign the object with its checksum, i.e.:
<data>[chkIndex] = calcChecksum(<data>, chkIndex) (as appropriate)
where chkIndex is 0x53bc for Files and 0x20 for Tails.

File Methods

8000abc0 | fileInit(k)
k = 0,1; <callers: 801b9790 (k=1) on reset; 801bbdf0 (k=0) on starting new file >

    set _u0[4] = true
    set parameters on file = FA (k = 0), FB (k = 1):
        5302 [2]:   24      // total hearts
        5304 [2]:   24      // ??? hearts
        5306 [2]:   24      // current hearts
        5308 [2]:   0
        531c [20]:  "F405"  // full [20] buffer copied from `80574aa8` (= r13 - 0x7bf8)
        53a5 [1]:   0
        53a9 [1]:   1
        53ad [1]:   0
        53ae [1]:   11
        53b0 [1]:   8
        53b1 [1]:   8
        53b2 [1]:   8

8000d390 | fileLoadSelected()
FS → FA

    cf.fileLoad(cf.selectedFileNo)

8000d3a0 | fileLoad(n)
Fn → FA

    if n != 0,1,2: return
    if cf.getFooter-chkB(n) == 0:   // if checksum not stale
        copy Fn → FA
        copy st.tailsExt[n] → fileTail
    else:
        return cf.fileClearA()

8000dae0 | fileCopyFBToFCur()
FB → FC

    copy FB → FC

8000e180 | fileSaveSelected()
FA → FS; <caller: 80014770 on save>

    cf.fileSave(cf.selectedFileNo)

8000e190 | fileSave(n)
FA → Fn

    if n != 0,1,2: return
    chk FA
    copy FA → Fn
    chk fileTail
    copy fileTail → sf.tailsInt[n]  

8000e8e0 | fileCopy(m, n)
Fm → Fn; <caller: 80014610 on copy>

    if m != 0,1,2 or n != 0,1,2: return
    chk Fm
    copy Fm → Fn
    chk sf.tailsInt[m]
    copy sf.tailsInt[m] → sf.tailsInt[n]
    chk st.tailsExt[m]
    copy st.tailsExt[m] → st.tailsExt[n]

8000f0a0 | fileSaveSelected-2()
FA → FS; <caller: 800146d0 on delete>

    if s:=cf.selectedFileNo != 0,1,2: return
    chk FA
    copy FA → FS
    chk fileTail
    copy fileTail → sf.tailsInt[s]
    copy fileTail → st.tailsExt[s]

8000f850 | fileCopyFCurToFB()
FC → FB

    copy FC → FB

8000ff10 | tailSaveSelected()

    if s:=cf.selectedFileNo != 0,1,2: return
    chk fileTail
    copy fileTail → st.tailsExt[s]

80010080 | fileClearA()
0 → FA; <caller: 800146d0 on delete>

    set FA to 0
    set FA[53ad] = 1
    chk FA
    set fileTail to 0
    chk fileTail

Core Methods

80010560 | reset()
<caller: 801b9790 on reset> {put a memory breakpoint on FA}
More on loadToCurrent() on the commits page.

    set FA to 0         // size [53c0]
    set FB to 0         // size [53c0]
    set fileTail to 0   // size [24]
    loadToCurrent()

800115e0 | isFileInactive()
Determines FC, the current file. Return of false means FA; true means FB. In practice, FC = FA during gameplay or while a file is being anti-committed, and FB otherwise.

    ptr = FUN_802e2d80(1,0)     // seems to return *805789f4 – namely the respawner object
    return ptr != 0 && (*(short*)(ptr + 8) == 0)    // this data is 0 iff title-screen
        && cf.anticommitFlag == false               // set during file selects/starts

800113d0 | calcChecksum(data, size)

    return checksum(data, size)     // the fn at 803b0ae0

Spawn Data Commits

80010340 | commitType1DataToFA(x)
Called during a save; commits Link’s co-ords and location.

    FA := cf.getFileA()                     // fn 800113b0, returns 80955464
    linkPtr := getLinkPtr()                 // fn 8005c6f0, returns 80578a18
    spawnSlave  := *805b6ae0                // see re/spawn
    reloadLayer := *805789fc                // adjacent to data used by reloader
    reloaderPtr := *805789f4                // see re/spawn

    if linkPtr != nullptr:
        FA[0010] = linkPtr[0c0]             // x pos (float)          → FA t1 x-pos
        FA[0014] = linkPtr[0c4]             // y pos (float)          → FA t1 y-pos
        FA[0018] = linkPtr[0c8]             // z pos (float)          → FA t1 z-pos
        FA[530e] = linkPtr[13e]             // velocity angle (short) → FA t1 angle
    if *805789c0 != nullptr:
        FA[5308] = *805789c0[2398]          // still dunno what this is (short)
    if *80578958 != nullptr:
        FA[08d0] = FUN_8019a050(*80578958)  // gets area index (from *80578958[7964][12])
    if reloaderPtr != nullptr:
        FA[531c] = spawnSlave[0]            // slave spawn area       → FA t1 area
        FA[53b3] = spawnSlave[25]           // slave spawn night      → FA t1 night
        FA[53a5] = reloadLayer              // reloader layer         → FA t1 forced layer
        FA[53a8] = spawnSlave[24]           // slave spawn entrance   → FA t1 entrance
    FA[53a9] = x                            // unknown
    *8052e8d8 = FA[539c] (copy 9B)          // expanded fn 80195720: loadSkyKeepPuzzle()

80011650 | commitType2DataToFA()

    FA := cf.getFileA()                     // fn 800113b0, returns 80955464
    linkPtr := getLinkPtr()                 // fn 8005c6f0, returns 80578a18

    if linkPtr != nullptr:
        FA[001c] = linkPtr[0c0]             // x pos (float)          → FA t2 x-pos
        FA[0020] = linkPtr[0c4]             // y pos (float)          → FA t2 y-pos
        FA[0024] = linkPtr[0c8]             // z pos (float)          → FA t2 z-pos
        FA[5310] = linkPtr[13e]             // velocity angle (short) → FA t2 angle
    if *805789c0 != nullptr:
        FA[530a] = *805789c0[2398]          // still dunno what this is (short)
    if reloaderPtr != nullptr:
        FA[533c] = spawnSlave[0]            // slave spawn area       → FA t2 area
        FA[53a6] = reloadLayer              // reloader layer         → FA t2 forced layer
        FA[53aa] = spawnSlave[24]           // slave spawn entrance   → FA t2 entrance

80011730 | commitType3DataToFC(pos, ang)
Called during a load; commits Link’s co-ords and location, then does something mysterious. Note that unlike the other 2 commits, this one targets FileC (so will commit to FileB, i.e. BiT file, when doing a load during BiT).

    // expanded fn 8000b000: cf.setDataCur-0028(linkPos)
        FC[0028] = pos[0]                   // x pos (float)          → FC t3 x-pos
        FC[002c] = pos[1]                   // y pos (float)          → FC t3 y-pos
        FC[0030] = pos[2]                   // z pos (float)          → FC t3 z-pos
    // the following 8 FC sets are all expanded function calls
        FC[5312] = *(ang + 2)               // velocity angle (short) → FC t3 angle
    if *805789c0 != nullptr:
        FC[530c] = *805789c0[2398]          // still dunno what this is (short)
    if reloaderPtr != nullptr:
        FC[535c] = spawnSlave[0]            // slave spawn area       → FC t3 area
        FC[53b4] = spawnSlave[25]           // slave spawn night      → FC t3 night
        FC[53a7] = reloadLayer              // reloader layer         → FC t3 forced layer
        FC[53ab] = spawnSlave[24]           // slave spawn entrance   → FC t3 entrance
    FC[53a9] = 0
    FC[53b5] = 2
    **(*8057865c+24)(*8057865c, 0x77)       // unknown dynamic function call

800113e0 | loadFooter()

    cf.determineFooter-chkA()
    cf.loadFSDataToFooter()

8000a120 | loadFSDataToFooter()

    cf._u0[3] = false
    for n in [0,3):
        if (Fn[0x53ad] != 1 && cf.getFooter-chkB(n) != 1):
            cf._u0[3] = true;
            copy Fn.getOffset-heroname() → cf.heronames[n] // upto min(len,8) utf16, then appends \x0
            cf.savetimes[n] = Fn[0x8]
            cf.curhearts[n] = Fn[0x5306]
            w = count for x in (0x7c0 + 0x4*i, i in [0,8)) if (*(uint*)Fn[x] & 0xffff) == 0x72
            cf.widths[n] = max(w*4 + Fn[0x5302], 0x50)

SF/ST methods

8000a400 | initSFandFSData()

    cf._u0[0] = true
    cf.resetSFandFSData()
    cf.opFlag = false
    cf.selectedFileNo = 1

80010120 | resetSFandFSData()

    set SF to 0 (size [fbe0])
    cf.selectedFileNo = 0;
    for n in [0,3): cf.chkA[n] = 0
    for n in [0,3):
        cf.heronames[n] = \x0
        cf.savetimes[n] = 0 (size [8] each)
        cf.curhearts[n] = 0
        cf.widths[n] = 0
    sf.initSOUJ() // writes "SOUJ" to first 4B of sf
    sf->header + 0x1c = 0x1d (size [4])
    for n in [0,3):
        Fn[0x53ad] = 1
        chk Fn
    for n in [0,3):
        chk sf.tailsInt[n]
    cf.fileClearA()
    cf._u0[1] = false
    cf._u0[2] = false
    cf._u0[3] = false
    cf._u0[4] = false
    cf._u0[5] = false
    cf.errorFlag = false
    for n in [0,3): cf.chkB[n] = 0
    cf.resetST()

80010280 | resetST()

    set ST to 0                     // size [80]
    for n in [0,3): chk st.tailsExt[n]
    for n in [0,3): cf.chkC[n] = 0

80010470 | tailsCopySFtoST(n)

    if cf.getFooter-chkB(n) == false:
        copy sf.tailsInt[n] → st.tailsExt[n]
    else:
        set sf.tailsInt[n] to 0     // size [20]
        chk sf.tailsInt[n]
    cf.chkC[n] = false

80011510 | SFMatchesGameID
Checks if SF has the correct Game ID (including region)

    return sf.gameID == "SOUJ"      // compare 4 chars

Checksum Flag Tests

80011420 | determineFooter-chkA()

    for n in [0,3): cf.chkA[n] = (Fn[0x53ad] == 1)

8000d2f0 | determineFooter-chkC()

    for n in [0,3): cf.chkC[n] = (cf.saveTails[n][0x20] != calcChecksum(cf.saveTails[n], 0x20))

Note: chkB is determined during CF::verifySF()

80011590 | isSFFileChecksumCorrect(n)

    return Fn[0x53bc] == calcChecksum(Fn, 0x53bc)

Incomplete

These decompilations are incomplete.

80009ed0 | CF (the constructor)

    constructs something at offset 0x34 of FA and FB
    for i in [0,3]: cf.heronames[i] = '\x0'
    cf.area = '\0'
    *(r13-0x4444) = this    // assigns the static pointer for CF
    initialises SF and stores ptr in cf.saveFiles
    initialises ST and stores ptr in cf.saveTails
    initSFandFSData(this)
    return this
8000a050 _f0 assigns SF and ST?
8000a090 _f1 backs up SF and ST?
800105c0 _f2 generates a post-ending file (hero mode or pre-Demise)

8000d1d0 | verifySF()

    cf._u0[5] = _f3(???) == 0 || *(int *)(sf.header[0x1c]) != 0x1d
    for n in [0,3):
        cf.chkB[n] = (cf.isSFFileChecksumCorrect(n) == 0)
    for n in [0,3):
        if (*(uint *)(sf.tailsInt[n][0x20] != cf.calcChecksum(sf.tailsInt[n], 0x20)):
            set sf.tailsInt[n] to 0 (size [20])
            chk sf.tailsInt[n]