#ifndef COMPILE_H
#include "COMPILE.H"
#endif
#ifdef MEDIA648_A31E_A35E_X31J
extern char G0716_ac_ErrorMessage[100];

#ifdef MEDIA645_A31E_A35E
struct IntuitionBase* IntuitionBase = NULL;
#endif
static struct {
        void* Functions[29];
        FTL_LIBRARY FTLLibrary;
} G9011_s_PRIMLibrary = {
        { (void*)F6008_PRIM_29_Empty,                           /* 29 -$74 Placeholder function (does nothing) */
          (void*)F6038_PRIM_28_Library_UnhookFunction,          /* 28 -$70 */
          (void*)F6039_PRIM_27_File_IsOpenable,                 /* 27 -$6C */
          (void*)F6043_PRIM_26_FTL_LoadAndExecute,              /* 26 -$68 */
          (void*)F6049_PRIM_25_FTL_DecompressCODESegment,       /* 25 -$64 Placeholder function (calls F6005_PRIM_21_FTL_FatalLoaderError). Actual code is loaded by APPA.FTL */
          (void*)F6004_PRIM_24_Input_Read,                      /* 24 -$60 */
          (void*)F6003_PRIM_23_Input_WaitForChar,               /* 23 -$5C */
          (void*)F6002_PRIM_22_Console_PrintMessage,            /* 22 -$58 */
          (void*)F6005_PRIM_21_FTL_FatalLoaderError,            /* 21 -$54 */
          (void*)F0920_PRIM_20_File_Read,                       /* 20 -$50 */
          (void*)F0919_PRIM_19_File_Seek,                       /* 19 -$4C */
          (void*)F6001_PRIM_18_File_Close,                      /* 18 -$48 */
          (void*)F6000_PRIM_17_File_Open,                       /* 17 -$44 */
          (void*)F0918_PRIM_16_Memory_Free,                     /* 16 -$40 */
          (void*)F0917_PRIM_15_Memory_Allocate,                 /* 15 -$3C */
          (void*)F6046_PRIM_14_Library_FindInList,              /* 14 -$38 */
          (void*)F6045_PRIM_13_Library_InitializeList,          /* 13 -$34 */
          (void*)F6048_PRIM_12_Library_RemoveFromList,          /* 12 -$30 */
          (void*)F6047_PRIM_11_Library_AddToList,               /* 11 -$2C */
          (void*)F6042_PRIM_10_Library_FlagAsCanBeExpunged,     /* 10 -$28 */
          (void*)F6041_PRIM_09_Library_Close,                   /* 09 -$24 */
          (void*)F6040_PRIM_08_Library_Open,                    /* 08 -$20 */
          (void*)F6006_PRIM_07_FTL_Execute,                     /* 07 -$1C */
          (void*)F6044_PRIM_06_FTL_FreeMemory,                  /* 06 -$18 */
          (void*)F0929_PRIM_05_FTL_Load,                        /* 05 -$14 */
          (void*)F6037_PRIM_04_Library_HookFunction,            /* 04 -$10 */
          NULL,                                                 /* 03      Expunge not implemented for PRIM */
          NULL,                                                 /* 02 */
          NULL                                                  /* 01 */
} };
static FTL_NODE FTLLibraryList = { NULL, NULL, NULL };
FTL_LIBRARY* PrimBase = &G9011_s_PRIMLibrary.FTLLibrary;


FUNCTION_HOOK* F6037_PRIM_04_Library_HookFunction(P3434_ps_FunctionHook, P3435_ppv_Library, P3436_ul_FunctionNumber, P3437_ppv_A1, P3438_pv_ReplacementFunction)
register FUNCTION_HOOK* P3434_ps_FunctionHook;
void** P3435_ppv_Library;
unsigned long P3436_ul_FunctionNumber;
void** P3437_ppv_A1;
void* P3438_pv_ReplacementFunction;
{
        register FUNCTION_HOOK* L3515_ps_CurrentFunction;
        register void** L3516_ppv_LibraryFunctionPointer;


#ifdef MEDIA645_A31E_A35E
        F6011_BeginLibraryFunction();
#endif
        L3516_ppv_LibraryFunctionPointer = P3435_ppv_Library - P3436_ul_FunctionNumber;
        L3515_ps_CurrentFunction = (FUNCTION_HOOK*)*L3516_ppv_LibraryFunctionPointer;
        P3434_ps_FunctionHook->lea = 0x43F9;
        P3434_ps_FunctionHook->A1 = *P3437_ppv_A1;
        P3434_ps_FunctionHook->jmp = 0x4EF9;
        P3434_ps_FunctionHook->ReplacementFunction = P3438_pv_ReplacementFunction;
        P3434_ps_FunctionHook->illegal_rte = 0x4AFC4E73;
        P3434_ps_FunctionHook->LibraryFunctionPointer = L3516_ppv_LibraryFunctionPointer;
        if (F6053_IsFunctionHooked(L3515_ps_CurrentFunction)) {
                L3515_ps_CurrentFunction->NextFunction = P3434_ps_FunctionHook;
        }
        P3434_ps_FunctionHook->PreviousFunction = L3515_ps_CurrentFunction;
        P3434_ps_FunctionHook->NextFunction = NULL;
        *L3516_ppv_LibraryFunctionPointer = (void*)P3434_ps_FunctionHook;
#ifdef MEDIA645_A31E_A35E
        F6012_EndLibraryFunction();
#endif
        return L3515_ps_CurrentFunction;
}

void* F6038_PRIM_28_Library_UnhookFunction(P3439_ps_)
register FUNCTION_HOOK* P3439_ps_;
{
#ifdef MEDIA645_A31E_A35E
        F6011_BeginLibraryFunction();
#endif
        if (P3439_ps_->NextFunction == NULL) {
                *(P3439_ps_->LibraryFunctionPointer) = (void*)P3439_ps_->PreviousFunction;
        } else {
                P3439_ps_->NextFunction->PreviousFunction = P3439_ps_->PreviousFunction;
        }
        if (F6053_IsFunctionHooked(P3439_ps_->PreviousFunction)) {
                P3439_ps_->PreviousFunction->NextFunction = P3439_ps_->NextFunction;
        }
#ifdef MEDIA645_A31E_A35E
        F6012_EndLibraryFunction();
#endif
        return *(P3439_ps_->LibraryFunctionPointer);
}

char F6039_PRIM_27_File_IsOpenable(P3440_l_Name, P3441_l_)
long P3440_l_Name;
long P3441_l_;
{
        long L3517_l_[3];


#ifdef MEDIA645_A31E_A35E
        F6011_BeginLibraryFunction();
#endif
        L3517_l_[0] = P3440_l_Name;
        L3517_l_[1] = 0x2E46544C; /* '.FTL' */
        L3517_l_[2] = 0;
        if (F6015_F6000_PRIM_17_File_Open(&L3517_l_, P3441_l_)) {
#ifdef MEDIA645_A31E_A35E
                F6012_EndLibraryFunction();
#endif
                return 1;
        }
#ifdef MEDIA645_A31E_A35E
        F6012_EndLibraryFunction();
#endif
        return 0;
}

FTL_LIBRARY* F6040_PRIM_08_Library_Open(P3442_l_Name, P3443_l_MinimumVersion)
long P3442_l_Name;
long P3443_l_MinimumVersion;
{
        register FTL_LIBRARY* L3518_ps_;
        register FTL_EXECUTABLE* L3519_ps_;
        register FTL_EXECUTABLE* L3520_ps_;
        register unsigned int16_t L3521_i_;
        register BOOLEAN L3522_B_;
        FILE_HANDLE L3523_;
        FTL_EXECUTABLE L3524_s_;


#ifdef MEDIA645_A31E_A35E
        F6011_BeginLibraryFunction();
#endif
        L3523_ = NULL;
        L3518_ps_ = F6030_F6046_PRIM_14_Library_FindInList(&FTLLibraryList, P3442_l_Name);
        if (L3518_ps_) {
                L3518_ps_->OpenCount++;
        } else {
                L3522_B_ = C0_FALSE;
                if (!F6036_F6039_PRIM_27_File_IsOpenable(P3442_l_Name, &L3523_)) {
                        if (!F6026_F0929_PRIM_05_FTL_Load(L3523_, &L3524_s_)) {
                                F6016_F6001_PRIM_18_File_Close(L3523_);
                                L3523_ = NULL;
                                L3522_B_ = C1_TRUE;
                                if ((L3518_ps_ = F6025_F6006_PRIM_07_FTL_Execute(&L3524_s_, 0L)) != 0) {
                                        if (L3518_ps_->Version >= (int16_t)P3443_l_MinimumVersion) {
                                                L3519_ps_ = &L3524_s_;
                                                L3520_ps_ = &L3518_ps_->FTLExecutable;
                                                L3521_i_ = 0;
#ifdef MEDIA692_X31J
                                                while (L3521_i_ < sizeof(FTL_EXECUTABLE)) {
#endif
#ifdef MEDIA645_A31E_A35E
                                                do {
#endif
                                                        *((char*)L3520_ps_)++ = *((char*)L3519_ps_)++; /* Copy data */
                                                        L3521_i_++;
#ifdef MEDIA692_X31J
                                                }
#endif
#ifdef MEDIA645_A31E_A35E
                                                } while (L3521_i_ < sizeof(FTL_EXECUTABLE));
#endif
                                                L3518_ps_->Base = PrimBase;
                                                L3518_ps_->OpenCount = 1;
                                                L3518_ps_->Flags = 0;
                                                F6027_F6047_PRIM_11_Library_AddToList(&FTLLibraryList, &L3518_ps_->FTLNode);
                                                goto loc_224;
                                        }
                                }
                        }
                }
                if (L3522_B_) {
                        F6024_F6044_PRIM_06_FTL_FreeMemory(&L3524_s_);
                }
                L3518_ps_ = NULL;
        }
        loc_224:
        if (L3523_) {
                F6016_F6001_PRIM_18_File_Close(L3523_);
        }
#ifdef MEDIA645_A31E_A35E
        F6012_EndLibraryFunction();
#endif
        return L3518_ps_;
}

void F6041_PRIM_09_Library_Close(P3444_l_Name)
long P3444_l_Name;
{
        register FTL_LIBRARY* L3525_ps_;


#ifdef MEDIA645_A31E_A35E
        F6011_BeginLibraryFunction();
#endif
        L3525_ps_ = F6030_F6046_PRIM_14_Library_FindInList(&FTLLibraryList, P3444_l_Name);
        if (L3525_ps_) {
                L3525_ps_->OpenCount--;
                if ((L3525_ps_->OpenCount == 0) && M007_GET(L3525_ps_->Flags, MASK0x0001_CAN_BE_EXPUNGED)) {
                        F6010_ExecuteLibraryFunction(L3525_ps_, 3); /* Expunge */
                        F6028_F6048_PRIM_12_Library_RemoveFromList(&L3525_ps_->FTLNode);
                        F6024_F6044_PRIM_06_FTL_FreeMemory(&L3525_ps_->FTLExecutable);
                }
        }
#ifdef MEDIA645_A31E_A35E
        F6012_EndLibraryFunction();
#endif
}

void F6042_PRIM_10_Library_FlagAsCanBeExpunged(P3445_ps_)
FTL_LIBRARY* P3445_ps_;
{
#ifdef MEDIA645_A31E_A35E
        F6011_BeginLibraryFunction();
#endif
        P3445_ps_->Flags |= MASK0x0001_CAN_BE_EXPUNGED;
#ifdef MEDIA645_A31E_A35E
        F6012_EndLibraryFunction();
#endif
}

int16_t F6043_PRIM_26_FTL_LoadAndExecute(P3446_l_Name, P3447_ps_, P3448_l_)
long P3446_l_Name;
register FTL_EXECUTABLE* P3447_ps_;
long P3448_l_;
{
        register int16_t L3526_i_;
        register BOOLEAN L3527_B_;
        FILE_HANDLE L3528_;


#ifdef MEDIA645_A31E_A35E
        F6011_BeginLibraryFunction();
#endif
        L3527_B_ = C0_FALSE;
        L3528_ = NULL;
        if (F6036_F6039_PRIM_27_File_IsOpenable(P3446_l_Name, &L3528_)) {
                L3526_i_ = 29;
        } else {
                if ((L3526_i_ = F6026_F0929_PRIM_05_FTL_Load(L3528_, P3447_ps_)) == 0) {
                        L3527_B_ = C1_TRUE;
                        F6025_F6006_PRIM_07_FTL_Execute(P3447_ps_, P3448_l_);
                }
        }
        if (L3527_B_) {
                F6024_F6044_PRIM_06_FTL_FreeMemory(P3447_ps_);
        }
        if (L3528_) {
                F6016_F6001_PRIM_18_File_Close(L3528_);
        }
#ifdef MEDIA645_A31E_A35E
        F6012_EndLibraryFunction();
#endif
        return L3526_i_;
}

int16_t F0929_PRIM_05_FTL_Load(P2746_FileHandle, P1024_ps_Descriptor)
FILE_HANDLE P2746_FileHandle;
#ifdef MEDIA692_X31J
register
#endif
FTL_EXECUTABLE* P1024_ps_Descriptor;
{
        register char* L1816_pc_BaseAddress;
        register SEGMENTHEADER* L1822_ps_SegmentHeaders;
        register char* L1817_pc_WorkingAddress;
        register unsigned int16_t L1805_ui_;
        register unsigned long L1809_ul_Size;
        register unsigned int16_t L1806_ui_ComputedChecksum;
        register int16_t L1811_i_DATASegmentIndex;
        register int16_t L1810_i_JUMPSegmentIndex;
        int16_t L1812_i_CODESegmentIndex;
        char* L1813_pc_DATAAddress;
        char* L1814_pc_A5WorldAddress;
        char* L1815_pc_CODEAddress;
        char* L1818_pc_AllocatedTempBuffer;
        char* L1819_pc_TempBuffer;
        unsigned long L1807_ul_CompressedDataByteCount;
        unsigned long L1808_ul_DataRelocationByteCount;
        long L3529_l_;
        HEADER L1820_s_Header;
        JUMPSEGMENTHEADER L1821_s_JUMPSegment;
#ifdef MEDIA692_X31J
        unsigned int16_t L3530_ui_key;
#endif


#ifdef MEDIA645_A31E_A35E
        F6011_BeginLibraryFunction();
#endif
        L1822_ps_SegmentHeaders = NULL;
        P1024_ps_Descriptor->MemoryAddress = NULL;
        L1818_pc_AllocatedTempBuffer = NULL;
        if (F6018_F0920_PRIM_20_File_Read(P2746_FileHandle, (long)sizeof(HEADER), &L1820_s_Header)) { /* Read the FTL file header */
                L1805_ui_ = 512;
                goto T0018041;
        }
        if ((L1820_s_Header.Magic != 0x6160) || (L1820_s_Header.Unknown1 != 2) || (L1820_s_Header.c_6 != 1) || (L1820_s_Header.c_7 != 0)) {
#ifdef MEDIA692_X31J
                F6019_F6002_PRIM_22_Console_PrintMessage("header info\n");
#endif
                L1805_ui_ = 1;
                goto T0018041;
        }
        if (L1820_s_Header.SegmentCount > 100) {
                L1805_ui_ = 26;
                goto T0018041;
        }
        L1809_ul_Size = L1820_s_Header.SegmentCount * sizeof(SEGMENTHEADER);
        if ((L1822_ps_SegmentHeaders = (SEGMENTHEADER*)F6013_F0917_PRIM_15_Memory_Allocate((long)L1809_ul_Size)) == NULL) { /* Allocate memory for segment headers */
                L1805_ui_ = 513;
                goto T0018041;
        }
        if (F6018_F0920_PRIM_20_File_Read(P2746_FileHandle, L1809_ul_Size, L1822_ps_SegmentHeaders)) { /* Read segment headers */
                L1805_ui_ = 514;
                goto T0018041;
        }
        if (F0930_GetHeaderChecksum(&L1820_s_Header, L1822_ps_SegmentHeaders) != L1820_s_Header.Checksum) { /* Compute and compare checksum on header and segment headers */
#ifdef MEDIA692_X31J
                F7101_sprintf(G0716_ac_ErrorMessage, "Err: file_header_checksum() 0x%x fhead.headerChecksum 0x%x\n", (long)F0930_GetHeaderChecksum(&L1820_s_Header, L1822_ps_SegmentHeaders), (long)L1820_s_Header.Checksum);
                F6019_F6002_PRIM_22_Console_PrintMessage(G0716_ac_ErrorMessage);
#endif
                L1805_ui_ = 14;
                goto T0018041;
        }
        L1810_i_JUMPSegmentIndex = -1;
        L1811_i_DATASegmentIndex = -1;
        L1812_i_CODESegmentIndex = -1;
        for (L1805_ui_ = 0; L1805_ui_ < L1820_s_Header.SegmentCount; L1805_ui_++) {
                switch (L1822_ps_SegmentHeaders[L1805_ui_].Type) {
                        case 0x0010:
                                if (L1822_ps_SegmentHeaders[L1805_ui_].Id != 1) {
                                        L1805_ui_ = 20;
                                        goto T0018041;
                                }
                                if (L1810_i_JUMPSegmentIndex == -1) {
                                        L1810_i_JUMPSegmentIndex = L1805_ui_;
                                } else {
                                        L1805_ui_ = 23;    /* Error if there is more than 1 segment of that type */
                                        goto T0018041;
                                }
                                break;
                        case 0x0011:
                                if (L1822_ps_SegmentHeaders[L1805_ui_].Id != 1) {
                                        L1805_ui_ = 21;
                                        goto T0018041;
                                }
                                if (L1811_i_DATASegmentIndex == -1) {
                                        L1811_i_DATASegmentIndex = L1805_ui_;
                                } else {
                                        L1805_ui_ = 24;    /* Error if there is more than 1 segment of that type */
                                        goto T0018041;
                                }
                                break;
                        case 0x0012:
                                if (L1822_ps_SegmentHeaders[L1805_ui_].Id != 1) {
                                        L1805_ui_ = 22;
                                        goto T0018041;
                                }
                                if (L1812_i_CODESegmentIndex == -1) {
                                        L1812_i_CODESegmentIndex = L1805_ui_;
                                } else {
                                        L1805_ui_ = 25;    /* Error if there is more than 1 segment of that type */
                                        goto T0018041;
                                }
                                break;
                        default:
                                break;
                }
        }
        if ((L1810_i_JUMPSegmentIndex == -1) || (L1811_i_DATASegmentIndex == -1) || (L1812_i_CODESegmentIndex == -1)) { /* Error if there is no segment of one of the three types 0x0010 0x0011 0x0012 */
                L1805_ui_ = 2;
                goto T0018041;
        }
        if (F6017_F0919_PRIM_19_File_Seek(P2746_FileHandle, (long)L1822_ps_SegmentHeaders[L1810_i_JUMPSegmentIndex].Offset)) {
                L1805_ui_ = 515;
                goto T0018041;
        }
        if (F6018_F0920_PRIM_20_File_Read(P2746_FileHandle, (long)sizeof(JUMPSEGMENTHEADER), &L1821_s_JUMPSegment)) {
                L1805_ui_ = 516;
                goto T0018041;
        }
        L1809_ul_Size = 0L + L1821_s_JUMPSegment.BelowA5Size + L1821_s_JUMPSegment.AboveA5Size + L1821_s_JUMPSegment.CODESize + 12;
        P1024_ps_Descriptor->MemoryAddress = L1816_pc_BaseAddress = (char*)F6013_F0917_PRIM_15_Memory_Allocate(L1809_ul_Size);
        if (L1816_pc_BaseAddress == NULL) {
                L1805_ui_ = 517;
                goto T0018041;
        }
        L1813_pc_DATAAddress = (char*)(((long)L1816_pc_BaseAddress + 0 + 1) & 0xFFFFFFFE);
        L1814_pc_A5WorldAddress = (char*)(((long)L1813_pc_DATAAddress + L1821_s_JUMPSegment.BelowA5Size + 1) & 0xFFFFFFFE);
        L1815_pc_CODEAddress = (char*)(((long)L1814_pc_A5WorldAddress + L1821_s_JUMPSegment.AboveA5Size + 1) & 0xFFFFFFFE);
        L1809_ul_Size = L1822_ps_SegmentHeaders[L1810_i_JUMPSegmentIndex].Size; /* The temporary buffer should be large enough the store the JUMP segment */
        if (L1822_ps_SegmentHeaders[L1811_i_DATASegmentIndex].Size > L1809_ul_Size) {
                L1809_ul_Size = L1822_ps_SegmentHeaders[L1811_i_DATASegmentIndex].Size; /* The temporary buffer should be large enough the store the DATA segment */
        }
        if (L1822_ps_SegmentHeaders[L1812_i_CODESegmentIndex].Size > L1809_ul_Size) {
                L1809_ul_Size = L1822_ps_SegmentHeaders[L1812_i_CODESegmentIndex].Size;
        }
        if (L1821_s_JUMPSegment.CODECompressed || (L1809_ul_Size > L1821_s_JUMPSegment.CODESize)) {
                if ((L1818_pc_AllocatedTempBuffer = (char*)F6013_F0917_PRIM_15_Memory_Allocate(L1809_ul_Size + 1)) == NULL) { /* Allocate a new buffer of memory if necessary (if the memory for CODE is too small) */
                        L1805_ui_ = 518;
                        goto T0018041;
                }
                L1819_pc_TempBuffer = (char*)(((long)L1818_pc_AllocatedTempBuffer + 1) & 0xFFFFFFFE);
        } else {
                L1819_pc_TempBuffer = L1815_pc_CODEAddress; /* Use the memory destined to store CODE as the temporary buffer */
        }

        /* Build the actual jump table in memory from the entry points list in the JUMP segment */
        L1816_pc_BaseAddress = L1819_pc_TempBuffer;
        L1817_pc_WorkingAddress = (L1814_pc_A5WorldAddress + L1821_s_JUMPSegment.JumpTableOffset); /* Address of the jump table in A5 world */
        L1809_ul_Size = L1822_ps_SegmentHeaders[L1810_i_JUMPSegmentIndex].Size - sizeof(JUMPSEGMENTHEADER); /* Size of jumps in JUMP segment (size of segment - size of header) */
        if (F6018_F0920_PRIM_20_File_Read(P2746_FileHandle, L1809_ul_Size, L1816_pc_BaseAddress)) { /* Load the jumps in the temporary buffer */
                L1805_ui_ = 519;
                goto T0018041;
        }
        L1806_ui_ComputedChecksum = F0931_ChecksumWords(L1816_pc_BaseAddress, (long)L1809_ul_Size);
        L1806_ui_ComputedChecksum += F0931_ChecksumWords(&L1821_s_JUMPSegment, (long)sizeof(JUMPSEGMENTHEADER));
        if ((unsigned int16_t)(L1806_ui_ComputedChecksum - L1821_s_JUMPSegment.JUMPChecksum) != L1821_s_JUMPSegment.JUMPChecksum) { /* Error if invalid checksum on JUMP segment */
#ifdef MEDIA692_X31J
                F7101_sprintf(G0716_ac_ErrorMessage, "Err: computed checksum 0x%x jumpChecksum 0x%x\n", (unsigned int16_t)(L1806_ui_ComputedChecksum - L1821_s_JUMPSegment.JUMPChecksum), (long)L1821_s_JUMPSegment.JUMPChecksum);
                F6019_F6002_PRIM_22_Console_PrintMessage(G0716_ac_ErrorMessage);
#endif
                L1805_ui_ = 15;
                goto T0018041;
        }
        for (L3529_l_ = L1809_ul_Size; L3529_l_ > 0; L3529_l_ -= 4) { /* Loop through all entry points and create a jmp instruction with actual entry point address */
                ((JUMPTABLEENTRY*)L1817_pc_WorkingAddress)->Instruction = 0x4EF9; /* jmp instruction */
                ((JUMPTABLEENTRY*)L1817_pc_WorkingAddress)->Operand = *((long*)L1816_pc_BaseAddress) + (long)L1815_pc_CODEAddress; /* Address from jumptable + base address of the CODE => actual address in memory */
                L1817_pc_WorkingAddress += 8; /* Each jmp instruction is 6 bytes large, but preceded by 2 unused bytes */
                L1816_pc_BaseAddress += 4;
        }

        /* Load compressed DATA in temporary buffer */
        L1816_pc_BaseAddress = L1819_pc_TempBuffer;
        L1817_pc_WorkingAddress = L1813_pc_DATAAddress;
        L1809_ul_Size = (unsigned long)L1822_ps_SegmentHeaders[L1811_i_DATASegmentIndex].Size;
        if (F6017_F0919_PRIM_19_File_Seek(P2746_FileHandle, (long)L1822_ps_SegmentHeaders[L1811_i_DATASegmentIndex].Offset)) {
                L1805_ui_ = 520;
                goto T0018041;
        }
        if (F6018_F0920_PRIM_20_File_Read(P2746_FileHandle, L1809_ul_Size, L1816_pc_BaseAddress)) { /* Load compressed DATA in the temporary buffer */
                L1805_ui_ = 521;
                goto T0018041;
        }
        L1806_ui_ComputedChecksum = F0932_ChecksumBytes(L1816_pc_BaseAddress, (long)L1809_ul_Size);
        if (L1806_ui_ComputedChecksum != L1821_s_JUMPSegment.DATAChecksum) { /* Error if invalid checksum on DATA */
#ifdef MEDIA692_X31J
                F7101_sprintf(G0716_ac_ErrorMessage, "Err: computed checksum 0x%x initChecksum 0x%x\n", (long)L1806_ui_ComputedChecksum, (long)L1821_s_JUMPSegment.DATAChecksum);
                F6019_F6002_PRIM_22_Console_PrintMessage(G0716_ac_ErrorMessage);
#endif
                L1805_ui_ = 16;
                goto T0018041;
        } /* T0018033 */
        /* Uncompress DATA into final location */
        L1807_ul_CompressedDataByteCount = ((DATASEGMENTHEADER*)L1816_pc_BaseAddress)->DATACompressedSize; /* Compressed DATA size */
        L1808_ul_DataRelocationByteCount = ((DATASEGMENTHEADER*)L1816_pc_BaseAddress)->RelocationDataSize; /* Size of data relocation information */
        L1816_pc_BaseAddress += sizeof(DATASEGMENTHEADER); /* Address of the first byte of actual DATA (after the header) */
        while (L1807_ul_CompressedDataByteCount > 0) {
                L1805_ui_ = *((int16_t*)L1816_pc_BaseAddress); /* Read one word from input compressed data */
                *((int16_t*)L1817_pc_WorkingAddress) = L1805_ui_; /* Output word to data segment */
                L1816_pc_BaseAddress += 2; /* Proceed to the next input word in compressed data */
                L1807_ul_CompressedDataByteCount -= 2;
                L1817_pc_WorkingAddress += 2;
                if (L1805_ui_ == 0) { /* If word is 0, read next word and duplicate 0x0000 word */
                        L1805_ui_ = *((int16_t*)L1816_pc_BaseAddress); /* Read next word from input compressed data: repeat count */
                        L1816_pc_BaseAddress += 2;
                        L1807_ul_CompressedDataByteCount -= 2;
                        for (; L1805_ui_ > 0; L1805_ui_--) { /* Loop to output 0x00 bytes to data segment */
                                *((char*)L1817_pc_WorkingAddress) = 0;
                                L1817_pc_WorkingAddress++;
                        }
                }
        }
        /* Apply relocation information to DATA. Relocation information consists of words (signed integers) that are offsets relative to the A5 world address. */
        while (L1808_ul_DataRelocationByteCount > 0) {
                *((long*)(L1814_pc_A5WorldAddress + *((int16_t*)L1816_pc_BaseAddress))) += (long)L1814_pc_A5WorldAddress; /* Add A5 world address to the specified data word */
                L1816_pc_BaseAddress += 2; /* Proceed to next relocation word (a signed int16_t offset in DATA) */
                L1808_ul_DataRelocationByteCount -= 2;
        }
        if (L1821_s_JUMPSegment.CODECompressed) {
                L1816_pc_BaseAddress = L1819_pc_TempBuffer;
        } else {
                L1816_pc_BaseAddress = L1815_pc_CODEAddress;
        }
        L1809_ul_Size = (unsigned long)L1822_ps_SegmentHeaders[L1812_i_CODESegmentIndex].Size;
        if (F6017_F0919_PRIM_19_File_Seek(P2746_FileHandle, (long)L1822_ps_SegmentHeaders[L1812_i_CODESegmentIndex].Offset)) {
                L1805_ui_ = 528;
                goto T0018041;
        }
        if (F6018_F0920_PRIM_20_File_Read(P2746_FileHandle, L1809_ul_Size, L1816_pc_BaseAddress)) { /* Load CODE into its final location */
                L1805_ui_ = 529;
                goto T0018041;
        }
        if (L1821_s_JUMPSegment.CODECompressed) {
                if (F6034_F6049_PRIM_25_FTL_DecompressCODESegment(L1819_pc_TempBuffer, L1815_pc_CODEAddress) < 0) {
                        L1805_ui_ = 27;
                        goto T0018041;
                }
        }
        L1806_ui_ComputedChecksum = F0932_ChecksumBytes(L1815_pc_CODEAddress, L1821_s_JUMPSegment.CODESize);
        if (L1806_ui_ComputedChecksum != L1821_s_JUMPSegment.CODEChecksum) { /* Error if invalid checksum on CODE */
#ifdef MEDIA692_X31J
                F7101_sprintf(G0716_ac_ErrorMessage, "Err: computed checksum 0x%x codeChecksum 0x%x\n", (long)L1806_ui_ComputedChecksum, (long)L1821_s_JUMPSegment.CODEChecksum);
                F6019_F6002_PRIM_22_Console_PrintMessage(G0716_ac_ErrorMessage);
#endif
                L1805_ui_ = 17;
                goto T0018041;
        }
        P1024_ps_Descriptor->A5World = L1814_pc_A5WorldAddress;
        P1024_ps_Descriptor->JumpTableAddress = L1814_pc_A5WorldAddress + L1821_s_JUMPSegment.JumpTableOffset + 2; /* Each jump contains 2 unused bytes at the start (8 bytes large and contains a 6 bytes jmp instruction) */
        P1024_ps_Descriptor->StackSize = L1821_s_JUMPSegment.StackSize;
        L1805_ui_ = 0; /* Success return code */
        T0018041:
        if (L1822_ps_SegmentHeaders != NULL) {
                F6014_F0918_PRIM_16_Memory_Free((char*)L1822_ps_SegmentHeaders);
        }
        if (L1818_pc_AllocatedTempBuffer != NULL) { /* If a memory block was allocated for the temporary buffer (instead of using the memory for CODE) */
                F6014_F0918_PRIM_16_Memory_Free((char*)L1818_pc_AllocatedTempBuffer);
        }
#ifdef MEDIA692_X31J
        if (L1805_ui_ != 0) {
                F7101_sprintf(G0716_ac_ErrorMessage, "Loader Error:%x\n", (long)L1805_ui_);
                F6019_F6002_PRIM_22_Console_PrintMessage(G0716_ac_ErrorMessage);
                while (F6020_F6003_PRIM_23_Input_WaitForChar()) {
                        F6021_F6004_PRIM_24_Input_Read();
                }
                F6019_F6002_PRIM_22_Console_PrintMessage(">ignore (y/n)?\n");
                L3530_ui_key = F6021_F6004_PRIM_24_Input_Read();
                if ((L3530_ui_key == 'y') || (L3530_ui_key == 'Y')) {
                        L1805_ui_ = 0;
                }
        }
#endif
        if ((L1805_ui_ != 0) && (P1024_ps_Descriptor->MemoryAddress != NULL)) {
                F6014_F0918_PRIM_16_Memory_Free((char*)P1024_ps_Descriptor->MemoryAddress);
                P1024_ps_Descriptor->MemoryAddress = NULL;
        }
#ifdef MEDIA645_A31E_A35E
        F6012_EndLibraryFunction();
#endif
        return L1805_ui_;
/*
At the end of this function, the memory layout is the following:
        DATA (decompressed)
        'Application Parameters' area (32 bytes)
        Jumptable (8 bytes entries with 2 unused bytes at the beginning of each)
        CODE
        <12 bytes>
*/
}

void F6044_PRIM_06_FTL_FreeMemory(P3449_ps_)
register FTL_EXECUTABLE* P3449_ps_;
{
#ifdef MEDIA645_A31E_A35E
        F6011_BeginLibraryFunction();
#endif
        if (P3449_ps_->MemoryAddress) {
                F6014_F0918_PRIM_16_Memory_Free(P3449_ps_->MemoryAddress);
                P3449_ps_->MemoryAddress = NULL;
        }
#ifdef MEDIA645_A31E_A35E
        F6012_EndLibraryFunction();
#endif
}

void F6045_PRIM_13_Library_InitializeList(P3450_ps_)
register FTL_NODE* P3450_ps_;
{
#ifdef MEDIA645_A31E_A35E
        F6011_BeginLibraryFunction();
#endif
        P3450_ps_->Next = (FTL_NODE*)&P3450_ps_->Previous; /* This marks the last item in the list */
        P3450_ps_->Previous = NULL; /* First node, no previous node */
        P3450_ps_->Name = (long)P3450_ps_; /* Name is the address of the node itself */
#ifdef MEDIA645_A31E_A35E
        F6012_EndLibraryFunction();
#endif
}

FTL_LIBRARY* F6046_PRIM_14_Library_FindInList(P3451_ps_List, P3452_l_LibraryName)
register FTL_NODE* P3451_ps_List;
register long P3452_l_LibraryName;
{
#ifdef MEDIA645_A31E_A35E
        F6011_BeginLibraryFunction();
#endif
        P3451_ps_List = P3451_ps_List->Next;
        while (P3451_ps_List->Next) {
                if (P3451_ps_List->Name == P3452_l_LibraryName) {
#ifdef MEDIA645_A31E_A35E
                        F6012_EndLibraryFunction();
#endif
                        return (FTL_LIBRARY*)P3451_ps_List;
                }
                P3451_ps_List = P3451_ps_List->Next;
        }
#ifdef MEDIA645_A31E_A35E
        F6012_EndLibraryFunction();
#endif
        return NULL;
}

void F6047_PRIM_11_Library_AddToList(P3453_ps_List, P3454_ps_)
register FTL_NODE* P3453_ps_List;
register FTL_NODE* P3454_ps_;
{


#ifdef MEDIA645_A31E_A35E
        F6011_BeginLibraryFunction();
#endif
        P3454_ps_->Previous = P3453_ps_List;
        P3454_ps_->Next = P3453_ps_List->Next;
        P3453_ps_List->Next = P3454_ps_;
        P3454_ps_->Next->Previous = P3454_ps_;
#ifdef MEDIA645_A31E_A35E
        F6012_EndLibraryFunction();
#endif
}

void F6048_PRIM_12_Library_RemoveFromList(P3455_ps_)
register FTL_NODE* P3455_ps_;
{
#ifdef MEDIA645_A31E_A35E
        F6011_BeginLibraryFunction();
#endif
        P3455_ps_->Previous->Next = P3455_ps_->Next;
        P3455_ps_->Next->Previous = P3455_ps_->Previous;
#ifdef MEDIA645_A31E_A35E
        F6012_EndLibraryFunction();
#endif
}

void F6049_PRIM_25_FTL_DecompressCODESegment(
)
{
#ifdef MEDIA645_A31E_A35E
        F6011_BeginLibraryFunction();
#endif
        F6022_F6005_PRIM_21_FTL_FatalLoaderError(0x700L);
#ifdef MEDIA645_A31E_A35E
        F6012_EndLibraryFunction();
#endif
}

FTL_LIBRARY* F6050_InitializeFTLLibraryManagement(
)
{
#ifdef MEDIA645_A31E_A35E
        register int16_t L3531_i_;
#endif


        G9011_s_PRIMLibrary.FTLLibrary.FTLNode.Name = FTL_PRIM;
        G9011_s_PRIMLibrary.FTLLibrary.FTLExecutable.A5World = F6009_GetInitialA5World();
        G9011_s_PRIMLibrary.FTLLibrary.Base = PrimBase;
        G9011_s_PRIMLibrary.FTLLibrary.OpenCount = 0;
        G9011_s_PRIMLibrary.FTLLibrary.Flags = 0;
        G9011_s_PRIMLibrary.FTLLibrary.Version = 5;
        G9011_s_PRIMLibrary.FTLLibrary.Unused1 = 0;
        F6029_F6045_PRIM_13_Library_InitializeList(&FTLLibraryList);
#ifdef MEDIA645_A31E_A35E
        L3531_i_ = 0;
        if (IntuitionBase = (struct IntuitionBase*)OpenLibrary("intuition.library", 31L)) { /* Version 31 minimum */
                while (!CloseWorkBench()) { /* Try closing the Workbench */
                        if (L3531_i_++ >= 12) /* Maximum 12 attempts */
                                break;
                        Delay(25L); /* One attempt every 25 VBL cycles */
                }
                CloseLibrary(IntuitionBase);
                IntuitionBase = NULL;
        }
#endif
        return PrimBase;
}

static void F6051_UnLoadFTLLibrary(P3456_ps_)
register FTL_LIBRARY* P3456_ps_;
{
        F6010_ExecuteLibraryFunction(P3456_ps_, 3); /* Expunge */
        F6028_F6048_PRIM_12_Library_RemoveFromList(&P3456_ps_->FTLNode);
        F6024_F6044_PRIM_06_FTL_FreeMemory(&P3456_ps_->FTLExecutable);
}

void F6052_FTL_UnloadAll(
)
{
        register FTL_NODE* L3532_ps_;
        register FTL_LIBRARY* L3533_ps_;
        register int16_t L3534_i_LoadedLibraryCount;
        register int16_t L3535_i_UnloadedLibraryCount;
#ifdef MEDIA692_X31J
        register unsigned long L3536_ul_;
        long L3537_l_[3];
#endif


        L3532_ps_ = &FTLLibraryList;
        do {
                L3533_ps_ = (FTL_LIBRARY*)L3532_ps_;
                L3534_i_LoadedLibraryCount = L3535_i_UnloadedLibraryCount = 0;
                while ((L3533_ps_ = (FTL_LIBRARY*)L3533_ps_->FTLNode.Next) != &L3532_ps_->Previous) { /* If not the last in the list */
                        L3534_i_LoadedLibraryCount++;
                        if (L3533_ps_->OpenCount == 0) {
                                L3533_ps_ = (FTL_LIBRARY*)L3533_ps_->FTLNode.Previous;
                                F6051_UnLoadFTLLibrary(L3533_ps_->FTLNode.Next);
                                L3535_i_UnloadedLibraryCount++;
                        }
                }
                if (!L3534_i_LoadedLibraryCount)
                        break;
                if (!L3535_i_UnloadedLibraryCount)
                        goto circular;
        } while (L3535_i_UnloadedLibraryCount < L3534_i_LoadedLibraryCount);
        return;
        circular: ;
#ifdef MEDIA692_X31J
        F6019_F6002_PRIM_22_Console_PrintMessage("Gack! Circularly dependant driver linkage!\n");
        F6019_F6002_PRIM_22_Console_PrintMessage("The naughty drivers and their openCnts are:\n");
        L3537_l_[0] = 0x20202000; /* 3 spaces */
        L3537_l_[2] = 0x2C200000; /* comma and space */
        L3533_ps_ = (FTL_LIBRARY*)L3532_ps_;
        while ((L3533_ps_ = (FTL_LIBRARY*)L3533_ps_->FTLNode.Next) != &L3532_ps_->Previous) {
                F6019_F6002_PRIM_22_Console_PrintMessage(&L3537_l_);
                L3537_l_[0] = L3533_ps_->FTLNode.Name;
                L3536_ul_ = L3533_ps_->OpenCount;
                L3537_l_[1] = 0x20000000; /* space */
                L3537_l_[1] |= '0' + (L3536_ul_ % 10);
                L3536_ul_ /= 10;
                L3537_l_[1] |= ('0' + (L3536_ul_ % 10)) << 8;
                L3536_ul_ /= 10;
                L3537_l_[1] |= ('0' + (L3536_ul_ % 10)) << 16;
        }
        L3537_l_[2] = 0x2E0A0000; /* period and new line */
        F6019_F6002_PRIM_22_Console_PrintMessage(&L3537_l_);
#endif
}

static BOOLEAN F6053_IsFunctionHooked(P3457_ps_function)
FUNCTION_HOOK* P3457_ps_function;
{
        return (P3457_ps_function->lea == 0x43F9) && (P3457_ps_function->jmp == 0x4EF9) && (P3457_ps_function->illegal_rte == 0x4AFC4E73);
}

static unsigned int16_t F0930_GetHeaderChecksum(P1025_ps_, P1026_ps_)
HEADER* P1025_ps_;
SEGMENTHEADER* P1026_ps_;
{
        register unsigned int16_t L1823_ui_;
        register unsigned int16_t L1824_ui_;
        register unsigned int16_t L1825_ui_;
        register unsigned char* L1826_puc_;


        L1824_ui_ = 0;
        L1826_puc_ = (unsigned char*)P1025_ps_;
        for (L1823_ui_ = 4; L1823_ui_ < 20; L1823_ui_++) {
                L1824_ui_ += (L1826_puc_[L1823_ui_] * L1823_ui_);
        }
        L1826_puc_ = (unsigned char*)P1026_ps_;
        L1825_ui_ = P1025_ps_->SegmentCount * sizeof(SEGMENTHEADER);
        for (L1823_ui_ = 0; L1823_ui_ < L1825_ui_; L1823_ui_++) {
                L1824_ui_ += L1826_puc_[L1823_ui_] * ((L1823_ui_ & 0x00FF) + 1);
        }
        return L1824_ui_;
}

static unsigned int16_t F0931_ChecksumWords(P1027_pi_, P1028_l_)
register int16_t* P1027_pi_;
register long P1028_l_;
{
        register unsigned int16_t L1827_ui_;


        L1827_ui_ = 0;
        while (P1028_l_ > 0) {
                L1827_ui_ += *P1027_pi_++;
                P1028_l_ -= 2;
        }
        return L1827_ui_;
}

static unsigned int16_t F0932_ChecksumBytes(P1029_puc_, P1030_l_)
register unsigned char* P1029_puc_;
register long P1030_l_;
{
        register unsigned int16_t L1828_ui_;


        L1828_ui_ = 0;
        while (P1030_l_ > 0) {
                L1828_ui_ += *P1029_puc_++;
                P1030_l_--;
        }
        return L1828_ui_;
}
#endif
